diff --git a/code/framework/src/integrations/server/instance.cpp b/code/framework/src/integrations/server/instance.cpp index 14bce09bc..a970cc7f0 100644 --- a/code/framework/src/integrations/server/instance.cpp +++ b/code/framework/src/integrations/server/instance.cpp @@ -297,13 +297,14 @@ namespace Framework::Integrations::Server { Logging::GetLogger(FRAMEWORK_INNER_SERVER)->debug("Disconnecting peer {}, reason: {}", guid.g, reason); const auto e = _worldEngine->GetEntityByGUID(guid.g); - + _worldEngine->GetWorld()->defer_begin(); if (e.is_valid()) { if (_onPlayerDisconnectCallback) _onPlayerDisconnectCallback(e, guid.g); _worldEngine->RemoveEntity(e); } + _worldEngine->GetWorld()->defer_end(); net->GetPeer()->CloseConnection(guid, true); }); diff --git a/code/framework/src/world/client.cpp b/code/framework/src/world/client.cpp index eac4b22c2..2037a29ce 100644 --- a/code/framework/src/world/client.cpp +++ b/code/framework/src/world/client.cpp @@ -34,12 +34,10 @@ namespace Framework::World { flecs::entity ClientEngine::GetEntityByServerID(flecs::entity_t id) const { flecs::entity ent = {}; - _queryGetEntityByServerID.iter([&ent, id](flecs::iter it, Modules::Base::ServerID *rhs) { - for (size_t i = 0; i < it.count(); i++) { - if (id == rhs[i].id) { - ent = it.entity(i); - return; - } + _queryGetEntityByServerID.each([&ent, id](flecs::entity e, Modules::Base::ServerID& rhs) { + if (id == rhs.id) { + ent = e; + return; } }); return ent; @@ -57,22 +55,27 @@ namespace Framework::World { flecs::entity ClientEngine::CreateEntity(flecs::entity_t serverID) const { const auto e = _world->entity(); - const auto sid = e.get_mut(); - sid->id = serverID; + auto &sid = e.ensure(); + sid.id = serverID; return e; } void ClientEngine::OnConnect(Networking::NetworkPeer *peer, float tickInterval) { _networkPeer = peer; - _streamEntities = _world->system("StreamEntities").kind(flecs::PostUpdate).interval(tickInterval).iter([this](flecs::iter it, Modules::Base::Transform *tr, Modules::Base::Streamable *rs) { + _streamEntities = _world->system("StreamEntities").kind(flecs::PostUpdate).interval(tickInterval).run([this](flecs::iter &it) { const auto myGUID = _networkPeer->GetPeer()->GetMyGUID(); - for (size_t i = 0; i < it.count(); i++) { - const auto &es = &rs[i]; + while (it.next()) { + const auto tr = it.field(0); + const auto rs = it.field(1); - if (es->GetBaseEvents().updateProc && Framework::World::Engine::IsEntityOwner(it.entity(i), myGUID.g)) { - es->GetBaseEvents().updateProc(_networkPeer, (SLNet::UNASSIGNED_RAKNET_GUID).g, it.entity(i)); + for (auto i : it) { + const auto &es = &rs[i]; + + if (es->GetBaseEvents().updateProc && Framework::World::Engine::IsEntityOwner(it.entity(i), myGUID.g)) { + es->GetBaseEvents().updateProc(_networkPeer, (SLNet::UNASSIGNED_RAKNET_GUID).g, it.entity(i)); + } } } }); @@ -87,19 +90,14 @@ namespace Framework::World { } _world->defer_begin(); - _allStreamableEntities.iter([this](flecs::iter it, Modules::Base::Transform *tr, Modules::Base::Streamable *s) { - (void)tr; - (void)s; - - for (size_t i = 0; i < it.count(); i++) { - if (_onEntityDestroyCallback) { - if (!_onEntityDestroyCallback(it.entity(i))) { - continue; - } + _allStreamableEntities.each([this](flecs::entity e, Modules::Base::Transform&, Modules::Base::Streamable&) { + if (_onEntityDestroyCallback) { + if (!_onEntityDestroyCallback(e)) { + return; } - - it.entity(i).destruct(); } + + e.destruct(); }); _world->defer_end(); diff --git a/code/framework/src/world/engine.cpp b/code/framework/src/world/engine.cpp index b522b23c6..eb803755d 100644 --- a/code/framework/src/world/engine.cpp +++ b/code/framework/src/world/engine.cpp @@ -54,15 +54,12 @@ namespace Framework::World { flecs::entity Engine::GetEntityByGUID(uint64_t guid) const { flecs::entity ourEntity = {}; - _findAllStreamerEntities.iter([&ourEntity, guid](flecs::iter &it, Modules::Base::Streamer *s) { - for (const auto i : it) { - if (s[i].guid == guid) { - ourEntity = it.entity(i); - return; - } + _findAllStreamerEntities.each([&ourEntity, guid](flecs::entity e, Modules::Base::Streamer &s) { + if (s.guid == guid) { + ourEntity = e; + return; } }); - return ourEntity; } diff --git a/code/framework/src/world/modules/base.hpp b/code/framework/src/world/modules/base.hpp index 089a0ab41..52501459c 100644 --- a/code/framework/src/world/modules/base.hpp +++ b/code/framework/src/world/modules/base.hpp @@ -168,8 +168,8 @@ namespace Framework::World::Modules { #endif } - static void SetupServerEmitters(Streamable *streamable); - static void SetupClientEmitters(Streamable *streamable); + static void SetupServerEmitters(Streamable& streamable); + static void SetupClientEmitters(Streamable& streamable); static void SetupServerReceivers(Framework::Networking::NetworkPeer *net, Framework::World::Engine *worldEngine); static void SetupClientReceivers(Framework::Networking::NetworkPeer *net, Framework::World::ClientEngine *worldEngine, Framework::World::Archetypes::StreamingFactory *streamingFactory); }; diff --git a/code/framework/src/world/modules/modules_impl.cpp b/code/framework/src/world/modules/modules_impl.cpp index ce169b04c..b799460bd 100644 --- a/code/framework/src/world/modules/modules_impl.cpp +++ b/code/framework/src/world/modules/modules_impl.cpp @@ -25,8 +25,8 @@ } namespace Framework::World::Modules { - void Base::SetupServerEmitters(Streamable *streamable) { - streamable->events.spawnProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { + void Base::SetupServerEmitters(Streamable& streamable) { + streamable.events.spawnProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { Framework::Networking::Messages::GameSyncEntitySpawn entitySpawn; const auto tr = e.get(); if (tr) @@ -37,7 +37,7 @@ namespace Framework::World::Modules { return true; }; - streamable->events.despawnProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { + streamable.events.despawnProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { CALL_CUSTOM_PROC(despawnProc); Framework::Networking::Messages::GameSyncEntityDespawn entityDespawn; entityDespawn.SetServerID(e.id()); @@ -45,7 +45,7 @@ namespace Framework::World::Modules { return true; }; - streamable->events.selfUpdateProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { + streamable.events.selfUpdateProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { Framework::Networking::Messages::GameSyncEntitySelfUpdate entitySelfUpdate; entitySelfUpdate.SetServerID(e.id()); peer->Send(entitySelfUpdate, guid); @@ -53,7 +53,7 @@ namespace Framework::World::Modules { return true; }; - streamable->events.updateProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { + streamable.events.updateProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { Framework::Networking::Messages::GameSyncEntityUpdate entityUpdate; const auto tr = e.get(); const auto es = e.get(); @@ -65,7 +65,7 @@ namespace Framework::World::Modules { return true; }; - streamable->events.ownerUpdateProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { + streamable.events.ownerUpdateProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { Framework::Networking::Messages::GameSyncEntityOwnerUpdate entityUpdate; const auto tr = e.get(); const auto es = e.get(); @@ -77,8 +77,8 @@ namespace Framework::World::Modules { return true; }; } - void Base::SetupClientEmitters(Streamable *streamable) { - streamable->events.updateProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { + void Base::SetupClientEmitters(Streamable& streamable) { + streamable.events.updateProc = [&](Framework::Networking::NetworkPeer *peer, uint64_t guid, flecs::entity e) { Framework::Networking::Messages::GameSyncEntityUpdate entityUpdate; const auto tr = e.get(); const auto sid = e.get(); @@ -130,6 +130,7 @@ namespace Framework::World::Modules { const auto e = worldEngine->CreateEntity(msg->GetServerID()); streamingFactory->SetupClient(e, SLNet::UNASSIGNED_RAKNET_GUID.g); + e.add(); const auto tr = e.get_mut(); *tr = msg->GetTransform(); }); diff --git a/code/framework/src/world/server.cpp b/code/framework/src/world/server.cpp index c48ef7569..419bf1241 100644 --- a/code/framework/src/world/server.cpp +++ b/code/framework/src/world/server.cpp @@ -122,43 +122,49 @@ namespace Framework::World { streamer.collectRangeExemptEntitiesProc(e, streamer); }); - _world->system("TickRateRegulator").interval(3.0f).iter([](flecs::iter &it, Modules::Base::TickRateRegulator *tr, Modules::Base::Transform *t, Modules::Base::Streamable *s) { - for (const auto i : it) { - bool decreaseRate = true; - constexpr float EPSILON = 0.01f; - - // Check if position has changed - if (glm::abs(t[i].pos.x - tr[i].pos.x) > EPSILON || glm::abs(t[i].pos.y - tr[i].pos.y) > EPSILON || glm::abs(t[i].pos.z - tr[i].pos.z) > EPSILON) { - decreaseRate = false; - } + _world->system("TickRateRegulator").interval(3.0f).run([](flecs::iter &it) { + while (it.next()) { + const auto tr = it.field(0); + const auto t = it.field(1); + const auto s = it.field(2); + + for (auto i : it) { + bool decreaseRate = true; + constexpr float EPSILON = 0.01f; + + // Check if position has changed + if (glm::abs(t[i].pos.x - tr[i].pos.x) > EPSILON || glm::abs(t[i].pos.y - tr[i].pos.y) > EPSILON || glm::abs(t[i].pos.z - tr[i].pos.z) > EPSILON) { + decreaseRate = false; + } - // Check if rotation quaternion has changed - if (glm::abs(t[i].rot.x - tr[i].rot.x) > EPSILON || glm::abs(t[i].rot.y - tr[i].rot.y) > EPSILON || glm::abs(t[i].rot.z - tr[i].rot.z) > EPSILON || glm::abs(t[i].rot.w - tr[i].rot.w) > EPSILON) { - decreaseRate = false; - } + // Check if rotation quaternion has changed + if (glm::abs(t[i].rot.x - tr[i].rot.x) > EPSILON || glm::abs(t[i].rot.y - tr[i].rot.y) > EPSILON || glm::abs(t[i].rot.z - tr[i].rot.z) > EPSILON || glm::abs(t[i].rot.w - tr[i].rot.w) > EPSILON) { + decreaseRate = false; + } - // Check if velocity has changed - if (glm::abs(t[i].vel.x - tr[i].vel.x) > EPSILON || glm::abs(t[i].vel.y - tr[i].vel.y) > EPSILON || glm::abs(t[i].vel.z - tr[i].vel.z) > EPSILON) { - decreaseRate = false; - } + // Check if velocity has changed + if (glm::abs(t[i].vel.x - tr[i].vel.x) > EPSILON || glm::abs(t[i].vel.y - tr[i].vel.y) > EPSILON || glm::abs(t[i].vel.z - tr[i].vel.z) > EPSILON) { + decreaseRate = false; + } - // Check if generation ID has changed - if (t[i].GetGeneration() != tr[i].lastGenID) { - decreaseRate = true; - } + // Check if generation ID has changed + if (t[i].GetGeneration() != tr[i].lastGenID) { + decreaseRate = true; + } - // Update all values - tr[i].lastGenID = t[i].GetGeneration(); - tr[i].pos = t[i].pos; - tr[i].rot = t[i].rot; - tr[i].vel = t[i].vel; + // Update all values + tr[i].lastGenID = t[i].GetGeneration(); + tr[i].pos = t[i].pos; + tr[i].rot = t[i].rot; + tr[i].vel = t[i].vel; - // Decrease tick rate if needed - if (decreaseRate) { - s[i].updateInterval += 5.0f; - } - else { - s[i].updateInterval = s[i].defaultUpdateInterval; + // Decrease tick rate if needed + if (decreaseRate) { + s[i].updateInterval += 5.0f; + } + else { + s[i].updateInterval = s[i].defaultUpdateInterval; + } } } }); @@ -167,68 +173,74 @@ namespace Framework::World { _world->system("StreamEntities") .kind(flecs::PostUpdate) .interval(tickInterval) - .iter([this](flecs::iter it, Modules::Base::Transform *tr, Modules::Base::Streamer *s, Modules::Base::Streamable *rs) { - for (size_t i = 0; i < it.count(); i++) { - // Skip streamer entities we plan to remove. - if (it.entity(i).get() != nullptr) - continue; - - // Grab all streamable entities. - _allStreamableEntities.each([&](flecs::entity e, Modules::Base::Transform &otherTr, Modules::Base::Streamable &otherS) { - // Skip dead entities. - if (!e.is_alive()) - return; - - // Let streamer send an update to self if an event is assigned. - if (e == it.entity(i) && rs[i].GetBaseEvents().selfUpdateProc) { - rs[i].GetBaseEvents().selfUpdateProc(_networkPeer, s[i].guid, e); - return; - } - - // Figure out entity visibility. - const auto id = e.id(); - const auto canSend = _isEntityVisible(it.entity(i), e, tr[i], s[i], rs[i], otherTr, otherS); - const auto map_it = s[i].entities.find(id); - - // Entity is already known to this streamer. - if (map_it != s[i].entities.end()) { - // If we can't stream an entity anymore, despawn it - if (!canSend) { - s[i].entities.erase(map_it); - if (otherS.GetBaseEvents().despawnProc) - otherS.GetBaseEvents().despawnProc(_networkPeer, s[i].guid, e); + .run([this](flecs::iter &it) { + while (it.next()) { + const auto tr = it.field(0); + const auto s = it.field(1); + const auto rs = it.field(2); + + for (auto i : it) { + // Skip streamer entities we plan to remove. + if (it.entity(i).get() != nullptr) + continue; + + // Grab all streamable entities. + _allStreamableEntities.each([&](flecs::entity e, Modules::Base::Transform &otherTr, Modules::Base::Streamable &otherS) { + // Skip dead entities. + if (!e.is_alive()) + return; + + // Let streamer send an update to self if an event is assigned. + if (e == it.entity(i) && rs[i].GetBaseEvents().selfUpdateProc) { + rs[i].GetBaseEvents().selfUpdateProc(_networkPeer, s[i].guid, e); + return; } - // otherwise we do regular updates - else if (rs[i].owner != otherS.owner) { - auto &data = map_it->second; - if (static_cast(Utils::Time::GetTime()) - data.lastUpdate > otherS.updateInterval) { - if (otherS.GetBaseEvents().updateProc) - otherS.GetBaseEvents().updateProc(_networkPeer, s[i].guid, e); - data.lastUpdate = static_cast(Utils::Time::GetTime()); + // Figure out entity visibility. + const auto id = e.id(); + const auto canSend = _isEntityVisible(it.entity(i), e, tr[i], s[i], rs[i], otherTr, otherS); + const auto map_it = s[i].entities.find(id); + + // Entity is already known to this streamer. + if (map_it != s[i].entities.end()) { + // If we can't stream an entity anymore, despawn it + if (!canSend) { + s[i].entities.erase(map_it); + if (otherS.GetBaseEvents().despawnProc) + otherS.GetBaseEvents().despawnProc(_networkPeer, s[i].guid, e); } - } - else { - auto &data = map_it->second; - - // If the entity is owned by this streamer, we send a full update. - if (static_cast(Utils::Time::GetTime()) - data.lastUpdate > otherS.updateInterval) { - if (otherS.GetBaseEvents().ownerUpdateProc) - otherS.GetBaseEvents().ownerUpdateProc(_networkPeer, s[i].guid, e); - data.lastUpdate = static_cast(Utils::Time::GetTime()); + + // otherwise we do regular updates + else if (rs[i].owner != otherS.owner) { + auto &data = map_it->second; + if (static_cast(Utils::Time::GetTime()) - data.lastUpdate > otherS.updateInterval) { + if (otherS.GetBaseEvents().updateProc) + otherS.GetBaseEvents().updateProc(_networkPeer, s[i].guid, e); + data.lastUpdate = static_cast(Utils::Time::GetTime()); + } + } + else { + auto &data = map_it->second; + + // If the entity is owned by this streamer, we send a full update. + if (static_cast(Utils::Time::GetTime()) - data.lastUpdate > otherS.updateInterval) { + if (otherS.GetBaseEvents().ownerUpdateProc) + otherS.GetBaseEvents().ownerUpdateProc(_networkPeer, s[i].guid, e); + data.lastUpdate = static_cast(Utils::Time::GetTime()); + } } } - } - // this is a new entity, spawn it unless user says otherwise - else if (canSend && otherS.GetBaseEvents().spawnProc) { - if (otherS.GetBaseEvents().spawnProc(_networkPeer, s[i].guid, e)) { - Modules::Base::Streamer::StreamData data; - data.lastUpdate = static_cast(Utils::Time::GetTime()); - s[i].entities[id] = data; + // this is a new entity, spawn it unless user says otherwise + else if (canSend && otherS.GetBaseEvents().spawnProc) { + if (otherS.GetBaseEvents().spawnProc(_networkPeer, s[i].guid, e)) { + Modules::Base::Streamer::StreamData data; + data.lastUpdate = static_cast(Utils::Time::GetTime()); + s[i].entities[id] = data; + } } - } - }); + }); + } } }); diff --git a/code/framework/src/world/types/player.hpp b/code/framework/src/world/types/player.hpp index f4ce878e5..035d85e6c 100644 --- a/code/framework/src/world/types/player.hpp +++ b/code/framework/src/world/types/player.hpp @@ -18,8 +18,8 @@ namespace Framework::World::Archetypes { class PlayerFactory { private: inline void SetupDefaults(flecs::entity e, uint64_t guid) { - const auto streamer = e.get_mut(); - streamer->guid = guid; + auto &streamer = e.ensure(); + streamer.guid = guid; } public: @@ -30,14 +30,15 @@ namespace Framework::World::Archetypes { inline void SetupServer(flecs::entity e, uint64_t guid, uint16_t playerIndex, const std::string &nickname) { SetupDefaults(e, guid); - const auto streamable = e.get_mut(); - streamable->assignOwnerProc = [](flecs::entity, World::Modules::Base::Streamable &) { + auto &streamable = e.ensure(); + streamable.assignOwnerProc = [](flecs::entity, World::Modules::Base::Streamable &) { return true; /* always keep current owner */ }; - const auto streamer = e.get_mut(); - streamer->nickname = nickname; - streamer->playerIndex = playerIndex; + auto &streamer = e.ensure(); + streamer.nickname = nickname; + streamer.playerIndex = playerIndex; + streamer.guid = guid; } }; } // namespace Framework::World::Archetypes diff --git a/code/framework/src/world/types/streaming.hpp b/code/framework/src/world/types/streaming.hpp index f870278cf..09875fc4d 100644 --- a/code/framework/src/world/types/streaming.hpp +++ b/code/framework/src/world/types/streaming.hpp @@ -18,9 +18,9 @@ namespace Framework::World::Archetypes { inline void SetupDefaults(flecs::entity e, uint64_t guid) { e.add(); - const auto streamable = e.get_mut(); - streamable->owner = guid; - streamable->defaultUpdateInterval = CoreModules::GetTickRate() * 1000.0f; // we need ms here + auto &streamable = e.ensure(); + streamable.owner = guid; + streamable.defaultUpdateInterval = CoreModules::GetTickRate() * 1000.0f; // we need ms here e.add(); } @@ -29,14 +29,17 @@ namespace Framework::World::Archetypes { inline void SetupClient(flecs::entity e, uint64_t guid) { SetupDefaults(e, guid); - const auto streamable = e.get_mut(); + auto& streamable = e.ensure(); Framework::World::Modules::Base::SetupClientEmitters(streamable); + + auto ass = e.get_mut(); + (void)ass; } inline void SetupServer(flecs::entity e, uint64_t guid) { SetupDefaults(e, guid); - const auto streamable = e.get_mut(); + auto& streamable = e.ensure(); Framework::World::Modules::Base::SetupServerEmitters(streamable); } }; diff --git a/vendors/flecs/.editorconfig b/vendors/flecs/.editorconfig index bc4f2de74..d6be9e311 100644 --- a/vendors/flecs/.editorconfig +++ b/vendors/flecs/.editorconfig @@ -7,3 +7,6 @@ indent_size = 4 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = false + +[*.yml] +indent_size = 2 diff --git a/vendors/flecs/.gitattributes b/vendors/flecs/.gitattributes new file mode 100644 index 000000000..5a0d5e480 --- /dev/null +++ b/vendors/flecs/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto eol=lf diff --git a/vendors/flecs/.github/workflows/ci.yml b/vendors/flecs/.github/workflows/ci.yml index 258827cb0..573df4127 100644 --- a/vendors/flecs/.github/workflows/ci.yml +++ b/vendors/flecs/.github/workflows/ci.yml @@ -2,7 +2,18 @@ name: CI on: [ push, pull_request ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: + typos: + runs-on: ubuntu-latest + name: typos + steps: + - uses: actions/checkout@v4 + - uses: crate-ci/typos@master + build-linux: runs-on: ubuntu-latest timeout-minutes: 30 @@ -23,7 +34,7 @@ jobs: bake --strict build-macos: - runs-on: macOS-latest + runs-on: macos-latest timeout-minutes: 30 strategy: fail-fast: false @@ -135,9 +146,9 @@ jobs: - { os: ubuntu-20.04, cc: gcc-8, cxx: g++-8 } - { os: ubuntu-20.04, cc: gcc-9, cxx: g++-9 } - { os: ubuntu-20.04, cc: gcc-10, cxx: g++-10 } - - { os: ubuntu-20.04, cc: gcc-11, cxx: g++-11 } + - { os: ubuntu-latest, cc: gcc-11, cxx: g++-11 } - { os: ubuntu-latest, cc: gcc-12, cxx: g++-12 } - - { os: ubuntu-latest, cc: gcc-13, cxx: g++-13 } + - { os: ubuntu-24.04, cc: gcc-13, cxx: g++-13 } - { os: ubuntu-20.04, cc: clang-8, cxx: clang++-8 } - { os: ubuntu-20.04, cc: clang-9, cxx: clang++-9 } - { os: ubuntu-20.04, cc: clang-10, cxx: clang++-10 } @@ -187,13 +198,13 @@ jobs: bake examples/os_api --strict --cfg release build-cmake: - needs: build-linux + needs: [build-linux, build-macos] runs-on: ${{ matrix.os }} timeout-minutes: 30 strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macOS-latest ] + os: [ ubuntu-latest, macos-13, macos-latest ] steps: - uses: actions/checkout@v4 @@ -229,10 +240,8 @@ jobs: strategy: matrix: - toolset: [default, v141, v142, clang-cl] + toolset: [default, v142, clang-cl] include: - - toolset: v141 - toolset_option: -T"v141" - toolset: v142 toolset_option: -T"v142" - toolset: clang-cl @@ -286,6 +295,34 @@ jobs: working-directory: meson_build run: | meson compile + + build-bazel-linux: + needs: build-linux + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - uses: actions/checkout@v4 + - name: build flecs + run: bazel build //:flecs + - name: build examples + run: bazel build //examples/... + - name: run tests + run: bazel test //test/... + + build-bazel-windows: + needs: build-windows + runs-on: windows-latest + timeout-minutes: 30 + + steps: + - uses: actions/checkout@v4 + - name: build flecs + run: bazel build //:flecs + - name: build examples + run: bazel build //examples/... + - name: run tests + run: bazel test //test/... build-custom: needs: build-linux @@ -339,41 +376,21 @@ jobs: bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_MODULE bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_MODULE - - name: FLECS_SNAPSHOT - run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_SNAPSHOT - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_SNAPSHOT - - name: FLECS_STATS run: | bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_STATS bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_STATS - - name: FLECS_PARSER - run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PARSER - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PARSER - - - name: FLECS_PLECS + - name: FLECS_SCRIPT run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PLECS - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PLECS + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_SCRIPT + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_SCRIPT - name: FLECS_META run: | bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_META bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_META - - name: FLECS_META_C - run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_META_C - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_META_C - - - name: FLECS_EXPR - run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_EXPR - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_EXPR - - name: FLECS_JSON run: | bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_JSON @@ -384,11 +401,6 @@ jobs: bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_DOC bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_DOC - - name: FLECS_COREDOC - run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_COREDOC - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_COREDOC - - name: FLECS_LOG run: | bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_LOG @@ -441,7 +453,29 @@ jobs: steps: - uses: actions/checkout@v4 - name: build flecs - run: ${{ matrix.compiler }} flecs.c --shared -fPIC -pedantic -Wall -Wextra -Wno-unused-parameter -Werror -Wshadow -Wconversion -Wno-missing-field-initializers + run: ${{ matrix.compiler }} distr/flecs.c --shared -fPIC -pedantic -Wall -Wextra -Wno-unused-parameter -Werror -Wshadow -Wconversion -Wno-missing-field-initializers + + test-amalgamated: + needs: build-linux + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v4 + + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake rebuild + + - name: git diff check + run: git diff --quiet build-scan-build: needs: build-linux @@ -474,7 +508,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macOS-latest ] + os: [ ubuntu-latest, macos-13, macos-latest ] steps: - uses: actions/checkout@v4 @@ -493,8 +527,11 @@ jobs: - name: build flecs run: bake --strict - - name: test api - run: bake run test/api -- -j 8 + - name: test core + run: bake run test/core -- -j 8 + + - name: test query + run: bake run test/query -- -j 8 - name: test addons run: bake run test/addons -- -j 8 @@ -502,6 +539,9 @@ jobs: - name: test meta run: bake run test/meta -- -j 8 + - name: test script + run: bake run test/script -- -j 8 + - name: test collections run: bake run test/collections -- -j 8 @@ -515,9 +555,9 @@ jobs: target: - { os: ubuntu-20.04, cc: gcc-9, cxx: g++-9 } - { os: ubuntu-20.04, cc: gcc-10, cxx: g++-10 } - - { os: ubuntu-20.04, cc: gcc-11, cxx: g++-11 } + - { os: ubuntu-22.04, cc: gcc-11, cxx: g++-11 } - { os: ubuntu-latest, cc: gcc-12, cxx: g++-12 } - - { os: ubuntu-latest, cc: gcc-13, cxx: g++-13 } + - { os: ubuntu-24.04, cc: gcc-13, cxx: g++-13 } - { os: ubuntu-20.04, cc: clang-8, cxx: clang++-8 } - { os: ubuntu-20.04, cc: clang-9, cxx: clang++-9 } - { os: ubuntu-20.04, cc: clang-10, cxx: clang++-10 } @@ -555,7 +595,7 @@ jobs: run: bake --strict - name: test c++ - run: bake run test/cpp_api -- -j 8 + run: bake run test/cpp -- -j 8 test-cpp-macos: needs: [build-macos] @@ -565,11 +605,9 @@ jobs: fail-fast: false matrix: os: - - { version: macOS-11, xcode: '11.7' } - - { version: macOS-11, xcode: '12.4' } - - { version: macOS-11, xcode: '13.0' } - - { version: macOS-12, xcode: '13.1' } - - { version: macOS-12, xcode: '14.0' } + - { version: macos-13, xcode: '14.3.1' } + - { version: macos-14, xcode: '15.4' } + - { version: macos-15, xcode: '16.0' } env: CC: clang @@ -596,7 +634,7 @@ jobs: run: bake --strict - name: test c++ - run: bake run test/cpp_api -- -j 8 + run: bake run test/cpp -- -j 8 test-windows: needs: build-windows @@ -619,8 +657,8 @@ jobs: - name: build flecs run: bake/bake - - name: test api - run: bake/bake run test\api -- -j 8 + - name: test core + run: bake/bake run test\core -- -j 8 - name: test addons run: bake/bake run test\addons -- -j 8 @@ -628,11 +666,14 @@ jobs: - name: test meta run: bake/bake run test\meta -- -j 8 + - name: test script + run: bake/bake run test\script -- -j 8 + - name: test collections run: bake/bake run test\collections -- -j 8 - name: test c++ - run: bake/bake run test\cpp_api -- -j 8 + run: bake/bake run test\cpp -- -j 8 test-msys: runs-on: windows-latest @@ -641,15 +682,19 @@ jobs: fail-fast: false matrix: include: - - { sys: mingw64 } - - { sys: mingw32 } - - { sys: ucrt64 } - - { sys: clang64 } + - { sys: mingw64, cc: gcc, cxx: g++ } + - { sys: mingw32, cc: gcc, cxx: g++ } + - { sys: ucrt64, cc: gcc, cxx: g++ } + - { sys: clang64, cc: clang, cxx: clang++ } defaults: run: shell: msys2 {0} + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} + steps: - uses: actions/checkout@v4 - uses: msys2/setup-msys2@v2 @@ -671,10 +716,13 @@ jobs: bake/bake setup - name: build flecs - run: bake/bake --strict + run: bake/bake --strict --trace + + - name: test core + run: bake/bake run test/core -- -j 8 - - name: test api - run: bake/bake run test/api -- -j 8 + - name: test query + run: bake/bake run test/query -- -j 8 - name: test addons run: bake/bake run test/addons -- -j 8 @@ -682,20 +730,47 @@ jobs: - name: test meta run: bake/bake run test/meta -- -j 8 + - name: test script + run: bake/bake run test/script -- -j 8 + - name: test collections run: bake/bake run test/collections -- -j 8 - name: test c++ - run: bake/bake run test/cpp_api -- -j 8 + run: bake/bake run test/cpp -- -j 8 + + test-sanitized-core: + needs: [ build-linux ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-20.04 ] + + steps: + - uses: actions/checkout@v4 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict - test-sanitized-api: + - name: run tests + run: | + bake run test/core --cfg sanitize -- -j 8 + + test-sanitized-query: needs: [ build-linux ] runs-on: ${{ matrix.os }} timeout-minutes: 30 strategy: fail-fast: false matrix: - os: [ ubuntu-latest ] + os: [ ubuntu-20.04 ] steps: - uses: actions/checkout@v4 @@ -710,7 +785,7 @@ jobs: - name: run tests run: | - bake run test/api --cfg sanitize -- -j 8 + bake run test/query --cfg sanitize -- -j 8 test-sanitized-addons: needs: [ build-linux ] @@ -719,7 +794,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest ] + os: [ ubuntu-20.04 ] steps: - uses: actions/checkout@v4 @@ -743,7 +818,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest ] + os: [ ubuntu-20.04 ] steps: - uses: actions/checkout@v4 @@ -760,6 +835,30 @@ jobs: run: | bake run test/meta --cfg sanitize -- -j 8 + test-sanitized-script: + needs: [ build-linux ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-20.04 ] + + steps: + - uses: actions/checkout@v4 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: run tests + run: | + bake run test/script --cfg sanitize -- -j 8 + test-sanitized-collections: needs: [ build-linux ] runs-on: ${{ matrix.os }} @@ -767,7 +866,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest ] + os: [ ubuntu-20.04 ] steps: - uses: actions/checkout@v4 @@ -784,14 +883,14 @@ jobs: run: | bake run test/collections --cfg sanitize -- -j 8 - test-sanitized-cpp_api: + test-sanitized-cpp: needs: [ build-linux ] runs-on: ${{ matrix.os }} timeout-minutes: 30 strategy: fail-fast: false matrix: - os: [ ubuntu-latest ] + os: [ ubuntu-20.04 ] steps: - uses: actions/checkout@v4 @@ -806,7 +905,7 @@ jobs: - name: run tests run: | - bake run test/cpp_api --cfg sanitize -- -j 8 + bake run test/cpp --cfg sanitize -- -j 8 test-cmake: needs: [ build-linux, build-macos ] @@ -815,7 +914,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macOS-latest ] + os: [ ubuntu-latest, macos-13, macos-latest ] steps: - uses: actions/checkout@v4 diff --git a/vendors/flecs/.github/workflows/pages.yml b/vendors/flecs/.github/workflows/pages.yml index 504c999fd..735d0d6a5 100644 --- a/vendors/flecs/.github/workflows/pages.yml +++ b/vendors/flecs/.github/workflows/pages.yml @@ -30,21 +30,26 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - + - name: Setup Pages uses: actions/configure-pages@v4 - + + - name: Install Dependencies + run: | + sudo apt install --yes graphviz + wget https://github.com/doxygen/doxygen/releases/download/Release_1_10_0/doxygen-1.10.0.linux.bin.tar.gz + tar -xzf ./doxygen-1.10.0.linux.bin.tar.gz + - name: Doxygen - uses: mattnotmitt/doxygen-action@v1.9.5 - with: - doxyfile-path: 'docs/cfg/Doxyfile' - + run: | + ./doxygen-1.10.0/bin/doxygen docs/cfg/Doxyfile + - name: Upload artifact - uses: actions/upload-pages-artifact@v2 + uses: actions/upload-pages-artifact@v3 with: # Upload entire repository path: 'docs/html' - + - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v3 + uses: actions/deploy-pages@v4 diff --git a/vendors/flecs/.gitignore b/vendors/flecs/.gitignore index ceb92aac8..0d0a8bebd 100644 --- a/vendors/flecs/.gitignore +++ b/vendors/flecs/.gitignore @@ -7,12 +7,9 @@ gcov *.pdb deps cmake-build-debug -bazel-bin -bazel-flecs -bazel-bazel -bazel-genfiles -bazel-out -bazel-testlogs +bazel-* **/CMakeFiles/* .vs out +docs/html +MODULE.bazel.lock \ No newline at end of file diff --git a/vendors/flecs/BUILD b/vendors/flecs/BUILD index 760c45559..8aca0fecc 100644 --- a/vendors/flecs/BUILD +++ b/vendors/flecs/BUILD @@ -1,7 +1,7 @@ - cc_library( name = "flecs", visibility = ["//visibility:public"], + defines = ["flecs_EXPORTS"], srcs = glob(["src/**/*.c", "src/**/*.h", "src/**/*.inl"]), hdrs = glob(["include/**/*.h", "include/**/*.hpp", "include/**/*.inl"]), diff --git a/vendors/flecs/LICENSE b/vendors/flecs/LICENSE index 8d7b5a07a..b190649a9 100644 --- a/vendors/flecs/LICENSE +++ b/vendors/flecs/LICENSE @@ -2,6 +2,8 @@ MIT License Copyright (c) 2019 Sander Mertens +Portions Copyright (c) Meta Platforms, Inc. and affiliates + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights diff --git a/vendors/flecs/MODULE.bazel b/vendors/flecs/MODULE.bazel new file mode 100644 index 000000000..d1a2765b0 --- /dev/null +++ b/vendors/flecs/MODULE.bazel @@ -0,0 +1,53 @@ +"A fast entity component system (ECS) for C & C++" +module(name = "flecs") + +git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") + +git_repository( + name = "bake", + remote = "https://github.com/SanderMertens/bake.git", + commit = "10c02123d6e3e200578128d73c3fa3bb996dff4f", + build_file_content = """ +cc_library( + name = "driver-test", + visibility = ["//visibility:public"], + deps = [":util", ":bake"], + defines = ["__BAKE__", "bake_test_EXPORTS"], + + srcs = glob(["drivers/test/src/**/*.c", "drivers/test/src/**/*.h"]), + hdrs = glob(["drivers/test/include/**/*.h"]), + includes = ["drivers/test/include"], +) + +cc_library( + name = "bake", + visibility = ["//visibility:public"], + deps = [":util"], + + srcs = glob(["src/*.c", "src/*.h"]), + hdrs = glob(["include/*.h", "include/bake/*.h"]), + includes = ["include"], +) + +cc_library( + name = "util", + visibility = ["//visibility:public"], + defines = ["__BAKE__", "_XOPEN_SOURCE=600", "bake_util_EXPORTS", "UT_IMPL"], + + linkopts = select({ + "@bazel_tools//src/conditions:windows": ["Dbghelp.lib", "Shell32.lib", "Shlwapi.lib"], + "//conditions:default": ["-lrt -lpthread -ldl"], + }), + + srcs = glob(["util/src/*.c"]) + select({ + "@bazel_tools//src/conditions:windows": glob(["util/src/win/*.c"]), + "//conditions:default": glob(["util/src/posix/*.c"]), + }), + hdrs = glob(["util/include/*.h", "util/include/bake-util/*.h"]) + select({ + "@bazel_tools//src/conditions:windows": glob(["util/include/bake-util/win/*.h"]), + "//conditions:default": glob(["util/include/bake-util/posix/*.h"]), + }), + includes = ["util/include"], +) +""", +) diff --git a/vendors/flecs/README.md b/vendors/flecs/README.md index f5b6ae62b..cd9d82e4b 100644 --- a/vendors/flecs/README.md +++ b/vendors/flecs/README.md @@ -2,7 +2,7 @@ [![Version](https://img.shields.io/github/v/release/sandermertens/flecs?include_prereleases&style=for-the-badge)](https://github.com/SanderMertens/flecs/releases) [![MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](https://github.com/SanderMertens/flecs/blob/master/LICENSE) -[![Documentation](https://img.shields.io/badge/docs-flecs-blue?style=for-the-badge&color=blue)](https://www.flecs.dev/flecs/md_docs_Docs.html) +[![Documentation](https://img.shields.io/badge/docs-flecs-blue?style=for-the-badge&color=blue)](https://www.flecs.dev/flecs/md_docs_2Docs.html) [![actions](https://img.shields.io/github/actions/workflow/status/SanderMertens/flecs/ci.yml?branch=master&style=for-the-badge)](https://github.com/SanderMertens/flecs/actions?query=workflow%3ACI) [![Discord Chat](https://img.shields.io/discord/633826290415435777.svg?style=for-the-badge&color=%235a64f6)](https://discord.gg/BEzP5Rgrrp) @@ -10,21 +10,20 @@ Flecs is a fast and lightweight Entity Component System that lets you build game - Fast and [portable](#language-bindings) zero dependency [C99 API](https://www.flecs.dev/flecs/group__c.html) - Modern type-safe [C++11 API](https://www.flecs.dev/flecs/group__cpp.html) that doesn't use STL containers -- First open source ECS with full support for [Entity Relationships](https://www.flecs.dev/flecs/md_docs_Relationships.html)! -- Fast native support for [hierarchies](https://www.flecs.dev/flecs/md_docs_Relationships.html#autotoc_md277) and [prefabs](https://www.flecs.dev/flecs/md_docs_Relationships.html#autotoc_md305) +- First open source ECS with full support for [Entity Relationships](https://www.flecs.dev/flecs/md_docs_2Relationships.html)! +- Fast native support for [hierarchies](https://www.flecs.dev/flecs/md_docs_2Relationships.html#the-childof-relationship) and [prefabs](https://www.flecs.dev/flecs/md_docs_2Relationships.html#the-isa-relationship) - Code base that builds in less than 5 seconds - Runs [in the browser](https://flecs.dev/city) without modifications with emscripten - Cache friendly [archetype/SoA storage](https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9) that can process millions of entities every frame -- Supports entities with hundreds of components and applications with tens of thousands of archetypes - Automatic component registration that works out of the box across shared libraries/DLLs - Write free functions with [queries](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/queries/basics) or run code automatically in [systems](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/systems/pipeline) - Run games on multiple CPU cores with a fast lockless scheduler -- Verified on all major compilers and platforms with [CI](https://github.com/SanderMertens/flecs/actions) running more than 6000 tests +- Verified on all major compilers and platforms with [CI](https://github.com/SanderMertens/flecs/actions) running more than 8000 tests - Integrated [reflection framework](https://www.flecs.dev/flecs/group__c__addons__meta.html) with [JSON serializer](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection/basics_json) and support for [runtime components](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection/runtime_component) - [Unit annotations](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection/units) for components -- Powerful [query language](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules) with support for [joins](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules/setting_variables) and [inheritance](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules/component_inheritance) +- Powerful [query language](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/queries) with support for [joins](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/queries/setting_variables) and [inheritance](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/queries/component_inheritance) - [Statistics addon](https://www.flecs.dev/flecs/group__c__addons__stats.html) for profiling ECS performance -- A web-based UI for monitoring & controlling your apps ([demo](https://flecs.dev/explorer), [code](https://github.com/flecs-hub/explorer)): +- A web-based UI for monitoring & controlling your apps: [![Flecs Explorer](docs/img/explorer.png)](https://flecs.dev/explorer) @@ -38,22 +37,6 @@ ECS is a way of organizing code and data that lets you build games that are larg For more information, check the [ECS FAQ](https://github.com/SanderMertens/ecs-faq)! -## Try it out! -The [Flecs playground](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js) lets you try Flecs without writing any C/C++ code! - -[![Flecs playground](docs/img/playground.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js) - -To learn how to use the playground, check the [Flecs Script Tutorial](https://www.flecs.dev/flecs/md_docs_FlecsScriptTutorial.html). - -## Documentation -- [Quickstart](https://www.flecs.dev/flecs/md_docs_Quickstart.html) -- [FAQ](https://www.flecs.dev/flecs/md_docs_FAQ.html) -- [Examples](https://github.com/SanderMertens/flecs/tree/master/examples) -- [All Documentation](https://www.flecs.dev/flecs/md_docs_Docs.html) - -## Performance -For a list of regularly tracked benchmarks, see the [ECS Benchmark](https://github.com/SanderMertens/ecs_benchmark) project. - ## Show me the code! C99 example: ```c @@ -62,8 +45,8 @@ typedef struct { } Position, Velocity; void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i++) { p[i].x += v[i].x; @@ -79,9 +62,9 @@ int main(int argc, char *argv[]) { ECS_SYSTEM(ecs, Move, EcsOnUpdate, Position, Velocity); - ecs_entity_t e = ecs_new_id(ecs); - ecs_set(ecs, e, Position, {10, 20}); - ecs_set(ecs, e, Velocity, {1, 2}); + ecs_entity_t e = ecs_insert(ecs, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); while (ecs_progress(ecs, 0)) { } } @@ -108,7 +91,7 @@ int main(int argc, char *argv[]) { }); auto e = ecs.entity() - .set([](Position& p, Velocity& v) { + .insert([](Position& p, Velocity& v) { p = {10, 20}; v = {1, 2}; }); @@ -120,65 +103,54 @@ int main(int argc, char *argv[]) { ## Projects using Flecs If you have a project you'd like to share, let me know on [Discord](https://discord.gg/BEzP5Rgrrp)! +### [Hytale](https://hytale.com/) +[![Hytale](docs/img/projects/hytale.png)](https://hytale.com/) + +> We knew that we wanted to build Hytale around an Entity-Component-System (ECS). When we analyzed the options, FLECS rose to the top. FLECS provides the backbone of the Hytale Game Engine. Its flexibility has allowed us to build highly varied gameplay while supporting our vision for empowering Creators. + +-- Dann Webster, Hypixel studios + ### Tempest Rising -https://store.steampowered.com/app/1486920/Tempest_Rising/ [![Tempest Rising](docs/img/projects/tempest_rising.png)](https://store.steampowered.com/app/1486920/Tempest_Rising/) -### Territory Control -https://store.steampowered.com/app/690290/Territory_Control_2/ -[![image](docs/img/projects/territory_control.jpeg)](https://store.steampowered.com/app/690290/Territory_Control_2/) +### Territory Control 2 +[![image](docs/img/projects/territory_control.jpg)](https://store.steampowered.com/app/690290/Territory_Control_2/) + +### Age of Respair +[![image](docs/img/projects/age_of_respair.png)](https://store.steampowered.com/app/3164360/Age_of_Respair/) ### The Forge -https://github.com/ConfettiFX/The-Forge [![image](docs/img/projects/the_forge.jpg)](https://github.com/ConfettiFX/The-Forge) +### Extermination Shock +[![image](docs/img/projects/extermination_shock.png)](https://store.steampowered.com/app/2510820/Extermination_Shock/) + +### Tome Tumble Tournament +[![image](docs/img/projects/tome_tumble.png)](https://terzalo.itch.io/tome-tumble-tournament) + +### Hyperion Minecraft Server +[![image](docs/img/projects/hyperion.png)](https://github.com/andrewgazelka/hyperion) + ### Sol Survivor -https://nicok.itch.io/sol-survivor-demo [![image](docs/img/projects/sol_survivor.png)](https://nicok.itch.io/sol-survivor-demo) ### Equilibrium Engine -https://github.com/clibequilibrium/EquilibriumEngine [![image](docs/img/projects/equilibrium_engine.png)](https://github.com/clibequilibrium/EquilibriumEngine) -### Gravitas -https://thepunkcollective.itch.io/gravitas -[![image](docs/img/projects/gravitas.png)](https://thepunkcollective.itch.io/gravitas) - ### After Sun -https://github.com/foxnne/aftersun [![image](docs/img/projects/after_sun.png)](https://github.com/foxnne/aftersun) -### Tower defense (open source demo) -https://www.flecs.dev/tower_defense/etc ([repository](https://github.com/SanderMertens/tower_defense)) -![image](docs/img/projects/tower_defense.png) - -### Procedural City (open source demo) -https://www.flecs.dev/city ([repository](https://github.com/flecs-hub/city)) -![image](docs/img/projects/city.png) - -## Resources - -### Resources provided by the community :heart: -- [Unreal Minimum Viable Flecs Project](https://github.com/PreyK/Unreal-Minimum-Viable-Flecs) -- [Bgfx/Imgui module](https://github.com/flecs-hub/flecs-systems-bgfx/tree/bgfx_imgui) -- [Tower defense example](https://gist.github.com/oldmanauz/b4ced44737bf9d248233538fa06a989e) -- [Unreal + Flecs example](https://github.com/PreyK/Unreal-Minimum-Viable-Flecs) -- [Building a space battle with Flecs in UE4](https://twitter.com/ajmmertens/status/1361070033334456320) -- [Flecs + SDL + Web ASM example](https://github.com/HeatXD/flecs_web_demo) ([live demo](https://heatxd.github.io/flecs_web_demo/)) -- [Flecs + Raylib example](https://github.com/Lexxicon/FlecsRaylib) -- [Flecs + gunslinger example](https://github.com/MrFrenik/gs_examples/blob/main/ex_demos/flecs/source/main.c) -- [Flecs based 3D game engine with editor](https://bit.ly/3T9cc1o) - -### Flecs around the web -- [Discord](https://discord.gg/BEzP5Rgrrp) -- [Medium](https://ajmmertens.medium.com) -- [Twitter](https://twitter.com/ajmmertens) -- [Reddit](https://www.reddit.com/r/flecs) +### Flecs Demo's +https://github.com/SanderMertens/tower_defense +[![Tower Defense](docs/img/projects/tower_defense.png)](https://www.flecs.dev/tower_defense/etc) + +https://github.com/flecs-hub/city +[![City](docs/img/projects/city.png)](https://www.flecs.dev/city) ## Flecs Hub [Flecs Hub](https://github.com/flecs-hub) is a collection of repositories that show how Flecs can be used to build game systems like input handling, hierarchical transforms and rendering. -Module | Description +Module | Description ------------|------------------ [flecs.components.cglm](https://github.com/flecs-hub/flecs-components-cglm) | Component registration for cglm (math) types [flecs.components.input](https://github.com/flecs-hub/flecs-components-input) | Components that describe keyboard and mouse input @@ -195,12 +167,12 @@ Module | Description ## Language bindings The following language bindings have been developed with Flecs! Note that these are projects built and maintained by helpful community members, and may not always be up to date with the latest commit from master! - C#: - - [flecs-hub/flecs-cs](https://github.com/flecs-hub/flecs-cs) - [BeanCheeseBurrito/Flecs.NET](https://github.com/BeanCheeseBurrito/Flecs.NET) - Rust: - - [flecs-rs](https://github.com/jazzay/flecs-rs) + - [Flecs-Rust](https://github.com/Indra-db/Flecs-Rust) - [flecs-polyglot](https://github.com/flecs-hub/flecs-polyglot) -- Zig: + - [flecs-rs](https://github.com/jazzay/flecs-rs) +- Zig: - [michal-z/zig-gamedev](https://github.com/michal-z/zig-gamedev/tree/main/libs/zflecs) - [foxnne/zig-flecs](https://github.com/foxnne/zig-flecs) - [prime31/zig-flecs](https://github.com/prime31/zig-flecs) diff --git a/vendors/flecs/WORKSPACE b/vendors/flecs/WORKSPACE index 34536ebfa..3f82cc7b4 100644 --- a/vendors/flecs/WORKSPACE +++ b/vendors/flecs/WORKSPACE @@ -49,4 +49,3 @@ cc_library( ) """ ) - diff --git a/vendors/flecs/WORKSPACE.bzlmod b/vendors/flecs/WORKSPACE.bzlmod new file mode 100644 index 000000000..00614f5b0 --- /dev/null +++ b/vendors/flecs/WORKSPACE.bzlmod @@ -0,0 +1 @@ +# Overrides WORKSPACE when building with bzlmod \ No newline at end of file diff --git a/vendors/flecs/_typos.toml b/vendors/flecs/_typos.toml new file mode 100644 index 000000000..c1e798008 --- /dev/null +++ b/vendors/flecs/_typos.toml @@ -0,0 +1,17 @@ +files.extend-exclude = ["distr/*"] + +# typos can't handle "2nd" as a word (yet?) +default.extend-ignore-identifiers-re = ["_2nd"] + +[default.extend-words] +ser = "ser" # serialization +thr = "thr" # thread + +[default.extend-identifiers] +ba = "ba" # block_allocator +ecs_eis = "ecs_eis" # entity index store (entity_index.h) +numer = "numer" # numerator (posix_impl.inl) +ot = "ot" # Opaque Type or Other Table +Fo = "Fo" # used in test cases/examples +UE4 = "UE4" +PNGs = "PNGs" diff --git a/vendors/flecs/flecs.c b/vendors/flecs/distr/flecs.c similarity index 64% rename from vendors/flecs/flecs.c rename to vendors/flecs/distr/flecs.c index 73a3b545b..29bb00275 100644 --- a/vendors/flecs/flecs.c +++ b/vendors/flecs/distr/flecs.c @@ -109,12 +109,12 @@ void flecs_entity_index_remove( uint64_t entity); /* Set generation of entity */ -void flecs_entity_index_set_generation( +void flecs_entity_index_make_alive( ecs_entity_index_t *index, uint64_t entity); /* Get current generation of entity */ -uint64_t flecs_entity_index_get_generation( +uint64_t flecs_entity_index_get_alive( const ecs_entity_index_t *index, uint64_t entity); @@ -167,14 +167,6 @@ void flecs_entity_index_clear( const uint64_t* flecs_entity_index_ids( const ecs_entity_index_t *index); -void flecs_entity_index_copy( - ecs_entity_index_t *dst, - const ecs_entity_index_t *src); - -void flecs_entity_index_restore( - ecs_entity_index_t *dst, - const ecs_entity_index_t *src); - #define ecs_eis(world) (&((world)->store.entity_index)) #define flecs_entities_init(world) flecs_entity_index_init(&world->allocator, ecs_eis(world)) #define flecs_entities_fini(world) flecs_entity_index_fini(ecs_eis(world)) @@ -183,8 +175,8 @@ void flecs_entity_index_restore( #define flecs_entities_get_any(world, entity) flecs_entity_index_get_any(ecs_eis(world), entity) #define flecs_entities_ensure(world, entity) flecs_entity_index_ensure(ecs_eis(world), entity) #define flecs_entities_remove(world, entity) flecs_entity_index_remove(ecs_eis(world), entity) -#define flecs_entities_set_generation(world, entity) flecs_entity_index_set_generation(ecs_eis(world), entity) -#define flecs_entities_get_generation(world, entity) flecs_entity_index_get_generation(ecs_eis(world), entity) +#define flecs_entities_make_alive(world, entity) flecs_entity_index_make_alive(ecs_eis(world), entity) +#define flecs_entities_get_alive(world, entity) flecs_entity_index_get_alive(ecs_eis(world), entity) #define flecs_entities_is_alive(world, entity) flecs_entity_index_is_alive(ecs_eis(world), entity) #define flecs_entities_is_valid(world, entity) flecs_entity_index_is_valid(ecs_eis(world), entity) #define flecs_entities_exists(world, entity) flecs_entity_index_exists(ecs_eis(world), entity) @@ -197,92 +189,6 @@ void flecs_entity_index_restore( #define flecs_entities_not_alive_count(world) flecs_entity_index_not_alive_count(ecs_eis(world)) #define flecs_entities_clear(world) flecs_entity_index_clear(ecs_eis(world)) #define flecs_entities_ids(world) flecs_entity_index_ids(ecs_eis(world)) -#define flecs_entities_copy(dst, src) flecs_entity_index_copy(dst, src) -#define flecs_entities_restore(dst, src) flecs_entity_index_restore(dst, src) - -#endif - -/** - * @file datastructures/stack_allocator.h - * @brief Stack allocator. - */ - -#ifndef FLECS_STACK_ALLOCATOR_H -#define FLECS_STACK_ALLOCATOR_H - -/** Stack allocator for quick allocation of small temporary values */ -#define ECS_STACK_PAGE_SIZE (4096) - -typedef struct ecs_stack_page_t { - void *data; - struct ecs_stack_page_t *next; - int16_t sp; - uint32_t id; -} ecs_stack_page_t; - -typedef struct ecs_stack_t { - ecs_stack_page_t first; - ecs_stack_page_t *tail_page; - ecs_stack_cursor_t *tail_cursor; -#ifdef FLECS_DEBUG - int32_t cursor_count; -#endif -} ecs_stack_t; - -FLECS_DBG_API -void flecs_stack_init( - ecs_stack_t *stack); - -FLECS_DBG_API -void flecs_stack_fini( - ecs_stack_t *stack); - -FLECS_DBG_API -void* flecs_stack_alloc( - ecs_stack_t *stack, - ecs_size_t size, - ecs_size_t align); - -#define flecs_stack_alloc_t(stack, T)\ - flecs_stack_alloc(stack, ECS_SIZEOF(T), ECS_ALIGNOF(T)) - -#define flecs_stack_alloc_n(stack, T, count)\ - flecs_stack_alloc(stack, ECS_SIZEOF(T) * count, ECS_ALIGNOF(T)) - -FLECS_DBG_API -void* flecs_stack_calloc( - ecs_stack_t *stack, - ecs_size_t size, - ecs_size_t align); - -#define flecs_stack_calloc_t(stack, T)\ - flecs_stack_calloc(stack, ECS_SIZEOF(T), ECS_ALIGNOF(T)) - -#define flecs_stack_calloc_n(stack, T, count)\ - flecs_stack_calloc(stack, ECS_SIZEOF(T) * count, ECS_ALIGNOF(T)) - -FLECS_DBG_API -void flecs_stack_free( - void *ptr, - ecs_size_t size); - -#define flecs_stack_free_t(ptr, T)\ - flecs_stack_free(ptr, ECS_SIZEOF(T)) - -#define flecs_stack_free_n(ptr, T, count)\ - flecs_stack_free(ptr, ECS_SIZEOF(T) * count) - -void flecs_stack_reset( - ecs_stack_t *stack); - -FLECS_DBG_API -ecs_stack_cursor_t* flecs_stack_get_cursor( - ecs_stack_t *stack); - -FLECS_DBG_API -void flecs_stack_restore_cursor( - ecs_stack_t *stack, - ecs_stack_cursor_t *cursor); #endif @@ -365,136 +271,7 @@ void flecs_bitset_swap( #endif /** - * @file switch_list.h - * @brief Interleaved linked list for storing mutually exclusive values. - */ - -#ifndef FLECS_SWITCH_LIST_H -#define FLECS_SWITCH_LIST_H - - -typedef struct ecs_switch_header_t { - int32_t element; /* First element for value */ - int32_t count; /* Number of elements for value */ -} ecs_switch_header_t; - -typedef struct ecs_switch_node_t { - int32_t next; /* Next node in list */ - int32_t prev; /* Prev node in list */ -} ecs_switch_node_t; - -struct ecs_switch_t { - ecs_map_t hdrs; /* map */ - ecs_vec_t nodes; /* vec */ - ecs_vec_t values; /* vec */ -}; - -/** Init new switch. */ -FLECS_DBG_API -void flecs_switch_init( - ecs_switch_t* sw, - ecs_allocator_t *allocator, - int32_t elements); - -/** Fini switch. */ -FLECS_DBG_API -void flecs_switch_fini( - ecs_switch_t *sw); - -/** Remove all values. */ -FLECS_DBG_API -void flecs_switch_clear( - ecs_switch_t *sw); - -/** Add element to switch, initialize value to 0 */ -FLECS_DBG_API -void flecs_switch_add( - ecs_switch_t *sw); - -/** Set number of elements in switch list */ -FLECS_DBG_API -void flecs_switch_set_count( - ecs_switch_t *sw, - int32_t count); - -/** Get number of elements */ -FLECS_DBG_API -int32_t flecs_switch_count( - ecs_switch_t *sw); - -/** Ensure that element exists. */ -FLECS_DBG_API -void flecs_switch_ensure( - ecs_switch_t *sw, - int32_t count); - -/** Add n elements. */ -FLECS_DBG_API -void flecs_switch_addn( - ecs_switch_t *sw, - int32_t count); - -/** Set value of element. */ -FLECS_DBG_API -void flecs_switch_set( - ecs_switch_t *sw, - int32_t element, - uint64_t value); - -/** Remove element. */ -FLECS_DBG_API -void flecs_switch_remove( - ecs_switch_t *sw, - int32_t element); - -/** Get value for element. */ -FLECS_DBG_API -uint64_t flecs_switch_get( - const ecs_switch_t *sw, - int32_t element); - -/** Swap element. */ -FLECS_DBG_API -void flecs_switch_swap( - ecs_switch_t *sw, - int32_t elem_1, - int32_t elem_2); - -/** Get vector with all values. Use together with count(). */ -FLECS_DBG_API -ecs_vec_t* flecs_switch_values( - const ecs_switch_t *sw); - -/** Return number of different values. */ -FLECS_DBG_API -int32_t flecs_switch_case_count( - const ecs_switch_t *sw, - uint64_t value); - -/** Return first element for value. */ -FLECS_DBG_API -int32_t flecs_switch_first( - const ecs_switch_t *sw, - uint64_t value); - -/** Return next element for value. Use with first(). */ -FLECS_DBG_API -int32_t flecs_switch_next( - const ecs_switch_t *sw, - int32_t elem); - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif - -/** - * @file table.h + * @file storage/table.h * @brief Table storage implementation. */ @@ -502,7 +279,7 @@ extern "C" { #define FLECS_TABLE_H /** - * @file table_graph.h + * @file storage/table_graph.h * @brief Table graph types and functions. */ @@ -518,11 +295,15 @@ extern "C" { typedef struct ecs_table_diff_builder_t { ecs_vec_t added; ecs_vec_t removed; + ecs_flags32_t added_flags; + ecs_flags32_t removed_flags; } ecs_table_diff_builder_t; typedef struct ecs_table_diff_t { ecs_type_t added; /* Components added between tables */ ecs_type_t removed; /* Components removed between tables */ + ecs_flags32_t added_flags; + ecs_flags32_t removed_flags; } ecs_table_diff_t; /** Edge linked list (used to keep track of incoming edges) */ @@ -536,7 +317,7 @@ typedef struct ecs_graph_edge_t { ecs_graph_edge_hdr_t hdr; ecs_table_t *from; /* Edge source table */ ecs_table_t *to; /* Edge destination table */ - ecs_table_diff_t *diff; /* Index into diff vector, if non trivial edge */ + ecs_table_diff_t *diff; /* Added/removed components for edge */ ecs_id_t id; /* Id associated with edge */ } ecs_graph_edge_t; @@ -604,9 +385,59 @@ void flecs_table_diff_build_noalloc( ecs_table_diff_builder_t *builder, ecs_table_diff_t *diff); +void flecs_table_edges_add_flags( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + ecs_flags32_t flags); + #endif +#ifdef FLECS_SANITIZE +#define ecs_vec_from_column(arg_column, table, arg_elem_size) {\ + .array = (arg_column)->data,\ + .count = table->data.count,\ + .size = table->data.size,\ + .elem_size = arg_elem_size\ +} + +#define ecs_vec_from_column_ext(arg_column, arg_count, arg_size, arg_elem_size) {\ + .array = (arg_column)->data,\ + .count = arg_count,\ + .size = arg_size,\ + .elem_size = arg_elem_size\ +} + +#define ecs_vec_from_entities(table) {\ + .array = table->data.entities,\ + .count = table->data.count,\ + .size = table->data.size,\ + .elem_size = ECS_SIZEOF(ecs_entity_t)\ +} +#else +#define ecs_vec_from_column(arg_column, table, arg_elem_size) {\ + .array = (arg_column)->data,\ + .count = table->data.count,\ + .size = table->data.size,\ +} + +#define ecs_vec_from_column_ext(arg_column, arg_count, arg_size, arg_elem_size) {\ + .array = (arg_column)->data,\ + .count = arg_count,\ + .size = arg_size,\ +} + +#define ecs_vec_from_entities(table) {\ + .array = table->data.entities,\ + .count = table->data.count,\ + .size = table->data.size,\ +} +#endif + +#define ecs_vec_from_column_t(arg_column, table, T)\ + ecs_vec_from_column(arg_column, table, ECS_SIZEOF(T)) + /* Table event type for notifying tables of world events */ typedef enum ecs_table_eventkind_t { EcsTableTriggersForId, @@ -616,9 +447,6 @@ typedef enum ecs_table_eventkind_t { typedef struct ecs_table_event_t { ecs_table_eventkind_t kind; - /* Query event */ - ecs_query_t *query; - /* Component info event */ ecs_entity_t component; @@ -641,10 +469,7 @@ typedef struct ecs_table__t { struct ecs_table_record_t *records; /* Array with table records */ ecs_hashmap_t *name_index; /* Cached pointer to name index */ - ecs_switch_t *sw_columns; /* Switch columns */ ecs_bitset_t *bs_columns; /* Bitset columns */ - int16_t sw_count; - int16_t sw_offset; int16_t bs_count; int16_t bs_offset; int16_t ft_offset; @@ -652,16 +477,16 @@ typedef struct ecs_table__t { /** Table column */ typedef struct ecs_column_t { - ecs_vec_t data; /* Vector with component data */ - ecs_id_t id; /* Component id */ + void *data; /* Array with component data */ ecs_type_info_t *ti; /* Component type info */ - ecs_size_t size; /* Component size */ } ecs_column_t; /** Table data */ struct ecs_data_t { - ecs_vec_t entities; /* Entity ids */ + ecs_entity_t *entities; /* Entity ids */ ecs_column_t *columns; /* Component data */ + int32_t count; + int32_t size; }; /** A table is the Flecs equivalent of an archetype. Tables store all entities @@ -678,7 +503,8 @@ struct ecs_table_t { ecs_graph_node_t node; /* Graph node */ int32_t *dirty_state; /* Keep track of changes in columns */ - int32_t *column_map; /* Map type index <-> column + int16_t *component_map; /* Get column for component id */ + int16_t *column_map; /* Map type index <-> column * - 0..count(T): type index -> column * - count(T)..count(C): column -> type index */ @@ -727,16 +553,6 @@ void flecs_table_clear_entities_silent( ecs_world_t *world, ecs_table_t *table); -/* Clear table data. Don't call OnRemove handlers. */ -void flecs_table_clear_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data); - -/* Return number of entities in data */ -int32_t flecs_table_data_count( - const ecs_data_t *data); - /* Add a new entry to the table for the specified entity */ int32_t flecs_table_append( ecs_world_t *world, @@ -772,17 +588,9 @@ void flecs_table_move( int32_t flecs_table_appendn( ecs_world_t *world, ecs_table_t *table, - ecs_data_t *data, int32_t count, const ecs_entity_t *ids); -/* Set table to a fixed size. Useful for preallocating memory in advance. */ -void flecs_table_set_size( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data, - int32_t count); - /* Shrink table to contents */ bool flecs_table_shrink( ecs_world_t *world, @@ -803,7 +611,7 @@ void flecs_table_remove_actions( ecs_table_t *table); /* Free table */ -void flecs_table_free( +void flecs_table_fini( ecs_world_t *world, ecs_table_t *table); @@ -811,20 +619,12 @@ void flecs_table_free( void flecs_table_free_type( ecs_world_t *world, ecs_table_t *table); - -/* Replace data */ -void flecs_table_replace_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data); /* Merge data of one table into another table */ void flecs_table_merge( ecs_world_t *world, ecs_table_t *new_table, - ecs_table_t *old_table, - ecs_data_t *new_data, - ecs_data_t *old_data); + ecs_table_t *old_table); void flecs_table_swap( ecs_world_t *world, @@ -840,26 +640,34 @@ void flecs_table_mark_dirty( void flecs_table_notify( ecs_world_t *world, ecs_table_t *table, + ecs_id_t id, ecs_table_event_t *event); void flecs_table_delete_entities( ecs_world_t *world, ecs_table_t *table); -int32_t flecs_table_column_to_union_index( - const ecs_table_t *table, - int32_t column); - /* Increase observer count of table */ void flecs_table_traversable_add( ecs_table_t *table, int32_t value); -ecs_vec_t* flecs_table_entities( - ecs_table_t *table); +void flecs_table_emit( + ecs_world_t *world, + ecs_table_t *table, + ecs_entity_t event); -ecs_entity_t* flecs_table_entities_array( - ecs_table_t *table); +int32_t flecs_table_get_toggle_column( + ecs_table_t *table, + ecs_id_t id); + +ecs_bitset_t* flecs_table_get_toggle( + ecs_table_t *table, + ecs_id_t id); + +ecs_id_t flecs_column_id( + ecs_table_t *table, + int32_t column_index); #endif @@ -877,9 +685,6 @@ extern const ecs_entity_t EcsFlag; #define ecs_world_t_tag invalid #define ecs_stage_t_tag invalid #define ecs_query_t_tag EcsQuery -#define ecs_rule_t_tag EcsQuery -#define ecs_table_t_tag invalid -#define ecs_filter_t_tag EcsQuery #define ecs_observer_t_tag EcsObserver /* Mixin kinds */ @@ -887,7 +692,6 @@ typedef enum ecs_mixin_kind_t { EcsMixinWorld, EcsMixinEntity, EcsMixinObservable, - EcsMixinIterable, EcsMixinDtor, EcsMixinMax } ecs_mixin_kind_t; @@ -904,9 +708,7 @@ struct ecs_mixins_t { /* Mixin tables */ extern ecs_mixins_t ecs_world_t_mixins; extern ecs_mixins_t ecs_stage_t_mixins; -extern ecs_mixins_t ecs_filter_t_mixins; extern ecs_mixins_t ecs_query_t_mixins; -extern ecs_mixins_t ecs_trigger_t_mixins; extern ecs_mixins_t ecs_observer_t_mixins; /* Types that have no mixins */ @@ -922,14 +724,6 @@ typedef struct ecs_hashed_string_t { uint64_t hash; } ecs_hashed_string_t; -/** Must appear as first member in payload of table cache */ -typedef struct ecs_table_cache_hdr_t { - struct ecs_table_cache_t *cache; - ecs_table_t *table; - struct ecs_table_cache_hdr_t *prev, *next; - bool empty; -} ecs_table_cache_hdr_t; - /** Linked list of tables in table cache */ typedef struct ecs_table_cache_list_t { ecs_table_cache_hdr_t *first; @@ -944,194 +738,6 @@ typedef struct ecs_table_cache_t { ecs_table_cache_list_t empty_tables; } ecs_table_cache_t; -/* Sparse query term */ -typedef struct flecs_switch_term_t { - ecs_switch_t *sw_column; - ecs_entity_t sw_case; - int32_t signature_column_index; -} flecs_switch_term_t; - -/* Bitset query term */ -typedef struct flecs_bitset_term_t { - ecs_bitset_t *bs_column; - int32_t column_index; -} flecs_bitset_term_t; - -typedef struct flecs_flat_monitor_t { - int32_t table_state; - int32_t monitor; -} flecs_flat_monitor_t; - -/* Flat table term */ -typedef struct flecs_flat_table_term_t { - int32_t field_index; /* Iterator field index */ - ecs_term_t *term; - ecs_vec_t monitor; -} flecs_flat_table_term_t; - -/* Entity filter. This filters the entities of a matched table, for example when - * it has disabled components or union relationships (switch). */ -typedef struct ecs_entity_filter_t { - ecs_vec_t sw_terms; /* Terms with switch (union) entity filter */ - ecs_vec_t bs_terms; /* Terms with bitset (toggle) entity filter */ - ecs_vec_t ft_terms; /* Terms with components from flattened tree */ - int32_t flat_tree_column; -} ecs_entity_filter_t; - -typedef struct ecs_entity_filter_iter_t { - ecs_entity_filter_t *entity_filter; - ecs_iter_t *it; - int32_t *columns; - ecs_table_t *prev; - ecs_table_range_t range; - int32_t bs_offset; - int32_t sw_offset; - int32_t sw_smallest; - int32_t flat_tree_offset; - int32_t target_count; -} ecs_entity_filter_iter_t; - -/** Table match data. - * Each table matched by the query is represented by a ecs_query_table_match_t - * instance, which are linked together in a list. A table may match a query - * multiple times (due to wildcard queries) with different columns being matched - * by the query. */ -struct ecs_query_table_match_t { - ecs_query_table_match_t *next, *prev; - ecs_table_t *table; /* The current table. */ - int32_t offset; /* Starting point in table */ - int32_t count; /* Number of entities to iterate in table */ - int32_t *columns; /* Mapping from query fields to table columns */ - int32_t *storage_columns; /* Mapping from query fields to storage columns */ - ecs_id_t *ids; /* Resolved (component) ids for current table */ - ecs_entity_t *sources; /* Subjects (sources) of ids */ - ecs_vec_t refs; /* Cached components for non-this terms */ - uint64_t group_id; /* Value used to organize tables in groups */ - int32_t *monitor; /* Used to monitor table for changes */ - ecs_entity_filter_t *entity_filter; /* Entity specific filters */ - - /* Next match in cache for same table (includes empty tables) */ - ecs_query_table_match_t *next_match; -}; - -/** Table record type for query table cache. A query only has one per table. */ -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; - -/** Points to the beginning & ending of a query group */ -typedef struct ecs_query_table_list_t { - ecs_query_table_match_t *first; - ecs_query_table_match_t *last; - ecs_query_group_info_t info; -} ecs_query_table_list_t; - -/* Query event type for notifying queries of world events */ -typedef enum ecs_query_eventkind_t { - EcsQueryTableMatch, - EcsQueryTableRematch, - EcsQueryTableUnmatch, - EcsQueryOrphan -} ecs_query_eventkind_t; - -typedef struct ecs_query_event_t { - ecs_query_eventkind_t kind; - ecs_table_t *table; - ecs_query_t *parent_query; -} ecs_query_event_t; - -/* Query level block allocators have sizes that depend on query field count */ -typedef struct ecs_query_allocators_t { - ecs_block_allocator_t columns; - ecs_block_allocator_t ids; - ecs_block_allocator_t sources; - ecs_block_allocator_t monitors; -} ecs_query_allocators_t; - -/** Query that is automatically matched against tables */ -struct ecs_query_t { - ecs_header_t hdr; - - /* Query filter */ - ecs_filter_t filter; - - /* Tables matched with query */ - ecs_table_cache_t cache; - - /* Linked list with all matched non-empty tables, in iteration order */ - ecs_query_table_list_t list; - - /* Contains head/tail to nodes of query groups (if group_by is used) */ - ecs_map_t groups; - - /* Table sorting */ - ecs_entity_t order_by_component; - ecs_order_by_action_t order_by; - ecs_sort_table_action_t sort_table; - ecs_vec_t table_slices; - int32_t order_by_term; - - /* Table grouping */ - ecs_entity_t group_by_id; - ecs_group_by_action_t group_by; - ecs_group_create_action_t on_group_create; - ecs_group_delete_action_t on_group_delete; - void *group_by_ctx; - ecs_ctx_free_t group_by_ctx_free; - - /* Subqueries */ - ecs_query_t *parent; - ecs_vec_t subqueries; - - /* Flags for query properties */ - ecs_flags32_t flags; - - /* Monitor generation */ - int32_t monitor_generation; - - int32_t cascade_by; /* Identify cascade term */ - int32_t match_count; /* How often have tables been (un)matched */ - int32_t prev_match_count; /* Track if sorting is needed */ - int32_t rematch_count; /* Track which tables were added during rematch */ - - /* User context */ - void *ctx; /* User context to pass to callback */ - void *binding_ctx; /* Context to be used for language bindings */ - - ecs_ctx_free_t ctx_free; /** Callback to free ctx */ - ecs_ctx_free_t binding_ctx_free; /** Callback to free binding_ctx */ - - /* Mixins */ - ecs_iterable_t iterable; - ecs_poly_dtor_t dtor; - - /* Query-level allocators */ - ecs_query_allocators_t allocators; -}; - -/** All observers for a specific (component) id */ -typedef struct ecs_event_id_record_t { - /* Triggers for Self */ - ecs_map_t self; /* map */ - ecs_map_t self_up; /* map */ - ecs_map_t up; /* map */ - - ecs_map_t observers; /* map */ - - /* Triggers for SuperSet, SubSet */ - ecs_map_t set_observers; /* map */ - - /* Triggers for Self with non-This subject */ - ecs_map_t entity_observers; /* map */ - - /* Number of active observers for (component) id */ - int32_t observer_count; -} ecs_event_id_record_t; - /* World level allocators are for operations that are not multithreaded */ typedef struct ecs_world_allocators_t { ecs_map_params_t ptr; @@ -1155,6 +761,8 @@ typedef struct ecs_stage_allocators_t { ecs_stack_t iter_stack; ecs_stack_t deser_stack; ecs_block_allocator_t cmd_entry_chunk; + ecs_block_allocator_t query_impl; + ecs_block_allocator_t query_cache; } ecs_stage_allocators_t; /** Types for deferred operations */ @@ -1165,7 +773,7 @@ typedef enum ecs_cmd_kind_t { EcsCmdRemove, EcsCmdSet, EcsCmdEmplace, - EcsCmdMut, + EcsCmdEnsure, EcsCmdModified, EcsCmdModifiedNoHook, EcsCmdAddModified, @@ -1186,7 +794,7 @@ typedef struct ecs_cmd_entry_t { } ecs_cmd_entry_t; typedef struct ecs_cmd_1_t { - void *value; /* Component value (used by set / get_mut) */ + void *value; /* Component value (used by set / ensure) */ ecs_size_t size; /* Size of value */ bool clone_value; /* Clone entity with value (used for clone) */ } ecs_cmd_1_t; @@ -1208,6 +816,8 @@ typedef struct ecs_cmd_t { ecs_cmd_1_t _1; /* Data for single entity operation */ ecs_cmd_n_t _n; /* Data for multi entity operation */ } is; + + ecs_entity_t system; /* System that enqueued the command */ } ecs_cmd_t; /* Data structures that store the command queue */ @@ -1217,10 +827,22 @@ typedef struct ecs_commands_t { ecs_sparse_t entries; /* - command batching */ } ecs_commands_t; +/** Callback used to capture commands of a frame */ +typedef void (*ecs_on_commands_action_t)( + const ecs_stage_t *stage, + const ecs_vec_t *commands, + void *ctx); + /** A stage is a context that allows for safely using the API from multiple * threads. Stage pointers can be passed to the world argument of API * operations, which causes the operation to be ran on the stage instead of the - * world. */ + * world. The features provided by a stage are: + * + * - A command queue for deferred ECS operations and events + * - Thread specific allocators + * - Thread specific world state (like current scope, with, current system) + * - Thread specific buffers for preventing allocations + */ struct ecs_stage_t { ecs_header_t hdr; @@ -1249,22 +871,26 @@ struct ecs_stage_t { ecs_entity_t base; /* Currently instantiated top-level base */ const ecs_entity_t *lookup_path; /* Search path used by lookup operations */ - /* Properties */ - bool auto_merge; /* Should this stage automatically merge? */ - bool async; /* Is stage asynchronous? (write only) */ + /* Running system */ + ecs_entity_t system; /* Thread specific allocators */ ecs_stage_allocators_t allocators; ecs_allocator_t allocator; - /* Caches for rule creation */ + /* Caches for query creation */ ecs_vec_t variables; ecs_vec_t operations; + + /* Temporary token storage for DSL parser. This allows for parsing and + * interpreting a term without having to do allocations. */ + char parser_tokens[1024]; + char *parser_token; /* Pointer to next token */ }; /* Component monitor */ typedef struct ecs_monitor_t { - ecs_vec_t queries; /* vector */ + ecs_vec_t queries; /* vector */ bool is_dirty; /* Should queries be rematched? */ } ecs_monitor_t; @@ -1286,7 +912,7 @@ typedef struct ecs_store_t { /* Entity lookup */ ecs_entity_index_t entity_index; - /* Table lookup by id */ + /* Tables */ ecs_sparse_t tables; /* sparse */ /* Table lookup by hash */ @@ -1295,15 +921,19 @@ typedef struct ecs_store_t { /* Root table */ ecs_table_t root; + /* Observers */ + ecs_sparse_t observers; /* sparse */ + /* Records cache */ ecs_vec_t records; - /* Stack of ids being deleted. */ + /* Stack of ids being deleted during cleanup action. */ ecs_vec_t marked_ids; /* vector */ - - /* Entity ids associated with depth (for flat hierarchies) */ - ecs_vec_t depth_ids; - ecs_map_t entity_to_depth; /* What it says */ + + /* Components deleted during cleanup action. Used to delay cleaning up of + * type info so it's guaranteed that this data is available while the + * storage is cleaning up tables. */ + ecs_vec_t deleted_components; /* vector */ } ecs_store_t; /* fini actions */ @@ -1336,7 +966,6 @@ struct ecs_world_t { /* -- Mixins -- */ ecs_world_t *self; ecs_observable_t observable; - ecs_iterable_t iterable; /* Unique id per generated event used to prevent duplicate notifications */ int32_t event_id; @@ -1362,9 +991,18 @@ struct ecs_world_t { ecs_hashmap_t symbols; /* -- Staging -- */ - ecs_stage_t *stages; /* Stages */ + ecs_stage_t **stages; /* Stages */ int32_t stage_count; /* Number of stages */ + /* Internal callback for command inspection. Only one callback can be set at + * a time. After assignment the action will become active at the start of + * the next frame, set by ecs_frame_begin, and will be reset by + * ecs_frame_end. */ + ecs_on_commands_action_t on_commands; + ecs_on_commands_action_t on_commands_active; + void *on_commands_ctx; + void *on_commands_ctx_active; + /* -- Multithreading -- */ ecs_os_cond_t worker_cond; /* Signal that worker threads can start */ ecs_os_cond_t sync_cond; /* Signal that worker thread job is done */ @@ -1385,6 +1023,9 @@ struct ecs_world_t { /* -- World flags -- */ ecs_flags32_t flags; + /* -- Default query flags -- */ + ecs_flags32_t default_query_flags; + /* Count that increases when component monitors change */ int32_t monitor_generation; @@ -1404,7 +1045,7 @@ struct ecs_world_t { #endif /** - * @file table_cache.h + * @file storage/table_cache.h * @brief Data structure for fast table iteration/lookups. */ @@ -1423,6 +1064,12 @@ void ecs_table_cache_insert( const ecs_table_t *table, ecs_table_cache_hdr_t *result); +void ecs_table_cache_insert_w_empty( + ecs_table_cache_t *cache, + const ecs_table_t *table, + ecs_table_cache_hdr_t *result, + bool is_empty); + void ecs_table_cache_replace( ecs_table_cache_t *cache, const ecs_table_t *table, @@ -1447,6 +1094,7 @@ bool ecs_table_cache_is_empty( #define flecs_table_cache_count(cache) (cache)->tables.count #define flecs_table_cache_empty_count(cache) (cache)->empty_tables.count +#define flecs_table_cache_all_count(cache) ((cache)->tables.count + (cache)->empty_tables.count) bool flecs_table_cache_iter( ecs_table_cache_t *cache, @@ -1469,21 +1117,13 @@ ecs_table_cache_hdr_t* flecs_table_cache_next_( #endif /** - * @file id_index.h + * @file storage/id_index.h * @brief Index for looking up tables by (component) id. */ #ifndef FLECS_ID_INDEX_H #define FLECS_ID_INDEX_H -/* Payload for id cache */ -struct ecs_table_record_t { - ecs_table_cache_hdr_t hdr; /* Table cache header */ - int16_t index; /* First type index where id occurs in table */ - int16_t count; /* Number of times id occurs in table */ - int16_t column; /* First column index where id occurs */ -}; - /* Linked list of id records */ typedef struct ecs_id_record_elem_t { struct ecs_id_record_t *prev, *next; @@ -1505,7 +1145,7 @@ typedef struct ecs_reachable_cache_t { ecs_vec_t ids; /* vec */ } ecs_reachable_cache_t; -/* Payload for id index which contains all datastructures for an id. */ +/* Payload for id index which contains all data structures for an id. */ struct ecs_id_record_t { /* Cache with all tables that contain the id. Must be first member. */ ecs_table_cache_t cache; /* table_cache */ @@ -1522,6 +1162,9 @@ struct ecs_id_record_t { /* Name lookup index (currently only used for ChildOf pairs) */ ecs_hashmap_t *name_index; + /* Storage for sparse components or union relationships */ + void *sparse; + /* Lists for all id records that match a pair wildcard. The wildcard id * record is at the head of the list. */ ecs_id_record_elem_t first; /* (R, *) */ @@ -1539,7 +1182,7 @@ struct ecs_id_record_t { * queried for. */ int32_t keep_alive; - /* Cache invalidation counter */ + /* Cache for finding components that are reachable through a relationship */ ecs_reachable_cache_t reachable; }; @@ -1548,13 +1191,6 @@ ecs_id_record_t* flecs_id_record_get( const ecs_world_t *world, ecs_id_t id); -/* Get id record for id for searching. - * Same as flecs_id_record_get, but replaces (R, *) with (Union, R) if R is a - * union relationship. */ -ecs_id_record_t* flecs_query_id_record_get( - const ecs_world_t *world, - ecs_id_t id); - /* Ensure id record for id */ ecs_id_record_t* flecs_id_record_ensure( ecs_world_t *world, @@ -1606,6 +1242,11 @@ ecs_table_record_t* flecs_id_record_get_table( const ecs_id_record_t *idr, const ecs_table_t *table); +/* Init sparse storage */ +void flecs_id_record_init_sparse( + ecs_world_t *world, + ecs_id_record_t *idr); + /* Bootstrap cached id records */ void flecs_init_id_records( ecs_world_t *world); @@ -1614,11295 +1255,9992 @@ void flecs_init_id_records( void flecs_fini_id_records( ecs_world_t *world); +/* Return flags for matching id records */ +ecs_flags32_t flecs_id_flags_get( + ecs_world_t *world, + ecs_id_t id); + #endif + /** + * @file query/query.h + * @brief Query implementation. + */ + /** - * @file observable.h - * @brief Functions for sending events. + * @file query/compiler/compiler.h + * @brief Query compiler functions. */ -#ifndef FLECS_OBSERVABLE_H -#define FLECS_OBSERVABLE_H + /** + * @file query/types.h + * @brief Internal types and functions for queries. + */ -ecs_event_record_t* flecs_event_record_get( - const ecs_observable_t *o, - ecs_entity_t event); +#ifndef FLECS_QUERY_TYPES +#define FLECS_QUERY_TYPES -ecs_event_record_t* flecs_event_record_ensure( - ecs_observable_t *o, - ecs_entity_t event); +typedef struct ecs_query_impl_t ecs_query_impl_t; +typedef uint8_t ecs_var_id_t; +typedef int16_t ecs_query_lbl_t; +typedef ecs_flags64_t ecs_write_flags_t; -ecs_event_id_record_t* flecs_event_id_record_get( - const ecs_event_record_t *er, - ecs_id_t id); +#define flecs_query_impl(query) (ECS_CONST_CAST(ecs_query_impl_t*, query)) +#define EcsQueryMaxVarCount (64) +#define EcsVarNone ((ecs_var_id_t)-1) +#define EcsThisName "this" -ecs_event_id_record_t* flecs_event_id_record_ensure( - ecs_world_t *world, - ecs_event_record_t *er, - ecs_id_t id); +/* -- Variable types -- */ +typedef enum { + EcsVarEntity, /* Variable that stores an entity id */ + EcsVarTable, /* Variable that stores a table */ + EcsVarAny /* Used when requesting either entity or table var */ +} ecs_var_kind_t; -void flecs_event_id_record_remove( - ecs_event_record_t *er, - ecs_id_t id); +typedef struct ecs_query_var_t { + int8_t kind; /* variable kind (EcsVarEntity or EcsVarTable) */ + bool anonymous; /* variable is anonymous */ + ecs_var_id_t id; /* variable id */ + ecs_var_id_t table_id; /* id to table variable, if any */ + ecs_var_id_t base_id; /* id to base entity variable, for lookups */ + const char *name; /* variable name */ + const char *lookup; /* Lookup string for variable */ +#ifdef FLECS_DEBUG + const char *label; /* for debugging */ +#endif +} ecs_query_var_t; -void flecs_observable_init( - ecs_observable_t *observable); +/* Placeholder values for queries with only $this variable */ +extern ecs_query_var_t flecs_this_array; +extern char *flecs_this_name_array; -void flecs_observable_fini( - ecs_observable_t *observable); +/* -- Instruction kinds -- */ +typedef enum { + EcsQueryAnd, /* And operator: find or match id against variable source */ + EcsQueryAndAny, /* And operator with support for matching Any src/id */ + EcsQueryOnlyAny, /* Dedicated instruction for _ queries where the src is unknown */ + EcsQueryTriv, /* Trivial search (batches multiple terms) */ + EcsQueryCache, /* Cached search */ + EcsQueryIsCache, /* Cached search for queries that are entirely cached */ + EcsQueryUp, /* Up traversal */ + EcsQuerySelfUp, /* Self|up traversal */ + EcsQueryWith, /* Match id against fixed or variable source */ + EcsQueryTrav, /* Support for transitive/reflexive queries */ + EcsQueryAndFrom, /* AndFrom operator */ + EcsQueryOrFrom, /* OrFrom operator */ + EcsQueryNotFrom, /* NotFrom operator */ + EcsQueryIds, /* Test for existence of ids matching wildcard */ + EcsQueryIdsRight, /* Find ids in use that match (R, *) wildcard */ + EcsQueryIdsLeft, /* Find ids in use that match (*, T) wildcard */ + EcsQueryEach, /* Iterate entities in table, populate entity variable */ + EcsQueryStore, /* Store table or entity in variable */ + EcsQueryReset, /* Reset value of variable to wildcard (*) */ + EcsQueryOr, /* Or operator */ + EcsQueryOptional, /* Optional operator */ + EcsQueryIfVar, /* Conditional execution on whether variable is set */ + EcsQueryIfSet, /* Conditional execution on whether term is set */ + EcsQueryNot, /* Sets iterator state after term was not matched */ + EcsQueryEnd, /* End of control flow block */ + EcsQueryPredEq, /* Test if variable is equal to, or assign to if not set */ + EcsQueryPredNeq, /* Test if variable is not equal to */ + EcsQueryPredEqName, /* Same as EcsQueryPredEq but with matching by name */ + EcsQueryPredNeqName, /* Same as EcsQueryPredNeq but with matching by name */ + EcsQueryPredEqMatch, /* Same as EcsQueryPredEq but with fuzzy matching by name */ + EcsQueryPredNeqMatch, /* Same as EcsQueryPredNeq but with fuzzy matching by name */ + EcsQueryMemberEq, /* Compare member value */ + EcsQueryMemberNeq, /* Compare member value */ + EcsQueryToggle, /* Evaluate toggle bitset, if present */ + EcsQueryToggleOption, /* Toggle for optional terms */ + EcsQueryUnionEq, /* Evaluate union relationship */ + EcsQueryUnionEqWith, /* Evaluate union relationship against fixed or variable source */ + EcsQueryUnionNeq, /* Evaluate union relationship */ + EcsQueryUnionEqUp, /* Evaluate union relationship w/up traversal */ + EcsQueryUnionEqSelfUp, /* Evaluate union relationship w/self|up traversal */ + EcsQueryLookup, /* Lookup relative to variable */ + EcsQuerySetVars, /* Populate it.sources from variables */ + EcsQuerySetThis, /* Populate This entity variable */ + EcsQuerySetFixed, /* Set fixed source entity ids */ + EcsQuerySetIds, /* Set fixed (component) ids */ + EcsQuerySetId, /* Set id if not set */ + EcsQueryContain, /* Test if table contains entity */ + EcsQueryPairEq, /* Test if both elements of pair are the same */ + EcsQueryYield, /* Yield result back to application */ + EcsQueryNothing /* Must be last */ +} ecs_query_op_kind_t; + +/* Op flags to indicate if ecs_query_ref_t is entity or variable */ +#define EcsQueryIsEntity (1 << 0) +#define EcsQueryIsVar (1 << 1) +#define EcsQueryIsSelf (1 << 6) + +/* Op flags used to shift EcsQueryIsEntity and EcsQueryIsVar */ +#define EcsQuerySrc 0 +#define EcsQueryFirst 2 +#define EcsQuerySecond 4 -bool flecs_observers_exist( - ecs_observable_t *observable, - ecs_id_t id, - ecs_entity_t event); +/* References to variable or entity */ +typedef union { + ecs_var_id_t var; + ecs_entity_t entity; +} ecs_query_ref_t; -void flecs_observer_fini( - ecs_observer_t *observer); +/* Query instruction */ +typedef struct ecs_query_op_t { + uint8_t kind; /* Instruction kind */ + ecs_flags8_t flags; /* Flags storing whether 1st/2nd are variables */ + int8_t field_index; /* Query field corresponding with operation */ + int8_t term_index; /* Query term corresponding with operation */ + ecs_query_lbl_t prev; /* Backtracking label (no data) */ + ecs_query_lbl_t next; /* Forwarding label. Must come after prev */ + ecs_query_lbl_t other; /* Misc register used for control flow */ + ecs_flags16_t match_flags; /* Flags that modify matching behavior */ + ecs_query_ref_t src; + ecs_query_ref_t first; + ecs_query_ref_t second; + ecs_flags64_t written; /* Bitset with variables written by op */ +} ecs_query_op_t; -void flecs_emit( - ecs_world_t *world, - ecs_world_t *stage, - ecs_event_desc_t *desc); + /* And context */ +typedef struct { + ecs_id_record_t *idr; + ecs_table_cache_iter_t it; + int16_t column; + int16_t remaining; +} ecs_query_and_ctx_t; -bool flecs_default_observer_next_callback( - ecs_iter_t *it); +/* Union context */ +typedef struct { + ecs_id_record_t *idr; + ecs_table_range_t range; + ecs_map_iter_t tgt_iter; + ecs_entity_t cur; + ecs_entity_t tgt; + int32_t row; +} ecs_query_union_ctx_t; -void flecs_observers_invoke( - ecs_world_t *world, - ecs_map_t *observers, - ecs_iter_t *it, - ecs_table_t *table, - ecs_entity_t trav, - int32_t evtx); +/* Down traversal cache (for resolving up queries w/unknown source) */ +typedef struct { + ecs_table_t *table; + bool leaf; /* Table owns and inherits id (for Up queries without Self) */ +} ecs_trav_down_elem_t; -void flecs_emit_propagate_invalidate( - ecs_world_t *world, - ecs_table_t *table, - int32_t offset, - int32_t count); +typedef struct { + ecs_vec_t elems; /* vector */ + bool ready; +} ecs_trav_down_t; -#endif +typedef struct { + ecs_entity_t src; + ecs_id_t id; + ecs_table_record_t *tr; + bool ready; +} ecs_trav_up_t; -/** - * @file iter.h - * @brief Iterator utilities. - */ +typedef enum { + EcsTravUp = 1, + EcsTravDown = 2 +} ecs_trav_direction_t; -#ifndef FLECS_ITER_H -#define FLECS_ITER_H +typedef struct { + ecs_map_t src; /* map or map */ + ecs_id_t with; + ecs_trav_direction_t dir; +} ecs_trav_up_cache_t; -void flecs_iter_init( - const ecs_world_t *world, - ecs_iter_t *it, - ecs_flags8_t fields); +/* And up context */ +typedef struct { + union { + ecs_query_and_ctx_t and; + ecs_query_union_ctx_t union_; + } is; + ecs_table_t *table; + int32_t row; + int32_t end; + ecs_entity_t trav; + ecs_id_t with; + ecs_id_t matched; + ecs_id_record_t *idr_with; + ecs_id_record_t *idr_trav; + ecs_trav_down_t *down; + int32_t cache_elem; + ecs_trav_up_cache_t cache; +} ecs_query_up_ctx_t; -void flecs_iter_validate( - ecs_iter_t *it); +/* Cache for storing results of upward/downward "all" traversal. This type of + * traversal iterates and caches the entire tree. */ +typedef struct { + ecs_entity_t entity; + ecs_id_record_t *idr; + const ecs_table_record_t *tr; +} ecs_trav_elem_t; -void flecs_iter_populate_data( - ecs_world_t *world, - ecs_iter_t *it, - ecs_table_t *table, - int32_t offset, - int32_t count, - void **ptrs); +typedef struct { + ecs_id_t id; + ecs_id_record_t *idr; + ecs_vec_t entities; + bool up; +} ecs_trav_cache_t; -bool flecs_iter_next_row( - ecs_iter_t *it); +/* Trav context */ +typedef struct { + ecs_query_and_ctx_t and; + int32_t index; + int32_t offset; + int32_t count; + ecs_trav_cache_t cache; + bool yield_reflexive; +} ecs_query_trav_ctx_t; -bool flecs_iter_next_instanced( - ecs_iter_t *it, - bool result); + /* Eq context */ +typedef struct { + ecs_table_range_t range; + int32_t index; + int16_t name_col; + bool redo; +} ecs_query_eq_ctx_t; -void* flecs_iter_calloc( - ecs_iter_t *it, - ecs_size_t size, - ecs_size_t align); + /* Each context */ +typedef struct { + int32_t row; +} ecs_query_each_ctx_t; -#define flecs_iter_calloc_t(it, T)\ - flecs_iter_calloc(it, ECS_SIZEOF(T), ECS_ALIGNOF(T)) + /* Setthis context */ +typedef struct { + ecs_table_range_t range; +} ecs_query_setthis_ctx_t; -#define flecs_iter_calloc_n(it, T, count)\ - flecs_iter_calloc(it, ECS_SIZEOF(T) * count, ECS_ALIGNOF(T)) +/* Ids context */ +typedef struct { + ecs_id_record_t *cur; +} ecs_query_ids_ctx_t; -void flecs_iter_free( - void *ptr, - ecs_size_t size); +/* Control flow context */ +typedef struct { + ecs_query_lbl_t op_index; + ecs_id_t field_id; + bool is_set; +} ecs_query_ctrl_ctx_t; -#define flecs_iter_free_t(ptr, T)\ - flecs_iter_free(ptr, ECS_SIZEOF(T)) +/* Trivial iterator context */ +typedef struct { + ecs_table_cache_iter_t it; + const ecs_table_record_t *tr; + int32_t start_from; + int32_t first_to_eval; +} ecs_query_trivial_ctx_t; -#define flecs_iter_free_n(ptr, T, count)\ - flecs_iter_free(ptr, ECS_SIZEOF(T) * count) +/* *From operator iterator context */ +typedef struct { + ecs_query_and_ctx_t and; + ecs_type_t *type; + int32_t first_id_index; + int32_t cur_id_index; +} ecs_query_xfrom_ctx_t; -#endif +/* Member equality context */ +typedef struct { + ecs_query_each_ctx_t each; + void *data; +} ecs_query_membereq_ctx_t; -/** - * @file poly.h - * @brief Functions for managing poly objects. - */ +/* Toggle context */ +typedef struct { + ecs_table_range_t range; + int32_t cur; + int32_t block_index; + ecs_flags64_t block; + ecs_termset_t prev_set_fields; + bool optional_not; + bool has_bitset; +} ecs_query_toggle_ctx_t; + +typedef struct ecs_query_op_ctx_t { + union { + ecs_query_and_ctx_t and; + ecs_query_xfrom_ctx_t xfrom; + ecs_query_up_ctx_t up; + ecs_query_trav_ctx_t trav; + ecs_query_ids_ctx_t ids; + ecs_query_eq_ctx_t eq; + ecs_query_each_ctx_t each; + ecs_query_setthis_ctx_t setthis; + ecs_query_ctrl_ctx_t ctrl; + ecs_query_trivial_ctx_t trivial; + ecs_query_membereq_ctx_t membereq; + ecs_query_toggle_ctx_t toggle; + ecs_query_union_ctx_t union_; + } is; +} ecs_query_op_ctx_t; -#ifndef FLECS_POLY_H -#define FLECS_POLY_H +typedef struct { + /* Labels used for control flow */ + ecs_query_lbl_t lbl_query; /* Used to find the op that does the actual searching */ + ecs_query_lbl_t lbl_begin; + ecs_query_lbl_t lbl_cond_eval; + ecs_write_flags_t written_or; /* Cond written flags at start of or chain */ + ecs_write_flags_t cond_written_or; /* Cond written flags at start of or chain */ + ecs_query_ref_t src_or; /* Source for terms in current or chain */ + bool src_written_or; /* Was src populated before OR chain */ + bool in_or; /* Whether we're in an or chain */ +} ecs_query_compile_ctrlflow_t; -#include +/* Query compiler state */ +typedef struct { + ecs_vec_t *ops; + ecs_write_flags_t written; /* Bitmask to check which variables have been written */ + ecs_write_flags_t cond_written; /* Track conditional writes (optional operators) */ -/* Initialize poly */ -void* ecs_poly_init_( - ecs_poly_t *object, - int32_t kind, - ecs_size_t size, - ecs_mixins_t *mixins); + /* Maintain control flow per scope */ + ecs_query_compile_ctrlflow_t ctrlflow[FLECS_QUERY_SCOPE_NESTING_MAX]; + ecs_query_compile_ctrlflow_t *cur; /* Current scope */ -#define ecs_poly_init(object, type)\ - ecs_poly_init_(object, type##_magic, sizeof(type), &type##_mixins) + int32_t scope; /* Nesting level of query scopes */ + ecs_flags32_t scope_is_not; /* Whether scope is prefixed with not */ + ecs_oper_kind_t oper; /* Temp storage to track current operator for term */ + int32_t skipped; /* Term skipped during compilation */ +} ecs_query_compile_ctx_t; -/* Deinitialize object for specified type */ -void ecs_poly_fini_( - ecs_poly_t *object, - int32_t kind); +/* Query run state */ +typedef struct { + uint64_t *written; /* Bitset to check which variables have been written */ + ecs_query_lbl_t op_index; /* Currently evaluated operation */ + ecs_var_t *vars; /* Variable storage */ + ecs_iter_t *it; /* Iterator */ + ecs_query_op_ctx_t *op_ctx; /* Operation context (stack) */ + ecs_world_t *world; /* Reference to world */ + const ecs_query_impl_t *query; /* Reference to query */ + const ecs_query_var_t *query_vars; /* Reference to query variable array */ + ecs_query_iter_t *qit; +} ecs_query_run_ctx_t; + +struct ecs_query_impl_t { + ecs_query_t pub; /* Public query data */ + + ecs_stage_t *stage; /* Stage used for allocations */ -#define ecs_poly_fini(object, type)\ - ecs_poly_fini_(object, type##_magic) + /* Variables */ + ecs_query_var_t *vars; /* Variables */ + int32_t var_count; /* Number of variables */ + int32_t var_size; /* Size of variable array */ + ecs_hashmap_t tvar_index; /* Name index for table variables */ + ecs_hashmap_t evar_index; /* Name index for entity variables */ + ecs_var_id_t *src_vars; /* Array with ids to source variables for fields */ -/* Utility functions for creating an object on the heap */ -#define ecs_poly_new(type)\ - (type*)ecs_poly_init(ecs_os_calloc_t(type), type) + /* Query plan */ + ecs_query_op_t *ops; /* Operations */ + int32_t op_count; /* Number of operations */ -#define ecs_poly_free(obj, type)\ - ecs_poly_fini(obj, type);\ - ecs_os_free(obj) + /* Misc */ + int16_t tokens_len; /* Length of tokens buffer */ + char *tokens; /* Buffer with string tokens used by terms */ + int32_t *monitor; /* Change monitor for fields with fixed src */ -/* Get or create poly component for an entity */ -EcsPoly* ecs_poly_bind_( - ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t tag); + /* Query cache */ + struct ecs_query_cache_t *cache; /* Cache, if query contains cached terms */ -#define ecs_poly_bind(world, entity, T) \ - ecs_poly_bind_(world, entity, T##_tag) + /* User context */ + ecs_ctx_free_t ctx_free; /* Callback to free ctx */ + ecs_ctx_free_t binding_ctx_free; /* Callback to free binding_ctx */ -void ecs_poly_modified_( - ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t tag); + /* Mixins */ + flecs_poly_dtor_t dtor; +}; -#define ecs_poly_modified(world, entity, T) \ - ecs_poly_modified_(world, entity, T##_tag) -/* Get poly component for an entity */ -const EcsPoly* ecs_poly_bind_get_( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t tag); +/* Query cache types */ -#define ecs_poly_bind_get(world, entity, T) \ - ecs_poly_bind_get_(world, entity, T##_tag) +/** Table match data. + * Each table matched by the query is represented by an ecs_query_cache_table_match_t + * instance, which are linked together in a list. A table may match a query + * multiple times (due to wildcard queries) with different columns being matched + * by the query. */ +struct ecs_query_cache_table_match_t { + ecs_query_cache_table_match_t *next, *prev; + ecs_table_t *table; /* The current table. */ + int32_t offset; /* Starting point in table */ + int32_t count; /* Number of entities to iterate in table */ + const ecs_table_record_t **trs; /* Information about where to find field in table */ + ecs_id_t *ids; /* Resolved (component) ids for current table */ + ecs_entity_t *sources; /* Subjects (sources) of ids */ + ecs_termset_t set_fields; /* Fields that are set */ + ecs_termset_t up_fields; /* Fields that are matched through traversal */ + uint64_t group_id; /* Value used to organize tables in groups */ + int32_t *monitor; /* Used to monitor table for changes */ -ecs_poly_t* ecs_poly_get_( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t tag); + /* Next match in cache for same table (includes empty tables) */ + ecs_query_cache_table_match_t *next_match; +}; -#define ecs_poly_get(world, entity, T) \ - ((T*)ecs_poly_get_(world, entity, T##_tag)) +/** Table record type for query table cache. A query only has one per table. */ +typedef struct ecs_query_cache_table_t { + ecs_table_cache_hdr_t hdr; /* Header for ecs_table_cache_t */ + ecs_query_cache_table_match_t *first; /* List with matches for table */ + ecs_query_cache_table_match_t *last; /* Last discovered match for table */ + uint64_t table_id; + int32_t rematch_count; /* Track whether table was rematched */ +} ecs_query_cache_table_t; -/* Utilities for testing/asserting an object type */ -#ifndef FLECS_NDEBUG -#define ecs_poly_assert(object, ty)\ - do {\ - ecs_assert(object != NULL, ECS_INVALID_PARAMETER, NULL);\ - const ecs_header_t *hdr = (const ecs_header_t *)object;\ - const char *type_name = hdr->mixins->type_name;\ - ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, type_name);\ - ecs_assert(hdr->type == ty##_magic, ECS_INVALID_PARAMETER, type_name);\ - } while (0) -#else -#define ecs_poly_assert(object, ty) -#endif +/** Points to the beginning & ending of a query group */ +typedef struct ecs_query_cache_table_list_t { + ecs_query_cache_table_match_t *first; + ecs_query_cache_table_match_t *last; + ecs_query_group_info_t info; +} ecs_query_cache_table_list_t; -/* Utility functions for getting a mixin from an object */ -ecs_iterable_t* ecs_get_iterable( - const ecs_poly_t *poly); +/* Query event type for notifying queries of world events */ +typedef enum ecs_query_cache_eventkind_t { + EcsQueryTableMatch, + EcsQueryTableRematch, + EcsQueryTableUnmatch, +} ecs_query_cache_eventkind_t; -ecs_observable_t* ecs_get_observable( - const ecs_poly_t *object); +typedef struct ecs_query_cache_event_t { + ecs_query_cache_eventkind_t kind; + ecs_table_t *table; +} ecs_query_cache_event_t; -ecs_poly_dtor_t* ecs_get_dtor( - const ecs_poly_t *poly); +/* Query level block allocators have sizes that depend on query field count */ +typedef struct ecs_query_cache_allocators_t { + ecs_block_allocator_t trs; + ecs_block_allocator_t ids; + ecs_block_allocator_t sources; + ecs_block_allocator_t monitors; +} ecs_query_cache_allocators_t; -#endif +/** Query that is automatically matched against tables */ +typedef struct ecs_query_cache_t { + /* Uncached query used to populate the cache */ + ecs_query_t *query; -/** - * @file stage.h - * @brief Stage functions. - */ + /* Observer to keep the cache in sync */ + ecs_observer_t *observer; -#ifndef FLECS_STAGE_H -#define FLECS_STAGE_H + /* Tables matched with query */ + ecs_table_cache_t cache; -/* Initialize stage data structures */ -void flecs_stage_init( - ecs_world_t *world, - ecs_stage_t *stage); + /* Linked list with all matched non-empty tables, in iteration order */ + ecs_query_cache_table_list_t list; -/* Deinitialize stage */ -void flecs_stage_fini( - ecs_world_t *world, - ecs_stage_t *stage); + /* Contains head/tail to nodes of query groups (if group_by is used) */ + ecs_map_t groups; -/* Post-frame merge actions */ -void flecs_stage_merge_post_frame( - ecs_world_t *world, - ecs_stage_t *stage); + /* Table sorting */ + ecs_entity_t order_by; + ecs_order_by_action_t order_by_callback; + ecs_sort_table_action_t order_by_table_callback; + ecs_vec_t table_slices; + int32_t order_by_term; -bool flecs_defer_cmd( - ecs_stage_t *stage); + /* Table grouping */ + ecs_entity_t group_by; + ecs_group_by_action_t group_by_callback; + ecs_group_create_action_t on_group_create; + ecs_group_delete_action_t on_group_delete; + void *group_by_ctx; + ecs_ctx_free_t group_by_ctx_free; -bool flecs_defer_begin( - ecs_world_t *world, - ecs_stage_t *stage); + /* Monitor generation */ + int32_t monitor_generation; -bool flecs_defer_modified( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_entity_t component); + int32_t cascade_by; /* Identify cascade term */ + int32_t match_count; /* How often have tables been (un)matched */ + int32_t prev_match_count; /* Track if sorting is needed */ + int32_t rematch_count; /* Track which tables were added during rematch */ + + ecs_entity_t entity; -bool flecs_defer_clone( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_entity_t src, - bool clone_value); + /* Zero'd out sources array, used for results that only match on $this */ + ecs_entity_t *sources; -bool flecs_defer_bulk_new( - ecs_world_t *world, - ecs_stage_t *stage, - int32_t count, - ecs_id_t id, - const ecs_entity_t **ids_out); + /* Map field indices from cache to query */ + int8_t *field_map; -bool flecs_defer_path( - ecs_stage_t *stage, - ecs_entity_t parent, - ecs_entity_t entity, - const char *name); + /* Query-level allocators */ + ecs_query_cache_allocators_t allocators; +} ecs_query_cache_t; -bool flecs_defer_delete( - ecs_stage_t *stage, - ecs_entity_t entity); +#endif -bool flecs_defer_clear( - ecs_stage_t *stage, - ecs_entity_t entity); -bool flecs_defer_on_delete_action( +/* Compile query to list of operations */ +int flecs_query_compile( + ecs_world_t *world, ecs_stage_t *stage, - ecs_id_t id, - ecs_entity_t action); + ecs_query_impl_t *query); -bool flecs_defer_enable( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_entity_t component, - bool enable); +/* Compile single term */ +int flecs_query_compile_term( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx); -bool flecs_defer_add( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_id_t id); +/* Compile term ref (first, second or src) */ +void flecs_query_compile_term_ref( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_term_ref_t *term_ref, + ecs_query_ref_t *ref, + ecs_flags8_t ref_kind, + ecs_var_kind_t kind, + ecs_query_compile_ctx_t *ctx, + bool create_wildcard_vars); -bool flecs_defer_remove( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_id_t id); +/* Mark variable as written */ +void flecs_query_write( + ecs_var_id_t var_id, + uint64_t *written); -void* flecs_defer_set( - ecs_world_t *world, - ecs_stage_t *stage, - ecs_cmd_kind_t op_kind, - ecs_entity_t entity, - ecs_entity_t component, - ecs_size_t size, - void *value); +/* Mark variable as written in compiler context */ +void flecs_query_write_ctx( + ecs_var_id_t var_id, + ecs_query_compile_ctx_t *ctx, + bool cond_write); -bool flecs_defer_end( - ecs_world_t *world, - ecs_stage_t *stage); +/* Add operation to query plan */ +ecs_query_lbl_t flecs_query_op_insert( + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx); -bool flecs_defer_purge( - ecs_world_t *world, - ecs_stage_t *stage); +/* Insert each instruction */ +void flecs_query_insert_each( + ecs_var_id_t tvar, + ecs_var_id_t evar, + ecs_query_compile_ctx_t *ctx, + bool cond_write); + +/* Insert instruction that populates field */ +void flecs_query_insert_populate( + ecs_query_impl_t *query, + ecs_query_compile_ctx_t *ctx, + ecs_flags64_t populated); + +/* Add discovered variable */ +ecs_var_id_t flecs_query_add_var( + ecs_query_impl_t *query, + const char *name, + ecs_vec_t *vars, + ecs_var_kind_t kind); -void flecs_enqueue( - ecs_world_t *world, - ecs_stage_t *stage, - ecs_event_desc_t *desc); +/* Find variable by name/kind */ +ecs_var_id_t flecs_query_find_var_id( + const ecs_query_impl_t *query, + const char *name, + ecs_var_kind_t kind); -void flecs_commands_push( - ecs_stage_t *stage); +ecs_query_op_t* flecs_query_begin_block( + ecs_query_op_kind_t kind, + ecs_query_compile_ctx_t *ctx); -void flecs_commands_pop( - ecs_stage_t *stage); +void flecs_query_end_block( + ecs_query_compile_ctx_t *ctx, + bool reset); -#endif /** - * @file world.h - * @brief World-level API. + * @file query/engine/engine.h + * @brief Query engine functions. */ -#ifndef FLECS_WORLD_H -#define FLECS_WORLD_H + /** + * @file query/engine/cache.h + * @brief Query cache functions. + */ -/* Get current stage */ -ecs_stage_t* flecs_stage_from_world( - ecs_world_t **world_ptr); -/* Get current thread-specific stage from readonly world */ -const ecs_stage_t* flecs_stage_from_readonly_world( - const ecs_world_t *world); +/* Create query cache */ +ecs_query_cache_t* flecs_query_cache_init( + ecs_query_impl_t *impl, + const ecs_query_desc_t *desc); -/* Get component callbacks */ -const ecs_type_info_t *flecs_type_info_get( - const ecs_world_t *world, - ecs_entity_t component); +/* Destroy query cache */ +void flecs_query_cache_fini( + ecs_query_impl_t *impl); -/* Get or create component callbacks */ -ecs_type_info_t* flecs_type_info_ensure( +/* Notify query cache of event (separate from query observer) */ +void flecs_query_cache_notify( ecs_world_t *world, - ecs_entity_t component); + ecs_query_t *q, + ecs_query_cache_event_t *event); -bool flecs_type_info_init_id( +/* Get cache entry for table */ +ecs_query_cache_table_t* flecs_query_cache_get_table( + ecs_query_cache_t *query, + ecs_table_t *table); + +/* Sort tables (order_by implementation) */ +void flecs_query_cache_sort_tables( ecs_world_t *world, - ecs_entity_t component, - ecs_size_t size, - ecs_size_t alignment, - const ecs_type_hooks_t *li); + ecs_query_impl_t *impl); -#define flecs_type_info_init(world, T, ...)\ - flecs_type_info_init_id(world, ecs_id(T), ECS_SIZEOF(T), ECS_ALIGNOF(T),\ - &(ecs_type_hooks_t)__VA_ARGS__) +void flecs_query_cache_build_sorted_tables( + ecs_query_cache_t *cache); -void flecs_type_info_fini( - ecs_type_info_t *ti); +/* Return number of tables in cache */ +int32_t flecs_query_cache_table_count( + ecs_query_cache_t *cache); -void flecs_type_info_free( - ecs_world_t *world, - ecs_entity_t component); +/* Return number of empty tables in cache */ +int32_t flecs_query_cache_empty_table_count( + ecs_query_cache_t *cache); -void flecs_eval_component_monitors( - ecs_world_t *world); +/* Return number of entities in cache (requires iterating tables) */ +int32_t flecs_query_cache_entity_count( + const ecs_query_cache_t *cache); -void flecs_monitor_mark_dirty( - ecs_world_t *world, - ecs_entity_t id); +/** + * @file query/engine/cache_iter.h + * @brief Cache iterator functions. + */ -void flecs_monitor_register( - ecs_world_t *world, - ecs_entity_t id, - ecs_query_t *query); -void flecs_monitor_unregister( - ecs_world_t *world, - ecs_entity_t id, - ecs_query_t *query); +/* Cache search */ +bool flecs_query_cache_search( + const ecs_query_run_ctx_t *ctx); -void flecs_notify_tables( - ecs_world_t *world, - ecs_id_t id, - ecs_table_event_t *event); +/* Cache search where entire query is cached */ +bool flecs_query_is_cache_search( + const ecs_query_run_ctx_t *ctx); -void flecs_register_table( - ecs_world_t *world, - ecs_table_t *table); +/* Cache test */ +bool flecs_query_cache_test( + const ecs_query_run_ctx_t *ctx, + bool redo); -void flecs_unregister_table( - ecs_world_t *world, - ecs_table_t *table); +/* Cache test where entire query is cached */ +bool flecs_query_is_cache_test( + const ecs_query_run_ctx_t *ctx, + bool redo); -void flecs_table_set_empty( - ecs_world_t *world, - ecs_table_t *table); +/** + * @file query/engine/change_detection.h + * @brief Query change detection functions. + */ -void flecs_delete_table( - ecs_world_t *world, - ecs_table_t *table); -void flecs_process_pending_tables( - const ecs_world_t *world); +/* Synchronize cache monitor with table dirty state */ +void flecs_query_sync_match_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match); -/* Suspend/resume readonly state. To fully support implicit registration of - * components, it should be possible to register components while the world is - * in readonly mode. It is not uncommon that a component is used first from - * within a system, which are often ran while in readonly mode. - * - * Suspending readonly mode is only allowed when the world is not multithreaded. - * When a world is multithreaded, it is not safe to (even temporarily) leave - * readonly mode, so a multithreaded application should always explicitly - * register components in advance. - * - * These operations also suspend deferred mode. - */ -typedef struct ecs_suspend_readonly_state_t { - bool is_readonly; - bool is_deferred; - int32_t defer_count; - ecs_entity_t scope; - ecs_entity_t with; - ecs_vec_t commands; - ecs_stack_t defer_stack; - ecs_stage_t *stage; -} ecs_suspend_readonly_state_t; +/* Mark iterated out fields dirty */ +void flecs_query_mark_fields_dirty( + ecs_query_impl_t *impl, + ecs_iter_t *it); -ecs_world_t* flecs_suspend_readonly( - const ecs_world_t *world, - ecs_suspend_readonly_state_t *state); +/* Compare cache monitor with table dirty state to detect changes */ +bool flecs_query_check_table_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_t *table, + int32_t term); -void flecs_resume_readonly( - ecs_world_t *world, - ecs_suspend_readonly_state_t *state); +/* Mark out fields with fixed source dirty */ +void flecs_query_mark_fixed_fields_dirty( + ecs_query_impl_t *impl, + ecs_iter_t *it); -/* Convenience macro's for world allocator */ -#define flecs_walloc(world, size)\ - flecs_alloc(&world->allocator, size) -#define flecs_walloc_n(world, T, count)\ - flecs_alloc_n(&world->allocator, T, count) -#define flecs_wcalloc(world, size)\ - flecs_calloc(&world->allocator, size) -#define flecs_wcalloc_n(world, T, count)\ - flecs_calloc_n(&world->allocator, T, count) -#define flecs_wfree(world, size, ptr)\ - flecs_free(&world->allocator, size, ptr) -#define flecs_wfree_n(world, T, count, ptr)\ - flecs_free_n(&world->allocator, T, count, ptr) -#define flecs_wrealloc(world, size_dst, size_src, ptr)\ - flecs_realloc(&world->allocator, size_dst, size_src, ptr) -#define flecs_wrealloc_n(world, T, count_dst, count_src, ptr)\ - flecs_realloc_n(&world->allocator, T, count_dst, count_src, ptr) -#define flecs_wdup(world, size, ptr)\ - flecs_dup(&world->allocator, size, ptr) -#define flecs_wdup_n(world, T, count, ptr)\ - flecs_dup_n(&world->allocator, T, count, ptr) +/* Synchronize fixed source monitor */ +bool flecs_query_update_fixed_monitor( + ecs_query_impl_t *impl); -#endif +/* Compare fixed source monitor */ +bool flecs_query_check_fixed_monitor( + ecs_query_impl_t *impl); /** - * @file datastructures/name_index.h - * @brief Data structure for resolving 64bit keys by string (name). + * @file query/engine/trav_cache.h + * @brief Traversal cache functions */ -#ifndef FLECS_NAME_INDEX_H -#define FLECS_NAME_INDEX_H -void flecs_name_index_init( - ecs_hashmap_t *hm, - ecs_allocator_t *allocator); +/* Traversal cache for transitive queries. Finds all reachable entities by + * following a relationship */ -void flecs_name_index_init_if( - ecs_hashmap_t *hm, - ecs_allocator_t *allocator); +/* Find all entities when traversing downwards */ +void flecs_query_get_trav_down_cache( + const ecs_query_run_ctx_t *ctx, + ecs_trav_cache_t *cache, + ecs_entity_t trav, + ecs_entity_t entity); -bool flecs_name_index_is_init( - const ecs_hashmap_t *hm); +/* Find all entities when traversing upwards */ +void flecs_query_get_trav_up_cache( + const ecs_query_run_ctx_t *ctx, + ecs_trav_cache_t *cache, + ecs_entity_t trav, + ecs_table_t *table); -ecs_hashmap_t* flecs_name_index_new( - ecs_world_t *world, - ecs_allocator_t *allocator); +/* Free traversal cache */ +void flecs_query_trav_cache_fini( + ecs_allocator_t *a, + ecs_trav_cache_t *cache); -void flecs_name_index_fini( - ecs_hashmap_t *map); +/* Traversal caches for up traversal. Enables searching upwards until an entity + * with the queried for id has been found. */ -void flecs_name_index_free( - ecs_hashmap_t *map); +/* Traverse downwards from starting entity to find all tables for which the + * specified entity is the source of the queried for id ('with'). */ +ecs_trav_down_t* flecs_query_get_down_cache( + const ecs_query_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + ecs_entity_t trav, + ecs_entity_t entity, + ecs_id_record_t *idr_with, + bool self, + bool empty); -ecs_hashmap_t* flecs_name_index_copy( - ecs_hashmap_t *dst); +/* Free down traversal cache */ +void flecs_query_down_cache_fini( + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache); -ecs_hashed_string_t flecs_get_hashed_string( - const char *name, - ecs_size_t length, - uint64_t hash); +ecs_trav_up_t* flecs_query_get_up_cache( + const ecs_query_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + ecs_table_t *table, + ecs_id_t with, + ecs_entity_t trav, + ecs_id_record_t *idr_with, + ecs_id_record_t *idr_trav); -const uint64_t* flecs_name_index_find_ptr( - const ecs_hashmap_t *map, - const char *name, - ecs_size_t length, - uint64_t hash); +/* Free up traversal cache */ +void flecs_query_up_cache_fini( + ecs_trav_up_cache_t *cache); -uint64_t flecs_name_index_find( - const ecs_hashmap_t *map, - const char *name, - ecs_size_t length, - uint64_t hash); +/** + * @file query/engine/trivial_iter.h + * @brief Trivial iterator functions. + */ -void flecs_name_index_ensure( - ecs_hashmap_t *map, - uint64_t id, - const char *name, - ecs_size_t length, - uint64_t hash); -void flecs_name_index_remove( - ecs_hashmap_t *map, - uint64_t id, - uint64_t hash); +/* Iterator for queries with trivial terms. */ +bool flecs_query_trivial_search( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + bool redo, + ecs_flags64_t field_set); -void flecs_name_index_update_name( - ecs_hashmap_t *map, - uint64_t e, - uint64_t hash, - const char *name); +/* Iterator for queries with only trivial terms. */ +bool flecs_query_is_trivial_search( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + bool redo); -#endif +/* Trivial test for constrained $this. */ +bool flecs_query_trivial_test( + const ecs_query_run_ctx_t *ctx, + bool first, + ecs_flags64_t field_set); +/* Query evaluation utilities */ -//////////////////////////////////////////////////////////////////////////////// -//// Bootstrap API -//////////////////////////////////////////////////////////////////////////////// +void flecs_query_set_iter_this( + ecs_iter_t *it, + const ecs_query_run_ctx_t *ctx); -/* Bootstrap world */ -void flecs_bootstrap( - ecs_world_t *world); +ecs_query_op_ctx_t* flecs_op_ctx_( + const ecs_query_run_ctx_t *ctx); -#define flecs_bootstrap_component(world, id_)\ - ecs_component_init(world, &(ecs_component_desc_t){\ - .entity = ecs_entity(world, { .id = ecs_id(id_), .name = #id_, .symbol = #id_ }),\ - .type.size = sizeof(id_),\ - .type.alignment = ECS_ALIGNOF(id_)\ - }); +#define flecs_op_ctx(ctx, op_kind) (&flecs_op_ctx_(ctx)->is.op_kind) -#define flecs_bootstrap_tag(world, name)\ - ecs_ensure(world, name);\ - ecs_add_id(world, name, EcsFinal);\ - ecs_add_pair(world, name, EcsChildOf, ecs_get_scope(world));\ - ecs_set(world, name, EcsComponent, {.size = 0});\ - ecs_set_name(world, name, (const char*)&#name[ecs_os_strlen(world->info.name_prefix)]);\ - ecs_set_symbol(world, name, #name) +void flecs_reset_source_set_flag( + ecs_iter_t *it, + int32_t field_index); -/* Bootstrap functions for other parts in the code */ -void flecs_bootstrap_hierarchy(ecs_world_t *world); +void flecs_set_source_set_flag( + ecs_iter_t *it, + int32_t field_index); + +ecs_table_range_t flecs_range_from_entity( + ecs_entity_t e, + const ecs_query_run_ctx_t *ctx); +ecs_table_range_t flecs_query_var_get_range( + int32_t var_id, + const ecs_query_run_ctx_t *ctx); -//////////////////////////////////////////////////////////////////////////////// -//// Entity API -//////////////////////////////////////////////////////////////////////////////// +ecs_table_t* flecs_query_var_get_table( + int32_t var_id, + const ecs_query_run_ctx_t *ctx); -/* Mark an entity as being watched. This is used to trigger automatic rematching - * when entities used in system expressions change their components. */ -void flecs_add_flag( - ecs_world_t *world, - ecs_entity_t entity, - uint32_t flag); +ecs_table_t* flecs_query_get_table( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + const ecs_query_run_ctx_t *ctx); -void flecs_record_add_flag( - ecs_record_t *record, - uint32_t flag); +ecs_table_range_t flecs_query_get_range( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + const ecs_query_run_ctx_t *ctx); -ecs_entity_t flecs_get_oneof( - const ecs_world_t *world, - ecs_entity_t e); +ecs_entity_t flecs_query_var_get_entity( + ecs_var_id_t var_id, + const ecs_query_run_ctx_t *ctx); -void flecs_notify_on_remove( - ecs_world_t *world, +void flecs_query_var_reset( + ecs_var_id_t var_id, + const ecs_query_run_ctx_t *ctx); + +void flecs_query_var_set_range( + const ecs_query_op_t *op, + ecs_var_id_t var_id, ecs_table_t *table, - ecs_table_t *other_table, - int32_t row, + int32_t offset, int32_t count, - const ecs_type_t *diff); + const ecs_query_run_ctx_t *ctx); -void flecs_notify_on_set( - ecs_world_t *world, +void flecs_query_var_narrow_range( + ecs_var_id_t var_id, ecs_table_t *table, - int32_t row, + int32_t offset, int32_t count, - ecs_type_t *type, - bool owned); - -int32_t flecs_relation_depth( - const ecs_world_t *world, - ecs_entity_t r, - const ecs_table_t *table); + const ecs_query_run_ctx_t *ctx); -void flecs_instantiate( - ecs_world_t *world, - ecs_entity_t base, - ecs_table_t *table, - int32_t row, - int32_t count); +void flecs_query_var_set_entity( + const ecs_query_op_t *op, + ecs_var_id_t var_id, + ecs_entity_t entity, + const ecs_query_run_ctx_t *ctx); -void* flecs_get_base_component( - const ecs_world_t *world, - ecs_table_t *table, +void flecs_query_set_vars( + const ecs_query_op_t *op, ecs_id_t id, - ecs_id_record_t *table_index, - int32_t recur_depth); + const ecs_query_run_ctx_t *ctx); -void flecs_invoke_hook( - ecs_world_t *world, - ecs_table_t *table, - int32_t count, - int32_t row, - 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_table_range_t flecs_get_ref_range( + const ecs_query_ref_t *ref, + ecs_flags16_t flag, + const ecs_query_run_ctx_t *ctx); -//////////////////////////////////////////////////////////////////////////////// -//// Query API -//////////////////////////////////////////////////////////////////////////////// +ecs_entity_t flecs_get_ref_entity( + const ecs_query_ref_t *ref, + ecs_flags16_t flag, + const ecs_query_run_ctx_t *ctx); -/* Match table with term */ -bool flecs_term_match_table( - ecs_world_t *world, - const ecs_term_t *term, - const ecs_table_t *table, - ecs_id_t *id_out, - int32_t *column_out, - ecs_entity_t *subject_out, - int32_t *match_indices, - bool first, - ecs_flags32_t iter_flags); +ecs_id_t flecs_query_op_get_id_w_written( + const ecs_query_op_t *op, + uint64_t written, + const ecs_query_run_ctx_t *ctx); -/* Match table with filter */ -bool flecs_filter_match_table( - ecs_world_t *world, - const ecs_filter_t *filter, - const ecs_table_t *table, - ecs_id_t *ids, - int32_t *columns, - ecs_entity_t *sources, - int32_t *match_indices, - int32_t *matches_left, - bool first, - int32_t skip_term, - ecs_flags32_t iter_flags); +ecs_id_t flecs_query_op_get_id( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx); -ecs_iter_t flecs_filter_iter_w_flags( - const ecs_world_t *stage, - const ecs_filter_t *filter, - ecs_flags32_t flags); +int16_t flecs_query_next_column( + ecs_table_t *table, + ecs_id_t id, + int32_t column); -void flecs_query_notify( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_event_t *event); +void flecs_query_it_set_tr( + ecs_iter_t *it, + int32_t field_index, + const ecs_table_record_t *tr); -ecs_id_t flecs_to_public_id( - ecs_id_t id); +ecs_id_t flecs_query_it_set_id( + ecs_iter_t *it, + ecs_table_t *table, + int32_t field_index, + int32_t column); -ecs_id_t flecs_from_public_id( - ecs_world_t *world, - ecs_id_t id); +void flecs_query_set_match( + const ecs_query_op_t *op, + ecs_table_t *table, + int32_t column, + const ecs_query_run_ctx_t *ctx); -void flecs_filter_apply_iter_flags( - ecs_iter_t *it, - const ecs_filter_t *filter); +void flecs_query_set_trav_match( + const ecs_query_op_t *op, + const ecs_table_record_t *tr, + ecs_entity_t trav, + ecs_entity_t second, + const ecs_query_run_ctx_t *ctx); -//////////////////////////////////////////////////////////////////////////////// -//// Safe(r) integer casting -//////////////////////////////////////////////////////////////////////////////// +bool flecs_query_table_filter( + ecs_table_t *table, + ecs_query_lbl_t other, + ecs_flags32_t filter_mask); -#define FLECS_CONVERSION_ERR(T, value)\ - "illegal conversion from value " #value " to type " #T +bool flecs_query_setids( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); -#define flecs_signed_char__ (CHAR_MIN < 0) -#define flecs_signed_short__ true -#define flecs_signed_int__ true -#define flecs_signed_long__ true -#define flecs_signed_size_t__ false -#define flecs_signed_int8_t__ true -#define flecs_signed_int16_t__ true -#define flecs_signed_int32_t__ true -#define flecs_signed_int64_t__ true -#define flecs_signed_intptr_t__ true -#define flecs_signed_uint8_t__ false -#define flecs_signed_uint16_t__ false -#define flecs_signed_uint32_t__ false -#define flecs_signed_uint64_t__ false -#define flecs_signed_uintptr_t__ false -#define flecs_signed_ecs_size_t__ true -#define flecs_signed_ecs_entity_t__ false +bool flecs_query_run_until( + bool redo, + ecs_query_run_ctx_t *ctx, + const ecs_query_op_t *ops, + ecs_query_lbl_t first, + ecs_query_lbl_t cur, + int32_t last); -uint64_t flecs_ito_( - size_t dst_size, - bool dst_signed, - bool lt_zero, - uint64_t value, - const char *err); -#ifndef FLECS_NDEBUG -#define flecs_ito(T, value)\ - (T)flecs_ito_(\ - sizeof(T),\ - flecs_signed_##T##__,\ - (value) < 0,\ - (uint64_t)(value),\ - FLECS_CONVERSION_ERR(T, (value))) +/* Select evaluation */ -#define flecs_uto(T, value)\ - (T)flecs_ito_(\ - sizeof(T),\ - flecs_signed_##T##__,\ - false,\ - (uint64_t)(value),\ - FLECS_CONVERSION_ERR(T, (value))) -#else -#define flecs_ito(T, value) (T)(value) -#define flecs_uto(T, value) (T)(value) -#endif +bool flecs_query_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); -#define flecs_itosize(value) flecs_ito(size_t, (value)) -#define flecs_utosize(value) flecs_uto(ecs_size_t, (value)) -#define flecs_itoi16(value) flecs_ito(int16_t, (value)) -#define flecs_itoi32(value) flecs_ito(int32_t, (value)) +bool flecs_query_select_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_flags32_t table_filter); -//////////////////////////////////////////////////////////////////////////////// -//// Entity filter -//////////////////////////////////////////////////////////////////////////////// +bool flecs_query_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); -void flecs_entity_filter_init( - ecs_world_t *world, - ecs_entity_filter_t **entity_filter, - const ecs_filter_t *filter, - const ecs_table_t *table, - ecs_id_t *ids, - int32_t *columns); +bool flecs_query_with_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); -void flecs_entity_filter_fini( - ecs_world_t *world, - ecs_entity_filter_t *entity_filter); +bool flecs_query_select_w_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_id_t id, + ecs_flags32_t filter_mask); -int flecs_entity_filter_next( - ecs_entity_filter_iter_t *it); -//////////////////////////////////////////////////////////////////////////////// -//// Utilities -//////////////////////////////////////////////////////////////////////////////// +/* Union evaluation */ -uint64_t flecs_hash( - const void *data, - ecs_size_t length); +bool flecs_query_union_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); -uint64_t flecs_wyhash( - const void *data, - ecs_size_t length); +bool flecs_query_union( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); -/* Get next power of 2 */ -int32_t flecs_next_pow_of_2( - int32_t n); +bool flecs_query_union_neq( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); -/* Convert 64bit value to ecs_record_t type. ecs_record_t is stored as 64bit int in the - * entity index */ -ecs_record_t flecs_to_row( - uint64_t value); +bool flecs_query_union_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + bool neq); -/* Get 64bit integer from ecs_record_t */ -uint64_t flecs_from_row( - ecs_record_t record); +bool flecs_query_union_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); -/* Convert a symbol name to an entity name by removing the prefix */ -const char* flecs_name_from_symbol( - ecs_world_t *world, - const char *type_name); +bool flecs_query_union_self_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); -/* Compare function for entity ids */ -int flecs_entity_compare( - ecs_entity_t e1, - const void *ptr1, - ecs_entity_t e2, - const void *ptr2); -bool flecs_name_is_id( - const char *name); +/* Toggle evaluation*/ -ecs_entity_t flecs_name_to_id( - const ecs_world_t *world, - const char *name); +bool flecs_query_toggle( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); -/* Convert floating point to string */ -char * ecs_ftoa( - double f, - char * buf, - int precision); +bool flecs_query_toggle_option( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); -uint64_t flecs_string_hash( - const void *ptr); -void flecs_table_hashmap_init( - ecs_world_t *world, - ecs_hashmap_t *hm); +/* Equality predicate evaluation */ -void flecs_colorize_buf( - char *msg, - bool enable_colors, - ecs_strbuf_t *buf); +bool flecs_query_pred_eq_match( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); -bool flecs_isident( - char ch); +bool flecs_query_pred_neq_match( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); -int32_t flecs_search_w_idr( - const ecs_world_t *world, - const ecs_table_t *table, - ecs_id_t id, - ecs_id_t *id_out, - ecs_id_record_t *idr); +bool flecs_query_pred_eq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); -int32_t flecs_search_relation_w_idr( - const ecs_world_t *world, - const ecs_table_t *table, - int32_t offset, - ecs_id_t id, - ecs_entity_t rel, - ecs_flags32_t flags, - ecs_entity_t *subject_out, - ecs_id_t *id_out, - struct ecs_table_record_t **tr_out, - ecs_id_record_t *idr); +bool flecs_query_pred_eq_name( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); -bool flecs_type_can_inherit_id( - const ecs_world_t *world, - const ecs_table_t *table, - const ecs_id_record_t *idr, - ecs_id_t id); +bool flecs_query_pred_neq_w_range( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx, + ecs_table_range_t r); -#endif +bool flecs_query_pred_neq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); +bool flecs_query_pred_neq_name( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); -/* -- Identifier Component -- */ -static ECS_DTOR(EcsIdentifier, ptr, { - ecs_os_strset(&ptr->value, NULL); -}) -static ECS_COPY(EcsIdentifier, dst, src, { - ecs_os_strset(&dst->value, src->value); - dst->hash = src->hash; - dst->length = src->length; - dst->index_hash = src->index_hash; - dst->index = src->index; -}) +/* Component member evaluation */ -static ECS_MOVE(EcsIdentifier, dst, src, { - ecs_os_strset(&dst->value, NULL); - dst->value = src->value; - dst->hash = src->hash; - dst->length = src->length; - dst->index_hash = src->index_hash; - dst->index = src->index; +bool flecs_query_member_eq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); - src->value = NULL; - src->hash = 0; - src->index_hash = 0; - src->index = 0; - src->length = 0; -}) +bool flecs_query_member_neq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); -static -void ecs_on_set(EcsIdentifier)(ecs_iter_t *it) { - EcsIdentifier *ptr = ecs_field(it, EcsIdentifier, 1); - - ecs_world_t *world = it->real_world; - ecs_entity_t evt = it->event; - ecs_id_t evt_id = it->event_id; - ecs_entity_t kind = ECS_PAIR_SECOND(evt_id); /* Name, Symbol, Alias */ - ecs_id_t pair = ecs_childof(0); - ecs_hashmap_t *index = NULL; - if (kind == EcsSymbol) { - index = &world->symbols; - } else if (kind == EcsAlias) { - index = &world->aliases; - } else if (kind == EcsName) { - ecs_assert(it->table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_search(world, it->table, ecs_childof(EcsWildcard), &pair); - ecs_assert(pair != 0, ECS_INTERNAL_ERROR, NULL); +/* Up traversal */ - if (evt == EcsOnSet) { - index = flecs_id_name_index_ensure(world, pair); - } else { - index = flecs_id_name_index_get(world, pair); - } - } +typedef enum ecs_query_up_select_trav_kind_t { + FlecsQueryUpSelectUp, + FlecsQueryUpSelectSelfUp +} ecs_query_up_select_trav_kind_t; - int i, count = it->count; +typedef enum ecs_query_up_select_kind_t { + FlecsQueryUpSelectDefault, + FlecsQueryUpSelectId, + FlecsQueryUpSelectUnion +} ecs_query_up_select_kind_t; - for (i = 0; i < count; i ++) { - EcsIdentifier *cur = &ptr[i]; - uint64_t hash; - ecs_size_t len; - const char *name = cur->value; +bool flecs_query_up_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_query_up_select_trav_kind_t trav_kind, + ecs_query_up_select_kind_t kind); - if (cur->index && cur->index != index) { - /* If index doesn't match up, the value must have been copied from - * another entity, so reset index & cached index hash */ - cur->index = NULL; - cur->index_hash = 0; - } +bool flecs_query_up_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); - if (cur->value && (evt == EcsOnSet)) { - len = cur->length = ecs_os_strlen(name); - hash = cur->hash = flecs_hash(name, len); - } else { - len = cur->length = 0; - hash = cur->hash = 0; - cur->index = NULL; - } +bool flecs_query_self_up_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + bool id_only); - if (index) { - uint64_t index_hash = cur->index_hash; - ecs_entity_t e = it->entities[i]; - if (hash != index_hash) { - if (index_hash) { - flecs_name_index_remove(index, e, index_hash); - } - if (hash) { - flecs_name_index_ensure(index, e, name, len, hash); - cur->index_hash = hash; - cur->index = index; - } - } else { - /* Name didn't change, but the string could have been - * reallocated. Make sure name index points to correct string */ - flecs_name_index_update_name(index, e, hash, name); - } - } - } -} +/* Transitive relationship traversal */ +bool flecs_query_trav( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); -/* -- Poly component -- */ -static ECS_COPY(EcsPoly, dst, src, { - (void)dst; - (void)src; - ecs_abort(ECS_INVALID_OPERATION, "poly component cannot be copied"); -}) +/** + * @file query/util.h + * @brief Utility functions + */ -static ECS_MOVE(EcsPoly, dst, src, { - if (dst->poly && (dst->poly != src->poly)) { - ecs_poly_dtor_t *dtor = ecs_get_dtor(dst->poly); - ecs_assert(dtor != NULL, ECS_INTERNAL_ERROR, NULL); - dtor[0](dst->poly); - } - dst->poly = src->poly; - src->poly = NULL; -}) +/* Helper type for passing around context required for error messages */ +typedef struct { + const ecs_world_t *world; + const ecs_query_desc_t *desc; + ecs_query_t *query; + ecs_term_t *term; + int32_t term_index; +} ecs_query_validator_ctx_t; -static ECS_DTOR(EcsPoly, ptr, { - if (ptr->poly) { - ecs_poly_dtor_t *dtor = ecs_get_dtor(ptr->poly); - ecs_assert(dtor != NULL, ECS_INTERNAL_ERROR, NULL); - dtor[0](ptr->poly); - } -}) +/* Convert integer to label */ +ecs_query_lbl_t flecs_itolbl( + int64_t val); +/* Convert integer to variable id */ +ecs_var_id_t flecs_itovar( + int64_t val); -/* -- Builtin triggers -- */ +/* Convert unsigned integer to variable id */ +ecs_var_id_t flecs_utovar( + uint64_t val); -static -void flecs_assert_relation_unused( - ecs_world_t *world, - ecs_entity_t rel, - ecs_entity_t property) -{ - if (world->flags & (EcsWorldInit|EcsWorldFini)) { - return; - } +/* Get name for term ref */ +const char* flecs_term_ref_var_name( + ecs_term_ref_t *ref); - ecs_vec_t *marked_ids = &world->store.marked_ids; - int32_t i, count = ecs_vec_count(marked_ids); - for (i = 0; i < count; i ++) { - ecs_marked_id_t *mid = ecs_vec_get_t(marked_ids, ecs_marked_id_t, i); - if (mid->id == ecs_pair(rel, EcsWildcard)) { - /* If id is being cleaned up, no need to throw error as tables will - * be cleaned up */ - return; - } - } +/* Is term ref wildcard */ +bool flecs_term_ref_is_wildcard( + ecs_term_ref_t *ref); - bool in_use = ecs_id_in_use(world, ecs_pair(rel, EcsWildcard)); - if (property != EcsUnion) { - in_use |= ecs_id_in_use(world, rel); - } - if (in_use) { - char *r_str = ecs_get_fullpath(world, rel); - char *p_str = ecs_get_fullpath(world, property); +/* Does term use builtin predicates (eq, neq, ...)*/ +bool flecs_term_is_builtin_pred( + ecs_term_t *term); - ecs_throw(ECS_ID_IN_USE, - "cannot change property '%s' for relationship '%s': already in use", - p_str, r_str); - - ecs_os_free(r_str); - ecs_os_free(p_str); - } +/* Does term have fixed id */ +bool flecs_term_is_fixed_id( + ecs_query_t *q, + ecs_term_t *term); -error: - return; -} +/* Is term part of OR chain */ +bool flecs_term_is_or( + const ecs_query_t *q, + const ecs_term_t *term); -static -bool flecs_set_id_flag( - ecs_id_record_t *idr, - ecs_flags32_t flag) -{ - if (!(idr->flags & flag)) { - idr->flags |= flag; - return true; - } - return false; -} +/* Get ref flags (IsEntity) or IsVar) for ref (Src, First, Second) */ +ecs_flags16_t flecs_query_ref_flags( + ecs_flags16_t flags, + ecs_flags16_t kind); -static -bool flecs_unset_id_flag( - ecs_id_record_t *idr, - ecs_flags32_t flag) -{ - if ((idr->flags & flag)) { - idr->flags &= ~flag; - return true; - } - return false; -} +/* Check if variable is written */ +bool flecs_query_is_written( + ecs_var_id_t var_id, + uint64_t written); -static -void flecs_register_id_flag_for_relation( - ecs_iter_t *it, - ecs_entity_t prop, - ecs_flags32_t flag, - ecs_flags32_t not_flag, - ecs_flags32_t entity_flag) -{ - ecs_world_t *world = it->world; - ecs_entity_t event = it->event; +/* Check if ref is written (calls flecs_query_is_written)*/ +bool flecs_ref_is_written( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t kind, + uint64_t written); - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - bool changed = false; +/* Get allocator from iterator */ +ecs_allocator_t* flecs_query_get_allocator( + const ecs_iter_t *it); - if (event == EcsOnAdd) { - ecs_id_record_t *idr = flecs_id_record_ensure(world, e); - changed |= flecs_set_id_flag(idr, flag); - idr = flecs_id_record_ensure(world, ecs_pair(e, EcsWildcard)); - do { - changed |= flecs_set_id_flag(idr, flag); - } while ((idr = idr->first.next)); - if (entity_flag) flecs_add_flag(world, e, entity_flag); - } else if (event == EcsOnRemove) { - ecs_id_record_t *idr = flecs_id_record_get(world, e); - if (idr) changed |= flecs_unset_id_flag(idr, not_flag); - idr = flecs_id_record_get(world, ecs_pair(e, EcsWildcard)); - if (idr) { - do { - changed |= flecs_unset_id_flag(idr, not_flag); - } while ((idr = idr->first.next)); - } - } +/* Convert instruction kind to string */ +const char* flecs_query_op_str( + uint16_t kind); - if (changed) { - flecs_assert_relation_unused(world, e, prop); - } - } -} +/* Convert term to string */ +void flecs_term_to_buf( + const ecs_world_t *world, + const ecs_term_t *term, + ecs_strbuf_t *buf, + int32_t t); -static -void flecs_register_final(ecs_iter_t *it) { - ecs_world_t *world = it->world; - - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - if (flecs_id_record_get(world, ecs_pair(EcsIsA, e)) != NULL) { - char *e_str = ecs_get_fullpath(world, e); - ecs_throw(ECS_ID_IN_USE, - "cannot change property 'Final' for '%s': already inherited from", - e_str); - ecs_os_free(e_str); - error: - continue; - } - } -} -static -void flecs_register_on_delete(ecs_iter_t *it) { - ecs_id_t id = ecs_field_id(it, 1); - flecs_register_id_flag_for_relation(it, EcsOnDelete, - ECS_ID_ON_DELETE_FLAG(ECS_PAIR_SECOND(id)), - EcsIdOnDeleteMask, - EcsEntityIsId); -} +#ifdef FLECS_DEBUG +#define flecs_set_var_label(var, lbl) (var)->label = lbl +#else +#define flecs_set_var_label(var, lbl) +#endif -static -void flecs_register_on_delete_object(ecs_iter_t *it) { - ecs_id_t id = ecs_field_id(it, 1); - flecs_register_id_flag_for_relation(it, EcsOnDeleteTarget, - ECS_ID_ON_DELETE_TARGET_FLAG(ECS_PAIR_SECOND(id)), - EcsIdOnDeleteObjectMask, - EcsEntityIsId); -} +/* Finalize query data & validate */ +int flecs_query_finalize_query( + ecs_world_t *world, + ecs_query_t *q, + const ecs_query_desc_t *desc); -static -void flecs_register_traversable(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsAcyclic, EcsIdTraversable, - EcsIdTraversable, 0); -} +/* Internal function for creating iterator, doesn't run aperiodic tasks */ +ecs_iter_t flecs_query_iter( + const ecs_world_t *world, + const ecs_query_t *q); -static -void flecs_register_tag(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsTag, EcsIdTag, ~EcsIdTag, 0); +/* Internal function for initializing an iterator after vars are constrained */ +void flecs_query_iter_constrain( + ecs_iter_t *it); - /* Ensure that all id records for tag have type info set to NULL */ - ecs_world_t *world = it->real_world; - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; +/** + * @file observable.h + * @brief Functions for sending events. + */ - if (it->event == EcsOnAdd) { - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_pair(e, EcsWildcard)); - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - do { - if (idr->type_info != NULL) { - flecs_assert_relation_unused(world, e, EcsTag); - } - idr->type_info = NULL; - } while ((idr = idr->first.next)); - } - } -} +#ifndef FLECS_OBSERVABLE_H +#define FLECS_OBSERVABLE_H -static -void flecs_register_exclusive(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsExclusive, EcsIdExclusive, - EcsIdExclusive, 0); -} +/** All observers for a specific (component) id */ +typedef struct ecs_event_id_record_t { + /* Triggers for Self */ + ecs_map_t self; /* map */ + ecs_map_t self_up; /* map */ + ecs_map_t up; /* map */ -static -void flecs_register_dont_inherit(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsDontInherit, - EcsIdDontInherit, EcsIdDontInherit, 0); -} + ecs_map_t observers; /* map */ -static -void flecs_register_always_override(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsAlwaysOverride, - EcsIdAlwaysOverride, EcsIdAlwaysOverride, 0); -} + /* Triggers for SuperSet, SubSet */ + ecs_map_t set_observers; /* map */ -static -void flecs_register_with(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsWith, EcsIdWith, 0, 0); -} + /* Triggers for Self with non-This subject */ + ecs_map_t entity_observers; /* map */ -static -void flecs_register_union(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsUnion, EcsIdUnion, 0, 0); -} + /* Number of active observers for (component) id */ + int32_t observer_count; +} ecs_event_id_record_t; -static -void flecs_register_slot_of(ecs_iter_t *it) { - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_add_id(it->world, it->entities[i], EcsUnion); - } -} +typedef struct ecs_observer_impl_t { + ecs_observer_t pub; -static -void flecs_on_symmetric_add_remove(ecs_iter_t *it) { - ecs_entity_t pair = ecs_field_id(it, 1); + int32_t *last_event_id; /**< Last handled event id */ + int32_t last_event_id_storage; - if (!ECS_HAS_ID_FLAG(pair, PAIR)) { - /* If relationship was not added as a pair, there's nothing to do */ - return; - } + ecs_id_t register_id; /**< Id observer is registered with (single term observers only) */ + int8_t term_index; /**< Index of the term in parent observer (single term observers only) */ - ecs_world_t *world = it->world; - ecs_entity_t rel = ECS_PAIR_FIRST(pair); - ecs_entity_t obj = ecs_pair_second(world, pair); - ecs_entity_t event = it->event; - - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t subj = it->entities[i]; - if (event == EcsOnAdd) { - if (!ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { - ecs_add_pair(it->world, obj, rel, subj); - } - } else { - if (ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { - ecs_remove_pair(it->world, obj, rel, subj); - } - } - } -} + ecs_flags32_t flags; /**< Observer flags */ + uint64_t id; /**< Internal id (not entity id) */ + ecs_vec_t children; /**< If multi observer, vector stores child observers */ -static -void flecs_register_symmetric(ecs_iter_t *it) { - ecs_world_t *world = it->real_world; + ecs_query_t *not_query; /**< Query used to populate observer data when a + term with a not operator triggers. */ - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t r = it->entities[i]; - flecs_assert_relation_unused(world, r, EcsSymmetric); + /* Mixins */ + flecs_poly_dtor_t dtor; +} ecs_observer_impl_t; - /* 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(world, { - .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} - }); - } -} +#define flecs_observer_impl(observer) (ECS_CONST_CAST(ecs_observer_impl_t*, observer)) -static -void flecs_on_component(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsComponent *c = ecs_field(it, EcsComponent, 1); +ecs_event_record_t* flecs_event_record_get( + const ecs_observable_t *o, + ecs_entity_t event); - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; +ecs_event_record_t* flecs_event_record_ensure( + ecs_observable_t *o, + ecs_entity_t event); - uint32_t component_id = (uint32_t)e; /* Strip generation */ - ecs_assert(component_id < ECS_MAX_COMPONENT_ID, ECS_OUT_OF_RANGE, - "component id must be smaller than %u", ECS_MAX_COMPONENT_ID); - (void)component_id; +ecs_event_id_record_t* flecs_event_id_record_get( + const ecs_event_record_t *er, + ecs_id_t id); - if (it->event == EcsOnSet) { - if (flecs_type_info_init_id( - world, e, c[i].size, c[i].alignment, NULL)) - { - flecs_assert_relation_unused(world, e, ecs_id(EcsComponent)); - } - } else if (it->event == EcsOnRemove) { - flecs_type_info_free(world, e); - } - } -} +ecs_event_id_record_t* flecs_event_id_record_ensure( + ecs_world_t *world, + ecs_event_record_t *er, + ecs_id_t id); -static -void flecs_ensure_module_tag(ecs_iter_t *it) { - ecs_world_t *world = it->world; +void flecs_event_id_record_remove( + ecs_event_record_t *er, + ecs_id_t id); - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); - if (parent) { - ecs_add_id(world, parent, EcsModule); - } - } -} +void flecs_observable_init( + ecs_observable_t *observable); -/* -- Iterable mixins -- */ +void flecs_observable_fini( + ecs_observable_t *observable); -static -void flecs_on_event_iterable_init( - const ecs_world_t *world, - const ecs_poly_t *poly, /* Observable */ - ecs_iter_t *it, - ecs_term_t *filter) -{ - ecs_iter_poly(world, poly, it, filter); - it->event_id = filter->id; -} +bool flecs_observers_exist( + ecs_observable_t *observable, + ecs_id_t id, + ecs_entity_t event); -/* -- Bootstrapping -- */ +ecs_observer_t* flecs_observer_init( + ecs_world_t *world, + ecs_entity_t entity, + const ecs_observer_desc_t *desc); -#define flecs_bootstrap_builtin_t(world, table, name)\ - flecs_bootstrap_builtin(world, table, ecs_id(name), #name, sizeof(name),\ - ECS_ALIGNOF(name)) +void flecs_observer_fini( + ecs_observer_t *observer); -static -void flecs_bootstrap_builtin( +void flecs_emit( + ecs_world_t *world, + ecs_world_t *stage, + ecs_flags64_t set_mask, + ecs_event_desc_t *desc); + +bool flecs_default_next_callback( + ecs_iter_t *it); + +void flecs_observers_invoke( ecs_world_t *world, + ecs_map_t *observers, + ecs_iter_t *it, ecs_table_t *table, - ecs_entity_t entity, - const char *symbol, - ecs_size_t size, - ecs_size_t alignment) -{ - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t trav); - ecs_column_t *columns = table->data.columns; - ecs_assert(columns != NULL, ECS_INTERNAL_ERROR, NULL); +void flecs_emit_propagate_invalidate( + ecs_world_t *world, + ecs_table_t *table, + int32_t offset, + int32_t count); - ecs_record_t *record = flecs_entities_ensure(world, entity); - record->table = table; +void flecs_observer_set_disable_bit( + ecs_world_t *world, + ecs_entity_t e, + ecs_flags32_t bit, + bool cond); - int32_t index = flecs_table_append(world, table, entity, false, false); - record->row = ECS_ROW_TO_RECORD(index, 0); +#endif - EcsComponent *component = ecs_vec_first(&columns[0].data); - component[index].size = size; - component[index].alignment = alignment; +/** + * @file iter.h + * @brief Iterator utilities. + */ - const char *name = &symbol[3]; /* Strip 'Ecs' */ - ecs_size_t symbol_length = ecs_os_strlen(symbol); - ecs_size_t name_length = symbol_length - 3; +#ifndef FLECS_ITER_H +#define FLECS_ITER_H - EcsIdentifier *name_col = ecs_vec_first(&columns[1].data); - uint64_t name_hash = flecs_hash(name, name_length); - name_col[index].value = ecs_os_strdup(name); - name_col[index].length = name_length; - name_col[index].hash = name_hash; - name_col[index].index_hash = 0; - name_col[index].index = table->_->name_index; - flecs_name_index_ensure( - table->_->name_index, entity, name, name_length, name_hash); +void flecs_iter_init( + const ecs_world_t *world, + ecs_iter_t *it, + ecs_flags8_t fields); - EcsIdentifier *symbol_col = ecs_vec_first(&columns[2].data); - symbol_col[index].value = ecs_os_strdup(symbol); - symbol_col[index].length = symbol_length; - symbol_col[index].hash = flecs_hash(symbol, symbol_length); - symbol_col[index].index_hash = 0; - symbol_col[index].index = NULL; -} +void* flecs_iter_calloc( + ecs_iter_t *it, + ecs_size_t size, + ecs_size_t align); -/** Initialize component table. This table is manually constructed to bootstrap - * flecs. After this function has been called, the builtin components can be - * created. - * The reason this table is constructed manually is because it requires the size - * and alignment of the EcsComponent and EcsIdentifier components, which haven't - * been created yet */ -static -ecs_table_t* flecs_bootstrap_component_table( - ecs_world_t *world) -{ - /* Before creating table, manually set flags for ChildOf/Identifier, as this - * can no longer be done after they are in use. */ - ecs_id_record_t *idr = flecs_id_record_ensure(world, EcsChildOf); - idr->flags |= EcsIdOnDeleteObjectDelete | EcsIdDontInherit | - EcsIdTraversable | EcsIdTag; +#define flecs_iter_calloc_t(it, T)\ + flecs_iter_calloc(it, ECS_SIZEOF(T), ECS_ALIGNOF(T)) - /* Initialize id records cached on world */ - world->idr_childof_wildcard = flecs_id_record_ensure(world, - ecs_pair(EcsChildOf, EcsWildcard)); - world->idr_childof_wildcard->flags |= EcsIdOnDeleteObjectDelete | - EcsIdDontInherit | EcsIdTraversable | EcsIdTag | EcsIdExclusive; - idr = flecs_id_record_ensure(world, ecs_pair_t(EcsIdentifier, EcsWildcard)); - idr->flags |= EcsIdDontInherit; - world->idr_identifier_name = - flecs_id_record_ensure(world, ecs_pair_t(EcsIdentifier, EcsName)); - world->idr_childof_0 = flecs_id_record_ensure(world, - ecs_pair(EcsChildOf, 0)); +#define flecs_iter_calloc_n(it, T, count)\ + flecs_iter_calloc(it, ECS_SIZEOF(T) * count, ECS_ALIGNOF(T)) - ecs_id_t ids[] = { - ecs_id(EcsComponent), - EcsFinal, - ecs_pair_t(EcsIdentifier, EcsName), - ecs_pair_t(EcsIdentifier, EcsSymbol), - ecs_pair(EcsChildOf, EcsFlecsCore), - ecs_pair(EcsOnDelete, EcsPanic) - }; +void flecs_iter_free( + void *ptr, + ecs_size_t size); - ecs_type_t array = { - .array = ids, - .count = 6 - }; +#define flecs_iter_free_t(ptr, T)\ + flecs_iter_free(ptr, ECS_SIZEOF(T)) - ecs_table_t *result = flecs_table_find_or_create(world, &array); - ecs_data_t *data = &result->data; +#define flecs_iter_free_n(ptr, T, count)\ + flecs_iter_free(ptr, ECS_SIZEOF(T) * count) - /* Preallocate enough memory for initial components */ - ecs_allocator_t *a = &world->allocator; - ecs_vec_init_t(a, &data->entities, ecs_entity_t, EcsFirstUserComponentId); - ecs_vec_init_t(a, &data->columns[0].data, EcsComponent, EcsFirstUserComponentId); - ecs_vec_init_t(a, &data->columns[1].data, EcsIdentifier, EcsFirstUserComponentId); - ecs_vec_init_t(a, &data->columns[2].data, EcsIdentifier, EcsFirstUserComponentId); - - return result; -} +#endif -static -void flecs_bootstrap_entity( - ecs_world_t *world, - ecs_entity_t id, - const char *name, - ecs_entity_t parent) -{ - char symbol[256]; - ecs_os_strcpy(symbol, "flecs.core."); - ecs_os_strcat(symbol, name); - - ecs_ensure(world, id); - ecs_add_pair(world, id, EcsChildOf, parent); - ecs_set_name(world, id, name); - ecs_set_symbol(world, id, symbol); +/** + * @file poly.h + * @brief Functions for managing poly objects. + */ - ecs_assert(ecs_get_name(world, id) != NULL, ECS_INTERNAL_ERROR, NULL); +#ifndef FLECS_POLY_H +#define FLECS_POLY_H - if (!parent || parent == EcsFlecsCore) { - ecs_assert(ecs_lookup_fullpath(world, name) == id, - ECS_INTERNAL_ERROR, NULL); - } -} +#include -void flecs_bootstrap( - ecs_world_t *world) -{ - ecs_log_push(); +/* Initialize poly */ +void* flecs_poly_init_( + ecs_poly_t *object, + int32_t kind, + ecs_size_t size, + ecs_mixins_t *mixins); - ecs_set_name_prefix(world, "Ecs"); +#define flecs_poly_init(object, type)\ + flecs_poly_init_(object, type##_magic, sizeof(type), &type##_mixins) - /* Ensure builtin ids are alive */ - ecs_ensure(world, ecs_id(EcsComponent)); - ecs_ensure(world, EcsFinal); - ecs_ensure(world, ecs_id(EcsIdentifier)); - ecs_ensure(world, EcsName); - ecs_ensure(world, EcsSymbol); - ecs_ensure(world, EcsAlias); - ecs_ensure(world, EcsChildOf); - ecs_ensure(world, EcsFlecs); - ecs_ensure(world, EcsFlecsCore); - ecs_ensure(world, EcsOnAdd); - ecs_ensure(world, EcsOnRemove); - ecs_ensure(world, EcsOnSet); - ecs_ensure(world, EcsUnSet); - ecs_ensure(world, EcsOnDelete); - ecs_ensure(world, EcsPanic); - ecs_ensure(world, EcsFlag); - ecs_ensure(world, EcsIsA); - ecs_ensure(world, EcsWildcard); - ecs_ensure(world, EcsAny); - ecs_ensure(world, EcsTag); +/* Deinitialize object for specified type */ +void flecs_poly_fini_( + ecs_poly_t *object, + int32_t kind); - /* Register type information for builtin components */ - flecs_type_info_init(world, EcsComponent, { - .ctor = ecs_default_ctor, - .on_set = flecs_on_component, - .on_remove = flecs_on_component - }); +#define flecs_poly_fini(object, type)\ + flecs_poly_fini_(object, type##_magic) - flecs_type_info_init(world, EcsIdentifier, { - .ctor = ecs_default_ctor, - .dtor = ecs_dtor(EcsIdentifier), - .copy = ecs_copy(EcsIdentifier), - .move = ecs_move(EcsIdentifier), - .on_set = ecs_on_set(EcsIdentifier), - .on_remove = ecs_on_set(EcsIdentifier) - }); +/* Utility functions for creating an object on the heap */ +#define flecs_poly_new(type)\ + (type*)flecs_poly_init(ecs_os_calloc_t(type), type) - flecs_type_info_init(world, EcsPoly, { - .ctor = ecs_default_ctor, - .copy = ecs_copy(EcsPoly), - .move = ecs_move(EcsPoly), - .dtor = ecs_dtor(EcsPoly) - }); +#define flecs_poly_free(obj, type)\ + flecs_poly_fini(obj, type);\ + ecs_os_free(obj) - flecs_type_info_init(world, EcsIterable, { 0 }); - flecs_type_info_init(world, EcsTarget, { 0 }); +/* Get or create poly component for an entity */ +EcsPoly* flecs_poly_bind_( + ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t tag); - /* Create and cache often used id records on world */ - flecs_init_id_records(world); +#define flecs_poly_bind(world, entity, T) \ + flecs_poly_bind_(world, entity, T##_tag) - /* Create table for builtin components. This table temporarily stores the - * entities associated with builtin components, until they get moved to - * other tables once properties are added (see below) */ - ecs_table_t *table = flecs_bootstrap_component_table(world); - assert(table != NULL); +void flecs_poly_modified_( + ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t tag); - /* Bootstrap builtin components */ - flecs_bootstrap_builtin_t(world, table, EcsIdentifier); - flecs_bootstrap_builtin_t(world, table, EcsComponent); - flecs_bootstrap_builtin_t(world, table, EcsIterable); - flecs_bootstrap_builtin_t(world, table, EcsPoly); - flecs_bootstrap_builtin_t(world, table, EcsTarget); +#define flecs_poly_modified(world, entity, T) \ + flecs_poly_modified_(world, entity, T##_tag) - /* Initialize default entity id range */ - world->info.last_component_id = EcsFirstUserComponentId; - flecs_entities_max_id(world) = EcsFirstUserEntityId; - world->info.min_id = 0; - world->info.max_id = 0; +/* Get poly component for an entity */ +const EcsPoly* flecs_poly_bind_get_( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t tag); - /* Make EcsOnAdd, EcsOnSet events iterable to enable .yield_existing */ - ecs_set(world, EcsOnAdd, EcsIterable, { .init = flecs_on_event_iterable_init }); - ecs_set(world, EcsOnSet, EcsIterable, { .init = flecs_on_event_iterable_init }); - - /* Register observer for tag property before adding EcsTag */ - ecs_observer(world, { - .entity = ecs_entity(world, {.add = { ecs_childof(EcsFlecsInternals)}}), - .filter.terms[0] = { .id = EcsTag, .src.flags = EcsSelf }, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = flecs_register_tag, - .yield_existing = true - }); +#define flecs_poly_bind_get(world, entity, T) \ + flecs_poly_bind_get_(world, entity, T##_tag) - /* Populate core module */ - ecs_set_scope(world, EcsFlecsCore); +ecs_poly_t* flecs_poly_get_( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t tag); - flecs_bootstrap_tag(world, EcsName); - flecs_bootstrap_tag(world, EcsSymbol); - flecs_bootstrap_tag(world, EcsAlias); +#define flecs_poly_get(world, entity, T) \ + ((T*)flecs_poly_get_(world, entity, T##_tag)) - flecs_bootstrap_tag(world, EcsQuery); - flecs_bootstrap_tag(world, EcsObserver); +/* Utilities for testing/asserting an object type */ +#ifndef FLECS_NDEBUG +#define flecs_poly_assert(object, ty)\ + do {\ + ecs_assert(object != NULL, ECS_INVALID_PARAMETER, NULL);\ + const ecs_header_t *hdr = (const ecs_header_t *)object;\ + const char *type_name = hdr->mixins->type_name;\ + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, type_name);\ + ecs_assert(hdr->type == ty##_magic, ECS_INVALID_PARAMETER, type_name);\ + } while (0) +#else +#define flecs_poly_assert(object, ty) +#endif - flecs_bootstrap_tag(world, EcsModule); - flecs_bootstrap_tag(world, EcsPrivate); - flecs_bootstrap_tag(world, EcsPrefab); - flecs_bootstrap_tag(world, EcsSlotOf); - flecs_bootstrap_tag(world, EcsDisabled); - flecs_bootstrap_tag(world, EcsEmpty); +ecs_observable_t* ecs_get_observable( + const ecs_poly_t *object); - /* Initialize builtin modules */ - ecs_set_name(world, EcsFlecs, "flecs"); - ecs_add_id(world, EcsFlecs, EcsModule); - ecs_add_pair(world, EcsFlecs, EcsOnDelete, EcsPanic); +flecs_poly_dtor_t* ecs_get_dtor( + const ecs_poly_t *poly); - ecs_add_pair(world, EcsFlecsCore, EcsChildOf, EcsFlecs); - ecs_set_name(world, EcsFlecsCore, "core"); - ecs_add_id(world, EcsFlecsCore, EcsModule); +#endif - ecs_add_pair(world, EcsFlecsInternals, EcsChildOf, EcsFlecsCore); - ecs_set_name(world, EcsFlecsInternals, "internals"); - ecs_add_id(world, EcsFlecsInternals, EcsModule); +/** + * @file stage.h + * @brief Stage functions. + */ - /* Self check */ - ecs_record_t *r = flecs_entities_get(world, EcsFlecs); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(r->row & EcsEntityIsTraversable, ECS_INTERNAL_ERROR, NULL); - (void)r; +#ifndef FLECS_STAGE_H +#define FLECS_STAGE_H - /* Initialize builtin entities */ - flecs_bootstrap_entity(world, EcsWorld, "World", EcsFlecsCore); - flecs_bootstrap_entity(world, EcsWildcard, "*", EcsFlecsCore); - flecs_bootstrap_entity(world, EcsAny, "_", EcsFlecsCore); - flecs_bootstrap_entity(world, EcsThis, "this", EcsFlecsCore); - flecs_bootstrap_entity(world, EcsVariable, "$", EcsFlecsCore); - flecs_bootstrap_entity(world, EcsFlag, "Flag", EcsFlecsCore); +/* Post-frame merge actions */ +void flecs_stage_merge_post_frame( + ecs_world_t *world, + ecs_stage_t *stage); - /* Component/relationship properties */ - flecs_bootstrap_tag(world, EcsTransitive); - flecs_bootstrap_tag(world, EcsReflexive); - flecs_bootstrap_tag(world, EcsSymmetric); - flecs_bootstrap_tag(world, EcsFinal); - flecs_bootstrap_tag(world, EcsDontInherit); - flecs_bootstrap_tag(world, EcsAlwaysOverride); - flecs_bootstrap_tag(world, EcsTag); - flecs_bootstrap_tag(world, EcsUnion); - flecs_bootstrap_tag(world, EcsExclusive); - flecs_bootstrap_tag(world, EcsAcyclic); - flecs_bootstrap_tag(world, EcsTraversable); - flecs_bootstrap_tag(world, EcsWith); - flecs_bootstrap_tag(world, EcsOneOf); - - flecs_bootstrap_tag(world, EcsOnDelete); - flecs_bootstrap_tag(world, EcsOnDeleteTarget); - flecs_bootstrap_tag(world, EcsRemove); - flecs_bootstrap_tag(world, EcsDelete); - flecs_bootstrap_tag(world, EcsPanic); +bool flecs_defer_cmd( + ecs_stage_t *stage); - flecs_bootstrap_tag(world, EcsFlatten); - flecs_bootstrap_tag(world, EcsDefaultChildComponent); +bool flecs_defer_begin( + ecs_world_t *world, + ecs_stage_t *stage); - /* Builtin predicates */ - flecs_bootstrap_tag(world, EcsPredEq); - flecs_bootstrap_tag(world, EcsPredMatch); - flecs_bootstrap_tag(world, EcsPredLookup); - flecs_bootstrap_tag(world, EcsScopeOpen); - flecs_bootstrap_tag(world, EcsScopeClose); +bool flecs_defer_modified( + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_entity_t component); - /* Builtin relationships */ - flecs_bootstrap_tag(world, EcsIsA); - flecs_bootstrap_tag(world, EcsChildOf); - flecs_bootstrap_tag(world, EcsDependsOn); +bool flecs_defer_clone( + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_entity_t src, + bool clone_value); - /* Builtin events */ - flecs_bootstrap_entity(world, EcsOnAdd, "OnAdd", EcsFlecsCore); - flecs_bootstrap_entity(world, EcsOnRemove, "OnRemove", EcsFlecsCore); - 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); +bool flecs_defer_bulk_new( + ecs_world_t *world, + ecs_stage_t *stage, + int32_t count, + ecs_id_t id, + const ecs_entity_t **ids_out); - /* Tag relationships (relationships that should never have data) */ - ecs_add_id(world, EcsIsA, EcsTag); - ecs_add_id(world, EcsChildOf, EcsTag); - ecs_add_id(world, EcsSlotOf, EcsTag); - ecs_add_id(world, EcsDependsOn, EcsTag); - ecs_add_id(world, EcsFlatten, EcsTag); - ecs_add_id(world, EcsDefaultChildComponent, EcsTag); - ecs_add_id(world, EcsUnion, EcsTag); - ecs_add_id(world, EcsFlag, EcsTag); - ecs_add_id(world, EcsWith, EcsTag); +bool flecs_defer_path( + ecs_stage_t *stage, + ecs_entity_t parent, + ecs_entity_t entity, + const char *name); - /* Exclusive properties */ - ecs_add_id(world, EcsChildOf, EcsExclusive); - ecs_add_id(world, EcsOnDelete, EcsExclusive); - ecs_add_id(world, EcsOnDeleteTarget, EcsExclusive); - ecs_add_id(world, EcsDefaultChildComponent, EcsExclusive); +bool flecs_defer_delete( + ecs_stage_t *stage, + ecs_entity_t entity); - /* Sync properties of ChildOf and Identifier with bootstrapped flags */ - ecs_add_pair(world, EcsChildOf, EcsOnDeleteTarget, EcsDelete); - ecs_add_id(world, EcsChildOf, EcsAcyclic); - ecs_add_id(world, EcsChildOf, EcsTraversable); - ecs_add_id(world, EcsChildOf, EcsDontInherit); - ecs_add_id(world, ecs_id(EcsIdentifier), EcsDontInherit); +bool flecs_defer_clear( + ecs_stage_t *stage, + ecs_entity_t entity); - /* Create triggers in internals scope */ - ecs_set_scope(world, EcsFlecsInternals); +bool flecs_defer_on_delete_action( + ecs_stage_t *stage, + ecs_id_t id, + ecs_entity_t action); - /* Term used to also match prefabs */ - ecs_term_t match_prefab = { - .id = EcsPrefab, - .oper = EcsOptional, - .src.flags = EcsSelf - }; +bool flecs_defer_enable( + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_entity_t component, + bool enable); - /* Register observers for components/relationship properties. Most observers - * set flags on an id record when a property is added to a component, which - * allows for quick property testing in various operations. */ - ecs_observer(world, { - .filter.terms = {{ .id = EcsFinal, .src.flags = EcsSelf }, match_prefab }, - .events = {EcsOnAdd}, - .callback = flecs_register_final - }); +bool flecs_defer_add( + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_id_t id); - ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(EcsOnDelete, EcsWildcard), .src.flags = EcsSelf }, - match_prefab - }, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = flecs_register_on_delete - }); +bool flecs_defer_remove( + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_id_t id); - ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(EcsOnDeleteTarget, EcsWildcard), .src.flags = EcsSelf }, - match_prefab - }, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = flecs_register_on_delete_object - }); +void* flecs_defer_set( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_cmd_kind_t op_kind, + ecs_entity_t entity, + ecs_entity_t component, + ecs_size_t size, + void *value, + bool *is_new); - ecs_observer(world, { - .filter.terms = { - { .id = EcsTraversable, .src.flags = EcsSelf }, - match_prefab - }, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = flecs_register_traversable - }); +bool flecs_defer_end( + ecs_world_t *world, + ecs_stage_t *stage); - ecs_observer(world, { - .filter.terms = {{ .id = EcsExclusive, .src.flags = EcsSelf }, match_prefab }, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = flecs_register_exclusive - }); +bool flecs_defer_purge( + ecs_world_t *world, + ecs_stage_t *stage); - ecs_observer(world, { - .filter.terms = {{ .id = EcsSymmetric, .src.flags = EcsSelf }, match_prefab }, - .events = {EcsOnAdd}, - .callback = flecs_register_symmetric - }); +void flecs_enqueue( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_event_desc_t *desc); - ecs_observer(world, { - .filter.terms = {{ .id = EcsDontInherit, .src.flags = EcsSelf }, match_prefab }, - .events = {EcsOnAdd}, - .callback = flecs_register_dont_inherit - }); +void flecs_commands_push( + ecs_stage_t *stage); - ecs_observer(world, { - .filter.terms = {{ .id = EcsAlwaysOverride, .src.flags = EcsSelf } }, - .events = {EcsOnAdd}, - .callback = flecs_register_always_override - }); +void flecs_commands_pop( + ecs_stage_t *stage); - ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(EcsWith, EcsWildcard), .src.flags = EcsSelf }, - match_prefab - }, - .events = {EcsOnAdd}, - .callback = flecs_register_with - }); +ecs_entity_t flecs_stage_set_system( + ecs_stage_t *stage, + ecs_entity_t system); - ecs_observer(world, { - .filter.terms = {{ .id = EcsUnion, .src.flags = EcsSelf }, match_prefab }, - .events = {EcsOnAdd}, - .callback = flecs_register_union - }); +ecs_allocator_t* flecs_stage_get_allocator( + ecs_world_t *world); - /* Entities used as slot are marked as exclusive to ensure a slot can always - * only point to a single entity. */ - ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(EcsSlotOf, EcsWildcard), .src.flags = EcsSelf }, - match_prefab - }, - .events = {EcsOnAdd}, - .callback = flecs_register_slot_of - }); +ecs_stack_t* flecs_stage_get_stack_allocator( + ecs_world_t *world); - /* Define observer to make sure that adding a module to a child entity also - * adds it to the parent. */ - ecs_observer(world, { - .filter.terms = {{ .id = EcsModule, .src.flags = EcsSelf }, match_prefab}, - .events = {EcsOnAdd}, - .callback = flecs_ensure_module_tag - }); +#endif - /* Set scope back to flecs core */ - ecs_set_scope(world, EcsFlecsCore); +/** + * @file world.h + * @brief World-level API. + */ - /* Traversable relationships are always acyclic */ - ecs_add_pair(world, EcsTraversable, EcsWith, EcsAcyclic); +#ifndef FLECS_WORLD_H +#define FLECS_WORLD_H - /* Transitive relationships are always Traversable */ - ecs_add_pair(world, EcsTransitive, EcsWith, EcsTraversable); +/* Get current stage */ +ecs_stage_t* flecs_stage_from_world( + ecs_world_t **world_ptr); - /* DontInherit components */ - ecs_add_id(world, EcsPrefab, EcsDontInherit); - ecs_add_id(world, ecs_id(EcsComponent), EcsDontInherit); - ecs_add_id(world, EcsOnDelete, EcsDontInherit); +/* Get current thread-specific stage from readonly world */ +ecs_stage_t* flecs_stage_from_readonly_world( + const ecs_world_t *world); - /* Acyclic/Traversable components */ - ecs_add_id(world, EcsIsA, EcsTraversable); - ecs_add_id(world, EcsDependsOn, EcsTraversable); - ecs_add_id(world, EcsWith, EcsAcyclic); +/* Get component callbacks */ +const ecs_type_info_t *flecs_type_info_get( + const ecs_world_t *world, + ecs_entity_t component); - /* Transitive relationships */ - ecs_add_id(world, EcsIsA, EcsTransitive); - ecs_add_id(world, EcsIsA, EcsReflexive); +/* Get or create component callbacks */ +ecs_type_info_t* flecs_type_info_ensure( + ecs_world_t *world, + ecs_entity_t component); - /* Exclusive properties */ - ecs_add_id(world, EcsSlotOf, EcsExclusive); - ecs_add_id(world, EcsOneOf, EcsExclusive); - ecs_add_id(world, EcsFlatten, EcsExclusive); +bool flecs_type_info_init_id( + ecs_world_t *world, + ecs_entity_t component, + ecs_size_t size, + ecs_size_t alignment, + const ecs_type_hooks_t *li); - /* Private properties */ - ecs_add_id(world, ecs_id(EcsPoly), EcsPrivate); - ecs_add_id(world, ecs_id(EcsIdentifier), EcsPrivate); - ecs_add_id(world, EcsChildOf, EcsPrivate); - ecs_add_id(world, EcsIsA, EcsPrivate); - - /* Run bootstrap functions for other parts of the code */ - flecs_bootstrap_hierarchy(world); +#define flecs_type_info_init(world, T, ...)\ + flecs_type_info_init_id(world, ecs_id(T), ECS_SIZEOF(T), ECS_ALIGNOF(T),\ + &(ecs_type_hooks_t)__VA_ARGS__) - ecs_set_scope(world, 0); +void flecs_type_info_fini( + ecs_type_info_t *ti); - ecs_set_name_prefix(world, NULL); +void flecs_type_info_free( + ecs_world_t *world, + ecs_entity_t component); - ecs_assert(world->idr_childof_wildcard != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(world->idr_isa_wildcard != NULL, ECS_INTERNAL_ERROR, NULL); +void flecs_eval_component_monitors( + ecs_world_t *world); - ecs_log_pop(); -} +void flecs_monitor_mark_dirty( + ecs_world_t *world, + ecs_entity_t id); -/** - * @file entity.c - * @brief Entity API. - * - * This file contains the implementation for the entity API, which includes - * creating/deleting entities, adding/removing/setting components, instantiating - * prefabs, and several other APIs for retrieving entity data. - * - * The file also contains the implementation of the command buffer, which is - * located here so it can call functions private to the compilation unit. - */ +void flecs_monitor_register( + ecs_world_t *world, + ecs_entity_t id, + ecs_query_t *query); -#include +void flecs_monitor_unregister( + ecs_world_t *world, + ecs_entity_t id, + ecs_query_t *query); -static -const ecs_entity_t* flecs_bulk_new( +void flecs_notify_tables( ecs_world_t *world, - ecs_table_t *table, - const ecs_entity_t *entities, - ecs_type_t *component_ids, - int32_t count, - void **c_info, - bool move, - int32_t *row_out, - ecs_table_diff_t *diff); + ecs_id_t id, + ecs_table_event_t *event); -typedef struct { - const ecs_type_info_t *ti; - void *ptr; -} flecs_component_ptr_t; +void flecs_register_table( + ecs_world_t *world, + ecs_table_t *table); -static -flecs_component_ptr_t flecs_get_component_w_index( - ecs_table_t *table, - int32_t column_index, - int32_t row) -{ - ecs_check(column_index < table->column_count, ECS_NOT_A_COMPONENT, NULL); - ecs_column_t *column = &table->data.columns[column_index]; - return (flecs_component_ptr_t){ - .ti = column->ti, - .ptr = ecs_vec_get(&column->data, column->size, row) - }; -error: - return (flecs_component_ptr_t){0}; -} +void flecs_unregister_table( + ecs_world_t *world, + ecs_table_t *table); -static -flecs_component_ptr_t flecs_get_component_ptr( - const ecs_world_t *world, - ecs_table_t *table, - int32_t row, - ecs_id_t id) -{ - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); +void flecs_table_set_empty( + ecs_world_t *world, + ecs_table_t *table); - ecs_table_record_t *tr = flecs_table_record_get(world, table, id); - if (!tr || (tr->column == -1)) { - ecs_check(tr == NULL, ECS_NOT_A_COMPONENT, NULL); - return (flecs_component_ptr_t){0}; - } +void flecs_delete_table( + ecs_world_t *world, + ecs_table_t *table); - return flecs_get_component_w_index(table, tr->column, row); -error: - return (flecs_component_ptr_t){0}; -} +void flecs_process_pending_tables( + const ecs_world_t *world); -static -void* flecs_get_component( - const ecs_world_t *world, - ecs_table_t *table, - int32_t row, - ecs_id_t id) -{ - return flecs_get_component_ptr(world, table, row, id).ptr; -} +/* Convenience macro's for world allocator */ +#define flecs_walloc(world, size)\ + flecs_alloc(&world->allocator, size) +#define flecs_walloc_t(world, T)\ + flecs_alloc_t(&world->allocator, T) +#define flecs_walloc_n(world, T, count)\ + flecs_alloc_n(&world->allocator, T, count) +#define flecs_wcalloc(world, size)\ + flecs_calloc(&world->allocator, size) +#define flecs_wfree_t(world, T, ptr)\ + flecs_free_t(&world->allocator, T, ptr) +#define flecs_wcalloc_n(world, T, count)\ + flecs_calloc_n(&world->allocator, T, count) +#define flecs_wfree(world, size, ptr)\ + flecs_free(&world->allocator, size, ptr) +#define flecs_wfree_n(world, T, count, ptr)\ + flecs_free_n(&world->allocator, T, count, ptr) +#define flecs_wrealloc(world, size_dst, size_src, ptr)\ + flecs_realloc(&world->allocator, size_dst, size_src, ptr) +#define flecs_wrealloc_n(world, T, count_dst, count_src, ptr)\ + flecs_realloc_n(&world->allocator, T, count_dst, count_src, ptr) +#define flecs_wdup(world, size, ptr)\ + flecs_dup(&world->allocator, size, ptr) +#define flecs_wdup_n(world, T, count, ptr)\ + flecs_dup_n(&world->allocator, T, count, ptr) -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) -{ - /* Cycle detected in IsA relationship */ - ecs_check(recur_depth < ECS_MAX_RECURSION, ECS_INVALID_PARAMETER, NULL); +#endif - /* Table (and thus entity) does not have component, look for base */ - if (!(table->flags & EcsTableHasIsA)) { - return NULL; - } +/** + * @file datastructures/name_index.h + * @brief Data structure for resolving 64bit keys by string (name). + */ - /* Exclude Name */ - if (id == ecs_pair(ecs_id(EcsIdentifier), EcsName)) { - return NULL; - } +#ifndef FLECS_NAME_INDEX_H +#define FLECS_NAME_INDEX_H - /* Table should always be in the table index for (IsA, *), otherwise the - * HasBase flag should not have been set */ - ecs_table_record_t *tr_isa = flecs_id_record_get_table( - world->idr_isa_wildcard, table); - ecs_check(tr_isa != NULL, ECS_INTERNAL_ERROR, NULL); +void flecs_name_index_init( + ecs_hashmap_t *hm, + ecs_allocator_t *allocator); - ecs_type_t type = table->type; - ecs_id_t *ids = type.array; - int32_t i = tr_isa->index, end = tr_isa->count + tr_isa->index; - void *ptr = NULL; +void flecs_name_index_init_if( + ecs_hashmap_t *hm, + ecs_allocator_t *allocator); - do { - ecs_id_t pair = ids[i ++]; - ecs_entity_t base = ecs_pair_second(world, pair); +bool flecs_name_index_is_init( + const ecs_hashmap_t *hm); - ecs_record_t *r = flecs_entities_get(world, base); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); +ecs_hashmap_t* flecs_name_index_new( + ecs_world_t *world, + ecs_allocator_t *allocator); - table = r->table; - if (!table) { - continue; - } +void flecs_name_index_fini( + ecs_hashmap_t *map); - const ecs_table_record_t *tr = - flecs_id_record_get_table(table_index, table); - if (!tr || tr->column == -1) { - ptr = flecs_get_base_component(world, table, id, table_index, - recur_depth + 1); - } else { - int32_t row = ECS_RECORD_TO_ROW(r->row); - ptr = flecs_get_component_w_index(table, tr->column, row).ptr; - } - } while (!ptr && (i < end)); +void flecs_name_index_free( + ecs_hashmap_t *map); - return ptr; -error: - return NULL; -} +ecs_hashmap_t* flecs_name_index_copy( + ecs_hashmap_t *dst); -static -void flecs_instantiate_slot( - ecs_world_t *world, - ecs_entity_t base, - ecs_entity_t instance, - ecs_entity_t slot_of, - ecs_entity_t slot, - ecs_entity_t child) -{ - if (base == slot_of) { - /* Instance inherits from slot_of, add slot to instance */ - ecs_add_pair(world, instance, slot, child); - } else { - /* Slot is registered for other prefab, travel hierarchy - * upwards to find instance that inherits from slot_of */ - ecs_entity_t parent = instance; - int32_t depth = 0; - do { - if (ecs_has_pair(world, parent, EcsIsA, slot_of)) { - const char *name = ecs_get_name(world, slot); - if (name == NULL) { - char *slot_of_str = ecs_get_fullpath(world, slot_of); - ecs_throw(ECS_INVALID_OPERATION, "prefab '%s' has unnamed " - "slot (slots must be named)", slot_of_str); - ecs_os_free(slot_of_str); - return; - } +ecs_hashed_string_t flecs_get_hashed_string( + const char *name, + ecs_size_t length, + uint64_t hash); - /* The 'slot' variable is currently pointing to a child (or - * grandchild) of the current base. Find the original slot by - * looking it up under the prefab it was registered. */ - if (depth == 0) { - /* If the current instance is an instance of slot_of, just - * lookup the slot by name, which is faster than having to - * create a relative path. */ - slot = ecs_lookup_child(world, slot_of, name); - } else { - /* If the slot is more than one level away from the slot_of - * parent, use a relative path to find the slot */ - char *path = ecs_get_path_w_sep(world, parent, child, ".", - NULL); - slot = ecs_lookup_path_w_sep(world, slot_of, path, ".", - NULL, false); - ecs_os_free(path); - } +const uint64_t* flecs_name_index_find_ptr( + const ecs_hashmap_t *map, + const char *name, + ecs_size_t length, + uint64_t hash); - if (slot == 0) { - char *slot_of_str = ecs_get_fullpath(world, slot_of); - char *slot_str = ecs_get_fullpath(world, slot); - ecs_throw(ECS_INVALID_OPERATION, - "'%s' is not in hierarchy for slot '%s'", - slot_of_str, slot_str); - ecs_os_free(slot_of_str); - ecs_os_free(slot_str); - } +uint64_t flecs_name_index_find( + const ecs_hashmap_t *map, + const char *name, + ecs_size_t length, + uint64_t hash); - ecs_add_pair(world, parent, slot, child); - break; - } +void flecs_name_index_ensure( + ecs_hashmap_t *map, + uint64_t id, + const char *name, + ecs_size_t length, + uint64_t hash); - depth ++; - } while ((parent = ecs_get_target(world, parent, EcsChildOf, 0))); - - if (parent == 0) { - char *slot_of_str = ecs_get_fullpath(world, slot_of); - char *slot_str = ecs_get_fullpath(world, slot); - ecs_throw(ECS_INVALID_OPERATION, - "'%s' is not in hierarchy for slot '%s'", - slot_of_str, slot_str); - ecs_os_free(slot_of_str); - ecs_os_free(slot_str); - } - } +void flecs_name_index_remove( + ecs_hashmap_t *map, + uint64_t id, + uint64_t hash); -error: - return; -} +void flecs_name_index_update_name( + ecs_hashmap_t *map, + uint64_t e, + uint64_t hash, + const char *name); -static -ecs_table_t* flecs_find_table_add( +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +//// Bootstrap API +//////////////////////////////////////////////////////////////////////////////// + +/* Bootstrap world */ +void flecs_bootstrap( + ecs_world_t *world); + +#define flecs_bootstrap_component(world, id_)\ + ecs_component_init(world, &(ecs_component_desc_t){\ + .entity = ecs_entity(world, { .id = ecs_id(id_), .name = #id_, .symbol = #id_ }),\ + .type.size = sizeof(id_),\ + .type.alignment = ECS_ALIGNOF(id_)\ + }); + +#define flecs_bootstrap_tag(world, name)\ + ecs_make_alive(world, name);\ + ecs_add_id(world, name, EcsFinal);\ + ecs_add_pair(world, name, EcsChildOf, ecs_get_scope(world));\ + ecs_set_name(world, name, (const char*)&#name[ecs_os_strlen(world->info.name_prefix)]);\ + ecs_set_symbol(world, name, #name); + +#define flecs_bootstrap_trait(world, name)\ + flecs_bootstrap_tag(world, name)\ + ecs_add_id(world, name, EcsTrait) + + +/* Bootstrap functions for other parts in the code */ +void flecs_bootstrap_hierarchy(ecs_world_t *world); + + +//////////////////////////////////////////////////////////////////////////////// +//// Entity API +//////////////////////////////////////////////////////////////////////////////// + +/* Mark an entity as being watched. This is used to trigger automatic rematching + * when entities used in system expressions change their components. */ +void flecs_add_flag( + ecs_world_t *world, + ecs_entity_t entity, + uint32_t flag); + +void flecs_record_add_flag( + ecs_record_t *record, + uint32_t flag); + +ecs_entity_t flecs_get_oneof( + const ecs_world_t *world, + ecs_entity_t e); + +void flecs_notify_on_remove( ecs_world_t *world, ecs_table_t *table, - ecs_id_t id, - ecs_table_diff_builder_t *diff) -{ - ecs_table_diff_t temp_diff = ECS_TABLE_DIFF_INIT; - table = flecs_table_traverse_add(world, table, &id, &temp_diff); - ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_table_diff_build_append_table(world, diff, &temp_diff); - return table; -error: - return NULL; -} + ecs_table_t *other_table, + int32_t row, + int32_t count, + const ecs_table_diff_t *diff); -static -ecs_table_t* flecs_find_table_remove( +void flecs_notify_on_set( ecs_world_t *world, ecs_table_t *table, - ecs_id_t id, - ecs_table_diff_builder_t *diff) -{ - ecs_table_diff_t temp_diff = ECS_TABLE_DIFF_INIT; - table = flecs_table_traverse_remove(world, table, &id, &temp_diff); - ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_table_diff_build_append_table(world, diff, &temp_diff); - return table; -error: - return NULL; -} + int32_t row, + int32_t count, + ecs_type_t *type, + bool owned); -static -void flecs_instantiate_children( +int32_t flecs_relation_depth( + const ecs_world_t *world, + ecs_entity_t r, + const ecs_table_t *table); + +typedef struct ecs_instantiate_ctx_t { + ecs_entity_t root_prefab; + ecs_entity_t root_instance; +} ecs_instantiate_ctx_t; + +void flecs_instantiate( ecs_world_t *world, ecs_entity_t base, ecs_table_t *table, int32_t row, int32_t count, - ecs_table_t *child_table) -{ - if (!ecs_table_count(child_table)) { - return; - } - - ecs_type_t type = child_table->type; - ecs_data_t *child_data = &child_table->data; + const ecs_instantiate_ctx_t *ctx); - ecs_entity_t slot_of = 0; - ecs_entity_t *ids = type.array; - int32_t type_count = type.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); - /* Instantiate child table for each instance */ +void flecs_invoke_hook( + ecs_world_t *world, + ecs_table_t *table, + const ecs_table_record_t *tr, + int32_t count, + int32_t row, + const ecs_entity_t *entities, + ecs_id_t id, + const ecs_type_info_t *ti, + ecs_entity_t event, + ecs_iter_action_t hook); - /* Create component array for creating the table */ - ecs_type_t components = { - .array = ecs_os_alloca_n(ecs_entity_t, type_count + 1) - }; +//////////////////////////////////////////////////////////////////////////////// +//// Query API +//////////////////////////////////////////////////////////////////////////////// - void **component_data = ecs_os_alloca_n(void*, type_count + 1); +void flecs_query_apply_iter_flags( + ecs_iter_t *it, + const ecs_query_t *query); - /* Copy in component identifiers. Find the base index in the component - * array, since we'll need this to replace the base with the instance id */ - int j, i, childof_base_index = -1, pos = 0; - for (i = 0; i < type_count; i ++) { - ecs_id_t id = ids[i]; +//////////////////////////////////////////////////////////////////////////////// +//// Safe(r) integer casting +//////////////////////////////////////////////////////////////////////////////// - /* If id has DontInherit flag don't inherit it, except for the name - * and ChildOf pairs. The name is preserved so applications can lookup - * the instantiated children by name. The ChildOf pair is replaced later - * with the instance parent. */ - if ((id != ecs_pair(ecs_id(EcsIdentifier), EcsName)) && - ECS_PAIR_FIRST(id) != EcsChildOf) - { - if (id == EcsUnion) { - /* This should eventually be handled by the DontInherit property - * but right now there is no way to selectively apply it to - * EcsUnion itself: it would also apply to (Union, *) pairs, - * which would make all union relationships uninheritable. - * - * The reason this is explicitly skipped is so that slot - * instances don't all end up with the Union property. */ - continue; - } - ecs_table_record_t *tr = &child_table->_->records[i]; - ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; - if (idr->flags & EcsIdDontInherit) { - continue; - } - } +#define FLECS_CONVERSION_ERR(T, value)\ + "illegal conversion from value " #value " to type " #T - /* If child is a slot, keep track of which parent to add it to, but - * don't add slot relationship to child of instance. If this is a child - * of a prefab, keep the SlotOf relationship intact. */ - if (!(table->flags & EcsTableIsPrefab)) { - if (ECS_IS_PAIR(id) && ECS_PAIR_FIRST(id) == EcsSlotOf) { - ecs_assert(slot_of == 0, ECS_INTERNAL_ERROR, NULL); - slot_of = ecs_pair_second(world, id); - continue; - } - } +#define flecs_signed_char__ (CHAR_MIN < 0) +#define flecs_signed_short__ true +#define flecs_signed_int__ true +#define flecs_signed_long__ true +#define flecs_signed_size_t__ false +#define flecs_signed_int8_t__ true +#define flecs_signed_int16_t__ true +#define flecs_signed_int32_t__ true +#define flecs_signed_int64_t__ true +#define flecs_signed_intptr_t__ true +#define flecs_signed_uint8_t__ false +#define flecs_signed_uint16_t__ false +#define flecs_signed_uint32_t__ false +#define flecs_signed_uint64_t__ false +#define flecs_signed_uintptr_t__ false +#define flecs_signed_ecs_size_t__ true +#define flecs_signed_ecs_entity_t__ false - /* Keep track of the element that creates the ChildOf relationship with - * the prefab parent. We need to replace this element to make sure the - * created children point to the instance and not the prefab */ - if (ECS_HAS_RELATION(id, EcsChildOf) && - (ECS_PAIR_SECOND(id) == (uint32_t)base)) { - childof_base_index = pos; - } +uint64_t flecs_ito_( + size_t dst_size, + bool dst_signed, + bool lt_zero, + uint64_t value, + const char *err); - int32_t storage_index = ecs_table_type_to_column_index(child_table, i); - if (storage_index != -1) { - ecs_vec_t *column = &child_data->columns[storage_index].data; - component_data[pos] = ecs_vec_first(column); - } else { - component_data[pos] = NULL; - } +#ifndef FLECS_NDEBUG +#define flecs_ito(T, value)\ + (T)flecs_ito_(\ + sizeof(T),\ + flecs_signed_##T##__,\ + (value) < 0,\ + (uint64_t)(value),\ + FLECS_CONVERSION_ERR(T, (value))) - components.array[pos] = id; - pos ++; - } +#define flecs_uto(T, value)\ + (T)flecs_ito_(\ + sizeof(T),\ + flecs_signed_##T##__,\ + false,\ + (uint64_t)(value),\ + FLECS_CONVERSION_ERR(T, (value))) +#else +#define flecs_ito(T, value) (T)(value) +#define flecs_uto(T, value) (T)(value) +#endif - /* Table must contain children of base */ - ecs_assert(childof_base_index != -1, ECS_INTERNAL_ERROR, NULL); +#define flecs_itosize(value) flecs_ito(size_t, (value)) +#define flecs_utosize(value) flecs_uto(ecs_size_t, (value)) +#define flecs_itoi16(value) flecs_ito(int16_t, (value)) +#define flecs_itoi32(value) flecs_ito(int32_t, (value)) - /* If children are added to a prefab, make sure they are prefabs too */ - if (table->flags & EcsTableIsPrefab) { - components.array[pos] = EcsPrefab; - component_data[pos] = NULL; - pos ++; - } +//////////////////////////////////////////////////////////////////////////////// +//// Utilities +//////////////////////////////////////////////////////////////////////////////// - components.count = pos; +uint64_t flecs_hash( + const void *data, + ecs_size_t length); - /* Instantiate the prefab child table for each new instance */ - ecs_entity_t *instances = ecs_vec_first(&table->data.entities); - int32_t child_count = ecs_vec_count(&child_data->entities); - bool has_union = child_table->flags & EcsTableHasUnion; +uint64_t flecs_wyhash( + const void *data, + ecs_size_t length); - for (i = row; i < count + row; i ++) { - ecs_entity_t instance = instances[i]; - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); - ecs_table_t *i_table = NULL; - - /* Replace ChildOf element in the component array with instance id */ - components.array[childof_base_index] = ecs_pair(EcsChildOf, instance); +/* Get next power of 2 */ +int32_t flecs_next_pow_of_2( + int32_t n); - /* Find or create table */ - for (j = 0; j < components.count; j ++) { - i_table = flecs_find_table_add( - world, i_table, components.array[j], &diff); - } +/* Convert 64bit value to ecs_record_t type. ecs_record_t is stored as 64bit int in the + * entity index */ +ecs_record_t flecs_to_row( + uint64_t value); - ecs_assert(i_table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(i_table->type.count == components.count, - ECS_INTERNAL_ERROR, NULL); +/* Get 64bit integer from ecs_record_t */ +uint64_t flecs_from_row( + ecs_record_t record); - /* The instance is trying to instantiate from a base that is also - * its parent. This would cause the hierarchy to instantiate itself - * which would cause infinite recursion. */ - ecs_entity_t *children = ecs_vec_first(&child_data->entities); +/* Convert a symbol name to an entity name by removing the prefix */ +const char* flecs_name_from_symbol( + ecs_world_t *world, + const char *type_name); -#ifdef FLECS_DEBUG - for (j = 0; j < child_count; j ++) { - ecs_entity_t child = children[j]; - ecs_check(child != instance, ECS_INVALID_PARAMETER, NULL); - } -#else - /* Bit of boilerplate to ensure that we don't get warnings about the - * error label not being used. */ - ecs_check(true, ECS_INVALID_OPERATION, NULL); -#endif +/* Compare function for entity ids used for order_by */ +int flecs_entity_compare( + ecs_entity_t e1, + const void *ptr1, + ecs_entity_t e2, + const void *ptr2); - /* Create children */ - int32_t child_row; - ecs_table_diff_t table_diff; - flecs_table_diff_build_noalloc(&diff, &table_diff); - const ecs_entity_t *i_children = flecs_bulk_new(world, i_table, NULL, - &components, child_count, component_data, false, &child_row, - &table_diff); - flecs_table_diff_builder_fini(world, &diff); +/* Compare function for component ids used for qsort */ +int flecs_id_qsort_cmp( + const void *a, + const void *b); - /* If children have union relationships, initialize */ - if (has_union) { - ecs_table__t *meta = child_table->_; - ecs_assert(meta != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(i_table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t u, u_count = meta->sw_count; - for (u = 0; u < u_count; u ++) { - ecs_switch_t *src_sw = &meta->sw_columns[i]; - ecs_switch_t *dst_sw = &i_table->_->sw_columns[i]; - ecs_vec_t *v_src_values = flecs_switch_values(src_sw); - ecs_vec_t *v_dst_values = flecs_switch_values(dst_sw); - uint64_t *src_values = ecs_vec_first(v_src_values); - uint64_t *dst_values = ecs_vec_first(v_dst_values); - for (j = 0; j < child_count; j ++) { - dst_values[j] = src_values[j]; - } - } - } +/* Load file contents into string */ +char* flecs_load_from_file( + const char *filename); - /* If children are slots, add slot relationships to parent */ - if (slot_of) { - for (j = 0; j < child_count; j ++) { - ecs_entity_t child = children[j]; - ecs_entity_t i_child = i_children[j]; - flecs_instantiate_slot(world, base, instance, slot_of, - child, i_child); - } - } +bool flecs_name_is_id( + const char *name); - /* If prefab child table has children itself, recursively instantiate */ - for (j = 0; j < child_count; j ++) { - ecs_entity_t child = children[j]; - flecs_instantiate(world, child, i_table, child_row + j, 1); - } - } -error: - return; -} +ecs_entity_t flecs_name_to_id( + const char *name); -void flecs_instantiate( - ecs_world_t *world, - ecs_entity_t base, - ecs_table_t *table, - int32_t row, - int32_t count) -{ - ecs_record_t *record = flecs_entities_get_any(world, base); - ecs_table_t *base_table = record->table; - if (!base_table || !(base_table->flags & EcsTableIsPrefab)) { - /* Don't instantiate children from base entities that aren't prefabs */ - return; - } +/* Convert floating point to string */ +char * ecs_ftoa( + double f, + char * buf, + int precision); - ecs_id_record_t *idr = flecs_id_record_get(world, ecs_childof(base)); - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_all_iter((ecs_table_cache_t*)idr, &it)) { - const ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - flecs_instantiate_children( - world, base, table, row, count, tr->hdr.table); - } - } -} +uint64_t flecs_string_hash( + const void *ptr); -static -void flecs_set_union( +void flecs_table_hashmap_init( ecs_world_t *world, - ecs_table_t *table, - int32_t row, - int32_t count, - const ecs_type_t *ids) -{ - ecs_id_t *array = ids->array; - int32_t i, id_count = ids->count; + ecs_hashmap_t *hm); - for (i = 0; i < id_count; i ++) { - ecs_id_t id = array[i]; +void flecs_colorize_buf( + char *msg, + bool enable_colors, + ecs_strbuf_t *buf); - if (ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_pair(EcsUnion, ECS_PAIR_FIRST(id))); - if (!idr) { - continue; - } +int32_t flecs_search_w_idr( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t *id_out, + ecs_id_record_t *idr); - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t column = tr->index - table->_->sw_offset; - ecs_switch_t *sw = &table->_->sw_columns[column]; - ecs_entity_t union_case = 0; - union_case = ecs_pair_second(world, id); +int32_t flecs_search_relation_w_idr( + const ecs_world_t *world, + const ecs_table_t *table, + int32_t offset, + ecs_id_t id, + ecs_entity_t rel, + ecs_flags64_t flags, + ecs_entity_t *subject_out, + ecs_id_t *id_out, + struct ecs_table_record_t **tr_out, + ecs_id_record_t *idr); - int32_t r; - for (r = 0; r < count; r ++) { - flecs_switch_set(sw, row + r, union_case); - } - } - } -} +bool flecs_type_can_inherit_id( + const ecs_world_t *world, + const ecs_table_t *table, + const ecs_id_record_t *idr, + ecs_id_t id); -static -void flecs_notify_on_add( - ecs_world_t *world, - ecs_table_t *table, - ecs_table_t *other_table, - int32_t row, - int32_t count, - const ecs_type_t *added, - ecs_flags32_t flags) -{ - ecs_assert(added != NULL, ECS_INTERNAL_ERROR, NULL); +int ecs_term_finalize( + const ecs_world_t *world, + ecs_term_t *term); - if (added->count) { - ecs_flags32_t table_flags = table->flags; +int32_t flecs_query_pivot_term( + const ecs_world_t *world, + const ecs_query_t *query); - if (table_flags & EcsTableHasUnion) { - flecs_set_union(world, table, row, count, added); - } +#endif - if (table_flags & (EcsTableHasOnAdd|EcsTableHasIsA|EcsTableHasTraversable)) { - flecs_emit(world, world, &(ecs_event_desc_t){ - .event = EcsOnAdd, - .ids = added, - .table = table, - .other_table = other_table, - .offset = row, - .count = count, - .observable = world, - .flags = flags - }); - } - } -} -void flecs_notify_on_remove( - ecs_world_t *world, - ecs_table_t *table, - ecs_table_t *other_table, - int32_t row, - int32_t count, - const ecs_type_t *removed) -{ - ecs_assert(removed != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(count != 0, ECS_INTERNAL_ERROR, NULL); +/* -- Identifier Component -- */ +static ECS_DTOR(EcsIdentifier, ptr, { + ecs_os_strset(&ptr->value, NULL); +}) - if (removed->count && (table->flags & - (EcsTableHasOnRemove|EcsTableHasUnSet|EcsTableHasIsA|EcsTableHasTraversable))) - { - flecs_emit(world, world, &(ecs_event_desc_t) { - .event = EcsOnRemove, - .ids = removed, - .table = table, - .other_table = other_table, - .offset = row, - .count = count, - .observable = world - }); - } -} +static ECS_COPY(EcsIdentifier, dst, src, { + ecs_os_strset(&dst->value, src->value); + dst->hash = src->hash; + dst->length = src->length; + dst->index_hash = src->index_hash; + dst->index = src->index; +}) + +static ECS_MOVE(EcsIdentifier, dst, src, { + ecs_os_strset(&dst->value, NULL); + dst->value = src->value; + dst->hash = src->hash; + dst->length = src->length; + dst->index_hash = src->index_hash; + dst->index = src->index; + + src->value = NULL; + src->hash = 0; + src->index_hash = 0; + src->index = 0; + src->length = 0; +}) static -void flecs_update_name_index( - ecs_world_t *world, - ecs_table_t *src, - ecs_table_t *dst, - int32_t offset, - int32_t count) -{ - ecs_assert(src != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(dst != NULL, ECS_INTERNAL_ERROR, NULL); - if (!(dst->flags & EcsTableHasName)) { - /* If destination table doesn't have a name, we don't need to update the - * name index. Even if the src table had a name, the on_remove hook for - * EcsIdentifier will remove the entity from the index. */ - return; - } +void ecs_on_set(EcsIdentifier)(ecs_iter_t *it) { + EcsIdentifier *ptr = ecs_field(it, EcsIdentifier, 0); + + ecs_world_t *world = it->real_world; + ecs_entity_t evt = it->event; + ecs_id_t evt_id = it->event_id; + ecs_entity_t kind = ECS_PAIR_SECOND(evt_id); /* Name, Symbol, Alias */ + ecs_id_t pair = ecs_childof(0); + ecs_hashmap_t *index = NULL; - ecs_hashmap_t *src_index = src->_->name_index; - ecs_hashmap_t *dst_index = dst->_->name_index; - if ((src_index == dst_index) || (!src_index && !dst_index)) { - /* If the name index didn't change, the entity still has the same parent - * so nothing needs to be done. */ - return; + if (kind == EcsSymbol) { + index = &world->symbols; + } else if (kind == EcsAlias) { + index = &world->aliases; + } else if (kind == EcsName) { + ecs_assert(it->table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_search(world, it->table, ecs_childof(EcsWildcard), &pair); + ecs_assert(pair != 0, ECS_INTERNAL_ERROR, NULL); + + if (evt == EcsOnSet) { + index = flecs_id_name_index_ensure(world, pair); + } else { + index = flecs_id_name_index_get(world, pair); + } } - EcsIdentifier *names = ecs_table_get_pair(world, - dst, EcsIdentifier, EcsName, offset); - ecs_assert(names != NULL, ECS_INTERNAL_ERROR, NULL); + int i, count = it->count; - int32_t i; - ecs_entity_t *entities = ecs_vec_get_t( - &dst->data.entities, ecs_entity_t, offset); for (i = 0; i < count; i ++) { - ecs_entity_t e = entities[i]; - EcsIdentifier *name = &names[i]; + EcsIdentifier *cur = &ptr[i]; + uint64_t hash; + ecs_size_t len; + const char *name = cur->value; - uint64_t index_hash = name->index_hash; - if (index_hash) { - flecs_name_index_remove(src_index, e, index_hash); + if (cur->index && cur->index != index) { + /* If index doesn't match up, the value must have been copied from + * another entity, so reset index & cached index hash */ + cur->index = NULL; + cur->index_hash = 0; } - const char *name_str = name->value; - if (name_str) { - ecs_assert(name->hash != 0, ECS_INTERNAL_ERROR, NULL); - flecs_name_index_ensure( - dst_index, e, name_str, name->length, name->hash); - name->index = dst_index; + if (cur->value && (evt == EcsOnSet)) { + len = cur->length = ecs_os_strlen(name); + hash = cur->hash = flecs_hash(name, len); + } else { + len = cur->length = 0; + hash = cur->hash = 0; + cur->index = NULL; + } + + if (index) { + uint64_t index_hash = cur->index_hash; + ecs_entity_t e = it->entities[i]; + + if (hash != index_hash) { + if (index_hash) { + flecs_name_index_remove(index, e, index_hash); + } + if (hash) { + flecs_name_index_ensure(index, e, name, len, hash); + cur->index_hash = hash; + cur->index = index; + } + } else { + /* Name didn't change, but the string could have been + * reallocated. Make sure name index points to correct string */ + flecs_name_index_update_name(index, e, hash, name); + } } } } -static -ecs_record_t* flecs_new_entity( - ecs_world_t *world, - ecs_entity_t entity, - ecs_record_t *record, - ecs_table_t *table, - ecs_table_diff_t *diff, - bool ctor, - ecs_flags32_t evt_flags) -{ - ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t row = flecs_table_append(world, table, entity, ctor, true); - record->table = table; - record->row = ECS_ROW_TO_RECORD(row, record->row & ECS_ROW_FLAGS_MASK); - ecs_assert(ecs_vec_count(&table->data.entities) > row, - ECS_INTERNAL_ERROR, NULL); - flecs_notify_on_add(world, table, NULL, row, 1, &diff->added, evt_flags); +/* -- Poly component -- */ - return record; -} +static ECS_COPY(EcsPoly, dst, src, { + (void)dst; + (void)src; + ecs_abort(ECS_INVALID_OPERATION, "poly component cannot be copied"); +}) + +static ECS_MOVE(EcsPoly, dst, src, { + if (dst->poly && (dst->poly != src->poly)) { + flecs_poly_dtor_t *dtor = ecs_get_dtor(dst->poly); + ecs_assert(dtor != NULL, ECS_INTERNAL_ERROR, NULL); + dtor[0](dst->poly); + } + + dst->poly = src->poly; + src->poly = NULL; +}) + +static ECS_DTOR(EcsPoly, ptr, { + if (ptr->poly) { + flecs_poly_dtor_t *dtor = ecs_get_dtor(ptr->poly); + ecs_assert(dtor != NULL, ECS_INTERNAL_ERROR, NULL); + dtor[0](ptr->poly); + } +}) + + +/* -- Builtin triggers -- */ static -void flecs_move_entity( - ecs_world_t *world, - ecs_entity_t entity, - ecs_record_t *record, - ecs_table_t *dst_table, - ecs_table_diff_t *diff, - bool ctor, - ecs_flags32_t evt_flags) +void flecs_assert_relation_unused( + ecs_world_t *world, + ecs_entity_t rel, + ecs_entity_t property) { - ecs_table_t *src_table = record->table; - int32_t src_row = ECS_RECORD_TO_ROW(record->row); - - ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(src_table != dst_table, ECS_INTERNAL_ERROR, NULL); - ecs_assert(src_table->type.count > 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(src_row >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ecs_vec_count(&src_table->data.entities) > src_row, - ECS_INTERNAL_ERROR, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(record == flecs_entities_get(world, entity), - ECS_INTERNAL_ERROR, NULL); + if (world->flags & (EcsWorldInit|EcsWorldFini)) { + return; + } - /* Append new row to destination table */ - int32_t dst_row = flecs_table_append(world, dst_table, entity, - false, false); + ecs_vec_t *marked_ids = &world->store.marked_ids; + int32_t i, count = ecs_vec_count(marked_ids); + for (i = 0; i < count; i ++) { + ecs_marked_id_t *mid = ecs_vec_get_t(marked_ids, ecs_marked_id_t, i); + if (mid->id == ecs_pair(rel, EcsWildcard)) { + /* If id is being cleaned up, no need to throw error as tables will + * be cleaned up */ + return; + } + } - /* Invoke remove actions for removed components */ - flecs_notify_on_remove( - world, src_table, dst_table, src_row, 1, &diff->removed); + bool in_use = ecs_id_in_use(world, ecs_pair(rel, EcsWildcard)); - /* Copy entity & components from src_table to dst_table */ - flecs_table_move(world, entity, entity, dst_table, dst_row, - src_table, src_row, ctor); + /* Hack to make enum unions work. C++ enum reflection registers enum + * constants right after creating the enum entity. The enum constant + * entities have a component of the enum type with the constant value, which + * is why it shows up as in use. */ + if (property != EcsUnion) { + in_use |= ecs_id_in_use(world, rel); + } - /* Update entity index & delete old data after running remove actions */ - record->table = dst_table; - record->row = ECS_ROW_TO_RECORD(dst_row, record->row & ECS_ROW_FLAGS_MASK); - - flecs_table_delete(world, src_table, src_row, false); - flecs_notify_on_add( - world, dst_table, src_table, dst_row, 1, &diff->added, evt_flags); + if (in_use) { + char *r_str = ecs_get_path(world, rel); + char *p_str = ecs_get_path(world, property); - flecs_update_name_index(world, src_table, dst_table, dst_row, 1); + ecs_throw(ECS_ID_IN_USE, + "cannot change property '%s' for relationship '%s': already in use", + p_str, r_str); + + ecs_os_free(r_str); + ecs_os_free(p_str); + } error: return; } static -void flecs_delete_entity( +bool flecs_set_id_flag( ecs_world_t *world, - ecs_record_t *record, - ecs_table_diff_t *diff) -{ - ecs_table_t *table = record->table; - int32_t row = ECS_RECORD_TO_ROW(record->row); - - /* Invoke remove actions before deleting */ - flecs_notify_on_remove(world, table, NULL, row, 1, &diff->removed); - flecs_table_delete(world, table, row, true); + ecs_id_record_t *idr, + ecs_flags32_t flag) +{ + if (!(idr->flags & flag)) { + idr->flags |= flag; + if (flag == EcsIdIsSparse) { + flecs_id_record_init_sparse(world, idr); + } + return true; + } + return false; } -/* Updating component monitors is a relatively expensive operation that only - * happens for entities that are monitored. The approach balances the amount of - * processing between the operation on the entity vs the amount of work that - * needs to be done to rematch queries, as a simple brute force approach does - * not scale when there are many tables / queries. Therefore we need to do a bit - * of bookkeeping that is more intelligent than simply flipping a flag */ static -void flecs_update_component_monitor_w_array( - ecs_world_t *world, - ecs_type_t *ids) +bool flecs_unset_id_flag( + ecs_id_record_t *idr, + ecs_flags32_t flag) { - if (!ids) { - return; + if ((idr->flags & flag)) { + idr->flags &= ~flag; + return true; } + return false; +} - int i; - for (i = 0; i < ids->count; i ++) { - ecs_entity_t id = ids->array[i]; - if (ECS_HAS_ID_FLAG(id, PAIR)) { - flecs_monitor_mark_dirty(world, - ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard)); +static +void flecs_register_id_flag_for_relation( + ecs_iter_t *it, + ecs_entity_t prop, + ecs_flags32_t flag, + ecs_flags32_t not_flag, + ecs_flags32_t entity_flag) +{ + ecs_world_t *world = it->world; + ecs_entity_t event = it->event; + + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + bool changed = false; + + if (event == EcsOnAdd) { + ecs_id_record_t *idr; + if (!ecs_has_id(world, e, EcsRelationship) && + !ecs_has_id(world, e, EcsTarget)) + { + idr = flecs_id_record_ensure(world, e); + changed |= flecs_set_id_flag(world, idr, flag); + } + + idr = flecs_id_record_ensure(world, ecs_pair(e, EcsWildcard)); + do { + changed |= flecs_set_id_flag(world, idr, flag); + } while ((idr = idr->first.next)); + if (entity_flag) flecs_add_flag(world, e, entity_flag); + } else if (event == EcsOnRemove) { + ecs_id_record_t *idr = flecs_id_record_get(world, e); + if (idr) changed |= flecs_unset_id_flag(idr, not_flag); + idr = flecs_id_record_get(world, ecs_pair(e, EcsWildcard)); + if (idr) { + do { + changed |= flecs_unset_id_flag(idr, not_flag); + } while ((idr = idr->first.next)); + } } - flecs_monitor_mark_dirty(world, id); + if (changed) { + flecs_assert_relation_unused(world, e, prop); + } } } static -void flecs_update_component_monitors( - ecs_world_t *world, - ecs_type_t *added, - ecs_type_t *removed) -{ - flecs_update_component_monitor_w_array(world, added); - flecs_update_component_monitor_w_array(world, removed); +void flecs_register_final(ecs_iter_t *it) { + ecs_world_t *world = it->world; + + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + if (flecs_id_record_get(world, ecs_pair(EcsIsA, e)) != NULL) { + char *e_str = ecs_get_path(world, e); + ecs_throw(ECS_ID_IN_USE, + "cannot change property 'Final' for '%s': already inherited from", + e_str); + ecs_os_free(e_str); + error: + continue; + } + } } static -void flecs_commit( - ecs_world_t *world, - ecs_entity_t entity, - ecs_record_t *record, - ecs_table_t *dst_table, - ecs_table_diff_t *diff, - bool construct, - ecs_flags32_t evt_flags) -{ - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); - flecs_journal_begin(world, EcsJournalMove, entity, - &diff->added, &diff->removed); - - ecs_table_t *src_table = NULL; - int is_trav = 0; - if (record) { - src_table = record->table; - is_trav = (record->row & EcsEntityIsTraversable) != 0; - } +void flecs_register_tag(ecs_iter_t *it) { + flecs_register_id_flag_for_relation(it, EcsPairIsTag, EcsIdTag, EcsIdTag, 0); - if (src_table == dst_table) { - /* If source and destination table are the same no action is needed * - * However, if a component was added in the process of traversing a - * table, this suggests that a union relationship could have changed. */ - if (src_table) { - flecs_notify_on_add(world, src_table, src_table, - ECS_RECORD_TO_ROW(record->row), 1, &diff->added, evt_flags); + /* Ensure that all id records for tag have type info set to NULL */ + ecs_world_t *world = it->real_world; + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + + if (it->event == EcsOnAdd) { + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_pair(e, EcsWildcard)); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + do { + if (idr->type_info != NULL) { + flecs_assert_relation_unused(world, e, EcsPairIsTag); + } + idr->type_info = NULL; + } while ((idr = idr->first.next)); } - flecs_journal_end(); - return; } +} - if (src_table) { - ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_table_traversable_add(dst_table, is_trav); +static +void flecs_register_on_delete(ecs_iter_t *it) { + ecs_id_t id = ecs_field_id(it, 0); + flecs_register_id_flag_for_relation(it, EcsOnDelete, + ECS_ID_ON_DELETE_FLAG(ECS_PAIR_SECOND(id)), + EcsIdOnDeleteMask, + EcsEntityIsId); +} - if (dst_table->type.count) { - flecs_move_entity(world, entity, record, dst_table, diff, - construct, evt_flags); - } else { - flecs_delete_entity(world, record, diff); - record->table = NULL; - } +static +void flecs_register_on_delete_object(ecs_iter_t *it) { + ecs_id_t id = ecs_field_id(it, 0); + flecs_register_id_flag_for_relation(it, EcsOnDeleteTarget, + ECS_ID_ON_DELETE_TARGET_FLAG(ECS_PAIR_SECOND(id)), + EcsIdOnDeleteObjectMask, + EcsEntityIsId); +} - flecs_table_traversable_add(src_table, -is_trav); - } else { - flecs_table_traversable_add(dst_table, is_trav); - if (dst_table->type.count) { - flecs_new_entity(world, entity, record, dst_table, diff, - construct, evt_flags); - } - } +static +void flecs_register_on_instantiate(ecs_iter_t *it) { + ecs_id_t id = ecs_field_id(it, 0); + flecs_register_id_flag_for_relation(it, EcsOnInstantiate, + ECS_ID_ON_INSTANTIATE_FLAG(ECS_PAIR_SECOND(id)), + 0, 0); +} - /* If the entity is being watched, it is being monitored for changes and - * requires rematching systems when components are added or removed. This - * ensures that systems that rely on components from containers or prefabs - * update the matched tables when the application adds or removes a - * component from, for example, a container. */ - if (is_trav) { - flecs_update_component_monitors(world, &diff->added, &diff->removed); - } +typedef struct ecs_on_trait_ctx_t { + ecs_flags32_t flag, not_flag; +} ecs_on_trait_ctx_t; - if ((!src_table || !src_table->type.count) && world->range_check_enabled) { - ecs_check(!world->info.max_id || entity <= world->info.max_id, - ECS_OUT_OF_RANGE, 0); - ecs_check(entity >= world->info.min_id, - ECS_OUT_OF_RANGE, 0); - } +static +void flecs_register_trait(ecs_iter_t *it) { + ecs_on_trait_ctx_t *ctx = it->ctx; + flecs_register_id_flag_for_relation( + it, it->ids[0], ctx->flag, ctx->not_flag, 0); +} -error: - flecs_journal_end(); - return; +static +void flecs_register_trait_pair(ecs_iter_t *it) { + ecs_on_trait_ctx_t *ctx = it->ctx; + flecs_register_id_flag_for_relation( + it, ecs_pair_first(it->world, it->ids[0]), ctx->flag, ctx->not_flag, 0); } static -const ecs_entity_t* flecs_bulk_new( - ecs_world_t *world, - ecs_table_t *table, - const ecs_entity_t *entities, - ecs_type_t *component_ids, - int32_t count, - void **component_data, - bool is_move, - int32_t *row_out, - ecs_table_diff_t *diff) -{ - int32_t sparse_count = 0; - if (!entities) { - sparse_count = flecs_entities_count(world); - entities = flecs_entities_new_ids(world, count); +void flecs_register_slot_of(ecs_iter_t *it) { + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_add_id(it->world, it->entities[i], EcsUnion); } +} - if (!table) { - return entities; - } +static +void flecs_on_symmetric_add_remove(ecs_iter_t *it) { + ecs_entity_t pair = ecs_field_id(it, 0); - ecs_type_t type = table->type; - if (!type.count) { - return entities; + if (!ECS_HAS_ID_FLAG(pair, PAIR)) { + /* If relationship was not added as a pair, there's nothing to do */ + return; } - ecs_type_t component_array = { 0 }; - if (!component_ids) { - component_ids = &component_array; - component_array.array = type.array; - component_array.count = type.count; + ecs_world_t *world = it->world; + ecs_entity_t rel = ECS_PAIR_FIRST(pair); + ecs_entity_t obj = ecs_pair_second(world, pair); + ecs_entity_t event = it->event; + + + if (obj) { + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t subj = it->entities[i]; + if (event == EcsOnAdd) { + if (!ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { + ecs_add_pair(it->world, obj, rel, subj); + } + } else { + if (ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { + ecs_remove_pair(it->world, obj, rel, subj); + } + } + } } +} - ecs_data_t *data = &table->data; - int32_t row = flecs_table_appendn(world, table, data, count, entities); +static +void flecs_register_symmetric(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; - /* Update entity index. */ - int i; + int i, count = it->count; for (i = 0; i < count; i ++) { - ecs_record_t *r = flecs_entities_get(world, entities[i]); - r->table = table; - r->row = ECS_ROW_TO_RECORD(row + i, 0); - } + ecs_entity_t r = it->entities[i]; + flecs_assert_relation_unused(world, r, EcsSymmetric); - flecs_defer_begin(world, &world->stages[0]); - flecs_notify_on_add(world, table, NULL, row, count, &diff->added, - (component_data == NULL) ? 0 : EcsEventNoOnSet); + /* 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(world, { + .entity = ecs_entity(world, { .parent = r }), + .query.terms[0] = { .id = ecs_pair(r, EcsWildcard) }, + .callback = flecs_on_symmetric_add_remove, + .events = {EcsOnAdd, EcsOnRemove} + }); + } +} - if (component_data) { - int32_t c_i; - for (c_i = 0; c_i < component_ids->count; c_i ++) { - void *src_ptr = component_data[c_i]; - if (!src_ptr) { - continue; - } +static +void flecs_on_component(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsComponent *c = ecs_field(it, EcsComponent, 0); - /* Find component in storage type */ - ecs_entity_t id = component_ids->array[c_i]; - const ecs_table_record_t *tr = flecs_table_record_get( - world, table, id); - ecs_assert(tr != NULL, ECS_INVALID_PARAMETER, - "id is not a component"); - ecs_assert(tr->column != -1, ECS_INVALID_PARAMETER, - "id is not a component"); - ecs_assert(tr->count == 1, ECS_INVALID_PARAMETER, - "ids cannot be wildcards"); + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; - int32_t index = tr->column; - ecs_column_t *column = &table->data.columns[index]; - ecs_type_info_t *ti = column->ti; - int32_t size = column->size; - ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); - void *ptr = ecs_vec_get(&column->data, size, row); + uint32_t component_id = (uint32_t)e; /* Strip generation */ + ecs_assert(component_id < ECS_MAX_COMPONENT_ID, ECS_OUT_OF_RANGE, + "component id must be smaller than %u", ECS_MAX_COMPONENT_ID); + (void)component_id; - ecs_copy_t copy; - ecs_move_t move; - if (is_move && (move = ti->hooks.move)) { - move(ptr, src_ptr, count, ti); - } else if (!is_move && (copy = ti->hooks.copy)) { - copy(ptr, src_ptr, count, ti); - } else { - ecs_os_memcpy(ptr, src_ptr, size * count); - } - }; + if (it->event != EcsOnRemove) { + ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); + if (parent) { + ecs_add_id(world, parent, EcsModule); + } + } - int32_t j, storage_count = table->column_count; - for (j = 0; j < storage_count; j ++) { - ecs_type_t set_type = { - .array = &table->data.columns[j].id, - .count = 1 - }; - flecs_notify_on_set(world, table, row, count, &set_type, true); + if (it->event == EcsOnSet) { + if (flecs_type_info_init_id( + world, e, c[i].size, c[i].alignment, NULL)) + { + flecs_assert_relation_unused(world, e, ecs_id(EcsComponent)); + } + } else if (it->event == EcsOnRemove) { + #ifdef FLECS_DEBUG + if (ecs_should_log(0)) { + char *path = ecs_get_path(world, e); + ecs_trace("unregistering component '%s'", path); + ecs_os_free(path); + } + #endif + if (!ecs_vec_count(&world->store.marked_ids)) { + flecs_type_info_free(world, e); + } else { + ecs_vec_append_t(&world->allocator, + &world->store.deleted_components, ecs_entity_t)[0] = e; + } } } +} - flecs_defer_end(world, &world->stages[0]); - - if (row_out) { - *row_out = row; - } +static +void flecs_ensure_module_tag(ecs_iter_t *it) { + ecs_world_t *world = it->world; - if (sparse_count) { - entities = flecs_entities_ids(world); - return &entities[sparse_count]; - } else { - return entities; + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); + if (parent) { + ecs_add_id(world, parent, EcsModule); + } } } static -void flecs_add_id_w_record( - ecs_world_t *world, - ecs_entity_t entity, - ecs_record_t *record, - ecs_id_t id, - bool construct) +void flecs_disable_observer( + ecs_iter_t *it) { - ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_world_t *world = it->world; + ecs_entity_t evt = it->event; - ecs_table_t *src_table = record->table; - ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; - ecs_table_t *dst_table = flecs_table_traverse_add( - world, src_table, &id, &diff); - flecs_commit(world, entity, record, dst_table, &diff, construct, - EcsEventNoOnSet); /* No OnSet, this function is only called from - * functions that are about to set the component. */ + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + flecs_observer_set_disable_bit(world, it->entities[i], + EcsObserverIsDisabled, evt == EcsOnAdd); + } } static -void flecs_add_id( - ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) +void flecs_disable_module_observers( + ecs_world_t *world, + ecs_entity_t module, + bool should_disable) { - ecs_stage_t *stage = flecs_stage_from_world(&world); - if (flecs_defer_add(stage, entity, id)) { - return; - } + ecs_iter_t child_it = ecs_children(world, module); + while (ecs_children_next(&child_it)) { + ecs_table_t *table = child_it.table; + bool table_disabled = table->flags & EcsTableIsDisabled; + int32_t i; - ecs_record_t *r = flecs_entities_get(world, entity); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; - ecs_table_t *src_table = r->table; - ecs_table_t *dst_table = flecs_table_traverse_add( - world, src_table, &id, &diff); + /* Recursively walk modules, don't propagate to disabled modules */ + if (ecs_table_has_id(world, table, EcsModule) && !table_disabled) { + for (i = 0; i < child_it.count; i ++) { + flecs_disable_module_observers( + world, child_it.entities[i], should_disable); + } + continue; + } - flecs_commit(world, entity, r, dst_table, &diff, true, 0); + /* Only disable observers */ + if (!ecs_table_has_id(world, table, EcsObserver)) { + continue; + } - flecs_defer_end(world, stage); + for (i = 0; i < child_it.count; i ++) { + flecs_observer_set_disable_bit(world, child_it.entities[i], + EcsObserverIsParentDisabled, should_disable); + } + } } static -void flecs_remove_id( - ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) -{ - ecs_stage_t *stage = flecs_stage_from_world(&world); - if (flecs_defer_remove(stage, entity, id)) { - return; +void flecs_disable_module(ecs_iter_t *it) { + int32_t i; + for (i = 0; i < it->count; i ++) { + flecs_disable_module_observers( + it->real_world, it->entities[i], it->event == EcsOnAdd); } +} - ecs_record_t *r = flecs_entities_get(world, entity); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_t *src_table = r->table; - ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; - ecs_table_t *dst_table = flecs_table_traverse_remove( - world, src_table, &id, &diff); - - flecs_commit(world, entity, r, dst_table, &diff, true, 0); +/* -- Bootstrapping -- */ - flecs_defer_end(world, stage); -} +#define flecs_bootstrap_builtin_t(world, table, name)\ + flecs_bootstrap_builtin(world, table, ecs_id(name), #name, sizeof(name),\ + ECS_ALIGNOF(name)) static -flecs_component_ptr_t flecs_get_mut( +void flecs_bootstrap_builtin( ecs_world_t *world, + ecs_table_t *table, ecs_entity_t entity, - ecs_entity_t id, - ecs_record_t *r) + const char *symbol, + ecs_size_t size, + ecs_size_t alignment) { - flecs_component_ptr_t dst = {0}; - - ecs_poly_assert(world, ecs_world_t); - ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(r != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check((id & ECS_COMPONENT_MASK) == id || - ECS_HAS_ID_FLAG(id, PAIR), ECS_INVALID_PARAMETER, NULL); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - if (r->table) { - dst = flecs_get_component_ptr( - world, r->table, ECS_RECORD_TO_ROW(r->row), id); - if (dst.ptr) { - return dst; - } - } + ecs_column_t *columns = table->data.columns; + ecs_assert(columns != NULL, ECS_INTERNAL_ERROR, NULL); - /* If entity didn't have component yet, add it */ - flecs_add_id_w_record(world, entity, r, id, true); + ecs_record_t *record = flecs_entities_ensure(world, entity); + record->table = table; - /* Flush commands so the pointer we're fetching is stable */ - flecs_defer_end(world, &world->stages[0]); - flecs_defer_begin(world, &world->stages[0]); + int32_t index = flecs_table_append(world, table, entity, false, false); + record->row = ECS_ROW_TO_RECORD(index, 0); - ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(r->table->column_count != 0, ECS_INTERNAL_ERROR, NULL); - dst = flecs_get_component_ptr( - world, r->table, ECS_RECORD_TO_ROW(r->row), id); -error: - return dst; -} + EcsComponent *component = columns[0].data; + component[index].size = size; + component[index].alignment = alignment; -void flecs_invoke_hook( - ecs_world_t *world, - ecs_table_t *table, - int32_t count, - int32_t row, - 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) -{ - int32_t defer = world->stages[0].defer; - if (defer < 0) { - world->stages[0].defer *= -1; - } + const char *name = &symbol[3]; /* Strip 'Ecs' */ + ecs_size_t symbol_length = ecs_os_strlen(symbol); + ecs_size_t name_length = symbol_length - 3; - 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 = ECS_CONST_CAST(ecs_size_t*, &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; - it.offset = row; - flecs_iter_validate(&it); - hook(&it); - ecs_iter_fini(&it); + EcsIdentifier *name_col = columns[1].data; + uint64_t name_hash = flecs_hash(name, name_length); + name_col[index].value = ecs_os_strdup(name); + name_col[index].length = name_length; + name_col[index].hash = name_hash; + name_col[index].index_hash = 0; + name_col[index].index = table->_->name_index; + flecs_name_index_ensure( + table->_->name_index, entity, name, name_length, name_hash); - world->stages[0].defer = defer; + EcsIdentifier *symbol_col = columns[2].data; + symbol_col[index].value = ecs_os_strdup(symbol); + symbol_col[index].length = symbol_length; + symbol_col[index].hash = flecs_hash(symbol, symbol_length); + symbol_col[index].index_hash = 0; + symbol_col[index].index = NULL; } -void flecs_notify_on_set( - ecs_world_t *world, - ecs_table_t *table, - int32_t row, - int32_t count, - ecs_type_t *ids, - bool owned) +/** Initialize component table. This table is manually constructed to bootstrap + * flecs. After this function has been called, the builtin components can be + * created. + * The reason this table is constructed manually is because it requires the size + * and alignment of the EcsComponent and EcsIdentifier components, which haven't + * been created yet */ +static +ecs_table_t* flecs_bootstrap_component_table( + ecs_world_t *world) { - ecs_assert(ids != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_data_t *data = &table->data; - - ecs_entity_t *entities = ecs_vec_get_t( - &data->entities, ecs_entity_t, row); - ecs_assert(entities != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert((row + count) <= ecs_vec_count(&data->entities), - ECS_INTERNAL_ERROR, NULL); - - if (owned) { - int i; - for (i = 0; i < ids->count; i ++) { - ecs_id_t id = ids->array[i]; - const ecs_table_record_t *tr = flecs_table_record_get(world, - table, id); - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(tr->column != -1, ECS_INTERNAL_ERROR, NULL); - ecs_assert(tr->count == 1, ECS_INTERNAL_ERROR, NULL); - - int32_t index = tr->column; - ecs_column_t *column = &table->data.columns[index]; - const ecs_type_info_t *ti = column->ti; - ecs_iter_action_t on_set = ti->hooks.on_set; - if (on_set) { - ecs_vec_t *c = &column->data; - void *ptr = ecs_vec_get(c, column->size, row); - flecs_invoke_hook(world, table, count, row, entities, ptr, id, - ti, EcsOnSet, on_set); - } - } - } - - /* Run OnSet notifications */ - if (table->flags & EcsTableHasOnSet && ids->count) { - flecs_emit(world, world, &(ecs_event_desc_t) { - .event = EcsOnSet, - .ids = ids, - .table = table, - .offset = row, - .count = count, - .observable = world - }); - } -} + /* Before creating table, manually set flags for ChildOf/Identifier, as this + * can no longer be done after they are in use. */ + ecs_id_record_t *idr = flecs_id_record_ensure(world, EcsChildOf); + idr->flags |= EcsIdOnDeleteObjectDelete | EcsIdOnInstantiateDontInherit | + EcsIdTraversable | EcsIdTag; -void flecs_record_add_flag( - ecs_record_t *record, - uint32_t flag) -{ - if (flag == EcsEntityIsTraversable) { - if (!(record->row & flag)) { - ecs_table_t *table = record->table; - if (table) { - flecs_table_traversable_add(table, 1); - } - } - } - record->row |= flag; -} + /* Initialize id records cached on world */ + world->idr_childof_wildcard = flecs_id_record_ensure(world, + ecs_pair(EcsChildOf, EcsWildcard)); + world->idr_childof_wildcard->flags |= EcsIdOnDeleteObjectDelete | + EcsIdOnInstantiateDontInherit | EcsIdTraversable | EcsIdTag | EcsIdExclusive; + idr = flecs_id_record_ensure(world, ecs_pair_t(EcsIdentifier, EcsWildcard)); + idr->flags |= EcsIdOnInstantiateDontInherit; + world->idr_identifier_name = + flecs_id_record_ensure(world, ecs_pair_t(EcsIdentifier, EcsName)); + world->idr_childof_0 = flecs_id_record_ensure(world, + ecs_pair(EcsChildOf, 0)); -void flecs_add_flag( - ecs_world_t *world, - ecs_entity_t entity, - uint32_t flag) -{ - ecs_record_t *record = flecs_entities_get_any(world, entity); - ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_record_add_flag(record, flag); -} + ecs_id_t ids[] = { + ecs_id(EcsComponent), + EcsFinal, + ecs_pair_t(EcsIdentifier, EcsName), + ecs_pair_t(EcsIdentifier, EcsSymbol), + ecs_pair(EcsChildOf, EcsFlecsCore), + ecs_pair(EcsOnDelete, EcsPanic) + }; -/* -- Public functions -- */ + ecs_type_t array = { + .array = ids, + .count = 6 + }; -bool ecs_commit( - ecs_world_t *world, - ecs_entity_t entity, - ecs_record_t *record, - ecs_table_t *table, - const ecs_type_t *added, - const ecs_type_t *removed) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!ecs_is_deferred(world), ECS_INVALID_OPERATION, NULL); + ecs_table_t *result = flecs_table_find_or_create(world, &array); - ecs_table_t *src_table = NULL; - if (!record) { - record = flecs_entities_get(world, entity); - src_table = record->table; + /* Preallocate enough memory for initial components */ + ecs_allocator_t *a = &world->allocator; + ecs_vec_t v_entities = ecs_vec_from_entities(result); + ecs_vec_init_t(a, &v_entities, ecs_entity_t, EcsFirstUserComponentId); + + { + ecs_column_t *column = &result->data.columns[0]; + ecs_vec_t v = ecs_vec_from_column_t(column, result, EcsComponent); + ecs_vec_init_t(a, &v, EcsComponent, EcsFirstUserComponentId); + ecs_assert(v.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + column->data = v.array; } - - ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; - - if (added) { - diff.added = *added; + { + ecs_column_t *column = &result->data.columns[1]; + ecs_vec_t v = ecs_vec_from_column_t(column, result, EcsIdentifier); + ecs_vec_init_t(a, &v, EcsIdentifier, EcsFirstUserComponentId); + ecs_assert(v.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + column->data = v.array; } - if (removed) { - diff.removed = *removed; + { + ecs_column_t *column = &result->data.columns[2]; + ecs_vec_t v = ecs_vec_from_column_t(column, result, EcsIdentifier); + ecs_vec_init_t(a, &v, EcsIdentifier, EcsFirstUserComponentId); + ecs_assert(v.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + column->data = v.array; } - ecs_defer_begin(world); - flecs_commit(world, entity, record, table, &diff, true, 0); - ecs_defer_end(world); - - return src_table != table; -error: - return false; + result->data.entities = v_entities.array; + result->data.count = 0; + result->data.size = v_entities.size; + + return result; } -ecs_entity_t ecs_set_with( +static +void flecs_bootstrap_entity( ecs_world_t *world, - ecs_id_t id) + ecs_entity_t id, + const char *name, + ecs_entity_t parent) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_id_t prev = stage->with; - stage->with = id; - return prev; -error: - return 0; -} + char symbol[256]; + ecs_os_strcpy(symbol, "flecs.core."); + ecs_os_strcat(symbol, name); + + ecs_make_alive(world, id); + ecs_add_pair(world, id, EcsChildOf, parent); + ecs_set_name(world, id, name); + ecs_set_symbol(world, id, symbol); -ecs_id_t ecs_get_with( - const ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); - return stage->with; -error: - return 0; + ecs_assert(ecs_get_name(world, id) != NULL, ECS_INTERNAL_ERROR, NULL); + + if (!parent || parent == EcsFlecsCore) { + ecs_assert(ecs_lookup(world, name) == id, + ECS_INTERNAL_ERROR, NULL); + } } -ecs_entity_t ecs_new_id( +void flecs_bootstrap( ecs_world_t *world) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - - const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); + ecs_log_push(); - /* It is possible that the world passed to this function is a stage, so - * make sure we have the actual world. Cast away const since this is one of - * the few functions that may modify the world while it is in readonly mode, - * since it is thread safe (uses atomic inc when in threading mode) */ - ecs_world_t *unsafe_world = - ECS_CONST_CAST(ecs_world_t*, ecs_get_world(world)); + ecs_set_name_prefix(world, "Ecs"); - ecs_entity_t entity; - if (stage->async || (unsafe_world->flags & EcsWorldMultiThreaded)) { - /* When using an async stage or world is in multithreading mode, make - * sure OS API has threading functions initialized */ - ecs_assert(ecs_os_has_threading(), ECS_INVALID_OPERATION, NULL); + /* Ensure builtin ids are alive */ + ecs_make_alive(world, ecs_id(EcsComponent)); + ecs_make_alive(world, EcsFinal); + ecs_make_alive(world, ecs_id(EcsIdentifier)); + ecs_make_alive(world, EcsName); + ecs_make_alive(world, EcsSymbol); + ecs_make_alive(world, EcsAlias); + ecs_make_alive(world, EcsChildOf); + ecs_make_alive(world, EcsFlecs); + ecs_make_alive(world, EcsFlecsCore); + ecs_make_alive(world, EcsOnAdd); + ecs_make_alive(world, EcsOnRemove); + ecs_make_alive(world, EcsOnSet); + ecs_make_alive(world, EcsOnDelete); + ecs_make_alive(world, EcsPanic); + ecs_make_alive(world, EcsFlag); + ecs_make_alive(world, EcsIsA); + ecs_make_alive(world, EcsWildcard); + ecs_make_alive(world, EcsAny); + ecs_make_alive(world, EcsPairIsTag); + ecs_make_alive(world, EcsCanToggle); + ecs_make_alive(world, EcsTrait); + ecs_make_alive(world, EcsRelationship); + ecs_make_alive(world, EcsTarget); + ecs_make_alive(world, EcsSparse); + ecs_make_alive(world, EcsUnion); - /* Can't atomically increase number above max int */ - ecs_assert(flecs_entities_max_id(unsafe_world) < UINT_MAX, - ECS_INVALID_OPERATION, NULL); - entity = (ecs_entity_t)ecs_os_ainc( - (int32_t*)&flecs_entities_max_id(unsafe_world)); - } else { - entity = flecs_entities_new_id(unsafe_world); - } + /* Register type information for builtin components */ + flecs_type_info_init(world, EcsComponent, { + .ctor = flecs_default_ctor, + .on_set = flecs_on_component, + .on_remove = flecs_on_component + }); - ecs_assert(!unsafe_world->info.max_id || - ecs_entity_t_lo(entity) <= unsafe_world->info.max_id, - ECS_OUT_OF_RANGE, NULL); + flecs_type_info_init(world, EcsIdentifier, { + .ctor = flecs_default_ctor, + .dtor = ecs_dtor(EcsIdentifier), + .copy = ecs_copy(EcsIdentifier), + .move = ecs_move(EcsIdentifier), + .on_set = ecs_on_set(EcsIdentifier), + .on_remove = ecs_on_set(EcsIdentifier) + }); - flecs_journal(world, EcsJournalNew, entity, 0, 0); + flecs_type_info_init(world, EcsPoly, { + .ctor = flecs_default_ctor, + .copy = ecs_copy(EcsPoly), + .move = ecs_move(EcsPoly), + .dtor = ecs_dtor(EcsPoly) + }); - return entity; -error: - return 0; -} + flecs_type_info_init(world, EcsDefaultChildComponent, { + .ctor = flecs_default_ctor, + }); -ecs_entity_t ecs_new_low_id( - ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + /* Create and cache often used id records on world */ + flecs_init_id_records(world); - /* It is possible that the world passed to this function is a stage, so - * make sure we have the actual world. Cast away const since this is one of - * the few functions that may modify the world while it is in readonly mode, - * but only if single threaded. */ - ecs_world_t *unsafe_world = - ECS_CONST_CAST(ecs_world_t*, ecs_get_world(world)); - if (unsafe_world->flags & EcsWorldReadonly) { - /* Can't issue new comp id while iterating when in multithreaded mode */ - ecs_check(ecs_get_stage_count(world) <= 1, - ECS_INVALID_WHILE_READONLY, NULL); - } + /* Create table for builtin components. This table temporarily stores the + * entities associated with builtin components, until they get moved to + * other tables once properties are added (see below) */ + ecs_table_t *table = flecs_bootstrap_component_table(world); + assert(table != NULL); - ecs_entity_t id = 0; - if (unsafe_world->info.last_component_id < FLECS_HI_COMPONENT_ID) { - do { - id = unsafe_world->info.last_component_id ++; - } while (ecs_exists(unsafe_world, id) && id <= FLECS_HI_COMPONENT_ID); - } + /* Bootstrap builtin components */ + flecs_bootstrap_builtin_t(world, table, EcsIdentifier); + flecs_bootstrap_builtin_t(world, table, EcsComponent); + flecs_bootstrap_builtin_t(world, table, EcsPoly); + flecs_bootstrap_builtin_t(world, table, EcsDefaultChildComponent); - if (!id || id >= FLECS_HI_COMPONENT_ID) { - /* If the low component ids are depleted, return a regular entity id */ - id = ecs_new_id(unsafe_world); - } else { - flecs_entities_ensure(world, id); - } + /* Initialize default entity id range */ + world->info.last_component_id = EcsFirstUserComponentId; + flecs_entities_max_id(world) = EcsFirstUserEntityId; + world->info.min_id = 0; + world->info.max_id = 0; - ecs_assert(ecs_get_type(world, id) == NULL, ECS_INTERNAL_ERROR, NULL); + /* Register observer for tag property before adding EcsPairIsTag */ + ecs_observer(world, { + .entity = ecs_entity(world, { .parent = EcsFlecsInternals }), + .query.terms[0] = { .id = EcsPairIsTag }, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_register_tag, + .yield_existing = true + }); - return id; -error: - return 0; -} + /* Populate core module */ + ecs_set_scope(world, EcsFlecsCore); -ecs_entity_t ecs_new_w_id( - ecs_world_t *world, - ecs_id_t id) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!id || ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + flecs_bootstrap_tag(world, EcsName); + flecs_bootstrap_tag(world, EcsSymbol); + flecs_bootstrap_tag(world, EcsAlias); - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_entity_t entity = ecs_new_id(world); + flecs_bootstrap_tag(world, EcsQuery); + flecs_bootstrap_tag(world, EcsObserver); - ecs_id_t ids[3]; - ecs_type_t to_add = { .array = ids, .count = 0 }; + flecs_bootstrap_tag(world, EcsModule); + flecs_bootstrap_tag(world, EcsPrivate); + flecs_bootstrap_tag(world, EcsPrefab); + flecs_bootstrap_tag(world, EcsSlotOf); + flecs_bootstrap_tag(world, EcsDisabled); + flecs_bootstrap_tag(world, EcsNotQueryable); + flecs_bootstrap_tag(world, EcsEmpty); - if (id) { - ids[to_add.count ++] = id; - } + /* Initialize builtin modules */ + ecs_set_name(world, EcsFlecs, "flecs"); + ecs_add_id(world, EcsFlecs, EcsModule); + ecs_add_pair(world, EcsFlecs, EcsOnDelete, EcsPanic); - ecs_id_t with = stage->with; - if (with) { - ids[to_add.count ++] = with; - } + ecs_add_pair(world, EcsFlecsCore, EcsChildOf, EcsFlecs); + ecs_set_name(world, EcsFlecsCore, "core"); + ecs_add_id(world, EcsFlecsCore, EcsModule); - ecs_entity_t scope = stage->scope; - if (scope) { - if (!id || !ECS_HAS_RELATION(id, EcsChildOf)) { - ids[to_add.count ++] = ecs_pair(EcsChildOf, scope); - } - } - if (to_add.count) { - if (flecs_defer_add(stage, entity, to_add.array[0])) { - int i; - for (i = 1; i < to_add.count; i ++) { - flecs_defer_add(stage, entity, to_add.array[i]); - } - return entity; - } + ecs_add_pair(world, EcsFlecsInternals, EcsChildOf, EcsFlecsCore); + ecs_set_name(world, EcsFlecsInternals, "internals"); + ecs_add_id(world, EcsFlecsInternals, EcsModule); - int32_t i, count = to_add.count; - ecs_table_t *table = &world->store.root; - - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); - for (i = 0; i < count; i ++) { - table = flecs_find_table_add( - world, table, to_add.array[i], &diff); - } + /* Self check */ + ecs_record_t *r = flecs_entities_get(world, EcsFlecs); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->row & EcsEntityIsTraversable, ECS_INTERNAL_ERROR, NULL); + (void)r; - ecs_table_diff_t table_diff; - flecs_table_diff_build_noalloc(&diff, &table_diff); - ecs_record_t *r = flecs_entities_get(world, entity); - flecs_new_entity(world, entity, r, table, &table_diff, true, true); - flecs_table_diff_builder_fini(world, &diff); - } else { - if (flecs_defer_cmd(stage)) { - return entity; - } + /* Initialize builtin entities */ + flecs_bootstrap_entity(world, EcsWorld, "World", EcsFlecsCore); + flecs_bootstrap_entity(world, EcsWildcard, "*", EcsFlecsCore); + flecs_bootstrap_entity(world, EcsAny, "_", EcsFlecsCore); + flecs_bootstrap_entity(world, EcsThis, "this", EcsFlecsCore); + flecs_bootstrap_entity(world, EcsVariable, "$", EcsFlecsCore); + flecs_bootstrap_entity(world, EcsFlag, "Flag", EcsFlecsCore); - flecs_entities_ensure(world, entity); - } - flecs_defer_end(world, stage); + /* Component/relationship properties */ + flecs_bootstrap_trait(world, EcsTransitive); + flecs_bootstrap_trait(world, EcsReflexive); + flecs_bootstrap_trait(world, EcsSymmetric); + flecs_bootstrap_trait(world, EcsFinal); + flecs_bootstrap_trait(world, EcsPairIsTag); + flecs_bootstrap_trait(world, EcsExclusive); + flecs_bootstrap_trait(world, EcsAcyclic); + flecs_bootstrap_trait(world, EcsTraversable); + flecs_bootstrap_trait(world, EcsWith); + flecs_bootstrap_trait(world, EcsOneOf); + flecs_bootstrap_trait(world, EcsCanToggle); + flecs_bootstrap_trait(world, EcsTrait); + flecs_bootstrap_trait(world, EcsRelationship); + flecs_bootstrap_trait(world, EcsTarget); + flecs_bootstrap_trait(world, EcsOnDelete); + flecs_bootstrap_trait(world, EcsOnDeleteTarget); + flecs_bootstrap_trait(world, EcsOnInstantiate); + flecs_bootstrap_trait(world, EcsSparse); + flecs_bootstrap_trait(world, EcsUnion); - return entity; -error: - return 0; -} + flecs_bootstrap_tag(world, EcsRemove); + flecs_bootstrap_tag(world, EcsDelete); + flecs_bootstrap_tag(world, EcsPanic); -ecs_entity_t ecs_new_w_table( - ecs_world_t *world, - ecs_table_t *table) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_bootstrap_tag(world, EcsOverride); + flecs_bootstrap_tag(world, EcsInherit); + flecs_bootstrap_tag(world, EcsDontInherit); - flecs_stage_from_world(&world); - ecs_entity_t entity = ecs_new_id(world); - ecs_record_t *r = flecs_entities_get(world, entity); + /* Builtin predicates */ + flecs_bootstrap_tag(world, EcsPredEq); + flecs_bootstrap_tag(world, EcsPredMatch); + flecs_bootstrap_tag(world, EcsPredLookup); + flecs_bootstrap_tag(world, EcsScopeOpen); + flecs_bootstrap_tag(world, EcsScopeClose); - ecs_table_diff_t table_diff = { .added = table->type }; - flecs_new_entity(world, entity, r, table, &table_diff, true, true); - return entity; -error: - return 0; -} + /* Builtin relationships */ + flecs_bootstrap_tag(world, EcsIsA); + flecs_bootstrap_tag(world, EcsChildOf); + flecs_bootstrap_tag(world, EcsDependsOn); -#ifdef FLECS_PARSER + /* Builtin events */ + flecs_bootstrap_entity(world, EcsOnAdd, "OnAdd", EcsFlecsCore); + flecs_bootstrap_entity(world, EcsOnRemove, "OnRemove", EcsFlecsCore); + flecs_bootstrap_entity(world, EcsOnSet, "OnSet", 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); -/* Traverse table graph by either adding or removing identifiers parsed from the - * passed in expression. */ -static -ecs_table_t *flecs_traverse_from_expr( - ecs_world_t *world, - ecs_table_t *table, - const char *name, - const char *expr, - ecs_table_diff_builder_t *diff, - bool replace_and, - bool *error) -{ - const char *ptr = expr; - if (ptr) { - ecs_term_t term = {0}; - while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term, NULL))){ - if (!ecs_term_is_initialized(&term)) { - break; - } + /* Unqueryable entities */ + ecs_add_id(world, EcsThis, EcsNotQueryable); + ecs_add_id(world, EcsWildcard, EcsNotQueryable); + ecs_add_id(world, EcsAny, EcsNotQueryable); + ecs_add_id(world, EcsVariable, EcsNotQueryable); - if (!(term.first.flags & (EcsSelf|EcsUp))) { - term.first.flags = EcsSelf; - } - if (!(term.second.flags & (EcsSelf|EcsUp))) { - term.second.flags = EcsSelf; - } - if (!(term.src.flags & (EcsSelf|EcsUp))) { - term.src.flags = EcsSelf; - } + /* Tag relationships (relationships that should never have data) */ + ecs_add_id(world, EcsIsA, EcsPairIsTag); + ecs_add_id(world, EcsChildOf, EcsPairIsTag); + ecs_add_id(world, EcsSlotOf, EcsPairIsTag); + ecs_add_id(world, EcsDependsOn, EcsPairIsTag); + ecs_add_id(world, EcsFlag, EcsPairIsTag); + ecs_add_id(world, EcsWith, EcsPairIsTag); - if (ecs_term_finalize(world, &term)) { - ecs_term_fini(&term); - if (error) { - *error = true; - } - return NULL; - } + /* Exclusive properties */ + ecs_add_id(world, EcsChildOf, EcsExclusive); + ecs_add_id(world, EcsOnDelete, EcsExclusive); + ecs_add_id(world, EcsOnDeleteTarget, EcsExclusive); + ecs_add_id(world, EcsOnInstantiate, EcsExclusive); + + /* Relationships */ + ecs_add_id(world, EcsChildOf, EcsRelationship); + ecs_add_id(world, EcsIsA, EcsRelationship); + ecs_add_id(world, EcsSlotOf, EcsRelationship); + ecs_add_id(world, EcsDependsOn, EcsRelationship); + ecs_add_id(world, EcsWith, EcsRelationship); + ecs_add_id(world, EcsOnDelete, EcsRelationship); + ecs_add_id(world, EcsOnDeleteTarget, EcsRelationship); + ecs_add_id(world, EcsOnInstantiate, EcsRelationship); + ecs_add_id(world, ecs_id(EcsIdentifier), EcsRelationship); + + /* Targets */ + ecs_add_id(world, EcsOverride, EcsTarget); + ecs_add_id(world, EcsInherit, EcsTarget); + ecs_add_id(world, EcsDontInherit, EcsTarget); - if (!ecs_id_is_valid(world, term.id)) { - ecs_term_fini(&term); - ecs_parser_error(name, expr, (ptr - expr), - "invalid term for add expression"); - return NULL; - } + /* Sync properties of ChildOf and Identifier with bootstrapped flags */ + ecs_add_pair(world, EcsChildOf, EcsOnDeleteTarget, EcsDelete); + ecs_add_id(world, EcsChildOf, EcsTrait); + ecs_add_id(world, EcsChildOf, EcsAcyclic); + ecs_add_id(world, EcsChildOf, EcsTraversable); + ecs_add_pair(world, EcsChildOf, EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, ecs_id(EcsIdentifier), EcsOnInstantiate, EcsDontInherit); - if (term.oper == EcsAnd || !replace_and) { - /* Regular AND expression */ - table = flecs_find_table_add(world, table, term.id, diff); - } + /* Create triggers in internals scope */ + ecs_set_scope(world, EcsFlecsInternals); - ecs_term_fini(&term); - } + /* Register observers for components/relationship properties. Most observers + * set flags on an id record when a property is added to a component, which + * allows for quick property testing in various operations. */ + ecs_observer(world, { + .query.terms = {{ .id = EcsFinal }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_final + }); - if (!ptr) { - if (error) { - *error = true; - } - return NULL; - } - } + ecs_observer(world, { + .query.terms = { + { .id = ecs_pair(EcsOnDelete, EcsWildcard) } + }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_register_on_delete + }); - return table; -} + ecs_observer(world, { + .query.terms = { + { .id = ecs_pair(EcsOnDeleteTarget, EcsWildcard) } + }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_register_on_delete_object + }); -/* Add/remove components based on the parsed expression. This operation is - * slower than flecs_traverse_from_expr, but safe to use from a deferred context. */ -static -void flecs_defer_from_expr( - ecs_world_t *world, - ecs_entity_t entity, - const char *name, - const char *expr, - bool is_add, - bool replace_and) -{ - const char *ptr = expr; - if (ptr) { - ecs_term_t term = {0}; - while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term, NULL))) { - if (!ecs_term_is_initialized(&term)) { - break; - } + ecs_observer(world, { + .query.terms = { + { .id = ecs_pair(EcsOnInstantiate, EcsWildcard) } + }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_on_instantiate + }); - if (ecs_term_finalize(world, &term)) { - return; - } + ecs_observer(world, { + .query.terms = {{ .id = EcsSymmetric }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_symmetric + }); - if (!ecs_id_is_valid(world, term.id)) { - ecs_term_fini(&term); - ecs_parser_error(name, expr, (ptr - expr), - "invalid term for add expression"); - return; - } + static ecs_on_trait_ctx_t traversable_trait = { EcsIdTraversable, EcsIdTraversable }; + ecs_observer(world, { + .query.terms = {{ .id = EcsTraversable }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_register_trait, + .ctx = &traversable_trait + }); - if (term.oper == EcsAnd || !replace_and) { - /* Regular AND expression */ - if (is_add) { - ecs_add_id(world, entity, term.id); - } else { - ecs_remove_id(world, entity, term.id); - } - } + static ecs_on_trait_ctx_t exclusive_trait = { EcsIdExclusive, EcsIdExclusive }; + ecs_observer(world, { + .query.terms = {{ .id = EcsExclusive }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_register_trait, + .ctx = &exclusive_trait + }); - ecs_term_fini(&term); - } - } -} -#endif + static ecs_on_trait_ctx_t toggle_trait = { EcsIdCanToggle, 0 }; + ecs_observer(world, { + .query.terms = {{ .id = EcsCanToggle }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_trait, + .ctx = &toggle_trait + }); -/* If operation is not deferred, add components by finding the target - * table and moving the entity towards it. */ -static -int flecs_traverse_add( - ecs_world_t *world, - ecs_entity_t result, - const char *name, - const ecs_entity_desc_t *desc, - ecs_entity_t scope, - ecs_id_t with, - bool flecs_new_entity, - bool name_assigned) -{ - const char *sep = desc->sep; - const char *root_sep = desc->root_sep; - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); + static ecs_on_trait_ctx_t with_trait = { EcsIdWith, 0 }; + ecs_observer(world, { + .query.terms = { + { .id = ecs_pair(EcsWith, EcsWildcard) }, + }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_trait_pair, + .ctx = &with_trait + }); - /* Find existing table */ - ecs_table_t *src_table = NULL, *table = NULL; - ecs_record_t *r = flecs_entities_get(world, result); - table = r->table; + static ecs_on_trait_ctx_t sparse_trait = { EcsIdIsSparse, 0 }; + ecs_observer(world, { + .query.terms = {{ .id = EcsSparse }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_trait, + .ctx = &sparse_trait + }); - /* If a name is provided but not yet assigned, add the Name component */ - if (name && !name_assigned) { - table = flecs_find_table_add(world, table, - ecs_pair(ecs_id(EcsIdentifier), EcsName), &diff); - } + static ecs_on_trait_ctx_t union_trait = { EcsIdIsUnion, 0 }; + ecs_observer(world, { + .query.terms = {{ .id = EcsUnion }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_trait, + .ctx = &union_trait + }); - /* Add components from the 'add' id array */ - int32_t i = 0; - ecs_id_t id; - const ecs_id_t *ids = desc->add; - while ((i < FLECS_ID_DESC_MAX) && (id = ids[i ++])) { - bool should_add = true; - if (ECS_HAS_ID_FLAG(id, PAIR) && ECS_PAIR_FIRST(id) == EcsChildOf) { - scope = ECS_PAIR_SECOND(id); - if ((!desc->id && desc->name) || (name && !name_assigned)) { - /* If name is added to entity, pass scope to add_path instead - * of adding it to the table. The provided name may have nested - * elements, in which case the parent provided here is not the - * parent the entity will end up with. */ - should_add = false; - } - } - if (should_add) { - table = flecs_find_table_add(world, table, id, &diff); - } - } + /* Entities used as slot are marked as exclusive to ensure a slot can always + * only point to a single entity. */ + ecs_observer(world, { + .query.terms = { + { .id = ecs_pair(EcsSlotOf, EcsWildcard) } + }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_slot_of + }); - /* Find destination table */ - /* If this is a new entity without a name, add the scope. If a name is - * provided, the scope will be added by the add_path_w_sep function */ - if (flecs_new_entity) { - if (flecs_new_entity && scope && !name && !name_assigned) { - table = flecs_find_table_add( - world, table, ecs_pair(EcsChildOf, scope), &diff); - } - if (with) { - table = flecs_find_table_add(world, table, with, &diff); - } - } + /* Define observer to make sure that adding a module to a child entity also + * adds it to the parent. */ + ecs_observer(world, { + .query.terms = {{ .id = EcsModule } }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_ensure_module_tag + }); - /* Add components from the 'add_expr' expression */ - if (desc->add_expr && ecs_os_strcmp(desc->add_expr, "0")) { -#ifdef FLECS_PARSER - bool error = false; - table = flecs_traverse_from_expr( - world, table, name, desc->add_expr, &diff, true, &error); - if (error) { - flecs_table_diff_builder_fini(world, &diff); - return -1; - } -#else - ecs_abort(ECS_UNSUPPORTED, "parser addon is not available"); -#endif - } + /* Observer that tracks whether observers are disabled */ + ecs_observer(world, { + .query.terms = { + { .id = EcsObserver }, + { .id = EcsDisabled }, + }, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_disable_observer + }); - /* Commit entity to destination table */ - if (src_table != table) { - flecs_defer_begin(world, &world->stages[0]); - ecs_table_diff_t table_diff; - flecs_table_diff_build_noalloc(&diff, &table_diff); - flecs_commit(world, result, r, table, &table_diff, true, 0); - flecs_table_diff_builder_fini(world, &diff); - flecs_defer_end(world, &world->stages[0]); - } + /* Observer that tracks whether modules are disabled */ + ecs_observer(world, { + .query.terms = { + { .id = EcsModule }, + { .id = EcsDisabled }, + }, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_disable_module + }); - /* Set name */ - if (name && !name_assigned) { - ecs_add_path_w_sep(world, result, scope, name, sep, root_sep); - ecs_assert(ecs_get_name(world, result) != NULL, - ECS_INTERNAL_ERROR, NULL); - } + /* Set scope back to flecs core */ + ecs_set_scope(world, EcsFlecsCore); - if (desc->symbol && desc->symbol[0]) { - const char *sym = ecs_get_symbol(world, result); - if (sym) { - ecs_assert(!ecs_os_strcmp(desc->symbol, sym), - ECS_INCONSISTENT_NAME, desc->symbol); - } else { - ecs_set_symbol(world, result, desc->symbol); - } - } + /* Traversable relationships are always acyclic */ + ecs_add_pair(world, EcsTraversable, EcsWith, EcsAcyclic); - flecs_table_diff_builder_fini(world, &diff); - return 0; + /* Transitive relationships are always Traversable */ + ecs_add_pair(world, EcsTransitive, EcsWith, EcsTraversable); + + /* DontInherit components */ + ecs_add_pair(world, EcsPrefab, EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, ecs_id(EcsComponent), EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, EcsOnDelete, EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, EcsUnion, EcsOnInstantiate, EcsDontInherit); + + /* Acyclic/Traversable components */ + ecs_add_id(world, EcsIsA, EcsTraversable); + ecs_add_id(world, EcsDependsOn, EcsTraversable); + ecs_add_id(world, EcsWith, EcsAcyclic); + + /* Transitive relationships */ + ecs_add_id(world, EcsIsA, EcsTransitive); + ecs_add_id(world, EcsIsA, EcsReflexive); + + /* Exclusive properties */ + ecs_add_id(world, EcsSlotOf, EcsExclusive); + ecs_add_id(world, EcsOneOf, EcsExclusive); + + /* Private properties */ + ecs_add_id(world, ecs_id(EcsPoly), EcsPrivate); + ecs_add_id(world, ecs_id(EcsIdentifier), EcsPrivate); + + /* Inherited components */ + ecs_add_pair(world, EcsIsA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, EcsDependsOn, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, EcsDisabled, EcsOnInstantiate, EcsInherit); + + /* Run bootstrap functions for other parts of the code */ + flecs_bootstrap_hierarchy(world); + + ecs_set_scope(world, 0); + ecs_set_name_prefix(world, NULL); + + ecs_assert(world->idr_childof_wildcard != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(world->idr_isa_wildcard != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_log_pop(); } -/* When in deferred mode, we need to add/remove components one by one using - * the regular operations. */ -static -void flecs_deferred_add_remove( - ecs_world_t *world, - ecs_entity_t entity, - const char *name, - const ecs_entity_desc_t *desc, - ecs_entity_t scope, - ecs_id_t with, - bool flecs_new_entity, - bool name_assigned) +/** + * @file query/each.c + * @brief Simple iterator for a single component id. + */ + + +ecs_iter_t ecs_each_id( + const ecs_world_t *stage, + ecs_id_t id) { - const char *sep = desc->sep; - const char *root_sep = desc->root_sep; + ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); - /* If this is a new entity without a name, add the scope. If a name is - * provided, the scope will be added by the add_path_w_sep function */ - if (flecs_new_entity) { - if (flecs_new_entity && scope && !name && !name_assigned) { - ecs_add_id(world, entity, ecs_pair(EcsChildOf, scope)); - } + const ecs_world_t *world = ecs_get_world(stage); - if (with) { - ecs_add_id(world, entity, with); - } - } + flecs_process_pending_tables(world); - /* Add components from the 'add' id array */ - int32_t i = 0; - ecs_id_t id; - const ecs_id_t *ids = desc->add; - while ((i < FLECS_ID_DESC_MAX) && (id = ids[i ++])) { - bool defer = true; - if (ECS_HAS_ID_FLAG(id, PAIR) && ECS_PAIR_FIRST(id) == EcsChildOf) { - scope = ECS_PAIR_SECOND(id); - if (name && (!desc->id || !name_assigned)) { - /* New named entities are created by temporarily going out of - * readonly mode to ensure no duplicates are created. */ - defer = false; - } - } - if (defer) { - ecs_add_id(world, entity, id); - } + ecs_iter_t it = { + .real_world = ECS_CONST_CAST(ecs_world_t*, world), + .world = ECS_CONST_CAST(ecs_world_t*, stage), + .field_count = 1, + .next = ecs_each_next + }; + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return it; } - /* Add components from the 'add_expr' expression */ - if (desc->add_expr) { -#ifdef FLECS_PARSER - flecs_defer_from_expr(world, entity, name, desc->add_expr, true, true); -#else - ecs_abort(ECS_UNSUPPORTED, "parser addon is not available"); -#endif + ecs_each_iter_t *each_iter = &it.priv_.iter.each; + each_iter->ids = id; + each_iter->sizes = 0; + if (idr->type_info) { + each_iter->sizes = idr->type_info->size; } - int32_t thread_count = ecs_get_stage_count(world); + each_iter->sources = 0; + each_iter->trs = NULL; + flecs_table_cache_iter((ecs_table_cache_t*)idr, &each_iter->it); - /* Set name */ - if (name && !name_assigned) { - ecs_add_path_w_sep(world, entity, scope, name, sep, root_sep); - } + return it; +error: + return (ecs_iter_t){0}; +} - /* Set symbol */ - if (desc->symbol) { - const char *sym = ecs_get_symbol(world, entity); - if (!sym || ecs_os_strcmp(sym, desc->symbol)) { - if (thread_count <= 1) { /* See above */ - ecs_suspend_readonly_state_t state; - ecs_world_t *real_world = flecs_suspend_readonly(world, &state); - ecs_set_symbol(world, entity, desc->symbol); - flecs_resume_readonly(real_world, &state); - } else { - ecs_set_symbol(world, entity, desc->symbol); - } - } +bool ecs_each_next( + ecs_iter_t *it) +{ + ecs_each_iter_t *each_iter = &it->priv_.iter.each; + const ecs_table_record_t *next = flecs_table_cache_next( + &each_iter->it, ecs_table_record_t); + it->flags |= EcsIterIsValid; + if (next) { + each_iter->trs = next; + ecs_table_t *table = next->hdr.table; + it->table = table; + it->count = ecs_table_count(table); + it->entities = ecs_table_entities(table); + it->ids = &table->type.array[next->index]; + it->trs = &each_iter->trs; + it->sources = &each_iter->sources; + it->sizes = &each_iter->sizes; + it->set_fields = 1; + + return true; + } else { + return false; } } -ecs_entity_t ecs_entity_init( - ecs_world_t *world, - const ecs_entity_desc_t *desc) +ecs_iter_t ecs_children( + const ecs_world_t *stage, + ecs_entity_t parent) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); + return ecs_each_id(stage, ecs_childof(parent)); +} - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_entity_t scope = stage->scope; - ecs_id_t with = ecs_get_with(world); - ecs_entity_t result = desc->id; +bool ecs_children_next( + ecs_iter_t *it) +{ + return ecs_each_next(it); +} - const char *name = desc->name; - const char *sep = desc->sep; - if (!sep) { - sep = "."; - } +/** + * @file entity.c + * @brief Entity API. + * + * This file contains the implementation for the entity API, which includes + * creating/deleting entities, adding/removing/setting components, instantiating + * prefabs, and several other APIs for retrieving entity data. + * + * The file also contains the implementation of the command buffer, which is + * located here so it can call functions private to the compilation unit. + */ - if (name) { - if (!name[0]) { - name = NULL; - } else if (flecs_name_is_id(name)){ - ecs_entity_t id = flecs_name_to_id(world, name); - if (!id) { - return 0; - } - if (result && (id != result)) { - ecs_err("name id conflicts with provided id"); - return 0; - } - name = NULL; - result = id; - } - } +#include - const char *root_sep = desc->root_sep; - bool flecs_new_entity = false; - bool name_assigned = false; +#ifdef FLECS_SCRIPT +/** + * @file addons/script/script.h + * @brief Flecs script implementation. + */ - /* Remove optional prefix from name. Entity names can be derived from - * language identifiers, such as components (typenames) and systems - * function names). Because C does not have namespaces, such identifiers - * often encode the namespace as a prefix. - * To ensure interoperability between C and C++ (and potentially other - * languages with namespacing) the entity must be stored without this prefix - * and with the proper namespace, which is what the name_prefix is for */ - const char *prefix = world->info.name_prefix; - if (name && prefix) { - ecs_size_t len = ecs_os_strlen(prefix); - if (!ecs_os_strncmp(name, prefix, len) && - (isupper(name[len]) || name[len] == '_')) - { - if (name[len] == '_') { - name = name + len + 1; - } else { - name = name + len; - } - } - } +#ifndef FLECS_SCRIPT_PRIVATE_H +#define FLECS_SCRIPT_PRIVATE_H - /* Find or create entity */ - if (!result) { - if (name) { - /* If add array contains a ChildOf pair, use it as scope instead */ - const ecs_id_t *ids = desc->add; - ecs_id_t id; - int32_t i = 0; - while ((i < FLECS_ID_DESC_MAX) && (id = ids[i ++])) { - if (ECS_HAS_ID_FLAG(id, PAIR) && - (ECS_PAIR_FIRST(id) == EcsChildOf)) - { - scope = ECS_PAIR_SECOND(id); - } - } - result = ecs_lookup_path_w_sep( - world, scope, name, sep, root_sep, false); - if (result) { - name_assigned = true; - } - } +#ifdef FLECS_SCRIPT - if (!result) { - if (desc->use_low_id) { - result = ecs_new_low_id(world); - } else { - result = ecs_new_id(world); - } - flecs_new_entity = true; - ecs_assert(ecs_get_type(world, result) == NULL, - ECS_INTERNAL_ERROR, NULL); - } - } else { - /* Make sure provided id is either alive or revivable */ - ecs_ensure(world, result); +#include - name_assigned = ecs_has_pair( - world, result, ecs_id(EcsIdentifier), EcsName); - if (name && name_assigned) { - /* If entity has name, verify that name matches. The name provided - * to the function could either have been relative to the current - * scope, or fully qualified. */ - char *path; - ecs_size_t root_sep_len = root_sep ? ecs_os_strlen(root_sep) : 0; - if (root_sep && !ecs_os_strncmp(name, root_sep, root_sep_len)) { - /* Fully qualified name was provided, so make sure to - * compare with fully qualified name */ - path = ecs_get_path_w_sep(world, 0, result, sep, root_sep); - } else { - /* Relative name was provided, so make sure to compare with - * relative name */ - if (!sep || sep[0]) { - path = ecs_get_path_w_sep(world, scope, result, sep, ""); - } else { - /* Safe, only freed when sep is valid */ - path = ECS_CONST_CAST(char*, ecs_get_name(world, result)); - } - } - if (path) { - if (ecs_os_strcmp(path, name)) { - /* Mismatching name */ - ecs_err("existing entity '%s' is initialized with " - "conflicting name '%s'", path, name); - if (!sep || sep[0]) { - ecs_os_free(path); - } - return 0; - } - if (!sep || sep[0]) { - ecs_os_free(path); - } - } - } - } +typedef struct ecs_script_scope_t ecs_script_scope_t; +typedef struct ecs_script_entity_t ecs_script_entity_t; - ecs_assert(name_assigned == ecs_has_pair( - world, result, ecs_id(EcsIdentifier), EcsName), - ECS_INTERNAL_ERROR, NULL); +typedef struct ecs_script_impl_t { + ecs_script_t pub; + ecs_allocator_t allocator; + ecs_script_scope_t *root; + char *token_buffer; + int32_t token_buffer_size; + int32_t refcount; +} ecs_script_impl_t; - if (stage->defer) { - flecs_deferred_add_remove((ecs_world_t*)stage, result, name, desc, - scope, with, flecs_new_entity, name_assigned); - } else { - if (flecs_traverse_add(world, result, name, desc, - scope, with, flecs_new_entity, name_assigned)) - { - return 0; - } - } +typedef struct ecs_script_parser_t ecs_script_parser_t; - return result; -error: - return 0; -} +#define flecs_script_impl(script) ((ecs_script_impl_t*)script) -const ecs_entity_t* ecs_bulk_init( - ecs_world_t *world, - const ecs_bulk_desc_t *desc) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); +/** + * @file addons/script/tokenizer.h + * @brief Script tokenizer. + */ - const ecs_entity_t *entities = desc->entities; - int32_t count = desc->count; +#ifndef FLECS_SCRIPT_TOKENIZER_H +#define FLECS_SCRIPT_TOKENIZER_H + +/* Tokenizer */ +typedef enum ecs_script_token_kind_t { + EcsTokEnd = '\0', + EcsTokUnknown, + EcsTokScopeOpen = '{', + EcsTokScopeClose = '}', + EcsTokParenOpen = '(', + EcsTokParenClose = ')', + EcsTokBracketOpen = '[', + EcsTokBracketClose = ']', + EcsTokMul = '*', + EcsTokComma = ',', + EcsTokSemiColon = ';', + EcsTokColon = ':', + EcsTokAssign = '=', + EcsTokBitwiseOr = '|', + EcsTokNot = '!', + EcsTokOptional = '?', + EcsTokAnnotation = '@', + EcsTokNewline = '\n', + EcsTokEq, + EcsTokNeq, + EcsTokMatch, + EcsTokOr, + EcsTokIdentifier, + EcsTokString, + EcsTokNumber, + EcsTokKeywordModule, + EcsTokKeywordUsing, + EcsTokKeywordWith, + EcsTokKeywordIf, + EcsTokKeywordElse, + EcsTokKeywordTemplate, + EcsTokKeywordProp, + EcsTokKeywordConst, +} ecs_script_token_kind_t; + +typedef struct ecs_script_token_t { + const char *value; + ecs_script_token_kind_t kind; +} ecs_script_token_t; + +typedef struct ecs_script_tokens_t { + int32_t count; + ecs_script_token_t tokens[256]; +} ecs_script_tokens_t; - int32_t sparse_count = 0; - if (!entities) { - sparse_count = flecs_entities_count(world); - entities = flecs_entities_new_ids(world, count); - ecs_assert(entities != NULL, ECS_INTERNAL_ERROR, NULL); - } else { - int i; - for (i = 0; i < count; i ++) { - ecs_ensure(world, entities[i]); - } - } +const char* flecs_script_expr( + ecs_script_parser_t *parser, + const char *ptr, + ecs_script_token_t *out, + char until); - ecs_type_t ids; - ecs_table_t *table = desc->table; - if (!table) { - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); +const char* flecs_script_until( + ecs_script_parser_t *parser, + const char *ptr, + ecs_script_token_t *out, + char until); - int32_t i = 0; - ecs_id_t id; - while ((id = desc->ids[i])) { - table = flecs_find_table_add(world, table, id, &diff); - i ++; - } +const char* flecs_script_token_kind_str( + ecs_script_token_kind_t kind); - ids.array = ECS_CONST_CAST(ecs_id_t*, desc->ids); - ids.count = i; +const char* flecs_script_token( + ecs_script_parser_t *parser, + const char *ptr, + ecs_script_token_t *out, + bool is_lookahead); - 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); - } +const char* flecs_scan_whitespace( + ecs_script_parser_t *parser, + const char *pos); - if (!sparse_count) { - return entities; - } else { - /* Refetch entity ids, in case the underlying array was reallocated */ - entities = flecs_entities_ids(world); - return &entities[sparse_count]; - } -error: - return NULL; -} +#endif -static -void flecs_check_component( - ecs_world_t *world, - ecs_entity_t result, - const EcsComponent *ptr, - ecs_size_t size, - ecs_size_t alignment) -{ - if (ptr->size != size) { - char *path = ecs_get_fullpath(world, result); - ecs_abort(ECS_INVALID_COMPONENT_SIZE, path); - ecs_os_free(path); - } - if (ptr->alignment != alignment) { - char *path = ecs_get_fullpath(world, result); - ecs_abort(ECS_INVALID_COMPONENT_ALIGNMENT, path); - ecs_os_free(path); - } -} -ecs_entity_t ecs_component_init( - ecs_world_t *world, - const ecs_component_desc_t *desc) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); +struct ecs_script_parser_t { + ecs_script_impl_t *script; + ecs_script_scope_t *scope; + const char *pos; + char *token_cur; + char *token_keep; + bool significant_newline; - /* If existing entity is provided, check if it is already registered as a - * component and matches the size/alignment. This can prevent having to - * suspend readonly mode, and increases the number of scenarios in which - * this function can be called in multithreaded mode. */ - ecs_entity_t result = desc->entity; - if (result && ecs_is_alive(world, result)) { - const EcsComponent *const_ptr = ecs_get(world, result, EcsComponent); - if (const_ptr) { - flecs_check_component(world, result, const_ptr, - desc->type.size, desc->type.alignment); - return result; - } - } + /* For term parser */ + ecs_term_t *term; + ecs_oper_kind_t extra_oper; + ecs_term_ref_t *extra_args; +}; - ecs_suspend_readonly_state_t readonly_state; - world = flecs_suspend_readonly(world, &readonly_state); +/** + * @file addons/script/ast.h + * @brief Script AST. + */ - bool new_component = true; - if (!result) { - result = ecs_new_low_id(world); - } else { - ecs_ensure(world, result); - new_component = ecs_has(world, result, EcsComponent); - } +#ifndef FLECS_SCRIPT_AST_H +#define FLECS_SCRIPT_AST_H + +typedef enum ecs_script_node_kind_t { + EcsAstScope, + EcsAstTag, + EcsAstComponent, + EcsAstDefaultComponent, + EcsAstVarComponent, + EcsAstWithVar, + EcsAstWithTag, + EcsAstWithComponent, + EcsAstWith, + EcsAstUsing, + EcsAstModule, + EcsAstAnnotation, + EcsAstTemplate, + EcsAstProp, + EcsAstConst, + EcsAstEntity, + EcsAstPairScope, + EcsAstIf, +} ecs_script_node_kind_t; + +typedef struct ecs_script_node_t { + ecs_script_node_kind_t kind; + const char *pos; +} ecs_script_node_t; + +struct ecs_script_scope_t { + ecs_script_node_t node; + ecs_vec_t stmts; + ecs_script_scope_t *parent; + ecs_id_t default_component_eval; +}; - EcsComponent *ptr = ecs_get_mut(world, result, EcsComponent); - if (!ptr->size) { - ecs_assert(ptr->alignment == 0, ECS_INTERNAL_ERROR, NULL); - ptr->size = desc->type.size; - ptr->alignment = desc->type.alignment; - if (!new_component || ptr->size != desc->type.size) { - if (!ptr->size) { - ecs_trace("#[green]tag#[reset] %s created", - ecs_get_name(world, result)); - } else { - ecs_trace("#[green]component#[reset] %s created", - ecs_get_name(world, result)); - } - } - } else { - flecs_check_component(world, result, ptr, - desc->type.size, desc->type.alignment); - } +typedef struct ecs_script_id_t { + const char *first; + const char *second; + ecs_id_t flag; + ecs_id_t eval; +} ecs_script_id_t; + +typedef struct ecs_script_tag_t { + ecs_script_node_t node; + ecs_script_id_t id; +} ecs_script_tag_t; + +typedef struct ecs_script_component_t { + ecs_script_node_t node; + ecs_script_id_t id; + const char *expr; + ecs_value_t eval; + bool is_collection; +} ecs_script_component_t; - ecs_modified(world, result, EcsComponent); +typedef struct ecs_script_default_component_t { + ecs_script_node_t node; + const char *expr; + ecs_value_t eval; +} ecs_script_default_component_t; - if (desc->type.size && - !ecs_id_in_use(world, result) && - !ecs_id_in_use(world, ecs_pair(result, EcsWildcard))) - { - ecs_set_hooks_id(world, result, &desc->type.hooks); - } +typedef struct ecs_script_var_component_t { + ecs_script_node_t node; + const char *name; +} ecs_script_var_component_t; - if (result >= world->info.last_component_id && result < FLECS_HI_COMPONENT_ID) { - world->info.last_component_id = result + 1; - } +struct ecs_script_entity_t { + ecs_script_node_t node; + const char *kind; + const char *name; + bool name_is_var; + bool kind_w_expr; + ecs_script_scope_t *scope; + + // Populated during eval + ecs_script_entity_t *parent; + ecs_entity_t eval; + ecs_entity_t eval_kind; +}; - /* Ensure components cannot be deleted */ - ecs_add_pair(world, result, EcsOnDelete, EcsPanic); +typedef struct ecs_script_with_t { + ecs_script_node_t node; + ecs_script_scope_t *expressions; + ecs_script_scope_t *scope; +} ecs_script_with_t; + +typedef struct ecs_script_inherit_t { + ecs_script_node_t node; + ecs_script_scope_t *base_list; +} ecs_script_inherit_t; + +typedef struct ecs_script_pair_scope_t { + ecs_script_node_t node; + ecs_script_id_t id; + ecs_script_scope_t *scope; +} ecs_script_pair_scope_t; + +typedef struct ecs_script_using_t { + ecs_script_node_t node; + const char *name; +} ecs_script_using_t; - flecs_resume_readonly(world, &readonly_state); - - ecs_assert(result != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ecs_has(world, result, EcsComponent), ECS_INTERNAL_ERROR, NULL); +typedef struct ecs_script_module_t { + ecs_script_node_t node; + const char *name; +} ecs_script_module_t; - return result; -error: - return 0; -} +typedef struct ecs_script_annot_t { + ecs_script_node_t node; + const char *name; + const char *expr; +} ecs_script_annot_t; -const ecs_entity_t* ecs_bulk_new_w_id( - ecs_world_t *world, - ecs_id_t id, - int32_t count) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); +typedef struct ecs_script_template_node_t { + ecs_script_node_t node; + const char *name; + ecs_script_scope_t* scope; +} ecs_script_template_node_t; - const ecs_entity_t *ids; - if (flecs_defer_bulk_new(world, stage, count, id, &ids)) { - return ids; - } +typedef struct ecs_script_var_node_t { + ecs_script_node_t node; + const char *name; + const char *type; + const char *expr; +} ecs_script_var_node_t; - ecs_table_t *table = &world->store.root; - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); - - if (id) { - table = flecs_find_table_add(world, table, id, &diff); - } +typedef struct ecs_script_if_t { + ecs_script_node_t node; + ecs_script_scope_t *if_true; + ecs_script_scope_t *if_false; + const char *expr; +} ecs_script_if_t; - ecs_table_diff_t td; - flecs_table_diff_build_noalloc(&diff, &td); - ids = flecs_bulk_new(world, table, NULL, NULL, count, NULL, false, NULL, &td); - flecs_table_diff_builder_fini(world, &diff); - flecs_defer_end(world, stage); +#define ecs_script_node(kind, node)\ + ((ecs_script_##kind##_t*)node) - return ids; -error: - return NULL; -} +bool flecs_scope_is_empty( + ecs_script_scope_t *scope); -void ecs_clear( - ecs_world_t *world, - ecs_entity_t entity) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_valid(world, entity), ECS_INVALID_PARAMETER, NULL); +ecs_script_scope_t* flecs_script_insert_scope( + ecs_script_parser_t *parser); - ecs_stage_t *stage = flecs_stage_from_world(&world); - if (flecs_defer_clear(stage, entity)) { - return; - } +ecs_script_entity_t* flecs_script_insert_entity( + ecs_script_parser_t *parser, + const char *name); - ecs_record_t *r = flecs_entities_get(world, entity); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); +ecs_script_pair_scope_t* flecs_script_insert_pair_scope( + ecs_script_parser_t *parser, + const char *first, + const char *second); - ecs_table_t *table = r->table; - if (table) { - ecs_table_diff_t diff = { - .removed = table->type - }; +ecs_script_with_t* flecs_script_insert_with( + ecs_script_parser_t *parser); - flecs_delete_entity(world, r, &diff); - r->table = NULL; +ecs_script_using_t* flecs_script_insert_using( + ecs_script_parser_t *parser, + const char *name); - if (r->row & EcsEntityIsTraversable) { - flecs_table_traversable_add(table, -1); - } - } +ecs_script_module_t* flecs_script_insert_module( + ecs_script_parser_t *parser, + const char *name); - flecs_defer_end(world, stage); -error: - return; -} +ecs_script_template_node_t* flecs_script_insert_template( + ecs_script_parser_t *parser, + const char *name); -static -void flecs_throw_invalid_delete( - ecs_world_t *world, - ecs_id_t id) -{ - char *id_str = NULL; - if (!(world->flags & EcsWorldQuit)) { - id_str = ecs_id_str(world, id); - ecs_throw(ECS_CONSTRAINT_VIOLATED, id_str); - } -error: - ecs_os_free(id_str); -} +ecs_script_annot_t* flecs_script_insert_annot( + ecs_script_parser_t *parser, + const char *name, + const char *expr); -static -void flecs_marked_id_push( - ecs_world_t *world, - ecs_id_record_t* idr, - ecs_entity_t action, - bool delete_id) -{ - ecs_marked_id_t *m = ecs_vec_append_t(&world->allocator, - &world->store.marked_ids, ecs_marked_id_t); +ecs_script_var_node_t* flecs_script_insert_var( + ecs_script_parser_t *parser, + const char *name); - m->idr = idr; - m->id = idr->id; - m->action = action; - m->delete_id = delete_id; +ecs_script_tag_t* flecs_script_insert_tag( + ecs_script_parser_t *parser, + const char *name); - flecs_id_record_claim(world, idr); -} +ecs_script_tag_t* flecs_script_insert_pair_tag( + ecs_script_parser_t *parser, + const char *first, + const char *second); -static -void flecs_id_mark_for_delete( - ecs_world_t *world, - ecs_id_record_t *idr, - ecs_entity_t action, - bool delete_id); +ecs_script_component_t* flecs_script_insert_component( + ecs_script_parser_t *parser, + const char *name); -static -void flecs_targets_mark_for_delete( - ecs_world_t *world, - ecs_table_t *table) -{ - ecs_id_record_t *idr; - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); - int32_t i, count = ecs_vec_count(&table->data.entities); - for (i = 0; i < count; i ++) { - ecs_record_t *r = flecs_entities_get(world, entities[i]); - if (!r) { - continue; - } +ecs_script_component_t* flecs_script_insert_pair_component( + ecs_script_parser_t *parser, + const char *first, + const char *second); - /* If entity is not used as id or as relationship target, there won't - * be any tables with a reference to it. */ - ecs_flags32_t flags = r->row & ECS_ROW_FLAGS_MASK; - if (!(flags & (EcsEntityIsId|EcsEntityIsTarget))) { - continue; - } +ecs_script_default_component_t* flecs_script_insert_default_component( + ecs_script_parser_t *parser); - ecs_entity_t e = entities[i]; - if (flags & EcsEntityIsId) { - if ((idr = flecs_id_record_get(world, e))) { - flecs_id_mark_for_delete(world, idr, - ECS_ID_ON_DELETE(idr->flags), true); - } - if ((idr = flecs_id_record_get(world, ecs_pair(e, EcsWildcard)))) { - flecs_id_mark_for_delete(world, idr, - ECS_ID_ON_DELETE(idr->flags), true); - } - } - if (flags & EcsEntityIsTarget) { - if ((idr = flecs_id_record_get(world, ecs_pair(EcsWildcard, e)))) { - flecs_id_mark_for_delete(world, idr, - ECS_ID_ON_DELETE_TARGET(idr->flags), true); - } - if ((idr = flecs_id_record_get(world, ecs_pair(EcsFlag, e)))) { - flecs_id_mark_for_delete(world, idr, - ECS_ID_ON_DELETE_TARGET(idr->flags), true); - } - } - } -} +ecs_script_var_component_t* flecs_script_insert_var_component( + ecs_script_parser_t *parser, + const char *name); -static -bool flecs_id_is_delete_target( - ecs_id_t id, - ecs_entity_t action) -{ - if (!action && ecs_id_is_pair(id) && ECS_PAIR_FIRST(id) == EcsWildcard) { - /* If no explicit delete action is provided, and the id we're deleting - * has the form (*, Target), use OnDeleteTarget action */ - return true; - } - return false; -} +ecs_script_if_t* flecs_script_insert_if( + ecs_script_parser_t *parser); -static -ecs_entity_t flecs_get_delete_action( - ecs_table_t *table, - ecs_table_record_t *tr, - ecs_entity_t action, - bool delete_target) -{ - ecs_entity_t result = action; - if (!result && delete_target) { - ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; - ecs_id_t id = idr->id; +#endif - /* If action is not specified and we're deleting a relationship target, - * derive the action from the current record */ - int32_t i = tr->index, count = tr->count; - do { - ecs_type_t *type = &table->type; - ecs_table_record_t *trr = &table->_->records[i]; - ecs_id_record_t *idrr = (ecs_id_record_t*)trr->hdr.cache; - result = ECS_ID_ON_DELETE_TARGET(idrr->flags); - if (result == EcsDelete) { - /* Delete takes precedence over Remove */ - break; - } +/** + * @file addons/script/visit.h + * @brief Script AST visitor utilities. + */ - if (count > 1) { - /* If table contains multiple pairs for target they are not - * guaranteed to occupy consecutive elements in the table's type - * vector, so a linear search is needed to find matches. */ - for (++ i; i < type->count; i ++) { - if (ecs_id_match(type->array[i], id)) { - break; - } - } +#ifndef FLECS_SCRIPT_VISIT_H +#define FLECS_SCRIPT_VISIT_H - /* We should always have as many matching ids as tr->count */ - ecs_assert(i < type->count, ECS_INTERNAL_ERROR, NULL); - } - } while (--count); - } +typedef struct ecs_script_visit_t ecs_script_visit_t; - return result; -} +typedef int (*ecs_visit_action_t)( + ecs_script_visit_t *visitor, + ecs_script_node_t *node); -static -void flecs_update_monitors_for_delete( - ecs_world_t *world, - ecs_id_t id) -{ - flecs_update_component_monitors(world, NULL, &(ecs_type_t){ - .array = (ecs_id_t[]){id}, - .count = 1 - }); -} +struct ecs_script_visit_t { + ecs_script_impl_t *script; + ecs_visit_action_t visit; + ecs_script_node_t* nodes[256]; + ecs_script_node_t *prev, *next; + int32_t depth; +}; -static -void flecs_id_mark_for_delete( - ecs_world_t *world, - ecs_id_record_t *idr, - ecs_entity_t action, - bool delete_id) -{ - if (idr->flags & EcsIdMarkedForDelete) { - return; - } +int ecs_script_visit_( + ecs_script_visit_t *visitor, + ecs_visit_action_t visit, + ecs_script_impl_t *script); - idr->flags |= EcsIdMarkedForDelete; - flecs_marked_id_push(world, idr, action, delete_id); +#define ecs_script_visit(script, visitor, visit) \ + ecs_script_visit_((ecs_script_visit_t*)visitor,\ + (ecs_visit_action_t)visit,\ + script) - ecs_id_t id = idr->id; +int ecs_script_visit_node_( + ecs_script_visit_t *v, + ecs_script_node_t *node); - bool delete_target = flecs_id_is_delete_target(id, action); +#define ecs_script_visit_node(visitor, node) \ + ecs_script_visit_node_((ecs_script_visit_t*)visitor, \ + (ecs_script_node_t*)node) - /* Mark all tables with the id for delete */ - ecs_table_cache_iter_t it; - if (flecs_table_cache_iter(&idr->cache, &it)) { - ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; - if (table->flags & EcsTableMarkedForDelete) { - continue; - } +int ecs_script_visit_scope_( + ecs_script_visit_t *v, + ecs_script_scope_t *node); - ecs_entity_t cur_action = flecs_get_delete_action(table, tr, action, - delete_target); +#define ecs_script_visit_scope(visitor, node) \ + ecs_script_visit_scope_((ecs_script_visit_t*)visitor, node) - /* If this is a Delete action, recursively mark ids & tables */ - if (cur_action == EcsDelete) { - table->flags |= EcsTableMarkedForDelete; - ecs_log_push_2(); - flecs_targets_mark_for_delete(world, table); - ecs_log_pop_2(); - } else if (cur_action == EcsPanic) { - flecs_throw_invalid_delete(world, id); - } - } - } +ecs_script_node_t* ecs_script_parent_node_( + ecs_script_visit_t *v); - /* Same for empty tables */ - if (flecs_table_cache_empty_iter(&idr->cache, &it)) { - ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - tr->hdr.table->flags |= EcsTableMarkedForDelete; - } - } +#define ecs_script_parent_node(visitor) \ + ecs_script_parent_node_((ecs_script_visit_t*)visitor) - /* Signal query cache monitors */ - flecs_update_monitors_for_delete(world, id); +ecs_script_scope_t* ecs_script_current_scope_( + ecs_script_visit_t *v); - /* If id is a wildcard pair, update cache monitors for non-wildcard ids */ - if (ecs_id_is_wildcard(id)) { - ecs_assert(ECS_HAS_ID_FLAG(id, PAIR), ECS_INTERNAL_ERROR, NULL); - ecs_id_record_t *cur = idr; - if (ECS_PAIR_SECOND(id) == EcsWildcard) { - while ((cur = cur->first.next)) { - flecs_update_monitors_for_delete(world, cur->id); - } - } else { - ecs_assert(ECS_PAIR_FIRST(id) == EcsWildcard, - ECS_INTERNAL_ERROR, NULL); - while ((cur = cur->second.next)) { - flecs_update_monitors_for_delete(world, cur->id); - } - } - } -} +#define ecs_script_current_scope(visitor) \ + ecs_script_current_scope_((ecs_script_visit_t*)visitor) -static -bool flecs_on_delete_mark( - ecs_world_t *world, - ecs_id_t id, - ecs_entity_t action, - bool delete_id) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - /* If there's no id record, there's nothing to delete */ - return false; - } +ecs_script_node_t* ecs_script_parent_( + ecs_script_visit_t *v, + ecs_script_node_t *node); - if (!action) { - /* If no explicit action is provided, derive it */ - if (!ecs_id_is_pair(id) || ECS_PAIR_SECOND(id) == EcsWildcard) { - /* Delete actions are determined by the component, or in the case - * of a pair by the relationship. */ - action = ECS_ID_ON_DELETE(idr->flags); - } - } +#define ecs_script_parent(visitor, node) \ + ecs_script_parent_((ecs_script_visit_t*)visitor, (ecs_script_node_t*)node) - if (action == EcsPanic) { - /* This id is protected from deletion */ - flecs_throw_invalid_delete(world, id); - return false; - } +ecs_script_node_t* ecs_script_next_node_( + ecs_script_visit_t *v); - flecs_id_mark_for_delete(world, idr, action, delete_id); +#define ecs_script_next_node(visitor) \ + ecs_script_next_node_((ecs_script_visit_t*)visitor) - return true; -} +int32_t ecs_script_node_line_number_( + ecs_script_impl_t *script, + ecs_script_node_t *node); -static -void flecs_remove_from_table( - ecs_world_t *world, - ecs_table_t *table) -{ - ecs_table_diff_t temp_diff; - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); - ecs_table_t *dst_table = table; +#define ecs_script_node_line_number(script, node) \ + ecs_script_node_line_number_(script, (ecs_script_node_t*)node) - /* To find the dst table, remove all ids that are marked for deletion */ - int32_t i, t, count = ecs_vec_count(&world->store.marked_ids); - ecs_marked_id_t *ids = ecs_vec_first(&world->store.marked_ids); - const ecs_table_record_t *tr; - for (i = 0; i < count; i ++) { - const ecs_id_record_t *idr = ids[i].idr; +#endif - if (!(tr = flecs_id_record_get_table(idr, dst_table))) { - continue; - } +/** + * @file addons/script/visit_eval.h + * @brief Script evaluation visitor. + */ - t = tr->index; +#ifndef FLECS_SCRIPT_VISIT_EVAL_H +#define FLECS_SCRIPT_VISIT_EVAL_H - do { - ecs_id_t id = dst_table->type.array[t]; - dst_table = flecs_table_traverse_remove( - world, dst_table, &id, &temp_diff); - flecs_table_diff_build_append_table(world, &diff, &temp_diff); - } while (dst_table->type.count && (t = ecs_search_offset( - world, dst_table, t, idr->id, NULL)) != -1); - } +typedef struct ecs_script_eval_visitor_t { + ecs_script_visit_t base; + ecs_world_t *world; + ecs_allocator_t *allocator; + ecs_script_template_t *template; + ecs_entity_t module; + ecs_entity_t parent; + ecs_script_entity_t *entity; + ecs_vec_t using; + ecs_vec_t with; + ecs_vec_t with_type_info; + ecs_vec_t annot; + ecs_entity_t with_relationship; + int32_t with_relationship_sp; + ecs_script_vars_t *vars; + ecs_stack_t stack; +} ecs_script_eval_visitor_t; + +void flecs_script_eval_error_( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node, + const char *fmt, + ...); - ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); +#define flecs_script_eval_error(v, node, ...)\ + flecs_script_eval_error_(v, (ecs_script_node_t*)node, __VA_ARGS__) - if (!dst_table->type.count) { - /* If this removes all components, clear table */ - flecs_table_clear_entities(world, table); - } else { - /* Otherwise, merge table into dst_table */ - if (dst_table != table) { - int32_t table_count = ecs_table_count(table); - if (diff.removed.count && table_count) { - ecs_log_push_3(); - ecs_table_diff_t td; - flecs_table_diff_build_noalloc(&diff, &td); - flecs_notify_on_remove(world, table, NULL, 0, table_count, - &td.removed); - ecs_log_pop_3(); - } +ecs_entity_t flecs_script_find_entity( + ecs_script_eval_visitor_t *v, + ecs_entity_t from, + const char *path); - flecs_table_merge(world, dst_table, table, - &dst_table->data, &table->data); - } - } +ecs_entity_t flecs_script_create_entity( + ecs_script_eval_visitor_t *v, + const char *name); - flecs_table_diff_builder_fini(world, &diff); -} +const ecs_type_info_t* flecs_script_get_type_info( + ecs_script_eval_visitor_t *v, + void *node, + ecs_id_t id); -static -bool flecs_on_delete_clear_tables( - ecs_world_t *world) -{ - /* Iterate in reverse order so that DAGs get deleted bottom to top */ - int32_t i, last = ecs_vec_count(&world->store.marked_ids), first = 0; - ecs_marked_id_t *ids = ecs_vec_first(&world->store.marked_ids); - do { - for (i = last - 1; i >= first; i --) { - ecs_id_record_t *idr = ids[i].idr; - ecs_entity_t action = ids[i].action; +int flecs_script_eval_expr( + ecs_script_eval_visitor_t *v, + const char *expr, + ecs_value_t *value); - /* Empty all tables for id */ - { - ecs_table_cache_iter_t it; - if (flecs_table_cache_iter(&idr->cache, &it)) { - ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; +int flecs_script_eval_template( + ecs_script_eval_visitor_t *v, + ecs_script_template_node_t *template); - if ((action == EcsRemove) || - !(table->flags & EcsTableMarkedForDelete)) - { - flecs_remove_from_table(world, table); - } else { - ecs_dbg_3( - "#[red]delete#[reset] entities from table %u", - (uint32_t)table->id); - flecs_table_delete_entities(world, table); - } - } - } - } +ecs_script_template_t* flecs_script_template_init( + ecs_script_impl_t *script); - /* Run commands so children get notified before parent is deleted */ - if (world->stages[0].defer) { - flecs_defer_end(world, &world->stages[0]); - flecs_defer_begin(world, &world->stages[0]); - } +void flecs_script_template_fini( + ecs_script_impl_t *script, + ecs_script_template_t *template); - /* User code (from triggers) could have enqueued more ids to delete, - * reobtain the array in case it got reallocated */ - ids = ecs_vec_first(&world->store.marked_ids); - } +void flecs_script_eval_visit_init( + ecs_script_impl_t *script, + ecs_script_eval_visitor_t *v); - /* Check if new ids were marked since we started */ - int32_t new_last = ecs_vec_count(&world->store.marked_ids); - if (new_last != last) { - /* Iterate remaining ids */ - ecs_assert(new_last > last, ECS_INTERNAL_ERROR, NULL); - first = last; - last = new_last; - } else { - break; - } - } while (true); +void flecs_script_eval_visit_fini( + ecs_script_eval_visitor_t *v); - return true; -} +int flecs_script_eval_node( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node); -static -bool flecs_on_delete_clear_ids( - ecs_world_t *world) -{ - int32_t i, count = ecs_vec_count(&world->store.marked_ids); - ecs_marked_id_t *ids = ecs_vec_first(&world->store.marked_ids); - int twice = 2; +#endif - do { - for (i = 0; i < count; i ++) { - /* Release normal ids before wildcard ids */ - if (ecs_id_is_wildcard(ids[i].id)) { - if (twice == 2) { - continue; - } - } else { - if (twice == 1) { - continue; - } - } - ecs_id_record_t *idr = ids[i].idr; - bool delete_id = ids[i].delete_id; +struct ecs_script_template_t { + /* Template handle */ + ecs_entity_t entity; - flecs_id_record_release_tables(world, idr); + /* Template AST node */ + ecs_script_template_node_t *node; - /* Release the claim taken by flecs_marked_id_push. This may delete the - * id record as all other claims may have been released. */ - int32_t rc = flecs_id_record_release(world, idr); - ecs_assert(rc >= 0, ECS_INTERNAL_ERROR, NULL); - (void)rc; + /* Hoisted using statements */ + ecs_vec_t using_; - /* If rc is 0, the id was likely deleted by a nested delete_with call - * made by an on_remove handler/OnRemove observer */ - if (rc) { - if (delete_id) { - /* If id should be deleted, release initial claim. This happens when - * a component, tag, or part of a pair is deleted. */ - flecs_id_record_release(world, idr); - } else { - /* If id should not be deleted, unmark id record for deletion. This - * happens when all instances *of* an id are deleted, for example - * when calling ecs_remove_all or ecs_delete_with. */ - idr->flags &= ~EcsIdMarkedForDelete; - } - } - } - } while (-- twice); + /* Hoisted variables */ + ecs_script_vars_t *vars; - return true; -} + /* Default values for props */ + ecs_vec_t prop_defaults; -static -void flecs_on_delete( - ecs_world_t *world, - ecs_id_t id, - ecs_entity_t action, - bool delete_id) -{ - /* Cleanup can happen recursively. If a cleanup action is already in - * progress, only append ids to the marked_ids. The topmost cleanup - * frame will handle the actual cleanup. */ - int32_t count = ecs_vec_count(&world->store.marked_ids); + /* Type info for template component */ + const ecs_type_info_t *type_info; +}; - /* Make sure we're evaluating a consistent list of non-empty tables */ - ecs_run_aperiodic(world, EcsAperiodicEmptyTables); +ecs_script_t* flecs_script_new( + ecs_world_t *world); - /* Collect all ids that need to be deleted */ - flecs_on_delete_mark(world, id, action, delete_id); +ecs_script_scope_t* flecs_script_scope_new( + ecs_script_parser_t *parser); - /* Only perform cleanup if we're the first stack frame doing it */ - if (!count && ecs_vec_count(&world->store.marked_ids)) { - ecs_dbg_2("#[red]delete#[reset]"); - ecs_log_push_2(); +int flecs_script_visit_free( + ecs_script_t *script); - /* Empty tables with all the to be deleted ids */ - flecs_on_delete_clear_tables(world); +ecs_script_vars_t* flecs_script_vars_push( + ecs_script_vars_t *parent, + ecs_stack_t *stack, + ecs_allocator_t *allocator); - /* All marked tables are empty, ensure they're in the right list */ - ecs_run_aperiodic(world, EcsAperiodicEmptyTables); +int flecs_terms_parse( + ecs_script_t *script, + ecs_term_t *terms, + int32_t *term_count_out); - /* Release remaining references to the ids */ - flecs_on_delete_clear_ids(world); +const char* flecs_id_parse( + const ecs_world_t *world, + const char *name, + const char *expr, + ecs_id_t *id); - /* Verify deleted ids are no longer in use */ -#ifdef FLECS_DEBUG - ecs_marked_id_t *ids = ecs_vec_first(&world->store.marked_ids); - int32_t i; count = ecs_vec_count(&world->store.marked_ids); - for (i = 0; i < count; i ++) { - ecs_assert(!ecs_id_in_use(world, ids[i].id), - ECS_INTERNAL_ERROR, NULL); - } -#endif - ecs_assert(!ecs_id_in_use(world, id), ECS_INTERNAL_ERROR, NULL); +const char* flecs_term_parse( + ecs_world_t *world, + const char *name, + const char *expr, + ecs_term_t *term, + char *token_buffer); - /* Ids are deleted, clear stack */ - ecs_vec_clear(&world->store.marked_ids); +#endif // FLECS_SCRIPT +#endif // FLECS_SCRIPT_PRIVATE_H - ecs_log_pop_2(); - } -} +#endif -void ecs_delete_with( +static +const ecs_entity_t* flecs_bulk_new( ecs_world_t *world, - ecs_id_t id) -{ - flecs_journal_begin(world, EcsJournalDeleteWith, id, NULL, NULL); - - ecs_stage_t *stage = flecs_stage_from_world(&world); - if (flecs_defer_on_delete_action(stage, id, EcsDelete)) { - return; - } + ecs_table_t *table, + const ecs_entity_t *entities, + ecs_type_t *component_ids, + int32_t count, + void **c_info, + bool move, + int32_t *row_out, + ecs_table_diff_t *diff); - flecs_on_delete(world, id, EcsDelete, false); - flecs_defer_end(world, stage); +typedef struct { + const ecs_type_info_t *ti; + void *ptr; +} flecs_component_ptr_t; - flecs_journal_end(); +static +flecs_component_ptr_t flecs_table_get_component( + ecs_table_t *table, + int32_t column_index, + int32_t row) +{ + ecs_check(column_index < table->column_count, ECS_NOT_A_COMPONENT, NULL); + ecs_column_t *column = &table->data.columns[column_index]; + return (flecs_component_ptr_t){ + .ti = column->ti, + .ptr = ECS_ELEM(column->data, column->ti->size, row) + }; +error: + return (flecs_component_ptr_t){0}; } -void ecs_remove_all( - ecs_world_t *world, - ecs_id_t id) +static +flecs_component_ptr_t flecs_get_component_ptr( + ecs_table_t *table, + int32_t row, + ecs_id_record_t *idr) { - flecs_journal_begin(world, EcsJournalRemoveAll, id, NULL, NULL); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); - if (flecs_defer_on_delete_action(stage, id, EcsRemove)) { - return; + if (!idr) { + return (flecs_component_ptr_t){0}; } - flecs_on_delete(world, id, EcsRemove, false); - flecs_defer_end(world, stage); + if (idr->flags & EcsIdIsSparse) { + ecs_entity_t entity = ecs_table_entities(table)[row]; + return (flecs_component_ptr_t){ + .ti = idr->type_info, + .ptr = flecs_sparse_get_any(idr->sparse, 0, entity) + }; + } - flecs_journal_end(); + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr || (tr->column == -1)) { + return (flecs_component_ptr_t){0}; + } + + return flecs_table_get_component(table, tr->column, row); } -void ecs_delete( - ecs_world_t *world, - ecs_entity_t entity) +static +void* flecs_get_component( + ecs_table_t *table, + int32_t row, + ecs_id_record_t *idr) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); + return flecs_get_component_ptr(table, row, idr).ptr; +} - ecs_stage_t *stage = flecs_stage_from_world(&world); - if (flecs_defer_delete(stage, entity)) { - return; +void* flecs_get_base_component( + const ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + ecs_id_record_t *idr, + int32_t recur_depth) +{ + ecs_check(recur_depth < ECS_MAX_RECURSION, ECS_INVALID_PARAMETER, + "cycle detected in IsA relationship"); + + /* Table (and thus entity) does not have component, look for base */ + if (!(table->flags & EcsTableHasIsA)) { + return NULL; } - ecs_record_t *r = flecs_entities_try(world, entity); - if (r) { - flecs_journal_begin(world, EcsJournalDelete, entity, NULL, NULL); + if (!(idr->flags & EcsIdOnInstantiateInherit)) { + return NULL; + } - ecs_flags32_t row_flags = ECS_RECORD_TO_ROW_FLAGS(r->row); - ecs_table_t *table; - if (row_flags) { - if (row_flags & EcsEntityIsTarget) { - 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 & EcsEntityIsId) { - flecs_on_delete(world, entity, 0, true); - flecs_on_delete(world, ecs_pair(entity, EcsWildcard), 0, true); - } - if (row_flags & EcsEntityIsTraversable) { - table = r->table; - if (table) { - flecs_table_traversable_add(table, -1); - } - } - /* Merge operations before deleting entity */ - flecs_defer_end(world, stage); - flecs_defer_begin(world, stage); - } + /* Exclude Name */ + if (id == ecs_pair(ecs_id(EcsIdentifier), EcsName)) { + return NULL; + } - table = r->table; + /* Table should always be in the table index for (IsA, *), otherwise the + * HasBase flag should not have been set */ + ecs_table_record_t *tr_isa = flecs_id_record_get_table( + world->idr_isa_wildcard, table); + ecs_check(tr_isa != NULL, ECS_INTERNAL_ERROR, NULL); - if (table) { - ecs_table_diff_t diff = { - .removed = table->type - }; + ecs_type_t type = table->type; + ecs_id_t *ids = type.array; + int32_t i = tr_isa->index, end = tr_isa->count + tr_isa->index; + void *ptr = NULL; - flecs_delete_entity(world, r, &diff); + do { + ecs_id_t pair = ids[i ++]; + ecs_entity_t base = ecs_pair_second(world, pair); - r->row = 0; - r->table = NULL; + ecs_record_t *r = flecs_entities_get(world, base); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + + table = r->table; + if (!table) { + continue; } - - flecs_entities_remove(world, entity); - flecs_journal_end(); - } + const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + ptr = flecs_get_base_component(world, table, id, idr, + recur_depth + 1); + } else { + if (idr->flags & EcsIdIsSparse) { + return flecs_sparse_get_any(idr->sparse, 0, base); + } else { + int32_t row = ECS_RECORD_TO_ROW(r->row); + return flecs_table_get_component(table, tr->column, row).ptr; + } + } + } while (!ptr && (i < end)); - flecs_defer_end(world, stage); + return ptr; error: - return; + return NULL; } -void ecs_add_id( +static +void flecs_instantiate_slot( ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) + ecs_entity_t base, + ecs_entity_t instance, + ecs_entity_t slot_of, + ecs_entity_t slot, + ecs_entity_t child) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - flecs_add_id(world, entity, id); + if (base == slot_of) { + /* Instance inherits from slot_of, add slot to instance */ + ecs_add_pair(world, instance, slot, child); + } else { + /* Slot is registered for other prefab, travel hierarchy + * upwards to find instance that inherits from slot_of */ + ecs_entity_t parent = instance; + int32_t depth = 0; + do { + if (ecs_has_pair(world, parent, EcsIsA, slot_of)) { + const char *name = ecs_get_name(world, slot); + if (name == NULL) { + char *slot_of_str = ecs_get_path(world, slot_of); + ecs_throw(ECS_INVALID_OPERATION, "prefab '%s' has unnamed " + "slot (slots must be named)", slot_of_str); + ecs_os_free(slot_of_str); + return; + } + + /* The 'slot' variable is currently pointing to a child (or + * grandchild) of the current base. Find the original slot by + * looking it up under the prefab it was registered. */ + if (depth == 0) { + /* If the current instance is an instance of slot_of, just + * lookup the slot by name, which is faster than having to + * create a relative path. */ + slot = ecs_lookup_child(world, slot_of, name); + } else { + /* If the slot is more than one level away from the slot_of + * parent, use a relative path to find the slot */ + char *path = ecs_get_path_w_sep(world, parent, child, ".", + NULL); + slot = ecs_lookup_path_w_sep(world, slot_of, path, ".", + NULL, false); + ecs_os_free(path); + } + + if (slot == 0) { + char *slot_of_str = ecs_get_path(world, slot_of); + char *slot_str = ecs_get_path(world, slot); + ecs_throw(ECS_INVALID_OPERATION, + "'%s' is not in hierarchy for slot '%s'", + slot_of_str, slot_str); + ecs_os_free(slot_of_str); + ecs_os_free(slot_str); + } + + ecs_add_pair(world, parent, slot, child); + break; + } + + depth ++; + } while ((parent = ecs_get_target(world, parent, EcsChildOf, 0))); + + if (parent == 0) { + char *slot_of_str = ecs_get_path(world, slot_of); + char *slot_str = ecs_get_path(world, slot); + ecs_throw(ECS_INVALID_OPERATION, + "'%s' is not in hierarchy for slot '%s'", + slot_of_str, slot_str); + ecs_os_free(slot_of_str); + ecs_os_free(slot_str); + } + } + error: return; } -void ecs_remove_id( +static +ecs_table_t* flecs_find_table_add( ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) + ecs_table_t *table, + ecs_id_t id, + ecs_table_diff_builder_t *diff) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id) || ecs_id_is_wildcard(id), - ECS_INVALID_PARAMETER, NULL); - flecs_remove_id(world, entity, id); + ecs_table_diff_t temp_diff = ECS_TABLE_DIFF_INIT; + table = flecs_table_traverse_add(world, table, &id, &temp_diff); + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_table_diff_build_append_table(world, diff, &temp_diff); + return table; error: - return; + return NULL; } -void ecs_override_id( +static +ecs_table_t* flecs_find_table_remove( ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) + ecs_table_t *table, + ecs_id_t id, + ecs_table_diff_builder_t *diff) { - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - ecs_add_id(world, entity, ECS_OVERRIDE | id); + ecs_table_diff_t temp_diff = ECS_TABLE_DIFF_INIT; + table = flecs_table_traverse_remove(world, table, &id, &temp_diff); + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_table_diff_build_append_table(world, diff, &temp_diff); + return table; error: - return; + return NULL; } -ecs_entity_t ecs_clone( - ecs_world_t *world, - ecs_entity_t dst, - ecs_entity_t src, - bool copy_value) +static +int32_t flecs_child_type_insert( + ecs_type_t *type, + void **component_data, + ecs_id_t id) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(src != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, src), ECS_INVALID_PARAMETER, NULL); - ecs_check(!dst || !ecs_get_table(world, dst), ECS_INVALID_PARAMETER, NULL); + int32_t i, count = type->count; + for (i = 0; i < count; i ++) { + ecs_id_t cur = type->array[i]; + if (cur == id) { + /* Id is already part of type */ + return -1; + } - ecs_stage_t *stage = flecs_stage_from_world(&world); - if (!dst) { - dst = ecs_new_id(world); + if (cur > id) { + /* A larger id was found so id can't be part of the type. */ + break; + } } - if (flecs_defer_clone(stage, dst, src, copy_value)) { - return dst; + /* Assumes that the array has enough memory to store the new element. */ + int32_t to_move = type->count - i; + if (to_move) { + ecs_os_memmove(&type->array[i + 1], + &type->array[i], to_move * ECS_SIZEOF(ecs_id_t)); + ecs_os_memmove(&component_data[i + 1], + &component_data[i], to_move * ECS_SIZEOF(void*)); } - ecs_record_t *src_r = flecs_entities_get(world, src); - ecs_assert(src_r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_t *src_table = src_r->table; - if (!src_table) { - goto done; - } + component_data[i] = NULL; + type->array[i] = id; + type->count ++; - ecs_table_t *dst_table = src_table; - if (src_table->flags & EcsTableHasName) { - dst_table = ecs_table_remove_id(world, src_table, - ecs_pair_t(EcsIdentifier, EcsName)); + return i; +} + +static +void flecs_instantiate_children( + ecs_world_t *world, + ecs_entity_t base, + ecs_table_t *table, + int32_t row, + int32_t count, + ecs_table_t *child_table, + const ecs_instantiate_ctx_t *ctx) +{ + if (!ecs_table_count(child_table)) { + return; } - ecs_type_t dst_type = dst_table->type; - ecs_table_diff_t diff = { .added = dst_type }; - ecs_record_t *dst_r = flecs_entities_get(world, dst); - flecs_new_entity(world, dst, dst_r, dst_table, &diff, true, true); - int32_t row = ECS_RECORD_TO_ROW(dst_r->row); + ecs_type_t type = child_table->type; + ecs_data_t *child_data = &child_table->data; - if (copy_value) { - flecs_table_move(world, dst, src, dst_table, - row, src_table, ECS_RECORD_TO_ROW(src_r->row), true); - int32_t i, count = dst_table->column_count; - for (i = 0; i < count; i ++) { - ecs_type_t type = { - .array = &dst_table->data.columns[i].id, - .count = 1 - }; - flecs_notify_on_set(world, dst_table, row, 1, &type, true); - } - } + ecs_entity_t slot_of = 0; + ecs_entity_t *ids = type.array; + int32_t type_count = type.count; -done: - flecs_defer_end(world, stage); - return dst; -error: - return 0; -} + /* Instantiate child table for each instance */ -const void* ecs_get_id( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(flecs_stage_from_readonly_world(world)->async == false, - ECS_INVALID_PARAMETER, NULL); + /* Create component array for creating the table */ + ecs_table_diff_t diff = { .added = {0}}; + diff.added.array = ecs_os_alloca_n(ecs_entity_t, type_count + 1); + void **component_data = ecs_os_alloca_n(void*, type_count + 1); - world = ecs_get_world(world); + /* Copy in component identifiers. Find the base index in the component + * array, since we'll need this to replace the base with the instance id */ + int j, i, childof_base_index = -1; + for (i = 0; i < type_count; i ++) { + ecs_id_t id = ids[i]; - ecs_record_t *r = flecs_entities_get(world, entity); - ecs_assert(r != NULL, ECS_INVALID_PARAMETER, NULL); + /* If id has DontInherit flag don't inherit it, except for the name + * and ChildOf pairs. The name is preserved so applications can lookup + * the instantiated children by name. The ChildOf pair is replaced later + * with the instance parent. */ + if ((id != ecs_pair(ecs_id(EcsIdentifier), EcsName)) && + ECS_PAIR_FIRST(id) != EcsChildOf) + { + ecs_table_record_t *tr = &child_table->_->records[i]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + } - ecs_table_t *table = r->table; - if (!table) { - return NULL; - } + /* If child is a slot, keep track of which parent to add it to, but + * don't add slot relationship to child of instance. If this is a child + * of a prefab, keep the SlotOf relationship intact. */ + if (!(table->flags & EcsTableIsPrefab)) { + if (ECS_IS_PAIR(id) && ECS_PAIR_FIRST(id) == EcsSlotOf) { + ecs_assert(slot_of == 0, ECS_INTERNAL_ERROR, NULL); + slot_of = ecs_pair_second(world, id); + continue; + } + } - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - return NULL; + /* Keep track of the element that creates the ChildOf relationship with + * the prefab parent. We need to replace this element to make sure the + * created children point to the instance and not the prefab */ + if (ECS_HAS_RELATION(id, EcsChildOf) && + (ECS_PAIR_SECOND(id) == (uint32_t)base)) { + childof_base_index = diff.added.count; + } + + /* If this is a pure override, make sure we have a concrete version of the + * component. This relies on the fact that overrides always come after + * concrete components in the table type so we can check the components + * that have already been added to the child table type. */ + if (ECS_HAS_ID_FLAG(id, AUTO_OVERRIDE)) { + ecs_id_t concreteId = id & ~ECS_AUTO_OVERRIDE; + flecs_child_type_insert(&diff.added, component_data, concreteId); + continue; + } + + int32_t storage_index = ecs_table_type_to_column_index(child_table, i); + if (storage_index != -1) { + component_data[diff.added.count] = + child_data->columns[storage_index].data; + } else { + component_data[diff.added.count] = NULL; + } + + diff.added.array[diff.added.count] = id; + diff.added.count ++; + diff.added_flags |= flecs_id_flags_get(world, id); } - const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (!tr) { - return flecs_get_base_component(world, table, id, idr, 0); - } else { - ecs_check(tr->column != -1, ECS_NOT_A_COMPONENT, NULL); + /* Table must contain children of base */ + ecs_assert(childof_base_index != -1, ECS_INTERNAL_ERROR, NULL); + + /* If children are added to a prefab, make sure they are prefabs too */ + if (table->flags & EcsTableIsPrefab) { + if (flecs_child_type_insert( + &diff.added, component_data, EcsPrefab) != -1) + { + childof_base_index ++; + } } - int32_t row = ECS_RECORD_TO_ROW(r->row); - return flecs_get_component_w_index(table, tr->column, row).ptr; -error: - return NULL; -} + /* Instantiate the prefab child table for each new instance */ + const ecs_entity_t *instances = ecs_table_entities(table); + int32_t child_count = ecs_table_count(child_table); + ecs_entity_t *child_ids = flecs_walloc_n(world, ecs_entity_t, child_count); -void* ecs_get_mut_id( - ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + for (i = row; i < count + row; i ++) { + ecs_entity_t instance = instances[i]; + ecs_table_t *i_table = NULL; + + /* Replace ChildOf element in the component array with instance id */ + diff.added.array[childof_base_index] = ecs_pair(EcsChildOf, instance); - ecs_stage_t *stage = flecs_stage_from_world(&world); - if (flecs_defer_cmd(stage)) { - return flecs_defer_set(world, stage, EcsCmdMut, entity, id, 0, NULL); - } + /* Find or create table */ + i_table = flecs_table_find_or_create(world, &diff.added); - 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); + ecs_assert(i_table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(i_table->type.count == diff.added.count, + ECS_INTERNAL_ERROR, NULL); - flecs_defer_end(world, stage); - return result; -error: - return NULL; -} + /* The instance is trying to instantiate from a base that is also + * its parent. This would cause the hierarchy to instantiate itself + * which would cause infinite recursion. */ + const ecs_entity_t *children = ecs_table_entities(child_table); -void* ecs_get_mut_modified_id( - ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); +#ifdef FLECS_DEBUG + for (j = 0; j < child_count; j ++) { + ecs_entity_t child = children[j]; + ecs_check(child != instance, ECS_INVALID_PARAMETER, + "cycle detected in IsA relationship"); + } +#else + /* Bit of boilerplate to ensure that we don't get warnings about the + * error label not being used. */ + ecs_check(true, ECS_INVALID_OPERATION, NULL); +#endif - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_check(flecs_defer_cmd(stage), ECS_INVALID_PARAMETER, NULL); + /* Attempt to reserve ids for children that have the same offset from + * the instance as from the base prefab. This ensures stable ids for + * instance children, even across networked applications. */ + ecs_instantiate_ctx_t ctx_cur = {base, instance}; + if (ctx) { + ctx_cur = *ctx; + } - return flecs_defer_set(world, stage, EcsCmdSet, entity, id, 0, NULL); -error: - return NULL; -} + for (j = 0; j < child_count; j ++) { + if ((uint32_t)children[j] < (uint32_t)ctx_cur.root_prefab) { + /* Child id is smaller than root prefab id, can't use offset */ + child_ids[j] = ecs_new(world); + continue; + } -static -ecs_record_t* flecs_access_begin( - ecs_world_t *stage, - ecs_entity_t entity, - bool write) -{ - ecs_check(ecs_os_has_threading(), ECS_MISSING_OS_API, NULL); + /* Get prefab offset, ignore lifecycle generation count */ + ecs_entity_t prefab_offset = + (uint32_t)children[j] - (uint32_t)ctx_cur.root_prefab; + ecs_assert(prefab_offset != 0, ECS_INTERNAL_ERROR, NULL); - const ecs_world_t *world = ecs_get_world(stage); - ecs_record_t *r = flecs_entities_get(world, entity); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + /* First check if any entity with the desired id exists */ + ecs_entity_t instance_child = (uint32_t)ctx_cur.root_instance + prefab_offset; + ecs_entity_t alive_id = flecs_entities_get_alive(world, instance_child); + if (alive_id && flecs_entities_is_alive(world, alive_id)) { + /* Alive entity with requested id exists, can't use offset id */ + child_ids[j] = ecs_new(world); + continue; + } - ecs_table_t *table; - if (!(table = r->table)) { - return NULL; - } + /* Id is not in use. Make it alive & match the generation of the instance. */ + instance_child = ctx_cur.root_instance + prefab_offset; + flecs_entities_make_alive(world, instance_child); + flecs_entities_ensure(world, instance_child); + ecs_assert(ecs_is_alive(world, instance_child), ECS_INTERNAL_ERROR, NULL); + child_ids[j] = instance_child; + } - int32_t count = ecs_os_ainc(&table->_->lock); - (void)count; - if (write) { - ecs_check(count == 1, ECS_ACCESS_VIOLATION, NULL); - } + /* Create children */ + int32_t child_row; + const ecs_entity_t *i_children = flecs_bulk_new(world, i_table, child_ids, + &diff.added, child_count, component_data, false, &child_row, &diff); - return r; -error: - return NULL; -} + /* If children are slots, add slot relationships to parent */ + if (slot_of) { + for (j = 0; j < child_count; j ++) { + ecs_entity_t child = children[j]; + ecs_entity_t i_child = i_children[j]; + flecs_instantiate_slot(world, base, instance, slot_of, + child, i_child); + } + } -static -void flecs_access_end( - const ecs_record_t *r, - bool write) -{ - ecs_check(ecs_os_has_threading(), ECS_MISSING_OS_API, NULL); - ecs_check(r != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(r->table != NULL, ECS_INVALID_PARAMETER, NULL); - int32_t count = ecs_os_adec(&r->table->_->lock); - (void)count; - if (write) { - ecs_check(count == 0, ECS_ACCESS_VIOLATION, NULL); + /* If prefab child table has children itself, recursively instantiate */ + for (j = 0; j < child_count; j ++) { + ecs_entity_t child = children[j]; + flecs_instantiate(world, child, i_table, child_row + j, 1, &ctx_cur); + } } - ecs_check(count >= 0, ECS_ACCESS_VIOLATION, NULL); + flecs_wfree_n(world, ecs_entity_t, child_count, child_ids); error: - return; + return; } -ecs_record_t* ecs_write_begin( +void flecs_instantiate( ecs_world_t *world, - ecs_entity_t entity) + ecs_entity_t base, + ecs_table_t *table, + int32_t row, + int32_t count, + const ecs_instantiate_ctx_t *ctx) { - return flecs_access_begin(world, entity, true); -} + ecs_record_t *record = flecs_entities_get_any(world, base); + ecs_table_t *base_table = record->table; + if (!base_table) { + return; + } -void ecs_write_end( - ecs_record_t *r) -{ - flecs_access_end(r, true); + /* If prefab has union relationships, also set them on instance */ + if (base_table->flags & EcsTableHasUnion) { + const ecs_entity_t *entities = ecs_table_entities(table); + ecs_id_record_t *union_idr = flecs_id_record_get(world, + ecs_pair(EcsWildcard, EcsUnion)); + ecs_assert(union_idr != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_table_record_t *tr = flecs_id_record_get_table( + union_idr, base_table); + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t i = 0, j, union_count = 0; + do { + ecs_id_t id = base_table->type.array[i]; + if (ECS_PAIR_SECOND(id) == EcsUnion) { + ecs_entity_t rel = ECS_PAIR_FIRST(id); + ecs_entity_t tgt = ecs_get_target(world, base, rel, 0); + ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); + + for (j = row; j < (row + count); j ++) { + ecs_add_pair(world, entities[j], rel, tgt); + } + + union_count ++; + } + + i ++; + } while (union_count < tr->count); + } + + if (!(base_table->flags & EcsTableIsPrefab)) { + /* Don't instantiate children from base entities that aren't prefabs */ + return; + } + + ecs_id_record_t *idr = flecs_id_record_get(world, ecs_childof(base)); + ecs_table_cache_iter_t it; + if (idr && flecs_table_cache_all_iter((ecs_table_cache_t*)idr, &it)) { + ecs_os_perf_trace_push("flecs.instantiate"); + const ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + flecs_instantiate_children( + world, base, table, row, count, tr->hdr.table, ctx); + } + ecs_os_perf_trace_pop("flecs.instantiate"); + } } -const ecs_record_t* ecs_read_begin( +static +void flecs_sparse_on_add( ecs_world_t *world, - ecs_entity_t entity) + ecs_table_t *table, + int32_t row, + int32_t count, + const ecs_type_t *added, + bool construct) { - return flecs_access_begin(world, entity, false); -} + int32_t i, j; + for (i = 0; i < added->count; i ++) { + ecs_id_t id = added->array[i]; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr && idr->flags & EcsIdIsSparse) { + const ecs_type_info_t *ti = idr->type_info; + ecs_xtor_t ctor = ti->hooks.ctor; + ecs_iter_action_t on_add = ti->hooks.on_add; + const ecs_entity_t *entities = ecs_table_entities(table); + for (j = 0; j < count; j ++) { + ecs_entity_t e = entities[row + j]; + void *ptr = flecs_sparse_ensure(idr->sparse, 0, e); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + if (construct && ctor) { + ctor(ptr, 1, ti); + } -void ecs_read_end( - const ecs_record_t *r) -{ - flecs_access_end(r, false); + if (on_add) { + const ecs_table_record_t *tr = + flecs_id_record_get_table(idr, table); + flecs_invoke_hook(world, table, tr, count, row, + &entities[row + j],id, ti, EcsOnAdd, on_add); + } + } + } + } } -ecs_entity_t ecs_record_get_entity( - const ecs_record_t *record) +static +void flecs_sparse_on_remove( + ecs_world_t *world, + ecs_table_t *table, + int32_t row, + int32_t count, + const ecs_type_t *removed) { - ecs_check(record != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_table_t *table = record->table; - if (!table) { - return 0; + int32_t i, j; + for (i = 0; i < removed->count; i ++) { + ecs_id_t id = removed->array[i]; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr && idr->flags & EcsIdIsSparse) { + const ecs_type_info_t *ti = idr->type_info; + const ecs_table_record_t *tr = + flecs_id_record_get_table(idr, table); + ecs_xtor_t dtor = ti->hooks.dtor; + ecs_iter_action_t on_remove = ti->hooks.on_remove; + const ecs_entity_t *entities = ecs_table_entities(table); + for (j = 0; j < count; j ++) { + ecs_entity_t e = entities[row + j]; + if (on_remove) { + flecs_invoke_hook(world, table, tr, count, row, + &entities[row + j], id, ti, EcsOnRemove, on_remove); + } + void *ptr = flecs_sparse_remove_fast(idr->sparse, 0, e); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + if (dtor) { + dtor(ptr, 1, ti); + } + } + } } - - return ecs_vec_get_t(&table->data.entities, ecs_entity_t, - ECS_RECORD_TO_ROW(record->row))[0]; -error: - return 0; } -const void* ecs_record_get_id( - ecs_world_t *stage, - const ecs_record_t *r, - ecs_id_t id) +static +void flecs_union_on_add( + ecs_world_t *world, + ecs_table_t *table, + int32_t row, + int32_t count, + const ecs_type_t *added) { - const ecs_world_t *world = ecs_get_world(stage); - return flecs_get_component(world, r->table, ECS_RECORD_TO_ROW(r->row), id); + int32_t i, j; + for (i = 0; i < added->count; i ++) { + ecs_id_t id = added->array[i]; + if (ECS_IS_PAIR(id)) { + ecs_id_t wc = ecs_pair(ECS_PAIR_FIRST(id), EcsUnion); + ecs_id_record_t *idr = flecs_id_record_get(world, wc); + if (idr && idr->flags & EcsIdIsUnion) { + const ecs_entity_t *entities = ecs_table_entities(table); + for (j = 0; j < count; j ++) { + ecs_entity_t e = entities[row + j]; + flecs_switch_set( + idr->sparse, (uint32_t)e, ecs_pair_second(world, id)); + } + } + } + } } -bool ecs_record_has_id( - ecs_world_t *stage, - const ecs_record_t *r, - ecs_id_t id) +static +void flecs_union_on_remove( + ecs_world_t *world, + ecs_table_t *table, + int32_t row, + int32_t count, + const ecs_type_t *removed) { - const ecs_world_t *world = ecs_get_world(stage); - if (r->table) { - return ecs_table_has_id(world, r->table, id); + int32_t i, j; + for (i = 0; i < removed->count; i ++) { + ecs_id_t id = removed->array[i]; + if (ECS_IS_PAIR(id)) { + ecs_id_t wc = ecs_pair(ECS_PAIR_FIRST(id), EcsUnion); + ecs_id_record_t *idr = flecs_id_record_get(world, wc); + if (idr && idr->flags & EcsIdIsUnion) { + const ecs_entity_t *entities = ecs_table_entities(table); + for (j = 0; j < count; j ++) { + ecs_entity_t e = entities[row + j]; + flecs_switch_reset(idr->sparse, (uint32_t)e); + } + } + } } - return false; } -void* ecs_record_get_mut_id( - ecs_world_t *stage, - ecs_record_t *r, - ecs_id_t id) +static +void flecs_notify_on_add( + ecs_world_t *world, + ecs_table_t *table, + ecs_table_t *other_table, + int32_t row, + int32_t count, + const ecs_table_diff_t *diff, + ecs_flags32_t flags, + ecs_flags64_t set_mask, + bool construct, + bool sparse) { - const ecs_world_t *world = ecs_get_world(stage); - return flecs_get_component(world, r->table, ECS_RECORD_TO_ROW(r->row), id); -} + ecs_assert(diff != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_t *added = &diff->added; -ecs_ref_t ecs_ref_init_id( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) -{ - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - - world = ecs_get_world(world); + if (added->count) { + ecs_flags32_t diff_flags = + diff->added_flags|(table->flags & EcsTableHasTraversable); + if (!diff_flags) { + return; + } - ecs_record_t *record = flecs_entities_get(world, entity); - ecs_check(record != NULL, ECS_INVALID_PARAMETER, - "cannot create ref for empty entity"); + if (sparse && (diff_flags & EcsTableHasSparse)) { + flecs_sparse_on_add(world, table, row, count, added, construct); + } - ecs_ref_t result = { - .entity = entity, - .id = id, - .record = record - }; + if (diff_flags & EcsTableHasUnion) { + flecs_union_on_add(world, table, row, count, added); + } - ecs_table_t *table = record->table; - if (table) { - result.tr = flecs_table_record_get(world, table, id); + if (diff_flags & (EcsTableHasOnAdd|EcsTableHasTraversable)) { + flecs_emit(world, world, set_mask, &(ecs_event_desc_t){ + .event = EcsOnAdd, + .ids = added, + .table = table, + .other_table = other_table, + .offset = row, + .count = count, + .observable = world, + .flags = flags + }); + } } - - return result; -error: - return (ecs_ref_t){0}; } -void ecs_ref_update( - const ecs_world_t *world, - ecs_ref_t *ref) +void flecs_notify_on_remove( + ecs_world_t *world, + ecs_table_t *table, + ecs_table_t *other_table, + int32_t row, + int32_t count, + const ecs_table_diff_t *diff) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref->entity != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref->id != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref->record != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_record_t *r = ref->record; - ecs_table_t *table = r->table; - if (!table) { - return; - } + ecs_assert(diff != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_t *removed = &diff->removed; + ecs_assert(count != 0, ECS_INTERNAL_ERROR, NULL); - ecs_table_record_t *tr = ref->tr; - if (!tr || tr->hdr.table != table) { - tr = ref->tr = flecs_table_record_get(world, table, ref->id); - if (!tr) { + if (removed->count) { + ecs_flags32_t diff_flags = + diff->removed_flags|(table->flags & EcsTableHasTraversable); + if (!diff_flags) { return; } - ecs_assert(tr->hdr.table == r->table, ECS_INTERNAL_ERROR, NULL); + if (diff_flags & EcsTableHasUnion) { + flecs_union_on_remove(world, table, row, count, removed); + } + + if (diff_flags & (EcsTableHasOnRemove|EcsTableHasTraversable)) { + flecs_emit(world, world, 0, &(ecs_event_desc_t) { + .event = EcsOnRemove, + .ids = removed, + .table = table, + .other_table = other_table, + .offset = row, + .count = count, + .observable = world + }); + } + + if (diff_flags & EcsTableHasSparse) { + flecs_sparse_on_remove(world, table, row, count, removed); + } } -error: - return; } -void* ecs_ref_get_id( - const ecs_world_t *world, - ecs_ref_t *ref, - ecs_id_t id) +static +void flecs_update_name_index( + ecs_world_t *world, + ecs_table_t *src, + ecs_table_t *dst, + int32_t offset, + int32_t count) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref->entity != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref->id != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref->record != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(id == ref->id, ECS_INVALID_PARAMETER, NULL); + ecs_assert(src != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(dst != NULL, ECS_INTERNAL_ERROR, NULL); + if (!(dst->flags & EcsTableHasName)) { + /* If destination table doesn't have a name, we don't need to update the + * name index. Even if the src table had a name, the on_remove hook for + * EcsIdentifier will remove the entity from the index. */ + return; + } - ecs_record_t *r = ref->record; - ecs_table_t *table = r->table; - if (!table) { - return NULL; + ecs_hashmap_t *src_index = src->_->name_index; + ecs_hashmap_t *dst_index = dst->_->name_index; + if ((src_index == dst_index) || (!src_index && !dst_index)) { + /* If the name index didn't change, the entity still has the same parent + * so nothing needs to be done. */ + return; } - int32_t row = ECS_RECORD_TO_ROW(r->row); - ecs_check(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); + EcsIdentifier *names = ecs_table_get_pair(world, + dst, EcsIdentifier, EcsName, offset); + ecs_assert(names != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_record_t *tr = ref->tr; - if (!tr || tr->hdr.table != table) { - tr = ref->tr = flecs_table_record_get(world, table, id); - if (!tr) { - return NULL; + int32_t i; + const ecs_entity_t *entities = &ecs_table_entities(dst)[offset]; + for (i = 0; i < count; i ++) { + ecs_entity_t e = entities[i]; + EcsIdentifier *name = &names[i]; + + uint64_t index_hash = name->index_hash; + if (index_hash) { + flecs_name_index_remove(src_index, e, index_hash); } + const char *name_str = name->value; + if (name_str) { + ecs_assert(name->hash != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(tr->hdr.table == r->table, ECS_INTERNAL_ERROR, NULL); + flecs_name_index_ensure( + dst_index, e, name_str, name->length, name->hash); + name->index = dst_index; + } } - - int32_t column = tr->column; - ecs_assert(column != -1, ECS_INTERNAL_ERROR, NULL); - return flecs_get_component_w_index(table, column, row).ptr; -error: - return NULL; } -void* ecs_emplace_id( +static +ecs_record_t* flecs_new_entity( ecs_world_t *world, ecs_entity_t entity, - ecs_id_t id) + ecs_record_t *record, + ecs_table_t *table, + ecs_table_diff_t *diff, + bool ctor, + ecs_flags32_t evt_flags) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - ecs_check(!ecs_has_id(world, entity, id), ECS_INVALID_PARAMETER, - "cannot emplace a component the entity already has"); - - ecs_stage_t *stage = flecs_stage_from_world(&world); - - if (flecs_defer_cmd(stage)) { - return flecs_defer_set(world, stage, EcsCmdEmplace, entity, id, 0, NULL); - } - - ecs_record_t *r = flecs_entities_get(world, entity); - flecs_add_id_w_record(world, entity, r, id, false /* Add without ctor */); - flecs_defer_end(world, stage); + ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t row = flecs_table_append(world, table, entity, ctor, true); + record->table = table; + record->row = ECS_ROW_TO_RECORD(row, record->row & ECS_ROW_FLAGS_MASK); - void *ptr = flecs_get_component(world, r->table, ECS_RECORD_TO_ROW(r->row), id); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ecs_table_count(table) > row, ECS_INTERNAL_ERROR, NULL); + flecs_notify_on_add( + world, table, NULL, row, 1, diff, evt_flags, 0, ctor, true); + ecs_assert(table == record->table, ECS_INTERNAL_ERROR, NULL); - return ptr; -error: - return NULL; + return record; } static -void flecs_modified_id_if( +void flecs_move_entity( ecs_world_t *world, ecs_entity_t entity, - ecs_id_t id, - bool owned) + ecs_record_t *record, + ecs_table_t *dst_table, + ecs_table_diff_t *diff, + bool ctor, + ecs_flags32_t evt_flags) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + ecs_table_t *src_table = record->table; + int32_t src_row = ECS_RECORD_TO_ROW(record->row); - ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(src_table != dst_table, ECS_INTERNAL_ERROR, NULL); + ecs_assert(src_table->type.count > 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(src_row >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_table_count(src_table) > src_row, ECS_INTERNAL_ERROR, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(record == flecs_entities_get(world, entity), + ECS_INTERNAL_ERROR, NULL); + ecs_assert(record->table == src_table, ECS_INTERNAL_ERROR, NULL); - if (flecs_defer_modified(stage, entity, id)) { - return; - } + /* Append new row to destination table */ + int32_t dst_row = flecs_table_append(world, dst_table, entity, + false, false); - ecs_record_t *r = flecs_entities_get(world, entity); - ecs_table_t *table = r->table; - if (!flecs_table_record_get(world, table, id)) { - flecs_defer_end(world, stage); - return; - } + /* Invoke remove actions for removed components */ + flecs_notify_on_remove(world, src_table, dst_table, src_row, 1, diff); - ecs_type_t ids = { .array = &id, .count = 1 }; - flecs_notify_on_set(world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, owned); + /* Copy entity & components from src_table to dst_table */ + flecs_table_move(world, entity, entity, dst_table, dst_row, + src_table, src_row, ctor); + ecs_assert(record->table == src_table, ECS_INTERNAL_ERROR, NULL); - flecs_table_mark_dirty(world, table, id); - flecs_defer_end(world, stage); + /* Update entity index & delete old data after running remove actions */ + record->table = dst_table; + record->row = ECS_ROW_TO_RECORD(dst_row, record->row & ECS_ROW_FLAGS_MASK); + + flecs_table_delete(world, src_table, src_row, false); + flecs_notify_on_add(world, dst_table, src_table, dst_row, 1, diff, + evt_flags, 0, ctor, true); + + flecs_update_name_index(world, src_table, dst_table, dst_row, 1); + + ecs_assert(record->table == dst_table, ECS_INTERNAL_ERROR, NULL); error: return; } -void ecs_modified_id( +static +void flecs_delete_entity( ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + ecs_record_t *record, + ecs_table_diff_t *diff) +{ + ecs_table_t *table = record->table; + int32_t row = ECS_RECORD_TO_ROW(record->row); - ecs_stage_t *stage = flecs_stage_from_world(&world); + /* Invoke remove actions before deleting */ + flecs_notify_on_remove(world, table, NULL, row, 1, diff); + flecs_table_delete(world, table, row, true); +} - if (flecs_defer_modified(stage, entity, id)) { +/* Updating component monitors is a relatively expensive operation that only + * happens for entities that are monitored. The approach balances the amount of + * processing between the operation on the entity vs the amount of work that + * needs to be done to rematch queries, as a simple brute force approach does + * not scale when there are many tables / queries. Therefore we need to do a bit + * of bookkeeping that is more intelligent than simply flipping a flag */ +static +void flecs_update_component_monitor_w_array( + ecs_world_t *world, + ecs_type_t *ids) +{ + if (!ids) { return; } - /* If the entity does not have the component, calling ecs_modified is - * invalid. The assert needs to happen after the defer statement, as the - * entity may not have the component when this function is called while - * operations are being deferred. */ - ecs_check(ecs_has_id(world, entity, id), ECS_INVALID_PARAMETER, NULL); + int i; + for (i = 0; i < ids->count; i ++) { + ecs_entity_t id = ids->array[i]; + if (ECS_HAS_ID_FLAG(id, PAIR)) { + flecs_monitor_mark_dirty(world, + ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard)); + } - ecs_record_t *r = flecs_entities_get(world, entity); - ecs_table_t *table = r->table; - ecs_type_t ids = { .array = &id, .count = 1 }; - flecs_notify_on_set(world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, true); + flecs_monitor_mark_dirty(world, id); + } +} - flecs_table_mark_dirty(world, table, id); - flecs_defer_end(world, stage); -error: - return; +static +void flecs_update_component_monitors( + ecs_world_t *world, + ecs_type_t *added, + ecs_type_t *removed) +{ + flecs_update_component_monitor_w_array(world, added); + flecs_update_component_monitor_w_array(world, removed); } static -void flecs_copy_ptr_w_id( +void flecs_commit( ecs_world_t *world, - ecs_stage_t *stage, ecs_entity_t entity, - ecs_id_t id, - size_t size, - void *ptr) + ecs_record_t *record, + ecs_table_t *dst_table, + ecs_table_diff_t *diff, + bool construct, + ecs_flags32_t evt_flags) { - if (flecs_defer_cmd(stage)) { - flecs_defer_set(world, stage, EcsCmdSet, entity, id, - flecs_utosize(size), ptr); + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); + flecs_journal_begin(world, EcsJournalMove, entity, + &diff->added, &diff->removed); + + ecs_table_t *src_table = NULL; + int is_trav = 0; + if (record) { + src_table = record->table; + is_trav = (record->row & EcsEntityIsTraversable) != 0; + } + + if (src_table == dst_table) { + /* If source and destination table are the same no action is needed * + * However, if a component was added in the process of traversing a + * table, this suggests that a union relationship could have changed. */ + if (src_table && src_table->flags & EcsTableHasUnion) { + diff->added_flags |= EcsIdIsUnion; + flecs_notify_on_add(world, src_table, src_table, + ECS_RECORD_TO_ROW(record->row), 1, diff, evt_flags, 0, + construct, true); + } + flecs_journal_end(); return; } - ecs_record_t *r = flecs_entities_get(world, entity); - flecs_component_ptr_t dst = flecs_get_mut(world, entity, id, r); - const ecs_type_info_t *ti = dst.ti; - ecs_check(dst.ptr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_os_perf_trace_push("flecs.commit"); - if (ptr) { - ecs_copy_t copy = ti->hooks.copy; - if (copy) { - copy(dst.ptr, ptr, 1, ti); + if (src_table) { + ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_table_traversable_add(dst_table, is_trav); + + if (dst_table->type.count) { + flecs_move_entity(world, entity, record, dst_table, diff, + construct, evt_flags); } else { - ecs_os_memcpy(dst.ptr, ptr, flecs_utosize(size)); + flecs_delete_entity(world, record, diff); + record->table = NULL; + } + + flecs_table_traversable_add(src_table, -is_trav); + } else { + flecs_table_traversable_add(dst_table, is_trav); + if (dst_table->type.count) { + flecs_new_entity(world, entity, record, dst_table, diff, + construct, evt_flags); } - } else { - ecs_os_memset(dst.ptr, 0, size); } - flecs_table_mark_dirty(world, r->table, id); + /* If the entity is being watched, it is being monitored for changes and + * requires rematching systems when components are added or removed. This + * ensures that systems that rely on components from containers or prefabs + * update the matched tables when the application adds or removes a + * component from, for example, a container. */ + if (is_trav) { + flecs_update_component_monitors(world, &diff->added, &diff->removed); + } - ecs_table_t *table = r->table; - if (table->flags & EcsTableHasOnSet || ti->hooks.on_set) { - ecs_type_t ids = { .array = &id, .count = 1 }; - flecs_notify_on_set( - world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, true); + if ((!src_table || !src_table->type.count) && world->range_check_enabled) { + ecs_check(!world->info.max_id || entity <= world->info.max_id, + ECS_OUT_OF_RANGE, 0); + ecs_check(entity >= world->info.min_id, + ECS_OUT_OF_RANGE, 0); } - flecs_defer_end(world, stage); + ecs_os_perf_trace_pop("flecs.commit"); + error: + flecs_journal_end(); return; } static -void flecs_move_ptr_w_id( +const ecs_entity_t* flecs_bulk_new( ecs_world_t *world, - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_id_t id, - size_t size, - void *ptr, - ecs_cmd_kind_t cmd_kind) + ecs_table_t *table, + const ecs_entity_t *entities, + ecs_type_t *component_ids, + int32_t count, + void **component_data, + bool is_move, + int32_t *row_out, + ecs_table_diff_t *diff) { - if (flecs_defer_cmd(stage)) { - flecs_defer_set(world, stage, cmd_kind, entity, id, - flecs_utosize(size), ptr); - return; + int32_t sparse_count = 0; + if (!entities) { + sparse_count = flecs_entities_count(world); + entities = flecs_entities_new_ids(world, count); } - ecs_record_t *r = flecs_entities_get(world, entity); - flecs_component_ptr_t dst = flecs_get_mut(world, entity, id, r); - ecs_check(dst.ptr != NULL, ECS_INVALID_PARAMETER, NULL); + if (!table) { + return entities; + } - const ecs_type_info_t *ti = dst.ti; - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_move_t move; - if (cmd_kind != EcsCmdEmplace) { - /* ctor will have happened by get_mut */ - move = ti->hooks.move_dtor; - } else { - move = ti->hooks.ctor_move_dtor; + ecs_type_t type = table->type; + if (!type.count) { + return entities; } - if (move) { - move(dst.ptr, ptr, 1, ti); - } else { - ecs_os_memcpy(dst.ptr, ptr, flecs_utosize(size)); + + ecs_type_t component_array = { 0 }; + if (!component_ids) { + component_ids = &component_array; + component_array.array = type.array; + component_array.count = type.count; } - flecs_table_mark_dirty(world, r->table, id); + int32_t row = flecs_table_appendn(world, table, count, entities); - if (cmd_kind == EcsCmdSet) { - ecs_table_t *table = r->table; - if (table->flags & EcsTableHasOnSet || ti->hooks.on_set) { - ecs_type_t ids = { .array = &id, .count = 1 }; - flecs_notify_on_set( - world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, true); - } + /* Update entity index. */ + int i; + for (i = 0; i < count; i ++) { + ecs_record_t *r = flecs_entities_get(world, entities[i]); + r->table = table; + r->row = ECS_ROW_TO_RECORD(row + i, 0); } - flecs_defer_end(world, stage); -error: - return; -} + flecs_defer_begin(world, world->stages[0]); -ecs_entity_t ecs_set_id( - ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id, - size_t size, - const void *ptr) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!entity || ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + flecs_notify_on_add(world, table, NULL, row, count, diff, + (component_data == NULL) ? 0 : EcsEventNoOnSet, 0, true, true); - ecs_stage_t *stage = flecs_stage_from_world(&world); + if (component_data) { + int32_t c_i; + for (c_i = 0; c_i < component_ids->count; c_i ++) { + void *src_ptr = component_data[c_i]; + if (!src_ptr) { + continue; + } - if (!entity) { - entity = ecs_new_id(world); - ecs_entity_t scope = stage->scope; - if (scope) { - ecs_add_pair(world, entity, EcsChildOf, scope); + /* Find component in storage type */ + ecs_entity_t id = component_ids->array[c_i]; + const ecs_table_record_t *tr = flecs_table_record_get( + world, table, id); + ecs_assert(tr != NULL, ECS_INVALID_PARAMETER, + "id is not a component"); + ecs_assert(tr->column != -1, ECS_INVALID_PARAMETER, + "id is not a component"); + ecs_assert(tr->count == 1, ECS_INVALID_PARAMETER, + "ids cannot be wildcards"); + + int32_t index = tr->column; + ecs_column_t *column = &table->data.columns[index]; + ecs_type_info_t *ti = column->ti; + int32_t size = ti->size; + ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); + void *ptr = ECS_ELEM(column->data, size, row); + + ecs_copy_t copy; + ecs_move_t move; + if (is_move && (move = ti->hooks.move)) { + move(ptr, src_ptr, count, ti); + } else if (!is_move && (copy = ti->hooks.copy)) { + copy(ptr, src_ptr, count, ti); + } else { + ecs_os_memcpy(ptr, src_ptr, size * count); + } + }; + + int32_t j, storage_count = table->column_count; + for (j = 0; j < storage_count; j ++) { + ecs_id_t id = flecs_column_id(table, j); + ecs_type_t set_type = { + .array = &id, + .count = 1 + }; + + flecs_notify_on_set(world, table, row, count, &set_type, true); } } - /* Safe to cast away const: function won't modify if move arg is false */ - flecs_copy_ptr_w_id(world, stage, entity, id, size, - ECS_CONST_CAST(void*, ptr)); - return entity; -error: - return 0; + flecs_defer_end(world, world->stages[0]); + + if (row_out) { + *row_out = row; + } + + if (sparse_count) { + entities = flecs_entities_ids(world); + return &entities[sparse_count]; + } else { + return entities; + } } -void ecs_enable_id( +static +void flecs_add_id_w_record( ecs_world_t *world, ecs_entity_t entity, + ecs_record_t *record, ecs_id_t id, - bool enable) + bool construct) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_valid(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_table_t *src_table = record->table; + ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; + ecs_table_t *dst_table = flecs_table_traverse_add( + world, src_table, &id, &diff); + flecs_commit(world, entity, record, dst_table, &diff, construct, + EcsEventNoOnSet); /* No OnSet, this function is only called from + * functions that are about to set the component. */ +} - if (flecs_defer_enable(stage, entity, id, enable)) { +static +void flecs_add_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_stage_t *stage = flecs_stage_from_world(&world); + if (flecs_defer_add(stage, entity, id)) { return; - } else { - /* Operations invoked by enable/disable should not be deferred */ - stage->defer --; } ecs_record_t *r = flecs_entities_get(world, entity); - ecs_entity_t bs_id = id | ECS_TOGGLE; - - ecs_table_t *table = r->table; - int32_t index = -1; - if (table) { - index = ecs_table_get_type_index(world, table, bs_id); - } - - if (index == -1) { - ecs_add_id(world, entity, bs_id); - ecs_enable_id(world, entity, id, enable); - return; - } - - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - index -= table->_->bs_offset; - ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; + ecs_table_t *src_table = r->table; + ecs_table_t *dst_table = flecs_table_traverse_add( + world, src_table, &id, &diff); - /* Data cannot be NULl, since entity is stored in the table */ - ecs_bitset_t *bs = &table->_->bs_columns[index]; - ecs_assert(bs != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_commit(world, entity, r, dst_table, &diff, true, 0); - flecs_bitset_set(bs, ECS_RECORD_TO_ROW(r->row), enable); -error: - return; + flecs_defer_end(world, stage); } -bool ecs_is_enabled_id( - const ecs_world_t *world, +static +void flecs_remove_id( + ecs_world_t *world, ecs_entity_t entity, ecs_id_t id) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - - /* Make sure we're not working with a stage */ - world = ecs_get_world(world); + ecs_stage_t *stage = flecs_stage_from_world(&world); + if (flecs_defer_remove(stage, entity, id)) { + return; + } ecs_record_t *r = flecs_entities_get(world, entity); ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_t *table = r->table; - if (!table) { - return false; - } - - ecs_entity_t bs_id = id | ECS_TOGGLE; - int32_t index = ecs_table_get_type_index(world, table, bs_id); - if (index == -1) { - /* If table does not have TOGGLE column for component, component is - * always enabled, if the entity has it */ - return ecs_has_id(world, entity, id); - } + ecs_table_t *src_table = r->table; + ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; + ecs_table_t *dst_table = flecs_table_traverse_remove( + world, src_table, &id, &diff); - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - index -= table->_->bs_offset; - ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_bitset_t *bs = &table->_->bs_columns[index]; + flecs_commit(world, entity, r, dst_table, &diff, true, 0); - return flecs_bitset_get(bs, ECS_RECORD_TO_ROW(r->row)); -error: - return false; + flecs_defer_end(world, stage); } -bool ecs_has_id( - const ecs_world_t *world, +static +flecs_component_ptr_t flecs_ensure( + ecs_world_t *world, ecs_entity_t entity, - ecs_id_t id) + ecs_entity_t id, + ecs_record_t *r) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); - - /* Make sure we're not working with a stage */ - world = ecs_get_world(world); + flecs_component_ptr_t dst = {0}; - ecs_record_t *r = flecs_entities_get_any(world, entity); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_t *table = r->table; - if (!table) { - return false; - } + flecs_poly_assert(world, ecs_world_t); + ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(r != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check((id & ECS_COMPONENT_MASK) == id || + ECS_HAS_ID_FLAG(id, PAIR), ECS_INVALID_PARAMETER, + "invalid component id specified for ensure"); - ecs_id_record_t *idr = flecs_id_record_get(world, id); - int32_t column; - if (idr) { - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (tr) { - return true; + ecs_id_record_t *idr = NULL; + ecs_table_t *table; + if ((table = r->table)) { + if (id < FLECS_HI_COMPONENT_ID) { + int16_t column_index = table->component_map[id]; + if (column_index > 0) { + ecs_column_t *column = &table->data.columns[column_index - 1]; + ecs_type_info_t *ti = column->ti; + dst.ptr = ECS_ELEM(column->data, ti->size, + ECS_RECORD_TO_ROW(r->row)); + dst.ti = ti; + return dst; + } else if (column_index < 0) { + column_index = flecs_ito(int16_t, -column_index - 1); + const ecs_table_record_t *tr = &table->_->records[column_index]; + idr = (ecs_id_record_t*)tr->hdr.cache; + if (idr->flags & EcsIdIsSparse) { + dst.ptr = flecs_sparse_get_any(idr->sparse, 0, entity); + dst.ti = idr->type_info; + return dst; + } + } + } else { + idr = flecs_id_record_get(world, id); + dst = flecs_get_component_ptr(table, ECS_RECORD_TO_ROW(r->row), idr); + if (dst.ptr) { + return dst; + } } } - if (!(table->flags & (EcsTableHasUnion|EcsTableHasIsA))) { - return false; - } + /* If entity didn't have component yet, add it */ + flecs_add_id_w_record(world, entity, r, id, true); - ecs_table_record_t *tr; - column = ecs_search_relation(world, table, 0, id, - EcsIsA, 0, 0, 0, &tr); - if (column == -1) { - return false; - } + /* Flush commands so the pointer we're fetching is stable */ + flecs_defer_end(world, world->stages[0]); + flecs_defer_begin(world, world->stages[0]); - table = tr->hdr.table; - if ((table->flags & EcsTableHasUnion) && ECS_HAS_ID_FLAG(id, PAIR) && - ECS_PAIR_SECOND(id) != EcsWildcard) - { - if (ECS_PAIR_FIRST(table->type.array[column]) == EcsUnion) { - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_switch_t *sw = &table->_->sw_columns[ - column - table->_->sw_offset]; - int32_t row = ECS_RECORD_TO_ROW(r->row); - uint64_t value = flecs_switch_get(sw, row); - return value == ecs_pair_second(world, id); - } + if (!idr) { + idr = flecs_id_record_get(world, id); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); } - - return true; + + ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + dst = flecs_get_component_ptr(r->table, ECS_RECORD_TO_ROW(r->row), idr); error: - return false; + return dst; } -bool ecs_owns_id( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id) +void flecs_invoke_hook( + ecs_world_t *world, + ecs_table_t *table, + const ecs_table_record_t *tr, + int32_t count, + int32_t row, + const ecs_entity_t *entities, + ecs_id_t id, + const ecs_type_info_t *ti, + ecs_entity_t event, + ecs_iter_action_t hook) { - return (ecs_search(world, ecs_get_table(world, entity), id, 0) != -1); -} + int32_t defer = world->stages[0]->defer; + if (defer < 0) { + world->stages[0]->defer *= -1; + } -ecs_entity_t ecs_get_target( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t rel, - int32_t index) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(rel != 0, ECS_INVALID_PARAMETER, NULL); + ecs_iter_t it = { .field_count = 1}; + it.entities = entities; - world = ecs_get_world(world); + flecs_iter_init(world, &it, flecs_iter_cache_all); + it.world = world; + it.real_world = world; + it.table = table; + it.trs[0] = tr; + it.row_fields = !!(((ecs_id_record_t*)tr->hdr.cache)->flags & EcsIdIsSparse); + it.ref_fields = it.row_fields; + it.sizes = ECS_CONST_CAST(ecs_size_t*, &ti->size); + it.ids[0] = id; + it.event = event; + it.event_id = id; + it.ctx = ti->hooks.ctx; + it.callback_ctx = ti->hooks.binding_ctx; + it.count = count; + it.offset = row; + it.flags = EcsIterIsValid; - ecs_record_t *r = flecs_entities_get(world, entity); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_t *table = r->table; - if (!table) { - goto not_found; - } + hook(&it); + ecs_iter_fini(&it); - ecs_id_t wc = ecs_pair(rel, EcsWildcard); - ecs_id_record_t *idr = flecs_id_record_get(world, wc); - const ecs_table_record_t *tr = NULL; - if (idr) { - tr = flecs_id_record_get_table(idr, table); - } - if (!tr) { - if (table->flags & EcsTableHasUnion) { - wc = ecs_pair(EcsUnion, rel); - tr = flecs_table_record_get(world, table, wc); - if (tr) { - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_switch_t *sw = &table->_->sw_columns[ - tr->index - table->_->sw_offset]; - int32_t row = ECS_RECORD_TO_ROW(r->row); - return flecs_switch_get(sw, row); - + world->stages[0]->defer = defer; +} + +void flecs_notify_on_set( + ecs_world_t *world, + ecs_table_t *table, + int32_t row, + int32_t count, + ecs_type_t *ids, + bool owned) +{ + ecs_assert(ids != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_entity_t *entities = &ecs_table_entities(table)[row]; + ecs_assert(entities != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert((row + count) <= ecs_table_count(table), + ECS_INTERNAL_ERROR, NULL); + + if (owned) { + int i; + for (i = 0; i < ids->count; i ++) { + ecs_id_t id = ids->array[i]; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *ti = idr->type_info; + ecs_iter_action_t on_set = ti->hooks.on_set; + if (!on_set) { + continue; } - } - if (!idr || !(idr->flags & EcsIdDontInherit)) { - goto look_in_base; - } else { - return 0; - } - } else if (table->flags & EcsTableHasTarget) { - EcsTarget *tf = ecs_table_get_id(world, table, - ecs_pair_t(EcsTarget, rel), ECS_RECORD_TO_ROW(r->row)); - if (tf) { - return ecs_record_get_entity(tf->target); + const ecs_table_record_t *tr = + flecs_id_record_get_table(idr, table); + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + + if (idr->flags & EcsIdIsSparse) { + int32_t j; + for (j = 0; j < count; j ++) { + flecs_invoke_hook(world, table, tr, 1, row, &entities[j], + id, ti, EcsOnSet, on_set); + } + } else { + ecs_assert(tr->column != -1, ECS_INTERNAL_ERROR, NULL); + ecs_assert(tr->count == 1, ECS_INTERNAL_ERROR, NULL); + if (on_set) { + flecs_invoke_hook(world, table, tr, count, row, entities, + id, ti, EcsOnSet, on_set); + } + } } } - if (index >= tr->count) { - index -= tr->count; - goto look_in_base; + /* Run OnSet notifications */ + if (table->flags & EcsTableHasOnSet && ids->count) { + flecs_emit(world, world, 0, &(ecs_event_desc_t) { + .event = EcsOnSet, + .ids = ids, + .table = table, + .offset = row, + .count = count, + .observable = world + }); } +} - return ecs_pair_second(world, table->type.array[tr->index + index]); -look_in_base: - if (table->flags & EcsTableHasIsA) { - ecs_table_record_t *tr_isa = flecs_id_record_get_table( - world->idr_isa_wildcard, table); - ecs_assert(tr_isa != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_id_t *ids = table->type.array; - int32_t i = tr_isa->index, end = (i + tr_isa->count); - for (; i < end; i ++) { - ecs_id_t isa_pair = ids[i]; - ecs_entity_t base = ecs_pair_second(world, isa_pair); - ecs_assert(base != 0, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t t = ecs_get_target(world, base, rel, index); - if (t) { - return t; +void flecs_record_add_flag( + ecs_record_t *record, + uint32_t flag) +{ + if (flag == EcsEntityIsTraversable) { + if (!(record->row & flag)) { + ecs_table_t *table = record->table; + if (table) { + flecs_table_traversable_add(table, 1); } } } - -not_found: -error: - return 0; + record->row |= flag; } -ecs_entity_t ecs_get_parent( - const ecs_world_t *world, - ecs_entity_t entity) +void flecs_add_flag( + ecs_world_t *world, + ecs_entity_t entity, + uint32_t flag) { - return ecs_get_target(world, entity, EcsChildOf, 0); + ecs_record_t *record = flecs_entities_get_any(world, entity); + ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_record_add_flag(record, flag); } -ecs_entity_t ecs_get_target_for_id( - const ecs_world_t *world, +/* -- Public functions -- */ + +bool ecs_commit( + ecs_world_t *world, ecs_entity_t entity, - ecs_entity_t rel, - ecs_id_t id) + ecs_record_t *record, + ecs_table_t *table, + const ecs_type_t *added, + const ecs_type_t *removed) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(!ecs_is_deferred(world), ECS_INVALID_OPERATION, + "commit cannot be called on stage or while world is deferred"); - if (!id) { - return ecs_get_target(world, entity, rel, 0); + ecs_table_t *src_table = NULL; + if (!record) { + record = flecs_entities_get(world, entity); + src_table = record->table; } - world = ecs_get_world(world); - - ecs_table_t *table = ecs_get_table(world, entity); - ecs_entity_t subject = 0; - - if (rel) { - int32_t column = ecs_search_relation( - world, table, 0, id, rel, 0, &subject, 0, 0); - if (column == -1) { - return 0; - } - } else { - entity = 0; /* Don't return entity if id was not found */ - - if (table) { - ecs_id_t *ids = table->type.array; - int32_t i, count = table->type.count; - - for (i = 0; i < count; i ++) { - ecs_id_t ent = ids[i]; - if (ent & ECS_ID_FLAGS_MASK) { - /* Skip ids with pairs, roles since 0 was provided for rel */ - break; - } + ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; - if (ecs_has_id(world, ent, id)) { - subject = ent; - break; - } - } - } + if (added) { + diff.added = *added; + diff.added_flags = table->flags & EcsTableAddEdgeFlags; } - - if (subject == 0) { - return entity; - } else { - return subject; + if (removed) { + diff.removed = *removed; + if (src_table) { + diff.removed_flags = src_table->flags & EcsTableRemoveEdgeFlags; + } } -error: - return 0; -} - -int32_t ecs_get_depth( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t rel) -{ - ecs_check(ecs_is_valid(world, rel), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_has_id(world, rel, EcsAcyclic), ECS_INVALID_PARAMETER, NULL); - ecs_table_t *table = ecs_get_table(world, entity); - if (table) { - return ecs_table_get_depth(world, table, rel); - } + ecs_defer_begin(world); + flecs_commit(world, entity, record, table, &diff, true, 0); + ecs_defer_end(world); - return 0; + return src_table != table; error: - return -1; -} - -static -ecs_entity_t flecs_id_for_depth( - ecs_world_t *world, - int32_t depth) -{ - ecs_vec_t *depth_ids = &world->store.depth_ids; - int32_t i, count = ecs_vec_count(depth_ids); - for (i = count; i <= depth; i ++) { - ecs_entity_t *el = ecs_vec_append_t( - &world->allocator, depth_ids, ecs_entity_t); - el[0] = ecs_new_w_pair(world, EcsChildOf, EcsFlecsInternals); - ecs_map_val_t *v = ecs_map_ensure(&world->store.entity_to_depth, el[0]); - v[0] = flecs_ito(uint64_t, i); - } - - return ecs_vec_get_t(&world->store.depth_ids, ecs_entity_t, depth)[0]; + return false; } -static -int32_t flecs_depth_for_id( +ecs_entity_t ecs_set_with( ecs_world_t *world, - ecs_entity_t id) + ecs_id_t id) { - ecs_map_val_t *v = ecs_map_get(&world->store.entity_to_depth, id); - ecs_assert(v != NULL, ECS_INTERNAL_ERROR, NULL); - return flecs_uto(int32_t, v[0]); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_id_t prev = stage->with; + stage->with = id; + return prev; +error: + return 0; } -static -int32_t flecs_depth_for_flat_table( - ecs_world_t *world, - ecs_table_t *table) +ecs_id_t ecs_get_with( + const ecs_world_t *world) { - ecs_assert(table->flags & EcsTableHasTarget, ECS_INTERNAL_ERROR, NULL); - ecs_id_t id; - int32_t col = ecs_search(world, table, - ecs_pair(EcsFlatten, EcsWildcard), &id); - ecs_assert(col != -1, ECS_INTERNAL_ERROR, NULL); - (void)col; - return flecs_depth_for_id(world, ECS_PAIR_SECOND(id)); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); + return stage->with; +error: + return 0; } -static -void flecs_flatten( - ecs_world_t *world, - ecs_entity_t root, - ecs_id_t pair, - int32_t depth, - const ecs_flatten_desc_t *desc) +ecs_entity_t ecs_new( + ecs_world_t *world) { - ecs_id_record_t *idr = flecs_id_record_get(world, pair); - if (!idr) { - return; - } - - ecs_entity_t depth_id = flecs_id_for_depth(world, depth); - ecs_id_t root_pair = ecs_pair(EcsChildOf, root); - ecs_id_t tgt_pair = ecs_pair_t(EcsTarget, EcsChildOf); - ecs_id_t depth_pair = ecs_pair(EcsFlatten, depth_id); - ecs_id_t name_pair = ecs_pair_t(EcsIdentifier, EcsName); - - ecs_entity_t rel = ECS_PAIR_FIRST(pair); - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) { - const ecs_table_record_t *tr; - 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); - bool has_tgt = table->flags & EcsTableHasTarget; - flecs_emit_propagate_invalidate(world, table, 0, count); - - ecs_entity_t *entities = table->data.entities.array; - for (i = 0; i < count; i ++) { - ecs_record_t *record = flecs_entities_get(world, entities[i]); - ecs_flags32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); - if (flags & EcsEntityIsTarget) { - flecs_flatten(world, root, ecs_pair(rel, entities[i]), - depth + 1, desc); - } - } - - ecs_table_diff_t tmpdiff; - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); - ecs_table_t *dst; - - dst = flecs_table_traverse_add(world, table, &root_pair, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - - dst = flecs_table_traverse_add(world, dst, &tgt_pair, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - - if (!desc->lose_depth) { - if (!has_tgt) { - dst = flecs_table_traverse_add(world, dst, &depth_pair, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - } else { - int32_t cur_depth = flecs_depth_for_flat_table(world, table); - cur_depth += depth; - ecs_entity_t e_depth = flecs_id_for_depth(world, cur_depth); - ecs_id_t p_depth = ecs_pair(EcsFlatten, e_depth); - dst = flecs_table_traverse_add(world, dst, &p_depth, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - } - } - - if (!desc->keep_names) { - dst = flecs_table_traverse_remove(world, dst, &name_pair, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - } - - int32_t dst_count = ecs_table_count(dst); - - ecs_table_diff_t td; - flecs_table_diff_build_noalloc(&diff, &td); - flecs_notify_on_remove(world, table, NULL, 0, count, &td.removed); - flecs_table_merge(world, dst, table, &dst->data, &table->data); - flecs_notify_on_add(world, dst, NULL, dst_count, count, - &td.added, 0); - flecs_table_diff_builder_fini(world, &diff); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - EcsTarget *fh = ecs_table_get_id(world, dst, tgt_pair, 0); - ecs_assert(fh != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(count != 0, ECS_INTERNAL_ERROR, NULL); - int32_t remain = count; - - for (i = dst_count; i < (dst_count + count); i ++) { - if (!has_tgt) { - fh[i].target = flecs_entities_get_any(world, - ECS_PAIR_SECOND(pair)); - fh[i].count = remain; - remain --; - } - ecs_assert(fh[i].target != NULL, ECS_INTERNAL_ERROR, NULL); - } - } - } + /* It is possible that the world passed to this function is a stage, so + * make sure we have the actual world. Cast away const since this is one of + * the few functions that may modify the world while it is in readonly mode, + * since it is thread safe (uses atomic inc when in threading mode) */ + ecs_world_t *unsafe_world = + ECS_CONST_CAST(ecs_world_t*, ecs_get_world(world)); - ecs_delete_with(world, pair); - flecs_id_record_release(world, idr); -} + ecs_entity_t entity; + if (unsafe_world->flags & EcsWorldMultiThreaded) { + /* When world is in multithreading mode, make sure OS API has threading + * functions initialized */ + ecs_assert(ecs_os_has_threading(), ECS_INVALID_OPERATION, + "thread safe id creation unavailable: threading API not available"); -void ecs_flatten( - ecs_world_t *world, - ecs_id_t pair, - const ecs_flatten_desc_t *desc) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_entity_t rel = ECS_PAIR_FIRST(pair); - ecs_entity_t root = ecs_pair_second(world, pair); - ecs_flatten_desc_t private_desc = {0}; - if (desc) { - private_desc = *desc; + /* Can't atomically increase number above max int */ + ecs_assert(flecs_entities_max_id(unsafe_world) < UINT_MAX, + ECS_INVALID_OPERATION, "thread safe ids exhausted"); + entity = (ecs_entity_t)ecs_os_ainc( + (int32_t*)&flecs_entities_max_id(unsafe_world)); + } else { + entity = flecs_entities_new_id(unsafe_world); } - - ecs_run_aperiodic(world, 0); - ecs_defer_begin(world); - - ecs_id_record_t *idr = flecs_id_record_get(world, pair); - - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) { - const ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; - if (!table->_->traversable_count) { - continue; - } - if (table->flags & EcsTableIsPrefab) { - continue; - } + ecs_assert(!unsafe_world->info.max_id || + ecs_entity_t_lo(entity) <= unsafe_world->info.max_id, + ECS_OUT_OF_RANGE, NULL); - int32_t i, count = ecs_table_count(table); - ecs_entity_t *entities = table->data.entities.array; - for (i = 0; i < count; i ++) { - ecs_record_t *record = flecs_entities_get(world, entities[i]); - ecs_flags32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); - if (flags & EcsEntityIsTarget) { - flecs_flatten(world, root, ecs_pair(rel, entities[i]), 1, - &private_desc); - } - } - } - } + flecs_journal(world, EcsJournalNew, entity, 0, 0); - ecs_defer_end(world); + return entity; +error: + return 0; } -static -const char* flecs_get_identifier( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t tag) +ecs_entity_t ecs_new_low_id( + ecs_world_t *world) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - const EcsIdentifier *ptr = ecs_get_pair( - world, entity, EcsIdentifier, tag); - - if (ptr) { - return ptr->value; - } else { - return NULL; + /* It is possible that the world passed to this function is a stage, so + * make sure we have the actual world. Cast away const since this is one of + * the few functions that may modify the world while it is in readonly mode, + * but only if single threaded. */ + ecs_world_t *unsafe_world = + ECS_CONST_CAST(ecs_world_t*, ecs_get_world(world)); + if (unsafe_world->flags & EcsWorldReadonly) { + /* Can't issue new comp id while iterating when in multithreaded mode */ + ecs_check(ecs_get_stage_count(world) <= 1, + ECS_INVALID_WHILE_READONLY, NULL); } -error: - return NULL; -} -const char* ecs_get_name( - const ecs_world_t *world, - ecs_entity_t entity) -{ - return flecs_get_identifier(world, entity, EcsName); -} + ecs_entity_t id = 0; + if (unsafe_world->info.last_component_id < FLECS_HI_COMPONENT_ID) { + do { + id = unsafe_world->info.last_component_id ++; + } while (ecs_exists(unsafe_world, id) && id <= FLECS_HI_COMPONENT_ID); + } -const char* ecs_get_symbol( - const ecs_world_t *world, - ecs_entity_t entity) -{ - world = ecs_get_world(world); - if (ecs_owns_pair(world, entity, ecs_id(EcsIdentifier), EcsSymbol)) { - return flecs_get_identifier(world, entity, EcsSymbol); + if (!id || id >= FLECS_HI_COMPONENT_ID) { + /* If the low component ids are depleted, return a regular entity id */ + id = ecs_new(unsafe_world); } else { - return NULL; + flecs_entities_ensure(world, id); } + + ecs_assert(ecs_get_type(world, id) == NULL, ECS_INTERNAL_ERROR, NULL); + + return id; +error: + return 0; } -static -ecs_entity_t flecs_set_identifier( +ecs_entity_t ecs_new_w_id( ecs_world_t *world, - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_entity_t tag, - const char *name) + ecs_id_t id) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(entity != 0 || name != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - if (!entity) { - entity = ecs_new_id(world); - } + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_entity_t entity = ecs_new(world); - if (!name) { - ecs_remove_pair(world, entity, ecs_id(EcsIdentifier), tag); + if (flecs_defer_add(stage, entity, id)) { return entity; } - EcsIdentifier *ptr = ecs_get_mut_pair(world, entity, EcsIdentifier, tag); - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_diff_builder_t diff_builder = ECS_TABLE_DIFF_INIT; + flecs_table_diff_builder_init(world, &diff_builder); + ecs_table_t *table = flecs_find_table_add( + world, &world->store.root, id, &diff_builder); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - if (tag == EcsName) { - /* Insert command after get_mut, but before the name is potentially - * freed. Even though the name is a const char*, it is possible that the - * application passed in the existing name of the entity which could - * still cause it to be freed. */ - flecs_defer_path(stage, 0, entity, name); - } + ecs_table_diff_t diff; + flecs_table_diff_build_noalloc(&diff_builder, &diff); + ecs_record_t *r = flecs_entities_get(world, entity); + flecs_new_entity(world, entity, r, table, &diff, true, 0); + flecs_table_diff_builder_fini(world, &diff_builder); + + flecs_defer_end(world, stage); - ecs_os_strset(&ptr->value, name); - ecs_modified_pair(world, entity, ecs_id(EcsIdentifier), tag); - return entity; error: return 0; } -ecs_entity_t ecs_set_name( +ecs_entity_t ecs_new_w_table( ecs_world_t *world, - ecs_entity_t entity, - const char *name) + ecs_table_t *table) { - if (!entity) { - return ecs_entity(world, { - .name = name - }); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); + + flecs_stage_from_world(&world); + ecs_entity_t entity = ecs_new(world); + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_flags32_t flags = table->flags & EcsTableAddEdgeFlags; + if (table->flags & EcsTableHasIsA) { + flags |= EcsTableHasOnAdd; } - ecs_stage_t *stage = flecs_stage_from_world(&world); - flecs_set_identifier(world, stage, entity, EcsName, name); + ecs_table_diff_t table_diff = { + .added = table->type, + .added_flags = flags + }; - return entity; -} + flecs_new_entity(world, entity, r, table, &table_diff, true, 0); -ecs_entity_t ecs_set_symbol( - ecs_world_t *world, - ecs_entity_t entity, - const char *name) -{ - return flecs_set_identifier(world, NULL, entity, EcsSymbol, name); + return entity; +error: + return 0; } -void ecs_set_alias( +static +void flecs_copy_id( ecs_world_t *world, - ecs_entity_t entity, - const char *name) -{ - flecs_set_identifier(world, NULL, entity, EcsAlias, name); -} - -ecs_id_t ecs_make_pair( - ecs_entity_t relationship, - ecs_entity_t target) -{ - ecs_assert(!ECS_IS_PAIR(relationship) && !ECS_IS_PAIR(target), - ECS_INVALID_PARAMETER, "cannot create nested pairs"); - return ecs_pair(relationship, target); -} - -bool ecs_is_valid( - const ecs_world_t *world, - ecs_entity_t entity) + ecs_record_t *r, + ecs_id_t id, + size_t size, + void *dst_ptr, + void *src_ptr, + const ecs_type_info_t *ti) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(dst_ptr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(src_ptr != NULL, ECS_INVALID_PARAMETER, NULL); - /* 0 is not a valid entity id */ - if (!entity) { - return false; - } - - /* Entity identifiers should not contain flag bits */ - if (entity & ECS_ID_FLAGS_MASK) { - return false; + ecs_copy_t copy = ti->hooks.copy; + if (copy) { + copy(dst_ptr, src_ptr, 1, ti); + } else { + ecs_os_memcpy(dst_ptr, src_ptr, flecs_utosize(size)); } - /* Entities should not contain data in dead zone bits */ - if (entity & ~0xFF00FFFFFFFFFFFF) { - return false; - } + flecs_table_mark_dirty(world, r->table, id); - /* If entity doesn't exist in the world, the id is valid as long as the - * generation is 0. Using a non-existing id with a non-zero generation - * requires calling ecs_ensure first. */ - if (!ecs_exists(world, entity)) { - return ECS_GENERATION(entity) == 0; + ecs_table_t *table = r->table; + if (table->flags & EcsTableHasOnSet || ti->hooks.on_set) { + ecs_type_t ids = { .array = &id, .count = 1 }; + flecs_notify_on_set( + world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, true); } - - /* If id exists, it must be alive (the generation count must match) */ - return ecs_is_alive(world, entity); error: - return false; + return; } -ecs_id_t ecs_strip_generation( - ecs_entity_t e) +/* Traverse table graph by either adding or removing identifiers parsed from the + * passed in expression. */ +static +int flecs_traverse_from_expr( + ecs_world_t *world, + const char *name, + const char *expr, + ecs_vec_t *ids) { - /* If this is not a pair, erase the generation bits */ - if (!(e & ECS_ID_FLAGS_MASK)) { - e &= ~ECS_GENERATION_MASK; - } - - return e; -} +#ifdef FLECS_SCRIPT + const char *ptr = expr; + if (ptr) { + ecs_id_t id = 0; + while (ptr[0] && (ptr = flecs_id_parse(world, name, ptr, &id))) { + if (!id) { + break; + } -bool ecs_is_alive( - const ecs_world_t *world, - ecs_entity_t entity) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); + if (!ecs_id_is_valid(world, id)) { + char *idstr = ecs_id_str(world, id); + ecs_parser_error(name, expr, (ptr - expr), + "id %s is invalid for add expression", idstr); + ecs_os_free(idstr); + goto error; + } - world = ecs_get_world(world); + ecs_vec_append_t(&world->allocator, ids, ecs_id_t)[0] = id; + } - return flecs_entities_is_alive(world, entity); + if (!ptr) { + goto error; + } + } + return 0; +#else + (void)world; + (void)name; + (void)expr; + (void)ids; + ecs_err("cannot parse component expression: script addon required"); + goto error; +#endif error: - return false; + return -1; } -ecs_entity_t ecs_get_alive( - const ecs_world_t *world, - ecs_entity_t entity) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - - if (!entity) { - return 0; - } - - /* Make sure we're not working with a stage */ - world = ecs_get_world(world); +/* Add/remove components based on the parsed expression. This operation is + * slower than flecs_traverse_from_expr, but safe to use from a deferred context. */ +static +void flecs_defer_from_expr( + ecs_world_t *world, + ecs_entity_t entity, + const char *name, + const char *expr) +{ +#ifdef FLECS_SCRIPT + const char *ptr = expr; + if (ptr) { + ecs_id_t id = 0; + while (ptr[0] && (ptr = flecs_id_parse(world, name, ptr, &id))) { + if (!id) { + break; + } + ecs_add_id(world, entity, id); + } + } +#else + (void)world; + (void)entity; + (void)name; + (void)expr; + ecs_err("cannot parse component expression: script addon required"); +#endif +} - if (flecs_entities_is_alive(world, entity)) { - return entity; +/* If operation is not deferred, add components by finding the target + * table and moving the entity towards it. */ +static +int flecs_traverse_add( + ecs_world_t *world, + ecs_entity_t result, + const char *name, + const ecs_entity_desc_t *desc, + ecs_entity_t scope, + ecs_id_t with, + bool new_entity, + bool name_assigned) +{ + const char *sep = desc->sep; + const char *root_sep = desc->root_sep; + ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; + flecs_table_diff_builder_init(world, &diff); + ecs_vec_t ids; + + /* Add components from the 'add_expr' expression. Look up before naming + * entity, so that expression can't resolve to self. */ + ecs_vec_init_t(&world->allocator, &ids, ecs_id_t, 0); + if (desc->add_expr && ecs_os_strcmp(desc->add_expr, "0")) { + if (flecs_traverse_from_expr(world, name, desc->add_expr, &ids)) { + goto error; + } } - /* Make sure id does not have generation. This guards against accidentally - * "upcasting" a not alive identifier to a alive one. */ - if ((uint32_t)entity != entity) { - return 0; + /* Set symbol */ + if (desc->symbol && desc->symbol[0]) { + const char *sym = ecs_get_symbol(world, result); + if (sym) { + ecs_assert(!ecs_os_strcmp(desc->symbol, sym), + ECS_INCONSISTENT_NAME, desc->symbol); + } else { + ecs_set_symbol(world, result, desc->symbol); + } } - ecs_entity_t current = flecs_entities_get_generation(world, entity); - if (!current || !flecs_entities_is_alive(world, current)) { - return 0; + /* If a name is provided but not yet assigned, add the Name component */ + if (name && !name_assigned) { + ecs_add_path_w_sep(world, result, scope, name, sep, root_sep); + } else if (new_entity && scope) { + ecs_add_pair(world, result, EcsChildOf, scope); } - return current; -error: - return 0; -} + /* Find existing table */ + ecs_table_t *src_table = NULL, *table = NULL; + ecs_record_t *r = flecs_entities_get(world, result); + table = r->table; -void ecs_ensure( - ecs_world_t *world, - ecs_entity_t entity) -{ - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); + /* Add components from the 'add' array */ + if (desc->add) { + int32_t i = 0; + ecs_id_t id; - /* Const cast is safe, function checks for threading */ - world = ECS_CONST_CAST(ecs_world_t*, ecs_get_world(world)); + while ((id = desc->add[i ++])) { + table = flecs_find_table_add(world, table, id, &diff); + } + } - /* 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); + /* Add components from the 'set' array */ + if (desc->set) { + int32_t i = 0; + ecs_id_t id; - /* Check if a version of the provided id is alive */ - ecs_entity_t any = ecs_get_alive(world, (uint32_t)entity); - if (any == entity) { - /* If alive and equal to the argument, there's nothing left to do */ - return; + while ((id = desc->set[i ++].type)) { + table = flecs_find_table_add(world, table, id, &diff); + } } - /* If the id is currently alive but did not match the argument, fail */ - ecs_check(!any, ECS_INVALID_PARAMETER, NULL); + /* Add ids from .expr */ + { + int32_t i, count = ecs_vec_count(&ids); + ecs_id_t *expr_ids = ecs_vec_first(&ids); + for (i = 0; i < count; i ++) { + table = flecs_find_table_add(world, table, expr_ids[i], &diff); + } + } - /* Set generation if not alive. The sparse set checks if the provided - * id matches its own generation which is necessary for alive ids. This - * check would cause ecs_ensure to fail if the generation of the 'entity' - * argument doesn't match with its generation. - * - * While this could've been addressed in the sparse set, this is a rare - * scenario that can only be triggered by ecs_ensure. Implementing it here - * allows the sparse set to not do this check, which is more efficient. */ - flecs_entities_set_generation(world, entity); + /* Find destination table */ + /* If this is a new entity without a name, add the scope. If a name is + * provided, the scope will be added by the add_path_w_sep function */ + if (new_entity) { + if (new_entity && scope && !name && !name_assigned) { + table = flecs_find_table_add( + world, table, ecs_pair(EcsChildOf, scope), &diff); + } + if (with) { + table = flecs_find_table_add(world, table, with, &diff); + } + } - /* Ensure id exists. The underlying datastructure will verify that the - * generation count matches the provided one. */ - flecs_entities_ensure(world, entity); + /* Commit entity to destination table */ + if (src_table != table) { + flecs_defer_begin(world, world->stages[0]); + ecs_table_diff_t table_diff; + flecs_table_diff_build_noalloc(&diff, &table_diff); + flecs_commit(world, result, r, table, &table_diff, true, 0); + flecs_table_diff_builder_fini(world, &diff); + flecs_defer_end(world, world->stages[0]); + } + + /* Set component values */ + if (desc->set) { + table = r->table; + ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t i = 0, row = ECS_RECORD_TO_ROW(r->row); + const ecs_value_t *v; + + flecs_defer_begin(world, world->stages[0]); + + while ((void)(v = &desc->set[i ++]), v->type) { + if (!v->ptr) { + continue; + } + ecs_assert(ECS_RECORD_TO_ROW(r->row) == row, ECS_INTERNAL_ERROR, NULL); + ecs_id_record_t *idr = flecs_id_record_get(world, v->type); + flecs_component_ptr_t ptr = flecs_get_component_ptr(table, row, idr); + ecs_check(ptr.ptr != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *ti = idr->type_info; + flecs_copy_id(world, r, v->type, + flecs_itosize(ti->size), ptr.ptr, v->ptr, ti); + } + + flecs_defer_end(world, world->stages[0]); + } + + flecs_table_diff_builder_fini(world, &diff); + ecs_vec_fini_t(&world->allocator, &ids, ecs_id_t); + return 0; error: - return; + ecs_vec_fini_t(&world->allocator, &ids, ecs_id_t); + return -1; } -void ecs_ensure_id( +/* When in deferred mode, we need to add/remove components one by one using + * the regular operations. */ +static +void flecs_deferred_add_remove( ecs_world_t *world, - ecs_id_t id) + ecs_entity_t entity, + const char *name, + const ecs_entity_desc_t *desc, + ecs_entity_t scope, + ecs_id_t with, + bool flecs_new_entity, + bool name_assigned) { - ecs_poly_assert(world, ecs_world_t); - if (ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_entity_t r = ECS_PAIR_FIRST(id); - ecs_entity_t o = ECS_PAIR_SECOND(id); - - ecs_check(r != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(o != 0, ECS_INVALID_PARAMETER, NULL); + const char *sep = desc->sep; + const char *root_sep = desc->root_sep; - if (flecs_entities_get_generation(world, r) == 0) { - ecs_assert(!ecs_exists(world, r), ECS_INVALID_PARAMETER, - "first element of pair is not alive"); - flecs_entities_ensure(world, r); + /* If this is a new entity without a name, add the scope. If a name is + * provided, the scope will be added by the add_path_w_sep function */ + if (flecs_new_entity) { + if (flecs_new_entity && scope && !name && !name_assigned) { + ecs_add_id(world, entity, ecs_pair(EcsChildOf, scope)); } - if (flecs_entities_get_generation(world, o) == 0) { - ecs_assert(!ecs_exists(world, o), ECS_INVALID_PARAMETER, - "second element of pair is not alive"); - flecs_entities_ensure(world, o); + + if (with) { + ecs_add_id(world, entity, with); } - } else { - flecs_entities_ensure(world, id & ECS_COMPONENT_MASK); } -error: - return; -} -bool ecs_exists( - const ecs_world_t *world, - ecs_entity_t entity) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); + /* Add components from the 'add' id array */ + if (desc->add) { + int32_t i = 0; + ecs_id_t id; - world = ecs_get_world(world); + while ((id = desc->add[i ++])) { + bool defer = true; + if (ECS_HAS_ID_FLAG(id, PAIR) && ECS_PAIR_FIRST(id) == EcsChildOf) { + scope = ECS_PAIR_SECOND(id); + if (name && (!desc->id || !name_assigned)) { + /* New named entities are created by temporarily going out of + * readonly mode to ensure no duplicates are created. */ + defer = false; + } + } + if (defer) { + ecs_add_id(world, entity, id); + } + } + } - return flecs_entities_exists(world, entity); -error: - return false; -} + /* Set component values */ + if (desc->set) { + int32_t i = 0; + const ecs_value_t *v; + while ((void)(v = &desc->set[i ++]), v->type) { + if (v->ptr) { + ecs_set_id(world, entity, v->type, 0, v->ptr); + } else { + ecs_add_id(world, entity, v->type); + } + } + } -ecs_table_t* ecs_get_table( - const ecs_world_t *world, - ecs_entity_t entity) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_valid(world, entity), ECS_INVALID_PARAMETER, NULL); - - world = ecs_get_world(world); + /* Add components from the 'add_expr' expression */ + if (desc->add_expr) { + flecs_defer_from_expr(world, entity, name, desc->add_expr); + } - ecs_record_t *record = flecs_entities_get(world, entity); - ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); - return record->table; -error: - return NULL; -} + int32_t thread_count = ecs_get_stage_count(world); -const ecs_type_t* ecs_get_type( - const ecs_world_t *world, - ecs_entity_t entity) -{ - ecs_table_t *table = ecs_get_table(world, entity); - if (table) { - return &table->type; + /* Set symbol */ + if (desc->symbol) { + const char *sym = ecs_get_symbol(world, entity); + if (!sym || ecs_os_strcmp(sym, desc->symbol)) { + if (thread_count <= 1) { /* See above */ + ecs_suspend_readonly_state_t state; + ecs_world_t *real_world = flecs_suspend_readonly(world, &state); + ecs_set_symbol(world, entity, desc->symbol); + flecs_resume_readonly(real_world, &state); + } else { + ecs_set_symbol(world, entity, desc->symbol); + } + } } - return NULL; + /* Set name */ + if (name && !name_assigned) { + ecs_add_path_w_sep(world, entity, scope, name, sep, root_sep); + } } -const ecs_type_info_t* ecs_get_type_info( - const ecs_world_t *world, - ecs_id_t id) +ecs_entity_t ecs_entity_init( + ecs_world_t *world, + const ecs_entity_desc_t *desc) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_entity_desc_t was not initialized to zero"); - world = ecs_get_world(world); + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_entity_t scope = stage->scope; + ecs_id_t with = ecs_get_with(world); + ecs_entity_t result = desc->id; - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr && ECS_IS_PAIR(id)) { - idr = flecs_id_record_get(world, - ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard)); - if (!idr || !idr->type_info) { - idr = NULL; - } - if (!idr) { - ecs_entity_t first = ecs_pair_first(world, id); - if (!first || !ecs_has_id(world, first, EcsTag)) { - idr = flecs_id_record_get(world, - ecs_pair(EcsWildcard, ECS_PAIR_SECOND(id))); - if (!idr || !idr->type_info) { - idr = NULL; +#ifdef FLECS_DEBUG + if (desc->add) { + ecs_id_t id; + int32_t i = 0; + while ((id = desc->add[i ++])) { + if (ECS_HAS_ID_FLAG(id, PAIR) && + (ECS_PAIR_FIRST(id) == EcsChildOf)) + { + if (desc->name) { + ecs_check(false, ECS_INVALID_PARAMETER, "%s: cannot set parent in " + "ecs_entity_desc_t::add, use ecs_entity_desc_t::parent", + desc->name); + } else { + ecs_check(false, ECS_INVALID_PARAMETER, "cannot set parent in " + "ecs_entity_desc_t::add, use ecs_entity_desc_t::parent"); } } } } +#endif - if (idr) { - return idr->type_info; - } else if (!(id & ECS_ID_FLAGS_MASK)) { - return flecs_type_info_get(world, id); + const char *name = desc->name; + const char *sep = desc->sep; + if (!sep) { + sep = "."; } -error: - return NULL; -} -ecs_entity_t ecs_get_typeid( - const ecs_world_t *world, - ecs_id_t id) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - const ecs_type_info_t *ti = ecs_get_type_info(world, id); - if (ti) { - ecs_assert(ti->component != 0, ECS_INTERNAL_ERROR, NULL); - return ti->component; + if (name) { + if (!name[0]) { + name = NULL; + } else if (flecs_name_is_id(name)){ + ecs_entity_t id = flecs_name_to_id(name); + if (!id) { + return 0; + } + if (result && (id != result)) { + ecs_err("name id conflicts with provided id"); + return 0; + } + name = NULL; + result = id; + } } -error: - return 0; -} -bool ecs_id_is_tag( - const ecs_world_t *world, - ecs_id_t id) -{ - if (ecs_id_is_wildcard(id)) { - /* If id is a wildcard, we can't tell if it's a tag or not, except - * when the relationship part of a pair has the Tag property */ - if (ECS_HAS_ID_FLAG(id, PAIR)) { - if (ECS_PAIR_FIRST(id) != EcsWildcard) { - ecs_entity_t rel = ecs_pair_first(world, id); - if (ecs_is_valid(world, rel)) { - if (ecs_has_id(world, rel, EcsTag)) { - return true; - } - } else { - /* During bootstrap it's possible that not all ids are valid - * yet. Using ecs_get_typeid will ensure correct values are - * returned for only those components initialized during - * bootstrap, while still asserting if another invalid id - * is provided. */ - if (ecs_get_typeid(world, id) == 0) { - return true; - } - } + const char *root_sep = desc->root_sep; + bool flecs_new_entity = false; + bool name_assigned = false; + + /* Remove optional prefix from name. Entity names can be derived from + * language identifiers, such as components (typenames) and systems + * function names). Because C does not have namespaces, such identifiers + * often encode the namespace as a prefix. + * To ensure interoperability between C and C++ (and potentially other + * languages with namespacing) the entity must be stored without this prefix + * and with the proper namespace, which is what the name_prefix is for */ + const char *prefix = world->info.name_prefix; + if (name && prefix) { + ecs_size_t len = ecs_os_strlen(prefix); + if (!ecs_os_strncmp(name, prefix, len) && + (isupper(name[len]) || name[len] == '_')) + { + if (name[len] == '_') { + name = name + len + 1; } else { - /* If relationship is wildcard id is not guaranteed to be a tag */ + name = name + len; } } - } else { - if (ecs_get_typeid(world, id) == 0) { - return true; - } } - return false; -} - -bool ecs_id_is_union( - const ecs_world_t *world, - ecs_id_t id) -{ - if (!ECS_IS_PAIR(id)) { - return false; - } else if (ECS_PAIR_FIRST(id) == EcsUnion) { - return true; - } else { - ecs_entity_t first = ecs_pair_first(world, id); - if (ecs_has_id(world, first, EcsUnion)) { - return true; - } + /* Parent field takes precedence over scope */ + if (desc->parent) { + scope = desc->parent; } - return false; -} + /* Find or create entity */ + if (!result) { + if (name) { + /* If add array contains a ChildOf pair, use it as scope instead */ + result = ecs_lookup_path_w_sep( + world, scope, name, sep, root_sep, false); + if (result) { + name_assigned = true; + } + } -int32_t ecs_count_id( - const ecs_world_t *world, - ecs_entity_t id) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + if (!result) { + if (desc->use_low_id) { + result = ecs_new_low_id(world); + } else { + result = ecs_new(world); + } + flecs_new_entity = true; + ecs_assert(ecs_get_type(world, result) == NULL, + ECS_INTERNAL_ERROR, NULL); + } + } else { + /* Make sure provided id is either alive or revivable */ + ecs_make_alive(world, result); - if (!id) { - return 0; + name_assigned = ecs_has_pair( + world, result, ecs_id(EcsIdentifier), EcsName); + if (name && name_assigned) { + /* If entity has name, verify that name matches. The name provided + * to the function could either have been relative to the current + * scope, or fully qualified. */ + char *path; + ecs_size_t root_sep_len = root_sep ? ecs_os_strlen(root_sep) : 0; + if (root_sep && !ecs_os_strncmp(name, root_sep, root_sep_len)) { + /* Fully qualified name was provided, so make sure to + * compare with fully qualified name */ + path = ecs_get_path_w_sep(world, 0, result, sep, root_sep); + } else { + /* Relative name was provided, so make sure to compare with + * relative name */ + if (!sep || sep[0]) { + path = ecs_get_path_w_sep(world, scope, result, sep, ""); + } else { + /* Safe, only freed when sep is valid */ + path = ECS_CONST_CAST(char*, ecs_get_name(world, result)); + } + } + if (path) { + if (ecs_os_strcmp(path, name)) { + /* Mismatching name */ + ecs_err("existing entity '%s' is initialized with " + "conflicting name '%s'", path, name); + if (!sep || sep[0]) { + ecs_os_free(path); + } + return 0; + } + if (!sep || sep[0]) { + ecs_os_free(path); + } + } + } } - int32_t count = 0; - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = id, - .src.flags = EcsSelf, - .flags = EcsTermMatchDisabled|EcsTermMatchPrefab - }); - - it.flags |= EcsIterNoData; - it.flags |= EcsIterEvalTables; + ecs_assert(name_assigned == ecs_has_pair( + world, result, ecs_id(EcsIdentifier), EcsName), + ECS_INTERNAL_ERROR, NULL); - while (ecs_term_next(&it)) { - count += it.count; + if (stage->defer) { + flecs_deferred_add_remove((ecs_world_t*)stage, result, name, desc, + scope, with, flecs_new_entity, name_assigned); + } else { + if (flecs_traverse_add(world, result, name, desc, + scope, with, flecs_new_entity, name_assigned)) + { + return 0; + } } - return count; + return result; error: return 0; } -void ecs_enable( +const ecs_entity_t* ecs_bulk_init( ecs_world_t *world, - ecs_entity_t entity, - bool enabled) + const ecs_bulk_desc_t *desc) { - if (ecs_has_id(world, entity, EcsPrefab)) { - /* If entity is a type, enable/disable all entities in the type */ - const ecs_type_t *type = ecs_get_type(world, entity); - ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_id_t *ids = type->array; - int32_t i, count = type->count; - for (i = 0; i < count; i ++) { - ecs_id_t id = ids[i]; - if (ecs_id_get_flags(world, id) & EcsIdDontInherit) { - continue; - } - ecs_enable(world, id, enabled); - } + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_bulk_desc_t was not initialized to zero"); + + const ecs_entity_t *entities = desc->entities; + int32_t count = desc->count; + + int32_t sparse_count = 0; + if (!entities) { + sparse_count = flecs_entities_count(world); + entities = flecs_entities_new_ids(world, count); + ecs_assert(entities != NULL, ECS_INTERNAL_ERROR, NULL); } else { - if (enabled) { - ecs_remove_id(world, entity, EcsDisabled); - } else { - ecs_add_id(world, entity, EcsDisabled); + int i; + for (i = 0; i < count; i ++) { + ecs_make_alive(world, entities[i]); } } -} - -bool ecs_defer_begin( - ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); - return flecs_defer_begin(world, stage); -error: - return false; -} -bool ecs_defer_end( - ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); - return flecs_defer_end(world, stage); -error: - return false; -} + ecs_type_t ids; + ecs_table_t *table = desc->table; + if (!table) { + ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; + flecs_table_diff_builder_init(world, &diff); -void ecs_defer_suspend( - ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_deferred(world), ECS_INVALID_OPERATION, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_check(stage->defer > 0, ECS_INVALID_OPERATION, NULL); - stage->defer = -stage->defer; -error: - return; -} + int32_t i = 0; + ecs_id_t id; + while ((id = desc->ids[i])) { + table = flecs_find_table_add(world, table, id, &diff); + i ++; + } -void ecs_defer_resume( - ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_check(stage->defer < 0, ECS_INVALID_OPERATION, NULL); - stage->defer = -stage->defer; + ids.array = ECS_CONST_CAST(ecs_id_t*, desc->ids); + ids.count = i; + + 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; + } else { + /* Refetch entity ids, in case the underlying array was reallocated */ + entities = flecs_entities_ids(world); + return &entities[sparse_count]; + } error: - return; + return NULL; } -const char* ecs_id_flag_str( - ecs_entity_t entity) +static +void flecs_check_component( + ecs_world_t *world, + ecs_entity_t result, + const EcsComponent *ptr, + ecs_size_t size, + ecs_size_t alignment) { - if (ECS_HAS_ID_FLAG(entity, PAIR)) { - return "PAIR"; - } else - if (ECS_HAS_ID_FLAG(entity, TOGGLE)) { - return "TOGGLE"; - } else - if (ECS_HAS_ID_FLAG(entity, AND)) { - return "AND"; - } else - if (ECS_HAS_ID_FLAG(entity, OVERRIDE)) { - return "OVERRIDE"; - } else { - return "UNKNOWN"; + if (ptr->size != size) { + char *path = ecs_get_path(world, result); + ecs_abort(ECS_INVALID_COMPONENT_SIZE, path); + ecs_os_free(path); + } + if (ptr->alignment != alignment) { + char *path = ecs_get_path(world, result); + ecs_abort(ECS_INVALID_COMPONENT_ALIGNMENT, path); + ecs_os_free(path); } } -void ecs_id_str_buf( - const ecs_world_t *world, - ecs_id_t id, - ecs_strbuf_t *buf) +ecs_entity_t ecs_component_init( + ecs_world_t *world, + const ecs_component_desc_t *desc) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_component_desc_t was not initialized to 0"); - world = ecs_get_world(world); + /* If existing entity is provided, check if it is already registered as a + * component and matches the size/alignment. This can prevent having to + * suspend readonly mode, and increases the number of scenarios in which + * this function can be called in multithreaded mode. */ + ecs_entity_t result = desc->entity; + if (result && ecs_is_alive(world, result)) { + const EcsComponent *const_ptr = ecs_get(world, result, EcsComponent); + if (const_ptr) { + flecs_check_component(world, result, const_ptr, + desc->type.size, desc->type.alignment); + return result; + } + } - if (ECS_HAS_ID_FLAG(id, TOGGLE)) { - ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_TOGGLE)); - ecs_strbuf_appendch(buf, '|'); + ecs_suspend_readonly_state_t readonly_state; + world = flecs_suspend_readonly(world, &readonly_state); + + bool new_component = true; + if (!result) { + result = ecs_new_low_id(world); + } else { + ecs_make_alive(world, result); + new_component = ecs_has(world, result, EcsComponent); } - if (ECS_HAS_ID_FLAG(id, OVERRIDE)) { - ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_OVERRIDE)); - ecs_strbuf_appendch(buf, '|'); + EcsComponent *ptr = ecs_ensure(world, result, EcsComponent); + if (!ptr->size) { + ecs_assert(ptr->alignment == 0, ECS_INTERNAL_ERROR, NULL); + ptr->size = desc->type.size; + ptr->alignment = desc->type.alignment; + if (!new_component || ptr->size != desc->type.size) { + if (!ptr->size) { + ecs_trace("#[green]tag#[reset] %s created", + ecs_get_name(world, result)); + } else { + ecs_trace("#[green]component#[reset] %s created", + ecs_get_name(world, result)); + } + } + } else { + flecs_check_component(world, result, ptr, + desc->type.size, desc->type.alignment); } - if (ECS_HAS_ID_FLAG(id, AND)) { - ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_AND)); - ecs_strbuf_appendch(buf, '|'); + if (desc->type.name && new_component) { + ecs_entity(world, { .id = result, .name = desc->type.name }); } - if (ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_entity_t rel = ECS_PAIR_FIRST(id); - ecs_entity_t obj = ECS_PAIR_SECOND(id); + ecs_modified(world, result, EcsComponent); - ecs_entity_t e; - if ((e = ecs_get_alive(world, rel))) { - rel = e; - } - if ((e = ecs_get_alive(world, obj))) { - obj = e; - } + if (desc->type.size && + !ecs_id_in_use(world, result) && + !ecs_id_in_use(world, ecs_pair(result, EcsWildcard))) + { + ecs_set_hooks_id(world, result, &desc->type.hooks); + } - ecs_strbuf_appendch(buf, '('); - ecs_get_path_w_sep_buf(world, 0, rel, NULL, NULL, buf); - ecs_strbuf_appendch(buf, ','); - ecs_get_path_w_sep_buf(world, 0, obj, NULL, NULL, buf); - ecs_strbuf_appendch(buf, ')'); - } else { - ecs_entity_t e = id & ECS_COMPONENT_MASK; - ecs_get_path_w_sep_buf(world, 0, e, NULL, NULL, buf); + if (result >= world->info.last_component_id && result < FLECS_HI_COMPONENT_ID) { + world->info.last_component_id = result + 1; } + flecs_resume_readonly(world, &readonly_state); + + ecs_assert(result != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_has(world, result, EcsComponent), ECS_INTERNAL_ERROR, NULL); + + return result; error: - return; + return 0; } -char* ecs_id_str( - const ecs_world_t *world, - ecs_id_t id) +const ecs_entity_t* ecs_bulk_new_w_id( + ecs_world_t *world, + ecs_id_t id, + int32_t count) { - ecs_strbuf_t buf = ECS_STRBUF_INIT; - ecs_id_str_buf(world, id, &buf); - return ecs_strbuf_get(&buf); -} + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stage_t *stage = flecs_stage_from_world(&world); -static -void ecs_type_str_buf( - const ecs_world_t *world, - const ecs_type_t *type, - ecs_strbuf_t *buf) -{ - ecs_entity_t *ids = type->array; - int32_t i, count = type->count; + const ecs_entity_t *ids; + if (flecs_defer_bulk_new(world, stage, count, id, &ids)) { + return ids; + } - for (i = 0; i < count; i ++) { - ecs_entity_t id = ids[i]; + ecs_table_t *table = &world->store.root; + ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; + flecs_table_diff_builder_init(world, &diff); + + if (id) { + table = flecs_find_table_add(world, table, id, &diff); + } - if (i) { - ecs_strbuf_appendch(buf, ','); - ecs_strbuf_appendch(buf, ' '); - } + ecs_table_diff_t td; + flecs_table_diff_build_noalloc(&diff, &td); + ids = flecs_bulk_new(world, table, NULL, NULL, count, NULL, false, NULL, &td); + flecs_table_diff_builder_fini(world, &diff); + flecs_defer_end(world, stage); - if (id == 1) { - ecs_strbuf_appendlit(buf, "Component"); - } else { - ecs_id_str_buf(world, id, buf); - } - } + return ids; +error: + return NULL; } -char* ecs_type_str( - const ecs_world_t *world, - const ecs_type_t *type) +void ecs_clear( + ecs_world_t *world, + ecs_entity_t entity) { - if (!type) { - return ecs_os_strdup(""); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_valid(world, entity), ECS_INVALID_PARAMETER, NULL); + + ecs_stage_t *stage = flecs_stage_from_world(&world); + if (flecs_defer_clear(stage, entity)) { + return; } - ecs_strbuf_t buf = ECS_STRBUF_INIT; - ecs_type_str_buf(world, type, &buf); - return ecs_strbuf_get(&buf); -} + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); -char* ecs_table_str( - const ecs_world_t *world, - const ecs_table_t *table) -{ + ecs_table_t *table = r->table; if (table) { - return ecs_type_str(world, &table->type); - } else { - return NULL; - } -} + ecs_table_diff_t diff = { + .removed = table->type, + .removed_flags = table->flags & EcsTableRemoveEdgeFlags + }; -char* ecs_entity_str( - const ecs_world_t *world, - ecs_entity_t entity) -{ - ecs_strbuf_t buf = ECS_STRBUF_INIT; - ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + flecs_delete_entity(world, r, &diff); + r->table = NULL; - ecs_get_path_w_sep_buf(world, 0, entity, 0, "", &buf); - - ecs_strbuf_appendlit(&buf, " ["); - const ecs_type_t *type = ecs_get_type(world, entity); - if (type) { - ecs_type_str_buf(world, type, &buf); - } - ecs_strbuf_appendch(&buf, ']'); + if (r->row & EcsEntityIsTraversable) { + flecs_table_traversable_add(table, -1); + } + } - return ecs_strbuf_get(&buf); + flecs_defer_end(world, stage); error: - return NULL; + return; } static -void flecs_flush_bulk_new( +void flecs_throw_invalid_delete( ecs_world_t *world, - ecs_cmd_t *cmd) + ecs_id_t id) { - ecs_entity_t *entities = cmd->is._n.entities; - - if (cmd->id) { - int i, count = cmd->is._n.count; - for (i = 0; i < count; i ++) { - flecs_entities_ensure(world, entities[i]); - flecs_add_id(world, entities[i], cmd->id); - } - } - - ecs_os_free(entities); + char *id_str = NULL; + if (!(world->flags & EcsWorldQuit)) { + id_str = ecs_id_str(world, id); + ecs_err("(OnDelete, Panic) constraint violated while deleting %s", + id_str); + ecs_os_free(id_str); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } } static -void flecs_dtor_value( +void flecs_marked_id_push( ecs_world_t *world, - ecs_id_t id, - void *value, - int32_t count) + ecs_id_record_t* idr, + ecs_entity_t action, + bool delete_id) { - const ecs_type_info_t *ti = ecs_get_type_info(world, id); - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_xtor_t dtor = ti->hooks.dtor; - if (dtor) { - ecs_size_t size = ti->size; - void *ptr; - int i; - for (i = 0, ptr = value; i < count; i ++, ptr = ECS_OFFSET(ptr, size)) { - dtor(ptr, 1, ti); - } - } + ecs_marked_id_t *m = ecs_vec_append_t(&world->allocator, + &world->store.marked_ids, ecs_marked_id_t); + + m->idr = idr; + m->id = idr->id; + m->action = action; + m->delete_id = delete_id; + + flecs_id_record_claim(world, idr); } static -void flecs_free_cmd_event( +void flecs_id_mark_for_delete( ecs_world_t *world, - ecs_event_desc_t *desc) + ecs_id_record_t *idr, + ecs_entity_t action, + bool delete_id); + +static +void flecs_targets_mark_for_delete( + ecs_world_t *world, + ecs_table_t *table) { - if (desc->ids) { - flecs_stack_free_n(desc->ids->array, ecs_id_t, desc->ids->count); - } + ecs_id_record_t *idr; + const ecs_entity_t *entities = ecs_table_entities(table); + int32_t i, count = ecs_table_count(table); + for (i = 0; i < count; i ++) { + ecs_record_t *r = flecs_entities_get(world, entities[i]); + if (!r) { + continue; + } - /* Safe const cast, command makes a copy of type object */ - flecs_stack_free_t(ECS_CONST_CAST(ecs_type_t*, desc->ids), - ecs_type_t); + /* If entity is not used as id or as relationship target, there won't + * be any tables with a reference to it. */ + ecs_flags32_t flags = r->row & ECS_ROW_FLAGS_MASK; + if (!(flags & (EcsEntityIsId|EcsEntityIsTarget))) { + continue; + } - if (desc->param) { - flecs_dtor_value(world, desc->event, - /* Safe const cast, command makes copy of value */ - ECS_CONST_CAST(void*, desc->param), 1); + ecs_entity_t e = entities[i]; + if (flags & EcsEntityIsId) { + if ((idr = flecs_id_record_get(world, e))) { + flecs_id_mark_for_delete(world, idr, + ECS_ID_ON_DELETE(idr->flags), true); + } + if ((idr = flecs_id_record_get(world, ecs_pair(e, EcsWildcard)))) { + flecs_id_mark_for_delete(world, idr, + ECS_ID_ON_DELETE(idr->flags), true); + } + } + if (flags & EcsEntityIsTarget) { + if ((idr = flecs_id_record_get(world, ecs_pair(EcsWildcard, e)))) { + flecs_id_mark_for_delete(world, idr, + ECS_ID_ON_DELETE_TARGET(idr->flags), true); + } + if ((idr = flecs_id_record_get(world, ecs_pair(EcsFlag, e)))) { + flecs_id_mark_for_delete(world, idr, + ECS_ID_ON_DELETE_TARGET(idr->flags), true); + } + } } } static -void flecs_discard_cmd( - ecs_world_t *world, - ecs_cmd_t *cmd) +bool flecs_id_is_delete_target( + ecs_id_t id, + ecs_entity_t action) { - if (cmd->kind == EcsCmdBulkNew) { - ecs_os_free(cmd->is._n.entities); - } else if (cmd->kind == EcsCmdEvent) { - flecs_free_cmd_event(world, cmd->is._1.value); - } else { - ecs_assert(cmd->kind != EcsCmdEvent, ECS_INTERNAL_ERROR, NULL); - void *value = cmd->is._1.value; - if (value) { - flecs_dtor_value(world, cmd->id, value, 1); - flecs_stack_free(value, cmd->is._1.size); - } + if (!action && ecs_id_is_pair(id) && ECS_PAIR_FIRST(id) == EcsWildcard) { + /* If no explicit delete action is provided, and the id we're deleting + * has the form (*, Target), use OnDeleteTarget action */ + return true; } + return false; } static -bool flecs_remove_invalid( - ecs_world_t *world, - ecs_id_t id, - ecs_id_t *id_out) +ecs_entity_t flecs_get_delete_action( + ecs_table_t *table, + ecs_table_record_t *tr, + ecs_entity_t action, + bool delete_target) { - if (ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_entity_t rel = ECS_PAIR_FIRST(id); - if (!flecs_entities_is_valid(world, rel)) { - /* After relationship is deleted we can no longer see what its - * delete action was, so pretend this never happened */ - *id_out = 0; - return true; - } else { - ecs_entity_t obj = ECS_PAIR_SECOND(id); - if (!flecs_entities_is_valid(world, obj)) { - /* Check the relationship's policy for deleted objects */ - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_pair(rel, EcsWildcard)); - if (idr) { - ecs_entity_t action = ECS_ID_ON_DELETE_TARGET(idr->flags); - if (action == EcsDelete) { - /* Entity should be deleted, don't bother checking - * other ids */ - return false; - } else if (action == EcsPanic) { - /* If policy is throw this object should not have - * been deleted */ - flecs_throw_invalid_delete(world, id); - } else { - *id_out = 0; - return true; + ecs_entity_t result = action; + if (!result && delete_target) { + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_id_t id = idr->id; + + /* If action is not specified and we're deleting a relationship target, + * derive the action from the current record */ + int32_t i = tr->index, count = tr->count; + do { + ecs_type_t *type = &table->type; + ecs_table_record_t *trr = &table->_->records[i]; + ecs_id_record_t *idrr = (ecs_id_record_t*)trr->hdr.cache; + result = ECS_ID_ON_DELETE_TARGET(idrr->flags); + if (result == EcsDelete) { + /* Delete takes precedence over Remove */ + break; + } + + if (count > 1) { + /* If table contains multiple pairs for target they are not + * guaranteed to occupy consecutive elements in the table's type + * vector, so a linear search is needed to find matches. */ + for (++ i; i < type->count; i ++) { + if (ecs_id_match(type->array[i], id)) { + break; } - } else { - *id_out = 0; - return true; } + + /* We should always have as many matching ids as tr->count */ + ecs_assert(i < type->count, ECS_INTERNAL_ERROR, NULL); } - } - } else { - id &= ECS_COMPONENT_MASK; - if (!flecs_entities_is_valid(world, id)) { - /* After relationship is deleted we can no longer see what its - * delete action was, so pretend this never happened */ - *id_out = 0; - return true; - } + } while (--count); } - return true; + return result; } static -ecs_table_t* flecs_cmd_batch_add_diff( +void flecs_update_monitors_for_delete( ecs_world_t *world, - ecs_table_t *dst, - ecs_table_t *cur, - ecs_table_t *prev) + ecs_id_t id) { - int32_t p = 0, p_count = prev->type.count; - int32_t c = 0, c_count = cur->type.count; - ecs_id_t *p_ids = prev->type.array; - ecs_id_t *c_ids = cur->type.array; + flecs_update_component_monitors(world, NULL, &(ecs_type_t){ + .array = (ecs_id_t[]){id}, + .count = 1 + }); +} - for (; c < c_count;) { - ecs_id_t c_id = c_ids[c]; - ecs_id_t p_id = p_ids[p]; +static +void flecs_id_mark_for_delete( + ecs_world_t *world, + ecs_id_record_t *idr, + ecs_entity_t action, + bool delete_id) +{ + if (idr->flags & EcsIdMarkedForDelete) { + return; + } - if (p_id < c_id) { - /* Previous id no longer exists in new table, so it was removed */ - dst = ecs_table_remove_id(world, dst, p_id); - } - if (c_id < p_id) { - /* Current id didn't exist in previous table, so it was added */ - dst = ecs_table_add_id(world, dst, c_id); - } + idr->flags |= EcsIdMarkedForDelete; + flecs_marked_id_push(world, idr, action, delete_id); - c += c_id <= p_id; - p += p_id <= c_id; - if (p == p_count) { - break; + ecs_id_t id = idr->id; + + bool delete_target = flecs_id_is_delete_target(id, action); + + /* Mark all tables with the id for delete */ + ecs_table_cache_iter_t it; + if (flecs_table_cache_iter(&idr->cache, &it)) { + ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + if (table->flags & EcsTableMarkedForDelete) { + continue; + } + + ecs_entity_t cur_action = flecs_get_delete_action(table, tr, action, + delete_target); + + /* If this is a Delete action, recursively mark ids & tables */ + if (cur_action == EcsDelete) { + table->flags |= EcsTableMarkedForDelete; + ecs_log_push_2(); + flecs_targets_mark_for_delete(world, table); + ecs_log_pop_2(); + } else if (cur_action == EcsPanic) { + flecs_throw_invalid_delete(world, id); + } } } - /* Remainder */ - for (; p < p_count; p ++) { - ecs_id_t p_id = p_ids[p]; - dst = ecs_table_remove_id(world, dst, p_id); - } - for (; c < c_count; c ++) { - ecs_id_t c_id = c_ids[c]; - dst = ecs_table_add_id(world, dst, c_id); + /* Same for empty tables */ + if (flecs_table_cache_empty_iter(&idr->cache, &it)) { + ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + tr->hdr.table->flags |= EcsTableMarkedForDelete; + } } - return dst; + /* Signal query cache monitors */ + flecs_update_monitors_for_delete(world, id); + + /* If id is a wildcard pair, update cache monitors for non-wildcard ids */ + if (ecs_id_is_wildcard(id)) { + ecs_assert(ECS_HAS_ID_FLAG(id, PAIR), ECS_INTERNAL_ERROR, NULL); + ecs_id_record_t *cur = idr; + if (ECS_PAIR_SECOND(id) == EcsWildcard) { + while ((cur = cur->first.next)) { + flecs_update_monitors_for_delete(world, cur->id); + } + } else { + ecs_assert(ECS_PAIR_FIRST(id) == EcsWildcard, + ECS_INTERNAL_ERROR, NULL); + while ((cur = cur->second.next)) { + flecs_update_monitors_for_delete(world, cur->id); + } + } + } } static -void flecs_cmd_batch_for_entity( +bool flecs_on_delete_mark( ecs_world_t *world, - ecs_table_diff_builder_t *diff, - ecs_entity_t entity, - ecs_cmd_t *cmds, - int32_t start) + ecs_id_t id, + ecs_entity_t action, + bool delete_id) { - ecs_record_t *r = flecs_entities_get(world, entity); - ecs_table_t *table = NULL; - if (r) { - table = r->table; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + /* If there's no id record, there's nothing to delete */ + return false; } - world->info.cmd.batched_entity_count ++; + if (!action) { + /* If no explicit action is provided, derive it */ + if (!ecs_id_is_pair(id) || ECS_PAIR_SECOND(id) == EcsWildcard) { + /* Delete actions are determined by the component, or in the case + * of a pair by the relationship. */ + action = ECS_ID_ON_DELETE(idr->flags); + } + } - 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 */ - int32_t cur = start; - ecs_id_t id; - bool has_set = false; + if (action == EcsPanic) { + /* This id is protected from deletion */ + flecs_throw_invalid_delete(world, id); + return false; + } - do { - cmd = &cmds[cur]; - id = cmd->id; - next_for_entity = cmd->next_for_entity; - if (next_for_entity < 0) { - /* First command for an entity has a negative index, flip sign */ - next_for_entity *= -1; + flecs_id_mark_for_delete(world, idr, action, delete_id); + + return true; +} + +static +void flecs_remove_from_table( + ecs_world_t *world, + ecs_table_t *table) +{ + ecs_table_diff_t temp_diff = { .added = {0} }; + ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; + flecs_table_diff_builder_init(world, &diff); + ecs_table_t *dst_table = table; + + /* To find the dst table, remove all ids that are marked for deletion */ + int32_t i, t, count = ecs_vec_count(&world->store.marked_ids); + ecs_marked_id_t *ids = ecs_vec_first(&world->store.marked_ids); + const ecs_table_record_t *tr; + for (i = 0; i < count; i ++) { + const ecs_id_record_t *idr = ids[i].idr; + + if (!(tr = flecs_id_record_get_table(idr, dst_table))) { + continue; } - /* Check if added id is still valid (like is the parent of a ChildOf - * pair still alive), if not run cleanup actions for entity */ - if (id) { - if (flecs_remove_invalid(world, id, &id)) { - if (!id) { - /* Entity should remain alive but id should not be added */ - cmd->kind = EcsCmdSkip; - continue; - } - /* Entity should remain alive and id is still valid */ - } else { - /* Id was no longer valid and had a Delete policy */ - cmd->kind = EcsCmdSkip; - ecs_delete(world, entity); - flecs_table_diff_builder_clear(diff); - return; + t = tr->index; + + do { + ecs_id_t id = dst_table->type.array[t]; + ecs_table_t *tgt_table = flecs_table_traverse_remove( + world, dst_table, &id, &temp_diff); + ecs_assert(tgt_table != dst_table, ECS_INTERNAL_ERROR, NULL); + dst_table = tgt_table; + flecs_table_diff_build_append_table(world, &diff, &temp_diff); + } while (dst_table->type.count && (t = ecs_search_offset( + world, dst_table, t, idr->id, NULL)) != -1); + } + + ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); + + if (!dst_table->type.count) { + /* If this removes all components, clear table */ + flecs_table_clear_entities(world, table); + } else { + /* Otherwise, merge table into dst_table */ + if (dst_table != table) { + int32_t table_count = ecs_table_count(table); + if (diff.removed.count && table_count) { + ecs_log_push_3(); + ecs_table_diff_t td; + flecs_table_diff_build_noalloc(&diff, &td); + flecs_notify_on_remove(world, table, NULL, 0, table_count, &td); + ecs_log_pop_3(); } + + flecs_table_merge(world, dst_table, table); } + } - ecs_cmd_kind_t kind = cmd->kind; - switch(kind) { - case EcsCmdAddModified: - /* Add is batched, but keep Modified */ - cmd->kind = EcsCmdModified; + flecs_table_diff_builder_fini(world, &diff); +} - /* fall through */ - case EcsCmdAdd: - table = flecs_find_table_add(world, table, id, diff); - world->info.cmd.batched_command_count ++; - break; - case EcsCmdModified: - 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) { - const ecs_type_info_t *ti = ptr.ti; - ecs_iter_action_t on_set; - if ((on_set = ti->hooks.on_set)) { - ecs_table_t *prev_table = r->table; - ecs_defer_begin(world); - flecs_invoke_hook(world, start_table, 1, row, &entity, - ptr.ptr, cmd->id, ptr.ti, EcsOnSet, on_set); - ecs_defer_end(world); +static +bool flecs_on_delete_clear_tables( + ecs_world_t *world) +{ + /* Iterate in reverse order so that DAGs get deleted bottom to top */ + int32_t i, last = ecs_vec_count(&world->store.marked_ids), first = 0; + ecs_marked_id_t *ids = ecs_vec_first(&world->store.marked_ids); + do { + for (i = last - 1; i >= first; i --) { + ecs_id_record_t *idr = ids[i].idr; + ecs_entity_t action = ids[i].action; - /* Don't run on_set hook twice, but make sure to still - * invoke observers. */ - cmd->kind = EcsCmdModifiedNoHook; + /* Empty all tables for id */ + { + ecs_table_cache_iter_t it; + if (flecs_table_cache_iter(&idr->cache, &it)) { + ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; - /* If entity changed tables in hook, add difference to - * destination table, so we don't lose the side effects - * of the hook. */ - if (r->table != prev_table) { - table = flecs_cmd_batch_add_diff( - world, table, r->table, prev_table); + if ((action == EcsRemove) || + !(table->flags & EcsTableMarkedForDelete)) + { + flecs_remove_from_table(world, table); + } else { + ecs_dbg_3( + "#[red]delete#[reset] entities from table %u", + (uint32_t)table->id); + flecs_table_delete_entities(world, table); } } } } - break; - case EcsCmdSet: - case EcsCmdMut: - table = flecs_find_table_add(world, table, id, diff); - world->info.cmd.batched_command_count ++; - has_set = true; - break; - case EcsCmdEmplace: - /* Don't add for emplace, as this requires a special call to ensure - * the constructor is not invoked for the component */ - break; - case EcsCmdRemove: - table = flecs_find_table_remove(world, table, id, diff); - world->info.cmd.batched_command_count ++; - break; - case EcsCmdClear: - if (table) { - ecs_id_t *ids = ecs_vec_grow_t(&world->allocator, - &diff->removed, ecs_id_t, table->type.count); - ecs_os_memcpy_n(ids, table->type.array, ecs_id_t, - table->type.count); + + /* Run commands so children get notified before parent is deleted */ + if (world->stages[0]->defer) { + flecs_defer_end(world, world->stages[0]); + flecs_defer_begin(world, world->stages[0]); } - table = &world->store.root; - world->info.cmd.batched_command_count ++; - break; - case EcsCmdClone: - case EcsCmdBulkNew: - case EcsCmdPath: - case EcsCmdDelete: - case EcsCmdOnDeleteAction: - case EcsCmdEnable: - case EcsCmdDisable: - case EcsCmdEvent: - case EcsCmdSkip: - case EcsCmdModifiedNoHook: - break; + + /* User code (from triggers) could have enqueued more ids to delete, + * reobtain the array in case it got reallocated */ + ids = ecs_vec_first(&world->store.marked_ids); } - /* Add, remove and clear operations can be skipped since they have no - * side effects besides adding/removing components */ - if (kind == EcsCmdAdd || kind == EcsCmdRemove || kind == EcsCmdClear) { - cmd->kind = EcsCmdSkip; + /* Check if new ids were marked since we started */ + int32_t new_last = ecs_vec_count(&world->store.marked_ids); + if (new_last != last) { + /* Iterate remaining ids */ + ecs_assert(new_last > last, ECS_INTERNAL_ERROR, NULL); + first = last; + last = new_last; + } else { + break; } - } while ((cur = next_for_entity)); + } while (true); - /* Move entity to destination table in single operation */ - flecs_table_diff_build_noalloc(diff, &table_diff); - flecs_defer_begin(world, &world->stages[0]); - flecs_commit(world, entity, r, table, &table_diff, true, 0); - flecs_defer_end(world, &world->stages[0]); - flecs_table_diff_builder_clear(diff); + return true; +} - /* If the batch contains set commands, copy the component value from the - * temporary command storage to the actual component storage before OnSet - * observers are invoked. This ensures that for multi-component OnSet - * observers all components are assigned a valid value before the observer - * is invoked. - * This only happens for entities that didn't have the assigned component - * yet, as for entities that did have the component already the value will - * have been assigned directly to the component storage. */ - if (has_set) { - cur = start; - do { - cmd = &cmds[cur]; - next_for_entity = cmd->next_for_entity; - if (next_for_entity < 0) { - next_for_entity *= -1; - } - switch(cmd->kind) { - case EcsCmdSet: - case EcsCmdMut: { - flecs_component_ptr_t ptr = {0}; - if (r->table) { - ptr = flecs_get_component_ptr(world, - r->table, ECS_RECORD_TO_ROW(r->row), cmd->id); +static +bool flecs_on_delete_clear_ids( + ecs_world_t *world) +{ + int32_t i, count = ecs_vec_count(&world->store.marked_ids); + ecs_marked_id_t *ids = ecs_vec_first(&world->store.marked_ids); + int twice = 2; + + do { + for (i = 0; i < count; i ++) { + /* Release normal ids before wildcard ids */ + if (ecs_id_is_wildcard(ids[i].id)) { + if (twice == 2) { + continue; + } + } else { + if (twice == 1) { + continue; } + } - /* It's possible that even though the component was set, the - * command queue also contained a remove command, so before we - * do anything ensure the entity actually has the component. */ - if (ptr.ptr) { - const ecs_type_info_t *ti = ptr.ti; - ecs_move_t move = ti->hooks.move; - if (move) { - move(ptr.ptr, cmd->is._1.value, 1, ti); - ecs_xtor_t dtor = ti->hooks.dtor; - if (dtor) { - dtor(cmd->is._1.value, 1, ti); - cmd->is._1.value = NULL; - } - } else { - ecs_os_memcpy(ptr.ptr, cmd->is._1.value, ti->size); - } - if (cmd->kind == EcsCmdSet) { - /* A set operation is add + copy + modified. We just did - * the add the copy, so the only thing that's left is a - * modified command, which will call the OnSet - * observers. */ - cmd->kind = EcsCmdModified; - } else { - /* If this was a get_mut, nothing's left to be done */ - cmd->kind = EcsCmdSkip; - } + ecs_id_record_t *idr = ids[i].idr; + bool delete_id = ids[i].delete_id; + + flecs_id_record_release_tables(world, idr); + + /* Release the claim taken by flecs_marked_id_push. This may delete the + * id record as all other claims may have been released. */ + int32_t rc = flecs_id_record_release(world, idr); + ecs_assert(rc >= 0, ECS_INTERNAL_ERROR, NULL); + (void)rc; + + /* If rc is 0, the id was likely deleted by a nested delete_with call + * made by an on_remove handler/OnRemove observer */ + if (rc) { + if (delete_id) { + /* If id should be deleted, release initial claim. This happens when + * a component, tag, or part of a pair is deleted. */ + flecs_id_record_release(world, idr); } else { - /* The entity no longer has the component which means that - * there was a remove command for the component in the - * command queue. In that case skip the command. */ - cmd->kind = EcsCmdSkip; + /* If id should not be deleted, unmark id record for deletion. This + * happens when all instances *of* an id are deleted, for example + * when calling ecs_remove_all or ecs_delete_with. */ + idr->flags &= ~EcsIdMarkedForDelete; } - break; - } - case EcsCmdClone: - case EcsCmdBulkNew: - case EcsCmdAdd: - case EcsCmdRemove: - case EcsCmdEmplace: - case EcsCmdModified: - case EcsCmdModifiedNoHook: - case EcsCmdAddModified: - case EcsCmdPath: - case EcsCmdDelete: - case EcsCmdClear: - case EcsCmdOnDeleteAction: - case EcsCmdEnable: - case EcsCmdDisable: - case EcsCmdEvent: - case EcsCmdSkip: - break; } - } while ((cur = next_for_entity)); - } + } + } while (-- twice); + + return true; } -/* Leave safe section. Run all deferred commands. */ -bool flecs_defer_end( +static +void flecs_on_delete( ecs_world_t *world, - ecs_stage_t *stage) + ecs_id_t id, + ecs_entity_t action, + bool delete_id) { - ecs_poly_assert(world, ecs_world_t); - ecs_poly_assert(stage, ecs_stage_t); + /* Cleanup can happen recursively. If a cleanup action is already in + * progress, only append ids to the marked_ids. The topmost cleanup + * frame will handle the actual cleanup. */ + int32_t i, count = ecs_vec_count(&world->store.marked_ids); - if (stage->defer < 0) { - /* Defer suspending makes it possible to do operations on the storage - * without flushing the commands in the queue */ - return false; - } + /* Make sure we're evaluating a consistent list of non-empty tables */ + ecs_run_aperiodic(world, EcsAperiodicEmptyTables); - ecs_assert(stage->defer > 0, ECS_INTERNAL_ERROR, NULL); + /* Collect all ids that need to be deleted */ + flecs_on_delete_mark(world, id, action, delete_id); - if (!--stage->defer) { - /* Test whether we're flushing to another queue or whether we're - * flushing to the storage */ - bool merge_to_world = false; - if (ecs_poly_is(world, ecs_world_t)) { - merge_to_world = world->stages[0].defer == 0; - } + /* Only perform cleanup if we're the first stack frame doing it */ + if (!count && ecs_vec_count(&world->store.marked_ids)) { + ecs_dbg_2("#[red]delete#[reset]"); + ecs_log_push_2(); - ecs_stage_t *dst_stage = flecs_stage_from_world(&world); - ecs_commands_t *commands = stage->cmd; - ecs_vec_t *queue = &commands->queue; + /* Empty tables with all the to be deleted ids */ + flecs_on_delete_clear_tables(world); - if (ecs_vec_count(queue)) { - ecs_cmd_t *cmds = ecs_vec_first(queue); - int32_t i, count = ecs_vec_count(queue); + /* All marked tables are empty, ensure they're in the right list */ + ecs_run_aperiodic(world, EcsAperiodicEmptyTables); - ecs_table_diff_builder_t diff; - flecs_table_diff_builder_init(world, &diff); - flecs_commands_push(stage); + /* Release remaining references to the ids */ + flecs_on_delete_clear_ids(world); - for (i = 0; i < count; i ++) { - ecs_cmd_t *cmd = &cmds[i]; - ecs_entity_t e = cmd->entity; - bool is_alive = flecs_entities_is_alive(world, e); + /* Verify deleted ids are no longer in use */ +#ifdef FLECS_DEBUG + ecs_marked_id_t *ids = ecs_vec_first(&world->store.marked_ids); + count = ecs_vec_count(&world->store.marked_ids); + for (i = 0; i < count; i ++) { + ecs_assert(!ecs_id_in_use(world, ids[i].id), + ECS_INTERNAL_ERROR, NULL); + } +#endif + ecs_assert(!ecs_id_in_use(world, id), ECS_INTERNAL_ERROR, NULL); - /* A negative index indicates the first command for an entity */ - if (merge_to_world && (cmd->next_for_entity < 0)) { - /* Batch commands for entity to limit archetype moves */ - if (is_alive) { - flecs_cmd_batch_for_entity(world, &diff, e, cmds, i); - } else { - world->info.cmd.discard_count ++; - } - } + /* Ids are deleted, clear stack */ + ecs_vec_clear(&world->store.marked_ids); - /* Invalidate entry */ - if (cmd->entry) { - cmd->entry->first = -1; - } + /* If any components got deleted, cleanup type info. Delaying this + * ensures that type info remains available during cleanup. */ + count = ecs_vec_count(&world->store.deleted_components); + ecs_entity_t *comps = ecs_vec_first(&world->store.deleted_components); + for (i = 0; i < count; i ++) { + flecs_type_info_free(world, comps[i]); + } - /* If entity is no longer alive, this could be because the queue - * contained both a delete and a subsequent add/remove/set which - * should be ignored. */ - ecs_cmd_kind_t kind = cmd->kind; - if ((kind != EcsCmdPath) && ((kind == EcsCmdSkip) || (e && !is_alive))) { - world->info.cmd.discard_count ++; - flecs_discard_cmd(world, cmd); - continue; - } + ecs_vec_clear(&world->store.deleted_components); - ecs_id_t id = cmd->id; + ecs_log_pop_2(); + } +} - switch(kind) { - case EcsCmdAdd: - ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); - if (flecs_remove_invalid(world, id, &id)) { - if (id) { - world->info.cmd.add_count ++; - flecs_add_id(world, e, id); - } else { - world->info.cmd.discard_count ++; - } - } else { - world->info.cmd.discard_count ++; - ecs_delete(world, e); - } - break; - case EcsCmdRemove: - flecs_remove_id(world, e, id); - world->info.cmd.remove_count ++; - break; - case EcsCmdClone: - ecs_clone(world, e, id, cmd->is._1.clone_value); - break; - case EcsCmdSet: - flecs_move_ptr_w_id(world, dst_stage, e, - cmd->id, flecs_itosize(cmd->is._1.size), - cmd->is._1.value, kind); - world->info.cmd.set_count ++; - break; - case EcsCmdEmplace: - if (merge_to_world) { - ecs_emplace_id(world, e, id); - } - flecs_move_ptr_w_id(world, dst_stage, e, - cmd->id, flecs_itosize(cmd->is._1.size), - cmd->is._1.value, kind); - world->info.cmd.get_mut_count ++; - break; - case EcsCmdMut: - flecs_move_ptr_w_id(world, dst_stage, e, - cmd->id, flecs_itosize(cmd->is._1.size), - cmd->is._1.value, kind); - world->info.cmd.get_mut_count ++; - break; - case EcsCmdModified: - flecs_modified_id_if(world, e, id, true); - world->info.cmd.modified_count ++; - break; - case EcsCmdModifiedNoHook: - flecs_modified_id_if(world, e, id, false); - world->info.cmd.modified_count ++; - break; - case EcsCmdAddModified: - flecs_add_id(world, e, id); - flecs_modified_id_if(world, e, id, true); - world->info.cmd.add_count ++; - world->info.cmd.modified_count ++; - break; - case EcsCmdDelete: { - ecs_delete(world, e); - world->info.cmd.delete_count ++; - break; - } - case EcsCmdClear: - ecs_clear(world, e); - world->info.cmd.clear_count ++; - break; - case EcsCmdOnDeleteAction: - ecs_defer_begin(world); - flecs_on_delete(world, id, e, false); - ecs_defer_end(world); - world->info.cmd.other_count ++; - break; - case EcsCmdEnable: - ecs_enable_id(world, e, id, true); - world->info.cmd.other_count ++; - break; - case EcsCmdDisable: - ecs_enable_id(world, e, id, false); - world->info.cmd.other_count ++; - break; - case EcsCmdBulkNew: - flecs_flush_bulk_new(world, cmd); - world->info.cmd.other_count ++; - continue; - case EcsCmdPath: - ecs_ensure(world, e); - if (cmd->id) { - ecs_add_pair(world, e, EcsChildOf, cmd->id); - } - ecs_set_name(world, e, cmd->is._1.value); - ecs_os_free(cmd->is._1.value); - cmd->is._1.value = NULL; - break; - case EcsCmdEvent: { - ecs_event_desc_t *desc = cmd->is._1.value; - ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_emit((ecs_world_t*)stage, desc); - flecs_free_cmd_event(world, desc); - break; - } - case EcsCmdSkip: - break; - } - - if (cmd->is._1.value) { - flecs_stack_free(cmd->is._1.value, cmd->is._1.size); - } - } - - flecs_stack_reset(&commands->stack); - ecs_vec_clear(queue); - flecs_commands_pop(stage); - - flecs_table_diff_builder_fini(world, &diff); - } +void ecs_delete_with( + ecs_world_t *world, + ecs_id_t id) +{ + flecs_journal_begin(world, EcsJournalDeleteWith, id, NULL, NULL); - return true; + ecs_stage_t *stage = flecs_stage_from_world(&world); + if (flecs_defer_on_delete_action(stage, id, EcsDelete)) { + return; } - return false; + flecs_on_delete(world, id, EcsDelete, false); + flecs_defer_end(world, stage); + + flecs_journal_end(); } -/* Delete operations from queue without executing them. */ -bool flecs_defer_purge( +void ecs_remove_all( ecs_world_t *world, - ecs_stage_t *stage) + ecs_id_t id) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_journal_begin(world, EcsJournalRemoveAll, id, NULL, NULL); - if (!--stage->defer) { - ecs_vec_t commands = stage->cmd->queue; + ecs_stage_t *stage = flecs_stage_from_world(&world); + if (flecs_defer_on_delete_action(stage, id, EcsRemove)) { + return; + } - if (ecs_vec_count(&commands)) { - ecs_cmd_t *cmds = ecs_vec_first(&commands); - int32_t i, count = ecs_vec_count(&commands); - for (i = 0; i < count; i ++) { - flecs_discard_cmd(world, &cmds[i]); - } + flecs_on_delete(world, id, EcsRemove, false); + flecs_defer_end(world, stage); - ecs_vec_fini_t(&stage->allocator, &stage->cmd->queue, ecs_cmd_t); + flecs_journal_end(); +} - ecs_vec_clear(&commands); - flecs_stack_reset(&stage->cmd->stack); - flecs_sparse_clear(&stage->cmd->entries); - } +void ecs_delete( + ecs_world_t *world, + ecs_entity_t entity) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); - return true; + ecs_stage_t *stage = flecs_stage_from_world(&world); + if (flecs_defer_delete(stage, entity)) { + return; } -error: - return false; -} - -/** - * @file entity_filter.c - * @brief Filters that are applied to entities in a table. - * - * After a table has been matched by a query, additional filters may have to - * be applied before returning entities to the application. The two scenarios - * under which this happens are queries for union relationship pairs (entities - * for multiple targets are stored in the same table) and toggles (components - * that are enabled/disabled with a bitset). - */ - + ecs_os_perf_trace_push("flecs.delete"); -static -int flecs_entity_filter_find_smallest_term( - ecs_table_t *table, - ecs_entity_filter_iter_t *iter) -{ - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_switch_term_t *sw_terms = ecs_vec_first(&iter->entity_filter->sw_terms); - int32_t i, count = ecs_vec_count(&iter->entity_filter->sw_terms); - int32_t min = INT_MAX, index = 0; + ecs_record_t *r = flecs_entities_try(world, entity); + if (r) { + flecs_journal_begin(world, EcsJournalDelete, entity, NULL, NULL); - for (i = 0; i < count; i ++) { - /* The array with sparse queries for the matched table */ - flecs_switch_term_t *sparse_column = &sw_terms[i]; + ecs_flags32_t row_flags = ECS_RECORD_TO_ROW_FLAGS(r->row); + ecs_table_t *table; + if (row_flags) { + if (row_flags & EcsEntityIsTarget) { + 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 & EcsEntityIsId) { + flecs_on_delete(world, entity, 0, true); + flecs_on_delete(world, ecs_pair(entity, EcsWildcard), 0, true); + } + if (row_flags & EcsEntityIsTraversable) { + table = r->table; + if (table) { + flecs_table_traversable_add(table, -1); + } + } + /* Merge operations before deleting entity */ + flecs_defer_end(world, stage); + flecs_defer_begin(world, stage); + } - /* Pointer to the switch column struct of the table */ - ecs_switch_t *sw = sparse_column->sw_column; + table = r->table; - /* If the sparse column pointer hadn't been retrieved yet, do it now */ - if (!sw) { - /* Get the table column index from the signature column index */ - int32_t table_column_index = iter->columns[ - sparse_column->signature_column_index]; + if (table) { + ecs_table_diff_t diff = { + .removed = table->type, + .removed_flags = table->flags & EcsTableRemoveEdgeFlags + }; - /* Translate the table column index to switch column index */ - table_column_index -= table->_->sw_offset; - ecs_assert(table_column_index >= 1, ECS_INTERNAL_ERROR, NULL); + flecs_delete_entity(world, r, &diff); - /* Get the sparse column */ - sw = sparse_column->sw_column = - &table->_->sw_columns[table_column_index - 1]; + r->row = 0; + r->table = NULL; } + + flecs_entities_remove(world, entity); - /* Find the smallest column */ - int32_t case_count = flecs_switch_case_count(sw, sparse_column->sw_case); - if (case_count < min) { - min = case_count; - index = i + 1; - } + flecs_journal_end(); } - return index; + flecs_defer_end(world, stage); +error: + ecs_os_perf_trace_pop("flecs.delete"); + return; } -static -int flecs_entity_filter_switch_next( - ecs_table_t *table, - ecs_entity_filter_iter_t *iter, - bool filter) +void ecs_add_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) { - bool first_iteration = false; - int32_t switch_smallest; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + flecs_add_id(world, entity, id); +error: + return; +} - if (!(switch_smallest = iter->sw_smallest)) { - switch_smallest = iter->sw_smallest = - flecs_entity_filter_find_smallest_term(table, iter); - first_iteration = true; - } +void ecs_remove_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id) || ecs_id_is_wildcard(id), + ECS_INVALID_PARAMETER, NULL); + flecs_remove_id(world, entity, id); +error: + return; +} - switch_smallest -= 1; +void ecs_auto_override_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + ecs_add_id(world, entity, ECS_AUTO_OVERRIDE | id); +error: + return; +} - flecs_switch_term_t *columns = ecs_vec_first(&iter->entity_filter->sw_terms); - flecs_switch_term_t *column = &columns[switch_smallest]; - ecs_switch_t *sw, *sw_smallest = column->sw_column; - ecs_entity_t case_smallest = column->sw_case; +ecs_entity_t ecs_clone( + ecs_world_t *world, + ecs_entity_t dst, + ecs_entity_t src, + bool copy_value) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(src != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, src), ECS_INVALID_PARAMETER, NULL); + ecs_check(!dst || !ecs_get_table(world, dst), ECS_INVALID_PARAMETER, NULL); - /* Find next entity to iterate in sparse column */ - int32_t first, sparse_first = iter->sw_offset; + ecs_stage_t *stage = flecs_stage_from_world(&world); + if (!dst) { + dst = ecs_new(world); + } - if (!filter) { - if (first_iteration) { - first = flecs_switch_first(sw_smallest, case_smallest); - } else { - first = flecs_switch_next(sw_smallest, sparse_first); - } - } else { - int32_t cur_first = iter->range.offset, cur_count = iter->range.count; - first = cur_first; - while (flecs_switch_get(sw_smallest, first) != case_smallest) { - first ++; - if (first >= (cur_first + cur_count)) { - first = -1; - break; - } - } + if (flecs_defer_clone(stage, dst, src, copy_value)) { + return dst; } - if (first == -1) { + ecs_record_t *src_r = flecs_entities_get(world, src); + ecs_assert(src_r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *src_table = src_r->table; + if (!src_table) { goto done; } - /* Check if entity matches with other sparse columns, if any */ - int32_t i, count = ecs_vec_count(&iter->entity_filter->sw_terms); - do { - for (i = 0; i < count; i ++) { - if (i == switch_smallest) { - /* Already validated this one */ - continue; - } + ecs_table_t *dst_table = src_table; + if (src_table->flags & EcsTableHasName) { + dst_table = ecs_table_remove_id(world, src_table, + ecs_pair_t(EcsIdentifier, EcsName)); + } - column = &columns[i]; - sw = column->sw_column; + ecs_type_t dst_type = dst_table->type; + ecs_table_diff_t diff = { + .added = dst_type, + .added_flags = dst_table->flags & EcsTableAddEdgeFlags + }; + ecs_record_t *dst_r = flecs_entities_get(world, dst); + /* Note 'ctor' parameter below is set to 'false' if the value will be copied, since flecs_table_move + will call a copy constructor */ + flecs_new_entity(world, dst, dst_r, dst_table, &diff, !copy_value, 0); + int32_t row = ECS_RECORD_TO_ROW(dst_r->row); - if (flecs_switch_get(sw, first) != column->sw_case) { - first = flecs_switch_next(sw_smallest, first); - if (first == -1) { - goto done; - } - } + if (copy_value) { + flecs_table_move(world, dst, src, dst_table, + row, src_table, ECS_RECORD_TO_ROW(src_r->row), true); + int32_t i, count = dst_table->column_count; + for (i = 0; i < count; i ++) { + ecs_id_t id = flecs_column_id(dst_table, i); + ecs_type_t type = { + .array = &id, + .count = 1 + }; + flecs_notify_on_set(world, dst_table, row, 1, &type, true); } - } while (i != count); - - iter->range.offset = iter->sw_offset = first; - iter->range.count = 1; + } - return 0; done: - /* Iterated all elements in the sparse list, we should move to the - * next matched table. */ - iter->sw_smallest = 0; - iter->sw_offset = 0; - - return -1; + flecs_defer_end(world, stage); + return dst; +error: + return 0; } -#define BS_MAX ((uint64_t)0xFFFFFFFFFFFFFFFF) - -static -int flecs_entity_filter_bitset_next( - ecs_table_t *table, - ecs_entity_filter_iter_t *iter) -{ - /* Precomputed single-bit test */ - static const uint64_t bitmask[64] = { - (uint64_t)1 << 0, (uint64_t)1 << 1, (uint64_t)1 << 2, (uint64_t)1 << 3, - (uint64_t)1 << 4, (uint64_t)1 << 5, (uint64_t)1 << 6, (uint64_t)1 << 7, - (uint64_t)1 << 8, (uint64_t)1 << 9, (uint64_t)1 << 10, (uint64_t)1 << 11, - (uint64_t)1 << 12, (uint64_t)1 << 13, (uint64_t)1 << 14, (uint64_t)1 << 15, - (uint64_t)1 << 16, (uint64_t)1 << 17, (uint64_t)1 << 18, (uint64_t)1 << 19, - (uint64_t)1 << 20, (uint64_t)1 << 21, (uint64_t)1 << 22, (uint64_t)1 << 23, - (uint64_t)1 << 24, (uint64_t)1 << 25, (uint64_t)1 << 26, (uint64_t)1 << 27, - (uint64_t)1 << 28, (uint64_t)1 << 29, (uint64_t)1 << 30, (uint64_t)1 << 31, - (uint64_t)1 << 32, (uint64_t)1 << 33, (uint64_t)1 << 34, (uint64_t)1 << 35, - (uint64_t)1 << 36, (uint64_t)1 << 37, (uint64_t)1 << 38, (uint64_t)1 << 39, - (uint64_t)1 << 40, (uint64_t)1 << 41, (uint64_t)1 << 42, (uint64_t)1 << 43, - (uint64_t)1 << 44, (uint64_t)1 << 45, (uint64_t)1 << 46, (uint64_t)1 << 47, - (uint64_t)1 << 48, (uint64_t)1 << 49, (uint64_t)1 << 50, (uint64_t)1 << 51, - (uint64_t)1 << 52, (uint64_t)1 << 53, (uint64_t)1 << 54, (uint64_t)1 << 55, - (uint64_t)1 << 56, (uint64_t)1 << 57, (uint64_t)1 << 58, (uint64_t)1 << 59, - (uint64_t)1 << 60, (uint64_t)1 << 61, (uint64_t)1 << 62, (uint64_t)1 << 63 - }; +#define ecs_get_low_id(table, r, id)\ + ecs_assert(table->component_map != NULL, ECS_INTERNAL_ERROR, NULL);\ + int16_t column_index = table->component_map[id];\ + if (column_index > 0) {\ + ecs_column_t *column = &table->data.columns[column_index - 1];\ + return ECS_ELEM(column->data, column->ti->size, \ + ECS_RECORD_TO_ROW(r->row));\ + } else if (column_index < 0) {\ + column_index = flecs_ito(int16_t, -column_index - 1);\ + const ecs_table_record_t *tr = &table->_->records[column_index];\ + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache;\ + if (idr->flags & EcsIdIsSparse) {\ + return flecs_sparse_get_any(idr->sparse, 0, entity);\ + }\ + } - /* Precomputed test to verify if remainder of block is set (or not) */ - static const uint64_t bitmask_remain[64] = { - BS_MAX, BS_MAX - (BS_MAX >> 63), BS_MAX - (BS_MAX >> 62), - BS_MAX - (BS_MAX >> 61), BS_MAX - (BS_MAX >> 60), BS_MAX - (BS_MAX >> 59), - BS_MAX - (BS_MAX >> 58), BS_MAX - (BS_MAX >> 57), BS_MAX - (BS_MAX >> 56), - BS_MAX - (BS_MAX >> 55), BS_MAX - (BS_MAX >> 54), BS_MAX - (BS_MAX >> 53), - BS_MAX - (BS_MAX >> 52), BS_MAX - (BS_MAX >> 51), BS_MAX - (BS_MAX >> 50), - BS_MAX - (BS_MAX >> 49), BS_MAX - (BS_MAX >> 48), BS_MAX - (BS_MAX >> 47), - BS_MAX - (BS_MAX >> 46), BS_MAX - (BS_MAX >> 45), BS_MAX - (BS_MAX >> 44), - BS_MAX - (BS_MAX >> 43), BS_MAX - (BS_MAX >> 42), BS_MAX - (BS_MAX >> 41), - BS_MAX - (BS_MAX >> 40), BS_MAX - (BS_MAX >> 39), BS_MAX - (BS_MAX >> 38), - BS_MAX - (BS_MAX >> 37), BS_MAX - (BS_MAX >> 36), BS_MAX - (BS_MAX >> 35), - BS_MAX - (BS_MAX >> 34), BS_MAX - (BS_MAX >> 33), BS_MAX - (BS_MAX >> 32), - BS_MAX - (BS_MAX >> 31), BS_MAX - (BS_MAX >> 30), BS_MAX - (BS_MAX >> 29), - BS_MAX - (BS_MAX >> 28), BS_MAX - (BS_MAX >> 27), BS_MAX - (BS_MAX >> 26), - BS_MAX - (BS_MAX >> 25), BS_MAX - (BS_MAX >> 24), BS_MAX - (BS_MAX >> 23), - BS_MAX - (BS_MAX >> 22), BS_MAX - (BS_MAX >> 21), BS_MAX - (BS_MAX >> 20), - BS_MAX - (BS_MAX >> 19), BS_MAX - (BS_MAX >> 18), BS_MAX - (BS_MAX >> 17), - BS_MAX - (BS_MAX >> 16), BS_MAX - (BS_MAX >> 15), BS_MAX - (BS_MAX >> 14), - BS_MAX - (BS_MAX >> 13), BS_MAX - (BS_MAX >> 12), BS_MAX - (BS_MAX >> 11), - BS_MAX - (BS_MAX >> 10), BS_MAX - (BS_MAX >> 9), BS_MAX - (BS_MAX >> 8), - BS_MAX - (BS_MAX >> 7), BS_MAX - (BS_MAX >> 6), BS_MAX - (BS_MAX >> 5), - BS_MAX - (BS_MAX >> 4), BS_MAX - (BS_MAX >> 3), BS_MAX - (BS_MAX >> 2), - BS_MAX - (BS_MAX >> 1) - }; +const void* ecs_get_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - int32_t i, count = ecs_vec_count(&iter->entity_filter->bs_terms); - flecs_bitset_term_t *terms = ecs_vec_first(&iter->entity_filter->bs_terms); - int32_t bs_offset = table->_->bs_offset; - int32_t first = iter->bs_offset; - int32_t last = 0; + world = ecs_get_world(world); - for (i = 0; i < count; i ++) { - flecs_bitset_term_t *column = &terms[i]; - ecs_bitset_t *bs = terms[i].bs_column; + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_assert(r != NULL, ECS_INVALID_PARAMETER, NULL); - if (!bs) { - int32_t index = column->column_index; - ecs_assert((index - bs_offset >= 0), ECS_INTERNAL_ERROR, NULL); - bs = &table->_->bs_columns[index - bs_offset]; - terms[i].bs_column = bs; - } - - int32_t bs_elem_count = bs->count; - int32_t bs_block = first >> 6; - int32_t bs_block_count = ((bs_elem_count - 1) >> 6) + 1; + ecs_table_t *table = r->table; + if (!table) { + return NULL; + } - if (bs_block >= bs_block_count) { - goto done; + if (id < FLECS_HI_COMPONENT_ID) { + ecs_get_low_id(table, r, id); + if (!(table->flags & EcsTableHasIsA)) { + return NULL; } + } - uint64_t *data = bs->data; - int32_t bs_start = first & 0x3F; - - /* Step 1: find the first non-empty block */ - uint64_t v = data[bs_block]; - uint64_t remain = bitmask_remain[bs_start]; - while (!(v & remain)) { - /* If no elements are remaining, move to next block */ - if ((++bs_block) >= bs_block_count) { - /* No non-empty blocks left */ - goto done; - } + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return NULL; + } - bs_start = 0; - remain = BS_MAX; /* Test the full block */ - v = data[bs_block]; + const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return flecs_get_base_component(world, table, id, idr, 0); + } else { + if (idr->flags & EcsIdIsSparse) { + return flecs_sparse_get_any(idr->sparse, 0, entity); } + ecs_check(tr->column != -1, ECS_NOT_A_COMPONENT, NULL); + } - /* Step 2: find the first non-empty element in the block */ - while (!(v & bitmask[bs_start])) { - bs_start ++; - - /* Block was not empty, so bs_start must be smaller than 64 */ - ecs_assert(bs_start < 64, ECS_INTERNAL_ERROR, NULL); - } + int32_t row = ECS_RECORD_TO_ROW(r->row); + return flecs_table_get_component(table, tr->column, row).ptr; +error: + return NULL; +} - /* Step 3: Find number of contiguous enabled elements after start */ - int32_t bs_end = bs_start, bs_block_end = bs_block; +void* ecs_get_mut_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - remain = bitmask_remain[bs_end]; - while ((v & remain) == remain) { - bs_end = 0; - bs_block_end ++; + world = ecs_get_world(world); - if (bs_block_end == bs_block_count) { - break; - } + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_assert(r != NULL, ECS_INVALID_PARAMETER, NULL); - v = data[bs_block_end]; - remain = BS_MAX; /* Test the full block */ - } + ecs_table_t *table = r->table; + if (!table) { + return NULL; + } - /* Step 4: find remainder of enabled elements in current block */ - if (bs_block_end != bs_block_count) { - while ((v & bitmask[bs_end])) { - bs_end ++; - } - } + if (id < FLECS_HI_COMPONENT_ID) { + ecs_get_low_id(table, r, id); + return NULL; + } - /* Block was not 100% occupied, so bs_start must be smaller than 64 */ - ecs_assert(bs_end < 64, ECS_INTERNAL_ERROR, NULL); + ecs_id_record_t *idr = flecs_id_record_get(world, id); + int32_t row = ECS_RECORD_TO_ROW(r->row); + return flecs_get_component_ptr(table, row, idr).ptr; +error: + return NULL; +} - /* Step 5: translate to element start/end and make sure that each column - * range is a subset of the previous one. */ - first = bs_block * 64 + bs_start; - int32_t cur_last = bs_block_end * 64 + bs_end; - - /* No enabled elements found in table */ - if (first == cur_last) { - goto done; - } +void* ecs_ensure_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - /* If multiple bitsets are evaluated, make sure each subsequent range - * is equal or a subset of the previous range */ - if (i) { - /* If the first element of a subsequent bitset is larger than the - * previous last value, start over. */ - if (first >= last) { - i = -1; - continue; - } + ecs_stage_t *stage = flecs_stage_from_world(&world); + if (flecs_defer_cmd(stage)) { + return flecs_defer_set( + world, stage, EcsCmdEnsure, entity, id, 0, NULL, NULL); + } - /* Make sure the last element of the range doesn't exceed the last - * element of the previous range. */ - if (cur_last > last) { - cur_last = last; - } - } + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + void *result = flecs_ensure(world, entity, id, r).ptr; + ecs_check(result != NULL, ECS_INVALID_PARAMETER, NULL); - last = cur_last; - int32_t elem_count = last - first; + flecs_defer_end(world, stage); + return result; +error: + return NULL; +} - /* Make sure last element doesn't exceed total number of elements in - * the table */ - if (elem_count > (bs_elem_count - first)) { - elem_count = (bs_elem_count - first); - if (!elem_count) { - iter->bs_offset = 0; - goto done; - } - } - - iter->range.offset = first; - iter->range.count = elem_count; - iter->bs_offset = first; - } +void* ecs_ensure_modified_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - /* Keep track of last processed element for iteration */ - iter->bs_offset = last; + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_check(flecs_defer_cmd(stage), ECS_INVALID_PARAMETER, NULL); - return 0; -done: - iter->sw_smallest = 0; - iter->sw_offset = 0; - return -1; + return flecs_defer_set(world, stage, EcsCmdSet, entity, id, 0, NULL, NULL); +error: + return NULL; } -#undef BS_MAX - static -int32_t flecs_get_flattened_target( - ecs_world_t *world, - EcsTarget *cur, - ecs_entity_t rel, - ecs_id_t id, - ecs_entity_t *src_out, - ecs_table_record_t **tr_out) +ecs_record_t* flecs_access_begin( + ecs_world_t *stage, + ecs_entity_t entity, + bool write) { - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - return -1; - } + ecs_check(ecs_os_has_threading(), ECS_MISSING_OS_API, NULL); - ecs_record_t *r = cur->target; + const ecs_world_t *world = ecs_get_world(stage); + ecs_record_t *r = flecs_entities_get(world, entity); ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_t *table = r->table; - if (!table) { - return -1; + ecs_table_t *table; + if (!(table = r->table)) { + return NULL; } - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (tr) { - *src_out = ecs_record_get_entity(r); - *tr_out = tr; - return tr->index; + int32_t count = ecs_os_ainc(&table->_->lock); + (void)count; + if (write) { + ecs_check(count == 1, ECS_ACCESS_VIOLATION, NULL); } - if (table->flags & EcsTableHasTarget) { - int32_t col = table->column_map[table->_->ft_offset]; - ecs_assert(col != -1, ECS_INTERNAL_ERROR, NULL); - EcsTarget *next = table->data.columns[col].data.array; - next = ECS_ELEM_T(next, EcsTarget, ECS_RECORD_TO_ROW(r->row)); - return flecs_get_flattened_target( - world, next, rel, id, src_out, tr_out); + return r; +error: + return NULL; +} + +static +void flecs_access_end( + const ecs_record_t *r, + bool write) +{ + ecs_check(ecs_os_has_threading(), ECS_MISSING_OS_API, NULL); + ecs_check(r != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(r->table != NULL, ECS_INVALID_PARAMETER, NULL); + int32_t count = ecs_os_adec(&r->table->_->lock); + (void)count; + if (write) { + ecs_check(count == 0, ECS_ACCESS_VIOLATION, NULL); } + ecs_check(count >= 0, ECS_ACCESS_VIOLATION, NULL); - return ecs_search_relation( - world, table, 0, id, rel, EcsSelf|EcsUp, src_out, NULL, tr_out); +error: + return; } -void flecs_entity_filter_init( +ecs_record_t* ecs_write_begin( ecs_world_t *world, - ecs_entity_filter_t **entity_filter, - const ecs_filter_t *filter, - const ecs_table_t *table, - ecs_id_t *ids, - int32_t *columns) + ecs_entity_t entity) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(entity_filter != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(filter != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ids != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(columns != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_allocator_t *a = &world->allocator; - ecs_entity_filter_t ef; - ecs_os_zeromem(&ef); - ecs_vec_t *sw_terms = &ef.sw_terms; - ecs_vec_t *bs_terms = &ef.bs_terms; - ecs_vec_t *ft_terms = &ef.ft_terms; - if (*entity_filter) { - ef.sw_terms = (*entity_filter)->sw_terms; - ef.bs_terms = (*entity_filter)->bs_terms; - ef.ft_terms = (*entity_filter)->ft_terms; - } - ecs_vec_reset_t(a, sw_terms, flecs_switch_term_t); - ecs_vec_reset_t(a, bs_terms, flecs_bitset_term_t); - ecs_vec_reset_t(a, ft_terms, flecs_flat_table_term_t); - ecs_term_t *terms = filter->terms; - int32_t i, term_count = filter->term_count; - bool has_filter = false; - ef.flat_tree_column = -1; - - /* Look for union fields */ - if (table->flags & EcsTableHasUnion) { - for (i = 0; i < term_count; i ++) { - if (ecs_term_match_0(&terms[i])) { - continue; - } + return flecs_access_begin(world, entity, true); +} - ecs_id_t id = terms[i].id; - if (ECS_HAS_ID_FLAG(id, PAIR) && ECS_PAIR_SECOND(id) == EcsWildcard) { - continue; - } - - int32_t field = terms[i].field_index; - int32_t column = columns[field]; - if (column <= 0) { - continue; - } +void ecs_write_end( + ecs_record_t *r) +{ + flecs_access_end(r, true); +} - ecs_id_t table_id = table->type.array[column - 1]; - if (ECS_PAIR_FIRST(table_id) != EcsUnion) { - continue; - } +const ecs_record_t* ecs_read_begin( + ecs_world_t *world, + ecs_entity_t entity) +{ + return flecs_access_begin(world, entity, false); +} - flecs_switch_term_t *el = ecs_vec_append_t(a, sw_terms, - flecs_switch_term_t); - el->signature_column_index = field; - el->sw_case = ecs_pair_second(world, id); - el->sw_column = NULL; - ids[field] = id; - has_filter = true; - } +void ecs_read_end( + const ecs_record_t *r) +{ + flecs_access_end(r, false); +} + +ecs_entity_t ecs_record_get_entity( + const ecs_record_t *record) +{ + ecs_check(record != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_table_t *table = record->table; + if (!table) { + return 0; } - /* Look for disabled fields */ - if (table->flags & EcsTableHasToggle) { - for (i = 0; i < term_count; i ++) { - if (ecs_term_match_0(&terms[i])) { - continue; - } + return table->data.entities[ECS_RECORD_TO_ROW(record->row)]; +error: + return 0; +} - int32_t field = terms[i].field_index; - ecs_id_t id = ids[field]; - ecs_id_t bs_id = ECS_TOGGLE | id; - int32_t bs_index = ecs_table_get_type_index(world, table, bs_id); +const void* ecs_record_get_id( + const ecs_world_t *stage, + const ecs_record_t *r, + ecs_id_t id) +{ + const ecs_world_t *world = ecs_get_world(stage); + ecs_id_record_t *idr = flecs_id_record_get(world, id); + return flecs_get_component(r->table, ECS_RECORD_TO_ROW(r->row), idr); +} - if (bs_index != -1) { - flecs_bitset_term_t *bc = ecs_vec_append_t(a, bs_terms, - flecs_bitset_term_t); - bc->column_index = bs_index; - bc->bs_column = NULL; - has_filter = true; - } - } +bool ecs_record_has_id( + ecs_world_t *stage, + const ecs_record_t *r, + ecs_id_t id) +{ + const ecs_world_t *world = ecs_get_world(stage); + if (r->table) { + return ecs_table_has_id(world, r->table, id); } + return false; +} - /* Look for flattened fields */ - if (table->flags & EcsTableHasTarget) { - const ecs_table_record_t *tr = flecs_table_record_get(world, table, - ecs_pair_t(EcsTarget, EcsWildcard)); - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t column = tr->index; - ecs_assert(column != -1, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t rel = ecs_pair_second(world, table->type.array[column]); +void* ecs_record_ensure_id( + ecs_world_t *stage, + ecs_record_t *r, + ecs_id_t id) +{ + const ecs_world_t *world = ecs_get_world(stage); + ecs_id_record_t *idr = flecs_id_record_get(world, id); + return flecs_get_component(r->table, ECS_RECORD_TO_ROW(r->row), idr); +} - for (i = 0; i < term_count; i ++) { - if (ecs_term_match_0(&terms[i])) { - continue; - } +ecs_ref_t ecs_ref_init_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + + world = ecs_get_world(world); - if (terms[i].src.trav == rel) { - ef.flat_tree_column = table->column_map[column]; - ecs_assert(ef.flat_tree_column != -1, - ECS_INTERNAL_ERROR, NULL); - has_filter = true; - - flecs_flat_table_term_t *term = ecs_vec_append_t( - a, ft_terms, flecs_flat_table_term_t); - term->field_index = terms[i].field_index; - term->term = &terms[i]; - ecs_os_zeromem(&term->monitor); - } - } - } + ecs_record_t *record = flecs_entities_get(world, entity); + ecs_check(record != NULL, ECS_INVALID_PARAMETER, + "cannot create ref for empty entity"); - if (has_filter) { - if (!*entity_filter) { - *entity_filter = ecs_os_malloc_t(ecs_entity_filter_t); - } - ecs_assert(*entity_filter != NULL, ECS_OUT_OF_MEMORY, NULL); - **entity_filter = ef; + ecs_ref_t result = { + .entity = entity, + .id = id, + .record = record + }; + + ecs_table_t *table = record->table; + if (table) { + result.tr = flecs_table_record_get(world, table, id); + result.table_id = table->id; } + + return result; +error: + return (ecs_ref_t){0}; } -void flecs_entity_filter_fini( - ecs_world_t *world, - ecs_entity_filter_t *ef) +static +bool flecs_ref_needs_sync( + ecs_ref_t *ref, + ecs_table_record_t *tr, + const ecs_table_t *table) +{ + ecs_assert(ref != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + return !tr || ref->table_id != table->id || tr->hdr.table != table; +} + +void ecs_ref_update( + const ecs_world_t *world, + ecs_ref_t *ref) { - if (!ef) { + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ref != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ref->entity != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(ref->id != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(ref->record != NULL, ECS_INVALID_PARAMETER, NULL); + + ecs_record_t *r = ref->record; + ecs_table_t *table = r->table; + if (!table) { return; } - ecs_allocator_t *a = &world->allocator; + ecs_table_record_t *tr = ref->tr; + if (flecs_ref_needs_sync(ref, tr, table)) { + tr = ref->tr = flecs_table_record_get(world, table, ref->id); + if (!tr) { + return; + } - flecs_flat_table_term_t *fields = ecs_vec_first(&ef->ft_terms); - int32_t i, term_count = ecs_vec_count(&ef->ft_terms); - for (i = 0; i < term_count; i ++) { - ecs_vec_fini_t(NULL, &fields[i].monitor, flecs_flat_monitor_t); + ecs_assert(tr->hdr.table == r->table, ECS_INTERNAL_ERROR, NULL); + ref->table_id = table->id; } - - ecs_vec_fini_t(a, &ef->sw_terms, flecs_switch_term_t); - ecs_vec_fini_t(a, &ef->bs_terms, flecs_bitset_term_t); - ecs_vec_fini_t(a, &ef->ft_terms, flecs_flat_table_term_t); - ecs_os_free(ef); +error: + return; } -int flecs_entity_filter_next( - ecs_entity_filter_iter_t *it) +void* ecs_ref_get_id( + const ecs_world_t *world, + ecs_ref_t *ref, + ecs_id_t id) { - ecs_table_t *table = it->range.table; - flecs_switch_term_t *sw_terms = ecs_vec_first(&it->entity_filter->sw_terms); - flecs_bitset_term_t *bs_terms = ecs_vec_first(&it->entity_filter->bs_terms); - ecs_entity_filter_t *ef = it->entity_filter; - int32_t flat_tree_column = ef->flat_tree_column; - ecs_table_range_t *range = &it->range; - int32_t range_end = range->offset + range->count; - int result = EcsIterNext; - bool found = false; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ref != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ref->entity != 0, ECS_INVALID_PARAMETER, "ref not initialized"); + ecs_check(ref->id != 0, ECS_INVALID_PARAMETER, "ref not initialized"); + ecs_check(ref->record != NULL, ECS_INVALID_PARAMETER, "ref not initialized"); + ecs_check(id == ref->id, ECS_INVALID_PARAMETER, "ref not initialized"); - do { - found = false; + ecs_record_t *r = ref->record; + ecs_table_t *table = r->table; + if (!table) { + return NULL; + } - if (bs_terms) { - if (flecs_entity_filter_bitset_next(table, it) == -1) { - /* No more enabled components for table */ - it->bs_offset = 0; - break; - } else { - result = EcsIterYield; - found = true; - } - } + int32_t row = ECS_RECORD_TO_ROW(r->row); + ecs_check(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); - if (sw_terms) { - if (flecs_entity_filter_switch_next(table, it, found) == -1) { - /* No more elements in sparse column */ - if (found) { - /* Try again */ - result = EcsIterNext; - found = false; - } else { - /* Nothing found */ - it->bs_offset = 0; - break; - } - } else { - result = EcsIterYield; - found = true; - it->bs_offset = range->offset + range->count; - } + ecs_table_record_t *tr = ref->tr; + if (flecs_ref_needs_sync(ref, tr, table)) { + tr = ref->tr = flecs_table_record_get(world, table, id); + if (!tr) { + return NULL; } - if (flat_tree_column != -1) { - bool first_for_table = it->prev != table; - ecs_iter_t *iter = it->it; - ecs_world_t *world = iter->real_world; - EcsTarget *ft = table->data.columns[flat_tree_column].data.array; - int32_t ft_offset; - int32_t ft_count; + ref->table_id = table->id; + ecs_assert(tr->hdr.table == r->table, ECS_INTERNAL_ERROR, NULL); + } - if (first_for_table) { - ft_offset = it->flat_tree_offset = range->offset; - it->target_count = 1; - } else { - it->flat_tree_offset += ft[it->flat_tree_offset].count; - ft_offset = it->flat_tree_offset; - it->target_count ++; - } + int32_t column = tr->column; + if (column == -1) { + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + if (idr->flags & EcsIdIsSparse) { + return flecs_sparse_get_any(idr->sparse, 0, ref->entity); + } + } + return flecs_table_get_component(table, column, row).ptr; +error: + return NULL; +} - ecs_assert(ft_offset < ecs_table_count(table), - ECS_INTERNAL_ERROR, NULL); +void* ecs_emplace_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id, + bool *is_new) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - EcsTarget *cur = &ft[ft_offset]; - ft_count = cur->count; - bool is_last = (ft_offset + ft_count) >= range_end; - - int32_t i, field_count = ecs_vec_count(&ef->ft_terms); - flecs_flat_table_term_t *fields = ecs_vec_first(&ef->ft_terms); - for (i = 0; i < field_count; i ++) { - flecs_flat_table_term_t *field = &fields[i]; - ecs_vec_init_if_t(&field->monitor, flecs_flat_monitor_t); - int32_t field_index = field->field_index; - ecs_id_t id = it->it->ids[field_index]; - ecs_id_t flat_pair = table->type.array[flat_tree_column]; - ecs_entity_t rel = ECS_PAIR_FIRST(flat_pair); - ecs_entity_t tgt; - ecs_table_record_t *tr; - int32_t tgt_col = flecs_get_flattened_target( - world, cur, rel, id, &tgt, &tr); - if (tgt_col != -1) { - iter->sources[field_index] = tgt; - iter->columns[field_index] = /* encode flattened field */ - -(iter->field_count + tgt_col + 1); - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - - /* Keep track of maximum value encountered in target table - * dirty state so this doesn't have to be recomputed when - * synchronizing the query monitor. */ - ecs_vec_set_min_count_zeromem_t(NULL, &field->monitor, - flecs_flat_monitor_t, it->target_count); - ecs_table_t *tgt_table = tr->hdr.table; - int32_t *ds = flecs_table_get_dirty_state(world, tgt_table); - ecs_assert(ds != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_vec_get_t(&field->monitor, flecs_flat_monitor_t, - it->target_count - 1)->table_state = ds[tgt_col + 1]; - } else { - if (field->term->oper == EcsOptional) { - iter->columns[field_index] = 0; - iter->ptrs[field_index] = NULL; - } else { - it->prev = NULL; - break; - } - } - } - if (i != field_count) { - if (is_last) { - break; - } - } else { - found = true; - if ((ft_offset + ft_count) == range_end) { - result = EcsIterNextYield; - } else { - result = EcsIterYield; - } - } + ecs_stage_t *stage = flecs_stage_from_world(&world); - range->offset = ft_offset; - range->count = ft_count; - it->prev = table; - } - } while (!found); + if (flecs_defer_cmd(stage)) { + return flecs_defer_set( + world, stage, EcsCmdEmplace, entity, id, 0, NULL, is_new); + } - it->prev = table; + ecs_check(is_new || !ecs_has_id(world, entity, id), ECS_INVALID_PARAMETER, + "cannot emplace a component the entity already has"); - if (!found) { - return EcsIterNext; - } else { - return result; - } -} + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_table_t *table = r->table; + flecs_add_id_w_record(world, entity, r, id, false /* Add without ctor */); + flecs_defer_end(world, stage); -/** - * @file entity_name.c - * @brief Functions for working with named entities. - */ + ecs_id_record_t *idr = flecs_id_record_get(world, id); + void *ptr = flecs_get_component(r->table, ECS_RECORD_TO_ROW(r->row), idr); + ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, + "emplaced component was removed during operation, make sure to not " + "remove component T in on_add(T) hook/OnAdd(T) observer"); -#include + if (is_new) { + *is_new = table != r->table; + } -#define ECS_NAME_BUFFER_LENGTH (64) + return ptr; +error: + return NULL; +} static -bool flecs_path_append( - const ecs_world_t *world, - ecs_entity_t parent, - ecs_entity_t child, - const char *sep, - const char *prefix, - ecs_strbuf_t *buf) +void flecs_modified_id_if( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id, + bool owned) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(sep[0] != 0, ECS_INVALID_PARAMETER, NULL); - - ecs_entity_t cur = 0; - const char *name = NULL; - ecs_size_t name_len = 0; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - if (child && ecs_is_alive(world, child)) { - cur = ecs_get_target(world, child, EcsChildOf, 0); - if (cur) { - ecs_assert(cur != child, ECS_CYCLE_DETECTED, NULL); - if (cur != parent && (cur != EcsFlecsCore || prefix != NULL)) { - flecs_path_append(world, parent, cur, sep, prefix, buf); - if (!sep[1]) { - ecs_strbuf_appendch(buf, sep[0]); - } else { - ecs_strbuf_appendstr(buf, sep); - } - } - } else if (prefix && prefix[0]) { - if (!prefix[1]) { - ecs_strbuf_appendch(buf, prefix[0]); - } else { - ecs_strbuf_appendstr(buf, prefix); - } - } + ecs_stage_t *stage = flecs_stage_from_world(&world); - const EcsIdentifier *id = ecs_get_pair( - world, child, EcsIdentifier, EcsName); - if (id) { - name = id->value; - name_len = id->length; - } + if (flecs_defer_modified(stage, entity, id)) { + return; } - if (name) { - ecs_strbuf_appendstrn(buf, name, name_len); - } else { - ecs_strbuf_appendint(buf, flecs_uto(int64_t, (uint32_t)child)); + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_table_t *table = r->table; + if (!flecs_table_record_get(world, table, id)) { + flecs_defer_end(world, stage); + return; } - return cur != 0; + ecs_type_t ids = { .array = &id, .count = 1 }; + flecs_notify_on_set(world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, owned); + + flecs_table_mark_dirty(world, table, id); + flecs_defer_end(world, stage); +error: + return; } -bool flecs_name_is_id( - const char *name) +void ecs_modified_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) { - ecs_assert(name != NULL, ECS_INTERNAL_ERROR, NULL); - - if (!isdigit(name[0])) { - return false; - } + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - ecs_size_t i, length = ecs_os_strlen(name); - for (i = 1; i < length; i ++) { - char ch = name[i]; + ecs_stage_t *stage = flecs_stage_from_world(&world); - if (!isdigit(ch)) { - break; - } + if (flecs_defer_modified(stage, entity, id)) { + return; } - return i >= length; -} + /* If the entity does not have the component, calling ecs_modified is + * invalid. The assert needs to happen after the defer statement, as the + * entity may not have the component when this function is called while + * operations are being deferred. */ + ecs_check(ecs_has_id(world, entity, id), ECS_INVALID_PARAMETER, NULL); -ecs_entity_t flecs_name_to_id( - const ecs_world_t *world, - const char *name) -{ - int64_t result = atoll(name); - ecs_assert(result >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t alive = ecs_get_alive(world, (ecs_entity_t)result); - if (alive) { - return alive; - } else { - if ((uint32_t)result == (uint64_t)result) { - return (ecs_entity_t)result; - } else { - return 0; - } - } + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_table_t *table = r->table; + ecs_type_t ids = { .array = &id, .count = 1 }; + flecs_notify_on_set(world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, true); + + flecs_table_mark_dirty(world, table, id); + flecs_defer_end(world, stage); +error: + return; } static -ecs_entity_t flecs_get_builtin( - const char *name) +void flecs_set_id_copy( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_id_t id, + size_t size, + void *ptr) { - if (name[0] == '.' && name[1] == '\0') { - return EcsThis; - } else if (name[0] == '*' && name[1] == '\0') { - return EcsWildcard; - } else if (name[0] == '_' && name[1] == '\0') { - return EcsAny; - } else if (name[0] == '$' && name[1] == '\0') { - return EcsVariable; + if (flecs_defer_cmd(stage)) { + flecs_defer_set(world, stage, EcsCmdSet, entity, id, + flecs_utosize(size), ptr, NULL); + return; } - return 0; + ecs_record_t *r = flecs_entities_get(world, entity); + flecs_component_ptr_t dst = flecs_ensure(world, entity, id, r); + + flecs_copy_id(world, r, id, size, dst.ptr, ptr, dst.ti); + + flecs_defer_end(world, stage); } static -bool flecs_is_sep( - const char **ptr, - const char *sep) +void flecs_set_id_move( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_id_t id, + size_t size, + void *ptr, + ecs_cmd_kind_t cmd_kind) { - ecs_size_t len = ecs_os_strlen(sep); + if (flecs_defer_cmd(stage)) { + flecs_defer_set(world, stage, cmd_kind, entity, id, + flecs_utosize(size), ptr, NULL); + return; + } - if (!ecs_os_strncmp(*ptr, sep, len)) { - *ptr += len; - return true; + ecs_record_t *r = flecs_entities_get(world, entity); + flecs_component_ptr_t dst = flecs_ensure(world, entity, id, r); + ecs_check(dst.ptr != NULL, ECS_INVALID_PARAMETER, NULL); + + const ecs_type_info_t *ti = dst.ti; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_move_t move; + if (cmd_kind != EcsCmdEmplace) { + /* ctor will have happened by ensure */ + move = ti->hooks.move_dtor; } else { - return false; + move = ti->hooks.ctor_move_dtor; + } + if (move) { + move(dst.ptr, ptr, 1, ti); + } else { + ecs_os_memcpy(dst.ptr, ptr, flecs_utosize(size)); } -} -static -const char* flecs_path_elem( - const char *path, - const char *sep, - int32_t *len) -{ - const char *ptr; - char ch; - int32_t template_nesting = 0; - int32_t count = 0; + flecs_table_mark_dirty(world, r->table, id); - for (ptr = path; (ch = *ptr); ptr ++) { - if (ch == '<') { - template_nesting ++; - } else if (ch == '>') { - template_nesting --; + if (cmd_kind == EcsCmdSet) { + ecs_table_t *table = r->table; + if (table->flags & EcsTableHasOnSet || ti->hooks.on_set) { + ecs_type_t ids = { .array = &id, .count = 1 }; + flecs_notify_on_set( + world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, true); } + } - ecs_check(template_nesting >= 0, ECS_INVALID_PARAMETER, path); - - if (!template_nesting && flecs_is_sep(&ptr, sep)) { - break; - } + flecs_defer_end(world, stage); +error: + return; +} - count ++; - } +void ecs_set_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id, + size_t size, + const void *ptr) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - if (len) { - *len = count; - } + ecs_stage_t *stage = flecs_stage_from_world(&world); - if (count) { - return ptr; - } else { - return NULL; - } + /* Safe to cast away const: function won't modify if move arg is false */ + flecs_set_id_copy(world, stage, entity, id, size, + ECS_CONST_CAST(void*, ptr)); error: - return NULL; + return; } +#if defined(FLECS_DEBUG) || defined(FLECS_KEEP_ASSERT) static -bool flecs_is_root_path( - const char *path, - const char *prefix) +bool flecs_can_toggle( + ecs_world_t *world, + ecs_id_t id) { - if (prefix) { - return !ecs_os_strncmp(path, prefix, ecs_os_strlen(prefix)); - } else { + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { return false; } + + return (idr->flags & EcsIdCanToggle) != 0; } +#endif -static -ecs_entity_t flecs_get_parent_from_path( - const ecs_world_t *world, - ecs_entity_t parent, - const char **path_ptr, - const char *prefix, - bool new_entity) +void ecs_enable_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id, + bool enable) { - bool start_from_root = false; - const char *path = *path_ptr; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stage_t *stage = flecs_stage_from_world(&world); - if (flecs_is_root_path(path, prefix)) { - path += ecs_os_strlen(prefix); - parent = 0; - start_from_root = true; - } + ecs_check(ecs_is_valid(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + ecs_check(flecs_can_toggle(world, id), ECS_INVALID_OPERATION, + "add CanToggle trait to component"); - if (!start_from_root && !parent && new_entity) { - parent = ecs_get_scope(world); + if (flecs_defer_enable(stage, entity, id, enable)) { + return; } - *path_ptr = path; - - return parent; -} - -static -void flecs_on_set_symbol(ecs_iter_t *it) { - EcsIdentifier *n = ecs_field(it, EcsIdentifier, 1); - ecs_world_t *world = it->world; + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_entity_t bs_id = id | ECS_TOGGLE; + + ecs_table_t *table = r->table; + int32_t index = -1; + if (table) { + index = ecs_table_get_type_index(world, table, bs_id); + } - int i; - for (i = 0; i < it->count; i ++) { - ecs_entity_t e = it->entities[i]; - flecs_name_index_ensure( - &world->symbols, e, n[i].value, n[i].length, n[i].hash); + if (index == -1) { + ecs_add_id(world, entity, bs_id); + flecs_defer_end(world, stage); + ecs_enable_id(world, entity, id, enable); + return; } -} + + ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); + index -= table->_->bs_offset; + ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); -void flecs_bootstrap_hierarchy(ecs_world_t *world) { - ecs_observer(world, { - .entity = ecs_entity(world, {.add = {ecs_childof(EcsFlecsInternals)}}), - .filter.terms[0] = { - .id = ecs_pair(ecs_id(EcsIdentifier), EcsSymbol), - .src.flags = EcsSelf - }, - .callback = flecs_on_set_symbol, - .events = {EcsOnSet}, - .yield_existing = true - }); -} + /* Data cannot be NULL, since entity is stored in the table */ + ecs_bitset_t *bs = &table->_->bs_columns[index]; + ecs_assert(bs != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_bitset_set(bs, ECS_RECORD_TO_ROW(r->row), enable); -/* Public functions */ + flecs_defer_end(world, stage); +error: + return; +} -void ecs_get_path_w_sep_buf( +bool ecs_is_enabled_id( const ecs_world_t *world, - ecs_entity_t parent, - ecs_entity_t child, - const char *sep, - const char *prefix, - ecs_strbuf_t *buf) + ecs_entity_t entity, + ecs_id_t id) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(buf != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + /* Make sure we're not working with a stage */ world = ecs_get_world(world); - if (child == EcsWildcard) { - ecs_strbuf_appendch(buf, '*'); - return; - } - if (child == EcsAny) { - ecs_strbuf_appendch(buf, '_'); - return; + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = r->table; + if (!table) { + return false; } - if (!sep) { - sep = "."; + ecs_entity_t bs_id = id | ECS_TOGGLE; + int32_t index = ecs_table_get_type_index(world, table, bs_id); + if (index == -1) { + /* If table does not have TOGGLE column for component, component is + * always enabled, if the entity has it */ + return ecs_has_id(world, entity, id); } - if (!child || parent != child) { - flecs_path_append(world, parent, child, sep, prefix, buf); - } else { - ecs_strbuf_appendstrn(buf, "", 0); - } + ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); + index -= table->_->bs_offset; + ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_bitset_t *bs = &table->_->bs_columns[index]; + return flecs_bitset_get(bs, ECS_RECORD_TO_ROW(r->row)); error: - return; + return false; } -char* ecs_get_path_w_sep( +bool ecs_has_id( const ecs_world_t *world, - ecs_entity_t parent, - ecs_entity_t child, - const char *sep, - const char *prefix) + ecs_entity_t entity, + ecs_id_t id) { - ecs_strbuf_t buf = ECS_STRBUF_INIT; - ecs_get_path_w_sep_buf(world, parent, child, sep, prefix, &buf); - return ecs_strbuf_get(&buf); -} + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); -ecs_entity_t ecs_lookup_child( - const ecs_world_t *world, - ecs_entity_t parent, - const char *name) -{ - ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); + /* Make sure we're not working with a stage */ world = ecs_get_world(world); - if (flecs_name_is_id(name)) { - ecs_entity_t result = flecs_name_to_id(world, name); - if (result && ecs_is_alive(world, result)) { - if (parent && !ecs_has_pair(world, result, EcsChildOf, parent)) { - return 0; - } - return result; - } + ecs_record_t *r = flecs_entities_get_any(world, entity); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = r->table; + if (!table) { + return false; } - ecs_id_t pair = ecs_childof(parent); - ecs_hashmap_t *index = flecs_id_name_index_get(world, pair); - if (index) { - return flecs_name_index_find(index, name, 0, 0); + if (id < FLECS_HI_COMPONENT_ID) { + if (table->component_map[id] != 0) { + return true; + } } else { - return 0; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (tr) { + return true; + } + } } -error: - return 0; -} -ecs_entity_t ecs_lookup( - const ecs_world_t *world, - const char *name) -{ - if (!name) { - return 0; + if (!(table->flags & (EcsTableHasUnion|EcsTableHasIsA))) { + return false; } - ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); - world = ecs_get_world(world); - - ecs_entity_t e = flecs_get_builtin(name); - if (e) { - return e; + if (ECS_IS_PAIR(id) && (table->flags & EcsTableHasUnion)) { + ecs_id_record_t *u_idr = flecs_id_record_get(world, + ecs_pair(ECS_PAIR_FIRST(id), EcsUnion)); + if (u_idr && u_idr->flags & EcsIdIsUnion) { + uint64_t cur = flecs_switch_get(u_idr->sparse, (uint32_t)entity); + return (uint32_t)cur == ECS_PAIR_SECOND(id); + } } - if (flecs_name_is_id(name)) { - return flecs_name_to_id(world, name); + ecs_table_record_t *tr; + int32_t column = ecs_search_relation(world, table, 0, id, + EcsIsA, 0, 0, 0, &tr); + if (column == -1) { + return false; } - e = flecs_name_index_find(&world->aliases, name, 0, 0); - if (e) { - return e; - } - - return ecs_lookup_child(world, 0, name); + return true; error: - return 0; + return false; } -ecs_entity_t ecs_lookup_symbol( +bool ecs_owns_id( const ecs_world_t *world, - const char *name, - bool lookup_as_path, - bool recursive) -{ - if (!name) { - return 0; - } + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); + /* Make sure we're not working with a stage */ world = ecs_get_world(world); - ecs_entity_t e = flecs_name_index_find(&world->symbols, name, 0, 0); - if (e) { - return e; + ecs_record_t *r = flecs_entities_get_any(world, entity); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = r->table; + if (!table) { + return false; } - if (lookup_as_path) { - return ecs_lookup_path_w_sep(world, 0, name, ".", NULL, recursive); + if (id < FLECS_HI_COMPONENT_ID) { + return table->component_map[id] != 0; + } else { + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (tr) { + return true; + } + } } error: - return 0; + return false; } -ecs_entity_t ecs_lookup_path_w_sep( +ecs_entity_t ecs_get_target( const ecs_world_t *world, - ecs_entity_t parent, - const char *path, - const char *sep, - const char *prefix, - bool recursive) + ecs_entity_t entity, + ecs_entity_t rel, + int32_t index) { - if (!path) { - return 0; - } + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(rel != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); - const ecs_world_t *stage = world; world = ecs_get_world(world); - ecs_entity_t e = flecs_get_builtin(path); - if (e) { - return e; + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = r->table; + if (!table) { + goto not_found; } - e = flecs_name_index_find(&world->aliases, path, 0, 0); - if (e) { - return e; + ecs_id_t wc = ecs_pair(rel, EcsWildcard); + ecs_id_record_t *idr = flecs_id_record_get(world, wc); + const ecs_table_record_t *tr = NULL; + if (idr) { + tr = flecs_id_record_get_table(idr, table); } - - char buff[ECS_NAME_BUFFER_LENGTH]; - const char *ptr, *ptr_start; - char *elem = buff; - int32_t len, size = ECS_NAME_BUFFER_LENGTH; - ecs_entity_t cur; - bool lookup_path_search = false; - - const ecs_entity_t *lookup_path = ecs_get_lookup_path(stage); - const ecs_entity_t *lookup_path_cur = lookup_path; - while (lookup_path_cur && *lookup_path_cur) { - lookup_path_cur ++; + if (!tr) { + if (!idr || (idr->flags & EcsIdOnInstantiateInherit)) { + goto look_in_base; + } else { + return 0; + } } - if (!sep) { - sep = "."; + if (index >= tr->count) { + index -= tr->count; + goto look_in_base; } - parent = flecs_get_parent_from_path(stage, parent, &path, prefix, true); + ecs_entity_t result = + ecs_pair_second(world, table->type.array[tr->index + index]); - if (!sep[0]) { - return ecs_lookup_child(world, parent, path); + if (result == EcsUnion) { + wc = ecs_pair(rel, EcsUnion); + ecs_id_record_t *wc_idr = flecs_id_record_get(world, wc); + ecs_assert(wc_idr != NULL, ECS_INTERNAL_ERROR, NULL); + result = flecs_switch_get(wc_idr->sparse, (uint32_t)entity); } -retry: - cur = parent; - ptr_start = ptr = path; + return result; +look_in_base: + if (table->flags & EcsTableHasIsA) { + ecs_table_record_t *tr_isa = flecs_id_record_get_table( + world->idr_isa_wildcard, table); + ecs_assert(tr_isa != NULL, ECS_INTERNAL_ERROR, NULL); - while ((ptr = flecs_path_elem(ptr, sep, &len))) { - if (len < size) { - ecs_os_memcpy(elem, ptr_start, len); - } else { - if (size == ECS_NAME_BUFFER_LENGTH) { - elem = NULL; + ecs_id_t *ids = table->type.array; + int32_t i = tr_isa->index, end = (i + tr_isa->count); + for (; i < end; i ++) { + ecs_id_t isa_pair = ids[i]; + ecs_entity_t base = ecs_pair_second(world, isa_pair); + ecs_assert(base != 0, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t t = ecs_get_target(world, base, rel, index); + if (t) { + return t; } - - elem = ecs_os_realloc(elem, len + 1); - ecs_os_memcpy(elem, ptr_start, len); - size = len + 1; } + } - elem[len] = '\0'; - ptr_start = ptr; +not_found: +error: + return 0; +} - cur = ecs_lookup_child(world, cur, elem); - if (!cur) { - goto tail; - } +ecs_entity_t ecs_get_parent( + const ecs_world_t *world, + ecs_entity_t entity) +{ + return ecs_get_target(world, entity, EcsChildOf, 0); +} + +ecs_entity_t ecs_get_target_for_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t rel, + ecs_id_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + + if (!id) { + return ecs_get_target(world, entity, rel, 0); } -tail: - if (!cur && recursive) { - if (!lookup_path_search) { - if (parent) { - parent = ecs_get_target(world, parent, EcsChildOf, 0); - goto retry; - } else { - lookup_path_search = true; - } + world = ecs_get_world(world); + + ecs_table_t *table = ecs_get_table(world, entity); + ecs_entity_t subject = 0; + + if (rel) { + int32_t column = ecs_search_relation( + world, table, 0, id, rel, 0, &subject, 0, 0); + if (column == -1) { + return 0; } + } else { + entity = 0; /* Don't return entity if id was not found */ - if (lookup_path_search) { - if (lookup_path_cur != lookup_path) { - lookup_path_cur --; - parent = lookup_path_cur[0]; - goto retry; + if (table) { + ecs_id_t *ids = table->type.array; + int32_t i, count = table->type.count; + + for (i = 0; i < count; i ++) { + ecs_id_t ent = ids[i]; + if (ent & ECS_ID_FLAGS_MASK) { + /* Skip ids with pairs, roles since 0 was provided for rel */ + break; + } + + if (ecs_has_id(world, ent, id)) { + subject = ent; + break; + } } } } - if (elem != buff) { - ecs_os_free(elem); + if (subject == 0) { + return entity; + } else { + return subject; } - - return cur; error: return 0; } -ecs_entity_t ecs_set_scope( - ecs_world_t *world, - ecs_entity_t scope) +int32_t ecs_get_depth( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t rel) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_check(ecs_is_valid(world, rel), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_has_id(world, rel, EcsAcyclic), ECS_INVALID_PARAMETER, + "cannot safely determine depth for relationship that is not acyclic " + "(add Acyclic property to relationship)"); - ecs_entity_t cur = stage->scope; - stage->scope = scope; + ecs_table_t *table = ecs_get_table(world, entity); + if (table) { + return ecs_table_get_depth(world, table, rel); + } - return cur; -error: return 0; -} - -ecs_entity_t ecs_get_scope( - const ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); - return stage->scope; error: - return 0; + return -1; } -ecs_entity_t* ecs_set_lookup_path( - ecs_world_t *world, - const ecs_entity_t *lookup_path) +static +const char* flecs_get_identifier( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t tag) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - /* Safe: application owns lookup path */ - ecs_entity_t *cur = ECS_CONST_CAST(ecs_entity_t*, stage->lookup_path); - stage->lookup_path = lookup_path; + const EcsIdentifier *ptr = ecs_get_pair( + world, entity, EcsIdentifier, tag); - return cur; + if (ptr) { + return ptr->value; + } else { + return NULL; + } error: return NULL; } -ecs_entity_t* ecs_get_lookup_path( - const ecs_world_t *world) +const char* ecs_get_name( + const ecs_world_t *world, + ecs_entity_t entity) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); - /* Safe: application owns lookup path */ - return ECS_CONST_CAST(ecs_entity_t*, stage->lookup_path); -error: - return NULL; + return flecs_get_identifier(world, entity, EcsName); } -const char* ecs_set_name_prefix( - ecs_world_t *world, - const char *prefix) +const char* ecs_get_symbol( + const ecs_world_t *world, + ecs_entity_t entity) { - ecs_poly_assert(world, ecs_world_t); - const char *old_prefix = world->info.name_prefix; - world->info.name_prefix = prefix; - return old_prefix; + world = ecs_get_world(world); + if (ecs_owns_pair(world, entity, ecs_id(EcsIdentifier), EcsSymbol)) { + return flecs_get_identifier(world, entity, EcsSymbol); + } else { + return NULL; + } } static -void flecs_add_path( +ecs_entity_t flecs_set_identifier( ecs_world_t *world, - bool defer_suspend, - ecs_entity_t parent, + ecs_stage_t *stage, ecs_entity_t entity, + ecs_entity_t tag, const char *name) { - ecs_suspend_readonly_state_t srs; - ecs_world_t *real_world = NULL; - if (defer_suspend) { - real_world = flecs_suspend_readonly(world, &srs); - ecs_assert(real_world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(entity != 0 || name != NULL, ECS_INVALID_PARAMETER, NULL); + + if (!entity) { + entity = ecs_new(world); } - if (parent) { - ecs_add_pair(world, entity, EcsChildOf, parent); + if (!name) { + ecs_remove_pair(world, entity, ecs_id(EcsIdentifier), tag); + return entity; } - ecs_set_name(world, entity, name); + EcsIdentifier *ptr = ecs_ensure_pair(world, entity, EcsIdentifier, tag); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); - if (defer_suspend) { - flecs_resume_readonly(real_world, &srs); - flecs_defer_path((ecs_stage_t*)world, parent, entity, name); + if (tag == EcsName) { + /* Insert command after ensure, but before the name is potentially + * freed. Even though the name is a const char*, it is possible that the + * application passed in the existing name of the entity which could + * still cause it to be freed. */ + flecs_defer_path(stage, 0, entity, name); } + + ecs_os_strset(&ptr->value, name); + ecs_modified_pair(world, entity, ecs_id(EcsIdentifier), tag); + + return entity; +error: + return 0; } -ecs_entity_t ecs_add_path_w_sep( +ecs_entity_t ecs_set_name( ecs_world_t *world, ecs_entity_t entity, - ecs_entity_t parent, - const char *path, - const char *sep, - const char *prefix) + const char *name) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - - if (!sep) { - sep = "."; + if (!entity) { + return ecs_entity(world, { + .name = name + }); } - if (!path) { - if (!entity) { - entity = ecs_new_id(world); - } - - if (parent) { - ecs_add_pair(world, entity, EcsChildOf, entity); - } + ecs_stage_t *stage = flecs_stage_from_world(&world); + flecs_set_identifier(world, stage, entity, EcsName, name); - return entity; - } + return entity; +} - bool root_path = flecs_is_root_path(path, prefix); - parent = flecs_get_parent_from_path(world, parent, &path, prefix, !entity); +ecs_entity_t ecs_set_symbol( + ecs_world_t *world, + ecs_entity_t entity, + const char *name) +{ + return flecs_set_identifier(world, NULL, entity, EcsSymbol, name); +} - char buff[ECS_NAME_BUFFER_LENGTH]; - const char *ptr = path; - const char *ptr_start = path; - char *elem = buff; - int32_t len, size = ECS_NAME_BUFFER_LENGTH; - - /* If we're in deferred/readonly mode suspend it, so that the name index is - * immediately updated. Without this, we could create multiple entities for - * the same name in a single command queue. */ - bool suspend_defer = ecs_poly_is(world, ecs_stage_t) && - (ecs_get_stage_count(world) <= 1); - ecs_entity_t cur = parent; - char *name = NULL; +void ecs_set_alias( + ecs_world_t *world, + ecs_entity_t entity, + const char *name) +{ + flecs_set_identifier(world, NULL, entity, EcsAlias, name); +} - if (sep[0]) { - while ((ptr = flecs_path_elem(ptr, sep, &len))) { - if (len < size) { - ecs_os_memcpy(elem, ptr_start, len); - } else { - if (size == ECS_NAME_BUFFER_LENGTH) { - elem = NULL; - } +ecs_id_t ecs_make_pair( + ecs_entity_t relationship, + ecs_entity_t target) +{ + ecs_assert(!ECS_IS_PAIR(relationship) && !ECS_IS_PAIR(target), + ECS_INVALID_PARAMETER, "cannot create nested pairs"); + return ecs_pair(relationship, target); +} - elem = ecs_os_realloc(elem, len + 1); - ecs_os_memcpy(elem, ptr_start, len); - size = len + 1; - } +bool ecs_is_valid( + const ecs_world_t *world, + ecs_entity_t entity) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - elem[len] = '\0'; - ptr_start = ptr; + /* 0 is not a valid entity id */ + if (!entity) { + return false; + } + + /* Entity identifiers should not contain flag bits */ + if (entity & ECS_ID_FLAGS_MASK) { + return false; + } - ecs_entity_t e = ecs_lookup_child(world, cur, elem); - if (!e) { - if (name) { - ecs_os_free(name); - } + /* Entities should not contain data in dead zone bits */ + if (entity & ~0xFF00FFFFFFFFFFFF) { + return false; + } - name = ecs_os_strdup(elem); + /* If id exists, it must be alive (the generation count must match) */ + return ecs_is_alive(world, entity); +error: + return false; +} - /* If this is the last entity in the path, use the provided id */ - bool last_elem = false; - if (!flecs_path_elem(ptr, sep, NULL)) { - e = entity; - last_elem = true; - } +ecs_id_t ecs_strip_generation( + ecs_entity_t e) +{ + /* If this is not a pair, erase the generation bits */ + if (!(e & ECS_ID_FLAGS_MASK)) { + e &= ~ECS_GENERATION_MASK; + } - if (!e) { - if (last_elem) { - ecs_entity_t prev = ecs_set_scope(world, 0); - e = ecs_new(world, 0); - ecs_set_scope(world, prev); - } else { - e = ecs_new_id(world); - } - } + return e; +} - if (!cur && last_elem && root_path) { - ecs_remove_pair(world, e, EcsChildOf, EcsWildcard); - } +bool ecs_is_alive( + const ecs_world_t *world, + ecs_entity_t entity) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); - flecs_add_path(world, suspend_defer, cur, e, name); - } + world = ecs_get_world(world); - cur = e; - } + return flecs_entities_is_alive(world, entity); +error: + return false; +} - if (entity && (cur != entity)) { - ecs_throw(ECS_ALREADY_DEFINED, name); - } +ecs_entity_t ecs_get_alive( + const ecs_world_t *world, + ecs_entity_t entity) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + + if (!entity) { + return 0; + } - if (name) { - ecs_os_free(name); - } + /* Make sure we're not working with a stage */ + world = ecs_get_world(world); - if (elem != buff) { - ecs_os_free(elem); - } - } else { - flecs_add_path(world, suspend_defer, parent, entity, path); + if (flecs_entities_is_alive(world, entity)) { + return entity; } - return cur; + /* Make sure id does not have generation. This guards against accidentally + * "upcasting" a not alive identifier to an alive one. */ + if ((uint32_t)entity != entity) { + return 0; + } + + ecs_entity_t current = flecs_entities_get_alive(world, entity); + if (!current || !flecs_entities_is_alive(world, current)) { + return 0; + } + + return current; error: return 0; } -ecs_entity_t ecs_new_from_path_w_sep( +void ecs_make_alive( ecs_world_t *world, - ecs_entity_t parent, - const char *path, - const char *sep, - const char *prefix) + ecs_entity_t entity) { - return ecs_add_path_w_sep(world, 0, parent, path, sep, prefix); -} + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); -/** - * @file filter.c - * @brief Uncached query implementation. - * - * Uncached queries (filters) are stateless objects that do not cache their - * results. This file contains the creation and validation of uncached queries - * and code for query iteration. - * - * There file contains the implementation for term queries and filters. Term - * queries are uncached queries that only apply to a single term. Filters are - * uncached queries that support multiple terms. Filters are built on top of - * term queries: before iteration a filter will first find a "pivot" term (the - * term with the smallest number of elements), and create a term iterator for - * it. The output of that term iterator is then evaluated against the rest of - * the terms of the filter. - * - * Cached queries and observers are built using filters. - */ + /* Const cast is safe, function checks for threading */ + world = ECS_CONST_CAST(ecs_world_t*, ecs_get_world(world)); -#include + /* 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, + "cannot make entity alive while world is in multithreaded mode"); + + /* Check if a version of the provided id is alive */ + ecs_entity_t any = ecs_get_alive(world, (uint32_t)entity); + if (any == entity) { + /* If alive and equal to the argument, there's nothing left to do */ + return; + } -ecs_filter_t ECS_FILTER_INIT = { .hdr = { .magic = ecs_filter_t_magic }}; + /* If the id is currently alive but did not match the argument, fail */ + ecs_check(!any, ECS_INVALID_PARAMETER, + "entity is alive with different generation"); -/* Helper type for passing around context required for error messages */ -typedef struct { - const ecs_world_t *world; - ecs_filter_t *filter; - ecs_term_t *term; - int32_t term_index; -} ecs_filter_finalize_ctx_t; + /* Set generation if not alive. The sparse set checks if the provided + * id matches its own generation which is necessary for alive ids. This + * check would cause ecs_ensure to fail if the generation of the 'entity' + * argument doesn't match with its generation. + * + * While this could've been addressed in the sparse set, this is a rare + * scenario that can only be triggered by ecs_ensure. Implementing it here + * allows the sparse set to not do this check, which is more efficient. */ + flecs_entities_make_alive(world, entity); -static -char* flecs_filter_str( - const ecs_world_t *world, - const ecs_filter_t *filter, - const ecs_filter_finalize_ctx_t *ctx, - int32_t *term_start_out); + /* Ensure id exists. The underlying data structure will verify that the + * generation count matches the provided one. */ + flecs_entities_ensure(world, entity); +error: + return; +} -static -void flecs_filter_error( - const ecs_filter_finalize_ctx_t *ctx, - const char *fmt, - ...) +void ecs_make_alive_id( + ecs_world_t *world, + ecs_id_t id) { - va_list args; - va_start(args, fmt); + flecs_poly_assert(world, ecs_world_t); + if (ECS_HAS_ID_FLAG(id, PAIR)) { + ecs_entity_t r = ECS_PAIR_FIRST(id); + ecs_entity_t o = ECS_PAIR_SECOND(id); - int32_t term_start = 0; + ecs_check(r != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(o != 0, ECS_INVALID_PARAMETER, NULL); - char *expr = NULL; - if (ctx->filter) { - expr = flecs_filter_str(ctx->world, ctx->filter, ctx, &term_start); + if (flecs_entities_get_alive(world, r) == 0) { + ecs_assert(!ecs_exists(world, r), ECS_INVALID_PARAMETER, + "first element of pair is not alive"); + flecs_entities_ensure(world, r); + } + if (flecs_entities_get_alive(world, o) == 0) { + ecs_assert(!ecs_exists(world, o), ECS_INVALID_PARAMETER, + "second element of pair is not alive"); + flecs_entities_ensure(world, o); + } } else { - expr = ecs_term_str(ctx->world, ctx->term); - } - const char *name = NULL; - if (ctx->filter && ctx->filter->entity) { - name = ecs_get_name(ctx->filter->world, ctx->filter->entity); + flecs_entities_ensure(world, id & ECS_COMPONENT_MASK); } - ecs_parser_errorv(name, expr, term_start, fmt, args); - ecs_os_free(expr); - - va_end(args); +error: + return; } -static -int flecs_term_id_finalize_flags( - ecs_term_id_t *term_id, - ecs_filter_finalize_ctx_t *ctx) +bool ecs_exists( + const ecs_world_t *world, + ecs_entity_t entity) { - if ((term_id->flags & EcsIsEntity) && (term_id->flags & EcsIsVariable)) { - flecs_filter_error(ctx, "cannot set both IsEntity and IsVariable"); - return -1; - } + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); - if (!(term_id->flags & (EcsIsEntity|EcsIsVariable|EcsIsName))) { - if (term_id->id || term_id->name) { - if (term_id->id == EcsThis || - term_id->id == EcsWildcard || - term_id->id == EcsAny || - term_id->id == EcsVariable) - { - /* Builtin variable ids default to variable */ - term_id->flags |= EcsIsVariable; - } else { - term_id->flags |= EcsIsEntity; - } - } - } + world = ecs_get_world(world); - if (term_id->flags & EcsParent) { - term_id->flags |= EcsUp; - term_id->trav = EcsChildOf; - } + return flecs_entities_exists(world, entity); +error: + return false; +} - if ((term_id->flags & EcsCascade) && !(term_id->flags & (EcsUp|EcsDown))) { - term_id->flags |= EcsUp; - } +void ecs_set_version( + ecs_world_t *world, + ecs_entity_t entity_with_generation) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot change entity generation when world is in readonly mode"); + ecs_assert(!(ecs_is_deferred(world)), ECS_INVALID_OPERATION, + "cannot change entity generation while world is deferred"); - if ((term_id->flags & (EcsUp|EcsDown)) && !term_id->trav) { - term_id->trav = EcsIsA; - } + flecs_entities_make_alive(world, entity_with_generation); - if (term_id->trav && !(term_id->flags & EcsTraverseFlags)) { - term_id->flags |= EcsUp; + ecs_record_t *r = flecs_entities_get(world, entity_with_generation); + if (r && r->table) { + int32_t row = ECS_RECORD_TO_ROW(r->row); + ecs_entity_t *entities = r->table->data.entities; + entities[row] = entity_with_generation; } - - return 0; } -static -int flecs_term_id_lookup( +ecs_table_t* ecs_get_table( const ecs_world_t *world, - ecs_entity_t scope, - ecs_term_id_t *term_id, - bool free_name, - ecs_filter_finalize_ctx_t *ctx) + ecs_entity_t entity) { - const char *name = term_id->name; - if (!name) { - return 0; - } - - if (term_id->flags & EcsIsVariable) { - if (!ecs_os_strcmp(name, "This") || !ecs_os_strcmp(name, "this")) { - term_id->id = EcsThis; - if (free_name) { - /* Safe, if free_name is true the filter owns the name */ - ecs_os_free(ECS_CONST_CAST(char*, term_id->name)); - } - term_id->name = NULL; - } - return 0; - } else if (term_id->flags & EcsIsName) { - return 0; - } - - ecs_assert(term_id->flags & EcsIsEntity, ECS_INTERNAL_ERROR, NULL); - - if (ecs_identifier_is_0(name)) { - if (term_id->id) { - flecs_filter_error(ctx, "name '0' does not match entity id"); - return -1; - } - return 0; - } + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_valid(world, entity), ECS_INVALID_PARAMETER, NULL); + + world = ecs_get_world(world); - ecs_entity_t e = ecs_lookup_symbol(world, name, true, true); - if (scope && !e) { - e = ecs_lookup_child(world, scope, name); - } + ecs_record_t *record = flecs_entities_get(world, entity); + ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); + return record->table; +error: + return NULL; +} - if (!e) { - if (ctx->filter && (ctx->filter->flags & EcsFilterUnresolvedByName)) { - term_id->flags |= EcsIsName; - term_id->flags &= ~EcsIsEntity; - } else { - flecs_filter_error(ctx, "unresolved identifier '%s'", name); - return -1; - } +const ecs_type_t* ecs_get_type( + const ecs_world_t *world, + ecs_entity_t entity) +{ + ecs_table_t *table = ecs_get_table(world, entity); + if (table) { + return &table->type; } - if (term_id->id && term_id->id != e) { - char *e_str = ecs_get_fullpath(world, term_id->id); - flecs_filter_error(ctx, "name '%s' does not match term.id '%s'", - name, e_str); - ecs_os_free(e_str); - return -1; - } + return NULL; +} - term_id->id = e; +const ecs_type_info_t* ecs_get_type_info( + const ecs_world_t *world, + ecs_id_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); - if (!ecs_os_strcmp(name, "*") || !ecs_os_strcmp(name, "_") || - !ecs_os_strcmp(name, "$")) - { - term_id->flags &= ~EcsIsEntity; - term_id->flags |= EcsIsVariable; - } + world = ecs_get_world(world); - /* Check if looked up id is alive (relevant for numerical ids) */ - if (!(term_id->flags & EcsIsName)) { - if (!ecs_is_alive(world, term_id->id)) { - flecs_filter_error(ctx, "identifier '%s' is not alive", term_id->name); - return -1; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr && ECS_IS_PAIR(id)) { + idr = flecs_id_record_get(world, + ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard)); + if (!idr || !idr->type_info) { + idr = NULL; } - - if (free_name) { - /* Safe, if free_name is true, the filter owns the name */ - ecs_os_free(ECS_CONST_CAST(char*, name)); + if (!idr) { + ecs_entity_t first = ecs_pair_first(world, id); + if (!first || !ecs_has_id(world, first, EcsPairIsTag)) { + idr = flecs_id_record_get(world, + ecs_pair(EcsWildcard, ECS_PAIR_SECOND(id))); + if (!idr || !idr->type_info) { + idr = NULL; + } + } } - - term_id->name = NULL; } - return 0; + if (idr) { + return idr->type_info; + } else if (!(id & ECS_ID_FLAGS_MASK)) { + return flecs_type_info_get(world, id); + } +error: + return NULL; } -static -int flecs_term_ids_finalize( +ecs_entity_t ecs_get_typeid( const ecs_world_t *world, - ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) + ecs_id_t id) { - ecs_term_id_t *src = &term->src; - ecs_term_id_t *first = &term->first; - ecs_term_id_t *second = &term->second; - - /* Include inherited components (like from prefabs) by default for src */ - if (!(src->flags & EcsTraverseFlags)) { - src->flags |= EcsSelf | EcsUp; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + const ecs_type_info_t *ti = ecs_get_type_info(world, id); + if (ti) { + ecs_assert(ti->component != 0, ECS_INTERNAL_ERROR, NULL); + return ti->component; } +error: + return 0; +} - /* Include subsets for component by default, to support inheritance */ - if (!(first->flags & EcsTraverseFlags)) { - first->flags |= EcsSelf; - if (first->id && first->flags & EcsIsEntity) { - if (flecs_id_record_get(world, ecs_pair(EcsIsA, first->id))) { - first->flags |= EcsDown; +bool ecs_id_is_tag( + const ecs_world_t *world, + ecs_id_t id) +{ + if (ecs_id_is_wildcard(id)) { + /* If id is a wildcard, we can't tell if it's a tag or not, except + * when the relationship part of a pair has the Tag property */ + if (ECS_HAS_ID_FLAG(id, PAIR)) { + if (ECS_PAIR_FIRST(id) != EcsWildcard) { + ecs_entity_t rel = ecs_pair_first(world, id); + if (ecs_is_valid(world, rel)) { + if (ecs_has_id(world, rel, EcsPairIsTag)) { + return true; + } + } else { + /* During bootstrap it's possible that not all ids are valid + * yet. Using ecs_get_typeid will ensure correct values are + * returned for only those components initialized during + * bootstrap, while still asserting if another invalid id + * is provided. */ + if (ecs_get_typeid(world, id) == 0) { + return true; + } + } + } else { + /* If relationship is wildcard id is not guaranteed to be a tag */ } } + } else { + if (ecs_get_typeid(world, id) == 0) { + return true; + } } - /* Traverse Self by default for pair target */ - if (!(second->flags & EcsTraverseFlags)) { - second->flags |= EcsSelf; - } - - /* Source defaults to This */ - if ((src->id == 0) && (src->name == NULL) && !(src->flags & EcsIsEntity)) { - src->id = EcsThis; - src->flags |= EcsIsVariable; - } - - /* Initialize term identifier flags */ - if (flecs_term_id_finalize_flags(src, ctx)) { - return -1; - } - if (flecs_term_id_finalize_flags(first, ctx)) { - return -1; - } - - if (flecs_term_id_finalize_flags(second, ctx)) { - return -1; - } - - /* Lookup term identifiers by name */ - if (flecs_term_id_lookup(world, 0, src, term->move, ctx)) { - return -1; - } - if (flecs_term_id_lookup(world, 0, first, term->move, ctx)) { - return -1; - } - - ecs_entity_t first_id = 0; - ecs_entity_t oneof = 0; - if (first->flags & EcsIsEntity) { - first_id = first->id; - - /* If first element of pair has OneOf property, lookup second element of - * pair in the value of the OneOf property */ - oneof = flecs_get_oneof(world, first_id); - } + return false; +} - if (flecs_term_id_lookup(world, oneof, &term->second, term->move, ctx)) { - return -1; - } +int32_t ecs_count_id( + const ecs_world_t *world, + ecs_entity_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - /* If source is 0, reset traversal flags */ - if (src->id == 0 && src->flags & EcsIsEntity) { - src->flags &= ~EcsTraverseFlags; - src->trav = 0; - } - /* If second is 0, reset traversal flags */ - if (second->id == 0 && second->flags & EcsIsEntity) { - second->flags &= ~EcsTraverseFlags; - second->trav = 0; + if (!id) { + return 0; } - /* If source is wildcard, term won't return any data */ - if ((src->flags & EcsIsVariable) && ecs_id_is_wildcard(src->id)) { - term->inout |= EcsInOutNone; + int32_t count = 0; + ecs_iter_t it = ecs_each_id(world, id); + while (ecs_each_next(&it)) { + count += it.count; } + return count; +error: return 0; } -static -ecs_entity_t flecs_term_id_get_entity( - const ecs_term_id_t *term_id) +void ecs_enable( + ecs_world_t *world, + ecs_entity_t entity, + bool enabled) { - if (term_id->flags & EcsIsEntity) { - return term_id->id; /* Id is known */ - } else if (term_id->flags & EcsIsVariable) { - /* Return wildcard for variables, as they aren't known yet */ - if (term_id->id != EcsAny) { - /* Any variable should not use wildcard, as this would return all - * ids matching a wildcard, whereas Any returns the first match */ - return EcsWildcard; - } else { - return EcsAny; + if (ecs_has_id(world, entity, EcsPrefab)) { + /* If entity is a type, enable/disable all entities in the type */ + const ecs_type_t *type = ecs_get_type(world, entity); + ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_id_t *ids = type->array; + int32_t i, count = type->count; + for (i = 0; i < count; i ++) { + ecs_id_t id = ids[i]; + if (!(ecs_id_get_flags(world, id) & EcsIdOnInstantiateDontInherit)){ + ecs_enable(world, id, enabled); + } } } else { - return 0; /* Term id is uninitialized */ + if (enabled) { + ecs_remove_id(world, entity, EcsDisabled); + } else { + ecs_add_id(world, entity, EcsDisabled); + } } } -static -int flecs_term_populate_id( - ecs_term_t *term) +bool ecs_defer_begin( + ecs_world_t *world) { - ecs_entity_t first = flecs_term_id_get_entity(&term->first); - ecs_entity_t second = flecs_term_id_get_entity(&term->second); - ecs_id_t role = term->id_flags; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stage_t *stage = flecs_stage_from_world(&world); + return flecs_defer_begin(world, stage); +error: + return false; +} - if (first & ECS_ID_FLAGS_MASK) { - return -1; - } - if (second & ECS_ID_FLAGS_MASK) { - return -1; - } +bool ecs_defer_end( + ecs_world_t *world) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stage_t *stage = flecs_stage_from_world(&world); + return flecs_defer_end(world, stage); +error: + return false; +} - if ((second || term->second.flags == EcsIsEntity)) { - role = term->id_flags |= ECS_PAIR; - } +void ecs_defer_suspend( + ecs_world_t *world) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_deferred(world), ECS_INVALID_OPERATION, + "world/stage must be deferred before it can be suspended"); + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_check(stage->defer > 0, ECS_INVALID_OPERATION, + "world/stage is already suspended"); + stage->defer = -stage->defer; +error: + return; +} + +void ecs_defer_resume( + ecs_world_t *world) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_check(stage->defer < 0, ECS_INVALID_OPERATION, + "world/stage must be suspended before it can be resumed"); + stage->defer = -stage->defer; +error: + return; +} - if (!second && !ECS_HAS_ID_FLAG(role, PAIR)) { - term->id = first | role; +const char* ecs_id_flag_str( + ecs_entity_t entity) +{ + if (ECS_HAS_ID_FLAG(entity, PAIR)) { + return "PAIR"; + } else + if (ECS_HAS_ID_FLAG(entity, TOGGLE)) { + return "TOGGLE"; + } else + if (ECS_HAS_ID_FLAG(entity, AUTO_OVERRIDE)) { + return "AUTO_OVERRIDE"; } else { - term->id = ecs_pair(first, second) | role; + return "UNKNOWN"; } - - return 0; } -static -int flecs_term_populate_from_id( +void ecs_id_str_buf( const ecs_world_t *world, - ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) + ecs_id_t id, + ecs_strbuf_t *buf) { - ecs_entity_t first = 0; - ecs_entity_t second = 0; - ecs_id_t role = term->id & ECS_ID_FLAGS_MASK; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - if (!role && term->id_flags) { - role = term->id_flags; - term->id |= role; - } + world = ecs_get_world(world); - if (term->id_flags && term->id_flags != role) { - flecs_filter_error(ctx, "mismatch between term.id & term.id_flags"); - return -1; + if (ECS_HAS_ID_FLAG(id, TOGGLE)) { + ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_TOGGLE)); + ecs_strbuf_appendch(buf, '|'); } - term->id_flags = role; + if (ECS_HAS_ID_FLAG(id, AUTO_OVERRIDE)) { + ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_AUTO_OVERRIDE)); + ecs_strbuf_appendch(buf, '|'); + } - if (ECS_HAS_ID_FLAG(term->id, PAIR)) { - first = ECS_PAIR_FIRST(term->id); - second = ECS_PAIR_SECOND(term->id); + if (ECS_HAS_ID_FLAG(id, PAIR)) { + ecs_entity_t rel = ECS_PAIR_FIRST(id); + ecs_entity_t obj = ECS_PAIR_SECOND(id); - if (!first) { - flecs_filter_error(ctx, "missing first element in term.id"); - return -1; - } - if (!second) { - if (first != EcsChildOf) { - flecs_filter_error(ctx, "missing second element in term.id"); - return -1; - } else { - /* (ChildOf, 0) is allowed so filter can be used to efficiently - * query for root entities */ - } + ecs_entity_t e; + if ((e = ecs_get_alive(world, rel))) { + rel = e; } - } else { - first = term->id & ECS_COMPONENT_MASK; - if (!first) { - flecs_filter_error(ctx, "missing first element in term.id"); - return -1; + if ((e = ecs_get_alive(world, obj))) { + obj = e; } - } - ecs_entity_t term_first = flecs_term_id_get_entity(&term->first); - if (term_first) { - if ((uint32_t)term_first != (uint32_t)first) { - flecs_filter_error(ctx, "mismatch between term.id and term.first"); - return -1; - } + ecs_strbuf_appendch(buf, '('); + ecs_get_path_w_sep_buf(world, 0, rel, NULL, NULL, buf, false); + ecs_strbuf_appendch(buf, ','); + ecs_get_path_w_sep_buf(world, 0, obj, NULL, NULL, buf, false); + ecs_strbuf_appendch(buf, ')'); } else { - if (!(term->first.id = ecs_get_alive(world, first))) { - term->first.id = first; - } + ecs_entity_t e = id & ECS_COMPONENT_MASK; + ecs_get_path_w_sep_buf(world, 0, e, NULL, NULL, buf, false); } - ecs_entity_t term_second = flecs_term_id_get_entity(&term->second); - if (term_second) { - if ((uint32_t)term_second != second) { - flecs_filter_error(ctx, "mismatch between term.id and term.second"); - return -1; - } - } else if (second) { - if (!(term->second.id = ecs_get_alive(world, second))) { - term->second.id = second; - } - } +error: + return; +} - return 0; +char* ecs_id_str( + const ecs_world_t *world, + ecs_id_t id) +{ + ecs_strbuf_t buf = ECS_STRBUF_INIT; + ecs_id_str_buf(world, id, &buf); + return ecs_strbuf_get(&buf); } static -int flecs_term_verify_eq_pred( - const ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) +void ecs_type_str_buf( + const ecs_world_t *world, + const ecs_type_t *type, + ecs_strbuf_t *buf) { - ecs_entity_t first_id = term->first.id; - const ecs_term_id_t *second = &term->second; - const ecs_term_id_t *src = &term->src; + ecs_entity_t *ids = type->array; + int32_t i, count = type->count; - if (term->oper != EcsAnd && term->oper != EcsNot && term->oper != EcsOr) { - flecs_filter_error(ctx, "invalid operator combination"); - goto error; - } + for (i = 0; i < count; i ++) { + ecs_entity_t id = ids[i]; - if ((src->flags & EcsIsName) && (second->flags & EcsIsName)) { - flecs_filter_error(ctx, "both sides of operator cannot be a name"); - goto error; - } + if (i) { + ecs_strbuf_appendch(buf, ','); + ecs_strbuf_appendch(buf, ' '); + } - if ((src->flags & EcsIsEntity) && (second->flags & EcsIsEntity)) { - flecs_filter_error(ctx, "both sides of operator cannot be an entity"); - goto error; + if (id == 1) { + ecs_strbuf_appendlit(buf, "Component"); + } else { + ecs_id_str_buf(world, id, buf); + } } +} - if (!(src->flags & EcsIsVariable)) { - flecs_filter_error(ctx, "left-hand of operator must be a variable"); - goto error; +char* ecs_type_str( + const ecs_world_t *world, + const ecs_type_t *type) +{ + if (!type) { + return ecs_os_strdup(""); } - if (first_id == EcsPredMatch && !(second->flags & EcsIsName)) { - flecs_filter_error(ctx, "right-hand of match operator must be a string"); - goto error; - } + ecs_strbuf_t buf = ECS_STRBUF_INIT; + ecs_type_str_buf(world, type, &buf); + return ecs_strbuf_get(&buf); +} - if ((src->flags & EcsIsVariable) && (second->flags & EcsIsVariable)) { - if (src->id && src->id == second->id) { - flecs_filter_error(ctx, "both sides of operator are equal"); - goto error; - } - if (src->name && second->name && !ecs_os_strcmp(src->name, second->name)) { - flecs_filter_error(ctx, "both sides of operator are equal"); - goto error; - } +char* ecs_table_str( + const ecs_world_t *world, + const ecs_table_t *table) +{ + if (table) { + return ecs_type_str(world, &table->type); + } else { + return NULL; } - - return 0; -error: - return -1; } -static -int flecs_term_verify( +char* ecs_entity_str( const ecs_world_t *world, - const ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) + ecs_entity_t entity) { - const ecs_term_id_t *first = &term->first; - const ecs_term_id_t *second = &term->second; - const ecs_term_id_t *src = &term->src; - ecs_entity_t first_id = 0, second_id = 0; - ecs_id_t role = term->id_flags; - ecs_id_t id = term->id; - - if ((src->flags & EcsIsName) && (second->flags & EcsIsName)) { - flecs_filter_error(ctx, "mismatch between term.id_flags & term.id"); - return -1; - } + ecs_strbuf_t buf = ECS_STRBUF_INIT; + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - if (first->flags & EcsIsEntity) { - first_id = first->id; - } - if (second->flags & EcsIsEntity) { - second_id = second->id; + ecs_get_path_w_sep_buf(world, 0, entity, 0, "", &buf, false); + + ecs_strbuf_appendlit(&buf, " ["); + const ecs_type_t *type = ecs_get_type(world, entity); + if (type) { + ecs_type_str_buf(world, type, &buf); } + ecs_strbuf_appendch(&buf, ']'); - if (first_id == EcsPredEq || first_id == EcsPredMatch || first_id == EcsPredLookup) { - return flecs_term_verify_eq_pred(term, ctx); - } + return ecs_strbuf_get(&buf); +error: + return NULL; +} - if (role != (id & ECS_ID_FLAGS_MASK)) { - flecs_filter_error(ctx, "mismatch between term.id_flags & term.id"); - return -1; - } +static +void flecs_flush_bulk_new( + ecs_world_t *world, + ecs_cmd_t *cmd) +{ + ecs_entity_t *entities = cmd->is._n.entities; - if (ecs_term_id_is_set(second) && !ECS_HAS_ID_FLAG(role, PAIR)) { - flecs_filter_error(ctx, "expected PAIR flag for term with pair"); - return -1; - } else if (!ecs_term_id_is_set(second) && ECS_HAS_ID_FLAG(role, PAIR)) { - if (first_id != EcsChildOf) { - flecs_filter_error(ctx, "unexpected PAIR flag for term without pair"); - return -1; - } else { - /* Exception is made for ChildOf so we can use (ChildOf, 0) to match - * all entities in the root */ + if (cmd->id) { + int i, count = cmd->is._n.count; + for (i = 0; i < count; i ++) { + flecs_entities_ensure(world, entities[i]); + flecs_add_id(world, entities[i], cmd->id); } } - if (!ecs_term_id_is_set(src)) { - flecs_filter_error(ctx, "term.src is not initialized"); - return -1; + ecs_os_free(entities); +} + +static +void flecs_dtor_value( + ecs_world_t *world, + ecs_id_t id, + void *value, + int32_t count) +{ + const ecs_type_info_t *ti = ecs_get_type_info(world, id); + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_xtor_t dtor = ti->hooks.dtor; + if (dtor) { + ecs_size_t size = ti->size; + void *ptr; + int i; + for (i = 0, ptr = value; i < count; i ++, ptr = ECS_OFFSET(ptr, size)) { + dtor(ptr, 1, ti); + } } +} - if (!ecs_term_id_is_set(first)) { - flecs_filter_error(ctx, "term.first is not initialized"); - return -1; +static +void flecs_free_cmd_event( + ecs_world_t *world, + ecs_event_desc_t *desc) +{ + if (desc->ids) { + flecs_stack_free_n(desc->ids->array, ecs_id_t, desc->ids->count); } - if (ECS_HAS_ID_FLAG(role, PAIR)) { - if (!ECS_PAIR_FIRST(id)) { - flecs_filter_error(ctx, "invalid 0 for first element in pair id"); - return -1; - } - if ((ECS_PAIR_FIRST(id) != EcsChildOf) && !ECS_PAIR_SECOND(id)) { - flecs_filter_error(ctx, "invalid 0 for second element in pair id"); - return -1; - } + /* Safe const cast, command makes a copy of type object */ + flecs_stack_free_t(ECS_CONST_CAST(ecs_type_t*, desc->ids), + ecs_type_t); - if ((first->flags & EcsIsEntity) && - (ecs_entity_t_lo(first_id) != ECS_PAIR_FIRST(id))) - { - flecs_filter_error(ctx, "mismatch between term.id and term.first"); - return -1; - } - if ((first->flags & EcsIsVariable) && - !ecs_id_is_wildcard(ECS_PAIR_FIRST(id))) - { - char *id_str = ecs_id_str(world, id); - flecs_filter_error(ctx, - "expected wildcard for variable term.first (got %s)", id_str); - ecs_os_free(id_str); - return -1; - } + if (desc->param) { + flecs_dtor_value(world, desc->event, + /* Safe const cast, command makes copy of value */ + ECS_CONST_CAST(void*, desc->param), 1); + } +} - if ((second->flags & EcsIsEntity) && - (ecs_entity_t_lo(second_id) != ECS_PAIR_SECOND(id))) - { - flecs_filter_error(ctx, "mismatch between term.id and term.second"); - return -1; - } - if ((second->flags & EcsIsVariable) && - !ecs_id_is_wildcard(ECS_PAIR_SECOND(id))) - { - char *id_str = ecs_id_str(world, id); - flecs_filter_error(ctx, - "expected wildcard for variable term.second (got %s)", id_str); - ecs_os_free(id_str); - return -1; - } +static +void flecs_discard_cmd( + ecs_world_t *world, + ecs_cmd_t *cmd) +{ + if (cmd->kind == EcsCmdBulkNew) { + ecs_os_free(cmd->is._n.entities); + } else if (cmd->kind == EcsCmdEvent) { + flecs_free_cmd_event(world, cmd->is._1.value); } else { - ecs_entity_t component = id & ECS_COMPONENT_MASK; - if (!component) { - flecs_filter_error(ctx, "missing component id"); - return -1; - } - if ((first->flags & EcsIsEntity) && - (ecs_entity_t_lo(first_id) != ecs_entity_t_lo(component))) - { - flecs_filter_error(ctx, "mismatch between term.id and term.first"); - return -1; - } - if ((first->flags & EcsIsVariable) && !ecs_id_is_wildcard(component)) { - char *id_str = ecs_id_str(world, id); - flecs_filter_error(ctx, - "expected wildcard for variable term.first (got %s)", id_str); - ecs_os_free(id_str); - return -1; + ecs_assert(cmd->kind != EcsCmdEvent, ECS_INTERNAL_ERROR, NULL); + void *value = cmd->is._1.value; + if (value) { + flecs_dtor_value(world, cmd->id, value, 1); + flecs_stack_free(value, cmd->is._1.size); } } +} - if (first_id) { - if (ecs_term_id_is_set(second)) { - ecs_flags32_t mask = EcsIsEntity | EcsIsVariable; - if ((src->flags & mask) == (second->flags & mask)) { - bool is_same = false; - if (src->flags & EcsIsEntity) { - is_same = src->id == second->id; - } else if (src->name && second->name) { - is_same = !ecs_os_strcmp(src->name, second->name); - } - - if (is_same && ecs_has_id(world, first_id, EcsAcyclic) - && !(term->flags & EcsTermReflexive)) - { - char *pred_str = ecs_get_fullpath(world, term->first.id); - flecs_filter_error(ctx, "term with acyclic relationship" - " '%s' cannot have same subject and object", - pred_str); - ecs_os_free(pred_str); - return -1; - } - } - } - - if (second_id && !ecs_id_is_wildcard(second_id)) { - ecs_entity_t oneof = flecs_get_oneof(world, first_id); - if (oneof) { - if (!ecs_has_pair(world, second_id, EcsChildOf, oneof)) { - char *second_str = ecs_get_fullpath(world, second_id); - char *oneof_str = ecs_get_fullpath(world, oneof); - char *id_str = ecs_id_str(world, term->id); - flecs_filter_error(ctx, - "invalid target '%s' for %s: must be child of '%s'", - second_str, id_str, oneof_str); - ecs_os_free(second_str); - ecs_os_free(oneof_str); - ecs_os_free(id_str); - return -1; +static +bool flecs_remove_invalid( + ecs_world_t *world, + ecs_id_t id, + ecs_id_t *id_out) +{ + if (ECS_HAS_ID_FLAG(id, PAIR)) { + ecs_entity_t rel = ECS_PAIR_FIRST(id); + if (!flecs_entities_is_valid(world, rel)) { + /* After relationship is deleted we can no longer see what its + * delete action was, so pretend this never happened */ + *id_out = 0; + return true; + } else { + ecs_entity_t obj = ECS_PAIR_SECOND(id); + if (!flecs_entities_is_valid(world, obj)) { + /* Check the relationship's policy for deleted objects */ + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_pair(rel, EcsWildcard)); + if (idr) { + ecs_entity_t action = ECS_ID_ON_DELETE_TARGET(idr->flags); + if (action == EcsDelete) { + /* Entity should be deleted, don't bother checking + * other ids */ + return false; + } else if (action == EcsPanic) { + /* If policy is throw this object should not have + * been deleted */ + flecs_throw_invalid_delete(world, id); + } else { + *id_out = 0; + return true; + } + } else { + *id_out = 0; + return true; } } } - } - - if (term->src.trav) { - if (!ecs_has_id(world, term->src.trav, EcsTraversable)) { - char *r_str = ecs_get_fullpath(world, term->src.trav); - flecs_filter_error(ctx, - "cannot traverse non-traversable relationship '%s'", r_str); - ecs_os_free(r_str); - return -1; + } else { + id &= ECS_COMPONENT_MASK; + if (!flecs_entities_is_valid(world, id)) { + /* After relationship is deleted we can no longer see what its + * delete action was, so pretend this never happened */ + *id_out = 0; + return true; } } - return 0; + return true; } static -int flecs_term_finalize( - const ecs_world_t *world, - ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) +ecs_table_t* flecs_cmd_batch_add_diff( + ecs_world_t *world, + ecs_table_t *dst, + ecs_table_t *cur, + ecs_table_t *prev) { - ctx->term = term; + int32_t p = 0, p_count = prev->type.count; + int32_t c = 0, c_count = cur->type.count; + ecs_id_t *p_ids = prev->type.array; + ecs_id_t *c_ids = cur->type.array; - ecs_term_id_t *src = &term->src; - ecs_term_id_t *first = &term->first; - ecs_term_id_t *second = &term->second; - ecs_flags32_t first_flags = first->flags; - ecs_flags32_t src_flags = src->flags; - ecs_flags32_t second_flags = second->flags; + for (; c < c_count;) { + ecs_id_t c_id = c_ids[c]; + ecs_id_t p_id = p_ids[p]; - if (term->id) { - if (flecs_term_populate_from_id(world, term, ctx)) { - return -1; + if (p_id < c_id) { + /* Previous id no longer exists in new table, so it was removed */ + dst = ecs_table_remove_id(world, dst, p_id); + } + if (c_id < p_id) { + /* Current id didn't exist in previous table, so it was added */ + dst = ecs_table_add_id(world, dst, c_id); } - } - - if (flecs_term_ids_finalize(world, term, ctx)) { - return -1; - } - if ((first->flags & EcsIsVariable) && (term->first.id == EcsAny)) { - term->flags |= EcsTermMatchAny; - } - if ((second->flags & EcsIsVariable) && (term->second.id == EcsAny)) { - term->flags |= EcsTermMatchAny; - } - if ((src->flags & EcsIsVariable) && (term->src.id == EcsAny)) { - term->flags |= EcsTermMatchAnySrc; + c += c_id <= p_id; + p += p_id <= c_id; + if (p == p_count) { + break; + } } - /* If EcsVariable is used by itself, assign to predicate (singleton) */ - if ((src->id == EcsVariable) && (src->flags & EcsIsVariable)) { - src->id = first->id; - src->flags &= ~(EcsIsVariable | EcsIsEntity); - src->flags |= first->flags & (EcsIsVariable | EcsIsEntity); + /* Remainder */ + for (; p < p_count; p ++) { + ecs_id_t p_id = p_ids[p]; + dst = ecs_table_remove_id(world, dst, p_id); } - if ((second->id == EcsVariable) && (second->flags & EcsIsVariable)) { - second->id = first->id; - second->flags &= ~(EcsIsVariable | EcsIsEntity); - second->flags |= first->flags & (EcsIsVariable | EcsIsEntity); + for (; c < c_count; c ++) { + ecs_id_t c_id = c_ids[c]; + dst = ecs_table_add_id(world, dst, c_id); } - ecs_flags32_t mask = EcsIsEntity | EcsIsVariable; - if ((src->flags & mask) == (second->flags & mask)) { - bool is_same = false; - if (src->flags & EcsIsEntity) { - is_same = src->id == second->id; - } else if (src->name && second->name) { - is_same = !ecs_os_strcmp(src->name, second->name); - } - if (is_same) { - term->flags |= EcsTermSrcSecondEq; - } - } - if ((src->flags & mask) == (first->flags & mask)) { - bool is_same = false; - if (src->flags & EcsIsEntity) { - is_same = src->id == first->id; - } else if (src->name && first->name) { - is_same = !ecs_os_strcmp(src->name, first->name); - } - if (is_same) { - term->flags |= EcsTermSrcFirstEq; - } - } + return dst; +} - if (!term->id) { - if (flecs_term_populate_id(term)) { - return -1; - } +static +void flecs_cmd_batch_for_entity( + ecs_world_t *world, + ecs_table_diff_builder_t *diff, + ecs_entity_t entity, + ecs_cmd_t *cmds, + int32_t start) +{ + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_table_t *table = NULL; + if (r) { + table = r->table; } - /* If term queries for !(ChildOf, _), translate it to the builtin - * (ChildOf, 0) index which is a cheaper way to find root entities */ - if (term->oper == EcsNot && term->id == ecs_pair(EcsChildOf, EcsAny)) { - term->oper = EcsAnd; - term->id = ecs_pair(EcsChildOf, 0); - second->id = 0; - second->flags |= EcsIsEntity; - second->flags &= ~EcsIsVariable; - } + world->info.cmd.batched_entity_count ++; - ecs_entity_t first_id = 0; - if (first->flags & EcsIsEntity) { - first_id = first->id; - } + 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 */ + int32_t cur = start; + ecs_id_t id; - term->idr = flecs_query_id_record_get(world, term->id); - ecs_flags32_t id_flags = term->idr ? term->idr->flags : 0; + /* Mask to let observable implementation know which components were set. + * This prevents the code from overwriting components with an override if + * the batch also contains an IsA pair. */ + ecs_flags64_t set_mask = 0; - if (first_id) { - ecs_entity_t first_trav = first->trav; - - /* If component is inherited from, set correct traversal flags */ - ecs_flags32_t first_trav_flags = first_flags & EcsTraverseFlags; - if (!first_trav && first_trav_flags != EcsSelf) { - /* Inheritance uses IsA by default, but can use any relationship */ - first_trav = EcsIsA; - } - - ecs_record_t *trav_record = NULL; - ecs_table_t *trav_table = NULL; - if (first_trav) { - trav_record = flecs_entities_get(world, first_trav); - trav_table = trav_record ? trav_record->table : NULL; - if (first_trav != EcsIsA) { - if (!trav_table || !ecs_table_has_id(world, trav_table, EcsTraversable)) { - flecs_filter_error(ctx, "first.trav is not traversable"); - return -1; - } - } + do { + cmd = &cmds[cur]; + id = cmd->id; + next_for_entity = cmd->next_for_entity; + if (next_for_entity < 0) { + /* First command for an entity has a negative index, flip sign */ + next_for_entity *= -1; } - /* Only enable inheritance for ids which are inherited from at the time - * of filter creation. To force component inheritance to be evaluated, - * an application can explicitly set traversal flags. */ - if ((first_trav_flags & EcsDown) || - flecs_id_record_get(world, ecs_pair(first_trav, first->id))) - { - if (first_trav_flags == EcsSelf) { - flecs_filter_error(ctx, "first.trav specified with self"); - return -1; - } - - if (!first_trav_flags || (first_trav_flags & EcsDown)) { - term->flags |= EcsTermIdInherited; - first->trav = first_trav; - if (!first_trav_flags) { - first->flags &= ~EcsTraverseFlags; - first->flags |= EcsDown; - ecs_assert(trav_table != NULL, ECS_INTERNAL_ERROR, NULL); - if ((first_trav == EcsIsA) || ecs_table_has_id( - world, trav_table, EcsReflexive)) - { - first->flags |= EcsSelf; - } + /* Check if added id is still valid (like is the parent of a ChildOf + * pair still alive), if not run cleanup actions for entity */ + if (id) { + if (flecs_remove_invalid(world, id, &id)) { + if (!id) { + /* Entity should remain alive but id should not be added */ + cmd->kind = EcsCmdSkip; + continue; } + /* Entity should remain alive and id is still valid */ + } else { + /* Id was no longer valid and had a Delete policy */ + cmd->kind = EcsCmdSkip; + ecs_delete(world, entity); + flecs_table_diff_builder_clear(diff); + return; } } - /* Don't traverse ids that cannot be inherited */ - if ((id_flags & EcsIdDontInherit) && (src->trav == EcsIsA)) { - if (src_flags & (EcsUp | EcsDown)) { - flecs_filter_error(ctx, - "traversing not allowed for id that can't be inherited"); - return -1; - } - src->flags &= ~(EcsUp | EcsDown); - src->trav = 0; - } + ecs_cmd_kind_t kind = cmd->kind; + switch(kind) { + case EcsCmdAddModified: + /* Add is batched, but keep Modified */ + cmd->kind = EcsCmdModified; - /* If component id is final, don't attempt component inheritance */ - ecs_record_t *first_record = flecs_entities_get(world, first_id); - ecs_table_t *first_table = first_record ? first_record->table : NULL; - if (first_table) { - if (ecs_table_has_id(world, first_table, EcsFinal)) { - if (first_flags & EcsDown) { - flecs_filter_error(ctx, "final id cannot be traversed down"); - return -1; - } - } + /* fall through */ + case EcsCmdAdd: + table = flecs_find_table_add(world, table, id, diff); + world->info.cmd.batched_command_count ++; + break; + case EcsCmdModified: + 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); + ecs_id_record_t *idr = flecs_id_record_get(world, cmd->id); + const ecs_table_record_t *tr = + flecs_id_record_get_table(idr, start_table); + if (tr) { + const ecs_type_info_t *ti = idr->type_info; + ecs_iter_action_t on_set; + if ((on_set = ti->hooks.on_set)) { + ecs_table_t *prev_table = r->table; + ecs_defer_begin(world); + flecs_invoke_hook(world, start_table, tr, 1, row, + &entity,cmd->id, ti, EcsOnSet, on_set); + ecs_defer_end(world); - /* Add traversal flags for transitive relationships */ - if (!(second_flags & EcsTraverseFlags) && ecs_term_id_is_set(second)) { - if (!((src->flags & EcsIsVariable) && (src->id == EcsAny))) { - if (!((second->flags & EcsIsVariable) && (second->id == EcsAny))) { - if (ecs_table_has_id(world, first_table, EcsTransitive)) { - second->flags |= EcsSelf|EcsUp|EcsTraverseAll; - second->trav = first_id; - term->flags |= EcsTermTransitive; + /* Don't run on_set hook twice, but make sure to still + * invoke observers. */ + cmd->kind = EcsCmdModifiedNoHook; + + /* If entity changed tables in hook, add difference to + * destination table, so we don't lose the side effects + * of the hook. */ + if (r->table != prev_table) { + table = flecs_cmd_batch_add_diff( + world, table, r->table, prev_table); } } } } + break; + case EcsCmdSet: + case EcsCmdEnsure: { + ecs_id_t *ids = diff->added.array; + uint8_t added_index = flecs_ito(uint8_t, diff->added.count); - if (ecs_table_has_id(world, first_table, EcsReflexive)) { - term->flags |= EcsTermReflexive; - } - } - } - - if (first->id == EcsVariable) { - flecs_filter_error(ctx, "invalid $ for term.first"); - return -1; - } - - if (term->id_flags & ECS_AND) { - term->oper = EcsAndFrom; - term->id &= ECS_COMPONENT_MASK; - term->id_flags = 0; - } + table = flecs_find_table_add(world, table, id, diff); - if (term->oper == EcsAndFrom || term->oper == EcsOrFrom || term->oper == EcsNotFrom) { - if (term->inout != EcsInOutDefault && term->inout != EcsInOutNone) { - flecs_filter_error(ctx, - "invalid inout value for AndFrom/OrFrom/NotFrom term"); - return -1; - } - } + if (diff->added.count == (added_index + 1)) { + /* Single id was added, must be at the end of the array */ + ecs_assert(ids[added_index] == id, ECS_INTERNAL_ERROR, NULL); + set_mask |= (1llu << added_index); + } else { + /* Id was already added or multiple ids got added. Do a linear + * search to find the index we need to set the set_mask. */ + int32_t i; + for (i = 0; i < diff->added.count; i ++) { + if (ids[i] == id) { + break; + } + } - /* Is term trivial */ - bool trivial_term = true; - if (term->oper != EcsAnd) { - trivial_term = false; - } - if (ecs_id_is_wildcard(term->id)) { - if (!(term->idr && term->idr->flags & EcsIdExclusive)) { - trivial_term = false; - } - if (first->flags & EcsIsVariable) { - if (!ecs_id_is_wildcard(first->id) || first->id == EcsAny) { - trivial_term = false; + ecs_assert(i != diff->added.count, ECS_INTERNAL_ERROR, NULL); + set_mask |= (1llu << i); } + + world->info.cmd.batched_command_count ++; + break; } - if (second->flags & EcsIsVariable) { - if (!ecs_id_is_wildcard(second->id) || second->id == EcsAny) { - trivial_term = false; + case EcsCmdEmplace: + /* Don't add for emplace, as this requires a special call to ensure + * the constructor is not invoked for the component */ + break; + case EcsCmdRemove: + table = flecs_find_table_remove(world, table, id, diff); + world->info.cmd.batched_command_count ++; + break; + case EcsCmdClear: + if (table) { + ecs_id_t *ids = ecs_vec_grow_t(&world->allocator, + &diff->removed, ecs_id_t, table->type.count); + ecs_os_memcpy_n(ids, table->type.array, ecs_id_t, + table->type.count); + diff->removed_flags |= table->flags & EcsTableRemoveEdgeFlags; } + table = &world->store.root; + world->info.cmd.batched_command_count ++; + break; + case EcsCmdClone: + case EcsCmdBulkNew: + case EcsCmdPath: + case EcsCmdDelete: + case EcsCmdOnDeleteAction: + case EcsCmdEnable: + case EcsCmdDisable: + case EcsCmdEvent: + case EcsCmdSkip: + case EcsCmdModifiedNoHook: + break; } - } - - if (term->flags & EcsTermIdInherited) { - trivial_term = false; - } - if (src->trav && src->trav != EcsIsA) { - trivial_term = false; - } - if (first->trav && first->trav != EcsIsA) { - trivial_term = false; - } - if (second->trav && second->trav != EcsIsA) { - trivial_term = false; - } - if (!(src->flags & EcsSelf)) { - trivial_term = false; - } - if (trivial_term) { - ECS_BIT_SET(term->flags, EcsTermIsTrivial); - } - - if (flecs_term_verify(world, term, ctx)) { - return -1; - } - - return 0; -} - -ecs_id_t flecs_to_public_id( - ecs_id_t id) -{ - if (ECS_PAIR_FIRST(id) == EcsUnion) { - return ecs_pair(ECS_PAIR_SECOND(id), EcsWildcard); - } else { - return id; - } -} -ecs_id_t flecs_from_public_id( - ecs_world_t *world, - ecs_id_t id) -{ - if (ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_entity_t first = ECS_PAIR_FIRST(id); - ecs_id_record_t *idr = flecs_id_record_ensure(world, - ecs_pair(first, EcsWildcard)); - if (idr->flags & EcsIdUnion) { - return ecs_pair(EcsUnion, first); + /* Add, remove and clear operations can be skipped since they have no + * side effects besides adding/removing components */ + if (kind == EcsCmdAdd || kind == EcsCmdRemove || kind == EcsCmdClear) { + cmd->kind = EcsCmdSkip; } - } + } while ((cur = next_for_entity)); - return id; -} + /* Invoke OnAdd handlers after commit. This ensures that observers with + * mixed OnAdd/OnSet events won't get called with uninitialized values for + * an OnSet field. */ + ecs_type_t added = { diff->added.array, diff->added.count }; + diff->added.array = NULL; + diff->added.count = 0; -bool ecs_identifier_is_0( - const char *id) -{ - return id[0] == '0' && !id[1]; -} + /* Move entity to destination table in single operation */ + flecs_table_diff_build_noalloc(diff, &table_diff); + flecs_defer_begin(world, world->stages[0]); + flecs_commit(world, entity, r, table, &table_diff, true, 0); + flecs_defer_end(world, world->stages[0]); -bool ecs_id_match( - ecs_id_t id, - ecs_id_t pattern) -{ - if (id == pattern) { - return true; + /* If destination table has new sparse components, make sure they're created + * for the entity. */ + if (table && (table->flags & EcsTableHasSparse) && added.count) { + flecs_sparse_on_add( + world, table, ECS_RECORD_TO_ROW(r->row), 1, &added, true); } - if (ECS_HAS_ID_FLAG(pattern, PAIR)) { - if (!ECS_HAS_ID_FLAG(id, PAIR)) { - return false; - } - - ecs_entity_t id_rel = ECS_PAIR_FIRST(id); - ecs_entity_t id_obj = ECS_PAIR_SECOND(id); - ecs_entity_t pattern_rel = ECS_PAIR_FIRST(pattern); - ecs_entity_t pattern_obj = ECS_PAIR_SECOND(pattern); - - ecs_check(id_rel != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(id_obj != 0, ECS_INVALID_PARAMETER, NULL); - - ecs_check(pattern_rel != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(pattern_obj != 0, ECS_INVALID_PARAMETER, NULL); - - if (pattern_rel == EcsWildcard) { - if (pattern_obj == EcsWildcard || pattern_obj == id_obj) { - return true; + /* If the batch contains set commands, copy the component value from the + * temporary command storage to the actual component storage before OnSet + * observers are invoked. This ensures that for multi-component OnSet + * observers all components are assigned a valid value before the observer + * is invoked. + * This only happens for entities that didn't have the assigned component + * yet, as for entities that did have the component already the value will + * have been assigned directly to the component storage. */ + if (set_mask) { + cur = start; + do { + cmd = &cmds[cur]; + next_for_entity = cmd->next_for_entity; + if (next_for_entity < 0) { + next_for_entity *= -1; } - } else if (pattern_rel == EcsFlag) { - /* Used for internals, helps to keep track of which ids are used in - * pairs that have additional flags (like OVERRIDE and TOGGLE) */ - if (ECS_HAS_ID_FLAG(id, PAIR) && !ECS_IS_PAIR(id)) { - if (ECS_PAIR_FIRST(id) == pattern_obj) { - return true; + + switch(cmd->kind) { + case EcsCmdSet: + case EcsCmdEnsure: { + flecs_component_ptr_t ptr = {0}; + if (r->table) { + ecs_id_record_t *idr = flecs_id_record_get(world, cmd->id); + ptr = flecs_get_component_ptr( + r->table, ECS_RECORD_TO_ROW(r->row), idr); } - if (ECS_PAIR_SECOND(id) == pattern_obj) { - return true; + + /* It's possible that even though the component was set, the + * command queue also contained a remove command, so before we + * do anything ensure the entity actually has the component. */ + if (ptr.ptr) { + const ecs_type_info_t *ti = ptr.ti; + ecs_move_t move = ti->hooks.move; + if (move) { + move(ptr.ptr, cmd->is._1.value, 1, ti); + ecs_xtor_t dtor = ti->hooks.dtor; + if (dtor) { + dtor(cmd->is._1.value, 1, ti); + cmd->is._1.value = NULL; + } + } else { + ecs_os_memcpy(ptr.ptr, cmd->is._1.value, ti->size); + } + + if (cmd->kind == EcsCmdSet) { + /* A set operation is add + copy + modified. We just did + * the add the copy, so the only thing that's left is a + * modified command, which will call the OnSet + * observers. */ + cmd->kind = EcsCmdModified; + } else { + /* If this was an ensure, nothing's left to be done */ + cmd->kind = EcsCmdSkip; + } + } else { + /* The entity no longer has the component which means that + * there was a remove command for the component in the + * command queue. In that case skip the command. */ + cmd->kind = EcsCmdSkip; } + break; } - } else if (pattern_obj == EcsWildcard) { - if (pattern_rel == id_rel) { - return true; + case EcsCmdClone: + case EcsCmdBulkNew: + case EcsCmdAdd: + case EcsCmdRemove: + case EcsCmdEmplace: + case EcsCmdModified: + case EcsCmdModifiedNoHook: + case EcsCmdAddModified: + case EcsCmdPath: + case EcsCmdDelete: + case EcsCmdClear: + case EcsCmdOnDeleteAction: + case EcsCmdEnable: + case EcsCmdDisable: + case EcsCmdEvent: + case EcsCmdSkip: + break; } - } - } else { - if ((id & ECS_ID_FLAGS_MASK) != (pattern & ECS_ID_FLAGS_MASK)) { - return false; - } + } while ((cur = next_for_entity)); + } - if ((ECS_COMPONENT_MASK & pattern) == EcsWildcard) { - return true; + if (added.count && r->table) { + ecs_table_diff_t add_diff = ECS_TABLE_DIFF_INIT; + add_diff.added = added; + add_diff.added_flags = diff->added_flags; + + flecs_defer_begin(world, world->stages[0]); + flecs_notify_on_add(world, r->table, start_table, + ECS_RECORD_TO_ROW(r->row), 1, &add_diff, 0, set_mask, true, false); + flecs_defer_end(world, world->stages[0]); + if (r->row & EcsEntityIsTraversable) { + /* Update monitors since we didn't do this in flecs_commit. */ + flecs_update_component_monitors(world, &added, NULL); } } -error: - return false; -} + diff->added.array = added.array; + diff->added.count = added.count; -bool ecs_id_is_pair( - ecs_id_t id) -{ - return ECS_HAS_ID_FLAG(id, PAIR); + flecs_table_diff_builder_clear(diff); } -bool ecs_id_is_wildcard( - ecs_id_t id) +/* Leave safe section. Run all deferred commands. */ +bool flecs_defer_end( + ecs_world_t *world, + ecs_stage_t *stage) { - if ((id == EcsWildcard) || (id == EcsAny)) { - return true; - } + flecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(stage, ecs_stage_t); - bool is_pair = ECS_IS_PAIR(id); - if (!is_pair) { + if (stage->defer < 0) { + /* Defer suspending makes it possible to do operations on the storage + * without flushing the commands in the queue */ return false; } - ecs_entity_t first = ECS_PAIR_FIRST(id); - ecs_entity_t second = ECS_PAIR_SECOND(id); - - return (first == EcsWildcard) || (second == EcsWildcard) || - (first == EcsAny) || (second == EcsAny); -} + ecs_assert(stage->defer > 0, ECS_INTERNAL_ERROR, NULL); -bool ecs_id_is_valid( - const ecs_world_t *world, - ecs_id_t id) -{ - if (!id) { - return false; - } - if (ecs_id_is_wildcard(id)) { - return false; - } + if (!--stage->defer) { + ecs_os_perf_trace_push("flecs.commands.merge"); - if (ECS_HAS_ID_FLAG(id, PAIR)) { - if (!ECS_PAIR_FIRST(id)) { - return false; - } - if (!ECS_PAIR_SECOND(id)) { - return false; - } - } else if (id & ECS_ID_FLAGS_MASK) { - if (!ecs_is_valid(world, id & ECS_COMPONENT_MASK)) { - return false; + /* Test whether we're flushing to another queue or whether we're + * flushing to the storage */ + bool merge_to_world = false; + if (flecs_poly_is(world, ecs_world_t)) { + merge_to_world = world->stages[0]->defer == 0; } - } - return true; -} + ecs_stage_t *dst_stage = flecs_stage_from_world(&world); + ecs_commands_t *commands = stage->cmd; + ecs_vec_t *queue = &commands->queue; -ecs_flags32_t ecs_id_get_flags( - const ecs_world_t *world, - ecs_id_t id) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (idr) { - return idr->flags; - } else { - return 0; - } -} + if (ecs_vec_count(queue)) { + /* Internal callback for capturing commands */ + if (world->on_commands_active) { + world->on_commands_active(stage, queue, + world->on_commands_ctx_active); + } -bool ecs_term_id_is_set( - const ecs_term_id_t *id) -{ - return id->id != 0 || id->name != NULL || id->flags & EcsIsEntity; -} + ecs_cmd_t *cmds = ecs_vec_first(queue); + int32_t i, count = ecs_vec_count(queue); -bool ecs_term_is_initialized( - const ecs_term_t *term) -{ - return term->id != 0 || ecs_term_id_is_set(&term->first); -} + ecs_table_diff_builder_t diff; + flecs_table_diff_builder_init(world, &diff); + flecs_commands_push(stage); -bool ecs_term_match_this( - const ecs_term_t *term) -{ - return (term->src.flags & EcsIsVariable) && (term->src.id == EcsThis); -} + for (i = 0; i < count; i ++) { + ecs_cmd_t *cmd = &cmds[i]; + ecs_entity_t e = cmd->entity; + bool is_alive = flecs_entities_is_alive(world, e); -bool ecs_term_match_0( - const ecs_term_t *term) -{ - return (term->src.id == 0) && (term->src.flags & EcsIsEntity); -} + /* A negative index indicates the first command for an entity */ + if (merge_to_world && (cmd->next_for_entity < 0)) { + /* Batch commands for entity to limit archetype moves */ + if (is_alive) { + flecs_cmd_batch_for_entity(world, &diff, e, cmds, i); + } else { + world->info.cmd.discard_count ++; + } + } -int ecs_term_finalize( - const ecs_world_t *world, - ecs_term_t *term) -{ - ecs_filter_finalize_ctx_t ctx = {0}; - ctx.world = world; - ctx.term = term; - return flecs_term_finalize(world, term, &ctx); -} + /* Invalidate entry */ + if (cmd->entry) { + cmd->entry->first = -1; + } -ecs_term_t ecs_term_copy( - const ecs_term_t *src) -{ - ecs_term_t dst = *src; - dst.name = ecs_os_strdup(src->name); - dst.first.name = ecs_os_strdup(src->first.name); - dst.src.name = ecs_os_strdup(src->src.name); - dst.second.name = ecs_os_strdup(src->second.name); - return dst; -} + /* If entity is no longer alive, this could be because the queue + * contained both a delete and a subsequent add/remove/set which + * should be ignored. */ + ecs_cmd_kind_t kind = cmd->kind; + if ((kind != EcsCmdPath) && ((kind == EcsCmdSkip) || (e && !is_alive))) { + world->info.cmd.discard_count ++; + flecs_discard_cmd(world, cmd); + continue; + } -ecs_term_t ecs_term_move( - ecs_term_t *src) -{ - if (src->move) { - ecs_term_t dst = *src; - src->name = NULL; - src->first.name = NULL; - src->src.name = NULL; - src->second.name = NULL; - dst.move = false; - return dst; - } else { - ecs_term_t dst = ecs_term_copy(src); - dst.move = false; - return dst; - } -} + ecs_id_t id = cmd->id; -void ecs_term_fini( - ecs_term_t *term) -{ - /* Safe, values are owned by term */ - ecs_os_free(ECS_CONST_CAST(char*, term->first.name)); - ecs_os_free(ECS_CONST_CAST(char*, term->src.name)); - ecs_os_free(ECS_CONST_CAST(char*, term->second.name)); - ecs_os_free(term->name); + switch(kind) { + case EcsCmdAdd: + ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); + if (flecs_remove_invalid(world, id, &id)) { + if (id) { + world->info.cmd.add_count ++; + flecs_add_id(world, e, id); + } else { + world->info.cmd.discard_count ++; + } + } else { + world->info.cmd.discard_count ++; + ecs_delete(world, e); + } + break; + case EcsCmdRemove: + flecs_remove_id(world, e, id); + world->info.cmd.remove_count ++; + break; + case EcsCmdClone: + ecs_clone(world, e, id, cmd->is._1.clone_value); + world->info.cmd.other_count ++; + break; + case EcsCmdSet: + flecs_set_id_move(world, dst_stage, e, + cmd->id, flecs_itosize(cmd->is._1.size), + cmd->is._1.value, kind); + world->info.cmd.set_count ++; + break; + case EcsCmdEmplace: + if (merge_to_world) { + bool is_new; + ecs_emplace_id(world, e, id, &is_new); + if (!is_new) { + kind = EcsCmdEnsure; + } + } + flecs_set_id_move(world, dst_stage, e, + cmd->id, flecs_itosize(cmd->is._1.size), + cmd->is._1.value, kind); + world->info.cmd.ensure_count ++; + break; + case EcsCmdEnsure: + flecs_set_id_move(world, dst_stage, e, + cmd->id, flecs_itosize(cmd->is._1.size), + cmd->is._1.value, kind); + world->info.cmd.ensure_count ++; + break; + case EcsCmdModified: + flecs_modified_id_if(world, e, id, true); + world->info.cmd.modified_count ++; + break; + case EcsCmdModifiedNoHook: + flecs_modified_id_if(world, e, id, false); + world->info.cmd.modified_count ++; + break; + case EcsCmdAddModified: + flecs_add_id(world, e, id); + flecs_modified_id_if(world, e, id, true); + world->info.cmd.set_count ++; + break; + case EcsCmdDelete: { + ecs_delete(world, e); + world->info.cmd.delete_count ++; + break; + } + case EcsCmdClear: + ecs_clear(world, e); + world->info.cmd.clear_count ++; + break; + case EcsCmdOnDeleteAction: + ecs_defer_begin(world); + flecs_on_delete(world, id, e, false); + ecs_defer_end(world); + world->info.cmd.other_count ++; + break; + case EcsCmdEnable: + ecs_enable_id(world, e, id, true); + world->info.cmd.other_count ++; + break; + case EcsCmdDisable: + ecs_enable_id(world, e, id, false); + world->info.cmd.other_count ++; + break; + case EcsCmdBulkNew: + flecs_flush_bulk_new(world, cmd); + world->info.cmd.other_count ++; + continue; + case EcsCmdPath: { + bool keep_alive = true; + ecs_make_alive(world, e); + if (cmd->id) { + if (ecs_is_alive(world, cmd->id)) { + ecs_add_pair(world, e, EcsChildOf, cmd->id); + } else { + ecs_delete(world, e); + keep_alive = false; + } + } + if (keep_alive) { + ecs_set_name(world, e, cmd->is._1.value); + } + ecs_os_free(cmd->is._1.value); + cmd->is._1.value = NULL; + world->info.cmd.other_count ++; + break; + } + case EcsCmdEvent: { + ecs_event_desc_t *desc = cmd->is._1.value; + ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_emit((ecs_world_t*)stage, desc); + flecs_free_cmd_event(world, desc); + world->info.cmd.event_count ++; + break; + } + case EcsCmdSkip: + break; + } - term->first.name = NULL; - term->src.name = NULL; - term->second.name = NULL; - term->name = NULL; -} + if (cmd->is._1.value) { + flecs_stack_free(cmd->is._1.value, cmd->is._1.size); + } + } -static -ecs_term_t* flecs_filter_or_other_type( - ecs_filter_t *f, - int32_t t) -{ - ecs_term_t *term = &f->terms[t]; - ecs_term_t *first = NULL; - while (t--) { - if (f->terms[t].oper != EcsOr) { - break; - } - first = &f->terms[t]; - } + flecs_stack_reset(&commands->stack); + ecs_vec_clear(queue); + flecs_commands_pop(stage); - if (first) { - ecs_world_t *world = f->world; - const ecs_type_info_t *first_type; - if (first->idr) { - first_type = first->idr->type_info; - } else { - first_type = ecs_get_type_info(world, first->id); - } - const ecs_type_info_t *term_type; - if (term->idr) { - term_type = term->idr->type_info; - } else { - term_type = ecs_get_type_info(world, term->id); - } + flecs_table_diff_builder_fini(world, &diff); - if (first_type == term_type) { - return NULL; + /* Internal callback for capturing commands, signal queue is done */ + if (world->on_commands_active) { + world->on_commands_active(stage, NULL, + world->on_commands_ctx_active); + } } - return first; - } else { - return NULL; + + ecs_os_perf_trace_pop("flecs.commands.merge"); + + return true; } + + return false; } -int ecs_filter_finalize( - const ecs_world_t *world, - ecs_filter_t *f) +/* Delete operations from queue without executing them. */ +bool flecs_defer_purge( + ecs_world_t *world, + ecs_stage_t *stage) { - int32_t i, term_count = f->term_count, field_count = 0; - ecs_term_t *terms = f->terms; - int32_t filter_terms = 0, scope_nesting = 0; - bool cond_set = false; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_filter_finalize_ctx_t ctx = {0}; - ctx.world = world; - ctx.filter = f; + if (!--stage->defer) { + ecs_vec_t commands = stage->cmd->queue; - f->flags |= EcsFilterMatchOnlyThis; + if (ecs_vec_count(&commands)) { + ecs_cmd_t *cmds = ecs_vec_first(&commands); + int32_t i, count = ecs_vec_count(&commands); + for (i = 0; i < count; i ++) { + flecs_discard_cmd(world, &cmds[i]); + } - for (i = 0; i < term_count; i ++) { - bool filter_term = false; - ecs_term_t *term = &terms[i]; - ctx.term_index = i; - if (flecs_term_finalize(world, term, &ctx)) { - return -1; - } + ecs_vec_fini_t(&stage->allocator, &stage->cmd->queue, ecs_cmd_t); - if (i && term[-1].oper == EcsOr) { - if (term[-1].src.id != term->src.id) { - flecs_filter_error(&ctx, "mismatching src.id for OR terms"); - return -1; - } - if (term->oper != EcsOr && term->oper != EcsAnd) { - flecs_filter_error(&ctx, - "term after OR operator must use AND operator"); - return -1; - } - } else { - field_count ++; + ecs_vec_clear(&commands); + flecs_stack_reset(&stage->cmd->stack); + flecs_sparse_clear(&stage->cmd->entries); } - term->field_index = field_count - 1; + return true; + } - if (ecs_id_is_wildcard(term->id)) { - f->flags |= EcsFilterHasWildcards; - } +error: + return false; +} - if (ecs_term_match_this(term)) { - ECS_BIT_SET(f->flags, EcsFilterMatchThis); - } else { - ECS_BIT_CLEAR(f->flags, EcsFilterMatchOnlyThis); - } +/** + * @file entity_name.c + * @brief Functions for working with named entities. + */ - if (term->id == EcsPrefab) { - ECS_BIT_SET(f->flags, EcsFilterMatchPrefab); - } - if (term->id == EcsDisabled && (term->src.flags & EcsSelf)) { - ECS_BIT_SET(f->flags, EcsFilterMatchDisabled); - } +#include - if (ECS_BIT_IS_SET(f->flags, EcsFilterNoData)) { - term->inout = EcsInOutNone; - } - - if (term->oper == EcsNot && term->inout == EcsInOutDefault) { - term->inout = EcsInOutNone; - } +#define ECS_NAME_BUFFER_LENGTH (64) - if ((term->id == EcsWildcard) || (term->id == - ecs_pair(EcsWildcard, EcsWildcard))) - { - /* If term type is unknown beforehand, default the inout type to - * none. This prevents accidentally requesting lots of components, - * which can put stress on serializer code. */ - if (term->inout == EcsInOutDefault) { - term->inout = EcsInOutNone; - } - } +static +bool flecs_path_append( + const ecs_world_t *world, + ecs_entity_t parent, + ecs_entity_t child, + const char *sep, + const char *prefix, + ecs_strbuf_t *buf, + bool escape) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_assert(sep[0] != 0, ECS_INVALID_PARAMETER, NULL); - if (term->inout == EcsInOutNone) { - filter_term = true; - } else if (term->idr) { - if (!term->idr->type_info && !(term->idr->flags & EcsIdUnion)) { - filter_term = true; - } - } else if (!ecs_id_is_union(world, term->id)) { - /* Union ids aren't filters because they return their target - * as component value with type ecs_entity_t */ - if (ecs_id_is_tag(world, term->id)) { - filter_term = true; - } else if (ECS_PAIR_SECOND(term->id) == EcsWildcard) { - /* If the second element of a pair is a wildcard and the first - * element is not a type, we can't know in advance what the - * type of the term is, so it can't provide data. */ - if (!ecs_get_type_info(world, ecs_pair_first(world, term->id))) { - filter_term = true; - } - } - } - - if (!filter_term) { - if (term->oper == EcsOr || (i && term[-1].oper == EcsOr)) { - ecs_term_t *first = flecs_filter_or_other_type(f, i); - if (first) { - if (first == &term[-1]) { - filter_terms ++; + ecs_entity_t cur = 0; + const char *name = NULL; + ecs_size_t name_len = 0; + + if (child && ecs_is_alive(world, child)) { + ecs_record_t *r = flecs_entities_get(world, child); + bool hasName = false; + if (r && r->table) { + hasName = r->table->flags & EcsTableHasName; + } + + if (hasName) { + cur = ecs_get_target(world, child, EcsChildOf, 0); + if (cur) { + ecs_assert(cur != child, ECS_CYCLE_DETECTED, NULL); + if (cur != parent && (cur != EcsFlecsCore || prefix != NULL)) { + flecs_path_append(world, parent, cur, sep, prefix, buf, escape); + if (!sep[1]) { + ecs_strbuf_appendch(buf, sep[0]); + } else { + ecs_strbuf_appendstr(buf, sep); } - filter_term = true; + } + } else if (prefix && prefix[0]) { + if (!prefix[1]) { + ecs_strbuf_appendch(buf, prefix[0]); + } else { + ecs_strbuf_appendstr(buf, prefix); } } - } - - if (filter_term) { - filter_terms ++; - term->flags |= EcsTermNoData; - } else { - f->data_fields |= (1llu << term->field_index); - } - if (term->oper != EcsNot || !ecs_term_match_this(term)) { - ECS_BIT_CLEAR(f->flags, EcsFilterMatchAnything); - } + const EcsIdentifier *id = ecs_get_pair( + world, child, EcsIdentifier, EcsName); + if (id) { + name = id->value; + name_len = id->length; + } + } + } - if (term->idr) { - if (ecs_os_has_threading()) { - ecs_os_ainc(&term->idr->keep_alive); - } else { - term->idr->keep_alive ++; + if (name) { + /* Check if we need to escape separator character */ + const char *sep_in_name = NULL; + if (!sep[1]) { + sep_in_name = strchr(name, sep[0]); + } + + if (sep_in_name || escape) { + const char *name_ptr; + char ch; + for (name_ptr = name; (ch = name_ptr[0]); name_ptr ++) { + char esc[3]; + if (ch != sep[0]) { + if (escape) { + flecs_chresc(esc, ch, '\"'); + ecs_strbuf_appendch(buf, esc[0]); + if (esc[1]) { + ecs_strbuf_appendch(buf, esc[1]); + } + } else { + ecs_strbuf_appendch(buf, ch); + } + } else { + if (!escape) { + ecs_strbuf_appendch(buf, '\\'); + ecs_strbuf_appendch(buf, sep[0]); + } else { + ecs_strbuf_appendlit(buf, "\\\\"); + flecs_chresc(esc, ch, '\"'); + ecs_strbuf_appendch(buf, esc[0]); + if (esc[1]) { + ecs_strbuf_appendch(buf, esc[1]); + } + } + } } + } else { + ecs_strbuf_appendstrn(buf, name, name_len); } + } else { + ecs_strbuf_appendch(buf, '#'); + ecs_strbuf_appendint(buf, flecs_uto(int64_t, (uint32_t)child)); + } - if (term->oper == EcsOptional || term->oper == EcsNot) { - cond_set = true; - } - - if (term->first.id == EcsPredEq || term->first.id == EcsPredMatch || - term->first.id == EcsPredLookup) - { - f->flags |= EcsFilterHasPred; - } + return cur != 0; +} - if (term->first.id == EcsScopeOpen) { - f->flags |= EcsFilterHasScopes; - scope_nesting ++; - } - if (term->first.id == EcsScopeClose) { - if (i && terms[i - 1].first.id == EcsScopeOpen) { - flecs_filter_error(&ctx, "invalid empty scope"); - return -1; +bool flecs_name_is_id( + const char *name) +{ + ecs_assert(name != NULL, ECS_INTERNAL_ERROR, NULL); + if (name[0] == '#') { + /* If name is not just digits it's not an id */ + const char *ptr; + char ch; + for (ptr = name + 1; (ch = ptr[0]); ptr ++) { + if (!isdigit(ch)) { + return false; } - - f->flags |= EcsFilterHasScopes; - scope_nesting --; - } - if (scope_nesting < 0) { - flecs_filter_error(&ctx, "'}' without matching '{'"); } + return true; } + return false; +} - if (scope_nesting != 0) { - flecs_filter_error(&ctx, "missing '}'"); - return -1; - } +ecs_entity_t flecs_name_to_id( + const char *name) +{ + ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(name[0] == '#', ECS_INVALID_PARAMETER, NULL); + return flecs_ito(uint64_t, atoll(name + 1)); +} - if (term_count && (terms[term_count - 1].oper == EcsOr)) { - flecs_filter_error(&ctx, "last term of filter can't have OR operator"); - return -1; - } - - f->field_count = flecs_ito(int8_t, field_count); - - if (field_count) { - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_id_record_t *idr = term->idr; - int32_t field = term->field_index; - - if (term->oper == EcsOr || (i && (term[-1].oper == EcsOr))) { - if (flecs_filter_or_other_type(f, i)) { - f->sizes[field] = 0; - continue; - } - } - - if (idr) { - if (!ECS_IS_PAIR(idr->id) || ECS_PAIR_FIRST(idr->id) != EcsWildcard) { - if (idr->flags & EcsIdUnion) { - f->sizes[field] = ECS_SIZEOF(ecs_entity_t); - } else if (idr->type_info) { - f->sizes[field] = idr->type_info->size; - } - } - } else { - bool is_union = false; - if (ECS_IS_PAIR(term->id)) { - ecs_entity_t first = ecs_pair_first(world, term->id); - if (ecs_has_id(world, first, EcsUnion)) { - is_union = true; - } - } - if (is_union) { - f->sizes[field] = ECS_SIZEOF(ecs_entity_t); - } else { - const ecs_type_info_t *ti = ecs_get_type_info( - world, term->id); - if (ti) { - f->sizes[field] = ti->size; - } - } - } - } - } else { - f->sizes = NULL; - } - - ecs_assert(filter_terms <= term_count, ECS_INTERNAL_ERROR, NULL); - if (filter_terms == term_count) { - ECS_BIT_SET(f->flags, EcsFilterNoData); - } - - ECS_BIT_COND(f->flags, EcsFilterHasCondSet, cond_set); - - /* Check if this is a trivial filter */ - if ((f->flags & EcsFilterMatchOnlyThis)) { - if (!(f->flags & - (EcsFilterHasPred|EcsFilterMatchDisabled|EcsFilterMatchPrefab))) - { - ECS_BIT_SET(f->flags, EcsFilterMatchOnlySelf); - - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_term_id_t *src = &term->src; - - if (src->flags & EcsUp) { - ECS_BIT_CLEAR(f->flags, EcsFilterMatchOnlySelf); - } - - if (!(term->flags & EcsTermIsTrivial)) { - break; - } - - if (!(f->flags & EcsFilterNoData)) { - if (term->inout == EcsInOutNone) { - break; - } - } - } - if (term_count && (i == term_count)) { - ECS_BIT_SET(f->flags, EcsFilterIsTrivial); - } - } +static +ecs_entity_t flecs_get_builtin( + const char *name) +{ + if (name[0] == '.' && name[1] == '\0') { + return EcsThis; + } else if (name[0] == '*' && name[1] == '\0') { + return EcsWildcard; + } else if (name[0] == '_' && name[1] == '\0') { + return EcsAny; + } else if (name[0] == '$' && name[1] == '\0') { + return EcsVariable; } return 0; } -/* Implementation for iterable mixin */ static -void flecs_filter_iter_init( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter) +bool flecs_is_sep( + const char **ptr, + const char *sep) { - ecs_poly_assert(poly, ecs_filter_t); + ecs_size_t len = ecs_os_strlen(sep); - if (filter) { - iter[1] = ecs_filter_iter(world, ECS_CONST_CAST(ecs_filter_t*, poly)); - iter[0] = ecs_term_chain_iter(&iter[1], filter); + if (!ecs_os_strncmp(*ptr, sep, len)) { + *ptr += len; + return true; } else { - iter[0] = ecs_filter_iter(world, ECS_CONST_CAST(ecs_filter_t*, poly)); + return false; } } -/* Implementation for dtor mixin */ static -void flecs_filter_fini( - ecs_filter_t *filter) -{ - if (filter->terms) { - int i, count = filter->term_count; - for (i = 0; i < count; i ++) { - ecs_term_t *term = &filter->terms[i]; - if (term->idr) { - if (!(filter->world->flags & EcsWorldQuit)) { - if (ecs_os_has_threading()) { - ecs_os_adec(&term->idr->keep_alive); - } else { - term->idr->keep_alive --; - } - } - } - ecs_term_fini(&filter->terms[i]); - } - - if (filter->flags & EcsFilterOwnsTermsStorage) { - /* Memory allocated for both terms & sizes */ - ecs_os_free(filter->terms); - } else { - ecs_os_free(filter->sizes); - } - } - - filter->terms = NULL; - - if (filter->flags & EcsFilterOwnsStorage) { - ecs_os_free(filter); - } -} - -void ecs_filter_fini( - ecs_filter_t *filter) -{ - if ((filter->flags & EcsFilterOwnsStorage) && filter->entity) { - /* If filter is associated with entity, use poly dtor path */ - ecs_delete(filter->world, filter->entity); - } else { - flecs_filter_fini(filter); - } -} - -ecs_filter_t* ecs_filter_init( - ecs_world_t *world, - const ecs_filter_desc_t *desc) +const char* flecs_path_elem( + const char *path, + const char *sep, + char **buffer_out, + ecs_size_t *size_out) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - flecs_stage_from_world(&world); - - ecs_filter_t *f = desc->storage; - int32_t i, term_count = desc->terms_buffer_count, storage_count = 0, expr_count = 0; - const ecs_term_t *terms = desc->terms_buffer; - ecs_term_t *storage_terms = NULL, *expr_terms = NULL; - - if (f) { - ecs_check(f->hdr.magic == ecs_filter_t_magic, - ECS_INVALID_PARAMETER, NULL); - storage_count = f->term_count; - storage_terms = f->terms; - ecs_poly_init(f, ecs_filter_t); - } else { - f = ecs_poly_new(ecs_filter_t); - f->flags |= EcsFilterOwnsStorage; - } - if (!storage_terms) { - f->flags |= EcsFilterOwnsTermsStorage; + char *buffer = NULL; + if (buffer_out) { + buffer = *buffer_out; } - ECS_BIT_COND(f->flags, EcsFilterIsInstanced, desc->instanced); - ECS_BIT_SET(f->flags, EcsFilterMatchAnything); - f->flags |= desc->flags; - f->world = world; + const char *ptr; + char ch; + int32_t template_nesting = 0; + int32_t pos = 0; + ecs_size_t size = size_out ? *size_out : 0; - /* If terms_buffer was not set, count number of initialized terms in - * static desc::terms array */ - if (!terms) { - ecs_check(term_count == 0, ECS_INVALID_PARAMETER, NULL); - terms = desc->terms; - for (i = 0; i < FLECS_TERM_DESC_MAX; i ++) { - if (!ecs_term_is_initialized(&terms[i])) { - break; + for (ptr = path; (ch = *ptr); ptr ++) { + if (ch == '<') { + template_nesting ++; + } else if (ch == '>') { + template_nesting --; + } else if (ch == '\\') { + ptr ++; + if (buffer) { + buffer[pos] = ptr[0]; } - term_count ++; + pos ++; + continue; } - } else { - ecs_check(term_count != 0, ECS_INVALID_PARAMETER, NULL); - } - - /* If expr is set, parse query expression */ - const char *expr = desc->expr; - ecs_entity_t entity = desc->entity; - if (expr) { -#ifdef FLECS_PARSER - const char *name = NULL; - const char *ptr = desc->expr; - ecs_term_t term = {0}; - ecs_term_id_t extra_args[ECS_PARSER_MAX_ARGS]; - int32_t expr_size = 0; - ecs_os_zeromem(extra_args); + ecs_check(template_nesting >= 0, ECS_INVALID_PARAMETER, path); - if (entity) { - name = ecs_get_name(world, entity); + if (!template_nesting && flecs_is_sep(&ptr, sep)) { + break; } - while (ptr[0] && - (ptr = ecs_parse_term(world, name, expr, ptr, &term, extra_args))) - { - if (!ecs_term_is_initialized(&term)) { - break; - } - - int32_t arg = 0; - - do { - ecs_assert(arg <= ECS_PARSER_MAX_ARGS, ECS_INTERNAL_ERROR, NULL); - - if (expr_count == expr_size) { - expr_size = expr_size ? expr_size * 2 : 8; - expr_terms = ecs_os_realloc_n(expr_terms, ecs_term_t, expr_size); - } - - ecs_term_t *expr_term = &expr_terms[expr_count ++]; - *expr_term = term; - - if (arg) { - expr_term->src = expr_term[-1].second; - expr_term->second = extra_args[arg - 1]; - - if (expr_term->first.name != NULL) { - expr_term->first.name = ecs_os_strdup( - expr_term->first.name); - } - if (expr_term->src.name != NULL) { - expr_term->src.name = ecs_os_strdup( - expr_term->src.name); - } + if (buffer) { + if (pos == (size - 1)) { + if (size == ECS_NAME_BUFFER_LENGTH) { /* stack buffer */ + char *new_buffer = ecs_os_malloc(size * 2 + 1); + ecs_os_memcpy(new_buffer, buffer, size); + buffer = new_buffer; + } else { /* heap buffer */ + buffer = ecs_os_realloc(buffer, size * 2 + 1); } - } while (ecs_term_id_is_set(&extra_args[arg ++])); - - if (ptr[0] == '\n') { - break; + size *= 2; } - ecs_os_zeromem(extra_args); - } - - if (!ptr) { - /* Set terms in filter object to make sur they get cleaned up */ - f->terms = expr_terms; - f->term_count = flecs_ito(int8_t, expr_count); - f->flags |= EcsFilterOwnsTermsStorage; - goto error; - } -#else - (void)expr; - ecs_abort(ECS_UNSUPPORTED, "parser addon is not available"); -#endif - } - - /* If storage is provided, make sure it's large enough */ - ecs_check(!storage_terms || storage_count >= (term_count + expr_count), - ECS_INVALID_PARAMETER, NULL); - - if (term_count || expr_count) { - /* Allocate storage for terms and sizes array */ - if (!storage_terms) { - ecs_assert(f->flags & EcsFilterOwnsTermsStorage, - ECS_INTERNAL_ERROR, NULL); - f->term_count = flecs_ito(int8_t, term_count + expr_count); - ecs_size_t terms_size = ECS_SIZEOF(ecs_term_t) * f->term_count; - ecs_size_t sizes_size = ECS_SIZEOF(int32_t) * f->term_count; - f->terms = ecs_os_calloc(terms_size + sizes_size); - f->sizes = ECS_OFFSET(f->terms, terms_size); - } else { - f->terms = storage_terms; - f->term_count = flecs_ito(int8_t, storage_count); - f->sizes = ecs_os_calloc_n(ecs_size_t, term_count); - } - - /* Copy terms to filter storage */ - for (i = 0; i < term_count; i ++) { - f->terms[i] = ecs_term_copy(&terms[i]); - /* Allow freeing resources from expr parser during finalization */ - f->terms[i].move = true; - } - - /* Move expr terms to filter storage */ - for (i = 0; i < expr_count; i ++) { - f->terms[i + term_count] = ecs_term_move(&expr_terms[i]); - /* Allow freeing resources from expr parser during finalization */ - f->terms[i + term_count].move = true; + buffer[pos] = ch; } - ecs_os_free(expr_terms); - } - /* Ensure all fields are consistent and properly filled out */ - if (ecs_filter_finalize(world, f)) { - goto error; + pos ++; } - /* Any allocated resources remaining in terms are now owned by filter */ - for (i = 0; i < f->term_count; i ++) { - f->terms[i].move = false; + if (buffer) { + buffer[pos] = '\0'; + *buffer_out = buffer; + *size_out = size; } - f->variable_names[0] = NULL; - f->iterable.init = flecs_filter_iter_init; - f->dtor = (ecs_poly_dtor_t)flecs_filter_fini; - f->entity = entity; - - if (entity && (f->flags & EcsFilterOwnsStorage)) { - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_filter_t); - poly->poly = f; - ecs_poly_modified(world, entity, ecs_filter_t); + if (pos) { + return ptr; + } else { + return NULL; } - - return f; error: - ecs_filter_fini(f); return NULL; } -void ecs_filter_copy( - ecs_filter_t *dst, - const ecs_filter_t *src) -{ - if (src == dst) { - return; - } - - if (src) { - *dst = *src; - - int32_t i, term_count = src->term_count; - ecs_size_t terms_size = ECS_SIZEOF(ecs_term_t) * term_count; - ecs_size_t sizes_size = ECS_SIZEOF(int32_t) * term_count; - dst->terms = ecs_os_malloc(terms_size + sizes_size); - dst->sizes = ECS_OFFSET(dst->terms, terms_size); - dst->flags |= EcsFilterOwnsTermsStorage; - ecs_os_memcpy_n(dst->sizes, src->sizes, int32_t, term_count); - - for (i = 0; i < term_count; i ++) { - dst->terms[i] = ecs_term_copy(&src->terms[i]); - } - } else { - ecs_os_memset_t(dst, 0, ecs_filter_t); - } -} - -void ecs_filter_move( - ecs_filter_t *dst, - ecs_filter_t *src) +static +bool flecs_is_root_path( + const char *path, + const char *prefix) { - if (src == dst) { - return; - } - - if (src) { - *dst = *src; - if (src->flags & EcsFilterOwnsTermsStorage) { - dst->terms = src->terms; - dst->sizes = src->sizes; - dst->flags |= EcsFilterOwnsTermsStorage; - } else { - ecs_filter_copy(dst, src); - } - src->terms = NULL; - src->sizes = NULL; - src->term_count = 0; + if (prefix) { + return !ecs_os_strncmp(path, prefix, ecs_os_strlen(prefix)); } else { - ecs_os_memset_t(dst, 0, ecs_filter_t); + return false; } } static -void flecs_filter_str_add_id( +ecs_entity_t flecs_get_parent_from_path( const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_term_id_t *id, - bool is_subject, - ecs_flags32_t default_traverse_flags) + ecs_entity_t parent, + const char **path_ptr, + const char *sep, + const char *prefix, + bool new_entity) { - bool is_added = false; - if (!is_subject || id->id != EcsThis) { - if (id->flags & EcsIsVariable && !ecs_id_is_wildcard(id->id)) { - ecs_strbuf_appendlit(buf, "$"); - } - if (id->id) { - char *path = ecs_get_fullpath(world, id->id); - ecs_strbuf_appendstr(buf, path); - ecs_os_free(path); - } else if (id->name) { - ecs_strbuf_appendstr(buf, id->name); - } else { - ecs_strbuf_appendlit(buf, "0"); - } - is_added = true; - } + bool start_from_root = false; + const char *path = *path_ptr; - ecs_flags32_t flags = id->flags; - if (!(flags & EcsTraverseFlags)) { - /* If flags haven't been set yet, initialize with defaults. This can - * happen if an error is thrown while the term is being finalized */ - flags |= default_traverse_flags; + if (flecs_is_root_path(path, prefix)) { + path += ecs_os_strlen(prefix); + parent = 0; + start_from_root = true; } - if ((flags & EcsTraverseFlags) != default_traverse_flags) { - if (is_added) { - ecs_strbuf_list_push(buf, ":", "|"); - } else { - ecs_strbuf_list_push(buf, "", "|"); - } - if (id->flags & EcsSelf) { - ecs_strbuf_list_appendstr(buf, "self"); - } - if (id->flags & EcsUp) { - ecs_strbuf_list_appendstr(buf, "up"); + if (path[0] == '#') { + parent = flecs_name_to_id(path); + + path ++; + while (path[0] && isdigit(path[0])) { + path ++; /* Skip id part of path */ } - if (id->flags & EcsDown) { - ecs_strbuf_list_appendstr(buf, "down"); + + /* Skip next separator so that the returned path points to the next + * name element. */ + ecs_size_t sep_len = ecs_os_strlen(sep); + if (!ecs_os_strncmp(path, sep, ecs_os_strlen(sep))) { + path += sep_len; } - if (id->trav && (id->trav != EcsIsA)) { - ecs_strbuf_list_push(buf, "(", ""); + start_from_root = true; + } + + if (!start_from_root && !parent && new_entity) { + parent = ecs_get_scope(world); + } + + *path_ptr = path; - char *rel_path = ecs_get_fullpath(world, id->trav); - ecs_strbuf_appendstr(buf, rel_path); - ecs_os_free(rel_path); + return parent; +} - ecs_strbuf_list_pop(buf, ")"); - } +static +void flecs_on_set_symbol(ecs_iter_t *it) { + EcsIdentifier *n = ecs_field(it, EcsIdentifier, 0); + ecs_world_t *world = it->real_world; - ecs_strbuf_list_pop(buf, ""); + int i; + for (i = 0; i < it->count; i ++) { + ecs_entity_t e = it->entities[i]; + flecs_name_index_ensure( + &world->symbols, e, n[i].value, n[i].length, n[i].hash); } } -static -void flecs_term_str_w_strbuf( +void flecs_bootstrap_hierarchy(ecs_world_t *world) { + ecs_observer(world, { + .entity = ecs_entity(world, { .parent = EcsFlecsInternals }), + .query.terms[0] = { + .id = ecs_pair(ecs_id(EcsIdentifier), EcsSymbol) + }, + .callback = flecs_on_set_symbol, + .events = {EcsOnSet}, + .yield_existing = true + }); +} + + +/* Public functions */ + +void ecs_get_path_w_sep_buf( const ecs_world_t *world, - const ecs_term_t *term, + ecs_entity_t parent, + ecs_entity_t child, + const char *sep, + const char *prefix, ecs_strbuf_t *buf, - int32_t t) + bool escape) { - const ecs_term_id_t *src = &term->src; - const ecs_term_id_t *second = &term->second; - - uint8_t def_src_mask = EcsSelf|EcsUp; - uint8_t def_first_mask = EcsSelf; - uint8_t def_second_mask = EcsSelf; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(buf != NULL, ECS_INVALID_PARAMETER, NULL); - bool pred_set = ecs_term_id_is_set(&term->first); - bool subj_set = !ecs_term_match_0(term); - bool obj_set = ecs_term_id_is_set(second); + world = ecs_get_world(world); - if (term->first.id == EcsScopeOpen) { - ecs_strbuf_appendlit(buf, "{"); - return; - } else if (term->first.id == EcsScopeClose) { - ecs_strbuf_appendlit(buf, "}"); + if (child == EcsWildcard) { + ecs_strbuf_appendch(buf, '*'); return; } - - if (!t || !(term[-1].oper == EcsOr)) { - if (term->inout == EcsIn) { - ecs_strbuf_appendlit(buf, "[in] "); - } else if (term->inout == EcsInOut) { - ecs_strbuf_appendlit(buf, "[inout] "); - } else if (term->inout == EcsOut) { - ecs_strbuf_appendlit(buf, "[out] "); - } else if (term->inout == EcsInOutNone && term->oper != EcsNot) { - ecs_strbuf_appendlit(buf, "[none] "); - } - } - - if (term->first.flags & EcsIsEntity && term->first.id != 0) { - if (ecs_has_id(world, term->first.id, EcsDontInherit)) { - def_src_mask = EcsSelf; - } + if (child == EcsAny) { + ecs_strbuf_appendch(buf, '_'); + return; } - if (term->oper == EcsNot) { - ecs_strbuf_appendlit(buf, "!"); - } else if (term->oper == EcsOptional) { - ecs_strbuf_appendlit(buf, "?"); + if (!sep) { + sep = "."; } - if (!subj_set) { - flecs_filter_str_add_id(world, buf, &term->first, false, - def_first_mask); - if (!obj_set) { - ecs_strbuf_appendlit(buf, "()"); - } else { - ecs_strbuf_appendlit(buf, "(0,"); - flecs_filter_str_add_id(world, buf, &term->second, false, - def_second_mask); - ecs_strbuf_appendlit(buf, ")"); - } - } else if (ecs_term_match_this(term) && - (src->flags & EcsTraverseFlags) == def_src_mask) - { - if (pred_set) { - if (obj_set) { - ecs_strbuf_appendlit(buf, "("); - } - flecs_filter_str_add_id(world, buf, &term->first, false, def_first_mask); - if (obj_set) { - ecs_strbuf_appendlit(buf, ","); - flecs_filter_str_add_id( - world, buf, &term->second, false, def_second_mask); - ecs_strbuf_appendlit(buf, ")"); - } - } else if (term->id) { - char *str = ecs_id_str(world, term->id); - ecs_strbuf_appendstr(buf, str); - ecs_os_free(str); - } + if (!child || parent != child) { + flecs_path_append(world, parent, child, sep, prefix, buf, escape); } else { - if (term->id_flags && !ECS_HAS_ID_FLAG(term->id_flags, PAIR)) { - ecs_strbuf_appendstr(buf, ecs_id_flag_str(term->id_flags)); - ecs_strbuf_appendch(buf, '|'); - } - - flecs_filter_str_add_id(world, buf, &term->first, false, def_first_mask); - ecs_strbuf_appendlit(buf, "("); - if (term->src.flags & EcsIsEntity && term->src.id == term->first.id) { - ecs_strbuf_appendlit(buf, "$"); - } else { - flecs_filter_str_add_id(world, buf, &term->src, true, def_src_mask); - } - if (obj_set) { - ecs_strbuf_appendlit(buf, ","); - flecs_filter_str_add_id(world, buf, &term->second, false, def_second_mask); - } - ecs_strbuf_appendlit(buf, ")"); + ecs_strbuf_appendstrn(buf, "", 0); } + +error: + return; } -char* ecs_term_str( +char* ecs_get_path_w_sep( const ecs_world_t *world, - const ecs_term_t *term) + ecs_entity_t parent, + ecs_entity_t child, + const char *sep, + const char *prefix) { ecs_strbuf_t buf = ECS_STRBUF_INIT; - flecs_term_str_w_strbuf(world, term, &buf, 0); + ecs_get_path_w_sep_buf(world, parent, child, sep, prefix, &buf, false); return ecs_strbuf_get(&buf); } -static -char* flecs_filter_str( +ecs_entity_t ecs_lookup_child( const ecs_world_t *world, - const ecs_filter_t *filter, - const ecs_filter_finalize_ctx_t *ctx, - int32_t *term_start_out) + ecs_entity_t parent, + const char *name) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(filter != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_strbuf_t buf = ECS_STRBUF_INIT; - ecs_term_t *terms = filter->terms; - int32_t i, count = filter->term_count; - - for (i = 0; i < count; i ++) { - ecs_term_t *term = &terms[i]; - - if (term_start_out && ctx) { - if (ctx->term_index == i) { - term_start_out[0] = ecs_strbuf_written(&buf); - if (i) { - term_start_out[0] += 2; /* whitespace + , */ - } - } - } - - flecs_term_str_w_strbuf(world, term, &buf, i); + ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); + world = ecs_get_world(world); - if (i != (count - 1)) { - if (term->oper == EcsOr) { - ecs_strbuf_appendlit(&buf, " || "); - } else { - if (term->first.id != EcsScopeOpen) { - if (term[1].first.id != EcsScopeClose) { - ecs_strbuf_appendlit(&buf, ", "); - } - } + if (flecs_name_is_id(name)) { + ecs_entity_t result = flecs_name_to_id(name); + if (result && ecs_is_alive(world, result)) { + if (parent && !ecs_has_pair(world, result, EcsChildOf, parent)) { + return 0; } + return result; } } - return ecs_strbuf_get(&buf); + ecs_id_t pair = ecs_childof(parent); + ecs_hashmap_t *index = flecs_id_name_index_get(world, pair); + if (index) { + return flecs_name_index_find(index, name, 0, 0); + } else { + return 0; + } error: - return NULL; + return 0; } -char* ecs_filter_str( +ecs_entity_t ecs_lookup( const ecs_world_t *world, - const ecs_filter_t *filter) -{ - return flecs_filter_str(world, filter, NULL, NULL); + const char *path) +{ + return ecs_lookup_path_w_sep(world, 0, path, ".", NULL, true); } -int32_t ecs_filter_find_this_var( - const ecs_filter_t *filter) -{ - ecs_check(filter != NULL, ECS_INVALID_PARAMETER, NULL); - - if (ECS_BIT_IS_SET(filter->flags, EcsFilterMatchThis)) { - /* Filters currently only support the This variable at index 0. Only - * return 0 if filter actually has terms for the This variable. */ +ecs_entity_t ecs_lookup_symbol( + const ecs_world_t *world, + const char *name, + bool lookup_as_path, + bool recursive) +{ + if (!name) { return 0; } -error: - return -1; -} + ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); + world = ecs_get_world(world); -/* Check if the id is a pair that has Any as first or second element. Any - * pairs behave just like Wildcard pairs and reuses the same data structures, - * with as only difference that the number of results returned for an Any pair - * is never more than one. This function is used to tell the difference. */ -static -bool is_any_pair( - ecs_id_t id) -{ - if (!ECS_HAS_ID_FLAG(id, PAIR)) { - return false; + ecs_entity_t e = 0; + if (lookup_as_path) { + e = ecs_lookup_path_w_sep(world, 0, name, ".", NULL, recursive); } - if (ECS_PAIR_FIRST(id) == EcsAny) { - return true; - } - if (ECS_PAIR_SECOND(id) == EcsAny) { - return true; + if (!e) { + e = flecs_name_index_find(&world->symbols, name, 0, 0); } - return false; + return e; +error: + return 0; } -static -bool flecs_n_term_match_table( - ecs_world_t *world, - const ecs_term_t *term, - const ecs_table_t *table, - ecs_entity_t type_id, - ecs_oper_kind_t oper, - ecs_id_t *id_out, - int32_t *column_out, - ecs_entity_t *subject_out, - int32_t *match_index_out, - bool first, - ecs_flags32_t iter_flags) +ecs_entity_t ecs_lookup_path_w_sep( + const ecs_world_t *world, + ecs_entity_t parent, + const char *path, + const char *sep, + const char *prefix, + bool recursive) { - (void)column_out; - - const ecs_type_t *type = ecs_get_type(world, type_id); - ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); + if (!path) { + return 0; + } - ecs_id_t *ids = type->array; - int32_t i, count = type->count; - ecs_term_t temp = *term; - temp.oper = EcsAnd; + ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_world_t *stage = world; + world = ecs_get_world(world); - for (i = 0; i < count; i ++) { - ecs_id_t id = ids[i]; - if (ecs_id_get_flags(world, id) & EcsIdDontInherit) { - continue; - } - bool result; - if (ECS_HAS_ID_FLAG(id, AND)) { - ecs_oper_kind_t id_oper = EcsAndFrom; - result = flecs_n_term_match_table(world, term, table, - id & ECS_COMPONENT_MASK, id_oper, id_out, column_out, - subject_out, match_index_out, first, iter_flags); - } else { - temp.id = id; - result = flecs_term_match_table(world, &temp, table, id_out, - 0, subject_out, match_index_out, first, iter_flags); - } - if (!result && oper == EcsAndFrom) { - return false; - } else - if (result && oper == EcsOrFrom) { - return true; - } + ecs_entity_t e = flecs_get_builtin(path); + if (e) { + return e; } - if (oper == EcsAndFrom) { - if (id_out) { - id_out[0] = type_id; - } - return true; - } else - if (oper == EcsOrFrom) { - return false; + e = flecs_name_index_find(&world->aliases, path, 0, 0); + if (e) { + return e; } - return false; -} - -bool flecs_term_match_table( - ecs_world_t *world, - const ecs_term_t *term, - const ecs_table_t *table, - ecs_id_t *id_out, - int32_t *column_out, - ecs_entity_t *subject_out, - int32_t *match_index_out, - bool first, - ecs_flags32_t iter_flags) -{ - const ecs_term_id_t *src = &term->src; - ecs_oper_kind_t oper = term->oper; - const ecs_table_t *match_table = table; - ecs_id_t id = term->id; + char buff[ECS_NAME_BUFFER_LENGTH], *elem = buff; + const char *ptr; + int32_t size = ECS_NAME_BUFFER_LENGTH; + ecs_entity_t cur; + bool lookup_path_search = false; - ecs_entity_t src_id = src->id; - if (ecs_term_match_0(term)) { - if (id_out) { - id_out[0] = id; /* If no entity is matched, just set id */ - } - return true; + const ecs_entity_t *lookup_path = ecs_get_lookup_path(stage); + const ecs_entity_t *lookup_path_cur = lookup_path; + while (lookup_path_cur && *lookup_path_cur) { + lookup_path_cur ++; } - if (oper == EcsAndFrom || oper == EcsOrFrom) { - return flecs_n_term_match_table(world, term, table, term->id, - term->oper, id_out, column_out, subject_out, match_index_out, first, - iter_flags); + if (!sep) { + sep = "."; } - /* If source is not This, search in table of source */ - if (!ecs_term_match_this(term)) { - if (iter_flags & EcsIterEntityOptional) { - /* Treat entity terms as optional */ - oper = EcsOptional; - } - - if (ecs_is_alive(world, src_id)) { - match_table = ecs_get_table(world, src_id); - } else { - match_table = NULL; - } + parent = flecs_get_parent_from_path( + stage, parent, &path, sep, prefix, true); - if (match_table) { - } else if (oper != EcsOptional) { - return false; - } - } else { - /* If filter contains This terms, a table must be provided */ - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + if (parent && !(parent = ecs_get_alive(world, parent))) { + return 0; } - if (!match_table) { - return false; + if (!path[0]) { + return parent; } - ecs_entity_t source = 0; - - /* If first = false, we're searching from an offset. This supports returning - * multiple results when using wildcard filters. */ - int32_t column = 0; - if (!first && column_out && column_out[0] != 0) { - column = column_out[0]; - if (column < 0) { - /* In case column is not from This, flip sign */ - column = -column; - } - - /* Remove base 1 offset */ - column --; + if (!sep[0]) { + return ecs_lookup_child(world, parent, path); } - /* Find location, source and id of match in table type */ - ecs_table_record_t *tr = 0; - bool is_any = is_any_pair(id); - - column = flecs_search_relation_w_idr(world, match_table, - column, id, src->trav, src->flags, &source, id_out, &tr, term->idr); +retry: + cur = parent; + ptr = path; - if (tr && match_index_out) { - if (!is_any) { - match_index_out[0] = tr->count; - } else { - match_index_out[0] = 1; + while ((ptr = flecs_path_elem(ptr, sep, &elem, &size))) { + cur = ecs_lookup_child(world, cur, elem); + if (!cur) { + goto tail; } } - bool result = column != -1; - - if (oper == EcsNot) { - if (match_index_out) { - match_index_out[0] = 1; +tail: + if (!cur && recursive) { + if (!lookup_path_search) { + if (parent) { + parent = ecs_get_target(world, parent, EcsChildOf, 0); + goto retry; + } else { + lookup_path_search = true; + } } - result = !result; - } - if (oper == EcsOptional) { - result = true; - } - - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - if ((column == -1) && (src->flags & EcsUp) && (table->flags & EcsTableHasTarget)) { - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_id_t rel = ECS_PAIR_SECOND(table->type.array[table->_->ft_offset]); - if (rel == (uint32_t)src->trav) { - result = true; + if (lookup_path_search) { + if (lookup_path_cur != lookup_path) { + lookup_path_cur --; + parent = lookup_path_cur[0]; + goto retry; + } } } - if (!result) { - if (iter_flags & EcsFilterPopulate) { - column = 0; - } else { - return false; - } + if (elem != buff) { + ecs_os_free(elem); } - if (!ecs_term_match_this(term)) { - if (!source) { - source = src_id; - } - } - - if (id_out && column < 0) { - id_out[0] = id; - } - - if (column_out) { - if (column >= 0) { - column ++; - if (source != 0) { - column *= -1; - } - column_out[0] = column; - } else { - column_out[0] = 0; - } - } - - if (subject_out) { - subject_out[0] = source; - } - - return result; + return cur; +error: + return 0; } -bool flecs_filter_match_table( +ecs_entity_t ecs_set_scope( ecs_world_t *world, - const ecs_filter_t *filter, - const ecs_table_t *table, - ecs_id_t *ids, - int32_t *columns, - ecs_entity_t *sources, - int32_t *match_indices, - int32_t *matches_left, - bool first, - int32_t skip_term, - ecs_flags32_t iter_flags) + ecs_entity_t scope) { - ecs_term_t *terms = filter->terms; - int32_t i, count = filter->term_count; - int32_t match_count = 1; - bool result = true; - - if (matches_left) { - match_count = *matches_left; - } - - for (i = 0; i < count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_oper_kind_t oper = term->oper; - if (i == skip_term) { - if (oper != EcsAndFrom && oper != EcsOrFrom && oper != EcsNotFrom) { - continue; - } - } - - ecs_term_id_t *src = &term->src; - const ecs_table_t *match_table = table; - int32_t t_i = term->field_index; - - ecs_entity_t src_id = src->id; - if (!src_id) { - if (ids) { - ids[t_i] = term->id; - } - continue; - } - - if (!ecs_term_match_this(term)) { - if (ecs_is_alive(world, src_id)) { - match_table = ecs_get_table(world, src_id); - } else { - match_table = NULL; - } - } else { - if (ECS_BIT_IS_SET(iter_flags, EcsIterIgnoreThis)) { - continue; - } - - /* If filter contains This terms, table must be provided */ - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - } - - int32_t match_index = 0; - if (!i || term[-1].oper != EcsOr) { - result = false; - } else { - if (result) { - continue; /* Already found matching OR term */ - } - } + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stage_t *stage = flecs_stage_from_world(&world); - bool term_result = flecs_term_match_table(world, term, match_table, - ids ? &ids[t_i] : NULL, - columns ? &columns[t_i] : NULL, - sources ? &sources[t_i] : NULL, - &match_index, - first, - iter_flags); + ecs_entity_t cur = stage->scope; + stage->scope = scope; - if (i && term[-1].oper == EcsOr) { - result |= term_result; - } else { - result = term_result; - } + return cur; +error: + return 0; +} - if (oper != EcsOr && !result) { - return false; - } +ecs_entity_t ecs_get_scope( + const ecs_world_t *world) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); + return stage->scope; +error: + return 0; +} - if (first && match_index) { - match_count *= match_index; - } - if (match_indices) { - match_indices[t_i] = match_index; - } - } +ecs_entity_t* ecs_set_lookup_path( + ecs_world_t *world, + const ecs_entity_t *lookup_path) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stage_t *stage = flecs_stage_from_world(&world); - if (matches_left) { - *matches_left = match_count; - } + /* Safe: application owns lookup path */ + ecs_entity_t *cur = ECS_CONST_CAST(ecs_entity_t*, stage->lookup_path); + stage->lookup_path = lookup_path; - return true; + return cur; +error: + return NULL; } -static -void term_iter_init_no_data( - ecs_term_iter_t *iter) +ecs_entity_t* ecs_get_lookup_path( + const ecs_world_t *world) { - iter->term = (ecs_term_t){ .field_index = -1 }; - iter->self_index = NULL; - iter->index = 0; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); + /* Safe: application owns lookup path */ + return ECS_CONST_CAST(ecs_entity_t*, stage->lookup_path); +error: + return NULL; } -static -void term_iter_init_w_idr( - const ecs_term_t *term, - ecs_term_iter_t *iter, - ecs_id_record_t *idr, - bool empty_tables) +const char* ecs_set_name_prefix( + ecs_world_t *world, + const char *prefix) { - if (idr) { - if (empty_tables) { - flecs_table_cache_all_iter(&idr->cache, &iter->it); - } else { - flecs_table_cache_iter(&idr->cache, &iter->it); - } - } else { - term_iter_init_no_data(iter); - } - - iter->index = 0; - iter->empty_tables = empty_tables; - iter->size = 0; - if (term && term->idr && term->idr->type_info) { - iter->size = term->idr->type_info->size; - } + flecs_poly_assert(world, ecs_world_t); + const char *old_prefix = world->info.name_prefix; + world->info.name_prefix = prefix; + return old_prefix; } static -void term_iter_init_wildcard( - const ecs_world_t *world, - ecs_term_iter_t *iter, - bool empty_tables) +void flecs_add_path( + ecs_world_t *world, + bool defer_suspend, + ecs_entity_t parent, + ecs_entity_t entity, + const char *name) { - iter->term = (ecs_term_t){ .field_index = -1 }; - iter->self_index = flecs_id_record_get(world, EcsAny); - ecs_id_record_t *idr = iter->cur = iter->self_index; - term_iter_init_w_idr(NULL, iter, idr, empty_tables); -} - -static -void term_iter_init( - const ecs_world_t *world, - ecs_term_t *term, - ecs_term_iter_t *iter, - bool empty_tables) -{ - const ecs_term_id_t *src = &term->src; - - iter->term = *term; - - if (src->flags & EcsSelf) { - iter->self_index = term->idr; - if (!iter->self_index) { - iter->self_index = flecs_query_id_record_get(world, term->id); - } - } - - if (src->flags & EcsUp) { - iter->set_index = flecs_id_record_get(world, - ecs_pair(src->trav, EcsWildcard)); + ecs_suspend_readonly_state_t srs; + ecs_world_t *real_world = NULL; + if (defer_suspend) { + real_world = flecs_suspend_readonly(world, &srs); + ecs_assert(real_world != NULL, ECS_INTERNAL_ERROR, NULL); } - ecs_id_record_t *idr; - if (iter->self_index) { - idr = iter->cur = iter->self_index; - } else { - idr = iter->cur = iter->set_index; + if (parent) { + ecs_add_pair(world, entity, EcsChildOf, parent); } - term_iter_init_w_idr(term, iter, idr, empty_tables); -} - -ecs_iter_t ecs_term_iter( - const ecs_world_t *stage, - ecs_term_t *term) -{ - ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(term != NULL, ECS_INVALID_PARAMETER, NULL); - - const ecs_world_t *world = ecs_get_world(stage); + ecs_assert(name[0] != '#', ECS_INVALID_PARAMETER, + "path should not contain identifier with #"); - flecs_process_pending_tables(world); + ecs_set_name(world, entity, name); - if (ecs_term_finalize(world, term)) { - ecs_throw(ECS_INVALID_PARAMETER, NULL); + if (defer_suspend) { + ecs_stage_t *stage = flecs_stage_from_world(&world); + flecs_resume_readonly(real_world, &srs); + flecs_defer_path(stage, parent, entity, name); } - - ecs_iter_t it = { - .real_world = ECS_CONST_CAST(ecs_world_t*, world), - .world = ECS_CONST_CAST(ecs_world_t*, stage), - .field_count = 1, - .next = ecs_term_next - }; - - /* Term iter populates the iterator with arrays from its own cache, ensure - * they don't get overwritten by flecs_iter_validate. - * - * Note: the reason the term iterator doesn't use the iterator cache itself - * (which could easily accommodate a single term) is that the filter iterator - * is built on top of the term iterator. The private cache of the term - * iterator keeps the filter iterator code simple, as it doesn't need to - * worry about the term iter overwriting the iterator fields. */ - flecs_iter_init(stage, &it, 0); - term_iter_init(world, term, &it.priv.iter.term, false); - ECS_BIT_COND(it.flags, EcsIterNoData, it.priv.iter.term.size == 0); - - return it; -error: - return (ecs_iter_t){ 0 }; } -ecs_iter_t ecs_term_chain_iter( - const ecs_iter_t *chain_it, - ecs_term_t *term) +ecs_entity_t ecs_add_path_w_sep( + ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t parent, + const char *path, + const char *sep, + const char *prefix) { - ecs_check(chain_it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(term != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_world_t *world = chain_it->real_world; ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - if (ecs_term_finalize(world, term)) { - ecs_throw(ECS_INVALID_PARAMETER, NULL); - } - - ecs_iter_t it = { - .real_world = world, - .world = chain_it->world, - .terms = term, - .field_count = 1, - .chain_it = ECS_CONST_CAST(ecs_iter_t*, chain_it), - .next = ecs_term_next - }; - - flecs_iter_init(chain_it->world, &it, flecs_iter_cache_all); - - term_iter_init(world, term, &it.priv.iter.term, false); - - return it; -error: - return (ecs_iter_t){ 0 }; -} - -ecs_iter_t ecs_children( - const ecs_world_t *world, - ecs_entity_t parent) -{ - return ecs_term_iter(world, &(ecs_term_t){ .id = ecs_childof(parent) }); -} - -bool ecs_children_next( - ecs_iter_t *it) -{ - return ecs_term_next(it); -} - -static -const ecs_table_record_t *flecs_term_iter_next_table( - ecs_term_iter_t *iter) -{ - ecs_id_record_t *idr = iter->cur; - if (!idr) { - return NULL; + if (!sep) { + sep = "."; } - return flecs_table_cache_next(&iter->it, ecs_table_record_t); -} - -static -bool flecs_term_iter_find_superset( - ecs_world_t *world, - ecs_table_t *table, - ecs_term_t *term, - ecs_entity_t *source, - ecs_id_t *id, - int32_t *column) -{ - ecs_term_id_t *src = &term->src; + if (!path) { + if (!entity) { + entity = ecs_new(world); + } - /* Test if following the relationship finds the id */ - int32_t index = flecs_search_relation_w_idr(world, table, 0, - term->id, src->trav, src->flags, source, id, 0, term->idr); + if (parent) { + ecs_add_pair(world, entity, EcsChildOf, entity); + } - if (index == -1) { - *source = 0; - return false; + return entity; } - ecs_assert(*source != 0, ECS_INTERNAL_ERROR, NULL); + bool root_path = flecs_is_root_path(path, prefix); + parent = flecs_get_parent_from_path( + world, parent, &path, sep, prefix, !entity); - *column = (index + 1) * -1; + char buff[ECS_NAME_BUFFER_LENGTH]; + const char *ptr = path; + char *elem = buff; + int32_t size = ECS_NAME_BUFFER_LENGTH; + + /* If we're in deferred/readonly mode suspend it, so that the name index is + * immediately updated. Without this, we could create multiple entities for + * the same name in a single command queue. */ + bool suspend_defer = ecs_is_deferred(world) && + !(world->flags & EcsWorldMultiThreaded); + + ecs_entity_t cur = parent; + char *name = NULL; - return true; -} + if (sep[0]) { + while ((ptr = flecs_path_elem(ptr, sep, &elem, &size))) { + ecs_entity_t e = ecs_lookup_child(world, cur, elem); + if (!e) { + if (name) { + ecs_os_free(name); + } -static -bool flecs_term_iter_next( - ecs_world_t *world, - ecs_term_iter_t *iter, - bool match_prefab, - bool match_disabled) -{ - ecs_table_t *table = iter->table; - ecs_entity_t source = 0; - const ecs_table_record_t *tr; - ecs_term_t *term = &iter->term; + name = ecs_os_strdup(elem); - do { - if (table) { - iter->cur_match ++; - if (iter->cur_match >= iter->match_count) { - table = NULL; - } else { - iter->last_column = ecs_search_offset( - world, table, iter->last_column + 1, term->id, 0); - iter->column = iter->last_column + 1; - if (iter->last_column >= 0) { - iter->id = table->type.array[iter->last_column]; + /* If this is the last entity in the path, use the provided id */ + bool last_elem = false; + if (!flecs_path_elem(ptr, sep, NULL, NULL)) { + e = entity; + last_elem = true; } - } - } - if (!table) { - if (!(tr = flecs_term_iter_next_table(iter))) { - if (iter->cur != iter->set_index && iter->set_index != NULL) { - if (iter->observed_table_count != 0) { - iter->cur = iter->set_index; - if (iter->empty_tables) { - flecs_table_cache_all_iter( - &iter->set_index->cache, &iter->it); - } else { - flecs_table_cache_iter( - &iter->set_index->cache, &iter->it); - } - iter->index = 0; - tr = flecs_term_iter_next_table(iter); + if (!e) { + if (last_elem) { + ecs_entity_t prev = ecs_set_scope(world, 0); + e = ecs_entity(world, {0}); + ecs_set_scope(world, prev); + } else { + e = ecs_new(world); } } - if (!tr) { - return false; + if (!cur && last_elem && root_path) { + ecs_remove_pair(world, e, EcsChildOf, EcsWildcard); } - } - - table = tr->hdr.table; - if (table->_->traversable_count) { - iter->observed_table_count ++; - } - - if (!match_prefab && (table->flags & EcsTableIsPrefab)) { - continue; - } - - if (!match_disabled && (table->flags & EcsTableIsDisabled)) { - continue; - } - iter->table = table; - iter->match_count = tr->count; - if (is_any_pair(term->id)) { - iter->match_count = 1; + flecs_add_path(world, suspend_defer, cur, e, name); } - iter->cur_match = 0; - iter->last_column = tr->index; - iter->column = tr->index + 1; - iter->id = flecs_to_public_id(table->type.array[tr->index]); + cur = e; } - if (iter->cur == iter->set_index) { - if (iter->self_index) { - if (flecs_id_record_get_table(iter->self_index, table) != NULL) { - /* If the table has the id itself and this term matched Self - * we already matched it */ - continue; - } - } - - if (!flecs_term_iter_find_superset( - world, table, term, &source, &iter->id, &iter->column)) - { - continue; - } - - /* The tr->count field refers to the number of relationship instances, - * not to the number of matches. Superset terms can only yield a - * single match. */ - iter->match_count = 1; + if (entity && (cur != entity)) { + ecs_throw(ECS_ALREADY_DEFINED, name); } - break; - } while (true); + if (name) { + ecs_os_free(name); + } - iter->subject = source; + if (elem != buff) { + ecs_os_free(elem); + } + } else { + flecs_add_path(world, suspend_defer, parent, entity, path); + } - return true; + return cur; +error: + return 0; } -static -bool flecs_term_iter_set_table( +ecs_entity_t ecs_new_from_path_w_sep( ecs_world_t *world, - ecs_term_iter_t *iter, - ecs_table_t *table) + ecs_entity_t parent, + const char *path, + const char *sep, + const char *prefix) { - const ecs_table_record_t *tr = NULL; - const ecs_id_record_t *idr = iter->self_index; - if (idr) { - tr = ecs_table_cache_get(&idr->cache, table); - if (tr) { - iter->match_count = tr->count; - iter->last_column = tr->index; - iter->column = tr->index + 1; - iter->id = flecs_to_public_id(table->type.array[tr->index]); - } - } - - if (!tr) { - idr = iter->set_index; - if (idr) { - tr = ecs_table_cache_get(&idr->cache, table); - if (!flecs_term_iter_find_superset(world, table, &iter->term, - &iter->subject, &iter->id, &iter->column)) - { - return false; - } - iter->match_count = 1; - } - } + return ecs_add_path_w_sep(world, 0, parent, path, sep, prefix); +} - if (!tr) { - return false; - } +/** + * @file id.c + * @brief Id utilities. + */ - /* Populate fields as usual */ - iter->table = table; - iter->cur_match = 0; - return true; -} +#ifdef FLECS_SCRIPT +#endif -bool ecs_term_next( - ecs_iter_t *it) +bool ecs_id_match( + ecs_id_t id, + ecs_id_t pattern) { - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_term_next, ECS_INVALID_PARAMETER, NULL); - - flecs_iter_validate(it); + if (id == pattern) { + return true; + } - ecs_term_iter_t *iter = &it->priv.iter.term; - ecs_term_t *term = &iter->term; - ecs_world_t *world = it->real_world; - ecs_table_t *table; + if (ECS_HAS_ID_FLAG(pattern, PAIR)) { + if (!ECS_HAS_ID_FLAG(id, PAIR)) { + return false; + } - it->ids = &iter->id; - it->sources = &iter->subject; - it->columns = &iter->column; - it->terms = &iter->term; - it->sizes = &iter->size; - it->ptrs = &iter->ptr; + ecs_entity_t id_first = ECS_PAIR_FIRST(id); + ecs_entity_t id_second = ECS_PAIR_SECOND(id); + ecs_entity_t pattern_first = ECS_PAIR_FIRST(pattern); + ecs_entity_t pattern_second = ECS_PAIR_SECOND(pattern); - ecs_iter_t *chain_it = it->chain_it; - if (chain_it) { - ecs_iter_next_action_t next = chain_it->next; - bool match; + ecs_check(id_first != 0, ECS_INVALID_PARAMETER, + "first element of pair cannot be 0"); + ecs_check(id_second != 0, ECS_INVALID_PARAMETER, + "second element of pair cannot be 0"); - do { - if (!next(chain_it)) { - goto done; + ecs_check(pattern_first != 0, ECS_INVALID_PARAMETER, + "first element of pair cannot be 0"); + ecs_check(pattern_second != 0, ECS_INVALID_PARAMETER, + "second element of pair cannot be 0"); + + if (pattern_first == EcsWildcard) { + if (pattern_second == EcsWildcard || pattern_second == id_second) { + return true; } - - table = chain_it->table; - match = flecs_term_match_table(world, term, table, - it->ids, it->columns, it->sources, it->match_indices, true, - it->flags); - } while (!match); - goto yield; - + } else if (pattern_first == EcsFlag) { + /* Used for internals, helps to keep track of which ids are used in + * pairs that have additional flags (like OVERRIDE and TOGGLE) */ + if (ECS_HAS_ID_FLAG(id, PAIR) && !ECS_IS_PAIR(id)) { + if (ECS_PAIR_FIRST(id) == pattern_second) { + return true; + } + if (ECS_PAIR_SECOND(id) == pattern_second) { + return true; + } + } + } else if (pattern_second == EcsWildcard) { + if (pattern_first == id_first) { + return true; + } + } } else { - if (!flecs_term_iter_next(world, iter, - (term->flags & EcsTermMatchPrefab) != 0, - (term->flags & EcsTermMatchDisabled) != 0)) - { - goto done; + if ((id & ECS_ID_FLAGS_MASK) != (pattern & ECS_ID_FLAGS_MASK)) { + return false; } - table = iter->table; - - /* Source must either be 0 (EcsThis) or nonzero in case of substitution */ - ecs_assert(iter->subject || iter->cur != iter->set_index, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(iter->table != NULL, ECS_INTERNAL_ERROR, NULL); + if ((ECS_COMPONENT_MASK & pattern) == EcsWildcard) { + return true; + } } -yield: - flecs_iter_populate_data(world, it, table, 0, ecs_table_count(table), - it->ptrs); - ECS_BIT_SET(it->flags, EcsIterIsValid); - return true; -done: - ecs_iter_fini(it); error: return false; } -static -void flecs_init_filter_iter( - ecs_iter_t *it, - const ecs_filter_t *filter) +bool ecs_id_is_pair( + ecs_id_t id) { - ecs_assert(filter != NULL, ECS_INTERNAL_ERROR, NULL); - it->priv.iter.filter.filter = filter; - it->field_count = filter->field_count; + return ECS_HAS_ID_FLAG(id, PAIR); } -int32_t ecs_filter_pivot_term( - const ecs_world_t *world, - const ecs_filter_t *filter) +bool ecs_id_is_wildcard( + ecs_id_t id) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(filter != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_term_t *terms = filter->terms; - int32_t i, term_count = filter->term_count; - int32_t pivot_term = -1, min_count = -1, self_pivot_term = -1; - - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_id_t id = term->id; - - if ((term->oper != EcsAnd) || (i && (term[-1].oper == EcsOr))) { - continue; - } - - if (!ecs_term_match_this(term)) { - continue; - } - - ecs_id_record_t *idr = flecs_query_id_record_get(world, id); - if (!idr) { - /* If one of the terms does not match with any data, iterator - * should not return anything */ - return -2; /* -2 indicates filter doesn't match anything */ - } - - int32_t table_count = flecs_table_cache_count(&idr->cache); - if (min_count == -1 || table_count < min_count) { - min_count = table_count; - pivot_term = i; - if ((term->src.flags & EcsTraverseFlags) == EcsSelf) { - self_pivot_term = i; - } - } + if ((id == EcsWildcard) || (id == EcsAny)) { + return true; } - if (self_pivot_term != -1) { - pivot_term = self_pivot_term; + bool is_pair = ECS_IS_PAIR(id); + if (!is_pair) { + return false; } - return pivot_term; -error: - return -2; -} + ecs_entity_t first = ECS_PAIR_FIRST(id); + ecs_entity_t second = ECS_PAIR_SECOND(id); -void flecs_filter_apply_iter_flags( - ecs_iter_t *it, - const ecs_filter_t *filter) -{ - ECS_BIT_COND(it->flags, EcsIterIsInstanced, - ECS_BIT_IS_SET(filter->flags, EcsFilterIsInstanced)); - ECS_BIT_COND(it->flags, EcsIterNoData, - ECS_BIT_IS_SET(filter->flags, EcsFilterNoData)); - ECS_BIT_COND(it->flags, EcsIterHasCondSet, - ECS_BIT_IS_SET(filter->flags, EcsFilterHasCondSet)); + return (first == EcsWildcard) || (second == EcsWildcard) || + (first == EcsAny) || (second == EcsAny); } -ecs_iter_t flecs_filter_iter_w_flags( - const ecs_world_t *stage, - const ecs_filter_t *filter, - ecs_flags32_t flags) +bool ecs_id_is_valid( + const ecs_world_t *world, + ecs_id_t id) { - ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(filter != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(filter->flags & (EcsFilterHasPred|EcsFilterHasScopes)), - ECS_UNSUPPORTED, NULL); - const ecs_world_t *world = ecs_get_world(stage); - - if (!(flags & EcsIterMatchVar)) { - flecs_process_pending_tables(world); + if (!id) { + return false; + } + if (ecs_id_is_wildcard(id)) { + return false; } - ecs_iter_t it = { - .real_world = ECS_CONST_CAST(ecs_world_t*, world), - .world = ECS_CONST_CAST(ecs_world_t*, stage), - .terms = filter ? filter->terms : NULL, - .next = ecs_filter_next, - .flags = flags, - .sizes = filter->sizes - }; - - ecs_filter_iter_t *iter = &it.priv.iter.filter; - iter->pivot_term = -1; - - flecs_init_filter_iter(&it, filter); - flecs_filter_apply_iter_flags(&it, filter); - - /* Find term that represents smallest superset */ - if (ECS_BIT_IS_SET(flags, EcsIterIgnoreThis)) { - term_iter_init_no_data(&iter->term_iter); - } else if (ECS_BIT_IS_SET(filter->flags, EcsFilterMatchThis)) { - ecs_term_t *terms = filter->terms; - int32_t pivot_term = -1; - ecs_check(terms != NULL, ECS_INVALID_PARAMETER, NULL); - - pivot_term = ecs_filter_pivot_term(world, filter); - iter->kind = EcsIterEvalTables; - iter->pivot_term = pivot_term; - - if (pivot_term == -2) { - /* One or more terms have no matching results */ - term_iter_init_no_data(&iter->term_iter); - } else if (pivot_term == -1) { - /* No terms meet the criteria to be a pivot term, evaluate filter - * against all tables */ - term_iter_init_wildcard(world, &iter->term_iter, - ECS_BIT_IS_SET(filter->flags, EcsFilterMatchEmptyTables)); - } else { - ecs_assert(pivot_term >= 0, ECS_INTERNAL_ERROR, NULL); - term_iter_init(world, &terms[pivot_term], &iter->term_iter, - ECS_BIT_IS_SET(filter->flags, EcsFilterMatchEmptyTables)); + if (ECS_HAS_ID_FLAG(id, PAIR)) { + if (!ECS_PAIR_FIRST(id)) { + return false; } - } else { - if (!ECS_BIT_IS_SET(filter->flags, EcsFilterMatchAnything)) { - term_iter_init_no_data(&iter->term_iter); - } else { - iter->kind = EcsIterEvalNone; + if (!ECS_PAIR_SECOND(id)) { + return false; + } + } else if (id & ECS_ID_FLAGS_MASK) { + if (!ecs_is_valid(world, id & ECS_COMPONENT_MASK)) { + return false; } } - ECS_BIT_COND(it.flags, EcsIterNoData, - ECS_BIT_IS_SET(filter->flags, EcsFilterNoData)); - - if (ECS_BIT_IS_SET(filter->flags, EcsFilterMatchThis)) { - /* Make space for one variable if the filter has terms for This var */ - it.variable_count = 1; - - /* Set variable name array */ - it.variable_names = ECS_CONST_CAST(char**, filter->variable_names); - } - - flecs_iter_init(stage, &it, flecs_iter_cache_all); - - return it; -error: - return (ecs_iter_t){ 0 }; + return true; } -ecs_iter_t ecs_filter_iter( - const ecs_world_t *stage, - const ecs_filter_t *filter) +ecs_flags32_t ecs_id_get_flags( + const ecs_world_t *world, + ecs_id_t id) { - return flecs_filter_iter_w_flags(stage, filter, 0); + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + return idr->flags; + } else { + return 0; + } } -ecs_iter_t ecs_filter_chain_iter( - const ecs_iter_t *chain_it, - const ecs_filter_t *filter) +ecs_id_t ecs_id_from_str( + const ecs_world_t *world, + const char *expr) { - ecs_iter_t it = { - .terms = filter->terms, - .field_count = filter->field_count, - .world = chain_it->world, - .real_world = chain_it->real_world, - .chain_it = ECS_CONST_CAST(ecs_iter_t*, chain_it), - .next = ecs_filter_next, - .sizes = filter->sizes - }; - - flecs_iter_init(chain_it->world, &it, flecs_iter_cache_all); - ecs_filter_iter_t *iter = &it.priv.iter.filter; - flecs_init_filter_iter(&it, filter); - - iter->kind = EcsIterEvalChain; +#ifdef FLECS_SCRIPT + ecs_id_t result; - return it; + /* Temporarily disable parser logging */ + int prev_level = ecs_log_set_level(-3); + if (!flecs_id_parse(world, NULL, expr, &result)) { + /* Invalid expression */ + ecs_log_set_level(prev_level); + return 0; + } + ecs_log_set_level(prev_level); + return result; +#else + (void)world; + (void)expr; + ecs_abort(ECS_UNSUPPORTED, "ecs_id_from_str requires FLECS_SCRIPT addon"); +#endif } -bool ecs_filter_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_filter_next, ECS_INVALID_PARAMETER, NULL); +/** + * @file iter.c + * @brief Iterator API. + * + * The iterator API contains functions that apply to all iterators, such as + * resource management, or fetching resources for a matched table. The API also + * contains functions for generic iterators, which make it possible to iterate + * an iterator without needing to know what created the iterator. + */ - if (flecs_iter_next_row(it)) { - return true; - } - - return flecs_iter_next_instanced(it, ecs_filter_next_instanced(it)); -error: - return false; -} - -bool ecs_filter_next_instanced( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_filter_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->chain_it != it, ECS_INVALID_PARAMETER, NULL); - - ecs_filter_iter_t *iter = &it->priv.iter.filter; - const ecs_filter_t *filter = iter->filter; - ecs_world_t *world = it->real_world; - ecs_table_t *table = NULL; - bool match; - - flecs_iter_validate(it); - - ecs_iter_t *chain_it = it->chain_it; - ecs_iter_kind_t kind = iter->kind; - - if (chain_it) { - ecs_assert(kind == EcsIterEvalChain, ECS_INVALID_PARAMETER, NULL); - - ecs_iter_next_action_t next = chain_it->next; - do { - if (!next(chain_it)) { - goto done; - } - - table = chain_it->table; - match = flecs_filter_match_table(world, filter, table, - it->ids, it->columns, it->sources, it->match_indices, NULL, - true, -1, it->flags); - } while (!match); - - goto yield; - } else if (kind == EcsIterEvalTables || kind == EcsIterEvalCondition) { - ecs_term_iter_t *term_iter = &iter->term_iter; - ecs_term_t *term = &term_iter->term; - int32_t pivot_term = iter->pivot_term; - bool first; - - /* Check if the This variable has been set on the iterator. If set, - * the filter should only be applied to the variable value */ - ecs_var_t *this_var = NULL; - ecs_table_t *this_table = NULL; - if (it->variable_count) { - if (ecs_iter_var_is_constrained(it, 0)) { - this_var = it->variables; - this_table = this_var->range.table; - - /* If variable is constrained, make sure it's a value that's - * pointing to a table, as a filter can't iterate single - * entities (yet) */ - ecs_assert(this_table != NULL, ECS_INVALID_OPERATION, NULL); - - /* Can't set variable for filter that does not iterate tables */ - ecs_assert(kind == EcsIterEvalTables, - ECS_INVALID_OPERATION, NULL); - } - } - - do { - /* If there are no matches left for the previous table, this is the - * first match of the next table. */ - first = iter->matches_left == 0; - - if (first) { - if (kind != EcsIterEvalCondition) { - /* Check if this variable was constrained */ - if (this_table != NULL) { - /* If this is the first match of a new result and the - * previous result was equal to the value of a - * constrained var, there's nothing left to iterate */ - if (it->table == this_table) { - goto done; - } - - /* If table doesn't match term iterator, it doesn't - * match filter. */ - if (!flecs_term_iter_set_table( - world, term_iter, this_table)) - { - goto done; - } - - it->offset = this_var->range.offset; - it->count = this_var->range.count; - - /* But if it does, forward it to filter matching */ - ecs_assert(term_iter->table == this_table, - ECS_INTERNAL_ERROR, NULL); - - /* If This variable is not constrained, iterate as usual */ - } else { - it->offset = 0; - it->count = 0; - - /* Find new match, starting with the leading term */ - if (!flecs_term_iter_next(world, term_iter, - ECS_BIT_IS_SET(filter->flags, - EcsFilterMatchPrefab), - ECS_BIT_IS_SET(filter->flags, - EcsFilterMatchDisabled))) - { - goto done; - } - } - - ecs_assert(term_iter->match_count != 0, - ECS_INTERNAL_ERROR, NULL); - - if (pivot_term == -1) { - /* Without a pivot term, we're iterating all tables with - * a wildcard, so the match count is meaningless. */ - term_iter->match_count = 1; - } else { - it->match_indices[pivot_term] = term_iter->match_count; - } - - iter->matches_left = term_iter->match_count; - - /* Filter iterator takes control over iterating all the - * permutations that match the wildcard. */ - term_iter->match_count = 1; - - table = term_iter->table; - - if (pivot_term != -1) { - int32_t index = term->field_index; - it->ids[index] = term_iter->id; - it->sources[index] = term_iter->subject; - it->columns[index] = term_iter->column; - } - } else { - /* Progress iterator to next match for table, if any */ - table = it->table; - if (term_iter->index == 0) { - iter->matches_left = 1; - term_iter->index = 1; /* prevents looping again */ - } else { - goto done; - } - } - - /* Match the remainder of the terms */ - match = flecs_filter_match_table(world, filter, table, - it->ids, it->columns, it->sources, - it->match_indices, &iter->matches_left, first, - pivot_term, it->flags); - if (!match) { - it->table = table; - iter->matches_left = 0; - continue; - } - - /* Table got matched, set This variable */ - if (table) { - ecs_assert(it->variable_count == 1, ECS_INTERNAL_ERROR, NULL); - ecs_assert(it->variables != NULL, ECS_INTERNAL_ERROR, NULL); - it->variables[0].range.table = table; - } - - ecs_assert(iter->matches_left != 0, ECS_INTERNAL_ERROR, NULL); - } - - /* If this is not the first result for the table, and the table - * is matched more than once, iterate remaining matches */ - if (!first && (iter->matches_left > 0)) { - table = it->table; - - /* Find first term that still has matches left */ - int32_t i, j, count = it->field_count; - for (i = count - 1; i >= 0; i --) { - int32_t mi = -- it->match_indices[i]; - if (mi) { - if (mi < 0) { - continue; - } - break; - } - } - - /* If matches_left > 0 we should've found at least one match */ - ecs_assert(i >= 0, ECS_INTERNAL_ERROR, NULL); - - /* Progress first term to next match (must be at least one) */ - int32_t column = it->columns[i]; - if (column < 0) { - /* If this term was matched on a non-This entity, reconvert - * the column back to a positive value */ - column = -column; - } - - it->columns[i] = column + 1; - flecs_term_match_table(world, &filter->terms[i], table, - &it->ids[i], &it->columns[i], &it->sources[i], - &it->match_indices[i], false, it->flags); - - /* Reset remaining terms (if any) to first match */ - for (j = i + 1; j < count; j ++) { - flecs_term_match_table(world, &filter->terms[j], table, - &it->ids[j], &it->columns[j], &it->sources[j], - &it->match_indices[j], true, it->flags); - } - } - - match = iter->matches_left != 0; - iter->matches_left --; - - ecs_assert(iter->matches_left >= 0, ECS_INTERNAL_ERROR, NULL); - } while (!match); - - goto yield; - } - -done: -error: - ecs_iter_fini(it); - return false; - -yield: - if (!it->count && table) { - it->count = ecs_table_count(table); - } - flecs_iter_populate_data(world, it, table, it->offset, it->count, it->ptrs); - ECS_BIT_SET(it->flags, EcsIterIsValid); - return true; -} - -/** - * @file iter.c - * @brief Iterator API. - * - * The iterator API contains functions that apply to all iterators, such as - * resource management, or fetching resources for a matched table. The API also - * contains functions for generic iterators, which make it possible to iterate - * an iterator without needing to know what created the iterator. - */ - -#include +#include /* Utility macros to enforce consistency when initializing iterator fields */ @@ -12911,12 +11249,12 @@ bool ecs_filter_next_instanced( #define INIT_CACHE(it, stack, fields, f, T, count)\ if (!it->f && (fields & flecs_iter_cache_##f) && count) {\ it->f = flecs_stack_calloc_n(stack, T, count);\ - it->priv.cache.used |= flecs_iter_cache_##f;\ + it->priv_.cache.used |= flecs_iter_cache_##f;\ } /* If array is allocated, free it when finalizing the iterator */ #define FINI_CACHE(it, f, T, count)\ - if (it->priv.cache.used & flecs_iter_cache_##f) {\ + if (it->priv_.cache.used & flecs_iter_cache_##f) {\ flecs_stack_free_n((void*)it->f, T, count);\ } @@ -12950,34 +11288,14 @@ void flecs_iter_init( ECS_CONST_CAST(ecs_world_t**, &world)); ecs_stack_t *stack = &stage->allocators.iter_stack; - it->priv.cache.used = 0; - it->priv.cache.allocated = 0; - it->priv.cache.stack_cursor = flecs_stack_get_cursor(stack); - it->priv.entity_iter = flecs_stack_calloc_t( - stack, ecs_entity_filter_iter_t); + it->priv_.cache.used = 0; + it->priv_.cache.allocated = 0; + it->priv_.cache.stack_cursor = flecs_stack_get_cursor(stack); INIT_CACHE(it, stack, fields, ids, ecs_id_t, it->field_count); INIT_CACHE(it, stack, fields, sources, ecs_entity_t, it->field_count); - INIT_CACHE(it, stack, fields, match_indices, int32_t, it->field_count); - INIT_CACHE(it, stack, fields, columns, int32_t, it->field_count); + INIT_CACHE(it, stack, fields, trs, ecs_table_record_t*, it->field_count); INIT_CACHE(it, stack, fields, variables, ecs_var_t, it->variable_count); - INIT_CACHE(it, stack, fields, ptrs, void*, it->field_count); -} - -void flecs_iter_validate( - ecs_iter_t *it) -{ - ECS_BIT_SET(it->flags, EcsIterIsValid); - - /* Make sure multithreaded iterator isn't created for real world */ - ecs_world_t *world = it->real_world; - ecs_poly_assert(world, ecs_world_t); - ecs_check(!(world->flags & EcsWorldMultiThreaded) || it->world != it->real_world, - ECS_INVALID_PARAMETER, - "create iterator for stage when world is in multithreaded mode"); - (void)world; -error: - return; } void ecs_iter_fini( @@ -12996,273 +11314,129 @@ void ecs_iter_fini( FINI_CACHE(it, ids, ecs_id_t, it->field_count); FINI_CACHE(it, sources, ecs_entity_t, it->field_count); - FINI_CACHE(it, match_indices, int32_t, it->field_count); - FINI_CACHE(it, columns, int32_t, it->field_count); + FINI_CACHE(it, trs, ecs_table_record_t*, it->field_count); FINI_CACHE(it, variables, ecs_var_t, it->variable_count); - FINI_CACHE(it, ptrs, void*, it->field_count); - flecs_stack_free_t(it->priv.entity_iter, ecs_entity_filter_iter_t); ecs_stage_t *stage = flecs_stage_from_world(&world); flecs_stack_restore_cursor(&stage->allocators.iter_stack, - it->priv.cache.stack_cursor); + it->priv_.cache.stack_cursor); } -static -bool flecs_iter_populate_term_data( - ecs_world_t *world, - ecs_iter_t *it, - int32_t t, - int32_t column, - void **ptr_out) -{ - bool is_shared = false; - ecs_table_t *table; - void *data; - int32_t row, u_index; - - if (!column) { - /* Term has no data. This includes terms that have Not operators. */ - goto no_data; - } +/* --- Public API --- */ - /* Filter terms may match with data but don't return it */ - if (it->terms[t].inout == EcsInOutNone) { - goto no_data; - } +void* ecs_field_w_size( + const ecs_iter_t *it, + size_t size, + int8_t index) +{ + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + ecs_check(!size || ecs_field_size(it, index) == size || + !ecs_field_size(it, index), + ECS_INVALID_PARAMETER, "mismatching size for field %d", index); + (void)size; - ecs_assert(it->sizes != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t size = it->sizes[t]; - if (!size) { - goto no_data; + const ecs_table_record_t *tr = it->trs[index]; + if (!tr) { + ecs_assert(!ecs_field_is_set(it, index), ECS_INTERNAL_ERROR, NULL); + return NULL; } - if (column < 0) { - table = it->table; - is_shared = true; - - /* Data is not from This */ - if (it->references && (!table || !(table->flags & EcsTableHasTarget))) { - /* The reference array is used only for components matched on a - * table (vs. individual entities). Remaining components should be - * assigned outside of this function */ - if (ecs_term_match_this(&it->terms[t])) { - - /* Iterator provides cached references for non-This terms */ - ecs_ref_t *ref = &it->references[-column - 1]; - if (ptr_out) { - if (ref->id) { - ptr_out[0] = (void*)ecs_ref_get_id(world, ref, ref->id); - } else { - ptr_out[0] = NULL; - } - } - - if (!ref->id) { - is_shared = false; - } - - return is_shared; - } - - return true; - } else { - ecs_entity_t subj = it->sources[t]; - ecs_assert(subj != 0, ECS_INTERNAL_ERROR, NULL); - - /* Don't use ecs_get_id directly. Instead, go directly to the - * storage so that we can get both the pointer and size */ - ecs_record_t *r = flecs_entities_get(world, subj); - ecs_assert(r != NULL && r->table != NULL, ECS_INTERNAL_ERROR, NULL); - - row = ECS_RECORD_TO_ROW(r->row); - table = r->table; - - ecs_id_t id = it->ids[t]; - ecs_table_record_t *tr; - - if (!(tr = flecs_table_record_get(world, table, id)) || (tr->column == -1)) { - u_index = flecs_table_column_to_union_index(table, -column - 1); - if (u_index != -1) { - goto has_union; - } - goto no_data; - } + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_assert(!(idr->flags & EcsIdIsSparse), ECS_INVALID_OPERATION, + "use ecs_field_at to access fields for sparse components"); + (void)idr; - /* We now have row and column, so we can get the storage for the id - * which gives us the pointer and size */ - column = tr->column; - ecs_vec_t *s = &table->data.columns[column].data; - data = ecs_vec_first(s); - /* Fallthrough to has_data */ - } - } else { - /* Data is from This, use table from iterator */ + ecs_entity_t src = it->sources[index]; + ecs_table_t *table; + int32_t row; + if (!src) { table = it->table; - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - row = it->offset; - - int32_t storage_column = ecs_table_type_to_column_index( - table, column - 1); - if (storage_column == -1) { - u_index = flecs_table_column_to_union_index(table, column - 1); - if (u_index != -1) { - goto has_union; - } - goto no_data; - } - - if (!it->count) { - goto no_data; - } - - ecs_vec_t *s = &table->data.columns[storage_column].data; - data = ecs_vec_first(s); - - /* Fallthrough to has_data */ - } - -has_data: - if (ptr_out) ptr_out[0] = ECS_ELEM(data, size, row); - return is_shared; - -has_union: { - /* Edge case: if column is a switch we should return the vector with case - * identifiers. Will be replaced in the future with pluggable storage */ - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_switch_t *sw = &table->_->sw_columns[u_index]; - data = ecs_vec_first(flecs_switch_values(sw)); - goto has_data; + } else { + ecs_record_t *r = flecs_entities_get(it->real_world, src); + table = r->table; + row = ECS_RECORD_TO_ROW(r->row); } -no_data: - if (ptr_out) ptr_out[0] = NULL; - return false; -} + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(tr->hdr.table == table, ECS_INTERNAL_ERROR, NULL); -void flecs_iter_populate_data( - ecs_world_t *world, - ecs_iter_t *it, - ecs_table_t *table, - int32_t offset, - int32_t count, - void **ptrs) -{ - ecs_table_t *prev_table = it->table; - if (prev_table) { - it->frame_offset += ecs_table_count(prev_table); - } + int32_t column_index = tr->column; + ecs_assert(column_index != -1, ECS_NOT_A_COMPONENT, + "only components can be fetched with fields"); + ecs_assert(column_index >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column_index < table->column_count, ECS_INTERNAL_ERROR, NULL); - it->table = table; - it->offset = offset; - it->count = count; - if (table) { - ecs_assert(count != 0 || !ecs_table_count(table) || (it->flags & EcsIterTableOnly), + ecs_column_t *column = &table->data.columns[column_index]; + ecs_assert((row < table->data.count) || + (it->query && (it->query->flags & EcsQueryMatchEmptyTables)), ECS_INTERNAL_ERROR, NULL); - if (count) { - it->entities = ecs_vec_get_t( - &table->data.entities, ecs_entity_t, offset); - } else { - it->entities = NULL; - } - } - - int t, field_count = it->field_count; - if (ECS_BIT_IS_SET(it->flags, EcsIterNoData)) { - ECS_BIT_CLEAR(it->flags, EcsIterHasShared); - return; - } - bool has_shared = false; - if (ptrs) { - for (t = 0; t < field_count; t ++) { - int32_t column = it->columns[t]; - has_shared |= flecs_iter_populate_term_data(world, it, t, column, - &ptrs[t]); - } + if (!size) { + size = (size_t)column->ti->size; } - ECS_BIT_COND(it->flags, EcsIterHasShared, has_shared); + return ECS_ELEM(column->data, (ecs_size_t)size, row); +error: + return NULL; } -bool flecs_iter_next_row( - ecs_iter_t *it) +void* ecs_field_at_w_size( + const ecs_iter_t *it, + size_t size, + int8_t index, + int32_t row) { - ecs_assert(it != NULL, ECS_INTERNAL_ERROR, NULL); - - bool is_instanced = ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced); - if (!is_instanced) { - int32_t instance_count = it->instance_count; - int32_t count = it->count; - int32_t offset = it->offset; - - if (instance_count > count && offset < (instance_count - 1)) { - ecs_assert(count == 1, ECS_INTERNAL_ERROR, NULL); - int t, field_count = it->field_count; - - for (t = 0; t < field_count; t ++) { - ecs_entity_t src = it->sources[t]; - if (!src) { - void *ptr = it->ptrs[t]; - if (ptr) { - it->ptrs[t] = ECS_OFFSET(ptr, it->sizes[t]); - } - } - } - - if (it->entities) { - it->entities ++; - } - it->offset ++; - - return true; - } + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + ecs_check(!size || ecs_field_size(it, index) == size || + !ecs_field_size(it, index), + ECS_INVALID_PARAMETER, "mismatching size for field %d", index); + + const ecs_table_record_t *tr = it->trs[index]; + if (!tr) { + ecs_assert(!ecs_field_is_set(it, index), ECS_INTERNAL_ERROR, NULL); + return NULL; } - return false; -} + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_assert((idr->flags & EcsIdIsSparse), ECS_INVALID_OPERATION, + "use ecs_field to access fields for non-sparse components"); + ecs_assert(it->row_fields & (1ull << index), ECS_INTERNAL_ERROR, NULL); -bool flecs_iter_next_instanced( - ecs_iter_t *it, - bool result) -{ - it->instance_count = it->count; - bool is_instanced = ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced); - bool has_shared = ECS_BIT_IS_SET(it->flags, EcsIterHasShared); - if (result && !is_instanced && it->count && has_shared) { - it->count = 1; + ecs_entity_t src = it->sources[index]; + if (!src) { + src = ecs_table_entities(it->table)[row + it->offset]; } - return result; -} - -/* --- Public API --- */ - -void* ecs_field_w_size( - const ecs_iter_t *it, - size_t size, - int32_t index) -{ - ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - - ecs_check(!size || ecs_field_size(it, index) == size || - (!ecs_field_size(it, index) && (!it->ptrs[index - 1])), - ECS_INVALID_PARAMETER, NULL); - (void)size; - - return it->ptrs[index - 1]; + return flecs_sparse_get_any(idr->sparse, flecs_uto(int32_t, size), src); error: return NULL; } bool ecs_field_is_readonly( const ecs_iter_t *it, - int32_t index) -{ - ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - ecs_term_t *term = &it->terms[index - 1]; + int8_t index) +{ + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(it->query != NULL, ECS_INVALID_PARAMETER, + "operation only valid for query iterators"); + ecs_check(it->query->terms != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + const ecs_term_t *term = &it->query->terms[index]; if (term->inout == EcsIn) { return true; @@ -13271,8 +11445,8 @@ bool ecs_field_is_readonly( return true; } - ecs_term_id_t *src = &term->src; - if (!(src->flags & EcsSelf)) { + const ecs_term_ref_t *src = &term->src; + if (!(src->id & EcsSelf)) { return true; } } @@ -13282,11 +11456,19 @@ bool ecs_field_is_readonly( bool ecs_field_is_writeonly( const ecs_iter_t *it, - int32_t index) -{ - ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - ecs_term_t *term = &it->terms[index - 1]; + int8_t index) +{ + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(it->query != NULL, ECS_INVALID_PARAMETER, + "operation only valid for query iterators"); + ecs_check(it->query->terms != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + const ecs_term_t *term = &it->query->terms[index]; return term->inout == EcsOut; error: return false; @@ -13294,74 +11476,97 @@ bool ecs_field_is_writeonly( bool ecs_field_is_set( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - int32_t column = it->columns[index - 1]; - if (!column) { - return false; - } else if (column < 0) { - if (it->references) { - column = -column - 1; - ecs_ref_t *ref = &it->references[column]; - return ref->entity != 0; - } else { - return true; - } - } - return true; + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + return it->set_fields & (1llu << (index)); error: return false; } bool ecs_field_is_self( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - return it->sources == NULL || it->sources[index - 1] == 0; + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + return it->sources == NULL || it->sources[index] == 0; +error: + return false; } ecs_id_t ecs_field_id( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - return it->ids[index - 1]; + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + return it->ids[index]; +error: + return 0; } -int32_t ecs_field_column_index( +int32_t ecs_field_column( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - int32_t result = it->columns[index - 1]; - if (result <= 0) { - return -1; + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + const ecs_table_record_t *tr = it->trs[index]; + if (tr) { + return tr->index; } else { - return result - 1; + return -1; } +error: + return 0; } ecs_entity_t ecs_field_src( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + if (it->sources) { - return it->sources[index - 1]; + return it->sources[index]; } else { return 0; } +error: + return 0; } size_t ecs_field_size( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - return (size_t)it->sizes[index - 1]; + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + return (size_t)it->sizes[index]; +error: + return 0; } char* ecs_iter_str( @@ -13373,12 +11578,12 @@ char* ecs_iter_str( ecs_world_t *world = it->world; ecs_strbuf_t buf = ECS_STRBUF_INIT; - int i; + int8_t i; if (it->field_count) { ecs_strbuf_list_push(&buf, "id: ", ","); for (i = 0; i < it->field_count; i ++) { - ecs_id_t id = ecs_field_id(it, i + 1); + ecs_id_t id = ecs_field_id(it, i); char *str = ecs_id_str(world, id); ecs_strbuf_list_appendstr(&buf, str); ecs_os_free(str); @@ -13387,8 +11592,8 @@ char* ecs_iter_str( ecs_strbuf_list_push(&buf, "src: ", ","); for (i = 0; i < it->field_count; i ++) { - ecs_entity_t subj = ecs_field_src(it, i + 1); - char *str = ecs_get_fullpath(world, subj); + ecs_entity_t subj = ecs_field_src(it, i); + char *str = ecs_get_path(world, subj); ecs_strbuf_list_appendstr(&buf, str); ecs_os_free(str); } @@ -13396,7 +11601,7 @@ char* ecs_iter_str( ecs_strbuf_list_push(&buf, "set: ", ","); for (i = 0; i < it->field_count; i ++) { - if (ecs_field_is_set(it, i + 1)) { + if (ecs_field_is_set(it, i)) { ecs_strbuf_list_appendlit(&buf, "true"); } else { ecs_strbuf_list_appendlit(&buf, "false"); @@ -13409,7 +11614,7 @@ char* ecs_iter_str( int32_t actual_count = 0; for (i = 0; i < it->variable_count; i ++) { const char *var_name = it->variable_names[i]; - if (!var_name || var_name[0] == '_' || !strcmp(var_name, "This")) { + if (!var_name || var_name[0] == '_' || !strcmp(var_name, "this")) { /* Skip anonymous variables */ continue; } @@ -13424,7 +11629,7 @@ char* ecs_iter_str( ecs_strbuf_list_push(&buf, "var: ", ","); } - char *str = ecs_get_fullpath(world, var.entity); + char *str = ecs_get_path(world, var.entity); ecs_strbuf_list_append(&buf, "%s=%s", var_name, str); ecs_os_free(str); @@ -13439,7 +11644,7 @@ char* ecs_iter_str( ecs_strbuf_appendlit(&buf, "this:\n"); for (i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; - char *str = ecs_get_fullpath(world, e); + char *str = ecs_get_path(world, e); ecs_strbuf_appendlit(&buf, " - "); ecs_strbuf_appendstr(&buf, str); ecs_strbuf_appendch(&buf, '\n'); @@ -13450,16 +11655,6 @@ char* ecs_iter_str( return ecs_strbuf_get(&buf); } -void ecs_iter_poly( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter_out, - ecs_term_t *filter) -{ - ecs_iterable_t *iterable = ecs_get_iterable(poly); - iterable->init(world, poly, iter_out, filter); -} - bool ecs_iter_next( ecs_iter_t *iter) { @@ -13476,7 +11671,6 @@ int32_t ecs_iter_count( ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ECS_BIT_SET(it->flags, EcsIterNoData); - ECS_BIT_SET(it->flags, EcsIterIsInstanced); int32_t count = 0; while (ecs_iter_next(it)) { @@ -13493,7 +11687,6 @@ ecs_entity_t ecs_iter_first( ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ECS_BIT_SET(it->flags, EcsIterNoData); - ECS_BIT_SET(it->flags, EcsIterIsInstanced); ecs_entity_t result = 0; if (ecs_iter_next(it)) { @@ -13512,7 +11705,6 @@ bool ecs_iter_is_true( ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ECS_BIT_SET(it->flags, EcsIterNoData); - ECS_BIT_SET(it->flags, EcsIterIsInstanced); bool result = ecs_iter_next(it); if (result) { @@ -13527,8 +11719,10 @@ ecs_entity_t ecs_iter_get_var( ecs_iter_t *it, int32_t var_id) { - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(it->variables != NULL, ECS_INVALID_PARAMETER, NULL); ecs_var_t *var = &it->variables[var_id]; @@ -13542,8 +11736,7 @@ ecs_entity_t ecs_iter_get_var( if ((var->range.count == 1) || (ecs_table_count(table) == 1)) { ecs_assert(ecs_table_count(table) > var->range.offset, ECS_INTERNAL_ERROR, NULL); - e = ecs_vec_get_t(&table->data.entities, ecs_entity_t, - var->range.offset)[0]; + e = ecs_table_entities(table)[var->range.offset]; } } } else { @@ -13559,8 +11752,10 @@ ecs_table_t* ecs_iter_get_var_as_table( ecs_iter_t *it, int32_t var_id) { - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(it->variables != NULL, ECS_INVALID_PARAMETER, NULL); ecs_var_t *var = &it->variables[var_id]; @@ -13605,8 +11800,10 @@ ecs_table_range_t ecs_iter_get_var_as_range( ecs_iter_t *it, int32_t var_id) { - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(it->variables != NULL, ECS_INVALID_PARAMETER, NULL); ecs_table_range_t result = { 0 }; @@ -13647,12 +11844,14 @@ void ecs_iter_set_var( ecs_entity_t entity) { ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < FLECS_VARIABLE_COUNT_MAX, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < FLECS_QUERY_VARIABLE_COUNT_MAX, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); - /* Can't set variable while iterating */ - ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_PARAMETER, NULL); + ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_PARAMETER, + "cannot constrain variable while iterating"); ecs_check(it->variables != NULL, ECS_INTERNAL_ERROR, NULL); ecs_var_t *var = &it->variables[var_id]; @@ -13671,9 +11870,8 @@ void ecs_iter_set_var( it->constrained_vars |= flecs_ito(uint64_t, 1 << var_id); - if (it->set_var) { - it->set_var(it); - } + /* Update iterator for constrained iterator */ + flecs_query_iter_constrain(it); error: return; @@ -13694,9 +11892,10 @@ void ecs_iter_set_var_as_range( const ecs_table_range_t *range) { ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < FLECS_VARIABLE_COUNT_MAX, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(range != 0, ECS_INVALID_PARAMETER, NULL); ecs_check(range->table != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(!range->offset || range->offset < ecs_table_count(range->table), @@ -13704,22 +11903,24 @@ void ecs_iter_set_var_as_range( ecs_check((range->offset + range->count) <= ecs_table_count(range->table), ECS_INVALID_PARAMETER, NULL); - /* Can't set variable while iterating */ - ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_OPERATION, NULL); + ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_OPERATION, + "cannot set query variables while iterating"); ecs_var_t *var = &it->variables[var_id]; var->range = *range; if (range->count == 1) { ecs_table_t *table = range->table; - var->entity = ecs_vec_get_t( - &table->data.entities, ecs_entity_t, range->offset)[0]; + var->entity = ecs_table_entities(table)[range->offset]; } else { var->entity = 0; } it->constrained_vars |= flecs_uto(uint64_t, 1 << var_id); + /* Update iterator for constrained iterator */ + flecs_query_iter_constrain(it); + error: return; } @@ -13752,9 +11953,9 @@ ecs_iter_t ecs_page_iter( ecs_check(it->next != NULL, ECS_INVALID_PARAMETER, NULL); ecs_iter_t result = *it; - result.priv.cache.stack_cursor = NULL; /* Don't copy allocator cursor */ + result.priv_.cache.stack_cursor = NULL; /* Don't copy allocator cursor */ - result.priv.iter.page = (ecs_page_iter_t){ + result.priv_.iter.page = (ecs_page_iter_t){ .offset = offset, .limit = limit, .remaining = limit @@ -13768,33 +11969,7 @@ ecs_iter_t ecs_page_iter( return (ecs_iter_t){ 0 }; } -static -void flecs_offset_iter( - ecs_iter_t *it, - int32_t offset) -{ - it->entities = &it->entities[offset]; - - int32_t t, field_count = it->field_count; - void **it_ptrs = it->ptrs; - if (it_ptrs) { - for (t = 0; t < field_count; t ++) { - void *ptrs = it_ptrs[t]; - if (!ptrs) { - continue; - } - - if (it->sources[t]) { - continue; - } - - it->ptrs[t] = ECS_OFFSET(ptrs, offset * it->sizes[t]); - } - } -} - -static -bool ecs_page_next_instanced( +bool ecs_page_next( ecs_iter_t *it) { ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); @@ -13802,20 +11977,16 @@ bool ecs_page_next_instanced( ecs_check(it->next == ecs_page_next, ECS_INVALID_PARAMETER, NULL); ecs_iter_t *chain_it = it->chain_it; - bool instanced = ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced); do { if (!ecs_iter_next(chain_it)) { goto depleted; } - ecs_page_iter_t *iter = &it->priv.iter.page; + ecs_page_iter_t *iter = &it->priv_.iter.page; /* Copy everything up to the private iterator data */ - ecs_os_memcpy(it, chain_it, offsetof(ecs_iter_t, priv)); - - /* Keep instancing setting from original iterator */ - ECS_BIT_COND(it->flags, EcsIterIsInstanced, instanced); + ecs_os_memcpy(it, chain_it, offsetof(ecs_iter_t, priv_)); if (!chain_it->table) { goto yield; /* Task query */ @@ -13841,10 +12012,11 @@ bool ecs_page_next_instanced( it->count = 0; continue; } else { - it->offset += offset; - count = it->count -= offset; iter->offset = 0; - flecs_offset_iter(it, offset); + it->offset = offset; + count = it->count -= offset; + it->entities = + &(ecs_table_entities(it->table)[it->offset]); } } @@ -13862,33 +12034,13 @@ bool ecs_page_next_instanced( } while (it->count == 0); yield: - if (!ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced)) { - it->offset = 0; - } - return true; + done: /* Cleanup iterator resources if it wasn't yet depleted */ ecs_iter_fini(chain_it); -depleted: -error: - return false; -} - -bool ecs_page_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_page_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->chain_it != NULL, ECS_INVALID_PARAMETER, NULL); - ECS_BIT_SET(it->chain_it->flags, EcsIterIsInstanced); - - if (flecs_iter_next_row(it)) { - return true; - } - - return flecs_iter_next_instanced(it, ecs_page_next_instanced(it)); +depleted: error: return false; } @@ -13901,13 +12053,14 @@ ecs_iter_t ecs_worker_iter( ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(it->next != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(count > 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(index >= 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); ecs_check(index < count, ECS_INVALID_PARAMETER, NULL); ecs_iter_t result = *it; - result.priv.cache.stack_cursor = NULL; /* Don't copy allocator cursor */ + result.priv_.cache.stack_cursor = NULL; /* Don't copy allocator cursor */ - result.priv.iter.worker = (ecs_worker_iter_t){ + result.priv_.iter.worker = (ecs_worker_iter_t){ .index = index, .count = count }; @@ -13920,20 +12073,17 @@ ecs_iter_t ecs_worker_iter( return (ecs_iter_t){ 0 }; } -static -bool ecs_worker_next_instanced( +bool ecs_worker_next( ecs_iter_t *it) { ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(it->chain_it != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(it->next == ecs_worker_next, ECS_INVALID_PARAMETER, NULL); - bool instanced = ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced); - ecs_iter_t *chain_it = it->chain_it; - ecs_worker_iter_t *iter = &it->priv.iter.worker; + ecs_worker_iter_t *iter = &it->priv_.iter.worker; int32_t res_count = iter->count, res_index = iter->index; - int32_t per_worker, instances_per_worker, first; + int32_t per_worker, first; do { if (!ecs_iter_next(chain_it)) { @@ -13941,15 +12091,10 @@ bool ecs_worker_next_instanced( } /* Copy everything up to the private iterator data */ - ecs_os_memcpy(it, chain_it, offsetof(ecs_iter_t, priv)); - - /* Keep instancing setting from original iterator */ - ECS_BIT_COND(it->flags, EcsIterIsInstanced, instanced); + ecs_os_memcpy(it, chain_it, offsetof(ecs_iter_t, priv_)); int32_t count = it->count; - int32_t instance_count = it->instance_count; per_worker = count / res_count; - instances_per_worker = instance_count / res_count; first = per_worker * res_index; count -= per_worker * res_count; @@ -13974,41 +12119,17 @@ bool ecs_worker_next_instanced( } } while (!per_worker); - it->instance_count = instances_per_worker; it->frame_offset += first; - - flecs_offset_iter(it, it->offset + first); it->count = per_worker; + it->offset += first; - if (ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced)) { - it->offset += first; - } else { - it->offset = 0; - } + it->entities = &(ecs_table_entities(it->table)[it->offset]); return true; error: return false; } -bool ecs_worker_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_worker_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->chain_it != NULL, ECS_INVALID_PARAMETER, NULL); - - ECS_BIT_SET(it->chain_it->flags, EcsIterIsInstanced); - - if (flecs_iter_next_row(it)) { - return true; - } - - return flecs_iter_next_instanced(it, ecs_worker_next_instanced(it)); -error: - return false; -} - /** * @file misc.c * @brief Miscellaneous functions. @@ -14137,6 +12258,12 @@ int flecs_entity_compare( return (e1 > e2) - (e1 < e2); } +int flecs_id_qsort_cmp(const void *a, const void *b) { + ecs_id_t id_a = *(const ecs_id_t*)a; + ecs_id_t id_b = *(const ecs_id_t*)b; + return (id_a > id_b) - (id_a < id_b); +} + uint64_t flecs_string_hash( const void *ptr) { @@ -14145,7 +12272,7 @@ uint64_t flecs_string_hash( return str->hash; } -char* ecs_vasprintf( +char* flecs_vasprintf( const char *fmt, va_list args) { @@ -14169,18 +12296,18 @@ char* ecs_vasprintf( return NULL; } - ecs_os_vsprintf(result, fmt, args); + ecs_os_vsnprintf(result, size + 1, fmt, args); return result; } -char* ecs_asprintf( +char* flecs_asprintf( const char *fmt, ...) { va_list args; va_start(args, fmt); - char *result = ecs_vasprintf(fmt, args); + char *result = flecs_vasprintf(fmt, args); va_end(args); return result; } @@ -14217,6 +12344,259 @@ char* flecs_to_snake_case(const char *str) { return out; } +char* flecs_load_from_file( + const char *filename) +{ + FILE* file; + char* content = NULL; + int32_t bytes; + size_t size; + + /* Open file for reading */ + ecs_os_fopen(&file, filename, "r"); + if (!file) { + ecs_err("%s (%s)", ecs_os_strerror(errno), filename); + goto error; + } + + /* Determine file size */ + fseek(file, 0, SEEK_END); + bytes = (int32_t)ftell(file); + if (bytes == -1) { + goto error; + } + fseek(file, 0, SEEK_SET); + + /* Load contents in memory */ + content = ecs_os_malloc(bytes + 1); + size = (size_t)bytes; + if (!(size = fread(content, 1, size, file)) && bytes) { + ecs_err("%s: read zero bytes instead of %d", filename, size); + ecs_os_free(content); + content = NULL; + goto error; + } else { + content[size] = '\0'; + } + + fclose(file); + + return content; +error: + ecs_os_free(content); + return NULL; +} + +char* flecs_chresc( + char *out, + char in, + char delimiter) +{ + char *bptr = out; + switch(in) { + case '\a': + *bptr++ = '\\'; + *bptr = 'a'; + break; + case '\b': + *bptr++ = '\\'; + *bptr = 'b'; + break; + case '\f': + *bptr++ = '\\'; + *bptr = 'f'; + break; + case '\n': + *bptr++ = '\\'; + *bptr = 'n'; + break; + case '\r': + *bptr++ = '\\'; + *bptr = 'r'; + break; + case '\t': + *bptr++ = '\\'; + *bptr = 't'; + break; + case '\v': + *bptr++ = '\\'; + *bptr = 'v'; + break; + case '\\': + *bptr++ = '\\'; + *bptr = '\\'; + break; + case '\033': + *bptr = '['; /* Used for terminal colors */ + break; + default: + if (in == delimiter) { + *bptr++ = '\\'; + *bptr = delimiter; + } else { + *bptr = in; + } + break; + } + + *(++bptr) = '\0'; + + return bptr; +} + +const char* flecs_chrparse( + const char *in, + char *out) +{ + const char *result = in + 1; + char ch; + + if (in[0] == '\\') { + result ++; + + switch(in[1]) { + case 'a': + ch = '\a'; + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case 'v': + ch = '\v'; + break; + case '\\': + ch = '\\'; + break; + case '"': + ch = '"'; + break; + case '0': + ch = '\0'; + break; + case ' ': + ch = ' '; + break; + case '$': + ch = '$'; + break; + default: + goto error; + } + } else { + ch = in[0]; + } + + if (out) { + *out = ch; + } + + return result; +error: + return NULL; +} + +ecs_size_t flecs_stresc( + char *out, + ecs_size_t n, + char delimiter, + const char *in) +{ + const char *ptr = in; + char ch, *bptr = out, buff[3]; + ecs_size_t written = 0; + while ((ch = *ptr++)) { + if ((written += (ecs_size_t)(flecs_chresc( + buff, ch, delimiter) - buff)) <= n) + { + /* If size != 0, an out buffer must be provided. */ + ecs_check(out != NULL, ECS_INVALID_PARAMETER, NULL); + *bptr++ = buff[0]; + if ((ch = buff[1])) { + *bptr = ch; + bptr++; + } + } + } + + if (bptr) { + while (written < n) { + *bptr = '\0'; + bptr++; + written++; + } + } + return written; +error: + return 0; +} + +char* flecs_astresc( + char delimiter, + const char *in) +{ + if (!in) { + return NULL; + } + + ecs_size_t len = flecs_stresc(NULL, 0, delimiter, in); + char *out = ecs_os_malloc_n(char, len + 1); + flecs_stresc(out, len, delimiter, in); + out[len] = '\0'; + return out; +} + +const char* flecs_parse_digit( + const char *ptr, + char *token) +{ + char *tptr = token; + char ch = ptr[0]; + + if (!isdigit(ch) && ch != '-') { + ecs_parser_error(NULL, NULL, 0, "invalid start of number '%s'", ptr); + return NULL; + } + + tptr[0] = ch; + tptr ++; + ptr ++; + + for (; (ch = *ptr); ptr ++) { + if (!isdigit(ch) && (ch != '.') && (ch != 'e')) { + break; + } + + tptr[0] = ch; + tptr ++; + } + + tptr[0] = '\0'; + + return ptr; +} + +const char* flecs_parse_ws_eol( + const char *ptr) +{ + while (isspace(*ptr)) { + ptr ++; + } + + return ptr; +} + /** * @file observable.c * @brief Observable implementation. @@ -14235,7 +12615,6 @@ void flecs_observable_init( observable->on_add.event = EcsOnAdd; observable->on_remove.event = EcsOnRemove; observable->on_set.event = EcsOnSet; - observable->un_set.event = EcsUnSet; } void flecs_observable_fini( @@ -14247,8 +12626,6 @@ void flecs_observable_fini( ECS_INTERNAL_ERROR, NULL); ecs_assert(!ecs_map_is_init(&observable->on_set.event_ids), ECS_INTERNAL_ERROR, NULL); - ecs_assert(!ecs_map_is_init(&observable->un_set.event_ids), - ECS_INTERNAL_ERROR, NULL); ecs_sparse_t *events = &observable->events; int32_t i, count = flecs_sparse_count(events); @@ -14258,7 +12635,7 @@ void flecs_observable_fini( ecs_assert(er != NULL, ECS_INTERNAL_ERROR, NULL); (void)er; - /* All triggers should've unregistered by now */ + /* All observers should've unregistered by now */ ecs_assert(!ecs_map_is_init(&er->event_ids), ECS_INTERNAL_ERROR, NULL); } @@ -14273,11 +12650,10 @@ ecs_event_record_t* flecs_event_record_get( ecs_assert(o != NULL, ECS_INTERNAL_ERROR, NULL); /* Builtin events*/ - if (event == EcsOnAdd) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_add); - else if (event == EcsOnRemove) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_remove); - else if (event == EcsOnSet) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_set); - else if (event == EcsUnSet) return ECS_CONST_CAST(ecs_event_record_t*, &o->un_set); - else if (event == EcsWildcard) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_wildcard); + if (event == EcsOnAdd) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_add); + else if (event == EcsOnRemove) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_remove); + else if (event == EcsOnSet) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_set); + else if (event == EcsWildcard) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_wildcard); /* User events */ return flecs_sparse_try_t(&o->events, ecs_event_record_t, event); @@ -14429,13 +12805,19 @@ int32_t flecs_event_observers_get( ecs_id_t id_fwc = ecs_pair(EcsWildcard, ECS_PAIR_SECOND(id)); ecs_id_t id_swc = ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard); ecs_id_t id_pwc = ecs_pair(EcsWildcard, EcsWildcard); - iders[count] = flecs_event_id_record_get_if(er, id_fwc); - count += iders[count] != 0; - iders[count] = flecs_event_id_record_get_if(er, id_swc); - count += iders[count] != 0; - iders[count] = flecs_event_id_record_get_if(er, id_pwc); - count += iders[count] != 0; - } else { + if (id_fwc != id) { + iders[count] = flecs_event_id_record_get_if(er, id_fwc); + count += iders[count] != 0; + } + if (id_swc != id) { + iders[count] = flecs_event_id_record_get_if(er, id_swc); + count += iders[count] != 0; + } + if (id_pwc != id) { + iders[count] = flecs_event_id_record_get_if(er, id_pwc); + count += iders[count] != 0; + } + } else if (id != EcsWildcard) { iders[count] = flecs_event_id_record_get_if(er, EcsWildcard); count += iders[count] != 0; } @@ -14483,6 +12865,7 @@ void flecs_emit_propagate_id( } const ecs_table_record_t *tr; + int32_t event_cur = it->event_cur; while ((tr = flecs_table_cache_next(&idt, ecs_table_record_t))) { ecs_table_t *table = tr->hdr.table; if (!ecs_table_count(table)) { @@ -14496,23 +12879,23 @@ void flecs_emit_propagate_id( it->other_table = NULL; it->offset = 0; it->count = entity_count; + it->up_fields = 1; if (entity_count) { - it->entities = ecs_vec_first(&table->data.entities); + it->entities = ecs_table_entities(table); } /* Treat as new event as this could invoke observers again for * different tables. */ - int32_t evtx = ++ world->event_id; + it->event_cur = ++ world->event_id; int32_t ider_i; for (ider_i = 0; ider_i < ider_count; ider_i ++) { ecs_event_id_record_t *ider = iders[ider_i]; - flecs_observers_invoke(world, &ider->up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->up, it, table, trav); if (!owned) { /* Owned takes precedence */ - flecs_observers_invoke( - world, &ider->self_up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->self_up, it, table, trav); } } @@ -14520,7 +12903,7 @@ void flecs_emit_propagate_id( continue; } - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + const ecs_entity_t *entities = ecs_table_entities(table); for (e = 0; e < entity_count; e ++) { ecs_record_t *r = flecs_entities_get(world, entities[e]); ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); @@ -14533,6 +12916,9 @@ void flecs_emit_propagate_id( } } } + + it->event_cur = event_cur; + it->up_fields = 0; } static @@ -14552,6 +12938,7 @@ void flecs_emit_propagate( ecs_dbg_3("propagate events/invalidate cache for %s", idstr); ecs_os_free(idstr); } + ecs_log_push_3(); /* Propagate to records of traversable relationships */ @@ -14562,7 +12949,9 @@ void flecs_emit_propagate( /* Get traversed relationship */ ecs_entity_t trav = ECS_PAIR_FIRST(cur->id); if (propagate_trav && propagate_trav != trav) { - continue; + if (propagate_trav != EcsIsA) { + continue; + } } flecs_emit_propagate_id( @@ -14609,7 +12998,7 @@ void flecs_emit_propagate_invalidate_tables( } int32_t e, entity_count = ecs_table_count(table); - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + const ecs_entity_t *entities = ecs_table_entities(table); for (e = 0; e < entity_count; e ++) { ecs_record_t *r = flecs_entities_get(world, entities[e]); @@ -14630,8 +13019,7 @@ void flecs_emit_propagate_invalidate( int32_t offset, int32_t count) { - ecs_entity_t *entities = ecs_vec_get_t(&table->data.entities, - ecs_entity_t, offset); + const ecs_entity_t *entities = &ecs_table_entities(table)[offset]; int32_t i; for (i = 0; i < count; i ++) { ecs_record_t *record = flecs_entities_get(world, entities[i]); @@ -14649,10 +13037,60 @@ void flecs_emit_propagate_invalidate( } } +static +void flecs_propagate_entities( + ecs_world_t *world, + ecs_iter_t *it, + ecs_id_record_t *idr, + const ecs_entity_t *entities, + int32_t count, + ecs_entity_t src, + ecs_event_id_record_t **iders, + int32_t ider_count) +{ + if (!count) { + return; + } + + ecs_entity_t old_src = it->sources[0]; + ecs_table_t *old_table = it->table; + ecs_table_t *old_other_table = it->other_table; + const ecs_entity_t *old_entities = it->entities; + int32_t old_count = it->count; + int32_t old_offset = it->offset; + + int32_t i; + for (i = 0; i < count; i ++) { + ecs_record_t *record = flecs_entities_get(world, entities[i]); + if (!record) { + /* If the event is emitted after a bulk operation, it's possible + * that it hasn't been populated with entities yet. */ + continue; + } + + ecs_id_record_t *idr_t = record->idr; + if (idr_t) { + /* Entity is used as target in traversable pairs, propagate */ + ecs_entity_t e = src ? src : entities[i]; + it->sources[0] = e; + flecs_emit_propagate( + world, it, idr, idr_t, 0, iders, ider_count); + } + } + + it->table = old_table; + it->other_table = old_other_table; + it->entities = old_entities; + it->count = old_count; + it->offset = old_offset; + it->sources[0] = old_src; +} + static void flecs_override_copy( ecs_world_t *world, ecs_table_t *table, + const ecs_table_record_t *tr, const ecs_type_info_t *ti, void *dst, const void *src, @@ -14663,9 +13101,10 @@ void flecs_override_copy( ecs_copy_t copy = ti->hooks.copy; ecs_size_t size = ti->size; int32_t i; + if (copy) { for (i = 0; i < count; i ++) { - copy(ptr, src, count, ti); + copy(ptr, src, 1, ti); ptr = ECS_OFFSET(ptr, size); } } else { @@ -14677,10 +13116,9 @@ void flecs_override_copy( ecs_iter_action_t on_set = ti->hooks.on_set; if (on_set) { - ecs_entity_t *entities = ecs_vec_get_t( - &table->data.entities, ecs_entity_t, offset); - flecs_invoke_hook(world, table, count, offset, entities, - dst, ti->component, ti, EcsOnSet, on_set); + const ecs_entity_t *entities = &ecs_table_entities(table)[offset]; + flecs_invoke_hook(world, table, tr, count, offset, entities, + ti->component, ti, EcsOnSet, on_set); } } @@ -14704,7 +13142,7 @@ void* flecs_override( * (like what happens during an auto override), we need to copy the * value of the inherited component to the new component. * Also flag to the callee that this component was overridden, so - * that an OnSet event can be emmitted for it. + * that an OnSet event can be emitted for it. * Note that this is different from a component that was overridden * after it was inherited, as this does not change the actual value * of the component for the entity (it is copied from the existing @@ -14719,7 +13157,7 @@ void* flecs_override( ecs_column_t *column = &table->data.columns[index]; ecs_size_t size = column->ti->size; - return ecs_vec_get(&column->data, size, it->offset); + return ECS_ELEM(column->data, size, it->offset); } } @@ -14736,8 +13174,7 @@ void flecs_emit_forward_up( ecs_table_t *table, ecs_id_record_t *idr, ecs_vec_t *stack, - ecs_vec_t *reachable_ids, - int32_t evtx); + ecs_vec_t *reachable_ids); static void flecs_emit_forward_id( @@ -14751,9 +13188,7 @@ void flecs_emit_forward_id( ecs_entity_t tgt, ecs_table_t *tgt_table, int32_t column, - int32_t offset, - ecs_entity_t trav, - int32_t evtx) + ecs_entity_t trav) { ecs_id_t id = idr->id; ecs_entity_t event = er ? er->event : 0; @@ -14777,15 +13212,15 @@ void flecs_emit_forward_id( it->ids[0] = id; it->sources[0] = tgt; it->event_id = id; - it->ptrs[0] = NULL; - it->sizes[0] = 0; + ECS_CONST_CAST(int32_t*, it->sizes)[0] = 0; /* safe, owned by observer */ + it->up_fields = 1; int32_t storage_i = ecs_table_type_to_column_index(tgt_table, column); if (storage_i != -1) { ecs_assert(idr->type_info != NULL, ECS_INTERNAL_ERROR, NULL); ecs_column_t *c = &tgt_table->data.columns[storage_i]; - it->ptrs[0] = ecs_vec_get(&c->data, c->ti->size, offset); - it->sizes[0] = c->ti->size; + it->trs[0] = &tgt_table->_->records[column]; + ECS_CONST_CAST(int32_t*, it->sizes)[0] = c->ti->size; /* safe, see above */ } ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); @@ -14793,11 +13228,11 @@ void flecs_emit_forward_id( for (ider_i = 0; ider_i < ider_count; ider_i ++) { ecs_event_id_record_t *ider = iders[ider_i]; - flecs_observers_invoke(world, &ider->up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->up, it, table, trav); /* Owned takes precedence */ if (!owned) { - flecs_observers_invoke(world, &ider->self_up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->self_up, it, table, trav); } } @@ -14807,11 +13242,11 @@ void flecs_emit_forward_id( /* If component was added together with IsA relationship, still emit * OnSet event, as it's a new value for the entity. */ - void *base_ptr = it->ptrs[0]; + ecs_table_record_t *base_tr = ECS_CONST_CAST( + ecs_table_record_t*, it->trs[0]); void *ptr = flecs_override(it, emit_ids, id, table, idr); if (ptr) { override = true; - it->ptrs[0] = ptr; } if (ider_onset_count) { @@ -14819,25 +13254,28 @@ void flecs_emit_forward_id( for (ider_onset_i = 0; ider_onset_i < ider_onset_count; ider_onset_i ++) { ecs_event_id_record_t *ider = iders_onset[ider_onset_i]; - flecs_observers_invoke(world, &ider->up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->up, it, table, trav); /* Owned takes precedence */ if (!owned) { flecs_observers_invoke( - world, &ider->self_up, it, table, trav, evtx); + world, &ider->self_up, it, table, trav); } else if (override) { ecs_entity_t src = it->sources[0]; it->sources[0] = 0; - flecs_observers_invoke(world, &ider->self, it, table, 0, evtx); - flecs_observers_invoke(world, &ider->self_up, it, table, 0, evtx); + it->trs[0] = tr; + flecs_observers_invoke(world, &ider->self, it, table, 0); + flecs_observers_invoke(world, &ider->self_up, it, table, 0); it->sources[0] = src; } } it->event = event; - it->ptrs[0] = base_ptr; + it->trs[0] = base_tr; } } + + it->up_fields = 0; } static @@ -14854,10 +13292,8 @@ void flecs_emit_forward_and_cache_id( ecs_table_t *tgt_table, const ecs_table_record_t *tgt_tr, int32_t column, - int32_t offset, ecs_vec_t *reachable_ids, - ecs_entity_t trav, - int32_t evtx) + ecs_entity_t trav) { /* Cache forwarded id for (rel, tgt) pair */ ecs_reachable_elem_t *elem = ecs_vec_append_t(&world->allocator, @@ -14872,7 +13308,7 @@ void flecs_emit_forward_and_cache_id( ecs_assert(tgt_table == tgt_record->table, ECS_INTERNAL_ERROR, NULL); flecs_emit_forward_id(world, er, er_onset, emit_ids, it, table, idr, - tgt, tgt_table, column, offset, trav, evtx); + tgt, tgt_table, column, trav); } static @@ -14912,8 +13348,7 @@ void flecs_emit_forward_cached_ids( ecs_reachable_cache_t *rc, ecs_vec_t *reachable_ids, ecs_vec_t *stack, - ecs_entity_t trav, - int32_t evtx) + ecs_entity_t trav) { ecs_reachable_elem_t *elems = ecs_vec_first_t(&rc->ids, ecs_reachable_elem_t); @@ -14936,11 +13371,10 @@ void flecs_emit_forward_cached_ids( continue; } - int32_t rc_offset = ECS_RECORD_TO_ROW(rc_record->row); flecs_emit_forward_and_cache_id(world, er, er_onset, emit_ids, it, table, rc_idr, rc_elem->src, rc_record, rc_record->table, rc_tr, rc_tr->index, - rc_offset, reachable_ids, trav, evtx); + reachable_ids, trav); } } @@ -14979,13 +13413,11 @@ void flecs_emit_forward_table_up( ecs_record_t *tgt_record, ecs_id_record_t *tgt_idr, ecs_vec_t *stack, - ecs_vec_t *reachable_ids, - int32_t evtx) + ecs_vec_t *reachable_ids) { ecs_allocator_t *a = &world->allocator; int32_t i, id_count = tgt_table->type.count; ecs_id_t *ids = tgt_table->type.array; - int32_t offset = ECS_RECORD_TO_ROW(tgt_record->row); int32_t rc_child_offset = ecs_vec_count(reachable_ids); int32_t stack_count = ecs_vec_count(stack); @@ -15014,12 +13446,12 @@ void flecs_emit_forward_table_up( ecs_id_t id = ids[i]; ecs_table_record_t *tgt_tr = &tgt_table->_->records[i]; ecs_id_record_t *idr = (ecs_id_record_t*)tgt_tr->hdr.cache; - if (inherit && (idr->flags & EcsIdDontInherit)) { + if (inherit && !(idr->flags & EcsIdOnInstantiateInherit)) { continue; } /* Id has the same relationship, traverse to find ids for forwarding */ - if (ECS_PAIR_FIRST(id) == trav) { + if (ECS_PAIR_FIRST(id) == trav || ECS_PAIR_FIRST(id) == EcsIsA) { ecs_table_t **t = ecs_vec_append_t(&world->allocator, stack, ecs_table_t*); t[0] = tgt_table; @@ -15036,13 +13468,13 @@ void flecs_emit_forward_table_up( } ecs_log_push_3(); flecs_emit_forward_cached_ids(world, er, er_onset, emit_ids, it, - table, idr_rc, reachable_ids, stack, trav, evtx); + table, idr_rc, reachable_ids, stack, trav); ecs_log_pop_3(); } else { /* Cache is dirty, traverse upwards */ do { flecs_emit_forward_up(world, er, er_onset, emit_ids, it, - table, idr, stack, reachable_ids, evtx); + table, idr, stack, reachable_ids); if (++i >= id_count) { break; } @@ -15079,7 +13511,7 @@ void flecs_emit_forward_table_up( flecs_emit_forward_and_cache_id(world, er, er_onset, emit_ids, it, table, idr, tgt, tgt_record, tgt_table, tgt_tr, i, - offset, reachable_ids, trav, evtx); + reachable_ids, trav); } if (parent_revalidate) { @@ -15124,12 +13556,11 @@ void flecs_emit_forward_up( ecs_table_t *table, ecs_id_record_t *idr, ecs_vec_t *stack, - ecs_vec_t *reachable_ids, - int32_t evtx) + ecs_vec_t *reachable_ids) { ecs_id_t id = idr->id; ecs_entity_t tgt = ECS_PAIR_SECOND(id); - tgt = flecs_entities_get_generation(world, tgt); + tgt = flecs_entities_get_alive(world, tgt); ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); ecs_record_t *tgt_record = flecs_entities_try(world, tgt); ecs_table_t *tgt_table; @@ -15138,7 +13569,7 @@ void flecs_emit_forward_up( } flecs_emit_forward_table_up(world, er, er_onset, emit_ids, it, table, - tgt, tgt_table, tgt_record, idr, stack, reachable_ids, evtx); + tgt, tgt_table, tgt_record, idr, stack, reachable_ids); } static @@ -15149,8 +13580,7 @@ void flecs_emit_forward( const ecs_type_t *emit_ids, ecs_iter_t *it, ecs_table_t *table, - ecs_id_record_t *idr, - int32_t evtx) + ecs_id_record_t *idr) { ecs_reachable_cache_t *rc = &idr->reachable; @@ -15167,7 +13597,7 @@ void flecs_emit_forward( ecs_vec_init_t(&world->allocator, &stack, ecs_table_t*, 0); ecs_vec_reset_t(&world->allocator, &rc->ids, ecs_reachable_elem_t); flecs_emit_forward_up(world, er, er_onset, emit_ids, it, table, - idr, &stack, &rc->ids, evtx); + idr, &stack, &rc->ids); it->sources[0] = 0; ecs_vec_fini_t(&world->allocator, &stack, ecs_table_t*); @@ -15212,9 +13642,48 @@ void flecs_emit_forward( ECS_INTERNAL_ERROR, NULL); ecs_dbg_assert(r->table == elem->table, ECS_INTERNAL_ERROR, NULL); - int32_t offset = ECS_RECORD_TO_ROW(r->row); flecs_emit_forward_id(world, er, er_onset, emit_ids, it, table, - rc_idr, elem->src, r->table, tr->index, offset, trav, evtx); + rc_idr, elem->src, r->table, tr->index, trav); + } + } + + /* Propagate events for new reachable ids downwards */ + if (table->_->traversable_count) { + int32_t i; + const ecs_entity_t *entities = ecs_table_entities(table); + entities = ECS_ELEM_T(entities, ecs_entity_t, it->offset); + for (i = 0; i < it->count; i ++) { + ecs_record_t *r = flecs_entities_get(world, entities[i]); + if (r->idr) { + break; + } + } + + if (i != it->count) { + ecs_reachable_elem_t *elems = ecs_vec_first_t(&rc->ids, + ecs_reachable_elem_t); + int32_t count = ecs_vec_count(&rc->ids); + for (i = 0; i < count; i ++) { + ecs_reachable_elem_t *elem = &elems[i]; + const ecs_table_record_t *tr = elem->tr; + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_id_record_t *rc_idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_record_t *r = elem->record; + + ecs_assert(rc_idr->id == elem->id, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_entities_get(world, elem->src) == r, + ECS_INTERNAL_ERROR, NULL); + ecs_dbg_assert(r->table == elem->table, ECS_INTERNAL_ERROR, NULL); + (void)r; + + ecs_event_id_record_t *iders[5] = {0}; + int32_t ider_count = flecs_event_observers_get( + er, rc_idr->id, iders); + + flecs_propagate_entities(world, it, rc_idr, it->entities, + it->count, elem->src, iders, ider_count); + } } } } @@ -15227,9 +13696,10 @@ void flecs_emit_forward( void flecs_emit( ecs_world_t *world, ecs_world_t *stage, + ecs_flags64_t set_mask, ecs_event_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(desc->event != 0, ECS_INVALID_PARAMETER, NULL); ecs_check(desc->event != EcsWildcard, ECS_INVALID_PARAMETER, NULL); @@ -15238,6 +13708,8 @@ void flecs_emit( ecs_check(desc->table != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(desc->observable != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_os_perf_trace_push("flecs.emit"); + ecs_time_t t = {0}; bool measure_time = world->flags & EcsWorldMeasureSystemTime; if (measure_time) { @@ -15248,13 +13720,13 @@ void flecs_emit( ecs_entity_t event = desc->event; ecs_table_t *table = desc->table, *other_table = desc->other_table; int32_t offset = desc->offset; - int32_t i, r, count = desc->count; + int32_t i, count = desc->count; ecs_flags32_t table_flags = table->flags; /* Deferring cannot be suspended for observers */ - int32_t defer = world->stages[0].defer; + int32_t defer = world->stages[0]->defer; if (defer < 0) { - world->stages[0].defer *= -1; + world->stages[0]->defer *= -1; } /* Table events are emitted for internal table operations only, and do not @@ -15265,26 +13737,28 @@ void flecs_emit( count = ecs_table_count(table) - offset; } - /* When the NoOnSet flag is provided, no OnSet/UnSet events should be - * generated when new components are inherited. */ - bool no_on_set = desc->flags & EcsEventNoOnSet; + /* The world event id is used to determine if an observer has already been + * triggered for an event. Observers for multiple components are split up + * into multiple observers for a single component, and this counter is used + * to make sure a multi observer only triggers once, even if multiple of its + * single-component observers trigger. */ + int32_t evtx = ++world->event_id; ecs_id_t ids_cache = 0; - void *ptrs_cache = NULL; ecs_size_t sizes_cache = 0; - int32_t columns_cache = 0; + const ecs_table_record_t* trs_cache = 0; ecs_entity_t sources_cache = 0; ecs_iter_t it = { .world = stage, .real_world = world, .event = event, + .event_cur = evtx, .table = table, .field_count = 1, .ids = &ids_cache, - .ptrs = &ptrs_cache, .sizes = &sizes_cache, - .columns = &columns_cache, + .trs = (const ecs_table_record_t**)&trs_cache, .sources = &sources_cache, .other_table = other_table, .offset = offset, @@ -15293,34 +13767,26 @@ void flecs_emit( .flags = desc->flags | EcsIterIsValid }; - /* The world event id is used to determine if an observer has already been - * triggered for an event. Observers for multiple components are split up - * into multiple observers for a single component, and this counter is used - * to make sure a multi observer only triggers once, even if multiple of its - * single-component observers trigger. */ - int32_t evtx = ++world->event_id; - ecs_observable_t *observable = ecs_get_observable(desc->observable); ecs_check(observable != NULL, ECS_INVALID_PARAMETER, NULL); /* Event records contain all observers for a specific event. In addition to * the emitted event, also request data for the Wildcard event (for - * observers subscribing to the wildcard event), OnSet and UnSet events. The - * latter to are used for automatically emitting OnSet/UnSet events for + * observers subscribing to the wildcard event), OnSet events. The + * latter to are used for automatically emitting OnSet events for * inherited components, for example when an IsA relationship is added to an * entity. This doesn't add much overhead, as fetching records is cheap for * builtin event types. */ const ecs_event_record_t *er = flecs_event_record_get_if(observable, event); const ecs_event_record_t *wcer = flecs_event_record_get_if(observable, EcsWildcard); const ecs_event_record_t *er_onset = flecs_event_record_get_if(observable, EcsOnSet); - const ecs_event_record_t *er_unset = flecs_event_record_get_if(observable, EcsUnSet); ecs_data_t *storage = NULL; ecs_column_t *columns = NULL; if (count) { storage = &table->data; columns = storage->columns; - it.entities = ecs_vec_get_t(&storage->entities, ecs_entity_t, offset); + it.entities = &ecs_table_entities(table)[offset]; } int32_t id_count = ids->count; @@ -15341,13 +13807,7 @@ void flecs_emit( /* Does table has observed entities */ bool has_observed = table_flags & EcsTableHasTraversable; - /* When a relationship is removed, the events reachable through that - * relationship should emit UnSet events. This is part of the behavior that - * allows observers to be agnostic of whether a component is inherited. */ - bool can_unset = count && (event == EcsOnRemove) && !no_on_set; - ecs_event_id_record_t *iders[5] = {0}; - int32_t unset_count = 0; if (count && can_forward && has_observed) { flecs_emit_propagate_invalidate(world, table, offset, count); @@ -15369,22 +13829,37 @@ void flecs_emit( ecs_id_record_t *idr = NULL; const ecs_type_info_t *ti = NULL; ecs_id_t id = id_array[i]; + ecs_assert(id == EcsAny || !ecs_id_is_wildcard(id), + ECS_INVALID_PARAMETER, "cannot emit wildcard ids"); int32_t ider_i, ider_count = 0; bool is_pair = ECS_IS_PAIR(id); void *override_ptr = NULL; + bool override_base_added = false; + ecs_table_record_t *base_tr = NULL; ecs_entity_t base = 0; + bool id_can_override = can_override; + ecs_flags64_t id_bit = 1llu << i; + if (id_bit & set_mask) { + /* Component is already set, so don't override with prefab value */ + id_can_override = false; + } /* Check if this id is a pair of an traversable relationship. If so, we * may have to forward ids from the pair's target. */ - if ((can_forward && is_pair) || can_override) { - idr = flecs_query_id_record_get(world, id); + if ((can_forward && is_pair) || id_can_override) { + idr = flecs_id_record_get(world, id); + if (!idr) { + /* Possible for union ids */ + continue; + } + ecs_flags32_t idr_flags = idr->flags; if (is_pair && (idr_flags & EcsIdTraversable)) { const ecs_event_record_t *er_fwd = NULL; if (ECS_PAIR_FIRST(id) == EcsIsA) { if (event == EcsOnAdd) { - if (!world->stages[0].base) { + if (!world->stages[0]->base) { /* Adding an IsA relationship can trigger prefab * instantiation, which can instantiate prefab * hierarchies for the entity to which the @@ -15394,41 +13869,63 @@ void flecs_emit( /* Setting this value prevents flecs_instantiate * from being called recursively, in case prefab * children also have IsA relationships. */ - world->stages[0].base = tgt; - flecs_instantiate(world, tgt, table, offset, count); - world->stages[0].base = 0; + world->stages[0]->base = tgt; + flecs_instantiate(world, tgt, table, offset, count, NULL); + world->stages[0]->base = 0; } /* Adding an IsA relationship will emit OnSet events for * any new reachable components. */ er_fwd = er_onset; - } else if (event == EcsOnRemove) { - /* Vice versa for removing an IsA relationship. */ - er_fwd = er_unset; } } /* Forward events for components from pair target */ - flecs_emit_forward(world, er, er_fwd, ids, &it, table, idr, evtx); + flecs_emit_forward(world, er, er_fwd, ids, &it, table, idr); + ecs_assert(it.event_cur == evtx, ECS_INTERNAL_ERROR, NULL); } - if (can_override && (!(idr_flags & EcsIdDontInherit))) { + if (id_can_override && !(idr_flags & EcsIdOnInstantiateDontInherit)) { /* Initialize overridden components with value from base */ ti = idr->type_info; if (ti) { - ecs_table_record_t *base_tr = NULL; int32_t base_column = ecs_search_relation(world, table, 0, id, EcsIsA, EcsUp, &base, NULL, &base_tr); if (base_column != -1) { /* Base found with component */ ecs_table_t *base_table = base_tr->hdr.table; - base_column = base_tr->column; - ecs_assert(base_column != -1, ECS_INTERNAL_ERROR, NULL); - ecs_record_t *base_r = flecs_entities_get(world, base); - ecs_assert(base_r != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t base_row = ECS_RECORD_TO_ROW(base_r->row); - ecs_vec_t *base_v = &base_table->data.columns[base_column].data; - override_ptr = ecs_vec_get(base_v, ti->size, base_row); + if (idr->flags & EcsIdIsSparse) { + override_ptr = flecs_sparse_get_any( + idr->sparse, 0, base); + } else { + base_column = base_tr->column; + ecs_assert(base_column != -1, ECS_INTERNAL_ERROR, NULL); + ecs_record_t *base_r = flecs_entities_get(world, base); + ecs_assert(base_r != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t base_row = ECS_RECORD_TO_ROW(base_r->row); + override_ptr = base_table->data.columns[base_column].data; + override_ptr = ECS_ELEM(override_ptr, ti->size, base_row); + } + + /* For ids with override policy, check if base was added + * in same operation. This will determine later on + * whether we need to emit an OnSet event. */ + if (!(idr->flags & + (EcsIdOnInstantiateInherit|EcsIdOnInstantiateDontInherit))) { + int32_t base_i; + for (base_i = 0; base_i < id_count; base_i ++) { + ecs_id_t base_id = id_array[base_i]; + if (!ECS_IS_PAIR(base_id)) { + continue; + } + if (ECS_PAIR_FIRST(base_id) != EcsIsA) { + continue; + } + if (ECS_PAIR_SECOND(base_id) == (uint32_t)base) { + override_base_added = true; + } + } + } } } } @@ -15440,18 +13937,10 @@ void flecs_emit( * example, both observers for (ChildOf, p) and (ChildOf, *) would * match an event for (ChildOf, p). */ ider_count = flecs_event_observers_get(er, id, iders); - idr = idr ? idr : flecs_query_id_record_get(world, id); + idr = idr ? idr : flecs_id_record_get(world, id); ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); } - if (can_unset) { - /* Increase UnSet count in case this is a component (has data). This - * will cause the event loop to be ran again as UnSet event. */ - idr = idr ? idr : flecs_query_id_record_get(world, id); - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - unset_count += (idr->type_info != NULL); - } - if (!ider_count && !override_ptr) { /* If nothing more to do for this id, early out */ continue; @@ -15466,30 +13955,62 @@ void flecs_emit( continue; } - int32_t column = tr->index, storage_i; - it.columns[0] = column + 1; - it.ptrs[0] = NULL; - it.sizes[0] = 0; + int32_t storage_i; + it.trs[0] = tr; + ECS_CONST_CAST(int32_t*, it.sizes)[0] = 0; /* safe, owned by observer */ it.event_id = id; it.ids[0] = id; if (count) { storage_i = tr->column; - if (storage_i != -1) { - /* If this is a component, fetch pointer & size */ - ecs_assert(idr->type_info != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_column_t *c = &columns[storage_i]; - ecs_size_t size = c->ti->size; - void *ptr = ecs_vec_get(&c->data, size, offset); - it.sizes[0] = size; + bool is_sparse = idr->flags & EcsIdIsSparse; + + if (!ecs_id_is_wildcard(id) && (storage_i != -1 || is_sparse)) { + void *ptr; + ecs_size_t size = idr->type_info->size; + + if (is_sparse) { + ecs_assert(count == 1, ECS_UNSUPPORTED, + "events for multiple entities are currently unsupported" + " for sparse components"); + ecs_entity_t e = ecs_table_entities(table)[offset]; + ptr = flecs_sparse_get(idr->sparse, 0, e); + } else{ + ecs_assert(idr->type_info != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_column_t *c = &columns[storage_i]; + ptr = ECS_ELEM(c->data, size, offset); + } + + /* Safe, owned by observer */ + ECS_CONST_CAST(int32_t*, it.sizes)[0] = size; if (override_ptr) { if (event == EcsOnAdd) { /* If this is a new override, initialize the component * with the value of the overridden component. */ - flecs_override_copy( - world, table, ti, ptr, override_ptr, offset, count); - } else if (er_onset) { + flecs_override_copy(world, table, tr, ti, ptr, + override_ptr, offset, count); + + /* If the base for this component got added in the same + * operation, generate an OnSet event as this is the + * first time this value is observed for the entity. */ + if (override_base_added) { + ecs_event_id_record_t *iders_set[5] = {0}; + int32_t ider_set_i, ider_set_count = + flecs_event_observers_get(er_onset, id, iders_set); + for (ider_set_i = 0; ider_set_i < ider_set_count; ider_set_i ++) { + ecs_event_id_record_t *ider = iders_set[ider_set_i]; + flecs_observers_invoke( + world, &ider->self, &it, table, 0); + ecs_assert(it.event_cur == evtx, + ECS_INTERNAL_ERROR, NULL); + flecs_observers_invoke( + world, &ider->self_up, &it, table, 0); + ecs_assert(it.event_cur == evtx, + ECS_INTERNAL_ERROR, NULL); + } + } + } else if (er_onset && it.other_table) { /* If an override was removed, this re-exposes the * overridden component. Because this causes the actual * (now inherited) value of the component to change, an @@ -15502,31 +14023,36 @@ void flecs_emit( /* Set the source temporarily to the base and base * component pointer. */ it.sources[0] = base; - it.ptrs[0] = ptr; + it.trs[0] = base_tr; + it.up_fields = 1; + for (ider_set_i = 0; ider_set_i < ider_set_count; ider_set_i ++) { ecs_event_id_record_t *ider = iders_set[ider_set_i]; - flecs_observers_invoke(world, &ider->self_up, &it, table, EcsIsA, evtx); - flecs_observers_invoke(world, &ider->up, &it, table, EcsIsA, evtx); + flecs_observers_invoke( + world, &ider->self_up, &it, table, EcsIsA); + ecs_assert(it.event_cur == evtx, + ECS_INTERNAL_ERROR, NULL); + flecs_observers_invoke( + world, &ider->up, &it, table, EcsIsA); + ecs_assert(it.event_cur == evtx, + ECS_INTERNAL_ERROR, NULL); } + it.sources[0] = 0; + it.trs[0] = tr; } } } - - it.ptrs[0] = ptr; - } else { - if (it.event == EcsUnSet) { - /* Only valid for components, not tags */ - continue; - } } } /* Actually invoke observers for this event/id */ for (ider_i = 0; ider_i < ider_count; ider_i ++) { ecs_event_id_record_t *ider = iders[ider_i]; - flecs_observers_invoke(world, &ider->self, &it, table, 0, evtx); - flecs_observers_invoke(world, &ider->self_up, &it, table, 0, evtx); + flecs_observers_invoke(world, &ider->self, &it, table, 0); + ecs_assert(it.event_cur == evtx, ECS_INTERNAL_ERROR, NULL); + flecs_observers_invoke(world, &ider->self_up, &it, table, 0); + ecs_assert(it.event_cur == evtx, ECS_INTERNAL_ERROR, NULL); } if (!ider_count || !count || !has_observed) { @@ -15537,47 +14063,13 @@ void flecs_emit( * entities that are used as targets of traversable relationships. If the * entity/entities for which the event was generated is used as such a * target, events must be propagated downwards. */ - ecs_entity_t *entities = it.entities; - it.entities = NULL; - - for (r = 0; r < count; r ++) { - ecs_record_t *record = flecs_entities_get(world, entities[r]); - if (!record) { - /* If the event is emitted after a bulk operation, it's possible - * that it hasn't been populated with entities yet. */ - continue; - } - - ecs_id_record_t *idr_t = record->idr; - if (idr_t) { - /* Entity is used as target in traversable pairs, propagate */ - ecs_entity_t e = entities[r]; - it.sources[0] = e; - flecs_emit_propagate( - world, &it, idr, idr_t, 0, iders, ider_count); - } - } - - it.table = table; - it.other_table = other_table; - it.entities = entities; - it.count = count; - it.offset = offset; - it.sources[0] = 0; + flecs_propagate_entities( + world, &it, idr, it.entities, count, 0, iders, ider_count); } can_override = false; /* Don't override twice */ - can_unset = false; /* Don't unset twice */ can_forward = false; /* Don't forward twice */ - if (unset_count && er_unset && (er != er_unset)) { - /* Repeat event loop for UnSet event */ - unset_count = 0; - er = er_unset; - it.event = EcsUnSet; - goto repeat_event; - } - if (wcer && er != wcer) { /* Repeat event loop for Wildcard event */ er = wcer; @@ -15586,7 +14078,9 @@ void flecs_emit( } error: - world->stages[0].defer = defer; + world->stages[0]->defer = defer; + + ecs_os_perf_trace_pop("flecs.emit"); if (measure_time) { world->info.emit_time_total += (ecs_ftime_t)ecs_time_measure(&t); @@ -15636,7 +14130,9 @@ void ecs_emit( desc->const_param = NULL; } - flecs_emit(world, stage, desc); + ecs_defer_begin(world); + flecs_emit(world, stage, 0, desc); + ecs_defer_end(world); if (desc->ids == &default_ids) { desc->ids = NULL; @@ -15649,13 +14145,13 @@ void ecs_enqueue( ecs_world_t *world, ecs_event_desc_t *desc) { - ecs_check(ecs_is_deferred(world), ECS_INVALID_PARAMETER, - "can't enqueue if not in deferred mode"); - ecs_stage_t *stage = flecs_stage_from_world(&world); + if (!ecs_is_deferred(world)) { + ecs_emit(world, desc); + return; + } + ecs_stage_t *stage = flecs_stage_from_world(&world); flecs_enqueue(world, stage, desc); -error: - return; } /** @@ -15677,7 +14173,7 @@ ecs_entity_t flecs_get_observer_event( { /* If operator is Not, reverse the event */ if (term->oper == EcsNot) { - if (event == EcsOnAdd) { + if (event == EcsOnAdd || event == EcsOnSet) { event = EcsOnRemove; } else if (event == EcsOnRemove) { event = EcsOnAdd; @@ -15700,9 +14196,6 @@ ecs_flags32_t flecs_id_flag_for_event( if (e == EcsOnSet) { return EcsIdHasOnSet; } - if (e == EcsUnSet) { - return EcsIdHasUnSet; - } if (e == EcsOnTableFill) { return EcsIdHasOnTableFill; } @@ -15715,6 +14208,11 @@ ecs_flags32_t flecs_id_flag_for_event( if (e == EcsOnTableDelete) { return EcsIdHasOnTableDelete; } + if (e == EcsWildcard) { + return EcsIdHasOnAdd|EcsIdHasOnRemove|EcsIdHasOnSet| + EcsIdHasOnTableFill|EcsIdHasOnTableEmpty| + EcsIdHasOnTableCreate|EcsIdHasOnTableDelete; + } return 0; } @@ -15765,21 +14263,38 @@ void flecs_inc_observer_count( } } +static +ecs_id_t flecs_observer_id( + ecs_id_t id) +{ + if (ECS_IS_PAIR(id)) { + if (ECS_PAIR_FIRST(id) == EcsAny) { + id = ecs_pair(EcsWildcard, ECS_PAIR_SECOND(id)); + } + if (ECS_PAIR_SECOND(id) == EcsAny) { + id = ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard); + } + } + + return id; +} + static void flecs_register_observer_for_id( ecs_world_t *world, ecs_observable_t *observable, - ecs_observer_t *observer, + ecs_observer_t *o, size_t offset) { - ecs_id_t term_id = observer->register_id; - ecs_term_t *term = &observer->filter.terms[0]; - ecs_entity_t trav = term->src.trav; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_id_t term_id = flecs_observer_id(impl->register_id); + ecs_term_t *term = &o->query->terms[0]; + ecs_entity_t trav = term->trav; int i; - for (i = 0; i < observer->event_count; i ++) { + for (i = 0; i < o->event_count; i ++) { ecs_entity_t event = flecs_get_observer_event( - term, observer->events[i]); + term, o->events[i]); /* Get observers for event */ ecs_event_record_t *er = flecs_event_record_ensure(observable, event); @@ -15792,7 +14307,7 @@ void flecs_register_observer_for_id( ecs_map_t *observers = ECS_OFFSET(idt, offset); ecs_map_init_w_params_if(observers, &world->allocators.ptr); - ecs_map_insert_ptr(observers, observer->filter.entity, observer); + ecs_map_insert_ptr(observers, impl->id, o); flecs_inc_observer_count(world, event, er, term_id, 1); if (trav) { @@ -15806,20 +14321,20 @@ static void flecs_uni_observer_register( ecs_world_t *world, ecs_observable_t *observable, - ecs_observer_t *observer) + ecs_observer_t *o) { - ecs_term_t *term = &observer->filter.terms[0]; - ecs_flags32_t flags = term->src.flags; + ecs_term_t *term = &o->query->terms[0]; + ecs_flags64_t flags = ECS_TERM_REF_FLAGS(&term->src); if ((flags & (EcsSelf|EcsUp)) == (EcsSelf|EcsUp)) { - flecs_register_observer_for_id(world, observable, observer, + flecs_register_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, self_up)); } else if (flags & EcsSelf) { - flecs_register_observer_for_id(world, observable, observer, + flecs_register_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, self)); } else if (flags & EcsUp) { - ecs_assert(term->src.trav != 0, ECS_INTERNAL_ERROR, NULL); - flecs_register_observer_for_id(world, observable, observer, + ecs_assert(term->trav != 0, ECS_INTERNAL_ERROR, NULL); + flecs_register_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, up)); } } @@ -15828,17 +14343,18 @@ static void flecs_unregister_observer_for_id( ecs_world_t *world, ecs_observable_t *observable, - ecs_observer_t *observer, + ecs_observer_t *o, size_t offset) { - ecs_id_t term_id = observer->register_id; - ecs_term_t *term = &observer->filter.terms[0]; - ecs_entity_t trav = term->src.trav; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_id_t term_id = flecs_observer_id(impl->register_id); + ecs_term_t *term = &o->query->terms[0]; + ecs_entity_t trav = term->trav; int i; - for (i = 0; i < observer->event_count; i ++) { + for (i = 0; i < o->event_count; i ++) { ecs_entity_t event = flecs_get_observer_event( - term, observer->events[i]); + term, o->events[i]); /* Get observers for event */ ecs_event_record_t *er = flecs_event_record_get(observable, event); @@ -15849,7 +14365,7 @@ void flecs_unregister_observer_for_id( ecs_assert(idt != NULL, ECS_INTERNAL_ERROR, NULL); ecs_map_t *id_observers = ECS_OFFSET(idt, offset); - ecs_map_remove(id_observers, observer->filter.entity); + ecs_map_remove(id_observers, impl->id); if (!ecs_map_count(id_observers)) { ecs_map_fini(id_observers); } @@ -15866,101 +14382,101 @@ static void flecs_unregister_observer( ecs_world_t *world, ecs_observable_t *observable, - ecs_observer_t *observer) + ecs_observer_t *o) { - ecs_assert(observer != NULL, ECS_INTERNAL_ERROR, NULL); - if (!observer->filter.terms) { - ecs_assert(observer->filter.term_count == 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(o != NULL, ECS_INTERNAL_ERROR, NULL); + if (o->query->term_count == 0) { return; } - ecs_term_t *term = &observer->filter.terms[0]; - ecs_flags32_t flags = term->src.flags; + ecs_term_t *term = &o->query->terms[0]; + ecs_flags64_t flags = ECS_TERM_REF_FLAGS(&term->src); if ((flags & (EcsSelf|EcsUp)) == (EcsSelf|EcsUp)) { - flecs_unregister_observer_for_id(world, observable, observer, + flecs_unregister_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, self_up)); } else if (flags & EcsSelf) { - flecs_unregister_observer_for_id(world, observable, observer, + flecs_unregister_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, self)); } else if (flags & EcsUp) { - flecs_unregister_observer_for_id(world, observable, observer, + flecs_unregister_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, up)); } } static bool flecs_ignore_observer( - ecs_observer_t *observer, + ecs_observer_t *o, ecs_table_t *table, - int32_t evtx) + ecs_iter_t *it) { - ecs_assert(observer != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(o != 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] == evtx) { + ecs_observer_impl_t *impl = flecs_observer_impl(o); + int32_t *last_event_id = impl->last_event_id; + if (last_event_id && last_event_id[0] == it->event_cur) { return true; } - ecs_flags32_t table_flags = table->flags, filter_flags = observer->filter.flags; + if (impl->flags & (EcsObserverIsDisabled|EcsObserverIsParentDisabled)) { + return true; + } + + ecs_flags32_t table_flags = table->flags, query_flags = o->query->flags; bool result = (table_flags & EcsTableIsPrefab) && - !(filter_flags & EcsFilterMatchPrefab); + !(query_flags & EcsQueryMatchPrefab); result = result || ((table_flags & EcsTableIsDisabled) && - !(filter_flags & EcsFilterMatchDisabled)); + !(query_flags & EcsQueryMatchDisabled)); 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 void flecs_observer_invoke( ecs_world_t *world, ecs_iter_t *it, - ecs_observer_t *observer, + ecs_observer_t *o, ecs_iter_action_t callback, - int32_t term_index, - bool simple_result) + int32_t term_index) { ecs_assert(it->callback != NULL, ECS_INVALID_PARAMETER, NULL); if (ecs_should_log_3()) { - char *path = ecs_get_fullpath(world, it->system); + char *path = ecs_get_path(world, it->system); ecs_dbg_3("observer: invoke %s", path); ecs_os_free(path); } ecs_log_push_3(); + ecs_entity_t old_system = flecs_stage_set_system( + world->stages[0], o->entity); world->info.observers_ran_frame ++; - 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]; - if (term->oper != EcsNot) { + ecs_query_t *query = o->query; + ecs_assert(term_index < query->term_count, ECS_INTERNAL_ERROR, NULL); + ecs_term_t *term = &query->terms[term_index]; + if (it->table && (term->oper != EcsNot)) { ecs_assert((it->offset + it->count) <= ecs_table_count(it->table), ECS_INTERNAL_ERROR, NULL); } - bool instanced = filter->flags & EcsFilterIsInstanced; - bool match_this = filter->flags & EcsFilterMatchThis; - bool table_only = it->flags & EcsIterTableOnly; - if (match_this && (simple_result || instanced || table_only)) { + ecs_termset_t row_fields = it->row_fields; + it->row_fields = query->row_fields; + + bool match_this = query->flags & EcsQueryMatchThis; + if (match_this) { callback(it); + ecs_os_inc(&query->eval_count); } else { - ecs_entity_t observer_src = term->src.id; - if (observer_src && !(term->src.flags & EcsIsEntity)) { + ecs_entity_t observer_src = ECS_TERM_REF_ID(&term->src); + if (observer_src && !(term->src.id & EcsIsEntity)) { observer_src = 0; } - ecs_entity_t *entities = it->entities; + const ecs_entity_t *entities = it->entities; int32_t i, count = it->count; ecs_entity_t src = it->sources[0]; it->count = 1; @@ -15969,21 +14485,29 @@ void flecs_observer_invoke( it->entities = &e; if (!observer_src) { callback(it); + ecs_os_inc(&query->eval_count); } else if (observer_src == e) { ecs_entity_t dummy = 0; it->entities = &dummy; if (!src) { it->sources[0] = e; } + callback(it); + ecs_os_inc(&query->eval_count); it->sources[0] = src; break; } } + it->entities = entities; it->count = count; } + it->row_fields = row_fields; + + flecs_stage_set_system(world->stages[0], old_system); + ecs_log_pop_3(); } @@ -15994,61 +14518,64 @@ void flecs_default_uni_observer_run_callback(ecs_iter_t *it) { it->callback = o->callback; if (ecs_should_log_3()) { - char *path = ecs_get_fullpath(it->world, it->system); + char *path = ecs_get_path(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)); + flecs_observer_invoke(it->real_world, it, o, o->callback, 0); ecs_log_pop_3(); } static void flecs_uni_observer_invoke( ecs_world_t *world, - ecs_observer_t *observer, + ecs_observer_t *o, ecs_iter_t *it, ecs_table_t *table, - ecs_entity_t trav, - int32_t evtx, - bool simple_result) + ecs_entity_t trav) { - ecs_filter_t *filter = &observer->filter; - ecs_term_t *term = &filter->terms[0]; - if (flecs_ignore_observer(observer, table, evtx)) { + ecs_query_t *query = o->query; + ecs_term_t *term = &query->terms[0]; + if (flecs_ignore_observer(o, table, it)) { return; } ecs_assert(trav == 0 || it->sources[0] != 0, ECS_INTERNAL_ERROR, NULL); - if (trav && term->src.trav != trav) { + if (trav && term->trav != trav) { return; } + ecs_observer_impl_t *impl = flecs_observer_impl(o); bool is_filter = term->inout == EcsInOutNone; ECS_BIT_COND(it->flags, EcsIterNoData, is_filter); - it->system = observer->filter.entity; - it->ctx = observer->ctx; - it->binding_ctx = observer->binding_ctx; - it->term_index = observer->term_index; - it->terms = term; - + it->system = o->entity; + it->ctx = o->ctx; + it->callback_ctx = o->callback_ctx; + it->run_ctx = o->run_ctx; + it->term_index = impl->term_index; + it->query = query; + it->ref_fields = query->fixed_fields | query->row_fields; + it->row_fields = query->row_fields; + ecs_entity_t event = it->event; + int32_t event_cur = it->event_cur; it->event = flecs_get_observer_event(term, event); - if (observer->run) { - it->next = flecs_default_observer_next_callback; + if (o->run) { + it->next = flecs_default_next_callback; it->callback = flecs_default_uni_observer_run_callback; - it->ctx = observer; - observer->run(it); + it->ctx = o; + o->run(it); } else { - ecs_iter_action_t callback = observer->callback; + ecs_iter_action_t callback = o->callback; it->callback = callback; - flecs_observer_invoke(world, it, observer, callback, 0, simple_result); + flecs_observer_invoke(world, it, o, callback, 0); } it->event = event; + it->event_cur = event_cur; } void flecs_observers_invoke( @@ -16056,18 +14583,16 @@ void flecs_observers_invoke( ecs_map_t *observers, ecs_iter_t *it, ecs_table_t *table, - ecs_entity_t trav, - int32_t evtx) + 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, evtx, simple_result); + flecs_uni_observer_invoke(world, o, it, table, trav); } ecs_table_unlock(it->world, table); @@ -16075,135 +14600,163 @@ void flecs_observers_invoke( } static -bool flecs_multi_observer_invoke(ecs_iter_t *it) { +void flecs_multi_observer_invoke( + ecs_iter_t *it) +{ ecs_observer_t *o = it->ctx; - ecs_world_t *world = it->real_world; + flecs_poly_assert(o, ecs_observer_t); - if (o->last_event_id[0] == world->event_id) { + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_world_t *world = it->real_world; + + if (impl->last_event_id[0] == it->event_cur) { /* Already handled this event */ - return false; + return; } - o->last_event_id[0] = world->event_id; - - ecs_iter_t user_it = *it; - user_it.field_count = o->filter.field_count; - user_it.terms = o->filter.terms; - user_it.flags = 0; - ECS_BIT_COND(user_it.flags, EcsIterNoData, - ECS_BIT_IS_SET(o->filter.flags, EcsFilterNoData)); - user_it.ids = NULL; - user_it.columns = NULL; - user_it.sources = NULL; - user_it.sizes = NULL; - user_it.ptrs = NULL; - - flecs_iter_init(it->world, &user_it, flecs_iter_cache_all); - user_it.flags |= (it->flags & EcsIterTableOnly); + impl->last_event_id[0] = world->event_id; ecs_table_t *table = it->table; ecs_table_t *prev_table = it->other_table; - int32_t pivot_term = it->term_index; - ecs_term_t *term = &o->filter.terms[pivot_term]; + int8_t pivot_term = it->term_index; + ecs_term_t *term = &o->query->terms[pivot_term]; - int32_t column = it->columns[0]; - if (term->oper == EcsNot) { + bool is_not = term->oper == EcsNot; + if (is_not) { table = it->other_table; prev_table = it->table; } - if (!table) { - table = &world->store.root; - } - if (!prev_table) { - prev_table = &world->store.root; - } + table = table ? table : &world->store.root; + prev_table = prev_table ? prev_table : &world->store.root; - if (column < 0) { - column = -column; - } + ecs_iter_t user_it; + + bool match; + if (is_not) { + match = ecs_query_has_table(o->query, table, &user_it); + if (match) { + /* The target table matches but the entity hasn't moved to it yet. + * Now match the not_query, which will populate the iterator with + * data from the table the entity is still stored in. */ + ecs_iter_fini(&user_it); + match = ecs_query_has_table(impl->not_query, prev_table, &user_it); + + /* A not query replaces Not terms with Optional terms, so if the + * regular query matches, the not_query should also match. */ + ecs_assert(match, ECS_INTERNAL_ERROR, NULL); + } + } else { + ecs_table_range_t range = { + .table = table, + .offset = it->offset, + .count = it->count + }; - user_it.columns[0] = 0; - user_it.columns[pivot_term] = column; - user_it.sources[pivot_term] = it->sources[0]; - user_it.sizes = o->filter.sizes; + match = ecs_query_has_range(o->query, &range, &user_it); + } - if (flecs_filter_match_table(world, &o->filter, table, user_it.ids, - user_it.columns, user_it.sources, NULL, NULL, false, pivot_term, - user_it.flags)) - { - /* Monitor observers only invoke when the filter matches for the first + if (match) { + /* Monitor observers only invoke when the query matches for the first * time with an entity */ - if (o->is_monitor) { - if (flecs_filter_match_table(world, &o->filter, prev_table, - NULL, NULL, NULL, NULL, NULL, true, -1, user_it.flags)) - { + if (impl->flags & EcsObserverIsMonitor) { + ecs_iter_t table_it; + if (ecs_query_has_table(o->query, prev_table, &table_it)) { + ecs_iter_fini(&table_it); + ecs_iter_fini(&user_it); goto done; } } - /* While filter matching needs to be reversed for a Not term, the - * component data must be fetched from the table we got notified for. - * Repeat the matching process for the non-matching table so we get the - * correct column ids and sources, which we need for populate_data */ - if (term->oper == EcsNot) { - flecs_filter_match_table(world, &o->filter, prev_table, user_it.ids, - user_it.columns, user_it.sources, NULL, NULL, false, -1, - user_it.flags | EcsFilterPopulate); - } - - flecs_iter_populate_data(world, &user_it, it->table, it->offset, - it->count, user_it.ptrs); - - user_it.ptrs[pivot_term] = it->ptrs[0]; - user_it.ids[pivot_term] = it->event_id; - user_it.system = o->filter.entity; + /* Patch data from original iterator. If the observer query has + * wildcards which triggered the original event, the component id that + * got matched by ecs_query_has_range may not be the same as the one + * that caused the event. We need to make sure to communicate the + * component id that actually triggered the observer. */ + int8_t pivot_field = term->field_index; + ecs_assert(pivot_field >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(pivot_field < user_it.field_count, ECS_INTERNAL_ERROR, NULL); + user_it.ids[pivot_field] = it->event_id; + user_it.trs[pivot_field] = it->trs[0]; user_it.term_index = pivot_term; + user_it.ctx = o->ctx; - user_it.binding_ctx = o->binding_ctx; - user_it.field_count = o->filter.field_count; + user_it.callback_ctx = o->callback_ctx; + user_it.run_ctx = o->run_ctx; + user_it.param = it->param; user_it.callback = o->callback; - - flecs_iter_validate(&user_it); + user_it.system = o->entity; + user_it.event = it->event; + user_it.event_id = it->event_id; + user_it.other_table = it->other_table; + + ecs_entity_t old_system = flecs_stage_set_system( + world->stages[0], o->entity); 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); + + if (o->run) { + user_it.next = flecs_default_next_callback; + o->run(&user_it); + } else { + user_it.callback(&user_it); + } + ecs_iter_fini(&user_it); - return true; + + ecs_table_unlock(it->world, table); + flecs_stage_set_system(world->stages[0], old_system); + } else { + /* While the observer query was strictly speaking evaluated, it's more + * useful to measure how often the observer was actually invoked. */ + o->query->eval_count --; } done: - ecs_iter_fini(&user_it); - return false; + return; } -bool ecs_observer_default_run_action(ecs_iter_t *it) { +static +void flecs_multi_observer_invoke_no_query( + ecs_iter_t *it) +{ ecs_observer_t *o = it->ctx; - if (o->is_multi) { - return flecs_multi_observer_invoke(it); + flecs_poly_assert(o, ecs_observer_t); + + ecs_world_t *world = it->real_world; + ecs_table_t *table = it->table; + ecs_iter_t user_it = *it; + + user_it.ctx = o->ctx; + user_it.callback_ctx = o->callback_ctx; + user_it.run_ctx = o->run_ctx; + user_it.param = it->param; + user_it.callback = o->callback; + user_it.system = o->entity; + user_it.event = it->event; + + ecs_entity_t old_system = flecs_stage_set_system( + world->stages[0], o->entity); + ecs_table_lock(it->world, table); + + if (o->run) { + user_it.next = flecs_default_next_callback; + o->run(&user_it); } else { - it->ctx = o->ctx; - 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; + user_it.callback(&user_it); } -} -static -void flecs_default_multi_observer_run_callback(ecs_iter_t *it) { - flecs_multi_observer_invoke(it); + ecs_table_unlock(it->world, table); + flecs_stage_set_system(world->stages[0], old_system); } /* 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) { +bool flecs_default_next_callback(ecs_iter_t *it) { if (it->interrupted_by) { return false; } else { /* Use interrupted_by to signal the next iteration must return false */ + ecs_assert(it->system != 0, ECS_INTERNAL_ERROR, NULL); it->interrupted_by = it->system; return true; } @@ -16212,101 +14765,71 @@ bool flecs_default_observer_next_callback(ecs_iter_t *it) { /* Run action for children of multi observer */ static void flecs_multi_observer_builtin_run(ecs_iter_t *it) { - ecs_observer_t *observer = it->ctx; - ecs_run_action_t run = observer->run; + ecs_observer_t *o = it->ctx; + ecs_run_action_t run = o->run; if (run) { - it->next = flecs_default_observer_next_callback; - it->callback = flecs_default_multi_observer_run_callback; - it->interrupted_by = 0; - run(it); - } else { - flecs_multi_observer_invoke(it); - } -} - -static -void flecs_uni_observer_yield_existing( - ecs_world_t *world, - ecs_observer_t *observer) -{ - ecs_iter_action_t callback = observer->callback; - - ecs_defer_begin(world); - - /* If yield existing is enabled, observer for each thing that matches - * the event, if the event is iterable. */ - int i, count = observer->event_count; - for (i = 0; i < count; i ++) { - ecs_entity_t evt = observer->events[i]; - const EcsIterable *iterable = ecs_get(world, evt, EcsIterable); - if (!iterable) { - continue; - } - - ecs_iter_t it; - iterable->init(world, world, &it, &observer->filter.terms[0]); - it.system = observer->filter.entity; - it.ctx = observer->ctx; - it.binding_ctx = observer->binding_ctx; - it.event = evt; - - ecs_iter_next_action_t next = it.next; - ecs_assert(next != NULL, ECS_INTERNAL_ERROR, NULL); - while (next(&it)) { - it.event_id = it.ids[0]; - callback(&it); + if (flecs_observer_impl(o)->flags & EcsObserverBypassQuery) { + it->next = flecs_default_next_callback; + it->callback = flecs_multi_observer_invoke; + it->interrupted_by = 0; + it->run_ctx = o->run_ctx; + run(it); + return; } } - ecs_defer_end(world); + flecs_multi_observer_invoke(it); } static -void flecs_multi_observer_yield_existing( +void flecs_observer_yield_existing( ecs_world_t *world, - ecs_observer_t *observer) + ecs_observer_t *o, + bool yield_on_remove) { - ecs_run_action_t run = observer->run; + ecs_run_action_t run = o->run; if (!run) { - run = flecs_default_multi_observer_run_callback; + run = flecs_multi_observer_invoke_no_query; } ecs_run_aperiodic(world, EcsAperiodicEmptyTables); ecs_defer_begin(world); - int32_t pivot_term = ecs_filter_pivot_term(world, &observer->filter); - if (pivot_term < 0) { - return; - } - /* If yield existing is enabled, invoke for each thing that matches * the event, if the event is iterable. */ - int i, count = observer->event_count; + int i, count = o->event_count; for (i = 0; i < count; i ++) { - ecs_entity_t evt = observer->events[i]; - const EcsIterable *iterable = ecs_get(world, evt, EcsIterable); - if (!iterable) { - continue; - } + ecs_entity_t event = o->events[i]; - ecs_iter_t it; - iterable->init(world, world, &it, &observer->filter.terms[pivot_term]); - it.terms = observer->filter.terms; - it.field_count = 1; - it.term_index = pivot_term; - it.system = observer->filter.entity; - it.ctx = observer; - it.binding_ctx = observer->binding_ctx; - it.event = evt; + /* We only yield for OnRemove events if the observer is deleted. */ + if (event == EcsOnRemove) { + if (!yield_on_remove) { + continue; + } + } else { + if (yield_on_remove) { + continue; + } + } - ecs_iter_next_action_t next = it.next; - ecs_assert(next != NULL, ECS_INTERNAL_ERROR, NULL); - while (next(&it)) { + ecs_iter_t it = ecs_query_iter(world, o->query); + it.system = o->entity; + it.ctx = o; + it.callback = flecs_default_uni_observer_run_callback; + it.callback_ctx = o->callback_ctx; + it.run_ctx = o->run_ctx; + it.event = o->events[i]; + while (ecs_query_next(&it)) { it.event_id = it.ids[0]; + it.event_cur = ++ world->event_id; + + ecs_iter_next_action_t next = it.next; + it.next = flecs_default_next_callback; run(&it); - world->event_id ++; + it.next = next; + it.interrupted_by = 0; } } @@ -16316,106 +14839,183 @@ void flecs_multi_observer_yield_existing( static int flecs_uni_observer_init( ecs_world_t *world, - ecs_observer_t *observer, + ecs_observer_t *o, const ecs_observer_desc_t *desc) { - 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; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_term_t *term = &o->query->terms[0]; + impl->last_event_id = desc->last_event_id; + if (!impl->last_event_id) { + impl->last_event_id = &impl->last_event_id_storage; } - observer->register_id = flecs_from_public_id(world, term->id); - term->field_index = desc->term_index; + impl->register_id = term->id; + term->field_index = flecs_ito(int8_t, desc->term_index_); if (ecs_id_is_tag(world, term->id)) { - /* If id is a tag, downgrade OnSet/UnSet to OnAdd/OnRemove. */ - int32_t e, count = observer->event_count; + /* If id is a tag, downgrade OnSet to OnAdd. */ + int32_t e, count = o->event_count; + bool has_on_add = false; + for (e = 0; e < count; e ++) { + if (o->events[e] == EcsOnAdd) { + has_on_add = true; + } + } + for (e = 0; e < count; e ++) { - if (observer->events[e] == EcsOnSet) { - observer->events[e] = EcsOnAdd; - } else - if (observer->events[e] == EcsUnSet) { - observer->events[e] = EcsOnRemove; + if (o->events[e] == EcsOnSet) { + if (has_on_add) { + /* Already registered */ + o->events[e] = 0; + } else { + o->events[e] = EcsOnAdd; + } } } } - flecs_uni_observer_register(world, observer->observable, observer); + flecs_uni_observer_register(world, o->observable, o); + + return 0; +} + +static +int flecs_observer_add_child( + ecs_world_t *world, + ecs_observer_t *o, + const ecs_observer_desc_t *child_desc) +{ + ecs_assert(child_desc->query.flags & EcsQueryNested, + ECS_INTERNAL_ERROR, NULL); - if (desc->yield_existing) { - flecs_uni_observer_yield_existing(world, observer); + ecs_observer_t *child_observer = flecs_observer_init( + world, 0, child_desc); + if (!child_observer) { + return -1; } + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_vec_append_t(&world->allocator, &impl->children, + ecs_observer_t*)[0] = child_observer; + child_observer->entity = o->entity; return 0; } static int flecs_multi_observer_init( ecs_world_t *world, - ecs_observer_t *observer, + ecs_observer_t *o, const ecs_observer_desc_t *desc) { + ecs_observer_impl_t *impl = flecs_observer_impl(o); + /* Create last event id for filtering out the same event that arrives from * more than one term */ - observer->last_event_id = ecs_os_calloc_t(int32_t); + impl->last_event_id = ecs_os_calloc_t(int32_t); /* Mark observer as multi observer */ - observer->is_multi = true; + impl->flags |= EcsObserverIsMulti; + + /* Vector that stores a single-component observer for each query term */ + ecs_vec_init_t(&world->allocator, &impl->children, ecs_observer_t*, 2); - /* Create a child observer for each term in the filter */ - ecs_filter_t *filter = &observer->filter; + /* Create a child observer for each term in the query */ + ecs_query_t *query = o->query; ecs_observer_desc_t child_desc = *desc; - child_desc.last_event_id = observer->last_event_id; + child_desc.last_event_id = impl->last_event_id; child_desc.run = NULL; child_desc.callback = flecs_multi_observer_builtin_run; - child_desc.ctx = observer; + child_desc.ctx = o; child_desc.ctx_free = NULL; - child_desc.filter.expr = NULL; - child_desc.filter.terms_buffer = NULL; - child_desc.filter.terms_buffer_count = 0; - child_desc.binding_ctx = NULL; - child_desc.binding_ctx_free = NULL; + child_desc.query.expr = NULL; + child_desc.callback_ctx = NULL; + child_desc.callback_ctx_free = NULL; + child_desc.run_ctx = NULL; + child_desc.run_ctx_free = NULL; child_desc.yield_existing = false; + child_desc.flags_ &= ~(EcsObserverYieldOnCreate|EcsObserverYieldOnDelete); ecs_os_zeromem(&child_desc.entity); - ecs_os_zeromem(&child_desc.filter.terms); - ecs_os_memcpy_n(child_desc.events, observer->events, - ecs_entity_t, observer->event_count); + ecs_os_zeromem(&child_desc.query.terms); + ecs_os_zeromem(&child_desc.query); + ecs_os_memcpy_n(child_desc.events, o->events, ecs_entity_t, o->event_count); - int i, term_count = filter->term_count; - bool optional_only = filter->flags & EcsFilterMatchThis; + child_desc.query.flags |= EcsQueryNested; + + int i, term_count = query->term_count; + bool optional_only = query->flags & EcsQueryMatchThis; + bool has_not = false; for (i = 0; i < term_count; i ++) { - if (filter->terms[i].oper != EcsOptional) { - if (ecs_term_match_this(&filter->terms[i])) { + if (query->terms[i].oper != EcsOptional) { + if (ecs_term_match_this(&query->terms[i])) { optional_only = false; } } + + if ((query->terms[i].oper == EcsNot) && + (query->terms[i].inout != EcsInOutFilter)) + { + has_not = true; + } } - if (filter->flags & EcsFilterMatchPrefab) { - child_desc.filter.flags |= EcsFilterMatchPrefab; + /* If an observer is only interested in table events, we only need to + * observe a single component, as each table event will be emitted for all + * components of the source table. */ + bool only_table_events = true; + for (i = 0; i < o->event_count; i ++) { + ecs_entity_t e = o->events[i]; + if (e != EcsOnTableCreate && e != EcsOnTableDelete && + e != EcsOnTableEmpty && e != EcsOnTableFill) + { + only_table_events = false; + break; + } } - if (filter->flags & EcsFilterMatchDisabled) { - child_desc.filter.flags |= EcsFilterMatchDisabled; + + if (query->flags & EcsQueryMatchPrefab) { + child_desc.query.flags |= EcsQueryMatchPrefab; } - /* Create observers as children of observer */ - ecs_entity_t old_scope = ecs_set_scope(world, observer->filter.entity); + if (query->flags & EcsQueryMatchDisabled) { + child_desc.query.flags |= EcsQueryMatchDisabled; + } + bool self_term_handled = false; for (i = 0; i < term_count; i ++) { - if (filter->terms[i].src.flags & EcsFilter) { + if (query->terms[i].inout == EcsInOutFilter) { continue; } - ecs_term_t *term = &child_desc.filter.terms[0]; - child_desc.term_index = filter->terms[i].field_index; - *term = filter->terms[i]; + ecs_term_t *term = &child_desc.query.terms[0]; + child_desc.term_index_ = query->terms[i].field_index; + *term = query->terms[i]; - ecs_oper_kind_t oper = term->oper; + int16_t oper = term->oper; ecs_id_t id = term->id; + if (only_table_events) { + /* For table event observers, only observe a single $this|self + * term. Make sure to create observers for non-self terms, as those + * require event propagation. */ + if (ecs_term_match_this(term) && + (term->src.id & EcsTraverseFlags) == EcsSelf) + { + if (oper == EcsAnd) { + if (!self_term_handled) { + self_term_handled = true; + } else { + continue; + } + } + } + } + /* AndFrom & OrFrom terms insert multiple observers */ if (oper == EcsAndFrom || oper == EcsOrFrom) { const ecs_type_t *type = ecs_get_type(world, id); + if (!type) { + continue; + } + int32_t ti, ti_count = type->count; ecs_id_t *ti_ids = type->array; @@ -16425,7 +15025,7 @@ int flecs_multi_observer_init( for (ti = 0; ti < ti_count; ti ++) { ecs_id_t ti_id = ti_ids[ti]; ecs_id_record_t *idr = flecs_id_record_get(world, ti_id); - if (idr->flags & EcsIdDontInherit) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { continue; } @@ -16433,7 +15033,7 @@ int flecs_multi_observer_init( term->first.id = ti_ids[ti]; term->id = ti_ids[ti]; - if (ecs_observer_init(world, &child_desc) == 0) { + if (flecs_observer_add_child(world, o, &child_desc)) { goto error; } } @@ -16449,13 +15049,16 @@ int flecs_multi_observer_init( if (optional_only) { term->id = EcsAny; term->first.id = EcsAny; - term->src.id = EcsThis; - term->src.flags = EcsIsVariable; + term->src.id = EcsThis | EcsIsVariable | EcsSelf; term->second.id = 0; } else if (term->oper == EcsOptional) { - continue; + if (only_table_events) { + /* For table events optional terms aren't necessary */ + continue; + } } - if (ecs_observer_init(world, &child_desc) == 0) { + + if (flecs_observer_add_child(world, o, &child_desc)) { goto error; } @@ -16464,10 +15067,25 @@ int flecs_multi_observer_init( } } - ecs_set_scope(world, old_scope); + /* If observer has Not terms, we need to create a query that replaces Not + * with Optional which we can use to populate the observer data for the + * table that the entity moved away from (or to, if it's an OnRemove + * observer). */ + if (has_not) { + ecs_query_desc_t not_desc = desc->query; + not_desc.expr = NULL; + + ecs_os_memcpy_n(not_desc.terms, o->query->terms, + ecs_term_t, term_count); /* cast suppresses warning */ + + for (i = 0; i < term_count; i ++) { + if (not_desc.terms[i].oper == EcsNot) { + not_desc.terms[i].oper = EcsOptional; + } + } - if (desc->yield_existing) { - flecs_multi_observer_yield_existing(world, observer); + flecs_observer_impl(o)->not_query = + ecs_query_init(world, ¬_desc); } return 0; @@ -16475,214 +15093,335 @@ int flecs_multi_observer_init( return -1; } -ecs_entity_t ecs_observer_init( +static +void flecs_observer_poly_fini(void *ptr) { + flecs_observer_fini(ptr); +} + +ecs_observer_t* flecs_observer_init( ecs_world_t *world, + ecs_entity_t entity, const ecs_observer_desc_t *desc) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, NULL); - - ecs_entity_t entity = desc->entity; - if (!entity) { - entity = ecs_new(world, 0); + ecs_assert(flecs_poly_is(world, ecs_world_t), + ECS_INTERNAL_ERROR, NULL); + ecs_check(desc->callback != NULL || desc->run != NULL, + ECS_INVALID_OPERATION, + "cannot create observer: must at least specify callback or run"); + + ecs_observer_impl_t *impl = flecs_sparse_add_t( + &world->store.observers, ecs_observer_impl_t); + ecs_assert(impl != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_init(impl, ecs_observer_t); + ecs_observer_t *o = &impl->pub; + impl->id = flecs_sparse_last_id(&world->store.observers); + impl->dtor = flecs_observer_poly_fini; + + /* Make writeable copy of query desc so that we can set name. This will + * make debugging easier, as any error messages related to creating the + * query will have the name of the observer. */ + ecs_query_desc_t query_desc = desc->query; + query_desc.entity = 0; + query_desc.cache_kind = EcsQueryCacheNone; + + /* Create query */ + ecs_query_t *query = o->query = ecs_query_init( + world, &query_desc); + if (query == NULL) { + flecs_observer_fini(o); + return 0; } - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_observer_t); - if (!poly->poly) { - ecs_check(desc->callback != NULL || desc->run != NULL, - ECS_INVALID_OPERATION, NULL); - - ecs_observer_t *observer = ecs_poly_new(ecs_observer_t); - ecs_assert(observer != NULL, ECS_INTERNAL_ERROR, NULL); - observer->dtor = (ecs_poly_dtor_t)flecs_observer_fini; - - /* Make writeable copy of filter desc so that we can set name. This will - * make debugging easier, as any error messages related to creating the - * filter will have the name of the observer. */ - ecs_filter_desc_t filter_desc = desc->filter; - filter_desc.entity = entity; - ecs_filter_t *filter = filter_desc.storage = &observer->filter; - *filter = ECS_FILTER_INIT; - - /* Parse filter */ - if (ecs_filter_init(world, &filter_desc) == NULL) { - flecs_observer_fini(observer); - return 0; - } + flecs_poly_assert(query, ecs_query_t); - /* Observer must have at least one term */ - ecs_check(observer->filter.term_count > 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(o->query->term_count > 0, ECS_INVALID_PARAMETER, + "observer must have at least one term"); - poly->poly = observer; + ecs_observable_t *observable = desc->observable; + if (!observable) { + observable = ecs_get_observable(world); + } + + o->run = desc->run; + o->callback = desc->callback; + o->ctx = desc->ctx; + o->callback_ctx = desc->callback_ctx; + o->run_ctx = desc->run_ctx; + o->ctx_free = desc->ctx_free; + o->callback_ctx_free = desc->callback_ctx_free; + o->run_ctx_free = desc->run_ctx_free; + o->observable = observable; + o->entity = entity; + impl->term_index = desc->term_index_; + impl->flags = desc->flags_; - ecs_observable_t *observable = desc->observable; - if (!observable) { - observable = ecs_get_observable(world); + ecs_check(!(desc->yield_existing && + (desc->flags_ & (EcsObserverYieldOnCreate|EcsObserverYieldOnDelete))), + ECS_INVALID_PARAMETER, + "cannot set yield_existing and YieldOn* flags at the same time"); + + /* Check if observer is monitor. Monitors are created as multi observers + * since they require pre/post checking of the filter to test if the + * entity is entering/leaving the monitor. */ + int i; + for (i = 0; i < FLECS_EVENT_DESC_MAX; i ++) { + ecs_entity_t event = desc->events[i]; + if (!event) { + break; } - observer->run = desc->run; - observer->callback = desc->callback; - observer->ctx = desc->ctx; - observer->binding_ctx = desc->binding_ctx; - observer->ctx_free = desc->ctx_free; - observer->binding_ctx_free = desc->binding_ctx_free; - observer->term_index = desc->term_index; - observer->observable = observable; + if (event == EcsMonitor) { + ecs_check(i == 0, ECS_INVALID_PARAMETER, + "monitor observers can only have a single Monitor event"); - /* Check if observer is monitor. Monitors are created as multi observers - * since they require pre/post checking of the filter to test if the - * entity is entering/leaving the monitor. */ - int i; - for (i = 0; i < FLECS_EVENT_DESC_MAX; i ++) { - ecs_entity_t event = desc->events[i]; - if (!event) { - break; + o->events[0] = EcsOnAdd; + o->events[1] = EcsOnRemove; + o->event_count ++; + impl->flags |= EcsObserverIsMonitor; + if (desc->yield_existing) { + impl->flags |= EcsObserverYieldOnCreate; + impl->flags |= EcsObserverYieldOnDelete; + } + } else { + o->events[i] = event; + if (desc->yield_existing) { + if (event == EcsOnRemove) { + impl->flags |= EcsObserverYieldOnDelete; + } else { + impl->flags |= EcsObserverYieldOnCreate; + } } + } - if (event == EcsMonitor) { - /* Monitor event must be first and last event */ - ecs_check(i == 0, ECS_INVALID_PARAMETER, NULL); + o->event_count ++; + } - observer->events[0] = EcsOnAdd; - observer->events[1] = EcsOnRemove; - observer->event_count ++; - observer->is_monitor = true; - } else { - observer->events[i] = event; - } + /* Observer must have at least one event */ + ecs_check(o->event_count != 0, ECS_INVALID_PARAMETER, + "observer must have at least one event"); + + bool multi = false; - observer->event_count ++; + if (query->term_count == 1 && !desc->last_event_id) { + ecs_term_t *term = &query->terms[0]; + /* If the query has a single term but it is a *From operator, we + * need to create a multi observer */ + multi |= (term->oper == EcsAndFrom) || (term->oper == EcsOrFrom); + + /* An observer with only optional terms is a special case that is + * only handled by multi observers */ + multi |= term->oper == EcsOptional; + } + + bool is_monitor = impl->flags & EcsObserverIsMonitor; + if (query->term_count == 1 && !is_monitor && !multi) { + if (flecs_uni_observer_init(world, o, desc)) { + goto error; } + } else { + if (flecs_multi_observer_init(world, o, desc)) { + goto error; + } + } - /* Observer must have at least one event */ - ecs_check(observer->event_count != 0, ECS_INVALID_PARAMETER, NULL); + if (impl->flags & EcsObserverYieldOnCreate) { + flecs_observer_yield_existing(world, o, false); + } - bool multi = false; + return o; +error: + return NULL; +} - if (filter->term_count == 1 && !desc->last_event_id) { - ecs_term_t *term = &filter->terms[0]; - /* If the filter has a single term but it is a *From operator, we - * need to create a multi observer */ - multi |= (term->oper == EcsAndFrom) || (term->oper == EcsOrFrom); - - /* An observer with only optional terms is a special case that is - * only handled by multi observers */ - multi |= term->oper == EcsOptional; - } +ecs_entity_t ecs_observer_init( + ecs_world_t *world, + const ecs_observer_desc_t *desc) +{ + ecs_entity_t entity = 0; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_observer_desc_t was not initialized to zero"); + ecs_check(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, + "cannot create observer while world is being deleted"); - if (filter->term_count == 1 && !observer->is_monitor && !multi) { - if (flecs_uni_observer_init(world, observer, desc)) { - goto error; - } - } else { - if (flecs_multi_observer_init(world, observer, desc)) { - goto error; - } - } + entity = desc->entity; + if (!entity) { + entity = ecs_entity(world, {0}); + } + + EcsPoly *poly = flecs_poly_bind(world, entity, ecs_observer_t); + if (!poly->poly) { + ecs_observer_t *o = flecs_observer_init(world, entity, desc); + ecs_assert(o->entity == entity, ECS_INTERNAL_ERROR, NULL); + poly->poly = o; if (ecs_get_name(world, entity)) { ecs_trace("#[green]observer#[reset] %s created", ecs_get_name(world, entity)); } } else { - ecs_poly_assert(poly->poly, ecs_observer_t); - ecs_observer_t *observer = (ecs_observer_t*)poly->poly; + flecs_poly_assert(poly->poly, ecs_observer_t); + ecs_observer_t *o = (ecs_observer_t*)poly->poly; - if (desc->run) { - observer->run = desc->run; + if (o->ctx_free) { + if (o->ctx && o->ctx != desc->ctx) { + o->ctx_free(o->ctx); + } } - if (desc->callback) { - observer->callback = desc->callback; + + if (o->callback_ctx_free) { + if (o->callback_ctx && o->callback_ctx != desc->callback_ctx) { + o->callback_ctx_free(o->callback_ctx); + o->callback_ctx_free = NULL; + o->callback_ctx = NULL; + } + } + + if (o->run_ctx_free) { + if (o->run_ctx && o->run_ctx != desc->run_ctx) { + o->run_ctx_free(o->run_ctx); + o->run_ctx_free = NULL; + o->run_ctx = NULL; + } } - if (observer->ctx_free) { - if (observer->ctx && observer->ctx != desc->ctx) { - observer->ctx_free(observer->ctx); + if (desc->run) { + o->run = desc->run; + if (!desc->callback) { + o->callback = NULL; } } - if (observer->binding_ctx_free) { - if (observer->binding_ctx && observer->binding_ctx != desc->binding_ctx) { - observer->binding_ctx_free(observer->binding_ctx); + + if (desc->callback) { + o->callback = desc->callback; + if (!desc->run) { + o->run = NULL; } } if (desc->ctx) { - observer->ctx = desc->ctx; + o->ctx = desc->ctx; + } + + if (desc->callback_ctx) { + o->callback_ctx = desc->callback_ctx; } - if (desc->binding_ctx) { - observer->binding_ctx = desc->binding_ctx; + + if (desc->run_ctx) { + o->run_ctx = desc->run_ctx; } + if (desc->ctx_free) { - observer->ctx_free = desc->ctx_free; + o->ctx_free = desc->ctx_free; + } + + if (desc->callback_ctx_free) { + o->callback_ctx_free = desc->callback_ctx_free; } - if (desc->binding_ctx_free) { - observer->binding_ctx_free = desc->binding_ctx_free; + + if (desc->run_ctx_free) { + o->run_ctx_free = desc->run_ctx_free; } } - ecs_poly_modified(world, entity, ecs_observer_t); + flecs_poly_modified(world, entity, ecs_observer_t); return entity; error: - ecs_delete(world, entity); + if (entity) { + ecs_delete(world, entity); + } return 0; } -void* ecs_observer_get_ctx( - const ecs_world_t *world, - ecs_entity_t observer) -{ - const EcsPoly *o = ecs_poly_bind_get(world, observer, ecs_observer_t); - if (o) { - ecs_poly_assert(o->poly, ecs_observer_t); - return ((ecs_observer_t*)o->poly)->ctx; - } else { - return NULL; - } -} - -void* ecs_observer_get_binding_ctx( +const ecs_observer_t* ecs_observer_get( const ecs_world_t *world, ecs_entity_t observer) { - const EcsPoly *o = ecs_poly_bind_get(world, observer, ecs_observer_t); - if (o) { - ecs_poly_assert(o->poly, ecs_observer_t); - return ((ecs_observer_t*)o->poly)->binding_ctx; - } else { - return NULL; - } + return flecs_poly_get(world, observer, ecs_observer_t); } void flecs_observer_fini( - ecs_observer_t *observer) + ecs_observer_t *o) { - if (observer->is_multi) { - ecs_os_free(observer->last_event_id); + ecs_assert(o != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(o->query != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_world_t *world = o->query->world; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + + if (impl->flags & EcsObserverYieldOnDelete) { + flecs_observer_yield_existing(world, o, true); + } + + if (impl->flags & EcsObserverIsMulti) { + ecs_observer_t **children = ecs_vec_first(&impl->children); + int32_t i, children_count = ecs_vec_count(&impl->children); + + for (i = 0; i < children_count; i ++) { + flecs_observer_fini(children[i]); + } + + ecs_os_free(impl->last_event_id); } else { - if (observer->filter.term_count) { - flecs_unregister_observer( - observer->filter.world, observer->observable, observer); + if (o->query->term_count) { + flecs_unregister_observer(world, o->observable, o); } else { - /* Observer creation failed while creating filter */ + /* Observer creation failed while creating query */ } } - /* Cleanup filters */ - ecs_filter_fini(&observer->filter); + ecs_vec_fini_t(&world->allocator, &impl->children, ecs_observer_t*); + + /* Cleanup queries */ + ecs_query_fini(o->query); + if (impl->not_query) { + ecs_query_fini(impl->not_query); + } /* Cleanup context */ - if (observer->ctx_free) { - observer->ctx_free(observer->ctx); + if (o->ctx_free) { + o->ctx_free(o->ctx); + } + + if (o->callback_ctx_free) { + o->callback_ctx_free(o->callback_ctx); + } + + if (o->run_ctx_free) { + o->run_ctx_free(o->run_ctx); } - if (observer->binding_ctx_free) { - observer->binding_ctx_free(observer->binding_ctx); + flecs_poly_fini(o, ecs_observer_t); + flecs_sparse_remove_t( + &world->store.observers, ecs_observer_impl_t, impl->id); +} + +void flecs_observer_set_disable_bit( + ecs_world_t *world, + ecs_entity_t e, + ecs_flags32_t bit, + bool cond) +{ + const EcsPoly *poly = ecs_get_pair(world, e, EcsPoly, EcsObserver); + if (!poly || !poly->poly) { + return; } - ecs_poly_free(observer, ecs_observer_t); + ecs_observer_t *o = poly->poly; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + if (impl->flags & EcsObserverIsMulti) { + ecs_observer_t **children = ecs_vec_first(&impl->children); + int32_t i, children_count = ecs_vec_count(&impl->children); + if (children_count) { + for (i = 0; i < children_count; i ++) { + ECS_BIT_COND(flecs_observer_impl(children[i])->flags, bit, cond); + } + } + } else { + flecs_poly_assert(o, ecs_observer_t); + ECS_BIT_COND(impl->flags, bit, cond); + } } /** @@ -16797,11 +15536,9 @@ void flecs_log_msg( int32_t line, const char *msg) { - FILE *stream; - if (level >= 0) { + FILE *stream = ecs_os_api.log_out_; + if (!stream) { stream = stdout; - } else { - stream = stderr; } bool use_colors = ecs_os_api.flags_ & EcsOsApiLogWithColors; @@ -16826,7 +15563,7 @@ void flecs_log_msg( fputs(" ", stream); } char time_buf[20]; - ecs_os_sprintf(time_buf, "%u", (uint32_t)delta); + ecs_os_snprintf(time_buf, 20, "%u", (uint32_t)delta); fputs("+", stream); fputs(time_buf, stream); fputs(" ", stream); @@ -16840,7 +15577,7 @@ void flecs_log_msg( now = time(NULL); } char time_buf[20]; - ecs_os_sprintf(time_buf, "%u", (uint32_t)now); + ecs_os_snprintf(time_buf, 20, "%u", (uint32_t)now); fputs(time_buf, stream); fputs(" ", stream); } @@ -16920,7 +15657,7 @@ void flecs_log_msg( } void ecs_os_dbg( - const char *file, + const char *file, int32_t line, const char *msg) { @@ -17038,6 +15775,26 @@ void ecs_os_strset(char **str, const char *value) { ecs_os_free(old); } +void ecs_os_perf_trace_push_( + const char *file, + size_t line, + const char *name) +{ + if (ecs_os_api.perf_trace_push_) { + ecs_os_api.perf_trace_push_(file, line, name); + } +} + +void ecs_os_perf_trace_pop_( + const char *file, + size_t line, + const char *name) +{ + if (ecs_os_api.perf_trace_pop_) { + ecs_os_api.perf_trace_pop_(file, line, name); + } +} + /* Replace dots with underscores */ static char *module_file_base(const char *module, char sep) { @@ -17229,10 +15986,6 @@ const char* ecs_os_strerror(int err) { * Mixins are like a vtable, but for members. Each type populates the table with * offsets to the members that correspond with the mixin. If an entry in the * mixin table is not set, the type does not support the mixin. - * - * An example is the Iterable mixin, which makes it possible to create an - * iterator for any poly object (like filters, queries, the world) that - * implements the Iterable mixin. */ @@ -17240,7 +15993,6 @@ static const char* mixin_kind_str[] = { [EcsMixinWorld] = "world", [EcsMixinEntity] = "entity", [EcsMixinObservable] = "observable", - [EcsMixinIterable] = "iterable", [EcsMixinDtor] = "dtor", [EcsMixinMax] = "max (should never be requested by application)" }; @@ -17250,7 +16002,6 @@ ecs_mixins_t ecs_world_t_mixins = { .elems = { [EcsMixinWorld] = offsetof(ecs_world_t, self), [EcsMixinObservable] = offsetof(ecs_world_t, observable), - [EcsMixinIterable] = offsetof(ecs_world_t, iterable) } }; @@ -17261,32 +16012,12 @@ ecs_mixins_t ecs_stage_t_mixins = { } }; -ecs_mixins_t ecs_query_t_mixins = { - .type_name = "ecs_query_t", - .elems = { - [EcsMixinWorld] = offsetof(ecs_query_t, filter.world), - [EcsMixinEntity] = offsetof(ecs_query_t, filter.entity), - [EcsMixinIterable] = offsetof(ecs_query_t, iterable), - [EcsMixinDtor] = offsetof(ecs_query_t, dtor) - } -}; - ecs_mixins_t ecs_observer_t_mixins = { .type_name = "ecs_observer_t", .elems = { - [EcsMixinWorld] = offsetof(ecs_observer_t, filter.world), - [EcsMixinEntity] = offsetof(ecs_observer_t, filter.entity), - [EcsMixinDtor] = offsetof(ecs_observer_t, dtor) - } -}; - -ecs_mixins_t ecs_filter_t_mixins = { - .type_name = "ecs_filter_t", - .elems = { - [EcsMixinWorld] = offsetof(ecs_filter_t, world), - [EcsMixinEntity] = offsetof(ecs_filter_t, entity), - [EcsMixinIterable] = offsetof(ecs_filter_t, iterable), - [EcsMixinDtor] = offsetof(ecs_filter_t, dtor) + [EcsMixinWorld] = offsetof(ecs_observer_t, world), + [EcsMixinEntity] = offsetof(ecs_observer_t, entity), + [EcsMixinDtor] = offsetof(ecs_observer_impl_t, dtor) } }; @@ -17300,7 +16031,8 @@ void* assert_mixin( const ecs_header_t *hdr = poly; ecs_assert(hdr != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, NULL); + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); const ecs_mixins_t *mixins = hdr->mixins; ecs_assert(mixins != NULL, ECS_INVALID_PARAMETER, NULL); @@ -17315,7 +16047,7 @@ void* assert_mixin( return ECS_OFFSET(hdr, offset); } -void* ecs_poly_init_( +void* flecs_poly_init_( ecs_poly_t *poly, int32_t type, ecs_size_t size, @@ -17328,12 +16060,13 @@ void* ecs_poly_init_( hdr->magic = ECS_OBJECT_MAGIC; hdr->type = type; + hdr->refcount = 1; hdr->mixins = mixins; return poly; } -void ecs_poly_fini_( +void flecs_poly_fini_( ecs_poly_t *poly, int32_t type) { @@ -17343,12 +16076,52 @@ void ecs_poly_fini_( ecs_header_t *hdr = poly; /* Don't deinit poly that wasn't initialized */ - ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, NULL); - ecs_assert(hdr->type == type, ECS_INVALID_PARAMETER, NULL); + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); + ecs_assert(hdr->type == type, ECS_INVALID_PARAMETER, + "incorrect function called to free flecs object"); hdr->magic = 0; } -EcsPoly* ecs_poly_bind_( +int32_t flecs_poly_claim_( + ecs_poly_t *poly) +{ + ecs_assert(poly != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_header_t *hdr = poly; + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); + if (ecs_os_has_threading()) { + return ecs_os_ainc(&hdr->refcount); + } else { + return ++hdr->refcount; + } +} + +int32_t flecs_poly_release_( + ecs_poly_t *poly) +{ + ecs_assert(poly != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_header_t *hdr = poly; + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); + if (ecs_os_has_threading()) { + return ecs_os_adec(&hdr->refcount); + } else { + return --hdr->refcount; + } +} + +int32_t flecs_poly_refcount( + ecs_poly_t *poly) +{ + ecs_assert(poly != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_header_t *hdr = poly; + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); + return hdr->refcount; +} + +EcsPoly* flecs_poly_bind_( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag) @@ -17368,7 +16141,7 @@ EcsPoly* ecs_poly_bind_( /* If this is a new poly, leave the actual creation up to the caller so they * call tell the difference between a create or an update */ - EcsPoly *result = ecs_get_mut_pair(world, entity, EcsPoly, tag); + EcsPoly *result = ecs_ensure_pair(world, entity, EcsPoly, tag); if (deferred) { ecs_defer_resume(world); @@ -17377,7 +16150,7 @@ EcsPoly* ecs_poly_bind_( return result; } -void ecs_poly_modified_( +void flecs_poly_modified_( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag) @@ -17385,7 +16158,7 @@ void ecs_poly_modified_( ecs_modified_pair(world, entity, ecs_id(EcsPoly), tag); } -const EcsPoly* ecs_poly_bind_get_( +const EcsPoly* flecs_poly_bind_get_( const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag) @@ -17393,35 +16166,30 @@ const EcsPoly* ecs_poly_bind_get_( return ecs_get_pair(world, entity, EcsPoly, tag); } -ecs_poly_t* ecs_poly_get_( +ecs_poly_t* flecs_poly_get_( const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag) { - const EcsPoly *p = ecs_poly_bind_get_(world, entity, tag); + const EcsPoly *p = flecs_poly_bind_get_(world, entity, tag); if (p) { return p->poly; } return NULL; } -bool ecs_poly_is_( +bool flecs_poly_is_( const ecs_poly_t *poly, int32_t type) { ecs_assert(poly != NULL, ECS_INVALID_PARAMETER, NULL); const ecs_header_t *hdr = poly; - ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, NULL); + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); return hdr->type == type; } -ecs_iterable_t* ecs_get_iterable( - const ecs_poly_t *poly) -{ - return (ecs_iterable_t*)assert_mixin(poly, EcsMixinIterable); -} - ecs_observable_t* ecs_get_observable( const ecs_poly_t *poly) { @@ -17443,1385 +16211,1270 @@ ecs_entity_t ecs_get_entity( return *(ecs_entity_t*)assert_mixin(poly, EcsMixinEntity); } -ecs_poly_dtor_t* ecs_get_dtor( +flecs_poly_dtor_t* ecs_get_dtor( const ecs_poly_t *poly) { - return (ecs_poly_dtor_t*)assert_mixin(poly, EcsMixinDtor); + return (flecs_poly_dtor_t*)assert_mixin(poly, EcsMixinDtor); } /** - * @file query.c - * @brief Cached query implementation. - * - * Cached queries store a list of matched tables. The inputs for a cached query - * are a filter and an observer. The filter is used to initially populate the - * cache, and an observer is used to keep the cacne up to date. + * @file search.c + * @brief Search functions to find (component) ids in table types. * - * Cached queries additionally support features like sorting and grouping. - * With sorting, an application can iterate over entities that can be sorted by - * a component. Grouping allows an application to group matched tables, which is - * used internally to implement the cascade feature, and can additionally be - * used to implement things like world cells. + * Search functions are used to find the column index of a (component) id in a + * table. Additionally, search functions implement the logic for finding a + * component id by following a relationship upwards. */ static -uint64_t flecs_query_get_group_id( - ecs_query_t *query, - ecs_table_t *table) -{ - if (query->group_by) { - return query->group_by(query->filter.world, table, - query->group_by_id, query->group_by_ctx); - } else { - return 0; - } -} - -static -void flecs_query_compute_group_id( - ecs_query_t *query, - ecs_query_table_match_t *match) +int32_t flecs_type_search( + const ecs_table_t *table, + ecs_id_record_t *idr, + ecs_id_t *ids, + ecs_id_t *id_out, + ecs_table_record_t **tr_out) { - ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - - if (query->group_by) { - ecs_table_t *table = match->table; - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - - match->group_id = flecs_query_get_group_id(query, table); - } else { - match->group_id = 0; + ecs_table_record_t *tr = ecs_table_cache_get(&idr->cache, table); + if (tr) { + int32_t r = tr->index; + if (tr_out) tr_out[0] = tr; + if (id_out) { + id_out[0] = ids[r]; + } + return r; } -} -static -ecs_query_table_list_t* flecs_query_get_group( - const ecs_query_t *query, - uint64_t group_id) -{ - return ecs_map_get_deref(&query->groups, ecs_query_table_list_t, group_id); + return -1; } static -ecs_query_table_list_t* flecs_query_ensure_group( - ecs_query_t *query, - uint64_t id) +int32_t flecs_type_offset_search( + int32_t offset, + ecs_id_t id, + ecs_id_t *ids, + int32_t count, + ecs_id_t *id_out) { - ecs_query_table_list_t *group = ecs_map_get_deref(&query->groups, - ecs_query_table_list_t, id); + ecs_assert(ids != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(count > 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(offset > 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - if (!group) { - group = ecs_map_insert_alloc_t(&query->groups, - ecs_query_table_list_t, id); - ecs_os_zeromem(group); - if (query->on_group_create) { - group->info.ctx = query->on_group_create( - query->filter.world, id, query->group_by_ctx); + while (offset < count) { + ecs_id_t type_id = ids[offset ++]; + if (ecs_id_match(type_id, id)) { + if (id_out) { + id_out[0] = type_id; + } + return offset - 1; } } - return group; + return -1; } -static -void flecs_query_remove_group( - ecs_query_t *query, - uint64_t id) +bool flecs_type_can_inherit_id( + const ecs_world_t *world, + const ecs_table_t *table, + const ecs_id_record_t *idr, + ecs_id_t id) { - if (query->on_group_delete) { - ecs_query_table_list_t *group = ecs_map_get_deref(&query->groups, - ecs_query_table_list_t, id); - if (group) { - query->on_group_delete(query->filter.world, id, - group->info.ctx, query->group_by_ctx); - } + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + if (idr->flags & EcsIdOnInstantiateDontInherit) { + return false; } - ecs_map_remove_free(&query->groups, id); -} - -static -uint64_t flecs_query_default_group_by( - ecs_world_t *world, - ecs_table_t *table, - ecs_id_t id, - void *ctx) -{ - (void)ctx; - - ecs_id_t match; - if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &match) != -1) { - return ecs_pair_second(world, match); + if (idr->flags & EcsIdExclusive) { + if (ECS_HAS_ID_FLAG(id, PAIR)) { + ecs_entity_t er = ECS_PAIR_FIRST(id); + if (flecs_table_record_get( + world, table, ecs_pair(er, EcsWildcard))) + { + return false; + } + } } - return 0; + return true; } -/* Find the last node of the group after which this group should be inserted */ static -ecs_query_table_match_t* flecs_query_find_group_insertion_node( - ecs_query_t *query, - uint64_t group_id) +int32_t flecs_type_search_relation( + const ecs_world_t *world, + const ecs_table_t *table, + int32_t offset, + ecs_id_t id, + ecs_id_record_t *idr, + ecs_id_t rel, + ecs_id_record_t *idr_r, + bool self, + ecs_entity_t *subject_out, + ecs_id_t *id_out, + ecs_table_record_t **tr_out) { - /* Grouping must be enabled */ - ecs_assert(query->group_by != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_map_iter_t it = ecs_map_iter(&query->groups); - ecs_query_table_list_t *list, *closest_list = NULL; - uint64_t id, closest_id = 0; - - bool desc = false; + ecs_type_t type = table->type; + ecs_id_t *ids = type.array; + int32_t count = type.count; - if (query->cascade_by) { - desc = (query->filter.terms[ - query->cascade_by - 1].src.flags & EcsDesc) != 0; + if (self) { + if (offset) { + int32_t r = flecs_type_offset_search(offset, id, ids, count, id_out); + if (r != -1) { + return r; + } + } else { + int32_t r = flecs_type_search(table, idr, ids, id_out, tr_out); + if (r != -1) { + return r; + } + } } - /* Find closest smaller group id */ - while (ecs_map_next(&it)) { - id = ecs_map_key(&it); - - if (!desc) { - if (id >= group_id) { - continue; + ecs_flags32_t flags = table->flags; + if ((flags & EcsTableHasPairs) && rel) { + bool is_a = rel == ecs_pair(EcsIsA, EcsWildcard); + if (is_a) { + if (!(flags & EcsTableHasIsA)) { + return -1; } - } else { - if (id <= group_id) { - continue; + idr_r = world->idr_isa_wildcard; + + if (!flecs_type_can_inherit_id(world, table, idr, id)) { + return -1; } } - list = ecs_map_ptr(&it); - if (!list->last) { - ecs_assert(list->first == NULL, ECS_INTERNAL_ERROR, NULL); - continue; + if (!idr_r) { + idr_r = flecs_id_record_get(world, rel); + if (!idr_r) { + return -1; + } } - bool comp; - if (!desc) { - comp = ((group_id - id) < (group_id - closest_id)); + ecs_id_t id_r; + int32_t r, r_column; + if (offset) { + r_column = flecs_type_offset_search(offset, rel, ids, count, &id_r); } else { - comp = ((group_id - id) > (group_id - closest_id)); + r_column = flecs_type_search(table, idr_r, ids, &id_r, 0); } + while (r_column != -1) { + ecs_entity_t obj = ECS_PAIR_SECOND(id_r); + ecs_assert(obj != 0, ECS_INTERNAL_ERROR, NULL); - if (!closest_list || comp) { - closest_id = id; - closest_list = list; + ecs_record_t *rec = flecs_entities_get_any(world, obj); + ecs_assert(rec != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_table_t *obj_table = rec->table; + if (obj_table) { + ecs_assert(obj_table != table, ECS_CYCLE_DETECTED, NULL); + + r = flecs_type_search_relation(world, obj_table, 0, id, idr, + rel, idr_r, true, subject_out, id_out, tr_out); + if (r != -1) { + if (subject_out && !subject_out[0]) { + subject_out[0] = ecs_get_alive(world, obj); + } + return r_column; + } + + if (!is_a) { + r = flecs_type_search_relation(world, obj_table, 0, id, idr, + ecs_pair(EcsIsA, EcsWildcard), world->idr_isa_wildcard, + true, subject_out, id_out, tr_out); + if (r != -1) { + if (subject_out && !subject_out[0]) { + subject_out[0] = ecs_get_alive(world, obj); + } + return r_column; + } + } + } + + r_column = flecs_type_offset_search( + r_column + 1, rel, ids, count, &id_r); } } - if (closest_list) { - return closest_list->last; - } else { - return NULL; /* Group should be first in query */ - } + return -1; } -/* Initialize group with first node */ -static -void flecs_query_create_group( - ecs_query_t *query, - ecs_query_table_match_t *match) +int32_t flecs_search_relation_w_idr( + const ecs_world_t *world, + const ecs_table_t *table, + int32_t offset, + ecs_id_t id, + ecs_entity_t rel, + ecs_flags64_t flags, + ecs_entity_t *subject_out, + ecs_id_t *id_out, + struct ecs_table_record_t **tr_out, + ecs_id_record_t *idr) { - uint64_t group_id = match->group_id; + if (!table) return -1; - /* If query has grouping enabled & this is a new/empty group, find - * the insertion point for the group */ - ecs_query_table_match_t *insert_after = flecs_query_find_group_insertion_node( - query, group_id); + flecs_poly_assert(world, ecs_world_t); + ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - if (!insert_after) { - /* This group should appear first in the query list */ - ecs_query_table_match_t *query_first = query->list.first; - if (query_first) { - /* If this is not the first match for the query, insert before it */ - match->next = query_first; - query_first->prev = match; - query->list.first = match; - } else { - /* If this is the first match of the query, initialize its list */ - ecs_assert(query->list.last == NULL, ECS_INTERNAL_ERROR, NULL); - query->list.first = match; - query->list.last = match; + flags = flags ? flags : (EcsSelf|EcsUp); + + if (!idr) { + idr = flecs_id_record_get(world, id); + if (!idr) { + return -1; } - } else { - ecs_assert(query->list.first != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.last != NULL, ECS_INTERNAL_ERROR, NULL); + } - /* This group should appear after another group */ - ecs_query_table_match_t *insert_before = insert_after->next; - match->prev = insert_after; - insert_after->next = match; - match->next = insert_before; - if (insert_before) { - insert_before->prev = match; + if (subject_out) subject_out[0] = 0; + if (!(flags & EcsUp)) { + if (offset) { + return ecs_search_offset(world, table, offset, id, id_out); } else { - ecs_assert(query->list.last == insert_after, - ECS_INTERNAL_ERROR, NULL); - - /* This group should appear last in the query list */ - query->list.last = match; + return flecs_type_search( + table, idr, table->type.array, id_out, tr_out); } } -} -/* Find the list the node should be part of */ -static -ecs_query_table_list_t* flecs_query_get_node_list( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - if (query->group_by) { - return flecs_query_get_group(query, match->group_id); - } else { - return &query->list; - } -} + int32_t result = flecs_type_search_relation(world, table, offset, id, idr, + ecs_pair(rel, EcsWildcard), NULL, flags & EcsSelf, subject_out, + id_out, tr_out); -/* Find or create the list the node should be part of */ -static -ecs_query_table_list_t* flecs_query_ensure_node_list( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - if (query->group_by) { - return flecs_query_ensure_group(query, match->group_id); - } else { - return &query->list; - } + return result; } -/* Remove node from list */ -static -void flecs_query_remove_table_node( - ecs_query_t *query, - ecs_query_table_match_t *match) +int32_t ecs_search_relation( + const ecs_world_t *world, + const ecs_table_t *table, + int32_t offset, + ecs_id_t id, + ecs_entity_t rel, + ecs_flags64_t flags, + ecs_entity_t *subject_out, + ecs_id_t *id_out, + struct ecs_table_record_t **tr_out) { - ecs_query_table_match_t *prev = match->prev; - ecs_query_table_match_t *next = match->next; + if (!table) return -1; - ecs_assert(prev != match, ECS_INTERNAL_ERROR, NULL); - ecs_assert(next != match, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!prev || prev != next, ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(world, ecs_world_t); + ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - ecs_query_table_list_t *list = flecs_query_get_node_list(query, match); + flags = flags ? flags : (EcsSelf|EcsUp); - if (!list || !list->first) { - /* If list contains no matches, the match must be empty */ - ecs_assert(!list || list->last == NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(prev == NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(next == NULL, ECS_INTERNAL_ERROR, NULL); - return; + if (subject_out) subject_out[0] = 0; + if (!(flags & EcsUp)) { + return ecs_search_offset(world, table, offset, id, id_out); } - ecs_assert(prev != NULL || query->list.first == match, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(next != NULL || query->list.last == match, - ECS_INTERNAL_ERROR, NULL); - - if (prev) { - prev->next = next; - } - if (next) { - next->prev = prev; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return -1; } - ecs_assert(list->info.table_count > 0, ECS_INTERNAL_ERROR, NULL); - list->info.table_count --; - - if (query->group_by) { - uint64_t group_id = match->group_id; - - /* Make sure query.list is updated if this is the first or last group */ - if (query->list.first == match) { - ecs_assert(prev == NULL, ECS_INTERNAL_ERROR, NULL); - query->list.first = next; - prev = next; - } - if (query->list.last == match) { - ecs_assert(next == NULL, ECS_INTERNAL_ERROR, NULL); - query->list.last = prev; - next = prev; - } + int32_t result = flecs_type_search_relation(world, table, offset, id, idr, + ecs_pair(rel, EcsWildcard), NULL, flags & EcsSelf, subject_out, + id_out, tr_out); - ecs_assert(query->list.info.table_count > 0, ECS_INTERNAL_ERROR, NULL); - query->list.info.table_count --; - list->info.match_count ++; + return result; +} - /* Make sure group list only contains nodes that belong to the group */ - if (prev && prev->group_id != group_id) { - /* The previous node belonged to another group */ - prev = next; - } - if (next && next->group_id != group_id) { - /* The next node belonged to another group */ - next = prev; - } - - /* Do check again, in case both prev & next belonged to another group */ - if ((!prev && !next) || (prev && prev->group_id != group_id)) { - /* There are no more matches left in this group */ - flecs_query_remove_group(query, group_id); - list = NULL; - } - } - - if (list) { - if (list->first == match) { - list->first = next; - } - if (list->last == match) { - list->last = prev; - } - } +int32_t flecs_search_w_idr( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t *id_out, + ecs_id_record_t *idr) +{ + if (!table) return -1; - match->prev = NULL; - match->next = NULL; + flecs_poly_assert(world, ecs_world_t); + (void)world; - query->match_count ++; + ecs_type_t type = table->type; + ecs_id_t *ids = type.array; + return flecs_type_search(table, idr, ids, id_out, 0); } -/* Add node to list */ -static -void flecs_query_insert_table_node( - ecs_query_t *query, - ecs_query_table_match_t *match) +int32_t ecs_search( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id, + ecs_id_t *id_out) { - /* Node should not be part of an existing list */ - ecs_assert(match->prev == NULL && match->next == NULL, - ECS_INTERNAL_ERROR, NULL); + if (!table) return -1; - /* If this is the first match, activate system */ - if (!query->list.first && query->filter.entity) { - ecs_remove_id(query->filter.world, query->filter.entity, EcsEmpty); - } + flecs_poly_assert(world, ecs_world_t); + ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - flecs_query_compute_group_id(query, match); + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return -1; + } - ecs_query_table_list_t *list = flecs_query_ensure_node_list(query, match); - if (list->last) { - ecs_assert(query->list.first != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.last != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(list->first != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_type_t type = table->type; + ecs_id_t *ids = type.array; + return flecs_type_search(table, idr, ids, id_out, 0); +} - ecs_query_table_match_t *last = list->last; - ecs_query_table_match_t *last_next = last->next; +int32_t ecs_search_offset( + const ecs_world_t *world, + const ecs_table_t *table, + int32_t offset, + ecs_id_t id, + ecs_id_t *id_out) +{ + if (!offset) { + flecs_poly_assert(world, ecs_world_t); + return ecs_search(world, table, id, id_out); + } - match->prev = last; - match->next = last_next; - last->next = match; + if (!table) return -1; - if (last_next) { - last_next->prev = match; - } + ecs_type_t type = table->type; + ecs_id_t *ids = type.array; + int32_t count = type.count; + return flecs_type_offset_search(offset, id, ids, count, id_out); +} - list->last = match; +static +int32_t flecs_relation_depth_walk( + const ecs_world_t *world, + const ecs_id_record_t *idr, + const ecs_table_t *first, + const ecs_table_t *table) +{ + int32_t result = 0; - if (query->group_by) { - /* Make sure to update query list if this is the last group */ - if (query->list.last == last) { - query->list.last = match; - } - } - } else { - ecs_assert(list->first == NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return 0; + } - list->first = match; - list->last = match; + int32_t i = tr->index, end = i + tr->count; + for (; i != end; i ++) { + ecs_entity_t o = ecs_pair_second(world, table->type.array[i]); + ecs_assert(o != 0, ECS_INTERNAL_ERROR, NULL); - if (query->group_by) { - /* Initialize group with its first node */ - flecs_query_create_group(query, match); + ecs_table_t *ot = ecs_get_table(world, o); + if (!ot) { + continue; + } + + ecs_assert(ot != first, ECS_CYCLE_DETECTED, NULL); + int32_t cur = flecs_relation_depth_walk(world, idr, first, ot); + if (cur > result) { + result = cur; } } + + return result + 1; +} - if (query->group_by) { - list->info.table_count ++; - list->info.match_count ++; +int32_t flecs_relation_depth( + const ecs_world_t *world, + ecs_entity_t r, + const ecs_table_t *table) +{ + ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair(r, EcsWildcard)); + if (!idr) { + return 0; } - query->list.info.table_count ++; - query->match_count ++; + return flecs_relation_depth_walk(world, idr, table, table); +} - ecs_assert(match->prev != match, ECS_INTERNAL_ERROR, NULL); - ecs_assert(match->next != match, ECS_INTERNAL_ERROR, NULL); +/** + * @file stage.c + * @brief Staging implementation. + * + * A stage is an object that can be used to temporarily store mutations to a + * world while a world is in readonly mode. ECS operations that are invoked on + * a stage are stored in a command buffer, which is flushed during sync points, + * or manually by the user. + * + * Stages contain additional state to enable other API functionality without + * having to mutate the world, such as setting the current scope, and allocators + * that are local to a stage. + * + * In a multi threaded application, each thread has its own stage which allows + * threads to insert mutations without having to lock administration. + */ - ecs_assert(list->first != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(list->last != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(list->last == match, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.first != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.last != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.first->prev == NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.last->next == NULL, ECS_INTERNAL_ERROR, NULL); -} static -ecs_query_table_match_t* flecs_query_cache_add( - ecs_world_t *world, - ecs_query_table_t *elem) +ecs_cmd_t* flecs_cmd_new( + ecs_stage_t *stage) { - ecs_query_table_match_t *result = - flecs_bcalloc(&world->allocators.query_table_match); - - if (!elem->first) { - elem->first = result; - elem->last = result; - } else { - ecs_assert(elem->last != NULL, ECS_INTERNAL_ERROR, NULL); - elem->last->next_match = result; - elem->last = result; - } - - return result; + ecs_cmd_t *cmd = ecs_vec_append_t(&stage->allocator, &stage->cmd->queue, + ecs_cmd_t); + cmd->is._1.value = NULL; + cmd->next_for_entity = 0; + cmd->entry = NULL; + cmd->system = stage->system; + return cmd; } -typedef struct { - ecs_table_t *table; - int32_t column; -} flecs_table_column_t; - static -void flecs_query_get_column_for_term( - ecs_query_t *query, - ecs_query_table_match_t *match, - int32_t t, - flecs_table_column_t *out) +ecs_cmd_t* flecs_cmd_new_batched( + ecs_stage_t *stage, + ecs_entity_t e) { - const ecs_filter_t *filter = &query->filter; - ecs_world_t *world = filter->world; - ecs_term_t *term = &filter->terms[t]; - int32_t field = term->field_index; - ecs_entity_t src = match->sources[field]; - ecs_table_t *table = NULL; - int32_t column = -1; + ecs_vec_t *cmds = &stage->cmd->queue; + ecs_cmd_entry_t *entry = flecs_sparse_get_any_t( + &stage->cmd->entries, ecs_cmd_entry_t, e); - if (term->oper != EcsNot) { - if (!src) { - if (term->src.flags != EcsIsEntity) { - table = match->table; - column = match->storage_columns[field]; - if (column == -2) { - /* Shared field */ - column = -1; - } - } + int32_t cur = ecs_vec_count(cmds); + ecs_cmd_t *cmd = flecs_cmd_new(stage); + if (entry) { + if (entry->first == -1) { + /* Existing but invalidated entry */ + entry->first = cur; + cmd->entry = entry; } else { - table = ecs_get_table(world, src); - if (ecs_term_match_this(term)) { - int32_t ref_index = -match->columns[field] - 1; - ecs_ref_t *ref = ecs_vec_get_t(&match->refs, ecs_ref_t, ref_index); - if (ref->id != 0) { - ecs_ref_update(world, ref); - column = ref->tr->column; - } - } else { - column = -(match->columns[field] + 1); + int32_t last = entry->last; + ecs_cmd_t *arr = ecs_vec_first_t(cmds, ecs_cmd_t); + ecs_assert(arr[last].entity == e, ECS_INTERNAL_ERROR, NULL); + ecs_cmd_t *last_op = &arr[last]; + last_op->next_for_entity = cur; + if (last == entry->first) { + /* Flip sign bit so flush logic can tell which command + * is the first for an entity */ + last_op->next_for_entity *= -1; } } + } else { + cmd->entry = entry = flecs_sparse_ensure_fast_t( + &stage->cmd->entries, ecs_cmd_entry_t, e); + entry->first = cur; } - out->table = table; - out->column = column; + entry->last = cur; + + return cmd; } -/* Get match monitor. Monitors are used to keep track of whether components - * matched by the query in a table have changed. */ static -bool flecs_query_get_match_monitor( - ecs_query_t *query, - ecs_query_table_match_t *match) +void flecs_stage_merge( + ecs_world_t *world) { - if (match->monitor) { - return false; - } - - int32_t *monitor = flecs_balloc(&query->allocators.monitors); - monitor[0] = 0; - - /* Mark terms that don't need to be monitored. This saves time when reading - * and/or updating the monitor. */ - const ecs_filter_t *f = &query->filter; - int32_t i, field = -1, term_count = f->term_count; - flecs_table_column_t tc; - - for (i = 0; i < term_count; i ++) { - if (field == f->terms[i].field_index) { - if (monitor[field + 1] != -1) { - continue; - } - } + bool is_stage = flecs_poly_is(world, ecs_stage_t); + ecs_stage_t *stage = flecs_stage_from_world(&world); - field = f->terms[i].field_index; - monitor[field + 1] = -1; + bool measure_frame_time = ECS_BIT_IS_SET(world->flags, + EcsWorldMeasureFrameTime); - if (f->terms[i].inout != EcsIn && - f->terms[i].inout != EcsInOut && - f->terms[i].inout != EcsInOutDefault) { - continue; /* If term isn't read, don't monitor */ - } + ecs_time_t t_start = {0}; + if (measure_frame_time) { + ecs_os_get_time(&t_start); + } - int32_t column = match->columns[field]; - if (column == 0) { - continue; /* Don't track terms that aren't matched */ - } + ecs_dbg_3("#[magenta]merge"); + ecs_log_push_3(); - flecs_query_get_column_for_term(query, match, i, &tc); - if (tc.column == -1) { - continue; /* Don't track terms that aren't stored */ + if (is_stage) { + /* Check for consistency if force_merge is enabled. In practice this + * function will never get called with force_merge disabled for just + * a single stage. */ + ecs_assert(stage->defer == 1, ECS_INVALID_OPERATION, + "mismatching defer_begin/defer_end detected"); + flecs_defer_end(world, stage); + } else { + /* Merge stages. Only merge if the stage has auto_merging turned on, or + * if this is a forced merge (like when ecs_merge is called) */ + int32_t i, count = ecs_get_stage_count(world); + for (i = 0; i < count; i ++) { + ecs_stage_t *s = (ecs_stage_t*)ecs_get_stage(world, i); + flecs_poly_assert(s, ecs_stage_t); + flecs_defer_end(world, s); } - - monitor[field + 1] = 0; } - /* If matched table needs entity filter, make sure to test fields that could - * be matched by flattened parents. */ - ecs_entity_filter_t *ef = match->entity_filter; - if (ef && ef->flat_tree_column != -1) { - int32_t *fields = ecs_vec_first(&ef->ft_terms); - int32_t field_count = ecs_vec_count(&ef->ft_terms); - for (i = 0; i < field_count; i ++) { - monitor[fields[i] + 1] = 0; - } - } + flecs_eval_component_monitors(world); - match->monitor = monitor; + if (measure_frame_time) { + world->info.merge_time_total += (ecs_ftime_t)ecs_time_measure(&t_start); + } - query->flags |= EcsQueryHasMonitor; + world->info.merge_count_total ++; - return true; + /* If stage is unmanaged, deferring is always enabled */ + if (stage->id == -1) { + flecs_defer_begin(world, stage); + } + + ecs_log_pop_3(); } -/* Synchronize match monitor with table dirty state */ -static -void flecs_query_sync_match_monitor( - ecs_query_t *query, - ecs_query_table_match_t *match) +bool flecs_defer_begin( + ecs_world_t *world, + ecs_stage_t *stage) { - ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - if (!match->monitor) { - if (query->flags & EcsQueryHasMonitor) { - flecs_query_get_match_monitor(query, match); - } else { - return; - } - } + flecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(stage, ecs_stage_t); + (void)world; + if (stage->defer < 0) return false; + return (++ stage->defer) == 1; +} - int32_t *monitor = match->monitor; - ecs_table_t *table = match->table; - if (table) { - int32_t *dirty_state = flecs_table_get_dirty_state( - query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - monitor[0] = dirty_state[0]; /* Did table gain/lose entities */ +bool flecs_defer_cmd( + ecs_stage_t *stage) +{ + if (stage->defer) { + return (stage->defer > 0); } - ecs_filter_t *filter = &query->filter; - { - flecs_table_column_t tc; - int32_t t, term_count = filter->term_count; - for (t = 0; t < term_count; t ++) { - int32_t field = filter->terms[t].field_index; - if (monitor[field + 1] == -1) { - continue; - } - - flecs_query_get_column_for_term(query, match, t, &tc); + stage->defer ++; + return false; +} - monitor[field + 1] = flecs_table_get_dirty_state( - filter->world, tc.table)[tc.column + 1]; +bool flecs_defer_modified( + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_id_t id) +{ + if (flecs_defer_cmd(stage)) { + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); + if (cmd) { + cmd->kind = EcsCmdModified; + cmd->id = id; + cmd->entity = entity; } + return true; } + return false; +} - ecs_entity_filter_t *ef = match->entity_filter; - if (ef && ef->flat_tree_column != -1) { - flecs_flat_table_term_t *fields = ecs_vec_first(&ef->ft_terms); - int32_t f, field_count = ecs_vec_count(&ef->ft_terms); - for (f = 0; f < field_count; f ++) { - flecs_flat_table_term_t *field = &fields[f]; - flecs_flat_monitor_t *tgt_mon = ecs_vec_first(&field->monitor); - int32_t tgt, tgt_count = ecs_vec_count(&field->monitor); - for (tgt = 0; tgt < tgt_count; tgt ++) { - tgt_mon[tgt].monitor = tgt_mon[tgt].table_state; - } - } +bool flecs_defer_clone( + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_entity_t src, + bool clone_value) +{ + if (flecs_defer_cmd(stage)) { + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdClone; + cmd->id = src; + cmd->entity = entity; + cmd->is._1.clone_value = clone_value; + return true; } - - query->prev_match_count = query->match_count; + return false; } -/* Check if single match term has changed */ -static -bool flecs_query_check_match_monitor_term( - ecs_query_t *query, - ecs_query_table_match_t *match, - int32_t term) +bool flecs_defer_path( + ecs_stage_t *stage, + ecs_entity_t parent, + ecs_entity_t entity, + const char *name) { - ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - - if (flecs_query_get_match_monitor(query, match)) { + if (stage->defer > 0) { + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdPath; + cmd->entity = entity; + cmd->id = parent; + cmd->is._1.value = ecs_os_strdup(name); return true; } - - int32_t *monitor = match->monitor; - int32_t state = monitor[term]; - if (state == -1) { - return false; - } - - ecs_table_t *table = match->table; - if (table) { - int32_t *dirty_state = flecs_table_get_dirty_state( - query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - if (!term) { - return monitor[0] != dirty_state[0]; - } - } else if (!term) { - return false; - } - - flecs_table_column_t cur; - flecs_query_get_column_for_term(query, match, term - 1, &cur); - ecs_assert(cur.column != -1, ECS_INTERNAL_ERROR, NULL); - - return monitor[term] != flecs_table_get_dirty_state( - query->filter.world, cur.table)[cur.column + 1]; + return false; } -/* Check if any term for match has changed */ -static -bool flecs_query_check_match_monitor( - ecs_query_t *query, - ecs_query_table_match_t *match, - const ecs_iter_t *it) +bool flecs_defer_delete( + ecs_stage_t *stage, + ecs_entity_t entity) { - ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - - if (flecs_query_get_match_monitor(query, match)) { + if (flecs_defer_cmd(stage)) { + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdDelete; + cmd->entity = entity; return true; } - - int32_t *monitor = match->monitor; - ecs_table_t *table = match->table; - int32_t *dirty_state = NULL; - if (table) { - dirty_state = flecs_table_get_dirty_state( - query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - if (monitor[0] != dirty_state[0]) { - return true; - } - } - - bool has_flat = false, is_this = false; - const ecs_filter_t *filter = &query->filter; - ecs_world_t *world = filter->world; - int32_t i, j, field_count = filter->field_count; - int32_t *storage_columns = match->storage_columns; - int32_t *columns = it ? it->columns : NULL; - if (!columns) { - columns = match->columns; - } - ecs_vec_t *refs = &match->refs; - for (i = 0; i < field_count; i ++) { - int32_t mon = monitor[i + 1]; - if (mon == -1) { - continue; - } - - int32_t column = storage_columns[i]; - if (columns[i] >= 0) { - /* owned component */ - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - if (mon != dirty_state[column + 1]) { - return true; - } - continue; - } else if (column == -1) { - continue; /* owned but not a component */ - } - - column = columns[i]; - if (!column) { - /* Not matched */ - continue; - } - - ecs_assert(column < 0, ECS_INTERNAL_ERROR, NULL); - column = -column; - - /* Find term index from field index, which differ when using || */ - int32_t term_index = i; - if (filter->terms[i].field_index != i) { - for (j = i; j < filter->term_count; j ++) { - if (filter->terms[j].field_index == i) { - term_index = j; - break; - } - } - } - - is_this = ecs_term_match_this(&filter->terms[term_index]); - - /* Flattened fields are encoded by adding field_count to the column - * index of the parent component. */ - if (is_this && it && (column > field_count)) { - has_flat = true; - } else { - if (is_this) { - /* Component reached through traversal from this */ - int32_t ref_index = column - 1; - ecs_ref_t *ref = ecs_vec_get_t(refs, ecs_ref_t, ref_index); - if (ref->id != 0) { - ecs_ref_update(world, ref); - ecs_table_record_t *tr = ref->tr; - ecs_table_t *src_table = tr->hdr.table; - column = tr->index; - column = ecs_table_type_to_column_index(src_table, column); - int32_t *src_dirty_state = flecs_table_get_dirty_state( - world, src_table); - if (mon != src_dirty_state[column + 1]) { - return true; - } - } - } else { - /* Component from static source */ - ecs_entity_t src = match->sources[i]; - ecs_table_t *src_table = ecs_get_table(world, src); - ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); - column = ecs_table_type_to_column_index(src_table, column - 1); - int32_t *src_dirty_state = flecs_table_get_dirty_state( - world, src_table); - if (mon != src_dirty_state[column + 1]) { - return true; - } - } - } - } - - if (has_flat) { - ecs_entity_filter_t *ef = match->entity_filter; - flecs_flat_table_term_t *fields = ecs_vec_first(&ef->ft_terms); - ecs_entity_filter_iter_t *ent_it = it->priv.entity_iter; - int32_t cur_tgt = ent_it->target_count - 1; - field_count = ecs_vec_count(&ef->ft_terms); - - for (i = 0; i < field_count; i ++) { - flecs_flat_table_term_t *field = &fields[i]; - flecs_flat_monitor_t *fmon = ecs_vec_get_t(&field->monitor, - flecs_flat_monitor_t, cur_tgt); - if (fmon->monitor != fmon->table_state) { - return true; - } - } - } - return false; } -/* Check if any term for matched table has changed */ -static -bool flecs_query_check_table_monitor( - ecs_query_t *query, - ecs_query_table_t *table, - int32_t term) +bool flecs_defer_clear( + ecs_stage_t *stage, + ecs_entity_t entity) { - ecs_query_table_match_t *cur, *end = table->last->next; - - for (cur = table->first; cur != end; cur = cur->next) { - ecs_query_table_match_t *match = (ecs_query_table_match_t*)cur; - if (term == -1) { - if (flecs_query_check_match_monitor(query, match, NULL)) { - return true; - } - } else { - if (flecs_query_check_match_monitor_term(query, match, term)) { - return true; - } - } + if (flecs_defer_cmd(stage)) { + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); + cmd->kind = EcsCmdClear; + cmd->entity = entity; + return true; } - return false; } -static -bool flecs_query_check_query_monitor( - ecs_query_t *query) +bool flecs_defer_on_delete_action( + ecs_stage_t *stage, + ecs_id_t id, + ecs_entity_t action) { - ecs_table_cache_iter_t it; - if (flecs_table_cache_iter(&query->cache, &it)) { - ecs_query_table_t *qt; - while ((qt = flecs_table_cache_next(&it, ecs_query_table_t))) { - if (flecs_query_check_table_monitor(query, qt, -1)) { - return true; - } - } + if (flecs_defer_cmd(stage)) { + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdOnDeleteAction; + cmd->id = id; + cmd->entity = action; + return true; } - return false; } -static -void flecs_query_init_query_monitors( - ecs_query_t *query) +bool flecs_defer_enable( + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_id_t id, + bool enable) { - ecs_query_table_match_t *cur = query->list.first; - - /* Ensure each match has a monitor */ - for (; cur != NULL; cur = cur->next) { - ecs_query_table_match_t *match = (ecs_query_table_match_t*)cur; - flecs_query_get_match_monitor(query, match); + if (flecs_defer_cmd(stage)) { + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = enable ? EcsCmdEnable : EcsCmdDisable; + cmd->entity = entity; + cmd->id = id; + return true; } + return false; } -/* The group by function for cascade computes the tree depth for the table type. - * This causes tables in the query cache to be ordered by depth, which ensures - * breadth-first iteration order. */ -static -uint64_t flecs_query_group_by_cascade( +bool flecs_defer_bulk_new( ecs_world_t *world, - ecs_table_t *table, + ecs_stage_t *stage, + int32_t count, ecs_id_t id, - void *ctx) + const ecs_entity_t **ids_out) { - (void)id; - ecs_term_t *term = ctx; - ecs_entity_t rel = term->src.trav; - int32_t depth = flecs_relation_depth(world, rel, table); - return flecs_ito(uint64_t, depth); + if (flecs_defer_cmd(stage)) { + ecs_entity_t *ids = ecs_os_malloc(count * ECS_SIZEOF(ecs_entity_t)); + + /* Use ecs_new_id as this is thread safe */ + int i; + for (i = 0; i < count; i ++) { + ids[i] = ecs_new(world); + } + + *ids_out = ids; + + /* Store data in op */ + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdBulkNew; + cmd->id = id; + cmd->is._n.entities = ids; + cmd->is._n.count = count; + cmd->entity = 0; + return true; + } + return false; } -static -void flecs_query_add_ref( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_table_match_t *qm, - ecs_entity_t component, +bool flecs_defer_add( + ecs_stage_t *stage, ecs_entity_t entity, - ecs_size_t size) + ecs_id_t id) { - ecs_assert(entity != 0, ECS_INTERNAL_ERROR, NULL); - ecs_ref_t *ref = ecs_vec_append_t(&world->allocator, &qm->refs, ecs_ref_t); - ecs_assert(entity != 0, ECS_INTERNAL_ERROR, NULL); - - if (size) { - *ref = ecs_ref_init_id(world, entity, component); - } else { - *ref = (ecs_ref_t){ - .entity = entity, - .id = 0 - }; + if (flecs_defer_cmd(stage)) { + ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); + cmd->kind = EcsCmdAdd; + cmd->id = id; + cmd->entity = entity; + return true; } - - query->flags |= EcsQueryHasRefs; + return false; } -static -ecs_query_table_match_t* flecs_query_add_table_match( - ecs_query_t *query, - ecs_query_table_t *qt, - ecs_table_t *table) +bool flecs_defer_remove( + ecs_stage_t *stage, + ecs_entity_t entity, + ecs_id_t id) { - /* Add match for table. One table can have more than one match, if - * the query contains wildcards. */ - ecs_query_table_match_t *qm = flecs_query_cache_add(query->filter.world, qt); - qm->table = table; - - qm->columns = flecs_balloc(&query->allocators.columns); - qm->storage_columns = flecs_balloc(&query->allocators.columns); - qm->ids = flecs_balloc(&query->allocators.ids); - qm->sources = flecs_balloc(&query->allocators.sources); - - /* Insert match to iteration list if table is not empty */ - if (!table || ecs_table_count(table) != 0) { - flecs_query_insert_table_node(query, qm); + if (flecs_defer_cmd(stage)) { + ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); + cmd->kind = EcsCmdRemove; + cmd->id = id; + cmd->entity = entity; + return true; } - - return qm; + return false; } -static -void flecs_query_set_table_match( +void* flecs_defer_set( ecs_world_t *world, - ecs_query_t *query, - ecs_query_table_match_t *qm, - ecs_table_t *table, - ecs_iter_t *it) + ecs_stage_t *stage, + ecs_cmd_kind_t cmd_kind, + ecs_entity_t entity, + ecs_id_t id, + ecs_size_t size, + void *value, + bool *is_new) { - ecs_allocator_t *a = &world->allocator; - ecs_filter_t *filter = &query->filter; - int32_t i, term_count = filter->term_count; - int32_t field_count = filter->field_count; - ecs_term_t *terms = filter->terms; + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); - /* Reset resources in case this is an existing record */ - ecs_vec_reset_t(a, &qm->refs, ecs_ref_t); - ecs_os_memcpy_n(qm->columns, it->columns, int32_t, field_count); - ecs_os_memcpy_n(qm->ids, it->ids, ecs_id_t, field_count); - ecs_os_memcpy_n(qm->sources, it->sources, ecs_entity_t, field_count); + /* 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. */ + if (world->flags & EcsWorldMultiThreaded) { + ti = ecs_get_type_info(world, id); + } else { + /* When not in multi threaded mode, it's safe to find or + * 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. */ + ti = idr->type_info; + } + } else { + ti = idr->type_info; + } - if (table) { - /* Initialize storage columns for faster access to component storage */ - for (i = 0; i < field_count; i ++) { - if (terms[i].inout == EcsInOutNone) { - qm->storage_columns[i] = -1; - continue; + /* If the id isn't associated with a type, we can't set anything */ + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, + "provided component is not a type"); + + /* Make sure the size of the value equals the type size */ + ecs_assert(!size || size == ti->size, ECS_INVALID_PARAMETER, + "mismatching size specified for component in ensure/emplace/set"); + size = ti->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; + 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) { + const ecs_table_record_t *tr = flecs_id_record_get_table( + idr, table); + if (tr) { + if (tr->column != -1) { + /* Entity has the component */ + existing = table->data.columns[tr->column].data; + existing = ECS_ELEM(existing, size, + ECS_RECORD_TO_ROW(r->row)); + } else { + ecs_assert(idr->flags & EcsIdIsSparse, + ECS_NOT_A_COMPONENT, NULL); + existing = flecs_sparse_get_any(idr->sparse, 0, entity); + } } + } + } + + /* Get existing value from storage */ + void *cmd_value = existing; + bool emplace = cmd_kind == EcsCmdEmplace; + + /* 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->cmd->stack; + cmd_value = flecs_stack_alloc(stack, size, ti->alignment); - int32_t column = qm->columns[i]; - if (column > 0) { - qm->storage_columns[i] = ecs_table_type_to_column_index(table, - qm->columns[i] - 1); + /* 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 { - /* Shared field (not from table) */ - qm->storage_columns[i] = -2; + 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 */ - flecs_entity_filter_init(world, &qm->entity_filter, filter, - table, qm->ids, qm->columns); + /* Check if entity inherits component */ + void *base = NULL; + if (table && (table->flags & EcsTableHasIsA)) { + base = flecs_get_base_component(world, table, id, idr, 0); + } - if (qm->entity_filter) { - query->flags &= ~EcsQueryTrivialIter; + 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 (table->flags & EcsTableHasUnion) { - query->flags &= ~EcsQueryTrivialIter; + } + + if (!cmd) { + /* 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 a command when the entity was + * already deleted. */ + return cmd_value; } + cmd = flecs_cmd_new(stage); } - /* Add references for substituted terms */ - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (!ecs_term_match_this(term)) { - /* non-This terms are set during iteration */ - continue; + 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; + + if (is_new) { + *is_new = true; + } + } 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 == EcsCmdSet) { + cmd->kind = EcsCmdAddModified; + } else { + cmd->kind = EcsCmdAdd; } + cmd->id = id; + cmd->entity = entity; - int32_t field = terms[i].field_index; - ecs_entity_t src = it->sources[field]; - ecs_size_t size = 0; - if (it->sizes) { - size = it->sizes[field]; + if (is_new) { + *is_new = false; } - if (src) { - ecs_id_t id = it->ids[field]; - ecs_assert(ecs_is_valid(world, src), ECS_INTERNAL_ERROR, NULL); + } - if (id) { - flecs_query_add_ref(world, query, qm, id, src, size); + return cmd_value; +error: + return NULL; +} - /* Use column index to bind term and ref */ - if (qm->columns[field] != 0) { - qm->columns[field] = -ecs_vec_count(&qm->refs); - } +void flecs_enqueue( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_event_desc_t *desc) +{ + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdEvent; + cmd->entity = desc->entity; + + ecs_stack_t *stack = &stage->cmd->stack; + ecs_event_desc_t *desc_cmd = flecs_stack_alloc_t(stack, ecs_event_desc_t); + ecs_os_memcpy_t(desc_cmd, desc, ecs_event_desc_t); + + if (desc->ids && desc->ids->count != 0) { + ecs_type_t *type_cmd = flecs_stack_alloc_t(stack, ecs_type_t); + int32_t id_count = desc->ids->count; + type_cmd->count = id_count; + type_cmd->array = flecs_stack_alloc_n(stack, ecs_id_t, id_count); + ecs_os_memcpy_n(type_cmd->array, desc->ids->array, ecs_id_t, id_count); + desc_cmd->ids = type_cmd; + } else { + desc_cmd->ids = NULL; + } + + cmd->is._1.value = desc_cmd; + cmd->is._1.size = ECS_SIZEOF(ecs_event_desc_t); + + if (desc->param || desc->const_param) { + ecs_assert(!(desc->const_param && desc->param), ECS_INVALID_PARAMETER, + "cannot set param and const_param at the same time"); + + const ecs_type_info_t *ti = ecs_get_type_info(world, desc->event); + ecs_assert(ti != NULL, ECS_INVALID_PARAMETER, + "can only enqueue events with data for events that are components"); + + void *param_cmd = flecs_stack_alloc(stack, ti->size, ti->alignment); + ecs_assert(param_cmd != NULL, ECS_INTERNAL_ERROR, NULL); + if (desc->param) { + if (ti->hooks.move_ctor) { + ti->hooks.move_ctor(param_cmd, desc->param, 1, ti); + } else { + ecs_os_memcpy(param_cmd, desc->param, ti->size); + } + } else { + if (ti->hooks.copy_ctor) { + ti->hooks.copy_ctor(param_cmd, desc->const_param, 1, ti); + } else { + ecs_os_memcpy(param_cmd, desc->const_param, ti->size); } } + + desc_cmd->param = param_cmd; + desc_cmd->const_param = NULL; } } -static -ecs_query_table_t* flecs_query_table_insert( +void flecs_stage_merge_post_frame( ecs_world_t *world, - ecs_query_t *query, - ecs_table_t *table) + ecs_stage_t *stage) { - ecs_query_table_t *qt = flecs_bcalloc(&world->allocators.query_table); - if (table) { - qt->table_id = table->id; - } else { - qt->table_id = 0; + /* Execute post frame actions */ + int32_t i, count = ecs_vec_count(&stage->post_frame_actions); + ecs_action_elem_t *elems = ecs_vec_first(&stage->post_frame_actions); + for (i = 0; i < count; i ++) { + elems[i].action(world, elems[i].ctx); } - ecs_table_cache_insert(&query->cache, table, &qt->hdr); - return qt; + + ecs_vec_clear(&stage->post_frame_actions); } -/** Populate query cache with tables */ static -void flecs_query_match_tables( - ecs_world_t *world, - ecs_query_t *query) +void flecs_commands_init( + ecs_stage_t *stage, + ecs_commands_t *cmd) { - ecs_table_t *table = NULL; - ecs_query_table_t *qt = NULL; + flecs_stack_init(&cmd->stack); + ecs_vec_init_t(&stage->allocator, &cmd->queue, ecs_cmd_t, 0); + flecs_sparse_init_t(&cmd->entries, &stage->allocator, + &stage->allocators.cmd_entry_chunk, ecs_cmd_entry_t); +} - ecs_iter_t it = ecs_filter_iter(world, &query->filter); - ECS_BIT_SET(it.flags, EcsIterIsInstanced); - ECS_BIT_SET(it.flags, EcsIterNoData); - ECS_BIT_SET(it.flags, EcsIterTableOnly); - ECS_BIT_SET(it.flags, EcsIterEntityOptional); +static +void flecs_commands_fini( + ecs_stage_t *stage, + ecs_commands_t *cmd) +{ + /* Make sure stage has no unmerged data */ + ecs_assert(ecs_vec_count(&stage->cmd->queue) == 0, ECS_INTERNAL_ERROR, NULL); - while (ecs_filter_next(&it)) { - if ((table != it.table) || (!it.table && !qt)) { - /* New table matched, add record to cache */ - table = it.table; - qt = flecs_query_table_insert(world, query, table); - } + flecs_stack_fini(&cmd->stack); + ecs_vec_fini_t(&stage->allocator, &cmd->queue, ecs_cmd_t); + flecs_sparse_fini(&cmd->entries); +} - ecs_query_table_match_t *qm = flecs_query_add_table_match(query, qt, table); - flecs_query_set_table_match(world, query, qm, table, &it); - } +void flecs_commands_push( + ecs_stage_t *stage) +{ + int32_t sp = ++ stage->cmd_sp; + ecs_assert(sp < ECS_MAX_DEFER_STACK, ECS_INTERNAL_ERROR, NULL); + stage->cmd = &stage->cmd_stack[sp]; +} + +void flecs_commands_pop( + ecs_stage_t *stage) +{ + int32_t sp = -- stage->cmd_sp; + ecs_assert(sp >= 0, ECS_INTERNAL_ERROR, NULL); + stage->cmd = &stage->cmd_stack[sp]; +} + +ecs_entity_t flecs_stage_set_system( + ecs_stage_t *stage, + ecs_entity_t system) +{ + ecs_entity_t old = stage->system; + stage->system = system; + return old; } static -bool flecs_query_match_table( - ecs_world_t *world, - ecs_query_t *query, - ecs_table_t *table) +ecs_stage_t* flecs_stage_new( + ecs_world_t *world) { - if (!ecs_map_is_init(&query->cache.index)) { - return false; - } + flecs_poly_assert(world, ecs_world_t); + ecs_stage_t *stage = ecs_os_calloc(sizeof(ecs_stage_t)); + flecs_poly_init(stage, ecs_stage_t); - ecs_query_table_t *qt = NULL; - ecs_filter_t *filter = &query->filter; - int var_id = ecs_filter_find_this_var(filter); - if (var_id == -1) { - /* If query doesn't match with This term, it can't match with tables */ - return false; - } + stage->world = world; + stage->thread_ctx = world; - ecs_iter_t it = flecs_filter_iter_w_flags(world, filter, EcsIterMatchVar| - EcsIterIsInstanced|EcsIterNoData|EcsIterEntityOptional); - ecs_iter_set_var_as_table(&it, var_id, table); + flecs_stack_init(&stage->allocators.iter_stack); + flecs_stack_init(&stage->allocators.deser_stack); + flecs_allocator_init(&stage->allocator); + flecs_ballocator_init_n(&stage->allocators.cmd_entry_chunk, ecs_cmd_entry_t, + FLECS_SPARSE_PAGE_SIZE); + flecs_ballocator_init_t(&stage->allocators.query_impl, ecs_query_impl_t); + flecs_ballocator_init_t(&stage->allocators.query_cache, ecs_query_cache_t); - while (ecs_filter_next(&it)) { - ecs_assert(it.table == table, ECS_INTERNAL_ERROR, NULL); - if (qt == NULL) { - table = it.table; - qt = flecs_query_table_insert(world, query, table); - } + ecs_allocator_t *a = &stage->allocator; + ecs_vec_init_t(a, &stage->post_frame_actions, ecs_action_elem_t, 0); - ecs_query_table_match_t *qm = flecs_query_add_table_match(query, qt, table); - flecs_query_set_table_match(world, query, qm, table, &it); + int32_t i; + for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) { + flecs_commands_init(stage, &stage->cmd_stack[i]); } - return qt != NULL; + stage->cmd = &stage->cmd_stack[0]; + return stage; } -ECS_SORT_TABLE_WITH_COMPARE(_, flecs_query_sort_table_generic, order_by, static) - static -void flecs_query_sort_table( +void flecs_stage_free( ecs_world_t *world, - ecs_table_t *table, - int32_t column_index, - ecs_order_by_action_t compare, - ecs_sort_table_action_t sort) + ecs_stage_t *stage) { - ecs_data_t *data = &table->data; - if (!ecs_vec_count(&data->entities)) { - /* Nothing to sort */ - return; - } + (void)world; + flecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(stage, ecs_stage_t); - int32_t count = flecs_table_data_count(data); - if (count < 2) { - return; - } + flecs_poly_fini(stage, ecs_stage_t); - ecs_entity_t *entities = ecs_vec_first(&data->entities); + ecs_allocator_t *a = &stage->allocator; + + ecs_vec_fini_t(a, &stage->post_frame_actions, ecs_action_elem_t); + ecs_vec_fini(NULL, &stage->variables, 0); + ecs_vec_fini(NULL, &stage->operations, 0); - void *ptr = NULL; - int32_t size = 0; - if (column_index != -1) { - ecs_column_t *column = &data->columns[column_index]; - ecs_type_info_t *ti = column->ti; - size = ti->size; - ptr = ecs_vec_first(&column->data); + int32_t i; + for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) { + flecs_commands_fini(stage, &stage->cmd_stack[i]); } - if (sort) { - sort(world, table, entities, ptr, size, 0, count - 1, compare); - } else { - flecs_query_sort_table_generic(world, table, entities, ptr, size, 0, count - 1, compare); - } + flecs_stack_fini(&stage->allocators.iter_stack); + flecs_stack_fini(&stage->allocators.deser_stack); + flecs_ballocator_fini(&stage->allocators.cmd_entry_chunk); + flecs_ballocator_fini(&stage->allocators.query_impl); + flecs_ballocator_fini(&stage->allocators.query_cache); + flecs_allocator_fini(&stage->allocator); + + ecs_os_free(stage); } -/* Helper struct for building sorted table ranges */ -typedef struct sort_helper_t { - ecs_query_table_match_t *match; - ecs_entity_t *entities; - const void *ptr; - int32_t row; - int32_t elem_size; - int32_t count; - bool shared; -} sort_helper_t; +ecs_allocator_t* flecs_stage_get_allocator( + ecs_world_t *world) +{ + ecs_stage_t *stage = flecs_stage_from_world( + ECS_CONST_CAST(ecs_world_t**, &world)); + return &stage->allocator; +} -static -const void* ptr_from_helper( - sort_helper_t *helper) +ecs_stack_t* flecs_stage_get_stack_allocator( + ecs_world_t *world) { - ecs_assert(helper->row < helper->count, ECS_INTERNAL_ERROR, NULL); - ecs_assert(helper->elem_size >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(helper->row >= 0, ECS_INTERNAL_ERROR, NULL); - if (helper->shared) { - return helper->ptr; - } else { - return ECS_ELEM(helper->ptr, helper->elem_size, helper->row); - } + ecs_stage_t *stage = flecs_stage_from_world( + ECS_CONST_CAST(ecs_world_t**, &world)); + return &stage->allocators.iter_stack; } -static -ecs_entity_t e_from_helper( - sort_helper_t *helper) +ecs_world_t* ecs_stage_new( + ecs_world_t *world) { - if (helper->row < helper->count) { - return helper->entities[helper->row]; - } else { - return 0; - } + ecs_stage_t *stage = flecs_stage_new(world); + stage->id = -1; + + flecs_defer_begin(world, stage); + + return (ecs_world_t*)stage; } -static -void flecs_query_build_sorted_table_range( - ecs_query_t *query, - ecs_query_table_list_t *list) +void ecs_stage_free( + ecs_world_t *world) { - ecs_world_t *world = query->filter.world; - ecs_assert(!(world->flags & EcsWorldMultiThreaded), ECS_UNSUPPORTED, - "cannot sort query in multithreaded mode"); + flecs_poly_assert(world, ecs_stage_t); + ecs_stage_t *stage = (ecs_stage_t*)world; + ecs_check(stage->id == -1, ECS_INVALID_PARAMETER, + "cannot free stage that's owned by world"); + flecs_stage_free(stage->world, stage); +error: + return; +} - ecs_entity_t id = query->order_by_component; - ecs_order_by_action_t compare = query->order_by; - int32_t table_count = list->info.table_count; - if (!table_count) { - return; - } +void ecs_set_stage_count( + ecs_world_t *world, + int32_t stage_count) +{ + flecs_poly_assert(world, ecs_world_t); - ecs_vec_init_if_t(&query->table_slices, ecs_query_table_match_t); - int32_t to_sort = 0; - int32_t order_by_term = query->order_by_term; + /* World must have at least one default stage */ + ecs_assert(stage_count >= 1 || (world->flags & EcsWorldFini), + ECS_INTERNAL_ERROR, NULL); - sort_helper_t *helper = flecs_alloc_n( - &world->allocator, sort_helper_t, table_count); - ecs_query_table_match_t *cur, *end = list->last->next; - for (cur = list->first; cur != end; cur = cur->next) { - ecs_table_t *table = cur->table; - ecs_data_t *data = &table->data; + const ecs_entity_t *lookup_path = NULL; + if (world->stage_count >= 1) { + lookup_path = world->stages[0]->lookup_path; + } - ecs_assert(ecs_table_count(table) != 0, ECS_INTERNAL_ERROR, NULL); + int32_t i, count = world->stage_count; + if (stage_count < count) { + for (i = stage_count; i < count; i ++) { + /* If stage contains a thread handle, ecs_set_threads was used to + * create the stages. ecs_set_threads and ecs_set_stage_count should + * not be mixed. */ + ecs_stage_t *stage = world->stages[i]; + flecs_poly_assert(stage, ecs_stage_t); + ecs_check(stage->thread == 0, ECS_INVALID_OPERATION, + "cannot mix using set_stage_count and set_threads"); + flecs_stage_free(world, stage); + } + } - if (id) { - const ecs_term_t *term = &query->filter.terms[order_by_term]; - int32_t field = term->field_index; - int32_t column = cur->columns[field]; - ecs_size_t size = query->filter.sizes[field]; - ecs_assert(column != 0, ECS_INTERNAL_ERROR, NULL); - if (column >= 0) { - column = table->column_map[column - 1]; - ecs_vec_t *vec = &data->columns[column].data; - helper[to_sort].ptr = ecs_vec_first(vec); - helper[to_sort].elem_size = size; - helper[to_sort].shared = false; - } else { - ecs_entity_t src = cur->sources[field]; - ecs_assert(src != 0, ECS_INTERNAL_ERROR, NULL); - ecs_record_t *r = flecs_entities_get(world, src); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + if (stage_count) { + world->stages = ecs_os_realloc_n( + world->stages, ecs_stage_t*, stage_count); - if (term->src.flags & EcsUp) { - ecs_entity_t base = 0; - ecs_search_relation(world, r->table, 0, id, - EcsIsA, term->src.flags & EcsTraverseFlags, &base, 0, 0); - if (base && base != src) { /* Component could be inherited */ - r = flecs_entities_get(world, base); - } - } + for (i = count; i < stage_count; i ++) { + ecs_stage_t *stage = world->stages[i] = flecs_stage_new(world); + stage->id = i; - helper[to_sort].ptr = ecs_table_get_id( - world, r->table, id, ECS_RECORD_TO_ROW(r->row)); - helper[to_sort].elem_size = size; - helper[to_sort].shared = true; - } - ecs_assert(helper[to_sort].ptr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(helper[to_sort].elem_size != 0, ECS_INTERNAL_ERROR, NULL); - } else { - helper[to_sort].ptr = NULL; - helper[to_sort].elem_size = 0; - helper[to_sort].shared = false; + /* Set thread_ctx to stage, as this stage might be used in a + * multithreaded context */ + stage->thread_ctx = (ecs_world_t*)stage; + stage->thread = 0; } - - helper[to_sort].match = cur; - helper[to_sort].entities = ecs_vec_first(&data->entities); - helper[to_sort].row = 0; - helper[to_sort].count = ecs_table_count(table); - to_sort ++; + } else { + /* Set to NULL to prevent double frees */ + ecs_os_free(world->stages); + world->stages = NULL; } - ecs_assert(to_sort != 0, ECS_INTERNAL_ERROR, NULL); - - bool proceed; - do { - int32_t j, min = 0; - proceed = true; - - ecs_entity_t e1; - while (!(e1 = e_from_helper(&helper[min]))) { - min ++; - if (min == to_sort) { - proceed = false; - break; - } - } - - if (!proceed) { - break; - } - - for (j = min + 1; j < to_sort; j++) { - ecs_entity_t e2 = e_from_helper(&helper[j]); - if (!e2) { - continue; - } - - const void *ptr1 = ptr_from_helper(&helper[min]); - const void *ptr2 = ptr_from_helper(&helper[j]); - - if (compare(e1, ptr1, e2, ptr2) > 0) { - min = j; - e1 = e_from_helper(&helper[min]); - } - } - - sort_helper_t *cur_helper = &helper[min]; - if (!cur || cur->columns != cur_helper->match->columns) { - cur = ecs_vec_append_t(NULL, &query->table_slices, - ecs_query_table_match_t); - *cur = *(cur_helper->match); - cur->offset = cur_helper->row; - cur->count = 1; - } else { - cur->count ++; - } - - cur_helper->row ++; - } while (proceed); - - /* Iterate through the vector of slices to set the prev/next ptrs. This - * can't be done while building the vector, as reallocs may occur */ - int32_t i, count = ecs_vec_count(&query->table_slices); - ecs_query_table_match_t *nodes = ecs_vec_first(&query->table_slices); - for (i = 0; i < count; i ++) { - nodes[i].prev = &nodes[i - 1]; - nodes[i].next = &nodes[i + 1]; + /* Regardless of whether the stage was just initialized or not, when the + * ecs_set_stage_count function is called, all stages inherit the auto_merge + * property from the world */ + for (i = 0; i < stage_count; i ++) { + world->stages[i]->lookup_path = lookup_path; } - nodes[0].prev = NULL; - nodes[i - 1].next = NULL; - - flecs_free_n(&world->allocator, sort_helper_t, table_count, helper); + world->stage_count = stage_count; +error: + return; } -static -void flecs_query_build_sorted_tables( - ecs_query_t *query) +int32_t ecs_get_stage_count( + const ecs_world_t *world) { - ecs_vec_clear(&query->table_slices); + world = ecs_get_world(world); + return world->stage_count; +} - if (query->group_by) { - /* Populate sorted node list in grouping order */ - ecs_query_table_match_t *cur = query->list.first; - if (cur) { - do { - /* Find list for current group */ - uint64_t group_id = cur->group_id; - ecs_query_table_list_t *list = ecs_map_get_deref( - &query->groups, ecs_query_table_list_t, group_id); - ecs_assert(list != NULL, ECS_INTERNAL_ERROR, NULL); +int32_t ecs_stage_get_id( + const ecs_world_t *world) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - /* Sort tables in current group */ - flecs_query_build_sorted_table_range(query, list); + if (flecs_poly_is(world, ecs_stage_t)) { + ecs_stage_t *stage = ECS_CONST_CAST(ecs_stage_t*, world); - /* Find next group to sort */ - cur = list->last->next; - } while (cur); - } + /* Index 0 is reserved for main stage */ + return stage->id; + } else if (flecs_poly_is(world, ecs_world_t)) { + return 0; } else { - flecs_query_build_sorted_table_range(query, &query->list); + ecs_throw(ECS_INTERNAL_ERROR, NULL); } +error: + return 0; } -static -void flecs_query_sort_tables( +ecs_world_t* ecs_get_stage( + const ecs_world_t *world, + int32_t stage_id) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(world->stage_count > stage_id, ECS_INVALID_PARAMETER, NULL); + return (ecs_world_t*)world->stages[stage_id]; +error: + return NULL; +} + +bool ecs_readonly_begin( ecs_world_t *world, - ecs_query_t *query) + bool multi_threaded) { - ecs_order_by_action_t compare = query->order_by; - if (!compare) { - return; - } + flecs_poly_assert(world, ecs_world_t); - ecs_sort_table_action_t sort = query->sort_table; - - ecs_entity_t order_by_component = query->order_by_component; - int32_t order_by_term = query->order_by_term; + flecs_process_pending_tables(world); - /* Iterate over non-empty tables. Don't bother with empty tables as they - * have nothing to sort */ + ecs_dbg_3("#[bold]readonly"); + ecs_log_push_3(); - bool tables_sorted = false; + int32_t i, count = ecs_get_stage_count(world); + for (i = 0; i < count; i ++) { + ecs_stage_t *stage = world->stages[i]; + stage->lookup_path = world->stages[0]->lookup_path; + ecs_assert(stage->defer == 0, ECS_INVALID_OPERATION, + "deferred mode cannot be enabled when entering readonly mode"); + flecs_defer_begin(world, stage); + } - ecs_id_record_t *idr = flecs_id_record_get(world, order_by_component); - ecs_table_cache_iter_t it; - ecs_query_table_t *qt; - flecs_table_cache_iter(&query->cache, &it); + bool is_readonly = ECS_BIT_IS_SET(world->flags, EcsWorldReadonly); - while ((qt = flecs_table_cache_next(&it, ecs_query_table_t))) { - ecs_table_t *table = qt->hdr.table; - bool dirty = false; + /* From this point on, the world is "locked" for mutations, and it is only + * allowed to enqueue commands from stages */ + ECS_BIT_SET(world->flags, EcsWorldReadonly); + ECS_BIT_COND(world->flags, EcsWorldMultiThreaded, multi_threaded); - if (flecs_query_check_table_monitor(query, qt, 0)) { - dirty = true; - } + return is_readonly; +} - int32_t column = -1; - if (order_by_component) { - if (flecs_query_check_table_monitor(query, qt, order_by_term + 1)) { - dirty = true; - } +void ecs_readonly_end( + ecs_world_t *world) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(world->flags & EcsWorldReadonly, ECS_INVALID_OPERATION, + "world is not in readonly mode"); - if (dirty) { - column = -1; + /* After this it is safe again to mutate the world directly */ + ECS_BIT_CLEAR(world->flags, EcsWorldReadonly); + ECS_BIT_CLEAR(world->flags, EcsWorldMultiThreaded); - const ecs_table_record_t *tr = flecs_id_record_get_table( - idr, table); - if (tr) { - column = tr->column; - } + ecs_log_pop_3(); - if (column == -1) { - /* Component is shared, no sorting is needed */ - dirty = false; - } - } - } + flecs_stage_merge(world); +error: + return; +} - if (!dirty) { - continue; - } +void ecs_merge( + ecs_world_t *world) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(flecs_poly_is(world, ecs_world_t) || + flecs_poly_is(world, ecs_stage_t), ECS_INVALID_PARAMETER, NULL); + flecs_stage_merge(world); +error: + return; +} - /* Something has changed, sort the table. Prefers using - * flecs_query_sort_table when available */ - flecs_query_sort_table(world, table, column, compare, sort); - tables_sorted = true; - } +bool ecs_stage_is_readonly( + const ecs_world_t *stage) +{ + const ecs_world_t *world = ecs_get_world(stage); - if (tables_sorted || query->match_count != query->prev_match_count) { - flecs_query_build_sorted_tables(query); - query->match_count ++; /* Increase version if tables changed */ + if (flecs_poly_is(stage, ecs_stage_t)) { + if (((const ecs_stage_t*)stage)->id == -1) { + /* Stage is not owned by world, so never readonly */ + return false; + } } -} -static -bool flecs_query_has_refs( - ecs_query_t *query) -{ - ecs_term_t *terms = query->filter.terms; - int32_t i, count = query->filter.term_count; - for (i = 0; i < count; i ++) { - if (terms[i].src.flags & (EcsUp | EcsIsEntity)) { + if (world->flags & EcsWorldReadonly) { + if (flecs_poly_is(stage, ecs_world_t)) { + return true; + } + } else { + if (flecs_poly_is(stage, ecs_stage_t)) { return true; } } @@ -18829,32637 +17482,37992 @@ bool flecs_query_has_refs( return false; } -static -void flecs_query_for_each_component_monitor( - ecs_world_t *world, - ecs_query_t *query, - void(*callback)( - ecs_world_t* world, - ecs_id_t id, - ecs_query_t *query)) +bool ecs_is_deferred( + const ecs_world_t *world) { - ecs_term_t *terms = query->filter.terms; - int32_t i, count = query->filter.term_count; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); + return stage->defer > 0; +error: + return false; +} - for (i = 0; i < count; i++) { - ecs_term_t *term = &terms[i]; - ecs_term_id_t *src = &term->src; +/** + * @file value.c + * @brief Utility functions to work with non-trivial pointers of user types. + */ - if (src->flags & EcsUp) { - callback(world, ecs_pair(src->trav, EcsWildcard), query); - if (src->trav != EcsIsA) { - callback(world, ecs_pair(EcsIsA, EcsWildcard), query); - } - callback(world, term->id, query); - } else if (src->flags & EcsSelf && !ecs_term_match_this(term)) { - callback(world, term->id, query); - } +int ecs_value_init_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void *ptr) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); + (void)world; + + ecs_xtor_t ctor; + if ((ctor = ti->hooks.ctor)) { + ctor(ptr, 1, ti); + } else { + ecs_os_memset(ptr, 0, ti->size); } + + return 0; +error: + return -1; } -static -bool flecs_query_is_term_id_supported( - ecs_term_id_t *term_id) +int ecs_value_init( + const ecs_world_t *world, + ecs_entity_t type, + void *ptr) { - if (!(term_id->flags & EcsIsVariable)) { - return true; - } - if (ecs_id_is_wildcard(term_id->id)) { - return true; - } - return false; + flecs_poly_assert(world, ecs_world_t); + const ecs_type_info_t *ti = ecs_get_type_info(world, type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); + return ecs_value_init_w_type_info(world, ti, ptr); +error: + return -1; } -static -int flecs_query_process_signature( +void* ecs_value_new_w_type_info( ecs_world_t *world, - ecs_query_t *query) + const ecs_type_info_t *ti) { - ecs_term_t *terms = query->filter.terms; - int32_t i, count = query->filter.term_count; - - for (i = 0; i < count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_term_id_t *first = &term->first; - ecs_term_id_t *src = &term->src; - ecs_term_id_t *second = &term->second; - ecs_inout_kind_t inout = term->inout; - - bool is_src_ok = flecs_query_is_term_id_supported(src); - bool is_first_ok = flecs_query_is_term_id_supported(first); - bool is_second_ok = flecs_query_is_term_id_supported(second); - - (void)first; - (void)second; - (void)is_src_ok; - (void)is_first_ok; - (void)is_second_ok; - - /* Queries do not support named variables */ - ecs_check(is_src_ok || ecs_term_match_this(term), - ECS_UNSUPPORTED, NULL); - ecs_check(is_first_ok, ECS_UNSUPPORTED, NULL); - ecs_check(is_second_ok, ECS_UNSUPPORTED, NULL); - ecs_check(!(src->flags & EcsFilter), ECS_INVALID_PARAMETER, - "invalid usage of Filter for query"); - - if (inout != EcsIn && inout != EcsInOutNone) { - /* Non-this terms default to EcsIn */ - if (ecs_term_match_this(term) || inout != EcsInOutDefault) { - query->flags |= EcsQueryHasOutTerms; - } - - bool match_non_this = !ecs_term_match_this(term) || - (term->src.flags & EcsUp); - if (match_non_this && inout != EcsInOutDefault) { - query->flags |= EcsQueryHasNonThisOutTerms; - } - } - - if (src->flags & EcsCascade) { - /* Query can only have one cascade column */ - ecs_assert(query->cascade_by == 0, ECS_INVALID_PARAMETER, NULL); - query->cascade_by = i + 1; - } - } - - query->flags |= (ecs_flags32_t)(flecs_query_has_refs(query) * EcsQueryHasRefs); + flecs_poly_assert(world, ecs_world_t); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); + (void)world; - if (!(query->flags & EcsQueryIsSubquery)) { - flecs_query_for_each_component_monitor(world, query, flecs_monitor_register); + void *result = flecs_alloc(&world->allocator, ti->size); + if (ecs_value_init_w_type_info(world, ti, result) != 0) { + flecs_free(&world->allocator, ti->size, result); + goto error; } - return 0; + return result; error: - return -1; + return NULL; } -/** When a table becomes empty remove it from the query list, or vice versa. */ -static -void flecs_query_update_table( - ecs_query_t *query, - ecs_table_t *table, - bool empty) +void* ecs_value_new( + ecs_world_t *world, + ecs_entity_t type) { - int32_t prev_count = ecs_query_table_count(query); - ecs_table_cache_set_empty(&query->cache, table, empty); - int32_t cur_count = ecs_query_table_count(query); - - if (prev_count != cur_count) { - ecs_query_table_t *qt = ecs_table_cache_get(&query->cache, table); - ecs_assert(qt != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_query_table_match_t *cur, *next; + flecs_poly_assert(world, ecs_world_t); + const ecs_type_info_t *ti = ecs_get_type_info(world, type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); - for (cur = qt->first; cur != NULL; cur = next) { - next = cur->next_match; + return ecs_value_new_w_type_info(world, ti); +error: + return NULL; +} - if (empty) { - ecs_assert(ecs_table_count(table) == 0, - ECS_INTERNAL_ERROR, NULL); +int ecs_value_fini_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void *ptr) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); + (void)world; - flecs_query_remove_table_node(query, cur); - } else { - ecs_assert(ecs_table_count(table) != 0, - ECS_INTERNAL_ERROR, NULL); - flecs_query_insert_table_node(query, cur); - } - } + ecs_xtor_t dtor; + if ((dtor = ti->hooks.dtor)) { + dtor(ptr, 1, ti); } - ecs_assert(cur_count || query->list.first == NULL, - ECS_INTERNAL_ERROR, NULL); + return 0; +error: + return -1; } -static -void flecs_query_add_subquery( - ecs_world_t *world, - ecs_query_t *parent, - ecs_query_t *subquery) +int ecs_value_fini( + const ecs_world_t *world, + ecs_entity_t type, + void* ptr) { - ecs_vec_init_if_t(&parent->subqueries, ecs_query_t*); - ecs_query_t **elem = ecs_vec_append_t( - NULL, &parent->subqueries, ecs_query_t*); - *elem = subquery; - - ecs_table_cache_t *cache = &parent->cache; - ecs_table_cache_iter_t it; - ecs_query_table_t *qt; - flecs_table_cache_all_iter(cache, &it); - while ((qt = flecs_table_cache_next(&it, ecs_query_table_t))) { - flecs_query_match_table(world, subquery, qt->hdr.table); - } + flecs_poly_assert(world, ecs_world_t); + (void)world; + const ecs_type_info_t *ti = ecs_get_type_info(world, type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); + return ecs_value_fini_w_type_info(world, ti, ptr); +error: + return -1; } -static -void flecs_query_notify_subqueries( +int ecs_value_free( ecs_world_t *world, - ecs_query_t *query, - ecs_query_event_t *event) + ecs_entity_t type, + void* ptr) { - if (query->subqueries.array) { - ecs_query_t **queries = ecs_vec_first(&query->subqueries); - int32_t i, count = ecs_vec_count(&query->subqueries); + flecs_poly_assert(world, ecs_world_t); + const ecs_type_info_t *ti = ecs_get_type_info(world, type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); + if (ecs_value_fini_w_type_info(world, ti, ptr) != 0) { + goto error; + } - ecs_query_event_t sub_event = *event; - sub_event.parent_query = query; + flecs_free(&world->allocator, ti->size, ptr); - for (i = 0; i < count; i ++) { - ecs_query_t *sub = queries[i]; - flecs_query_notify(world, sub, &sub_event); - } - } + return 0; +error: + return -1; } -/* Remove table */ -static -void flecs_query_table_match_free( - ecs_query_t *query, - ecs_query_table_t *elem, - ecs_query_table_match_t *first) +int ecs_value_copy_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void* dst, + const void *src) { - ecs_query_table_match_t *cur, *next; - ecs_world_t *world = query->filter.world; - - for (cur = first; cur != NULL; cur = next) { - flecs_bfree(&query->allocators.columns, cur->columns); - flecs_bfree(&query->allocators.columns, cur->storage_columns); - flecs_bfree(&query->allocators.ids, cur->ids); - flecs_bfree(&query->allocators.sources, cur->sources); - - if (cur->monitor) { - flecs_bfree(&query->allocators.monitors, cur->monitor); - } - if (!elem->hdr.empty) { - flecs_query_remove_table_node(query, cur); - } - - ecs_vec_fini_t(&world->allocator, &cur->refs, ecs_ref_t); - flecs_entity_filter_fini(world, cur->entity_filter); - - next = cur->next_match; + flecs_poly_assert(world, ecs_world_t); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); + (void)world; - flecs_bfree(&world->allocators.query_table_match, cur); + ecs_copy_t copy; + if ((copy = ti->hooks.copy)) { + copy(dst, src, 1, ti); + } else { + ecs_os_memcpy(dst, src, ti->size); } + + return 0; +error: + return -1; } -static -void flecs_query_table_free( - ecs_query_t *query, - ecs_query_table_t *elem) +int ecs_value_copy( + const ecs_world_t *world, + ecs_entity_t type, + void* dst, + const void *src) { - flecs_query_table_match_free(query, elem, elem->first); - flecs_bfree(&query->filter.world->allocators.query_table, elem); + flecs_poly_assert(world, ecs_world_t); + const ecs_type_info_t *ti = ecs_get_type_info(world, type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); + return ecs_value_copy_w_type_info(world, ti, dst, src); +error: + return -1; } -static -void flecs_query_unmatch_table( - ecs_query_t *query, - ecs_table_t *table, - ecs_query_table_t *elem) +int ecs_value_move_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void* dst, + void *src) { - 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); + flecs_poly_assert(world, ecs_world_t); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); + (void)world; + + ecs_move_t move; + if ((move = ti->hooks.move)) { + move(dst, src, 1, ti); + } else { + ecs_os_memcpy(dst, src, ti->size); } + + return 0; +error: + return -1; } -/* Rematch system with tables after a change happened to a watched entity */ -static -void flecs_query_rematch_tables( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_t *parent_query) +int ecs_value_move( + const ecs_world_t *world, + ecs_entity_t type, + void* dst, + void *src) { - ecs_iter_t it, parent_it; - ecs_table_t *table = NULL; - ecs_query_table_t *qt = NULL; - ecs_query_table_match_t *qm = NULL; - - if (query->monitor_generation == world->monitor_generation) { - return; - } + flecs_poly_assert(world, ecs_world_t); + const ecs_type_info_t *ti = ecs_get_type_info(world, type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); + return ecs_value_move_w_type_info(world, ti, dst, src); +error: + return -1; +} - query->monitor_generation = world->monitor_generation; +int ecs_value_move_ctor_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void* dst, + void *src) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); + (void)world; - if (parent_query) { - parent_it = ecs_query_iter(world, parent_query); - it = ecs_filter_chain_iter(&parent_it, &query->filter); + ecs_move_t move; + if ((move = ti->hooks.move_ctor)) { + move(dst, src, 1, ti); } else { - it = ecs_filter_iter(world, &query->filter); + ecs_os_memcpy(dst, src, ti->size); } - ECS_BIT_SET(it.flags, EcsIterIsInstanced); - ECS_BIT_SET(it.flags, EcsIterNoData); - ECS_BIT_SET(it.flags, EcsIterEntityOptional); + return 0; +error: + return -1; +} - world->info.rematch_count_total ++; - int32_t rematch_count = ++ query->rematch_count; +int ecs_value_move_ctor( + const ecs_world_t *world, + ecs_entity_t type, + void* dst, + void *src) +{ + flecs_poly_assert(world, ecs_world_t); + const ecs_type_info_t *ti = ecs_get_type_info(world, type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); + return ecs_value_move_w_type_info(world, ti, dst, src); +error: + return -1; +} - ecs_time_t t = {0}; - if (world->flags & EcsWorldMeasureFrameTime) { - ecs_time_measure(&t); - } +/** + * @file world.c + * @brief World-level API. + */ - while (ecs_filter_next(&it)) { - if ((table != it.table) || (!it.table && !qt)) { - if (qm && qm->next_match) { - flecs_query_table_match_free(query, qt, qm->next_match); - qm->next_match = NULL; - } - table = it.table; +/* Id flags */ +const ecs_id_t ECS_PAIR = (1ull << 63); +const ecs_id_t ECS_AUTO_OVERRIDE = (1ull << 62); +const ecs_id_t ECS_TOGGLE = (1ull << 61); - qt = ecs_table_cache_get(&query->cache, table); - if (!qt) { - qt = flecs_query_table_insert(world, query, table); - } +/** 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(EcsPoly) = 3; - ecs_assert(qt->hdr.table == table, ECS_INTERNAL_ERROR, NULL); - qt->rematch_count = rematch_count; - qm = NULL; - } - if (!qm) { - qm = qt->first; - } else { - qm = qm->next_match; - } - if (!qm) { - qm = flecs_query_add_table_match(query, qt, table); - } +/* Poly target components */ +const ecs_entity_t EcsQuery = 5; +const ecs_entity_t EcsObserver = 6; +const ecs_entity_t EcsSystem = 7; - flecs_query_set_table_match(world, query, qm, table, &it); +/* Core scopes & entities */ +const ecs_entity_t EcsWorld = FLECS_HI_COMPONENT_ID + 0; +const ecs_entity_t EcsFlecs = FLECS_HI_COMPONENT_ID + 1; +const ecs_entity_t EcsFlecsCore = FLECS_HI_COMPONENT_ID + 2; +const ecs_entity_t EcsFlecsInternals = FLECS_HI_COMPONENT_ID + 3; +const ecs_entity_t EcsModule = FLECS_HI_COMPONENT_ID + 4; +const ecs_entity_t EcsPrivate = FLECS_HI_COMPONENT_ID + 5; +const ecs_entity_t EcsPrefab = FLECS_HI_COMPONENT_ID + 6; +const ecs_entity_t EcsDisabled = FLECS_HI_COMPONENT_ID + 7; +const ecs_entity_t EcsNotQueryable = FLECS_HI_COMPONENT_ID + 8; + +const ecs_entity_t EcsSlotOf = FLECS_HI_COMPONENT_ID + 9; +const ecs_entity_t EcsFlag = FLECS_HI_COMPONENT_ID + 10; + +/* Marker entities for query encoding */ +const ecs_entity_t EcsWildcard = FLECS_HI_COMPONENT_ID + 11; +const ecs_entity_t EcsAny = FLECS_HI_COMPONENT_ID + 12; +const ecs_entity_t EcsThis = FLECS_HI_COMPONENT_ID + 13; +const ecs_entity_t EcsVariable = FLECS_HI_COMPONENT_ID + 14; + +/* Traits */ +const ecs_entity_t EcsTransitive = FLECS_HI_COMPONENT_ID + 15; +const ecs_entity_t EcsReflexive = FLECS_HI_COMPONENT_ID + 16; +const ecs_entity_t EcsSymmetric = FLECS_HI_COMPONENT_ID + 17; +const ecs_entity_t EcsFinal = FLECS_HI_COMPONENT_ID + 18; +const ecs_entity_t EcsOnInstantiate = FLECS_HI_COMPONENT_ID + 19; +const ecs_entity_t EcsOverride = FLECS_HI_COMPONENT_ID + 20; +const ecs_entity_t EcsInherit = FLECS_HI_COMPONENT_ID + 21; +const ecs_entity_t EcsDontInherit = FLECS_HI_COMPONENT_ID + 22; +const ecs_entity_t EcsPairIsTag = FLECS_HI_COMPONENT_ID + 23; +const ecs_entity_t EcsExclusive = FLECS_HI_COMPONENT_ID + 24; +const ecs_entity_t EcsAcyclic = FLECS_HI_COMPONENT_ID + 25; +const ecs_entity_t EcsTraversable = FLECS_HI_COMPONENT_ID + 26; +const ecs_entity_t EcsWith = FLECS_HI_COMPONENT_ID + 27; +const ecs_entity_t EcsOneOf = FLECS_HI_COMPONENT_ID + 28; +const ecs_entity_t EcsCanToggle = FLECS_HI_COMPONENT_ID + 29; +const ecs_entity_t EcsTrait = FLECS_HI_COMPONENT_ID + 30; +const ecs_entity_t EcsRelationship = FLECS_HI_COMPONENT_ID + 31; +const ecs_entity_t EcsTarget = FLECS_HI_COMPONENT_ID + 32; - if (table && ecs_table_count(table) && query->group_by) { - if (flecs_query_get_group_id(query, table) != qm->group_id) { - /* Update table group */ - flecs_query_remove_table_node(query, qm); - flecs_query_insert_table_node(query, qm); - } - } - } - if (qm && qm->next_match) { - flecs_query_table_match_free(query, qt, qm->next_match); - qm->next_match = NULL; - } +/* Builtin relationships */ +const ecs_entity_t EcsChildOf = FLECS_HI_COMPONENT_ID + 33; +const ecs_entity_t EcsIsA = FLECS_HI_COMPONENT_ID + 34; +const ecs_entity_t EcsDependsOn = FLECS_HI_COMPONENT_ID + 35; - /* Iterate all tables in cache, remove ones that weren't just matched */ - ecs_table_cache_iter_t cache_it; - 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, qt); - } - } - } +/* Identifier tags */ +const ecs_entity_t EcsName = FLECS_HI_COMPONENT_ID + 36; +const ecs_entity_t EcsSymbol = FLECS_HI_COMPONENT_ID + 37; +const ecs_entity_t EcsAlias = FLECS_HI_COMPONENT_ID + 38; - if (world->flags & EcsWorldMeasureFrameTime) { - world->info.rematch_time_total += (ecs_ftime_t)ecs_time_measure(&t); - } -} +/* Events */ +const ecs_entity_t EcsOnAdd = FLECS_HI_COMPONENT_ID + 39; +const ecs_entity_t EcsOnRemove = FLECS_HI_COMPONENT_ID + 40; +const ecs_entity_t EcsOnSet = FLECS_HI_COMPONENT_ID + 41; +const ecs_entity_t EcsOnDelete = FLECS_HI_COMPONENT_ID + 43; +const ecs_entity_t EcsOnDeleteTarget = FLECS_HI_COMPONENT_ID + 44; +const ecs_entity_t EcsOnTableCreate = FLECS_HI_COMPONENT_ID + 45; +const ecs_entity_t EcsOnTableDelete = FLECS_HI_COMPONENT_ID + 46; +const ecs_entity_t EcsOnTableEmpty = FLECS_HI_COMPONENT_ID + 47; +const ecs_entity_t EcsOnTableFill = FLECS_HI_COMPONENT_ID + 48; -static -void flecs_query_remove_subquery( - ecs_query_t *parent, - ecs_query_t *sub) -{ - ecs_assert(parent != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(sub != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(parent->subqueries.array, ECS_INTERNAL_ERROR, NULL); +/* Timers */ +const ecs_entity_t ecs_id(EcsTickSource) = FLECS_HI_COMPONENT_ID + 49; +const ecs_entity_t ecs_id(EcsTimer) = FLECS_HI_COMPONENT_ID + 50; +const ecs_entity_t ecs_id(EcsRateFilter) = FLECS_HI_COMPONENT_ID + 51; - int32_t i, count = ecs_vec_count(&parent->subqueries); - ecs_query_t **sq = ecs_vec_first(&parent->subqueries); +/* Actions */ +const ecs_entity_t EcsRemove = FLECS_HI_COMPONENT_ID + 52; +const ecs_entity_t EcsDelete = FLECS_HI_COMPONENT_ID + 53; +const ecs_entity_t EcsPanic = FLECS_HI_COMPONENT_ID + 54; - for (i = 0; i < count; i ++) { - if (sq[i] == sub) { - break; - } - } +/* Storage */ +const ecs_entity_t EcsSparse = FLECS_HI_COMPONENT_ID + 55; +const ecs_entity_t EcsUnion = FLECS_HI_COMPONENT_ID + 56; - ecs_vec_remove_t(&parent->subqueries, ecs_query_t*, i); -} +/* Misc */ +const ecs_entity_t ecs_id(EcsDefaultChildComponent) = FLECS_HI_COMPONENT_ID + 57; -/* -- Private API -- */ +/* Builtin predicate ids (used by query engine) */ +const ecs_entity_t EcsPredEq = FLECS_HI_COMPONENT_ID + 58; +const ecs_entity_t EcsPredMatch = FLECS_HI_COMPONENT_ID + 59; +const ecs_entity_t EcsPredLookup = FLECS_HI_COMPONENT_ID + 60; +const ecs_entity_t EcsScopeOpen = FLECS_HI_COMPONENT_ID + 61; +const ecs_entity_t EcsScopeClose = FLECS_HI_COMPONENT_ID + 62; -void flecs_query_notify( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_event_t *event) +/* Systems */ +const ecs_entity_t EcsMonitor = FLECS_HI_COMPONENT_ID + 63; +const ecs_entity_t EcsEmpty = FLECS_HI_COMPONENT_ID + 64; +const ecs_entity_t ecs_id(EcsPipeline) = FLECS_HI_COMPONENT_ID + 65; +const ecs_entity_t EcsOnStart = FLECS_HI_COMPONENT_ID + 66; +const ecs_entity_t EcsPreFrame = FLECS_HI_COMPONENT_ID + 67; +const ecs_entity_t EcsOnLoad = FLECS_HI_COMPONENT_ID + 68; +const ecs_entity_t EcsPostLoad = FLECS_HI_COMPONENT_ID + 69; +const ecs_entity_t EcsPreUpdate = FLECS_HI_COMPONENT_ID + 70; +const ecs_entity_t EcsOnUpdate = FLECS_HI_COMPONENT_ID + 71; +const ecs_entity_t EcsOnValidate = FLECS_HI_COMPONENT_ID + 72; +const ecs_entity_t EcsPostUpdate = FLECS_HI_COMPONENT_ID + 73; +const ecs_entity_t EcsPreStore = FLECS_HI_COMPONENT_ID + 74; +const ecs_entity_t EcsOnStore = FLECS_HI_COMPONENT_ID + 75; +const ecs_entity_t EcsPostFrame = FLECS_HI_COMPONENT_ID + 76; +const ecs_entity_t EcsPhase = FLECS_HI_COMPONENT_ID + 77; + +/* Meta primitive components (don't use low ids to save id space) */ +#ifdef FLECS_META +const ecs_entity_t ecs_id(ecs_bool_t) = FLECS_HI_COMPONENT_ID + 80; +const ecs_entity_t ecs_id(ecs_char_t) = FLECS_HI_COMPONENT_ID + 81; +const ecs_entity_t ecs_id(ecs_byte_t) = FLECS_HI_COMPONENT_ID + 82; +const ecs_entity_t ecs_id(ecs_u8_t) = FLECS_HI_COMPONENT_ID + 83; +const ecs_entity_t ecs_id(ecs_u16_t) = FLECS_HI_COMPONENT_ID + 84; +const ecs_entity_t ecs_id(ecs_u32_t) = FLECS_HI_COMPONENT_ID + 85; +const ecs_entity_t ecs_id(ecs_u64_t) = FLECS_HI_COMPONENT_ID + 86; +const ecs_entity_t ecs_id(ecs_uptr_t) = FLECS_HI_COMPONENT_ID + 87; +const ecs_entity_t ecs_id(ecs_i8_t) = FLECS_HI_COMPONENT_ID + 88; +const ecs_entity_t ecs_id(ecs_i16_t) = FLECS_HI_COMPONENT_ID + 89; +const ecs_entity_t ecs_id(ecs_i32_t) = FLECS_HI_COMPONENT_ID + 90; +const ecs_entity_t ecs_id(ecs_i64_t) = FLECS_HI_COMPONENT_ID + 91; +const ecs_entity_t ecs_id(ecs_iptr_t) = FLECS_HI_COMPONENT_ID + 92; +const ecs_entity_t ecs_id(ecs_f32_t) = FLECS_HI_COMPONENT_ID + 93; +const ecs_entity_t ecs_id(ecs_f64_t) = FLECS_HI_COMPONENT_ID + 94; +const ecs_entity_t ecs_id(ecs_string_t) = FLECS_HI_COMPONENT_ID + 95; +const ecs_entity_t ecs_id(ecs_entity_t) = FLECS_HI_COMPONENT_ID + 96; +const ecs_entity_t ecs_id(ecs_id_t) = FLECS_HI_COMPONENT_ID + 97; + +/** Meta module component ids */ +const ecs_entity_t ecs_id(EcsType) = FLECS_HI_COMPONENT_ID + 98; +const ecs_entity_t ecs_id(EcsTypeSerializer) = FLECS_HI_COMPONENT_ID + 99; +const ecs_entity_t ecs_id(EcsPrimitive) = FLECS_HI_COMPONENT_ID + 100; +const ecs_entity_t ecs_id(EcsEnum) = FLECS_HI_COMPONENT_ID + 101; +const ecs_entity_t ecs_id(EcsBitmask) = FLECS_HI_COMPONENT_ID + 102; +const ecs_entity_t ecs_id(EcsMember) = FLECS_HI_COMPONENT_ID + 103; +const ecs_entity_t ecs_id(EcsMemberRanges) = FLECS_HI_COMPONENT_ID + 104; +const ecs_entity_t ecs_id(EcsStruct) = FLECS_HI_COMPONENT_ID + 105; +const ecs_entity_t ecs_id(EcsArray) = FLECS_HI_COMPONENT_ID + 106; +const ecs_entity_t ecs_id(EcsVector) = FLECS_HI_COMPONENT_ID + 107; +const ecs_entity_t ecs_id(EcsOpaque) = FLECS_HI_COMPONENT_ID + 108; +const ecs_entity_t ecs_id(EcsUnit) = FLECS_HI_COMPONENT_ID + 109; +const ecs_entity_t ecs_id(EcsUnitPrefix) = FLECS_HI_COMPONENT_ID + 110; +const ecs_entity_t EcsConstant = FLECS_HI_COMPONENT_ID + 111; +const ecs_entity_t EcsQuantity = FLECS_HI_COMPONENT_ID + 112; +#endif + +/* Doc module components */ +#ifdef FLECS_DOC +const ecs_entity_t ecs_id(EcsDocDescription) = FLECS_HI_COMPONENT_ID + 113; +const ecs_entity_t EcsDocBrief = FLECS_HI_COMPONENT_ID + 114; +const ecs_entity_t EcsDocDetail = FLECS_HI_COMPONENT_ID + 115; +const ecs_entity_t EcsDocLink = FLECS_HI_COMPONENT_ID + 116; +const ecs_entity_t EcsDocColor = FLECS_HI_COMPONENT_ID + 117; +const ecs_entity_t EcsDocUuid = FLECS_HI_COMPONENT_ID + 118; +#endif + +/* REST module components */ +#ifdef FLECS_REST +const ecs_entity_t ecs_id(EcsRest) = FLECS_HI_COMPONENT_ID + 119; +#endif + +/* Max static id: + * #define EcsFirstUserEntityId (FLECS_HI_COMPONENT_ID + 128) */ + +/* Default lookup path */ +static ecs_entity_t ecs_default_lookup_path[2] = { 0, 0 }; + +/* Declarations for addons. Located in world.c to avoid issues during linking of + * static library */ + +#ifdef FLECS_ALERTS +ECS_COMPONENT_DECLARE(EcsAlert); +ECS_COMPONENT_DECLARE(EcsAlertInstance); +ECS_COMPONENT_DECLARE(EcsAlertsActive); +ECS_TAG_DECLARE(EcsAlertInfo); +ECS_TAG_DECLARE(EcsAlertWarning); +ECS_TAG_DECLARE(EcsAlertError); +ECS_TAG_DECLARE(EcsAlertCritical); +#endif +#ifdef FLECS_UNITS +ecs_entity_t EcsUnitPrefixes; + +ecs_entity_t EcsYocto; +ecs_entity_t EcsZepto; +ecs_entity_t EcsAtto; +ecs_entity_t EcsFemto; +ecs_entity_t EcsPico; +ecs_entity_t EcsNano; +ecs_entity_t EcsMicro; +ecs_entity_t EcsMilli; +ecs_entity_t EcsCenti; +ecs_entity_t EcsDeci; +ecs_entity_t EcsDeca; +ecs_entity_t EcsHecto; +ecs_entity_t EcsKilo; +ecs_entity_t EcsMega; +ecs_entity_t EcsGiga; +ecs_entity_t EcsTera; +ecs_entity_t EcsPeta; +ecs_entity_t EcsExa; +ecs_entity_t EcsZetta; +ecs_entity_t EcsYotta; + +ecs_entity_t EcsKibi; +ecs_entity_t EcsMebi; +ecs_entity_t EcsGibi; +ecs_entity_t EcsTebi; +ecs_entity_t EcsPebi; +ecs_entity_t EcsExbi; +ecs_entity_t EcsZebi; +ecs_entity_t EcsYobi; + +ecs_entity_t EcsDuration; + ecs_entity_t EcsPicoSeconds; + ecs_entity_t EcsNanoSeconds; + ecs_entity_t EcsMicroSeconds; + ecs_entity_t EcsMilliSeconds; + ecs_entity_t EcsSeconds; + ecs_entity_t EcsMinutes; + ecs_entity_t EcsHours; + ecs_entity_t EcsDays; + +ecs_entity_t EcsTime; + ecs_entity_t EcsDate; + +ecs_entity_t EcsMass; + ecs_entity_t EcsGrams; + ecs_entity_t EcsKiloGrams; + +ecs_entity_t EcsElectricCurrent; + ecs_entity_t EcsAmpere; + +ecs_entity_t EcsAmount; + ecs_entity_t EcsMole; + +ecs_entity_t EcsLuminousIntensity; + ecs_entity_t EcsCandela; + +ecs_entity_t EcsForce; + ecs_entity_t EcsNewton; + +ecs_entity_t EcsLength; + ecs_entity_t EcsMeters; + ecs_entity_t EcsPicoMeters; + ecs_entity_t EcsNanoMeters; + ecs_entity_t EcsMicroMeters; + ecs_entity_t EcsMilliMeters; + ecs_entity_t EcsCentiMeters; + ecs_entity_t EcsKiloMeters; + ecs_entity_t EcsMiles; + ecs_entity_t EcsPixels; + +ecs_entity_t EcsPressure; + ecs_entity_t EcsPascal; + ecs_entity_t EcsBar; + +ecs_entity_t EcsSpeed; + ecs_entity_t EcsMetersPerSecond; + ecs_entity_t EcsKiloMetersPerSecond; + ecs_entity_t EcsKiloMetersPerHour; + ecs_entity_t EcsMilesPerHour; + +ecs_entity_t EcsAcceleration; + +ecs_entity_t EcsTemperature; + ecs_entity_t EcsKelvin; + ecs_entity_t EcsCelsius; + ecs_entity_t EcsFahrenheit; + +ecs_entity_t EcsData; + ecs_entity_t EcsBits; + ecs_entity_t EcsKiloBits; + ecs_entity_t EcsMegaBits; + ecs_entity_t EcsGigaBits; + ecs_entity_t EcsBytes; + ecs_entity_t EcsKiloBytes; + ecs_entity_t EcsMegaBytes; + ecs_entity_t EcsGigaBytes; + ecs_entity_t EcsKibiBytes; + ecs_entity_t EcsGibiBytes; + ecs_entity_t EcsMebiBytes; + +ecs_entity_t EcsDataRate; + ecs_entity_t EcsBitsPerSecond; + ecs_entity_t EcsKiloBitsPerSecond; + ecs_entity_t EcsMegaBitsPerSecond; + ecs_entity_t EcsGigaBitsPerSecond; + ecs_entity_t EcsBytesPerSecond; + ecs_entity_t EcsKiloBytesPerSecond; + ecs_entity_t EcsMegaBytesPerSecond; + ecs_entity_t EcsGigaBytesPerSecond; + +ecs_entity_t EcsPercentage; + +ecs_entity_t EcsAngle; + ecs_entity_t EcsRadians; + ecs_entity_t EcsDegrees; + +ecs_entity_t EcsColor; + ecs_entity_t EcsColorRgb; + ecs_entity_t EcsColorHsl; + ecs_entity_t EcsColorCss; + +ecs_entity_t EcsBel; +ecs_entity_t EcsDeciBel; + +ecs_entity_t EcsFrequency; + ecs_entity_t EcsHertz; + ecs_entity_t EcsKiloHertz; + ecs_entity_t EcsMegaHertz; + ecs_entity_t EcsGigaHertz; + +ecs_entity_t EcsUri; + ecs_entity_t EcsUriHyperlink; + ecs_entity_t EcsUriImage; + ecs_entity_t EcsUriFile; +#endif + +/* -- Private functions -- */ + +ecs_stage_t* flecs_stage_from_readonly_world( + const ecs_world_t *world) { - bool notify = true; + ecs_assert(flecs_poly_is(world, ecs_world_t) || + flecs_poly_is(world, ecs_stage_t), + ECS_INTERNAL_ERROR, + NULL); - switch(event->kind) { - case EcsQueryTableMatch: - /* Creation of new table */ - if (flecs_query_match_table(world, query, event->table)) { - if (query->subqueries.array) { - flecs_query_notify_subqueries(world, query, event); - } - } - notify = false; - break; - case EcsQueryTableUnmatch: - /* Deletion of table */ - flecs_query_unmatch_table(query, event->table, NULL); - break; - case EcsQueryTableRematch: - /* Rematch tables of query */ - flecs_query_rematch_tables(world, query, event->parent_query); - break; - case EcsQueryOrphan: - ecs_assert(query->flags & EcsQueryIsSubquery, ECS_INTERNAL_ERROR, NULL); - query->flags |= EcsQueryIsOrphaned; - query->parent = NULL; - break; + if (flecs_poly_is(world, ecs_world_t)) { + return ECS_CONST_CAST(ecs_stage_t*, world->stages[0]); + } else if (flecs_poly_is(world, ecs_stage_t)) { + return ECS_CONST_CAST(ecs_stage_t*, world); } - if (notify) { - flecs_query_notify_subqueries(world, query, event); - } + return NULL; } -static -void flecs_query_order_by( - ecs_world_t *world, - ecs_query_t *query, - ecs_entity_t order_by_component, - ecs_order_by_action_t order_by, - ecs_sort_table_action_t action) +ecs_stage_t* flecs_stage_from_world( + ecs_world_t **world_ptr) { - ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(query->flags & EcsQueryIsOrphaned), ECS_INVALID_PARAMETER, NULL); - ecs_check(!ecs_id_is_wildcard(order_by_component), - ECS_INVALID_PARAMETER, NULL); - - /* Find order_by_component term & make sure it is queried for */ - const ecs_filter_t *filter = &query->filter; - int32_t i, count = filter->term_count; - int32_t order_by_term = -1; + ecs_world_t *world = *world_ptr; - if (order_by_component) { - for (i = 0; i < count; i ++) { - ecs_term_t *term = &filter->terms[i]; - - /* Only And terms are supported */ - if (term->id == order_by_component && term->oper == EcsAnd) { - order_by_term = i; - break; - } - } + ecs_assert(flecs_poly_is(world, ecs_world_t) || + flecs_poly_is(world, ecs_stage_t), + ECS_INTERNAL_ERROR, + NULL); - ecs_check(order_by_term != -1, ECS_INVALID_PARAMETER, - "sorted component not is queried for"); + if (flecs_poly_is(world, ecs_world_t)) { + return world->stages[0]; } - query->order_by_component = order_by_component; - query->order_by = order_by; - query->order_by_term = order_by_term; - query->sort_table = action; + *world_ptr = ((ecs_stage_t*)world)->world; + return ECS_CONST_CAST(ecs_stage_t*, world); +} + +ecs_world_t* flecs_suspend_readonly( + const ecs_world_t *stage_world, + ecs_suspend_readonly_state_t *state) +{ + ecs_assert(stage_world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(state != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_world_t *world = + ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage_world)); + flecs_poly_assert(world, ecs_world_t); - ecs_vec_fini_t(NULL, &query->table_slices, ecs_query_table_match_t); - flecs_query_sort_tables(world, query); + bool is_readonly = ECS_BIT_IS_SET(world->flags, EcsWorldReadonly); + ecs_world_t *temp_world = world; + ecs_stage_t *stage = flecs_stage_from_world(&temp_world); - if (!query->table_slices.array) { - flecs_query_build_sorted_tables(query); + if (!is_readonly && !stage->defer) { + state->is_readonly = false; + state->is_deferred = false; + return world; } - query->flags &= ~EcsQueryTrivialIter; -error: - return; -} + ecs_dbg_3("suspending readonly mode"); -static -void flecs_query_group_by( - ecs_query_t *query, - ecs_entity_t sort_component, - ecs_group_by_action_t group_by) -{ - /* Cannot change grouping once a query has been created */ - ecs_check(query->group_by_id == 0, ECS_INVALID_OPERATION, NULL); - ecs_check(query->group_by == 0, ECS_INVALID_OPERATION, NULL); + /* Cannot suspend when running with multiple threads */ + ecs_assert(!(world->flags & EcsWorldReadonly) || + !(world->flags & EcsWorldMultiThreaded), ECS_INVALID_WHILE_READONLY, NULL); - if (!group_by) { - /* Builtin function that groups by relationship */ - group_by = flecs_query_default_group_by; - } + state->is_readonly = is_readonly; + state->is_deferred = stage->defer != 0; - query->group_by_id = sort_component; - query->group_by = group_by; + /* Silence readonly checks */ + world->flags &= ~EcsWorldReadonly; - ecs_map_init_w_params(&query->groups, - &query->filter.world->allocators.query_table_list); -error: - return; + /* Hack around safety checks (this ought to look ugly) */ + state->defer_count = stage->defer; + state->commands = stage->cmd->queue; + state->defer_stack = stage->cmd->stack; + flecs_stack_init(&stage->cmd->stack); + state->scope = stage->scope; + state->with = stage->with; + stage->defer = 0; + ecs_vec_init_t(NULL, &stage->cmd->queue, ecs_cmd_t, 0); + + return world; } -/* Implementation for iterable mixin */ -static -void flecs_query_iter_init( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter) +void flecs_resume_readonly( + ecs_world_t *world, + ecs_suspend_readonly_state_t *state) { - ecs_poly_assert(poly, ecs_query_t); + flecs_poly_assert(world, ecs_world_t); + ecs_assert(state != NULL, ECS_INTERNAL_ERROR, NULL); - if (filter) { - iter[1] = ecs_query_iter(world, ECS_CONST_CAST(ecs_query_t*, poly)); - iter[0] = ecs_term_chain_iter(&iter[1], filter); - } else { - iter[0] = ecs_query_iter(world, ECS_CONST_CAST(ecs_query_t*, poly)); + ecs_world_t *temp_world = world; + ecs_stage_t *stage = flecs_stage_from_world(&temp_world); + + if (state->is_readonly || state->is_deferred) { + ecs_dbg_3("resuming readonly mode"); + + ecs_run_aperiodic(world, 0); + + /* Restore readonly state / defer count */ + ECS_BIT_COND(world->flags, EcsWorldReadonly, state->is_readonly); + stage->defer = state->defer_count; + ecs_vec_fini_t(&stage->allocator, &stage->cmd->queue, ecs_cmd_t); + stage->cmd->queue = state->commands; + flecs_stack_fini(&stage->cmd->stack); + stage->cmd->stack = state->defer_stack; + stage->scope = state->scope; + stage->with = state->with; } } +/* Evaluate component monitor. If a monitored entity changed it will have set a + * flag in one of the world's component monitors. Queries can register + * themselves with component monitors to determine whether they need to rematch + * with tables. */ static -void flecs_query_on_event( - ecs_iter_t *it) +void flecs_eval_component_monitor( + ecs_world_t *world) { - /* Because this is the observer::run callback, checking if this is event is - * already handled is not done for us. */ - ecs_world_t *world = it->world; - ecs_observer_t *o = it->ctx; - if (o->last_event_id) { - if (o->last_event_id[0] == world->event_id) { - return; - } - o->last_event_id[0] = world->event_id; - } - - ecs_query_t *query = o->ctx; - ecs_table_t *table = it->table; - ecs_entity_t event = it->event; + flecs_poly_assert(world, ecs_world_t); - if (event == EcsOnTableCreate) { - /* Creation of new table */ - if (flecs_query_match_table(world, query, table)) { - if (query->subqueries.array) { - ecs_query_event_t evt = { - .kind = EcsQueryTableMatch, - .table = table, - .parent_query = query - }; - flecs_query_notify_subqueries(world, query, &evt); - } - } + if (!world->monitors.is_dirty) { return; } - ecs_assert(query != NULL, ECS_INTERNAL_ERROR, NULL); + world->info.eval_comp_monitors_total ++; - /* The observer isn't doing the matching because the query can do it more - * efficiently by checking the table with the query cache. */ - if (ecs_table_cache_get(&query->cache, table) == NULL) { - return; - } + ecs_os_perf_trace_push("flecs.component_monitor.eval"); - 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.array) { - ecs_query_event_t evt = { - .kind = EcsQueryTableUnmatch, - .table = table, - .parent_query = query - }; - flecs_query_notify_subqueries(world, query, &evt); + world->monitors.is_dirty = false; + + ecs_map_iter_t it = ecs_map_iter(&world->monitors.monitors); + while (ecs_map_next(&it)) { + ecs_monitor_t *m = ecs_map_ptr(&it); + if (!m->is_dirty) { + continue; } - return; - } -} -static -void flecs_query_table_cache_free( - ecs_query_t *query) -{ - ecs_table_cache_iter_t it; - ecs_query_table_t *qt; + m->is_dirty = false; - if (flecs_table_cache_all_iter(&query->cache, &it)) { - while ((qt = flecs_table_cache_next(&it, ecs_query_table_t))) { - flecs_query_table_free(query, qt); + int32_t i, count = ecs_vec_count(&m->queries); + ecs_query_t **elems = ecs_vec_first(&m->queries); + for (i = 0; i < count; i ++) { + ecs_query_t *q = elems[i]; + flecs_poly_assert(q, ecs_query_t); + flecs_query_cache_notify(world, q, &(ecs_query_cache_event_t) { + .kind = EcsQueryTableRematch + }); } } - ecs_table_cache_fini(&query->cache); + ecs_os_perf_trace_pop("flecs.component_monitor.eval"); } -static -void flecs_query_allocators_init( - ecs_query_t *query) +void flecs_monitor_mark_dirty( + ecs_world_t *world, + ecs_entity_t id) { - int32_t field_count = query->filter.field_count; - if (field_count) { - flecs_ballocator_init(&query->allocators.columns, - field_count * ECS_SIZEOF(int32_t)); - flecs_ballocator_init(&query->allocators.ids, - field_count * ECS_SIZEOF(ecs_id_t)); - flecs_ballocator_init(&query->allocators.sources, - field_count * ECS_SIZEOF(ecs_entity_t)); - flecs_ballocator_init(&query->allocators.monitors, - (1 + field_count) * ECS_SIZEOF(int32_t)); + ecs_map_t *monitors = &world->monitors.monitors; + + /* Only flag if there are actually monitors registered, so that we + * don't waste cycles evaluating monitors if there's no interest */ + if (ecs_map_is_init(monitors)) { + ecs_monitor_t *m = ecs_map_get_deref(monitors, ecs_monitor_t, id); + if (m) { + if (!world->monitors.is_dirty) { + world->monitor_generation ++; + } + m->is_dirty = true; + world->monitors.is_dirty = true; + } } } -static -void flecs_query_allocators_fini( +void flecs_monitor_register( + ecs_world_t *world, + ecs_entity_t id, ecs_query_t *query) { - int32_t field_count = query->filter.field_count; - if (field_count) { - flecs_ballocator_fini(&query->allocators.columns); - flecs_ballocator_fini(&query->allocators.ids); - flecs_ballocator_fini(&query->allocators.sources); - flecs_ballocator_fini(&query->allocators.monitors); - } + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(query != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(query, ecs_query_t); + + ecs_map_t *monitors = &world->monitors.monitors; + ecs_map_init_if(monitors, &world->allocator); + ecs_monitor_t *m = ecs_map_ensure_alloc_t(monitors, ecs_monitor_t, id); + ecs_vec_init_if_t(&m->queries, ecs_query_t*); + ecs_query_t **q = ecs_vec_append_t( + &world->allocator, &m->queries, ecs_query_t*); + *q = query; } -static -void flecs_query_fini( +void flecs_monitor_unregister( + ecs_world_t *world, + ecs_entity_t id, ecs_query_t *query) { - ecs_world_t *world = query->filter.world; + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(query != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(query, ecs_query_t); - ecs_group_delete_action_t on_delete = query->on_group_delete; - if (on_delete) { - ecs_map_iter_t it = ecs_map_iter(&query->groups); - while (ecs_map_next(&it)) { - ecs_query_table_list_t *group = ecs_map_ptr(&it); - uint64_t group_id = ecs_map_key(&it); - on_delete(world, group_id, group->info.ctx, query->group_by_ctx); - } - query->on_group_delete = NULL; + ecs_map_t *monitors = &world->monitors.monitors; + if (!ecs_map_is_init(monitors)) { + return; } - if (query->group_by_ctx_free) { - if (query->group_by_ctx) { - query->group_by_ctx_free(query->group_by_ctx); - } + ecs_monitor_t *m = ecs_map_get_deref(monitors, ecs_monitor_t, id); + if (!m) { + return; } - if ((query->flags & EcsQueryIsSubquery) && - !(query->flags & EcsQueryIsOrphaned)) - { - flecs_query_remove_subquery(query->parent, query); + int32_t i, count = ecs_vec_count(&m->queries); + ecs_query_t **queries = ecs_vec_first(&m->queries); + for (i = 0; i < count; i ++) { + if (queries[i] == query) { + ecs_vec_remove_t(&m->queries, ecs_query_t*, i); + count --; + break; + } } - flecs_query_notify_subqueries(world, query, &(ecs_query_event_t){ - .kind = EcsQueryOrphan - }); - - flecs_query_for_each_component_monitor(world, query, - flecs_monitor_unregister); - flecs_query_table_cache_free(query); - - ecs_map_fini(&query->groups); - - ecs_vec_fini_t(NULL, &query->subqueries, ecs_query_t*); - ecs_vec_fini_t(NULL, &query->table_slices, ecs_query_table_match_t); - ecs_filter_fini(&query->filter); - - flecs_query_allocators_fini(query); - - if (query->ctx_free) { - query->ctx_free(query->ctx); - } - if (query->binding_ctx_free) { - query->binding_ctx_free(query->binding_ctx); + if (!count) { + ecs_vec_fini_t(&world->allocator, &m->queries, ecs_query_t*); + ecs_map_remove_free(monitors, id); } - ecs_poly_free(query, ecs_query_t); + if (!ecs_map_count(monitors)) { + ecs_map_fini(monitors); + } } -/* -- Public API -- */ - -ecs_query_t* ecs_query_init( - ecs_world_t *world, - const ecs_query_desc_t *desc) +static +void flecs_init_store( + ecs_world_t *world) { - ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, NULL); - - ecs_query_t *result = ecs_poly_new(ecs_query_t); - ecs_observer_desc_t observer_desc = { .filter = desc->filter }; - ecs_entity_t entity = desc->filter.entity; - - observer_desc.filter.flags = EcsFilterMatchEmptyTables; - observer_desc.filter.storage = &result->filter; - result->filter = ECS_FILTER_INIT; - - if (ecs_filter_init(world, &observer_desc.filter) == NULL) { - goto error; - } - - ECS_BIT_COND(result->flags, EcsQueryTrivialIter, - !!(result->filter.flags & EcsFilterMatchOnlyThis)); + ecs_os_memset(&world->store, 0, ECS_SIZEOF(ecs_store_t)); - flecs_query_allocators_init(result); + ecs_allocator_t *a = &world->allocator; + ecs_vec_init_t(a, &world->store.records, ecs_table_record_t, 0); + ecs_vec_init_t(a, &world->store.marked_ids, ecs_marked_id_t, 0); + ecs_vec_init_t(a, &world->store.deleted_components, ecs_entity_t, 0); - if (result->filter.term_count) { - observer_desc.entity = entity; - observer_desc.run = flecs_query_on_event; - 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; + /* Initialize entity index */ + flecs_entities_init(world); - /* ecs_filter_init could have moved away resources from the terms array - * in the descriptor, so use the terms array from the filter. */ - observer_desc.filter.terms_buffer = result->filter.terms; - observer_desc.filter.terms_buffer_count = result->filter.term_count; - observer_desc.filter.expr = NULL; /* Already parsed */ + /* Initialize table sparse set */ + flecs_sparse_init_t(&world->store.tables, + a, &world->allocators.sparse_chunk, ecs_table_t); - entity = ecs_observer_init(world, &observer_desc); - if (!entity) { - goto error; - } - } + /* Initialize table map */ + flecs_table_hashmap_init(world, &world->store.table_map); - result->iterable.init = flecs_query_iter_init; - result->dtor = (ecs_poly_dtor_t)flecs_query_fini; - result->prev_match_count = -1; + /* Initialize root table */ + flecs_init_root_table(world); - result->ctx = desc->ctx; - result->binding_ctx = desc->binding_ctx; - result->ctx_free = desc->ctx_free; - result->binding_ctx_free = desc->binding_ctx_free; + /* Initialize observer sparse set */ + flecs_sparse_init_t(&world->store.observers, + a, &world->allocators.sparse_chunk, ecs_observer_impl_t); +} - if (ecs_should_log_1()) { - char *filter_expr = ecs_filter_str(world, &result->filter); - ecs_dbg_1("#[green]query#[normal] [%s] created", - filter_expr ? filter_expr : ""); - ecs_os_free(filter_expr); - } +static +void flecs_clean_tables( + ecs_world_t *world) +{ + int32_t i, count = flecs_sparse_count(&world->store.tables); - ecs_log_push_1(); + /* Ensure that first table in sparse set has id 0. This is a dummy table + * that only exists so that there is no table with id 0 */ + ecs_table_t *first = flecs_sparse_get_dense_t(&world->store.tables, + ecs_table_t, 0); + (void)first; - if (flecs_query_process_signature(world, result)) { - goto error; + for (i = 1; i < count; i ++) { + ecs_table_t *t = flecs_sparse_get_dense_t(&world->store.tables, + ecs_table_t, i); + flecs_table_fini(world, t); } - /* Group before matching so we won't have to move tables around later */ - int32_t cascade_by = result->cascade_by; - if (cascade_by) { - flecs_query_group_by(result, result->filter.terms[cascade_by - 1].id, - flecs_query_group_by_cascade); - result->group_by_ctx = &result->filter.terms[cascade_by - 1]; + /* Free table types separately so that if application destructors rely on + * a type it's still valid. */ + for (i = 1; i < count; i ++) { + ecs_table_t *t = flecs_sparse_get_dense_t(&world->store.tables, + ecs_table_t, i); + flecs_table_free_type(world, t); } - if (desc->group_by || desc->group_by_id) { - /* Can't have a cascade term and group by at the same time, as cascade - * uses the group_by mechanism */ - ecs_check(!result->cascade_by, ECS_INVALID_PARAMETER, NULL); - flecs_query_group_by(result, desc->group_by_id, desc->group_by); - result->group_by_ctx = desc->group_by_ctx; - result->on_group_create = desc->on_group_create; - result->on_group_delete = desc->on_group_delete; - result->group_by_ctx_free = desc->group_by_ctx_free; + /* Clear the root table */ + if (count) { + flecs_table_reset(world, &world->store.root); } +} - if (desc->parent != NULL) { - result->flags |= EcsQueryIsSubquery; - } +static +void flecs_fini_root_tables( + ecs_world_t *world, + ecs_id_record_t *idr, + bool fini_targets) +{ + ecs_stage_t *stage0 = world->stages[0]; + bool finished = false; + const ecs_size_t MAX_DEFERRED_DELETE_QUEUE_SIZE = 4096; + while (!finished) { + ecs_table_cache_iter_t it; + ecs_size_t queue_size = 0; + finished = true; - /* If the query refers to itself, add the components that were queried for - * to the query itself. */ - if (entity) { - int32_t t, term_count = result->filter.term_count; - ecs_term_t *terms = result->filter.terms; + bool has_roots = flecs_table_cache_iter(&idr->cache, &it); + ecs_assert(has_roots == true, ECS_INTERNAL_ERROR, NULL); + (void)has_roots; - for (t = 0; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; - if (term->src.id == entity) { - ecs_add_id(world, entity, term->id); + const ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + if (table->flags & EcsTableHasBuiltins) { + continue; /* Query out modules */ } - } - } - if (!entity) { - entity = ecs_new_id(world); - } + int32_t i, count = ecs_table_count(table); + const ecs_entity_t *entities = ecs_table_entities(table); - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_query_t); - if (poly->poly) { - /* If entity already had poly query, delete previous */ - flecs_query_fini(poly->poly); + if (fini_targets) { + /* Only delete entities that are used as pair target. Iterate + * backwards to minimize moving entities around in table. */ + for (i = count - 1; i >= 0; i --) { + ecs_record_t *r = flecs_entities_get(world, entities[i]); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table == table, ECS_INTERNAL_ERROR, NULL); + if (ECS_RECORD_TO_ROW_FLAGS(r->row) & EcsEntityIsTarget) { + ecs_delete(world, entities[i]); + queue_size++; + /* Flush the queue before it grows too big: */ + if(queue_size >= MAX_DEFERRED_DELETE_QUEUE_SIZE) { + finished = false; + break; /* restart iteration */ + } + } + } + } else { + /* Delete remaining entities that are not in use (added to another + * entity). This limits table moves during cleanup and delays + * cleanup of tags. */ + for (i = count - 1; i >= 0; i --) { + ecs_record_t *r = flecs_entities_get(world, entities[i]); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table == table, ECS_INTERNAL_ERROR, NULL); + if (!ECS_RECORD_TO_ROW_FLAGS(r->row)) { + ecs_delete(world, entities[i]); + queue_size++; + /* Flush the queue before it grows too big: */ + if(queue_size >= MAX_DEFERRED_DELETE_QUEUE_SIZE) { + finished = false; + break; /* restart iteration */ + } + } + } + } + if(!finished) { + /* flush queue and restart iteration */ + flecs_defer_end(world, stage0); + flecs_defer_begin(world, stage0); + break; + } + } } - poly->poly = result; - result->filter.entity = entity; +} - /* Ensure that while initially populating the query with tables, they are - * in the right empty/non-empty list. This ensures the query won't miss - * empty/non-empty events for tables that are currently out of sync, but - * change back to being in sync before processing pending events. */ - ecs_run_aperiodic(world, EcsAperiodicEmptyTables); +static +void flecs_fini_roots( + ecs_world_t *world) +{ + ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair(EcsChildOf, 0)); - ecs_table_cache_init(world, &result->cache); + ecs_run_aperiodic(world, EcsAperiodicEmptyTables); - if (!desc->parent) { - flecs_query_match_tables(world, result); - } else { - flecs_query_add_subquery(world, desc->parent, result); - result->parent = desc->parent; - } + /* Delete root entities that are not modules. This prioritizes deleting + * regular entities first, which reduces the chance of components getting + * destructed in random order because it got deleted before entities, + * thereby bypassing the OnDeleteTarget policy. */ + flecs_defer_begin(world, world->stages[0]); + flecs_fini_root_tables(world, idr, true); + flecs_defer_end(world, world->stages[0]); - if (desc->order_by) { - flecs_query_order_by( - world, result, desc->order_by_component, desc->order_by, - desc->sort_table); - } + flecs_defer_begin(world, world->stages[0]); + flecs_fini_root_tables(world, idr, false); + flecs_defer_end(world, world->stages[0]); +} - if (!ecs_query_table_count(result) && result->filter.term_count) { - ecs_add_id(world, entity, EcsEmpty); - } +static +void flecs_fini_store(ecs_world_t *world) { + flecs_clean_tables(world); + flecs_sparse_fini(&world->store.tables); + flecs_table_fini(world, &world->store.root); + flecs_entities_clear(world); + flecs_hashmap_fini(&world->store.table_map); - ecs_poly_modified(world, entity, ecs_query_t); + ecs_assert(flecs_sparse_count(&world->store.observers) == 0, + ECS_INTERNAL_ERROR, NULL); + flecs_sparse_fini(&world->store.observers); - ecs_log_pop_1(); + ecs_assert(ecs_vec_count(&world->store.marked_ids) == 0, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_vec_count(&world->store.deleted_components) == 0, + ECS_INTERNAL_ERROR, NULL); - return result; -error: - if (result) { - ecs_filter_fini(&result->filter); - ecs_os_free(result); - } - return NULL; + ecs_allocator_t *a = &world->allocator; + ecs_vec_fini_t(a, &world->store.records, ecs_table_record_t); + ecs_vec_fini_t(a, &world->store.marked_ids, ecs_marked_id_t); + ecs_vec_fini_t(a, &world->store.deleted_components, ecs_entity_t); } -void ecs_query_fini( - ecs_query_t *query) -{ - ecs_poly_assert(query, ecs_query_t); - ecs_delete(query->filter.world, query->filter.entity); -} - -const ecs_filter_t* ecs_query_get_filter( - const ecs_query_t *query) -{ - ecs_poly_assert(query, ecs_query_t); - return &query->filter; -} - -static -void flecs_query_set_var( - ecs_iter_t *it) +static +void flecs_world_allocators_init( + ecs_world_t *world) { - ecs_check(it->constrained_vars == 1, ECS_INVALID_OPERATION, - "can only set $this variable for queries"); + ecs_world_allocators_t *a = &world->allocators; - ecs_var_t *var = &it->variables[0]; - ecs_table_t *table = var->range.table; - if (!table) { - goto nodata; - } + flecs_allocator_init(&world->allocator); - ecs_query_iter_t *qit = &it->priv.iter.query; - ecs_query_t *query = qit->query; - ecs_query_table_t *qt = ecs_table_cache_get(&query->cache, table); - if (!qt) { - goto nodata; - } + ecs_map_params_init(&a->ptr, &world->allocator); + ecs_map_params_init(&a->query_table_list, &world->allocator); - qit->node = qt->first; - qit->last = qt->last->next_match; - it->offset = var->range.offset; - it->count = var->range.count; - return; -error: -nodata: - it->priv.iter.query.node = NULL; - it->priv.iter.query.last = NULL; - return; + flecs_ballocator_init_t(&a->query_table, ecs_query_cache_table_t); + flecs_ballocator_init_t(&a->query_table_match, ecs_query_cache_table_match_t); + flecs_ballocator_init_n(&a->graph_edge_lo, ecs_graph_edge_t, FLECS_HI_COMPONENT_ID); + flecs_ballocator_init_t(&a->graph_edge, ecs_graph_edge_t); + flecs_ballocator_init_t(&a->id_record, ecs_id_record_t); + flecs_ballocator_init_n(&a->id_record_chunk, ecs_id_record_t, FLECS_SPARSE_PAGE_SIZE); + flecs_ballocator_init_t(&a->table_diff, ecs_table_diff_t); + flecs_ballocator_init_n(&a->sparse_chunk, int32_t, FLECS_SPARSE_PAGE_SIZE); + flecs_ballocator_init_t(&a->hashmap, ecs_hashmap_t); + flecs_table_diff_builder_init(world, &world->allocators.diff_builder); } -ecs_iter_t ecs_query_iter( - const ecs_world_t *stage, - ecs_query_t *query) +static +void flecs_world_allocators_fini( + ecs_world_t *world) { - ecs_poly_assert(query, ecs_query_t); - ecs_check(!(query->flags & EcsQueryIsOrphaned), - ECS_INVALID_PARAMETER, NULL); - - ecs_world_t *world = query->filter.world; - ecs_poly_assert(world, ecs_world_t); - - /* Process table events to ensure that the list of iterated tables doesn't - * contain empty tables. */ - flecs_process_pending_tables(world); + ecs_world_allocators_t *a = &world->allocators; - /* If query has order_by, apply sort */ - flecs_query_sort_tables(world, query); + ecs_map_params_fini(&a->ptr); + ecs_map_params_fini(&a->query_table_list); + flecs_ballocator_fini(&a->query_table); + flecs_ballocator_fini(&a->query_table_match); + flecs_ballocator_fini(&a->graph_edge_lo); + flecs_ballocator_fini(&a->graph_edge); + flecs_ballocator_fini(&a->id_record); + flecs_ballocator_fini(&a->id_record_chunk); + flecs_ballocator_fini(&a->table_diff); + flecs_ballocator_fini(&a->sparse_chunk); + flecs_ballocator_fini(&a->hashmap); + flecs_table_diff_builder_fini(world, &world->allocators.diff_builder); - /* If monitors changed, do query rematching */ - if (!(world->flags & EcsWorldReadonly) && query->flags & EcsQueryHasRefs) { - flecs_eval_component_monitors(world); - } + flecs_allocator_fini(&world->allocator); +} - /* Prepare iterator */ +#define ECS_STRINGIFY_INNER(x) #x +#define ECS_STRINGIFY(x) ECS_STRINGIFY_INNER(x) - int32_t table_count; - if (ecs_vec_count(&query->table_slices)) { - table_count = ecs_vec_count(&query->table_slices); - } else { - table_count = ecs_query_table_count(query); - } +static const char flecs_compiler_info[] +#if defined(__clang__) + = "clang " __clang_version__; +#elif defined(__GNUC__) + = "gcc " ECS_STRINGIFY(__GNUC__) "." ECS_STRINGIFY(__GNUC_MINOR__); +#elif defined(_MSC_VER) + = "msvc " ECS_STRINGIFY(_MSC_VER); +#elif defined(__TINYC__) + = "tcc " ECS_STRINGIFY(__TINYC__); +#else + = "unknown compiler"; +#endif - ecs_query_iter_t it = { - .query = query, - .node = query->list.first, - .last = NULL - }; +static const char *flecs_addons_info[] = { +#ifdef FLECS_CPP + "FLECS_CPP", +#endif +#ifdef FLECS_MODULE + "FLECS_MODULE", +#endif +#ifdef FLECS_SCRIPT + "FLECS_SCRIPT", +#endif +#ifdef FLECS_STATS + "FLECS_STATS", +#endif +#ifdef FLECS_METRICS + "FLECS_METRICS", +#endif +#ifdef FLECS_ALERTS + "FLECS_ALERTS", +#endif +#ifdef FLECS_SYSTEM + "FLECS_SYSTEM", +#endif +#ifdef FLECS_PIPELINE + "FLECS_PIPELINE", +#endif +#ifdef FLECS_TIMER + "FLECS_TIMER", +#endif +#ifdef FLECS_META + "FLECS_META", +#endif +#ifdef FLECS_UNITS + "FLECS_UNITS", +#endif +#ifdef FLECS_JSON + "FLECS_JSON", +#endif +#ifdef FLECS_DOC + "FLECS_DOC", +#endif +#ifdef FLECS_LOG + "FLECS_LOG", +#endif +#ifdef FLECS_JOURNAL + "FLECS_JOURNAL", +#endif +#ifdef FLECS_APP + "FLECS_APP", +#endif +#ifdef FLECS_OS_API_IMPL + "FLECS_OS_API_IMPL", +#endif +#ifdef FLECS_SCRIPT + "FLECS_SCRIPT", +#endif +#ifdef FLECS_HTTP + "FLECS_HTTP", +#endif +#ifdef FLECS_REST + "FLECS_REST", +#endif +NULL +}; - if (query->order_by && query->list.info.table_count) { - it.node = ecs_vec_first(&query->table_slices); - } +static const ecs_build_info_t flecs_build_info = { + .compiler = flecs_compiler_info, + .addons = flecs_addons_info, +#ifdef FLECS_DEBUG + .debug = true, +#endif +#ifdef FLECS_SANITIZE + .sanitize = true, +#endif +#ifdef FLECS_PERF_TRACE + .perf_trace = true, +#endif + .version = FLECS_VERSION, + .version_major = FLECS_VERSION_MAJOR, + .version_minor = FLECS_VERSION_MINOR, + .version_patch = FLECS_VERSION_PATCH +}; - ecs_iter_t result = { - .real_world = world, - .world = ECS_CONST_CAST(ecs_world_t*, stage), - .terms = query->filter.terms, - .field_count = query->filter.field_count, - .table_count = table_count, - .variable_count = 1, - .priv.iter.query = it, - .next = ecs_query_next, - .set_var = flecs_query_set_var - }; +static +void flecs_log_build_info(void) { + const ecs_build_info_t *bi = ecs_get_build_info(); + ecs_assert(bi != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_filter_apply_iter_flags(&result, &query->filter); + ecs_trace("flecs version %s", bi->version); - ecs_filter_t *filter = &query->filter; - ecs_iter_t fit; - if (!(query->flags & EcsQueryTrivialIter)) { - /* Check if non-This terms (like singleton terms) still match */ - if (!(filter->flags & EcsFilterMatchOnlyThis)) { - fit = flecs_filter_iter_w_flags(ECS_CONST_CAST(ecs_world_t*, stage), - &query->filter, EcsIterIgnoreThis); - if (!ecs_filter_next(&fit)) { - /* No match, so return nothing */ - goto noresults; - } - } + ecs_trace("addons included in build:"); + ecs_log_push(); - flecs_iter_init(stage, &result, flecs_iter_cache_all); + const char **addon = bi->addons; + do { + ecs_trace(addon[0]); + } while ((++ addon)[0]); + ecs_log_pop(); - /* Copy the data */ - if (!(filter->flags & EcsFilterMatchOnlyThis)) { - int32_t field_count = filter->field_count; - if (field_count) { - if (result.ptrs) { - ecs_os_memcpy_n(result.ptrs, fit.ptrs, void*, field_count); - } - ecs_os_memcpy_n(result.ids, fit.ids, ecs_id_t, field_count); - ecs_os_memcpy_n(result.columns, fit.columns, int32_t, field_count); - ecs_os_memcpy_n(result.sources, fit.sources, int32_t, field_count); - } - ecs_iter_fini(&fit); - } + if (bi->sanitize) { + ecs_trace("sanitize build, rebuild without FLECS_SANITIZE for (much) " + "improved performance"); + } else if (bi->debug) { + ecs_trace("debug build, rebuild with NDEBUG or FLECS_NDEBUG for " + "improved performance"); } else { - /* Trivial iteration, use arrays from query cache */ - flecs_iter_init(stage, &result, - flecs_iter_cache_ptrs|flecs_iter_cache_variables); + ecs_trace("#[green]release#[reset] build"); } - result.sizes = query->filter.sizes; + ecs_trace("compiled with %s", bi->compiler); +} - return result; -error: -noresults: - result.priv.iter.query.node = NULL; - return result; +/* -- Public functions -- */ + +const ecs_build_info_t* ecs_get_build_info(void) { + return &flecs_build_info; } -void ecs_query_set_group( - ecs_iter_t *it, - uint64_t group_id) +const ecs_world_info_t* ecs_get_world_info( + const ecs_world_t *world) { - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_PARAMETER, NULL); + world = ecs_get_world(world); + return &world->info; +} - ecs_query_iter_t *qit = &it->priv.iter.query; - ecs_query_t *q = qit->query; - ecs_check(q != NULL, ECS_INVALID_PARAMETER, NULL); +ecs_world_t *ecs_mini(void) { +#ifdef FLECS_OS_API_IMPL + ecs_set_os_api_impl(); +#endif + ecs_os_init(); - ecs_query_table_list_t *node = flecs_query_get_group(q, group_id); - if (!node) { - qit->node = NULL; - return; - } + ecs_trace("#[bold]bootstrapping world"); + ecs_log_push(); - ecs_query_table_match_t *first = node->first; - if (first) { - qit->node = node->first; - qit->last = node->last->next; - } else { - qit->node = NULL; - qit->last = NULL; + ecs_trace("tracing enabled, call ecs_log_set_level(-1) to disable"); + + if (!ecs_os_has_heap()) { + ecs_abort(ECS_MISSING_OS_API, NULL); } - -error: - return; -} -const ecs_query_group_info_t* ecs_query_get_group_info( - const ecs_query_t *query, - uint64_t group_id) -{ - ecs_query_table_list_t *node = flecs_query_get_group(query, group_id); - if (!node) { - return NULL; + if (!ecs_os_has_threading()) { + ecs_trace("threading unavailable, to use threads set OS API first (see examples)"); } - - return &node->info; -} -void* ecs_query_get_group_ctx( - const ecs_query_t *query, - uint64_t group_id) -{ - const ecs_query_group_info_t *info = - ecs_query_get_group_info(query, group_id); - if (!info) { - return NULL; - } else { - return info->ctx; + if (!ecs_os_has_time()) { + ecs_trace("time management not available"); } -} -static -void flecs_query_mark_columns_dirty( - ecs_query_t *query, - ecs_query_table_match_t *qm) -{ - ecs_table_t *table = qm->table; - ecs_filter_t *filter = &query->filter; - if ((table && table->dirty_state) || (query->flags & EcsQueryHasNonThisOutTerms)) { - ecs_term_t *terms = filter->terms; - int32_t i, count = filter->term_count; + flecs_log_build_info(); - for (i = 0; i < count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_inout_kind_t inout = term->inout; - if (inout == EcsIn || inout == EcsInOutNone) { - /* Don't mark readonly terms dirty */ - continue; - } + ecs_world_t *world = ecs_os_calloc_t(ecs_world_t); + ecs_assert(world != NULL, ECS_OUT_OF_MEMORY, NULL); + flecs_poly_init(world, ecs_world_t); - flecs_table_column_t tc; - flecs_query_get_column_for_term(query, qm, i, &tc); + world->flags |= EcsWorldInit; - if (tc.column == -1) { - continue; - } + flecs_world_allocators_init(world); + ecs_allocator_t *a = &world->allocator; - ecs_assert(tc.table != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t *dirty_state = tc.table->dirty_state; - if (!dirty_state) { - continue; - } + world->self = world; + flecs_sparse_init_t(&world->type_info, a, + &world->allocators.sparse_chunk, ecs_type_info_t); + ecs_map_init_w_params(&world->id_index_hi, &world->allocators.ptr); + world->id_index_lo = ecs_os_calloc_n(ecs_id_record_t, FLECS_HI_ID_RECORD_ID); + flecs_observable_init(&world->observable); - if (table != tc.table) { - if (inout == EcsInOutDefault) { - continue; - } - } + world->pending_tables = ecs_os_calloc_t(ecs_sparse_t); + flecs_sparse_init_t(world->pending_tables, a, + &world->allocators.sparse_chunk, ecs_table_t*); + world->pending_buffer = ecs_os_calloc_t(ecs_sparse_t); + flecs_sparse_init_t(world->pending_buffer, a, + &world->allocators.sparse_chunk, ecs_table_t*); - ecs_assert(tc.column >= 0, ECS_INTERNAL_ERROR, NULL); + flecs_name_index_init(&world->aliases, a); + flecs_name_index_init(&world->symbols, a); + ecs_vec_init_t(a, &world->fini_actions, ecs_action_elem_t, 0); - dirty_state[tc.column + 1] ++; - } + world->info.time_scale = 1.0; + if (ecs_os_has_time()) { + ecs_os_get_time(&world->world_start_time); } -} - -bool ecs_query_next_table( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - flecs_iter_validate(it); + ecs_set_stage_count(world, 1); + ecs_default_lookup_path[0] = EcsFlecsCore; + ecs_set_lookup_path(world, ecs_default_lookup_path); + flecs_init_store(world); - ecs_query_iter_t *iter = &it->priv.iter.query; - ecs_query_table_match_t *node = iter->node; - ecs_query_t *query = iter->query; + flecs_bootstrap(world); - ecs_query_table_match_t *prev = iter->prev; - if (prev) { - if (query->flags & EcsQueryHasMonitor) { - flecs_query_sync_match_monitor(query, prev); - } - if (query->flags & EcsQueryHasOutTerms) { - if (it->count) { - flecs_query_mark_columns_dirty(query, prev); - } - } - } + world->flags &= ~EcsWorldInit; - if (node != iter->last) { - it->table = node->table; - it->group_id = node->group_id; - it->count = 0; - iter->node = node->next; - iter->prev = node; - return true; - } + ecs_trace("world ready!"); + ecs_log_pop(); -error: - query->match_count = query->prev_match_count; - ecs_iter_fini(it); - return false; + return world; } -static -void flecs_query_populate_trivial( - ecs_iter_t *it, - ecs_query_table_match_t *match) -{; - ecs_table_t *table = match->table; - int32_t offset, count; - if (!it->constrained_vars) { - it->offset = offset = 0; - it->count = count = ecs_table_count(table); - } else { - offset = it->offset; - count = it->count; - } - - it->ids = match->ids; - it->sources = match->sources; - it->columns = match->columns; - it->group_id = match->group_id; - it->instance_count = 0; - it->references = ecs_vec_first(&match->refs); - - if (!it->references) { - ecs_data_t *data = &table->data; - if (!(it->flags & EcsIterNoData)) { - int32_t i; - for (i = 0; i < it->field_count; i ++) { - int32_t column = match->storage_columns[i]; - if (column < 0) { - it->ptrs[i] = NULL; - continue; - } - - ecs_size_t size = it->sizes[i]; - if (!size) { - it->ptrs[i] = NULL; - continue; - } - - it->ptrs[i] = ecs_vec_get(&data->columns[column].data, - it->sizes[i], offset); - } - } +ecs_world_t *ecs_init(void) { + ecs_world_t *world = ecs_mini(); - it->frame_offset += it->table ? ecs_table_count(it->table) : 0; - it->table = table; - it->entities = ecs_vec_get_t(&data->entities, ecs_entity_t, offset); - } else { - flecs_iter_populate_data( - it->real_world, it, table, offset, count, it->ptrs); - } +#ifdef FLECS_MODULE_H + ecs_trace("#[bold]import addons"); + ecs_log_push(); + ecs_trace("use ecs_mini to create world without importing addons"); +#ifdef FLECS_SYSTEM + ECS_IMPORT(world, FlecsSystem); +#endif +#ifdef FLECS_PIPELINE + ECS_IMPORT(world, FlecsPipeline); +#endif +#ifdef FLECS_TIMER + ECS_IMPORT(world, FlecsTimer); +#endif +#ifdef FLECS_META + ECS_IMPORT(world, FlecsMeta); +#endif +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); +#endif +#ifdef FLECS_SCRIPT + ECS_IMPORT(world, FlecsScript); +#endif +#ifdef FLECS_REST + ECS_IMPORT(world, FlecsRest); +#endif +#ifdef FLECS_UNITS + ecs_trace("#[green]module#[reset] flecs.units is not automatically imported"); +#endif + ecs_trace("addons imported!"); + ecs_log_pop(); +#endif + return world; } -int ecs_query_populate( - ecs_iter_t *it, - bool when_changed) +ecs_world_t* ecs_init_w_args( + int argc, + char *argv[]) { - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), - ECS_INVALID_PARAMETER, NULL); - - ecs_query_iter_t *iter = &it->priv.iter.query; - ecs_query_t *query = iter->query; - ecs_query_table_match_t *match = iter->prev; - ecs_assert(match != NULL, ECS_INVALID_OPERATION, NULL); - if (query->flags & EcsQueryTrivialIter) { - flecs_query_populate_trivial(it, match); - return EcsIterNextYield; - } - - ecs_table_t *table = match->table; - ecs_world_t *world = query->filter.world; - const ecs_filter_t *filter = &query->filter; - ecs_entity_filter_iter_t *ent_it = it->priv.entity_iter; - ecs_assert(ent_it != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_range_t *range = &ent_it->range; - int32_t t, term_count = filter->term_count; - int result; - -repeat: - result = EcsIterNextYield; - - ecs_os_memcpy_n(it->sources, match->sources, ecs_entity_t, - filter->field_count); - - for (t = 0; t < term_count; t ++) { - ecs_term_t *term = &filter->terms[t]; - int32_t field = term->field_index; - if (!ecs_term_match_this(term)) { - continue; - } + ecs_world_t *world = ecs_init(); - it->ids[field] = match->ids[field]; - it->columns[field] = match->columns[field]; - } + (void)argc; + (void)argv; - if (table) { - range->offset = match->offset; - range->count = match->count; - if (!range->count) { - range->count = ecs_table_count(table); - ecs_assert(range->count != 0, ECS_INTERNAL_ERROR, NULL); - } - - if (match->entity_filter) { - ent_it->entity_filter = match->entity_filter; - ent_it->columns = match->columns; - ent_it->range.table = table; - ent_it->it = it; - result = flecs_entity_filter_next(ent_it); - if (result == EcsIterNext) { - goto done; - } +#ifdef FLECS_DOC + if (argc) { + char *app = argv[0]; + char *last_elem = strrchr(app, '/'); + if (!last_elem) { + last_elem = strrchr(app, '\\'); } - - it->group_id = match->group_id; - } else { - range->offset = 0; - range->count = 0; - } - - if (when_changed) { - if (!ecs_query_changed(NULL, it)) { - if (result == EcsIterYield) { - goto repeat; - } else { - result = EcsIterNext; - goto done; - } + if (last_elem) { + app = last_elem + 1; } + ecs_set_pair(world, EcsWorld, EcsDocDescription, EcsName, {app}); } +#endif - it->references = ecs_vec_first(&match->refs); - it->instance_count = 0; - - flecs_iter_populate_data(world, it, table, range->offset, range->count, - it->ptrs); - -error: -done: - return result; + return world; } -bool ecs_query_next( - ecs_iter_t *it) +void ecs_quit( + ecs_world_t *world) { - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - - if (flecs_iter_next_row(it)) { - return true; - } - - return flecs_iter_next_instanced(it, ecs_query_next_instanced(it)); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_stage_from_world(&world); + world->flags |= EcsWorldQuit; error: - return false; + return; } -bool ecs_query_next_instanced( - ecs_iter_t *it) +bool ecs_should_quit( + const ecs_world_t *world) { - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - - ecs_query_iter_t *iter = &it->priv.iter.query; - ecs_query_t *query = iter->query; - ecs_flags32_t flags = query->flags; - - ecs_query_table_match_t *prev, *next, *cur = iter->node, *last = iter->last; - if ((prev = iter->prev)) { - /* Match has been iterated, update monitor for change tracking */ - if (flags & EcsQueryHasMonitor) { - flecs_query_sync_match_monitor(query, prev); - } - if (flags & EcsQueryHasOutTerms) { - flecs_query_mark_columns_dirty(query, prev); - } - } - - flecs_iter_validate(it); - iter->skip_count = 0; - - /* Trivial iteration: each entry in the cache is a full match and ids are - * only matched on $this or through traversal starting from $this. */ - if (flags & EcsQueryTrivialIter) { - if (cur == last) { - goto done; - } - iter->node = cur->next; - iter->prev = cur; - flecs_query_populate_trivial(it, cur); - return true; - } - - /* Non-trivial iteration: query matches with static sources, or matches with - * tables that require per-entity filtering. */ - for (; cur != last; cur = next) { - next = cur->next; - iter->prev = cur; - switch(ecs_query_populate(it, false)) { - case EcsIterNext: iter->node = next; continue; - case EcsIterYield: next = cur; /* fall through */ - case EcsIterNextYield: goto yield; - default: ecs_abort(ECS_INTERNAL_ERROR, NULL); - } - } - -done: error: - query->match_count = query->prev_match_count; - ecs_iter_fini(it); - return false; - -yield: - iter->node = next; - iter->prev = cur; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + world = ecs_get_world(world); + return ECS_BIT_IS_SET(world->flags, EcsWorldQuit); +error: return true; } -bool ecs_query_changed( - ecs_query_t *query, - const ecs_iter_t *it) +void flecs_notify_tables( + ecs_world_t *world, + ecs_id_t id, + ecs_table_event_t *event) { - if (it) { - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), - ECS_INVALID_PARAMETER, NULL); - - ecs_query_table_match_t *qm = - (ecs_query_table_match_t*)it->priv.iter.query.prev; - ecs_assert(qm != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_poly_assert(world, ecs_world_t); - if (!query) { - query = it->priv.iter.query.query; - } else { - ecs_check(query == it->priv.iter.query.query, - ECS_INVALID_PARAMETER, NULL); + /* If no id is specified, broadcast to all tables */ + if (!id) { + ecs_sparse_t *tables = &world->store.tables; + int32_t i, count = flecs_sparse_count(tables); + for (i = 0; i < count; i ++) { + ecs_table_t *table = flecs_sparse_get_dense_t(tables, ecs_table_t, i); + flecs_table_notify(world, table, id, event); } - ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_poly_assert(query, ecs_query_t); - - return flecs_query_check_match_monitor(query, qm, it); - } - - ecs_poly_assert(query, ecs_query_t); - ecs_check(!(query->flags & EcsQueryIsOrphaned), - ECS_INVALID_PARAMETER, NULL); - - flecs_process_pending_tables(query->filter.world); + /* If id is specified, only broadcast to tables with id */ + } else { + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return; + } - if (!(query->flags & EcsQueryHasMonitor)) { - query->flags |= EcsQueryHasMonitor; - flecs_query_init_query_monitors(query); - return true; /* Monitors didn't exist yet */ - } + ecs_table_cache_iter_t it; + const ecs_table_record_t *tr; - if (query->match_count != query->prev_match_count) { - return true; + flecs_table_cache_all_iter(&idr->cache, &it); + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + flecs_table_notify(world, tr->hdr.table, id, event); + } } - - return flecs_query_check_query_monitor(query); -error: - return false; } -void ecs_query_skip( - ecs_iter_t *it) +void flecs_default_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *ti) { - ecs_assert(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - ecs_assert(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), - ECS_INVALID_PARAMETER, NULL); - - if (it->instance_count > it->count) { - it->priv.iter.query.skip_count ++; - if (it->priv.iter.query.skip_count == it->instance_count) { - /* For non-instanced queries, make sure all entities are skipped */ - it->priv.iter.query.prev = NULL; - } - } else { - it->priv.iter.query.prev = NULL; - } + ecs_os_memset(ptr, 0, ti->size * count); } -bool ecs_query_orphaned( - const ecs_query_t *query) +static +void flecs_default_copy_ctor(void *dst_ptr, const void *src_ptr, + int32_t count, const ecs_type_info_t *ti) { - ecs_poly_assert(query, ecs_query_t); - return query->flags & EcsQueryIsOrphaned; + const ecs_type_hooks_t *cl = &ti->hooks; + cl->ctor(dst_ptr, count, ti); + cl->copy(dst_ptr, src_ptr, count, ti); } -char* ecs_query_str( - const ecs_query_t *query) +static +void flecs_default_move_ctor(void *dst_ptr, void *src_ptr, + int32_t count, const ecs_type_info_t *ti) { - return ecs_filter_str(query->filter.world, &query->filter); + const ecs_type_hooks_t *cl = &ti->hooks; + cl->ctor(dst_ptr, count, ti); + cl->move(dst_ptr, src_ptr, count, ti); } -int32_t ecs_query_table_count( - const ecs_query_t *query) +static +void flecs_default_ctor_w_move_w_dtor(void *dst_ptr, void *src_ptr, + int32_t count, const ecs_type_info_t *ti) { - ecs_run_aperiodic(query->filter.world, EcsAperiodicEmptyTables); - return query->cache.tables.count; + const ecs_type_hooks_t *cl = &ti->hooks; + cl->ctor(dst_ptr, count, ti); + cl->move(dst_ptr, src_ptr, count, ti); + cl->dtor(src_ptr, count, ti); } -int32_t ecs_query_empty_table_count( - const ecs_query_t *query) +static +void flecs_default_move_ctor_w_dtor(void *dst_ptr, void *src_ptr, + int32_t count, const ecs_type_info_t *ti) { - ecs_run_aperiodic(query->filter.world, EcsAperiodicEmptyTables); - return query->cache.empty_tables.count; + const ecs_type_hooks_t *cl = &ti->hooks; + cl->move_ctor(dst_ptr, src_ptr, count, ti); + cl->dtor(src_ptr, count, ti); } -int32_t ecs_query_entity_count( - const ecs_query_t *query) +static +void flecs_default_move(void *dst_ptr, void *src_ptr, + int32_t count, const ecs_type_info_t *ti) { - ecs_run_aperiodic(query->filter.world, EcsAperiodicEmptyTables); - - int32_t result = 0; - ecs_table_cache_hdr_t *cur, *last = query->cache.tables.last; - if (!last) { - return 0; - } - - for (cur = query->cache.tables.first; cur != NULL; cur = cur->next) { - result += ecs_table_count(cur->table); - } - - return result; + const ecs_type_hooks_t *cl = &ti->hooks; + cl->move(dst_ptr, src_ptr, count, ti); } -void* ecs_query_get_ctx( - const ecs_query_t *query) +static +void flecs_default_dtor(void *dst_ptr, void *src_ptr, + int32_t count, const ecs_type_info_t *ti) { - return query->ctx; + /* When there is no move, destruct the destination component & memcpy the + * component to dst. The src component does not have to be destructed when + * a component has a trivial move. */ + const ecs_type_hooks_t *cl = &ti->hooks; + cl->dtor(dst_ptr, count, ti); + ecs_os_memcpy(dst_ptr, src_ptr, flecs_uto(ecs_size_t, ti->size) * count); } -void* ecs_query_get_binding_ctx( - const ecs_query_t *query) +static +void flecs_default_move_w_dtor(void *dst_ptr, void *src_ptr, + int32_t count, const ecs_type_info_t *ti) { - return query->binding_ctx; + /* If a component has a move, the move will take care of memcpying the data + * and destroying any data in dst. Because this is not a trivial move, the + * src component must also be destructed. */ + const ecs_type_hooks_t *cl = &ti->hooks; + cl->move(dst_ptr, src_ptr, count, ti); + cl->dtor(src_ptr, count, ti); } -/** - * @file search.c - * @brief Search functions to find (component) ids in table types. - * - * Search functions are used to find the column index of a (component) id in a - * table. Additionally, search functions implement the logic for finding a - * component id by following a relationship upwards. - */ +void ecs_set_hooks_id( + ecs_world_t *world, + ecs_entity_t component, + const ecs_type_hooks_t *h) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_stage_from_world(&world); -static -int32_t flecs_type_search( - const ecs_table_t *table, - ecs_id_t search_id, - ecs_id_record_t *idr, - ecs_id_t *ids, - ecs_id_t *id_out, - ecs_table_record_t **tr_out) -{ - ecs_table_record_t *tr = ecs_table_cache_get(&idr->cache, table); - if (tr) { - int32_t r = tr->index; - if (tr_out) tr_out[0] = tr; - if (id_out) { - if (ECS_PAIR_FIRST(search_id) == EcsUnion) { - id_out[0] = ids[r]; - } else { - id_out[0] = flecs_to_public_id(ids[r]); - } - } - return r; - } + /* Ensure that no tables have yet been created for the component */ + ecs_assert( ecs_id_in_use(world, component) == false, + ECS_ALREADY_IN_USE, ecs_get_name(world, component)); + ecs_assert( ecs_id_in_use(world, ecs_pair(component, EcsWildcard)) == false, + ECS_ALREADY_IN_USE, ecs_get_name(world, component)); - return -1; -} + ecs_type_info_t *ti = flecs_type_info_ensure(world, component); + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); -static -int32_t flecs_type_offset_search( - int32_t offset, - ecs_id_t id, - ecs_id_t *ids, - int32_t count, - ecs_id_t *id_out) -{ - ecs_assert(ids != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(count > 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(offset > 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(!ti->component || ti->component == component, + ECS_INCONSISTENT_COMPONENT_ACTION, NULL); - while (offset < count) { - ecs_id_t type_id = ids[offset ++]; - if (ecs_id_match(type_id, id)) { - if (id_out) { - id_out[0] = flecs_to_public_id(type_id); - } - return offset - 1; - } - } + if (!ti->size) { + const EcsComponent *component_ptr = ecs_get( + world, component, EcsComponent); - return -1; -} + /* Cannot register lifecycle actions for things that aren't a component */ + ecs_check(component_ptr != NULL, ECS_INVALID_PARAMETER, + "provided entity is not a component"); + ecs_check(component_ptr->size != 0, ECS_INVALID_PARAMETER, + "cannot register type hooks for type with size 0"); -bool flecs_type_can_inherit_id( - const ecs_world_t *world, - const ecs_table_t *table, - const ecs_id_record_t *idr, - ecs_id_t id) -{ - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - if (idr->flags & EcsIdDontInherit) { - return false; - } - if (idr->flags & EcsIdExclusive) { - if (ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_entity_t er = ECS_PAIR_FIRST(id); - if (flecs_table_record_get( - world, table, ecs_pair(er, EcsWildcard))) - { - return false; - } - } + ti->size = component_ptr->size; + ti->alignment = component_ptr->alignment; } - return true; -} - -static -int32_t flecs_type_search_relation( - const ecs_world_t *world, - const ecs_table_t *table, - int32_t offset, - ecs_id_t id, - ecs_id_record_t *idr, - ecs_id_t rel, - ecs_id_record_t *idr_r, - bool self, - ecs_entity_t *subject_out, - ecs_id_t *id_out, - ecs_table_record_t **tr_out) -{ - ecs_type_t type = table->type; - ecs_id_t *ids = type.array; - int32_t count = type.count; - if (self) { - if (offset) { - int32_t r = flecs_type_offset_search(offset, id, ids, count, id_out); - if (r != -1) { - return r; - } - } else { - int32_t r = flecs_type_search(table, id, idr, ids, id_out, tr_out); - if (r != -1) { - return r; - } - } - } + if (h->ctor) ti->hooks.ctor = h->ctor; + if (h->dtor) ti->hooks.dtor = h->dtor; + if (h->copy) ti->hooks.copy = h->copy; + if (h->move) ti->hooks.move = h->move; + if (h->copy_ctor) ti->hooks.copy_ctor = h->copy_ctor; + if (h->move_ctor) ti->hooks.move_ctor = h->move_ctor; + if (h->ctor_move_dtor) ti->hooks.ctor_move_dtor = h->ctor_move_dtor; + if (h->move_dtor) ti->hooks.move_dtor = h->move_dtor; - ecs_flags32_t flags = table->flags; - if ((flags & EcsTableHasPairs) && rel) { - bool is_a = rel == ecs_pair(EcsIsA, EcsWildcard); - if (is_a) { - if (!(flags & EcsTableHasIsA)) { - return -1; - } - idr_r = world->idr_isa_wildcard; + if (h->on_add) ti->hooks.on_add = h->on_add; + if (h->on_remove) ti->hooks.on_remove = h->on_remove; + if (h->on_set) ti->hooks.on_set = h->on_set; - if (!flecs_type_can_inherit_id(world, table, idr, id)) { - return -1; - } - } + if (h->ctx) ti->hooks.ctx = h->ctx; + if (h->binding_ctx) ti->hooks.binding_ctx = h->binding_ctx; + if (h->lifecycle_ctx) ti->hooks.lifecycle_ctx = h->lifecycle_ctx; + if (h->ctx_free) ti->hooks.ctx_free = h->ctx_free; + if (h->binding_ctx_free) ti->hooks.binding_ctx_free = h->binding_ctx_free; + if (h->lifecycle_ctx_free) ti->hooks.lifecycle_ctx_free = h->lifecycle_ctx_free; - if (!idr_r) { - idr_r = flecs_id_record_get(world, rel); - if (!idr_r) { - return -1; - } - } + /* If no constructor is set, invoking any of the other lifecycle actions + * is not safe as they will potentially access uninitialized memory. For + * ease of use, if no constructor is specified, set a default one that + * initializes the component to 0. */ + if (!h->ctor && (h->dtor || h->copy || h->move)) { + ti->hooks.ctor = flecs_default_ctor; + } - ecs_id_t id_r; - int32_t r, r_column; - if (offset) { - r_column = flecs_type_offset_search(offset, rel, ids, count, &id_r); - } else { - r_column = flecs_type_search(table, id, idr_r, ids, &id_r, 0); - } - while (r_column != -1) { - ecs_entity_t obj = ECS_PAIR_SECOND(id_r); - ecs_assert(obj != 0, ECS_INTERNAL_ERROR, NULL); + /* Set default copy ctor, move ctor and merge */ + if (h->copy && !h->copy_ctor) { + ti->hooks.copy_ctor = flecs_default_copy_ctor; + } - ecs_record_t *rec = flecs_entities_get_any(world, obj); - ecs_assert(rec != NULL, ECS_INTERNAL_ERROR, NULL); + if (h->move && !h->move_ctor) { + ti->hooks.move_ctor = flecs_default_move_ctor; + } - ecs_table_t *obj_table = rec->table; - if (obj_table) { - ecs_assert(obj_table != table, ECS_CYCLE_DETECTED, NULL); - - r = flecs_type_search_relation(world, obj_table, 0, id, idr, - rel, idr_r, true, subject_out, id_out, tr_out); - if (r != -1) { - if (subject_out && !subject_out[0]) { - subject_out[0] = ecs_get_alive(world, obj); - } - return r_column; + if (!h->ctor_move_dtor) { + if (h->move) { + if (h->dtor) { + if (h->move_ctor) { + /* If an explicit move ctor has been set, use callback + * that uses the move ctor vs. using a ctor+move */ + ti->hooks.ctor_move_dtor = flecs_default_move_ctor_w_dtor; + } else { + /* If no explicit move_ctor has been set, use + * combination of ctor + move + dtor */ + ti->hooks.ctor_move_dtor = flecs_default_ctor_w_move_w_dtor; } - - if (!is_a) { - r = flecs_type_search_relation(world, obj_table, 0, id, idr, - ecs_pair(EcsIsA, EcsWildcard), world->idr_isa_wildcard, - true, subject_out, id_out, tr_out); - if (r != -1) { - if (subject_out && !subject_out[0]) { - subject_out[0] = ecs_get_alive(world, obj); - } - return r_column; - } + } else { + /* If no dtor has been set, this is just a move ctor */ + ti->hooks.ctor_move_dtor = ti->hooks.move_ctor; + } + } else { + /* If move is not set but move_ctor and dtor is, we can still set + * ctor_move_dtor. */ + if (h->move_ctor) { + if (h->dtor) { + ti->hooks.ctor_move_dtor = flecs_default_move_ctor_w_dtor; + } else { + ti->hooks.ctor_move_dtor = ti->hooks.move_ctor; } } - - r_column = flecs_type_offset_search( - r_column + 1, rel, ids, count, &id_r); - } - } - - return -1; -} - -int32_t flecs_search_relation_w_idr( - const ecs_world_t *world, - const ecs_table_t *table, - int32_t offset, - ecs_id_t id, - ecs_entity_t rel, - ecs_flags32_t flags, - ecs_entity_t *subject_out, - ecs_id_t *id_out, - struct ecs_table_record_t **tr_out, - ecs_id_record_t *idr) -{ - if (!table) return -1; - - ecs_poly_assert(world, ecs_world_t); - ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - - flags = flags ? flags : (EcsSelf|EcsUp); - - if (!idr) { - idr = flecs_query_id_record_get(world, id); - if (!idr) { - return -1; } } - if (subject_out) subject_out[0] = 0; - if (!(flags & EcsUp)) { - if (offset) { - return ecs_search_offset(world, table, offset, id, id_out); + if (!h->move_dtor) { + if (h->move) { + if (h->dtor) { + ti->hooks.move_dtor = flecs_default_move_w_dtor; + } else { + ti->hooks.move_dtor = flecs_default_move; + } } else { - return flecs_type_search( - table, id, idr, table->type.array, id_out, tr_out); + if (h->dtor) { + ti->hooks.move_dtor = flecs_default_dtor; + } } } - int32_t result = flecs_type_search_relation(world, table, offset, id, idr, - ecs_pair(rel, EcsWildcard), NULL, flags & EcsSelf, subject_out, - id_out, tr_out); - - return result; +error: + return; } -int32_t ecs_search_relation( +const ecs_type_hooks_t* ecs_get_hooks_id( const ecs_world_t *world, - const ecs_table_t *table, - int32_t offset, - ecs_id_t id, - ecs_entity_t rel, - ecs_flags32_t flags, - ecs_entity_t *subject_out, - ecs_id_t *id_out, - struct ecs_table_record_t **tr_out) + ecs_entity_t id) { - if (!table) return -1; - - ecs_poly_assert(world, ecs_world_t); - ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - - flags = flags ? flags : (EcsSelf|EcsUp); - - if (subject_out) subject_out[0] = 0; - if (!(flags & EcsUp)) { - return ecs_search_offset(world, table, offset, id, id_out); - } - - ecs_id_record_t *idr = flecs_query_id_record_get(world, id); - if (!idr) { - return -1; + const ecs_type_info_t *ti = ecs_get_type_info(world, id); + if (ti) { + return &ti->hooks; } - - int32_t result = flecs_type_search_relation(world, table, offset, id, idr, - ecs_pair(rel, EcsWildcard), NULL, flags & EcsSelf, subject_out, - id_out, tr_out); - - return result; + return NULL; } -int32_t flecs_search_w_idr( - const ecs_world_t *world, - const ecs_table_t *table, - ecs_id_t id, - ecs_id_t *id_out, - ecs_id_record_t *idr) +void ecs_atfini( + ecs_world_t *world, + ecs_fini_action_t action, + void *ctx) { - if (!table) return -1; + flecs_poly_assert(world, ecs_world_t); + ecs_check(action != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_poly_assert(world, ecs_world_t); - ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - (void)world; + ecs_action_elem_t *elem = ecs_vec_append_t(NULL, &world->fini_actions, + ecs_action_elem_t); + ecs_assert(elem != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_type_t type = table->type; - ecs_id_t *ids = type.array; - return flecs_type_search(table, id, idr, ids, id_out, 0); + elem->action = action; + elem->ctx = ctx; +error: + return; } -int32_t ecs_search( - const ecs_world_t *world, - const ecs_table_t *table, - ecs_id_t id, - ecs_id_t *id_out) +void ecs_run_post_frame( + ecs_world_t *world, + ecs_fini_action_t action, + void *ctx) { - if (!table) return -1; - - ecs_poly_assert(world, ecs_world_t); - ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - - ecs_id_record_t *idr = flecs_query_id_record_get(world, id); - if (!idr) { - return -1; - } - - ecs_type_t type = table->type; - ecs_id_t *ids = type.array; - return flecs_type_search(table, id, idr, ids, id_out, 0); -} + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(action != NULL, ECS_INVALID_PARAMETER, NULL); -int32_t ecs_search_offset( - const ecs_world_t *world, - const ecs_table_t *table, - int32_t offset, - ecs_id_t id, - ecs_id_t *id_out) -{ - if (!offset) { - ecs_poly_assert(world, ecs_world_t); - return ecs_search(world, table, id, id_out); - } + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_check((world->flags & EcsWorldFrameInProgress), ECS_INVALID_OPERATION, + "cannot register post frame action while frame is not in progress"); - if (!table) return -1; + ecs_action_elem_t *elem = ecs_vec_append_t(&stage->allocator, + &stage->post_frame_actions, ecs_action_elem_t); + ecs_assert(elem != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_type_t type = table->type; - ecs_id_t *ids = type.array; - int32_t count = type.count; - return flecs_type_offset_search(offset, id, ids, count, id_out); + elem->action = action; + elem->ctx = ctx; +error: + return; } +/* Unset data in tables */ static -int32_t flecs_relation_depth_walk( - const ecs_world_t *world, - const ecs_id_record_t *idr, - const ecs_table_t *first, - const ecs_table_t *table) +void flecs_fini_unset_tables( + ecs_world_t *world) { - int32_t result = 0; - - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (!tr) { - return 0; - } - - int32_t i = tr->index, end = i + tr->count; - for (; i != end; i ++) { - ecs_entity_t o = ecs_pair_second(world, table->type.array[i]); - ecs_assert(o != 0, ECS_INTERNAL_ERROR, NULL); + ecs_sparse_t *tables = &world->store.tables; + int32_t i, count = flecs_sparse_count(tables); - ecs_table_t *ot = ecs_get_table(world, o); - if (!ot) { - continue; - } - - ecs_assert(ot != first, ECS_CYCLE_DETECTED, NULL); - int32_t cur = flecs_relation_depth_walk(world, idr, first, ot); - if (cur > result) { - result = cur; - } + for (i = 0; i < count; i ++) { + ecs_table_t *table = flecs_sparse_get_dense_t(tables, ecs_table_t, i); + flecs_table_remove_actions(world, table); } - - return result + 1; } -int32_t flecs_relation_depth( - const ecs_world_t *world, - ecs_entity_t r, - const ecs_table_t *table) +/* Invoke fini actions */ +static +void flecs_fini_actions( + ecs_world_t *world) { - ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair(r, EcsWildcard)); - if (!idr) { - return 0; - } - - int32_t depth_offset = 0; - if (table->flags & EcsTableHasTarget) { - if (ecs_table_get_type_index(world, table, - ecs_pair_t(EcsTarget, r)) != -1) - { - ecs_id_t id; - int32_t col = ecs_search(world, table, - ecs_pair(EcsFlatten, EcsWildcard), &id); - if (col == -1) { - return 0; - } - - ecs_entity_t did = ecs_pair_second(world, id); - ecs_assert(did != 0, ECS_INTERNAL_ERROR, NULL); - uint64_t *val = ecs_map_get(&world->store.entity_to_depth, did); - ecs_assert(val != NULL, ECS_INTERNAL_ERROR, NULL); - depth_offset = flecs_uto(int32_t, val[0]); - } + int32_t i, count = ecs_vec_count(&world->fini_actions); + ecs_action_elem_t *elems = ecs_vec_first(&world->fini_actions); + for (i = 0; i < count; i ++) { + elems[i].action(world, elems[i].ctx); } - return flecs_relation_depth_walk(world, idr, table, table) + depth_offset; + ecs_vec_fini_t(NULL, &world->fini_actions, ecs_action_elem_t); } -/** - * @file stage.c - * @brief Staging implementation. - * - * A stage is an object that can be used to temporarily store mutations to a - * world while a world is in readonly mode. ECS operations that are invoked on - * a stage are stored in a command buffer, which is flushed during sync points, - * or manually by the user. - * - * Stages contain additional state to enable other API functionality without - * having to mutate the world, such as setting the current scope, and allocators - * that are local to a stage. - * - * In a multi threaded application, each thread has its own stage which allows - * threads to insert mutations without having to lock administration. - */ - - +/* Cleanup remaining type info elements */ static -ecs_cmd_t* flecs_cmd_new( - ecs_stage_t *stage) +void flecs_fini_type_info( + ecs_world_t *world) { - ecs_cmd_t *cmd = ecs_vec_append_t(&stage->allocator, &stage->cmd->queue, - ecs_cmd_t); - cmd->is._1.value = NULL; - cmd->next_for_entity = 0; - cmd->entry = NULL; - return cmd; + int32_t i, count = flecs_sparse_count(&world->type_info); + ecs_sparse_t *type_info = &world->type_info; + for (i = 0; i < count; i ++) { + ecs_type_info_t *ti = flecs_sparse_get_dense_t(type_info, + ecs_type_info_t, i); + flecs_type_info_fini(ti); + } + flecs_sparse_fini(&world->type_info); } -static -ecs_cmd_t* flecs_cmd_new_batched( - ecs_stage_t *stage, +ecs_entity_t flecs_get_oneof( + const ecs_world_t *world, ecs_entity_t e) { - ecs_vec_t *cmds = &stage->cmd->queue; - ecs_cmd_entry_t *entry = flecs_sparse_get_any_t( - &stage->cmd->entries, ecs_cmd_entry_t, e); - - int32_t cur = ecs_vec_count(cmds); - ecs_cmd_t *cmd = flecs_cmd_new(stage); - if (entry) { - if (entry->first == -1) { - /* Existing but invalidated entry */ - entry->first = cur; - cmd->entry = entry; + if (ecs_is_alive(world, e)) { + if (ecs_has_id(world, e, EcsOneOf)) { + return e; } else { - int32_t last = entry->last; - ecs_cmd_t *arr = ecs_vec_first_t(cmds, ecs_cmd_t); - ecs_assert(arr[last].entity == e, ECS_INTERNAL_ERROR, NULL); - ecs_cmd_t *last_op = &arr[last]; - last_op->next_for_entity = cur; - if (last == entry->first) { - /* Flip sign bit so flush logic can tell which command - * is the first for an entity */ - last_op->next_for_entity *= -1; - } + return ecs_get_target(world, e, EcsOneOf, 0); } } else { - cmd->entry = entry = flecs_sparse_ensure_fast_t( - &stage->cmd->entries, ecs_cmd_entry_t, e); - entry->first = cur; + return 0; } - - entry->last = cur; - - return cmd; } -static -void flecs_stages_merge( - ecs_world_t *world, - bool force_merge) +/* The destroyer of worlds */ +int ecs_fini( + ecs_world_t *world) { - bool is_stage = ecs_poly_is(world, ecs_stage_t); - ecs_stage_t *stage = flecs_stage_from_world(&world); - - bool measure_frame_time = ECS_BIT_IS_SET(world->flags, - EcsWorldMeasureFrameTime); + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot fini world while it is in readonly mode"); + ecs_assert(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, + "cannot fini world when it is already being deleted"); + ecs_assert(world->stages[0]->defer == 0, ECS_INVALID_OPERATION, + "call defer_end before destroying world"); - ecs_time_t t_start = {0}; - if (measure_frame_time) { - ecs_os_get_time(&t_start); - } + ecs_trace("#[bold]shutting down world"); + ecs_log_push(); - ecs_dbg_3("#[magenta]merge"); - ecs_log_push_3(); + world->flags |= EcsWorldQuit; - if (is_stage) { - /* Check for consistency if force_merge is enabled. In practice this - * function will never get called with force_merge disabled for just - * a single stage. */ - if (force_merge || stage->auto_merge) { - ecs_assert(stage->defer == 1, ECS_INVALID_OPERATION, - "mismatching defer_begin/defer_end detected"); - flecs_defer_end(world, stage); - } - } else { - /* Merge stages. Only merge if the stage has auto_merging turned on, or - * if this is a forced merge (like when ecs_merge is called) */ - int32_t i, count = ecs_get_stage_count(world); - for (i = 0; i < count; i ++) { - ecs_stage_t *s = (ecs_stage_t*)ecs_get_stage(world, i); - ecs_poly_assert(s, ecs_stage_t); - if (force_merge || s->auto_merge) { - flecs_defer_end(world, s); - } - } - } + /* Delete root entities first using regular APIs. This ensures that cleanup + * policies get a chance to execute. */ + ecs_dbg_1("#[bold]cleanup root entities"); + ecs_log_push_1(); + flecs_fini_roots(world); + ecs_log_pop_1(); - flecs_eval_component_monitors(world); + world->flags |= EcsWorldFini; - if (measure_frame_time) { - world->info.merge_time_total += (ecs_ftime_t)ecs_time_measure(&t_start); - } + /* Run fini actions (simple callbacks ran when world is deleted) before + * destroying the storage */ + ecs_dbg_1("#[bold]run fini actions"); + ecs_log_push_1(); + flecs_fini_actions(world); + ecs_log_pop_1(); - world->info.merge_count_total ++; + ecs_dbg_1("#[bold]cleanup remaining entities"); + ecs_log_push_1(); - /* If stage is asynchronous, deferring is always enabled */ - if (stage->async) { - flecs_defer_begin(world, stage); + /* Operations invoked during OnRemove/destructors are deferred and + * will be discarded after world cleanup */ + flecs_defer_begin(world, world->stages[0]); + + /* Run OnRemove actions for components while the store is still + * unmodified by cleanup. */ + flecs_fini_unset_tables(world); + + /* This will destroy all entities and components. */ + flecs_fini_store(world); + + /* Purge deferred operations from the queue. This discards operations but + * makes sure that any resources in the queue are freed */ + flecs_defer_purge(world, world->stages[0]); + ecs_log_pop_1(); + + /* All queries are cleaned up, so monitors should've been cleaned up too */ + ecs_assert(!ecs_map_is_init(&world->monitors.monitors), + ECS_INTERNAL_ERROR, NULL); + + /* Cleanup world ctx and binding_ctx */ + if (world->ctx_free) { + world->ctx_free(world->ctx); } - - ecs_log_pop_3(); + if (world->binding_ctx_free) { + world->binding_ctx_free(world->binding_ctx); + } + + /* After this point no more user code is invoked */ + + ecs_dbg_1("#[bold]cleanup world data structures"); + ecs_log_push_1(); + flecs_entities_fini(world); + flecs_sparse_fini(world->pending_tables); + flecs_sparse_fini(world->pending_buffer); + ecs_os_free(world->pending_tables); + ecs_os_free(world->pending_buffer); + flecs_fini_id_records(world); + flecs_fini_type_info(world); + flecs_observable_fini(&world->observable); + flecs_name_index_fini(&world->aliases); + flecs_name_index_fini(&world->symbols); + ecs_set_stage_count(world, 0); + ecs_log_pop_1(); + + flecs_world_allocators_fini(world); + + /* End of the world */ + flecs_poly_free(world, ecs_world_t); + ecs_os_fini(); + + ecs_trace("world destroyed, bye!"); + ecs_log_pop(); + + return 0; } -static -void flecs_stage_auto_merge( - ecs_world_t *world) +bool ecs_is_fini( + const ecs_world_t *world) { - flecs_stages_merge(world, false); + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); + world = ecs_get_world(world); + return ECS_BIT_IS_SET(world->flags, EcsWorldFini); } -static -void flecs_stage_manual_merge( +void ecs_dim( + ecs_world_t *world, + int32_t entity_count) +{ + flecs_poly_assert(world, ecs_world_t); + flecs_entities_set_size(world, entity_count + FLECS_HI_COMPONENT_ID); +} + +void flecs_eval_component_monitors( ecs_world_t *world) { - flecs_stages_merge(world, true); + flecs_poly_assert(world, ecs_world_t); + flecs_process_pending_tables(world); + flecs_eval_component_monitor(world); } -bool flecs_defer_begin( +void ecs_measure_frame_time( ecs_world_t *world, - ecs_stage_t *stage) + bool enable) { - ecs_poly_assert(world, ecs_world_t); - ecs_poly_assert(stage, ecs_stage_t); - (void)world; - if (stage->defer < 0) return false; - return (++ stage->defer) == 1; + flecs_poly_assert(world, ecs_world_t); + ecs_check(ecs_os_has_time(), ECS_MISSING_OS_API, NULL); + + if (ECS_EQZERO(world->info.target_fps) || enable) { + ECS_BIT_COND(world->flags, EcsWorldMeasureFrameTime, enable); + } +error: + return; } -bool flecs_defer_cmd( - ecs_stage_t *stage) +void ecs_measure_system_time( + ecs_world_t *world, + bool enable) { - if (stage->defer) { - return (stage->defer > 0); - } + flecs_poly_assert(world, ecs_world_t); + ecs_check(ecs_os_has_time(), ECS_MISSING_OS_API, NULL); + ECS_BIT_COND(world->flags, EcsWorldMeasureSystemTime, enable); +error: + return; +} - stage->defer ++; - return false; +void ecs_set_target_fps( + ecs_world_t *world, + ecs_ftime_t fps) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(ecs_os_has_time(), ECS_MISSING_OS_API, NULL); + + ecs_measure_frame_time(world, true); + world->info.target_fps = fps; +error: + return; } -bool flecs_defer_modified( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_id_t id) +void ecs_set_default_query_flags( + ecs_world_t *world, + ecs_flags32_t flags) { - if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); - if (cmd) { - cmd->kind = EcsCmdModified; - cmd->id = id; - cmd->entity = entity; - } - return true; - } - return false; + flecs_poly_assert(world, ecs_world_t); + flecs_process_pending_tables(world); + world->default_query_flags = flags; } -bool flecs_defer_clone( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_entity_t src, - bool clone_value) -{ - if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage); - cmd->kind = EcsCmdClone; - cmd->id = src; - cmd->entity = entity; - cmd->is._1.clone_value = clone_value; - return true; - } - return false; +void* ecs_get_ctx( + const ecs_world_t *world) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + world = ecs_get_world(world); + return world->ctx; +error: + return NULL; } -bool flecs_defer_path( - ecs_stage_t *stage, - ecs_entity_t parent, - ecs_entity_t entity, - const char *name) +void* ecs_get_binding_ctx( + const ecs_world_t *world) { - if (stage->defer > 0) { - ecs_cmd_t *cmd = flecs_cmd_new(stage); - cmd->kind = EcsCmdPath; - cmd->entity = entity; - cmd->id = parent; - cmd->is._1.value = ecs_os_strdup(name); - return true; - } - return false; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + world = ecs_get_world(world); + return world->binding_ctx; +error: + return NULL; } -bool flecs_defer_delete( - ecs_stage_t *stage, - ecs_entity_t entity) +void ecs_set_ctx( + ecs_world_t *world, + void *ctx, + ecs_ctx_free_t ctx_free) { - if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage); - cmd->kind = EcsCmdDelete; - cmd->entity = entity; - return true; - } - return false; + flecs_poly_assert(world, ecs_world_t); + world->ctx = ctx; + world->ctx_free = ctx_free; } -bool flecs_defer_clear( - ecs_stage_t *stage, - ecs_entity_t entity) +void ecs_set_binding_ctx( + ecs_world_t *world, + void *ctx, + ecs_ctx_free_t ctx_free) { - if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); - cmd->kind = EcsCmdClear; - cmd->entity = entity; - return true; - } - return false; + flecs_poly_assert(world, ecs_world_t); + world->binding_ctx = ctx; + world->binding_ctx_free = ctx_free; } -bool flecs_defer_on_delete_action( - ecs_stage_t *stage, - ecs_id_t id, - ecs_entity_t action) +void ecs_set_entity_range( + ecs_world_t *world, + ecs_entity_t id_start, + ecs_entity_t id_end) { - if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage); - cmd->kind = EcsCmdOnDeleteAction; - cmd->id = id; - cmd->entity = action; - return true; + flecs_poly_assert(world, ecs_world_t); + ecs_check(!id_end || id_end > id_start, ECS_INVALID_PARAMETER, NULL); + + if (id_start == 0) { + id_start = flecs_entities_max_id(world) + 1; } - return false; + + uint32_t start = (uint32_t)id_start; + uint32_t end = (uint32_t)id_end; + + flecs_entities_max_id(world) = start - 1; + + world->info.min_id = start; + world->info.max_id = end; +error: + return; } -bool flecs_defer_enable( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_id_t id, +bool ecs_enable_range_check( + ecs_world_t *world, bool enable) { - if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage); - cmd->kind = enable ? EcsCmdEnable : EcsCmdDisable; - cmd->entity = entity; - cmd->id = id; - return true; - } - return false; + flecs_poly_assert(world, ecs_world_t); + bool old_value = world->range_check_enabled; + world->range_check_enabled = enable; + return old_value; } -bool flecs_defer_bulk_new( - ecs_world_t *world, - ecs_stage_t *stage, - int32_t count, - ecs_id_t id, - const ecs_entity_t **ids_out) +ecs_entity_t ecs_get_max_id( + const ecs_world_t *world) { - if (flecs_defer_cmd(stage)) { - ecs_entity_t *ids = ecs_os_malloc(count * ECS_SIZEOF(ecs_entity_t)); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + world = ecs_get_world(world); + return flecs_entities_max_id(world); +error: + return 0; +} - /* Use ecs_new_id as this is thread safe */ - int i; - for (i = 0; i < count; i ++) { - ids[i] = ecs_new_id(world); - } +const ecs_type_info_t* flecs_type_info_get( + const ecs_world_t *world, + ecs_entity_t component) +{ + flecs_poly_assert(world, ecs_world_t); - *ids_out = ids; + ecs_assert(component != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!(component & ECS_ID_FLAGS_MASK), ECS_INTERNAL_ERROR, NULL); - /* Store data in op */ - ecs_cmd_t *cmd = flecs_cmd_new(stage); - cmd->kind = EcsCmdBulkNew; - cmd->id = id; - cmd->is._n.entities = ids; - cmd->is._n.count = count; - cmd->entity = 0; - return true; - } - return false; + return flecs_sparse_try_t(&world->type_info, ecs_type_info_t, component); } -bool flecs_defer_add( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_id_t id) +ecs_type_info_t* flecs_type_info_ensure( + ecs_world_t *world, + ecs_entity_t component) { - if (flecs_defer_cmd(stage)) { - ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); - ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); - cmd->kind = EcsCmdAdd; - cmd->id = id; - cmd->entity = entity; - return true; + flecs_poly_assert(world, ecs_world_t); + ecs_assert(component != 0, ECS_INTERNAL_ERROR, NULL); + + const ecs_type_info_t *ti = flecs_type_info_get(world, component); + ecs_type_info_t *ti_mut = NULL; + if (!ti) { + ti_mut = flecs_sparse_ensure_t( + &world->type_info, ecs_type_info_t, component); + ecs_assert(ti_mut != NULL, ECS_INTERNAL_ERROR, NULL); + ti_mut->component = component; + } else { + ti_mut = ECS_CONST_CAST(ecs_type_info_t*, ti); } - return false; -} -bool flecs_defer_remove( - ecs_stage_t *stage, - ecs_entity_t entity, - ecs_id_t id) -{ - if (flecs_defer_cmd(stage)) { - ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); - ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); - cmd->kind = EcsCmdRemove; - cmd->id = id; - cmd->entity = entity; - return true; + if (!ti_mut->name) { + const char *sym = ecs_get_symbol(world, component); + if (sym) { + ti_mut->name = ecs_os_strdup(sym); + } else { + const char *name = ecs_get_name(world, component); + if (name) { + ti_mut->name = ecs_os_strdup(name); + } + } } - return false; + + return ti_mut; } -void* flecs_defer_set( +bool flecs_type_info_init_id( ecs_world_t *world, - ecs_stage_t *stage, - ecs_cmd_kind_t cmd_kind, - ecs_entity_t entity, - ecs_id_t id, + ecs_entity_t component, ecs_size_t size, - void *value) + ecs_size_t alignment, + const ecs_type_hooks_t *li) { - ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); + bool changed = false; - /* 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. */ - 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. */ - 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. */ - ti = idr->type_info; - } + flecs_entities_ensure(world, component); + + ecs_type_info_t *ti = NULL; + if (!size || !alignment) { + ecs_assert(size == 0 && alignment == 0, + ECS_INVALID_COMPONENT_SIZE, NULL); + ecs_assert(li == NULL, ECS_INCONSISTENT_COMPONENT_ACTION, NULL); + flecs_sparse_remove_t(&world->type_info, ecs_type_info_t, component); } else { - ti = idr->type_info; + ti = flecs_type_info_ensure(world, component); + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + changed |= ti->size != size; + changed |= ti->alignment != alignment; + ti->size = size; + ti->alignment = alignment; + if (li) { + ecs_set_hooks_id(world, component, li); + } } - /* If the id isn't associated with a type, we can't set anything */ - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); + /* Set type info for id record of component */ + ecs_id_record_t *idr = flecs_id_record_ensure(world, component); + changed |= flecs_id_record_set_type_info(world, idr, ti); + bool is_tag = idr->flags & EcsIdTag; - /* Make sure the size of the value equals the type size */ - ecs_assert(!size || size == ti->size, ECS_INVALID_PARAMETER, NULL); - size = ti->size; + /* All id records with component as relationship inherit type info */ + idr = flecs_id_record_ensure(world, ecs_pair(component, EcsWildcard)); + do { + if (is_tag) { + changed |= flecs_id_record_set_type_info(world, idr, NULL); + } else if (ti) { + changed |= flecs_id_record_set_type_info(world, idr, ti); + } else if ((idr->type_info != NULL) && + (idr->type_info->component == component)) + { + changed |= flecs_id_record_set_type_info(world, idr, NULL); + } + } while ((idr = idr->first.next)); - /* 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; - 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) { - const ecs_table_record_t *tr = flecs_id_record_get_table( - idr, table); - if (tr) { - ecs_assert(tr->column != -1, ECS_NOT_A_COMPONENT, NULL); - /* Entity has the component */ - ecs_vec_t *column = &table->data.columns[tr->column].data; - existing = ecs_vec_get(column, size, ECS_RECORD_TO_ROW(r->row)); - } + /* All non-tag id records with component as object inherit type info, + * if relationship doesn't have type info */ + idr = flecs_id_record_ensure(world, ecs_pair(EcsWildcard, component)); + do { + if (!(idr->flags & EcsIdTag) && !idr->type_info) { + changed |= flecs_id_record_set_type_info(world, idr, ti); } + } while ((idr = idr->first.next)); + + /* Type info of (*, component) should always point to component */ + ecs_assert(flecs_id_record_get(world, ecs_pair(EcsWildcard, component))-> + type_info == ti, ECS_INTERNAL_ERROR, NULL); + + return changed; +} + +void flecs_type_info_fini( + ecs_type_info_t *ti) +{ + if (ti->hooks.ctx_free) { + ti->hooks.ctx_free(ti->hooks.ctx); + } + if (ti->hooks.binding_ctx_free) { + ti->hooks.binding_ctx_free(ti->hooks.binding_ctx); + } + if (ti->hooks.lifecycle_ctx_free) { + ti->hooks.lifecycle_ctx_free(ti->hooks.lifecycle_ctx); + } + if (ti->name) { + /* Safe to cast away const, world has ownership over string */ + ecs_os_free(ECS_CONST_CAST(char*, ti->name)); + ti->name = NULL; } - /* Get existing value from storage */ - void *cmd_value = existing; - bool emplace = cmd_kind == EcsCmdEmplace; + ti->size = 0; + ti->alignment = 0; +} - /* 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->cmd->stack; - cmd_value = flecs_stack_alloc(stack, size, ti->alignment); +void flecs_type_info_free( + ecs_world_t *world, + ecs_entity_t component) +{ + if (world->flags & EcsWorldQuit) { + /* If world is in the final teardown stages, cleanup policies are no + * longer applied and it can't be guaranteed that a component is not + * deleted before entities that use it. The remaining type info elements + * will be deleted after the store is finalized. */ + return; + } - /* 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 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 a command when the entity was - * already deleted. */ - return cmd_value; - } - cmd = flecs_cmd_new(stage); - } - - 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 == EcsCmdSet) { - cmd->kind = EcsCmdAddModified; - } else { - cmd->kind = EcsCmdAdd; - } - cmd->id = id; - cmd->entity = entity; + ecs_type_info_t *ti = flecs_sparse_try_t(&world->type_info, + ecs_type_info_t, component); + if (ti) { + flecs_type_info_fini(ti); + flecs_sparse_remove_t(&world->type_info, ecs_type_info_t, component); } - - return cmd_value; -error: - return NULL; } -void flecs_enqueue( +static +ecs_ftime_t flecs_insert_sleep( ecs_world_t *world, - ecs_stage_t *stage, - ecs_event_desc_t *desc) + ecs_time_t *stop) { - ecs_cmd_t *cmd = flecs_cmd_new(stage); - cmd->kind = EcsCmdEvent; - cmd->entity = desc->entity; + flecs_poly_assert(world, ecs_world_t); - ecs_stack_t *stack = &stage->cmd->stack; - ecs_event_desc_t *desc_cmd = flecs_stack_alloc_t(stack, ecs_event_desc_t); - ecs_os_memcpy_t(desc_cmd, desc, ecs_event_desc_t); + ecs_time_t start = *stop, now = start; + ecs_ftime_t delta_time = (ecs_ftime_t)ecs_time_measure(stop); - if (desc->ids && desc->ids->count != 0) { - ecs_type_t *type_cmd = flecs_stack_alloc_t(stack, ecs_type_t); - int32_t id_count = desc->ids->count; - type_cmd->count = id_count; - type_cmd->array = flecs_stack_alloc_n(stack, ecs_id_t, id_count); - ecs_os_memcpy_n(type_cmd->array, desc->ids->array, ecs_id_t, id_count); - desc_cmd->ids = type_cmd; - } else { - desc_cmd->ids = NULL; + if (ECS_EQZERO(world->info.target_fps)) { + return delta_time; } - cmd->is._1.value = desc_cmd; - cmd->is._1.size = ECS_SIZEOF(ecs_event_desc_t); + ecs_os_perf_trace_push("flecs.insert_sleep"); - if (desc->param || desc->const_param) { - ecs_assert(!(desc->const_param && desc->param), ECS_INVALID_PARAMETER, - "cannot set param and const_param at the same time"); + ecs_ftime_t target_delta_time = + ((ecs_ftime_t)1.0 / (ecs_ftime_t)world->info.target_fps); - const ecs_type_info_t *ti = ecs_get_type_info(world, desc->event); - ecs_assert(ti != NULL, ECS_INVALID_PARAMETER, - "can only enqueue events with data for events that are components"); + /* Calculate the time we need to sleep by taking the measured delta from the + * previous frame, and subtracting it from target_delta_time. */ + ecs_ftime_t sleep = target_delta_time - delta_time; - void *param_cmd = flecs_stack_alloc(stack, ti->size, ti->alignment); - ecs_assert(param_cmd != NULL, ECS_INTERNAL_ERROR, NULL); - if (desc->param) { - if (ti->hooks.move_ctor) { - ti->hooks.move_ctor(param_cmd, desc->param, 1, ti); - } else { - ecs_os_memcpy(param_cmd, desc->param, ti->size); - } - } else { - if (ti->hooks.copy_ctor) { - ti->hooks.copy_ctor(param_cmd, desc->const_param, 1, ti); - } else { - ecs_os_memcpy(param_cmd, desc->const_param, ti->size); - } - } + /* Pick a sleep interval that is 4 times smaller than the time one frame + * should take. */ + ecs_ftime_t sleep_time = sleep / (ecs_ftime_t)4.0; - desc_cmd->param = param_cmd; - desc_cmd->const_param = NULL; - } -} + do { + /* Only call sleep when sleep_time is not 0. On some platforms, even + * a sleep with a timeout of 0 can cause stutter. */ + if (ECS_NEQZERO(sleep_time)) { + ecs_sleepf((double)sleep_time); + } -void flecs_stage_merge_post_frame( - ecs_world_t *world, - ecs_stage_t *stage) -{ - /* Execute post frame actions */ - int32_t i, count = ecs_vec_count(&stage->post_frame_actions); - ecs_action_elem_t *elems = ecs_vec_first(&stage->post_frame_actions); - for (i = 0; i < count; i ++) { - elems[i].action(world, elems[i].ctx); - } + now = start; + delta_time = (ecs_ftime_t)ecs_time_measure(&now); + } while ((target_delta_time - delta_time) > + (sleep_time / (ecs_ftime_t)2.0)); - ecs_vec_clear(&stage->post_frame_actions); -} + ecs_os_perf_trace_pop("flecs.insert_sleep"); -static -void flecs_commands_init( - ecs_stage_t *stage, - ecs_commands_t *cmd) -{ - flecs_stack_init(&cmd->stack); - ecs_vec_init_t(&stage->allocator, &cmd->queue, ecs_cmd_t, 0); - flecs_sparse_init_t(&cmd->entries, &stage->allocator, - &stage->allocators.cmd_entry_chunk, ecs_cmd_entry_t); + *stop = now; + return delta_time; } static -void flecs_commands_fini( - ecs_stage_t *stage, - ecs_commands_t *cmd) -{ - /* Make sure stage has no unmerged data */ - ecs_assert(ecs_vec_count(&stage->cmd->queue) == 0, ECS_INTERNAL_ERROR, NULL); - - flecs_stack_fini(&cmd->stack); - ecs_vec_fini_t(&stage->allocator, &cmd->queue, ecs_cmd_t); - flecs_sparse_fini(&cmd->entries); -} - -void flecs_commands_push( - ecs_stage_t *stage) +ecs_ftime_t flecs_start_measure_frame( + ecs_world_t *world, + ecs_ftime_t user_delta_time) { - int32_t sp = ++ stage->cmd_sp; - ecs_assert(sp < ECS_MAX_DEFER_STACK, ECS_INTERNAL_ERROR, NULL); - stage->cmd = &stage->cmd_stack[sp]; -} + flecs_poly_assert(world, ecs_world_t); -void flecs_commands_pop( - ecs_stage_t *stage) -{ - int32_t sp = -- stage->cmd_sp; - ecs_assert(sp >= 0, ECS_INTERNAL_ERROR, NULL); - stage->cmd = &stage->cmd_stack[sp]; -} + ecs_ftime_t delta_time = 0; -void flecs_stage_init( - ecs_world_t *world, - ecs_stage_t *stage) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_poly_init(stage, ecs_stage_t); + if ((world->flags & EcsWorldMeasureFrameTime) || + (ECS_EQZERO(user_delta_time))) + { + ecs_time_t t = world->frame_start_time; + do { + if (world->frame_start_time.nanosec || world->frame_start_time.sec){ + delta_time = flecs_insert_sleep(world, &t); + } else { + ecs_time_measure(&t); + if (ECS_NEQZERO(world->info.target_fps)) { + delta_time = (ecs_ftime_t)1.0 / world->info.target_fps; + } else { + /* Best guess */ + delta_time = (ecs_ftime_t)1.0 / (ecs_ftime_t)60.0; - stage->world = world; - stage->thread_ctx = world; - stage->auto_merge = true; - stage->async = false; + if (ECS_EQZERO(delta_time)) { + delta_time = user_delta_time; + break; + } + } + } - flecs_stack_init(&stage->allocators.iter_stack); - flecs_stack_init(&stage->allocators.deser_stack); - flecs_allocator_init(&stage->allocator); - flecs_ballocator_init_n(&stage->allocators.cmd_entry_chunk, ecs_cmd_entry_t, - FLECS_SPARSE_PAGE_SIZE); + /* Keep trying while delta_time is zero */ + } while (ECS_EQZERO(delta_time)); - ecs_allocator_t *a = &stage->allocator; - ecs_vec_init_t(a, &stage->post_frame_actions, ecs_action_elem_t, 0); + world->frame_start_time = t; - int32_t i; - for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) { - flecs_commands_init(stage, &stage->cmd_stack[i]); + /* Keep track of total time passed in world */ + world->info.world_time_total_raw += (double)delta_time; } - stage->cmd = &stage->cmd_stack[0]; + return (ecs_ftime_t)delta_time; } -void flecs_stage_fini( - ecs_world_t *world, - ecs_stage_t *stage) +static +void flecs_stop_measure_frame( + ecs_world_t* world) { - (void)world; - ecs_poly_assert(world, ecs_world_t); - ecs_poly_assert(stage, ecs_stage_t); - - ecs_poly_fini(stage, ecs_stage_t); - - ecs_allocator_t *a = &stage->allocator; - - ecs_vec_fini_t(a, &stage->post_frame_actions, ecs_action_elem_t); - ecs_vec_fini(NULL, &stage->variables, 0); - ecs_vec_fini(NULL, &stage->operations, 0); + flecs_poly_assert(world, ecs_world_t); - int32_t i; - for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) { - flecs_commands_fini(stage, &stage->cmd_stack[i]); + if (world->flags & EcsWorldMeasureFrameTime) { + ecs_time_t t = world->frame_start_time; + world->info.frame_time_total += (ecs_ftime_t)ecs_time_measure(&t); } - - flecs_stack_fini(&stage->allocators.iter_stack); - flecs_stack_fini(&stage->allocators.deser_stack); - flecs_ballocator_fini(&stage->allocators.cmd_entry_chunk); - flecs_allocator_fini(&stage->allocator); } -void ecs_set_stage_count( +ecs_ftime_t ecs_frame_begin( ecs_world_t *world, - int32_t stage_count) + ecs_ftime_t user_delta_time) { - ecs_poly_assert(world, ecs_world_t); - - /* World must have at least one default stage */ - ecs_assert(stage_count >= 1 || (world->flags & EcsWorldFini), - ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(world, ecs_world_t); + ecs_check(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot begin frame while world is in readonly mode"); + ecs_check(!(world->flags & EcsWorldFrameInProgress), ECS_INVALID_OPERATION, + "cannot begin frame while frame is already in progress"); + ecs_check(ECS_NEQZERO(user_delta_time) || ecs_os_has_time(), + ECS_MISSING_OS_API, "get_time"); - bool auto_merge = true; - const ecs_entity_t *lookup_path = NULL; - ecs_entity_t scope = 0; - ecs_entity_t with = 0; - if (world->stage_count >= 1) { - auto_merge = world->stages[0].auto_merge; - lookup_path = world->stages[0].lookup_path; - scope = world->stages[0].scope; - with = world->stages[0].with; + /* Start measuring total frame time */ + ecs_ftime_t delta_time = flecs_start_measure_frame(world, user_delta_time); + if (ECS_EQZERO(user_delta_time)) { + user_delta_time = delta_time; } - int32_t i, count = world->stage_count; - if (count && count != stage_count) { - ecs_stage_t *stages = world->stages; - - for (i = 0; i < count; i ++) { - /* If stage contains a thread handle, ecs_set_threads was used to - * create the stages. ecs_set_threads and ecs_set_stage_count should not - * be mixed. */ - ecs_poly_assert(&stages[i], ecs_stage_t); - ecs_check(stages[i].thread == 0, ECS_INVALID_OPERATION, NULL); - flecs_stage_fini(world, &stages[i]); - } + world->info.delta_time_raw = user_delta_time; + world->info.delta_time = user_delta_time * world->info.time_scale; - ecs_os_free(world->stages); - } + /* Keep track of total scaled time passed in world */ + world->info.world_time_total += (double)world->info.delta_time; - if (stage_count) { - world->stages = ecs_os_malloc_n(ecs_stage_t, stage_count); + /* Command buffer capturing */ + world->on_commands_active = world->on_commands; + world->on_commands = NULL; - for (i = 0; i < stage_count; i ++) { - ecs_stage_t *stage = &world->stages[i]; - flecs_stage_init(world, stage); - stage->id = i; + world->on_commands_ctx_active = world->on_commands_ctx; + world->on_commands_ctx = NULL; - /* Set thread_ctx to stage, as this stage might be used in a - * multithreaded context */ - stage->thread_ctx = (ecs_world_t*)stage; - stage->thread = 0; - } - } else { - /* Set to NULL to prevent double frees */ - world->stages = NULL; - } + ecs_run_aperiodic(world, 0); - /* Regardless of whether the stage was just initialized or not, when the - * ecs_set_stage_count function is called, all stages inherit the auto_merge - * property from the world */ - for (i = 0; i < stage_count; i ++) { - world->stages[i].auto_merge = auto_merge; - world->stages[i].lookup_path = lookup_path; - world->stages[0].scope = scope; - world->stages[0].with = with; - } + world->flags |= EcsWorldFrameInProgress; - world->stage_count = stage_count; + return world->info.delta_time; error: - return; + return (ecs_ftime_t)0; } -int32_t ecs_get_stage_count( - const ecs_world_t *world) +void ecs_frame_end( + ecs_world_t *world) { - world = ecs_get_world(world); - return world->stage_count; -} + flecs_poly_assert(world, ecs_world_t); + ecs_check(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot end frame while world is in readonly mode"); + ecs_check((world->flags & EcsWorldFrameInProgress), ECS_INVALID_OPERATION, + "cannot end frame while frame is not in progress"); -int32_t ecs_get_stage_id( - const ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + world->info.frame_count_total ++; + + int32_t i, count = world->stage_count; + for (i = 0; i < count; i ++) { + flecs_stage_merge_post_frame(world, world->stages[i]); + } - if (ecs_poly_is(world, ecs_stage_t)) { - ecs_stage_t *stage = ECS_CONST_CAST(ecs_stage_t*, world); + flecs_stop_measure_frame(world); - /* Index 0 is reserved for main stage */ - return stage->id; - } else if (ecs_poly_is(world, ecs_world_t)) { - return 0; - } else { - ecs_throw(ECS_INTERNAL_ERROR, NULL); - } + /* Reset command handler each frame */ + world->on_commands_active = NULL; + world->on_commands_ctx_active = NULL; + + world->flags &= ~EcsWorldFrameInProgress; + error: - return 0; + return; } -ecs_world_t* ecs_get_stage( - const ecs_world_t *world, - int32_t stage_id) +void flecs_delete_table( + ecs_world_t *world, + ecs_table_t *table) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(world->stage_count > stage_id, ECS_INVALID_PARAMETER, NULL); - return (ecs_world_t*)&world->stages[stage_id]; -error: - return NULL; + flecs_poly_assert(world, ecs_world_t); + flecs_table_fini(world, table); } -bool ecs_readonly_begin( +static +void flecs_process_empty_queries( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); - - flecs_process_pending_tables(world); - - ecs_dbg_3("#[bold]readonly"); - ecs_log_push_3(); + flecs_poly_assert(world, ecs_world_t); - int32_t i, count = ecs_get_stage_count(world); - for (i = 0; i < count; i ++) { - ecs_stage_t *stage = &world->stages[i]; - stage->lookup_path = world->stages[0].lookup_path; - ecs_assert(stage->defer == 0, ECS_INVALID_OPERATION, - "deferred mode cannot be enabled when entering readonly mode"); - flecs_defer_begin(world, stage); + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_pair(ecs_id(EcsPoly), EcsQuery)); + if (!idr) { + return; } - bool is_readonly = ECS_BIT_IS_SET(world->flags, EcsWorldReadonly); + ecs_run_aperiodic(world, EcsAperiodicEmptyTables); - /* From this point on, the world is "locked" for mutations, and it is only - * allowed to enqueue commands from stages */ - ECS_BIT_SET(world->flags, EcsWorldReadonly); + /* Make sure that we defer adding the inactive tags until after iterating + * the query */ + flecs_defer_begin(world, world->stages[0]); + + ecs_table_cache_iter_t it; + const ecs_table_record_t *tr; + if (flecs_table_cache_iter(&idr->cache, &it)) { + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + EcsPoly *queries = ecs_table_get_column(table, tr->column, 0); + int32_t i, count = ecs_table_count(table); - /* If world has more than one stage, signal we might be running on multiple - * threads. This is a stricter version of readonly mode: while some - * mutations like implicit component registration are still allowed in plain - * readonly mode, no mutations are allowed when multithreaded. */ - if (world->worker_cond) { - ECS_BIT_SET(world->flags, EcsWorldMultiThreaded); + for (i = 0; i < count; i ++) { + ecs_query_t *query = queries[i].poly; + const ecs_entity_t *entities = ecs_table_entities(table); + if (!ecs_query_is_true(query)) { + ecs_add_id(world, entities[i], EcsEmpty); + } + } + } } - return is_readonly; + flecs_defer_end(world, world->stages[0]); } -void ecs_readonly_end( - ecs_world_t *world) +/** Walk over tables that had a state change which requires bookkeeping */ +void flecs_process_pending_tables( + const ecs_world_t *world_r) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(world->flags & EcsWorldReadonly, ECS_INVALID_OPERATION, NULL); + flecs_poly_assert(world_r, ecs_world_t); - /* After this it is safe again to mutate the world directly */ - ECS_BIT_CLEAR(world->flags, EcsWorldReadonly); - ECS_BIT_CLEAR(world->flags, EcsWorldMultiThreaded); + /* We can't update the administration while in readonly mode, but we can + * ensure that when this function is called there are no pending events. */ + if (world_r->flags & EcsWorldReadonly) { + ecs_assert(flecs_sparse_count(world_r->pending_tables) == 0, + ECS_INTERNAL_ERROR, NULL); + return; + } - ecs_log_pop_3(); + /* Safe to cast, world is not readonly */ + ecs_world_t *world = ECS_CONST_CAST(ecs_world_t*, world_r); - flecs_stage_auto_merge(world); -error: - return; -} + /* If pending buffer is NULL there already is a stackframe that's iterating + * the table list. This can happen when an observer for a table event results + * in a mutation that causes another table to change state. A typical + * example of this is a system that becomes active/inactive as the result of + * a query (and as a result, its matched tables) becoming empty/non empty */ + if (!world->pending_buffer) { + return; + } -void ecs_merge( - ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_poly_is(world, ecs_world_t) || - ecs_poly_is(world, ecs_stage_t), ECS_INVALID_PARAMETER, NULL); - flecs_stage_manual_merge(world); -error: - return; -} + /* Swap buffer. The logic could in theory have been implemented with a + * single sparse set, but that would've complicated (and slowed down) the + * iteration. Additionally, by using a double buffer approach we can still + * keep most of the original ordering of events intact, which is desirable + * as it means that the ordering of tables in the internal data structures is + * more predictable. */ + int32_t i, count = flecs_sparse_count(world->pending_tables); + if (!count) { + return; + } -void ecs_set_automerge( - ecs_world_t *world, - bool auto_merge) -{ - /* If a world is provided, set auto_merge globally for the world. This - * doesn't actually do anything (the main stage never merges) but it serves - * as the default for when stages are created. */ - if (ecs_poly_is(world, ecs_world_t)) { - world->stages[0].auto_merge = auto_merge; + ecs_os_perf_trace_push("flecs.process_pending_tables"); - /* Propagate change to all stages */ - int i, stage_count = ecs_get_stage_count(world); - for (i = 0; i < stage_count; i ++) { - ecs_stage_t *stage = (ecs_stage_t*)ecs_get_stage(world, i); - stage->auto_merge = auto_merge; - } + flecs_journal_begin(world, EcsJournalTableEvents, 0, 0, 0); - /* If a stage is provided, override the auto_merge value for the individual - * stage. This allows an application to control per-stage which stage should - * be automatically merged and which one shouldn't */ - } else { - ecs_poly_assert(world, ecs_stage_t); - ecs_stage_t *stage = (ecs_stage_t*)world; - stage->auto_merge = auto_merge; - } -} + do { + ecs_sparse_t *pending_tables = world->pending_tables; + world->pending_tables = world->pending_buffer; + world->pending_buffer = NULL; -bool ecs_stage_is_readonly( - const ecs_world_t *stage) -{ - const ecs_world_t *world = ecs_get_world(stage); + /* Make sure that any ECS operations that occur while delivering the + * events does not cause inconsistencies, like sending an Empty + * notification for a table that just became non-empty. */ + flecs_defer_begin(world, world->stages[0]); - if (ecs_poly_is(stage, ecs_stage_t)) { - if (((const ecs_stage_t*)stage)->async) { - return false; - } - } + for (i = 0; i < count; i ++) { + ecs_table_t *table = flecs_sparse_get_dense_t( + pending_tables, ecs_table_t*, i)[0]; + if (!table->id) { + /* Table is being deleted, ignore empty events */ + continue; + } - if (world->flags & EcsWorldReadonly) { - if (ecs_poly_is(stage, ecs_world_t)) { - return true; - } - } else { - if (ecs_poly_is(stage, ecs_stage_t)) { - return true; - } - } + /* For each id in the table, add it to the empty/non empty list + * based on its current state */ + if (flecs_table_records_update_empty(table)) { + int32_t table_count = ecs_table_count(table); + if (table->flags & (EcsTableHasOnTableFill|EcsTableHasOnTableEmpty)) { + /* Only emit an event when there was a change in the + * administration. It is possible that a table ended up in the + * pending_tables list by going from empty->non-empty, but then + * became empty again. By the time we run this code, no changes + * in the administration would actually be made. */ + ecs_entity_t evt = table_count ? EcsOnTableFill : EcsOnTableEmpty; + if (ecs_should_log_3()) { + ecs_dbg_3("table %u state change (%s)", + (uint32_t)table->id, + table_count ? "non-empty" : "empty"); + } - return false; -} + ecs_log_push_3(); -ecs_world_t* ecs_async_stage_new( - ecs_world_t *world) -{ - ecs_stage_t *stage = ecs_os_calloc(sizeof(ecs_stage_t)); - flecs_stage_init(world, stage); + flecs_table_emit(world, table, evt); - stage->id = -1; - stage->auto_merge = false; - stage->async = true; + ecs_log_pop_3(); + } + world->info.empty_table_count += (table_count == 0) * 2 - 1; + } + } - flecs_defer_begin(world, stage); + flecs_sparse_clear(pending_tables); + ecs_defer_end(world); - return (ecs_world_t*)stage; -} + world->pending_buffer = pending_tables; + } while ((count = flecs_sparse_count(world->pending_tables))); -void ecs_async_stage_free( - ecs_world_t *world) -{ - ecs_poly_assert(world, ecs_stage_t); - ecs_stage_t *stage = (ecs_stage_t*)world; - ecs_check(stage->async == true, ECS_INVALID_PARAMETER, NULL); - flecs_stage_fini(stage->world, stage); - ecs_os_free(stage); -error: - return; + flecs_journal_end(); + + ecs_os_perf_trace_pop("flecs.process_pending_tables"); } -bool ecs_stage_is_async( - ecs_world_t *stage) +void flecs_table_set_empty( + ecs_world_t *world, + ecs_table_t *table) { - if (!stage) { - return false; - } - - if (!ecs_poly_is(stage, ecs_stage_t)) { - return false; - } - - return ((ecs_stage_t*)stage)->async; -} - -bool ecs_is_deferred( - const ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); - return stage->defer > 0; -error: - return false; -} - -/** - * @file value.c - * @brief Utility functions to work with non-trivial pointers of user types. - */ - - -int ecs_value_init_w_type_info( - const ecs_world_t *world, - const ecs_type_info_t *ti, - void *ptr) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); - (void)world; + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_xtor_t ctor; - if ((ctor = ti->hooks.ctor)) { - ctor(ptr, 1, ti); - } else { - ecs_os_memset(ptr, 0, ti->size); + if (ecs_table_count(table)) { + table->_->generation = 0; } - return 0; -error: - return -1; + flecs_sparse_ensure_fast_t(world->pending_tables, ecs_table_t*, + (uint32_t)table->id)[0] = table; } -int ecs_value_init( +bool ecs_id_in_use( const ecs_world_t *world, - ecs_entity_t type, - void *ptr) + ecs_id_t id) { - ecs_poly_assert(world, ecs_world_t); - const ecs_type_info_t *ti = ecs_get_type_info(world, type); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); - return ecs_value_init_w_type_info(world, ti, ptr); -error: - return -1; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return false; + } + return (flecs_table_cache_count(&idr->cache) != 0) || + (flecs_table_cache_empty_count(&idr->cache) != 0); } -void* ecs_value_new_w_type_info( +void ecs_run_aperiodic( ecs_world_t *world, - const ecs_type_info_t *ti) + ecs_flags32_t flags) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); - (void)world; + flecs_poly_assert(world, ecs_world_t); + + if (!flags || (flags & EcsAperiodicEmptyTables)) { + flecs_process_pending_tables(world); + } - void *result = flecs_alloc(&world->allocator, ti->size); - if (ecs_value_init_w_type_info(world, ti, result) != 0) { - flecs_free(&world->allocator, ti->size, result); - goto error; + if ((flags & EcsAperiodicEmptyQueries)) { + flecs_process_empty_queries(world); } - return result; -error: - return NULL; + if (!flags || (flags & EcsAperiodicComponentMonitors)) { + flecs_eval_component_monitors(world); + } } -void* ecs_value_new( +int32_t ecs_delete_empty_tables( ecs_world_t *world, - ecs_entity_t type) -{ - ecs_poly_assert(world, ecs_world_t); - const ecs_type_info_t *ti = ecs_get_type_info(world, type); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); - - return ecs_value_new_w_type_info(world, ti); -error: - return NULL; -} - -int ecs_value_fini_w_type_info( - const ecs_world_t *world, - const ecs_type_info_t *ti, - void *ptr) + ecs_id_t id, + uint16_t clear_generation, + uint16_t delete_generation, + int32_t min_id_count, + double time_budget_seconds) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); - (void)world; + flecs_poly_assert(world, ecs_world_t); - ecs_xtor_t dtor; - if ((dtor = ti->hooks.dtor)) { - dtor(ptr, 1, ti); - } + ecs_os_perf_trace_push("flecs.delete_empty_tables"); - return 0; -error: - return -1; -} + /* Make sure empty tables are in the empty table lists */ + ecs_run_aperiodic(world, EcsAperiodicEmptyTables); -int ecs_value_fini( - const ecs_world_t *world, - ecs_entity_t type, - void* ptr) -{ - ecs_poly_assert(world, ecs_world_t); - (void)world; - const ecs_type_info_t *ti = ecs_get_type_info(world, type); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); - return ecs_value_fini_w_type_info(world, ti, ptr); -error: - return -1; -} + ecs_time_t start = {0}, cur = {0}; + int32_t delete_count = 0; + bool time_budget = false; -int ecs_value_free( - ecs_world_t *world, - ecs_entity_t type, - void* ptr) -{ - ecs_poly_assert(world, ecs_world_t); - const ecs_type_info_t *ti = ecs_get_type_info(world, type); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); - if (ecs_value_fini_w_type_info(world, ti, ptr) != 0) { - goto error; + if (ECS_NEQZERO(time_budget_seconds) || (ecs_should_log_1() && ecs_os_has_time())) { + ecs_time_measure(&start); } - flecs_free(&world->allocator, ti->size, ptr); - - return 0; -error: - return -1; -} - -int ecs_value_copy_w_type_info( - const ecs_world_t *world, - const ecs_type_info_t *ti, - void* dst, - const void *src) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); - (void)world; + if (ECS_NEQZERO(time_budget_seconds)) { + time_budget = true; + } - ecs_copy_t copy; - if ((copy = ti->hooks.copy)) { - copy(dst, src, 1, ti); - } else { - ecs_os_memcpy(dst, src, ti->size); + if (!id) { + id = EcsAny; /* Iterate all empty tables */ } - return 0; -error: - return -1; -} + ecs_id_record_t *idr = flecs_id_record_get(world, id); + ecs_table_cache_iter_t it; + if (idr && flecs_table_cache_empty_iter((ecs_table_cache_t*)idr, &it)) { + ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + if (time_budget) { + cur = start; + if (ecs_time_measure(&cur) > time_budget_seconds) { + goto done; + } + } -int ecs_value_copy( - const ecs_world_t *world, - ecs_entity_t type, - void* dst, - const void *src) -{ - ecs_poly_assert(world, ecs_world_t); - const ecs_type_info_t *ti = ecs_get_type_info(world, type); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); - return ecs_value_copy_w_type_info(world, ti, dst, src); -error: - return -1; -} + ecs_table_t *table = tr->hdr.table; + ecs_assert(ecs_table_count(table) == 0, ECS_INTERNAL_ERROR, NULL); -int ecs_value_move_w_type_info( - const ecs_world_t *world, - const ecs_type_info_t *ti, - void* dst, - void *src) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); - (void)world; + if (table->type.count < min_id_count) { + continue; + } - ecs_move_t move; - if ((move = ti->hooks.move)) { - move(dst, src, 1, ti); - } else { - ecs_os_memcpy(dst, src, ti->size); + uint16_t gen = ++ table->_->generation; + if (delete_generation && (gen > delete_generation)) { + flecs_table_fini(world, table); + delete_count ++; + } else if (clear_generation && (gen > clear_generation)) { + flecs_table_shrink(world, table); + } + } } - return 0; -error: - return -1; -} +done: + ecs_os_perf_trace_pop("flecs.delete_empty_tables"); -int ecs_value_move( - const ecs_world_t *world, - ecs_entity_t type, - void* dst, - void *src) -{ - ecs_poly_assert(world, ecs_world_t); - const ecs_type_info_t *ti = ecs_get_type_info(world, type); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); - return ecs_value_move_w_type_info(world, ti, dst, src); -error: - return -1; + return delete_count; } -int ecs_value_move_ctor_w_type_info( - const ecs_world_t *world, - const ecs_type_info_t *ti, - void* dst, - void *src) +ecs_entities_t ecs_get_entities( + const ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); - (void)world; - - ecs_move_t move; - if ((move = ti->hooks.move_ctor)) { - move(dst, src, 1, ti); - } else { - ecs_os_memcpy(dst, src, ti->size); - } - - return 0; -error: - return -1; + ecs_entities_t result; + result.ids = flecs_entities_ids(world); + result.count = flecs_entities_size(world); + result.alive_count = flecs_entities_count(world); + return result; } -int ecs_value_move_ctor( - const ecs_world_t *world, - ecs_entity_t type, - void* dst, - void *src) +ecs_flags32_t ecs_world_get_flags( + const ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); - const ecs_type_info_t *ti = ecs_get_type_info(world, type); - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); - return ecs_value_move_w_type_info(world, ti, dst, src); -error: - return -1; + flecs_poly_assert(world, ecs_world_t); + return world->flags; } /** - * @file world.c - * @brief World-level API. + * @file addons/alerts.c + * @brief Alerts addon. */ -/* Id flags */ -const ecs_id_t ECS_PAIR = (1ull << 63); -const ecs_id_t ECS_OVERRIDE = (1ull << 62); -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; - -/* 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 = FLECS_HI_COMPONENT_ID + 0; -const ecs_entity_t EcsFlecs = FLECS_HI_COMPONENT_ID + 1; -const ecs_entity_t EcsFlecsCore = FLECS_HI_COMPONENT_ID + 2; -const ecs_entity_t EcsFlecsInternals = FLECS_HI_COMPONENT_ID + 3; -const ecs_entity_t EcsModule = FLECS_HI_COMPONENT_ID + 4; -const ecs_entity_t EcsPrivate = FLECS_HI_COMPONENT_ID + 5; -const ecs_entity_t EcsPrefab = FLECS_HI_COMPONENT_ID + 6; -const ecs_entity_t EcsDisabled = FLECS_HI_COMPONENT_ID + 7; +#ifdef FLECS_ALERTS -const ecs_entity_t EcsSlotOf = FLECS_HI_COMPONENT_ID + 8; -const ecs_entity_t EcsFlag = FLECS_HI_COMPONENT_ID + 9; - -/* Relationship properties */ -const ecs_entity_t EcsWildcard = FLECS_HI_COMPONENT_ID + 10; -const ecs_entity_t EcsAny = FLECS_HI_COMPONENT_ID + 11; -const ecs_entity_t EcsThis = FLECS_HI_COMPONENT_ID + 12; -const ecs_entity_t EcsVariable = FLECS_HI_COMPONENT_ID + 13; -const ecs_entity_t EcsTransitive = FLECS_HI_COMPONENT_ID + 14; -const ecs_entity_t EcsReflexive = FLECS_HI_COMPONENT_ID + 15; -const ecs_entity_t EcsSymmetric = FLECS_HI_COMPONENT_ID + 16; -const ecs_entity_t EcsFinal = FLECS_HI_COMPONENT_ID + 17; -const ecs_entity_t EcsDontInherit = FLECS_HI_COMPONENT_ID + 18; -const ecs_entity_t EcsAlwaysOverride = FLECS_HI_COMPONENT_ID + 19; -const ecs_entity_t EcsTag = FLECS_HI_COMPONENT_ID + 20; -const ecs_entity_t EcsUnion = FLECS_HI_COMPONENT_ID + 21; -const ecs_entity_t EcsExclusive = FLECS_HI_COMPONENT_ID + 22; -const ecs_entity_t EcsAcyclic = FLECS_HI_COMPONENT_ID + 23; -const ecs_entity_t EcsTraversable = FLECS_HI_COMPONENT_ID + 24; -const ecs_entity_t EcsWith = FLECS_HI_COMPONENT_ID + 25; -const ecs_entity_t EcsOneOf = FLECS_HI_COMPONENT_ID + 26; +ECS_COMPONENT_DECLARE(FlecsAlerts); -/* Builtin relationships */ -const ecs_entity_t EcsChildOf = FLECS_HI_COMPONENT_ID + 27; -const ecs_entity_t EcsIsA = FLECS_HI_COMPONENT_ID + 28; -const ecs_entity_t EcsDependsOn = FLECS_HI_COMPONENT_ID + 29; +typedef struct EcsAlert { + char *message; + ecs_map_t instances; /* Active instances for metric */ + ecs_ftime_t retain_period; /* How long to retain the alert */ + ecs_vec_t severity_filters; /* Severity filters */ + + /* Member range monitoring */ + ecs_id_t id; /* (Component) id that contains to monitor member */ + ecs_entity_t member; /* Member to monitor */ + int32_t offset; /* Offset of member in component */ + int32_t size; /* Size of component */ + ecs_primitive_kind_t kind; /* Primitive type kind */ + ecs_ref_t ranges; /* Reference to ranges component */ + int32_t var_id; /* Variable from which to obtain data (0 = $this) */ +} EcsAlert; -/* Identifier tags */ -const ecs_entity_t EcsName = FLECS_HI_COMPONENT_ID + 30; -const ecs_entity_t EcsSymbol = FLECS_HI_COMPONENT_ID + 31; -const ecs_entity_t EcsAlias = FLECS_HI_COMPONENT_ID + 32; +typedef struct EcsAlertTimeout { + ecs_ftime_t inactive_time; /* Time the alert has been inactive */ + ecs_ftime_t expire_time; /* Expiration duration */ +} EcsAlertTimeout; -/* Events */ -const ecs_entity_t EcsOnAdd = FLECS_HI_COMPONENT_ID + 33; -const ecs_entity_t EcsOnRemove = FLECS_HI_COMPONENT_ID + 34; -const ecs_entity_t EcsOnSet = FLECS_HI_COMPONENT_ID + 35; -const ecs_entity_t EcsUnSet = FLECS_HI_COMPONENT_ID + 36; -const ecs_entity_t EcsOnDelete = FLECS_HI_COMPONENT_ID + 37; -const ecs_entity_t EcsOnTableCreate = FLECS_HI_COMPONENT_ID + 38; -const ecs_entity_t EcsOnTableDelete = FLECS_HI_COMPONENT_ID + 39; -const ecs_entity_t EcsOnTableEmpty = FLECS_HI_COMPONENT_ID + 40; -const ecs_entity_t EcsOnTableFill = FLECS_HI_COMPONENT_ID + 41; -const ecs_entity_t EcsOnDeleteTarget = FLECS_HI_COMPONENT_ID + 46; +ECS_COMPONENT_DECLARE(EcsAlertTimeout); -/* Timers */ -const ecs_entity_t ecs_id(EcsTickSource) = FLECS_HI_COMPONENT_ID + 47; -const ecs_entity_t ecs_id(EcsTimer) = FLECS_HI_COMPONENT_ID + 48; -const ecs_entity_t ecs_id(EcsRateFilter) = FLECS_HI_COMPONENT_ID + 49; +static +ECS_CTOR(EcsAlert, ptr, { + ecs_os_zeromem(ptr); + ecs_map_init(&ptr->instances, NULL); + ecs_vec_init_t(NULL, &ptr->severity_filters, ecs_alert_severity_filter_t, 0); +}) -/* Actions */ -const ecs_entity_t EcsRemove = FLECS_HI_COMPONENT_ID + 50; -const ecs_entity_t EcsDelete = FLECS_HI_COMPONENT_ID + 51; -const ecs_entity_t EcsPanic = FLECS_HI_COMPONENT_ID + 52; +static +ECS_DTOR(EcsAlert, ptr, { + ecs_os_free(ptr->message); + ecs_map_fini(&ptr->instances); + ecs_vec_fini_t(NULL, &ptr->severity_filters, ecs_alert_severity_filter_t); +}) -/* Misc */ -const ecs_entity_t ecs_id(EcsTarget) = FLECS_HI_COMPONENT_ID + 53; -const ecs_entity_t EcsFlatten = FLECS_HI_COMPONENT_ID + 54; -const ecs_entity_t EcsDefaultChildComponent = FLECS_HI_COMPONENT_ID + 55; +static +ECS_MOVE(EcsAlert, dst, src, { + ecs_os_free(dst->message); + dst->message = src->message; + src->message = NULL; -/* Builtin predicate ids (used by rule engine) */ -const ecs_entity_t EcsPredEq = FLECS_HI_COMPONENT_ID + 56; -const ecs_entity_t EcsPredMatch = FLECS_HI_COMPONENT_ID + 57; -const ecs_entity_t EcsPredLookup = FLECS_HI_COMPONENT_ID + 58; -const ecs_entity_t EcsScopeOpen = FLECS_HI_COMPONENT_ID + 59; -const ecs_entity_t EcsScopeClose = FLECS_HI_COMPONENT_ID + 60; + ecs_map_fini(&dst->instances); + dst->instances = src->instances; + src->instances = (ecs_map_t){0}; -/* Systems */ -const ecs_entity_t EcsMonitor = FLECS_HI_COMPONENT_ID + 61; -const ecs_entity_t EcsEmpty = FLECS_HI_COMPONENT_ID + 62; -const ecs_entity_t ecs_id(EcsPipeline) = FLECS_HI_COMPONENT_ID + 63; -const ecs_entity_t EcsOnStart = FLECS_HI_COMPONENT_ID + 64; -const ecs_entity_t EcsPreFrame = FLECS_HI_COMPONENT_ID + 65; -const ecs_entity_t EcsOnLoad = FLECS_HI_COMPONENT_ID + 66; -const ecs_entity_t EcsPostLoad = FLECS_HI_COMPONENT_ID + 67; -const ecs_entity_t EcsPreUpdate = FLECS_HI_COMPONENT_ID + 68; -const ecs_entity_t EcsOnUpdate = FLECS_HI_COMPONENT_ID + 69; -const ecs_entity_t EcsOnValidate = FLECS_HI_COMPONENT_ID + 70; -const ecs_entity_t EcsPostUpdate = FLECS_HI_COMPONENT_ID + 71; -const ecs_entity_t EcsPreStore = FLECS_HI_COMPONENT_ID + 72; -const ecs_entity_t EcsOnStore = FLECS_HI_COMPONENT_ID + 73; -const ecs_entity_t EcsPostFrame = FLECS_HI_COMPONENT_ID + 74; -const ecs_entity_t EcsPhase = FLECS_HI_COMPONENT_ID + 75; + ecs_vec_fini_t(NULL, &dst->severity_filters, ecs_alert_severity_filter_t); + dst->severity_filters = src->severity_filters; + src->severity_filters = (ecs_vec_t){0}; -/* Meta primitive components (don't use low ids to save id space) */ -const ecs_entity_t ecs_id(ecs_bool_t) = FLECS_HI_COMPONENT_ID + 80; -const ecs_entity_t ecs_id(ecs_char_t) = FLECS_HI_COMPONENT_ID + 81; -const ecs_entity_t ecs_id(ecs_byte_t) = FLECS_HI_COMPONENT_ID + 82; -const ecs_entity_t ecs_id(ecs_u8_t) = FLECS_HI_COMPONENT_ID + 83; -const ecs_entity_t ecs_id(ecs_u16_t) = FLECS_HI_COMPONENT_ID + 84; -const ecs_entity_t ecs_id(ecs_u32_t) = FLECS_HI_COMPONENT_ID + 85; -const ecs_entity_t ecs_id(ecs_u64_t) = FLECS_HI_COMPONENT_ID + 86; -const ecs_entity_t ecs_id(ecs_uptr_t) = FLECS_HI_COMPONENT_ID + 87; -const ecs_entity_t ecs_id(ecs_i8_t) = FLECS_HI_COMPONENT_ID + 88; -const ecs_entity_t ecs_id(ecs_i16_t) = FLECS_HI_COMPONENT_ID + 89; -const ecs_entity_t ecs_id(ecs_i32_t) = FLECS_HI_COMPONENT_ID + 90; -const ecs_entity_t ecs_id(ecs_i64_t) = FLECS_HI_COMPONENT_ID + 91; -const ecs_entity_t ecs_id(ecs_iptr_t) = FLECS_HI_COMPONENT_ID + 92; -const ecs_entity_t ecs_id(ecs_f32_t) = FLECS_HI_COMPONENT_ID + 93; -const ecs_entity_t ecs_id(ecs_f64_t) = FLECS_HI_COMPONENT_ID + 94; -const ecs_entity_t ecs_id(ecs_string_t) = FLECS_HI_COMPONENT_ID + 95; -const ecs_entity_t ecs_id(ecs_entity_t) = FLECS_HI_COMPONENT_ID + 96; -const ecs_entity_t ecs_id(ecs_id_t) = FLECS_HI_COMPONENT_ID + 97; + dst->retain_period = src->retain_period; + dst->id = src->id; + dst->member = src->member; + dst->offset = src->offset; + dst->size = src->size; + dst->kind = src->kind; + dst->ranges = src->ranges; + dst->var_id = src->var_id; +}) -/** Meta module component ids */ -const ecs_entity_t ecs_id(EcsMetaType) = FLECS_HI_COMPONENT_ID + 98; -const ecs_entity_t ecs_id(EcsMetaTypeSerialized) = FLECS_HI_COMPONENT_ID + 99; -const ecs_entity_t ecs_id(EcsPrimitive) = FLECS_HI_COMPONENT_ID + 100; -const ecs_entity_t ecs_id(EcsEnum) = FLECS_HI_COMPONENT_ID + 101; -const ecs_entity_t ecs_id(EcsBitmask) = FLECS_HI_COMPONENT_ID + 102; -const ecs_entity_t ecs_id(EcsMember) = FLECS_HI_COMPONENT_ID + 103; -const ecs_entity_t ecs_id(EcsMemberRanges) = FLECS_HI_COMPONENT_ID + 104; -const ecs_entity_t ecs_id(EcsStruct) = FLECS_HI_COMPONENT_ID + 105; -const ecs_entity_t ecs_id(EcsArray) = FLECS_HI_COMPONENT_ID + 106; -const ecs_entity_t ecs_id(EcsVector) = FLECS_HI_COMPONENT_ID + 107; -const ecs_entity_t ecs_id(EcsOpaque) = FLECS_HI_COMPONENT_ID + 108; -const ecs_entity_t ecs_id(EcsUnit) = FLECS_HI_COMPONENT_ID + 109; -const ecs_entity_t ecs_id(EcsUnitPrefix) = FLECS_HI_COMPONENT_ID + 110; -const ecs_entity_t EcsConstant = FLECS_HI_COMPONENT_ID + 111; -const ecs_entity_t EcsQuantity = FLECS_HI_COMPONENT_ID + 112; +static +ECS_CTOR(EcsAlertsActive, ptr, { + ecs_map_init(&ptr->alerts, NULL); + ptr->info_count = 0; + ptr->warning_count = 0; + ptr->error_count = 0; +}) -/* Doc module components */ -const ecs_entity_t ecs_id(EcsDocDescription) = FLECS_HI_COMPONENT_ID + 113; -const ecs_entity_t EcsDocBrief = FLECS_HI_COMPONENT_ID + 114; -const ecs_entity_t EcsDocDetail = FLECS_HI_COMPONENT_ID + 115; -const ecs_entity_t EcsDocLink = FLECS_HI_COMPONENT_ID + 116; -const ecs_entity_t EcsDocColor = FLECS_HI_COMPONENT_ID + 117; +static +ECS_DTOR(EcsAlertsActive, ptr, { + ecs_map_fini(&ptr->alerts); +}) -/* REST module components */ -const ecs_entity_t ecs_id(EcsRest) = FLECS_HI_COMPONENT_ID + 118; +static +ECS_MOVE(EcsAlertsActive, dst, src, { + ecs_map_fini(&dst->alerts); + dst->alerts = src->alerts; + dst->info_count = src->info_count; + dst->warning_count = src->warning_count; + dst->error_count = src->error_count; + src->alerts = (ecs_map_t){0}; +}) -/* Default lookup path */ -static ecs_entity_t ecs_default_lookup_path[2] = { 0, 0 }; +static +ECS_DTOR(EcsAlertInstance, ptr, { + ecs_os_free(ptr->message); +}) -/* Declarations for addons. Located in world.c to avoid issues during linking of - * static library */ -#ifdef FLECS_ALERTS -ECS_COMPONENT_DECLARE(EcsAlert); -ECS_COMPONENT_DECLARE(EcsAlertInstance); -ECS_COMPONENT_DECLARE(EcsAlertsActive); -ECS_TAG_DECLARE(EcsAlertInfo); -ECS_TAG_DECLARE(EcsAlertWarning); -ECS_TAG_DECLARE(EcsAlertError); -ECS_TAG_DECLARE(EcsAlertCritical); -#endif -#ifdef FLECS_UNITS -ECS_DECLARE(EcsUnitPrefixes); - -ECS_DECLARE(EcsYocto); -ECS_DECLARE(EcsZepto); -ECS_DECLARE(EcsAtto); -ECS_DECLARE(EcsFemto); -ECS_DECLARE(EcsPico); -ECS_DECLARE(EcsNano); -ECS_DECLARE(EcsMicro); -ECS_DECLARE(EcsMilli); -ECS_DECLARE(EcsCenti); -ECS_DECLARE(EcsDeci); -ECS_DECLARE(EcsDeca); -ECS_DECLARE(EcsHecto); -ECS_DECLARE(EcsKilo); -ECS_DECLARE(EcsMega); -ECS_DECLARE(EcsGiga); -ECS_DECLARE(EcsTera); -ECS_DECLARE(EcsPeta); -ECS_DECLARE(EcsExa); -ECS_DECLARE(EcsZetta); -ECS_DECLARE(EcsYotta); - -ECS_DECLARE(EcsKibi); -ECS_DECLARE(EcsMebi); -ECS_DECLARE(EcsGibi); -ECS_DECLARE(EcsTebi); -ECS_DECLARE(EcsPebi); -ECS_DECLARE(EcsExbi); -ECS_DECLARE(EcsZebi); -ECS_DECLARE(EcsYobi); - -ECS_DECLARE(EcsDuration); - ECS_DECLARE(EcsPicoSeconds); - ECS_DECLARE(EcsNanoSeconds); - ECS_DECLARE(EcsMicroSeconds); - ECS_DECLARE(EcsMilliSeconds); - ECS_DECLARE(EcsSeconds); - ECS_DECLARE(EcsMinutes); - ECS_DECLARE(EcsHours); - ECS_DECLARE(EcsDays); - -ECS_DECLARE(EcsTime); - ECS_DECLARE(EcsDate); - -ECS_DECLARE(EcsMass); - ECS_DECLARE(EcsGrams); - ECS_DECLARE(EcsKiloGrams); - -ECS_DECLARE(EcsElectricCurrent); - ECS_DECLARE(EcsAmpere); - -ECS_DECLARE(EcsAmount); - ECS_DECLARE(EcsMole); - -ECS_DECLARE(EcsLuminousIntensity); - ECS_DECLARE(EcsCandela); - -ECS_DECLARE(EcsForce); - ECS_DECLARE(EcsNewton); - -ECS_DECLARE(EcsLength); - ECS_DECLARE(EcsMeters); - ECS_DECLARE(EcsPicoMeters); - ECS_DECLARE(EcsNanoMeters); - ECS_DECLARE(EcsMicroMeters); - ECS_DECLARE(EcsMilliMeters); - ECS_DECLARE(EcsCentiMeters); - ECS_DECLARE(EcsKiloMeters); - ECS_DECLARE(EcsMiles); - ECS_DECLARE(EcsPixels); - -ECS_DECLARE(EcsPressure); - ECS_DECLARE(EcsPascal); - ECS_DECLARE(EcsBar); - -ECS_DECLARE(EcsSpeed); - ECS_DECLARE(EcsMetersPerSecond); - ECS_DECLARE(EcsKiloMetersPerSecond); - ECS_DECLARE(EcsKiloMetersPerHour); - ECS_DECLARE(EcsMilesPerHour); - -ECS_DECLARE(EcsAcceleration); - -ECS_DECLARE(EcsTemperature); - ECS_DECLARE(EcsKelvin); - ECS_DECLARE(EcsCelsius); - ECS_DECLARE(EcsFahrenheit); - -ECS_DECLARE(EcsData); - ECS_DECLARE(EcsBits); - ECS_DECLARE(EcsKiloBits); - ECS_DECLARE(EcsMegaBits); - ECS_DECLARE(EcsGigaBits); - ECS_DECLARE(EcsBytes); - ECS_DECLARE(EcsKiloBytes); - ECS_DECLARE(EcsMegaBytes); - ECS_DECLARE(EcsGigaBytes); - ECS_DECLARE(EcsKibiBytes); - ECS_DECLARE(EcsGibiBytes); - ECS_DECLARE(EcsMebiBytes); - -ECS_DECLARE(EcsDataRate); - ECS_DECLARE(EcsBitsPerSecond); - ECS_DECLARE(EcsKiloBitsPerSecond); - ECS_DECLARE(EcsMegaBitsPerSecond); - ECS_DECLARE(EcsGigaBitsPerSecond); - ECS_DECLARE(EcsBytesPerSecond); - ECS_DECLARE(EcsKiloBytesPerSecond); - ECS_DECLARE(EcsMegaBytesPerSecond); - ECS_DECLARE(EcsGigaBytesPerSecond); - -ECS_DECLARE(EcsPercentage); - -ECS_DECLARE(EcsAngle); - ECS_DECLARE(EcsRadians); - ECS_DECLARE(EcsDegrees); - -ECS_DECLARE(EcsBel); -ECS_DECLARE(EcsDeciBel); - -ECS_DECLARE(EcsFrequency); - ECS_DECLARE(EcsHertz); - ECS_DECLARE(EcsKiloHertz); - ECS_DECLARE(EcsMegaHertz); - ECS_DECLARE(EcsGigaHertz); - -ECS_DECLARE(EcsUri); - ECS_DECLARE(EcsUriHyperlink); - ECS_DECLARE(EcsUriImage); - ECS_DECLARE(EcsUriFile); -#endif +static +ECS_MOVE(EcsAlertInstance, dst, src, { + ecs_os_free(dst->message); + dst->message = src->message; + src->message = NULL; +}) -/* -- Private functions -- */ +static +ECS_COPY(EcsAlertInstance, dst, src, { + ecs_os_free(dst->message); + dst->message = ecs_os_strdup(src->message); +}) -const ecs_stage_t* flecs_stage_from_readonly_world( - const ecs_world_t *world) +static +void flecs_alerts_add_alert_to_src( + ecs_world_t *world, + ecs_entity_t source, + ecs_entity_t alert, + ecs_entity_t alert_instance) { - ecs_assert(ecs_poly_is(world, ecs_world_t) || - ecs_poly_is(world, ecs_stage_t), - ECS_INTERNAL_ERROR, - NULL); - - if (ecs_poly_is(world, ecs_world_t)) { - return &world->stages[0]; + EcsAlertsActive *active = ecs_ensure( + world, source, EcsAlertsActive); + ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); - } else if (ecs_poly_is(world, ecs_stage_t)) { - return ECS_CONST_CAST(ecs_stage_t*, world); + ecs_entity_t severity = ecs_get_target(world, alert, ecs_id(EcsAlert), 0); + if (severity == EcsAlertInfo) { + active->info_count ++; + } else if (severity == EcsAlertWarning) { + active->warning_count ++; + } else if (severity == EcsAlertError) { + active->error_count ++; } - - return NULL; + + ecs_entity_t *ptr = ecs_map_ensure(&active->alerts, alert); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ptr[0] = alert_instance; + ecs_modified(world, source, EcsAlertsActive); } -ecs_stage_t* flecs_stage_from_world( - ecs_world_t **world_ptr) +static +void flecs_alerts_remove_alert_from_src( + ecs_world_t *world, + ecs_entity_t source, + ecs_entity_t alert) { - ecs_world_t *world = *world_ptr; - - ecs_assert(ecs_poly_is(world, ecs_world_t) || - ecs_poly_is(world, ecs_stage_t), - ECS_INTERNAL_ERROR, - NULL); + EcsAlertsActive *active = ecs_ensure( + world, source, EcsAlertsActive); + ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_map_remove(&active->alerts, alert); - if (ecs_poly_is(world, ecs_world_t)) { - return &world->stages[0]; + ecs_entity_t severity = ecs_get_target(world, alert, ecs_id(EcsAlert), 0); + if (severity == EcsAlertInfo) { + active->info_count --; + } else if (severity == EcsAlertWarning) { + active->warning_count --; + } else if (severity == EcsAlertError) { + active->error_count --; } - *world_ptr = ((ecs_stage_t*)world)->world; - return ECS_CONST_CAST(ecs_stage_t*, world); + if (!ecs_map_count(&active->alerts)) { + ecs_remove(world, source, EcsAlertsActive); + } else { + ecs_modified(world, source, EcsAlertsActive); + } } -ecs_world_t* flecs_suspend_readonly( - const ecs_world_t *stage_world, - ecs_suspend_readonly_state_t *state) +static +ecs_entity_t flecs_alert_get_severity( + ecs_world_t *world, + ecs_iter_t *it, + EcsAlert *alert) { - ecs_assert(stage_world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(state != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_world_t *world = - ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage_world)); - ecs_poly_assert(world, ecs_world_t); - - bool is_readonly = ECS_BIT_IS_SET(world->flags, EcsWorldReadonly); - ecs_world_t *temp_world = world; - ecs_stage_t *stage = flecs_stage_from_world(&temp_world); - - if (!is_readonly && !stage->defer) { - state->is_readonly = false; - state->is_deferred = false; - return world; + int32_t i, filter_count = ecs_vec_count(&alert->severity_filters); + ecs_alert_severity_filter_t *filters = + ecs_vec_first(&alert->severity_filters); + for (i = 0; i < filter_count; i ++) { + ecs_alert_severity_filter_t *filter = &filters[i]; + if (!filter->var) { + if (ecs_table_has_id(world, it->table, filters[i].with)) { + return filters[i].severity; + } + } else { + ecs_entity_t src = ecs_iter_get_var(it, filter->_var_index); + if (src && src != EcsWildcard) { + if (ecs_has_id(world, src, filters[i].with)) { + return filters[i].severity; + } + } + } } - ecs_dbg_3("suspending readonly mode"); - - /* Cannot suspend when running with multiple threads */ - ecs_assert(!(world->flags & EcsWorldReadonly) || - (ecs_get_stage_count(world) <= 1), ECS_INVALID_WHILE_READONLY, NULL); - - state->is_readonly = is_readonly; - state->is_deferred = stage->defer != 0; - - /* Silence readonly checks */ - world->flags &= ~EcsWorldReadonly; - - /* Hack around safety checks (this ought to look ugly) */ - state->defer_count = stage->defer; - state->commands = stage->cmd->queue; - state->defer_stack = stage->cmd->stack; - flecs_stack_init(&stage->cmd->stack); - state->scope = stage->scope; - state->with = stage->with; - stage->defer = 0; - ecs_vec_init_t(NULL, &stage->cmd->queue, ecs_cmd_t, 0); - - return world; + return 0; } -void flecs_resume_readonly( - ecs_world_t *world, - ecs_suspend_readonly_state_t *state) +static +ecs_entity_t flecs_alert_out_of_range_kind( + EcsAlert *alert, + const EcsMemberRanges *ranges, + const void *value_ptr) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(state != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_world_t *temp_world = world; - ecs_stage_t *stage = flecs_stage_from_world(&temp_world); - - if (state->is_readonly || state->is_deferred) { - ecs_dbg_3("resuming readonly mode"); - - ecs_run_aperiodic(world, 0); + double value = 0; - /* Restore readonly state / defer count */ - ECS_BIT_COND(world->flags, EcsWorldReadonly, state->is_readonly); - stage->defer = state->defer_count; - ecs_vec_fini_t(&stage->allocator, &stage->cmd->queue, ecs_cmd_t); - stage->cmd->queue = state->commands; - flecs_stack_fini(&stage->cmd->stack); - stage->cmd->stack = state->defer_stack; - stage->scope = state->scope; - stage->with = state->with; + switch(alert->kind) { + case EcsU8: value = *(const uint8_t*)value_ptr; break; + case EcsU16: value = *(const uint16_t*)value_ptr; break; + case EcsU32: value = *(const uint32_t*)value_ptr; break; + case EcsU64: value = (double)*(const uint64_t*)value_ptr; break; + case EcsI8: value = *(const int8_t*)value_ptr; break; + case EcsI16: value = *(const int16_t*)value_ptr; break; + case EcsI32: value = *(const int32_t*)value_ptr; break; + case EcsI64: value = (double)*(const int64_t*)value_ptr; break; + case EcsF32: value = (double)*(const float*)value_ptr; break; + case EcsF64: value = *(const double*)value_ptr; break; + case EcsBool: + case EcsChar: + case EcsByte: + case EcsUPtr: + case EcsIPtr: + case EcsString: + case EcsEntity: + case EcsId: + return 0; } -} -/* Evaluate component monitor. If a monitored entity changed it will have set a - * flag in one of the world's component monitors. Queries can register - * themselves with component monitors to determine whether they need to rematch - * with tables. */ -static -void flecs_eval_component_monitor( - ecs_world_t *world) -{ - ecs_poly_assert(world, ecs_world_t); + bool has_error = ECS_NEQ(ranges->error.min, ranges->error.max); + bool has_warning = ECS_NEQ(ranges->warning.min, ranges->warning.max); - if (!world->monitors.is_dirty) { - return; + if (has_error && (value < ranges->error.min || value > ranges->error.max)) { + return EcsAlertError; + } else if (has_warning && + (value < ranges->warning.min || value > ranges->warning.max)) + { + return EcsAlertWarning; + } else { + return 0; } +} - world->monitors.is_dirty = false; +static +void MonitorAlerts(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + EcsAlert *alert = ecs_field(it, EcsAlert, 0); + EcsPoly *poly = ecs_field(it, EcsPoly, 1); - ecs_map_iter_t it = ecs_map_iter(&world->monitors.monitors); - while (ecs_map_next(&it)) { - ecs_monitor_t *m = ecs_map_ptr(&it); - if (!m->is_dirty) { + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t a = it->entities[i]; /* Alert entity */ + ecs_entity_t default_severity = ecs_get_target( + world, a, ecs_id(EcsAlert), 0); + ecs_query_t *q = poly[i].poly; + if (!q) { continue; } - m->is_dirty = false; + flecs_poly_assert(q, ecs_query_t); - int32_t i, count = ecs_vec_count(&m->queries); - ecs_query_t **elems = ecs_vec_first(&m->queries); - for (i = 0; i < count; i ++) { - ecs_query_t *q = elems[i]; - flecs_query_notify(world, q, &(ecs_query_event_t) { - .kind = EcsQueryTableRematch - }); + ecs_id_t member_id = alert[i].id; + const EcsMemberRanges *ranges = NULL; + if (member_id) { + ranges = ecs_ref_get(world, &alert[i].ranges, EcsMemberRanges); } - } -} -void flecs_monitor_mark_dirty( - ecs_world_t *world, - ecs_entity_t id) -{ - ecs_map_t *monitors = &world->monitors.monitors; + ecs_iter_t rit = ecs_query_iter(world, q); + rit.flags |= EcsIterNoData; - /* Only flag if there are actually monitors registered, so that we - * don't waste cycles evaluating monitors if there's no interest */ - if (ecs_map_is_init(monitors)) { - ecs_monitor_t *m = ecs_map_get_deref(monitors, ecs_monitor_t, id); - if (m) { - if (!world->monitors.is_dirty) { - world->monitor_generation ++; + while (ecs_query_next(&rit)) { + ecs_entity_t severity = flecs_alert_get_severity( + world, &rit, &alert[i]); + if (!severity) { + severity = default_severity; } - m->is_dirty = true; - world->monitors.is_dirty = true; - } - } -} -void flecs_monitor_register( - ecs_world_t *world, - ecs_entity_t id, - ecs_query_t *query) -{ - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_map_t *monitors = &world->monitors.monitors; - ecs_map_init_if(monitors, &world->allocator); - ecs_monitor_t *m = ecs_map_ensure_alloc_t(monitors, ecs_monitor_t, id); - ecs_vec_init_if_t(&m->queries, ecs_query_t*); - ecs_query_t **q = ecs_vec_append_t( - &world->allocator, &m->queries, ecs_query_t*); - *q = query; -} - -void flecs_monitor_unregister( - ecs_world_t *world, - ecs_entity_t id, - ecs_query_t *query) -{ - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query != NULL, ECS_INTERNAL_ERROR, NULL); + const void *member_data = NULL; + ecs_entity_t member_src = 0; + if (ranges) { + if (alert[i].var_id) { + member_src = ecs_iter_get_var(&rit, alert[i].var_id); + if (!member_src || member_src == EcsWildcard) { + continue; + } + } + if (!member_src) { + member_data = ecs_table_get_id( + world, rit.table, member_id, rit.offset); + } else { + member_data = ecs_get_id(world, member_src, member_id); + } + if (!member_data) { + continue; + } + member_data = ECS_OFFSET(member_data, alert[i].offset); + } - ecs_map_t *monitors = &world->monitors.monitors; - if (!ecs_map_is_init(monitors)) { - return; - } + int32_t j, alert_src_count = rit.count; + for (j = 0; j < alert_src_count; j ++) { + ecs_entity_t src_severity = severity; + ecs_entity_t e = rit.entities[j]; + if (member_data) { + ecs_entity_t range_severity = flecs_alert_out_of_range_kind( + &alert[i], ranges, member_data); + if (!member_src) { + member_data = ECS_OFFSET(member_data, alert[i].size); + } + if (!range_severity) { + continue; + } + if (range_severity < src_severity) { + /* Range severity should not exceed alert severity */ + src_severity = range_severity; + } + } - ecs_monitor_t *m = ecs_map_get_deref(monitors, ecs_monitor_t, id); - if (!m) { - return; - } + ecs_entity_t *aptr = ecs_map_ensure(&alert[i].instances, e); + ecs_assert(aptr != NULL, ECS_INTERNAL_ERROR, NULL); + if (!aptr[0]) { + /* Alert does not yet exist for entity */ + ecs_entity_t ai = ecs_new_w_pair(world, EcsChildOf, a); + ecs_set(world, ai, EcsAlertInstance, { .message = NULL }); + ecs_set(world, ai, EcsMetricSource, { .entity = e }); + ecs_set(world, ai, EcsMetricValue, { .value = 0 }); + ecs_add_pair(world, ai, ecs_id(EcsAlert), src_severity); + if (ECS_NEQZERO(alert[i].retain_period)) { + ecs_set(world, ai, EcsAlertTimeout, { + .inactive_time = 0, + .expire_time = alert[i].retain_period + }); + } - int32_t i, count = ecs_vec_count(&m->queries); - ecs_query_t **queries = ecs_vec_first(&m->queries); - for (i = 0; i < count; i ++) { - if (queries[i] == query) { - ecs_vec_remove_t(&m->queries, ecs_query_t*, i); - count --; - break; + ecs_defer_suspend(it->world); + flecs_alerts_add_alert_to_src(world, e, a, ai); + ecs_defer_resume(it->world); + aptr[0] = ai; + } else { + /* Make sure alert severity is up to date */ + if (ecs_vec_count(&alert[i].severity_filters) || member_data) { + ecs_entity_t cur_severity = ecs_get_target( + world, aptr[0], ecs_id(EcsAlert), 0); + if (cur_severity != src_severity) { + ecs_add_pair(world, aptr[0], ecs_id(EcsAlert), + src_severity); + } + } + } + } } } - - if (!count) { - ecs_vec_fini_t(&world->allocator, &m->queries, ecs_query_t*); - ecs_map_remove_free(monitors, id); - } - - if (!ecs_map_count(monitors)) { - ecs_map_fini(monitors); - } } static -void flecs_init_store( - ecs_world_t *world) -{ - ecs_os_memset(&world->store, 0, ECS_SIZEOF(ecs_store_t)); - - ecs_allocator_t *a = &world->allocator; - ecs_vec_init_t(a, &world->store.records, ecs_table_record_t, 0); - ecs_vec_init_t(a, &world->store.marked_ids, ecs_marked_id_t, 0); - ecs_vec_init_t(a, &world->store.depth_ids, ecs_entity_t, 0); - ecs_map_init(&world->store.entity_to_depth, &world->allocator); - - /* Initialize entity index */ - flecs_entities_init(world); - - /* Initialize root table */ - flecs_sparse_init_t(&world->store.tables, - a, &world->allocators.sparse_chunk, ecs_table_t); - - /* Initialize table map */ - flecs_table_hashmap_init(world, &world->store.table_map); +void MonitorAlertInstances(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + EcsAlertInstance *alert_instance = ecs_field(it, EcsAlertInstance, 0); + EcsMetricSource *source = ecs_field(it, EcsMetricSource, 1); + EcsMetricValue *value = ecs_field(it, EcsMetricValue, 2); + EcsAlertTimeout *timeout = ecs_field(it, EcsAlertTimeout, 3); - /* Initialize one root table per stage */ - flecs_init_root_table(world); -} + /* Get alert component from alert instance parent (the alert) */ + ecs_id_t childof_pair; + if (ecs_search(world, it->table, ecs_childof(EcsWildcard), &childof_pair) == -1) { + ecs_err("alert instances must be a child of an alert"); + return; + } -static -void flecs_clean_tables( - ecs_world_t *world) -{ - int32_t i, count = flecs_sparse_count(&world->store.tables); + ecs_entity_t parent = ecs_pair_second(world, childof_pair); + ecs_assert(parent != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_has(world, parent, EcsAlert), ECS_INVALID_OPERATION, + "alert entity does not have Alert component"); - /* Ensure that first table in sparse set has id 0. This is a dummy table - * that only exists so that there is no table with id 0 */ - ecs_table_t *first = flecs_sparse_get_dense_t(&world->store.tables, - ecs_table_t, 0); - (void)first; + EcsAlert *alert = ecs_ensure(world, parent, EcsAlert); + const EcsPoly *poly = ecs_get_pair(world, parent, EcsPoly, EcsQuery); + ecs_assert(poly != NULL, ECS_INVALID_OPERATION, + "alert entity does not have (Poly, Query) component"); - for (i = 1; i < count; i ++) { - ecs_table_t *t = flecs_sparse_get_dense_t(&world->store.tables, - ecs_table_t, i); - flecs_table_free(world, t); + ecs_query_t *query = poly->poly; + if (!query) { + return; } - /* Free table types separately so that if application destructors rely on - * a type it's still valid. */ - for (i = 1; i < count; i ++) { - ecs_table_t *t = flecs_sparse_get_dense_t(&world->store.tables, - ecs_table_t, i); - flecs_table_free_type(world, t); - } + flecs_poly_assert(query, ecs_query_t); - /* Clear the root table */ - if (count) { - flecs_table_reset(world, &world->store.root); + ecs_id_t member_id = alert->id; + const EcsMemberRanges *ranges = NULL; + if (member_id) { + ranges = ecs_ref_get(world, &alert->ranges, EcsMemberRanges); } -} - -static -void flecs_fini_root_tables( - ecs_world_t *world, - ecs_id_record_t *idr, - bool fini_targets) -{ - ecs_table_cache_iter_t it; - bool has_roots = flecs_table_cache_iter(&idr->cache, &it); - ecs_assert(has_roots == true, ECS_INTERNAL_ERROR, NULL); - (void)has_roots; + ecs_script_vars_t *vars = ecs_script_vars_init(it->world); + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t ai = it->entities[i]; + ecs_entity_t e = source[i].entity; - const ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; - if (table->flags & EcsTableHasBuiltins) { - continue; /* Filter out modules */ + /* If source of alert is no longer alive, delete alert instance even if + * the alert has a retain period. */ + if (!ecs_is_alive(world, e)) { + ecs_delete(world, ai); + continue; } - int32_t i, count = table->data.entities.count; - ecs_entity_t *entities = table->data.entities.array; + /* Check if alert instance still matches query */ + ecs_iter_t rit = ecs_query_iter(world, query); + rit.flags |= EcsIterNoData; + ecs_iter_set_var(&rit, 0, e); - if (fini_targets) { - /* Only delete entities that are used as pair target. Iterate - * backwards to minimize moving entities around in table. */ - for (i = count - 1; i >= 0; i --) { - ecs_record_t *r = flecs_entities_get(world, entities[i]); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - if (ECS_RECORD_TO_ROW_FLAGS(r->row) & EcsEntityIsTarget) { - ecs_delete(world, entities[i]); + if (ecs_query_next(&rit)) { + bool match = true; + + /* If alert is monitoring member range, test value against range */ + if (ranges) { + ecs_entity_t member_src = e; + if (alert->var_id) { + member_src = ecs_iter_get_var(&rit, alert->var_id); } - } - } else { - /* Delete remaining entities that are not in use (added to another - * entity). This limits table moves during cleanup and delays - * cleanup of tags. */ - for (i = count - 1; i >= 0; i --) { - ecs_record_t *r = flecs_entities_get(world, entities[i]); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - if (!ECS_RECORD_TO_ROW_FLAGS(r->row)) { - ecs_delete(world, entities[i]); + + const void *member_data = ecs_get_id( + world, member_src, member_id); + if (!member_data) { + match = false; + } else { + member_data = ECS_OFFSET(member_data, alert->offset); + if (flecs_alert_out_of_range_kind( + alert, ranges, member_data) == 0) + { + match = false; + } } } - } - } -} -static -void flecs_fini_roots( - ecs_world_t *world) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair(EcsChildOf, 0)); + if (match) { + /* Only increase alert duration if the alert was active */ + value[i].value += (double)it->delta_system_time; - ecs_run_aperiodic(world, EcsAperiodicEmptyTables); + bool generate_message = alert->message; + if (generate_message) { + if (alert_instance[i].message) { + /* If a message was already generated, only regenerate if + * query has multiple variables. Variable values could have + * changed, this ensures the message remains up to date. */ + generate_message = rit.variable_count > 1; + } + } - /* Delete root entities that are not modules. This prioritizes deleting - * regular entities first, which reduces the chance of components getting - * destructed in random order because it got deleted before entities, - * thereby bypassing the OnDeleteTarget policy. */ - flecs_defer_begin(world, &world->stages[0]); - flecs_fini_root_tables(world, idr, true); - flecs_defer_end(world, &world->stages[0]); + if (generate_message) { + if (alert_instance[i].message) { + ecs_os_free(alert_instance[i].message); + } - flecs_defer_begin(world, &world->stages[0]); - flecs_fini_root_tables(world, idr, false); - flecs_defer_end(world, &world->stages[0]); -} + ecs_script_vars_from_iter(&rit, vars, 0); + alert_instance[i].message = ecs_script_string_interpolate( + world, alert->message, vars); + } -static -void flecs_fini_store(ecs_world_t *world) { - flecs_clean_tables(world); - flecs_sparse_fini(&world->store.tables); - flecs_table_free(world, &world->store.root); - flecs_entities_clear(world); - flecs_hashmap_fini(&world->store.table_map); + if (timeout) { + if (ECS_NEQZERO(timeout[i].inactive_time)) { + /* The alert just became active. Remove Disabled tag */ + flecs_alerts_add_alert_to_src(world, e, parent, ai); + ecs_remove_id(world, ai, EcsDisabled); + } + timeout[i].inactive_time = 0; + } - ecs_allocator_t *a = &world->allocator; - ecs_vec_fini_t(a, &world->store.records, ecs_table_record_t); - ecs_vec_fini_t(a, &world->store.marked_ids, ecs_marked_id_t); - ecs_vec_fini_t(a, &world->store.depth_ids, ecs_entity_t); - ecs_map_fini(&world->store.entity_to_depth); -} + /* Alert instance still matches query, keep it alive */ + ecs_iter_fini(&rit); + continue; + } -/* Implementation for iterable mixin */ -static -bool flecs_world_iter_next( - ecs_iter_t *it) -{ - if (ECS_BIT_IS_SET(it->flags, EcsIterIsValid)) { - ECS_BIT_CLEAR(it->flags, EcsIterIsValid); - ecs_iter_fini(it); - return false; - } + ecs_iter_fini(&rit); + } - ecs_world_t *world = it->real_world; - it->entities = ECS_CONST_CAST(ecs_entity_t*, flecs_entities_ids(world)); - it->count = flecs_entities_count(world); - flecs_iter_validate(it); + /* Alert instance is no longer active */ + if (timeout) { + if (ECS_EQZERO(timeout[i].inactive_time)) { + /* The alert just became inactive. Add Disabled tag */ + flecs_alerts_remove_alert_from_src(world, e, parent); + ecs_add_id(world, ai, EcsDisabled); + } + ecs_ftime_t t = timeout[i].inactive_time; + timeout[i].inactive_time += it->delta_system_time; + if (t < timeout[i].expire_time) { + /* Alert instance no longer matches query, but is still + * within the timeout period. Keep it alive. */ + continue; + } + } - return true; + /* Alert instance no longer matches query, remove it */ + flecs_alerts_remove_alert_from_src(world, e, parent); + ecs_map_remove(&alert->instances, e); + ecs_delete(world, ai); + } + + ecs_script_vars_fini(vars); } -static -void flecs_world_iter_init( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter) +ecs_entity_t ecs_alert_init( + ecs_world_t *world, + const ecs_alert_desc_t *desc) { - ecs_poly_assert(poly, ecs_world_t); - (void)poly; + flecs_poly_assert(world, ecs_world_t); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_alert_desc_t was not initialized to zero"); + ecs_check(!desc->query.entity || desc->entity == desc->query.entity, + ECS_INVALID_PARAMETER, NULL); - if (filter) { - iter[0] = ecs_term_iter(world, filter); - } else { - iter[0] = (ecs_iter_t){ - .world = ECS_CONST_CAST(ecs_world_t*, world), - .real_world = ECS_CONST_CAST(ecs_world_t*, ecs_get_world(world)), - .next = flecs_world_iter_next - }; + ecs_entity_t result = desc->entity; + if (!result) { + result = ecs_new(world); } -} -static -void flecs_world_allocators_init( - ecs_world_t *world) -{ - ecs_world_allocators_t *a = &world->allocators; + ecs_query_desc_t private_desc = desc->query; + private_desc.entity = result; - flecs_allocator_init(&world->allocator); + ecs_query_t *q = ecs_query_init(world, &private_desc); + if (!q) { + ecs_err("failed to create alert filter"); + return 0; + } - ecs_map_params_init(&a->ptr, &world->allocator); - ecs_map_params_init(&a->query_table_list, &world->allocator); + if (!(q->flags & EcsQueryMatchThis)) { + ecs_err("alert filter must have at least one '$this' term"); + ecs_query_fini(q); + return 0; + } - flecs_ballocator_init_t(&a->query_table, ecs_query_table_t); - flecs_ballocator_init_t(&a->query_table_match, ecs_query_table_match_t); - flecs_ballocator_init_n(&a->graph_edge_lo, ecs_graph_edge_t, FLECS_HI_COMPONENT_ID); - flecs_ballocator_init_t(&a->graph_edge, ecs_graph_edge_t); - flecs_ballocator_init_t(&a->id_record, ecs_id_record_t); - flecs_ballocator_init_n(&a->id_record_chunk, ecs_id_record_t, FLECS_SPARSE_PAGE_SIZE); - flecs_ballocator_init_t(&a->table_diff, ecs_table_diff_t); - flecs_ballocator_init_n(&a->sparse_chunk, int32_t, FLECS_SPARSE_PAGE_SIZE); - flecs_ballocator_init_t(&a->hashmap, ecs_hashmap_t); - flecs_table_diff_builder_init(world, &world->allocators.diff_builder); -} + /* Initialize Alert component which identifiers entity as alert */ + EcsAlert *alert = ecs_ensure(world, result, EcsAlert); + ecs_assert(alert != NULL, ECS_INTERNAL_ERROR, NULL); + alert->message = ecs_os_strdup(desc->message); + alert->retain_period = desc->retain_period; -static -void flecs_world_allocators_fini( - ecs_world_t *world) -{ - ecs_world_allocators_t *a = &world->allocators; - - ecs_map_params_fini(&a->ptr); - ecs_map_params_fini(&a->query_table_list); - - flecs_ballocator_fini(&a->query_table); - flecs_ballocator_fini(&a->query_table_match); - flecs_ballocator_fini(&a->graph_edge_lo); - flecs_ballocator_fini(&a->graph_edge); - flecs_ballocator_fini(&a->id_record); - flecs_ballocator_fini(&a->id_record_chunk); - flecs_ballocator_fini(&a->table_diff); - flecs_ballocator_fini(&a->sparse_chunk); - flecs_ballocator_fini(&a->hashmap); - flecs_table_diff_builder_fini(world, &world->allocators.diff_builder); + /* Initialize severity filters */ + int32_t i; + for (i = 0; i < 4; i ++) { + if (desc->severity_filters[i].with) { + if (!desc->severity_filters[i].severity) { + ecs_err("severity filter must have severity"); + goto error; + } + ecs_alert_severity_filter_t *sf = ecs_vec_append_t(NULL, + &alert->severity_filters, ecs_alert_severity_filter_t); + *sf = desc->severity_filters[i]; + if (sf->var) { + sf->_var_index = ecs_query_find_var(q, sf->var); + if (sf->_var_index == -1) { + ecs_err("unresolved variable '%s' in alert severity filter", + sf->var); + goto error; + } + } + } + } - flecs_allocator_fini(&world->allocator); -} + /* Fetch data for member monitoring */ + if (desc->member) { + alert->member = desc->member; + if (!desc->id) { + alert->id = ecs_get_parent(world, desc->member); + if (!alert->id) { + ecs_err("ecs_alert_desc_t::member is not a member"); + goto error; + } + ecs_check(alert->id != 0, ECS_INVALID_PARAMETER, NULL); + } else { + alert->id = desc->id; + } -static -void flecs_log_addons(void) { - ecs_trace("addons included in build:"); - ecs_log_push(); - #ifdef FLECS_CPP - ecs_trace("FLECS_CPP"); - #endif - #ifdef FLECS_MODULE - ecs_trace("FLECS_MODULE"); - #endif - #ifdef FLECS_PARSER - ecs_trace("FLECS_PARSER"); - #endif - #ifdef FLECS_PLECS - ecs_trace("FLECS_PLECS"); - #endif - #ifdef FLECS_RULES - ecs_trace("FLECS_RULES"); - #endif - #ifdef FLECS_SNAPSHOT - ecs_trace("FLECS_SNAPSHOT"); - #endif - #ifdef FLECS_STATS - ecs_trace("FLECS_STATS"); - #endif - #ifdef FLECS_MONITOR - ecs_trace("FLECS_MONITOR"); - #endif - #ifdef FLECS_METRICS - ecs_trace("FLECS_METRICS"); - #endif - #ifdef FLECS_SYSTEM - ecs_trace("FLECS_SYSTEM"); - #endif - #ifdef FLECS_PIPELINE - ecs_trace("FLECS_PIPELINE"); - #endif - #ifdef FLECS_TIMER - ecs_trace("FLECS_TIMER"); - #endif - #ifdef FLECS_META - ecs_trace("FLECS_META"); - #endif - #ifdef FLECS_META_C - ecs_trace("FLECS_META_C"); - #endif - #ifdef FLECS_UNITS - ecs_trace("FLECS_UNITS"); - #endif - #ifdef FLECS_EXPR - ecs_trace("FLECS_EXPR"); - #endif - #ifdef FLECS_JSON - ecs_trace("FLECS_JSON"); - #endif - #ifdef FLECS_DOC - ecs_trace("FLECS_DOC"); - #endif - #ifdef FLECS_COREDOC - ecs_trace("FLECS_COREDOC"); - #endif - #ifdef FLECS_LOG - ecs_trace("FLECS_LOG"); - #endif - #ifdef FLECS_JOURNAL - ecs_trace("FLECS_JOURNAL"); - #endif - #ifdef FLECS_APP - ecs_trace("FLECS_APP"); - #endif - #ifdef FLECS_OS_API_IMPL - ecs_trace("FLECS_OS_API_IMPL"); - #endif - #ifdef FLECS_SCRIPT - ecs_trace("FLECS_SCRIPT"); - #endif - #ifdef FLECS_HTTP - ecs_trace("FLECS_HTTP"); - #endif - #ifdef FLECS_REST - ecs_trace("FLECS_REST"); - #endif - ecs_log_pop(); -} + ecs_id_record_t *idr = flecs_id_record_ensure(world, alert->id); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + if (!idr->type_info) { + ecs_err("ecs_alert_desc_t::id must be a component"); + goto error; + } -/* -- Public functions -- */ + ecs_entity_t type = idr->type_info->component; + if (type != ecs_get_parent(world, desc->member)) { + char *type_name = ecs_get_path(world, type); + ecs_err("member '%s' is not a member of '%s'", + ecs_get_name(world, desc->member), type_name); + ecs_os_free(type_name); + goto error; + } -ecs_world_t *ecs_mini(void) { -#ifdef FLECS_OS_API_IMPL - ecs_set_os_api_impl(); -#endif - ecs_os_init(); + const EcsMember *member = ecs_get(world, alert->member, EcsMember); + if (!member) { + ecs_err("ecs_alert_desc_t::member is not a member"); + goto error; + } + if (!member->type) { + ecs_err("ecs_alert_desc_t::member must have a type"); + goto error; + } + + const EcsPrimitive *pr = ecs_get(world, member->type, EcsPrimitive); + if (!pr) { + ecs_err("ecs_alert_desc_t::member must be of a primitive type"); + goto error; + } - ecs_trace("#[bold]bootstrapping world"); - ecs_log_push(); + if (!ecs_has(world, desc->member, EcsMemberRanges)) { + ecs_err("ecs_alert_desc_t::member must have warning/error ranges"); + goto error; + } - ecs_trace("tracing enabled, call ecs_log_set_level(-1) to disable"); + int32_t var_id = 0; + if (desc->var) { + var_id = ecs_query_find_var(q, desc->var); + if (var_id == -1) { + ecs_err("unresolved variable '%s' in alert member", desc->var); + goto error; + } + } - if (!ecs_os_has_heap()) { - ecs_abort(ECS_MISSING_OS_API, NULL); + alert->offset = member->offset; + alert->size = idr->type_info->size; + alert->kind = pr->kind; + alert->ranges = ecs_ref_init(world, desc->member, EcsMemberRanges); + alert->var_id = var_id; } - if (!ecs_os_has_threading()) { - ecs_trace("threading unavailable, to use threads set OS API first (see examples)"); - } + ecs_modified(world, result, EcsAlert); - if (!ecs_os_has_time()) { - ecs_trace("time management not available"); + /* Register alert as metric */ + ecs_add(world, result, EcsMetric); + ecs_add_pair(world, result, EcsMetric, EcsCounter); + + /* Add severity to alert */ + ecs_entity_t severity = desc->severity; + if (!severity) { + severity = EcsAlertError; } - flecs_log_addons(); + ecs_add_pair(world, result, ecs_id(EcsAlert), severity); -#ifdef FLECS_SANITIZE - ecs_trace("sanitize build, rebuild without FLECS_SANITIZE for (much) " - "improved performance"); -#elif defined(FLECS_DEBUG) - ecs_trace("debug build, rebuild with NDEBUG or FLECS_NDEBUG for improved " - "performance"); + if (desc->doc_name) { +#ifdef FLECS_DOC + ecs_doc_set_name(world, result, desc->doc_name); #else - ecs_trace("#[green]release#[reset] build"); + ecs_err("cannot set doc_name for alert, requires FLECS_DOC addon"); + goto error; #endif + } -#ifdef __clang__ - ecs_trace("compiled with clang %s", __clang_version__); -#elif defined(__GNUC__) - ecs_trace("compiled with gcc %d.%d", __GNUC__, __GNUC_MINOR__); -#elif defined (_MSC_VER) - ecs_trace("compiled with msvc %d", _MSC_VER); -#elif defined (__TINYC__) - ecs_trace("compiled with tcc %d", __TINYC__); + if (desc->brief) { +#ifdef FLECS_DOC + ecs_doc_set_brief(world, result, desc->brief); +#else + ecs_err("cannot set brief for alert, requires FLECS_DOC addon"); + goto error; #endif + } - ecs_world_t *world = ecs_os_calloc_t(ecs_world_t); - ecs_assert(world != NULL, ECS_OUT_OF_MEMORY, NULL); - ecs_poly_init(world, ecs_world_t); - - world->flags |= EcsWorldInit; - - flecs_world_allocators_init(world); - ecs_allocator_t *a = &world->allocator; - - world->self = world; - flecs_sparse_init_t(&world->type_info, a, - &world->allocators.sparse_chunk, ecs_type_info_t); - ecs_map_init_w_params(&world->id_index_hi, &world->allocators.ptr); - world->id_index_lo = ecs_os_calloc_n(ecs_id_record_t, FLECS_HI_ID_RECORD_ID); - flecs_observable_init(&world->observable); - world->iterable.init = flecs_world_iter_init; + return result; +error: + if (result) { + ecs_delete(world, result); + } + return 0; +} - world->pending_tables = ecs_os_calloc_t(ecs_sparse_t); - flecs_sparse_init_t(world->pending_tables, a, - &world->allocators.sparse_chunk, ecs_table_t*); - world->pending_buffer = ecs_os_calloc_t(ecs_sparse_t); - flecs_sparse_init_t(world->pending_buffer, a, - &world->allocators.sparse_chunk, ecs_table_t*); +int32_t ecs_get_alert_count( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t alert) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(!alert || ecs_has(world, alert, EcsAlert), + ECS_INVALID_PARAMETER, NULL); - flecs_name_index_init(&world->aliases, a); - flecs_name_index_init(&world->symbols, a); - ecs_vec_init_t(a, &world->fini_actions, ecs_action_elem_t, 0); + const EcsAlertsActive *active = ecs_get(world, entity, EcsAlertsActive); + if (!active) { + return 0; + } - world->info.time_scale = 1.0; - if (ecs_os_has_time()) { - ecs_os_get_time(&world->world_start_time); + if (alert) { + return ecs_map_get(&active->alerts, alert) != NULL; } - ecs_set_stage_count(world, 1); - ecs_default_lookup_path[0] = EcsFlecsCore; - ecs_set_lookup_path(world, ecs_default_lookup_path); - flecs_init_store(world); + return ecs_map_count(&active->alerts); +error: + return 0; +} - flecs_bootstrap(world); +ecs_entity_t ecs_get_alert( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t alert) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(alert != 0, ECS_INVALID_PARAMETER, NULL); - world->flags &= ~EcsWorldInit; + const EcsAlertsActive *active = ecs_get(world, entity, EcsAlertsActive); + if (!active) { + return 0; + } - ecs_trace("world ready!"); - ecs_log_pop(); + ecs_entity_t *ptr = ecs_map_get(&active->alerts, alert); + if (ptr) { + return ptr[0]; + } - return world; +error: + return 0; } -ecs_world_t *ecs_init(void) { - ecs_world_t *world = ecs_mini(); +void FlecsAlertsImport(ecs_world_t *world) { + ECS_MODULE_DEFINE(world, FlecsAlerts); -#ifdef FLECS_MODULE_H - ecs_trace("#[bold]import addons"); - ecs_log_push(); - ecs_trace("use ecs_mini to create world without importing addons"); -#ifdef FLECS_SYSTEM - ECS_IMPORT(world, FlecsSystem); -#endif -#ifdef FLECS_PIPELINE ECS_IMPORT(world, FlecsPipeline); -#endif -#ifdef FLECS_TIMER ECS_IMPORT(world, FlecsTimer); -#endif -#ifdef FLECS_META - ECS_IMPORT(world, FlecsMeta); -#endif + ECS_IMPORT(world, FlecsMetrics); #ifdef FLECS_DOC ECS_IMPORT(world, FlecsDoc); #endif -#ifdef FLECS_COREDOC - ECS_IMPORT(world, FlecsCoreDoc); -#endif -#ifdef FLECS_SCRIPT - ECS_IMPORT(world, FlecsScript); -#endif -#ifdef FLECS_REST - ECS_IMPORT(world, FlecsRest); -#endif -#ifdef FLECS_UNITS - ecs_trace("#[green]module#[reset] flecs.units is not automatically imported"); -#endif - ecs_trace("addons imported!"); - ecs_log_pop(); -#endif - return world; -} -ecs_world_t* ecs_init_w_args( - int argc, - char *argv[]) -{ - ecs_world_t *world = ecs_init(); + ecs_set_name_prefix(world, "Ecs"); + ECS_COMPONENT_DEFINE(world, EcsAlert); + ecs_remove_pair(world, ecs_id(EcsAlert), ecs_id(EcsIdentifier), EcsSymbol); + ECS_COMPONENT_DEFINE(world, EcsAlertsActive); - (void)argc; - (void)argv; + ecs_set_name_prefix(world, "EcsAlert"); + ECS_COMPONENT_DEFINE(world, EcsAlertInstance); + ECS_COMPONENT_DEFINE(world, EcsAlertTimeout); -#ifdef FLECS_DOC - if (argc) { - char *app = argv[0]; - char *last_elem = strrchr(app, '/'); - if (!last_elem) { - last_elem = strrchr(app, '\\'); - } - if (last_elem) { - app = last_elem + 1; - } - ecs_set_pair(world, EcsWorld, EcsDocDescription, EcsName, {app}); - } -#endif + ECS_TAG_DEFINE(world, EcsAlertInfo); + ECS_TAG_DEFINE(world, EcsAlertWarning); + ECS_TAG_DEFINE(world, EcsAlertError); + ECS_TAG_DEFINE(world, EcsAlertCritical); - return world; -} + ecs_add_id(world, ecs_id(EcsAlert), EcsPairIsTag); + ecs_add_id(world, ecs_id(EcsAlert), EcsExclusive); + ecs_add_id(world, ecs_id(EcsAlertsActive), EcsPrivate); -void ecs_quit( - ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_stage_from_world(&world); - world->flags |= EcsWorldQuit; -error: - return; -} + ecs_struct(world, { + .entity = ecs_id(EcsAlertInstance), + .members = { + { .name = "message", .type = ecs_id(ecs_string_t) } + } + }); -bool ecs_should_quit( - const ecs_world_t *world) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - world = ecs_get_world(world); - return ECS_BIT_IS_SET(world->flags, EcsWorldQuit); -error: - return true; -} + ecs_set_hooks(world, EcsAlert, { + .ctor = ecs_ctor(EcsAlert), + .dtor = ecs_dtor(EcsAlert), + .move = ecs_move(EcsAlert) + }); -void flecs_notify_tables( - ecs_world_t *world, - ecs_id_t id, - ecs_table_event_t *event) -{ - ecs_poly_assert(world, ecs_world_t); + ecs_set_hooks(world, EcsAlertsActive, { + .ctor = ecs_ctor(EcsAlertsActive), + .dtor = ecs_dtor(EcsAlertsActive), + .move = ecs_move(EcsAlertsActive) + }); - /* If no id is specified, broadcast to all tables */ - if (!id) { - ecs_sparse_t *tables = &world->store.tables; - int32_t i, count = flecs_sparse_count(tables); - for (i = 0; i < count; i ++) { - ecs_table_t *table = flecs_sparse_get_dense_t(tables, ecs_table_t, i); - flecs_table_notify(world, table, event); - } + ecs_set_hooks(world, EcsAlertInstance, { + .ctor = flecs_default_ctor, + .dtor = ecs_dtor(EcsAlertInstance), + .move = ecs_move(EcsAlertInstance), + .copy = ecs_copy(EcsAlertInstance) + }); - /* If id is specified, only broadcast to tables with id */ - } else { - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - return; + ecs_struct(world, { + .entity = ecs_id(EcsAlertsActive), + .members = { + { .name = "info_count", .type = ecs_id(ecs_i32_t) }, + { .name = "warning_count", .type = ecs_id(ecs_i32_t) }, + { .name = "error_count", .type = ecs_id(ecs_i32_t) } } + }); - ecs_table_cache_iter_t it; - const ecs_table_record_t *tr; + ECS_SYSTEM(world, MonitorAlerts, EcsPreStore, + Alert, + (Poly, Query)); - flecs_table_cache_all_iter(&idr->cache, &it); - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - flecs_table_notify(world, tr->hdr.table, event); - } - } -} + ECS_SYSTEM(world, MonitorAlertInstances, EcsOnStore, Instance, + flecs.metrics.Source, + flecs.metrics.Value, + ?Timeout, + ?Disabled); -void ecs_default_ctor( - void *ptr, - int32_t count, - const ecs_type_info_t *ti) -{ - ecs_os_memset(ptr, 0, ti->size * count); -} + ecs_system(world, { + .entity = ecs_id(MonitorAlerts), + .immediate = true, + .interval = (ecs_ftime_t)0.5 + }); -static -void flecs_default_copy_ctor(void *dst_ptr, const void *src_ptr, - int32_t count, const ecs_type_info_t *ti) -{ - const ecs_type_hooks_t *cl = &ti->hooks; - cl->ctor(dst_ptr, count, ti); - cl->copy(dst_ptr, src_ptr, count, ti); + ecs_system(world, { + .entity = ecs_id(MonitorAlertInstances), + .interval = (ecs_ftime_t)0.5 + }); } -static -void flecs_default_move_ctor(void *dst_ptr, void *src_ptr, - int32_t count, const ecs_type_info_t *ti) -{ - const ecs_type_hooks_t *cl = &ti->hooks; - cl->ctor(dst_ptr, count, ti); - cl->move(dst_ptr, src_ptr, count, ti); -} +#endif -static -void flecs_default_ctor_w_move_w_dtor(void *dst_ptr, void *src_ptr, - int32_t count, const ecs_type_info_t *ti) -{ - const ecs_type_hooks_t *cl = &ti->hooks; - cl->ctor(dst_ptr, count, ti); - cl->move(dst_ptr, src_ptr, count, ti); - cl->dtor(src_ptr, count, ti); -} +/** + * @file addons/app.c + * @brief App addon. + */ -static -void flecs_default_move_ctor_w_dtor(void *dst_ptr, void *src_ptr, - int32_t count, const ecs_type_info_t *ti) -{ - const ecs_type_hooks_t *cl = &ti->hooks; - cl->move_ctor(dst_ptr, src_ptr, count, ti); - cl->dtor(src_ptr, count, ti); -} -static -void flecs_default_move(void *dst_ptr, void *src_ptr, - int32_t count, const ecs_type_info_t *ti) -{ - const ecs_type_hooks_t *cl = &ti->hooks; - cl->move(dst_ptr, src_ptr, count, ti); -} +#ifdef FLECS_APP static -void flecs_default_dtor(void *dst_ptr, void *src_ptr, - int32_t count, const ecs_type_info_t *ti) +int flecs_default_run_action( + ecs_world_t *world, + ecs_app_desc_t *desc) { - /* When there is no move, destruct the destination component & memcpy the - * component to dst. The src component does not have to be destructed when - * a component has a trivial move. */ - const ecs_type_hooks_t *cl = &ti->hooks; - cl->dtor(dst_ptr, count, ti); - ecs_os_memcpy(dst_ptr, src_ptr, flecs_uto(ecs_size_t, ti->size) * count); -} + if (desc->init) { + desc->init(world); + } -static -void flecs_default_move_w_dtor(void *dst_ptr, void *src_ptr, - int32_t count, const ecs_type_info_t *ti) -{ - /* If a component has a move, the move will take care of memcpying the data - * and destroying any data in dst. Because this is not a trivial move, the - * src component must also be destructed. */ - const ecs_type_hooks_t *cl = &ti->hooks; - cl->move(dst_ptr, src_ptr, count, ti); - cl->dtor(src_ptr, count, ti); + int result = 0; + if (desc->frames) { + int32_t i; + for (i = 0; i < desc->frames; i ++) { + if ((result = ecs_app_run_frame(world, desc)) != 0) { + break; + } + } + } else { + while ((result = ecs_app_run_frame(world, desc)) == 0) { } + } + + /* Ensure quit flag is set on world, which can be used to determine if + * world needs to be cleaned up. */ +#ifndef __EMSCRIPTEN__ + ecs_quit(world); +#endif + + if (result == 1) { + return 0; /* Normal exit */ + } else { + return result; /* Error code */ + } } -void ecs_set_hooks_id( +static +int flecs_default_frame_action( ecs_world_t *world, - ecs_entity_t component, - const ecs_type_hooks_t *h) + const ecs_app_desc_t *desc) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + return !ecs_progress(world, desc->delta_time); +} - flecs_stage_from_world(&world); +static ecs_app_run_action_t run_action = flecs_default_run_action; +static ecs_app_frame_action_t frame_action = flecs_default_frame_action; +static ecs_app_desc_t ecs_app_desc; - /* Ensure that no tables have yet been created for the component */ - ecs_assert( ecs_id_in_use(world, component) == false, - ECS_ALREADY_IN_USE, ecs_get_name(world, component)); - ecs_assert( ecs_id_in_use(world, ecs_pair(component, EcsWildcard)) == false, - ECS_ALREADY_IN_USE, ecs_get_name(world, component)); +/* Serve REST API from wasm image when running in emscripten */ +#ifdef ECS_TARGET_EM +#include - ecs_type_info_t *ti = flecs_type_info_ensure(world, component); - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); +ecs_http_server_t *flecs_wasm_rest_server; - ecs_check(!ti->component || ti->component == component, - ECS_INCONSISTENT_COMPONENT_ACTION, NULL); - - if (!ti->size) { - const EcsComponent *component_ptr = ecs_get( - world, component, EcsComponent); - - /* Cannot register lifecycle actions for things that aren't a component */ - ecs_check(component_ptr != NULL, ECS_INVALID_PARAMETER, NULL); - /* Cannot register lifecycle actions for components with size 0 */ - ecs_check(component_ptr->size != 0, ECS_INVALID_PARAMETER, NULL); - - ti->size = component_ptr->size; - ti->alignment = component_ptr->alignment; - } - - if (h->ctor) ti->hooks.ctor = h->ctor; - if (h->dtor) ti->hooks.dtor = h->dtor; - if (h->copy) ti->hooks.copy = h->copy; - if (h->move) ti->hooks.move = h->move; - if (h->copy_ctor) ti->hooks.copy_ctor = h->copy_ctor; - if (h->move_ctor) ti->hooks.move_ctor = h->move_ctor; - if (h->ctor_move_dtor) ti->hooks.ctor_move_dtor = h->ctor_move_dtor; - if (h->move_dtor) ti->hooks.move_dtor = h->move_dtor; - - if (h->on_add) ti->hooks.on_add = h->on_add; - if (h->on_remove) ti->hooks.on_remove = h->on_remove; - if (h->on_set) ti->hooks.on_set = h->on_set; - - if (h->ctx) ti->hooks.ctx = h->ctx; - if (h->binding_ctx) ti->hooks.binding_ctx = h->binding_ctx; - if (h->ctx_free) ti->hooks.ctx_free = h->ctx_free; - if (h->binding_ctx_free) ti->hooks.binding_ctx_free = h->binding_ctx_free; - - /* If no constructor is set, invoking any of the other lifecycle actions - * is not safe as they will potentially access uninitialized memory. For - * ease of use, if no constructor is specified, set a default one that - * initializes the component to 0. */ - if (!h->ctor && (h->dtor || h->copy || h->move)) { - ti->hooks.ctor = ecs_default_ctor; - } - - /* Set default copy ctor, move ctor and merge */ - if (h->copy && !h->copy_ctor) { - ti->hooks.copy_ctor = flecs_default_copy_ctor; - } - - if (h->move && !h->move_ctor) { - ti->hooks.move_ctor = flecs_default_move_ctor; - } - - if (!h->ctor_move_dtor) { - if (h->move) { - if (h->dtor) { - if (h->move_ctor) { - /* If an explicit move ctor has been set, use callback - * that uses the move ctor vs. using a ctor+move */ - ti->hooks.ctor_move_dtor = flecs_default_move_ctor_w_dtor; - } else { - /* If no explicit move_ctor has been set, use - * combination of ctor + move + dtor */ - ti->hooks.ctor_move_dtor = flecs_default_ctor_w_move_w_dtor; - } - } else { - /* If no dtor has been set, this is just a move ctor */ - ti->hooks.ctor_move_dtor = ti->hooks.move_ctor; - } - } else { - /* If move is not set but move_ctor and dtor is, we can still set - * ctor_move_dtor. */ - if (h->move_ctor) { - if (h->dtor) { - ti->hooks.ctor_move_dtor = flecs_default_move_ctor_w_dtor; - } else { - ti->hooks.ctor_move_dtor = ti->hooks.move_ctor; - } - } - } - } - - if (!h->move_dtor) { - if (h->move) { - if (h->dtor) { - ti->hooks.move_dtor = flecs_default_move_w_dtor; - } else { - ti->hooks.move_dtor = flecs_default_move; - } +EMSCRIPTEN_KEEPALIVE +char* flecs_explorer_request(const char *method, char *request) { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + ecs_http_server_request(flecs_wasm_rest_server, method, request, &reply); + if (reply.code == 200) { + return ecs_strbuf_get(&reply.body); + } else { + char *body = ecs_strbuf_get(&reply.body); + if (body) { + return body; } else { - if (h->dtor) { - ti->hooks.move_dtor = flecs_default_dtor; - } + return flecs_asprintf( + "{\"error\": \"bad request\", \"status\": %d}", reply.code); } } - -error: - return; } +#endif -const ecs_type_hooks_t* ecs_get_hooks_id( +int ecs_app_run( ecs_world_t *world, - ecs_entity_t id) + ecs_app_desc_t *desc) { - const ecs_type_info_t *ti = ecs_get_type_info(world, id); - if (ti) { - return &ti->hooks; + ecs_app_desc = *desc; + + /* Don't set FPS & threads if using emscripten */ +#ifndef ECS_TARGET_EM + if (ECS_NEQZERO(ecs_app_desc.target_fps)) { + ecs_set_target_fps(world, ecs_app_desc.target_fps); } - return NULL; -} + if (ecs_app_desc.threads) { + ecs_set_threads(world, ecs_app_desc.threads); + } +#endif -void ecs_atfini( - ecs_world_t *world, - ecs_fini_action_t action, - void *ctx) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_check(action != NULL, ECS_INVALID_PARAMETER, NULL); + /* REST server enables connecting to app with explorer */ + if (desc->enable_rest) { +#ifdef FLECS_REST +#ifdef ECS_TARGET_EM + flecs_wasm_rest_server = ecs_rest_server_init(world, NULL); +#else + ECS_IMPORT(world, FlecsRest); + ecs_set(world, EcsWorld, EcsRest, {.port = desc->port }); +#endif +#else + ecs_warn("cannot enable remote API, REST addon not available"); +#endif + } - ecs_action_elem_t *elem = ecs_vec_append_t(NULL, &world->fini_actions, - ecs_action_elem_t); - ecs_assert(elem != NULL, ECS_INTERNAL_ERROR, NULL); + /* Monitoring periodically collects statistics */ + if (desc->enable_stats) { +#ifdef FLECS_STATS + ECS_IMPORT(world, FlecsStats); +#else + ecs_warn("cannot enable monitoring, MONITOR addon not available"); +#endif + } - elem->action = action; - elem->ctx = ctx; -error: - return; + return run_action(world, &ecs_app_desc); } -void ecs_run_post_frame( +int ecs_app_run_frame( ecs_world_t *world, - ecs_fini_action_t action, - void *ctx) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(action != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_action_elem_t *elem = ecs_vec_append_t(&stage->allocator, - &stage->post_frame_actions, ecs_action_elem_t); - ecs_assert(elem != NULL, ECS_INTERNAL_ERROR, NULL); - - elem->action = action; - elem->ctx = ctx; -error: - return; -} - -/* Unset data in tables */ -static -void flecs_fini_unset_tables( - ecs_world_t *world) + const ecs_app_desc_t *desc) { - ecs_sparse_t *tables = &world->store.tables; - int32_t i, count = flecs_sparse_count(tables); - - for (i = 0; i < count; i ++) { - ecs_table_t *table = flecs_sparse_get_dense_t(tables, ecs_table_t, i); - flecs_table_remove_actions(world, table); - } + return frame_action(world, desc); } -/* Invoke fini actions */ -static -void flecs_fini_actions( - ecs_world_t *world) +int ecs_app_set_run_action( + ecs_app_run_action_t callback) { - int32_t i, count = ecs_vec_count(&world->fini_actions); - ecs_action_elem_t *elems = ecs_vec_first(&world->fini_actions); - for (i = 0; i < count; i ++) { - elems[i].action(world, elems[i].ctx); + if (run_action != flecs_default_run_action && run_action != callback) { + ecs_err("run action already set"); + return -1; } - ecs_vec_fini_t(NULL, &world->fini_actions, ecs_action_elem_t); -} + run_action = callback; -/* Cleanup remaining type info elements */ -static -void flecs_fini_type_info( - ecs_world_t *world) -{ - int32_t i, count = flecs_sparse_count(&world->type_info); - ecs_sparse_t *type_info = &world->type_info; - for (i = 0; i < count; i ++) { - ecs_type_info_t *ti = flecs_sparse_get_dense_t(type_info, - ecs_type_info_t, i); - flecs_type_info_fini(ti); - } - flecs_sparse_fini(&world->type_info); + return 0; } -ecs_entity_t flecs_get_oneof( - const ecs_world_t *world, - ecs_entity_t e) +int ecs_app_set_frame_action( + ecs_app_frame_action_t callback) { - if (ecs_is_alive(world, e)) { - if (ecs_has_id(world, e, EcsOneOf)) { - return e; - } else { - return ecs_get_target(world, e, EcsOneOf, 0); - } - } else { - return 0; + if (frame_action != flecs_default_frame_action && frame_action != callback) { + ecs_err("frame action already set"); + return -1; } -} - -/* The destroyer of worlds */ -int ecs_fini( - ecs_world_t *world) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); - ecs_assert(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, NULL); - ecs_assert(world->stages[0].defer == 0, ECS_INVALID_OPERATION, - "call defer_end before destroying world"); - - ecs_trace("#[bold]shutting down world"); - ecs_log_push(); - world->flags |= EcsWorldQuit; + frame_action = callback; - /* Delete root entities first using regular APIs. This ensures that cleanup - * policies get a chance to execute. */ - ecs_dbg_1("#[bold]cleanup root entities"); - ecs_log_push_1(); - flecs_fini_roots(world); - ecs_log_pop_1(); + return 0; +} - world->flags |= EcsWorldFini; +#endif - /* Run fini actions (simple callbacks ran when world is deleted) before - * destroying the storage */ - ecs_dbg_1("#[bold]run fini actions"); - ecs_log_push_1(); - flecs_fini_actions(world); - ecs_log_pop_1(); +/** + * @file addons/doc.c + * @brief Doc addon. + */ - ecs_dbg_1("#[bold]cleanup remaining entities"); - ecs_log_push_1(); - /* Operations invoked during UnSet/OnRemove/destructors are deferred and - * will be discarded after world cleanup */ - flecs_defer_begin(world, &world->stages[0]); +#ifdef FLECS_DOC - /* Run UnSet/OnRemove actions for components while the store is still - * unmodified by cleanup. */ - flecs_fini_unset_tables(world); +static ECS_COPY(EcsDocDescription, dst, src, { + ecs_os_strset((char**)&dst->value, src->value); - /* This will destroy all entities and components. */ - flecs_fini_store(world); +}) - /* Purge deferred operations from the queue. This discards operations but - * makes sure that any resources in the queue are freed */ - flecs_defer_purge(world, &world->stages[0]); - ecs_log_pop_1(); +static ECS_MOVE(EcsDocDescription, dst, src, { + ecs_os_free((char*)dst->value); + dst->value = src->value; + src->value = NULL; +}) - /* All queries are cleaned up, so monitors should've been cleaned up too */ - ecs_assert(!ecs_map_is_init(&world->monitors.monitors), - ECS_INTERNAL_ERROR, NULL); +static ECS_DTOR(EcsDocDescription, ptr, { + ecs_os_free((char*)ptr->value); +}) - /* Cleanup world ctx and binding_ctx */ - if (world->ctx_free) { - world->ctx_free(world->ctx); - } - if (world->binding_ctx_free) { - world->binding_ctx_free(world->binding_ctx); +static +void flecs_doc_set( + ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t kind, + const char *value) +{ + if (value) { + ecs_set_pair(world, entity, EcsDocDescription, kind, { + /* Safe, value gets copied by copy hook */ + .value = ECS_CONST_CAST(char*, value) + }); + } else { + ecs_remove_pair(world, entity, ecs_id(EcsDocDescription), kind); } - - /* After this point no more user code is invoked */ - - ecs_dbg_1("#[bold]cleanup world datastructures"); - ecs_log_push_1(); - flecs_entities_fini(world); - flecs_sparse_fini(world->pending_tables); - flecs_sparse_fini(world->pending_buffer); - ecs_os_free(world->pending_tables); - ecs_os_free(world->pending_buffer); - flecs_fini_id_records(world); - flecs_fini_type_info(world); - flecs_observable_fini(&world->observable); - flecs_name_index_fini(&world->aliases); - flecs_name_index_fini(&world->symbols); - ecs_set_stage_count(world, 0); - ecs_log_pop_1(); - - flecs_world_allocators_fini(world); - - /* End of the world */ - ecs_poly_free(world, ecs_world_t); - ecs_os_fini(); - - ecs_trace("world destroyed, bye!"); - ecs_log_pop(); - - return 0; } -bool ecs_is_fini( - const ecs_world_t *world) +void ecs_doc_set_uuid( + ecs_world_t *world, + ecs_entity_t entity, + const char *name) { - ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); - world = ecs_get_world(world); - return ECS_BIT_IS_SET(world->flags, EcsWorldFini); + flecs_doc_set(world, entity, EcsDocUuid, name); } -void ecs_dim( +void ecs_doc_set_name( ecs_world_t *world, - int32_t entity_count) + ecs_entity_t entity, + const char *name) { - ecs_poly_assert(world, ecs_world_t); - flecs_entities_set_size(world, entity_count + FLECS_HI_COMPONENT_ID); + flecs_doc_set(world, entity, EcsName, name); } -void flecs_eval_component_monitors( - ecs_world_t *world) +void ecs_doc_set_brief( + ecs_world_t *world, + ecs_entity_t entity, + const char *brief) { - ecs_poly_assert(world, ecs_world_t); - flecs_process_pending_tables(world); - flecs_eval_component_monitor(world); + flecs_doc_set(world, entity, EcsDocBrief, brief); } -void ecs_measure_frame_time( +void ecs_doc_set_detail( ecs_world_t *world, - bool enable) + ecs_entity_t entity, + const char *detail) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(ecs_os_has_time(), ECS_MISSING_OS_API, NULL); - - if (ECS_EQZERO(world->info.target_fps) || enable) { - ECS_BIT_COND(world->flags, EcsWorldMeasureFrameTime, enable); - } -error: - return; + flecs_doc_set(world, entity, EcsDocDetail, detail); } -void ecs_measure_system_time( +void ecs_doc_set_link( ecs_world_t *world, - bool enable) + ecs_entity_t entity, + const char *link) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(ecs_os_has_time(), ECS_MISSING_OS_API, NULL); - ECS_BIT_COND(world->flags, EcsWorldMeasureSystemTime, enable); -error: - return; + flecs_doc_set(world, entity, EcsDocLink, link); } -void ecs_set_target_fps( +void ecs_doc_set_color( ecs_world_t *world, - ecs_ftime_t fps) + ecs_entity_t entity, + const char *color) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(ecs_os_has_time(), ECS_MISSING_OS_API, NULL); - - ecs_measure_frame_time(world, true); - world->info.target_fps = fps; -error: - return; + flecs_doc_set(world, entity, EcsDocColor, color); } -void* ecs_get_ctx( - const ecs_world_t *world) +const char* ecs_doc_get_uuid( + const ecs_world_t *world, + ecs_entity_t entity) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - world = ecs_get_world(world); - return world->ctx; -error: - return NULL; + const EcsDocDescription *ptr = ecs_get_pair( + world, entity, EcsDocDescription, EcsDocUuid); + if (ptr) { + return ptr->value; + } else { + return NULL; + } } -void* ecs_get_binding_ctx( - const ecs_world_t *world) +const char* ecs_doc_get_name( + const ecs_world_t *world, + ecs_entity_t entity) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - world = ecs_get_world(world); - return world->binding_ctx; -error: - return NULL; + const EcsDocDescription *ptr = ecs_get_pair( + world, entity, EcsDocDescription, EcsName); + if (ptr) { + return ptr->value; + } else { + return ecs_get_name(world, entity); + } } -void ecs_set_ctx( - ecs_world_t *world, - void *ctx, - ecs_ctx_free_t ctx_free) +const char* ecs_doc_get_brief( + const ecs_world_t *world, + ecs_entity_t entity) { - ecs_poly_assert(world, ecs_world_t); - world->ctx = ctx; - world->ctx_free = ctx_free; + const EcsDocDescription *ptr = ecs_get_pair( + world, entity, EcsDocDescription, EcsDocBrief); + if (ptr) { + return ptr->value; + } else { + return NULL; + } } -void ecs_set_binding_ctx( - ecs_world_t *world, - void *ctx, - ecs_ctx_free_t ctx_free) +const char* ecs_doc_get_detail( + const ecs_world_t *world, + ecs_entity_t entity) { - ecs_poly_assert(world, ecs_world_t); - world->binding_ctx = ctx; - world->binding_ctx_free = ctx_free; + const EcsDocDescription *ptr = ecs_get_pair( + world, entity, EcsDocDescription, EcsDocDetail); + if (ptr) { + return ptr->value; + } else { + return NULL; + } } -void ecs_set_entity_range( - ecs_world_t *world, - ecs_entity_t id_start, - ecs_entity_t id_end) +const char* ecs_doc_get_link( + const ecs_world_t *world, + ecs_entity_t entity) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(!id_end || id_end > id_start, ECS_INVALID_PARAMETER, NULL); - ecs_check(!id_end || id_end > flecs_entities_max_id(world), - ECS_INVALID_PARAMETER, NULL); - - uint32_t start = (uint32_t)id_start; - uint32_t end = (uint32_t)id_end; - - if (flecs_entities_max_id(world) < start) { - flecs_entities_max_id(world) = start - 1; + const EcsDocDescription *ptr = ecs_get_pair( + world, entity, EcsDocDescription, EcsDocLink); + if (ptr) { + return ptr->value; + } else { + return NULL; } - - world->info.min_id = start; - world->info.max_id = end; -error: - return; } -bool ecs_enable_range_check( - ecs_world_t *world, - bool enable) +const char* ecs_doc_get_color( + const ecs_world_t *world, + ecs_entity_t entity) { - ecs_poly_assert(world, ecs_world_t); - bool old_value = world->range_check_enabled; - world->range_check_enabled = enable; - return old_value; + const EcsDocDescription *ptr = ecs_get_pair( + world, entity, EcsDocDescription, EcsDocColor); + if (ptr) { + return ptr->value; + } else { + return NULL; + } } -ecs_entity_t ecs_get_max_id( - const ecs_world_t *world) +/* Doc definitions for core components */ +static +void flecs_doc_import_core_definitions( + ecs_world_t *world) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - world = ecs_get_world(world); - return flecs_entities_max_id(world); -error: - return 0; -} + ecs_doc_set_brief(world, EcsFlecs, "Flecs root module"); + ecs_doc_set_link(world, EcsFlecs, "https://github.com/SanderMertens/flecs"); + ecs_doc_set_brief(world, EcsFlecsCore, "Module with builtin components"); + ecs_doc_set_brief(world, EcsFlecsInternals, "Module with internal entities"); -void ecs_set_entity_generation( - ecs_world_t *world, - ecs_entity_t entity_with_generation) + ecs_doc_set_brief(world, EcsWorld, "Entity associated with world"); + + ecs_doc_set_brief(world, ecs_id(EcsComponent), "Component that is added to components"); + ecs_doc_set_brief(world, EcsModule, "Tag that is added to modules"); + ecs_doc_set_brief(world, EcsPrefab, "Tag that is added to prefabs"); + ecs_doc_set_brief(world, EcsDisabled, "Tag that is added to disabled entities"); + ecs_doc_set_brief(world, EcsPrivate, "Tag that is added to private components"); + ecs_doc_set_brief(world, EcsFlag, "Internal tag for tracking ids with special id flags"); + ecs_doc_set_brief(world, ecs_id(EcsPoly), "Internal component that stores pointer to poly objects"); + + ecs_doc_set_brief(world, ecs_id(EcsIdentifier), "Component used for entity names"); + ecs_doc_set_brief(world, EcsName, "Tag used with EcsIdentifier to store entity name"); + ecs_doc_set_brief(world, EcsSymbol, "Tag used with EcsIdentifier to store entity symbol"); + ecs_doc_set_brief(world, EcsAlias, "Tag used with EcsIdentifier to store entity alias"); + + ecs_doc_set_brief(world, EcsQuery, "Tag added to query entities"); + ecs_doc_set_brief(world, EcsObserver, "Tag added to observer entities"); + + ecs_doc_set_brief(world, EcsTransitive, "Trait that enables transitive evaluation of relationships"); + ecs_doc_set_brief(world, EcsReflexive, "Trait that enables reflexive evaluation of relationships"); + ecs_doc_set_brief(world, EcsFinal, "Trait that indicates an entity cannot be inherited from"); + ecs_doc_set_brief(world, EcsDontInherit, "Trait that indicates it should not be inherited"); + ecs_doc_set_brief(world, EcsPairIsTag, "Trait that ensures a pair cannot contain a value"); + ecs_doc_set_brief(world, EcsAcyclic, "Trait that indicates a relationship is acyclic"); + ecs_doc_set_brief(world, EcsTraversable, "Trait that indicates a relationship is traversable"); + ecs_doc_set_brief(world, EcsExclusive, "Trait that ensures a relationship can only have one target"); + ecs_doc_set_brief(world, EcsSymmetric, "Trait that causes a relationship to be two-way"); + ecs_doc_set_brief(world, EcsWith, "Trait for adding additional components when a component is added"); + ecs_doc_set_brief(world, EcsOneOf, "Trait that enforces target of relationship is a child of "); + ecs_doc_set_brief(world, EcsOnDelete, "Cleanup trait for specifying what happens when component is deleted"); + ecs_doc_set_brief(world, EcsOnDeleteTarget, "Cleanup trait for specifying what happens when pair target is deleted"); + ecs_doc_set_brief(world, EcsRemove, "Cleanup action used with OnDelete/OnDeleteTarget"); + ecs_doc_set_brief(world, EcsDelete, "Cleanup action used with OnDelete/OnDeleteTarget"); + ecs_doc_set_brief(world, EcsPanic, "Cleanup action used with OnDelete/OnDeleteTarget"); + ecs_doc_set_brief(world, ecs_id(EcsDefaultChildComponent), "Sets default component hint for children of entity"); + ecs_doc_set_brief(world, EcsIsA, "Relationship used for expressing inheritance"); + ecs_doc_set_brief(world, EcsChildOf, "Relationship used for expressing hierarchies"); + ecs_doc_set_brief(world, EcsDependsOn, "Relationship used for expressing dependencies"); + ecs_doc_set_brief(world, EcsSlotOf, "Relationship used for expressing prefab slots"); + ecs_doc_set_brief(world, EcsOnAdd, "Event emitted when component is added"); + ecs_doc_set_brief(world, EcsOnRemove, "Event emitted when component is removed"); + ecs_doc_set_brief(world, EcsOnSet, "Event emitted when component is set"); + ecs_doc_set_brief(world, EcsMonitor, "Marker used to create monitor observers"); + ecs_doc_set_brief(world, EcsOnTableFill, "Event emitted when table becomes non-empty"); + ecs_doc_set_brief(world, EcsOnTableEmpty, "Event emitted when table becomes empty"); + ecs_doc_set_brief(world, EcsOnTableCreate, "Event emitted when table is created"); + ecs_doc_set_brief(world, EcsOnTableDelete, "Event emitted when table is deleted"); + + ecs_doc_set_brief(world, EcsThis, "Query marker to express $this variable"); + ecs_doc_set_brief(world, EcsWildcard, "Query marker to express match all wildcard"); + ecs_doc_set_brief(world, EcsAny, "Query marker to express match at least one wildcard"); + + ecs_doc_set_brief(world, EcsPredEq, "Query marker to express == operator"); + ecs_doc_set_brief(world, EcsPredMatch, "Query marker to express ~= operator"); + ecs_doc_set_brief(world, EcsPredLookup, "Query marker to express by-name lookup"); + ecs_doc_set_brief(world, EcsScopeOpen, "Query marker to express scope open"); + ecs_doc_set_brief(world, EcsScopeClose, "Query marker to express scope close"); + ecs_doc_set_brief(world, EcsEmpty, "Tag used to indicate a query has no results"); +} + +/* Doc definitions for doc components */ +static +void flecs_doc_import_doc_definitions( + ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); - ecs_assert(!(ecs_is_deferred(world)), ECS_INVALID_OPERATION, NULL); + ecs_entity_t doc = ecs_lookup(world, "flecs.doc"); + ecs_doc_set_brief(world, doc, "Flecs module with documentation components"); - flecs_entities_set_generation(world, entity_with_generation); + ecs_doc_set_brief(world, EcsDocBrief, "Brief description"); + ecs_doc_set_brief(world, EcsDocDetail, "Detailed description"); + ecs_doc_set_brief(world, EcsDocLink, "Link to additional documentation"); + ecs_doc_set_brief(world, EcsDocColor, "Color hint for entity"); - ecs_record_t *r = flecs_entities_get(world, entity_with_generation); - if (r && r->table) { - int32_t row = ECS_RECORD_TO_ROW(r->row); - ecs_entity_t *entities = r->table->data.entities.array; - entities[row] = entity_with_generation; - } + ecs_doc_set_brief(world, ecs_id(EcsDocDescription), "Component used to add documentation"); + ecs_doc_set_brief(world, EcsDocBrief, "Used as (Description, Brief) to add a brief description"); + ecs_doc_set_brief(world, EcsDocDetail, "Used as (Description, Detail) to add a detailed description"); + ecs_doc_set_brief(world, EcsDocLink, "Used as (Description, Link) to add a link"); } -const ecs_type_info_t* flecs_type_info_get( - const ecs_world_t *world, - ecs_entity_t component) -{ - ecs_poly_assert(world, ecs_world_t); - - ecs_assert(component != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!(component & ECS_ID_FLAGS_MASK), ECS_INTERNAL_ERROR, NULL); +void FlecsDocImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsDoc); - return flecs_sparse_try_t(&world->type_info, ecs_type_info_t, component); -} + ecs_set_name_prefix(world, "EcsDoc"); -ecs_type_info_t* flecs_type_info_ensure( - ecs_world_t *world, - ecs_entity_t component) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_assert(component != 0, ECS_INTERNAL_ERROR, NULL); + flecs_bootstrap_component(world, EcsDocDescription); + flecs_bootstrap_tag(world, EcsDocUuid); + flecs_bootstrap_tag(world, EcsDocBrief); + flecs_bootstrap_tag(world, EcsDocDetail); + flecs_bootstrap_tag(world, EcsDocLink); + flecs_bootstrap_tag(world, EcsDocColor); - const ecs_type_info_t *ti = flecs_type_info_get(world, component); - ecs_type_info_t *ti_mut = NULL; - if (!ti) { - ti_mut = flecs_sparse_ensure_t( - &world->type_info, ecs_type_info_t, component); - ecs_assert(ti_mut != NULL, ECS_INTERNAL_ERROR, NULL); - ti_mut->component = component; - } else { - ti_mut = ECS_CONST_CAST(ecs_type_info_t*, ti); - } + ecs_set_hooks(world, EcsDocDescription, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsDocDescription), + .copy = ecs_copy(EcsDocDescription), + .dtor = ecs_dtor(EcsDocDescription) + }); - if (!ti_mut->name) { - const char *sym = ecs_get_symbol(world, component); - if (sym) { - ti_mut->name = ecs_os_strdup(sym); - } else { - const char *name = ecs_get_name(world, component); - if (name) { - ti_mut->name = ecs_os_strdup(name); - } - } - } + ecs_add_pair(world, ecs_id(EcsDocDescription), EcsOnInstantiate, EcsDontInherit); + ecs_add_id(world, ecs_id(EcsDocDescription), EcsPrivate); - return ti_mut; + flecs_doc_import_core_definitions(world); + flecs_doc_import_doc_definitions(world); } -bool flecs_type_info_init_id( - ecs_world_t *world, - ecs_entity_t component, - ecs_size_t size, - ecs_size_t alignment, - const ecs_type_hooks_t *li) -{ - bool changed = false; - - flecs_entities_ensure(world, component); +#endif - ecs_type_info_t *ti = NULL; - if (!size || !alignment) { - ecs_assert(size == 0 && alignment == 0, - ECS_INVALID_COMPONENT_SIZE, NULL); - ecs_assert(li == NULL, ECS_INCONSISTENT_COMPONENT_ACTION, NULL); - flecs_sparse_remove_t(&world->type_info, ecs_type_info_t, component); - } else { - ti = flecs_type_info_ensure(world, component); - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - changed |= ti->size != size; - changed |= ti->alignment != alignment; - ti->size = size; - ti->alignment = alignment; - if (li) { - ecs_set_hooks_id(world, component, li); - } - } +/** + * @file addons/flecs_cpp.c + * @brief Utilities for C++ addon. + */ - /* Set type info for id record of component */ - ecs_id_record_t *idr = flecs_id_record_ensure(world, component); - changed |= flecs_id_record_set_type_info(world, idr, ti); - bool is_tag = idr->flags & EcsIdTag; +#include - /* All id records with component as relationship inherit type info */ - idr = flecs_id_record_ensure(world, ecs_pair(component, EcsWildcard)); - do { - if (is_tag) { - changed |= flecs_id_record_set_type_info(world, idr, NULL); - } else if (ti) { - changed |= flecs_id_record_set_type_info(world, idr, ti); - } else if ((idr->type_info != NULL) && - (idr->type_info->component == component)) - { - changed |= flecs_id_record_set_type_info(world, idr, NULL); - } - } while ((idr = idr->first.next)); +/* Utilities for C++ API */ - /* All non-tag id records with component as object inherit type info, - * if relationship doesn't have type info */ - idr = flecs_id_record_ensure(world, ecs_pair(EcsWildcard, component)); - do { - if (!(idr->flags & EcsIdTag) && !idr->type_info) { - changed |= flecs_id_record_set_type_info(world, idr, ti); - } - } while ((idr = idr->first.next)); +#ifdef FLECS_CPP - /* Type info of (*, component) should always point to component */ - ecs_assert(flecs_id_record_get(world, ecs_pair(EcsWildcard, component))-> - type_info == ti, ECS_INTERNAL_ERROR, NULL); +/* Convert compiler-specific typenames extracted from __PRETTY_FUNCTION__ to + * a uniform identifier */ - return changed; -} +#define ECS_CONST_PREFIX "const " +#define ECS_STRUCT_PREFIX "struct " +#define ECS_CLASS_PREFIX "class " +#define ECS_ENUM_PREFIX "enum " -void flecs_type_info_fini( - ecs_type_info_t *ti) -{ - if (ti->hooks.ctx_free) { - ti->hooks.ctx_free(ti->hooks.ctx); - } - if (ti->hooks.binding_ctx_free) { - ti->hooks.binding_ctx_free(ti->hooks.binding_ctx); - } - if (ti->name) { - /* Safe to cast away const, world has ownership over string */ - ecs_os_free(ECS_CONST_CAST(char*, ti->name)); - ti->name = NULL; - } -} +#define ECS_CONST_LEN (-1 + (ecs_size_t)sizeof(ECS_CONST_PREFIX)) +#define ECS_STRUCT_LEN (-1 + (ecs_size_t)sizeof(ECS_STRUCT_PREFIX)) +#define ECS_CLASS_LEN (-1 + (ecs_size_t)sizeof(ECS_CLASS_PREFIX)) +#define ECS_ENUM_LEN (-1 + (ecs_size_t)sizeof(ECS_ENUM_PREFIX)) -void flecs_type_info_free( - ecs_world_t *world, - ecs_entity_t component) +static +ecs_size_t ecs_cpp_strip_prefix( + char *typeName, + ecs_size_t len, + const char *prefix, + ecs_size_t prefix_len) { - if (world->flags & EcsWorldQuit) { - /* If world is in the final teardown stages, cleanup policies are no - * longer applied and it can't be guaranteed that a component is not - * deleted before entities that use it. The remaining type info elements - * will be deleted after the store is finalized. */ - return; - } - - ecs_type_info_t *ti = flecs_sparse_try_t(&world->type_info, - ecs_type_info_t, component); - if (ti) { - flecs_type_info_fini(ti); - flecs_sparse_remove_t(&world->type_info, ecs_type_info_t, component); + if ((len > prefix_len) && !ecs_os_strncmp(typeName, prefix, prefix_len)) { + ecs_os_memmove(typeName, typeName + prefix_len, len - prefix_len); + typeName[len - prefix_len] = '\0'; + len -= prefix_len; } + return len; } -static -ecs_ftime_t flecs_insert_sleep( - ecs_world_t *world, - ecs_time_t *stop) +static +void ecs_cpp_trim_type_name( + char *typeName) { - ecs_poly_assert(world, ecs_world_t); + ecs_size_t len = ecs_os_strlen(typeName); - ecs_time_t start = *stop, now = start; - ecs_ftime_t delta_time = (ecs_ftime_t)ecs_time_measure(stop); + len = ecs_cpp_strip_prefix(typeName, len, ECS_CONST_PREFIX, ECS_CONST_LEN); + len = ecs_cpp_strip_prefix(typeName, len, ECS_STRUCT_PREFIX, ECS_STRUCT_LEN); + len = ecs_cpp_strip_prefix(typeName, len, ECS_CLASS_PREFIX, ECS_CLASS_LEN); + len = ecs_cpp_strip_prefix(typeName, len, ECS_ENUM_PREFIX, ECS_ENUM_LEN); - if (ECS_EQZERO(world->info.target_fps)) { - return delta_time; + while (typeName[len - 1] == ' ' || + typeName[len - 1] == '&' || + typeName[len - 1] == '*') + { + len --; + typeName[len] = '\0'; } - ecs_ftime_t target_delta_time = - ((ecs_ftime_t)1.0 / (ecs_ftime_t)world->info.target_fps); - - /* Calculate the time we need to sleep by taking the measured delta from the - * previous frame, and subtracting it from target_delta_time. */ - ecs_ftime_t sleep = target_delta_time - delta_time; - - /* Pick a sleep interval that is 4 times smaller than the time one frame - * should take. */ - ecs_ftime_t sleep_time = sleep / (ecs_ftime_t)4.0; - - do { - /* Only call sleep when sleep_time is not 0. On some platforms, even - * a sleep with a timeout of 0 can cause stutter. */ - if (ECS_NEQZERO(sleep_time)) { - ecs_sleepf((double)sleep_time); + /* Remove const at end of string */ + if (len > ECS_CONST_LEN) { + if (!ecs_os_strncmp(&typeName[len - ECS_CONST_LEN], " const", ECS_CONST_LEN)) { + typeName[len - ECS_CONST_LEN] = '\0'; } + len -= ECS_CONST_LEN; + } - now = start; - delta_time = (ecs_ftime_t)ecs_time_measure(&now); - } while ((target_delta_time - delta_time) > - (sleep_time / (ecs_ftime_t)2.0)); - - *stop = now; - return delta_time; -} - -static -ecs_ftime_t flecs_start_measure_frame( - ecs_world_t *world, - ecs_ftime_t user_delta_time) -{ - ecs_poly_assert(world, ecs_world_t); - - ecs_ftime_t delta_time = 0; - - if ((world->flags & EcsWorldMeasureFrameTime) || - (ECS_EQZERO(user_delta_time))) - { - ecs_time_t t = world->frame_start_time; - do { - if (world->frame_start_time.nanosec || world->frame_start_time.sec){ - delta_time = flecs_insert_sleep(world, &t); - } else { - ecs_time_measure(&t); - if (ECS_NEQZERO(world->info.target_fps)) { - delta_time = (ecs_ftime_t)1.0 / world->info.target_fps; - } else { - /* Best guess */ - delta_time = (ecs_ftime_t)1.0 / (ecs_ftime_t)60.0; - } + /* Check if there are any remaining "struct " strings, which can happen + * if this is a template type on msvc. */ + if (len > ECS_STRUCT_LEN) { + char *ptr = typeName; + while ((ptr = strstr(ptr + 1, ECS_STRUCT_PREFIX)) != 0) { + /* Make sure we're not matched with part of a longer identifier + * that contains 'struct' */ + if (ptr[-1] == '<' || ptr[-1] == ',' || isspace(ptr[-1])) { + ecs_os_memmove(ptr, ptr + ECS_STRUCT_LEN, + ecs_os_strlen(ptr + ECS_STRUCT_LEN) + 1); + len -= ECS_STRUCT_LEN; } - - /* Keep trying while delta_time is zero */ - } while (ECS_EQZERO(delta_time)); - - world->frame_start_time = t; - - /* Keep track of total time passed in world */ - world->info.world_time_total_raw += (ecs_ftime_t)delta_time; + } } - - return (ecs_ftime_t)delta_time; } -static -void flecs_stop_measure_frame( - ecs_world_t* world) +char* ecs_cpp_get_type_name( + char *type_name, + const char *func_name, + size_t len, + size_t front_len) { - ecs_poly_assert(world, ecs_world_t); - - if (world->flags & EcsWorldMeasureFrameTime) { - ecs_time_t t = world->frame_start_time; - world->info.frame_time_total += (ecs_ftime_t)ecs_time_measure(&t); - } + memcpy(type_name, func_name + front_len, len); + type_name[len] = '\0'; + ecs_cpp_trim_type_name(type_name); + return type_name; } -ecs_ftime_t ecs_frame_begin( - ecs_world_t *world, - ecs_ftime_t user_delta_time) +char* ecs_cpp_get_symbol_name( + char *symbol_name, + const char *type_name, + size_t len) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); - ecs_check(ECS_NEQZERO(user_delta_time) || ecs_os_has_time(), - ECS_MISSING_OS_API, "get_time"); - - /* Start measuring total frame time */ - ecs_ftime_t delta_time = flecs_start_measure_frame(world, user_delta_time); - if (ECS_EQZERO(user_delta_time)) { - user_delta_time = delta_time; - } - - world->info.delta_time_raw = user_delta_time; - world->info.delta_time = user_delta_time * world->info.time_scale; - - /* Keep track of total scaled time passed in world */ - world->info.world_time_total += world->info.delta_time; + const char *ptr; + size_t i; + for (i = 0, ptr = type_name; i < len && *ptr; i ++, ptr ++) { + if (*ptr == ':') { + symbol_name[i] = '.'; + ptr ++; + } else { + symbol_name[i] = *ptr; + } + } - ecs_run_aperiodic(world, 0); + symbol_name[i] = '\0'; - return world->info.delta_time; -error: - return (ecs_ftime_t)0; + return symbol_name; } -void ecs_frame_end( - ecs_world_t *world) +static +const char* flecs_cpp_func_rchr( + const char *func_name, + ecs_size_t func_name_len, + ecs_size_t func_back_len, + char ch) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); - - world->info.frame_count_total ++; - - ecs_stage_t *stages = world->stages; - int32_t i, count = world->stage_count; - for (i = 0; i < count; i ++) { - flecs_stage_merge_post_frame(world, &stages[i]); + const char *r = strrchr(func_name, ch); + if ((r - func_name) >= (func_name_len - flecs_uto(ecs_size_t, func_back_len))) { + return NULL; } - - flecs_stop_measure_frame(world); -error: - return; + return r; } -const ecs_world_info_t* ecs_get_world_info( - const ecs_world_t *world) +static +const char* flecs_cpp_func_max( + const char *a, + const char *b) { - world = ecs_get_world(world); - return &world->info; + if (a > b) return a; + return b; } -void flecs_delete_table( - ecs_world_t *world, - ecs_table_t *table) +char* ecs_cpp_get_constant_name( + char *constant_name, + const char *func_name, + size_t func_name_len, + size_t func_back_len) { - ecs_poly_assert(world, ecs_world_t); - flecs_table_free(world, table); + ecs_size_t f_len = flecs_uto(ecs_size_t, func_name_len); + ecs_size_t fb_len = flecs_uto(ecs_size_t, func_back_len); + const char *start = flecs_cpp_func_rchr(func_name, f_len, fb_len, ' '); + start = flecs_cpp_func_max(start, flecs_cpp_func_rchr( + func_name, f_len, fb_len, ')')); + start = flecs_cpp_func_max(start, flecs_cpp_func_rchr( + func_name, f_len, fb_len, ':')); + start = flecs_cpp_func_max(start, flecs_cpp_func_rchr( + func_name, f_len, fb_len, ',')); + ecs_assert(start != NULL, ECS_INVALID_PARAMETER, func_name); + start ++; + + ecs_size_t len = flecs_uto(ecs_size_t, + (f_len - (start - func_name) - fb_len)); + ecs_os_memcpy_n(constant_name, start, char, len); + constant_name[len] = '\0'; + return constant_name; } -static -void flecs_process_empty_queries( - ecs_world_t *world) +// Names returned from the name_helper class do not start with :: +// but are relative to the root. If the namespace of the type +// overlaps with the namespace of the current module, strip it from +// the implicit identifier. +// This allows for registration of component types that are not in the +// module namespace to still be registered under the module scope. +const char* ecs_cpp_trim_module( + ecs_world_t *world, + const char *type_name) { - 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_entity_t scope = ecs_get_scope(world); + if (!scope) { + return type_name; } - ecs_run_aperiodic(world, EcsAperiodicEmptyTables); - - /* Make sure that we defer adding the inactive tags until after iterating - * the query */ - flecs_defer_begin(world, &world->stages[0]); - - ecs_table_cache_iter_t it; - const ecs_table_record_t *tr; - if (flecs_table_cache_iter(&idr->cache, &it)) { - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; - EcsPoly *queries = ecs_table_get_column(table, tr->column, 0); - int32_t i, count = ecs_table_count(table); - - for (i = 0; i < count; i ++) { - ecs_query_t *query = queries[i].poly; - ecs_entity_t *entities = table->data.entities.array; - if (!ecs_query_table_count(query)) { - ecs_add_id(world, entities[i], EcsEmpty); - } + char *path = ecs_get_path_w_sep(world, 0, scope, "::", NULL); + if (path) { + ecs_size_t len = ecs_os_strlen(path); + if (!ecs_os_strncmp(path, type_name, len)) { + // Type is a child of current parent, trim name of parent + type_name += len; + ecs_assert(type_name[0], ECS_INVALID_PARAMETER, + "invalid C++ type name"); + ecs_assert(type_name[0] == ':', ECS_INVALID_PARAMETER, + "invalid C++ type name"); + ecs_assert(type_name[1] == ':', ECS_INVALID_PARAMETER, + "invalid C++ type name"); + type_name += 2; + } else { + // Type is not a child of current parent, trim entire path + char *ptr = strrchr(type_name, ':'); + if (ptr) { + type_name = ptr + 1; } + } } + ecs_os_free(path); - flecs_defer_end(world, &world->stages[0]); + return type_name; } -/** Walk over tables that had a state change which requires bookkeeping */ -void flecs_process_pending_tables( - const ecs_world_t *world_r) +// Validate registered component +void ecs_cpp_component_validate( + ecs_world_t *world, + ecs_entity_t id, + const char *name, + const char *symbol, + size_t size, + size_t alignment, + bool implicit_name) { - ecs_poly_assert(world_r, ecs_world_t); - - /* We can't update the administration while in readonly mode, but we can - * ensure that when this function is called there are no pending events. */ - if (world_r->flags & EcsWorldReadonly) { - ecs_assert(flecs_sparse_count(world_r->pending_tables) == 0, - ECS_INTERNAL_ERROR, NULL); - return; - } + /* If entity has a name check if it matches */ + if (ecs_is_valid(world, id) && ecs_get_name(world, id) != NULL) { + if (!implicit_name && id >= EcsFirstUserComponentId) { +#ifndef FLECS_NDEBUG + char *path = ecs_get_path_w_sep( + world, 0, id, "::", NULL); + if (ecs_os_strcmp(path, name)) { + ecs_abort(ECS_INCONSISTENT_NAME, + "component '%s' already registered with name '%s'", + name, path); + } + ecs_os_free(path); +#endif + } - /* Safe to cast, world is not readonly */ - ecs_world_t *world = ECS_CONST_CAST(ecs_world_t*, world_r); - - /* If pending buffer is NULL there already is a stackframe that's iterating - * the table list. This can happen when an observer for a table event results - * in a mutation that causes another table to change state. A typical - * example of this is a system that becomes active/inactive as the result of - * a query (and as a result, its matched tables) becoming empty/non empty */ - if (!world->pending_buffer) { - return; - } + if (symbol) { + const char *existing_symbol = ecs_get_symbol(world, id); + if (existing_symbol) { + if (ecs_os_strcmp(symbol, existing_symbol)) { + ecs_abort(ECS_INCONSISTENT_NAME, + "component '%s' with symbol '%s' already registered with symbol '%s'", + name, symbol, existing_symbol); + } + } + } + } else { + /* Ensure that the entity id valid */ + if (!ecs_is_alive(world, id)) { + ecs_make_alive(world, id); + } - /* Swap buffer. The logic could in theory have been implemented with a - * single sparse set, but that would've complicated (and slowed down) the - * iteration. Additionally, by using a double buffer approach we can still - * keep most of the original ordering of events intact, which is desirable - * as it means that the ordering of tables in the internal datastructures is - * more predictable. */ - int32_t i, count = flecs_sparse_count(world->pending_tables); - if (!count) { - return; + /* Register name with entity, so that when the entity is created the + * correct id will be resolved from the name. Only do this when the + * entity is empty. */ + ecs_add_path_w_sep(world, id, 0, name, "::", "::"); } - flecs_journal_begin(world, EcsJournalTableEvents, 0, 0, 0); + /* If a component was already registered with this id but with a + * different size, the ecs_component_init function will fail. */ - do { - ecs_sparse_t *pending_tables = world->pending_tables; - world->pending_tables = world->pending_buffer; - world->pending_buffer = NULL; + /* We need to explicitly call ecs_component_init here again. Even though + * the component was already registered, it may have been registered + * with a different world. This ensures that the component is registered + * with the same id for the current world. + * If the component was registered already, nothing will change. */ + ecs_entity_t ent = ecs_component_init(world, &(ecs_component_desc_t){ + .entity = id, + .type.size = flecs_uto(int32_t, size), + .type.alignment = flecs_uto(int32_t, alignment) + }); + (void)ent; + ecs_assert(ent == id, ECS_INTERNAL_ERROR, NULL); +} - /* Make sure that any ECS operations that occur while delivering the - * events does not cause inconsistencies, like sending an Empty - * notification for a table that just became non-empty. */ - flecs_defer_begin(world, &world->stages[0]); +ecs_entity_t ecs_cpp_component_register( + ecs_world_t *world, + ecs_entity_t id, + const char *name, + const char *symbol, + ecs_size_t size, + ecs_size_t alignment, + bool implicit_name, + bool *existing_out) +{ + (void)size; + (void)alignment; - for (i = 0; i < count; i ++) { - ecs_table_t *table = flecs_sparse_get_dense_t( - pending_tables, ecs_table_t*, i)[0]; - if (!table->id) { - /* Table is being deleted, ignore empty events */ - continue; - } - - /* For each id in the table, add it to the empty/non empty list - * based on its current state */ - if (flecs_table_records_update_empty(table)) { - int32_t table_count = ecs_table_count(table); - if (table->flags & (EcsTableHasOnTableFill|EcsTableHasOnTableEmpty)) { - /* Only emit an event when there was a change in the - * administration. It is possible that a table ended up in the - * pending_tables list by going from empty->non-empty, but then - * became empty again. By the time we run this code, no changes - * in the administration would actually be made. */ - ecs_entity_t evt = table_count ? EcsOnTableFill : EcsOnTableEmpty; - if (ecs_should_log_3()) { - ecs_dbg_3("table %u state change (%s)", - (uint32_t)table->id, - table_count ? "non-empty" : "empty"); - } - - ecs_log_push_3(); - - flecs_emit(world, world, &(ecs_event_desc_t){ - .event = evt, - .table = table, - .ids = &table->type, - .observable = world, - .flags = EcsEventTableOnly - }); + /* If the component is not yet registered, ensure no other component + * or entity has been registered with this name. Ensure component is + * looked up from root. */ + bool existing = false; + ecs_entity_t prev_scope = ecs_set_scope(world, 0); + ecs_entity_t ent; + if (id) { + ent = id; + } else { + ent = ecs_lookup_path_w_sep(world, 0, name, "::", "::", false); + existing = ent != 0 && ecs_has(world, ent, EcsComponent); + } + ecs_set_scope(world, prev_scope); - ecs_log_pop_3(); + /* If entity exists, compare symbol name to ensure that the component + * we are trying to register under this name is the same */ + if (ent) { + const EcsComponent *component = ecs_get(world, ent, EcsComponent); + if (component != NULL) { + const char *sym = ecs_get_symbol(world, ent); + if (sym && ecs_os_strcmp(sym, symbol)) { + /* Application is trying to register a type with an entity that + * was already associated with another type. In most cases this + * is an error, with the exception of a scenario where the + * application is wrapping a C type with a C++ type. + * + * In this case the C++ type typically inherits from the C type, + * and adds convenience methods to the derived class without + * changing anything that would change the size or layout. + * + * To meet this condition, the new type must have the same size + * and alignment as the existing type, and the name of the type + * type must be equal to the registered name (not symbol). + * + * The latter ensures that it was the intent of the application + * to alias the type, vs. accidentally registering an unrelated + * type with the same size/alignment. */ + char *type_path = ecs_get_path(world, ent); + if (ecs_os_strcmp(type_path, symbol) || + component->size != size || + component->alignment != alignment) + { + ecs_err( + "component with name '%s' is already registered for"\ + " type '%s' (trying to register for type '%s')", + name, sym, symbol); + ecs_abort(ECS_NAME_IN_USE, NULL); } - world->info.empty_table_count += (table_count == 0) * 2 - 1; + ecs_os_free(type_path); + } else if (!sym) { + ecs_set_symbol(world, ent, symbol); } } - flecs_sparse_clear(pending_tables); - ecs_defer_end(world); + /* If no entity is found, lookup symbol to check if the component was + * registered under a different name. */ + } else if (!implicit_name) { + ent = ecs_lookup_symbol(world, symbol, false, false); + ecs_assert(ent == 0 || (ent == id), ECS_INCONSISTENT_COMPONENT_ID, symbol); + } - world->pending_buffer = pending_tables; - } while ((count = flecs_sparse_count(world->pending_tables))); + if (existing_out) { + *existing_out = existing; + } - flecs_journal_end(); + return ent; } -void flecs_table_set_empty( +ecs_entity_t ecs_cpp_component_register_explicit( ecs_world_t *world, - ecs_table_t *table) + ecs_entity_t s_id, + ecs_entity_t id, + const char *name, + const char *type_name, + const char *symbol, + size_t size, + size_t alignment, + bool is_component, + bool *existing_out) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + char *existing_name = NULL; + if (existing_out) *existing_out = false; - if (ecs_table_count(table)) { - table->_->generation = 0; + // If an explicit id is provided, it is possible that the symbol and + // name differ from the actual type, as the application may alias + // one type to another. + if (!id) { + if (!name) { + // If no name was provided first check if a type with the provided + // symbol was already registered. + id = ecs_lookup_symbol(world, symbol, false, false); + if (id) { + existing_name = ecs_get_path_w_sep(world, 0, id, "::", "::"); + name = existing_name; + if (existing_out) *existing_out = true; + } else { + // If type is not yet known, derive from type name + name = ecs_cpp_trim_module(world, type_name); + } + } + } else { + // If an explicit id is provided but it has no name, inherit + // the name from the type. + if (!ecs_is_valid(world, id) || !ecs_get_name(world, id)) { + name = ecs_cpp_trim_module(world, type_name); + } } - flecs_sparse_ensure_fast_t(world->pending_tables, ecs_table_t*, - (uint32_t)table->id)[0] = table; -} + ecs_entity_t entity; + if (is_component || size != 0) { + entity = ecs_entity(world, { + .id = s_id, + .name = name, + .sep = "::", + .root_sep = "::", + .symbol = symbol, + .use_low_id = true + }); + ecs_assert(entity != 0, ECS_INVALID_OPERATION, + "registration failed for component %s", name); -bool ecs_id_in_use( - const ecs_world_t *world, - ecs_id_t id) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - return false; + entity = ecs_component_init(world, &(ecs_component_desc_t){ + .entity = entity, + .type.size = flecs_uto(int32_t, size), + .type.alignment = flecs_uto(int32_t, alignment) + }); + ecs_assert(entity != 0, ECS_INVALID_OPERATION, + "registration failed for component %s", name); + } else { + entity = ecs_entity(world, { + .id = s_id, + .name = name, + .sep = "::", + .root_sep = "::", + .symbol = symbol, + .use_low_id = true + }); } - return (flecs_table_cache_count(&idr->cache) != 0) || - (flecs_table_cache_empty_count(&idr->cache) != 0); + + ecs_assert(entity != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!s_id || s_id == entity, ECS_INTERNAL_ERROR, NULL); + ecs_os_free(existing_name); + + return entity; } -void ecs_run_aperiodic( +void ecs_cpp_enum_init( ecs_world_t *world, - ecs_flags32_t flags) + ecs_entity_t id) { - ecs_poly_assert(world, ecs_world_t); - - if (!flags || (flags & EcsAperiodicEmptyTables)) { - flecs_process_pending_tables(world); - } - if ((flags & EcsAperiodicEmptyQueries)) { - flecs_process_empty_queries(world); - } - if (!flags || (flags & EcsAperiodicComponentMonitors)) { - flecs_eval_component_monitors(world); - } + (void)world; + (void)id; +#ifdef FLECS_META + ecs_suspend_readonly_state_t readonly_state; + world = flecs_suspend_readonly(world, &readonly_state); + ecs_set(world, id, EcsEnum, {0}); + flecs_resume_readonly(world, &readonly_state); +#endif } -int32_t ecs_delete_empty_tables( +ecs_entity_t ecs_cpp_enum_constant_register( ecs_world_t *world, - ecs_id_t id, - uint16_t clear_generation, - uint16_t delete_generation, - int32_t min_id_count, - double time_budget_seconds) + ecs_entity_t parent, + ecs_entity_t id, + const char *name, + int value) { - ecs_poly_assert(world, ecs_world_t); + ecs_suspend_readonly_state_t readonly_state; + world = flecs_suspend_readonly(world, &readonly_state); - /* Make sure empty tables are in the empty table lists */ - ecs_run_aperiodic(world, EcsAperiodicEmptyTables); + const char *parent_name = ecs_get_name(world, parent); + ecs_size_t parent_name_len = ecs_os_strlen(parent_name); + if (!ecs_os_strncmp(name, parent_name, parent_name_len)) { + name += parent_name_len; + if (name[0] == '_') { + name ++; + } + } - ecs_time_t start = {0}, cur = {0}; - int32_t delete_count = 0, clear_count = 0; - bool time_budget = false; + ecs_entity_t prev = ecs_set_scope(world, parent); + id = ecs_entity(world, { + .id = id, + .name = name + }); + ecs_assert(id != 0, ECS_INVALID_OPERATION, name); + ecs_set_scope(world, prev); - if (ECS_NEQZERO(time_budget_seconds) || (ecs_should_log_1() && ecs_os_has_time())) { - ecs_time_measure(&start); - } + #ifdef FLECS_DEBUG + const EcsComponent *cptr = ecs_get(world, parent, EcsComponent); + ecs_assert(cptr != NULL, ECS_INVALID_PARAMETER, "enum is not a component"); + ecs_assert(cptr->size == ECS_SIZEOF(int32_t), ECS_UNSUPPORTED, + "enum component must have 32bit size"); + #endif - if (ECS_NEQZERO(time_budget_seconds)) { - time_budget = true; - } +#ifdef FLECS_META + ecs_set_id(world, id, ecs_pair(EcsConstant, ecs_id(ecs_i32_t)), + sizeof(ecs_i32_t), &value); +#endif - if (!id) { - id = EcsAny; /* Iterate all empty tables */ - } + flecs_resume_readonly(world, &readonly_state); - ecs_id_record_t *idr = flecs_id_record_get(world, id); - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_empty_iter((ecs_table_cache_t*)idr, &it)) { - ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - if (time_budget) { - cur = start; - if (ecs_time_measure(&cur) > time_budget_seconds) { - goto done; - } - } + ecs_trace("#[green]constant#[reset] %s.%s created with value %d", + ecs_get_name(world, parent), name, value); - ecs_table_t *table = tr->hdr.table; - ecs_assert(ecs_table_count(table) == 0, ECS_INTERNAL_ERROR, NULL); + return id; +} - if (table->type.count < min_id_count) { - continue; - } +static int32_t flecs_reset_count = 0; - uint16_t gen = ++ table->_->generation; - if (delete_generation && (gen > delete_generation)) { - flecs_table_free(world, table); - delete_count ++; - } else if (clear_generation && (gen > clear_generation)) { - if (flecs_table_shrink(world, table)) { - clear_count ++; - } - } - } - } +int32_t ecs_cpp_reset_count_get(void) { + return flecs_reset_count; +} -done: - if (ecs_should_log_1() && ecs_os_has_time()) { - if (delete_count) { - ecs_dbg_1("#[red]deleted#[normal] %d empty tables in %.2fs", - delete_count, ecs_time_measure(&start)); - } - if (clear_count) { - ecs_dbg_1("#[red]cleared#[normal] %d empty tables in %.2fs", - clear_count, ecs_time_measure(&start)); - } +int32_t ecs_cpp_reset_count_inc(void) { + return ++flecs_reset_count; +} + +#ifdef FLECS_META +const ecs_member_t* ecs_cpp_last_member( + const ecs_world_t *world, + ecs_entity_t type) +{ + const EcsStruct *st = ecs_get(world, type, EcsStruct); + if (!st) { + char *type_str = ecs_get_path(world, type); + ecs_err("entity '%s' is not a struct", type_str); + ecs_os_free(type_str); + return 0; } - return delete_count; + ecs_member_t *m = ecs_vec_get_t(&st->members, ecs_member_t, + ecs_vec_count(&st->members) - 1); + ecs_assert(m != NULL, ECS_INTERNAL_ERROR, NULL); + + return m; } +#endif + +#endif /** - * @file addons/alerts.c - * @brief Alerts addon. + * @file addons/http.c + * @brief HTTP addon. + * + * This is a heavily modified version of the EmbeddableWebServer (see copyright + * below). This version has been stripped from everything not strictly necessary + * for receiving/replying to simple HTTP requests, and has been modified to use + * the Flecs OS API. + * + * EmbeddableWebServer Copyright (c) 2016, 2019, 2020 Forrest Heller, and + * CONTRIBUTORS (see below) - All rights reserved. + * + * CONTRIBUTORS: + * Martin Pulec - bug fixes, warning fixes, IPv6 support + * Daniel Barry - bug fix (ifa_addr != NULL) + * + * Released under the BSD 2-clause license: + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. THIS SOFTWARE IS + * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef FLECS_ALERTS +#ifdef FLECS_HTTP -ECS_COMPONENT_DECLARE(FlecsAlerts); +#ifdef ECS_TARGET_MSVC +#pragma comment(lib, "Ws2_32.lib") +#endif -typedef struct EcsAlert { - char *message; - ecs_map_t instances; /* Active instances for metric */ - ecs_ftime_t retain_period; /* How long to retain the alert */ - ecs_vec_t severity_filters; /* Severity filters */ - - /* Member range monitoring */ - ecs_id_t id; /* (Component) id that contains to monitor member */ - ecs_entity_t member; /* Member to monitor */ - int32_t offset; /* Offset of member in component */ - int32_t size; /* Size of component */ - ecs_primitive_kind_t kind; /* Primitive type kind */ - ecs_ref_t ranges; /* Reference to ranges component */ - int32_t var_id; /* Variable from which to obtain data (0 = $this) */ -} EcsAlert; +#if defined(ECS_TARGET_WINDOWS) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +typedef SOCKET ecs_http_socket_t; +#else +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __FreeBSD__ +#include +#endif +typedef int ecs_http_socket_t; -typedef struct EcsAlertTimeout { - ecs_ftime_t inactive_time; /* Time the alert has been inactive */ - ecs_ftime_t expire_time; /* Expiration duration */ -} EcsAlertTimeout; +#if !defined(MSG_NOSIGNAL) +#define MSG_NOSIGNAL (0) +#endif -ECS_COMPONENT_DECLARE(EcsAlertTimeout); +#endif -static -ECS_CTOR(EcsAlert, ptr, { - ecs_os_zeromem(ptr); - ecs_map_init(&ptr->instances, NULL); - ecs_vec_init_t(NULL, &ptr->severity_filters, ecs_alert_severity_filter_t, 0); -}) +/* Max length of request method */ +#define ECS_HTTP_METHOD_LEN_MAX (8) -static -ECS_DTOR(EcsAlert, ptr, { - ecs_os_free(ptr->message); - ecs_map_fini(&ptr->instances); - ecs_vec_fini_t(NULL, &ptr->severity_filters, ecs_alert_severity_filter_t); -}) +/* Timeout (s) before connection purge */ +#define ECS_HTTP_CONNECTION_PURGE_TIMEOUT (1.0) -static -ECS_MOVE(EcsAlert, dst, src, { - ecs_os_free(dst->message); - dst->message = src->message; - src->message = NULL; +/* Number of dequeues before purging */ +#define ECS_HTTP_CONNECTION_PURGE_RETRY_COUNT (5) - ecs_map_fini(&dst->instances); - dst->instances = src->instances; - src->instances = (ecs_map_t){0}; +/* Number of retries receiving request */ +#define ECS_HTTP_REQUEST_RECV_RETRY (10) - ecs_vec_fini_t(NULL, &dst->severity_filters, ecs_alert_severity_filter_t); - dst->severity_filters = src->severity_filters; - src->severity_filters = (ecs_vec_t){0}; +/* Minimum interval between dequeueing requests (ms) */ +#define ECS_HTTP_MIN_DEQUEUE_INTERVAL (50) - dst->retain_period = src->retain_period; - dst->id = src->id; - dst->member = src->member; - dst->offset = src->offset; - dst->size = src->size; - dst->kind = src->kind; - dst->ranges = src->ranges; - dst->var_id = src->var_id; -}) +/* Minimum interval between printing statistics (ms) */ +#define ECS_HTTP_MIN_STATS_INTERVAL (10 * 1000) -static -ECS_CTOR(EcsAlertsActive, ptr, { - ecs_map_init(&ptr->alerts, NULL); - ptr->info_count = 0; - ptr->warning_count = 0; - ptr->error_count = 0; -}) +/* Receive buffer size */ +#define ECS_HTTP_SEND_RECV_BUFFER_SIZE (16 * 1024) -static -ECS_DTOR(EcsAlertsActive, ptr, { - ecs_map_fini(&ptr->alerts); -}) +/* Max length of request (path + query + headers + body) */ +#define ECS_HTTP_REQUEST_LEN_MAX (10 * 1024 * 1024) -static -ECS_MOVE(EcsAlertsActive, dst, src, { - ecs_map_fini(&dst->alerts); - dst->alerts = src->alerts; - dst->info_count = src->info_count; - dst->warning_count = src->warning_count; - dst->error_count = src->error_count; - src->alerts = (ecs_map_t){0}; -}) +/* Total number of outstanding send requests */ +#define ECS_HTTP_SEND_QUEUE_MAX (256) -static -ECS_DTOR(EcsAlertInstance, ptr, { - ecs_os_free(ptr->message); -}) +/* Global statistics */ +int64_t ecs_http_request_received_count = 0; +int64_t ecs_http_request_invalid_count = 0; +int64_t ecs_http_request_handled_ok_count = 0; +int64_t ecs_http_request_handled_error_count = 0; +int64_t ecs_http_request_not_handled_count = 0; +int64_t ecs_http_request_preflight_count = 0; +int64_t ecs_http_send_ok_count = 0; +int64_t ecs_http_send_error_count = 0; +int64_t ecs_http_busy_count = 0; -static -ECS_MOVE(EcsAlertInstance, dst, src, { - ecs_os_free(dst->message); - dst->message = src->message; - src->message = NULL; -}) +/* Send request queue */ +typedef struct ecs_http_send_request_t { + ecs_http_socket_t sock; + char *headers; + int32_t header_length; + char *content; + int32_t content_length; +} ecs_http_send_request_t; -static -ECS_COPY(EcsAlertInstance, dst, src, { - ecs_os_free(dst->message); - dst->message = ecs_os_strdup(src->message); -}) +typedef struct ecs_http_send_queue_t { + ecs_http_send_request_t requests[ECS_HTTP_SEND_QUEUE_MAX]; + int32_t head; + int32_t tail; + ecs_os_thread_t thread; + int32_t wait_ms; +} ecs_http_send_queue_t; -static -void flecs_alerts_add_alert_to_src( - ecs_world_t *world, - ecs_entity_t source, - ecs_entity_t alert, - ecs_entity_t alert_instance) -{ - EcsAlertsActive *active = ecs_get_mut( - world, source, EcsAlertsActive); - ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); +typedef struct ecs_http_request_key_t { + const char *array; + ecs_size_t count; +} ecs_http_request_key_t; - ecs_entity_t severity = ecs_get_target(world, alert, ecs_id(EcsAlert), 0); - if (severity == EcsAlertInfo) { - active->info_count ++; - } else if (severity == EcsAlertWarning) { - active->warning_count ++; - } else if (severity == EcsAlertError) { - active->error_count ++; - } +typedef struct ecs_http_request_entry_t { + char *content; + int32_t content_length; + int code; + double time; +} ecs_http_request_entry_t; - ecs_entity_t *ptr = ecs_map_ensure(&active->alerts, alert); - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); - ptr[0] = alert_instance; - ecs_modified(world, source, EcsAlertsActive); -} +/* HTTP server struct */ +struct ecs_http_server_t { + bool should_run; + bool running; + + ecs_http_socket_t sock; + ecs_os_mutex_t lock; + ecs_os_thread_t thread; + + ecs_http_reply_action_t callback; + void *ctx; + + double cache_timeout; + double cache_purge_timeout; + + ecs_sparse_t connections; /* sparse */ + ecs_sparse_t requests; /* sparse */ + + bool initialized; + + uint16_t port; + const char *ipaddr; + + double dequeue_timeout; /* used to not lock request queue too often */ + double stats_timeout; /* used for periodic reporting of statistics */ + + double request_time; /* time spent on requests in last stats interval */ + double request_time_total; /* total time spent on requests */ + int32_t requests_processed; /* requests processed in last stats interval */ + int32_t requests_processed_total; /* total requests processed */ + int32_t dequeue_count; /* number of dequeues in last stats interval */ + ecs_http_send_queue_t send_queue; + + ecs_hashmap_t request_cache; +}; + +/** Fragment state, used by HTTP request parser */ +typedef enum { + HttpFragStateBegin, + HttpFragStateMethod, + HttpFragStatePath, + HttpFragStateVersion, + HttpFragStateHeaderStart, + HttpFragStateHeaderName, + HttpFragStateHeaderValueStart, + HttpFragStateHeaderValue, + HttpFragStateCR, + HttpFragStateCRLF, + HttpFragStateCRLFCR, + HttpFragStateBody, + HttpFragStateDone +} HttpFragState; + +/** A fragment is a partially received HTTP request */ +typedef struct { + HttpFragState state; + ecs_strbuf_t buf; + ecs_http_method_t method; + int32_t body_offset; + int32_t query_offset; + int32_t header_offsets[ECS_HTTP_HEADER_COUNT_MAX]; + int32_t header_value_offsets[ECS_HTTP_HEADER_COUNT_MAX]; + int32_t header_count; + int32_t param_offsets[ECS_HTTP_QUERY_PARAM_COUNT_MAX]; + int32_t param_value_offsets[ECS_HTTP_QUERY_PARAM_COUNT_MAX]; + int32_t param_count; + int32_t content_length; + char *header_buf_ptr; + char header_buf[32]; + bool parse_content_length; + bool invalid; +} ecs_http_fragment_t; + +/** Extend public connection type with fragment data */ +typedef struct { + ecs_http_connection_t pub; + ecs_http_socket_t sock; + + /* Connection is purged after both timeout expires and connection has + * exceeded retry count. This ensures that a connection does not immediately + * timeout when a frame takes longer than usual */ + double dequeue_timeout; + int32_t dequeue_retries; +} ecs_http_connection_impl_t; + +typedef struct { + ecs_http_request_t pub; + uint64_t conn_id; /* for sanity check */ + char *res; + int32_t req_len; +} ecs_http_request_impl_t; static -void flecs_alerts_remove_alert_from_src( - ecs_world_t *world, - ecs_entity_t source, - ecs_entity_t alert) +ecs_size_t http_send( + ecs_http_socket_t sock, + const void *buf, + ecs_size_t size, + int flags) { - EcsAlertsActive *active = ecs_get_mut( - world, source, EcsAlertsActive); - ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_map_remove(&active->alerts, alert); + ecs_assert(size >= 0, ECS_INTERNAL_ERROR, NULL); +#ifdef ECS_TARGET_POSIX + ssize_t send_bytes = send(sock, buf, flecs_itosize(size), + flags | MSG_NOSIGNAL); + return flecs_itoi32(send_bytes); +#else + int send_bytes = send(sock, buf, size, flags); + return flecs_itoi32(send_bytes); +#endif +} - ecs_entity_t severity = ecs_get_target(world, alert, ecs_id(EcsAlert), 0); - if (severity == EcsAlertInfo) { - active->info_count --; - } else if (severity == EcsAlertWarning) { - active->warning_count --; - } else if (severity == EcsAlertError) { - active->error_count --; +static +ecs_size_t http_recv( + ecs_http_socket_t sock, + void *buf, + ecs_size_t size, + int flags) +{ + ecs_size_t ret; +#ifdef ECS_TARGET_POSIX + ssize_t recv_bytes = recv(sock, buf, flecs_itosize(size), flags); + ret = flecs_itoi32(recv_bytes); +#else + int recv_bytes = recv(sock, buf, size, flags); + ret = flecs_itoi32(recv_bytes); +#endif + if (ret == -1) { + ecs_dbg("recv failed: %s (sock = %d)", ecs_os_strerror(errno), sock); + } else if (ret == 0) { + ecs_dbg("recv: received 0 bytes (sock = %d)", sock); } - if (!ecs_map_count(&active->alerts)) { - ecs_remove(world, source, EcsAlertsActive); - } else { - ecs_modified(world, source, EcsAlertsActive); - } + return ret; } static -ecs_entity_t flecs_alert_get_severity( - ecs_world_t *world, - ecs_iter_t *it, - EcsAlert *alert) +void http_sock_set_timeout( + ecs_http_socket_t sock, + int32_t timeout_ms) { - int32_t i, filter_count = ecs_vec_count(&alert->severity_filters); - ecs_alert_severity_filter_t *filters = - ecs_vec_first(&alert->severity_filters); - for (i = 0; i < filter_count; i ++) { - ecs_alert_severity_filter_t *filter = &filters[i]; - if (!filter->var) { - if (ecs_table_has_id(world, it->table, filters[i].with)) { - return filters[i].severity; - } - } else { - ecs_entity_t src = ecs_iter_get_var(it, filter->_var_index); - if (src && src != EcsWildcard) { - if (ecs_has_id(world, src, filters[i].with)) { - return filters[i].severity; - } - } - } + int r; +#ifdef ECS_TARGET_POSIX + struct timeval tv; + tv.tv_sec = timeout_ms * 1000; + tv.tv_usec = 0; + r = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv); +#else + DWORD t = (DWORD)timeout_ms; + r = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&t, sizeof t); +#endif + if (r) { + ecs_warn("http: failed to set socket timeout: %s", + ecs_os_strerror(errno)); } - - return 0; } static -ecs_entity_t flecs_alert_out_of_range_kind( - EcsAlert *alert, - const EcsMemberRanges *ranges, - const void *value_ptr) +void http_sock_keep_alive( + ecs_http_socket_t sock) { - double value = 0; - - switch(alert->kind) { - case EcsU8: value = *(const uint8_t*)value_ptr; break; - case EcsU16: value = *(const uint16_t*)value_ptr; break; - case EcsU32: value = *(const uint32_t*)value_ptr; break; - case EcsU64: value = (double)*(const uint64_t*)value_ptr; break; - case EcsI8: value = *(const int8_t*)value_ptr; break; - case EcsI16: value = *(const int16_t*)value_ptr; break; - case EcsI32: value = *(const int32_t*)value_ptr; break; - case EcsI64: value = (double)*(const int64_t*)value_ptr; break; - case EcsF32: value = (double)*(const float*)value_ptr; break; - case EcsF64: value = *(const double*)value_ptr; break; - case EcsBool: - case EcsChar: - case EcsByte: - case EcsUPtr: - case EcsIPtr: - case EcsString: - case EcsEntity: - case EcsId: - return 0; + int v = 1; + if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (const char*)&v, sizeof v)) { + ecs_warn("http: failed to set socket KEEPALIVE: %s", + ecs_os_strerror(errno)); } +} - bool has_error = ECS_NEQ(ranges->error.min, ranges->error.max); - bool has_warning = ECS_NEQ(ranges->warning.min, ranges->warning.max); - - if (has_error && (value < ranges->error.min || value > ranges->error.max)) { - return EcsAlertError; - } else if (has_warning && - (value < ranges->warning.min || value > ranges->warning.max)) - { - return EcsAlertWarning; +static +void http_sock_nonblock(ecs_http_socket_t sock, bool enable) { + (void)sock; + (void)enable; +#ifdef ECS_TARGET_POSIX + int flags; + flags = fcntl(sock,F_GETFL,0); + if (flags == -1) { + ecs_warn("http: failed to set socket NONBLOCK: %s", + ecs_os_strerror(errno)); + return; + } + if (enable) { + flags = fcntl(sock, F_SETFL, flags | O_NONBLOCK); } else { - return 0; + flags = fcntl(sock, F_SETFL, flags & ~O_NONBLOCK); } + if (flags == -1) { + ecs_warn("http: failed to set socket NONBLOCK: %s", + ecs_os_strerror(errno)); + return; + } +#endif } static -void MonitorAlerts(ecs_iter_t *it) { - ecs_world_t *world = it->real_world; - EcsAlert *alert = ecs_field(it, EcsAlert, 1); - EcsPoly *poly = ecs_field(it, EcsPoly, 2); - - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t a = it->entities[i]; /* Alert entity */ - ecs_entity_t default_severity = ecs_get_target( - world, a, ecs_id(EcsAlert), 0); - ecs_rule_t *rule = poly[i].poly; - ecs_poly_assert(rule, ecs_rule_t); +int http_getnameinfo( + const struct sockaddr* addr, + ecs_size_t addr_len, + char *host, + ecs_size_t host_len, + char *port, + ecs_size_t port_len, + int flags) +{ + ecs_assert(addr_len > 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(host_len > 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(port_len > 0, ECS_INTERNAL_ERROR, NULL); +#if defined(ECS_TARGET_WINDOWS) + return getnameinfo(addr, addr_len, host, + flecs_ito(uint32_t, host_len), port, flecs_ito(uint32_t, port_len), + flags); +#else + return getnameinfo(addr, flecs_ito(uint32_t, addr_len), host, + flecs_ito(uint32_t, host_len), port, flecs_ito(uint32_t, port_len), + flags); +#endif +} - ecs_id_t member_id = alert[i].id; - const EcsMemberRanges *ranges = NULL; - if (member_id) { - ranges = ecs_ref_get(world, &alert[i].ranges, EcsMemberRanges); - } +static +int http_bind( + ecs_http_socket_t sock, + const struct sockaddr* addr, + ecs_size_t addr_len) +{ + ecs_assert(addr_len > 0, ECS_INTERNAL_ERROR, NULL); +#if defined(ECS_TARGET_WINDOWS) + return bind(sock, addr, addr_len); +#else + return bind(sock, addr, flecs_ito(uint32_t, addr_len)); +#endif +} - ecs_iter_t rit = ecs_rule_iter(world, rule); - rit.flags |= EcsIterNoData; - rit.flags |= EcsIterIsInstanced; +static +bool http_socket_is_valid( + ecs_http_socket_t sock) +{ +#if defined(ECS_TARGET_WINDOWS) + return sock != INVALID_SOCKET; +#else + return sock >= 0; +#endif +} - while (ecs_rule_next(&rit)) { - ecs_entity_t severity = flecs_alert_get_severity( - world, &rit, &alert[i]); - if (!severity) { - severity = default_severity; - } +#if defined(ECS_TARGET_WINDOWS) +#define HTTP_SOCKET_INVALID INVALID_SOCKET +#else +#define HTTP_SOCKET_INVALID (-1) +#endif - const void *member_data = NULL; - ecs_entity_t member_src = 0; - if (ranges) { - if (alert[i].var_id) { - member_src = ecs_iter_get_var(&rit, alert[i].var_id); - if (!member_src || member_src == EcsWildcard) { - continue; - } - } - if (!member_src) { - member_data = ecs_table_get_id( - world, rit.table, member_id, rit.offset); - } else { - member_data = ecs_get_id(world, member_src, member_id); - } - if (!member_data) { - continue; - } - member_data = ECS_OFFSET(member_data, alert[i].offset); - } +static +void http_close( + ecs_http_socket_t *sock) +{ + ecs_assert(sock != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t j, alert_src_count = rit.count; - for (j = 0; j < alert_src_count; j ++) { - ecs_entity_t src_severity = severity; - ecs_entity_t e = rit.entities[j]; - if (member_data) { - ecs_entity_t range_severity = flecs_alert_out_of_range_kind( - &alert[i], ranges, member_data); - if (!member_src) { - member_data = ECS_OFFSET(member_data, alert[i].size); - } - if (!range_severity) { - continue; - } - if (range_severity < src_severity) { - /* Range severity should not exceed alert severity */ - src_severity = range_severity; - } - } +#if defined(ECS_TARGET_WINDOWS) + closesocket(*sock); +#else + ecs_dbg_2("http: closing socket %u", *sock); + shutdown(*sock, SHUT_RDWR); + close(*sock); +#endif + *sock = HTTP_SOCKET_INVALID; +} - ecs_entity_t *aptr = ecs_map_ensure(&alert[i].instances, e); - ecs_assert(aptr != NULL, ECS_INTERNAL_ERROR, NULL); - if (!aptr[0]) { - /* Alert does not yet exist for entity */ - ecs_entity_t ai = ecs_new_w_pair(world, EcsChildOf, a); - ecs_set(world, ai, EcsAlertInstance, { .message = NULL }); - ecs_set(world, ai, EcsMetricSource, { .entity = e }); - ecs_set(world, ai, EcsMetricValue, { .value = 0 }); - ecs_add_pair(world, ai, ecs_id(EcsAlert), src_severity); - if (ECS_NEQZERO(alert[i].retain_period)) { - ecs_set(world, ai, EcsAlertTimeout, { - .inactive_time = 0, - .expire_time = alert[i].retain_period - }); - } +static +ecs_http_socket_t http_accept( + ecs_http_socket_t sock, + struct sockaddr* addr, + ecs_size_t *addr_len) +{ + socklen_t len = (socklen_t)addr_len[0]; + ecs_http_socket_t result = accept(sock, addr, &len); + addr_len[0] = (ecs_size_t)len; + return result; +} - ecs_defer_suspend(it->world); - flecs_alerts_add_alert_to_src(world, e, a, ai); - ecs_defer_resume(it->world); - aptr[0] = ai; - } else { - /* Make sure alert severity is up to date */ - if (ecs_vec_count(&alert[i].severity_filters) || member_data) { - ecs_entity_t cur_severity = ecs_get_target( - world, aptr[0], ecs_id(EcsAlert), 0); - if (cur_severity != src_severity) { - ecs_add_pair(world, aptr[0], ecs_id(EcsAlert), - src_severity); - } - } - } - } - } - } +static +void http_reply_fini(ecs_http_reply_t* reply) { + ecs_assert(reply != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_os_free(reply->body.content); } static -void MonitorAlertInstances(ecs_iter_t *it) { - ecs_world_t *world = it->real_world; - EcsAlertInstance *alert_instance = ecs_field(it, EcsAlertInstance, 1); - EcsMetricSource *source = ecs_field(it, EcsMetricSource, 2); - EcsMetricValue *value = ecs_field(it, EcsMetricValue, 3); - EcsAlertTimeout *timeout = ecs_field(it, EcsAlertTimeout, 4); +void http_request_fini(ecs_http_request_impl_t *req) { + ecs_assert(req != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(req->pub.conn != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(req->pub.conn->server != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(req->pub.conn->id == req->conn_id, ECS_INTERNAL_ERROR, NULL); + ecs_os_free(req->res); + flecs_sparse_remove_t(&req->pub.conn->server->requests, + ecs_http_request_impl_t, req->pub.id); +} - /* Get alert component from alert instance parent (the alert) */ - ecs_id_t childof_pair; - if (ecs_search(world, it->table, ecs_childof(EcsWildcard), &childof_pair) == -1) { - ecs_err("alert instances must be a child of an alert"); - return; - } - ecs_entity_t parent = ecs_pair_second(world, childof_pair); - ecs_assert(parent != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ecs_has(world, parent, EcsAlert), ECS_INVALID_OPERATION, - "alert entity does not have Alert component"); - EcsAlert *alert = ecs_get_mut(world, parent, EcsAlert); - const EcsPoly *poly = ecs_get_pair(world, parent, EcsPoly, EcsQuery); - ecs_assert(poly != NULL, ECS_INVALID_OPERATION, - "alert entity does not have (Poly, Query) component"); - ecs_rule_t *rule = poly->poly; - ecs_poly_assert(rule, ecs_rule_t); +static +void http_connection_free(ecs_http_connection_impl_t *conn) { + ecs_assert(conn != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(conn->pub.id != 0, ECS_INTERNAL_ERROR, NULL); + uint64_t conn_id = conn->pub.id; - ecs_id_t member_id = alert->id; - const EcsMemberRanges *ranges = NULL; - if (member_id) { - ranges = ecs_ref_get(world, &alert->ranges, EcsMemberRanges); + if (http_socket_is_valid(conn->sock)) { + http_close(&conn->sock); } - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + flecs_sparse_remove_t(&conn->pub.server->connections, + ecs_http_connection_impl_t, conn_id); +} - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t ai = it->entities[i]; - ecs_entity_t e = source[i].entity; +// https://stackoverflow.com/questions/10156409/convert-hex-string-char-to-int +static +char http_hex_2_int(char a, char b){ + a = (a <= '9') ? (char)(a - '0') : (char)((a & 0x7) + 9); + b = (b <= '9') ? (char)(b - '0') : (char)((b & 0x7) + 9); + return (char)((a << 4) + b); +} - /* If source of alert is no longer alive, delete alert instance even if - * the alert has a retain period. */ - if (!ecs_is_alive(world, e)) { - ecs_delete(world, ai); - continue; +static +void http_decode_url_str( + char *str) +{ + char ch, *ptr, *dst = str; + for (ptr = str; (ch = *ptr); ptr++) { + if (ch == '%') { + dst[0] = http_hex_2_int(ptr[1], ptr[2]); + dst ++; + ptr += 2; + } else { + dst[0] = ptr[0]; + dst ++; } + } + dst[0] = '\0'; +} - /* Check if alert instance still matches rule */ - ecs_iter_t rit = ecs_rule_iter(world, rule); - rit.flags |= EcsIterNoData; - rit.flags |= EcsIterIsInstanced; - ecs_iter_set_var(&rit, 0, e); - - if (ecs_rule_next(&rit)) { - bool match = true; - - /* If alert is monitoring member range, test value against range */ - if (ranges) { - ecs_entity_t member_src = e; - if (alert->var_id) { - member_src = ecs_iter_get_var(&rit, alert->var_id); - } - - const void *member_data = ecs_get_id( - world, member_src, member_id); - if (!member_data) { - match = false; - } else { - member_data = ECS_OFFSET(member_data, alert->offset); - if (flecs_alert_out_of_range_kind( - alert, ranges, member_data) == 0) - { - match = false; - } - } - } - - if (match) { - /* Only increase alert duration if the alert was active */ - value[i].value += (double)it->delta_system_time; - - bool generate_message = alert->message; - if (generate_message) { - if (alert_instance[i].message) { - /* If a message was already generated, only regenerate if - * rule has multiple variables. Variable values could have - * changed, this ensures the message remains up to date. */ - generate_message = rit.variable_count > 1; - } - } - - if (generate_message) { - if (alert_instance[i].message) { - ecs_os_free(alert_instance[i].message); - } - - ecs_iter_to_vars(&rit, &vars, 0); - alert_instance[i].message = ecs_interpolate_string( - world, alert->message, &vars); - } - - if (timeout) { - if (ECS_NEQZERO(timeout[i].inactive_time)) { - /* The alert just became active. Remove Disabled tag */ - flecs_alerts_add_alert_to_src(world, e, parent, ai); - ecs_remove_id(world, ai, EcsDisabled); - } - timeout[i].inactive_time = 0; - } - - /* Alert instance still matches rule, keep it alive */ - ecs_iter_fini(&rit); - continue; - } - - ecs_iter_fini(&rit); - } - - /* Alert instance is no longer active */ - - if (timeout) { - if (ECS_EQZERO(timeout[i].inactive_time)) { - /* The alert just became inactive. Add Disabled tag */ - flecs_alerts_remove_alert_from_src(world, e, parent); - ecs_add_id(world, ai, EcsDisabled); - } - ecs_ftime_t t = timeout[i].inactive_time; - timeout[i].inactive_time += it->delta_system_time; - if (t < timeout[i].expire_time) { - /* Alert instance no longer matches rule, but is still - * within the timeout period. Keep it alive. */ - continue; - } - } - - /* Alert instance no longer matches rule, remove it */ - flecs_alerts_remove_alert_from_src(world, e, parent); - ecs_map_remove(&alert->instances, e); - ecs_delete(world, ai); +static +void http_parse_method( + ecs_http_fragment_t *frag) +{ + char *method = ecs_strbuf_get_small(&frag->buf); + if (!ecs_os_strcmp(method, "GET")) frag->method = EcsHttpGet; + else if (!ecs_os_strcmp(method, "POST")) frag->method = EcsHttpPost; + else if (!ecs_os_strcmp(method, "PUT")) frag->method = EcsHttpPut; + else if (!ecs_os_strcmp(method, "DELETE")) frag->method = EcsHttpDelete; + else if (!ecs_os_strcmp(method, "OPTIONS")) frag->method = EcsHttpOptions; + else { + frag->method = EcsHttpMethodUnsupported; + frag->invalid = true; } + ecs_strbuf_reset(&frag->buf); +} - ecs_vars_fini(&vars); +static +bool http_header_writable( + ecs_http_fragment_t *frag) +{ + return frag->header_count < ECS_HTTP_HEADER_COUNT_MAX; } -ecs_entity_t ecs_alert_init( - ecs_world_t *world, - const ecs_alert_desc_t *desc) +static +void http_header_buf_reset( + ecs_http_fragment_t *frag) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(!desc->filter.entity || desc->entity == desc->filter.entity, - ECS_INVALID_PARAMETER, NULL); + frag->header_buf[0] = '\0'; + frag->header_buf_ptr = frag->header_buf; +} - ecs_entity_t result = desc->entity; - if (!result) { - result = ecs_new(world, 0); +static +void http_header_buf_append( + ecs_http_fragment_t *frag, + char ch) +{ + if ((frag->header_buf_ptr - frag->header_buf) < + ECS_SIZEOF(frag->header_buf)) + { + frag->header_buf_ptr[0] = ch; + frag->header_buf_ptr ++; + } else { + frag->header_buf_ptr[0] = '\0'; } +} - ecs_filter_desc_t private_desc = desc->filter; - private_desc.entity = result; - - ecs_rule_t *rule = ecs_rule_init(world, &private_desc); - if (!rule) { - ecs_err("failed to create alert filter"); - return 0; - } +static +uint64_t http_request_key_hash(const void *ptr) { + const ecs_http_request_key_t *key = ptr; + const char *array = key->array; + int32_t count = key->count; + return flecs_hash(array, count * ECS_SIZEOF(char)); +} - const ecs_filter_t *filter = ecs_rule_get_filter(rule); - if (!(filter->flags & EcsFilterMatchThis)) { - ecs_err("alert filter must have at least one '$this' term"); - ecs_rule_fini(rule); - return 0; - } +static +int http_request_key_compare(const void *ptr_1, const void *ptr_2) { + const ecs_http_request_key_t *type_1 = ptr_1; + const ecs_http_request_key_t *type_2 = ptr_2; - /* Initialize Alert component which identifiers entity as alert */ - EcsAlert *alert = ecs_get_mut(world, result, EcsAlert); - ecs_assert(alert != NULL, ECS_INTERNAL_ERROR, NULL); - alert->message = ecs_os_strdup(desc->message); - alert->retain_period = desc->retain_period; + int32_t count_1 = type_1->count; + int32_t count_2 = type_2->count; - /* Initialize severity filters */ - int32_t i; - for (i = 0; i < 4; i ++) { - if (desc->severity_filters[i].with) { - if (!desc->severity_filters[i].severity) { - ecs_err("severity filter must have severity"); - goto error; - } - ecs_alert_severity_filter_t *sf = ecs_vec_append_t(NULL, - &alert->severity_filters, ecs_alert_severity_filter_t); - *sf = desc->severity_filters[i]; - if (sf->var) { - sf->_var_index = ecs_rule_find_var(rule, sf->var); - if (sf->_var_index == -1) { - ecs_err("unresolved variable '%s' in alert severity filter", - sf->var); - goto error; - } - } - } + if (count_1 != count_2) { + return (count_1 > count_2) - (count_1 < count_2); } - /* Fetch data for member monitoring */ - if (desc->member) { - alert->member = desc->member; - if (!desc->id) { - alert->id = ecs_get_parent(world, desc->member); - if (!alert->id) { - ecs_err("ecs_alert_desc_t::member is not a member"); - goto error; - } - ecs_check(alert->id != 0, ECS_INVALID_PARAMETER, NULL); - } else { - alert->id = desc->id; - } - - ecs_id_record_t *idr = flecs_id_record_ensure(world, alert->id); - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - if (!idr->type_info) { - ecs_err("ecs_alert_desc_t::id must be a component"); - goto error; - } - - ecs_entity_t type = idr->type_info->component; - if (type != ecs_get_parent(world, desc->member)) { - char *type_name = ecs_get_fullpath(world, type); - ecs_err("member '%s' is not a member of '%s'", - ecs_get_name(world, desc->member), type_name); - ecs_os_free(type_name); - goto error; - } + return ecs_os_memcmp(type_1->array, type_2->array, count_1); +} - const EcsMember *member = ecs_get(world, alert->member, EcsMember); - if (!member) { - ecs_err("ecs_alert_desc_t::member is not a member"); - goto error; - } - if (!member->type) { - ecs_err("ecs_alert_desc_t::member must have a type"); - goto error; - } - - const EcsPrimitive *pr = ecs_get(world, member->type, EcsPrimitive); - if (!pr) { - ecs_err("ecs_alert_desc_t::member must be of a primitive type"); - goto error; - } +static +ecs_http_request_entry_t* http_find_request_entry( + ecs_http_server_t *srv, + const char *array, + int32_t count) +{ + ecs_http_request_key_t key; + key.array = array; + key.count = count; - if (!ecs_has(world, desc->member, EcsMemberRanges)) { - ecs_err("ecs_alert_desc_t::member must have warning/error ranges"); - goto error; - } + ecs_time_t t = {0, 0}; + ecs_http_request_entry_t *entry = flecs_hashmap_get( + &srv->request_cache, &key, ecs_http_request_entry_t); - int32_t var_id = 0; - if (desc->var) { - var_id = ecs_rule_find_var(rule, desc->var); - if (var_id == -1) { - ecs_err("unresolved variable '%s' in alert member", desc->var); - goto error; - } + if (entry) { + double tf = ecs_time_measure(&t); + if ((tf - entry->time) < srv->cache_timeout) { + return entry; } - - alert->offset = member->offset; - alert->size = idr->type_info->size; - alert->kind = pr->kind; - alert->ranges = ecs_ref_init(world, desc->member, EcsMemberRanges); - alert->var_id = var_id; - } - - ecs_modified(world, result, EcsAlert); - - /* Register alert as metric */ - ecs_add(world, result, EcsMetric); - ecs_add_pair(world, result, EcsMetric, EcsCounter); - - /* Add severity to alert */ - ecs_entity_t severity = desc->severity; - if (!severity) { - severity = EcsAlertError; } + return NULL; +} - ecs_add_pair(world, result, ecs_id(EcsAlert), severity); - - if (desc->doc_name) { -#ifdef FLECS_DOC - ecs_doc_set_name(world, result, desc->doc_name); -#else - ecs_err("cannot set doc_name for alert, requires FLECS_DOC addon"); - goto error; -#endif +static +void http_insert_request_entry( + ecs_http_server_t *srv, + ecs_http_request_impl_t *req, + ecs_http_reply_t *reply) +{ + int32_t content_length = ecs_strbuf_written(&reply->body); + if (!content_length) { + return; } - if (desc->brief) { -#ifdef FLECS_DOC - ecs_doc_set_brief(world, result, desc->brief); -#else - ecs_err("cannot set brief for alert, requires FLECS_DOC addon"); - goto error; -#endif + ecs_http_request_key_t key; + key.array = req->res; + key.count = req->req_len; + ecs_http_request_entry_t *entry = flecs_hashmap_get( + &srv->request_cache, &key, ecs_http_request_entry_t); + if (!entry) { + flecs_hashmap_result_t elem = flecs_hashmap_ensure( + &srv->request_cache, &key, ecs_http_request_entry_t); + ecs_http_request_key_t *elem_key = elem.key; + elem_key->array = ecs_os_memdup_n(key.array, char, key.count); + entry = elem.value; + } else { + ecs_os_free(entry->content); } - return result; -error: - if (result) { - ecs_delete(world, result); - } - return 0; + ecs_time_t t = {0, 0}; + entry->time = ecs_time_measure(&t); + entry->content_length = ecs_strbuf_written(&reply->body); + entry->content = ecs_strbuf_get(&reply->body); + entry->code = reply->code; + ecs_strbuf_appendstrn(&reply->body, + entry->content, entry->content_length); } -int32_t ecs_get_alert_count( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t alert) +static +char* http_decode_request( + ecs_http_request_impl_t *req, + ecs_http_fragment_t *frag) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(!alert || ecs_has(world, alert, EcsAlert), - ECS_INVALID_PARAMETER, NULL); - - const EcsAlertsActive *active = ecs_get(world, entity, EcsAlertsActive); - if (!active) { - return 0; - } + ecs_os_zeromem(req); - if (alert) { - return ecs_map_get(&active->alerts, alert) != NULL; + ecs_size_t req_len = frag->buf.length; + char *res = ecs_strbuf_get(&frag->buf); + if (!res) { + return NULL; } - return ecs_map_count(&active->alerts); -error: - return 0; -} - -ecs_entity_t ecs_get_alert( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t alert) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(alert != 0, ECS_INVALID_PARAMETER, NULL); + req->pub.method = frag->method; + req->pub.path = res + 1; + http_decode_url_str(req->pub.path); - const EcsAlertsActive *active = ecs_get(world, entity, EcsAlertsActive); - if (!active) { - return 0; + if (frag->body_offset) { + req->pub.body = &res[frag->body_offset]; + } + int32_t i, count = frag->header_count; + for (i = 0; i < count; i ++) { + req->pub.headers[i].key = &res[frag->header_offsets[i]]; + req->pub.headers[i].value = &res[frag->header_value_offsets[i]]; + } + count = frag->param_count; + for (i = 0; i < count; i ++) { + req->pub.params[i].key = &res[frag->param_offsets[i]]; + req->pub.params[i].value = &res[frag->param_value_offsets[i]]; + /* Safe, member is only const so that end-user can't change it */ + http_decode_url_str(ECS_CONST_CAST(char*, req->pub.params[i].value)); } - ecs_entity_t *ptr = ecs_map_get(&active->alerts, alert); - if (ptr) { - return ptr[0]; + req->pub.header_count = frag->header_count; + req->pub.param_count = frag->param_count; + req->res = res; + req->req_len = frag->header_offsets[0]; + if (!req->req_len) { + req->req_len = req_len; } -error: - return 0; + return res; } -void FlecsAlertsImport(ecs_world_t *world) { - ECS_MODULE_DEFINE(world, FlecsAlerts); - - ECS_IMPORT(world, FlecsPipeline); - ECS_IMPORT(world, FlecsTimer); - ECS_IMPORT(world, FlecsMetrics); -#ifdef FLECS_DOC - ECS_IMPORT(world, FlecsDoc); -#endif - - ecs_set_name_prefix(world, "Ecs"); - ECS_COMPONENT_DEFINE(world, EcsAlert); - ecs_remove_pair(world, ecs_id(EcsAlert), ecs_id(EcsIdentifier), EcsSymbol); - ECS_COMPONENT_DEFINE(world, EcsAlertsActive); - - ecs_set_name_prefix(world, "EcsAlert"); - ECS_COMPONENT_DEFINE(world, EcsAlertInstance); - ECS_COMPONENT_DEFINE(world, EcsAlertTimeout); - - ECS_TAG_DEFINE(world, EcsAlertInfo); - ECS_TAG_DEFINE(world, EcsAlertWarning); - ECS_TAG_DEFINE(world, EcsAlertError); - ECS_TAG_DEFINE(world, EcsAlertCritical); - - ecs_add_id(world, ecs_id(EcsAlert), EcsTag); - ecs_add_id(world, ecs_id(EcsAlert), EcsExclusive); - ecs_add_id(world, ecs_id(EcsAlertsActive), EcsPrivate); - - ecs_struct(world, { - .entity = ecs_id(EcsAlertInstance), - .members = { - { .name = "message", .type = ecs_id(ecs_string_t) } - } - }); +static +ecs_http_request_entry_t* http_enqueue_request( + ecs_http_connection_impl_t *conn, + uint64_t conn_id, + ecs_http_fragment_t *frag) +{ + ecs_http_server_t *srv = conn->pub.server; - ecs_set_hooks(world, EcsAlert, { - .ctor = ecs_ctor(EcsAlert), - .dtor = ecs_dtor(EcsAlert), - .move = ecs_move(EcsAlert) - }); + ecs_os_mutex_lock(srv->lock); + bool is_alive = conn->pub.id == conn_id; - ecs_set_hooks(world, EcsAlertsActive, { - .ctor = ecs_ctor(EcsAlertsActive), - .dtor = ecs_dtor(EcsAlertsActive), - .move = ecs_move(EcsAlertsActive) - }); + if (!is_alive || frag->invalid) { + /* Don't enqueue invalid requests or requests for purged connections */ + ecs_strbuf_reset(&frag->buf); + } else { + ecs_http_request_impl_t req; + char *res = http_decode_request(&req, frag); + if (res) { + req.pub.conn = (ecs_http_connection_t*)conn; - ecs_set_hooks(world, EcsAlertInstance, { - .ctor = ecs_default_ctor, - .dtor = ecs_dtor(EcsAlertInstance), - .move = ecs_move(EcsAlertInstance), - .copy = ecs_copy(EcsAlertInstance) - }); + /* Check cache for GET requests */ + if (frag->method == EcsHttpGet) { + ecs_http_request_entry_t *entry = + http_find_request_entry(srv, res, frag->header_offsets[0]); + if (entry) { + /* If an entry is found, don't enqueue a request. Instead + * return the cached response immediately. */ + ecs_os_free(res); + return entry; + } + } - ecs_struct(world, { - .entity = ecs_id(EcsAlertsActive), - .members = { - { .name = "info_count", .type = ecs_id(ecs_i32_t) }, - { .name = "warning_count", .type = ecs_id(ecs_i32_t) }, - { .name = "error_count", .type = ecs_id(ecs_i32_t) } + ecs_http_request_impl_t *req_ptr = flecs_sparse_add_t( + &srv->requests, ecs_http_request_impl_t); + *req_ptr = req; + req_ptr->pub.id = flecs_sparse_last_id(&srv->requests); + req_ptr->conn_id = conn->pub.id; + ecs_os_linc(&ecs_http_request_received_count); } - }); - - ECS_SYSTEM(world, MonitorAlerts, EcsPreStore, Alert, (Poly, Query)); - ECS_SYSTEM(world, MonitorAlertInstances, EcsOnStore, Instance, - flecs.metrics.Source, flecs.metrics.Value, ?EcsAlertTimeout, ?Disabled); - - ecs_system(world, { - .entity = ecs_id(MonitorAlerts), - .no_readonly = true, - .interval = 0.5 - }); + } - ecs_system(world, { - .entity = ecs_id(MonitorAlertInstances), - .interval = 0.5 - }); + ecs_os_mutex_unlock(srv->lock); + return NULL; } -#endif - -/** - * @file addons/app.c - * @brief App addon. - */ - - -#ifdef FLECS_APP - static -int flecs_default_run_action( - ecs_world_t *world, - ecs_app_desc_t *desc) +bool http_parse_request( + ecs_http_fragment_t *frag, + const char* req_frag, + ecs_size_t req_frag_len) { - if (desc->init) { - desc->init(world); - } + int32_t i; + for (i = 0; i < req_frag_len; i++) { + char c = req_frag[i]; + switch (frag->state) { + case HttpFragStateBegin: + ecs_os_memset_t(frag, 0, ecs_http_fragment_t); + frag->state = HttpFragStateMethod; + frag->header_buf_ptr = frag->header_buf; - int result = 0; - if (desc->frames) { - int32_t i; - for (i = 0; i < desc->frames; i ++) { - if ((result = ecs_app_run_frame(world, desc)) != 0) { - break; + /* fall through */ + case HttpFragStateMethod: + if (c == ' ') { + http_parse_method(frag); + ecs_strbuf_reset(&frag->buf); + frag->state = HttpFragStatePath; + frag->buf.content = NULL; + } else { + ecs_strbuf_appendch(&frag->buf, c); } - } - } else { - while ((result = ecs_app_run_frame(world, desc)) == 0) { } - } - - /* Ensure quit flag is set on world, which can be used to determine if - * world needs to be cleaned up. */ -#ifndef __EMSCRIPTEN__ - ecs_quit(world); -#endif + break; + case HttpFragStatePath: + if (c == ' ') { + frag->state = HttpFragStateVersion; + ecs_strbuf_appendch(&frag->buf, '\0'); + } else { + if (c == '?' || c == '=' || c == '&') { + ecs_strbuf_appendch(&frag->buf, '\0'); + int32_t offset = ecs_strbuf_written(&frag->buf); + if (c == '?' || c == '&') { + frag->param_offsets[frag->param_count] = offset; + } else { + frag->param_value_offsets[frag->param_count] = offset; + frag->param_count ++; + } + } else { + ecs_strbuf_appendch(&frag->buf, c); + } + } + break; + case HttpFragStateVersion: + if (c == '\r') { + frag->state = HttpFragStateCR; + } /* version is not stored */ + break; + case HttpFragStateHeaderStart: + if (http_header_writable(frag)) { + frag->header_offsets[frag->header_count] = + ecs_strbuf_written(&frag->buf); + } + http_header_buf_reset(frag); + frag->state = HttpFragStateHeaderName; - if (result == 1) { - return 0; /* Normal exit */ + /* fall through */ + case HttpFragStateHeaderName: + if (c == ':') { + frag->state = HttpFragStateHeaderValueStart; + http_header_buf_append(frag, '\0'); + frag->parse_content_length = !ecs_os_strcmp( + frag->header_buf, "Content-Length"); + + if (http_header_writable(frag)) { + ecs_strbuf_appendch(&frag->buf, '\0'); + frag->header_value_offsets[frag->header_count] = + ecs_strbuf_written(&frag->buf); + } + } else if (c == '\r') { + frag->state = HttpFragStateCR; + } else { + http_header_buf_append(frag, c); + if (http_header_writable(frag)) { + ecs_strbuf_appendch(&frag->buf, c); + } + } + break; + case HttpFragStateHeaderValueStart: + http_header_buf_reset(frag); + frag->state = HttpFragStateHeaderValue; + if (c == ' ') { /* skip first space */ + break; + } + + /* fall through */ + case HttpFragStateHeaderValue: + if (c == '\r') { + if (frag->parse_content_length) { + http_header_buf_append(frag, '\0'); + int32_t len = atoi(frag->header_buf); + if (len < 0) { + frag->invalid = true; + } else { + frag->content_length = len; + } + frag->parse_content_length = false; + } + if (http_header_writable(frag)) { + int32_t cur = ecs_strbuf_written(&frag->buf); + if (frag->header_offsets[frag->header_count] < cur && + frag->header_value_offsets[frag->header_count] < cur) + { + ecs_strbuf_appendch(&frag->buf, '\0'); + frag->header_count ++; + } + } + frag->state = HttpFragStateCR; + } else { + if (frag->parse_content_length) { + http_header_buf_append(frag, c); + } + if (http_header_writable(frag)) { + ecs_strbuf_appendch(&frag->buf, c); + } + } + break; + case HttpFragStateCR: + if (c == '\n') { + frag->state = HttpFragStateCRLF; + } else { + frag->state = HttpFragStateHeaderStart; + } + break; + case HttpFragStateCRLF: + if (c == '\r') { + frag->state = HttpFragStateCRLFCR; + } else { + frag->state = HttpFragStateHeaderStart; + i--; + } + break; + case HttpFragStateCRLFCR: + if (c == '\n') { + if (frag->content_length != 0) { + frag->body_offset = ecs_strbuf_written(&frag->buf); + frag->state = HttpFragStateBody; + } else { + frag->state = HttpFragStateDone; + } + } else { + frag->state = HttpFragStateHeaderStart; + } + break; + case HttpFragStateBody: { + ecs_strbuf_appendch(&frag->buf, c); + if ((ecs_strbuf_written(&frag->buf) - frag->body_offset) == + frag->content_length) + { + frag->state = HttpFragStateDone; + } + } + break; + case HttpFragStateDone: + break; + } + } + + if (frag->state == HttpFragStateDone) { + return true; } else { - return result; /* Error code */ + return false; } } static -int flecs_default_frame_action( - ecs_world_t *world, - const ecs_app_desc_t *desc) +ecs_http_send_request_t* http_send_queue_post( + ecs_http_server_t *srv) { - return !ecs_progress(world, desc->delta_time); + /* This function should only be called while the server is locked. Before + * the lock is released, the returned element should be populated. */ + ecs_http_send_queue_t *sq = &srv->send_queue; + int32_t next = (sq->head + 1) % ECS_HTTP_SEND_QUEUE_MAX; + if (next == sq->tail) { + return NULL; + } + + /* Don't enqueue new requests if server is shutting down */ + if (!srv->should_run) { + return NULL; + } + + /* Return element at end of the queue */ + ecs_http_send_request_t *result = &sq->requests[sq->head]; + sq->head = next; + return result; } -static ecs_app_run_action_t run_action = flecs_default_run_action; -static ecs_app_frame_action_t frame_action = flecs_default_frame_action; -static ecs_app_desc_t ecs_app_desc; +static +ecs_http_send_request_t* http_send_queue_get( + ecs_http_server_t *srv) +{ + ecs_os_mutex_lock(srv->lock); + ecs_http_send_queue_t *sq = &srv->send_queue; + if (sq->tail == sq->head) { + return NULL; + } -/* Serve REST API from wasm image when running in emscripten */ -#ifdef ECS_TARGET_EM -#include + int32_t next = (sq->tail + 1) % ECS_HTTP_SEND_QUEUE_MAX; + ecs_http_send_request_t *result = &sq->requests[sq->tail]; + sq->tail = next; + return result; +} -ecs_http_server_t *flecs_wasm_rest_server; +static +void* http_server_send_queue(void* arg) { + ecs_http_server_t *srv = arg; + int32_t wait_ms = srv->send_queue.wait_ms; -EMSCRIPTEN_KEEPALIVE -char* flecs_explorer_request(const char *method, char *request) { - ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; - ecs_http_server_request(flecs_wasm_rest_server, method, request, &reply); - if (reply.code == 200) { - return ecs_strbuf_get(&reply.body); - } else { - char *body = ecs_strbuf_get(&reply.body); - if (body) { - return body; + /* Run for as long as the server is running or there are messages. When the + * server is stopping, no new messages will be enqueued */ + while (srv->should_run || (srv->send_queue.head != srv->send_queue.tail)) { + ecs_http_send_request_t* r = http_send_queue_get(srv); + if (!r) { + ecs_os_mutex_unlock(srv->lock); + /* If the queue is empty, wait so we don't run too fast */ + if (srv->should_run) { + ecs_os_sleep(0, wait_ms * 1000 * 1000); + } } else { - return ecs_asprintf( - "{\"error\": \"bad request (code %d)\"}", reply.code); + ecs_http_socket_t sock = r->sock; + char *headers = r->headers; + int32_t headers_length = r->header_length; + char *content = r->content; + int32_t content_length = r->content_length; + ecs_os_mutex_unlock(srv->lock); + + if (http_socket_is_valid(sock)) { + bool error = false; + + http_sock_nonblock(sock, false); + + /* Write headers */ + ecs_size_t written = http_send(sock, headers, headers_length, 0); + if (written != headers_length) { + ecs_err("http: failed to write HTTP response headers: %s", + ecs_os_strerror(errno)); + ecs_os_linc(&ecs_http_send_error_count); + error = true; + } else if (content_length >= 0) { + /* Write content */ + written = http_send(sock, content, content_length, 0); + if (written != content_length) { + ecs_err("http: failed to write HTTP response body: %s", + ecs_os_strerror(errno)); + ecs_os_linc(&ecs_http_send_error_count); + error = true; + } + } + if (!error) { + ecs_os_linc(&ecs_http_send_ok_count); + } + + http_close(&sock); + } else { + ecs_err("http: invalid socket\n"); + } + + ecs_os_free(content); + ecs_os_free(headers); } } + return NULL; } -#endif -int ecs_app_run( - ecs_world_t *world, - ecs_app_desc_t *desc) +static +void http_append_send_headers( + ecs_strbuf_t *hdrs, + int code, + const char* status, + const char* content_type, + ecs_strbuf_t *extra_headers, + ecs_size_t content_len, + bool preflight) { - ecs_app_desc = *desc; + ecs_strbuf_appendlit(hdrs, "HTTP/1.1 "); + ecs_strbuf_appendint(hdrs, code); + ecs_strbuf_appendch(hdrs, ' '); + ecs_strbuf_appendstr(hdrs, status); + ecs_strbuf_appendlit(hdrs, "\r\n"); - /* Don't set FPS & threads if custom run action is set, as the platform on - * which the app is running may not support it. */ - if (run_action == flecs_default_run_action) { - if (ECS_NEQZERO(ecs_app_desc.target_fps)) { - ecs_set_target_fps(world, ecs_app_desc.target_fps); - } - if (ecs_app_desc.threads) { - ecs_set_threads(world, ecs_app_desc.threads); - } + if (content_type) { + ecs_strbuf_appendlit(hdrs, "Content-Type: "); + ecs_strbuf_appendstr(hdrs, content_type); + ecs_strbuf_appendlit(hdrs, "\r\n"); } - /* REST server enables connecting to app with explorer */ - if (desc->enable_rest) { -#ifdef FLECS_REST -#ifdef ECS_TARGET_EM - flecs_wasm_rest_server = ecs_rest_server_init(world, NULL); -#else - ecs_set(world, EcsWorld, EcsRest, {.port = desc->port }); -#endif -#else - ecs_warn("cannot enable remote API, REST addon not available"); -#endif + if (content_len >= 0) { + ecs_strbuf_appendlit(hdrs, "Content-Length: "); + ecs_strbuf_append(hdrs, "%d", content_len); + ecs_strbuf_appendlit(hdrs, "\r\n"); } - /* Monitoring periodically collects statistics */ - if (desc->enable_monitor) { -#ifdef FLECS_MONITOR - ECS_IMPORT(world, FlecsMonitor); -#else - ecs_warn("cannot enable monitoring, MONITOR addon not available"); -#endif + ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Origin: *\r\n"); + if (preflight) { + ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Private-Network: true\r\n"); + ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Methods: GET, PUT, DELETE, OPTIONS\r\n"); + ecs_strbuf_appendlit(hdrs, "Access-Control-Max-Age: 600\r\n"); } - return run_action(world, &ecs_app_desc); -} + ecs_strbuf_mergebuff(hdrs, extra_headers); -int ecs_app_run_frame( - ecs_world_t *world, - const ecs_app_desc_t *desc) -{ - return frame_action(world, desc); + ecs_strbuf_appendlit(hdrs, "\r\n"); } -int ecs_app_set_run_action( - ecs_app_run_action_t callback) +static +void http_send_reply( + ecs_http_connection_impl_t* conn, + ecs_http_reply_t* reply, + bool preflight) { - if (run_action != flecs_default_run_action && run_action != callback) { - ecs_err("run action already set"); - return -1; + ecs_strbuf_t hdrs = ECS_STRBUF_INIT; + int32_t content_length = reply->body.length; + char *content = ecs_strbuf_get(&reply->body); + + /* Use asynchronous send queue for outgoing data so send operations won't + * hold up main thread */ + ecs_http_send_request_t *req = NULL; + + if (!preflight) { + req = http_send_queue_post(conn->pub.server); + if (!req) { + reply->code = 503; /* queue full, server is busy */ + ecs_os_linc(&ecs_http_busy_count); + } } - run_action = callback; + http_append_send_headers(&hdrs, reply->code, reply->status, + reply->content_type, &reply->headers, content_length, preflight); + ecs_size_t headers_length = ecs_strbuf_written(&hdrs); + char *headers = ecs_strbuf_get(&hdrs); - return 0; + if (!req) { + ecs_size_t written = http_send(conn->sock, headers, headers_length, 0); + if (written != headers_length) { + ecs_err("http: failed to send reply to '%s:%s': %s", + conn->pub.host, conn->pub.port, ecs_os_strerror(errno)); + ecs_os_linc(&ecs_http_send_error_count); + } + ecs_os_free(content); + ecs_os_free(headers); + http_close(&conn->sock); + return; + } + + /* Second, enqueue send request for response body */ + req->sock = conn->sock; + req->headers = headers; + req->header_length = headers_length; + req->content = content; + req->content_length = content_length; + + /* Take ownership of values */ + reply->body.content = NULL; + conn->sock = HTTP_SOCKET_INVALID; } -int ecs_app_set_frame_action( - ecs_app_frame_action_t callback) +static +void http_recv_connection( + ecs_http_server_t *srv, + ecs_http_connection_impl_t *conn, + uint64_t conn_id, + ecs_http_socket_t sock) { - if (frame_action != flecs_default_frame_action && frame_action != callback) { - ecs_err("frame action already set"); - return -1; - } + ecs_size_t bytes_read; + char recv_buf[ECS_HTTP_SEND_RECV_BUFFER_SIZE]; + ecs_http_fragment_t frag = {0}; + int32_t retries = 0; - frame_action = callback; + do { + if ((bytes_read = http_recv( + sock, recv_buf, ECS_SIZEOF(recv_buf), 0)) > 0) + { + bool is_alive = conn->pub.id == conn_id; + if (!is_alive) { + /* Connection has been purged by main thread */ + goto done; + } - return 0; -} + if (http_parse_request(&frag, recv_buf, bytes_read)) { + if (frag.method == EcsHttpOptions) { + ecs_http_reply_t reply; + reply.body = ECS_STRBUF_INIT; + reply.code = 200; + reply.content_type = NULL; + reply.headers = ECS_STRBUF_INIT; + reply.status = "OK"; + http_send_reply(conn, &reply, true); + ecs_os_linc(&ecs_http_request_preflight_count); + } else { + ecs_http_request_entry_t *entry = + http_enqueue_request(conn, conn_id, &frag); + if (entry) { + ecs_http_reply_t reply; + reply.body = ECS_STRBUF_INIT; + reply.code = entry->code; + reply.content_type = "application/json"; + reply.headers = ECS_STRBUF_INIT; + reply.status = "OK"; + ecs_strbuf_appendstrn(&reply.body, + entry->content, entry->content_length); + http_send_reply(conn, &reply, false); + http_connection_free(conn); -#endif + /* Lock was transferred from enqueue_request */ + ecs_os_mutex_unlock(srv->lock); + } + } + } else { + ecs_os_linc(&ecs_http_request_invalid_count); + } + } -/** - * @file addons/coredoc.c - * @brief Core doc addon. - */ + ecs_os_sleep(0, 10 * 1000 * 1000); + } while ((bytes_read == -1) && (++retries < ECS_HTTP_REQUEST_RECV_RETRY)); + if (retries == ECS_HTTP_REQUEST_RECV_RETRY) { + http_close(&sock); + } -#ifdef FLECS_COREDOC +done: + ecs_strbuf_reset(&frag.buf); +} -#define URL_ROOT "https://www.flecs.dev/flecs/md_docs_Relationships.html/" +typedef struct { + ecs_http_connection_impl_t *conn; + uint64_t id; +} http_conn_res_t; -void FlecsCoreDocImport( - ecs_world_t *world) -{ - ECS_MODULE(world, FlecsCoreDoc); +static +http_conn_res_t http_init_connection( + ecs_http_server_t *srv, + ecs_http_socket_t sock_conn, + struct sockaddr_storage *remote_addr, + ecs_size_t remote_addr_len) +{ + http_sock_set_timeout(sock_conn, 100); + http_sock_keep_alive(sock_conn); + http_sock_nonblock(sock_conn, true); - ECS_IMPORT(world, FlecsMeta); - ECS_IMPORT(world, FlecsDoc); + /* Create new connection */ + ecs_os_mutex_lock(srv->lock); + ecs_http_connection_impl_t *conn = flecs_sparse_add_t( + &srv->connections, ecs_http_connection_impl_t); + uint64_t conn_id = conn->pub.id = flecs_sparse_last_id(&srv->connections); + conn->pub.server = srv; + conn->sock = sock_conn; + ecs_os_mutex_unlock(srv->lock); - ecs_set_name_prefix(world, "Ecs"); + char *remote_host = conn->pub.host; + char *remote_port = conn->pub.port; - /* Initialize reflection data for core components */ + /* Fetch name & port info */ + if (http_getnameinfo((struct sockaddr*) remote_addr, remote_addr_len, + remote_host, ECS_SIZEOF(conn->pub.host), + remote_port, ECS_SIZEOF(conn->pub.port), + NI_NUMERICHOST | NI_NUMERICSERV)) + { + ecs_os_strcpy(remote_host, "unknown"); + ecs_os_strcpy(remote_port, "unknown"); + } - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsComponent), - .members = { - {.name = "size", .type = ecs_id(ecs_i32_t)}, - {.name = "alignment", .type = ecs_id(ecs_i32_t)} - } - }); + ecs_dbg_2("http: connection established from '%s:%s' (socket %u)", + remote_host, remote_port, sock_conn); + + return (http_conn_res_t){ .conn = conn, .id = conn_id }; +} - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsDocDescription), - .members = { - {.name = "value", .type = ecs_id(ecs_string_t)} +static +int http_accept_connections( + ecs_http_server_t* srv, + const struct sockaddr* addr, + ecs_size_t addr_len) +{ +#ifdef ECS_TARGET_WINDOWS + /* If on Windows, test if winsock needs to be initialized */ + SOCKET testsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (INVALID_SOCKET == testsocket && WSANOTINITIALISED == WSAGetLastError()){ + WSADATA data = { 0 }; + int result = WSAStartup(MAKEWORD(2, 2), &data); + if (result) { + ecs_warn("http: WSAStartup failed with GetLastError = %d\n", + GetLastError()); + return 0; } - }); + } else { + http_close(&testsocket); + } +#endif - /* Initialize documentation data for core components */ - ecs_doc_set_brief(world, EcsFlecs, "Flecs root module"); - ecs_doc_set_link(world, EcsFlecs, "https://github.com/SanderMertens/flecs"); + /* Resolve name + port (used for logging) */ + char addr_host[256]; + char addr_port[20]; - ecs_doc_set_brief(world, EcsFlecsCore, "Flecs module with builtin components"); + int ret = 0; /* 0 = ok, 1 = port occupied */ - ecs_doc_set_brief(world, EcsWorld, "Entity associated with world"); + ecs_http_socket_t sock = HTTP_SOCKET_INVALID; + ecs_assert(srv->sock == HTTP_SOCKET_INVALID, ECS_INTERNAL_ERROR, NULL); - ecs_doc_set_brief(world, ecs_id(EcsComponent), "Component that is added to all components"); - ecs_doc_set_brief(world, EcsModule, "Tag that is added to modules"); - ecs_doc_set_brief(world, EcsPrefab, "Tag that is added to prefabs"); - ecs_doc_set_brief(world, EcsDisabled, "Tag that is added to disabled entities"); + if (http_getnameinfo( + addr, addr_len, addr_host, ECS_SIZEOF(addr_host), addr_port, + ECS_SIZEOF(addr_port), NI_NUMERICHOST | NI_NUMERICSERV)) + { + ecs_os_strcpy(addr_host, "unknown"); + ecs_os_strcpy(addr_port, "unknown"); + } - ecs_doc_set_brief(world, ecs_id(EcsIdentifier), "Component used for entity names"); - ecs_doc_set_brief(world, EcsName, "Tag used with EcsIdentifier to signal entity name"); - ecs_doc_set_brief(world, EcsSymbol, "Tag used with EcsIdentifier to signal entity symbol"); - - ecs_doc_set_brief(world, EcsTransitive, "Transitive relationship property"); - ecs_doc_set_brief(world, EcsReflexive, "Reflexive relationship property"); - ecs_doc_set_brief(world, EcsFinal, "Final relationship property"); - ecs_doc_set_brief(world, EcsDontInherit, "DontInherit relationship property"); - ecs_doc_set_brief(world, EcsTag, "Tag relationship property"); - ecs_doc_set_brief(world, EcsAcyclic, "Acyclic relationship property"); - ecs_doc_set_brief(world, EcsTraversable, "Traversable relationship property"); - ecs_doc_set_brief(world, EcsExclusive, "Exclusive relationship property"); - ecs_doc_set_brief(world, EcsSymmetric, "Symmetric relationship property"); - ecs_doc_set_brief(world, EcsWith, "With relationship property"); - ecs_doc_set_brief(world, EcsOnDelete, "OnDelete relationship cleanup property"); - ecs_doc_set_brief(world, EcsOnDeleteTarget, "OnDeleteTarget relationship cleanup property"); - ecs_doc_set_brief(world, EcsDefaultChildComponent, "Sets default component hint for children of entity"); - ecs_doc_set_brief(world, EcsRemove, "Remove relationship cleanup property"); - ecs_doc_set_brief(world, EcsDelete, "Delete relationship cleanup property"); - ecs_doc_set_brief(world, EcsPanic, "Panic relationship cleanup property"); - ecs_doc_set_brief(world, EcsIsA, "Builtin IsA relationship"); - ecs_doc_set_brief(world, EcsChildOf, "Builtin ChildOf relationship"); - ecs_doc_set_brief(world, EcsDependsOn, "Builtin DependsOn relationship"); - ecs_doc_set_brief(world, EcsOnAdd, "Builtin OnAdd event"); - ecs_doc_set_brief(world, EcsOnRemove, "Builtin OnRemove event"); - ecs_doc_set_brief(world, EcsOnSet, "Builtin OnSet event"); - ecs_doc_set_brief(world, EcsUnSet, "Builtin UnSet event"); - - ecs_doc_set_link(world, EcsTransitive, URL_ROOT "#transitive-property"); - ecs_doc_set_link(world, EcsReflexive, URL_ROOT "#reflexive-property"); - ecs_doc_set_link(world, EcsFinal, URL_ROOT "#final-property"); - ecs_doc_set_link(world, EcsDontInherit, URL_ROOT "#dontinherit-property"); - ecs_doc_set_link(world, EcsTag, URL_ROOT "#tag-property"); - ecs_doc_set_link(world, EcsAcyclic, URL_ROOT "#acyclic-property"); - ecs_doc_set_link(world, EcsTraversable, URL_ROOT "#traversable-property"); - ecs_doc_set_link(world, EcsExclusive, URL_ROOT "#exclusive-property"); - ecs_doc_set_link(world, EcsSymmetric, URL_ROOT "#symmetric-property"); - ecs_doc_set_link(world, EcsWith, URL_ROOT "#with-property"); - ecs_doc_set_link(world, EcsOnDelete, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsOnDeleteTarget, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsRemove, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsDelete, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsPanic, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsIsA, URL_ROOT "#the-isa-relationship"); - ecs_doc_set_link(world, EcsChildOf, URL_ROOT "#the-childof-relationship"); - - /* Initialize documentation for meta components */ - ecs_entity_t meta = ecs_lookup_fullpath(world, "flecs.meta"); - ecs_doc_set_brief(world, meta, "Flecs module with reflection components"); + ecs_os_mutex_lock(srv->lock); + if (srv->should_run) { + ecs_dbg_2("http: initializing connection socket"); - ecs_doc_set_brief(world, ecs_id(EcsMetaType), "Component added to types"); - ecs_doc_set_brief(world, ecs_id(EcsMetaTypeSerialized), "Component that stores reflection data in an optimized format"); - ecs_doc_set_brief(world, ecs_id(EcsPrimitive), "Component added to primitive types"); - ecs_doc_set_brief(world, ecs_id(EcsEnum), "Component added to enumeration types"); - ecs_doc_set_brief(world, ecs_id(EcsBitmask), "Component added to bitmask types"); - ecs_doc_set_brief(world, ecs_id(EcsMember), "Component added to struct members"); - ecs_doc_set_brief(world, ecs_id(EcsStruct), "Component added to struct types"); - ecs_doc_set_brief(world, ecs_id(EcsArray), "Component added to array types"); - ecs_doc_set_brief(world, ecs_id(EcsVector), "Component added to vector types"); + sock = socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (!http_socket_is_valid(sock)) { + ecs_err("http: unable to create new connection socket: %s", + ecs_os_strerror(errno)); + ecs_os_mutex_unlock(srv->lock); + goto done; + } - ecs_doc_set_brief(world, ecs_id(ecs_bool_t), "bool component"); - ecs_doc_set_brief(world, ecs_id(ecs_char_t), "char component"); - ecs_doc_set_brief(world, ecs_id(ecs_byte_t), "byte component"); - ecs_doc_set_brief(world, ecs_id(ecs_u8_t), "8 bit unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_u16_t), "16 bit unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_u32_t), "32 bit unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_u64_t), "64 bit unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_uptr_t), "word sized unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_i8_t), "8 bit signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_i16_t), "16 bit signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_i32_t), "32 bit signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_i64_t), "64 bit signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_iptr_t), "word sized signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_f32_t), "32 bit floating point component"); - ecs_doc_set_brief(world, ecs_id(ecs_f64_t), "64 bit floating point component"); - ecs_doc_set_brief(world, ecs_id(ecs_string_t), "string component"); - ecs_doc_set_brief(world, ecs_id(ecs_entity_t), "entity component"); + int reuse = 1, result; + result = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char*)&reuse, ECS_SIZEOF(reuse)); + if (result) { + ecs_warn("http: failed to setsockopt: %s", ecs_os_strerror(errno)); + } - /* Initialize documentation for doc components */ - ecs_entity_t doc = ecs_lookup_fullpath(world, "flecs.doc"); - ecs_doc_set_brief(world, doc, "Flecs module with documentation components"); + if (addr->sa_family == AF_INET6) { + int ipv6only = 0; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + (char*)&ipv6only, ECS_SIZEOF(ipv6only))) + { + ecs_warn("http: failed to setsockopt: %s", + ecs_os_strerror(errno)); + } + } - ecs_doc_set_brief(world, ecs_id(EcsDocDescription), "Component used to add documentation"); - ecs_doc_set_brief(world, EcsDocBrief, "Used as (Description, Brief) to add a brief description"); - ecs_doc_set_brief(world, EcsDocDetail, "Used as (Description, Detail) to add a detailed description"); - ecs_doc_set_brief(world, EcsDocLink, "Used as (Description, Link) to add a link"); -} + result = http_bind(sock, addr, addr_len); + if (result) { + if (errno == EADDRINUSE) { + ret = 1; + ecs_warn("http: address '%s:%s' in use, retrying with port %u", + addr_host, addr_port, srv->port + 1); + } else { + ecs_err("http: failed to bind to '%s:%s': %s", + addr_host, addr_port, ecs_os_strerror(errno)); + } -#endif + ecs_os_mutex_unlock(srv->lock); + goto done; + } -/** - * @file addons/doc.c - * @brief Doc addon. - */ + http_sock_set_timeout(sock, 1000); + srv->sock = sock; -#ifdef FLECS_DOC + result = listen(srv->sock, SOMAXCONN); + if (result) { + ecs_warn("http: could not listen for SOMAXCONN (%d) connections: %s", + SOMAXCONN, ecs_os_strerror(errno)); + } -static ECS_COPY(EcsDocDescription, dst, src, { - ecs_os_strset((char**)&dst->value, src->value); + ecs_trace("http: listening for incoming connections on '%s:%s'", + addr_host, addr_port); + } else { + ecs_dbg_2("http: server shut down while initializing"); + } + ecs_os_mutex_unlock(srv->lock); -}) + struct sockaddr_storage remote_addr; + ecs_size_t remote_addr_len = 0; -static ECS_MOVE(EcsDocDescription, dst, src, { - ecs_os_free((char*)dst->value); - dst->value = src->value; - src->value = NULL; -}) + while (srv->should_run) { + remote_addr_len = ECS_SIZEOF(remote_addr); + ecs_http_socket_t sock_conn = http_accept(srv->sock, (struct sockaddr*) &remote_addr, + &remote_addr_len); -static ECS_DTOR(EcsDocDescription, ptr, { - ecs_os_free((char*)ptr->value); -}) + if (!http_socket_is_valid(sock_conn)) { + if (srv->should_run) { + ecs_dbg("http: connection attempt failed: %s", + ecs_os_strerror(errno)); + } + continue; + } + + http_conn_res_t conn = http_init_connection(srv, sock_conn, &remote_addr, remote_addr_len); + http_recv_connection(srv, conn.conn, conn.id, sock_conn); + } + +done: + ecs_os_mutex_lock(srv->lock); + if (http_socket_is_valid(sock) && errno != EBADF) { + http_close(&sock); + srv->sock = sock; + } + ecs_os_mutex_unlock(srv->lock); + + ecs_trace("http: no longer accepting connections on '%s:%s'", + addr_host, addr_port); + + return ret; +} static -void flecs_doc_set( - ecs_world_t *world, - ecs_entity_t entity, - ecs_entity_t kind, - const char *value) -{ - if (value) { - ecs_set_pair(world, entity, EcsDocDescription, kind, { - /* Safe, value gets copied by copy hook */ - .value = ECS_CONST_CAST(char*, value) - }); +void* http_server_thread(void* arg) { + ecs_http_server_t *srv = arg; + struct sockaddr_in addr; + ecs_os_zeromem(&addr); + addr.sin_family = AF_INET; + + int retries = 0; +retry: + addr.sin_port = htons(srv->port); + + if (!srv->ipaddr) { + addr.sin_addr.s_addr = htonl(INADDR_ANY); } else { - ecs_remove_pair(world, entity, ecs_id(EcsDocDescription), kind); + inet_pton(AF_INET, srv->ipaddr, &(addr.sin_addr)); } -} -void ecs_doc_set_name( - ecs_world_t *world, - ecs_entity_t entity, - const char *name) -{ - flecs_doc_set(world, entity, EcsName, name); -} + if (http_accept_connections( + srv, (struct sockaddr*)&addr, ECS_SIZEOF(addr)) == 1) + { + srv->port ++; + retries ++; + if (retries < 10) { + goto retry; + } else { + ecs_err("http: failed to connect (retried 10 times)"); + } + } -void ecs_doc_set_brief( - ecs_world_t *world, - ecs_entity_t entity, - const char *brief) -{ - flecs_doc_set(world, entity, EcsDocBrief, brief); + return NULL; } -void ecs_doc_set_detail( - ecs_world_t *world, - ecs_entity_t entity, - const char *detail) +static +void http_do_request( + ecs_http_server_t *srv, + ecs_http_reply_t *reply, + const ecs_http_request_impl_t *req) { - flecs_doc_set(world, entity, EcsDocDetail, detail); -} + ecs_check(srv != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(srv->callback != NULL, ECS_INVALID_OPERATION, + "missing request handler for server"); -void ecs_doc_set_link( - ecs_world_t *world, - ecs_entity_t entity, - const char *link) -{ - flecs_doc_set(world, entity, EcsDocLink, link); + if (srv->callback(ECS_CONST_CAST(ecs_http_request_t*, req), reply, + srv->ctx) == false) + { + reply->code = 404; + reply->status = "Resource not found"; + ecs_os_linc(&ecs_http_request_not_handled_count); + } else { + if (reply->code >= 400) { + ecs_os_linc(&ecs_http_request_handled_error_count); + } else { + ecs_os_linc(&ecs_http_request_handled_ok_count); + } + } +error: + return; } -void ecs_doc_set_color( - ecs_world_t *world, - ecs_entity_t entity, - const char *color) +static +void http_handle_request( + ecs_http_server_t *srv, + ecs_http_request_impl_t *req) { - flecs_doc_set(world, entity, EcsDocColor, color); -} + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + ecs_http_connection_impl_t *conn = + (ecs_http_connection_impl_t*)req->pub.conn; -const char* ecs_doc_get_name( - const ecs_world_t *world, - ecs_entity_t entity) -{ - const EcsDocDescription *ptr = ecs_get_pair( - world, entity, EcsDocDescription, EcsName); - if (ptr) { - return ptr->value; + if (req->pub.method != EcsHttpOptions) { + if (srv->callback((ecs_http_request_t*)req, &reply, srv->ctx) == false) { + reply.code = 404; + reply.status = "Resource not found"; + ecs_os_linc(&ecs_http_request_not_handled_count); + } else { + if (reply.code >= 400) { + ecs_os_linc(&ecs_http_request_handled_error_count); + } else { + ecs_os_linc(&ecs_http_request_handled_ok_count); + } + } + + if (req->pub.method == EcsHttpGet) { + http_insert_request_entry(srv, req, &reply); + } + + http_send_reply(conn, &reply, false); + ecs_dbg_2("http: reply sent to '%s:%s'", conn->pub.host, conn->pub.port); } else { - return ecs_get_name(world, entity); + /* Already taken care of */ } + + http_reply_fini(&reply); + http_request_fini(req); + http_connection_free(conn); } -const char* ecs_doc_get_brief( - const ecs_world_t *world, - ecs_entity_t entity) +static +void http_purge_request_cache( + ecs_http_server_t *srv, + bool fini) { - const EcsDocDescription *ptr = ecs_get_pair( - world, entity, EcsDocDescription, EcsDocBrief); - if (ptr) { - return ptr->value; - } else { - return NULL; + ecs_time_t t = {0, 0}; + double time = ecs_time_measure(&t); + ecs_map_iter_t it = ecs_map_iter(&srv->request_cache.impl); + while (ecs_map_next(&it)) { + ecs_hm_bucket_t *bucket = ecs_map_ptr(&it); + int32_t i, count = ecs_vec_count(&bucket->values); + ecs_http_request_key_t *keys = ecs_vec_first(&bucket->keys); + ecs_http_request_entry_t *entries = ecs_vec_first(&bucket->values); + for (i = count - 1; i >= 0; i --) { + ecs_http_request_entry_t *entry = &entries[i]; + if (fini || ((time - entry->time) > srv->cache_purge_timeout)) { + ecs_http_request_key_t *key = &keys[i]; + /* Safe, code owns the value */ + ecs_os_free(ECS_CONST_CAST(char*, key->array)); + ecs_os_free(entry->content); + flecs_hm_bucket_remove(&srv->request_cache, bucket, + ecs_map_key(&it), i); + } + } + } + + if (fini) { + flecs_hashmap_fini(&srv->request_cache); } } -const char* ecs_doc_get_detail( - const ecs_world_t *world, - ecs_entity_t entity) +static +int32_t http_dequeue_requests( + ecs_http_server_t *srv, + double delta_time) { - const EcsDocDescription *ptr = ecs_get_pair( - world, entity, EcsDocDescription, EcsDocDetail); - if (ptr) { - return ptr->value; - } else { - return NULL; + ecs_os_mutex_lock(srv->lock); + + int32_t i, request_count = flecs_sparse_count(&srv->requests); + for (i = request_count - 1; i >= 1; i --) { + ecs_http_request_impl_t *req = flecs_sparse_get_dense_t( + &srv->requests, ecs_http_request_impl_t, i); + http_handle_request(srv, req); + } + + int32_t connections_count = flecs_sparse_count(&srv->connections); + for (i = connections_count - 1; i >= 1; i --) { + ecs_http_connection_impl_t *conn = flecs_sparse_get_dense_t( + &srv->connections, ecs_http_connection_impl_t, i); + + conn->dequeue_timeout += delta_time; + conn->dequeue_retries ++; + + if ((conn->dequeue_timeout > + (double)ECS_HTTP_CONNECTION_PURGE_TIMEOUT) && + (conn->dequeue_retries > ECS_HTTP_CONNECTION_PURGE_RETRY_COUNT)) + { + ecs_dbg("http: purging connection '%s:%s' (sock = %d)", + conn->pub.host, conn->pub.port, conn->sock); + http_connection_free(conn); + } } + + http_purge_request_cache(srv, false); + ecs_os_mutex_unlock(srv->lock); + + return request_count - 1; } -const char* ecs_doc_get_link( - const ecs_world_t *world, - ecs_entity_t entity) +const char* ecs_http_get_header( + const ecs_http_request_t* req, + const char* name) { - const EcsDocDescription *ptr = ecs_get_pair( - world, entity, EcsDocDescription, EcsDocLink); - if (ptr) { - return ptr->value; - } else { - return NULL; + for (ecs_size_t i = 0; i < req->header_count; i++) { + if (!ecs_os_strcmp(req->headers[i].key, name)) { + return req->headers[i].value; + } } + return NULL; } -const char* ecs_doc_get_color( - const ecs_world_t *world, - ecs_entity_t entity) +const char* ecs_http_get_param( + const ecs_http_request_t* req, + const char* name) { - const EcsDocDescription *ptr = ecs_get_pair( - world, entity, EcsDocDescription, EcsDocColor); - if (ptr) { - return ptr->value; - } else { - return NULL; + for (ecs_size_t i = 0; i < req->param_count; i++) { + if (!ecs_os_strcmp(req->params[i].key, name)) { + return req->params[i].value; + } } + return NULL; } -void FlecsDocImport( - ecs_world_t *world) -{ - ECS_MODULE(world, FlecsDoc); - - ecs_set_name_prefix(world, "EcsDoc"); - - flecs_bootstrap_component(world, EcsDocDescription); - flecs_bootstrap_tag(world, EcsDocBrief); - flecs_bootstrap_tag(world, EcsDocDetail); - flecs_bootstrap_tag(world, EcsDocLink); - flecs_bootstrap_tag(world, EcsDocColor); +ecs_http_server_t* ecs_http_server_init( + const ecs_http_server_desc_t *desc) +{ + ecs_check(ecs_os_has_threading(), ECS_UNSUPPORTED, + "missing OS API implementation"); - ecs_set_hooks(world, EcsDocDescription, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsDocDescription), - .copy = ecs_copy(EcsDocDescription), - .dtor = ecs_dtor(EcsDocDescription) - }); + ecs_http_server_t* srv = ecs_os_calloc_t(ecs_http_server_t); + srv->lock = ecs_os_mutex_new(); + srv->sock = HTTP_SOCKET_INVALID; - ecs_add_id(world, ecs_id(EcsDocDescription), EcsDontInherit); - ecs_add_id(world, ecs_id(EcsDocDescription), EcsPrivate); - -} + srv->should_run = false; + srv->initialized = true; -#endif + srv->cache_timeout = desc->cache_timeout; + srv->cache_purge_timeout = desc->cache_purge_timeout; -/** - * @file addons/flecs_cpp.c - * @brief Utilities for C++ addon. - */ + if (!ECS_EQZERO(srv->cache_timeout) && + ECS_EQZERO(srv->cache_purge_timeout)) + { + srv->cache_purge_timeout = srv->cache_timeout * 10; + } -#include + srv->callback = desc->callback; + srv->ctx = desc->ctx; + srv->port = desc->port; + srv->ipaddr = desc->ipaddr; + srv->send_queue.wait_ms = desc->send_queue_wait_ms; + if (!srv->send_queue.wait_ms) { + srv->send_queue.wait_ms = 1; + } -/* Utilities for C++ API */ + flecs_sparse_init_t(&srv->connections, NULL, NULL, ecs_http_connection_impl_t); + flecs_sparse_init_t(&srv->requests, NULL, NULL, ecs_http_request_impl_t); -#ifdef FLECS_CPP + /* Start at id 1 */ + flecs_sparse_new_id(&srv->connections); + flecs_sparse_new_id(&srv->requests); -/* Convert compiler-specific typenames extracted from __PRETTY_FUNCTION__ to - * a uniform identifier */ + /* Initialize request cache */ + flecs_hashmap_init(&srv->request_cache, + ecs_http_request_key_t, ecs_http_request_entry_t, + http_request_key_hash, http_request_key_compare, NULL); -#define ECS_CONST_PREFIX "const " -#define ECS_STRUCT_PREFIX "struct " -#define ECS_CLASS_PREFIX "class " -#define ECS_ENUM_PREFIX "enum " +#ifndef ECS_TARGET_WINDOWS + /* Ignore pipe signal. SIGPIPE can occur when a message is sent to a client + * but te client already disconnected. */ + signal(SIGPIPE, SIG_IGN); +#endif -#define ECS_CONST_LEN (-1 + (ecs_size_t)sizeof(ECS_CONST_PREFIX)) -#define ECS_STRUCT_LEN (-1 + (ecs_size_t)sizeof(ECS_STRUCT_PREFIX)) -#define ECS_CLASS_LEN (-1 + (ecs_size_t)sizeof(ECS_CLASS_PREFIX)) -#define ECS_ENUM_LEN (-1 + (ecs_size_t)sizeof(ECS_ENUM_PREFIX)) + return srv; +error: + return NULL; +} -static -ecs_size_t ecs_cpp_strip_prefix( - char *typeName, - ecs_size_t len, - const char *prefix, - ecs_size_t prefix_len) +void ecs_http_server_fini( + ecs_http_server_t* srv) { - if ((len > prefix_len) && !ecs_os_strncmp(typeName, prefix, prefix_len)) { - ecs_os_memmove(typeName, typeName + prefix_len, len - prefix_len); - typeName[len - prefix_len] = '\0'; - len -= prefix_len; + if (srv->should_run) { + ecs_http_server_stop(srv); } - return len; + ecs_os_mutex_free(srv->lock); + http_purge_request_cache(srv, true); + flecs_sparse_fini(&srv->requests); + flecs_sparse_fini(&srv->connections); + ecs_os_free(srv); } -static -void ecs_cpp_trim_type_name( - char *typeName) +int ecs_http_server_start( + ecs_http_server_t *srv) { - ecs_size_t len = ecs_os_strlen(typeName); + ecs_check(srv != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(srv->initialized, ECS_INVALID_PARAMETER, NULL); + ecs_check(!srv->should_run, ECS_INVALID_PARAMETER, NULL); + ecs_check(!srv->thread, ECS_INVALID_PARAMETER, NULL); - len = ecs_cpp_strip_prefix(typeName, len, ECS_CONST_PREFIX, ECS_CONST_LEN); - len = ecs_cpp_strip_prefix(typeName, len, ECS_STRUCT_PREFIX, ECS_STRUCT_LEN); - len = ecs_cpp_strip_prefix(typeName, len, ECS_CLASS_PREFIX, ECS_CLASS_LEN); - len = ecs_cpp_strip_prefix(typeName, len, ECS_ENUM_PREFIX, ECS_ENUM_LEN); + srv->should_run = true; - while (typeName[len - 1] == ' ' || - typeName[len - 1] == '&' || - typeName[len - 1] == '*') - { - len --; - typeName[len] = '\0'; - } + ecs_dbg("http: starting server thread"); - /* Remove const at end of string */ - if (len > ECS_CONST_LEN) { - if (!ecs_os_strncmp(&typeName[len - ECS_CONST_LEN], " const", ECS_CONST_LEN)) { - typeName[len - ECS_CONST_LEN] = '\0'; - } - len -= ECS_CONST_LEN; + srv->thread = ecs_os_thread_new(http_server_thread, srv); + if (!srv->thread) { + goto error; } - /* Check if there are any remaining "struct " strings, which can happen - * if this is a template type on msvc. */ - if (len > ECS_STRUCT_LEN) { - char *ptr = typeName; - while ((ptr = strstr(ptr + 1, ECS_STRUCT_PREFIX)) != 0) { - /* Make sure we're not matched with part of a longer identifier - * that contains 'struct' */ - if (ptr[-1] == '<' || ptr[-1] == ',' || isspace(ptr[-1])) { - ecs_os_memmove(ptr, ptr + ECS_STRUCT_LEN, - ecs_os_strlen(ptr + ECS_STRUCT_LEN) + 1); - len -= ECS_STRUCT_LEN; - } - } + srv->send_queue.thread = ecs_os_thread_new(http_server_send_queue, srv); + if (!srv->send_queue.thread) { + goto error; } -} -char* ecs_cpp_get_type_name( - char *type_name, - const char *func_name, - size_t len, - size_t front_len) -{ - memcpy(type_name, func_name + front_len, len); - type_name[len] = '\0'; - ecs_cpp_trim_type_name(type_name); - return type_name; + return 0; +error: + return -1; } -char* ecs_cpp_get_symbol_name( - char *symbol_name, - const char *type_name, - size_t len) +void ecs_http_server_stop( + ecs_http_server_t* srv) { - // Symbol is same as name, but with '::' replaced with '.' - ecs_os_strcpy(symbol_name, type_name); + ecs_check(srv != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(srv->initialized, ECS_INVALID_OPERATION, + "cannot stop HTTP server: not initialized"); + ecs_check(srv->should_run, ECS_INVALID_PARAMETER, + "cannot stop HTTP server: already stopped/stopping"); - char *ptr; - size_t i; - for (i = 0, ptr = symbol_name; i < len && *ptr; i ++, ptr ++) { - if (*ptr == ':') { - symbol_name[i] = '.'; - ptr ++; - } else { - symbol_name[i] = *ptr; - } + /* Stop server thread */ + ecs_dbg("http: shutting down server thread"); + + ecs_os_mutex_lock(srv->lock); + srv->should_run = false; + if (http_socket_is_valid(srv->sock)) { + http_close(&srv->sock); } + ecs_os_mutex_unlock(srv->lock); - symbol_name[i] = '\0'; + ecs_os_thread_join(srv->thread); + ecs_os_thread_join(srv->send_queue.thread); + ecs_trace("http: server threads shut down"); - return symbol_name; -} + /* Cleanup all outstanding requests */ + int i, count = flecs_sparse_count(&srv->requests); + for (i = count - 1; i >= 1; i --) { + http_request_fini(flecs_sparse_get_dense_t( + &srv->requests, ecs_http_request_impl_t, i)); + } -static -const char* flecs_cpp_func_rchr( - const char *func_name, - ecs_size_t func_name_len, - ecs_size_t func_back_len, - char ch) -{ - const char *r = strrchr(func_name, ch); - if ((r - func_name) >= (func_name_len - flecs_uto(ecs_size_t, func_back_len))) { - return NULL; + /* Close all connections */ + count = flecs_sparse_count(&srv->connections); + for (i = count - 1; i >= 1; i --) { + http_connection_free(flecs_sparse_get_dense_t( + &srv->connections, ecs_http_connection_impl_t, i)); } - return r; -} -static -const char* flecs_cpp_func_max( - const char *a, - const char *b) -{ - if (a > b) return a; - return b; + ecs_assert(flecs_sparse_count(&srv->connections) == 1, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_sparse_count(&srv->requests) == 1, + ECS_INTERNAL_ERROR, NULL); + + srv->thread = 0; +error: + return; } -char* ecs_cpp_get_constant_name( - char *constant_name, - const char *func_name, - size_t func_name_len, - size_t func_back_len) +void ecs_http_server_dequeue( + ecs_http_server_t* srv, + ecs_ftime_t delta_time) { - ecs_size_t f_len = flecs_uto(ecs_size_t, func_name_len); - ecs_size_t fb_len = flecs_uto(ecs_size_t, func_back_len); - const char *start = flecs_cpp_func_rchr(func_name, f_len, fb_len, ' '); - start = flecs_cpp_func_max(start, flecs_cpp_func_rchr( - func_name, f_len, fb_len, ')')); - start = flecs_cpp_func_max(start, flecs_cpp_func_rchr( - func_name, f_len, fb_len, ':')); - start = flecs_cpp_func_max(start, flecs_cpp_func_rchr( - func_name, f_len, fb_len, ',')); - ecs_assert(start != NULL, ECS_INVALID_PARAMETER, func_name); - start ++; + ecs_check(srv != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(srv->initialized, ECS_INVALID_PARAMETER, NULL); + ecs_check(srv->should_run, ECS_INVALID_PARAMETER, NULL); - ecs_size_t len = flecs_uto(ecs_size_t, - (f_len - (start - func_name) - fb_len)); - ecs_os_memcpy_n(constant_name, start, char, len); - constant_name[len] = '\0'; - return constant_name; -} + srv->dequeue_timeout += (double)delta_time; + srv->stats_timeout += (double)delta_time; -// Names returned from the name_helper class do not start with :: -// but are relative to the root. If the namespace of the type -// overlaps with the namespace of the current module, strip it from -// the implicit identifier. -// This allows for registration of component types that are not in the -// module namespace to still be registered under the module scope. -const char* ecs_cpp_trim_module( - ecs_world_t *world, - const char *type_name) -{ - ecs_entity_t scope = ecs_get_scope(world); - if (!scope) { - return type_name; + if ((1000 * srv->dequeue_timeout) > (double)ECS_HTTP_MIN_DEQUEUE_INTERVAL) { + srv->dequeue_timeout = 0; + + ecs_time_t t = {0}; + ecs_time_measure(&t); + int32_t request_count = http_dequeue_requests(srv, srv->dequeue_timeout); + srv->requests_processed += request_count; + srv->requests_processed_total += request_count; + double time_spent = ecs_time_measure(&t); + srv->request_time += time_spent; + srv->request_time_total += time_spent; + srv->dequeue_count ++; } - char *path = ecs_get_path_w_sep(world, 0, scope, "::", NULL); - if (path) { - const char *ptr = strrchr(type_name, ':'); - ecs_assert(ptr != type_name, ECS_INTERNAL_ERROR, NULL); - if (ptr) { - ptr --; - ecs_assert(ptr[0] == ':', ECS_INTERNAL_ERROR, NULL); - ecs_size_t name_path_len = (ecs_size_t)(ptr - type_name); - if (name_path_len <= ecs_os_strlen(path)) { - if (!ecs_os_strncmp(type_name, path, name_path_len)) { - type_name = &type_name[name_path_len + 2]; - } - } - } + if ((1000 * srv->stats_timeout) > (double)ECS_HTTP_MIN_STATS_INTERVAL) { + srv->stats_timeout = 0; + ecs_dbg("http: processed %d requests in %.3fs (avg %.3fs / dequeue)", + srv->requests_processed, srv->request_time, + (srv->request_time / (double)srv->dequeue_count)); + srv->requests_processed = 0; + srv->request_time = 0; + srv->dequeue_count = 0; } - ecs_os_free(path); - return type_name; +error: + return; } -// Validate registered component -void ecs_cpp_component_validate( - ecs_world_t *world, - ecs_entity_t id, - const char *name, - const char *symbol, - size_t size, - size_t alignment, - bool implicit_name) +int ecs_http_server_http_request( + ecs_http_server_t* srv, + const char *req, + ecs_size_t len, + ecs_http_reply_t *reply_out) { - /* If entity has a name check if it matches */ - if (ecs_is_valid(world, id) && ecs_get_name(world, id) != NULL) { - if (!implicit_name && id >= EcsFirstUserComponentId) { -#ifndef FLECS_NDEBUG - char *path = ecs_get_path_w_sep( - world, 0, id, "::", NULL); - if (ecs_os_strcmp(path, name)) { - ecs_abort(ECS_INCONSISTENT_NAME, - "component '%s' already registered with name '%s'", - name, path); - } - ecs_os_free(path); -#endif - } - - if (symbol) { - const char *existing_symbol = ecs_get_symbol(world, id); - if (existing_symbol) { - if (ecs_os_strcmp(symbol, existing_symbol)) { - ecs_abort(ECS_INCONSISTENT_NAME, - "component '%s' with symbol '%s' already registered with symbol '%s'", - name, symbol, existing_symbol); - } - } - } - } else { - /* Ensure that the entity id valid */ - if (!ecs_is_alive(world, id)) { - ecs_ensure(world, id); - } - - /* Register name with entity, so that when the entity is created the - * correct id will be resolved from the name. Only do this when the - * entity is empty. */ - ecs_add_path_w_sep(world, id, 0, name, "::", "::"); + if (!len) { + len = ecs_os_strlen(req); } - /* If a component was already registered with this id but with a - * different size, the ecs_component_init function will fail. */ - - /* We need to explicitly call ecs_component_init here again. Even though - * the component was already registered, it may have been registered - * with a different world. This ensures that the component is registered - * with the same id for the current world. - * If the component was registered already, nothing will change. */ - ecs_entity_t ent = ecs_component_init(world, &(ecs_component_desc_t){ - .entity = id, - .type.size = flecs_uto(int32_t, size), - .type.alignment = flecs_uto(int32_t, alignment) - }); - (void)ent; - ecs_assert(ent == id, ECS_INTERNAL_ERROR, NULL); -} + ecs_http_fragment_t frag = {0}; + if (!http_parse_request(&frag, req, len)) { + ecs_strbuf_reset(&frag.buf); + reply_out->code = 400; + return -1; + } -ecs_entity_t ecs_cpp_component_register( - ecs_world_t *world, - ecs_entity_t id, - const char *name, - const char *symbol, - ecs_size_t size, - ecs_size_t alignment, - bool implicit_name, - bool *existing_out) -{ - (void)size; - (void)alignment; + ecs_http_request_impl_t request; + char *res = http_decode_request(&request, &frag); + if (!res) { + reply_out->code = 400; + return -1; + } - /* If the component is not yet registered, ensure no other component - * or entity has been registered with this name. Ensure component is - * looked up from root. */ - bool existing = false; - ecs_entity_t prev_scope = ecs_set_scope(world, 0); - ecs_entity_t ent; - if (id) { - ent = id; + ecs_http_request_entry_t *entry = + http_find_request_entry(srv, request.res, request.req_len); + if (entry) { + reply_out->body = ECS_STRBUF_INIT; + reply_out->code = entry->code; + reply_out->content_type = "application/json"; + reply_out->headers = ECS_STRBUF_INIT; + reply_out->status = "OK"; + ecs_strbuf_appendstrn(&reply_out->body, + entry->content, entry->content_length); } else { - ent = ecs_lookup_path_w_sep(world, 0, name, "::", "::", false); - existing = ent != 0 && ecs_has(world, ent, EcsComponent); - } - ecs_set_scope(world, prev_scope); + http_do_request(srv, reply_out, &request); - /* If entity exists, compare symbol name to ensure that the component - * we are trying to register under this name is the same */ - if (ent) { - const EcsComponent *component = ecs_get(world, ent, EcsComponent); - if (component != NULL) { - const char *sym = ecs_get_symbol(world, ent); - if (sym && ecs_os_strcmp(sym, symbol)) { - /* Application is trying to register a type with an entity that - * was already associated with another type. In most cases this - * is an error, with the exception of a scenario where the - * application is wrapping a C type with a C++ type. - * - * In this case the C++ type typically inherits from the C type, - * and adds convenience methods to the derived class without - * changing anything that would change the size or layout. - * - * To meet this condition, the new type must have the same size - * and alignment as the existing type, and the name of the type - * type must be equal to the registered name (not symbol). - * - * The latter ensures that it was the intent of the application - * to alias the type, vs. accidentally registering an unrelated - * type with the same size/alignment. */ - char *type_path = ecs_get_fullpath(world, ent); - if (ecs_os_strcmp(type_path, symbol) || - component->size != size || - component->alignment != alignment) - { - ecs_err( - "component with name '%s' is already registered for"\ - " type '%s' (trying to register for type '%s')", - name, sym, symbol); - ecs_abort(ECS_NAME_IN_USE, NULL); - } - ecs_os_free(type_path); - } else if (!sym) { - ecs_set_symbol(world, ent, symbol); - } + if (request.pub.method == EcsHttpGet) { + http_insert_request_entry(srv, &request, reply_out); } - - /* If no entity is found, lookup symbol to check if the component was - * registered under a different name. */ - } else if (!implicit_name) { - ent = ecs_lookup_symbol(world, symbol, false, false); - ecs_assert(ent == 0 || (ent == id), ECS_INCONSISTENT_COMPONENT_ID, symbol); } - if (existing_out) { - *existing_out = existing; - } + ecs_os_free(res); - return ent; + http_purge_request_cache(srv, false); + + return (reply_out->code >= 400) ? -1 : 0; } -ecs_entity_t ecs_cpp_component_register_explicit( - ecs_world_t *world, - ecs_entity_t s_id, - ecs_entity_t id, - const char *name, - const char *type_name, - const char *symbol, - size_t size, - size_t alignment, - bool is_component, - bool *existing_out) +int ecs_http_server_request( + ecs_http_server_t* srv, + const char *method, + const char *req, + ecs_http_reply_t *reply_out) { - char *existing_name = NULL; - if (existing_out) *existing_out = false; + const char *http_ver = " HTTP/1.1\r\n\r\n"; + int32_t method_len = ecs_os_strlen(method); + int32_t req_len = ecs_os_strlen(req); + int32_t http_ver_len = ecs_os_strlen(http_ver); + char reqbuf[1024], *reqstr = reqbuf; - // If an explicit id is provided, it is possible that the symbol and - // name differ from the actual type, as the application may alias - // one type to another. - if (!id) { - if (!name) { - // If no name was provided first check if a type with the provided - // symbol was already registered. - id = ecs_lookup_symbol(world, symbol, false, false); - if (id) { - existing_name = ecs_get_path_w_sep(world, 0, id, "::", "::"); - name = existing_name; - if (existing_out) *existing_out = true; - } else { - // If type is not yet known, derive from type name - name = ecs_cpp_trim_module(world, type_name); - } - } - } else { - // If an explicit id is provided but it has no name, inherit - // the name from the type. - if (!ecs_is_valid(world, id) || !ecs_get_name(world, id)) { - name = ecs_cpp_trim_module(world, type_name); - } + int32_t len = method_len + req_len + http_ver_len + 1; + if (method_len + req_len + http_ver_len >= 1024) { + reqstr = ecs_os_malloc(len + 1); } - ecs_entity_t entity; - if (is_component || size != 0) { - entity = ecs_entity(world, { - .id = s_id, - .name = name, - .sep = "::", - .root_sep = "::", - .symbol = symbol, - .use_low_id = true - }); - ecs_assert(entity != 0, ECS_INVALID_OPERATION, NULL); + char *ptr = reqstr; + ecs_os_memcpy(ptr, method, method_len); ptr += method_len; + ptr[0] = ' '; ptr ++; + ecs_os_memcpy(ptr, req, req_len); ptr += req_len; + ecs_os_memcpy(ptr, http_ver, http_ver_len); ptr += http_ver_len; + ptr[0] = '\n'; - entity = ecs_component_init(world, &(ecs_component_desc_t){ - .entity = entity, - .type.size = flecs_uto(int32_t, size), - .type.alignment = flecs_uto(int32_t, alignment) - }); - ecs_assert(entity != 0, ECS_INVALID_OPERATION, NULL); - } else { - entity = ecs_entity(world, { - .id = s_id, - .name = name, - .sep = "::", - .root_sep = "::", - .symbol = symbol, - .use_low_id = true - }); + int result = ecs_http_server_http_request(srv, reqstr, len, reply_out); + if (reqbuf != reqstr) { + ecs_os_free(reqstr); } - ecs_assert(entity != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!s_id || s_id == entity, ECS_INTERNAL_ERROR, NULL); - ecs_os_free(existing_name); - - return entity; + return result; } -void ecs_cpp_enum_init( - ecs_world_t *world, - ecs_entity_t id) +void* ecs_http_server_ctx( + ecs_http_server_t* srv) { - (void)world; - (void)id; -#ifdef FLECS_META - ecs_suspend_readonly_state_t readonly_state; - world = flecs_suspend_readonly(world, &readonly_state); - ecs_set(world, id, EcsEnum, {0}); - flecs_resume_readonly(world, &readonly_state); -#endif + return srv->ctx; } -ecs_entity_t ecs_cpp_enum_constant_register( - ecs_world_t *world, - ecs_entity_t parent, - ecs_entity_t id, - const char *name, - int value) -{ - ecs_suspend_readonly_state_t readonly_state; - world = flecs_suspend_readonly(world, &readonly_state); - - const char *parent_name = ecs_get_name(world, parent); - ecs_size_t parent_name_len = ecs_os_strlen(parent_name); - if (!ecs_os_strncmp(name, parent_name, parent_name_len)) { - name += parent_name_len; - if (name[0] == '_') { - name ++; - } - } - - ecs_entity_t prev = ecs_set_scope(world, parent); - id = ecs_entity(world, { - .id = id, - .name = name - }); - ecs_assert(id != 0, ECS_INVALID_OPERATION, name); - ecs_set_scope(world, prev); - - #ifdef FLECS_DEBUG - const EcsComponent *cptr = ecs_get(world, parent, EcsComponent); - ecs_assert(cptr != NULL, ECS_INVALID_PARAMETER, "enum is not a component"); - ecs_assert(cptr->size == ECS_SIZEOF(int32_t), ECS_UNSUPPORTED, - "enum component must have 32bit size"); - #endif - -#ifdef FLECS_META - ecs_set_id(world, id, ecs_pair(EcsConstant, ecs_id(ecs_i32_t)), - sizeof(ecs_i32_t), &value); #endif - flecs_resume_readonly(world, &readonly_state); - - ecs_trace("#[green]constant#[reset] %s.%s created with value %d", - ecs_get_name(world, parent), name, value); - - return id; -} +/** + * @file addons/journal.c + * @brief Journal addon. + */ -static int32_t flecs_reset_count = 0; -int32_t ecs_cpp_reset_count_get(void) { - return flecs_reset_count; -} +#ifdef FLECS_JOURNAL -int32_t ecs_cpp_reset_count_inc(void) { - return ++flecs_reset_count; +static +char* flecs_journal_entitystr( + ecs_world_t *world, + ecs_entity_t entity) +{ + char *path; + const char *_path = ecs_get_symbol(world, entity); + if (_path && !strchr(_path, '.')) { + path = flecs_asprintf("#[blue]%s", _path); + } else { + uint32_t gen = entity >> 32; + if (gen) { + path = flecs_asprintf("#[normal]_%u_%u", (uint32_t)entity, gen); + } else { + path = flecs_asprintf("#[normal]_%u", (uint32_t)entity); + } + } + return path; } -#ifdef FLECS_META -const ecs_member_t* ecs_cpp_last_member( - const ecs_world_t *world, - ecs_entity_t type) +static +char* flecs_journal_idstr( + ecs_world_t *world, + ecs_id_t id) { - const EcsStruct *st = ecs_get(world, type, EcsStruct); - if (!st) { - char *type_str = ecs_get_fullpath(world, type); - ecs_err("entity '%s' is not a struct", type_str); - ecs_os_free(type_str); - return 0; + if (ECS_IS_PAIR(id)) { + char *first_path = flecs_journal_entitystr(world, + ecs_pair_first(world, id)); + char *second_path = flecs_journal_entitystr(world, + ecs_pair_second(world, id)); + char *result = flecs_asprintf("#[cyan]ecs_pair#[normal](%s, %s)", + first_path, second_path); + ecs_os_free(first_path); + ecs_os_free(second_path); + return result; + } else if (!(id & ECS_ID_FLAGS_MASK)) { + return flecs_journal_entitystr(world, id); + } else { + return ecs_id_str(world, id); } +} - ecs_member_t *m = ecs_vec_get_t(&st->members, ecs_member_t, - ecs_vec_count(&st->members) - 1); - ecs_assert(m != NULL, ECS_INTERNAL_ERROR, NULL); +static int flecs_journal_sp = 0; - return m; -} -#endif +void flecs_journal_begin( + ecs_world_t *world, + ecs_journal_kind_t kind, + ecs_entity_t entity, + ecs_type_t *add, + ecs_type_t *remove) +{ + flecs_journal_sp ++; -#endif + if (ecs_os_api.log_level_ < FLECS_JOURNAL_LOG_LEVEL) { + return; + } -/** - * @file addons/http.c - * @brief HTTP addon. - * - * This is a heavily modified version of the EmbeddableWebServer (see copyright - * below). This version has been stripped from everything not strictly necessary - * for receiving/replying to simple HTTP requests, and has been modified to use - * the Flecs OS API. - * - * EmbeddableWebServer Copyright (c) 2016, 2019, 2020 Forrest Heller, and - * CONTRIBUTORS (see below) - All rights reserved. - * - * CONTRIBUTORS: - * Martin Pulec - bug fixes, warning fixes, IPv6 support - * Daniel Barry - bug fix (ifa_addr != NULL) - * - * Released under the BSD 2-clause license: - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. THIS SOFTWARE IS - * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#ifdef FLECS_HTTP - -#ifdef ECS_TARGET_MSVC -#pragma comment(lib, "Ws2_32.lib") -#endif + char *path = NULL; + char *var_id = NULL; + if (entity) { + if (kind != EcsJournalDeleteWith && kind != EcsJournalRemoveAll) { + path = ecs_get_path(world, entity); + var_id = flecs_journal_entitystr(world, entity); + } else { + path = ecs_id_str(world, entity); + var_id = flecs_journal_idstr(world, entity); + } + } -#if defined(ECS_TARGET_WINDOWS) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#include -typedef SOCKET ecs_http_socket_t; -#else -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __FreeBSD__ -#include -#endif -typedef int ecs_http_socket_t; + if (kind == EcsJournalNew) { + ecs_print(4, "#[magenta]#ifndef #[normal]_var_%s", var_id); + ecs_print(4, "#[magenta]#define #[normal]_var_%s", var_id); + ecs_print(4, "#[green]ecs_entity_t %s;", var_id); + ecs_print(4, "#[magenta]#endif"); + ecs_print(4, "%s = #[cyan]ecs_new_id#[reset](world); " + "#[grey] // %s = new()", var_id, path); + } + if (add) { + for (int i = 0; i < add->count; i ++) { + char *jidstr = flecs_journal_idstr(world, add->array[i]); + char *idstr = ecs_id_str(world, add->array[i]); + ecs_print(4, "#[cyan]ecs_add_id#[reset](world, %s, %s); " + "#[grey] // add(%s, %s)", var_id, jidstr, + path, idstr); + ecs_os_free(idstr); + ecs_os_free(jidstr); + } + } + if (remove) { + for (int i = 0; i < remove->count; i ++) { + char *jidstr = flecs_journal_idstr(world, remove->array[i]); + char *idstr = ecs_id_str(world, remove->array[i]); + ecs_print(4, "#[cyan]ecs_remove_id#[reset](world, %s, %s); " + "#[grey] // remove(%s, %s)", var_id, jidstr, + path, idstr); + ecs_os_free(idstr); + ecs_os_free(jidstr); + } + } + if (kind == EcsJournalClear) { + ecs_print(4, "#[cyan]ecs_clear#[reset](world, %s); " + "#[grey] // clear(%s)", var_id, path); + } else if (kind == EcsJournalDelete) { + ecs_print(4, "#[cyan]ecs_delete#[reset](world, %s); " + "#[grey] // delete(%s)", var_id, path); + } else if (kind == EcsJournalDeleteWith) { + ecs_print(4, "#[cyan]ecs_delete_with#[reset](world, %s); " + "#[grey] // delete_with(%s)", var_id, path); + } else if (kind == EcsJournalRemoveAll) { + ecs_print(4, "#[cyan]ecs_remove_all#[reset](world, %s); " + "#[grey] // remove_all(%s)", var_id, path); + } else if (kind == EcsJournalTableEvents) { + ecs_print(4, "#[cyan]ecs_run_aperiodic#[reset](world, " + "EcsAperiodicEmptyTables);"); + } + ecs_os_free(var_id); + ecs_os_free(path); + ecs_log_push(); +} -#if !defined(MSG_NOSIGNAL) -#define MSG_NOSIGNAL (0) -#endif +void flecs_journal_end(void) { + flecs_journal_sp --; + ecs_assert(flecs_journal_sp >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_log_pop(); +} #endif -/* Max length of request method */ -#define ECS_HTTP_METHOD_LEN_MAX (8) - -/* Timeout (s) before connection purge */ -#define ECS_HTTP_CONNECTION_PURGE_TIMEOUT (1.0) - -/* Number of dequeues before purging */ -#define ECS_HTTP_CONNECTION_PURGE_RETRY_COUNT (5) - -/* Number of retries receiving request */ -#define ECS_HTTP_REQUEST_RECV_RETRY (10) - -/* Minimum interval between dequeueing requests (ms) */ -#define ECS_HTTP_MIN_DEQUEUE_INTERVAL (50) - -/* Minimum interval between printing statistics (ms) */ -#define ECS_HTTP_MIN_STATS_INTERVAL (10 * 1000) +/** + * @file addons/log.c + * @brief Log addon. + */ -/* Receive buffer size */ -#define ECS_HTTP_SEND_RECV_BUFFER_SIZE (16 * 1024) -/* Max length of request (path + query + headers + body) */ -#define ECS_HTTP_REQUEST_LEN_MAX (10 * 1024 * 1024) +#ifdef FLECS_LOG -/* Total number of outstanding send requests */ -#define ECS_HTTP_SEND_QUEUE_MAX (256) +#include -/* Cache invalidation timeout (s) */ -#define ECS_HTTP_CACHE_TIMEOUT ((ecs_ftime_t)1.0) +void flecs_colorize_buf( + char *msg, + bool enable_colors, + ecs_strbuf_t *buf) +{ + ecs_assert(msg != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(buf != NULL, ECS_INTERNAL_ERROR, NULL); -/* Cache entry purge timeout (s) */ -#define ECS_HTTP_CACHE_PURGE_TIMEOUT ((ecs_ftime_t)10.0) + char *ptr, ch, prev = '\0'; + bool isNum = false; + char isStr = '\0'; + bool isVar = false; + bool overrideColor = false; + bool autoColor = true; + bool dontAppend = false; -/* Global statistics */ -int64_t ecs_http_request_received_count = 0; -int64_t ecs_http_request_invalid_count = 0; -int64_t ecs_http_request_handled_ok_count = 0; -int64_t ecs_http_request_handled_error_count = 0; -int64_t ecs_http_request_not_handled_count = 0; -int64_t ecs_http_request_preflight_count = 0; -int64_t ecs_http_send_ok_count = 0; -int64_t ecs_http_send_error_count = 0; -int64_t ecs_http_busy_count = 0; + for (ptr = msg; (ch = *ptr); ptr++) { + dontAppend = false; -/* Send request queue */ -typedef struct ecs_http_send_request_t { - ecs_http_socket_t sock; - char *headers; - int32_t header_length; - char *content; - int32_t content_length; -} ecs_http_send_request_t; + if (!overrideColor) { + if (isNum && !isdigit(ch) && !isalpha(ch) && (ch != '.') && (ch != '%')) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); + isNum = false; + } + if (isStr && (isStr == ch) && prev != '\\') { + isStr = '\0'; + } else if (((ch == '\'') || (ch == '"')) && !isStr && + !isalpha(prev) && (prev != '\\')) + { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_CYAN); + isStr = ch; + } -typedef struct ecs_http_send_queue_t { - ecs_http_send_request_t requests[ECS_HTTP_SEND_QUEUE_MAX]; - int32_t head; - int32_t tail; - ecs_os_thread_t thread; - int32_t wait_ms; -} ecs_http_send_queue_t; + if ((isdigit(ch) || (ch == '%' && isdigit(prev)) || + (ch == '-' && isdigit(ptr[1]))) && !isNum && !isStr && !isVar && + !isalpha(prev) && !isdigit(prev) && (prev != '_') && + (prev != '.')) + { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_GREEN); + isNum = true; + } -typedef struct ecs_http_request_key_t { - const char *array; - ecs_size_t count; -} ecs_http_request_key_t; + if (isVar && !isalpha(ch) && !isdigit(ch) && ch != '_') { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); + isVar = false; + } -typedef struct ecs_http_request_entry_t { - char *content; - int32_t content_length; - ecs_ftime_t time; -} ecs_http_request_entry_t; + if (!isStr && !isVar && ch == '$' && isalpha(ptr[1])) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_CYAN); + isVar = true; + } + } -/* HTTP server struct */ -struct ecs_http_server_t { - bool should_run; - bool running; + if (!isVar && !isStr && !isNum && ch == '#' && ptr[1] == '[') { + bool isColor = true; + overrideColor = true; - ecs_http_socket_t sock; - ecs_os_mutex_t lock; - ecs_os_thread_t thread; + /* Custom colors */ + if (!ecs_os_strncmp(&ptr[2], "]", ecs_os_strlen("]"))) { + autoColor = false; + } else if (!ecs_os_strncmp(&ptr[2], "green]", ecs_os_strlen("green]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_GREEN); + } else if (!ecs_os_strncmp(&ptr[2], "red]", ecs_os_strlen("red]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_RED); + } else if (!ecs_os_strncmp(&ptr[2], "blue]", ecs_os_strlen("blue]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_BLUE); + } else if (!ecs_os_strncmp(&ptr[2], "magenta]", ecs_os_strlen("magenta]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_MAGENTA); + } else if (!ecs_os_strncmp(&ptr[2], "cyan]", ecs_os_strlen("cyan]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_CYAN); + } else if (!ecs_os_strncmp(&ptr[2], "yellow]", ecs_os_strlen("yellow]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_YELLOW); + } else if (!ecs_os_strncmp(&ptr[2], "grey]", ecs_os_strlen("grey]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_GREY); + } else if (!ecs_os_strncmp(&ptr[2], "white]", ecs_os_strlen("white]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); + } else if (!ecs_os_strncmp(&ptr[2], "bold]", ecs_os_strlen("bold]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_BOLD); + } else if (!ecs_os_strncmp(&ptr[2], "normal]", ecs_os_strlen("normal]"))) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); + } else if (!ecs_os_strncmp(&ptr[2], "reset]", ecs_os_strlen("reset]"))) { + overrideColor = false; + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); + } else { + isColor = false; + overrideColor = false; + } - ecs_http_reply_action_t callback; - void *ctx; + if (isColor) { + ptr += 2; + while ((ch = *ptr) != ']') ptr ++; + dontAppend = true; + } + if (!autoColor) { + overrideColor = true; + } + } - ecs_sparse_t connections; /* sparse */ - ecs_sparse_t requests; /* sparse */ + if (ch == '\n') { + if (isNum || isStr || isVar || overrideColor) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); + overrideColor = false; + isNum = false; + isStr = false; + isVar = false; + } + } - bool initialized; + if (!dontAppend) { + ecs_strbuf_appendstrn(buf, ptr, 1); + } - uint16_t port; - const char *ipaddr; + if (!overrideColor) { + if (((ch == '\'') || (ch == '"')) && !isStr) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); + } + } - double dequeue_timeout; /* used to not lock request queue too often */ - double stats_timeout; /* used for periodic reporting of statistics */ + prev = ch; + } - double request_time; /* time spent on requests in last stats interval */ - double request_time_total; /* total time spent on requests */ - int32_t requests_processed; /* requests processed in last stats interval */ - int32_t requests_processed_total; /* total requests processed */ - int32_t dequeue_count; /* number of dequeues in last stats interval */ - ecs_http_send_queue_t send_queue; + if (isNum || isStr || isVar || overrideColor) { + if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); + } +} - ecs_hashmap_t request_cache; -}; +void ecs_printv_( + int level, + const char *file, + int32_t line, + const char *fmt, + va_list args) +{ + (void)level; + (void)line; -/** Fragment state, used by HTTP request parser */ -typedef enum { - HttpFragStateBegin, - HttpFragStateMethod, - HttpFragStatePath, - HttpFragStateVersion, - HttpFragStateHeaderStart, - HttpFragStateHeaderName, - HttpFragStateHeaderValueStart, - HttpFragStateHeaderValue, - HttpFragStateCR, - HttpFragStateCRLF, - HttpFragStateCRLFCR, - HttpFragStateBody, - HttpFragStateDone -} HttpFragState; + ecs_strbuf_t msg_buf = ECS_STRBUF_INIT; -/** A fragment is a partially received HTTP request */ -typedef struct { - HttpFragState state; - ecs_strbuf_t buf; - ecs_http_method_t method; - int32_t body_offset; - int32_t query_offset; - int32_t header_offsets[ECS_HTTP_HEADER_COUNT_MAX]; - int32_t header_value_offsets[ECS_HTTP_HEADER_COUNT_MAX]; - int32_t header_count; - int32_t param_offsets[ECS_HTTP_QUERY_PARAM_COUNT_MAX]; - int32_t param_value_offsets[ECS_HTTP_QUERY_PARAM_COUNT_MAX]; - int32_t param_count; - int32_t content_length; - char *header_buf_ptr; - char header_buf[32]; - bool parse_content_length; - bool invalid; -} ecs_http_fragment_t; + /* Apply color. Even if we don't want color, we still need to call the + * colorize function to get rid of the color tags (e.g. #[green]) */ + char *msg_nocolor = flecs_vasprintf(fmt, args); + flecs_colorize_buf(msg_nocolor, + ecs_os_api.flags_ & EcsOsApiLogWithColors, &msg_buf); + ecs_os_free(msg_nocolor); -/** Extend public connection type with fragment data */ -typedef struct { - ecs_http_connection_t pub; - ecs_http_socket_t sock; + char *msg = ecs_strbuf_get(&msg_buf); - /* Connection is purged after both timeout expires and connection has - * exceeded retry count. This ensures that a connection does not immediately - * timeout when a frame takes longer than usual */ - double dequeue_timeout; - int32_t dequeue_retries; -} ecs_http_connection_impl_t; + if (msg) { + ecs_os_api.log_(level, file, line, msg); + ecs_os_free(msg); + } else { + ecs_os_api.log_(level, file, line, ""); + } +} -typedef struct { - ecs_http_request_t pub; - uint64_t conn_id; /* for sanity check */ - char *res; - int32_t req_len; -} ecs_http_request_impl_t; +void ecs_print_( + int level, + const char *file, + int32_t line, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + ecs_printv_(level, file, line, fmt, args); + va_end(args); +} -static -ecs_size_t http_send( - ecs_http_socket_t sock, - const void *buf, - ecs_size_t size, - int flags) +void ecs_logv_( + int level, + const char *file, + int32_t line, + const char *fmt, + va_list args) { - ecs_assert(size >= 0, ECS_INTERNAL_ERROR, NULL); -#ifdef ECS_TARGET_POSIX - ssize_t send_bytes = send(sock, buf, flecs_itosize(size), - flags | MSG_NOSIGNAL); - return flecs_itoi32(send_bytes); -#else - int send_bytes = send(sock, buf, size, flags); - return flecs_itoi32(send_bytes); -#endif + if (level > ecs_os_api.log_level_) { + return; + } + + ecs_printv_(level, file, line, fmt, args); } -static -ecs_size_t http_recv( - ecs_http_socket_t sock, - void *buf, - ecs_size_t size, - int flags) +void ecs_log_( + int level, + const char *file, + int32_t line, + const char *fmt, + ...) { - ecs_size_t ret; -#ifdef ECS_TARGET_POSIX - ssize_t recv_bytes = recv(sock, buf, flecs_itosize(size), flags); - ret = flecs_itoi32(recv_bytes); -#else - int recv_bytes = recv(sock, buf, size, flags); - ret = flecs_itoi32(recv_bytes); -#endif - if (ret == -1) { - ecs_dbg("recv failed: %s (sock = %d)", ecs_os_strerror(errno), sock); - } else if (ret == 0) { - ecs_dbg("recv: received 0 bytes (sock = %d)", sock); + if (level > ecs_os_api.log_level_) { + return; } - return ret; + va_list args; + va_start(args, fmt); + ecs_printv_(level, file, line, fmt, args); + va_end(args); } -static -void http_sock_set_timeout( - ecs_http_socket_t sock, - int32_t timeout_ms) + +void ecs_log_push_( + int32_t level) { - int r; -#ifdef ECS_TARGET_POSIX - struct timeval tv; - tv.tv_sec = timeout_ms * 1000; - tv.tv_usec = 0; - r = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv); -#else - DWORD t = (DWORD)timeout_ms; - r = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&t, sizeof t); -#endif - if (r) { - ecs_warn("http: failed to set socket timeout: %s", - ecs_os_strerror(errno)); + if (level <= ecs_os_api.log_level_) { + ecs_os_api.log_indent_ ++; } } -static -void http_sock_keep_alive( - ecs_http_socket_t sock) +void ecs_log_pop_( + int32_t level) { - int v = 1; - if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (const char*)&v, sizeof v)) { - ecs_warn("http: failed to set socket KEEPALIVE: %s", - ecs_os_strerror(errno)); + if (level <= ecs_os_api.log_level_) { + ecs_os_api.log_indent_ --; + ecs_assert(ecs_os_api.log_indent_ >= 0, ECS_INTERNAL_ERROR, NULL); } } -static -void http_sock_nonblock(ecs_http_socket_t sock, bool enable) { - (void)sock; - (void)enable; -#ifdef ECS_TARGET_POSIX - int flags; - flags = fcntl(sock,F_GETFL,0); - if (flags == -1) { - ecs_warn("http: failed to set socket NONBLOCK: %s", - ecs_os_strerror(errno)); - return; - } - if (enable) { - flags = fcntl(sock, F_SETFL, flags | O_NONBLOCK); - } else { - flags = fcntl(sock, F_SETFL, flags & ~O_NONBLOCK); - } - if (flags == -1) { - ecs_warn("http: failed to set socket NONBLOCK: %s", - ecs_os_strerror(errno)); - return; +void ecs_parser_errorv_( + const char *name, + const char *expr, + int64_t column_arg, + const char *fmt, + va_list args) +{ + if (column_arg > 65536) { + /* Limit column size, which prevents the code from throwing up when the + * function is called with (expr - ptr), and expr is NULL. */ + column_arg = 0; } -#endif -} + + int32_t column = flecs_itoi32(column_arg); -static -int http_getnameinfo( - const struct sockaddr* addr, - ecs_size_t addr_len, - char *host, - ecs_size_t host_len, - char *port, - ecs_size_t port_len, - int flags) -{ - ecs_assert(addr_len > 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(host_len > 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(port_len > 0, ECS_INTERNAL_ERROR, NULL); -#if defined(ECS_TARGET_WINDOWS) - return getnameinfo(addr, addr_len, host, - flecs_ito(uint32_t, host_len), port, flecs_ito(uint32_t, port_len), - flags); -#else - return getnameinfo(addr, flecs_ito(uint32_t, addr_len), host, - flecs_ito(uint32_t, host_len), port, flecs_ito(uint32_t, port_len), - flags); -#endif -} + if (ecs_os_api.log_level_ >= -2) { + ecs_strbuf_t msg_buf = ECS_STRBUF_INIT; -static -int http_bind( - ecs_http_socket_t sock, - const struct sockaddr* addr, - ecs_size_t addr_len) -{ - ecs_assert(addr_len > 0, ECS_INTERNAL_ERROR, NULL); -#if defined(ECS_TARGET_WINDOWS) - return bind(sock, addr, addr_len); -#else - return bind(sock, addr, flecs_ito(uint32_t, addr_len)); -#endif -} + /* Count number of newlines up until column_arg */ + int32_t i, line = 1; + if (expr) { + for (i = 0; i < column; i ++) { + if (expr[i] == '\n') { + line ++; + } + } + + ecs_strbuf_append(&msg_buf, "%d: ", line); + } -static -bool http_socket_is_valid( - ecs_http_socket_t sock) -{ -#if defined(ECS_TARGET_WINDOWS) - return sock != INVALID_SOCKET; -#else - return sock >= 0; -#endif -} + ecs_strbuf_vappend(&msg_buf, fmt, args); -#if defined(ECS_TARGET_WINDOWS) -#define HTTP_SOCKET_INVALID INVALID_SOCKET -#else -#define HTTP_SOCKET_INVALID (-1) -#endif + if (expr) { + ecs_strbuf_appendch(&msg_buf, '\n'); -static -void http_close( - ecs_http_socket_t *sock) -{ - ecs_assert(sock != NULL, ECS_INTERNAL_ERROR, NULL); + /* Find start of line by taking column and looking for the + * last occurring newline */ + if (column != -1) { + const char *ptr = &expr[column]; + if (ptr[0] == '\n') { + ptr --; + } -#if defined(ECS_TARGET_WINDOWS) - closesocket(*sock); -#else - ecs_dbg_2("http: closing socket %u", *sock); - shutdown(*sock, SHUT_RDWR); - close(*sock); -#endif - *sock = HTTP_SOCKET_INVALID; -} + while (ptr[0] != '\n' && ptr > expr) { + ptr --; + } + + if (ptr[0] == '\n') { + ptr ++; + } -static -ecs_http_socket_t http_accept( - ecs_http_socket_t sock, - struct sockaddr* addr, - ecs_size_t *addr_len) -{ - socklen_t len = (socklen_t)addr_len[0]; - ecs_http_socket_t result = accept(sock, addr, &len); - addr_len[0] = (ecs_size_t)len; - return result; -} + if (ptr == expr) { + /* ptr is already at start of line */ + } else { + column -= (int32_t)(ptr - expr); + expr = ptr; + } + } -static -void http_reply_fini(ecs_http_reply_t* reply) { - ecs_assert(reply != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_os_free(reply->body.content); -} + /* Strip newlines from current statement, if any */ + char *newline_ptr = strchr(expr, '\n'); + if (newline_ptr) { + /* Strip newline from expr */ + ecs_strbuf_appendstrn(&msg_buf, expr, + (int32_t)(newline_ptr - expr)); + } else { + ecs_strbuf_appendstr(&msg_buf, expr); + } -static -void http_request_fini(ecs_http_request_impl_t *req) { - ecs_assert(req != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(req->pub.conn != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(req->pub.conn->server != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(req->pub.conn->id == req->conn_id, ECS_INTERNAL_ERROR, NULL); - ecs_os_free(req->res); - flecs_sparse_remove_t(&req->pub.conn->server->requests, - ecs_http_request_impl_t, req->pub.id); -} + ecs_strbuf_appendch(&msg_buf, '\n'); -static -void http_connection_free(ecs_http_connection_impl_t *conn) { - ecs_assert(conn != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(conn->pub.id != 0, ECS_INTERNAL_ERROR, NULL); - uint64_t conn_id = conn->pub.id; + if (column != -1) { + int32_t c; + for (c = 0; c < column; c ++) { + ecs_strbuf_appendch(&msg_buf, ' '); + } + ecs_strbuf_appendch(&msg_buf, '^'); + } + } - if (http_socket_is_valid(conn->sock)) { - http_close(&conn->sock); + char *msg = ecs_strbuf_get(&msg_buf); + ecs_os_err(name, 0, msg); + ecs_os_free(msg); } - - flecs_sparse_remove_t(&conn->pub.server->connections, - ecs_http_connection_impl_t, conn_id); -} - -// https://stackoverflow.com/questions/10156409/convert-hex-string-char-to-int -static -char http_hex_2_int(char a, char b){ - a = (a <= '9') ? (char)(a - '0') : (char)((a & 0x7) + 9); - b = (b <= '9') ? (char)(b - '0') : (char)((b & 0x7) + 9); - return (char)((a << 4) + b); } -static -void http_decode_url_str( - char *str) +void ecs_parser_error_( + const char *name, + const char *expr, + int64_t column, + const char *fmt, + ...) { - char ch, *ptr, *dst = str; - for (ptr = str; (ch = *ptr); ptr++) { - if (ch == '%') { - dst[0] = http_hex_2_int(ptr[1], ptr[2]); - dst ++; - ptr += 2; - } else { - dst[0] = ptr[0]; - dst ++; - } + if (ecs_os_api.log_level_ >= -2) { + va_list args; + va_start(args, fmt); + ecs_parser_errorv_(name, expr, column, fmt, args); + va_end(args); } - dst[0] = '\0'; } -static -void http_parse_method( - ecs_http_fragment_t *frag) +void ecs_abort_( + int32_t err, + const char *file, + int32_t line, + const char *fmt, + ...) { - char *method = ecs_strbuf_get_small(&frag->buf); - if (!ecs_os_strcmp(method, "GET")) frag->method = EcsHttpGet; - else if (!ecs_os_strcmp(method, "POST")) frag->method = EcsHttpPost; - else if (!ecs_os_strcmp(method, "PUT")) frag->method = EcsHttpPut; - else if (!ecs_os_strcmp(method, "DELETE")) frag->method = EcsHttpDelete; - else if (!ecs_os_strcmp(method, "OPTIONS")) frag->method = EcsHttpOptions; - else { - frag->method = EcsHttpMethodUnsupported; - frag->invalid = true; + if (fmt) { + va_list args; + va_start(args, fmt); + char *msg = flecs_vasprintf(fmt, args); + va_end(args); + ecs_fatal_(file, line, "%s (%s)", msg, ecs_strerror(err)); + ecs_os_free(msg); + } else { + ecs_fatal_(file, line, "%s", ecs_strerror(err)); } - ecs_strbuf_reset(&frag->buf); + ecs_os_api.log_last_error_ = err; } -static -bool http_header_writable( - ecs_http_fragment_t *frag) +void ecs_assert_log_( + int32_t err, + const char *cond_str, + const char *file, + int32_t line, + const char *fmt, + ...) { - return frag->header_count < ECS_HTTP_HEADER_COUNT_MAX; + if (fmt) { + va_list args; + va_start(args, fmt); + char *msg = flecs_vasprintf(fmt, args); + va_end(args); + ecs_fatal_(file, line, "assert: %s %s (%s)", + cond_str, msg, ecs_strerror(err)); + ecs_os_free(msg); + } else { + ecs_fatal_(file, line, "assert: %s %s", + cond_str, ecs_strerror(err)); + } + ecs_os_api.log_last_error_ = err; } -static -void http_header_buf_reset( - ecs_http_fragment_t *frag) +void ecs_deprecated_( + const char *file, + int32_t line, + const char *msg) { - frag->header_buf[0] = '\0'; - frag->header_buf_ptr = frag->header_buf; + ecs_err_(file, line, "%s", msg); } -static -void http_header_buf_append( - ecs_http_fragment_t *frag, - char ch) -{ - if ((frag->header_buf_ptr - frag->header_buf) < - ECS_SIZEOF(frag->header_buf)) - { - frag->header_buf_ptr[0] = ch; - frag->header_buf_ptr ++; - } else { - frag->header_buf_ptr[0] = '\0'; +bool ecs_should_log(int32_t level) { +# if !defined(FLECS_LOG_3) + if (level == 3) { + return false; } -} +# endif +# if !defined(FLECS_LOG_2) + if (level == 2) { + return false; + } +# endif +# if !defined(FLECS_LOG_1) + if (level == 1) { + return false; + } +# endif -static -uint64_t http_request_key_hash(const void *ptr) { - const ecs_http_request_key_t *key = ptr; - const char *array = key->array; - int32_t count = key->count; - return flecs_hash(array, count * ECS_SIZEOF(char)); + return level <= ecs_os_api.log_level_; } -static -int http_request_key_compare(const void *ptr_1, const void *ptr_2) { - const ecs_http_request_key_t *type_1 = ptr_1; - const ecs_http_request_key_t *type_2 = ptr_2; - - int32_t count_1 = type_1->count; - int32_t count_2 = type_2->count; +#define ECS_ERR_STR(code) case code: return &(#code[4]) - if (count_1 != count_2) { - return (count_1 > count_2) - (count_1 < count_2); +const char* ecs_strerror( + int32_t error_code) +{ + switch (error_code) { + ECS_ERR_STR(ECS_INVALID_PARAMETER); + ECS_ERR_STR(ECS_NOT_A_COMPONENT); + ECS_ERR_STR(ECS_INTERNAL_ERROR); + ECS_ERR_STR(ECS_ALREADY_DEFINED); + ECS_ERR_STR(ECS_INVALID_COMPONENT_SIZE); + ECS_ERR_STR(ECS_INVALID_COMPONENT_ALIGNMENT); + ECS_ERR_STR(ECS_NAME_IN_USE); + ECS_ERR_STR(ECS_OUT_OF_MEMORY); + ECS_ERR_STR(ECS_DOUBLE_FREE); + ECS_ERR_STR(ECS_OPERATION_FAILED); + ECS_ERR_STR(ECS_INVALID_CONVERSION); + ECS_ERR_STR(ECS_MODULE_UNDEFINED); + ECS_ERR_STR(ECS_MISSING_SYMBOL); + ECS_ERR_STR(ECS_ALREADY_IN_USE); + ECS_ERR_STR(ECS_CYCLE_DETECTED); + ECS_ERR_STR(ECS_LEAK_DETECTED); + ECS_ERR_STR(ECS_COLUMN_INDEX_OUT_OF_RANGE); + ECS_ERR_STR(ECS_COLUMN_IS_NOT_SHARED); + ECS_ERR_STR(ECS_COLUMN_IS_SHARED); + ECS_ERR_STR(ECS_COLUMN_TYPE_MISMATCH); + ECS_ERR_STR(ECS_INVALID_WHILE_READONLY); + ECS_ERR_STR(ECS_INVALID_FROM_WORKER); + ECS_ERR_STR(ECS_OUT_OF_RANGE); + ECS_ERR_STR(ECS_MISSING_OS_API); + ECS_ERR_STR(ECS_UNSUPPORTED); + ECS_ERR_STR(ECS_ACCESS_VIOLATION); + ECS_ERR_STR(ECS_COMPONENT_NOT_REGISTERED); + ECS_ERR_STR(ECS_INCONSISTENT_COMPONENT_ID); + ECS_ERR_STR(ECS_INCONSISTENT_NAME); + ECS_ERR_STR(ECS_INCONSISTENT_COMPONENT_ACTION); + ECS_ERR_STR(ECS_INVALID_OPERATION); + ECS_ERR_STR(ECS_CONSTRAINT_VIOLATED); + ECS_ERR_STR(ECS_LOCKED_STORAGE); + ECS_ERR_STR(ECS_ID_IN_USE); } - return ecs_os_memcmp(type_1->array, type_2->array, count_1); + return "unknown error code"; } -static -ecs_http_request_entry_t* http_find_request_entry( - ecs_http_server_t *srv, - const char *array, - int32_t count) -{ - ecs_http_request_key_t key; - key.array = array; - key.count = count; +#else - ecs_time_t t = {0, 0}; - ecs_http_request_entry_t *entry = flecs_hashmap_get( - &srv->request_cache, &key, ecs_http_request_entry_t); +/* Empty bodies for when logging is disabled */ - if (entry) { - ecs_ftime_t tf = (ecs_ftime_t)ecs_time_measure(&t); - if ((tf - entry->time) < ECS_HTTP_CACHE_TIMEOUT) { - return entry; - } - } - return NULL; +void ecs_log_( + int32_t level, + const char *file, + int32_t line, + const char *fmt, + ...) +{ + (void)level; + (void)file; + (void)line; + (void)fmt; } -static -void http_insert_request_entry( - ecs_http_server_t *srv, - ecs_http_request_impl_t *req, - ecs_http_reply_t *reply) +void ecs_parser_error_( + const char *name, + const char *expr, + int64_t column, + const char *fmt, + ...) { - int32_t content_length = ecs_strbuf_written(&reply->body); - if (!content_length) { - return; - } - - ecs_http_request_key_t key; - key.array = req->res; - key.count = req->req_len; - ecs_http_request_entry_t *entry = flecs_hashmap_get( - &srv->request_cache, &key, ecs_http_request_entry_t); - if (!entry) { - flecs_hashmap_result_t elem = flecs_hashmap_ensure( - &srv->request_cache, &key, ecs_http_request_entry_t); - ecs_http_request_key_t *elem_key = elem.key; - elem_key->array = ecs_os_memdup_n(key.array, char, key.count); - entry = elem.value; - } else { - ecs_os_free(entry->content); - } - - ecs_time_t t = {0, 0}; - entry->time = (ecs_ftime_t)ecs_time_measure(&t); - entry->content_length = ecs_strbuf_written(&reply->body); - entry->content = ecs_strbuf_get(&reply->body); - ecs_strbuf_appendstrn(&reply->body, - entry->content, entry->content_length); + (void)name; + (void)expr; + (void)column; + (void)fmt; } -static -char* http_decode_request( - ecs_http_request_impl_t *req, - ecs_http_fragment_t *frag) +void ecs_parser_errorv_( + const char *name, + const char *expr, + int64_t column, + const char *fmt, + va_list args) { - ecs_os_zeromem(req); - - char *res = ecs_strbuf_get(&frag->buf); - if (!res) { - return NULL; - } + (void)name; + (void)expr; + (void)column; + (void)fmt; + (void)args; +} - req->pub.method = frag->method; - req->pub.path = res + 1; - http_decode_url_str(req->pub.path); +void ecs_abort_( + int32_t error_code, + const char *file, + int32_t line, + const char *fmt, + ...) +{ + (void)error_code; + (void)file; + (void)line; + (void)fmt; +} - if (frag->body_offset) { - req->pub.body = &res[frag->body_offset]; - } - int32_t i, count = frag->header_count; - for (i = 0; i < count; i ++) { - req->pub.headers[i].key = &res[frag->header_offsets[i]]; - req->pub.headers[i].value = &res[frag->header_value_offsets[i]]; - } - count = frag->param_count; - for (i = 0; i < count; i ++) { - req->pub.params[i].key = &res[frag->param_offsets[i]]; - req->pub.params[i].value = &res[frag->param_value_offsets[i]]; - /* Safe, member is only const so that end-user can't change it */ - http_decode_url_str(ECS_CONST_CAST(char*, req->pub.params[i].value)); - } +void ecs_assert_log_( + int32_t error_code, + const char *condition_str, + const char *file, + int32_t line, + const char *fmt, + ...) +{ + (void)error_code; + (void)condition_str; + (void)file; + (void)line; + (void)fmt; +} - req->pub.header_count = frag->header_count; - req->pub.param_count = frag->param_count; - req->res = res; - req->req_len = frag->header_offsets[0]; +#endif - return res; +int ecs_log_get_level(void) { + return ecs_os_api.log_level_; } -static -ecs_http_request_entry_t* http_enqueue_request( - ecs_http_connection_impl_t *conn, - uint64_t conn_id, - ecs_http_fragment_t *frag) +int ecs_log_set_level( + int level) { - ecs_http_server_t *srv = conn->pub.server; + int prev = ecs_os_api.log_level_; + ecs_os_api.log_level_ = level; + return prev; +} - ecs_os_mutex_lock(srv->lock); - bool is_alive = conn->pub.id == conn_id; - - if (!is_alive || frag->invalid) { - /* Don't enqueue invalid requests or requests for purged connections */ - ecs_strbuf_reset(&frag->buf); - } else { - ecs_http_request_impl_t req; - char *res = http_decode_request(&req, frag); - if (res) { - req.pub.conn = (ecs_http_connection_t*)conn; - - /* Check cache for GET requests */ - if (frag->method == EcsHttpGet) { - ecs_http_request_entry_t *entry = - http_find_request_entry(srv, res, frag->header_offsets[0]); - if (entry) { - /* If an entry is found, don't enqueue a request. Instead - * return the cached response immediately. */ - ecs_os_free(res); - return entry; - } - } - - ecs_http_request_impl_t *req_ptr = flecs_sparse_add_t( - &srv->requests, ecs_http_request_impl_t); - *req_ptr = req; - req_ptr->pub.id = flecs_sparse_last_id(&srv->requests); - req_ptr->conn_id = conn->pub.id; - ecs_os_linc(&ecs_http_request_received_count); - } - } +bool ecs_log_enable_colors( + bool enabled) +{ + bool prev = ecs_os_api.flags_ & EcsOsApiLogWithColors; + ECS_BIT_COND(ecs_os_api.flags_, EcsOsApiLogWithColors, enabled); + return prev; +} - ecs_os_mutex_unlock(srv->lock); - return NULL; +bool ecs_log_enable_timestamp( + bool enabled) +{ + bool prev = ecs_os_api.flags_ & EcsOsApiLogWithTimeStamp; + ECS_BIT_COND(ecs_os_api.flags_, EcsOsApiLogWithTimeStamp, enabled); + return prev; } -static -bool http_parse_request( - ecs_http_fragment_t *frag, - const char* req_frag, - ecs_size_t req_frag_len) +bool ecs_log_enable_timedelta( + bool enabled) { - int32_t i; - for (i = 0; i < req_frag_len; i++) { - char c = req_frag[i]; - switch (frag->state) { - case HttpFragStateBegin: - ecs_os_memset_t(frag, 0, ecs_http_fragment_t); - frag->buf.max = ECS_HTTP_METHOD_LEN_MAX; - frag->state = HttpFragStateMethod; - frag->header_buf_ptr = frag->header_buf; - - /* fall through */ - case HttpFragStateMethod: - if (c == ' ') { - http_parse_method(frag); - frag->state = HttpFragStatePath; - frag->buf.max = ECS_HTTP_REQUEST_LEN_MAX; - } else { - ecs_strbuf_appendch(&frag->buf, c); - } - break; - case HttpFragStatePath: - if (c == ' ') { - frag->state = HttpFragStateVersion; - ecs_strbuf_appendch(&frag->buf, '\0'); - } else { - if (c == '?' || c == '=' || c == '&') { - ecs_strbuf_appendch(&frag->buf, '\0'); - int32_t offset = ecs_strbuf_written(&frag->buf); - if (c == '?' || c == '&') { - frag->param_offsets[frag->param_count] = offset; - } else { - frag->param_value_offsets[frag->param_count] = offset; - frag->param_count ++; - } - } else { - ecs_strbuf_appendch(&frag->buf, c); - } - } - break; - case HttpFragStateVersion: - if (c == '\r') { - frag->state = HttpFragStateCR; - } /* version is not stored */ - break; - case HttpFragStateHeaderStart: - if (http_header_writable(frag)) { - frag->header_offsets[frag->header_count] = - ecs_strbuf_written(&frag->buf); - } - http_header_buf_reset(frag); - frag->state = HttpFragStateHeaderName; + bool prev = ecs_os_api.flags_ & EcsOsApiLogWithTimeDelta; + ECS_BIT_COND(ecs_os_api.flags_, EcsOsApiLogWithTimeDelta, enabled); + return prev; +} - /* fall through */ - case HttpFragStateHeaderName: - if (c == ':') { - frag->state = HttpFragStateHeaderValueStart; - http_header_buf_append(frag, '\0'); - frag->parse_content_length = !ecs_os_strcmp( - frag->header_buf, "Content-Length"); +int ecs_log_last_error(void) +{ + int result = ecs_os_api.log_last_error_; + ecs_os_api.log_last_error_ = 0; + return result; +} - if (http_header_writable(frag)) { - ecs_strbuf_appendch(&frag->buf, '\0'); - frag->header_value_offsets[frag->header_count] = - ecs_strbuf_written(&frag->buf); - } - } else if (c == '\r') { - frag->state = HttpFragStateCR; - } else { - http_header_buf_append(frag, c); - if (http_header_writable(frag)) { - ecs_strbuf_appendch(&frag->buf, c); - } - } - break; - case HttpFragStateHeaderValueStart: - http_header_buf_reset(frag); - frag->state = HttpFragStateHeaderValue; - if (c == ' ') { /* skip first space */ - break; - } +/** + * @file addons/metrics.c + * @brief Metrics addon. + */ - /* fall through */ - case HttpFragStateHeaderValue: - if (c == '\r') { - if (frag->parse_content_length) { - http_header_buf_append(frag, '\0'); - int32_t len = atoi(frag->header_buf); - if (len < 0) { - frag->invalid = true; - } else { - frag->content_length = len; - } - frag->parse_content_length = false; - } - if (http_header_writable(frag)) { - int32_t cur = ecs_strbuf_written(&frag->buf); - if (frag->header_offsets[frag->header_count] < cur && - frag->header_value_offsets[frag->header_count] < cur) - { - ecs_strbuf_appendch(&frag->buf, '\0'); - frag->header_count ++; - } - } - frag->state = HttpFragStateCR; - } else { - if (frag->parse_content_length) { - http_header_buf_append(frag, c); - } - if (http_header_writable(frag)) { - ecs_strbuf_appendch(&frag->buf, c); - } - } - break; - case HttpFragStateCR: - if (c == '\n') { - frag->state = HttpFragStateCRLF; - } else { - frag->state = HttpFragStateHeaderStart; - } - break; - case HttpFragStateCRLF: - if (c == '\r') { - frag->state = HttpFragStateCRLFCR; - } else { - frag->state = HttpFragStateHeaderStart; - i--; - } - break; - case HttpFragStateCRLFCR: - if (c == '\n') { - if (frag->content_length != 0) { - frag->body_offset = ecs_strbuf_written(&frag->buf); - frag->state = HttpFragStateBody; - } else { - frag->state = HttpFragStateDone; - } - } else { - frag->state = HttpFragStateHeaderStart; - } - break; - case HttpFragStateBody: { - ecs_strbuf_appendch(&frag->buf, c); - if ((ecs_strbuf_written(&frag->buf) - frag->body_offset) == - frag->content_length) - { - frag->state = HttpFragStateDone; - } - } - break; - case HttpFragStateDone: - break; - } - } - if (frag->state == HttpFragStateDone) { - return true; - } else { - return false; - } -} +#ifdef FLECS_METRICS -static -ecs_http_send_request_t* http_send_queue_post( - ecs_http_server_t *srv) -{ - /* This function should only be called while the server is locked. Before - * the lock is released, the returned element should be populated. */ - ecs_http_send_queue_t *sq = &srv->send_queue; - int32_t next = (sq->head + 1) % ECS_HTTP_SEND_QUEUE_MAX; - if (next == sq->tail) { - return NULL; - } +/* Public components */ +ECS_COMPONENT_DECLARE(FlecsMetrics); +ECS_TAG_DECLARE(EcsMetricInstance); +ECS_COMPONENT_DECLARE(EcsMetricValue); +ECS_COMPONENT_DECLARE(EcsMetricSource); +ECS_TAG_DECLARE(EcsMetric); +ECS_TAG_DECLARE(EcsCounter); +ECS_TAG_DECLARE(EcsCounterIncrement); +ECS_TAG_DECLARE(EcsCounterId); +ECS_TAG_DECLARE(EcsGauge); - /* Don't enqueue new requests if server is shutting down */ - if (!srv->should_run) { - return NULL; - } +/* Internal components */ +static ECS_COMPONENT_DECLARE(EcsMetricMember); +static ECS_COMPONENT_DECLARE(EcsMetricId); +static ECS_COMPONENT_DECLARE(EcsMetricOneOf); +static ECS_COMPONENT_DECLARE(EcsMetricCountIds); +static ECS_COMPONENT_DECLARE(EcsMetricCountTargets); +static ECS_COMPONENT_DECLARE(EcsMetricMemberInstance); +static ECS_COMPONENT_DECLARE(EcsMetricIdInstance); +static ECS_COMPONENT_DECLARE(EcsMetricOneOfInstance); - /* Return element at end of the queue */ - ecs_http_send_request_t *result = &sq->requests[sq->head]; - sq->head = next; - return result; -} +/** Context for metric */ +typedef struct { + ecs_entity_t metric; /**< Metric entity */ + ecs_entity_t kind; /**< Metric kind (gauge, counter) */ +} ecs_metric_ctx_t; -static -ecs_http_send_request_t* http_send_queue_get( - ecs_http_server_t *srv) -{ - ecs_os_mutex_lock(srv->lock); - ecs_http_send_queue_t *sq = &srv->send_queue; - if (sq->tail == sq->head) { - return NULL; - } +/** Context for metric that monitors member */ +typedef struct { + ecs_metric_ctx_t metric; + ecs_primitive_kind_t type_kind; /**< Primitive type kind of member */ + uint16_t offset; /**< Offset of member in component */ +} ecs_member_metric_ctx_t; - int32_t next = (sq->tail + 1) % ECS_HTTP_SEND_QUEUE_MAX; - ecs_http_send_request_t *result = &sq->requests[sq->tail]; - sq->tail = next; - return result; -} +/** Context for metric that monitors whether entity has id */ +typedef struct { + ecs_metric_ctx_t metric; + ecs_id_record_t *idr; /**< Id record for monitored component */ +} ecs_id_metric_ctx_t; -static -void* http_server_send_queue(void* arg) { - ecs_http_server_t *srv = arg; - int32_t wait_ms = srv->send_queue.wait_ms; +/** Context for metric that monitors whether entity has pair target */ +typedef struct { + ecs_metric_ctx_t metric; + ecs_id_record_t *idr; /**< Id record for monitored component */ + ecs_size_t size; /**< Size of metric type */ + ecs_map_t target_offset; /**< Pair target to metric type offset */ +} ecs_oneof_metric_ctx_t; - /* Run for as long as the server is running or there are messages. When the - * server is stopping, no new messages will be enqueued */ - while (srv->should_run || (srv->send_queue.head != srv->send_queue.tail)) { - ecs_http_send_request_t* r = http_send_queue_get(srv); - if (!r) { - ecs_os_mutex_unlock(srv->lock); - /* If the queue is empty, wait so we don't run too fast */ - if (srv->should_run) { - ecs_os_sleep(0, wait_ms * 1000 * 1000); - } - } else { - ecs_http_socket_t sock = r->sock; - char *headers = r->headers; - int32_t headers_length = r->header_length; - char *content = r->content; - int32_t content_length = r->content_length; - ecs_os_mutex_unlock(srv->lock); +/** Context for metric that monitors how many entities have a pair target */ +typedef struct { + ecs_metric_ctx_t metric; + ecs_id_record_t *idr; /**< Id record for monitored component */ + ecs_map_t targets; /**< Map of counters for each target */ +} ecs_count_targets_metric_ctx_t; - if (http_socket_is_valid(sock)) { - bool error = false; +/** Stores context shared for all instances of member metric */ +typedef struct { + ecs_member_metric_ctx_t *ctx; +} EcsMetricMember; - http_sock_nonblock(sock, false); +/** Stores context shared for all instances of id metric */ +typedef struct { + ecs_id_metric_ctx_t *ctx; +} EcsMetricId; - /* Write headers */ - ecs_size_t written = http_send(sock, headers, headers_length, 0); - if (written != headers_length) { - ecs_err("http: failed to write HTTP response headers: %s", - ecs_os_strerror(errno)); - ecs_os_linc(&ecs_http_send_error_count); - error = true; - } else if (content_length >= 0) { - /* Write content */ - written = http_send(sock, content, content_length, 0); - if (written != content_length) { - ecs_err("http: failed to write HTTP response body: %s", - ecs_os_strerror(errno)); - ecs_os_linc(&ecs_http_send_error_count); - error = true; - } - } - if (!error) { - ecs_os_linc(&ecs_http_send_ok_count); - } +/** Stores context shared for all instances of oneof metric */ +typedef struct { + ecs_oneof_metric_ctx_t *ctx; +} EcsMetricOneOf; - http_close(&sock); - } else { - ecs_err("http: invalid socket\n"); - } +/** Stores context shared for all instances of id counter metric */ +typedef struct { + ecs_id_t id; +} EcsMetricCountIds; - ecs_os_free(content); - ecs_os_free(headers); - } - } - return NULL; -} +/** Stores context shared for all instances of target counter metric */ +typedef struct { + ecs_count_targets_metric_ctx_t *ctx; +} EcsMetricCountTargets; -static -void http_append_send_headers( - ecs_strbuf_t *hdrs, - int code, - const char* status, - const char* content_type, - ecs_strbuf_t *extra_headers, - ecs_size_t content_len, - bool preflight) -{ - ecs_strbuf_appendlit(hdrs, "HTTP/1.1 "); - ecs_strbuf_appendint(hdrs, code); - ecs_strbuf_appendch(hdrs, ' '); - ecs_strbuf_appendstr(hdrs, status); - ecs_strbuf_appendlit(hdrs, "\r\n"); +/** Instance of member metric */ +typedef struct { + ecs_ref_t ref; + ecs_member_metric_ctx_t *ctx; +} EcsMetricMemberInstance; - if (content_type) { - ecs_strbuf_appendlit(hdrs, "Content-Type: "); - ecs_strbuf_appendstr(hdrs, content_type); - ecs_strbuf_appendlit(hdrs, "\r\n"); - } +/** Instance of id metric */ +typedef struct { + ecs_record_t *r; + ecs_id_metric_ctx_t *ctx; +} EcsMetricIdInstance; - if (content_len >= 0) { - ecs_strbuf_appendlit(hdrs, "Content-Length: "); - ecs_strbuf_append(hdrs, "%d", content_len); - ecs_strbuf_appendlit(hdrs, "\r\n"); - } +/** Instance of oneof metric */ +typedef struct { + ecs_record_t *r; + ecs_oneof_metric_ctx_t *ctx; +} EcsMetricOneOfInstance; - ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Origin: *\r\n"); - if (preflight) { - ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Private-Network: true\r\n"); - ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Methods: GET, PUT, OPTIONS\r\n"); - ecs_strbuf_appendlit(hdrs, "Access-Control-Max-Age: 600\r\n"); - } +/** Component lifecycle */ - ecs_strbuf_mergebuff(hdrs, extra_headers); +static ECS_DTOR(EcsMetricMember, ptr, { + ecs_os_free(ptr->ctx); +}) - ecs_strbuf_appendlit(hdrs, "\r\n"); -} +static ECS_MOVE(EcsMetricMember, dst, src, { + *dst = *src; + src->ctx = NULL; +}) -static -void http_send_reply( - ecs_http_connection_impl_t* conn, - ecs_http_reply_t* reply, - bool preflight) -{ - ecs_strbuf_t hdrs = ECS_STRBUF_INIT; - char *content = ecs_strbuf_get(&reply->body); - int32_t content_length = reply->body.length - 1; +static ECS_DTOR(EcsMetricId, ptr, { + ecs_os_free(ptr->ctx); +}) - /* Use asynchronous send queue for outgoing data so send operations won't - * hold up main thread */ - ecs_http_send_request_t *req = NULL; +static ECS_MOVE(EcsMetricId, dst, src, { + *dst = *src; + src->ctx = NULL; +}) - if (!preflight) { - req = http_send_queue_post(conn->pub.server); - if (!req) { - reply->code = 503; /* queue full, server is busy */ - ecs_os_linc(&ecs_http_busy_count); - } +static ECS_DTOR(EcsMetricOneOf, ptr, { + if (ptr->ctx) { + ecs_map_fini(&ptr->ctx->target_offset); + ecs_os_free(ptr->ctx); } +}) - http_append_send_headers(&hdrs, reply->code, reply->status, - reply->content_type, &reply->headers, content_length, preflight); - char *headers = ecs_strbuf_get(&hdrs); - ecs_size_t headers_length = ecs_strbuf_written(&hdrs); +static ECS_MOVE(EcsMetricOneOf, dst, src, { + *dst = *src; + src->ctx = NULL; +}) - if (!req) { - ecs_size_t written = http_send(conn->sock, headers, headers_length, 0); - if (written != headers_length) { - ecs_err("http: failed to send reply to '%s:%s': %s", - conn->pub.host, conn->pub.port, ecs_os_strerror(errno)); - ecs_os_linc(&ecs_http_send_error_count); - } - ecs_os_free(content); - ecs_os_free(headers); - http_close(&conn->sock); - return; +static ECS_DTOR(EcsMetricCountTargets, ptr, { + if (ptr->ctx) { + ecs_map_fini(&ptr->ctx->targets); + ecs_os_free(ptr->ctx); } +}) - /* Second, enqueue send request for response body */ - req->sock = conn->sock; - req->headers = headers; - req->header_length = headers_length; - req->content = content; - req->content_length = content_length; +static ECS_MOVE(EcsMetricCountTargets, dst, src, { + *dst = *src; + src->ctx = NULL; +}) - /* Take ownership of values */ - reply->body.content = NULL; - conn->sock = HTTP_SOCKET_INVALID; -} +/** Observer used for creating new instances of member metric */ +static void flecs_metrics_on_member_metric(ecs_iter_t *it) { + ecs_world_t *world = it->world; + ecs_member_metric_ctx_t *ctx = it->ctx; + ecs_id_t id = ecs_field_id(it, 0); -static -void http_recv_connection( - ecs_http_server_t *srv, - ecs_http_connection_impl_t *conn, - uint64_t conn_id, - ecs_http_socket_t sock) -{ - ecs_size_t bytes_read; - char recv_buf[ECS_HTTP_SEND_RECV_BUFFER_SIZE]; - ecs_http_fragment_t frag = {0}; - int32_t retries = 0; - - do { - if ((bytes_read = http_recv( - sock, recv_buf, ECS_SIZEOF(recv_buf), 0)) > 0) - { - bool is_alive = conn->pub.id == conn_id; - if (!is_alive) { - /* Connection has been purged by main thread */ - goto done; - } + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t m = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); - if (http_parse_request(&frag, recv_buf, bytes_read)) { - if (frag.method == EcsHttpOptions) { - ecs_http_reply_t reply; - reply.body = ECS_STRBUF_INIT; - reply.code = 200; - reply.content_type = NULL; - reply.headers = ECS_STRBUF_INIT; - reply.status = "OK"; - http_send_reply(conn, &reply, true); - ecs_os_linc(&ecs_http_request_preflight_count); - } else { - ecs_http_request_entry_t *entry = - http_enqueue_request(conn, conn_id, &frag); - if (entry) { - ecs_http_reply_t reply; - reply.body = ECS_STRBUF_INIT; - reply.code = 200; - reply.content_type = "application/json"; - reply.headers = ECS_STRBUF_INIT; - reply.status = "OK"; - ecs_strbuf_appendstrn(&reply.body, - entry->content, entry->content_length); - http_send_reply(conn, &reply, false); - http_connection_free(conn); + EcsMetricMemberInstance *src = ecs_emplace( + world, m, EcsMetricMemberInstance, NULL); + src->ref = ecs_ref_init_id(world, e, id); + src->ctx = ctx; + ecs_modified(world, m, EcsMetricMemberInstance); + ecs_set(world, m, EcsMetricValue, { 0 }); + ecs_set(world, m, EcsMetricSource, { e }); + ecs_add(world, m, EcsMetricInstance); + ecs_add_pair(world, m, EcsMetric, ctx->metric.kind); + } +} - /* Lock was transferred from enqueue_request */ - ecs_os_mutex_unlock(srv->lock); - } - } - } else { - ecs_os_linc(&ecs_http_request_invalid_count); - } - } +/** Observer used for creating new instances of id metric */ +static void flecs_metrics_on_id_metric(ecs_iter_t *it) { + ecs_world_t *world = it->world; + ecs_id_metric_ctx_t *ctx = it->ctx; - ecs_os_sleep(0, 10 * 1000 * 1000); - } while ((bytes_read == -1) && (++retries < ECS_HTTP_REQUEST_RECV_RETRY)); + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t m = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); - if (retries == ECS_HTTP_REQUEST_RECV_RETRY) { - http_close(&sock); + EcsMetricIdInstance *src = ecs_emplace( + world, m, EcsMetricIdInstance, NULL); + src->r = ecs_record_find(world, e); + src->ctx = ctx; + ecs_modified(world, m, EcsMetricIdInstance); + ecs_set(world, m, EcsMetricValue, { 0 }); + ecs_set(world, m, EcsMetricSource, { e }); + ecs_add(world, m, EcsMetricInstance); + ecs_add_pair(world, m, EcsMetric, ctx->metric.kind); } - -done: - ecs_strbuf_reset(&frag.buf); } -typedef struct { - ecs_http_connection_impl_t *conn; - uint64_t id; -} http_conn_res_t; - -static -http_conn_res_t http_init_connection( - ecs_http_server_t *srv, - ecs_http_socket_t sock_conn, - struct sockaddr_storage *remote_addr, - ecs_size_t remote_addr_len) -{ - http_sock_set_timeout(sock_conn, 100); - http_sock_keep_alive(sock_conn); - http_sock_nonblock(sock_conn, true); +/** Observer used for creating new instances of oneof metric */ +static void flecs_metrics_on_oneof_metric(ecs_iter_t *it) { + if (it->event == EcsOnRemove) { + return; + } - /* Create new connection */ - ecs_os_mutex_lock(srv->lock); - ecs_http_connection_impl_t *conn = flecs_sparse_add_t( - &srv->connections, ecs_http_connection_impl_t); - uint64_t conn_id = conn->pub.id = flecs_sparse_last_id(&srv->connections); - conn->pub.server = srv; - conn->sock = sock_conn; - ecs_os_mutex_unlock(srv->lock); + ecs_world_t *world = it->world; + ecs_oneof_metric_ctx_t *ctx = it->ctx; - char *remote_host = conn->pub.host; - char *remote_port = conn->pub.port; + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t m = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); - /* Fetch name & port info */ - if (http_getnameinfo((struct sockaddr*) remote_addr, remote_addr_len, - remote_host, ECS_SIZEOF(conn->pub.host), - remote_port, ECS_SIZEOF(conn->pub.port), - NI_NUMERICHOST | NI_NUMERICSERV)) - { - ecs_os_strcpy(remote_host, "unknown"); - ecs_os_strcpy(remote_port, "unknown"); + EcsMetricOneOfInstance *src = ecs_emplace( + world, m, EcsMetricOneOfInstance, NULL); + src->r = ecs_record_find(world, e); + src->ctx = ctx; + ecs_modified(world, m, EcsMetricOneOfInstance); + ecs_add_pair(world, m, ctx->metric.metric, ecs_id(EcsMetricValue)); + ecs_set(world, m, EcsMetricSource, { e }); + ecs_add(world, m, EcsMetricInstance); + ecs_add_pair(world, m, EcsMetric, ctx->metric.kind); } - - ecs_dbg_2("http: connection established from '%s:%s' (socket %u)", - remote_host, remote_port, sock_conn); - - return (http_conn_res_t){ .conn = conn, .id = conn_id }; } -static -void http_accept_connections( - ecs_http_server_t* srv, - const struct sockaddr* addr, - ecs_size_t addr_len) -{ -#ifdef ECS_TARGET_WINDOWS - /* If on Windows, test if winsock needs to be initialized */ - SOCKET testsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (INVALID_SOCKET == testsocket && WSANOTINITIALISED == WSAGetLastError()){ - WSADATA data = { 0 }; - int result = WSAStartup(MAKEWORD(2, 2), &data); - if (result) { - ecs_warn("http: WSAStartup failed with GetLastError = %d\n", - GetLastError()); - return; +/** Set doc name of metric instance to name of source entity */ +#ifdef FLECS_DOC +static void SetMetricDocName(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsMetricSource *src = ecs_field(it, EcsMetricSource, 0); + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t src_e = src[i].entity; + const char *name = ecs_get_name(world, src_e); + if (name) { + ecs_doc_set_name(world, it->entities[i], name); } - } else { - http_close(&testsocket); } +} #endif - /* Resolve name + port (used for logging) */ - char addr_host[256]; - char addr_port[20]; - - ecs_http_socket_t sock = HTTP_SOCKET_INVALID; - ecs_assert(srv->sock == HTTP_SOCKET_INVALID, ECS_INTERNAL_ERROR, NULL); +/** Delete metric instances for entities that are no longer alive */ +static void ClearMetricInstance(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsMetricSource *src = ecs_field(it, EcsMetricSource, 0); - if (http_getnameinfo( - addr, addr_len, addr_host, ECS_SIZEOF(addr_host), addr_port, - ECS_SIZEOF(addr_port), NI_NUMERICHOST | NI_NUMERICSERV)) - { - ecs_os_strcpy(addr_host, "unknown"); - ecs_os_strcpy(addr_port, "unknown"); + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t src_e = src[i].entity; + if (!ecs_is_alive(world, src_e)) { + ecs_delete(world, it->entities[i]); + } } +} - ecs_os_mutex_lock(srv->lock); - if (srv->should_run) { - ecs_dbg_2("http: initializing connection socket"); - - sock = socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP); - if (!http_socket_is_valid(sock)) { - ecs_err("http: unable to create new connection socket: %s", - ecs_os_strerror(errno)); - ecs_os_mutex_unlock(srv->lock); - goto done; - } +/** Update member metric */ +static void UpdateMemberInstance(ecs_iter_t *it, bool counter) { + ecs_world_t *world = it->real_world; + EcsMetricValue *m = ecs_field(it, EcsMetricValue, 0); + EcsMetricMemberInstance *mi = ecs_field(it, EcsMetricMemberInstance, 1); + ecs_ftime_t dt = it->delta_time; - int reuse = 1, result; - result = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (char*)&reuse, ECS_SIZEOF(reuse)); - if (result) { - ecs_warn("http: failed to setsockopt: %s", ecs_os_strerror(errno)); + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_member_metric_ctx_t *ctx = mi[i].ctx; + ecs_ref_t *ref = &mi[i].ref; + if (!ref->entity) { + continue; } - if (addr->sa_family == AF_INET6) { - int ipv6only = 0; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, - (char*)&ipv6only, ECS_SIZEOF(ipv6only))) - { - ecs_warn("http: failed to setsockopt: %s", - ecs_os_strerror(errno)); + const void *ptr = ecs_ref_get_id(world, ref, ref->id); + if (ptr) { + ptr = ECS_OFFSET(ptr, ctx->offset); + if (!counter) { + m[i].value = ecs_meta_ptr_to_float(ctx->type_kind, ptr); + } else { + m[i].value += + ecs_meta_ptr_to_float(ctx->type_kind, ptr) * (double)dt; } + } else { + ecs_delete(it->world, it->entities[i]); } + } +} - result = http_bind(sock, addr, addr_len); - if (result) { - ecs_err("http: failed to bind to '%s:%s': %s", - addr_host, addr_port, ecs_os_strerror(errno)); - ecs_os_mutex_unlock(srv->lock); - goto done; - } - - http_sock_set_timeout(sock, 1000); - - srv->sock = sock; +static void UpdateGaugeMemberInstance(ecs_iter_t *it) { + UpdateMemberInstance(it, false); +} - result = listen(srv->sock, SOMAXCONN); - if (result) { - ecs_warn("http: could not listen for SOMAXCONN (%d) connections: %s", - SOMAXCONN, ecs_os_strerror(errno)); - } +static void UpdateCounterMemberInstance(ecs_iter_t *it) { + UpdateMemberInstance(it, false); +} - ecs_trace("http: listening for incoming connections on '%s:%s'", - addr_host, addr_port); - } else { - ecs_dbg_2("http: server shut down while initializing"); - } - ecs_os_mutex_unlock(srv->lock); +static void UpdateCounterIncrementMemberInstance(ecs_iter_t *it) { + UpdateMemberInstance(it, true); +} - struct sockaddr_storage remote_addr; - ecs_size_t remote_addr_len = 0; +/** Update id metric */ +static void UpdateIdInstance(ecs_iter_t *it, bool counter) { + ecs_world_t *world = it->real_world; + EcsMetricValue *m = ecs_field(it, EcsMetricValue, 0); + EcsMetricIdInstance *mi = ecs_field(it, EcsMetricIdInstance, 1); + ecs_ftime_t dt = it->delta_time; - while (srv->should_run) { - remote_addr_len = ECS_SIZEOF(remote_addr); - ecs_http_socket_t sock_conn = http_accept(srv->sock, (struct sockaddr*) &remote_addr, - &remote_addr_len); + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_record_t *r = mi[i].r; + if (!r) { + continue; + } - if (!http_socket_is_valid(sock_conn)) { - if (srv->should_run) { - ecs_dbg("http: connection attempt failed: %s", - ecs_os_strerror(errno)); - } + ecs_table_t *table = r->table; + if (!table) { + ecs_delete(it->world, it->entities[i]); continue; } - http_conn_res_t conn = http_init_connection(srv, sock_conn, &remote_addr, remote_addr_len); - http_recv_connection(srv, conn.conn, conn.id, sock_conn); + ecs_id_metric_ctx_t *ctx = mi[i].ctx; + ecs_id_record_t *idr = ctx->idr; + if (flecs_search_w_idr(world, table, NULL, idr) != -1) { + if (!counter) { + m[i].value = 1.0; + } else { + m[i].value += 1.0 * (double)dt; + } + } else { + ecs_delete(it->world, it->entities[i]); + } } +} -done: - ecs_os_mutex_lock(srv->lock); - if (http_socket_is_valid(sock) && errno != EBADF) { - http_close(&sock); - srv->sock = sock; - } - ecs_os_mutex_unlock(srv->lock); +static void UpdateGaugeIdInstance(ecs_iter_t *it) { + UpdateIdInstance(it, false); +} - ecs_trace("http: no longer accepting connections on '%s:%s'", - addr_host, addr_port); +static void UpdateCounterIdInstance(ecs_iter_t *it) { + UpdateIdInstance(it, true); } -static -void* http_server_thread(void* arg) { - ecs_http_server_t *srv = arg; - struct sockaddr_in addr; - ecs_os_zeromem(&addr); - addr.sin_family = AF_INET; - addr.sin_port = htons(srv->port); +/** Update oneof metric */ +static void UpdateOneOfInstance(ecs_iter_t *it, bool counter) { + ecs_world_t *world = it->real_world; + ecs_table_t *table = it->table; + void *m = ecs_table_get_column(table, it->trs[0]->column, it->offset); + EcsMetricOneOfInstance *mi = ecs_field(it, EcsMetricOneOfInstance, 1); + ecs_ftime_t dt = it->delta_time; - if (!srv->ipaddr) { - addr.sin_addr.s_addr = htonl(INADDR_ANY); - } else { - inet_pton(AF_INET, srv->ipaddr, &(addr.sin_addr)); - } + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_oneof_metric_ctx_t *ctx = mi[i].ctx; + ecs_record_t *r = mi[i].r; + if (!r) { + continue; + } - http_accept_connections(srv, (struct sockaddr*)&addr, ECS_SIZEOF(addr)); - return NULL; -} + ecs_table_t *mtable = r->table; -static -void http_do_request( - ecs_http_server_t *srv, - ecs_http_reply_t *reply, - const ecs_http_request_impl_t *req) -{ - if (srv->callback(ECS_CONST_CAST(ecs_http_request_t*, req), reply, - srv->ctx) == false) - { - reply->code = 404; - reply->status = "Resource not found"; - ecs_os_linc(&ecs_http_request_not_handled_count); - } else { - if (reply->code >= 400) { - ecs_os_linc(&ecs_http_request_handled_error_count); - } else { - ecs_os_linc(&ecs_http_request_handled_ok_count); + double *value = ECS_ELEM(m, ctx->size, i); + if (!counter) { + ecs_os_memset(value, 0, ctx->size); } - } -} -static -void http_handle_request( - ecs_http_server_t *srv, - ecs_http_request_impl_t *req) -{ - ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; - ecs_http_connection_impl_t *conn = - (ecs_http_connection_impl_t*)req->pub.conn; + if (!mtable) { + ecs_delete(it->world, it->entities[i]); + continue; + } - if (req->pub.method != EcsHttpOptions) { - if (srv->callback((ecs_http_request_t*)req, &reply, srv->ctx) == false) { - reply.code = 404; - reply.status = "Resource not found"; - ecs_os_linc(&ecs_http_request_not_handled_count); - } else { - if (reply.code >= 400) { - ecs_os_linc(&ecs_http_request_handled_error_count); - } else { - ecs_os_linc(&ecs_http_request_handled_ok_count); - } + ecs_id_record_t *idr = ctx->idr; + ecs_id_t id; + if (flecs_search_w_idr(world, mtable, &id, idr) == -1) { + ecs_delete(it->world, it->entities[i]); + continue; } - if (req->pub.method == EcsHttpGet) { - http_insert_request_entry(srv, req, &reply); + ecs_entity_t tgt = ECS_PAIR_SECOND(id); + uint64_t *offset = ecs_map_get(&ctx->target_offset, tgt); + if (!offset) { + ecs_err("unexpected relationship target for metric"); + continue; } - http_send_reply(conn, &reply, false); - ecs_dbg_2("http: reply sent to '%s:%s'", conn->pub.host, conn->pub.port); - } else { - /* Already taken care of */ + value = ECS_OFFSET(value, *offset); + + if (!counter) { + *value = 1.0; + } else { + *value += 1.0 * (double)dt; + } } +} - http_reply_fini(&reply); - http_request_fini(req); - http_connection_free(conn); +static void UpdateGaugeOneOfInstance(ecs_iter_t *it) { + UpdateOneOfInstance(it, false); } -static -void http_purge_request_cache( - ecs_http_server_t *srv, - bool fini) -{ - ecs_time_t t = {0, 0}; - ecs_ftime_t time = (ecs_ftime_t)ecs_time_measure(&t); - ecs_map_iter_t it = ecs_map_iter(&srv->request_cache.impl); - while (ecs_map_next(&it)) { - ecs_hm_bucket_t *bucket = ecs_map_ptr(&it); - int32_t i, count = ecs_vec_count(&bucket->values); - ecs_http_request_key_t *keys = ecs_vec_first(&bucket->keys); - ecs_http_request_entry_t *entries = ecs_vec_first(&bucket->values); - for (i = count - 1; i >= 0; i --) { - ecs_http_request_entry_t *entry = &entries[i]; - if (fini || ((time - entry->time) > ECS_HTTP_CACHE_PURGE_TIMEOUT)) { - ecs_http_request_key_t *key = &keys[i]; - /* Safe, code owns the value */ - ecs_os_free(ECS_CONST_CAST(char*, key->array)); - ecs_os_free(entry->content); - flecs_hm_bucket_remove(&srv->request_cache, bucket, - ecs_map_key(&it), i); +static void UpdateCounterOneOfInstance(ecs_iter_t *it) { + UpdateOneOfInstance(it, true); +} + +static void UpdateCountTargets(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + EcsMetricCountTargets *m = ecs_field(it, EcsMetricCountTargets, 0); + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_count_targets_metric_ctx_t *ctx = m[i].ctx; + ecs_id_record_t *cur = ctx->idr; + while ((cur = cur->first.next)) { + ecs_id_t id = cur->id; + ecs_entity_t *mi = ecs_map_ensure(&ctx->targets, id); + if (!mi[0]) { + mi[0] = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); + ecs_entity_t tgt = ecs_pair_second(world, cur->id); + const char *name = ecs_get_name(world, tgt); + if (name) { + ecs_set_name(world, mi[0], name); + } + + EcsMetricSource *source = ecs_ensure( + world, mi[0], EcsMetricSource); + source->entity = tgt; } + + EcsMetricValue *value = ecs_ensure(world, mi[0], EcsMetricValue); + value->value += (double)ecs_count_id(world, cur->id) * + (double)it->delta_system_time; } } +} - if (fini) { - flecs_hashmap_fini(&srv->request_cache); +static void UpdateCountIds(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + EcsMetricCountIds *m = ecs_field(it, EcsMetricCountIds, 0); + EcsMetricValue *v = ecs_field(it, EcsMetricValue, 1); + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + v[i].value += (double)ecs_count_id(world, m[i].id) * + (double)it->delta_system_time; } } +/** Initialize member metric */ static -int32_t http_dequeue_requests( - ecs_http_server_t *srv, - double delta_time) +int flecs_member_metric_init( + ecs_world_t *world, + ecs_entity_t metric, + const ecs_metric_desc_t *desc) { - ecs_os_mutex_lock(srv->lock); - - int32_t i, request_count = flecs_sparse_count(&srv->requests); - for (i = request_count - 1; i >= 1; i --) { - ecs_http_request_impl_t *req = flecs_sparse_get_dense_t( - &srv->requests, ecs_http_request_impl_t, i); - http_handle_request(srv, req); - } + ecs_entity_t type = 0, member_type = 0, member = 0, id = 0; + uintptr_t offset = 0; - int32_t connections_count = flecs_sparse_count(&srv->connections); - for (i = connections_count - 1; i >= 1; i --) { - ecs_http_connection_impl_t *conn = flecs_sparse_get_dense_t( - &srv->connections, ecs_http_connection_impl_t, i); + if (desc->dotmember) { + if (!desc->id) { + char *metric_name = ecs_get_path(world, metric); + ecs_err("missing id for metric '%s' with member '%s", + metric_name, desc->dotmember); + ecs_os_free(metric_name); + goto error; + } - conn->dequeue_timeout += delta_time; - conn->dequeue_retries ++; - - if ((conn->dequeue_timeout > - (double)ECS_HTTP_CONNECTION_PURGE_TIMEOUT) && - (conn->dequeue_retries > ECS_HTTP_CONNECTION_PURGE_RETRY_COUNT)) - { - ecs_dbg("http: purging connection '%s:%s' (sock = %d)", - conn->pub.host, conn->pub.port, conn->sock); - http_connection_free(conn); + if (desc->member) { + char *metric_name = ecs_get_path(world, metric); + ecs_err("cannot set both member and dotmember for metric '%s'", + metric_name); + ecs_os_free(metric_name); + goto error; } - } - http_purge_request_cache(srv, false); - ecs_os_mutex_unlock(srv->lock); - - return request_count - 1; -} + type = ecs_get_typeid(world, desc->id); -const char* ecs_http_get_header( - const ecs_http_request_t* req, - const char* name) -{ - for (ecs_size_t i = 0; i < req->header_count; i++) { - if (!ecs_os_strcmp(req->headers[i].key, name)) { - return req->headers[i].value; + ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, NULL); + if (ecs_meta_push(&cur)) { + char *metric_name = ecs_get_path(world, metric); + ecs_err("invalid type for metric '%s'", metric_name); + ecs_os_free(metric_name); + goto error; } - } - return NULL; -} - -const char* ecs_http_get_param( - const ecs_http_request_t* req, - const char* name) -{ - for (ecs_size_t i = 0; i < req->param_count; i++) { - if (!ecs_os_strcmp(req->params[i].key, name)) { - return req->params[i].value; + if (ecs_meta_dotmember(&cur, desc->dotmember)) { + char *metric_name = ecs_get_path(world, metric); + ecs_err("invalid dotmember '%s' for metric '%s'", + desc->dotmember, metric_name); + ecs_os_free(metric_name); + goto error; } - } - return NULL; -} - -ecs_http_server_t* ecs_http_server_init( - const ecs_http_server_desc_t *desc) -{ - ecs_check(ecs_os_has_threading(), ECS_UNSUPPORTED, - "missing OS API implementation"); - ecs_http_server_t* srv = ecs_os_calloc_t(ecs_http_server_t); - srv->lock = ecs_os_mutex_new(); - srv->sock = HTTP_SOCKET_INVALID; + id = desc->id; + member_type = ecs_meta_get_type(&cur); + offset = (uintptr_t)ecs_meta_get_ptr(&cur); + member = ecs_meta_get_member_id(&cur); + } else { + const EcsMember *m = ecs_get(world, desc->member, EcsMember); + if (!m) { + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); + ecs_err("entity '%s' provided for metric '%s' is not a member", + member_name, metric_name); + ecs_os_free(member_name); + ecs_os_free(metric_name); + goto error; + } - srv->should_run = false; - srv->initialized = true; + type = ecs_get_parent(world, desc->member); + if (!type) { + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); + ecs_err("member '%s' provided for metric '%s' is not part of a type", + member_name, metric_name); + ecs_os_free(member_name); + ecs_os_free(metric_name); + goto error; + } + + id = type; + if (desc->id) { + if (type != ecs_get_typeid(world, desc->id)) { + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); + char *id_name = ecs_get_path(world, desc->id); + ecs_err("member '%s' for metric '%s' is not of type '%s'", + member_name, metric_name, id_name); + ecs_os_free(id_name); + ecs_os_free(member_name); + ecs_os_free(metric_name); + goto error; + } + id = desc->id; + } - srv->callback = desc->callback; - srv->ctx = desc->ctx; - srv->port = desc->port; - srv->ipaddr = desc->ipaddr; - srv->send_queue.wait_ms = desc->send_queue_wait_ms; - if (!srv->send_queue.wait_ms) { - srv->send_queue.wait_ms = 1; + member = desc->member; + member_type = m->type; + offset = flecs_ito(uintptr_t, m->offset); } - flecs_sparse_init_t(&srv->connections, NULL, NULL, ecs_http_connection_impl_t); - flecs_sparse_init_t(&srv->requests, NULL, NULL, ecs_http_request_impl_t); - - /* Start at id 1 */ - flecs_sparse_new_id(&srv->connections); - flecs_sparse_new_id(&srv->requests); - - /* Initialize request cache */ - flecs_hashmap_init(&srv->request_cache, - ecs_http_request_key_t, ecs_http_request_entry_t, - http_request_key_hash, http_request_key_compare, NULL); - -#ifndef ECS_TARGET_WINDOWS - /* Ignore pipe signal. SIGPIPE can occur when a message is sent to a client - * but te client already disconnected. */ - signal(SIGPIPE, SIG_IGN); -#endif - - return srv; -error: - return NULL; -} - -void ecs_http_server_fini( - ecs_http_server_t* srv) -{ - if (srv->should_run) { - ecs_http_server_stop(srv); + const EcsPrimitive *p = ecs_get(world, member_type, EcsPrimitive); + if (!p) { + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); + ecs_err("member '%s' provided for metric '%s' must have primitive type", + member_name, metric_name); + ecs_os_free(member_name); + ecs_os_free(metric_name); + goto error; } - ecs_os_mutex_free(srv->lock); - http_purge_request_cache(srv, true); - flecs_sparse_fini(&srv->requests); - flecs_sparse_fini(&srv->connections); - ecs_os_free(srv); -} - -int ecs_http_server_start( - ecs_http_server_t *srv) -{ - ecs_check(srv != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(srv->initialized, ECS_INVALID_PARAMETER, NULL); - ecs_check(!srv->should_run, ECS_INVALID_PARAMETER, NULL); - ecs_check(!srv->thread, ECS_INVALID_PARAMETER, NULL); - - srv->should_run = true; - - ecs_dbg("http: starting server thread"); - srv->thread = ecs_os_thread_new(http_server_thread, srv); - if (!srv->thread) { + const EcsType *mt = ecs_get(world, type, EcsType); + if (!mt) { + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); + ecs_err("parent of member '%s' for metric '%s' is not a type", + member_name, metric_name); + ecs_os_free(member_name); + ecs_os_free(metric_name); goto error; } - srv->send_queue.thread = ecs_os_thread_new(http_server_send_queue, srv); - if (!srv->send_queue.thread) { + if (mt->kind != EcsStructType) { + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); + ecs_err("parent of member '%s' for metric '%s' is not a struct", + member_name, metric_name); + ecs_os_free(member_name); + ecs_os_free(metric_name); goto error; } + ecs_member_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_member_metric_ctx_t); + ctx->metric.metric = metric; + ctx->metric.kind = desc->kind; + ctx->type_kind = p->kind; + ctx->offset = flecs_uto(uint16_t, offset); + + ecs_observer(world, { + .entity = metric, + .events = { EcsOnAdd }, + .query.terms[0] = { .id = id }, + .callback = flecs_metrics_on_member_metric, + .yield_existing = true, + .ctx = ctx + }); + + ecs_set_pair(world, metric, EcsMetricMember, member, { .ctx = ctx }); + ecs_add_pair(world, metric, EcsMetric, desc->kind); + ecs_add_id(world, metric, EcsMetric); + return 0; error: return -1; } -void ecs_http_server_stop( - ecs_http_server_t* srv) +/** Update id metric */ +static +int flecs_id_metric_init( + ecs_world_t *world, + ecs_entity_t metric, + const ecs_metric_desc_t *desc) { - ecs_check(srv != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(srv->initialized, ECS_INVALID_OPERATION, NULL); - ecs_check(srv->should_run, ECS_INVALID_PARAMETER, NULL); - - /* Stop server thread */ - ecs_dbg("http: shutting down server thread"); - - ecs_os_mutex_lock(srv->lock); - srv->should_run = false; - if (http_socket_is_valid(srv->sock)) { - http_close(&srv->sock); - } - ecs_os_mutex_unlock(srv->lock); - - ecs_os_thread_join(srv->thread); - ecs_os_thread_join(srv->send_queue.thread); - ecs_trace("http: server threads shut down"); - - /* Cleanup all outstanding requests */ - int i, count = flecs_sparse_count(&srv->requests); - for (i = count - 1; i >= 1; i --) { - http_request_fini(flecs_sparse_get_dense_t( - &srv->requests, ecs_http_request_impl_t, i)); - } + ecs_id_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_id_metric_ctx_t); + ctx->metric.metric = metric; + ctx->metric.kind = desc->kind; + ctx->idr = flecs_id_record_ensure(world, desc->id); + ecs_check(ctx->idr != NULL, ECS_INTERNAL_ERROR, NULL); - /* Close all connections */ - count = flecs_sparse_count(&srv->connections); - for (i = count - 1; i >= 1; i --) { - http_connection_free(flecs_sparse_get_dense_t( - &srv->connections, ecs_http_connection_impl_t, i)); - } + ecs_observer(world, { + .entity = metric, + .events = { EcsOnAdd }, + .query.terms[0] = { .id = desc->id }, + .callback = flecs_metrics_on_id_metric, + .yield_existing = true, + .ctx = ctx + }); - ecs_assert(flecs_sparse_count(&srv->connections) == 1, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(flecs_sparse_count(&srv->requests) == 1, - ECS_INTERNAL_ERROR, NULL); + ecs_set(world, metric, EcsMetricId, { .ctx = ctx }); + ecs_add_pair(world, metric, EcsMetric, desc->kind); + ecs_add_id(world, metric, EcsMetric); - srv->thread = 0; + return 0; error: - return; + return -1; } -void ecs_http_server_dequeue( - ecs_http_server_t* srv, - ecs_ftime_t delta_time) +/** Update oneof metric */ +static +int flecs_oneof_metric_init( + ecs_world_t *world, + ecs_entity_t metric, + ecs_entity_t scope, + const ecs_metric_desc_t *desc) { - ecs_check(srv != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(srv->initialized, ECS_INVALID_PARAMETER, NULL); - ecs_check(srv->should_run, ECS_INVALID_PARAMETER, NULL); - - srv->dequeue_timeout += (double)delta_time; - srv->stats_timeout += (double)delta_time; + ecs_oneof_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_oneof_metric_ctx_t); + ctx->metric.metric = metric; + ctx->metric.kind = desc->kind; + ctx->idr = flecs_id_record_ensure(world, desc->id); + ecs_check(ctx->idr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_map_init(&ctx->target_offset, NULL); - if ((1000 * srv->dequeue_timeout) > (double)ECS_HTTP_MIN_DEQUEUE_INTERVAL) { - srv->dequeue_timeout = 0; + /* Add member for each child of oneof to metric, so it can be used as metric + * instance type that holds values for all targets */ + ecs_iter_t it = ecs_children(world, scope); + uint64_t offset = 0; + while (ecs_children_next(&it)) { + int32_t i, count = it.count; + for (i = 0; i < count; i ++) { + ecs_entity_t tgt = it.entities[i]; + const char *name = ecs_get_name(world, tgt); + if (!name) { + /* Member must have name */ + continue; + } - ecs_time_t t = {0}; - ecs_time_measure(&t); - int32_t request_count = http_dequeue_requests(srv, srv->dequeue_timeout); - srv->requests_processed += request_count; - srv->requests_processed_total += request_count; - double time_spent = ecs_time_measure(&t); - srv->request_time += time_spent; - srv->request_time_total += time_spent; - srv->dequeue_count ++; - } + char *to_snake_case = flecs_to_snake_case(name); - if ((1000 * srv->stats_timeout) > (double)ECS_HTTP_MIN_STATS_INTERVAL) { - srv->stats_timeout = 0; - ecs_dbg("http: processed %d requests in %.3fs (avg %.3fs / dequeue)", - srv->requests_processed, srv->request_time, - (srv->request_time / (double)srv->dequeue_count)); - srv->requests_processed = 0; - srv->request_time = 0; - srv->dequeue_count = 0; - } + ecs_entity_t mbr = ecs_entity(world, { + .name = to_snake_case, + .parent = ecs_childof(metric) + }); -error: - return; -} + ecs_os_free(to_snake_case); -int ecs_http_server_http_request( - ecs_http_server_t* srv, - const char *req, - ecs_size_t len, - ecs_http_reply_t *reply_out) -{ - if (!len) { - len = ecs_os_strlen(req); - } + ecs_set(world, mbr, EcsMember, { + .type = ecs_id(ecs_f64_t), + .unit = EcsSeconds + }); - ecs_http_fragment_t frag = {0}; - if (!http_parse_request(&frag, req, len)) { - ecs_strbuf_reset(&frag.buf); - reply_out->code = 400; - return -1; - } + /* Truncate upper 32 bits of target so we can lookup the offset + * with the id we get from the pair */ + ecs_map_ensure(&ctx->target_offset, (uint32_t)tgt)[0] = offset; - ecs_http_request_impl_t request; - char *res = http_decode_request(&request, &frag); - if (!res) { - reply_out->code = 400; - return -1; + offset += sizeof(double); + } } - http_do_request(srv, reply_out, &request); - ecs_os_free(res); + ctx->size = flecs_uto(ecs_size_t, offset); - return (reply_out->code >= 400) ? -1 : 0; -} + ecs_observer(world, { + .entity = metric, + .events = { EcsMonitor }, + .query.terms[0] = { .id = desc->id }, + .callback = flecs_metrics_on_oneof_metric, + .yield_existing = true, + .ctx = ctx + }); -int ecs_http_server_request( - ecs_http_server_t* srv, - const char *method, - const char *req, - ecs_http_reply_t *reply_out) -{ - ecs_strbuf_t reqbuf = ECS_STRBUF_INIT; - ecs_strbuf_appendstr_zerocpy_const(&reqbuf, method); - ecs_strbuf_appendlit(&reqbuf, " "); - ecs_strbuf_appendstr_zerocpy_const(&reqbuf, req); - ecs_strbuf_appendlit(&reqbuf, " HTTP/1.1\r\n\r\n"); - int32_t len = ecs_strbuf_written(&reqbuf); - char *reqstr = ecs_strbuf_get(&reqbuf); - int result = ecs_http_server_http_request(srv, reqstr, len, reply_out); - ecs_os_free(reqstr); - return result; -} + ecs_set(world, metric, EcsMetricOneOf, { .ctx = ctx }); + ecs_add_pair(world, metric, EcsMetric, desc->kind); + ecs_add_id(world, metric, EcsMetric); -void* ecs_http_server_ctx( - ecs_http_server_t* srv) -{ - return srv->ctx; + return 0; +error: + return -1; } -#endif - -/** - * @file addons/journal.c - * @brief Journal addon. - */ - - -#ifdef FLECS_JOURNAL - static -char* flecs_journal_entitystr( +int flecs_count_id_targets_metric_init( ecs_world_t *world, - ecs_entity_t entity) + ecs_entity_t metric, + const ecs_metric_desc_t *desc) { - char *path; - const char *_path = ecs_get_symbol(world, entity); - if (_path && !strchr(_path, '.')) { - path = ecs_asprintf("#[blue]%s", _path); - } else { - uint32_t gen = entity >> 32; - if (gen) { - path = ecs_asprintf("#[normal]_%u_%u", (uint32_t)entity, gen); - } else { - path = ecs_asprintf("#[normal]_%u", (uint32_t)entity); - } - } - return path; + ecs_count_targets_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_count_targets_metric_ctx_t); + ctx->metric.metric = metric; + ctx->metric.kind = desc->kind; + ctx->idr = flecs_id_record_ensure(world, desc->id); + ecs_check(ctx->idr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_map_init(&ctx->targets, NULL); + + ecs_set(world, metric, EcsMetricCountTargets, { .ctx = ctx }); + ecs_add_pair(world, metric, EcsMetric, desc->kind); + ecs_add_id(world, metric, EcsMetric); + + return 0; +error: + return -1; } static -char* flecs_journal_idstr( +int flecs_count_ids_metric_init( ecs_world_t *world, - ecs_id_t id) + ecs_entity_t metric, + const ecs_metric_desc_t *desc) { - if (ECS_IS_PAIR(id)) { - char *first_path = flecs_journal_entitystr(world, - ecs_pair_first(world, id)); - char *second_path = flecs_journal_entitystr(world, - ecs_pair_second(world, id)); - char *result = ecs_asprintf("#[cyan]ecs_pair#[normal](%s, %s)", - first_path, second_path); - ecs_os_free(first_path); - ecs_os_free(second_path); - return result; - } else if (!(id & ECS_ID_FLAGS_MASK)) { - return flecs_journal_entitystr(world, id); - } else { - return ecs_id_str(world, id); - } + ecs_set(world, metric, EcsMetricCountIds, { .id = desc->id }); + ecs_set(world, metric, EcsMetricValue, { .value = 0 }); + return 0; } -static int flecs_journal_sp = 0; - -void flecs_journal_begin( +ecs_entity_t ecs_metric_init( ecs_world_t *world, - ecs_journal_kind_t kind, - ecs_entity_t entity, - ecs_type_t *add, - ecs_type_t *remove) + const ecs_metric_desc_t *desc) { - flecs_journal_sp ++; + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_metric_desc_t was not initialized to zero"); + flecs_poly_assert(world, ecs_world_t); - if (ecs_os_api.log_level_ < FLECS_JOURNAL_LOG_LEVEL) { - return; + ecs_entity_t result = desc->entity; + if (!result) { + result = ecs_new(world); } - char *path = NULL; - char *var_id = NULL; - if (entity) { - if (kind != EcsJournalDeleteWith && kind != EcsJournalRemoveAll) { - path = ecs_get_fullpath(world, entity); - var_id = flecs_journal_entitystr(world, entity); - } else { - path = ecs_id_str(world, entity); - var_id = flecs_journal_idstr(world, entity); - } + ecs_entity_t kind = desc->kind; + if (!kind) { + ecs_err("missing metric kind"); + goto error; } - if (kind == EcsJournalNew) { - ecs_print(4, "#[magenta]#ifndef #[normal]_var_%s", var_id); - ecs_print(4, "#[magenta]#define #[normal]_var_%s", var_id); - ecs_print(4, "#[green]ecs_entity_t %s;", var_id); - ecs_print(4, "#[magenta]#endif"); - ecs_print(4, "%s = #[cyan]ecs_new_id#[reset](world); " - "#[grey] // %s = new()", var_id, path); + if (kind != EcsGauge && + kind != EcsCounter && + kind != EcsCounterId && + kind != EcsCounterIncrement) + { + ecs_err("invalid metric kind %s", ecs_get_path(world, kind)); + goto error; } - if (add) { - for (int i = 0; i < add->count; i ++) { - char *jidstr = flecs_journal_idstr(world, add->array[i]); - char *idstr = ecs_id_str(world, add->array[i]); - ecs_print(4, "#[cyan]ecs_add_id#[reset](world, %s, %s); " - "#[grey] // add(%s, %s)", var_id, jidstr, - path, idstr); - ecs_os_free(idstr); - ecs_os_free(jidstr); - } + + if (kind == EcsCounterIncrement && !desc->member && !desc->dotmember) { + ecs_err("CounterIncrement can only be used in combination with member"); + goto error; } - if (remove) { - for (int i = 0; i < remove->count; i ++) { - char *jidstr = flecs_journal_idstr(world, remove->array[i]); - char *idstr = ecs_id_str(world, remove->array[i]); - ecs_print(4, "#[cyan]ecs_remove_id#[reset](world, %s, %s); " - "#[grey] // remove(%s, %s)", var_id, jidstr, - path, idstr); - ecs_os_free(idstr); - ecs_os_free(jidstr); - } + + if (kind == EcsCounterId && (desc->member || desc->dotmember)) { + ecs_err("CounterId cannot be used in combination with member"); + goto error; } - if (kind == EcsJournalClear) { - ecs_print(4, "#[cyan]ecs_clear#[reset](world, %s); " - "#[grey] // clear(%s)", var_id, path); - } else if (kind == EcsJournalDelete) { - ecs_print(4, "#[cyan]ecs_delete#[reset](world, %s); " - "#[grey] // delete(%s)", var_id, path); - } else if (kind == EcsJournalDeleteWith) { - ecs_print(4, "#[cyan]ecs_delete_with#[reset](world, %s); " - "#[grey] // delete_with(%s)", var_id, path); - } else if (kind == EcsJournalRemoveAll) { - ecs_print(4, "#[cyan]ecs_remove_all#[reset](world, %s); " - "#[grey] // remove_all(%s)", var_id, path); - } else if (kind == EcsJournalTableEvents) { - ecs_print(4, "#[cyan]ecs_run_aperiodic#[reset](world, " - "EcsAperiodicEmptyTables);"); + + if (desc->brief) { +#ifdef FLECS_DOC + ecs_doc_set_brief(world, result, desc->brief); +#else + ecs_warn("FLECS_DOC is not enabled, ignoring metrics brief"); +#endif } - ecs_os_free(var_id); - ecs_os_free(path); - ecs_log_push(); -} -void flecs_journal_end(void) { - flecs_journal_sp --; - ecs_assert(flecs_journal_sp >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_log_pop(); + if (desc->member || desc->dotmember) { + if (flecs_member_metric_init(world, result, desc)) { + goto error; + } + } else if (desc->id) { + if (desc->targets) { + if (!ecs_id_is_pair(desc->id)) { + ecs_err("cannot specify targets for id that is not a pair"); + goto error; + } + if (ECS_PAIR_FIRST(desc->id) == EcsWildcard) { + ecs_err("first element of pair cannot be wildcard with " + " targets enabled"); + goto error; + } + if (ECS_PAIR_SECOND(desc->id) != EcsWildcard) { + ecs_err("second element of pair must be wildcard with " + " targets enabled"); + goto error; + } + + if (kind == EcsCounterId) { + if (flecs_count_id_targets_metric_init(world, result, desc)) { + goto error; + } + } else { + ecs_entity_t first = ecs_pair_first(world, desc->id); + ecs_entity_t scope = flecs_get_oneof(world, first); + if (!scope) { + ecs_err("first element of pair must have OneOf with " + " targets enabled"); + goto error; + } + + if (flecs_oneof_metric_init(world, result, scope, desc)) { + goto error; + } + } + } else { + if (kind == EcsCounterId) { + if (flecs_count_ids_metric_init(world, result, desc)) { + goto error; + } + } else { + if (flecs_id_metric_init(world, result, desc)) { + goto error; + } + } + } + } else { + ecs_err("missing source specified for metric"); + goto error; + } + + return result; +error: + if (result && result != desc->entity) { + ecs_delete(world, result); + } + return 0; } -#endif +void FlecsMetricsImport(ecs_world_t *world) { + ECS_MODULE_DEFINE(world, FlecsMetrics); -/** - * @file addons/log.c - * @brief Log addon. - */ + ECS_IMPORT(world, FlecsPipeline); + ECS_IMPORT(world, FlecsMeta); + ECS_IMPORT(world, FlecsUnits); + ecs_set_name_prefix(world, "Ecs"); + ECS_TAG_DEFINE(world, EcsMetric); + ecs_entity_t old_scope = ecs_set_scope(world, EcsMetric); + ECS_TAG_DEFINE(world, EcsCounter); + ECS_TAG_DEFINE(world, EcsCounterIncrement); + ECS_TAG_DEFINE(world, EcsCounterId); + ECS_TAG_DEFINE(world, EcsGauge); + ecs_set_scope(world, old_scope); -#ifdef FLECS_LOG + ecs_set_name_prefix(world, "EcsMetric"); + ECS_TAG_DEFINE(world, EcsMetricInstance); + ECS_COMPONENT_DEFINE(world, EcsMetricValue); + ECS_COMPONENT_DEFINE(world, EcsMetricSource); + ECS_COMPONENT_DEFINE(world, EcsMetricMemberInstance); + ECS_COMPONENT_DEFINE(world, EcsMetricIdInstance); + ECS_COMPONENT_DEFINE(world, EcsMetricOneOfInstance); + ECS_COMPONENT_DEFINE(world, EcsMetricMember); + ECS_COMPONENT_DEFINE(world, EcsMetricId); + ECS_COMPONENT_DEFINE(world, EcsMetricOneOf); + ECS_COMPONENT_DEFINE(world, EcsMetricCountIds); + ECS_COMPONENT_DEFINE(world, EcsMetricCountTargets); -#include + ecs_add_id(world, ecs_id(EcsMetricMemberInstance), EcsPrivate); + ecs_add_id(world, ecs_id(EcsMetricIdInstance), EcsPrivate); + ecs_add_id(world, ecs_id(EcsMetricOneOfInstance), EcsPrivate); -void flecs_colorize_buf( - char *msg, - bool enable_colors, - ecs_strbuf_t *buf) -{ - ecs_assert(msg != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(buf != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_struct(world, { + .entity = ecs_id(EcsMetricValue), + .members = { + { .name = "value", .type = ecs_id(ecs_f64_t) } + } + }); - char *ptr, ch, prev = '\0'; - bool isNum = false; - char isStr = '\0'; - bool isVar = false; - bool overrideColor = false; - bool autoColor = true; - bool dontAppend = false; + ecs_struct(world, { + .entity = ecs_id(EcsMetricSource), + .members = { + { .name = "entity", .type = ecs_id(ecs_entity_t) } + } + }); - for (ptr = msg; (ch = *ptr); ptr++) { - dontAppend = false; + ecs_set_hooks(world, EcsMetricMember, { + .ctor = flecs_default_ctor, + .dtor = ecs_dtor(EcsMetricMember), + .move = ecs_move(EcsMetricMember) + }); - if (!overrideColor) { - if (isNum && !isdigit(ch) && !isalpha(ch) && (ch != '.') && (ch != '%')) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); - isNum = false; - } - if (isStr && (isStr == ch) && prev != '\\') { - isStr = '\0'; - } else if (((ch == '\'') || (ch == '"')) && !isStr && - !isalpha(prev) && (prev != '\\')) - { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_CYAN); - isStr = ch; - } + ecs_set_hooks(world, EcsMetricId, { + .ctor = flecs_default_ctor, + .dtor = ecs_dtor(EcsMetricId), + .move = ecs_move(EcsMetricId) + }); - if ((isdigit(ch) || (ch == '%' && isdigit(prev)) || - (ch == '-' && isdigit(ptr[1]))) && !isNum && !isStr && !isVar && - !isalpha(prev) && !isdigit(prev) && (prev != '_') && - (prev != '.')) - { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_GREEN); - isNum = true; - } + ecs_set_hooks(world, EcsMetricOneOf, { + .ctor = flecs_default_ctor, + .dtor = ecs_dtor(EcsMetricOneOf), + .move = ecs_move(EcsMetricOneOf) + }); - if (isVar && !isalpha(ch) && !isdigit(ch) && ch != '_') { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); - isVar = false; - } + ecs_set_hooks(world, EcsMetricCountTargets, { + .ctor = flecs_default_ctor, + .dtor = ecs_dtor(EcsMetricCountTargets), + .move = ecs_move(EcsMetricCountTargets) + }); - if (!isStr && !isVar && ch == '$' && isalpha(ptr[1])) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_CYAN); - isVar = true; - } - } + ecs_add_id(world, EcsMetric, EcsOneOf); - if (!isVar && !isStr && !isNum && ch == '#' && ptr[1] == '[') { - bool isColor = true; - overrideColor = true; +#ifdef FLECS_DOC + ECS_OBSERVER(world, SetMetricDocName, EcsOnSet, + Source); +#endif - /* Custom colors */ - if (!ecs_os_strncmp(&ptr[2], "]", ecs_os_strlen("]"))) { - autoColor = false; - } else if (!ecs_os_strncmp(&ptr[2], "green]", ecs_os_strlen("green]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_GREEN); - } else if (!ecs_os_strncmp(&ptr[2], "red]", ecs_os_strlen("red]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_RED); - } else if (!ecs_os_strncmp(&ptr[2], "blue]", ecs_os_strlen("red]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_BLUE); - } else if (!ecs_os_strncmp(&ptr[2], "magenta]", ecs_os_strlen("magenta]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_MAGENTA); - } else if (!ecs_os_strncmp(&ptr[2], "cyan]", ecs_os_strlen("cyan]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_CYAN); - } else if (!ecs_os_strncmp(&ptr[2], "yellow]", ecs_os_strlen("yellow]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_YELLOW); - } else if (!ecs_os_strncmp(&ptr[2], "grey]", ecs_os_strlen("grey]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_GREY); - } else if (!ecs_os_strncmp(&ptr[2], "white]", ecs_os_strlen("white]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); - } else if (!ecs_os_strncmp(&ptr[2], "bold]", ecs_os_strlen("bold]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_BOLD); - } else if (!ecs_os_strncmp(&ptr[2], "normal]", ecs_os_strlen("normal]"))) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); - } else if (!ecs_os_strncmp(&ptr[2], "reset]", ecs_os_strlen("reset]"))) { - overrideColor = false; - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); - } else { - isColor = false; - overrideColor = false; - } + ECS_SYSTEM(world, ClearMetricInstance, EcsPreStore, + [in] Source); - if (isColor) { - ptr += 2; - while ((ch = *ptr) != ']') ptr ++; - dontAppend = true; - } - if (!autoColor) { - overrideColor = true; - } - } + ECS_SYSTEM(world, UpdateGaugeMemberInstance, EcsPreStore, + [out] Value, + [in] MemberInstance, + [none] (Metric, Gauge)); - if (ch == '\n') { - if (isNum || isStr || isVar || overrideColor) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); - overrideColor = false; - isNum = false; - isStr = false; - isVar = false; - } - } + ECS_SYSTEM(world, UpdateCounterMemberInstance, EcsPreStore, + [out] Value, + [in] MemberInstance, + [none] (Metric, Counter)); - if (!dontAppend) { - ecs_strbuf_appendstrn(buf, ptr, 1); - } + ECS_SYSTEM(world, UpdateCounterIncrementMemberInstance, EcsPreStore, + [out] Value, + [in] MemberInstance, + [none] (Metric, CounterIncrement)); - if (!overrideColor) { - if (((ch == '\'') || (ch == '"')) && !isStr) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); - } - } + ECS_SYSTEM(world, UpdateGaugeIdInstance, EcsPreStore, + [out] Value, + [in] IdInstance, + [none] (Metric, Gauge)); - prev = ch; - } + ECS_SYSTEM(world, UpdateCounterIdInstance, EcsPreStore, + [inout] Value, + [in] IdInstance, + [none] (Metric, Counter)); - if (isNum || isStr || isVar || overrideColor) { - if (enable_colors) ecs_strbuf_appendlit(buf, ECS_NORMAL); - } + ECS_SYSTEM(world, UpdateGaugeOneOfInstance, EcsPreStore, + [none] (_, Value), + [in] OneOfInstance, + [none] (Metric, Gauge)); + + ECS_SYSTEM(world, UpdateCounterOneOfInstance, EcsPreStore, + [none] (_, Value), + [in] OneOfInstance, + [none] (Metric, Counter)); + + ECS_SYSTEM(world, UpdateCountIds, EcsPreStore, + [inout] CountIds, Value); + + ECS_SYSTEM(world, UpdateCountTargets, EcsPreStore, + [inout] CountTargets); } -void ecs_printv_( - int level, - const char *file, - int32_t line, - const char *fmt, - va_list args) -{ - (void)level; - (void)line; +#endif - ecs_strbuf_t msg_buf = ECS_STRBUF_INIT; +/** + * @file addons/module.c + * @brief Module addon. + */ - /* Apply color. Even if we don't want color, we still need to call the - * colorize function to get rid of the color tags (e.g. #[green]) */ - char *msg_nocolor = ecs_vasprintf(fmt, args); - flecs_colorize_buf(msg_nocolor, - ecs_os_api.flags_ & EcsOsApiLogWithColors, &msg_buf); - ecs_os_free(msg_nocolor); - char *msg = ecs_strbuf_get(&msg_buf); +#ifdef FLECS_MODULE - if (msg) { - ecs_os_api.log_(level, file, line, msg); - ecs_os_free(msg); - } else { - ecs_os_api.log_(level, file, line, ""); - } -} +#include -void ecs_print_( - int level, - const char *file, - int32_t line, - const char *fmt, - ...) +char* flecs_module_path_from_c( + const char *c_name) { - va_list args; - va_start(args, fmt); - ecs_printv_(level, file, line, fmt, args); - va_end(args); -} + ecs_strbuf_t str = ECS_STRBUF_INIT; + const char *ptr; + char ch; -void ecs_logv_( - int level, - const char *file, - int32_t line, - const char *fmt, - va_list args) -{ - if (level > ecs_os_api.log_level_) { - return; + for (ptr = c_name; (ch = *ptr); ptr++) { + if (isupper(ch)) { + ch = flecs_ito(char, tolower(ch)); + if (ptr != c_name) { + ecs_strbuf_appendstrn(&str, ".", 1); + } + } + + ecs_strbuf_appendstrn(&str, &ch, 1); } - ecs_printv_(level, file, line, fmt, args); + return ecs_strbuf_get(&str); } -void ecs_log_( - int level, - const char *file, - int32_t line, - const char *fmt, - ...) +ecs_entity_t ecs_import( + ecs_world_t *world, + ecs_module_action_t module, + const char *module_name) { - if (level > ecs_os_api.log_level_) { - return; - } + ecs_check(!(world->flags & EcsWorldReadonly), + ECS_INVALID_WHILE_READONLY, NULL); - va_list args; - va_start(args, fmt); - ecs_printv_(level, file, line, fmt, args); - va_end(args); -} + ecs_entity_t old_scope = ecs_set_scope(world, 0); + const char *old_name_prefix = world->info.name_prefix; + + char *path = flecs_module_path_from_c(module_name); + ecs_entity_t e = ecs_lookup(world, path); + ecs_os_free(path); + + if (!e) { + ecs_trace("#[magenta]import#[reset] %s", module_name); + ecs_log_push(); + /* Load module */ + module(world); -void ecs_log_push_( - int32_t level) -{ - if (level <= ecs_os_api.log_level_) { - ecs_os_api.log_indent_ ++; + /* Lookup module entity (must be registered by module) */ + e = ecs_lookup(world, module_name); + ecs_check(e != 0, ECS_MODULE_UNDEFINED, module_name); + + ecs_log_pop(); } + + /* Restore to previous state */ + ecs_set_scope(world, old_scope); + world->info.name_prefix = old_name_prefix; + + return e; +error: + return 0; } -void ecs_log_pop_( - int32_t level) +ecs_entity_t ecs_import_c( + ecs_world_t *world, + ecs_module_action_t module, + const char *c_name) { - if (level <= ecs_os_api.log_level_) { - ecs_os_api.log_indent_ --; - ecs_assert(ecs_os_api.log_indent_ >= 0, ECS_INTERNAL_ERROR, NULL); - } + char *name = flecs_module_path_from_c(c_name); + ecs_entity_t e = ecs_import(world, module, name); + ecs_os_free(name); + return e; } -void ecs_parser_errorv_( - const char *name, - const char *expr, - int64_t column_arg, - const char *fmt, - va_list args) +ecs_entity_t ecs_import_from_library( + ecs_world_t *world, + const char *library_name, + const char *module_name) { - if (column_arg > 65536) { - /* Limit column size, which prevents the code from throwing up when the - * function is called with (expr - ptr), and expr is NULL. */ - column_arg = 0; - } - int32_t column = flecs_itoi32(column_arg); - - if (ecs_os_api.log_level_ >= -2) { - ecs_strbuf_t msg_buf = ECS_STRBUF_INIT; - - ecs_strbuf_vappend(&msg_buf, fmt, args); + ecs_check(library_name != NULL, ECS_INVALID_PARAMETER, NULL); - if (expr) { - ecs_strbuf_appendch(&msg_buf, '\n'); + char *import_func = ECS_CONST_CAST(char*, module_name); + char *module = ECS_CONST_CAST(char*, module_name); - /* Find start of line by taking column and looking for the - * last occurring newline */ - if (column != -1) { - const char *ptr = &expr[column]; - while (ptr[0] != '\n' && ptr > expr) { - ptr --; - } + if (!ecs_os_has_modules() || !ecs_os_has_dl()) { + ecs_err( + "library loading not supported, set module_to_dl, dlopen, dlclose " + "and dlproc os API callbacks first"); + return 0; + } - if (ptr == expr) { - /* ptr is already at start of line */ + /* If no module name is specified, try default naming convention for loading + * the main module from the library */ + if (!import_func) { + import_func = ecs_os_malloc(ecs_os_strlen(library_name) + ECS_SIZEOF("Import")); + ecs_assert(import_func != NULL, ECS_OUT_OF_MEMORY, NULL); + + const char *ptr; + char ch, *bptr = import_func; + bool capitalize = true; + for (ptr = library_name; (ch = *ptr); ptr ++) { + if (ch == '.') { + capitalize = true; + } else { + if (capitalize) { + *bptr = flecs_ito(char, toupper(ch)); + bptr ++; + capitalize = false; } else { - column -= (int32_t)(ptr - expr + 1); - expr = ptr + 1; + *bptr = flecs_ito(char, tolower(ch)); + bptr ++; } } + } - /* Strip newlines from current statement, if any */ - char *newline_ptr = strchr(expr, '\n'); - if (newline_ptr) { - /* Strip newline from expr */ - ecs_strbuf_appendstrn(&msg_buf, expr, - (int32_t)(newline_ptr - expr)); - } else { - ecs_strbuf_appendstr(&msg_buf, expr); - } - - ecs_strbuf_appendch(&msg_buf, '\n'); + *bptr = '\0'; - if (column != -1) { - int32_t c; - for (c = 0; c < column; c ++) { - ecs_strbuf_appendch(&msg_buf, ' '); - } - ecs_strbuf_appendch(&msg_buf, '^'); - } - } + module = ecs_os_strdup(import_func); + ecs_assert(module != NULL, ECS_OUT_OF_MEMORY, NULL); - char *msg = ecs_strbuf_get(&msg_buf); - ecs_os_err(name, 0, msg); - ecs_os_free(msg); + ecs_os_strcat(bptr, "Import"); } -} -void ecs_parser_error_( - const char *name, - const char *expr, - int64_t column, - const char *fmt, - ...) -{ - if (ecs_os_api.log_level_ >= -2) { - va_list args; - va_start(args, fmt); - ecs_parser_errorv_(name, expr, column, fmt, args); - va_end(args); + char *library_filename = ecs_os_module_to_dl(library_name); + if (!library_filename) { + ecs_err("failed to find library file for '%s'", library_name); + if (module != module_name) { + ecs_os_free(module); + } + return 0; + } else { + ecs_trace("found file '%s' for library '%s'", + library_filename, library_name); } -} -void ecs_abort_( - int32_t err, - const char *file, - int32_t line, - const char *fmt, - ...) -{ - if (fmt) { - va_list args; - va_start(args, fmt); - char *msg = ecs_vasprintf(fmt, args); - va_end(args); - ecs_fatal_(file, line, "%s (%s)", msg, ecs_strerror(err)); - ecs_os_free(msg); + ecs_os_dl_t dl = ecs_os_dlopen(library_filename); + if (!dl) { + ecs_err("failed to load library '%s' ('%s')", + library_name, library_filename); + + ecs_os_free(library_filename); + + if (module != module_name) { + ecs_os_free(module); + } + + return 0; } else { - ecs_fatal_(file, line, "%s", ecs_strerror(err)); + ecs_trace("library '%s' ('%s') loaded", + library_name, library_filename); } - ecs_os_api.log_last_error_ = err; -} -void ecs_assert_log_( - int32_t err, - const char *cond_str, - const char *file, - int32_t line, - const char *fmt, - ...) -{ - if (fmt) { - va_list args; - va_start(args, fmt); - char *msg = ecs_vasprintf(fmt, args); - va_end(args); - ecs_fatal_(file, line, "assert: %s %s (%s)", - cond_str, msg, ecs_strerror(err)); - ecs_os_free(msg); + ecs_module_action_t action = (ecs_module_action_t) + ecs_os_dlproc(dl, import_func); + if (!action) { + ecs_err("failed to load import function %s from library %s", + import_func, library_name); + ecs_os_free(library_filename); + ecs_os_dlclose(dl); + return 0; } else { - ecs_fatal_(file, line, "assert: %s %s", - cond_str, ecs_strerror(err)); + ecs_trace("found import function '%s' in library '%s' for module '%s'", + import_func, library_name, module); } - ecs_os_api.log_last_error_ = err; -} -void ecs_deprecated_( - const char *file, - int32_t line, - const char *msg) -{ - ecs_err_(file, line, "%s", msg); -} + /* Do not free id, as it will be stored as the component identifier */ + ecs_entity_t result = ecs_import(world, action, module); -bool ecs_should_log(int32_t level) { -# if !defined(FLECS_LOG_3) - if (level == 3) { - return false; - } -# endif -# if !defined(FLECS_LOG_2) - if (level == 2) { - return false; - } -# endif -# if !defined(FLECS_LOG_1) - if (level == 1) { - return false; + if (import_func != module_name) { + ecs_os_free(import_func); } -# endif - - return level <= ecs_os_api.log_level_; -} -#define ECS_ERR_STR(code) case code: return &(#code[4]) - -const char* ecs_strerror( - int32_t error_code) -{ - switch (error_code) { - ECS_ERR_STR(ECS_INVALID_PARAMETER); - ECS_ERR_STR(ECS_NOT_A_COMPONENT); - ECS_ERR_STR(ECS_INTERNAL_ERROR); - ECS_ERR_STR(ECS_ALREADY_DEFINED); - ECS_ERR_STR(ECS_INVALID_COMPONENT_SIZE); - ECS_ERR_STR(ECS_INVALID_COMPONENT_ALIGNMENT); - ECS_ERR_STR(ECS_NAME_IN_USE); - ECS_ERR_STR(ECS_OUT_OF_MEMORY); - ECS_ERR_STR(ECS_DOUBLE_FREE); - ECS_ERR_STR(ECS_OPERATION_FAILED); - ECS_ERR_STR(ECS_INVALID_CONVERSION); - ECS_ERR_STR(ECS_MODULE_UNDEFINED); - ECS_ERR_STR(ECS_MISSING_SYMBOL); - ECS_ERR_STR(ECS_ALREADY_IN_USE); - ECS_ERR_STR(ECS_CYCLE_DETECTED); - ECS_ERR_STR(ECS_LEAK_DETECTED); - ECS_ERR_STR(ECS_COLUMN_INDEX_OUT_OF_RANGE); - ECS_ERR_STR(ECS_COLUMN_IS_NOT_SHARED); - ECS_ERR_STR(ECS_COLUMN_IS_SHARED); - ECS_ERR_STR(ECS_COLUMN_TYPE_MISMATCH); - ECS_ERR_STR(ECS_INVALID_WHILE_READONLY); - ECS_ERR_STR(ECS_INVALID_FROM_WORKER); - ECS_ERR_STR(ECS_OUT_OF_RANGE); - ECS_ERR_STR(ECS_MISSING_OS_API); - ECS_ERR_STR(ECS_UNSUPPORTED); - ECS_ERR_STR(ECS_ACCESS_VIOLATION); - ECS_ERR_STR(ECS_COMPONENT_NOT_REGISTERED); - ECS_ERR_STR(ECS_INCONSISTENT_COMPONENT_ID); - ECS_ERR_STR(ECS_INCONSISTENT_NAME); - ECS_ERR_STR(ECS_INCONSISTENT_COMPONENT_ACTION); - ECS_ERR_STR(ECS_INVALID_OPERATION); - ECS_ERR_STR(ECS_CONSTRAINT_VIOLATED); - ECS_ERR_STR(ECS_LOCKED_STORAGE); - ECS_ERR_STR(ECS_ID_IN_USE); + if (module != module_name) { + ecs_os_free(module); } - return "unknown error code"; -} - -#else - -/* Empty bodies for when logging is disabled */ - -void ecs_log_( - int32_t level, - const char *file, - int32_t line, - const char *fmt, - ...) -{ - (void)level; - (void)file; - (void)line; - (void)fmt; -} + ecs_os_free(library_filename); -void ecs_parser_error_( - const char *name, - const char *expr, - int64_t column, - const char *fmt, - ...) -{ - (void)name; - (void)expr; - (void)column; - (void)fmt; + return result; +error: + return 0; } -void ecs_parser_errorv_( - const char *name, - const char *expr, - int64_t column, - const char *fmt, - va_list args) +ecs_entity_t ecs_module_init( + ecs_world_t *world, + const char *c_name, + const ecs_component_desc_t *desc) { - (void)name; - (void)expr; - (void)column; - (void)fmt; - (void)args; -} + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_poly_assert(world, ecs_world_t); -void ecs_abort_( - int32_t error_code, - const char *file, - int32_t line, - const char *fmt, - ...) -{ - (void)error_code; - (void)file; - (void)line; - (void)fmt; -} + ecs_entity_t old_scope = ecs_set_scope(world, 0); -void ecs_assert_log_( - int32_t error_code, - const char *condition_str, - const char *file, - int32_t line, - const char *fmt, - ...) -{ - (void)error_code; - (void)condition_str; - (void)file; - (void)line; - (void)fmt; -} + ecs_entity_t e = desc->entity; + if (!e) { + char *module_path = flecs_module_path_from_c(c_name); + e = ecs_entity(world, { .name = module_path }); + ecs_set_symbol(world, e, module_path); + ecs_os_free(module_path); + } else if (!ecs_exists(world, e)) { + char *module_path = flecs_module_path_from_c(c_name); + ecs_make_alive(world, e); + ecs_add_fullpath(world, e, module_path); + ecs_set_symbol(world, e, module_path); + ecs_os_free(module_path); + } + + ecs_add_id(world, e, EcsModule); -#endif + ecs_component_desc_t private_desc = *desc; + private_desc.entity = e; -int ecs_log_get_level(void) { - return ecs_os_api.log_level_; -} + if (desc->type.size) { + ecs_entity_t result = ecs_component_init(world, &private_desc); + ecs_assert(result != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(result == e, ECS_INTERNAL_ERROR, NULL); + (void)result; + } -int ecs_log_set_level( - int level) -{ - int prev = level; - ecs_os_api.log_level_ = level; - return prev; -} + ecs_set_scope(world, old_scope); -bool ecs_log_enable_colors( - bool enabled) -{ - bool prev = ecs_os_api.flags_ & EcsOsApiLogWithColors; - ECS_BIT_COND(ecs_os_api.flags_, EcsOsApiLogWithColors, enabled); - return prev; + return e; +error: + return 0; } -bool ecs_log_enable_timestamp( - bool enabled) -{ - bool prev = ecs_os_api.flags_ & EcsOsApiLogWithTimeStamp; - ECS_BIT_COND(ecs_os_api.flags_, EcsOsApiLogWithTimeStamp, enabled); - return prev; -} +#endif -bool ecs_log_enable_timedelta( - bool enabled) -{ - bool prev = ecs_os_api.flags_ & EcsOsApiLogWithTimeDelta; - ECS_BIT_COND(ecs_os_api.flags_, EcsOsApiLogWithTimeDelta, enabled); - return prev; -} +/** + * @file addons/rest.c + * @brief Rest addon. + */ -int ecs_log_last_error(void) -{ - int result = ecs_os_api.log_last_error_; - ecs_os_api.log_last_error_ = 0; - return result; -} /** - * @file addons/meta_c.c - * @brief C utilities for meta addon. + * @file addons/pipeline/pipeline.h + * @brief Internal functions/types for pipeline addon. */ +#ifndef FLECS_PIPELINE_PRIVATE_H +#define FLECS_PIPELINE_PRIVATE_H -#ifdef FLECS_META_C -#include +/** Instruction data for pipeline. + * This type is the element type in the "ops" vector of a pipeline. */ +typedef struct ecs_pipeline_op_t { + int32_t offset; /* Offset in systems vector */ + int32_t count; /* Number of systems to run before next op */ + double time_spent; /* Time spent merging commands for sync point */ + int64_t commands_enqueued; /* Number of commands enqueued for sync point */ + bool multi_threaded; /* Whether systems can be ran multi threaded */ + bool immediate; /* Whether systems are staged or not */ +} ecs_pipeline_op_t; -#define ECS_META_IDENTIFIER_LENGTH (256) +struct ecs_pipeline_state_t { + ecs_query_t *query; /* Pipeline query */ + ecs_vec_t ops; /* Pipeline schedule */ + ecs_vec_t systems; /* Vector with system ids */ -#define ecs_meta_error(ctx, ptr, ...)\ - ecs_parser_error((ctx)->name, (ctx)->desc, ptr - (ctx)->desc, __VA_ARGS__); + ecs_entity_t last_system; /* Last system ran by pipeline */ + ecs_id_record_t *idr_inactive; /* Cached record for quick inactive test */ + int32_t match_count; /* Used to track of rebuild is necessary */ + int32_t rebuild_count; /* Number of pipeline rebuilds */ + ecs_iter_t *iters; /* Iterator for worker(s) */ + int32_t iter_count; -typedef char ecs_meta_token_t[ECS_META_IDENTIFIER_LENGTH]; + /* Members for continuing pipeline iteration after pipeline rebuild */ + ecs_pipeline_op_t *cur_op; /* Current pipeline op */ + int32_t cur_i; /* Index in current result */ + int32_t ran_since_merge; /* Index in current op */ + bool immediate; /* Is pipeline in readonly mode */ +}; -typedef struct meta_parse_ctx_t { - const char *name; - const char *desc; -} meta_parse_ctx_t; +typedef struct EcsPipeline { + /* Stable ptr so threads can safely access while entity/components move */ + ecs_pipeline_state_t *state; +} EcsPipeline; -typedef struct meta_type_t { - ecs_meta_token_t type; - ecs_meta_token_t params; - bool is_const; - bool is_ptr; -} meta_type_t; +//////////////////////////////////////////////////////////////////////////////// +//// Pipeline API +//////////////////////////////////////////////////////////////////////////////// -typedef struct meta_member_t { - meta_type_t type; - ecs_meta_token_t name; - int64_t count; - bool is_partial; -} meta_member_t; +bool flecs_pipeline_update( + ecs_world_t *world, + ecs_pipeline_state_t *pq, + bool start_of_frame); -typedef struct meta_constant_t { - ecs_meta_token_t name; - int64_t value; - bool is_value_set; -} meta_constant_t; +void flecs_run_pipeline( + ecs_world_t *world, + ecs_pipeline_state_t *pq, + ecs_ftime_t delta_time); -typedef struct meta_params_t { - meta_type_t key_type; - meta_type_t type; - int64_t count; - bool is_key_value; - bool is_fixed_size; -} meta_params_t; +int32_t flecs_run_pipeline_ops( + ecs_world_t* world, + ecs_stage_t* stage, + int32_t stage_index, + int32_t stage_count, + ecs_ftime_t delta_time); -static -const char* skip_scope(const char *ptr, meta_parse_ctx_t *ctx) { - /* Keep track of which characters were used to open the scope */ - char stack[256]; - int32_t sp = 0; - char ch; +//////////////////////////////////////////////////////////////////////////////// +//// Worker API +//////////////////////////////////////////////////////////////////////////////// - while ((ch = *ptr)) { - if (ch == '(' || ch == '<') { - stack[sp] = ch; +void flecs_workers_progress( + ecs_world_t *world, + ecs_pipeline_state_t *pq, + ecs_ftime_t delta_time); - sp ++; - if (sp >= 256) { - ecs_meta_error(ctx, ptr, "maximum level of nesting reached"); - goto error; - } - } else if (ch == ')' || ch == '>') { - sp --; - if ((sp < 0) || (ch == '>' && stack[sp] != '<') || - (ch == ')' && stack[sp] != '(')) - { - ecs_meta_error(ctx, ptr, "mismatching %c in identifier", ch); - goto error; - } - } +void flecs_create_worker_threads( + ecs_world_t *world); - ptr ++; +void flecs_join_worker_threads( + ecs_world_t *world); - if (!sp) { - break; - } - } +void flecs_signal_workers( + ecs_world_t *world); - return ptr; -error: - return NULL; -} +void flecs_wait_for_sync( + ecs_world_t *world); -static -const char* parse_c_digit( - const char *ptr, - int64_t *value_out) -{ - char token[24]; - ptr = ecs_parse_ws_eol(ptr); - ptr = ecs_parse_digit(ptr, token); - if (!ptr) { - goto error; - } +#endif - *value_out = strtol(token, NULL, 0); - return ecs_parse_ws_eol(ptr); -error: - return NULL; -} +#ifdef FLECS_REST -static -const char* parse_c_identifier( - const char *ptr, - char *buff, - char *params, - meta_parse_ctx_t *ctx) -{ - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(buff != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ctx != NULL, ECS_INTERNAL_ERROR, NULL); +/* Retain captured commands for one minute at 60 FPS */ +#define FLECS_REST_COMMAND_RETAIN_COUNT (60 * 60) - char *bptr = buff, ch; +static ECS_TAG_DECLARE(EcsRestPlecs); - if (params) { - params[0] = '\0'; - } +typedef struct { + ecs_world_t *world; + ecs_http_server_t *srv; + int32_t rc; + ecs_map_t cmd_captures; + double last_time; +} ecs_rest_ctx_t; - /* Ignore whitespaces */ - ptr = ecs_parse_ws_eol(ptr); - ch = *ptr; +typedef struct { + char *cmds; + ecs_time_t start_time; + ecs_strbuf_t buf; +} ecs_rest_cmd_sync_capture_t; - if (!isalpha(ch) && (ch != '_')) { - ecs_meta_error(ctx, ptr, "invalid identifier (starts with '%c')", ch); - goto error; +typedef struct { + ecs_vec_t syncs; +} ecs_rest_cmd_capture_t; + +static ECS_COPY(EcsRest, dst, src, { + ecs_rest_ctx_t *impl = src->impl; + if (impl) { + impl->rc ++; } - while ((ch = *ptr) && !isspace(ch) && ch != ';' && ch != ',' && ch != ')' && - ch != '>' && ch != '}' && ch != '*') - { - /* Type definitions can contain macros or templates */ - if (ch == '(' || ch == '<') { - if (!params) { - ecs_meta_error(ctx, ptr, "unexpected %c", *ptr); - goto error; - } + ecs_os_strset(&dst->ipaddr, src->ipaddr); + dst->port = src->port; + dst->impl = impl; +}) - const char *end = skip_scope(ptr, ctx); - ecs_os_strncpy(params, ptr, (ecs_size_t)(end - ptr)); - params[end - ptr] = '\0'; +static ECS_MOVE(EcsRest, dst, src, { + *dst = *src; + src->ipaddr = NULL; + src->impl = NULL; +}) - ptr = end; - } else { - *bptr = ch; - bptr ++; - ptr ++; +static ECS_DTOR(EcsRest, ptr, { + ecs_rest_ctx_t *impl = ptr->impl; + if (impl) { + impl->rc --; + if (!impl->rc) { + ecs_rest_server_fini(impl->srv); } } + ecs_os_free(ptr->ipaddr); +}) - *bptr = '\0'; - - if (!ch) { - ecs_meta_error(ctx, ptr, "unexpected end of token"); - goto error; - } - - return ptr; -error: - return NULL; -} +static char *rest_last_err; +static ecs_os_api_log_t rest_prev_log; -static -const char * meta_open_scope( - const char *ptr, - meta_parse_ctx_t *ctx) +static +void flecs_rest_capture_log( + int32_t level, + const char *file, + int32_t line, + const char *msg) { - /* Skip initial whitespaces */ - ptr = ecs_parse_ws_eol(ptr); + (void)file; (void)line; - /* Is this the start of the type definition? */ - if (ctx->desc == ptr) { - if (*ptr != '{') { - ecs_meta_error(ctx, ptr, "missing '{' in struct definition"); - goto error; +#ifdef FLECS_DEBUG + if (level < 0) { + /* Also log to previous log function in debug mode */ + if (rest_prev_log) { + ecs_log_enable_colors(true); + rest_prev_log(level, file, line, msg); + ecs_log_enable_colors(false); } - - ptr ++; - ptr = ecs_parse_ws_eol(ptr); } +#endif - /* Is this the end of the type definition? */ - if (!*ptr) { - ecs_meta_error(ctx, ptr, "missing '}' at end of struct definition"); - goto error; - } - - /* Is this the end of the type definition? */ - if (*ptr == '}') { - ptr = ecs_parse_ws_eol(ptr + 1); - if (*ptr) { - ecs_meta_error(ctx, ptr, - "stray characters after struct definition"); - goto error; - } - return NULL; + if (!rest_last_err && level < 0) { + rest_last_err = ecs_os_strdup(msg); } +} - return ptr; -error: - return NULL; +static +char* flecs_rest_get_captured_log(void) { + char *result = rest_last_err; + rest_last_err = NULL; + return result; } static -const char* meta_parse_constant( - const char *ptr, - meta_constant_t *token, - meta_parse_ctx_t *ctx) -{ - ptr = meta_open_scope(ptr, ctx); - if (!ptr) { - return NULL; - } +void flecs_reply_verror( + ecs_http_reply_t *reply, + const char *fmt, + va_list args) +{ + ecs_strbuf_appendlit(&reply->body, "{\"error\":\""); + ecs_strbuf_vappend(&reply->body, fmt, args); + ecs_strbuf_appendlit(&reply->body, "\"}"); +} - token->is_value_set = false; +static +void flecs_reply_error( + ecs_http_reply_t *reply, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + flecs_reply_verror(reply, fmt, args); + va_end(args); +} - /* Parse token, constant identifier */ - ptr = parse_c_identifier(ptr, token->name, NULL, ctx); - if (!ptr) { - return NULL; +static +void flecs_rest_bool_param( + const ecs_http_request_t *req, + const char *name, + bool *value_out) +{ + const char *value = ecs_http_get_param(req, name); + if (value) { + if (!ecs_os_strcmp(value, "true")) { + value_out[0] = true; + } else { + value_out[0] = false; + } } +} - ptr = ecs_parse_ws_eol(ptr); - if (!ptr) { - return NULL; +static +void flecs_rest_int_param( + const ecs_http_request_t *req, + const char *name, + int32_t *value_out) +{ + const char *value = ecs_http_get_param(req, name); + if (value) { + *value_out = atoi(value); } +} - /* Explicit value assignment */ - if (*ptr == '=') { - int64_t value = 0; - ptr = parse_c_digit(ptr + 1, &value); - token->value = value; - token->is_value_set = true; +static +void flecs_rest_string_param( + const ecs_http_request_t *req, + const char *name, + char **value_out) +{ + const char *value = ecs_http_get_param(req, name); + if (value) { + *value_out = ECS_CONST_CAST(char*, value); } +} - /* Expect a ',' or '}' */ - if (*ptr != ',' && *ptr != '}') { - ecs_meta_error(ctx, ptr, "missing , after enum constant"); - goto error; - } +static +void flecs_rest_parse_json_ser_entity_params( + ecs_world_t *world, + ecs_entity_to_json_desc_t *desc, + const ecs_http_request_t *req) +{ + flecs_rest_bool_param(req, "entity_id", &desc->serialize_entity_id); + flecs_rest_bool_param(req, "doc", &desc->serialize_doc); + flecs_rest_bool_param(req, "full_paths", &desc->serialize_full_paths); + flecs_rest_bool_param(req, "inherited", &desc->serialize_inherited); + flecs_rest_bool_param(req, "values", &desc->serialize_values); + flecs_rest_bool_param(req, "builtin", &desc->serialize_builtin); + flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info); + flecs_rest_bool_param(req, "matches", &desc->serialize_matches); + flecs_rest_bool_param(req, "alerts", &desc->serialize_alerts); - if (*ptr == ',') { - return ptr + 1; - } else { - return ptr; + char *rel = NULL; + flecs_rest_string_param(req, "refs", &rel); + if (rel) { + desc->serialize_refs = ecs_lookup(world, rel); } -error: - return NULL; } static -const char* meta_parse_type( - const char *ptr, - meta_type_t *token, - meta_parse_ctx_t *ctx) +void flecs_rest_parse_json_ser_iter_params( + ecs_iter_to_json_desc_t *desc, + const ecs_http_request_t *req) { - token->is_ptr = false; - token->is_const = false; + flecs_rest_bool_param(req, "entity_ids", &desc->serialize_entity_ids); + flecs_rest_bool_param(req, "doc", &desc->serialize_doc); + flecs_rest_bool_param(req, "full_paths", &desc->serialize_full_paths); + flecs_rest_bool_param(req, "inherited", &desc->serialize_inherited); + flecs_rest_bool_param(req, "values", &desc->serialize_values); + flecs_rest_bool_param(req, "builtin", &desc->serialize_builtin); + flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info); + flecs_rest_bool_param(req, "field_info", &desc->serialize_field_info); + flecs_rest_bool_param(req, "query_info", &desc->serialize_query_info); + flecs_rest_bool_param(req, "query_plan", &desc->serialize_query_plan); + flecs_rest_bool_param(req, "query_profile", &desc->serialize_query_profile); + flecs_rest_bool_param(req, "table", &desc->serialize_table); + flecs_rest_bool_param(req, "fields", &desc->serialize_fields); - ptr = ecs_parse_ws_eol(ptr); + bool results = true; + flecs_rest_bool_param(req, "results", &results); + desc->dont_serialize_results = !results; +} - /* Parse token, expect type identifier or ECS_PROPERTY */ - ptr = parse_c_identifier(ptr, token->type, token->params, ctx); - if (!ptr) { - goto error; +static +bool flecs_rest_get_entity( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) +{ + char *path = &req->path[7]; + ecs_dbg_2("rest: request entity '%s'", path); + + ecs_entity_t e = ecs_lookup_path_w_sep( + world, 0, path, "/", NULL, false); + if (!e) { + ecs_dbg_2("rest: entity '%s' not found", path); + flecs_reply_error(reply, "entity '%s' not found", path); + reply->code = 404; + return true; } - if (!strcmp(token->type, "ECS_PRIVATE")) { - /* Members from this point are not stored in metadata */ - ptr += ecs_os_strlen(ptr); - goto done; + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + flecs_rest_parse_json_ser_entity_params(world, &desc, req); + if (ecs_entity_to_json_buf(world, e, &reply->body, &desc) != 0) { + ecs_strbuf_reset(&reply->body); + reply->code = 500; + reply->status = "Internal server error"; + return true; } + return true; +} - /* If token is const, set const flag and continue parsing type */ - if (!strcmp(token->type, "const")) { - token->is_const = true; +static +bool flecs_rest_put_entity( + ecs_world_t *world, + ecs_http_reply_t *reply, + const char *path) +{ + ecs_dbg_2("rest: create entity '%s'", path); - /* Parse type after const */ - ptr = parse_c_identifier(ptr + 1, token->type, token->params, ctx); - } + ecs_entity_t result = ecs_entity(world, { + .name = path, + .sep = "/" + }); - /* Check if type is a pointer */ - ptr = ecs_parse_ws_eol(ptr); - if (*ptr == '*') { - token->is_ptr = true; - ptr ++; + if (!result) { + ecs_dbg_2("rest: failed to create entity '%s'", path); + flecs_reply_error(reply, "failed to create entity '%s'", path); + reply->code = 500; + return true; } -done: - return ptr; -error: - return NULL; + ecs_strbuf_appendlit(&reply->body, "{\"id\":\""); + ecs_strbuf_appendint(&reply->body, (uint32_t)result); + ecs_strbuf_appendlit(&reply->body, "\"}"); + + return true; } static -const char* meta_parse_member( - const char *ptr, - meta_member_t *token, - meta_parse_ctx_t *ctx) +bool flecs_rest_get_world( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) { - ptr = meta_open_scope(ptr, ctx); - if (!ptr) { - return NULL; + (void)req; + if (ecs_world_to_json_buf(world, &reply->body, NULL) != 0) { + ecs_strbuf_reset(&reply->body); + reply->code = 500; + reply->status = "Internal server error"; + return true; } + return true; +} - token->count = 1; - token->is_partial = false; - - /* Parse member type */ - ptr = meta_parse_type(ptr, &token->type, ctx); - if (!ptr) { - token->is_partial = true; - goto error; +static +ecs_entity_t flecs_rest_entity_from_path( + ecs_world_t *world, + ecs_http_reply_t *reply, + const char *path) +{ + ecs_entity_t e = ecs_lookup_path_w_sep( + world, 0, path, "/", NULL, false); + if (!e) { + flecs_reply_error(reply, "entity '%s' not found", path); + reply->code = 404; } + return e; +} - if (!ptr[0]) { - return ptr; +static +bool flecs_rest_get_component( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + const char *path) +{ + ecs_entity_t e; + if (!(e = flecs_rest_entity_from_path(world, reply, path))) { + return true; } - /* Next token is the identifier */ - ptr = parse_c_identifier(ptr, token->name, NULL, ctx); - if (!ptr) { - goto error; + const char *component = ecs_http_get_param(req, "component"); + if (!component) { + flecs_reply_error(reply, "missing component for remove endpoint"); + reply->code = 400; + return true; } - /* Skip whitespace between member and [ or ; */ - ptr = ecs_parse_ws_eol(ptr); - - /* Check if this is an array */ - char *array_start = strchr(token->name, '['); - if (!array_start) { - /* If the [ was separated by a space, it will not be parsed as part of - * the name */ - if (*ptr == '[') { - /* safe, will not be modified */ - array_start = ECS_CONST_CAST(char*, ptr); - } + ecs_entity_t id; + if (!flecs_id_parse(world, path, component, &id)) { + flecs_reply_error(reply, "unresolved component '%s'", component); + reply->code = 400; + return true; } - if (array_start) { - /* Check if the [ matches with a ] */ - char *array_end = strchr(array_start, ']'); - if (!array_end) { - ecs_meta_error(ctx, ptr, "missing ']'"); - goto error; - - } else if (array_end - array_start == 0) { - ecs_meta_error(ctx, ptr, "dynamic size arrays are not supported"); - goto error; - } - - token->count = atoi(array_start + 1); - - if (array_start == ptr) { - /* If [ was found after name, continue parsing after ] */ - ptr = array_end + 1; - } else { - /* If [ was fonud in name, replace it with 0 terminator */ - array_start[0] = '\0'; - } + ecs_entity_t type = ecs_get_typeid(world, id); + if (!type) { + flecs_reply_error(reply, "component '%s' is not a type", component); + reply->code = 400; + return true; } - /* Expect a ; */ - if (*ptr != ';') { - ecs_meta_error(ctx, ptr, "missing ; after member declaration"); - goto error; + const void *ptr = ecs_get_id(world, e, id); + if (!ptr) { + flecs_reply_error(reply, "failed to get component '%s'", component); + reply->code = 500; + return true; } - return ptr + 1; -error: - return NULL; + ecs_ptr_to_json_buf(world, type, ptr, &reply->body); + + return true; } static -int meta_parse_desc( - const char *ptr, - meta_params_t *token, - meta_parse_ctx_t *ctx) +bool flecs_rest_put_component( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + const char *path) { - token->is_key_value = false; - token->is_fixed_size = false; - - ptr = ecs_parse_ws_eol(ptr); - if (*ptr != '(' && *ptr != '<') { - ecs_meta_error(ctx, ptr, - "expected '(' at start of collection definition"); - goto error; + ecs_entity_t e; + if (!(e = flecs_rest_entity_from_path(world, reply, path))) { + return true; } - ptr ++; - - /* Parse type identifier */ - ptr = meta_parse_type(ptr, &token->type, ctx); - if (!ptr) { - goto error; + const char *component = ecs_http_get_param(req, "component"); + if (!component) { + flecs_reply_error(reply, "missing component for remove endpoint"); + reply->code = 400; + return true; } - ptr = ecs_parse_ws_eol(ptr); - - /* If next token is a ',' the first type was a key type */ - if (*ptr == ',') { - ptr = ecs_parse_ws_eol(ptr + 1); - - if (isdigit(*ptr)) { - int64_t value; - ptr = parse_c_digit(ptr, &value); - if (!ptr) { - goto error; - } + ecs_entity_t id; + if (!flecs_id_parse(world, path, component, &id)) { + flecs_reply_error(reply, "unresolved component '%s'", component); + reply->code = 400; + return true; + } - token->count = value; - token->is_fixed_size = true; - } else { - token->key_type = token->type; + const char *data = ecs_http_get_param(req, "value"); + if (!data) { + ecs_add_id(world, e, id); + return true; + } - /* Parse element type */ - ptr = meta_parse_type(ptr, &token->type, ctx); - ptr = ecs_parse_ws_eol(ptr); + ecs_entity_t type = ecs_get_typeid(world, id); + if (!type) { + flecs_reply_error(reply, "component '%s' is not a type", component); + reply->code = 400; + return true; + } - token->is_key_value = true; - } + void *ptr = ecs_ensure_id(world, e, id); + if (!ptr) { + flecs_reply_error(reply, "failed to create component '%s'", component); + reply->code = 500; + return true; } - if (*ptr != ')' && *ptr != '>') { - ecs_meta_error(ctx, ptr, - "expected ')' at end of collection definition"); - goto error; + if (!ecs_ptr_from_json(world, type, ptr, data, NULL)) { + flecs_reply_error(reply, "invalid value for component '%s'", component); + reply->code = 400; + return true; } - return 0; -error: - return -1; -} + ecs_modified_id(world, e, id); -static -ecs_entity_t meta_lookup( - ecs_world_t *world, - meta_type_t *token, - const char *ptr, - int64_t count, - meta_parse_ctx_t *ctx); + return true; +} static -ecs_entity_t meta_lookup_array( +bool flecs_rest_delete_component( ecs_world_t *world, - ecs_entity_t e, - const char *params_decl, - meta_parse_ctx_t *ctx) + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + const char *path) { - meta_parse_ctx_t param_ctx = { - .name = ctx->name, - .desc = params_decl - }; - - meta_params_t params; - if (meta_parse_desc(params_decl, ¶ms, ¶m_ctx)) { - goto error; - } - if (!params.is_fixed_size) { - ecs_meta_error(ctx, params_decl, "missing size for array"); - goto error; - } - - if (!params.count) { - ecs_meta_error(ctx, params_decl, "invalid array size"); - goto error; + ecs_entity_t e; + if (!(e = flecs_rest_entity_from_path(world, reply, path))) { + return true; } - ecs_entity_t element_type = ecs_lookup_symbol( - world, params.type.type, true, true); - if (!element_type) { - ecs_meta_error(ctx, params_decl, "unknown element type '%s'", - params.type.type); + const char *component = ecs_http_get_param(req, "component"); + if (!component) { + flecs_reply_error(reply, "missing component for remove endpoint"); + reply->code = 400; + return true; } - if (!e) { - e = ecs_new_id(world); + ecs_entity_t id; + if (!flecs_id_parse(world, path, component, &id)) { + flecs_reply_error(reply, "unresolved component '%s'", component); + reply->code = 400; + return true; } - ecs_check(params.count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL); + ecs_remove_id(world, e, id); - return ecs_set(world, e, EcsArray, { element_type, (int32_t)params.count }); -error: - return 0; + return true; } static -ecs_entity_t meta_lookup_vector( +bool flecs_rest_delete_entity( ecs_world_t *world, - ecs_entity_t e, - const char *params_decl, - meta_parse_ctx_t *ctx) + ecs_http_reply_t *reply, + const char *path) { - meta_parse_ctx_t param_ctx = { - .name = ctx->name, - .desc = params_decl - }; - - meta_params_t params; - if (meta_parse_desc(params_decl, ¶ms, ¶m_ctx)) { - goto error; - } - - if (params.is_key_value) { - ecs_meta_error(ctx, params_decl, - "unexpected key value parameters for vector"); - goto error; - } - - ecs_entity_t element_type = meta_lookup( - world, ¶ms.type, params_decl, 1, ¶m_ctx); - - if (!e) { - e = ecs_new_id(world); + ecs_entity_t e; + if (!(e = flecs_rest_entity_from_path(world, reply, path))) { + return true; } - return ecs_set(world, e, EcsVector, { element_type }); -error: - return 0; + ecs_delete(world, e); + + return true; } static -ecs_entity_t meta_lookup_bitmask( +bool flecs_rest_toggle( ecs_world_t *world, - ecs_entity_t e, - const char *params_decl, - meta_parse_ctx_t *ctx) + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + const char *path) { - (void)e; + ecs_entity_t e; + if (!(e = flecs_rest_entity_from_path(world, reply, path))) { + return true; + } - meta_parse_ctx_t param_ctx = { - .name = ctx->name, - .desc = params_decl - }; + bool enable = true; + flecs_rest_bool_param(req, "enable", &enable); - meta_params_t params; - if (meta_parse_desc(params_decl, ¶ms, ¶m_ctx)) { - goto error; + const char *component = ecs_http_get_param(req, "component"); + if (!component) { + ecs_enable(world, e, enable); + return true; } - if (params.is_key_value) { - ecs_meta_error(ctx, params_decl, - "unexpected key value parameters for bitmask"); - goto error; + ecs_entity_t id; + if (!flecs_id_parse(world, path, component, &id)) { + flecs_reply_error(reply, "unresolved component '%s'", component); + reply->code = 400; + return true; } - if (params.is_fixed_size) { - ecs_meta_error(ctx, params_decl, - "unexpected size for bitmask"); - goto error; + ecs_entity_t rel = 0; + if (ECS_IS_PAIR(id)) { + rel = ecs_pair_first(world, id); + } else { + rel = id & ECS_COMPONENT_MASK; } - ecs_entity_t bitmask_type = meta_lookup( - world, ¶ms.type, params_decl, 1, ¶m_ctx); - ecs_check(bitmask_type != 0, ECS_INVALID_PARAMETER, NULL); - -#ifndef FLECS_NDEBUG - /* Make sure this is a bitmask type */ - const EcsMetaType *type_ptr = ecs_get(world, bitmask_type, EcsMetaType); - ecs_check(type_ptr != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(type_ptr->kind == EcsBitmaskType, ECS_INVALID_PARAMETER, NULL); -#endif + if (!ecs_has_id(world, rel, EcsCanToggle)) { + flecs_reply_error(reply, "cannot toggle component '%s'", component); + reply->code = 400; + return true; + } - return bitmask_type; -error: - return 0; + ecs_enable_id(world, e, id, enable); + + return true; } static -ecs_entity_t meta_lookup( +bool flecs_rest_script( ecs_world_t *world, - meta_type_t *token, - const char *ptr, - int64_t count, - meta_parse_ctx_t *ctx) + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + const char *path) { - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(token != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ctx != NULL, ECS_INTERNAL_ERROR, NULL); + (void)world; + (void)req; + (void)reply; +#ifdef FLECS_SCRIPT + ecs_entity_t script = flecs_rest_entity_from_path(world, reply, path); + if (!script) { + script = ecs_entity(world, { .name = path }); + } - const char *typename = token->type; - ecs_entity_t type = 0; + const char *code = ecs_http_get_param(req, "code"); + if (!code) { + flecs_reply_error(reply, "missing data parameter"); + return true; + } - /* Parse vector type */ - if (!token->is_ptr) { - if (!ecs_os_strcmp(typename, "ecs_array")) { - type = meta_lookup_array(world, 0, token->params, ctx); + bool try = false; + flecs_rest_bool_param(req, "try", &try); - } else if (!ecs_os_strcmp(typename, "ecs_vector") || - !ecs_os_strcmp(typename, "flecs::vector")) - { - type = meta_lookup_vector(world, 0, token->params, ctx); + bool prev_color = ecs_log_enable_colors(false); + ecs_os_api_log_t prev_log = ecs_os_api.log_; + rest_prev_log = try ? NULL : prev_log; + ecs_os_api.log_ = flecs_rest_capture_log; - } else if (!ecs_os_strcmp(typename, "flecs::bitmask")) { - type = meta_lookup_bitmask(world, 0, token->params, ctx); + script = ecs_script(world, { + .entity = script, + .code = code + }); - } else if (!ecs_os_strcmp(typename, "flecs::byte")) { - type = ecs_id(ecs_byte_t); + if (!script) { + char *err = flecs_rest_get_captured_log(); + char *escaped_err = flecs_astresc('"', err); + if (escaped_err) { + flecs_reply_error(reply, escaped_err); + } else { + flecs_reply_error(reply, "error parsing script"); + } + if (!try) { + reply->code = 400; /* bad request */ + } + ecs_os_free(escaped_err); + ecs_os_free(err); + } - } else if (!ecs_os_strcmp(typename, "char")) { - type = ecs_id(ecs_char_t); + ecs_os_api.log_ = prev_log; + ecs_log_enable_colors(prev_color); - } else if (!ecs_os_strcmp(typename, "bool") || - !ecs_os_strcmp(typename, "_Bool")) - { - type = ecs_id(ecs_bool_t); + return true; +#else + return false; +#endif +} - } else if (!ecs_os_strcmp(typename, "int8_t")) { - type = ecs_id(ecs_i8_t); - } else if (!ecs_os_strcmp(typename, "int16_t")) { - type = ecs_id(ecs_i16_t); - } else if (!ecs_os_strcmp(typename, "int32_t")) { - type = ecs_id(ecs_i32_t); - } else if (!ecs_os_strcmp(typename, "int64_t")) { - type = ecs_id(ecs_i64_t); +static +void flecs_rest_reply_set_captured_log( + ecs_http_reply_t *reply) +{ + char *err = flecs_rest_get_captured_log(); + if (err) { + char *escaped_err = flecs_astresc('"', err); + flecs_reply_error(reply, escaped_err); + ecs_os_free(escaped_err); + ecs_os_free(err); + } - } else if (!ecs_os_strcmp(typename, "uint8_t")) { - type = ecs_id(ecs_u8_t); - } else if (!ecs_os_strcmp(typename, "uint16_t")) { - type = ecs_id(ecs_u16_t); - } else if (!ecs_os_strcmp(typename, "uint32_t")) { - type = ecs_id(ecs_u32_t); - } else if (!ecs_os_strcmp(typename, "uint64_t")) { - type = ecs_id(ecs_u64_t); + reply->code = 400; +} - } else if (!ecs_os_strcmp(typename, "float")) { - type = ecs_id(ecs_f32_t); - } else if (!ecs_os_strcmp(typename, "double")) { - type = ecs_id(ecs_f64_t); +static +void flecs_rest_iter_to_reply( + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + ecs_poly_t *query, + ecs_iter_t *it) +{ + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + flecs_rest_parse_json_ser_iter_params(&desc, req); + desc.query = query; - } else if (!ecs_os_strcmp(typename, "ecs_entity_t")) { - type = ecs_id(ecs_entity_t); + int32_t offset = 0; + int32_t limit = 1000; - } else if (!ecs_os_strcmp(typename, "ecs_id_t")) { - type = ecs_id(ecs_id_t); + flecs_rest_int_param(req, "offset", &offset); + flecs_rest_int_param(req, "limit", &limit); - } else if (!ecs_os_strcmp(typename, "char*")) { - type = ecs_id(ecs_string_t); + if (offset < 0 || limit < 0) { + flecs_reply_error(reply, "invalid offset/limit parameter"); + return; + } + + ecs_iter_t pit = ecs_page_iter(it, offset, limit); + if (ecs_iter_to_json_buf(&pit, &reply->body, &desc)) { + flecs_rest_reply_set_captured_log(reply); + } + + flecs_rest_int_param(req, "offset", &offset); +} + +static +bool flecs_rest_reply_existing_query( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + const char *name) +{ + ecs_entity_t qe = ecs_lookup(world, name); + if (!qe) { + flecs_reply_error(reply, "unresolved identifier '%s'", name); + reply->code = 404; + return true; + } + + ecs_query_t *q = NULL; + const EcsPoly *poly_comp = ecs_get_pair(world, qe, EcsPoly, EcsQuery); + if (!poly_comp) { + poly_comp = ecs_get_pair(world, qe, EcsPoly, EcsObserver); + if (poly_comp) { + q = ((ecs_observer_t*)poly_comp->poly)->query; } else { - type = ecs_lookup_symbol(world, typename, true, true); + flecs_reply_error(reply, + "resolved identifier '%s' is not a query", name); + reply->code = 400; + return true; } } else { - if (!ecs_os_strcmp(typename, "char")) { - typename = "flecs.meta.string"; - } else - if (token->is_ptr) { - typename = "flecs.meta.uptr"; - } else - if (!ecs_os_strcmp(typename, "char*") || - !ecs_os_strcmp(typename, "flecs::string")) - { - typename = "flecs.meta.string"; - } + q = poly_comp->poly; + } - type = ecs_lookup_symbol(world, typename, true, true); + if (!q) { + flecs_reply_error(reply, "query '%s' is not initialized", name); + reply->code = 400; + return true; } - if (count != 1) { - ecs_check(count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL); + ecs_iter_t it = ecs_query_iter(world, q); - type = ecs_set(world, 0, EcsArray, {type, (int32_t)count}); - } + ecs_dbg_2("rest: request query '%s'", name); + bool prev_color = ecs_log_enable_colors(false); + rest_prev_log = ecs_os_api.log_; + ecs_os_api.log_ = flecs_rest_capture_log; - if (!type) { - ecs_meta_error(ctx, ptr, "unknown type '%s'", typename); - goto error; + const char *vars = ecs_http_get_param(req, "vars"); + if (vars) { + #ifdef FLECS_SCRIPT + if (ecs_query_args_parse(q, &it, vars) == NULL) { + flecs_rest_reply_set_captured_log(reply); + return true; + } + #else + flecs_reply_error(reply, + "cannot parse query arg expression: script addon required"); + reply->code = 400; + return true; + #endif } - return type; -error: - return 0; + flecs_rest_iter_to_reply(req, reply, q, &it); + + ecs_os_api.log_ = rest_prev_log; + ecs_log_enable_colors(prev_color); + + return true; } static -int meta_parse_struct( +bool flecs_rest_get_query( ecs_world_t *world, - ecs_entity_t t, - const char *desc) + const ecs_http_request_t* req, + ecs_http_reply_t *reply) { - const char *ptr = desc; - const char *name = ecs_get_name(world, t); + const char *q_name = ecs_http_get_param(req, "name"); + if (q_name) { + return flecs_rest_reply_existing_query(world, req, reply, q_name); + } - meta_member_t token; - meta_parse_ctx_t ctx = { - .name = name, - .desc = ptr - }; + const char *expr = ecs_http_get_param(req, "expr"); + if (!expr) { + ecs_strbuf_appendlit(&reply->body, "Missing parameter 'expr'"); + reply->code = 400; /* bad request */ + return true; + } - ecs_entity_t old_scope = ecs_set_scope(world, t); + bool try = false; + flecs_rest_bool_param(req, "try", &try); - while ((ptr = meta_parse_member(ptr, &token, &ctx)) && ptr[0]) { - ecs_entity_t m = ecs_entity(world, { - .name = token.name - }); + ecs_dbg_2("rest: request query '%s'", expr); + bool prev_color = ecs_log_enable_colors(false); + ecs_os_api_log_t prev_log = ecs_os_api.log_; + rest_prev_log = try ? NULL : prev_log; + ecs_os_api.log_ = flecs_rest_capture_log; - ecs_entity_t type = meta_lookup( - world, &token.type, ptr, 1, &ctx); - if (!type) { - goto error; + ecs_query_t *q = ecs_query(world, { .expr = expr }); + if (!q) { + flecs_rest_reply_set_captured_log(reply); + if (try) { + /* If client is trying queries, don't spam console with errors */ + reply->code = 200; } + } else { + ecs_iter_t it = ecs_query_iter(world, q); + flecs_rest_iter_to_reply(req, reply, q, &it); + ecs_query_fini(q); + } - ecs_set(world, m, EcsMember, { - .type = type, - .count = (ecs_size_t)token.count - }); + ecs_os_api.log_ = prev_log; + ecs_log_enable_colors(prev_color); + + return true; +} + +#ifdef FLECS_STATS + +static +void flecs_rest_array_append_( + ecs_strbuf_t *reply, + const char *field, + int32_t field_len, + const ecs_float_t *values, + int32_t t) +{ + ecs_strbuf_list_appendch(reply, '"'); + ecs_strbuf_appendstrn(reply, field, field_len); + ecs_strbuf_appendlit(reply, "\":"); + ecs_strbuf_list_push(reply, "[", ","); + + int32_t i; + for (i = t + 1; i <= (t + ECS_STAT_WINDOW); i ++) { + int32_t index = i % ECS_STAT_WINDOW; + ecs_strbuf_list_next(reply); + ecs_strbuf_appendflt(reply, (double)values[index], '"'); } - ecs_set_scope(world, old_scope); + ecs_strbuf_list_pop(reply, "]"); +} - return 0; -error: - return -1; +#define flecs_rest_array_append(reply, field, values, t)\ + flecs_rest_array_append_(reply, field, sizeof(field) - 1, values, t) + +static +void flecs_rest_gauge_append( + ecs_strbuf_t *reply, + const ecs_metric_t *m, + const char *field, + int32_t field_len, + int32_t t, + const char *brief, + int32_t brief_len) +{ + ecs_strbuf_list_appendch(reply, '"'); + ecs_strbuf_appendstrn(reply, field, field_len); + ecs_strbuf_appendlit(reply, "\":"); + ecs_strbuf_list_push(reply, "{", ","); + + flecs_rest_array_append(reply, "avg", m->gauge.avg, t); + flecs_rest_array_append(reply, "min", m->gauge.min, t); + flecs_rest_array_append(reply, "max", m->gauge.max, t); + + if (brief) { + ecs_strbuf_list_appendlit(reply, "\"brief\":\""); + ecs_strbuf_appendstrn(reply, brief, brief_len); + ecs_strbuf_appendch(reply, '"'); + } + + ecs_strbuf_list_pop(reply, "}"); } static -int meta_parse_constants( - ecs_world_t *world, - ecs_entity_t t, - const char *desc, - bool is_bitmask) +void flecs_rest_counter_append( + ecs_strbuf_t *reply, + const ecs_metric_t *m, + const char *field, + int32_t field_len, + int32_t t, + const char *brief, + int32_t brief_len) { - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(t != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_rest_gauge_append(reply, m, field, field_len, t, brief, brief_len); +} - const char *ptr = desc; - const char *name = ecs_get_name(world, t); - int32_t name_len = ecs_os_strlen(name); - const ecs_world_info_t *info = ecs_get_world_info(world); - const char *name_prefix = info->name_prefix; - int32_t name_prefix_len = name_prefix ? ecs_os_strlen(name_prefix) : 0; +#define ECS_GAUGE_APPEND_T(reply, s, field, t, brief)\ + flecs_rest_gauge_append(reply, &(s)->field, #field, sizeof(#field) - 1, t, brief, sizeof(brief) - 1) - meta_parse_ctx_t ctx = { - .name = name, - .desc = ptr - }; +#define ECS_COUNTER_APPEND_T(reply, s, field, t, brief)\ + flecs_rest_counter_append(reply, &(s)->field, #field, sizeof(#field) - 1, t, brief, sizeof(brief) - 1) - meta_constant_t token; - int64_t last_value = 0; +#define ECS_GAUGE_APPEND(reply, s, field, brief)\ + ECS_GAUGE_APPEND_T(reply, s, field, (s)->t, brief) - ecs_entity_t old_scope = ecs_set_scope(world, t); +#define ECS_COUNTER_APPEND(reply, s, field, brief)\ + ECS_COUNTER_APPEND_T(reply, s, field, (s)->t, brief) - while ((ptr = meta_parse_constant(ptr, &token, &ctx))) { - if (token.is_value_set) { - last_value = token.value; - } else if (is_bitmask) { - ecs_meta_error(&ctx, ptr, - "bitmask requires explicit value assignment"); - goto error; - } +static +void flecs_world_stats_to_json( + ecs_strbuf_t *reply, + const EcsWorldStats *monitor_stats) +{ + const ecs_world_stats_t *stats = &monitor_stats->stats; - if (name_prefix) { - if (!ecs_os_strncmp(token.name, name_prefix, name_prefix_len)) { - ecs_os_memmove(token.name, token.name + name_prefix_len, - ecs_os_strlen(token.name) - name_prefix_len + 1); - } - } + ecs_strbuf_list_push(reply, "{", ","); + ECS_GAUGE_APPEND(reply, stats, entities.count, "Alive entity ids in the world"); + ECS_GAUGE_APPEND(reply, stats, entities.not_alive_count, "Not alive entity ids in the world"); - if (!ecs_os_strncmp(token.name, name, name_len)) { - ecs_os_memmove(token.name, token.name + name_len, - ecs_os_strlen(token.name) - name_len + 1); - } + ECS_GAUGE_APPEND(reply, stats, performance.fps, "Frames per second"); + ECS_COUNTER_APPEND(reply, stats, performance.frame_time, "Time spent in frame"); + ECS_COUNTER_APPEND(reply, stats, performance.system_time, "Time spent on running systems in frame"); + ECS_COUNTER_APPEND(reply, stats, performance.emit_time, "Time spent on notifying observers in frame"); + ECS_COUNTER_APPEND(reply, stats, performance.merge_time, "Time spent on merging commands in frame"); + ECS_COUNTER_APPEND(reply, stats, performance.rematch_time, "Time spent on revalidating query caches in frame"); - ecs_entity_t c = ecs_entity(world, { - .name = token.name - }); + ECS_COUNTER_APPEND(reply, stats, commands.add_count, "Add commands executed"); + ECS_COUNTER_APPEND(reply, stats, commands.remove_count, "Remove commands executed"); + ECS_COUNTER_APPEND(reply, stats, commands.delete_count, "Delete commands executed"); + ECS_COUNTER_APPEND(reply, stats, commands.clear_count, "Clear commands executed"); + ECS_COUNTER_APPEND(reply, stats, commands.set_count, "Set commands executed"); + ECS_COUNTER_APPEND(reply, stats, commands.ensure_count, "Get_mut commands executed"); + ECS_COUNTER_APPEND(reply, stats, commands.modified_count, "Modified commands executed"); + ECS_COUNTER_APPEND(reply, stats, commands.other_count, "Misc commands executed"); + ECS_COUNTER_APPEND(reply, stats, commands.discard_count, "Commands for already deleted entities"); + ECS_COUNTER_APPEND(reply, stats, commands.batched_entity_count, "Entities with batched commands"); + ECS_COUNTER_APPEND(reply, stats, commands.batched_count, "Number of commands batched"); - if (!is_bitmask) { - ecs_set_pair_object(world, c, EcsConstant, ecs_i32_t, - {(ecs_i32_t)last_value}); - } else { - ecs_set_pair_object(world, c, EcsConstant, ecs_u32_t, - {(ecs_u32_t)last_value}); - } + ECS_COUNTER_APPEND(reply, stats, frame.merge_count, "Number of merges (sync points)"); + ECS_COUNTER_APPEND(reply, stats, frame.pipeline_build_count, "Pipeline rebuilds (happen when systems become active/enabled)"); + ECS_COUNTER_APPEND(reply, stats, frame.systems_ran, "Systems ran in frame"); + ECS_COUNTER_APPEND(reply, stats, frame.observers_ran, "Number of times an observer was invoked in frame"); + ECS_COUNTER_APPEND(reply, stats, frame.event_emit_count, "Events emitted in frame"); + ECS_COUNTER_APPEND(reply, stats, frame.rematch_count, "Number of query cache revalidations"); - last_value ++; - } + ECS_GAUGE_APPEND(reply, stats, tables.count, "Tables in the world (including empty)"); + ECS_GAUGE_APPEND(reply, stats, tables.empty_count, "Empty tables in the world"); + ECS_COUNTER_APPEND(reply, stats, tables.create_count, "Number of new tables created"); + ECS_COUNTER_APPEND(reply, stats, tables.delete_count, "Number of tables deleted"); - ecs_set_scope(world, old_scope); + ECS_GAUGE_APPEND(reply, stats, components.tag_count, "Tag ids in use"); + ECS_GAUGE_APPEND(reply, stats, components.component_count, "Component ids in use"); + ECS_GAUGE_APPEND(reply, stats, components.pair_count, "Pair ids in use"); + ECS_GAUGE_APPEND(reply, stats, components.type_count, "Registered component types"); + ECS_COUNTER_APPEND(reply, stats, components.create_count, "Number of new component, tag and pair ids created"); + ECS_COUNTER_APPEND(reply, stats, components.delete_count, "Number of component, pair and tag ids deleted"); - return 0; -error: - return -1; + ECS_GAUGE_APPEND(reply, stats, queries.query_count, "Queries in the world"); + ECS_GAUGE_APPEND(reply, stats, queries.observer_count, "Observers in the world"); + ECS_GAUGE_APPEND(reply, stats, queries.system_count, "Systems in the world"); + + ECS_COUNTER_APPEND(reply, stats, memory.alloc_count, "Allocations by OS API"); + ECS_COUNTER_APPEND(reply, stats, memory.realloc_count, "Reallocs by OS API"); + ECS_COUNTER_APPEND(reply, stats, memory.free_count, "Frees by OS API"); + ECS_GAUGE_APPEND(reply, stats, memory.outstanding_alloc_count, "Outstanding allocations by OS API"); + ECS_COUNTER_APPEND(reply, stats, memory.block_alloc_count, "Blocks allocated by block allocators"); + ECS_COUNTER_APPEND(reply, stats, memory.block_free_count, "Blocks freed by block allocators"); + ECS_GAUGE_APPEND(reply, stats, memory.block_outstanding_alloc_count, "Outstanding block allocations"); + ECS_COUNTER_APPEND(reply, stats, memory.stack_alloc_count, "Pages allocated by stack allocators"); + ECS_COUNTER_APPEND(reply, stats, memory.stack_free_count, "Pages freed by stack allocators"); + ECS_GAUGE_APPEND(reply, stats, memory.stack_outstanding_alloc_count, "Outstanding page allocations"); + + ECS_COUNTER_APPEND(reply, stats, http.request_received_count, "Received requests"); + ECS_COUNTER_APPEND(reply, stats, http.request_invalid_count, "Received invalid requests"); + ECS_COUNTER_APPEND(reply, stats, http.request_handled_ok_count, "Requests handled successfully"); + ECS_COUNTER_APPEND(reply, stats, http.request_handled_error_count, "Requests handled with error code"); + ECS_COUNTER_APPEND(reply, stats, http.request_not_handled_count, "Requests not handled (unknown endpoint)"); + ECS_COUNTER_APPEND(reply, stats, http.request_preflight_count, "Preflight requests received"); + ECS_COUNTER_APPEND(reply, stats, http.send_ok_count, "Successful replies"); + ECS_COUNTER_APPEND(reply, stats, http.send_error_count, "Unsuccessful replies"); + ECS_COUNTER_APPEND(reply, stats, http.busy_count, "Dropped requests due to full send queue (503)"); + + ecs_strbuf_list_pop(reply, "}"); } static -int meta_parse_enum( +void flecs_system_stats_to_json( ecs_world_t *world, - ecs_entity_t t, - const char *desc) + ecs_strbuf_t *reply, + ecs_entity_t system, + const ecs_system_stats_t *stats) { - ecs_add(world, t, EcsEnum); - return meta_parse_constants(world, t, desc, false); + ecs_strbuf_list_push(reply, "{", ","); + ecs_strbuf_list_appendlit(reply, "\"name\":\""); + ecs_get_path_w_sep_buf(world, 0, system, ".", NULL, reply, true); + ecs_strbuf_appendch(reply, '"'); + + bool disabled = ecs_has_id(world, system, EcsDisabled); + ecs_strbuf_list_appendlit(reply, "\"disabled\":"); + ecs_strbuf_appendstr(reply, disabled ? "true" : "false"); + + if (!stats->task) { + ECS_GAUGE_APPEND(reply, &stats->query, matched_table_count, ""); + ECS_GAUGE_APPEND(reply, &stats->query, matched_entity_count, ""); + } + + ECS_COUNTER_APPEND_T(reply, stats, time_spent, stats->query.t, ""); + ecs_strbuf_list_pop(reply, "}"); } static -int meta_parse_bitmask( - ecs_world_t *world, - ecs_entity_t t, - const char *desc) +void flecs_sync_stats_to_json( + ecs_http_reply_t *reply, + const ecs_pipeline_stats_t *pstats, + const ecs_sync_stats_t *stats) { - ecs_add(world, t, EcsBitmask); - return meta_parse_constants(world, t, desc, true); + ecs_strbuf_list_push(&reply->body, "{", ","); + + ecs_strbuf_list_appendlit(&reply->body, "\"multi_threaded\":"); + ecs_strbuf_appendbool(&reply->body, stats->multi_threaded); + + ecs_strbuf_list_appendlit(&reply->body, "\"immediate\":"); + ecs_strbuf_appendbool(&reply->body, stats->immediate); + + ECS_GAUGE_APPEND_T(&reply->body, stats, time_spent, pstats->t, ""); + ECS_GAUGE_APPEND_T(&reply->body, stats, commands_enqueued, pstats->t, ""); + + ecs_strbuf_list_pop(&reply->body, "}"); } -int ecs_meta_from_desc( +static +void flecs_all_systems_stats_to_json( ecs_world_t *world, - ecs_entity_t component, - ecs_type_kind_t kind, - const char *desc) + ecs_http_reply_t *reply, + ecs_entity_t period) { - switch(kind) { - case EcsStructType: - if (meta_parse_struct(world, component, desc)) { - goto error; - } - break; - case EcsEnumType: - if (meta_parse_enum(world, component, desc)) { - goto error; - } - break; - case EcsBitmaskType: - if (meta_parse_bitmask(world, component, desc)) { - goto error; + const EcsSystemStats *stats = ecs_get_pair(world, EcsWorld, + EcsSystemStats, period); + + ecs_strbuf_list_push(&reply->body, "[", ","); + + if (stats) { + ecs_map_iter_t it = ecs_map_iter(&stats->stats); + while (ecs_map_next(&it)) { + ecs_entity_t id = ecs_map_key(&it); + ecs_system_stats_t *sys_stats = ecs_map_ptr(&it); + + if (!ecs_is_alive(world, id)) { + continue; + } + + ecs_strbuf_list_next(&reply->body); + flecs_system_stats_to_json(world, &reply->body, id, sys_stats); } - break; - case EcsPrimitiveType: - case EcsArrayType: - case EcsVectorType: - case EcsOpaqueType: - break; - default: - ecs_throw(ECS_INTERNAL_ERROR, "invalid type kind"); } - return 0; -error: - return -1; + ecs_strbuf_list_pop(&reply->body, "]"); } -#endif +static +void flecs_pipeline_stats_to_json( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + ecs_entity_t period) +{ + char *pipeline_name = NULL; + flecs_rest_string_param(req, "name", &pipeline_name); -/** - * @file addons/metrics.c - * @brief Metrics addon. - */ + if (!pipeline_name || !ecs_os_strcmp(pipeline_name, "all")) { + flecs_all_systems_stats_to_json(world, reply, period); + return; + } + ecs_entity_t e = ecs_lookup(world, pipeline_name); + if (!e) { + flecs_reply_error(reply, "pipeline '%s' not found", pipeline_name); + reply->code = 404; + return; + } -#ifdef FLECS_METRICS + const EcsPipelineStats *stats = ecs_get_pair(world, EcsWorld, + EcsPipelineStats, period); + const EcsSystemStats *system_stats = ecs_get_pair(world, EcsWorld, + EcsSystemStats, period); + if (!stats || !system_stats) { + goto noresults; + } -/* Public components */ -ECS_COMPONENT_DECLARE(FlecsMetrics); -ECS_TAG_DECLARE(EcsMetricInstance); -ECS_COMPONENT_DECLARE(EcsMetricValue); -ECS_COMPONENT_DECLARE(EcsMetricSource); -ECS_TAG_DECLARE(EcsMetric); -ECS_TAG_DECLARE(EcsCounter); -ECS_TAG_DECLARE(EcsCounterIncrement); -ECS_TAG_DECLARE(EcsCounterId); -ECS_TAG_DECLARE(EcsGauge); - -/* Internal components */ -static ECS_COMPONENT_DECLARE(EcsMetricMember); -static ECS_COMPONENT_DECLARE(EcsMetricId); -static ECS_COMPONENT_DECLARE(EcsMetricOneOf); -static ECS_COMPONENT_DECLARE(EcsMetricCountIds); -static ECS_COMPONENT_DECLARE(EcsMetricCountTargets); -static ECS_COMPONENT_DECLARE(EcsMetricMemberInstance); -static ECS_COMPONENT_DECLARE(EcsMetricIdInstance); -static ECS_COMPONENT_DECLARE(EcsMetricOneOfInstance); - -/** Context for metric */ -typedef struct { - ecs_entity_t metric; /**< Metric entity */ - ecs_entity_t kind; /**< Metric kind (gauge, counter) */ -} ecs_metric_ctx_t; - -/** Context for metric that monitors member */ -typedef struct { - ecs_metric_ctx_t metric; - ecs_primitive_kind_t type_kind; /**< Primitive type kind of member */ - uint16_t offset; /**< Offset of member in component */ -} ecs_member_metric_ctx_t; - -/** Context for metric that monitors whether entity has id */ -typedef struct { - ecs_metric_ctx_t metric; - ecs_id_record_t *idr; /**< Id record for monitored component */ -} ecs_id_metric_ctx_t; - -/** Context for metric that monitors whether entity has pair target */ -typedef struct { - ecs_metric_ctx_t metric; - ecs_id_record_t *idr; /**< Id record for monitored component */ - ecs_size_t size; /**< Size of metric type */ - ecs_map_t target_offset; /**< Pair target to metric type offset */ -} ecs_oneof_metric_ctx_t; - -/** Context for metric that monitors how many entities have a pair target */ -typedef struct { - ecs_metric_ctx_t metric; - ecs_id_record_t *idr; /**< Id record for monitored component */ - ecs_map_t targets; /**< Map of counters for each target */ -} ecs_count_targets_metric_ctx_t; - -/** Stores context shared for all instances of member metric */ -typedef struct { - ecs_member_metric_ctx_t *ctx; -} EcsMetricMember; - -/** Stores context shared for all instances of id metric */ -typedef struct { - ecs_id_metric_ctx_t *ctx; -} EcsMetricId; - -/** Stores context shared for all instances of oneof metric */ -typedef struct { - ecs_oneof_metric_ctx_t *ctx; -} EcsMetricOneOf; - -/** Stores context shared for all instances of id counter metric */ -typedef struct { - ecs_id_t id; -} EcsMetricCountIds; - -/** Stores context shared for all instances of target counter metric */ -typedef struct { - ecs_count_targets_metric_ctx_t *ctx; -} EcsMetricCountTargets; - -/** Instance of member metric */ -typedef struct { - ecs_ref_t ref; - ecs_member_metric_ctx_t *ctx; -} EcsMetricMemberInstance; - -/** Instance of id metric */ -typedef struct { - ecs_record_t *r; - ecs_id_metric_ctx_t *ctx; -} EcsMetricIdInstance; + ecs_pipeline_stats_t *pstats = ecs_map_get_deref( + &stats->stats, ecs_pipeline_stats_t, e); + if (!pstats) { + goto noresults; + } -/** Instance of oneof metric */ -typedef struct { - ecs_record_t *r; - ecs_oneof_metric_ctx_t *ctx; -} EcsMetricOneOfInstance; + const EcsPipeline *p = ecs_get(world, e, EcsPipeline); -/** Component lifecycle */ + ecs_strbuf_list_push(&reply->body, "[", ","); -static ECS_DTOR(EcsMetricMember, ptr, { - ecs_os_free(ptr->ctx); -}) + ecs_pipeline_op_t *ops = ecs_vec_first_t(&p->state->ops, ecs_pipeline_op_t); + ecs_entity_t *systems = ecs_vec_first_t(&p->state->systems, ecs_entity_t); + ecs_sync_stats_t *syncs = ecs_vec_first_t( + &pstats->sync_points, ecs_sync_stats_t); -static ECS_MOVE(EcsMetricMember, dst, src, { - *dst = *src; - src->ctx = NULL; -}) + int32_t s, o, op_count = ecs_vec_count(&p->state->ops); + for (o = 0; o < op_count; o ++) { + ecs_pipeline_op_t *op = &ops[o]; + for (s = op->offset; s < (op->offset + op->count); s ++) { + ecs_entity_t system = systems[s]; -static ECS_DTOR(EcsMetricId, ptr, { - ecs_os_free(ptr->ctx); -}) + if (!ecs_is_alive(world, system)) { + continue; + } -static ECS_MOVE(EcsMetricId, dst, src, { - *dst = *src; - src->ctx = NULL; -}) + ecs_system_stats_t *sys_stats = ecs_map_get_deref( + &system_stats->stats, ecs_system_stats_t, system); + ecs_strbuf_list_next(&reply->body); + flecs_system_stats_to_json(world, &reply->body, system, sys_stats); + } -static ECS_DTOR(EcsMetricOneOf, ptr, { - if (ptr->ctx) { - ecs_map_fini(&ptr->ctx->target_offset); - ecs_os_free(ptr->ctx); + ecs_strbuf_list_next(&reply->body); + flecs_sync_stats_to_json(reply, pstats, &syncs[o]); } -}) -static ECS_MOVE(EcsMetricOneOf, dst, src, { - *dst = *src; - src->ctx = NULL; -}) + ecs_strbuf_list_pop(&reply->body, "]"); + return; +noresults: + ecs_strbuf_appendlit(&reply->body, "[]"); +} -static ECS_DTOR(EcsMetricCountTargets, ptr, { - if (ptr->ctx) { - ecs_map_fini(&ptr->ctx->targets); - ecs_os_free(ptr->ctx); - } -}) +static +bool flecs_rest_get_stats( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) +{ + char *period_str = NULL; + flecs_rest_string_param(req, "period", &period_str); + char *category = &req->path[6]; -static ECS_MOVE(EcsMetricCountTargets, dst, src, { - *dst = *src; - src->ctx = NULL; -}) + ecs_entity_t period = EcsPeriod1s; + if (period_str) { + char *period_name = flecs_asprintf("Period%s", period_str); + period = ecs_lookup_child(world, ecs_id(FlecsStats), period_name); + ecs_os_free(period_name); + if (!period) { + flecs_reply_error(reply, "bad request (invalid period string)"); + reply->code = 400; + return false; + } + } -/** Observer used for creating new instances of member metric */ -static void flecs_metrics_on_member_metric(ecs_iter_t *it) { - ecs_world_t *world = it->world; - ecs_member_metric_ctx_t *ctx = it->ctx; - ecs_id_t id = ecs_field_id(it, 1); + if (!ecs_os_strcmp(category, "world")) { + const EcsWorldStats *stats = ecs_get_pair(world, EcsWorld, + EcsWorldStats, period); + flecs_world_stats_to_json(&reply->body, stats); + return true; - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_entity_t m = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); + } else if (!ecs_os_strcmp(category, "pipeline")) { + flecs_pipeline_stats_to_json(world, req, reply, period); + return true; - EcsMetricMemberInstance *src = ecs_emplace( - world, m, EcsMetricMemberInstance); - src->ref = ecs_ref_init_id(world, e, id); - src->ctx = ctx; - ecs_modified(world, m, EcsMetricMemberInstance); - ecs_set(world, m, EcsMetricValue, { 0 }); - ecs_set(world, m, EcsMetricSource, { e }); - ecs_add(world, m, EcsMetricInstance); - ecs_add_pair(world, m, EcsMetric, ctx->metric.kind); + } else { + flecs_reply_error(reply, "bad request (unsupported category)"); + reply->code = 400; + return false; } } +#else +static +bool flecs_rest_get_stats( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) +{ + (void)world; + (void)req; + (void)reply; + return false; +} +#endif -/** Observer used for creating new instances of id metric */ -static void flecs_metrics_on_id_metric(ecs_iter_t *it) { - ecs_world_t *world = it->world; - ecs_id_metric_ctx_t *ctx = it->ctx; - - int32_t i, count = it->count; +static +void flecs_rest_reply_table_append_type( + ecs_world_t *world, + ecs_strbuf_t *reply, + const ecs_table_t *table) +{ + ecs_strbuf_list_push(reply, "[", ","); + int32_t i, count = table->type.count; + ecs_id_t *ids = table->type.array; for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_entity_t m = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); - - EcsMetricIdInstance *src = ecs_emplace(world, m, EcsMetricIdInstance); - src->r = ecs_record_find(world, e); - src->ctx = ctx; - ecs_modified(world, m, EcsMetricIdInstance); - ecs_set(world, m, EcsMetricValue, { 0 }); - ecs_set(world, m, EcsMetricSource, { e }); - ecs_add(world, m, EcsMetricInstance); - ecs_add_pair(world, m, EcsMetric, ctx->metric.kind); + ecs_strbuf_list_next(reply); + ecs_strbuf_appendch(reply, '"'); + ecs_id_str_buf(world, ids[i], reply); + ecs_strbuf_appendch(reply, '"'); } + ecs_strbuf_list_pop(reply, "]"); } -/** Observer used for creating new instances of oneof metric */ -static void flecs_metrics_on_oneof_metric(ecs_iter_t *it) { - if (it->event == EcsOnRemove) { - return; - } +static +void flecs_rest_reply_table_append_memory( + ecs_strbuf_t *reply, + const ecs_table_t *table) +{ + int32_t used = 0, allocated = 0; + int32_t count = ecs_table_count(table), size = ecs_table_size(table); - ecs_world_t *world = it->world; - ecs_oneof_metric_ctx_t *ctx = it->ctx; + used += count * ECS_SIZEOF(ecs_entity_t); + allocated += size * ECS_SIZEOF(ecs_entity_t); - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_entity_t m = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); + int32_t i, storage_count = table->column_count; + ecs_column_t *columns = table->data.columns; - EcsMetricOneOfInstance *src = ecs_emplace(world, m, EcsMetricOneOfInstance); - src->r = ecs_record_find(world, e); - src->ctx = ctx; - ecs_modified(world, m, EcsMetricOneOfInstance); - ecs_add_pair(world, m, ctx->metric.metric, ecs_id(EcsMetricValue)); - ecs_set(world, m, EcsMetricSource, { e }); - ecs_add(world, m, EcsMetricInstance); - ecs_add_pair(world, m, EcsMetric, ctx->metric.kind); + for (i = 0; i < storage_count; i ++) { + used += count * columns[i].ti->size; + allocated += size * columns[i].ti->size; } + + ecs_strbuf_list_push(reply, "{", ","); + ecs_strbuf_list_appendlit(reply, "\"used\":"); + ecs_strbuf_appendint(reply, used); + ecs_strbuf_list_appendlit(reply, "\"allocated\":"); + ecs_strbuf_appendint(reply, allocated); + ecs_strbuf_list_pop(reply, "}"); } -/** Set doc name of metric instance to name of source entity */ -#ifdef FLECS_DOC -static void SetMetricDocName(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsMetricSource *src = ecs_field(it, EcsMetricSource, 1); +static +void flecs_rest_reply_table_append( + ecs_world_t *world, + ecs_strbuf_t *reply, + const ecs_table_t *table) +{ + ecs_strbuf_list_next(reply); + ecs_strbuf_list_push(reply, "{", ","); + ecs_strbuf_list_appendlit(reply, "\"id\":"); + ecs_strbuf_appendint(reply, (uint32_t)table->id); + ecs_strbuf_list_appendlit(reply, "\"type\":"); + flecs_rest_reply_table_append_type(world, reply, table); + ecs_strbuf_list_appendlit(reply, "\"count\":"); + ecs_strbuf_appendint(reply, ecs_table_count(table)); + ecs_strbuf_list_appendlit(reply, "\"memory\":"); + flecs_rest_reply_table_append_memory(reply, table); + ecs_strbuf_list_pop(reply, "}"); +} - int32_t i, count = it->count; +static +bool flecs_rest_get_tables( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) +{ + (void)req; + + ecs_strbuf_list_push(&reply->body, "[", ","); + ecs_sparse_t *tables = &world->store.tables; + int32_t i, count = flecs_sparse_count(tables); for (i = 0; i < count; i ++) { - ecs_entity_t src_e = src[i].entity; - const char *name = ecs_get_name(world, src_e); - if (name) { - ecs_doc_set_name(world, it->entities[i], name); - } + ecs_table_t *table = flecs_sparse_get_dense_t(tables, ecs_table_t, i); + flecs_rest_reply_table_append(world, &reply->body, table); } -} -#endif + ecs_strbuf_list_pop(&reply->body, "]"); -/** Delete metric instances for entities that are no longer alive */ -static void ClearMetricInstance(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsMetricSource *src = ecs_field(it, EcsMetricSource, 1); + return true; +} - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t src_e = src[i].entity; - if (!ecs_is_alive(world, src_e)) { - ecs_delete(world, it->entities[i]); - } +static +const char* flecs_rest_cmd_kind_to_str( + ecs_cmd_kind_t kind) +{ + switch(kind) { + case EcsCmdClone: return "Clone"; + case EcsCmdBulkNew: return "BulkNew"; + case EcsCmdAdd: return "Add"; + case EcsCmdRemove: return "Remove"; + case EcsCmdSet: return "Set"; + case EcsCmdEmplace: return "Emplace"; + case EcsCmdEnsure: return "Ensure"; + case EcsCmdModified: return "Modified"; + case EcsCmdModifiedNoHook: return "ModifiedNoHook"; + case EcsCmdAddModified: return "AddModified"; + case EcsCmdPath: return "Path"; + case EcsCmdDelete: return "Delete"; + case EcsCmdClear: return "Clear"; + case EcsCmdOnDeleteAction: return "OnDeleteAction"; + case EcsCmdEnable: return "Enable"; + case EcsCmdDisable: return "Disable"; + case EcsCmdEvent: return "Event"; + case EcsCmdSkip: return "Skip"; + default: return "Unknown"; + } +} + +static +bool flecs_rest_cmd_has_id( + const ecs_cmd_t *cmd) +{ + switch(cmd->kind) { + case EcsCmdClear: + case EcsCmdDelete: + case EcsCmdClone: + case EcsCmdDisable: + case EcsCmdPath: + return false; + case EcsCmdBulkNew: + case EcsCmdAdd: + case EcsCmdRemove: + case EcsCmdSet: + case EcsCmdEmplace: + case EcsCmdEnsure: + case EcsCmdModified: + case EcsCmdModifiedNoHook: + case EcsCmdAddModified: + case EcsCmdOnDeleteAction: + case EcsCmdEnable: + case EcsCmdEvent: + case EcsCmdSkip: + default: + return true; } } -/** Update member metric */ -static void UpdateMemberInstance(ecs_iter_t *it, bool counter) { - ecs_world_t *world = it->real_world; - EcsMetricValue *m = ecs_field(it, EcsMetricValue, 1); - EcsMetricMemberInstance *mi = ecs_field(it, EcsMetricMemberInstance, 2); - ecs_ftime_t dt = it->delta_time; +static +void flecs_rest_server_garbage_collect_all( + ecs_rest_ctx_t *impl) +{ + ecs_map_iter_t it = ecs_map_iter(&impl->cmd_captures); - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_member_metric_ctx_t *ctx = mi[i].ctx; - ecs_ref_t *ref = &mi[i].ref; - const void *ptr = ecs_ref_get_id(world, ref, ref->id); - if (ptr) { - ptr = ECS_OFFSET(ptr, ctx->offset); - if (!counter) { - m[i].value = ecs_meta_ptr_to_float(ctx->type_kind, ptr); - } else { - m[i].value += - ecs_meta_ptr_to_float(ctx->type_kind, ptr) * (double)dt; - } - } else { - ecs_delete(it->world, it->entities[i]); + while (ecs_map_next(&it)) { + ecs_rest_cmd_capture_t *capture = ecs_map_ptr(&it); + int32_t i, count = ecs_vec_count(&capture->syncs); + ecs_rest_cmd_sync_capture_t *syncs = ecs_vec_first(&capture->syncs); + for (i = 0; i < count; i ++) { + ecs_rest_cmd_sync_capture_t *sync = &syncs[i]; + ecs_os_free(sync->cmds); } + ecs_vec_fini_t(NULL, &capture->syncs, ecs_rest_cmd_sync_capture_t); + ecs_os_free(capture); } -} - -static void UpdateGaugeMemberInstance(ecs_iter_t *it) { - UpdateMemberInstance(it, false); -} -static void UpdateCounterMemberInstance(ecs_iter_t *it) { - UpdateMemberInstance(it, false); + ecs_map_fini(&impl->cmd_captures); } -static void UpdateCounterIncrementMemberInstance(ecs_iter_t *it) { - UpdateMemberInstance(it, true); -} +static +void flecs_rest_server_garbage_collect( + ecs_world_t *world, + ecs_rest_ctx_t *impl) +{ + const ecs_world_info_t *wi = ecs_get_world_info(world); + ecs_map_iter_t it = ecs_map_iter(&impl->cmd_captures); + ecs_vec_t removed_frames = {0}; -/** Update id metric */ -static void UpdateIdInstance(ecs_iter_t *it, bool counter) { - ecs_world_t *world = it->real_world; - EcsMetricValue *m = ecs_field(it, EcsMetricValue, 1); - EcsMetricIdInstance *mi = ecs_field(it, EcsMetricIdInstance, 2); - ecs_ftime_t dt = it->delta_time; + while (ecs_map_next(&it)) { + int64_t frame = flecs_uto(int64_t, ecs_map_key(&it)); + if ((wi->frame_count_total - frame) > FLECS_REST_COMMAND_RETAIN_COUNT) { + ecs_rest_cmd_capture_t *capture = ecs_map_ptr(&it); + int32_t i, count = ecs_vec_count(&capture->syncs); + ecs_rest_cmd_sync_capture_t *syncs = ecs_vec_first(&capture->syncs); + for (i = 0; i < count; i ++) { + ecs_rest_cmd_sync_capture_t *sync = &syncs[i]; + ecs_os_free(sync->cmds); + } + ecs_vec_fini_t(NULL, &capture->syncs, ecs_rest_cmd_sync_capture_t); + ecs_os_free(capture); - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_table_t *table = mi[i].r->table; - if (!table) { - ecs_delete(it->world, it->entities[i]); - continue; + ecs_vec_init_if_t(&removed_frames, int64_t); + ecs_vec_append_t(NULL, &removed_frames, int64_t)[0] = frame; } + } - ecs_id_metric_ctx_t *ctx = mi[i].ctx; - ecs_id_record_t *idr = ctx->idr; - if (flecs_search_w_idr(world, table, idr->id, NULL, idr) != -1) { - if (!counter) { - m[i].value = 1.0; - } else { - m[i].value += 1.0 * (double)dt; + int32_t i, count = ecs_vec_count(&removed_frames); + if (count) { + int64_t *frames = ecs_vec_first(&removed_frames); + if (count) { + for (i = 0; i < count; i ++) { + ecs_map_remove(&impl->cmd_captures, + flecs_ito(uint64_t, frames[i])); } - } else { - ecs_delete(it->world, it->entities[i]); } + ecs_vec_fini_t(NULL, &removed_frames, int64_t); } } -static void UpdateGaugeIdInstance(ecs_iter_t *it) { - UpdateIdInstance(it, false); -} +static +void flecs_rest_cmd_to_json( + ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_cmd_t *cmd) +{ + ecs_strbuf_list_push(buf, "{", ","); -static void UpdateCounterIdInstance(ecs_iter_t *it) { - UpdateIdInstance(it, true); -} + ecs_strbuf_list_appendlit(buf, "\"kind\":\""); + ecs_strbuf_appendstr(buf, flecs_rest_cmd_kind_to_str(cmd->kind)); + ecs_strbuf_appendlit(buf, "\""); -/** Update oneof metric */ -static void UpdateOneOfInstance(ecs_iter_t *it, bool counter) { - ecs_world_t *world = it->real_world; - ecs_table_t *table = it->table; - void *m = ecs_table_get_column(table, - ecs_table_type_to_column_index(table, it->columns[0] - 1), it->offset); - EcsMetricOneOfInstance *mi = ecs_field(it, EcsMetricOneOfInstance, 2); - ecs_ftime_t dt = it->delta_time; + if (flecs_rest_cmd_has_id(cmd)) { + ecs_strbuf_list_appendlit(buf, "\"id\":\""); + char *idstr = ecs_id_str(world, cmd->id); + ecs_strbuf_appendstr(buf, idstr); + ecs_strbuf_appendlit(buf, "\""); + ecs_os_free(idstr); + } - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_oneof_metric_ctx_t *ctx = mi[i].ctx; - ecs_table_t *mtable = mi[i].r->table; + if (cmd->system) { + ecs_strbuf_list_appendlit(buf, "\"system\":\""); + char *sysstr = ecs_get_path(world, cmd->system); + ecs_strbuf_appendstr(buf, sysstr); + ecs_strbuf_appendlit(buf, "\""); + ecs_os_free(sysstr); + } - double *value = ECS_ELEM(m, ctx->size, i); - if (!counter) { - ecs_os_memset(value, 0, ctx->size); - } + if (cmd->kind == EcsCmdBulkNew) { + /* Todo */ + } else if (cmd->kind == EcsCmdEvent) { + /* Todo */ + } else { + if (cmd->entity) { + ecs_strbuf_list_appendlit(buf, "\"entity\":\""); + char *path = ecs_get_path_w_sep(world, 0, cmd->entity, ".", ""); + ecs_strbuf_appendstr(buf, path); + ecs_strbuf_appendlit(buf, "\""); + ecs_os_free(path); - if (!mtable) { - ecs_delete(it->world, it->entities[i]); - continue; - } + ecs_strbuf_list_appendlit(buf, "\"is_alive\":\""); + if (ecs_is_alive(world, cmd->entity)) { + ecs_strbuf_appendlit(buf, "true"); + } else { + ecs_strbuf_appendlit(buf, "false"); + } + ecs_strbuf_appendlit(buf, "\""); - ecs_id_record_t *idr = ctx->idr; - ecs_id_t id; - if (flecs_search_w_idr(world, mtable, idr->id, &id, idr) == -1) { - ecs_delete(it->world, it->entities[i]); - continue; + ecs_strbuf_list_appendlit(buf, "\"next_for_entity\":"); + ecs_strbuf_appendint(buf, cmd->next_for_entity); } + } - ecs_entity_t tgt = ECS_PAIR_SECOND(id); - uint64_t *offset = ecs_map_get(&ctx->target_offset, tgt); - if (!offset) { - ecs_err("unexpected relationship target for metric"); - continue; - } + ecs_strbuf_list_pop(buf, "}"); +} - value = ECS_OFFSET(value, *offset); +static +void flecs_rest_on_commands( + const ecs_stage_t *stage, + const ecs_vec_t *commands, + void *ctx) +{ + ecs_world_t *world = stage->world; + ecs_rest_cmd_capture_t *capture = ctx; + ecs_assert(capture != NULL, ECS_INTERNAL_ERROR, NULL); + + if (commands) { + ecs_vec_init_if_t(&capture->syncs, ecs_rest_cmd_sync_capture_t); + ecs_rest_cmd_sync_capture_t *sync = ecs_vec_append_t( + NULL, &capture->syncs, ecs_rest_cmd_sync_capture_t); + + int32_t i, count = ecs_vec_count(commands); + ecs_cmd_t *cmds = ecs_vec_first(commands); + sync->buf = ECS_STRBUF_INIT; + ecs_strbuf_list_push(&sync->buf, "{", ","); + ecs_strbuf_list_appendlit(&sync->buf, "\"commands\":"); + ecs_strbuf_list_push(&sync->buf, "[", ","); + for (i = 0; i < count; i ++) { + ecs_strbuf_list_next(&sync->buf); + flecs_rest_cmd_to_json(world, &sync->buf, &cmds[i]); + } + ecs_strbuf_list_pop(&sync->buf, "]"); - if (!counter) { - *value = 1.0; - } else { - *value += 1.0 * (double)dt; - } - } -} + /* Measure how long it takes to process queue */ + sync->start_time = (ecs_time_t){0}; + ecs_time_measure(&sync->start_time); + } else { + /* Finished processing queue, measure duration */ + ecs_rest_cmd_sync_capture_t *sync = ecs_vec_last_t( + &capture->syncs, ecs_rest_cmd_sync_capture_t); + double duration = ecs_time_measure(&sync->start_time); -static void UpdateGaugeOneOfInstance(ecs_iter_t *it) { - UpdateOneOfInstance(it, false); -} + ecs_strbuf_list_appendlit(&sync->buf, "\"duration\":"); + ecs_strbuf_appendflt(&sync->buf, duration, '"'); + ecs_strbuf_list_pop(&sync->buf, "}"); -static void UpdateCounterOneOfInstance(ecs_iter_t *it) { - UpdateOneOfInstance(it, true); + sync->cmds = ecs_strbuf_get(&sync->buf); + } } -static void UpdateCountTargets(ecs_iter_t *it) { - ecs_world_t *world = it->real_world; - EcsMetricCountTargets *m = ecs_field(it, EcsMetricCountTargets, 1); +static +bool flecs_rest_get_commands_capture( + ecs_world_t *world, + ecs_rest_ctx_t *impl, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) +{ + (void)req; + const ecs_world_info_t *wi = ecs_get_world_info(world); + ecs_strbuf_appendstr(&reply->body, "{"); + ecs_strbuf_appendlit(&reply->body, "\"frame\":"); + ecs_strbuf_appendint(&reply->body, wi->frame_count_total); + ecs_strbuf_appendstr(&reply->body, "}"); + + ecs_map_init_if(&impl->cmd_captures, &world->allocator); + ecs_rest_cmd_capture_t *capture = ecs_map_ensure_alloc_t( + &impl->cmd_captures, ecs_rest_cmd_capture_t, + flecs_ito(uint64_t, wi->frame_count_total)); - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_count_targets_metric_ctx_t *ctx = m[i].ctx; - ecs_id_record_t *cur = ctx->idr; - while ((cur = cur->first.next)) { - ecs_id_t id = cur->id; - ecs_entity_t *mi = ecs_map_ensure(&ctx->targets, id); - if (!mi[0]) { - mi[0] = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); - ecs_entity_t tgt = ecs_pair_second(world, cur->id); - const char *name = ecs_get_name(world, tgt); - if (name) { - ecs_set_name(world, mi[0], name); - } + world->on_commands = flecs_rest_on_commands; + world->on_commands_ctx = capture; - EcsMetricSource *source = ecs_get_mut( - world, mi[0], EcsMetricSource); - source->entity = tgt; - } + /* Run garbage collection so that requests don't linger */ + flecs_rest_server_garbage_collect(world, impl); - EcsMetricValue *value = ecs_get_mut(world, mi[0], EcsMetricValue); - value->value += (double)ecs_count_id(world, cur->id) * - (double)it->delta_system_time; - } - } + return true; } -static void UpdateCountIds(ecs_iter_t *it) { - ecs_world_t *world = it->real_world; - EcsMetricCountIds *m = ecs_field(it, EcsMetricCountIds, 1); - EcsMetricValue *v = ecs_field(it, EcsMetricValue, 2); +static +bool flecs_rest_get_commands_request( + ecs_world_t *world, + ecs_rest_ctx_t *impl, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) +{ + (void)world; + char *frame_str = &req->path[15]; + int32_t frame = atoi(frame_str); + + ecs_map_init_if(&impl->cmd_captures, &world->allocator); + const ecs_rest_cmd_capture_t *capture = ecs_map_get_deref( + &impl->cmd_captures, ecs_rest_cmd_capture_t, + flecs_ito(uint64_t, frame)); + + if (!capture) { + ecs_strbuf_appendstr(&reply->body, "{"); + ecs_strbuf_append(&reply->body, + "\"error\": \"no capture for frame %u\"", frame); + ecs_strbuf_appendstr(&reply->body, "}"); + reply->code = 404; + return true; + } + + ecs_strbuf_appendstr(&reply->body, "{"); + ecs_strbuf_list_append(&reply->body, "\"syncs\":"); + ecs_strbuf_list_push(&reply->body, "[", ","); + + int32_t i, count = ecs_vec_count(&capture->syncs); + ecs_rest_cmd_sync_capture_t *syncs = ecs_vec_first(&capture->syncs); - int32_t i, count = it->count; for (i = 0; i < count; i ++) { - v[i].value += (double)ecs_count_id(world, m[i].id) * - (double)it->delta_system_time; + ecs_rest_cmd_sync_capture_t *sync = &syncs[i]; + ecs_strbuf_list_appendstr(&reply->body, sync->cmds); } + + ecs_strbuf_list_pop(&reply->body, "]"); + ecs_strbuf_appendstr(&reply->body, "}"); + + return true; } -/** Initialize member metric */ static -int flecs_member_metric_init( - ecs_world_t *world, - ecs_entity_t metric, - const ecs_metric_desc_t *desc) +bool flecs_rest_reply( + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + void *ctx) { - ecs_entity_t type = 0, member_type = 0, member = 0, id = 0; - uintptr_t offset = 0; + ecs_rest_ctx_t *impl = ctx; + ecs_world_t *world = impl->world; - if (desc->dotmember) { - if (!desc->id) { - char *metric_name = ecs_get_fullpath(world, metric); - ecs_err("missing id for metric '%s' with member '%s", - metric_name, desc->dotmember); - ecs_os_free(metric_name); - goto error; - } + if (req->path == NULL) { + ecs_dbg("rest: bad request (missing path)"); + flecs_reply_error(reply, "bad request (missing path)"); + reply->code = 400; + return false; + } - if (desc->member) { - char *metric_name = ecs_get_fullpath(world, metric); - ecs_err("cannot set both member and dotmember for metric '%s'", - metric_name); - ecs_os_free(metric_name); - goto error; - } + if (req->method == EcsHttpGet) { + /* Entity endpoint */ + if (!ecs_os_strncmp(req->path, "entity/", 7)) { + return flecs_rest_get_entity(world, req, reply); - type = ecs_get_typeid(world, desc->id); + /* Component GET endpoint */ + } else if (!ecs_os_strncmp(req->path, "component/", 10)) { + return flecs_rest_get_component(world, req, reply, &req->path[10]); - ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, NULL); - if (ecs_meta_push(&cur)) { - char *metric_name = ecs_get_fullpath(world, metric); - ecs_err("invalid type for metric '%s'", metric_name); - ecs_os_free(metric_name); - goto error; - } - if (ecs_meta_dotmember(&cur, desc->dotmember)) { - char *metric_name = ecs_get_fullpath(world, metric); - ecs_err("invalid dotmember '%s' for metric '%s'", - desc->dotmember, metric_name); - ecs_os_free(metric_name); - goto error; - } + /* Query endpoint */ + } else if (!ecs_os_strcmp(req->path, "query")) { + return flecs_rest_get_query(world, req, reply); - id = desc->id; - member_type = ecs_meta_get_type(&cur); - offset = (uintptr_t)ecs_meta_get_ptr(&cur); - member = ecs_meta_get_member_id(&cur); - } else { - const EcsMember *m = ecs_get(world, desc->member, EcsMember); - if (!m) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); - ecs_err("entity '%s' provided for metric '%s' is not a member", - member_name, metric_name); - ecs_os_free(member_name); - ecs_os_free(metric_name); - goto error; - } + /* World endpoint */ + } else if (!ecs_os_strcmp(req->path, "world")) { + return flecs_rest_get_world(world, req, reply); - type = ecs_get_parent(world, desc->member); - if (!type) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); - ecs_err("member '%s' provided for metric '%s' is not part of a type", - member_name, metric_name); - ecs_os_free(member_name); - ecs_os_free(metric_name); - goto error; + /* Stats endpoint */ + } else if (!ecs_os_strncmp(req->path, "stats/", 6)) { + return flecs_rest_get_stats(world, req, reply); + + /* Tables endpoint */ + } else if (!ecs_os_strncmp(req->path, "tables", 6)) { + return flecs_rest_get_tables(world, req, reply); + + /* Commands capture endpoint */ + } else if (!ecs_os_strncmp(req->path, "commands/capture", 16)) { + return flecs_rest_get_commands_capture(world, impl, req, reply); + + /* Commands request endpoint (request commands from specific frame) */ + } else if (!ecs_os_strncmp(req->path, "commands/frame/", 15)) { + return flecs_rest_get_commands_request(world, impl, req, reply); } - - id = type; - if (desc->id) { - if (type != ecs_get_typeid(world, desc->id)) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); - char *id_name = ecs_get_fullpath(world, desc->id); - ecs_err("member '%s' for metric '%s' is not of type '%s'", - member_name, metric_name, id_name); - ecs_os_free(id_name); - ecs_os_free(member_name); - ecs_os_free(metric_name); - goto error; - } - id = desc->id; + + } else if (req->method == EcsHttpPut) { + /* Component PUT endpoint */ + if (!ecs_os_strncmp(req->path, "entity/", 7)) { + return flecs_rest_put_entity(world, reply, &req->path[7]); + + /* Component PUT endpoint */ + } else if (!ecs_os_strncmp(req->path, "component/", 10)) { + return flecs_rest_put_component(world, req, reply, &req->path[10]); + + /* Enable endpoint */ + } else if (!ecs_os_strncmp(req->path, "toggle/", 7)) { + return flecs_rest_toggle(world, req, reply, &req->path[7]); + + /* Script endpoint */ + } else if (!ecs_os_strncmp(req->path, "script/", 7)) { + return flecs_rest_script(world, req, reply, &req->path[7]); } + } else if (req->method == EcsHttpDelete) { + /* Entity DELETE endpoint */ + if (!ecs_os_strncmp(req->path, "entity/", 7)) { + return flecs_rest_delete_entity(world, reply, &req->path[7]); - member = desc->member; - member_type = m->type; - offset = flecs_ito(uintptr_t, m->offset); + /* Component DELETE endpoint */ + } else if (!ecs_os_strncmp(req->path, "component/", 10)) { + return flecs_rest_delete_component(world, req, reply, &req->path[10]); + } } - const EcsPrimitive *p = ecs_get(world, member_type, EcsPrimitive); - if (!p) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); - ecs_err("member '%s' provided for metric '%s' must have primitive type", - member_name, metric_name); - ecs_os_free(member_name); - ecs_os_free(metric_name); - goto error; - } + return false; +} - const EcsMetaType *mt = ecs_get(world, type, EcsMetaType); - if (!mt) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); - ecs_err("parent of member '%s' for metric '%s' is not a type", - member_name, metric_name); - ecs_os_free(member_name); - ecs_os_free(metric_name); - goto error; +ecs_http_server_t* ecs_rest_server_init( + ecs_world_t *world, + const ecs_http_server_desc_t *desc) +{ + ecs_rest_ctx_t *srv_ctx = ecs_os_calloc_t(ecs_rest_ctx_t); + ecs_http_server_desc_t private_desc = {0}; + if (desc) { + private_desc = *desc; } + private_desc.callback = flecs_rest_reply; + private_desc.ctx = srv_ctx; - if (mt->kind != EcsStructType) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); - ecs_err("parent of member '%s' for metric '%s' is not a struct", - member_name, metric_name); - ecs_os_free(member_name); - ecs_os_free(metric_name); - goto error; + ecs_http_server_t *srv = ecs_http_server_init(&private_desc); + if (!srv) { + ecs_os_free(srv_ctx); + return NULL; } - ecs_member_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_member_metric_ctx_t); - ctx->metric.metric = metric; - ctx->metric.kind = desc->kind; - ctx->type_kind = p->kind; - ctx->offset = flecs_uto(uint16_t, offset); + srv_ctx->world = world; + srv_ctx->srv = srv; + srv_ctx->rc = 1; + srv_ctx->srv = srv; + return srv; +} - ecs_observer(world, { - .entity = metric, - .events = { EcsOnAdd }, - .filter.terms[0] = { - .id = id, - .src.flags = EcsSelf, - .inout = EcsInOutNone - }, - .callback = flecs_metrics_on_member_metric, - .yield_existing = true, - .ctx = ctx - }); +void ecs_rest_server_fini( + ecs_http_server_t *srv) +{ + ecs_rest_ctx_t *impl = ecs_http_server_ctx(srv); + flecs_rest_server_garbage_collect_all(impl); + ecs_os_free(impl); + ecs_http_server_fini(srv); +} - ecs_set_pair(world, metric, EcsMetricMember, member, { .ctx = ctx }); - ecs_add_pair(world, metric, EcsMetric, desc->kind); - ecs_add_id(world, metric, EcsMetric); +static +void flecs_on_set_rest(ecs_iter_t *it) { + EcsRest *rest = ecs_field(it, EcsRest, 0); - return 0; -error: - return -1; + int i; + for(i = 0; i < it->count; i ++) { + if (!rest[i].port) { + rest[i].port = ECS_REST_DEFAULT_PORT; + } + + ecs_http_server_t *srv = ecs_rest_server_init(it->real_world, + &(ecs_http_server_desc_t){ + .ipaddr = rest[i].ipaddr, + .port = rest[i].port, + .cache_timeout = 0.2 + }); + + if (!srv) { + const char *ipaddr = rest[i].ipaddr ? rest[i].ipaddr : "0.0.0.0"; + ecs_err("failed to create REST server on %s:%u", + ipaddr, rest[i].port); + continue; + } + + rest[i].impl = ecs_http_server_ctx(srv); + + ecs_http_server_start(srv); + } } -/** Update id metric */ static -int flecs_id_metric_init( - ecs_world_t *world, - ecs_entity_t metric, - const ecs_metric_desc_t *desc) -{ - ecs_id_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_id_metric_ctx_t); - ctx->metric.metric = metric; - ctx->metric.kind = desc->kind; - ctx->idr = flecs_id_record_ensure(world, desc->id); - ecs_check(ctx->idr != NULL, ECS_INVALID_PARAMETER, NULL); +void DequeueRest(ecs_iter_t *it) { + EcsRest *rest = ecs_field(it, EcsRest, 0); - ecs_observer(world, { - .entity = metric, - .events = { EcsOnAdd }, - .filter.terms[0] = { - .id = desc->id, - .src.flags = EcsSelf, - .inout = EcsInOutNone - }, - .callback = flecs_metrics_on_id_metric, - .yield_existing = true, - .ctx = ctx - }); + if (it->delta_system_time > (ecs_ftime_t)1.0) { + ecs_warn( + "detected large progress interval (%.2fs), REST request may timeout", + (double)it->delta_system_time); + } - ecs_set(world, metric, EcsMetricId, { .ctx = ctx }); - ecs_add_pair(world, metric, EcsMetric, desc->kind); - ecs_add_id(world, metric, EcsMetric); + const ecs_world_info_t *wi = ecs_get_world_info(it->world); - return 0; -error: - return -1; + int32_t i; + for(i = 0; i < it->count; i ++) { + ecs_rest_ctx_t *ctx = rest[i].impl; + if (ctx) { + float elapsed = (float)(wi->world_time_total_raw - ctx->last_time); + ecs_http_server_dequeue(ctx->srv, (ecs_ftime_t)elapsed); + flecs_rest_server_garbage_collect(it->world, ctx); + ctx->last_time = wi->world_time_total_raw; + } + } } -/** Update oneof metric */ static -int flecs_oneof_metric_init( - ecs_world_t *world, - ecs_entity_t metric, - ecs_entity_t scope, - const ecs_metric_desc_t *desc) -{ - ecs_oneof_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_oneof_metric_ctx_t); - ctx->metric.metric = metric; - ctx->metric.kind = desc->kind; - ctx->idr = flecs_id_record_ensure(world, desc->id); - ecs_check(ctx->idr != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_map_init(&ctx->target_offset, NULL); +void DisableRest(ecs_iter_t *it) { + ecs_world_t *world = it->world; - /* Add member for each child of oneof to metric, so it can be used as metric - * instance type that holds values for all targets */ - ecs_iter_t it = ecs_children(world, scope); - uint64_t offset = 0; - while (ecs_children_next(&it)) { - int32_t i, count = it.count; - for (i = 0; i < count; i ++) { - ecs_entity_t tgt = it.entities[i]; - const char *name = ecs_get_name(world, tgt); - if (!name) { - /* Member must have name */ - continue; - } + ecs_iter_t rit = ecs_each_id(world, ecs_id(EcsRest)); - char *to_snake_case = flecs_to_snake_case(name); + if (it->event == EcsOnAdd) { + /* REST module was disabled */ + while (ecs_each_next(&rit)) { + EcsRest *rest = ecs_field(&rit, EcsRest, 0); + int i; + for (i = 0; i < rit.count; i ++) { + ecs_rest_ctx_t *ctx = rest[i].impl; + ecs_http_server_stop(ctx->srv); + } + } + } else if (it->event == EcsOnRemove) { + /* REST module was enabled */ + while (ecs_each_next(&rit)) { + EcsRest *rest = ecs_field(&rit, EcsRest, 0); + int i; + for (i = 0; i < rit.count; i ++) { + ecs_rest_ctx_t *ctx = rest[i].impl; + ecs_http_server_start(ctx->srv); + } + } + } +} - ecs_entity_t mbr = ecs_entity(world, { - .name = to_snake_case, - .add = { ecs_childof(metric) } - }); +void FlecsRestImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsRest); - ecs_os_free(to_snake_case); + ECS_IMPORT(world, FlecsPipeline); +#ifdef FLECS_SCRIPT + ECS_IMPORT(world, FlecsScript); +#endif +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsRest), + "Module that implements Flecs REST API"); +#endif - ecs_set(world, mbr, EcsMember, { - .type = ecs_id(ecs_f64_t), - .unit = EcsSeconds - }); + ecs_set_name_prefix(world, "Ecs"); - /* Truncate upper 32 bits of target so we can lookup the offset - * with the id we get from the pair */ - ecs_map_ensure(&ctx->target_offset, (uint32_t)tgt)[0] = offset; + flecs_bootstrap_component(world, EcsRest); - offset += sizeof(double); - } - } + ecs_set_hooks(world, EcsRest, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsRest), + .copy = ecs_copy(EcsRest), + .dtor = ecs_dtor(EcsRest), + .on_set = flecs_on_set_rest + }); - ctx->size = flecs_uto(ecs_size_t, offset); + ecs_system(world, { + .entity = ecs_entity(world, {.name = "DequeueRest", .add = ecs_ids( ecs_dependson(EcsPostFrame))}), + .query.terms = { + { .id = ecs_id(EcsRest) }, + }, + .callback = DequeueRest, + .immediate = true + }); ecs_observer(world, { - .entity = metric, - .events = { EcsMonitor }, - .filter.terms[0] = { - .id = desc->id, - .src.flags = EcsSelf, - .inout = EcsInOutNone + .query = { + .terms = {{ .id = EcsDisabled, .src.id = ecs_id(FlecsRest) }} }, - .callback = flecs_metrics_on_oneof_metric, - .yield_existing = true, - .ctx = ctx + .events = {EcsOnAdd, EcsOnRemove}, + .callback = DisableRest }); - ecs_set(world, metric, EcsMetricOneOf, { .ctx = ctx }); - ecs_add_pair(world, metric, EcsMetric, desc->kind); - ecs_add_id(world, metric, EcsMetric); + ecs_set_name_prefix(world, "EcsRest"); + ECS_TAG_DEFINE(world, EcsRestPlecs); - return 0; -error: - return -1; + /* Enable frame time measurements so we're guaranteed to have a delta time + * value to pass into the HTTP server. */ + if (ecs_os_has_time()) { + ecs_measure_frame_time(world, true); + } } -static -int flecs_count_id_targets_metric_init( - ecs_world_t *world, - ecs_entity_t metric, - const ecs_metric_desc_t *desc) -{ - ecs_count_targets_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_count_targets_metric_ctx_t); - ctx->metric.metric = metric; - ctx->metric.kind = desc->kind; - ctx->idr = flecs_id_record_ensure(world, desc->id); - ecs_check(ctx->idr != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_map_init(&ctx->targets, NULL); +#endif - ecs_set(world, metric, EcsMetricCountTargets, { .ctx = ctx }); - ecs_add_pair(world, metric, EcsMetric, desc->kind); - ecs_add_id(world, metric, EcsMetric); +/** + * @file addons/timer.c + * @brief Timer addon. + */ - return 0; -error: - return -1; -} +/** + * @file addons/system/system.h + * @brief Internal types and functions for system addon. + */ -static -int flecs_count_ids_metric_init( +#ifndef FLECS_SYSTEM_PRIVATE_H +#define FLECS_SYSTEM_PRIVATE_H + +#ifdef FLECS_SYSTEM + + +#define ecs_system_t_magic (0x65637383) +#define ecs_system_t_tag EcsSystem + +extern ecs_mixins_t ecs_system_t_mixins; + +/* Invoked when system becomes active / inactive */ +void ecs_system_activate( ecs_world_t *world, - ecs_entity_t metric, - const ecs_metric_desc_t *desc) -{ - ecs_set(world, metric, EcsMetricCountIds, { .id = desc->id }); - ecs_set(world, metric, EcsMetricValue, { .value = 0 }); - return 0; -} + ecs_entity_t system, + bool activate, + const ecs_system_t *system_data); -ecs_entity_t ecs_metric_init( +/* Internal function to run a system */ +ecs_entity_t flecs_run_intern( ecs_world_t *world, - const ecs_metric_desc_t *desc) -{ - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - ecs_poly_assert(world, ecs_world_t); + ecs_stage_t *stage, + ecs_entity_t system, + ecs_system_t *system_data, + int32_t stage_current, + int32_t stage_count, + ecs_ftime_t delta_time, + void *param); - ecs_entity_t result = desc->entity; - if (!result) { - result = ecs_new_id(world); - } +#endif - ecs_entity_t kind = desc->kind; - if (!kind) { - ecs_err("missing metric kind"); - goto error; - } +#endif - if (kind != EcsGauge && - kind != EcsCounter && - kind != EcsCounterId && - kind != EcsCounterIncrement) - { - ecs_err("invalid metric kind %s", ecs_get_fullpath(world, kind)); - goto error; - } - if (kind == EcsCounterIncrement && !desc->member && !desc->dotmember) { - ecs_err("CounterIncrement can only be used in combination with member"); - goto error; - } +#ifdef FLECS_TIMER - if (kind == EcsCounterId && (desc->member || desc->dotmember)) { - ecs_err("CounterId cannot be used in combination with member"); - goto error; +static +void AddTickSource(ecs_iter_t *it) { + int32_t i; + for (i = 0; i < it->count; i ++) { + ecs_set(it->world, it->entities[i], EcsTickSource, {0}); } +} - if (desc->brief) { -#ifdef FLECS_DOC - ecs_doc_set_brief(world, result, desc->brief); -#else - ecs_warn("FLECS_DOC is not enabled, ignoring metrics brief"); -#endif - } +static +void ProgressTimers(ecs_iter_t *it) { + EcsTimer *timer = ecs_field(it, EcsTimer, 0); + EcsTickSource *tick_source = ecs_field(it, EcsTickSource, 1); - if (desc->member || desc->dotmember) { - if (flecs_member_metric_init(world, result, desc)) { - goto error; + ecs_assert(timer != NULL, ECS_INTERNAL_ERROR, NULL); + + int i; + for (i = 0; i < it->count; i ++) { + tick_source[i].tick = false; + + if (!timer[i].active) { + continue; } - } else if (desc->id) { - if (desc->targets) { - if (!ecs_id_is_pair(desc->id)) { - ecs_err("cannot specify targets for id that is not a pair"); - goto error; - } - if (ECS_PAIR_FIRST(desc->id) == EcsWildcard) { - ecs_err("first element of pair cannot be wildcard with " - " targets enabled"); - goto error; - } - if (ECS_PAIR_SECOND(desc->id) != EcsWildcard) { - ecs_err("second element of pair must be wildcard with " - " targets enabled"); - goto error; + + const ecs_world_info_t *info = ecs_get_world_info(it->world); + ecs_ftime_t time_elapsed = timer[i].time + info->delta_time_raw; + ecs_ftime_t timeout = timer[i].timeout; + + if (time_elapsed >= timeout) { + ecs_ftime_t t = time_elapsed - timeout; + if (t > timeout) { + t = 0; } - if (kind == EcsCounterId) { - if (flecs_count_id_targets_metric_init(world, result, desc)) { - goto error; - } - } else { - ecs_entity_t first = ecs_pair_first(world, desc->id); - ecs_entity_t scope = flecs_get_oneof(world, first); - if (!scope) { - ecs_err("first element of pair must have OneOf with " - " targets enabled"); - goto error; - } + timer[i].time = t; /* Initialize with remainder */ + tick_source[i].tick = true; + tick_source[i].time_elapsed = time_elapsed - timer[i].overshoot; + timer[i].overshoot = t; - if (flecs_oneof_metric_init(world, result, scope, desc)) { - goto error; - } + if (timer[i].single_shot) { + timer[i].active = false; } } else { - if (kind == EcsCounterId) { - if (flecs_count_ids_metric_init(world, result, desc)) { - goto error; - } - } else { - if (flecs_id_metric_init(world, result, desc)) { - goto error; - } - } - } - } else { - ecs_err("missing source specified for metric"); - goto error; - } - - return result; -error: - if (result && result != desc->entity) { - ecs_delete(world, result); + timer[i].time = time_elapsed; + } } - return 0; } -void FlecsMetricsImport(ecs_world_t *world) { - ECS_MODULE_DEFINE(world, FlecsMetrics); +static +void ProgressRateFilters(ecs_iter_t *it) { + EcsRateFilter *filter = ecs_field(it, EcsRateFilter, 0); + EcsTickSource *tick_dst = ecs_field(it, EcsTickSource, 1); - ECS_IMPORT(world, FlecsPipeline); - ECS_IMPORT(world, FlecsMeta); - ECS_IMPORT(world, FlecsUnits); + int i; + for (i = 0; i < it->count; i ++) { + ecs_entity_t src = filter[i].src; + bool inc = false; - ecs_set_name_prefix(world, "Ecs"); - ECS_TAG_DEFINE(world, EcsMetric); - ecs_entity_t old_scope = ecs_set_scope(world, EcsMetric); - ECS_TAG_DEFINE(world, EcsCounter); - ECS_TAG_DEFINE(world, EcsCounterIncrement); - ECS_TAG_DEFINE(world, EcsCounterId); - ECS_TAG_DEFINE(world, EcsGauge); - ecs_set_scope(world, old_scope); + filter[i].time_elapsed += it->delta_time; - ecs_set_name_prefix(world, "EcsMetric"); - ECS_TAG_DEFINE(world, EcsMetricInstance); - ECS_COMPONENT_DEFINE(world, EcsMetricValue); - ECS_COMPONENT_DEFINE(world, EcsMetricSource); - ECS_COMPONENT_DEFINE(world, EcsMetricMemberInstance); - ECS_COMPONENT_DEFINE(world, EcsMetricIdInstance); - ECS_COMPONENT_DEFINE(world, EcsMetricOneOfInstance); - ECS_COMPONENT_DEFINE(world, EcsMetricMember); - ECS_COMPONENT_DEFINE(world, EcsMetricId); - ECS_COMPONENT_DEFINE(world, EcsMetricOneOf); - ECS_COMPONENT_DEFINE(world, EcsMetricCountIds); - ECS_COMPONENT_DEFINE(world, EcsMetricCountTargets); + if (src) { + const EcsTickSource *tick_src = ecs_get( + it->world, src, EcsTickSource); + if (tick_src) { + inc = tick_src->tick; + } else { + inc = true; + } + } else { + inc = true; + } - ecs_add_id(world, ecs_id(EcsMetricMemberInstance), EcsPrivate); - ecs_add_id(world, ecs_id(EcsMetricIdInstance), EcsPrivate); - ecs_add_id(world, ecs_id(EcsMetricOneOfInstance), EcsPrivate); + if (inc) { + filter[i].tick_count ++; + bool triggered = !(filter[i].tick_count % filter[i].rate); + tick_dst[i].tick = triggered; + tick_dst[i].time_elapsed = filter[i].time_elapsed; - ecs_struct(world, { - .entity = ecs_id(EcsMetricValue), - .members = { - { .name = "value", .type = ecs_id(ecs_f64_t) } + if (triggered) { + filter[i].time_elapsed = 0; + } + } else { + tick_dst[i].tick = false; } - }); + } +} - ecs_struct(world, { - .entity = ecs_id(EcsMetricSource), - .members = { - { .name = "entity", .type = ecs_id(ecs_entity_t) } - } - }); +static +void ProgressTickSource(ecs_iter_t *it) { + EcsTickSource *tick_src = ecs_field(it, EcsTickSource, 0); - ecs_set_hooks(world, EcsMetricMember, { - .ctor = ecs_default_ctor, - .dtor = ecs_dtor(EcsMetricMember), - .move = ecs_move(EcsMetricMember) - }); + /* If tick source has no filters, tick unconditionally */ + int i; + for (i = 0; i < it->count; i ++) { + tick_src[i].tick = true; + tick_src[i].time_elapsed = it->delta_time; + } +} - ecs_set_hooks(world, EcsMetricId, { - .ctor = ecs_default_ctor, - .dtor = ecs_dtor(EcsMetricId), - .move = ecs_move(EcsMetricId) - }); +ecs_entity_t ecs_set_timeout( + ecs_world_t *world, + ecs_entity_t timer, + ecs_ftime_t timeout) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_set_hooks(world, EcsMetricOneOf, { - .ctor = ecs_default_ctor, - .dtor = ecs_dtor(EcsMetricOneOf), - .move = ecs_move(EcsMetricOneOf) - }); + if (!timer) { + timer = ecs_entity(world, {0}); + } - ecs_set_hooks(world, EcsMetricCountTargets, { - .ctor = ecs_default_ctor, - .dtor = ecs_dtor(EcsMetricCountTargets), - .move = ecs_move(EcsMetricCountTargets) + ecs_set(world, timer, EcsTimer, { + .timeout = timeout, + .single_shot = true, + .active = true }); - ecs_add_id(world, EcsMetric, EcsOneOf); - -#ifdef FLECS_DOC - ECS_OBSERVER(world, SetMetricDocName, EcsOnSet, EcsMetricSource); -#endif - - ECS_SYSTEM(world, ClearMetricInstance, EcsPreStore, - [in] Source); - - ECS_SYSTEM(world, UpdateGaugeMemberInstance, EcsPreStore, - [out] Value, - [in] MemberInstance, - [none] (Metric, Gauge)); - - ECS_SYSTEM(world, UpdateCounterMemberInstance, EcsPreStore, - [out] Value, - [in] MemberInstance, - [none] (Metric, Counter)); - - ECS_SYSTEM(world, UpdateCounterIncrementMemberInstance, EcsPreStore, - [out] Value, - [in] MemberInstance, - [none] (Metric, CounterIncrement)); - - ECS_SYSTEM(world, UpdateGaugeIdInstance, EcsPreStore, - [out] Value, - [in] IdInstance, - [none] (Metric, Gauge)); - - ECS_SYSTEM(world, UpdateCounterIdInstance, EcsPreStore, - [inout] Value, - [in] IdInstance, - [none] (Metric, Counter)); - - ECS_SYSTEM(world, UpdateGaugeOneOfInstance, EcsPreStore, - [none] (_, Value), - [in] OneOfInstance, - [none] (Metric, Gauge)); - - ECS_SYSTEM(world, UpdateCounterOneOfInstance, EcsPreStore, - [none] (_, Value), - [in] OneOfInstance, - [none] (Metric, Counter)); - - ECS_SYSTEM(world, UpdateCountIds, EcsPreStore, - [inout] CountIds, Value); + ecs_system_t *system_data = flecs_poly_get(world, timer, ecs_system_t); + if (system_data) { + system_data->tick_source = timer; + } - ECS_SYSTEM(world, UpdateCountTargets, EcsPreStore, - [inout] CountTargets); +error: + return timer; } -#endif - -/** - * @file addons/module.c - * @brief Module addon. - */ - - -#ifdef FLECS_MODULE - -#include - -char* ecs_module_path_from_c( - const char *c_name) +ecs_ftime_t ecs_get_timeout( + const ecs_world_t *world, + ecs_entity_t timer) { - ecs_strbuf_t str = ECS_STRBUF_INIT; - const char *ptr; - char ch; - - for (ptr = c_name; (ch = *ptr); ptr++) { - if (isupper(ch)) { - ch = flecs_ito(char, tolower(ch)); - if (ptr != c_name) { - ecs_strbuf_appendstrn(&str, ".", 1); - } - } + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(timer != 0, ECS_INVALID_PARAMETER, NULL); - ecs_strbuf_appendstrn(&str, &ch, 1); + const EcsTimer *value = ecs_get(world, timer, EcsTimer); + if (value) { + return value->timeout; } - - return ecs_strbuf_get(&str); +error: + return 0; } -ecs_entity_t ecs_import( +ecs_entity_t ecs_set_interval( ecs_world_t *world, - ecs_module_action_t module, - const char *module_name) + ecs_entity_t timer, + ecs_ftime_t interval) { - ecs_check(!(world->flags & EcsWorldReadonly), - ECS_INVALID_WHILE_READONLY, NULL); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_entity_t old_scope = ecs_set_scope(world, 0); - const char *old_name_prefix = world->info.name_prefix; + if (!timer) { + timer = ecs_new_w(world, EcsTimer); + } - char *path = ecs_module_path_from_c(module_name); - ecs_entity_t e = ecs_lookup_fullpath(world, path); - ecs_os_free(path); - - if (!e) { - ecs_trace("#[magenta]import#[reset] %s", module_name); - ecs_log_push(); + EcsTimer *t = ecs_ensure(world, timer, EcsTimer); + ecs_check(t != NULL, ECS_INTERNAL_ERROR, NULL); + t->timeout = interval; + t->active = true; + ecs_modified(world, timer, EcsTimer); - /* Load module */ - module(world); + ecs_system_t *system_data = flecs_poly_get(world, timer, ecs_system_t); + if (system_data) { + system_data->tick_source = timer; + } +error: + return timer; +} - /* Lookup module entity (must be registered by module) */ - e = ecs_lookup_fullpath(world, module_name); - ecs_check(e != 0, ECS_MODULE_UNDEFINED, module_name); +ecs_ftime_t ecs_get_interval( + const ecs_world_t *world, + ecs_entity_t timer) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_log_pop(); + if (!timer) { + return 0; } - /* Restore to previous state */ - ecs_set_scope(world, old_scope); - world->info.name_prefix = old_name_prefix; - - return e; + const EcsTimer *value = ecs_get(world, timer, EcsTimer); + if (value) { + return value->timeout; + } error: return 0; } -ecs_entity_t ecs_import_c( +void ecs_start_timer( ecs_world_t *world, - ecs_module_action_t module, - const char *c_name) + ecs_entity_t timer) { - char *name = ecs_module_path_from_c(c_name); - ecs_entity_t e = ecs_import(world, module, name); - ecs_os_free(name); - return e; + EcsTimer *ptr = ecs_ensure(world, timer, EcsTimer); + ecs_check(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ptr->active = true; + ptr->time = 0; +error: + return; } -ecs_entity_t ecs_import_from_library( +void ecs_stop_timer( ecs_world_t *world, - const char *library_name, - const char *module_name) + ecs_entity_t timer) { - ecs_check(library_name != NULL, ECS_INVALID_PARAMETER, NULL); - - char *import_func = ECS_CONST_CAST(char*, module_name); - char *module = ECS_CONST_CAST(char*, module_name); - - if (!ecs_os_has_modules() || !ecs_os_has_dl()) { - ecs_err( - "library loading not supported, set module_to_dl, dlopen, dlclose " - "and dlproc os API callbacks first"); - return 0; - } - - /* If no module name is specified, try default naming convention for loading - * the main module from the library */ - if (!import_func) { - import_func = ecs_os_malloc(ecs_os_strlen(library_name) + ECS_SIZEOF("Import")); - ecs_assert(import_func != NULL, ECS_OUT_OF_MEMORY, NULL); - - const char *ptr; - char ch, *bptr = import_func; - bool capitalize = true; - for (ptr = library_name; (ch = *ptr); ptr ++) { - if (ch == '.') { - capitalize = true; - } else { - if (capitalize) { - *bptr = flecs_ito(char, toupper(ch)); - bptr ++; - capitalize = false; - } else { - *bptr = flecs_ito(char, tolower(ch)); - bptr ++; - } - } - } - - *bptr = '\0'; - - module = ecs_os_strdup(import_func); - ecs_assert(module != NULL, ECS_OUT_OF_MEMORY, NULL); - - ecs_os_strcat(bptr, "Import"); - } - - char *library_filename = ecs_os_module_to_dl(library_name); - if (!library_filename) { - ecs_err("failed to find library file for '%s'", library_name); - if (module != module_name) { - ecs_os_free(module); - } - return 0; - } else { - ecs_trace("found file '%s' for library '%s'", - library_filename, library_name); - } - - ecs_os_dl_t dl = ecs_os_dlopen(library_filename); - if (!dl) { - ecs_err("failed to load library '%s' ('%s')", - library_name, library_filename); - - ecs_os_free(library_filename); - - if (module != module_name) { - ecs_os_free(module); - } - - return 0; - } else { - ecs_trace("library '%s' ('%s') loaded", - library_name, library_filename); - } + EcsTimer *ptr = ecs_ensure(world, timer, EcsTimer); + ecs_check(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ptr->active = false; +error: + return; +} - ecs_module_action_t action = (ecs_module_action_t) - ecs_os_dlproc(dl, import_func); - if (!action) { - ecs_err("failed to load import function %s from library %s", - import_func, library_name); - ecs_os_free(library_filename); - ecs_os_dlclose(dl); - return 0; - } else { - ecs_trace("found import function '%s' in library '%s' for module '%s'", - import_func, library_name, module); - } +void ecs_reset_timer( + ecs_world_t *world, + ecs_entity_t timer) +{ + EcsTimer *ptr = ecs_ensure(world, timer, EcsTimer); + ecs_check(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ptr->time = 0; +error: + return; +} - /* Do not free id, as it will be stored as the component identifier */ - ecs_entity_t result = ecs_import(world, action, module); +ecs_entity_t ecs_set_rate( + ecs_world_t *world, + ecs_entity_t filter, + int32_t rate, + ecs_entity_t source) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - if (import_func != module_name) { - ecs_os_free(import_func); + if (!filter) { + filter = ecs_entity(world, {0}); } - if (module != module_name) { - ecs_os_free(module); - } + ecs_set(world, filter, EcsRateFilter, { + .rate = rate, + .src = source + }); - ecs_os_free(library_filename); + ecs_system_t *system_data = flecs_poly_get(world, filter, ecs_system_t); + if (system_data) { + system_data->tick_source = filter; + } - return result; error: - return 0; + return filter; } -ecs_entity_t ecs_module_init( +void ecs_set_tick_source( ecs_world_t *world, - const char *c_name, - const ecs_component_desc_t *desc) + ecs_entity_t system, + ecs_entity_t tick_source) { - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_poly_assert(world, ecs_world_t); - - ecs_entity_t old_scope = ecs_set_scope(world, 0); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(system != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(tick_source != 0, ECS_INVALID_PARAMETER, NULL); - ecs_entity_t e = desc->entity; - if (!e) { - char *module_path = ecs_module_path_from_c(c_name); - e = ecs_new_from_fullpath(world, module_path); - ecs_set_symbol(world, e, module_path); - ecs_os_free(module_path); - } else if (!ecs_exists(world, e)) { - char *module_path = ecs_module_path_from_c(c_name); - ecs_ensure(world, e); - ecs_add_fullpath(world, e, module_path); - ecs_set_symbol(world, e, module_path); - ecs_os_free(module_path); - } - - ecs_add_id(world, e, EcsModule); + ecs_system_t *system_data = flecs_poly_get(world, system, ecs_system_t); + ecs_check(system_data != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_component_desc_t private_desc = *desc; - private_desc.entity = e; + system_data->tick_source = tick_source; +error: + return; +} - if (desc->type.size) { - ecs_entity_t result = ecs_component_init(world, &private_desc); - ecs_assert(result != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(result == e, ECS_INTERNAL_ERROR, NULL); - (void)result; +static +void RandomizeTimers(ecs_iter_t *it) { + EcsTimer *timer = ecs_field(it, EcsTimer, 0); + int32_t i; + for (i = 0; i < it->count; i ++) { + timer[i].time = + ((ecs_ftime_t)rand() / (ecs_ftime_t)RAND_MAX) * timer[i].timeout; } +} - ecs_set_scope(world, old_scope); - - return e; -error: - return 0; +void ecs_randomize_timers( + ecs_world_t *world) +{ + ecs_observer(world, { + .entity = ecs_entity(world, { .name = "flecs.timer.RandomizeTimers" }), + .query.terms = {{ + .id = ecs_id(EcsTimer) + }}, + .events = {EcsOnSet}, + .yield_existing = true, + .callback = RandomizeTimers + }); } +void FlecsTimerImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsTimer); + ECS_IMPORT(world, FlecsPipeline); +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsTimer), + "Module that implements system timers (used by .interval)"); #endif -/** - * @file addons/monitor.c - * @brief Monitor addon. - */ - + ecs_set_name_prefix(world, "Ecs"); -#ifdef FLECS_MONITOR + flecs_bootstrap_component(world, EcsTimer); + flecs_bootstrap_component(world, EcsRateFilter); -ECS_COMPONENT_DECLARE(FlecsMonitor); -ECS_COMPONENT_DECLARE(EcsWorldStats); -ECS_COMPONENT_DECLARE(EcsWorldSummary); -ECS_COMPONENT_DECLARE(EcsPipelineStats); + ecs_set_hooks(world, EcsTimer, { + .ctor = flecs_default_ctor + }); -ecs_entity_t EcsPeriod1s = 0; -ecs_entity_t EcsPeriod1m = 0; -ecs_entity_t EcsPeriod1h = 0; -ecs_entity_t EcsPeriod1d = 0; -ecs_entity_t EcsPeriod1w = 0; + /* Add EcsTickSource to timers and rate filters */ + ecs_system(world, { + .entity = ecs_entity(world, {.name = "AddTickSource", .add = ecs_ids( ecs_dependson(EcsPreFrame) )}), + .query.terms = { + { .id = ecs_id(EcsTimer), .oper = EcsOr }, + { .id = ecs_id(EcsRateFilter), .oper = EcsAnd }, + { .id = ecs_id(EcsTickSource), .oper = EcsNot, .inout = EcsOut} + }, + .callback = AddTickSource + }); -static int32_t flecs_day_interval_count = 24; -static int32_t flecs_week_interval_count = 168; + /* Timer handling */ + ecs_system(world, { + .entity = ecs_entity(world, {.name = "ProgressTimers", .add = ecs_ids( ecs_dependson(EcsPreFrame))}), + .query.terms = { + { .id = ecs_id(EcsTimer) }, + { .id = ecs_id(EcsTickSource) } + }, + .callback = ProgressTimers + }); -static -ECS_COPY(EcsPipelineStats, dst, src, { - (void)dst; - (void)src; - ecs_abort(ECS_INVALID_OPERATION, "cannot copy pipeline stats component"); -}) + /* Rate filter handling */ + ecs_system(world, { + .entity = ecs_entity(world, {.name = "ProgressRateFilters", .add = ecs_ids( ecs_dependson(EcsPreFrame))}), + .query.terms = { + { .id = ecs_id(EcsRateFilter), .inout = EcsIn }, + { .id = ecs_id(EcsTickSource), .inout = EcsOut } + }, + .callback = ProgressRateFilters + }); -static -ECS_MOVE(EcsPipelineStats, dst, src, { - ecs_os_memcpy_t(dst, src, EcsPipelineStats); - ecs_os_zeromem(src); -}) + /* TickSource without a timer or rate filter just increases each frame */ + ecs_system(world, { + .entity = ecs_entity(world, { .name = "ProgressTickSource", .add = ecs_ids( ecs_dependson(EcsPreFrame))}), + .query.terms = { + { .id = ecs_id(EcsTickSource), .inout = EcsOut }, + { .id = ecs_id(EcsRateFilter), .oper = EcsNot }, + { .id = ecs_id(EcsTimer), .oper = EcsNot } + }, + .callback = ProgressTickSource + }); +} -static -ECS_DTOR(EcsPipelineStats, ptr, { - ecs_pipeline_stats_fini(&ptr->stats); -}) +#endif -static -void UpdateWorldSummary(ecs_iter_t *it) { - EcsWorldSummary *summary = ecs_field(it, EcsWorldSummary, 1); +/** + * @file addons/units.c + * @brief Units addon. + */ - const ecs_world_info_t *info = ecs_get_world_info(it->world); - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - summary[i].target_fps = (double)info->target_fps; +#ifdef FLECS_UNITS - summary[i].frame_time_last = (double)info->frame_time_total - summary[i].frame_time_total; - summary[i].system_time_last = (double)info->system_time_total - summary[i].system_time_total; - summary[i].merge_time_last = (double)info->merge_time_total - summary[i].merge_time_total; +void FlecsUnitsImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsUnits); + ECS_IMPORT(world, FlecsMeta); - summary[i].frame_time_total = (double)info->frame_time_total; - summary[i].system_time_total = (double)info->system_time_total; - summary[i].merge_time_total = (double)info->merge_time_total; - } -} +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsUnits), + "Module with (amongst others) SI units for annotating component members"); +#endif -static -void MonitorStats(ecs_iter_t *it) { - ecs_world_t *world = it->real_world; + ecs_set_name_prefix(world, "Ecs"); - EcsStatsHeader *hdr = ecs_field_w_size(it, 0, 1); - ecs_id_t kind = ecs_pair_first(it->world, ecs_field_id(it, 1)); - void *stats = ECS_OFFSET_T(hdr, EcsStatsHeader); + EcsUnitPrefixes = ecs_entity(world, { + .name = "prefixes", + .add = ecs_ids( EcsModule ) + }); - ecs_ftime_t elapsed = hdr->elapsed; - hdr->elapsed += it->delta_time; + /* Initialize unit prefixes */ - int32_t t_last = (int32_t)(elapsed * 60); - int32_t t_next = (int32_t)(hdr->elapsed * 60); - int32_t i, dif = t_last - t_next; + ecs_entity_t prev_scope = ecs_set_scope(world, EcsUnitPrefixes); - ecs_world_stats_t last_world = {0}; - ecs_pipeline_stats_t last_pipeline = {0}; - void *last = NULL; - - if (!dif) { - /* Copy last value so we can pass it to reduce_last */ - if (kind == ecs_id(EcsWorldStats)) { - last = &last_world; - ecs_world_stats_copy_last(&last_world, stats); - } else if (kind == ecs_id(EcsPipelineStats)) { - last = &last_pipeline; - ecs_pipeline_stats_copy_last(&last_pipeline, stats); - } - } - - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_get(world, stats); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_get(world, ecs_get_pipeline(world), stats); - } - - if (!dif) { - /* Still in same interval, combine with last measurement */ - hdr->reduce_count ++; - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_reduce_last(stats, last, hdr->reduce_count); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_reduce_last(stats, last, hdr->reduce_count); - } - } else if (dif > 1) { - /* More than 16ms has passed, backfill */ - for (i = 1; i < dif; i ++) { - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_repeat_last(stats); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_world_stats_repeat_last(stats); - } - } - hdr->reduce_count = 0; - } - - if (last && kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_fini(last); - } -} - -static -void ReduceStats(ecs_iter_t *it) { - void *dst = ecs_field_w_size(it, 0, 1); - void *src = ecs_field_w_size(it, 0, 2); - - ecs_id_t kind = ecs_pair_first(it->world, ecs_field_id(it, 1)); - - dst = ECS_OFFSET_T(dst, EcsStatsHeader); - src = ECS_OFFSET_T(src, EcsStatsHeader); - - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_reduce(dst, src); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_reduce(dst, src); - } -} - -static -void AggregateStats(ecs_iter_t *it) { - int32_t interval = *(int32_t*)it->ctx; - - EcsStatsHeader *dst_hdr = ecs_field_w_size(it, 0, 1); - EcsStatsHeader *src_hdr = ecs_field_w_size(it, 0, 2); - - void *dst = ECS_OFFSET_T(dst_hdr, EcsStatsHeader); - void *src = ECS_OFFSET_T(src_hdr, EcsStatsHeader); - - ecs_id_t kind = ecs_pair_first(it->world, ecs_field_id(it, 1)); - - ecs_world_stats_t last_world = {0}; - ecs_pipeline_stats_t last_pipeline = {0}; - void *last = NULL; - - if (dst_hdr->reduce_count != 0) { - /* Copy last value so we can pass it to reduce_last */ - if (kind == ecs_id(EcsWorldStats)) { - last_world.t = 0; - ecs_world_stats_copy_last(&last_world, dst); - last = &last_world; - } else if (kind == ecs_id(EcsPipelineStats)) { - last_pipeline.t = 0; - ecs_pipeline_stats_copy_last(&last_pipeline, dst); - last = &last_pipeline; - } - } - - /* Reduce from minutes to the current day */ - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_reduce(dst, src); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_reduce(dst, src); - } - - if (dst_hdr->reduce_count != 0) { - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_reduce_last(dst, last, dst_hdr->reduce_count); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_reduce_last(dst, last, dst_hdr->reduce_count); - } - } - - /* A day has 60 24 minute intervals */ - dst_hdr->reduce_count ++; - if (dst_hdr->reduce_count >= interval) { - dst_hdr->reduce_count = 0; - } - - if (last && kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_fini(last); - } -} - -static -void flecs_stats_monitor_import( - ecs_world_t *world, - ecs_id_t kind, - size_t size) -{ - ecs_entity_t prev = ecs_set_scope(world, kind); - - // Called each frame, collects 60 measurements per second - ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1s", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1s), - .src.id = EcsWorld - }}, - .callback = MonitorStats + EcsYocto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Yocto" }), + .symbol = "y", + .translation = { .factor = 10, .power = -24 } }); - - // Called each second, reduces into 60 measurements per minute - ecs_entity_t mw1m = ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1m", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1m), - .src.id = EcsWorld - }, { - .id = ecs_pair(kind, EcsPeriod1s), - .src.id = EcsWorld - }}, - .callback = ReduceStats, - .interval = 1.0 + EcsZepto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Zepto" }), + .symbol = "z", + .translation = { .factor = 10, .power = -21 } }); - - // Called each minute, reduces into 60 measurements per hour - ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1h", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1h), - .src.id = EcsWorld - }, { - .id = ecs_pair(kind, EcsPeriod1m), - .src.id = EcsWorld - }}, - .callback = ReduceStats, - .rate = 60, - .tick_source = mw1m + EcsAtto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Atto" }), + .symbol = "a", + .translation = { .factor = 10, .power = -18 } }); - - // Called each minute, reduces into 60 measurements per day - ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1d", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1d), - .src.id = EcsWorld - }, { - .id = ecs_pair(kind, EcsPeriod1m), - .src.id = EcsWorld - }}, - .callback = AggregateStats, - .rate = 60, - .tick_source = mw1m, - .ctx = &flecs_day_interval_count + EcsFemto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Femto" }), + .symbol = "a", + .translation = { .factor = 10, .power = -15 } }); - - // Called each hour, reduces into 60 measurements per week - ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1w", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1w), - .src.id = EcsWorld - }, { - .id = ecs_pair(kind, EcsPeriod1h), - .src.id = EcsWorld - }}, - .callback = AggregateStats, - .rate = 60, - .tick_source = mw1m, - .ctx = &flecs_week_interval_count + EcsPico = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Pico" }), + .symbol = "p", + .translation = { .factor = 10, .power = -12 } + }); + EcsNano = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Nano" }), + .symbol = "n", + .translation = { .factor = 10, .power = -9 } + }); + EcsMicro = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Micro" }), + .symbol = "μ", + .translation = { .factor = 10, .power = -6 } + }); + EcsMilli = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Milli" }), + .symbol = "m", + .translation = { .factor = 10, .power = -3 } + }); + EcsCenti = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Centi" }), + .symbol = "c", + .translation = { .factor = 10, .power = -2 } + }); + EcsDeci = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Deci" }), + .symbol = "d", + .translation = { .factor = 10, .power = -1 } + }); + EcsDeca = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Deca" }), + .symbol = "da", + .translation = { .factor = 10, .power = 1 } + }); + EcsHecto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Hecto" }), + .symbol = "h", + .translation = { .factor = 10, .power = 2 } + }); + EcsKilo = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Kilo" }), + .symbol = "k", + .translation = { .factor = 10, .power = 3 } + }); + EcsMega = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Mega" }), + .symbol = "M", + .translation = { .factor = 10, .power = 6 } + }); + EcsGiga = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Giga" }), + .symbol = "G", + .translation = { .factor = 10, .power = 9 } + }); + EcsTera = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Tera" }), + .symbol = "T", + .translation = { .factor = 10, .power = 12 } + }); + EcsPeta = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Peta" }), + .symbol = "P", + .translation = { .factor = 10, .power = 15 } + }); + EcsExa = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Exa" }), + .symbol = "E", + .translation = { .factor = 10, .power = 18 } + }); + EcsZetta = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Zetta" }), + .symbol = "Z", + .translation = { .factor = 10, .power = 21 } + }); + EcsYotta = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Yotta" }), + .symbol = "Y", + .translation = { .factor = 10, .power = 24 } }); - ecs_set_scope(world, prev); + EcsKibi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Kibi" }), + .symbol = "Ki", + .translation = { .factor = 1024, .power = 1 } + }); + EcsMebi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Mebi" }), + .symbol = "Mi", + .translation = { .factor = 1024, .power = 2 } + }); + EcsGibi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Gibi" }), + .symbol = "Gi", + .translation = { .factor = 1024, .power = 3 } + }); + EcsTebi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Tebi" }), + .symbol = "Ti", + .translation = { .factor = 1024, .power = 4 } + }); + EcsPebi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Pebi" }), + .symbol = "Pi", + .translation = { .factor = 1024, .power = 5 } + }); + EcsExbi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Exbi" }), + .symbol = "Ei", + .translation = { .factor = 1024, .power = 6 } + }); + EcsZebi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Zebi" }), + .symbol = "Zi", + .translation = { .factor = 1024, .power = 7 } + }); + EcsYobi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ + .entity = ecs_entity(world, { .name = "Yobi" }), + .symbol = "Yi", + .translation = { .factor = 1024, .power = 8 } + }); - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1s), size, NULL); - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1m), size, NULL); - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1h), size, NULL); - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1d), size, NULL); - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1w), size, NULL); -} + ecs_set_scope(world, prev_scope); -static -void flecs_world_monitor_import( - ecs_world_t *world) -{ - ECS_COMPONENT_DEFINE(world, EcsWorldStats); + /* Duration units */ - flecs_stats_monitor_import(world, ecs_id(EcsWorldStats), - sizeof(EcsWorldStats)); -} + EcsDuration = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Duration" }); + prev_scope = ecs_set_scope(world, EcsDuration); -static -void flecs_pipeline_monitor_import( - ecs_world_t *world) -{ - ECS_COMPONENT_DEFINE(world, EcsPipelineStats); + EcsSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Seconds" }), + .quantity = EcsDuration, + .symbol = "s" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsSeconds, + .kind = EcsF32 + }); + EcsPicoSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "PicoSeconds" }), + .quantity = EcsDuration, + .base = EcsSeconds, + .prefix = EcsPico }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsPicoSeconds, + .kind = EcsF32 + }); - ecs_set_hooks(world, EcsPipelineStats, { - .ctor = ecs_default_ctor, - .copy = ecs_copy(EcsPipelineStats), - .move = ecs_move(EcsPipelineStats), - .dtor = ecs_dtor(EcsPipelineStats) - }); - flecs_stats_monitor_import(world, ecs_id(EcsPipelineStats), - sizeof(EcsPipelineStats)); -} + EcsNanoSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "NanoSeconds" }), + .quantity = EcsDuration, + .base = EcsSeconds, + .prefix = EcsNano }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsNanoSeconds, + .kind = EcsF32 + }); -void FlecsMonitorImport( - ecs_world_t *world) -{ - ECS_MODULE_DEFINE(world, FlecsMonitor); - ECS_IMPORT(world, FlecsPipeline); - ECS_IMPORT(world, FlecsTimer); -#ifdef FLECS_META - ECS_IMPORT(world, FlecsMeta); -#endif -#ifdef FLECS_UNITS - ECS_IMPORT(world, FlecsUnits); -#endif + EcsMicroSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MicroSeconds" }), + .quantity = EcsDuration, + .base = EcsSeconds, + .prefix = EcsMicro }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMicroSeconds, + .kind = EcsF32 + }); - ecs_set_name_prefix(world, "Ecs"); + EcsMilliSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MilliSeconds" }), + .quantity = EcsDuration, + .base = EcsSeconds, + .prefix = EcsMilli }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMilliSeconds, + .kind = EcsF32 + }); - EcsPeriod1s = ecs_new_entity(world, "EcsPeriod1s"); - EcsPeriod1m = ecs_new_entity(world, "EcsPeriod1m"); - EcsPeriod1h = ecs_new_entity(world, "EcsPeriod1h"); - EcsPeriod1d = ecs_new_entity(world, "EcsPeriod1d"); - EcsPeriod1w = ecs_new_entity(world, "EcsPeriod1w"); + EcsMinutes = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Minutes" }), + .quantity = EcsDuration, + .base = EcsSeconds, + .symbol = "min", + .translation = { .factor = 60, .power = 1 } }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMinutes, + .kind = EcsU32 + }); - ECS_COMPONENT_DEFINE(world, EcsWorldSummary); + EcsHours = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Hours" }), + .quantity = EcsDuration, + .base = EcsMinutes, + .symbol = "h", + .translation = { .factor = 60, .power = 1 } }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsHours, + .kind = EcsU32 + }); -#if defined(FLECS_META) && defined(FLECS_UNITS) - ecs_struct(world, { - .entity = ecs_id(EcsWorldSummary), - .members = { - { .name = "target_fps", .type = ecs_id(ecs_f64_t), .unit = EcsHertz }, - { .name = "frame_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "system_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "merge_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "frame_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "system_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "merge_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds } - } - }); -#endif + EcsDays = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Days" }), + .quantity = EcsDuration, + .base = EcsHours, + .symbol = "d", + .translation = { .factor = 24, .power = 1 } }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsDays, + .kind = EcsU32 + }); + ecs_set_scope(world, prev_scope); - ecs_system(world, { - .entity = ecs_entity(world, { - .name = "UpdateWorldSummary", - .add = {ecs_dependson(EcsPreFrame)} - }), - .query.filter.terms[0] = { .id = ecs_id(EcsWorldSummary) }, - .callback = UpdateWorldSummary - }); + /* Time units */ - ECS_SYSTEM(world, UpdateWorldSummary, EcsPreFrame, WorldSummary); - ecs_set(world, EcsWorld, EcsWorldSummary, {0}); + EcsTime = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Time" }); + prev_scope = ecs_set_scope(world, EcsTime); - flecs_world_monitor_import(world); - flecs_pipeline_monitor_import(world); - - if (ecs_os_has_time()) { - ecs_measure_frame_time(world, true); - ecs_measure_system_time(world, true); - } -} + EcsDate = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Date" }), + .quantity = EcsTime }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsDate, + .kind = EcsU32 + }); + ecs_set_scope(world, prev_scope); -#endif + /* Mass units */ -/** - * @file addons/parser.c - * @brief Parser addon. - */ + EcsMass = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Mass" }); + prev_scope = ecs_set_scope(world, EcsMass); + EcsGrams = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Grams" }), + .quantity = EcsMass, + .symbol = "g" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsGrams, + .kind = EcsF32 + }); + EcsKiloGrams = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KiloGrams" }), + .quantity = EcsMass, + .prefix = EcsKilo, + .base = EcsGrams }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKiloGrams, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); + /* Electric current units */ -#ifdef FLECS_PARSER + EcsElectricCurrent = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "ElectricCurrent" }); + prev_scope = ecs_set_scope(world, EcsElectricCurrent); + EcsAmpere = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Ampere" }), + .quantity = EcsElectricCurrent, + .symbol = "A" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsAmpere, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); -#include + /* Amount of substance units */ -#define TOK_COLON ':' -#define TOK_AND ',' -#define TOK_OR "||" -#define TOK_NOT '!' -#define TOK_OPTIONAL '?' -#define TOK_BITWISE_OR '|' -#define TOK_BRACKET_OPEN '[' -#define TOK_BRACKET_CLOSE ']' -#define TOK_SCOPE_OPEN '{' -#define TOK_SCOPE_CLOSE '}' -#define TOK_VARIABLE '$' -#define TOK_PAREN_OPEN '(' -#define TOK_PAREN_CLOSE ')' -#define TOK_EQ "==" -#define TOK_NEQ "!=" -#define TOK_MATCH "~=" -#define TOK_EXPR_STRING '"' + EcsAmount = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Amount" }); + prev_scope = ecs_set_scope(world, EcsAmount); + EcsMole = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Mole" }), + .quantity = EcsAmount, + .symbol = "mol" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMole, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); -#define TOK_SELF "self" -#define TOK_UP "up" -#define TOK_DOWN "down" -#define TOK_CASCADE "cascade" -#define TOK_PARENT "parent" -#define TOK_DESC "desc" + /* Luminous intensity units */ -#define TOK_OVERRIDE "OVERRIDE" -#define TOK_ROLE_AND "AND" -#define TOK_ROLE_OR "OR" -#define TOK_ROLE_NOT "NOT" -#define TOK_ROLE_TOGGLE "TOGGLE" + EcsLuminousIntensity = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "LuminousIntensity" }); + prev_scope = ecs_set_scope(world, EcsLuminousIntensity); + EcsCandela = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Candela" }), + .quantity = EcsLuminousIntensity, + .symbol = "cd" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsCandela, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); -#define TOK_IN "in" -#define TOK_OUT "out" -#define TOK_INOUT "inout" -#define TOK_INOUT_NONE "none" + /* Force units */ -static -const ecs_id_t ECS_OR = (1ull << 59); + EcsForce = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Force" }); + prev_scope = ecs_set_scope(world, EcsForce); + EcsNewton = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Newton" }), + .quantity = EcsForce, + .symbol = "N" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsNewton, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); -static -const ecs_id_t ECS_NOT = (1ull << 58); + /* Length units */ -#define ECS_MAX_TOKEN_SIZE (256) + EcsLength = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Length" }); + prev_scope = ecs_set_scope(world, EcsLength); + EcsMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Meters" }), + .quantity = EcsLength, + .symbol = "m" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMeters, + .kind = EcsF32 + }); -typedef char ecs_token_t[ECS_MAX_TOKEN_SIZE]; + EcsPicoMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "PicoMeters" }), + .quantity = EcsLength, + .base = EcsMeters, + .prefix = EcsPico }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsPicoMeters, + .kind = EcsF32 + }); -const char* ecs_parse_ws_eol( - const char *ptr) -{ - while (isspace(*ptr)) { - ptr ++; - } + EcsNanoMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "NanoMeters" }), + .quantity = EcsLength, + .base = EcsMeters, + .prefix = EcsNano }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsNanoMeters, + .kind = EcsF32 + }); - return ptr; -} + EcsMicroMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MicroMeters" }), + .quantity = EcsLength, + .base = EcsMeters, + .prefix = EcsMicro }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMicroMeters, + .kind = EcsF32 + }); -const char* ecs_parse_ws( - const char *ptr) -{ - while ((*ptr != '\n') && isspace(*ptr)) { - ptr ++; - } + EcsMilliMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MilliMeters" }), + .quantity = EcsLength, + .base = EcsMeters, + .prefix = EcsMilli }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMilliMeters, + .kind = EcsF32 + }); - return ptr; -} + EcsCentiMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "CentiMeters" }), + .quantity = EcsLength, + .base = EcsMeters, + .prefix = EcsCenti }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsCentiMeters, + .kind = EcsF32 + }); -const char* ecs_parse_digit( - const char *ptr, - char *token) -{ - char *tptr = token; - char ch = ptr[0]; + EcsKiloMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KiloMeters" }), + .quantity = EcsLength, + .base = EcsMeters, + .prefix = EcsKilo }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKiloMeters, + .kind = EcsF32 + }); + + EcsMiles = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Miles" }), + .quantity = EcsLength, + .symbol = "mi" + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMiles, + .kind = EcsF32 + }); - if (!isdigit(ch) && ch != '-') { - ecs_parser_error(NULL, NULL, 0, "invalid start of number '%s'", ptr); - return NULL; - } + EcsPixels = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Pixels" }), + .quantity = EcsLength, + .symbol = "px" + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsPixels, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); - tptr[0] = ch; - tptr ++; - ptr ++; + /* Pressure units */ - for (; (ch = *ptr); ptr ++) { - if (!isdigit(ch) && (ch != '.') && (ch != 'e')) { - break; - } + EcsPressure = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Pressure" }); + prev_scope = ecs_set_scope(world, EcsPressure); + EcsPascal = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Pascal" }), + .quantity = EcsPressure, + .symbol = "Pa" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsPascal, + .kind = EcsF32 + }); + EcsBar = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Bar" }), + .quantity = EcsPressure, + .symbol = "bar" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsBar, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); - tptr[0] = ch; - tptr ++; - } + /* Speed units */ - tptr[0] = '\0'; + EcsSpeed = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Speed" }); + prev_scope = ecs_set_scope(world, EcsSpeed); + EcsMetersPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MetersPerSecond" }), + .quantity = EcsSpeed, + .base = EcsMeters, + .over = EcsSeconds }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMetersPerSecond, + .kind = EcsF32 + }); + EcsKiloMetersPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KiloMetersPerSecond" }), + .quantity = EcsSpeed, + .base = EcsKiloMeters, + .over = EcsSeconds }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKiloMetersPerSecond, + .kind = EcsF32 + }); + EcsKiloMetersPerHour = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KiloMetersPerHour" }), + .quantity = EcsSpeed, + .base = EcsKiloMeters, + .over = EcsHours }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKiloMetersPerHour, + .kind = EcsF32 + }); + EcsMilesPerHour = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MilesPerHour" }), + .quantity = EcsSpeed, + .base = EcsMiles, + .over = EcsHours }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMilesPerHour, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); - return ptr; -} + /* Acceleration */ -/* -- Private functions -- */ + EcsAcceleration = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Acceleration" }), + .base = EcsMetersPerSecond, + .over = EcsSeconds }); + ecs_quantity_init(world, &(ecs_entity_desc_t){ + .id = EcsAcceleration + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsAcceleration, + .kind = EcsF32 + }); -bool flecs_isident( - char ch) -{ - return isalpha(ch) || (ch == '_'); -} + /* Temperature units */ -static -bool flecs_valid_identifier_start_char( - char ch) -{ - if (ch && (flecs_isident(ch) || (ch == '*') || - (ch == '0') || (ch == TOK_VARIABLE) || isdigit(ch))) - { - return true; - } + EcsTemperature = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Temperature" }); + prev_scope = ecs_set_scope(world, EcsTemperature); + EcsKelvin = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Kelvin" }), + .quantity = EcsTemperature, + .symbol = "K" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKelvin, + .kind = EcsF32 + }); + EcsCelsius = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Celsius" }), + .quantity = EcsTemperature, + .symbol = "°C" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsCelsius, + .kind = EcsF32 + }); + EcsFahrenheit = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Fahrenheit" }), + .quantity = EcsTemperature, + .symbol = "F" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsFahrenheit, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); - return false; -} + /* Data units */ -static -bool flecs_valid_token_start_char( - char ch) -{ - if ((ch == '"') || (ch == '{') || (ch == '}') || (ch == ',') || (ch == '-') - || (ch == '[') || (ch == ']') || (ch == '`') || - flecs_valid_identifier_start_char(ch)) - { - return true; - } + EcsData = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Data" }); + prev_scope = ecs_set_scope(world, EcsData); - return false; -} + EcsBits = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Bits" }), + .quantity = EcsData, + .symbol = "bit" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsBits, + .kind = EcsU64 + }); -static -bool flecs_valid_token_char( - char ch) -{ - if (ch && (flecs_isident(ch) || isdigit(ch) || ch == '.' || ch == '"')) { - return true; - } + EcsKiloBits = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KiloBits" }), + .quantity = EcsData, + .base = EcsBits, + .prefix = EcsKilo }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKiloBits, + .kind = EcsU64 + }); - return false; -} + EcsMegaBits = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MegaBits" }), + .quantity = EcsData, + .base = EcsBits, + .prefix = EcsMega }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMegaBits, + .kind = EcsU64 + }); -static -bool flecs_valid_operator_char( - char ch) -{ - if (ch == TOK_OPTIONAL || ch == TOK_NOT) { - return true; - } + EcsGigaBits = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "GigaBits" }), + .quantity = EcsData, + .base = EcsBits, + .prefix = EcsGiga }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsGigaBits, + .kind = EcsU64 + }); - return false; -} + EcsBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Bytes" }), + .quantity = EcsData, + .symbol = "B", + .base = EcsBits, + .translation = { .factor = 8, .power = 1 } }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsBytes, + .kind = EcsU64 + }); -const char* ecs_parse_token( - const char *name, - const char *expr, - const char *ptr, - char *token_out, - char delim) -{ - int64_t column = ptr - expr; + EcsKiloBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KiloBytes" }), + .quantity = EcsData, + .base = EcsBytes, + .prefix = EcsKilo }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKiloBytes, + .kind = EcsU64 + }); - ptr = ecs_parse_ws(ptr); - char *tptr = token_out, ch = ptr[0]; + EcsMegaBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MegaBytes" }), + .quantity = EcsData, + .base = EcsBytes, + .prefix = EcsMega }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMegaBytes, + .kind = EcsU64 + }); - if (!flecs_valid_token_start_char(ch)) { - if (ch == '\0' || ch == '\n') { - ecs_parser_error(name, expr, column, - "unexpected end of expression"); - } else { - ecs_parser_error(name, expr, column, - "invalid start of token '%s'", ptr); - } - return NULL; - } + EcsGigaBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "GigaBytes" }), + .quantity = EcsData, + .base = EcsBytes, + .prefix = EcsGiga }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsGigaBytes, + .kind = EcsU64 + }); - tptr[0] = ch; - tptr ++; - ptr ++; + EcsKibiBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KibiBytes" }), + .quantity = EcsData, + .base = EcsBytes, + .prefix = EcsKibi }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKibiBytes, + .kind = EcsU64 + }); - if (ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == ',' || ch == '`') { - tptr[0] = 0; - return ptr; - } + EcsMebiBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MebiBytes" }), + .quantity = EcsData, + .base = EcsBytes, + .prefix = EcsMebi }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMebiBytes, + .kind = EcsU64 + }); - int tmpl_nesting = 0; - bool in_str = ch == '"'; + EcsGibiBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "GibiBytes" }), + .quantity = EcsData, + .base = EcsBytes, + .prefix = EcsGibi }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsGibiBytes, + .kind = EcsU64 + }); - for (; (ch = *ptr); ptr ++) { - if (ch == '<') { - tmpl_nesting ++; - } else if (ch == '>') { - if (!tmpl_nesting) { - break; - } - tmpl_nesting --; - } else if (ch == '"') { - in_str = !in_str; - } else - if (!flecs_valid_token_char(ch) && !in_str) { - break; - } - if (delim && (ch == delim)) { - break; - } + ecs_set_scope(world, prev_scope); - tptr[0] = ch; - tptr ++; - } + /* DataRate units */ - tptr[0] = '\0'; + EcsDataRate = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "DataRate" }); + prev_scope = ecs_set_scope(world, EcsDataRate); - if (tmpl_nesting != 0) { - ecs_parser_error(name, expr, column, - "identifier '%s' has mismatching < > pairs", ptr); - return NULL; - } + EcsBitsPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "BitsPerSecond" }), + .quantity = EcsDataRate, + .base = EcsBits, + .over = EcsSeconds }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsBitsPerSecond, + .kind = EcsU64 + }); - const char *next_ptr = ecs_parse_ws(ptr); - if (next_ptr[0] == ':' && next_ptr != ptr) { - /* Whitespace between token and : is significant */ - ptr = next_ptr - 1; - } else { - ptr = next_ptr; - } + EcsKiloBitsPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KiloBitsPerSecond" }), + .quantity = EcsDataRate, + .base = EcsKiloBits, + .over = EcsSeconds + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKiloBitsPerSecond, + .kind = EcsU64 + }); - return ptr; -} + EcsMegaBitsPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MegaBitsPerSecond" }), + .quantity = EcsDataRate, + .base = EcsMegaBits, + .over = EcsSeconds + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMegaBitsPerSecond, + .kind = EcsU64 + }); -const char* ecs_parse_identifier( - const char *name, - const char *expr, - const char *ptr, - char *token_out) -{ - if (!flecs_valid_identifier_start_char(ptr[0]) && (ptr[0] != '"')) { - ecs_parser_error(name, expr, (ptr - expr), - "expected start of identifier"); - return NULL; - } + EcsGigaBitsPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "GigaBitsPerSecond" }), + .quantity = EcsDataRate, + .base = EcsGigaBits, + .over = EcsSeconds + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsGigaBitsPerSecond, + .kind = EcsU64 + }); - ptr = ecs_parse_token(name, expr, ptr, token_out, 0); + EcsBytesPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "BytesPerSecond" }), + .quantity = EcsDataRate, + .base = EcsBytes, + .over = EcsSeconds }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsBytesPerSecond, + .kind = EcsU64 + }); - return ptr; -} + EcsKiloBytesPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KiloBytesPerSecond" }), + .quantity = EcsDataRate, + .base = EcsKiloBytes, + .over = EcsSeconds + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKiloBytesPerSecond, + .kind = EcsU64 + }); -static -int flecs_parse_identifier( - const char *token, - ecs_term_id_t *out) -{ - const char *tptr = token; - if (tptr[0] == TOK_VARIABLE && tptr[1]) { - out->flags |= EcsIsVariable; - tptr ++; - } - if (tptr[0] == TOK_EXPR_STRING && tptr[1]) { - out->flags |= EcsIsName; - tptr ++; - if (tptr[0] == TOK_NOT) { - /* Already parsed */ - tptr ++; - } - } + EcsMegaBytesPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MegaBytesPerSecond" }), + .quantity = EcsDataRate, + .base = EcsMegaBytes, + .over = EcsSeconds + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMegaBytesPerSecond, + .kind = EcsU64 + }); - char *name = ecs_os_strdup(tptr); - out->name = name; + EcsGigaBytesPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "GigaBytesPerSecond" }), + .quantity = EcsDataRate, + .base = EcsGigaBytes, + .over = EcsSeconds + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsGigaBytesPerSecond, + .kind = EcsU64 + }); - ecs_size_t len = ecs_os_strlen(name); - if (out->flags & EcsIsName) { - if (name[len - 1] != TOK_EXPR_STRING) { - ecs_parser_error(NULL, token, 0, "missing '\"' at end of string"); - return -1; - } else { - name[len - 1] = '\0'; - } - } + ecs_set_scope(world, prev_scope); - return 0; -} + /* Percentage */ -static -ecs_entity_t flecs_parse_role( - const char *name, - const char *sig, - int64_t column, - const char *token) -{ - if (!ecs_os_strcmp(token, TOK_ROLE_AND)) { - return ECS_AND; - } else if (!ecs_os_strcmp(token, TOK_ROLE_OR)) { - return ECS_OR; - } else if (!ecs_os_strcmp(token, TOK_ROLE_NOT)) { - return ECS_NOT; - } else if (!ecs_os_strcmp(token, TOK_OVERRIDE)) { - return ECS_OVERRIDE; - } else if (!ecs_os_strcmp(token, TOK_ROLE_TOGGLE)) { - return ECS_TOGGLE; - } else { - ecs_parser_error(name, sig, column, "invalid role '%s'", token); - return 0; - } -} + EcsPercentage = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Percentage" }); + ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = EcsPercentage, + .symbol = "%" + }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsPercentage, + .kind = EcsF32 + }); -static -ecs_oper_kind_t flecs_parse_operator( - char ch) -{ - if (ch == TOK_OPTIONAL) { - return EcsOptional; - } else if (ch == TOK_NOT) { - return EcsNot; - } else { - ecs_throw(ECS_INTERNAL_ERROR, NULL); - } -error: - return 0; -} + /* Angles */ -static -const char* flecs_parse_annotation( - const char *name, - const char *sig, - int64_t column, - const char *ptr, - ecs_inout_kind_t *inout_kind_out) -{ - char token[ECS_MAX_TOKEN_SIZE]; + EcsAngle = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Angle" }); + prev_scope = ecs_set_scope(world, EcsAngle); + EcsRadians = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Radians" }), + .quantity = EcsAngle, + .symbol = "rad" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsRadians, + .kind = EcsF32 + }); - ptr = ecs_parse_identifier(name, sig, ptr, token); - if (!ptr) { - return NULL; - } - - if (!ecs_os_strcmp(token, TOK_IN)) { - *inout_kind_out = EcsIn; - } else - if (!ecs_os_strcmp(token, TOK_OUT)) { - *inout_kind_out = EcsOut; - } else - if (!ecs_os_strcmp(token, TOK_INOUT)) { - *inout_kind_out = EcsInOut; - } else if (!ecs_os_strcmp(token, TOK_INOUT_NONE)) { - *inout_kind_out = EcsInOutNone; - } + EcsDegrees = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Degrees" }), + .quantity = EcsAngle, + .symbol = "°" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsDegrees, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); - ptr = ecs_parse_ws(ptr); + /* Color */ - if (ptr[0] != TOK_BRACKET_CLOSE) { - printf("errr\n"); - ecs_parser_error(name, sig, column, "expected ]"); - return NULL; - } + EcsColor = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Color" }); + prev_scope = ecs_set_scope(world, EcsColor); + EcsColorRgb = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Rgb" }), + .quantity = EcsColor }); - return ptr + 1; -} + EcsColorHsl = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Hsl" }), + .quantity = EcsColor }); -static -uint8_t flecs_parse_set_token( - const char *token) -{ - if (!ecs_os_strcmp(token, TOK_SELF)) { - return EcsSelf; - } else if (!ecs_os_strcmp(token, TOK_UP)) { - return EcsUp; - } else if (!ecs_os_strcmp(token, TOK_DOWN)) { - return EcsDown; - } else if (!ecs_os_strcmp(token, TOK_CASCADE)) { - return EcsCascade; - } else if (!ecs_os_strcmp(token, TOK_DESC)) { - return EcsDesc; - } else if (!ecs_os_strcmp(token, TOK_PARENT)) { - return EcsParent; - } else { - return 0; - } -} + EcsColorCss = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Css" }), + .quantity = EcsColor }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsColorCss, + .kind = EcsString + }); -static -const char* flecs_parse_term_flags( - const ecs_world_t *world, - const char *name, - const char *expr, - int64_t column, - const char *ptr, - char *token, - ecs_term_id_t *id, - char tok_end) -{ - char token_buf[ECS_MAX_TOKEN_SIZE] = {0}; - if (!token) { - token = token_buf; - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } - } + ecs_set_scope(world, prev_scope); - do { - uint8_t tok = flecs_parse_set_token(token); - if (!tok) { - ecs_parser_error(name, expr, column, - "invalid set token '%s'", token); - return NULL; - } + /* DeciBel */ - if (id->flags & tok) { - ecs_parser_error(name, expr, column, - "duplicate set token '%s'", token); - return NULL; - } - - id->flags |= tok; + EcsBel = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Bel" }), + .symbol = "B" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsBel, + .kind = EcsF32 + }); + EcsDeciBel = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "DeciBel" }), + .prefix = EcsDeci, + .base = EcsBel }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsDeciBel, + .kind = EcsF32 + }); - if (ptr[0] == TOK_PAREN_OPEN) { - ptr ++; + /* Frequency */ - /* Relationship (overrides IsA default) */ - if (!isdigit(ptr[0]) && flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } + EcsFrequency = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Frequency" }); + prev_scope = ecs_set_scope(world, EcsFrequency); - id->trav = ecs_lookup_fullpath(world, token); - if (!id->trav) { - ecs_parser_error(name, expr, column, - "unresolved identifier '%s'", token); - return NULL; - } + EcsHertz = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Hertz" }), + .quantity = EcsFrequency, + .symbol = "Hz" }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsHertz, + .kind = EcsF32 + }); - if (ptr[0] == TOK_AND) { - ptr = ecs_parse_ws(ptr + 1); - } else if (ptr[0] != TOK_PAREN_CLOSE) { - ecs_parser_error(name, expr, column, - "expected ',' or ')'"); - return NULL; - } - } + EcsKiloHertz = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "KiloHertz" }), + .prefix = EcsKilo, + .base = EcsHertz }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsKiloHertz, + .kind = EcsF32 + }); - if (ptr[0] != TOK_PAREN_CLOSE) { - ecs_parser_error(name, expr, column, "expected ')', got '%c'", - ptr[0]); - return NULL; - } else { - ptr = ecs_parse_ws(ptr + 1); - if (ptr[0] != tok_end && ptr[0] != TOK_AND && ptr[0] != 0) { - ecs_parser_error(name, expr, column, - "expected end of set expr"); - return NULL; - } - } - } + EcsMegaHertz = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "MegaHertz" }), + .prefix = EcsMega, + .base = EcsHertz }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsMegaHertz, + .kind = EcsF32 + }); - /* Next token in set expression */ - if (ptr[0] == TOK_BITWISE_OR) { - ptr ++; - if (flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } - } + EcsGigaHertz = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "GigaHertz" }), + .prefix = EcsGiga, + .base = EcsHertz }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsGigaHertz, + .kind = EcsF32 + }); + ecs_set_scope(world, prev_scope); - /* End of set expression */ - } else if (ptr[0] == tok_end || ptr[0] == TOK_AND || !ptr[0]) { - break; - } - } while (true); + EcsUri = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Uri" }); + prev_scope = ecs_set_scope(world, EcsUri); - return ptr; -} + EcsUriHyperlink = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Hyperlink" }), + .quantity = EcsUri }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsUriHyperlink, + .kind = EcsString + }); -static -const char* flecs_parse_arguments( - const ecs_world_t *world, - const char *name, - const char *expr, - int64_t column, - const char *ptr, - char *token, - ecs_term_t *term, - ecs_term_id_t *extra_args) -{ - (void)column; + EcsUriImage = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Image" }), + .quantity = EcsUri }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsUriImage, + .kind = EcsString + }); - int32_t arg = 0; + EcsUriFile = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "File" }), + .quantity = EcsUri }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsUriFile, + .kind = EcsString + }); + ecs_set_scope(world, prev_scope); - if (extra_args) { - ecs_os_memset_n(extra_args, 0, ecs_term_id_t, ECS_PARSER_MAX_ARGS); - } + /* Documentation */ +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); - if (!term) { - arg = 2; - } + ecs_doc_set_brief(world, EcsDuration, + "Time amount (e.g. \"20 seconds\", \"2 hours\")"); + ecs_doc_set_brief(world, EcsSeconds, "Time amount in seconds"); + ecs_doc_set_brief(world, EcsMinutes, "60 seconds"); + ecs_doc_set_brief(world, EcsHours, "60 minutes"); + ecs_doc_set_brief(world, EcsDays, "24 hours"); - do { - if (flecs_valid_token_start_char(ptr[0])) { - if ((arg == ECS_PARSER_MAX_ARGS) || (!extra_args && arg == 2)) { - ecs_parser_error(name, expr, (ptr - expr), - "too many arguments in term"); - return NULL; - } + ecs_doc_set_brief(world, EcsTime, + "Time passed since an epoch (e.g. \"5pm\", \"March 3rd 2022\")"); + ecs_doc_set_brief(world, EcsDate, + "Seconds passed since January 1st 1970"); - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } + ecs_doc_set_brief(world, EcsMass, "Units of mass (e.g. \"5 kilograms\")"); - ecs_term_id_t *term_id = NULL; + ecs_doc_set_brief(world, EcsElectricCurrent, + "Units of electrical current (e.g. \"2 ampere\")"); - if (arg == 0) { - term_id = &term->src; - } else if (arg == 1) { - term_id = &term->second; - } else { - term_id = &extra_args[arg - 2]; - } + ecs_doc_set_brief(world, EcsAmount, + "Units of amount of substance (e.g. \"2 mole\")"); - /* If token is a colon, the token is an identifier followed by a - * set expression. */ - if (ptr[0] == TOK_COLON) { - if (flecs_parse_identifier(token, term_id)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - return NULL; - } + ecs_doc_set_brief(world, EcsLuminousIntensity, + "Units of luminous intensity (e.g. \"1 candela\")"); - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, - NULL, term_id, TOK_PAREN_CLOSE); - if (!ptr) { - return NULL; - } + ecs_doc_set_brief(world, EcsForce, "Units of force (e.g. \"10 newton\")"); - /* Check for term flags */ - } else if (!ecs_os_strcmp(token, TOK_CASCADE) || - !ecs_os_strcmp(token, TOK_DESC) || - !ecs_os_strcmp(token, TOK_SELF) || - !ecs_os_strcmp(token, TOK_UP) || - !ecs_os_strcmp(token, TOK_DOWN) || - !(ecs_os_strcmp(token, TOK_PARENT))) - { - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, - token, term_id, TOK_PAREN_CLOSE); - if (!ptr) { - return NULL; - } + ecs_doc_set_brief(world, EcsLength, + "Units of length (e.g. \"5 meters\", \"20 miles\")"); - /* Regular identifier */ - } else if (flecs_parse_identifier(token, term_id)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - return NULL; - } + ecs_doc_set_brief(world, EcsPressure, + "Units of pressure (e.g. \"1 bar\", \"1000 pascal\")"); - if (ptr[0] == TOK_AND) { - ptr = ecs_parse_ws(ptr + 1); + ecs_doc_set_brief(world, EcsSpeed, + "Units of movement (e.g. \"5 meters/second\")"); - if (term) { - term->id_flags = ECS_PAIR; - } + ecs_doc_set_brief(world, EcsAcceleration, + "Unit of speed increase (e.g. \"5 meters/second/second\")"); - } else if (ptr[0] == TOK_PAREN_CLOSE) { - ptr = ecs_parse_ws(ptr + 1); - break; + ecs_doc_set_brief(world, EcsTemperature, + "Units of temperature (e.g. \"5 degrees Celsius\")"); - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected ',' or ')'"); - return NULL; - } + ecs_doc_set_brief(world, EcsData, + "Units of information (e.g. \"8 bits\", \"100 megabytes\")"); - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected identifier or set expression"); - return NULL; - } + ecs_doc_set_brief(world, EcsDataRate, + "Units of data transmission (e.g. \"100 megabits/second\")"); - arg ++; + ecs_doc_set_brief(world, EcsAngle, + "Units of rotation (e.g. \"1.2 radians\", \"180 degrees\")"); - } while (true); + ecs_doc_set_brief(world, EcsFrequency, + "The number of occurrences of a repeating event per unit of time."); - return ptr; + ecs_doc_set_brief(world, EcsUri, "Universal resource identifier."); +#endif } +#endif + +/** + * @file datastructures/allocator.c + * @brief Allocator for any size. + * + * Allocators create a block allocator for each requested size. + */ + + static -void flecs_parser_unexpected_char( - const char *name, - const char *expr, - const char *ptr, - char ch) +ecs_size_t flecs_allocator_size( + ecs_size_t size) { - if (ch && (ch != '\n')) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected character '%c'", ch); - } else { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected end of term"); - } + return ECS_ALIGN(size, 16); } static -const char* flecs_parse_term( - const ecs_world_t *world, - const char *name, - const char *expr, - ecs_term_t *term_out, - ecs_term_id_t *extra_args) +ecs_size_t flecs_allocator_size_hash( + ecs_size_t size) { - const char *ptr = expr; - char token[ECS_MAX_TOKEN_SIZE] = {0}; - ecs_term_t term = { .move = true /* parser never owns resources */ }; + return size >> 4; +} - ptr = ecs_parse_ws(ptr); +void flecs_allocator_init( + ecs_allocator_t *a) +{ + flecs_ballocator_init_n(&a->chunks, ecs_block_allocator_t, + FLECS_SPARSE_PAGE_SIZE); + flecs_sparse_init_t(&a->sizes, NULL, &a->chunks, ecs_block_allocator_t); +} - /* Inout specifiers always come first */ - if (ptr[0] == TOK_BRACKET_OPEN) { - ptr = flecs_parse_annotation(name, expr, (ptr - expr), ptr + 1, &term.inout); - if (!ptr) { - goto error; - } - ptr = ecs_parse_ws(ptr); - } +void flecs_allocator_fini( + ecs_allocator_t *a) +{ + ecs_assert(a != NULL, ECS_INVALID_PARAMETER, NULL); - if (flecs_valid_operator_char(ptr[0])) { - term.oper = flecs_parse_operator(ptr[0]); - ptr = ecs_parse_ws(ptr + 1); + int32_t i = 0, count = flecs_sparse_count(&a->sizes); + for (i = 0; i < count; i ++) { + ecs_block_allocator_t *ba = flecs_sparse_get_dense_t( + &a->sizes, ecs_block_allocator_t, i); + flecs_ballocator_fini(ba); } + flecs_sparse_fini(&a->sizes); - /* If next token is the start of an identifier, it could be either a type - * role, source or component identifier */ - if (flecs_valid_identifier_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - goto error; - } + flecs_ballocator_fini(&a->chunks); +} - /* Is token a type role? */ - if (ptr[0] == TOK_BITWISE_OR && ptr[1] != TOK_BITWISE_OR) { - ptr ++; - goto flecs_parse_role; - } +ecs_block_allocator_t* flecs_allocator_get( + ecs_allocator_t *a, + ecs_size_t size) +{ + ecs_assert(size >= 0, ECS_INTERNAL_ERROR, NULL); + if (!size) { + return NULL; + } - /* Is token a predicate? */ - if (ptr[0] == TOK_PAREN_OPEN) { - goto parse_predicate; - } + ecs_assert(a != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(size <= flecs_allocator_size(size), ECS_INTERNAL_ERROR, NULL); + size = flecs_allocator_size(size); + ecs_size_t hash = flecs_allocator_size_hash(size); + ecs_block_allocator_t *result = flecs_sparse_get_any_t(&a->sizes, + ecs_block_allocator_t, (uint32_t)hash); + + if (!result) { + result = flecs_sparse_ensure_fast_t(&a->sizes, + ecs_block_allocator_t, (uint32_t)hash); + flecs_ballocator_init(result, size); + } - /* Next token must be a predicate */ - goto parse_predicate; + ecs_assert(result->data_size == size, ECS_INTERNAL_ERROR, NULL); - /* Pair with implicit subject */ - } else if (ptr[0] == TOK_PAREN_OPEN) { - goto parse_pair; + return result; +} - /* Open query scope */ - } else if (ptr[0] == TOK_SCOPE_OPEN) { - term.first.id = EcsScopeOpen; - term.src.id = 0; - term.src.flags = EcsIsEntity; - term.inout = EcsInOutNone; - goto parse_done; +char* flecs_strdup( + ecs_allocator_t *a, + const char* str) +{ + ecs_size_t len = ecs_os_strlen(str); + char *result = flecs_alloc_n(a, char, len + 1); + ecs_os_memcpy(result, str, len + 1); + return result; +} - /* Close query scope */ - } else if (ptr[0] == TOK_SCOPE_CLOSE) { - term.first.id = EcsScopeClose; - term.src.id = 0; - term.src.flags = EcsIsEntity; - term.inout = EcsInOutNone; - ptr = ecs_parse_ws(ptr + 1); - goto parse_done; +void flecs_strfree( + ecs_allocator_t *a, + char* str) +{ + ecs_size_t len = ecs_os_strlen(str); + flecs_free_n(a, char, len + 1, str); +} - /* Nothing else expected here */ +void* flecs_dup( + ecs_allocator_t *a, + ecs_size_t size, + const void *src) +{ + ecs_block_allocator_t *ba = flecs_allocator_get(a, size); + if (ba) { + void *dst = flecs_balloc(ba); + ecs_os_memcpy(dst, src, size); + return dst; } else { - flecs_parser_unexpected_char(name, expr, ptr, ptr[0]); - goto error; - } - -flecs_parse_role: - term.id_flags = flecs_parse_role(name, expr, (ptr - expr), token); - if (!term.id_flags) { - goto error; + return NULL; } +} - ptr = ecs_parse_ws(ptr); - - /* If next token is the source token, this is an empty source */ - if (flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - goto error; - } +/** + * @file datastructures/bitset.c + * @brief Bitset data structure. + * + * Simple bitset implementation. The bitset allows for storage of arbitrary + * numbers of bits. + */ - /* If not, it's a predicate */ - goto parse_predicate; - } else if (ptr[0] == TOK_PAREN_OPEN) { - goto parse_pair; - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected identifier after role"); - goto error; +static +void ensure( + ecs_bitset_t *bs, + ecs_size_t size) +{ + if (!bs->size) { + int32_t new_size = ((size - 1) / 64 + 1) * ECS_SIZEOF(uint64_t); + bs->size = ((size - 1) / 64 + 1) * 64; + bs->data = ecs_os_calloc(new_size); + } else if (size > bs->size) { + int32_t prev_size = ((bs->size - 1) / 64 + 1) * ECS_SIZEOF(uint64_t); + bs->size = ((size - 1) / 64 + 1) * 64; + int32_t new_size = ((size - 1) / 64 + 1) * ECS_SIZEOF(uint64_t); + bs->data = ecs_os_realloc(bs->data, new_size); + ecs_os_memset(ECS_OFFSET(bs->data, prev_size), 0, new_size - prev_size); } +} -parse_predicate: - if (flecs_parse_identifier(token, &term.first)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - goto error; +void flecs_bitset_init( + ecs_bitset_t* bs) +{ + bs->size = 0; + bs->count = 0; + bs->data = NULL; +} + +void flecs_bitset_ensure( + ecs_bitset_t *bs, + int32_t count) +{ + if (count > bs->count) { + bs->count = count; + ensure(bs, count); } +} - /* Set expression */ - if (ptr[0] == TOK_COLON) { - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, NULL, - &term.first, TOK_COLON); - if (!ptr) { - goto error; - } +void flecs_bitset_fini( + ecs_bitset_t *bs) +{ + ecs_os_free(bs->data); + bs->data = NULL; + bs->count = 0; +} - ptr = ecs_parse_ws(ptr); +void flecs_bitset_addn( + ecs_bitset_t *bs, + int32_t count) +{ + int32_t elem = bs->count += count; + ensure(bs, elem); +} - if (ptr[0] == TOK_AND || !ptr[0]) { - goto parse_done; - } +void flecs_bitset_set( + ecs_bitset_t *bs, + int32_t elem, + bool value) +{ + ecs_check(elem < bs->count, ECS_INVALID_PARAMETER, NULL); + uint32_t hi = ((uint32_t)elem) >> 6; + uint32_t lo = ((uint32_t)elem) & 0x3F; + uint64_t v = bs->data[hi]; + bs->data[hi] = (v & ~((uint64_t)1 << lo)) | ((uint64_t)value << lo); +error: + return; +} - if (ptr[0] != TOK_COLON) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected token '%c' after predicate set expression", ptr[0]); - goto error; - } +bool flecs_bitset_get( + const ecs_bitset_t *bs, + int32_t elem) +{ + ecs_check(elem < bs->count, ECS_INVALID_PARAMETER, NULL); + return !!(bs->data[elem >> 6] & ((uint64_t)1 << ((uint64_t)elem & 0x3F))); +error: + return false; +} - ptr = ecs_parse_ws(ptr + 1); - } else if (!ecs_os_strncmp(ptr, TOK_EQ, 2)) { - ptr = ecs_parse_ws(ptr + 2); - goto parse_eq; - } else if (!ecs_os_strncmp(ptr, TOK_NEQ, 2)) { - ptr = ecs_parse_ws(ptr + 2); - goto parse_neq; - } else if (!ecs_os_strncmp(ptr, TOK_MATCH, 2)) { - ptr = ecs_parse_ws(ptr + 2); - goto parse_match; - } else { - ptr = ecs_parse_ws(ptr); - } +int32_t flecs_bitset_count( + const ecs_bitset_t *bs) +{ + return bs->count; +} - if (ptr[0] == TOK_PAREN_OPEN) { - ptr ++; - if (ptr[0] == TOK_PAREN_CLOSE) { - term.src.flags = EcsIsEntity; - term.src.id = 0; - ptr ++; - ptr = ecs_parse_ws(ptr); - } else { - ptr = flecs_parse_arguments( - world, name, expr, (ptr - expr), ptr, token, &term, extra_args); - } +void flecs_bitset_remove( + ecs_bitset_t *bs, + int32_t elem) +{ + ecs_check(elem < bs->count, ECS_INVALID_PARAMETER, NULL); + int32_t last = bs->count - 1; + bool last_value = flecs_bitset_get(bs, last); + flecs_bitset_set(bs, elem, last_value); + flecs_bitset_set(bs, last, 0); + bs->count --; +error: + return; +} - goto parse_done; - } +void flecs_bitset_swap( + ecs_bitset_t *bs, + int32_t elem_a, + int32_t elem_b) +{ + ecs_check(elem_a < bs->count, ECS_INVALID_PARAMETER, NULL); + ecs_check(elem_b < bs->count, ECS_INVALID_PARAMETER, NULL); - goto parse_done; + bool a = flecs_bitset_get(bs, elem_a); + bool b = flecs_bitset_get(bs, elem_b); + flecs_bitset_set(bs, elem_a, b); + flecs_bitset_set(bs, elem_b, a); +error: + return; +} -parse_eq: - term.src = term.first; - term.first = (ecs_term_id_t){0}; - term.first.id = EcsPredEq; - goto parse_right_operand; +/** + * @file datastructures/block_allocator.c + * @brief Block allocator. + * + * A block allocator is an allocator for a fixed size that allocates blocks of + * memory with N elements of the requested size. + */ -parse_neq: - term.src = term.first; - term.first = (ecs_term_id_t){0}; - term.first.id = EcsPredEq; - if (term.oper != EcsAnd) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid operator combination"); - goto error; - } - term.oper = EcsNot; - goto parse_right_operand; - -parse_match: - term.src = term.first; - term.first = (ecs_term_id_t){0}; - term.first.id = EcsPredMatch; - goto parse_right_operand; - -parse_right_operand: - if (flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - goto error; - } - if (term.first.id == EcsPredMatch) { - if (token[0] == '"' && token[1] == '!') { - term.oper = EcsNot; - } - } +// #ifdef FLECS_SANITIZE +// #define FLECS_MEMSET_UNINITIALIZED +// #endif - if (flecs_parse_identifier(token, &term.second)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - goto error; - } +int64_t ecs_block_allocator_alloc_count = 0; +int64_t ecs_block_allocator_free_count = 0; - term.src.flags &= ~EcsTraverseFlags; - term.src.flags |= EcsSelf; - term.inout = EcsInOutNone; - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected identifier"); - goto error; - } - goto parse_done; -parse_pair: - ptr = ecs_parse_identifier(name, expr, ptr + 1, token); - if (!ptr) { - goto error; - } +#ifndef FLECS_USE_OS_ALLOC - if (ptr[0] == TOK_COLON) { - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, - NULL, &term.first, TOK_PAREN_CLOSE); - if (!ptr) { - goto error; - } +static +ecs_block_allocator_chunk_header_t* flecs_balloc_block( + ecs_block_allocator_t *allocator) +{ + if (!allocator->chunk_size) { + return NULL; } - if (ptr[0] == TOK_AND) { - ptr = ecs_parse_ws(ptr + 1); - if (ptr[0] == TOK_PAREN_CLOSE) { - ecs_parser_error(name, expr, (ptr - expr), - "expected identifier for second element of pair"); - goto error; - } - - term.src.id = EcsThis; - term.src.flags |= EcsIsVariable; - goto parse_pair_predicate; - } else if (ptr[0] == TOK_PAREN_CLOSE) { - term.src.id = EcsThis; - term.src.flags |= EcsIsVariable; - goto parse_pair_predicate; + ecs_block_allocator_block_t *block = + ecs_os_malloc(ECS_SIZEOF(ecs_block_allocator_block_t) + + allocator->block_size); + ecs_block_allocator_chunk_header_t *first_chunk = ECS_OFFSET(block, + ECS_SIZEOF(ecs_block_allocator_block_t)); + + block->memory = first_chunk; + if (!allocator->block_tail) { + ecs_assert(!allocator->block_head, ECS_INTERNAL_ERROR, 0); + block->next = NULL; + allocator->block_head = block; + allocator->block_tail = block; } else { - flecs_parser_unexpected_char(name, expr, ptr, ptr[0]); - goto error; + block->next = NULL; + allocator->block_tail->next = block; + allocator->block_tail = block; } -parse_pair_predicate: - if (flecs_parse_identifier(token, &term.first)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - goto error; + ecs_block_allocator_chunk_header_t *chunk = first_chunk; + int32_t i, end; + for (i = 0, end = allocator->chunks_per_block - 1; i < end; ++i) { + chunk->next = ECS_OFFSET(chunk, allocator->chunk_size); + chunk = chunk->next; } - ptr = ecs_parse_ws(ptr); - if (flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - goto error; - } + ecs_os_linc(&ecs_block_allocator_alloc_count); - if (ptr[0] == TOK_COLON) { - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, - NULL, &term.second, TOK_PAREN_CLOSE); - if (!ptr) { - goto error; - } - } + chunk->next = NULL; + return first_chunk; +} - if (ptr[0] == TOK_PAREN_CLOSE || ptr[0] == TOK_AND) { - goto parse_pair_object; - } else { - flecs_parser_unexpected_char(name, expr, ptr, ptr[0]); - goto error; - } - } else if (ptr[0] == TOK_PAREN_CLOSE) { - /* No object */ - ptr ++; - goto parse_done; - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected pair object or ')'"); - goto error; - } +#endif -parse_pair_object: - if (flecs_parse_identifier(token, &term.second)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - goto error; - } +void flecs_ballocator_init( + ecs_block_allocator_t *ba, + ecs_size_t size) +{ + ecs_assert(ba != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); + ba->data_size = size; +#ifdef FLECS_SANITIZE + ba->alloc_count = 0; + size += ECS_SIZEOF(int64_t); +#endif + ba->chunk_size = ECS_ALIGN(size, 16); + ba->chunks_per_block = ECS_MAX(4096 / ba->chunk_size, 1); + ba->block_size = ba->chunks_per_block * ba->chunk_size; + ba->head = NULL; + ba->block_head = NULL; + ba->block_tail = NULL; +} - if (term.id_flags == 0) { - term.id_flags = ECS_PAIR; - } +ecs_block_allocator_t* flecs_ballocator_new( + ecs_size_t size) +{ + ecs_block_allocator_t *result = ecs_os_calloc_t(ecs_block_allocator_t); + flecs_ballocator_init(result, size); + return result; +} - if (ptr[0] == TOK_AND) { - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_arguments( - world, name, expr, (ptr - expr), ptr, token, NULL, extra_args); - if (!ptr) { - goto error; - } - } else { - ptr ++; - } +void flecs_ballocator_fini( + ecs_block_allocator_t *ba) +{ + ecs_assert(ba != NULL, ECS_INTERNAL_ERROR, NULL); - ptr = ecs_parse_ws(ptr); - goto parse_done; +#ifdef FLECS_SANITIZE + ecs_assert(ba->alloc_count == 0, ECS_LEAK_DETECTED, + "(size = %u)", (uint32_t)ba->data_size); +#endif -parse_done: - *term_out = term; - return ptr; + ecs_block_allocator_block_t *block; + for (block = ba->block_head; block;) { + ecs_block_allocator_block_t *next = block->next; + ecs_os_free(block); + ecs_os_linc(&ecs_block_allocator_free_count); + block = next; + } -error: - ecs_term_fini(&term); - *term_out = (ecs_term_t){0}; - return NULL; + ba->block_head = NULL; } -static -bool flecs_is_valid_end_of_term( - const char *ptr) +void flecs_ballocator_free( + ecs_block_allocator_t *ba) { - if ((ptr[0] == TOK_AND) || /* another term with And operator */ - (ptr[0] == TOK_OR[0]) || /* another term with Or operator */ - (ptr[0] == '\n') || /* newlines are valid */ - (ptr[0] == '\0') || /* end of string */ - (ptr[0] == '/') || /* comment (in plecs) */ - (ptr[0] == '{') || /* scope (in plecs) */ - (ptr[0] == '}') || - (ptr[0] == '[') || /* collection scope (in plecs) */ - (ptr[0] == ']') || - (ptr[0] == ':') || /* inheritance (in plecs) */ - (ptr[0] == '=')) /* assignment (in plecs) */ - { - return true; - } - return false; + flecs_ballocator_fini(ba); + ecs_os_free(ba); } -char* ecs_parse_term( - const ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_term_t *term, - ecs_term_id_t *extra_args) +void* flecs_balloc( + ecs_block_allocator_t *ba) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(term != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_term_id_t *src = &term->src; + void *result; +#ifdef FLECS_USE_OS_ALLOC + result = ecs_os_malloc(ba->data_size); +#else - if (ptr != expr) { - if (ptr[0]) { - if (ptr[0] == ',') { - ptr ++; - } else if (ptr[0] == '|') { - ptr += 2; - } else if (ptr[0] == '{') { - ptr ++; - } else if (ptr[0] == '}') { - /* nothing to be done */ - } else { - ecs_parser_error(name, expr, (ptr - expr), - "invalid preceding token"); - } - } - } + if (!ba) return NULL; - ptr = ecs_parse_ws_eol(ptr); - if (!ptr[0]) { - *term = (ecs_term_t){0}; - return ECS_CONST_CAST(char*, ptr); + if (!ba->head) { + ba->head = flecs_balloc_block(ba); + ecs_assert(ba->head != NULL, ECS_INTERNAL_ERROR, NULL); } - if (ptr == expr && !strcmp(expr, "0")) { - return ECS_CONST_CAST(char*, &ptr[1]); - } + result = ba->head; + ba->head = ba->head->next; - /* Parse next element */ - ptr = flecs_parse_term(world, name, ptr, term, extra_args); - if (!ptr) { - goto error; - } +#ifdef FLECS_SANITIZE + ecs_assert(ba->alloc_count >= 0, ECS_INTERNAL_ERROR, "corrupted allocator"); + ba->alloc_count ++; + *(int64_t*)result = ba->chunk_size; + result = ECS_OFFSET(result, ECS_SIZEOF(int64_t)); +#endif +#endif - /* Check for $() notation */ - if (term->first.name && !ecs_os_strcmp(term->first.name, "$")) { - if (term->src.name) { - /* Safe, parser owns name */ - ecs_os_free(ECS_CONST_CAST(char*, term->first.name)); - - term->first = term->src; +#ifdef FLECS_MEMSET_UNINITIALIZED + ecs_os_memset(result, 0xAA, ba->data_size); +#endif - if (term->second.name) { - term->src = term->second; - } else { - term->src.id = EcsThis; - term->src.name = NULL; - term->src.flags |= EcsIsVariable; - } + return result; +} - const char *var_name = strrchr(term->first.name, '.'); - if (var_name) { - var_name ++; - } else { - var_name = term->first.name; - } +void* flecs_bcalloc( + ecs_block_allocator_t *ba) +{ +#ifdef FLECS_USE_OS_ALLOC + ecs_assert(ba != NULL, ECS_INTERNAL_ERROR, NULL); + return ecs_os_calloc(ba->data_size); +#else + if (!ba) return NULL; + void *result = flecs_balloc(ba); + ecs_os_memset(result, 0, ba->data_size); + return result; +#endif +} - term->second.name = ecs_os_strdup(var_name); - term->second.flags |= EcsIsVariable; - } - } +void flecs_bfree( + ecs_block_allocator_t *ba, + void *memory) +{ + flecs_bfree_w_dbg_info(ba, memory, NULL); +} - /* Post-parse consistency checks */ +void flecs_bfree_w_dbg_info( + ecs_block_allocator_t *ba, + void *memory, + const char *type_name) +{ + (void)type_name; - /* If next token is OR, term is part of an OR expression */ - if (!ecs_os_strncmp(ptr, TOK_OR, 2)) { - /* An OR operator must always follow an AND or another OR */ - if (term->oper != EcsAnd) { - ecs_parser_error(name, expr, (ptr - expr), - "cannot combine || with other operators"); - goto error; - } +#ifdef FLECS_USE_OS_ALLOC + (void)ba; + ecs_os_free(memory); + return; +#else - term->oper = EcsOr; + if (!ba) { + ecs_assert(memory == NULL, ECS_INTERNAL_ERROR, NULL); + return; + } + if (memory == NULL) { + return; } - /* Term must either end in end of expression, AND or OR token */ - if (!flecs_is_valid_end_of_term(ptr)) { - if (!flecs_isident(ptr[0]) || ((ptr != expr) && (ptr[-1] != ' '))) { - ecs_parser_error(name, expr, (ptr - expr), - "expected end of expression or next term"); - goto error; +#ifdef FLECS_SANITIZE + memory = ECS_OFFSET(memory, -ECS_SIZEOF(int64_t)); + if (*(int64_t*)memory != ba->chunk_size) { + if (type_name) { + ecs_err("chunk %p returned to wrong allocator " + "(chunk = %ub, allocator = %ub, type = %s)", + memory, *(int64_t*)memory, ba->chunk_size, type_name); + } else { + ecs_err("chunk %p returned to wrong allocator " + "(chunk = %ub, allocator = %ub)", + memory, *(int64_t*)memory, ba->chunk_size); } + ecs_abort(ECS_INTERNAL_ERROR, NULL); } - /* If the term just contained a 0, the expression has nothing. Ensure - * that after the 0 nothing else follows */ - if (term->first.name && !ecs_os_strcmp(term->first.name, "0")) { - if (ptr[0]) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected term after 0"); - goto error; - } + ba->alloc_count --; +#endif - if (src->flags != 0) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid combination of 0 with non-default subject"); - goto error; - } + ecs_block_allocator_chunk_header_t *chunk = memory; + chunk->next = ba->head; + ba->head = chunk; + ecs_assert(ba->alloc_count >= 0, ECS_INTERNAL_ERROR, + "corrupted allocator (size = %d)", ba->chunk_size); +#endif +} - src->flags = EcsIsEntity; - src->id = 0; - /* Safe, parser owns string */ - ecs_os_free(ECS_CONST_CAST(char*, term->first.name)); - term->first.name = NULL; +void* flecs_brealloc( + ecs_block_allocator_t *dst, + ecs_block_allocator_t *src, + void *memory) +{ + void *result; +#ifdef FLECS_USE_OS_ALLOC + (void)src; + result = ecs_os_realloc(memory, dst->data_size); +#else + if (dst == src) { + return memory; } - /* Cannot combine EcsIsEntity/0 with operators other than AND */ - if (term->oper != EcsAnd && ecs_term_match_0(term)) { - if (term->first.id != EcsScopeOpen && term->first.id != EcsScopeClose) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid operator for empty source"); - goto error; + result = flecs_balloc(dst); + if (result && src) { + ecs_size_t size = src->data_size; + if (dst->data_size < size) { + size = dst->data_size; } + ecs_os_memcpy(result, memory, size); } - - /* Automatically assign This if entity is not assigned and the set is - * nothing */ - if (!(src->flags & EcsIsEntity)) { - if (!src->name) { - if (!src->id) { - src->id = EcsThis; - src->flags |= EcsIsVariable; - } - } + flecs_bfree(src, memory); +#endif +#ifdef FLECS_MEMSET_UNINITIALIZED + if (dst && src && (dst->data_size > src->data_size)) { + ecs_os_memset(ECS_OFFSET(result, src->data_size), 0xAA, + dst->data_size - src->data_size); + } else if (dst && !src) { + ecs_os_memset(result, 0xAA, dst->data_size); } +#endif - if (src->name && !ecs_os_strcmp(src->name, "0")) { - src->id = 0; - src->flags = EcsIsEntity; - } + return result; +} - /* Process role */ - if (term->id_flags == ECS_AND) { - term->oper = EcsAndFrom; - term->id_flags = 0; - } else if (term->id_flags == ECS_OR) { - term->oper = EcsOrFrom; - term->id_flags = 0; - } else if (term->id_flags == ECS_NOT) { - term->oper = EcsNotFrom; - term->id_flags = 0; +void* flecs_bdup( + ecs_block_allocator_t *ba, + void *memory) +{ +#ifdef FLECS_USE_OS_ALLOC + if (memory && ba->chunk_size) { + return ecs_os_memdup(memory, ba->data_size); + } else { + return NULL; } - - ptr = ecs_parse_ws(ptr); - - return ECS_CONST_CAST(char*, ptr); -error: - if (term) { - ecs_term_fini(term); +#else + void *result = flecs_balloc(ba); + if (result) { + ecs_os_memcpy(result, memory, ba->data_size); } - return NULL; + return result; +#endif } -#endif +// This is free and unencumbered software released into the public domain under The Unlicense (http://unlicense.org/) +// main repo: https://github.com/wangyi-fudan/wyhash +// author: 王一 Wang Yi +// contributors: Reini Urban, Dietrich Epp, Joshua Haberman, Tommy Ettinger, +// Daniel Lemire, Otmar Ertl, cocowalla, leo-yuriev, +// Diego Barrios Romero, paulie-g, dumblob, Yann Collet, ivte-ms, +// hyb, James Z.M. Gao, easyaspi314 (Devin), TheOneric -/** - * @file addons/plecs.c - * @brief Plecs addon. - */ +/* quick example: + string s="fjsakfdsjkf"; + uint64_t hash=wyhash(s.c_str(), s.size(), 0, wyp_); +*/ -#ifdef FLECS_PLECS +#ifndef WYHASH_CONDOM +//protections that produce different results: +//1: normal valid behavior +//2: extra protection against entropy loss (probability=2^-63), aka. "blind multiplication" +#define WYHASH_CONDOM 1 +#endif -ECS_COMPONENT_DECLARE(EcsScript); +#ifndef WYHASH_32BIT_MUM +//0: normal version, slow on 32 bit systems +//1: faster on 32 bit systems but produces different results, incompatible with wy2u0k function +#define WYHASH_32BIT_MUM 0 +#endif -#include +//includes +#include +#include +#if defined(_MSC_VER) && defined(_M_X64) + #include + #pragma intrinsic(_umul128) +#endif -#define TOK_NEWLINE '\n' -#define TOK_USING "using" -#define TOK_MODULE "module" -#define TOK_WITH "with" -#define TOK_CONST "const" -#define TOK_PROP "prop" -#define TOK_ASSEMBLY "assembly" +//likely and unlikely macros +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) + #define likely_(x) __builtin_expect(x,1) + #define unlikely_(x) __builtin_expect(x,0) +#else + #define likely_(x) (x) + #define unlikely_(x) (x) +#endif -#define STACK_MAX_SIZE (64) +//128bit multiply function +static inline void wymum_(uint64_t *A, uint64_t *B){ +#if(WYHASH_32BIT_MUM) + uint64_t hh=(*A>>32)*(*B>>32), hl=(*A>>32)*(uint32_t)*B, lh=(uint32_t)*A*(*B>>32), ll=(uint64_t)(uint32_t)*A*(uint32_t)*B; + #if(WYHASH_CONDOM>1) + *A^=_wyrot(hl)^hh; *B^=_wyrot(lh)^ll; + #else + *A=_wyrot(hl)^hh; *B=_wyrot(lh)^ll; + #endif +#elif defined(__SIZEOF_INT128__) + __uint128_t r=*A; r*=*B; + #if(WYHASH_CONDOM>1) + *A^=(uint64_t)r; *B^=(uint64_t)(r>>64); + #else + *A=(uint64_t)r; *B=(uint64_t)(r>>64); + #endif +#elif defined(_MSC_VER) && defined(_M_X64) + #if(WYHASH_CONDOM>1) + uint64_t a, b; + a=_umul128(*A,*B,&b); + *A^=a; *B^=b; + #else + *A=_umul128(*A,*B,B); + #endif +#else + uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo; + uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t>32)+(rm1>>32)+c; + #if(WYHASH_CONDOM>1) + *A^=lo; *B^=hi; + #else + *A=lo; *B=hi; + #endif +#endif +} -typedef struct { - ecs_value_t value; - bool owned; -} plecs_with_value_t; +//multiply and xor mix function, aka MUM +static inline uint64_t wymix_(uint64_t A, uint64_t B){ wymum_(&A,&B); return A^B; } -typedef struct { - const char *name; - const char *code; - - ecs_entity_t last_predicate; - ecs_entity_t last_subject; - ecs_entity_t last_object; - - ecs_id_t last_assign_id; - ecs_entity_t assign_to; - - ecs_entity_t scope[STACK_MAX_SIZE]; - ecs_entity_t default_scope_type[STACK_MAX_SIZE]; - ecs_entity_t with[STACK_MAX_SIZE]; - ecs_entity_t using[STACK_MAX_SIZE]; - int32_t with_frames[STACK_MAX_SIZE]; - plecs_with_value_t with_value_frames[STACK_MAX_SIZE]; - int32_t using_frames[STACK_MAX_SIZE]; - int32_t sp; - int32_t with_frame; - int32_t using_frame; - ecs_entity_t global_with; - ecs_entity_t assembly; - const char *assembly_start, *assembly_stop; - - char *annot[STACK_MAX_SIZE]; - int32_t annot_count; - - ecs_vars_t vars; - char var_name[256]; - ecs_entity_t var_type; - - bool with_stmt; - bool scope_assign_stmt; - bool assign_stmt; - bool assembly_stmt; - bool assembly_instance; - bool isa_stmt; - bool decl_stmt; - bool decl_type; - bool var_stmt; - bool var_is_prop; - bool is_module; - - int32_t errors; -} plecs_state_t; - -static -int flecs_plecs_parse( - ecs_world_t *world, - const char *name, - const char *expr, - ecs_vars_t *vars, - ecs_entity_t script, - ecs_entity_t instance); +//endian macros +#ifndef WYHASH_LITTLE_ENDIAN + #if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + #define WYHASH_LITTLE_ENDIAN 1 + #elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + #define WYHASH_LITTLE_ENDIAN 0 + #else + #warning could not determine endianness! Falling back to little endian. + #define WYHASH_LITTLE_ENDIAN 1 + #endif +#endif -static void flecs_dtor_script(EcsScript *ptr) { - ecs_os_free(ptr->script); - ecs_vec_fini_t(NULL, &ptr->using_, ecs_entity_t); +//read functions +#if (WYHASH_LITTLE_ENDIAN) +static inline uint64_t wyr8_(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v;} +static inline uint64_t wyr4_(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v;} +#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) +static inline uint64_t wyr8_(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return __builtin_bswap64(v);} +static inline uint64_t wyr4_(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return __builtin_bswap32(v);} +#elif defined(_MSC_VER) +static inline uint64_t wyr8_(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return _byteswap_uint64(v);} +static inline uint64_t wyr4_(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return _byteswap_ulong(v);} +#else +static inline uint64_t wyr8_(const uint8_t *p) { + uint64_t v; memcpy(&v, p, 8); + return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >> 8) & 0xff000000)| ((v << 8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000)); +} +static inline uint64_t wyr4_(const uint8_t *p) { + uint32_t v; memcpy(&v, p, 4); + return (((v >> 24) & 0xff)| ((v >> 8) & 0xff00)| ((v << 8) & 0xff0000)| ((v << 24) & 0xff000000)); +} +#endif +static inline uint64_t wyr3_(const uint8_t *p, size_t k) { return (((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1];} - int i, count = ptr->prop_defaults.count; - ecs_value_t *values = ptr->prop_defaults.array; - for (i = 0; i < count; i ++) { - ecs_value_free(ptr->world, values[i].type, values[i].ptr); +//wyhash main function +static inline uint64_t wyhash(const void *key, size_t len, uint64_t seed, const uint64_t *secret){ + const uint8_t *p=(const uint8_t *)key; seed^=wymix_(seed^secret[0],secret[1]); uint64_t a, b; + if(likely_(len<=16)){ + if(likely_(len>=4)){ a=(wyr4_(p)<<32)|wyr4_(p+((len>>3)<<2)); b=(wyr4_(p+len-4)<<32)|wyr4_(p+len-4-((len>>3)<<2)); } + else if(likely_(len>0)){ a=wyr3_(p,len); b=0;} + else a=b=0; + } + else{ + size_t i=len; + if(unlikely_(i>48)){ + uint64_t see1=seed, see2=seed; + do{ + seed=wymix_(wyr8_(p)^secret[1],wyr8_(p+8)^seed); + see1=wymix_(wyr8_(p+16)^secret[2],wyr8_(p+24)^see1); + see2=wymix_(wyr8_(p+32)^secret[3],wyr8_(p+40)^see2); + p+=48; i-=48; + }while(likely_(i>48)); + seed^=see1^see2; } - - ecs_vec_fini_t(NULL, &ptr->prop_defaults, ecs_value_t); + while(unlikely_(i>16)){ seed=wymix_(wyr8_(p)^secret[1],wyr8_(p+8)^seed); i-=16; p+=16; } + a=wyr8_(p+i-16); b=wyr8_(p+i-8); + } + a^=secret[1]; b^=seed; wymum_(&a,&b); + return wymix_(a^secret[0]^len,b^secret[1]); } -static -ECS_MOVE(EcsScript, dst, src, { - flecs_dtor_script(dst); - dst->using_ = src->using_; - dst->prop_defaults = src->prop_defaults; - dst->script = src->script; - dst->world = src->world; - ecs_os_zeromem(&src->using_); - ecs_os_zeromem(&src->prop_defaults); - src->script = NULL; - src->world = NULL; -}) - -static -ECS_DTOR(EcsScript, ptr, { - flecs_dtor_script(ptr); -}) +//the default secret parameters +static const uint64_t wyp_[4] = {0xa0761d6478bd642full, 0xe7037ed1a0b428dbull, 0x8ebc6af09c88c6e3ull, 0x589965cc75374cc3ull}; -/* Assembly ctor to initialize with default property values */ -static -void flecs_assembly_ctor( - void *ptr, - int32_t count, - const ecs_type_info_t *ti) +uint64_t flecs_hash( + const void *data, + ecs_size_t length) { - ecs_world_t *world = ti->hooks.ctx; - ecs_entity_t assembly = ti->component; - const EcsStruct *st = ecs_get(world, assembly, EcsStruct); - - if (!st) { - ecs_err("assembly '%s' is not a struct, cannot construct", ti->name); - return; - } - - const EcsScript *script = ecs_get(world, assembly, EcsScript); - if (!script) { - ecs_err("assembly '%s' is not a script, cannot construct", ti->name); - return; - } + return wyhash(data, flecs_ito(size_t, length), 0, wyp_); +} - if (st->members.count != script->prop_defaults.count) { - ecs_err("number of props (%d) of assembly '%s' does not match members" - " (%d), cannot construct", script->prop_defaults.count, - ti->name, st->members.count); - return; - } +/** + * @file datastructures/hashmap.c + * @brief Hashmap data structure. + * + * The hashmap data structure is built on top of the map data structure. Where + * the map data structure can only work with 64bit key values, the hashmap can + * hash keys of any size, and handles collisions between hashes. + */ - const ecs_member_t *members = st->members.array; - int32_t i, m, member_count = st->members.count; - ecs_value_t *values = script->prop_defaults.array; - for (m = 0; m < member_count; m ++) { - const ecs_member_t *member = &members[m]; - ecs_value_t *value = &values[m]; - const ecs_type_info_t *mti = ecs_get_type_info(world, member->type); - if (!mti) { - ecs_err("failed to get type info for prop '%s' of assembly '%s'", - member->name, ti->name); - return; - } - for (i = 0; i < count; i ++) { - void *el = ECS_ELEM(ptr, ti->size, i); - ecs_value_copy_w_type_info(world, mti, - ECS_OFFSET(el, member->offset), value->ptr); +static +int32_t flecs_hashmap_find_key( + const ecs_hashmap_t *map, + ecs_vec_t *keys, + ecs_size_t key_size, + const void *key) +{ + int32_t i, count = ecs_vec_count(keys); + void *key_array = ecs_vec_first(keys); + for (i = 0; i < count; i ++) { + void *key_ptr = ECS_OFFSET(key_array, key_size * i); + if (map->compare(key_ptr, key) == 0) { + return i; } } + return -1; } -/* Assembly on_set handler to update contents for new property values */ -static -void flecs_assembly_on_set( - ecs_iter_t *it) +void flecs_hashmap_init_( + ecs_hashmap_t *map, + ecs_size_t key_size, + ecs_size_t value_size, + ecs_hash_value_action_t hash, + ecs_compare_action_t compare, + ecs_allocator_t *allocator) { - if (it->table->flags & EcsTableIsPrefab) { - /* Don't instantiate assemblies for prefabs */ - return; - } - - ecs_world_t *world = it->world; - ecs_entity_t assembly = ecs_field_id(it, 1); - const char *name = ecs_get_name(world, assembly); - ecs_record_t *r = ecs_record_find(world, assembly); - - const EcsComponent *ct = ecs_record_get(world, r, EcsComponent); - ecs_get(world, assembly, EcsComponent); - if (!ct) { - ecs_err("assembly '%s' is not a component", name); - return; - } + map->key_size = key_size; + map->value_size = value_size; + map->hash = hash; + map->compare = compare; + flecs_ballocator_init_t(&map->bucket_allocator, ecs_hm_bucket_t); + ecs_map_init(&map->impl, allocator); +} - const EcsStruct *st = ecs_record_get(world, r, EcsStruct); - if (!st) { - ecs_err("assembly '%s' is not a struct", name); - return; - } +void flecs_hashmap_fini( + ecs_hashmap_t *map) +{ + ecs_allocator_t *a = map->impl.allocator; + ecs_map_iter_t it = ecs_map_iter(&map->impl); - const EcsScript *script = ecs_record_get(world, r, EcsScript); - if (!script) { - ecs_err("assembly '%s' is missing a script", name); - return; + while (ecs_map_next(&it)) { + ecs_hm_bucket_t *bucket = ecs_map_ptr(&it); + ecs_vec_fini(a, &bucket->keys, map->key_size); + ecs_vec_fini(a, &bucket->values, map->value_size); +#ifdef FLECS_SANITIZE + flecs_bfree(&map->bucket_allocator, bucket); +#endif } - void *data = ecs_field_w_size(it, flecs_ito(size_t, ct->size), 1); + flecs_ballocator_fini(&map->bucket_allocator); + ecs_map_fini(&map->impl); +} - int32_t i, m; - for (i = 0; i < it->count; i ++) { - /* Create variables to hold assembly properties */ - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - /* Populate properties from assembly members */ - const ecs_member_t *members = st->members.array; - for (m = 0; m < st->members.count; m ++) { - const ecs_member_t *member = &members[m]; - - ecs_value_t v = {0}; /* Prevent allocating value */ - ecs_expr_var_t *var = ecs_vars_declare_w_value( - &vars, member->name, &v); - if (var == NULL) { - ecs_err("could not create prop '%s' for assembly '%s'", - member->name, name); - break; - } - - /* Assign assembly property from assembly instance */ - var->value.type = member->type; - var->value.ptr = ECS_OFFSET(data, member->offset); - var->owned = false; - } - - /* Populate $this variable with instance entity */ - ecs_entity_t instance = it->entities[i]; - ecs_value_t v = {0}; - ecs_expr_var_t *var = ecs_vars_declare_w_value( - &vars, "this", &v); - var->value.type = ecs_id(ecs_entity_t); - var->value.ptr = &instance; - var->owned = false; - - /* Update script with new code/properties */ - ecs_script_update(world, assembly, instance, script->script, &vars); - ecs_vars_fini(&vars); - - if (ecs_record_has_id(world, r, EcsFlatten)) { - ecs_flatten(it->real_world, ecs_childof(instance), NULL); - } +void flecs_hashmap_copy( + ecs_hashmap_t *dst, + const ecs_hashmap_t *src) +{ + ecs_assert(dst != src, ECS_INVALID_PARAMETER, NULL); - data = ECS_OFFSET(data, ct->size); - } -} + flecs_hashmap_init_(dst, src->key_size, src->value_size, src->hash, + src->compare, src->impl.allocator); + ecs_map_copy(&dst->impl, &src->impl); -/* Delete contents of assembly instance */ -static -void flecs_assembly_on_remove( - ecs_iter_t *it) -{ - int32_t i; - for (i = 0; i < it->count; i ++) { - ecs_entity_t instance = it->entities[i]; - ecs_script_clear(it->world, 0, instance); + ecs_allocator_t *a = dst->impl.allocator; + ecs_map_iter_t it = ecs_map_iter(&dst->impl); + while (ecs_map_next(&it)) { + ecs_hm_bucket_t **bucket_ptr = ecs_map_ref(&it, ecs_hm_bucket_t); + ecs_hm_bucket_t *src_bucket = bucket_ptr[0]; + ecs_hm_bucket_t *dst_bucket = flecs_balloc(&dst->bucket_allocator); + bucket_ptr[0] = dst_bucket; + dst_bucket->keys = ecs_vec_copy(a, &src_bucket->keys, dst->key_size); + dst_bucket->values = ecs_vec_copy(a, &src_bucket->values, dst->value_size); } } -/* Set default property values on assembly Script component */ -static -int flecs_assembly_init_defaults( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_entity_t assembly, - EcsScript *script, - plecs_state_t *state) +void* flecs_hashmap_get_( + const ecs_hashmap_t *map, + ecs_size_t key_size, + const void *key, + ecs_size_t value_size) { - const EcsStruct *st = ecs_get(world, assembly, EcsStruct); - int32_t i, count = st->members.count; - const ecs_member_t *members = st->members.array; - - ecs_vec_init_t(NULL, &script->prop_defaults, ecs_value_t, count); - - for (i = 0; i < count; i ++) { - const ecs_member_t *member = &members[i]; - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, member->name); - if (!var) { - char *assembly_name = ecs_get_fullpath(world, assembly); - ecs_parser_error(name, expr, ptr - expr, - "missing property '%s' for assembly '%s'", - member->name, assembly_name); - ecs_os_free(assembly_name); - return -1; - } + ecs_assert(map->key_size == key_size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(map->value_size == value_size, ECS_INVALID_PARAMETER, NULL); - if (member->type != var->value.type) { - char *assembly_name = ecs_get_fullpath(world, assembly); - ecs_parser_error(name, expr, ptr - expr, - "property '%s' for assembly '%s' has mismatching type", - member->name, assembly_name); - ecs_os_free(assembly_name); - return -1; - } + uint64_t hash = map->hash(key); + ecs_hm_bucket_t *bucket = ecs_map_get_deref(&map->impl, + ecs_hm_bucket_t, hash); + if (!bucket) { + return NULL; + } - ecs_value_t *pv = ecs_vec_append_t(NULL, - &script->prop_defaults, ecs_value_t); - pv->type = member->type; - pv->ptr = var->value.ptr; - var->owned = false; /* Transfer ownership */ + int32_t index = flecs_hashmap_find_key(map, &bucket->keys, key_size, key); + if (index == -1) { + return NULL; } - return 0; + return ecs_vec_get(&bucket->values, value_size, index); } -/* Create new assembly */ -static -int flecs_assembly_create( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_entity_t assembly, - char *script_code, - plecs_state_t *state) +flecs_hashmap_result_t flecs_hashmap_ensure_( + ecs_hashmap_t *map, + ecs_size_t key_size, + const void *key, + ecs_size_t value_size) { - const EcsStruct *st = ecs_get(world, assembly, EcsStruct); - if (!st || !st->members.count) { - char *assembly_name = ecs_get_fullpath(world, assembly); - ecs_parser_error(name, expr, ptr - expr, - "assembly '%s' has no properties", assembly_name); - ecs_os_free(assembly_name); - ecs_os_free(script_code); - return -1; - } - - ecs_add_id(world, assembly, EcsAlwaysOverride); - - EcsScript *script = ecs_get_mut(world, assembly, EcsScript); - flecs_dtor_script(script); - script->world = world; - script->script = script_code; - ecs_vec_reset_t(NULL, &script->using_, ecs_entity_t); - - ecs_entity_t scope = ecs_get_scope(world); - if (scope && (scope = ecs_get_target(world, scope, EcsChildOf, 0))) { - ecs_vec_append_t(NULL, &script->using_, ecs_entity_t)[0] = scope; - } + ecs_assert(map->key_size == key_size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(map->value_size == value_size, ECS_INVALID_PARAMETER, NULL); - int i, count = state->using_frame; - for (i = 0; i < count; i ++) { - ecs_vec_append_t(NULL, &script->using_, ecs_entity_t)[0] = - state->using[i]; + uint64_t hash = map->hash(key); + ecs_hm_bucket_t **r = ecs_map_ensure_ref(&map->impl, ecs_hm_bucket_t, hash); + ecs_hm_bucket_t *bucket = r[0]; + if (!bucket) { + bucket = r[0] = flecs_bcalloc(&map->bucket_allocator); } - if (flecs_assembly_init_defaults( - world, name, expr, ptr, assembly, script, state)) - { - return -1; + ecs_allocator_t *a = map->impl.allocator; + void *value_ptr, *key_ptr; + ecs_vec_t *keys = &bucket->keys; + ecs_vec_t *values = &bucket->values; + if (!keys->array) { + ecs_vec_init(a, &bucket->keys, key_size, 1); + ecs_vec_init(a, &bucket->values, value_size, 1); + keys = &bucket->keys; + values = &bucket->values; + key_ptr = ecs_vec_append(a, keys, key_size); + value_ptr = ecs_vec_append(a, values, value_size); + ecs_os_memcpy(key_ptr, key, key_size); + ecs_os_memset(value_ptr, 0, value_size); + } else { + int32_t index = flecs_hashmap_find_key(map, keys, key_size, key); + if (index == -1) { + key_ptr = ecs_vec_append(a, keys, key_size); + value_ptr = ecs_vec_append(a, values, value_size); + ecs_os_memcpy(key_ptr, key, key_size); + ecs_os_memset(value_ptr, 0, value_size); + } else { + key_ptr = ecs_vec_get(keys, key_size, index); + value_ptr = ecs_vec_get(values, value_size, index); + } } - ecs_modified(world, assembly, EcsScript); - - ecs_set_hooks_id(world, assembly, &(ecs_type_hooks_t) { - .ctor = flecs_assembly_ctor, - .on_set = flecs_assembly_on_set, - .on_remove = flecs_assembly_on_remove, - .ctx = world - }); - - return 0; + return (flecs_hashmap_result_t){ + .key = key_ptr, .value = value_ptr, .hash = hash + }; } -/* Parser */ - -static -bool plecs_is_newline_comment( - const char *ptr) +void flecs_hashmap_set_( + ecs_hashmap_t *map, + ecs_size_t key_size, + void *key, + ecs_size_t value_size, + const void *value) { - if (ptr[0] == '/' && ptr[1] == '/') { - return true; - } - return false; + void *value_ptr = flecs_hashmap_ensure_(map, key_size, key, value_size).value; + ecs_assert(value_ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_os_memcpy(value_ptr, value, value_size); } -static -const char* plecs_parse_fluff( - const char *ptr) +ecs_hm_bucket_t* flecs_hashmap_get_bucket( + const ecs_hashmap_t *map, + uint64_t hash) { - do { - /* Skip whitespaces before checking for a comment */ - ptr = ecs_parse_ws(ptr); - - /* Newline comment, skip until newline character */ - if (plecs_is_newline_comment(ptr)) { - ptr += 2; - - while (ptr[0] && ptr[0] != TOK_NEWLINE) { - ptr ++; - } - } - - /* If a newline character is found, skip it */ - if (ptr[0] == TOK_NEWLINE) { - ptr ++; - } + ecs_assert(map != NULL, ECS_INTERNAL_ERROR, NULL); + return ecs_map_get_deref(&map->impl, ecs_hm_bucket_t, hash); +} - } while (isspace(ptr[0]) || plecs_is_newline_comment(ptr)); +void flecs_hm_bucket_remove( + ecs_hashmap_t *map, + ecs_hm_bucket_t *bucket, + uint64_t hash, + int32_t index) +{ + ecs_vec_remove(&bucket->keys, map->key_size, index); + ecs_vec_remove(&bucket->values, map->value_size, index); - return ptr; + if (!ecs_vec_count(&bucket->keys)) { + ecs_allocator_t *a = map->impl.allocator; + ecs_vec_fini(a, &bucket->keys, map->key_size); + ecs_vec_fini(a, &bucket->values, map->value_size); + ecs_hm_bucket_t *b = ecs_map_remove_ptr(&map->impl, hash); + ecs_assert(bucket == b, ECS_INTERNAL_ERROR, NULL); (void)b; + flecs_bfree(&map->bucket_allocator, bucket); + } } -static -ecs_entity_t plecs_lookup( - const ecs_world_t *world, - const char *path, - plecs_state_t *state, - ecs_entity_t rel, - bool is_subject) +void flecs_hashmap_remove_w_hash_( + ecs_hashmap_t *map, + ecs_size_t key_size, + const void *key, + ecs_size_t value_size, + uint64_t hash) { - ecs_entity_t e = 0; + ecs_assert(map->key_size == key_size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(map->value_size == value_size, ECS_INVALID_PARAMETER, NULL); + (void)value_size; - if (!is_subject) { - ecs_entity_t oneof = 0; - if (rel) { - if (ecs_has_id(world, rel, EcsOneOf)) { - oneof = rel; - } else { - oneof = ecs_get_target(world, rel, EcsOneOf, 0); - } - if (oneof) { - return ecs_lookup_path_w_sep( - world, oneof, path, NULL, NULL, false); - } - } - int using_scope = state->using_frame - 1; - for (; using_scope >= 0; using_scope--) { - e = ecs_lookup_path_w_sep( - world, state->using[using_scope], path, NULL, NULL, false); - if (e) { - break; - } - } + ecs_hm_bucket_t *bucket = ecs_map_get_deref(&map->impl, + ecs_hm_bucket_t, hash); + if (!bucket) { + return; } - if (!e) { - e = ecs_lookup_path_w_sep(world, 0, path, NULL, NULL, !is_subject); + int32_t index = flecs_hashmap_find_key(map, &bucket->keys, key_size, key); + if (index == -1) { + return; } - return e; + flecs_hm_bucket_remove(map, bucket, hash, index); } -/* Lookup action used for deserializing entity refs in component values */ -static -ecs_entity_t plecs_lookup_action( - const ecs_world_t *world, - const char *path, - void *ctx) +void flecs_hashmap_remove_( + ecs_hashmap_t *map, + ecs_size_t key_size, + const void *key, + ecs_size_t value_size) { - return plecs_lookup(world, path, ctx, 0, false); + ecs_assert(map->key_size == key_size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(map->value_size == value_size, ECS_INVALID_PARAMETER, NULL); + + uint64_t hash = map->hash(key); + flecs_hashmap_remove_w_hash_(map, key_size, key, value_size, hash); } -static -void plecs_apply_with_frame( - ecs_world_t *world, - plecs_state_t *state, - ecs_entity_t e) +flecs_hashmap_iter_t flecs_hashmap_iter( + ecs_hashmap_t *map) { - int32_t i, frame_count = state->with_frames[state->sp]; - for (i = 0; i < frame_count; i ++) { - ecs_id_t id = state->with[i]; - plecs_with_value_t *v = &state->with_value_frames[i]; - if (v->value.type) { - void *ptr = ecs_get_mut_id(world, e, id); - ecs_value_copy(world, v->value.type, ptr, v->value.ptr); - ecs_modified_id(world, e, id); - } else { - ecs_add_id(world, e, id); - } - } + return (flecs_hashmap_iter_t){ + .it = ecs_map_iter(&map->impl) + }; } -static -ecs_entity_t plecs_ensure_entity( - ecs_world_t *world, - plecs_state_t *state, - const char *path, - ecs_entity_t rel, - bool is_subject) +void* flecs_hashmap_next_( + flecs_hashmap_iter_t *it, + ecs_size_t key_size, + void *key_out, + ecs_size_t value_size) { - if (!path) { - return 0; - } - - ecs_entity_t e = 0; - bool is_anonymous = !ecs_os_strcmp(path, "_"); - bool is_new = false; - if (is_anonymous) { - path = NULL; - e = ecs_new_id(world); - is_new = true; + int32_t index = ++ it->index; + ecs_hm_bucket_t *bucket = it->bucket; + while (!bucket || it->index >= ecs_vec_count(&bucket->keys)) { + ecs_map_next(&it->it); + bucket = it->bucket = ecs_map_ptr(&it->it); + if (!bucket) { + return NULL; + } + index = it->index = 0; } - if (!e) { - e = plecs_lookup(world, path, state, rel, is_subject); + if (key_out) { + *(void**)key_out = ecs_vec_get(&bucket->keys, key_size, index); } + + return ecs_vec_get(&bucket->values, value_size, index); +} - if (!e) { - is_new = true; - if (rel && flecs_get_oneof(world, rel)) { - /* If relationship has oneof and entity was not found, don't proceed - * with creating an entity as this can cause asserts later on */ - char *relstr = ecs_get_fullpath(world, rel); - ecs_parser_error(state->name, 0, 0, - "invalid identifier '%s' for relationship '%s'", path, relstr); - ecs_os_free(relstr); - return 0; - } - - ecs_entity_t prev_scope = 0; - ecs_entity_t prev_with = 0; - if (!is_subject) { - /* Don't apply scope/with for non-subject entities */ - prev_scope = ecs_set_scope(world, 0); - prev_with = ecs_set_with(world, 0); - } - - e = ecs_add_path(world, e, 0, path); - ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); - - if (prev_scope) { - ecs_set_scope(world, prev_scope); - } - if (prev_with) { - ecs_set_with(world, prev_with); - } - } else { - /* If entity exists, make sure it gets the right scope and with */ - if (is_subject) { - ecs_entity_t scope = ecs_get_scope(world); - if (scope) { - ecs_add_pair(world, e, EcsChildOf, scope); - } +/** + * @file datastructures/map.c + * @brief Map data structure. + * + * Map data structure for 64bit keys and dynamic payload size. + */ - ecs_entity_t with = ecs_get_with(world); - if (with) { - ecs_add_id(world, e, with); - } - } - } - if (is_new) { - if (state->assembly && !state->assembly_instance) { - ecs_add_id(world, e, EcsPrefab); - } +/* The ratio used to determine whether the map should flecs_map_rehash. If + * (element_count * ECS_LOAD_FACTOR) > bucket_count, bucket count is increased. */ +#define ECS_LOAD_FACTOR (12) +#define ECS_BUCKET_END(b, c) ECS_ELEM_T(b, ecs_bucket_t, c) - if (state->global_with) { - ecs_add_id(world, e, state->global_with); - } - } +static +uint8_t flecs_log2(uint32_t v) { + static const uint8_t log2table[32] = + {0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}; - return e; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return log2table[(uint32_t)(v * 0x07C4ACDDU) >> 27]; } +/* Get bucket count for number of elements */ static -ecs_entity_t plecs_ensure_term_id( - ecs_world_t *world, - plecs_state_t *state, - ecs_term_id_t *term_id, - const char *expr, - int64_t column, - ecs_entity_t pred, - bool is_subject) +int32_t flecs_map_get_bucket_count( + int32_t count) { - ecs_entity_t result = 0; - const char *name = term_id->name; - if (term_id->flags & EcsIsVariable) { - if (name != NULL) { - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, name); - if (!var) { - ecs_parser_error(name, expr, column, - "unresolved variable '%s'", name); - return 0; - } - if (var->value.type != ecs_id(ecs_entity_t)) { - ecs_parser_error(name, expr, column, - "variable '%s' is not an entity", name); - return 0; - } - result = *(ecs_entity_t*)var->value.ptr; - if (!result) { - ecs_parser_error(name, expr, column, - "variable '%s' is not initialized with valid entity", name); - return 0; - } - } else if (term_id->id) { - result = term_id->id; - } else { - ecs_parser_error(name, expr, column, "invalid variable in term"); - return 0; - } - } else { - result = plecs_ensure_entity(world, state, name, pred, is_subject); - } - return result; + return flecs_next_pow_of_2((int32_t)(count * ECS_LOAD_FACTOR * 0.1)); } +/* Get bucket shift amount for a given bucket count */ static -bool plecs_pred_is_subj( - ecs_term_t *term, - plecs_state_t *state) +uint8_t flecs_map_get_bucket_shift ( + int32_t bucket_count) { - if (term->src.name != NULL) { - return false; - } - if (term->second.name != NULL) { - return false; - } - if (ecs_term_match_0(term)) { - return false; - } - if (state->with_stmt) { - return false; - } - if (state->assign_stmt) { - return false; - } - if (state->isa_stmt) { - return false; - } - if (state->decl_type) { - return false; - } + return (uint8_t)(64u - flecs_log2((uint32_t)bucket_count)); +} - return true; +/* Get bucket index for provided map key */ +static +int32_t flecs_map_get_bucket_index( + uint16_t bucket_shift, + ecs_map_key_t key) +{ + ecs_assert(bucket_shift != 0, ECS_INTERNAL_ERROR, NULL); + return (int32_t)((11400714819323198485ull * key) >> bucket_shift); } -/* Set masks aren't useful in plecs, so translate them back to entity names */ +/* Get bucket for key */ static -const char* plecs_set_mask_to_name( - ecs_flags32_t flags) +ecs_bucket_t* flecs_map_get_bucket( + const ecs_map_t *map, + ecs_map_key_t key) { - flags &= EcsTraverseFlags; - if (flags == EcsSelf) { - return "self"; - } else if (flags == EcsUp) { - return "up"; - } else if (flags == EcsDown) { - return "down"; - } else if (flags == EcsCascade || flags == (EcsUp|EcsCascade)) { - return "cascade"; - } else if (flags == EcsParent) { - return "parent"; - } - return NULL; + ecs_assert(map != NULL, ECS_INVALID_PARAMETER, NULL); + int32_t bucket_id = flecs_map_get_bucket_index(map->bucket_shift, key); + ecs_assert(bucket_id < map->bucket_count, ECS_INTERNAL_ERROR, NULL); + return &map->buckets[bucket_id]; } +/* Add element to bucket */ static -char* plecs_trim_annot( - char *annot) +ecs_map_val_t* flecs_map_bucket_add( + ecs_block_allocator_t *allocator, + ecs_bucket_t *bucket, + ecs_map_key_t key) { - annot = ECS_CONST_CAST(char*, ecs_parse_ws(annot)); - int32_t len = ecs_os_strlen(annot) - 1; - while (isspace(annot[len]) && (len > 0)) { - annot[len] = '\0'; - len --; - } - return annot; + ecs_bucket_entry_t *new_entry = flecs_balloc(allocator); + new_entry->key = key; + new_entry->next = bucket->first; + bucket->first = new_entry; + return &new_entry->value; } +/* Remove element from bucket */ static -void plecs_apply_annotations( - ecs_world_t *world, - ecs_entity_t subj, - plecs_state_t *state) +ecs_map_val_t flecs_map_bucket_remove( + ecs_map_t *map, + ecs_bucket_t *bucket, + ecs_map_key_t key) { - (void)world; - (void)subj; - (void)state; -#ifdef FLECS_DOC - int32_t i = 0, count = state->annot_count; - for (i = 0; i < count; i ++) { - char *annot = state->annot[i]; - if (!ecs_os_strncmp(annot, "@brief ", 7)) { - annot = plecs_trim_annot(annot + 7); - ecs_doc_set_brief(world, subj, annot); - } else if (!ecs_os_strncmp(annot, "@link ", 6)) { - annot = plecs_trim_annot(annot + 6); - ecs_doc_set_link(world, subj, annot); - } else if (!ecs_os_strncmp(annot, "@name ", 6)) { - annot = plecs_trim_annot(annot + 6); - ecs_doc_set_name(world, subj, annot); - } else if (!ecs_os_strncmp(annot, "@color ", 7)) { - annot = plecs_trim_annot(annot + 7); - ecs_doc_set_color(world, subj, annot); + ecs_bucket_entry_t *entry; + for (entry = bucket->first; entry; entry = entry->next) { + if (entry->key == key) { + ecs_map_val_t value = entry->value; + ecs_bucket_entry_t **next_holder = &bucket->first; + while(*next_holder != entry) { + next_holder = &(*next_holder)->next; + } + *next_holder = entry->next; + flecs_bfree(map->entry_allocator, entry); + map->count --; + return value; } } -#else - ecs_warn("cannot apply annotations, doc addon is missing"); -#endif + + return 0; } +/* Free contents of bucket */ static -int plecs_create_term( - ecs_world_t *world, - ecs_term_t *term, - const char *name, - const char *expr, - int64_t column, - plecs_state_t *state) +void flecs_map_bucket_clear( + ecs_block_allocator_t *allocator, + ecs_bucket_t *bucket) { - state->last_subject = 0; - state->last_predicate = 0; - state->last_object = 0; - state->last_assign_id = 0; - - const char *subj_name = term->src.name; - if (!subj_name) { - subj_name = plecs_set_mask_to_name(term->src.flags); + ecs_bucket_entry_t *entry = bucket->first; + while(entry) { + ecs_bucket_entry_t *next = entry->next; + flecs_bfree(allocator, entry); + entry = next; } +} - if (!ecs_term_id_is_set(&term->first)) { - ecs_parser_error(name, expr, column, "missing term in expression"); - return -1; +/* Get payload pointer for key from bucket */ +static +ecs_map_val_t* flecs_map_bucket_get( + ecs_bucket_t *bucket, + ecs_map_key_t key) +{ + ecs_bucket_entry_t *entry; + for (entry = bucket->first; entry; entry = entry->next) { + if (entry->key == key) { + return &entry->value; + } } + return NULL; +} - if (state->assign_stmt && !ecs_term_match_this(term)) { - ecs_parser_error(name, expr, column, - "invalid statement in assign statement"); - return -1; +/* Grow number of buckets */ +static +void flecs_map_rehash( + ecs_map_t *map, + int32_t count) +{ + count = flecs_next_pow_of_2(count); + if (count < 2) { + count = 2; } + ecs_assert(count > map->bucket_count, ECS_INTERNAL_ERROR, NULL); + + int32_t old_count = map->bucket_count; + ecs_bucket_t *buckets = map->buckets, *b, *end = ECS_BUCKET_END(buckets, old_count); - bool pred_as_subj = plecs_pred_is_subj(term, state); - ecs_entity_t subj = 0, obj = 0, pred = plecs_ensure_term_id( - world, state, &term->first, expr, column, 0, pred_as_subj); - if (!pred) { - return -1; + if (map->allocator) { + map->buckets = flecs_calloc_n(map->allocator, ecs_bucket_t, count); + } else { + map->buckets = ecs_os_calloc_n(ecs_bucket_t, count); } + map->bucket_count = count; + map->bucket_shift = flecs_map_get_bucket_shift(count); - subj = plecs_ensure_entity(world, state, subj_name, pred, true); - - if (ecs_term_id_is_set(&term->second)) { - obj = plecs_ensure_term_id(world, state, &term->second, expr, column, - pred, !state->assign_stmt && !state->with_stmt); - if (!obj) { - return -1; + /* Remap old bucket entries to new buckets */ + for (b = buckets; b < end; b++) { + ecs_bucket_entry_t* entry; + for (entry = b->first; entry;) { + ecs_bucket_entry_t* next = entry->next; + int32_t bucket_index = flecs_map_get_bucket_index( + map->bucket_shift, entry->key); + ecs_bucket_t *bucket = &map->buckets[bucket_index]; + entry->next = bucket->first; + bucket->first = entry; + entry = next; } } - if (state->assign_stmt || state->isa_stmt) { - subj = state->assign_to; + if (map->allocator) { + flecs_free_n(map->allocator, ecs_bucket_t, old_count, buckets); + } else { + ecs_os_free(buckets); } +} - if (state->isa_stmt && obj) { - ecs_parser_error(name, expr, column, - "invalid object in inheritance statement"); - return -1; - } +void ecs_map_params_init( + ecs_map_params_t *params, + ecs_allocator_t *allocator) +{ + params->allocator = allocator; + flecs_ballocator_init_t(¶ms->entry_allocator, ecs_bucket_entry_t); +} - if (state->isa_stmt) { - pred = ecs_pair(EcsIsA, pred); - } +void ecs_map_params_fini( + ecs_map_params_t *params) +{ + flecs_ballocator_fini(¶ms->entry_allocator); +} - if (subj == EcsVariable) { - subj = pred; - } +void ecs_map_init_w_params( + ecs_map_t *result, + ecs_map_params_t *params) +{ + ecs_os_zeromem(result); - if (subj) { - ecs_id_t id; - if (!obj) { - id = term->id_flags | pred; - } else { - id = term->id_flags | ecs_pair(pred, obj); - state->last_object = obj; - } - state->last_assign_id = id; - state->last_predicate = pred; - state->last_subject = subj; - ecs_add_id(world, subj, id); + result->allocator = params->allocator; - pred_as_subj = false; + if (params->entry_allocator.chunk_size) { + result->entry_allocator = ¶ms->entry_allocator; + result->shared_allocator = true; } else { - if (!obj) { - /* If no subject or object were provided, use predicate as subj - * unless the expression explictly excluded the subject */ - if (pred_as_subj) { - state->last_subject = pred; - subj = pred; - } else { - state->last_predicate = pred; - pred_as_subj = false; - } - } else { - state->last_predicate = pred; - state->last_object = obj; - pred_as_subj = false; - } + result->entry_allocator = flecs_ballocator_new_t(ecs_bucket_entry_t); } - /* If this is a with clause (the list of entities between 'with' and scope - * open), add subject to the array of with frames */ - if (state->with_stmt) { - ecs_assert(pred != 0, ECS_INTERNAL_ERROR, NULL); - ecs_id_t id; - - if (obj) { - id = ecs_pair(pred, obj); - } else { - id = pred; - } + flecs_map_rehash(result, 0); +} - state->with[state->with_frame ++] = id; - } else { - if (subj && !state->scope_assign_stmt) { - plecs_apply_with_frame(world, state, subj); - } - } - - /* If an id was provided by itself, add default scope type to it */ - ecs_entity_t default_scope_type = state->default_scope_type[state->sp]; - if (pred_as_subj && default_scope_type) { - ecs_add_id(world, subj, default_scope_type); - } - - /* If annotations preceded the statement, append */ - if (!state->decl_type && state->annot_count) { - if (!subj) { - ecs_parser_error(name, expr, column, - "missing subject for annotations"); - return -1; - } - - plecs_apply_annotations(world, subj, state); +void ecs_map_init_w_params_if( + ecs_map_t *result, + ecs_map_params_t *params) +{ + if (!ecs_map_is_init(result)) { + ecs_map_init_w_params(result, params); } - - return 0; } -static -const char* plecs_parse_inherit_stmt( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +void ecs_map_init( + ecs_map_t *result, + ecs_allocator_t *allocator) { - if (state->isa_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "cannot nest inheritance"); - return NULL; - } - - if (!state->last_subject) { - ecs_parser_error(name, expr, ptr - expr, - "missing entity to assign inheritance to"); - return NULL; - } - - state->isa_stmt = true; - state->assign_to = state->last_subject; - - return ptr; + ecs_map_init_w_params(result, &(ecs_map_params_t) { + .allocator = allocator + }); } -static -const char* plecs_parse_assign_var_expr( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state, - ecs_expr_var_t *var) +void ecs_map_init_if( + ecs_map_t *result, + ecs_allocator_t *allocator) { - ecs_value_t value = {0}; + if (!ecs_map_is_init(result)) { + ecs_map_init(result, allocator); + } +} - if (state->last_assign_id) { - value.type = state->last_assign_id; - value.ptr = ecs_value_new(world, state->last_assign_id); - if (!var && state->assembly_instance) { - var = ecs_vars_lookup(&state->vars, state->var_name); - } +void ecs_map_fini( + ecs_map_t *map) +{ + if (!ecs_map_is_init(map)) { + return; } - ptr = ecs_parse_expr(world, ptr, &value, - &(ecs_parse_expr_desc_t){ - .name = name, - .expr = expr, - .lookup_action = plecs_lookup_action, - .lookup_ctx = state, - .vars = &state->vars - }); - if (!ptr) { - if (state->last_assign_id) { - ecs_value_free(world, value.type, value.ptr); + bool sanitize = false; +#ifdef FLECS_SANITIZE + sanitize = true; +#endif + + /* Free buckets in sanitized mode, so we can replace the allocator with + * regular malloc/free and use asan/valgrind to find memory errors. */ + ecs_allocator_t *a = map->allocator; + ecs_block_allocator_t *ea = map->entry_allocator; + if (map->shared_allocator || sanitize) { + ecs_bucket_t *bucket = map->buckets, *end = &bucket[map->bucket_count]; + while (bucket != end) { + flecs_map_bucket_clear(ea, bucket); + bucket ++; } - goto error; } - if (var) { - bool ignore = state->var_is_prop && state->assembly_instance; - if (!ignore) { - if (var->value.ptr) { - ecs_value_free(world, var->value.type, var->value.ptr); - var->value.ptr = value.ptr; - var->value.type = value.type; - } - } else { - ecs_value_free(world, value.type, value.ptr); - } + if (ea && !map->shared_allocator) { + flecs_ballocator_free(ea); + map->entry_allocator = NULL; + } + if (a) { + flecs_free_n(a, ecs_bucket_t, map->bucket_count, map->buckets); } else { - var = ecs_vars_declare_w_value( - &state->vars, state->var_name, &value); - if (!var) { - goto error; - } + ecs_os_free(map->buckets); } - state->var_is_prop = false; - return ptr; -error: - return NULL; + map->bucket_shift = 0; } -static -const char* plecs_parse_assign_expr( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state, - ecs_expr_var_t *var) +ecs_map_val_t* ecs_map_get( + const ecs_map_t *map, + ecs_map_key_t key) { - (void)world; - - if (state->var_stmt) { - return plecs_parse_assign_var_expr(world, name, expr, ptr, state, var); - } + return flecs_map_bucket_get(flecs_map_get_bucket(map, key), key); +} - if (!state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "unexpected value outside of assignment statement"); - return NULL; +void* ecs_map_get_deref_( + const ecs_map_t *map, + ecs_map_key_t key) +{ + ecs_map_val_t* ptr = flecs_map_bucket_get( + flecs_map_get_bucket(map, key), key); + if (ptr) { + return (void*)(uintptr_t)ptr[0]; } + return NULL; +} - ecs_id_t assign_id = state->last_assign_id; - if (!assign_id) { - ecs_parser_error(name, expr, ptr - expr, - "missing type for assignment statement"); - return NULL; +void ecs_map_insert( + ecs_map_t *map, + ecs_map_key_t key, + ecs_map_val_t value) +{ + ecs_assert(ecs_map_get(map, key) == NULL, ECS_INVALID_PARAMETER, NULL); + int32_t map_count = ++map->count; + int32_t tgt_bucket_count = flecs_map_get_bucket_count(map_count); + int32_t bucket_count = map->bucket_count; + if (tgt_bucket_count > bucket_count) { + flecs_map_rehash(map, tgt_bucket_count); } - ecs_entity_t assign_to = state->assign_to; - if (!assign_to) { - assign_to = state->last_subject; - } + ecs_bucket_t *bucket = flecs_map_get_bucket(map, key); + flecs_map_bucket_add(map->entry_allocator, bucket, key)[0] = value; +} - if (!assign_to) { - ecs_parser_error(name, expr, ptr - expr, - "missing entity to assign to"); - return NULL; - } +void* ecs_map_insert_alloc( + ecs_map_t *map, + ecs_size_t elem_size, + ecs_map_key_t key) +{ + void *elem = ecs_os_calloc(elem_size); + ecs_map_insert_ptr(map, key, (uintptr_t)elem); + return elem; +} - ecs_entity_t type = ecs_get_typeid(world, assign_id); - if (!type) { - char *id_str = ecs_id_str(world, assign_id); - ecs_parser_error(name, expr, ptr - expr, - "invalid assignment, '%s' is not a type", id_str); - ecs_os_free(id_str); - return NULL; +ecs_map_val_t* ecs_map_ensure( + ecs_map_t *map, + ecs_map_key_t key) +{ + ecs_bucket_t *bucket = flecs_map_get_bucket(map, key); + ecs_map_val_t *result = flecs_map_bucket_get(bucket, key); + if (result) { + return result; } - if (assign_to == EcsVariable) { - assign_to = type; + int32_t map_count = ++map->count; + int32_t tgt_bucket_count = flecs_map_get_bucket_count(map_count); + int32_t bucket_count = map->bucket_count; + if (tgt_bucket_count > bucket_count) { + flecs_map_rehash(map, tgt_bucket_count); + bucket = flecs_map_get_bucket(map, key); } - void *value_ptr = ecs_get_mut_id(world, assign_to, assign_id); + ecs_map_val_t* v = flecs_map_bucket_add(map->entry_allocator, bucket, key); + *v = 0; + return v; +} - ptr = ecs_parse_expr(world, ptr, &(ecs_value_t){type, value_ptr}, - &(ecs_parse_expr_desc_t){ - .name = name, - .expr = expr, - .lookup_action = plecs_lookup_action, - .lookup_ctx = state, - .vars = &state->vars - }); - if (!ptr) { - return NULL; +void* ecs_map_ensure_alloc( + ecs_map_t *map, + ecs_size_t elem_size, + ecs_map_key_t key) +{ + ecs_map_val_t *val = ecs_map_ensure(map, key); + if (!*val) { + void *elem = ecs_os_calloc(elem_size); + *val = (ecs_map_val_t)(uintptr_t)elem; + return elem; + } else { + return (void*)(uintptr_t)*val; } - - ecs_modified_id(world, assign_to, assign_id); - - return ptr; } -static -const char* plecs_parse_assign_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +ecs_map_val_t ecs_map_remove( + ecs_map_t *map, + ecs_map_key_t key) { - (void)world; + return flecs_map_bucket_remove(map, flecs_map_get_bucket(map, key), key); +} - state->isa_stmt = false; +void ecs_map_remove_free( + ecs_map_t *map, + ecs_map_key_t key) +{ + ecs_map_val_t val = ecs_map_remove(map, key); + if (val) { + ecs_os_free((void*)(uintptr_t)val); + } +} - /* Component scope (add components to entity) */ - if (!state->assign_to) { - if (!state->last_subject) { - ecs_parser_error(name, expr, ptr - expr, - "missing entity to assign to"); - return NULL; - } - state->assign_to = state->last_subject; +void ecs_map_clear( + ecs_map_t *map) +{ + ecs_assert(map != NULL, ECS_INVALID_PARAMETER, NULL); + int32_t i, count = map->bucket_count; + for (i = 0; i < count; i ++) { + flecs_map_bucket_clear(map->entry_allocator, &map->buckets[i]); + } + if (map->allocator) { + flecs_free_n(map->allocator, ecs_bucket_t, count, map->buckets); + } else { + ecs_os_free(map->buckets); } + map->buckets = NULL; + map->bucket_count = 0; + map->count = 0; + flecs_map_rehash(map, 2); +} - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid assign statement in assign statement"); - return NULL; +ecs_map_iter_t ecs_map_iter( + const ecs_map_t *map) +{ + if (ecs_map_is_init(map)) { + return (ecs_map_iter_t){ + .map = map, + .bucket = NULL, + .entry = NULL + }; + } else { + return (ecs_map_iter_t){ 0 }; } +} - state->assign_stmt = true; - - /* Assignment without a preceding component */ - if (ptr[0] == '{') { - ecs_entity_t type = 0; +bool ecs_map_next( + ecs_map_iter_t *iter) +{ + const ecs_map_t *map = iter->map; + ecs_bucket_t *end; + if (!map || (iter->bucket == (end = &map->buckets[map->bucket_count]))) { + return false; + } - /* If we're in a scope & last_subject is a type, assign to scope */ - if (ecs_get_scope(world) != 0) { - type = ecs_get_typeid(world, state->last_subject); - if (type != 0) { - type = state->last_subject; + ecs_bucket_entry_t *entry = NULL; + if (!iter->bucket) { + for (iter->bucket = map->buckets; + iter->bucket != end; + ++iter->bucket) + { + if (iter->bucket->first) { + entry = iter->bucket->first; + break; } } - - /* If type hasn't been set yet, check if scope has default type */ - if (!type && !state->scope_assign_stmt) { - type = state->default_scope_type[state->sp]; + if (iter->bucket == end) { + return false; } - - /* If no type has been found still, check if last with id is a type */ - if (!type && !state->scope_assign_stmt) { - int32_t with_frame_count = state->with_frames[state->sp]; - if (with_frame_count) { - type = state->with[with_frame_count - 1]; + } else if ((entry = iter->entry) == NULL) { + do { + ++iter->bucket; + if (iter->bucket == end) { + return false; } - } - - if (!type) { - ecs_parser_error(name, expr, ptr - expr, - "missing type for assignment"); - return NULL; - } - - state->last_assign_id = type; + } while(!iter->bucket->first); + entry = iter->bucket->first; } - return ptr; + ecs_assert(entry != NULL, ECS_INTERNAL_ERROR, NULL); + iter->entry = entry->next; + iter->res = &entry->key; + + return true; } -static -const char* plecs_parse_assign_with_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +void ecs_map_copy( + ecs_map_t *dst, + const ecs_map_t *src) { - int32_t with_frame = state->with_frame - 1; - if (with_frame < 0) { - ecs_parser_error(name, expr, ptr - expr, - "missing type in with value"); - return NULL; + if (ecs_map_is_init(dst)) { + ecs_assert(ecs_map_count(dst) == 0, ECS_INVALID_PARAMETER, NULL); + ecs_map_fini(dst); } - - ecs_id_t id = state->with[with_frame]; - ecs_id_record_t *idr = flecs_id_record_get(world, id); - const ecs_type_info_t *ti = idr ? idr->type_info : NULL; - if (!ti) { - char *typename = ecs_id_str(world, id); - ecs_parser_error(name, expr, ptr - expr, - "id '%s' in with value is not a type", typename); - ecs_os_free(typename); - return NULL; + + if (!ecs_map_is_init(src)) { + return; } - plecs_with_value_t *v = &state->with_value_frames[with_frame]; - v->value.type = ti->component; - v->value.ptr = ecs_value_new(world, ti->component); - v->owned = true; - if (!v->value.ptr) { - char *typename = ecs_id_str(world, id); - ecs_parser_error(name, expr, ptr - expr, - "failed to create value for '%s'", typename); - ecs_os_free(typename); - return NULL; - } + ecs_map_init(dst, src->allocator); - ptr = ecs_parse_expr(world, ptr, &v->value, - &(ecs_parse_expr_desc_t){ - .name = name, - .expr = expr, - .lookup_action = plecs_lookup_action, - .lookup_ctx = state, - .vars = &state->vars - }); - if (!ptr) { - return NULL; + ecs_map_iter_t it = ecs_map_iter(src); + while (ecs_map_next(&it)) { + ecs_map_insert(dst, ecs_map_key(&it), ecs_map_value(&it)); } - - return ptr; } -static -const char* plecs_parse_assign_with_var( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - ecs_assert(ptr[0] == '$', ECS_INTERNAL_ERROR, NULL); - ecs_assert(state->with_stmt, ECS_INTERNAL_ERROR, NULL); - - char var_name[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr; - ptr = ecs_parse_token(name, expr, ptr + 1, var_name, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "unresolved variable '%s'", var_name); - return NULL; - } - - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, var_name); - if (!var) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved variable '%s'", var_name); - return NULL; - } +/** + * @file datastructures/name_index.c + * @brief Data structure for resolving 64bit keys by string (name). + */ - int32_t with_frame = state->with_frame; - state->with[with_frame] = var->value.type; - state->with_value_frames[with_frame].value = var->value; - state->with_value_frames[with_frame].owned = false; - state->with_frame ++; - return ptr; +static +uint64_t flecs_name_index_hash( + const void *ptr) +{ + const ecs_hashed_string_t *str = ptr; + ecs_assert(str->hash != 0, ECS_INTERNAL_ERROR, NULL); + return str->hash; } static -const char* plecs_parse_var_as_component( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +int flecs_name_index_compare( + const void *ptr1, + const void *ptr2) { - ecs_assert(ptr[0] == '$', ECS_INTERNAL_ERROR, NULL); - ecs_assert(!state->var_stmt, ECS_INTERNAL_ERROR, NULL); - char var_name[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr; - ptr = ecs_parse_token(name, expr, ptr + 1, var_name, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "unresolved variable '%s'", var_name); - return NULL; - } - - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, var_name); - if (!var) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved variable '%s'", var_name); - return NULL; + const ecs_hashed_string_t *str1 = ptr1; + const ecs_hashed_string_t *str2 = ptr2; + ecs_size_t len1 = str1->length; + ecs_size_t len2 = str2->length; + if (len1 != len2) { + return (len1 > len2) - (len1 < len2); } - if (!state->assign_to) { - ecs_parser_error(name, expr, ptr - expr, - "missing lvalue for variable assignment '%s'", var_name); - return NULL; - } + return ecs_os_memcmp(str1->value, str2->value, len1); +} - /* Use type of variable as component */ - ecs_entity_t type = var->value.type; - ecs_entity_t assign_to = state->assign_to; - if (!assign_to) { - assign_to = state->last_subject; - } +void flecs_name_index_init( + ecs_hashmap_t *hm, + ecs_allocator_t *allocator) +{ + flecs_hashmap_init_(hm, + ECS_SIZEOF(ecs_hashed_string_t), ECS_SIZEOF(uint64_t), + flecs_name_index_hash, + flecs_name_index_compare, + allocator); +} - void *dst = ecs_get_mut_id(world, assign_to, type); - if (!dst) { - char *type_name = ecs_get_fullpath(world, type); - ecs_parser_error(name, expr, ptr - expr, - "failed to obtain component for type '%s' of variable '%s'", - type_name, var_name); - ecs_os_free(type_name); - return NULL; +void flecs_name_index_init_if( + ecs_hashmap_t *hm, + ecs_allocator_t *allocator) +{ + if (!hm->compare) { + flecs_name_index_init(hm, allocator); } +} - if (ecs_value_copy(world, type, dst, var->value.ptr)) { - char *type_name = ecs_get_fullpath(world, type); - ecs_parser_error(name, expr, ptr - expr, - "failed to copy value for variable '%s' of type '%s'", - var_name, type_name); - ecs_os_free(type_name); - return NULL; - } +bool flecs_name_index_is_init( + const ecs_hashmap_t *hm) +{ + return hm->compare != NULL; +} - ecs_modified_id(world, assign_to, type); +ecs_hashmap_t* flecs_name_index_new( + ecs_world_t *world, + ecs_allocator_t *allocator) +{ + ecs_hashmap_t *result = flecs_bcalloc(&world->allocators.hashmap); + flecs_name_index_init(result, allocator); + result->hashmap_allocator = &world->allocators.hashmap; + return result; +} - return ptr; +void flecs_name_index_fini( + ecs_hashmap_t *map) +{ + flecs_hashmap_fini(map); } -static -void plecs_push_using( - ecs_entity_t scope, - plecs_state_t *state) +void flecs_name_index_free( + ecs_hashmap_t *map) { - for (int i = 0; i < state->using_frame; i ++) { - if (state->using[i] == scope) { - return; - } + if (map) { + flecs_name_index_fini(map); + flecs_bfree(map->hashmap_allocator, map); } +} - state->using[state->using_frame ++] = scope; +ecs_hashmap_t* flecs_name_index_copy( + ecs_hashmap_t *map) +{ + ecs_hashmap_t *result = flecs_bcalloc(map->hashmap_allocator); + result->hashmap_allocator = map->hashmap_allocator; + flecs_hashmap_copy(result, map); + return result; } -static -const char* plecs_parse_using_stmt( - ecs_world_t *world, +ecs_hashed_string_t flecs_get_hashed_string( const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) + ecs_size_t length, + uint64_t hash) { - if (state->isa_stmt || state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid usage of using keyword"); - return NULL; + if (!length) { + length = ecs_os_strlen(name); + } else { + ecs_assert(length == ecs_os_strlen(name), ECS_INTERNAL_ERROR, NULL); } - char using_path[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr + 1; - ptr = ecs_parse_token(name, expr, ptr + 5, using_path, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "expected identifier for using statement"); - return NULL; + if (!hash) { + hash = flecs_hash(name, length); + } else { + ecs_assert(hash == flecs_hash(name, length), ECS_INTERNAL_ERROR, NULL); } - ecs_size_t len = ecs_os_strlen(using_path); - if (!len) { - ecs_parser_error(name, expr, tmp - expr, - "missing identifier for using statement"); + return (ecs_hashed_string_t) { + .value = ECS_CONST_CAST(char*, name), + .length = length, + .hash = hash + }; +} + +const uint64_t* flecs_name_index_find_ptr( + const ecs_hashmap_t *map, + const char *name, + ecs_size_t length, + uint64_t hash) +{ + ecs_hashed_string_t hs = flecs_get_hashed_string(name, length, hash); + ecs_hm_bucket_t *b = flecs_hashmap_get_bucket(map, hs.hash); + if (!b) { return NULL; } - /* Lookahead as * is not matched by parse_token */ - if (ptr[0] == '*') { - using_path[len] = '*'; - using_path[len + 1] = '\0'; - len ++; - ptr ++; - } + ecs_hashed_string_t *keys = ecs_vec_first(&b->keys); + int32_t i, count = ecs_vec_count(&b->keys); - ecs_entity_t scope; - if (len > 2 && !ecs_os_strcmp(&using_path[len - 2], ".*")) { - using_path[len - 2] = '\0'; - scope = ecs_lookup_fullpath(world, using_path); - if (!scope) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved identifier '%s' in using statement", using_path); - return NULL; - } + for (i = 0; i < count; i ++) { + ecs_hashed_string_t *key = &keys[i]; + ecs_assert(key->hash == hs.hash, ECS_INTERNAL_ERROR, NULL); - /* Add each child of the scope to using stack */ - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ - .id = ecs_childof(scope) }); - while (ecs_term_next(&it)) { - int32_t i, count = it.count; - for (i = 0; i < count; i ++) { - plecs_push_using(it.entities[i], state); - } - } - } else { - scope = plecs_ensure_entity(world, state, using_path, 0, false); - if (!scope) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved identifier '%s' in using statement", using_path); - return NULL; + if (hs.length != key->length) { + continue; } - plecs_push_using(scope, state); + if (!ecs_os_strcmp(name, key->value)) { + uint64_t *e = ecs_vec_get_t(&b->values, uint64_t, i); + ecs_assert(e != NULL, ECS_INTERNAL_ERROR, NULL); + return e; + } } - state->using_frames[state->sp] = state->using_frame; - return ptr; + return NULL; } -static -const char* plecs_parse_module_stmt( - ecs_world_t *world, +uint64_t flecs_name_index_find( + const ecs_hashmap_t *map, const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) + ecs_size_t length, + uint64_t hash) { - const char *expr_start = ecs_parse_ws_eol(expr); - if (expr_start != ptr) { - ecs_parser_error(name, expr, ptr - expr, - "module must be first statement of script"); - return NULL; - } - - char module_path[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr + 1; - ptr = ecs_parse_token(name, expr, ptr + 6, module_path, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "expected identifier for module statement"); - return NULL; - } - - ecs_component_desc_t desc = {0}; - desc.entity = ecs_entity(world, { .name = module_path }); - ecs_entity_t module = ecs_module_init(world, NULL, &desc); - if (!module) { - return NULL; + const uint64_t *id = flecs_name_index_find_ptr(map, name, length, hash); + if (id) { + return id[0]; } - - state->is_module = true; - state->sp ++; - state->scope[state->sp] = module; - ecs_set_scope(world, module); - return ptr; + return 0; } -static -const char* plecs_parse_with_stmt( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +void flecs_name_index_remove( + ecs_hashmap_t *map, + uint64_t e, + uint64_t hash) { - if (state->isa_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid with after inheritance"); - return NULL; + ecs_hm_bucket_t *b = flecs_hashmap_get_bucket(map, hash); + if (!b) { + return; } - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid with in assign_stmt"); - return NULL; + uint64_t *ids = ecs_vec_first(&b->values); + int32_t i, count = ecs_vec_count(&b->values); + for (i = 0; i < count; i ++) { + if (ids[i] == e) { + flecs_hm_bucket_remove(map, b, hash, i); + break; + } } - - /* Add following expressions to with list */ - state->with_stmt = true; - return ptr + 5; } -static -const char* plecs_parse_assembly_stmt( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +void flecs_name_index_update_name( + ecs_hashmap_t *map, + uint64_t e, + uint64_t hash, + const char *name) { - if (state->isa_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid with after inheritance"); - return NULL; + ecs_hm_bucket_t *b = flecs_hashmap_get_bucket(map, hash); + if (!b) { + return; } - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid with in assign_stmt"); - return NULL; + uint64_t *ids = ecs_vec_first(&b->values); + int32_t i, count = ecs_vec_count(&b->values); + for (i = 0; i < count; i ++) { + if (ids[i] == e) { + ecs_hashed_string_t *key = ecs_vec_get_t( + &b->keys, ecs_hashed_string_t, i); + key->value = ECS_CONST_CAST(char*, name); + ecs_assert(ecs_os_strlen(name) == key->length, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_hash(name, key->length) == key->hash, + ECS_INTERNAL_ERROR, NULL); + return; + } } - state->assembly_stmt = true; - - return ptr + 9; + /* Record must already have been in the index */ + ecs_abort(ECS_INTERNAL_ERROR, NULL); } -static -const char* plecs_parse_var_type( - ecs_world_t *world, +void flecs_name_index_ensure( + ecs_hashmap_t *map, + uint64_t id, const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state, - ecs_entity_t *type_out) + ecs_size_t length, + uint64_t hash) { - char prop_type_name[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr + 1; - ptr = ecs_parse_token(name, expr, ptr + 1, prop_type_name, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "expected type for prop declaration"); - return NULL; - } + ecs_check(name != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_entity_t prop_type = plecs_lookup(world, prop_type_name, state, 0, false); - if (!prop_type) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved property type '%s'", prop_type_name); - return NULL; + ecs_hashed_string_t key = flecs_get_hashed_string(name, length, hash); + + uint64_t existing = flecs_name_index_find( + map, name, key.length, key.hash); + if (existing) { + if (existing != id) { + ecs_abort(ECS_ALREADY_DEFINED, + "conflicting id registered with name '%s'", name); + } } - *type_out = prop_type; - - return ptr; + flecs_hashmap_result_t hmr = flecs_hashmap_ensure( + map, &key, uint64_t); + *((uint64_t*)hmr.value) = id; +error: + return; } -static -const char* plecs_parse_const_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - ptr = ecs_parse_token(name, expr, ptr + 5, state->var_name, 0); - if (!ptr) { - return NULL; - } - - ptr = ecs_parse_ws(ptr); - - if (ptr[0] == ':') { - ptr = plecs_parse_var_type( - world, name, expr, ptr, state, &state->last_assign_id); - if (!ptr) { - return NULL; - } +/** + * @file datastructures/sparse.c + * @brief Sparse set data structure. + */ - ptr = ecs_parse_ws(ptr); - } - if (ptr[0] != '=') { - ecs_parser_error(name, expr, ptr - expr, - "expected '=' after const declaration"); - return NULL; - } +/* Utility to get a pointer to the payload */ +#define DATA(array, size, offset) (ECS_OFFSET(array, size * offset)) - state->var_stmt = true; - return ptr + 1; -} +typedef struct ecs_page_t { + int32_t *sparse; /* Sparse array with indices to dense array */ + void *data; /* Store data in sparse array to reduce + * indirection and provide stable pointers. */ +} ecs_page_t; static -const char* plecs_parse_prop_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +ecs_page_t* flecs_sparse_page_new( + ecs_sparse_t *sparse, + int32_t page_index) { - char prop_name[ECS_MAX_TOKEN_SIZE]; - ptr = ecs_parse_token(name, expr, ptr + 5, prop_name, 0); - if (!ptr) { - return NULL; - } - - ptr = ecs_parse_ws(ptr); - - if (ptr[0] != ':') { - ecs_parser_error(name, expr, ptr - expr, - "expected ':' after prop declaration"); - return NULL; - } - - ecs_entity_t prop_type; - ptr = plecs_parse_var_type(world, name, expr, ptr, state, &prop_type); - if (!ptr) { - return NULL; - } + ecs_allocator_t *a = sparse->allocator; + ecs_block_allocator_t *ca = sparse->page_allocator; + int32_t count = ecs_vec_count(&sparse->pages); + ecs_page_t *pages; - ecs_entity_t assembly = state->assembly; - if (!assembly) { - ecs_parser_error(name, expr, ptr - expr, - "unexpected prop '%s' outside of assembly", prop_name); - return NULL; + if (count <= page_index) { + ecs_vec_set_count_t(a, &sparse->pages, ecs_page_t, page_index + 1); + pages = ecs_vec_first_t(&sparse->pages, ecs_page_t); + ecs_os_memset_n(&pages[count], 0, ecs_page_t, (1 + page_index - count)); + } else { + pages = ecs_vec_first_t(&sparse->pages, ecs_page_t); } - if (!state->assembly_instance) { - ecs_entity_t prop_member = ecs_entity(world, { - .name = prop_name, - .add = { ecs_childof(assembly) } - }); + ecs_assert(pages != NULL, ECS_INTERNAL_ERROR, NULL); - if (!prop_member) { - return NULL; - } + ecs_page_t *result = &pages[page_index]; + ecs_assert(result->sparse == NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(result->data == NULL, ECS_INTERNAL_ERROR, NULL); - ecs_set(world, prop_member, EcsMember, { - .type = prop_type - }); - } + /* Initialize sparse array with zero's, as zero is used to indicate that the + * sparse element has not been paired with a dense element. Use zero + * as this means we can take advantage of calloc having a possibly better + * performance than malloc + memset. */ + result->sparse = ca ? flecs_bcalloc(ca) + : ecs_os_calloc_n(int32_t, FLECS_SPARSE_PAGE_SIZE); - if (ptr[0] != '=') { - ecs_parser_error(name, expr, ptr - expr, - "expected '=' after prop type"); - return NULL; - } + /* Initialize the data array with zero's to guarantee that data is + * always initialized. When an entry is removed, data is reset back to + * zero. Initialize now, as this can take advantage of calloc. */ + result->data = a ? flecs_calloc(a, sparse->size * FLECS_SPARSE_PAGE_SIZE) + : ecs_os_calloc(sparse->size * FLECS_SPARSE_PAGE_SIZE); - ecs_os_strcpy(state->var_name, prop_name); - state->last_assign_id = prop_type; - state->var_stmt = true; - state->var_is_prop = true; + ecs_assert(result->sparse != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(result->data != NULL, ECS_INTERNAL_ERROR, NULL); - return plecs_parse_fluff(ptr + 1); + return result; } static -const char* plecs_parse_scope_open( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +void flecs_sparse_page_free( + ecs_sparse_t *sparse, + ecs_page_t *page) { - state->isa_stmt = false; + ecs_allocator_t *a = sparse->allocator; + ecs_block_allocator_t *ca = sparse->page_allocator; - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid scope in assign_stmt"); - return NULL; + if (ca) { + flecs_bfree(ca, page->sparse); + } else { + ecs_os_free(page->sparse); } - - state->sp ++; - - ecs_entity_t scope = 0; - ecs_entity_t default_scope_type = 0; - bool assembly_stmt = false; - - if (!state->with_stmt) { - if (state->last_subject) { - scope = state->last_subject; - ecs_set_scope(world, state->last_subject); - - /* Check if scope has a default child component */ - ecs_entity_t def_type_src = ecs_get_target_for_id(world, scope, - 0, ecs_pair(EcsDefaultChildComponent, EcsWildcard)); - - if (def_type_src) { - default_scope_type = ecs_get_target( - world, def_type_src, EcsDefaultChildComponent, 0); - } - } else { - if (state->last_object) { - scope = ecs_pair( - state->last_predicate, state->last_object); - ecs_set_with(world, scope); - } else { - if (state->last_predicate) { - scope = ecs_pair(EcsChildOf, state->last_predicate); - } - ecs_set_scope(world, state->last_predicate); - } - } - - state->scope[state->sp] = scope; - state->default_scope_type[state->sp] = default_scope_type; - - if (state->assembly_stmt) { - assembly_stmt = true; - if (state->assembly) { - ecs_parser_error(name, expr, ptr - expr, - "invalid nested assembly"); - return NULL; - } - state->assembly = scope; - state->assembly_stmt = false; - state->assembly_start = ptr; - } + if (a) { + flecs_free(a, sparse->size * FLECS_SPARSE_PAGE_SIZE, page->data); } else { - state->scope[state->sp] = state->scope[state->sp - 1]; - state->default_scope_type[state->sp] = - state->default_scope_type[state->sp - 1]; - state->assign_to = 0; + ecs_os_free(page->data); } +} - state->using_frames[state->sp] = state->using_frame; - state->with_frames[state->sp] = state->with_frame; - state->with_stmt = false; - - ecs_vars_push(&state->vars); - - /* Declare variable to hold assembly instance during instantiation */ - if (assembly_stmt) { - ecs_value_t val = {0}; - ecs_expr_var_t *var = ecs_vars_declare_w_value( - &state->vars, "this", &val); - var->value.ptr = ECS_CONST_CAST(void*, &EcsThis); /* Dummy value */ - var->value.type = ecs_id(ecs_entity_t); - var->owned = false; +static +ecs_page_t* flecs_sparse_get_page( + const ecs_sparse_t *sparse, + int32_t page_index) +{ + ecs_assert(page_index >= 0, ECS_INVALID_PARAMETER, NULL); + if (page_index >= ecs_vec_count(&sparse->pages)) { + return NULL; } - - return ptr; + return ecs_vec_get_t(&sparse->pages, ecs_page_t, page_index); } static -void plecs_free_with_frame( - ecs_world_t *world, - plecs_state_t *state) +ecs_page_t* flecs_sparse_get_or_create_page( + ecs_sparse_t *sparse, + int32_t page_index) { - int32_t i, prev_with = state->with_frames[state->sp]; - for (i = prev_with; i < state->with_frame; i ++) { - plecs_with_value_t *v = &state->with_value_frames[i]; - if (!v->owned) { - continue; - } - if (v->value.type) { - ecs_value_free(world, v->value.type, v->value.ptr); - v->value.type = 0; - v->value.ptr = NULL; - v->owned = false; - } + ecs_page_t *page = flecs_sparse_get_page(sparse, page_index); + if (page && page->sparse) { + return page; } + + return flecs_sparse_page_new(sparse, page_index); } static -void plecs_free_all_with_frames( - ecs_world_t *world, - plecs_state_t *state) +void flecs_sparse_grow_dense( + ecs_sparse_t *sparse) { - int32_t i; - for (i = state->sp - 1; i >= 0; i --) { - state->sp = i; - plecs_free_with_frame(world, state); - } + ecs_vec_append_t(sparse->allocator, &sparse->dense, uint64_t); } static -const char* plecs_parse_scope_close( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +uint64_t flecs_sparse_strip_generation( + uint64_t *index_out) { - if (state->isa_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid '}' after inheritance statement"); - return NULL; - } - - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "unfinished assignment before }"); - return NULL; - } - - ecs_entity_t cur = state->scope[state->sp], assembly = state->assembly; - if (state->sp && (cur == state->scope[state->sp - 1])) { - /* Previous scope is also from the assembly, not found the end yet */ - cur = 0; - } - if (cur && cur == assembly) { - ecs_size_t assembly_len = flecs_ito(ecs_size_t, ptr - state->assembly_start); - if (assembly_len) { - assembly_len --; - char *script = ecs_os_malloc_n(char, assembly_len + 1); - ecs_os_memcpy(script, state->assembly_start, assembly_len); - script[assembly_len] = '\0'; - state->assembly = 0; - state->assembly_start = NULL; - if (flecs_assembly_create(world, name, expr, ptr, assembly, script, state)) { - return NULL; - } - } else { - ecs_parser_error(name, expr, ptr - expr, "empty assembly"); - return NULL; - } - } - - state->scope[state->sp] = 0; - state->default_scope_type[state->sp] = 0; - state->sp --; - - if (state->sp < 0) { - ecs_parser_error(name, expr, ptr - expr, "invalid } without a {"); - return NULL; - } - - ecs_id_t id = state->scope[state->sp]; - if (!id || ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_set_with(world, id); - } - - if (!id || !ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_set_scope(world, id); - } - - plecs_free_with_frame(world, state); - - state->with_frame = state->with_frames[state->sp]; - state->using_frame = state->using_frames[state->sp]; - state->last_subject = 0; - state->assign_stmt = false; - - ecs_vars_pop(&state->vars); - - return plecs_parse_fluff(ptr + 1); + uint64_t index = *index_out; + uint64_t gen = index & ECS_GENERATION_MASK; + /* Make sure there's no junk in the id */ + ecs_assert(gen == (index & (0xFFFFFFFFull << 32)), + ECS_INVALID_PARAMETER, NULL); + *index_out -= gen; + return gen; } static -const char *plecs_parse_plecs_term( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +void flecs_sparse_assign_index( + ecs_page_t * page, + uint64_t * dense_array, + uint64_t index, + int32_t dense) { - ecs_term_t term = {0}; - ecs_entity_t decl_id = 0; - if (state->decl_stmt) { - decl_id = state->last_predicate; - } - - ptr = ecs_parse_term(world, name, expr, ptr, &term, NULL); - if (!ptr) { - return NULL; - } - - if (flecs_isident(ptr[0])) { - state->decl_type = true; - } - - if (!ecs_term_is_initialized(&term)) { - ecs_parser_error(name, expr, ptr - expr, "expected identifier"); - return NULL; /* No term found */ - } - - if (plecs_create_term(world, &term, name, expr, (ptr - expr), state)) { - ecs_term_fini(&term); - return NULL; /* Failed to create term */ - } - - if (decl_id && state->last_subject) { - ecs_add_id(world, state->last_subject, decl_id); - } - - state->decl_type = false; - - ecs_term_fini(&term); - - return ptr; + /* Initialize sparse-dense pair. This assigns the dense index to the sparse + * array, and the sparse index to the dense array .*/ + page->sparse[FLECS_SPARSE_OFFSET(index)] = dense; + dense_array[dense] = index; } static -const char* plecs_parse_annotation( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +uint64_t flecs_sparse_inc_gen( + uint64_t index) { - do { - if(state->annot_count >= STACK_MAX_SIZE) { - ecs_parser_error(name, expr, ptr - expr, - "max number of annotations reached"); - return NULL; - } - - char ch; - const char *start = ptr; - for (; (ch = *ptr) && ch != '\n'; ptr ++) { } - - int32_t len = (int32_t)(ptr - start); - char *annot = ecs_os_malloc_n(char, len + 1); - ecs_os_memcpy_n(annot, start, char, len); - annot[len] = '\0'; - - state->annot[state->annot_count] = annot; - state->annot_count ++; - - ptr = plecs_parse_fluff(ptr); - } while (ptr[0] == '@'); + /* When an index is deleted, its generation is increased so that we can do + * liveliness checking while recycling ids */ + return ECS_GENERATION_INC(index); +} - return ptr; +static +uint64_t flecs_sparse_inc_id( + ecs_sparse_t *sparse) +{ + /* Generate a new id. The last issued id could be stored in an external + * variable, such as is the case with the last issued entity id, which is + * stored on the world. */ + return ++ sparse->max_id; } static -void plecs_clear_annotations( - plecs_state_t *state) +uint64_t flecs_sparse_get_id( + const ecs_sparse_t *sparse) { - int32_t i, count = state->annot_count; - for (i = 0; i < count; i ++) { - ecs_os_free(state->annot[i]); - } - state->annot_count = 0; + ecs_assert(sparse != NULL, ECS_INTERNAL_ERROR, NULL); + return sparse->max_id; } static -const char* plecs_parse_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) +void flecs_sparse_set_id( + ecs_sparse_t *sparse, + uint64_t value) { - state->assign_stmt = false; - state->scope_assign_stmt = false; - state->isa_stmt = false; - state->with_stmt = false; - state->decl_stmt = false; - state->var_stmt = false; - state->last_subject = 0; - state->last_predicate = 0; - state->last_object = 0; - state->assign_to = 0; - state->last_assign_id = 0; + /* Sometimes the max id needs to be assigned directly, which typically + * happens when the API calls get_or_create for an id that hasn't been + * issued before. */ + sparse->max_id = value; +} - plecs_clear_annotations(state); +/* Pair dense id with new sparse id */ +static +uint64_t flecs_sparse_create_id( + ecs_sparse_t *sparse, + int32_t dense) +{ + uint64_t index = flecs_sparse_inc_id(sparse); + flecs_sparse_grow_dense(sparse); - ptr = plecs_parse_fluff(ptr); + ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(index)); + ecs_assert(page->sparse[FLECS_SPARSE_OFFSET(index)] == 0, ECS_INTERNAL_ERROR, NULL); + + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + flecs_sparse_assign_index(page, dense_array, index, dense); + + return index; +} - char ch = ptr[0]; +/* Create new id */ +static +uint64_t flecs_sparse_new_index( + ecs_sparse_t *sparse) +{ + int32_t dense_count = ecs_vec_count(&sparse->dense); + int32_t count = sparse->count ++; - if (!ch) { - goto done; - } else if (ch == '{') { - ptr = plecs_parse_fluff(ptr + 1); - goto scope_open; - } else if (ch == '}') { - goto scope_close; - } else if (ch == '-') { - ptr = plecs_parse_fluff(ptr + 1); - state->assign_to = ecs_get_scope(world); - state->scope_assign_stmt = true; - goto assign_stmt; - } else if (ch == '@') { - ptr = plecs_parse_annotation(name, expr, ptr, state); - if (!ptr) goto error; - goto term_expr; - } else if (!ecs_os_strncmp(ptr, TOK_USING " ", 5)) { - ptr = plecs_parse_using_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; - goto done; - } else if (!ecs_os_strncmp(ptr, TOK_MODULE " ", 6)) { - ptr = plecs_parse_module_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; - goto done; - } else if (!ecs_os_strncmp(ptr, TOK_WITH " ", 5)) { - ptr = plecs_parse_with_stmt(name, expr, ptr, state); - if (!ptr) goto error; - goto term_expr; - } else if (!ecs_os_strncmp(ptr, TOK_CONST " ", 6)) { - ptr = plecs_parse_const_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; - goto assign_expr; - } else if (!ecs_os_strncmp(ptr, TOK_ASSEMBLY " ", 9)) { - ptr = plecs_parse_assembly_stmt(name, expr, ptr, state); - if (!ptr) goto error; - goto decl_stmt; - } else if (!ecs_os_strncmp(ptr, TOK_PROP " ", 5)) { - ptr = plecs_parse_prop_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; - goto assign_expr; + ecs_assert(count <= dense_count, ECS_INTERNAL_ERROR, NULL); + if (count < dense_count) { + /* If there are unused elements in the dense array, return first */ + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + return dense_array[count]; } else { - goto term_expr; - } - -term_expr: - if (!ptr[0]) { - goto done; - } - - if (ptr[0] == '$' && !isspace(ptr[1])) { - if (state->with_stmt) { - ptr = plecs_parse_assign_with_var(name, expr, ptr, state); - if (!ptr) { - return NULL; - } - } else if (!state->var_stmt) { - goto assign_var_as_component; - } - } else if (!(ptr = plecs_parse_plecs_term(world, name, ptr, ptr, state))) { - goto error; + return flecs_sparse_create_id(sparse, count); } +} - - const char *tptr = ecs_parse_ws(ptr); - if (flecs_isident(tptr[0])) { - if (state->decl_stmt) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected ' ' in declaration statement"); - goto error; - } - ptr = tptr; - goto decl_stmt; - } - -next_term: - ptr = plecs_parse_fluff(ptr); - - if (ptr[0] == ':' && ptr[1] == '-') { - ptr = plecs_parse_fluff(ptr + 2); - goto assign_stmt; - } else if (ptr[0] == ':') { - ptr = plecs_parse_fluff(ptr + 1); - goto inherit_stmt; - } else if (ptr[0] == ',') { - ptr = plecs_parse_fluff(ptr + 1); - goto term_expr; - } else if (ptr[0] == '{' || ptr[0] == '[') { - if (state->assign_stmt) { - goto assign_expr; - } else if (state->with_stmt && !isspace(ptr[-1])) { - /* If this is a { in a with statement which directly follows a - * non-whitespace character, the with id has a value */ - ptr = plecs_parse_assign_with_stmt(world, name, expr, ptr, state); - if (!ptr) { - goto error; - } - - goto next_term; - } else { - ptr = plecs_parse_fluff(ptr + 1); - goto scope_open; - } +/* Get value from sparse set when it is guaranteed that the value exists. This + * function is used when values are obtained using a dense index */ +static +void* flecs_sparse_get_sparse( + const ecs_sparse_t *sparse, + int32_t dense, + uint64_t index) +{ + flecs_sparse_strip_generation(&index); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); + if (!page || !page->sparse) { + return NULL; } - state->assign_stmt = false; - goto done; + int32_t offset = FLECS_SPARSE_OFFSET(index); + ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); + (void)dense; -decl_stmt: - state->decl_stmt = true; - goto term_expr; + return DATA(page->data, sparse->size, offset); +} -inherit_stmt: - ptr = plecs_parse_inherit_stmt(name, expr, ptr, state); - if (!ptr) goto error; +/* Swap dense elements. A swap occurs when an element is removed, or when a + * removed element is recycled. */ +static +void flecs_sparse_swap_dense( + ecs_sparse_t * sparse, + ecs_page_t * page_a, + int32_t a, + int32_t b) +{ + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + uint64_t index_a = dense_array[a]; + uint64_t index_b = dense_array[b]; - /* Expect base identifier */ - goto term_expr; + ecs_page_t *page_b = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(index_b)); + flecs_sparse_assign_index(page_a, dense_array, index_a, b); + flecs_sparse_assign_index(page_b, dense_array, index_b, a); +} -assign_stmt: - ptr = plecs_parse_assign_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; +void flecs_sparse_init( + ecs_sparse_t *result, + struct ecs_allocator_t *allocator, + ecs_block_allocator_t *page_allocator, + ecs_size_t size) +{ + ecs_assert(result != NULL, ECS_OUT_OF_MEMORY, NULL); + result->size = size; + result->max_id = UINT64_MAX; + result->allocator = allocator; + result->page_allocator = page_allocator; - ptr = plecs_parse_fluff(ptr); + ecs_vec_init_t(allocator, &result->pages, ecs_page_t, 0); + ecs_vec_init_t(allocator, &result->dense, uint64_t, 1); + result->dense.count = 1; - /* Assignment without a preceding component */ - if (ptr[0] == '{' || ptr[0] == '[') { - goto assign_expr; - } + /* Consume first value in dense array as 0 is used in the sparse array to + * indicate that a sparse element hasn't been paired yet. */ + ecs_vec_first_t(&result->dense, uint64_t)[0] = 0; - /* Expect component identifiers */ - goto term_expr; + result->count = 1; +} -assign_expr: - ptr = plecs_parse_assign_expr(world, name, expr, ptr, state, NULL); - if (!ptr) goto error; +void flecs_sparse_clear( + ecs_sparse_t *sparse) +{ + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ptr = plecs_parse_fluff(ptr); - if (ptr[0] == ',') { - ptr ++; - goto term_expr; - } else if (ptr[0] == '{') { - if (state->var_stmt) { - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, state->var_name); - if (var && var->value.type == ecs_id(ecs_entity_t)) { - ecs_assert(var->value.ptr != NULL, ECS_INTERNAL_ERROR, NULL); - /* The code contained an entity{...} variable assignment, use - * the assigned entity id as type for parsing the expression */ - state->last_assign_id = *(ecs_entity_t*)var->value.ptr; - ptr = plecs_parse_assign_expr(world, name, expr, ptr, state, var); - goto done; - } + int32_t i, count = ecs_vec_count(&sparse->pages); + ecs_page_t *pages = ecs_vec_first_t(&sparse->pages, ecs_page_t); + for (i = 0; i < count; i ++) { + int32_t *indices = pages[i].sparse; + if (indices) { + ecs_os_memset_n(indices, 0, int32_t, FLECS_SPARSE_PAGE_SIZE); } - ecs_parser_error(name, expr, (ptr - expr), - "unexpected '{' after assignment"); - goto error; } - state->assign_stmt = false; - state->assign_to = 0; - goto done; + ecs_vec_set_count_t(sparse->allocator, &sparse->dense, uint64_t, 1); -assign_var_as_component: { - ptr = plecs_parse_var_as_component(world, name, expr, ptr, state); - if (!ptr) { - goto error; - } - state->assign_stmt = false; - state->assign_to = 0; - goto done; + sparse->count = 1; + sparse->max_id = 0; } -scope_open: - ptr = plecs_parse_scope_open(world, name, expr, ptr, state); - if (!ptr) goto error; - goto done; - -scope_close: - ptr = plecs_parse_scope_close(world, name, expr, ptr, state); - if (!ptr) goto error; - goto done; +void flecs_sparse_fini( + ecs_sparse_t *sparse) +{ + ecs_assert(sparse != NULL, ECS_INTERNAL_ERROR, NULL); + + int32_t i, count = ecs_vec_count(&sparse->pages); + ecs_page_t *pages = ecs_vec_first_t(&sparse->pages, ecs_page_t); + for (i = 0; i < count; i ++) { + flecs_sparse_page_free(sparse, &pages[i]); + } -done: - return ptr; -error: - return NULL; + ecs_vec_fini_t(sparse->allocator, &sparse->pages, ecs_page_t); + ecs_vec_fini_t(sparse->allocator, &sparse->dense, uint64_t); } -static -int flecs_plecs_parse( - ecs_world_t *world, - const char *name, - const char *expr, - ecs_vars_t *vars, - ecs_entity_t script, - ecs_entity_t instance) +uint64_t flecs_sparse_new_id( + ecs_sparse_t *sparse) { - const char *ptr = expr; - ecs_term_t term = {0}; - plecs_state_t state = {0}; - - if (!expr) { - return 0; - } + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + return flecs_sparse_new_index(sparse); +} - state.scope[0] = 0; - ecs_entity_t prev_scope = ecs_set_scope(world, 0); - ecs_entity_t prev_with = ecs_set_with(world, 0); +void* flecs_sparse_add( + ecs_sparse_t *sparse, + ecs_size_t size) +{ + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + uint64_t index = flecs_sparse_new_index(sparse); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); + ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); + return DATA(page->data, size, FLECS_SPARSE_OFFSET(index)); +} - if (ECS_IS_PAIR(prev_with) && ECS_PAIR_FIRST(prev_with) == EcsChildOf) { - ecs_set_scope(world, ECS_PAIR_SECOND(prev_with)); - state.scope[0] = ecs_pair_second(world, prev_with); - } else { - state.global_with = prev_with; - } +uint64_t flecs_sparse_last_id( + const ecs_sparse_t *sparse) +{ + ecs_assert(sparse != NULL, ECS_INTERNAL_ERROR, NULL); + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + return dense_array[sparse->count - 1]; +} + +void* flecs_sparse_ensure( + ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index) +{ + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ecs_vec_count(&sparse->dense) > 0, ECS_INTERNAL_ERROR, NULL); + (void)size; - ecs_vars_init(world, &state.vars); + uint64_t gen = flecs_sparse_strip_generation(&index); + ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(index)); + int32_t offset = FLECS_SPARSE_OFFSET(index); + int32_t dense = page->sparse[offset]; - if (script) { - const EcsScript *s = ecs_get(world, script, EcsScript); - if (!s) { - ecs_err("%s: provided script entity is not a script", name); - goto error; - } - if (s && ecs_has(world, script, EcsStruct)) { - state.assembly = script; - state.assembly_instance = true; + if (dense) { + /* Check if element is alive. If element is not alive, update indices so + * that the first unused dense element points to the sparse element. */ + int32_t count = sparse->count; + 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; - if (s->using_.count) { - ecs_os_memcpy_n(state.using, s->using_.array, - ecs_entity_t, s->using_.count); - state.using_frame = s->using_.count; - state.using_frames[0] = s->using_.count; - } + /* First unused element is now last used element */ + sparse->count ++; - if (instance) { - ecs_set_scope(world, instance); - } + /* Set dense element to new generation */ + ecs_vec_first_t(&sparse->dense, uint64_t)[dense] = index | gen; + } else { + /* Dense is already alive, nothing to be done */ } - } - if (vars) { - state.vars.root.parent = vars->cur; - } + /* Ensure provided generation matches current. Only allow mismatching + * generations if the provided generation count is 0. This allows for + * using the ensure function in combination with ids that have their + * generation stripped. */ +#ifdef FLECS_DEBUG + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + ecs_assert(!gen || dense_array[dense] == (index | gen), ECS_INTERNAL_ERROR, NULL); +#endif + } else { + /* Element is not paired yet. Must add a new element to dense array */ + flecs_sparse_grow_dense(sparse); - do { - expr = ptr = plecs_parse_stmt(world, name, expr, ptr, &state); - if (!ptr) { - goto error; - } + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + int32_t dense_count = ecs_vec_count(&sparse->dense) - 1; + int32_t count = sparse->count ++; - if (!ptr[0]) { - break; /* End of expression */ + /* If index is larger than max id, update max id */ + if (index >= flecs_sparse_get_id(sparse)) { + flecs_sparse_set_id(sparse, index); } - } while (true); - ecs_set_scope(world, prev_scope); - ecs_set_with(world, prev_with); - plecs_clear_annotations(&state); + if (count < dense_count) { + /* If there are unused elements in the list, move the first unused + * element to the end of the list */ + uint64_t unused = dense_array[count]; + ecs_page_t *unused_page = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(unused)); + flecs_sparse_assign_index(unused_page, dense_array, unused, dense_count); + } - if (state.is_module) { - state.sp --; + flecs_sparse_assign_index(page, dense_array, index, count); + dense_array[count] |= gen; } - if (state.sp != 0) { - ecs_parser_error(name, expr, 0, "missing end of scope"); - goto error; - } + return DATA(page->data, sparse->size, offset); +} - if (state.assign_stmt) { - ecs_parser_error(name, expr, 0, "unfinished assignment"); - goto error; - } +void* flecs_sparse_ensure_fast( + ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index_long) +{ + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ecs_vec_count(&sparse->dense) > 0, ECS_INTERNAL_ERROR, NULL); + (void)size; - if (state.errors) { - goto error; - } + uint32_t index = (uint32_t)index_long; + ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(index)); + int32_t offset = FLECS_SPARSE_OFFSET(index); + int32_t dense = page->sparse[offset]; + int32_t count = sparse->count; - ecs_vars_fini(&state.vars); + if (!dense) { + /* Element is not paired yet. Must add a new element to dense array */ + sparse->count = count + 1; + if (count == ecs_vec_count(&sparse->dense)) { + flecs_sparse_grow_dense(sparse); + } - return 0; -error: - plecs_free_all_with_frames(world, &state); - ecs_vars_fini(&state.vars); - ecs_set_scope(world, state.scope[0]); - ecs_set_with(world, prev_with); - ecs_term_fini(&term); - return -1; -} + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + flecs_sparse_assign_index(page, dense_array, index, count); + } -int ecs_plecs_from_str( - ecs_world_t *world, - const char *name, - const char *expr) -{ - return flecs_plecs_parse(world, name, expr, NULL, 0, 0); + return DATA(page->data, sparse->size, offset); } -static -char* flecs_load_from_file( - const char *filename) +void* flecs_sparse_remove_fast( + ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index) { - FILE* file; - char* content = NULL; - int32_t bytes; - size_t size; + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + (void)size; - /* Open file for reading */ - ecs_os_fopen(&file, filename, "r"); - if (!file) { - ecs_err("%s (%s)", ecs_os_strerror(errno), filename); - goto error; + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); + if (!page || !page->sparse) { + return NULL; } - /* Determine file size */ - fseek(file, 0 , SEEK_END); - bytes = (int32_t)ftell(file); - if (bytes == -1) { - goto error; - } - rewind(file); + flecs_sparse_strip_generation(&index); + int32_t offset = FLECS_SPARSE_OFFSET(index); + int32_t dense = page->sparse[offset]; - /* Load contents in memory */ - content = ecs_os_malloc(bytes + 1); - size = (size_t)bytes; - if (!(size = fread(content, 1, size, file)) && bytes) { - ecs_err("%s: read zero bytes instead of %d", filename, size); - ecs_os_free(content); - content = NULL; - goto error; + if (dense) { + int32_t count = sparse->count; + if (dense == (count - 1)) { + /* If dense is the last used element, simply decrease count */ + sparse->count --; + } else if (dense < count) { + /* If element is alive, move it to unused elements */ + flecs_sparse_swap_dense(sparse, page, dense, count - 1); + sparse->count --; + } + + /* Reset memory to zero on remove */ + return DATA(page->data, sparse->size, offset); } else { - content[size] = '\0'; + /* Element is not paired and thus not alive, nothing to be done */ + return NULL; } - - fclose(file); - - return content; -error: - ecs_os_free(content); - return NULL; } -int ecs_plecs_from_file( - ecs_world_t *world, - const char *filename) + +void flecs_sparse_remove( + ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index) { - char *script = flecs_load_from_file(filename); - if (!script) { - return -1; + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + (void)size; + + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); + if (!page || !page->sparse) { + return; } - int result = ecs_plecs_from_str(world, filename, script); - ecs_os_free(script); - return result; -} + uint64_t gen = flecs_sparse_strip_generation(&index); + int32_t offset = FLECS_SPARSE_OFFSET(index); + int32_t dense = page->sparse[offset]; -static -ecs_id_t flecs_script_tag( - ecs_entity_t script, - ecs_entity_t instance) -{ - if (!instance) { - return ecs_pair_t(EcsScript, script); + if (dense) { + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + uint64_t cur_gen = dense_array[dense] & ECS_GENERATION_MASK; + if (gen != cur_gen) { + /* Generation doesn't match which means that the provided entity is + * already not alive. */ + return; + } + + /* Increase generation */ + dense_array[dense] = index | flecs_sparse_inc_gen(cur_gen); + + int32_t count = sparse->count; + + if (dense == (count - 1)) { + /* If dense is the last used element, simply decrease count */ + sparse->count --; + } else if (dense < count) { + /* If element is alive, move it to unused elements */ + flecs_sparse_swap_dense(sparse, page, dense, count - 1); + sparse->count --; + } else { + /* Element is not alive, nothing to be done */ + return; + } + + /* Reset memory to zero on remove */ + void *ptr = DATA(page->data, sparse->size, offset); + ecs_os_memset(ptr, 0, size); } else { - return ecs_pair(EcsChildOf, instance); + /* Element is not paired and thus not alive, nothing to be done */ + return; } } -void ecs_script_clear( - ecs_world_t *world, - ecs_entity_t script, - ecs_entity_t instance) +void* flecs_sparse_get_dense( + const ecs_sparse_t *sparse, + ecs_size_t size, + int32_t dense_index) { - ecs_delete_with(world, flecs_script_tag(script, instance)); + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(dense_index < sparse->count, ECS_INVALID_PARAMETER, NULL); + (void)size; + + dense_index ++; + + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + return flecs_sparse_get_sparse(sparse, dense_index, dense_array[dense_index]); } -int ecs_script_update( - ecs_world_t *world, - ecs_entity_t e, - ecs_entity_t instance, - const char *script, - ecs_vars_t *vars) +bool flecs_sparse_is_alive( + const ecs_sparse_t *sparse, + uint64_t index) { - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(script != NULL, ECS_INTERNAL_ERROR, NULL); - - int result = 0; - bool is_defer = ecs_is_deferred(world); - ecs_suspend_readonly_state_t srs; - ecs_world_t *real_world = NULL; - if (is_defer) { - ecs_assert(ecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL); - real_world = flecs_suspend_readonly(world, &srs); - ecs_assert(real_world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); + if (!page || !page->sparse) { + return false; } - ecs_script_clear(world, e, instance); - - EcsScript *s = ecs_get_mut(world, e, EcsScript); - if (!s->script || ecs_os_strcmp(s->script, script)) { - s->script = ecs_os_strdup(script); - ecs_modified(world, e, EcsScript); + int32_t offset = FLECS_SPARSE_OFFSET(index); + int32_t dense = page->sparse[offset]; + if (!dense || (dense >= sparse->count)) { + return false; } - ecs_entity_t prev = ecs_set_with(world, flecs_script_tag(e, instance)); - if (flecs_plecs_parse(world, ecs_get_name(world, e), script, vars, e, instance)) { - ecs_delete_with(world, ecs_pair_t(EcsScript, e)); - result = -1; - } - ecs_set_with(world, prev); + uint64_t gen = flecs_sparse_strip_generation(&index); + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + uint64_t cur_gen = dense_array[dense] & ECS_GENERATION_MASK; - if (is_defer) { - flecs_resume_readonly(real_world, &srs); + if (cur_gen != gen) { + return false; } - return result; + ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); + return true; } -ecs_entity_t ecs_script_init( - ecs_world_t *world, - const ecs_script_desc_t *desc) +void* flecs_sparse_try( + const ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index) { - const char *script = NULL; - ecs_entity_t e = desc->entity; - - ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_check(desc != NULL, ECS_INTERNAL_ERROR, NULL); - - if (!e) { - if (desc->filename) { - e = ecs_new_from_path_w_sep(world, 0, desc->filename, "/", NULL); - } else { - e = ecs_new_id(world); - } - } - - script = desc->str; - if (!script && desc->filename) { - script = flecs_load_from_file(desc->filename); - if (!script) { - goto error; - } + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + (void)size; + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); + if (!page || !page->sparse) { + return NULL; } - if (ecs_script_update(world, e, 0, script, NULL)) { - goto error; + int32_t offset = FLECS_SPARSE_OFFSET(index); + int32_t dense = page->sparse[offset]; + if (!dense || (dense >= sparse->count)) { + return NULL; } - if (script != desc->str) { - /* Safe cast, only happens when script is loaded from file */ - ecs_os_free(ECS_CONST_CAST(char*, script)); + uint64_t gen = flecs_sparse_strip_generation(&index); + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + uint64_t cur_gen = dense_array[dense] & ECS_GENERATION_MASK; + if (cur_gen != gen) { + return NULL; } - return e; -error: - if (script != desc->str) { - /* Safe cast, only happens when script is loaded from file */ - ecs_os_free(ECS_CONST_CAST(char*, script)); - } - if (!desc->entity) { - ecs_delete(world, e); - } - return 0; + ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); + return DATA(page->data, sparse->size, offset); } -void FlecsScriptImport( - ecs_world_t *world) +void* flecs_sparse_get( + const ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index) { - ECS_MODULE(world, FlecsScript); - ECS_IMPORT(world, FlecsMeta); - - ecs_set_name_prefix(world, "Ecs"); - ECS_COMPONENT_DEFINE(world, EcsScript); + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + (void)size; - ecs_set_hooks(world, EcsScript, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsScript), - .dtor = ecs_dtor(EcsScript) - }); + ecs_page_t *page = ecs_vec_get_t(&sparse->pages, ecs_page_t, FLECS_SPARSE_PAGE(index)); + int32_t offset = FLECS_SPARSE_OFFSET(index); + int32_t dense = page->sparse[offset]; + ecs_assert(dense != 0, ECS_INTERNAL_ERROR, NULL); - ecs_add_id(world, ecs_id(EcsScript), EcsTag); - ecs_add_id(world, ecs_id(EcsScript), EcsPrivate); + uint64_t gen = flecs_sparse_strip_generation(&index); + uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); + uint64_t cur_gen = dense_array[dense] & ECS_GENERATION_MASK; + (void)cur_gen; (void)gen; - ecs_struct(world, { - .entity = ecs_id(EcsScript), - .members = { - { .name = "using", .type = ecs_vector(world, { - .entity = ecs_entity(world, { .name = "UsingVector" }), - .type = ecs_id(ecs_entity_t) - }), - .count = 0 - }, - { .name = "script", .type = ecs_id(ecs_string_t), .count = 0 } - } - }); + ecs_assert(cur_gen == gen, ECS_INVALID_PARAMETER, NULL); + ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); + ecs_assert(dense < sparse->count, ECS_INTERNAL_ERROR, NULL); + return DATA(page->data, sparse->size, offset); } -#endif - -/** - * @file addons/rest.c - * @brief Rest addon. - */ - - -#ifdef FLECS_REST - -static ECS_TAG_DECLARE(EcsRestPlecs); - -typedef struct { - ecs_world_t *world; - ecs_http_server_t *srv; - int32_t rc; -} ecs_rest_ctx_t; - -static ECS_COPY(EcsRest, dst, src, { - ecs_rest_ctx_t *impl = src->impl; - if (impl) { - impl->rc ++; +void* flecs_sparse_get_any( + const ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index) +{ + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + (void)size; + + flecs_sparse_strip_generation(&index); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); + if (!page || !page->sparse) { + return NULL; } - ecs_os_strset(&dst->ipaddr, src->ipaddr); - dst->port = src->port; - dst->impl = impl; -}) - -static ECS_MOVE(EcsRest, dst, src, { - *dst = *src; - src->ipaddr = NULL; - src->impl = NULL; -}) - -static ECS_DTOR(EcsRest, ptr, { - ecs_rest_ctx_t *impl = ptr->impl; - if (impl) { - impl->rc --; - if (!impl->rc) { - ecs_http_server_fini(impl->srv); - ecs_os_free(impl); - } + int32_t offset = FLECS_SPARSE_OFFSET(index); + int32_t dense = page->sparse[offset]; + bool in_use = dense && (dense < sparse->count); + if (!in_use) { + return NULL; } - ecs_os_free(ptr->ipaddr); -}) -static char *rest_last_err; -static ecs_os_api_log_t rest_prev_log; + ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); + return DATA(page->data, sparse->size, offset); +} -static -void flecs_rest_capture_log( - int32_t level, - const char *file, - int32_t line, - const char *msg) +int32_t flecs_sparse_count( + const ecs_sparse_t *sparse) { - (void)file; (void)line; - -#ifdef FLECS_DEBUG - if (level < 0) { - /* Also log to previous log function in debug mode */ - if (rest_prev_log) { - ecs_log_enable_colors(true); - rest_prev_log(level, file, line, msg); - ecs_log_enable_colors(false); - } + if (!sparse || !sparse->count) { + return 0; } -#endif - if (!rest_last_err && level < 0) { - rest_last_err = ecs_os_strdup(msg); - } + return sparse->count - 1; } -static -char* flecs_rest_get_captured_log(void) { - char *result = rest_last_err; - rest_last_err = NULL; - return result; +const uint64_t* flecs_sparse_ids( + const ecs_sparse_t *sparse) +{ + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + if (sparse->dense.array) { + return &(ecs_vec_first_t(&sparse->dense, uint64_t)[1]); + } else { + return NULL; + } } -static -void flecs_reply_verror( - ecs_http_reply_t *reply, - const char *fmt, - va_list args) +void ecs_sparse_init( + ecs_sparse_t *sparse, + ecs_size_t elem_size) { - ecs_strbuf_appendlit(&reply->body, "{\"error\":\""); - ecs_strbuf_vappend(&reply->body, fmt, args); - ecs_strbuf_appendlit(&reply->body, "\"}"); + flecs_sparse_init(sparse, NULL, NULL, elem_size); } -static -void flecs_reply_error( - ecs_http_reply_t *reply, - const char *fmt, - ...) +void* ecs_sparse_add( + ecs_sparse_t *sparse, + ecs_size_t elem_size) { - va_list args; - va_start(args, fmt); - flecs_reply_verror(reply, fmt, args); - va_end(args); + return flecs_sparse_add(sparse, elem_size); } -static -void flecs_rest_bool_param( - const ecs_http_request_t *req, - const char *name, - bool *value_out) +uint64_t ecs_sparse_last_id( + const ecs_sparse_t *sparse) { - const char *value = ecs_http_get_param(req, name); - if (value) { - if (!ecs_os_strcmp(value, "true")) { - value_out[0] = true; - } else { - value_out[0] = false; - } - } + return flecs_sparse_last_id(sparse); } -static -void flecs_rest_int_param( - const ecs_http_request_t *req, - const char *name, - int32_t *value_out) +int32_t ecs_sparse_count( + const ecs_sparse_t *sparse) { - const char *value = ecs_http_get_param(req, name); - if (value) { - *value_out = atoi(value); - } + return flecs_sparse_count(sparse); } -static -void flecs_rest_string_param( - const ecs_http_request_t *req, - const char *name, - char **value_out) +void* ecs_sparse_get_dense( + const ecs_sparse_t *sparse, + ecs_size_t elem_size, + int32_t index) { - const char *value = ecs_http_get_param(req, name); - if (value) { - *value_out = ECS_CONST_CAST(char*, value); - } + return flecs_sparse_get_dense(sparse, elem_size, index); } -static -void flecs_rest_parse_json_ser_entity_params( - ecs_world_t *world, - ecs_entity_to_json_desc_t *desc, - const ecs_http_request_t *req) +void* ecs_sparse_get( + const ecs_sparse_t *sparse, + ecs_size_t elem_size, + uint64_t id) { - flecs_rest_bool_param(req, "path", &desc->serialize_path); - flecs_rest_bool_param(req, "label", &desc->serialize_label); - flecs_rest_bool_param(req, "brief", &desc->serialize_brief); - flecs_rest_bool_param(req, "link", &desc->serialize_link); - flecs_rest_bool_param(req, "color", &desc->serialize_color); - flecs_rest_bool_param(req, "ids", &desc->serialize_ids); - flecs_rest_bool_param(req, "id_labels", &desc->serialize_id_labels); - flecs_rest_bool_param(req, "base", &desc->serialize_base); - flecs_rest_bool_param(req, "values", &desc->serialize_values); - flecs_rest_bool_param(req, "private", &desc->serialize_private); - flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info); - flecs_rest_bool_param(req, "matches", &desc->serialize_matches); - flecs_rest_bool_param(req, "alerts", &desc->serialize_alerts); - - char *rel = NULL; - flecs_rest_string_param(req, "refs", &rel); - if (rel) { - desc->serialize_refs = ecs_lookup_fullpath(world, rel); - } + return flecs_sparse_get(sparse, elem_size, id); } +/** + * @file datastructures/stack_allocator.c + * @brief Stack allocator. + * + * The stack allocator enables pushing and popping values to a stack, and has + * a lower overhead when compared to block allocators. A stack allocator is a + * good fit for small temporary allocations. + * + * The stack allocator allocates memory in pages. If the requested size of an + * allocation exceeds the page size, a regular allocator is used instead. + */ + + +#define FLECS_STACK_PAGE_OFFSET ECS_ALIGN(ECS_SIZEOF(ecs_stack_page_t), 16) + +int64_t ecs_stack_allocator_alloc_count = 0; +int64_t ecs_stack_allocator_free_count = 0; + static -void flecs_rest_parse_json_ser_iter_params( - ecs_iter_to_json_desc_t *desc, - const ecs_http_request_t *req) -{ - flecs_rest_bool_param(req, "term_ids", &desc->serialize_term_ids); - flecs_rest_bool_param(req, "term_labels", &desc->serialize_term_labels); - flecs_rest_bool_param(req, "ids", &desc->serialize_ids); - flecs_rest_bool_param(req, "id_labels", &desc->serialize_id_labels); - flecs_rest_bool_param(req, "sources", &desc->serialize_sources); - flecs_rest_bool_param(req, "variables", &desc->serialize_variables); - flecs_rest_bool_param(req, "is_set", &desc->serialize_is_set); - flecs_rest_bool_param(req, "values", &desc->serialize_values); - flecs_rest_bool_param(req, "private", &desc->serialize_private); - flecs_rest_bool_param(req, "entities", &desc->serialize_entities); - flecs_rest_bool_param(req, "entity_labels", &desc->serialize_entity_labels); - flecs_rest_bool_param(req, "variable_labels", &desc->serialize_variable_labels); - flecs_rest_bool_param(req, "variable_ids", &desc->serialize_variable_ids); - 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, "table", &desc->serialize_table); +ecs_stack_page_t* flecs_stack_page_new(uint32_t page_id) { + ecs_stack_page_t *result = ecs_os_malloc( + FLECS_STACK_PAGE_OFFSET + ECS_STACK_PAGE_SIZE); + result->data = ECS_OFFSET(result, FLECS_STACK_PAGE_OFFSET); + result->next = NULL; + result->id = page_id + 1; + ecs_os_linc(&ecs_stack_allocator_alloc_count); + return result; } -static -bool flecs_rest_reply_entity( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply) +void* flecs_stack_alloc( + ecs_stack_t *stack, + ecs_size_t size, + ecs_size_t align) { - char *path = &req->path[7]; - ecs_dbg_2("rest: request entity '%s'", path); + ecs_assert(size > 0, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t e = ecs_lookup_path_w_sep( - world, 0, path, "/", NULL, false); - if (!e) { - ecs_dbg_2("rest: entity '%s' not found", path); - flecs_reply_error(reply, "entity '%s' not found", path); - reply->code = 404; - return true; + ecs_stack_page_t *page = stack->tail_page; + if (page == &stack->first && !page->data) { + page->data = ecs_os_malloc(ECS_STACK_PAGE_SIZE); + ecs_os_linc(&ecs_stack_allocator_alloc_count); } - ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - flecs_rest_parse_json_ser_entity_params(world, &desc, req); - if (ecs_entity_to_json_buf(world, e, &reply->body, &desc) != 0) { - ecs_strbuf_reset(&reply->body); - reply->code = 500; - reply->status = "Internal server error"; - return true; - } - return true; -} + int16_t sp = flecs_ito(int16_t, ECS_ALIGN(page->sp, align)); + int16_t next_sp = flecs_ito(int16_t, sp + size); + void *result = NULL; -static -bool flecs_rest_reply_world( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply) -{ - (void)req; - if (ecs_world_to_json_buf(world, &reply->body, NULL) != 0) { - ecs_strbuf_reset(&reply->body); - reply->code = 500; - reply->status = "Internal server error"; - return true; + if (next_sp > ECS_STACK_PAGE_SIZE) { + if (size > ECS_STACK_PAGE_SIZE) { + result = ecs_os_malloc(size); /* Too large for page */ + goto done; + } + + if (page->next) { + page = page->next; + } else { + page = page->next = flecs_stack_page_new(page->id); + } + sp = 0; + next_sp = flecs_ito(int16_t, size); + stack->tail_page = page; } - return true; + + page->sp = next_sp; + result = ECS_OFFSET(page->data, sp); + +done: +#ifdef FLECS_SANITIZE + ecs_os_memset(result, 0xAA, size); +#endif + return result; } -static -ecs_entity_t flecs_rest_entity_from_path( - ecs_world_t *world, - ecs_http_reply_t *reply, - const char *path) +void* flecs_stack_calloc( + ecs_stack_t *stack, + ecs_size_t size, + ecs_size_t align) { - ecs_entity_t e = ecs_lookup_path_w_sep( - world, 0, path, "/", NULL, false); - if (!e) { - flecs_reply_error(reply, "entity '%s' not found", path); - reply->code = 404; - } - return e; + void *ptr = flecs_stack_alloc(stack, size, align); + ecs_os_memset(ptr, 0, size); + return ptr; } -static -bool flecs_rest_set( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply, - const char *path) +void flecs_stack_free( + void *ptr, + ecs_size_t size) { - ecs_entity_t e; - if (!(e = flecs_rest_entity_from_path(world, reply, path))) { - return true; - } - - const char *data = ecs_http_get_param(req, "data"); - ecs_from_json_desc_t desc = {0}; - desc.expr = data; - desc.name = path; - if (ecs_entity_from_json(world, e, data, &desc) == NULL) { - flecs_reply_error(reply, "invalid request"); - reply->code = 400; - return true; + if (size > ECS_STACK_PAGE_SIZE) { + ecs_os_free(ptr); } - - return true; } -static -bool flecs_rest_delete( - ecs_world_t *world, - ecs_http_reply_t *reply, - const char *path) +ecs_stack_cursor_t* flecs_stack_get_cursor( + ecs_stack_t *stack) { - ecs_entity_t e; - if (!(e = flecs_rest_entity_from_path(world, reply, path))) { - return true; - } + ecs_assert(stack != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_delete(world, e); - - return true; -} + ecs_stack_page_t *page = stack->tail_page; + int16_t sp = stack->tail_page->sp; + ecs_stack_cursor_t *result = flecs_stack_alloc_t(stack, ecs_stack_cursor_t); + result->page = page; + result->sp = sp; + result->is_free = false; -static -bool flecs_rest_enable( - ecs_world_t *world, - ecs_http_reply_t *reply, - const char *path, - bool enable) -{ - ecs_entity_t e; - if (!(e = flecs_rest_entity_from_path(world, reply, path))) { - return true; - } +#ifdef FLECS_DEBUG + ++ stack->cursor_count; + result->owner = stack; +#endif - ecs_enable(world, e, enable); - - return true; + result->prev = stack->tail_cursor; + stack->tail_cursor = result; + return result; } -static -bool flecs_rest_script( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply) +#define FLECS_STACK_LEAK_MSG \ + "a stack allocator leak is most likely due to an unterminated " \ + "iteration: call ecs_iter_fini to fix" + +void flecs_stack_restore_cursor( + ecs_stack_t *stack, + ecs_stack_cursor_t *cursor) { - (void)world; - (void)req; - (void)reply; -#ifdef FLECS_PLECS - const char *data = ecs_http_get_param(req, "data"); - if (!data) { - flecs_reply_error(reply, "missing data parameter"); - return true; + if (!cursor) { + return; } - bool prev_color = ecs_log_enable_colors(false); - rest_prev_log = ecs_os_api.log_; - ecs_os_api.log_ = flecs_rest_capture_log; + ecs_dbg_assert(stack == cursor->owner, ECS_INVALID_OPERATION, + "attempting to restore a cursor for the wrong stack"); + ecs_dbg_assert(stack->cursor_count > 0, ECS_DOUBLE_FREE, + "double free detected in stack allocator"); + ecs_assert(cursor->is_free == false, ECS_DOUBLE_FREE, + "double free detected in stack allocator"); - ecs_entity_t script = ecs_script(world, { - .entity = ecs_entity(world, { .name = "scripts.main" }), - .str = data - }); + cursor->is_free = true; - if (!script) { - char *err = flecs_rest_get_captured_log(); - char *escaped_err = ecs_astresc('"', err); - flecs_reply_error(reply, escaped_err); - reply->code = 400; /* bad request */ - ecs_os_free(escaped_err); - ecs_os_free(err); +#ifdef FLECS_DEBUG + -- stack->cursor_count; +#endif + + /* If cursor is not the last on the stack no memory should be freed */ + if (cursor != stack->tail_cursor) { + return; } - ecs_os_api.log_ = rest_prev_log; - ecs_log_enable_colors(prev_color); + /* Iterate freed cursors to know how much memory we can free */ + do { + ecs_stack_cursor_t* prev = cursor->prev; + if (!prev || !prev->is_free) { + break; /* Found active cursor, free up until this point */ + } + cursor = prev; + } while (cursor); - return true; -#else - return false; -#endif + stack->tail_cursor = cursor->prev; + stack->tail_page = cursor->page; + stack->tail_page->sp = cursor->sp; + + /* If the cursor count is zero, stack should be empty + * if the cursor count is non-zero, stack should not be empty */ + ecs_dbg_assert((stack->cursor_count == 0) == + (stack->tail_page == &stack->first && stack->tail_page->sp == 0), + ECS_LEAK_DETECTED, FLECS_STACK_LEAK_MSG); } -static -void flecs_rest_reply_set_captured_log( - ecs_http_reply_t *reply) +void flecs_stack_reset( + ecs_stack_t *stack) { - char *err = flecs_rest_get_captured_log(); - if (err) { - char *escaped_err = ecs_astresc('"', err); - flecs_reply_error(reply, escaped_err); - reply->code = 400; - ecs_os_free(escaped_err); - ecs_os_free(err); - } + ecs_dbg_assert(stack->cursor_count == 0, ECS_LEAK_DETECTED, + FLECS_STACK_LEAK_MSG); + stack->tail_page = &stack->first; + stack->first.sp = 0; + stack->tail_cursor = NULL; } -static -int flecs_rest_iter_to_reply( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply, - ecs_iter_t *it) +void flecs_stack_init( + ecs_stack_t *stack) { - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_entities = true; - desc.serialize_variables = true; - flecs_rest_parse_json_ser_iter_params(&desc, req); - - int32_t offset = 0; - int32_t limit = 1000; + ecs_os_zeromem(stack); + stack->tail_page = &stack->first; + stack->first.data = NULL; +} - flecs_rest_int_param(req, "offset", &offset); - flecs_rest_int_param(req, "limit", &limit); +void flecs_stack_fini( + ecs_stack_t *stack) +{ + ecs_stack_page_t *next, *cur = &stack->first; + ecs_dbg_assert(stack->cursor_count == 0, ECS_LEAK_DETECTED, + FLECS_STACK_LEAK_MSG); + ecs_assert(stack->tail_page == &stack->first, ECS_LEAK_DETECTED, + FLECS_STACK_LEAK_MSG); + ecs_assert(stack->tail_page->sp == 0, ECS_LEAK_DETECTED, + FLECS_STACK_LEAK_MSG); - if (offset < 0 || limit < 0) { - flecs_reply_error(reply, "invalid offset/limit parameter"); - reply->code = 400; - return -1; - } + do { + next = cur->next; + if (cur == &stack->first) { + if (cur->data) { + ecs_os_linc(&ecs_stack_allocator_free_count); + } + ecs_os_free(cur->data); + } else { + ecs_os_linc(&ecs_stack_allocator_free_count); + ecs_os_free(cur); + } + } while ((cur = next)); +} - ecs_iter_t pit = ecs_page_iter(it, offset, limit); - if (ecs_iter_to_json_buf(world, &pit, &reply->body, &desc)) { - flecs_rest_reply_set_captured_log(reply); - return -1; - } +/** + * @file datastructures/strbuf.c + * @brief Utility for constructing strings. + * + * A buffer builds up a list of elements which individually can be up to N bytes + * large. While appending, data is added to these elements. More elements are + * added on the fly when needed. When an application calls ecs_strbuf_get, all + * elements are combined in one string and the element administration is freed. + * + * This approach prevents reallocs of large blocks of memory, and therefore + * copying large blocks of memory when appending to a large buffer. A buffer + * preallocates some memory for the element overhead so that for small strings + * there is hardly any overhead, while for large strings the overhead is offset + * by the reduced time spent on copying memory. + * + * The functionality provided by strbuf is similar to std::stringstream. + */ - return 0; -} +#include -static -bool flecs_rest_reply_existing_query( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply, - const char *name) -{ - ecs_entity_t q = ecs_lookup_fullpath(world, name); - if (!q) { - flecs_reply_error(reply, "unresolved identifier '%s'", name); - reply->code = 404; - return true; - } +/** + * stm32tpl -- STM32 C++ Template Peripheral Library + * Visit https://github.com/antongus/stm32tpl for new versions + * + * Copyright (c) 2011-2020 Anton B. Gusev aka AHTOXA + */ - const EcsPoly *poly = ecs_get_pair(world, q, EcsPoly, EcsQuery); - if (!poly) { - flecs_reply_error(reply, - "resolved identifier '%s' is not a query", name); - reply->code = 400; - return true; - } +#define MAX_PRECISION (10) +#define EXP_THRESHOLD (3) +#define INT64_MAX_F ((double)INT64_MAX) - ecs_iter_t it; - ecs_iter_poly(world, poly->poly, &it, NULL); +static const double rounders[MAX_PRECISION + 1] = +{ + 0.5, // 0 + 0.05, // 1 + 0.005, // 2 + 0.0005, // 3 + 0.00005, // 4 + 0.000005, // 5 + 0.0000005, // 6 + 0.00000005, // 7 + 0.000000005, // 8 + 0.0000000005, // 9 + 0.00000000005 // 10 +}; - ecs_dbg_2("rest: request query '%s'", q); - bool prev_color = ecs_log_enable_colors(false); - rest_prev_log = ecs_os_api.log_; - ecs_os_api.log_ = flecs_rest_capture_log; +static +char* flecs_strbuf_itoa( + char *buf, + int64_t v) +{ + char *ptr = buf; + char * p1; + char c; - const char *vars = ecs_http_get_param(req, "vars"); - if (vars) { - if (!ecs_poly_is(poly->poly, ecs_rule_t)) { - flecs_reply_error(reply, - "variables are only supported for rule queries"); - reply->code = 400; - return true; - } - if (ecs_rule_parse_vars(poly->poly, &it, vars) == NULL) { - flecs_rest_reply_set_captured_log(reply); - return true; + if (!v) { + *ptr++ = '0'; + } else { + if (v < 0) { + ptr[0] = '-'; + ptr ++; + v *= -1; } - } - flecs_rest_iter_to_reply(world, req, reply, &it); + char *p = ptr; + while (v) { + int64_t vdiv = v / 10; + int64_t vmod = v - (vdiv * 10); + p[0] = (char)('0' + vmod); + p ++; + v = vdiv; + } - ecs_os_api.log_ = rest_prev_log; - ecs_log_enable_colors(prev_color); + p1 = p; - return true; + while (p > ptr) { + c = *--p; + *p = *ptr; + *ptr++ = c; + } + ptr = p1; + } + return ptr; } static -bool flecs_rest_reply_query( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply) +void flecs_strbuf_ftoa( + ecs_strbuf_t *out, + double f, + int precision, + char nan_delim) { - const char *q_name = ecs_http_get_param(req, "name"); - if (q_name) { - return flecs_rest_reply_existing_query(world, req, reply, q_name); - } + char buf[64]; + char * ptr = buf; + char c; + int64_t intPart; + int64_t exp = 0; - const char *q = ecs_http_get_param(req, "q"); - if (!q) { - ecs_strbuf_appendlit(&reply->body, "Missing parameter 'q'"); - reply->code = 400; /* bad request */ - return true; + if (ecs_os_isnan(f)) { + if (nan_delim) { + ecs_strbuf_appendch(out, nan_delim); + ecs_strbuf_appendlit(out, "NaN"); + ecs_strbuf_appendch(out, nan_delim); + return; + } else { + ecs_strbuf_appendlit(out, "NaN"); + return; + } + } + if (ecs_os_isinf(f)) { + if (nan_delim) { + ecs_strbuf_appendch(out, nan_delim); + ecs_strbuf_appendlit(out, "Inf"); + ecs_strbuf_appendch(out, nan_delim); + return; + } else { + ecs_strbuf_appendlit(out, "Inf"); + return; + } } - ecs_dbg_2("rest: request query '%s'", q); - bool prev_color = ecs_log_enable_colors(false); - rest_prev_log = ecs_os_api.log_; - ecs_os_api.log_ = flecs_rest_capture_log; - - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ - .expr = q - }); - if (!r) { - flecs_rest_reply_set_captured_log(reply); - } else { - ecs_iter_t it = ecs_rule_iter(world, r); - flecs_rest_iter_to_reply(world, req, reply, &it); - ecs_rule_fini(r); + if (precision > MAX_PRECISION) { + precision = MAX_PRECISION; } - ecs_os_api.log_ = rest_prev_log; - ecs_log_enable_colors(prev_color); - - return true; -} + if (f < 0) { + f = -f; + *ptr++ = '-'; + } -#ifdef FLECS_MONITOR + if (precision < 0) { + if (f < 1.0) precision = 6; + else if (f < 10.0) precision = 5; + else if (f < 100.0) precision = 4; + else if (f < 1000.0) precision = 3; + else if (f < 10000.0) precision = 2; + else if (f < 100000.0) precision = 1; + else precision = 0; + } -static -void flecs_rest_array_append_( - ecs_strbuf_t *reply, - const char *field, - int32_t field_len, - const ecs_float_t *values, - int32_t t) -{ - ecs_strbuf_list_appendch(reply, '"'); - ecs_strbuf_appendstrn(reply, field, field_len); - ecs_strbuf_appendlit(reply, "\":"); - ecs_strbuf_list_push(reply, "[", ","); + if (precision) { + f += rounders[precision]; + } - int32_t i; - for (i = t + 1; i <= (t + ECS_STAT_WINDOW); i ++) { - int32_t index = i % ECS_STAT_WINDOW; - ecs_strbuf_list_next(reply); - ecs_strbuf_appendflt(reply, (double)values[index], '"'); + /* Make sure that number can be represented as 64bit int, increase exp */ + while (f > INT64_MAX_F) { + f /= 1000 * 1000 * 1000; + exp += 9; } - ecs_strbuf_list_pop(reply, "]"); -} + intPart = (int64_t)f; + f -= (double)intPart; -#define flecs_rest_array_append(reply, field, values, t)\ - flecs_rest_array_append_(reply, field, sizeof(field) - 1, values, t) + ptr = flecs_strbuf_itoa(ptr, intPart); -static -void flecs_rest_gauge_append( - ecs_strbuf_t *reply, - const ecs_metric_t *m, - const char *field, - int32_t field_len, - int32_t t, - const char *brief, - int32_t brief_len) -{ - ecs_strbuf_list_appendch(reply, '"'); - ecs_strbuf_appendstrn(reply, field, field_len); - ecs_strbuf_appendlit(reply, "\":"); - ecs_strbuf_list_push(reply, "{", ","); + if (precision) { + *ptr++ = '.'; + while (precision--) { + f *= 10.0; + c = (char)f; + *ptr++ = (char)('0' + c); + f -= c; + } + } + *ptr = 0; - flecs_rest_array_append(reply, "avg", m->gauge.avg, t); - flecs_rest_array_append(reply, "min", m->gauge.min, t); - flecs_rest_array_append(reply, "max", m->gauge.max, t); + /* Remove trailing 0s */ + while ((&ptr[-1] != buf) && (ptr[-1] == '0')) { + ptr[-1] = '\0'; + ptr --; + } + if (ptr != buf && ptr[-1] == '.') { + ptr[-1] = '\0'; + ptr --; + } - if (brief) { - ecs_strbuf_list_appendlit(reply, "\"brief\":\""); - ecs_strbuf_appendstrn(reply, brief, brief_len); - ecs_strbuf_appendch(reply, '"'); + /* If 0s before . exceed threshold, convert to exponent to save space + * without losing precision. */ + char *cur = ptr; + while ((&cur[-1] != buf) && (cur[-1] == '0')) { + cur --; } - ecs_strbuf_list_pop(reply, "}"); -} + if (exp || ((ptr - cur) > EXP_THRESHOLD)) { + cur[0] = '\0'; + exp += (ptr - cur); + ptr = cur; + } -static -void flecs_rest_counter_append( - ecs_strbuf_t *reply, - const ecs_metric_t *m, - const char *field, - int32_t field_len, - int32_t t, - const char *brief, - int32_t brief_len) -{ - flecs_rest_gauge_append(reply, m, field, field_len, t, brief, brief_len); -} + if (exp) { + char *p1 = &buf[1]; + if (nan_delim) { + ecs_os_memmove(buf + 1, buf, 1 + (ptr - buf)); + buf[0] = nan_delim; + p1 ++; + } -#define ECS_GAUGE_APPEND_T(reply, s, field, t, brief)\ - flecs_rest_gauge_append(reply, &(s)->field, #field, sizeof(#field) - 1, t, brief, sizeof(brief) - 1) + /* Make sure that exp starts after first character */ + c = p1[0]; -#define ECS_COUNTER_APPEND_T(reply, s, field, t, brief)\ - flecs_rest_counter_append(reply, &(s)->field, #field, sizeof(#field) - 1, t, brief, sizeof(brief) - 1) + if (c) { + p1[0] = '.'; + do { + char t = (++p1)[0]; + if (t == '.') { + exp ++; + p1 --; + break; + } + p1[0] = c; + c = t; + exp ++; + } while (c); + ptr = p1 + 1; + } else { + ptr = p1; + } -#define ECS_GAUGE_APPEND(reply, s, field, brief)\ - ECS_GAUGE_APPEND_T(reply, s, field, (s)->t, brief) + ptr[0] = 'e'; + ptr = flecs_strbuf_itoa(ptr + 1, exp); -#define ECS_COUNTER_APPEND(reply, s, field, brief)\ - ECS_COUNTER_APPEND_T(reply, s, field, (s)->t, brief) + if (nan_delim) { + ptr[0] = nan_delim; + ptr ++; + } + + ptr[0] = '\0'; + } + + ecs_strbuf_appendstrn(out, buf, (int32_t)(ptr - buf)); +} +/* Add an extra element to the buffer */ static -void flecs_world_stats_to_json( - ecs_strbuf_t *reply, - const EcsWorldStats *monitor_stats) +void flecs_strbuf_grow( + ecs_strbuf_t *b) { - const ecs_world_stats_t *stats = &monitor_stats->stats; + if (!b->content) { + b->content = b->small_string; + b->size = ECS_STRBUF_SMALL_STRING_SIZE; + } else if (b->content == b->small_string) { + b->size *= 2; + b->content = ecs_os_malloc_n(char, b->size); + ecs_os_memcpy(b->content, b->small_string, b->length); + } else { + b->size *= 2; + if (b->size < 16) b->size = 16; + b->content = ecs_os_realloc_n(b->content, char, b->size); + } +} - ecs_strbuf_list_push(reply, "{", ","); - ECS_GAUGE_APPEND(reply, stats, entities.count, "Alive entity ids in the world"); - ECS_GAUGE_APPEND(reply, stats, entities.not_alive_count, "Not alive entity ids in the world"); +static +char* flecs_strbuf_ptr( + ecs_strbuf_t *b) +{ + ecs_assert(b->content != NULL, ECS_INTERNAL_ERROR, NULL); + return &b->content[b->length]; +} - ECS_GAUGE_APPEND(reply, stats, performance.fps, "Frames per second"); - ECS_COUNTER_APPEND(reply, stats, performance.frame_time, "Time spent in frame"); - ECS_COUNTER_APPEND(reply, stats, performance.system_time, "Time spent on running systems in frame"); - ECS_COUNTER_APPEND(reply, stats, performance.emit_time, "Time spent on notifying observers in frame"); - ECS_COUNTER_APPEND(reply, stats, performance.merge_time, "Time spent on merging commands in frame"); - ECS_COUNTER_APPEND(reply, stats, performance.rematch_time, "Time spent on revalidating query caches in frame"); +/* Append a format string to a buffer */ +static +void flecs_strbuf_vappend( + ecs_strbuf_t *b, + const char* str, + va_list args) +{ + va_list arg_cpy; - ECS_COUNTER_APPEND(reply, stats, commands.add_count, "Add commands executed"); - ECS_COUNTER_APPEND(reply, stats, commands.remove_count, "Remove commands executed"); - ECS_COUNTER_APPEND(reply, stats, commands.delete_count, "Delete commands executed"); - ECS_COUNTER_APPEND(reply, stats, commands.clear_count, "Clear commands executed"); - ECS_COUNTER_APPEND(reply, stats, commands.set_count, "Set commands executed"); - ECS_COUNTER_APPEND(reply, stats, commands.get_mut_count, "Get_mut commands executed"); - ECS_COUNTER_APPEND(reply, stats, commands.modified_count, "Modified commands executed"); - ECS_COUNTER_APPEND(reply, stats, commands.other_count, "Misc commands executed"); - ECS_COUNTER_APPEND(reply, stats, commands.discard_count, "Commands for already deleted entities"); - ECS_COUNTER_APPEND(reply, stats, commands.batched_entity_count, "Entities with batched commands"); - ECS_COUNTER_APPEND(reply, stats, commands.batched_count, "Number of commands batched"); + if (!str) { + return; + } - ECS_COUNTER_APPEND(reply, stats, frame.merge_count, "Number of merges (sync points)"); - ECS_COUNTER_APPEND(reply, stats, frame.pipeline_build_count, "Pipeline rebuilds (happen when systems become active/enabled)"); - ECS_COUNTER_APPEND(reply, stats, frame.systems_ran, "Systems ran in frame"); - ECS_COUNTER_APPEND(reply, stats, frame.observers_ran, "Number of times an observer was invoked in frame"); - ECS_COUNTER_APPEND(reply, stats, frame.event_emit_count, "Events emitted in frame"); - ECS_COUNTER_APPEND(reply, stats, frame.rematch_count, "Number of query cache revalidations"); + /* Compute the memory required to add the string to the buffer. If user + * provided buffer, use space left in buffer, otherwise use space left in + * current element. */ + int32_t mem_left = b->size - b->length; + int32_t mem_required; - ECS_GAUGE_APPEND(reply, stats, tables.count, "Tables in the world (including empty)"); - ECS_GAUGE_APPEND(reply, stats, tables.empty_count, "Empty tables in the world"); - ECS_COUNTER_APPEND(reply, stats, tables.create_count, "Number of new tables created"); - ECS_COUNTER_APPEND(reply, stats, tables.delete_count, "Number of tables deleted"); + va_copy(arg_cpy, args); - ECS_GAUGE_APPEND(reply, stats, components.tag_count, "Tag ids in use"); - ECS_GAUGE_APPEND(reply, stats, components.component_count, "Component ids in use"); - ECS_GAUGE_APPEND(reply, stats, components.pair_count, "Pair ids in use"); - ECS_GAUGE_APPEND(reply, stats, components.type_count, "Registered component types"); - ECS_COUNTER_APPEND(reply, stats, components.create_count, "Number of new component, tag and pair ids created"); - ECS_COUNTER_APPEND(reply, stats, components.delete_count, "Number of component, pair and tag ids deleted"); + if (b->content) { + mem_required = ecs_os_vsnprintf( + flecs_strbuf_ptr(b), + flecs_itosize(mem_left), str, args); + } else { + mem_required = ecs_os_vsnprintf(NULL, 0, str, args); + mem_left = 0; + } - ECS_GAUGE_APPEND(reply, stats, queries.query_count, "Queries in the world"); - ECS_GAUGE_APPEND(reply, stats, queries.observer_count, "Observers in the world"); - ECS_GAUGE_APPEND(reply, stats, queries.system_count, "Systems in the world"); + ecs_assert(mem_required != -1, ECS_INTERNAL_ERROR, NULL); - ECS_COUNTER_APPEND(reply, stats, memory.alloc_count, "Allocations by OS API"); - ECS_COUNTER_APPEND(reply, stats, memory.realloc_count, "Reallocs by OS API"); - ECS_COUNTER_APPEND(reply, stats, memory.free_count, "Frees by OS API"); - ECS_GAUGE_APPEND(reply, stats, memory.outstanding_alloc_count, "Outstanding allocations by OS API"); - ECS_COUNTER_APPEND(reply, stats, memory.block_alloc_count, "Blocks allocated by block allocators"); - ECS_COUNTER_APPEND(reply, stats, memory.block_free_count, "Blocks freed by block allocators"); - ECS_GAUGE_APPEND(reply, stats, memory.block_outstanding_alloc_count, "Outstanding block allocations"); - ECS_COUNTER_APPEND(reply, stats, memory.stack_alloc_count, "Pages allocated by stack allocators"); - ECS_COUNTER_APPEND(reply, stats, memory.stack_free_count, "Pages freed by stack allocators"); - ECS_GAUGE_APPEND(reply, stats, memory.stack_outstanding_alloc_count, "Outstanding page allocations"); + if ((mem_required + 1) >= mem_left) { + while ((mem_required + 1) >= mem_left) { + flecs_strbuf_grow(b); + mem_left = b->size - b->length; + } + ecs_os_vsnprintf(flecs_strbuf_ptr(b), + flecs_itosize(mem_required + 1), str, arg_cpy); + } - ECS_COUNTER_APPEND(reply, stats, http.request_received_count, "Received requests"); - ECS_COUNTER_APPEND(reply, stats, http.request_invalid_count, "Received invalid requests"); - ECS_COUNTER_APPEND(reply, stats, http.request_handled_ok_count, "Requests handled successfully"); - ECS_COUNTER_APPEND(reply, stats, http.request_handled_error_count, "Requests handled with error code"); - ECS_COUNTER_APPEND(reply, stats, http.request_not_handled_count, "Requests not handled (unknown endpoint)"); - ECS_COUNTER_APPEND(reply, stats, http.request_preflight_count, "Preflight requests received"); - ECS_COUNTER_APPEND(reply, stats, http.send_ok_count, "Successful replies"); - ECS_COUNTER_APPEND(reply, stats, http.send_error_count, "Unsuccessful replies"); - ECS_COUNTER_APPEND(reply, stats, http.busy_count, "Dropped requests due to full send queue (503)"); + b->length += mem_required; - ecs_strbuf_list_pop(reply, "}"); + va_end(arg_cpy); } static -void flecs_system_stats_to_json( - ecs_world_t *world, - ecs_strbuf_t *reply, - ecs_entity_t system, - const ecs_system_stats_t *stats) +void flecs_strbuf_appendstr( + ecs_strbuf_t *b, + const char* str, + int n) { - ecs_strbuf_list_push(reply, "{", ","); - ecs_strbuf_list_appendlit(reply, "\"name\":\""); - ecs_get_path_w_sep_buf(world, 0, system, ".", NULL, reply); - ecs_strbuf_appendch(reply, '"'); - - if (!stats->task) { - ECS_GAUGE_APPEND(reply, &stats->query, matched_table_count, ""); - ECS_GAUGE_APPEND(reply, &stats->query, matched_entity_count, ""); + int32_t mem_left = b->size - b->length; + while (n >= mem_left) { + flecs_strbuf_grow(b); + mem_left = b->size - b->length; } - ECS_COUNTER_APPEND_T(reply, stats, time_spent, stats->query.t, ""); - ecs_strbuf_list_pop(reply, "}"); + ecs_os_memcpy(flecs_strbuf_ptr(b), str, n); + b->length += n; } static -void flecs_pipeline_stats_to_json( - ecs_world_t *world, - ecs_strbuf_t *reply, - const EcsPipelineStats *stats) +void flecs_strbuf_appendch( + ecs_strbuf_t *b, + char ch) { - ecs_strbuf_list_push(reply, "[", ","); - - int32_t i, count = ecs_vec_count(&stats->stats.systems), sync_cur = 0; - ecs_entity_t *ids = ecs_vec_first_t(&stats->stats.systems, ecs_entity_t); - for (i = 0; i < count; i ++) { - ecs_entity_t id = ids[i]; - - ecs_strbuf_list_next(reply); - - if (id) { - ecs_system_stats_t *sys_stats = ecs_map_get_deref( - &stats->stats.system_stats, ecs_system_stats_t, id); - flecs_system_stats_to_json(world, reply, id, sys_stats); - } else { - /* Sync point */ - ecs_strbuf_list_push(reply, "{", ","); - ecs_sync_stats_t *sync_stats = ecs_vec_get_t( - &stats->stats.sync_points, ecs_sync_stats_t, sync_cur); - - ecs_strbuf_list_appendlit(reply, "\"system_count\":"); - ecs_strbuf_appendint(reply, sync_stats->system_count); - - ecs_strbuf_list_appendlit(reply, "\"multi_threaded\":"); - ecs_strbuf_appendbool(reply, sync_stats->multi_threaded); + if (b->size == b->length) { + flecs_strbuf_grow(b); + } - ecs_strbuf_list_appendlit(reply, "\"no_readonly\":"); - ecs_strbuf_appendbool(reply, sync_stats->no_readonly); + flecs_strbuf_ptr(b)[0] = ch; + b->length ++; +} - ECS_GAUGE_APPEND_T(reply, sync_stats, - time_spent, stats->stats.t, ""); - ECS_GAUGE_APPEND_T(reply, sync_stats, - commands_enqueued, stats->stats.t, ""); +void ecs_strbuf_vappend( + ecs_strbuf_t *b, + const char* fmt, + va_list args) +{ + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(fmt != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_strbuf_vappend(b, fmt, args); +} - ecs_strbuf_list_pop(reply, "}"); - sync_cur ++; - } - } +void ecs_strbuf_append( + ecs_strbuf_t *b, + const char* fmt, + ...) +{ + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(fmt != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_strbuf_list_pop(reply, "]"); + va_list args; + va_start(args, fmt); + flecs_strbuf_vappend(b, fmt, args); + va_end(args); } -static -bool flecs_rest_reply_stats( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply) +void ecs_strbuf_appendstrn( + ecs_strbuf_t *b, + const char* str, + int32_t len) { - char *period_str = NULL; - flecs_rest_string_param(req, "period", &period_str); - char *category = &req->path[6]; + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_strbuf_appendstr(b, str, len); +} - ecs_entity_t period = EcsPeriod1s; - if (period_str) { - char *period_name = ecs_asprintf("Period%s", period_str); - period = ecs_lookup_child(world, ecs_id(FlecsMonitor), period_name); - ecs_os_free(period_name); - if (!period) { - flecs_reply_error(reply, "bad request (invalid period string)"); - reply->code = 400; - return false; - } - } +void ecs_strbuf_appendch( + ecs_strbuf_t *b, + char ch) +{ + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_strbuf_appendch(b, ch); +} - if (!ecs_os_strcmp(category, "world")) { - const EcsWorldStats *stats = ecs_get_pair(world, EcsWorld, - EcsWorldStats, period); - flecs_world_stats_to_json(&reply->body, stats); - return true; +void ecs_strbuf_appendint( + ecs_strbuf_t *b, + int64_t v) +{ + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + char numbuf[32]; + char *ptr = flecs_strbuf_itoa(numbuf, v); + ecs_strbuf_appendstrn(b, numbuf, flecs_ito(int32_t, ptr - numbuf)); +} - } else if (!ecs_os_strcmp(category, "pipeline")) { - const EcsPipelineStats *stats = ecs_get_pair(world, EcsWorld, - EcsPipelineStats, period); - flecs_pipeline_stats_to_json(world, &reply->body, stats); - return true; +void ecs_strbuf_appendflt( + ecs_strbuf_t *b, + double flt, + char nan_delim) +{ + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_strbuf_ftoa(b, flt, 10, nan_delim); +} +void ecs_strbuf_appendbool( + ecs_strbuf_t *buffer, + bool v) +{ + ecs_assert(buffer != NULL, ECS_INVALID_PARAMETER, NULL); + if (v) { + ecs_strbuf_appendlit(buffer, "true"); } else { - flecs_reply_error(reply, "bad request (unsupported category)"); - reply->code = 400; - return false; + ecs_strbuf_appendlit(buffer, "false"); } } -#else -static -bool flecs_rest_reply_stats( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply) + +void ecs_strbuf_appendstr( + ecs_strbuf_t *b, + const char* str) { - (void)world; - (void)req; - (void)reply; - return false; + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_strbuf_appendstr(b, str, ecs_os_strlen(str)); } -#endif -static -void flecs_rest_reply_table_append_type( - ecs_world_t *world, - ecs_strbuf_t *reply, - const ecs_table_t *table) +void ecs_strbuf_mergebuff( + ecs_strbuf_t *b, + ecs_strbuf_t *src) { - ecs_strbuf_list_push(reply, "[", ","); - int32_t i, count = table->type.count; - ecs_id_t *ids = table->type.array; - for (i = 0; i < count; i ++) { - ecs_strbuf_list_next(reply); - ecs_strbuf_appendch(reply, '"'); - ecs_id_str_buf(world, ids[i], reply); - ecs_strbuf_appendch(reply, '"'); + if (src->content) { + ecs_strbuf_appendstr(b, src->content); } - ecs_strbuf_list_pop(reply, "]"); + ecs_strbuf_reset(src); } -static -void flecs_rest_reply_table_append_memory( - ecs_strbuf_t *reply, - const ecs_table_t *table) +char* ecs_strbuf_get( + ecs_strbuf_t *b) { - int32_t used = 0, allocated = 0; - - used += table->data.entities.count * ECS_SIZEOF(ecs_entity_t); - allocated += table->data.entities.size * ECS_SIZEOF(ecs_entity_t); + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + char *result = b->content; + if (!result) { + return NULL; + } - int32_t i, storage_count = table->column_count; - ecs_column_t *columns = table->data.columns; + ecs_strbuf_appendch(b, '\0'); + result = b->content; - for (i = 0; i < storage_count; i ++) { - used += columns[i].data.count * columns[i].ti->size; - allocated += columns[i].data.size * columns[i].ti->size; + if (result == b->small_string) { + result = ecs_os_memdup_n(result, char, b->length + 1); } - ecs_strbuf_list_push(reply, "{", ","); - ecs_strbuf_list_append(reply, "\"used\":%d", used); - ecs_strbuf_list_append(reply, "\"allocated\":%d", allocated); - ecs_strbuf_list_pop(reply, "}"); + b->length = 0; + b->content = NULL; + b->size = 0; + b->list_sp = 0; + return result; } -static -void flecs_rest_reply_table_append( - ecs_world_t *world, - ecs_strbuf_t *reply, - const ecs_table_t *table) +char* ecs_strbuf_get_small( + ecs_strbuf_t *b) { - ecs_strbuf_list_next(reply); - ecs_strbuf_list_push(reply, "{", ","); - ecs_strbuf_list_append(reply, "\"id\":%u", (uint32_t)table->id); - ecs_strbuf_list_appendstr(reply, "\"type\":"); - flecs_rest_reply_table_append_type(world, reply, table); - ecs_strbuf_list_append(reply, "\"count\":%d", ecs_table_count(table)); - ecs_strbuf_list_append(reply, "\"memory\":"); - flecs_rest_reply_table_append_memory(reply, table); - ecs_strbuf_list_pop(reply, "}"); + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + char *result = b->content; + result[b->length] = '\0'; + b->length = 0; + b->content = NULL; + b->size = 0; + return result; } -static -bool flecs_rest_reply_tables( - ecs_world_t *world, - const ecs_http_request_t* req, - ecs_http_reply_t *reply) +void ecs_strbuf_reset( + ecs_strbuf_t *b) { - (void)req; - - ecs_strbuf_list_push(&reply->body, "[", ","); - ecs_sparse_t *tables = &world->store.tables; - int32_t i, count = flecs_sparse_count(tables); - for (i = 0; i < count; i ++) { - ecs_table_t *table = flecs_sparse_get_dense_t(tables, ecs_table_t, i); - flecs_rest_reply_table_append(world, &reply->body, table); + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + if (b->content && b->content != b->small_string) { + ecs_os_free(b->content); } - ecs_strbuf_list_pop(&reply->body, "]"); - - return true; + *b = ECS_STRBUF_INIT; } -static -bool flecs_rest_reply( - const ecs_http_request_t* req, - ecs_http_reply_t *reply, - void *ctx) +void ecs_strbuf_list_push( + ecs_strbuf_t *b, + const char *list_open, + const char *separator) { - ecs_rest_ctx_t *impl = ctx; - ecs_world_t *world = impl->world; - - if (req->path == NULL) { - ecs_dbg("rest: bad request (missing path)"); - flecs_reply_error(reply, "bad request (missing path)"); - reply->code = 400; - return false; - } - - if (req->method == EcsHttpGet) { - /* Entity endpoint */ - if (!ecs_os_strncmp(req->path, "entity/", 7)) { - return flecs_rest_reply_entity(world, req, reply); - - /* Query endpoint */ - } else if (!ecs_os_strcmp(req->path, "query")) { - return flecs_rest_reply_query(world, req, reply); - - /* World endpoint */ - } else if (!ecs_os_strcmp(req->path, "world")) { - return flecs_rest_reply_world(world, req, reply); + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(list_open != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(separator != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(b->list_sp >= 0, ECS_INVALID_OPERATION, + "strbuf list is corrupt"); + b->list_sp ++; + ecs_assert(b->list_sp < ECS_STRBUF_MAX_LIST_DEPTH, + ECS_INVALID_OPERATION, "max depth for strbuf list stack exceeded"); - /* Stats endpoint */ - } else if (!ecs_os_strncmp(req->path, "stats/", 6)) { - return flecs_rest_reply_stats(world, req, reply); + b->list_stack[b->list_sp].count = 0; + b->list_stack[b->list_sp].separator = separator; - /* Tables endpoint */ - } else if (!ecs_os_strncmp(req->path, "tables", 6)) { - return flecs_rest_reply_tables(world, req, reply); + if (list_open) { + char ch = list_open[0]; + if (ch && !list_open[1]) { + ecs_strbuf_appendch(b, ch); + } else { + ecs_strbuf_appendstr(b, list_open); } + } +} - } else if (req->method == EcsHttpPut) { - /* Set endpoint */ - if (!ecs_os_strncmp(req->path, "set/", 4)) { - return flecs_rest_set(world, req, reply, &req->path[4]); - - /* Delete endpoint */ - } else if (!ecs_os_strncmp(req->path, "delete/", 7)) { - return flecs_rest_delete(world, reply, &req->path[7]); - - /* Enable endpoint */ - } else if (!ecs_os_strncmp(req->path, "enable/", 7)) { - return flecs_rest_enable(world, reply, &req->path[7], true); - - /* Disable endpoint */ - } else if (!ecs_os_strncmp(req->path, "disable/", 8)) { - return flecs_rest_enable(world, reply, &req->path[8], false); +void ecs_strbuf_list_pop( + ecs_strbuf_t *b, + const char *list_close) +{ + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(list_close != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(b->list_sp > 0, ECS_INVALID_OPERATION, + "pop called more often than push for strbuf list"); - /* Script endpoint */ - } else if (!ecs_os_strncmp(req->path, "script", 6)) { - return flecs_rest_script(world, req, reply); + b->list_sp --; + + if (list_close) { + char ch = list_close[0]; + if (ch && !list_close[1]) { + ecs_strbuf_appendch(b, list_close[0]); + } else { + ecs_strbuf_appendstr(b, list_close); } } - - return false; } -ecs_http_server_t* ecs_rest_server_init( - ecs_world_t *world, - const ecs_http_server_desc_t *desc) +void ecs_strbuf_list_next( + ecs_strbuf_t *b) { - ecs_rest_ctx_t *srv_ctx = ecs_os_calloc_t(ecs_rest_ctx_t); - ecs_http_server_desc_t private_desc = {0}; - if (desc) { - private_desc = *desc; - } - private_desc.callback = flecs_rest_reply; - private_desc.ctx = srv_ctx; + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_http_server_t *srv = ecs_http_server_init(&private_desc); - if (!srv) { - ecs_os_free(srv_ctx); - return NULL; + int32_t list_sp = b->list_sp; + if (b->list_stack[list_sp].count != 0) { + const char *sep = b->list_stack[list_sp].separator; + if (sep && !sep[1]) { + ecs_strbuf_appendch(b, sep[0]); + } else { + ecs_strbuf_appendstr(b, sep); + } } - - srv_ctx->world = world; - srv_ctx->srv = srv; - srv_ctx->rc = 1; - srv_ctx->srv = srv; - return srv; + b->list_stack[list_sp].count ++; } -void ecs_rest_server_fini( - ecs_http_server_t *srv) +void ecs_strbuf_list_appendch( + ecs_strbuf_t *b, + char ch) { - ecs_rest_ctx_t *srv_ctx = ecs_http_server_ctx(srv); - ecs_os_free(srv_ctx); - ecs_http_server_fini(srv); + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_strbuf_list_next(b); + flecs_strbuf_appendch(b, ch); } -static -void flecs_on_set_rest(ecs_iter_t *it) { - EcsRest *rest = it->ptrs[0]; - - int i; - for(i = 0; i < it->count; i ++) { - if (!rest[i].port) { - rest[i].port = ECS_REST_DEFAULT_PORT; - } - - ecs_http_server_t *srv = ecs_rest_server_init(it->real_world, - &(ecs_http_server_desc_t){ - .ipaddr = rest[i].ipaddr, - .port = rest[i].port - }); - - if (!srv) { - const char *ipaddr = rest[i].ipaddr ? rest[i].ipaddr : "0.0.0.0"; - ecs_err("failed to create REST server on %s:%u", - ipaddr, rest[i].port); - continue; - } +void ecs_strbuf_list_append( + ecs_strbuf_t *b, + const char *fmt, + ...) +{ + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(fmt != NULL, ECS_INVALID_PARAMETER, NULL); - rest[i].impl = ecs_http_server_ctx(srv); + ecs_strbuf_list_next(b); - ecs_http_server_start(srv); - } + va_list args; + va_start(args, fmt); + flecs_strbuf_vappend(b, fmt, args); + va_end(args); } -static -void DequeueRest(ecs_iter_t *it) { - EcsRest *rest = ecs_field(it, EcsRest, 1); - - if (it->delta_system_time > (ecs_ftime_t)1.0) { - ecs_warn( - "detected large progress interval (%.2fs), REST request may timeout", - (double)it->delta_system_time); - } +void ecs_strbuf_list_appendstr( + ecs_strbuf_t *b, + const char *str) +{ + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - int32_t i; - for(i = 0; i < it->count; i ++) { - ecs_rest_ctx_t *ctx = rest[i].impl; - if (ctx) { - ecs_http_server_dequeue(ctx->srv, it->delta_time); - } - } + ecs_strbuf_list_next(b); + ecs_strbuf_appendstr(b, str); } -static -void DisableRest(ecs_iter_t *it) { - ecs_world_t *world = it->world; - - ecs_iter_t rit = ecs_term_iter(world, &(ecs_term_t){ - .id = ecs_id(EcsRest), - .src.flags = EcsSelf - }); +void ecs_strbuf_list_appendstrn( + ecs_strbuf_t *b, + const char *str, + int32_t n) +{ + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - if (it->event == EcsOnAdd) { - /* REST module was disabled */ - while (ecs_term_next(&rit)) { - EcsRest *rest = ecs_field(&rit, EcsRest, 1); - int i; - for (i = 0; i < rit.count; i ++) { - ecs_rest_ctx_t *ctx = rest[i].impl; - ecs_http_server_stop(ctx->srv); - } - } - } else if (it->event == EcsOnRemove) { - /* REST module was enabled */ - while (ecs_term_next(&rit)) { - EcsRest *rest = ecs_field(&rit, EcsRest, 1); - int i; - for (i = 0; i < rit.count; i ++) { - ecs_rest_ctx_t *ctx = rest[i].impl; - ecs_http_server_start(ctx->srv); - } - } - } + ecs_strbuf_list_next(b); + ecs_strbuf_appendstrn(b, str, n); } -void FlecsRestImport( - ecs_world_t *world) +int32_t ecs_strbuf_written( + const ecs_strbuf_t *b) { - ECS_MODULE(world, FlecsRest); - - ECS_IMPORT(world, FlecsPipeline); -#ifdef FLECS_PLECS - ECS_IMPORT(world, FlecsScript); -#endif - - ecs_set_name_prefix(world, "Ecs"); - - flecs_bootstrap_component(world, EcsRest); - - ecs_set_hooks(world, EcsRest, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsRest), - .copy = ecs_copy(EcsRest), - .dtor = ecs_dtor(EcsRest), - .on_set = flecs_on_set_rest - }); - - ECS_SYSTEM(world, DequeueRest, EcsPostFrame, EcsRest); - - ecs_system(world, { - .entity = ecs_id(DequeueRest), - .no_readonly = true - }); - - ecs_observer(world, { - .filter = { - .terms = {{ .id = EcsDisabled, .src.id = ecs_id(FlecsRest) }} - }, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = DisableRest - }); - - ecs_set_name_prefix(world, "EcsRest"); - ECS_TAG_DEFINE(world, EcsRestPlecs); + ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + return b->length; } -#endif - -/** - * @file addons/snapshot.c - * @brief Snapshot addon. - */ - - -#ifdef FLECS_SNAPSHOT +static +ecs_switch_page_t* flecs_switch_page_ensure( + ecs_switch_t* sw, + uint32_t elem) +{ + int32_t page_index = FLECS_SPARSE_PAGE(elem); + ecs_vec_set_min_count_zeromem_t( + sw->hdrs.allocator, &sw->pages, ecs_switch_page_t, page_index + 1); -/* World snapshot */ -struct ecs_snapshot_t { - ecs_world_t *world; - ecs_entity_index_t entity_index; - ecs_vec_t tables; - uint64_t last_id; -}; + ecs_switch_page_t *page = ecs_vec_get_t( + &sw->pages, ecs_switch_page_t, page_index); + if (!ecs_vec_count(&page->nodes)) { + ecs_vec_init_t(sw->hdrs.allocator, &page->nodes, ecs_switch_node_t, + FLECS_SPARSE_PAGE_SIZE); + ecs_vec_init_t(sw->hdrs.allocator, &page->values, uint64_t, + FLECS_SPARSE_PAGE_SIZE); + ecs_vec_set_min_count_zeromem_t(sw->hdrs.allocator, &page->nodes, + ecs_switch_node_t, FLECS_SPARSE_PAGE_SIZE); + ecs_vec_set_min_count_zeromem_t(sw->hdrs.allocator, &page->values, + uint64_t, FLECS_SPARSE_PAGE_SIZE); + } -/** Small footprint data structure for storing data associated with a table. */ -typedef struct ecs_table_leaf_t { - ecs_table_t *table; - ecs_type_t type; - ecs_data_t *data; -} ecs_table_leaf_t; + return page; +} static -ecs_data_t* flecs_duplicate_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *main_data) +ecs_switch_page_t* flecs_switch_page_get( + const ecs_switch_t* sw, + uint32_t elem) { - if (!ecs_table_count(table)) { + int32_t page_index = FLECS_SPARSE_PAGE(elem); + if (page_index >= ecs_vec_count(&sw->pages)) { return NULL; } - ecs_data_t *result = ecs_os_calloc_t(ecs_data_t); - int32_t i, column_count = table->column_count; - result->columns = flecs_wdup_n(world, ecs_column_t, column_count, - main_data->columns); - - /* Copy entities */ - ecs_allocator_t *a = &world->allocator; - result->entities = ecs_vec_copy_t(a, &main_data->entities, ecs_entity_t); - - /* Copy each column */ - for (i = 0; i < column_count; i ++) { - ecs_column_t *column = &result->columns[i]; - ecs_type_info_t *ti = column->ti; - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t size = ti->size; - ecs_copy_t copy = ti->hooks.copy; - if (copy) { - ecs_vec_t dst = ecs_vec_copy(a, &column->data, size); - int32_t count = ecs_vec_count(&column->data); - void *dst_ptr = ecs_vec_first(&dst); - void *src_ptr = ecs_vec_first(&column->data); - - ecs_xtor_t ctor = ti->hooks.ctor; - if (ctor) { - ctor(dst_ptr, count, ti); - } - - copy(dst_ptr, src_ptr, count, ti); - column->data = dst; - } else { - column->data = ecs_vec_copy(a, &column->data, size); - } + ecs_switch_page_t *page = ecs_vec_get_t( + &sw->pages, ecs_switch_page_t, page_index); + if (!ecs_vec_count(&page->nodes)) { + return NULL; } - return result; + return page; } static -void snapshot_table( - const ecs_world_t *world, - ecs_snapshot_t *snapshot, - ecs_table_t *table) +void flecs_switch_page_fini( + ecs_switch_t* sw, + ecs_switch_page_t *page) { - if (table->flags & EcsTableHasBuiltins) { - return; + if (ecs_vec_count(&page->nodes)) { + ecs_vec_fini_t(sw->hdrs.allocator, &page->nodes, ecs_switch_node_t); + ecs_vec_fini_t(sw->hdrs.allocator, &page->values, uint64_t); } - - ecs_table_leaf_t *l = ecs_vec_get_t( - &snapshot->tables, ecs_table_leaf_t, (int32_t)table->id); - ecs_assert(l != NULL, ECS_INTERNAL_ERROR, NULL); - - l->table = table; - l->type = flecs_type_copy( - ECS_CONST_CAST(ecs_world_t*, world), &table->type); - l->data = flecs_duplicate_data( - ECS_CONST_CAST(ecs_world_t*, world), table, &table->data); } static -ecs_snapshot_t* snapshot_create( - const ecs_world_t *world, - const ecs_entity_index_t *entity_index, - ecs_iter_t *iter, - ecs_iter_next_action_t next) +ecs_switch_node_t* flecs_switch_get_node( + ecs_switch_t* sw, + uint32_t element) { - ecs_snapshot_t *result = ecs_os_calloc_t(ecs_snapshot_t); - ecs_assert(result != NULL, ECS_OUT_OF_MEMORY, NULL); - - ecs_run_aperiodic(ECS_CONST_CAST(ecs_world_t*, world), 0); - - result->world = ECS_CONST_CAST(ecs_world_t*, world); - - /* If no iterator is provided, the snapshot will be taken of the entire - * world, and we can simply copy the entity index as it will be restored - * entirely upon snapshote restore. */ - if (!iter && entity_index) { - flecs_entities_copy(&result->entity_index, entity_index); - } - - /* Create vector with as many elements as tables, so we can store the - * snapshot tables at their element ids. When restoring a snapshot, the code - * will run a diff between the tables in the world and the snapshot, to see - * which of the world tables still exist, no longer exist, or need to be - * deleted. */ - uint64_t t, table_count = flecs_sparse_last_id(&world->store.tables) + 1; - ecs_vec_init_t(NULL, &result->tables, ecs_table_leaf_t, (int32_t)table_count); - ecs_vec_set_count_t(NULL, &result->tables, ecs_table_leaf_t, (int32_t)table_count); - ecs_table_leaf_t *arr = ecs_vec_first_t(&result->tables, ecs_table_leaf_t); - - /* Array may have holes, so initialize with 0 */ - ecs_os_memset_n(arr, 0, ecs_table_leaf_t, table_count); - - /* Iterate tables in iterator */ - if (iter) { - while (next(iter)) { - ecs_table_t *table = iter->table; - snapshot_table(world, result, table); - } - } else { - for (t = 1; t < table_count; t ++) { - ecs_table_t *table = flecs_sparse_get_t( - &world->store.tables, ecs_table_t, t); - snapshot_table(world, result, table); - } + if (!element) { + return NULL; } - return result; + ecs_switch_page_t *page = flecs_switch_page_ensure(sw, element); + int32_t page_offset = FLECS_SPARSE_OFFSET(element); + return ecs_vec_get_t(&page->nodes, ecs_switch_node_t, page_offset); } -/** Create a snapshot */ -ecs_snapshot_t* ecs_snapshot_take( - ecs_world_t *stage) +void flecs_switch_init( + ecs_switch_t* sw, + ecs_allocator_t *allocator) { - const ecs_world_t *world = ecs_get_world(stage); - - ecs_snapshot_t *result = snapshot_create( - world, ecs_eis(world), NULL, NULL); - - result->last_id = flecs_entities_max_id(world); - - return result; + ecs_map_init(&sw->hdrs, allocator); + ecs_vec_init_t(allocator, &sw->pages, ecs_switch_page_t, 0); } -/** Create a filtered snapshot */ -ecs_snapshot_t* ecs_snapshot_take_w_iter( - ecs_iter_t *iter) +void flecs_switch_fini( + ecs_switch_t* sw) { - ecs_world_t *world = iter->world; - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_snapshot_t *result = snapshot_create( - world, ecs_eis(world), iter, iter ? iter->next : NULL); - - result->last_id = flecs_entities_max_id(world); - - return result; + int32_t i, count = ecs_vec_count(&sw->pages); + ecs_switch_page_t *pages = ecs_vec_first(&sw->pages); + for (i = 0; i < count; i ++) { + flecs_switch_page_fini(sw, &pages[i]); + } + ecs_vec_fini_t(sw->hdrs.allocator, &sw->pages, ecs_switch_page_t); + ecs_map_fini(&sw->hdrs); } -/* Restoring an unfiltered snapshot restores the world to the exact state it was - * when the snapshot was taken. */ -static -void restore_unfiltered( - ecs_world_t *world, - ecs_snapshot_t *snapshot) +bool flecs_switch_set( + ecs_switch_t *sw, + uint32_t element, + uint64_t value) { - flecs_entity_index_restore(ecs_eis(world), &snapshot->entity_index); - flecs_entity_index_fini(&snapshot->entity_index); - - flecs_entities_max_id(world) = snapshot->last_id; + ecs_switch_page_t *page = flecs_switch_page_ensure(sw, element); + int32_t page_offset = FLECS_SPARSE_OFFSET(element); - ecs_table_leaf_t *leafs = ecs_vec_first_t(&snapshot->tables, ecs_table_leaf_t); - int32_t i, count = (int32_t)flecs_sparse_last_id(&world->store.tables); - int32_t snapshot_count = ecs_vec_count(&snapshot->tables); + uint64_t *elem = ecs_vec_get_t(&page->values, uint64_t, page_offset); + if (elem[0] == value) { + return false; + } - for (i = 1; i <= count; i ++) { - ecs_table_t *world_table = flecs_sparse_get_t( - &world->store.tables, ecs_table_t, (uint32_t)i); + ecs_switch_node_t *node = ecs_vec_get_t( + &page->nodes, ecs_switch_node_t, page_offset); - if (world_table && (world_table->flags & EcsTableHasBuiltins)) { - continue; + uint64_t prev_value = elem[0]; + if (prev_value) { + ecs_switch_node_t *prev = flecs_switch_get_node(sw, node->prev); + if (prev) { + prev->next = node->next; } - ecs_table_leaf_t *snapshot_table = NULL; - if (i < snapshot_count) { - snapshot_table = &leafs[i]; - if (!snapshot_table->table) { - snapshot_table = NULL; - } + ecs_switch_node_t *next = flecs_switch_get_node(sw, node->next); + if (next) { + next->prev = node->prev; } - /* If the world table no longer exists but the snapshot table does, - * reinsert it */ - if (!world_table && snapshot_table) { - ecs_table_t *table = flecs_table_find_or_create(world, - &snapshot_table->type); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + if (!prev) { + uint64_t *hdr = ecs_map_get(&sw->hdrs, prev_value); + ecs_assert(hdr[0] == (uint64_t)element, ECS_INTERNAL_ERROR, NULL); + hdr[0] = (uint64_t)node->next; + } + } - if (snapshot_table->data) { - flecs_table_replace_data(world, table, snapshot_table->data); - } + elem[0] = value; - /* If the world table still exists, replace its data */ - } else if (world_table && snapshot_table) { - ecs_assert(snapshot_table->table == world_table, - ECS_INTERNAL_ERROR, NULL); + if (value) { + uint64_t *hdr = ecs_map_ensure(&sw->hdrs, value); - if (snapshot_table->data) { - flecs_table_replace_data( - world, world_table, snapshot_table->data); - } else { - flecs_table_clear_data( - world, world_table, &world_table->data); - flecs_table_init_data(world, world_table); - } - - /* If the snapshot table doesn't exist, this table was created after the - * snapshot was taken and needs to be deleted */ - } else if (world_table && !snapshot_table) { - /* Deleting a table invokes OnRemove triggers & updates the entity - * index. That is not what we want, since entities may no longer be - * valid (if they don't exist in the snapshot) or may have been - * restored in a different table. Therefore first clear the data - * from the table (which doesn't invoke triggers), and then delete - * the table. */ - flecs_table_clear_data(world, world_table, &world_table->data); - flecs_delete_table(world, world_table); - - /* If there is no world & snapshot table, nothing needs to be done */ - } else { } + if (!hdr[0]) { + hdr[0] = (uint64_t)element; + node->next = 0; + } else { + ecs_switch_node_t *head = flecs_switch_get_node(sw, (uint32_t)hdr[0]); + ecs_assert(head->prev == 0, ECS_INTERNAL_ERROR, NULL); + head->prev = element; - if (snapshot_table) { - ecs_os_free(snapshot_table->data); - flecs_type_free(world, &snapshot_table->type); + node->next = (uint32_t)hdr[0]; + hdr[0] = (uint64_t)element; + ecs_assert(node->next != element, ECS_INTERNAL_ERROR, NULL); } + + node->prev = 0; } - /* Now that all tables have been restored and world is in a consistent - * state, run OnSet systems */ - int32_t world_count = flecs_sparse_count(&world->store.tables); - for (i = 0; i < world_count; i ++) { - ecs_table_t *table = flecs_sparse_get_dense_t( - &world->store.tables, ecs_table_t, i); - if (table->flags & EcsTableHasBuiltins) { - continue; - } + return true; +} - int32_t tcount = ecs_table_count(table); - if (tcount) { - int32_t j, storage_count = table->column_count; - for (j = 0; j < storage_count; j ++) { - ecs_type_t type = { - .array = &table->data.columns[j].id, - .count = 1 - }; - flecs_notify_on_set(world, table, 0, tcount, &type, true); - } - } +bool flecs_switch_reset( + ecs_switch_t *sw, + uint32_t element) +{ + return flecs_switch_set(sw, element, 0); +} + +uint64_t flecs_switch_get( + const ecs_switch_t *sw, + uint32_t element) +{ + ecs_switch_page_t *page = flecs_switch_page_get(sw, element); + if (!page) { + return 0; } + + int32_t page_offset = FLECS_SPARSE_OFFSET(element); + uint64_t *elem = ecs_vec_get_t(&page->values, uint64_t, page_offset); + return elem[0]; } -/* Restoring a filtered snapshots only restores the entities in the snapshot - * to their previous state. */ -static -void restore_filtered( - ecs_world_t *world, - ecs_snapshot_t *snapshot) +uint32_t flecs_switch_first( + const ecs_switch_t *sw, + uint64_t value) { - ecs_table_leaf_t *leafs = ecs_vec_first_t(&snapshot->tables, ecs_table_leaf_t); - int32_t l = 0, snapshot_count = ecs_vec_count(&snapshot->tables); + uint64_t *hdr = ecs_map_get(&sw->hdrs, value); + if (!hdr) { + return 0; + } - for (l = 0; l < snapshot_count; l ++) { - ecs_table_leaf_t *snapshot_table = &leafs[l]; - ecs_table_t *table = snapshot_table->table; + return (uint32_t)hdr[0]; +} - if (!table) { - continue; - } +FLECS_DBG_API +uint32_t flecs_switch_next( + const ecs_switch_t *sw, + uint32_t previous) +{ + ecs_switch_page_t *page = flecs_switch_page_get(sw, previous); + if (!page) { + return 0; + } - ecs_data_t *data = snapshot_table->data; - if (!data) { - flecs_type_free(world, &snapshot_table->type); - continue; - } + int32_t offset = FLECS_SPARSE_OFFSET(previous); + ecs_switch_node_t *elem = ecs_vec_get_t( + &page->nodes, ecs_switch_node_t, offset); + return elem->next; +} - /* Delete entity from storage first, so that when we restore it to the - * current table we can be sure that there won't be any duplicates */ - int32_t i, entity_count = ecs_vec_count(&data->entities); - ecs_entity_t *entities = ecs_vec_first( - &snapshot_table->data->entities); - for (i = 0; i < entity_count; i ++) { - ecs_entity_t e = entities[i]; - ecs_record_t *r = flecs_entities_try(world, e); - if (r && r->table) { - flecs_table_delete(world, r->table, - ECS_RECORD_TO_ROW(r->row), true); - } else { - /* Make sure that the entity has the same generation count */ - flecs_entities_set_generation(world, e); - } - } +ecs_map_iter_t flecs_switch_targets( + const ecs_switch_t *sw) +{ + return ecs_map_iter(&sw->hdrs); +} - /* Merge data from snapshot table with world table */ - int32_t old_count = ecs_table_count(snapshot_table->table); - int32_t new_count = flecs_table_data_count(snapshot_table->data); +/** + * @file datastructures/vec.c + * @brief Vector with allocator support. + */ - flecs_table_merge(world, table, table, &table->data, snapshot_table->data); - /* Run OnSet systems for merged entities */ - if (new_count) { - int32_t j, storage_count = table->column_count; - for (j = 0; j < storage_count; j ++) { - ecs_type_t type = { - .array = &table->data.columns[j].id, - .count = 1 - }; - flecs_notify_on_set( - world, table, old_count, new_count, &type, true); - } +void ecs_vec_init( + ecs_allocator_t *allocator, + ecs_vec_t *v, + ecs_size_t size, + int32_t elem_count) +{ + ecs_assert(size != 0, ECS_INVALID_PARAMETER, NULL); + v->array = NULL; + v->count = 0; + if (elem_count) { + if (allocator) { + v->array = flecs_alloc(allocator, size * elem_count); + } else { + v->array = ecs_os_malloc(size * elem_count); } + } + v->size = elem_count; +#ifdef FLECS_SANITIZE + v->elem_size = size; +#endif +} - flecs_wfree_n(world, ecs_column_t, table->column_count, - snapshot_table->data->columns); - ecs_os_free(snapshot_table->data); - flecs_type_free(world, &snapshot_table->type); +void ecs_vec_init_if( + ecs_vec_t *vec, + ecs_size_t size) +{ + ecs_san_assert(!vec->elem_size || vec->elem_size == size, ECS_INVALID_PARAMETER, NULL); + (void)vec; + (void)size; +#ifdef FLECS_SANITIZE + if (!vec->elem_size) { + ecs_assert(vec->count == 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(vec->size == 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(vec->array == NULL, ECS_INTERNAL_ERROR, NULL); + vec->elem_size = size; } +#endif } -/** Restore a snapshot */ -void ecs_snapshot_restore( - ecs_world_t *world, - ecs_snapshot_t *snapshot) +void ecs_vec_fini( + ecs_allocator_t *allocator, + ecs_vec_t *v, + ecs_size_t size) { - ecs_run_aperiodic(world, 0); - - if (flecs_entity_index_count(&snapshot->entity_index) > 0) { - /* Unfiltered snapshots have a copy of the entity index which is - * copied back entirely when the snapshot is restored */ - restore_unfiltered(world, snapshot); - } else { - restore_filtered(world, snapshot); + if (v->array) { + ecs_san_assert(!size || size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + if (allocator) { + flecs_free(allocator, size * v->size, v->array); + } else { + ecs_os_free(v->array); + } + v->array = NULL; + v->count = 0; + v->size = 0; } +} - ecs_vec_fini_t(NULL, &snapshot->tables, ecs_table_leaf_t); +ecs_vec_t* ecs_vec_reset( + ecs_allocator_t *allocator, + ecs_vec_t *v, + ecs_size_t size) +{ + if (!v->size) { + ecs_vec_init(allocator, v, size, 0); + } else { + ecs_san_assert(size == v->elem_size, ECS_INTERNAL_ERROR, NULL); + ecs_vec_clear(v); + } + return v; +} - ecs_os_free(snapshot); +void ecs_vec_clear( + ecs_vec_t *vec) +{ + vec->count = 0; } -ecs_iter_t ecs_snapshot_iter( - ecs_snapshot_t *snapshot) +ecs_vec_t ecs_vec_copy( + ecs_allocator_t *allocator, + const ecs_vec_t *v, + ecs_size_t size) { - ecs_snapshot_iter_t iter = { - .tables = snapshot->tables, - .index = 0 + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + void *array; + if (allocator) { + array = flecs_dup(allocator, size * v->size, v->array); + } else { + array = ecs_os_memdup(v->array, size * v->size); + } + return (ecs_vec_t) { + .count = v->count, + .size = v->size, + .array = array +#ifdef FLECS_SANITIZE + , .elem_size = size +#endif }; +} - return (ecs_iter_t){ - .world = snapshot->world, - .table_count = ecs_vec_count(&snapshot->tables), - .priv.iter.snapshot = iter, - .next = ecs_snapshot_next +ecs_vec_t ecs_vec_copy_shrink( + ecs_allocator_t *allocator, + const ecs_vec_t *v, + ecs_size_t size) +{ + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + int32_t count = v->count; + void *array = NULL; + if (count) { + if (allocator) { + array = flecs_dup(allocator, size * count, v->array); + } else { + array = ecs_os_memdup(v->array, size * count); + } + } + return (ecs_vec_t) { + .count = count, + .size = count, + .array = array +#ifdef FLECS_SANITIZE + , .elem_size = size +#endif }; } -bool ecs_snapshot_next( - ecs_iter_t *it) +void ecs_vec_reclaim( + ecs_allocator_t *allocator, + ecs_vec_t *v, + ecs_size_t size) { - ecs_snapshot_iter_t *iter = &it->priv.iter.snapshot; - ecs_table_leaf_t *tables = ecs_vec_first_t(&iter->tables, ecs_table_leaf_t); - int32_t count = ecs_vec_count(&iter->tables); - int32_t i; - - for (i = iter->index; i < count; i ++) { - ecs_table_t *table = tables[i].table; - if (!table) { - continue; + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + int32_t count = v->count; + if (count < v->size) { + if (count) { + if (allocator) { + v->array = flecs_realloc( + allocator, size * count, size * v->size, v->array); + } else { + v->array = ecs_os_realloc(v->array, size * count); + } + v->size = count; + } else { + ecs_vec_fini(allocator, v, size); } + } +} - ecs_data_t *data = tables[i].data; - - it->table = table; - it->count = ecs_table_count(table); - if (data) { - it->entities = ecs_vec_first(&data->entities); - } else { - it->entities = NULL; +void ecs_vec_set_size( + ecs_allocator_t *allocator, + ecs_vec_t *v, + ecs_size_t size, + int32_t elem_count) +{ + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + if (v->size != elem_count) { + if (elem_count < v->count) { + elem_count = v->count; } - ECS_BIT_SET(it->flags, EcsIterIsValid); - iter->index = i + 1; - - goto yield; + elem_count = flecs_next_pow_of_2(elem_count); + if (elem_count < 2) { + elem_count = 2; + } + if (elem_count != v->size) { + if (allocator) { + v->array = flecs_realloc( + allocator, size * elem_count, size * v->size, v->array); + } else { + v->array = ecs_os_realloc(v->array, size * elem_count); + } + v->size = elem_count; + } } +} - ECS_BIT_CLEAR(it->flags, EcsIterIsValid); - return false; +void ecs_vec_set_min_size( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count) +{ + if (elem_count > vec->size) { + ecs_vec_set_size(allocator, vec, size, elem_count); + } +} -yield: - ECS_BIT_CLEAR(it->flags, EcsIterIsValid); - return true; +void ecs_vec_set_min_count( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count) +{ + ecs_vec_set_min_size(allocator, vec, size, elem_count); + if (vec->count < elem_count) { + vec->count = elem_count; + } } -/** Cleanup snapshot */ -void ecs_snapshot_free( - ecs_snapshot_t *snapshot) +void ecs_vec_set_min_count_zeromem( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count) { - flecs_entity_index_fini(&snapshot->entity_index); + int32_t count = vec->count; + if (count < elem_count) { + ecs_vec_set_min_count(allocator, vec, size, elem_count); + ecs_os_memset(ECS_ELEM(vec->array, size, count), 0, + size * (elem_count - count)); + } +} - ecs_table_leaf_t *tables = ecs_vec_first_t(&snapshot->tables, ecs_table_leaf_t); - int32_t i, count = ecs_vec_count(&snapshot->tables); - for (i = 0; i < count; i ++) { - ecs_table_leaf_t *snapshot_table = &tables[i]; - ecs_table_t *table = snapshot_table->table; - if (table) { - ecs_data_t *data = snapshot_table->data; - if (data) { - flecs_table_clear_data(snapshot->world, table, data); - ecs_os_free(data); - } - flecs_type_free(snapshot->world, &snapshot_table->type); +void ecs_vec_set_count( + ecs_allocator_t *allocator, + ecs_vec_t *v, + ecs_size_t size, + int32_t elem_count) +{ + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + if (v->count != elem_count) { + if (v->size < elem_count) { + ecs_vec_set_size(allocator, v, size, elem_count); } - } - ecs_vec_fini_t(NULL, &snapshot->tables, ecs_table_leaf_t); - ecs_os_free(snapshot); + v->count = elem_count; + } } -#endif - -/** - * @file addons/stats.c - * @brief Stats addon. - */ - +void* ecs_vec_grow( + ecs_allocator_t *allocator, + ecs_vec_t *v, + ecs_size_t size, + int32_t elem_count) +{ + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(elem_count > 0, ECS_INTERNAL_ERROR, NULL); + int32_t count = v->count; + ecs_vec_set_count(allocator, v, size, count + elem_count); + return ECS_ELEM(v->array, size, count); +} -#ifdef FLECS_SYSTEM -/** - * @file addons/system/system.h - * @brief Internal types and functions for system addon. - */ +void* ecs_vec_append( + ecs_allocator_t *allocator, + ecs_vec_t *v, + ecs_size_t size) +{ + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + int32_t count = v->count; + if (v->size == count) { + ecs_vec_set_size(allocator, v, size, count + 1); + } + v->count = count + 1; + return ECS_ELEM(v->array, size, count); +} -#ifndef FLECS_SYSTEM_PRIVATE_H -#define FLECS_SYSTEM_PRIVATE_H +void ecs_vec_remove( + ecs_vec_t *v, + ecs_size_t size, + int32_t index) +{ + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(index < v->count, ECS_OUT_OF_RANGE, NULL); + if (index == --v->count) { + return; + } -#ifdef FLECS_SYSTEM + ecs_os_memcpy( + ECS_ELEM(v->array, size, index), + ECS_ELEM(v->array, size, v->count), + size); +} +void ecs_vec_remove_last( + ecs_vec_t *v) +{ + v->count --; +} -#define ecs_system_t_magic (0x65637383) -#define ecs_system_t_tag EcsSystem +int32_t ecs_vec_count( + const ecs_vec_t *v) +{ + return v->count; +} -extern ecs_mixins_t ecs_system_t_mixins; +int32_t ecs_vec_size( + const ecs_vec_t *v) +{ + return v->size; +} -typedef struct ecs_system_t { - ecs_header_t hdr; +void* ecs_vec_get( + const ecs_vec_t *v, + ecs_size_t size, + int32_t index) +{ + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(index >= 0, ECS_OUT_OF_RANGE, NULL); + ecs_assert(index < v->count, ECS_OUT_OF_RANGE, NULL); + return ECS_ELEM(v->array, size, index); +} - ecs_run_action_t run; /* See ecs_system_desc_t */ - ecs_iter_action_t action; /* See ecs_system_desc_t */ +void* ecs_vec_last( + const ecs_vec_t *v, + ecs_size_t size) +{ + ecs_san_assert(!v->elem_size || size == v->elem_size, + ECS_INVALID_PARAMETER, NULL); + return ECS_ELEM(v->array, size, v->count - 1); +} - ecs_query_t *query; /* System query */ - ecs_entity_t query_entity; /* Entity associated with query */ - ecs_entity_t tick_source; /* Tick source associated with system */ - - /* Schedule parameters */ - bool multi_threaded; - bool no_readonly; +void* ecs_vec_first( + const ecs_vec_t *v) +{ + return v->array; +} - int64_t invoke_count; /* Number of times system is invoked */ - ecs_ftime_t time_spent; /* Time spent on running system */ - ecs_ftime_t time_passed; /* Time passed since last invocation */ - int64_t last_frame; /* Last frame for which the system was considered */ + /** + * @file queries/api.c + * @brief User facing API for rules. + */ - void *ctx; /* Userdata for system */ - void *binding_ctx; /* Optional language binding context */ +#include - ecs_ctx_free_t ctx_free; - ecs_ctx_free_t binding_ctx_free; +/* Placeholder arrays for queries that only have $this variable */ +ecs_query_var_t flecs_this_array = { + .kind = EcsVarTable, + .table_id = EcsVarNone +}; +char *flecs_this_name_array = NULL; - /* Mixins */ - ecs_world_t *world; - ecs_entity_t entity; - ecs_poly_dtor_t dtor; -} ecs_system_t; +ecs_mixins_t ecs_query_t_mixins = { + .type_name = "ecs_query_t", + .elems = { + [EcsMixinWorld] = offsetof(ecs_query_impl_t, pub.real_world), + [EcsMixinEntity] = offsetof(ecs_query_impl_t, pub.entity), + [EcsMixinDtor] = offsetof(ecs_query_impl_t, dtor) + } +}; -/* Invoked when system becomes active / inactive */ -void ecs_system_activate( - ecs_world_t *world, - ecs_entity_t system, - bool activate, - const ecs_system_t *system_data); +int32_t ecs_query_find_var( + const ecs_query_t *q, + const char *name) +{ + flecs_poly_assert(q, ecs_query_t); -/* Internal function to run a system */ -ecs_entity_t ecs_run_intern( - ecs_world_t *world, - ecs_stage_t *stage, - ecs_entity_t system, - ecs_system_t *system_data, - int32_t stage_current, - int32_t stage_count, - ecs_ftime_t delta_time, - int32_t offset, - int32_t limit, - void *param); + ecs_query_impl_t *impl = flecs_query_impl(q); + ecs_var_id_t var_id = flecs_query_find_var_id(impl, name, EcsVarEntity); + if (var_id == EcsVarNone) { + if (q->flags & EcsQueryMatchThis) { + if (!ecs_os_strcmp(name, EcsThisName)) { + var_id = 0; + } + } + if (var_id == EcsVarNone) { + return -1; + } + } + return (int32_t)var_id; +} -#endif +const char* ecs_query_var_name( + const ecs_query_t *q, + int32_t var_id) +{ + flecs_poly_assert(q, ecs_query_t); -#endif + if (var_id) { + ecs_assert(var_id < flecs_query_impl(q)->var_count, + ECS_INVALID_PARAMETER, NULL); + return flecs_query_impl(q)->vars[var_id].name; + } else { + return EcsThisName; + } +} -#endif +bool ecs_query_var_is_entity( + const ecs_query_t *q, + int32_t var_id) +{ + flecs_poly_assert(q, ecs_query_t); -#ifdef FLECS_PIPELINE -/** - * @file addons/pipeline/pipeline.h - * @brief Internal functions/types for pipeline addon. - */ + return flecs_query_impl(q)->vars[var_id].kind == EcsVarEntity; +} -#ifndef FLECS_PIPELINE_PRIVATE_H -#define FLECS_PIPELINE_PRIVATE_H +static +int flecs_query_set_caching_policy( + ecs_query_impl_t *impl, + const ecs_query_desc_t *desc) +{ + ecs_query_cache_kind_t kind = desc->cache_kind; + bool group_order_by = desc->group_by || desc->group_by_callback || + desc->order_by || desc->order_by_callback; + + /* If caching policy is default, try to pick a policy that does the right + * thing in most cases. */ + if (kind == EcsQueryCacheDefault) { + if (desc->entity || group_order_by) { + /* If the query is created with an entity handle (typically + * indicating that the query is named or belongs to a system) the + * chance is very high that the query will be reused, so enable + * caching. + * Additionally, if the query uses features that require a cache + * such as group_by/order_by, also enable caching. */ + kind = EcsQueryCacheAuto; + } else { + /* Be conservative in other scenario's, as caching adds significant + * overhead to the cost of query creation which doesn't offset the + * benefit of faster iteration if it's only used once. */ + kind = EcsQueryCacheNone; + } + } + /* Don't cache query, even if it has cacheable terms */ + if (kind == EcsQueryCacheNone) { + impl->pub.cache_kind = EcsQueryCacheNone; + if (desc->group_by || desc->order_by) { + ecs_err("cannot create uncached query with group_by/order_by"); + return -1; + } + return 0; + } -/** Instruction data for pipeline. - * This type is the element type in the "ops" vector of a pipeline. */ -typedef struct ecs_pipeline_op_t { - int32_t offset; /* Offset in systems vector */ - int32_t count; /* Number of systems to run before next op */ - double time_spent; /* Time spent merging commands for sync point */ - int64_t commands_enqueued; /* Number of commands enqueued for sync point */ - bool multi_threaded; /* Whether systems can be ran multi threaded */ - bool no_readonly; /* Whether systems are staged or not */ -} ecs_pipeline_op_t; + /* Entire query must be cached */ + if (desc->cache_kind == EcsQueryCacheAll) { + if (impl->pub.flags & EcsQueryIsCacheable) { + impl->pub.cache_kind = EcsQueryCacheAll; + return 0; + } else { + ecs_err("cannot enforce QueryCacheAll, " + "query contains uncacheable terms"); + return -1; + } + } -struct ecs_pipeline_state_t { - ecs_query_t *query; /* Pipeline query */ - ecs_vec_t ops; /* Pipeline schedule */ - ecs_vec_t systems; /* Vector with system ids */ + /* Only cache terms that are cacheable */ + if (kind == EcsQueryCacheAuto) { + if (impl->pub.flags & EcsQueryIsCacheable) { + /* If all terms of the query are cacheable, just set the policy to + * All which simplifies work for the compiler. */ + impl->pub.cache_kind = EcsQueryCacheAll; + } else if (!(impl->pub.flags & EcsQueryHasCacheable)) { + /* Same for when the query has no cacheable terms */ + impl->pub.cache_kind = EcsQueryCacheNone; + } else { + /* Part of the query is cacheable. Make sure to only create a cache + * if the cacheable part of the query contains not just not/optional + * terms, as this would build a cache that contains all tables. */ + int32_t not_optional_terms = 0, cacheable_terms = 0; + if (!group_order_by) { + int32_t i, term_count = impl->pub.term_count; + const ecs_term_t *terms = impl->pub.terms; + for (i = 0; i < term_count; i ++) { + const ecs_term_t *term = &terms[i]; + if (term->flags_ & EcsTermIsCacheable) { + cacheable_terms ++; + if (term->oper == EcsNot || term->oper == EcsOptional) { + not_optional_terms ++; + } + } + } + } - ecs_entity_t last_system; /* Last system ran by pipeline */ - ecs_id_record_t *idr_inactive; /* Cached record for quick inactive test */ - int32_t match_count; /* Used to track of rebuild is necessary */ - int32_t rebuild_count; /* Number of pipeline rebuilds */ - ecs_iter_t *iters; /* Iterator for worker(s) */ - int32_t iter_count; + if (group_order_by || cacheable_terms != not_optional_terms) { + impl->pub.cache_kind = EcsQueryCacheAuto; + } else { + impl->pub.cache_kind = EcsQueryCacheNone; + } + } + } - /* Members for continuing pipeline iteration after pipeline rebuild */ - ecs_pipeline_op_t *cur_op; /* Current pipeline op */ - int32_t cur_i; /* Index in current result */ - int32_t ran_since_merge; /* Index in current op */ - bool no_readonly; /* Is pipeline in readonly mode */ -}; + return 0; +} -typedef struct EcsPipeline { - /* Stable ptr so threads can safely access while entity/components move */ - ecs_pipeline_state_t *state; -} EcsPipeline; +static +int flecs_query_create_cache( + ecs_query_impl_t *impl, + ecs_query_desc_t *desc) +{ + ecs_query_t *q = &impl->pub; + if (flecs_query_set_caching_policy(impl, desc)) { + return -1; + } -//////////////////////////////////////////////////////////////////////////////// -//// Pipeline API -//////////////////////////////////////////////////////////////////////////////// + if ((q->cache_kind != EcsQueryCacheNone) && !q->entity) { + /* Cached queries need an entity handle for observer components */ + q->entity = ecs_new(q->world); + desc->entity = q->entity; + } -bool flecs_pipeline_update( - ecs_world_t *world, - ecs_pipeline_state_t *pq, - bool start_of_frame); + if (q->cache_kind == EcsQueryCacheAll) { + /* Create query cache for all terms */ + if (!flecs_query_cache_init(impl, desc)) { + goto error; + } + } else if (q->cache_kind == EcsQueryCacheAuto) { + /* Query is partially cached */ + ecs_query_desc_t cache_desc = *desc; + ecs_os_memset_n(&cache_desc.terms, 0, ecs_term_t, FLECS_TERM_COUNT_MAX); + cache_desc.expr = NULL; -void flecs_run_pipeline( - ecs_world_t *world, - ecs_pipeline_state_t *pq, - ecs_ftime_t delta_time); + /* Maps field indices from cache to query */ + int8_t field_map[FLECS_TERM_COUNT_MAX]; -int32_t flecs_run_pipeline_ops( - ecs_world_t* world, - ecs_stage_t* stage, - int32_t stage_index, - int32_t stage_count, - ecs_ftime_t delta_time); + int32_t i, count = q->term_count, dst_count = 0, dst_field = 0; + ecs_term_t *terms = q->terms; + for (i = 0; i < count; i ++) { + ecs_term_t *term = &terms[i]; + if (term->flags_ & EcsTermIsCacheable) { + cache_desc.terms[dst_count] = *term; + field_map[dst_field] = flecs_ito(int8_t, term->field_index); + dst_count ++; + if (i) { + dst_field += term->field_index != term[-1].field_index; + } else { + dst_field ++; + } + } + } -//////////////////////////////////////////////////////////////////////////////// -//// Worker API -//////////////////////////////////////////////////////////////////////////////// + if (dst_count) { + if (!flecs_query_cache_init(impl, &cache_desc)) { + goto error; + } -void flecs_workers_progress( - ecs_world_t *world, - ecs_pipeline_state_t *pq, - ecs_ftime_t delta_time); + impl->cache->field_map = flecs_alloc_n(&impl->stage->allocator, + int8_t, FLECS_TERM_COUNT_MAX); -void flecs_create_worker_threads( - ecs_world_t *world); + ecs_os_memcpy_n(impl->cache->field_map, field_map, int8_t, dst_count); + } + } else { + /* Check if query has features that are unsupported for uncached */ + ecs_assert(q->cache_kind == EcsQueryCacheNone, ECS_INTERNAL_ERROR, NULL); + + if (!(q->flags & EcsQueryNested)) { + /* If uncached query is not create to populate a cached query, it + * should not have cascade modifiers */ + int32_t i, count = q->term_count; + ecs_term_t *terms = q->terms; + for (i = 0; i < count; i ++) { + ecs_term_t *term = &terms[i]; + if (term->src.id & EcsCascade) { + char *query_str = ecs_query_str(q); + ecs_err( + "cascade is unsupported for uncached query\n %s", + query_str); + ecs_os_free(query_str); + goto error; + } + } + } + } -void flecs_join_worker_threads( - ecs_world_t *world); + return 0; +error: + return -1; +} -void flecs_signal_workers( - ecs_world_t *world); +static +void flecs_query_fini( + ecs_query_impl_t *impl) +{ + ecs_stage_t *stage = impl->stage; + ecs_assert(stage != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_allocator_t *a = &stage->allocator; -void flecs_wait_for_sync( - ecs_world_t *world); + if (impl->ctx_free) { + impl->ctx_free(impl->pub.ctx); + } -#endif + if (impl->binding_ctx_free) { + impl->binding_ctx_free(impl->pub.binding_ctx); + } -#endif + if (impl->vars != &flecs_this_array) { + flecs_free(a, (ECS_SIZEOF(ecs_query_var_t) + ECS_SIZEOF(char*)) * + impl->var_size, impl->vars); + flecs_name_index_fini(&impl->tvar_index); + flecs_name_index_fini(&impl->evar_index); + } -#ifdef FLECS_STATS + flecs_free_n(a, ecs_query_op_t, impl->op_count, impl->ops); + flecs_free_n(a, ecs_var_id_t, impl->pub.field_count, impl->src_vars); + flecs_free_n(a, int32_t, impl->pub.field_count, impl->monitor); -#define ECS_GAUGE_RECORD(m, t, value)\ - flecs_gauge_record(m, t, (ecs_float_t)(value)) + ecs_query_t *q = &impl->pub; + int i, count = q->term_count; + for (i = 0; i < count; i ++) { + ecs_term_t *term = &q->terms[i]; + if (!(term->flags_ & EcsTermKeepAlive)) { + continue; + } -#define ECS_COUNTER_RECORD(m, t, value)\ - flecs_counter_record(m, t, (double)(value)) + ecs_id_record_t *idr = flecs_id_record_get(q->real_world, term->id); + if (idr) { + if (!(q->world->flags & EcsWorldQuit)) { + if (ecs_os_has_threading()) { + int32_t idr_keep_alive = ecs_os_adec(&idr->keep_alive); + ecs_assert(idr_keep_alive >= 0, ECS_INTERNAL_ERROR, NULL); + (void)idr_keep_alive; + } else { + idr->keep_alive --; + ecs_assert(idr->keep_alive >= 0, ECS_INTERNAL_ERROR, NULL); + } + } + } + } -#define ECS_METRIC_FIRST(stats)\ - ECS_CAST(ecs_metric_t*, ECS_OFFSET(&stats->first_, ECS_SIZEOF(int64_t))) + if (impl->tokens) { + flecs_free(&impl->stage->allocator, impl->tokens_len, impl->tokens); + } -#define ECS_METRIC_LAST(stats)\ - ECS_CAST(ecs_metric_t*, ECS_OFFSET(&stats->last_, -ECS_SIZEOF(ecs_metric_t))) + if (impl->cache) { + flecs_free_n(a, int8_t, FLECS_TERM_COUNT_MAX, impl->cache->field_map); + flecs_query_cache_fini(impl); + } -static -int32_t t_next( - int32_t t) -{ - return (t + 1) % ECS_STAT_WINDOW; + flecs_poly_fini(impl, ecs_query_t); + flecs_bfree(&stage->allocators.query_impl, impl); } static -int32_t t_prev( - int32_t t) -{ - return (t - 1 + ECS_STAT_WINDOW) % ECS_STAT_WINDOW; +void flecs_query_poly_fini(void *ptr) { + flecs_query_fini(ptr); } static -void flecs_gauge_record( - ecs_metric_t *m, - int32_t t, - ecs_float_t value) +void flecs_query_add_self_ref( + ecs_query_t *q) { - m->gauge.avg[t] = value; - m->gauge.min[t] = value; - m->gauge.max[t] = value; -} + if (q->entity) { + int32_t t, term_count = q->term_count; + ecs_term_t *terms = q->terms; -static -double flecs_counter_record( - ecs_metric_t *m, - int32_t t, - double value) -{ - int32_t tp = t_prev(t); - double prev = m->counter.value[tp]; - m->counter.value[t] = value; - double gauge_value = value - prev; - if (gauge_value < 0) { - gauge_value = 0; /* Counters are monotonically increasing */ + for (t = 0; t < term_count; t ++) { + ecs_term_t *term = &terms[t]; + if (ECS_TERM_REF_ID(&term->src) == q->entity) { + ecs_add_id(q->world, q->entity, term->id); + } + } } - flecs_gauge_record(m, t, (ecs_float_t)gauge_value); - return gauge_value; -} - -static -void flecs_metric_print( - const char *name, - ecs_float_t value) -{ - ecs_size_t len = ecs_os_strlen(name); - ecs_trace("%s: %*s %.2f", name, 32 - len, "", (double)value); } -static -void flecs_gauge_print( - const char *name, - int32_t t, - const ecs_metric_t *m) +void ecs_query_fini( + ecs_query_t *q) { - flecs_metric_print(name, m->gauge.avg[t]); -} + flecs_poly_assert(q, ecs_query_t); -static -void flecs_counter_print( - const char *name, - int32_t t, - const ecs_metric_t *m) -{ - flecs_metric_print(name, m->counter.rate.avg[t]); + if (q->entity) { + /* If query is associated with entity, use poly dtor path */ + ecs_delete(q->world, q->entity); + } else { + flecs_query_fini(flecs_query_impl(q)); + } } -void ecs_metric_reduce( - ecs_metric_t *dst, - const ecs_metric_t *src, - int32_t t_dst, - int32_t t_src) +ecs_query_t* ecs_query_init( + ecs_world_t *world, + const ecs_query_desc_t *const_desc) { - ecs_check(dst != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(src != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_world_t *world_arg = world; + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_query_impl_t *result = flecs_bcalloc(&stage->allocators.query_impl); + flecs_poly_init(result, ecs_query_t); + + ecs_query_desc_t desc = *const_desc; + ecs_entity_t entity = const_desc->entity; - bool min_set = false; - dst->gauge.avg[t_dst] = 0; - dst->gauge.min[t_dst] = 0; - dst->gauge.max[t_dst] = 0; + if (entity) { + /* Remove existing query if entity has one */ + bool deferred = false; + if (ecs_is_deferred(world)) { + deferred = true; + /* Ensures that remove operation doesn't get applied after bind */ + ecs_defer_suspend(world); + } + ecs_remove_pair(world, entity, ecs_id(EcsPoly), EcsQuery); + if (deferred) { + ecs_defer_resume(world); + } + } - ecs_float_t fwindow = (ecs_float_t)ECS_STAT_WINDOW; + /* Initialize the query */ + result->pub.entity = entity; + result->pub.real_world = world; + result->pub.world = world_arg; + result->stage = stage; - int32_t i; - for (i = 0; i < ECS_STAT_WINDOW; i ++) { - int32_t t = (t_src + i) % ECS_STAT_WINDOW; - dst->gauge.avg[t_dst] += src->gauge.avg[t] / fwindow; + ecs_assert(flecs_poly_is(result->pub.real_world, ecs_world_t), + ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_poly_is(result->stage, ecs_stage_t), + ECS_INTERNAL_ERROR, NULL); - if (!min_set || (src->gauge.min[t] < dst->gauge.min[t_dst])) { - dst->gauge.min[t_dst] = src->gauge.min[t]; - min_set = true; - } - if ((src->gauge.max[t] > dst->gauge.max[t_dst])) { - dst->gauge.max[t_dst] = src->gauge.max[t]; - } + /* Validate input, translate to canonical query representation */ + if (flecs_query_finalize_query(world, &result->pub, &desc)) { + goto error; } - dst->counter.value[t_dst] = src->counter.value[t_src]; + /* If query terms have itself as source, add term ids to self. This makes it + * easy to attach components to queries, which is one of the ways + * applications can attach data to systems. */ + flecs_query_add_self_ref(&result->pub); -error: - return; -} + /* Initialize static context */ + result->pub.ctx = const_desc->ctx; + result->pub.binding_ctx = const_desc->binding_ctx; + result->ctx_free = const_desc->ctx_free; + result->binding_ctx_free = const_desc->binding_ctx_free; + result->dtor = flecs_query_poly_fini; + result->cache = NULL; -void ecs_metric_reduce_last( - ecs_metric_t *m, - int32_t prev, - int32_t count) -{ - ecs_check(m != NULL, ECS_INVALID_PARAMETER, NULL); - int32_t t = t_next(prev); + /* Initialize query cache if necessary */ + if (flecs_query_create_cache(result, &desc)) { + goto error; + } - if (m->gauge.min[t] < m->gauge.min[prev]) { - m->gauge.min[prev] = m->gauge.min[t]; + if (flecs_query_compile(world, stage, result)) { + goto error; } - if (m->gauge.max[t] > m->gauge.max[prev]) { - m->gauge.max[prev] = m->gauge.max[t]; + /* Entity could've been set by finalize query if query is cached */ + entity = result->pub.entity; + if (entity) { + EcsPoly *poly = flecs_poly_bind(world, entity, ecs_query_t); + poly->poly = result; + flecs_poly_modified(world, entity, ecs_query_t); } - ecs_float_t fcount = (ecs_float_t)(count + 1); - ecs_float_t cur = m->gauge.avg[prev]; - ecs_float_t next = m->gauge.avg[t]; + return &result->pub; +error: + result->pub.entity = 0; + ecs_query_fini(&result->pub); + return NULL; +} - cur *= ((fcount - 1) / fcount); - next *= 1 / fcount; +bool ecs_query_has( + ecs_query_t *q, + ecs_entity_t entity, + ecs_iter_t *it) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_check(q->flags & EcsQueryMatchThis, ECS_INVALID_PARAMETER, NULL); - m->gauge.avg[prev] = cur + next; - m->counter.value[prev] = m->counter.value[t]; + *it = ecs_query_iter(q->world, q); + ecs_iter_set_var(it, 0, entity); + return ecs_query_next(it); +error: + return false; +} + +bool ecs_query_has_table( + ecs_query_t *q, + ecs_table_t *table, + ecs_iter_t *it) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_check(q->flags & EcsQueryMatchThis, ECS_INVALID_PARAMETER, NULL); + *it = ecs_query_iter(q->world, q); + ecs_iter_set_var_as_table(it, 0, table); + return ecs_query_next(it); error: - return; + return false; } -void ecs_metric_copy( - ecs_metric_t *m, - int32_t dst, - int32_t src) +bool ecs_query_has_range( + ecs_query_t *q, + ecs_table_range_t *range, + ecs_iter_t *it) { - ecs_check(m != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(dst != src, ECS_INVALID_PARAMETER, NULL); + flecs_poly_assert(q, ecs_query_t); - m->gauge.avg[dst] = m->gauge.avg[src]; - m->gauge.min[dst] = m->gauge.min[src]; - m->gauge.max[dst] = m->gauge.max[src]; - m->counter.value[dst] = m->counter.value[src]; + if (q->flags & EcsQueryMatchThis) { + if (range->table) { + if ((range->offset + range->count) > ecs_table_count(range->table)) { + return false; + } + } + } -error: - return; + *it = ecs_query_iter(q->world, q); + if (q->flags & EcsQueryMatchThis) { + ecs_iter_set_var_as_range(it, 0, range); + } + + return ecs_query_next(it); } -static -void flecs_stats_reduce( - ecs_metric_t *dst_cur, - ecs_metric_t *dst_last, - ecs_metric_t *src_cur, - int32_t t_dst, - int32_t t_src) +ecs_query_count_t ecs_query_count( + const ecs_query_t *q) { - for (; dst_cur <= dst_last; dst_cur ++, src_cur ++) { - ecs_metric_reduce(dst_cur, src_cur, t_dst, t_src); + flecs_poly_assert(q, ecs_query_t); + ecs_query_count_t result = {0}; + + if (!(q->flags & EcsQueryMatchThis)) { + return result; + } + + ecs_run_aperiodic(q->world, EcsAperiodicEmptyTables); + + ecs_query_impl_t *impl = flecs_query_impl(q); + if (impl->cache && q->flags & EcsQueryIsCacheable) { + result.results = flecs_query_cache_table_count(impl->cache); + result.entities = flecs_query_cache_entity_count(impl->cache); + result.tables = flecs_query_cache_table_count(impl->cache); + result.empty_tables = flecs_query_cache_empty_table_count(impl->cache); + } else { + ecs_iter_t it = flecs_query_iter(q->world, q); + it.flags |= EcsIterNoData; + + while (ecs_query_next(&it)) { + result.results ++; + result.entities += it.count; + ecs_iter_skip(&it); + } } + + return result; } -static -void flecs_stats_reduce_last( - ecs_metric_t *dst_cur, - ecs_metric_t *dst_last, - ecs_metric_t *src_cur, - int32_t t_dst, - int32_t t_src, - int32_t count) +bool ecs_query_is_true( + const ecs_query_t *q) { - int32_t t_dst_next = t_next(t_dst); - for (; dst_cur <= dst_last; dst_cur ++, src_cur ++) { - /* Reduce into previous value */ - ecs_metric_reduce_last(dst_cur, t_dst, count); + flecs_poly_assert(q, ecs_query_t); - /* Restore old value */ - dst_cur->gauge.avg[t_dst_next] = src_cur->gauge.avg[t_src]; - dst_cur->gauge.min[t_dst_next] = src_cur->gauge.min[t_src]; - dst_cur->gauge.max[t_dst_next] = src_cur->gauge.max[t_src]; - dst_cur->counter.value[t_dst_next] = src_cur->counter.value[t_src]; + ecs_run_aperiodic(q->world, EcsAperiodicEmptyTables); + + ecs_query_impl_t *impl = flecs_query_impl(q); + if (impl->cache && q->flags & EcsQueryIsCacheable) { + return flecs_query_cache_table_count(impl->cache) != 0; + } else { + ecs_iter_t it = flecs_query_iter(q->world, q); + return ecs_iter_is_true(&it); } } -static -void flecs_stats_repeat_last( - ecs_metric_t *cur, - ecs_metric_t *last, - int32_t t) +int32_t ecs_query_match_count( + const ecs_query_t *q) { - int32_t prev = t_prev(t); - for (; cur <= last; cur ++) { - ecs_metric_copy(cur, t, prev); + flecs_poly_assert(q, ecs_query_t); + + ecs_query_impl_t *impl = flecs_query_impl(q); + if (!impl->cache) { + return 0; + } else { + return impl->cache->match_count; } } -static -void flecs_stats_copy_last( - ecs_metric_t *dst_cur, - ecs_metric_t *dst_last, - ecs_metric_t *src_cur, - int32_t t_dst, - int32_t t_src) -{ - for (; dst_cur <= dst_last; dst_cur ++, src_cur ++) { - dst_cur->gauge.avg[t_dst] = src_cur->gauge.avg[t_src]; - dst_cur->gauge.min[t_dst] = src_cur->gauge.min[t_src]; - dst_cur->gauge.max[t_dst] = src_cur->gauge.max[t_src]; - dst_cur->counter.value[t_dst] = src_cur->counter.value[t_src]; - } -} - -void ecs_world_stats_get( - const ecs_world_t *world, - ecs_world_stats_t *s) +const ecs_query_t* ecs_query_get_cache_query( + const ecs_query_t *q) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); - - world = ecs_get_world(world); - - int32_t t = s->t = t_next(s->t); - - double delta_frame_count = - ECS_COUNTER_RECORD(&s->frame.frame_count, t, world->info.frame_count_total); - ECS_COUNTER_RECORD(&s->frame.merge_count, t, world->info.merge_count_total); - ECS_COUNTER_RECORD(&s->frame.rematch_count, t, world->info.rematch_count_total); - ECS_COUNTER_RECORD(&s->frame.pipeline_build_count, t, world->info.pipeline_build_count_total); - ECS_COUNTER_RECORD(&s->frame.systems_ran, t, world->info.systems_ran_frame); - ECS_COUNTER_RECORD(&s->frame.observers_ran, t, world->info.observers_ran_frame); - ECS_COUNTER_RECORD(&s->frame.event_emit_count, t, world->event_id); - - double delta_world_time = - ECS_COUNTER_RECORD(&s->performance.world_time_raw, t, world->info.world_time_total_raw); - ECS_COUNTER_RECORD(&s->performance.world_time, t, world->info.world_time_total); - ECS_COUNTER_RECORD(&s->performance.frame_time, t, world->info.frame_time_total); - ECS_COUNTER_RECORD(&s->performance.system_time, t, world->info.system_time_total); - ECS_COUNTER_RECORD(&s->performance.emit_time, t, world->info.emit_time_total); - ECS_COUNTER_RECORD(&s->performance.merge_time, t, world->info.merge_time_total); - ECS_COUNTER_RECORD(&s->performance.rematch_time, t, world->info.rematch_time_total); - ECS_GAUGE_RECORD(&s->performance.delta_time, t, delta_world_time); - if (ECS_NEQZERO(delta_world_time) && ECS_NEQZERO(delta_frame_count)) { - ECS_GAUGE_RECORD(&s->performance.fps, t, (double)1 / (delta_world_time / (double)delta_frame_count)); + ecs_query_impl_t *impl = flecs_query_impl(q); + if (!impl->cache) { + return NULL; } else { - ECS_GAUGE_RECORD(&s->performance.fps, t, 0); - } - - ECS_GAUGE_RECORD(&s->entities.count, t, flecs_entities_count(world)); - ECS_GAUGE_RECORD(&s->entities.not_alive_count, t, flecs_entities_not_alive_count(world)); - - ECS_GAUGE_RECORD(&s->components.tag_count, t, world->info.tag_id_count); - ECS_GAUGE_RECORD(&s->components.component_count, t, world->info.component_id_count); - ECS_GAUGE_RECORD(&s->components.pair_count, t, world->info.pair_id_count); - ECS_GAUGE_RECORD(&s->components.type_count, t, ecs_sparse_count(&world->type_info)); - ECS_COUNTER_RECORD(&s->components.create_count, t, world->info.id_create_total); - ECS_COUNTER_RECORD(&s->components.delete_count, t, world->info.id_delete_total); - - ECS_GAUGE_RECORD(&s->queries.query_count, t, ecs_count_id(world, EcsQuery)); - ECS_GAUGE_RECORD(&s->queries.observer_count, t, ecs_count_id(world, EcsObserver)); - if (ecs_is_alive(world, EcsSystem)) { - ECS_GAUGE_RECORD(&s->queries.system_count, t, ecs_count_id(world, EcsSystem)); + return impl->cache->query; } - ECS_COUNTER_RECORD(&s->tables.create_count, t, world->info.table_create_total); - ECS_COUNTER_RECORD(&s->tables.delete_count, t, world->info.table_delete_total); - ECS_GAUGE_RECORD(&s->tables.count, t, world->info.table_count); - ECS_GAUGE_RECORD(&s->tables.empty_count, t, world->info.empty_table_count); - - ECS_COUNTER_RECORD(&s->commands.add_count, t, world->info.cmd.add_count); - ECS_COUNTER_RECORD(&s->commands.remove_count, t, world->info.cmd.remove_count); - ECS_COUNTER_RECORD(&s->commands.delete_count, t, world->info.cmd.delete_count); - ECS_COUNTER_RECORD(&s->commands.clear_count, t, world->info.cmd.clear_count); - ECS_COUNTER_RECORD(&s->commands.set_count, t, world->info.cmd.set_count); - ECS_COUNTER_RECORD(&s->commands.get_mut_count, t, world->info.cmd.get_mut_count); - ECS_COUNTER_RECORD(&s->commands.modified_count, t, world->info.cmd.modified_count); - ECS_COUNTER_RECORD(&s->commands.other_count, t, world->info.cmd.other_count); - ECS_COUNTER_RECORD(&s->commands.discard_count, t, world->info.cmd.discard_count); - ECS_COUNTER_RECORD(&s->commands.batched_entity_count, t, world->info.cmd.batched_entity_count); - ECS_COUNTER_RECORD(&s->commands.batched_count, t, world->info.cmd.batched_command_count); - - int64_t outstanding_allocs = ecs_os_api_malloc_count + - ecs_os_api_calloc_count - ecs_os_api_free_count; - ECS_COUNTER_RECORD(&s->memory.alloc_count, t, ecs_os_api_malloc_count + ecs_os_api_calloc_count); - ECS_COUNTER_RECORD(&s->memory.realloc_count, t, ecs_os_api_realloc_count); - ECS_COUNTER_RECORD(&s->memory.free_count, t, ecs_os_api_free_count); - ECS_GAUGE_RECORD(&s->memory.outstanding_alloc_count, t, outstanding_allocs); +} - outstanding_allocs = ecs_block_allocator_alloc_count - ecs_block_allocator_free_count; - ECS_COUNTER_RECORD(&s->memory.block_alloc_count, t, ecs_block_allocator_alloc_count); - ECS_COUNTER_RECORD(&s->memory.block_free_count, t, ecs_block_allocator_free_count); - ECS_GAUGE_RECORD(&s->memory.block_outstanding_alloc_count, t, outstanding_allocs); + /** + * @file query/util.c + * @brief Query utilities. + */ - outstanding_allocs = ecs_stack_allocator_alloc_count - ecs_stack_allocator_free_count; - ECS_COUNTER_RECORD(&s->memory.stack_alloc_count, t, ecs_stack_allocator_alloc_count); - ECS_COUNTER_RECORD(&s->memory.stack_free_count, t, ecs_stack_allocator_free_count); - ECS_GAUGE_RECORD(&s->memory.stack_outstanding_alloc_count, t, outstanding_allocs); -#ifdef FLECS_HTTP - ECS_COUNTER_RECORD(&s->http.request_received_count, t, ecs_http_request_received_count); - ECS_COUNTER_RECORD(&s->http.request_invalid_count, t, ecs_http_request_invalid_count); - ECS_COUNTER_RECORD(&s->http.request_handled_ok_count, t, ecs_http_request_handled_ok_count); - ECS_COUNTER_RECORD(&s->http.request_handled_error_count, t, ecs_http_request_handled_error_count); - ECS_COUNTER_RECORD(&s->http.request_not_handled_count, t, ecs_http_request_not_handled_count); - ECS_COUNTER_RECORD(&s->http.request_preflight_count, t, ecs_http_request_preflight_count); - ECS_COUNTER_RECORD(&s->http.send_ok_count, t, ecs_http_send_ok_count); - ECS_COUNTER_RECORD(&s->http.send_error_count, t, ecs_http_send_error_count); - ECS_COUNTER_RECORD(&s->http.busy_count, t, ecs_http_busy_count); -#endif +ecs_query_lbl_t flecs_itolbl(int64_t val) { + return flecs_ito(int16_t, val); +} -error: - return; +ecs_var_id_t flecs_itovar(int64_t val) { + return flecs_ito(uint8_t, val); } -void ecs_world_stats_reduce( - ecs_world_stats_t *dst, - const ecs_world_stats_t *src) -{ - flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), - ECS_METRIC_FIRST(src), (dst->t = t_next(dst->t)), src->t); +ecs_var_id_t flecs_utovar(uint64_t val) { + return flecs_uto(uint8_t, val); } -void ecs_world_stats_reduce_last( - ecs_world_stats_t *dst, - const ecs_world_stats_t *src, - int32_t count) +bool flecs_term_is_builtin_pred( + ecs_term_t *term) { - flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), - ECS_METRIC_FIRST(src), (dst->t = t_prev(dst->t)), src->t, count); + if (term->first.id & EcsIsEntity) { + ecs_entity_t id = ECS_TERM_REF_ID(&term->first); + if (id == EcsPredEq || id == EcsPredMatch || id == EcsPredLookup) { + return true; + } + } + return false; } -void ecs_world_stats_repeat_last( - ecs_world_stats_t *stats) +const char* flecs_term_ref_var_name( + ecs_term_ref_t *ref) { - flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats), - (stats->t = t_next(stats->t))); + if (!(ref->id & EcsIsVariable)) { + return NULL; + } + + if (ECS_TERM_REF_ID(ref) == EcsThis) { + return EcsThisName; + } + + return ref->name; } -void ecs_world_stats_copy_last( - ecs_world_stats_t *dst, - const ecs_world_stats_t *src) +bool flecs_term_ref_is_wildcard( + ecs_term_ref_t *ref) { - flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), - ECS_METRIC_FIRST(src), dst->t, t_next(src->t)); + if ((ref->id & EcsIsVariable) && + ((ECS_TERM_REF_ID(ref) == EcsWildcard) || (ECS_TERM_REF_ID(ref) == EcsAny))) + { + return true; + } + return false; } -void ecs_query_stats_get( - const ecs_world_t *world, - const ecs_query_t *query, - ecs_query_stats_t *s) +bool flecs_term_is_fixed_id( + ecs_query_t *q, + ecs_term_t *term) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); - (void)world; + /* Transitive/inherited terms have variable ids */ + if (term->flags_ & (EcsTermTransitive|EcsTermIdInherited)) { + return false; + } - int32_t t = s->t = t_next(s->t); + /* Or terms can match different ids */ + if (term->oper == EcsOr) { + return false; + } + if ((term != q->terms) && term[-1].oper == EcsOr) { + return false; + } - if (query->filter.flags & EcsFilterMatchThis) { - ECS_GAUGE_RECORD(&s->matched_entity_count, t, - ecs_query_entity_count(query)); - ECS_GAUGE_RECORD(&s->matched_table_count, t, - ecs_query_table_count(query)); - ECS_GAUGE_RECORD(&s->matched_empty_table_count, t, - ecs_query_empty_table_count(query)); - } else { - ECS_GAUGE_RECORD(&s->matched_entity_count, t, 0); - ECS_GAUGE_RECORD(&s->matched_table_count, t, 0); - ECS_GAUGE_RECORD(&s->matched_empty_table_count, t, 0); + /* Wildcards can assume different ids */ + if (ecs_id_is_wildcard(term->id)) { + return false; } -error: - return; + /* Any terms can have fixed ids, but they require special handling */ + if (term->flags_ & (EcsTermMatchAny|EcsTermMatchAnySrc)) { + return false; + } + + /* First terms that are Not or Optional require special handling */ + if (term->oper == EcsNot || term->oper == EcsOptional) { + if (term == q->terms) { + return false; + } + } + + return true; } -void ecs_query_stats_reduce( - ecs_query_stats_t *dst, - const ecs_query_stats_t *src) +bool flecs_term_is_or( + const ecs_query_t *q, + const ecs_term_t *term) { - flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), - ECS_METRIC_FIRST(src), (dst->t = t_next(dst->t)), src->t); + bool first_term = term == q->terms; + return (term->oper == EcsOr) || (!first_term && term[-1].oper == EcsOr); } -void ecs_query_stats_reduce_last( - ecs_query_stats_t *dst, - const ecs_query_stats_t *src, - int32_t count) +ecs_flags16_t flecs_query_ref_flags( + ecs_flags16_t flags, + ecs_flags16_t kind) { - flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), - ECS_METRIC_FIRST(src), (dst->t = t_prev(dst->t)), src->t, count); + return (flags >> kind) & (EcsQueryIsVar | EcsQueryIsEntity); } -void ecs_query_stats_repeat_last( - ecs_query_stats_t *stats) +bool flecs_query_is_written( + ecs_var_id_t var_id, + uint64_t written) { - flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats), - (stats->t = t_next(stats->t))); + if (var_id == EcsVarNone) { + return true; + } + + ecs_assert(var_id < EcsQueryMaxVarCount, ECS_INTERNAL_ERROR, NULL); + return (written & (1ull << var_id)) != 0; } -void ecs_query_stats_copy_last( - ecs_query_stats_t *dst, - const ecs_query_stats_t *src) +void flecs_query_write( + ecs_var_id_t var_id, + uint64_t *written) { - flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), - ECS_METRIC_FIRST(src), dst->t, t_next(src->t)); + ecs_assert(var_id < EcsQueryMaxVarCount, ECS_INTERNAL_ERROR, NULL); + *written |= (1ull << var_id); } -#ifdef FLECS_SYSTEM - -bool ecs_system_stats_get( - const ecs_world_t *world, - ecs_entity_t system, - ecs_system_stats_t *s) +void flecs_query_write_ctx( + ecs_var_id_t var_id, + ecs_query_compile_ctx_t *ctx, + bool cond_write) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(system != 0, ECS_INVALID_PARAMETER, NULL); - - world = ecs_get_world(world); - - const ecs_system_t *ptr = ecs_poly_get(world, system, ecs_system_t); - if (!ptr) { - return false; + bool is_written = flecs_query_is_written(var_id, ctx->written); + flecs_query_write(var_id, &ctx->written); + if (!is_written) { + if (cond_write) { + flecs_query_write(var_id, &ctx->cond_written); + } } - - ecs_query_stats_get(world, ptr->query, &s->query); - int32_t t = s->query.t; - - ECS_COUNTER_RECORD(&s->time_spent, t, ptr->time_spent); - ECS_COUNTER_RECORD(&s->invoke_count, t, ptr->invoke_count); - - s->task = !(ptr->query->filter.flags & EcsFilterMatchThis); - - return true; -error: - return false; } -void ecs_system_stats_reduce( - ecs_system_stats_t *dst, - const ecs_system_stats_t *src) +bool flecs_ref_is_written( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t kind, + uint64_t written) { - ecs_query_stats_reduce(&dst->query, &src->query); - dst->task = src->task; - flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), - ECS_METRIC_FIRST(src), dst->query.t, src->query.t); + ecs_flags16_t flags = flecs_query_ref_flags(op->flags, kind); + if (flags & EcsQueryIsEntity) { + ecs_assert(!(flags & EcsQueryIsVar), ECS_INTERNAL_ERROR, NULL); + if (ref->entity) { + return true; + } + } else if (flags & EcsQueryIsVar) { + return flecs_query_is_written(ref->var, written); + } + return false; } -void ecs_system_stats_reduce_last( - ecs_system_stats_t *dst, - const ecs_system_stats_t *src, - int32_t count) +ecs_allocator_t* flecs_query_get_allocator( + const ecs_iter_t *it) { - ecs_query_stats_reduce_last(&dst->query, &src->query, count); - dst->task = src->task; - flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), - ECS_METRIC_FIRST(src), dst->query.t, src->query.t, count); + ecs_world_t *world = it->world; + if (flecs_poly_is(world, ecs_world_t)) { + return &world->allocator; + } else { + ecs_assert(flecs_poly_is(world, ecs_stage_t), ECS_INTERNAL_ERROR, NULL); + return &((ecs_stage_t*)world)->allocator; + } } -void ecs_system_stats_repeat_last( - ecs_system_stats_t *stats) +const char* flecs_query_op_str( + uint16_t kind) { - ecs_query_stats_repeat_last(&stats->query); - flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats), - (stats->query.t)); + switch(kind) { + case EcsQueryAnd: return "and "; + case EcsQueryAndAny: return "andany "; + case EcsQueryTriv: return "triv "; + case EcsQueryCache: return "cache "; + case EcsQueryIsCache: return "xcache "; + case EcsQueryOnlyAny: return "any "; + case EcsQueryUp: return "up "; + case EcsQuerySelfUp: return "selfup "; + case EcsQueryWith: return "with "; + case EcsQueryTrav: return "trav "; + case EcsQueryAndFrom: return "andfrom "; + case EcsQueryOrFrom: return "orfrom "; + case EcsQueryNotFrom: return "notfrom "; + case EcsQueryIds: return "ids "; + case EcsQueryIdsRight: return "idsr "; + case EcsQueryIdsLeft: return "idsl "; + case EcsQueryEach: return "each "; + case EcsQueryStore: return "store "; + case EcsQueryReset: return "reset "; + case EcsQueryOr: return "or "; + case EcsQueryOptional: return "option "; + case EcsQueryIfVar: return "ifvar "; + case EcsQueryIfSet: return "ifset "; + case EcsQueryEnd: return "end "; + case EcsQueryNot: return "not "; + case EcsQueryPredEq: return "eq "; + case EcsQueryPredNeq: return "neq "; + case EcsQueryPredEqName: return "eq_nm "; + case EcsQueryPredNeqName: return "neq_nm "; + case EcsQueryPredEqMatch: return "eq_m "; + case EcsQueryPredNeqMatch: return "neq_m "; + case EcsQueryMemberEq: return "membereq "; + case EcsQueryMemberNeq: return "memberneq "; + case EcsQueryToggle: return "toggle "; + case EcsQueryToggleOption: return "togglopt "; + case EcsQueryUnionEq: return "union "; + case EcsQueryUnionEqWith: return "union_w "; + case EcsQueryUnionNeq: return "unionneq "; + case EcsQueryUnionEqUp: return "union_up "; + case EcsQueryUnionEqSelfUp: return "union_sup "; + case EcsQueryLookup: return "lookup "; + case EcsQuerySetVars: return "setvars "; + case EcsQuerySetThis: return "setthis "; + case EcsQuerySetFixed: return "setfix "; + case EcsQuerySetIds: return "setids "; + case EcsQuerySetId: return "setid "; + case EcsQueryContain: return "contain "; + case EcsQueryPairEq: return "pair_eq "; + case EcsQueryYield: return "yield "; + case EcsQueryNothing: return "nothing "; + default: return "!invalid "; + } +} + +static +int32_t flecs_query_op_ref_str( + const ecs_query_impl_t *query, + ecs_query_ref_t *ref, + ecs_flags16_t flags, + ecs_strbuf_t *buf) +{ + int32_t color_chars = 0; + if (flags & EcsQueryIsVar) { + ecs_assert(ref->var < query->var_count, ECS_INTERNAL_ERROR, NULL); + ecs_query_var_t *var = &query->vars[ref->var]; + ecs_strbuf_appendlit(buf, "#[green]$#[reset]"); + if (var->kind == EcsVarTable) { + ecs_strbuf_appendch(buf, '['); + } + ecs_strbuf_appendlit(buf, "#[green]"); + if (var->name) { + ecs_strbuf_appendstr(buf, var->name); + } else { + if (var->id) { +#ifdef FLECS_DEBUG + if (var->label) { + ecs_strbuf_appendstr(buf, var->label); + ecs_strbuf_appendch(buf, '\''); + } +#endif + ecs_strbuf_append(buf, "%d", var->id); + } else { + ecs_strbuf_appendlit(buf, "this"); + } + } + ecs_strbuf_appendlit(buf, "#[reset]"); + if (var->kind == EcsVarTable) { + ecs_strbuf_appendch(buf, ']'); + } + color_chars = ecs_os_strlen("#[green]#[reset]#[green]#[reset]"); + } else if (flags & EcsQueryIsEntity) { + char *path = ecs_get_path(query->pub.world, ref->entity); + ecs_strbuf_appendlit(buf, "#[blue]"); + ecs_strbuf_appendstr(buf, path); + ecs_strbuf_appendlit(buf, "#[reset]"); + ecs_os_free(path); + color_chars = ecs_os_strlen("#[blue]#[reset]"); + } + return color_chars; } -void ecs_system_stats_copy_last( - ecs_system_stats_t *dst, - const ecs_system_stats_t *src) +static +void flecs_query_str_append_bitset( + ecs_strbuf_t *buf, + ecs_flags64_t bitset) { - ecs_query_stats_copy_last(&dst->query, &src->query); - dst->task = src->task; - flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), - ECS_METRIC_FIRST(src), dst->query.t, t_next(src->query.t)); + ecs_strbuf_list_push(buf, "{", ","); + int8_t b; + for (b = 0; b < 64; b ++) { + if (bitset & (1llu << b)) { + ecs_strbuf_list_append(buf, "%d", b); + } + } + ecs_strbuf_list_pop(buf, "}"); } -#endif +static +void flecs_query_plan_w_profile( + const ecs_query_t *q, + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + ecs_query_impl_t *impl = flecs_query_impl(q); + ecs_query_op_t *ops = impl->ops; + int32_t i, count = impl->op_count, indent = 0; + if (!count) { + ecs_strbuf_append(buf, ""); + return; /* No plan */ + } -#ifdef FLECS_PIPELINE + for (i = 0; i < count; i ++) { + ecs_query_op_t *op = &ops[i]; + ecs_flags16_t flags = op->flags; + ecs_flags16_t src_flags = flecs_query_ref_flags(flags, EcsQuerySrc); + ecs_flags16_t first_flags = flecs_query_ref_flags(flags, EcsQueryFirst); + ecs_flags16_t second_flags = flecs_query_ref_flags(flags, EcsQuerySecond); -bool ecs_pipeline_stats_get( - ecs_world_t *stage, - ecs_entity_t pipeline, - ecs_pipeline_stats_t *s) -{ - ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(pipeline != 0, ECS_INVALID_PARAMETER, NULL); + if (it) { +#ifdef FLECS_DEBUG + const ecs_query_iter_t *rit = &it->priv_.iter.query; + ecs_strbuf_append(buf, + "#[green]%4d -> #[red]%4d <- #[grey] | ", + rit->profile[i].count[0], + rit->profile[i].count[1]); +#endif + } - const ecs_world_t *world = ecs_get_world(stage); - const EcsPipeline *pqc = ecs_get(world, pipeline, EcsPipeline); - if (!pqc) { - return false; - } - ecs_pipeline_state_t *pq = pqc->state; - ecs_assert(pq != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_strbuf_append(buf, + "#[normal]%2d. [#[grey]%2d#[reset], #[green]%2d#[reset]] ", + i, op->prev, op->next); + int32_t hidden_chars, start = ecs_strbuf_written(buf); + if (op->kind == EcsQueryEnd) { + indent --; + } - int32_t sys_count = 0, active_sys_count = 0; + ecs_strbuf_append(buf, "%*s", indent, ""); + ecs_strbuf_appendstr(buf, flecs_query_op_str(op->kind)); + ecs_strbuf_appendstr(buf, " "); - /* Count number of active systems */ - ecs_iter_t it = ecs_query_iter(stage, pq->query); - while (ecs_query_next(&it)) { - if (flecs_id_record_get_table(pq->idr_inactive, it.table) != NULL) { - continue; + int32_t written = ecs_strbuf_written(buf); + for (int32_t j = 0; j < (12 - (written - start)); j ++) { + ecs_strbuf_appendch(buf, ' '); } - active_sys_count += it.count; - } + + hidden_chars = flecs_query_op_ref_str(impl, &op->src, src_flags, buf); - /* Count total number of systems in pipeline */ - it = ecs_query_iter(stage, pq->query); - while (ecs_query_next(&it)) { - sys_count += it.count; - } + if (op->kind == EcsQueryNot || + op->kind == EcsQueryOr || + op->kind == EcsQueryOptional || + op->kind == EcsQueryIfVar || + op->kind == EcsQueryIfSet) + { + indent ++; + } - /* Also count synchronization points */ - ecs_vec_t *ops = &pq->ops; - ecs_pipeline_op_t *op = ecs_vec_first_t(ops, ecs_pipeline_op_t); - ecs_pipeline_op_t *op_last = ecs_vec_last_t(ops, ecs_pipeline_op_t); - int32_t pip_count = active_sys_count + ecs_vec_count(ops); + if (op->kind == EcsQueryTriv) { + flecs_query_str_append_bitset(buf, op->src.entity); + } - if (!sys_count) { - return false; - } + if (op->kind == EcsQueryIfSet) { + ecs_strbuf_append(buf, "[%d]\n", op->other); + continue; + } - if (ecs_map_is_init(&s->system_stats) && !sys_count) { - ecs_map_fini(&s->system_stats); - } - ecs_map_init_if(&s->system_stats, NULL); + bool is_toggle = op->kind == EcsQueryToggle || + op->kind == EcsQueryToggleOption; - if (op) { - ecs_entity_t *systems = NULL; - if (pip_count) { - ecs_vec_init_if_t(&s->systems, ecs_entity_t); - ecs_vec_set_count_t(NULL, &s->systems, ecs_entity_t, pip_count); - systems = ecs_vec_first_t(&s->systems, ecs_entity_t); + if (!first_flags && !second_flags && !is_toggle) { + ecs_strbuf_appendstr(buf, "\n"); + continue; + } - /* Populate systems vector, keep track of sync points */ - it = ecs_query_iter(stage, pq->query); - - int32_t i, i_system = 0, ran_since_merge = 0; - while (ecs_query_next(&it)) { - if (flecs_id_record_get_table(pq->idr_inactive, it.table) != NULL) { - continue; - } + written = ecs_strbuf_written(buf) - hidden_chars; + for (int32_t j = 0; j < (30 - (written - start)); j ++) { + ecs_strbuf_appendch(buf, ' '); + } - for (i = 0; i < it.count; i ++) { - systems[i_system ++] = it.entities[i]; - ran_since_merge ++; - if (op != op_last && ran_since_merge == op->count) { - ran_since_merge = 0; - op++; - systems[i_system ++] = 0; /* 0 indicates a merge point */ - } + if (is_toggle) { + if (op->first.entity) { + flecs_query_str_append_bitset(buf, op->first.entity); + } + if (op->second.entity) { + if (op->first.entity) { + ecs_strbuf_appendlit(buf, ", !"); } + flecs_query_str_append_bitset(buf, op->second.entity); } + ecs_strbuf_appendstr(buf, "\n"); + continue; + } - systems[i_system ++] = 0; /* Last merge */ - ecs_assert(pip_count == i_system, ECS_INTERNAL_ERROR, NULL); + ecs_strbuf_appendstr(buf, "("); + if (op->kind == EcsQueryMemberEq || op->kind == EcsQueryMemberNeq) { + uint32_t offset = (uint32_t)op->first.entity; + uint32_t size = (uint32_t)(op->first.entity >> 32); + ecs_strbuf_append(buf, "#[yellow]elem#[reset]([%d], 0x%x, 0x%x)", + op->field_index, size, offset); } else { - ecs_vec_fini_t(NULL, &s->systems, ecs_entity_t); + flecs_query_op_ref_str(impl, &op->first, first_flags, buf); } - /* Get sync point statistics */ - int32_t i, count = ecs_vec_count(ops); - if (count) { - ecs_vec_init_if_t(&s->sync_points, ecs_sync_stats_t); - ecs_vec_set_min_count_zeromem_t(NULL, &s->sync_points, ecs_sync_stats_t, count); - op = ecs_vec_first_t(ops, ecs_pipeline_op_t); - - for (i = 0; i < count; i ++) { - ecs_pipeline_op_t *cur = &op[i]; - ecs_sync_stats_t *el = ecs_vec_get_t(&s->sync_points, - ecs_sync_stats_t, i); - - ECS_COUNTER_RECORD(&el->time_spent, s->t, cur->time_spent); - ECS_COUNTER_RECORD(&el->commands_enqueued, s->t, - cur->commands_enqueued); - - el->system_count = cur->count; - el->multi_threaded = cur->multi_threaded; - el->no_readonly = cur->no_readonly; + if (second_flags) { + ecs_strbuf_appendstr(buf, ", "); + flecs_query_op_ref_str(impl, &op->second, second_flags, buf); + } else { + switch (op->kind) { + case EcsQueryPredEqName: + case EcsQueryPredNeqName: + case EcsQueryPredEqMatch: + case EcsQueryPredNeqMatch: { + int8_t term_index = op->term_index; + ecs_strbuf_appendstr(buf, ", #[yellow]\""); + ecs_strbuf_appendstr(buf, q->terms[term_index].second.name); + ecs_strbuf_appendstr(buf, "\"#[reset]"); + break; + } + case EcsQueryLookup: { + ecs_var_id_t src_id = op->src.var; + ecs_strbuf_appendstr(buf, ", #[yellow]\""); + ecs_strbuf_appendstr(buf, impl->vars[src_id].lookup); + ecs_strbuf_appendstr(buf, "\"#[reset]"); + break; + } + default: + break; } } - } - /* Separately populate system stats map from build query, which includes - * systems that aren't currently active */ - it = ecs_query_iter(stage, pq->query); - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - ecs_system_stats_t *stats = ecs_map_ensure_alloc_t(&s->system_stats, - ecs_system_stats_t, it.entities[i]); - stats->query.t = s->t; - ecs_system_stats_get(world, it.entities[i], stats); - } + ecs_strbuf_appendch(buf, ')'); + + ecs_strbuf_appendch(buf, '\n'); } +} - s->t = t_next(s->t); +char* ecs_query_plan_w_profile( + const ecs_query_t *q, + const ecs_iter_t *it) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_strbuf_t buf = ECS_STRBUF_INIT; - return true; -error: - return false; + flecs_query_plan_w_profile(q, it, &buf); + // ecs_query_impl_t *impl = flecs_query_impl(q); + // if (impl->cache) { + // ecs_strbuf_appendch(&buf, '\n'); + // flecs_query_plan_w_profile(impl->cache->query, it, &buf); + // } + +#ifdef FLECS_LOG + char *str = ecs_strbuf_get(&buf); + flecs_colorize_buf(str, ecs_os_api.flags_ & EcsOsApiLogWithColors, &buf); + ecs_os_free(str); +#endif + + return ecs_strbuf_get(&buf); } -void ecs_pipeline_stats_fini( - ecs_pipeline_stats_t *stats) +char* ecs_query_plan( + const ecs_query_t *q) { - ecs_map_iter_t it = ecs_map_iter(&stats->system_stats); - while (ecs_map_next(&it)) { - ecs_system_stats_t *elem = ecs_map_ptr(&it); - ecs_os_free(elem); - } - ecs_map_fini(&stats->system_stats); - ecs_vec_fini_t(NULL, &stats->systems, ecs_entity_t); - ecs_vec_fini_t(NULL, &stats->sync_points, ecs_sync_stats_t); + return ecs_query_plan_w_profile(q, NULL); } -void ecs_pipeline_stats_reduce( - ecs_pipeline_stats_t *dst, - const ecs_pipeline_stats_t *src) +static +void flecs_query_str_add_id( + const ecs_world_t *world, + ecs_strbuf_t *buf, + const ecs_term_t *term, + const ecs_term_ref_t *ref, + bool is_src) { - int32_t system_count = ecs_vec_count(&src->systems); - ecs_vec_init_if_t(&dst->systems, ecs_entity_t); - ecs_vec_set_count_t(NULL, &dst->systems, ecs_entity_t, system_count); - ecs_entity_t *dst_systems = ecs_vec_first_t(&dst->systems, ecs_entity_t); - ecs_entity_t *src_systems = ecs_vec_first_t(&src->systems, ecs_entity_t); - ecs_os_memcpy_n(dst_systems, src_systems, ecs_entity_t, system_count); + bool is_added = false; + ecs_entity_t ref_id = ECS_TERM_REF_ID(ref); + if (ref->id & EcsIsVariable && !ecs_id_is_wildcard(ref_id)){ + ecs_strbuf_appendlit(buf, "$"); + } - int32_t i, sync_count = ecs_vec_count(&src->sync_points); - ecs_vec_init_if_t(&dst->sync_points, ecs_sync_stats_t); - ecs_vec_set_min_count_zeromem_t(NULL, &dst->sync_points, ecs_sync_stats_t, sync_count); - ecs_sync_stats_t *dst_syncs = ecs_vec_first_t(&dst->sync_points, ecs_sync_stats_t); - ecs_sync_stats_t *src_syncs = ecs_vec_first_t(&src->sync_points, ecs_sync_stats_t); - for (i = 0; i < sync_count; i ++) { - ecs_sync_stats_t *dst_el = &dst_syncs[i]; - ecs_sync_stats_t *src_el = &src_syncs[i]; - flecs_stats_reduce(ECS_METRIC_FIRST(dst_el), ECS_METRIC_LAST(dst_el), - ECS_METRIC_FIRST(src_el), dst->t, src->t); - dst_el->system_count = src_el->system_count; - dst_el->multi_threaded = src_el->multi_threaded; - dst_el->no_readonly = src_el->no_readonly; + if (ref_id) { + char *path = ecs_get_path(world, ref_id); + ecs_strbuf_appendstr(buf, path); + ecs_os_free(path); + } else if (ref->name) { + ecs_strbuf_appendstr(buf, ref->name); + } else { + ecs_strbuf_appendlit(buf, "0"); } + is_added = true; - ecs_map_init_if(&dst->system_stats, NULL); - ecs_map_iter_t it = ecs_map_iter(&src->system_stats); - - while (ecs_map_next(&it)) { - ecs_system_stats_t *sys_src = ecs_map_ptr(&it); - ecs_system_stats_t *sys_dst = ecs_map_ensure_alloc_t(&dst->system_stats, - ecs_system_stats_t, ecs_map_key(&it)); - sys_dst->query.t = dst->t; - ecs_system_stats_reduce(sys_dst, sys_src); + ecs_flags64_t flags = ECS_TERM_REF_FLAGS(ref); + if (!(flags & EcsTraverseFlags)) { + /* If flags haven't been set yet, initialize with defaults. This can + * happen if an error is thrown while the term is being finalized */ + flags |= EcsSelf; } - dst->t = t_next(dst->t); -} -void ecs_pipeline_stats_reduce_last( - ecs_pipeline_stats_t *dst, - const ecs_pipeline_stats_t *src, - int32_t count) -{ - int32_t i, sync_count = ecs_vec_count(&src->sync_points); - ecs_sync_stats_t *dst_syncs = ecs_vec_first_t(&dst->sync_points, ecs_sync_stats_t); - ecs_sync_stats_t *src_syncs = ecs_vec_first_t(&src->sync_points, ecs_sync_stats_t); + if ((flags & EcsTraverseFlags) != EcsSelf) { + if (is_added) { + ecs_strbuf_list_push(buf, "|", "|"); + } else { + ecs_strbuf_list_push(buf, "", "|"); + } + if (is_src) { + if (flags & EcsSelf) { + ecs_strbuf_list_appendstr(buf, "self"); + } - for (i = 0; i < sync_count; i ++) { - ecs_sync_stats_t *dst_el = &dst_syncs[i]; - ecs_sync_stats_t *src_el = &src_syncs[i]; - flecs_stats_reduce_last(ECS_METRIC_FIRST(dst_el), ECS_METRIC_LAST(dst_el), - ECS_METRIC_FIRST(src_el), dst->t, src->t, count); - dst_el->system_count = src_el->system_count; - dst_el->multi_threaded = src_el->multi_threaded; - dst_el->no_readonly = src_el->no_readonly; - } + if (flags & EcsCascade) { + ecs_strbuf_list_appendstr(buf, "cascade"); + } else if (flags & EcsUp) { + ecs_strbuf_list_appendstr(buf, "up"); + } - ecs_map_init_if(&dst->system_stats, NULL); - ecs_map_iter_t it = ecs_map_iter(&src->system_stats); - while (ecs_map_next(&it)) { - ecs_system_stats_t *sys_src = ecs_map_ptr(&it); - ecs_system_stats_t *sys_dst = ecs_map_ensure_alloc_t(&dst->system_stats, - ecs_system_stats_t, ecs_map_key(&it)); - sys_dst->query.t = dst->t; - ecs_system_stats_reduce_last(sys_dst, sys_src, count); + if (flags & EcsDesc) { + ecs_strbuf_list_appendstr(buf, "desc"); + } + + if (term->trav) { + char *rel_path = ecs_get_path(world, term->trav); + ecs_strbuf_appendlit(buf, " "); + ecs_strbuf_appendstr(buf, rel_path); + ecs_os_free(rel_path); + } + } + + ecs_strbuf_list_pop(buf, ""); } - dst->t = t_prev(dst->t); } -void ecs_pipeline_stats_repeat_last( - ecs_pipeline_stats_t *stats) +void flecs_term_to_buf( + const ecs_world_t *world, + const ecs_term_t *term, + ecs_strbuf_t *buf, + int32_t t) { - int32_t i, sync_count = ecs_vec_count(&stats->sync_points); - ecs_sync_stats_t *syncs = ecs_vec_first_t(&stats->sync_points, ecs_sync_stats_t); + const ecs_term_ref_t *src = &term->src; + const ecs_term_ref_t *first = &term->first; + const ecs_term_ref_t *second = &term->second; - for (i = 0; i < sync_count; i ++) { - ecs_sync_stats_t *el = &syncs[i]; - flecs_stats_repeat_last(ECS_METRIC_FIRST(el), ECS_METRIC_LAST(el), - (stats->t)); + ecs_entity_t src_id = ECS_TERM_REF_ID(src); + ecs_entity_t first_id = ECS_TERM_REF_ID(first); + + bool src_set = !ecs_term_match_0(term); + bool second_set = ecs_term_ref_is_set(second); + + if (first_id == EcsScopeOpen) { + ecs_strbuf_appendlit(buf, "{"); + return; + } else if (first_id == EcsScopeClose) { + ecs_strbuf_appendlit(buf, "}"); + return; } - ecs_map_iter_t it = ecs_map_iter(&stats->system_stats); - while (ecs_map_next(&it)) { - ecs_system_stats_t *sys = ecs_map_ptr(&it); - sys->query.t = stats->t; - ecs_system_stats_repeat_last(sys); + if (((ECS_TERM_REF_ID(&term->first) == EcsPredEq) || + (ECS_TERM_REF_ID(&term->first) == EcsPredMatch)) && + (term->first.id & EcsIsEntity)) + { + ecs_strbuf_appendlit(buf, "$"); + if (ECS_TERM_REF_ID(&term->src) == EcsThis && + (term->src.id & EcsIsVariable)) + { + ecs_strbuf_appendlit(buf, "this"); + } else if (term->src.id & EcsIsVariable) { + ecs_strbuf_appendstr(buf, term->src.name); + } else { + /* Shouldn't happen */ + } + + if (ECS_TERM_REF_ID(&term->first) == EcsPredEq) { + if (term->oper == EcsNot) { + ecs_strbuf_appendlit(buf, " != "); + } else { + ecs_strbuf_appendlit(buf, " == "); + } + } else if (ECS_TERM_REF_ID(&term->first) == EcsPredMatch) { + ecs_strbuf_appendlit(buf, " ~= "); + } + + if (term->second.id & EcsIsEntity) { + if (term->second.id != 0) { + ecs_get_path_w_sep_buf(world, 0, ECS_TERM_REF_ID(&term->second), + ".", NULL, buf, false); + } + } else { + if (term->second.id & EcsIsVariable) { + ecs_strbuf_appendlit(buf, "$"); + if (term->second.name) { + ecs_strbuf_appendstr(buf, term->second.name); + } else if (ECS_TERM_REF_ID(&term->second) == EcsThis) { + ecs_strbuf_appendlit(buf, "this"); + } + } else if (term->second.id & EcsIsName) { + ecs_strbuf_appendlit(buf, "\""); + if ((ECS_TERM_REF_ID(&term->first) == EcsPredMatch) && + (term->oper == EcsNot)) + { + ecs_strbuf_appendlit(buf, "!"); + } + ecs_strbuf_appendstr(buf, term->second.name); + ecs_strbuf_appendlit(buf, "\""); + } + } + + return; } - stats->t = t_next(stats->t); -} -void ecs_pipeline_stats_copy_last( - ecs_pipeline_stats_t *dst, - const ecs_pipeline_stats_t *src) -{ - int32_t i, sync_count = ecs_vec_count(&src->sync_points); - ecs_vec_init_if_t(&dst->sync_points, ecs_sync_stats_t); - ecs_vec_set_min_count_zeromem_t(NULL, &dst->sync_points, ecs_sync_stats_t, sync_count); - ecs_sync_stats_t *dst_syncs = ecs_vec_first_t(&dst->sync_points, ecs_sync_stats_t); - ecs_sync_stats_t *src_syncs = ecs_vec_first_t(&src->sync_points, ecs_sync_stats_t); + if (!t || !(term[-1].oper == EcsOr)) { + if (term->inout == EcsIn) { + ecs_strbuf_appendlit(buf, "[in] "); + } else if (term->inout == EcsInOut) { + ecs_strbuf_appendlit(buf, "[inout] "); + } else if (term->inout == EcsOut) { + ecs_strbuf_appendlit(buf, "[out] "); + } else if (term->inout == EcsInOutNone && term->oper != EcsNot) { + ecs_strbuf_appendlit(buf, "[none] "); + } + } - for (i = 0; i < sync_count; i ++) { - ecs_sync_stats_t *dst_el = &dst_syncs[i]; - ecs_sync_stats_t *src_el = &src_syncs[i]; - flecs_stats_copy_last(ECS_METRIC_FIRST(dst_el), ECS_METRIC_LAST(dst_el), - ECS_METRIC_FIRST(src_el), dst->t, t_next(src->t)); - dst_el->system_count = src_el->system_count; - dst_el->multi_threaded = src_el->multi_threaded; - dst_el->no_readonly = src_el->no_readonly; + if (term->oper == EcsNot) { + ecs_strbuf_appendlit(buf, "!"); + } else if (term->oper == EcsOptional) { + ecs_strbuf_appendlit(buf, "?"); } - ecs_map_init_if(&dst->system_stats, NULL); + if (!src_set) { + flecs_query_str_add_id(world, buf, term, &term->first, false); + if (!second_set) { + ecs_strbuf_appendlit(buf, "()"); + } else { + ecs_strbuf_appendlit(buf, "(#0,"); + flecs_query_str_add_id(world, buf, term, &term->second, false); + ecs_strbuf_appendlit(buf, ")"); + } + } else { + ecs_id_t flags = term->id & ECS_ID_FLAGS_MASK; + if (flags && !ECS_HAS_ID_FLAG(flags, PAIR)) { + ecs_strbuf_appendstr(buf, ecs_id_flag_str(flags)); + ecs_strbuf_appendch(buf, '|'); + } - ecs_map_iter_t it = ecs_map_iter(&src->system_stats); - while (ecs_map_next(&it)) { - ecs_system_stats_t *sys_src = ecs_map_ptr(&it); - ecs_system_stats_t *sys_dst = ecs_map_ensure_alloc_t(&dst->system_stats, - ecs_system_stats_t, ecs_map_key(&it)); - sys_dst->query.t = dst->t; - ecs_system_stats_copy_last(sys_dst, sys_src); + flecs_query_str_add_id(world, buf, term, &term->first, false); + ecs_strbuf_appendlit(buf, "("); + if (term->src.id & EcsIsEntity && src_id == first_id) { + ecs_strbuf_appendlit(buf, "$"); + } else { + flecs_query_str_add_id(world, buf, term, &term->src, true); + } + if (second_set) { + ecs_strbuf_appendlit(buf, ","); + flecs_query_str_add_id(world, buf, term, &term->second, false); + } + ecs_strbuf_appendlit(buf, ")"); } } -#endif - -void ecs_world_stats_log( +char* ecs_term_str( const ecs_world_t *world, - const ecs_world_stats_t *s) + const ecs_term_t *term) { - int32_t t = s->t; - - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); - - world = ecs_get_world(world); - - flecs_counter_print("Frame", t, &s->frame.frame_count); - ecs_trace("-------------------------------------"); - flecs_counter_print("pipeline rebuilds", t, &s->frame.pipeline_build_count); - flecs_counter_print("systems ran", t, &s->frame.systems_ran); - ecs_trace(""); - flecs_metric_print("target FPS", world->info.target_fps); - flecs_metric_print("time scale", world->info.time_scale); - ecs_trace(""); - flecs_gauge_print("actual FPS", t, &s->performance.fps); - flecs_counter_print("frame time", t, &s->performance.frame_time); - flecs_counter_print("system time", t, &s->performance.system_time); - flecs_counter_print("merge time", t, &s->performance.merge_time); - flecs_counter_print("simulation time elapsed", t, &s->performance.world_time); - ecs_trace(""); - flecs_gauge_print("tag id count", t, &s->components.tag_count); - flecs_gauge_print("component id count", t, &s->components.component_count); - flecs_gauge_print("pair id count", t, &s->components.pair_count); - flecs_gauge_print("type count", t, &s->components.type_count); - flecs_counter_print("id create count", t, &s->components.create_count); - flecs_counter_print("id delete count", t, &s->components.delete_count); - ecs_trace(""); - flecs_gauge_print("alive entity count", t, &s->entities.count); - flecs_gauge_print("not alive entity count", t, &s->entities.not_alive_count); - ecs_trace(""); - flecs_gauge_print("query count", t, &s->queries.query_count); - flecs_gauge_print("observer count", t, &s->queries.observer_count); - flecs_gauge_print("system count", t, &s->queries.system_count); - ecs_trace(""); - flecs_gauge_print("table count", t, &s->tables.count); - flecs_gauge_print("empty table count", t, &s->tables.empty_count); - flecs_counter_print("table create count", t, &s->tables.create_count); - flecs_counter_print("table delete count", t, &s->tables.delete_count); - ecs_trace(""); - flecs_counter_print("add commands", t, &s->commands.add_count); - flecs_counter_print("remove commands", t, &s->commands.remove_count); - flecs_counter_print("delete commands", t, &s->commands.delete_count); - flecs_counter_print("clear commands", t, &s->commands.clear_count); - flecs_counter_print("set commands", t, &s->commands.set_count); - flecs_counter_print("get_mut commands", t, &s->commands.get_mut_count); - flecs_counter_print("modified commands", t, &s->commands.modified_count); - flecs_counter_print("other commands", t, &s->commands.other_count); - flecs_counter_print("discarded commands", t, &s->commands.discard_count); - flecs_counter_print("batched entities", t, &s->commands.batched_entity_count); - flecs_counter_print("batched commands", t, &s->commands.batched_count); - ecs_trace(""); - -error: - return; + ecs_strbuf_t buf = ECS_STRBUF_INIT; + flecs_term_to_buf(world, term, &buf, 0); + return ecs_strbuf_get(&buf); } -#endif +char* ecs_query_str( + const ecs_query_t *q) +{ + ecs_check(q != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_world_t *world = q->world; -/** - * @file addons/timer.c - * @brief Timer addon. - */ + ecs_strbuf_t buf = ECS_STRBUF_INIT; + const ecs_term_t *terms = q->terms; + int32_t i, count = q->term_count; + for (i = 0; i < count; i ++) { + const ecs_term_t *term = &terms[i]; -#ifdef FLECS_TIMER + flecs_term_to_buf(world, term, &buf, i); -static -void AddTickSource(ecs_iter_t *it) { - int32_t i; - for (i = 0; i < it->count; i ++) { - ecs_set(it->world, it->entities[i], EcsTickSource, {0}); + if (i != (count - 1)) { + if (term->oper == EcsOr) { + ecs_strbuf_appendlit(&buf, " || "); + } else { + if (ECS_TERM_REF_ID(&term->first) != EcsScopeOpen) { + if (ECS_TERM_REF_ID(&term[1].first) != EcsScopeClose) { + ecs_strbuf_appendlit(&buf, ", "); + } + } + } + } } + + return ecs_strbuf_get(&buf); +error: + return NULL; } -static -void ProgressTimers(ecs_iter_t *it) { - EcsTimer *timer = ecs_field(it, EcsTimer, 1); - EcsTickSource *tick_source = ecs_field(it, EcsTickSource, 2); +int32_t flecs_query_pivot_term( + const ecs_world_t *world, + const ecs_query_t *query) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(timer != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_term_t *terms = query->terms; + int32_t i, term_count = query->term_count; + int32_t pivot_term = -1, min_count = -1, self_pivot_term = -1; - int i; - for (i = 0; i < it->count; i ++) { - tick_source[i].tick = false; + for (i = 0; i < term_count; i ++) { + const ecs_term_t *term = &terms[i]; + ecs_id_t id = term->id; - if (!timer[i].active) { + if ((term->oper != EcsAnd) || (i && (term[-1].oper == EcsOr))) { continue; } - const ecs_world_info_t *info = ecs_get_world_info(it->world); - ecs_ftime_t time_elapsed = timer[i].time + info->delta_time_raw; - ecs_ftime_t timeout = timer[i].timeout; - - if (time_elapsed >= timeout) { - ecs_ftime_t t = time_elapsed - timeout; - if (t > timeout) { - t = 0; - } + if (!ecs_term_match_this(term)) { + continue; + } - timer[i].time = t; /* Initialize with remainder */ - tick_source[i].tick = true; - tick_source[i].time_elapsed = time_elapsed - timer[i].overshoot; - timer[i].overshoot = t; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + /* If one of the terms does not match with any data, iterator + * should not return anything */ + return -2; /* -2 indicates query doesn't match anything */ + } - if (timer[i].single_shot) { - timer[i].active = false; + int32_t table_count = flecs_table_cache_count(&idr->cache); + if (min_count == -1 || table_count < min_count) { + min_count = table_count; + pivot_term = i; + if ((term->src.id & EcsTraverseFlags) == EcsSelf) { + self_pivot_term = i; } - } else { - timer[i].time = time_elapsed; - } + } + } + + if (self_pivot_term != -1) { + pivot_term = self_pivot_term; } + + return pivot_term; +error: + return -2; +} + +void flecs_query_apply_iter_flags( + ecs_iter_t *it, + const ecs_query_t *query) +{ + ECS_BIT_COND(it->flags, EcsIterHasCondSet, + ECS_BIT_IS_SET(query->flags, EcsQueryHasCondSet)); + ECS_BIT_COND(it->flags, EcsIterNoData, query->data_fields == 0); } +/** + * @file query/validator.c + * @brief Validate and finalize queries. + */ + +#include + +#ifdef FLECS_SCRIPT +#endif + static -void ProgressRateFilters(ecs_iter_t *it) { - EcsRateFilter *filter = ecs_field(it, EcsRateFilter, 1); - EcsTickSource *tick_dst = ecs_field(it, EcsTickSource, 2); +void flecs_query_validator_error( + const ecs_query_validator_ctx_t *ctx, + const char *fmt, + ...) +{ + ecs_strbuf_t buf = ECS_STRBUF_INIT; - int i; - for (i = 0; i < it->count; i ++) { - ecs_entity_t src = filter[i].src; - bool inc = false; + if (ctx->desc && ctx->desc->expr) { + ecs_strbuf_appendlit(&buf, "expr: "); + ecs_strbuf_appendstr(&buf, ctx->desc->expr); + ecs_strbuf_appendlit(&buf, "\n"); + } - filter[i].time_elapsed += it->delta_time; + if (ctx->query) { + ecs_query_t *query = ctx->query; + const ecs_term_t *terms = query->terms; + int32_t i, count = query->term_count; - if (src) { - const EcsTickSource *tick_src = ecs_get( - it->world, src, EcsTickSource); - if (tick_src) { - inc = tick_src->tick; + for (i = 0; i < count; i ++) { + const ecs_term_t *term = &terms[i]; + if (ctx->term_index == i) { + ecs_strbuf_appendlit(&buf, " > "); } else { - inc = true; + ecs_strbuf_appendlit(&buf, " "); } - } else { - inc = true; + flecs_term_to_buf(ctx->world, term, &buf, i); + if (term->oper == EcsOr) { + ecs_strbuf_appendlit(&buf, " ||"); + } else if (i != (count - 1)) { + ecs_strbuf_appendlit(&buf, ","); + } + ecs_strbuf_appendlit(&buf, "\n"); } + } else { + ecs_strbuf_appendlit(&buf, " > "); + flecs_term_to_buf(ctx->world, ctx->term, &buf, 0); + ecs_strbuf_appendlit(&buf, "\n"); + } - if (inc) { - filter[i].tick_count ++; - bool triggered = !(filter[i].tick_count % filter[i].rate); - tick_dst[i].tick = triggered; - tick_dst[i].time_elapsed = filter[i].time_elapsed; - - if (triggered) { - filter[i].time_elapsed = 0; - } - } else { - tick_dst[i].tick = false; - } + char *expr = ecs_strbuf_get(&buf); + const char *name = NULL; + if (ctx->query && ctx->query->entity) { + name = ecs_get_name(ctx->query->world, ctx->query->entity); } -} -static -void ProgressTickSource(ecs_iter_t *it) { - EcsTickSource *tick_src = ecs_field(it, EcsTickSource, 1); + va_list args; + va_start(args, fmt); + char *msg = flecs_vasprintf(fmt, args); + ecs_parser_error(name, NULL, 0, "%s\n%s", msg, expr); + ecs_os_free(msg); + ecs_os_free(expr); - /* If tick source has no filters, tick unconditionally */ - int i; - for (i = 0; i < it->count; i ++) { - tick_src[i].tick = true; - tick_src[i].time_elapsed = it->delta_time; - } + va_end(args); } -ecs_entity_t ecs_set_timeout( - ecs_world_t *world, - ecs_entity_t timer, - ecs_ftime_t timeout) +static +int flecs_term_ref_finalize_flags( + ecs_term_ref_t *ref, + ecs_query_validator_ctx_t *ctx) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + if ((ref->id & EcsIsEntity) && (ref->id & EcsIsVariable)) { + flecs_query_validator_error(ctx, "cannot set both IsEntity and IsVariable"); + return -1; + } - timer = ecs_set(world, timer, EcsTimer, { - .timeout = timeout, - .single_shot = true, - .active = true - }); + if (ref->name && ref->name[0] == '$') { + if (!ref->name[1]) { + if (!(ref->id & EcsIsName)) { + if (ref->id & ~EcsTermRefFlags) { + flecs_query_validator_error(ctx, + "conflicting values for .name and .id"); + return -1; + } - ecs_system_t *system_data = ecs_poly_get(world, timer, ecs_system_t); - if (system_data) { - system_data->tick_source = timer; + ref->id |= EcsVariable; + ref->id |= EcsIsVariable; + } + } else { + ref->name = &ref->name[1]; + ref->id |= EcsIsVariable; + } + } + + if (!(ref->id & (EcsIsEntity|EcsIsVariable|EcsIsName))) { + if (ECS_TERM_REF_ID(ref) || ref->name) { + if (ECS_TERM_REF_ID(ref) == EcsThis || + ECS_TERM_REF_ID(ref) == EcsWildcard || + ECS_TERM_REF_ID(ref) == EcsAny || + ECS_TERM_REF_ID(ref) == EcsVariable) + { + /* Builtin variable ids default to variable */ + ref->id |= EcsIsVariable; + } else { + ref->id |= EcsIsEntity; + } + } } -error: - return timer; + return 0; } -ecs_ftime_t ecs_get_timeout( +static +int flecs_term_ref_lookup( const ecs_world_t *world, - ecs_entity_t timer) + ecs_entity_t scope, + ecs_term_ref_t *ref, + ecs_query_validator_ctx_t *ctx) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(timer != 0, ECS_INVALID_PARAMETER, NULL); + const char *name = ref->name; + if (!name) { + return 0; + } - const EcsTimer *value = ecs_get(world, timer, EcsTimer); - if (value) { - return value->timeout; + if (ref->id & EcsIsVariable) { + if (!ecs_os_strcmp(name, "this")) { + ref->id = EcsThis | ECS_TERM_REF_FLAGS(ref); + ref->name = NULL; + return 0; + } + return 0; + } else if (ref->id & EcsIsName) { + if (ref->name == NULL) { + flecs_query_validator_error(ctx, "IsName flag specified without name"); + return -1; + } + return 0; } -error: - return 0; -} -ecs_entity_t ecs_set_interval( - ecs_world_t *world, - ecs_entity_t timer, - ecs_ftime_t interval) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ref->id & EcsIsEntity, ECS_INTERNAL_ERROR, NULL); - if (!timer) { - timer = ecs_new(world, EcsTimer); + if (flecs_identifier_is_0(name)) { + if (ECS_TERM_REF_ID(ref)) { + flecs_query_validator_error( + ctx, "name '0' does not match entity id"); + return -1; + } + ref->name = NULL; + return 0; } - EcsTimer *t = ecs_get_mut(world, timer, EcsTimer); - ecs_check(t != NULL, ECS_INVALID_PARAMETER, NULL); - t->timeout = interval; - t->active = true; - ecs_modified(world, timer, EcsTimer); + ecs_entity_t e = 0; + if (scope) { + e = ecs_lookup_child(world, scope, name); + } - ecs_system_t *system_data = ecs_poly_get(world, timer, ecs_system_t); - if (system_data) { - system_data->tick_source = timer; + if (!e) { + e = ecs_lookup(world, name); } -error: - return timer; -} -ecs_ftime_t ecs_get_interval( - const ecs_world_t *world, - ecs_entity_t timer) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + if (!e) { + if (ctx->query && (ctx->query->flags & EcsQueryAllowUnresolvedByName)) { + ref->id |= EcsIsName; + ref->id &= ~EcsIsEntity; + return 0; + } else { + flecs_query_validator_error(ctx, "unresolved identifier '%s'", name); + return -1; + } + } - if (!timer) { - return 0; + ecs_entity_t ref_id = ECS_TERM_REF_ID(ref); + if (ref_id && ref_id != e) { + char *e_str = ecs_get_path(world, ref_id); + flecs_query_validator_error(ctx, "name '%s' does not match term.id '%s'", + name, e_str); + ecs_os_free(e_str); + return -1; } - const EcsTimer *value = ecs_get(world, timer, EcsTimer); - if (value) { - return value->timeout; + ref->id = e | ECS_TERM_REF_FLAGS(ref); + ref_id = ECS_TERM_REF_ID(ref); + + if (!ecs_os_strcmp(name, "*") || !ecs_os_strcmp(name, "_") || + !ecs_os_strcmp(name, "$")) + { + ref->id &= ~EcsIsEntity; + ref->id |= EcsIsVariable; } -error: + + /* Check if looked up id is alive (relevant for numerical ids) */ + if (!(ref->id & EcsIsName) && ref_id) { + if (!ecs_is_alive(world, ref_id)) { + flecs_query_validator_error(ctx, "identifier '%s' is not alive", ref->name); + return -1; + } + + ref->name = NULL; + return 0; + } + return 0; } -void ecs_start_timer( - ecs_world_t *world, - ecs_entity_t timer) -{ - EcsTimer *ptr = ecs_get_mut(world, timer, EcsTimer); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); - ptr->active = true; - ptr->time = 0; -error: - return; -} +static +ecs_id_t flecs_wildcard_to_any(ecs_id_t id) { + ecs_id_t flags = id & EcsTermRefFlags; + + if (ECS_IS_PAIR(id)) { + ecs_entity_t first = ECS_PAIR_FIRST(id); + ecs_entity_t second = ECS_PAIR_SECOND(id); + if (first == EcsWildcard) id = ecs_pair(EcsAny, second); + if (second == EcsWildcard) id = ecs_pair(ECS_PAIR_FIRST(id), EcsAny); + } else if ((id & ~EcsTermRefFlags) == EcsWildcard) { + id = EcsAny; + } -void ecs_stop_timer( - ecs_world_t *world, - ecs_entity_t timer) -{ - EcsTimer *ptr = ecs_get_mut(world, timer, EcsTimer); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); - ptr->active = false; -error: - return; + return id | flags; } -void ecs_reset_timer( - ecs_world_t *world, - ecs_entity_t timer) +static +int flecs_term_refs_finalize( + const ecs_world_t *world, + ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) { - EcsTimer *ptr = ecs_get_mut(world, timer, EcsTimer); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); - ptr->time = 0; -error: - return; -} + ecs_term_ref_t *src = &term->src; + ecs_term_ref_t *first = &term->first; + ecs_term_ref_t *second = &term->second; -ecs_entity_t ecs_set_rate( - ecs_world_t *world, - ecs_entity_t filter, - int32_t rate, - ecs_entity_t source) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + /* Include subsets for component by default, to support inheritance */ + if (!(first->id & EcsTraverseFlags)) { + first->id |= EcsSelf; + } - filter = ecs_set(world, filter, EcsRateFilter, { - .rate = rate, - .src = source - }); + /* Traverse Self by default for pair target */ + if (!(second->id & EcsTraverseFlags)) { + if (ECS_TERM_REF_ID(second) || second->name || (second->id & EcsIsEntity)) { + second->id |= EcsSelf; + } + } - ecs_system_t *system_data = ecs_poly_get(world, filter, ecs_system_t); - if (system_data) { - system_data->tick_source = filter; - } + /* Source defaults to This */ + if (!ECS_TERM_REF_ID(src) && (src->name == NULL) && !(src->id & EcsIsEntity)) { + src->id = EcsThis | ECS_TERM_REF_FLAGS(src); + src->id |= EcsIsVariable; + } -error: - return filter; -} + /* Initialize term identifier flags */ + if (flecs_term_ref_finalize_flags(src, ctx)) { + return -1; + } -void ecs_set_tick_source( - ecs_world_t *world, - ecs_entity_t system, - ecs_entity_t tick_source) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(system != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(tick_source != 0, ECS_INVALID_PARAMETER, NULL); + if (flecs_term_ref_finalize_flags(first, ctx)) { + return -1; + } - ecs_system_t *system_data = ecs_poly_get(world, system, ecs_system_t); - ecs_check(system_data != NULL, ECS_INVALID_PARAMETER, NULL); - - system_data->tick_source = tick_source; -error: - return; -} + if (flecs_term_ref_finalize_flags(second, ctx)) { + return -1; + } -static -void RandomizeTimers(ecs_iter_t *it) { - EcsTimer *timer = ecs_field(it, EcsTimer, 1); - int32_t i; - for (i = 0; i < it->count; i ++) { - timer[i].time = - ((ecs_ftime_t)rand() / (ecs_ftime_t)RAND_MAX) * timer[i].timeout; + /* Lookup term identifiers by name */ + if (flecs_term_ref_lookup(world, 0, src, ctx)) { + return -1; + } + if (flecs_term_ref_lookup(world, 0, first, ctx)) { + return -1; } -} -void ecs_randomize_timers( - ecs_world_t *world) -{ - ecs_observer(world, { - .entity = ecs_entity(world, { .name = "flecs.timer.RandomizeTimers" }), - .filter.terms = {{ - .id = ecs_id(EcsTimer) - }}, - .events = {EcsOnSet}, - .yield_existing = true, - .callback = RandomizeTimers - }); -} + ecs_entity_t first_id = 0; + ecs_entity_t oneof = 0; + if (first->id & EcsIsEntity) { + first_id = ECS_TERM_REF_ID(first); -void FlecsTimerImport( - ecs_world_t *world) -{ - ECS_MODULE(world, FlecsTimer); + if (!first_id) { + flecs_query_validator_error(ctx, "invalid \"0\" for first.name"); + return -1; + } - ECS_IMPORT(world, FlecsPipeline); + /* If first element of pair has OneOf property, lookup second element of + * pair in the value of the OneOf property */ + oneof = flecs_get_oneof(world, first_id); + } - ecs_set_name_prefix(world, "Ecs"); + if (flecs_term_ref_lookup(world, oneof, &term->second, ctx)) { + return -1; + } - flecs_bootstrap_component(world, EcsTimer); - flecs_bootstrap_component(world, EcsRateFilter); + /* If source is 0, reset traversal flags */ + if (ECS_TERM_REF_ID(src) == 0 && src->id & EcsIsEntity) { + src->id &= ~EcsTraverseFlags; + term->trav = 0; + } - ecs_set_hooks(world, EcsTimer, { - .ctor = ecs_default_ctor - }); + /* If source is wildcard, term won't return any data */ + if ((src->id & EcsIsVariable) && ecs_id_is_wildcard(ECS_TERM_REF_ID(src))) { + term->inout = EcsInOutNone; + } - /* Add EcsTickSource to timers and rate filters */ - ecs_system(world, { - .entity = ecs_entity(world, {.name = "AddTickSource", .add = { ecs_dependson(EcsPreFrame) }}), - .query.filter.terms = { - { .id = ecs_id(EcsTimer), .oper = EcsOr, .inout = EcsIn }, - { .id = ecs_id(EcsRateFilter), .oper = EcsAnd, .inout = EcsIn }, - { .id = ecs_id(EcsTickSource), .oper = EcsNot, .inout = EcsOut} - }, - .callback = AddTickSource - }); + /* If operator is Not, automatically convert wildcard queries to any */ + if (term->oper == EcsNot) { + if (ECS_TERM_REF_ID(first) == EcsWildcard) { + first->id = EcsAny | ECS_TERM_REF_FLAGS(first); + } - /* Timer handling */ - ecs_system(world, { - .entity = ecs_entity(world, {.name = "ProgressTimers", .add = { ecs_dependson(EcsPreFrame)}}), - .query.filter.terms = { - { .id = ecs_id(EcsTimer) }, - { .id = ecs_id(EcsTickSource) } - }, - .callback = ProgressTimers - }); + if (ECS_TERM_REF_ID(second) == EcsWildcard) { + second->id = EcsAny | ECS_TERM_REF_FLAGS(second); + } - /* Rate filter handling */ - ecs_system(world, { - .entity = ecs_entity(world, {.name = "ProgressRateFilters", .add = { ecs_dependson(EcsPreFrame)}}), - .query.filter.terms = { - { .id = ecs_id(EcsRateFilter), .inout = EcsIn }, - { .id = ecs_id(EcsTickSource), .inout = EcsOut } - }, - .callback = ProgressRateFilters - }); + term->id = flecs_wildcard_to_any(term->id); + } - /* TickSource without a timer or rate filter just increases each frame */ - ecs_system(world, { - .entity = ecs_entity(world, { .name = "ProgressTickSource", .add = { ecs_dependson(EcsPreFrame)}}), - .query.filter.terms = { - { .id = ecs_id(EcsTickSource), .inout = EcsOut }, - { .id = ecs_id(EcsRateFilter), .oper = EcsNot }, - { .id = ecs_id(EcsTimer), .oper = EcsNot } - }, - .callback = ProgressTickSource - }); + return 0; } -#endif - -/** - * @file addons/units.c - * @brief Units addon. - */ - - -#ifdef FLECS_UNITS +static +ecs_entity_t flecs_term_ref_get_entity( + const ecs_term_ref_t *ref) +{ + if (ref->id & EcsIsEntity) { + return ECS_TERM_REF_ID(ref); /* Id is known */ + } else if (ref->id & EcsIsVariable) { + /* Return wildcard for variables, as they aren't known yet */ + if (ECS_TERM_REF_ID(ref) != EcsAny) { + /* Any variable should not use wildcard, as this would return all + * ids matching a wildcard, whereas Any returns the first match */ + return EcsWildcard; + } else { + return EcsAny; + } + } else { + return 0; /* Term id is uninitialized */ + } +} -void FlecsUnitsImport( - ecs_world_t *world) +static +int flecs_term_populate_id( + ecs_term_t *term) { - ECS_MODULE(world, FlecsUnits); + ecs_entity_t first = flecs_term_ref_get_entity(&term->first); + ecs_entity_t second = flecs_term_ref_get_entity(&term->second); + ecs_id_t flags = term->id & ECS_ID_FLAGS_MASK; - ecs_set_name_prefix(world, "Ecs"); + if (first & ECS_ID_FLAGS_MASK) { + return -1; + } + if (second & ECS_ID_FLAGS_MASK) { + return -1; + } - EcsUnitPrefixes = ecs_entity(world, { - .name = "prefixes", - .add = { EcsModule } - }); + if ((second || (term->second.id & EcsIsEntity))) { + flags |= ECS_PAIR; + } - /* Initialize unit prefixes */ + if (!second && !ECS_HAS_ID_FLAG(flags, PAIR)) { + term->id = first | flags; + } else { + term->id = ecs_pair(first, second) | flags; + } - ecs_entity_t prev_scope = ecs_set_scope(world, EcsUnitPrefixes); + return 0; +} - EcsYocto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Yocto" }), - .symbol = "y", - .translation = { .factor = 10, .power = -24 } - }); - EcsZepto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Zepto" }), - .symbol = "z", - .translation = { .factor = 10, .power = -21 } - }); - EcsAtto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Atto" }), - .symbol = "a", - .translation = { .factor = 10, .power = -18 } - }); - EcsFemto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Femto" }), - .symbol = "a", - .translation = { .factor = 10, .power = -15 } - }); - EcsPico = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Pico" }), - .symbol = "p", - .translation = { .factor = 10, .power = -12 } - }); - EcsNano = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Nano" }), - .symbol = "n", - .translation = { .factor = 10, .power = -9 } - }); - EcsMicro = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Micro" }), - .symbol = "μ", - .translation = { .factor = 10, .power = -6 } - }); - EcsMilli = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Milli" }), - .symbol = "m", - .translation = { .factor = 10, .power = -3 } - }); - EcsCenti = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Centi" }), - .symbol = "c", - .translation = { .factor = 10, .power = -2 } - }); - EcsDeci = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Deci" }), - .symbol = "d", - .translation = { .factor = 10, .power = -1 } - }); - EcsDeca = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Deca" }), - .symbol = "da", - .translation = { .factor = 10, .power = 1 } - }); - EcsHecto = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Hecto" }), - .symbol = "h", - .translation = { .factor = 10, .power = 2 } - }); - EcsKilo = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Kilo" }), - .symbol = "k", - .translation = { .factor = 10, .power = 3 } - }); - EcsMega = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Mega" }), - .symbol = "M", - .translation = { .factor = 10, .power = 6 } - }); - EcsGiga = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Giga" }), - .symbol = "G", - .translation = { .factor = 10, .power = 9 } - }); - EcsTera = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Tera" }), - .symbol = "T", - .translation = { .factor = 10, .power = 12 } - }); - EcsPeta = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Peta" }), - .symbol = "P", - .translation = { .factor = 10, .power = 15 } - }); - EcsExa = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Exa" }), - .symbol = "E", - .translation = { .factor = 10, .power = 18 } - }); - EcsZetta = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Zetta" }), - .symbol = "Z", - .translation = { .factor = 10, .power = 21 } - }); - EcsYotta = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Yotta" }), - .symbol = "Y", - .translation = { .factor = 10, .power = 24 } - }); +static +int flecs_term_populate_from_id( + const ecs_world_t *world, + ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) +{ + ecs_entity_t first = 0; + ecs_entity_t second = 0; - EcsKibi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Kibi" }), - .symbol = "Ki", - .translation = { .factor = 1024, .power = 1 } - }); - EcsMebi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Mebi" }), - .symbol = "Mi", - .translation = { .factor = 1024, .power = 2 } - }); - EcsGibi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Gibi" }), - .symbol = "Gi", - .translation = { .factor = 1024, .power = 3 } - }); - EcsTebi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Tebi" }), - .symbol = "Ti", - .translation = { .factor = 1024, .power = 4 } - }); - EcsPebi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Pebi" }), - .symbol = "Pi", - .translation = { .factor = 1024, .power = 5 } - }); - EcsExbi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Exbi" }), - .symbol = "Ei", - .translation = { .factor = 1024, .power = 6 } - }); - EcsZebi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Zebi" }), - .symbol = "Zi", - .translation = { .factor = 1024, .power = 7 } - }); - EcsYobi = ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t){ - .entity = ecs_entity(world, { .name = "Yobi" }), - .symbol = "Yi", - .translation = { .factor = 1024, .power = 8 } - }); + if (ECS_HAS_ID_FLAG(term->id, PAIR)) { + first = ECS_PAIR_FIRST(term->id); + second = ECS_PAIR_SECOND(term->id); - ecs_set_scope(world, prev_scope); + if (!first) { + flecs_query_validator_error(ctx, "missing first element in term.id"); + return -1; + } + if (!second) { + if (first != EcsChildOf) { + flecs_query_validator_error(ctx, "missing second element in term.id"); + return -1; + } else { + /* (ChildOf, 0) is allowed so query can be used to efficiently + * query for root entities */ + } + } + } else { + first = term->id & ECS_COMPONENT_MASK; + if (!first) { + flecs_query_validator_error(ctx, "missing first element in term.id"); + return -1; + } + } - /* Duration units */ + ecs_entity_t term_first = flecs_term_ref_get_entity(&term->first); + if (term_first) { + if ((uint32_t)term_first != (uint32_t)first) { + flecs_query_validator_error(ctx, "mismatch between term.id and term.first"); + return -1; + } + } else { + ecs_entity_t first_id = ecs_get_alive(world, first); + if (!first_id) { + term->first.id = first | ECS_TERM_REF_FLAGS(&term->first); + } else { + term->first.id = first_id | ECS_TERM_REF_FLAGS(&term->first); + } + } - EcsDuration = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Duration" }); - prev_scope = ecs_set_scope(world, EcsDuration); + ecs_entity_t term_second = flecs_term_ref_get_entity(&term->second); + if (term_second) { + if ((uint32_t)term_second != second) { + flecs_query_validator_error(ctx, "mismatch between term.id and term.second"); + return -1; + } + } else if (second) { + ecs_entity_t second_id = ecs_get_alive(world, second); + if (!second_id) { + term->second.id = second | ECS_TERM_REF_FLAGS(&term->second); + } else { + term->second.id = second_id | ECS_TERM_REF_FLAGS(&term->second); + } + } - EcsSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Seconds" }), - .quantity = EcsDuration, - .symbol = "s" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsSeconds, - .kind = EcsF32 - }); - EcsPicoSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "PicoSeconds" }), - .quantity = EcsDuration, - .base = EcsSeconds, - .prefix = EcsPico }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsPicoSeconds, - .kind = EcsF32 - }); + return 0; +} +static +int flecs_term_verify_eq_pred( + const ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) +{ + const ecs_term_ref_t *second = &term->second; + const ecs_term_ref_t *src = &term->src; + ecs_entity_t first_id = ECS_TERM_REF_ID(&term->first); + ecs_entity_t second_id = ECS_TERM_REF_ID(&term->second); + ecs_entity_t src_id = ECS_TERM_REF_ID(&term->src); - EcsNanoSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "NanoSeconds" }), - .quantity = EcsDuration, - .base = EcsSeconds, - .prefix = EcsNano }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsNanoSeconds, - .kind = EcsF32 - }); + if (term->oper != EcsAnd && term->oper != EcsNot && term->oper != EcsOr) { + flecs_query_validator_error(ctx, "invalid operator combination"); + goto error; + } - EcsMicroSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MicroSeconds" }), - .quantity = EcsDuration, - .base = EcsSeconds, - .prefix = EcsMicro }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMicroSeconds, - .kind = EcsF32 - }); + if ((src->id & EcsIsName) && (second->id & EcsIsName)) { + flecs_query_validator_error(ctx, "both sides of operator cannot be a name"); + goto error; + } - EcsMilliSeconds = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MilliSeconds" }), - .quantity = EcsDuration, - .base = EcsSeconds, - .prefix = EcsMilli }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMilliSeconds, - .kind = EcsF32 - }); + if ((src->id & EcsIsEntity) && (second->id & EcsIsEntity)) { + flecs_query_validator_error(ctx, "both sides of operator cannot be an entity"); + goto error; + } - EcsMinutes = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Minutes" }), - .quantity = EcsDuration, - .base = EcsSeconds, - .symbol = "min", - .translation = { .factor = 60, .power = 1 } }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMinutes, - .kind = EcsU32 - }); + if (!(src->id & EcsIsVariable)) { + flecs_query_validator_error(ctx, "left-hand of operator must be a variable"); + goto error; + } - EcsHours = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Hours" }), - .quantity = EcsDuration, - .base = EcsMinutes, - .symbol = "h", - .translation = { .factor = 60, .power = 1 } }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsHours, - .kind = EcsU32 - }); + if (first_id == EcsPredMatch && !(second->id & EcsIsName)) { + flecs_query_validator_error(ctx, "right-hand of match operator must be a string"); + goto error; + } - EcsDays = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Days" }), - .quantity = EcsDuration, - .base = EcsHours, - .symbol = "d", - .translation = { .factor = 24, .power = 1 } }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsDays, - .kind = EcsU32 - }); - ecs_set_scope(world, prev_scope); + if ((src->id & EcsIsVariable) && (second->id & EcsIsVariable)) { + if (src_id && src_id == second_id) { + flecs_query_validator_error(ctx, "both sides of operator are equal"); + goto error; + } + if (src->name && second->name && !ecs_os_strcmp(src->name, second->name)) { + flecs_query_validator_error(ctx, "both sides of operator are equal"); + goto error; + } + } - /* Time units */ + if (first_id == EcsPredEq) { + if (second_id == EcsPredEq || second_id == EcsPredMatch) { + flecs_query_validator_error(ctx, + "invalid right-hand side for equality operator"); + goto error; + } + } - EcsTime = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Time" }); - prev_scope = ecs_set_scope(world, EcsTime); + return 0; +error: + return -1; +} - EcsDate = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Date" }), - .quantity = EcsTime }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsDate, - .kind = EcsU32 - }); - ecs_set_scope(world, prev_scope); +static +int flecs_term_verify( + const ecs_world_t *world, + const ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) +{ + const ecs_term_ref_t *first = &term->first; + const ecs_term_ref_t *second = &term->second; + const ecs_term_ref_t *src = &term->src; + ecs_entity_t first_id = 0, second_id = 0; + ecs_id_t flags = term->id & ECS_ID_FLAGS_MASK; + ecs_id_t id = term->id; - /* Mass units */ + if ((src->id & EcsIsName) && (second->id & EcsIsName)) { + flecs_query_validator_error(ctx, "mismatch between term.id_flags & term.id"); + return -1; + } - EcsMass = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Mass" }); - prev_scope = ecs_set_scope(world, EcsMass); - EcsGrams = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Grams" }), - .quantity = EcsMass, - .symbol = "g" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsGrams, - .kind = EcsF32 - }); - EcsKiloGrams = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KiloGrams" }), - .quantity = EcsMass, - .prefix = EcsKilo, - .base = EcsGrams }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKiloGrams, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); + ecs_entity_t src_id = ECS_TERM_REF_ID(src); + if (first->id & EcsIsEntity) { + first_id = ECS_TERM_REF_ID(first); + } - /* Electric current units */ + if (second->id & EcsIsEntity) { + second_id = ECS_TERM_REF_ID(second); + } - EcsElectricCurrent = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "ElectricCurrent" }); - prev_scope = ecs_set_scope(world, EcsElectricCurrent); - EcsAmpere = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Ampere" }), - .quantity = EcsElectricCurrent, - .symbol = "A" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsAmpere, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); + if (first_id == EcsPredEq || first_id == EcsPredMatch || first_id == EcsPredLookup) { + return flecs_term_verify_eq_pred(term, ctx); + } - /* Amount of substance units */ + if (ecs_term_ref_is_set(second) && !ECS_HAS_ID_FLAG(flags, PAIR)) { + flecs_query_validator_error(ctx, "expected PAIR flag for term with pair"); + return -1; + } else if (!ecs_term_ref_is_set(second) && ECS_HAS_ID_FLAG(flags, PAIR)) { + if (first_id != EcsChildOf) { + flecs_query_validator_error(ctx, "unexpected PAIR flag for term without pair"); + return -1; + } else { + /* Exception is made for ChildOf so we can use (ChildOf, 0) to match + * all entities in the root */ + } + } - EcsAmount = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Amount" }); - prev_scope = ecs_set_scope(world, EcsAmount); - EcsMole = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Mole" }), - .quantity = EcsAmount, - .symbol = "mol" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMole, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); + if (!ecs_term_ref_is_set(src)) { + flecs_query_validator_error(ctx, "term.src is not initialized"); + return -1; + } - /* Luminous intensity units */ + if (!ecs_term_ref_is_set(first)) { + flecs_query_validator_error(ctx, "term.first is not initialized"); + return -1; + } - EcsLuminousIntensity = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "LuminousIntensity" }); - prev_scope = ecs_set_scope(world, EcsLuminousIntensity); - EcsCandela = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Candela" }), - .quantity = EcsLuminousIntensity, - .symbol = "cd" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsCandela, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); + if (ECS_HAS_ID_FLAG(flags, PAIR)) { + if (!ECS_PAIR_FIRST(id)) { + flecs_query_validator_error(ctx, "invalid 0 for first element in pair id"); + return -1; + } + if ((ECS_PAIR_FIRST(id) != EcsChildOf) && !ECS_PAIR_SECOND(id)) { + flecs_query_validator_error(ctx, "invalid 0 for second element in pair id"); + return -1; + } + + if ((first->id & EcsIsEntity) && + (ecs_entity_t_lo(first_id) != ECS_PAIR_FIRST(id))) + { + flecs_query_validator_error(ctx, "mismatch between term.id and term.first"); + return -1; + } + if ((first->id & EcsIsVariable) && + !ecs_id_is_wildcard(ECS_PAIR_FIRST(id))) + { + char *id_str = ecs_id_str(world, id); + flecs_query_validator_error(ctx, + "expected wildcard for variable term.first (got %s)", id_str); + ecs_os_free(id_str); + return -1; + } + + if ((second->id & EcsIsEntity) && + (ecs_entity_t_lo(second_id) != ECS_PAIR_SECOND(id))) + { + flecs_query_validator_error(ctx, "mismatch between term.id and term.second"); + return -1; + } + if ((second->id & EcsIsVariable) && + !ecs_id_is_wildcard(ECS_PAIR_SECOND(id))) + { + char *id_str = ecs_id_str(world, id); + flecs_query_validator_error(ctx, + "expected wildcard for variable term.second (got %s)", id_str); + ecs_os_free(id_str); + return -1; + } + } else { + ecs_entity_t component = id & ECS_COMPONENT_MASK; + if (!component) { + flecs_query_validator_error(ctx, "missing component id"); + return -1; + } + if ((first->id & EcsIsEntity) && + (ecs_entity_t_lo(first_id) != ecs_entity_t_lo(component))) + { + flecs_query_validator_error(ctx, "mismatch between term.id and term.first"); + return -1; + } + if ((first->id & EcsIsVariable) && !ecs_id_is_wildcard(component)) { + char *id_str = ecs_id_str(world, id); + flecs_query_validator_error(ctx, + "expected wildcard for variable term.first (got %s)", id_str); + ecs_os_free(id_str); + return -1; + } + } + + if (first_id) { + if (ecs_term_ref_is_set(second)) { + ecs_flags64_t mask = EcsIsEntity | EcsIsVariable; + if ((src->id & mask) == (second->id & mask)) { + bool is_same = false; + if (src->id & EcsIsEntity) { + is_same = src_id == second_id; + } else if (src->name && second->name) { + is_same = !ecs_os_strcmp(src->name, second->name); + } + + if (is_same && ecs_has_id(world, first_id, EcsAcyclic) + && !(term->flags_ & EcsTermReflexive)) + { + char *pred_str = ecs_get_path(world, term->first.id); + flecs_query_validator_error(ctx, "term with acyclic relationship" + " '%s' cannot have same subject and object", pred_str); + ecs_os_free(pred_str); + return -1; + } + } + } + + if (second_id && !ecs_id_is_wildcard(second_id)) { + ecs_entity_t oneof = flecs_get_oneof(world, first_id); + if (oneof) { + if (!ecs_has_pair(world, second_id, EcsChildOf, oneof)) { + char *second_str = ecs_get_path(world, second_id); + char *oneof_str = ecs_get_path(world, oneof); + char *id_str = ecs_id_str(world, term->id); + flecs_query_validator_error(ctx, + "invalid target '%s' for %s: must be child of '%s'", + second_str, id_str, oneof_str); + ecs_os_free(second_str); + ecs_os_free(oneof_str); + ecs_os_free(id_str); + return -1; + } + } + } + } + + if (term->trav) { + if (!ecs_has_id(world, term->trav, EcsTraversable)) { + char *r_str = ecs_get_path(world, term->trav); + flecs_query_validator_error(ctx, + "cannot traverse non-traversable relationship '%s'", r_str); + ecs_os_free(r_str); + return -1; + } + } + + return 0; +} + +static +int flecs_term_finalize( + const ecs_world_t *world, + ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) +{ + ctx->term = term; + + ecs_term_ref_t *src = &term->src; + ecs_term_ref_t *first = &term->first; + ecs_term_ref_t *second = &term->second; + ecs_flags64_t first_flags = ECS_TERM_REF_FLAGS(first); + ecs_flags64_t second_flags = ECS_TERM_REF_FLAGS(second); + + if (first->name && (first->id & ~EcsTermRefFlags)) { + flecs_query_validator_error(ctx, + "first.name and first.id have competing values"); + return -1; + } + if (src->name && (src->id & ~EcsTermRefFlags)) { + flecs_query_validator_error(ctx, + "src.name and src.id have competing values"); + return -1; + } + if (second->name && (second->id & ~EcsTermRefFlags)) { + flecs_query_validator_error(ctx, + "second.name and second.id have competing values"); + return -1; + } + + if (term->id & ~ECS_ID_FLAGS_MASK) { + if (flecs_term_populate_from_id(world, term, ctx)) { + return -1; + } + } + + if (flecs_term_refs_finalize(world, term, ctx)) { + return -1; + } + + ecs_entity_t first_id = ECS_TERM_REF_ID(first); + ecs_entity_t second_id = ECS_TERM_REF_ID(second); + ecs_entity_t src_id = ECS_TERM_REF_ID(src); + + if ((first->id & EcsIsVariable) && (first_id == EcsAny)) { + term->flags_ |= EcsTermMatchAny; + } + + if ((second->id & EcsIsVariable) && (second_id == EcsAny)) { + term->flags_ |= EcsTermMatchAny; + } + + if ((src->id & EcsIsVariable) && (src_id == EcsAny)) { + term->flags_ |= EcsTermMatchAnySrc; + } + + ecs_flags64_t ent_var_mask = EcsIsEntity | EcsIsVariable; + + /* If EcsVariable is used by itself, assign to predicate (singleton) */ + if ((ECS_TERM_REF_ID(src) == EcsVariable) && (src->id & EcsIsVariable)) { + src->id = first->id | ECS_TERM_REF_FLAGS(src); + src->id &= ~ent_var_mask; + src->id |= first->id & ent_var_mask; + src->name = first->name; + } + + if ((ECS_TERM_REF_ID(second) == EcsVariable) && (second->id & EcsIsVariable)) { + second->id = first->id | ECS_TERM_REF_FLAGS(second); + second->id &= ~ent_var_mask; + second->id |= first->id & ent_var_mask; + second->name = first->name; + } + + if (!(term->id & ~ECS_ID_FLAGS_MASK)) { + if (flecs_term_populate_id(term)) { + return -1; + } + } + + /* If term queries for !(ChildOf, _), translate it to the builtin + * (ChildOf, 0) index which is a cheaper way to find root entities */ + if (term->oper == EcsNot && term->id == ecs_pair(EcsChildOf, EcsAny)) { + /* Only if the source is not EcsAny */ + if (!(ECS_TERM_REF_ID(&term->src) == EcsAny && (term->src.id & EcsIsVariable))) { + term->oper = EcsAnd; + term->id = ecs_pair(EcsChildOf, 0); + second->id = 0; + second->id |= EcsSelf|EcsIsEntity; + } + } + + ecs_entity_t first_entity = 0; + if ((first->id & EcsIsEntity)) { + first_entity = first_id; + } + + ecs_id_record_t *idr = flecs_id_record_get(world, term->id); + ecs_flags32_t id_flags = 0; + if (idr) { + id_flags = idr->flags; + } else if (ECS_IS_PAIR(term->id)) { + ecs_id_record_t *wc_idr = flecs_id_record_get( + world, ecs_pair(ECS_PAIR_FIRST(term->id), EcsWildcard)); + if (wc_idr) { + id_flags = wc_idr->flags; + } + } + + if (src_id || src->name) { + if (!(term->src.id & EcsTraverseFlags)) { + if (id_flags & EcsIdOnInstantiateInherit) { + term->src.id |= EcsSelf|EcsUp; + if (!term->trav) { + term->trav = EcsIsA; + } + } else { + term->src.id |= EcsSelf; + + if (term->trav) { + char *idstr = ecs_id_str(world, term->id); + flecs_query_validator_error(ctx, ".trav specified for " + "'%s' which can't be inherited", idstr); + ecs_os_free(idstr); + return -1; + } + } + } + + if (term->first.id & EcsCascade) { + flecs_query_validator_error(ctx, + "cascade modifier invalid for term.first"); + return -1; + } + + if (term->second.id & EcsCascade) { + flecs_query_validator_error(ctx, + "cascade modifier invalid for term.second"); + return -1; + } + + if (term->first.id & EcsDesc) { + flecs_query_validator_error(ctx, + "desc modifier invalid for term.first"); + return -1; + } + + if (term->second.id & EcsDesc) { + flecs_query_validator_error(ctx, + "desc modifier invalid for term.second"); + return -1; + } + + if (term->src.id & EcsDesc && !(term->src.id & EcsCascade)) { + flecs_query_validator_error(ctx, + "desc modifier invalid without cascade"); + return -1; + } + + if (term->src.id & EcsCascade) { + /* Cascade always implies up traversal */ + term->src.id |= EcsUp; + } + + if ((src->id & EcsUp) && !term->trav) { + /* When traversal flags are specified but no traversal relationship, + * default to ChildOf, which is the most common kind of traversal. */ + term->trav = EcsChildOf; + } + } + + if (!(id_flags & EcsIdOnInstantiateInherit) && (term->trav == EcsIsA)) { + if (src->id & EcsUp) { + char *idstr = ecs_id_str(world, term->id); + flecs_query_validator_error(ctx, "IsA traversal not allowed " + "for '%s', add the (OnInstantiate, Inherit) trait", idstr); + ecs_os_free(idstr); + return -1; + } + } + + if (first_entity) { + /* Only enable inheritance for ids which are inherited from at the time + * of query creation. To force component inheritance to be evaluated, + * an application can explicitly set traversal flags. */ + if (flecs_id_record_get(world, ecs_pair(EcsIsA, first->id))) { + if (!((first_flags & EcsTraverseFlags) == EcsSelf)) { + term->flags_ |= EcsTermIdInherited; + } + } + + /* If component id is final, don't attempt component inheritance */ + ecs_record_t *first_record = flecs_entities_get(world, first_entity); + ecs_table_t *first_table = first_record ? first_record->table : NULL; + if (first_table) { + if (ecs_term_ref_is_set(second)) { + /* Add traversal flags for transitive relationships */ + if (!((second_flags & EcsTraverseFlags) == EcsSelf)) { + if (!((src->id & EcsIsVariable) && (src_id == EcsAny))) { + if (!((second->id & EcsIsVariable) && (second_id == EcsAny))) { + if (ecs_table_has_id(world, first_table, EcsTransitive)) { + term->flags_ |= EcsTermTransitive; + } + + if (ecs_table_has_id(world, first_table, EcsReflexive)) { + term->flags_ |= EcsTermReflexive; + } + } + } + } + + /* Check if term is union */ + if (ecs_table_has_id(world, first_table, EcsUnion)) { + /* Any wildcards don't need special handling as they just return + * (Rel, *). */ + if (ECS_IS_PAIR(term->id) && ECS_PAIR_SECOND(term->id) != EcsAny) { + term->flags_ |= EcsTermIsUnion; + } + } + } + } + + /* Check if term has toggleable component */ + if (id_flags & EcsIdCanToggle) { + /* If the term isn't matched on a #0 source */ + if (term->src.id != EcsIsEntity) { + term->flags_ |= EcsTermIsToggle; + + } + } + + /* Check if this is a member query */ +#ifdef FLECS_META + if (ecs_id(EcsMember) != 0) { + if (first_entity) { + if (ecs_has(world, first_entity, EcsMember)) { + term->flags_ |= EcsTermIsMember; + } + } + } +#endif + } + + if (ECS_TERM_REF_ID(first) == EcsVariable) { + flecs_query_validator_error(ctx, "invalid $ for term.first"); + return -1; + } + + if (term->oper == EcsAndFrom || term->oper == EcsOrFrom || term->oper == EcsNotFrom) { + if (term->inout != EcsInOutDefault && term->inout != EcsInOutNone) { + flecs_query_validator_error(ctx, + "invalid inout value for AndFrom/OrFrom/NotFrom term"); + return -1; + } + } + + /* Is term trivial/cacheable */ + bool cacheable_term = true; + bool trivial_term = true; + if (term->oper != EcsAnd || term->flags_ & EcsTermIsOr) { + trivial_term = false; + } + + if (ecs_id_is_wildcard(term->id)) { + if (!(id_flags & EcsIdExclusive)) { + trivial_term = false; + } + + if (first->id & EcsIsVariable) { + if (!ecs_id_is_wildcard(first_id) || first_id == EcsAny) { + trivial_term = false; + cacheable_term = false; + } + } + + if (second->id & EcsIsVariable) { + if (!ecs_id_is_wildcard(second_id) || second_id == EcsAny) { + trivial_term = false; + cacheable_term = false; + } + } + } + + if (!ecs_term_match_this(term)) { + trivial_term = false; + } + + if (term->flags_ & EcsTermTransitive) { + trivial_term = false; + cacheable_term = false; + } + + if (term->flags_ & EcsTermIdInherited) { + trivial_term = false; + cacheable_term = false; + } + + if (term->flags_ & EcsTermReflexive) { + trivial_term = false; + cacheable_term = false; + } + + if (term->trav && term->trav != EcsIsA) { + trivial_term = false; + } + + if (!(src->id & EcsSelf)) { + trivial_term = false; + } + + if (((ECS_TERM_REF_ID(&term->first) == EcsPredEq) || + (ECS_TERM_REF_ID(&term->first) == EcsPredMatch) || + (ECS_TERM_REF_ID(&term->first) == EcsPredLookup)) && + (term->first.id & EcsIsEntity)) + { + trivial_term = false; + cacheable_term = false; + } + + if (ECS_TERM_REF_ID(src) != EcsThis) { + cacheable_term = false; + } + + if (term->id == ecs_childof(0)) { + cacheable_term = false; + } + + if (term->flags_ & EcsTermIsMember) { + trivial_term = false; + cacheable_term = false; + } + + if (term->flags_ & EcsTermIsToggle) { + trivial_term = false; + } + + if (term->flags_ & EcsTermIsUnion) { + trivial_term = false; + cacheable_term = false; + } + + ECS_BIT_COND16(term->flags_, EcsTermIsTrivial, trivial_term); + ECS_BIT_COND16(term->flags_, EcsTermIsCacheable, cacheable_term); + + if (flecs_term_verify(world, term, ctx)) { + return -1; + } + + return 0; +} + +bool flecs_identifier_is_0( + const char *id) +{ + return id[0] == '#' && id[1] == '0' && !id[2]; +} + +bool ecs_term_ref_is_set( + const ecs_term_ref_t *ref) +{ + return ECS_TERM_REF_ID(ref) != 0 || ref->name != NULL || ref->id & EcsIsEntity; +} + +bool ecs_term_is_initialized( + const ecs_term_t *term) +{ + return term->id != 0 || ecs_term_ref_is_set(&term->first); +} + +bool ecs_term_match_this( + const ecs_term_t *term) +{ + return (term->src.id & EcsIsVariable) && + (ECS_TERM_REF_ID(&term->src) == EcsThis); +} + +bool ecs_term_match_0( + const ecs_term_t *term) +{ + return (!ECS_TERM_REF_ID(&term->src) && (term->src.id & EcsIsEntity)); +} + +int ecs_term_finalize( + const ecs_world_t *world, + ecs_term_t *term) +{ + ecs_query_validator_ctx_t ctx = {0}; + ctx.world = world; + ctx.term = term; + return flecs_term_finalize(world, term, &ctx); +} + +static +ecs_term_t* flecs_query_or_other_type( + ecs_query_t *q, + int32_t t) +{ + ecs_term_t *term = &q->terms[t]; + ecs_term_t *first = NULL; + while (t--) { + if (q->terms[t].oper != EcsOr) { + break; + } + first = &q->terms[t]; + } + + if (first) { + ecs_world_t *world = q->world; + const ecs_type_info_t *first_type = ecs_get_type_info(world, first->id); + const ecs_type_info_t *term_type = ecs_get_type_info(world, term->id); + + if (first_type == term_type) { + return NULL; + } + return first; + } else { + return NULL; + } +} + +static +void flecs_normalize_term_name( + ecs_term_ref_t *ref) +{ + if (ref->name && ref->name[0] == '$' && ref->name[1]) { + ecs_assert(ref->id & EcsIsVariable, ECS_INTERNAL_ERROR, NULL); + const char *old = ref->name; + ref->name = &old[1]; + + if (!ecs_os_strcmp(ref->name, "this")) { + ref->name = NULL; + ref->id |= EcsThis; + } + } +} + +static +int flecs_query_finalize_terms( + const ecs_world_t *world, + ecs_query_t *q, + const ecs_query_desc_t *desc) +{ + int8_t i, term_count = q->term_count, field_count = 0; + ecs_term_t *terms = q->terms; + int32_t scope_nesting = 0, cacheable_terms = 0; + bool cond_set = false; + + ecs_query_validator_ctx_t ctx = {0}; + ctx.world = world; + ctx.query = q; + ctx.desc = desc; + + q->flags |= EcsQueryMatchOnlyThis; + + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + if (term->oper == EcsOr) { + term->flags_ |= EcsTermIsOr; + if (i != (term_count - 1)) { + term[1].flags_ |= EcsTermIsOr; + } + } + } + + bool cacheable = true; + bool match_nothing = true; + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + bool prev_is_or = i && term[-1].oper == EcsOr; + bool nodata_term = false; + ctx.term_index = i; + + if (flecs_term_finalize(world, term, &ctx)) { + return -1; + } + + if (term->src.id != EcsIsEntity) { + /* If term doesn't match 0 entity, query doesn't match nothing */ + match_nothing = false; + } + + if (scope_nesting) { + /* Terms inside a scope are not cacheable */ + ECS_BIT_CLEAR16(term->flags_, EcsTermIsCacheable); + } + + /* If one of the terms in an OR chain isn't cacheable, none are */ + if (term->flags_ & EcsTermIsCacheable) { + /* Current term is marked as cacheable. Check if it is part of an OR + * chain, and if so, the previous term was also cacheable. */ + if (prev_is_or) { + if (term[-1].flags_ & EcsTermIsCacheable) { + cacheable_terms ++; + } else { + ECS_BIT_CLEAR16(term->flags_, EcsTermIsCacheable); + } + } else { + cacheable_terms ++; + } + + /* Toggle terms may be cacheable for fetching the initial component, + * but require an additional toggle instruction for evaluation. */ + if (term->flags_ & EcsTermIsToggle) { + cacheable = false; + } + } else if (prev_is_or) { + /* Current term is not cacheable. If it is part of an OR chain, mark + * previous terms in the chain as also not cacheable. */ + int32_t j; + for (j = i - 1; j >= 0; j --) { + if (terms[j].oper != EcsOr) { + break; + } + if (terms[j].flags_ & EcsTermIsCacheable) { + cacheable_terms --; + ECS_BIT_CLEAR16(terms[j].flags_, EcsTermIsCacheable); + } + } + } + + if (prev_is_or) { + if (ECS_TERM_REF_ID(&term[-1].src) != ECS_TERM_REF_ID(&term->src)) { + flecs_query_validator_error(&ctx, "mismatching src.id for OR terms"); + return -1; + } + if (term->oper != EcsOr && term->oper != EcsAnd) { + flecs_query_validator_error(&ctx, + "term after OR operator must use AND operator"); + return -1; + } + } else { + field_count ++; + } + + term->field_index = flecs_ito(int8_t, field_count - 1); + + if (ecs_id_is_wildcard(term->id)) { + q->flags |= EcsQueryMatchWildcards; + } else if (!(term->flags_ & EcsTermIsOr)) { + ECS_TERMSET_SET(q->static_id_fields, 1u << term->field_index); + } + + if (ecs_term_match_this(term)) { + ECS_BIT_SET(q->flags, EcsQueryMatchThis); + } else { + ECS_BIT_CLEAR(q->flags, EcsQueryMatchOnlyThis); + } + + if (ECS_TERM_REF_ID(term) == EcsPrefab) { + ECS_BIT_SET(q->flags, EcsQueryMatchPrefab); + } + if (ECS_TERM_REF_ID(term) == EcsDisabled && (term->src.id & EcsSelf)) { + ECS_BIT_SET(q->flags, EcsQueryMatchDisabled); + } + + if (term->oper == EcsNot && term->inout == EcsInOutDefault) { + term->inout = EcsInOutNone; + } + + if ((term->id == EcsWildcard) || (term->id == + ecs_pair(EcsWildcard, EcsWildcard))) + { + /* If term type is unknown beforehand, default the inout type to + * none. This prevents accidentally requesting lots of components, + * which can put stress on serializer code. */ + if (term->inout == EcsInOutDefault) { + term->inout = EcsInOutNone; + } + } + + if (term->src.id == EcsIsEntity) { + nodata_term = true; + } else if (term->inout == EcsInOutNone) { + nodata_term = true; + } else if (!ecs_get_type_info(world, term->id)) { + nodata_term = true; + } else if (term->flags_ & EcsTermIsUnion) { + nodata_term = true; + } else if (term->flags_ & EcsTermIsMember) { + nodata_term = true; + } else if (scope_nesting) { + nodata_term = true; + } else { + if (ecs_id_is_tag(world, term->id)) { + nodata_term = true; + } else if ((ECS_PAIR_SECOND(term->id) == EcsWildcard) || + (ECS_PAIR_SECOND(term->id) == EcsAny)) + { + /* If the second element of a pair is a wildcard and the first + * element is not a type, we can't know in advance what the + * type of the term is, so it can't provide data. */ + if (!ecs_get_type_info(world, ecs_pair_first(world, term->id))) { + nodata_term = true; + } + } + } + + if (!nodata_term && term->inout != EcsIn && term->inout != EcsInOutNone) { + /* Non-this terms default to EcsIn */ + if (ecs_term_match_this(term) || term->inout != EcsInOutDefault) { + q->flags |= EcsQueryHasOutTerms; + } + + bool match_non_this = !ecs_term_match_this(term) || + (term->src.id & EcsUp); + if (match_non_this && term->inout != EcsInOutDefault) { + q->flags |= EcsQueryHasNonThisOutTerms; + } + } + + if (!nodata_term) { + /* If terms in an OR chain do not all return the same type, the + * field will not provide any data */ + if (term->flags_ & EcsTermIsOr) { + ecs_term_t *first = flecs_query_or_other_type(q, i); + if (first) { + nodata_term = true; + } + q->data_fields &= (ecs_termset_t)~(1llu << term->field_index); + } + } + + if (term->flags_ & EcsTermIsMember) { + nodata_term = false; + } + + if (!nodata_term && term->oper != EcsNot) { + ECS_TERMSET_SET(q->data_fields, 1u << term->field_index); + + if (term->inout != EcsIn) { + ECS_TERMSET_SET(q->write_fields, 1u << term->field_index); + } + if (term->inout != EcsOut) { + ECS_TERMSET_SET(q->read_fields, 1u << term->field_index); + } + if (term->inout == EcsInOutDefault) { + ECS_TERMSET_SET(q->shared_readonly_fields, + 1u << term->field_index); + } + } + + if (ECS_TERM_REF_ID(&term->src) && (term->src.id & EcsIsEntity)) { + ECS_TERMSET_SET(q->fixed_fields, 1u << term->field_index); + } + + if ((term->src.id & EcsIsVariable) && + (ECS_TERM_REF_ID(&term->src) != EcsThis)) + { + ECS_TERMSET_SET(q->var_fields, 1u << term->field_index); + } + + ecs_id_record_t *idr = flecs_id_record_get(world, term->id); + if (idr) { + if (ecs_os_has_threading()) { + ecs_os_ainc(&idr->keep_alive); + } else { + idr->keep_alive ++; + } + + term->flags_ |= EcsTermKeepAlive; + + if (idr->flags & EcsIdIsSparse) { + term->flags_ |= EcsTermIsSparse; + ECS_BIT_CLEAR16(term->flags_, EcsTermIsTrivial); + if (term->flags_ & EcsTermIsCacheable) { + cacheable_terms --; + ECS_BIT_CLEAR16(term->flags_, EcsTermIsCacheable); + } + + /* Sparse component fields must be accessed with ecs_field_at */ + if (!nodata_term) { + q->row_fields |= flecs_uto(uint32_t, 1llu << i); + } + } + } + + if (term->oper == EcsOptional || term->oper == EcsNot) { + cond_set = true; + } + + ecs_entity_t first_id = ECS_TERM_REF_ID(&term->first); + if (first_id == EcsPredEq || first_id == EcsPredMatch || + first_id == EcsPredLookup) + { + q->flags |= EcsQueryHasPred; + term->src.id = (term->src.id & ~EcsTraverseFlags) | EcsSelf; + term->inout = EcsInOutNone; + } else { + if (!ecs_term_match_0(term) && term->oper != EcsNot && + term->oper != EcsNotFrom) + { + ECS_TERMSET_SET(q->set_fields, 1u << term->field_index); + } + } + + if (first_id == EcsScopeOpen) { + q->flags |= EcsQueryHasScopes; + scope_nesting ++; + } + + if (scope_nesting) { + term->flags_ |= EcsTermIsScope; + ECS_BIT_CLEAR16(term->flags_, EcsTermIsTrivial); + ECS_BIT_CLEAR16(term->flags_, EcsTermIsCacheable); + cacheable_terms --; + } + + if (first_id == EcsScopeClose) { + if (i && ECS_TERM_REF_ID(&terms[i - 1].first) == EcsScopeOpen) { + flecs_query_validator_error(&ctx, "invalid empty scope"); + return -1; + } + + q->flags |= EcsQueryHasScopes; + scope_nesting --; + } + + if (scope_nesting < 0) { + flecs_query_validator_error(&ctx, "'}' without matching '{'"); + } + } + + if (scope_nesting != 0) { + flecs_query_validator_error(&ctx, "missing '}'"); + return -1; + } + + if (term_count && (terms[term_count - 1].oper == EcsOr)) { + flecs_query_validator_error(&ctx, + "last term of query can't have OR operator"); + return -1; + } + + q->field_count = flecs_ito(int8_t, field_count); + + if (field_count) { + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + int32_t field = term->field_index; + q->ids[field] = term->id; + + if (term->flags_ & EcsTermIsOr) { + if (flecs_query_or_other_type(q, i)) { + q->sizes[field] = 0; + q->ids[field] = 0; + continue; + } + } + + ecs_id_record_t *idr = flecs_id_record_get(world, term->id); + if (idr) { + if (!ECS_IS_PAIR(idr->id) || ECS_PAIR_FIRST(idr->id) != EcsWildcard) { + if (idr->type_info) { + q->sizes[field] = idr->type_info->size; + q->ids[field] = idr->id; + } + } + } else { + const ecs_type_info_t *ti = ecs_get_type_info( + world, term->id); + if (ti) { + q->sizes[field] = ti->size; + q->ids[field] = term->id; + } + } + } + } + + ECS_BIT_COND(q->flags, EcsQueryHasCondSet, cond_set); + + /* Check if this is a trivial query */ + if ((q->flags & EcsQueryMatchOnlyThis)) { + if (!(q->flags & + (EcsQueryHasPred|EcsQueryMatchDisabled|EcsQueryMatchPrefab))) + { + ECS_BIT_SET(q->flags, EcsQueryMatchOnlySelf); + + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + ecs_term_ref_t *src = &term->src; + + if (src->id & EcsUp) { + ECS_BIT_CLEAR(q->flags, EcsQueryMatchOnlySelf); + } + + if (!(term->flags_ & EcsTermIsTrivial)) { + break; + } + } + + if (term_count && (i == term_count)) { + ECS_BIT_SET(q->flags, EcsQueryIsTrivial); + } + } + } + + /* Set cacheable flags */ + ECS_BIT_COND(q->flags, EcsQueryHasCacheable, + cacheable_terms != 0); + + /* Exclude queries with order_by from setting the IsCacheable flag. This + * allows the routine that evaluates entirely cached queries to use more + * optimized logic as it doesn't have to deal with order_by edge cases */ + ECS_BIT_COND(q->flags, EcsQueryIsCacheable, + cacheable && (cacheable_terms == term_count) && + !desc->order_by_callback); + + /* If none of the terms match a source, the query matches nothing */ + ECS_BIT_COND(q->flags, EcsQueryMatchNothing, match_nothing); + + for (i = 0; i < q->term_count; i ++) { + ecs_term_t *term = &q->terms[i]; + /* Post process term names in case they were used to create variables */ + flecs_normalize_term_name(&term->first); + flecs_normalize_term_name(&term->second); + flecs_normalize_term_name(&term->src); + } + + return 0; +} + +static +int flecs_query_query_populate_terms( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_query_t *q, + const ecs_query_desc_t *desc) +{ + /* Count number of initialized terms in desc->terms */ + int32_t i, term_count = 0; + for (i = 0; i < FLECS_TERM_COUNT_MAX; i ++) { + if (!ecs_term_is_initialized(&desc->terms[i])) { + break; + } + term_count ++; + } + + /* Copy terms from array to query */ + if (term_count) { + ecs_os_memcpy_n(&q->terms, desc->terms, ecs_term_t, term_count); + } + + /* Parse query expression if set */ + const char *expr = desc->expr; + if (expr && expr[0]) { + #ifdef FLECS_SCRIPT + ecs_script_impl_t script = { + .pub.world = world, + .pub.name = desc->entity ? ecs_get_name(world, desc->entity) : NULL, + .pub.code = expr + }; + + /* Allocate buffer that's large enough to tokenize the query string */ + script.token_buffer_size = ecs_os_strlen(expr) * 2 + 1; + script.token_buffer = flecs_alloc( + &flecs_query_impl(q)->stage->allocator, script.token_buffer_size); + + if (flecs_terms_parse(&script.pub, &q->terms[term_count], + &term_count)) + { + flecs_free(&stage->allocator, + script.token_buffer_size, script.token_buffer); + goto error; + } + + /* Store on query object so we can free later */ + flecs_query_impl(q)->tokens = script.token_buffer; + flecs_query_impl(q)->tokens_len = + flecs_ito(int16_t, script.token_buffer_size); + #else + (void)world; + (void)stage; + ecs_err("cannot parse query expression: script addon required"); + goto error; + #endif + } + + q->term_count = flecs_ito(int8_t, term_count); + + return 0; +error: + return -1; +} + +#ifndef FLECS_SANITIZE +static +bool flecs_query_finalize_simple( + ecs_world_t *world, + ecs_query_t *q, + const ecs_query_desc_t *desc) +{ + /* Filter out queries that aren't simple enough */ + if (desc->expr) { + return false; + } + + if (desc->order_by_callback || desc->group_by_callback) { + return false; + } + + int8_t i, term_count; + for (i = 0; i < FLECS_TERM_COUNT_MAX; i ++) { + if (!ecs_term_is_initialized(&desc->terms[i])) { + break; + } + + ecs_id_t id = desc->terms[i].id; + if (ecs_id_is_wildcard(id)) { + return false; + } + + if (id == EcsThis || ECS_PAIR_FIRST(id) == EcsThis || + ECS_PAIR_SECOND(id) == EcsThis) + { + return false; + } + + if (id == EcsVariable || ECS_PAIR_FIRST(id) == EcsVariable || + ECS_PAIR_SECOND(id) == EcsVariable) + { + return false; + } + + if (id == EcsPrefab || id == EcsDisabled) { + return false; + } + + ecs_term_t term = { .id = desc->terms[i].id }; + if (ecs_os_memcmp_t(&term, &desc->terms[i], ecs_term_t)) { + return false; + } + } + + if (!i) { + return false; /* No terms */ + } + + term_count = i; + ecs_os_memcpy_n(&q->terms, desc->terms, ecs_term_t, term_count); + + /* Simple query that only queries for component ids */ + + /* Populate terms */ + int8_t cacheable_count = 0, trivial_count = 0, up_count = 0; + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &q->terms[i]; + ecs_id_t id = term->id; + + ecs_entity_t first = id; + if (ECS_IS_PAIR(id)) { + ecs_entity_t second = flecs_entities_get_alive(world, + ECS_PAIR_SECOND(id)); + first = flecs_entities_get_alive(world, ECS_PAIR_FIRST(id)); + term->second.id = second | EcsIsEntity | EcsSelf; + } + + term->field_index = i; + term->first.id = first | EcsIsEntity | EcsSelf; + term->src.id = EcsThis | EcsIsVariable | EcsSelf; + + q->ids[i] = id; + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + idr->keep_alive ++; + term->flags_ |= EcsTermKeepAlive; + } + + if (!idr && ECS_IS_PAIR(id)) { + idr = flecs_id_record_get(world, + ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard)); + } + + bool cacheable = true, trivial = true; + if (idr) { + if (idr->type_info) { + q->sizes[i] = idr->type_info->size; + q->flags |= EcsQueryHasOutTerms; + q->data_fields |= (ecs_termset_t)(1llu << i); + } + + if (idr->flags & EcsIdOnInstantiateInherit) { + term->src.id |= EcsUp; + term->trav = EcsIsA; + up_count ++; + } + + if (idr->flags & EcsIdCanToggle) { + term->flags_ |= EcsTermIsToggle; + trivial = false; + } + + if (ECS_IS_PAIR(id)) { + if (idr->flags & EcsIdIsUnion) { + term->flags_ |= EcsTermIsUnion; + trivial = false; + cacheable = false; + } + } + + if (idr->flags & EcsIdIsSparse) { + term->flags_ |= EcsTermIsSparse; + cacheable = false; trivial = false; + q->row_fields |= flecs_uto(uint32_t, 1llu << i); + } + } + + if (ECS_IS_PAIR(id)) { + if (ecs_has_id(world, first, EcsTransitive)) { + term->flags_ |= EcsTermTransitive; + trivial = false; + cacheable = false; + } + if (ecs_has_id(world, first, EcsReflexive)) { + term->flags_ |= EcsTermReflexive; + trivial = false; + cacheable = false; + } + } + + if (flecs_id_record_get(world, ecs_pair(EcsIsA, first)) != NULL) { + term->flags_ |= EcsTermIdInherited; + cacheable = false; trivial = false; + } + + if (cacheable) { + term->flags_ |= EcsTermIsCacheable; + cacheable_count ++; + } + + if (trivial) { + term->flags_ |= EcsTermIsTrivial; + trivial_count ++; + } + } + + /* Initialize static data */ + q->term_count = term_count; + q->field_count = term_count; + q->set_fields = (ecs_termset_t)((1llu << i) - 1); + q->static_id_fields = (ecs_termset_t)((1llu << i) - 1); + q->flags |= EcsQueryMatchThis|EcsQueryMatchOnlyThis|EcsQueryHasTableThisVar; + + if (cacheable_count) { + q->flags |= EcsQueryHasCacheable; + } + + if (cacheable_count == term_count && trivial_count == term_count) { + q->flags |= EcsQueryIsCacheable|EcsQueryIsTrivial; + } + + if (!up_count) { + q->flags |= EcsQueryMatchOnlySelf; + } + + return true; +} +#endif + +static +char* flecs_query_append_token( + char *dst, + const char *src) +{ + int32_t len = ecs_os_strlen(src); + ecs_os_memcpy(dst, src, len + 1); + return dst + len + 1; +} + +static +void flecs_query_populate_tokens( + ecs_query_impl_t *impl) +{ + ecs_query_t *q = &impl->pub; + int32_t i, term_count = q->term_count; + + char *old_tokens = impl->tokens; + int32_t old_tokens_len = impl->tokens_len; + impl->tokens = NULL; + impl->tokens_len = 0; + + /* Step 1: determine size of token buffer */ + int32_t len = 0; + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &q->terms[i]; + + if (term->first.name) { + len += ecs_os_strlen(term->first.name) + 1; + } + if (term->second.name) { + len += ecs_os_strlen(term->second.name) + 1; + } + if (term->src.name) { + len += ecs_os_strlen(term->src.name) + 1; + } + } + + /* Step 2: reassign term tokens to buffer */ + if (len) { + impl->tokens = flecs_alloc(&impl->stage->allocator, len); + impl->tokens_len = flecs_ito(int16_t, len); + char *token = impl->tokens, *next; + + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &q->terms[i]; + if (term->first.name) { + next = flecs_query_append_token(token, term->first.name); + term->first.name = token; + token = next; + } + if (term->second.name) { + next = flecs_query_append_token(token, term->second.name); + term->second.name = token; + token = next; + } + if (term->src.name) { + next = flecs_query_append_token(token, term->src.name); + term->src.name = token; + token = next; + } + } + } + + if (old_tokens) { + flecs_free(&impl->stage->allocator, old_tokens_len, old_tokens); + } +} + +int flecs_query_finalize_query( + ecs_world_t *world, + ecs_query_t *q, + const ecs_query_desc_t *desc) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_query_desc_t was not initialized to zero"); + ecs_stage_t *stage = flecs_stage_from_world(&world); + + q->flags |= desc->flags | world->default_query_flags; + + /* Fast routine that initializes simple queries and skips complex validation + * logic if it's not needed. When running in sanitized mode, always take the + * slow path. This in combination with the test suite ensures that the + * result of the fast & slow code is the same. */ + #ifndef FLECS_SANITIZE + if (flecs_query_finalize_simple(world, q, desc)) { + return 0; + } + #endif + + /* Populate term array from desc terms & DSL expression */ + if (flecs_query_query_populate_terms(world, stage, q, desc)) { + goto error; + } + + /* Ensure all fields are consistent and properly filled out */ + if (flecs_query_finalize_terms(world, q, desc)) { + goto error; + } + + /* Store remaining string tokens in terms (after entity lookups) in single + * token buffer which simplifies memory management & reduces allocations. */ + flecs_query_populate_tokens(flecs_query_impl(q)); + + return 0; +error: + return -1; +} + + +static +ecs_entity_index_page_t* flecs_entity_index_ensure_page( + ecs_entity_index_t *index, + uint32_t id) +{ + int32_t page_index = (int32_t)(id >> FLECS_ENTITY_PAGE_BITS); + if (page_index >= ecs_vec_count(&index->pages)) { + ecs_vec_set_min_count_zeromem_t(index->allocator, &index->pages, + ecs_entity_index_page_t*, page_index + 1); + } + + ecs_entity_index_page_t **page_ptr = ecs_vec_get_t(&index->pages, + ecs_entity_index_page_t*, page_index); + ecs_entity_index_page_t *page = *page_ptr; + if (!page) { + page = *page_ptr = flecs_bcalloc(&index->page_allocator); + ecs_assert(page != NULL, ECS_OUT_OF_MEMORY, NULL); + } + + return page; +} + +void flecs_entity_index_init( + ecs_allocator_t *allocator, + ecs_entity_index_t *index) +{ + index->allocator = allocator; + index->alive_count = 1; + ecs_vec_init_t(allocator, &index->dense, uint64_t, 1); + ecs_vec_set_count_t(allocator, &index->dense, uint64_t, 1); + ecs_vec_init_t(allocator, &index->pages, ecs_entity_index_page_t*, 0); + flecs_ballocator_init(&index->page_allocator, + ECS_SIZEOF(ecs_entity_index_page_t)); +} + +void flecs_entity_index_fini( + ecs_entity_index_t *index) +{ + ecs_vec_fini_t(index->allocator, &index->dense, uint64_t); +#if defined(FLECS_SANITIZE) || defined(FLECS_USE_OS_ALLOC) + int32_t i, count = ecs_vec_count(&index->pages); + ecs_entity_index_page_t **pages = ecs_vec_first(&index->pages); + for (i = 0; i < count; i ++) { + flecs_bfree(&index->page_allocator, pages[i]); + } +#endif + ecs_vec_fini_t(index->allocator, &index->pages, ecs_entity_index_page_t*); + flecs_ballocator_fini(&index->page_allocator); +} + +ecs_record_t* flecs_entity_index_get_any( + const ecs_entity_index_t *index, + uint64_t entity) +{ + uint32_t id = (uint32_t)entity; + int32_t page_index = (int32_t)(id >> FLECS_ENTITY_PAGE_BITS); + ecs_entity_index_page_t *page = ecs_vec_get_t(&index->pages, + ecs_entity_index_page_t*, page_index)[0]; + ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; + ecs_assert(r->dense != 0, ECS_INVALID_PARAMETER, + "entity %u does not exist", (uint32_t)entity); + return r; +} + +ecs_record_t* flecs_entity_index_get( + const ecs_entity_index_t *index, + uint64_t entity) +{ + ecs_record_t *r = flecs_entity_index_get_any(index, entity); + ecs_assert(r->dense < index->alive_count, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] == entity, + ECS_INVALID_PARAMETER, "mismatching liveliness generation for entity"); + return r; +} + +ecs_record_t* flecs_entity_index_try_get_any( + const ecs_entity_index_t *index, + uint64_t entity) +{ + uint32_t id = (uint32_t)entity; + int32_t page_index = (int32_t)(id >> FLECS_ENTITY_PAGE_BITS); + if (page_index >= ecs_vec_count(&index->pages)) { + return NULL; + } + + ecs_entity_index_page_t *page = ecs_vec_get_t(&index->pages, + ecs_entity_index_page_t*, page_index)[0]; + if (!page) { + return NULL; + } + + ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; + if (!r->dense) { + return NULL; + } + + return r; +} + +ecs_record_t* flecs_entity_index_try_get( + const ecs_entity_index_t *index, + uint64_t entity) +{ + ecs_record_t *r = flecs_entity_index_try_get_any(index, entity); + if (r) { + if (r->dense >= index->alive_count) { + return NULL; + } + if (ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] != entity) { + return NULL; + } + } + return r; +} + +ecs_record_t* flecs_entity_index_ensure( + ecs_entity_index_t *index, + uint64_t entity) +{ + uint32_t id = (uint32_t)entity; + ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id); + ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; + + int32_t dense = r->dense; + if (dense) { + /* Entity is already alive, nothing to be done */ + if (dense < index->alive_count) { + ecs_assert( + ecs_vec_get_t(&index->dense, uint64_t, dense)[0] == entity, + ECS_INTERNAL_ERROR, NULL); + return r; + } + } else { + /* Entity doesn't have a dense index yet */ + ecs_vec_append_t(index->allocator, &index->dense, uint64_t)[0] = entity; + r->dense = dense = ecs_vec_count(&index->dense) - 1; + index->max_id = id > index->max_id ? id : index->max_id; + } + + ecs_assert(dense != 0, ECS_INTERNAL_ERROR, NULL); + + /* Entity is not alive, swap with first not alive element */ + uint64_t *ids = ecs_vec_first(&index->dense); + uint64_t e_swap = ids[index->alive_count]; + ecs_record_t *r_swap = flecs_entity_index_get_any(index, e_swap); + ecs_assert(r_swap->dense == index->alive_count, + ECS_INTERNAL_ERROR, NULL); + + r_swap->dense = dense; + r->dense = index->alive_count; + ids[dense] = e_swap; + ids[index->alive_count ++] = entity; + + ecs_assert(flecs_entity_index_is_alive(index, entity), + ECS_INTERNAL_ERROR, NULL); + + return r; +} + +void flecs_entity_index_remove( + ecs_entity_index_t *index, + uint64_t entity) +{ + ecs_record_t *r = flecs_entity_index_try_get(index, entity); + if (!r) { + /* Entity is not alive or doesn't exist, nothing to be done */ + return; + } + + int32_t dense = r->dense; + int32_t i_swap = -- index->alive_count; + uint64_t *e_swap_ptr = ecs_vec_get_t(&index->dense, uint64_t, i_swap); + uint64_t e_swap = e_swap_ptr[0]; + ecs_record_t *r_swap = flecs_entity_index_get_any(index, e_swap); + ecs_assert(r_swap->dense == i_swap, ECS_INTERNAL_ERROR, NULL); + + r_swap->dense = dense; + r->table = NULL; + r->idr = NULL; + r->row = 0; + r->dense = i_swap; + ecs_vec_get_t(&index->dense, uint64_t, dense)[0] = e_swap; + e_swap_ptr[0] = ECS_GENERATION_INC(entity); + ecs_assert(!flecs_entity_index_is_alive(index, entity), + ECS_INTERNAL_ERROR, NULL); +} + +void flecs_entity_index_make_alive( + ecs_entity_index_t *index, + uint64_t entity) +{ + ecs_record_t *r = flecs_entity_index_try_get_any(index, entity); + if (r) { + ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] = entity; + } +} + +uint64_t flecs_entity_index_get_alive( + const ecs_entity_index_t *index, + uint64_t entity) +{ + ecs_record_t *r = flecs_entity_index_try_get_any(index, entity); + if (r) { + return ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0]; + } else { + return 0; + } +} + +bool flecs_entity_index_is_alive( + const ecs_entity_index_t *index, + uint64_t entity) +{ + return flecs_entity_index_try_get(index, entity) != NULL; +} + +bool flecs_entity_index_is_valid( + const ecs_entity_index_t *index, + uint64_t entity) +{ + uint32_t id = (uint32_t)entity; + ecs_record_t *r = flecs_entity_index_try_get_any(index, id); + if (!r || !r->dense) { + /* Doesn't exist yet, so is valid */ + return true; + } + + /* If the id exists, it must be alive */ + return r->dense < index->alive_count; +} + +bool flecs_entity_index_exists( + const ecs_entity_index_t *index, + uint64_t entity) +{ + return flecs_entity_index_try_get_any(index, entity) != NULL; +} + +uint64_t flecs_entity_index_new_id( + ecs_entity_index_t *index) +{ + if (index->alive_count != ecs_vec_count(&index->dense)) { + /* Recycle id */ + return ecs_vec_get_t(&index->dense, uint64_t, index->alive_count ++)[0]; + } + + /* Create new id */ + uint32_t id = (uint32_t)++ index->max_id; + + /* Make sure id hasn't been issued before */ + ecs_assert(!flecs_entity_index_exists(index, id), ECS_INVALID_OPERATION, + "new entity %u id already in use (likely due to overlapping ranges)", (uint32_t)id); + + ecs_vec_append_t(index->allocator, &index->dense, uint64_t)[0] = id; + + ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id); + ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; + r->dense = index->alive_count ++; + ecs_assert(index->alive_count == ecs_vec_count(&index->dense), + ECS_INTERNAL_ERROR, NULL); + + return id; +} + +uint64_t* flecs_entity_index_new_ids( + ecs_entity_index_t *index, + int32_t count) +{ + int32_t alive_count = index->alive_count; + int32_t new_count = alive_count + count; + int32_t dense_count = ecs_vec_count(&index->dense); + + if (new_count < dense_count) { + /* Recycle ids */ + index->alive_count = new_count; + return ecs_vec_get_t(&index->dense, uint64_t, alive_count); + } + + /* Allocate new ids */ + ecs_vec_set_count_t(index->allocator, &index->dense, uint64_t, new_count); + int32_t i, to_add = new_count - dense_count; + for (i = 0; i < to_add; i ++) { + uint32_t id = (uint32_t)++ index->max_id; + + /* Make sure id hasn't been issued before */ + ecs_assert(!flecs_entity_index_exists(index, id), ECS_INVALID_OPERATION, + "new entity %u id already in use (likely due to overlapping ranges)", (uint32_t)id); + + int32_t dense = dense_count + i; + ecs_vec_get_t(&index->dense, uint64_t, dense)[0] = id; + ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id); + ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; + r->dense = dense; + } + + index->alive_count = new_count; + return ecs_vec_get_t(&index->dense, uint64_t, alive_count); +} + +void flecs_entity_index_set_size( + ecs_entity_index_t *index, + int32_t size) +{ + ecs_vec_set_size_t(index->allocator, &index->dense, uint64_t, size); +} + +int32_t flecs_entity_index_count( + const ecs_entity_index_t *index) +{ + return index->alive_count - 1; +} + +int32_t flecs_entity_index_size( + const ecs_entity_index_t *index) +{ + return ecs_vec_count(&index->dense) - 1; +} + +int32_t flecs_entity_index_not_alive_count( + const ecs_entity_index_t *index) +{ + return ecs_vec_count(&index->dense) - index->alive_count; +} + +void flecs_entity_index_clear( + ecs_entity_index_t *index) +{ + int32_t i, count = ecs_vec_count(&index->pages); + ecs_entity_index_page_t **pages = ecs_vec_first_t(&index->pages, + ecs_entity_index_page_t*); + for (i = 0; i < count; i ++) { + ecs_entity_index_page_t *page = pages[i]; + if (page) { + ecs_os_zeromem(page); + } + } + + ecs_vec_set_count_t(index->allocator, &index->dense, uint64_t, 1); + + index->alive_count = 1; + index->max_id = 0; +} + +const uint64_t* flecs_entity_index_ids( + const ecs_entity_index_t *index) +{ + return ecs_vec_get_t(&index->dense, uint64_t, 1); +} + +/** + * @file storage/id_index.c + * @brief Index for looking up tables by (component) id. + * + * An id record stores the administration for an in use (component) id, that is + * an id that has been used in tables. + * + * An id record contains a table cache, which stores the list of tables that + * have the id. Each entry in the cache (a table record) stores the first + * occurrence of the id in the table and the number of occurrences of the id in + * the table (in the case of wildcard ids). + * + * Id records are used in lots of scenarios, like uncached queries, or for + * getting a component array/component for an entity. + */ + + +static +ecs_id_record_elem_t* flecs_id_record_elem( + ecs_id_record_t *head, + ecs_id_record_elem_t *list, + ecs_id_record_t *idr) +{ + return ECS_OFFSET(idr, (uintptr_t)list - (uintptr_t)head); +} + +static +void flecs_id_record_elem_insert( + ecs_id_record_t *head, + ecs_id_record_t *idr, + ecs_id_record_elem_t *elem) +{ + ecs_id_record_elem_t *head_elem = flecs_id_record_elem(idr, elem, head); + ecs_id_record_t *cur = head_elem->next; + elem->next = cur; + elem->prev = head; + if (cur) { + ecs_id_record_elem_t *cur_elem = flecs_id_record_elem(idr, elem, cur); + cur_elem->prev = idr; + } + head_elem->next = idr; +} + +static +void flecs_id_record_elem_remove( + ecs_id_record_t *idr, + ecs_id_record_elem_t *elem) +{ + ecs_id_record_t *prev = elem->prev; + ecs_id_record_t *next = elem->next; + ecs_assert(prev != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_id_record_elem_t *prev_elem = flecs_id_record_elem(idr, elem, prev); + prev_elem->next = next; + if (next) { + ecs_id_record_elem_t *next_elem = flecs_id_record_elem(idr, elem, next); + next_elem->prev = prev; + } +} + +static +void flecs_insert_id_elem( + ecs_world_t *world, + ecs_id_record_t *idr, + ecs_id_t wildcard, + ecs_id_record_t *widr) +{ + ecs_assert(ecs_id_is_wildcard(wildcard), ECS_INTERNAL_ERROR, NULL); + if (!widr) { + widr = flecs_id_record_ensure(world, wildcard); + } + ecs_assert(widr != NULL, ECS_INTERNAL_ERROR, NULL); + + if (ECS_PAIR_SECOND(wildcard) == EcsWildcard) { + ecs_assert(ECS_PAIR_FIRST(wildcard) != EcsWildcard, + ECS_INTERNAL_ERROR, NULL); + flecs_id_record_elem_insert(widr, idr, &idr->first); + } else { + ecs_assert(ECS_PAIR_FIRST(wildcard) == EcsWildcard, + ECS_INTERNAL_ERROR, NULL); + flecs_id_record_elem_insert(widr, idr, &idr->second); + + if (idr->flags & EcsIdTraversable) { + flecs_id_record_elem_insert(widr, idr, &idr->trav); + } + } +} + +static +void flecs_remove_id_elem( + ecs_id_record_t *idr, + ecs_id_t wildcard) +{ + ecs_assert(ecs_id_is_wildcard(wildcard), ECS_INTERNAL_ERROR, NULL); + + if (ECS_PAIR_SECOND(wildcard) == EcsWildcard) { + ecs_assert(ECS_PAIR_FIRST(wildcard) != EcsWildcard, + ECS_INTERNAL_ERROR, NULL); + flecs_id_record_elem_remove(idr, &idr->first); + } else { + ecs_assert(ECS_PAIR_FIRST(wildcard) == EcsWildcard, + ECS_INTERNAL_ERROR, NULL); + flecs_id_record_elem_remove(idr, &idr->second); + + if (idr->flags & EcsIdTraversable) { + flecs_id_record_elem_remove(idr, &idr->trav); + } + } +} + +static +ecs_id_t flecs_id_record_hash( + ecs_id_t id) +{ + id = ecs_strip_generation(id); + if (ECS_IS_PAIR(id)) { + ecs_entity_t r = ECS_PAIR_FIRST(id); + ecs_entity_t o = ECS_PAIR_SECOND(id); + if (r == EcsAny) { + r = EcsWildcard; + } + if (o == EcsAny) { + o = EcsWildcard; + } + id = ecs_pair(r, o); + } + return id; +} + +void flecs_id_record_init_sparse( + ecs_world_t *world, + ecs_id_record_t *idr) +{ + if (!idr->sparse) { + if (idr->flags & EcsIdIsSparse) { + ecs_assert(!(idr->flags & EcsIdIsUnion), ECS_CONSTRAINT_VIOLATED, + "cannot mix union and sparse traits"); + ecs_assert(idr->type_info != NULL, ECS_INVALID_OPERATION, + "only components can be marked as sparse"); + idr->sparse = flecs_walloc_t(world, ecs_sparse_t); + flecs_sparse_init(idr->sparse, NULL, NULL, idr->type_info->size); + } else + if (idr->flags & EcsIdIsUnion) { + idr->sparse = flecs_walloc_t(world, ecs_switch_t); + flecs_switch_init(idr->sparse, &world->allocator); + } + } +} + +static +void flecs_id_record_fini_sparse( + ecs_world_t *world, + ecs_id_record_t *idr) +{ + if (idr->sparse) { + if (idr->flags & EcsIdIsSparse) { + ecs_assert(flecs_sparse_count(idr->sparse) == 0, + ECS_INTERNAL_ERROR, NULL); + flecs_sparse_fini(idr->sparse); + flecs_wfree_t(world, ecs_sparse_t, idr->sparse); + } else + if (idr->flags & EcsIdIsUnion) { + flecs_switch_fini(idr->sparse); + flecs_wfree_t(world, ecs_switch_t, idr->sparse); + } else { + ecs_abort(ECS_INTERNAL_ERROR, "unknown sparse storage"); + } + } +} + +static +ecs_flags32_t flecs_id_record_event_flags( + ecs_world_t *world, + ecs_id_t id) +{ + ecs_observable_t *o = &world->observable; + ecs_flags32_t result = 0; + result |= flecs_observers_exist(o, id, EcsOnAdd) * EcsIdHasOnAdd; + result |= flecs_observers_exist(o, id, EcsOnRemove) * EcsIdHasOnRemove; + result |= flecs_observers_exist(o, id, EcsOnSet) * EcsIdHasOnSet; + result |= flecs_observers_exist(o, id, EcsOnTableFill) * EcsIdHasOnTableFill; + result |= flecs_observers_exist(o, id, EcsOnTableEmpty) * EcsIdHasOnTableEmpty; + result |= flecs_observers_exist(o, id, EcsOnTableCreate) * EcsIdHasOnTableCreate; + result |= flecs_observers_exist(o, id, EcsOnTableDelete) * EcsIdHasOnTableDelete; + return result; +} + +static +ecs_id_record_t* flecs_id_record_new( + ecs_world_t *world, + ecs_id_t id) +{ + ecs_id_record_t *idr, *idr_t = NULL; + ecs_id_t hash = flecs_id_record_hash(id); + if (hash >= FLECS_HI_ID_RECORD_ID) { + idr = flecs_bcalloc(&world->allocators.id_record); + ecs_map_insert_ptr(&world->id_index_hi, hash, idr); + } else { + idr = &world->id_index_lo[hash]; + ecs_os_zeromem(idr); + } + + ecs_table_cache_init(world, &idr->cache); + + idr->id = id; + idr->refcount = 1; + idr->reachable.current = -1; + + bool is_wildcard = ecs_id_is_wildcard(id); + bool is_pair = ECS_IS_PAIR(id); + + ecs_entity_t rel = 0, tgt = 0, role = id & ECS_ID_FLAGS_MASK; + if (is_pair) { + // rel = ecs_pair_first(world, id); + rel = ECS_PAIR_FIRST(id); + rel = flecs_entities_get_alive(world, rel); + ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL); + + /* Relationship object can be 0, as tables without a ChildOf + * relationship are added to the (ChildOf, 0) id record */ + tgt = ECS_PAIR_SECOND(id); + +#ifdef FLECS_DEBUG + /* Check constraints */ + if (tgt) { + tgt = flecs_entities_get_alive(world, tgt); + ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); + + /* Can't use relationship as target */ + if (ecs_has_id(world, tgt, EcsRelationship)) { + if (!ecs_id_is_wildcard(rel) && + !ecs_has_id(world, rel, EcsTrait)) + { + char *idstr = ecs_id_str(world, id); + char *tgtstr = ecs_id_str(world, tgt); + ecs_err("constraint violated: relationship '%s' cannot be used" + " as target in pair '%s'", tgtstr, idstr); + ecs_os_free(tgtstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } + } + } + + if (ecs_has_id(world, rel, EcsTarget)) { + char *idstr = ecs_id_str(world, id); + char *relstr = ecs_id_str(world, rel); + ecs_err("constraint violated: " + "%s: target '%s' cannot be used as relationship", + idstr, relstr); + ecs_os_free(relstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } + + if (tgt && !ecs_id_is_wildcard(tgt) && tgt != EcsUnion) { + /* Check if target of relationship satisfies OneOf property */ + ecs_entity_t oneof = flecs_get_oneof(world, rel); + if (oneof) { + if (!ecs_has_pair(world, tgt, EcsChildOf, oneof)) { + char *idstr = ecs_id_str(world, id); + char *tgtstr = ecs_get_path(world, tgt); + char *oneofstr = ecs_get_path(world, oneof); + ecs_err("OneOf constraint violated: " + "%s: '%s' is not a child of '%s'", + idstr, tgtstr, oneofstr); + ecs_os_free(oneofstr); + ecs_os_free(tgtstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } + } + + /* Check if we're not trying to inherit from a final target */ + if (rel == EcsIsA) { + if (ecs_has_id(world, tgt, EcsFinal)) { + char *idstr = ecs_id_str(world, id); + char *tgtstr = ecs_get_path(world, tgt); + ecs_err("Final constraint violated: " + "%s: cannot inherit from final entity '%s'", + idstr, tgtstr); + ecs_os_free(tgtstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } + } + } +#endif + + if (!is_wildcard && (rel != EcsFlag)) { + /* Inherit flags from (relationship, *) record */ + ecs_id_record_t *idr_r = flecs_id_record_ensure( + world, ecs_pair(rel, EcsWildcard)); + idr->parent = idr_r; + idr->flags = idr_r->flags; + + /* If pair is not a wildcard, append it to wildcard lists. These + * allow for quickly enumerating all relationships for an object, + * or all objects for a relationship. */ + flecs_insert_id_elem(world, idr, ecs_pair(rel, EcsWildcard), idr_r); + + idr_t = flecs_id_record_ensure(world, ecs_pair(EcsWildcard, tgt)); + flecs_insert_id_elem(world, idr, ecs_pair(EcsWildcard, tgt), idr_t); + } + } else { + rel = id & ECS_COMPONENT_MASK; + ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL); + + /* Can't use relationship outside of a pair */ +#ifdef FLECS_DEBUG + rel = flecs_entities_get_alive(world, rel); + bool is_tgt = false; + if (ecs_has_id(world, rel, EcsRelationship) || + (is_tgt = ecs_has_id(world, rel, EcsTarget))) + { + char *idstr = ecs_id_str(world, id); + char *relstr = ecs_id_str(world, rel); + ecs_err("constraint violated: " + "%s: relationship%s '%s' cannot be used as component", + idstr, is_tgt ? " target" : "", relstr); + ecs_os_free(relstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } +#endif + } + + /* Initialize type info if id is not a tag */ + if (!is_wildcard && (!role || is_pair)) { + if (!(idr->flags & EcsIdTag)) { + const ecs_type_info_t *ti = flecs_type_info_get(world, rel); + if (!ti && tgt) { + ti = flecs_type_info_get(world, tgt); + } + idr->type_info = ti; + } + } + + /* Mark entities that are used as component/pair ids. When a tracked + * entity is deleted, cleanup policies are applied so that the store + * won't contain any tables with deleted ids. */ + + /* Flag for OnDelete policies */ + flecs_add_flag(world, rel, EcsEntityIsId); + if (tgt) { + /* Flag for OnDeleteTarget policies */ + ecs_record_t *tgt_r = flecs_entities_get_any(world, tgt); + ecs_assert(tgt_r != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_record_add_flag(tgt_r, EcsEntityIsTarget); + if (idr->flags & EcsIdTraversable) { + /* Flag used to determine if object should be traversed when + * propagating events or with super/subset queries */ + flecs_record_add_flag(tgt_r, EcsEntityIsTraversable); + + /* Add reference to (*, tgt) id record to entity record */ + tgt_r->idr = idr_t; + } + } + + idr->flags |= flecs_id_record_event_flags(world, id); + + if (idr->flags & EcsIdIsSparse) { + flecs_id_record_init_sparse(world, idr); + } else if (idr->flags & EcsIdIsUnion) { + if (ECS_IS_PAIR(id) && ECS_PAIR_SECOND(id) == EcsUnion) { + flecs_id_record_init_sparse(world, idr); + } + } + + if (ecs_should_log_1()) { + char *id_str = ecs_id_str(world, id); + ecs_dbg_1("#[green]id#[normal] %s #[green]created", id_str); + ecs_os_free(id_str); + } + + /* Update counters */ + world->info.id_create_total ++; + world->info.component_id_count += idr->type_info != NULL; + world->info.tag_id_count += idr->type_info == NULL; + world->info.pair_id_count += is_pair; + + return idr; +} + +static +void flecs_id_record_assert_empty( + ecs_id_record_t *idr) +{ + (void)idr; + ecs_assert(flecs_table_cache_count(&idr->cache) == 0, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_table_cache_empty_count(&idr->cache) == 0, + ECS_INTERNAL_ERROR, NULL); +} + +static +void flecs_id_record_free( + ecs_world_t *world, + ecs_id_record_t *idr) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_id_t id = idr->id; + + flecs_id_record_assert_empty(idr); + + /* Id is still in use by a query */ + ecs_assert((world->flags & EcsWorldQuit) || (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(id); + ecs_entity_t tgt = ECS_PAIR_SECOND(id); + if (!ecs_id_is_wildcard(id)) { + if (ECS_PAIR_FIRST(id) != EcsFlag) { + /* If id is not a wildcard, remove it from the wildcard lists */ + flecs_remove_id_elem(idr, ecs_pair(rel, EcsWildcard)); + flecs_remove_id_elem(idr, ecs_pair(EcsWildcard, tgt)); + } + } else { + ecs_log_push_2(); + + /* If id is a wildcard, it means that all id records that match the + * wildcard are also empty, so release them */ + if (ECS_PAIR_FIRST(id) == EcsWildcard) { + /* Iterate (*, Target) list */ + ecs_id_record_t *cur, *next = idr->second.next; + while ((cur = next)) { + flecs_id_record_assert_empty(cur); + next = cur->second.next; + flecs_id_record_release(world, cur); + } + } else { + /* Iterate (Relationship, *) list */ + ecs_assert(ECS_PAIR_SECOND(id) == EcsWildcard, + ECS_INTERNAL_ERROR, NULL); + ecs_id_record_t *cur, *next = idr->first.next; + while ((cur = next)) { + flecs_id_record_assert_empty(cur); + next = cur->first.next; + flecs_id_record_release(world, cur); + } + } + + ecs_log_pop_2(); + } + } + + /* Cleanup sparse storage */ + flecs_id_record_fini_sparse(world, idr); + + /* Update counters */ + world->info.id_delete_total ++; + world->info.pair_id_count -= ECS_IS_PAIR(id); + world->info.component_id_count -= idr->type_info != NULL; + world->info.tag_id_count -= idr->type_info == NULL; + + /* Unregister the id record from the world & free resources */ + ecs_table_cache_fini(&idr->cache); + flecs_name_index_free(idr->name_index); + ecs_vec_fini_t(&world->allocator, &idr->reachable.ids, ecs_reachable_elem_t); + + ecs_id_t hash = flecs_id_record_hash(id); + if (hash >= FLECS_HI_ID_RECORD_ID) { + ecs_map_remove(&world->id_index_hi, hash); + flecs_bfree(&world->allocators.id_record, idr); + } else { + idr->id = 0; /* Tombstone */ + } + + if (ecs_should_log_1()) { + char *id_str = ecs_id_str(world, id); + ecs_dbg_1("#[green]id#[normal] %s #[red]deleted", id_str); + ecs_os_free(id_str); + } +} + +ecs_id_record_t* flecs_id_record_ensure( + ecs_world_t *world, + ecs_id_t id) +{ + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + idr = flecs_id_record_new(world, id); + } + return idr; +} + +ecs_id_record_t* flecs_id_record_get( + const ecs_world_t *world, + ecs_id_t id) +{ + flecs_poly_assert(world, ecs_world_t); + if (id == ecs_pair(EcsIsA, EcsWildcard)) { + return world->idr_isa_wildcard; + } else if (id == ecs_pair(EcsChildOf, EcsWildcard)) { + return world->idr_childof_wildcard; + } else if (id == ecs_pair_t(EcsIdentifier, EcsName)) { + return world->idr_identifier_name; + } + + ecs_id_t hash = flecs_id_record_hash(id); + ecs_id_record_t *idr = NULL; + if (hash >= FLECS_HI_ID_RECORD_ID) { + idr = ecs_map_get_deref(&world->id_index_hi, ecs_id_record_t, hash); + } else { + idr = &world->id_index_lo[hash]; + if (!idr->id) { + idr = NULL; + } + } + + return idr; +} + +void flecs_id_record_claim( + ecs_world_t *world, + ecs_id_record_t *idr) +{ + (void)world; + idr->refcount ++; +} + +int32_t flecs_id_record_release( + ecs_world_t *world, + ecs_id_record_t *idr) +{ + int32_t rc = -- idr->refcount; + ecs_assert(rc >= 0, ECS_INTERNAL_ERROR, NULL); + + if (!rc) { + flecs_id_record_free(world, idr); + } + + return rc; +} + +void flecs_id_record_release_tables( + ecs_world_t *world, + ecs_id_record_t *idr) +{ + ecs_table_cache_iter_t it; + if (flecs_table_cache_all_iter(&idr->cache, &it)) { + ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + /* Release current table */ + flecs_table_fini(world, tr->hdr.table); + } + } +} + +bool flecs_id_record_set_type_info( + ecs_world_t *world, + ecs_id_record_t *idr, + const ecs_type_info_t *ti) +{ + bool is_wildcard = ecs_id_is_wildcard(idr->id); + if (!is_wildcard) { + if (ti) { + if (!idr->type_info) { + world->info.tag_id_count --; + world->info.component_id_count ++; + } + } else { + if (idr->type_info) { + world->info.tag_id_count ++; + world->info.component_id_count --; + } + } + } + + bool changed = idr->type_info != ti; + idr->type_info = ti; + + return changed; +} + +ecs_hashmap_t* flecs_id_record_name_index_ensure( + ecs_world_t *world, + ecs_id_record_t *idr) +{ + ecs_hashmap_t *map = idr->name_index; + if (!map) { + map = idr->name_index = flecs_name_index_new(world, &world->allocator); + } + + return map; +} + +ecs_hashmap_t* flecs_id_name_index_ensure( + ecs_world_t *world, + ecs_id_t id) +{ + flecs_poly_assert(world, ecs_world_t); + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + + return flecs_id_record_name_index_ensure(world, idr); +} + +ecs_hashmap_t* flecs_id_name_index_get( + const ecs_world_t *world, + ecs_id_t id) +{ + flecs_poly_assert(world, ecs_world_t); + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return NULL; + } + + return idr->name_index; +} + +ecs_table_record_t* flecs_table_record_get( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id) +{ + flecs_poly_assert(world, ecs_world_t); + + ecs_id_record_t* idr = flecs_id_record_get(world, id); + if (!idr) { + return NULL; + } + + return (ecs_table_record_t*)ecs_table_cache_get(&idr->cache, table); +} + +ecs_table_record_t* flecs_id_record_get_table( + const ecs_id_record_t *idr, + const ecs_table_t *table) +{ + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + return (ecs_table_record_t*)ecs_table_cache_get(&idr->cache, table); +} + +void flecs_init_id_records( + ecs_world_t *world) +{ + /* Cache often used id records on world */ + world->idr_wildcard = flecs_id_record_ensure(world, EcsWildcard); + world->idr_wildcard_wildcard = flecs_id_record_ensure(world, + ecs_pair(EcsWildcard, EcsWildcard)); + world->idr_any = flecs_id_record_ensure(world, EcsAny); + world->idr_isa_wildcard = flecs_id_record_ensure(world, + ecs_pair(EcsIsA, EcsWildcard)); +} + +void flecs_fini_id_records( + ecs_world_t *world) +{ + /* Loop & delete first element until there are no elements left. Id records + * can recursively delete each other, this ensures we always have a + * valid iterator. */ + while (ecs_map_count(&world->id_index_hi) > 0) { + ecs_map_iter_t it = ecs_map_iter(&world->id_index_hi); + ecs_map_next(&it); + flecs_id_record_release(world, ecs_map_ptr(&it)); + } + + int32_t i; + for (i = 0; i < FLECS_HI_ID_RECORD_ID; i ++) { + ecs_id_record_t *idr = &world->id_index_lo[i]; + if (idr->id) { + flecs_id_record_release(world, idr); + } + } + + ecs_assert(ecs_map_count(&world->id_index_hi) == 0, + ECS_INTERNAL_ERROR, NULL); + + ecs_map_fini(&world->id_index_hi); + ecs_os_free(world->id_index_lo); +} + +static +ecs_flags32_t flecs_id_flags( + ecs_world_t *world, + ecs_id_t id) +{ + const ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + ecs_flags32_t extra_flags = 0; + if (idr->flags & EcsIdOnInstantiateInherit) { + extra_flags |= EcsIdHasOnAdd|EcsIdHasOnRemove; + } + return idr->flags|extra_flags; + } + return flecs_id_record_event_flags(world, id); +} + +ecs_flags32_t flecs_id_flags_get( + ecs_world_t *world, + ecs_id_t id) +{ + ecs_flags32_t result = flecs_id_flags(world, id); + + if (id != EcsAny) { + result |= flecs_id_flags(world, EcsAny); + } + + if (ECS_IS_PAIR(id)) { + ecs_entity_t first = ECS_PAIR_FIRST(id); + ecs_entity_t second = ECS_PAIR_SECOND(id); + + if (id != ecs_pair(first, EcsWildcard)) { + result |= flecs_id_flags(world, ecs_pair(first, EcsWildcard)); + } + if (id != ecs_pair(EcsWildcard, second)) { + result |= flecs_id_flags(world, ecs_pair(EcsWildcard, second)); + } + if (id != ecs_pair(EcsWildcard, EcsWildcard)) { + result |= flecs_id_flags(world, ecs_pair(EcsWildcard, EcsWildcard)); + } + + if (first == EcsIsA) { + result |= EcsIdHasOnAdd|EcsIdHasOnRemove; + } + } else if (id != EcsWildcard) { + result |= flecs_id_flags(world, EcsWildcard); + } + + return result; +} + +/** + * @file storage/table.c + * @brief Table storage implementation. + * + * Tables are the data structure that store the component data. Tables have + * columns for each component in the table, and rows for each entity stored in + * the table. Once created, the component list for a table doesn't change, but + * entities can move from one table to another. + * + * Each table has a type, which is a vector with the (component) ids in the + * table. The vector is sorted by id, which ensures that there can be only one + * table for each unique combination of components. + * + * Not all ids in a table have to be components. Tags are ids that have no + * data type associated with them, and as a result don't need to be explicitly + * stored beyond an element in the table type. To save space and speed up table + * creation, each table has a reference to a "storage table", which is a table + * that only includes component ids (so excluding tags). + * + * Note that the actual data is not stored on the storage table. The storage + * table is only used for sharing administration. A column_map member maps + * between column indices of the table and its storage table. Tables are + * refcounted, which ensures that storage tables won't be deleted if other + * tables have references to it. + */ + + +/* Table sanity check to detect storage issues. Only enabled in SANITIZE mode as + * this can severely slow down many ECS operations. */ +#ifdef FLECS_SANITIZE +static +void flecs_table_check_sanity( + ecs_world_t *world, + ecs_table_t *table) +{ + int32_t i, count = ecs_table_count(table); + int32_t size = ecs_table_size(table); + ecs_assert(count <= size, ECS_INTERNAL_ERROR, NULL); + + int32_t bs_offset = table->_ ? table->_->bs_offset : 0; + int32_t bs_count = table->_ ? table->_->bs_count : 0; + int32_t type_count = table->type.count; + ecs_id_t *ids = table->type.array; + + ecs_assert((bs_count + bs_offset) <= type_count, ECS_INTERNAL_ERROR, NULL); + + if (size) { + ecs_assert(table->data.entities != NULL, ECS_INTERNAL_ERROR, NULL); + } else { + ecs_assert(table->data.entities == NULL, ECS_INTERNAL_ERROR, NULL); + } + + if (table->column_count) { + int32_t column_count = table->column_count; + ecs_assert(type_count >= column_count, ECS_INTERNAL_ERROR, NULL); + + int16_t *column_map = table->column_map; + ecs_assert(column_map != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(table->data.columns != NULL, ECS_INTERNAL_ERROR, NULL); + + for (i = 0; i < column_count; i ++) { + int32_t column_map_id = column_map[i + type_count]; + ecs_assert(column_map_id >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(table->data.columns[i].ti != NULL, + ECS_INTERNAL_ERROR, NULL); + if (size) { + ecs_assert(table->data.columns[i].data != NULL, + ECS_INTERNAL_ERROR, NULL); + } else { + ecs_assert(table->data.columns[i].data == NULL, + ECS_INTERNAL_ERROR, NULL); + } + } + } else { + ecs_assert(table->column_map == NULL, ECS_INTERNAL_ERROR, NULL); + } + + if (bs_count) { + ecs_assert(table->_->bs_columns != NULL, ECS_INTERNAL_ERROR, NULL); + for (i = 0; i < bs_count; i ++) { + ecs_bitset_t *bs = &table->_->bs_columns[i]; + ecs_assert(flecs_bitset_count(bs) == count, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(ECS_HAS_ID_FLAG(ids[i + bs_offset], TOGGLE), + ECS_INTERNAL_ERROR, NULL); + } + } + + ecs_assert((table->_->traversable_count == 0) || + (table->flags & EcsTableHasTraversable), ECS_INTERNAL_ERROR, NULL); +} +#else +#define flecs_table_check_sanity(world, table) +#endif + +/* Set flags for type hooks so table operations can quickly check whether a + * fast or complex operation that invokes hooks is required. */ +static +ecs_flags32_t flecs_type_info_flags( + const ecs_type_info_t *ti) +{ + ecs_flags32_t flags = 0; + + if (ti->hooks.ctor) { + flags |= EcsTableHasCtors; + } + if (ti->hooks.on_add) { + flags |= EcsTableHasCtors; + } + if (ti->hooks.dtor) { + flags |= EcsTableHasDtors; + } + if (ti->hooks.on_remove) { + flags |= EcsTableHasDtors; + } + if (ti->hooks.copy) { + flags |= EcsTableHasCopy; + } + if (ti->hooks.move) { + flags |= EcsTableHasMove; + } + + return flags; +} + +static +void flecs_table_init_columns( + ecs_world_t *world, + ecs_table_t *table, + int32_t column_count) +{ + int16_t i, cur = 0, ids_count = flecs_ito(int16_t, table->type.count); + + for (i = 0; i < ids_count; i ++) { + ecs_id_t id = table->type.array[i]; + if (id < FLECS_HI_COMPONENT_ID) { + table->component_map[id] = flecs_ito(int16_t, -(i + 1)); + } + } + + if (!column_count) { + return; + } + + ecs_column_t *columns = flecs_wcalloc_n(world, ecs_column_t, column_count); + table->data.columns = columns; + + ecs_id_t *ids = table->type.array; + ecs_table_record_t *records = table->_->records; + int16_t *t2s = table->column_map; + int16_t *s2t = &table->column_map[ids_count]; + + for (i = 0; i < ids_count; i ++) { + ecs_id_t id = ids[i]; + ecs_table_record_t *tr = &records[i]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + const ecs_type_info_t *ti = idr->type_info; + + if (!ti || (idr->flags & EcsIdIsSparse)) { + t2s[i] = -1; + continue; + } + + t2s[i] = cur; + s2t[cur] = i; + tr->column = flecs_ito(int16_t, cur); + + columns[cur].ti = ECS_CONST_CAST(ecs_type_info_t*, ti); + + if (id < FLECS_HI_COMPONENT_ID) { + table->component_map[id] = flecs_ito(int16_t, cur + 1); + } + + table->flags |= flecs_type_info_flags(ti); + cur ++; + } + + int32_t record_count = table->_->record_count; + for (; i < record_count; i ++) { + ecs_table_record_t *tr = &records[i]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_id_t id = idr->id; + + if (ecs_id_is_wildcard(id)) { + ecs_table_record_t *first_tr = &records[tr->index]; + tr->column = first_tr->column; + } + } +} + +/* Initialize table storage */ +void flecs_table_init_data( + ecs_world_t *world, + ecs_table_t *table) +{ + flecs_table_init_columns(world, table, table->column_count); + + ecs_table__t *meta = table->_; + int32_t i, bs_count = meta->bs_count; + + if (bs_count) { + meta->bs_columns = flecs_wcalloc_n(world, ecs_bitset_t, bs_count); + for (i = 0; i < bs_count; i ++) { + flecs_bitset_init(&meta->bs_columns[i]); + } + } +} + +/* Initialize table flags. Table flags are used in lots of scenarios to quickly + * check the features of a table without having to inspect the table type. Table + * flags are typically used to early-out of potentially expensive operations. */ +static +void flecs_table_init_flags( + ecs_world_t *world, + ecs_table_t *table) +{ + ecs_id_t *ids = table->type.array; + int32_t count = table->type.count; + + int32_t i; + for (i = 0; i < count; i ++) { + ecs_id_t id = ids[i]; + + if (id <= EcsLastInternalComponentId) { + table->flags |= EcsTableHasBuiltins; + } + + if (id == EcsModule) { + table->flags |= EcsTableHasBuiltins; + table->flags |= EcsTableHasModule; + } else if (id == EcsPrefab) { + table->flags |= EcsTableIsPrefab; + } else if (id == EcsDisabled) { + table->flags |= EcsTableIsDisabled; + } else if (id == EcsNotQueryable) { + table->flags |= EcsTableNotQueryable; + } else { + if (ECS_IS_PAIR(id)) { + ecs_entity_t r = ECS_PAIR_FIRST(id); + + table->flags |= EcsTableHasPairs; + + if (r == EcsIsA) { + table->flags |= EcsTableHasIsA; + } else if (r == EcsChildOf) { + table->flags |= EcsTableHasChildOf; + ecs_entity_t obj = ecs_pair_second(world, id); + ecs_assert(obj != 0, ECS_INTERNAL_ERROR, NULL); + + if (obj == EcsFlecs || obj == EcsFlecsCore || + ecs_has_id(world, obj, EcsModule)) + { + /* If table contains entities that are inside one of the + * builtin modules, it contains builtin entities */ + table->flags |= EcsTableHasBuiltins; + table->flags |= EcsTableHasModule; + } + } else if (id == ecs_pair_t(EcsIdentifier, EcsName)) { + table->flags |= EcsTableHasName; + } else if (r == ecs_id(EcsPoly)) { + table->flags |= EcsTableHasBuiltins; + } + } else { + if (ECS_HAS_ID_FLAG(id, TOGGLE)) { + ecs_table__t *meta = table->_; + table->flags |= EcsTableHasToggle; + + if (!meta->bs_count) { + meta->bs_offset = flecs_ito(int16_t, i); + } + meta->bs_count ++; + } + if (ECS_HAS_ID_FLAG(id, AUTO_OVERRIDE)) { + table->flags |= EcsTableHasOverrides; + } + } + } + } +} + +/* Utility function that appends an element to the table record array */ +static +void flecs_table_append_to_records( + ecs_world_t *world, + ecs_table_t *table, + ecs_vec_t *records, + ecs_id_t id, + int32_t column) +{ + /* To avoid a quadratic search, use the O(1) lookup that the index + * already provides. */ + ecs_id_record_t *idr = flecs_id_record_ensure(world, id); + ecs_table_record_t *tr = (ecs_table_record_t*)flecs_id_record_get_table( + idr, table); + if (!tr) { + tr = ecs_vec_append_t(&world->allocator, records, ecs_table_record_t); + tr->index = flecs_ito(int16_t, column); + tr->count = 1; + + ecs_table_cache_insert(&idr->cache, table, &tr->hdr); + } else { + tr->count ++; + } + + ecs_assert(tr->hdr.cache != NULL, ECS_INTERNAL_ERROR, NULL); +} + +void flecs_table_emit( + ecs_world_t *world, + ecs_table_t *table, + ecs_entity_t event) +{ + flecs_emit(world, world, 0, &(ecs_event_desc_t) { + .ids = &table->type, + .event = event, + .table = table, + .flags = EcsEventTableOnly, + .observable = world + }); +} + +/* Main table initialization function */ +void flecs_table_init( + ecs_world_t *world, + ecs_table_t *table, + ecs_table_t *from) +{ + /* Make sure table->flags is initialized */ + flecs_table_init_flags(world, table); + + /* The following code walks the table type to discover which id records the + * table needs to register table records with. + * + * In addition to registering itself with id records for each id in the + * table type, a table also registers itself with wildcard id records. For + * example, if a table contains (Eats, Apples), it will register itself with + * wildcard id records (Eats, *), (*, Apples) and (*, *). This makes it + * easier for wildcard queries to find the relevant tables. */ + + int32_t dst_i = 0, dst_count = table->type.count; + int32_t src_i = 0, src_count = 0; + ecs_id_t *dst_ids = table->type.array; + ecs_id_t *src_ids = NULL; + ecs_table_record_t *tr = NULL, *src_tr = NULL; + if (from) { + src_count = from->type.count; + src_ids = from->type.array; + src_tr = from->_->records; + } + + /* We don't know in advance how large the records array will be, so use + * cached vector. This eliminates unnecessary allocations, and/or expensive + * iterations to determine how many records we need. */ + ecs_allocator_t *a = &world->allocator; + ecs_vec_t *records = &world->store.records; + ecs_vec_reset_t(a, records, ecs_table_record_t); + ecs_id_record_t *idr, *childof_idr = NULL; + + int32_t last_id = -1; /* Track last regular (non-pair) id */ + int32_t first_pair = -1; /* Track the first pair in the table */ + int32_t first_role = -1; /* Track first id with role */ + + /* Scan to find boundaries of regular ids, pairs and roles */ + for (dst_i = 0; dst_i < dst_count; dst_i ++) { + ecs_id_t dst_id = dst_ids[dst_i]; + if (first_pair == -1 && ECS_IS_PAIR(dst_id)) { + first_pair = dst_i; + } + if ((dst_id & ECS_COMPONENT_MASK) == dst_id) { + last_id = dst_i; + } else if (first_role == -1 && !ECS_IS_PAIR(dst_id)) { + first_role = dst_i; + } + } + + /* The easy part: initialize a record for every id in the type */ + for (dst_i = 0; (dst_i < dst_count) && (src_i < src_count); ) { + ecs_id_t dst_id = dst_ids[dst_i]; + ecs_id_t src_id = src_ids[src_i]; + + idr = NULL; + + if (dst_id == src_id) { + ecs_assert(src_tr != NULL, ECS_INTERNAL_ERROR, NULL); + idr = (ecs_id_record_t*)src_tr[src_i].hdr.cache; + } else if (dst_id < src_id) { + idr = flecs_id_record_ensure(world, dst_id); + } + if (idr) { + tr = ecs_vec_append_t(a, records, ecs_table_record_t); + tr->hdr.cache = (ecs_table_cache_t*)idr; + tr->index = flecs_ito(int16_t, dst_i); + tr->count = 1; + } + + dst_i += dst_id <= src_id; + src_i += dst_id >= src_id; + } + + /* Add remaining ids that the "from" table didn't have */ + for (; (dst_i < dst_count); dst_i ++) { + ecs_id_t dst_id = dst_ids[dst_i]; + tr = ecs_vec_append_t(a, records, ecs_table_record_t); + idr = flecs_id_record_ensure(world, dst_id); + tr->hdr.cache = (ecs_table_cache_t*)idr; + ecs_assert(tr->hdr.cache != NULL, ECS_INTERNAL_ERROR, NULL); + tr->index = flecs_ito(int16_t, dst_i); + tr->count = 1; + } + + /* We're going to insert records from the vector into the index that + * will get patched up later. To ensure the record pointers don't get + * invalidated we need to grow the vector so that it won't realloc as + * we're adding the next set of records */ + if (first_role != -1 || first_pair != -1) { + int32_t start = first_role; + if (first_pair != -1 && (start != -1 || first_pair < start)) { + start = first_pair; + } + + /* Total number of records can never be higher than + * - number of regular (non-pair) ids + + * - three records for pairs: (R,T), (R,*), (*,T) + * - one wildcard (*), one any (_) and one pair wildcard (*,*) record + * - one record for (ChildOf, 0) + */ + int32_t flag_id_count = dst_count - start; + int32_t record_count = start + 3 * flag_id_count + 3 + 1; + ecs_vec_set_min_size_t(a, records, ecs_table_record_t, record_count); + } + + /* Add records for ids with roles (used by cleanup logic) */ + if (first_role != -1) { + for (dst_i = first_role; dst_i < dst_count; dst_i ++) { + ecs_id_t id = dst_ids[dst_i]; + if (!ECS_IS_PAIR(id)) { + ecs_entity_t first = 0; + ecs_entity_t second = 0; + if (ECS_HAS_ID_FLAG(id, PAIR)) { + first = ECS_PAIR_FIRST(id); + second = ECS_PAIR_SECOND(id); + } else { + first = id & ECS_COMPONENT_MASK; + } + if (first) { + flecs_table_append_to_records(world, table, records, + ecs_pair(EcsFlag, first), dst_i); + } + if (second) { + flecs_table_append_to_records(world, table, records, + ecs_pair(EcsFlag, second), dst_i); + } + } + } + } + + int32_t last_pair = -1; + bool has_childof = table->flags & EcsTableHasChildOf; + if (first_pair != -1) { + /* Add a (Relationship, *) record for each relationship. */ + ecs_entity_t r = 0; + for (dst_i = first_pair; dst_i < dst_count; dst_i ++) { + ecs_id_t dst_id = dst_ids[dst_i]; + if (!ECS_IS_PAIR(dst_id)) { + break; /* no more pairs */ + } + if (r != ECS_PAIR_FIRST(dst_id)) { /* New relationship, new record */ + tr = ecs_vec_get_t(records, ecs_table_record_t, dst_i); + + ecs_id_record_t *p_idr = (ecs_id_record_t*)tr->hdr.cache; + r = ECS_PAIR_FIRST(dst_id); + if (r == EcsChildOf) { + childof_idr = p_idr; + } + + idr = p_idr->parent; /* (R, *) */ + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + + tr = ecs_vec_append_t(a, records, ecs_table_record_t); + tr->hdr.cache = (ecs_table_cache_t*)idr; + tr->index = flecs_ito(int16_t, dst_i); + tr->count = 0; + } + + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + tr->count ++; + } + + last_pair = dst_i; + + /* Add a (*, Target) record for each relationship target. Type + * ids are sorted relationship-first, so we can't simply do a single + * linear scan to find all occurrences for a target. */ + for (dst_i = first_pair; dst_i < last_pair; dst_i ++) { + ecs_id_t dst_id = dst_ids[dst_i]; + ecs_id_t tgt_id = ecs_pair(EcsWildcard, ECS_PAIR_SECOND(dst_id)); + + flecs_table_append_to_records( + world, table, records, tgt_id, dst_i); + } + } + + /* Lastly, add records for all-wildcard ids */ + if (last_id >= 0) { + tr = ecs_vec_append_t(a, records, ecs_table_record_t); + tr->hdr.cache = (ecs_table_cache_t*)world->idr_wildcard; + tr->index = 0; + tr->count = flecs_ito(int16_t, last_id + 1); + } + if (last_pair - first_pair) { + tr = ecs_vec_append_t(a, records, ecs_table_record_t); + tr->hdr.cache = (ecs_table_cache_t*)world->idr_wildcard_wildcard; + tr->index = flecs_ito(int16_t, first_pair); + tr->count = flecs_ito(int16_t, last_pair - first_pair); + } + if (dst_count) { + tr = ecs_vec_append_t(a, records, ecs_table_record_t); + tr->hdr.cache = (ecs_table_cache_t*)world->idr_any; + tr->index = 0; + tr->count = 1; + } + if (dst_count && !has_childof) { + tr = ecs_vec_append_t(a, records, ecs_table_record_t); + childof_idr = world->idr_childof_0; + tr->hdr.cache = (ecs_table_cache_t*)childof_idr; + tr->index = 0; + tr->count = 1; + } + + /* Now that all records have been added, copy them to array */ + int32_t i, dst_record_count = ecs_vec_count(records); + ecs_table_record_t *dst_tr = flecs_wdup_n(world, ecs_table_record_t, + dst_record_count, ecs_vec_first_t(records, ecs_table_record_t)); + table->_->record_count = flecs_ito(int16_t, dst_record_count); + table->_->records = dst_tr; + int32_t column_count = 0; + + /* Register & patch up records */ + for (i = 0; i < dst_record_count; i ++) { + tr = &dst_tr[i]; + idr = (ecs_id_record_t*)dst_tr[i].hdr.cache; + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + + if (ecs_table_cache_get(&idr->cache, table)) { + /* If this is a target wildcard record it has already been + * registered, but the record is now at a different location in + * memory. Patch up the linked list with the new address */ + ecs_table_cache_replace(&idr->cache, table, &tr->hdr); + } else { + /* Other records are not registered yet */ + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_cache_insert(&idr->cache, table, &tr->hdr); + } + + /* Claim id record so it stays alive as long as the table exists */ + flecs_id_record_claim(world, idr); + + /* Initialize event flags */ + table->flags |= idr->flags & EcsIdEventMask; + + /* Initialize column index (will be overwritten by init_columns) */ + tr->column = -1; + + if (ECS_ID_ON_INSTANTIATE(idr->flags) == EcsOverride) { + table->flags |= EcsTableHasOverrides; + } + + if ((i < table->type.count) && (idr->type_info != NULL)) { + if (!(idr->flags & EcsIdIsSparse)) { + column_count ++; + } + } + } + + table->component_map = flecs_wcalloc_n( + world, int16_t, FLECS_HI_COMPONENT_ID); + + if (column_count) { + table->column_map = flecs_walloc_n(world, int16_t, + dst_count + column_count); + } + + table->column_count = flecs_ito(int16_t, column_count); + flecs_table_init_data(world, table); + + if (table->flags & EcsTableHasName) { + ecs_assert(childof_idr != NULL, ECS_INTERNAL_ERROR, NULL); + table->_->name_index = + flecs_id_record_name_index_ensure(world, childof_idr); + ecs_assert(table->_->name_index != NULL, ECS_INTERNAL_ERROR, NULL); + } + + if (table->flags & EcsTableHasOnTableCreate) { + flecs_table_emit(world, table, EcsOnTableCreate); + } +} + +/* Unregister table from id records */ +static +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]; + ecs_table_cache_t *cache = tr->hdr.cache; + ecs_id_t id = ((ecs_id_record_t*)cache)->id; + + ecs_assert(tr->hdr.cache == cache, ECS_INTERNAL_ERROR, NULL); + ecs_assert(tr->hdr.table == table, ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_id_record_get(world, id) == (ecs_id_record_t*)cache, + ECS_INTERNAL_ERROR, NULL); + (void)id; + + ecs_table_cache_remove(cache, table_id, &tr->hdr); + flecs_id_record_release(world, (ecs_id_record_t*)cache); + } + + flecs_wfree_n(world, ecs_table_record_t, count, table->_->records); +} + +/* Keep track for what kind of builtin events observers are registered that can + * potentially match the table. This allows code to early out of calling the + * emit function that notifies observers. */ +static +void flecs_table_add_trigger_flags( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + ecs_entity_t event) +{ + (void)world; + + ecs_flags32_t flags = 0; + + if (event == EcsOnAdd) { + flags = EcsTableHasOnAdd; + } else if (event == EcsOnRemove) { + flags = EcsTableHasOnRemove; + } else if (event == EcsOnSet) { + flags = EcsTableHasOnSet; + } else if (event == EcsOnTableFill) { + flags = EcsTableHasOnTableFill; + } else if (event == EcsOnTableEmpty) { + flags = EcsTableHasOnTableEmpty; + } else if (event == EcsWildcard) { + flags = EcsTableHasOnAdd|EcsTableHasOnRemove|EcsTableHasOnSet| + EcsTableHasOnTableFill|EcsTableHasOnTableEmpty| + EcsTableHasOnTableCreate|EcsTableHasOnTableDelete; + } + + table->flags |= flags; + + /* Add observer flags to incoming edges for id */ + if (id && ((flags == EcsTableHasOnAdd) || (flags == EcsTableHasOnRemove))) { + flecs_table_edges_add_flags(world, table, id, flags); + } +} + +/* Invoke OnRemove observers for all entities in table. Useful during table + * deletion or when clearing entities from a table. */ +static +void flecs_table_notify_on_remove( + ecs_world_t *world, + ecs_table_t *table) +{ + int32_t count = ecs_table_count(table); + if (count) { + ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; + diff.removed = table->type; + diff.removed_flags = table->flags & EcsTableRemoveEdgeFlags; + flecs_notify_on_remove(world, table, NULL, 0, count, &diff); + } +} + +/* Invoke type hook for entities in table */ +static +void flecs_table_invoke_hook( + ecs_world_t *world, + ecs_table_t *table, + ecs_iter_action_t callback, + ecs_entity_t event, + ecs_column_t *column, + const ecs_entity_t *entities, + int32_t row, + int32_t count) +{ + int32_t column_index = flecs_ito(int32_t, column - table->data.columns); + ecs_assert(column_index >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column_index < table->column_count, ECS_INTERNAL_ERROR, NULL); + int32_t type_index = table->column_map[table->type.count + column_index]; + ecs_assert(type_index >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type_index < table->type.count, ECS_INTERNAL_ERROR, NULL); + const ecs_table_record_t *tr = &table->_->records[type_index]; + flecs_invoke_hook(world, table, tr, count, row, entities, + table->type.array[type_index], column->ti, event, callback); +} + +/* Construct components */ +static +void flecs_table_invoke_ctor( + ecs_column_t *column, + int32_t row, + int32_t count) +{ + ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column->data != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_type_info_t *ti = column->ti; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_xtor_t ctor = ti->hooks.ctor; + if (ctor) { + void *ptr = ECS_ELEM(column->data, ti->size, row); + ctor(ptr, count, ti); + } +} + +/* Destruct components */ +static +void flecs_table_invoke_dtor( + ecs_column_t *column, + int32_t row, + int32_t count) +{ + ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column->data != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_type_info_t *ti = column->ti; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_xtor_t dtor = ti->hooks.dtor; + if (dtor) { + void *ptr = ECS_ELEM(column->data, ti->size, row); + dtor(ptr, count, ti); + } +} + +/* Run hooks that get invoked when component is added to entity */ +static +void flecs_table_invoke_add_hooks( + ecs_world_t *world, + ecs_table_t *table, + ecs_column_t *column, + ecs_entity_t *entities, + int32_t row, + int32_t count, + bool construct) +{ + ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column->data != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_type_info_t *ti = column->ti; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + + if (construct) { + flecs_table_invoke_ctor(column, row, count); + } + + ecs_iter_action_t on_add = ti->hooks.on_add; + if (on_add) { + flecs_table_invoke_hook(world, table, on_add, EcsOnAdd, column, + entities, row, count); + } +} + +/* Run hooks that get invoked when component is removed from entity */ +static +void flecs_table_invoke_remove_hooks( + ecs_world_t *world, + ecs_table_t *table, + ecs_column_t *column, + ecs_entity_t *entities, + int32_t row, + int32_t count, + bool dtor) +{ + ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column->data != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_type_info_t *ti = column->ti; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_iter_action_t on_remove = ti->hooks.on_remove; + if (on_remove) { + flecs_table_invoke_hook(world, table, on_remove, EcsOnRemove, column, + entities, row, count); + } + + if (dtor) { + flecs_table_invoke_dtor(column, row, count); + } +} + +/* Destruct all components and/or delete all entities in table in range */ +static +void flecs_table_dtor_all( + ecs_world_t *world, + ecs_table_t *table, + int32_t row, + int32_t count, + bool is_delete) +{ + const ecs_entity_t *entities = ecs_table_entities(table); + int32_t column_count = table->column_count; + int32_t i, c, end = row + count; + + ecs_assert(!column_count || table->data.columns != NULL, + ECS_INTERNAL_ERROR, NULL); + + if (is_delete && table->_->traversable_count) { + /* If table contains monitored entities with traversable relationships, + * make sure to invalidate observer cache */ + flecs_emit_propagate_invalidate(world, table, row, count); + } + + /* If table has components with destructors, iterate component columns */ + if (table->flags & EcsTableHasDtors) { + /* Throw up a lock just to be sure */ + table->_->lock = true; + + /* Run on_remove callbacks first before destructing components */ + for (c = 0; c < column_count; c++) { + ecs_column_t *column = &table->data.columns[c]; + ecs_iter_action_t on_remove = column->ti->hooks.on_remove; + if (on_remove) { + flecs_table_invoke_hook(world, table, on_remove, EcsOnRemove, + column, &entities[row], row, count); + } + } + + /* Destruct components */ + for (c = 0; c < column_count; c++) { + flecs_table_invoke_dtor(&table->data.columns[c], row, count); + } + + /* Iterate entities first, then components. This ensures that only one + * entity is invalidated at a time, which ensures that destructors can + * safely access other entities. */ + for (i = row; i < end; i ++) { + /* Update entity index after invoking destructors so that entity can + * be safely used in destructor callbacks. */ + ecs_entity_t e = entities[i]; + ecs_assert(!e || ecs_is_valid(world, e), + ECS_INTERNAL_ERROR, NULL); + + if (is_delete) { + flecs_entities_remove(world, e); + ecs_assert(ecs_is_valid(world, e) == false, + ECS_INTERNAL_ERROR, NULL); + } else { + // If this is not a delete, clear the entity index record + ecs_record_t *record = flecs_entities_get(world, e); + record->table = NULL; + record->row = 0; + } + } + + table->_->lock = false; + + /* If table does not have destructors, just update entity index */ + } else { + if (is_delete) { + for (i = row; i < end; i ++) { + ecs_entity_t e = entities[i]; + ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); + flecs_entities_remove(world, e); + ecs_assert(!ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); + } + } else { + for (i = row; i < end; i ++) { + ecs_entity_t e = entities[i]; + ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); + ecs_record_t *record = flecs_entities_get(world, e); + record->table = NULL; + record->row = record->row & ECS_ROW_FLAGS_MASK; + (void)e; + } + } + } +} + +#define FLECS_LOCKED_STORAGE_MSG \ + "to fix, defer operations with defer_begin/defer_end" + +/* Cleanup table storage */ +static +void flecs_table_fini_data( + ecs_world_t *world, + ecs_table_t *table, + bool do_on_remove, + bool is_delete, + bool deactivate, + bool deallocate) +{ + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + + if (do_on_remove) { + flecs_table_notify_on_remove(world, table); + } + + int32_t count = ecs_table_count(table); + if (count) { + flecs_table_dtor_all(world, table, 0, count, is_delete); + } + + if (deallocate) { + ecs_column_t *columns = table->data.columns; + if (columns) { + int32_t c, column_count = table->column_count; + for (c = 0; c < column_count; c ++) { + ecs_column_t *column = &columns[c]; + ecs_vec_t v = ecs_vec_from_column(column, table, column->ti->size); + ecs_vec_fini(&world->allocator, &v, column->ti->size); + column->data = NULL; + } + + flecs_wfree_n(world, ecs_column_t, table->column_count, columns); + table->data.columns = NULL; + } + } + + ecs_table__t *meta = table->_; + ecs_bitset_t *bs_columns = meta->bs_columns; + if (bs_columns) { + int32_t c, column_count = meta->bs_count; + if (deallocate) { + for (c = 0; c < column_count; c ++) { + flecs_bitset_fini(&bs_columns[c]); + } + } + else { + for (c = 0; c < column_count; c++) { + bs_columns[c].count = 0; + } + } + + if (deallocate) { + flecs_wfree_n(world, ecs_bitset_t, column_count, bs_columns); + meta->bs_columns = NULL; + } + } + + if (deallocate) { + ecs_vec_t v = ecs_vec_from_entities(table); + ecs_vec_fini_t(&world->allocator, &v, ecs_entity_t); + table->data.entities = NULL; + table->data.size = 0; + } + + table->data.count = 0; + + if (deactivate && count) { + flecs_table_set_empty(world, table); + } + + table->_->traversable_count = 0; + table->flags &= ~EcsTableHasTraversable; +} + +const ecs_entity_t* ecs_table_entities( + const ecs_table_t *table) +{ + return table->data.entities; +} + +/* Cleanup, no OnRemove, delete from entity index, deactivate table, retain allocations */ +void ecs_table_clear_entities( + ecs_world_t* world, + ecs_table_t* table) +{ + flecs_table_fini_data(world, table, true, true, true, false); +} + +/* Cleanup, no OnRemove, clear entity index, deactivate table, free allocations */ +void flecs_table_clear_entities_silent( + ecs_world_t *world, + ecs_table_t *table) +{ + flecs_table_fini_data(world, table, false, false, true, true); +} + +/* Cleanup, run OnRemove, clear entity index, deactivate table, free allocations */ +void flecs_table_clear_entities( + ecs_world_t *world, + ecs_table_t *table) +{ + flecs_table_fini_data(world, table, true, false, true, true); +} + +/* Cleanup, run OnRemove, delete from entity index, deactivate table, free allocations */ +void flecs_table_delete_entities( + ecs_world_t *world, + ecs_table_t *table) +{ + flecs_table_fini_data(world, table, true, true, true, true); +} + +/* Unset all components in table. This function is called before a table is + * deleted, and invokes all OnRemove handlers, if any */ +void flecs_table_remove_actions( + ecs_world_t *world, + ecs_table_t *table) +{ + (void)world; + flecs_table_notify_on_remove(world, table); +} + +/* Free table resources. */ +void flecs_table_fini( + ecs_world_t *world, + ecs_table_t *table) +{ + bool is_root = table == &world->store.root; + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + ecs_assert(is_root || table->id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(is_root || flecs_sparse_is_alive(&world->store.tables, table->id), + ECS_INTERNAL_ERROR, NULL); + (void)world; + + ecs_os_perf_trace_push("flecs.table.free"); + + if (!is_root && !(world->flags & EcsWorldQuit)) { + if (table->flags & EcsTableHasOnTableDelete) { + flecs_table_emit(world, table, EcsOnTableDelete); + } + } + + if (ecs_should_log_2()) { + char *expr = ecs_type_str(world, &table->type); + ecs_dbg_2( + "#[green]table#[normal] [%s] #[red]deleted#[reset] with id %d", + expr, table->id); + ecs_os_free(expr); + ecs_log_push_2(); + } + + world->info.empty_table_count -= (ecs_table_count(table) == 0); + + /* Cleanup data, no OnRemove, delete from entity index, don't deactivate */ + flecs_table_fini_data(world, table, false, true, false, true); + flecs_table_clear_edges(world, table); + + if (!is_root) { + ecs_type_t ids = { + .array = table->type.array, + .count = table->type.count + }; + + flecs_hashmap_remove_w_hash( + &world->store.table_map, &ids, ecs_table_t*, table->_->hash); + } + + flecs_wfree_n(world, int32_t, table->column_count + 1, table->dirty_state); + flecs_wfree_n(world, int16_t, table->column_count + table->type.count, + table->column_map); + flecs_wfree_n(world, int16_t, FLECS_HI_COMPONENT_ID, table->component_map); + flecs_table_records_unregister(world, table); + + /* Update counters */ + world->info.table_count --; + world->info.table_delete_total ++; + + flecs_free_t(&world->allocator, ecs_table__t, table->_); + + if (!(world->flags & EcsWorldFini)) { + ecs_assert(!is_root, ECS_INTERNAL_ERROR, NULL); + flecs_table_free_type(world, table); + flecs_sparse_remove_t(&world->store.tables, ecs_table_t, table->id); + } + + ecs_log_pop_2(); + + ecs_os_perf_trace_pop("flecs.table.free"); +} + +/* Free table type. Do this separately from freeing the table as types can be + * in use by application destructors. */ +void flecs_table_free_type( + ecs_world_t *world, + ecs_table_t *table) +{ + flecs_wfree_n(world, ecs_id_t, table->type.count, table->type.array); +} + +/* Reset a table to its initial state. */ +void flecs_table_reset( + ecs_world_t *world, + ecs_table_t *table) +{ + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + flecs_table_clear_edges(world, table); +} + +/* Keep track of number of traversable entities in table. A traversable entity + * is an entity used as target in a pair with a traversable relationship. The + * traversable count and flag are used by code to early out of mechanisms like + * event propagation and recursive cleanup. */ +void flecs_table_traversable_add( + ecs_table_t *table, + int32_t value) +{ + int32_t result = table->_->traversable_count += value; + ecs_assert(result >= 0, ECS_INTERNAL_ERROR, NULL); + if (result == 0) { + table->flags &= ~EcsTableHasTraversable; + } else if (result == value) { + table->flags |= EcsTableHasTraversable; + } +} + +/* Mark table column dirty. This usually happens as the result of a set + * operation, or iteration of a query with [out] fields. */ +static +void flecs_table_mark_table_dirty( + ecs_world_t *world, + ecs_table_t *table, + int32_t index) +{ + (void)world; + if (table->dirty_state) { + table->dirty_state[index] ++; + } +} + +/* Mark table component dirty */ +void flecs_table_mark_dirty( + ecs_world_t *world, + ecs_table_t *table, + ecs_entity_t component) +{ + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + + if (table->dirty_state) { + int32_t column; + if (component < FLECS_HI_COMPONENT_ID) { + column = table->component_map[component]; + if (column <= 0) { + return; + } + } else { + ecs_id_record_t *idr = flecs_id_record_get(world, component); + if (!idr) { + return; + } + + const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr || tr->column == -1) { + return; + } + + column = tr->column + 1; + } + + /* Column is offset by 1, 0 is reserved for entity column. */ + + table->dirty_state[column] ++; + } +} + +/* Get (or create) dirty state of table. Used by queries for change tracking */ +int32_t* flecs_table_get_dirty_state( + ecs_world_t *world, + ecs_table_t *table) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + if (!table->dirty_state) { + int32_t column_count = table->column_count; + table->dirty_state = flecs_alloc_n(&world->allocator, + int32_t, column_count + 1); + ecs_assert(table->dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + for (int i = 0; i < column_count + 1; i ++) { + table->dirty_state[i] = 1; + } + } + return table->dirty_state; +} + +/* Table move logic for bitset (toggle component) column */ +static +void flecs_table_move_bitset_columns( + ecs_table_t *dst_table, + int32_t dst_index, + ecs_table_t *src_table, + int32_t src_index, + int32_t count, + bool clear) +{ + ecs_table__t *dst_meta = dst_table->_; + ecs_table__t *src_meta = src_table->_; + if (!dst_meta && !src_meta) { + return; + } + + int32_t i_old = 0, src_column_count = src_meta ? src_meta->bs_count : 0; + int32_t i_new = 0, dst_column_count = dst_meta ? dst_meta->bs_count : 0; + + if (!src_column_count && !dst_column_count) { + return; + } + + ecs_bitset_t *src_columns = src_meta ? src_meta->bs_columns : NULL; + ecs_bitset_t *dst_columns = dst_meta ? dst_meta->bs_columns : NULL; + + ecs_type_t dst_type = dst_table->type; + ecs_type_t src_type = src_table->type; + + int32_t offset_new = dst_meta ? dst_meta->bs_offset : 0; + int32_t offset_old = src_meta ? src_meta->bs_offset : 0; + + ecs_id_t *dst_ids = dst_type.array; + ecs_id_t *src_ids = src_type.array; + + for (; (i_new < dst_column_count) && (i_old < src_column_count);) { + ecs_id_t dst_id = dst_ids[i_new + offset_new]; + ecs_id_t src_id = src_ids[i_old + offset_old]; + + if (dst_id == src_id) { + ecs_bitset_t *src_bs = &src_columns[i_old]; + ecs_bitset_t *dst_bs = &dst_columns[i_new]; + + flecs_bitset_ensure(dst_bs, dst_index + count); + + int i; + for (i = 0; i < count; i ++) { + uint64_t value = flecs_bitset_get(src_bs, src_index + i); + flecs_bitset_set(dst_bs, dst_index + i, value); + } + + if (clear) { + ecs_assert(count == flecs_bitset_count(src_bs), + ECS_INTERNAL_ERROR, NULL); + flecs_bitset_fini(src_bs); + } + } else if (dst_id > src_id) { + ecs_bitset_t *src_bs = &src_columns[i_old]; + flecs_bitset_fini(src_bs); + } + + i_new += dst_id <= src_id; + i_old += dst_id >= src_id; + } + + /* Clear remaining columns */ + if (clear) { + for (; (i_old < src_column_count); i_old ++) { + ecs_bitset_t *src_bs = &src_columns[i_old]; + ecs_assert(count == flecs_bitset_count(src_bs), + ECS_INTERNAL_ERROR, NULL); + flecs_bitset_fini(src_bs); + } + } +} + +/* Grow table column. When a column needs to be reallocated this function takes + * care of correctly invoking ctor/move/dtor hooks. */ +static +void flecs_table_grow_column( + ecs_world_t *world, + ecs_vec_t *column, + const ecs_type_info_t *ti, + int32_t to_add, + int32_t dst_size, + bool construct) +{ + ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + + int32_t count = ecs_vec_count(column); + int32_t size = ecs_vec_size(column); + int32_t elem_size = ti->size; + int32_t dst_count = count + to_add; + bool can_realloc = dst_size != size; + void *result = NULL; + + ecs_assert(dst_size >= dst_count, ECS_INTERNAL_ERROR, NULL); + + /* If the array could possibly realloc and the component has a move action + * defined, move old elements manually */ + ecs_move_t move_ctor; + if (count && can_realloc && (move_ctor = ti->hooks.ctor_move_dtor)) { + ecs_xtor_t ctor = ti->hooks.ctor; + ecs_assert(ctor != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(move_ctor != NULL, ECS_INTERNAL_ERROR, NULL); + + /* Create vector */ + ecs_vec_t dst; + ecs_vec_init(&world->allocator, &dst, elem_size, dst_size); + dst.count = dst_count; + + void *src_buffer = column->array; + void *dst_buffer = dst.array; + + /* Move (and construct) existing elements to new vector */ + move_ctor(dst_buffer, src_buffer, count, ti); + + if (construct) { + /* Construct new element(s) */ + result = ECS_ELEM(dst_buffer, elem_size, count); + ctor(result, to_add, ti); + } + + /* Free old vector */ + ecs_vec_fini(&world->allocator, column, elem_size); + + *column = dst; + } else { + /* If array won't realloc or has no move, simply add new elements */ + if (can_realloc) { + ecs_vec_set_size(&world->allocator, column, elem_size, dst_size); + } + + result = ecs_vec_grow(&world->allocator, column, elem_size, to_add); + + ecs_xtor_t ctor; + if (construct && (ctor = ti->hooks.ctor)) { + /* If new elements need to be constructed and component has a + * constructor, construct */ + ctor(result, to_add, ti); + } + } + + ecs_assert(column->size == dst_size, ECS_INTERNAL_ERROR, NULL); +} + +/* Grow all data structures in a table */ +static +int32_t flecs_table_grow_data( + ecs_world_t *world, + ecs_table_t *table, + int32_t to_add, + int32_t size, + const ecs_entity_t *ids) +{ + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + + int32_t count = ecs_table_count(table); + int32_t column_count = table->column_count; + + /* Add entity to column with entity ids */ + ecs_vec_t v_entities = ecs_vec_from_entities(table); + ecs_vec_set_size_t(&world->allocator, &v_entities, ecs_entity_t, size); + + ecs_entity_t *e = ecs_vec_last_t(&v_entities, ecs_entity_t) + 1; + v_entities.count += to_add; + if (v_entities.size > size) { + size = v_entities.size; + } + + /* Update table entities/count/size */ + int32_t prev_count = table->data.count, prev_size = table->data.size; + table->data.entities = v_entities.array; + table->data.count = v_entities.count; + table->data.size = v_entities.size; + + /* Initialize entity ids and record ptrs */ + int32_t i; + if (ids) { + ecs_os_memcpy_n(e, ids, ecs_entity_t, to_add); + } else { + ecs_os_memset(e, 0, ECS_SIZEOF(ecs_entity_t) * to_add); + } + + /* Add elements to each column array */ + ecs_column_t *columns = table->data.columns; + for (i = 0; i < column_count; i ++) { + ecs_column_t *column = &columns[i]; + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v_column = ecs_vec_from_column_ext(column, prev_count, prev_size, ti->size); + flecs_table_grow_column(world, &v_column, ti, to_add, size, true); + ecs_assert(v_column.size == size, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v_column.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v_column.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); + column->data = v_column.array; + flecs_table_invoke_add_hooks(world, table, column, e, + count, to_add, false); + } + + ecs_table__t *meta = table->_; + int32_t bs_count = meta->bs_count; + ecs_bitset_t *bs_columns = meta->bs_columns; + + /* Add elements to each bitset column */ + for (i = 0; i < bs_count; i ++) { + ecs_bitset_t *bs = &bs_columns[i]; + flecs_bitset_addn(bs, to_add); + } + + /* If the table is monitored indicate that there has been a change */ + flecs_table_mark_table_dirty(world, table, 0); + + if (!(world->flags & EcsWorldReadonly) && !count) { + flecs_table_set_empty(world, table); + } + + /* Return index of first added entity */ + return count; +} + +/* Append operation for tables that don't have any complex logic */ +static +void flecs_table_fast_append( + ecs_world_t *world, + ecs_table_t *table) +{ + /* Add elements to each column array */ + ecs_column_t *columns = table->data.columns; + int32_t i, count = table->column_count; + for (i = 0; i < count; i ++) { + ecs_column_t *column = &columns[i]; + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v = ecs_vec_from_column(column, table, ti->size); + ecs_vec_append(&world->allocator, &v, ti->size); + column->data = v.array; + } +} + +/* Append entity to table */ +int32_t flecs_table_append( + ecs_world_t *world, + ecs_table_t *table, + ecs_entity_t entity, + bool construct, + bool on_add) +{ + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + + flecs_table_check_sanity(world, table); + + /* Get count & size before growing entities array. This tells us whether the + * arrays will realloc */ + int32_t count = ecs_table_count(table); + int32_t column_count = table->column_count; + ecs_column_t *columns = table->data.columns; + + /* Grow buffer with entity ids, set new element to new entity */ + ecs_vec_t v_entities = ecs_vec_from_entities(table); + ecs_entity_t *e = ecs_vec_append_t(&world->allocator, + &v_entities, ecs_entity_t); + + ecs_assert(e != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t *entities = table->data.entities = v_entities.array; + *e = entity; + + /* If the table is monitored indicate that there has been a change */ + flecs_table_mark_table_dirty(world, table, 0); + ecs_assert(count >= 0, ECS_INTERNAL_ERROR, NULL); + + /* Fast path: no switch columns, no lifecycle actions */ + if (!(table->flags & EcsTableIsComplex)) { + flecs_table_fast_append(world, table); + table->data.count = v_entities.count; + table->data.size = v_entities.size; + if (!count) { + flecs_table_set_empty(world, table); /* See below */ + } + return count; + } + + int32_t prev_count = table->data.count; + int32_t prev_size = table->data.size; + + ecs_assert(table->data.count == v_entities.count - 1, + ECS_INTERNAL_ERROR, NULL); + table->data.count = v_entities.count; + table->data.size = v_entities.size; + + /* If this is the first entity in this table, signal queries so that the + * table moves from an inactive table to an active table. */ + if (!count) { + flecs_table_set_empty(world, table); + } + + /* Reobtain size to ensure that the columns have the same size as the + * entities and record vectors. This keeps reasoning about when allocations + * occur easier. */ + int32_t size = v_entities.size; + + /* Grow component arrays with 1 element */ + int32_t i; + for (i = 0; i < column_count; i ++) { + ecs_column_t *column = &columns[i]; + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v_column = ecs_vec_from_column_ext(column, prev_count, prev_size, ti->size); + flecs_table_grow_column(world, &v_column, ti, 1, size, construct); + column->data = v_column.array; + + ecs_iter_action_t on_add_hook; + if (on_add && (on_add_hook = column->ti->hooks.on_add)) { + flecs_table_invoke_hook(world, table, on_add_hook, EcsOnAdd, column, + &entities[count], count, 1); + } + + ecs_assert(v_column.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v_column.count == v_entities.count, + ECS_INTERNAL_ERROR, NULL); + } + + ecs_table__t *meta = table->_; + int32_t bs_count = meta->bs_count; + ecs_bitset_t *bs_columns = meta->bs_columns; + + /* Add element to each bitset column */ + for (i = 0; i < bs_count; i ++) { + ecs_assert(bs_columns != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_bitset_t *bs = &bs_columns[i]; + flecs_bitset_addn(bs, 1); + } + + flecs_table_check_sanity(world, table); + + return count; +} + +/* Delete operation for tables that don't have any complex logic */ +static +void flecs_table_fast_delete( + ecs_table_t *table, + int32_t row) +{ + ecs_column_t *columns = table->data.columns; + int32_t i, count = table->column_count; + for (i = 0; i < count; i ++) { + ecs_column_t *column = &columns[i]; + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v = ecs_vec_from_column(column, table, ti->size); + ecs_vec_remove(&v, ti->size, row); + column->data = v.array; + } +} + +/* Delete entity from table */ +void flecs_table_delete( + ecs_world_t *world, + ecs_table_t *table, + int32_t row, + bool destruct) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + + flecs_table_check_sanity(world, table); + + int32_t count = ecs_table_count(table); + ecs_assert(count > 0, ECS_INTERNAL_ERROR, NULL); + count --; + ecs_assert(row <= count, ECS_INTERNAL_ERROR, NULL); + + /* Move last entity id to row */ + ecs_entity_t *entities = table->data.entities; + ecs_entity_t entity_to_move = entities[count]; + ecs_entity_t entity_to_delete = entities[row]; + entities[row] = entity_to_move; + + /* Update record of moved entity in entity index */ + if (row != count) { + ecs_record_t *record_to_move = flecs_entities_get(world, entity_to_move); + if (record_to_move) { + uint32_t row_flags = record_to_move->row & ECS_ROW_FLAGS_MASK; + record_to_move->row = ECS_ROW_TO_RECORD(row, row_flags); + ecs_assert(record_to_move->table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(record_to_move->table == table, ECS_INTERNAL_ERROR, NULL); + } + } + + /* If the table is monitored indicate that there has been a change */ + flecs_table_mark_table_dirty(world, table, 0); + + /* Destruct component data */ + ecs_column_t *columns = table->data.columns; + int32_t column_count = table->column_count; + int32_t i; + + /* If this is a table without lifecycle callbacks or special columns, take + * fast path that just remove an element from the array(s) */ + if (!(table->flags & EcsTableIsComplex)) { + if (row != count) { + flecs_table_fast_delete(table, row); + } + + table->data.count --; + if (!count) { + flecs_table_set_empty(world, table); + } + + flecs_table_check_sanity(world, table); + return; + } + + /* Last element, destruct & remove */ + if (row == count) { + /* If table has component destructors, invoke */ + if (destruct && (table->flags & EcsTableHasDtors)) { + for (i = 0; i < column_count; i ++) { + flecs_table_invoke_remove_hooks(world, table, &columns[i], + &entity_to_delete, row, 1, true); + } + } + + /* Not last element, move last element to deleted element & destruct */ + } else { + /* If table has component destructors, invoke */ + if ((table->flags & (EcsTableHasDtors | EcsTableHasMove))) { + for (i = 0; i < column_count; i ++) { + ecs_column_t *column = &columns[i]; + ecs_type_info_t *ti = column->ti; + ecs_size_t size = ti->size; + void *dst = ECS_ELEM(column->data, size, row); + void *src = ECS_ELEM(column->data, size, count); + + ecs_iter_action_t on_remove = ti->hooks.on_remove; + if (destruct && on_remove) { + flecs_table_invoke_hook(world, table, on_remove, + EcsOnRemove, column, &entity_to_delete, row, 1); + } + + ecs_move_t move_dtor = ti->hooks.move_dtor; + + /* If neither move nor move_ctor are set, this indicates that + * non-destructive move semantics are not supported for this + * type. In such cases, we set the move_dtor as ctor_move_dtor, + * which indicates a destructive move operation. This adjustment + * ensures compatibility with different language bindings. */ + if (!ti->hooks.move_ctor && ti->hooks.ctor_move_dtor) { + move_dtor = ti->hooks.ctor_move_dtor; + } + + if (move_dtor) { + move_dtor(dst, src, 1, ti); + } else { + ecs_os_memcpy(dst, src, size); + } + } + } else { + flecs_table_fast_delete(table, row); + } + } + + /* Remove elements from bitset columns */ + ecs_table__t *meta = table->_; + ecs_bitset_t *bs_columns = meta->bs_columns; + int32_t bs_count = meta->bs_count; + for (i = 0; i < bs_count; i ++) { + flecs_bitset_remove(&bs_columns[i], row); + } + + table->data.count --; + if (!count) { + flecs_table_set_empty(world, table); + } + + flecs_table_check_sanity(world, table); +} + +/* Move operation for tables that don't have any complex logic */ +static +void flecs_table_fast_move( + ecs_table_t *dst_table, + int32_t dst_index, + ecs_table_t *src_table, + int32_t src_index) +{ + int32_t i_new = 0, dst_column_count = dst_table->column_count; + int32_t i_old = 0, src_column_count = src_table->column_count; + + ecs_column_t *src_columns = src_table->data.columns; + ecs_column_t *dst_columns = dst_table->data.columns; + + for (; (i_new < dst_column_count) && (i_old < src_column_count);) { + ecs_column_t *dst_column = &dst_columns[i_new]; + ecs_column_t *src_column = &src_columns[i_old]; + ecs_id_t dst_id = flecs_column_id(dst_table, i_new); + ecs_id_t src_id = flecs_column_id(src_table, i_old); + + if (dst_id == src_id) { + int32_t size = dst_column->ti->size; + void *dst = ECS_ELEM(dst_column->data, size, dst_index); + void *src = ECS_ELEM(src_column->data, size, src_index); + ecs_os_memcpy(dst, src, size); + } + + i_new += dst_id <= src_id; + i_old += dst_id >= src_id; + } +} + +/* Move entity from src to dst table */ +void flecs_table_move( + ecs_world_t *world, + ecs_entity_t dst_entity, + ecs_entity_t src_entity, + ecs_table_t *dst_table, + int32_t dst_index, + ecs_table_t *src_table, + int32_t src_index, + bool construct) +{ + ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!dst_table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + ecs_assert(!src_table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + + ecs_assert(src_index >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(dst_index >= 0, ECS_INTERNAL_ERROR, NULL); + + flecs_table_check_sanity(world, dst_table); + flecs_table_check_sanity(world, src_table); + + if (!((dst_table->flags | src_table->flags) & EcsTableIsComplex)) { + flecs_table_fast_move(dst_table, dst_index, src_table, src_index); + flecs_table_check_sanity(world, dst_table); + flecs_table_check_sanity(world, src_table); + return; + } + + flecs_table_move_bitset_columns( + dst_table, dst_index, src_table, src_index, 1, false); + + /* If the source and destination entities are the same, move component + * between tables. If the entities are not the same (like when cloning) use + * a copy. */ + bool same_entity = dst_entity == src_entity; + + /* Call move_dtor for moved away from storage only if the entity is at the + * last index in the source table. If it isn't the last entity, the last + * entity in the table will be moved to the src storage, which will take + * care of cleaning up resources. */ + bool use_move_dtor = ecs_table_count(src_table) == (src_index + 1); + + int32_t i_new = 0, dst_column_count = dst_table->column_count; + int32_t i_old = 0, src_column_count = src_table->column_count; + + ecs_column_t *src_columns = src_table->data.columns; + ecs_column_t *dst_columns = dst_table->data.columns; + + for (; (i_new < dst_column_count) && (i_old < src_column_count); ) { + ecs_column_t *dst_column = &dst_columns[i_new]; + ecs_column_t *src_column = &src_columns[i_old]; + ecs_id_t dst_id = flecs_column_id(dst_table, i_new); + ecs_id_t src_id = flecs_column_id(src_table, i_old); + + if (dst_id == src_id) { + ecs_type_info_t *ti = dst_column->ti; + int32_t size = ti->size; + + ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); + void *dst = ECS_ELEM(dst_column->data, size, dst_index); + void *src = ECS_ELEM(src_column->data, size, src_index); + + if (same_entity) { + ecs_move_t move = ti->hooks.move_ctor; + if (use_move_dtor || !move) { + /* Also use move_dtor if component doesn't have a move_ctor + * registered, to ensure that the dtor gets called to + * cleanup resources. */ + move = ti->hooks.ctor_move_dtor; + } + + if (move) { + move(dst, src, 1, ti); + } else { + ecs_os_memcpy(dst, src, size); + } + } else { + ecs_copy_t copy = ti->hooks.copy_ctor; + if (copy) { + copy(dst, src, 1, ti); + } else { + ecs_os_memcpy(dst, src, size); + } + } + } else { + if (dst_id < src_id) { + flecs_table_invoke_add_hooks(world, dst_table, + dst_column, &dst_entity, dst_index, 1, construct); + } else if (same_entity) { + flecs_table_invoke_remove_hooks(world, src_table, + src_column, &src_entity, src_index, 1, use_move_dtor); + } + } + + i_new += dst_id <= src_id; + i_old += dst_id >= src_id; + } + + for (; (i_new < dst_column_count); i_new ++) { + flecs_table_invoke_add_hooks(world, dst_table, &dst_columns[i_new], + &dst_entity, dst_index, 1, construct); + } + + if (same_entity) { + for (; (i_old < src_column_count); i_old ++) { + flecs_table_invoke_remove_hooks(world, src_table, &src_columns[i_old], + &src_entity, src_index, 1, use_move_dtor); + } + } + + flecs_table_check_sanity(world, dst_table); + flecs_table_check_sanity(world, src_table); +} + +/* Append n entities to table */ +int32_t flecs_table_appendn( + ecs_world_t *world, + ecs_table_t *table, + int32_t to_add, + const ecs_entity_t *ids) +{ + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + + flecs_table_check_sanity(world, table); + int32_t cur_count = ecs_table_count(table); + int32_t result = flecs_table_grow_data( + world, table, to_add, cur_count + to_add, ids); + flecs_table_check_sanity(world, table); + + return result; +} + +/* Shrink table storage to fit number of entities */ +bool flecs_table_shrink( + ecs_world_t *world, + ecs_table_t *table) +{ + ecs_assert(table != NULL, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + (void)world; + + flecs_table_check_sanity(world, table); + + bool has_payload = table->data.entities != NULL; + ecs_vec_t v_entities = ecs_vec_from_entities(table); + ecs_vec_reclaim_t(&world->allocator, &v_entities, ecs_entity_t); + + int32_t i, count = table->column_count; + for (i = 0; i < count; i ++) { + ecs_column_t *column = &table->data.columns[i]; + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v_column = ecs_vec_from_column(column, table, ti->size); + ecs_vec_reclaim(&world->allocator, &v_column, ti->size); + column->data = v_column.array; + } + + table->data.count = v_entities.count; + table->data.size = v_entities.size; + table->data.entities = v_entities.array; + + flecs_table_check_sanity(world, table); + + return has_payload; +} + +/* Swap operation for bitset (toggle component) columns */ +static +void flecs_table_swap_bitset_columns( + ecs_table_t *table, + int32_t row_1, + int32_t row_2) +{ + int32_t i = 0, column_count = table->_->bs_count; + if (!column_count) { + return; + } + + ecs_bitset_t *columns = table->_->bs_columns; + + for (i = 0; i < column_count; i ++) { + ecs_bitset_t *bs = &columns[i]; + flecs_bitset_swap(bs, row_1, row_2); + } +} + +/* Swap two rows in a table. Used for table sorting. */ +void flecs_table_swap( + ecs_world_t *world, + ecs_table_t *table, + int32_t row_1, + int32_t row_2) +{ + (void)world; + + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + ecs_assert(row_1 >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(row_2 >= 0, ECS_INTERNAL_ERROR, NULL); + + flecs_table_check_sanity(world, table); + + if (row_1 == row_2) { + return; + } + + /* If the table is monitored indicate that there has been a change */ + flecs_table_mark_table_dirty(world, table, 0); + + ecs_entity_t *entities = table->data.entities; + ecs_entity_t e1 = entities[row_1]; + ecs_entity_t e2 = entities[row_2]; + + ecs_record_t *record_ptr_1 = flecs_entities_get(world, e1); + ecs_record_t *record_ptr_2 = flecs_entities_get(world, e2); + + ecs_assert(record_ptr_1 != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(record_ptr_2 != NULL, ECS_INTERNAL_ERROR, NULL); + + /* Keep track of whether entity is watched */ + uint32_t flags_1 = ECS_RECORD_TO_ROW_FLAGS(record_ptr_1->row); + uint32_t flags_2 = ECS_RECORD_TO_ROW_FLAGS(record_ptr_2->row); + + /* Swap entities & records */ + entities[row_1] = e2; + entities[row_2] = e1; + record_ptr_1->row = ECS_ROW_TO_RECORD(row_2, flags_1); + record_ptr_2->row = ECS_ROW_TO_RECORD(row_1, flags_2); + + flecs_table_swap_bitset_columns(table, row_1, row_2); + + ecs_column_t *columns = table->data.columns; + if (!columns) { + flecs_table_check_sanity(world, table); + return; + } + + /* Find the maximum size of column elements + * and allocate a temporary buffer for swapping */ + int32_t i, temp_buffer_size = ECS_SIZEOF(uint64_t), column_count = table->column_count; + for (i = 0; i < column_count; i++) { + temp_buffer_size = ECS_MAX(temp_buffer_size, columns[i].ti->size); + } + + void* tmp = ecs_os_alloca(temp_buffer_size); + + /* Swap columns */ + for (i = 0; i < column_count; i ++) { + int32_t size = columns[i].ti->size; + ecs_column_t *column = &columns[i]; + void *ptr = column->data; + const ecs_type_info_t *ti = column->ti; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + + void *el_1 = ECS_ELEM(ptr, size, row_1); + void *el_2 = ECS_ELEM(ptr, size, row_2); + + ecs_move_t move = ti->hooks.move; + if (!move) { + ecs_os_memcpy(tmp, el_1, size); + ecs_os_memcpy(el_1, el_2, size); + ecs_os_memcpy(el_2, tmp, size); + } else { + ecs_move_t move_ctor = ti->hooks.move_ctor; + ecs_move_t move_dtor = ti->hooks.move_dtor; + ecs_assert(move_ctor != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(move_dtor != NULL, ECS_INTERNAL_ERROR, NULL); + move_ctor(tmp, el_1, 1, ti); + move(el_1, el_2, 1, ti); + move_dtor(el_2, tmp, 1, ti); + } + } + + flecs_table_check_sanity(world, table); +} + +static +void flecs_table_merge_vec( + ecs_world_t *world, + ecs_vec_t *dst, + ecs_vec_t *src, + int32_t size, + int32_t elem_size) +{ + int32_t dst_count = dst->count; + + if (!dst_count) { + ecs_vec_fini(&world->allocator, dst, size); + *dst = *src; + src->array = NULL; + src->count = 0; + src->size = 0; + } else { + int32_t src_count = src->count; + + if (elem_size) { + ecs_vec_set_size(&world->allocator, + dst, size, elem_size); + } + ecs_vec_set_count(&world->allocator, + dst, size, dst_count + src_count); + + void *dst_ptr = ECS_ELEM(dst->array, size, dst_count); + void *src_ptr = src->array; + ecs_os_memcpy(dst_ptr, src_ptr, size * src_count); + + ecs_vec_fini(&world->allocator, src, size); + } +} + +/* Merge data from one table column into other table column */ +static +void flecs_table_merge_column( + ecs_world_t *world, + ecs_vec_t *dst_vec, + ecs_vec_t *src_vec, + ecs_column_t *dst, + ecs_column_t *src, + int32_t column_size) +{ + const ecs_type_info_t *ti = dst->ti; + ecs_assert(ti == src->ti, ECS_INTERNAL_ERROR, NULL); + ecs_size_t elem_size = ti->size; + int32_t dst_count = ecs_vec_count(dst_vec); + + if (!dst_count) { + ecs_vec_fini(&world->allocator, dst_vec, elem_size); + *dst_vec = *src_vec; + + /* If the new table is not empty, copy the contents from the + * src into the dst. */ + } else { + int32_t src_count = src_vec->count; + + flecs_table_grow_column(world, dst_vec, ti, src_count, column_size, false); + void *dst_ptr = ECS_ELEM(dst_vec->array, elem_size, dst_count); + void *src_ptr = src_vec->array; + + /* Move values into column */ + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_move_t move = ti->hooks.ctor_move_dtor; + if (move) { + move(dst_ptr, src_ptr, src_count, ti); + } else { + ecs_os_memcpy(dst_ptr, src_ptr, elem_size * src_count); + } + + ecs_vec_fini(&world->allocator, src_vec, elem_size); + } + + dst->data = dst_vec->array; + src->data = NULL; +} + +/* Merge storage of two tables. */ +static +void flecs_table_merge_data( + ecs_world_t *world, + ecs_table_t *dst_table, + ecs_table_t *src_table, + int32_t dst_count, + int32_t src_count) +{ + int32_t i_new = 0, dst_column_count = dst_table->column_count; + int32_t i_old = 0, src_column_count = src_table->column_count; + ecs_column_t *src_columns = src_table->data.columns; + ecs_column_t *dst_columns = dst_table->data.columns; + + ecs_assert(!dst_column_count || dst_columns, ECS_INTERNAL_ERROR, NULL); + + if (!src_count) { + return; + } + + /* Merge entities */ + ecs_vec_t dst_entities = ecs_vec_from_entities(dst_table); + ecs_vec_t src_entities = ecs_vec_from_entities(src_table); + flecs_table_merge_vec(world, &dst_entities, &src_entities, + ECS_SIZEOF(ecs_entity_t), 0); + ecs_assert(dst_entities.count == src_count + dst_count, + ECS_INTERNAL_ERROR, NULL); + int32_t column_size = dst_entities.size; + ecs_allocator_t *a = &world->allocator; + + for (; (i_new < dst_column_count) && (i_old < src_column_count); ) { + ecs_column_t *dst_column = &dst_columns[i_new]; + ecs_column_t *src_column = &src_columns[i_old]; + ecs_id_t dst_id = flecs_column_id(dst_table, i_new); + ecs_id_t src_id = flecs_column_id(src_table, i_old); + ecs_size_t dst_elem_size = dst_column->ti->size; + ecs_size_t src_elem_size = src_column->ti->size; + + ecs_vec_t dst_vec = ecs_vec_from_column( + dst_column, dst_table, dst_elem_size); + ecs_vec_t src_vec = ecs_vec_from_column( + src_column, src_table, src_elem_size); + + if (dst_id == src_id) { + flecs_table_merge_column(world, &dst_vec, &src_vec, dst_column, + src_column, column_size); + flecs_table_mark_table_dirty(world, dst_table, i_new + 1); + i_new ++; + i_old ++; + } else if (dst_id < src_id) { + /* New column, make sure vector is large enough. */ + ecs_vec_set_size(a, &dst_vec, dst_elem_size, column_size); + dst_column->data = dst_vec.array; + flecs_table_invoke_ctor(dst_column, dst_count, src_count); + i_new ++; + } else if (dst_id > src_id) { + /* Old column does not occur in new table, destruct */ + flecs_table_invoke_dtor(src_column, 0, src_count); + ecs_vec_fini(a, &src_vec, src_elem_size); + src_column->data = NULL; + i_old ++; + } + } + + flecs_table_move_bitset_columns( + dst_table, dst_count, src_table, 0, src_count, true); + + /* Initialize remaining columns */ + for (; i_new < dst_column_count; i_new ++) { + ecs_column_t *column = &dst_columns[i_new]; + int32_t elem_size = column->ti->size; + ecs_assert(elem_size != 0, ECS_INTERNAL_ERROR, NULL); + ecs_vec_t vec = ecs_vec_from_column(column, dst_table, elem_size); + ecs_vec_set_size(a, &vec, elem_size, column_size); + column->data = vec.array; + flecs_table_invoke_ctor(column, dst_count, src_count); + } + + /* Destruct remaining columns */ + for (; i_old < src_column_count; i_old ++) { + ecs_column_t *column = &src_columns[i_old]; + int32_t elem_size = column->ti->size; + ecs_assert(elem_size != 0, ECS_INTERNAL_ERROR, NULL); + flecs_table_invoke_dtor(column, 0, src_count); + ecs_vec_t vec = ecs_vec_from_column(column, src_table, elem_size); + ecs_vec_fini(a, &vec, elem_size); + column->data = vec.array; + } + + /* Mark entity column as dirty */ + flecs_table_mark_table_dirty(world, dst_table, 0); + + dst_table->data.entities = dst_entities.array; + dst_table->data.count = dst_entities.count; + dst_table->data.size = dst_entities.size; + + src_table->data.entities = src_entities.array; + src_table->data.count = src_entities.count; + src_table->data.size = src_entities.size; +} + +/* Merge source table into destination table. This typically happens as result + * of a bulk operation, like when a component is removed from all entities in + * the source table (like for the Remove OnDelete policy). */ +void flecs_table_merge( + ecs_world_t *world, + ecs_table_t *dst_table, + ecs_table_t *src_table) +{ + ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!src_table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + ecs_assert(!dst_table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + + flecs_table_check_sanity(world, src_table); + flecs_table_check_sanity(world, dst_table); + + const ecs_entity_t *src_entities = ecs_table_entities(src_table); + int32_t src_count = ecs_table_count(src_table); + int32_t dst_count = ecs_table_count(dst_table); + + /* First, update entity index so old entities point to new type */ + int32_t i; + for(i = 0; i < src_count; i ++) { + ecs_record_t *record = flecs_entities_ensure(world, src_entities[i]); + uint32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); + record->row = ECS_ROW_TO_RECORD(dst_count + i, flags); + record->table = dst_table; + } + + /* Merge table columns */ + flecs_table_merge_data(world, dst_table, src_table, dst_count, src_count); + + if (src_count) { + if (!dst_count) { + flecs_table_set_empty(world, dst_table); + } + flecs_table_set_empty(world, src_table); + + flecs_table_traversable_add(dst_table, src_table->_->traversable_count); + flecs_table_traversable_add(src_table, -src_table->_->traversable_count); + ecs_assert(src_table->_->traversable_count == 0, ECS_INTERNAL_ERROR, NULL); + } + + flecs_table_check_sanity(world, src_table); + flecs_table_check_sanity(world, dst_table); +} + +/* Internal mechanism for propagating information to tables */ +void flecs_table_notify( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + ecs_table_event_t *event) +{ + if (world->flags & EcsWorldFini) { + return; + } + + switch(event->kind) { + case EcsTableTriggersForId: + flecs_table_add_trigger_flags(world, table, id, event->event); + break; + case EcsTableNoTriggersForId: + break; /* TODO */ + } +} + +int32_t flecs_table_get_toggle_column( + ecs_table_t *table, + ecs_id_t id) +{ + ecs_id_t *ids = table->type.array; + int32_t i = table->_->bs_offset, end = i + table->_->bs_count; + + for (; i < end; i ++) { + if (ids[i] == (ECS_TOGGLE | id)) { + return i; + } + } + + return -1; +} + +ecs_bitset_t* flecs_table_get_toggle( + ecs_table_t *table, + ecs_id_t id) +{ + int32_t toggle_column = flecs_table_get_toggle_column(table, id); + if (toggle_column == -1) { + return NULL; + } + + toggle_column -= table->_->bs_offset; + ecs_assert(toggle_column >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(toggle_column < table->_->bs_count, ECS_INTERNAL_ERROR, NULL); + return &table->_->bs_columns[toggle_column]; +} + +ecs_id_t flecs_column_id( + ecs_table_t *table, + int32_t column_index) +{ + int32_t type_index = table->column_map[table->type.count + column_index]; + return table->type.array[type_index]; +} + +/* -- Public API -- */ + +void ecs_table_lock( + ecs_world_t *world, + ecs_table_t *table) +{ + if (table) { + if (flecs_poly_is(world, ecs_world_t) && !(world->flags & EcsWorldReadonly)) { + table->_->lock ++; + } + } +} + +void ecs_table_unlock( + ecs_world_t *world, + ecs_table_t *table) +{ + if (table) { + if (flecs_poly_is(world, ecs_world_t) && !(world->flags & EcsWorldReadonly)) { + table->_->lock --; + ecs_assert(table->_->lock >= 0, ECS_INVALID_OPERATION, + "table_unlock called more often than table_lock"); + } + } +} + +const ecs_type_t* ecs_table_get_type( + const ecs_table_t *table) +{ + if (table) { + return &table->type; + } else { + return NULL; + } +} + +int32_t ecs_table_get_type_index( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); + + if (id < FLECS_HI_COMPONENT_ID) { + int16_t res = table->component_map[id]; + if (res > 0) { + return table->column_map[table->type.count + (res - 1)]; + } + + return -res - 1; + } + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return -1; + } + + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return -1; + } + + return tr->index; +error: + return -1; +} + +int32_t ecs_table_get_column_index( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + + if (id < FLECS_HI_COMPONENT_ID) { + int16_t res = table->component_map[id]; + if (res > 0) { + return res - 1; + } + return -1; + } + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return -1; + } + + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return -1; + } + + return tr->column; +error: + return -1; +} + +int32_t ecs_table_column_count( + const ecs_table_t *table) +{ + return table->column_count; +} + +int32_t ecs_table_type_to_column_index( + const ecs_table_t *table, + int32_t index) +{ + ecs_assert(index >= 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(index < table->type.count, ECS_INVALID_PARAMETER, NULL); + int16_t *column_map = table->column_map; + if (column_map) { + return column_map[index]; + } +error: + return -1; +} + +int32_t ecs_table_column_to_type_index( + const ecs_table_t *table, + int32_t index) +{ + ecs_check(index < table->column_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(table->column_map != NULL, ECS_INVALID_PARAMETER, NULL); + int32_t offset = table->type.count; + return table->column_map[offset + index]; +error: + return -1; +} + +void* ecs_table_get_column( + const ecs_table_t *table, + int32_t index, + int32_t offset) +{ + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(index < table->column_count, ECS_INVALID_PARAMETER, NULL); + + ecs_column_t *column = &table->data.columns[index]; + void *result = column->data; + if (offset) { + result = ECS_ELEM(result, column->ti->size, offset); + } + + return result; +error: + return NULL; +} + +void* ecs_table_get_id( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id, + int32_t offset) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + + world = ecs_get_world(world); + + int32_t index = ecs_table_get_column_index(world, table, id); + if (index == -1) { + return NULL; + } + + return ecs_table_get_column(table, index, offset); +error: + return NULL; +} + +size_t ecs_table_get_column_size( + const ecs_table_t *table, + int32_t column) +{ + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(column < table->column_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(table->column_map != NULL, ECS_INVALID_PARAMETER, NULL); + + return flecs_ito(size_t, table->data.columns[column].ti->size); +error: + return 0; +} + +int32_t ecs_table_count( + const ecs_table_t *table) +{ + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + return table->data.count; +} + +int32_t ecs_table_size( + const ecs_table_t *table) +{ + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + return table->data.size; +} + +bool ecs_table_has_id( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id) +{ + return ecs_table_get_type_index(world, table, id) != -1; +} + +int32_t ecs_table_get_depth( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_entity_t rel) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, rel), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_has_id(world, rel, EcsAcyclic), ECS_INVALID_PARAMETER, + "cannot safely determine depth for relationship that is not acyclic " + "(add Acyclic property to relationship)"); + + world = ecs_get_world(world); + + return flecs_relation_depth(world, rel, table); +error: + return -1; +} + +bool ecs_table_has_flags( + ecs_table_t *table, + ecs_flags32_t flags) +{ + return (table->flags & flags) == flags; +} + +void ecs_table_swap_rows( + ecs_world_t* world, + ecs_table_t* table, + int32_t row_1, + int32_t row_2) +{ + flecs_table_swap(world, table, row_1, row_2); +} + +int32_t flecs_table_observed_count( + const ecs_table_t *table) +{ + return table->_->traversable_count; +} + +void* ecs_record_get_by_column( + const ecs_record_t *r, + int32_t index, + size_t c_size) +{ + (void)c_size; + ecs_table_t *table = r->table; + + ecs_check(index < table->column_count, ECS_INVALID_PARAMETER, NULL); + ecs_column_t *column = &table->data.columns[index]; + ecs_size_t size = column->ti->size; + + ecs_check(!flecs_utosize(c_size) || flecs_utosize(c_size) == size, + ECS_INVALID_PARAMETER, NULL); + + return ECS_ELEM(column->data, size, ECS_RECORD_TO_ROW(r->row)); +error: + return NULL; +} + +ecs_record_t* ecs_record_find( + const ecs_world_t *world, + ecs_entity_t entity) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); + + world = ecs_get_world(world); + + ecs_record_t *r = flecs_entities_get(world, entity); + if (r) { + return r; + } +error: + return NULL; +} + +/** + * @file storage/table_cache.c + * @brief Data structure for fast table iteration/lookups. + * + * A table cache is a data structure that provides constant time operations for + * insertion and removal of tables, and to testing whether a table is registered + * with the cache. A table cache also provides functions to iterate the tables + * in a cache. + * + * The world stores a table cache per (component) id inside the id record + * administration. Cached queries store a table cache with matched tables. + * + * A table cache has separate lists for non-empty tables and empty tables. This + * improves performance as applications don't waste time iterating empty tables. + */ + + +static +void flecs_table_cache_list_remove( + ecs_table_cache_t *cache, + ecs_table_cache_hdr_t *elem) +{ + ecs_table_cache_hdr_t *next = elem->next; + ecs_table_cache_hdr_t *prev = elem->prev; + + if (next) { + next->prev = prev; + } + if (prev) { + prev->next = next; + } + + cache->empty_tables.count -= !!elem->empty; + cache->tables.count -= !elem->empty; + + if (cache->empty_tables.first == elem) { + cache->empty_tables.first = next; + } else if (cache->tables.first == elem) { + cache->tables.first = next; + } + if (cache->empty_tables.last == elem) { + cache->empty_tables.last = prev; + } + if (cache->tables.last == elem) { + cache->tables.last = prev; + } +} + +static +void flecs_table_cache_list_insert( + ecs_table_cache_t *cache, + ecs_table_cache_hdr_t *elem) +{ + ecs_table_cache_hdr_t *last; + if (elem->empty) { + last = cache->empty_tables.last; + cache->empty_tables.last = elem; + if ((++ cache->empty_tables.count) == 1) { + cache->empty_tables.first = elem; + } + } else { + last = cache->tables.last; + cache->tables.last = elem; + if ((++ cache->tables.count) == 1) { + cache->tables.first = elem; + } + } + + elem->next = NULL; + elem->prev = last; + + if (last) { + last->next = elem; + } +} + +void ecs_table_cache_init( + ecs_world_t *world, + ecs_table_cache_t *cache) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_map_init_w_params(&cache->index, &world->allocators.ptr); +} + +void ecs_table_cache_fini( + ecs_table_cache_t *cache) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_map_fini(&cache->index); +} + +bool ecs_table_cache_is_empty( + const ecs_table_cache_t *cache) +{ + return ecs_map_count(&cache->index) == 0; +} + +void ecs_table_cache_insert_w_empty( + ecs_table_cache_t *cache, + const ecs_table_t *table, + ecs_table_cache_hdr_t *result, + bool empty) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_table_cache_get(cache, table) == NULL, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(result != NULL, ECS_INTERNAL_ERROR, NULL); + + result->cache = cache; + result->table = ECS_CONST_CAST(ecs_table_t*, table); + result->empty = empty; + + flecs_table_cache_list_insert(cache, result); + + if (table) { + ecs_map_insert_ptr(&cache->index, table->id, result); + } + + ecs_assert(empty || cache->tables.first != NULL, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(!empty || cache->empty_tables.first != NULL, + ECS_INTERNAL_ERROR, NULL); +} + +void ecs_table_cache_insert( + ecs_table_cache_t *cache, + const ecs_table_t *table, + ecs_table_cache_hdr_t *result) +{ + bool empty; + if (!table) { + empty = false; + } else { + empty = ecs_table_count(table) == 0; + } + + ecs_table_cache_insert_w_empty(cache, table, result, empty); +} + +void ecs_table_cache_replace( + ecs_table_cache_t *cache, + const ecs_table_t *table, + ecs_table_cache_hdr_t *elem) +{ + ecs_table_cache_hdr_t **r = ecs_map_get_ref( + &cache->index, ecs_table_cache_hdr_t, table->id); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_table_cache_hdr_t *old = *r; + ecs_assert(old != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_table_cache_hdr_t *prev = old->prev, *next = old->next; + if (prev) { + ecs_assert(prev->next == old, ECS_INTERNAL_ERROR, NULL); + prev->next = elem; + } + if (next) { + ecs_assert(next->prev == old, ECS_INTERNAL_ERROR, NULL); + next->prev = elem; + } + + if (cache->empty_tables.first == old) { + cache->empty_tables.first = elem; + } + if (cache->empty_tables.last == old) { + cache->empty_tables.last = elem; + } + if (cache->tables.first == old) { + cache->tables.first = elem; + } + if (cache->tables.last == old) { + cache->tables.last = elem; + } + + *r = elem; + elem->prev = prev; + elem->next = next; +} + +void* ecs_table_cache_get( + const ecs_table_cache_t *cache, + const ecs_table_t *table) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + if (table) { + if (ecs_map_is_init(&cache->index)) { + return ecs_map_get_deref(&cache->index, void**, table->id); + } + return NULL; + } else { + ecs_table_cache_hdr_t *elem = cache->tables.first; + ecs_assert(!elem || elem->table == NULL, ECS_INTERNAL_ERROR, NULL); + return elem; + } +} + +void* ecs_table_cache_remove( + ecs_table_cache_t *cache, + uint64_t table_id, + ecs_table_cache_hdr_t *elem) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + 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); + + flecs_table_cache_list_remove(cache, elem); + ecs_map_remove(&cache->index, table_id); + + return elem; +} + +bool ecs_table_cache_set_empty( + ecs_table_cache_t *cache, + const ecs_table_t *table, + bool empty) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_table_cache_hdr_t *elem = ecs_map_get_deref(&cache->index, + ecs_table_cache_hdr_t, table->id); + if (!elem) { + return false; + } + + if (elem->empty == empty) { + return false; + } + + flecs_table_cache_list_remove(cache, elem); + elem->empty = empty; + flecs_table_cache_list_insert(cache, elem); + + return true; +} + +bool flecs_table_cache_iter( + ecs_table_cache_t *cache, + ecs_table_cache_iter_t *out) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(out != NULL, ECS_INTERNAL_ERROR, NULL); + out->next = cache->tables.first; + out->next_list = NULL; + out->cur = NULL; + return out->next != NULL; +} + +bool flecs_table_cache_empty_iter( + ecs_table_cache_t *cache, + ecs_table_cache_iter_t *out) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(out != NULL, ECS_INTERNAL_ERROR, NULL); + out->next = cache->empty_tables.first; + out->next_list = NULL; + out->cur = NULL; + return out->next != NULL; +} + +bool flecs_table_cache_all_iter( + ecs_table_cache_t *cache, + ecs_table_cache_iter_t *out) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(out != NULL, ECS_INTERNAL_ERROR, NULL); + out->next = cache->empty_tables.first; + out->next_list = cache->tables.first; + out->cur = NULL; + return out->next != NULL || out->next_list != NULL; +} + +ecs_table_cache_hdr_t* flecs_table_cache_next_( + ecs_table_cache_iter_t *it) +{ + ecs_table_cache_hdr_t *next = it->next; + if (!next) { + next = it->next_list; + it->next_list = NULL; + if (!next) { + return NULL; + } + } + + it->cur = next; + it->next = next->next; + return next; +} + +/** + * @file storage/table_graph.c + * @brief Data structure to speed up table transitions. + * + * The table graph is used to speed up finding tables in add/remove operations. + * For example, if component C is added to an entity in table [A, B], the entity + * must be moved to table [A, B, C]. The graph speeds this process up with an + * edge for component C that connects [A, B] to [A, B, C]. + */ + + +/* Id sequence (type) utilities */ + +static +uint64_t flecs_type_hash(const void *ptr) { + const ecs_type_t *type = ptr; + ecs_id_t *ids = type->array; + int32_t count = type->count; + return flecs_hash(ids, count * ECS_SIZEOF(ecs_id_t)); +} + +static +int flecs_type_compare(const void *ptr_1, const void *ptr_2) { + const ecs_type_t *type_1 = ptr_1; + const ecs_type_t *type_2 = ptr_2; + + int32_t count_1 = type_1->count; + int32_t count_2 = type_2->count; + + if (count_1 != count_2) { + return (count_1 > count_2) - (count_1 < count_2); + } + + const ecs_id_t *ids_1 = type_1->array; + const ecs_id_t *ids_2 = type_2->array; + int result = 0; + + int32_t i; + for (i = 0; !result && (i < count_1); i ++) { + ecs_id_t id_1 = ids_1[i]; + ecs_id_t id_2 = ids_2[i]; + result = (id_1 > id_2) - (id_1 < id_2); + } + + return result; +} + +void flecs_table_hashmap_init( + ecs_world_t *world, + ecs_hashmap_t *hm) +{ + flecs_hashmap_init(hm, ecs_type_t, ecs_table_t*, + flecs_type_hash, flecs_type_compare, &world->allocator); +} + +/* Find location where to insert id into type */ +static +int flecs_type_find_insert( + const ecs_type_t *type, + int32_t offset, + ecs_id_t to_add) +{ + ecs_id_t *array = type->array; + int32_t i, count = type->count; + + for (i = offset; i < count; i ++) { + ecs_id_t id = array[i]; + if (id == to_add) { + return -1; + } + if (id > to_add) { + return i; + } + } + return i; +} + +/* Find location of id in type */ +static +int flecs_type_find( + const ecs_type_t *type, + ecs_id_t id) +{ + ecs_id_t *array = type->array; + int32_t i, count = type->count; + + for (i = 0; i < count; i ++) { + ecs_id_t cur = array[i]; + if (ecs_id_match(cur, id)) { + return i; + } + if (cur > id) { + return -1; + } + } + + return -1; +} + +/* Count number of matching ids */ +static +int flecs_type_count_matches( + const ecs_type_t *type, + ecs_id_t wildcard, + int32_t offset) +{ + ecs_id_t *array = type->array; + int32_t i = offset, count = type->count; + + for (; i < count; i ++) { + ecs_id_t cur = array[i]; + if (!ecs_id_match(cur, wildcard)) { + break; + } + } + + return i - offset; +} + +/* Create type from source type with id */ +static +int flecs_type_new_with( + ecs_world_t *world, + ecs_type_t *dst, + const ecs_type_t *src, + ecs_id_t with) +{ + ecs_id_t *src_array = src->array; + int32_t at = flecs_type_find_insert(src, 0, with); + if (at == -1) { + return -1; + } + + int32_t dst_count = src->count + 1; + ecs_id_t *dst_array = flecs_walloc_n(world, ecs_id_t, dst_count); + dst->count = dst_count; + dst->array = dst_array; + + if (at) { + ecs_os_memcpy_n(dst_array, src_array, ecs_id_t, at); + } + + int32_t remain = src->count - at; + if (remain) { + ecs_os_memcpy_n(&dst_array[at + 1], &src_array[at], ecs_id_t, remain); + } + + dst_array[at] = with; + + return 0; +} + +/* Create type from source type without ids matching wildcard */ +static +int flecs_type_new_filtered( + ecs_world_t *world, + ecs_type_t *dst, + const ecs_type_t *src, + ecs_id_t wildcard, + int32_t at) +{ + *dst = flecs_type_copy(world, src); + ecs_id_t *dst_array = dst->array; + ecs_id_t *src_array = src->array; + if (at) { + ecs_assert(dst_array != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_os_memcpy_n(dst_array, src_array, ecs_id_t, at); + } + + int32_t i = at + 1, w = at, count = src->count; + for (; i < count; i ++) { + ecs_id_t id = src_array[i]; + if (!ecs_id_match(id, wildcard)) { + dst_array[w] = id; + w ++; + } + } + + dst->count = w; + if (w != count) { + dst->array = flecs_wrealloc_n(world, ecs_id_t, w, count, dst->array); + } + + return 0; +} + +/* Create type from source type without id */ +static +int flecs_type_new_without( + ecs_world_t *world, + ecs_type_t *dst, + const ecs_type_t *src, + ecs_id_t without) +{ + ecs_id_t *src_array = src->array; + int32_t count = 1, at = flecs_type_find(src, without); + if (at == -1) { + return -1; + } + + int32_t src_count = src->count; + if (src_count == 1) { + dst->array = NULL; + dst->count = 0; + return 0; + } + + if (ecs_id_is_wildcard(without)) { + if (ECS_IS_PAIR(without)) { + ecs_entity_t r = ECS_PAIR_FIRST(without); + ecs_entity_t o = ECS_PAIR_SECOND(without); + if (r == EcsWildcard && o != EcsWildcard) { + return flecs_type_new_filtered(world, dst, src, without, at); + } + } + count += flecs_type_count_matches(src, without, at + 1); + } + + int32_t dst_count = src_count - count; + dst->count = dst_count; + if (!dst_count) { + dst->array = NULL; + return 0; + } + + ecs_id_t *dst_array = flecs_walloc_n(world, ecs_id_t, dst_count); + dst->array = dst_array; + + if (at) { + ecs_os_memcpy_n(dst_array, src_array, ecs_id_t, at); + } + + int32_t remain = dst_count - at; + if (remain) { + ecs_os_memcpy_n( + &dst_array[at], &src_array[at + count], ecs_id_t, remain); + } + + return 0; +} + +/* Copy type */ +ecs_type_t flecs_type_copy( + ecs_world_t *world, + const ecs_type_t *src) +{ + int32_t src_count = src->count; + if (!src_count) { + return (ecs_type_t){ 0 }; + } + + ecs_id_t *ids = flecs_walloc_n(world, ecs_id_t, src_count); + ecs_os_memcpy_n(ids, src->array, ecs_id_t, src_count); + return (ecs_type_t) { + .array = ids, + .count = src_count + }; +} + +/* Free type */ +void flecs_type_free( + ecs_world_t *world, + ecs_type_t *type) +{ + int32_t count = type->count; + if (count) { + flecs_wfree_n(world, ecs_id_t, type->count, type->array); + } +} + +/* Add to type */ +static +void flecs_type_add( + ecs_world_t *world, + ecs_type_t *type, + ecs_id_t add) +{ + ecs_type_t new_type; + int res = flecs_type_new_with(world, &new_type, type, add); + if (res != -1) { + flecs_type_free(world, type); + type->array = new_type.array; + type->count = new_type.count; + } +} + +/* Graph edge utilities */ + +void flecs_table_diff_builder_init( + ecs_world_t *world, + ecs_table_diff_builder_t *builder) +{ + ecs_allocator_t *a = &world->allocator; + ecs_vec_init_t(a, &builder->added, ecs_id_t, 256); + ecs_vec_init_t(a, &builder->removed, ecs_id_t, 256); + builder->added_flags = 0; + builder->removed_flags = 0; +} + +void flecs_table_diff_builder_fini( + ecs_world_t *world, + ecs_table_diff_builder_t *builder) +{ + ecs_allocator_t *a = &world->allocator; + ecs_vec_fini_t(a, &builder->added, ecs_id_t); + ecs_vec_fini_t(a, &builder->removed, ecs_id_t); +} + +void flecs_table_diff_builder_clear( + ecs_table_diff_builder_t *builder) +{ + ecs_vec_clear(&builder->added); + ecs_vec_clear(&builder->removed); +} + +static +void flecs_table_diff_build_type( + ecs_world_t *world, + ecs_vec_t *vec, + ecs_type_t *type, + int32_t offset) +{ + int32_t count = vec->count - offset; + ecs_assert(count >= 0, ECS_INTERNAL_ERROR, NULL); + if (count) { + type->array = flecs_wdup_n(world, ecs_id_t, count, + ECS_ELEM_T(vec->array, ecs_id_t, offset)); + type->count = count; + ecs_vec_set_count_t(&world->allocator, vec, ecs_id_t, offset); + } +} + +void flecs_table_diff_build( + ecs_world_t *world, + ecs_table_diff_builder_t *builder, + ecs_table_diff_t *diff, + int32_t added_offset, + int32_t removed_offset) +{ + flecs_table_diff_build_type(world, &builder->added, &diff->added, + added_offset); + flecs_table_diff_build_type(world, &builder->removed, &diff->removed, + removed_offset); + diff->added_flags = builder->added_flags; + diff->removed_flags = builder->removed_flags; +} + +void flecs_table_diff_build_noalloc( + ecs_table_diff_builder_t *builder, + ecs_table_diff_t *diff) +{ + diff->added = (ecs_type_t){ + .array = builder->added.array, .count = builder->added.count }; + diff->removed = (ecs_type_t){ + .array = builder->removed.array, .count = builder->removed.count }; + diff->added_flags = builder->added_flags; + diff->removed_flags = builder->removed_flags; +} + +static +void flecs_table_diff_build_add_type_to_vec( + ecs_world_t *world, + ecs_vec_t *vec, + ecs_type_t *add) +{ + if (!add || !add->count) { + return; + } + + int32_t offset = vec->count; + ecs_vec_grow_t(&world->allocator, vec, ecs_id_t, add->count); + ecs_os_memcpy_n(ecs_vec_get_t(vec, ecs_id_t, offset), + add->array, ecs_id_t, add->count); +} + +void flecs_table_diff_build_append_table( + ecs_world_t *world, + ecs_table_diff_builder_t *dst, + ecs_table_diff_t *src) +{ + flecs_table_diff_build_add_type_to_vec(world, &dst->added, &src->added); + flecs_table_diff_build_add_type_to_vec(world, &dst->removed, &src->removed); + dst->added_flags |= src->added_flags; + dst->removed_flags |= src->removed_flags; +} + +static +void flecs_table_diff_free( + ecs_world_t *world, + ecs_table_diff_t *diff) +{ + flecs_wfree_n(world, ecs_id_t, diff->added.count, diff->added.array); + flecs_wfree_n(world, ecs_id_t, diff->removed.count, diff->removed.array); + flecs_bfree(&world->allocators.table_diff, diff); +} + +static +ecs_graph_edge_t* flecs_table_ensure_hi_edge( + ecs_world_t *world, + ecs_graph_edges_t *edges, + ecs_id_t id) +{ + if (!edges->hi) { + edges->hi = flecs_alloc_t(&world->allocator, ecs_map_t); + ecs_map_init_w_params(edges->hi, &world->allocators.ptr); + } + + ecs_graph_edge_t **r = ecs_map_ensure_ref(edges->hi, ecs_graph_edge_t, id); + ecs_graph_edge_t *edge = r[0]; + if (edge) { + return edge; + } + + if (id < FLECS_HI_COMPONENT_ID) { + edge = &edges->lo[id]; + } else { + edge = flecs_bcalloc(&world->allocators.graph_edge); + } + + r[0] = edge; + return edge; +} + +static +ecs_graph_edge_t* flecs_table_ensure_edge( + ecs_world_t *world, + ecs_graph_edges_t *edges, + ecs_id_t id) +{ + ecs_graph_edge_t *edge; + + if (id < FLECS_HI_COMPONENT_ID) { + if (!edges->lo) { + edges->lo = flecs_bcalloc(&world->allocators.graph_edge_lo); + } + edge = &edges->lo[id]; + } else { + edge = flecs_table_ensure_hi_edge(world, edges, id); + } + + return edge; +} + +static +void flecs_table_disconnect_edge( + ecs_world_t *world, + ecs_id_t id, + ecs_graph_edge_t *edge) +{ + ecs_assert(edge != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edge->id == id, ECS_INTERNAL_ERROR, NULL); + (void)id; + + /* Remove backref from destination table */ + ecs_graph_edge_hdr_t *next = edge->hdr.next; + ecs_graph_edge_hdr_t *prev = edge->hdr.prev; + + if (next) { + next->prev = prev; + } + if (prev) { + prev->next = next; + } + + /* Remove data associated with edge */ + ecs_table_diff_t *diff = edge->diff; + if (diff) { + flecs_table_diff_free(world, diff); + } + + /* If edge id is low, clear it from fast lookup array */ + if (id < FLECS_HI_COMPONENT_ID) { + ecs_os_memset_t(edge, 0, ecs_graph_edge_t); + } else { + flecs_bfree(&world->allocators.graph_edge, edge); + } +} + +static +void flecs_table_remove_edge( + ecs_world_t *world, + ecs_graph_edges_t *edges, + ecs_id_t id, + ecs_graph_edge_t *edge) +{ + ecs_assert(edges != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edges->hi != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_table_disconnect_edge(world, id, edge); + ecs_map_remove(edges->hi, id); +} + +static +void flecs_table_init_edges( + ecs_graph_edges_t *edges) +{ + edges->lo = NULL; + edges->hi = NULL; +} + +static +void flecs_table_init_node( + ecs_graph_node_t *node) +{ + flecs_table_init_edges(&node->add); + flecs_table_init_edges(&node->remove); +} + +bool flecs_table_records_update_empty( + ecs_table_t *table) +{ + bool result = false; + bool is_empty = ecs_table_count(table) == 0; + + int32_t i, count = table->_->record_count; + for (i = 0; i < count; i ++) { + ecs_table_record_t *tr = &table->_->records[i]; + ecs_table_cache_t *cache = tr->hdr.cache; + result |= ecs_table_cache_set_empty(cache, table, is_empty); + } + + return result; +} + +static +void flecs_init_table( + ecs_world_t *world, + ecs_table_t *table, + ecs_table_t *prev) +{ + table->flags = 0; + table->dirty_state = NULL; + table->_->lock = 0; + table->_->generation = 0; + + flecs_table_init_node(&table->node); + + flecs_table_init(world, table, prev); +} + +static +ecs_table_t *flecs_table_new( + ecs_world_t *world, + ecs_type_t *type, + flecs_hashmap_result_t table_elem, + ecs_table_t *prev) +{ + ecs_os_perf_trace_push("flecs.table.create"); + + ecs_table_t *result = flecs_sparse_add_t(&world->store.tables, ecs_table_t); + ecs_assert(result != NULL, ECS_INTERNAL_ERROR, NULL); + result->_ = flecs_calloc_t(&world->allocator, ecs_table__t); + ecs_assert(result->_ != NULL, ECS_INTERNAL_ERROR, NULL); + +#ifdef FLECS_SANITIZE + int32_t i, j, count = type->count; + for (i = 0; i < count - 1; i ++) { + if (type->array[i] >= type->array[i + 1]) { + for (j = 0; j < count; j ++) { + char *str = ecs_id_str(world, type->array[j]); + if (i == j) { + ecs_err(" > %d: %s", j, str); + } else { + ecs_err(" %d: %s", j, str); + } + ecs_os_free(str); + } + ecs_abort(ECS_CONSTRAINT_VIOLATED, "table type is not ordered"); + } + } +#endif + + result->id = flecs_sparse_last_id(&world->store.tables); + result->type = *type; + + if (ecs_should_log_2()) { + char *expr = ecs_type_str(world, &result->type); + ecs_dbg_2( + "#[green]table#[normal] [%s] #[green]created#[reset] with id %d", + expr, result->id); + ecs_os_free(expr); + } + + ecs_log_push_2(); + + /* Store table in table hashmap */ + *(ecs_table_t**)table_elem.value = result; + + /* Set keyvalue to one that has the same lifecycle as the table */ + *(ecs_type_t*)table_elem.key = result->type; + result->_->hash = table_elem.hash; + + flecs_init_table(world, result, prev); + + /* Update counters */ + world->info.table_count ++; + world->info.empty_table_count ++; + world->info.table_create_total ++; + + ecs_log_pop_2(); + + ecs_os_perf_trace_pop("flecs.table.create"); + + return result; +} + +static +ecs_table_t* flecs_table_ensure( + ecs_world_t *world, + ecs_type_t *type, + bool own_type, + ecs_table_t *prev) +{ + flecs_poly_assert(world, ecs_world_t); + + int32_t id_count = type->count; + if (!id_count) { + return &world->store.root; + } + + ecs_table_t *table; + flecs_hashmap_result_t elem = flecs_hashmap_ensure( + &world->store.table_map, type, ecs_table_t*); + if ((table = *(ecs_table_t**)elem.value)) { + if (own_type) { + flecs_type_free(world, type); + } + return table; + } + + /* If we get here, table needs to be created which is only allowed when the + * application is not currently in progress */ + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); + + /* If we get here, the table has not been found, so create it. */ + if (own_type) { + return flecs_table_new(world, type, elem, prev); + } + + ecs_type_t copy = flecs_type_copy(world, type); + return flecs_table_new(world, ©, elem, prev); +} + +static +void flecs_diff_insert_added( + ecs_world_t *world, + ecs_table_diff_builder_t *diff, + ecs_id_t id) +{ + ecs_vec_append_t(&world->allocator, &diff->added, ecs_id_t)[0] = id; +} + +static +void flecs_diff_insert_removed( + ecs_world_t *world, + ecs_table_diff_builder_t *diff, + ecs_id_t id) +{ + ecs_allocator_t *a = &world->allocator; + ecs_vec_append_t(a, &diff->removed, ecs_id_t)[0] = id; +} + +static +void flecs_compute_table_diff( + ecs_world_t *world, + ecs_table_t *node, + ecs_table_t *next, + ecs_graph_edge_t *edge, + ecs_id_t id) +{ + ecs_type_t node_type = node->type; + ecs_type_t next_type = next->type; + + if (ECS_IS_PAIR(id)) { + ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair( + ECS_PAIR_FIRST(id), EcsWildcard)); + if (idr->flags & EcsIdIsUnion) { + if (node != next) { + id = ecs_pair(ECS_PAIR_FIRST(id), EcsUnion); + } else { + ecs_table_diff_t *diff = flecs_bcalloc( + &world->allocators.table_diff); + diff->added.count = 1; + diff->added.array = flecs_wdup_n(world, ecs_id_t, 1, &id); + diff->added_flags = EcsTableHasUnion; + edge->diff = diff; + return; + } + } + } + + ecs_id_t *ids_node = node_type.array; + ecs_id_t *ids_next = next_type.array; + int32_t i_node = 0, node_count = node_type.count; + int32_t i_next = 0, next_count = next_type.count; + int32_t added_count = 0; + int32_t removed_count = 0; + ecs_flags32_t added_flags = 0, removed_flags = 0; + bool trivial_edge = !ECS_HAS_RELATION(id, EcsIsA); + + /* First do a scan to see how big the diff is, so we don't have to realloc + * or alloc more memory than required. */ + for (; i_node < node_count && i_next < next_count; ) { + ecs_id_t id_node = ids_node[i_node]; + ecs_id_t id_next = ids_next[i_next]; + + bool added = id_next < id_node; + bool removed = id_node < id_next; + + trivial_edge &= !added || id_next == id; + trivial_edge &= !removed || id_node == id; + + if (added) { + added_flags |= flecs_id_flags_get(world, id_next) & + EcsTableAddEdgeFlags; + added_count ++; + } + + if (removed) { + removed_flags |= flecs_id_flags_get(world, id_node) & + EcsTableRemoveEdgeFlags; + removed_count ++; + } + + i_node += id_node <= id_next; + i_next += id_next <= id_node; + } + + for (; i_next < next_count; i_next ++) { + added_flags |= flecs_id_flags_get(world, ids_next[i_next]) & + EcsTableAddEdgeFlags; + added_count ++; + } + + for (; i_node < node_count; i_node ++) { + removed_flags |= flecs_id_flags_get(world, ids_node[i_node]) & + EcsTableRemoveEdgeFlags; + removed_count ++; + } + + trivial_edge &= (added_count + removed_count) <= 1 && + !ecs_id_is_wildcard(id) && !(added_flags|removed_flags); + + if (trivial_edge) { + /* If edge is trivial there's no need to create a diff element for it */ + return; + } + + ecs_table_diff_builder_t *builder = &world->allocators.diff_builder; + int32_t added_offset = builder->added.count; + int32_t removed_offset = builder->removed.count; + + for (i_node = 0, i_next = 0; i_node < node_count && i_next < next_count; ) { + ecs_id_t id_node = ids_node[i_node]; + ecs_id_t id_next = ids_next[i_next]; + + if (id_next < id_node) { + flecs_diff_insert_added(world, builder, id_next); + } else if (id_node < id_next) { + flecs_diff_insert_removed(world, builder, id_node); + } + + i_node += id_node <= id_next; + i_next += id_next <= id_node; + } + + for (; i_next < next_count; i_next ++) { + flecs_diff_insert_added(world, builder, ids_next[i_next]); + } + for (; i_node < node_count; i_node ++) { + flecs_diff_insert_removed(world, builder, ids_node[i_node]); + } + + ecs_table_diff_t *diff = flecs_bcalloc(&world->allocators.table_diff); + edge->diff = diff; + flecs_table_diff_build(world, builder, diff, added_offset, removed_offset); + diff->added_flags = added_flags; + diff->removed_flags = removed_flags; + + ecs_assert(diff->added.count == added_count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(diff->removed.count == removed_count, ECS_INTERNAL_ERROR, NULL); +} + +static +void flecs_add_overrides_for_base( + ecs_world_t *world, + ecs_type_t *dst_type, + ecs_id_t pair) +{ + ecs_entity_t base = ecs_pair_second(world, pair); + ecs_assert(base != 0, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *base_table = ecs_get_table(world, base); + if (!base_table) { + return; + } + + ecs_id_t *ids = base_table->type.array; + ecs_flags32_t flags = base_table->flags; + if (flags & EcsTableHasOverrides) { + int32_t i, count = base_table->type.count; + for (i = 0; i < count; i ++) { + ecs_id_t id = ids[i]; + ecs_id_t to_add = 0; + if (ECS_HAS_ID_FLAG(id, AUTO_OVERRIDE)) { + to_add = id & ~ECS_AUTO_OVERRIDE; + } else { + ecs_table_record_t *tr = &base_table->_->records[i]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + if (ECS_ID_ON_INSTANTIATE(idr->flags) == EcsOverride) { + to_add = id; + } + } + + if (to_add) { + ecs_id_t wc = ecs_pair(ECS_PAIR_FIRST(to_add), EcsWildcard); + bool exclusive = false; + if (ECS_IS_PAIR(to_add)) { + ecs_id_record_t *idr = flecs_id_record_get(world, wc); + if (idr) { + exclusive = (idr->flags & EcsIdExclusive) != 0; + } + } + if (!exclusive) { + flecs_type_add(world, dst_type, to_add); + } else { + int32_t column = flecs_type_find(dst_type, wc); + if (column == -1) { + flecs_type_add(world, dst_type, to_add); + } else { + dst_type->array[column] = to_add; + } + } + } + } + } + + if (flags & EcsTableHasIsA) { + ecs_table_record_t *tr = flecs_id_record_get_table( + world->idr_isa_wildcard, base_table); + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t i = tr->index, end = i + tr->count; + for (; i != end; i ++) { + flecs_add_overrides_for_base(world, dst_type, ids[i]); + } + } +} + +static +void flecs_add_with_property( + ecs_world_t *world, + ecs_id_record_t *idr_with_wildcard, + ecs_type_t *dst_type, + ecs_entity_t r, + ecs_entity_t o) +{ + r = ecs_get_alive(world, r); + + /* Check if component/relationship has With pairs, which contain ids + * that need to be added to the table. */ + ecs_table_t *table = ecs_get_table(world, r); + if (!table) { + return; + } + + ecs_table_record_t *tr = flecs_id_record_get_table( + idr_with_wildcard, table); + if (tr) { + int32_t i = tr->index, end = i + tr->count; + ecs_id_t *ids = table->type.array; + + for (; i < end; i ++) { + ecs_id_t id = ids[i]; + ecs_assert(ECS_PAIR_FIRST(id) == EcsWith, ECS_INTERNAL_ERROR, NULL); + ecs_id_t ra = ECS_PAIR_SECOND(id); + ecs_id_t a = ra; + if (o) { + a = ecs_pair(ra, o); + } + + flecs_type_add(world, dst_type, a); + flecs_add_with_property(world, idr_with_wildcard, dst_type, ra, o); + } + } + +} + +static +ecs_table_t* flecs_find_table_with( + ecs_world_t *world, + ecs_table_t *node, + ecs_id_t with) +{ + ecs_make_alive_id(world, with); + + ecs_id_record_t *idr = NULL; + ecs_entity_t r = 0, o = 0; + + if (ECS_IS_PAIR(with)) { + r = ECS_PAIR_FIRST(with); + o = ECS_PAIR_SECOND(with); + idr = flecs_id_record_ensure(world, ecs_pair(r, EcsWildcard)); + if (idr->flags & EcsIdIsUnion) { + with = ecs_pair(r, EcsUnion); + } else if (idr->flags & EcsIdExclusive) { + /* Relationship is exclusive, check if table already has it */ + ecs_table_record_t *tr = flecs_id_record_get_table(idr, node); + if (tr) { + /* Table already has an instance of the relationship, create + * a new id sequence with the existing id replaced */ + ecs_type_t dst_type = flecs_type_copy(world, &node->type); + ecs_assert(dst_type.array != NULL, ECS_INTERNAL_ERROR, NULL); + dst_type.array[tr->index] = with; + return flecs_table_ensure(world, &dst_type, true, node); + } + } + } else { + idr = flecs_id_record_ensure(world, with); + r = with; + } + + /* Create sequence with new id */ + ecs_type_t dst_type; + int res = flecs_type_new_with(world, &dst_type, &node->type, with); + if (res == -1) { + return node; /* Current table already has id */ + } + + if (r == EcsIsA) { + /* If adding a prefab, check if prefab has overrides */ + flecs_add_overrides_for_base(world, &dst_type, with); + } else if (r == EcsChildOf) { + o = ecs_get_alive(world, o); + if (ecs_has_id(world, o, EcsPrefab)) { + flecs_type_add(world, &dst_type, EcsPrefab); + } + } + + if (idr->flags & EcsIdWith) { + ecs_id_record_t *idr_with_wildcard = flecs_id_record_get(world, + ecs_pair(EcsWith, EcsWildcard)); + /* If id has With property, add targets to type */ + flecs_add_with_property(world, idr_with_wildcard, &dst_type, r, o); + } + + return flecs_table_ensure(world, &dst_type, true, node); +} + +static +ecs_table_t* flecs_find_table_without( + ecs_world_t *world, + ecs_table_t *node, + ecs_id_t without) +{ + if (ECS_IS_PAIR(without)) { + ecs_entity_t r = 0; + ecs_id_record_t *idr = NULL; + r = ECS_PAIR_FIRST(without); + idr = flecs_id_record_get(world, ecs_pair(r, EcsWildcard)); + if (idr && idr->flags & EcsIdIsUnion) { + without = ecs_pair(r, EcsUnion); + } + } + + /* Create sequence with new id */ + ecs_type_t dst_type; + int res = flecs_type_new_without(world, &dst_type, &node->type, without); + if (res == -1) { + return node; /* Current table does not have id */ + } + + return flecs_table_ensure(world, &dst_type, true, node); +} + +static +void flecs_table_init_edge( + ecs_table_t *table, + ecs_graph_edge_t *edge, + ecs_id_t id, + ecs_table_t *to) +{ + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edge != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edge->id == 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edge->hdr.next == NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edge->hdr.prev == NULL, ECS_INTERNAL_ERROR, NULL); + + edge->from = table; + edge->to = to; + edge->id = id; +} + +static +void flecs_init_edge_for_add( + ecs_world_t *world, + ecs_table_t *table, + ecs_graph_edge_t *edge, + ecs_id_t id, + ecs_table_t *to) +{ + flecs_table_init_edge(table, edge, id, to); + + flecs_table_ensure_hi_edge(world, &table->node.add, id); + + if ((table != to) || (table->flags & EcsTableHasUnion)) { + /* Add edges are appended to refs.next */ + ecs_graph_edge_hdr_t *to_refs = &to->node.refs; + ecs_graph_edge_hdr_t *next = to_refs->next; + + to_refs->next = &edge->hdr; + edge->hdr.prev = to_refs; + + edge->hdr.next = next; + if (next) { + next->prev = &edge->hdr; + } + + flecs_compute_table_diff(world, table, to, edge, id); + } +} + +static +void flecs_init_edge_for_remove( + ecs_world_t *world, + ecs_table_t *table, + ecs_graph_edge_t *edge, + ecs_id_t id, + ecs_table_t *to) +{ + flecs_table_init_edge(table, edge, id, to); + + flecs_table_ensure_hi_edge(world, &table->node.remove, id); + + if (table != to) { + /* Remove edges are appended to refs.prev */ + ecs_graph_edge_hdr_t *to_refs = &to->node.refs; + ecs_graph_edge_hdr_t *prev = to_refs->prev; + + to_refs->prev = &edge->hdr; + edge->hdr.next = to_refs; + + edge->hdr.prev = prev; + if (prev) { + prev->next = &edge->hdr; + } + + flecs_compute_table_diff(world, table, to, edge, id); + } +} + +static +ecs_table_t* flecs_create_edge_for_remove( + ecs_world_t *world, + ecs_table_t *node, + ecs_graph_edge_t *edge, + ecs_id_t id) +{ + ecs_table_t *to = flecs_find_table_without(world, node, id); + flecs_init_edge_for_remove(world, node, edge, id, to); + return to; +} + +static +ecs_table_t* flecs_create_edge_for_add( + ecs_world_t *world, + ecs_table_t *node, + ecs_graph_edge_t *edge, + ecs_id_t id) +{ + ecs_table_t *to = flecs_find_table_with(world, node, id); + flecs_init_edge_for_add(world, node, edge, id, to); + return to; +} + +ecs_table_t* flecs_table_traverse_remove( + ecs_world_t *world, + ecs_table_t *node, + ecs_id_t *id_ptr, + ecs_table_diff_t *diff) +{ + flecs_poly_assert(world, ecs_world_t); + + node = node ? node : &world->store.root; + + /* Removing 0 from an entity is not valid */ + ecs_check(id_ptr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(id_ptr[0] != 0, ECS_INVALID_PARAMETER, NULL); + + ecs_id_t id = id_ptr[0]; + ecs_graph_edge_t *edge = flecs_table_ensure_edge(world, &node->node.remove, id); + ecs_table_t *to = edge->to; + + if (!to) { + to = flecs_create_edge_for_remove(world, node, edge, id); + ecs_assert(to != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edge->to != NULL, ECS_INTERNAL_ERROR, NULL); + } - /* Force units */ + if (node != to) { + if (edge->diff) { + *diff = *edge->diff; + } else { + diff->added.count = 0; + diff->removed.array = id_ptr; + diff->removed.count = 1; + } + } - EcsForce = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Force" }); - prev_scope = ecs_set_scope(world, EcsForce); - EcsNewton = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Newton" }), - .quantity = EcsForce, - .symbol = "N" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsNewton, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); + return to; +error: + return NULL; +} - /* Length units */ +ecs_table_t* flecs_table_traverse_add( + ecs_world_t *world, + ecs_table_t *node, + ecs_id_t *id_ptr, + ecs_table_diff_t *diff) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_assert(diff != NULL, ECS_INTERNAL_ERROR, NULL); - EcsLength = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Length" }); - prev_scope = ecs_set_scope(world, EcsLength); - EcsMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Meters" }), - .quantity = EcsLength, - .symbol = "m" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMeters, - .kind = EcsF32 - }); + node = node ? node : &world->store.root; - EcsPicoMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "PicoMeters" }), - .quantity = EcsLength, - .base = EcsMeters, - .prefix = EcsPico }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsPicoMeters, - .kind = EcsF32 - }); + /* Adding 0 to an entity is not valid */ + ecs_check(id_ptr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(id_ptr[0] != 0, ECS_INVALID_PARAMETER, NULL); - EcsNanoMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "NanoMeters" }), - .quantity = EcsLength, - .base = EcsMeters, - .prefix = EcsNano }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsNanoMeters, - .kind = EcsF32 - }); + ecs_id_t id = id_ptr[0]; + ecs_graph_edge_t *edge = flecs_table_ensure_edge(world, &node->node.add, id); + ecs_table_t *to = edge->to; - EcsMicroMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MicroMeters" }), - .quantity = EcsLength, - .base = EcsMeters, - .prefix = EcsMicro }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMicroMeters, - .kind = EcsF32 - }); + if (!to) { + to = flecs_create_edge_for_add(world, node, edge, id); + ecs_assert(to != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edge->to != NULL, ECS_INTERNAL_ERROR, NULL); + } - EcsMilliMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MilliMeters" }), - .quantity = EcsLength, - .base = EcsMeters, - .prefix = EcsMilli }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMilliMeters, - .kind = EcsF32 - }); + if (node != to || edge->diff) { + if (edge->diff) { + *diff = *edge->diff; + if (diff->added_flags & EcsIdIsUnion) { + if (diff->added.count == 1) { + diff->added.array = id_ptr; + diff->added.count = 1; + diff->removed.count = 0; + } + } + } else { + diff->added.array = id_ptr; + diff->added.count = 1; + diff->removed.count = 0; + } + } - EcsCentiMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "CentiMeters" }), - .quantity = EcsLength, - .base = EcsMeters, - .prefix = EcsCenti }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsCentiMeters, - .kind = EcsF32 - }); + return to; +error: + return NULL; +} - EcsKiloMeters = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KiloMeters" }), - .quantity = EcsLength, - .base = EcsMeters, - .prefix = EcsKilo }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKiloMeters, - .kind = EcsF32 - }); - - EcsMiles = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Miles" }), - .quantity = EcsLength, - .symbol = "mi" - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMiles, - .kind = EcsF32 - }); +ecs_table_t* flecs_table_find_or_create( + ecs_world_t *world, + ecs_type_t *type) +{ + flecs_poly_assert(world, ecs_world_t); + return flecs_table_ensure(world, type, false, NULL); +} - EcsPixels = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Pixels" }), - .quantity = EcsLength, - .symbol = "px" - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsPixels, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); +void flecs_init_root_table( + ecs_world_t *world) +{ + flecs_poly_assert(world, ecs_world_t); - /* Pressure units */ + world->store.root.type = (ecs_type_t){0}; + world->store.root._ = flecs_calloc_t(&world->allocator, ecs_table__t); + flecs_init_table(world, &world->store.root, NULL); - EcsPressure = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Pressure" }); - prev_scope = ecs_set_scope(world, EcsPressure); - EcsPascal = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Pascal" }), - .quantity = EcsPressure, - .symbol = "Pa" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsPascal, - .kind = EcsF32 - }); - EcsBar = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Bar" }), - .quantity = EcsPressure, - .symbol = "bar" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsBar, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); + /* Ensure table indices start at 1, as 0 is reserved for the root */ + uint64_t new_id = flecs_sparse_new_id(&world->store.tables); + ecs_assert(new_id == 0, ECS_INTERNAL_ERROR, NULL); + (void)new_id; +} - /* Speed units */ +void flecs_table_edges_add_flags( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + ecs_flags32_t flags) +{ + ecs_graph_node_t *table_node = &table->node; + ecs_graph_edge_hdr_t *node_refs = &table_node->refs; - EcsSpeed = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Speed" }); - prev_scope = ecs_set_scope(world, EcsSpeed); - EcsMetersPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MetersPerSecond" }), - .quantity = EcsSpeed, - .base = EcsMeters, - .over = EcsSeconds }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMetersPerSecond, - .kind = EcsF32 - }); - EcsKiloMetersPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KiloMetersPerSecond" }), - .quantity = EcsSpeed, - .base = EcsKiloMeters, - .over = EcsSeconds }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKiloMetersPerSecond, - .kind = EcsF32 - }); - EcsKiloMetersPerHour = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KiloMetersPerHour" }), - .quantity = EcsSpeed, - .base = EcsKiloMeters, - .over = EcsHours }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKiloMetersPerHour, - .kind = EcsF32 - }); - EcsMilesPerHour = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MilesPerHour" }), - .quantity = EcsSpeed, - .base = EcsMiles, - .over = EcsHours }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMilesPerHour, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); - - /* Acceleration */ + /* Add flags to incoming matching add edges */ + if (flags == EcsTableHasOnAdd) { + ecs_graph_edge_hdr_t *next, *cur = node_refs->next; + if (cur) { + do { + ecs_graph_edge_t *edge = (ecs_graph_edge_t*)cur; + if ((id == EcsAny) || ecs_id_match(edge->id, id)) { + if (!edge->diff) { + edge->diff = flecs_bcalloc(&world->allocators.table_diff); + edge->diff->added.array = flecs_walloc_t(world, ecs_id_t); + edge->diff->added.count = 1; + edge->diff->added.array[0] = edge->id; + } + edge->diff->added_flags |= EcsTableHasOnAdd; + } + next = cur->next; + } while ((cur = next)); + } + } - EcsAcceleration = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Acceleration" }), - .base = EcsMetersPerSecond, - .over = EcsSeconds }); - ecs_quantity_init(world, &(ecs_entity_desc_t){ - .id = EcsAcceleration - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsAcceleration, - .kind = EcsF32 - }); + /* Add flags to outgoing matching remove edges */ + if (flags == EcsTableHasOnRemove) { + ecs_map_iter_t it = ecs_map_iter(table->node.remove.hi); + while (ecs_map_next(&it)) { + ecs_id_t edge_id = ecs_map_key(&it); + if ((id == EcsAny) || ecs_id_match(edge_id, id)) { + ecs_graph_edge_t *edge = ecs_map_ptr(&it); + if (!edge->diff) { + edge->diff = flecs_bcalloc(&world->allocators.table_diff); + edge->diff->removed.array = flecs_walloc_t(world, ecs_id_t); + edge->diff->removed.count = 1; + edge->diff->removed.array[0] = edge->id; + } + edge->diff->removed_flags |= EcsTableHasOnRemove; + } + } + } +} - /* Temperature units */ +void flecs_table_clear_edges( + ecs_world_t *world, + ecs_table_t *table) +{ + (void)world; + flecs_poly_assert(world, ecs_world_t); - EcsTemperature = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Temperature" }); - prev_scope = ecs_set_scope(world, EcsTemperature); - EcsKelvin = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Kelvin" }), - .quantity = EcsTemperature, - .symbol = "K" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKelvin, - .kind = EcsF32 - }); - EcsCelsius = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Celsius" }), - .quantity = EcsTemperature, - .symbol = "°C" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsCelsius, - .kind = EcsF32 - }); - EcsFahrenheit = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Fahrenheit" }), - .quantity = EcsTemperature, - .symbol = "F" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsFahrenheit, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); + ecs_log_push_1(); - /* Data units */ + ecs_map_iter_t it; + ecs_graph_node_t *table_node = &table->node; + ecs_graph_edges_t *node_add = &table_node->add; + ecs_graph_edges_t *node_remove = &table_node->remove; + ecs_map_t *add_hi = node_add->hi; + ecs_map_t *remove_hi = node_remove->hi; + ecs_graph_edge_hdr_t *node_refs = &table_node->refs; - EcsData = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Data" }); - prev_scope = ecs_set_scope(world, EcsData); + /* Cleanup outgoing edges */ + it = ecs_map_iter(add_hi); + while (ecs_map_next(&it)) { + flecs_table_disconnect_edge(world, ecs_map_key(&it), ecs_map_ptr(&it)); + } - EcsBits = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Bits" }), - .quantity = EcsData, - .symbol = "bit" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsBits, - .kind = EcsU64 - }); + it = ecs_map_iter(remove_hi); + while (ecs_map_next(&it)) { + flecs_table_disconnect_edge(world, ecs_map_key(&it), ecs_map_ptr(&it)); + } - EcsKiloBits = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KiloBits" }), - .quantity = EcsData, - .base = EcsBits, - .prefix = EcsKilo }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKiloBits, - .kind = EcsU64 - }); + /* Cleanup incoming add edges */ + ecs_graph_edge_hdr_t *next, *cur = node_refs->next; + if (cur) { + do { + ecs_graph_edge_t *edge = (ecs_graph_edge_t*)cur; + ecs_assert(edge->to == table, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edge->from != NULL, ECS_INTERNAL_ERROR, NULL); + next = cur->next; + flecs_table_remove_edge(world, &edge->from->node.add, edge->id, edge); + } while ((cur = next)); + } - EcsMegaBits = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MegaBits" }), - .quantity = EcsData, - .base = EcsBits, - .prefix = EcsMega }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMegaBits, - .kind = EcsU64 - }); + /* Cleanup incoming remove edges */ + cur = node_refs->prev; + if (cur) { + do { + ecs_graph_edge_t *edge = (ecs_graph_edge_t*)cur; + ecs_assert(edge->to == table, ECS_INTERNAL_ERROR, NULL); + ecs_assert(edge->from != NULL, ECS_INTERNAL_ERROR, NULL); + next = cur->prev; + flecs_table_remove_edge(world, &edge->from->node.remove, edge->id, edge); + } while ((cur = next)); + } - EcsGigaBits = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "GigaBits" }), - .quantity = EcsData, - .base = EcsBits, - .prefix = EcsGiga }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsGigaBits, - .kind = EcsU64 - }); + if (node_add->lo) { + flecs_bfree(&world->allocators.graph_edge_lo, node_add->lo); + } + if (node_remove->lo) { + flecs_bfree(&world->allocators.graph_edge_lo, node_remove->lo); + } - EcsBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Bytes" }), - .quantity = EcsData, - .symbol = "B", - .base = EcsBits, - .translation = { .factor = 8, .power = 1 } }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsBytes, - .kind = EcsU64 - }); + ecs_map_fini(add_hi); + ecs_map_fini(remove_hi); + flecs_free_t(&world->allocator, ecs_map_t, add_hi); + flecs_free_t(&world->allocator, ecs_map_t, remove_hi); + table_node->add.lo = NULL; + table_node->remove.lo = NULL; + table_node->add.hi = NULL; + table_node->remove.hi = NULL; - EcsKiloBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KiloBytes" }), - .quantity = EcsData, - .base = EcsBytes, - .prefix = EcsKilo }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKiloBytes, - .kind = EcsU64 - }); + ecs_log_pop_1(); +} - EcsMegaBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MegaBytes" }), - .quantity = EcsData, - .base = EcsBytes, - .prefix = EcsMega }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMegaBytes, - .kind = EcsU64 - }); +/* Public convenience functions for traversing table graph */ +ecs_table_t* ecs_table_add_id( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id) +{ + ecs_table_diff_t diff; + return flecs_table_traverse_add(world, table, &id, &diff); +} - EcsGigaBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "GigaBytes" }), - .quantity = EcsData, - .base = EcsBytes, - .prefix = EcsGiga }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsGigaBytes, - .kind = EcsU64 - }); +ecs_table_t* ecs_table_remove_id( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id) +{ + ecs_table_diff_t diff; + return flecs_table_traverse_remove(world, table, &id, &diff); +} + +ecs_table_t* ecs_table_find( + ecs_world_t *world, + const ecs_id_t *ids, + int32_t id_count) +{ + ecs_type_t type = { + .array = ECS_CONST_CAST(ecs_id_t*, ids), + .count = id_count + }; + return flecs_table_ensure(world, &type, false, NULL); +} + +/** + * @file addons/json/deserialize.c + * @brief Deserialize JSON strings into (component) values. + */ + +/** + * @file addons/json/json.h + * @brief Internal functions for JSON addon. + */ + +#ifndef FLECS_JSON_PRIVATE_H +#define FLECS_JSON_PRIVATE_H + + +#ifdef FLECS_JSON + +/* Deserialize from JSON */ +typedef enum ecs_json_token_t { + JsonObjectOpen, + JsonObjectClose, + JsonArrayOpen, + JsonArrayClose, + JsonColon, + JsonComma, + JsonNumber, + JsonString, + JsonBoolean, + JsonTrue, + JsonFalse, + JsonNull, + JsonLargeInt, + JsonLargeString, + JsonInvalid +} ecs_json_token_t; + +typedef struct ecs_json_value_ser_ctx_t { + ecs_entity_t type; + const EcsTypeSerializer *ser; + char *id_label; + bool initialized; +} ecs_json_value_ser_ctx_t; + +/* Cached data for serializer */ +typedef struct ecs_json_ser_ctx_t { + ecs_id_record_t *idr_doc_name; + ecs_id_record_t *idr_doc_color; + ecs_json_value_ser_ctx_t value_ctx[64]; +} ecs_json_ser_ctx_t; + +typedef struct ecs_json_this_data_t { + const ecs_entity_t *ids; + const EcsIdentifier *names; + const EcsDocDescription *label; + const EcsDocDescription *brief; + const EcsDocDescription *detail; + const EcsDocDescription *color; + const EcsDocDescription *link; + bool has_alerts; +} ecs_json_this_data_t; + +const char* flecs_json_parse( + const char *json, + ecs_json_token_t *token_kind, + char *token); + +const char* flecs_json_parse_large_string( + const char *json, + ecs_strbuf_t *buf); + +const char* flecs_json_parse_next_member( + const char *json, + char *token, + ecs_json_token_t *token_kind, + const ecs_from_json_desc_t *desc); + +const char* flecs_json_expect( + const char *json, + ecs_json_token_t token_kind, + char *token, + const ecs_from_json_desc_t *desc); + +const char* flecs_json_expect_string( + const char *json, + char *token, + char **out, + const ecs_from_json_desc_t *desc); - EcsKibiBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KibiBytes" }), - .quantity = EcsData, - .base = EcsBytes, - .prefix = EcsKibi }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKibiBytes, - .kind = EcsU64 - }); +const char* flecs_json_expect_member( + const char *json, + char *token, + const ecs_from_json_desc_t *desc); - EcsMebiBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MebiBytes" }), - .quantity = EcsData, - .base = EcsBytes, - .prefix = EcsMebi }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMebiBytes, - .kind = EcsU64 - }); +const char* flecs_json_expect_next_member( + const char *json, + char *token, + const ecs_from_json_desc_t *desc); - EcsGibiBytes = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "GibiBytes" }), - .quantity = EcsData, - .base = EcsBytes, - .prefix = EcsGibi }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsGibiBytes, - .kind = EcsU64 - }); +const char* flecs_json_expect_member_name( + const char *json, + char *token, + const char *member_name, + const ecs_from_json_desc_t *desc); - ecs_set_scope(world, prev_scope); +const char* flecs_json_skip_object( + const char *json, + char *token, + const ecs_from_json_desc_t *desc); - /* DataRate units */ +const char* flecs_json_skip_array( + const char *json, + char *token, + const ecs_from_json_desc_t *desc); - EcsDataRate = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "DataRate" }); - prev_scope = ecs_set_scope(world, EcsDataRate); +/* Serialize to JSON */ +void flecs_json_next( + ecs_strbuf_t *buf); - EcsBitsPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "BitsPerSecond" }), - .quantity = EcsDataRate, - .base = EcsBits, - .over = EcsSeconds }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsBitsPerSecond, - .kind = EcsU64 - }); +void flecs_json_number( + ecs_strbuf_t *buf, + double value); - EcsKiloBitsPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KiloBitsPerSecond" }), - .quantity = EcsDataRate, - .base = EcsKiloBits, - .over = EcsSeconds - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKiloBitsPerSecond, - .kind = EcsU64 - }); +void flecs_json_u32( + ecs_strbuf_t *buf, + uint32_t value); - EcsMegaBitsPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MegaBitsPerSecond" }), - .quantity = EcsDataRate, - .base = EcsMegaBits, - .over = EcsSeconds - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMegaBitsPerSecond, - .kind = EcsU64 - }); +void flecs_json_true( + ecs_strbuf_t *buf); - EcsGigaBitsPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "GigaBitsPerSecond" }), - .quantity = EcsDataRate, - .base = EcsGigaBits, - .over = EcsSeconds - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsGigaBitsPerSecond, - .kind = EcsU64 - }); +void flecs_json_false( + ecs_strbuf_t *buf); - EcsBytesPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "BytesPerSecond" }), - .quantity = EcsDataRate, - .base = EcsBytes, - .over = EcsSeconds }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsBytesPerSecond, - .kind = EcsU64 - }); +void flecs_json_bool( + ecs_strbuf_t *buf, + bool value); - EcsKiloBytesPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KiloBytesPerSecond" }), - .quantity = EcsDataRate, - .base = EcsKiloBytes, - .over = EcsSeconds - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKiloBytesPerSecond, - .kind = EcsU64 - }); +void flecs_json_null( + ecs_strbuf_t *buf); - EcsMegaBytesPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MegaBytesPerSecond" }), - .quantity = EcsDataRate, - .base = EcsMegaBytes, - .over = EcsSeconds - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMegaBytesPerSecond, - .kind = EcsU64 - }); +void flecs_json_array_push( + ecs_strbuf_t *buf); - EcsGigaBytesPerSecond = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "GigaBytesPerSecond" }), - .quantity = EcsDataRate, - .base = EcsGigaBytes, - .over = EcsSeconds - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsGigaBytesPerSecond, - .kind = EcsU64 - }); +void flecs_json_array_pop( + ecs_strbuf_t *buf); - ecs_set_scope(world, prev_scope); +void flecs_json_object_push( + ecs_strbuf_t *buf); - /* Percentage */ +void flecs_json_object_pop( + ecs_strbuf_t *buf); - EcsPercentage = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Percentage" }); - ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = EcsPercentage, - .symbol = "%" - }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsPercentage, - .kind = EcsF32 - }); +void flecs_json_string( + ecs_strbuf_t *buf, + const char *value); - /* Angles */ +void flecs_json_string_escape( + ecs_strbuf_t *buf, + const char *value); - EcsAngle = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Angle" }); - prev_scope = ecs_set_scope(world, EcsAngle); - EcsRadians = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Radians" }), - .quantity = EcsAngle, - .symbol = "rad" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsRadians, - .kind = EcsF32 - }); +void flecs_json_member( + ecs_strbuf_t *buf, + const char *name); - EcsDegrees = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Degrees" }), - .quantity = EcsAngle, - .symbol = "°" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsDegrees, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); +void flecs_json_membern( + ecs_strbuf_t *buf, + const char *name, + int32_t name_len); - /* DeciBel */ +#define flecs_json_memberl(buf, name)\ + flecs_json_membern(buf, name, sizeof(name) - 1) - EcsBel = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Bel" }), - .symbol = "B" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsBel, - .kind = EcsF32 - }); - EcsDeciBel = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "DeciBel" }), - .prefix = EcsDeci, - .base = EcsBel }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsDeciBel, - .kind = EcsF32 - }); +void flecs_json_path( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e); - EcsFrequency = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Frequency" }); - prev_scope = ecs_set_scope(world, EcsFrequency); +void flecs_json_label( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e); - EcsHertz = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Hertz" }), - .quantity = EcsFrequency, - .symbol = "Hz" }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsHertz, - .kind = EcsF32 - }); +void flecs_json_path_or_label( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e, + bool path); - EcsKiloHertz = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "KiloHertz" }), - .prefix = EcsKilo, - .base = EcsHertz }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsKiloHertz, - .kind = EcsF32 - }); +void flecs_json_color( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e); - EcsMegaHertz = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "MegaHertz" }), - .prefix = EcsMega, - .base = EcsHertz }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsMegaHertz, - .kind = EcsF32 - }); +void flecs_json_id( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_id_t id); - EcsGigaHertz = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "GigaHertz" }), - .prefix = EcsGiga, - .base = EcsHertz }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsGigaHertz, - .kind = EcsF32 - }); - ecs_set_scope(world, prev_scope); +void flecs_json_id_member( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_id_t id, + bool fullpath); - EcsUri = ecs_quantity_init(world, &(ecs_entity_desc_t){ - .name = "Uri" }); - prev_scope = ecs_set_scope(world, EcsUri); +ecs_primitive_kind_t flecs_json_op_to_primitive_kind( + ecs_meta_type_op_kind_t kind); - EcsUriHyperlink = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Hyperlink" }), - .quantity = EcsUri }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsUriHyperlink, - .kind = EcsString - }); +int flecs_json_serialize_iter_result( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + ecs_json_ser_ctx_t *ser_ctx); - EcsUriImage = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "Image" }), - .quantity = EcsUri }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsUriImage, - .kind = EcsString - }); +void flecs_json_serialize_field( + const ecs_world_t *world, + const ecs_iter_t *it, + const ecs_query_t *q, + int field, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ctx); - EcsUriFile = ecs_unit_init(world, &(ecs_unit_desc_t){ - .entity = ecs_entity(world, { .name = "File" }), - .quantity = EcsUri }); - ecs_primitive_init(world, &(ecs_primitive_desc_t){ - .entity = EcsUriFile, - .kind = EcsString - }); - ecs_set_scope(world, prev_scope); +void flecs_json_serialize_query( + const ecs_world_t *world, + const ecs_query_t *q, + ecs_strbuf_t *buf); - /* Documentation */ -#ifdef FLECS_DOC - ECS_IMPORT(world, FlecsDoc); +int flecs_json_ser_type( + const ecs_world_t *world, + const ecs_vec_t *ser, + const void *base, + ecs_strbuf_t *str); - ecs_doc_set_brief(world, EcsDuration, - "Time amount (e.g. \"20 seconds\", \"2 hours\")"); - ecs_doc_set_brief(world, EcsSeconds, "Time amount in seconds"); - ecs_doc_set_brief(world, EcsMinutes, "60 seconds"); - ecs_doc_set_brief(world, EcsHours, "60 minutes"); - ecs_doc_set_brief(world, EcsDays, "24 hours"); +int flecs_json_serialize_iter_result_fields( + const ecs_world_t *world, + const ecs_iter_t *it, + int32_t i, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + ecs_json_ser_ctx_t *ser_ctx); - ecs_doc_set_brief(world, EcsTime, - "Time passed since an epoch (e.g. \"5pm\", \"March 3rd 2022\")"); - ecs_doc_set_brief(world, EcsDate, - "Seconds passed since January 1st 1970"); +bool flecs_json_serialize_get_value_ctx( + const ecs_world_t *world, + ecs_id_t id, + ecs_json_value_ser_ctx_t *ctx, + const ecs_iter_to_json_desc_t *desc); - ecs_doc_set_brief(world, EcsMass, "Units of mass (e.g. \"5 kilograms\")"); +int flecs_json_serialize_iter_result_table( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + int32_t count, + bool has_this, + const char *parent_path, + const ecs_json_this_data_t *this_data); - ecs_doc_set_brief(world, EcsElectricCurrent, - "Units of electrical current (e.g. \"2 ampere\")"); +int flecs_json_serialize_iter_result_query( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ser_ctx, + const ecs_iter_to_json_desc_t *desc, + int32_t count, + bool has_this, + const char *parent_path, + const ecs_json_this_data_t *this_data); - ecs_doc_set_brief(world, EcsAmount, - "Units of amount of substance (e.g. \"2 mole\")"); +void flecs_json_serialize_iter_this( + const ecs_iter_t *it, + const char *parent_path, + const ecs_json_this_data_t *this_data, + int32_t row, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc); - ecs_doc_set_brief(world, EcsLuminousIntensity, - "Units of luminous intensity (e.g. \"1 candela\")"); +bool flecs_json_serialize_vars( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc); - ecs_doc_set_brief(world, EcsForce, "Units of force (e.g. \"10 newton\")"); +int flecs_json_serialize_matches( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity); - ecs_doc_set_brief(world, EcsLength, - "Units of length (e.g. \"5 meters\", \"20 miles\")"); +int flecs_json_serialize_refs( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity, + ecs_entity_t relationship); - ecs_doc_set_brief(world, EcsPressure, - "Units of pressure (e.g. \"1 bar\", \"1000 pascal\")"); +int flecs_json_serialize_alerts( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity); - ecs_doc_set_brief(world, EcsSpeed, - "Units of movement (e.g. \"5 meters/second\")"); +bool flecs_json_is_builtin( + ecs_id_t id); - ecs_doc_set_brief(world, EcsAcceleration, - "Unit of speed increase (e.g. \"5 meters/second/second\")"); +#endif - ecs_doc_set_brief(world, EcsTemperature, - "Units of temperature (e.g. \"5 degrees Celsius\")"); +#endif /* FLECS_JSON_PRIVATE_H */ - ecs_doc_set_brief(world, EcsData, - "Units of information (e.g. \"8 bits\", \"100 megabytes\")"); +#include - ecs_doc_set_brief(world, EcsDataRate, - "Units of data transmission (e.g. \"100 megabits/second\")"); +#ifdef FLECS_JSON - ecs_doc_set_brief(world, EcsAngle, - "Units of rotation (e.g. \"1.2 radians\", \"180 degrees\")"); +typedef struct { + ecs_allocator_t *a; + ecs_vec_t table_type; + ecs_vec_t remove_ids; + ecs_map_t anonymous_ids; + ecs_map_t missing_reflection; + const char *expr; +} ecs_from_json_ctx_t; - ecs_doc_set_brief(world, EcsFrequency, - "The number of occurrences of a repeating event per unit of time."); +static +void flecs_from_json_ctx_init( + ecs_allocator_t *a, + ecs_from_json_ctx_t *ctx) +{ + ctx->a = a; + ecs_vec_init_t(a, &ctx->table_type, ecs_id_t, 0); + ecs_vec_init_t(a, &ctx->remove_ids, ecs_id_t, 0); + ecs_map_init(&ctx->anonymous_ids, a); + ecs_map_init(&ctx->missing_reflection, a); +} - ecs_doc_set_brief(world, EcsUri, "Universal resource identifier."); -#endif +static +void flecs_from_json_ctx_fini( + ecs_from_json_ctx_t *ctx) +{ + ecs_vec_fini_t(ctx->a, &ctx->table_type, ecs_record_t*); + ecs_vec_fini_t(ctx->a, &ctx->remove_ids, ecs_record_t*); + ecs_map_fini(&ctx->anonymous_ids); + ecs_map_fini(&ctx->missing_reflection); } -#endif +static +ecs_entity_t flecs_json_new_id( + ecs_world_t *world, + ecs_entity_t ser_id) +{ + /* Try to honor low id requirements */ + if (ser_id < FLECS_HI_COMPONENT_ID) { + return ecs_new_low_id(world); + } else { + return ecs_new(world); + } +} -/** - * @file datastructures/allocator.c - * @brief Allocator for any size. - * - * Allocators create a block allocator for each requested size. - */ +static +void flecs_json_missing_reflection( + ecs_world_t *world, + ecs_id_t id, + const char *json, + ecs_from_json_ctx_t *ctx, + const ecs_from_json_desc_t *desc) +{ + if (!desc->strict || ecs_map_get(&ctx->missing_reflection, id)) { + return; + } + /* Don't spam log when multiple values of a type can't be deserialized */ + ecs_map_ensure(&ctx->missing_reflection, id); + + char *id_str = ecs_id_str(world, id); + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "missing reflection for '%s'", id_str); + ecs_os_free(id_str); +} static -ecs_size_t flecs_allocator_size( - ecs_size_t size) +ecs_entity_t flecs_json_lookup( + ecs_world_t *world, + ecs_entity_t parent, + const char *name, + const ecs_from_json_desc_t *desc) { - return ECS_ALIGN(size, 16); + ecs_entity_t scope = 0; + if (parent) { + scope = ecs_set_scope(world, parent); + } + + ecs_entity_t result = desc->lookup_action(world, name, desc->lookup_ctx); + if (parent) { + ecs_set_scope(world, scope); + } + + return result; } static -ecs_size_t flecs_allocator_size_hash( - ecs_size_t size) +void flecs_json_mark_reserved( + ecs_map_t *anonymous_ids, + ecs_entity_t e) { - return size >> 4; + ecs_entity_t *reserved = ecs_map_ensure(anonymous_ids, e); + ecs_assert(reserved[0] == 0, ECS_INTERNAL_ERROR, NULL); + reserved[0] = 0; } -void flecs_allocator_init( - ecs_allocator_t *a) +static +ecs_entity_t flecs_json_ensure_entity( + ecs_world_t *world, + const char *name, + ecs_map_t *anonymous_ids) { - flecs_ballocator_init_n(&a->chunks, ecs_block_allocator_t, - FLECS_SPARSE_PAGE_SIZE); - flecs_sparse_init_t(&a->sizes, NULL, &a->chunks, ecs_block_allocator_t); + ecs_entity_t e = 0; + + if (flecs_name_is_id(name)) { + /* Anonymous entity, find or create mapping to new id */ + ecs_entity_t ser_id = flecs_ito(ecs_entity_t, atoll(&name[1])); + ecs_entity_t *deser_id = ecs_map_get(anonymous_ids, ser_id); + if (deser_id) { + if (!deser_id[0]) { + /* Id is already issued by deserializer, create new id */ + deser_id[0] = flecs_json_new_id(world, ser_id); + + /* Mark new id as reserved */ + flecs_json_mark_reserved(anonymous_ids, deser_id[0]); + } else { + /* Id mapping exists */ + } + } else { + /* Id has not yet been issued by deserializer, which means it's safe + * to use. This allows the deserializer to bind to existing + * anonymous ids, as they will never be reissued. */ + deser_id = ecs_map_ensure(anonymous_ids, ser_id); + if (!ecs_exists(world, ser_id) || + (ecs_is_alive(world, ser_id) && !ecs_get_name(world, ser_id))) + { + /* Only use existing id if it's alive or doesn't exist yet. The + * id could have been recycled for another entity + * Also don't use existing id if the existing entity is not + * anonymous. */ + deser_id[0] = ser_id; + ecs_make_alive(world, ser_id); + } else { + /* If id exists and is not alive, create a new id */ + deser_id[0] = flecs_json_new_id(world, ser_id); + + /* Mark new id as reserved */ + flecs_json_mark_reserved(anonymous_ids, deser_id[0]); + } + } + + e = deser_id[0]; + } else { + e = ecs_lookup_path_w_sep(world, 0, name, ".", NULL, false); + if (!e) { + e = ecs_entity(world, { .name = name }); + flecs_json_mark_reserved(anonymous_ids, e); + } + } + + return e; } -void flecs_allocator_fini( - ecs_allocator_t *a) +static +bool flecs_json_add_id_to_type( + ecs_id_t id) { - int32_t i = 0, count = flecs_sparse_count(&a->sizes); - for (i = 0; i < count; i ++) { - ecs_block_allocator_t *ba = flecs_sparse_get_dense_t( - &a->sizes, ecs_block_allocator_t, i); - flecs_ballocator_fini(ba); + if (id == ecs_pair_t(EcsIdentifier, EcsName)) { + return false; } - flecs_sparse_fini(&a->sizes); - flecs_ballocator_fini(&a->chunks); + if (ECS_IS_PAIR(id) && ECS_PAIR_FIRST(id) == EcsChildOf) { + return false; + } + return true; } -ecs_block_allocator_t* flecs_allocator_get( - ecs_allocator_t *a, - ecs_size_t size) +static +const char* flecs_json_deser_tags( + ecs_world_t *world, + ecs_entity_t e, + const char *json, + const ecs_from_json_desc_t *desc, + ecs_from_json_ctx_t *ctx) { - ecs_assert(size >= 0, ECS_INTERNAL_ERROR, NULL); - if (!size) { - return NULL; - } + ecs_json_token_t token_kind; + char token[ECS_MAX_TOKEN_SIZE]; - ecs_assert(a != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(size <= flecs_allocator_size(size), ECS_INTERNAL_ERROR, NULL); - size = flecs_allocator_size(size); - ecs_size_t hash = flecs_allocator_size_hash(size); - ecs_block_allocator_t *result = flecs_sparse_get_any_t(&a->sizes, - ecs_block_allocator_t, (uint32_t)hash); + const char *expr = ctx->expr, *lah; - if (!result) { - result = flecs_sparse_ensure_fast_t(&a->sizes, - ecs_block_allocator_t, (uint32_t)hash); - flecs_ballocator_init(result, size); + json = flecs_json_expect(json, JsonArrayOpen, token, desc); + if (!json) { + goto error; } - ecs_assert(result->data_size == size, ECS_INTERNAL_ERROR, NULL); + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonArrayClose) { + json = lah; + goto end; + } - return result; -} + do { + char *str = NULL; + json = flecs_json_expect_string(json, token, &str, desc); + if (!json) { + goto error; + } -char* flecs_strdup( - ecs_allocator_t *a, - const char* str) -{ - ecs_size_t len = ecs_os_strlen(str); - char *result = flecs_alloc_n(a, char, len + 1); - ecs_os_memcpy(result, str, len + 1); - return result; -} + ecs_entity_t tag = flecs_json_lookup(world, 0, str, desc); + if (flecs_json_add_id_to_type(tag)) { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = tag; + } -void flecs_strfree( - ecs_allocator_t *a, - char* str) -{ - ecs_size_t len = ecs_os_strlen(str); - flecs_free_n(a, char, len + 1, str); -} + ecs_add_id(world, e, tag); -void* flecs_dup( - ecs_allocator_t *a, - ecs_size_t size, - const void *src) -{ - ecs_block_allocator_t *ba = flecs_allocator_get(a, size); - if (ba) { - void *dst = flecs_balloc(ba); - ecs_os_memcpy(dst, src, size); - return dst; - } else { - return NULL; + if (str != token) { + ecs_os_free(str); + } + + json = flecs_json_parse(json, &token_kind, token); + if (token_kind != JsonComma) { + break; + } + } while (true); + + if (token_kind != JsonArrayClose) { + ecs_parser_error(NULL, expr, json - expr, "expected }"); + goto error; } -} -/** - * @file datastructures/bitset.c - * @brief Bitset data structure. - * - * Simple bitset implementation. The bitset allows for storage of arbitrary - * numbers of bits. - */ +end: + return json; +error: + return NULL; +} static -void ensure( - ecs_bitset_t *bs, - ecs_size_t size) +const char* flecs_json_deser_pairs( + ecs_world_t *world, + ecs_entity_t e, + const char *json, + const ecs_from_json_desc_t *desc, + ecs_from_json_ctx_t *ctx) { - if (!bs->size) { - int32_t new_size = ((size - 1) / 64 + 1) * ECS_SIZEOF(uint64_t); - bs->size = ((size - 1) / 64 + 1) * 64; - bs->data = ecs_os_calloc(new_size); - } else if (size > bs->size) { - int32_t prev_size = ((bs->size - 1) / 64 + 1) * ECS_SIZEOF(uint64_t); - bs->size = ((size - 1) / 64 + 1) * 64; - int32_t new_size = ((size - 1) / 64 + 1) * ECS_SIZEOF(uint64_t); - bs->data = ecs_os_realloc(bs->data, new_size); - ecs_os_memset(ECS_OFFSET(bs->data, prev_size), 0, new_size - prev_size); - } -} + ecs_json_token_t token_kind; + char token[ECS_MAX_TOKEN_SIZE]; -void flecs_bitset_init( - ecs_bitset_t* bs) -{ - bs->size = 0; - bs->count = 0; - bs->data = NULL; -} + const char *expr = ctx->expr, *lah; -void flecs_bitset_ensure( - ecs_bitset_t *bs, - int32_t count) -{ - if (count > bs->count) { - bs->count = count; - ensure(bs, count); + json = flecs_json_expect(json, JsonObjectOpen, token, desc); + if (!json) { + goto error; } -} -void flecs_bitset_fini( - ecs_bitset_t *bs) -{ - ecs_os_free(bs->data); - bs->data = NULL; - bs->count = 0; -} - -void flecs_bitset_addn( - ecs_bitset_t *bs, - int32_t count) -{ - int32_t elem = bs->count += count; - ensure(bs, elem); -} + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonObjectClose) { + json = lah; + goto end; + } -void flecs_bitset_set( - ecs_bitset_t *bs, - int32_t elem, - bool value) -{ - ecs_check(elem < bs->count, ECS_INVALID_PARAMETER, NULL); - uint32_t hi = ((uint32_t)elem) >> 6; - uint32_t lo = ((uint32_t)elem) & 0x3F; - uint64_t v = bs->data[hi]; - bs->data[hi] = (v & ~((uint64_t)1 << lo)) | ((uint64_t)value << lo); -error: - return; -} + do { + json = flecs_json_expect_member(json, token, desc); + if (!json) { + goto error; + } -bool flecs_bitset_get( - const ecs_bitset_t *bs, - int32_t elem) -{ - ecs_check(elem < bs->count, ECS_INVALID_PARAMETER, NULL); - return !!(bs->data[elem >> 6] & ((uint64_t)1 << ((uint64_t)elem & 0x3F))); -error: - return false; -} + ecs_entity_t rel = flecs_json_lookup(world, 0, token, desc); -int32_t flecs_bitset_count( - const ecs_bitset_t *bs) -{ - return bs->count; -} + bool multiple_targets = false; -void flecs_bitset_remove( - ecs_bitset_t *bs, - int32_t elem) -{ - ecs_check(elem < bs->count, ECS_INVALID_PARAMETER, NULL); - int32_t last = bs->count - 1; - bool last_value = flecs_bitset_get(bs, last); - flecs_bitset_set(bs, elem, last_value); - flecs_bitset_set(bs, last, 0); - bs->count --; -error: - return; -} + do { + json = flecs_json_parse(json, &token_kind, token); + + if (token_kind == JsonString) { + ecs_entity_t tgt = flecs_json_lookup(world, 0, token, desc); + ecs_id_t id = ecs_pair(rel, tgt); + ecs_add_id(world, e, id); + if (flecs_json_add_id_to_type(id)) { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = id; + } + } else if (token_kind == JsonLargeString) { + ecs_strbuf_t large_token = ECS_STRBUF_INIT; + json = flecs_json_parse_large_string(json, &large_token); + if (!json) { + break; + } -void flecs_bitset_swap( - ecs_bitset_t *bs, - int32_t elem_a, - int32_t elem_b) -{ - ecs_check(elem_a < bs->count, ECS_INVALID_PARAMETER, NULL); - ecs_check(elem_b < bs->count, ECS_INVALID_PARAMETER, NULL); + char *str = ecs_strbuf_get(&large_token); + ecs_entity_t tgt = flecs_json_lookup(world, 0, str, desc); + ecs_os_free(str); + ecs_id_t id = ecs_pair(rel, tgt); + ecs_add_id(world, e, id); + if (flecs_json_add_id_to_type(id)) { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = id; + } + } else if (token_kind == JsonArrayOpen) { + if (multiple_targets) { + ecs_parser_error(NULL, expr, json - expr, + "expected string"); + goto error; + } - bool a = flecs_bitset_get(bs, elem_a); - bool b = flecs_bitset_get(bs, elem_b); - flecs_bitset_set(bs, elem_a, b); - flecs_bitset_set(bs, elem_b, a); -error: - return; -} + multiple_targets = true; + } else if (token_kind == JsonArrayClose) { + if (!multiple_targets) { + ecs_parser_error(NULL, expr, json - expr, + "unexpected ]"); + goto error; + } -/** - * @file datastructures/block_allocator.c - * @brief Block allocator. - * - * A block allocator is an allocator for a fixed size that allocates blocks of - * memory with N elements of the requested size. - */ + multiple_targets = false; + } else if (token_kind == JsonComma) { + if (!multiple_targets) { + ecs_parser_error(NULL, expr, json - expr, + "unexpected ,"); + goto error; + } + } else { + ecs_parser_error(NULL, expr, json - expr, + "expected array or string"); + goto error; + } + } while (multiple_targets); + json = flecs_json_parse(json, &token_kind, token); + if (token_kind != JsonComma) { + break; + } + } while (true); -// #ifdef FLECS_SANITIZE -// #define FLECS_MEMSET_UNINITIALIZED -// #endif + if (token_kind != JsonObjectClose) { + ecs_parser_error(NULL, expr, json - expr, "expected }"); + goto error; + } -int64_t ecs_block_allocator_alloc_count = 0; -int64_t ecs_block_allocator_free_count = 0; +end: + return json; +error: + return NULL; +} static -ecs_block_allocator_chunk_header_t* flecs_balloc_block( - ecs_block_allocator_t *allocator) +const char* flecs_json_deser_components( + ecs_world_t *world, + ecs_entity_t e, + const char *json, + const ecs_from_json_desc_t *desc, + ecs_from_json_ctx_t *ctx) { - if (!allocator->chunk_size) { - return NULL; - } + ecs_json_token_t token_kind; + char token[ECS_MAX_TOKEN_SIZE]; - ecs_block_allocator_block_t *block = - ecs_os_malloc(ECS_SIZEOF(ecs_block_allocator_block_t) + - allocator->block_size); - ecs_block_allocator_chunk_header_t *first_chunk = ECS_OFFSET(block, - ECS_SIZEOF(ecs_block_allocator_block_t)); + const char *expr = ctx->expr, *lah; - block->memory = first_chunk; - if (!allocator->block_tail) { - ecs_assert(!allocator->block_head, ECS_INTERNAL_ERROR, 0); - block->next = NULL; - allocator->block_head = block; - allocator->block_tail = block; - } else { - block->next = NULL; - allocator->block_tail->next = block; - allocator->block_tail = block; + json = flecs_json_expect(json, JsonObjectOpen, token, desc); + if (!json) { + goto error; } - ecs_block_allocator_chunk_header_t *chunk = first_chunk; - int32_t i, end; - for (i = 0, end = allocator->chunks_per_block - 1; i < end; ++i) { - chunk->next = ECS_OFFSET(chunk, allocator->chunk_size); - chunk = chunk->next; + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonObjectClose) { + json = lah; + goto end; } - ecs_os_linc(&ecs_block_allocator_alloc_count); + do { + json = flecs_json_expect_member(json, token, desc); + if (!json) { + goto error; + } - chunk->next = NULL; - return first_chunk; -} + ecs_id_t id = 0; -void flecs_ballocator_init( - ecs_block_allocator_t *ba, - ecs_size_t size) -{ - ecs_assert(ba != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); - ba->data_size = size; -#ifdef FLECS_SANITIZE - size += ECS_SIZEOF(int64_t); -#endif - ba->chunk_size = ECS_ALIGN(size, 16); - ba->chunks_per_block = ECS_MAX(4096 / ba->chunk_size, 1); - ba->block_size = ba->chunks_per_block * ba->chunk_size; - ba->head = NULL; - ba->block_head = NULL; - ba->block_tail = NULL; -} + if (token[0] != '(') { + id = flecs_json_lookup(world, 0, token, desc); + } else { + char token_buffer[256]; + ecs_term_t term = {0}; + if (!flecs_term_parse(world, NULL, token, &term, token_buffer)) { + goto error; + } -ecs_block_allocator_t* flecs_ballocator_new( - ecs_size_t size) -{ - ecs_block_allocator_t *result = ecs_os_calloc_t(ecs_block_allocator_t); - flecs_ballocator_init(result, size); - return result; -} + ecs_assert(term.first.name != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(term.second.name != NULL, ECS_INTERNAL_ERROR, NULL); -void flecs_ballocator_fini( - ecs_block_allocator_t *ba) -{ - ecs_assert(ba != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t rel = flecs_json_lookup( + world, 0, term.first.name, desc); + ecs_entity_t tgt = flecs_json_lookup( + world, 0, term.second.name, desc); + + id = ecs_pair(rel, tgt); + } -#ifdef FLECS_SANITIZE - ecs_assert(ba->alloc_count == 0, ECS_LEAK_DETECTED, - "(size = %u)", (uint32_t)ba->data_size); -#endif + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind != JsonNull) { + ecs_entity_t type = ecs_get_typeid(world, id); + if (!type) { + flecs_json_missing_reflection(world, id, json, ctx, desc); + if (desc->strict) { + goto error; + } - ecs_block_allocator_block_t *block; - for (block = ba->block_head; block;) { - ecs_block_allocator_block_t *next = block->next; - ecs_os_free(block); - ecs_os_linc(&ecs_block_allocator_free_count); - block = next; + json = flecs_json_skip_object(json + 1, token, desc); + if (!json) { + goto error; + } + } else { + void *ptr = ecs_ensure_id(world, e, id); + + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind != JsonNull) { + const char *next = ecs_ptr_from_json( + world, type, ptr, json, desc); + if (!next) { + flecs_json_missing_reflection( + world, id, json, ctx, desc); + if (desc->strict) { + goto error; + } + + json = flecs_json_skip_object(json + 1, token, desc); + if (!json) { + goto error; + } + } else { + json = next; + ecs_modified_id(world, e, id); + } + } else { + json = lah; + } + } + } else { + ecs_add_id(world, e, id); + json = lah; + } + + /* Don't add ids that have their own fields in serialized data. */ + if (flecs_json_add_id_to_type(id)) { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = id; + } + + json = flecs_json_parse(json, &token_kind, token); + if (token_kind != JsonComma) { + break; + } + } while (true); + + if (token_kind != JsonObjectClose) { + ecs_parser_error(NULL, expr, json - expr, "expected }"); + goto error; } - ba->block_head = NULL; -} -void flecs_ballocator_free( - ecs_block_allocator_t *ba) -{ - flecs_ballocator_fini(ba); - ecs_os_free(ba); +end: + return json; +error: + return NULL; } -void* flecs_balloc( - ecs_block_allocator_t *ba) +static +const char* flecs_entity_from_json( + ecs_world_t *world, + ecs_entity_t e, + const char *json, + const ecs_from_json_desc_t *desc, + ecs_from_json_ctx_t *ctx) { - void *result; -#ifdef FLECS_USE_OS_ALLOC - result = ecs_os_malloc(ba->data_size); -#else + ecs_json_token_t token_kind; + char token[ECS_MAX_TOKEN_SIZE]; - if (!ba) return NULL; + const char *expr = ctx->expr, *lah; - if (!ba->head) { - ba->head = flecs_balloc_block(ba); + ecs_vec_clear(&ctx->table_type); + + ecs_entity_t parent = 0; + + json = flecs_json_expect(json, JsonObjectOpen, token, desc); + if (!json) { + goto error; } - result = ba->head; - ba->head = ba->head->next; + lah = flecs_json_parse(json, &token_kind, token); + if (!lah) { + goto error; + } -#ifdef FLECS_SANITIZE - ecs_assert(ba->alloc_count >= 0, ECS_INTERNAL_ERROR, "corrupted allocator"); - ba->alloc_count ++; - *(int64_t*)result = ba->chunk_size; - result = ECS_OFFSET(result, ECS_SIZEOF(int64_t)); -#endif -#endif + if (token_kind == JsonObjectClose) { + json = lah; + goto end; + } -#ifdef FLECS_MEMSET_UNINITIALIZED - ecs_os_memset(result, 0xAA, ba->data_size); -#endif + json = flecs_json_expect_member(json, token, desc); + if (!json) { + goto error; + } - return result; -} + if (!ecs_os_strcmp(token, "parent")) { + char *str = NULL; + json = flecs_json_expect_string(json, token, &str, desc); + if (!json) { + goto error; + } -void* flecs_bcalloc( - ecs_block_allocator_t *ba) -{ -#ifdef FLECS_USE_OS_ALLOC - return ecs_os_calloc(ba->data_size); -#endif + parent = flecs_json_lookup(world, 0, str, desc); - if (!ba) return NULL; - void *result = flecs_balloc(ba); - ecs_os_memset(result, 0, ba->data_size); - return result; -} + if (e) { + ecs_add_pair(world, e, EcsChildOf, parent); + } -void flecs_bfree( - ecs_block_allocator_t *ba, - void *memory) -{ -#ifdef FLECS_USE_OS_ALLOC - ecs_os_free(memory); - return; -#endif + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = + ecs_pair(EcsChildOf, parent); - if (!ba) { - ecs_assert(memory == NULL, ECS_INTERNAL_ERROR, NULL); - return; + if (str != token) ecs_os_free(str); + + json = flecs_json_parse_next_member(json, token, &token_kind, desc); + if (!json) { + goto error; + } + if (token_kind == JsonObjectClose) { + goto end; + } } - if (memory == NULL) { - return; + + if (!ecs_os_strcmp(token, "name")) { + char *str = NULL; + json = flecs_json_expect_string(json, token, &str, desc); + if (!json) { + goto error; + } + + if (!e) { + e = flecs_json_lookup(world, parent, str, desc); + } else { + ecs_set_name(world, e, str); + } + + if (str[0] != '#') { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = + ecs_pair_t(EcsIdentifier, EcsName); + } + + if (str != token) ecs_os_free(str); + + json = flecs_json_parse_next_member(json, token, &token_kind, desc); + if (!json) { + goto error; + } + if (token_kind == JsonObjectClose) { + goto end; + } + } + + if (!ecs_os_strcmp(token, "id")) { + json = flecs_json_parse(json, &token_kind, token); + if (!json) { + goto error; + } + + uint64_t id; + if (token_kind == JsonNumber || token_kind == JsonLargeInt) { + id = flecs_ito(uint64_t, atoll(token)); + } else { + ecs_parser_error(NULL, expr, json - expr, "expected entity id"); + goto error; + } + + if (!e) { + char name[32]; + ecs_os_snprintf(name, 32, "#%u", (uint32_t)id); + e = flecs_json_lookup(world, 0, name, desc); + } else { + /* If we already have an id, ignore explicit id */ + } + + json = flecs_json_parse_next_member(json, token, &token_kind, desc); + if (!json) { + goto error; + } + if (token_kind == JsonObjectClose) { + goto end; + } } -#ifdef FLECS_SANITIZE - memory = ECS_OFFSET(memory, -ECS_SIZEOF(int64_t)); - if (*(int64_t*)memory != ba->chunk_size) { - ecs_err("chunk %p returned to wrong allocator " - "(chunk = %ub, allocator = %ub)", - memory, *(int64_t*)memory, ba->chunk_size); - ecs_abort(ECS_INTERNAL_ERROR, NULL); + if (!e) { + ecs_parser_error(NULL, expr, json - expr, "failed to create entity"); + return NULL; } - ba->alloc_count --; -#endif - - ecs_block_allocator_chunk_header_t *chunk = memory; - chunk->next = ba->head; - ba->head = chunk; - ecs_assert(ba->alloc_count >= 0, ECS_INTERNAL_ERROR, "corrupted allocator"); -} - -void* flecs_brealloc( - ecs_block_allocator_t *dst, - ecs_block_allocator_t *src, - void *memory) -{ - void *result; -#ifdef FLECS_USE_OS_ALLOC - result = ecs_os_realloc(memory, dst->data_size); -#else - if (dst == src) { - return memory; - } + if (!ecs_os_strcmp(token, "has_alerts")) { + json = flecs_json_expect(json, JsonBoolean, token, desc); + if (!json) { + goto error; + } - result = flecs_balloc(dst); - if (result && src) { - ecs_size_t size = src->data_size; - if (dst->data_size < size) { - size = dst->data_size; + json = flecs_json_parse_next_member(json, token, &token_kind, desc); + if (!json) { + goto error; + } + if (token_kind == JsonObjectClose) { + goto end; } - ecs_os_memcpy(result, memory, size); - } - flecs_bfree(src, memory); -#endif -#ifdef FLECS_MEMSET_UNINITIALIZED - if (dst && src && (dst->data_size > src->data_size)) { - ecs_os_memset(ECS_OFFSET(result, src->data_size), 0xAA, - dst->data_size - src->data_size); - } else if (dst && !src) { - ecs_os_memset(result, 0xAA, dst->data_size); } -#endif - return result; -} + if (!ecs_os_strcmp(token, "tags")) { + json = flecs_json_deser_tags(world, e, json, desc, ctx); + if (!json) { + goto error; + } -void* flecs_bdup( - ecs_block_allocator_t *ba, - void *memory) -{ -#ifdef FLECS_USE_OS_ALLOC - if (memory && ba->chunk_size) { - return ecs_os_memdup(memory, ba->data_size); - } else { - return NULL; - } -#endif + json = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonObjectClose) { + goto end; + } else if (token_kind != JsonComma) { + ecs_parser_error(NULL, expr, json - expr, "expected ','"); + goto error; + } - void *result = flecs_balloc(ba); - if (result) { - ecs_os_memcpy(result, memory, ba->data_size); + json = flecs_json_expect_member(json, token, desc); + if (!json) { + goto error; + } } - return result; -} -// This is free and unencumbered software released into the public domain under The Unlicense (http://unlicense.org/) -// main repo: https://github.com/wangyi-fudan/wyhash -// author: 王一 Wang Yi -// contributors: Reini Urban, Dietrich Epp, Joshua Haberman, Tommy Ettinger, -// Daniel Lemire, Otmar Ertl, cocowalla, leo-yuriev, -// Diego Barrios Romero, paulie-g, dumblob, Yann Collet, ivte-ms, -// hyb, James Z.M. Gao, easyaspi314 (Devin), TheOneric + if (!ecs_os_strcmp(token, "pairs")) { + json = flecs_json_deser_pairs(world, e, json, desc, ctx); + if (!json) { + goto error; + } -/* quick example: - string s="fjsakfdsjkf"; - uint64_t hash=wyhash(s.c_str(), s.size(), 0, wyp_); -*/ + json = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonObjectClose) { + goto end; + } else if (token_kind != JsonComma) { + ecs_parser_error(NULL, expr, json - expr, "expected ','"); + goto error; + } + json = flecs_json_expect_member(json, token, desc); + if (!json) { + goto error; + } + } -#ifndef WYHASH_CONDOM -//protections that produce different results: -//1: normal valid behavior -//2: extra protection against entropy loss (probability=2^-63), aka. "blind multiplication" -#define WYHASH_CONDOM 1 -#endif + if (!ecs_os_strcmp(token, "components")) { + json = flecs_json_deser_components(world, e, json, desc, ctx); + if (!json) { + goto error; + } + } -#ifndef WYHASH_32BIT_MUM -//0: normal version, slow on 32 bit systems -//1: faster on 32 bit systems but produces different results, incompatible with wy2u0k function -#define WYHASH_32BIT_MUM 0 -#endif + json = flecs_json_expect(json, JsonObjectClose, token, desc); + if (!json) { + goto error; + } -//includes -#include -#include -#if defined(_MSC_VER) && defined(_M_X64) - #include - #pragma intrinsic(_umul128) -#endif + ecs_record_t *r = flecs_entities_get(world, e); + ecs_table_t *table = r ? r->table : NULL; + if (table) { + ecs_id_t *ids = ecs_vec_first(&ctx->table_type); + int32_t ids_count = ecs_vec_count(&ctx->table_type); + qsort(ids, flecs_itosize(ids_count), sizeof(ecs_id_t), flecs_id_qsort_cmp); + + ecs_table_t *dst_table = ecs_table_find(world, + ecs_vec_first(&ctx->table_type), ecs_vec_count(&ctx->table_type)); + if (dst_table->type.count == 0) { + dst_table = NULL; + } + + /* Entity had existing components that weren't in the serialized data */ + if (table != dst_table) { + ecs_assert(ecs_get_target(world, e, EcsChildOf, 0) != EcsFlecsCore, + ECS_INVALID_OPERATION, "%s\n[%s] => \n[%s]", + ecs_get_path(world, e), + ecs_table_str(world, table), + ecs_table_str(world, dst_table)); + + if (!dst_table) { + ecs_clear(world, e); + } else { + ecs_vec_clear(&ctx->remove_ids); -//likely and unlikely macros -#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) - #define likely_(x) __builtin_expect(x,1) - #define unlikely_(x) __builtin_expect(x,0) -#else - #define likely_(x) (x) - #define unlikely_(x) (x) -#endif + ecs_type_t *type = &table->type, *dst_type = &dst_table->type; + int32_t i = 0, i_dst = 0; + for (; (i_dst < dst_type->count) && (i < type->count); ) { + ecs_id_t id = type->array[i], dst_id = dst_type->array[i_dst]; -//128bit multiply function -static inline void wymum_(uint64_t *A, uint64_t *B){ -#if(WYHASH_32BIT_MUM) - uint64_t hh=(*A>>32)*(*B>>32), hl=(*A>>32)*(uint32_t)*B, lh=(uint32_t)*A*(*B>>32), ll=(uint64_t)(uint32_t)*A*(uint32_t)*B; - #if(WYHASH_CONDOM>1) - *A^=_wyrot(hl)^hh; *B^=_wyrot(lh)^ll; - #else - *A=_wyrot(hl)^hh; *B=_wyrot(lh)^ll; - #endif -#elif defined(__SIZEOF_INT128__) - __uint128_t r=*A; r*=*B; - #if(WYHASH_CONDOM>1) - *A^=(uint64_t)r; *B^=(uint64_t)(r>>64); - #else - *A=(uint64_t)r; *B=(uint64_t)(r>>64); - #endif -#elif defined(_MSC_VER) && defined(_M_X64) - #if(WYHASH_CONDOM>1) - uint64_t a, b; - a=_umul128(*A,*B,&b); - *A^=a; *B^=b; - #else - *A=_umul128(*A,*B,B); - #endif -#else - uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo; - uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t>32)+(rm1>>32)+c; - #if(WYHASH_CONDOM>1) - *A^=lo; *B^=hi; - #else - *A=lo; *B=hi; - #endif -#endif -} + if (dst_id > id) { + ecs_vec_append_t( + ctx->a, &ctx->remove_ids, ecs_id_t)[0] = id; + } -//multiply and xor mix function, aka MUM -static inline uint64_t wymix_(uint64_t A, uint64_t B){ wymum_(&A,&B); return A^B; } + i_dst += dst_id <= id; + i += dst_id >= id; + } -//endian macros -#ifndef WYHASH_LITTLE_ENDIAN - #if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - #define WYHASH_LITTLE_ENDIAN 1 - #elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) - #define WYHASH_LITTLE_ENDIAN 0 - #else - #warning could not determine endianness! Falling back to little endian. - #define WYHASH_LITTLE_ENDIAN 1 - #endif -#endif + ecs_type_t removed = { + .array = ecs_vec_first(&ctx->remove_ids), + .count = ecs_vec_count(&ctx->remove_ids) + }; -//read functions -#if (WYHASH_LITTLE_ENDIAN) -static inline uint64_t wyr8_(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v;} -static inline uint64_t wyr4_(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v;} -#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) -static inline uint64_t wyr8_(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return __builtin_bswap64(v);} -static inline uint64_t wyr4_(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return __builtin_bswap32(v);} -#elif defined(_MSC_VER) -static inline uint64_t wyr8_(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return _byteswap_uint64(v);} -static inline uint64_t wyr4_(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return _byteswap_ulong(v);} -#else -static inline uint64_t wyr8_(const uint8_t *p) { - uint64_t v; memcpy(&v, p, 8); - return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >> 8) & 0xff000000)| ((v << 8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000)); -} -static inline uint64_t wyr4_(const uint8_t *p) { - uint32_t v; memcpy(&v, p, 4); - return (((v >> 24) & 0xff)| ((v >> 8) & 0xff00)| ((v << 8) & 0xff0000)| ((v << 24) & 0xff000000)); -} -#endif -static inline uint64_t wyr3_(const uint8_t *p, size_t k) { return (((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1];} + ecs_commit(world, e, r, dst_table, NULL, &removed); + } -//wyhash main function -static inline uint64_t wyhash(const void *key, size_t len, uint64_t seed, const uint64_t *secret){ - const uint8_t *p=(const uint8_t *)key; seed^=wymix_(seed^secret[0],secret[1]); uint64_t a, b; - if(likely_(len<=16)){ - if(likely_(len>=4)){ a=(wyr4_(p)<<32)|wyr4_(p+((len>>3)<<2)); b=(wyr4_(p+len-4)<<32)|wyr4_(p+len-4-((len>>3)<<2)); } - else if(likely_(len>0)){ a=wyr3_(p,len); b=0;} - else a=b=0; - } - else{ - size_t i=len; - if(unlikely_(i>48)){ - uint64_t see1=seed, see2=seed; - do{ - seed=wymix_(wyr8_(p)^secret[1],wyr8_(p+8)^seed); - see1=wymix_(wyr8_(p+16)^secret[2],wyr8_(p+24)^see1); - see2=wymix_(wyr8_(p+32)^secret[3],wyr8_(p+40)^see2); - p+=48; i-=48; - }while(likely_(i>48)); - seed^=see1^see2; + ecs_assert(ecs_get_table(world, e) == dst_table, + ECS_INTERNAL_ERROR, NULL); + } } - while(unlikely_(i>16)){ seed=wymix_(wyr8_(p)^secret[1],wyr8_(p+8)^seed); i-=16; p+=16; } - a=wyr8_(p+i-16); b=wyr8_(p+i-8); - } - a^=secret[1]; b^=seed; wymum_(&a,&b); - return wymix_(a^secret[0]^len,b^secret[1]); -} -//the default secret parameters -static const uint64_t wyp_[4] = {0xa0761d6478bd642full, 0xe7037ed1a0b428dbull, 0x8ebc6af09c88c6e3ull, 0x589965cc75374cc3ull}; +end: + return json; +error: + return NULL; +} -uint64_t flecs_hash( - const void *data, - ecs_size_t length) +const char* ecs_entity_from_json( + ecs_world_t *world, + ecs_entity_t e, + const char *json, + const ecs_from_json_desc_t *desc_arg) { - return wyhash(data, flecs_ito(size_t, length), 0, wyp_); -} + ecs_from_json_desc_t desc = {0}; + if (desc_arg) { + desc = *desc_arg; + } -/** - * @file datastructures/hashmap.c - * @brief Hashmap data structure. - * - * The hashmap data structure is built on top of the map data structure. Where - * the map data structure can only work with 64bit key values, the hashmap can - * hash keys of any size, and handles collisions between hashes. - */ + desc.expr = json; + ecs_allocator_t *a = &world->allocator; + ecs_from_json_ctx_t ctx; + flecs_from_json_ctx_init(a, &ctx); + ctx.expr = json; -static -int32_t flecs_hashmap_find_key( - const ecs_hashmap_t *map, - ecs_vec_t *keys, - ecs_size_t key_size, - const void *key) -{ - int32_t i, count = ecs_vec_count(keys); - void *key_array = ecs_vec_first(keys); - for (i = 0; i < count; i ++) { - void *key_ptr = ECS_OFFSET(key_array, key_size * i); - if (map->compare(key_ptr, key) == 0) { - return i; - } + if (!desc.lookup_action) { + desc.lookup_action = (ecs_entity_t(*)( + const ecs_world_t*, const char*, void*))flecs_json_ensure_entity; + desc.lookup_ctx = &ctx.anonymous_ids; } - return -1; -} -void flecs_hashmap_init_( - ecs_hashmap_t *map, - ecs_size_t key_size, - ecs_size_t value_size, - ecs_hash_value_action_t hash, - ecs_compare_action_t compare, - ecs_allocator_t *allocator) -{ - map->key_size = key_size; - map->value_size = value_size; - map->hash = hash; - map->compare = compare; - flecs_ballocator_init_t(&map->bucket_allocator, ecs_hm_bucket_t); - ecs_map_init(&map->impl, allocator); + json = flecs_entity_from_json(world, e, json, &desc, &ctx); + + flecs_from_json_ctx_fini(&ctx); + return json; } -void flecs_hashmap_fini( - ecs_hashmap_t *map) +const char* ecs_world_from_json( + ecs_world_t *world, + const char *json, + const ecs_from_json_desc_t *desc_arg) { - ecs_allocator_t *a = map->impl.allocator; - ecs_map_iter_t it = ecs_map_iter(&map->impl); + ecs_json_token_t token_kind; + char token[ECS_MAX_TOKEN_SIZE]; - while (ecs_map_next(&it)) { - ecs_hm_bucket_t *bucket = ecs_map_ptr(&it); - ecs_vec_fini(a, &bucket->keys, map->key_size); - ecs_vec_fini(a, &bucket->values, map->value_size); -#ifdef FLECS_SANITIZE - flecs_bfree(&map->bucket_allocator, bucket); -#endif + ecs_from_json_desc_t desc = {0}; + if (desc_arg) { + desc = *desc_arg; } - flecs_ballocator_fini(&map->bucket_allocator); - ecs_map_fini(&map->impl); -} + desc.expr = json; -void flecs_hashmap_copy( - ecs_hashmap_t *dst, - const ecs_hashmap_t *src) -{ - ecs_assert(dst != src, ECS_INVALID_PARAMETER, NULL); + ecs_allocator_t *a = &world->allocator; + ecs_from_json_ctx_t ctx; + flecs_from_json_ctx_init(a, &ctx); - flecs_hashmap_init_(dst, src->key_size, src->value_size, src->hash, - src->compare, src->impl.allocator); - ecs_map_copy(&dst->impl, &src->impl); + const char *expr = json, *lah; + ctx.expr = expr; - ecs_allocator_t *a = dst->impl.allocator; - ecs_map_iter_t it = ecs_map_iter(&dst->impl); - while (ecs_map_next(&it)) { - ecs_hm_bucket_t **bucket_ptr = ecs_map_ref(&it, ecs_hm_bucket_t); - ecs_hm_bucket_t *src_bucket = bucket_ptr[0]; - ecs_hm_bucket_t *dst_bucket = flecs_balloc(&dst->bucket_allocator); - bucket_ptr[0] = dst_bucket; - dst_bucket->keys = ecs_vec_copy(a, &src_bucket->keys, dst->key_size); - dst_bucket->values = ecs_vec_copy(a, &src_bucket->values, dst->value_size); + if (!desc.lookup_action) { + desc.lookup_action = (ecs_entity_t(*)( + const ecs_world_t*, const char*, void*))flecs_json_ensure_entity; + desc.lookup_ctx = &ctx.anonymous_ids; } -} - -void* flecs_hashmap_get_( - const ecs_hashmap_t *map, - ecs_size_t key_size, - const void *key, - ecs_size_t value_size) -{ - ecs_assert(map->key_size == key_size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(map->value_size == value_size, ECS_INVALID_PARAMETER, NULL); - uint64_t hash = map->hash(key); - ecs_hm_bucket_t *bucket = ecs_map_get_deref(&map->impl, - ecs_hm_bucket_t, hash); - if (!bucket) { - return NULL; + json = flecs_json_expect(json, JsonObjectOpen, token, &desc); + if (!json) { + goto error; } - int32_t index = flecs_hashmap_find_key(map, &bucket->keys, key_size, key); - if (index == -1) { - return NULL; + json = flecs_json_expect_member_name(json, token, "results", &desc); + if (!json) { + goto error; } - return ecs_vec_get(&bucket->values, value_size, index); -} - -flecs_hashmap_result_t flecs_hashmap_ensure_( - ecs_hashmap_t *map, - ecs_size_t key_size, - const void *key, - ecs_size_t value_size) -{ - ecs_assert(map->key_size == key_size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(map->value_size == value_size, ECS_INVALID_PARAMETER, NULL); + json = flecs_json_expect(json, JsonArrayOpen, token, &desc); + if (!json) { + goto error; + } - uint64_t hash = map->hash(key); - ecs_hm_bucket_t **r = ecs_map_ensure_ref(&map->impl, ecs_hm_bucket_t, hash); - ecs_hm_bucket_t *bucket = r[0]; - if (!bucket) { - bucket = r[0] = flecs_bcalloc(&map->bucket_allocator); + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonArrayClose) { + json = lah; + goto end; } - ecs_allocator_t *a = map->impl.allocator; - void *value_ptr, *key_ptr; - ecs_vec_t *keys = &bucket->keys; - ecs_vec_t *values = &bucket->values; - if (!keys->array) { - keys = ecs_vec_init(a, &bucket->keys, key_size, 1); - values = ecs_vec_init(a, &bucket->values, value_size, 1); - key_ptr = ecs_vec_append(a, keys, key_size); - value_ptr = ecs_vec_append(a, values, value_size); - ecs_os_memcpy(key_ptr, key, key_size); - ecs_os_memset(value_ptr, 0, value_size); - } else { - int32_t index = flecs_hashmap_find_key(map, keys, key_size, key); - if (index == -1) { - key_ptr = ecs_vec_append(a, keys, key_size); - value_ptr = ecs_vec_append(a, values, value_size); - ecs_os_memcpy(key_ptr, key, key_size); - ecs_os_memset(value_ptr, 0, value_size); - } else { - key_ptr = ecs_vec_get(keys, key_size, index); - value_ptr = ecs_vec_get(values, value_size, index); + do { + json = flecs_entity_from_json(world, 0, json, &desc, &ctx); + if (!json) { + goto error; } - } - return (flecs_hashmap_result_t){ - .key = key_ptr, .value = value_ptr, .hash = hash - }; -} + json = flecs_json_parse(json, &token_kind, token); + if (token_kind != JsonComma) { + if (token_kind != JsonArrayClose) { + ecs_parser_error(NULL, expr, json - expr, "expected ']'"); + goto error; + } + break; + } + } while (true); -void flecs_hashmap_set_( - ecs_hashmap_t *map, - ecs_size_t key_size, - void *key, - ecs_size_t value_size, - const void *value) -{ - void *value_ptr = flecs_hashmap_ensure_(map, key_size, key, value_size).value; - ecs_assert(value_ptr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_os_memcpy(value_ptr, value, value_size); +end: + json = flecs_json_expect(json, JsonObjectClose, token, &desc); + + flecs_from_json_ctx_fini(&ctx); + return json; +error: + flecs_from_json_ctx_fini(&ctx); + return NULL; } -ecs_hm_bucket_t* flecs_hashmap_get_bucket( - const ecs_hashmap_t *map, - uint64_t hash) +const char* ecs_world_from_json_file( + ecs_world_t *world, + const char *filename, + const ecs_from_json_desc_t *desc) { - ecs_assert(map != NULL, ECS_INTERNAL_ERROR, NULL); - return ecs_map_get_deref(&map->impl, ecs_hm_bucket_t, hash); + char *json = flecs_load_from_file(filename); + if (!json) { + ecs_err("file not found: %s", filename); + return NULL; + } + + const char *result = ecs_world_from_json(world, json, desc); + ecs_os_free(json); + return result; } -void flecs_hm_bucket_remove( - ecs_hashmap_t *map, - ecs_hm_bucket_t *bucket, - uint64_t hash, - int32_t index) +#endif + +/** + * @file addons/json/deserialize_value.c + * @brief Deserialize JSON strings into (component) values. + */ + +#include + +#ifdef FLECS_JSON + +const char* ecs_ptr_from_json( + const ecs_world_t *world, + ecs_entity_t type, + void *ptr, + const char *json, + const ecs_from_json_desc_t *desc) { - ecs_vec_remove(&bucket->keys, map->key_size, index); - ecs_vec_remove(&bucket->values, map->value_size, index); + ecs_json_token_t token_kind = 0; + char token_buffer[ECS_MAX_TOKEN_SIZE], t_lah[ECS_MAX_TOKEN_SIZE]; + char *token = token_buffer; + int depth = 0; + + const char *name = NULL; + const char *expr = NULL; + + ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, ptr); + if (cur.valid == false) { + return NULL; + } + + if (desc) { + name = desc->name; + expr = desc->expr; + cur.lookup_action = desc->lookup_action; + cur.lookup_ctx = desc->lookup_ctx; + } + + while ((json = flecs_json_parse(json, &token_kind, token))) { + if (token_kind == JsonLargeString) { + ecs_strbuf_t large_token = ECS_STRBUF_INIT; + json = flecs_json_parse_large_string(json, &large_token); + if (!json) { + break; + } + + token = ecs_strbuf_get(&large_token); + token_kind = JsonString; + } - if (!ecs_vec_count(&bucket->keys)) { - ecs_allocator_t *a = map->impl.allocator; - ecs_vec_fini(a, &bucket->keys, map->key_size); - ecs_vec_fini(a, &bucket->values, map->value_size); - ecs_hm_bucket_t *b = ecs_map_remove_ptr(&map->impl, hash); - ecs_assert(bucket == b, ECS_INTERNAL_ERROR, NULL); (void)b; - flecs_bfree(&map->bucket_allocator, bucket); - } -} + if (token_kind == JsonObjectOpen) { + depth ++; + if (ecs_meta_push(&cur) != 0) { + goto error; + } -void flecs_hashmap_remove_w_hash_( - ecs_hashmap_t *map, - ecs_size_t key_size, - const void *key, - ecs_size_t value_size, - uint64_t hash) -{ - ecs_assert(map->key_size == key_size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(map->value_size == value_size, ECS_INVALID_PARAMETER, NULL); - (void)value_size; + if (ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, json - expr, "expected '['"); + return NULL; + } + } else if (token_kind == JsonObjectClose) { + depth --; - ecs_hm_bucket_t *bucket = ecs_map_get_deref(&map->impl, - ecs_hm_bucket_t, hash); - if (!bucket) { - return; - } + if (ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, json - expr, "expected ']'"); + return NULL; + } - int32_t index = flecs_hashmap_find_key(map, &bucket->keys, key_size, key); - if (index == -1) { - return; - } + if (ecs_meta_pop(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonArrayOpen) { + depth ++; + if (ecs_meta_push(&cur) != 0) { + goto error; + } - flecs_hm_bucket_remove(map, bucket, hash, index); -} + if (!ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, json - expr, "expected '{'"); + return NULL; + } + } else if (token_kind == JsonArrayClose) { + depth --; -void flecs_hashmap_remove_( - ecs_hashmap_t *map, - ecs_size_t key_size, - const void *key, - ecs_size_t value_size) -{ - ecs_assert(map->key_size == key_size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(map->value_size == value_size, ECS_INVALID_PARAMETER, NULL); + if (!ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, json - expr, "expected '}'"); + return NULL; + } - uint64_t hash = map->hash(key); - flecs_hashmap_remove_w_hash_(map, key_size, key, value_size, hash); -} + if (ecs_meta_pop(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonComma) { + if (ecs_meta_next(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonNull) { + if (ecs_meta_set_null(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonString) { + const char *lah = flecs_json_parse( + json, &token_kind, t_lah); + if (token_kind == JsonColon) { + /* Member assignment */ + json = lah; + if (ecs_meta_dotmember(&cur, token) != 0) { + goto error; + } + } else { + if (ecs_meta_set_string(&cur, token) != 0) { + goto error; + } + } + } else if (token_kind == JsonNumber) { + double number = atof(token); + if (ecs_meta_set_float(&cur, number) != 0) { + goto error; + } + } else if (token_kind == JsonLargeInt) { + int64_t number = flecs_ito(int64_t, atoll(token)); + if (ecs_meta_set_int(&cur, number) != 0) { + goto error; + } + } else if (token_kind == JsonNull) { + if (ecs_meta_set_null(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonTrue) { + if (ecs_meta_set_bool(&cur, true) != 0) { + goto error; + } + } else if (token_kind == JsonFalse) { + if (ecs_meta_set_bool(&cur, false) != 0) { + goto error; + } + } else { + goto error; + } -flecs_hashmap_iter_t flecs_hashmap_iter( - ecs_hashmap_t *map) -{ - return (flecs_hashmap_iter_t){ - .it = ecs_map_iter(&map->impl) - }; -} + if (token != token_buffer) { + ecs_os_free(token); + token = token_buffer; + } -void* flecs_hashmap_next_( - flecs_hashmap_iter_t *it, - ecs_size_t key_size, - void *key_out, - ecs_size_t value_size) -{ - int32_t index = ++ it->index; - ecs_hm_bucket_t *bucket = it->bucket; - while (!bucket || it->index >= ecs_vec_count(&bucket->keys)) { - ecs_map_next(&it->it); - bucket = it->bucket = ecs_map_ptr(&it->it); - if (!bucket) { - return NULL; + if (!depth) { + break; } - index = it->index = 0; } - if (key_out) { - *(void**)key_out = ecs_vec_get(&bucket->keys, key_size, index); - } - - return ecs_vec_get(&bucket->values, value_size, index); + return json; +error: + return NULL; } +#endif + /** - * @file datastructures/map.c - * @brief Map data structure. - * - * Map data structure for 64bit keys and dynamic payload size. + * @file addons/json/json.c + * @brief JSON serializer utilities. */ +#include -/* The ratio used to determine whether the map should flecs_map_rehash. If - * (element_count * ECS_LOAD_FACTOR) > bucket_count, bucket count is increased. */ -#define ECS_LOAD_FACTOR (12) -#define ECS_BUCKET_END(b, c) ECS_ELEM_T(b, ecs_bucket_t, c) - -static -uint8_t flecs_log2(uint32_t v) { - static const uint8_t log2table[32] = - {0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, - 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}; - - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - return log2table[(uint32_t)(v * 0x07C4ACDDU) >> 27]; -} +#ifdef FLECS_JSON -/* Get bucket count for number of elements */ static -int32_t flecs_map_get_bucket_count( - int32_t count) +const char* flecs_json_token_str( + ecs_json_token_t token_kind) { - return flecs_next_pow_of_2((int32_t)(count * ECS_LOAD_FACTOR * 0.1)); + switch(token_kind) { + case JsonObjectOpen: return "{"; + case JsonObjectClose: return "}"; + case JsonArrayOpen: return "["; + case JsonArrayClose: return "]"; + case JsonColon: return ":"; + case JsonComma: return ","; + case JsonNumber: return "number"; + case JsonLargeInt: return "large integer"; + case JsonLargeString: + case JsonString: return "string"; + case JsonBoolean: return "bool"; + case JsonTrue: return "true"; + case JsonFalse: return "false"; + case JsonNull: return "null"; + case JsonInvalid: return "invalid"; + default: + ecs_throw(ECS_INTERNAL_ERROR, NULL); + } +error: + return "<>"; } -/* Get bucket shift amount for a given bucket count */ -static -uint8_t flecs_map_get_bucket_shift ( - int32_t bucket_count) +const char* flecs_json_parse( + const char *json, + ecs_json_token_t *token_kind, + char *token) { - return (uint8_t)(64u - flecs_log2((uint32_t)bucket_count)); -} + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); + json = flecs_parse_ws_eol(json); -/* Get bucket index for provided map key */ -static -int32_t flecs_map_get_bucket_index( - uint16_t bucket_shift, - ecs_map_key_t key) -{ - ecs_assert(bucket_shift != 0, ECS_INTERNAL_ERROR, NULL); - return (int32_t)((11400714819323198485ull * key) >> bucket_shift); -} + char ch = json[0]; -/* Get bucket for key */ -static -ecs_bucket_t* flecs_map_get_bucket( - const ecs_map_t *map, - ecs_map_key_t key) -{ - ecs_assert(map != NULL, ECS_INVALID_PARAMETER, NULL); - int32_t bucket_id = flecs_map_get_bucket_index(map->bucket_shift, key); - ecs_assert(bucket_id < map->bucket_count, ECS_INTERNAL_ERROR, NULL); - return &map->buckets[bucket_id]; -} + if (ch == '{') { + token_kind[0] = JsonObjectOpen; + return json + 1; + } else if (ch == '}') { + token_kind[0] = JsonObjectClose; + return json + 1; + } else if (ch == '[') { + token_kind[0] = JsonArrayOpen; + return json + 1; + } else if (ch == ']') { + token_kind[0] = JsonArrayClose; + return json + 1; + } else if (ch == ':') { + token_kind[0] = JsonColon; + return json + 1; + } else if (ch == ',') { + token_kind[0] = JsonComma; + return json + 1; + } else if (ch == '"') { + const char *start = json; + char *token_ptr = token; + json ++; + for (; (ch = json[0]); ) { + if (token_ptr - token >= ECS_MAX_TOKEN_SIZE) { + /* Token doesn't fit in buffer, signal to app to try again with + * dynamic buffer. */ + token_kind[0] = JsonLargeString; + return start; + } -/* Add element to bucket */ -static -ecs_map_val_t* flecs_map_bucket_add( - ecs_block_allocator_t *allocator, - ecs_bucket_t *bucket, - ecs_map_key_t key) -{ - ecs_bucket_entry_t *new_entry = flecs_balloc(allocator); - new_entry->key = key; - new_entry->next = bucket->first; - bucket->first = new_entry; - return &new_entry->value; -} + if (ch == '"') { + json ++; + token_ptr[0] = '\0'; + break; + } -/* Remove element from bucket */ -static -ecs_map_val_t flecs_map_bucket_remove( - ecs_map_t *map, - ecs_bucket_t *bucket, - ecs_map_key_t key) -{ - ecs_bucket_entry_t *entry; - for (entry = bucket->first; entry; entry = entry->next) { - if (entry->key == key) { - ecs_map_val_t value = entry->value; - ecs_bucket_entry_t **next_holder = &bucket->first; - while(*next_holder != entry) { - next_holder = &(*next_holder)->next; + json = flecs_chrparse(json, token_ptr ++); + } + + if (!ch) { + token_kind[0] = JsonInvalid; + return NULL; + } else { + token_kind[0] = JsonString; + return json; + } + } else if (isdigit(ch) || (ch == '-')) { + token_kind[0] = JsonNumber; + const char *result = flecs_parse_digit(json, token); + + /* Cheap initial check if parsed token could represent large int */ + if (result - json > 15) { + /* Less cheap secondary check to see if number is integer */ + if (!strchr(token, '.')) { + token_kind[0] = JsonLargeInt; } - *next_holder = entry->next; - flecs_bfree(map->entry_allocator, entry); - map->count --; - return value; } + + return result; + } else if (isalpha(ch)) { + if (!ecs_os_strncmp(json, "null", 4)) { + token_kind[0] = JsonNull; + json += 4; + } else + if (!ecs_os_strncmp(json, "true", 4)) { + token_kind[0] = JsonTrue; + json += 4; + } else + if (!ecs_os_strncmp(json, "false", 5)) { + token_kind[0] = JsonFalse; + json += 5; + } + + if (isalpha(json[0]) || isdigit(json[0])) { + token_kind[0] = JsonInvalid; + return NULL; + } + + return json; + } else { + token_kind[0] = JsonInvalid; + return NULL; } - - return 0; } -/* Free contents of bucket */ -static -void flecs_map_bucket_clear( - ecs_block_allocator_t *allocator, - ecs_bucket_t *bucket) +const char* flecs_json_parse_large_string( + const char *json, + ecs_strbuf_t *buf) { - ecs_bucket_entry_t *entry = bucket->first; - while(entry) { - ecs_bucket_entry_t *next = entry->next; - flecs_bfree(allocator, entry); - entry = next; + if (json[0] != '"') { + return NULL; /* can only parse strings */ } -} -/* Get payload pointer for key from bucket */ -static -ecs_map_val_t* flecs_map_bucket_get( - ecs_bucket_t *bucket, - ecs_map_key_t key) -{ - ecs_bucket_entry_t *entry; - for (entry = bucket->first; entry; entry = entry->next) { - if (entry->key == key) { - return &entry->value; + char ch, ch_out; + json ++; + for (; (ch = json[0]); ) { + if (ch == '"') { + json ++; + break; } + + json = flecs_chrparse(json, &ch_out); + ecs_strbuf_appendch(buf, ch_out); + } + + if (!ch) { + return NULL; + } else { + return json; } - return NULL; } -/* Grow number of buckets */ -static -void flecs_map_rehash( - ecs_map_t *map, - int32_t count) +const char* flecs_json_parse_next_member( + const char *json, + char *token, + ecs_json_token_t *token_kind, + const ecs_from_json_desc_t *desc) { - count = flecs_next_pow_of_2(count); - if (count < 2) { - count = 2; + json = flecs_json_parse(json, token_kind, token); + if (*token_kind == JsonObjectClose) { + return json; } - ecs_assert(count > map->bucket_count, ECS_INTERNAL_ERROR, NULL); - - int32_t old_count = map->bucket_count; - ecs_bucket_t *buckets = map->buckets, *b, *end = ECS_BUCKET_END(buckets, old_count); - if (map->allocator) { - map->buckets = flecs_calloc_n(map->allocator, ecs_bucket_t, count); - } else { - map->buckets = ecs_os_calloc_n(ecs_bucket_t, count); + if (*token_kind != JsonComma) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expecteded } or ,"); + return NULL; } - map->bucket_count = count; - map->bucket_shift = flecs_map_get_bucket_shift(count); - /* Remap old bucket entries to new buckets */ - for (b = buckets; b < end; b++) { - ecs_bucket_entry_t* entry; - for (entry = b->first; entry;) { - ecs_bucket_entry_t* next = entry->next; - int32_t bucket_index = flecs_map_get_bucket_index( - map->bucket_shift, entry->key); - ecs_bucket_t *bucket = &map->buckets[bucket_index]; - entry->next = bucket->first; - bucket->first = entry; - entry = next; - } + json = flecs_json_parse(json, token_kind, token); + if (*token_kind != JsonString) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expecteded member name"); + return NULL; } - if (map->allocator) { - flecs_free_n(map->allocator, ecs_bucket_t, old_count, buckets); - } else { - ecs_os_free(buckets); + char temp_token[ECS_MAX_TOKEN_SIZE]; + ecs_json_token_t temp_token_kind; + + json = flecs_json_parse(json, &temp_token_kind, temp_token); + if (temp_token_kind != JsonColon) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expecteded :"); + return NULL; } + + return json; } -void ecs_map_params_init( - ecs_map_params_t *params, - ecs_allocator_t *allocator) +const char* flecs_json_expect( + const char *json, + ecs_json_token_t token_kind, + char *token, + const ecs_from_json_desc_t *desc) { - params->allocator = allocator; - flecs_ballocator_init_t(¶ms->entry_allocator, ecs_bucket_entry_t); + /* Strings must be handled by flecs_json_expect_string for LargeString */ + ecs_assert(token_kind != JsonString, ECS_INTERNAL_ERROR, NULL); + + ecs_json_token_t kind = 0; + const char *lah = flecs_json_parse(json, &kind, token); + + if (kind == JsonInvalid) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "invalid json"); + return NULL; + } else if (kind != token_kind) { + if (token_kind == JsonBoolean && + (kind == JsonTrue || kind == JsonFalse)) + { + /* ok */ + } else { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expected %s, got %s", + flecs_json_token_str(token_kind), flecs_json_token_str(kind)); + return NULL; + } + } + + return lah; } -void ecs_map_params_fini( - ecs_map_params_t *params) +const char* flecs_json_expect_string( + const char *json, + char *token, + char **out, + const ecs_from_json_desc_t *desc) { - flecs_ballocator_fini(¶ms->entry_allocator); + ecs_json_token_t token_kind = 0; + json = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonInvalid) { + ecs_parser_error( + desc->name, desc->expr, json - desc->expr, "invalid json"); + return NULL; + } else if (token_kind != JsonString && token_kind != JsonLargeString) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expected string"); + return NULL; + } + + if (token_kind == JsonLargeString) { + ecs_strbuf_t large_token = ECS_STRBUF_INIT; + json = flecs_json_parse_large_string(json, &large_token); + if (!json) { + return NULL; + } + + if (out) { + *out = ecs_strbuf_get(&large_token); + } else { + ecs_strbuf_reset(&large_token); + } + } else if (out) { + *out = token; + } + + return json; } -void ecs_map_init_w_params( - ecs_map_t *result, - ecs_map_params_t *params) +const char* flecs_json_expect_member( + const char *json, + char *token, + const ecs_from_json_desc_t *desc) { - ecs_os_zeromem(result); - - result->allocator = params->allocator; + char *out = NULL; + json = flecs_json_expect_string(json, token, &out, desc); + if (!json) { + return NULL; + } - if (params->entry_allocator.chunk_size) { - result->entry_allocator = ¶ms->entry_allocator; - result->shared_allocator = true; - } else { - result->entry_allocator = flecs_ballocator_new_t(ecs_bucket_entry_t); + if (out != token) { + ecs_os_free(out); } - flecs_map_rehash(result, 0); + json = flecs_json_expect(json, JsonColon, token, desc); + if (!json) { + return NULL; + } + return json; } -void ecs_map_init_w_params_if( - ecs_map_t *result, - ecs_map_params_t *params) +const char* flecs_json_expect_next_member( + const char *json, + char *token, + const ecs_from_json_desc_t *desc) { - if (!ecs_map_is_init(result)) { - ecs_map_init_w_params(result, params); + json = flecs_json_expect(json, JsonComma, token, desc); + if (!json) { + return NULL; } -} -void ecs_map_init( - ecs_map_t *result, - ecs_allocator_t *allocator) -{ - ecs_map_init_w_params(result, &(ecs_map_params_t) { - .allocator = allocator - }); + return flecs_json_expect_member(json, token, desc); } -void ecs_map_init_if( - ecs_map_t *result, - ecs_allocator_t *allocator) +const char* flecs_json_expect_member_name( + const char *json, + char *token, + const char *member_name, + const ecs_from_json_desc_t *desc) { - if (!ecs_map_is_init(result)) { - ecs_map_init(result, allocator); - } + json = flecs_json_expect_member(json, token, desc); + if (!json) { + return NULL; + } + if (ecs_os_strcmp(token, member_name)) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expected member '%s'", member_name); + return NULL; + } + return json; } -void ecs_map_fini( - ecs_map_t *map) +static +const char* flecs_json_skip_string( + const char *json) { - if (!ecs_map_is_init(map)) { - return; + if (json[0] != '"') { + return NULL; /* can only skip strings */ } - bool sanitize = false; -#ifdef FLECS_SANITIZE - sanitize = true; -#endif - - /* Free buckets in sanitized mode, so we can replace the allocator with - * regular malloc/free and use asan/valgrind to find memory errors. */ - ecs_allocator_t *a = map->allocator; - ecs_block_allocator_t *ea = map->entry_allocator; - if (map->shared_allocator || sanitize) { - ecs_bucket_t *bucket = map->buckets, *end = &bucket[map->bucket_count]; - while (bucket != end) { - flecs_map_bucket_clear(ea, bucket); - bucket ++; + char ch, ch_out; + json ++; + for (; (ch = json[0]); ) { + if (ch == '"') { + json ++; + break; } - } - if (ea && !map->shared_allocator) { - flecs_ballocator_free(ea); - map->entry_allocator = NULL; + json = flecs_chrparse(json, &ch_out); } - if (a) { - flecs_free_n(a, ecs_bucket_t, map->bucket_count, map->buckets); + + if (!ch) { + return NULL; } else { - ecs_os_free(map->buckets); + return json; } - - map->bucket_shift = 0; } -ecs_map_val_t* ecs_map_get( - const ecs_map_t *map, - ecs_map_key_t key) +const char* flecs_json_skip_object( + const char *json, + char *token, + const ecs_from_json_desc_t *desc) { - return flecs_map_bucket_get(flecs_map_get_bucket(map, key), key); -} + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_json_token_t token_kind = 0; -void* ecs_map_get_deref_( - const ecs_map_t *map, - ecs_map_key_t key) -{ - ecs_map_val_t* ptr = flecs_map_bucket_get( - flecs_map_get_bucket(map, key), key); - if (ptr) { - return (void*)(uintptr_t)ptr[0]; + while ((json = flecs_json_parse(json, &token_kind, token))) { + if (token_kind == JsonObjectOpen) { + json = flecs_json_skip_object(json, token, desc); + } else if (token_kind == JsonArrayOpen) { + json = flecs_json_skip_array(json, token, desc); + } else if (token_kind == JsonLargeString) { + json = flecs_json_skip_string(json); + } else if (token_kind == JsonObjectClose) { + return json; + } else if (token_kind == JsonArrayClose) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expected }, got ]"); + return NULL; + } + + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); } + + ecs_parser_error(desc->name, json, 0, + "expected }, got end of string"); return NULL; } -void ecs_map_insert( - ecs_map_t *map, - ecs_map_key_t key, - ecs_map_val_t value) +const char* flecs_json_skip_array( + const char *json, + char *token, + const ecs_from_json_desc_t *desc) { - ecs_assert(ecs_map_get(map, key) == NULL, ECS_INVALID_PARAMETER, NULL); - int32_t map_count = ++map->count; - int32_t tgt_bucket_count = flecs_map_get_bucket_count(map_count); - int32_t bucket_count = map->bucket_count; - if (tgt_bucket_count > bucket_count) { - flecs_map_rehash(map, tgt_bucket_count); + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_json_token_t token_kind = 0; + + while ((json = flecs_json_parse(json, &token_kind, token))) { + if (token_kind == JsonObjectOpen) { + json = flecs_json_skip_object(json, token, desc); + } else if (token_kind == JsonArrayOpen) { + json = flecs_json_skip_array(json, token, desc); + } else if (token_kind == JsonLargeString) { + json = flecs_json_skip_string(json); + } else if (token_kind == JsonObjectClose) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expected ]"); + return NULL; + } else if (token_kind == JsonArrayClose) { + return json; + } + + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); } - ecs_bucket_t *bucket = flecs_map_get_bucket(map, key); - flecs_map_bucket_add(map->entry_allocator, bucket, key)[0] = value; + ecs_parser_error(desc->name, desc->expr, json - desc->expr, "expected ]"); + return NULL; } -void* ecs_map_insert_alloc( - ecs_map_t *map, - ecs_size_t elem_size, - ecs_map_key_t key) +void flecs_json_next( + ecs_strbuf_t *buf) { - void *elem = ecs_os_calloc(elem_size); - ecs_map_insert_ptr(map, key, (uintptr_t)elem); - return elem; + ecs_strbuf_list_next(buf); } -ecs_map_val_t* ecs_map_ensure( - ecs_map_t *map, - ecs_map_key_t key) +void flecs_json_number( + ecs_strbuf_t *buf, + double value) { - ecs_bucket_t *bucket = flecs_map_get_bucket(map, key); - ecs_map_val_t *result = flecs_map_bucket_get(bucket, key); - if (result) { - return result; - } - - int32_t map_count = ++map->count; - int32_t tgt_bucket_count = flecs_map_get_bucket_count(map_count); - int32_t bucket_count = map->bucket_count; - if (tgt_bucket_count > bucket_count) { - flecs_map_rehash(map, tgt_bucket_count); - bucket = flecs_map_get_bucket(map, key); - } - - ecs_map_val_t* v = flecs_map_bucket_add(map->entry_allocator, bucket, key); - *v = 0; - return v; + ecs_strbuf_appendflt(buf, value, '"'); } -void* ecs_map_ensure_alloc( - ecs_map_t *map, - ecs_size_t elem_size, - ecs_map_key_t key) +void flecs_json_u32( + ecs_strbuf_t *buf, + uint32_t value) { - ecs_map_val_t *val = ecs_map_ensure(map, key); - if (!*val) { - void *elem = ecs_os_calloc(elem_size); - *val = (ecs_map_val_t)(uintptr_t)elem; - return elem; - } else { - return (void*)(uintptr_t)*val; - } + ecs_strbuf_appendint(buf, flecs_uto(int64_t, value)); } -ecs_map_val_t ecs_map_remove( - ecs_map_t *map, - ecs_map_key_t key) +void flecs_json_true( + ecs_strbuf_t *buf) { - return flecs_map_bucket_remove(map, flecs_map_get_bucket(map, key), key); + ecs_strbuf_appendlit(buf, "true"); } -void ecs_map_remove_free( - ecs_map_t *map, - ecs_map_key_t key) +void flecs_json_false( + ecs_strbuf_t *buf) { - ecs_map_val_t val = ecs_map_remove(map, key); - if (val) { - ecs_os_free((void*)(uintptr_t)val); - } + ecs_strbuf_appendlit(buf, "false"); } -void ecs_map_clear( - ecs_map_t *map) +void flecs_json_bool( + ecs_strbuf_t *buf, + bool value) { - ecs_assert(map != NULL, ECS_INVALID_PARAMETER, NULL); - int32_t i, count = map->bucket_count; - for (i = 0; i < count; i ++) { - flecs_map_bucket_clear(map->entry_allocator, &map->buckets[i]); - } - if (map->allocator) { - flecs_free_n(map->allocator, ecs_bucket_t, count, map->buckets); + if (value) { + flecs_json_true(buf); } else { - ecs_os_free(map->buckets); + flecs_json_false(buf); } - map->buckets = NULL; - map->bucket_count = 0; - map->count = 0; - flecs_map_rehash(map, 2); } -ecs_map_iter_t ecs_map_iter( - const ecs_map_t *map) +void flecs_json_null( + ecs_strbuf_t *buf) { - if (ecs_map_is_init(map)) { - return (ecs_map_iter_t){ - .map = map, - .bucket = NULL, - .entry = NULL - }; - } else { - return (ecs_map_iter_t){ 0 }; - } + ecs_strbuf_appendlit(buf, "null"); } -bool ecs_map_next( - ecs_map_iter_t *iter) +void flecs_json_array_push( + ecs_strbuf_t *buf) { - const ecs_map_t *map = iter->map; - ecs_bucket_t *end; - if (!map || (iter->bucket == (end = &map->buckets[map->bucket_count]))) { - return false; - } - - ecs_bucket_entry_t *entry = NULL; - if (!iter->bucket) { - for (iter->bucket = map->buckets; - iter->bucket != end; - ++iter->bucket) - { - if (iter->bucket->first) { - entry = iter->bucket->first; - break; - } - } - if (iter->bucket == end) { - return false; - } - } else if ((entry = iter->entry) == NULL) { - do { - ++iter->bucket; - if (iter->bucket == end) { - return false; - } - } while(!iter->bucket->first); - entry = iter->bucket->first; - } - - ecs_assert(entry != NULL, ECS_INTERNAL_ERROR, NULL); - iter->entry = entry->next; - iter->res = &entry->key; - - return true; + ecs_strbuf_list_push(buf, "[", ", "); } -void ecs_map_copy( - ecs_map_t *dst, - const ecs_map_t *src) +void flecs_json_array_pop( + ecs_strbuf_t *buf) { - if (ecs_map_is_init(dst)) { - ecs_assert(ecs_map_count(dst) == 0, ECS_INVALID_PARAMETER, NULL); - ecs_map_fini(dst); - } - - if (!ecs_map_is_init(src)) { - return; - } - - ecs_map_init(dst, src->allocator); - - ecs_map_iter_t it = ecs_map_iter(src); - while (ecs_map_next(&it)) { - ecs_map_insert(dst, ecs_map_key(&it), ecs_map_value(&it)); - } + ecs_strbuf_list_pop(buf, "]"); } -/** - * @file datastructures/name_index.c - * @brief Data structure for resolving 64bit keys by string (name). - */ - - -static -uint64_t flecs_name_index_hash( - const void *ptr) +void flecs_json_object_push( + ecs_strbuf_t *buf) { - const ecs_hashed_string_t *str = ptr; - ecs_assert(str->hash != 0, ECS_INTERNAL_ERROR, NULL); - return str->hash; + ecs_strbuf_list_push(buf, "{", ", "); } -static -int flecs_name_index_compare( - const void *ptr1, - const void *ptr2) +void flecs_json_object_pop( + ecs_strbuf_t *buf) { - const ecs_hashed_string_t *str1 = ptr1; - const ecs_hashed_string_t *str2 = ptr2; - ecs_size_t len1 = str1->length; - ecs_size_t len2 = str2->length; - if (len1 != len2) { - return (len1 > len2) - (len1 < len2); - } - - return ecs_os_memcmp(str1->value, str2->value, len1); + ecs_strbuf_list_pop(buf, "}"); } -void flecs_name_index_init( - ecs_hashmap_t *hm, - ecs_allocator_t *allocator) +void flecs_json_string( + ecs_strbuf_t *buf, + const char *value) { - flecs_hashmap_init_(hm, - ECS_SIZEOF(ecs_hashed_string_t), ECS_SIZEOF(uint64_t), - flecs_name_index_hash, - flecs_name_index_compare, - allocator); + ecs_strbuf_appendch(buf, '"'); + ecs_strbuf_appendstr(buf, value); + ecs_strbuf_appendch(buf, '"'); } -void flecs_name_index_init_if( - ecs_hashmap_t *hm, - ecs_allocator_t *allocator) +void flecs_json_string_escape( + ecs_strbuf_t *buf, + const char *value) { - if (!hm->compare) { - flecs_name_index_init(hm, allocator); + ecs_size_t length = flecs_stresc(NULL, 0, '"', value); + if (length == ecs_os_strlen(value)) { + ecs_strbuf_appendch(buf, '"'); + ecs_strbuf_appendstrn(buf, value, length); + ecs_strbuf_appendch(buf, '"'); + } else { + char *out = ecs_os_malloc(length + 3); + flecs_stresc(out + 1, length, '"', value); + out[0] = '"'; + out[length + 1] = '"'; + out[length + 2] = '\0'; + ecs_strbuf_appendstr(buf, out); + ecs_os_free(out); } } -bool flecs_name_index_is_init( - const ecs_hashmap_t *hm) +void flecs_json_member( + ecs_strbuf_t *buf, + const char *name) { - return hm->compare != NULL; + flecs_json_membern(buf, name, ecs_os_strlen(name)); } -ecs_hashmap_t* flecs_name_index_new( - ecs_world_t *world, - ecs_allocator_t *allocator) +void flecs_json_membern( + ecs_strbuf_t *buf, + const char *name, + int32_t name_len) { - ecs_hashmap_t *result = flecs_bcalloc(&world->allocators.hashmap); - flecs_name_index_init(result, allocator); - result->hashmap_allocator = &world->allocators.hashmap; - return result; + ecs_strbuf_list_appendch(buf, '"'); + ecs_strbuf_appendstrn(buf, name, name_len); + ecs_strbuf_appendlit(buf, "\":"); } -void flecs_name_index_fini( - ecs_hashmap_t *map) +void flecs_json_path( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e) { - flecs_hashmap_fini(map); + ecs_strbuf_appendch(buf, '"'); + ecs_get_path_w_sep_buf(world, 0, e, ".", "", buf, true); + ecs_strbuf_appendch(buf, '"'); } -void flecs_name_index_free( - ecs_hashmap_t *map) +static +const char* flecs_json_entity_label( + const ecs_world_t *world, + ecs_entity_t e) { - if (map) { - flecs_name_index_fini(map); - flecs_bfree(map->hashmap_allocator, map); + const char *lbl = NULL; + if (!e) { + return "#0"; } +#ifdef FLECS_DOC + lbl = ecs_doc_get_name(world, e); +#else + lbl = ecs_get_name(world, e); +#endif + return lbl; } -ecs_hashmap_t* flecs_name_index_copy( - ecs_hashmap_t *map) -{ - ecs_hashmap_t *result = flecs_bcalloc(map->hashmap_allocator); - result->hashmap_allocator = map->hashmap_allocator; - flecs_hashmap_copy(result, map); - return result; -} - -ecs_hashed_string_t flecs_get_hashed_string( - const char *name, - ecs_size_t length, - uint64_t hash) +void flecs_json_label( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e) { - if (!length) { - length = ecs_os_strlen(name); + const char *lbl = flecs_json_entity_label(world, e); + if (lbl) { + flecs_json_string_escape(buf, lbl); } else { - ecs_assert(length == ecs_os_strlen(name), ECS_INTERNAL_ERROR, NULL); + ecs_strbuf_appendch(buf, '"'); + ecs_strbuf_appendch(buf, '#'); + ecs_strbuf_appendint(buf, (uint32_t)e); + ecs_strbuf_appendch(buf, '"'); } +} - if (!hash) { - hash = flecs_hash(name, length); +void flecs_json_path_or_label( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e, + bool path) +{ + if (!path) { + flecs_json_label(buf, world, e); } else { - ecs_assert(hash == flecs_hash(name, length), ECS_INTERNAL_ERROR, NULL); + flecs_json_path(buf, world, e); } - - return (ecs_hashed_string_t) { - .value = ECS_CONST_CAST(char*, name), - .length = length, - .hash = hash - }; } -const uint64_t* flecs_name_index_find_ptr( - const ecs_hashmap_t *map, - const char *name, - ecs_size_t length, - uint64_t hash) +void flecs_json_color( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e) { - ecs_hashed_string_t hs = flecs_get_hashed_string(name, length, hash); - ecs_hm_bucket_t *b = flecs_hashmap_get_bucket(map, hs.hash); - if (!b) { - return NULL; - } + (void)world; + (void)e; - ecs_hashed_string_t *keys = ecs_vec_first(&b->keys); - int32_t i, count = ecs_vec_count(&b->keys); + const char *color = NULL; +#ifdef FLECS_DOC + color = ecs_doc_get_color(world, e); +#endif - for (i = 0; i < count; i ++) { - ecs_hashed_string_t *key = &keys[i]; - ecs_assert(key->hash == hs.hash, ECS_INTERNAL_ERROR, NULL); + if (color) { + ecs_strbuf_appendch(buf, '"'); + ecs_strbuf_appendstr(buf, color); + ecs_strbuf_appendch(buf, '"'); + } else { + ecs_strbuf_appendch(buf, '0'); + } +} - if (hs.length != key->length) { - continue; - } +void flecs_json_id( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_id_t id) +{ + ecs_strbuf_appendch(buf, '['); - if (!ecs_os_strcmp(name, key->value)) { - uint64_t *e = ecs_vec_get_t(&b->values, uint64_t, i); - ecs_assert(e != NULL, ECS_INTERNAL_ERROR, NULL); - return e; - } + if (ECS_IS_PAIR(id)) { + ecs_entity_t first = ecs_pair_first(world, id); + ecs_entity_t second = ecs_pair_second(world, id); + ecs_strbuf_appendch(buf, '"'); + ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf, true); + ecs_strbuf_appendch(buf, '"'); + ecs_strbuf_appendch(buf, ','); + ecs_strbuf_appendch(buf, '"'); + ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf, true); + ecs_strbuf_appendch(buf, '"'); + } else { + ecs_strbuf_appendch(buf, '"'); + ecs_get_path_w_sep_buf(world, 0, id & ECS_COMPONENT_MASK, ".", "", buf, true); + ecs_strbuf_appendch(buf, '"'); } - return NULL; -} - -uint64_t flecs_name_index_find( - const ecs_hashmap_t *map, - const char *name, - ecs_size_t length, - uint64_t hash) + ecs_strbuf_appendch(buf, ']'); +} + +static +void flecs_json_id_member_fullpath( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_id_t id) { - const uint64_t *id = flecs_name_index_find_ptr(map, name, length, hash); - if (id) { - return id[0]; + if (ECS_IS_PAIR(id)) { + ecs_strbuf_appendch(buf, '('); + ecs_entity_t first = ecs_pair_first(world, id); + ecs_entity_t second = ecs_pair_second(world, id); + ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf, true); + ecs_strbuf_appendch(buf, ','); + ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf, true); + ecs_strbuf_appendch(buf, ')'); + } else { + ecs_get_path_w_sep_buf(world, 0, id & ECS_COMPONENT_MASK, ".", "", buf, true); } - return 0; } -void flecs_name_index_remove( - ecs_hashmap_t *map, - uint64_t e, - uint64_t hash) +void flecs_json_id_member( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_id_t id, + bool fullpath) { - ecs_hm_bucket_t *b = flecs_hashmap_get_bucket(map, hash); - if (!b) { - return; + ecs_id_t flags = id & ECS_ID_FLAGS_MASK; + + if (flags & ECS_AUTO_OVERRIDE) { + ecs_strbuf_appendlit(buf, "auto_override|"); + id &= ~ECS_AUTO_OVERRIDE; } - uint64_t *ids = ecs_vec_first(&b->values); - int32_t i, count = ecs_vec_count(&b->values); - for (i = 0; i < count; i ++) { - if (ids[i] == e) { - flecs_hm_bucket_remove(map, b, hash, i); - break; - } + if (flags & ECS_TOGGLE) { + ecs_strbuf_appendlit(buf, "toggle|"); + id &= ~ECS_TOGGLE; } -} -void flecs_name_index_update_name( - ecs_hashmap_t *map, - uint64_t e, - uint64_t hash, - const char *name) -{ - ecs_hm_bucket_t *b = flecs_hashmap_get_bucket(map, hash); - if (!b) { + if (fullpath) { + flecs_json_id_member_fullpath(buf, world, id); return; } - uint64_t *ids = ecs_vec_first(&b->values); - int32_t i, count = ecs_vec_count(&b->values); - for (i = 0; i < count; i ++) { - if (ids[i] == e) { - ecs_hashed_string_t *key = ecs_vec_get_t( - &b->keys, ecs_hashed_string_t, i); - key->value = ECS_CONST_CAST(char*, name); - ecs_assert(ecs_os_strlen(name) == key->length, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(flecs_hash(name, key->length) == key->hash, - ECS_INTERNAL_ERROR, NULL); - return; + if (ECS_IS_PAIR(id)) { + ecs_strbuf_appendch(buf, '('); + ecs_entity_t first = ecs_pair_first(world, id); + ecs_entity_t second = ecs_pair_second(world, id); + { + const char *lbl = flecs_json_entity_label(world, first); + if (lbl) { + ecs_strbuf_appendstr(buf, lbl); + } + } + ecs_strbuf_appendch(buf, ','); + { + const char *lbl = flecs_json_entity_label(world, second); + if (lbl) { + ecs_strbuf_appendstr(buf, lbl); + } + } + ecs_strbuf_appendch(buf, ')'); + } else { + const char *lbl = flecs_json_entity_label(world, id & ECS_COMPONENT_MASK); + if (lbl) { + ecs_strbuf_appendstr(buf, lbl); } } - - /* Record must already have been in the index */ - ecs_abort(ECS_INTERNAL_ERROR, NULL); } -void flecs_name_index_ensure( - ecs_hashmap_t *map, - uint64_t id, - const char *name, - ecs_size_t length, - uint64_t hash) +ecs_primitive_kind_t flecs_json_op_to_primitive_kind( + ecs_meta_type_op_kind_t kind) { - ecs_check(name != NULL, ECS_INVALID_PARAMETER, NULL); + return kind - EcsOpPrimitive; +} - ecs_hashed_string_t key = flecs_get_hashed_string(name, length, hash); - - uint64_t existing = flecs_name_index_find( - map, name, key.length, key.hash); - if (existing) { - if (existing != id) { - ecs_abort(ECS_ALREADY_DEFINED, - "conflicting id registered with name '%s'", name); - } - } +#endif - flecs_hashmap_result_t hmr = flecs_hashmap_ensure( - map, &key, uint64_t); - *((uint64_t*)hmr.value) = id; -error: - return; -} +/** + * @file addons/json/serialize_entity.c + * @brief Serialize single entity. + */ /** - * @file datastructures/sparse.c - * @brief Sparse set data structure. + * @file addons/meta/meta.h + * @brief Private functions for meta addon. */ +#ifndef FLECS_META_PRIVATE_H +#define FLECS_META_PRIVATE_H -/** Compute the page index from an id by stripping the first 12 bits */ -#define PAGE(index) ((int32_t)((uint32_t)index >> FLECS_SPARSE_PAGE_BITS)) -/** This computes the offset of an index inside a page */ -#define OFFSET(index) ((int32_t)index & (FLECS_SPARSE_PAGE_SIZE - 1)) +#ifdef FLECS_META -/* Utility to get a pointer to the payload */ -#define DATA(array, size, offset) (ECS_OFFSET(array, size * offset)) +void ecs_meta_type_serialized_init( + ecs_iter_t *it); -typedef struct ecs_page_t { - int32_t *sparse; /* Sparse array with indices to dense array */ - void *data; /* Store data in sparse array to reduce - * indirection and provide stable pointers. */ -} ecs_page_t; +void ecs_meta_dtor_serialized( + EcsTypeSerializer *ptr); -static -ecs_page_t* flecs_sparse_page_new( - ecs_sparse_t *sparse, - int32_t page_index) -{ - ecs_allocator_t *a = sparse->allocator; - ecs_block_allocator_t *ca = sparse->page_allocator; - int32_t count = ecs_vec_count(&sparse->pages); - ecs_page_t *pages; +ecs_meta_type_op_kind_t flecs_meta_primitive_to_op_kind( + ecs_primitive_kind_t kind); - if (count <= page_index) { - ecs_vec_set_count_t(a, &sparse->pages, ecs_page_t, page_index + 1); - pages = ecs_vec_first_t(&sparse->pages, ecs_page_t); - ecs_os_memset_n(&pages[count], 0, ecs_page_t, (1 + page_index - count)); - } else { - pages = ecs_vec_first_t(&sparse->pages, ecs_page_t); - } +bool flecs_unit_validate( + ecs_world_t *world, + ecs_entity_t t, + EcsUnit *data); - ecs_assert(pages != NULL, ECS_INTERNAL_ERROR, NULL); +void flecs_meta_import_definitions( + ecs_world_t *world); - ecs_page_t *result = &pages[page_index]; - ecs_assert(result->sparse == NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(result->data == NULL, ECS_INTERNAL_ERROR, NULL); +int flecs_expr_ser_primitive( + const ecs_world_t *world, + ecs_primitive_kind_t kind, + const void *base, + ecs_strbuf_t *str, + bool is_expr); - /* Initialize sparse array with zero's, as zero is used to indicate that the - * sparse element has not been paired with a dense element. Use zero - * as this means we can take advantage of calloc having a possibly better - * performance than malloc + memset. */ - result->sparse = ca ? flecs_bcalloc(ca) - : ecs_os_calloc_n(int32_t, FLECS_SPARSE_PAGE_SIZE); +void flecs_rtt_init_default_hooks( + ecs_iter_t *it); - /* Initialize the data array with zero's to guarantee that data is - * always initialized. When an entry is removed, data is reset back to - * zero. Initialize now, as this can take advantage of calloc. */ - result->data = a ? flecs_calloc(a, sparse->size * FLECS_SPARSE_PAGE_SIZE) - : ecs_os_calloc(sparse->size * FLECS_SPARSE_PAGE_SIZE); +#endif - ecs_assert(result->sparse != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(result->data != NULL, ECS_INTERNAL_ERROR, NULL); +#endif - return result; -} -static -void flecs_sparse_page_free( - ecs_sparse_t *sparse, - ecs_page_t *page) +#ifdef FLECS_JSON + +int ecs_entity_to_json_buf( + const ecs_world_t *stage, + ecs_entity_t entity, + ecs_strbuf_t *buf, + const ecs_entity_to_json_desc_t *desc) { - ecs_allocator_t *a = sparse->allocator; - ecs_block_allocator_t *ca = sparse->page_allocator; + const ecs_world_t *world = ecs_get_world(stage); - if (ca) { - flecs_bfree(ca, page->sparse); - } else { - ecs_os_free(page->sparse); - } - if (a) { - flecs_free(a, sparse->size * FLECS_SPARSE_PAGE_SIZE, page->data); - } else { - ecs_os_free(page->data); + if (!entity || !ecs_is_valid(world, entity)) { + return -1; } -} -static -ecs_page_t* flecs_sparse_get_page( - const ecs_sparse_t *sparse, - int32_t page_index) -{ - ecs_assert(page_index >= 0, ECS_INVALID_PARAMETER, NULL); - if (page_index >= ecs_vec_count(&sparse->pages)) { - return NULL; - } - return ecs_vec_get_t(&sparse->pages, ecs_page_t, page_index); -} + /* Cache id record for flecs.doc ids */ + ecs_json_ser_ctx_t ser_ctx; + ecs_os_zeromem(&ser_ctx); +#ifdef FLECS_DOC + ser_ctx.idr_doc_name = flecs_id_record_get(world, + ecs_pair_t(EcsDocDescription, EcsName)); + ser_ctx.idr_doc_color = flecs_id_record_get(world, + ecs_pair_t(EcsDocDescription, EcsDocColor)); +#endif -static -ecs_page_t* flecs_sparse_get_or_create_page( - ecs_sparse_t *sparse, - int32_t page_index) -{ - ecs_page_t *page = flecs_sparse_get_page(sparse, page_index); - if (page && page->sparse) { - return page; + ecs_record_t *r = ecs_record_find(world, entity); + if (!r || !r->table) { + flecs_json_object_push(buf); + flecs_json_member(buf, "name"); + ecs_strbuf_appendch(buf, '"'); + ecs_strbuf_appendch(buf, '#'); + ecs_strbuf_appendint(buf, (uint32_t)entity); + ecs_strbuf_appendch(buf, '"'); + flecs_json_object_pop(buf); + return 0; } - return flecs_sparse_page_new(sparse, page_index); -} + /* Create iterator that's populated just with entity */ + int32_t row = ECS_RECORD_TO_ROW(r->row); + ecs_iter_t it = { + .world = ECS_CONST_CAST(ecs_world_t*, world), + .real_world = ECS_CONST_CAST(ecs_world_t*, world), + .table = r->table, + .offset = row, + .count = 1, + .entities = &ecs_table_entities(r->table)[row], + .field_count = 0 + }; -static -void flecs_sparse_grow_dense( - ecs_sparse_t *sparse) -{ - ecs_vec_append_t(sparse->allocator, &sparse->dense, uint64_t); -} + /* Initialize iterator parameters */ + ecs_iter_to_json_desc_t iter_desc = { + .serialize_table = true, + .serialize_entity_ids = desc ? desc->serialize_entity_id : false, + .serialize_values = desc ? desc->serialize_values : true, + .serialize_builtin = desc ? desc->serialize_builtin : false, + .serialize_doc = desc ? desc->serialize_doc : false, + .serialize_matches = desc ? desc->serialize_matches : false, + .serialize_refs = desc ? desc->serialize_refs : 0, + .serialize_alerts = desc ? desc->serialize_alerts : false, + .serialize_full_paths = desc ? desc->serialize_full_paths : true, + .serialize_inherited = desc ? desc->serialize_inherited : false, + .serialize_type_info = desc ? desc->serialize_type_info : false + }; -static -uint64_t flecs_sparse_strip_generation( - uint64_t *index_out) -{ - uint64_t index = *index_out; - uint64_t gen = index & ECS_GENERATION_MASK; - /* Make sure there's no junk in the id */ - ecs_assert(gen == (index & (0xFFFFFFFFull << 32)), - ECS_INVALID_PARAMETER, NULL); - *index_out -= gen; - return gen; -} + if (flecs_json_serialize_iter_result( + world, &it, buf, &iter_desc, &ser_ctx)) + { + return -1; + } -static -void flecs_sparse_assign_index( - ecs_page_t * page, - uint64_t * dense_array, - uint64_t index, - int32_t dense) -{ - /* Initialize sparse-dense pair. This assigns the dense index to the sparse - * array, and the sparse index to the dense array .*/ - page->sparse[OFFSET(index)] = dense; - dense_array[dense] = index; + return 0; } -static -uint64_t flecs_sparse_inc_gen( - uint64_t index) +char* ecs_entity_to_json( + const ecs_world_t *world, + ecs_entity_t entity, + const ecs_entity_to_json_desc_t *desc) { - /* When an index is deleted, its generation is increased so that we can do - * liveliness checking while recycling ids */ - return ECS_GENERATION_INC(index); -} + ecs_strbuf_t buf = ECS_STRBUF_INIT; -static -uint64_t flecs_sparse_inc_id( - ecs_sparse_t *sparse) -{ - /* Generate a new id. The last issued id could be stored in an external - * variable, such as is the case with the last issued entity id, which is - * stored on the world. */ - return ++ sparse->max_id; -} + if (ecs_entity_to_json_buf(world, entity, &buf, desc) != 0) { + ecs_strbuf_reset(&buf); + return NULL; + } -static -uint64_t flecs_sparse_get_id( - const ecs_sparse_t *sparse) -{ - ecs_assert(sparse != NULL, ECS_INTERNAL_ERROR, NULL); - return sparse->max_id; + return ecs_strbuf_get(&buf); } -static -void flecs_sparse_set_id( - ecs_sparse_t *sparse, - uint64_t value) -{ - /* Sometimes the max id needs to be assigned directly, which typically - * happens when the API calls get_or_create for an id that hasn't been - * issued before. */ - sparse->max_id = value; -} +#endif -/* Pair dense id with new sparse id */ -static -uint64_t flecs_sparse_create_id( - ecs_sparse_t *sparse, - int32_t dense) -{ - uint64_t index = flecs_sparse_inc_id(sparse); - flecs_sparse_grow_dense(sparse); +/** + * @file addons/json/serialize_field_info.c + * @brief Serialize query field information to JSON. + */ - ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, PAGE(index)); - ecs_assert(page->sparse[OFFSET(index)] == 0, ECS_INTERNAL_ERROR, NULL); - - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - flecs_sparse_assign_index(page, dense_array, index, dense); - - return index; -} -/* Create new id */ +#ifdef FLECS_JSON + static -uint64_t flecs_sparse_new_index( - ecs_sparse_t *sparse) +bool flecs_json_serialize_get_field_ctx( + const ecs_world_t *world, + const ecs_iter_t *it, + int32_t f, + ecs_json_ser_ctx_t *ser_ctx, + const ecs_iter_to_json_desc_t *desc) { - int32_t dense_count = ecs_vec_count(&sparse->dense); - int32_t count = sparse->count ++; - - ecs_assert(count <= dense_count, ECS_INTERNAL_ERROR, NULL); - if (count < dense_count) { - /* If there are unused elements in the dense array, return first */ - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - return dense_array[count]; + ecs_json_value_ser_ctx_t *value_ctx = &ser_ctx->value_ctx[f]; + if (it->query) { + return flecs_json_serialize_get_value_ctx( + world, it->query->ids[f], value_ctx, desc); + } else if (it->ids[f]) { + return flecs_json_serialize_get_value_ctx( + world, it->ids[f], value_ctx, desc); } else { - return flecs_sparse_create_id(sparse, count); + return false; } } -/* Get value from sparse set when it is guaranteed that the value exists. This - * function is used when values are obtained using a dense index */ -static -void* flecs_sparse_get_sparse( - const ecs_sparse_t *sparse, - int32_t dense, - uint64_t index) -{ - flecs_sparse_strip_generation(&index); - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); - if (!page || !page->sparse) { - return NULL; - } - - int32_t offset = OFFSET(index); - ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); - (void)dense; - - return DATA(page->data, sparse->size, offset); -} -/* Swap dense elements. A swap occurs when an element is removed, or when a - * removed element is recycled. */ -static -void flecs_sparse_swap_dense( - ecs_sparse_t * sparse, - ecs_page_t * page_a, - int32_t a, - int32_t b) +void flecs_json_serialize_field( + const ecs_world_t *world, + const ecs_iter_t *it, + const ecs_query_t *q, + int field, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ctx) { - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - uint64_t index_a = dense_array[a]; - uint64_t index_b = dense_array[b]; + flecs_json_object_push(buf); + flecs_json_memberl(buf, "id"); - ecs_page_t *page_b = flecs_sparse_get_or_create_page(sparse, PAGE(index_b)); - flecs_sparse_assign_index(page_a, dense_array, index_a, b); - flecs_sparse_assign_index(page_b, dense_array, index_b, a); -} + flecs_json_serialize_get_field_ctx(world, it, field, ctx, NULL); + ecs_json_value_ser_ctx_t *value_ctx = &ctx->value_ctx[field]; -void flecs_sparse_init( - ecs_sparse_t *result, - struct ecs_allocator_t *allocator, - ecs_block_allocator_t *page_allocator, - ecs_size_t size) -{ - ecs_assert(result != NULL, ECS_OUT_OF_MEMORY, NULL); - result->size = size; - result->max_id = UINT64_MAX; - result->allocator = allocator; - result->page_allocator = page_allocator; + if (value_ctx->id_label) { + flecs_json_string(buf, value_ctx->id_label); - ecs_vec_init_t(allocator, &result->pages, ecs_page_t, 0); - ecs_vec_init_t(allocator, &result->dense, uint64_t, 1); - result->dense.count = 1; + const ecs_term_t *term = &q->terms[0]; + int t; + for (t = 0; t < q->term_count; t ++) { + if (q->terms[t].field_index == field) { + term = &q->terms[t]; + break; + } + } - /* Consume first value in dense array as 0 is used in the sparse array to - * indicate that a sparse element hasn't been paired yet. */ - ecs_vec_first_t(&result->dense, uint64_t)[0] = 0; + if (term->oper != EcsNot) { + if (term->oper == EcsOptional) { + flecs_json_memberl(buf, "optional"); + flecs_json_bool(buf, true); + } - result->count = 1; -} + if (ECS_IS_PAIR(term->id)) { + if ((term->first.id & EcsIsEntity) && ECS_TERM_REF_ID(&term->first)) { + if (ecs_has_id(world, ECS_TERM_REF_ID(&term->first), EcsExclusive)) { + flecs_json_memberl(buf, "exclusive"); + flecs_json_bool(buf, true); + } + } + } -void flecs_sparse_clear( - ecs_sparse_t *sparse) -{ - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + if (value_ctx->type) { + flecs_json_memberl(buf, "type"); + flecs_json_label(buf, world, value_ctx->type); - int32_t i, count = ecs_vec_count(&sparse->pages); - ecs_page_t *pages = ecs_vec_first_t(&sparse->pages, ecs_page_t); - for (i = 0; i < count; i ++) { - int32_t *indices = pages[i].sparse; - if (indices) { - ecs_os_memset_n(indices, 0, int32_t, FLECS_SPARSE_PAGE_SIZE); + const char *symbol = ecs_get_symbol(world, value_ctx->type); + if (symbol) { + flecs_json_memberl(buf, "symbol"); + flecs_json_string(buf, symbol); + } + } + + if (value_ctx->ser) { + flecs_json_memberl(buf, "schema"); + ecs_type_info_to_json_buf(world, value_ctx->type, buf); + } + } else { + flecs_json_memberl(buf, "not"); + flecs_json_bool(buf, true); } + } else { + ecs_strbuf_appendlit(buf, "0"); } - ecs_vec_set_count_t(sparse->allocator, &sparse->dense, uint64_t, 1); - - sparse->count = 1; - sparse->max_id = 0; + flecs_json_object_pop(buf); } -void flecs_sparse_fini( - ecs_sparse_t *sparse) -{ - ecs_assert(sparse != NULL, ECS_INTERNAL_ERROR, NULL); - - int32_t i, count = ecs_vec_count(&sparse->pages); - ecs_page_t *pages = ecs_vec_first_t(&sparse->pages, ecs_page_t); - for (i = 0; i < count; i ++) { - flecs_sparse_page_free(sparse, &pages[i]); - } +#endif - ecs_vec_fini_t(sparse->allocator, &sparse->pages, ecs_page_t); - ecs_vec_fini_t(sparse->allocator, &sparse->dense, uint64_t); -} +/** + * @file addons/json/serialize_iter.c + * @brief Serialize iterator to JSON. + */ -uint64_t flecs_sparse_new_id( - ecs_sparse_t *sparse) -{ - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_sparse_new_index(sparse); -} -void* flecs_sparse_add( - ecs_sparse_t *sparse, - ecs_size_t size) -{ - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); - uint64_t index = flecs_sparse_new_index(sparse); - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); - ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); - return DATA(page->data, size, OFFSET(index)); -} +#ifdef FLECS_JSON -uint64_t flecs_sparse_last_id( - const ecs_sparse_t *sparse) +static +void flecs_json_serialize_id_str( + const ecs_world_t *world, + ecs_id_t id, + ecs_strbuf_t *buf) { - ecs_assert(sparse != NULL, ECS_INTERNAL_ERROR, NULL); - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - return dense_array[sparse->count - 1]; + ecs_strbuf_appendch(buf, '"'); + if (ECS_IS_PAIR(id)) { + ecs_entity_t first = ecs_pair_first(world, id); + ecs_entity_t second = ecs_pair_first(world, id); + ecs_strbuf_appendch(buf, '('); + ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf, true); + ecs_strbuf_appendch(buf, ','); + ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf, true); + ecs_strbuf_appendch(buf, ')'); + } else { + ecs_get_path_w_sep_buf( + world, 0, id & ECS_COMPONENT_MASK, ".", "", buf, true); + } + ecs_strbuf_appendch(buf, '"'); } -void* flecs_sparse_ensure( - ecs_sparse_t *sparse, - ecs_size_t size, - uint64_t index) +static +void flecs_json_serialize_type_info( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf) { - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(ecs_vec_count(&sparse->dense) > 0, ECS_INTERNAL_ERROR, NULL); - (void)size; + flecs_json_memberl(buf, "type_info"); + flecs_json_object_push(buf); - uint64_t gen = flecs_sparse_strip_generation(&index); - ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, PAGE(index)); - int32_t offset = OFFSET(index); - int32_t dense = page->sparse[offset]; + int32_t field_count = it->field_count; + if (!field_count) { + goto done; + } - if (dense) { - /* Check if element is alive. If element is not alive, update indices so - * that the first unused dense element points to the sparse element. */ - int32_t count = sparse->count; - 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; + if (it->flags & EcsIterNoData) { + goto done; + } - /* First unused element is now last used element */ - sparse->count ++; + for (int i = 0; i < field_count; i ++) { + flecs_json_next(buf); + ecs_entity_t typeid = 0; + if (it->query->terms[i].inout != EcsInOutNone) { + typeid = ecs_get_typeid(world, it->query->terms[i].id); + } + if (typeid) { + flecs_json_serialize_id_str(world, typeid, buf); + ecs_strbuf_appendch(buf, ':'); + ecs_type_info_to_json_buf(world, typeid, buf); } else { - /* Dense is already alive, nothing to be done */ + flecs_json_serialize_id_str(world, it->query->terms[i].id, buf); + ecs_strbuf_appendlit(buf, ":0"); } + } - /* Ensure provided generation matches current. Only allow mismatching - * generations if the provided generation count is 0. This allows for - * using the ensure function in combination with ids that have their - * generation stripped. */ -#ifdef FLECS_DEBUG - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - ecs_assert(!gen || dense_array[dense] == (index | gen), ECS_INTERNAL_ERROR, NULL); -#endif - } else { - /* Element is not paired yet. Must add a new element to dense array */ - flecs_sparse_grow_dense(sparse); +done: + flecs_json_object_pop(buf); +} - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - int32_t dense_count = ecs_vec_count(&sparse->dense) - 1; - int32_t count = sparse->count ++; +static +void flecs_json_serialize_field_info( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ctx) +{ + int32_t field_count = it->field_count; + if (!field_count || !it->query) { + return; + } - /* If index is larger than max id, update max id */ - if (index >= flecs_sparse_get_id(sparse)) { - flecs_sparse_set_id(sparse, index); - } + const ecs_query_t *q = it->query; - if (count < dense_count) { - /* If there are unused elements in the list, move the first unused - * element to the end of the list */ - uint64_t unused = dense_array[count]; - ecs_page_t *unused_page = flecs_sparse_get_or_create_page(sparse, PAGE(unused)); - flecs_sparse_assign_index(unused_page, dense_array, unused, dense_count); - } + flecs_json_memberl(buf, "field_info"); + flecs_json_array_push(buf); - flecs_sparse_assign_index(page, dense_array, index, count); - dense_array[count] |= gen; + int f; + for (f = 0; f < field_count; f ++) { + flecs_json_next(buf); + flecs_json_serialize_field(world, it, q, f, buf, ctx); } - return DATA(page->data, sparse->size, offset); + flecs_json_array_pop(buf); } -void* flecs_sparse_ensure_fast( - ecs_sparse_t *sparse, - ecs_size_t size, - uint64_t index_long) +static +void flecs_json_serialize_query_info( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf) { - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(ecs_vec_count(&sparse->dense) > 0, ECS_INTERNAL_ERROR, NULL); - (void)size; - - uint32_t index = (uint32_t)index_long; - ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, PAGE(index)); - int32_t offset = OFFSET(index); - int32_t dense = page->sparse[offset]; - int32_t count = sparse->count; - - if (!dense) { - /* Element is not paired yet. Must add a new element to dense array */ - sparse->count = count + 1; - if (count == ecs_vec_count(&sparse->dense)) { - flecs_sparse_grow_dense(sparse); - } - - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - flecs_sparse_assign_index(page, dense_array, index, count); + if (!it->query) { + return; } - return DATA(page->data, sparse->size, offset); + const ecs_query_t *q = it->query; + flecs_json_memberl(buf, "query_info"); + flecs_json_serialize_query(world, q, buf); } -void flecs_sparse_remove( - ecs_sparse_t *sparse, - ecs_size_t size, - uint64_t index) +static +void flecs_json_serialize_query_plan( + const ecs_world_t *world, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) { - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); - (void)size; + (void)world; + (void)buf; + (void)desc; - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); - if (!page || !page->sparse) { + if (!desc->query) { return; } - uint64_t gen = flecs_sparse_strip_generation(&index); - int32_t offset = OFFSET(index); - int32_t dense = page->sparse[offset]; + const ecs_query_t *q = desc->query; + flecs_poly_assert(q, ecs_query_t); + const ecs_query_t *cq = ecs_query_get_cache_query(q); + + flecs_json_memberl(buf, "query_plan"); - if (dense) { - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - uint64_t cur_gen = dense_array[dense] & ECS_GENERATION_MASK; - if (gen != cur_gen) { - /* Generation doesn't match which means that the provided entity is - * already not alive. */ - return; + bool prev_color = ecs_log_enable_colors(true); + char *plan = ecs_query_plan(q); + char *cache_plan = NULL; + if (cq) { + flecs_poly_assert(cq, ecs_query_t); + cache_plan = ecs_query_plan(cq); + } + + ecs_strbuf_t plan_buf = ECS_STRBUF_INIT; + if (plan) { + ecs_strbuf_appendstr(&plan_buf, plan); + } else { + if (q->term_count) { + ecs_strbuf_append(&plan_buf, " %sOptimized out (trivial query)\n", ECS_GREY); } + } - /* Increase generation */ - dense_array[dense] = index | flecs_sparse_inc_gen(cur_gen); - - int32_t count = sparse->count; + if (cq) { + ecs_strbuf_appendstr(&plan_buf, "\n\n"); + ecs_strbuf_appendstr(&plan_buf, " Cache plan\n"); + ecs_strbuf_appendstr(&plan_buf, " ---\n"); - if (dense == (count - 1)) { - /* If dense is the last used element, simply decrease count */ - sparse->count --; - } else if (dense < count) { - /* If element is alive, move it to unused elements */ - flecs_sparse_swap_dense(sparse, page, dense, count - 1); - sparse->count --; + if (cache_plan) { + ecs_strbuf_appendstr(&plan_buf, cache_plan); } else { - /* Element is not alive, nothing to be done */ - return; + ecs_strbuf_append(&plan_buf, " %sOptimized out (trivial query)\n", ECS_GREY); } + } - /* Reset memory to zero on remove */ - void *ptr = DATA(page->data, sparse->size, offset); - ecs_os_memset(ptr, 0, size); + char *plan_str = ecs_strbuf_get(&plan_buf); + if (plan_str) { + flecs_json_string_escape(buf, plan_str); + ecs_os_free(plan_str); } else { - /* Element is not paired and thus not alive, nothing to be done */ - return; + flecs_json_null(buf); } + + ecs_os_free(plan); + ecs_os_free(cache_plan); + ecs_log_enable_colors(prev_color); } -void flecs_sparse_set_generation( - ecs_sparse_t *sparse, - uint64_t index) +static +void flecs_json_serialize_query_profile( + const ecs_world_t *world, + ecs_strbuf_t *buf, + const ecs_iter_t *it, + const ecs_iter_to_json_desc_t *desc) { - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, PAGE(index)); + if (!desc->query) { + return; + } - uint64_t index_w_gen = index; - flecs_sparse_strip_generation(&index); - int32_t offset = OFFSET(index); - int32_t dense = page->sparse[offset]; + ecs_time_t t = {0}; + int32_t result_count = 0, entity_count = 0, i, sample_count = 100; + ecs_size_t component_bytes = 0, shared_component_bytes = 0; + double eval_time = 0, eval_min = 0, eval_max = 0; + ecs_time_measure(&t); + + for (i = 0; i < sample_count; i ++) { + result_count = 0; + entity_count = 0; + component_bytes = 0; + shared_component_bytes = 0; + + ecs_iter_t qit = ecs_query_iter(world, desc->query); + while (ecs_query_next(&qit)) { + result_count ++; + entity_count += qit.count; + + int8_t f, field_count = qit.field_count; + for (f = 0; f < field_count; f ++) { + size_t size = ecs_field_size(&qit, f); + if (ecs_field_is_set(&qit, f) && size) { + if (ecs_field_is_self(&qit, f)) { + component_bytes += + flecs_uto(ecs_size_t, size) * qit.count; + } else { + shared_component_bytes += flecs_uto(ecs_size_t, size); + } + } + } + } - if (dense) { - /* Increase generation */ - ecs_vec_get_t(&sparse->dense, uint64_t, dense)[0] = index_w_gen; - } else { - /* Element is not paired and thus not alive, nothing to be done */ + double time_measure = ecs_time_measure(&t); + if (!i) { + eval_min = time_measure; + } else if (time_measure < eval_min) { + eval_min = time_measure; + } + + if (time_measure > eval_max) { + eval_max = time_measure; + } + + eval_time += time_measure; + + /* Don't profile for too long */ + if (eval_time > 0.001) { + i ++; + break; + } } -} -void* flecs_sparse_get_dense( - const ecs_sparse_t *sparse, - ecs_size_t size, - int32_t dense_index) -{ - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(dense_index < sparse->count, ECS_INVALID_PARAMETER, NULL); - (void)size; + eval_time /= i; - dense_index ++; + flecs_json_memberl(buf, "query_profile"); + flecs_json_object_push(buf); + if (it->query) { + /* Correct for profiler */ + ECS_CONST_CAST(ecs_query_t*, it->query)->eval_count -= i; + flecs_json_memberl(buf, "eval_count"); + flecs_json_number(buf, it->query->eval_count); + } + flecs_json_memberl(buf, "result_count"); + flecs_json_number(buf, result_count); + flecs_json_memberl(buf, "entity_count"); + flecs_json_number(buf, entity_count); + + flecs_json_memberl(buf, "eval_time_avg_us"); + flecs_json_number(buf, eval_time * 1000.0 * 1000.0); + flecs_json_memberl(buf, "eval_time_min_us"); + flecs_json_number(buf, eval_min * 1000.0 * 1000.0); + flecs_json_memberl(buf, "eval_time_max_us"); + flecs_json_number(buf, eval_max * 1000.0 * 1000.0); + + flecs_json_memberl(buf, "component_bytes"); + flecs_json_number(buf, component_bytes); + flecs_json_memberl(buf, "shared_component_bytes"); + flecs_json_number(buf, shared_component_bytes); - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - return flecs_sparse_get_sparse(sparse, dense_index, dense_array[dense_index]); + flecs_json_object_pop(buf); } -bool flecs_sparse_is_alive( - const ecs_sparse_t *sparse, - uint64_t index) +static +void flecs_iter_free_ser_ctx( + ecs_iter_t *it, + ecs_json_ser_ctx_t *ser_ctx) { - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); - if (!page || !page->sparse) { - return false; + int32_t f, field_count = it->field_count; + for (f = 0; f < field_count; f ++) { + ecs_os_free(ser_ctx->value_ctx[f].id_label); } +} - int32_t offset = OFFSET(index); - int32_t dense = page->sparse[offset]; - if (!dense || (dense >= sparse->count)) { - return false; - } +int ecs_iter_to_json_buf( + ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + ecs_world_t *world = it->real_world; - uint64_t gen = flecs_sparse_strip_generation(&index); - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - uint64_t cur_gen = dense_array[dense] & ECS_GENERATION_MASK; + /* Cache id record for flecs.doc ids */ + ecs_json_ser_ctx_t ser_ctx; + ecs_os_zeromem(&ser_ctx); +#ifdef FLECS_DOC + ser_ctx.idr_doc_name = flecs_id_record_get(world, + ecs_pair_t(EcsDocDescription, EcsName)); + ser_ctx.idr_doc_color = flecs_id_record_get(world, + ecs_pair_t(EcsDocDescription, EcsDocColor)); +#endif - if (cur_gen != gen) { - return false; + flecs_json_object_push(buf); + + /* Serialize type info if enabled */ + if (desc && desc->serialize_type_info) { + flecs_json_serialize_type_info(world, it, buf); } - ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); - return true; -} + /* Serialize field info if enabled */ + if (desc && desc->serialize_field_info) { + flecs_json_serialize_field_info(world, it, buf, &ser_ctx); + } -void* flecs_sparse_try( - const ecs_sparse_t *sparse, - ecs_size_t size, - uint64_t index) -{ - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); - (void)size; - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); - if (!page || !page->sparse) { - return NULL; + /* Serialize query info if enabled */ + if (desc && desc->serialize_query_info) { + flecs_json_serialize_query_info(world, it, buf); } - int32_t offset = OFFSET(index); - int32_t dense = page->sparse[offset]; - if (!dense || (dense >= sparse->count)) { - return NULL; + /* Serialize query plan if enabled */ + if (desc && desc->serialize_query_plan) { + flecs_json_serialize_query_plan(world, buf, desc); } - uint64_t gen = flecs_sparse_strip_generation(&index); - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - uint64_t cur_gen = dense_array[dense] & ECS_GENERATION_MASK; - if (cur_gen != gen) { - return NULL; + /* Profile query */ + if (desc && desc->serialize_query_profile) { + flecs_json_serialize_query_profile(world, buf, it, desc); } - ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); - return DATA(page->data, sparse->size, offset); -} + /* Serialize results */ + if (!desc || !desc->dont_serialize_results) { + flecs_json_memberl(buf, "results"); + flecs_json_array_push(buf); -void* flecs_sparse_get( - const ecs_sparse_t *sparse, - ecs_size_t size, - uint64_t index) -{ - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); - (void)size; + /* 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, EcsIterNoData); + } - ecs_page_t *page = ecs_vec_get_t(&sparse->pages, ecs_page_t, PAGE(index)); - int32_t offset = OFFSET(index); - int32_t dense = page->sparse[offset]; - ecs_assert(dense != 0, ECS_INTERNAL_ERROR, NULL); + ecs_iter_next_action_t next = it->next; + while (next(it)) { + if (flecs_json_serialize_iter_result(world, it, buf, desc, &ser_ctx)) { + ecs_strbuf_reset(buf); + flecs_iter_free_ser_ctx(it, &ser_ctx); + ecs_iter_fini(it); + return -1; + } + } - uint64_t gen = flecs_sparse_strip_generation(&index); - uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); - uint64_t cur_gen = dense_array[dense] & ECS_GENERATION_MASK; - (void)cur_gen; (void)gen; + flecs_json_array_pop(buf); + } else { + ecs_iter_fini(it); + } - ecs_assert(cur_gen == gen, ECS_INVALID_PARAMETER, NULL); - ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); - ecs_assert(dense < sparse->count, ECS_INTERNAL_ERROR, NULL); - return DATA(page->data, sparse->size, offset); + flecs_iter_free_ser_ctx(it, &ser_ctx); + + flecs_json_object_pop(buf); + + return 0; } -void* flecs_sparse_get_any( - const ecs_sparse_t *sparse, - ecs_size_t size, - uint64_t index) +char* ecs_iter_to_json( + ecs_iter_t *it, + const ecs_iter_to_json_desc_t *desc) { - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); - (void)size; - - flecs_sparse_strip_generation(&index); - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); - if (!page || !page->sparse) { - return NULL; - } + ecs_strbuf_t buf = ECS_STRBUF_INIT; - int32_t offset = OFFSET(index); - int32_t dense = page->sparse[offset]; - bool in_use = dense && (dense < sparse->count); - if (!in_use) { + if (ecs_iter_to_json_buf(it, &buf, desc)) { + ecs_strbuf_reset(&buf); return NULL; } - ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); - return DATA(page->data, sparse->size, offset); + return ecs_strbuf_get(&buf); } -int32_t flecs_sparse_count( - const ecs_sparse_t *sparse) -{ - if (!sparse || !sparse->count) { - return 0; - } +#endif - return sparse->count - 1; -} +/** + * @file addons/json/serialize_iter_rows.c + * @brief Serialize (component) values to JSON strings. + */ -const uint64_t* flecs_sparse_ids( - const ecs_sparse_t *sparse) + +#ifdef FLECS_JSON + +static +bool flecs_json_skip_variable( + const char *name) { - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - if (sparse->dense.array) { - return &(ecs_vec_first_t(&sparse->dense, uint64_t)[1]); + if (!name || name[0] == '_' || !ecs_os_strcmp(name, "this")) { + return true; } else { - return NULL; + return false; } } -void ecs_sparse_init( - ecs_sparse_t *sparse, - ecs_size_t elem_size) +bool flecs_json_serialize_vars( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) { - flecs_sparse_init(sparse, NULL, NULL, elem_size); -} + char **variable_names = it->variable_names; + int32_t var_count = it->variable_count; + int32_t actual_count = 0; -void* ecs_sparse_add( - ecs_sparse_t *sparse, - ecs_size_t elem_size) -{ - return flecs_sparse_add(sparse, elem_size); -} + for (int i = 1; i < var_count; i ++) { + const char *var_name = variable_names[i]; + if (flecs_json_skip_variable(var_name)) continue; -uint64_t ecs_sparse_last_id( - const ecs_sparse_t *sparse) -{ - return flecs_sparse_last_id(sparse); + ecs_entity_t var = it->variables[i].entity; + if (!var) { + /* Can't happen, but not the place of the serializer to complain */ + continue; + } + + if (!actual_count) { + flecs_json_memberl(buf, "vars"); + flecs_json_object_push(buf); + actual_count ++; + } + + flecs_json_member(buf, var_name); + flecs_json_path_or_label(buf, world, var, + desc ? desc->serialize_full_paths : true); + } + + if (actual_count) { + flecs_json_object_pop(buf); + } + + return actual_count != 0; } -int32_t ecs_sparse_count( - const ecs_sparse_t *sparse) +int flecs_json_serialize_matches( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity) { - return flecs_sparse_count(sparse); + flecs_json_memberl(buf, "matches"); + flecs_json_array_push(buf); + + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_pair_t(EcsPoly, EcsQuery)); + + if (idr) { + ecs_table_cache_iter_t it; + if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) { + const ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + EcsPoly *queries = ecs_table_get_column(table, tr->column, 0); + + const ecs_entity_t *entities = ecs_table_entities(table); + int32_t i, count = ecs_table_count(table); + for (i = 0; i < count; i ++) { + ecs_query_t *q = queries[i].poly; + if (!q) { + continue; + } + + ecs_assert(flecs_poly_is(q, ecs_query_t), + ECS_INTERNAL_ERROR, NULL); + + if (!(q->flags & EcsQueryMatchThis)) { + continue; + } + + ecs_iter_t qit = ecs_query_iter(world, q); + if (!qit.variables) { + ecs_iter_fini(&qit); + continue; + } + + ecs_iter_set_var(&qit, 0, entity); + if (ecs_iter_is_true(&qit)) { + flecs_json_next(buf); + flecs_json_path(buf, world, entities[i]); + } + } + } + } + } + + flecs_json_array_pop(buf); + + return 0; } -void* ecs_sparse_get_dense( - const ecs_sparse_t *sparse, - ecs_size_t elem_size, - int32_t index) +static +int flecs_json_serialize_refs_idr( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_id_record_t *idr) { - return flecs_sparse_get_dense(sparse, elem_size, index); + char *id_str = ecs_id_str(world, ecs_pair_first(world, idr->id)); + + flecs_json_member(buf, id_str); + ecs_os_free(id_str); + + flecs_json_array_push(buf); + + ecs_table_cache_iter_t it; + if (idr && flecs_table_cache_all_iter((ecs_table_cache_t*)idr, &it)) { + const ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + const ecs_entity_t *entities = ecs_table_entities(table); + int32_t i, count = ecs_table_count(table); + for (i = 0; i < count; i ++) { + ecs_entity_t e = entities[i]; + flecs_json_next(buf); + flecs_json_path(buf, world, e); + } + } + } + + flecs_json_array_pop(buf); + + return 0; } -void* ecs_sparse_get( - const ecs_sparse_t *sparse, - ecs_size_t elem_size, - uint64_t id) +int flecs_json_serialize_refs( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity, + ecs_entity_t relationship) { - return flecs_sparse_get(sparse, elem_size, id); + flecs_json_memberl(buf, "refs"); + flecs_json_object_push(buf); + + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_pair(relationship, entity)); + + if (idr) { + if (relationship == EcsWildcard) { + ecs_id_record_t *cur = idr; + while ((cur = cur->second.next)) { + flecs_json_serialize_refs_idr(world, buf, cur); + } + } else { + flecs_json_serialize_refs_idr(world, buf, idr); + } + } + + flecs_json_object_pop(buf); + + return 0; } -/** - * @file datastructures/stack_allocator.c - * @brief Stack allocator. - * - * The stack allocator enables pushing and popping values to a stack, and has - * a lower overhead when compared to block allocators. A stack allocator is a - * good fit for small temporary allocations. - * - * The stack allocator allocates memory in pages. If the requested size of an - * allocation exceeds the page size, a regular allocator is used instead. - */ +#ifdef FLECS_ALERTS +static +int flecs_json_serialize_entity_alerts( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity, + const EcsAlertsActive *alerts, + bool self) +{ + ecs_assert(alerts != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_map_iter_t it = ecs_map_iter(&alerts->alerts); + while (ecs_map_next(&it)) { + flecs_json_next(buf); + flecs_json_object_push(buf); + ecs_entity_t ai = ecs_map_value(&it); + char *alert_name = ecs_get_path(world, ai); + flecs_json_memberl(buf, "alert"); + flecs_json_string(buf, alert_name); + ecs_os_free(alert_name); -#define FLECS_STACK_PAGE_OFFSET ECS_ALIGN(ECS_SIZEOF(ecs_stack_page_t), 16) + ecs_entity_t severity_id = ecs_get_target( + world, ai, ecs_id(EcsAlert), 0); + const char *severity = ecs_get_name(world, severity_id); -int64_t ecs_stack_allocator_alloc_count = 0; -int64_t ecs_stack_allocator_free_count = 0; + const EcsAlertInstance *alert = ecs_get( + world, ai, EcsAlertInstance); + if (alert) { + if (alert->message) { + flecs_json_memberl(buf, "message"); + flecs_json_string(buf, alert->message); + } + flecs_json_memberl(buf, "severity"); + flecs_json_string(buf, severity); + + if (!self) { + char *path = ecs_get_path(world, entity); + flecs_json_memberl(buf, "path"); + flecs_json_string(buf, path); + ecs_os_free(path); + } + } + flecs_json_object_pop(buf); + } -static -ecs_stack_page_t* flecs_stack_page_new(uint32_t page_id) { - ecs_stack_page_t *result = ecs_os_malloc( - FLECS_STACK_PAGE_OFFSET + ECS_STACK_PAGE_SIZE); - result->data = ECS_OFFSET(result, FLECS_STACK_PAGE_OFFSET); - result->next = NULL; - result->id = page_id + 1; - ecs_os_linc(&ecs_stack_allocator_alloc_count); - return result; + return 0; } -void* flecs_stack_alloc( - ecs_stack_t *stack, - ecs_size_t size, - ecs_size_t align) +static +int flecs_json_serialize_children_alerts( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity) { - ecs_stack_page_t *page = stack->tail_page; - if (page == &stack->first && !page->data) { - page->data = ecs_os_malloc(ECS_STACK_PAGE_SIZE); - ecs_os_linc(&ecs_stack_allocator_alloc_count); - } + ecs_query_t *q = ecs_query(ECS_CONST_CAST(ecs_world_t*, world), { + .terms = {{ .id = ecs_pair(EcsChildOf, entity) }} + }); - int16_t sp = flecs_ito(int16_t, ECS_ALIGN(page->sp, align)); - int16_t next_sp = flecs_ito(int16_t, sp + size); - void *result = NULL; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + EcsAlertsActive *alerts = ecs_table_get_id( + world, it.table, ecs_id(EcsAlertsActive), it.offset); - if (next_sp > ECS_STACK_PAGE_SIZE) { - if (size > ECS_STACK_PAGE_SIZE) { - result = ecs_os_malloc(size); /* Too large for page */ - goto done; - } + int32_t i; + for (i = 0; i < it.count; i ++) { + ecs_entity_t child = it.entities[i]; + if (alerts) { + if (flecs_json_serialize_entity_alerts( + world, buf, child, &alerts[i], false)) + { + goto error; + } + } - if (page->next) { - page = page->next; - } else { - page = page->next = flecs_stack_page_new(page->id); + ecs_record_t *r = flecs_entities_get(world, it.entities[i]); + if (r->row & EcsEntityIsTraversable) { + if (flecs_json_serialize_children_alerts( + world, buf, child)) + { + goto error; + } + } } - sp = 0; - next_sp = flecs_ito(int16_t, size); - stack->tail_page = page; } - page->sp = next_sp; - result = ECS_OFFSET(page->data, sp); + ecs_query_fini(q); -done: -#ifdef FLECS_SANITIZE - ecs_os_memset(result, 0xAA, size); -#endif - return result; + return 0; +error: + return -1; } +#endif -void* flecs_stack_calloc( - ecs_stack_t *stack, - ecs_size_t size, - ecs_size_t align) +int flecs_json_serialize_alerts( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity) { - void *ptr = flecs_stack_alloc(stack, size, align); - ecs_os_memset(ptr, 0, size); - return ptr; -} + (void)world; + (void)buf; + (void)entity; -void flecs_stack_free( - void *ptr, - ecs_size_t size) -{ - if (size > ECS_STACK_PAGE_SIZE) { - ecs_os_free(ptr); +#ifdef FLECS_ALERTS + if (!ecs_id(EcsAlertsActive)) { + return 0; /* Alert module not imported */ + } + + flecs_json_memberl(buf, "alerts"); + flecs_json_array_push(buf); + const EcsAlertsActive *alerts = ecs_get(world, entity, EcsAlertsActive); + if (alerts) { + flecs_json_serialize_entity_alerts(world, buf, entity, alerts, true); } + flecs_json_serialize_children_alerts(world, buf, entity); + flecs_json_array_pop(buf); +#endif + return 0; } -ecs_stack_cursor_t* flecs_stack_get_cursor( - ecs_stack_t *stack) +bool flecs_json_serialize_get_value_ctx( + const ecs_world_t *world, + ecs_id_t id, + ecs_json_value_ser_ctx_t *ctx, + const ecs_iter_to_json_desc_t *desc) { - ecs_stack_page_t *page = stack->tail_page; - int16_t sp = stack->tail_page->sp; - ecs_stack_cursor_t *result = flecs_stack_alloc_t(stack, ecs_stack_cursor_t); - result->page = page; - result->sp = sp; - result->is_free = false; + if (!id) { + return false; + } -#ifdef FLECS_DEBUG - ++ stack->cursor_count; - result->owner = stack; -#endif + if (!ctx->initialized) { + ctx->initialized = true; - result->prev = stack->tail_cursor; - stack->tail_cursor = result; - return result; + ecs_strbuf_t idlbl = ECS_STRBUF_INIT; + flecs_json_id_member(&idlbl, world, id, + desc ? desc->serialize_full_paths : true); + ctx->id_label = ecs_strbuf_get(&idlbl); + + ecs_entity_t type = ecs_get_typeid(world, id); + if (!type) { + return false; + } + + ctx->type = type; + ctx->ser = ecs_get(world, type, EcsTypeSerializer); + if (!ctx->ser) { + return false; + } + + return true; + } else { + return ctx->ser != NULL; + } } -void flecs_stack_restore_cursor( - ecs_stack_t *stack, - ecs_stack_cursor_t *cursor) +void flecs_json_serialize_iter_this( + const ecs_iter_t *it, + const char *parent_path, + const ecs_json_this_data_t *this_data, + int32_t row, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) { - if (!cursor) { - return; + ecs_assert(row < it->count, ECS_INTERNAL_ERROR, NULL); + + if (parent_path) { + flecs_json_memberl(buf, "parent"); + flecs_json_string(buf, parent_path); } - ecs_dbg_assert(stack == cursor->owner, ECS_INVALID_OPERATION, NULL); - ecs_dbg_assert(stack->cursor_count > 0, ECS_DOUBLE_FREE, NULL); - ecs_assert(cursor->is_free == false, ECS_DOUBLE_FREE, NULL); + flecs_json_memberl(buf, "name"); + if (this_data->names) { + flecs_json_string(buf, this_data->names[row].value); + } else { + ecs_strbuf_appendlit(buf, "\"#"); + ecs_strbuf_appendint(buf, flecs_uto(int64_t, + (uint32_t)it->entities[row])); + ecs_strbuf_appendlit(buf, "\""); + } - cursor->is_free = true; + if (desc && desc->serialize_entity_ids) { + flecs_json_memberl(buf, "id"); + flecs_json_u32(buf, (uint32_t)this_data->ids[row]); + } -#ifdef FLECS_DEBUG - -- stack->cursor_count; -#endif +#ifdef FLECS_DOC + if (desc && desc->serialize_doc) { + flecs_json_memberl(buf, "doc"); + flecs_json_object_push(buf); + if (this_data->label) { + flecs_json_memberl(buf, "label"); + flecs_json_string_escape(buf, this_data->label[row].value); + } else { + flecs_json_memberl(buf, "label"); + if (this_data->names) { + flecs_json_string(buf, this_data->names[row].value); + } else { + ecs_strbuf_appendlit(buf, "\"#"); + ecs_strbuf_appendint(buf, flecs_uto(int64_t, + (uint32_t)it->entities[row])); + ecs_strbuf_appendlit(buf, "\""); + } + } - /* If cursor is not the last on the stack no memory should be freed */ - if (cursor != stack->tail_cursor) { - return; - } + if (this_data->brief) { + flecs_json_memberl(buf, "brief"); + flecs_json_string_escape(buf, this_data->brief[row].value); + } - /* Iterate freed cursors to know how much memory we can free */ - do { - ecs_stack_cursor_t* prev = cursor->prev; - if (!prev || !prev->is_free) { - break; /* Found active cursor, free up until this point */ + if (this_data->detail) { + flecs_json_memberl(buf, "detail"); + flecs_json_string_escape(buf, this_data->detail[row].value); } - cursor = prev; - } while (cursor); - stack->tail_cursor = cursor->prev; - stack->tail_page = cursor->page; - stack->tail_page->sp = cursor->sp; + if (this_data->color) { + flecs_json_memberl(buf, "color"); + flecs_json_string_escape(buf, this_data->color[row].value); + } - /* If the cursor count is zero, stack should be empty - * if the cursor count is non-zero, stack should not be empty */ - ecs_dbg_assert((stack->cursor_count == 0) == - (stack->tail_page == &stack->first && stack->tail_page->sp == 0), - ECS_LEAK_DETECTED, NULL); -} + if (this_data->link) { + flecs_json_memberl(buf, "link"); + flecs_json_string_escape(buf, this_data->link[row].value); + } -void flecs_stack_reset( - ecs_stack_t *stack) -{ - ecs_dbg_assert(stack->cursor_count == 0, ECS_LEAK_DETECTED, NULL); - stack->tail_page = &stack->first; - stack->first.sp = 0; - stack->tail_cursor = NULL; -} + flecs_json_object_pop(buf); + } +#endif -void flecs_stack_init( - ecs_stack_t *stack) -{ - ecs_os_zeromem(stack); - stack->tail_page = &stack->first; - stack->first.data = NULL; + if (this_data->has_alerts) { + flecs_json_memberl(buf, "has_alerts"); + flecs_json_true(buf); + } } -void flecs_stack_fini( - ecs_stack_t *stack) +int flecs_json_serialize_iter_result( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + ecs_json_ser_ctx_t *ser_ctx) { - ecs_stack_page_t *next, *cur = &stack->first; - ecs_dbg_assert(stack->cursor_count == 0, ECS_LEAK_DETECTED, NULL); - ecs_assert(stack->tail_page == &stack->first, ECS_LEAK_DETECTED, NULL); - ecs_assert(stack->tail_page->sp == 0, ECS_LEAK_DETECTED, NULL); + char *parent_path = NULL; + ecs_json_this_data_t this_data = {0}; - do { - next = cur->next; - if (cur == &stack->first) { - if (cur->data) { - ecs_os_linc(&ecs_stack_allocator_free_count); + int32_t count = it->count; + bool has_this = true; + if (!count) { + count = 1; /* Query without this variable */ + has_this = false; + } else { + ecs_table_t *table = it->table; + if (table) { + this_data.ids = &ecs_table_entities(table)[it->offset]; + + /* Get path to parent once for entire table */ + if (table->flags & EcsTableHasChildOf) { + const ecs_table_record_t *tr = flecs_table_record_get( + world, table, ecs_pair(EcsChildOf, EcsWildcard)); + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t parent = ecs_pair_second( + world, table->type.array[tr->index]); + parent_path = ecs_get_path_w_sep(world, 0, parent, ".", ""); } - ecs_os_free(cur->data); + + /* Fetch name column once vs. calling ecs_get_name for each row */ + if (table->flags & EcsTableHasName) { + this_data.names = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsIdentifier, EcsName), it->offset); + } + + /* Get entity labels */ +#ifdef FLECS_DOC + if (desc && desc->serialize_doc) { + this_data.label = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsName), it->offset); + this_data.brief = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsDocBrief), it->offset); + this_data.detail = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsDocDetail), it->offset); + this_data.color = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsDocColor), it->offset); + this_data.link = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsDocLink), it->offset); + } +#endif + +#ifdef FLECS_ALERTS + if (it->table && (ecs_id(EcsAlertsActive) != 0)) { + /* Only add field if alerts addon is imported */ + if (ecs_table_has_id(world, table, ecs_id(EcsAlertsActive))) { + this_data.has_alerts = true; + } + } +#endif } else { - ecs_os_linc(&ecs_stack_allocator_free_count); - ecs_os_free(cur); + /* Very rare case, but could happen if someone's using an iterator + * to return empty entities. */ } - } while ((cur = next)); -} + } -/** - * @file datastructures/strbuf.c - * @brief Utility for constructing strings. - * - * A buffer builds up a list of elements which individually can be up to N bytes - * large. While appending, data is added to these elements. More elements are - * added on the fly when needed. When an application calls ecs_strbuf_get, all - * elements are combined in one string and the element administration is freed. - * - * This approach prevents reallocs of large blocks of memory, and therefore - * copying large blocks of memory when appending to a large buffer. A buffer - * preallocates some memory for the element overhead so that for small strings - * there is hardly any overhead, while for large strings the overhead is offset - * by the reduced time spent on copying memory. - * - * The functionality provided by strbuf is similar to std::stringstream. - */ + if (desc && desc->serialize_table) { + if (flecs_json_serialize_iter_result_table(world, it, buf, + desc, count, has_this, parent_path, &this_data)) + { + goto error; + } + } else { + if (flecs_json_serialize_iter_result_query(world, it, buf, ser_ctx, + desc, count, has_this, parent_path, &this_data)) + { + goto error; + } + } -#include + ecs_os_free(parent_path); + return 0; +error: + ecs_os_free(parent_path); + return -1; +} + +#endif /** - * stm32tpl -- STM32 C++ Template Peripheral Library - * Visit https://github.com/antongus/stm32tpl for new versions - * - * Copyright (c) 2011-2020 Anton B. Gusev aka AHTOXA + * @file addons/json/serialize_iter_result_query.c + * @brief Serialize matched query data of result. */ -#define MAX_PRECISION (10) -#define EXP_THRESHOLD (3) -#define INT64_MAX_F ((double)INT64_MAX) -static const double rounders[MAX_PRECISION + 1] = -{ - 0.5, // 0 - 0.05, // 1 - 0.005, // 2 - 0.0005, // 3 - 0.00005, // 4 - 0.000005, // 5 - 0.0000005, // 6 - 0.00000005, // 7 - 0.000000005, // 8 - 0.0000000005, // 9 - 0.00000000005 // 10 -}; +#ifdef FLECS_JSON static -char* flecs_strbuf_itoa( - char *buf, - int64_t v) +bool flecs_json_serialize_iter_result_is_set( + const ecs_iter_t *it, + ecs_strbuf_t *buf) { - char *ptr = buf; - char * p1; - char c; + if (!(it->flags & EcsIterHasCondSet)) { + return false; + } - if (!v) { - *ptr++ = '0'; - } else { - if (v < 0) { - ptr[0] = '-'; - ptr ++; - v *= -1; - } + flecs_json_memberl(buf, "is_set"); + flecs_json_array_push(buf); - char *p = ptr; - while (v) { - int64_t vdiv = v / 10; - int64_t vmod = v - (vdiv * 10); - p[0] = (char)('0' + vmod); - p ++; - v = vdiv; - } + int8_t i, count = it->field_count; + for (i = 0; i < count; i ++) { + ecs_strbuf_list_next(buf); + if (ecs_field_is_set(it, i)) { + flecs_json_true(buf); + } else { + flecs_json_false(buf); + } + } - p1 = p; + flecs_json_array_pop(buf); - while (p > ptr) { - c = *--p; - *p = *ptr; - *ptr++ = c; - } - ptr = p1; - } - return ptr; + return true; } static -int flecs_strbuf_ftoa( - ecs_strbuf_t *out, - double f, - int precision, - char nan_delim) +bool flecs_json_serialize_iter_result_ids( + const ecs_iter_t *it, + ecs_strbuf_t *buf) { - char buf[64]; - char * ptr = buf; - char c; - int64_t intPart; - int64_t exp = 0; - - if (ecs_os_isnan(f)) { - if (nan_delim) { - ecs_strbuf_appendch(out, nan_delim); - ecs_strbuf_appendlit(out, "NaN"); - return ecs_strbuf_appendch(out, nan_delim); - } else { - return ecs_strbuf_appendlit(out, "NaN"); - } - } - if (ecs_os_isinf(f)) { - if (nan_delim) { - ecs_strbuf_appendch(out, nan_delim); - ecs_strbuf_appendlit(out, "Inf"); - return ecs_strbuf_appendch(out, nan_delim); - } else { - return ecs_strbuf_appendlit(out, "Inf"); - } + const ecs_query_t *q = it->query; + if (!q) { + return false; } - if (precision > MAX_PRECISION) { - precision = MAX_PRECISION; + ecs_world_t *world = it->world; + int16_t f, field_count = flecs_ito(int16_t, it->field_count); + uint32_t field_mask = (uint32_t)((1llu << field_count) - 1); + + if (q->static_id_fields == field_mask) { + /* All matched ids are static, nothing to serialize */ + return false; } - if (f < 0) { - f = -f; - *ptr++ = '-'; - } + flecs_json_memberl(buf, "ids"); + flecs_json_array_push(buf); - if (precision < 0) { - if (f < 1.0) precision = 6; - else if (f < 10.0) precision = 5; - else if (f < 100.0) precision = 4; - else if (f < 1000.0) precision = 3; - else if (f < 10000.0) precision = 2; - else if (f < 100000.0) precision = 1; - else precision = 0; - } + for (f = 0; f < field_count; f ++) { + ecs_termset_t field_bit = (ecs_termset_t)(1u << f); - if (precision) { - f += rounders[precision]; - } + if (!(it->set_fields & field_bit)) { + /* Don't serialize ids for fields that aren't set */ + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } - /* Make sure that number can be represented as 64bit int, increase exp */ - while (f > INT64_MAX_F) { - f /= 1000 * 1000 * 1000; - exp += 9; - } + if (q->static_id_fields & field_bit) { + /* Only add non-static ids to save bandwidth/performance */ + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } - intPart = (int64_t)f; - f -= (double)intPart; + flecs_json_next(buf); + flecs_json_id(buf, world, it->ids[f]); + } - ptr = flecs_strbuf_itoa(ptr, intPart); + flecs_json_array_pop(buf); - if (precision) { - *ptr++ = '.'; - while (precision--) { - f *= 10.0; - c = (char)f; - *ptr++ = (char)('0' + c); - f -= c; - } - } - *ptr = 0; + return true; +} - /* Remove trailing 0s */ - while ((&ptr[-1] != buf) && (ptr[-1] == '0')) { - ptr[-1] = '\0'; - ptr --; - } - if (ptr != buf && ptr[-1] == '.') { - ptr[-1] = '\0'; - ptr --; +static +bool flecs_json_serialize_iter_result_sources( + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + const ecs_query_t *q = it->query; + if (!q) { + return false; } - /* If 0s before . exceed threshold, convert to exponent to save space - * without losing precision. */ - char *cur = ptr; - while ((&cur[-1] != buf) && (cur[-1] == '0')) { - cur --; + ecs_world_t *world = it->world; + int32_t f, field_count = it->field_count; + + for (f = 0; f < field_count; f ++) { + if (it->sources[f]) { + break; + } } - if (exp || ((ptr - cur) > EXP_THRESHOLD)) { - cur[0] = '\0'; - exp += (ptr - cur); - ptr = cur; + if (f == field_count) { + /* All fields are matched on $this */ + return false; } - if (exp) { - char *p1 = &buf[1]; - if (nan_delim) { - ecs_os_memmove(buf + 1, buf, 1 + (ptr - buf)); - buf[0] = nan_delim; - p1 ++; - } + flecs_json_memberl(buf, "sources"); + flecs_json_array_push(buf); - /* Make sure that exp starts after first character */ - c = p1[0]; + for (f = 0; f < field_count; f ++) { + ecs_termset_t field_bit = (ecs_termset_t)(1u << f); - if (c) { - p1[0] = '.'; - do { - char t = (++p1)[0]; - if (t == '.') { - exp ++; - p1 --; - break; - } - p1[0] = c; - c = t; - exp ++; - } while (c); - ptr = p1 + 1; - } else { - ptr = p1; + if (!(it->set_fields & field_bit)) { + /* Don't serialize source for fields that aren't set */ + ecs_strbuf_list_appendlit(buf, "0"); + continue; } - - ptr[0] = 'e'; - ptr = flecs_strbuf_itoa(ptr + 1, exp); - - if (nan_delim) { - ptr[0] = nan_delim; - ptr ++; + if (!it->sources[f]) { + /* Don't serialize source for fields that have $this source */ + ecs_strbuf_list_appendlit(buf, "0"); + continue; } - ptr[0] = '\0'; - } - - return ecs_strbuf_appendstrn(out, buf, (int32_t)(ptr - buf)); -} - -/* Add an extra element to the buffer */ -static -void flecs_strbuf_grow( - ecs_strbuf_t *b) -{ - /* Allocate new element */ - ecs_strbuf_element_embedded *e = ecs_os_malloc_t(ecs_strbuf_element_embedded); - b->size += b->current->pos; - b->current->next = (ecs_strbuf_element*)e; - b->current = (ecs_strbuf_element*)e; - b->elementCount ++; - e->super.buffer_embedded = true; - e->super.buf = e->buf; - e->super.pos = 0; - e->super.next = NULL; -} - -/* Add an extra dynamic element */ -static -void flecs_strbuf_grow_str( - ecs_strbuf_t *b, - const char *str, - char *alloc_str, - int32_t size) -{ - /* Allocate new element */ - ecs_strbuf_element_str *e = ecs_os_malloc_t(ecs_strbuf_element_str); - b->size += b->current->pos; - b->current->next = (ecs_strbuf_element*)e; - b->current = (ecs_strbuf_element*)e; - b->elementCount ++; - e->super.buffer_embedded = false; - e->super.pos = size ? size : (int32_t)ecs_os_strlen(str); - e->super.next = NULL; - e->super.buf = ECS_CONST_CAST(char*, str); - e->alloc_str = alloc_str; -} - -static -char* flecs_strbuf_ptr( - ecs_strbuf_t *b) -{ - if (b->buf) { - return &b->buf[b->current->pos]; - } else { - return &b->current->buf[b->current->pos]; + flecs_json_next(buf); + flecs_json_path(buf, world, it->sources[f]); } -} -/* Compute the amount of space left in the current element */ -static -int32_t flecs_strbuf_memLeftInCurrentElement( - ecs_strbuf_t *b) -{ - if (b->current->buffer_embedded) { - return ECS_STRBUF_ELEMENT_SIZE - b->current->pos; - } else { - return 0; - } + flecs_json_array_pop(buf); + + return true; } -/* Compute the amount of space left */ static -int32_t flecs_strbuf_memLeft( - ecs_strbuf_t *b) +bool flecs_json_serialize_common_for_table( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) { - if (b->max) { - return b->max - b->size - b->current->pos; - } else { - return INT_MAX; + ecs_strbuf_list_push(buf, "", ","); + flecs_json_serialize_vars(world, it, buf, desc); + + bool result = false; + if (!desc || desc->serialize_fields) { + ecs_strbuf_list_appendlit(buf, "\"fields\":"); + flecs_json_object_push(buf); + result |= flecs_json_serialize_iter_result_is_set(it, buf); + result |= flecs_json_serialize_iter_result_ids(it, buf); + result |= flecs_json_serialize_iter_result_sources(it, buf); } + + ecs_strbuf_list_pop(buf, ""); + return result; } static -void flecs_strbuf_init( - ecs_strbuf_t *b) +int flecs_json_serialize_iter_result_field_values( + const ecs_world_t *world, + const ecs_iter_t *it, + int32_t i, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + ecs_json_ser_ctx_t *ser_ctx) { - /* Initialize buffer structure only once */ - if (!b->elementCount) { - b->size = 0; - b->firstElement.super.next = NULL; - b->firstElement.super.pos = 0; - b->firstElement.super.buffer_embedded = true; - b->firstElement.super.buf = b->firstElement.buf; - b->elementCount ++; - b->current = (ecs_strbuf_element*)&b->firstElement; + int8_t f, field_count = it->field_count; + if (!field_count) { + return 0; } -} -/* Append a format string to a buffer */ -static -bool flecs_strbuf_vappend( - ecs_strbuf_t *b, - const char* str, - va_list args) -{ - bool result = true; - va_list arg_cpy; + ecs_strbuf_appendlit(buf, "\"values\":"); + flecs_json_array_push(buf); - if (!str) { - return result; + ecs_termset_t fields = it->set_fields; + if (it->query) { + fields &= it->query->data_fields; } - flecs_strbuf_init(b); + ecs_termset_t row_fields = it->query ? it->query->row_fields : 0; - int32_t memLeftInElement = flecs_strbuf_memLeftInCurrentElement(b); - int32_t memLeft = flecs_strbuf_memLeft(b); - - if (!memLeft) { - return false; - } + for (f = 0; f < field_count; f ++) { + ecs_termset_t field_bit = (ecs_termset_t)(1u << f); + if (!(fields & field_bit)) { + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } - /* Compute the memory required to add the string to the buffer. If user - * provided buffer, use space left in buffer, otherwise use space left in - * current element. */ - int32_t max_copy = b->buf ? memLeft : memLeftInElement; - int32_t memRequired; + ecs_json_value_ser_ctx_t *value_ctx = &ser_ctx->value_ctx[f]; + if (!flecs_json_serialize_get_value_ctx( + world, it->ids[f], value_ctx, desc)) + { + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } - va_copy(arg_cpy, args); - memRequired = vsnprintf( - flecs_strbuf_ptr(b), (size_t)(max_copy + 1), str, args); - - ecs_assert(memRequired != -1, ECS_INTERNAL_ERROR, NULL); - - if (memRequired <= memLeftInElement) { - /* Element was large enough to fit string */ - b->current->pos += memRequired; - } else if ((memRequired - memLeftInElement) < memLeft) { - /* If string is a format string, a new buffer of size memRequired is - * needed to re-evaluate the format string and only use the part that - * wasn't already copied to the previous element */ - if (memRequired <= ECS_STRBUF_ELEMENT_SIZE) { - /* Resulting string fits in standard-size buffer. Note that the - * entire string needs to fit, not just the remainder, as the - * format string cannot be partially evaluated */ - flecs_strbuf_grow(b); + void *ptr; + if (row_fields & field_bit) { + ptr = ecs_field_at_w_size(it, 0, f, i); + } else { + ecs_size_t size = it->sizes[f]; + ptr = ecs_field_w_size(it, flecs_itosize(size), f); - /* Copy entire string to new buffer */ - ecs_os_vsprintf(flecs_strbuf_ptr(b), str, arg_cpy); + if (!ptr) { + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } - /* Ignore the part of the string that was copied into the - * previous buffer. The string copied into the new buffer could - * be memmoved so that only the remainder is left, but that is - * most likely more expensive than just keeping the entire - * string. */ + if (!it->sources[f]) { + ptr = ECS_ELEM(ptr, size, i); + } + } - /* Update position in buffer */ - b->current->pos += memRequired; - } else { - /* Resulting string does not fit in standard-size buffer. - * Allocate a new buffer that can hold the entire string. */ - char *dst = ecs_os_malloc(memRequired + 1); - ecs_os_vsprintf(dst, str, arg_cpy); - flecs_strbuf_grow_str(b, dst, dst, memRequired); + flecs_json_next(buf); + if (flecs_json_ser_type(world, &value_ctx->ser->ops, ptr, buf) != 0) { + return -1; } } - va_end(arg_cpy); + flecs_json_array_pop(buf); - return flecs_strbuf_memLeft(b) > 0; + return 0; } -static -bool flecs_strbuf_appendstr( - ecs_strbuf_t *b, - const char* str, - int n) -{ - flecs_strbuf_init(b); - - int32_t memLeftInElement = flecs_strbuf_memLeftInCurrentElement(b); - int32_t memLeft = flecs_strbuf_memLeft(b); - if (memLeft <= 0) { - return false; +int flecs_json_serialize_iter_result_query( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ser_ctx, + const ecs_iter_to_json_desc_t *desc, + int32_t count, + bool has_this, + const char *parent_path, + const ecs_json_this_data_t *this_data) +{ + /* Serialize tags, pairs, vars once, since they're the same for each row */ + ecs_strbuf_t common_data_buf = ECS_STRBUF_INIT; + bool common_field_data = flecs_json_serialize_common_for_table( + world, it, &common_data_buf, desc); + int32_t common_data_len = ecs_strbuf_written(&common_data_buf); + char *common_data = NULL; + if (!common_data_len) { + ecs_strbuf_reset(&common_data_buf); + } else { + common_data = ecs_strbuf_get(&common_data_buf); } - /* Never write more than what the buffer can store */ - if (n > memLeft) { - n = memLeft; - } + int32_t i; + for (i = 0; i < count; i ++) { + flecs_json_next(buf); + flecs_json_object_push(buf); - if (n <= memLeftInElement) { - /* Element was large enough to fit string */ - ecs_os_strncpy(flecs_strbuf_ptr(b), str, n); - b->current->pos += n; - } else if ((n - memLeftInElement) < memLeft) { - ecs_os_strncpy(flecs_strbuf_ptr(b), str, memLeftInElement); + if (has_this) { + flecs_json_serialize_iter_this( + it, parent_path, this_data, i, buf, desc); + } - /* Element was not large enough, but buffer still has space */ - b->current->pos += memLeftInElement; - n -= memLeftInElement; + if (common_data) { + ecs_strbuf_list_appendstrn(buf, + common_data, common_data_len); + } - /* Current element was too small, copy remainder into new element */ - if (n < ECS_STRBUF_ELEMENT_SIZE) { - /* A standard-size buffer is large enough for the new string */ - flecs_strbuf_grow(b); + if (!desc || desc->serialize_fields) { + bool has_values = !desc || desc->serialize_values; + if (it->flags & EcsIterNoData || !it->field_count) { + has_values = false; + } - /* Copy the remainder to the new buffer */ - if (n) { - /* If a max number of characters to write is set, only a - * subset of the string should be copied to the buffer */ - ecs_os_strncpy( - flecs_strbuf_ptr(b), - str + memLeftInElement, - (size_t)n); - } else { - ecs_os_strcpy(flecs_strbuf_ptr(b), str + memLeftInElement); + const ecs_query_t *q = it->query; + if (q && !q->data_fields) { + has_values = false; } - /* Update to number of characters copied to new buffer */ - b->current->pos += n; - } else { - /* String doesn't fit in a single element, strdup */ - char *remainder = ecs_os_strdup(str + memLeftInElement); - flecs_strbuf_grow_str(b, remainder, remainder, n); - } - } else { - /* Buffer max has been reached */ - return false; - } + if (has_values) { + if (common_field_data) { + flecs_json_next(buf); + } - return flecs_strbuf_memLeft(b) > 0; -} + if (flecs_json_serialize_iter_result_field_values( + world, it, i, buf, desc, ser_ctx)) + { + ecs_os_free(common_data); + return -1; + } + } -static -bool flecs_strbuf_appendch( - ecs_strbuf_t *b, - char ch) -{ - flecs_strbuf_init(b); + ecs_strbuf_appendstr(buf, "}"); // "fields": { + } - int32_t memLeftInElement = flecs_strbuf_memLeftInCurrentElement(b); - int32_t memLeft = flecs_strbuf_memLeft(b); - if (memLeft <= 0) { - return false; + flecs_json_object_pop(buf); } - if (memLeftInElement) { - /* Element was large enough to fit string */ - flecs_strbuf_ptr(b)[0] = ch; - b->current->pos ++; - } else { - flecs_strbuf_grow(b); - flecs_strbuf_ptr(b)[0] = ch; - b->current->pos ++; - } + ecs_os_free(common_data); - return flecs_strbuf_memLeft(b) > 0; + return 0; } -bool ecs_strbuf_vappend( - ecs_strbuf_t *b, - const char* fmt, - va_list args) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(fmt != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_vappend(b, fmt, args); -} +#endif -bool ecs_strbuf_append( - ecs_strbuf_t *b, - const char* fmt, - ...) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(fmt != NULL, ECS_INVALID_PARAMETER, NULL); +/** + * @file addons/json/serialize_iter_result_table.c + * @brief Serialize all components of matched entity. + */ - va_list args; - va_start(args, fmt); - bool result = flecs_strbuf_vappend(b, fmt, args); - va_end(args); - return result; -} +#ifdef FLECS_JSON -bool ecs_strbuf_appendstrn( - ecs_strbuf_t *b, - const char* str, - int32_t len) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_appendstr(b, str, len); -} +#define FLECS_JSON_MAX_TABLE_COMPONENTS (256) -bool ecs_strbuf_appendch( - ecs_strbuf_t *b, - char ch) +bool flecs_json_is_builtin( + ecs_id_t id) { - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_appendch(b, ch); + if (ECS_IS_PAIR(id)) { + if (ECS_PAIR_FIRST(id) == EcsChildOf) { + return true; + } + if (id == ecs_pair_t(EcsIdentifier, EcsName)) { + return true; + } + } + return false; } -bool ecs_strbuf_appendint( - ecs_strbuf_t *b, - int64_t v) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - char numbuf[32]; - char *ptr = flecs_strbuf_itoa(numbuf, v); - return ecs_strbuf_appendstrn(b, numbuf, flecs_ito(int32_t, ptr - numbuf)); -} +static +bool flecs_json_serialize_table_type_info( + const ecs_world_t *world, + ecs_table_t *table, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + flecs_json_memberl(buf, "type_info"); + flecs_json_object_push(buf); -bool ecs_strbuf_appendflt( - ecs_strbuf_t *b, - double flt, - char nan_delim) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_ftoa(b, flt, 10, nan_delim); -} + int32_t i, type_count = table->type.count; + for (i = 0; i < type_count; i ++) { + const ecs_table_record_t *tr = &table->_->records[i]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_id_t id = table->type.array[i]; + if (!(idr->flags & EcsIdIsSparse) && + (!table->column_map || (table->column_map[i] == -1))) + { + continue; + } -bool ecs_strbuf_appendbool( - ecs_strbuf_t *buffer, - bool v) -{ - ecs_assert(buffer != NULL, ECS_INVALID_PARAMETER, NULL); - if (v) { - return ecs_strbuf_appendlit(buffer, "true"); - } else { - return ecs_strbuf_appendlit(buffer, "false"); - } -} + if (!desc || !desc->serialize_builtin) { + if (flecs_json_is_builtin(id)) { + continue; + } + } -bool ecs_strbuf_appendstr_zerocpy( - ecs_strbuf_t *b, - char* str) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_strbuf_init(b); - flecs_strbuf_grow_str(b, str, str, 0); - return true; -} + const ecs_type_info_t *ti = idr->type_info; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); -bool ecs_strbuf_appendstr_zerocpyn( - ecs_strbuf_t *b, - char *str, - int32_t n) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_strbuf_init(b); - flecs_strbuf_grow_str(b, str, str, n); - return true; -} + flecs_json_next(buf); + ecs_strbuf_appendlit(buf, "\""); + flecs_json_id_member(buf, world, id, desc->serialize_full_paths); + ecs_strbuf_appendlit(buf, "\":"); -bool ecs_strbuf_appendstr_zerocpy_const( - ecs_strbuf_t *b, - const char* str) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - /* Removes const modifier, but logic prevents changing / delete string */ - flecs_strbuf_init(b); - flecs_strbuf_grow_str(b, str, NULL, 0); - return true; -} + ecs_type_info_to_json_buf(world, ti->component, buf); + } + + flecs_json_object_pop(buf); -bool ecs_strbuf_appendstr_zerocpyn_const( - ecs_strbuf_t *b, - const char *str, - int32_t n) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - /* Removes const modifier, but logic prevents changing / delete string */ - flecs_strbuf_init(b); - flecs_strbuf_grow_str(b, str, NULL, n); return true; } -bool ecs_strbuf_appendstr( - ecs_strbuf_t *b, - const char* str) +static +bool flecs_json_serialize_table_tags( + const ecs_world_t *world, + const ecs_table_t *table, + const ecs_table_t *src_table, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) { - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_appendstr(b, str, ecs_os_strlen(str)); -} + int16_t f, type_count = flecs_ito(int16_t, table->type.count); + ecs_id_t *ids = table->type.array; + int16_t *column_map = table->column_map; + + int32_t tag_count = 0; + ecs_table_record_t *trs = table->_->records; + for (f = 0; f < type_count; f ++) { + ecs_id_t id = ids[f]; + if (ECS_IS_PAIR(id)) { + continue; + } -bool ecs_strbuf_mergebuff( - ecs_strbuf_t *dst_buffer, - ecs_strbuf_t *src_buffer) -{ - if (src_buffer->elementCount) { - if (src_buffer->buf) { - return ecs_strbuf_appendstrn( - dst_buffer, src_buffer->buf, src_buffer->length); - } else { - ecs_strbuf_element *e = (ecs_strbuf_element*)&src_buffer->firstElement; + if (!desc || !desc->serialize_builtin) { + if (flecs_json_is_builtin(id)) { + continue; + } + } + + if (column_map && column_map[f] != -1) { + continue; /* Ignore components */ + } - /* Copy first element as it is inlined in the src buffer */ - ecs_strbuf_appendstrn(dst_buffer, e->buf, e->pos); + const ecs_table_record_t *tr = &trs[f]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; - while ((e = e->next)) { - dst_buffer->current->next = ecs_os_malloc(sizeof(ecs_strbuf_element)); - *dst_buffer->current->next = *e; + if (src_table) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; } } + if (idr->flags & EcsIdIsSparse) { + continue; + } - *src_buffer = ECS_STRBUF_INIT; + if (!tag_count) { + flecs_json_memberl(buf, "tags"); + flecs_json_array_push(buf); + } + + flecs_json_next(buf); + + ecs_strbuf_appendlit(buf, "\""); + flecs_json_id_member(buf, world, id, + desc ? desc->serialize_full_paths : true); + ecs_strbuf_appendlit(buf, "\""); + + tag_count ++; } - return true; + if (tag_count) { + flecs_json_array_pop(buf); + } + + return tag_count != 0; } -char* ecs_strbuf_get( - ecs_strbuf_t *b) +static +bool flecs_json_serialize_table_pairs( + const ecs_world_t *world, + const ecs_table_t *table, + const ecs_table_t *src_table, + int32_t row, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) { - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + int16_t f, type_count = flecs_ito(int16_t, table->type.count); + ecs_id_t *ids = table->type.array; + int16_t *column_map = table->column_map; + + int32_t pair_count = 0; + bool same_first = false; - char* result = NULL; - if (b->elementCount) { - if (b->buf) { - b->buf[b->current->pos] = '\0'; - result = ecs_os_strdup(b->buf); - } else { - void *next = NULL; - int32_t len = b->size + b->current->pos + 1; - ecs_strbuf_element *e = (ecs_strbuf_element*)&b->firstElement; + ecs_table_record_t *trs = table->_->records; + for (f = 0; f < type_count; f ++) { + ecs_id_t id = ids[f]; + if (!ECS_IS_PAIR(id)) { + continue; + } - result = ecs_os_malloc(len); - char* ptr = result; + if (!desc || !desc->serialize_builtin) { + if (flecs_json_is_builtin(id)) { + continue; + } + } - do { - ecs_os_memcpy(ptr, e->buf, e->pos); - ptr += e->pos; - next = e->next; - if (e != &b->firstElement.super) { - if (!e->buffer_embedded) { - ecs_os_free(((ecs_strbuf_element_str*)e)->alloc_str); - } - ecs_os_free(e); - } - } while ((e = next)); + if (column_map && column_map[f] != -1) { + continue; /* Ignore components */ + } + + const ecs_table_record_t *tr = &trs[f]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; - result[len - 1] = '\0'; - b->length = len; + if (src_table) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + } + if (idr->flags & EcsIdIsSparse) { + continue; } - } else { - result = NULL; - } - b->elementCount = 0; + ecs_entity_t first = flecs_entities_get_alive( + world, ECS_PAIR_FIRST(id)); - b->content = result; + if (!pair_count) { + flecs_json_memberl(buf, "pairs"); + flecs_json_object_push(buf); + } - return result; -} + ecs_entity_t second = flecs_entities_get_alive( + world, ECS_PAIR_SECOND(id)); -char *ecs_strbuf_get_small( - ecs_strbuf_t *b) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + bool is_last = f == (type_count - 1); + bool is_same = !is_last && + (ECS_PAIR_FIRST(ids[f + 1]) == ECS_PAIR_FIRST(id)); - int32_t written = ecs_strbuf_written(b); - ecs_assert(written <= ECS_STRBUF_ELEMENT_SIZE, ECS_INVALID_OPERATION, NULL); - char *buf = b->firstElement.buf; - buf[written] = '\0'; - return buf; -} + if (same_first && f && ECS_PAIR_FIRST(ids[f - 1]) != ECS_PAIR_FIRST(id)) { + /* New pair has different first elem, so close array */ + flecs_json_array_pop(buf); + same_first = false; + } -void ecs_strbuf_reset( - ecs_strbuf_t *b) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + if (!same_first) { + /* Only append pair label if we're not appending to array */ + flecs_json_next(buf); + flecs_json_path_or_label(buf, world, first, + desc ? desc->serialize_full_paths : true); + ecs_strbuf_appendlit(buf, ":"); - if (b->elementCount && !b->buf) { - void *next = NULL; - ecs_strbuf_element *e = (ecs_strbuf_element*)&b->firstElement; - do { - next = e->next; - if (e != (ecs_strbuf_element*)&b->firstElement) { - ecs_os_free(e); + /* Open array scope if this is a pair with multiple targets */ + if (is_same) { + flecs_json_array_push(buf); + same_first = true; } - } while ((e = next)); + } + if (same_first) { + flecs_json_next(buf); + } + + if (second == EcsUnion) { + ecs_assert(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); + ecs_entity_t e = ecs_table_entities(table)[row]; + second = ecs_get_target(world, e, first, 0); + } + + flecs_json_path_or_label(buf, world, second, + desc ? desc->serialize_full_paths : true); + + pair_count ++; } - *b = ECS_STRBUF_INIT; + if (same_first) { + flecs_json_array_pop(buf); + } + + if (pair_count) { + flecs_json_object_pop(buf); + } + + return pair_count != 0; } -void ecs_strbuf_list_push( - ecs_strbuf_t *b, - const char *list_open, - const char *separator) +static +int flecs_json_serialize_table_components( + const ecs_world_t *world, + ecs_table_t *table, + const ecs_table_t *src_table, + ecs_strbuf_t *buf, + ecs_json_value_ser_ctx_t *values_ctx, + const ecs_iter_to_json_desc_t *desc, + int32_t row, + int32_t *component_count) { - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(list_open != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(separator != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(b->list_sp >= 0, ECS_INVALID_OPERATION, NULL); - b->list_sp ++; - ecs_assert(b->list_sp < ECS_STRBUF_MAX_LIST_DEPTH, - ECS_INVALID_OPERATION, NULL); + int32_t i, count = table->type.count; + for (i = 0; i < count; i ++) { + if (component_count[0] == FLECS_JSON_MAX_TABLE_COMPONENTS) { + break; + } - b->list_stack[b->list_sp].count = 0; - b->list_stack[b->list_sp].separator = separator; + ecs_id_t id = table->type.array[i]; + if (!desc || !desc->serialize_builtin) { + if (flecs_json_is_builtin(id)) { + continue; + } + } - if (list_open) { - char ch = list_open[0]; - if (ch && !list_open[1]) { - ecs_strbuf_appendch(b, ch); + void *ptr; + const ecs_table_record_t *tr = &table->_->records[i]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + + if (src_table) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + } + + const ecs_type_info_t *ti; + int32_t column_index = table->column_map ? table->column_map[i] : -1; + if (column_index != -1) { + ecs_column_t *column = &table->data.columns[column_index]; + ti = column->ti; + ptr = ECS_ELEM(column->data, ti->size, row); } else { - ecs_strbuf_appendstr(b, list_open); + if (!(idr->flags & EcsIdIsSparse)) { + continue; + } + ecs_entity_t e = ecs_table_entities(table)[row]; + ptr = flecs_sparse_get_any(idr->sparse, 0, e); + ti = idr->type_info; } - } -} -void ecs_strbuf_list_pop( - ecs_strbuf_t *b, - const char *list_close) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(list_close != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(b->list_sp > 0, ECS_INVALID_OPERATION, NULL); + if (!ptr) { + continue; + } - b->list_sp --; - - if (list_close) { - char ch = list_close[0]; - if (ch && !list_close[1]) { - ecs_strbuf_appendch(b, list_close[0]); + if (!component_count[0]) { + flecs_json_memberl(buf, "components"); + flecs_json_object_push(buf); + } + + bool has_reflection; + const EcsTypeSerializer *type_ser; + if (values_ctx) { + ecs_json_value_ser_ctx_t *value_ctx = + &values_ctx[component_count[0]]; + has_reflection = flecs_json_serialize_get_value_ctx( + world, id, value_ctx, desc); + flecs_json_member(buf, value_ctx->id_label); + type_ser = value_ctx->ser; } else { - ecs_strbuf_appendstr(b, list_close); + ecs_strbuf_list_next(buf); + ecs_strbuf_appendlit(buf, "\""); + flecs_json_id_member(buf, world, id, + desc ? desc->serialize_full_paths : true); + ecs_strbuf_appendlit(buf, "\":"); + type_ser = NULL; + if (!desc || desc->serialize_values) { + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + type_ser = ecs_get(world, ti->component, EcsTypeSerializer); + } + has_reflection = type_ser != NULL; + } + + component_count[0] ++; + + if (has_reflection && (!desc || desc->serialize_values)) { + ecs_assert(type_ser != NULL, ECS_INTERNAL_ERROR, NULL); + if (flecs_json_ser_type( + world, &type_ser->ops, ptr, buf) != 0) + { + goto error; + } + } else { + ecs_strbuf_appendlit(buf, "null"); } } + + if (component_count[0]) { + flecs_json_object_pop(buf); + } + + return 0; +error: + return -1; } -void ecs_strbuf_list_next( - ecs_strbuf_t *b) +static +bool flecs_json_serialize_table_inherited_type( + const ecs_world_t *world, + ecs_table_t *table, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) { - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); + if (!(table->flags & EcsTableHasIsA)) { + return false; + } - int32_t list_sp = b->list_sp; - if (b->list_stack[list_sp].count != 0) { - const char *sep = b->list_stack[list_sp].separator; - if (sep && !sep[1]) { - ecs_strbuf_appendch(b, sep[0]); - } else { - ecs_strbuf_appendstr(b, sep); + const ecs_table_record_t *tr = flecs_id_record_get_table( + world->idr_isa_wildcard, table); + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); /* Table has IsA flag */ + + int32_t i, start = tr->index, end = start + tr->count; + for (i = start; i < end; i ++) { + ecs_entity_t base = ecs_pair_second(world, table->type.array[i]); + ecs_record_t *base_record = ecs_record_find(world, base); + if (!base_record || !base_record->table) { + continue; + } + + ecs_table_t *base_table = base_record->table; + flecs_json_serialize_table_inherited_type(world, base_table, buf, desc); + + char *base_name = ecs_get_path(world, base); + flecs_json_member(buf, base_name); + flecs_json_object_push(buf); + ecs_os_free(base_name); + + flecs_json_serialize_table_tags( + world, base_table, table, buf, desc); + + flecs_json_serialize_table_pairs( + world, base_table, table, ECS_RECORD_TO_ROW(base_record->row), + buf, desc); + + int32_t component_count = 0; + flecs_json_serialize_table_components( + world, base_table, table, buf, NULL, desc, + ECS_RECORD_TO_ROW(base_record->row), &component_count); + + if (desc->serialize_type_info) { + flecs_json_serialize_table_type_info( + world, base_table, buf, desc); } + + flecs_json_object_pop(buf); } - b->list_stack[list_sp].count ++; + + return true; } -bool ecs_strbuf_list_appendch( - ecs_strbuf_t *b, - char ch) +static +bool flecs_json_serialize_table_inherited( + const ecs_world_t *world, + ecs_table_t *table, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) { - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_strbuf_list_next(b); - return flecs_strbuf_appendch(b, ch); + if (!(table->flags & EcsTableHasIsA)) { + return false; + } + + flecs_json_memberl(buf, "inherited"); + flecs_json_object_push(buf); + flecs_json_serialize_table_inherited_type(world, table, buf, desc); + flecs_json_object_pop(buf); + return true; } -bool ecs_strbuf_list_append( - ecs_strbuf_t *b, - const char *fmt, - ...) +static +bool flecs_json_serialize_table_tags_pairs_vars( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_table_t *table, + int32_t row, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) { - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(fmt != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_strbuf_list_next(b); + bool result = false; + ecs_strbuf_list_push(buf, "", ","); + result |= flecs_json_serialize_table_tags(world, table, NULL, buf, desc); + result |= flecs_json_serialize_table_pairs(world, table, NULL, row, buf, desc); + result |= flecs_json_serialize_vars(world, it, buf, desc); + if (desc->serialize_inherited) { + result |= flecs_json_serialize_table_inherited(world, table, buf, desc); + } - va_list args; - va_start(args, fmt); - bool result = flecs_strbuf_vappend(b, fmt, args); - va_end(args); + if (desc->serialize_type_info) { + /* If we're serializing tables and are requesting type info, it must be + * added to each result. */ + result |= flecs_json_serialize_table_type_info(world, table, buf, desc); + } + ecs_strbuf_list_pop(buf, ""); + if (!result) { + ecs_strbuf_reset(buf); + } return result; } -bool ecs_strbuf_list_appendstr( - ecs_strbuf_t *b, - const char *str) +int flecs_json_serialize_iter_result_table( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + int32_t count, + bool has_this, + const char *parent_path, + const ecs_json_this_data_t *this_data) { - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_table_t *table = it->table; + if (!table || !count) { + return 0; + } - ecs_strbuf_list_next(b); - return ecs_strbuf_appendstr(b, str); -} + /* Serialize tags, pairs, vars once, since they're the same for each row, + * except when table has union pairs, which can be different for each + * entity. */ + ecs_strbuf_t tags_pairs_vars_buf = ECS_STRBUF_INIT; + int32_t tags_pairs_vars_len = 0; + char *tags_pairs_vars = NULL; + bool has_union = table->flags & EcsTableHasUnion; -bool ecs_strbuf_list_appendstrn( - ecs_strbuf_t *b, - const char *str, - int32_t n) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); + if (!has_union) { + if (flecs_json_serialize_table_tags_pairs_vars( + world, it, table, 0, &tags_pairs_vars_buf, desc)) + { + tags_pairs_vars_len = ecs_strbuf_written(&tags_pairs_vars_buf); + tags_pairs_vars = ecs_strbuf_get(&tags_pairs_vars_buf); + } + } + + /* If one entity has more than 256 components (oof), bad luck */ + ecs_json_value_ser_ctx_t values_ctx[FLECS_JSON_MAX_TABLE_COMPONENTS] = {{0}}; + int32_t component_count = 0; + + int32_t i, end = it->offset + count; + int result = 0; + for (i = it->offset; i < end; i ++) { + flecs_json_next(buf); + flecs_json_object_push(buf); + + if (has_this) { + ecs_json_this_data_t this_data_cpy = *this_data; + flecs_json_serialize_iter_this( + it, parent_path, &this_data_cpy, i - it->offset, buf, desc); + } + + if (has_union) { + if (flecs_json_serialize_table_tags_pairs_vars( + world, it, table, i, &tags_pairs_vars_buf, desc)) + { + tags_pairs_vars_len = ecs_strbuf_written(&tags_pairs_vars_buf); + tags_pairs_vars = ecs_strbuf_get(&tags_pairs_vars_buf); + } + } + + if (tags_pairs_vars) { + ecs_strbuf_list_appendstrn(buf, + tags_pairs_vars, tags_pairs_vars_len); + } + + if (has_union) { + ecs_os_free(tags_pairs_vars); + tags_pairs_vars = NULL; + } - ecs_strbuf_list_next(b); - return ecs_strbuf_appendstrn(b, str, n); -} + component_count = 0; /* Each row has the same number of components */ + if (flecs_json_serialize_table_components( + world, table, NULL, buf, values_ctx, desc, i, &component_count)) + { + result = -1; + break; + } -int32_t ecs_strbuf_written( - const ecs_strbuf_t *b) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - if (b->current) { - return b->size + b->current->pos; - } else { - return 0; - } -} + if (desc->serialize_matches) { + flecs_json_serialize_matches( + world, buf, it->entities[i - it->offset]); + } -/** - * @file datastructures/switch_list.c - * @brief Interleaved linked list for storing mutually exclusive values. - * - * Datastructure that stores N interleaved linked lists in an array. - * This allows for efficient storage of elements with mutually exclusive values. - * Each linked list has a header element which points to the index in the array - * that stores the first node of the list. Each list node points to the next - * array element. - * - * The datastructure allows for efficient storage and retrieval for values with - * mutually exclusive values, such as enumeration values. The linked list allows - * an application to obtain all elements for a given (enumeration) value without - * having to search. - * - * While the list accepts 64 bit values, it only uses the lower 32bits of the - * value for selecting the correct linked list. - * - * The switch list is used to store union relationships. - */ + if (desc->serialize_refs) { + flecs_json_serialize_refs(world, buf, it->entities[i - it->offset], + desc->serialize_refs); + } + if (desc->serialize_alerts) { + flecs_json_serialize_alerts(world, buf, + it->entities[i - it->offset]); + } -#ifdef FLECS_SANITIZE -static -void flecs_switch_verify_nodes( - ecs_switch_header_t *hdr, - ecs_switch_node_t *nodes) -{ - if (!hdr) { - return; + flecs_json_object_pop(buf); } - int32_t prev = -1, elem = hdr->element, count = 0; - while (elem != -1) { - ecs_assert(prev == nodes[elem].prev, ECS_INTERNAL_ERROR, NULL); - prev = elem; - elem = nodes[elem].next; - count ++; + for (i = 0; i < component_count; i ++) { + ecs_os_free(values_ctx[i].id_label); } - ecs_assert(count == hdr->count, ECS_INTERNAL_ERROR, NULL); + ecs_os_free(tags_pairs_vars); + + return result; } -#else -#define flecs_switch_verify_nodes(hdr, nodes) + #endif +/** + * @file addons/json/serialize_query_info.c + * @brief Serialize (component) values to JSON strings. + */ + + +#ifdef FLECS_JSON + static -ecs_switch_header_t* flecs_switch_get_header( - const ecs_switch_t *sw, - uint64_t value) +const char* flecs_json_inout_str( + int16_t kind) { - if (value == 0) { - return NULL; + switch(kind) { + case EcsIn: return "in"; + case EcsOut: return "out"; + case EcsInOut: return "inout"; + case EcsInOutNone: return "none"; + case EcsInOutFilter: return "filter"; + case EcsInOutDefault: return "default"; + default: return "unknown"; } - return (ecs_switch_header_t*)ecs_map_get(&sw->hdrs, value); } static -ecs_switch_header_t *flecs_switch_ensure_header( - ecs_switch_t *sw, - uint64_t value) +const char* flecs_json_oper_str( + int16_t kind) { - ecs_switch_header_t *node = flecs_switch_get_header(sw, value); - if (!node && (value != 0)) { - node = (ecs_switch_header_t*)ecs_map_ensure(&sw->hdrs, value); - node->count = 0; - node->element = -1; + switch(kind) { + case EcsAnd: return "and"; + case EcsNot: return "not"; + case EcsOr: return "or"; + case EcsOptional: return "optional"; + case EcsAndFrom: return "andfrom"; + case EcsNotFrom: return "notfrom"; + case EcsOrFrom: return "orfrom"; + default: return "unknown"; } - - return node; } static -void flecs_switch_remove_node( - ecs_switch_header_t *hdr, - ecs_switch_node_t *nodes, - ecs_switch_node_t *node, - int32_t element) +void flecs_json_serialize_term_entity( + const ecs_world_t *world, + ecs_entity_t e, + ecs_strbuf_t *buf) { - ecs_assert(&nodes[element] == node, ECS_INTERNAL_ERROR, NULL); + flecs_json_memberl(buf, "entity"); + flecs_json_path(buf, world, e); - /* Update previous node/header */ - if (hdr->element == element) { - ecs_assert(node->prev == -1, ECS_INVALID_PARAMETER, NULL); - /* If this is the first node, update the header */ - hdr->element = node->next; - } else { - /* If this is not the first node, update the previous node to the - * removed node's next ptr */ - ecs_assert(node->prev != -1, ECS_INVALID_PARAMETER, NULL); - ecs_switch_node_t *prev_node = &nodes[node->prev]; - prev_node->next = node->next; - } + if (e) { + const char *symbol = ecs_get_symbol(world, e); + if (symbol) { + flecs_json_memberl(buf, "symbol"); + flecs_json_string(buf, symbol); + } - /* Update next node */ - int32_t next = node->next; - if (next != -1) { - ecs_assert(next >= 0, ECS_INVALID_PARAMETER, NULL); - /* If this is not the last node, update the next node to point to the - * removed node's prev ptr */ - ecs_switch_node_t *next_node = &nodes[next]; - next_node->prev = node->prev; + if (ecs_has(world, e, EcsComponent)) { + flecs_json_memberl(buf, "type"); + flecs_json_true(buf); + } } - - /* Decrease count of current header */ - hdr->count --; - ecs_assert(hdr->count >= 0, ECS_INTERNAL_ERROR, NULL); } -void flecs_switch_init( - ecs_switch_t *sw, - ecs_allocator_t *allocator, - int32_t elements) +static +void flecs_json_serialize_term_ref( + const ecs_world_t *world, + const ecs_term_ref_t *ref, + ecs_strbuf_t *buf) { - ecs_map_init(&sw->hdrs, allocator); - ecs_vec_init_t(allocator, &sw->nodes, ecs_switch_node_t, elements); - ecs_vec_init_t(allocator, &sw->values, uint64_t, elements); - - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); - uint64_t *values = ecs_vec_first(&sw->values); - - int i; - for (i = 0; i < elements; i ++) { - nodes[i].prev = -1; - nodes[i].next = -1; - values[i] = 0; + flecs_json_object_push(buf); + if (ref->id & EcsIsEntity) { + flecs_json_serialize_term_entity(world, ECS_TERM_REF_ID(ref), buf); + } else if (ref->id & EcsIsVariable) { + flecs_json_memberl(buf, "var"); + if (ref->name) { + flecs_json_string(buf, ref->name); + } else if (ref->id) { + if (ECS_TERM_REF_ID(ref) == EcsThis) { + flecs_json_string(buf, "this"); + } else { + flecs_json_path(buf, world, ECS_TERM_REF_ID(ref)); + } + } + } else if (ref->id & EcsIsName) { + flecs_json_memberl(buf, "name"); + flecs_json_string(buf, ref->name); } + flecs_json_object_pop(buf); } -void flecs_switch_clear( - ecs_switch_t *sw) -{ - ecs_map_clear(&sw->hdrs); - ecs_vec_fini_t(sw->hdrs.allocator, &sw->nodes, ecs_switch_node_t); - ecs_vec_fini_t(sw->hdrs.allocator, &sw->values, uint64_t); -} - -void flecs_switch_fini( - ecs_switch_t *sw) +static +void flecs_json_serialize_term_trav( + const ecs_world_t *world, + const ecs_term_t *term, + ecs_strbuf_t *buf) { - ecs_map_fini(&sw->hdrs); - ecs_vec_fini_t(sw->hdrs.allocator, &sw->nodes, ecs_switch_node_t); - ecs_vec_fini_t(sw->hdrs.allocator, &sw->values, uint64_t); -} + if (term->trav) { + flecs_json_memberl(buf, "trav"); + flecs_json_object_push(buf); + flecs_json_serialize_term_entity(world, term->trav, buf); + flecs_json_object_pop(buf); + } -void flecs_switch_add( - ecs_switch_t *sw) -{ - ecs_switch_node_t *node = ecs_vec_append_t(sw->hdrs.allocator, - &sw->nodes, ecs_switch_node_t); - uint64_t *value = ecs_vec_append_t(sw->hdrs.allocator, - &sw->values, uint64_t); - node->prev = -1; - node->next = -1; - *value = 0; + flecs_json_memberl(buf, "flags"); + flecs_json_array_push(buf); + if (term->src.id & EcsSelf) { + flecs_json_next(buf); + flecs_json_string(buf, "self"); + } + if (term->src.id & EcsCascade) { + flecs_json_next(buf); + flecs_json_string(buf, "cascade"); + } else + if (term->src.id & EcsUp) { + flecs_json_next(buf); + flecs_json_string(buf, "up"); + } + flecs_json_array_pop(buf); } -void flecs_switch_set_count( - ecs_switch_t *sw, - int32_t count) +static +void flecs_json_serialize_term( + const ecs_world_t *world, + const ecs_query_t *q, + int t, + ecs_strbuf_t *buf) { - int32_t old_count = ecs_vec_count(&sw->nodes); - if (old_count == count) { - return; - } + const ecs_term_t *term = &q->terms[t]; - ecs_vec_set_count_t(sw->hdrs.allocator, &sw->nodes, ecs_switch_node_t, count); - ecs_vec_set_count_t(sw->hdrs.allocator, &sw->values, uint64_t, count); + flecs_json_object_push(buf); + flecs_json_memberl(buf, "inout"); + flecs_json_string(buf, flecs_json_inout_str(term->inout)); - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); - uint64_t *values = ecs_vec_first(&sw->values); + flecs_json_memberl(buf, "has_value"); + flecs_json_bool(buf, !!((1llu << term->field_index) & q->data_fields)); - int32_t i; - for (i = old_count; i < count; i ++) { - ecs_switch_node_t *node = &nodes[i]; - node->prev = -1; - node->next = -1; - values[i] = 0; + ecs_entity_t first_id = ECS_TERM_REF_ID(&term->first); + if (term->first.id & EcsIsEntity && first_id) { + if (ecs_has_pair(world, first_id, EcsOnInstantiate, EcsInherit)) { + flecs_json_memberl(buf, "can_inherit"); + flecs_json_true(buf); + } } -} -int32_t flecs_switch_count( - ecs_switch_t *sw) -{ - ecs_assert(ecs_vec_count(&sw->values) == ecs_vec_count(&sw->nodes), - ECS_INTERNAL_ERROR, NULL); - return ecs_vec_count(&sw->values); -} + flecs_json_memberl(buf, "oper"); + flecs_json_string(buf, flecs_json_oper_str(term->oper)); -void flecs_switch_ensure( - ecs_switch_t *sw, - int32_t count) -{ - int32_t old_count = ecs_vec_count(&sw->nodes); - if (old_count >= count) { - return; + flecs_json_memberl(buf, "src"); + flecs_json_serialize_term_ref(world, &term->src, buf); + + flecs_json_memberl(buf, "first"); + flecs_json_serialize_term_ref(world, &term->first, buf); + + if (ECS_TERM_REF_ID(&term->second) || term->second.name || term->second.id & EcsIsEntity) { + flecs_json_memberl(buf, "second"); + flecs_json_serialize_term_ref(world, &term->second, buf); } - flecs_switch_set_count(sw, count); -} + flecs_json_serialize_term_trav(world, term, buf); -void flecs_switch_addn( - ecs_switch_t *sw, - int32_t count) -{ - int32_t old_count = ecs_vec_count(&sw->nodes); - flecs_switch_set_count(sw, old_count + count); + flecs_json_object_pop(buf); } -void flecs_switch_set( - ecs_switch_t *sw, - int32_t element, - uint64_t value) +void flecs_json_serialize_query( + const ecs_world_t *world, + const ecs_query_t *q, + ecs_strbuf_t *buf) { - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->nodes), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->values), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element >= 0, ECS_INVALID_PARAMETER, NULL); - - uint64_t *values = ecs_vec_first(&sw->values); - uint64_t cur_value = values[element]; - - /* If the node is already assigned to the value, nothing to be done */ - if (cur_value == value) { - return; - } - - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); - ecs_switch_node_t *node = &nodes[element]; + flecs_json_object_push(buf); - ecs_switch_header_t *dst_hdr = flecs_switch_ensure_header(sw, value); - ecs_switch_header_t *cur_hdr = flecs_switch_get_header(sw, cur_value); + if (q->var_count) { + flecs_json_memberl(buf, "vars"); + flecs_json_array_push(buf); + int32_t v, first = 0; - flecs_switch_verify_nodes(cur_hdr, nodes); - flecs_switch_verify_nodes(dst_hdr, nodes); + if (!(q->flags & EcsQueryMatchThis)) { + first = 1; + } - /* If value is not 0, and dst_hdr is NULL, then this is not a valid value - * for this switch */ - ecs_assert(dst_hdr != NULL || !value, ECS_INVALID_PARAMETER, NULL); + for (v = first; v < q->var_count; v ++) { + flecs_json_next(buf); + if (q->vars[v]) { + flecs_json_string_escape(buf, q->vars[v]); + } else { + flecs_json_string(buf, "this"); + } + } + flecs_json_array_pop(buf); + } - if (cur_hdr) { - flecs_switch_remove_node(cur_hdr, nodes, node, element); + flecs_json_memberl(buf, "terms"); + flecs_json_array_push(buf); + int t; + for (t = 0; t < q->term_count; t ++) { + flecs_json_next(buf); + flecs_json_serialize_term(world, q, t, buf); } + flecs_json_array_pop(buf); - /* Now update the node itself by adding it as the first node of dst */ - node->prev = -1; - values[element] = value; - if (dst_hdr) { - node->next = dst_hdr->element; + flecs_json_object_pop(buf); +} - /* Also update the dst header */ - int32_t first = dst_hdr->element; - if (first != -1) { - ecs_assert(first >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_switch_node_t *first_node = &nodes[first]; - first_node->prev = element; - } +#endif - dst_hdr->element = element; - dst_hdr->count ++; - } -} +/** + * @file addons/json/serialize_type_info.c + * @brief Serialize type (reflection) information to JSON. + */ -void flecs_switch_remove( - ecs_switch_t *sw, - int32_t elem) -{ - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(elem < ecs_vec_count(&sw->nodes), ECS_INVALID_PARAMETER, NULL); - ecs_assert(elem >= 0, ECS_INVALID_PARAMETER, NULL); - uint64_t *values = ecs_vec_first(&sw->values); - uint64_t value = values[elem]; - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); - ecs_switch_node_t *node = &nodes[elem]; +#ifdef FLECS_JSON - /* If node is currently assigned to a case, remove it from the list */ - if (value != 0) { - ecs_switch_header_t *hdr = flecs_switch_get_header(sw, value); - ecs_assert(hdr != NULL, ECS_INTERNAL_ERROR, NULL); +static +int json_typeinfo_ser_type( + const ecs_world_t *world, + ecs_entity_t type, + ecs_strbuf_t *buf); - flecs_switch_verify_nodes(hdr, nodes); - flecs_switch_remove_node(hdr, nodes, node, elem); +static +int json_typeinfo_ser_primitive( + ecs_primitive_kind_t kind, + ecs_strbuf_t *str) +{ + switch(kind) { + case EcsBool: + flecs_json_string(str, "bool"); + break; + case EcsChar: + case EcsString: + flecs_json_string(str, "text"); + break; + case EcsByte: + flecs_json_string(str, "byte"); + break; + case EcsU8: + case EcsU16: + case EcsU32: + case EcsU64: + case EcsI8: + case EcsI16: + case EcsI32: + case EcsI64: + case EcsIPtr: + case EcsUPtr: + flecs_json_string(str, "int"); + break; + case EcsF32: + case EcsF64: + flecs_json_string(str, "float"); + break; + case EcsEntity: + flecs_json_string(str, "entity"); + break; + case EcsId: + flecs_json_string(str, "id"); + break; + default: + return -1; } - int32_t last_elem = ecs_vec_count(&sw->nodes) - 1; - if (last_elem != elem) { - ecs_switch_node_t *last = ecs_vec_last_t(&sw->nodes, ecs_switch_node_t); - int32_t next = last->next, prev = last->prev; - if (next != -1) { - ecs_switch_node_t *n = &nodes[next]; - n->prev = elem; - } + return 0; +} - if (prev != -1) { - ecs_switch_node_t *n = &nodes[prev]; - n->next = elem; - } else { - ecs_switch_header_t *hdr = flecs_switch_get_header(sw, values[last_elem]); - if (hdr && hdr->element != -1) { - ecs_assert(hdr->element == last_elem, - ECS_INTERNAL_ERROR, NULL); - hdr->element = elem; - } +static +void json_typeinfo_ser_constants( + const ecs_world_t *world, + ecs_entity_t type, + ecs_strbuf_t *str) +{ + ecs_iter_t it = ecs_each_id(world, ecs_pair(EcsChildOf, type)); + while (ecs_each_next(&it)) { + int32_t i, count = it.count; + for (i = 0; i < count; i ++) { + flecs_json_next(str); + flecs_json_string(str, ecs_get_name(world, it.entities[i])); } } - - /* Remove element from arrays */ - ecs_vec_remove_t(&sw->nodes, ecs_switch_node_t, elem); - ecs_vec_remove_t(&sw->values, uint64_t, elem); } -uint64_t flecs_switch_get( - const ecs_switch_t *sw, - int32_t element) +static +void json_typeinfo_ser_enum( + const ecs_world_t *world, + ecs_entity_t type, + ecs_strbuf_t *str) { - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->nodes), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->values), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element >= 0, ECS_INVALID_PARAMETER, NULL); - - uint64_t *values = ecs_vec_first(&sw->values); - return values[element]; + ecs_strbuf_list_appendstr(str, "\"enum\""); + json_typeinfo_ser_constants(world, type, str); } -ecs_vec_t* flecs_switch_values( - const ecs_switch_t *sw) +static +void json_typeinfo_ser_bitmask( + const ecs_world_t *world, + ecs_entity_t type, + ecs_strbuf_t *str) { - return ECS_CONST_CAST(ecs_vec_t*, &sw->values); + ecs_strbuf_list_appendstr(str, "\"bitmask\""); + json_typeinfo_ser_constants(world, type, str); } -int32_t flecs_switch_case_count( - const ecs_switch_t *sw, - uint64_t value) +static +int json_typeinfo_ser_array( + const ecs_world_t *world, + ecs_entity_t elem_type, + int32_t count, + ecs_strbuf_t *str) { - ecs_switch_header_t *hdr = flecs_switch_get_header(sw, value); - if (!hdr) { - return 0; + ecs_strbuf_list_appendstr(str, "\"array\""); + + flecs_json_next(str); + if (json_typeinfo_ser_type(world, elem_type, str)) { + goto error; } - return hdr->count; + ecs_strbuf_list_append(str, "%u", count); + return 0; +error: + return -1; } -void flecs_switch_swap( - ecs_switch_t *sw, - int32_t elem_1, - int32_t elem_2) +static +int json_typeinfo_ser_array_type( + const ecs_world_t *world, + ecs_entity_t type, + ecs_strbuf_t *str) { - uint64_t v1 = flecs_switch_get(sw, elem_1); - uint64_t v2 = flecs_switch_get(sw, elem_2); + const EcsArray *arr = ecs_get(world, type, EcsArray); + ecs_assert(arr != NULL, ECS_INTERNAL_ERROR, NULL); + if (json_typeinfo_ser_array(world, arr->type, arr->count, str)) { + goto error; + } - flecs_switch_set(sw, elem_2, v1); - flecs_switch_set(sw, elem_1, v2); + return 0; +error: + return -1; } -int32_t flecs_switch_first( - const ecs_switch_t *sw, - uint64_t value) +static +int json_typeinfo_ser_vector( + const ecs_world_t *world, + ecs_entity_t type, + ecs_strbuf_t *str) { - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_switch_header_t *hdr = flecs_switch_get_header(sw, value); - if (!hdr) { - return -1; + const EcsVector *arr = ecs_get(world, type, EcsVector); + ecs_assert(arr != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_strbuf_list_appendstr(str, "\"vector\""); + + flecs_json_next(str); + if (json_typeinfo_ser_type(world, arr->type, str)) { + goto error; } - return hdr->element; + return 0; +error: + return -1; } -int32_t flecs_switch_next( - const ecs_switch_t *sw, - int32_t element) +/* Serialize unit information */ +static +int json_typeinfo_ser_unit( + const ecs_world_t *world, + ecs_strbuf_t *str, + ecs_entity_t unit) { - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->nodes), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element >= 0, ECS_INVALID_PARAMETER, NULL); + flecs_json_memberl(str, "unit"); + flecs_json_path(str, world, unit); - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); + const EcsUnit *uptr = ecs_get(world, unit, EcsUnit); + if (uptr) { + if (uptr->symbol) { + flecs_json_memberl(str, "symbol"); + flecs_json_string(str, uptr->symbol); + } + ecs_entity_t quantity = ecs_get_target(world, unit, EcsQuantity, 0); + if (quantity) { + flecs_json_memberl(str, "quantity"); + flecs_json_path(str, world, quantity); + } + } - return nodes[element].next; + return 0; } -/** - * @file datastructures/vec.c - * @brief Vector with allocator support. - */ - - -ecs_vec_t* ecs_vec_init( - ecs_allocator_t *allocator, - ecs_vec_t *v, - ecs_size_t size, - int32_t elem_count) +static +void json_typeinfo_ser_range( + ecs_strbuf_t *str, + const char *kind, + ecs_member_value_range_t *range) { - ecs_assert(size != 0, ECS_INVALID_PARAMETER, NULL); - v->array = NULL; - v->count = 0; - if (elem_count) { - if (allocator) { - v->array = flecs_alloc(allocator, size * elem_count); - } else { - v->array = ecs_os_malloc(size * elem_count); - } - } - v->size = elem_count; -#ifdef FLECS_SANITIZE - v->elem_size = size; -#endif - return v; + flecs_json_member(str, kind); + flecs_json_array_push(str); + flecs_json_next(str); + flecs_json_number(str, range->min); + flecs_json_next(str); + flecs_json_number(str, range->max); + flecs_json_array_pop(str); } -void ecs_vec_init_if( - ecs_vec_t *vec, - ecs_size_t size) +/* Forward serialization to the different type kinds */ +static +int json_typeinfo_ser_type_op( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + ecs_strbuf_t *str, + const EcsStruct *st) { - ecs_san_assert(!vec->elem_size || vec->elem_size == size, ECS_INVALID_PARAMETER, NULL); - (void)vec; - (void)size; -#ifdef FLECS_SANITIZE - if (!vec->elem_size) { - ecs_assert(vec->count == 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(vec->size == 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(vec->array == NULL, ECS_INTERNAL_ERROR, NULL); - vec->elem_size = size; + 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); } -#endif -} -void ecs_vec_fini( - ecs_allocator_t *allocator, - ecs_vec_t *v, - ecs_size_t size) -{ - if (v->array) { - ecs_san_assert(!size || size == v->elem_size, ECS_INVALID_PARAMETER, NULL); - if (allocator) { - flecs_free(allocator, size * v->size, v->array); - } else { - ecs_os_free(v->array); + flecs_json_array_push(str); + + switch(op->kind) { + case EcsOpPush: + case EcsOpPop: + /* Should not be parsed as single op */ + ecs_throw(ECS_INVALID_PARAMETER, + "unexpected push/pop serializer instruction"); + break; + case EcsOpEnum: + json_typeinfo_ser_enum(world, op->type, str); + break; + case EcsOpBitmask: + json_typeinfo_ser_bitmask(world, op->type, str); + break; + case EcsOpArray: + json_typeinfo_ser_array_type(world, op->type, str); + break; + case EcsOpVector: + json_typeinfo_ser_vector(world, op->type, str); + break; + case EcsOpOpaque: + /* Can't happen, already handled above */ + ecs_throw(ECS_INTERNAL_ERROR, NULL); + break; + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpEntity: + case EcsOpId: + case EcsOpString: + if (json_typeinfo_ser_primitive( + flecs_json_op_to_primitive_kind(op->kind), str)) + { + ecs_throw(ECS_INTERNAL_ERROR, NULL); } - v->array = NULL; - v->count = 0; - v->size = 0; + break; + case EcsOpScope: + case EcsOpPrimitive: + default: + ecs_throw(ECS_INTERNAL_ERROR, NULL); } -} -ecs_vec_t* ecs_vec_reset( - ecs_allocator_t *allocator, - ecs_vec_t *v, - ecs_size_t size) -{ - if (!v->size) { - ecs_vec_init(allocator, v, size, 0); - } else { - ecs_san_assert(size == v->elem_size, ECS_INTERNAL_ERROR, NULL); - ecs_vec_clear(v); - } - return v; -} + if (st) { + ecs_member_t *m = ecs_vec_get_t( + &st->members, ecs_member_t, op->member_index); + ecs_assert(m != NULL, ECS_INTERNAL_ERROR, NULL); -void ecs_vec_clear( - ecs_vec_t *vec) -{ - vec->count = 0; -} + bool value_range = ECS_NEQ(m->range.min, m->range.max); + bool error_range = ECS_NEQ(m->error_range.min, m->error_range.max); + bool warning_range = ECS_NEQ(m->warning_range.min, m->warning_range.max); -ecs_vec_t ecs_vec_copy( - ecs_allocator_t *allocator, - const ecs_vec_t *v, - ecs_size_t size) -{ - ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); - void *array; - if (allocator) { - array = flecs_dup(allocator, size * v->size, v->array); - } else { - array = ecs_os_memdup(v->array, size * v->size); - } - return (ecs_vec_t) { - .count = v->count, - .size = v->size, - .array = array -#ifdef FLECS_SANITIZE - , .elem_size = size -#endif - }; -} + ecs_entity_t unit = m->unit; + if (unit || error_range || warning_range || value_range) { + flecs_json_next(str); + flecs_json_next(str); + flecs_json_object_push(str); -void ecs_vec_reclaim( - ecs_allocator_t *allocator, - ecs_vec_t *v, - ecs_size_t size) -{ - ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); - int32_t count = v->count; - if (count < v->size) { - if (count) { - if (allocator) { - v->array = flecs_realloc( - allocator, size * count, size * v->size, v->array); - } else { - v->array = ecs_os_realloc(v->array, size * count); + if (unit) { + json_typeinfo_ser_unit(world, str, unit); } - v->size = count; - } else { - ecs_vec_fini(allocator, v, size); + if (value_range) { + json_typeinfo_ser_range(str, "range", &m->range); + } + if (error_range) { + json_typeinfo_ser_range(str, "error_range", &m->error_range); + } + if (warning_range) { + json_typeinfo_ser_range(str, "warning_range", &m->warning_range); + } + + flecs_json_object_pop(str); } } + + flecs_json_array_pop(str); + + return 0; +error: + return -1; } -void ecs_vec_set_size( - ecs_allocator_t *allocator, - ecs_vec_t *v, - ecs_size_t size, - int32_t elem_count) +/* Iterate over a slice of the type ops array */ +static +int json_typeinfo_ser_type_ops( + const ecs_world_t *world, + ecs_meta_type_op_t *ops, + int32_t op_count, + ecs_strbuf_t *str, + const EcsStruct *st) { - ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); - if (v->size != elem_count) { - if (elem_count < v->count) { - elem_count = v->count; + const EcsStruct *stack[64] = {st}; + int32_t sp = 1; + + for (int i = 0; i < op_count; i ++) { + ecs_meta_type_op_t *op = &ops[i]; + + if (op != ops) { + if (op->name) { + flecs_json_member(str, op->name); + } } - elem_count = flecs_next_pow_of_2(elem_count); - if (elem_count < 2) { - elem_count = 2; + 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; } - if (elem_count != v->size) { - if (allocator) { - v->array = flecs_realloc( - allocator, size * elem_count, size * v->size, v->array); - } else { - v->array = ecs_os_realloc(v->array, size * elem_count); + + switch(op->kind) { + case EcsOpPush: + flecs_json_object_push(str); + ecs_assert(sp < 63, ECS_INVALID_OPERATION, "type nesting too deep"); + stack[sp ++] = ecs_get(world, op->type, EcsStruct); + break; + case EcsOpPop: { + ecs_entity_t unit = ecs_get_target_for(world, op->type, EcsIsA, EcsUnit); + if (unit) { + flecs_json_member(str, "@self"); + flecs_json_array_push(str); + flecs_json_object_push(str); + json_typeinfo_ser_unit(world, str, unit); + flecs_json_object_pop(str); + flecs_json_array_pop(str); } - v->size = elem_count; + + flecs_json_object_pop(str); + sp --; + break; + } + case EcsOpArray: + case EcsOpVector: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpEntity: + case EcsOpId: + case EcsOpString: + case EcsOpOpaque: + if (json_typeinfo_ser_type_op(world, op, str, stack[sp - 1])) { + goto error; + } + break; + case EcsOpPrimitive: + case EcsOpScope: + default: + ecs_throw(ECS_INTERNAL_ERROR, NULL); } } -} -void ecs_vec_set_min_size( - struct ecs_allocator_t *allocator, - ecs_vec_t *vec, - ecs_size_t size, - int32_t elem_count) -{ - if (elem_count > vec->size) { - ecs_vec_set_size(allocator, vec, size, elem_count); - } + return 0; +error: + return -1; } -void ecs_vec_set_min_count( - struct ecs_allocator_t *allocator, - ecs_vec_t *vec, - ecs_size_t size, - int32_t elem_count) +static +int json_typeinfo_ser_type( + const ecs_world_t *world, + ecs_entity_t type, + ecs_strbuf_t *buf) { - ecs_vec_set_min_size(allocator, vec, size, elem_count); - if (vec->count < elem_count) { - vec->count = elem_count; + const EcsComponent *comp = ecs_get(world, type, EcsComponent); + if (!comp) { + ecs_strbuf_appendch(buf, '0'); + return 0; } -} -void ecs_vec_set_min_count_zeromem( - struct ecs_allocator_t *allocator, - ecs_vec_t *vec, - ecs_size_t size, - int32_t elem_count) -{ - int32_t count = vec->count; - if (count < elem_count) { - ecs_vec_set_min_count(allocator, vec, size, elem_count); - ecs_os_memset(ECS_ELEM(vec->array, size, count), 0, - size * (elem_count - count)); + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); + if (!ser) { + ecs_strbuf_appendch(buf, '0'); + return 0; } -} -void ecs_vec_set_count( - ecs_allocator_t *allocator, - ecs_vec_t *v, - ecs_size_t size, - int32_t elem_count) -{ - ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); - if (v->count != elem_count) { - if (v->size < elem_count) { - ecs_vec_set_size(allocator, v, size, elem_count); - } + const EcsStruct *st = ecs_get(world, type, EcsStruct); + ecs_meta_type_op_t *ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t); + int32_t count = ecs_vec_count(&ser->ops); - v->count = elem_count; + if (json_typeinfo_ser_type_ops(world, ops, count, buf, st)) { + return -1; } -} -void* ecs_vec_grow( - ecs_allocator_t *allocator, - ecs_vec_t *v, - ecs_size_t size, - int32_t elem_count) -{ - ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(elem_count > 0, ECS_INTERNAL_ERROR, NULL); - int32_t count = v->count; - ecs_vec_set_count(allocator, v, size, count + elem_count); - return ECS_ELEM(v->array, size, count); + return 0; } -void* ecs_vec_append( - ecs_allocator_t *allocator, - ecs_vec_t *v, - ecs_size_t size) +int ecs_type_info_to_json_buf( + const ecs_world_t *world, + ecs_entity_t type, + ecs_strbuf_t *buf) { - ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); - int32_t count = v->count; - if (v->size == count) { - ecs_vec_set_size(allocator, v, size, count + 1); - } - v->count = count + 1; - return ECS_ELEM(v->array, size, count); + return json_typeinfo_ser_type(world, type, buf); } -void ecs_vec_remove( - ecs_vec_t *v, - ecs_size_t size, - int32_t index) +char* ecs_type_info_to_json( + const ecs_world_t *world, + ecs_entity_t type) { - ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index < v->count, ECS_OUT_OF_RANGE, NULL); - if (index == --v->count) { - return; - } + ecs_strbuf_t str = ECS_STRBUF_INIT; - ecs_os_memcpy( - ECS_ELEM(v->array, size, index), - ECS_ELEM(v->array, size, v->count), - size); -} + if (ecs_type_info_to_json_buf(world, type, &str) != 0) { + ecs_strbuf_reset(&str); + return NULL; + } -void ecs_vec_remove_last( - ecs_vec_t *v) -{ - v->count --; + return ecs_strbuf_get(&str); } -int32_t ecs_vec_count( - const ecs_vec_t *v) -{ - return v->count; -} +#endif -int32_t ecs_vec_size( - const ecs_vec_t *v) -{ - return v->size; -} +/** + * @file addons/json/serialize_value.c + * @brief Serialize value to JSON. + */ -void* ecs_vec_get( - const ecs_vec_t *v, - ecs_size_t size, - int32_t index) -{ - ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index < v->count, ECS_OUT_OF_RANGE, NULL); - return ECS_ELEM(v->array, size, index); -} -void* ecs_vec_last( - const ecs_vec_t *v, - ecs_size_t size) -{ - ecs_san_assert(!v->elem_size || size == v->elem_size, - ECS_INVALID_PARAMETER, NULL); - return ECS_ELEM(v->array, size, v->count - 1); -} +#ifdef FLECS_JSON -void* ecs_vec_first( - const ecs_vec_t *v) -{ - return v->array; -} +static +int flecs_json_ser_type_ops( + const ecs_world_t *world, + ecs_meta_type_op_t *ops, + int32_t op_count, + const void *base, + ecs_strbuf_t *str, + int32_t in_array); +static +int flecs_json_ser_type_op( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str); +/* Serialize enumeration */ static -ecs_entity_index_page_t* flecs_entity_index_ensure_page( - ecs_entity_index_t *index, - uint32_t id) +int flecs_json_ser_enum( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str) { - int32_t page_index = (int32_t)(id >> FLECS_ENTITY_PAGE_BITS); - if (page_index >= ecs_vec_count(&index->pages)) { - ecs_vec_set_min_count_zeromem_t(index->allocator, &index->pages, - ecs_entity_index_page_t*, page_index + 1); - } + const EcsEnum *enum_type = ecs_get(world, op->type, EcsEnum); + ecs_check(enum_type != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_entity_index_page_t **page_ptr = ecs_vec_get_t(&index->pages, - ecs_entity_index_page_t*, page_index); - ecs_entity_index_page_t *page = *page_ptr; - if (!page) { - page = *page_ptr = flecs_bcalloc(&index->page_allocator); - ecs_assert(page != NULL, ECS_OUT_OF_MEMORY, NULL); + int32_t value = *(const int32_t*)base; + + /* Enumeration constants are stored in a map that is keyed on the + * enumeration value. */ + ecs_enum_constant_t *constant = ecs_map_get_deref(&enum_type->constants, + ecs_enum_constant_t, (ecs_map_key_t)value); + if (!constant) { + /* If the value is not found, it is not a valid enumeration constant */ + char *name = ecs_get_path(world, op->type); + ecs_err("enumeration value '%d' of type '%s' is not a valid constant", + value, name); + ecs_os_free(name); + goto error; } - return page; -} + ecs_strbuf_appendch(str, '"'); + ecs_strbuf_appendstr(str, ecs_get_name(world, constant->constant)); + ecs_strbuf_appendch(str, '"'); -void flecs_entity_index_init( - ecs_allocator_t *allocator, - ecs_entity_index_t *index) -{ - index->allocator = allocator; - index->alive_count = 1; - ecs_vec_init_t(allocator, &index->dense, uint64_t, 1); - ecs_vec_set_count_t(allocator, &index->dense, uint64_t, 1); - ecs_vec_init_t(allocator, &index->pages, ecs_entity_index_page_t*, 0); - flecs_ballocator_init(&index->page_allocator, - ECS_SIZEOF(ecs_entity_index_page_t)); + return 0; +error: + return -1; } -void flecs_entity_index_fini( - ecs_entity_index_t *index) +/* Serialize bitmask */ +static +int flecs_json_ser_bitmask( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *ptr, + ecs_strbuf_t *str) { - ecs_vec_fini_t(index->allocator, &index->dense, uint64_t); -#if defined(FLECS_SANITIZE) || defined(FLECS_USE_OS_ALLOC) - int32_t i, count = ecs_vec_count(&index->pages); - ecs_entity_index_page_t **pages = ecs_vec_first(&index->pages); - for (i = 0; i < count; i ++) { - flecs_bfree(&index->page_allocator, pages[i]); - } -#endif - ecs_vec_fini_t(index->allocator, &index->pages, ecs_entity_index_page_t*); - flecs_ballocator_fini(&index->page_allocator); -} + const EcsBitmask *bitmask_type = ecs_get(world, op->type, EcsBitmask); + ecs_check(bitmask_type != NULL, ECS_INVALID_PARAMETER, NULL); -ecs_record_t* flecs_entity_index_get_any( - const ecs_entity_index_t *index, - uint64_t entity) -{ - uint32_t id = (uint32_t)entity; - int32_t page_index = (int32_t)(id >> FLECS_ENTITY_PAGE_BITS); - ecs_entity_index_page_t *page = ecs_vec_get_t(&index->pages, - ecs_entity_index_page_t*, page_index)[0]; - ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; - ecs_assert(r->dense != 0, ECS_INVALID_PARAMETER, NULL); - return r; -} + uint32_t value = *(const uint32_t*)ptr; + if (!value) { + ecs_strbuf_appendch(str, '0'); + return 0; + } -ecs_record_t* flecs_entity_index_get( - const ecs_entity_index_t *index, - uint64_t entity) -{ - ecs_record_t *r = flecs_entity_index_get_any(index, entity); - ecs_assert(r->dense < index->alive_count, ECS_INVALID_PARAMETER, NULL); - ecs_assert(ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] == entity, - ECS_INVALID_PARAMETER, NULL); - return r; -} + ecs_strbuf_list_push(str, "\"", "|"); -ecs_record_t* flecs_entity_index_try_get_any( - const ecs_entity_index_t *index, - uint64_t entity) -{ - uint32_t id = (uint32_t)entity; - int32_t page_index = (int32_t)(id >> FLECS_ENTITY_PAGE_BITS); - if (page_index >= ecs_vec_count(&index->pages)) { - return NULL; + /* Multiple flags can be set at a given time. Iterate through all the flags + * and append the ones that are set. */ + ecs_map_iter_t it = ecs_map_iter(&bitmask_type->constants); + while (ecs_map_next(&it)) { + ecs_bitmask_constant_t *constant = ecs_map_ptr(&it); + ecs_map_key_t key = ecs_map_key(&it); + if ((value & key) == key) { + ecs_strbuf_list_appendstr(str, + ecs_get_name(world, constant->constant)); + value -= (uint32_t)key; + } } - ecs_entity_index_page_t *page = ecs_vec_get_t(&index->pages, - ecs_entity_index_page_t*, page_index)[0]; - if (!page) { - return NULL; + if (value != 0) { + /* All bits must have been matched by a constant */ + char *name = ecs_get_path(world, op->type); + ecs_err("bitmask value '%u' of type '%s' contains invalid/unknown bits", + value, name); + ecs_os_free(name); + goto error; } - ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; - if (!r->dense) { - return NULL; - } + ecs_strbuf_list_pop(str, "\""); - return r; + return 0; +error: + return -1; } -ecs_record_t* flecs_entity_index_try_get( - const ecs_entity_index_t *index, - uint64_t entity) +/* Serialize elements of a contiguous array */ +static +int flecs_json_ser_elements( + const ecs_world_t *world, + ecs_meta_type_op_t *ops, + int32_t op_count, + const void *base, + int32_t elem_count, + int32_t elem_size, + ecs_strbuf_t *str, + bool is_array) { - ecs_record_t *r = flecs_entity_index_try_get_any(index, entity); - if (r) { - if (r->dense >= index->alive_count) { - return NULL; - } - if (ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] != entity) { - return NULL; + flecs_json_array_push(str); + + const void *ptr = base; + + int i; + for (i = 0; i < elem_count; i ++) { + ecs_strbuf_list_next(str); + if (flecs_json_ser_type_ops(world, ops, op_count, ptr, str, is_array)) { + return -1; } + ptr = ECS_OFFSET(ptr, elem_size); } - return r; + + flecs_json_array_pop(str); + + return 0; } -ecs_record_t* flecs_entity_index_ensure( - ecs_entity_index_t *index, - uint64_t entity) +static +int flecs_json_ser_type_elements( + const ecs_world_t *world, + ecs_entity_t type, + const void *base, + int32_t elem_count, + ecs_strbuf_t *str, + bool is_array) { - uint32_t id = (uint32_t)entity; - ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id); - ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); + ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t dense = r->dense; - if (dense) { - /* Entity is already alive, nothing to be done */ - if (dense < index->alive_count) { - ecs_assert( - ecs_vec_get_t(&index->dense, uint64_t, dense)[0] == entity, - ECS_INTERNAL_ERROR, NULL); - return r; - } - } else { - /* Entity doesn't have a dense index yet */ - ecs_vec_append_t(index->allocator, &index->dense, uint64_t)[0] = entity; - r->dense = dense = ecs_vec_count(&index->dense) - 1; - index->max_id = id > index->max_id ? id : index->max_id; - } + const EcsComponent *comp = ecs_get(world, type, EcsComponent); + ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(dense != 0, ECS_INTERNAL_ERROR, NULL); + ecs_meta_type_op_t *ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t); + int32_t op_count = ecs_vec_count(&ser->ops); - /* Entity is not alive, swap with first not alive element */ - uint64_t *ids = ecs_vec_first(&index->dense); - uint64_t e_swap = ids[index->alive_count]; - ecs_record_t *r_swap = flecs_entity_index_get_any(index, e_swap); - ecs_assert(r_swap->dense == index->alive_count, - ECS_INTERNAL_ERROR, NULL); + return flecs_json_ser_elements( + world, ops, op_count, base, elem_count, comp->size, str, is_array); +} - r_swap->dense = dense; - r->dense = index->alive_count; - ids[dense] = e_swap; - ids[index->alive_count ++] = entity; +/* Serialize array */ +static +int json_ser_array( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *ptr, + ecs_strbuf_t *str) +{ + const EcsArray *a = ecs_get(world, op->type, EcsArray); + ecs_assert(a != NULL, ECS_INTERNAL_ERROR, NULL); + + return flecs_json_ser_type_elements( + world, a->type, ptr, a->count, str, true); +} + +/* Serialize vector */ +static +int json_ser_vector( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + const ecs_vec_t *value = base; + const EcsVector *v = ecs_get(world, op->type, EcsVector); + ecs_assert(v != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(flecs_entity_index_is_alive(index, entity), - ECS_INTERNAL_ERROR, NULL); + int32_t count = ecs_vec_count(value); + void *array = ecs_vec_first(value); - return r; + /* Serialize contiguous buffer of vector */ + return flecs_json_ser_type_elements(world, v->type, array, count, str, false); } -void flecs_entity_index_remove( - ecs_entity_index_t *index, - uint64_t entity) +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_serializer_t *ser, + ecs_entity_t type, + const void *value) { - ecs_record_t *r = flecs_entity_index_try_get(index, entity); - if (!r) { - /* Entity is not alive or doesn't exist, nothing to be done */ - return; + json_serializer_ctx_t *json_ser = ser->ctx; + if (json_ser->is_collection) { + ecs_strbuf_list_next(json_ser->str); } - - int32_t dense = r->dense; - int32_t i_swap = -- index->alive_count; - uint64_t *e_swap_ptr = ecs_vec_get_t(&index->dense, uint64_t, i_swap); - uint64_t e_swap = e_swap_ptr[0]; - ecs_record_t *r_swap = flecs_entity_index_get_any(index, e_swap); - ecs_assert(r_swap->dense == i_swap, ECS_INTERNAL_ERROR, NULL); - - r_swap->dense = dense; - r->table = NULL; - r->idr = NULL; - r->row = 0; - r->dense = i_swap; - ecs_vec_get_t(&index->dense, uint64_t, dense)[0] = e_swap; - e_swap_ptr[0] = ECS_GENERATION_INC(entity); - ecs_assert(!flecs_entity_index_is_alive(index, entity), - ECS_INTERNAL_ERROR, NULL); + return ecs_ptr_to_json_buf(ser->world, type, value, json_ser->str); } -void flecs_entity_index_set_generation( - ecs_entity_index_t *index, - uint64_t entity) +static +int json_ser_custom_member( + const ecs_serializer_t *ser, + const char *name) { - ecs_record_t *r = flecs_entity_index_try_get_any(index, entity); - if (r) { - ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] = entity; + 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; } -uint64_t flecs_entity_index_get_generation( - const ecs_entity_index_t *index, - uint64_t entity) +static +int json_ser_custom_type( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str) { - ecs_record_t *r = flecs_entity_index_try_get_any(index, entity); - if (r) { - return ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0]; - } else { - return 0; + const EcsOpaque *ct = ecs_get(world, op->type, EcsOpaque); + ecs_assert(ct != NULL, ECS_INVALID_OPERATION, + "entity %s in opaque type serializer instruction is not an opaque type", + ecs_get_name(world, op->type)); + ecs_assert(ct->as_type != 0, ECS_INVALID_OPERATION, + "opaque type %s has not populated as_type field", + ecs_get_name(world, op->type)); + ecs_assert(ct->serialize != NULL, ECS_INVALID_OPERATION, + "opaque type %s does not have serialize interface", + ecs_get_name(world, op->type)); + + const EcsType *pt = ecs_get(world, ct->as_type, EcsType); + ecs_assert(pt != NULL, ECS_INVALID_OPERATION, + "opaque type %s is missing flecs.meta.Type component", + ecs_get_name(world, op->type)); + + 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; } -} -bool flecs_entity_index_is_alive( - const ecs_entity_index_t *index, - uint64_t entity) -{ - return flecs_entity_index_try_get(index, entity) != NULL; -} + json_serializer_ctx_t json_ser = { + .str = str, + .is_struct = is_struct, + .is_collection = is_collection + }; -bool flecs_entity_index_is_valid( - const ecs_entity_index_t *index, - uint64_t entity) -{ - uint32_t id = (uint32_t)entity; - ecs_record_t *r = flecs_entity_index_try_get_any(index, id); - if (!r || !r->dense) { - /* Doesn't exist yet, so is valid */ - return true; + ecs_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 the id exists, it must be alive */ - return r->dense < index->alive_count; -} + if (kind == EcsStructType) { + flecs_json_object_pop(str); + } else if (kind == EcsArrayType || kind == EcsVectorType) { + flecs_json_array_pop(str); + } -bool flecs_entity_index_exists( - const ecs_entity_index_t *index, - uint64_t entity) -{ - return flecs_entity_index_try_get_any(index, entity) != NULL; + return 0; } -uint64_t flecs_entity_index_new_id( - ecs_entity_index_t *index) +/* Forward serialization to the different type kinds */ +static +int flecs_json_ser_type_op( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *ptr, + ecs_strbuf_t *str) { - if (index->alive_count != ecs_vec_count(&index->dense)) { - /* Recycle id */ - return ecs_vec_get_t(&index->dense, uint64_t, index->alive_count ++)[0]; + void *vptr = ECS_OFFSET(ptr, op->offset); + bool large_int = false; + if (op->kind == EcsOpI64) { + if (*(int64_t*)vptr >= 2147483648) { + large_int = true; + } + } else if (op->kind == EcsOpU64) { + if (*(uint64_t*)vptr >= 2147483648) { + large_int = true; + } } - /* Create new id */ - uint32_t id = (uint32_t)++ index->max_id; - ecs_vec_append_t(index->allocator, &index->dense, uint64_t)[0] = id; - - ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id); - ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; - r->dense = index->alive_count ++; - ecs_assert(index->alive_count == ecs_vec_count(&index->dense), - ECS_INTERNAL_ERROR, NULL); + if (large_int) { + ecs_strbuf_appendch(str, '"'); + } - return id; -} + switch(op->kind) { + case EcsOpPush: + case EcsOpPop: + /* Should not be parsed as single op */ + ecs_throw(ECS_INVALID_PARAMETER, NULL); + break; + case EcsOpF32: + ecs_strbuf_appendflt(str, + (ecs_f64_t)*(const ecs_f32_t*)vptr, '"'); + break; + case EcsOpF64: + ecs_strbuf_appendflt(str, + *(ecs_f64_t*)vptr, '"'); + break; + case EcsOpEnum: + if (flecs_json_ser_enum(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpBitmask: + if (flecs_json_ser_bitmask(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpArray: + if (json_ser_array(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpVector: + if (json_ser_vector(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpOpaque: + if (json_ser_custom_type(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpEntity: { + ecs_entity_t e = *(const ecs_entity_t*)vptr; + if (!e) { + ecs_strbuf_appendlit(str, "\"#0\""); + } else { + flecs_json_path(str, world, e); + } + break; + } + case EcsOpId: { + ecs_id_t id = *(const ecs_id_t*)vptr; + if (!id) { + ecs_strbuf_appendlit(str, "\"#0\""); + } else { + flecs_json_id(str, world, id); + } + break; + } -uint64_t* flecs_entity_index_new_ids( - ecs_entity_index_t *index, - int32_t count) -{ - int32_t alive_count = index->alive_count; - int32_t new_count = alive_count + count; - int32_t dense_count = ecs_vec_count(&index->dense); + case EcsOpU64: + case EcsOpI64: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + if (flecs_expr_ser_primitive(world, + flecs_json_op_to_primitive_kind(op->kind), + ECS_OFFSET(ptr, op->offset), str, true)) + { + ecs_throw(ECS_INTERNAL_ERROR, NULL); + } + break; - if (new_count < dense_count) { - /* Recycle ids */ - index->alive_count = new_count; - return ecs_vec_get_t(&index->dense, uint64_t, alive_count); + case EcsOpPrimitive: + case EcsOpScope: + default: + ecs_throw(ECS_INTERNAL_ERROR, NULL); } - /* Allocate new ids */ - ecs_vec_set_count_t(index->allocator, &index->dense, uint64_t, new_count); - int32_t i, to_add = new_count - dense_count; - for (i = 0; i < to_add; i ++) { - uint32_t id = (uint32_t)++ index->max_id; - int32_t dense = dense_count + i; - ecs_vec_get_t(&index->dense, uint64_t, dense)[0] = id; - ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id); - ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; - r->dense = dense; + if (large_int) { + ecs_strbuf_appendch(str, '"'); } - index->alive_count = new_count; - return ecs_vec_get_t(&index->dense, uint64_t, alive_count); + return 0; +error: + return -1; } -void flecs_entity_index_set_size( - ecs_entity_index_t *index, - int32_t size) +/* Iterate over a slice of the type ops array */ +static +int flecs_json_ser_type_ops( + const ecs_world_t *world, + ecs_meta_type_op_t *ops, + int32_t op_count, + const void *base, + ecs_strbuf_t *str, + int32_t in_array) { - ecs_vec_set_size_t(index->allocator, &index->dense, uint64_t, size); -} + for (int i = 0; i < op_count; i ++) { + ecs_meta_type_op_t *op = &ops[i]; -int32_t flecs_entity_index_count( - const ecs_entity_index_t *index) -{ - return index->alive_count - 1; -} + if (in_array <= 0) { + if (op->name) { + flecs_json_member(str, op->name); + } -int32_t flecs_entity_index_size( - const ecs_entity_index_t *index) -{ - return ecs_vec_count(&index->dense) - 1; + int32_t elem_count = op->count; + if (elem_count > 1) { + /* Serialize inline array */ + if (flecs_json_ser_elements(world, op, op->op_count, base, + elem_count, op->size, str, true)) + { + return -1; + } + + i += op->op_count - 1; + continue; + } + } + + switch(op->kind) { + case EcsOpPush: + flecs_json_object_push(str); + in_array --; + break; + case EcsOpPop: + flecs_json_object_pop(str); + in_array ++; + break; + case EcsOpArray: + case EcsOpVector: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpEntity: + case EcsOpId: + case EcsOpString: + case EcsOpOpaque: + if (flecs_json_ser_type_op(world, op, base, str)) { + goto error; + } + break; + default: + ecs_throw(ECS_INTERNAL_ERROR, NULL); + } + } + + return 0; +error: + return -1; } -int32_t flecs_entity_index_not_alive_count( - const ecs_entity_index_t *index) +/* Iterate over the type ops of a type */ +int flecs_json_ser_type( + const ecs_world_t *world, + const ecs_vec_t *v_ops, + const void *base, + ecs_strbuf_t *str) { - return ecs_vec_count(&index->dense) - index->alive_count; + ecs_meta_type_op_t *ops = ecs_vec_first_t(v_ops, ecs_meta_type_op_t); + int32_t count = ecs_vec_count(v_ops); + return flecs_json_ser_type_ops(world, ops, count, base, str, 0); } -void flecs_entity_index_clear( - ecs_entity_index_t *index) +static +int flecs_array_to_json_buf_w_type_data( + const ecs_world_t *world, + const void *ptr, + int32_t count, + ecs_strbuf_t *buf, + const EcsComponent *comp, + const EcsTypeSerializer *ser) { - int32_t i, count = ecs_vec_count(&index->pages); - ecs_entity_index_page_t **pages = ecs_vec_first_t(&index->pages, - ecs_entity_index_page_t*); - for (i = 0; i < count; i ++) { - ecs_entity_index_page_t *page = pages[i]; - if (page) { - ecs_os_zeromem(page); - } - } + if (count) { + ecs_size_t size = comp->size; - ecs_vec_set_count_t(index->allocator, &index->dense, uint64_t, 1); + flecs_json_array_push(buf); - index->alive_count = 1; - index->max_id = 0; -} + do { + ecs_strbuf_list_next(buf); + if (flecs_json_ser_type(world, &ser->ops, ptr, buf)) { + return -1; + } -const uint64_t* flecs_entity_index_ids( - const ecs_entity_index_t *index) -{ - return ecs_vec_get_t(&index->dense, uint64_t, 1); + ptr = ECS_OFFSET(ptr, size); + } while (-- count); + + flecs_json_array_pop(buf); + } else { + if (flecs_json_ser_type(world, &ser->ops, ptr, buf)) { + return -1; + } + } + + return 0; } -static -void flecs_entity_index_copy_intern( - ecs_entity_index_t * dst, - const ecs_entity_index_t * src) +int ecs_array_to_json_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *ptr, + int32_t count, + ecs_strbuf_t *buf) { - flecs_entity_index_set_size(dst, flecs_entity_index_size(src)); - const uint64_t *ids = flecs_entity_index_ids(src); - - int32_t i, count = src->alive_count; - for (i = 0; i < count - 1; i ++) { - uint64_t id = ids[i]; - ecs_record_t *src_ptr = flecs_entity_index_get(src, id); - ecs_record_t *dst_ptr = flecs_entity_index_ensure(dst, id); - flecs_entity_index_set_generation(dst, id); - ecs_os_memcpy_t(dst_ptr, src_ptr, ecs_record_t); + const EcsComponent *comp = ecs_get(world, type, EcsComponent); + if (!comp) { + char *path = ecs_get_path(world, type); + ecs_err("cannot serialize to JSON, '%s' is not a component", path); + ecs_os_free(path); + return -1; } - dst->max_id = src->max_id; + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); + if (!ser) { + char *path = ecs_get_path(world, type); + ecs_err("cannot serialize to JSON, '%s' has no reflection data", path); + ecs_os_free(path); + return -1; + } - ecs_assert(src->alive_count == dst->alive_count, ECS_INTERNAL_ERROR, NULL); + return flecs_array_to_json_buf_w_type_data(world, ptr, count, buf, comp, ser); } -void flecs_entity_index_copy( - ecs_entity_index_t *dst, - const ecs_entity_index_t *src) +char* ecs_array_to_json( + const ecs_world_t *world, + ecs_entity_t type, + const void* ptr, + int32_t count) { - if (!src) { - return; + ecs_strbuf_t str = ECS_STRBUF_INIT; + + if (ecs_array_to_json_buf(world, type, ptr, count, &str) != 0) { + ecs_strbuf_reset(&str); + return NULL; } - flecs_entity_index_init(src->allocator, dst); - flecs_entity_index_copy_intern(dst, src); + return ecs_strbuf_get(&str); } -void flecs_entity_index_restore( - ecs_entity_index_t *dst, - const ecs_entity_index_t *src) +int ecs_ptr_to_json_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *ptr, + ecs_strbuf_t *buf) { - if (!src) { - return; - } + return ecs_array_to_json_buf(world, type, ptr, 0, buf); +} - flecs_entity_index_clear(dst); - flecs_entity_index_copy_intern(dst, src); +char* ecs_ptr_to_json( + const ecs_world_t *world, + ecs_entity_t type, + const void* ptr) +{ + return ecs_array_to_json(world, type, ptr, 0); } +#endif + /** - * @file id_index.c - * @brief Index for looking up tables by (component) id. - * - * An id record stores the administration for an in use (component) id, that is - * an id that has been used in tables. - * - * An id record contains a table cache, which stores the list of tables that - * have the id. Each entry in the cache (a table record) stores the first - * occurrence of the id in the table and the number of occurrences of the id in - * the table (in the case of wildcard ids). - * - * Id records are used in lots of scenarios, like uncached queries, or for - * getting a component array/component for an entity. + * @file addons/json/serialize_world.c + * @brief Serialize world to JSON. */ -static -ecs_id_record_elem_t* flecs_id_record_elem( - ecs_id_record_t *head, - ecs_id_record_elem_t *list, - ecs_id_record_t *idr) -{ - return ECS_OFFSET(idr, (uintptr_t)list - (uintptr_t)head); -} +#ifdef FLECS_JSON -static -void flecs_id_record_elem_insert( - ecs_id_record_t *head, - ecs_id_record_t *idr, - ecs_id_record_elem_t *elem) +int ecs_world_to_json_buf( + ecs_world_t *world, + ecs_strbuf_t *buf_out, + const ecs_world_to_json_desc_t *desc) { - ecs_id_record_elem_t *head_elem = flecs_id_record_elem(idr, elem, head); - ecs_id_record_t *cur = head_elem->next; - elem->next = cur; - elem->prev = head; - if (cur) { - ecs_id_record_elem_t *cur_elem = flecs_id_record_elem(idr, elem, cur); - cur_elem->prev = idr; + ecs_query_desc_t query_desc = {0}; + + if (desc && desc->serialize_builtin && desc->serialize_modules) { + query_desc.terms[0].id = EcsAny; + } else { + bool serialize_builtin = desc && desc->serialize_builtin; + bool serialize_modules = desc && desc->serialize_modules; + int32_t term_id = 0; + + if (!serialize_builtin) { + query_desc.terms[term_id].id = ecs_pair(EcsChildOf, EcsFlecs); + query_desc.terms[term_id].oper = EcsNot; + query_desc.terms[term_id].src.id = EcsSelf | EcsUp; + term_id ++; + } + if (!serialize_modules) { + query_desc.terms[term_id].id = EcsModule; + query_desc.terms[term_id].oper = EcsNot; + query_desc.terms[term_id].src.id = EcsSelf | EcsUp; + } } - head_elem->next = idr; -} -static -void flecs_id_record_elem_remove( - ecs_id_record_t *idr, - ecs_id_record_elem_t *elem) -{ - ecs_id_record_t *prev = elem->prev; - ecs_id_record_t *next = elem->next; - ecs_assert(prev != NULL, ECS_INTERNAL_ERROR, NULL); + query_desc.flags = EcsQueryMatchDisabled|EcsQueryMatchPrefab; - ecs_id_record_elem_t *prev_elem = flecs_id_record_elem(idr, elem, prev); - prev_elem->next = next; - if (next) { - ecs_id_record_elem_t *next_elem = flecs_id_record_elem(idr, elem, next); - next_elem->prev = prev; + ecs_query_t *q = ecs_query_init(world, &query_desc); + if (!q) { + return -1; } + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t json_desc = { + .serialize_table = true, + .serialize_full_paths = true, + .serialize_entity_ids = true, + .serialize_values = true + }; + + int ret = ecs_iter_to_json_buf(&it, buf_out, &json_desc); + ecs_query_fini(q); + return ret; } -static -void flecs_insert_id_elem( +char* ecs_world_to_json( ecs_world_t *world, - ecs_id_record_t *idr, - ecs_id_t wildcard, - ecs_id_record_t *widr) + const ecs_world_to_json_desc_t *desc) { - ecs_assert(ecs_id_is_wildcard(wildcard), ECS_INTERNAL_ERROR, NULL); - if (!widr) { - widr = flecs_id_record_ensure(world, wildcard); - } - ecs_assert(widr != NULL, ECS_INTERNAL_ERROR, NULL); - - if (ECS_PAIR_SECOND(wildcard) == EcsWildcard) { - ecs_assert(ECS_PAIR_FIRST(wildcard) != EcsWildcard, - ECS_INTERNAL_ERROR, NULL); - flecs_id_record_elem_insert(widr, idr, &idr->first); - } else { - ecs_assert(ECS_PAIR_FIRST(wildcard) == EcsWildcard, - ECS_INTERNAL_ERROR, NULL); - flecs_id_record_elem_insert(widr, idr, &idr->second); + ecs_strbuf_t buf = ECS_STRBUF_INIT; - if (idr->flags & EcsIdTraversable) { - flecs_id_record_elem_insert(widr, idr, &idr->trav); - } + if (ecs_world_to_json_buf(world, &buf, desc)) { + ecs_strbuf_reset(&buf); + return NULL; } + + return ecs_strbuf_get(&buf); } +#endif + + +/** + * @file addons/meta/api.c + * @brief API for creating entities with reflection data. + */ + + +#ifdef FLECS_META + static -void flecs_remove_id_elem( - ecs_id_record_t *idr, - ecs_id_t wildcard) +bool flecs_type_is_number( + ecs_world_t *world, + ecs_entity_t type) { - ecs_assert(ecs_id_is_wildcard(wildcard), ECS_INTERNAL_ERROR, NULL); + const EcsPrimitive *p = ecs_get(world, type, EcsPrimitive); + if (!p) { + return false; + } - if (ECS_PAIR_SECOND(wildcard) == EcsWildcard) { - ecs_assert(ECS_PAIR_FIRST(wildcard) != EcsWildcard, - ECS_INTERNAL_ERROR, NULL); - flecs_id_record_elem_remove(idr, &idr->first); - } else { - ecs_assert(ECS_PAIR_FIRST(wildcard) == EcsWildcard, - ECS_INTERNAL_ERROR, NULL); - flecs_id_record_elem_remove(idr, &idr->second); + switch(p->kind) { + case EcsChar: + case EcsU8: + case EcsU16: + case EcsU32: + case EcsU64: + case EcsI8: + case EcsI16: + case EcsI32: + case EcsI64: + case EcsF32: + case EcsF64: + return true; - if (idr->flags & EcsIdTraversable) { - flecs_id_record_elem_remove(idr, &idr->trav); - } + case EcsBool: + case EcsByte: + case EcsUPtr: + case EcsIPtr: + case EcsString: + case EcsEntity: + case EcsId: + return false; + default: + ecs_abort(ECS_INVALID_PARAMETER, NULL); } } -static -ecs_id_t flecs_id_record_hash( - ecs_id_t id) +/* Serialize a primitive value */ +int flecs_expr_ser_primitive( + const ecs_world_t *world, + ecs_primitive_kind_t kind, + const void *base, + ecs_strbuf_t *str, + bool is_expr) { - id = ecs_strip_generation(id); - if (ECS_IS_PAIR(id)) { - ecs_entity_t r = ECS_PAIR_FIRST(id); - ecs_entity_t o = ECS_PAIR_SECOND(id); - if (r == EcsAny) { - r = EcsWildcard; + switch(kind) { + case EcsBool: + if (*(const bool*)base) { + ecs_strbuf_appendlit(str, "true"); + } else { + ecs_strbuf_appendlit(str, "false"); } - if (o == EcsAny) { - o = EcsWildcard; + break; + case EcsChar: { + char chbuf[3]; + char ch = *(const char*)base; + if (ch) { + flecs_chresc(chbuf, *(const char*)base, '"'); + if (is_expr) ecs_strbuf_appendch(str, '"'); + ecs_strbuf_appendstr(str, chbuf); + if (is_expr) ecs_strbuf_appendch(str, '"'); + } else { + ecs_strbuf_appendch(str, '0'); } - id = ecs_pair(r, o); + break; } - return id; + case EcsByte: + ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint8_t*)base)); + break; + case EcsU8: + ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint8_t*)base)); + break; + case EcsU16: + ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint16_t*)base)); + break; + case EcsU32: + ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint32_t*)base)); + break; + case EcsU64: + ecs_strbuf_append(str, "%llu", *(const uint64_t*)base); + break; + case EcsI8: + ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int8_t*)base)); + break; + case EcsI16: + ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int16_t*)base)); + break; + case EcsI32: + ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int32_t*)base)); + break; + case EcsI64: + ecs_strbuf_appendint(str, *(const int64_t*)base); + break; + case EcsF32: + ecs_strbuf_appendflt(str, (double)*(const float*)base, 0); + break; + case EcsF64: + ecs_strbuf_appendflt(str, *(const double*)base, 0); + break; + case EcsIPtr: + ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const intptr_t*)base)); + break; + case EcsUPtr: + ecs_strbuf_append(str, "%u", *(const uintptr_t*)base); + break; + case EcsString: { + const char *value = *ECS_CONST_CAST(const char**, base); + if (value) { + if (!is_expr) { + ecs_strbuf_appendstr(str, value); + } else { + ecs_size_t length = flecs_stresc(NULL, 0, '"', value); + if (length == ecs_os_strlen(value)) { + ecs_strbuf_appendch(str, '"'); + ecs_strbuf_appendstrn(str, value, length); + ecs_strbuf_appendch(str, '"'); + } else { + char *out = ecs_os_malloc(length + 3); + flecs_stresc(out + 1, length, '"', value); + out[0] = '"'; + out[length + 1] = '"'; + out[length + 2] = '\0'; + ecs_strbuf_appendstr(str, out); + ecs_os_free(out); + } + } + } else { + ecs_strbuf_appendlit(str, "null"); + } + break; + } + case EcsEntity: { + ecs_entity_t e = *(const ecs_entity_t*)base; + if (!e) { + ecs_strbuf_appendlit(str, "#0"); + } else { + ecs_get_path_w_sep_buf(world, 0, e, ".", NULL, str, false); + } + break; + } + case EcsId: { + ecs_id_t id = *(const ecs_id_t*)base; + if (!id) { + ecs_strbuf_appendlit(str, "#0"); + } else { + ecs_id_str_buf(world, id, str); + } + break; + } + default: + ecs_err("invalid primitive kind"); + return -1; + } + + return 0; } -static -ecs_id_record_t* flecs_id_record_new( +ecs_entity_t ecs_primitive_init( ecs_world_t *world, - ecs_id_t id) + const ecs_primitive_desc_t *desc) { - ecs_id_record_t *idr, *idr_t = NULL; - ecs_id_t hash = flecs_id_record_hash(id); - if (hash >= FLECS_HI_ID_RECORD_ID) { - idr = flecs_bcalloc(&world->allocators.id_record); - ecs_map_insert_ptr(&world->id_index_hi, hash, idr); - } else { - idr = &world->id_index_lo[hash]; - ecs_os_zeromem(idr); - } - - ecs_table_cache_init(world, &idr->cache); - - idr->id = id; - idr->refcount = 1; - idr->reachable.current = -1; - - bool is_wildcard = ecs_id_is_wildcard(id); - bool is_pair = ECS_IS_PAIR(id); - - ecs_entity_t rel = 0, tgt = 0, role = id & ECS_ID_FLAGS_MASK; - if (is_pair) { - // rel = ecs_pair_first(world, id); - rel = ECS_PAIR_FIRST(id); - rel = flecs_entities_get_generation(world, rel); - ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL); + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); - /* Relationship object can be 0, as tables without a ChildOf - * relationship are added to the (ChildOf, 0) id record */ - tgt = ECS_PAIR_SECOND(id); + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); + } -#ifdef FLECS_DEBUG - /* Check constraints */ - if (tgt) { - tgt = flecs_entities_get_generation(world, tgt); - ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); - } - if (tgt && !ecs_id_is_wildcard(tgt)) { - /* Check if target of relationship satisfies OneOf property */ - ecs_entity_t oneof = flecs_get_oneof(world, rel); - ecs_check( !oneof || ecs_has_pair(world, tgt, EcsChildOf, oneof), - ECS_CONSTRAINT_VIOLATED, NULL); - (void)oneof; + ecs_set(world, t, EcsPrimitive, { desc->kind }); - /* Check if we're not trying to inherit from a final target */ - if (rel == EcsIsA) { - bool is_final = ecs_has_id(world, tgt, EcsFinal); - ecs_check(!is_final, ECS_CONSTRAINT_VIOLATED, - "cannot inherit from final entity"); - (void)is_final; - } - } -#endif + flecs_resume_readonly(world, &rs); + return t; +} - if (!is_wildcard && (rel != EcsFlag)) { - /* Inherit flags from (relationship, *) record */ - ecs_id_record_t *idr_r = flecs_id_record_ensure( - world, ecs_pair(rel, EcsWildcard)); - idr->parent = idr_r; - idr->flags = idr_r->flags; +ecs_entity_t ecs_enum_init( + ecs_world_t *world, + const ecs_enum_desc_t *desc) +{ + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); - /* If pair is not a wildcard, append it to wildcard lists. These - * allow for quickly enumerating all relationships for an object, - * or all objecs for a relationship. */ - flecs_insert_id_elem(world, idr, ecs_pair(rel, EcsWildcard), idr_r); + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); + } - idr_t = flecs_id_record_ensure(world, ecs_pair(EcsWildcard, tgt)); - flecs_insert_id_elem(world, idr, ecs_pair(EcsWildcard, tgt), idr_t); + ecs_add(world, t, EcsEnum); - if (rel == EcsUnion) { - idr->flags |= EcsIdUnion; - } - } - } else { - rel = id & ECS_COMPONENT_MASK; - ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL); - } + ecs_entity_t old_scope = ecs_set_scope(world, t); - /* Initialize type info if id is not a tag */ - if (!is_wildcard && (!role || is_pair)) { - if (!(idr->flags & EcsIdTag)) { - const ecs_type_info_t *ti = flecs_type_info_get(world, rel); - if (!ti && tgt) { - ti = flecs_type_info_get(world, tgt); - } - idr->type_info = ti; + int i; + for (i = 0; i < ECS_MEMBER_DESC_CACHE_SIZE; i ++) { + const ecs_enum_constant_t *m_desc = &desc->constants[i]; + if (!m_desc->name) { + break; } - } - - /* Mark entities that are used as component/pair ids. When a tracked - * entity is deleted, cleanup policies are applied so that the store - * won't contain any tables with deleted ids. */ - /* Flag for OnDelete policies */ - flecs_add_flag(world, rel, EcsEntityIsId); - if (tgt) { - /* Flag for OnDeleteTarget policies */ - ecs_record_t *tgt_r = flecs_entities_get_any(world, tgt); - ecs_assert(tgt_r != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_record_add_flag(tgt_r, EcsEntityIsTarget); - if (idr->flags & EcsIdTraversable) { - /* Flag used to determine if object should be traversed when - * propagating events or with super/subset queries */ - flecs_record_add_flag(tgt_r, EcsEntityIsTraversable); + ecs_entity_t c = ecs_entity(world, { + .name = m_desc->name + }); - /* Add reference to (*, tgt) id record to entity record */ - tgt_r->idr = idr_t; + if (!m_desc->value) { + ecs_add_id(world, c, EcsConstant); + } else { + ecs_set_pair_second(world, c, EcsConstant, ecs_i32_t, + {m_desc->value}); } } - ecs_observable_t *o = &world->observable; - idr->flags |= flecs_observers_exist(o, id, EcsOnAdd) * EcsIdHasOnAdd; - idr->flags |= flecs_observers_exist(o, id, EcsOnRemove) * EcsIdHasOnRemove; - idr->flags |= flecs_observers_exist(o, id, EcsOnSet) * EcsIdHasOnSet; - idr->flags |= flecs_observers_exist(o, id, EcsUnSet) * EcsIdHasUnSet; - idr->flags |= flecs_observers_exist(o, id, EcsOnTableFill) * EcsIdHasOnTableFill; - idr->flags |= flecs_observers_exist(o, id, EcsOnTableEmpty) * EcsIdHasOnTableEmpty; - idr->flags |= flecs_observers_exist(o, id, EcsOnTableCreate) * EcsIdHasOnTableCreate; - idr->flags |= flecs_observers_exist(o, id, EcsOnTableDelete) * EcsIdHasOnTableDelete; + ecs_set_scope(world, old_scope); + flecs_resume_readonly(world, &rs); - if (ecs_should_log_1()) { - char *id_str = ecs_id_str(world, id); - ecs_dbg_1("#[green]id#[normal] %s #[green]created", id_str); - ecs_os_free(id_str); + if (i == 0) { + ecs_err("enum '%s' has no constants", ecs_get_name(world, t)); + ecs_delete(world, t); + return 0; } - /* Update counters */ - world->info.id_create_total ++; - world->info.component_id_count += idr->type_info != NULL; - world->info.tag_id_count += idr->type_info == NULL; - world->info.pair_id_count += is_pair; - - return idr; -#ifdef FLECS_DEBUG -error: - return NULL; -#endif -} - -static -void flecs_id_record_assert_empty( - ecs_id_record_t *idr) -{ - (void)idr; - ecs_assert(flecs_table_cache_count(&idr->cache) == 0, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(flecs_table_cache_empty_count(&idr->cache) == 0, - ECS_INTERNAL_ERROR, NULL); + return t; } -static -void flecs_id_record_free( +ecs_entity_t ecs_bitmask_init( ecs_world_t *world, - ecs_id_record_t *idr) + const ecs_bitmask_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_id_t id = idr->id; + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); - flecs_id_record_assert_empty(idr); + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); + } - /* Id is still in use by a filter, query, rule or observer */ - ecs_assert((world->flags & EcsWorldQuit) || (idr->keep_alive == 0), - ECS_ID_IN_USE, "cannot delete id that is queried for"); + ecs_add(world, t, EcsBitmask); - if (ECS_IS_PAIR(id)) { - ecs_entity_t rel = ECS_PAIR_FIRST(id); - ecs_entity_t tgt = ECS_PAIR_SECOND(id); - if (!ecs_id_is_wildcard(id)) { - if (ECS_PAIR_FIRST(id) != EcsFlag) { - /* If id is not a wildcard, remove it from the wildcard lists */ - flecs_remove_id_elem(idr, ecs_pair(rel, EcsWildcard)); - flecs_remove_id_elem(idr, ecs_pair(EcsWildcard, tgt)); - } - } else { - ecs_log_push_2(); + ecs_entity_t old_scope = ecs_set_scope(world, t); - /* If id is a wildcard, it means that all id records that match the - * wildcard are also empty, so release them */ - if (ECS_PAIR_FIRST(id) == EcsWildcard) { - /* Iterate (*, Target) list */ - ecs_id_record_t *cur, *next = idr->second.next; - while ((cur = next)) { - flecs_id_record_assert_empty(cur); - next = cur->second.next; - flecs_id_record_release(world, cur); - } - } else { - /* Iterate (Relationship, *) list */ - ecs_assert(ECS_PAIR_SECOND(id) == EcsWildcard, - ECS_INTERNAL_ERROR, NULL); - ecs_id_record_t *cur, *next = idr->first.next; - while ((cur = next)) { - flecs_id_record_assert_empty(cur); - next = cur->first.next; - flecs_id_record_release(world, cur); - } - } + int i; + for (i = 0; i < ECS_MEMBER_DESC_CACHE_SIZE; i ++) { + const ecs_bitmask_constant_t *m_desc = &desc->constants[i]; + if (!m_desc->name) { + break; + } - ecs_log_pop_2(); + ecs_entity_t c = ecs_entity(world, { + .name = m_desc->name + }); + + if (!m_desc->value) { + ecs_add_id(world, c, EcsConstant); + } else { + ecs_set_pair_second(world, c, EcsConstant, ecs_u32_t, + {m_desc->value}); } } - /* Update counters */ - world->info.id_delete_total ++; - world->info.pair_id_count -= ECS_IS_PAIR(id); - world->info.component_id_count -= idr->type_info != NULL; - world->info.tag_id_count -= idr->type_info == NULL; - - /* Unregister the id record from the world & free resources */ - ecs_table_cache_fini(&idr->cache); - flecs_name_index_free(idr->name_index); - ecs_vec_fini_t(&world->allocator, &idr->reachable.ids, ecs_reachable_elem_t); + ecs_set_scope(world, old_scope); + flecs_resume_readonly(world, &rs); - ecs_id_t hash = flecs_id_record_hash(id); - if (hash >= FLECS_HI_ID_RECORD_ID) { - ecs_map_remove(&world->id_index_hi, hash); - flecs_bfree(&world->allocators.id_record, idr); - } else { - idr->id = 0; /* Tombstone */ + if (i == 0) { + ecs_err("bitmask '%s' has no constants", ecs_get_name(world, t)); + ecs_delete(world, t); + return 0; } - if (ecs_should_log_1()) { - char *id_str = ecs_id_str(world, id); - ecs_dbg_1("#[green]id#[normal] %s #[red]deleted", id_str); - ecs_os_free(id_str); - } + return t; } -ecs_id_record_t* flecs_id_record_ensure( +ecs_entity_t ecs_array_init( ecs_world_t *world, - ecs_id_t id) + const ecs_array_desc_t *desc) { - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - idr = flecs_id_record_new(world, id); + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); + + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); } - return idr; + + ecs_set(world, t, EcsArray, { + .type = desc->type, + .count = desc->count + }); + + flecs_resume_readonly(world, &rs); + + return t; } -ecs_id_record_t* flecs_id_record_get( - const ecs_world_t *world, - ecs_id_t id) +ecs_entity_t ecs_vector_init( + ecs_world_t *world, + const ecs_vector_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); - if (id == ecs_pair(EcsIsA, EcsWildcard)) { - return world->idr_isa_wildcard; - } else if (id == ecs_pair(EcsChildOf, EcsWildcard)) { - return world->idr_childof_wildcard; - } else if (id == ecs_pair_t(EcsIdentifier, EcsName)) { - return world->idr_identifier_name; - } + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); - ecs_id_t hash = flecs_id_record_hash(id); - ecs_id_record_t *idr = NULL; - if (hash >= FLECS_HI_ID_RECORD_ID) { - idr = ecs_map_get_deref(&world->id_index_hi, ecs_id_record_t, hash); - } else { - idr = &world->id_index_lo[hash]; - if (!idr->id) { - idr = NULL; - } + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); } - return idr; + ecs_set(world, t, EcsVector, { + .type = desc->type + }); + + flecs_resume_readonly(world, &rs); + + return t; } -ecs_id_record_t* flecs_query_id_record_get( - const ecs_world_t *world, - ecs_id_t id) +static +bool flecs_member_range_overlaps( + const ecs_member_value_range_t *range, + const ecs_member_value_range_t *with) { - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - ecs_entity_t first = ECS_PAIR_FIRST(id); - if (ECS_IS_PAIR(id) && (first != EcsWildcard)) { - idr = flecs_id_record_get(world, ecs_pair(EcsUnion, first)); - } - return idr; + if (ECS_EQ(with->min, with->max)) { + return false; } - if (ECS_IS_PAIR(id) && - ECS_PAIR_SECOND(id) == EcsWildcard && - (idr->flags & EcsIdUnion)) - { - idr = flecs_id_record_get(world, - ecs_pair(EcsUnion, ECS_PAIR_FIRST(id))); + + if (ECS_EQ(range->min, range->max)) { + return false; } - return idr; -} + if (range->min < with->min || + range->max > with->max) + { + return true; + } -void flecs_id_record_claim( - ecs_world_t *world, - ecs_id_record_t *idr) -{ - (void)world; - idr->refcount ++; + return false; } -int32_t flecs_id_record_release( +ecs_entity_t ecs_struct_init( ecs_world_t *world, - ecs_id_record_t *idr) + const ecs_struct_desc_t *desc) { - int32_t rc = -- idr->refcount; - ecs_assert(rc >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); - if (!rc) { - flecs_id_record_free(world, idr); + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); } - return rc; -} + ecs_entity_t old_scope = ecs_set_scope(world, t); -void flecs_id_record_release_tables( - ecs_world_t *world, - ecs_id_record_t *idr) -{ - /* Cache should not contain tables that aren't empty */ - ecs_assert(flecs_table_cache_count(&idr->cache) == 0, - ECS_INTERNAL_ERROR, NULL); + int i; + for (i = 0; i < ECS_MEMBER_DESC_CACHE_SIZE; i ++) { + const ecs_member_t *m_desc = &desc->members[i]; + if (!m_desc->type) { + break; + } - ecs_table_cache_iter_t it; - if (flecs_table_cache_empty_iter(&idr->cache, &it)) { - ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - /* Release current table */ - flecs_table_free(world, tr->hdr.table); + if (!m_desc->name) { + ecs_err("member %d of struct '%s' does not have a name", i, + ecs_get_name(world, t)); + goto error; } - } -} -bool flecs_id_record_set_type_info( - ecs_world_t *world, - ecs_id_record_t *idr, - const ecs_type_info_t *ti) -{ - bool is_wildcard = ecs_id_is_wildcard(idr->id); - if (!is_wildcard) { - if (ti) { - if (!idr->type_info) { - world->info.tag_id_count --; - world->info.component_id_count ++; + ecs_entity_t m = ecs_entity(world, { + .name = m_desc->name + }); + + ecs_set(world, m, EcsMember, { + .type = m_desc->type, + .count = m_desc->count, + .offset = m_desc->offset, + .unit = m_desc->unit, + .use_offset = m_desc->use_offset + }); + + EcsMemberRanges *ranges = NULL; + const ecs_member_value_range_t *range = &m_desc->range; + const ecs_member_value_range_t *error = &m_desc->error_range; + const ecs_member_value_range_t *warning = &m_desc->warning_range; + if (ECS_NEQ(range->min, range->max)) { + ranges = ecs_ensure(world, m, EcsMemberRanges); + if (range->min > range->max) { + char *member_name = ecs_get_path(world, m); + ecs_err("member '%s' has an invalid value range [%f..%f]", + member_name, range->min, range->max); + ecs_os_free(member_name); + goto error; } - } else { - if (idr->type_info) { - world->info.tag_id_count ++; - world->info.component_id_count --; + ranges->value.min = range->min; + ranges->value.max = range->max; + } + if (ECS_NEQ(error->min, error->max)) { + if (error->min > error->max) { + char *member_name = ecs_get_path(world, m); + ecs_err("member '%s' has an invalid error range [%f..%f]", + member_name, error->min, error->max); + ecs_os_free(member_name); + goto error; + } + if (flecs_member_range_overlaps(error, range)) { + char *member_name = ecs_get_path(world, m); + ecs_err("error range of member '%s' overlaps with value range", + member_name); + ecs_os_free(member_name); + goto error; + } + if (!ranges) { + ranges = ecs_ensure(world, m, EcsMemberRanges); + } + ranges->error.min = error->min; + ranges->error.max = error->max; + } + + if (ECS_NEQ(warning->min, warning->max)) { + if (warning->min > warning->max) { + char *member_name = ecs_get_path(world, m); + ecs_err("member '%s' has an invalid warning range [%f..%f]", + member_name, warning->min, warning->max); + ecs_os_free(member_name); + goto error; + } + if (flecs_member_range_overlaps(warning, range)) { + char *member_name = ecs_get_path(world, m); + ecs_err("warning range of member '%s' overlaps with value " + "range", member_name); + ecs_os_free(member_name); + goto error; } + if (flecs_member_range_overlaps(warning, error)) { + char *member_name = ecs_get_path(world, m); + ecs_err("warning range of member '%s' overlaps with error " + "range", member_name); + ecs_os_free(member_name); + goto error; + } + + if (!ranges) { + ranges = ecs_ensure(world, m, EcsMemberRanges); + } + ranges->warning.min = warning->min; + ranges->warning.max = warning->max; + } + + if (ranges && !flecs_type_is_number(world, m_desc->type)) { + char *member_name = ecs_get_path(world, m); + ecs_err("member '%s' has an value/error/warning range, but is not a " + "number", member_name); + ecs_os_free(member_name); + goto error; + } + + if (ranges) { + ecs_modified(world, m, EcsMemberRanges); } } - bool changed = idr->type_info != ti; - idr->type_info = ti; + ecs_set_scope(world, old_scope); + flecs_resume_readonly(world, &rs); - return changed; -} + if (i == 0) { + ecs_err("struct '%s' has no members", ecs_get_name(world, t)); + goto error; + } -ecs_hashmap_t* flecs_id_record_name_index_ensure( - ecs_world_t *world, - ecs_id_record_t *idr) -{ - ecs_hashmap_t *map = idr->name_index; - if (!map) { - map = idr->name_index = flecs_name_index_new(world, &world->allocator); + if (!ecs_has(world, t, EcsStruct)) { + goto error; } - return map; + return t; +error: + flecs_resume_readonly(world, &rs); + if (t) { + ecs_delete(world, t); + } + return 0; } -ecs_hashmap_t* flecs_id_name_index_ensure( +ecs_entity_t ecs_opaque_init( ecs_world_t *world, - ecs_id_t id) + const ecs_opaque_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); + ecs_assert(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(desc->type.as_type != 0, ECS_INVALID_PARAMETER, NULL); - ecs_id_record_t *idr = flecs_id_record_get(world, id); - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); - return flecs_id_record_name_index_ensure(world, idr); + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); + } + + ecs_set_ptr(world, t, EcsOpaque, &desc->type); + + flecs_resume_readonly(world, &rs); + + return t; } -ecs_hashmap_t* flecs_id_name_index_get( - const ecs_world_t *world, - ecs_id_t id) +ecs_entity_t ecs_unit_init( + ecs_world_t *world, + const ecs_unit_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - return NULL; + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); } - return idr->name_index; -} + ecs_entity_t quantity = desc->quantity; + if (quantity) { + if (!ecs_has_id(world, quantity, EcsQuantity)) { + ecs_err("entity '%s' for unit '%s' is not a quantity", + ecs_get_name(world, quantity), ecs_get_name(world, t)); + goto error; + } -ecs_table_record_t* flecs_table_record_get( - const ecs_world_t *world, - const ecs_table_t *table, - ecs_id_t id) -{ - ecs_poly_assert(world, ecs_world_t); + ecs_add_pair(world, t, EcsQuantity, desc->quantity); + } else { + ecs_remove_pair(world, t, EcsQuantity, EcsWildcard); + } - ecs_id_record_t* idr = flecs_id_record_get(world, id); - if (!idr) { - return NULL; + EcsUnit *value = ecs_ensure(world, t, EcsUnit); + value->base = desc->base; + value->over = desc->over; + value->translation = desc->translation; + value->prefix = desc->prefix; + ecs_os_strset(&value->symbol, desc->symbol); + + if (!flecs_unit_validate(world, t, value)) { + goto error; } - return (ecs_table_record_t*)ecs_table_cache_get(&idr->cache, table); -} + ecs_modified(world, t, EcsUnit); -ecs_table_record_t* flecs_id_record_get_table( - const ecs_id_record_t *idr, - const ecs_table_t *table) -{ - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - return (ecs_table_record_t*)ecs_table_cache_get(&idr->cache, table); + flecs_resume_readonly(world, &rs); + return t; +error: + if (t) { + ecs_delete(world, t); + } + flecs_resume_readonly(world, &rs); + return 0; } -void flecs_init_id_records( - ecs_world_t *world) +ecs_entity_t ecs_unit_prefix_init( + ecs_world_t *world, + const ecs_unit_prefix_desc_t *desc) { - /* Cache often used id records on world */ - world->idr_wildcard = flecs_id_record_ensure(world, EcsWildcard); - world->idr_wildcard_wildcard = flecs_id_record_ensure(world, - ecs_pair(EcsWildcard, EcsWildcard)); - world->idr_any = flecs_id_record_ensure(world, EcsAny); - world->idr_isa_wildcard = flecs_id_record_ensure(world, - ecs_pair(EcsIsA, EcsWildcard)); -} + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); -void flecs_fini_id_records( - ecs_world_t *world) -{ - /* Loop & delete first element until there are no elements left. Id records - * can recursively delete each other, this ensures we always have a - * valid iterator. */ - while (ecs_map_count(&world->id_index_hi) > 0) { - ecs_map_iter_t it = ecs_map_iter(&world->id_index_hi); - ecs_map_next(&it); - flecs_id_record_release(world, ecs_map_ptr(&it)); + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); } - int32_t i; - for (i = 0; i < FLECS_HI_ID_RECORD_ID; i ++) { - ecs_id_record_t *idr = &world->id_index_lo[i]; - if (idr->id) { - flecs_id_record_release(world, idr); - } - } + ecs_set(world, t, EcsUnitPrefix, { + .symbol = ECS_CONST_CAST(char*, desc->symbol), + .translation = desc->translation + }); - ecs_assert(ecs_map_count(&world->id_index_hi) == 0, - ECS_INTERNAL_ERROR, NULL); + flecs_resume_readonly(world, &rs); - ecs_map_fini(&world->id_index_hi); - ecs_os_free(world->id_index_lo); + return t; } -/** - * @file table.c - * @brief Table storage implementation. - * - * Tables are the data structure that store the component data. Tables have - * columns for each component in the table, and rows for each entity stored in - * the table. Once created, the component list for a table doesn't change, but - * entities can move from one table to another. - * - * Each table has a type, which is a vector with the (component) ids in the - * table. The vector is sorted by id, which ensures that there can be only one - * table for each unique combination of components. - * - * Not all ids in a table have to be components. Tags are ids that have no - * data type associated with them, and as a result don't need to be explicitly - * stored beyond an element in the table type. To save space and speed up table - * creation, each table has a reference to a "storage table", which is a table - * that only includes component ids (so excluding tags). - * - * Note that the actual data is not stored on the storage table. The storage - * table is only used for sharing administration. A column_map member maps - * between column indices of the table and its storage table. Tables are - * refcounted, which ensures that storage tables won't be deleted if other - * tables have references to it. - */ - +ecs_entity_t ecs_quantity_init( + ecs_world_t *world, + const ecs_entity_desc_t *desc) +{ + ecs_suspend_readonly_state_t rs; + world = flecs_suspend_readonly(world, &rs); -/* Table sanity check to detect storage issues. Only enabled in SANITIZE mode as - * this can severly slow down many ECS operations. */ -#ifdef FLECS_SANITIZE -static -void flecs_table_check_sanity(ecs_table_t *table) { - int32_t size = ecs_vec_size(&table->data.entities); - int32_t count = ecs_vec_count(&table->data.entities); + ecs_entity_t t = ecs_entity_init(world, desc); + if (!t) { + return 0; + } - int32_t i; - int32_t sw_offset = table->_ ? table->_->sw_offset : 0; - int32_t sw_count = table->_ ? table->_->sw_count : 0; - int32_t bs_offset = table->_ ? table->_->bs_offset : 0; - int32_t bs_count = table->_ ? table->_->bs_count : 0; - int32_t type_count = table->type.count; - ecs_id_t *ids = table->type.array; + ecs_add_id(world, t, EcsQuantity); - ecs_assert((sw_count + sw_offset) <= type_count, ECS_INTERNAL_ERROR, NULL); - ecs_assert((bs_count + bs_offset) <= type_count, ECS_INTERNAL_ERROR, NULL); + flecs_resume_readonly(world, &rs); - if (table->column_count) { - int32_t column_count = table->column_count; - ecs_assert(type_count >= column_count, ECS_INTERNAL_ERROR, NULL); + return t; +} - int32_t *column_map = table->column_map; - ecs_assert(column_map != NULL, ECS_INTERNAL_ERROR, NULL); +#endif - ecs_assert(table->data.columns != NULL, ECS_INTERNAL_ERROR, NULL); +/** + * @file addons/meta/c_utils.c + * @brief C utilities for meta addon. + */ - for (i = 0; i < column_count; i ++) { - ecs_vec_t *column = &table->data.columns[i].data; - ecs_assert(size == column->size, ECS_INTERNAL_ERROR, NULL); - ecs_assert(count == column->count, ECS_INTERNAL_ERROR, NULL); - int32_t column_map_id = column_map[i + type_count]; - ecs_assert(column_map_id >= 0, ECS_INTERNAL_ERROR, NULL); - } - } else { - ecs_assert(table->column_map == NULL, ECS_INTERNAL_ERROR, NULL); - } - if (sw_count) { - ecs_assert(table->_->sw_columns != NULL, - ECS_INTERNAL_ERROR, NULL); - for (i = 0; i < sw_count; i ++) { - ecs_switch_t *sw = &table->_->sw_columns[i]; - ecs_assert(ecs_vec_count(&sw->values) == count, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(ECS_PAIR_FIRST(ids[i + sw_offset]) == EcsUnion, - ECS_INTERNAL_ERROR, NULL); - } - } +#ifdef FLECS_META - if (bs_count) { - ecs_assert(table->_->bs_columns != NULL, - ECS_INTERNAL_ERROR, NULL); - for (i = 0; i < bs_count; i ++) { - ecs_bitset_t *bs = &table->_->bs_columns[i]; - ecs_assert(flecs_bitset_count(bs) == count, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(ECS_HAS_ID_FLAG(ids[i + bs_offset], TOGGLE), - ECS_INTERNAL_ERROR, NULL); - } - } +#include - ecs_assert((table->_->traversable_count == 0) || - (table->flags & EcsTableHasTraversable), ECS_INTERNAL_ERROR, NULL); -} -#else -#define flecs_table_check_sanity(table) -#endif +#define ECS_META_IDENTIFIER_LENGTH (256) -/* Set flags for type hooks so table operations can quickly check whether a - * fast or complex operation that invokes hooks is required. */ -static -ecs_flags32_t flecs_type_info_flags( - const ecs_type_info_t *ti) -{ - ecs_flags32_t flags = 0; +#define ecs_meta_error(ctx, ptr, ...)\ + ecs_parser_error((ctx)->name, (ctx)->desc, ptr - (ctx)->desc, __VA_ARGS__); - if (ti->hooks.ctor) { - flags |= EcsTableHasCtors; - } - if (ti->hooks.on_add) { - flags |= EcsTableHasCtors; - } - if (ti->hooks.dtor) { - flags |= EcsTableHasDtors; - } - if (ti->hooks.on_remove) { - flags |= EcsTableHasDtors; - } - if (ti->hooks.copy) { - flags |= EcsTableHasCopy; - } - if (ti->hooks.move) { - flags |= EcsTableHasMove; - } +typedef char ecs_meta_token_t[ECS_META_IDENTIFIER_LENGTH]; - return flags; -} +typedef struct meta_parse_ctx_t { + const char *name; + const char *desc; +} meta_parse_ctx_t; -static -void flecs_table_init_columns( - ecs_world_t *world, - ecs_table_t *table, - int32_t column_count) -{ - if (!column_count) { - return; - } +typedef struct meta_type_t { + ecs_meta_token_t type; + ecs_meta_token_t params; + bool is_const; + bool is_ptr; +} meta_type_t; - int32_t i, cur = 0, ids_count = table->type.count; - ecs_column_t *columns = flecs_wcalloc_n(world, ecs_column_t, column_count); - table->data.columns = columns; +typedef struct meta_member_t { + meta_type_t type; + ecs_meta_token_t name; + int64_t count; + bool is_partial; +} meta_member_t; - ecs_id_t *ids = table->type.array; - ecs_table_record_t *records = table->_->records; - int32_t *t2s = table->column_map; - int32_t *s2t = &table->column_map[ids_count]; +typedef struct meta_constant_t { + ecs_meta_token_t name; + int64_t value; + bool is_value_set; +} meta_constant_t; - for (i = 0; i < ids_count; i ++) { - ecs_table_record_t *tr = &records[i]; - ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; - const ecs_type_info_t *ti = idr->type_info; - if (!ti) { - t2s[i] = -1; - continue; - } +typedef struct meta_params_t { + meta_type_t key_type; + meta_type_t type; + int64_t count; + bool is_key_value; + bool is_fixed_size; +} meta_params_t; - t2s[i] = cur; - s2t[cur] = i; - tr->column = flecs_ito(int16_t, cur); +static +const char* skip_scope(const char *ptr, meta_parse_ctx_t *ctx) { + /* Keep track of which characters were used to open the scope */ + char stack[256]; + int32_t sp = 0; + char ch; - columns[cur].ti = ECS_CONST_CAST(ecs_type_info_t*, ti); - columns[cur].id = ids[i]; - columns[cur].size = ti->size; + while ((ch = *ptr)) { + if (ch == '(' || ch == '<') { + stack[sp] = ch; - if (ECS_IS_PAIR(ids[i])) { - ecs_table_record_t *wc_tr = flecs_id_record_get_table( - idr->parent, table); - if (wc_tr->index == tr->index) { - wc_tr->column = tr->column; + sp ++; + if (sp >= 256) { + ecs_meta_error(ctx, ptr, "maximum level of nesting reached"); + goto error; + } + } else if (ch == ')' || ch == '>') { + sp --; + if ((sp < 0) || (ch == '>' && stack[sp] != '<') || + (ch == ')' && stack[sp] != '(')) + { + ecs_meta_error(ctx, ptr, "mismatching %c in identifier", ch); + goto error; } } -#ifdef FLECS_DEBUG - ecs_vec_init(NULL, &columns[cur].data, ti->size, 0); -#endif + ptr ++; - table->flags |= flecs_type_info_flags(ti); - cur ++; + if (!sp) { + break; + } } + + return ptr; +error: + return NULL; } -/* Initialize table storage */ -void flecs_table_init_data( - ecs_world_t *world, - ecs_table_t *table) +static +const char* parse_c_digit( + const char *ptr, + int64_t *value_out) { - ecs_data_t *storage = &table->data; - ecs_vec_init_t(NULL, &storage->entities, ecs_entity_t, 0); - - flecs_table_init_columns(world, table, table->column_count); - - ecs_table__t *meta = table->_; - int32_t i, sw_count = meta->sw_count; - int32_t bs_count = meta->bs_count; - - if (sw_count) { - meta->sw_columns = flecs_wcalloc_n(world, ecs_switch_t, sw_count); - for (i = 0; i < sw_count; i ++) { - flecs_switch_init(&meta->sw_columns[i], - &world->allocator, 0); - } + char token[24]; + ptr = flecs_parse_ws_eol(ptr); + ptr = flecs_parse_digit(ptr, token); + if (!ptr) { + goto error; } - if (bs_count) { - meta->bs_columns = flecs_wcalloc_n(world, ecs_bitset_t, bs_count); - for (i = 0; i < bs_count; i ++) { - flecs_bitset_init(&meta->bs_columns[i]); - } - } + *value_out = strtol(token, NULL, 0); + + return flecs_parse_ws_eol(ptr); +error: + return NULL; } -/* Initialize table flags. Table flags are used in lots of scenarios to quickly - * check the features of a table without having to inspect the table type. Table - * flags are typically used to early-out of potentially expensive operations. */ static -void flecs_table_init_flags( - ecs_world_t *world, - ecs_table_t *table) +const char* parse_c_identifier( + const char *ptr, + char *buff, + char *params, + meta_parse_ctx_t *ctx) { - ecs_id_t *ids = table->type.array; - int32_t count = table->type.count; - - int32_t i; - for (i = 0; i < count; i ++) { - ecs_id_t id = ids[i]; + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(buff != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ctx != NULL, ECS_INTERNAL_ERROR, NULL); - if (id <= EcsLastInternalComponentId) { - table->flags |= EcsTableHasBuiltins; - } + char *bptr = buff, ch; - if (id == EcsModule) { - table->flags |= EcsTableHasBuiltins; - table->flags |= EcsTableHasModule; - } else if (id == EcsPrefab) { - table->flags |= EcsTableIsPrefab; - } else if (id == EcsDisabled) { - table->flags |= EcsTableIsDisabled; - } else { - if (ECS_IS_PAIR(id)) { - ecs_entity_t r = ECS_PAIR_FIRST(id); + if (params) { + params[0] = '\0'; + } - table->flags |= EcsTableHasPairs; + /* Ignore whitespaces */ + ptr = flecs_parse_ws_eol(ptr); + ch = *ptr; - if (r == EcsIsA) { - table->flags |= EcsTableHasIsA; - } else if (r == EcsChildOf) { - table->flags |= EcsTableHasChildOf; - ecs_entity_t obj = ecs_pair_second(world, id); - ecs_assert(obj != 0, ECS_INTERNAL_ERROR, NULL); + if (!isalpha(ch) && (ch != '_')) { + ecs_meta_error(ctx, ptr, "invalid identifier (starts with '%c')", ch); + goto error; + } - if (obj == EcsFlecs || obj == EcsFlecsCore || - ecs_has_id(world, obj, EcsModule)) - { - /* If table contains entities that are inside one of the - * builtin modules, it contains builtin entities */ - table->flags |= EcsTableHasBuiltins; - table->flags |= EcsTableHasModule; - } - } else if (id == ecs_pair_t(EcsIdentifier, EcsName)) { - table->flags |= EcsTableHasName; - } else if (r == EcsUnion) { - ecs_table__t *meta = table->_; - table->flags |= EcsTableHasUnion; + while ((ch = *ptr) && !isspace(ch) && ch != ';' && ch != ',' && ch != ')' && + ch != '>' && ch != '}' && ch != '*') + { + /* Type definitions can contain macros or templates */ + if (ch == '(' || ch == '<') { + if (!params) { + ecs_meta_error(ctx, ptr, "unexpected %c", *ptr); + goto error; + } - if (!meta->sw_count) { - meta->sw_offset = flecs_ito(int16_t, i); - } - meta->sw_count ++; - } else if (r == ecs_id(EcsTarget)) { - ecs_table__t *meta = table->_; - table->flags |= EcsTableHasTarget; - meta->ft_offset = flecs_ito(int16_t, i); - } else if (r == ecs_id(EcsPoly)) { - table->flags |= EcsTableHasBuiltins; - } - } else { - if (ECS_HAS_ID_FLAG(id, TOGGLE)) { - ecs_table__t *meta = table->_; - table->flags |= EcsTableHasToggle; + const char *end = skip_scope(ptr, ctx); + ecs_os_strncpy(params, ptr, (ecs_size_t)(end - ptr)); + params[end - ptr] = '\0'; - if (!meta->bs_count) { - meta->bs_offset = flecs_ito(int16_t, i); - } - meta->bs_count ++; - } - if (ECS_HAS_ID_FLAG(id, OVERRIDE)) { - table->flags |= EcsTableHasOverrides; - } - } - } + ptr = end; + } else { + *bptr = ch; + bptr ++; + ptr ++; + } } -} -/* Utility function that appends an element to the table record array */ -static -void flecs_table_append_to_records( - ecs_world_t *world, - ecs_table_t *table, - ecs_vec_t *records, - ecs_id_t id, - int32_t column) -{ - /* To avoid a quadratic search, use the O(1) lookup that the index - * already provides. */ - ecs_id_record_t *idr = flecs_id_record_ensure(world, id); - ecs_table_record_t *tr = (ecs_table_record_t*)flecs_id_record_get_table( - idr, table); - if (!tr) { - tr = ecs_vec_append_t(&world->allocator, records, ecs_table_record_t); - tr->index = flecs_ito(int16_t, column); - tr->count = 1; + *bptr = '\0'; - ecs_table_cache_insert(&idr->cache, table, &tr->hdr); - } else { - tr->count ++; + if (!ch) { + ecs_meta_error(ctx, ptr, "unexpected end of token"); + goto error; } - ecs_assert(tr->hdr.cache != NULL, ECS_INTERNAL_ERROR, NULL); + return ptr; +error: + return NULL; } -/* Main table initialization function */ -void flecs_table_init( - ecs_world_t *world, - ecs_table_t *table, - ecs_table_t *from) +static +const char * meta_open_scope( + const char *ptr, + meta_parse_ctx_t *ctx) { - /* Make sure table->flags is initialized */ - flecs_table_init_flags(world, table); + /* Skip initial whitespaces */ + ptr = flecs_parse_ws_eol(ptr); - /* The following code walks the table type to discover which id records the - * table needs to register table records with. - * - * In addition to registering itself with id records for each id in the - * table type, a table also registers itself with wildcard id records. For - * example, if a table contains (Eats, Apples), it will register itself with - * wildcard id records (Eats, *), (*, Apples) and (*, *). This makes it - * easier for wildcard queries to find the relevant tables. */ + /* Is this the start of the type definition? */ + if (ctx->desc == ptr) { + if (*ptr != '{') { + ecs_meta_error(ctx, ptr, "missing '{' in struct definition"); + goto error; + } - int32_t dst_i = 0, dst_count = table->type.count; - int32_t src_i = 0, src_count = 0; - ecs_id_t *dst_ids = table->type.array; - ecs_id_t *src_ids = NULL; - ecs_table_record_t *tr = NULL, *src_tr = NULL; - if (from) { - src_count = from->type.count; - src_ids = from->type.array; - src_tr = from->_->records; + ptr ++; + ptr = flecs_parse_ws_eol(ptr); } - /* We don't know in advance how large the records array will be, so use - * cached vector. This eliminates unnecessary allocations, and/or expensive - * iterations to determine how many records we need. */ - ecs_allocator_t *a = &world->allocator; - ecs_vec_t *records = &world->store.records; - ecs_vec_reset_t(a, records, ecs_table_record_t); - ecs_id_record_t *idr, *childof_idr = NULL; - - int32_t last_id = -1; /* Track last regular (non-pair) id */ - int32_t first_pair = -1; /* Track the first pair in the table */ - int32_t first_role = -1; /* Track first id with role */ + /* Is this the end of the type definition? */ + if (!*ptr) { + ecs_meta_error(ctx, ptr, "missing '}' at end of struct definition"); + goto error; + } - /* Scan to find boundaries of regular ids, pairs and roles */ - for (dst_i = 0; dst_i < dst_count; dst_i ++) { - ecs_id_t dst_id = dst_ids[dst_i]; - if (first_pair == -1 && ECS_IS_PAIR(dst_id)) { - first_pair = dst_i; - } - if ((dst_id & ECS_COMPONENT_MASK) == dst_id) { - last_id = dst_i; - } else if (first_role == -1 && !ECS_IS_PAIR(dst_id)) { - first_role = dst_i; + /* Is this the end of the type definition? */ + if (*ptr == '}') { + ptr = flecs_parse_ws_eol(ptr + 1); + if (*ptr) { + ecs_meta_error(ctx, ptr, + "stray characters after struct definition"); + goto error; } + return NULL; } - /* The easy part: initialize a record for every id in the type */ - for (dst_i = 0; (dst_i < dst_count) && (src_i < src_count); ) { - ecs_id_t dst_id = dst_ids[dst_i]; - ecs_id_t src_id = src_ids[src_i]; + return ptr; +error: + return NULL; +} - idr = NULL; +static +const char* meta_parse_constant( + const char *ptr, + meta_constant_t *token, + meta_parse_ctx_t *ctx) +{ + ptr = meta_open_scope(ptr, ctx); + if (!ptr) { + return NULL; + } - if (dst_id == src_id) { - ecs_assert(src_tr != NULL, ECS_INTERNAL_ERROR, NULL); - idr = (ecs_id_record_t*)src_tr[src_i].hdr.cache; - } else if (dst_id < src_id) { - idr = flecs_id_record_ensure(world, dst_id); - } - if (idr) { - tr = ecs_vec_append_t(a, records, ecs_table_record_t); - tr->hdr.cache = (ecs_table_cache_t*)idr; - tr->index = flecs_ito(int16_t, dst_i); - tr->count = 1; - } + token->is_value_set = false; - dst_i += dst_id <= src_id; - src_i += dst_id >= src_id; + /* Parse token, constant identifier */ + ptr = parse_c_identifier(ptr, token->name, NULL, ctx); + if (!ptr) { + return NULL; } - /* Add remaining ids that the "from" table didn't have */ - for (; (dst_i < dst_count); dst_i ++) { - ecs_id_t dst_id = dst_ids[dst_i]; - tr = ecs_vec_append_t(a, records, ecs_table_record_t); - idr = flecs_id_record_ensure(world, dst_id); - tr->hdr.cache = (ecs_table_cache_t*)idr; - ecs_assert(tr->hdr.cache != NULL, ECS_INTERNAL_ERROR, NULL); - tr->index = flecs_ito(int16_t, dst_i); - tr->count = 1; + ptr = flecs_parse_ws_eol(ptr); + if (!ptr) { + return NULL; } - /* We're going to insert records from the vector into the index that - * will get patched up later. To ensure the record pointers don't get - * invalidated we need to grow the vector so that it won't realloc as - * we're adding the next set of records */ - if (first_role != -1 || first_pair != -1) { - int32_t start = first_role; - if (first_pair != -1 && (start != -1 || first_pair < start)) { - start = first_pair; - } - - /* Total number of records can never be higher than - * - number of regular (non-pair) ids + - * - three records for pairs: (R,T), (R,*), (*,T) - * - one wildcard (*), one any (_) and one pair wildcard (*,*) record - * - one record for (ChildOf, 0) - */ - int32_t flag_id_count = dst_count - start; - int32_t record_count = start + 3 * flag_id_count + 3 + 1; - ecs_vec_set_min_size_t(a, records, ecs_table_record_t, record_count); + /* Explicit value assignment */ + if (*ptr == '=') { + int64_t value = 0; + ptr = parse_c_digit(ptr + 1, &value); + token->value = value; + token->is_value_set = true; } - /* Add records for ids with roles (used by cleanup logic) */ - if (first_role != -1) { - for (dst_i = first_role; dst_i < dst_count; dst_i ++) { - ecs_id_t id = dst_ids[dst_i]; - if (!ECS_IS_PAIR(id)) { - ecs_entity_t first = 0; - ecs_entity_t second = 0; - if (ECS_HAS_ID_FLAG(id, PAIR)) { - first = ECS_PAIR_FIRST(id); - second = ECS_PAIR_SECOND(id); - } else { - first = id & ECS_COMPONENT_MASK; - } - if (first) { - flecs_table_append_to_records(world, table, records, - ecs_pair(EcsFlag, first), dst_i); - } - if (second) { - flecs_table_append_to_records(world, table, records, - ecs_pair(EcsFlag, second), dst_i); - } - } - } + /* Expect a ',' or '}' */ + if (*ptr != ',' && *ptr != '}') { + ecs_meta_error(ctx, ptr, "missing , after enum constant"); + goto error; } - int32_t last_pair = -1; - bool has_childof = table->flags & EcsTableHasChildOf; - if (first_pair != -1) { - /* Add a (Relationship, *) record for each relationship. */ - ecs_entity_t r = 0; - for (dst_i = first_pair; dst_i < dst_count; dst_i ++) { - ecs_id_t dst_id = dst_ids[dst_i]; - if (!ECS_IS_PAIR(dst_id)) { - break; /* no more pairs */ - } - if (r != ECS_PAIR_FIRST(dst_id)) { /* New relationship, new record */ - tr = ecs_vec_get_t(records, ecs_table_record_t, dst_i); - - ecs_id_record_t *p_idr = (ecs_id_record_t*)tr->hdr.cache; - r = ECS_PAIR_FIRST(dst_id); - if (r == EcsChildOf) { - childof_idr = p_idr; - } + if (*ptr == ',') { + return ptr + 1; + } else { + return ptr; + } +error: + return NULL; +} - idr = p_idr->parent; /* (R, *) */ - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); +static +const char* meta_parse_type( + const char *ptr, + meta_type_t *token, + meta_parse_ctx_t *ctx) +{ + token->is_ptr = false; + token->is_const = false; - tr = ecs_vec_append_t(a, records, ecs_table_record_t); - tr->hdr.cache = (ecs_table_cache_t*)idr; - tr->index = flecs_ito(int16_t, dst_i); - tr->count = 0; - } + ptr = flecs_parse_ws_eol(ptr); - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - tr->count ++; - } + /* Parse token, expect type identifier or ECS_PROPERTY */ + ptr = parse_c_identifier(ptr, token->type, token->params, ctx); + if (!ptr) { + goto error; + } - last_pair = dst_i; + if (!strcmp(token->type, "ECS_PRIVATE")) { + /* Members from this point are not stored in metadata */ + ptr += ecs_os_strlen(ptr); + goto done; + } - /* Add a (*, Target) record for each relationship target. Type - * ids are sorted relationship-first, so we can't simply do a single linear - * scan to find all occurrences for a target. */ - for (dst_i = first_pair; dst_i < last_pair; dst_i ++) { - ecs_id_t dst_id = dst_ids[dst_i]; - ecs_id_t tgt_id = ecs_pair(EcsWildcard, ECS_PAIR_SECOND(dst_id)); + /* If token is const, set const flag and continue parsing type */ + if (!strcmp(token->type, "const")) { + token->is_const = true; - flecs_table_append_to_records( - world, table, records, tgt_id, dst_i); - } + /* Parse type after const */ + ptr = parse_c_identifier(ptr + 1, token->type, token->params, ctx); } - /* Lastly, add records for all-wildcard ids */ - if (last_id >= 0) { - tr = ecs_vec_append_t(a, records, ecs_table_record_t); - tr->hdr.cache = (ecs_table_cache_t*)world->idr_wildcard; - tr->index = 0; - tr->count = flecs_ito(int16_t, last_id + 1); - } - if (last_pair - first_pair) { - tr = ecs_vec_append_t(a, records, ecs_table_record_t); - tr->hdr.cache = (ecs_table_cache_t*)world->idr_wildcard_wildcard; - tr->index = flecs_ito(int16_t, first_pair); - tr->count = flecs_ito(int16_t, last_pair - first_pair); - } - if (dst_count) { - tr = ecs_vec_append_t(a, records, ecs_table_record_t); - tr->hdr.cache = (ecs_table_cache_t*)world->idr_any; - tr->index = 0; - tr->count = 1; + /* Check if type is a pointer */ + ptr = flecs_parse_ws_eol(ptr); + if (*ptr == '*') { + token->is_ptr = true; + ptr ++; } - if (dst_count && !has_childof) { - tr = ecs_vec_append_t(a, records, ecs_table_record_t); - childof_idr = world->idr_childof_0; - tr->hdr.cache = (ecs_table_cache_t*)childof_idr; - tr->index = 0; - tr->count = 1; + +done: + return ptr; +error: + return NULL; +} + +static +const char* meta_parse_member( + const char *ptr, + meta_member_t *token, + meta_parse_ctx_t *ctx) +{ + ptr = meta_open_scope(ptr, ctx); + if (!ptr) { + return NULL; } - /* Now that all records have been added, copy them to array */ - int32_t i, dst_record_count = ecs_vec_count(records); - ecs_table_record_t *dst_tr = flecs_wdup_n(world, ecs_table_record_t, - dst_record_count, ecs_vec_first_t(records, ecs_table_record_t)); - table->_->record_count = flecs_ito(int16_t, dst_record_count); - table->_->records = dst_tr; - int32_t column_count = 0; + token->count = 1; + token->is_partial = false; - /* Register & patch up records */ - for (i = 0; i < dst_record_count; i ++) { - tr = &dst_tr[i]; - idr = (ecs_id_record_t*)dst_tr[i].hdr.cache; - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + /* Parse member type */ + ptr = meta_parse_type(ptr, &token->type, ctx); + if (!ptr) { + token->is_partial = true; + goto error; + } - if (ecs_table_cache_get(&idr->cache, table)) { - /* If this is a target wildcard record it has already been - * registered, but the record is now at a different location in - * memory. Patch up the linked list with the new address */ - ecs_table_cache_replace(&idr->cache, table, &tr->hdr); - } else { - /* Other records are not registered yet */ - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_cache_insert(&idr->cache, table, &tr->hdr); - } + if (!ptr[0]) { + return ptr; + } - /* Claim id record so it stays alive as long as the table exists */ - flecs_id_record_claim(world, idr); + /* Next token is the identifier */ + ptr = parse_c_identifier(ptr, token->name, NULL, ctx); + if (!ptr) { + goto error; + } - /* Initialize event flags */ - table->flags |= idr->flags & EcsIdEventMask; + /* Skip whitespace between member and [ or ; */ + ptr = flecs_parse_ws_eol(ptr); + + /* Check if this is an array */ + char *array_start = strchr(token->name, '['); + if (!array_start) { + /* If the [ was separated by a space, it will not be parsed as part of + * the name */ + if (*ptr == '[') { + /* safe, will not be modified */ + array_start = ECS_CONST_CAST(char*, ptr); + } + } - /* Initialize column index (will be overwritten by init_columns) */ - tr->column = -1; + if (array_start) { + /* Check if the [ matches with a ] */ + char *array_end = strchr(array_start, ']'); + if (!array_end) { + ecs_meta_error(ctx, ptr, "missing ']'"); + goto error; - if (idr->flags & EcsIdAlwaysOverride) { - table->flags |= EcsTableHasOverrides; + } else if (array_end - array_start == 0) { + ecs_meta_error(ctx, ptr, "dynamic size arrays are not supported"); + goto error; } - if ((i < table->type.count) && (idr->type_info != NULL)) { - column_count ++; - } - } + token->count = atoi(array_start + 1); - if (column_count) { - table->column_map = flecs_walloc_n(world, int32_t, - dst_count + column_count); + if (array_start == ptr) { + /* If [ was found after name, continue parsing after ] */ + ptr = array_end + 1; + } else { + /* If [ was found in name, replace it with 0 terminator */ + array_start[0] = '\0'; + } } - table->column_count = flecs_ito(int16_t, column_count); - flecs_table_init_data(world, table); - if (table->flags & EcsTableHasName) { - ecs_assert(childof_idr != NULL, ECS_INTERNAL_ERROR, NULL); - table->_->name_index = - flecs_id_record_name_index_ensure(world, childof_idr); - ecs_assert(table->_->name_index != NULL, ECS_INTERNAL_ERROR, NULL); + /* Expect a ; */ + if (*ptr != ';') { + ecs_meta_error(ctx, ptr, "missing ; after member declaration"); + goto error; } - if (table->flags & EcsTableHasOnTableCreate) { - flecs_emit(world, world, &(ecs_event_desc_t) { - .ids = &table->type, - .event = EcsOnTableCreate, - .table = table, - .flags = EcsEventTableOnly, - .observable = world - }); - } + return ptr + 1; +error: + return NULL; } -/* Unregister table from id records */ static -void flecs_table_records_unregister( - ecs_world_t *world, - ecs_table_t *table) +int meta_parse_desc( + const char *ptr, + meta_params_t *token, + meta_parse_ctx_t *ctx) { - 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]; - ecs_table_cache_t *cache = tr->hdr.cache; - ecs_id_t id = ((ecs_id_record_t*)cache)->id; + token->is_key_value = false; + token->is_fixed_size = false; - ecs_assert(tr->hdr.cache == cache, ECS_INTERNAL_ERROR, NULL); - ecs_assert(tr->hdr.table == table, ECS_INTERNAL_ERROR, NULL); - ecs_assert(flecs_id_record_get(world, id) == (ecs_id_record_t*)cache, - ECS_INTERNAL_ERROR, NULL); - (void)id; + ptr = flecs_parse_ws_eol(ptr); + if (*ptr != '(' && *ptr != '<') { + ecs_meta_error(ctx, ptr, + "expected '(' at start of collection definition"); + goto error; + } - ecs_table_cache_remove(cache, table_id, &tr->hdr); - flecs_id_record_release(world, (ecs_id_record_t*)cache); + ptr ++; + + /* Parse type identifier */ + ptr = meta_parse_type(ptr, &token->type, ctx); + if (!ptr) { + goto error; } - flecs_wfree_n(world, ecs_table_record_t, count, table->_->records); -} + ptr = flecs_parse_ws_eol(ptr); -/* Keep track for what kind of builtin events observers are registered that can - * potentially match the table. This allows code to early out of calling the - * emit function that notifies observers. */ -static -void flecs_table_add_trigger_flags( - ecs_world_t *world, - ecs_table_t *table, - ecs_entity_t event) -{ - (void)world; + /* If next token is a ',' the first type was a key type */ + if (*ptr == ',') { + ptr = flecs_parse_ws_eol(ptr + 1); + + if (isdigit(*ptr)) { + int64_t value; + ptr = parse_c_digit(ptr, &value); + if (!ptr) { + goto error; + } - if (event == EcsOnAdd) { - table->flags |= EcsTableHasOnAdd; - } else if (event == EcsOnRemove) { - table->flags |= EcsTableHasOnRemove; - } else if (event == EcsOnSet) { - table->flags |= EcsTableHasOnSet; - } else if (event == EcsUnSet) { - table->flags |= EcsTableHasUnSet; - } else if (event == EcsOnTableFill) { - table->flags |= EcsTableHasOnTableFill; - } else if (event == EcsOnTableEmpty) { - table->flags |= EcsTableHasOnTableEmpty; + token->count = value; + token->is_fixed_size = true; + } else { + token->key_type = token->type; + + /* Parse element type */ + ptr = meta_parse_type(ptr, &token->type, ctx); + ptr = flecs_parse_ws_eol(ptr); + + token->is_key_value = true; + } } -} -/* Invoke OnRemove observers for all entities in table. Useful during table - * deletion or when clearing entities from a table. */ -static -void flecs_table_notify_on_remove( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data) -{ - int32_t count = data->entities.count; - if (count) { - flecs_notify_on_remove(world, table, NULL, 0, count, &table->type); + if (*ptr != ')' && *ptr != '>') { + ecs_meta_error(ctx, ptr, + "expected ')' at end of collection definition"); + goto error; } + + return 0; +error: + return -1; } -/* Invoke type hook for entities in table */ static -void flecs_table_invoke_hook( +ecs_entity_t meta_lookup( ecs_world_t *world, - ecs_table_t *table, - ecs_iter_action_t callback, - ecs_entity_t event, - ecs_column_t *column, - ecs_entity_t *entities, - int32_t row, - int32_t count) -{ - void *ptr = ecs_vec_get(&column->data, column->size, row); - flecs_invoke_hook(world, table, count, row, entities, ptr, column->id, - column->ti, event, callback); -} + meta_type_t *token, + const char *ptr, + int64_t count, + meta_parse_ctx_t *ctx); -/* Construct components */ static -void flecs_table_invoke_ctor( - ecs_column_t *column, - int32_t row, - int32_t count) +ecs_entity_t meta_lookup_array( + ecs_world_t *world, + ecs_entity_t e, + const char *params_decl, + meta_parse_ctx_t *ctx) { - ecs_type_info_t *ti = column->ti; - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_xtor_t ctor = ti->hooks.ctor; - if (ctor) { - void *ptr = ecs_vec_get(&column->data, column->size, row); - ctor(ptr, count, ti); + meta_parse_ctx_t param_ctx = { + .name = ctx->name, + .desc = params_decl + }; + + meta_params_t params; + if (meta_parse_desc(params_decl, ¶ms, ¶m_ctx)) { + goto error; + } + if (!params.is_fixed_size) { + ecs_meta_error(ctx, params_decl, "missing size for array"); + goto error; } -} -/* Destruct components */ -static -void flecs_table_invoke_dtor( - ecs_column_t *column, - int32_t row, - int32_t count) -{ - ecs_type_info_t *ti = column->ti; - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_xtor_t dtor = ti->hooks.dtor; - if (dtor) { - void *ptr = ecs_vec_get(&column->data, column->size, row); - dtor(ptr, count, ti); + if (!params.count) { + ecs_meta_error(ctx, params_decl, "invalid array size"); + goto error; + } + + ecs_entity_t element_type = ecs_lookup_symbol( + world, params.type.type, true, true); + if (!element_type) { + ecs_meta_error(ctx, params_decl, "unknown element type '%s'", + params.type.type); } + + if (!e) { + e = ecs_new(world); + } + + ecs_check(params.count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL); + + ecs_set(world, e, EcsArray, { element_type, (int32_t)params.count }); + + return e; +error: + return 0; } -/* Run hooks that get invoked when component is added to entity */ static -void flecs_table_invoke_add_hooks( +ecs_entity_t meta_lookup_vector( ecs_world_t *world, - ecs_table_t *table, - ecs_column_t *column, - ecs_entity_t *entities, - int32_t row, - int32_t count, - bool construct) + ecs_entity_t e, + const char *params_decl, + meta_parse_ctx_t *ctx) { - ecs_type_info_t *ti = column->ti; - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + meta_parse_ctx_t param_ctx = { + .name = ctx->name, + .desc = params_decl + }; - if (construct) { - flecs_table_invoke_ctor(column, row, count); + meta_params_t params; + if (meta_parse_desc(params_decl, ¶ms, ¶m_ctx)) { + goto error; } - ecs_iter_action_t on_add = ti->hooks.on_add; - if (on_add) { - flecs_table_invoke_hook(world, table, on_add, EcsOnAdd, column, - entities, row, count); + if (params.is_key_value) { + ecs_meta_error(ctx, params_decl, + "unexpected key value parameters for vector"); + goto error; + } + + ecs_entity_t element_type = meta_lookup( + world, ¶ms.type, params_decl, 1, ¶m_ctx); + + if (!e) { + e = ecs_new(world); } + + ecs_set(world, e, EcsVector, { element_type }); + + return e; +error: + return 0; } -/* Run hooks that get invoked when component is removed from entity */ static -void flecs_table_invoke_remove_hooks( +ecs_entity_t meta_lookup_bitmask( ecs_world_t *world, - ecs_table_t *table, - ecs_column_t *column, - ecs_entity_t *entities, - int32_t row, - int32_t count, - bool dtor) + ecs_entity_t e, + const char *params_decl, + meta_parse_ctx_t *ctx) { - ecs_type_info_t *ti = column->ti; - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + (void)e; - ecs_iter_action_t on_remove = ti->hooks.on_remove; - if (on_remove) { - flecs_table_invoke_hook(world, table, on_remove, EcsOnRemove, column, - entities, row, count); + meta_parse_ctx_t param_ctx = { + .name = ctx->name, + .desc = params_decl + }; + + meta_params_t params; + if (meta_parse_desc(params_decl, ¶ms, ¶m_ctx)) { + goto error; } - - if (dtor) { - flecs_table_invoke_dtor(column, row, count); + + if (params.is_key_value) { + ecs_meta_error(ctx, params_decl, + "unexpected key value parameters for bitmask"); + goto error; + } + + if (params.is_fixed_size) { + ecs_meta_error(ctx, params_decl, + "unexpected size for bitmask"); + goto error; } + + ecs_entity_t bitmask_type = meta_lookup( + world, ¶ms.type, params_decl, 1, ¶m_ctx); + ecs_check(bitmask_type != 0, ECS_INVALID_PARAMETER, NULL); + +#ifndef FLECS_NDEBUG + /* Make sure this is a bitmask type */ + const EcsType *type_ptr = ecs_get(world, bitmask_type, EcsType); + ecs_check(type_ptr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(type_ptr->kind == EcsBitmaskType, ECS_INVALID_PARAMETER, NULL); +#endif + + return bitmask_type; +error: + return 0; } -/* Destruct all components and/or delete all entities in table in range */ static -void flecs_table_dtor_all( +ecs_entity_t meta_lookup( ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data, - int32_t row, - int32_t count, - bool update_entity_index, - bool is_delete) + meta_type_t *token, + const char *ptr, + int64_t count, + meta_parse_ctx_t *ctx) { - /* Can't delete and not update the entity index */ - ecs_assert(!is_delete || update_entity_index, ECS_INTERNAL_ERROR, NULL); + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(token != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ctx != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t ids_count = table->column_count; - ecs_entity_t *entities = data->entities.array; - int32_t i, c, end = row + count; + const char *typename = token->type; + ecs_entity_t type = 0; - if (is_delete && table->_->traversable_count) { - /* If table contains monitored entities with traversable relationships, - * make sure to invalidate observer cache */ - flecs_emit_propagate_invalidate(world, table, row, count); - } + /* Parse vector type */ + if (!token->is_ptr) { + if (!ecs_os_strcmp(typename, "ecs_array")) { + type = meta_lookup_array(world, 0, token->params, ctx); - /* If table has components with destructors, iterate component columns */ - if (table->flags & EcsTableHasDtors) { - /* Throw up a lock just to be sure */ - table->_->lock = true; + } else if (!ecs_os_strcmp(typename, "ecs_vector") || + !ecs_os_strcmp(typename, "flecs::vector")) + { + type = meta_lookup_vector(world, 0, token->params, ctx); - /* Run on_remove callbacks first before destructing components */ - for (c = 0; c < ids_count; c++) { - ecs_column_t *column = &data->columns[c]; - ecs_iter_action_t on_remove = column->ti->hooks.on_remove; - if (on_remove) { - flecs_table_invoke_hook(world, table, on_remove, EcsOnRemove, - column, &entities[row], row, count); - } - } + } else if (!ecs_os_strcmp(typename, "flecs::bitmask")) { + type = meta_lookup_bitmask(world, 0, token->params, ctx); - /* Destruct components */ - for (c = 0; c < ids_count; c++) { - flecs_table_invoke_dtor(&data->columns[c], row, count); - } + } else if (!ecs_os_strcmp(typename, "flecs::byte")) { + type = ecs_id(ecs_byte_t); - /* Iterate entities first, then components. This ensures that only one - * entity is invalidated at a time, which ensures that destructors can - * safely access other entities. */ - for (i = row; i < end; i ++) { - /* Update entity index after invoking destructors so that entity can - * be safely used in destructor callbacks. */ - if (update_entity_index) { - ecs_entity_t e = entities[i]; - ecs_assert(!e || ecs_is_valid(world, e), - ECS_INTERNAL_ERROR, NULL); + } else if (!ecs_os_strcmp(typename, "char")) { + type = ecs_id(ecs_char_t); - if (is_delete) { - flecs_entities_remove(world, e); - ecs_assert(ecs_is_valid(world, e) == false, - ECS_INTERNAL_ERROR, NULL); - } else { - // If this is not a delete, clear the entity index record - ecs_record_t *record = flecs_entities_get(world, e); - record->table = NULL; - record->row = 0; - } - } else { - /* This should only happen in rare cases, such as when the data - * cleaned up is not part of the world (like with snapshots) */ - } - } + } else if (!ecs_os_strcmp(typename, "bool") || + !ecs_os_strcmp(typename, "_Bool")) + { + type = ecs_id(ecs_bool_t); - table->_->lock = false; + } else if (!ecs_os_strcmp(typename, "int8_t")) { + type = ecs_id(ecs_i8_t); + } else if (!ecs_os_strcmp(typename, "int16_t")) { + type = ecs_id(ecs_i16_t); + } else if (!ecs_os_strcmp(typename, "int32_t")) { + type = ecs_id(ecs_i32_t); + } else if (!ecs_os_strcmp(typename, "int64_t")) { + type = ecs_id(ecs_i64_t); - /* If table does not have destructors, just update entity index */ - } else if (update_entity_index) { - if (is_delete) { - for (i = row; i < end; i ++) { - ecs_entity_t e = entities[i]; - ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); - flecs_entities_remove(world, e); - ecs_assert(!ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); - } + } else if (!ecs_os_strcmp(typename, "uint8_t")) { + type = ecs_id(ecs_u8_t); + } else if (!ecs_os_strcmp(typename, "uint16_t")) { + type = ecs_id(ecs_u16_t); + } else if (!ecs_os_strcmp(typename, "uint32_t")) { + type = ecs_id(ecs_u32_t); + } else if (!ecs_os_strcmp(typename, "uint64_t")) { + type = ecs_id(ecs_u64_t); + + } else if (!ecs_os_strcmp(typename, "float")) { + type = ecs_id(ecs_f32_t); + } else if (!ecs_os_strcmp(typename, "double")) { + type = ecs_id(ecs_f64_t); + + } else if (!ecs_os_strcmp(typename, "ecs_entity_t")) { + type = ecs_id(ecs_entity_t); + + } else if (!ecs_os_strcmp(typename, "ecs_id_t")) { + type = ecs_id(ecs_id_t); + + } else if (!ecs_os_strcmp(typename, "char*")) { + type = ecs_id(ecs_string_t); } else { - for (i = row; i < end; i ++) { - ecs_entity_t e = entities[i]; - ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); - ecs_record_t *record = flecs_entities_get(world, e); - record->table = NULL; - record->row = record->row & ECS_ROW_FLAGS_MASK; - (void)e; - } - } + type = ecs_lookup_symbol(world, typename, true, true); + } + } else { + if (!ecs_os_strcmp(typename, "char")) { + typename = "flecs.meta.string"; + } else + if (token->is_ptr) { + typename = "flecs.meta.uptr"; + } else + if (!ecs_os_strcmp(typename, "char*") || + !ecs_os_strcmp(typename, "flecs::string")) + { + typename = "flecs.meta.string"; + } + + type = ecs_lookup_symbol(world, typename, true, true); + } + + if (count != 1) { + ecs_check(count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL); + + type = ecs_insert(world, ecs_value(EcsArray, {type, (int32_t)count})); + } + + if (!type) { + ecs_meta_error(ctx, ptr, "unknown type '%s'", typename); + goto error; } + + return type; +error: + return 0; } -/* Cleanup table storage */ static -void flecs_table_fini_data( +int meta_parse_struct( ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data, - bool do_on_remove, - bool update_entity_index, - bool is_delete, - bool deactivate) + ecs_entity_t t, + const char *desc) +{ + const char *ptr = desc; + const char *name = ecs_get_name(world, t); + + meta_member_t token; + meta_parse_ctx_t ctx = { + .name = name, + .desc = ptr + }; + + ecs_entity_t old_scope = ecs_set_scope(world, t); + + while ((ptr = meta_parse_member(ptr, &token, &ctx)) && ptr[0]) { + ecs_entity_t m = ecs_entity(world, { + .name = token.name + }); + + ecs_entity_t type = meta_lookup( + world, &token.type, ptr, 1, &ctx); + if (!type) { + goto error; + } + + ecs_set(world, m, EcsMember, { + .type = type, + .count = (ecs_size_t)token.count + }); + } + + ecs_set_scope(world, old_scope); + + return 0; +error: + return -1; +} + +static +int meta_parse_constants( + ecs_world_t *world, + ecs_entity_t t, + const char *desc, + bool is_bitmask) { - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(t != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); + + const char *ptr = desc; + const char *name = ecs_get_name(world, t); + int32_t name_len = ecs_os_strlen(name); + const ecs_world_info_t *info = ecs_get_world_info(world); + const char *name_prefix = info->name_prefix; + int32_t name_prefix_len = name_prefix ? ecs_os_strlen(name_prefix) : 0; - if (!data) { - return; - } + meta_parse_ctx_t ctx = { + .name = name, + .desc = ptr + }; - if (do_on_remove) { - flecs_table_notify_on_remove(world, table, data); - } + meta_constant_t token; + int64_t last_value = 0; - int32_t count = flecs_table_data_count(data); - if (count) { - flecs_table_dtor_all(world, table, data, 0, count, - update_entity_index, is_delete); - } + ecs_entity_t old_scope = ecs_set_scope(world, t); - ecs_column_t *columns = data->columns; - if (columns) { - int32_t c, column_count = table->column_count; - for (c = 0; c < column_count; c ++) { - /* Sanity check */ - ecs_assert(columns[c].data.count == data->entities.count, - ECS_INTERNAL_ERROR, NULL); - ecs_vec_fini(&world->allocator, - &columns[c].data, columns[c].size); + while ((ptr = meta_parse_constant(ptr, &token, &ctx))) { + if (token.is_value_set) { + last_value = token.value; + } else if (is_bitmask) { + ecs_meta_error(&ctx, ptr, + "bitmask requires explicit value assignment"); + goto error; } - flecs_wfree_n(world, ecs_column_t, column_count, columns); - data->columns = NULL; - } - ecs_table__t *meta = table->_; - ecs_switch_t *sw_columns = meta->sw_columns; - if (sw_columns) { - int32_t c, column_count = meta->sw_count; - for (c = 0; c < column_count; c ++) { - flecs_switch_fini(&sw_columns[c]); + if (name_prefix) { + if (!ecs_os_strncmp(token.name, name_prefix, name_prefix_len)) { + ecs_os_memmove(token.name, token.name + name_prefix_len, + ecs_os_strlen(token.name) - name_prefix_len + 1); + } } - flecs_wfree_n(world, ecs_switch_t, column_count, sw_columns); - meta->sw_columns = NULL; - } - ecs_bitset_t *bs_columns = meta->bs_columns; - if (bs_columns) { - int32_t c, column_count = meta->bs_count; - for (c = 0; c < column_count; c ++) { - flecs_bitset_fini(&bs_columns[c]); + if (!ecs_os_strncmp(token.name, name, name_len)) { + ecs_os_memmove(token.name, token.name + name_len, + ecs_os_strlen(token.name) - name_len + 1); } - flecs_wfree_n(world, ecs_bitset_t, column_count, bs_columns); - meta->bs_columns = NULL; - } - - ecs_vec_fini_t(&world->allocator, &data->entities, ecs_entity_t); - - if (deactivate && count) { - flecs_table_set_empty(world, table); - } - - table->_->traversable_count = 0; - table->flags &= ~EcsTableHasTraversable; -} -ecs_vec_t* flecs_table_entities( - ecs_table_t *table) -{ - return &table->data.entities; -} + ecs_entity_t c = ecs_entity(world, { + .name = token.name + }); -ecs_entity_t* flecs_table_entities_array( - ecs_table_t *table) -{ - return ecs_vec_first(flecs_table_entities(table)); -} + if (!is_bitmask) { + ecs_set_pair_second(world, c, EcsConstant, ecs_i32_t, + {(ecs_i32_t)last_value}); + } else { + ecs_set_pair_second(world, c, EcsConstant, ecs_u32_t, + {(ecs_u32_t)last_value}); + } -/* Cleanup, no OnRemove, don't update entity index, don't deactivate table */ -void flecs_table_clear_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data) -{ - flecs_table_fini_data(world, table, data, false, false, false, false); -} + last_value ++; + } -/* Cleanup, no OnRemove, clear entity index, deactivate table */ -void flecs_table_clear_entities_silent( - ecs_world_t *world, - ecs_table_t *table) -{ - flecs_table_fini_data(world, table, &table->data, false, true, false, true); -} + ecs_set_scope(world, old_scope); -/* Cleanup, run OnRemove, clear entity index, deactivate table */ -void flecs_table_clear_entities( - ecs_world_t *world, - ecs_table_t *table) -{ - flecs_table_fini_data(world, table, &table->data, true, true, false, true); + return 0; +error: + return -1; } -/* Cleanup, run OnRemove, delete from entity index, deactivate table */ -void flecs_table_delete_entities( +static +int meta_parse_enum( ecs_world_t *world, - ecs_table_t *table) + ecs_entity_t t, + const char *desc) { - flecs_table_fini_data(world, table, &table->data, true, true, true, true); + ecs_add(world, t, EcsEnum); + return meta_parse_constants(world, t, desc, false); } -/* Unset all components in table. This function is called before a table is - * deleted, and invokes all UnSet handlers, if any */ -void flecs_table_remove_actions( +static +int meta_parse_bitmask( ecs_world_t *world, - ecs_table_t *table) + ecs_entity_t t, + const char *desc) { - (void)world; - flecs_table_notify_on_remove(world, table, &table->data); + ecs_add(world, t, EcsBitmask); + return meta_parse_constants(world, t, desc, true); } -/* Free table resources. */ -void flecs_table_free( +int ecs_meta_from_desc( ecs_world_t *world, - ecs_table_t *table) + ecs_entity_t component, + ecs_type_kind_t kind, + const char *desc) { - bool is_root = table == &world->store.root; - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - ecs_assert(is_root || table->id != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(is_root || flecs_sparse_is_alive(&world->store.tables, table->id), - ECS_INTERNAL_ERROR, NULL); - (void)world; - - if (!is_root && !(world->flags & EcsWorldQuit)) { - if (table->flags & EcsTableHasOnTableDelete) { - flecs_emit(world, world, &(ecs_event_desc_t) { - .ids = &table->type, - .event = EcsOnTableDelete, - .table = table, - .flags = EcsEventTableOnly, - .observable = world - }); + switch(kind) { + case EcsStructType: + if (meta_parse_struct(world, component, desc)) { + goto error; } + break; + case EcsEnumType: + if (meta_parse_enum(world, component, desc)) { + goto error; + } + break; + case EcsBitmaskType: + if (meta_parse_bitmask(world, component, desc)) { + goto error; + } + break; + case EcsPrimitiveType: + case EcsArrayType: + case EcsVectorType: + case EcsOpaqueType: + break; + default: + ecs_throw(ECS_INTERNAL_ERROR, "invalid type kind"); } - if (ecs_should_log_2()) { - char *expr = ecs_type_str(world, &table->type); - ecs_dbg_2( - "#[green]table#[normal] [%s] #[red]deleted#[reset] with id %d", - expr, table->id); - ecs_os_free(expr); - ecs_log_push_2(); - } - - world->info.empty_table_count -= (ecs_table_count(table) == 0); - - /* Cleanup data, no OnRemove, delete from entity index, don't deactivate */ - flecs_table_fini_data(world, table, &table->data, false, true, true, false); - flecs_table_clear_edges(world, table); + return 0; +error: + return -1; +} - if (!is_root) { - ecs_type_t ids = { - .array = table->type.array, - .count = table->type.count - }; +#endif - flecs_hashmap_remove_w_hash( - &world->store.table_map, &ids, ecs_table_t*, table->_->hash); - } +/** + * @file addons/meta/cursor.c + * @brief API for assigning values of runtime types with reflection. + */ - flecs_wfree_n(world, int32_t, table->column_count + 1, table->dirty_state); - flecs_wfree_n(world, int32_t, table->column_count + table->type.count, - table->column_map); - flecs_table_records_unregister(world, table); +#include - /* Update counters */ - world->info.table_count --; - world->info.table_delete_total ++; +#ifdef FLECS_META +#ifdef FLECS_SCRIPT +#endif - flecs_free_t(&world->allocator, ecs_table__t, table->_); +static +const char* flecs_meta_op_kind_str( + ecs_meta_type_op_kind_t kind) +{ + switch(kind) { - if (!(world->flags & EcsWorldFini)) { - ecs_assert(!is_root, ECS_INTERNAL_ERROR, NULL); - flecs_table_free_type(world, table); - flecs_sparse_remove_t(&world->store.tables, ecs_table_t, table->id); + case EcsOpEnum: return "Enum"; + case EcsOpBitmask: return "Bitmask"; + case EcsOpArray: return "Array"; + case EcsOpVector: return "Vector"; + case EcsOpOpaque: return "Opaque"; + case EcsOpPush: return "Push"; + case EcsOpPop: return "Pop"; + case EcsOpPrimitive: return "Primitive"; + case EcsOpBool: return "Bool"; + case EcsOpChar: return "Char"; + case EcsOpByte: return "Byte"; + case EcsOpU8: return "U8"; + case EcsOpU16: return "U16"; + case EcsOpU32: return "U32"; + case EcsOpU64: return "U64"; + case EcsOpI8: return "I8"; + case EcsOpI16: return "I16"; + case EcsOpI32: return "I32"; + case EcsOpI64: return "I64"; + case EcsOpF32: return "F32"; + case EcsOpF64: return "F64"; + case EcsOpUPtr: return "UPtr"; + case EcsOpIPtr: return "IPtr"; + case EcsOpString: return "String"; + case EcsOpEntity: return "Entity"; + case EcsOpId: return "Id"; + case EcsOpScope: return "Scope"; + default: return "<< invalid kind >>"; } - - ecs_log_pop_2(); -} - -/* Free table type. Do this separately from freeing the table as types can be - * in use by application destructors. */ -void flecs_table_free_type( - ecs_world_t *world, - ecs_table_t *table) -{ - flecs_wfree_n(world, ecs_id_t, table->type.count, table->type.array); } -/* Reset a table to its initial state. */ -void flecs_table_reset( - ecs_world_t *world, - ecs_table_t *table) +/* Get current scope */ +static +ecs_meta_scope_t* flecs_meta_cursor_get_scope( + const ecs_meta_cursor_t *cursor) { - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - flecs_table_clear_edges(world, table); + ecs_check(cursor != NULL, ECS_INVALID_PARAMETER, NULL); + return ECS_CONST_CAST(ecs_meta_scope_t*, &cursor->scope[cursor->depth]); +error: + return NULL; } -/* Keep track of number of traversable entities in table. A traversable entity - * is an entity used as target in a pair with a traversable relationship. The - * traversable count and flag are used by code to early out of mechanisms like - * event propagation and recursive cleanup. */ -void flecs_table_traversable_add( - ecs_table_t *table, - int32_t value) +/* Restore scope, if dotmember was used */ +static +ecs_meta_scope_t* flecs_meta_cursor_restore_scope( + ecs_meta_cursor_t *cursor, + const ecs_meta_scope_t* scope) { - int32_t result = table->_->traversable_count += value; - ecs_assert(result >= 0, ECS_INTERNAL_ERROR, NULL); - if (result == 0) { - table->flags &= ~EcsTableHasTraversable; - } else if (result == value) { - table->flags |= EcsTableHasTraversable; + ecs_check(cursor != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(scope != NULL, ECS_INVALID_PARAMETER, NULL); + if (scope->prev_depth) { + cursor->depth = scope->prev_depth; } +error: + return (ecs_meta_scope_t*)&cursor->scope[cursor->depth]; } -/* Mark table column dirty. This usually happens as the result of a set - * operation, or iteration of a query with [out] fields. */ +/* Get current operation for scope */ static -void flecs_table_mark_table_dirty( - ecs_world_t *world, - ecs_table_t *table, - int32_t index) +ecs_meta_type_op_t* flecs_meta_cursor_get_op( + ecs_meta_scope_t *scope) { - (void)world; - if (table->dirty_state) { - table->dirty_state[index] ++; - } + ecs_assert(scope->ops != NULL, ECS_INVALID_OPERATION, + "type serializer is missing instructions"); + return &scope->ops[scope->op_cur]; } -/* Mark table component dirty */ -void flecs_table_mark_dirty( - ecs_world_t *world, - ecs_table_t *table, - ecs_entity_t component) +/* Get component for type in current scope */ +static +const EcsComponent* get_ecs_component( + const ecs_world_t *world, + ecs_meta_scope_t *scope) { - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - - if (table->dirty_state) { - ecs_id_record_t *idr = flecs_id_record_get(world, component); - if (!idr) { - return; - } - - const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (!tr || tr->column == -1) { - return; - } - - table->dirty_state[tr->column + 1] ++; + const EcsComponent *comp = scope->comp; + if (!comp) { + comp = scope->comp = ecs_get(world, scope->type, EcsComponent); + ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); } + return comp; } -/* Get (or create) dirty state of table. Used by queries for change tracking */ -int32_t* flecs_table_get_dirty_state( - ecs_world_t *world, - ecs_table_t *table) +/* Get size for type in current scope */ +static +ecs_size_t get_size( + const ecs_world_t *world, + ecs_meta_scope_t *scope) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - if (!table->dirty_state) { - int32_t column_count = table->column_count; - table->dirty_state = flecs_alloc_n(&world->allocator, - int32_t, column_count + 1); - ecs_assert(table->dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - for (int i = 0; i < column_count + 1; i ++) { - table->dirty_state[i] = 1; - } - } - return table->dirty_state; + return get_ecs_component(world, scope)->size; } -/* Table move logic for switch (union relationship) column */ static -void flecs_table_move_switch_columns( - ecs_table_t *dst_table, - int32_t dst_index, - ecs_table_t *src_table, - int32_t src_index, - int32_t count, - bool clear) +int32_t get_elem_count( + ecs_meta_scope_t *scope) { - ecs_table__t *dst_meta = dst_table->_; - ecs_table__t *src_meta = src_table->_; - if (!dst_meta && !src_meta) { - return; - } + const EcsOpaque *opaque = scope->opaque; - int32_t i_old = 0, src_column_count = src_meta ? src_meta->sw_count : 0; - int32_t i_new = 0, dst_column_count = dst_meta ? dst_meta->sw_count : 0; - if (!src_column_count && !dst_column_count) { - return; + if (scope->vector) { + return ecs_vec_count(scope->vector); + } else if (opaque && opaque->count) { + return flecs_uto(int32_t, opaque->count(scope[-1].ptr)); } - ecs_switch_t *src_columns = src_meta ? src_meta->sw_columns : NULL; - ecs_switch_t *dst_columns = dst_meta ? dst_meta->sw_columns : NULL; - - ecs_type_t dst_type = dst_table->type; - ecs_type_t src_type = src_table->type; - - int32_t offset_new = dst_meta ? dst_meta->sw_offset : 0; - int32_t offset_old = src_meta ? src_meta->sw_offset : 0; - - ecs_id_t *dst_ids = dst_type.array; - ecs_id_t *src_ids = src_type.array; - - for (; (i_new < dst_column_count) && (i_old < src_column_count);) { - ecs_entity_t dst_id = dst_ids[i_new + offset_new]; - ecs_entity_t src_id = src_ids[i_old + offset_old]; - - if (dst_id == src_id) { - ecs_switch_t *src_switch = &src_columns[i_old]; - ecs_switch_t *dst_switch = &dst_columns[i_new]; + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + return op->count; +} - flecs_switch_ensure(dst_switch, dst_index + count); +/* Get pointer to current field/element */ +static +ecs_meta_type_op_t* flecs_meta_cursor_get_ptr( + const ecs_world_t *world, + ecs_meta_scope_t *scope) +{ + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + ecs_size_t size = get_size(world, scope); + const EcsOpaque *opaque = scope->opaque; - int i; - for (i = 0; i < count; i ++) { - uint64_t value = flecs_switch_get(src_switch, src_index + i); - flecs_switch_set(dst_switch, dst_index + i, value); + if (scope->vector) { + ecs_vec_set_min_count(NULL, scope->vector, size, scope->elem_cur + 1); + scope->ptr = ecs_vec_first(scope->vector); + } else if (opaque) { + if (scope->is_collection) { + if (!opaque->ensure_element) { + char *str = ecs_get_path(world, scope->type); + ecs_err("missing ensure_element for opaque type %s", str); + ecs_os_free(str); + return NULL; } + scope->is_empty_scope = false; - if (clear) { - ecs_assert(count == flecs_switch_count(src_switch), - ECS_INTERNAL_ERROR, NULL); - flecs_switch_clear(src_switch); + void *opaque_ptr = opaque->ensure_element( + scope->ptr, flecs_ito(size_t, scope->elem_cur)); + ecs_assert(opaque_ptr != NULL, ECS_INVALID_OPERATION, + "ensure_element returned NULL"); + return opaque_ptr; + } else if (op->name) { + if (!opaque->ensure_member) { + char *str = ecs_get_path(world, scope->type); + ecs_err("missing ensure_member for opaque type %s", str); + ecs_os_free(str); + return NULL; } - } else if (dst_id > src_id) { - ecs_switch_t *src_switch = &src_columns[i_old]; - flecs_switch_clear(src_switch); + ecs_assert(scope->ptr != NULL, ECS_INTERNAL_ERROR, NULL); + return opaque->ensure_member(scope->ptr, op->name); + } else { + ecs_err("invalid operation for opaque type"); + return NULL; } - - i_new += dst_id <= src_id; - i_old += dst_id >= src_id; } - /* Clear remaining columns */ - if (clear) { - for (; (i_old < src_column_count); i_old ++) { - ecs_switch_t *src_switch = &src_columns[i_old]; - ecs_assert(count == flecs_switch_count(src_switch), - ECS_INTERNAL_ERROR, NULL); - flecs_switch_clear(src_switch); - } - } + return ECS_OFFSET(scope->ptr, size * scope->elem_cur + op->offset); } -/* Table move logic for bitset (toggle component) column */ static -void flecs_table_move_bitset_columns( - ecs_table_t *dst_table, - int32_t dst_index, - ecs_table_t *src_table, - int32_t src_index, - int32_t count, - bool clear) +int flecs_meta_cursor_push_type( + const ecs_world_t *world, + ecs_meta_scope_t *scope, + ecs_entity_t type, + void *ptr) { - ecs_table__t *dst_meta = dst_table->_; - ecs_table__t *src_meta = src_table->_; - if (!dst_meta && !src_meta) { - return; - } - - int32_t i_old = 0, src_column_count = src_meta ? src_meta->bs_count : 0; - int32_t i_new = 0, dst_column_count = dst_meta ? dst_meta->bs_count : 0; - - if (!src_column_count && !dst_column_count) { - return; + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); + if (ser == NULL) { + char *str = ecs_id_str(world, type); + ecs_err("cannot open scope for '%s' (missing reflection data)", str); + ecs_os_free(str); + return -1; } - ecs_bitset_t *src_columns = src_meta ? src_meta->bs_columns : NULL; - ecs_bitset_t *dst_columns = dst_meta ? dst_meta->bs_columns : NULL; - - ecs_type_t dst_type = dst_table->type; - ecs_type_t src_type = src_table->type; - - int32_t offset_new = dst_meta ? dst_meta->bs_offset : 0; - int32_t offset_old = src_meta ? src_meta->bs_offset : 0; - - ecs_id_t *dst_ids = dst_type.array; - ecs_id_t *src_ids = src_type.array; - - for (; (i_new < dst_column_count) && (i_old < src_column_count);) { - ecs_id_t dst_id = dst_ids[i_new + offset_new]; - ecs_id_t src_id = src_ids[i_old + offset_old]; - - if (dst_id == src_id) { - ecs_bitset_t *src_bs = &src_columns[i_old]; - ecs_bitset_t *dst_bs = &dst_columns[i_new]; - - flecs_bitset_ensure(dst_bs, dst_index + count); - - int i; - for (i = 0; i < count; i ++) { - uint64_t value = flecs_bitset_get(src_bs, src_index + i); - flecs_bitset_set(dst_bs, dst_index + i, value); - } - - if (clear) { - ecs_assert(count == flecs_bitset_count(src_bs), - ECS_INTERNAL_ERROR, NULL); - flecs_bitset_fini(src_bs); - } - } else if (dst_id > src_id) { - ecs_bitset_t *src_bs = &src_columns[i_old]; - flecs_bitset_fini(src_bs); - } - - i_new += dst_id <= src_id; - i_old += dst_id >= src_id; - } + scope[0] = (ecs_meta_scope_t) { + .type = type, + .ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t), + .op_count = ecs_vec_count(&ser->ops), + .ptr = ptr + }; - /* Clear remaining columns */ - if (clear) { - for (; (i_old < src_column_count); i_old ++) { - ecs_bitset_t *src_bs = &src_columns[i_old]; - ecs_assert(count == flecs_bitset_count(src_bs), - ECS_INTERNAL_ERROR, NULL); - flecs_bitset_fini(src_bs); - } - } + return 0; } -/* Grow table column. When a column needs to be reallocated this function takes - * care of correctly invoking ctor/move/dtor hooks. */ -static -void flecs_table_grow_column( - ecs_world_t *world, - ecs_column_t *column, - int32_t to_add, - int32_t dst_size, - bool construct) +ecs_meta_cursor_t ecs_meta_cursor( + const ecs_world_t *world, + ecs_entity_t type, + void *ptr) { - ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(type != 0, ECS_INVALID_PARAMETER, NULL); - ecs_type_info_t *ti = column->ti; - int32_t size = column->size; - int32_t count = column->data.count; - int32_t src_size = column->data.size; - int32_t dst_count = count + to_add; - bool can_realloc = dst_size != src_size; - void *result = NULL; + ecs_meta_cursor_t result = { + .world = world, + .valid = true + }; - ecs_assert(dst_size >= dst_count, ECS_INTERNAL_ERROR, NULL); + if (flecs_meta_cursor_push_type(world, result.scope, type, ptr) != 0) { + result.valid = false; + } - /* If the array could possibly realloc and the component has a move action - * defined, move old elements manually */ - ecs_move_t move_ctor; - if (count && can_realloc && (move_ctor = ti->hooks.ctor_move_dtor)) { - ecs_xtor_t ctor = ti->hooks.ctor; - ecs_assert(ctor != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(move_ctor != NULL, ECS_INTERNAL_ERROR, NULL); + return result; +error: + return (ecs_meta_cursor_t){ 0 }; +} - /* Create vector */ - ecs_vec_t dst; - ecs_vec_init(&world->allocator, &dst, size, dst_size); - dst.count = dst_count; +void* ecs_meta_get_ptr( + ecs_meta_cursor_t *cursor) +{ + return flecs_meta_cursor_get_ptr(cursor->world, + flecs_meta_cursor_get_scope(cursor)); +} - void *src_buffer = column->data.array; - void *dst_buffer = dst.array; +int ecs_meta_next( + ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + scope = flecs_meta_cursor_restore_scope(cursor, scope); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - /* Move (and construct) existing elements to new vector */ - move_ctor(dst_buffer, src_buffer, count, ti); + if (scope->is_collection) { + scope->elem_cur ++; + scope->op_cur = 0; - if (construct) { - /* Construct new element(s) */ - result = ECS_ELEM(dst_buffer, size, count); - ctor(result, to_add, ti); + if (scope->opaque) { + return 0; } - /* Free old vector */ - ecs_vec_fini(&world->allocator, &column->data, size); - - column->data = dst; - } else { - /* If array won't realloc or has no move, simply add new elements */ - if (can_realloc) { - ecs_vec_set_size(&world->allocator, &column->data, size, dst_size); + if (scope->elem_cur >= get_elem_count(scope)) { + ecs_err("out of collection bounds (%d)", scope->elem_cur); + return -1; } + return 0; + } - result = ecs_vec_grow(&world->allocator, &column->data, size, to_add); + scope->op_cur += op->op_count; - ecs_xtor_t ctor; - if (construct && (ctor = ti->hooks.ctor)) { - /* If new elements need to be constructed and component has a - * constructor, construct */ - ctor(result, to_add, ti); - } + if (scope->op_cur >= scope->op_count) { + ecs_err("out of bounds"); + return -1; } - ecs_assert(column->data.size == dst_size, ECS_INTERNAL_ERROR, NULL); + return 0; } -/* Grow all data structures in a table */ -static -int32_t flecs_table_grow_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data, - int32_t to_add, - int32_t size, - const ecs_entity_t *ids) +int ecs_meta_elem( + ecs_meta_cursor_t *cursor, + int32_t elem) { - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(data != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + if (!scope->is_collection) { + ecs_err("ecs_meta_elem can be used for collections only"); + return -1; + } - int32_t cur_count = flecs_table_data_count(data); - int32_t column_count = table->column_count; + scope->elem_cur = elem; + scope->op_cur = 0; - /* Add entity to column with entity ids */ - ecs_vec_set_size_t(&world->allocator, &data->entities, ecs_entity_t, size); - ecs_entity_t *e = ecs_vec_last_t(&data->entities, ecs_entity_t) + 1; - data->entities.count += to_add; - if (data->entities.size > size) { - size = data->entities.size; + if (scope->elem_cur >= get_elem_count(scope) || (scope->elem_cur < 0)) { + ecs_err("out of collection bounds (%d)", scope->elem_cur); + return -1; } + + return 0; +} - /* Initialize entity ids and record ptrs */ - int32_t i; - if (ids) { - ecs_os_memcpy_n(e, ids, ecs_entity_t, to_add); - } else { - ecs_os_memset(e, 0, ECS_SIZEOF(ecs_entity_t) * to_add); +int ecs_meta_member( + ecs_meta_cursor_t *cursor, + const char *name) +{ + if (cursor->depth == 0) { + ecs_err("cannot move to member in root scope"); + return -1; } - /* Add elements to each column array */ - ecs_column_t *columns = data->columns; - for (i = 0; i < column_count; i ++) { - flecs_table_grow_column(world, &columns[i], to_add, size, true); - ecs_assert(columns[i].data.size == size, ECS_INTERNAL_ERROR, NULL); - flecs_table_invoke_add_hooks(world, table, &columns[i], e, - cur_count, to_add, false); - } + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + scope = flecs_meta_cursor_restore_scope(cursor, scope); - ecs_table__t *meta = table->_; - int32_t sw_count = meta->sw_count; - int32_t bs_count = meta->bs_count; - ecs_switch_t *sw_columns = meta->sw_columns; - ecs_bitset_t *bs_columns = meta->bs_columns; + ecs_hashmap_t *members = scope->members; + const ecs_world_t *world = cursor->world; - /* Add elements to each switch column */ - for (i = 0; i < sw_count; i ++) { - ecs_switch_t *sw = &sw_columns[i]; - flecs_switch_addn(sw, to_add); + if (!members) { + ecs_err("cannot move to member '%s' for non-struct type", name); + return -1; } - /* Add elements to each bitset column */ - for (i = 0; i < bs_count; i ++) { - ecs_bitset_t *bs = &bs_columns[i]; - flecs_bitset_addn(bs, to_add); + const uint64_t *cur_ptr = flecs_name_index_find_ptr(members, name, 0, 0); + if (!cur_ptr) { + char *path = ecs_get_path(world, scope->type); + ecs_err("unknown member '%s' for type '%s'", name, path); + ecs_os_free(path); + return -1; } - /* If the table is monitored indicate that there has been a change */ - flecs_table_mark_table_dirty(world, table, 0); + scope->op_cur = flecs_uto(int32_t, cur_ptr[0]); - if (!(world->flags & EcsWorldReadonly) && !cur_count) { - flecs_table_set_empty(world, table); + const EcsOpaque *opaque = scope->opaque; + if (opaque) { + if (!opaque->ensure_member) { + char *str = ecs_get_path(world, scope->type); + ecs_err("missing ensure_member for opaque type %s", str); + ecs_os_free(str); + } } - /* Return index of first added entity */ - return cur_count; + return 0; } -/* Append operation for tables that don't have any complex logic */ static -void flecs_table_fast_append( - ecs_world_t *world, - ecs_column_t *columns, - int32_t count) +const char* flecs_meta_parse_member( + const char *start, + char *token_out) { - /* Add elements to each column array */ - int32_t i; - for (i = 0; i < count; i ++) { - ecs_column_t *column = &columns[i]; - ecs_vec_append(&world->allocator, &column->data, column->size); + const char *ptr; + char ch; + for (ptr = start; (ch = *ptr); ptr ++) { + if (ch == '.') { + break; + } } -} - -/* Append entity to table */ -int32_t flecs_table_append( - ecs_world_t *world, - ecs_table_t *table, - ecs_entity_t entity, - bool construct, - bool on_add) -{ - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!(table->flags & EcsTableHasTarget), - ECS_INVALID_OPERATION, NULL); - - flecs_table_check_sanity(table); - - /* Get count & size before growing entities array. This tells us whether the - * arrays will realloc */ - ecs_data_t *data = &table->data; - int32_t count = data->entities.count; - int32_t column_count = table->column_count; - ecs_column_t *columns = table->data.columns; - - /* Grow buffer with entity ids, set new element to new entity */ - ecs_entity_t *e = ecs_vec_append_t(&world->allocator, - &data->entities, ecs_entity_t); - ecs_assert(e != NULL, ECS_INTERNAL_ERROR, NULL); - *e = entity; - - /* If the table is monitored indicate that there has been a change */ - flecs_table_mark_table_dirty(world, table, 0); - ecs_assert(count >= 0, ECS_INTERNAL_ERROR, NULL); - /* Fast path: no switch columns, no lifecycle actions */ - if (!(table->flags & EcsTableIsComplex)) { - flecs_table_fast_append(world, columns, column_count); - if (!count) { - flecs_table_set_empty(world, table); /* See below */ - } - return count; + int32_t len = flecs_ito(int32_t, ptr - start); + ecs_os_memcpy(token_out, start, len); + token_out[len] = '\0'; + if (ch == '.') { + ptr ++; } - ecs_entity_t *entities = data->entities.array; + return ptr; +} - /* Reobtain size to ensure that the columns have the same size as the - * entities and record vectors. This keeps reasoning about when allocations - * occur easier. */ - int32_t size = data->entities.size; +int ecs_meta_dotmember( + ecs_meta_cursor_t *cursor, + const char *name) +{ + ecs_meta_scope_t *cur_scope = flecs_meta_cursor_get_scope(cursor); + flecs_meta_cursor_restore_scope(cursor, cur_scope); - /* Grow component arrays with 1 element */ - int32_t i; - for (i = 0; i < column_count; i ++) { - ecs_column_t *column = &columns[i]; - flecs_table_grow_column(world, column, 1, size, construct); + int32_t prev_depth = cursor->depth; + int dotcount = 0; - ecs_iter_action_t on_add_hook; - if (on_add && (on_add_hook = column->ti->hooks.on_add)) { - flecs_table_invoke_hook(world, table, on_add_hook, EcsOnAdd, column, - &entities[count], count, 1); + char token[ECS_MAX_TOKEN_SIZE]; + const char *ptr = name; + while ((ptr = flecs_meta_parse_member(ptr, token))) { + if (dotcount) { + ecs_meta_push(cursor); } - ecs_assert(columns[i].data.size == - data->entities.size, ECS_INTERNAL_ERROR, NULL); - ecs_assert(columns[i].data.count == - data->entities.count, ECS_INTERNAL_ERROR, NULL); - } - - ecs_table__t *meta = table->_; - int32_t sw_count = meta->sw_count; - int32_t bs_count = meta->bs_count; - ecs_switch_t *sw_columns = meta->sw_columns; - ecs_bitset_t *bs_columns = meta->bs_columns; + if (ecs_meta_member(cursor, token)) { + goto error; + } - /* Add element to each switch column */ - for (i = 0; i < sw_count; i ++) { - ecs_assert(sw_columns != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_switch_t *sw = &sw_columns[i]; - flecs_switch_add(sw); - } + if (!ptr[0]) { + break; + } - /* Add element to each bitset column */ - for (i = 0; i < bs_count; i ++) { - ecs_assert(bs_columns != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_bitset_t *bs = &bs_columns[i]; - flecs_bitset_addn(bs, 1); + dotcount ++; } - /* If this is the first entity in this table, signal queries so that the - * table moves from an inactive table to an active table. */ - if (!count) { - flecs_table_set_empty(world, table); + cur_scope = flecs_meta_cursor_get_scope(cursor); + if (dotcount) { + cur_scope->prev_depth = prev_depth; } - flecs_table_check_sanity(table); - - return count; + return 0; +error: + return -1; } -/* Delete last operation for tables that don't have any complex logic */ -static -void flecs_table_fast_delete_last( - ecs_column_t *columns, - int32_t column_count) +int ecs_meta_push( + ecs_meta_cursor_t *cursor) { - int i; - for (i = 0; i < column_count; i ++) { - ecs_vec_remove_last(&columns[i].data); - } -} + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + const ecs_world_t *world = cursor->world; -/* Delete operation for tables that don't have any complex logic */ -static -void flecs_table_fast_delete( - ecs_column_t *columns, - int32_t column_count, - int32_t index) -{ - int i; - for (i = 0; i < column_count; i ++) { - ecs_column_t *column = &columns[i]; - ecs_vec_remove(&column->data, column->size, index); + if (cursor->depth == 0) { + if (!cursor->is_primitive_scope) { + if ((op->kind > EcsOpScope) && (op->count <= 1)) { + cursor->is_primitive_scope = true; + return 0; + } + } } -} -/* Delete entity from table */ -void flecs_table_delete( - ecs_world_t *world, - ecs_table_t *table, - int32_t index, - bool destruct) -{ - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!(table->flags & EcsTableHasTarget), - ECS_INVALID_OPERATION, NULL); + void *ptr = flecs_meta_cursor_get_ptr(world, scope); + cursor->depth ++; + ecs_check(cursor->depth < ECS_META_MAX_SCOPE_DEPTH, + ECS_INVALID_PARAMETER, NULL); - flecs_table_check_sanity(table); + ecs_meta_scope_t *next_scope = flecs_meta_cursor_get_scope(cursor); - ecs_data_t *data = &table->data; - int32_t count = data->entities.count; + /* If we're not already in an inline array and this operation is an inline + * array, push a frame for the array. + * Doing this first ensures that inline arrays take precedence over other + * kinds of push operations, such as for a struct element type. */ + if (!scope->is_inline_array && op->count > 1 && !scope->is_collection) { + /* Push a frame just for the element type, with inline_array = true */ + next_scope[0] = (ecs_meta_scope_t){ + .ops = op, + .op_count = op->op_count, + .ptr = scope->ptr, + .type = op->type, + .is_collection = true, + .is_inline_array = true + }; - ecs_assert(count > 0, ECS_INTERNAL_ERROR, NULL); - count --; - ecs_assert(index <= count, ECS_INTERNAL_ERROR, NULL); + /* With 'is_inline_array' set to true we ensure that we can never push + * the same inline array twice */ + return 0; + } - /* Move last entity id to index */ - ecs_entity_t *entities = data->entities.array; - ecs_entity_t entity_to_move = entities[count]; - ecs_entity_t entity_to_delete = entities[index]; - entities[index] = entity_to_move; - ecs_vec_remove_last(&data->entities); + /* Operation-specific switch behavior */ + switch(op->kind) { - /* Update record of moved entity in entity index */ - if (index != count) { - ecs_record_t *record_to_move = flecs_entities_get(world, entity_to_move); - if (record_to_move) { - uint32_t row_flags = record_to_move->row & ECS_ROW_FLAGS_MASK; - record_to_move->row = ECS_ROW_TO_RECORD(index, row_flags); - ecs_assert(record_to_move->table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(record_to_move->table == table, ECS_INTERNAL_ERROR, NULL); - } - } + /* Struct push: this happens when pushing a struct member. */ + case EcsOpPush: { + const EcsOpaque *opaque = scope->opaque; + if (opaque) { + /* If this is a nested push for an opaque type, push the type of the + * element instead of the next operation. This ensures that we won't + * use flattened offsets for nested members. */ + if (flecs_meta_cursor_push_type( + world, next_scope, op->type, ptr) != 0) + { + goto error; + } - /* If the table is monitored indicate that there has been a change */ - flecs_table_mark_table_dirty(world, table, 0); + /* Strip the Push operation since we already pushed */ + next_scope->members = next_scope->ops[0].members; + next_scope->ops = &next_scope->ops[1]; + next_scope->op_count --; + break; + } - /* If table is empty, deactivate it */ - if (!count) { - flecs_table_set_empty(world, table); + /* The ops array contains a flattened list for all members and nested + * members of a struct, so we can use (ops + 1) to initialize the ops + * array of the next scope. */ + next_scope[0] = (ecs_meta_scope_t) { + .ops = &op[1], /* op after push */ + .op_count = op->op_count - 1, /* don't include pop */ + .ptr = scope->ptr, + .type = op->type, + .members = op->members + }; + break; } - /* Destruct component data */ - ecs_column_t *columns = data->columns; - int32_t column_count = table->column_count; - int32_t i; - - /* If this is a table without lifecycle callbacks or special columns, take - * fast path that just remove an element from the array(s) */ - if (!(table->flags & EcsTableIsComplex)) { - if (index == count) { - flecs_table_fast_delete_last(columns, column_count); - } else { - flecs_table_fast_delete(columns, column_count, index); + /* Array push for an array type. Arrays can be encoded in 2 ways: either by + * setting the EcsMember::count member to a value >1, or by specifying an + * array type as member type. This is the latter case. */ + case EcsOpArray: { + if (flecs_meta_cursor_push_type(world, next_scope, op->type, ptr) != 0) { + goto error; } - flecs_table_check_sanity(table); - return; + const EcsArray *type_ptr = ecs_get(world, op->type, EcsArray); + next_scope->type = type_ptr->type; + next_scope->is_collection = true; + break; } - /* Last element, destruct & remove */ - if (index == count) { - /* If table has component destructors, invoke */ - if (destruct && (table->flags & EcsTableHasDtors)) { - for (i = 0; i < column_count; i ++) { - flecs_table_invoke_remove_hooks(world, table, &columns[i], - &entity_to_delete, index, 1, true); - } + /* Vector push */ + case EcsOpVector: { + next_scope->vector = ptr; + if (flecs_meta_cursor_push_type(world, next_scope, op->type, NULL) != 0) { + goto error; } - flecs_table_fast_delete_last(columns, column_count); + const EcsVector *type_ptr = ecs_get(world, op->type, EcsVector); + next_scope->type = type_ptr->type; + next_scope->is_collection = true; + break; + } - /* Not last element, move last element to deleted element & destruct */ - } else { - /* If table has component destructors, invoke */ - if ((table->flags & (EcsTableHasDtors | EcsTableHasMove))) { - for (i = 0; i < column_count; i ++) { - ecs_column_t *column = &columns[i]; - ecs_type_info_t *ti = column->ti; - ecs_size_t size = column->size; - void *dst = ecs_vec_get(&column->data, size, index); - void *src = ecs_vec_last(&column->data, size); + /* Opaque type push. Depending on the type the opaque type represents the + * scope will be pushed as a struct or collection type. The type information + * of the as_type is retained, as this is important for type checking and + * for nested opaque type support. */ + case EcsOpOpaque: { + const EcsOpaque *type_ptr = ecs_get(world, op->type, EcsOpaque); + ecs_entity_t as_type = type_ptr->as_type; + const EcsType *mtype_ptr = ecs_get(world, as_type, EcsType); - ecs_iter_action_t on_remove = ti->hooks.on_remove; - if (destruct && on_remove) { - flecs_table_invoke_hook(world, table, on_remove, EcsOnRemove, - column, &entity_to_delete, index, 1); - } + /* Check what kind of type the opaque type represents */ + switch(mtype_ptr->kind) { - ecs_move_t move_dtor = ti->hooks.move_dtor; - if (move_dtor) { - move_dtor(dst, src, 1, ti); - } else { - ecs_os_memcpy(dst, src, size); - } + /* Opaque vector support */ + case EcsVectorType: { + const EcsVector *vt = ecs_get(world, type_ptr->as_type, EcsVector); + next_scope->type = vt->type; - ecs_vec_remove_last(&column->data); + /* Push the element type of the vector type */ + if (flecs_meta_cursor_push_type( + world, next_scope, vt->type, NULL) != 0) + { + goto error; } - } else { - flecs_table_fast_delete(columns, column_count, index); + + /* This tracks whether any data was assigned inside the scope. When + * the scope is popped, and is_empty_scope is still true, the vector + * will be resized to 0. */ + next_scope->is_empty_scope = true; + next_scope->is_collection = true; + break; } - } - /* Remove elements from switch columns */ - ecs_table__t *meta = table->_; - ecs_switch_t *sw_columns = meta->sw_columns; - int32_t sw_count = meta->sw_count; - for (i = 0; i < sw_count; i ++) { - flecs_switch_remove(&sw_columns[i], index); - } + /* Opaque array support */ + case EcsArrayType: { + const EcsArray *at = ecs_get(world, type_ptr->as_type, EcsArray); + next_scope->type = at->type; - /* Remove elements from bitset columns */ - ecs_bitset_t *bs_columns = meta->bs_columns; - int32_t bs_count = meta->bs_count; - for (i = 0; i < bs_count; i ++) { - flecs_bitset_remove(&bs_columns[i], index); - } + /* Push the element type of the array type */ + if (flecs_meta_cursor_push_type( + world, next_scope, at->type, NULL) != 0) + { + goto error; + } - flecs_table_check_sanity(table); -} + /* Arrays are always a fixed size */ + next_scope->is_empty_scope = false; + next_scope->is_collection = true; + break; + } -/* Move operation for tables that don't have any complex logic */ -static -void flecs_table_fast_move( - ecs_table_t *dst_table, - int32_t dst_index, - ecs_table_t *src_table, - int32_t src_index) -{ - int32_t i_new = 0, dst_column_count = dst_table->column_count; - int32_t i_old = 0, src_column_count = src_table->column_count; + /* Opaque struct support */ + case EcsStructType: + /* Push struct type that represents the opaque type. This ensures + * that the deserializer retains information about members and + * member types, which is necessary for nested opaque types, and + * allows for error checking. */ + if (flecs_meta_cursor_push_type( + world, next_scope, as_type, NULL) != 0) + { + goto error; + } - ecs_column_t *src_columns = src_table->data.columns; - ecs_column_t *dst_columns = dst_table->data.columns; + /* Strip push op, since we already pushed */ + next_scope->members = next_scope->ops[0].members; + next_scope->ops = &next_scope->ops[1]; + next_scope->op_count --; + break; + + case EcsPrimitiveType: + case EcsEnumType: + case EcsBitmaskType: + case EcsOpaqueType: + default: + break; + } - for (; (i_new < dst_column_count) && (i_old < src_column_count);) { - ecs_column_t *dst_column = &dst_columns[i_new]; - ecs_column_t *src_column = &src_columns[i_old]; - ecs_id_t dst_id = dst_column->id; - ecs_id_t src_id = src_column->id; + next_scope->ptr = ptr; + next_scope->opaque = type_ptr; + break; + } - if (dst_id == src_id) { - int32_t size = dst_column->size; - void *dst = ecs_vec_get(&dst_column->data, size, dst_index); - void *src = ecs_vec_get(&src_column->data, size, src_index); - ecs_os_memcpy(dst, src, size); - } + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + case EcsOpEntity: + case EcsOpId: { + char *path = ecs_get_path(world, scope->type); + ecs_err("invalid push for type '%s'", path); + ecs_os_free(path); + goto error; + } + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + } - i_new += dst_id <= src_id; - i_old += dst_id >= src_id; + if (scope->is_collection && !scope->opaque) { + next_scope->ptr = ECS_OFFSET(next_scope->ptr, + scope->elem_cur * get_size(world, scope)); } + + return 0; +error: + return -1; } -/* Move entity from src to dst table */ -void flecs_table_move( - ecs_world_t *world, - ecs_entity_t dst_entity, - ecs_entity_t src_entity, - ecs_table_t *dst_table, - int32_t dst_index, - ecs_table_t *src_table, - int32_t src_index, - bool construct) +int ecs_meta_pop( + ecs_meta_cursor_t *cursor) { - ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!dst_table->_->lock, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!src_table->_->lock, ECS_LOCKED_STORAGE, NULL); - - ecs_assert(src_index >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(dst_index >= 0, ECS_INTERNAL_ERROR, NULL); - - flecs_table_check_sanity(dst_table); - flecs_table_check_sanity(src_table); - - if (!((dst_table->flags | src_table->flags) & EcsTableIsComplex)) { - flecs_table_fast_move(dst_table, dst_index, src_table, src_index); - flecs_table_check_sanity(dst_table); - flecs_table_check_sanity(src_table); - return; + if (cursor->is_primitive_scope) { + cursor->is_primitive_scope = false; + return 0; } - flecs_table_move_switch_columns(dst_table, dst_index, src_table, src_index, 1, false); - flecs_table_move_bitset_columns(dst_table, dst_index, src_table, src_index, 1, false); - - /* If the source and destination entities are the same, move component - * between tables. If the entities are not the same (like when cloning) use - * a copy. */ - bool same_entity = dst_entity == src_entity; - - /* Call move_dtor for moved away from storage only if the entity is at the - * last index in the source table. If it isn't the last entity, the last - * entity in the table will be moved to the src storage, which will take - * care of cleaning up resources. */ - bool use_move_dtor = ecs_table_count(src_table) == (src_index + 1); - - int32_t i_new = 0, dst_column_count = dst_table->column_count; - int32_t i_old = 0, src_column_count = src_table->column_count; - - ecs_column_t *src_columns = src_table->data.columns; - ecs_column_t *dst_columns = dst_table->data.columns; - - for (; (i_new < dst_column_count) && (i_old < src_column_count); ) { - ecs_column_t *dst_column = &dst_columns[i_new]; - ecs_column_t *src_column = &src_columns[i_old]; - ecs_id_t dst_id = dst_column->id; - ecs_id_t src_id = src_column->id; + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + scope = flecs_meta_cursor_restore_scope(cursor, scope); + cursor->depth --; + if (cursor->depth < 0) { + ecs_err("unexpected end of scope"); + return -1; + } - if (dst_id == src_id) { - int32_t size = dst_column->size; + ecs_meta_scope_t *next_scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(next_scope); - ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); - void *dst = ecs_vec_get(&dst_column->data, size, dst_index); - void *src = ecs_vec_get(&src_column->data, size, src_index); - ecs_type_info_t *ti = dst_column->ti; + if (!scope->is_inline_array) { + if (op->kind == EcsOpPush) { + next_scope->op_cur += op->op_count - 1; - if (same_entity) { - ecs_move_t move = ti->hooks.move_ctor; - if (use_move_dtor || !move) { - /* Also use move_dtor if component doesn't have a move_ctor - * registered, to ensure that the dtor gets called to - * cleanup resources. */ - move = ti->hooks.ctor_move_dtor; - } + /* push + op_count should point to the operation after pop */ + op = flecs_meta_cursor_get_op(next_scope); + ecs_assert(op->kind == EcsOpPop, ECS_INTERNAL_ERROR, NULL); + } else if (op->kind == EcsOpArray || op->kind == EcsOpVector) { + /* Collection type, nothing else to do */ + } else if (op->kind == EcsOpOpaque) { + const EcsOpaque *opaque = scope->opaque; + if (scope->is_collection) { + const EcsType *mtype = ecs_get(cursor->world, + opaque->as_type, EcsType); + ecs_assert(mtype != NULL, ECS_INTERNAL_ERROR, NULL); - if (move) { - move(dst, src, 1, ti); - } else { - ecs_os_memcpy(dst, src, size); + /* When popping an opaque collection type, call resize to make + * sure the vector isn't larger than the number of elements we + * deserialized. + * If the opaque type represents an array, don't call resize. */ + if (mtype->kind != EcsArrayType) { + ecs_assert(opaque != NULL, ECS_INTERNAL_ERROR, NULL); + if (!opaque->resize) { + char *str = ecs_get_path(cursor->world, scope->type); + ecs_err("missing resize for opaque type %s", str); + ecs_os_free(str); + return -1; + } + if (scope->is_empty_scope) { + /* If no values were serialized for scope, resize + * collection to 0 elements. */ + ecs_assert(!scope->elem_cur, ECS_INTERNAL_ERROR, NULL); + opaque->resize(scope->ptr, 0); + } else { + /* Otherwise resize collection to the index of the last + * deserialized element + 1 */ + opaque->resize(scope->ptr, + flecs_ito(size_t, scope->elem_cur + 1)); + } } } else { - ecs_copy_t copy = ti->hooks.copy_ctor; - if (copy) { - copy(dst, src, 1, ti); - } else { - ecs_os_memcpy(dst, src, size); - } + /* Opaque struct type, nothing to be done */ } } else { - if (dst_id < src_id) { - flecs_table_invoke_add_hooks(world, dst_table, - dst_column, &dst_entity, dst_index, 1, construct); - } else { - flecs_table_invoke_remove_hooks(world, src_table, - src_column, &src_entity, src_index, 1, use_move_dtor); - } + /* should not have been able to push if the previous scope was not + * a complex or collection type */ + ecs_assert(false, ECS_INTERNAL_ERROR, NULL); } - - i_new += dst_id <= src_id; - i_old += dst_id >= src_id; } - for (; (i_new < dst_column_count); i_new ++) { - flecs_table_invoke_add_hooks(world, dst_table, &dst_columns[i_new], - &dst_entity, dst_index, 1, construct); - } + return 0; +} - for (; (i_old < src_column_count); i_old ++) { - flecs_table_invoke_remove_hooks(world, src_table, &src_columns[i_old], - &src_entity, src_index, 1, use_move_dtor); - } +bool ecs_meta_is_collection( + const ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + return scope->is_collection; +} - flecs_table_check_sanity(dst_table); - flecs_table_check_sanity(src_table); +ecs_entity_t ecs_meta_get_type( + const ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + return op->type; } -/* Append n entities to table */ -int32_t flecs_table_appendn( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data, - int32_t to_add, - const ecs_entity_t *ids) +ecs_entity_t ecs_meta_get_unit( + const ecs_meta_cursor_t *cursor) { - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_entity_t type = scope->type; + const EcsStruct *st = ecs_get(cursor->world, type, EcsStruct); + if (!st) { + return 0; + } - flecs_table_check_sanity(table); - int32_t cur_count = flecs_table_data_count(data); - int32_t result = flecs_table_grow_data( - world, table, data, to_add, cur_count + to_add, ids); - flecs_table_check_sanity(table); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + ecs_member_t *m = ecs_vec_get_t( + &st->members, ecs_member_t, op->member_index); - return result; + return m->unit; } -/* Set allocated table size */ -void flecs_table_set_size( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data, - int32_t size) +const char* ecs_meta_get_member( + const ecs_meta_cursor_t *cursor) { - ecs_assert(table != NULL, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + return op->name; +} - flecs_table_check_sanity(table); +ecs_entity_t ecs_meta_get_member_id( + const ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_entity_t type = scope->type; + const EcsStruct *st = ecs_get(cursor->world, type, EcsStruct); + if (!st) { + return 0; + } - int32_t cur_count = flecs_table_data_count(data); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + ecs_member_t *m = ecs_vec_get_t( + &st->members, ecs_member_t, op->member_index); - if (cur_count < size) { - flecs_table_grow_data(world, table, data, 0, size, NULL); - flecs_table_check_sanity(table); - } + return m->member; } -/* Shrink table storage to fit number of entities */ -bool flecs_table_shrink( - ecs_world_t *world, - ecs_table_t *table) -{ - ecs_assert(table != NULL, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - (void)world; +/* Utilities for type conversions and bounds checking */ +static struct { + int64_t min, max; +} ecs_meta_bounds_signed[EcsMetaTypeOpKindLast + 1] = { + [EcsOpBool] = {false, true}, + [EcsOpChar] = {INT8_MIN, INT8_MAX}, + [EcsOpByte] = {0, UINT8_MAX}, + [EcsOpU8] = {0, UINT8_MAX}, + [EcsOpU16] = {0, UINT16_MAX}, + [EcsOpU32] = {0, UINT32_MAX}, + [EcsOpU64] = {0, INT64_MAX}, + [EcsOpI8] = {INT8_MIN, INT8_MAX}, + [EcsOpI16] = {INT16_MIN, INT16_MAX}, + [EcsOpI32] = {INT32_MIN, INT32_MAX}, + [EcsOpI64] = {INT64_MIN, INT64_MAX}, + [EcsOpUPtr] = {0, ((sizeof(void*) == 4) ? UINT32_MAX : INT64_MAX)}, + [EcsOpIPtr] = { + ((sizeof(void*) == 4) ? INT32_MIN : INT64_MIN), + ((sizeof(void*) == 4) ? INT32_MAX : INT64_MAX) + }, + [EcsOpEntity] = {0, INT64_MAX}, + [EcsOpId] = {0, INT64_MAX}, + [EcsOpEnum] = {INT32_MIN, INT32_MAX}, + [EcsOpBitmask] = {0, INT32_MAX} +}; + +static struct { + uint64_t min, max; +} ecs_meta_bounds_unsigned[EcsMetaTypeOpKindLast + 1] = { + [EcsOpBool] = {false, true}, + [EcsOpChar] = {0, INT8_MAX}, + [EcsOpByte] = {0, UINT8_MAX}, + [EcsOpU8] = {0, UINT8_MAX}, + [EcsOpU16] = {0, UINT16_MAX}, + [EcsOpU32] = {0, UINT32_MAX}, + [EcsOpU64] = {0, UINT64_MAX}, + [EcsOpI8] = {0, INT8_MAX}, + [EcsOpI16] = {0, INT16_MAX}, + [EcsOpI32] = {0, INT32_MAX}, + [EcsOpI64] = {0, INT64_MAX}, + [EcsOpUPtr] = {0, ((sizeof(void*) == 4) ? UINT32_MAX : UINT64_MAX)}, + [EcsOpIPtr] = {0, ((sizeof(void*) == 4) ? INT32_MAX : INT64_MAX)}, + [EcsOpEntity] = {0, UINT64_MAX}, + [EcsOpId] = {0, UINT64_MAX}, + [EcsOpEnum] = {0, INT32_MAX}, + [EcsOpBitmask] = {0, UINT32_MAX} +}; - flecs_table_check_sanity(table); +static struct { + double min, max; +} ecs_meta_bounds_float[EcsMetaTypeOpKindLast + 1] = { + [EcsOpBool] = {false, true}, + [EcsOpChar] = {INT8_MIN, INT8_MAX}, + [EcsOpByte] = {0, UINT8_MAX}, + [EcsOpU8] = {0, UINT8_MAX}, + [EcsOpU16] = {0, UINT16_MAX}, + [EcsOpU32] = {0, UINT32_MAX}, + [EcsOpU64] = {0, (double)UINT64_MAX}, + [EcsOpI8] = {INT8_MIN, INT8_MAX}, + [EcsOpI16] = {INT16_MIN, INT16_MAX}, + [EcsOpI32] = {INT32_MIN, INT32_MAX}, + [EcsOpI64] = {INT64_MIN, (double)INT64_MAX}, + [EcsOpUPtr] = {0, ((sizeof(void*) == 4) ? UINT32_MAX : (double)UINT64_MAX)}, + [EcsOpIPtr] = { + ((sizeof(void*) == 4) ? INT32_MIN : (double)INT64_MIN), + ((sizeof(void*) == 4) ? INT32_MAX : (double)INT64_MAX) + }, + [EcsOpEntity] = {0, (double)UINT64_MAX}, + [EcsOpId] = {0, (double)UINT64_MAX}, + [EcsOpEnum] = {INT32_MIN, INT32_MAX}, + [EcsOpBitmask] = {0, UINT32_MAX} +}; - ecs_data_t *data = &table->data; - bool has_payload = data->entities.array != NULL; - ecs_vec_reclaim_t(&world->allocator, &data->entities, ecs_entity_t); +#define set_T(T, ptr, value)\ + ((T*)ptr)[0] = ((T)value) - int32_t i, count = table->column_count; - for (i = 0; i < count; i ++) { - ecs_column_t *column = &data->columns[i]; - ecs_vec_reclaim(&world->allocator, &column->data, column->size); - } +#define case_T(kind, T, dst, src)\ +case kind:\ + set_T(T, dst, src);\ + break - return has_payload; -} +#define case_T_checked(kind, T, dst, src, bounds)\ +case kind:\ + if ((src < bounds[kind].min) || (src > bounds[kind].max)){\ + ecs_err("value %.0f is out of bounds for type %s", (double)src,\ + flecs_meta_op_kind_str(kind));\ + return -1;\ + }\ + set_T(T, dst, src);\ + break -/* Return number of entities in table */ -int32_t flecs_table_data_count( - const ecs_data_t *data) -{ - return data ? data->entities.count : 0; -} +#define cases_T_float(dst, src)\ + case_T(EcsOpF32, ecs_f32_t, dst, src);\ + case_T(EcsOpF64, ecs_f64_t, dst, src) -/* Swap operation for switch (union relationship) columns */ -static -void flecs_table_swap_switch_columns( - ecs_table_t *table, - int32_t row_1, - int32_t row_2) -{ - int32_t i = 0, column_count = table->_->sw_count; - if (!column_count) { - return; - } +#define cases_T_signed(dst, src, bounds)\ + case_T_checked(EcsOpChar, ecs_char_t, dst, src, bounds);\ + case_T_checked(EcsOpI8, ecs_i8_t, dst, src, bounds);\ + case_T_checked(EcsOpI16, ecs_i16_t, dst, src, bounds);\ + case_T_checked(EcsOpI32, ecs_i32_t, dst, src, bounds);\ + case_T_checked(EcsOpI64, ecs_i64_t, dst, src, bounds);\ + case_T_checked(EcsOpIPtr, ecs_iptr_t, dst, src, bounds);\ + case_T_checked(EcsOpEnum, ecs_i32_t, dst, src, bounds) - ecs_switch_t *columns = table->_->sw_columns; +#define cases_T_unsigned(dst, src, bounds)\ + case_T_checked(EcsOpByte, ecs_byte_t, dst, src, bounds);\ + case_T_checked(EcsOpU8, ecs_u8_t, dst, src, bounds);\ + case_T_checked(EcsOpU16, ecs_u16_t, dst, src, bounds);\ + case_T_checked(EcsOpU32, ecs_u32_t, dst, src, bounds);\ + case_T_checked(EcsOpU64, ecs_u64_t, dst, src, bounds);\ + case_T_checked(EcsOpUPtr, ecs_uptr_t, dst, src, bounds);\ + case_T_checked(EcsOpEntity, ecs_u64_t, dst, src, bounds);\ + case_T_checked(EcsOpId, ecs_u64_t, dst, src, bounds);\ + case_T_checked(EcsOpBitmask, ecs_u32_t, dst, src, bounds) - for (i = 0; i < column_count; i ++) { - ecs_switch_t *sw = &columns[i]; - flecs_switch_swap(sw, row_1, row_2); - } -} +#define cases_T_bool(dst, src)\ +case EcsOpBool:\ + set_T(ecs_bool_t, dst, value != 0);\ + break -/* Swap operation for bitset (toggle component) columns */ static -void flecs_table_swap_bitset_columns( - ecs_table_t *table, - int32_t row_1, - int32_t row_2) +void flecs_meta_conversion_error( + ecs_meta_cursor_t *cursor, + ecs_meta_type_op_t *op, + const char *from) { - int32_t i = 0, column_count = table->_->bs_count; - if (!column_count) { - return; - } - - ecs_bitset_t *columns = table->_->bs_columns; - - for (i = 0; i < column_count; i ++) { - ecs_bitset_t *bs = &columns[i]; - flecs_bitset_swap(bs, row_1, row_2); + if (op->kind == EcsOpPop) { + ecs_err("cursor: out of bounds"); + } else { + char *path = ecs_get_path(cursor->world, op->type); + ecs_err("unsupported conversion from %s to '%s'", from, path); + ecs_os_free(path); } } -/* Swap two rows in a table. Used for table sorting. */ -void flecs_table_swap( - ecs_world_t *world, - ecs_table_t *table, - int32_t row_1, - int32_t row_2) -{ - (void)world; - - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - ecs_assert(row_1 >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(row_2 >= 0, ECS_INTERNAL_ERROR, NULL); +int ecs_meta_set_bool( + ecs_meta_cursor_t *cursor, + bool value) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - flecs_table_check_sanity(table); - - if (row_1 == row_2) { - return; + switch(op->kind) { + cases_T_bool(ptr, value); + cases_T_signed(ptr, value, ecs_meta_bounds_signed); + cases_T_unsigned(ptr, value, ecs_meta_bounds_unsigned); + case EcsOpOpaque: { + const EcsOpaque *ot = ecs_get(cursor->world, op->type, EcsOpaque); + if (ot && ot->assign_bool) { + ot->assign_bool(ptr, value); + break; + } + } + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpPrimitive: + case EcsOpF32: + case EcsOpF64: + case EcsOpString: + flecs_meta_conversion_error(cursor, op, "bool"); + return -1; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); } - /* If the table is monitored indicate that there has been a change */ - flecs_table_mark_table_dirty(world, table, 0); - - ecs_entity_t *entities = table->data.entities.array; - ecs_entity_t e1 = entities[row_1]; - ecs_entity_t e2 = entities[row_2]; - - ecs_record_t *record_ptr_1 = flecs_entities_get(world, e1); - ecs_record_t *record_ptr_2 = flecs_entities_get(world, e2); + return 0; +error: + return -1; +} - ecs_assert(record_ptr_1 != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(record_ptr_2 != NULL, ECS_INTERNAL_ERROR, NULL); +int ecs_meta_set_char( + ecs_meta_cursor_t *cursor, + char value) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - /* Keep track of whether entity is watched */ - uint32_t flags_1 = ECS_RECORD_TO_ROW_FLAGS(record_ptr_1->row); - uint32_t flags_2 = ECS_RECORD_TO_ROW_FLAGS(record_ptr_2->row); + switch(op->kind) { + cases_T_bool(ptr, value); + cases_T_signed(ptr, value, ecs_meta_bounds_signed); + case EcsOpOpaque: { + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); + if (opaque->assign_char) { /* preferred operation */ + opaque->assign_char(ptr, value); + break; + } else if (opaque->assign_uint) { + opaque->assign_uint(ptr, (uint64_t)value); + break; + } else if (opaque->assign_int) { + opaque->assign_int(ptr, value); + break; + } + } + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpString: + case EcsOpEntity: + case EcsOpId: + flecs_meta_conversion_error(cursor, op, "char"); + return -1; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + } - /* Swap entities & records */ - entities[row_1] = e2; - entities[row_2] = e1; - record_ptr_1->row = ECS_ROW_TO_RECORD(row_2, flags_1); - record_ptr_2->row = ECS_ROW_TO_RECORD(row_1, flags_2); + return 0; +error: + return -1; +} - flecs_table_swap_switch_columns(table, row_1, row_2); - flecs_table_swap_bitset_columns(table, row_1, row_2); +int ecs_meta_set_int( + ecs_meta_cursor_t *cursor, + int64_t value) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - ecs_column_t *columns = table->data.columns; - if (!columns) { - flecs_table_check_sanity(table); - return; + switch(op->kind) { + cases_T_bool(ptr, value); + cases_T_signed(ptr, value, ecs_meta_bounds_signed); + cases_T_unsigned(ptr, value, ecs_meta_bounds_signed); + cases_T_float(ptr, value); + case EcsOpOpaque: { + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); + if (opaque->assign_int) { /* preferred operation */ + opaque->assign_int(ptr, value); + break; + } else if (opaque->assign_float) { /* most expressive */ + opaque->assign_float(ptr, (double)value); + break; + } else if (opaque->assign_uint && (value > 0)) { + opaque->assign_uint(ptr, flecs_ito(uint64_t, value)); + break; + } else if (opaque->assign_char && (value > 0) && (value < 256)) { + opaque->assign_char(ptr, flecs_ito(char, value)); + break; + } + } + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpPrimitive: + case EcsOpString: { + if(!value) return ecs_meta_set_null(cursor); + flecs_meta_conversion_error(cursor, op, "int"); + return -1; } - - /* Find the maximum size of column elements - * and allocate a temporary buffer for swapping */ - int32_t i, temp_buffer_size = ECS_SIZEOF(uint64_t), column_count = table->column_count; - for (i = 0; i < column_count; i++) { - temp_buffer_size = ECS_MAX(temp_buffer_size, columns[i].size); + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); } - void* tmp = ecs_os_alloca(temp_buffer_size); - - /* Swap columns */ - for (i = 0; i < column_count; i ++) { - int32_t size = columns[i].size; - void *ptr = columns[i].data.array; + return 0; +error: + return -1; +} - void *el_1 = ECS_ELEM(ptr, size, row_1); - void *el_2 = ECS_ELEM(ptr, size, row_2); +int ecs_meta_set_uint( + ecs_meta_cursor_t *cursor, + uint64_t value) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - ecs_os_memcpy(tmp, el_1, size); - ecs_os_memcpy(el_1, el_2, size); - ecs_os_memcpy(el_2, tmp, size); + switch(op->kind) { + cases_T_bool(ptr, value); + cases_T_signed(ptr, value, ecs_meta_bounds_unsigned); + cases_T_unsigned(ptr, value, ecs_meta_bounds_unsigned); + cases_T_float(ptr, value); + case EcsOpOpaque: { + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); + if (opaque->assign_uint) { /* preferred operation */ + opaque->assign_uint(ptr, value); + break; + } else if (opaque->assign_float) { /* most expressive */ + opaque->assign_float(ptr, (double)value); + break; + } else if (opaque->assign_int && (value < INT64_MAX)) { + opaque->assign_int(ptr, flecs_uto(int64_t, value)); + break; + } else if (opaque->assign_char && (value < 256)) { + opaque->assign_char(ptr, flecs_uto(char, value)); + break; + } + } + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpPrimitive: + case EcsOpString: + if(!value) return ecs_meta_set_null(cursor); + flecs_meta_conversion_error(cursor, op, "uint"); + return -1; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); } - flecs_table_check_sanity(table); + return 0; +error: + return -1; } -static -void flecs_table_merge_vec( - ecs_world_t *world, - ecs_vec_t *dst, - ecs_vec_t *src, - int32_t size, - int32_t elem_size) +int ecs_meta_set_float( + ecs_meta_cursor_t *cursor, + double value) { - int32_t dst_count = dst->count; - - if (!dst_count) { - ecs_vec_fini(&world->allocator, dst, size); - *dst = *src; - src->array = NULL; - src->count = 0; - src->size = 0; - } else { - int32_t src_count = src->count; + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - if (elem_size) { - ecs_vec_set_size(&world->allocator, - dst, size, elem_size); + switch(op->kind) { + case EcsOpBool: + if (ECS_EQZERO(value)) { + set_T(bool, ptr, false); + } else { + set_T(bool, ptr, true); } - ecs_vec_set_count(&world->allocator, - dst, size, dst_count + src_count); - - void *dst_ptr = ECS_ELEM(dst->array, size, dst_count); - void *src_ptr = src->array; - ecs_os_memcpy(dst_ptr, src_ptr, size * src_count); - - ecs_vec_fini(&world->allocator, src, size); + break; + cases_T_signed(ptr, value, ecs_meta_bounds_float); + cases_T_unsigned(ptr, value, ecs_meta_bounds_float); + cases_T_float(ptr, value); + case EcsOpOpaque: { + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); + if (opaque->assign_float) { /* preferred operation */ + opaque->assign_float(ptr, value); + break; + } else if (opaque->assign_int && /* most expressive */ + (value <= (double)INT64_MAX) && (value >= (double)INT64_MIN)) + { + opaque->assign_int(ptr, (int64_t)value); + break; + } else if (opaque->assign_uint && (value >= 0)) { + opaque->assign_uint(ptr, (uint64_t)value); + break; + } else if (opaque->assign_entity && (value >= 0)) { + opaque->assign_entity( + ptr, ECS_CONST_CAST(ecs_world_t*, cursor->world), + (ecs_entity_t)value); + break; + } + } + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpPrimitive: + case EcsOpString: + flecs_meta_conversion_error(cursor, op, "float"); + return -1; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); } + + return 0; +error: + return -1; } -/* Merge data from one table column into other table column */ -static -void flecs_table_merge_column( - ecs_world_t *world, - ecs_column_t *dst, - ecs_column_t *src, - int32_t column_size) +int ecs_meta_set_value( + ecs_meta_cursor_t *cursor, + const ecs_value_t *value) { - ecs_size_t size = dst->size; - int32_t dst_count = dst->data.count; - - if (!dst_count) { - ecs_vec_fini(&world->allocator, &dst->data, size); - *dst = *src; - src->data.array = NULL; - src->data.count = 0; - src->data.size = 0; + ecs_check(value != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_entity_t type = value->type; + ecs_check(type != 0, ECS_INVALID_PARAMETER, NULL); + const EcsType *mt = ecs_get(cursor->world, type, EcsType); + if (!mt) { + ecs_err("type of value does not have reflection data"); + return -1; + } - /* If the new table is not empty, copy the contents from the - * src into the dst. */ + if (mt->kind == EcsPrimitiveType) { + const EcsPrimitive *prim = ecs_get(cursor->world, type, EcsPrimitive); + ecs_check(prim != NULL, ECS_INTERNAL_ERROR, NULL); + switch(prim->kind) { + case EcsBool: return ecs_meta_set_bool(cursor, *(bool*)value->ptr); + case EcsChar: return ecs_meta_set_char(cursor, *(char*)value->ptr); + case EcsByte: return ecs_meta_set_uint(cursor, *(uint8_t*)value->ptr); + case EcsU8: return ecs_meta_set_uint(cursor, *(uint8_t*)value->ptr); + case EcsU16: return ecs_meta_set_uint(cursor, *(uint16_t*)value->ptr); + case EcsU32: return ecs_meta_set_uint(cursor, *(uint32_t*)value->ptr); + case EcsU64: return ecs_meta_set_uint(cursor, *(uint64_t*)value->ptr); + case EcsI8: return ecs_meta_set_int(cursor, *(int8_t*)value->ptr); + case EcsI16: return ecs_meta_set_int(cursor, *(int16_t*)value->ptr); + case EcsI32: return ecs_meta_set_int(cursor, *(int32_t*)value->ptr); + case EcsI64: return ecs_meta_set_int(cursor, *(int64_t*)value->ptr); + case EcsF32: return ecs_meta_set_float(cursor, (double)*(float*)value->ptr); + case EcsF64: return ecs_meta_set_float(cursor, *(double*)value->ptr); + case EcsUPtr: return ecs_meta_set_uint(cursor, *(uintptr_t*)value->ptr); + case EcsIPtr: return ecs_meta_set_int(cursor, *(intptr_t*)value->ptr); + case EcsString: return ecs_meta_set_string(cursor, *(char**)value->ptr); + case EcsEntity: return ecs_meta_set_entity(cursor, *(ecs_entity_t*)value->ptr); + case EcsId: return ecs_meta_set_id(cursor, *(ecs_id_t*)value->ptr); + default: + ecs_throw(ECS_INTERNAL_ERROR, "invalid type kind"); + goto error; + } + } else if (mt->kind == EcsEnumType) { + return ecs_meta_set_int(cursor, *(int32_t*)value->ptr); + } else if (mt->kind == EcsBitmaskType) { + return ecs_meta_set_int(cursor, *(uint32_t*)value->ptr); } else { - int32_t src_count = src->data.count; - - flecs_table_grow_column(world, dst, src_count, column_size, true); - void *dst_ptr = ECS_ELEM(dst->data.array, size, dst_count); - void *src_ptr = src->data.array; - - /* Move values into column */ - ecs_type_info_t *ti = dst->ti; - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_move_t move = ti->hooks.move_dtor; - if (move) { - move(dst_ptr, src_ptr, src_count, ti); - } else { - ecs_os_memcpy(dst_ptr, src_ptr, size * src_count); + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + if (op->type != value->type) { + char *type_str = ecs_get_path(cursor->world, value->type); + flecs_meta_conversion_error(cursor, op, type_str); + ecs_os_free(type_str); + goto error; } - - ecs_vec_fini(&world->allocator, &src->data, size); + return ecs_value_copy(cursor->world, value->type, ptr, value->ptr); } + +error: + return -1; } -/* Merge storage of two tables. */ static -void flecs_table_merge_data( - ecs_world_t *world, - ecs_table_t *dst_table, - ecs_table_t *src_table, - int32_t src_count, - int32_t dst_count, - ecs_data_t *src_data, - ecs_data_t *dst_data) +int flecs_meta_add_bitmask_constant( + ecs_meta_cursor_t *cursor, + ecs_meta_type_op_t *op, + void *out, + const char *value) { - int32_t i_new = 0, dst_column_count = dst_table->column_count; - int32_t i_old = 0, src_column_count = src_table->column_count; - ecs_column_t *src_columns = src_data->columns; - ecs_column_t *dst_columns = dst_data->columns; - - ecs_assert(!dst_column_count || dst_columns, ECS_INTERNAL_ERROR, NULL); + ecs_assert(op->type != 0, ECS_INTERNAL_ERROR, NULL); - if (!src_count) { - return; + if (!ecs_os_strcmp(value, "0")) { + return 0; } - /* Merge entities */ - flecs_table_merge_vec(world, &dst_data->entities, &src_data->entities, - ECS_SIZEOF(ecs_entity_t), 0); - ecs_assert(dst_data->entities.count == src_count + dst_count, - ECS_INTERNAL_ERROR, NULL); - int32_t column_size = dst_data->entities.size; - ecs_allocator_t *a = &world->allocator; - - for (; (i_new < dst_column_count) && (i_old < src_column_count); ) { - ecs_column_t *dst_column = &dst_columns[i_new]; - ecs_column_t *src_column = &src_columns[i_old]; - ecs_id_t dst_id = dst_column->id; - ecs_id_t src_id = src_column->id; - - if (dst_id == src_id) { - flecs_table_merge_column(world, dst_column, src_column, column_size); - flecs_table_mark_table_dirty(world, dst_table, i_new + 1); - i_new ++; - i_old ++; - } else if (dst_id < src_id) { - /* New column, make sure vector is large enough. */ - ecs_size_t size = dst_column->size; - ecs_vec_set_size(a, &dst_column->data, size, column_size); - ecs_vec_set_count(a, &dst_column->data, size, src_count + dst_count); - flecs_table_invoke_ctor(dst_column, dst_count, src_count); - i_new ++; - } else if (dst_id > src_id) { - /* Old column does not occur in new table, destruct */ - flecs_table_invoke_dtor(src_column, 0, src_count); - ecs_vec_fini(a, &src_column->data, src_column->size); - i_old ++; - } + ecs_entity_t c = ecs_lookup_child(cursor->world, op->type, value); + if (!c) { + char *path = ecs_get_path(cursor->world, op->type); + ecs_err("unresolved bitmask constant '%s' for type '%s'", value, path); + ecs_os_free(path); + return -1; } - flecs_table_move_switch_columns(dst_table, dst_count, src_table, 0, src_count, true); - flecs_table_move_bitset_columns(dst_table, dst_count, src_table, 0, src_count, true); - - /* Initialize remaining columns */ - for (; i_new < dst_column_count; i_new ++) { - ecs_column_t *column = &dst_columns[i_new]; - int32_t size = column->size; - ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); - ecs_vec_set_size(a, &column->data, size, column_size); - ecs_vec_set_count(a, &column->data, size, src_count + dst_count); - flecs_table_invoke_ctor(column, dst_count, src_count); + const ecs_u32_t *v = ecs_get_pair_second( + cursor->world, c, EcsConstant, ecs_u32_t); + if (v == NULL) { + char *path = ecs_get_path(cursor->world, op->type); + ecs_err("'%s' is not an bitmask constant for type '%s'", value, path); + ecs_os_free(path); + return -1; } - /* Destruct remaining columns */ - for (; i_old < src_column_count; i_old ++) { - ecs_column_t *column = &src_columns[i_old]; - flecs_table_invoke_dtor(column, 0, src_count); - ecs_vec_fini(a, &column->data, column->size); - } + *(ecs_u32_t*)out |= v[0]; - /* Mark entity column as dirty */ - flecs_table_mark_table_dirty(world, dst_table, 0); + return 0; } -/* Merge source table into destination table. This typically happens as result - * of a bulk operation, like when a component is removed from all entities in - * the source table (like for the Remove OnDelete policy). */ -void flecs_table_merge( - ecs_world_t *world, - ecs_table_t *dst_table, - ecs_table_t *src_table, - ecs_data_t *dst_data, - ecs_data_t *src_data) +static +int flecs_meta_parse_bitmask( + ecs_meta_cursor_t *cursor, + ecs_meta_type_op_t *op, + void *out, + const char *value) { - ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!src_table->_->lock, ECS_LOCKED_STORAGE, NULL); + char token[ECS_MAX_TOKEN_SIZE]; - flecs_table_check_sanity(src_table); - flecs_table_check_sanity(dst_table); - - bool move_data = false; - - /* If there is nothing to merge to, just clear the old table */ - if (!dst_table) { - flecs_table_clear_data(world, src_table, src_data); - flecs_table_check_sanity(src_table); - return; - } else { - ecs_assert(!dst_table->_->lock, ECS_LOCKED_STORAGE, NULL); - } + const char *prev = value, *ptr = value; - /* If there is no data to merge, drop out */ - if (!src_data) { - return; - } + *(ecs_u32_t*)out = 0; - if (!dst_data) { - dst_data = &dst_table->data; - if (dst_table == src_table) { - move_data = true; + while ((ptr = strchr(ptr, '|'))) { + ecs_os_memcpy(token, prev, ptr - prev); + token[ptr - prev] = '\0'; + if (flecs_meta_add_bitmask_constant(cursor, op, out, token) != 0) { + return -1; } - } - - ecs_entity_t *src_entities = src_data->entities.array; - int32_t src_count = src_data->entities.count; - int32_t dst_count = dst_data->entities.count; - - /* First, update entity index so old entities point to new type */ - int32_t i; - for(i = 0; i < src_count; i ++) { - ecs_record_t *record = flecs_entities_ensure(world, src_entities[i]); - uint32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); - record->row = ECS_ROW_TO_RECORD(dst_count + i, flags); - record->table = dst_table; - } - /* Merge table columns */ - if (move_data) { - *dst_data = *src_data; - } else { - flecs_table_merge_data(world, dst_table, src_table, src_count, dst_count, - src_data, dst_data); + ptr ++; + prev = ptr; } - if (src_count) { - if (!dst_count) { - flecs_table_set_empty(world, dst_table); - } - flecs_table_set_empty(world, src_table); - - flecs_table_traversable_add(dst_table, src_table->_->traversable_count); - flecs_table_traversable_add(src_table, -src_table->_->traversable_count); - ecs_assert(src_table->_->traversable_count == 0, ECS_INTERNAL_ERROR, NULL); + if (flecs_meta_add_bitmask_constant(cursor, op, out, prev) != 0) { + return -1; } - flecs_table_check_sanity(src_table); - flecs_table_check_sanity(dst_table); + return 0; } -/* Replace data with other data. Used by snapshots to restore previous state. */ -void flecs_table_replace_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data) +static +int flecs_meta_cursor_lookup( + ecs_meta_cursor_t *cursor, + const char *value, + ecs_entity_t *out) { - int32_t prev_count = 0; - ecs_data_t *table_data = &table->data; - ecs_assert(!data || data != table_data, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - - flecs_table_check_sanity(table); - - prev_count = table_data->entities.count; - flecs_table_notify_on_remove(world, table, table_data); - flecs_table_clear_data(world, table, table_data); - - if (data) { - table->data = *data; - } else { - flecs_table_init_data(world, table); - } - - int32_t count = ecs_table_count(table); - - if (!prev_count && count) { - flecs_table_set_empty(world, table); - } else if (prev_count && !count) { - flecs_table_set_empty(world, table); + if (ecs_os_strcmp(value, "#0")) { + if (cursor->lookup_action) { + *out = cursor->lookup_action( + cursor->world, value, + cursor->lookup_ctx); + } else { + *out = ecs_lookup_from(cursor->world, 0, value); + } + if (!*out) { + ecs_err("unresolved entity identifier '%s'", value); + return -1; + } } + return 0; +} - flecs_table_check_sanity(table); +static +bool flecs_meta_valid_digit( + const char *str) +{ + return str[0] == '-' || isdigit(str[0]); } -/* Internal mechanism for propagating information to tables */ -void flecs_table_notify( - ecs_world_t *world, - ecs_table_t *table, - ecs_table_event_t *event) +int ecs_meta_set_string( + ecs_meta_cursor_t *cursor, + const char *value) { - if (world->flags & EcsWorldFini) { - return; + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + + switch(op->kind) { + case EcsOpI8: + case EcsOpU8: + case EcsOpByte: + case EcsOpI16: + case EcsOpU16: + case EcsOpI32: + case EcsOpU32: + case EcsOpI64: + case EcsOpU64: + case EcsOpIPtr: + case EcsOpUPtr: + case EcsOpF32: + case EcsOpF64: + if (!flecs_meta_valid_digit(value)) { + ecs_err("expected number, got '%s'", value); + goto error; + } + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpString: + case EcsOpEntity: + case EcsOpId: + case EcsOpScope: + break; } - switch(event->kind) { - case EcsTableTriggersForId: - flecs_table_add_trigger_flags(world, table, event->event); + switch(op->kind) { + case EcsOpBool: + if (!ecs_os_strcmp(value, "true")) { + set_T(ecs_bool_t, ptr, true); + } else if (!ecs_os_strcmp(value, "false")) { + set_T(ecs_bool_t, ptr, false); + } else if (isdigit(value[0])) { + if (!ecs_os_strcmp(value, "0")) { + set_T(ecs_bool_t, ptr, false); + } else { + set_T(ecs_bool_t, ptr, true); + } + } else { + ecs_err("invalid value for boolean '%s'", value); + goto error; + } break; - case EcsTableNoTriggersForId: + case EcsOpI8: + case EcsOpU8: + case EcsOpByte: + set_T(ecs_i8_t, ptr, atol(value)); + break; + case EcsOpChar: + set_T(char, ptr, value[0]); + break; + case EcsOpI16: + case EcsOpU16: + set_T(ecs_i16_t, ptr, atol(value)); + break; + case EcsOpI32: + case EcsOpU32: + set_T(ecs_i32_t, ptr, atol(value)); + break; + case EcsOpI64: + case EcsOpU64: + set_T(ecs_i64_t, ptr, atoll(value)); + break; + case EcsOpIPtr: + case EcsOpUPtr: + set_T(ecs_iptr_t, ptr, atoll(value)); + break; + case EcsOpF32: + set_T(ecs_f32_t, ptr, atof(value)); + break; + case EcsOpF64: + set_T(ecs_f64_t, ptr, atof(value)); + break; + case EcsOpString: { + ecs_assert(*(ecs_string_t*)ptr != value, ECS_INVALID_PARAMETER, NULL); + ecs_os_free(*(ecs_string_t*)ptr); + char *result = ecs_os_strdup(value); + set_T(ecs_string_t, ptr, result); break; } -} - -/* -- Public API -- */ + case EcsOpEnum: { + ecs_assert(op->type != 0, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t c = ecs_lookup_child(cursor->world, op->type, value); + if (!c) { + char *path = ecs_get_path(cursor->world, op->type); + ecs_err("unresolved enum constant '%s' for type '%s'", value, path); + ecs_os_free(path); + goto error; + } -void ecs_table_lock( - ecs_world_t *world, - ecs_table_t *table) -{ - if (table) { - if (ecs_poly_is(world, ecs_world_t) && !(world->flags & EcsWorldReadonly)) { - table->_->lock ++; + const ecs_i32_t *v = ecs_get_pair_second( + cursor->world, c, EcsConstant, ecs_i32_t); + if (v == NULL) { + char *path = ecs_get_path(cursor->world, op->type); + ecs_err("'%s' is not an enum constant for type '%s'", value, path); + ecs_os_free(path); + goto error; } - } -} -void ecs_table_unlock( - ecs_world_t *world, - ecs_table_t *table) -{ - if (table) { - if (ecs_poly_is(world, ecs_world_t) && !(world->flags & EcsWorldReadonly)) { - table->_->lock --; - ecs_assert(table->_->lock >= 0, ECS_INVALID_OPERATION, NULL); + set_T(ecs_i32_t, ptr, v[0]); + break; + } + case EcsOpBitmask: + if (flecs_meta_parse_bitmask(cursor, op, ptr, value) != 0) { + goto error; + } + break; + case EcsOpEntity: { + ecs_entity_t e = 0; + if (flecs_meta_cursor_lookup(cursor, value, &e)) { + goto error; } + set_T(ecs_entity_t, ptr, e); + break; } -} + case EcsOpId: { + #ifdef FLECS_SCRIPT + ecs_id_t id = 0; + if (flecs_id_parse(cursor->world, NULL, value, &id) == NULL) { + goto error; + } -const ecs_type_t* ecs_table_get_type( - const ecs_table_t *table) -{ - if (table) { - return &table->type; - } else { - return NULL; + set_T(ecs_id_t, ptr, id); + #else + ecs_err("cannot parse component expression: script addon required"); + #endif + break; } -} - -int32_t ecs_table_get_type_index( - const ecs_world_t *world, - const ecs_table_t *table, - ecs_id_t id) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - return -1; + case EcsOpPop: + ecs_err("excess element '%s' in scope", value); + goto error; + case EcsOpOpaque: { + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); + if (opaque->assign_string) { /* preferred */ + opaque->assign_string(ptr, value); + break; + } else if (opaque->assign_char && value[0] && !value[1]) { + opaque->assign_char(ptr, value[0]); + break; + } else if (opaque->assign_entity) { + ecs_entity_t e = 0; + if (flecs_meta_cursor_lookup(cursor, value, &e)) { + goto error; + } + opaque->assign_entity(ptr, + ECS_CONST_CAST(ecs_world_t*, cursor->world), e); + break; + } } - - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (!tr) { - return -1; + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpScope: + case EcsOpPrimitive: + ecs_err("unsupported conversion from string '%s' to '%s'", + value, flecs_meta_op_kind_str(op->kind)); + goto error; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; } - return tr->index; + return 0; error: return -1; } -int32_t ecs_table_get_column_index( - const ecs_world_t *world, - const ecs_table_t *table, - ecs_id_t id) +int ecs_meta_set_string_literal( + ecs_meta_cursor_t *cursor, + const char *value) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - return -1; + ecs_size_t len = ecs_os_strlen(value); + if (value[0] != '\"' || value[len - 1] != '\"') { + ecs_err("invalid string literal '%s'", value); + goto error; } - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (!tr) { - return -1; - } + switch(op->kind) { + case EcsOpChar: + set_T(ecs_char_t, ptr, value[1]); + break; - return tr->column; -error: - return -1; -} + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + case EcsOpEntity: + case EcsOpId: + len -= 2; -int32_t ecs_table_column_count( - const ecs_table_t *table) -{ - return table->column_count; -} + char *result = ecs_os_malloc(len + 1); + ecs_os_memcpy(result, value + 1, len); + result[len] = '\0'; -int32_t ecs_table_type_to_column_index( - const ecs_table_t *table, - int32_t index) -{ - ecs_assert(index >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(index < table->type.count, ECS_INVALID_PARAMETER, NULL); - int32_t *column_map = table->column_map; - if (column_map) { - return column_map[index]; + if (ecs_meta_set_string(cursor, result)) { + ecs_os_free(result); + return -1; + } + + ecs_os_free(result); + break; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; } -error: - return -1; -} -int32_t ecs_table_column_to_type_index( - const ecs_table_t *table, - int32_t index) -{ - ecs_check(index < table->column_count, ECS_INVALID_PARAMETER, NULL); - ecs_check(table->column_map != NULL, ECS_INVALID_PARAMETER, NULL); - int32_t offset = table->type.count; - return table->column_map[offset + index]; + return 0; error: return -1; } -void* ecs_table_get_column( - const ecs_table_t *table, - int32_t index, - int32_t offset) +int ecs_meta_set_entity( + ecs_meta_cursor_t *cursor, + ecs_entity_t value) { - ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(index < table->column_count, ECS_INVALID_PARAMETER, NULL); + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - ecs_column_t *column = &table->data.columns[index]; - void *result = column->data.array; - if (offset) { - result = ECS_ELEM(result, column->size, offset); + switch(op->kind) { + case EcsOpEntity: + set_T(ecs_entity_t, ptr, value); + break; + case EcsOpId: + set_T(ecs_id_t, ptr, value); /* entities are valid ids */ + break; + case EcsOpOpaque: { + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + if (opaque && opaque->assign_entity) { + opaque->assign_entity(ptr, + ECS_CONST_CAST(ecs_world_t*, cursor->world), value); + break; + } } - - return result; -error: - return NULL; -} - -void* ecs_table_get_id( - const ecs_world_t *world, - const ecs_table_t *table, - ecs_id_t id, - int32_t offset) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - - world = ecs_get_world(world); - - int32_t index = ecs_table_get_column_index(world, table, id); - if (index == -1) { - return NULL; + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + flecs_meta_conversion_error(cursor, op, "entity"); + goto error; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; } - return ecs_table_get_column(table, index, offset); -error: - return NULL; -} - -size_t ecs_table_get_column_size( - const ecs_table_t *table, - int32_t column) -{ - ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(column < table->column_count, ECS_INVALID_PARAMETER, NULL); - ecs_check(table->column_map != NULL, ECS_INVALID_PARAMETER, NULL); - - return flecs_ito(size_t, table->data.columns[column].size); -error: return 0; -} - -int32_t ecs_table_count( - const ecs_table_t *table) -{ - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - return flecs_table_data_count(&table->data); -} - -bool ecs_table_has_id( - const ecs_world_t *world, - const ecs_table_t *table, - ecs_id_t id) -{ - return ecs_table_get_type_index(world, table, id) != -1; -} - -int32_t ecs_table_get_depth( - const ecs_world_t *world, - const ecs_table_t *table, - ecs_entity_t rel) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, rel), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_has_id(world, rel, EcsAcyclic), ECS_INVALID_PARAMETER, NULL); - - world = ecs_get_world(world); - - return flecs_relation_depth(world, rel, table); error: return -1; } -bool ecs_table_has_flags( - ecs_table_t *table, - ecs_flags32_t flags) +int ecs_meta_set_id( + ecs_meta_cursor_t *cursor, + ecs_entity_t value) { - return (table->flags & flags) == flags; -} + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); -int32_t flecs_table_column_to_union_index( - const ecs_table_t *table, - int32_t column) -{ - int32_t sw_count = table->_->sw_count; - if (sw_count) { - int32_t sw_offset = table->_->sw_offset; - if (column >= sw_offset && column < (sw_offset + sw_count)){ - return column - sw_offset; + switch(op->kind) { + case EcsOpId: + set_T(ecs_id_t, ptr, value); + break; + case EcsOpOpaque: { + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + if (opaque && opaque->assign_id) { + opaque->assign_id(ptr, + ECS_CONST_CAST(ecs_world_t*, cursor->world), value); + break; } } - return -1; -} - -void ecs_table_swap_rows( - ecs_world_t* world, - ecs_table_t* table, - int32_t row_1, - int32_t row_2) -{ - flecs_table_swap(world, table, row_1, row_2); -} - -int32_t flecs_table_observed_count( - const ecs_table_t *table) -{ - return table->_->traversable_count; -} - -void* ecs_record_get_column( - const ecs_record_t *r, - int32_t index, - size_t c_size) -{ - (void)c_size; - ecs_table_t *table = r->table; - - ecs_check(index < table->column_count, ECS_INVALID_PARAMETER, NULL); - ecs_column_t *column = &table->data.columns[index]; - ecs_size_t size = column->size; - - ecs_check(!flecs_utosize(c_size) || flecs_utosize(c_size) == size, - ECS_INVALID_PARAMETER, NULL); - - return ecs_vec_get(&column->data, size, ECS_RECORD_TO_ROW(r->row)); -error: - return NULL; -} - -ecs_record_t* ecs_record_find( - const ecs_world_t *world, - ecs_entity_t entity) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); - - world = ecs_get_world(world); - - ecs_record_t *r = flecs_entities_get(world, entity); - if (r) { - return r; - } -error: - return NULL; -} - -/** - * @file table_cache.c - * @brief Data structure for fast table iteration/lookups. - * - * A table cache is a data structure that provides constant time operations for - * insertion and removal of tables, and to testing whether a table is registered - * with the cache. A table cache also provides functions to iterate the tables - * in a cache. - * - * The world stores a table cache per (component) id inside the id record - * administration. Cached queries store a table cache with matched tables. - * - * A table cache has separate lists for non-empty tables and empty tables. This - * improves performance as applications don't waste time iterating empty tables. - */ - - -static -void flecs_table_cache_list_remove( - ecs_table_cache_t *cache, - ecs_table_cache_hdr_t *elem) -{ - ecs_table_cache_hdr_t *next = elem->next; - ecs_table_cache_hdr_t *prev = elem->prev; - - if (next) { - next->prev = prev; - } - if (prev) { - prev->next = next; + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + case EcsOpEntity: + flecs_meta_conversion_error(cursor, op, "id"); + goto error; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; } - cache->empty_tables.count -= !!elem->empty; - cache->tables.count -= !elem->empty; - - if (cache->empty_tables.first == elem) { - cache->empty_tables.first = next; - } else if (cache->tables.first == elem) { - cache->tables.first = next; - } - if (cache->empty_tables.last == elem) { - cache->empty_tables.last = prev; - } - if (cache->tables.last == elem) { - cache->tables.last = prev; - } + return 0; +error: + return -1; } -static -void flecs_table_cache_list_insert( - ecs_table_cache_t *cache, - ecs_table_cache_hdr_t *elem) +int ecs_meta_set_null( + ecs_meta_cursor_t *cursor) { - ecs_table_cache_hdr_t *last; - if (elem->empty) { - last = cache->empty_tables.last; - cache->empty_tables.last = elem; - if ((++ cache->empty_tables.count) == 1) { - cache->empty_tables.first = elem; - } - } else { - last = cache->tables.last; - cache->tables.last = elem; - if ((++ cache->tables.count) == 1) { - cache->tables.first = elem; + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch (op->kind) { + case EcsOpString: + ecs_os_free(*(char**)ptr); + set_T(ecs_string_t, ptr, NULL); + break; + case EcsOpOpaque: { + const EcsOpaque *ot = ecs_get(cursor->world, op->type, EcsOpaque); + if (ot && ot->assign_null) { + ot->assign_null(ptr); + break; } } - - elem->next = NULL; - elem->prev = last; - - if (last) { - last->next = elem; + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpEntity: + case EcsOpId: + flecs_meta_conversion_error(cursor, op, "null"); + goto error; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; } -} -void ecs_table_cache_init( - ecs_world_t *world, - ecs_table_cache_t *cache) -{ - ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_map_init_w_params(&cache->index, &world->allocators.ptr); + return 0; +error: + return -1; } -void ecs_table_cache_fini( - ecs_table_cache_t *cache) +bool ecs_meta_get_bool( + const ecs_meta_cursor_t *cursor) { - ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_map_fini(&cache->index); -} + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch(op->kind) { + case EcsOpBool: return *(ecs_bool_t*)ptr; + case EcsOpI8: return *(ecs_i8_t*)ptr != 0; + case EcsOpU8: return *(ecs_u8_t*)ptr != 0; + case EcsOpChar: return *(ecs_char_t*)ptr != 0; + case EcsOpByte: return *(ecs_u8_t*)ptr != 0; + case EcsOpI16: return *(ecs_i16_t*)ptr != 0; + case EcsOpU16: return *(ecs_u16_t*)ptr != 0; + case EcsOpI32: return *(ecs_i32_t*)ptr != 0; + case EcsOpU32: return *(ecs_u32_t*)ptr != 0; + case EcsOpI64: return *(ecs_i64_t*)ptr != 0; + case EcsOpU64: return *(ecs_u64_t*)ptr != 0; + case EcsOpIPtr: return *(ecs_iptr_t*)ptr != 0; + case EcsOpUPtr: return *(ecs_uptr_t*)ptr != 0; + case EcsOpF32: return ECS_NEQZERO(*(ecs_f32_t*)ptr); + case EcsOpF64: return ECS_NEQZERO(*(ecs_f64_t*)ptr); + case EcsOpString: return *(const char**)ptr != NULL; + case EcsOpEnum: return *(ecs_i32_t*)ptr != 0; + case EcsOpBitmask: return *(ecs_u32_t*)ptr != 0; + case EcsOpEntity: return *(ecs_entity_t*)ptr != 0; + case EcsOpId: return *(ecs_id_t*)ptr != 0; + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpPrimitive: + ecs_throw(ECS_INVALID_PARAMETER, "invalid element for bool"); + break; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; + } -bool ecs_table_cache_is_empty( - const ecs_table_cache_t *cache) -{ - return ecs_map_count(&cache->index) == 0; +error: + return 0; } -void ecs_table_cache_insert( - ecs_table_cache_t *cache, - const ecs_table_t *table, - ecs_table_cache_hdr_t *result) +char ecs_meta_get_char( + const ecs_meta_cursor_t *cursor) { - ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ecs_table_cache_get(cache, table) == NULL, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(result != NULL, ECS_INTERNAL_ERROR, NULL); - - bool empty; - if (!table) { - empty = false; - } else { - empty = ecs_table_count(table) == 0; - } - - result->cache = cache; - result->table = ECS_CONST_CAST(ecs_table_t*, table); - result->empty = empty; - - flecs_table_cache_list_insert(cache, result); - - if (table) { - ecs_map_insert_ptr(&cache->index, table->id, result); + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch(op->kind) { + case EcsOpChar: + return *(ecs_char_t*)ptr != 0; + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + case EcsOpEntity: + case EcsOpId: + ecs_throw(ECS_INVALID_PARAMETER, "invalid element for char"); + break; } - ecs_assert(empty || cache->tables.first != NULL, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(!empty || cache->empty_tables.first != NULL, - ECS_INTERNAL_ERROR, NULL); +error: + return 0; } -void ecs_table_cache_replace( - ecs_table_cache_t *cache, - const ecs_table_t *table, - ecs_table_cache_hdr_t *elem) +int64_t ecs_meta_get_int( + const ecs_meta_cursor_t *cursor) { - ecs_table_cache_hdr_t **r = ecs_map_get_ref( - &cache->index, ecs_table_cache_hdr_t, table->id); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_table_cache_hdr_t *old = *r; - ecs_assert(old != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_table_cache_hdr_t *prev = old->prev, *next = old->next; - if (prev) { - ecs_assert(prev->next == old, ECS_INTERNAL_ERROR, NULL); - prev->next = elem; - } - if (next) { - ecs_assert(next->prev == old, ECS_INTERNAL_ERROR, NULL); - next->prev = elem; + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch(op->kind) { + case EcsOpBool: return *(const ecs_bool_t*)ptr; + case EcsOpI8: return *(const ecs_i8_t*)ptr; + case EcsOpU8: return *(const ecs_u8_t*)ptr; + case EcsOpChar: return *(const ecs_char_t*)ptr; + case EcsOpByte: return *(const ecs_u8_t*)ptr; + case EcsOpI16: return *(const ecs_i16_t*)ptr; + case EcsOpU16: return *(const ecs_u16_t*)ptr; + case EcsOpI32: return *(const ecs_i32_t*)ptr; + case EcsOpU32: return *(const ecs_u32_t*)ptr; + case EcsOpI64: return *(const ecs_i64_t*)ptr; + case EcsOpU64: return flecs_uto(int64_t, *(const ecs_u64_t*)ptr); + case EcsOpIPtr: return *(const ecs_iptr_t*)ptr; + case EcsOpUPtr: return flecs_uto(int64_t, *(const ecs_uptr_t*)ptr); + case EcsOpF32: return (int64_t)*(const ecs_f32_t*)ptr; + case EcsOpF64: return (int64_t)*(const ecs_f64_t*)ptr; + case EcsOpString: return atoi(*(const char**)ptr); + case EcsOpEnum: return *(const ecs_i32_t*)ptr; + case EcsOpBitmask: return *(const ecs_u32_t*)ptr; + case EcsOpEntity: + ecs_throw(ECS_INVALID_PARAMETER, + "invalid conversion from entity to int"); + break; + case EcsOpId: + ecs_throw(ECS_INVALID_PARAMETER, + "invalid conversion from id to int"); + break; + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpPrimitive: + ecs_throw(ECS_INVALID_PARAMETER, "invalid element for int"); + break; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; } +error: + return 0; +} - if (cache->empty_tables.first == old) { - cache->empty_tables.first = elem; - } - if (cache->empty_tables.last == old) { - cache->empty_tables.last = elem; - } - if (cache->tables.first == old) { - cache->tables.first = elem; - } - if (cache->tables.last == old) { - cache->tables.last = elem; +uint64_t ecs_meta_get_uint( + const ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch(op->kind) { + case EcsOpBool: return *(ecs_bool_t*)ptr; + case EcsOpI8: return flecs_ito(uint64_t, *(const ecs_i8_t*)ptr); + case EcsOpU8: return *(ecs_u8_t*)ptr; + case EcsOpChar: return flecs_ito(uint64_t, *(const ecs_char_t*)ptr); + case EcsOpByte: return flecs_ito(uint64_t, *(const ecs_u8_t*)ptr); + case EcsOpI16: return flecs_ito(uint64_t, *(const ecs_i16_t*)ptr); + case EcsOpU16: return *(ecs_u16_t*)ptr; + case EcsOpI32: return flecs_ito(uint64_t, *(const ecs_i32_t*)ptr); + case EcsOpU32: return *(ecs_u32_t*)ptr; + case EcsOpI64: return flecs_ito(uint64_t, *(const ecs_i64_t*)ptr); + case EcsOpU64: return *(ecs_u64_t*)ptr; + case EcsOpIPtr: return flecs_ito(uint64_t, *(const ecs_i64_t*)ptr); + case EcsOpUPtr: return *(ecs_uptr_t*)ptr; + case EcsOpF32: return flecs_ito(uint64_t, *(const ecs_f32_t*)ptr); + case EcsOpF64: return flecs_ito(uint64_t, *(const ecs_f64_t*)ptr); + case EcsOpString: return flecs_ito(uint64_t, atoi(*(const char**)ptr)); + case EcsOpEnum: return flecs_ito(uint64_t, *(const ecs_i32_t*)ptr); + case EcsOpBitmask: return *(const ecs_u32_t*)ptr; + case EcsOpEntity: return *(const ecs_entity_t*)ptr; + case EcsOpId: return *(const ecs_id_t*)ptr; + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpPrimitive: + ecs_throw(ECS_INVALID_PARAMETER, "invalid element for uint"); + break; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; } - - *r = elem; - elem->prev = prev; - elem->next = next; +error: + return 0; } -void* ecs_table_cache_get( - const ecs_table_cache_t *cache, - const ecs_table_t *table) +static +double flecs_meta_to_float( + ecs_meta_type_op_kind_t kind, + const void *ptr) { - ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - if (table) { - if (ecs_map_is_init(&cache->index)) { - return ecs_map_get_deref(&cache->index, void**, table->id); - } - return NULL; - } else { - ecs_table_cache_hdr_t *elem = cache->tables.first; - ecs_assert(!elem || elem->table == NULL, ECS_INTERNAL_ERROR, NULL); - return elem; + switch(kind) { + case EcsOpBool: return *(const ecs_bool_t*)ptr; + case EcsOpI8: return *(const ecs_i8_t*)ptr; + case EcsOpU8: return *(const ecs_u8_t*)ptr; + case EcsOpChar: return *(const ecs_char_t*)ptr; + case EcsOpByte: return *(const ecs_u8_t*)ptr; + case EcsOpI16: return *(const ecs_i16_t*)ptr; + case EcsOpU16: return *(const ecs_u16_t*)ptr; + case EcsOpI32: return *(const ecs_i32_t*)ptr; + case EcsOpU32: return *(const ecs_u32_t*)ptr; + case EcsOpI64: return (double)*(const ecs_i64_t*)ptr; + case EcsOpU64: return (double)*(const ecs_u64_t*)ptr; + case EcsOpIPtr: return (double)*(const ecs_iptr_t*)ptr; + case EcsOpUPtr: return (double)*(const ecs_uptr_t*)ptr; + case EcsOpF32: return (double)*(const ecs_f32_t*)ptr; + case EcsOpF64: return *(const ecs_f64_t*)ptr; + case EcsOpString: return atof(*ECS_CONST_CAST(const char**, ptr)); + case EcsOpEnum: return *(const ecs_i32_t*)ptr; + case EcsOpBitmask: return *(const ecs_u32_t*)ptr; + case EcsOpEntity: + ecs_throw(ECS_INVALID_PARAMETER, + "invalid conversion from entity to float"); + break; + case EcsOpId: + ecs_throw(ECS_INVALID_PARAMETER, + "invalid conversion from id to float"); + break; + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpPrimitive: + ecs_throw(ECS_INVALID_PARAMETER, "invalid element for float"); + break; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; } +error: + return 0; } -void* ecs_table_cache_remove( - ecs_table_cache_t *cache, - uint64_t table_id, - ecs_table_cache_hdr_t *elem) +double ecs_meta_get_float( + const ecs_meta_cursor_t *cursor) { - ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - 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); - - flecs_table_cache_list_remove(cache, elem); - ecs_map_remove(&cache->index, table_id); - - return elem; + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + return flecs_meta_to_float(op->kind, ptr); } -bool ecs_table_cache_set_empty( - ecs_table_cache_t *cache, - const ecs_table_t *table, - bool empty) +/* Handler to get string from opaque (see ecs_meta_get_string below) */ +static int ecs_meta_get_string_value_from_opaque( + const struct ecs_serializer_t *ser, ecs_entity_t type, const void *value) { - ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_table_cache_hdr_t *elem = ecs_map_get_deref(&cache->index, - ecs_table_cache_hdr_t, table->id); - if (!elem) { - return false; - } - - if (elem->empty == empty) { - return false; + if(type != ecs_id(ecs_string_t)) { + ecs_err("Expected value call for opaque type to be a string"); + return -1; } - - flecs_table_cache_list_remove(cache, elem); - elem->empty = empty; - flecs_table_cache_list_insert(cache, elem); - - return true; + char*** ctx = (char ***) ser->ctx; + *ctx = ECS_CONST_CAST(char**, value); + return 0; } -bool flecs_table_cache_iter( - ecs_table_cache_t *cache, - ecs_table_cache_iter_t *out) +/* Handler to get string from opaque (see ecs_meta_get_string below) */ +static int ecs_meta_get_string_member_from_opaque( + const struct ecs_serializer_t* ser, const char* name) { - ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(out != NULL, ECS_INTERNAL_ERROR, NULL); - out->next = cache->tables.first; - out->next_list = NULL; - out->cur = NULL; - return out->next != NULL; + (void)ser; // silence unused warning + (void)name; // silence unused warning + ecs_err("Unexpected member call when serializing string from opaque"); + return -1; } -bool flecs_table_cache_empty_iter( - ecs_table_cache_t *cache, - ecs_table_cache_iter_t *out) +const char* ecs_meta_get_string( + const ecs_meta_cursor_t *cursor) { - ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(out != NULL, ECS_INTERNAL_ERROR, NULL); - out->next = cache->empty_tables.first; - out->next_list = NULL; - out->cur = NULL; - return out->next != NULL; + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch(op->kind) { + case EcsOpString: return *(const char**)ptr; + case EcsOpOpaque: { + /* If opaque type happens to map to a string, retrieve it. + Otherwise, fallback to default case (error). */ + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + if(opaque && opaque->as_type == ecs_id(ecs_string_t) && opaque->serialize) { + char** str = NULL; + ecs_serializer_t ser = { + .world = cursor->world, + .value = ecs_meta_get_string_value_from_opaque, + .member = ecs_meta_get_string_member_from_opaque, + .ctx = &str + }; + opaque->serialize(&ser, ptr); + if(str && *str) + return *str; + /* invalid string, so fall through */ + } + /* Not a compatible opaque type, so fall through */ + } + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpChar: + case EcsOpBool: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpEntity: + case EcsOpId: + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid element for string"); + break; + } +error: + return 0; } -bool flecs_table_cache_all_iter( - ecs_table_cache_t *cache, - ecs_table_cache_iter_t *out) +ecs_entity_t ecs_meta_get_entity( + const ecs_meta_cursor_t *cursor) { - ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(out != NULL, ECS_INTERNAL_ERROR, NULL); - out->next = cache->empty_tables.first; - out->next_list = cache->tables.first; - out->cur = NULL; - return out->next != NULL || out->next_list != NULL; + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch(op->kind) { + case EcsOpEntity: return *(ecs_entity_t*)ptr; + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpChar: + case EcsOpBool: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + case EcsOpId: + ecs_throw(ECS_INVALID_PARAMETER, "invalid element for entity"); + break; + } +error: + return 0; } -ecs_table_cache_hdr_t* flecs_table_cache_next_( - ecs_table_cache_iter_t *it) +ecs_entity_t ecs_meta_get_id( + const ecs_meta_cursor_t *cursor) { - ecs_table_cache_hdr_t *next = it->next; - if (!next) { - next = it->next_list; - it->next_list = NULL; - if (!next) { - return NULL; - } + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch(op->kind) { + case EcsOpEntity: return *(ecs_id_t*)ptr; /* Entities are valid ids */ + case EcsOpId: return *(ecs_id_t*)ptr; + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpChar: + case EcsOpBool: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + ecs_throw(ECS_INVALID_PARAMETER, "invalid element for entity"); + break; } +error: + return 0; +} - it->cur = next; - it->next = next->next; - return next; +double ecs_meta_ptr_to_float( + ecs_primitive_kind_t type_kind, + const void *ptr) +{ + ecs_meta_type_op_kind_t kind = flecs_meta_primitive_to_op_kind(type_kind); + return flecs_meta_to_float(kind, ptr); } +#endif + + /** - * @file table_graph.c - * @brief Data structure to speed up table transitions. - * - * The table graph is used to speed up finding tables in add/remove operations. - * For example, if component C is added to an entity in table [A, B], the entity - * must be moved to table [A, B, C]. The graph speeds this process up with an - * edge for component C that connects [A, B] to [A, B, C]. + * @file addons/meta/definitions.c + * @brief Reflection definitions for builtin types. */ -/* Id sequence (type) utilities */ +#ifdef FLECS_META +/* Opaque type serializatior addon vector */ static -uint64_t flecs_type_hash(const void *ptr) { - const ecs_type_t *type = ptr; - ecs_id_t *ids = type->array; - int32_t count = type->count; - return flecs_hash(ids, count * ECS_SIZEOF(ecs_id_t)); +int flecs_addon_vec_serialize(const ecs_serializer_t *ser, const void *ptr) { + char ***data = ECS_CONST_CAST(char***, ptr); + char **addons = data[0]; + do { + ser->value(ser, ecs_id(ecs_string_t), addons); + } while((++ addons)[0]); + return 0; } static -int flecs_type_compare(const void *ptr_1, const void *ptr_2) { - const ecs_type_t *type_1 = ptr_1; - const ecs_type_t *type_2 = ptr_2; - - int32_t count_1 = type_1->count; - int32_t count_2 = type_2->count; - - if (count_1 != count_2) { - return (count_1 > count_2) - (count_1 < count_2); - } - - const ecs_id_t *ids_1 = type_1->array; - const ecs_id_t *ids_2 = type_2->array; - int result = 0; - - int32_t i; - for (i = 0; !result && (i < count_1); i ++) { - ecs_id_t id_1 = ids_1[i]; - ecs_id_t id_2 = ids_2[i]; - result = (id_1 > id_2) - (id_1 < id_2); - } - - return result; -} - -void flecs_table_hashmap_init( - ecs_world_t *world, - ecs_hashmap_t *hm) -{ - flecs_hashmap_init(hm, ecs_type_t, ecs_table_t*, - flecs_type_hash, flecs_type_compare, &world->allocator); +size_t flecs_addon_vec_count(const void *ptr) { + int32_t count = 0; + char ***data = ECS_CONST_CAST(char***, ptr); + char **addons = data[0]; + do { + ++ count; + } while(addons[count]); + return flecs_ito(size_t, count); } -/* Find location where to insert id into type */ static -int flecs_type_find_insert( - const ecs_type_t *type, - int32_t offset, - ecs_id_t to_add) -{ - ecs_id_t *array = type->array; - int32_t i, count = type->count; - - for (i = offset; i < count; i ++) { - ecs_id_t id = array[i]; - if (id == to_add) { - return -1; - } - if (id > to_add) { - return i; - } - } - return i; +int flecs_const_str_serialize(const ecs_serializer_t *ser, const void *ptr) { + char **data = ECS_CONST_CAST(char**, ptr); + ser->value(ser, ecs_id(ecs_string_t), data); + return 0; } -/* Find location of id in type */ +/* Initialize reflection data for core components */ static -int flecs_type_find( - const ecs_type_t *type, - ecs_id_t id) +void flecs_meta_import_core_definitions( + ecs_world_t *world) { - ecs_id_t *array = type->array; - int32_t i, count = type->count; - - for (i = 0; i < count; i ++) { - ecs_id_t cur = array[i]; - if (ecs_id_match(cur, id)) { - return i; - } - if (cur > id) { - return -1; + ecs_struct(world, { + .entity = ecs_id(EcsComponent), + .members = { + { .name = "size", .type = ecs_id(ecs_i32_t) }, + { .name = "alignment", .type = ecs_id(ecs_i32_t) } } - } - - return -1; -} - -/* Count number of matching ids */ -static -int flecs_type_count_matches( - const ecs_type_t *type, - ecs_id_t wildcard, - int32_t offset) -{ - ecs_id_t *array = type->array; - int32_t i = offset, count = type->count; + }); - for (; i < count; i ++) { - ecs_id_t cur = array[i]; - if (!ecs_id_match(cur, wildcard)) { - break; + ecs_struct(world, { + .entity = ecs_id(EcsDefaultChildComponent), + .members = { + { .name = "component", .type = ecs_id(ecs_entity_t) } } - } - - return i - offset; -} - -/* Create type from source type with id */ -static -int flecs_type_new_with( - ecs_world_t *world, - ecs_type_t *dst, - const ecs_type_t *src, - ecs_id_t with) -{ - ecs_id_t *src_array = src->array; - int32_t at = flecs_type_find_insert(src, 0, with); - if (at == -1) { - return -1; - } - - int32_t dst_count = src->count + 1; - ecs_id_t *dst_array = flecs_walloc_n(world, ecs_id_t, dst_count); - dst->count = dst_count; - dst->array = dst_array; + }); - if (at) { - ecs_os_memcpy_n(dst_array, src_array, ecs_id_t, at); - } + /* Define const string as an opaque type that maps to string + This enables reflection for strings that are in .rodata, + (read-only) so that the meta add-on does not try to free them. + This opaque type defines how to serialize (read) the string, + but won't let users assign a new value. + */ + ecs_entity_t const_string = ecs_opaque(world, { + .entity = ecs_component(world, { + .entity = ecs_entity(world, { + .name = "flecs.core.const_string_t", + .root_sep = "" + }), + .type = { + .size = ECS_SIZEOF(const char*), + .alignment = ECS_ALIGNOF(const char*) + } + }), + .type = { + .as_type = ecs_id(ecs_string_t), + .serialize = flecs_const_str_serialize, + } + }); - int32_t remain = src->count - at; - if (remain) { - ecs_os_memcpy_n(&dst_array[at + 1], &src_array[at], ecs_id_t, remain); - } + ecs_entity_t string_vec = ecs_vector(world, { + .entity = ecs_entity(world, { + .name = "flecs.core.string_vec_t", + .root_sep = "" + }), + .type = ecs_id(ecs_string_t) + }); - dst_array[at] = with; + ecs_entity_t addon_vec = ecs_opaque(world, { + .entity = ecs_component(world, { + .entity = ecs_entity(world, { + .name = "flecs.core.addon_vec_t", + .root_sep = "" + }), + .type = { + .size = ECS_SIZEOF(char**), + .alignment = ECS_ALIGNOF(char**) + } + }), + .type = { + .as_type = string_vec, + .serialize = flecs_addon_vec_serialize, + .count = flecs_addon_vec_count, + } + }); - return 0; + ecs_struct(world, { + .entity = ecs_entity(world, { + .name = "flecs.core.build_info_t", + .root_sep = "" + }), + .members = { + { .name = "compiler", .type = const_string }, + { .name = "addons", .type = addon_vec }, + { .name = "version", .type = const_string }, + { .name = "version_major", .type = ecs_id(ecs_i16_t) }, + { .name = "version_minor", .type = ecs_id(ecs_i16_t) }, + { .name = "version_patch", .type = ecs_id(ecs_i16_t) }, + { .name = "debug", .type = ecs_id(ecs_bool_t) }, + { .name = "sanitize", .type = ecs_id(ecs_bool_t) }, + { .name = "perf_trace", .type = ecs_id(ecs_bool_t) } + } + }); } -/* Create type from source type without ids matching wildcard */ +/* Initialize reflection data for doc components */ static -int flecs_type_new_filtered( - ecs_world_t *world, - ecs_type_t *dst, - const ecs_type_t *src, - ecs_id_t wildcard, - int32_t at) +void flecs_meta_import_doc_definitions( + ecs_world_t *world) { - *dst = flecs_type_copy(world, src); - ecs_id_t *dst_array = dst->array; - ecs_id_t *src_array = src->array; - if (at) { - ecs_assert(dst_array != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_os_memcpy_n(dst_array, src_array, ecs_id_t, at); - } - - int32_t i = at + 1, w = at, count = src->count; - for (; i < count; i ++) { - ecs_id_t id = src_array[i]; - if (!ecs_id_match(id, wildcard)) { - dst_array[w] = id; - w ++; + (void)world; +#ifdef FLECS_DOC + ecs_struct(world, { + .entity = ecs_id(EcsDocDescription), + .members = { + { .name = "value", .type = ecs_id(ecs_string_t) } } - } - - dst->count = w; - if (w != count) { - dst->array = flecs_wrealloc_n(world, ecs_id_t, w, count, dst->array); - } - - return 0; + }); +#endif } -/* Create type from source type without id */ +/* Initialize reflection data for meta components */ static -int flecs_type_new_without( - ecs_world_t *world, - ecs_type_t *dst, - const ecs_type_t *src, - ecs_id_t without) +void flecs_meta_import_meta_definitions( + ecs_world_t *world) { - ecs_id_t *src_array = src->array; - int32_t count = 1, at = flecs_type_find(src, without); - if (at == -1) { - return -1; - } - - int32_t src_count = src->count; - if (src_count == 1) { - dst->array = NULL; - dst->count = 0; - return 0; - } - - if (ecs_id_is_wildcard(without)) { - if (ECS_IS_PAIR(without)) { - ecs_entity_t r = ECS_PAIR_FIRST(without); - ecs_entity_t o = ECS_PAIR_SECOND(without); - if (r == EcsWildcard && o != EcsWildcard) { - return flecs_type_new_filtered(world, dst, src, without, at); - } + ecs_entity_t type_kind = ecs_enum_init(world, &(ecs_enum_desc_t){ + .entity = ecs_entity(world, { .name = "TypeKind" }), + .constants = { + { .name = "PrimitiveType" }, + { .name = "BitmaskType" }, + { .name = "EnumType" }, + { .name = "StructType" }, + { .name = "ArrayType" }, + { .name = "VectorType" }, + { .name = "OpaqueType" } } - count += flecs_type_count_matches(src, without, at + 1); - } - - int32_t dst_count = src_count - count; - dst->count = dst_count; - if (!dst_count) { - dst->array = NULL; - return 0; - } - - ecs_id_t *dst_array = flecs_walloc_n(world, ecs_id_t, dst_count); - dst->array = dst_array; - - if (at) { - ecs_os_memcpy_n(dst_array, src_array, ecs_id_t, at); - } - - int32_t remain = dst_count - at; - if (remain) { - ecs_os_memcpy_n( - &dst_array[at], &src_array[at + count], ecs_id_t, remain); - } - - return 0; -} + }); -/* Copy type */ -ecs_type_t flecs_type_copy( - ecs_world_t *world, - const ecs_type_t *src) -{ - int32_t src_count = src->count; - if (!src_count) { - return (ecs_type_t){ 0 }; - } + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsType), + .members = { + { .name = "kind", .type = type_kind } + } + }); - ecs_id_t *ids = flecs_walloc_n(world, ecs_id_t, src_count); - ecs_os_memcpy_n(ids, src->array, ecs_id_t, src_count); - return (ecs_type_t) { - .array = ids, - .count = src_count - }; -} + ecs_entity_t primitive_kind = ecs_enum_init(world, &(ecs_enum_desc_t){ + .entity = ecs_entity(world, { .name = "PrimitiveKind" }), + .constants = { + { .name = "Bool", 1 }, + { .name = "Char" }, + { .name = "Byte" }, + { .name = "U8" }, + { .name = "U16" }, + { .name = "U32" }, + { .name = "U64 "}, + { .name = "I8" }, + { .name = "I16" }, + { .name = "I32" }, + { .name = "I64" }, + { .name = "F32" }, + { .name = "F64" }, + { .name = "UPtr "}, + { .name = "IPtr" }, + { .name = "String" }, + { .name = "Entity" }, + { .name = "Id" } + } + }); -/* Free type */ -void flecs_type_free( - ecs_world_t *world, - ecs_type_t *type) -{ - int32_t count = type->count; - if (count) { - flecs_wfree_n(world, ecs_id_t, type->count, type->array); - } -} + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsPrimitive), + .members = { + { .name = "kind", .type = primitive_kind } + } + }); -/* Add to type */ -static -void flecs_type_add( - ecs_world_t *world, - ecs_type_t *type, - ecs_id_t add) -{ - ecs_type_t new_type; - int res = flecs_type_new_with(world, &new_type, type, add); - if (res != -1) { - flecs_type_free(world, type); - type->array = new_type.array; - type->count = new_type.count; - } -} + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsMember), + .members = { + { .name = "type", .type = ecs_id(ecs_entity_t) }, + { .name = "count", .type = ecs_id(ecs_i32_t) }, + { .name = "unit", .type = ecs_id(ecs_entity_t) }, + { .name = "offset", .type = ecs_id(ecs_i32_t) }, + { .name = "use_offset", .type = ecs_id(ecs_bool_t) } + } + }); -/* Graph edge utilities */ + ecs_entity_t vr = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, { .name = "value_range" }), + .members = { + { .name = "min", .type = ecs_id(ecs_f64_t) }, + { .name = "max", .type = ecs_id(ecs_f64_t) } + } + }); -void flecs_table_diff_builder_init( - ecs_world_t *world, - ecs_table_diff_builder_t *builder) -{ - ecs_allocator_t *a = &world->allocator; - ecs_vec_init_t(a, &builder->added, ecs_id_t, 256); - ecs_vec_init_t(a, &builder->removed, ecs_id_t, 256); -} + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsMemberRanges), + .members = { + { .name = "value", .type = vr }, + { .name = "warning", .type = vr }, + { .name = "error", .type = vr } + } + }); -void flecs_table_diff_builder_fini( - ecs_world_t *world, - ecs_table_diff_builder_t *builder) -{ - ecs_allocator_t *a = &world->allocator; - ecs_vec_fini_t(a, &builder->added, ecs_id_t); - ecs_vec_fini_t(a, &builder->removed, ecs_id_t); -} + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsArray), + .members = { + { .name = "type", .type = ecs_id(ecs_entity_t) }, + { .name = "count", .type = ecs_id(ecs_i32_t) }, + } + }); -void flecs_table_diff_builder_clear( - ecs_table_diff_builder_t *builder) -{ - ecs_vec_clear(&builder->added); - ecs_vec_clear(&builder->removed); -} + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsVector), + .members = { + { .name = "type", .type = ecs_id(ecs_entity_t) } + } + }); -static -void flecs_table_diff_build_type( - ecs_world_t *world, - ecs_vec_t *vec, - ecs_type_t *type, - int32_t offset) -{ - int32_t count = vec->count - offset; - ecs_assert(count >= 0, ECS_INTERNAL_ERROR, NULL); - if (count) { - type->array = flecs_wdup_n(world, ecs_id_t, count, - ECS_ELEM_T(vec->array, ecs_id_t, offset)); - type->count = count; - ecs_vec_set_count_t(&world->allocator, vec, ecs_id_t, offset); - } -} + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsOpaque), + .members = { + { .name = "as_type", .type = ecs_id(ecs_entity_t) } + } + }); -void flecs_table_diff_build( - ecs_world_t *world, - ecs_table_diff_builder_t *builder, - ecs_table_diff_t *diff, - int32_t added_offset, - int32_t removed_offset) -{ - flecs_table_diff_build_type(world, &builder->added, &diff->added, - added_offset); - flecs_table_diff_build_type(world, &builder->removed, &diff->removed, - removed_offset); -} + ecs_entity_t ut = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, { .name = "unit_translation" }), + .members = { + { .name = "factor", .type = ecs_id(ecs_i32_t) }, + { .name = "power", .type = ecs_id(ecs_i32_t) } + } + }); -void flecs_table_diff_build_noalloc( - ecs_table_diff_builder_t *builder, - ecs_table_diff_t *diff) -{ - diff->added = (ecs_type_t){ - .array = builder->added.array, .count = builder->added.count }; - diff->removed = (ecs_type_t){ - .array = builder->removed.array, .count = builder->removed.count }; -} + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsUnit), + .members = { + { .name = "symbol", .type = ecs_id(ecs_string_t) }, + { .name = "prefix", .type = ecs_id(ecs_entity_t) }, + { .name = "base", .type = ecs_id(ecs_entity_t) }, + { .name = "over", .type = ecs_id(ecs_entity_t) }, + { .name = "translation", .type = ut } + } + }); -static -void flecs_table_diff_build_add_type_to_vec( - ecs_world_t *world, - ecs_vec_t *vec, - ecs_type_t *add) -{ - if (!add || !add->count) { - return; - } + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsUnitPrefix), + .members = { + { .name = "symbol", .type = ecs_id(ecs_string_t) }, + { .name = "translation", .type = ut } + } + }); - int32_t offset = vec->count; - ecs_vec_grow_t(&world->allocator, vec, ecs_id_t, add->count); - ecs_os_memcpy_n(ecs_vec_get_t(vec, ecs_id_t, offset), - add->array, ecs_id_t, add->count); -} + /* Meta doc definitions */ +#ifdef FLECS_DOC + ecs_entity_t meta = ecs_lookup(world, "flecs.meta"); + ecs_doc_set_brief(world, meta, "Flecs module with reflection components"); -void flecs_table_diff_build_append_table( - ecs_world_t *world, - ecs_table_diff_builder_t *dst, - ecs_table_diff_t *src) -{ - flecs_table_diff_build_add_type_to_vec(world, &dst->added, &src->added); - flecs_table_diff_build_add_type_to_vec(world, &dst->removed, &src->removed); -} + ecs_doc_set_brief(world, ecs_id(EcsType), "Component added to types"); + ecs_doc_set_brief(world, ecs_id(EcsTypeSerializer), "Component that stores reflection data in an optimized format"); + ecs_doc_set_brief(world, ecs_id(EcsPrimitive), "Component added to primitive types"); + ecs_doc_set_brief(world, ecs_id(EcsEnum), "Component added to enumeration types"); + ecs_doc_set_brief(world, ecs_id(EcsBitmask), "Component added to bitmask types"); + ecs_doc_set_brief(world, ecs_id(EcsMember), "Component added to struct members"); + ecs_doc_set_brief(world, ecs_id(EcsStruct), "Component added to struct types"); + ecs_doc_set_brief(world, ecs_id(EcsArray), "Component added to array types"); + ecs_doc_set_brief(world, ecs_id(EcsVector), "Component added to vector types"); -static -void flecs_table_diff_free( - ecs_world_t *world, - ecs_table_diff_t *diff) -{ - flecs_wfree_n(world, ecs_id_t, diff->added.count, diff->added.array); - flecs_wfree_n(world, ecs_id_t, diff->removed.count, diff->removed.array); - flecs_bfree(&world->allocators.table_diff, diff); + ecs_doc_set_brief(world, ecs_id(ecs_bool_t), "bool component"); + ecs_doc_set_brief(world, ecs_id(ecs_char_t), "char component"); + ecs_doc_set_brief(world, ecs_id(ecs_byte_t), "byte component"); + ecs_doc_set_brief(world, ecs_id(ecs_u8_t), "8 bit unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_u16_t), "16 bit unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_u32_t), "32 bit unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_u64_t), "64 bit unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_uptr_t), "word sized unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_i8_t), "8 bit signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_i16_t), "16 bit signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_i32_t), "32 bit signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_i64_t), "64 bit signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_iptr_t), "word sized signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_f32_t), "32 bit floating point component"); + ecs_doc_set_brief(world, ecs_id(ecs_f64_t), "64 bit floating point component"); + ecs_doc_set_brief(world, ecs_id(ecs_string_t), "string component"); + ecs_doc_set_brief(world, ecs_id(ecs_entity_t), "entity component"); +#endif } -static -ecs_graph_edge_t* flecs_table_ensure_hi_edge( - ecs_world_t *world, - ecs_graph_edges_t *edges, - ecs_id_t id) +void flecs_meta_import_definitions( + ecs_world_t *world) { - if (!edges->hi) { - edges->hi = flecs_alloc_t(&world->allocator, ecs_map_t); - ecs_map_init_w_params(edges->hi, &world->allocators.ptr); - } - - ecs_graph_edge_t **r = ecs_map_ensure_ref(edges->hi, ecs_graph_edge_t, id); - ecs_graph_edge_t *edge = r[0]; - if (edge) { - return edge; - } - - if (id < FLECS_HI_COMPONENT_ID) { - edge = &edges->lo[id]; - } else { - edge = flecs_bcalloc(&world->allocators.graph_edge); - } - - r[0] = edge; - return edge; + flecs_meta_import_core_definitions(world); + flecs_meta_import_doc_definitions(world); + flecs_meta_import_meta_definitions(world); } -static -ecs_graph_edge_t* flecs_table_ensure_edge( - ecs_world_t *world, - ecs_graph_edges_t *edges, - ecs_id_t id) -{ - ecs_graph_edge_t *edge; - - if (id < FLECS_HI_COMPONENT_ID) { - if (!edges->lo) { - edges->lo = flecs_bcalloc(&world->allocators.graph_edge_lo); - } - edge = &edges->lo[id]; - } else { - edge = flecs_table_ensure_hi_edge(world, edges, id); - } +#endif - return edge; -} +/** + * @file addons/meta/meta.c + * @brief Meta addon. + */ -static -void flecs_table_disconnect_edge( - ecs_world_t *world, - ecs_id_t id, - ecs_graph_edge_t *edge) -{ - ecs_assert(edge != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edge->id == id, ECS_INTERNAL_ERROR, NULL); - (void)id; - /* Remove backref from destination table */ - ecs_graph_edge_hdr_t *next = edge->hdr.next; - ecs_graph_edge_hdr_t *prev = edge->hdr.prev; +#ifdef FLECS_META - if (next) { - next->prev = prev; - } - if (prev) { - prev->next = next; - } +/* ecs_string_t lifecycle */ - /* Remove data associated with edge */ - ecs_table_diff_t *diff = edge->diff; - if (diff) { - flecs_table_diff_free(world, diff); - } +static ECS_COPY(ecs_string_t, dst, src, { + ecs_os_free(*(ecs_string_t*)dst); + *(ecs_string_t*)dst = ecs_os_strdup(*(const ecs_string_t*)src); +}) - /* If edge id is low, clear it from fast lookup array */ - if (id < FLECS_HI_COMPONENT_ID) { - ecs_os_memset_t(edge, 0, ecs_graph_edge_t); - } else { - flecs_bfree(&world->allocators.graph_edge, edge); - } -} +static ECS_MOVE(ecs_string_t, dst, src, { + ecs_os_free(*(ecs_string_t*)dst); + *(ecs_string_t*)dst = *(ecs_string_t*)src; + *(ecs_string_t*)src = NULL; +}) -static -void flecs_table_remove_edge( - ecs_world_t *world, - ecs_graph_edges_t *edges, - ecs_id_t id, - ecs_graph_edge_t *edge) -{ - ecs_assert(edges != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edges->hi != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_table_disconnect_edge(world, id, edge); - ecs_map_remove(edges->hi, id); -} +static ECS_DTOR(ecs_string_t, ptr, { + ecs_os_free(*(ecs_string_t*)ptr); + *(ecs_string_t*)ptr = NULL; +}) -static -void flecs_table_init_edges( - ecs_graph_edges_t *edges) -{ - edges->lo = NULL; - edges->hi = NULL; -} -static -void flecs_table_init_node( - ecs_graph_node_t *node) -{ - flecs_table_init_edges(&node->add); - flecs_table_init_edges(&node->remove); -} +/* EcsTypeSerializer lifecycle */ -bool flecs_table_records_update_empty( - ecs_table_t *table) +void ecs_meta_dtor_serialized( + EcsTypeSerializer *ptr) { - bool result = false; - bool is_empty = ecs_table_count(table) == 0; - - int32_t i, count = table->_->record_count; + int32_t i, count = ecs_vec_count(&ptr->ops); + ecs_meta_type_op_t *ops = ecs_vec_first(&ptr->ops); + for (i = 0; i < count; i ++) { - ecs_table_record_t *tr = &table->_->records[i]; - ecs_table_cache_t *cache = tr->hdr.cache; - result |= ecs_table_cache_set_empty(cache, table, is_empty); + ecs_meta_type_op_t *op = &ops[i]; + if (op->members) { + flecs_name_index_free(op->members); + } } - return result; + ecs_vec_fini_t(NULL, &ptr->ops, ecs_meta_type_op_t); } -static -void flecs_init_table( - ecs_world_t *world, - ecs_table_t *table, - ecs_table_t *prev) -{ - table->flags = 0; - table->dirty_state = NULL; - table->_->lock = 0; - table->_->generation = 0; - - flecs_table_init_node(&table->node); +static ECS_COPY(EcsTypeSerializer, dst, src, { + ecs_meta_dtor_serialized(dst); - flecs_table_init(world, table, prev); -} + dst->ops = ecs_vec_copy_t(NULL, &src->ops, ecs_meta_type_op_t); -static -ecs_table_t *flecs_create_table( - ecs_world_t *world, - ecs_type_t *type, - flecs_hashmap_result_t table_elem, - ecs_table_t *prev) -{ - ecs_table_t *result = flecs_sparse_add_t(&world->store.tables, ecs_table_t); - ecs_assert(result != NULL, ECS_INTERNAL_ERROR, NULL); - result->_ = flecs_calloc_t(&world->allocator, ecs_table__t); - ecs_assert(result->_ != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t o, count = ecs_vec_count(&dst->ops); + ecs_meta_type_op_t *ops = ecs_vec_first_t(&dst->ops, ecs_meta_type_op_t); - result->id = flecs_sparse_last_id(&world->store.tables); - result->type = *type; - - if (ecs_should_log_2()) { - char *expr = ecs_type_str(world, &result->type); - ecs_dbg_2( - "#[green]table#[normal] [%s] #[green]created#[reset] with id %d", - expr, result->id); - ecs_os_free(expr); + for (o = 0; o < count; o ++) { + ecs_meta_type_op_t *op = &ops[o]; + if (op->members) { + op->members = flecs_name_index_copy(op->members); + } } +}) - ecs_log_push_2(); +static ECS_MOVE(EcsTypeSerializer, dst, src, { + ecs_meta_dtor_serialized(dst); + dst->ops = src->ops; + src->ops = (ecs_vec_t){0}; +}) - /* Store table in table hashmap */ - *(ecs_table_t**)table_elem.value = result; +static ECS_DTOR(EcsTypeSerializer, ptr, { + ecs_meta_dtor_serialized(ptr); +}) - /* Set keyvalue to one that has the same lifecycle as the table */ - *(ecs_type_t*)table_elem.key = result->type; - result->_->hash = table_elem.hash; - flecs_init_table(world, result, prev); +/* EcsStruct lifecycle */ - /* Update counters */ - world->info.table_count ++; - world->info.empty_table_count ++; - world->info.table_create_total ++; +static void flecs_struct_dtor( + EcsStruct *ptr) +{ + ecs_member_t *members = ecs_vec_first_t(&ptr->members, ecs_member_t); + int32_t i, count = ecs_vec_count(&ptr->members); + for (i = 0; i < count; i ++) { + ecs_os_free(ECS_CONST_CAST(char*, members[i].name)); + } + ecs_vec_fini_t(NULL, &ptr->members, ecs_member_t); +} - ecs_log_pop_2(); +static ECS_COPY(EcsStruct, dst, src, { + flecs_struct_dtor(dst); - return result; -} + dst->members = ecs_vec_copy_t(NULL, &src->members, ecs_member_t); -static -ecs_table_t* flecs_table_ensure( - ecs_world_t *world, - ecs_type_t *type, - bool own_type, - ecs_table_t *prev) -{ - ecs_poly_assert(world, ecs_world_t); + ecs_member_t *members = ecs_vec_first_t(&dst->members, ecs_member_t); + int32_t m, count = ecs_vec_count(&dst->members); - int32_t id_count = type->count; - if (!id_count) { - return &world->store.root; + for (m = 0; m < count; m ++) { + members[m].name = ecs_os_strdup(members[m].name); } +}) - ecs_table_t *table; - flecs_hashmap_result_t elem = flecs_hashmap_ensure( - &world->store.table_map, type, ecs_table_t*); - if ((table = *(ecs_table_t**)elem.value)) { - if (own_type) { - flecs_type_free(world, type); - } - return table; - } +static ECS_MOVE(EcsStruct, dst, src, { + flecs_struct_dtor(dst); + dst->members = src->members; + src->members = (ecs_vec_t){0}; +}) - /* If we get here, table needs to be created which is only allowed when the - * application is not currently in progress */ - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); +static ECS_DTOR(EcsStruct, ptr, { flecs_struct_dtor(ptr); }) - /* If we get here, the table has not been found, so create it. */ - if (own_type) { - return flecs_create_table(world, type, elem, prev); - } - ecs_type_t copy = flecs_type_copy(world, type); - return flecs_create_table(world, ©, elem, prev); -} +/* EcsEnum lifecycle */ -static -void flecs_diff_insert_added( - ecs_world_t *world, - ecs_table_diff_builder_t *diff, - ecs_id_t id) +static void flecs_constants_dtor( + ecs_map_t *constants) { - ecs_vec_append_t(&world->allocator, &diff->added, ecs_id_t)[0] = id; + ecs_map_iter_t it = ecs_map_iter(constants); + while (ecs_map_next(&it)) { + ecs_enum_constant_t *c = ecs_map_ptr(&it); + ecs_os_free(ECS_CONST_CAST(char*, c->name)); + ecs_os_free(c); + } + ecs_map_fini(constants); } -static -void flecs_diff_insert_removed( - ecs_world_t *world, - ecs_table_diff_builder_t *diff, - ecs_id_t id) +static void flecs_constants_copy( + ecs_map_t *dst, + const ecs_map_t *src) { - ecs_allocator_t *a = &world->allocator; - ecs_vec_append_t(a, &diff->removed, ecs_id_t)[0] = id; -} + ecs_map_copy(dst, src); -static -void flecs_compute_table_diff( - ecs_world_t *world, - ecs_table_t *node, - ecs_table_t *next, - ecs_graph_edge_t *edge, - ecs_id_t id) -{ - if (ECS_IS_PAIR(id)) { - ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair( - ECS_PAIR_FIRST(id), EcsWildcard)); - if (idr->flags & EcsIdUnion) { - if (node != next) { - id = ecs_pair(EcsUnion, ECS_PAIR_FIRST(id)); - } else { - ecs_table_diff_t *diff = flecs_bcalloc( - &world->allocators.table_diff); - diff->added.count = 1; - diff->added.array = flecs_wdup_n(world, ecs_id_t, 1, &id); - edge->diff = diff; - return; - } - } + ecs_map_iter_t it = ecs_map_iter(dst); + while (ecs_map_next(&it)) { + ecs_enum_constant_t **r = ecs_map_ref(&it, ecs_enum_constant_t); + ecs_enum_constant_t *src_c = r[0]; + ecs_enum_constant_t *dst_c = ecs_os_calloc_t(ecs_enum_constant_t); + *dst_c = *src_c; + dst_c->name = ecs_os_strdup(dst_c->name); + r[0] = dst_c; } +} - ecs_type_t node_type = node->type; - ecs_type_t next_type = next->type; +static ECS_COPY(EcsEnum, dst, src, { + flecs_constants_dtor(&dst->constants); + flecs_constants_copy(&dst->constants, &src->constants); +}) - ecs_id_t *ids_node = node_type.array; - ecs_id_t *ids_next = next_type.array; - int32_t i_node = 0, node_count = node_type.count; - int32_t i_next = 0, next_count = next_type.count; - int32_t added_count = 0; - int32_t removed_count = 0; - bool trivial_edge = !ECS_HAS_RELATION(id, EcsIsA); +static ECS_MOVE(EcsEnum, dst, src, { + flecs_constants_dtor(&dst->constants); + dst->constants = src->constants; + ecs_os_zeromem(&src->constants); +}) - /* First do a scan to see how big the diff is, so we don't have to realloc - * or alloc more memory than required. */ - for (; i_node < node_count && i_next < next_count; ) { - ecs_id_t id_node = ids_node[i_node]; - ecs_id_t id_next = ids_next[i_next]; +static ECS_DTOR(EcsEnum, ptr, { flecs_constants_dtor(&ptr->constants); }) - bool added = id_next < id_node; - bool removed = id_node < id_next; - trivial_edge &= !added || id_next == id; - trivial_edge &= !removed || id_node == id; +/* EcsBitmask lifecycle */ - added_count += added; - removed_count += removed; +static ECS_COPY(EcsBitmask, dst, src, { + /* bitmask constant & enum constant have the same layout */ + flecs_constants_dtor(&dst->constants); + flecs_constants_copy(&dst->constants, &src->constants); +}) - i_node += id_node <= id_next; - i_next += id_next <= id_node; - } +static ECS_MOVE(EcsBitmask, dst, src, { + flecs_constants_dtor(&dst->constants); + dst->constants = src->constants; + ecs_os_zeromem(&src->constants); +}) - added_count += next_count - i_next; - removed_count += node_count - i_node; +static ECS_DTOR(EcsBitmask, ptr, { flecs_constants_dtor(&ptr->constants); }) - trivial_edge &= (added_count + removed_count) <= 1 && - !ecs_id_is_wildcard(id); - if (trivial_edge) { - /* If edge is trivial there's no need to create a diff element for it */ - return; - } +/* EcsUnit lifecycle */ - ecs_table_diff_builder_t *builder = &world->allocators.diff_builder; - int32_t added_offset = builder->added.count; - int32_t removed_offset = builder->removed.count; +static void dtor_unit( + EcsUnit *ptr) +{ + ecs_os_free(ptr->symbol); +} - for (i_node = 0, i_next = 0; i_node < node_count && i_next < next_count; ) { - ecs_id_t id_node = ids_node[i_node]; - ecs_id_t id_next = ids_next[i_next]; +static ECS_COPY(EcsUnit, dst, src, { + dtor_unit(dst); + dst->symbol = ecs_os_strdup(src->symbol); + dst->base = src->base; + dst->over = src->over; + dst->prefix = src->prefix; + dst->translation = src->translation; +}) - if (id_next < id_node) { - flecs_diff_insert_added(world, builder, id_next); - } else if (id_node < id_next) { - flecs_diff_insert_removed(world, builder, id_node); - } +static ECS_MOVE(EcsUnit, dst, src, { + dtor_unit(dst); + dst->symbol = src->symbol; + dst->base = src->base; + dst->over = src->over; + dst->prefix = src->prefix; + dst->translation = src->translation; - i_node += id_node <= id_next; - i_next += id_next <= id_node; - } + src->symbol = NULL; + src->base = 0; + src->over = 0; + src->prefix = 0; + src->translation = (ecs_unit_translation_t){0}; +}) - for (; i_next < next_count; i_next ++) { - flecs_diff_insert_added(world, builder, ids_next[i_next]); - } - for (; i_node < node_count; i_node ++) { - flecs_diff_insert_removed(world, builder, ids_node[i_node]); - } +static ECS_DTOR(EcsUnit, ptr, { dtor_unit(ptr); }) - ecs_table_diff_t *diff = flecs_bcalloc(&world->allocators.table_diff); - edge->diff = diff; - flecs_table_diff_build(world, builder, diff, added_offset, removed_offset); - ecs_assert(diff->added.count == added_count, ECS_INTERNAL_ERROR, NULL); - ecs_assert(diff->removed.count == removed_count, ECS_INTERNAL_ERROR, NULL); -} +/* EcsUnitPrefix lifecycle */ -static -void flecs_add_overrides_for_base( - ecs_world_t *world, - ecs_type_t *dst_type, - ecs_id_t pair) +static void dtor_unit_prefix( + EcsUnitPrefix *ptr) { - ecs_entity_t base = ecs_pair_second(world, pair); - ecs_assert(base != 0, ECS_INTERNAL_ERROR, NULL); - ecs_table_t *base_table = ecs_get_table(world, base); - if (!base_table) { - return; - } - - ecs_id_t *ids = base_table->type.array; - - ecs_flags32_t flags = base_table->flags; - if (flags & EcsTableHasOverrides) { - int32_t i, count = base_table->type.count; - for (i = 0; i < count; i ++) { - ecs_id_t id = ids[i]; - ecs_id_t to_add = 0; - if (ECS_HAS_ID_FLAG(id, OVERRIDE)) { - to_add = id & ~ECS_OVERRIDE; - } else { - ecs_table_record_t *tr = &base_table->_->records[i]; - ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; - if (idr->flags & EcsIdAlwaysOverride) { - to_add = id; - } - } - if (to_add) { - ecs_id_t wc = ecs_pair(ECS_PAIR_FIRST(to_add), EcsWildcard); - bool exclusive = false; - if (ECS_IS_PAIR(to_add)) { - ecs_id_record_t *idr = flecs_id_record_get(world, wc); - if (idr) { - exclusive = (idr->flags & EcsIdExclusive) != 0; - } - } - if (!exclusive) { - flecs_type_add(world, dst_type, to_add); - } else { - int32_t column = flecs_type_find(dst_type, wc); - if (column == -1) { - flecs_type_add(world, dst_type, to_add); - } else { - dst_type->array[column] = to_add; - } - } - } - } - } - - if (flags & EcsTableHasIsA) { - ecs_table_record_t *tr = flecs_id_record_get_table( - world->idr_isa_wildcard, base_table); - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t i = tr->index, end = i + tr->count; - for (; i != end; i ++) { - flecs_add_overrides_for_base(world, dst_type, ids[i]); - } - } + ecs_os_free(ptr->symbol); } -static -void flecs_add_with_property( - ecs_world_t *world, - ecs_id_record_t *idr_with_wildcard, - ecs_type_t *dst_type, - ecs_entity_t r, - ecs_entity_t o) -{ - r = ecs_get_alive(world, r); +static ECS_COPY(EcsUnitPrefix, dst, src, { + dtor_unit_prefix(dst); + dst->symbol = ecs_os_strdup(src->symbol); + dst->translation = src->translation; +}) - /* Check if component/relationship has With pairs, which contain ids - * that need to be added to the table. */ - ecs_table_t *table = ecs_get_table(world, r); - if (!table) { - return; - } - - ecs_table_record_t *tr = flecs_id_record_get_table( - idr_with_wildcard, table); - if (tr) { - int32_t i = tr->index, end = i + tr->count; - ecs_id_t *ids = table->type.array; +static ECS_MOVE(EcsUnitPrefix, dst, src, { + dtor_unit_prefix(dst); + dst->symbol = src->symbol; + dst->translation = src->translation; + + src->symbol = NULL; + src->translation = (ecs_unit_translation_t){0}; +}) - for (; i < end; i ++) { - ecs_id_t id = ids[i]; - ecs_assert(ECS_PAIR_FIRST(id) == EcsWith, ECS_INTERNAL_ERROR, NULL); - ecs_id_t ra = ECS_PAIR_SECOND(id); - ecs_id_t a = ra; - if (o) { - a = ecs_pair(ra, o); - } +static ECS_DTOR(EcsUnitPrefix, ptr, { dtor_unit_prefix(ptr); }) - flecs_type_add(world, dst_type, a); - flecs_add_with_property(world, idr_with_wildcard, dst_type, ra, o); - } - } +/* Type initialization */ +static +const char* flecs_type_kind_str( + ecs_type_kind_t kind) +{ + switch(kind) { + case EcsPrimitiveType: return "Primitive"; + case EcsBitmaskType: return "Bitmask"; + case EcsEnumType: return "Enum"; + case EcsStructType: return "Struct"; + case EcsArrayType: return "Array"; + case EcsVectorType: return "Vector"; + case EcsOpaqueType: return "Opaque"; + default: return "unknown"; + } } static -ecs_table_t* flecs_find_table_with( +int flecs_init_type( ecs_world_t *world, - ecs_table_t *node, - ecs_id_t with) -{ - ecs_ensure_id(world, with); - - ecs_id_record_t *idr = NULL; - ecs_entity_t r = 0, o = 0; + ecs_entity_t type, + ecs_type_kind_t kind, + ecs_size_t size, + ecs_size_t alignment) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type != 0, ECS_INTERNAL_ERROR, NULL); - if (ECS_IS_PAIR(with)) { - r = ECS_PAIR_FIRST(with); - o = ECS_PAIR_SECOND(with); - idr = flecs_id_record_ensure(world, ecs_pair(r, EcsWildcard)); - if (idr->flags & EcsIdUnion) { - ecs_type_t dst_type; - ecs_id_t union_id = ecs_pair(EcsUnion, r); - int res = flecs_type_new_with( - world, &dst_type, &node->type, union_id); - if (res == -1) { - return node; - } + EcsType *meta_type = ecs_ensure(world, type, EcsType); + if (meta_type->kind == 0) { + /* Determine if this is an existing type or a reflection-defined type (runtime type) */ + meta_type->existing = ecs_has(world, type, EcsComponent); - return flecs_table_ensure(world, &dst_type, true, node); - } else if (idr->flags & EcsIdExclusive) { - /* Relationship is exclusive, check if table already has it */ - ecs_table_record_t *tr = flecs_id_record_get_table(idr, node); - if (tr) { - /* Table already has an instance of the relationship, create - * a new id sequence with the existing id replaced */ - ecs_type_t dst_type = flecs_type_copy(world, &node->type); - ecs_assert(dst_type.array != NULL, ECS_INTERNAL_ERROR, NULL); - dst_type.array[tr->index] = with; - return flecs_table_ensure(world, &dst_type, true, node); - } - } + /* For existing types, ensure that component has a default constructor, to prevent crashing + * serializers on uninitialized values. For runtime types (rtt), the default hooks are set + by flecs_meta_rtt_init_default_hooks */ + ecs_type_info_t *ti = flecs_type_info_ensure(world, type); + if (meta_type->existing && !ti->hooks.ctor) { + ti->hooks.ctor = flecs_default_ctor; + } } else { - idr = flecs_id_record_ensure(world, with); - r = with; - } - - /* Create sequence with new id */ - ecs_type_t dst_type; - int res = flecs_type_new_with(world, &dst_type, &node->type, with); - if (res == -1) { - return node; /* Current table already has id */ + if (meta_type->kind != kind) { + ecs_err("type '%s' reregistered as '%s' (was '%s')", + ecs_get_name(world, type), + flecs_type_kind_str(kind), + flecs_type_kind_str(meta_type->kind)); + return -1; + } } - if (r == EcsIsA) { - /* If adding a prefab, check if prefab has overrides */ - flecs_add_overrides_for_base(world, &dst_type, with); - } else if (r == EcsChildOf) { - o = ecs_get_alive(world, o); - if (ecs_has_id(world, o, EcsPrefab)) { - flecs_type_add(world, &dst_type, EcsPrefab); + if (!meta_type->existing) { + EcsComponent *comp = ecs_ensure(world, type, EcsComponent); + comp->size = size; + comp->alignment = alignment; + ecs_modified(world, type, EcsComponent); + } else { + const EcsComponent *comp = ecs_get(world, type, EcsComponent); + if (comp->size < size) { + ecs_err("computed size (%d) for '%s' is larger than actual type (%d)", + size, ecs_get_name(world, type), comp->size); + return -1; + } + if (comp->alignment < alignment) { + ecs_err("computed alignment (%d) for '%s' is larger than actual type (%d)", + alignment, ecs_get_name(world, type), comp->alignment); + return -1; + } + if (comp->size == size && comp->alignment != alignment) { + if (comp->alignment < alignment) { + ecs_err("computed size for '%s' matches with actual type but " + "alignment is different (%d vs. %d)", ecs_get_name(world, type), + alignment, comp->alignment); + } } + + meta_type->partial = comp->size != size; } - if (idr->flags & EcsIdWith) { - ecs_id_record_t *idr_with_wildcard = flecs_id_record_get(world, - ecs_pair(EcsWith, EcsWildcard)); - /* If id has With property, add targets to type */ - flecs_add_with_property(world, idr_with_wildcard, &dst_type, r, o); - } + meta_type->kind = kind; + ecs_modified(world, type, EcsType); - return flecs_table_ensure(world, &dst_type, true, node); + return 0; } +#define init_type_t(world, type, kind, T) \ + flecs_init_type(world, type, kind, ECS_SIZEOF(T), ECS_ALIGNOF(T)) + static -ecs_table_t* flecs_find_table_without( - ecs_world_t *world, - ecs_table_t *node, - ecs_id_t without) +void flecs_set_struct_member( + ecs_member_t *member, + ecs_entity_t entity, + const char *name, + ecs_entity_t type, + int32_t count, + int32_t offset, + ecs_entity_t unit, + EcsMemberRanges *ranges) { - if (ECS_IS_PAIR(without)) { - ecs_entity_t r = 0; - ecs_id_record_t *idr = NULL; - r = ECS_PAIR_FIRST(without); - idr = flecs_id_record_get(world, ecs_pair(r, EcsWildcard)); - if (idr && idr->flags & EcsIdUnion) { - without = ecs_pair(EcsUnion, r); - } - } + member->member = entity; + member->type = type; + member->count = count; + member->unit = unit; + member->offset = offset; - /* Create sequence with new id */ - ecs_type_t dst_type; - int res = flecs_type_new_without(world, &dst_type, &node->type, without); - if (res == -1) { - return node; /* Current table does not have id */ + if (!count) { + member->count = 1; } - return flecs_table_ensure(world, &dst_type, true, node); -} + ecs_os_strset(ECS_CONST_CAST(char**, &member->name), name); -static -void flecs_table_init_edge( - ecs_table_t *table, - ecs_graph_edge_t *edge, - ecs_id_t id, - ecs_table_t *to) -{ - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edge != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edge->id == 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edge->hdr.next == NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edge->hdr.prev == NULL, ECS_INTERNAL_ERROR, NULL); - - edge->from = table; - edge->to = to; - edge->id = id; + if (ranges) { + member->range = ranges->value; + member->error_range = ranges->error; + member->warning_range = ranges->warning; + } else { + ecs_os_zeromem(&member->range); + ecs_os_zeromem(&member->error_range); + ecs_os_zeromem(&member->warning_range); + } } static -void flecs_init_edge_for_add( +int flecs_add_member_to_struct( ecs_world_t *world, - ecs_table_t *table, - ecs_graph_edge_t *edge, - ecs_id_t id, - ecs_table_t *to) + ecs_entity_t type, + ecs_entity_t member, + EcsMember *m, + EcsMemberRanges *ranges) { - flecs_table_init_edge(table, edge, id, to); + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(member != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(m != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_table_ensure_hi_edge(world, &table->node.add, id); + const char *name = ecs_get_name(world, member); + if (!name) { + char *path = ecs_get_path(world, type); + ecs_err("member for struct '%s' does not have a name", path); + ecs_os_free(path); + return -1; + } - if (table != to || table->flags & EcsTableHasUnion) { - /* Add edges are appended to refs.next */ - ecs_graph_edge_hdr_t *to_refs = &to->node.refs; - ecs_graph_edge_hdr_t *next = to_refs->next; - - to_refs->next = &edge->hdr; - edge->hdr.prev = to_refs; + if (!m->type) { + char *path = ecs_get_path(world, member); + ecs_err("member '%s' does not have a type", path); + ecs_os_free(path); + return -1; + } - edge->hdr.next = next; - if (next) { - next->prev = &edge->hdr; + if (ecs_get_typeid(world, m->type) == 0) { + char *path = ecs_get_path(world, member); + char *ent_path = ecs_get_path(world, m->type); + ecs_err("member '%s.type' is '%s' which is not a type", path, ent_path); + ecs_os_free(path); + ecs_os_free(ent_path); + return -1; + } + + ecs_entity_t unit = m->unit; + if (unit) { + if (!ecs_has(world, unit, EcsUnit)) { + ecs_err("entity '%s' for member '%s' is not a unit", + ecs_get_name(world, unit), name); + return -1; } - flecs_compute_table_diff(world, table, to, edge, id); + if (ecs_has(world, m->type, EcsUnit) && m->type != unit) { + ecs_err("unit mismatch for type '%s' and unit '%s' for member '%s'", + ecs_get_name(world, m->type), ecs_get_name(world, unit), name); + return -1; + } + } else { + if (ecs_has(world, m->type, EcsUnit)) { + ecs_entity_t unit_base = ecs_get_target_for( + world, m->type, EcsIsA, EcsUnit); + if (unit_base) { + unit = m->unit = unit_base; + } else { + unit = m->unit = m->type; + } + } } -} -static -void flecs_init_edge_for_remove( - ecs_world_t *world, - ecs_table_t *table, - ecs_graph_edge_t *edge, - ecs_id_t id, - ecs_table_t *to) -{ - flecs_table_init_edge(table, edge, id, to); + EcsStruct *s = ecs_ensure(world, type, EcsStruct); + ecs_assert(s != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_table_ensure_hi_edge(world, &table->node.remove, id); + /* First check if member is already added to struct */ + ecs_member_t *members = ecs_vec_first_t(&s->members, ecs_member_t); + int32_t i, count = ecs_vec_count(&s->members); + for (i = 0; i < count; i ++) { + if (members[i].member == member) { + flecs_set_struct_member(&members[i], member, name, m->type, + m->count, m->offset, unit, ranges); + break; + } + } - if (table != to) { - /* Remove edges are appended to refs.prev */ - ecs_graph_edge_hdr_t *to_refs = &to->node.refs; - ecs_graph_edge_hdr_t *prev = to_refs->prev; + /* If member wasn't added yet, add a new element to vector */ + if (i == count) { + ecs_vec_init_if_t(&s->members, ecs_member_t); + ecs_member_t *elem = ecs_vec_append_t(NULL, &s->members, ecs_member_t); + elem->name = NULL; + flecs_set_struct_member(elem, member, name, m->type, + m->count, m->offset, unit, ranges); - to_refs->prev = &edge->hdr; - edge->hdr.next = to_refs; + /* Reobtain members array in case it was reallocated */ + members = ecs_vec_first_t(&s->members, ecs_member_t); + count ++; + } - edge->hdr.prev = prev; - if (prev) { - prev->next = &edge->hdr; - } + bool explicit_offset = m->offset || m->use_offset; - flecs_compute_table_diff(world, table, to, edge, id); - } -} + /* Compute member offsets and size & alignment of struct */ + ecs_size_t size = 0; + ecs_size_t alignment = 0; -static -ecs_table_t* flecs_create_edge_for_remove( - ecs_world_t *world, - ecs_table_t *node, - ecs_graph_edge_t *edge, - ecs_id_t id) -{ - ecs_table_t *to = flecs_find_table_without(world, node, id); - flecs_init_edge_for_remove(world, node, edge, id, to); - return to; -} + if (!explicit_offset) { + for (i = 0; i < count; i ++) { + ecs_member_t *elem = &members[i]; -static -ecs_table_t* flecs_create_edge_for_add( - ecs_world_t *world, - ecs_table_t *node, - ecs_graph_edge_t *edge, - ecs_id_t id) -{ - ecs_table_t *to = flecs_find_table_with(world, node, id); - flecs_init_edge_for_add(world, node, edge, id, to); - return to; -} + ecs_assert(elem->name != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(elem->type != 0, ECS_INTERNAL_ERROR, NULL); -ecs_table_t* flecs_table_traverse_remove( - ecs_world_t *world, - ecs_table_t *node, - ecs_id_t *id_ptr, - ecs_table_diff_t *diff) -{ - ecs_poly_assert(world, ecs_world_t); + /* Get component of member type to get its size & alignment */ + const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); + if (!mbr_comp) { + char *path = ecs_get_path(world, elem->type); + ecs_err("member '%s' is not a type", path); + ecs_os_free(path); + return -1; + } - node = node ? node : &world->store.root; + ecs_size_t member_size = mbr_comp->size; + ecs_size_t member_alignment = mbr_comp->alignment; - /* Removing 0 from an entity is not valid */ - ecs_check(id_ptr != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(id_ptr[0] != 0, ECS_INVALID_PARAMETER, NULL); + if (!member_size || !member_alignment) { + char *path = ecs_get_path(world, elem->type); + ecs_err("member '%s' has 0 size/alignment", path); + ecs_os_free(path); + return -1; + } - ecs_id_t id = id_ptr[0]; - ecs_graph_edge_t *edge = flecs_table_ensure_edge(world, &node->node.remove, id); - ecs_table_t *to = edge->to; + member_size *= elem->count; + size = ECS_ALIGN(size, member_alignment); + elem->size = member_size; + elem->offset = size; - if (!to) { - to = flecs_create_edge_for_remove(world, node, edge, id); - ecs_assert(to != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edge->to != NULL, ECS_INTERNAL_ERROR, NULL); - } + /* Synchronize offset with Member component */ + if (elem->member == member) { + m->offset = elem->offset; + } else { + EcsMember *other = ecs_ensure(world, elem->member, EcsMember); + other->offset = elem->offset; + } - if (node != to) { - if (edge->diff) { - *diff = *edge->diff; - } else { - diff->added.count = 0; - diff->removed.array = id_ptr; - diff->removed.count = 1; - } - } + size += member_size; - return to; -error: - return NULL; -} + if (member_alignment > alignment) { + alignment = member_alignment; + } + } + } else { + /* If members have explicit offsets, we can't rely on computed + * size/alignment values. Calculate size as if this is the last member + * instead, since this will validate if the member fits in the struct. + * It doesn't matter if the size is smaller than the actual struct size + * because flecs_init_type function compares computed size with actual + * (component) size to determine if the type is partial. */ + ecs_member_t *elem = &members[i]; -ecs_table_t* flecs_table_traverse_add( - ecs_world_t *world, - ecs_table_t *node, - ecs_id_t *id_ptr, - ecs_table_diff_t *diff) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_assert(diff != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(elem->name != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(elem->type != 0, ECS_INTERNAL_ERROR, NULL); - node = node ? node : &world->store.root; + /* Get component of member type to get its size & alignment */ + const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); + if (!mbr_comp) { + char *path = ecs_get_path(world, elem->type); + ecs_err("member '%s' is not a type", path); + ecs_os_free(path); + return -1; + } - /* Adding 0 to an entity is not valid */ - ecs_check(id_ptr != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(id_ptr[0] != 0, ECS_INVALID_PARAMETER, NULL); + ecs_size_t member_size = mbr_comp->size; + ecs_size_t member_alignment = mbr_comp->alignment; - ecs_id_t id = id_ptr[0]; - ecs_graph_edge_t *edge = flecs_table_ensure_edge(world, &node->node.add, id); - ecs_table_t *to = edge->to; + if (!member_size || !member_alignment) { + char *path = ecs_get_path(world, elem->type); + ecs_err("member '%s' has 0 size/alignment", path); + ecs_os_free(path); + return -1; + } - if (!to) { - to = flecs_create_edge_for_add(world, node, edge, id); - ecs_assert(to != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edge->to != NULL, ECS_INTERNAL_ERROR, NULL); - } + member_size *= elem->count; + elem->size = member_size; + size = elem->offset + member_size; - if (node != to || edge->diff) { - if (edge->diff) { - *diff = *edge->diff; + const EcsComponent* comp = ecs_get(world, type, EcsComponent); + if (comp) { + alignment = comp->alignment; } else { - diff->added.array = id_ptr; - diff->added.count = 1; - diff->removed.count = 0; + alignment = member_alignment; } } - return to; -error: - return NULL; -} - -ecs_table_t* flecs_table_find_or_create( - ecs_world_t *world, - ecs_type_t *type) -{ - ecs_poly_assert(world, ecs_world_t); - return flecs_table_ensure(world, type, false, NULL); -} + if (size == 0) { + ecs_err("struct '%s' has 0 size", ecs_get_name(world, type)); + return -1; + } -void flecs_init_root_table( - ecs_world_t *world) -{ - ecs_poly_assert(world, ecs_world_t); + if (alignment == 0) { + ecs_err("struct '%s' has 0 alignment", ecs_get_name(world, type)); + return -1; + } - world->store.root.type = (ecs_type_t){0}; - world->store.root._ = flecs_calloc_t(&world->allocator, ecs_table__t); - flecs_init_table(world, &world->store.root, NULL); + /* Align struct size to struct alignment */ + size = ECS_ALIGN(size, alignment); - /* Ensure table indices start at 1, as 0 is reserved for the root */ - uint64_t new_id = flecs_sparse_new_id(&world->store.tables); - ecs_assert(new_id == 0, ECS_INTERNAL_ERROR, NULL); - (void)new_id; -} + ecs_modified(world, type, EcsStruct); -void flecs_table_clear_edges( - ecs_world_t *world, - ecs_table_t *table) -{ - (void)world; - ecs_poly_assert(world, ecs_world_t); + /* Do this last as it triggers the update of EcsTypeSerializer */ + if (flecs_init_type(world, type, EcsStructType, size, alignment)) { + return -1; + } - ecs_log_push_1(); + /* If current struct is also a member, assign to itself */ + if (ecs_has(world, type, EcsMember)) { + EcsMember *type_mbr = ecs_ensure(world, type, EcsMember); + ecs_assert(type_mbr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_map_iter_t it; - ecs_graph_node_t *table_node = &table->node; - ecs_graph_edges_t *node_add = &table_node->add; - ecs_graph_edges_t *node_remove = &table_node->remove; - ecs_map_t *add_hi = node_add->hi; - ecs_map_t *remove_hi = node_remove->hi; - ecs_graph_edge_hdr_t *node_refs = &table_node->refs; + type_mbr->type = type; + type_mbr->count = 1; - /* Cleanup outgoing edges */ - it = ecs_map_iter(add_hi); - while (ecs_map_next(&it)) { - flecs_table_disconnect_edge(world, ecs_map_key(&it), ecs_map_ptr(&it)); + ecs_modified(world, type, EcsMember); } - it = ecs_map_iter(remove_hi); + return 0; +} + +static +int flecs_add_constant_to_enum( + ecs_world_t *world, + ecs_entity_t type, + ecs_entity_t e, + ecs_id_t constant_id) +{ + EcsEnum *ptr = ecs_ensure(world, type, EcsEnum); + + /* Remove constant from map if it was already added */ + ecs_map_iter_t it = ecs_map_iter(&ptr->constants); while (ecs_map_next(&it)) { - flecs_table_disconnect_edge(world, ecs_map_key(&it), ecs_map_ptr(&it)); + ecs_enum_constant_t *c = ecs_map_ptr(&it); + if (c->constant == e) { + ecs_os_free(ECS_CONST_CAST(char*, c->name)); + ecs_map_remove_free(&ptr->constants, ecs_map_key(&it)); + } } - /* Cleanup incoming add edges */ - ecs_graph_edge_hdr_t *next, *cur = node_refs->next; - if (cur) { - do { - ecs_graph_edge_t *edge = (ecs_graph_edge_t*)cur; - ecs_assert(edge->to == table, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edge->from != NULL, ECS_INTERNAL_ERROR, NULL); - next = cur->next; - flecs_table_remove_edge(world, &edge->from->node.add, edge->id, edge); - } while ((cur = next)); - } + /* Check if constant sets explicit value */ + int32_t value = 0; + bool value_set = false; + if (ecs_id_is_pair(constant_id)) { + if (ecs_pair_second(world, constant_id) != ecs_id(ecs_i32_t)) { + char *path = ecs_get_path(world, e); + ecs_err("expected i32 type for enum constant '%s'", path); + ecs_os_free(path); + return -1; + } - /* Cleanup incoming remove edges */ - cur = node_refs->prev; - if (cur) { - do { - ecs_graph_edge_t *edge = (ecs_graph_edge_t*)cur; - ecs_assert(edge->to == table, ECS_INTERNAL_ERROR, NULL); - ecs_assert(edge->from != NULL, ECS_INTERNAL_ERROR, NULL); - next = cur->prev; - flecs_table_remove_edge(world, &edge->from->node.remove, edge->id, edge); - } while ((cur = next)); + const int32_t *value_ptr = ecs_get_pair_second( + world, e, EcsConstant, ecs_i32_t); + ecs_assert(value_ptr != NULL, ECS_INTERNAL_ERROR, NULL); + value = *value_ptr; + value_set = true; } - if (node_add->lo) { - flecs_bfree(&world->allocators.graph_edge_lo, node_add->lo); - } - if (node_remove->lo) { - flecs_bfree(&world->allocators.graph_edge_lo, node_remove->lo); + /* Make sure constant value doesn't conflict if set / find the next value */ + it = ecs_map_iter(&ptr->constants); + while (ecs_map_next(&it)) { + ecs_enum_constant_t *c = ecs_map_ptr(&it); + if (value_set) { + if (c->value == value) { + char *path = ecs_get_path(world, e); + ecs_err("conflicting constant value %d for '%s' (other is '%s')", + value, path, c->name); + ecs_os_free(path); + return -1; + } + } else { + if (c->value >= value) { + value = c->value + 1; + } + } } - ecs_map_fini(add_hi); - ecs_map_fini(remove_hi); - flecs_free_t(&world->allocator, ecs_map_t, add_hi); - flecs_free_t(&world->allocator, ecs_map_t, remove_hi); - table_node->add.lo = NULL; - table_node->remove.lo = NULL; - table_node->add.hi = NULL; - table_node->remove.hi = NULL; + ecs_map_init_if(&ptr->constants, &world->allocator); + ecs_enum_constant_t *c = ecs_map_insert_alloc_t(&ptr->constants, + ecs_enum_constant_t, (ecs_map_key_t)value); + c->name = ecs_os_strdup(ecs_get_name(world, e)); + c->value = value; + c->constant = e; - ecs_log_pop_1(); -} + ecs_i32_t *cptr = ecs_ensure_pair_second( + world, e, EcsConstant, ecs_i32_t); + ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL); + cptr[0] = value; -/* Public convenience functions for traversing table graph */ -ecs_table_t* ecs_table_add_id( - ecs_world_t *world, - ecs_table_t *table, - ecs_id_t id) -{ - ecs_table_diff_t diff; - return flecs_table_traverse_add(world, table, &id, &diff); -} + cptr = ecs_ensure_id(world, e, type); + cptr[0] = value; -ecs_table_t* ecs_table_remove_id( - ecs_world_t *world, - ecs_table_t *table, - ecs_id_t id) -{ - ecs_table_diff_t diff; - return flecs_table_traverse_remove(world, table, &id, &diff); + ecs_modified(world, type, EcsEnum); + return 0; } -ecs_table_t* ecs_table_find( - ecs_world_t *world, - const ecs_id_t *ids, - int32_t id_count) +static +int flecs_add_constant_to_bitmask( + ecs_world_t *world, + ecs_entity_t type, + ecs_entity_t e, + ecs_id_t constant_id) { - ecs_type_t type = { - .array = ECS_CONST_CAST(ecs_id_t*, ids), - .count = id_count - }; - return flecs_table_ensure(world, &type, false, NULL); -} + EcsBitmask *ptr = ecs_ensure(world, type, EcsBitmask); + + /* Remove constant from map if it was already added */ + ecs_map_iter_t it = ecs_map_iter(&ptr->constants); + while (ecs_map_next(&it)) { + ecs_bitmask_constant_t *c = ecs_map_ptr(&it); + if (c->constant == e) { + ecs_os_free(ECS_CONST_CAST(char*, c->name)); + ecs_map_remove_free(&ptr->constants, ecs_map_key(&it)); + } + } -/** - * @file expr/deserialize.c - * @brief Deserialize flecs string format into (component) values. - */ + /* Check if constant sets explicit value */ + uint32_t value = 1; + if (ecs_id_is_pair(constant_id)) { + if (ecs_pair_second(world, constant_id) != ecs_id(ecs_u32_t)) { + char *path = ecs_get_path(world, e); + ecs_err("expected u32 type for bitmask constant '%s'", path); + ecs_os_free(path); + return -1; + } -#include + const uint32_t *value_ptr = ecs_get_pair_second( + world, e, EcsConstant, ecs_u32_t); + ecs_assert(value_ptr != NULL, ECS_INTERNAL_ERROR, NULL); + value = *value_ptr; + } else { + value = 1u << (ecs_u32_t)ecs_map_count(&ptr->constants); + } -#ifdef FLECS_EXPR + /* Make sure constant value doesn't conflict */ + it = ecs_map_iter(&ptr->constants); + while (ecs_map_next(&it)) { + ecs_bitmask_constant_t *c = ecs_map_ptr(&it); + if (c->value == value) { + char *path = ecs_get_path(world, e); + ecs_err("conflicting constant value for '%s' (other is '%s')", + path, c->name); + ecs_os_free(path); + return -1; + } + } -/* String deserializer for values & simple expressions */ + ecs_map_init_if(&ptr->constants, &world->allocator); -/* Order in enumeration is important, as it is used for precedence */ -typedef enum ecs_expr_oper_t { - EcsExprOperUnknown, - EcsLeftParen, - EcsCondAnd, - EcsCondOr, - EcsCondEq, - EcsCondNeq, - EcsCondGt, - EcsCondGtEq, - EcsCondLt, - EcsCondLtEq, - EcsShiftLeft, - EcsShiftRight, - EcsAdd, - EcsSub, - EcsMul, - EcsDiv, - EcsMin -} ecs_expr_oper_t; + ecs_bitmask_constant_t *c = ecs_map_insert_alloc_t(&ptr->constants, + ecs_bitmask_constant_t, value); + c->name = ecs_os_strdup(ecs_get_name(world, e)); + c->value = value; + c->constant = e; -/* Used to track temporary values */ -#define EXPR_MAX_STACK_SIZE (256) + ecs_u32_t *cptr = ecs_ensure_pair_second( + world, e, EcsConstant, ecs_u32_t); + ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL); + cptr[0] = value; -typedef struct ecs_expr_value_t { - const ecs_type_info_t *ti; - void *ptr; -} ecs_expr_value_t; + cptr = ecs_ensure_id(world, e, type); + cptr[0] = value; -typedef struct ecs_value_stack_t { - ecs_expr_value_t values[EXPR_MAX_STACK_SIZE]; - ecs_stack_cursor_t *cursor; - ecs_stack_t *stack; - ecs_stage_t *stage; - int32_t count; -} ecs_value_stack_t; + return 0; +} static -const char* flecs_parse_expr( - ecs_world_t *world, - ecs_value_stack_t *stack, - const char *ptr, - ecs_value_t *value, - ecs_expr_oper_t op, - const ecs_parse_expr_desc_t *desc); +void flecs_set_primitive(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsPrimitive *type = ecs_field(it, EcsPrimitive, 0); -static -void* flecs_expr_value_new( - ecs_value_stack_t *stack, - ecs_entity_t type) -{ - ecs_stage_t *stage = stack->stage; - ecs_world_t *world = stage->world; - ecs_id_record_t *idr = flecs_id_record_get(world, type); - if (!idr) { - return NULL; + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + switch(type->kind) { + case EcsBool: + init_type_t(world, e, EcsPrimitiveType, bool); + break; + case EcsChar: + init_type_t(world, e, EcsPrimitiveType, char); + break; + case EcsByte: + init_type_t(world, e, EcsPrimitiveType, bool); + break; + case EcsU8: + init_type_t(world, e, EcsPrimitiveType, uint8_t); + break; + case EcsU16: + init_type_t(world, e, EcsPrimitiveType, uint16_t); + break; + case EcsU32: + init_type_t(world, e, EcsPrimitiveType, uint32_t); + break; + case EcsU64: + init_type_t(world, e, EcsPrimitiveType, uint64_t); + break; + case EcsI8: + init_type_t(world, e, EcsPrimitiveType, int8_t); + break; + case EcsI16: + init_type_t(world, e, EcsPrimitiveType, int16_t); + break; + case EcsI32: + init_type_t(world, e, EcsPrimitiveType, int32_t); + break; + case EcsI64: + init_type_t(world, e, EcsPrimitiveType, int64_t); + break; + case EcsF32: + init_type_t(world, e, EcsPrimitiveType, float); + break; + case EcsF64: + init_type_t(world, e, EcsPrimitiveType, double); + break; + case EcsUPtr: + init_type_t(world, e, EcsPrimitiveType, uintptr_t); + break; + case EcsIPtr: + init_type_t(world, e, EcsPrimitiveType, intptr_t); + break; + case EcsString: + init_type_t(world, e, EcsPrimitiveType, char*); + break; + case EcsEntity: + init_type_t(world, e, EcsPrimitiveType, ecs_entity_t); + break; + case EcsId: + init_type_t(world, e, EcsPrimitiveType, ecs_id_t); + break; + } } +} - const ecs_type_info_t *ti = idr->type_info; - if (!ti) { - return NULL; - } +static +void flecs_set_member(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsMember *member = ecs_field(it, EcsMember, 0); + EcsMemberRanges *ranges = ecs_table_get_id(world, it->table, + ecs_id(EcsMemberRanges), it->offset); - ecs_assert(ti->size != 0, ECS_INTERNAL_ERROR, NULL); - void *result = flecs_stack_alloc(stack->stack, ti->size, ti->alignment); - if (ti->hooks.ctor) { - ti->hooks.ctor(result, 1, ti); - } else { - ecs_os_memset(result, 0, ti->size); - } - if (ti->hooks.dtor) { - /* Track values that have destructors */ - stack->values[stack->count].ti = ti; - stack->values[stack->count].ptr = result; - stack->count ++; - } + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); + if (!parent) { + ecs_err("missing parent for member '%s'", ecs_get_name(world, e)); + continue; + } - return result; + flecs_add_member_to_struct(world, parent, e, &member[i], + ranges ? &ranges[i] : NULL); + } } static -const char* flecs_str_to_expr_oper( - const char *str, - ecs_expr_oper_t *op) -{ - if (!ecs_os_strncmp(str, "+", 1)) { - *op = EcsAdd; - return str + 1; - } else if (!ecs_os_strncmp(str, "-", 1)) { - *op = EcsSub; - return str + 1; - } else if (!ecs_os_strncmp(str, "*", 1)) { - *op = EcsMul; - return str + 1; - } else if (!ecs_os_strncmp(str, "/", 1)) { - *op = EcsDiv; - return str + 1; - } else if (!ecs_os_strncmp(str, "&&", 2)) { - *op = EcsCondAnd; - return str + 2; - } else if (!ecs_os_strncmp(str, "||", 2)) { - *op = EcsCondOr; - return str + 2; - } else if (!ecs_os_strncmp(str, "==", 2)) { - *op = EcsCondEq; - return str + 2; - } else if (!ecs_os_strncmp(str, "!=", 2)) { - *op = EcsCondNeq; - return str + 2; - } else if (!ecs_os_strncmp(str, ">=", 2)) { - *op = EcsCondGtEq; - return str + 2; - } else if (!ecs_os_strncmp(str, "<=", 2)) { - *op = EcsCondLtEq; - return str + 2; - } else if (!ecs_os_strncmp(str, ">>", 2)) { - *op = EcsShiftRight; - return str + 2; - } else if (!ecs_os_strncmp(str, "<<", 2)) { - *op = EcsShiftLeft; - return str + 2; - } else if (!ecs_os_strncmp(str, ">", 1)) { - *op = EcsCondGt; - return str + 1; - } else if (!ecs_os_strncmp(str, "<", 1)) { - *op = EcsCondLt; - return str + 1; +void flecs_set_member_ranges(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsMemberRanges *ranges = ecs_field(it, EcsMemberRanges, 0); + EcsMember *member = ecs_table_get_id(world, it->table, + ecs_id(EcsMember), it->offset); + if (!member) { + return; } - *op = EcsExprOperUnknown; - return NULL; + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); + if (!parent) { + ecs_err("missing parent for member '%s'", ecs_get_name(world, e)); + continue; + } + + flecs_add_member_to_struct(world, parent, e, &member[i], + &ranges[i]); + } } -const char *ecs_parse_expr_token( - const char *name, - const char *expr, - const char *ptr, - char *token) -{ - char *token_ptr = token; +static +void flecs_add_enum(ecs_iter_t *it) { + ecs_world_t *world = it->world; - if (ptr[0] == '/') { - char ch; - if (ptr[1] == '/') { - // Single line comment - for (ptr = &ptr[2]; (ch = ptr[0]) && (ch != '\n'); ptr ++) {} - token[0] = 0; - return ecs_parse_ws_eol(ptr); - } else if (ptr[1] == '*') { - // Multi line comment - for (ptr = &ptr[2]; (ch = ptr[0]); ptr ++) { - if (ch == '*' && ptr[1] == '/') { - token[0] = 0; - return ecs_parse_ws_eol(ptr + 2); - } - } + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; - ecs_parser_error(name, expr, ptr - expr, - "missing */ for multiline comment"); - return NULL; + if (init_type_t(world, e, EcsEnumType, ecs_i32_t)) { + continue; } - } - ecs_expr_oper_t op; - if (ptr[0] == '(') { - token[0] = '('; - token[1] = 0; - return ptr + 1; - } else if (ptr[0] != '-') { - const char *tptr = flecs_str_to_expr_oper(ptr, &op); - if (tptr) { - ecs_os_strncpy(token, ptr, tptr - ptr); - return tptr; - } + ecs_add_id(world, e, EcsExclusive); + ecs_add_id(world, e, EcsOneOf); + ecs_add_id(world, e, EcsPairIsTag); } +} - while ((ptr = ecs_parse_token(name, expr, ptr, token_ptr, 0))) { - if (ptr[0] == '|' && ptr[1] != '|') { - token_ptr = &token_ptr[ecs_os_strlen(token_ptr)]; - token_ptr[0] = '|'; - token_ptr[1] = '\0'; - token_ptr ++; - ptr ++; - } else { - break; +static +void flecs_add_bitmask(ecs_iter_t *it) { + ecs_world_t *world = it->world; + + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + + if (init_type_t(world, e, EcsBitmaskType, ecs_u32_t)) { + continue; } } - - return ptr; } static -const char* flecs_parse_multiline_string( - ecs_meta_cursor_t *cur, - const char *name, - const char *expr, - const char *ptr) -{ - /* Multiline string */ - ecs_strbuf_t str = ECS_STRBUF_INIT; - char ch; - while ((ch = ptr[0]) && (ch != '`')) { - if (ch == '\\' && ptr[1] == '`') { - ch = '`'; - ptr ++; +void flecs_add_constant(ecs_iter_t *it) { + ecs_world_t *world = it->world; + + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); + if (!parent) { + ecs_err("missing parent for constant '%s'", ecs_get_name(world, e)); + continue; } - ecs_strbuf_appendch(&str, ch); - ptr ++; - } - - if (ch != '`') { - ecs_parser_error(name, expr, ptr - expr, - "missing '`' to close multiline string"); - goto error; - } - char *strval = ecs_strbuf_get(&str); - if (ecs_meta_set_string(cur, strval) != 0) { - goto error; - } - ecs_os_free(strval); - return ptr + 1; -error: - return NULL; + if (ecs_has(world, parent, EcsEnum)) { + flecs_add_constant_to_enum(world, parent, e, it->event_id); + } else if (ecs_has(world, parent, EcsBitmask)) { + flecs_add_constant_to_bitmask(world, parent, e, it->event_id); + } + } } static -bool flecs_parse_is_float( - const char *ptr) -{ - ecs_assert(isdigit(ptr[0]), ECS_INTERNAL_ERROR, NULL); - char ch; - while ((ch = (++ptr)[0])) { - if (ch == '.' || ch == 'e') { - return true; +void flecs_set_array(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsArray *array = ecs_field(it, EcsArray, 0); + + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t elem_type = array[i].type; + int32_t elem_count = array[i].count; + + if (!elem_type) { + ecs_err("array '%s' has no element type", ecs_get_name(world, e)); + continue; } - if (!isdigit(ch)) { - return false; + + if (!elem_count) { + ecs_err("array '%s' has size 0", ecs_get_name(world, e)); + continue; + } + + const EcsComponent *elem_ptr = ecs_get(world, elem_type, EcsComponent); + if (flecs_init_type(world, e, EcsArrayType, + elem_ptr->size * elem_count, elem_ptr->alignment)) + { + continue; } } - return false; } -/* Attempt to resolve variable dotexpression to value (foo.bar) */ static -ecs_value_t flecs_dotresolve_var( - ecs_world_t *world, - ecs_vars_t *vars, - char *token) -{ - char *dot = strchr(token, '.'); - if (!dot) { - return (ecs_value_t){ .type = ecs_id(ecs_entity_t) }; - } +void flecs_set_vector(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsVector *array = ecs_field(it, EcsVector, 0); - dot[0] = '\0'; + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t elem_type = array[i].type; - const ecs_expr_var_t *var = ecs_vars_lookup(vars, token); - if (!var) { - return (ecs_value_t){0}; - } + if (!elem_type) { + ecs_err("vector '%s' has no element type", ecs_get_name(world, e)); + continue; + } - ecs_meta_cursor_t cur = ecs_meta_cursor( - world, var->value.type, var->value.ptr); - ecs_meta_push(&cur); - if (ecs_meta_dotmember(&cur, dot + 1) != 0) { - return (ecs_value_t){0}; + if (init_type_t(world, e, EcsVectorType, ecs_vec_t)) { + continue; + } } - - return (ecs_value_t){ - .ptr = ecs_meta_get_ptr(&cur), - .type = ecs_meta_get_type(&cur) - }; } static -int flecs_meta_call( - ecs_world_t *world, - ecs_value_stack_t *stack, - const char *name, - const char *expr, - const char *ptr, - ecs_meta_cursor_t *cur, - const char *function) -{ - ecs_entity_t type = ecs_meta_get_type(cur); - void *value_ptr = ecs_meta_get_ptr(cur); +void flecs_set_custom_type(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsOpaque *serialize = ecs_field(it, EcsOpaque, 0); - if (!ecs_os_strcmp(function, "parent")) { - if (type != ecs_id(ecs_entity_t)) { - ecs_parser_error(name, expr, ptr - expr, - "parent() can only be called on entity"); - return -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; - *(ecs_entity_t*)value_ptr = ecs_get_parent( - world, *(ecs_entity_t*)value_ptr); - } else if (!ecs_os_strcmp(function, "name")) { - if (type != ecs_id(ecs_entity_t)) { - ecs_parser_error(name, expr, ptr - expr, - "name() can only be called on entity"); - return -1; + if (!elem_type) { + ecs_err("custom type '%s' has no mapping type", ecs_get_name(world, e)); + continue; } - char **result = flecs_expr_value_new(stack, ecs_id(ecs_string_t)); - *result = ecs_os_strdup(ecs_get_name(world, *(ecs_entity_t*)value_ptr)); - *cur = ecs_meta_cursor(world, ecs_id(ecs_string_t), result); - } else if (!ecs_os_strcmp(function, "doc_name")) { -#ifdef FLECS_DOC - if (type != ecs_id(ecs_entity_t)) { - ecs_parser_error(name, expr, ptr - expr, - "name() can only be called on entity"); - return -1; + 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; } - char **result = flecs_expr_value_new(stack, ecs_id(ecs_string_t)); - *result = ecs_os_strdup(ecs_doc_get_name(world, *(ecs_entity_t*)value_ptr)); - *cur = ecs_meta_cursor(world, ecs_id(ecs_string_t), result); -#else - ecs_parser_error(name, expr, ptr - expr, - "doc_name() is not available without FLECS_DOC addon"); - return -1; -#endif - } else { - ecs_parser_error(name, expr, ptr - expr, - "unknown function '%s'", function); - return -1; + if (flecs_init_type(world, e, EcsOpaqueType, comp->size, comp->alignment)) { + continue; + } } - - return 0; } -/* Determine the type of an expression from the first character(s). This allows - * us to initialize a storage for a type if none was provided. */ -static -ecs_entity_t flecs_parse_discover_type( +bool flecs_unit_validate( ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_entity_t input_type, - const ecs_parse_expr_desc_t *desc) + ecs_entity_t t, + EcsUnit *data) { - /* String literal */ - if (ptr[0] == '"' || ptr[0] == '`') { - if (input_type == ecs_id(ecs_char_t)) { - return input_type; + char *derived_symbol = NULL; + const char *symbol = data->symbol; + + ecs_entity_t base = data->base; + ecs_entity_t over = data->over; + ecs_entity_t prefix = data->prefix; + ecs_unit_translation_t translation = data->translation; + + if (base) { + if (!ecs_has(world, base, EcsUnit)) { + ecs_err("entity '%s' for unit '%s' used as base is not a unit", + ecs_get_name(world, base), ecs_get_name(world, t)); + goto error; } - return ecs_id(ecs_string_t); } - /* Negative number literal */ - if (ptr[0] == '-') { - if (!isdigit(ptr[1])) { - ecs_parser_error(name, expr, ptr - expr, "invalid literal"); - return 0; + if (over) { + if (!base) { + ecs_err("invalid unit '%s': cannot specify over without base", + ecs_get_name(world, t)); + goto error; } - if (flecs_parse_is_float(ptr + 1)) { - return ecs_id(ecs_f64_t); - } else { - return ecs_id(ecs_i64_t); + if (!ecs_has(world, over, EcsUnit)) { + ecs_err("entity '%s' for unit '%s' used as over is not a unit", + ecs_get_name(world, over), ecs_get_name(world, t)); + goto error; } } - /* Positive number literal */ - if (isdigit(ptr[0])) { - if (flecs_parse_is_float(ptr)) { - return ecs_id(ecs_f64_t); + if (prefix) { + if (!base) { + ecs_err("invalid unit '%s': cannot specify prefix without base", + ecs_get_name(world, t)); + goto error; + } + const EcsUnitPrefix *prefix_ptr = ecs_get(world, prefix, EcsUnitPrefix); + if (!prefix_ptr) { + ecs_err("entity '%s' for unit '%s' used as prefix is not a prefix", + ecs_get_name(world, over), ecs_get_name(world, t)); + goto error; + } + + if (translation.factor || translation.power) { + if (prefix_ptr->translation.factor != translation.factor || + prefix_ptr->translation.power != translation.power) + { + ecs_err( + "factor for unit '%s' is inconsistent with prefix '%s'", + ecs_get_name(world, t), ecs_get_name(world, prefix)); + goto error; + } } else { - return ecs_id(ecs_u64_t); + translation = prefix_ptr->translation; } } - /* Variable */ - if (ptr[0] == '$') { - if (!desc || !desc->vars) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved variable (no variable scope)"); - return 0; + if (base) { + bool must_match = false; /* Must base symbol match symbol? */ + ecs_strbuf_t sbuf = ECS_STRBUF_INIT; + if (prefix) { + const EcsUnitPrefix *ptr = ecs_get(world, prefix, EcsUnitPrefix); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + if (ptr->symbol) { + ecs_strbuf_appendstr(&sbuf, ptr->symbol); + must_match = true; + } } - char token[ECS_MAX_TOKEN_SIZE]; - if (ecs_parse_expr_token(name, expr, &ptr[1], token) == NULL) { - return 0; + + const EcsUnit *uptr = ecs_get(world, base, EcsUnit); + ecs_assert(uptr != NULL, ECS_INTERNAL_ERROR, NULL); + if (uptr->symbol) { + ecs_strbuf_appendstr(&sbuf, uptr->symbol); } - const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, token); - if (!var) { - ecs_size_t len = ecs_os_strlen(token); - if (ptr[len + 1] == '(') { - while ((len > 0) && (token[len] != '.')) { - len --; - } - if (token[len] == '.') { - token[len] = '\0'; - } + if (over) { + uptr = ecs_get(world, over, EcsUnit); + ecs_assert(uptr != NULL, ECS_INTERNAL_ERROR, NULL); + if (uptr->symbol) { + ecs_strbuf_appendch(&sbuf, '/'); + ecs_strbuf_appendstr(&sbuf, uptr->symbol); + must_match = true; } + } - ecs_value_t v = flecs_dotresolve_var(world, desc->vars, token); - if (v.type) { - return v.type; - } + derived_symbol = ecs_strbuf_get(&sbuf); + if (derived_symbol && !ecs_os_strlen(derived_symbol)) { + ecs_os_free(derived_symbol); + derived_symbol = NULL; + } - ecs_parser_error(name, expr, ptr - expr, - "unresolved variable '%s'", token); - return 0; + if (derived_symbol && symbol && ecs_os_strcmp(symbol, derived_symbol)) { + if (must_match) { + ecs_err("symbol '%s' for unit '%s' does not match base" + " symbol '%s'", symbol, + ecs_get_name(world, t), derived_symbol); + goto error; + } + } + if (!symbol && derived_symbol && (prefix || over)) { + ecs_os_free(data->symbol); + data->symbol = derived_symbol; + } else { + ecs_os_free(derived_symbol); } - return var->value.type; } - /* Boolean */ - if (ptr[0] == 't' && !ecs_os_strncmp(ptr, "true", 4)) { - if (!isalpha(ptr[4]) && ptr[4] != '_') { - return ecs_id(ecs_bool_t); - } + data->base = base; + data->over = over; + data->prefix = prefix; + data->translation = translation; + + return true; +error: + ecs_os_free(derived_symbol); + return false; +} + +static +void flecs_set_unit(ecs_iter_t *it) { + EcsUnit *u = ecs_field(it, EcsUnit, 0); + + ecs_world_t *world = it->world; + + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + flecs_unit_validate(world, e, &u[i]); } - if (ptr[0] == 'f' && !ecs_os_strncmp(ptr, "false", 5)) { - if (!isalpha(ptr[5]) && ptr[5] != '_') { - return ecs_id(ecs_bool_t); +} + +static +void flecs_unit_quantity_monitor(ecs_iter_t *it) { + ecs_world_t *world = it->world; + + int i, count = it->count; + if (it->event == EcsOnAdd) { + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_add_pair(world, e, EcsQuantity, e); + } + } else { + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_remove_pair(world, e, EcsQuantity, e); } } +} - /* Entity identifier */ - if (isalpha(ptr[0])) { - if (!input_type) { /* Identifier could also be enum/bitmask constant */ - return ecs_id(ecs_entity_t); - } +static +void flecs_member_on_set(ecs_iter_t *it) { + EcsMember *mbr = ecs_field(it, EcsMember, 0); + if (!mbr->count) { + mbr->count = 1; } +} - /* If no default type was provided we can't automatically deduce the type of - * composite/collection expressions. */ - if (!input_type) { - if (ptr[0] == '{') { - ecs_parser_error(name, expr, ptr - expr, - "unknown type for composite literal"); - return 0; - } +void FlecsMetaImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsMeta); +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); +#endif - if (ptr[0] == '[') { - ecs_parser_error(name, expr, ptr - expr, - "unknown type for collection literal"); - return 0; - } + ecs_set_name_prefix(world, "Ecs"); + + flecs_bootstrap_component(world, EcsTypeSerializer); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsType), + .name = "type", .symbol = "EcsType", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsType), + .type.alignment = ECS_ALIGNOF(EcsType), + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsPrimitive), + .name = "primitive", .symbol = "EcsPrimitive", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsPrimitive), + .type.alignment = ECS_ALIGNOF(EcsPrimitive) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = EcsConstant, + .name = "constant", .symbol = "EcsConstant", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsEnum), + .name = "enum", .symbol = "EcsEnum", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsEnum), + .type.alignment = ECS_ALIGNOF(EcsEnum) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsBitmask), + .name = "bitmask", .symbol = "EcsBitmask", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsBitmask), + .type.alignment = ECS_ALIGNOF(EcsBitmask) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsMember), + .name = "member", .symbol = "EcsMember", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsMember), + .type.alignment = ECS_ALIGNOF(EcsMember) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsMemberRanges), + .name = "member_ranges", .symbol = "EcsMemberRanges", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsMemberRanges), + .type.alignment = ECS_ALIGNOF(EcsMemberRanges) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsStruct), + .name = "struct", .symbol = "EcsStruct", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsStruct), + .type.alignment = ECS_ALIGNOF(EcsStruct) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsArray), + .name = "array", .symbol = "EcsArray", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsArray), + .type.alignment = ECS_ALIGNOF(EcsArray) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsVector), + .name = "vector", .symbol = "EcsVector", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsVector), + .type.alignment = ECS_ALIGNOF(EcsVector) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsOpaque), + .name = "opaque", .symbol = "EcsOpaque", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsOpaque), + .type.alignment = ECS_ALIGNOF(EcsOpaque) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsUnit), + .name = "unit", .symbol = "EcsUnit", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsInherit)) + }), + .type.size = sizeof(EcsUnit), + .type.alignment = ECS_ALIGNOF(EcsUnit) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsUnitPrefix), + .name = "unit_prefix", .symbol = "EcsUnitPrefix", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsInherit)) + }), + .type.size = sizeof(EcsUnitPrefix), + .type.alignment = ECS_ALIGNOF(EcsUnitPrefix) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = EcsQuantity, + .name = "quantity", .symbol = "EcsQuantity", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsInherit)) + }) + }); + + ecs_set_hooks(world, EcsType, { .ctor = flecs_default_ctor }); + + ecs_set_hooks(world, EcsTypeSerializer, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsTypeSerializer), + .copy = ecs_copy(EcsTypeSerializer), + .dtor = ecs_dtor(EcsTypeSerializer) + }); + + ecs_set_hooks(world, EcsStruct, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsStruct), + .copy = ecs_copy(EcsStruct), + .dtor = ecs_dtor(EcsStruct) + }); + + ecs_set_hooks(world, EcsMember, { + .ctor = flecs_default_ctor, + .on_set = flecs_member_on_set + }); + + ecs_set_hooks(world, EcsMemberRanges, { + .ctor = flecs_default_ctor + }); + + ecs_set_hooks(world, EcsEnum, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsEnum), + .copy = ecs_copy(EcsEnum), + .dtor = ecs_dtor(EcsEnum) + }); + + ecs_set_hooks(world, EcsBitmask, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsBitmask), + .copy = ecs_copy(EcsBitmask), + .dtor = ecs_dtor(EcsBitmask) + }); + + ecs_set_hooks(world, EcsUnit, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsUnit), + .copy = ecs_copy(EcsUnit), + .dtor = ecs_dtor(EcsUnit) + }); + + ecs_set_hooks(world, EcsUnitPrefix, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsUnitPrefix), + .copy = ecs_copy(EcsUnitPrefix), + .dtor = ecs_dtor(EcsUnitPrefix) + }); + + /* Register triggers to finalize type information from component data */ + ecs_entity_t old_scope = ecs_set_scope( /* Keep meta scope clean */ + world, EcsFlecsInternals); + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsPrimitive) }, + .events = {EcsOnSet}, + .callback = flecs_set_primitive + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsMember) }, + .events = {EcsOnSet}, + .callback = flecs_set_member + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsMemberRanges) }, + .events = {EcsOnSet}, + .callback = flecs_set_member_ranges + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsEnum) }, + .events = {EcsOnAdd}, + .callback = flecs_add_enum + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsBitmask) }, + .events = {EcsOnAdd}, + .callback = flecs_add_bitmask + }); + + ecs_observer(world, { + .query.terms[0] = { .id = EcsConstant }, + .events = {EcsOnAdd}, + .callback = flecs_add_constant + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_pair(EcsConstant, EcsWildcard) }, + .events = {EcsOnSet}, + .callback = flecs_add_constant + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsArray) }, + .events = {EcsOnSet}, + .callback = flecs_set_array + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsVector) }, + .events = {EcsOnSet}, + .callback = flecs_set_vector + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsOpaque) }, + .events = {EcsOnSet}, + .callback = flecs_set_custom_type + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsUnit) }, + .events = {EcsOnSet}, + .callback = flecs_set_unit + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsType) }, + .events = {EcsOnSet}, + .callback = ecs_meta_type_serialized_init + }); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsType) }, + .events = {EcsOnSet}, + .callback = flecs_rtt_init_default_hooks + }); + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(EcsUnit) }, + { .id = EcsQuantity } + }, + .events = { EcsMonitor }, + .callback = flecs_unit_quantity_monitor + }); + ecs_set_scope(world, old_scope); + + /* Initialize primitive types */ + #define ECS_PRIMITIVE(world, type, primitive_kind)\ + ecs_entity_init(world, &(ecs_entity_desc_t){\ + .id = ecs_id(ecs_##type##_t),\ + .name = #type,\ + .symbol = #type });\ + ecs_set(world, ecs_id(ecs_##type##_t), EcsPrimitive, {\ + .kind = primitive_kind\ + }); + + ECS_PRIMITIVE(world, bool, EcsBool); + ECS_PRIMITIVE(world, char, EcsChar); + ECS_PRIMITIVE(world, byte, EcsByte); + ECS_PRIMITIVE(world, u8, EcsU8); + ECS_PRIMITIVE(world, u16, EcsU16); + ECS_PRIMITIVE(world, u32, EcsU32); + ECS_PRIMITIVE(world, u64, EcsU64); + ECS_PRIMITIVE(world, uptr, EcsUPtr); + ECS_PRIMITIVE(world, i8, EcsI8); + ECS_PRIMITIVE(world, i16, EcsI16); + ECS_PRIMITIVE(world, i32, EcsI32); + ECS_PRIMITIVE(world, i64, EcsI64); + ECS_PRIMITIVE(world, iptr, EcsIPtr); + ECS_PRIMITIVE(world, f32, EcsF32); + ECS_PRIMITIVE(world, f64, EcsF64); + ECS_PRIMITIVE(world, string, EcsString); + ECS_PRIMITIVE(world, entity, EcsEntity); + ECS_PRIMITIVE(world, id, EcsId); + + #undef ECS_PRIMITIVE + + ecs_set_hooks(world, ecs_string_t, { + .ctor = flecs_default_ctor, + .copy = ecs_copy(ecs_string_t), + .move = ecs_move(ecs_string_t), + .dtor = ecs_dtor(ecs_string_t) + }); + + /* Set default child components */ + ecs_set(world, ecs_id(EcsStruct), EcsDefaultChildComponent, {ecs_id(EcsMember)}); + ecs_set(world, ecs_id(EcsMember), EcsDefaultChildComponent, {ecs_id(EcsMember)}); + ecs_set(world, ecs_id(EcsEnum), EcsDefaultChildComponent, {EcsConstant}); + ecs_set(world, ecs_id(EcsBitmask), EcsDefaultChildComponent, {EcsConstant}); - ecs_parser_error(name, expr, ptr - expr, "invalid expression"); - } + /* Relationship properties */ + ecs_add_id(world, EcsQuantity, EcsExclusive); + ecs_add_id(world, EcsQuantity, EcsPairIsTag); - return input_type; + /* Import reflection definitions for builtin types */ + flecs_meta_import_definitions(world); } -/* Normalize types to their largest representation. - * Rather than taking the original type of a value, use the largest - * representation of the type so we don't have to worry about overflowing the - * original type in the operation. */ +#endif + +/* + * @file addons/meta/rtt_lifecycle.c + * @brief Runtime components lifecycle management + */ + + +#ifdef FLECS_META + +/* Stores all the information necessary to forward a hook call to a + * struct's member type */ +typedef struct ecs_rtt_call_data_t { + union { + ecs_xtor_t xtor; + ecs_move_t move; + ecs_copy_t copy; + } hook; + const ecs_type_info_t *type_info; + int32_t offset; + int32_t count; +} ecs_rtt_call_data_t; + +/* Lifecycle context for runtime structs */ +typedef struct ecs_rtt_struct_ctx_t { + ecs_vec_t vctor; /* vector */ + ecs_vec_t vdtor; /* vector */ + ecs_vec_t vmove; /* vector */ + ecs_vec_t vcopy; /* vector */ +} ecs_rtt_struct_ctx_t; + +/* Lifecycle context for runtime arrays */ +typedef struct ecs_rtt_array_ctx_t { + const ecs_type_info_t *type_info; + int32_t elem_count; +} ecs_rtt_array_ctx_t; + +/* Lifecycle context for runtime vectors */ +typedef struct ecs_rtt_vector_ctx_t { + const ecs_type_info_t *type_info; +} ecs_rtt_vector_ctx_t; + +/* Generic copy assign hook */ static -ecs_entity_t flecs_largest_type( - const EcsPrimitive *type) +void flecs_rtt_default_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) { - switch(type->kind) { - case EcsBool: return ecs_id(ecs_bool_t); - case EcsChar: return ecs_id(ecs_char_t); - case EcsByte: return ecs_id(ecs_u8_t); - case EcsU8: return ecs_id(ecs_u64_t); - case EcsU16: return ecs_id(ecs_u64_t); - case EcsU32: return ecs_id(ecs_u64_t); - case EcsU64: return ecs_id(ecs_u64_t); - case EcsI8: return ecs_id(ecs_i64_t); - case EcsI16: return ecs_id(ecs_i64_t); - case EcsI32: return ecs_id(ecs_i64_t); - case EcsI64: return ecs_id(ecs_i64_t); - case EcsF32: return ecs_id(ecs_f64_t); - case EcsF64: return ecs_id(ecs_f64_t); - case EcsUPtr: return ecs_id(ecs_u64_t); - case EcsIPtr: return ecs_id(ecs_i64_t); - case EcsString: return ecs_id(ecs_string_t); - case EcsEntity: return ecs_id(ecs_entity_t); - case EcsId: return ecs_id(ecs_id_t); - default: ecs_throw(ECS_INTERNAL_ERROR, NULL); - } -error: - return 0; + ecs_os_memcpy(dst_ptr, src_ptr, count * type_info->size); } -/** Test if a normalized type can promote to another type in an expression */ +/* Generic move assign hook */ static -bool flecs_is_type_number( - ecs_entity_t type) +void flecs_rtt_default_move( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) { - if (type == ecs_id(ecs_bool_t)) return false; - else if (type == ecs_id(ecs_char_t)) return false; - else if (type == ecs_id(ecs_u8_t)) return false; - else if (type == ecs_id(ecs_u64_t)) return true; - else if (type == ecs_id(ecs_i64_t)) return true; - else if (type == ecs_id(ecs_f64_t)) return true; - else if (type == ecs_id(ecs_string_t)) return false; - else if (type == ecs_id(ecs_entity_t)) return false; - else return false; + flecs_rtt_default_copy(dst_ptr, src_ptr, count, type_info); } +/* + * + * RTT struct support + * + */ + +/* Invokes struct member type's constructor/destructor using saved information + * in the lifecycle context */ static -bool flecs_oper_valid_for_type( - ecs_entity_t type, - ecs_expr_oper_t op) +void flecs_rtt_struct_xtor( + ecs_vec_t *xtor_data_vec, + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) { - switch(op) { - case EcsAdd: - case EcsSub: - case EcsMul: - case EcsDiv: - return flecs_is_type_number(type); - case EcsCondEq: - case EcsCondNeq: - case EcsCondAnd: - case EcsCondOr: - case EcsCondGt: - case EcsCondGtEq: - case EcsCondLt: - case EcsCondLtEq: - return flecs_is_type_number(type) || - (type == ecs_id(ecs_bool_t)) || - (type == ecs_id(ecs_char_t)) || - (type == ecs_id(ecs_entity_t)); - case EcsShiftLeft: - case EcsShiftRight: - return (type == ecs_id(ecs_u64_t)); - case EcsExprOperUnknown: - case EcsLeftParen: - case EcsMin: - return false; - default: - ecs_abort(ECS_INTERNAL_ERROR, NULL); + int cb_count = ecs_vec_count(xtor_data_vec); + int i, j; + for (j = 0; j < count; j++) { + void *elem_ptr = ECS_ELEM(ptr, type_info->size, j); + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *xtor_data = + ecs_vec_get_t(xtor_data_vec, ecs_rtt_call_data_t, i); + xtor_data->hook.xtor( + ECS_OFFSET(elem_ptr, xtor_data->offset), + xtor_data->count, + xtor_data->type_info); + } } } -/** Promote type to most expressive (f64 > i64 > u64) */ +/* Generic struct constructor. It will read hook information call data from + * the structs's lifecycle context and call the constructors configured when + * the type was created. */ static -ecs_entity_t flecs_promote_type( - ecs_entity_t type, - ecs_entity_t promote_to) +void flecs_rtt_struct_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) { - if (type == ecs_id(ecs_u64_t)) { - return promote_to; - } - if (promote_to == ecs_id(ecs_u64_t)) { - return type; - } - if (type == ecs_id(ecs_f64_t)) { - return type; - } - if (promote_to == ecs_id(ecs_f64_t)) { - return promote_to; - } - return ecs_id(ecs_i64_t); + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_rtt_struct_xtor(&rtt_ctx->vctor, ptr, count, type_info); } +/* Generic struct destructor. It will read hook information call data from + * the structs's lifecycle context and call the constructors configured when + * the type was created. */ static -int flecs_oper_precedence( - ecs_expr_oper_t left, - ecs_expr_oper_t right) +void flecs_rtt_struct_dtor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) { - return (left > right) - (left < right); + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_rtt_struct_xtor(&rtt_ctx->vdtor, ptr, count, type_info); } +/* Generic move hook. It will read hook information call data from the + * structs's lifecycle context and call the move hooks configured when + * the type was created. */ static -void flecs_value_cast( - ecs_world_t *world, - ecs_value_stack_t *stack, - ecs_value_t *value, - ecs_entity_t type) +void flecs_rtt_struct_move( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) { - if (value->type == type) { - return; - } - - ecs_value_t result; - result.type = type; - result.ptr = flecs_expr_value_new(stack, type); + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); - if (value->ptr) { - ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, result.ptr); - ecs_meta_set_value(&cur, value); + int cb_count = ecs_vec_count(&rtt_ctx->vmove); + int i, j; + for (j = 0; j < count; j++) { + ecs_size_t elem_offset = type_info->size * j; + void *elem_dst_ptr = ECS_OFFSET(dst_ptr, elem_offset); + void *elem_src_ptr = ECS_OFFSET(src_ptr, elem_offset); + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *move_data = + ecs_vec_get_t(&rtt_ctx->vmove, ecs_rtt_call_data_t, i); + move_data->hook.move( + ECS_OFFSET(elem_dst_ptr, move_data->offset), + ECS_OFFSET(elem_src_ptr, move_data->offset), + move_data->count, + move_data->type_info); + } } - - *value = result; } +/* Generic copy hook. It will read hook information call data from the + * structs's lifecycle context and call the copy hooks configured when + * the type was created. */ static -bool flecs_expr_op_is_equality( - ecs_expr_oper_t op) +void flecs_rtt_struct_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) { - switch(op) { - case EcsCondEq: - case EcsCondNeq: - case EcsCondGt: - case EcsCondGtEq: - case EcsCondLt: - case EcsCondLtEq: - return true; - case EcsCondAnd: - case EcsCondOr: - case EcsShiftLeft: - case EcsShiftRight: - case EcsAdd: - case EcsSub: - case EcsMul: - case EcsDiv: - case EcsExprOperUnknown: - case EcsLeftParen: - case EcsMin: - return false; - default: - ecs_throw(ECS_INTERNAL_ERROR, "invalid operator"); + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + + int cb_count = ecs_vec_count(&rtt_ctx->vcopy); + int i, j; + for (j = 0; j < count; j++) { + ecs_size_t elem_offset = type_info->size * j; + void *elem_dst_ptr = ECS_OFFSET(dst_ptr, elem_offset); + const void *elem_src_ptr = ECS_OFFSET(src_ptr, elem_offset); + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *copy_data = + ecs_vec_get_t(&rtt_ctx->vcopy, ecs_rtt_call_data_t, i); + copy_data->hook.copy( + ECS_OFFSET(elem_dst_ptr, copy_data->offset), + ECS_OFFSET(elem_src_ptr, copy_data->offset), + copy_data->count, + copy_data->type_info); + } } -error: - return false; } static -ecs_entity_t flecs_binary_expr_type( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_value_t *lvalue, - ecs_value_t *rvalue, - ecs_expr_oper_t op, - ecs_entity_t *operand_type_out) +void flecs_rtt_free_lifecycle_struct_ctx( + void *ctx) { - ecs_entity_t result_type = 0, operand_type = 0; - - switch(op) { - case EcsDiv: - /* Result type of a division is always a float */ - *operand_type_out = ecs_id(ecs_f64_t); - return ecs_id(ecs_f64_t); - case EcsCondAnd: - case EcsCondOr: - /* Result type of a condition operator is always a bool */ - *operand_type_out = ecs_id(ecs_bool_t); - return ecs_id(ecs_bool_t); - case EcsCondEq: - case EcsCondNeq: - case EcsCondGt: - case EcsCondGtEq: - case EcsCondLt: - case EcsCondLtEq: - /* Result type of equality operator is always bool, but operand types - * should not be casted to bool */ - result_type = ecs_id(ecs_bool_t); - break; - case EcsShiftLeft: - case EcsShiftRight: - case EcsAdd: - case EcsSub: - case EcsMul: - case EcsExprOperUnknown: - case EcsLeftParen: - case EcsMin: - break; - default: - ecs_throw(ECS_INTERNAL_ERROR, "invalid operator"); - } - - /* Result type for arithmetic operators is determined by operands */ - const EcsPrimitive *ltype_ptr = ecs_get(world, lvalue->type, EcsPrimitive); - const EcsPrimitive *rtype_ptr = ecs_get(world, rvalue->type, EcsPrimitive); - if (!ltype_ptr || !rtype_ptr) { - char *lname = ecs_get_fullpath(world, lvalue->type); - char *rname = ecs_get_fullpath(world, rvalue->type); - ecs_parser_error(name, expr, ptr - expr, - "invalid non-primitive type in binary expression (%s, %s)", - lname, rname); - ecs_os_free(lname); - ecs_os_free(rname); - return 0; - } - - ecs_entity_t ltype = flecs_largest_type(ltype_ptr); - ecs_entity_t rtype = flecs_largest_type(rtype_ptr); - if (ltype == rtype) { - operand_type = ltype; - goto done; - } - - if (flecs_expr_op_is_equality(op)) { - ecs_parser_error(name, expr, ptr - expr, - "mismatching types in equality expression"); - return 0; - } - - if (!flecs_is_type_number(ltype) || !flecs_is_type_number(rtype)) { - ecs_parser_error(name, expr, ptr - expr, - "incompatible types in binary expression"); - return 0; + if (!ctx) { + return; } - operand_type = flecs_promote_type(ltype, rtype); - -done: - if (op == EcsSub && operand_type == ecs_id(ecs_u64_t)) { - /* Result of subtracting two unsigned ints can be negative */ - operand_type = ecs_id(ecs_i64_t); - } + ecs_rtt_struct_ctx_t *lifecycle_ctx = ctx; - if (!result_type) { - result_type = operand_type; - } + ecs_vec_fini_t(NULL, &lifecycle_ctx->vctor, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vdtor, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vmove, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vcopy, ecs_rtt_call_data_t); - *operand_type_out = operand_type; - return result_type; -error: - return 0; + ecs_os_free(ctx); } -/* Macro's to let the compiler do the operations & conversion work for us */ - -#define ECS_VALUE_GET(value, T) (*(T*)value->ptr) - -#define ECS_BINARY_OP_T(left, right, result, op, R, T)\ - ECS_VALUE_GET(result, R) = ECS_VALUE_GET(left, T) op ECS_VALUE_GET(right, T) - -#define ECS_BINARY_OP(left, right, result, op)\ - if (left->type == ecs_id(ecs_u64_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_u64_t, ecs_u64_t);\ - } else if (left->type == ecs_id(ecs_i64_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_i64_t, ecs_i64_t);\ - } else if (left->type == ecs_id(ecs_f64_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_f64_t, ecs_f64_t);\ - } else {\ - ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ - } - -#define ECS_BINARY_COND_EQ_OP(left, right, result, op)\ - if (left->type == ecs_id(ecs_u64_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u64_t);\ - } else if (left->type == ecs_id(ecs_i64_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_i64_t);\ - } else if (left->type == ecs_id(ecs_f64_t)) { \ - ecs_parser_error(name, expr, ptr - expr, "unsupported operator for floating point");\ - return -1;\ - } else if (left->type == ecs_id(ecs_u8_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u8_t);\ - } else if (left->type == ecs_id(ecs_char_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_char_t);\ - } else if (left->type == ecs_id(ecs_bool_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ - } else {\ - ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ - } - -#define ECS_BINARY_COND_OP(left, right, result, op)\ - if (left->type == ecs_id(ecs_u64_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u64_t);\ - } else if (left->type == ecs_id(ecs_i64_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_i64_t);\ - } else if (left->type == ecs_id(ecs_f64_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_f64_t);\ - } else if (left->type == ecs_id(ecs_u8_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u8_t);\ - } else if (left->type == ecs_id(ecs_char_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_char_t);\ - } else if (left->type == ecs_id(ecs_bool_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ - } else {\ - ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ - } - -#define ECS_BINARY_BOOL_OP(left, right, result, op)\ - if (left->type == ecs_id(ecs_bool_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ - } else {\ - ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ - } - -#define ECS_BINARY_UINT_OP(left, right, result, op)\ - if (left->type == ecs_id(ecs_u64_t)) { \ - ECS_BINARY_OP_T(left, right, result, op, ecs_u64_t, ecs_u64_t);\ - } else {\ - ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ - } - static -int flecs_binary_expr_do( +ecs_rtt_struct_ctx_t * flecs_rtt_configure_struct_hooks( ecs_world_t *world, - ecs_value_stack_t *stack, - const char *name, - const char *expr, - const char *ptr, - ecs_value_t *lvalue, - ecs_value_t *rvalue, - ecs_value_t *result, - ecs_expr_oper_t op) + const ecs_type_info_t *ti, + bool ctor, + bool dtor, + bool move, + bool copy) { - /* Find expression type */ - ecs_entity_t operand_type, type = flecs_binary_expr_type( - world, name, expr, ptr, lvalue, rvalue, op, &operand_type); - if (!type) { - goto error; - } - - if (!flecs_oper_valid_for_type(type, op)) { - ecs_parser_error(name, expr, ptr - expr, "invalid operator for type"); - goto error; - } - - flecs_value_cast(world, stack, lvalue, operand_type); - flecs_value_cast(world, stack, rvalue, operand_type); - - ecs_value_t *storage = result; - ecs_value_t tmp_storage = {0}; - if (result->type != type) { - storage = &tmp_storage; - storage->type = type; - storage->ptr = flecs_expr_value_new(stack, type); + ecs_type_hooks_t hooks = ti->hooks; + if (hooks.lifecycle_ctx_free) { + hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); } - switch(op) { - case EcsAdd: - ECS_BINARY_OP(lvalue, rvalue, storage, +); - break; - case EcsSub: - ECS_BINARY_OP(lvalue, rvalue, storage, -); - break; - case EcsMul: - ECS_BINARY_OP(lvalue, rvalue, storage, *); - break; - case EcsDiv: - ECS_BINARY_OP(lvalue, rvalue, storage, /); - break; - case EcsCondEq: - ECS_BINARY_COND_EQ_OP(lvalue, rvalue, storage, ==); - break; - case EcsCondNeq: - ECS_BINARY_COND_EQ_OP(lvalue, rvalue, storage, !=); - break; - case EcsCondGt: - ECS_BINARY_COND_OP(lvalue, rvalue, storage, >); - break; - case EcsCondGtEq: - ECS_BINARY_COND_OP(lvalue, rvalue, storage, >=); - break; - case EcsCondLt: - ECS_BINARY_COND_OP(lvalue, rvalue, storage, <); - break; - case EcsCondLtEq: - ECS_BINARY_COND_OP(lvalue, rvalue, storage, <=); - break; - case EcsCondAnd: - ECS_BINARY_BOOL_OP(lvalue, rvalue, storage, &&); - break; - case EcsCondOr: - ECS_BINARY_BOOL_OP(lvalue, rvalue, storage, ||); - break; - case EcsShiftLeft: - ECS_BINARY_UINT_OP(lvalue, rvalue, storage, <<); - break; - case EcsShiftRight: - ECS_BINARY_UINT_OP(lvalue, rvalue, storage, >>); - break; - case EcsExprOperUnknown: - case EcsLeftParen: - case EcsMin: - ecs_parser_error(name, expr, ptr - expr, "unsupported operator"); - goto error; - default: - ecs_throw(ECS_INTERNAL_ERROR, NULL); - } + ecs_rtt_struct_ctx_t *rtt_ctx = NULL; + if (ctor || dtor || move || copy) { + rtt_ctx = ecs_os_malloc_t(ecs_rtt_struct_ctx_t); + ecs_vec_init_t(NULL, &rtt_ctx->vctor, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vdtor, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vmove, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vcopy, ecs_rtt_call_data_t, 0); + hooks.lifecycle_ctx = rtt_ctx; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_struct_ctx; - if (storage->ptr != result->ptr) { - if (!result->ptr) { - *result = *storage; - } else { - ecs_meta_cursor_t cur = ecs_meta_cursor(world, - result->type, result->ptr); - ecs_meta_set_value(&cur, storage); + if (ctor) { + hooks.ctor = flecs_rtt_struct_ctor; + } + if (dtor) { + hooks.dtor = flecs_rtt_struct_dtor; + } + if (move) { + hooks.move = flecs_rtt_struct_move; + } + if (copy) { + hooks.copy = flecs_rtt_struct_copy; } + } else { + hooks.lifecycle_ctx = NULL; + hooks.lifecycle_ctx_free = NULL; } - - return 0; -error: - return -1; + ecs_set_hooks_id(world, ti->component, &hooks); + return rtt_ctx; } +/* Checks if a struct member's types have hooks installed. If so, it generates + * and installs required hooks for the struct type itself. These hooks will + * invoke the member hooks when necessary */ static -const char* flecs_binary_expr_parse( +void flecs_rtt_init_default_hooks_struct( ecs_world_t *world, - ecs_value_stack_t *stack, - const char *name, - const char *expr, - const char *ptr, - ecs_value_t *lvalue, - ecs_value_t *result, - ecs_expr_oper_t left_op, - const ecs_parse_expr_desc_t *desc) + ecs_entity_t component, + const ecs_type_info_t *ti) { - ecs_entity_t result_type = result->type; - do { - ecs_expr_oper_t op; - ptr = flecs_str_to_expr_oper(ptr, &op); - if (!ptr) { - ecs_parser_error(name, expr, ptr - expr, "invalid operator"); - return NULL; + /* Obtain struct information to figure out what members it contains: */ + const EcsStruct *struct_info = ecs_get(world, component, EcsStruct); + ecs_assert(struct_info != NULL, ECS_INTERNAL_ERROR, NULL); + + /* These flags will be set to true if we determine we need to generate a + * hook of a particular type: */ + bool ctor_hook_required = false; + bool dtor_hook_required = false; + bool move_hook_required = false; + bool copy_hook_required = false; + + /* Iterate all struct members and see if any member type has hooks. If so, + * the struct itself will need to have that hook: */ + int i, member_count = ecs_vec_count(&struct_info->members); + ecs_member_t *members = ecs_vec_first(&struct_info->members); + for (i = 0; i < member_count; i++) { + ecs_member_t *m = &members[i]; + const ecs_type_info_t *member_ti = ecs_get_type_info(world, m->type); + ctor_hook_required |= member_ti->hooks.ctor && + member_ti->hooks.ctor != flecs_default_ctor; + dtor_hook_required |= member_ti->hooks.dtor != NULL; + move_hook_required |= member_ti->hooks.move != NULL; + copy_hook_required |= member_ti->hooks.copy != NULL; + } + + /* If any hook is required, then create a lifecycle context and configure a + * generic hook that will interpret that context: */ + ecs_rtt_struct_ctx_t *rtt_ctx = flecs_rtt_configure_struct_hooks( + world, + ti, + ctor_hook_required, + dtor_hook_required, + move_hook_required, + copy_hook_required); + + if (!rtt_ctx) { + return; /* no hooks required */ + } + + /* At least a hook was configured, therefore examine each struct member to + * build the vector of calls that will then be executed by the generic hook + * handler: */ + for (i = 0; i < member_count; i++) { + ecs_member_t *m = &members[i]; + const ecs_type_info_t *member_ti = ecs_get_type_info(world, m->type); + if (ctor_hook_required) { + ecs_rtt_call_data_t *ctor_data = + ecs_vec_append_t(NULL, &rtt_ctx->vctor, ecs_rtt_call_data_t); + ctor_data->count = m->count; + ctor_data->offset = m->offset; + ctor_data->type_info = member_ti; + if (member_ti->hooks.ctor) { + ctor_data->hook.xtor = member_ti->hooks.ctor; + } else { + ctor_data->hook.xtor = flecs_default_ctor; + } + } + if (dtor_hook_required && member_ti->hooks.dtor) { + ecs_rtt_call_data_t *dtor_data = + ecs_vec_append_t(NULL, &rtt_ctx->vdtor, ecs_rtt_call_data_t); + dtor_data->count = m->count; + dtor_data->offset = m->offset; + dtor_data->type_info = member_ti; + dtor_data->hook.xtor = member_ti->hooks.dtor; + } + if (move_hook_required) { + ecs_rtt_call_data_t *move_data = + ecs_vec_append_t(NULL, &rtt_ctx->vmove, ecs_rtt_call_data_t); + move_data->offset = m->offset; + move_data->type_info = member_ti; + move_data->count = m->count; + if (member_ti->hooks.move) { + move_data->hook.move = member_ti->hooks.move; + } else { + move_data->hook.move = flecs_rtt_default_move; + } } - - ptr = ecs_parse_ws_eol(ptr); - - ecs_value_t rvalue = {0}; - const char *rptr = flecs_parse_expr(world, stack, ptr, &rvalue, op, desc); - if (!rptr) { - return NULL; + if (copy_hook_required) { + ecs_rtt_call_data_t *copy_data = + ecs_vec_append_t(NULL, &rtt_ctx->vcopy, ecs_rtt_call_data_t); + copy_data->offset = m->offset; + copy_data->type_info = member_ti; + copy_data->count = m->count; + if (member_ti->hooks.copy) { + copy_data->hook.copy = member_ti->hooks.copy; + } else { + copy_data->hook.copy = flecs_rtt_default_copy; + } } + } +} - if (flecs_binary_expr_do(world, stack, name, expr, ptr, - lvalue, &rvalue, result, op)) - { - return NULL; - } +/* + * + * RTT array support + * + */ - ptr = rptr; +static +void flecs_rtt_free_lifecycle_array_ctx( + void *ctx) +{ + if (!ctx) { + return; + } - ecs_expr_oper_t right_op; - flecs_str_to_expr_oper(rptr, &right_op); - if (right_op > left_op) { - if (result_type) { - /* If result was initialized, preserve its value */ - lvalue->type = result->type; - lvalue->ptr = flecs_expr_value_new(stack, lvalue->type); - ecs_value_copy(world, lvalue->type, lvalue->ptr, result->ptr); - continue; - } else { - /* Otherwise move result to lvalue */ - *lvalue = *result; - ecs_os_zeromem(result); - continue; - } - } + ecs_os_free(ctx); +} - break; - } while (true); +/* Generic array constructor. It will invoke the constructor of the underlying + * type for all the elements */ +static +void flecs_rtt_array_ctor( + void *ptr, + int32_t count, /* note: "count" is how many arrays to initialize, not how + many elements are in the array */ + const ecs_type_info_t *type_info) +{ + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_xtor_t ctor = rtt_ctx->type_info->hooks.ctor; + int i; + for (i = 0; i < count; i++) { + void *arr = ECS_ELEM(ptr, type_info->size, i); + ctor(arr, rtt_ctx->elem_count, rtt_ctx->type_info); + } +} - return ptr; +/* Generic array constructor. It will invoke the destructor of the underlying + * type for all the elements */ +static +void flecs_rtt_array_dtor( + void *ptr, + int32_t count, /* note: "count" is how many arrays to destroy, not how + many elements are in the array */ + const ecs_type_info_t *type_info) +{ + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_xtor_t dtor = rtt_ctx->type_info->hooks.dtor; + int i; + for (i = 0; i < count; i++) { + void *arr = ECS_ELEM(ptr, type_info->size, i); + dtor(arr, rtt_ctx->elem_count, rtt_ctx->type_info); + } +} + +/* Generic array move hook. It will invoke the move hook of the underlying + * type for all the elements */ +static +void flecs_rtt_array_move( + void *dst_ptr, + void *src_ptr, + int32_t count, /* note: "count" is how many arrays to move, not how + many elements are in the array */ + const ecs_type_info_t *type_info) +{ + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_move_t move = rtt_ctx->type_info->hooks.move; + int i; + for (i = 0; i < count; i++) { + void *src_arr = ECS_ELEM(src_ptr, type_info->size, i); + void *dst_arr = ECS_ELEM(dst_ptr, type_info->size, i); + move(dst_arr, src_arr, rtt_ctx->elem_count, rtt_ctx->type_info); + } } +/* Generic array copy hook. It will invoke the copy hook of the underlying + * type for all the elements */ static -const char* flecs_funccall_parse( - ecs_world_t *world, - ecs_value_stack_t *stack, - const char *name, - const char *expr, - const char *ptr, - char *token, - ecs_meta_cursor_t *cur, - ecs_value_t *value, - bool isvar, - const ecs_parse_expr_desc_t *desc) +void flecs_rtt_array_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, /* note: "count" is how many arrays to copy, not how + many elements are in the array */ + const ecs_type_info_t *type_info) { - char *sep = strrchr(token, '.'); - if (!sep) { - ecs_parser_error(name, expr, ptr - expr, - "missing object for function call '%s'", token); - return NULL; + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_copy_t copy = rtt_ctx->type_info->hooks.copy; + int i; + for (i = 0; i < count; i++) { + const void *src_arr = ECS_ELEM(src_ptr, type_info->size, i); + void *dst_arr = ECS_ELEM(dst_ptr, type_info->size, i); + copy(dst_arr, src_arr, rtt_ctx->elem_count, rtt_ctx->type_info); } +} - sep[0] = '\0'; - const char *function = sep + 1; +/* Checks if an array's underlying type has hooks installed. If so, it generates + * and installs required hooks for the array type itself. These hooks will + * invoke the underlying type's hook for each element in the array. */ +static +void flecs_rtt_init_default_hooks_array( + ecs_world_t *world, + ecs_entity_t component) +{ + const EcsArray *array_info = ecs_get(world, component, EcsArray); + ecs_assert(array_info != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *array_ti = + ecs_get_type_info(world, array_info->type); + bool ctor_hook_required = + array_ti->hooks.ctor && array_ti->hooks.ctor != flecs_default_ctor; + bool dtor_hook_required = array_ti->hooks.dtor != NULL; + bool move_hook_required = array_ti->hooks.move != NULL; + bool copy_hook_required = array_ti->hooks.copy != NULL; - if (!isvar) { - if (ecs_meta_set_string(cur, token) != 0) { - goto error; - } - } else { - const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, token); - ecs_meta_set_value(cur, &var->value); + if (!ctor_hook_required && !dtor_hook_required && !move_hook_required && + !copy_hook_required) { + return; /* no hooks required */ } - do { - if (!function[0]) { - ecs_parser_error(name, expr, ptr - expr, - "missing function name for function call '%s'", token); - return NULL; - } - - if (flecs_meta_call(world, stack, name, expr, ptr, cur, - function) != 0) - { - goto error; - } - - ecs_entity_t type = ecs_meta_get_type(cur); - value->ptr = ecs_meta_get_ptr(cur); - value->type = type; + ecs_rtt_array_ctx_t *rtt_ctx = ecs_os_malloc_t(ecs_rtt_array_ctx_t); + rtt_ctx->type_info = array_ti; + rtt_ctx->elem_count = array_info->count; + ecs_type_hooks_t hooks = *ecs_get_hooks_id(world, component); + if (hooks.lifecycle_ctx_free) { + hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); + } - ptr += 2; - if (ptr[0] != '.') { - break; - } + hooks.lifecycle_ctx = rtt_ctx; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_array_ctx; - ptr ++; - char *paren = strchr(ptr, '('); - if (!paren) { - break; - } + if (ctor_hook_required) { + hooks.ctor = flecs_rtt_array_ctor; + } - ecs_size_t len = flecs_ito(int32_t, paren - ptr); - if (len >= ECS_MAX_TOKEN_SIZE) { - ecs_parser_error(name, expr, ptr - expr, - "token exceeds maximum token size"); - goto error; - } + if (dtor_hook_required) { + hooks.dtor = flecs_rtt_array_dtor; + } - ecs_os_strncpy(token, ptr, len); - token[len] = '\0'; - function = token; + if (move_hook_required) { + hooks.move = flecs_rtt_array_move; + } - ptr = paren; - } while (true); + if (copy_hook_required) { + hooks.copy = flecs_rtt_array_copy; + } - return ptr; -error: - return NULL; + ecs_set_hooks_id(world, component, &hooks); } +/* + * + * RTT vector support + * + */ + static -const char* flecs_parse_expr( - ecs_world_t *world, - ecs_value_stack_t *stack, - const char *ptr, - ecs_value_t *value, - ecs_expr_oper_t left_op, - const ecs_parse_expr_desc_t *desc) +void flecs_rtt_free_lifecycle_vector_ctx( + void *ctx) { - ecs_assert(value != NULL, ECS_INTERNAL_ERROR, NULL); - char token[ECS_MAX_TOKEN_SIZE]; - int depth = 0; - ecs_value_t result = {0}; - ecs_meta_cursor_t cur = {0}; - const char *name = desc ? desc->name : NULL; - const char *expr = desc ? desc->expr : NULL; - token[0] = '\0'; - expr = expr ? expr : ptr; - - ptr = ecs_parse_ws_eol(ptr); - - /* Check for postfix operators */ - ecs_expr_oper_t unary_op = EcsExprOperUnknown; - if (ptr[0] == '-' && !isdigit(ptr[1])) { - unary_op = EcsMin; - ptr = ecs_parse_ws_eol(ptr + 1); + if (!ctx) { + return; } - /* Initialize storage and cursor. If expression starts with a '(' storage - * will be initialized by a nested expression */ - if (ptr[0] != '(') { - ecs_entity_t type = flecs_parse_discover_type( - world, name, expr, ptr, value->type, desc); - if (!type) { - return NULL; - } + ecs_os_free(ctx); +} - result.type = type; - if (type != value->type) { - result.ptr = flecs_expr_value_new(stack, type); - } else { - result.ptr = value->ptr; - } +/* Generic vector constructor. Makes sure the vector structure is initialized to + * 0 elements */ +static +void flecs_rtt_vector_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + int i; + for (i = 0; i < count; i++) { + ecs_vec_t *vec = ECS_ELEM(ptr, type_info->size, i); + ecs_vec_init(NULL, vec, rtt_ctx->type_info->size, 0); + } +} - cur = ecs_meta_cursor(world, result.type, result.ptr); - if (!cur.valid) { - return NULL; +/* Generic vector destructor. It will invoke the destructor for each element of + * the vector and finalize resources associated to the vector itself. */ +static +void flecs_rtt_vector_dtor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_xtor_t dtor = rtt_ctx->type_info->hooks.dtor; + int i; + for (i = 0; i < count; i++) { + ecs_vec_t *vec = ECS_ELEM(ptr, type_info->size, i); + int32_t num_elements = ecs_vec_count(vec); + if (dtor && num_elements) { + dtor(ecs_vec_first(vec), num_elements, rtt_ctx->type_info); } - - cur.lookup_action = desc ? desc->lookup_action : NULL; - cur.lookup_ctx = desc ? desc->lookup_ctx : NULL; + ecs_vec_fini(NULL, vec, rtt_ctx->type_info->size); } +} - /* Loop that parses all values in a value scope */ - while ((ptr = ecs_parse_expr_token(name, expr, ptr, token))) { - /* Used to track of the result of the parsed token can be used as the - * lvalue for a binary expression */ - bool is_lvalue = false; - bool newline = false; +/* Generic vector move hook. */ +static +void flecs_rtt_vector_move( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + flecs_rtt_vector_dtor(dst_ptr, count, type_info); + int i; + for (i = 0; i < count; i++) { + ecs_vec_t *src_vec = ECS_ELEM(src_ptr, type_info->size, i); + ecs_vec_t *dst_vec = ECS_ELEM(dst_ptr, type_info->size, i); + *dst_vec = *src_vec; + src_vec->array = NULL; + src_vec->count = 0; + } +} - if (!token[0]) { - /* Comment */ - continue; +/* Generic vector copy hook. It makes a deep copy of vector contents */ +static +void flecs_rtt_vector_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + flecs_rtt_vector_dtor(dst_ptr, count, type_info); + ecs_copy_t copy = rtt_ctx->type_info->hooks.copy + ? rtt_ctx->type_info->hooks.copy + : flecs_rtt_default_copy; + ecs_xtor_t ctor = rtt_ctx->type_info->hooks.ctor + ? rtt_ctx->type_info->hooks.ctor + : flecs_default_ctor; + ecs_xtor_t dtor = rtt_ctx->type_info->hooks.dtor; + int i; + for (i = 0; i < count; i++) { + const ecs_vec_t *src_vec = ECS_ELEM(src_ptr, type_info->size, i); + ecs_vec_t *dst_vec = ECS_ELEM(dst_ptr, type_info->size, i); + int32_t src_count = ecs_vec_count(src_vec); + int32_t dst_count = ecs_vec_count(dst_vec); + if (dtor && dst_count) { + dtor(ecs_vec_first(dst_vec), dst_count, rtt_ctx->type_info); } + ecs_vec_set_count(NULL, dst_vec, rtt_ctx->type_info->size, src_count); + ctor(ecs_vec_first(dst_vec), src_count, rtt_ctx->type_info); + copy( + ecs_vec_first(dst_vec), + ecs_vec_first(src_vec), + src_count, + rtt_ctx->type_info); + } +} - if (!ecs_os_strcmp(token, "(")) { - ecs_value_t temp_result, *out; - if (!depth) { - out = &result; - } else { - temp_result.type = ecs_meta_get_type(&cur); - temp_result.ptr = ecs_meta_get_ptr(&cur); - out = &temp_result; - } - - /* Parenthesis, parse nested expression */ - ptr = flecs_parse_expr(world, stack, ptr, out, EcsLeftParen, desc); - if (ptr[0] != ')') { - ecs_parser_error(name, expr, ptr - expr, - "missing closing parenthesis"); - return NULL; - } - ptr = ecs_parse_ws(ptr + 1); - is_lvalue = true; +/* Generates and installs required hooks for managing the vector and underlying + * type lifecycle. Vectors always have hooks because at the very least the + * vector structure itself must be initialized/destroyed/copied/moved, even if + * empty. */ +static +void flecs_rtt_init_default_hooks_vector( + ecs_world_t *world, + ecs_entity_t component) +{ + const EcsVector *vector_info = ecs_get(world, component, EcsVector); + ecs_assert(vector_info != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *vector_ti = + ecs_get_type_info(world, vector_info->type); + ecs_rtt_vector_ctx_t *rtt_ctx = ecs_os_malloc_t(ecs_rtt_vector_ctx_t); + rtt_ctx->type_info = vector_ti; + ecs_type_hooks_t hooks = *ecs_get_hooks_id(world, component); + if (hooks.lifecycle_ctx_free) { + hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); + } + hooks.lifecycle_ctx = rtt_ctx; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_vector_ctx; + hooks.ctor = flecs_rtt_vector_ctor; + hooks.dtor = flecs_rtt_vector_dtor; + hooks.move = flecs_rtt_vector_move; + hooks.copy = flecs_rtt_vector_copy; + ecs_set_hooks_id(world, component, &hooks); +} + +void flecs_rtt_init_default_hooks( + ecs_iter_t *it) +{ + ecs_world_t *world = it->world; + EcsType *type_field = ecs_field(it, EcsType, 0); - } else if (!ecs_os_strcmp(token, "{")) { - /* Parse nested value scope */ - ecs_entity_t scope_type = ecs_meta_get_type(&cur); + int i; + for (i = 0; i < it->count; i++) { + EcsType *type = &type_field[i]; + if (type->existing) { + continue; /* non-rtt type. Ignore. */ + } + + /* If a component is defined from reflection data, configure appropriate + * default hooks. + * - For trivial types, at least set a default constructor so memory is + * zero-initialized + * - For struct types, configure a hook that in turn calls hooks of + * member types, if those member types have hooks defined themselves. + * - For array types, configure a hook that in turn calls hooks for the + * underlying type, for each element in the array. + * - For vector types, configure hooks to manage the vector structure + * itself, move the vector and deep-copy vector elements + * */ + + ecs_entity_t component = it->entities[i]; + const ecs_type_info_t *ti = ecs_get_type_info(world, component); - depth ++; /* Keep track of depth so we know when parsing is done */ - if (ecs_meta_push(&cur) != 0) { - goto error; + if (ti) { + if (type->kind == EcsStructType) { + flecs_rtt_init_default_hooks_struct(world, component, ti); + } else if (type->kind == EcsArrayType) { + flecs_rtt_init_default_hooks_array(world, component); + } else if (type->kind == EcsVectorType) { + flecs_rtt_init_default_hooks_vector(world, component); } + } - if (ecs_meta_is_collection(&cur)) { - char *path = ecs_get_fullpath(world, scope_type); - ecs_parser_error(name, expr, ptr - expr, - "expected '[' for collection type '%s'", path); - ecs_os_free(path); - return NULL; - } + /* Make sure there is at least a default constructor. This ensures that + * a new component value does not contain uninitialized memory, which + * could cause serializers to crash when for example inspecting string + * fields. */ + if (!ti || !ti->hooks.ctor) { + ecs_set_hooks_id( + world, + component, + &(ecs_type_hooks_t){.ctor = flecs_default_ctor}); } + } +} - else if (!ecs_os_strcmp(token, "}")) { - depth --; +#endif - if (ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, ptr - expr, "expected ']'"); - return NULL; - } +/** + * @file addons/meta/serialized.c + * @brief Serialize type into flat operations array to speed up deserialization. + */ - if (ecs_meta_pop(&cur) != 0) { - goto error; - } - } - else if (!ecs_os_strcmp(token, "[")) { - /* Open collection value scope */ - depth ++; - if (ecs_meta_push(&cur) != 0) { - goto error; - } +#ifdef FLECS_META - if (!ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, ptr - expr, "unexpected '['"); - return NULL; - } - } +static +int flecs_meta_serialize_type( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vec_t *ops); - else if (!ecs_os_strcmp(token, "]")) { - depth --; +ecs_meta_type_op_kind_t flecs_meta_primitive_to_op_kind(ecs_primitive_kind_t kind) { + return EcsOpPrimitive + kind; +} - if (!ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, ptr - expr, "expected '}'"); - return NULL; - } +static +ecs_size_t flecs_meta_type_size(ecs_world_t *world, ecs_entity_t type) { + const EcsComponent *comp = ecs_get(world, type, EcsComponent); + ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); + return comp->size; +} - if (ecs_meta_pop(&cur) != 0) { - goto error; - } - } +static +ecs_meta_type_op_t* flecs_meta_ops_add(ecs_vec_t *ops, ecs_meta_type_op_kind_t kind) { + ecs_meta_type_op_t *op = ecs_vec_append_t(NULL, ops, ecs_meta_type_op_t); + op->kind = kind; + op->offset = 0; + op->count = 1; + op->op_count = 1; + op->size = 0; + op->name = NULL; + op->members = NULL; + op->type = 0; + op->member_index = 0; + return op; +} - else if (!ecs_os_strcmp(token, "-")) { - if (unary_op != EcsExprOperUnknown) { - ecs_parser_error(name, expr, ptr - expr, - "unexpected unary operator"); - return NULL; - } - unary_op = EcsMin; - } +static +ecs_meta_type_op_t* flecs_meta_ops_get(ecs_vec_t *ops, int32_t index) { + ecs_meta_type_op_t* op = ecs_vec_get_t(ops, ecs_meta_type_op_t, index); + ecs_assert(op != NULL, ECS_INTERNAL_ERROR, NULL); + return op; +} - else if (!ecs_os_strcmp(token, ",")) { - /* Move to next field */ - if (ecs_meta_next(&cur) != 0) { - goto error; - } - } +static +int flecs_meta_serialize_primitive( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vec_t *ops) +{ + const EcsPrimitive *ptr = ecs_get(world, type, EcsPrimitive); + if (!ptr) { + char *name = ecs_get_path(world, type); + ecs_err("entity '%s' is not a primitive type", name); + ecs_os_free(name); + return -1; + } - else if (!ecs_os_strcmp(token, "null")) { - if (ecs_meta_set_null(&cur) != 0) { - goto error; - } + ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, flecs_meta_primitive_to_op_kind(ptr->kind)); + op->offset = offset; + op->type = type; + op->size = flecs_meta_type_size(world, type); + return 0; +} - is_lvalue = true; - } +static +int flecs_meta_serialize_enum( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vec_t *ops) +{ + (void)world; + + ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpEnum); + op->offset = offset; + op->type = type; + op->size = ECS_SIZEOF(ecs_i32_t); + return 0; +} - else if (token[0] == '\"') { - /* Regular string */ - if (ecs_meta_set_string_literal(&cur, token) != 0) { - goto error; - } +static +int flecs_meta_serialize_bitmask( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vec_t *ops) +{ + (void)world; + + ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpBitmask); + op->offset = offset; + op->type = type; + op->size = ECS_SIZEOF(ecs_u32_t); + return 0; +} - is_lvalue = true; - } +static +int flecs_meta_serialize_array( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vec_t *ops) +{ + (void)world; - else if (!ecs_os_strcmp(token, "`")) { - /* Multiline string */ - if (!(ptr = flecs_parse_multiline_string(&cur, name, expr, ptr))) { - goto error; - } + ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpArray); + op->offset = offset; + op->type = type; + op->size = flecs_meta_type_size(world, type); + return 0; +} - is_lvalue = true; +static +int flecs_meta_serialize_array_component( + ecs_world_t *world, + ecs_entity_t type, + ecs_vec_t *ops) +{ + const EcsArray *ptr = ecs_get(world, type, EcsArray); + if (!ptr) { + return -1; /* Should never happen, will trigger internal error */ + } - } else if (token[0] == '$') { - /* Variable */ - if (!desc || !desc->vars) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved variable '%s' (no variable scope)", token); - return NULL; - } + if (flecs_meta_serialize_type(world, ptr->type, 0, ops) != 0) { + return -1; + } - if (!token[1]) { - /* Empty name means default to assigned member */ - const char *member = ecs_meta_get_member(&cur); - if (!member) { - ecs_parser_error(name, expr, ptr - expr, - "invalid default variable outside member assignment"); - return NULL; - } - ecs_os_strcpy(&token[1], member); - } + ecs_meta_type_op_t *first = ecs_vec_first(ops); + first->count = ptr->count; + return 0; +} - const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, &token[1]); - if (!var) { - if (ptr[0] == '(') { - /* Function */ - ptr = flecs_funccall_parse(world, stack, name, expr, ptr, - &token[1], &cur, &result, true, desc); - if (!ptr) { - goto error; - } - } else { - ecs_value_t v = flecs_dotresolve_var(world, desc->vars, &token[1]); - if (!v.ptr) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved variable '%s'", token); - return NULL; - } else { - ecs_meta_set_value(&cur, &v); - } - } - } else { - ecs_meta_set_value(&cur, &var->value); - } - is_lvalue = true; +static +int flecs_meta_serialize_vector( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vec_t *ops) +{ + (void)world; + ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpVector); + op->offset = offset; + op->type = type; + op->size = flecs_meta_type_size(world, type); + return 0; +} - } else { - const char *tptr = ecs_parse_ws(ptr); - for (; ptr != tptr; ptr ++) { - if (ptr[0] == '\n') { - newline = true; - } - } +static +int flecs_meta_serialize_custom_type( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vec_t *ops) +{ + (void)world; + ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpOpaque); + op->offset = offset; + op->type = type; + op->size = flecs_meta_type_size(world, type); + return 0; +} - if (ptr[0] == ':') { - /* Member assignment */ - ptr ++; - if (ecs_meta_dotmember(&cur, token) != 0) { - goto error; - } - } else if (ptr[0] == '(') { - /* Function */ - ptr = flecs_funccall_parse(world, stack, name, expr, ptr, - token, &cur, &result, false, desc); - if (!ptr) { - goto error; - } - } else { - if (ecs_meta_set_string(&cur, token) != 0) { - goto error; - } - } +static +int flecs_meta_serialize_struct( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vec_t *ops) +{ + const EcsStruct *ptr = ecs_get(world, type, EcsStruct); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); - is_lvalue = true; - } + int32_t cur, first = ecs_vec_count(ops); + ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpPush); + op->offset = offset; + op->type = type; + op->size = flecs_meta_type_size(world, type); - /* If lvalue was parsed, apply operators. Expressions cannot start - * directly after a newline character. */ - if (is_lvalue && !newline) { - if (unary_op != EcsExprOperUnknown) { - if (unary_op == EcsMin) { - int64_t v = -1; - ecs_value_t lvalue = {.type = ecs_id(ecs_i64_t), .ptr = &v}; - ecs_value_t *out, rvalue, temp_out = {0}; + ecs_member_t *members = ecs_vec_first(&ptr->members); + int32_t i, count = ecs_vec_count(&ptr->members); - if (!depth) { - rvalue = result; - ecs_os_zeromem(&result); - out = &result; - } else { - ecs_entity_t cur_type = ecs_meta_get_type(&cur); - void *cur_ptr = ecs_meta_get_ptr(&cur); - rvalue.type = cur_type; - rvalue.ptr = cur_ptr; - temp_out.type = cur_type; - temp_out.ptr = cur_ptr; - out = &temp_out; - } + ecs_hashmap_t *member_index = NULL; + if (count) { + op->members = member_index = flecs_name_index_new( + world, &world->allocator); + } - flecs_binary_expr_do(world, stack, name, expr, ptr, &lvalue, - &rvalue, out, EcsMul); - } - unary_op = 0; - } + for (i = 0; i < count; i ++) { + ecs_member_t *member = &members[i]; - ecs_expr_oper_t right_op; - flecs_str_to_expr_oper(ptr, &right_op); - if (right_op) { - /* This is a binary expression, test precedence to determine if - * it should be evaluated here */ - if (flecs_oper_precedence(left_op, right_op) < 0) { - ecs_value_t lvalue; - ecs_value_t *op_result = &result; - ecs_value_t temp_storage; - if (!depth) { - /* Root level value, move result to lvalue storage */ - lvalue = result; - ecs_os_zeromem(&result); - } else { - /* Not a root level value. Move the parsed lvalue to a - * temporary storage, and initialize the result value - * for the binary operation with the current cursor */ - ecs_entity_t cur_type = ecs_meta_get_type(&cur); - void *cur_ptr = ecs_meta_get_ptr(&cur); - lvalue.type = cur_type; - lvalue.ptr = flecs_expr_value_new(stack, cur_type); - ecs_value_copy(world, cur_type, lvalue.ptr, cur_ptr); - temp_storage.type = cur_type; - temp_storage.ptr = cur_ptr; - op_result = &temp_storage; - } + cur = ecs_vec_count(ops); + if (flecs_meta_serialize_type(world, member->type, offset + member->offset, ops) != 0) { + continue; + } - /* Do the binary expression */ - ptr = flecs_binary_expr_parse(world, stack, name, expr, ptr, - &lvalue, op_result, left_op, desc); - if (!ptr) { - return NULL; - } - } - } + op = flecs_meta_ops_get(ops, cur); + if (!op->type) { + op->type = member->type; } - if (!depth) { - /* Reached the end of the root scope */ - break; + if (op->count <= 1) { + op->count = member->count; } - ptr = ecs_parse_ws_eol(ptr); - } + const char *member_name = member->name; + op->name = member_name; + op->op_count = ecs_vec_count(ops) - cur; + op->member_index = i; - if (!value->ptr) { - value->type = result.type; - value->ptr = flecs_expr_value_new(stack, result.type); + flecs_name_index_ensure( + member_index, flecs_ito(uint64_t, cur - first - 1), + member_name, 0, 0); } - if (value->ptr != result.ptr) { - cur = ecs_meta_cursor(world, value->type, value->ptr); - ecs_meta_set_value(&cur, &result); + ecs_meta_type_op_t *pop = flecs_meta_ops_add(ops, EcsOpPop); + pop->type = type; + flecs_meta_ops_get(ops, first)->op_count = ecs_vec_count(ops) - first; + return 0; +} + +static +int flecs_meta_serialize_type( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vec_t *ops) +{ + const EcsType *ptr = ecs_get(world, type, EcsType); + if (!ptr) { + char *path = ecs_get_path(world, type); + ecs_err("missing EcsType for type %s'", path); + ecs_os_free(path); + return -1; } - ecs_assert(value->type != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(value->ptr != NULL, ECS_INTERNAL_ERROR, NULL); + switch(ptr->kind) { + case EcsPrimitiveType: return flecs_meta_serialize_primitive(world, type, offset, ops); + case EcsEnumType: return flecs_meta_serialize_enum(world, type, offset, ops); + case EcsBitmaskType: return flecs_meta_serialize_bitmask(world, type, offset, ops); + case EcsStructType: return flecs_meta_serialize_struct(world, type, offset, ops); + case EcsArrayType: return flecs_meta_serialize_array(world, type, offset, ops); + case EcsVectorType: return flecs_meta_serialize_vector(world, type, offset, ops); + case EcsOpaqueType: return flecs_meta_serialize_custom_type(world, type, offset, ops); + } - return ptr; -error: - return NULL; + return 0; } -const char* ecs_parse_expr( +static +int flecs_meta_serialize_component( ecs_world_t *world, - const char *ptr, - ecs_value_t *value, - const ecs_parse_expr_desc_t *desc) + ecs_entity_t type, + ecs_vec_t *ops) { - /* Prepare storage for temporary values */ - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_value_stack_t stack; - stack.count = 0; - stack.stage = stage; - stack.stack = &stage->allocators.deser_stack; - stack.cursor = flecs_stack_get_cursor(stack.stack); - - /* Parse expression */ - bool storage_provided = value->ptr != NULL; - ptr = flecs_parse_expr(world, &stack, ptr, value, EcsExprOperUnknown, desc); - - /* If no result value was provided, allocate one as we can't return a - * pointer to a temporary storage */ - if (!storage_provided && value->ptr) { - ecs_assert(value->type != 0, ECS_INTERNAL_ERROR, NULL); - void *temp_storage = value->ptr; - value->ptr = ecs_value_new(world, value->type); - ecs_value_move_ctor(world, value->type, value->ptr, temp_storage); + const EcsType *ptr = ecs_get(world, type, EcsType); + if (!ptr) { + char *path = ecs_get_path(world, type); + ecs_err("missing EcsType for type %s'", path); + ecs_os_free(path); + return -1; } - - /* Cleanup temporary values */ - int i; - for (i = 0; i < stack.count; i ++) { - const ecs_type_info_t *ti = stack.values[i].ti; - ecs_assert(ti->hooks.dtor != NULL, ECS_INTERNAL_ERROR, NULL); - ti->hooks.dtor(stack.values[i].ptr, 1, ti); + + if (ptr->kind == EcsArrayType) { + return flecs_meta_serialize_array_component(world, type, ops); + } else { + return flecs_meta_serialize_type(world, type, 0, ops); } - flecs_stack_restore_cursor(stack.stack, stack.cursor); +} - return ptr; +void ecs_meta_type_serialized_init( + ecs_iter_t *it) +{ + ecs_world_t *world = it->world; + + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_vec_t ops; + ecs_vec_init_t(NULL, &ops, ecs_meta_type_op_t, 0); + flecs_meta_serialize_component(world, e, &ops); + + EcsTypeSerializer *ptr = ecs_ensure( + world, e, EcsTypeSerializer); + if (ptr->ops.array) { + ecs_meta_dtor_serialized(ptr); + } + + ptr->ops = ops; + } } #endif /** - * @file expr/serialize.c - * @brief Serialize (component) values to flecs string format. + * @file addons/os_api_impl/os_api_impl.c + * @brief Builtin implementation for OS API. + */ + + +#ifdef FLECS_OS_API_IMPL +#ifdef ECS_TARGET_WINDOWS +/** + * @file addons/os_api_impl/posix_impl.inl + * @brief Builtin Windows implementation for OS API. */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include -#ifdef FLECS_EXPR +typedef struct ecs_win_thread_t { + HANDLE thread; + ecs_os_thread_callback_t callback; + void *arg; +} ecs_win_thread_t; static -int flecs_expr_ser_type( - const ecs_world_t *world, - const ecs_vec_t *ser, - const void *base, - ecs_strbuf_t *str, - bool is_expr); +DWORD flecs_win_thread(void *ptr) { + ecs_win_thread_t *thread = ptr; + thread->callback(thread->arg); + return 0; +} static -int flecs_expr_ser_type_ops( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - const void *base, - ecs_strbuf_t *str, - int32_t in_array, - bool is_expr); +ecs_os_thread_t win_thread_new( + ecs_os_thread_callback_t callback, + void *arg) +{ + ecs_win_thread_t *thread = ecs_os_malloc_t(ecs_win_thread_t); + thread->arg= arg; + thread->callback = callback; + thread->thread = CreateThread( + NULL, 0, (LPTHREAD_START_ROUTINE)flecs_win_thread, thread, 0, NULL); + return (ecs_os_thread_t)(uintptr_t)thread; +} static -int flecs_expr_ser_type_op( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *base, - ecs_strbuf_t *str, - bool is_expr); +void* win_thread_join( + ecs_os_thread_t thr) +{ + ecs_win_thread_t *thread = (ecs_win_thread_t*)(uintptr_t)thr; + DWORD r = WaitForSingleObject(thread->thread, INFINITE); + if (r == WAIT_FAILED) { + ecs_err("win_thread_join: WaitForSingleObject failed"); + } + ecs_os_free(thread); + return NULL; +} static -ecs_primitive_kind_t flecs_expr_op_to_primitive_kind(ecs_meta_type_op_kind_t kind) { - return kind - EcsOpPrimitive; +ecs_os_thread_id_t win_thread_self(void) +{ + return (ecs_os_thread_id_t)GetCurrentThreadId(); } -/* Serialize a primitive value */ static -int flecs_expr_ser_primitive( - const ecs_world_t *world, - ecs_primitive_kind_t kind, - const void *base, - ecs_strbuf_t *str, - bool is_expr) +int32_t win_ainc( + int32_t *count) { - switch(kind) { - case EcsBool: - if (*(const bool*)base) { - ecs_strbuf_appendlit(str, "true"); - } else { - ecs_strbuf_appendlit(str, "false"); - } - break; - case EcsChar: { - char chbuf[3]; - char ch = *(const char*)base; - if (ch) { - ecs_chresc(chbuf, *(const char*)base, '"'); - if (is_expr) ecs_strbuf_appendch(str, '"'); - ecs_strbuf_appendstr(str, chbuf); - if (is_expr) ecs_strbuf_appendch(str, '"'); - } else { - ecs_strbuf_appendch(str, '0'); - } - break; - } - case EcsByte: - ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint8_t*)base)); - break; - case EcsU8: - ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint8_t*)base)); - break; - case EcsU16: - ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint16_t*)base)); - break; - case EcsU32: - ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint32_t*)base)); - break; - case EcsU64: - ecs_strbuf_append(str, "%llu", *(const uint64_t*)base); - break; - case EcsI8: - ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int8_t*)base)); - break; - case EcsI16: - ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int16_t*)base)); - break; - case EcsI32: - ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int32_t*)base)); - break; - case EcsI64: - ecs_strbuf_appendint(str, *(const int64_t*)base); - break; - case EcsF32: - ecs_strbuf_appendflt(str, (double)*(const float*)base, 0); - break; - case EcsF64: - ecs_strbuf_appendflt(str, *(const double*)base, 0); - break; - case EcsIPtr: - ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const intptr_t*)base)); - break; - case EcsUPtr: - ecs_strbuf_append(str, "%u", *(const uintptr_t*)base); - break; - case EcsString: { - const char *value = *ECS_CONST_CAST(const char**, base); - if (value) { - if (!is_expr) { - ecs_strbuf_appendstr(str, value); - } else { - ecs_size_t length = ecs_stresc(NULL, 0, '"', value); - if (length == ecs_os_strlen(value)) { - ecs_strbuf_appendch(str, '"'); - ecs_strbuf_appendstrn(str, value, length); - ecs_strbuf_appendch(str, '"'); - } else { - char *out = ecs_os_malloc(length + 3); - ecs_stresc(out + 1, length, '"', value); - out[0] = '"'; - out[length + 1] = '"'; - out[length + 2] = '\0'; - ecs_strbuf_appendstr_zerocpy(str, out); - } - } - } else { - ecs_strbuf_appendlit(str, "null"); - } - break; - } - case EcsEntity: { - ecs_entity_t e = *(const ecs_entity_t*)base; - if (!e) { - ecs_strbuf_appendch(str, '0'); - } else { - ecs_get_path_w_sep_buf(world, 0, e, ".", NULL, str); - } - break; - } - case EcsId: { - ecs_id_t id = *(const ecs_id_t*)base; - if (!id) { - ecs_strbuf_appendch(str, '0'); - } else { - ecs_id_str_buf(world, id, str); - } - break; - } - default: - ecs_err("invalid primitive kind"); - return -1; - } + return InterlockedIncrement((volatile long*)count); +} - return 0; +static +int32_t win_adec( + int32_t *count) +{ + return InterlockedDecrement((volatile long*)count); } -/* Serialize enumeration */ static -int flecs_expr_ser_enum( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *base, - ecs_strbuf_t *str) +int64_t win_lainc( + int64_t *count) { - const EcsEnum *enum_type = ecs_get(world, op->type, EcsEnum); - ecs_check(enum_type != NULL, ECS_INVALID_PARAMETER, NULL); + return InterlockedIncrement64(count); +} - int32_t val = *(const int32_t*)base; - - /* Enumeration constants are stored in a map that is keyed on the - * enumeration value. */ - ecs_enum_constant_t *c = ecs_map_get_deref(&enum_type->constants, - ecs_enum_constant_t, (ecs_map_key_t)val); - if (!c) { - char *path = ecs_get_fullpath(world, op->type); - ecs_err("value %d is not valid for enum type '%s'", val, path); - ecs_os_free(path); - goto error; +static +int64_t win_ladec( + int64_t *count) +{ + return InterlockedDecrement64(count); +} + +static +ecs_os_mutex_t win_mutex_new(void) { + CRITICAL_SECTION *mutex = ecs_os_malloc_t(CRITICAL_SECTION); + InitializeCriticalSection(mutex); + return (ecs_os_mutex_t)(uintptr_t)mutex; +} + +static +void win_mutex_free( + ecs_os_mutex_t m) +{ + CRITICAL_SECTION *mutex = (CRITICAL_SECTION*)(intptr_t)m; + DeleteCriticalSection(mutex); + ecs_os_free(mutex); +} + +static +void win_mutex_lock( + ecs_os_mutex_t m) +{ + CRITICAL_SECTION *mutex = (CRITICAL_SECTION*)(intptr_t)m; + EnterCriticalSection(mutex); +} + +static +void win_mutex_unlock( + ecs_os_mutex_t m) +{ + CRITICAL_SECTION *mutex = (CRITICAL_SECTION*)(intptr_t)m; + LeaveCriticalSection(mutex); +} + +static +ecs_os_cond_t win_cond_new(void) { + CONDITION_VARIABLE *cond = ecs_os_malloc_t(CONDITION_VARIABLE); + InitializeConditionVariable(cond); + return (ecs_os_cond_t)(uintptr_t)cond; +} + +static +void win_cond_free( + ecs_os_cond_t c) +{ + (void)c; +} + +static +void win_cond_signal( + ecs_os_cond_t c) +{ + CONDITION_VARIABLE *cond = (CONDITION_VARIABLE*)(intptr_t)c; + WakeConditionVariable(cond); +} + +static +void win_cond_broadcast( + ecs_os_cond_t c) +{ + CONDITION_VARIABLE *cond = (CONDITION_VARIABLE*)(intptr_t)c; + WakeAllConditionVariable(cond); +} + +static +void win_cond_wait( + ecs_os_cond_t c, + ecs_os_mutex_t m) +{ + CRITICAL_SECTION *mutex = (CRITICAL_SECTION*)(intptr_t)m; + CONDITION_VARIABLE *cond = (CONDITION_VARIABLE*)(intptr_t)c; + SleepConditionVariableCS(cond, mutex, INFINITE); +} + +static bool win_time_initialized; +static double win_time_freq; +static LARGE_INTEGER win_time_start; +static ULONG win_current_resolution; + +static +void win_time_setup(void) { + if ( win_time_initialized) { + return; } + + win_time_initialized = true; - ecs_strbuf_appendstr(str, ecs_get_name(world, c->constant)); + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&win_time_start); + win_time_freq = (double)freq.QuadPart / 1000000000.0; +} - return 0; -error: - return -1; +static +void win_sleep( + int32_t sec, + int32_t nanosec) +{ + HANDLE timer; + LARGE_INTEGER ft; + + ft.QuadPart = -((int64_t)sec * 10000000 + (int64_t)nanosec / 100); + + timer = CreateWaitableTimer(NULL, TRUE, NULL); + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); } -/* Serialize bitmask */ static -int flecs_expr_ser_bitmask( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *ptr, - ecs_strbuf_t *str) +void win_enable_high_timer_resolution(bool enable) { - const EcsBitmask *bitmask_type = ecs_get(world, op->type, EcsBitmask); - ecs_check(bitmask_type != NULL, ECS_INVALID_PARAMETER, NULL); - uint32_t value = *(const uint32_t*)ptr; + HMODULE hntdll = GetModuleHandle(TEXT("ntdll.dll")); + if (!hntdll) { + return; + } + + union { + LONG (__stdcall *f)( + ULONG desired, BOOLEAN set, ULONG * current); + FARPROC p; + } func; + + func.p = GetProcAddress(hntdll, "NtSetTimerResolution"); + if(!func.p) { + return; + } - ecs_strbuf_list_push(str, "", "|"); + ULONG current, resolution = 10000; /* 1 ms */ - /* Multiple flags can be set at a given time. Iterate through all the flags - * and append the ones that are set. */ - ecs_map_iter_t it = ecs_map_iter(&bitmask_type->constants); - int count = 0; - while (ecs_map_next(&it)) { - ecs_bitmask_constant_t *c = ecs_map_ptr(&it); - ecs_map_key_t key = ecs_map_key(&it); - if ((value & key) == key) { - ecs_strbuf_list_appendstr(str, ecs_get_name(world, c->constant)); - count ++; - value -= (uint32_t)key; - } + if (!enable && win_current_resolution) { + func.f(win_current_resolution, 0, ¤t); + win_current_resolution = 0; + return; + } else if (!enable) { + return; } - if (value != 0) { - /* All bits must have been matched by a constant */ - char *path = ecs_get_fullpath(world, op->type); - ecs_err( - "value for bitmask %s contains bits (%u) that cannot be mapped to constant", - path, value); - ecs_os_free(path); - goto error; + if (resolution == win_current_resolution) { + return; } - if (!count) { - ecs_strbuf_list_appendstr(str, "0"); + if (win_current_resolution) { + func.f(win_current_resolution, 0, ¤t); } - ecs_strbuf_list_pop(str, ""); + if (func.f(resolution, 1, ¤t)) { + /* Try setting a lower resolution */ + resolution *= 2; + if(func.f(resolution, 1, ¤t)) return; + } - return 0; -error: - return -1; + win_current_resolution = resolution; } -/* Serialize elements of a contiguous array */ static -int expr_ser_elements( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - const void *base, - int32_t elem_count, - int32_t elem_size, - ecs_strbuf_t *str, - bool is_array) -{ - ecs_strbuf_list_push(str, "[", ", "); +uint64_t win_time_now(void) { + uint64_t now; - const void *ptr = base; + LARGE_INTEGER qpc_t; + QueryPerformanceCounter(&qpc_t); + now = (uint64_t)((double)qpc_t.QuadPart / win_time_freq); - int i; - for (i = 0; i < elem_count; i ++) { - ecs_strbuf_list_next(str); - if (flecs_expr_ser_type_ops( - world, ops, op_count, ptr, str, is_array, true)) - { - return -1; - } - ptr = ECS_OFFSET(ptr, elem_size); + return now; +} + +static +void win_fini(void) { + if (ecs_os_api.flags_ & EcsOsApiHighResolutionTimer) { + win_enable_high_timer_resolution(false); } +} - ecs_strbuf_list_pop(str, "]"); +void ecs_set_os_api_impl(void) { + ecs_os_set_api_defaults(); - return 0; + ecs_os_api_t api = ecs_os_api; + + api.thread_new_ = win_thread_new; + api.thread_join_ = win_thread_join; + api.thread_self_ = win_thread_self; + api.task_new_ = win_thread_new; + api.task_join_ = win_thread_join; + api.ainc_ = win_ainc; + api.adec_ = win_adec; + api.lainc_ = win_lainc; + api.ladec_ = win_ladec; + api.mutex_new_ = win_mutex_new; + api.mutex_free_ = win_mutex_free; + api.mutex_lock_ = win_mutex_lock; + api.mutex_unlock_ = win_mutex_unlock; + api.cond_new_ = win_cond_new; + api.cond_free_ = win_cond_free; + api.cond_signal_ = win_cond_signal; + api.cond_broadcast_ = win_cond_broadcast; + api.cond_wait_ = win_cond_wait; + api.sleep_ = win_sleep; + api.now_ = win_time_now; + api.fini_ = win_fini; + + win_time_setup(); + + if (ecs_os_api.flags_ & EcsOsApiHighResolutionTimer) { + win_enable_high_timer_resolution(true); + } + + ecs_os_set_api(&api); } +#else +/** + * @file addons/os_api_impl/posix_impl.inl + * @brief Builtin POSIX implementation for OS API. + */ + +#include "pthread.h" + +#if defined(__APPLE__) && defined(__MACH__) +#include +#elif defined(__EMSCRIPTEN__) +#include +#else +#include +#endif + +/* This mutex is used to emulate atomic operations when the gnu builtins are + * not supported. This is probably not very fast but if the compiler doesn't + * support the gnu built-ins, then speed is probably not a priority. */ +#ifndef __GNUC__ +static pthread_mutex_t atomic_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + static -int expr_ser_type_elements( - const ecs_world_t *world, - ecs_entity_t type, - const void *base, - int32_t elem_count, - ecs_strbuf_t *str, - bool is_array) +ecs_os_thread_t posix_thread_new( + ecs_os_thread_callback_t callback, + void *arg) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); + pthread_t *thread = ecs_os_malloc(sizeof(pthread_t)); - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); + if (pthread_create (thread, NULL, callback, arg) != 0) { + ecs_os_abort(); + } - ecs_meta_type_op_t *ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t); - int32_t op_count = ecs_vec_count(&ser->ops); - return expr_ser_elements( - world, ops, op_count, base, elem_count, comp->size, str, is_array); + return (ecs_os_thread_t)(uintptr_t)thread; } -/* Serialize array */ static -int expr_ser_array( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *ptr, - ecs_strbuf_t *str) +void* posix_thread_join( + ecs_os_thread_t thread) { - const EcsArray *a = ecs_get(world, op->type, EcsArray); - ecs_assert(a != NULL, ECS_INTERNAL_ERROR, NULL); - - return expr_ser_type_elements( - world, a->type, ptr, a->count, str, true); + void *arg; + pthread_t *thr = (pthread_t*)(uintptr_t)thread; + pthread_join(*thr, &arg); + ecs_os_free(thr); + return arg; } -/* Serialize vector */ static -int expr_ser_vector( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *base, - ecs_strbuf_t *str) +ecs_os_thread_id_t posix_thread_self(void) { - const ecs_vec_t *value = base; - const EcsVector *v = ecs_get(world, op->type, EcsVector); - ecs_assert(v != NULL, ECS_INTERNAL_ERROR, NULL); - - int32_t count = ecs_vec_count(value); - void *array = ecs_vec_first(value); - - /* Serialize contiguous buffer of vector */ - return expr_ser_type_elements(world, v->type, array, count, str, false); + return (ecs_os_thread_id_t)pthread_self(); } -/* Forward serialization to the different type kinds */ static -int flecs_expr_ser_type_op( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *ptr, - ecs_strbuf_t *str, - bool is_expr) +int32_t posix_ainc( + int32_t *count) { - switch(op->kind) { - case EcsOpPush: - case EcsOpPop: - /* Should not be parsed as single op */ - ecs_throw(ECS_INVALID_PARAMETER, NULL); - break; - case EcsOpEnum: - if (flecs_expr_ser_enum(world, op, ECS_OFFSET(ptr, op->offset), str)) { - goto error; - } - break; - case EcsOpBitmask: - if (flecs_expr_ser_bitmask(world, op, ECS_OFFSET(ptr, op->offset), str)) { - goto error; - } - break; - case EcsOpArray: - if (expr_ser_array(world, op, ECS_OFFSET(ptr, op->offset), str)) { - goto error; - } - break; - case EcsOpVector: - if (expr_ser_vector(world, op, ECS_OFFSET(ptr, op->offset), str)) { - goto error; - } - break; - case EcsOpScope: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpEntity: - case EcsOpId: - case EcsOpString: - case EcsOpOpaque: - if (flecs_expr_ser_primitive(world, flecs_expr_op_to_primitive_kind(op->kind), - ECS_OFFSET(ptr, op->offset), str, is_expr)) - { - /* Unknown operation */ - ecs_err("unknown serializer operation kind (%d)", op->kind); - goto error; - } - break; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + int value; +#ifdef __GNUC__ + value = __sync_add_and_fetch (count, 1); + return value; +#else + if (pthread_mutex_lock(&atomic_mutex)) { + abort(); + } + value = (*count) += 1; + if (pthread_mutex_unlock(&atomic_mutex)) { + abort(); } + return value; +#endif +} - return 0; -error: - return -1; +static +int32_t posix_adec( + int32_t *count) +{ + int32_t value; +#ifdef __GNUC__ + value = __sync_sub_and_fetch (count, 1); + return value; +#else + if (pthread_mutex_lock(&atomic_mutex)) { + abort(); + } + value = (*count) -= 1; + if (pthread_mutex_unlock(&atomic_mutex)) { + abort(); + } + return value; +#endif } -/* Iterate over a slice of the type ops array */ static -int flecs_expr_ser_type_ops( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - const void *base, - ecs_strbuf_t *str, - int32_t in_array, - bool is_expr) +int64_t posix_lainc( + int64_t *count) { - for (int i = 0; i < op_count; i ++) { - ecs_meta_type_op_t *op = &ops[i]; + int64_t value; +#ifdef __GNUC__ + value = __sync_add_and_fetch (count, 1); + return value; +#else + if (pthread_mutex_lock(&atomic_mutex)) { + abort(); + } + value = (*count) += 1; + if (pthread_mutex_unlock(&atomic_mutex)) { + abort(); + } + return value; +#endif +} - if (in_array <= 0) { - if (op->name) { - ecs_strbuf_list_next(str); - ecs_strbuf_append(str, "%s: ", op->name); - } +static +int64_t posix_ladec( + int64_t *count) +{ + int64_t value; +#ifdef __GNUC__ + value = __sync_sub_and_fetch (count, 1); + return value; +#else + if (pthread_mutex_lock(&atomic_mutex)) { + abort(); + } + value = (*count) -= 1; + if (pthread_mutex_unlock(&atomic_mutex)) { + abort(); + } + return value; +#endif +} - int32_t elem_count = op->count; - if (elem_count > 1) { - /* Serialize inline array */ - if (expr_ser_elements(world, op, op->op_count, base, - elem_count, op->size, str, true)) - { - return -1; - } +static +ecs_os_mutex_t posix_mutex_new(void) { + pthread_mutex_t *mutex = ecs_os_malloc(sizeof(pthread_mutex_t)); + if (pthread_mutex_init(mutex, NULL)) { + abort(); + } + return (ecs_os_mutex_t)(uintptr_t)mutex; +} - i += op->op_count - 1; - continue; - } - } +static +void posix_mutex_free( + ecs_os_mutex_t m) +{ + pthread_mutex_t *mutex = (pthread_mutex_t*)(intptr_t)m; + pthread_mutex_destroy(mutex); + ecs_os_free(mutex); +} - switch(op->kind) { - case EcsOpPush: - ecs_strbuf_list_push(str, "{", ", "); - in_array --; - break; - case EcsOpPop: - ecs_strbuf_list_pop(str, "}"); - in_array ++; - break; - case EcsOpArray: - case EcsOpVector: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpScope: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpEntity: - case EcsOpId: - case EcsOpString: - case EcsOpOpaque: - if (flecs_expr_ser_type_op(world, op, base, str, is_expr)) { - goto error; - } - break; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - } +static +void posix_mutex_lock( + ecs_os_mutex_t m) +{ + pthread_mutex_t *mutex = (pthread_mutex_t*)(intptr_t)m; + if (pthread_mutex_lock(mutex)) { + abort(); } - - return 0; -error: - return -1; } -/* Iterate over the type ops of a type */ static -int flecs_expr_ser_type( - const ecs_world_t *world, - const ecs_vec_t *v_ops, - const void *base, - ecs_strbuf_t *str, - bool is_expr) +void posix_mutex_unlock( + ecs_os_mutex_t m) { - ecs_meta_type_op_t *ops = ecs_vec_first_t(v_ops, ecs_meta_type_op_t); - int32_t count = ecs_vec_count(v_ops); - return flecs_expr_ser_type_ops(world, ops, count, base, str, 0, is_expr); + pthread_mutex_t *mutex = (pthread_mutex_t*)(intptr_t)m; + if (pthread_mutex_unlock(mutex)) { + abort(); + } } -int ecs_ptr_to_expr_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *ptr, - ecs_strbuf_t *buf_out) +static +ecs_os_cond_t posix_cond_new(void) { + pthread_cond_t *cond = ecs_os_malloc(sizeof(pthread_cond_t)); + if (pthread_cond_init(cond, NULL)) { + abort(); + } + return (ecs_os_cond_t)(uintptr_t)cond; +} + +static +void posix_cond_free( + ecs_os_cond_t c) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - if (ser == NULL) { - char *path = ecs_get_fullpath(world, type); - ecs_err("cannot serialize value for type '%s'", path); - ecs_os_free(path); - goto error; + pthread_cond_t *cond = (pthread_cond_t*)(intptr_t)c; + if (pthread_cond_destroy(cond)) { + abort(); } + ecs_os_free(cond); +} - if (flecs_expr_ser_type(world, &ser->ops, ptr, buf_out, true)) { - goto error; +static +void posix_cond_signal( + ecs_os_cond_t c) +{ + pthread_cond_t *cond = (pthread_cond_t*)(intptr_t)c; + if (pthread_cond_signal(cond)) { + abort(); } +} - return 0; -error: - return -1; +static +void posix_cond_broadcast( + ecs_os_cond_t c) +{ + pthread_cond_t *cond = (pthread_cond_t*)(intptr_t)c; + if (pthread_cond_broadcast(cond)) { + abort(); + } } -char* ecs_ptr_to_expr( - const ecs_world_t *world, - ecs_entity_t type, - const void* ptr) +static +void posix_cond_wait( + ecs_os_cond_t c, + ecs_os_mutex_t m) { - ecs_strbuf_t str = ECS_STRBUF_INIT; + pthread_cond_t *cond = (pthread_cond_t*)(intptr_t)c; + pthread_mutex_t *mutex = (pthread_mutex_t*)(intptr_t)m; + if (pthread_cond_wait(cond, mutex)) { + abort(); + } +} + +static bool posix_time_initialized; + +#if defined(__APPLE__) && defined(__MACH__) +static mach_timebase_info_data_t posix_osx_timebase; +static uint64_t posix_time_start; +#else +static uint64_t posix_time_start; +#endif - if (ecs_ptr_to_expr_buf(world, type, ptr, &str) != 0) { - ecs_strbuf_reset(&str); - return NULL; +static +void posix_time_setup(void) { + if (posix_time_initialized) { + return; } + + posix_time_initialized = true; - return ecs_strbuf_get(&str); + #if defined(__APPLE__) && defined(__MACH__) + mach_timebase_info(&posix_osx_timebase); + posix_time_start = mach_absolute_time(); + #else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + posix_time_start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; + #endif } -int ecs_ptr_to_str_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *ptr, - ecs_strbuf_t *buf_out) +static +void posix_sleep( + int32_t sec, + int32_t nanosec) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - if (ser == NULL) { - char *path = ecs_get_fullpath(world, type); - ecs_err("cannot serialize value for type '%s'", path); - ecs_os_free(path); - goto error; - } + struct timespec sleepTime; + ecs_assert(sec >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(nanosec >= 0, ECS_INTERNAL_ERROR, NULL); - if (flecs_expr_ser_type(world, &ser->ops, ptr, buf_out, false)) { - goto error; + sleepTime.tv_sec = sec; + sleepTime.tv_nsec = nanosec; + if (nanosleep(&sleepTime, NULL)) { + ecs_err("nanosleep failed"); } +} - return 0; -error: - return -1; +/* prevent 64-bit overflow when computing relative timestamp + see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 +*/ +#if defined(ECS_TARGET_DARWIN) +static +int64_t posix_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { + int64_t q = value / denom; + int64_t r = value % denom; + return q * numer + r * numer / denom; } +#endif -char* ecs_ptr_to_str( - const ecs_world_t *world, - ecs_entity_t type, - const void* ptr) -{ - ecs_strbuf_t str = ECS_STRBUF_INIT; +static +uint64_t posix_time_now(void) { + ecs_assert(posix_time_initialized != 0, ECS_INTERNAL_ERROR, NULL); - if (ecs_ptr_to_str_buf(world, type, ptr, &str) != 0) { - ecs_strbuf_reset(&str); - return NULL; - } + uint64_t now; - return ecs_strbuf_get(&str); + #if defined(ECS_TARGET_DARWIN) + now = (uint64_t) posix_int64_muldiv( + (int64_t)mach_absolute_time(), + (int64_t)posix_osx_timebase.numer, + (int64_t)posix_osx_timebase.denom); + #elif defined(__EMSCRIPTEN__) + now = (long long)(emscripten_get_now() * 1000.0 * 1000); + #else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + now = ((uint64_t)ts.tv_sec * 1000 * 1000 * 1000 + (uint64_t)ts.tv_nsec); + #endif + + return now; } -int ecs_primitive_to_expr_buf( - const ecs_world_t *world, - ecs_primitive_kind_t kind, - const void *base, - ecs_strbuf_t *str) -{ - return flecs_expr_ser_primitive(world, kind, base, str, true); +void ecs_set_os_api_impl(void) { + ecs_os_set_api_defaults(); + + ecs_os_api_t api = ecs_os_api; + + api.thread_new_ = posix_thread_new; + api.thread_join_ = posix_thread_join; + api.thread_self_ = posix_thread_self; + api.task_new_ = posix_thread_new; + api.task_join_ = posix_thread_join; + api.ainc_ = posix_ainc; + api.adec_ = posix_adec; + api.lainc_ = posix_lainc; + api.ladec_ = posix_ladec; + api.mutex_new_ = posix_mutex_new; + api.mutex_free_ = posix_mutex_free; + api.mutex_lock_ = posix_mutex_lock; + api.mutex_unlock_ = posix_mutex_unlock; + api.cond_new_ = posix_cond_new; + api.cond_free_ = posix_cond_free; + api.cond_signal_ = posix_cond_signal; + api.cond_broadcast_ = posix_cond_broadcast; + api.cond_wait_ = posix_cond_wait; + api.sleep_ = posix_sleep; + api.now_ = posix_time_now; + + posix_time_setup(); + + ecs_os_set_api(&api); } +#endif #endif /** - * @file expr/utils.c - * @brief String parsing utilities. + * @file addons/pipeline/pipeline.c + * @brief Functions for building and running pipelines. */ -#ifdef FLECS_EXPR - -#include +#ifdef FLECS_PIPELINE -char* ecs_chresc( - char *out, - char in, - char delimiter) +static void flecs_pipeline_free( + ecs_pipeline_state_t *p) { - char *bptr = out; - switch(in) { - case '\a': - *bptr++ = '\\'; - *bptr = 'a'; - break; - case '\b': - *bptr++ = '\\'; - *bptr = 'b'; - break; - case '\f': - *bptr++ = '\\'; - *bptr = 'f'; - break; - case '\n': - *bptr++ = '\\'; - *bptr = 'n'; - break; - case '\r': - *bptr++ = '\\'; - *bptr = 'r'; - break; - case '\t': - *bptr++ = '\\'; - *bptr = 't'; - break; - case '\v': - *bptr++ = '\\'; - *bptr = 'v'; - break; - case '\\': - *bptr++ = '\\'; - *bptr = '\\'; - break; - default: - if (in == delimiter) { - *bptr++ = '\\'; - *bptr = delimiter; - } else { - *bptr = in; - } - break; + if (p) { + ecs_world_t *world = p->query->world; + ecs_allocator_t *a = &world->allocator; + ecs_vec_fini_t(a, &p->ops, ecs_pipeline_op_t); + ecs_vec_fini_t(a, &p->systems, ecs_entity_t); + ecs_os_free(p->iters); + ecs_query_fini(p->query); + ecs_os_free(p); } +} - *(++bptr) = '\0'; +static ECS_MOVE(EcsPipeline, dst, src, { + flecs_pipeline_free(dst->state); + dst->state = src->state; + src->state = NULL; +}) - return bptr; -} +static ECS_DTOR(EcsPipeline, ptr, { + flecs_pipeline_free(ptr->state); +}) -const char* ecs_chrparse( - const char *in, - char *out) +typedef enum ecs_write_kind_t { + WriteStateNone = 0, + WriteStateToStage, +} ecs_write_kind_t; + +typedef struct ecs_write_state_t { + bool write_barrier; + ecs_map_t ids; + ecs_map_t wildcard_ids; +} ecs_write_state_t; + +static +ecs_write_kind_t flecs_pipeline_get_write_state( + ecs_write_state_t *write_state, + ecs_id_t id) { - const char *result = in + 1; - char ch; + ecs_write_kind_t result = WriteStateNone; - if (in[0] == '\\') { - result ++; + if (write_state->write_barrier) { + /* Any component could have been written */ + return WriteStateToStage; + } - switch(in[1]) { - case 'a': - ch = '\a'; - break; - case 'b': - ch = '\b'; - break; - case 'f': - ch = '\f'; - break; - case 'n': - ch = '\n'; - break; - case 'r': - ch = '\r'; - break; - case 't': - ch = '\t'; - break; - case 'v': - ch = '\v'; - break; - case '\\': - ch = '\\'; - break; - case '"': - ch = '"'; - break; - case '0': - ch = '\0'; - break; - case ' ': - ch = ' '; - break; - case '$': - ch = '$'; - break; - default: - goto error; + if (id == EcsWildcard) { + /* Using a wildcard for id indicates read barrier. Return true if any + * components could have been staged */ + if (ecs_map_count(&write_state->ids) || + ecs_map_count(&write_state->wildcard_ids)) + { + return WriteStateToStage; + } + } + + if (!ecs_id_is_wildcard(id)) { + if (ecs_map_get(&write_state->ids, id)) { + result = WriteStateToStage; } } else { - ch = in[0]; + ecs_map_iter_t it = ecs_map_iter(&write_state->ids); + while (ecs_map_next(&it)) { + if (ecs_id_match(ecs_map_key(&it), id)) { + return WriteStateToStage; + } + } } - if (out) { - *out = ch; + if (ecs_map_count(&write_state->wildcard_ids)) { + ecs_map_iter_t it = ecs_map_iter(&write_state->wildcard_ids); + while (ecs_map_next(&it)) { + if (ecs_id_match(id, ecs_map_key(&it))) { + return WriteStateToStage; + } + } } return result; -error: - return NULL; } -ecs_size_t ecs_stresc( - char *out, - ecs_size_t n, - char delimiter, - const char *in) +static +void flecs_pipeline_set_write_state( + ecs_write_state_t *write_state, + ecs_id_t id) { - const char *ptr = in; - char ch, *bptr = out, buff[3]; - ecs_size_t written = 0; - while ((ch = *ptr++)) { - if ((written += (ecs_size_t)(ecs_chresc( - buff, ch, delimiter) - buff)) <= n) - { - /* If size != 0, an out buffer must be provided. */ - ecs_check(out != NULL, ECS_INVALID_PARAMETER, NULL); - *bptr++ = buff[0]; - if ((ch = buff[1])) { - *bptr = ch; - bptr++; - } - } + if (id == EcsWildcard) { + /* If writing to wildcard, flag all components as written */ + write_state->write_barrier = true; + return; } - if (bptr) { - while (written < n) { - *bptr = '\0'; - bptr++; - written++; - } + ecs_map_t *ids; + if (ecs_id_is_wildcard(id)) { + ids = &write_state->wildcard_ids; + } else { + ids = &write_state->ids; } - return written; -error: - return 0; + + ecs_map_ensure(ids, id)[0] = true; } -char* ecs_astresc( - char delimiter, - const char *in) +static +void flecs_pipeline_reset_write_state( + ecs_write_state_t *write_state) { - if (!in) { - return NULL; - } - - ecs_size_t len = ecs_stresc(NULL, 0, delimiter, in); - char *out = ecs_os_malloc_n(char, len + 1); - ecs_stresc(out, len, delimiter, in); - out[len] = '\0'; - return out; + ecs_map_clear(&write_state->ids); + ecs_map_clear(&write_state->wildcard_ids); + write_state->write_barrier = false; } static -const char* flecs_parse_var_name( - const char *ptr, - char *token_out) +bool flecs_pipeline_check_term( + ecs_world_t *world, + ecs_term_t *term, + bool is_active, + ecs_write_state_t *write_state) { - char ch, *bptr = token_out; + (void)world; - while ((ch = *ptr)) { - if (bptr - token_out > ECS_MAX_TOKEN_SIZE) { - goto error; - } + ecs_term_ref_t *src = &term->src; + if (term->inout == EcsInOutNone || term->inout == EcsInOutFilter) { + return false; + } - if (isalpha(ch) || isdigit(ch) || ch == '_') { - *bptr = ch; - bptr ++; - ptr ++; + ecs_id_t id = term->id; + int16_t oper = term->oper; + int16_t inout = term->inout; + bool from_any = ecs_term_match_0(term); + bool from_this = ecs_term_match_this(term); + bool is_shared = !from_any && (!from_this || !(src->id & EcsSelf)); + + ecs_write_kind_t ws = flecs_pipeline_get_write_state(write_state, id); + + if (from_this && ws >= WriteStateToStage) { + /* A staged write could have happened for an id that's matched on the + * main storage. Even if the id isn't read, still insert a merge so that + * a write to the main storage after the staged write doesn't get + * overwritten. */ + return true; + } + + if (inout == EcsInOutDefault) { + if (from_any) { + /* If no inout kind is specified for terms without a source, this is + * not interpreted as a read/write annotation but just a (component) + * id that's passed to a system. */ + return false; + } else if (is_shared) { + inout = EcsIn; } else { - break; + /* Default for owned terms is InOut */ + inout = EcsInOut; } } - if (bptr == token_out) { - goto error; + if (oper == EcsNot && inout == EcsOut) { + /* If a Not term is combined with Out, it signals that the system + * intends to add a component that the entity doesn't yet have */ + from_any = true; } - *bptr = '\0'; + if (from_any) { + switch(inout) { + case EcsOut: + case EcsInOut: + if (is_active) { + /* Only flag component as written if system is active */ + flecs_pipeline_set_write_state(write_state, id); + } + break; + case EcsInOutDefault: + case EcsInOutNone: + case EcsInOutFilter: + case EcsIn: + break; + } - return ptr; -error: - return NULL; + switch(inout) { + case EcsIn: + case EcsInOut: + if (ws == WriteStateToStage) { + /* If a system does a get/ensure, the component is fetched from + * the main store so it must be merged first */ + return true; + } + /* fall through */ + case EcsInOutDefault: + case EcsInOutNone: + case EcsInOutFilter: + case EcsOut: + break; + } + } + + return false; } static -const char* flecs_parse_interpolated_str( - const char *ptr, - char *token_out) +bool flecs_pipeline_check_terms( + ecs_world_t *world, + ecs_query_t *query, + bool is_active, + ecs_write_state_t *ws) { - char ch, *bptr = token_out; - - while ((ch = *ptr)) { - if (bptr - token_out > ECS_MAX_TOKEN_SIZE) { - goto error; - } + bool needs_merge = false; + ecs_term_t *terms = query->terms; + int32_t t, term_count = query->term_count; - if (ch == '\\') { - if (ptr[1] == '}') { - *bptr = '}'; - bptr ++; - ptr += 2; - continue; - } + /* Check This terms first. This way if a term indicating writing to a stage + * was added before the term, it won't cause merging. */ + for (t = 0; t < term_count; t ++) { + ecs_term_t *term = &terms[t]; + if (ecs_term_match_this(term)) { + needs_merge |= flecs_pipeline_check_term(world, term, is_active, ws); } + } - if (ch != '}') { - *bptr = ch; - bptr ++; - ptr ++; - } else { - ptr ++; - break; + /* Now check staged terms */ + for (t = 0; t < term_count; t ++) { + ecs_term_t *term = &terms[t]; + if (!ecs_term_match_this(term)) { + needs_merge |= flecs_pipeline_check_term(world, term, is_active, ws); } } - if (bptr == token_out) { - goto error; + return needs_merge; +} + +static +EcsPoly* flecs_pipeline_term_system( + ecs_iter_t *it) +{ + int32_t index = ecs_table_get_column_index( + it->real_world, it->table, flecs_poly_id(EcsSystem)); + ecs_assert(index != -1, ECS_INTERNAL_ERROR, NULL); + EcsPoly *poly = ecs_table_get_column(it->table, index, it->offset); + ecs_assert(poly != NULL, ECS_INTERNAL_ERROR, NULL); + return poly; +} + +static +bool flecs_pipeline_build( + ecs_world_t *world, + ecs_pipeline_state_t *pq) +{ + ecs_iter_t it = ecs_query_iter(world, pq->query); + + int32_t new_match_count = ecs_query_match_count(pq->query); + if (pq->match_count == new_match_count) { + /* No need to rebuild the pipeline */ + ecs_iter_fini(&it); + return false; } - *bptr = '\0'; + world->info.pipeline_build_count_total ++; + pq->rebuild_count ++; + + ecs_allocator_t *a = &world->allocator; + ecs_pipeline_op_t *op = NULL; + ecs_write_state_t ws = {0}; + ecs_map_init(&ws.ids, a); + ecs_map_init(&ws.wildcard_ids, a); + + ecs_vec_reset_t(a, &pq->ops, ecs_pipeline_op_t); + ecs_vec_reset_t(a, &pq->systems, ecs_entity_t); + + bool multi_threaded = false; + bool immediate = false; + bool first = true; + + /* Iterate systems in pipeline, add ops for running / merging */ + while (ecs_query_next(&it)) { + EcsPoly *poly = flecs_pipeline_term_system(&it); + bool is_active = ecs_table_get_type_index( + world, it.table, EcsEmpty) == -1; + + int32_t i; + for (i = 0; i < it.count; i ++) { + flecs_poly_assert(poly[i].poly, ecs_system_t); + ecs_system_t *sys = (ecs_system_t*)poly[i].poly; + ecs_query_t *q = sys->query; - return ptr; -error: - return NULL; -} + bool needs_merge = false; + needs_merge = flecs_pipeline_check_terms( + world, q, is_active, &ws); -char* ecs_interpolate_string( - ecs_world_t *world, - const char *str, - const ecs_vars_t *vars) -{ - char token[ECS_MAX_TOKEN_SIZE]; - ecs_strbuf_t result = ECS_STRBUF_INIT; - const char *ptr; - char ch; + if (is_active) { + if (first) { + multi_threaded = sys->multi_threaded; + immediate = sys->immediate; + first = false; + } - for(ptr = str; (ch = *ptr); ptr++) { - if (ch == '\\') { - ptr ++; - if (ptr[0] == '$') { - ecs_strbuf_appendch(&result, '$'); - continue; - } - if (ptr[0] == '\\') { - ecs_strbuf_appendch(&result, '\\'); - continue; - } - if (ptr[0] == '{') { - ecs_strbuf_appendch(&result, '{'); - continue; - } - if (ptr[0] == '}') { - ecs_strbuf_appendch(&result, '}'); - continue; + if (sys->multi_threaded != multi_threaded) { + needs_merge = true; + multi_threaded = sys->multi_threaded; + } + if (sys->immediate != immediate) { + needs_merge = true; + immediate = sys->immediate; + } } - ptr --; - } - if (ch == '$') { - ptr = flecs_parse_var_name(ptr + 1, token); - if (!ptr) { - ecs_parser_error(NULL, str, ptr - str, - "invalid variable name '%s'", ptr); - goto error; + if (immediate) { + needs_merge = true; } - ecs_expr_var_t *var = ecs_vars_lookup(vars, token); - if (!var) { - ecs_parser_error(NULL, str, ptr - str, - "unresolved variable '%s'", token); - goto error; - } + if (needs_merge) { + /* After merge all components will be merged, so reset state */ + flecs_pipeline_reset_write_state(&ws); - if (ecs_ptr_to_str_buf( - world, var->value.type, var->value.ptr, &result)) - { - goto error; - } + /* An inactive system can insert a merge if one of its + * components got written, which could make the system + * active. If this is the only system in the pipeline operation, + * it results in an empty operation when we get here. If that's + * the case, reuse the empty operation for the next op. */ + if (op && op->count) { + op = NULL; + } - ptr --; - } else if (ch == '{') { - ptr = flecs_parse_interpolated_str(ptr + 1, token); - if (!ptr) { - ecs_parser_error(NULL, str, ptr - str, - "invalid interpolated expression"); - goto error; - } + /* Re-evaluate columns to set write flags if system is active. + * If system is inactive, it can't write anything and so it + * should not insert unnecessary merges. */ + needs_merge = false; + if (is_active) { + needs_merge = flecs_pipeline_check_terms( + world, q, true, &ws); + } - ecs_parse_expr_desc_t expr_desc = { - .vars = ECS_CONST_CAST(ecs_vars_t*, vars) - }; - ecs_value_t expr_result = {0}; - if (!ecs_parse_expr(world, token, &expr_result, &expr_desc)) { - goto error; + /* The component states were just reset, so if we conclude that + * another merge is needed something is wrong. */ + ecs_assert(needs_merge == false, ECS_INTERNAL_ERROR, NULL); } - if (ecs_ptr_to_str_buf( - world, expr_result.type, expr_result.ptr, &result)) - { - goto error; + if (!op) { + op = ecs_vec_append_t(a, &pq->ops, ecs_pipeline_op_t); + op->offset = ecs_vec_count(&pq->systems); + op->count = 0; + op->multi_threaded = false; + op->immediate = false; + op->time_spent = 0; + op->commands_enqueued = 0; } - ecs_value_free(world, expr_result.type, expr_result.ptr); - - ptr --; - } else { - ecs_strbuf_appendch(&result, ch); + /* Don't increase count for inactive systems, as they are ignored by + * the query used to run the pipeline. */ + if (is_active) { + ecs_vec_append_t(a, &pq->systems, ecs_entity_t)[0] = + it.entities[i]; + if (!op->count) { + op->multi_threaded = multi_threaded; + op->immediate = immediate; + } + op->count ++; + } } } - return ecs_strbuf_get(&result); -error: - return NULL; -} + if (op && !op->count && ecs_vec_count(&pq->ops) > 1) { + ecs_vec_remove_last(&pq->ops); + } -void ecs_iter_to_vars( - const ecs_iter_t *it, - ecs_vars_t *vars, - int offset) -{ - ecs_check(vars != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!offset || offset < it->count, ECS_INVALID_PARAMETER, NULL); + ecs_map_fini(&ws.ids); + ecs_map_fini(&ws.wildcard_ids); - /* Set variable for $this */ - if (it->count) { - ecs_expr_var_t *var = ecs_vars_lookup(vars, "this"); - if (!var) { - ecs_value_t v = { - .ptr = &it->entities[offset], - .type = ecs_id(ecs_entity_t) - }; - var = ecs_vars_declare_w_value(vars, "this", &v); - var->owned = false; - } else { - var->value.ptr = &it->entities[offset]; - } - } + op = ecs_vec_first_t(&pq->ops, ecs_pipeline_op_t); - /* Set variables for fields */ - { - int32_t i, field_count = it->field_count; - for (i = 0; i < field_count; i ++) { - ecs_size_t size = it->sizes[i]; - if (!size) { - continue; - } + if (!op) { + ecs_dbg("#[green]pipeline#[reset] is empty"); + return true; + } else { + /* Add schedule to debug tracing */ + ecs_dbg("#[bold]pipeline rebuild"); + ecs_log_push_1(); - void *ptr = it->ptrs[i]; - if (!ptr) { - continue; - } + ecs_dbg("#[green]schedule#[reset]: threading: %d, staging: %d:", + op->multi_threaded, !op->immediate); + ecs_log_push_1(); - ptr = ECS_OFFSET(ptr, offset * size); + int32_t i, count = ecs_vec_count(&pq->systems); + int32_t op_index = 0, ran_since_merge = 0; + ecs_entity_t *systems = ecs_vec_first_t(&pq->systems, ecs_entity_t); + for (i = 0; i < count; i ++) { + ecs_entity_t system = systems[i]; + const EcsPoly *poly = ecs_get_pair(world, system, EcsPoly, EcsSystem); + flecs_poly_assert(poly->poly, ecs_system_t); + ecs_system_t *sys = (ecs_system_t*)poly->poly; - char name[16]; - ecs_os_sprintf(name, "%d", i + 1); - ecs_expr_var_t *var = ecs_vars_lookup(vars, name); - if (!var) { - ecs_value_t v = { .ptr = ptr, .type = it->ids[i] }; - var = ecs_vars_declare_w_value(vars, name, &v); - var->owned = false; +#ifdef FLECS_LOG_1 + char *path = ecs_get_path(world, system); + const char *doc_name = NULL; +#ifdef FLECS_DOC + const EcsDocDescription *doc_name_id = ecs_get_pair(world, system, + EcsDocDescription, EcsName); + if (doc_name_id) { + doc_name = doc_name_id->value; + } +#endif + if (doc_name) { + ecs_dbg("#[green]system#[reset] %s (%s)", path, doc_name); } else { - ecs_check(var->value.type == it->ids[i], - ECS_INVALID_PARAMETER, NULL); - var->value.ptr = ptr; + ecs_dbg("#[green]system#[reset] %s", path); } - } - } + ecs_os_free(path); +#endif - /* Set variables for query variables */ - { - int32_t i, var_count = it->variable_count; - for (i = 1 /* skip this variable */ ; i < var_count; i ++) { - ecs_entity_t *e_ptr = NULL; - ecs_var_t *query_var = &it->variables[i]; - if (query_var->entity) { - e_ptr = &query_var->entity; - } else { - ecs_table_range_t *range = &query_var->range; - if (range->count == 1) { - ecs_entity_t *entities = range->table->data.entities.array; - e_ptr = &entities[range->offset]; + ecs_assert(op[op_index].offset + ran_since_merge == i, + ECS_INTERNAL_ERROR, NULL); + + ran_since_merge ++; + if (ran_since_merge == op[op_index].count) { + ecs_dbg("#[magenta]merge#[reset]"); + ecs_log_pop_1(); + ran_since_merge = 0; + op_index ++; + if (op_index < ecs_vec_count(&pq->ops)) { + ecs_dbg( + "#[green]schedule#[reset]: " + "threading: %d, staging: %d:", + op[op_index].multi_threaded, + !op[op_index].immediate); } - } - if (!e_ptr) { - continue; + ecs_log_push_1(); } - ecs_expr_var_t *var = ecs_vars_lookup(vars, it->variable_names[i]); - if (!var) { - ecs_value_t v = { .ptr = e_ptr, .type = ecs_id(ecs_entity_t) }; - var = ecs_vars_declare_w_value(vars, it->variable_names[i], &v); - var->owned = false; - } else { - ecs_check(var->value.type == ecs_id(ecs_entity_t), - ECS_INVALID_PARAMETER, NULL); - var->value.ptr = e_ptr; + if (sys->last_frame == (world->info.frame_count_total + 1)) { + if (op_index < ecs_vec_count(&pq->ops)) { + pq->cur_op = &op[op_index]; + pq->cur_i = i; + } else { + pq->cur_op = NULL; + pq->cur_i = 0; + } } } - } - -error: - return; -} - -#endif -/** - * @file expr/vars.c - * @brief Utilities for variable substitution in flecs string expressions. - */ + ecs_log_pop_1(); + ecs_log_pop_1(); + } + pq->match_count = new_match_count; -#ifdef FLECS_EXPR + ecs_assert(pq->cur_op <= ecs_vec_last_t(&pq->ops, ecs_pipeline_op_t), + ECS_INTERNAL_ERROR, NULL); -static -void flecs_expr_var_scope_init( - ecs_world_t *world, - ecs_expr_var_scope_t *scope, - ecs_expr_var_scope_t *parent) -{ - flecs_name_index_init(&scope->var_index, &world->allocator); - ecs_vec_init_t(&world->allocator, &scope->vars, ecs_expr_var_t, 0); - scope->parent = parent; + return true; } static -void flecs_expr_var_scope_fini( - ecs_world_t *world, - ecs_expr_var_scope_t *scope) +void flecs_pipeline_next_system( + ecs_pipeline_state_t *pq) { - ecs_vec_t *vars = &scope->vars; - int32_t i, count = vars->count; - for (i = 0; i < count; i++) { - ecs_expr_var_t *var = ecs_vec_get_t(vars, ecs_expr_var_t, i); - if (var->owned) { - ecs_value_free(world, var->value.type, var->value.ptr); - } - flecs_strfree(&world->allocator, var->name); + if (!pq->cur_op) { + return; } - ecs_vec_fini_t(&world->allocator, &scope->vars, ecs_expr_var_t); - flecs_name_index_fini(&scope->var_index); -} - -void ecs_vars_init( - ecs_world_t *world, - ecs_vars_t *vars) -{ - flecs_expr_var_scope_init(world, &vars->root, NULL); - vars->world = world; - vars->cur = &vars->root; -} - -void ecs_vars_fini( - ecs_vars_t *vars) -{ - ecs_expr_var_scope_t *cur = vars->cur, *next; - do { - next = cur->parent; - flecs_expr_var_scope_fini(vars->world, cur); - if (cur != &vars->root) { - flecs_free_t(&vars->world->allocator, ecs_expr_var_scope_t, cur); - } else { - break; + pq->cur_i ++; + if (pq->cur_i >= (pq->cur_op->offset + pq->cur_op->count)) { + pq->cur_op ++; + if (pq->cur_op > ecs_vec_last_t(&pq->ops, ecs_pipeline_op_t)) { + pq->cur_op = NULL; } - } while ((cur = next)); -} - -void ecs_vars_push( - ecs_vars_t *vars) -{ - ecs_expr_var_scope_t *scope = flecs_calloc_t(&vars->world->allocator, - ecs_expr_var_scope_t); - flecs_expr_var_scope_init(vars->world, scope, vars->cur); - vars->cur = scope; -} - -int ecs_vars_pop( - ecs_vars_t *vars) -{ - ecs_expr_var_scope_t *scope = vars->cur; - ecs_check(scope != &vars->root, ECS_INVALID_OPERATION, NULL); - vars->cur = scope->parent; - flecs_expr_var_scope_fini(vars->world, scope); - flecs_free_t(&vars->world->allocator, ecs_expr_var_scope_t, scope); - return 0; -error: - return 1; + } } -ecs_expr_var_t* ecs_vars_declare( - ecs_vars_t *vars, - const char *name, - ecs_entity_t type) +bool flecs_pipeline_update( + ecs_world_t *world, + ecs_pipeline_state_t *pq, + bool start_of_frame) { - ecs_assert(vars != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(type != 0, ECS_INVALID_PARAMETER, NULL); - ecs_expr_var_scope_t *scope = vars->cur; - ecs_hashmap_t *var_index = &scope->var_index; + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot update pipeline while world is in readonly mode"); - if (flecs_name_index_find(var_index, name, 0, 0) != 0) { - ecs_err("variable %s redeclared", name); - goto error; + /* If any entity mutations happened that could have affected query matching + * notify appropriate queries so caches are up to date. This includes the + * pipeline query. */ + if (start_of_frame) { + ecs_run_aperiodic(world, 0); } - ecs_expr_var_t *var = ecs_vec_append_t(&vars->world->allocator, - &scope->vars, ecs_expr_var_t); - - var->value.ptr = ecs_value_new(vars->world, type); - if (!var->value.ptr) { - goto error; + ecs_assert(pq != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(pq->query != NULL, ECS_INTERNAL_ERROR, NULL); + + bool rebuilt = flecs_pipeline_build(world, pq); + if (start_of_frame) { + /* Initialize iterators */ + int32_t i, count = pq->iter_count; + for (i = 0; i < count; i ++) { + ecs_world_t *stage = ecs_get_stage(world, i); + pq->iters[i] = ecs_query_iter(stage, pq->query); + } + pq->cur_op = ecs_vec_first_t(&pq->ops, ecs_pipeline_op_t); + pq->cur_i = 0; + } else { + flecs_pipeline_next_system(pq); } - var->value.type = type; - var->name = flecs_strdup(&vars->world->allocator, name); - var->owned = true; - flecs_name_index_ensure(var_index, - flecs_ito(uint64_t, ecs_vec_count(&scope->vars)), var->name, 0, 0); - return var; -error: - return NULL; + return rebuilt; } -ecs_expr_var_t* ecs_vars_declare_w_value( - ecs_vars_t *vars, - const char *name, - ecs_value_t *value) +void ecs_run_pipeline( + ecs_world_t *world, + ecs_entity_t pipeline, + ecs_ftime_t delta_time) { - ecs_assert(vars != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(value != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_expr_var_scope_t *scope = vars->cur; - ecs_hashmap_t *var_index = &scope->var_index; - - if (flecs_name_index_find(var_index, name, 0, 0) != 0) { - ecs_err("variable %s redeclared", name); - ecs_value_free(vars->world, value->type, value->ptr); - goto error; + if (!pipeline) { + pipeline = world->pipeline; } - ecs_expr_var_t *var = ecs_vec_append_t(&vars->world->allocator, - &scope->vars, ecs_expr_var_t); - var->value = *value; - var->name = flecs_strdup(&vars->world->allocator, name); - var->owned = true; - value->ptr = NULL; /* Take ownership, prevent double free */ + /* create any worker task threads request */ + if (ecs_using_task_threads(world)) { + flecs_create_worker_threads(world); + } - flecs_name_index_ensure(var_index, - flecs_ito(uint64_t, ecs_vec_count(&scope->vars)), var->name, 0, 0); - return var; -error: - return NULL; -} + EcsPipeline *p = + ECS_CONST_CAST(EcsPipeline*, ecs_get(world, pipeline, EcsPipeline)); + flecs_workers_progress(world, p->state, delta_time); -static -ecs_expr_var_t* flecs_vars_scope_lookup( - ecs_expr_var_scope_t *scope, - const char *name) -{ - uint64_t var_id = flecs_name_index_find(&scope->var_index, name, 0, 0); - if (var_id == 0) { - if (scope->parent) { - return flecs_vars_scope_lookup(scope->parent, name); - } - return NULL; + if (ecs_using_task_threads(world)) { + /* task threads were temporary and may now be joined */ + flecs_join_worker_threads(world); } - - return ecs_vec_get_t(&scope->vars, ecs_expr_var_t, - flecs_uto(int32_t, var_id - 1)); } -ecs_expr_var_t* ecs_vars_lookup( - const ecs_vars_t *vars, - const char *name) +int32_t flecs_run_pipeline_ops( + ecs_world_t* world, + ecs_stage_t* stage, + int32_t stage_index, + int32_t stage_count, + ecs_ftime_t delta_time) { - return flecs_vars_scope_lookup(vars->cur, name); -} - -#endif - -/** - * @file json/deserialize.c - * @brief Deserialize JSON strings into (component) values. - */ - -/** - * @file json/json.h - * @brief Internal functions for JSON addon. - */ - - -#ifdef FLECS_JSON - -/* Deserialize from JSON */ -typedef enum ecs_json_token_t { - JsonObjectOpen, - JsonObjectClose, - JsonArrayOpen, - JsonArrayClose, - JsonColon, - JsonComma, - JsonNumber, - JsonString, - JsonTrue, - JsonFalse, - JsonNull, - JsonLargeInt, - JsonLargeString, - JsonInvalid -} ecs_json_token_t; - -const char* flecs_json_parse( - const char *json, - ecs_json_token_t *token_kind, - char *token); - -const char* flecs_json_parse_large_string( - const char *json, - ecs_strbuf_t *buf); - -const char* flecs_json_expect( - const char *json, - ecs_json_token_t token_kind, - char *token, - const ecs_from_json_desc_t *desc); - -const char* flecs_json_expect_member( - const char *json, - char *token, - const ecs_from_json_desc_t *desc); - -const char* flecs_json_expect_member_name( - const char *json, - char *token, - const char *member_name, - const ecs_from_json_desc_t *desc); - -const char* flecs_json_skip_object( - const char *json, - char *token, - const ecs_from_json_desc_t *desc); - -const char* flecs_json_skip_array( - const char *json, - char *token, - const ecs_from_json_desc_t *desc); + ecs_pipeline_state_t* pq = world->pq; + ecs_pipeline_op_t* op = pq->cur_op; + int32_t i = pq->cur_i; -/* Serialize to JSON */ -void flecs_json_next( - ecs_strbuf_t *buf); + ecs_assert(!stage_index || op->multi_threaded, ECS_INTERNAL_ERROR, NULL); -void flecs_json_number( - ecs_strbuf_t *buf, - double value); + int32_t count = ecs_vec_count(&pq->systems); + ecs_entity_t* systems = ecs_vec_first_t(&pq->systems, ecs_entity_t); + int32_t ran_since_merge = i - op->offset; -void flecs_json_true( - ecs_strbuf_t *buf); + for (; i < count; i++) { + ecs_entity_t system = systems[i]; + const EcsPoly* poly = ecs_get_pair(world, system, EcsPoly, EcsSystem); + flecs_poly_assert(poly->poly, ecs_system_t); + ecs_system_t* sys = (ecs_system_t*)poly->poly; -void flecs_json_false( - ecs_strbuf_t *buf); + /* Keep track of the last frame for which the system has ran, so we + * know from where to resume the schedule in case the schedule + * changes during a merge. */ + if (stage_index == 0) { + sys->last_frame = world->info.frame_count_total + 1; + } -void flecs_json_bool( - ecs_strbuf_t *buf, - bool value); + ecs_stage_t* s = NULL; + if (!op->immediate) { + /* If system is immediate it operates on the actual world, not + * the stage. Only pass stage to system if it's readonly. */ + s = stage; + } -void flecs_json_array_push( - ecs_strbuf_t *buf); + flecs_run_intern(world, s, system, sys, stage_index, + stage_count, delta_time, NULL); -void flecs_json_array_pop( - ecs_strbuf_t *buf); + ecs_os_linc(&world->info.systems_ran_frame); + ran_since_merge++; -void flecs_json_object_push( - ecs_strbuf_t *buf); + if (ran_since_merge == op->count) { + /* Merge */ + break; + } + } -void flecs_json_object_pop( - ecs_strbuf_t *buf); + return i; +} -void flecs_json_string( - ecs_strbuf_t *buf, - const char *value); +void flecs_run_pipeline( + ecs_world_t *world, + ecs_pipeline_state_t *pq, + ecs_ftime_t delta_time) +{ + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(pq != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(pq->query != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(world, ecs_stage_t); -void flecs_json_string_escape( - ecs_strbuf_t *buf, - const char *value); + ecs_stage_t *stage = flecs_stage_from_world(&world); + int32_t stage_index = ecs_stage_get_id(stage->thread_ctx); + int32_t stage_count = ecs_get_stage_count(world); + bool multi_threaded = world->worker_cond != 0; -void flecs_json_member( - ecs_strbuf_t *buf, - const char *name); + ecs_assert(!stage_index, ECS_INVALID_OPERATION, + "cannot run pipeline on stage"); -void flecs_json_membern( - ecs_strbuf_t *buf, - const char *name, - int32_t name_len); + // Update the pipeline the workers will execute + world->pq = pq; -#define flecs_json_memberl(buf, name)\ - flecs_json_membern(buf, name, sizeof(name) - 1) + // Update the pipeline before waking the workers. + flecs_pipeline_update(world, pq, true); -void flecs_json_path( - ecs_strbuf_t *buf, - const ecs_world_t *world, - ecs_entity_t e); + // If there are no operations to execute in the pipeline bail early, + // no need to wake the workers since they have nothing to do. + while (pq->cur_op != NULL) { + if (pq->cur_i == ecs_vec_count(&pq->systems)) { + flecs_pipeline_update(world, pq, false); + continue; + } -void flecs_json_label( - ecs_strbuf_t *buf, - const ecs_world_t *world, - ecs_entity_t e); + bool immediate = pq->cur_op->immediate; + bool op_multi_threaded = multi_threaded && pq->cur_op->multi_threaded; -void flecs_json_color( - ecs_strbuf_t *buf, - const ecs_world_t *world, - ecs_entity_t e); + pq->immediate = immediate; -void flecs_json_id( - ecs_strbuf_t *buf, - const ecs_world_t *world, - ecs_id_t id); + if (!immediate) { + ecs_readonly_begin(world, multi_threaded); + } -ecs_primitive_kind_t flecs_json_op_to_primitive_kind( - ecs_meta_type_op_kind_t kind); + ECS_BIT_COND(world->flags, EcsWorldMultiThreaded, op_multi_threaded); + ecs_assert(world->workers_waiting == 0, ECS_INTERNAL_ERROR, NULL); -#endif + if (op_multi_threaded) { + flecs_signal_workers(world); + } -#include + ecs_time_t st = { 0 }; + bool measure_time = world->flags & EcsWorldMeasureSystemTime; + if (measure_time) { + ecs_time_measure(&st); + } -#ifdef FLECS_JSON + const int32_t i = flecs_run_pipeline_ops( + world, stage, stage_index, stage_count, delta_time); -static -const char* flecs_json_parse_path( - const ecs_world_t *world, - const char *json, - char *token, - ecs_entity_t *out, - const ecs_from_json_desc_t *desc) -{ - json = flecs_json_expect(json, JsonString, token, desc); - if (!json) { - goto error; - } + if (measure_time) { + /* Don't include merge time in system time */ + world->info.system_time_total += (ecs_ftime_t)ecs_time_measure(&st); + } - ecs_entity_t result = ecs_lookup_fullpath(world, token); - if (!result) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "unresolved identifier '%s'", token); - goto error; - } + if (op_multi_threaded) { + flecs_wait_for_sync(world); + } - *out = result; + if (!immediate) { + ecs_time_t mt = { 0 }; + if (measure_time) { + ecs_time_measure(&mt); + } - return json; -error: - return NULL; -} + int32_t si; + for (si = 0; si < stage_count; si ++) { + ecs_stage_t *s = world->stages[si]; + pq->cur_op->commands_enqueued += ecs_vec_count(&s->cmd->queue); + } -const char* ecs_ptr_from_json( - const ecs_world_t *world, - ecs_entity_t type, - void *ptr, - const char *json, - const ecs_from_json_desc_t *desc) -{ - ecs_json_token_t token_kind = 0; - char token_buffer[ECS_MAX_TOKEN_SIZE], t_lah[ECS_MAX_TOKEN_SIZE]; - char *token = token_buffer; - int depth = 0; + ecs_readonly_end(world); + if (measure_time) { + pq->cur_op->time_spent += ecs_time_measure(&mt); + } + } - const char *name = NULL; - const char *expr = NULL; + /* Store the current state of the schedule after we synchronized the + * threads, to avoid race conditions. */ + pq->cur_i = i; - ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, ptr); - if (cur.valid == false) { - return NULL; + flecs_pipeline_update(world, pq, false); } +} - if (desc) { - name = desc->name; - expr = desc->expr; - cur.lookup_action = desc->lookup_action; - cur.lookup_ctx = desc->lookup_ctx; +static +void flecs_run_startup_systems( + ecs_world_t *world) +{ + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_dependson(EcsOnStart)); + if (!idr || !flecs_table_cache_count(&idr->cache)) { + /* Don't bother creating startup pipeline if no systems exist */ + return; } - while ((json = flecs_json_parse(json, &token_kind, token))) { - if (token_kind == JsonLargeString) { - ecs_strbuf_t large_token = ECS_STRBUF_INIT; - json = flecs_json_parse_large_string(json, &large_token); - if (!json) { - break; - } + ecs_dbg_2("#[bold]startup#[reset]"); + ecs_log_push_2(); + int32_t stage_count = world->stage_count; + world->stage_count = 1; /* Prevents running startup systems on workers */ - token = ecs_strbuf_get(&large_token); - token_kind = JsonString; + /* Creating a pipeline is relatively expensive, but this only happens + * for the first frame. The startup pipeline is deleted afterwards, which + * eliminates the overhead of keeping its query cache in sync. */ + ecs_dbg_2("#[bold]create startup pipeline#[reset]"); + ecs_log_push_2(); + ecs_entity_t start_pip = ecs_pipeline_init(world, &(ecs_pipeline_desc_t){ + .query = { + .terms = { + { .id = EcsSystem }, + { .id = EcsPhase, .src.id = EcsCascade, .trav = EcsDependsOn }, + { .id = ecs_dependson(EcsOnStart), .trav = EcsDependsOn }, + { .id = EcsDisabled, .src.id = EcsUp, .trav = EcsDependsOn, .oper = EcsNot }, + { .id = EcsDisabled, .src.id = EcsUp, .trav = EcsChildOf, .oper = EcsNot } + }, + .order_by_callback = flecs_entity_compare } + }); + ecs_log_pop_2(); - if (token_kind == JsonObjectOpen) { - depth ++; - if (ecs_meta_push(&cur) != 0) { - goto error; - } + /* Run & delete pipeline */ + ecs_dbg_2("#[bold]run startup systems#[reset]"); + ecs_log_push_2(); + ecs_assert(start_pip != 0, ECS_INTERNAL_ERROR, NULL); + const EcsPipeline *p = ecs_get(world, start_pip, EcsPipeline); + ecs_check(p != NULL, ECS_INVALID_OPERATION, + "pipeline entity is missing flecs.pipeline.Pipeline component"); + flecs_workers_progress(world, p->state, 0); + ecs_log_pop_2(); - if (ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, json - expr, "expected '['"); - return NULL; - } - } else if (token_kind == JsonObjectClose) { - depth --; + ecs_dbg_2("#[bold]delete startup pipeline#[reset]"); + ecs_log_push_2(); + ecs_delete(world, start_pip); + ecs_log_pop_2(); - if (ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, json - expr, "expected ']'"); - return NULL; - } + world->stage_count = stage_count; + ecs_log_pop_2(); - if (ecs_meta_pop(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonArrayOpen) { - depth ++; - if (ecs_meta_push(&cur) != 0) { - goto error; - } +error: + return; +} - if (!ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, json - expr, "expected '{'"); - return NULL; - } - } else if (token_kind == JsonArrayClose) { - depth --; +bool ecs_progress( + ecs_world_t *world, + ecs_ftime_t user_delta_time) +{ + ecs_ftime_t delta_time = ecs_frame_begin(world, user_delta_time); + + /* If this is the first frame, run startup systems */ + if (world->info.frame_count_total == 0) { + flecs_run_startup_systems(world); + } - if (!ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, json - expr, "expected '}'"); - return NULL; - } + /* create any worker task threads request */ + if (ecs_using_task_threads(world)) { + flecs_create_worker_threads(world); + } - if (ecs_meta_pop(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonComma) { - if (ecs_meta_next(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonNull) { - if (ecs_meta_set_null(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonString) { - const char *lah = flecs_json_parse( - json, &token_kind, t_lah); - if (token_kind == JsonColon) { - /* Member assignment */ - json = lah; - if (ecs_meta_dotmember(&cur, token) != 0) { - goto error; - } - } else { - if (ecs_meta_set_string(&cur, token) != 0) { - goto error; - } - } - } else if (token_kind == JsonNumber) { - double number = atof(token); - if (ecs_meta_set_float(&cur, number) != 0) { - goto error; - } - } else if (token_kind == JsonLargeInt) { - int64_t number = flecs_ito(int64_t, atoll(token)); - if (ecs_meta_set_int(&cur, number) != 0) { - goto error; - } - } else if (token_kind == JsonNull) { - if (ecs_meta_set_null(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonTrue) { - if (ecs_meta_set_bool(&cur, true) != 0) { - goto error; - } - } else if (token_kind == JsonFalse) { - if (ecs_meta_set_bool(&cur, false) != 0) { - goto error; - } - } else { - goto error; - } + ecs_dbg_3("#[bold]progress#[reset](dt = %.2f)", (double)delta_time); + ecs_log_push_3(); + const EcsPipeline *p = ecs_get(world, world->pipeline, EcsPipeline); + ecs_check(p != NULL, ECS_INVALID_OPERATION, + "pipeline entity is missing flecs.pipeline.Pipeline component"); + flecs_workers_progress(world, p->state, delta_time); + ecs_log_pop_3(); - if (token != token_buffer) { - ecs_os_free(token); - token = token_buffer; - } + ecs_frame_end(world); - if (!depth) { - break; - } + if (ecs_using_task_threads(world)) { + /* task threads were temporary and may now be joined */ + flecs_join_worker_threads(world); } - return json; + return !ECS_BIT_IS_SET(world->flags, EcsWorldQuit); error: - return NULL; + return false; } -const char* ecs_entity_from_json( +void ecs_set_time_scale( ecs_world_t *world, - ecs_entity_t e, - const char *json, - const ecs_from_json_desc_t *desc_param) + ecs_ftime_t scale) { - ecs_json_token_t token_kind = 0; - char token[ECS_MAX_TOKEN_SIZE]; + world->info.time_scale = scale; +} - ecs_from_json_desc_t desc = {0}; +void ecs_reset_clock( + ecs_world_t *world) +{ + world->info.world_time_total = 0; + world->info.world_time_total_raw = 0; +} - const char *name = NULL, *expr = json, *ids = NULL, *values = NULL, *lah; - if (desc_param) { - desc = *desc_param; - } +void ecs_set_pipeline( + ecs_world_t *world, + ecs_entity_t pipeline) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check( ecs_get(world, pipeline, EcsPipeline) != NULL, + ECS_INVALID_PARAMETER, "not a pipeline"); - json = flecs_json_expect(json, JsonObjectOpen, token, &desc); - if (!json) { - goto error; - } + world->pipeline = pipeline; +error: + return; +} - lah = flecs_json_parse(json, &token_kind, token); - if (!lah) { - goto error; +ecs_entity_t ecs_get_pipeline( + const ecs_world_t *world) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + world = ecs_get_world(world); + return world->pipeline; +error: + return 0; +} + +ecs_entity_t ecs_pipeline_init( + ecs_world_t *world, + const ecs_pipeline_desc_t *desc) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + + ecs_entity_t result = desc->entity; + if (!result) { + result = ecs_new(world); } - if (token_kind == JsonObjectClose) { - return lah; + ecs_query_desc_t qd = desc->query; + if (!qd.order_by_callback) { + qd.order_by_callback = flecs_entity_compare; } + qd.entity = result; - json = flecs_json_expect_member(json, token, &desc); - if (!json) { - return NULL; + ecs_query_t *query = ecs_query_init(world, &qd); + if (!query) { + ecs_delete(world, result); + return 0; } - if (!ecs_os_strcmp(token, "path")) { - json = flecs_json_expect(json, JsonString, token, &desc); - if (!json) { - goto error; - } + ecs_check(query->terms != NULL, ECS_INVALID_PARAMETER, + "pipeline query cannot be empty"); + ecs_check(query->terms[0].id == EcsSystem, + ECS_INVALID_PARAMETER, "pipeline must start with System term"); - ecs_add_fullpath(world, e, token); + ecs_pipeline_state_t *pq = ecs_os_calloc_t(ecs_pipeline_state_t); + pq->query = query; + pq->match_count = -1; + pq->idr_inactive = flecs_id_record_ensure(world, EcsEmpty); + ecs_set(world, result, EcsPipeline, { pq }); - json = flecs_json_parse(json, &token_kind, token); - if (!json) { - goto error; - } + return result; +error: + return 0; +} - if (token_kind == JsonObjectClose) { - return json; - } else if (token_kind != JsonComma) { - ecs_parser_error(name, expr, json - expr, "unexpected character"); - goto error; - } +/* -- Module implementation -- */ - json = flecs_json_expect_member_name(json, token, "ids", &desc); - if (!json) { - goto error; - } - } else if (ecs_os_strcmp(token, "ids")) { - ecs_parser_error(name, expr, json - expr, "expected member 'ids'"); - goto error; +static +void FlecsPipelineFini( + ecs_world_t *world, + void *ctx) +{ + (void)ctx; + if (ecs_get_stage_count(world)) { + ecs_set_threads(world, 0); } - json = flecs_json_expect(json, JsonArrayOpen, token, &desc); - if (!json) { - goto error; + ecs_assert(world->workers_running == 0, ECS_INTERNAL_ERROR, NULL); +} + +#define flecs_bootstrap_phase(world, phase, depends_on)\ + flecs_bootstrap_tag(world, phase);\ + flecs_bootstrap_phase_(world, phase, depends_on) +static +void flecs_bootstrap_phase_( + ecs_world_t *world, + ecs_entity_t phase, + ecs_entity_t depends_on) +{ + ecs_add_id(world, phase, EcsPhase); + if (depends_on) { + ecs_add_pair(world, phase, EcsDependsOn, depends_on); } +} + +void FlecsPipelineImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsPipeline); + ECS_IMPORT(world, FlecsSystem); +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsPipeline), + "Module that schedules and runs systems"); +#endif + ecs_set_name_prefix(world, "Ecs"); - ids = json; + flecs_bootstrap_component(world, EcsPipeline); + flecs_bootstrap_tag(world, EcsPhase); - json = flecs_json_skip_array(json, token, &desc); - if (!json) { - return NULL; - } + /* Create anonymous phases to which the builtin phases will have DependsOn + * relationships. This ensures that, for example, EcsOnUpdate doesn't have a + * direct DependsOn relationship on EcsPreUpdate, which ensures that when + * the EcsPreUpdate phase is disabled, EcsOnUpdate still runs. */ + ecs_entity_t phase_0 = ecs_entity(world, {0}); + ecs_entity_t phase_1 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_0)) }); + ecs_entity_t phase_2 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_1)) }); + ecs_entity_t phase_3 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_2)) }); + ecs_entity_t phase_4 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_3)) }); + ecs_entity_t phase_5 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_4)) }); + ecs_entity_t phase_6 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_5)) }); + ecs_entity_t phase_7 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_6)) }); + ecs_entity_t phase_8 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_7)) }); + + flecs_bootstrap_phase(world, EcsOnStart, 0); + flecs_bootstrap_phase(world, EcsPreFrame, 0); + flecs_bootstrap_phase(world, EcsOnLoad, phase_0); + flecs_bootstrap_phase(world, EcsPostLoad, phase_1); + flecs_bootstrap_phase(world, EcsPreUpdate, phase_2); + flecs_bootstrap_phase(world, EcsOnUpdate, phase_3); + flecs_bootstrap_phase(world, EcsOnValidate, phase_4); + flecs_bootstrap_phase(world, EcsPostUpdate, phase_5); + flecs_bootstrap_phase(world, EcsPreStore, phase_6); + flecs_bootstrap_phase(world, EcsOnStore, phase_7); + flecs_bootstrap_phase(world, EcsPostFrame, phase_8); - json = flecs_json_parse(json, &token_kind, token); - if (token_kind != JsonObjectClose) { - if (token_kind != JsonComma) { - ecs_parser_error(name, expr, json - expr, "expected ','"); - goto error; - } + ecs_set_hooks(world, EcsPipeline, { + .ctor = flecs_default_ctor, + .dtor = ecs_dtor(EcsPipeline), + .move = ecs_move(EcsPipeline) + }); - json = flecs_json_expect_member_name(json, token, "values", &desc); - if (!json) { - goto error; + world->pipeline = ecs_pipeline(world, { + .entity = ecs_entity(world, { .name = "BuiltinPipeline" }), + .query = { + .terms = { + { .id = EcsSystem }, + { .id = EcsPhase, .src.id = EcsCascade, .trav = EcsDependsOn }, + { .id = ecs_dependson(EcsOnStart), .trav = EcsDependsOn, .oper = EcsNot }, + { .id = EcsDisabled, .src.id = EcsUp, .trav = EcsDependsOn, .oper = EcsNot }, + { .id = EcsDisabled, .src.id = EcsUp, .trav = EcsChildOf, .oper = EcsNot } + }, + .order_by_callback = flecs_entity_compare } + }); - json = flecs_json_expect(json, JsonArrayOpen, token, &desc); - if (!json) { - goto error; - } + /* Cleanup thread administration when world is destroyed */ + ecs_atfini(world, FlecsPipelineFini, NULL); +} - values = json; - } +#endif - do { - ecs_entity_t first = 0, second = 0, type_id = 0; - ecs_id_t id; +/** + * @file addons/pipeline/worker.c + * @brief Functions for running pipelines on one or more threads. + */ - ids = flecs_json_parse(ids, &token_kind, token); - if (!ids) { - goto error; - } - if (token_kind == JsonArrayClose) { - if (values) { - if (values[0] != ']') { - ecs_parser_error(name, expr, values - expr, "expected ']'"); - goto error; - } - json = ecs_parse_ws_eol(values + 1); - } else { - json = ids; - } +#ifdef FLECS_PIPELINE - break; - } else if (token_kind == JsonArrayOpen) { - ids = flecs_json_parse_path(world, ids, token, &first, &desc); - if (!ids) { - goto error; - } +/* Synchronize workers */ +static +void flecs_sync_worker( + ecs_world_t* world) +{ + int32_t stage_count = ecs_get_stage_count(world); + if (stage_count <= 1) { + return; + } - ids = flecs_json_parse(ids, &token_kind, token); - if (!ids) { - goto error; - } + /* Signal that thread is waiting */ + ecs_os_mutex_lock(world->sync_mutex); + if (++world->workers_waiting == (stage_count - 1)) { + /* Only signal main thread when all threads are waiting */ + ecs_os_cond_signal(world->sync_cond); + } - if (token_kind == JsonComma) { - /* Id is a pair*/ - ids = flecs_json_parse_path(world, ids, token, &second, &desc); - if (!ids) { - goto error; - } + /* Wait until main thread signals that thread can continue */ + ecs_os_cond_wait(world->worker_cond, world->sync_mutex); + ecs_os_mutex_unlock(world->sync_mutex); +} - ids = flecs_json_expect(ids, JsonArrayClose, token, &desc); - if (!ids) { - goto error; - } - } else if (token_kind != JsonArrayClose) { - ecs_parser_error(name, expr, ids - expr, "expected ',' or ']'"); - goto error; - } +/* Worker thread */ +static +void* flecs_worker(void *arg) { + ecs_stage_t *stage = arg; + ecs_world_t *world = stage->world; - lah = flecs_json_parse(ids, &token_kind, token); - if (!lah) { - goto error; - } + flecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(stage, ecs_stage_t); - if (token_kind == JsonComma) { - ids = lah; - } else if (token_kind != JsonArrayClose) { - ecs_parser_error(name, expr, lah - expr, "expected ',' or ']'"); - goto error; - } - } else { - ecs_parser_error(name, expr, lah - expr, "expected '[' or ']'"); - goto error; - } + ecs_dbg_2("worker %d: start", stage->id); - if (second) { - id = ecs_pair(first, second); - type_id = ecs_get_typeid(world, id); - if (!type_id) { - ecs_parser_error(name, expr, ids - expr, "id is not a type"); - goto error; - } - } else { - id = first; - type_id = first; - } + /* Start worker, increase counter so main thread knows how many + * workers are ready */ + ecs_os_mutex_lock(world->sync_mutex); + world->workers_running ++; - /* Get mutable pointer */ - void *comp_ptr = ecs_get_mut_id(world, e, id); - if (!comp_ptr) { - char *idstr = ecs_id_str(world, id); - ecs_parser_error(name, expr, json - expr, - "id '%s' is not a valid component", idstr); - ecs_os_free(idstr); - goto error; - } + if (!(world->flags & EcsWorldQuitWorkers)) { + ecs_os_cond_wait(world->worker_cond, world->sync_mutex); + } - if (values) { - ecs_from_json_desc_t parse_desc = { - .name = name, - .expr = expr, - }; + ecs_os_mutex_unlock(world->sync_mutex); + + while (!(world->flags & EcsWorldQuitWorkers)) { + ecs_entity_t old_scope = ecs_set_scope((ecs_world_t*)stage, 0); + + ecs_dbg_3("worker %d: run", stage->id); + flecs_run_pipeline_ops(world, stage, stage->id, world->stage_count, + world->info.delta_time); + + ecs_set_scope((ecs_world_t*)stage, old_scope); - values = ecs_ptr_from_json( - world, type_id, comp_ptr, values, &parse_desc); - if (!values) { - goto error; - } + flecs_sync_worker(world); + } - lah = flecs_json_parse(values, &token_kind, token); - if (!lah) { - goto error; - } + ecs_dbg_2("worker %d: finalizing", stage->id); - if (token_kind == JsonComma) { - values = lah; - } else if (token_kind != JsonArrayClose) { - ecs_parser_error(name, expr, json - expr, - "expected ',' or ']'"); - goto error; - } else { - values = ecs_parse_ws_eol(values); - } + ecs_os_mutex_lock(world->sync_mutex); + world->workers_running --; + ecs_os_mutex_unlock(world->sync_mutex); - ecs_modified_id(world, e, id); - } - } while(ids[0]); + ecs_dbg_2("worker %d: stop", stage->id); - return flecs_json_expect(json, JsonObjectClose, token, &desc); -error: return NULL; } -static -ecs_entity_t flecs_json_new_id( - ecs_world_t *world, - ecs_entity_t ser_id) +/* Start threads */ +void flecs_create_worker_threads( + ecs_world_t *world) { - /* Try to honor low id requirements */ - if (ser_id < FLECS_HI_COMPONENT_ID) { - return ecs_new_low_id(world); - } else { - return ecs_new_id(world); + flecs_poly_assert(world, ecs_world_t); + int32_t stages = ecs_get_stage_count(world); + + for (int32_t i = 1; i < stages; i ++) { + ecs_stage_t *stage = (ecs_stage_t*)ecs_get_stage(world, i); + ecs_assert(stage != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(stage, ecs_stage_t); + + ecs_assert(stage->thread == 0, ECS_INTERNAL_ERROR, NULL); + if (ecs_using_task_threads(world)) { + /* workers are using tasks in an external task manager provided to + * the OS API */ + stage->thread = ecs_os_task_new(flecs_worker, stage); + } else { + /* workers are using long-running os threads */ + stage->thread = ecs_os_thread_new(flecs_worker, stage); + } + ecs_assert(stage->thread != 0, ECS_OPERATION_FAILED, + "failed to create thread"); } } static -ecs_entity_t flecs_json_lookup( +void flecs_start_workers( ecs_world_t *world, - ecs_entity_t parent, - const char *name, - const ecs_from_json_desc_t *desc) + int32_t threads) { - ecs_entity_t scope = 0; - if (parent) { - scope = ecs_set_scope(world, parent); - } - ecs_entity_t result = desc->lookup_action(world, name, desc->lookup_ctx); - if (parent) { - ecs_set_scope(world, scope); + ecs_set_stage_count(world, threads); + + ecs_assert(ecs_get_stage_count(world) == threads, ECS_INTERNAL_ERROR, NULL); + + if (!ecs_using_task_threads(world)) { + flecs_create_worker_threads(world); } - return result; } +/* Wait until all workers are running */ static -void flecs_json_mark_reserved( - ecs_map_t *anonymous_ids, - ecs_entity_t e) +void flecs_wait_for_workers( + ecs_world_t *world) { - ecs_entity_t *reserved = ecs_map_ensure(anonymous_ids, e); - ecs_assert(reserved[0] == 0, ECS_INTERNAL_ERROR, NULL); - reserved[0] = 0; -} + flecs_poly_assert(world, ecs_world_t); -static -bool flecs_json_name_is_anonymous( - const char *name) -{ - if (isdigit(name[0])) { - const char *ptr; - for (ptr = name + 1; *ptr; ptr ++) { - if (!isdigit(*ptr)) { - break; - } - } - if (!(*ptr)) { - return true; - } + int32_t stage_count = ecs_get_stage_count(world); + if (stage_count <= 1) { + return; } - return false; + + bool wait = true; + do { + ecs_os_mutex_lock(world->sync_mutex); + if (world->workers_running == (stage_count - 1)) { + wait = false; + } + ecs_os_mutex_unlock(world->sync_mutex); + } while (wait); } -static -ecs_entity_t flecs_json_ensure_entity( - ecs_world_t *world, - const char *name, - ecs_map_t *anonymous_ids) +/* Wait until all threads are waiting on sync point */ +void flecs_wait_for_sync( + ecs_world_t *world) { - ecs_entity_t e = 0; + int32_t stage_count = ecs_get_stage_count(world); + if (stage_count <= 1) { + return; + } - if (flecs_json_name_is_anonymous(name)) { - /* Anonymous entity, find or create mapping to new id */ - ecs_entity_t ser_id = flecs_ito(ecs_entity_t, atoll(name)); - ecs_entity_t *deser_id = ecs_map_get(anonymous_ids, ser_id); - if (deser_id) { - if (!deser_id[0]) { - /* Id is already issued by deserializer, create new id */ - deser_id[0] = flecs_json_new_id(world, ser_id); + ecs_dbg_3("#[bold]pipeline: waiting for worker sync"); - /* Mark new id as reserved */ - flecs_json_mark_reserved(anonymous_ids, deser_id[0]); - } else { - /* Id mapping exists */ - } - } else { - /* Id has not yet been issued by deserializer, which means it's safe - * to use. This allows the deserializer to bind to existing - * anonymous ids, as they will never be reissued. */ - deser_id = ecs_map_ensure(anonymous_ids, ser_id); - if (!ecs_exists(world, ser_id) || ecs_is_alive(world, ser_id)) { - /* Only use existing id if it's alive or doesn't exist yet. The - * id could have been recycled for another entity */ - deser_id[0] = ser_id; - ecs_ensure(world, ser_id); - } else { - /* If id exists and is not alive, create a new id */ - deser_id[0] = flecs_json_new_id(world, ser_id); + ecs_os_mutex_lock(world->sync_mutex); + if (world->workers_waiting != (stage_count - 1)) { + ecs_os_cond_wait(world->sync_cond, world->sync_mutex); + } - /* Mark new id as reserved */ - flecs_json_mark_reserved(anonymous_ids, deser_id[0]); - } - } + /* We shouldn't have been signalled unless all workers are waiting on sync */ + ecs_assert(world->workers_waiting == (stage_count - 1), + ECS_INTERNAL_ERROR, NULL); - e = deser_id[0]; - } else { - e = ecs_lookup_path_w_sep(world, 0, name, ".", NULL, false); - if (!e) { - e = ecs_entity(world, { .name = name }); - flecs_json_mark_reserved(anonymous_ids, e); - } - } + world->workers_waiting = 0; + ecs_os_mutex_unlock(world->sync_mutex); - return e; + ecs_dbg_3("#[bold]pipeline: workers synced"); } -static -ecs_table_t* flecs_json_parse_table( - ecs_world_t *world, - const char *json, - char *token, - const ecs_from_json_desc_t *desc) +/* Signal workers that they can start/resume work */ +void flecs_signal_workers( + ecs_world_t *world) { - ecs_json_token_t token_kind = 0; - ecs_table_t *table = NULL; + int32_t stage_count = ecs_get_stage_count(world); + if (stage_count <= 1) { + return; + } - do { - ecs_id_t id = 0; - json = flecs_json_expect(json, JsonArrayOpen, token, desc); - if (!json) { - goto error; - } + ecs_dbg_3("#[bold]pipeline: signal workers"); + ecs_os_mutex_lock(world->sync_mutex); + ecs_os_cond_broadcast(world->worker_cond); + ecs_os_mutex_unlock(world->sync_mutex); +} - json = flecs_json_expect(json, JsonString, token, desc); - if (!json) { - goto error; - } +void flecs_join_worker_threads( + ecs_world_t *world) +{ + flecs_poly_assert(world, ecs_world_t); + bool threads_active = false; - ecs_entity_t first = flecs_json_lookup(world, 0, token, desc); - if (!first) { - goto error; + /* Test if threads are created. Cannot use workers_running, since this is + * a potential race if threads haven't spun up yet. */ + int i, count = world->stage_count; + for (i = 1; i < count; i ++) { + ecs_stage_t *stage = world->stages[i]; + if (stage->thread) { + threads_active = true; + break; } + }; - json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonComma) { - json = flecs_json_expect(json, JsonString, token, desc); - if (!json) { - goto error; - } + /* If no threads are active, just return */ + if (!threads_active) { + return; + } - ecs_entity_t second = flecs_json_lookup(world, 0, token, desc); - if (!second) { - goto error; - } + /* Make sure all threads are running, to ensure they catch the signal */ + flecs_wait_for_workers(world); - id = ecs_pair(first, second); + /* Signal threads should quit */ + world->flags |= EcsWorldQuitWorkers; + flecs_signal_workers(world); - json = flecs_json_expect(json, JsonArrayClose, token, desc); - if (!json) { - goto error; - } - } else if (token_kind == JsonArrayClose) { - id = first; + /* Join all threads with main */ + for (i = 1; i < count; i ++) { + ecs_stage_t *stage = world->stages[i]; + if (ecs_using_task_threads(world)) { + ecs_os_task_join(stage->thread); } else { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']"); - goto error; + ecs_os_thread_join(stage->thread); } + stage->thread = 0; + } - table = ecs_table_add_id(world, table, id); - if (!table) { - goto error; - } + world->flags &= ~EcsWorldQuitWorkers; + ecs_assert(world->workers_running == 0, ECS_INTERNAL_ERROR, NULL); +} - const char *lah = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonComma) { - json = lah; - } else if (token_kind == JsonArrayClose) { - break; - } else { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']'"); - goto error; - } - } while (json[0]); +/* -- Private functions -- */ +void flecs_workers_progress( + ecs_world_t *world, + ecs_pipeline_state_t *pq, + ecs_ftime_t delta_time) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!ecs_is_deferred(world), ECS_INVALID_OPERATION, + "cannot call progress while world is deferred"); - return table; -error: - return NULL; + /* Make sure workers are running and ready */ + flecs_wait_for_workers(world); + + /* Run pipeline on main thread */ + ecs_world_t *stage = ecs_get_stage(world, 0); + ecs_entity_t old_scope = ecs_set_scope((ecs_world_t*)stage, 0); + flecs_run_pipeline(stage, pq, delta_time); + ecs_set_scope((ecs_world_t*)stage, old_scope); } static -int flecs_json_parse_entities( +void flecs_set_threads_internal( ecs_world_t *world, - ecs_allocator_t *a, - ecs_table_t *table, - ecs_entity_t parent, - const char *json, - char *token, - ecs_vec_t *records, - const ecs_from_json_desc_t *desc) + int32_t threads, + bool use_task_api) { - char name_token[ECS_MAX_TOKEN_SIZE]; - ecs_json_token_t token_kind = 0; - ecs_vec_clear(records); + ecs_assert(threads <= 1 || (use_task_api + ? ecs_os_has_task_support() + : ecs_os_has_threading()), + ECS_MISSING_OS_API, NULL); - do { - json = flecs_json_parse(json, &token_kind, name_token); - if (!json) { - goto error; - } - if ((token_kind != JsonNumber) && (token_kind != JsonString)) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected number or string"); - goto error; - } + int32_t stage_count = ecs_get_stage_count(world); + bool worker_method_changed = (use_task_api != world->workers_use_task_api); - ecs_entity_t e = flecs_json_lookup(world, parent, name_token, desc); - ecs_record_t *r = flecs_entities_try(world, e); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + if ((stage_count != threads) || worker_method_changed) { + /* Stop existing threads */ + if (stage_count > 1) { + flecs_join_worker_threads(world); + ecs_set_stage_count(world, 1); - if (r->table != table) { - bool cleared = false; - if (r->table) { - ecs_commit(world, e, r, r->table, NULL, &r->table->type); - cleared = true; - } - ecs_commit(world, e, r, table, &table->type, NULL); - if (cleared) { - char *entity_name = strrchr(name_token, '.'); - if (entity_name) { - entity_name ++; - } else { - entity_name = name_token; - } - if (!flecs_json_name_is_anonymous(entity_name)) { - ecs_set_name(world, e, entity_name); - } + if (world->worker_cond) { + ecs_os_cond_free(world->worker_cond); + } + if (world->sync_cond) { + ecs_os_cond_free(world->sync_cond); + } + if (world->sync_mutex) { + ecs_os_mutex_free(world->sync_mutex); } } - ecs_assert(table == r->table, ECS_INTERNAL_ERROR, NULL); - ecs_record_t** elem = ecs_vec_append_t(a, records, ecs_record_t*); - *elem = r; + world->workers_use_task_api = use_task_api; - json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonArrayClose) { - break; - } else if (token_kind != JsonComma) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']'"); - goto error; + /* Start threads if number of threads > 1 */ + if (threads > 1) { + world->worker_cond = ecs_os_cond_new(); + world->sync_cond = ecs_os_cond_new(); + world->sync_mutex = ecs_os_mutex_new(); + flecs_start_workers(world, threads); } - } while(json[0]); + } +} - return 0; -error: - return -1; +/* -- Public functions -- */ + +void ecs_set_threads( + ecs_world_t *world, + int32_t threads) +{ + flecs_set_threads_internal(world, threads, false /* use thread API */); } -static -const char* flecs_json_parse_column( +void ecs_set_task_threads( ecs_world_t *world, - ecs_table_t *table, - int32_t index, - const char *json, - char *token, - ecs_vec_t *records, - const ecs_from_json_desc_t *desc) + int32_t task_threads) { - if (!table->column_count) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "table has no components"); - goto error; - } + flecs_set_threads_internal(world, task_threads, true /* use task API */); +} - if (index >= table->type.count) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "more value arrays than component columns in table"); - goto error; - } +bool ecs_using_task_threads( + ecs_world_t *world) +{ + return world->workers_use_task_api; +} - int32_t data_column = table->column_map[index]; - if (data_column == -1) { - char *table_str = ecs_table_str(world, table); - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "values provided for tag at column %d of table [%s]", - index, table_str); +#endif - ecs_os_free(table_str); - goto error; - } +/** + * @file addons/script/ast.c + * @brief Script AST implementation. + */ - ecs_json_token_t token_kind = 0; - ecs_column_t *column = &table->data.columns[data_column]; - ecs_type_info_t *ti = column->ti; - ecs_size_t size = ti->size; - ecs_entity_t type = ti->component; - ecs_record_t **record_array = ecs_vec_first_t(records, ecs_record_t*); - int32_t entity = 0; - do { - ecs_record_t *r = record_array[entity]; - int32_t row = ECS_RECORD_TO_ROW(r->row); +#ifdef FLECS_SCRIPT - void *ptr = ecs_vec_get(&column->data, size, row); - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); +#define flecs_ast_strdup(parser, str)\ + (str ? flecs_strdup(&parser->script->allocator, str) : NULL) +#define flecs_ast_new(parser, T, kind)\ + (T*)flecs_ast_new_(parser, ECS_SIZEOF(T), kind) +#define flecs_ast_vec(parser, vec, T) \ + ecs_vec_init_t(&parser->script->allocator, &vec, T*, 0) +#define flecs_ast_append(parser, vec, T, node) \ + ecs_vec_append_t(&parser->script->allocator, &vec, T*)[0] = node - json = ecs_ptr_from_json(world, type, ptr, json, desc); - if (!json) { - break; - } +static +void* flecs_ast_new_( + ecs_script_parser_t *parser, + ecs_size_t size, + ecs_script_node_kind_t kind) +{ + ecs_assert(parser->script != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_allocator_t *a = &parser->script->allocator; + ecs_script_node_t *result = flecs_calloc(a, size); + result->kind = kind; + result->pos = parser->pos; + return result; +} - json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonArrayClose) { - break; - } else if (token_kind != JsonComma) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']'"); - } +ecs_script_scope_t* flecs_script_scope_new( + ecs_script_parser_t *parser) +{ + ecs_script_scope_t *result = flecs_ast_new( + parser, ecs_script_scope_t, EcsAstScope); + flecs_ast_vec(parser, result->stmts, ecs_script_node_t); + return result; +} + +bool flecs_scope_is_empty( + ecs_script_scope_t *scope) +{ + return ecs_vec_count(&scope->stmts) == 0; +} - entity ++; - } while (json[0]); +ecs_script_scope_t* flecs_script_insert_scope( + ecs_script_parser_t *parser) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_script_scope_t *result = flecs_script_scope_new(parser); + flecs_ast_append(parser, scope->stmts, ecs_script_scope_t, result); + return result; +} - return json; -error: - return NULL; +ecs_script_entity_t* flecs_script_insert_entity( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_entity_t *result = flecs_ast_new( + parser, ecs_script_entity_t, EcsAstEntity); + + if (name && !ecs_os_strcmp(name, "_")) { + name = NULL; + } + + result->name = name; + + ecs_script_scope_t *entity_scope = flecs_script_scope_new(parser); + ecs_assert(entity_scope != NULL, ECS_INTERNAL_ERROR, NULL); + result->scope = entity_scope; + + flecs_ast_append(parser, scope->stmts, ecs_script_entity_t, result); + return result; } static -const char* flecs_json_parse_values( - ecs_world_t *world, - ecs_table_t *table, - const char *json, - char *token, - ecs_vec_t *records, - ecs_vec_t *columns_set, - const ecs_from_json_desc_t *desc) +void flecs_script_set_id( + ecs_script_id_t *id, + const char *first, + const char *second) { - ecs_allocator_t *a = &world->allocator; - ecs_json_token_t token_kind = 0; - int32_t column = 0; + ecs_assert(first != NULL, ECS_INTERNAL_ERROR, NULL); + id->first = first; + id->second = second; +} - ecs_vec_clear(columns_set); +ecs_script_pair_scope_t* flecs_script_insert_pair_scope( + ecs_script_parser_t *parser, + const char *first, + const char *second) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_script_pair_scope_t *result = flecs_ast_new( + parser, ecs_script_pair_scope_t, EcsAstPairScope); + flecs_script_set_id(&result->id, first, second); + result->scope = flecs_script_scope_new(parser); - do { - json = flecs_json_parse(json, &token_kind, token); - if (!json) { - goto error; - } + flecs_ast_append(parser, scope->stmts, ecs_script_pair_scope_t, result); + return result; +} - if (token_kind == JsonArrayClose) { - break; - } else if (token_kind == JsonArrayOpen) { - json = flecs_json_parse_column(world, table, column, - json, token, records, desc); - if (!json) { - goto error; - } +ecs_script_tag_t* flecs_script_insert_pair_tag( + ecs_script_parser_t *parser, + const char *first, + const char *second) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_id_t *id_set = ecs_vec_append_t(a, columns_set, ecs_id_t); - *id_set = table->type.array[column]; + ecs_script_tag_t *result = flecs_ast_new( + parser, ecs_script_tag_t, EcsAstTag); + flecs_script_set_id(&result->id, first, second); - column ++; - } else if (token_kind == JsonNumber) { - if (!ecs_os_strcmp(token, "0")) { - column ++; /* no data */ - } else { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "unexpected number"); - goto error; - } - } + flecs_ast_append(parser, scope->stmts, ecs_script_tag_t, result); - json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonArrayClose) { - break; - } else if (token_kind != JsonComma) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']'"); - goto error; - } - } while (json[0]); + return result; +} - /* Send OnSet notifications */ - ecs_defer_begin(world); - ecs_type_t type = { - .array = columns_set->array, - .count = columns_set->count }; +ecs_script_tag_t* flecs_script_insert_tag( + ecs_script_parser_t *parser, + const char *name) +{ + return flecs_script_insert_pair_tag(parser, name, NULL); +} - int32_t table_count = ecs_table_count(table); - int32_t i, record_count = ecs_vec_count(records); +ecs_script_component_t* flecs_script_insert_pair_component( + ecs_script_parser_t *parser, + const char *first, + const char *second) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); - /* If the entire table was inserted, send bulk notification */ - if (table_count == ecs_vec_count(records)) { - flecs_notify_on_set(world, table, 0, ecs_table_count(table), &type, true); - } else { - ecs_record_t **rvec = ecs_vec_first_t(records, ecs_record_t*); - for (i = 0; i < record_count; i ++) { - ecs_record_t *r = rvec[i]; - int32_t row = ECS_RECORD_TO_ROW(r->row); - flecs_notify_on_set(world, table, row, 1, &type, true); - } - } + ecs_script_component_t *result = flecs_ast_new( + parser, ecs_script_component_t, EcsAstComponent); + flecs_script_set_id(&result->id, first, second); - ecs_defer_end(world); + flecs_ast_append(parser, scope->stmts, ecs_script_component_t, result); - return json; -error: - return NULL; + return result; } -static -const char* flecs_json_parse_result( - ecs_world_t *world, - ecs_allocator_t *a, - const char *json, - char *token, - ecs_vec_t *records, - ecs_vec_t *columns_set, - const ecs_from_json_desc_t *desc) +ecs_script_component_t* flecs_script_insert_component( + ecs_script_parser_t *parser, + const char *name) { - ecs_json_token_t token_kind = 0; - const char *ids = NULL, *values = NULL, *entities = NULL; + return flecs_script_insert_pair_component(parser, name, NULL); +} - json = flecs_json_expect(json, JsonObjectOpen, token, desc); - if (!json) { - goto error; - } +ecs_script_default_component_t* flecs_script_insert_default_component( + ecs_script_parser_t *parser) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); - json = flecs_json_expect_member_name(json, token, "ids", desc); - if (!json) { - goto error; - } + ecs_script_default_component_t *result = flecs_ast_new( + parser, ecs_script_default_component_t, EcsAstDefaultComponent); - json = flecs_json_expect(json, JsonArrayOpen, token, desc); - if (!json) { - goto error; - } + flecs_ast_append(parser, scope->stmts, + ecs_script_default_component_t, result); - ids = json; /* store start of ids array */ + return result; +} - json = flecs_json_skip_array(json, token, desc); - if (!json) { - goto error; - } +ecs_script_var_component_t* flecs_script_insert_var_component( + ecs_script_parser_t *parser, + const char *var_name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(var_name != NULL, ECS_INTERNAL_ERROR, NULL); - json = flecs_json_expect(json, JsonComma, token, desc); - if (!json) { - goto error; - } + ecs_script_var_component_t *result = flecs_ast_new( + parser, ecs_script_var_component_t, EcsAstVarComponent); + result->name = var_name; - json = flecs_json_expect_member(json, token, desc); - if (!json) { - goto error; - } + flecs_ast_append(parser, scope->stmts, + ecs_script_var_component_t, result); + return result; +} - ecs_entity_t parent = 0; - if (!ecs_os_strcmp(token, "parent")) { - json = flecs_json_expect(json, JsonString, token, desc); - if (!json) { - goto error; - } - parent = ecs_lookup_fullpath(world, token); +ecs_script_with_t* flecs_script_insert_with( + ecs_script_parser_t *parser) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); - json = flecs_json_expect(json, JsonComma, token, desc); - if (!json) { - goto error; - } + ecs_script_with_t *result = flecs_ast_new( + parser, ecs_script_with_t, EcsAstWith); - json = flecs_json_expect_member(json, token, desc); - if (!json) { - goto error; - } - } + result->expressions = flecs_script_scope_new(parser); + result->scope = flecs_script_scope_new(parser); - if (ecs_os_strcmp(token, "entities")) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected 'entities'"); - goto error; - } + flecs_ast_append(parser, scope->stmts, ecs_script_with_t, result); + return result; +} - json = flecs_json_expect(json, JsonArrayOpen, token, desc); - if (!json) { - goto error; - } +ecs_script_using_t* flecs_script_insert_using( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); - entities = json; /* store start of entity id array */ + ecs_script_using_t *result = flecs_ast_new( + parser, ecs_script_using_t, EcsAstUsing); - json = flecs_json_skip_array(json, token, desc); - if (!json) { - goto error; - } + result->name = name; + + flecs_ast_append(parser, scope->stmts, ecs_script_using_t, result); + return result; +} + +ecs_script_module_t* flecs_script_insert_module( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_module_t *result = flecs_ast_new( + parser, ecs_script_module_t, EcsAstModule); + + result->name = name; + + flecs_ast_append(parser, scope->stmts, ecs_script_module_t, result); + return result; +} + +ecs_script_annot_t* flecs_script_insert_annot( + ecs_script_parser_t *parser, + const char *name, + const char *expr) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); - json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonComma) { - json = flecs_json_expect_member_name(json, token, "values", desc); - if (!json) { - goto error; - } + ecs_script_annot_t *result = flecs_ast_new( + parser, ecs_script_annot_t, EcsAstAnnotation); - json = flecs_json_expect(json, JsonArrayOpen, token, desc); - if (!json) { - goto error; - } + result->name = name; + result->expr = expr; - values = json; /* store start of entities array */ - } else if (token_kind != JsonObjectClose) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or '}'"); - goto error; - } + flecs_ast_append(parser, scope->stmts, ecs_script_annot_t, result); + return result; +} - /* Find table from ids */ - ecs_table_t *table = flecs_json_parse_table(world, ids, token, desc); - if (!table) { - goto error; - } +ecs_script_template_node_t* flecs_script_insert_template( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); - /* Add entities to table */ - if (flecs_json_parse_entities(world, a, table, parent, - entities, token, records, desc)) - { - goto error; - } + ecs_script_template_node_t *result = flecs_ast_new( + parser, ecs_script_template_node_t, EcsAstTemplate); + result->name = name; + result->scope = flecs_script_scope_new(parser); - /* Parse values */ - if (values) { - json = flecs_json_parse_values(world, table, values, token, - records, columns_set, desc); - if (!json) { - goto error; - } + flecs_ast_append(parser, scope->stmts, ecs_script_template_node_t, result); + return result; +} - json = flecs_json_expect(json, JsonObjectClose, token, desc); - if (!json) { - goto error; - } - } +ecs_script_var_node_t* flecs_script_insert_var( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); - return json; -error: - return NULL; + ecs_script_var_node_t *result = flecs_ast_new( + parser, ecs_script_var_node_t, EcsAstConst); + result->name = name; + + flecs_ast_append(parser, scope->stmts, ecs_script_var_node_t, result); + return result; } -const char* ecs_world_from_json( - ecs_world_t *world, - const char *json, - const ecs_from_json_desc_t *desc_arg) +ecs_script_if_t* flecs_script_insert_if( + ecs_script_parser_t *parser) { - ecs_json_token_t token_kind; - char token[ECS_MAX_TOKEN_SIZE]; + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_from_json_desc_t desc = {0}; - ecs_allocator_t *a = &world->allocator; - ecs_vec_t records; - ecs_vec_t columns_set; - ecs_map_t anonymous_ids; - ecs_vec_init_t(a, &records, ecs_record_t*, 0); - ecs_vec_init_t(a, &columns_set, ecs_id_t, 0); - ecs_map_init(&anonymous_ids, a); + ecs_script_if_t *result = flecs_ast_new( + parser, ecs_script_if_t, EcsAstIf); + result->if_true = flecs_script_scope_new(parser); + result->if_false = flecs_script_scope_new(parser); - const char *name = NULL, *expr = json, *lah; - if (desc_arg) { - desc = *desc_arg; - } + flecs_ast_append(parser, scope->stmts, ecs_script_if_t, result); + return result; +} - if (!desc.lookup_action) { - desc.lookup_action = (ecs_entity_t(*)( - const ecs_world_t*, const char*, void*))flecs_json_ensure_entity; - desc.lookup_ctx = &anonymous_ids; - } +#endif - json = flecs_json_expect(json, JsonObjectOpen, token, &desc); - if (!json) { - goto error; - } +/** + * @file addons/script/expr.c + * @brief Evaluate script expressions. + */ - json = flecs_json_expect_member_name(json, token, "results", &desc); - if (!json) { - goto error; - } - json = flecs_json_expect(json, JsonArrayOpen, token, &desc); - if (!json) { - goto error; - } +#ifdef FLECS_SCRIPT +#include +#include - lah = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonArrayClose) { - json = lah; - goto end; - } +/* String deserializer for script expressions */ - do { - json = flecs_json_parse_result(world, a, json, token, - &records, &columns_set, &desc); - if (!json) { - goto error; - } +/* Order in enumeration is important, as it is used for precedence */ +typedef enum ecs_expr_oper_t { + EcsExprOperUnknown, + EcsLeftParen, + EcsCondAnd, + EcsCondOr, + EcsCondEq, + EcsCondNeq, + EcsCondGt, + EcsCondGtEq, + EcsCondLt, + EcsCondLtEq, + EcsShiftLeft, + EcsShiftRight, + EcsAdd, + EcsSub, + EcsMul, + EcsDiv, + EcsMin +} ecs_expr_oper_t; - json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonArrayClose) { - break; - } else if (token_kind != JsonComma) { - ecs_parser_error(name, expr, json - expr, - "expected ',' or ']'"); - goto error; - } - } while(json && json[0]); +/* Used to track temporary values */ +#define FLECS_EXPR_MAX_STACK_SIZE (256) -end: - ecs_vec_fini_t(a, &records, ecs_record_t*); - ecs_vec_fini_t(a, &columns_set, ecs_id_t); - ecs_map_fini(&anonymous_ids); +typedef struct ecs_expr_value_t { + const ecs_type_info_t *ti; + void *ptr; +} ecs_expr_value_t; - json = flecs_json_expect(json, JsonObjectClose, token, &desc); - if (!json) { - goto error; - } +typedef struct ecs_value_stack_t { + ecs_expr_value_t values[FLECS_EXPR_MAX_STACK_SIZE]; + ecs_stack_cursor_t *cursor; + ecs_stack_t *stack; + ecs_stage_t *stage; + int32_t count; +} ecs_value_stack_t; - return json; -error: - ecs_vec_fini_t(a, &records, ecs_record_t*); - ecs_vec_fini_t(a, &columns_set, ecs_id_t); - ecs_map_fini(&anonymous_ids); +static +const char* flecs_script_expr_run( + ecs_world_t *world, + ecs_value_stack_t *stack, + const char *ptr, + ecs_value_t *value, + ecs_expr_oper_t op, + const ecs_script_expr_run_desc_t *desc); - return NULL; +#define TOK_VARIABLE '$' + +/* -- Private functions -- */ + +static +bool flecs_isident( + char ch) +{ + return isalpha(ch) || (ch == '_'); } -#endif +static +bool flecs_valid_identifier_start_char( + char ch) +{ + if (ch && (flecs_isident(ch) || (ch == '*') || + (ch == '0') || (ch == TOK_VARIABLE) || isdigit(ch))) + { + return true; + } -/** - * @file json/json.c - * @brief JSON serializer utilities. - */ + return false; +} -#include +static +bool flecs_valid_token_start_char( + char ch) +{ + if ((ch == '"') || (ch == '{') || (ch == '}') || (ch == ',') || (ch == '-') + || (ch == '[') || (ch == ']') || (ch == '`') || + flecs_valid_identifier_start_char(ch)) + { + return true; + } -#ifdef FLECS_JSON + return false; +} static -const char* flecs_json_token_str( - ecs_json_token_t token_kind) +bool flecs_valid_token_char( + char ch) { - switch(token_kind) { - case JsonObjectOpen: return "{"; - case JsonObjectClose: return "}"; - case JsonArrayOpen: return "["; - case JsonArrayClose: return "]"; - case JsonColon: return ":"; - case JsonComma: return ","; - case JsonNumber: return "number"; - case JsonLargeInt: return "large integer"; - case JsonLargeString: - case JsonString: return "string"; - case JsonTrue: return "true"; - case JsonFalse: return "false"; - case JsonNull: return "null"; - case JsonInvalid: return "invalid"; - default: - ecs_throw(ECS_INTERNAL_ERROR, NULL); + if (ch && (flecs_isident(ch) || isdigit(ch) || ch == '.' || ch == '"')) { + return true; } -error: - return "<>"; + + return false; } -const char* flecs_json_parse( - const char *json, - ecs_json_token_t *token_kind, - char *token) +static +const char* flecs_parse_ws( + const char *ptr) { - json = ecs_parse_ws_eol(json); + while ((*ptr != '\n') && isspace(*ptr)) { + ptr ++; + } - char ch = json[0]; + return ptr; +} - if (ch == '{') { - token_kind[0] = JsonObjectOpen; - return json + 1; - } else if (ch == '}') { - token_kind[0] = JsonObjectClose; - return json + 1; - } else if (ch == '[') { - token_kind[0] = JsonArrayOpen; - return json + 1; - } else if (ch == ']') { - token_kind[0] = JsonArrayClose; - return json + 1; - } else if (ch == ':') { - token_kind[0] = JsonColon; - return json + 1; - } else if (ch == ',') { - token_kind[0] = JsonComma; - return json + 1; - } else if (ch == '"') { - const char *start = json; - char *token_ptr = token; - json ++; - for (; (ch = json[0]); ) { - if (ch == '"') { - json ++; - token_ptr[0] = '\0'; - break; - } +static +const char* flecs_parse_expr_token( + const char *name, + const char *expr, + const char *ptr, + char *token_out, + char delim) +{ + int64_t column = ptr - expr; - if (token_ptr - token >= ECS_MAX_TOKEN_SIZE) { - /* Token doesn't fit in buffer, signal to app to try again with - * dynamic buffer. */ - token_kind[0] = JsonLargeString; - return start; - } + ptr = flecs_parse_ws(ptr); + char *tptr = token_out, ch = ptr[0]; - json = ecs_chrparse(json, token_ptr ++); + if (!flecs_valid_token_start_char(ch)) { + if (ch == '\0' || ch == '\n') { + ecs_parser_error(name, expr, column, + "unexpected end of expression"); + } else { + ecs_parser_error(name, expr, column, + "invalid start of token '%s'", ptr); } + return NULL; + } - if (!ch) { - token_kind[0] = JsonInvalid; + bool isDigit = isdigit(ch) || (ch == '-'); + + if (ch == '-') { + if (!isdigit(ptr[1]) && ptr[1] != '$' && ptr[1] != '(') { + ecs_parser_error(name, expr, column, + "invalid number token"); return NULL; - } else { - token_kind[0] = JsonString; - return json; } - } else if (isdigit(ch) || (ch == '-')) { - token_kind[0] = JsonNumber; - const char *result = ecs_parse_digit(json, token); - - /* Cheap initial check if parsed token could represent large int */ - if (result - json > 15) { - /* Less cheap secondary check to see if number is integer */ - if (!strchr(token, '.')) { - token_kind[0] = JsonLargeInt; - } + if (ptr[1] == '$' || ptr[1] == '(') { + isDigit = false; /* -$var */ } + } - return result; - } else if (isalpha(ch)) { - if (!ecs_os_strncmp(json, "null", 4)) { - token_kind[0] = JsonNull; - json += 4; - } else - if (!ecs_os_strncmp(json, "true", 4)) { - token_kind[0] = JsonTrue; - json += 4; - } else - if (!ecs_os_strncmp(json, "false", 5)) { - token_kind[0] = JsonFalse; - json += 5; - } + bool hasDot = false; + bool hasE = false; - if (isalpha(json[0]) || isdigit(json[0])) { - token_kind[0] = JsonInvalid; - return NULL; - } + tptr[0] = ch; + tptr ++; + ptr ++; - return json; - } else { - token_kind[0] = JsonInvalid; - return NULL; + if (ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == ',' || ch == '`') { + tptr[0] = 0; + return ptr; } -} -const char* flecs_json_parse_large_string( - const char *json, - ecs_strbuf_t *buf) -{ - if (json[0] != '"') { - return NULL; /* can only parse strings */ - } + int tmpl_nesting = 0; + bool in_str = ch == '"'; - char ch, ch_out; - json ++; - for (; (ch = json[0]); ) { - if (ch == '"') { - json ++; + for (; (ch = *ptr); ptr ++) { + if (ch == '<') { + tmpl_nesting ++; + } else if (ch == '>') { + if (!tmpl_nesting) { + break; + } + tmpl_nesting --; + } else if (ch == '"') { + in_str = !in_str; + } else if (ch == '\\') { + ptr ++; + tptr[0] = ptr[0]; + tptr ++; + continue; + } else if (!flecs_valid_token_char(ch) && !in_str) { break; } - json = ecs_chrparse(json, &ch_out); - ecs_strbuf_appendch(buf, ch_out); + if (isDigit) { + if (!isdigit(ch) && (ch != '.') && (ch != 'e')) { + ecs_parser_error(name, expr, column, "invalid token"); + return NULL; + } + if (ch == '.') { + if (hasDot) { + ecs_parser_error(name, expr, column, "invalid token"); + return NULL; + } + hasDot = true; + } + if (ch == 'e') { + if (hasE) { + ecs_parser_error(name, expr, column, "invalid token"); + return NULL; + } + hasE = true; + } + } + + if (delim && (ch == delim)) { + break; + } + + tptr[0] = ch; + tptr ++; } - if (!ch) { + tptr[0] = '\0'; + + if (tmpl_nesting != 0) { + ecs_parser_error(name, expr, column, + "identifier '%s' has mismatching < > pairs", ptr); return NULL; + } + + const char *next_ptr = flecs_parse_ws(ptr); + if (next_ptr[0] == ':' && next_ptr != ptr) { + /* Whitespace between token and : is significant */ + ptr = next_ptr - 1; } else { - return json; + ptr = next_ptr; } + + return ptr; } -const char* flecs_json_expect( - const char *json, - ecs_json_token_t token_kind, - char *token, - const ecs_from_json_desc_t *desc) +static +void* flecs_expr_value_new( + ecs_value_stack_t *stack, + ecs_entity_t type) { - ecs_json_token_t kind = 0; - json = flecs_json_parse(json, &kind, token); - if (kind == JsonInvalid) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, "invalid json"); - return NULL; - } else if (kind != token_kind) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, "expected %s", - flecs_json_token_str(token_kind)); + ecs_stage_t *stage = stack->stage; + ecs_world_t *world = stage->world; + ecs_id_record_t *idr = flecs_id_record_get(world, type); + if (!idr) { return NULL; } - return json; -} -const char* flecs_json_expect_member( - const char *json, - char *token, - const ecs_from_json_desc_t *desc) -{ - json = flecs_json_expect(json, JsonString, token, desc); - if (!json) { + const ecs_type_info_t *ti = idr->type_info; + if (!ti) { return NULL; } - json = flecs_json_expect(json, JsonColon, token, desc); - if (!json) { - return NULL; + + ecs_assert(ti->size != 0, ECS_INTERNAL_ERROR, NULL); + void *result = flecs_stack_alloc(stack->stack, ti->size, ti->alignment); + if (ti->hooks.ctor) { + ti->hooks.ctor(result, 1, ti); + } else { + ecs_os_memset(result, 0, ti->size); } - return json; + if (ti->hooks.dtor) { + /* Track values that have destructors */ + stack->values[stack->count].ti = ti; + stack->values[stack->count].ptr = result; + stack->count ++; + } + + return result; } -const char* flecs_json_expect_member_name( - const char *json, - char *token, - const char *member_name, - const ecs_from_json_desc_t *desc) +static +const char* flecs_str_to_expr_oper( + const char *str, + ecs_expr_oper_t *op) { - json = flecs_json_expect_member(json, token, desc); - if (!json) { - return NULL; - } - if (ecs_os_strcmp(token, member_name)) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected member '%s'", member_name); - return NULL; + if (!ecs_os_strncmp(str, "+", 1)) { + *op = EcsAdd; + return str + 1; + } else if (!ecs_os_strncmp(str, "-", 1)) { + *op = EcsSub; + return str + 1; + } else if (!ecs_os_strncmp(str, "*", 1)) { + *op = EcsMul; + return str + 1; + } else if (!ecs_os_strncmp(str, "/", 1)) { + *op = EcsDiv; + return str + 1; + } else if (!ecs_os_strncmp(str, "&&", 2)) { + *op = EcsCondAnd; + return str + 2; + } else if (!ecs_os_strncmp(str, "||", 2)) { + *op = EcsCondOr; + return str + 2; + } else if (!ecs_os_strncmp(str, "==", 2)) { + *op = EcsCondEq; + return str + 2; + } else if (!ecs_os_strncmp(str, "!=", 2)) { + *op = EcsCondNeq; + return str + 2; + } else if (!ecs_os_strncmp(str, ">=", 2)) { + *op = EcsCondGtEq; + return str + 2; + } else if (!ecs_os_strncmp(str, "<=", 2)) { + *op = EcsCondLtEq; + return str + 2; + } else if (!ecs_os_strncmp(str, ">>", 2)) { + *op = EcsShiftRight; + return str + 2; + } else if (!ecs_os_strncmp(str, "<<", 2)) { + *op = EcsShiftLeft; + return str + 2; + } else if (!ecs_os_strncmp(str, ">", 1)) { + *op = EcsCondGt; + return str + 1; + } else if (!ecs_os_strncmp(str, "<", 1)) { + *op = EcsCondLt; + return str + 1; } - return json; + + *op = EcsExprOperUnknown; + return NULL; } -const char* flecs_json_skip_object( - const char *json, - char *token, - const ecs_from_json_desc_t *desc) +static +const char *flecs_script_expr_parse_token( + const char *name, + const char *expr, + const char *ptr, + char *token) { - ecs_json_token_t token_kind = 0; + char *token_ptr = token; - while ((json = flecs_json_parse(json, &token_kind, token))) { - if (token_kind == JsonObjectOpen) { - json = flecs_json_skip_object(json, token, desc); - } else if (token_kind == JsonArrayOpen) { - json = flecs_json_skip_array(json, token, desc); - } else if (token_kind == JsonObjectClose) { - return json; - } else if (token_kind == JsonArrayClose) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected }"); + if (ptr[0] == '/') { + char ch; + if (ptr[1] == '/') { + // Single line comment + for (ptr = &ptr[2]; (ch = ptr[0]) && (ch != '\n'); ptr ++) {} + token[0] = 0; + return flecs_parse_ws_eol(ptr); + } else if (ptr[1] == '*') { + // Multi line comment + for (ptr = &ptr[2]; (ch = ptr[0]); ptr ++) { + if (ch == '*' && ptr[1] == '/') { + token[0] = 0; + return flecs_parse_ws_eol(ptr + 2); + } + } + + ecs_parser_error(name, expr, ptr - expr, + "missing */ for multiline comment"); return NULL; } } - ecs_parser_error(desc->name, desc->expr, json - desc->expr, "expected }"); - return NULL; + ecs_expr_oper_t op; + if (ptr[0] == '(') { + token[0] = '('; + token[1] = 0; + return ptr + 1; + } else if (ptr[0] != '-') { + const char *tptr = flecs_str_to_expr_oper(ptr, &op); + if (tptr) { + ecs_os_strncpy(token, ptr, tptr - ptr); + return tptr; + } + } + + while ((ptr = flecs_parse_expr_token(name, expr, ptr, token_ptr, 0))) { + if (ptr[0] == '|' && ptr[1] != '|') { + token_ptr = &token_ptr[ecs_os_strlen(token_ptr)]; + token_ptr[0] = '|'; + token_ptr[1] = '\0'; + token_ptr ++; + ptr ++; + } else { + break; + } + } + + return ptr; } -const char* flecs_json_skip_array( - const char *json, - char *token, - const ecs_from_json_desc_t *desc) +static +const char* flecs_parse_multiline_string( + ecs_meta_cursor_t *cur, + const char *name, + const char *expr, + const char *ptr) { - ecs_json_token_t token_kind = 0; - - while ((json = flecs_json_parse(json, &token_kind, token))) { - if (token_kind == JsonObjectOpen) { - json = flecs_json_skip_object(json, token, desc); - } else if (token_kind == JsonArrayOpen) { - json = flecs_json_skip_array(json, token, desc); - } else if (token_kind == JsonObjectClose) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ]"); - return NULL; - } else if (token_kind == JsonArrayClose) { - return json; + /* Multiline string */ + ecs_strbuf_t str = ECS_STRBUF_INIT; + char ch; + while ((ch = ptr[0]) && (ch != '`')) { + if (ch == '\\' && ptr[1] == '`') { + ch = '`'; + ptr ++; } + ecs_strbuf_appendch(&str, ch); + ptr ++; + } + + if (ch != '`') { + ecs_parser_error(name, expr, ptr - expr, + "missing '`' to close multiline string"); + goto error; } - ecs_parser_error(desc->name, desc->expr, json - desc->expr, "expected ]"); + char *strval = ecs_strbuf_get(&str); + if (ecs_meta_set_string(cur, strval) != 0) { + ecs_os_free(strval); + goto error; + } + ecs_os_free(strval); + + return ptr + 1; +error: return NULL; } -void flecs_json_next( - ecs_strbuf_t *buf) +static +bool flecs_parse_is_float( + const char *ptr) { - ecs_strbuf_list_next(buf); + ecs_assert(isdigit(ptr[0]), ECS_INTERNAL_ERROR, NULL); + char ch; + while ((ch = (++ptr)[0])) { + if (ch == '.' || ch == 'e') { + return true; + } + if (!isdigit(ch)) { + return false; + } + } + return false; } -void flecs_json_number( - ecs_strbuf_t *buf, - double value) +/* Attempt to resolve variable dotexpression to value (foo.bar) */ +static +ecs_value_t flecs_dotresolve_var( + ecs_world_t *world, + ecs_script_vars_t *vars, + char *token) { - ecs_strbuf_appendflt(buf, value, '"'); -} + char *dot = strchr(token, '.'); + if (!dot) { + return (ecs_value_t){ .type = ecs_id(ecs_entity_t) }; + } -void flecs_json_true( - ecs_strbuf_t *buf) -{ - ecs_strbuf_appendlit(buf, "true"); -} + dot[0] = '\0'; -void flecs_json_false( - ecs_strbuf_t *buf) -{ - ecs_strbuf_appendlit(buf, "false"); -} + const ecs_script_var_t *var = ecs_script_vars_lookup(vars, token); + if (!var) { + return (ecs_value_t){0}; + } -void flecs_json_bool( - ecs_strbuf_t *buf, - bool value) -{ - if (value) { - flecs_json_true(buf); - } else { - flecs_json_false(buf); + ecs_meta_cursor_t cur = ecs_meta_cursor( + world, var->value.type, var->value.ptr); + ecs_meta_push(&cur); + if (ecs_meta_dotmember(&cur, dot + 1) != 0) { + return (ecs_value_t){0}; } -} -void flecs_json_array_push( - ecs_strbuf_t *buf) -{ - ecs_strbuf_list_push(buf, "[", ", "); + return (ecs_value_t){ + .ptr = ecs_meta_get_ptr(&cur), + .type = ecs_meta_get_type(&cur) + }; } -void flecs_json_array_pop( - ecs_strbuf_t *buf) +static +int flecs_meta_call( + ecs_world_t *world, + ecs_value_stack_t *stack, + const char *name, + const char *expr, + const char *ptr, + ecs_meta_cursor_t *cur, + const char *function) { - ecs_strbuf_list_pop(buf, "]"); -} + ecs_entity_t type = ecs_meta_get_type(cur); + void *value_ptr = ecs_meta_get_ptr(cur); -void flecs_json_object_push( - ecs_strbuf_t *buf) -{ - ecs_strbuf_list_push(buf, "{", ", "); -} + if (!ecs_os_strcmp(function, "parent")) { + if (type != ecs_id(ecs_entity_t)) { + ecs_parser_error(name, expr, ptr - expr, + "parent() can only be called on entity"); + return -1; + } -void flecs_json_object_pop( - ecs_strbuf_t *buf) -{ - ecs_strbuf_list_pop(buf, "}"); -} + *(ecs_entity_t*)value_ptr = ecs_get_parent( + world, *(ecs_entity_t*)value_ptr); + } else if (!ecs_os_strcmp(function, "name")) { + if (type != ecs_id(ecs_entity_t)) { + ecs_parser_error(name, expr, ptr - expr, + "name() can only be called on entity"); + return -1; + } -void flecs_json_string( - ecs_strbuf_t *buf, - const char *value) -{ - ecs_strbuf_appendch(buf, '"'); - ecs_strbuf_appendstr(buf, value); - ecs_strbuf_appendch(buf, '"'); -} + char **result = flecs_expr_value_new(stack, ecs_id(ecs_string_t)); + *result = ecs_os_strdup(ecs_get_name(world, *(ecs_entity_t*)value_ptr)); + *cur = ecs_meta_cursor(world, ecs_id(ecs_string_t), result); + } else if (!ecs_os_strcmp(function, "doc_name")) { +#ifdef FLECS_DOC + if (type != ecs_id(ecs_entity_t)) { + ecs_parser_error(name, expr, ptr - expr, + "name() can only be called on entity"); + return -1; + } -void flecs_json_string_escape( - ecs_strbuf_t *buf, - const char *value) -{ - ecs_size_t length = ecs_stresc(NULL, 0, '"', value); - if (length == ecs_os_strlen(value)) { - ecs_strbuf_appendch(buf, '"'); - ecs_strbuf_appendstrn(buf, value, length); - ecs_strbuf_appendch(buf, '"'); + char **result = flecs_expr_value_new(stack, ecs_id(ecs_string_t)); + *result = ecs_os_strdup(ecs_doc_get_name(world, *(ecs_entity_t*)value_ptr)); + *cur = ecs_meta_cursor(world, ecs_id(ecs_string_t), result); +#else + ecs_parser_error(name, expr, ptr - expr, + "doc_name() is not available without FLECS_DOC addon"); + return -1; +#endif } else { - char *out = ecs_os_malloc(length + 3); - ecs_stresc(out + 1, length, '"', value); - out[0] = '"'; - out[length + 1] = '"'; - out[length + 2] = '\0'; - ecs_strbuf_appendstr_zerocpy(buf, out); + ecs_parser_error(name, expr, ptr - expr, + "unknown function '%s'", function); + return -1; } -} -void flecs_json_member( - ecs_strbuf_t *buf, - const char *name) -{ - flecs_json_membern(buf, name, ecs_os_strlen(name)); + return 0; } -void flecs_json_membern( - ecs_strbuf_t *buf, +/* Determine the type of an expression from the first character(s). This allows + * us to initialize a storage for a type if none was provided. */ +static +ecs_entity_t flecs_parse_discover_type( + ecs_world_t *world, const char *name, - int32_t name_len) -{ - ecs_strbuf_list_appendch(buf, '"'); - ecs_strbuf_appendstrn(buf, name, name_len); - ecs_strbuf_appendlit(buf, "\":"); -} - -void flecs_json_path( - ecs_strbuf_t *buf, - const ecs_world_t *world, - ecs_entity_t e) + const char *expr, + const char *ptr, + ecs_entity_t input_type, + const ecs_script_expr_run_desc_t *desc) { - ecs_strbuf_appendch(buf, '"'); - ecs_get_path_w_sep_buf(world, 0, e, ".", "", buf); - ecs_strbuf_appendch(buf, '"'); -} + /* String literal */ + if (ptr[0] == '"' || ptr[0] == '`') { + if (input_type == ecs_id(ecs_char_t)) { + return input_type; + } + return ecs_id(ecs_string_t); + } -void flecs_json_label( - ecs_strbuf_t *buf, - const ecs_world_t *world, - ecs_entity_t e) -{ - const char *lbl = NULL; -#ifdef FLECS_DOC - lbl = ecs_doc_get_name(world, e); -#else - lbl = ecs_get_name(world, e); -#endif + /* Negative number literal */ + if (ptr[0] == '-') { + if (!isdigit(ptr[1])) { + ecs_parser_error(name, expr, ptr - expr, "invalid literal"); + return 0; + } + if (flecs_parse_is_float(ptr + 1)) { + return ecs_id(ecs_f64_t); + } else { + return ecs_id(ecs_i64_t); + } + } - if (lbl) { - ecs_strbuf_appendch(buf, '"'); - ecs_strbuf_appendstr(buf, lbl); - ecs_strbuf_appendch(buf, '"'); - } else { - ecs_strbuf_appendch(buf, '0'); + /* Positive number literal */ + if (isdigit(ptr[0])) { + if (flecs_parse_is_float(ptr)) { + return ecs_id(ecs_f64_t); + } else { + return ecs_id(ecs_u64_t); + } } -} -void flecs_json_color( - ecs_strbuf_t *buf, - const ecs_world_t *world, - ecs_entity_t e) -{ - (void)world; - (void)e; + /* Variable */ + if (ptr[0] == '$') { + if (!desc || !desc->vars) { + ecs_parser_error(name, expr, ptr - expr, + "unresolved variable (no variable scope)"); + return 0; + } - const char *color = NULL; -#ifdef FLECS_DOC - color = ecs_doc_get_color(world, e); -#endif + char token[ECS_MAX_TOKEN_SIZE]; + if (flecs_script_expr_parse_token(name, expr, &ptr[1], token) == NULL) { + return 0; + } - if (color) { - ecs_strbuf_appendch(buf, '"'); - ecs_strbuf_appendstr(buf, color); - ecs_strbuf_appendch(buf, '"'); - } else { - ecs_strbuf_appendch(buf, '0'); + const ecs_script_var_t *var = ecs_script_vars_lookup(desc->vars, token); + if (!var) { + ecs_size_t len = ecs_os_strlen(token); + if (ptr[len + 1] == '(') { + while ((len > 0) && (token[len] != '.')) { + len --; + } + if (token[len] == '.') { + token[len] = '\0'; + } + + return ecs_id(ecs_entity_t); + } + + ecs_value_t v = flecs_dotresolve_var(world, desc->vars, token); + if (v.type) { + return v.type; + } + + ecs_parser_error(name, expr, ptr - expr, + "unresolved variable '%s'", token); + return 0; + } + return var->value.type; } -} -void flecs_json_id( - ecs_strbuf_t *buf, - const ecs_world_t *world, - ecs_id_t id) -{ - ecs_strbuf_appendch(buf, '['); + /* Boolean */ + if (ptr[0] == 't' && !ecs_os_strncmp(ptr, "true", 4)) { + if (!isalpha(ptr[4]) && ptr[4] != '_') { + return ecs_id(ecs_bool_t); + } + } - if (ECS_IS_PAIR(id)) { - ecs_entity_t first = ecs_pair_first(world, id); - ecs_entity_t second = ecs_pair_second(world, id); - ecs_strbuf_appendch(buf, '"'); - ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf); - ecs_strbuf_appendch(buf, '"'); - ecs_strbuf_appendch(buf, ','); - ecs_strbuf_appendch(buf, '"'); - ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf); - ecs_strbuf_appendch(buf, '"'); - } else { - ecs_strbuf_appendch(buf, '"'); - ecs_get_path_w_sep_buf(world, 0, id & ECS_COMPONENT_MASK, ".", "", buf); - ecs_strbuf_appendch(buf, '"'); + if (ptr[0] == 'f' && !ecs_os_strncmp(ptr, "false", 5)) { + if (!isalpha(ptr[5]) && ptr[5] != '_') { + return ecs_id(ecs_bool_t); + } } - ecs_strbuf_appendch(buf, ']'); -} + /* Entity identifier */ + if (isalpha(ptr[0])) { + if (!input_type) { /* Identifier could also be enum/bitmask constant */ + char token[ECS_MAX_TOKEN_SIZE]; + const char *tptr = flecs_script_expr_parse_token( + name, expr, ptr, token); + if (!tptr) { + return 0; + } -ecs_primitive_kind_t flecs_json_op_to_primitive_kind( - ecs_meta_type_op_kind_t kind) -{ - return kind - EcsOpPrimitive; -} + if (tptr[0] != '[') { + return ecs_id(ecs_entity_t); + } -#endif + tptr = flecs_script_expr_parse_token( + name, expr, tptr + 1, token); + if (!tptr) { + return 0; + } -/** - * @file json/serialize.c - * @brief Serialize (component) values to JSON strings. - */ + ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(desc->lookup_action != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t type = desc->lookup_action( + world, token, desc->lookup_ctx); + if (!type) { + ecs_parser_error(name, expr, ptr - expr, + "unresolved type '%s'", token); + return 0; + } + if (tptr[0] != ']') { + ecs_parser_error(name, expr, ptr - expr, + "missing ']' after '%s'", token); + return 0; + } -#ifdef FLECS_JSON + if (tptr[1] != '.') { + return type; + } -/* Cached id records during serialization */ -typedef struct ecs_json_ser_idr_t { - ecs_id_record_t *idr_doc_name; - ecs_id_record_t *idr_doc_color; -} ecs_json_ser_idr_t; + ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, NULL); + ecs_meta_push(&cur); -static -int json_ser_type( - const ecs_world_t *world, - const ecs_vec_t *ser, - const void *base, - ecs_strbuf_t *str); + tptr = flecs_script_expr_parse_token( + name, expr, tptr + 2, token); + if (!tptr) { + return 0; + } -static -int json_ser_type_ops( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - const void *base, - ecs_strbuf_t *str, - int32_t in_array); + if (ecs_meta_dotmember(&cur, token) != 0) { + ecs_parser_error(name, expr, ptr - expr, + "failed to assign member '%s'", token); + return 0; + } -static -int json_ser_type_op( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *base, - ecs_strbuf_t *str); + return ecs_meta_get_type(&cur); + } + } -/* Serialize enumeration */ -static -int json_ser_enum( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *base, - ecs_strbuf_t *str) -{ - const EcsEnum *enum_type = ecs_get(world, op->type, EcsEnum); - ecs_check(enum_type != NULL, ECS_INVALID_PARAMETER, NULL); + /* If no default type was provided we can't automatically deduce the type of + * composite/collection expressions. */ + if (!input_type) { + if (ptr[0] == '{') { + ecs_parser_error(name, expr, ptr - expr, + "unknown type for composite literal"); + return 0; + } - int32_t value = *(const int32_t*)base; - - /* Enumeration constants are stored in a map that is keyed on the - * enumeration value. */ - ecs_enum_constant_t *constant = ecs_map_get_deref(&enum_type->constants, - ecs_enum_constant_t, (ecs_map_key_t)value); - if (!constant) { - /* If the value is not found, it is not a valid enumeration constant */ - char *name = ecs_get_fullpath(world, op->type); - ecs_err("enumeration value '%d' of type '%s' is not a valid constant", - value, name); - ecs_os_free(name); - goto error; + if (ptr[0] == '[') { + ecs_parser_error(name, expr, ptr - expr, + "unknown type for collection literal"); + return 0; + } + + ecs_parser_error(name, expr, ptr - expr, "invalid expression"); } - ecs_strbuf_appendch(str, '"'); - ecs_strbuf_appendstr(str, ecs_get_name(world, constant->constant)); - ecs_strbuf_appendch(str, '"'); + return input_type; +} - return 0; +/* Normalize types to their largest representation. + * Rather than taking the original type of a value, use the largest + * representation of the type so we don't have to worry about overflowing the + * original type in the operation. */ +static +ecs_entity_t flecs_largest_type( + const EcsPrimitive *type) +{ + switch(type->kind) { + case EcsBool: return ecs_id(ecs_bool_t); + case EcsChar: return ecs_id(ecs_char_t); + case EcsByte: return ecs_id(ecs_u8_t); + case EcsU8: return ecs_id(ecs_u64_t); + case EcsU16: return ecs_id(ecs_u64_t); + case EcsU32: return ecs_id(ecs_u64_t); + case EcsU64: return ecs_id(ecs_u64_t); + case EcsI8: return ecs_id(ecs_i64_t); + case EcsI16: return ecs_id(ecs_i64_t); + case EcsI32: return ecs_id(ecs_i64_t); + case EcsI64: return ecs_id(ecs_i64_t); + case EcsF32: return ecs_id(ecs_f64_t); + case EcsF64: return ecs_id(ecs_f64_t); + case EcsUPtr: return ecs_id(ecs_u64_t); + case EcsIPtr: return ecs_id(ecs_i64_t); + case EcsString: return ecs_id(ecs_string_t); + case EcsEntity: return ecs_id(ecs_entity_t); + case EcsId: return ecs_id(ecs_id_t); + default: ecs_throw(ECS_INTERNAL_ERROR, NULL); + } error: - return -1; + return 0; +} + +/** Test if a normalized type can promote to another type in an expression */ +static +bool flecs_is_type_number( + ecs_entity_t type) +{ + if (type == ecs_id(ecs_bool_t)) return false; + else if (type == ecs_id(ecs_char_t)) return false; + else if (type == ecs_id(ecs_u8_t)) return false; + else if (type == ecs_id(ecs_u64_t)) return true; + else if (type == ecs_id(ecs_i64_t)) return true; + else if (type == ecs_id(ecs_f64_t)) return true; + else if (type == ecs_id(ecs_string_t)) return false; + else if (type == ecs_id(ecs_entity_t)) return false; + else return false; } -/* Serialize bitmask */ static -int json_ser_bitmask( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *ptr, - ecs_strbuf_t *str) +bool flecs_oper_valid_for_type( + ecs_entity_t type, + ecs_expr_oper_t op) { - const EcsBitmask *bitmask_type = ecs_get(world, op->type, EcsBitmask); - ecs_check(bitmask_type != NULL, ECS_INVALID_PARAMETER, NULL); - - uint32_t value = *(const uint32_t*)ptr; - if (!value) { - ecs_strbuf_appendch(str, '0'); - return 0; + switch(op) { + case EcsAdd: + case EcsSub: + case EcsMul: + case EcsDiv: + return flecs_is_type_number(type); + case EcsCondEq: + case EcsCondNeq: + case EcsCondAnd: + case EcsCondOr: + case EcsCondGt: + case EcsCondGtEq: + case EcsCondLt: + case EcsCondLtEq: + return flecs_is_type_number(type) || + (type == ecs_id(ecs_bool_t)) || + (type == ecs_id(ecs_char_t)) || + (type == ecs_id(ecs_entity_t)); + case EcsShiftLeft: + case EcsShiftRight: + return (type == ecs_id(ecs_u64_t)); + case EcsExprOperUnknown: + case EcsLeftParen: + case EcsMin: + return false; + default: + ecs_abort(ECS_INTERNAL_ERROR, NULL); } +} - ecs_strbuf_list_push(str, "\"", "|"); - - /* Multiple flags can be set at a given time. Iterate through all the flags - * and append the ones that are set. */ - ecs_map_iter_t it = ecs_map_iter(&bitmask_type->constants); - while (ecs_map_next(&it)) { - ecs_bitmask_constant_t *constant = ecs_map_ptr(&it); - ecs_map_key_t key = ecs_map_key(&it); - if ((value & key) == key) { - ecs_strbuf_list_appendstr(str, - ecs_get_name(world, constant->constant)); - value -= (uint32_t)key; - } +/** Promote type to most expressive (f64 > i64 > u64) */ +static +ecs_entity_t flecs_promote_type( + ecs_entity_t type, + ecs_entity_t promote_to) +{ + if (type == ecs_id(ecs_u64_t)) { + return promote_to; } - - if (value != 0) { - /* All bits must have been matched by a constant */ - char *name = ecs_get_fullpath(world, op->type); - ecs_err("bitmask value '%u' of type '%s' contains invalid/unknown bits", - value, name); - ecs_os_free(name); - goto error; + if (promote_to == ecs_id(ecs_u64_t)) { + return type; } - - ecs_strbuf_list_pop(str, "\""); - - return 0; -error: - return -1; + if (type == ecs_id(ecs_f64_t)) { + return type; + } + if (promote_to == ecs_id(ecs_f64_t)) { + return promote_to; + } + return ecs_id(ecs_i64_t); } -/* Serialize elements of a contiguous array */ static -int json_ser_elements( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - const void *base, - int32_t elem_count, - int32_t elem_size, - ecs_strbuf_t *str, - bool is_array) +int flecs_oper_precedence( + ecs_expr_oper_t left, + ecs_expr_oper_t right) { - flecs_json_array_push(str); - - const void *ptr = base; - - int i; - for (i = 0; i < elem_count; i ++) { - ecs_strbuf_list_next(str); - if (json_ser_type_ops(world, ops, op_count, ptr, str, is_array)) { - return -1; - } - ptr = ECS_OFFSET(ptr, elem_size); - } - - flecs_json_array_pop(str); - - return 0; + return (left > right) - (left < right); } static -int json_ser_type_elements( - const ecs_world_t *world, - ecs_entity_t type, - const void *base, - int32_t elem_count, - ecs_strbuf_t *str, - bool is_array) +void flecs_value_cast( + ecs_world_t *world, + ecs_value_stack_t *stack, + ecs_value_t *value, + ecs_entity_t type) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); + if (value->type == type) { + return; + } - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_value_t result; + result.type = type; + result.ptr = flecs_expr_value_new(stack, type); - ecs_meta_type_op_t *ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t); - int32_t op_count = ecs_vec_count(&ser->ops); + if (value->ptr) { + ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, result.ptr); + ecs_meta_set_value(&cur, value); + } - return json_ser_elements( - world, ops, op_count, base, elem_count, comp->size, str, is_array); + *value = result; } -/* Serialize array */ static -int json_ser_array( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *ptr, - ecs_strbuf_t *str) +bool flecs_expr_op_is_equality( + ecs_expr_oper_t op) { - const EcsArray *a = ecs_get(world, op->type, EcsArray); - ecs_assert(a != NULL, ECS_INTERNAL_ERROR, NULL); - - return json_ser_type_elements( - world, a->type, ptr, a->count, str, true); + switch(op) { + case EcsCondEq: + case EcsCondNeq: + case EcsCondGt: + case EcsCondGtEq: + case EcsCondLt: + case EcsCondLtEq: + return true; + case EcsCondAnd: + case EcsCondOr: + case EcsShiftLeft: + case EcsShiftRight: + case EcsAdd: + case EcsSub: + case EcsMul: + case EcsDiv: + case EcsExprOperUnknown: + case EcsLeftParen: + case EcsMin: + return false; + default: + ecs_throw(ECS_INTERNAL_ERROR, "invalid operator"); + } +error: + return false; } -/* Serialize vector */ static -int json_ser_vector( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *base, - ecs_strbuf_t *str) +ecs_entity_t flecs_binary_expr_type( + ecs_world_t *world, + const char *name, + const char *expr, + const char *ptr, + ecs_value_t *lvalue, + ecs_value_t *rvalue, + ecs_expr_oper_t op, + ecs_entity_t *operand_type_out) { - const ecs_vec_t *value = base; - const EcsVector *v = ecs_get(world, op->type, EcsVector); - ecs_assert(v != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t result_type = 0, operand_type = 0; - int32_t count = ecs_vec_count(value); - void *array = ecs_vec_first(value); + switch(op) { + case EcsDiv: + /* Result type of a division is always a float */ + *operand_type_out = ecs_id(ecs_f64_t); + return ecs_id(ecs_f64_t); + case EcsCondAnd: + case EcsCondOr: + /* Result type of a condition operator is always a bool */ + *operand_type_out = ecs_id(ecs_bool_t); + return ecs_id(ecs_bool_t); + case EcsCondEq: + case EcsCondNeq: + case EcsCondGt: + case EcsCondGtEq: + case EcsCondLt: + case EcsCondLtEq: + /* Result type of equality operator is always bool, but operand types + * should not be casted to bool */ + result_type = ecs_id(ecs_bool_t); + break; + case EcsShiftLeft: + case EcsShiftRight: + case EcsAdd: + case EcsSub: + case EcsMul: + case EcsExprOperUnknown: + case EcsLeftParen: + case EcsMin: + break; + default: + ecs_throw(ECS_INTERNAL_ERROR, "invalid operator"); + } - /* Serialize contiguous buffer of vector */ - return json_ser_type_elements(world, v->type, array, count, str, false); -} + /* Result type for arithmetic operators is determined by operands */ + const EcsPrimitive *ltype_ptr = ecs_get(world, lvalue->type, EcsPrimitive); + const EcsPrimitive *rtype_ptr = ecs_get(world, rvalue->type, EcsPrimitive); + if (!ltype_ptr || !rtype_ptr) { + char *lname = ecs_get_path(world, lvalue->type); + char *rname = ecs_get_path(world, rvalue->type); + ecs_parser_error(name, expr, ptr - expr, + "invalid non-primitive type in binary expression (%s, %s)", + lname, rname); + ecs_os_free(lname); + ecs_os_free(rname); + return 0; + } -typedef struct json_serializer_ctx_t { - ecs_strbuf_t *str; - bool is_collection; - bool is_struct; -} json_serializer_ctx_t; + ecs_entity_t ltype = flecs_largest_type(ltype_ptr); + ecs_entity_t rtype = flecs_largest_type(rtype_ptr); + if (ltype == rtype) { + operand_type = ltype; + goto done; + } -static -int json_ser_custom_value( - const ecs_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); + if (flecs_expr_op_is_equality(op)) { + char *lstr = ecs_id_str(world, ltype); + char *rstr = ecs_id_str(world, rtype); + ecs_parser_error(name, expr, ptr - expr, + "mismatching types in equality expression (%s vs %s)", + lstr, rstr); + ecs_os_free(rstr); + ecs_os_free(lstr); + return 0; } - return ecs_ptr_to_json_buf(ser->world, type, value, json_ser->str); -} -static -int json_ser_custom_member( - const ecs_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; + if (!flecs_is_type_number(ltype) || !flecs_is_type_number(rtype)) { + ecs_parser_error(name, expr, ptr - expr, + "incompatible types in binary expression"); + return 0; } - flecs_json_member(json_ser->str, name); + + operand_type = flecs_promote_type(ltype, rtype); + +done: + if (op == EcsSub && operand_type == ecs_id(ecs_u64_t)) { + /* Result of subtracting two unsigned ints can be negative */ + operand_type = ecs_id(ecs_i64_t); + } + + if (!result_type) { + result_type = operand_type; + } + + *operand_type_out = operand_type; + return result_type; +error: 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); - ecs_assert(ct->serialize != NULL, ECS_INVALID_OPERATION, - ecs_get_name(world, op->type)); +/* Macro's to let the compiler do the operations & conversion work for us */ - const EcsMetaType *pt = ecs_get(world, ct->as_type, EcsMetaType); - ecs_assert(pt != NULL, ECS_INVALID_OPERATION, NULL); +#define ECS_VALUE_GET(value, T) (*(T*)value->ptr) - ecs_type_kind_t kind = pt->kind; - bool is_collection = false; - bool is_struct = false; +#define ECS_BINARY_OP_T(left, right, result, op, R, T)\ + ECS_VALUE_GET(result, R) = ECS_VALUE_GET(left, T) op ECS_VALUE_GET(right, T) - 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; +#define ECS_BINARY_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_u64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_u64_t, ecs_u64_t);\ + } else if (left->type == ecs_id(ecs_i64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_i64_t, ecs_i64_t);\ + } else if (left->type == ecs_id(ecs_f64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_f64_t, ecs_f64_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ } - json_serializer_ctx_t json_ser = { - .str = str, - .is_struct = is_struct, - .is_collection = is_collection - }; - - ecs_serializer_t ser = { - .world = world, - .value = json_ser_custom_value, - .member = json_ser_custom_member, - .ctx = &json_ser - }; +#define ECS_BINARY_COND_EQ_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_u64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u64_t);\ + } else if (left->type == ecs_id(ecs_i64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_i64_t);\ + } else if (left->type == ecs_id(ecs_f64_t)) { \ + ecs_parser_error(name, expr, ptr - expr, "unsupported operator for floating point");\ + return -1;\ + } else if (left->type == ecs_id(ecs_u8_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u8_t);\ + } else if (left->type == ecs_id(ecs_char_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_char_t);\ + } else if (left->type == ecs_id(ecs_bool_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ + } - if (ct->serialize(&ser, base)) { - return -1; +#define ECS_BINARY_COND_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_u64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u64_t);\ + } else if (left->type == ecs_id(ecs_i64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_i64_t);\ + } else if (left->type == ecs_id(ecs_f64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_f64_t);\ + } else if (left->type == ecs_id(ecs_u8_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u8_t);\ + } else if (left->type == ecs_id(ecs_char_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_char_t);\ + } else if (left->type == ecs_id(ecs_bool_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ } - if (kind == EcsStructType) { - flecs_json_object_pop(str); - } else if (kind == EcsArrayType || kind == EcsVectorType) { - flecs_json_array_pop(str); +#define ECS_BINARY_BOOL_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_bool_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ } - return 0; -} +#define ECS_BINARY_UINT_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_u64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_u64_t, ecs_u64_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ + } -/* Forward serialization to the different type kinds */ static -int json_ser_type_op( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *ptr, - ecs_strbuf_t *str) +int flecs_binary_expr_do( + ecs_world_t *world, + ecs_value_stack_t *stack, + const char *name, + const char *expr, + const char *ptr, + ecs_value_t *lvalue, + ecs_value_t *rvalue, + ecs_value_t *result, + ecs_expr_oper_t op) { - void *vptr = ECS_OFFSET(ptr, op->offset); - bool large_int = false; - if (op->kind == EcsOpI64) { - if (*(int64_t*)vptr >= 2147483648) { - large_int = true; - } - } else if (op->kind == EcsOpU64) { - if (*(uint64_t*)vptr >= 2147483648) { - large_int = true; - } + /* Find expression type */ + ecs_entity_t operand_type, type = flecs_binary_expr_type( + world, name, expr, ptr, lvalue, rvalue, op, &operand_type); + if (!type) { + goto error; } - if (large_int) { - ecs_strbuf_appendch(str, '"'); + if (!flecs_oper_valid_for_type(type, op)) { + ecs_parser_error(name, expr, ptr - expr, "invalid operator for type"); + goto error; } - switch(op->kind) { - case EcsOpPush: - case EcsOpPop: - /* Should not be parsed as single op */ - ecs_throw(ECS_INVALID_PARAMETER, NULL); + flecs_value_cast(world, stack, lvalue, operand_type); + flecs_value_cast(world, stack, rvalue, operand_type); + + ecs_value_t *storage = result; + ecs_value_t tmp_storage = {0}; + if (result->type != type) { + storage = &tmp_storage; + storage->type = type; + storage->ptr = flecs_expr_value_new(stack, type); + } + + switch(op) { + case EcsAdd: + ECS_BINARY_OP(lvalue, rvalue, storage, +); break; - case EcsOpF32: - ecs_strbuf_appendflt(str, - (ecs_f64_t)*(const ecs_f32_t*)vptr, '"'); + case EcsSub: + ECS_BINARY_OP(lvalue, rvalue, storage, -); break; - case EcsOpF64: - ecs_strbuf_appendflt(str, - *(ecs_f64_t*)vptr, '"'); + case EcsMul: + ECS_BINARY_OP(lvalue, rvalue, storage, *); break; - case EcsOpEnum: - if (json_ser_enum(world, op, vptr, str)) { - goto error; - } + case EcsDiv: + ECS_BINARY_OP(lvalue, rvalue, storage, /); break; - case EcsOpBitmask: - if (json_ser_bitmask(world, op, vptr, str)) { - goto error; - } + case EcsCondEq: + ECS_BINARY_COND_EQ_OP(lvalue, rvalue, storage, ==); break; - case EcsOpArray: - if (json_ser_array(world, op, vptr, str)) { - goto error; - } + case EcsCondNeq: + ECS_BINARY_COND_EQ_OP(lvalue, rvalue, storage, !=); break; - case EcsOpVector: - if (json_ser_vector(world, op, vptr, str)) { - goto error; - } + case EcsCondGt: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, >); break; - case EcsOpOpaque: - if (json_ser_custom_type(world, op, vptr, str)) { - goto error; - } + case EcsCondGtEq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, >=); break; - case EcsOpEntity: { - ecs_entity_t e = *(const ecs_entity_t*)vptr; - if (!e) { - ecs_strbuf_appendch(str, '0'); - } else { - flecs_json_path(str, world, e); - } + case EcsCondLt: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, <); break; - } - case EcsOpId: { - ecs_id_t id = *(const ecs_id_t*)vptr; - if (!id) { - ecs_strbuf_appendch(str, '0'); - } else { - flecs_json_id(str, world, id); - } + case EcsCondLtEq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, <=); + break; + case EcsCondAnd: + ECS_BINARY_BOOL_OP(lvalue, rvalue, storage, &&); + break; + case EcsCondOr: + ECS_BINARY_BOOL_OP(lvalue, rvalue, storage, ||); + break; + case EcsShiftLeft: + ECS_BINARY_UINT_OP(lvalue, rvalue, storage, <<); break; - } - - case EcsOpU64: - case EcsOpI64: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpString: - if (ecs_primitive_to_expr_buf(world, - flecs_json_op_to_primitive_kind(op->kind), - ECS_OFFSET(ptr, op->offset), str)) - { - ecs_throw(ECS_INTERNAL_ERROR, NULL); - } + case EcsShiftRight: + ECS_BINARY_UINT_OP(lvalue, rvalue, storage, >>); break; - - case EcsOpPrimitive: - case EcsOpScope: + case EcsExprOperUnknown: + case EcsLeftParen: + case EcsMin: + ecs_parser_error(name, expr, ptr - expr, "unsupported operator"); + goto error; default: ecs_throw(ECS_INTERNAL_ERROR, NULL); } - if (large_int) { - ecs_strbuf_appendch(str, '"'); + if (storage->ptr != result->ptr) { + if (!result->ptr) { + *result = *storage; + } else { + ecs_meta_cursor_t cur = ecs_meta_cursor(world, + result->type, result->ptr); + ecs_meta_set_value(&cur, storage); + } } return 0; @@ -51467,2126 +55475,3179 @@ int json_ser_type_op( return -1; } -/* Iterate over a slice of the type ops array */ static -int json_ser_type_ops( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - const void *base, - ecs_strbuf_t *str, - int32_t in_array) +const char* flecs_binary_expr_parse( + ecs_world_t *world, + ecs_value_stack_t *stack, + const char *name, + const char *expr, + const char *ptr, + ecs_value_t *lvalue, + ecs_value_t *result, + ecs_expr_oper_t left_op, + const ecs_script_expr_run_desc_t *desc) { - for (int i = 0; i < op_count; i ++) { - ecs_meta_type_op_t *op = &ops[i]; + ecs_entity_t result_type = result->type; + do { + ecs_expr_oper_t op; - if (in_array <= 0) { - if (op->name) { - flecs_json_member(str, op->name); - } + ptr = flecs_str_to_expr_oper(ptr, &op); + if (!ptr) { + ecs_parser_error(name, expr, ptr - expr, "invalid operator"); + return NULL; + } - int32_t elem_count = op->count; - if (elem_count > 1) { - /* Serialize inline array */ - if (json_ser_elements(world, op, op->op_count, base, - elem_count, op->size, str, true)) - { - return -1; - } + ptr = flecs_parse_ws_eol(ptr); - i += op->op_count - 1; + ecs_value_t rvalue = {0}; + const char *rptr = flecs_script_expr_run(world, stack, ptr, &rvalue, op, desc); + if (!rptr) { + return NULL; + } + + if (flecs_binary_expr_do(world, stack, name, expr, ptr, + lvalue, &rvalue, result, op)) + { + return NULL; + } + + ptr = rptr; + + ecs_expr_oper_t right_op; + flecs_str_to_expr_oper(rptr, &right_op); + if (right_op > left_op) { + if (result_type) { + /* If result was initialized, preserve its value */ + lvalue->type = result->type; + lvalue->ptr = flecs_expr_value_new(stack, lvalue->type); + ecs_value_copy(world, lvalue->type, lvalue->ptr, result->ptr); + continue; + } else { + /* Otherwise move result to lvalue */ + *lvalue = *result; + ecs_os_zeromem(result); continue; } } - - switch(op->kind) { - case EcsOpPush: - flecs_json_object_push(str); - in_array --; - break; - case EcsOpPop: - flecs_json_object_pop(str); - in_array ++; + + break; + } while (true); + + return ptr; +} + +static +const char* flecs_funccall_parse( + ecs_world_t *world, + ecs_value_stack_t *stack, + const char *name, + const char *expr, + const char *ptr, + char *token, + ecs_meta_cursor_t *cur, + ecs_value_t *value, + bool isvar, + const ecs_script_expr_run_desc_t *desc) +{ + char *sep = strrchr(token, '.'); + if (!sep) { + ecs_parser_error(name, expr, ptr - expr, + "missing object for function call '%s'", token); + return NULL; + } + + sep[0] = '\0'; + const char *function = sep + 1; + + if (!isvar) { + if (ecs_meta_set_string(cur, token) != 0) { + goto error; + } + } else { + const ecs_script_var_t *var = ecs_script_vars_lookup(desc->vars, token); + ecs_meta_set_value(cur, &var->value); + } + + do { + if (!function[0]) { + ecs_parser_error(name, expr, ptr - expr, + "missing function name for function call '%s'", token); + return NULL; + } + + if (flecs_meta_call(world, stack, name, expr, ptr, cur, + function) != 0) + { + goto error; + } + + ecs_entity_t type = ecs_meta_get_type(cur); + value->ptr = ecs_meta_get_ptr(cur); + value->type = type; + + ptr += 2; + if (ptr[0] != '.') { break; - case EcsOpArray: - case EcsOpVector: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpEntity: - case EcsOpId: - case EcsOpString: - case EcsOpOpaque: - if (json_ser_type_op(world, op, base, str)) { - goto error; - } + } + + ptr ++; + char *paren = strchr(ptr, '('); + if (!paren) { break; - default: - ecs_throw(ECS_INTERNAL_ERROR, NULL); } - } - return 0; + ecs_size_t len = flecs_ito(int32_t, paren - ptr); + if (len >= ECS_MAX_TOKEN_SIZE) { + ecs_parser_error(name, expr, ptr - expr, + "token exceeds maximum token size"); + goto error; + } + + ecs_os_strncpy(token, ptr, len); + token[len] = '\0'; + function = token; + + ptr = paren; + } while (true); + + return ptr; error: - return -1; + return NULL; } -/* Iterate over the type ops of a type */ static -int json_ser_type( +ecs_entity_t flecs_script_default_lookup( const ecs_world_t *world, - const ecs_vec_t *v_ops, - const void *base, - ecs_strbuf_t *str) + const char *name, + void *ctx) { - ecs_meta_type_op_t *ops = ecs_vec_first_t(v_ops, ecs_meta_type_op_t); - int32_t count = ecs_vec_count(v_ops); - return json_ser_type_ops(world, ops, count, base, str, 0); + (void)ctx; + return ecs_lookup(world, name); } static -int array_to_json_buf_w_type_data( - const ecs_world_t *world, - const void *ptr, - int32_t count, - ecs_strbuf_t *buf, - const EcsComponent *comp, - const EcsMetaTypeSerialized *ser) +const char* flecs_script_expr_run( + ecs_world_t *world, + ecs_value_stack_t *stack, + const char *ptr, + ecs_value_t *value, + ecs_expr_oper_t left_op, + const ecs_script_expr_run_desc_t *desc) { - if (count) { - ecs_size_t size = comp->size; + ecs_assert(value != NULL, ECS_INTERNAL_ERROR, NULL); + char token[ECS_MAX_TOKEN_SIZE]; + int depth = 0; + ecs_value_t result = {0}; + ecs_meta_cursor_t cur = {0}; + const char *name = desc ? desc->name : NULL; + const char *expr = desc ? desc->expr : NULL; + token[0] = '\0'; + expr = expr ? expr : ptr; - flecs_json_array_push(buf); + ptr = flecs_parse_ws_eol(ptr); - do { - ecs_strbuf_list_next(buf); - if (json_ser_type(world, &ser->ops, ptr, buf)) { - return -1; - } + /* Check for postfix operators */ + ecs_expr_oper_t unary_op = EcsExprOperUnknown; + if (ptr[0] == '-' && !isdigit(ptr[1])) { + unary_op = EcsMin; + ptr = flecs_parse_ws_eol(ptr + 1); + } - ptr = ECS_OFFSET(ptr, size); - } while (-- count); + /* Initialize storage and cursor. If expression starts with a '(' storage + * will be initialized by a nested expression */ + if (ptr[0] != '(') { + ecs_entity_t type = flecs_parse_discover_type( + world, name, expr, ptr, value->type, desc); + if (!type) { + return NULL; + } - flecs_json_array_pop(buf); - } else { - if (json_ser_type(world, &ser->ops, ptr, buf)) { - return -1; + result.type = type; + if (type != value->type) { + result.ptr = flecs_expr_value_new(stack, type); + } else { + result.ptr = value->ptr; } - } - return 0; -} + cur = ecs_meta_cursor(world, result.type, result.ptr); + if (!cur.valid) { + return NULL; + } -int ecs_array_to_json_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *ptr, - int32_t count, - ecs_strbuf_t *buf) -{ - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - if (!comp) { - char *path = ecs_get_fullpath(world, type); - ecs_err("cannot serialize to JSON, '%s' is not a component", path); - ecs_os_free(path); - return -1; + cur.lookup_action = desc ? desc->lookup_action : NULL; + cur.lookup_ctx = desc ? desc->lookup_ctx : NULL; } - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - if (!ser) { - char *path = ecs_get_fullpath(world, type); - ecs_err("cannot serialize to JSON, '%s' has no reflection data", path); - ecs_os_free(path); - return -1; - } + /* Loop that parses all values in a value scope */ + while ((ptr = flecs_script_expr_parse_token(name, expr, ptr, token))) { + /* Used to track of the result of the parsed token can be used as the + * lvalue for a binary expression */ + bool is_lvalue = false; + bool newline = false; - return array_to_json_buf_w_type_data(world, ptr, count, buf, comp, ser); -} + if (!token[0]) { + /* Comment */ + continue; + } -char* ecs_array_to_json( - const ecs_world_t *world, - ecs_entity_t type, - const void* ptr, - int32_t count) -{ - ecs_strbuf_t str = ECS_STRBUF_INIT; + if (!ecs_os_strcmp(token, "(")) { + ecs_value_t temp_result, *out; + if (!depth) { + out = &result; + } else { + temp_result.type = ecs_meta_get_type(&cur); + temp_result.ptr = ecs_meta_get_ptr(&cur); + out = &temp_result; + } - if (ecs_array_to_json_buf(world, type, ptr, count, &str) != 0) { - ecs_strbuf_reset(&str); - return NULL; - } + /* Parenthesis, parse nested expression */ + ptr = flecs_script_expr_run( + world, stack, ptr, out, EcsLeftParen, desc); + if (ptr[0] != ')') { + ecs_parser_error(name, expr, ptr - expr, + "missing closing parenthesis"); + return NULL; + } + ptr = flecs_parse_ws(ptr + 1); + is_lvalue = true; - return ecs_strbuf_get(&str); -} + } else if (!ecs_os_strcmp(token, "{")) { + /* Parse nested value scope */ + ecs_entity_t scope_type = ecs_meta_get_type(&cur); -int ecs_ptr_to_json_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *ptr, - ecs_strbuf_t *buf) -{ - return ecs_array_to_json_buf(world, type, ptr, 0, buf); -} + depth ++; /* Keep track of depth so we know when parsing is done */ + if (ecs_meta_push(&cur) != 0) { + goto error; + } -char* ecs_ptr_to_json( - const ecs_world_t *world, - ecs_entity_t type, - const void* ptr) -{ - return ecs_array_to_json(world, type, ptr, 0); -} + if (ecs_meta_is_collection(&cur)) { + char *path = ecs_get_path(world, scope_type); + ecs_parser_error(name, expr, ptr - expr, + "expected '[' for collection type '%s'", path); + ecs_os_free(path); + return NULL; + } + } -static -bool flecs_json_skip_id( - const ecs_world_t *world, - ecs_id_t id, - const ecs_entity_to_json_desc_t *desc, - ecs_entity_t ent, - ecs_entity_t inst, - ecs_entity_t *pred_out, - ecs_entity_t *obj_out, - ecs_entity_t *role_out, - bool *hidden_out) -{ - bool is_base = ent != inst; - ecs_entity_t pred = 0, obj = 0, role = 0; - bool hidden = false; + else if (!ecs_os_strcmp(token, "}")) { + depth --; - if (ECS_HAS_ID_FLAG(id, PAIR)) { - pred = ecs_pair_first(world, id); - obj = ecs_pair_second(world, id); - } else { - pred = id & ECS_COMPONENT_MASK; - if (id & ECS_ID_FLAGS_MASK) { - role = id & ECS_ID_FLAGS_MASK; + if (ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, ptr - expr, "expected ']'"); + return NULL; + } + + if (ecs_meta_pop(&cur) != 0) { + goto error; + } } - } - if (is_base) { - if (ecs_has_id(world, pred, EcsDontInherit)) { - return true; + else if (!ecs_os_strcmp(token, "[")) { + /* Open collection value scope */ + depth ++; + if (ecs_meta_push(&cur) != 0) { + goto error; + } + + if (!ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, ptr - expr, "unexpected '['"); + return NULL; + } } - } - if (!desc || !desc->serialize_private) { - if (ecs_has_id(world, pred, EcsPrivate)) { - return true; + + else if (!ecs_os_strcmp(token, "]")) { + depth --; + + if (!ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, ptr - expr, "expected '}'"); + return NULL; + } + + if (ecs_meta_pop(&cur) != 0) { + goto error; + } } - } - if (is_base) { - if (ecs_get_target_for_id(world, inst, EcsIsA, id) != ent) { - hidden = true; + + else if (!ecs_os_strcmp(token, "-")) { + if (unary_op != EcsExprOperUnknown) { + ecs_parser_error(name, expr, ptr - expr, + "unexpected unary operator"); + return NULL; + } + unary_op = EcsMin; } - } - if (hidden && (!desc || !desc->serialize_hidden)) { - return true; - } - *pred_out = pred; - *obj_out = obj; - *role_out = role; - if (hidden_out) *hidden_out = hidden; + else if (!ecs_os_strcmp(token, ",")) { + /* Move to next field */ + if (ecs_meta_next(&cur) != 0) { + goto error; + } + } - return false; -} + else if (!ecs_os_strcmp(token, "null")) { + if (ecs_meta_set_null(&cur) != 0) { + goto error; + } -static -int flecs_json_append_type_labels( - const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_id_t *ids, - int32_t count, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - (void)world; (void)buf; (void)ids; (void)count; (void)ent; (void)inst; - (void)desc; - -#ifdef FLECS_DOC - if (!desc || !desc->serialize_id_labels) { - return 0; - } + is_lvalue = true; + } - flecs_json_memberl(buf, "id_labels"); - flecs_json_array_push(buf); + else if (token[0] == '\"') { + /* Regular string */ + if (ecs_meta_set_string_literal(&cur, token) != 0) { + goto error; + } - int32_t i; - for (i = 0; i < count; i ++) { - ecs_entity_t pred = 0, obj = 0, role = 0; - if (flecs_json_skip_id(world, ids[i], desc, ent, inst, &pred, &obj, &role, 0)) { - continue; + is_lvalue = true; } - if (obj && (pred == EcsUnion)) { - pred = obj; - obj = ecs_get_target(world, ent, pred, 0); - if (!ecs_is_alive(world, obj)) { - /* Union relationships aren't automatically cleaned up, so they - * can contain invalid entity ids. Don't serialize value until - * relationship is valid again. */ - continue; + else if (!ecs_os_strcmp(token, "`")) { + /* Multiline string */ + if (!(ptr = flecs_parse_multiline_string(&cur, name, expr, ptr))) { + goto error; + } + + is_lvalue = true; + + } else if (token[0] == '$') { + /* Variable */ + if (!desc || !desc->vars) { + ecs_parser_error(name, expr, ptr - expr, + "unresolved variable '%s' (no variable scope)", token); + return NULL; + } + + if (!token[1]) { + /* Empty name means default to assigned member */ + const char *member = ecs_meta_get_member(&cur); + if (!member) { + ecs_parser_error(name, expr, ptr - expr, + "invalid default variable outside member assignment"); + return NULL; + } + ecs_os_strcpy(&token[1], member); + } + + const ecs_script_var_t *var = ecs_script_vars_lookup(desc->vars, &token[1]); + if (!var) { + if (ptr[0] == '(') { + /* Function */ + ptr = flecs_funccall_parse(world, stack, name, expr, ptr, + &token[1], &cur, &result, true, desc); + if (!ptr) { + goto error; + } + } else { + ecs_value_t v = flecs_dotresolve_var(world, desc->vars, &token[1]); + if (!v.ptr) { + ecs_parser_error(name, expr, ptr - expr, + "unresolved variable '%s'", token); + return NULL; + } else { + ecs_meta_set_value(&cur, &v); + } + } + } else { + ecs_meta_set_value(&cur, &var->value); + } + + is_lvalue = true; + } else { + const char *tptr = flecs_parse_ws(ptr); + for (; ptr != tptr; ptr ++) { + if (ptr[0] == '\n') { + newline = true; + } } - } - if (desc && desc->serialize_id_labels) { - flecs_json_next(buf); + if (ptr[0] == ':') { + /* Member assignment */ + ptr ++; + if (ecs_meta_dotmember(&cur, token) != 0) { + goto error; + } + } else if (ptr[0] == '(') { + /* Function */ + ptr = flecs_funccall_parse(world, stack, name, expr, ptr, + token, &cur, &result, false, desc); + if (!ptr) { + goto error; + } + } else { + if (ptr[0] != '[') { + /* Entity id expression */ + if (ecs_meta_set_string(&cur, token) != 0) { + goto error; + } + } else { + /* Component expression */ + ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(desc->lookup_action != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t e = desc->lookup_action( + world, token, desc->lookup_ctx); + if (!e) { + ecs_parser_error(name, expr, ptr - expr, + "entity '%s' not found", token); + goto error; + } - flecs_json_array_push(buf); - flecs_json_next(buf); - flecs_json_label(buf, world, pred); - if (obj) { - flecs_json_next(buf); - flecs_json_label(buf, world, obj); - } + ptr = flecs_script_expr_parse_token( + name, expr, ptr + 1, token); + if (!ptr) { + goto error; + } - flecs_json_array_pop(buf); - } - } + ecs_entity_t component = desc->lookup_action( + world, token, desc->lookup_ctx); + if (!component) { + ecs_parser_error(name, expr, ptr - expr, + "unresolved component '%s'", token); + goto error; + } - flecs_json_array_pop(buf); -#endif - return 0; -} + ecs_entity_t type = ecs_get_typeid(world, component); + if (!type) { + ecs_parser_error(name, expr, ptr - expr, + "entity '%s' is not a component", token); + goto error; + } -static -int flecs_json_append_type_values( - const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_id_t *ids, - int32_t count, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - if (!desc || !desc->serialize_values) { - return 0; - } + result.type = type; + result.ptr = ECS_CONST_CAST(void*, + ecs_get_id(world, e, component)); + if (!result.ptr) { + char *entitystr = ecs_id_str(world, e); + char *idstr = ecs_id_str(world, component); + ecs_parser_error(name, expr, ptr - expr, + "entity '%s' does not have component '%s'", + entitystr, idstr); + ecs_os_free(idstr); + ecs_os_free(entitystr); + goto error; + } - flecs_json_memberl(buf, "values"); - flecs_json_array_push(buf); + if (ptr[0] != ']') { + ecs_parser_error(name, expr, ptr - expr, + "missing ] for component operator"); + goto error; + } - int32_t i; - for (i = 0; i < count; i ++) { - bool hidden; - ecs_entity_t pred = 0, obj = 0, role = 0; - ecs_id_t id = ids[i]; - if (flecs_json_skip_id(world, id, desc, ent, inst, &pred, &obj, &role, - &hidden)) - { - continue; - } + ptr ++; - if (!hidden) { - bool serialized = false; - ecs_entity_t typeid = ecs_get_typeid(world, id); - if (typeid) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, typeid, EcsMetaTypeSerialized); - if (ser) { - const void *ptr = ecs_get_id(world, ent, id); - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + if (ptr[0] == '.') { + ecs_meta_cursor_t member_cur = ecs_meta_cursor( + world, result.type, result.ptr); - flecs_json_next(buf); - if (json_ser_type(world, &ser->ops, ptr, buf) != 0) { - /* Entity contains invalid value */ - return -1; + ptr = flecs_script_expr_parse_token( + name, expr, ptr + 1, token); + if (!ptr) { + goto error; + } + + ecs_meta_push(&member_cur); + if (ecs_meta_dotmember(&member_cur, token) != 0) { + ecs_parser_error(name, expr, ptr - expr, + "failed to assign member '%s'", token); + goto error; + } + + result.type = ecs_meta_get_type(&member_cur); + result.ptr = ecs_meta_get_ptr(&member_cur); } - serialized = true; } } - if (!serialized) { - flecs_json_next(buf); - flecs_json_number(buf, 0); - } - } else { - if (!desc || desc->serialize_hidden) { - flecs_json_next(buf); - flecs_json_number(buf, 0); - } + + is_lvalue = true; } - } - flecs_json_array_pop(buf); - - return 0; -} + /* If lvalue was parsed, apply operators. Expressions cannot start + * directly after a newline character. */ + if (is_lvalue && !newline) { + if (unary_op != EcsExprOperUnknown) { + if (unary_op == EcsMin) { + int64_t v = -1; + ecs_value_t lvalue = {.type = ecs_id(ecs_i64_t), .ptr = &v}; + ecs_value_t *out, rvalue, temp_out = {0}; -static -int flecs_json_append_type_info( - const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_id_t *ids, - int32_t count, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - if (!desc || !desc->serialize_type_info) { - return 0; - } + if (!depth) { + rvalue = result; + ecs_os_zeromem(&result); + out = &result; + } else { + ecs_entity_t cur_type = ecs_meta_get_type(&cur); + void *cur_ptr = ecs_meta_get_ptr(&cur); + rvalue.type = cur_type; + rvalue.ptr = cur_ptr; + temp_out.type = cur_type; + temp_out.ptr = cur_ptr; + out = &temp_out; + } - flecs_json_memberl(buf, "type_info"); - flecs_json_array_push(buf); + flecs_binary_expr_do(world, stack, name, expr, ptr, &lvalue, + &rvalue, out, EcsMul); + } + unary_op = 0; + } - int32_t i; - for (i = 0; i < count; i ++) { - bool hidden; - ecs_entity_t pred = 0, obj = 0, role = 0; - ecs_id_t id = ids[i]; - if (flecs_json_skip_id(world, id, desc, ent, inst, &pred, &obj, &role, - &hidden)) - { - continue; - } + ecs_expr_oper_t right_op; + flecs_str_to_expr_oper(ptr, &right_op); + if (right_op) { + /* This is a binary expression, test precedence to determine if + * it should be evaluated here */ + if (flecs_oper_precedence(left_op, right_op) < 0) { + ecs_value_t lvalue; + ecs_value_t *op_result = &result; + ecs_value_t temp_storage; + if (!depth) { + /* Root level value, move result to lvalue storage */ + lvalue = result; + ecs_os_zeromem(&result); + } else { + /* Not a root level value. Move the parsed lvalue to a + * temporary storage, and initialize the result value + * for the binary operation with the current cursor */ + ecs_entity_t cur_type = ecs_meta_get_type(&cur); + void *cur_ptr = ecs_meta_get_ptr(&cur); + lvalue.type = cur_type; + lvalue.ptr = flecs_expr_value_new(stack, cur_type); + ecs_value_copy(world, cur_type, lvalue.ptr, cur_ptr); + temp_storage.type = cur_type; + temp_storage.ptr = cur_ptr; + op_result = &temp_storage; + } - if (!hidden) { - ecs_entity_t typeid = ecs_get_typeid(world, id); - if (typeid) { - flecs_json_next(buf); - if (ecs_type_info_to_json_buf(world, typeid, buf) != 0) { - return -1; + /* Do the binary expression */ + ptr = flecs_binary_expr_parse(world, stack, name, expr, ptr, + &lvalue, op_result, left_op, desc); + if (!ptr) { + return NULL; + } } - } else { - flecs_json_next(buf); - flecs_json_number(buf, 0); - } - } else { - if (!desc || desc->serialize_hidden) { - flecs_json_next(buf); - flecs_json_number(buf, 0); } } - } - flecs_json_array_pop(buf); - - return 0; -} + if (!depth) { + /* Reached the end of the root scope */ + break; + } -static -int flecs_json_append_type_hidden( - const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_id_t *ids, - int32_t count, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - if (!desc || !desc->serialize_hidden) { - return 0; + ptr = flecs_parse_ws_eol(ptr); } - if (ent == inst) { - return 0; /* if this is not a base, components are never hidden */ + if (!value->ptr) { + value->type = result.type; + value->ptr = flecs_expr_value_new(stack, result.type); } - flecs_json_memberl(buf, "hidden"); - flecs_json_array_push(buf); - - int32_t i; - for (i = 0; i < count; i ++) { - bool hidden; - ecs_entity_t pred = 0, obj = 0, role = 0; - ecs_id_t id = ids[i]; - if (flecs_json_skip_id(world, id, desc, ent, inst, &pred, &obj, &role, - &hidden)) - { - continue; - } - - flecs_json_next(buf); - flecs_json_bool(buf, hidden); + if (value->ptr != result.ptr) { + cur = ecs_meta_cursor(world, value->type, value->ptr); + ecs_meta_set_value(&cur, &result); } - flecs_json_array_pop(buf); - - return 0; + ecs_assert(value->type != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(value->ptr != NULL, ECS_INTERNAL_ERROR, NULL); + + return ptr; +error: + return NULL; } -static -int flecs_json_append_type( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) +const char* ecs_script_expr_run( + ecs_world_t *world, + const char *ptr, + ecs_value_t *value, + const ecs_script_expr_run_desc_t *desc) { - const ecs_id_t *ids = NULL; - int32_t i, count = 0; - - const ecs_type_t *type = ecs_get_type(world, ent); - if (type) { - ids = type->array; - count = type->count; + ecs_script_expr_run_desc_t priv_desc = {0}; + if (desc) { + priv_desc = *desc; } - if (!desc || desc->serialize_ids) { - flecs_json_memberl(buf, "ids"); - flecs_json_array_push(buf); - - for (i = 0; i < count; i ++) { - ecs_entity_t pred = 0, obj = 0, role = 0; - if (flecs_json_skip_id(world, ids[i], desc, ent, inst, &pred, &obj, &role, 0)) { - continue; - } + if (!priv_desc.lookup_action) { + priv_desc.lookup_action = flecs_script_default_lookup; + } - if (obj && (pred == EcsUnion)) { - pred = obj; - obj = ecs_get_target(world, ent, pred, 0); - if (!ecs_is_alive(world, obj)) { - /* Union relationships aren't automatically cleaned up, so they - * can contain invalid entity ids. Don't serialize value until - * relationship is valid again. */ - continue; - } - } + /* Prepare storage for temporary values */ + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_value_stack_t stack; + stack.count = 0; + stack.stage = stage; + stack.stack = &stage->allocators.deser_stack; + stack.cursor = flecs_stack_get_cursor(stack.stack); - flecs_json_next(buf); - flecs_json_array_push(buf); - flecs_json_next(buf); - flecs_json_path(buf, world, pred); - if (obj || role) { - flecs_json_next(buf); - if (obj) { - flecs_json_path(buf, world, obj); - } else { - flecs_json_number(buf, 0); - } - if (role) { - flecs_json_next(buf); - flecs_json_string(buf, ecs_id_flag_str(role)); - } - } - flecs_json_array_pop(buf); - } - flecs_json_array_pop(buf); - } + /* Parse expression */ + bool storage_provided = value->ptr != NULL; + ptr = flecs_script_expr_run( + world, &stack, ptr, value, EcsExprOperUnknown, &priv_desc); - if (flecs_json_append_type_labels(world, buf, ids, count, ent, inst, desc)) { - return -1; + /* If no result value was provided, allocate one as we can't return a + * pointer to a temporary storage */ + if (!storage_provided && value->ptr) { + ecs_assert(value->type != 0, ECS_INTERNAL_ERROR, NULL); + void *temp_storage = value->ptr; + value->ptr = ecs_value_new(world, value->type); + ecs_value_move_ctor(world, value->type, value->ptr, temp_storage); } - if (flecs_json_append_type_values(world, buf, ids, count, ent, inst, desc)) { - return -1; - } - - if (flecs_json_append_type_info(world, buf, ids, count, ent, inst, desc)) { - return -1; - } - - if (flecs_json_append_type_hidden(world, buf, ids, count, ent, inst, desc)) { - return -1; + /* Cleanup temporary values */ + int i; + for (i = 0; i < stack.count; i ++) { + const ecs_type_info_t *ti = stack.values[i].ti; + ecs_assert(ti->hooks.dtor != NULL, ECS_INTERNAL_ERROR, NULL); + ti->hooks.dtor(stack.values[i].ptr, 1, ti); } + flecs_stack_restore_cursor(stack.stack, stack.cursor); - return 0; + return ptr; } -static -int flecs_json_append_base( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - const ecs_type_t *type = ecs_get_type(world, ent); - ecs_id_t *ids = NULL; - int32_t i, count = 0; - if (type) { - ids = type->array; - count = type->count; - } - - for (i = 0; i < count; i ++) { - ecs_id_t id = ids[i]; - if (ECS_HAS_RELATION(id, EcsIsA)) { - if (flecs_json_append_base(world, buf, ecs_pair_second(world, id), inst, desc)) - { - return -1; - } - } - } - - ecs_strbuf_list_next(buf); - flecs_json_object_push(buf); - flecs_json_memberl(buf, "path"); - flecs_json_path(buf, world, ent); +#endif - if (flecs_json_append_type(world, buf, ent, inst, desc)) { - return -1; - } +/** + * @file addons/script/interpolate.c + * @brief String interpolation. + */ - flecs_json_object_pop(buf); - return 0; -} +#ifdef FLECS_SCRIPT +#include -#ifdef FLECS_ALERTS static -int flecs_json_serialize_entity_alerts( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity, - const EcsAlertsActive *alerts, - bool self) +const char* flecs_parse_var_name( + const char *ptr, + char *token_out) { - ecs_assert(alerts != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_map_iter_t it = ecs_map_iter(&alerts->alerts); - while (ecs_map_next(&it)) { - flecs_json_next(buf); - flecs_json_object_push(buf); - ecs_entity_t ai = ecs_map_value(&it); - char *alert_name = ecs_get_fullpath(world, ai); - flecs_json_memberl(buf, "alert"); - flecs_json_string(buf, alert_name); - ecs_os_free(alert_name); + char ch, *bptr = token_out; - ecs_entity_t severity_id = ecs_get_target( - world, ai, ecs_id(EcsAlert), 0); - const char *severity = ecs_get_name(world, severity_id); + while ((ch = *ptr)) { + if (bptr - token_out > ECS_MAX_TOKEN_SIZE) { + goto error; + } - const EcsAlertInstance *alert = ecs_get( - world, ai, EcsAlertInstance); - if (alert) { - if (alert->message) { - flecs_json_memberl(buf, "message"); - flecs_json_string(buf, alert->message); - } - flecs_json_memberl(buf, "severity"); - flecs_json_string(buf, severity); - - if (!self) { - char *path = ecs_get_fullpath(world, entity); - flecs_json_memberl(buf, "path"); - flecs_json_string(buf, path); - ecs_os_free(path); - } + if (isalpha(ch) || isdigit(ch) || ch == '_') { + *bptr = ch; + bptr ++; + ptr ++; + } else { + break; } - flecs_json_object_pop(buf); } - return 0; + if (bptr == token_out) { + goto error; + } + + *bptr = '\0'; + + return ptr; +error: + return NULL; } static -int flecs_json_serialize_children_alerts( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity) +const char* flecs_parse_interpolated_str( + const char *ptr, + char *token_out) { - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter(ECS_CONST_CAST(ecs_world_t*, world), { - .storage = &f, - .terms = {{ .id = ecs_pair(EcsChildOf, entity) }} - }); + char ch, *bptr = token_out; - ecs_iter_t it = ecs_filter_iter(world, &f); - while (ecs_filter_next(&it)) { - EcsAlertsActive *alerts = ecs_table_get_id( - world, it.table, ecs_id(EcsAlertsActive), it.offset); + while ((ch = *ptr)) { + if (bptr - token_out > ECS_MAX_TOKEN_SIZE) { + goto error; + } - int32_t i; - for (i = 0; i < it.count; i ++) { - ecs_entity_t child = it.entities[i]; - if (alerts) { - if (flecs_json_serialize_entity_alerts( - world, buf, child, &alerts[i], false)) - { - goto error; - } + if (ch == '\\') { + if (ptr[1] == '}') { + *bptr = '}'; + bptr ++; + ptr += 2; + continue; } + } - ecs_record_t *r = flecs_entities_get(world, it.entities[i]); - if (r->row & EcsEntityIsTraversable) { - if (flecs_json_serialize_children_alerts( - world, buf, child)) - { - goto error; - } - } + if (ch != '}') { + *bptr = ch; + bptr ++; + ptr ++; + } else { + ptr ++; + break; } } - ecs_filter_fini(&f); + if (bptr == token_out) { + goto error; + } + + *bptr = '\0'; - return 0; + return ptr; error: - return -1; + return NULL; } -#endif -static -int flecs_json_serialize_alerts( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity) +char* ecs_script_string_interpolate( + ecs_world_t *world, + const char *str, + const ecs_script_vars_t *vars) { - (void)world; - (void)buf; - (void)entity; - -#ifdef FLECS_ALERTS - if (!ecs_id(EcsAlertsActive)) { - return 0; /* Alert module not imported */ - } + char token[ECS_MAX_TOKEN_SIZE]; + ecs_strbuf_t result = ECS_STRBUF_INIT; + const char *ptr; + char ch; - flecs_json_memberl(buf, "alerts"); - flecs_json_array_push(buf); - const EcsAlertsActive *alerts = ecs_get(world, entity, EcsAlertsActive); - if (alerts) { - flecs_json_serialize_entity_alerts(world, buf, entity, alerts, true); - } - flecs_json_serialize_children_alerts(world, buf, entity); - flecs_json_array_pop(buf); -#endif - return 0; -} + for(ptr = str; (ch = *ptr); ptr++) { + if (ch == '\\') { + ptr ++; + if (ptr[0] == '$') { + ecs_strbuf_appendch(&result, '$'); + continue; + } + if (ptr[0] == '\\') { + ecs_strbuf_appendch(&result, '\\'); + continue; + } + if (ptr[0] == '{') { + ecs_strbuf_appendch(&result, '{'); + continue; + } + if (ptr[0] == '}') { + ecs_strbuf_appendch(&result, '}'); + continue; + } + ptr --; + } -static -int flecs_json_serialize_refs_idr( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_id_record_t *idr) -{ - char *id_str = ecs_id_str(world, ecs_pair_first(world, idr->id)); + if (ch == '$') { + ptr = flecs_parse_var_name(ptr + 1, token); + if (!ptr) { + ecs_parser_error(NULL, str, ptr - str, + "invalid variable name '%s'", ptr); + goto error; + } - flecs_json_member(buf, id_str); - ecs_os_free(id_str); + ecs_script_var_t *var = ecs_script_vars_lookup(vars, token); + if (!var) { + ecs_parser_error(NULL, str, ptr - str, + "unresolved variable '%s'", token); + goto error; + } - flecs_json_array_push(buf); + if (ecs_ptr_to_str_buf( + world, var->value.type, var->value.ptr, &result)) + { + goto error; + } - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_all_iter((ecs_table_cache_t*)idr, &it)) { - const ecs_table_record_t *tr; - 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); - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); - for (i = 0; i < count; i ++) { - ecs_entity_t e = entities[i]; - flecs_json_next(buf); - flecs_json_path(buf, world, e); + ptr --; + } else if (ch == '{') { + ptr = flecs_parse_interpolated_str(ptr + 1, token); + if (!ptr) { + ecs_parser_error(NULL, str, ptr - str, + "invalid interpolated expression"); + goto error; } - } - } - - flecs_json_array_pop(buf); - return 0; -} + ecs_script_expr_run_desc_t expr_desc = { + .vars = ECS_CONST_CAST(ecs_script_vars_t*, vars) + }; -static -int flecs_json_serialize_refs( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity, - ecs_entity_t relationship) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_pair(relationship, entity)); + ecs_value_t expr_result = {0}; + if (!ecs_script_expr_run(world, token, &expr_result, &expr_desc)) { + goto error; + } - if (idr) { - if (relationship == EcsWildcard) { - ecs_id_record_t *cur = idr; - while ((cur = cur->second.next)) { - flecs_json_serialize_refs_idr(world, buf, cur); + if (ecs_ptr_to_str_buf( + world, expr_result.type, expr_result.ptr, &result)) + { + goto error; } + + ecs_value_free(world, expr_result.type, expr_result.ptr); + + ptr --; } else { - flecs_json_serialize_refs_idr(world, buf, idr); + ecs_strbuf_appendch(&result, ch); } } - - return 0; -} -static -int flecs_json_serialize_matches( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_pair_t(EcsPoly, EcsQuery)); + return ecs_strbuf_get(&result); +error: + return NULL; +} - if (idr) { - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) { - const ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; - EcsPoly *queries = ecs_table_get_column(table, tr->column, 0); +#endif - int32_t i, count = ecs_table_count(table); - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); - for (i = 0; i < count; i ++) { - ecs_poly_t *q = queries[i].poly; - ecs_iter_t qit; - ecs_iter_poly(world, q, &qit, NULL); - if (!qit.variables) { - ecs_iter_fini(&qit); - continue; - } - ecs_iter_set_var(&qit, 0, entity); - if (ecs_iter_is_true(&qit)) { - flecs_json_next(buf); - flecs_json_path(buf, world, entities[i]); - } - } - } - } - } - - return 0; -} +/** + * @file addons/script/parser.c + * @brief Script grammar parser. + */ -int ecs_entity_to_json_buf( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_strbuf_t *buf, - const ecs_entity_to_json_desc_t *desc) -{ - if (!entity || !ecs_is_valid(world, entity)) { - return -1; - } - flecs_json_object_push(buf); +#ifdef FLECS_SCRIPT +/** + * @file addons/script/parser.h + * @brief Script grammar parser. + * + * Macro utilities that facilitate a simple recursive descent parser. + */ - if (!desc || desc->serialize_path) { - flecs_json_memberl(buf, "path"); - flecs_json_path(buf, world, entity); - } +#ifndef FLECS_SCRIPT_PARSER_H +#define FLECS_SCRIPT_PARSER_H -#ifdef FLECS_DOC - if (desc && desc->serialize_label) { - flecs_json_memberl(buf, "label"); - const char *doc_name = ecs_doc_get_name(world, entity); - if (doc_name) { - flecs_json_string_escape(buf, doc_name); - } else { - char num_buf[20]; - ecs_os_sprintf(num_buf, "%u", (uint32_t)entity); - flecs_json_string(buf, num_buf); - } - } +#if defined(ECS_TARGET_CLANG) +/* Ignore unused enum constants in switch as it would blow up the parser code */ +#pragma clang diagnostic ignored "-Wswitch-enum" +/* To allow for nested Parse statements */ +#pragma clang diagnostic ignored "-Wshadow" +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#pragma GCC diagnostic ignored "-Wswitch-enum" +#pragma GCC diagnostic ignored "-Wshadow" +#elif defined(ECS_TARGET_MSVC) +/* Allow for variable shadowing */ +#pragma warning(disable : 4456) +#endif - if (desc && desc->serialize_brief) { - const char *doc_brief = ecs_doc_get_brief(world, entity); - if (doc_brief) { - flecs_json_memberl(buf, "brief"); - flecs_json_string_escape(buf, doc_brief); - } - } +/* Create script & parser structs with static token buffer */ +#define EcsParserFixedBuffer(w, script_name, expr, tokens, tokens_len)\ + ecs_script_impl_t script = {\ + .pub.world = ECS_CONST_CAST(ecs_world_t*, w),\ + .pub.name = script_name,\ + .pub.code = expr\ + };\ + ecs_script_parser_t parser = {\ + .script = flecs_script_impl(&script),\ + .pos = expr,\ + .token_cur = tokens\ + } + +/* Definitions for parser functions */ +#define ParserBegin\ + ecs_script_tokens_t token_stack = {0};\ + ecs_script_token_t *tokens = token_stack.tokens;\ + (void)tokens + +#define ParserEnd\ + Error("unexpected end of rule (parser error)");\ + error:\ + return NULL + +/* Get token */ +#define Token(n) (tokens[n].value) + +/* Error */ +#define Error(...)\ + ecs_parser_error(parser->script->pub.name, parser->script->pub.code,\ + (pos - parser->script->pub.code) - 1, __VA_ARGS__);\ + goto error + +/* Parse expression */ +#define Expr(until, ...)\ + {\ + ecs_assert(token_stack.count < 256, ECS_INTERNAL_ERROR, NULL);\ + ecs_script_token_t *t = &token_stack.tokens[token_stack.count ++];\ + if (!(pos = flecs_script_expr(parser, pos, t, until))) {\ + goto error;\ + }\ + if (!t->value[0] && (until == '\n' || until == '{')) {\ + pos ++;\ + Error("empty expression");\ + }\ + }\ + Parse_1(until, __VA_ARGS__) + +/* Parse token until character */ +#define Until(until, ...)\ + {\ + ecs_assert(token_stack.count < 256, ECS_INTERNAL_ERROR, NULL);\ + ecs_script_token_t *t = &token_stack.tokens[token_stack.count ++];\ + if (!(pos = flecs_script_until(parser, pos, t, until))) {\ + goto error;\ + }\ + }\ + Parse_1(until, __VA_ARGS__) + +/* Parse next token */ +#define Parse(...)\ + {\ + ecs_assert(token_stack.count < 256, ECS_INTERNAL_ERROR, NULL);\ + ecs_script_token_t *t = &token_stack.tokens[token_stack.count ++];\ + if (!(pos = flecs_script_token(parser, pos, t, false))) {\ + goto error;\ + }\ + switch(t->kind) {\ + __VA_ARGS__\ + default:\ + if (t->value) {\ + Error("unexpected %s'%s'", \ + flecs_script_token_kind_str(t->kind), t->value);\ + } else {\ + Error("unexpected %s", \ + flecs_script_token_kind_str(t->kind));\ + }\ + }\ + } + +/* Parse N consecutive tokens */ +#define Parse_1(tok, ...)\ + Parse(\ + case tok: {\ + __VA_ARGS__\ + }\ + ) + +#define Parse_2(tok1, tok2, ...)\ + Parse_1(tok1, \ + Parse(\ + case tok2: {\ + __VA_ARGS__\ + }\ + )\ + ) + +#define Parse_3(tok1, tok2, tok3, ...)\ + Parse_2(tok1, tok2, \ + Parse(\ + case tok3: {\ + __VA_ARGS__\ + }\ + )\ + ) + +#define Parse_4(tok1, tok2, tok3, tok4, ...)\ + Parse_3(tok1, tok2, tok3, \ + Parse(\ + case tok4: {\ + __VA_ARGS__\ + }\ + )\ + ) + +#define Parse_5(tok1, tok2, tok3, tok4, tok5, ...)\ + Parse_4(tok1, tok2, tok3, tok4, \ + Parse(\ + case tok5: {\ + __VA_ARGS__\ + }\ + )\ + ) + +#define LookAhead_Keep() \ + pos = lookahead;\ + parser->token_keep = parser->token_cur + +/* Same as Parse, but doesn't error out if token is not in handled cases */ +#define LookAhead(...)\ + const char *lookahead;\ + ecs_script_token_t lookahead_token;\ + const char *old_lh_token_cur = parser->token_cur;\ + if ((lookahead = flecs_script_token(parser, pos, &lookahead_token, true))) {\ + token_stack.tokens[token_stack.count ++] = lookahead_token;\ + switch(lookahead_token.kind) {\ + __VA_ARGS__\ + default:\ + token_stack.count --;\ + break;\ + }\ + if (old_lh_token_cur > parser->token_keep) {\ + parser->token_cur = ECS_CONST_CAST(char*, old_lh_token_cur);\ + } else {\ + parser->token_cur = parser->token_keep;\ + }\ + } + +/* Lookahead N consecutive tokens */ +#define LookAhead_1(tok, ...)\ + LookAhead(\ + case tok: {\ + __VA_ARGS__\ + }\ + ) + +#define LookAhead_2(tok1, tok2, ...)\ + LookAhead_1(tok1, \ + const char *old_ptr = pos;\ + pos = lookahead;\ + LookAhead(\ + case tok2: {\ + __VA_ARGS__\ + }\ + )\ + pos = old_ptr;\ + ) + +#define LookAhead_3(tok1, tok2, tok3, ...)\ + LookAhead_2(tok1, tok2, \ + const char *old_ptr = pos;\ + pos = lookahead;\ + LookAhead(\ + case tok3: {\ + __VA_ARGS__\ + }\ + )\ + pos = old_ptr;\ + ) + +/* Open scope */ +#define Scope(s, ...) {\ + ecs_script_scope_t *old_scope = parser->scope;\ + parser->scope = s;\ + __VA_ARGS__\ + parser->scope = old_scope;\ + } + +/* Parser loop */ +#define Loop(...)\ + int32_t token_stack_count = token_stack.count;\ + do {\ + token_stack.count = token_stack_count;\ + __VA_ARGS__\ + } while (true); - if (desc && desc->serialize_link) { - const char *doc_link = ecs_doc_get_link(world, entity); - if (doc_link) { - flecs_json_memberl(buf, "link"); - flecs_json_string_escape(buf, doc_link); - } - } +#define EndOfRule return pos - if (desc && desc->serialize_color) { - const char *doc_color = ecs_doc_get_color(world, entity); - if (doc_color) { - flecs_json_memberl(buf, "color"); - flecs_json_string_escape(buf, doc_color); - } - } #endif - const ecs_type_t *type = ecs_get_type(world, entity); - ecs_id_t *ids = NULL; - int32_t i, count = 0; - if (type) { - ids = type->array; - count = type->count; - } - if (!desc || desc->serialize_base) { - if (ecs_has_pair(world, entity, EcsIsA, EcsWildcard)) { - flecs_json_memberl(buf, "is_a"); - flecs_json_array_push(buf); +#define EcsTokEndOfStatement\ + case ';':\ + case '\n':\ + case '\0' - for (i = 0; i < count; i ++) { - ecs_id_t id = ids[i]; - if (ECS_HAS_RELATION(id, EcsIsA)) { - if (flecs_json_append_base( - world, buf, ecs_pair_second(world, id), entity, desc)) - { - return -1; - } - } - } +static +const char* flecs_script_stmt( + ecs_script_parser_t *parser, + const char *pos); - flecs_json_array_pop(buf); - } - } +/* Parse scope (statements inside {}) */ +static +const char* flecs_script_scope( + ecs_script_parser_t *parser, + ecs_script_scope_t *scope, + const char *pos) +{ + ParserBegin; - if (flecs_json_append_type(world, buf, entity, entity, desc)) { - goto error; - } + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(pos[-1] == '{', ECS_INTERNAL_ERROR, NULL); - if (desc && desc->serialize_alerts) { - if (flecs_json_serialize_alerts(world, buf, entity)) { - goto error; - } - } + ecs_script_scope_t *prev = parser->scope; + parser->scope = scope; - if (desc && desc->serialize_refs) { - flecs_json_memberl(buf, "refs"); - flecs_json_object_push(buf); - if (flecs_json_serialize_refs(world, buf, entity, desc->serialize_refs)) { - goto error; - } - flecs_json_object_pop(buf); - } + Loop( + LookAhead( + case EcsTokScopeClose: + pos = lookahead; + goto scope_close; + case EcsTokEnd: + Error("unexpected end of script"); + goto error; + ) - if (desc && desc->serialize_matches) { - flecs_json_memberl(buf, "matches"); - flecs_json_array_push(buf); - if (flecs_json_serialize_matches(world, buf, entity)) { + pos = flecs_script_stmt(parser, pos); + if (!pos) { goto error; } - flecs_json_array_pop(buf); - } + ) - flecs_json_object_pop(buf); +scope_close: + parser->scope = prev; - return 0; -error: - return -1; + ecs_assert(pos[-1] == '}', ECS_INTERNAL_ERROR, NULL); + return pos; + + ParserEnd; } -char* ecs_entity_to_json( - const ecs_world_t *world, - ecs_entity_t entity, - const ecs_entity_to_json_desc_t *desc) +/* Parse comma expression (expressions separated by ',') */ +static +const char* flecs_script_comma_expr( + ecs_script_parser_t *parser, + const char *pos, + bool is_base_list) { - ecs_strbuf_t buf = ECS_STRBUF_INIT; + ParserBegin; - if (ecs_entity_to_json_buf(world, entity, &buf, desc) != 0) { - ecs_strbuf_reset(&buf); - return NULL; - } + Loop( + LookAhead( + case '\n': + pos = lookahead; + continue; - return ecs_strbuf_get(&buf); -} + case EcsTokIdentifier: + LookAhead_Keep(); -static -bool flecs_json_skip_variable( - const char *name) -{ - if (!name || name[0] == '_' || !ecs_os_strcmp(name, "this")) { - return true; - } else { - return false; - } -} + if (is_base_list) { + flecs_script_insert_pair_tag(parser, "IsA", Token(0)); + } else { + flecs_script_insert_entity(parser, Token(0)); + } -static -void flecs_json_serialize_id( - const ecs_world_t *world, - ecs_id_t id, - ecs_strbuf_t *buf) -{ - flecs_json_id(buf, world, id); -} + LookAhead_1(',', + pos = lookahead; + continue; + ) + ) -static -void flecs_json_serialize_id_label( - const ecs_world_t *world, - ecs_id_t id, - ecs_strbuf_t *buf) -{ - ecs_entity_t pred = id, obj = 0; - if (ECS_IS_PAIR(id)) { - pred = ecs_pair_first(world, id); - obj = ecs_pair_second(world, id); - } + break; + ) - flecs_json_array_push(buf); - flecs_json_next(buf); - flecs_json_label(buf, world, pred); - if (obj) { - flecs_json_next(buf); - flecs_json_label(buf, world, obj); - } - flecs_json_array_pop(buf); + return pos; } +/* Parse with expression (expression after 'with' keyword) */ static -void flecs_json_serialize_iter_ids( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) +const char* flecs_script_with_expr( + ecs_script_parser_t *parser, + const char *pos) { - int32_t field_count = it->field_count; - if (!field_count) { - return; - } + ParserBegin; - flecs_json_memberl(buf, "ids"); - flecs_json_array_push(buf); + Parse( + // Position + case EcsTokIdentifier: { + // Position ( + LookAhead_1('(', + pos = lookahead; - for (int i = 0; i < field_count; i ++) { - flecs_json_next(buf); - flecs_json_serialize_id(world, it->terms[i].id, buf); - } + // Position ( expr ) + Expr(')', + ecs_script_component_t *component = + flecs_script_insert_component(parser, Token(0)); + component->node.kind = EcsAstWithComponent; + component->expr = Token(2); + EndOfRule; + ) + ) - flecs_json_array_pop(buf); + if (Token(0)[0] == '$') { + ecs_script_var_component_t *var = + flecs_script_insert_var_component(parser, &Token(0)[1]); + var->node.kind = EcsAstWithVar; + } else { + ecs_script_tag_t *tag = + flecs_script_insert_tag(parser, Token(0)); + tag->node.kind = EcsAstWithTag; + } + + EndOfRule; + } + + // ( + case '(': + // (Eats, Apples) + Parse_4(EcsTokIdentifier, ',', EcsTokIdentifier, ')', + // (Eats, Apples) ( expr + LookAhead_1('(', + pos = lookahead; + + // (Eats, Apples) ( expr ) + Expr(')', + ecs_script_component_t *component = + flecs_script_insert_pair_component(parser, + Token(1), Token(3)); + component->node.kind = EcsAstWithComponent; + component->expr = Token(6); + EndOfRule; + ) + ) + + ecs_script_tag_t *tag = + flecs_script_insert_pair_tag(parser, Token(1), Token(3)); + tag->node.kind = EcsAstWithTag; + EndOfRule; + ) + ) + + ParserEnd; } +/* Parse with expression list (expression list after 'with' keyword) */ static -void flecs_json_serialize_iter_id_labels( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) +const char* flecs_script_with( + ecs_script_parser_t *parser, + ecs_script_with_t *with, + const char *pos) { - int32_t field_count = it->field_count; - if (!field_count) { - return; - } + ParserBegin; - flecs_json_memberl(buf, "id_labels"); - flecs_json_array_push(buf); + bool has_next; + do { + Scope(with->expressions, + pos = flecs_script_with_expr(parser, pos); + ) - for (int i = 0; i < field_count; i ++) { - flecs_json_next(buf); - flecs_json_serialize_id_label(world, it->terms[i].id, buf); - } + if (!pos) { + goto error; + } - flecs_json_array_pop(buf); + Parse( + case ',': { + has_next = true; + break; + } + case '{': { + return flecs_script_scope(parser, with->scope, pos); + } + ) + } while (has_next); + + ParserEnd; } +/* Parenthesis expression */ static -void flecs_json_serialize_id_str( - const ecs_world_t *world, - ecs_id_t id, - ecs_strbuf_t *buf) +const char* flecs_script_paren_expr( + ecs_script_parser_t *parser, + const char *kind, + ecs_script_entity_t *entity, + const char *pos) { - ecs_strbuf_appendch(buf, '"'); - if (ECS_IS_PAIR(id)) { - ecs_entity_t first = ecs_pair_first(world, id); - ecs_entity_t second = ecs_pair_first(world, id); - ecs_strbuf_appendch(buf, '('); - ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf); - ecs_strbuf_appendch(buf, ','); - ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf); - ecs_strbuf_appendch(buf, ')'); - } else { - ecs_get_path_w_sep_buf( - world, 0, id & ECS_COMPONENT_MASK, ".", "", buf); - } - ecs_strbuf_appendch(buf, '"'); + ParserBegin; + + Expr(')', + entity->kind_w_expr = true; + + Scope(entity->scope, + ecs_script_component_t *component = + flecs_script_insert_component(parser, kind); + component->expr = Token(0); + ) + + Parse( + // Position spaceship (expr)\n + EcsTokEndOfStatement: { + EndOfRule; + } + + // Position spaceship (expr) { + case '{': { + return flecs_script_scope(parser, entity->scope, pos); + } + ) + ) + + ParserEnd; } +/* Parse a single statement */ static -void flecs_json_serialize_type_info( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) +const char* flecs_script_stmt( + ecs_script_parser_t *parser, + const char *pos) { - int32_t field_count = it->field_count; - if (!field_count) { - return; - } + ParserBegin; - if (it->flags & EcsIterNoData) { - return; - } + Parse( + case EcsTokIdentifier: goto identifier; + case '{': return flecs_script_scope(parser, + flecs_script_insert_scope(parser), pos); + case '(': goto paren; + case '@': goto annotation; + case EcsTokKeywordWith: goto with_stmt; + case EcsTokKeywordModule: goto module_stmt; + case EcsTokKeywordUsing: goto using_stmt; + case EcsTokKeywordTemplate: goto template_stmt; + case EcsTokKeywordProp: goto prop_var; + case EcsTokKeywordConst: goto const_var; + case EcsTokKeywordIf: goto if_stmt; + EcsTokEndOfStatement: EndOfRule; + ); - flecs_json_memberl(buf, "type_info"); - flecs_json_object_push(buf); +identifier: { + // enterprise } (end of scope) + LookAhead_1('}', + goto insert_tag; + ) - for (int i = 0; i < field_count; i ++) { - flecs_json_next(buf); - ecs_entity_t typeid = 0; - if (it->terms[i].inout != EcsInOutNone) { - typeid = ecs_get_typeid(world, it->terms[i].id); + Parse( + // enterprise { + case '{': { + return flecs_script_scope(parser, + flecs_script_insert_entity( + parser, Token(0))->scope, pos); } - if (typeid) { - flecs_json_serialize_id_str(world, typeid, buf); - ecs_strbuf_appendch(buf, ':'); - ecs_type_info_to_json_buf(world, typeid, buf); - } else { - flecs_json_serialize_id_str(world, it->terms[i].id, buf); - ecs_strbuf_appendlit(buf, ":0"); + + // Red, + case ',': { + flecs_script_insert_entity(parser, Token(0)); + pos = flecs_script_comma_expr(parser, pos, false); + EndOfRule; } - } - flecs_json_object_pop(buf); -} + // Npc\n + EcsTokEndOfStatement: { + // Npc\n{ + LookAhead_1('{', + pos = lookahead; + return flecs_script_scope(parser, + flecs_script_insert_entity( + parser, Token(0))->scope, pos); + ) -static -void flecs_json_serialize_iter_variables(ecs_iter_t *it, ecs_strbuf_t *buf) { - char **variable_names = it->variable_names; - int32_t var_count = it->variable_count; - int32_t actual_count = 0; + goto insert_tag; + } - for (int i = 1; i < var_count; i ++) { - const char *var_name = variable_names[i]; - if (flecs_json_skip_variable(var_name)) continue; + // auto_override | + case '|': { + goto identifier_flag; + } - if (!actual_count) { - flecs_json_memberl(buf, "vars"); - flecs_json_array_push(buf); - actual_count ++; + // Position: + case ':': { + goto identifier_colon; } - ecs_strbuf_list_next(buf); - flecs_json_string(buf, var_name); - } + // x = + case '=': { + goto identifier_assign; + } - if (actual_count) { - flecs_json_array_pop(buf); - } + // SpaceShip( + case '(': { + goto identifier_paren; + } + + // Spaceship enterprise + case EcsTokIdentifier: { + goto identifier_identifier; + } + ) } -static -void flecs_json_serialize_iter_result_ids( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - flecs_json_memberl(buf, "ids"); - flecs_json_array_push(buf); +insert_tag: { + if (Token(0)[0] == '$') { + if (!flecs_script_insert_var_component(parser, &Token(0)[1])) { + Error( + "invalid context for variable component '%s': must be " + "part of entity", tokens[0].value); + } + } else { + if (!flecs_script_insert_tag(parser, Token(0))) { + Error( + "invalid context for tag '%s': must be part of entity", + tokens[0].value); + } + } + + EndOfRule; +} + +// @ +annotation: { + // @brief + Parse_1(EcsTokIdentifier, + // $brief expr + Until('\n', + flecs_script_insert_annot(parser, Token(1), Token(2)); + EndOfRule; + ) + ) +} + +// with +with_stmt: { + ecs_script_with_t *with = flecs_script_insert_with(parser); + pos = flecs_script_with(parser, with, pos); + EndOfRule; +} + +// using +using_stmt: { + // using flecs.meta\n + Parse_1(EcsTokIdentifier, + flecs_script_insert_using(parser, Token(1)); + + Parse( + EcsTokEndOfStatement: + EndOfRule; + ) + ) +} + +// module +module_stmt: { + // using flecs.meta\n + Parse_2(EcsTokIdentifier, '\n', + flecs_script_insert_module(parser, Token(1)); + EndOfRule; + ) +} + +// template +template_stmt: { + // template SpaceShip + Parse_1(EcsTokIdentifier, + ecs_script_template_node_t *template = flecs_script_insert_template( + parser, Token(1)); + + Parse( + // template SpaceShip { + case '{': + return flecs_script_scope(parser, template->scope, pos); + + // template SpaceShip\n + EcsTokEndOfStatement: + EndOfRule; + ) + ) +} + +// prop +prop_var: { + // prop color = Color: + Parse_4(EcsTokIdentifier, '=', EcsTokIdentifier, ':', + ecs_script_var_node_t *var = flecs_script_insert_var( + parser, Token(1)); + var->node.kind = EcsAstProp; + var->type = Token(3); + + // prop color = Color : { + LookAhead_1('{', + // prop color = Color: {expr} + pos = lookahead; + Expr('}', + var->expr = Token(6); + EndOfRule; + ) + ) + + // prop color = Color : expr\n + Expr('\n', + var->expr = Token(5); + EndOfRule; + ) + ) +} + +// const +const_var: { + // const color + Parse_1(EcsTokIdentifier, + ecs_script_var_node_t *var = flecs_script_insert_var( + parser, Token(1)); + var->node.kind = EcsAstConst; + + Parse( + // const color = + case '=': { + // const color = Color : + LookAhead_2(EcsTokIdentifier, ':', + pos = lookahead; + + var->type = Token(3); + + // const color = Color: { + LookAhead_1('{', + // const color = Color: {expr} + pos = lookahead; + Expr('}', + var->expr = Token(6); + EndOfRule; + ) + ) + + // const color = Color: expr\n + Expr('\n', + var->expr = Token(5); + EndOfRule; + ) + ) + + // const PI = expr\n + Expr('\n', + var->expr = Token(3); + EndOfRule; + ) + } + ) + ) +} + +// if +if_stmt: { + // if expr { + Expr('{', + ecs_script_if_t *stmt = flecs_script_insert_if(parser); + stmt->expr = Token(1); + pos = flecs_script_scope(parser, stmt->if_true, pos); + if (!pos) { + goto error; + } - 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); - } + // if expr { } else + LookAhead_1(EcsTokKeywordElse, + pos = lookahead; + + // if expr { } else { + Parse_1('{', + return flecs_script_scope(parser, stmt->if_false, pos); + ) + ) + + EndOfRule; + ) +} + +// ( +paren: { + // (Likes, Apples) + Parse_4(EcsTokIdentifier, ',', EcsTokIdentifier, ')', + goto pair; + ) +} + +// (Likes, Apples) +pair: { + // (Likes, Apples) } (end of scope) + LookAhead_1('}', + flecs_script_insert_pair_tag(parser, Token(1), Token(3)); + EndOfRule; + ) + + Parse( + // (Likes, Apples)\n + EcsTokEndOfStatement: { + flecs_script_insert_pair_tag(parser, Token(1), Token(3)); + EndOfRule; + } + + // (Eats, Apples): + case ':': { + // (Eats, Apples): { + Parse_1('{', + // (Eats, Apples): { expr } + Expr('}', + ecs_script_component_t *comp = + flecs_script_insert_pair_component( + parser, Token(1), Token(3)); + comp->expr = Token(7); + EndOfRule; + ) + ) + } + + // (IsA, Machine) { + case '{': { + ecs_script_pair_scope_t *ps = flecs_script_insert_pair_scope( + parser, Token(1), Token(3)); + return flecs_script_scope(parser, ps->scope, pos); + } + ) +} + +// auto_override | +identifier_flag: { + ecs_id_t flag; + if (!ecs_os_strcmp(Token(0), "auto_override")) { + flag = ECS_AUTO_OVERRIDE; + } else { + Error("invalid flag '%s'", Token(0)); + } + + Parse( + // auto_override | ( + case '(': + // auto_override | (Rel, Tgt) + Parse_4(EcsTokIdentifier, ',', EcsTokIdentifier, ')', + ecs_script_tag_t *tag = flecs_script_insert_pair_tag( + parser, Token(3), Token(5)); + tag->id.flag = flag; + + Parse( + // auto_override | (Rel, Tgt)\n + EcsTokEndOfStatement: { + EndOfRule; + } - flecs_json_array_pop(buf); -} + // auto_override | (Rel, Tgt): + case ':': { + Parse_1('{', + // auto_override | (Rel, Tgt): {expr} + Expr('}', { + ecs_script_component_t *comp = + flecs_script_insert_pair_component( + parser, Token(3), Token(5)); + comp->expr = Token(9); + EndOfRule; + }) + ) + } + ) + ) + + // auto_override | Position + case EcsTokIdentifier: { + ecs_script_tag_t *tag = flecs_script_insert_tag( + parser, Token(2)); + tag->id.flag = flag; + + Parse( + // auto_override | Position\n + EcsTokEndOfStatement: { + EndOfRule; + } -static -void flecs_json_serialize_iter_result_id_labels( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - flecs_json_memberl(buf, "id_labels"); - flecs_json_array_push(buf); + // auto_override | Position: + case ':': { + Parse_1('{', + // auto_override | Position: {expr} + Expr('}', { + ecs_script_component_t *comp = flecs_script_insert_component( + parser, Token(2)); + comp->expr = Token(5); + EndOfRule; + }) + ) + } + ) + } + ) +} - for (int i = 0; i < it->field_count; i ++) { - flecs_json_next(buf); - flecs_json_serialize_id_label(world, ecs_field_id(it, i + 1), buf); +// Position: +identifier_colon: { + { + // Position: { + LookAhead_1('{', + pos = lookahead; + goto component_expr_scope; + ) } - flecs_json_array_pop(buf); + { + // Position: [ + LookAhead_1('[', + pos = lookahead; + goto component_expr_collection; + ) + } + + // enterprise : SpaceShip + Parse_1(EcsTokIdentifier, { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, Token(0)); + + Scope(entity->scope, + flecs_script_insert_pair_tag(parser, "IsA", Token(2)); + + LookAhead_1(',', { + pos = lookahead; + pos = flecs_script_comma_expr(parser, pos, true); + }) + ) + + Parse( + // enterprise : SpaceShip\n + EcsTokEndOfStatement: + EndOfRule; + + // enterprise : SpaceShip { + case '{': + return flecs_script_scope(parser, entity->scope, pos); + ) + }) +} + +// x = +identifier_assign: { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, Token(0)); + + // x = Position: + LookAhead_2(EcsTokIdentifier, ':', + pos = lookahead; + + // x = Position: { + Parse_1('{', { + // x = Position: {expr} + Expr('}', + Scope(entity->scope, + ecs_script_component_t *comp = + flecs_script_insert_component(parser, Token(2)); + comp->expr = Token(5); + ) + + // x = Position: {expr}\n + Parse( + EcsTokEndOfStatement: + EndOfRule; + ) + ) + }) + ) + + // x = f32\n + Expr('\n', + Scope(entity->scope, + ecs_script_default_component_t *comp = + flecs_script_insert_default_component(parser); + comp->expr = Token(2); + ) + + EndOfRule; + ) } -static -void flecs_json_serialize_iter_result_table_type( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_iter_to_json_desc_t *desc) -{ - if (!it->table) { - return; - } +// Spaceship enterprise +identifier_identifier: { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, Token(1)); + entity->kind = Token(0); - if (desc->serialize_ids) { - flecs_json_memberl(buf, "ids"); - flecs_json_array_push(buf); + // Spaceship enterprise : + LookAhead_1(':', + pos = lookahead; - ecs_type_t *type = &it->table->type; - for (int i = 0; i < type->count; i ++) { - ecs_id_t id = type->array[i]; - if (!desc->serialize_private) { - ecs_entity_t e = id; - if (ECS_IS_PAIR(id)) { - e = ecs_pair_first(world, id); - } - if (ecs_owns_id(world, e, EcsPrivate)) { - continue; - } - } - flecs_json_next(buf); - flecs_json_serialize_id(world, id, buf); + Parse_1(EcsTokIdentifier, { + Scope(entity->scope, + flecs_script_insert_pair_tag(parser, "IsA", Token(3)); + + LookAhead_1(',', { + pos = lookahead; + pos = flecs_script_comma_expr(parser, pos, true); + }) + ) + + goto identifier_identifier_x; + }) + ) + +identifier_identifier_x: + Parse( + // Spaceship enterprise\n + EcsTokEndOfStatement: { + EndOfRule; } - flecs_json_array_pop(buf); - } - if (desc->serialize_id_labels) { - flecs_json_memberl(buf, "id_labels"); - flecs_json_array_push(buf); + // Spaceship enterprise { + case '{': { + return flecs_script_scope(parser, entity->scope, pos); + } - ecs_type_t *type = &it->table->type; - for (int i = 0; i < type->count; i ++) { - ecs_id_t id = type->array[i]; - if (!desc->serialize_private) { - ecs_entity_t e = id; - if (ECS_IS_PAIR(id)) { - e = ecs_pair_first(world, id); - } - if (ecs_owns_id(world, e, EcsPrivate)) { - continue; - } - } - flecs_json_next(buf); - flecs_json_serialize_id_label(world, id, buf); + // Spaceship enterprise( + case '(': { + return flecs_script_paren_expr(parser, Token(0), entity, pos); } + ) +} - flecs_json_array_pop(buf); - } +// SpaceShip( +identifier_paren: { + // SpaceShip() + Expr(')', + Parse( + // SpaceShip(expr)\n + EcsTokEndOfStatement: { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, NULL); + + Scope(entity->scope, + ecs_script_component_t *comp = + flecs_script_insert_component(parser, Token(0)); + comp->expr = Token(2); + ) + + EndOfRule; + } + + // SpaceShip(expr) { + case '{': { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, NULL); + + Scope(entity->scope, + ecs_script_component_t *comp = + flecs_script_insert_component(parser, Token(0)); + comp->expr = Token(2); + ) + + return flecs_script_scope(parser, entity->scope, pos); + } + ) + ) } -static -void flecs_json_serialize_iter_result_sources( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - flecs_json_memberl(buf, "sources"); - flecs_json_array_push(buf); +// Position: { +component_expr_scope: { - for (int i = 0; i < it->field_count; i ++) { - flecs_json_next(buf); - ecs_entity_t subj = it->sources[i]; - if (subj) { - flecs_json_path(buf, world, subj); - } else { - ecs_strbuf_appendch(buf, '0'); - } - } + // Position: {expr} + Expr('}', { + ecs_script_component_t *comp = flecs_script_insert_component( + parser, Token(0)); + comp->expr = Token(3); + EndOfRule; + }) +} - flecs_json_array_pop(buf); +// Points: [ +component_expr_collection: { + // Position: [expr] + Expr(']', { + ecs_script_component_t *comp = flecs_script_insert_component( + parser, Token(0)); + comp->expr = Token(3); + comp->is_collection = true; + EndOfRule; + }) } -static -void flecs_json_serialize_iter_result_is_set( - const ecs_iter_t *it, - ecs_strbuf_t *buf) + ParserEnd; +} + +/* Parse script */ +ecs_script_t* ecs_script_parse( + ecs_world_t *world, + const char *name, + const char *code) { - if (!(it->flags & EcsIterHasCondSet)) { - return; + if (!code) { + code = ""; } - flecs_json_memberl(buf, "is_set"); - flecs_json_array_push(buf); + ecs_script_t *script = flecs_script_new(world); + script->name = ecs_os_strdup(name); + script->code = ecs_os_strdup(code); - for (int i = 0; i < it->field_count; i ++) { - ecs_strbuf_list_next(buf); - if (ecs_field_is_set(it, i + 1)) { - flecs_json_true(buf); - } else { - flecs_json_false(buf); - } - } + ecs_script_impl_t *impl = flecs_script_impl(script); - flecs_json_array_pop(buf); -} + ecs_script_parser_t parser = { + .script = impl, + .scope = impl->root, + .significant_newline = true + }; -static -void flecs_json_serialize_iter_result_variables( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - char **variable_names = it->variable_names; - ecs_var_t *variables = it->variables; - int32_t var_count = it->variable_count; - int32_t actual_count = 0; + /* Allocate a buffer that is able to store all parsed tokens. Multiply the + * size of the script by two so that there is enough space to add \0 + * terminators and expression deliminators ('""') + * The token buffer will exist for as long as the script object exists, and + * ensures that AST nodes don't need to do separate allocations for the data + * they contain. */ + impl->token_buffer_size = ecs_os_strlen(code) * 2 + 1; + impl->token_buffer = flecs_alloc( + &impl->allocator, impl->token_buffer_size); + parser.token_cur = impl->token_buffer; - for (int i = 1; i < var_count; i ++) { - const char *var_name = variable_names[i]; - if (flecs_json_skip_variable(var_name)) continue; + /* Start parsing code */ + const char *pos = script->code; - if (!actual_count) { - flecs_json_memberl(buf, "vars"); - flecs_json_array_push(buf); - actual_count ++; + do { + pos = flecs_script_stmt(&parser, pos); + if (!pos) { + /* NULL means error */ + goto error; } - ecs_strbuf_list_next(buf); - flecs_json_path(buf, world, variables[i].entity); - } + if (!pos[0]) { + /* \0 means end of input */ + break; + } + } while (true); - if (actual_count) { - flecs_json_array_pop(buf); - } + return script; +error: + ecs_script_free(script); + return NULL; } -static -void flecs_json_serialize_iter_result_variable_labels( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - char **variable_names = it->variable_names; - ecs_var_t *variables = it->variables; - int32_t var_count = it->variable_count; - int32_t actual_count = 0; +#endif - for (int i = 1; i < var_count; i ++) { - const char *var_name = variable_names[i]; - if (flecs_json_skip_variable(var_name)) continue; +/** + * @file addons/script/query_parser.c + * @brief Script grammar parser. + */ - if (!actual_count) { - flecs_json_memberl(buf, "var_labels"); - flecs_json_array_push(buf); - actual_count ++; + +#ifdef FLECS_SCRIPT + +#define EcsTokTermIdentifier\ + EcsTokIdentifier:\ + case EcsTokNumber:\ + case EcsTokMul + +#define EcsTokEndOfTerm\ + '}':\ + pos --; /* Give token back to parser */\ + case EcsTokOr:\ + if (t->kind == EcsTokOr) {\ + if (parser->term->oper != EcsAnd) {\ + Error("cannot mix operators in || expression");\ + }\ + parser->term->oper = EcsOr;\ + }\ + case ',':\ + case '\n':\ + case '\0' + +// $this == +static +const char* flecs_term_parse_equality_pred( + ecs_script_parser_t *parser, + const char *pos, + ecs_entity_t pred) +{ + ParserBegin; + + if (parser->term->oper != EcsAnd) { + Error("cannot mix operator with equality expression"); + } + + parser->term->src = parser->term->first; + parser->term->first = (ecs_term_ref_t){0}; + parser->term->first.id = pred; + + Parse( + // $this == foo + // ^ + case EcsTokTermIdentifier: { + parser->term->second.name = Token(0); + Parse( case EcsTokEndOfTerm: EndOfRule; ) + } + + // $this == "foo" + // ^ + case EcsTokString: { + parser->term->second.name = Token(0); + parser->term->second.id = EcsIsName; + + if (pred == EcsPredMatch) { + if (Token(0)[0] == '!') { + /* If match expression starts with !, set Not operator. The + * reason the ! is embedded in the expression is because + * there is only a single match (~=) operator. */ + parser->term->second.name ++; + parser->term->oper = EcsNot; + } + } + + Parse( + case EcsTokEndOfTerm: + EndOfRule; + ) } + ) - ecs_strbuf_list_next(buf); - flecs_json_label(buf, world, variables[i].entity); - } + ParserEnd; +} - if (actual_count) { - flecs_json_array_pop(buf); - } +static +ecs_entity_t flecs_query_parse_trav_flags( + const char *tok) +{ + if (!ecs_os_strcmp(tok, "self")) return EcsSelf; + else if (!ecs_os_strcmp(tok, "up")) return EcsUp; + else if (!ecs_os_strcmp(tok, "cascade")) return EcsCascade; + else if (!ecs_os_strcmp(tok, "desc")) return EcsDesc; + else return 0; } static -void flecs_json_serialize_iter_result_variable_ids( - const ecs_iter_t *it, - ecs_strbuf_t *buf) +const char* flecs_term_parse_trav( + ecs_script_parser_t *parser, + ecs_term_ref_t *ref, + const char *pos) { - char **variable_names = it->variable_names; - ecs_var_t *variables = it->variables; - int32_t var_count = it->variable_count; - int32_t actual_count = 0; + ParserBegin; - for (int i = 1; i < var_count; i ++) { - const char *var_name = variable_names[i]; - if (flecs_json_skip_variable(var_name)) continue; + Loop( + // self + Parse_1(EcsTokIdentifier, + ref->id |= flecs_query_parse_trav_flags(Token(0)); - if (!actual_count) { - flecs_json_memberl(buf, "var_ids"); - flecs_json_array_push(buf); - actual_count ++; - } + LookAhead( + // self| + case '|': + pos = lookahead; + continue; - ecs_strbuf_list_next(buf); - flecs_json_number(buf, (double)variables[i].entity); - } + // self IsA + case EcsTokIdentifier: + pos = lookahead; + parser->term->trav = ecs_lookup( + parser->script->pub.world, Token(1)); + if (!parser->term->trav) { + Error( + "unresolved traversal relationship '%s'", Token(1)); + goto error; + } - if (actual_count) { - flecs_json_array_pop(buf); - } + EndOfRule; + ) + + EndOfRule; + ) + ) + + ParserEnd; } +// Position( static -bool flecs_json_serialize_iter_result_entity_names( - const ecs_iter_t *it, - ecs_strbuf_t *buf) +const char* flecs_term_parse_arg( + ecs_script_parser_t *parser, + const char *pos, + int32_t arg) { - ecs_assert(it->count != 0, ECS_INTERNAL_ERROR, NULL); + ParserBegin; - EcsIdentifier *names = ecs_table_get_id(it->world, it->table, - ecs_pair(ecs_id(EcsIdentifier), EcsName), it->offset); - if (!names) { - return false; - } + ecs_term_ref_t *ref = NULL; - int i; - for (i = 0; i < it->count; i ++) { - flecs_json_next(buf); - flecs_json_string(buf, names[i].value); + // Position(src + if (arg == 0) { + ref = &parser->term->src; + + // Position(src, tgt + } else if (arg == 1) { + ref = &parser->term->second; + } else { + if (arg > FLECS_TERM_ARG_COUNT_MAX) { + Error("too many arguments in term"); + } + ref = &parser->extra_args[arg - 2]; } - return true; -} + bool is_trav_flag = false; -static -void flecs_json_serialize_iter_result_entity_ids( - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - if (!it->count) { - return; + LookAhead_1(EcsTokIdentifier, + is_trav_flag = flecs_query_parse_trav_flags(Token(0)) != 0; + ) + + if (is_trav_flag) { + // Position(self|up + // ^ + pos = flecs_term_parse_trav(parser, ref, pos); + if (!pos) { + goto error; + } + } else { + // Position(src + // ^ + Parse( + case EcsTokTermIdentifier: { + ref->name = Token(0); + + // Position(src| + // ^ + LookAhead_1('|', + pos = lookahead; + pos = flecs_term_parse_trav(parser, ref, pos); + if (!pos) { + goto error; + } + + // Position(src|up IsA + // ^ + LookAhead_1(EcsTokIdentifier, + pos = lookahead; + parser->term->trav = ecs_lookup( + parser->script->pub.world, Token(1)); + if (!parser->term->trav) { + Error( + "unresolved trav identifier '%s'", Token(1)); + } + ) + ) + + break; + } + ) } - flecs_json_memberl(buf, "entity_ids"); - flecs_json_array_push(buf); + Parse( + // Position(src, + // ^ + case ',': + if ((arg > 1) && parser->extra_oper != EcsAnd) { + Error("cannot mix operators in extra term arguments"); + } + parser->extra_oper = EcsAnd; + return flecs_term_parse_arg(parser, pos, arg + 1); - ecs_entity_t *entities = it->entities; + // Position(src, second || + // ^ + case EcsTokOr: + if ((arg > 1) && parser->extra_oper != EcsOr) { + Error("cannot mix operators in extra term arguments"); + } + parser->extra_oper = EcsOr; + return flecs_term_parse_arg(parser, pos, arg + 1); - int i, count = it->count; - for (i = 0; i < count; i ++) { - flecs_json_next(buf); - flecs_json_number(buf, (double)(uint32_t)entities[i]); - } + // Position(src) + // ^ + case ')': + Parse( + case EcsTokEndOfTerm: + EndOfRule; + ) + ) - flecs_json_array_pop(buf); + ParserEnd; } +// Position static -void flecs_json_serialize_iter_result_parent( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) +const char* flecs_term_parse_id( + ecs_script_parser_t *parser, + const char *pos) { - ecs_table_t *table = it->table; - if (!(table->flags & EcsTableHasChildOf)) { - return; - } - - ecs_table_record_t *tr = flecs_id_record_get_table( - world->idr_childof_wildcard, it->table); - if (tr == NULL) { - return; - } + ParserBegin; - ecs_id_t id = table->type.array[tr->index]; - ecs_entity_t parent = ecs_pair_second(world, id); - char *path = ecs_get_fullpath(world, parent); - flecs_json_memberl(buf, "parent"); - flecs_json_string(buf, path); - ecs_os_free(path); -} + Parse( + case EcsTokEq: + return flecs_term_parse_equality_pred( + parser, pos, EcsPredEq); + case EcsTokNeq: { + const char *ret = flecs_term_parse_equality_pred( + parser, pos, EcsPredEq); + if (ret) { + parser->term->oper = EcsNot; + } + return ret; + } + case EcsTokMatch: + return flecs_term_parse_equality_pred( + parser, pos, EcsPredMatch); -static -void flecs_json_serialize_iter_result_entities( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - if (!it->count) { - return; - } + // Position| + case '|': { + pos = flecs_term_parse_trav(parser, &parser->term->first, pos); + if (!pos) { + goto error; + } - flecs_json_serialize_iter_result_parent(world, it, buf); + // Position|self( + Parse( + case '(': + return flecs_term_parse_arg(parser, pos, 0); + case EcsTokEndOfTerm: + EndOfRule; + ) + } - flecs_json_memberl(buf, "entities"); - flecs_json_array_push(buf); + // Position( + case '(': { + // Position() + LookAhead_1(')', + pos = lookahead; + parser->term->src.id = EcsIsEntity; - if (!flecs_json_serialize_iter_result_entity_names(it, buf)) { - ecs_entity_t *entities = it->entities; + Parse( + case EcsTokEndOfTerm: + EndOfRule; + ) + ) - int i, count = it->count; - for (i = 0; i < count; i ++) { - flecs_json_next(buf); - flecs_json_number(buf, (double)(uint32_t)entities[i]); + return flecs_term_parse_arg(parser, pos, 0); } - } - flecs_json_array_pop(buf); + case EcsTokEndOfTerm: + EndOfRule; + ) + + ParserEnd; } -static -void flecs_json_serialize_iter_result_entity_labels( - const ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_json_ser_idr_t *ser_idr) +// ( +static const char* flecs_term_parse_pair( + ecs_script_parser_t *parser, + const char *pos) { - (void)buf; - (void)ser_idr; - if (!it->count) { - return; - } - - if (!ser_idr->idr_doc_name) { - return; - } - -#ifdef FLECS_DOC - ecs_table_t *table = it->table; - ecs_table_record_t *tr = flecs_id_record_get_table( - ser_idr->idr_doc_name, table); - if (tr == NULL) { - return; - } + ParserBegin; - EcsDocDescription *labels = ecs_table_get_column( - table, tr->column, it->offset); - ecs_assert(labels != NULL, ECS_INTERNAL_ERROR, NULL); + // (Position + // ^ + Parse( + case EcsTokTermIdentifier: { + parser->term->first.name = Token(0); - flecs_json_memberl(buf, "entity_labels"); - flecs_json_array_push(buf); + LookAhead_1('|', + // (Position|self + pos = lookahead; + pos = flecs_term_parse_trav( + parser, &parser->term->first, pos); + if (!pos) { + goto error; + } + ) - int i; - for (i = 0; i < it->count; i ++) { - flecs_json_next(buf); - flecs_json_string(buf, labels[i].value); - } + // (Position, + Parse_1(',', + return flecs_term_parse_arg(parser, pos, 1); + ) + } + ) - flecs_json_array_pop(buf); -#endif + ParserEnd; } +// AND static -void flecs_json_serialize_iter_result_colors( - const ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_json_ser_idr_t *ser_idr) +const char* flecs_term_parse_flags( + ecs_script_parser_t *parser, + const char *token_0, + const char *pos) { - (void)buf; - (void)ser_idr; + ecs_assert(token_0 != NULL, ECS_INTERNAL_ERROR, NULL); - if (!it->count) { - return; - } + ParserBegin; -#ifdef FLECS_DOC - if (!ser_idr->idr_doc_color) { - return; - } + ecs_id_t flag = 0; + int16_t oper = 0; + ecs_term_t *term = parser->term; - ecs_table_record_t *tr = flecs_id_record_get_table( - ser_idr->idr_doc_color, it->table); - if (tr == NULL) { - return; - } + // AND + if (!ecs_os_strcmp(token_0, "and")) oper = EcsAndFrom; + else if (!ecs_os_strcmp(token_0, "or")) oper = EcsOrFrom; + else if (!ecs_os_strcmp(token_0, "not")) oper = EcsNotFrom; + else if (!ecs_os_strcmp(token_0, "auto_override")) flag = ECS_AUTO_OVERRIDE; + else if (!ecs_os_strcmp(token_0, "toggle")) flag = ECS_TOGGLE; + else { + // Position + term->first.name = token_0; + return flecs_term_parse_id(parser, pos); + } + + if (oper || flag) { + // and | + // ^ + Parse_1('|', + Parse( + // and | Position + // ^ + case EcsTokTermIdentifier: { + if (oper) { + term->oper = oper; + } else if (flag) { + term->id = flag; + } - EcsDocDescription *colors = ecs_table_get_column( - it->table, tr->column, it->offset); - ecs_assert(colors != NULL, ECS_INTERNAL_ERROR, NULL); + term->first.name = Token(1); - flecs_json_memberl(buf, "colors"); - flecs_json_array_push(buf); + return flecs_term_parse_id(parser, pos); + } - int i; - for (i = 0; i < it->count; i ++) { - flecs_json_next(buf); - flecs_json_string(buf, colors[i].value); + // and | ( + // ^ + case '(': { + return flecs_term_parse_pair(parser, pos); + } + ) + ) } - flecs_json_array_pop(buf); -#endif + ParserEnd; } +// ! static -int flecs_json_serialize_iter_result_values( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) +const char* flecs_term_parse_unary( + ecs_script_parser_t *parser, + const char *pos) { - if (!it->ptrs || (it->flags & EcsIterNoData)) { - return 0; - } - - flecs_json_memberl(buf, "values"); - flecs_json_array_push(buf); - - int32_t i, term_count = it->field_count; - for (i = 0; i < term_count; i ++) { - ecs_strbuf_list_next(buf); + ParserBegin; - const void *ptr = NULL; - if (it->ptrs) { - ptr = it->ptrs[i]; + Parse( + // !( + case '(': { + return flecs_term_parse_pair(parser, pos); } - if (!ptr) { - /* No data in column. Append 0 if this is not an optional term */ - if (ecs_field_is_set(it, i + 1)) { - ecs_strbuf_appendch(buf, '0'); - continue; - } + // !{ + case '{': { + parser->term->first.id = EcsScopeOpen; + parser->term->src.id = EcsIsEntity; + parser->term->inout = EcsInOutNone; + EndOfRule; } - if (ecs_field_is_writeonly(it, i + 1)) { - ecs_strbuf_appendch(buf, '0'); - continue; + // !Position + // ^ + case EcsTokTermIdentifier: { + parser->term->first.name = Token(0); + return flecs_term_parse_id(parser, pos); } + ) - /* Get component id (can be different in case of pairs) */ - ecs_entity_t type = ecs_get_typeid(world, it->ids[i]); - if (!type) { - /* Odd, we have a ptr but no Component? Not the place of the - * serializer to complain about that. */ - ecs_strbuf_appendch(buf, '0'); - continue; - } + ParserEnd; +} - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - if (!comp) { - /* Also odd, typeid but not a component? */ - ecs_strbuf_appendch(buf, '0'); - continue; - } +// [ +static +const char* flecs_term_parse_inout( + ecs_script_parser_t *parser, + const char *pos) +{ + ParserBegin; - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - if (!ser) { - /* Not odd, component just has no reflection data */ - ecs_strbuf_appendch(buf, '0'); - continue; - } + ecs_term_t *term = parser->term; - /* If term is not set, append empty array. This indicates that the term - * could have had data but doesn't */ - if (!ecs_field_is_set(it, i + 1)) { - flecs_json_array_push(buf); - flecs_json_array_pop(buf); - continue; - } + // [inout] + // ^ + Parse_2(EcsTokIdentifier, ']', + if (!ecs_os_strcmp(Token(0), "default")) term->inout = EcsInOutDefault; + else if (!ecs_os_strcmp(Token(0), "none")) term->inout = EcsInOutNone; + else if (!ecs_os_strcmp(Token(0), "filter")) term->inout = EcsInOutFilter; + else if (!ecs_os_strcmp(Token(0), "inout")) term->inout = EcsInOut; + else if (!ecs_os_strcmp(Token(0), "in")) term->inout = EcsIn; + else if (!ecs_os_strcmp(Token(0), "out")) term->inout = EcsOut; - if (ecs_field_is_self(it, i + 1)) { - int32_t count = it->count; - if (array_to_json_buf_w_type_data(world, ptr, count, buf, comp, ser)) { - return -1; - } - } else { - if (array_to_json_buf_w_type_data(world, ptr, 0, buf, comp, ser)) { - return -1; + Parse( + // [inout] Position + // ^ + case EcsTokTermIdentifier: { + return flecs_term_parse_flags(parser, Token(2), pos); } - } - } - flecs_json_array_pop(buf); + // [inout] !Position + // ^ + case '!': + term->oper = EcsNot; + return flecs_term_parse_unary(parser, pos); + case '?': + term->oper = EcsOptional; + return flecs_term_parse_unary(parser, pos); - return 0; + // [inout] ( + // ^ + case '(': { + return flecs_term_parse_pair(parser, pos); + } + ) + ) + + ParserEnd; } static -int flecs_json_serialize_iter_result_columns( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_iter_to_json_desc_t *desc) +const char* flecs_query_term_parse( + ecs_script_parser_t *parser, + const char *pos) { - ecs_table_t *table = it->table; - if (!table || !table->column_count) { + ParserBegin; + + Parse( + case '[': + return flecs_term_parse_inout(parser, pos); + case EcsTokTermIdentifier: + return flecs_term_parse_flags(parser, Token(0), pos); + case '(': + return flecs_term_parse_pair(parser, pos); + case '!': + parser->term->oper = EcsNot; + return flecs_term_parse_unary(parser, pos); + case '?': + parser->term->oper = EcsOptional; + return flecs_term_parse_unary(parser, pos); + case '{': + parser->term->first.id = EcsScopeOpen; + parser->term->src.id = EcsIsEntity; + parser->term->inout = EcsInOutNone; + EndOfRule; + case '}': + parser->term->first.id = EcsScopeClose; + parser->term->src.id = EcsIsEntity; + parser->term->inout = EcsInOutNone; + LookAhead_1(',', + pos = lookahead; + ) + EndOfRule; + case '\n':\ + case '\0': + EndOfRule; + ); + + ParserEnd; +} + +int flecs_terms_parse( + ecs_script_t *script, + ecs_term_t *terms, + int32_t *term_count_out) +{ + if (!ecs_os_strcmp(script->code, "0")) { + *term_count_out = 0; return 0; } - flecs_json_memberl(buf, "values"); - flecs_json_array_push(buf); + ecs_script_parser_t parser = { + .script = flecs_script_impl(script), + .pos = script->code + }; - ecs_type_t *type = &table->type; - int32_t *column_map = table->column_map; - ecs_assert(column_map != NULL, ECS_INTERNAL_ERROR, NULL); + parser.token_cur = flecs_script_impl(script)->token_buffer; - for (int i = 0; i < type->count; i ++) { - int32_t storage_column = -1; - if (column_map) { - storage_column = column_map[i]; - } + int32_t term_count = 0; + const char *ptr = script->code; + ecs_term_ref_t extra_args[FLECS_TERM_ARG_COUNT_MAX]; + ecs_os_memset_n(extra_args, 0, ecs_term_ref_t, + FLECS_TERM_ARG_COUNT_MAX); - if (!desc->serialize_private) { - ecs_id_t id = type->array[i]; - ecs_entity_t e = id; - if (ECS_IS_PAIR(id)) { - e = ecs_pair_first(world, id); - } - if (ecs_owns_id(world, e, EcsPrivate)) { - continue; - } + parser.extra_args = extra_args; + parser.extra_oper = 0; + + do { + if (term_count == FLECS_TERM_COUNT_MAX) { + ecs_err("max number of terms (%d) reached, increase " + "FLECS_TERM_COUNT_MAX to support more", + FLECS_TERM_COUNT_MAX); + goto error; } - ecs_strbuf_list_next(buf); + /* Parse next term */ + ecs_term_t *term = &terms[term_count]; + parser.term = term; + ecs_os_memset_t(term, 0, ecs_term_t); + ecs_os_memset_n(extra_args, 0, ecs_term_ref_t, FLECS_TERM_ARG_COUNT_MAX); + parser.extra_oper = 0; - if (storage_column == -1) { - ecs_strbuf_appendch(buf, '0'); - continue; + ptr = flecs_query_term_parse(&parser, ptr); + if (!ptr) { + /* Parser error */ + goto error; } - ecs_entity_t typeid = table->data.columns[storage_column].ti->component; - if (!typeid) { - ecs_strbuf_appendch(buf, '0'); - continue; + if (!ecs_term_is_initialized(term)) { + /* Last term parsed */ + break; } - const EcsComponent *comp = ecs_get(world, typeid, EcsComponent); - if (!comp) { - ecs_strbuf_appendch(buf, '0'); - continue; - } + term_count ++; - const EcsMetaTypeSerialized *ser = ecs_get( - world, typeid, EcsMetaTypeSerialized); - if (!ser) { - ecs_strbuf_appendch(buf, '0'); - continue; + /* Unpack terms with more than two args into multiple terms so that: + * Rel(X, Y, Z) + * becomes: + * Rel(X, Y), Rel(Y, Z) */ + int32_t arg = 0; + while (ecs_term_ref_is_set(&extra_args[arg ++])) { + ecs_assert(arg <= FLECS_TERM_ARG_COUNT_MAX, + ECS_INTERNAL_ERROR, NULL); + + if (term_count == FLECS_TERM_COUNT_MAX) { + ecs_err("max number of terms (%d) reached, increase " + "FLECS_TERM_COUNT_MAX to support more", + FLECS_TERM_COUNT_MAX); + goto error; + } + + term = &terms[term_count ++]; + *term = term[-1]; + + if (parser.extra_oper == EcsAnd) { + term->src = term[-1].second; + term->second = extra_args[arg - 1]; + } else if (parser.extra_oper == EcsOr) { + term->src = term[-1].src; + term->second = extra_args[arg - 1]; + term[-1].oper = EcsOr; + } + + if (term->first.name != NULL) { + term->first.name = term->first.name; + } + + if (term->src.name != NULL) { + term->src.name = term->src.name; + } } - void *ptr = ecs_vec_first(&table->data.columns[storage_column].data); - if (array_to_json_buf_w_type_data(world, ptr, it->count, buf, comp, ser)) { - return -1; + if (arg) { + ecs_os_memset_n(extra_args, 0, ecs_term_ref_t, + FLECS_TERM_ARG_COUNT_MAX); } - } + } while (ptr[0]); - flecs_json_array_pop(buf); + (*term_count_out) += term_count; return 0; +error: + return -1; } -static -int flecs_json_serialize_iter_result( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_iter_to_json_desc_t *desc, - const ecs_json_ser_idr_t *ser_idr) +const char* flecs_term_parse( + ecs_world_t *world, + const char *name, + const char *expr, + ecs_term_t *term, + char *token_buffer) { - flecs_json_next(buf); - flecs_json_object_push(buf); + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(expr != NULL, ECS_INVALID_PARAMETER, name); + ecs_assert(term != NULL, ECS_INVALID_PARAMETER, NULL); - /* 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_table) { - flecs_json_serialize_iter_result_table_type(world, it, buf, desc); - } else { - if (!desc || desc->serialize_ids) { - flecs_json_serialize_iter_result_ids(world, it, buf); - } - if (desc && desc->serialize_id_labels) { - flecs_json_serialize_iter_result_id_labels(world, it, buf); - } - } + EcsParserFixedBuffer(world, name, expr, token_buffer, 256); + parser.term = term; - /* Include information on which entity the term is matched with */ - if (!desc || (desc->serialize_sources && !desc->serialize_table)) { - flecs_json_serialize_iter_result_sources(world, it, buf); + const char *result = flecs_query_term_parse(&parser, expr); + if (!result) { + return NULL; } - /* Write variable values for current result */ - if (!desc || desc->serialize_variables) { - flecs_json_serialize_iter_result_variables(world, it, buf); - } + ecs_os_memset_t(term, 0, ecs_term_t); - /* Write labels for variables */ - if (desc && desc->serialize_variable_labels) { - flecs_json_serialize_iter_result_variable_labels(world, it, buf); - } + return flecs_query_term_parse(&parser, expr); +} - /* Write ids for variables */ - if (desc && desc->serialize_variable_ids) { - flecs_json_serialize_iter_result_variable_ids(it, buf); - } +const char* flecs_id_parse( + const ecs_world_t *world, + const char *name, + const char *expr, + ecs_id_t *id) +{ + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(expr != NULL, ECS_INVALID_PARAMETER, name); + ecs_assert(id != NULL, ECS_INVALID_PARAMETER, NULL); + + char token_buffer[256]; + ecs_term_t term = {0}; + EcsParserFixedBuffer(world, name, expr, token_buffer, 256); + parser.term = &term; - /* Include information on which terms are set, to support optional terms */ - if (!desc || (desc->serialize_is_set && !desc->serialize_table)) { - flecs_json_serialize_iter_result_is_set(it, buf); + expr = flecs_scan_whitespace(&parser, expr); + if (!ecs_os_strcmp(expr, "#0")) { + *id = 0; + return &expr[1]; } - /* Write entity ids for current result (for queries with This terms) */ - if (!desc || desc->serialize_entities) { - flecs_json_serialize_iter_result_entities(world, it, buf); + const char *result = flecs_query_term_parse(&parser, expr); + if (!result) { + return NULL; } - /* Write ids for entities */ - if (desc && desc->serialize_entity_ids) { - flecs_json_serialize_iter_result_entity_ids(it, buf); + if (ecs_term_finalize(world, &term)) { + return NULL; } - /* Write labels for entities */ - if (desc && desc->serialize_entity_labels) { - flecs_json_serialize_iter_result_entity_labels(it, buf, ser_idr); + if (term.oper != EcsAnd) { + ecs_parser_error(name, expr, (result - expr), + "invalid operator for add expression"); + return NULL; } - /* Write colors for entities */ - if (desc && desc->serialize_colors) { - flecs_json_serialize_iter_result_colors(it, buf, ser_idr); + if ((term.src.id & ~EcsTraverseFlags) != (EcsThis|EcsIsVariable)) { + ecs_parser_error(name, expr, (result - expr), + "invalid source for add expression (must be $this)"); + return NULL; } - /* Serialize component values */ - if (desc && desc->serialize_table) { - if (flecs_json_serialize_iter_result_columns(world, it, buf, desc)) { - return -1; + *id = term.id; + + return result; +} + +static +const char* flecs_query_arg_parse( + ecs_script_parser_t *parser, + ecs_query_t *q, + ecs_iter_t *it, + const char *pos) +{ + ParserBegin; + + Parse_3(EcsTokIdentifier, ':', EcsTokIdentifier, { + int var = ecs_query_find_var(q, Token(0)); + if (var == -1) { + Error("unknown variable '%s'", Token(0)); } - } else { - if (!desc || desc->serialize_values) { - if (flecs_json_serialize_iter_result_values(world, it, buf)) { - return -1; - } + + ecs_entity_t val = ecs_lookup(q->world, Token(2)); + if (!val) { + Error("unresolved entity '%s'", Token(2)); } - } - /* Add "alerts": true member if table has entities with active alerts */ -#ifdef FLECS_ALERTS - if (it->table && (ecs_id(EcsAlertsActive) != 0)) { - /* Only add field if alerts addon is imported */ - if (ecs_table_has_id(world, it->table, ecs_id(EcsAlertsActive))) { - flecs_json_memberl(buf, "alerts"); - flecs_json_true(buf); + ecs_iter_set_var(it, var, val); + + EndOfRule; + }) + + ParserEnd; +} + +static +const char* flecs_query_args_parse( + ecs_script_parser_t *parser, + ecs_query_t *q, + ecs_iter_t *it, + const char *pos) +{ + ParserBegin; + + bool has_paren = false; + LookAhead( + case '\0': + pos = lookahead; + EndOfRule; + case '(': { + pos = lookahead; + has_paren = true; + LookAhead_1(')', + pos = lookahead; + EndOfRule; + ) + } + ) + + Loop( + pos = flecs_query_arg_parse(parser, q, it, pos); + if (!pos) { + goto error; } - } -#endif - flecs_json_object_pop(buf); + Parse( + case ',': + continue; + case '\0': + EndOfRule; + case ')': + if (!has_paren) { + Error("unexpected ')' without opening '(')"); + } + EndOfRule; + ) + ) - return 0; + ParserEnd; } -int ecs_iter_to_json_buf( - const ecs_world_t *world, +const char* ecs_query_args_parse( + ecs_query_t *q, ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_iter_to_json_desc_t *desc) + const char *expr) { - ecs_time_t duration = {0}; - if (desc && desc->measure_eval_duration) { - ecs_time_measure(&duration); + flecs_poly_assert(q, ecs_query_t); + ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(expr != NULL, ECS_INVALID_PARAMETER, NULL); + + const char *q_name = q->entity ? ecs_get_name(q->world, q->entity) : NULL; + if (ecs_os_strlen(expr) > 512) { + ecs_parser_error(q_name, expr, 0, "query argument expression too long"); + return NULL; } - flecs_json_object_push(buf); + char token_buffer[1024]; + EcsParserFixedBuffer(q->world, q_name, expr, token_buffer, 256); + return flecs_query_args_parse(&parser, q, it, expr); +error: + return NULL; +} - /* Serialize component ids of the terms (usually provided by query) */ - if (!desc || desc->serialize_term_ids) { - flecs_json_serialize_iter_ids(world, it, buf); - } +#endif + +/** + * @file addons/script/script.c + * @brief Script API. + */ + + +#ifdef FLECS_SCRIPT + +ECS_COMPONENT_DECLARE(EcsScript); - if (desc && desc->serialize_term_labels) { - flecs_json_serialize_iter_id_labels(world, it, buf); +static +ECS_MOVE(EcsScript, dst, src, { + if (dst->script && (dst->script != src->script)) { + if (dst->template_ && (dst->template_ != src->template_)) { + flecs_script_template_fini( + flecs_script_impl(dst->script), dst->template_); + } + ecs_script_free(dst->script); } + dst->script = src->script; + dst->template_ = src->template_; + src->script = NULL; + src->template_ = NULL; +}) - /* Serialize type info if enabled */ - if (desc && desc->serialize_type_info) { - flecs_json_serialize_type_info(world, it, buf); +static +ECS_DTOR(EcsScript, ptr, { + if (ptr->template_) { + flecs_script_template_fini( + flecs_script_impl(ptr->script), ptr->template_); } + if (ptr->script) { + ecs_script_free(ptr->script); + } +}) - /* Serialize variable names, if iterator has any */ - flecs_json_serialize_iter_variables(it, buf); +static +ecs_id_t flecs_script_tag( + ecs_entity_t script, + ecs_entity_t instance) +{ + if (!instance) { + return ecs_pair_t(EcsScript, script); + } else { + return ecs_pair(EcsChildOf, instance); + } +} - /* Serialize results */ - flecs_json_memberl(buf, "results"); - flecs_json_array_push(buf); +ecs_script_t* flecs_script_new( + ecs_world_t *world) +{ + ecs_script_impl_t *result = ecs_os_calloc_t(ecs_script_impl_t); + flecs_allocator_init(&result->allocator); + ecs_script_parser_t parser = { .script = result }; + result->root = flecs_script_scope_new(&parser); + result->pub.world = world; + result->refcount = 1; + return &result->pub; +} - /* Use instancing for improved performance */ - ECS_BIT_SET(it->flags, EcsIterIsInstanced); +void ecs_script_clear( + ecs_world_t *world, + ecs_entity_t script, + ecs_entity_t instance) +{ + ecs_delete_with(world, flecs_script_tag(script, instance)); +} - /* 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, EcsIterNoData); +int ecs_script_run( + ecs_world_t *world, + const char *name, + const char *code) +{ + ecs_script_t *script = ecs_script_parse(world, name, code); + if (!script) { + goto error; } - /* Cache id record for flecs.doc ids */ - ecs_json_ser_idr_t ser_idr = {NULL, NULL}; -#ifdef FLECS_DOC - ser_idr.idr_doc_name = flecs_id_record_get(world, - ecs_pair_t(EcsDocDescription, EcsName)); - ser_idr.idr_doc_color = flecs_id_record_get(world, - ecs_pair_t(EcsDocDescription, EcsDocColor)); -#endif + ecs_entity_t prev_scope = ecs_set_scope(world, 0); - ecs_iter_next_action_t next = it->next; - while (next(it)) { - if (flecs_json_serialize_iter_result(world, it, buf, desc, &ser_idr)) { - ecs_strbuf_reset(buf); - ecs_iter_fini(it); - return -1; - } + if (ecs_script_eval(script)) { + goto error_free; } - flecs_json_array_pop(buf); + ecs_set_scope(world, prev_scope); + + ecs_script_free(script); + return 0; +error_free: + ecs_script_free(script); +error: + return -1; +} - if (desc && desc->measure_eval_duration) { - double dt = ecs_time_measure(&duration); - flecs_json_memberl(buf, "eval_duration"); - flecs_json_number(buf, dt); +int ecs_script_run_file( + ecs_world_t *world, + const char *filename) +{ + char *script = flecs_load_from_file(filename); + if (!script) { + return -1; } - flecs_json_object_pop(buf); + int result = ecs_script_run(world, filename, script); + ecs_os_free(script); + return result; +} - return 0; +void ecs_script_free( + ecs_script_t *script) +{ + ecs_script_impl_t *impl = flecs_script_impl(script); + ecs_check(impl->refcount > 0, ECS_INVALID_OPERATION, NULL); + if (!--impl->refcount) { + flecs_script_visit_free(script); + flecs_free(&impl->allocator, + impl->token_buffer_size, impl->token_buffer); + flecs_allocator_fini(&impl->allocator); + ecs_os_free(ECS_CONST_CAST(char*, impl->pub.name)); /* safe, owned value */ + ecs_os_free(ECS_CONST_CAST(char*, impl->pub.code)); /* safe, owned value */ + ecs_os_free(impl); + } +error: + return; } -char* ecs_iter_to_json( - const ecs_world_t *world, - ecs_iter_t *it, - const ecs_iter_to_json_desc_t *desc) +int ecs_script_update( + ecs_world_t *world, + ecs_entity_t e, + ecs_entity_t instance, + const char *code) { - ecs_strbuf_t buf = ECS_STRBUF_INIT; + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(code != NULL, ECS_INTERNAL_ERROR, NULL); + + const char *name = ecs_get_name(world, e); + EcsScript *s = ecs_ensure(world, e, EcsScript); + if (s->template_) { + char *template_name = ecs_get_path(world, s->template_->entity); + ecs_err("cannot update scripts for individual templates, " + "update parent script instead (tried to update '%s')", + template_name); + ecs_os_free(template_name); + return -1; + } - if (ecs_iter_to_json_buf(world, it, &buf, desc)) { - ecs_strbuf_reset(&buf); - return NULL; + if (s->script) { + ecs_script_free(s->script); } - return ecs_strbuf_get(&buf); + s->script = ecs_script_parse(world, name, code); + if (!s->script) { + return -1; + } + + int result = 0; + bool is_defer = ecs_is_deferred(world); + ecs_suspend_readonly_state_t srs; + ecs_world_t *real_world = NULL; + if (is_defer) { + ecs_assert(flecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL); + real_world = flecs_suspend_readonly(world, &srs); + ecs_assert(real_world != NULL, ECS_INTERNAL_ERROR, NULL); + } + + ecs_script_clear(world, e, instance); + + ecs_entity_t prev = ecs_set_with(world, flecs_script_tag(e, instance)); + + if (ecs_script_eval(s->script)) { + ecs_delete_with(world, ecs_pair_t(EcsScript, e)); + result = -1; + } + + ecs_set_with(world, prev); + + if (is_defer) { + flecs_resume_readonly(real_world, &srs); + } + + return result; } -int ecs_world_to_json_buf( +ecs_entity_t ecs_script_init( ecs_world_t *world, - ecs_strbuf_t *buf_out, - const ecs_world_to_json_desc_t *desc) + const ecs_script_desc_t *desc) { - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_desc_t filter_desc = {0}; - filter_desc.storage = &f; - - if (desc && desc->serialize_builtin && desc->serialize_modules) { - filter_desc.terms[0].id = EcsAny; - } else { - bool serialize_builtin = desc && desc->serialize_builtin; - bool serialize_modules = desc && desc->serialize_modules; - int32_t term_id = 0; + const char *script = NULL; + ecs_entity_t e = desc->entity; + + ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_check(desc != NULL, ECS_INTERNAL_ERROR, NULL); - if (!serialize_builtin) { - filter_desc.terms[term_id].id = ecs_pair(EcsChildOf, EcsFlecs); - filter_desc.terms[term_id].oper = EcsNot; - filter_desc.terms[term_id].src.flags = EcsSelf | EcsParent; - term_id ++; + if (!e) { + if (desc->filename) { + e = ecs_new_from_path_w_sep(world, 0, desc->filename, "/", NULL); + } else { + e = ecs_new(world); } - if (!serialize_modules) { - filter_desc.terms[term_id].id = EcsModule; - filter_desc.terms[term_id].oper = EcsNot; - filter_desc.terms[term_id].src.flags = EcsSelf | EcsParent; + } + + script = desc->code; + if (!script && desc->filename) { + script = flecs_load_from_file(desc->filename); + if (!script) { + goto error; } } - if (ecs_filter_init(world, &filter_desc) == NULL) { - return -1; + if (ecs_script_update(world, e, 0, script)) { + goto error; } - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_to_json_desc_t json_desc = { - .serialize_table = true, - .serialize_ids = true, - .serialize_entities = true, - .serialize_private = true - }; + if (script != desc->code) { + /* Safe cast, only happens when script is loaded from file */ + ecs_os_free(ECS_CONST_CAST(char*, script)); + } - int ret = ecs_iter_to_json_buf(world, &it, buf_out, &json_desc); - ecs_filter_fini(&f); - return ret; + return e; +error: + if (script != desc->code) { + /* Safe cast, only happens when script is loaded from file */ + ecs_os_free(ECS_CONST_CAST(char*, script)); + } + if (!desc->entity) { + ecs_delete(world, e); + } + return 0; } -char* ecs_world_to_json( - ecs_world_t *world, - const ecs_world_to_json_desc_t *desc) -{ - ecs_strbuf_t buf = ECS_STRBUF_INIT; +static +int EcsScript_serialize(const ecs_serializer_t *ser, const void *ptr) { + const EcsScript *data = ptr; + if (data->script) { + ser->member(ser, "name"); + ser->value(ser, ecs_id(ecs_string_t), &data->script->name); + ser->member(ser, "code"); + ser->value(ser, ecs_id(ecs_string_t), &data->script->code); - if (ecs_world_to_json_buf(world, &buf, desc)) { - ecs_strbuf_reset(&buf); - return NULL; + char *ast = ecs_script_ast_to_str(data->script); + ser->member(ser, "ast"); + ser->value(ser, ecs_id(ecs_string_t), &ast); + ecs_os_free(ast); + } else { + char *nullString = NULL; + ser->member(ser, "name"); + ser->value(ser, ecs_id(ecs_string_t), &nullString); + ser->member(ser, "code"); + ser->value(ser, ecs_id(ecs_string_t), &nullString); + ser->member(ser, "ast"); + ser->value(ser, ecs_id(ecs_string_t), &nullString); } + return 0; +} - return ecs_strbuf_get(&buf); +void FlecsScriptImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsScript); + ECS_IMPORT(world, FlecsMeta); +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsScript), + "Module with components for managing Flecs scripts"); +#endif + + ecs_set_name_prefix(world, "Ecs"); + ECS_COMPONENT_DEFINE(world, EcsScript); + + ecs_set_hooks(world, EcsScript, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsScript), + .dtor = ecs_dtor(EcsScript) + }); + + ECS_COMPONENT(world, ecs_script_t); + + ecs_struct(world, { + .entity = ecs_id(ecs_script_t), + .members = { + { .name = "name", .type = ecs_id(ecs_string_t) }, + { .name = "code", .type = ecs_id(ecs_string_t) }, + { .name = "ast", .type = ecs_id(ecs_string_t) } + } + }); + + ecs_opaque(world, { + .entity = ecs_id(EcsScript), + .type.as_type = ecs_id(ecs_script_t), + .type.serialize = EcsScript_serialize + }); + + ecs_add_id(world, ecs_id(EcsScript), EcsPairIsTag); + ecs_add_id(world, ecs_id(EcsScript), EcsPrivate); + ecs_add_pair(world, ecs_id(EcsScript), EcsOnInstantiate, EcsDontInherit); } #endif /** - * @file json/serialize_type_info.c - * @brief Serialize type (reflection) information to JSON. + * @file addons/script/serialize.c + * @brief Serialize values to string. */ -#ifdef FLECS_JSON +#ifdef FLECS_SCRIPT static -int json_typeinfo_ser_type( +int flecs_expr_ser_type( const ecs_world_t *world, - ecs_entity_t type, - ecs_strbuf_t *buf); - -static -int json_typeinfo_ser_primitive( - ecs_primitive_kind_t kind, - ecs_strbuf_t *str) -{ - switch(kind) { - case EcsBool: - flecs_json_string(str, "bool"); - break; - case EcsChar: - case EcsString: - flecs_json_string(str, "text"); - break; - case EcsByte: - flecs_json_string(str, "byte"); - break; - case EcsU8: - case EcsU16: - case EcsU32: - case EcsU64: - case EcsI8: - case EcsI16: - case EcsI32: - case EcsI64: - case EcsIPtr: - case EcsUPtr: - flecs_json_string(str, "int"); - break; - case EcsF32: - case EcsF64: - flecs_json_string(str, "float"); - break; - case EcsEntity: - flecs_json_string(str, "entity"); - break; - case EcsId: - flecs_json_string(str, "id"); - break; - default: - return -1; - } - - return 0; -} + const ecs_vec_t *ser, + const void *base, + ecs_strbuf_t *str, + bool is_expr); static -void json_typeinfo_ser_constants( +int flecs_expr_ser_type_ops( const ecs_world_t *world, - ecs_entity_t type, - ecs_strbuf_t *str) -{ - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_pair(EcsChildOf, type) - }); - - while (ecs_term_next(&it)) { - int32_t i, count = it.count; - for (i = 0; i < count; i ++) { - flecs_json_next(str); - flecs_json_string(str, ecs_get_name(world, it.entities[i])); - } - } -} + ecs_meta_type_op_t *ops, + int32_t op_count, + const void *base, + ecs_strbuf_t *str, + int32_t in_array, + bool is_expr); static -void json_typeinfo_ser_enum( +int flecs_expr_ser_type_op( const ecs_world_t *world, - ecs_entity_t type, - ecs_strbuf_t *str) -{ - ecs_strbuf_list_appendstr(str, "\"enum\""); - json_typeinfo_ser_constants(world, type, str); -} + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str, + bool is_expr); static -void json_typeinfo_ser_bitmask( - const ecs_world_t *world, - ecs_entity_t type, - ecs_strbuf_t *str) -{ - ecs_strbuf_list_appendstr(str, "\"bitmask\""); - json_typeinfo_ser_constants(world, type, str); +ecs_primitive_kind_t flecs_expr_op_to_primitive_kind(ecs_meta_type_op_kind_t kind) { + return kind - EcsOpPrimitive; } +/* Serialize enumeration */ static -int json_typeinfo_ser_array( +int flecs_expr_ser_enum( const ecs_world_t *world, - ecs_entity_t elem_type, - int32_t count, - ecs_strbuf_t *str) + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str) { - ecs_strbuf_list_appendstr(str, "\"array\""); + const EcsEnum *enum_type = ecs_get(world, op->type, EcsEnum); + ecs_check(enum_type != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_json_next(str); - if (json_typeinfo_ser_type(world, elem_type, str)) { + int32_t val = *(const int32_t*)base; + + /* Enumeration constants are stored in a map that is keyed on the + * enumeration value. */ + ecs_enum_constant_t *c = ecs_map_get_deref(&enum_type->constants, + ecs_enum_constant_t, (ecs_map_key_t)val); + if (!c) { + char *path = ecs_get_path(world, op->type); + ecs_err("value %d is not valid for enum type '%s'", val, path); + ecs_os_free(path); goto error; } - ecs_strbuf_list_append(str, "%u", count); + ecs_strbuf_appendstr(str, ecs_get_name(world, c->constant)); + return 0; error: return -1; } +/* Serialize bitmask */ static -int json_typeinfo_ser_array_type( +int flecs_expr_ser_bitmask( const ecs_world_t *world, - ecs_entity_t type, - ecs_strbuf_t *str) + ecs_meta_type_op_t *op, + const void *ptr, + ecs_strbuf_t *str) { - const EcsArray *arr = ecs_get(world, type, EcsArray); - ecs_assert(arr != NULL, ECS_INTERNAL_ERROR, NULL); - if (json_typeinfo_ser_array(world, arr->type, arr->count, str)) { + const EcsBitmask *bitmask_type = ecs_get(world, op->type, EcsBitmask); + ecs_check(bitmask_type != NULL, ECS_INVALID_PARAMETER, NULL); + uint32_t value = *(const uint32_t*)ptr; + + ecs_strbuf_list_push(str, "", "|"); + + /* Multiple flags can be set at a given time. Iterate through all the flags + * and append the ones that are set. */ + ecs_map_iter_t it = ecs_map_iter(&bitmask_type->constants); + int count = 0; + while (ecs_map_next(&it)) { + ecs_bitmask_constant_t *c = ecs_map_ptr(&it); + ecs_map_key_t key = ecs_map_key(&it); + if ((value & key) == key) { + ecs_strbuf_list_appendstr(str, ecs_get_name(world, c->constant)); + count ++; + value -= (uint32_t)key; + } + } + + if (value != 0) { + /* All bits must have been matched by a constant */ + char *path = ecs_get_path(world, op->type); + ecs_err( + "value for bitmask %s contains bits (%u) that cannot be mapped to constant", + path, value); + ecs_os_free(path); goto error; } + if (!count) { + ecs_strbuf_list_appendstr(str, "0"); + } + + ecs_strbuf_list_pop(str, ""); + return 0; error: return -1; } +/* Serialize elements of a contiguous array */ static -int json_typeinfo_ser_vector( +int expr_ser_elements( const ecs_world_t *world, - ecs_entity_t type, - ecs_strbuf_t *str) + ecs_meta_type_op_t *ops, + int32_t op_count, + const void *base, + int32_t elem_count, + int32_t elem_size, + ecs_strbuf_t *str, + bool is_array) { - const EcsVector *arr = ecs_get(world, type, EcsVector); - ecs_assert(arr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_strbuf_list_push(str, "[", ", "); - ecs_strbuf_list_appendstr(str, "\"vector\""); + const void *ptr = base; - flecs_json_next(str); - if (json_typeinfo_ser_type(world, arr->type, str)) { - goto error; + int i; + for (i = 0; i < elem_count; i ++) { + ecs_strbuf_list_next(str); + if (flecs_expr_ser_type_ops( + world, ops, op_count, ptr, str, is_array, true)) + { + return -1; + } + ptr = ECS_OFFSET(ptr, elem_size); } + ecs_strbuf_list_pop(str, "]"); + return 0; -error: - return -1; } -/* Serialize unit information */ static -int json_typeinfo_ser_unit( +int expr_ser_type_elements( const ecs_world_t *world, + ecs_entity_t type, + const void *base, + int32_t elem_count, ecs_strbuf_t *str, - ecs_entity_t unit) + bool is_array) { - flecs_json_memberl(str, "unit"); - flecs_json_path(str, world, unit); + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); + ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); - const EcsUnit *uptr = ecs_get(world, unit, EcsUnit); - if (uptr) { - if (uptr->symbol) { - flecs_json_memberl(str, "symbol"); - flecs_json_string(str, uptr->symbol); - } - ecs_entity_t quantity = ecs_get_target(world, unit, EcsQuantity, 0); - if (quantity) { - flecs_json_memberl(str, "quantity"); - flecs_json_path(str, world, quantity); - } - } + const EcsComponent *comp = ecs_get(world, type, EcsComponent); + ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); - return 0; + ecs_meta_type_op_t *ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t); + int32_t op_count = ecs_vec_count(&ser->ops); + return expr_ser_elements( + world, ops, op_count, base, elem_count, comp->size, str, is_array); } +/* Serialize array */ static -void json_typeinfo_ser_range( - ecs_strbuf_t *str, - const char *kind, - ecs_member_value_range_t *range) +int expr_ser_array( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *ptr, + ecs_strbuf_t *str) { - flecs_json_member(str, kind); - flecs_json_array_push(str); - flecs_json_next(str); - flecs_json_number(str, range->min); - flecs_json_next(str); - flecs_json_number(str, range->max); - flecs_json_array_pop(str); + const EcsArray *a = ecs_get(world, op->type, EcsArray); + ecs_assert(a != NULL, ECS_INTERNAL_ERROR, NULL); + + return expr_ser_type_elements( + world, a->type, ptr, a->count, str, true); } -/* Forward serialization to the different type kinds */ +/* Serialize vector */ static -int json_typeinfo_ser_type_op( +int expr_ser_vector( const ecs_world_t *world, ecs_meta_type_op_t *op, - ecs_strbuf_t *str, - const EcsStruct *st) + const void *base, + 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); - } + const ecs_vec_t *value = base; + const EcsVector *v = ecs_get(world, op->type, EcsVector); + ecs_assert(v != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_json_array_push(str); + int32_t count = ecs_vec_count(value); + void *array = ecs_vec_first(value); + + /* Serialize contiguous buffer of vector */ + return expr_ser_type_elements(world, v->type, array, count, str, false); +} +/* Forward serialization to the different type kinds */ +static +int flecs_expr_ser_type_op( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *ptr, + ecs_strbuf_t *str, + bool is_expr) +{ switch(op->kind) { case EcsOpPush: case EcsOpPop: @@ -53594,21 +58655,27 @@ int json_typeinfo_ser_type_op( ecs_throw(ECS_INVALID_PARAMETER, NULL); break; case EcsOpEnum: - json_typeinfo_ser_enum(world, op->type, str); + if (flecs_expr_ser_enum(world, op, ECS_OFFSET(ptr, op->offset), str)) { + goto error; + } break; case EcsOpBitmask: - json_typeinfo_ser_bitmask(world, op->type, str); + if (flecs_expr_ser_bitmask(world, op, ECS_OFFSET(ptr, op->offset), str)) { + goto error; + } break; case EcsOpArray: - json_typeinfo_ser_array_type(world, op->type, str); + if (expr_ser_array(world, op, ECS_OFFSET(ptr, op->offset), str)) { + goto error; + } break; case EcsOpVector: - json_typeinfo_ser_vector(world, op->type, str); - break; - case EcsOpOpaque: - /* Can't happen, already handled above */ - ecs_throw(ECS_INTERNAL_ERROR, NULL); + if (expr_ser_vector(world, op, ECS_OFFSET(ptr, op->offset), str)) { + goto error; + } break; + case EcsOpScope: + case EcsOpPrimitive: case EcsOpBool: case EcsOpChar: case EcsOpByte: @@ -53627,10434 +58694,13235 @@ int json_typeinfo_ser_type_op( case EcsOpEntity: case EcsOpId: case EcsOpString: - if (json_typeinfo_ser_primitive( - flecs_json_op_to_primitive_kind(op->kind), str)) + case EcsOpOpaque: + if (flecs_expr_ser_primitive(world, flecs_expr_op_to_primitive_kind(op->kind), + ECS_OFFSET(ptr, op->offset), str, is_expr)) { - ecs_throw(ECS_INTERNAL_ERROR, NULL); + /* Unknown operation */ + ecs_err("unknown serializer operation kind (%d)", op->kind); + goto error; } break; - case EcsOpScope: - case EcsOpPrimitive: default: - ecs_throw(ECS_INTERNAL_ERROR, NULL); + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); } - if (st) { - ecs_member_t *m = ecs_vec_get_t( - &st->members, ecs_member_t, op->member_index); - ecs_assert(m != NULL, ECS_INTERNAL_ERROR, NULL); + return 0; +error: + return -1; +} + +/* Iterate over a slice of the type ops array */ +static +int flecs_expr_ser_type_ops( + const ecs_world_t *world, + ecs_meta_type_op_t *ops, + int32_t op_count, + const void *base, + ecs_strbuf_t *str, + int32_t in_array, + bool is_expr) +{ + for (int i = 0; i < op_count; i ++) { + ecs_meta_type_op_t *op = &ops[i]; + + if (in_array <= 0) { + if (op->name) { + ecs_strbuf_list_next(str); + ecs_strbuf_append(str, "%s: ", op->name); + } + + int32_t elem_count = op->count; + if (elem_count > 1) { + /* Serialize inline array */ + if (expr_ser_elements(world, op, op->op_count, base, + elem_count, op->size, str, true)) + { + return -1; + } + + i += op->op_count - 1; + continue; + } + } + + switch(op->kind) { + case EcsOpPush: + ecs_strbuf_list_push(str, "{", ", "); + in_array --; + break; + case EcsOpPop: + ecs_strbuf_list_pop(str, "}"); + in_array ++; + break; + case EcsOpArray: + case EcsOpVector: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpScope: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpEntity: + case EcsOpId: + case EcsOpString: + case EcsOpOpaque: + if (flecs_expr_ser_type_op(world, op, base, str, is_expr)) { + goto error; + } + break; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + } + } + + return 0; +error: + return -1; +} + +/* Iterate over the type ops of a type */ +static +int flecs_expr_ser_type( + const ecs_world_t *world, + const ecs_vec_t *v_ops, + const void *base, + ecs_strbuf_t *str, + bool is_expr) +{ + ecs_meta_type_op_t *ops = ecs_vec_first_t(v_ops, ecs_meta_type_op_t); + int32_t count = ecs_vec_count(v_ops); + return flecs_expr_ser_type_ops(world, ops, count, base, str, 0, is_expr); +} + +int ecs_ptr_to_expr_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *ptr, + ecs_strbuf_t *buf_out) +{ + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); + if (ser == NULL) { + char *path = ecs_get_path(world, type); + ecs_err("cannot serialize value for type '%s'", path); + ecs_os_free(path); + goto error; + } + + if (flecs_expr_ser_type(world, &ser->ops, ptr, buf_out, true)) { + goto error; + } + + return 0; +error: + return -1; +} + +char* ecs_ptr_to_expr( + const ecs_world_t *world, + ecs_entity_t type, + const void* ptr) +{ + ecs_strbuf_t str = ECS_STRBUF_INIT; + + if (ecs_ptr_to_expr_buf(world, type, ptr, &str) != 0) { + ecs_strbuf_reset(&str); + return NULL; + } + + return ecs_strbuf_get(&str); +} + +int ecs_ptr_to_str_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *ptr, + ecs_strbuf_t *buf_out) +{ + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); + if (ser == NULL) { + char *path = ecs_get_path(world, type); + ecs_err("cannot serialize value for type '%s'", path); + ecs_os_free(path); + goto error; + } + + if (flecs_expr_ser_type(world, &ser->ops, ptr, buf_out, false)) { + goto error; + } + + return 0; +error: + return -1; +} + +char* ecs_ptr_to_str( + const ecs_world_t *world, + ecs_entity_t type, + const void* ptr) +{ + ecs_strbuf_t str = ECS_STRBUF_INIT; + + if (ecs_ptr_to_str_buf(world, type, ptr, &str) != 0) { + ecs_strbuf_reset(&str); + return NULL; + } + + return ecs_strbuf_get(&str); +} + +#endif + +/** + * @file addons/script/template.c + * @brief Script template implementation. + */ + + +#ifdef FLECS_SCRIPT + +/* Template ctor to initialize with default property values */ +static +void flecs_script_template_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *ti) +{ + ecs_world_t *world = ti->hooks.ctx; + ecs_entity_t template_entity = ti->component; + + const EcsStruct *st = ecs_get(world, template_entity, EcsStruct); + if (!st) { + ecs_os_memset(ptr, 0, count * ti->size); + return; + } + + const EcsScript *script = ecs_get(world, template_entity, EcsScript); + if (!script) { + ecs_err("template '%s' is not a script, cannot construct", ti->name); + return; + } + + ecs_script_template_t *template = script->template_; + ecs_assert(template != NULL, ECS_INTERNAL_ERROR, NULL); + if (st->members.count != template->prop_defaults.count) { + ecs_err("number of props (%d) of template '%s' does not match members" + " (%d), cannot construct", template->prop_defaults.count, + ti->name, st->members.count); + return; + } + + const ecs_member_t *members = st->members.array; + int32_t i, m, member_count = st->members.count; + ecs_script_var_t *values = template->prop_defaults.array; + for (m = 0; m < member_count; m ++) { + const ecs_member_t *member = &members[m]; + ecs_script_var_t *value = &values[m]; + const ecs_type_info_t *mti = value->type_info; + ecs_assert(mti != NULL, ECS_INTERNAL_ERROR, NULL); + + for (i = 0; i < count; i ++) { + void *el = ECS_ELEM(ptr, ti->size, i); + ecs_value_copy_w_type_info(world, mti, + ECS_OFFSET(el, member->offset), value->value.ptr); + } + } +} + +/* Template on_set handler to update contents for new property values */ +static +void flecs_script_template_on_set( + ecs_iter_t *it) +{ + if (it->table->flags & EcsTableIsPrefab) { + /* Don't instantiate templates for prefabs */ + return; + } + + ecs_world_t *world = it->world; + ecs_entity_t template_entity = ecs_field_id(it, 0); + ecs_record_t *r = ecs_record_find(world, template_entity); + if (!r) { + ecs_err("template entity is empty (should never happen)"); + return; + } + + const EcsScript *script = ecs_record_get(world, r, EcsScript); + if (!script) { + ecs_err("template is missing script component"); + return; + } + + ecs_script_template_t *template = script->template_; + ecs_assert(template != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *ti = template->type_info; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + const EcsStruct *st = ecs_record_get(world, r, EcsStruct); + + void *data = ecs_field_w_size(it, flecs_ito(size_t, ti->size), 0); + + ecs_script_eval_visitor_t v; + flecs_script_eval_visit_init(flecs_script_impl(script->script), &v); + ecs_vec_t prev_using = v.using; + v.using = template->using_; + + ecs_script_scope_t *scope = template->node->scope; + + /* Dummy entity node for instance */ + ecs_script_entity_t instance_node = { + .node = { + .kind = EcsAstEntity, + .pos = template->node->node.pos + }, + .scope = scope + }; + + v.entity = &instance_node; + + int32_t i, m; + for (i = 0; i < it->count; i ++) { + v.parent = it->entities[i]; + instance_node.eval = it->entities[i]; - bool value_range = ECS_NEQ(m->range.min, m->range.max); - bool error_range = ECS_NEQ(m->error_range.min, m->error_range.max); - bool warning_range = ECS_NEQ(m->warning_range.min, m->warning_range.max); + /* Create variables to hold template properties */ + ecs_script_vars_t *vars = flecs_script_vars_push( + NULL, &v.stack, v.allocator); + vars->parent = template->vars; /* Include hoisted variables */ - ecs_entity_t unit = m->unit; - if (unit || error_range || warning_range || value_range) { - flecs_json_next(str); - flecs_json_next(str); - flecs_json_object_push(str); + /* Populate properties from template members */ + if (st) { + const ecs_member_t *members = st->members.array; + for (m = 0; m < st->members.count; m ++) { + const ecs_member_t *member = &members[m]; - if (unit) { - json_typeinfo_ser_unit(world, str, unit); - } - if (value_range) { - json_typeinfo_ser_range(str, "range", &m->range); - } - if (error_range) { - json_typeinfo_ser_range(str, "error_range", &m->error_range); - } - if (warning_range) { - json_typeinfo_ser_range(str, "warning_range", &m->warning_range); + /* Assign template property from template instance */ + ecs_script_var_t *var = ecs_script_vars_declare(vars, member->name); + var->value.type = member->type; + var->value.ptr = ECS_OFFSET(data, member->offset); } + } - flecs_json_object_pop(str); + /* Populate $this variable with instance entity */ + ecs_entity_t instance = it->entities[i]; + ecs_script_var_t *var = ecs_script_vars_declare(vars, "this"); + var->value.type = ecs_id(ecs_entity_t); + var->value.ptr = &instance; + + bool is_defer = ecs_is_deferred(world); + ecs_suspend_readonly_state_t srs; + ecs_world_t *real_world = NULL; + if (is_defer) { + ecs_assert(flecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL); + real_world = flecs_suspend_readonly(world, &srs); + ecs_assert(real_world != NULL, ECS_INTERNAL_ERROR, NULL); } - } - flecs_json_array_pop(str); + ecs_script_clear(world, template_entity, instance); - return 0; -error: - return -1; + /* Run template code */ + v.vars = vars; + ecs_script_visit_scope(&v, scope); + + if (is_defer) { + flecs_resume_readonly(real_world, &srs); + } + + /* Pop variable scope */ + ecs_script_vars_pop(vars); + + data = ECS_OFFSET(data, ti->size); + } + + v.using = prev_using; + flecs_script_eval_visit_fini(&v); } -/* Iterate over a slice of the type ops array */ static -int json_typeinfo_ser_type_ops( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - ecs_strbuf_t *str, - const EcsStruct *st) +int flecs_script_template_eval_prop( + ecs_script_eval_visitor_t *v, + ecs_script_var_node_t *node) { - const EcsStruct *stack[64] = {st}; - int32_t sp = 1; + ecs_script_template_t *template = v->template; + ecs_script_var_t *var = ecs_script_vars_declare(v->vars, node->name); + if (!var) { + flecs_script_eval_error(v, node, + "variable '%s' redeclared", node->name); + return -1; + } - for (int i = 0; i < op_count; i ++) { - ecs_meta_type_op_t *op = &ops[i]; + if (node->type) { + ecs_entity_t type = flecs_script_find_entity(v, 0, node->type); + if (!type) { + flecs_script_eval_error(v, node, + "unresolved type '%s' for const variable '%s'", + node->type, node->name); + return -1; + } - if (op != ops) { - if (op->name) { - flecs_json_member(str, op->name); - } + const ecs_type_info_t *ti = flecs_script_get_type_info(v, node, type); + if (!ti) { + return -1; } - 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; + var->value.type = type; + var->value.ptr = flecs_stack_alloc(&v->stack, ti->size, ti->alignment); + var->type_info = ti; + + if (flecs_script_eval_expr(v, node->expr, &var->value)) { + return -1; } - switch(op->kind) { - case EcsOpPush: - flecs_json_object_push(str); - ecs_assert(sp < 63, ECS_INVALID_OPERATION, "type nesting too deep"); - stack[sp ++] = ecs_get(world, op->type, EcsStruct); - break; - case EcsOpPop: - flecs_json_object_pop(str); - sp --; - break; - case EcsOpArray: - case EcsOpVector: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpEntity: - case EcsOpId: - case EcsOpString: - case EcsOpOpaque: - if (json_typeinfo_ser_type_op(world, op, str, stack[sp - 1])) { - goto error; + ecs_script_var_t *value = ecs_vec_append_t(&v->base.script->allocator, + &template->prop_defaults, ecs_script_var_t); + value->value.ptr = flecs_calloc(&v->base.script->allocator, ti->size); + value->value.type = type; + value->type_info = ti; + ecs_value_copy_w_type_info( + v->world, ti, value->value.ptr, var->value.ptr); + + ecs_entity_t mbr = ecs_entity(v->world, { + .name = node->name, + .parent = template->entity + }); + + ecs_set(v->world, mbr, EcsMember, { .type = var->value.type }); + } + + return 0; +} + +static +int flecs_script_template_eval( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node) +{ + switch(node->kind) { + case EcsAstScope: + case EcsAstTag: + case EcsAstComponent: + case EcsAstVarComponent: + case EcsAstDefaultComponent: + case EcsAstWithVar: + case EcsAstWithTag: + case EcsAstWithComponent: + case EcsAstUsing: + case EcsAstModule: + case EcsAstAnnotation: + case EcsAstConst: + case EcsAstEntity: + case EcsAstPairScope: + case EcsAstTemplate: + break; + case EcsAstProp: + return flecs_script_template_eval_prop(v, (ecs_script_var_node_t*)node); + case EcsAstWith: + if (ecs_script_visit_scope(v, ((ecs_script_with_t*)node)->expressions)) { + return -1; + } + if (ecs_script_visit_scope(v, ((ecs_script_with_t*)node)->scope)) { + return -1; + } + return 0; + case EcsAstIf: + if (ecs_script_visit_scope(v, ((ecs_script_if_t*)node)->if_true)) { + return -1; + } + if (((ecs_script_if_t*)node)->if_false) { + if (ecs_script_visit_scope(v, ((ecs_script_if_t*)node)->if_false)) { + return -1; } - break; - case EcsOpPrimitive: - case EcsOpScope: - default: - ecs_throw(ECS_INTERNAL_ERROR, NULL); } + return 0; } - return 0; -error: - return -1; + return flecs_script_eval_node(v, node); } static -int json_typeinfo_ser_type( - const ecs_world_t *world, - ecs_entity_t type, - ecs_strbuf_t *buf) +int flecs_script_template_preprocess( + ecs_script_eval_visitor_t *v, + ecs_script_template_t *template) { - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - if (!comp) { - ecs_strbuf_appendch(buf, '0'); - return 0; + ecs_visit_action_t prev_visit = v->base.visit; + v->template = template; + v->base.visit = (ecs_visit_action_t)flecs_script_template_eval; + v->vars = flecs_script_vars_push(v->vars, &v->stack, v->allocator); + int result = ecs_script_visit_scope(v, template->node->scope); + v->vars = ecs_script_vars_pop(v->vars); + v->base.visit = prev_visit; + v->template = NULL; + return result; +} + +static +int flecs_script_template_hoist_using( + ecs_script_eval_visitor_t *v, + ecs_script_template_t *template) +{ + if (v->module) { + ecs_vec_append_t( + v->allocator, &template->using_, ecs_entity_t)[0] = v->module; } - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - if (!ser) { - ecs_strbuf_appendch(buf, '0'); - return 0; + int i, count = ecs_vec_count(&v->using); + for (i = 0; i < count; i ++) { + ecs_vec_append_t(v->allocator, &template->using_, ecs_entity_t)[0] = + ecs_vec_get_t(&v->using, ecs_entity_t, i)[0]; } - const EcsStruct *st = ecs_get(world, type, EcsStruct); - ecs_meta_type_op_t *ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t); - int32_t count = ecs_vec_count(&ser->ops); + return 0; +} + +static +int flecs_script_template_hoist_vars( + ecs_script_eval_visitor_t *v, + ecs_script_template_t *template, + ecs_script_vars_t *vars) +{ + if (vars->parent) { + flecs_script_template_hoist_vars(v, template, vars); + } + + int32_t i, count = ecs_vec_count(&vars->vars); + ecs_script_var_t *src_vars = ecs_vec_first(&vars->vars); + for (i = 0; i < count; i ++) { + ecs_script_var_t *src = &src_vars[i]; + ecs_script_var_t *dst = ecs_script_vars_define_id( + template->vars, src->name, src->value.type); + ecs_value_copy(v->world, + src->value.type, dst->value.ptr, src->value.ptr); + } - return json_typeinfo_ser_type_ops(world, ops, count, buf, st); + return 0; } -int ecs_type_info_to_json_buf( - const ecs_world_t *world, - ecs_entity_t type, - ecs_strbuf_t *buf) +ecs_script_template_t* flecs_script_template_init( + ecs_script_impl_t *script) { - return json_typeinfo_ser_type(world, type, buf); + ecs_allocator_t *a = &script->allocator; + ecs_script_template_t *result = flecs_alloc_t(a, ecs_script_template_t); + ecs_vec_init_t(NULL, &result->prop_defaults, ecs_script_var_t, 0); + ecs_vec_init_t(NULL, &result->using_, ecs_entity_t, 0); + result->vars = ecs_script_vars_init(script->pub.world); + return result; } -char* ecs_type_info_to_json( - const ecs_world_t *world, - ecs_entity_t type) +void flecs_script_template_fini( + ecs_script_impl_t *script, + ecs_script_template_t *template) { - ecs_strbuf_t str = ECS_STRBUF_INIT; + ecs_assert(script != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_allocator_t *a = &script->allocator; - if (ecs_type_info_to_json_buf(world, type, &str) != 0) { - ecs_strbuf_reset(&str); - return NULL; + int32_t i, count = ecs_vec_count(&template->prop_defaults); + ecs_script_var_t *values = ecs_vec_first(&template->prop_defaults); + for (i = 0; i < count; i ++) { + ecs_script_var_t *value = &values[i]; + const ecs_type_info_t *ti = value->type_info; + if (ti->hooks.dtor) { + ti->hooks.dtor(value->value.ptr, 1, ti); + } + flecs_free(a, ti->size, value->value.ptr); } - return ecs_strbuf_get(&str); + ecs_vec_fini_t(a, &template->prop_defaults, ecs_script_var_t); + ecs_vec_fini_t(a, &template->using_, ecs_entity_t); + ecs_script_vars_fini(template->vars); + flecs_free_t(a, ecs_script_template_t, template); } -#endif +/* Create new template */ +int flecs_script_eval_template( + ecs_script_eval_visitor_t *v, + ecs_script_template_node_t *node) +{ + if (v->template) { + flecs_script_eval_error(v, node, "nested templates are not allowed"); + return -1; + } -/** - * @file meta/api.c - * @brief API for creating entities with reflection data. - */ + ecs_entity_t template_entity = flecs_script_create_entity(v, node->name); + if (!template_entity) { + return -1; + } -/** - * @file meta/meta.h - * @brief Private functions for meta addon. - */ + ecs_script_template_t *template = flecs_script_template_init(v->base.script); + template->entity = template_entity; + template->node = node; -#ifndef FLECS_META_PRIVATE_H -#define FLECS_META_PRIVATE_H + if (flecs_script_template_preprocess(v, template)) { + goto error; + } + if (flecs_script_template_hoist_using(v, template)) { + goto error; + } -#ifdef FLECS_META + if (flecs_script_template_hoist_vars(v, template, v->vars)) { + goto error; + } -void ecs_meta_type_serialized_init( - ecs_iter_t *it); + /* If template has no props, give template dummy size so we can register + * hooks for it. */ + if (!ecs_has(v->world, template_entity, EcsComponent)) { + ecs_set(v->world, template_entity, EcsComponent, {1, 1}); + } -void ecs_meta_dtor_serialized( - EcsMetaTypeSerialized *ptr); + template->type_info = ecs_get_type_info(v->world, template_entity); -ecs_meta_type_op_kind_t flecs_meta_primitive_to_op_kind( - ecs_primitive_kind_t kind); + ecs_add_pair(v->world, template_entity, EcsOnInstantiate, EcsOverride); -bool flecs_unit_validate( - ecs_world_t *world, - ecs_entity_t t, - EcsUnit *data); + EcsScript *script = ecs_ensure(v->world, template_entity, EcsScript); + if (script->script) { + if (script->template_) { + flecs_script_template_fini( + flecs_script_impl(script->script), script->template_); + } + ecs_script_free(script->script); + } + + script->script = &v->base.script->pub; + script->template_ = template; + ecs_modified(v->world, template_entity, EcsScript); + + ecs_set_hooks_id(v->world, template_entity, &(ecs_type_hooks_t) { + .ctor = flecs_script_template_ctor, + .on_set = flecs_script_template_on_set, + .ctx = v->world + }); + + /* Keep script alive for as long as template is alive */ + v->base.script->refcount ++; + + return 0; +error: + flecs_script_template_fini(v->base.script, template); + return -1; +} -#endif - #endif +/** + * @file addons/script/tokenizer.c + * @brief Script tokenizer. + */ -#ifdef FLECS_META -static -bool flecs_type_is_number( - ecs_world_t *world, - ecs_entity_t type) +#ifdef FLECS_SCRIPT + +#define Keyword(keyword, _kind)\ + } else if (!ecs_os_strncmp(pos, keyword " ", ecs_os_strlen(keyword) + 1)) {\ + out->value = keyword;\ + out->kind = _kind;\ + return pos + ecs_os_strlen(keyword); + +#define OperatorMultiChar(oper, _kind)\ + } else if (!ecs_os_strncmp(pos, oper, ecs_os_strlen(oper))) {\ + out->value = oper;\ + out->kind = _kind;\ + return pos + ecs_os_strlen(oper); + +#define Operator(oper, _kind)\ + } else if (pos[0] == oper[0]) {\ + out->value = oper;\ + out->kind = _kind;\ + return pos + 1; + +const char* flecs_script_token_kind_str( + ecs_script_token_kind_t kind) { - const EcsPrimitive *p = ecs_get(world, type, EcsPrimitive); - if (!p) { - return false; + switch(kind) { + case EcsTokUnknown: + return "unknown token "; + case EcsTokColon: + case EcsTokScopeOpen: + case EcsTokScopeClose: + case EcsTokParenOpen: + case EcsTokParenClose: + case EcsTokBracketOpen: + case EcsTokBracketClose: + case EcsTokAnnotation: + case EcsTokComma: + case EcsTokSemiColon: + case EcsTokMul: + case EcsTokAssign: + case EcsTokBitwiseOr: + case EcsTokNot: + case EcsTokOptional: + case EcsTokEq: + case EcsTokNeq: + case EcsTokMatch: + case EcsTokOr: + return ""; + case EcsTokKeywordWith: + case EcsTokKeywordUsing: + case EcsTokKeywordTemplate: + case EcsTokKeywordProp: + case EcsTokKeywordConst: + case EcsTokKeywordIf: + case EcsTokKeywordElse: + case EcsTokKeywordModule: + return "keyword "; + case EcsTokIdentifier: + return "identifier "; + case EcsTokString: + return "string "; + case EcsTokNumber: + return "number "; + case EcsTokNewline: + return "newline"; + case EcsTokEnd: + return "end of script"; + default: + return ""; } +} - switch(p->kind) { - case EcsChar: - case EcsU8: - case EcsU16: - case EcsU32: - case EcsU64: - case EcsI8: - case EcsI16: - case EcsI32: - case EcsI64: - case EcsF32: - case EcsF64: - return true; +const char* flecs_scan_whitespace( + ecs_script_parser_t *parser, + const char *pos) +{ + (void)parser; - case EcsBool: - case EcsByte: - case EcsUPtr: - case EcsIPtr: - case EcsString: - case EcsEntity: - case EcsId: - return false; - default: - ecs_abort(ECS_INVALID_PARAMETER, NULL); + if (parser->significant_newline) { + while (pos[0] && isspace(pos[0]) && pos[0] != '\n') { + pos ++; + } + } else { + while (pos[0] && isspace(pos[0])) { + pos ++; + } } + + return pos; } -ecs_entity_t ecs_primitive_init( - ecs_world_t *world, - const ecs_primitive_desc_t *desc) +static +const char* flecs_scan_whitespace_and_comment( + ecs_script_parser_t *parser, + const char *pos) { - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); +repeat_skip_whitespace_comment: + pos = flecs_scan_whitespace(parser, pos); + if (pos[0] == '/') { + if (pos[1] == '/') { + for (pos = pos + 2; pos[0] && pos[0] != '\n'; pos ++) { } + if (pos[0] == '\n') { + pos ++; + goto repeat_skip_whitespace_comment; + } + } else if (pos[1] == '*') { + for (pos = &pos[2]; pos[0] != 0; pos ++) { + if (pos[0] == '*' && pos[1] == '/') { + pos += 2; + goto repeat_skip_whitespace_comment; + } + } - ecs_entity_t t = desc->entity; - if (!t) { - t = ecs_new_low_id(world); + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "missing */ for multiline comment"); + } } - ecs_set(world, t, EcsPrimitive, { desc->kind }); + return pos; +} - flecs_resume_readonly(world, &rs); - return t; +// Identifier token +static +bool flecs_script_is_identifier( + char c) +{ + return isalpha(c) || (c == '_') || (c == '$') || (c == '#'); } -ecs_entity_t ecs_enum_init( - ecs_world_t *world, - const ecs_enum_desc_t *desc) +static +const char* flecs_script_identifier( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out) { - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); + out->kind = EcsTokIdentifier; + out->value = parser->token_cur; - ecs_entity_t t = desc->entity; - if (!t) { - t = ecs_new_low_id(world); - } + ecs_assert(flecs_script_is_identifier(pos[0]), ECS_INTERNAL_ERROR, NULL); + char *outpos = parser->token_cur; + do { + char c = pos[0]; + bool is_ident = flecs_script_is_identifier(c) || + isdigit(c) || (c == '.') || (c == '*'); - ecs_add(world, t, EcsEnum); + /* Retain \. for name lookup operation */ + if (!is_ident && c == '\\' && pos[1] == '.') { + is_ident = true; + } - ecs_entity_t old_scope = ecs_set_scope(world, t); + if (!is_ident) { + if (c == '\\') { + pos ++; + } else if (c == '<') { + int32_t indent = 0; + do { + c = *pos; + + if (c == '<') { + indent ++; + } else if (c == '>') { + indent --; + } else if (!c) { + ecs_parser_error(parser->script->pub.name, + parser->script->pub.code, pos - parser->script->pub.code, + "< without > in identifier"); + return NULL; + } - int i; - for (i = 0; i < ECS_MEMBER_DESC_CACHE_SIZE; i ++) { - const ecs_enum_constant_t *m_desc = &desc->constants[i]; - if (!m_desc->name) { + *outpos = c; + outpos ++; + pos ++; + + if (!indent) { + break; + } + } while (true); + + *outpos = '\0'; + parser->token_cur = outpos + 1; + return pos; + } else if (c == '>') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "> without < in identifier"); + return NULL; + } else { + *outpos = '\0'; + parser->token_cur = outpos + 1; + return pos; + } + } + + *outpos = *pos; + outpos ++; + pos ++; + } while (true); +} + +// Number token static +static +bool flecs_script_is_number( + char c) +{ + return isdigit(c) || (c == '-'); +} + +static +const char* flecs_script_number( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out) +{ + out->kind = EcsTokNumber; + out->value = parser->token_cur; + + ecs_assert(flecs_script_is_number(pos[0]), ECS_INTERNAL_ERROR, NULL); + char *outpos = parser->token_cur; + do { + char c = pos[0]; + if (!isdigit(c)) { + *outpos = '\0'; + parser->token_cur = outpos + 1; break; } - ecs_entity_t c = ecs_entity(world, { - .name = m_desc->name - }); + outpos[0] = pos[0]; + outpos ++; + pos ++; + } while (true); - if (!m_desc->value) { - ecs_add_id(world, c, EcsConstant); - } else { - ecs_set_pair_object(world, c, EcsConstant, ecs_i32_t, - {m_desc->value}); + return pos; +} + +static +const char* flecs_script_skip_string( + ecs_script_parser_t *parser, + const char *pos, + char delim) +{ + char ch; + for (; (ch = pos[0]) && pos[0] != delim; pos ++) { + if (ch == '\\') { + pos ++; } } - ecs_set_scope(world, old_scope); - flecs_resume_readonly(world, &rs); + if (!pos[0]) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "unterminated string"); + return NULL; + } - if (i == 0) { - ecs_err("enum '%s' has no constants", ecs_get_name(world, t)); - ecs_delete(world, t); - return 0; + return pos; +} + +static +const char* flecs_script_string( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out) +{ + const char *end = flecs_script_skip_string(parser, pos + 1, '"'); + if (!end) { + return NULL; } - return t; + ecs_assert(end[0] == '"', ECS_INTERNAL_ERROR, NULL); + end --; + + int32_t len = flecs_ito(int32_t, end - pos); + ecs_os_memcpy(parser->token_cur, pos + 1, len); + parser->token_cur[len] = '\0'; + + out->kind = EcsTokString; + out->value = parser->token_cur; + parser->token_cur += len + 1; + return end + 2; } -ecs_entity_t ecs_bitmask_init( - ecs_world_t *world, - const ecs_bitmask_desc_t *desc) +const char* flecs_script_expr( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out, + char until) { - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); + parser->pos = pos; - ecs_entity_t t = desc->entity; - if (!t) { - t = ecs_new_low_id(world); + int32_t scope_depth = until == '}' ? 1 : 0; + int32_t paren_depth = until == ')' ? 1 : 0; + + const char *start = pos = flecs_scan_whitespace(parser, pos); + char ch; + + for (; (ch = pos[0]); pos ++) { + if (ch == '{') { + if (ch == until) { + break; + } + scope_depth ++; + } else + if (ch == '}') { + scope_depth --; + if (!scope_depth && until == '}') { + break; + } + if (scope_depth < 0) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "mismatching { }"); + return NULL; + } + } else + if (ch == '(') { + paren_depth ++; + } else + if (ch == ')') { + paren_depth --; + if (!paren_depth && until == ')') { + break; + } + if (paren_depth < 0) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "mismatching ( )"); + return NULL; + } + } else + if (ch == '"') { + pos = flecs_script_skip_string(parser, pos + 1, '"'); + if (!pos) { + return NULL; + } + } else + if (ch == '`') { + pos = flecs_script_skip_string(parser, pos + 1, '`'); + if (!pos) { + return NULL; + } + } else + if (ch == until) { + break; + } } - ecs_add(world, t, EcsBitmask); + if (!pos[0]) { + if (until == '\0') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected end of script"); + return NULL; + } else + if (until == '\n') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected newline"); + return NULL; + } else { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected '%c'", until); + return NULL; + } + } - ecs_entity_t old_scope = ecs_set_scope(world, t); + if (scope_depth) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "mismatching { }"); + return NULL; + } + if (paren_depth) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "mismatching ( )"); + return NULL; + } - int i; - for (i = 0; i < ECS_MEMBER_DESC_CACHE_SIZE; i ++) { - const ecs_bitmask_constant_t *m_desc = &desc->constants[i]; - if (!m_desc->name) { + if (until != ']') { + parser->token_cur[0] = '{'; + } else { + parser->token_cur[0] = '['; + } + + int32_t len = flecs_ito(int32_t, pos - start); + ecs_os_memcpy(parser->token_cur + 1, start, len); + out->value = parser->token_cur; + parser->token_cur += len + 1; + + while (isspace(parser->token_cur[-1])) { + parser->token_cur --; + } + + if (until != ']') { + parser->token_cur[0] = '}'; + } else { + parser->token_cur[0] = ']'; + } + + parser->token_cur ++; + + parser->token_cur[0] = '\0'; + parser->token_cur ++; + + return pos; +} + +const char* flecs_script_until( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out, + char until) +{ + parser->pos = pos; + + const char *start = pos = flecs_scan_whitespace(parser, pos); + char ch; + + for (; (ch = pos[0]); pos ++) { + if (ch == until) { break; } + } - ecs_entity_t c = ecs_entity(world, { - .name = m_desc->name - }); - - if (!m_desc->value) { - ecs_add_id(world, c, EcsConstant); + if (!pos[0]) { + if (until == '\0') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected end of script"); + return NULL; + } else + if (until == '\n') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected newline"); + return NULL; } else { - ecs_set_pair_object(world, c, EcsConstant, ecs_u32_t, - {m_desc->value}); + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected '%c'", until); + return NULL; } } - ecs_set_scope(world, old_scope); - flecs_resume_readonly(world, &rs); + int32_t len = flecs_ito(int32_t, pos - start); + ecs_os_memcpy(parser->token_cur, start, len); + out->value = parser->token_cur; + parser->token_cur += len; - if (i == 0) { - ecs_err("bitmask '%s' has no constants", ecs_get_name(world, t)); - ecs_delete(world, t); - return 0; + while (isspace(parser->token_cur[-1])) { + parser->token_cur --; } - return t; + parser->token_cur[0] = '\0'; + parser->token_cur ++; + + return pos; } -ecs_entity_t ecs_array_init( - ecs_world_t *world, - const ecs_array_desc_t *desc) +const char* flecs_script_token( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out, + bool is_lookahead) { - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); + parser->pos = pos; - ecs_entity_t t = desc->entity; - if (!t) { - t = ecs_new_low_id(world); + // Skip whitespace and comments + pos = flecs_scan_whitespace_and_comment(parser, pos); + + out->kind = EcsTokUnknown; + out->value = NULL; + + if (pos[0] == '\0') { + out->kind = EcsTokEnd; + return pos; + } else if (pos[0] == '\n') { + out->kind = EcsTokNewline; + + // Parse multiple newlines/whitespaces as a single token + pos = flecs_scan_whitespace_and_comment(parser, pos + 1); + if (pos[0] == '\n') { + pos ++; + } + return pos; + + Operator (":", EcsTokColon) + Operator ("{", EcsTokScopeOpen) + Operator ("}", EcsTokScopeClose) + Operator ("(", EcsTokParenOpen) + Operator (")", EcsTokParenClose) + Operator ("[", EcsTokBracketOpen) + Operator ("]", EcsTokBracketClose) + Operator ("@", EcsTokAnnotation) + Operator (",", EcsTokComma) + Operator (";", EcsTokSemiColon) + Operator ("*", EcsTokMul) + Operator ("?", EcsTokOptional) + + OperatorMultiChar ("==", EcsTokEq) + OperatorMultiChar ("!=", EcsTokNeq) + OperatorMultiChar ("~=", EcsTokMatch) + OperatorMultiChar ("||", EcsTokOr) + + OperatorMultiChar ("!", EcsTokNot) + OperatorMultiChar ("=", EcsTokAssign) + OperatorMultiChar ("|", EcsTokBitwiseOr) + + Keyword ("with", EcsTokKeywordWith) + Keyword ("using", EcsTokKeywordUsing) + Keyword ("template", EcsTokKeywordTemplate) + Keyword ("prop", EcsTokKeywordProp) + Keyword ("const", EcsTokKeywordConst) + Keyword ("if", EcsTokKeywordIf) + Keyword ("else", EcsTokKeywordElse) + Keyword ("module", EcsTokKeywordModule) + + } else if (pos[0] == '"') { + return flecs_script_string(parser, pos, out); + + } else if (flecs_script_is_number(pos[0])) { + return flecs_script_number(parser, pos, out); + + } else if (flecs_script_is_identifier(pos[0])) { + return flecs_script_identifier(parser, pos, out); + } + + if (!is_lookahead) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "unknown token '%c'", pos[0]); } - ecs_set(world, t, EcsArray, { - .type = desc->type, - .count = desc->count - }); + return NULL; +} + +#endif + +/** + * @file addons/script/vars.c + * @brief Script variables. + */ + + +#ifdef FLECS_SCRIPT + +ecs_script_vars_t* flecs_script_vars_push( + ecs_script_vars_t *parent, + ecs_stack_t *stack, + ecs_allocator_t *allocator) +{ + ecs_check(stack || parent, ECS_INVALID_PARAMETER, + "must provide either parent scope or stack allocator"); + ecs_check(allocator || parent, ECS_INVALID_PARAMETER, + "must provide either parent scope or allocator"); + + if (!stack) { + stack = parent->stack; + } else if (parent) { + ecs_check(stack == parent->stack, ECS_INVALID_PARAMETER, + "provided stack allocator is different from parent scope"); + } + if (!allocator) { + allocator = parent->allocator; + } else if (parent) { + ecs_check(allocator == parent->allocator, ECS_INVALID_PARAMETER, + "provided allocator is different from parent scope"); + } + + ecs_stack_cursor_t *cursor = flecs_stack_get_cursor(stack); + ecs_script_vars_t *result = flecs_stack_calloc_t(stack, ecs_script_vars_t); + ecs_vec_init_t(allocator, &result->vars, ecs_script_var_t, 0); + result->parent = parent; + if (parent) { + result->world = parent->world; + } + result->stack = stack; + result->allocator = allocator; + result->cursor = cursor; + return result; +error: + return NULL; +} + +ecs_script_vars_t* ecs_script_vars_init( + ecs_world_t *world) +{ + ecs_script_vars_t *result = flecs_script_vars_push(NULL, + flecs_stage_get_stack_allocator(world), + flecs_stage_get_allocator(world)); + result->world = ecs_get_world(world); /* Provided world can be stage */ + return result; +} + +void ecs_script_vars_fini( + ecs_script_vars_t *vars) +{ + ecs_check(vars->parent == NULL, ECS_INVALID_PARAMETER, + "ecs_script_vars_fini can only be called on the roots cope"); + ecs_script_vars_pop(vars); +error: + return; +} + +ecs_script_vars_t* ecs_script_vars_push( + ecs_script_vars_t *parent) +{ + ecs_check(parent != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stack_t *stack = parent->stack; + ecs_allocator_t *allocator = parent->allocator; + ecs_check(stack != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(allocator != NULL, ECS_INVALID_PARAMETER, NULL); + return flecs_script_vars_push(parent, stack, allocator); +error: + return NULL; +} + +ecs_script_vars_t* ecs_script_vars_pop( + ecs_script_vars_t *vars) +{ + ecs_script_vars_t *parent = vars->parent; + ecs_stack_cursor_t *cursor = vars->cursor; + int32_t i, count = ecs_vec_count(&vars->vars); + if (count) { + ecs_script_var_t *var_array = ecs_vec_first(&vars->vars); + for (i = 0; i < count; i ++) { + ecs_script_var_t *var = &var_array[i]; + if (!var->value.ptr) { + continue; + } + + if (!var->type_info || !var->type_info->hooks.dtor) { + continue; + } - flecs_resume_readonly(world, &rs); + var->type_info->hooks.dtor(var->value.ptr, 1, var->type_info); + } - return t; + flecs_name_index_fini(&vars->var_index); + } + + ecs_vec_fini_t(vars->allocator, &vars->vars, ecs_script_var_t); + flecs_stack_restore_cursor(vars->stack, cursor); + return parent; } -ecs_entity_t ecs_vector_init( - ecs_world_t *world, - const ecs_vector_desc_t *desc) +ecs_script_var_t* ecs_script_vars_declare( + ecs_script_vars_t *vars, + const char *name) { - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); - - ecs_entity_t t = desc->entity; - if (!t) { - t = ecs_new_low_id(world); + if (!ecs_vec_count(&vars->vars)) { + flecs_name_index_init(&vars->var_index, vars->allocator); + } else { + if (flecs_name_index_find(&vars->var_index, name, 0, 0) != 0) { + goto error; + } } - ecs_set(world, t, EcsVector, { - .type = desc->type - }); + ecs_script_var_t *var = ecs_vec_append_t( + vars->allocator, &vars->vars, ecs_script_var_t); + var->name = name; + var->value.ptr = NULL; + var->value.type = 0; + var->type_info = NULL; - flecs_resume_readonly(world, &rs); + flecs_name_index_ensure(&vars->var_index, + flecs_ito(uint64_t, ecs_vec_count(&vars->vars)), name, 0, 0); - return t; + return var; +error: + return NULL; } -static -bool flecs_member_range_overlaps( - const ecs_member_value_range_t *range, - const ecs_member_value_range_t *with) +ecs_script_var_t* ecs_script_vars_define_id( + ecs_script_vars_t *vars, + const char *name, + ecs_entity_t type) { - if (ECS_EQ(with->min, with->max)) { - return false; - } + ecs_check(vars->world != NULL, ECS_INVALID_OPERATION, "variable scope is " + "not associated with world, create scope with ecs_script_vars_init"); - if (ECS_EQ(range->min, range->max)) { - return false; + const ecs_type_info_t *ti = ecs_get_type_info(vars->world, type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, + "the entity provided for the type parameter is not a type"); + + ecs_script_var_t *result = ecs_script_vars_declare(vars, name); + if (!result) { + return NULL; } - if (range->min < with->min || - range->max > with->max) - { - return true; + result->value.type = type; + result->value.ptr = flecs_stack_alloc(vars->stack, ti->size, ti->alignment); + result->type_info = ti; + + if (ti->hooks.ctor) { + ti->hooks.ctor(result->value.ptr, 1, ti); } - return false; + return result; +error: + return NULL; } -ecs_entity_t ecs_struct_init( - ecs_world_t *world, - const ecs_struct_desc_t *desc) +ecs_script_var_t* ecs_script_vars_lookup( + const ecs_script_vars_t *vars, + const char *name) { - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); - - ecs_entity_t t = desc->entity; - if (!t) { - t = ecs_new_low_id(world); + uint64_t var_id = 0; + if (ecs_vec_count(&vars->vars)) { + var_id = flecs_name_index_find(&vars->var_index, name, 0, 0); } - ecs_entity_t old_scope = ecs_set_scope(world, t); - - int i; - for (i = 0; i < ECS_MEMBER_DESC_CACHE_SIZE; i ++) { - const ecs_member_t *m_desc = &desc->members[i]; - if (!m_desc->type) { - break; + if (!var_id) { + if (vars->parent) { + return ecs_script_vars_lookup(vars->parent, name); } + return NULL; + } - if (!m_desc->name) { - ecs_err("member %d of struct '%s' does not have a name", i, - ecs_get_name(world, t)); - goto error; - } + return ecs_vec_get_t(&vars->vars, ecs_script_var_t, + flecs_uto(int32_t, var_id - 1)); +} - ecs_entity_t m = ecs_entity(world, { - .name = m_desc->name - }); +/* Static names for iterator fields */ +static const char* flecs_script_iter_field_names[] = { + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15" +}; - ecs_set(world, m, EcsMember, { - .type = m_desc->type, - .count = m_desc->count, - .offset = m_desc->offset, - .unit = m_desc->unit - }); +void ecs_script_vars_from_iter( + const ecs_iter_t *it, + ecs_script_vars_t *vars, + int offset) +{ + ecs_check(vars != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(!offset || offset < it->count, ECS_INVALID_PARAMETER, NULL); - EcsMemberRanges *ranges = NULL; - const ecs_member_value_range_t *range = &m_desc->range; - const ecs_member_value_range_t *error = &m_desc->error_range; - const ecs_member_value_range_t *warning = &m_desc->warning_range; - if (ECS_NEQ(range->min, range->max)) { - ranges = ecs_get_mut(world, m, EcsMemberRanges); - if (range->min > range->max) { - char *member_name = ecs_get_fullpath(world, m); - ecs_err("member '%s' has an invalid value range [%f..%f]", - member_name, range->min, range->max); - ecs_os_free(member_name); - goto error; - } - ranges->value.min = range->min; - ranges->value.max = range->max; + /* Set variable for $this */ + if (it->count) { + ecs_script_var_t *var = ecs_script_vars_lookup(vars, "this"); + if (!var) { + var = ecs_script_vars_declare(vars, "this"); + var->value.type = ecs_id(ecs_entity_t); } - if (ECS_NEQ(error->min, error->max)) { - if (error->min > error->max) { - char *member_name = ecs_get_fullpath(world, m); - ecs_err("member '%s' has an invalid error range [%f..%f]", - member_name, error->min, error->max); - ecs_os_free(member_name); - goto error; + + /* Safe, variable value will never be written */ + var->value.ptr = ECS_CONST_CAST(ecs_entity_t*, &it->entities[offset]); + } + + /* Set variables for fields */ + { + int8_t i, field_count = it->field_count; + for (i = 0; i < field_count; i ++) { + ecs_size_t size = it->sizes[i]; + if (!size) { + continue; } - if (flecs_member_range_overlaps(error, range)) { - char *member_name = ecs_get_fullpath(world, m); - ecs_err("error range of member '%s' overlaps with value range", - member_name); - ecs_os_free(member_name); - goto error; + + void *ptr = ecs_field_w_size(it, flecs_itosize(size), i); + if (!ptr) { + continue; } - if (!ranges) { - ranges = ecs_get_mut(world, m, EcsMemberRanges); + + ptr = ECS_OFFSET(ptr, offset * size); + + const char *name = flecs_script_iter_field_names[i]; + ecs_script_var_t *var = ecs_script_vars_lookup(vars, name); + if (!var) { + var = ecs_script_vars_declare(vars, name); + ecs_assert(ecs_script_vars_lookup(vars, name) != NULL, + ECS_INTERNAL_ERROR, NULL); + var->value.type = it->ids[i]; + } else { + ecs_check(var->value.type == it->ids[i], + ECS_INVALID_PARAMETER, NULL); } - ranges->error.min = error->min; - ranges->error.max = error->max; + var->value.ptr = ptr; } + } - if (ECS_NEQ(warning->min, warning->max)) { - if (warning->min > warning->max) { - char *member_name = ecs_get_fullpath(world, m); - ecs_err("member '%s' has an invalid warning range [%f..%f]", - member_name, warning->min, warning->max); - ecs_os_free(member_name); - goto error; - } - if (flecs_member_range_overlaps(warning, range)) { - char *member_name = ecs_get_fullpath(world, m); - ecs_err("warning range of member '%s' overlaps with value " - "range", member_name); - ecs_os_free(member_name); - goto error; + /* Set variables for query variables */ + { + int32_t i, var_count = it->variable_count; + for (i = 1 /* skip this variable */ ; i < var_count; i ++) { + const ecs_entity_t *e_ptr = NULL; + ecs_var_t *query_var = &it->variables[i]; + if (query_var->entity) { + e_ptr = &query_var->entity; + } else { + ecs_table_range_t *range = &query_var->range; + if (range->count == 1) { + const ecs_entity_t *entities = + ecs_table_entities(range->table); + e_ptr = &entities[range->offset]; + } } - if (flecs_member_range_overlaps(warning, error)) { - char *member_name = ecs_get_fullpath(world, m); - ecs_err("warning range of member '%s' overlaps with error " - "range", member_name); - ecs_os_free(member_name); - goto error; + if (!e_ptr) { + continue; } - if (!ranges) { - ranges = ecs_get_mut(world, m, EcsMemberRanges); + ecs_script_var_t *var = ecs_script_vars_lookup( + vars, it->variable_names[i]); + if (!var) { + var = ecs_script_vars_declare(vars, it->variable_names[i]); + var->value.type = ecs_id(ecs_entity_t); + } else { + ecs_check(var->value.type == ecs_id(ecs_entity_t), + ECS_INVALID_PARAMETER, NULL); } - ranges->warning.min = warning->min; - ranges->warning.max = warning->max; - } - - if (ranges && !flecs_type_is_number(world, m_desc->type)) { - char *member_name = ecs_get_fullpath(world, m); - ecs_err("member '%s' has an value/error/warning range, but is not a " - "number", member_name); - ecs_os_free(member_name); - goto error; - } - if (ranges) { - ecs_modified(world, m, EcsMemberRanges); + /* Safe, variable value will never be written */ + var->value.ptr = ECS_CONST_CAST(ecs_entity_t*, e_ptr); } } - ecs_set_scope(world, old_scope); - flecs_resume_readonly(world, &rs); +error: + return; +} - if (i == 0) { - ecs_err("struct '%s' has no members", ecs_get_name(world, t)); - goto error; - } +#endif - if (!ecs_has(world, t, EcsStruct)) { - goto error; - } +/** + * @file addons/script/visit.c + * @brief Script AST visitor utilities. + */ - return t; -error: - flecs_resume_readonly(world, &rs); - if (t) { - ecs_delete(world, t); + +#ifdef FLECS_SCRIPT + +ecs_script_node_t* ecs_script_parent_node_( + ecs_script_visit_t *v) +{ + if (v->depth > 1) { + return v->nodes[v->depth - 2]; /* Last node is current node */ + } else { + return NULL; } - return 0; } -ecs_entity_t ecs_opaque_init( - ecs_world_t *world, - const ecs_opaque_desc_t *desc) +ecs_script_scope_t* ecs_script_current_scope_( + ecs_script_visit_t *v) { - ecs_assert(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->type.as_type != 0, ECS_INVALID_PARAMETER, NULL); + int32_t depth; + for(depth = v->depth - 1; depth >= 0; depth --) { + ecs_script_node_t *node = v->nodes[depth]; + if (node->kind == EcsAstScope) { + return (ecs_script_scope_t*)node; + } + } - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); + return NULL; +} - ecs_entity_t t = desc->entity; - if (!t) { - t = ecs_new_low_id(world); +ecs_script_node_t* ecs_script_parent_( + ecs_script_visit_t *v, + ecs_script_node_t *child) +{ + int32_t depth; + for(depth = v->depth - 1; depth >= 0; depth --) { + ecs_script_node_t *node = v->nodes[depth]; + if (node == child && depth) { + return v->nodes[depth - 1]; + } } - ecs_set_ptr(world, t, EcsOpaque, &desc->type); + return NULL; +} - flecs_resume_readonly(world, &rs); +int32_t ecs_script_node_line_number_( + ecs_script_impl_t *script, + ecs_script_node_t *node) +{ + const char *ptr; + int32_t line_count = 1; + for (ptr = script->pub.code; ptr < node->pos; ptr ++) { + ecs_assert(ptr[0] != 0, ECS_INTERNAL_ERROR, NULL); + if (ptr[0] == '\n') { + line_count ++; + } + } - return t; + return line_count; } -ecs_entity_t ecs_unit_init( - ecs_world_t *world, - const ecs_unit_desc_t *desc) +int ecs_script_visit_scope_( + ecs_script_visit_t *v, + ecs_script_scope_t *scope) { - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); + ecs_script_node_t **nodes = ecs_vec_first_t( + &scope->stmts, ecs_script_node_t*); - ecs_entity_t t = desc->entity; - if (!t) { - t = ecs_new_low_id(world); - } + v->nodes[v->depth ++] = (ecs_script_node_t*)scope; - ecs_entity_t quantity = desc->quantity; - if (quantity) { - if (!ecs_has_id(world, quantity, EcsQuantity)) { - ecs_err("entity '%s' for unit '%s' is not a quantity", - ecs_get_name(world, quantity), ecs_get_name(world, t)); - goto error; + int32_t i, count = ecs_vec_count(&scope->stmts); + for (i = 0; i < count; i ++) { + if (!i) { + v->prev = NULL; + } else { + v->prev = nodes[i - 1]; } - ecs_add_pair(world, t, EcsQuantity, desc->quantity); - } else { - ecs_remove_pair(world, t, EcsQuantity, EcsWildcard); - } + if (i != (count - 1)) { + v->next = nodes[i + 1]; + } else { + v->next = NULL; + } - EcsUnit *value = ecs_get_mut(world, t, EcsUnit); - value->base = desc->base; - value->over = desc->over; - value->translation = desc->translation; - value->prefix = desc->prefix; - ecs_os_strset(&value->symbol, desc->symbol); + v->nodes[v->depth ++] = nodes[i]; - if (!flecs_unit_validate(world, t, value)) { - goto error; + if (v->visit(v, nodes[i])) { + return -1; + } + + v->depth --; } - ecs_modified(world, t, EcsUnit); + v->depth --; - flecs_resume_readonly(world, &rs); - return t; -error: - if (t) { - ecs_delete(world, t); - } - flecs_resume_readonly(world, &rs); return 0; } -ecs_entity_t ecs_unit_prefix_init( - ecs_world_t *world, - const ecs_unit_prefix_desc_t *desc) +int ecs_script_visit_node_( + ecs_script_visit_t *v, + ecs_script_node_t *node) { - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); + v->nodes[v->depth ++] = node; - ecs_entity_t t = desc->entity; - if (!t) { - t = ecs_new_low_id(world); + if (v->visit(v, node)) { + return -1; } - ecs_set(world, t, EcsUnitPrefix, { - .symbol = ECS_CONST_CAST(char*, desc->symbol), - .translation = desc->translation - }); - - flecs_resume_readonly(world, &rs); + v->depth --; - return t; + return 0; } -ecs_entity_t ecs_quantity_init( - ecs_world_t *world, - const ecs_entity_desc_t *desc) +int ecs_script_visit_( + ecs_script_visit_t *visitor, + ecs_visit_action_t visit, + ecs_script_impl_t *script) { - ecs_suspend_readonly_state_t rs; - world = flecs_suspend_readonly(world, &rs); - - ecs_entity_t t = ecs_entity_init(world, desc); - if (!t) { - return 0; + visitor->script = script; + visitor->visit = visit; + visitor->depth = 0; + int result = ecs_script_visit_node(visitor, script->root); + if (result) { + return -1; } - ecs_add_id(world, t, EcsQuantity); - - flecs_resume_readonly(world, &rs); + if (visitor->depth) { + ecs_parser_error(script->pub.name, NULL, 0, "unexpected end of script"); + return -1; + } - return t; + return 0; } #endif /** - * @file meta/cursor.c - * @brief API for assigning values of runtime types with reflection. + * @file addons/script/visit_eval.c + * @brief Script evaluation visitor. */ -#include -#ifdef FLECS_META -#ifdef FLECS_PARSER -#endif +#ifdef FLECS_SCRIPT + +void flecs_script_eval_error_( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + char *msg = flecs_vasprintf(fmt, args); + va_end(args); + + if (node) { + int32_t line = ecs_script_node_line_number(v->base.script, node); + ecs_parser_error(v->base.script->pub.name, NULL, 0, "%d: %s", line, msg); + } else { + ecs_parser_error(v->base.script->pub.name, NULL, 0, "%s", msg); + } + + ecs_os_free(msg); +} static -const char* flecs_meta_op_kind_str( - ecs_meta_type_op_kind_t kind) +ecs_value_t* flecs_script_with_append( + ecs_allocator_t *a, + ecs_script_eval_visitor_t *v, + const ecs_type_info_t *ti) { - switch(kind) { + if (ecs_vec_count(&v->with)) { + ecs_assert(ecs_vec_last_t(&v->with, ecs_value_t)->type == 0, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_vec_last_t(&v->with, ecs_value_t)->ptr == NULL, + ECS_INTERNAL_ERROR, NULL); + ecs_vec_remove_last(&v->with); + } - case EcsOpEnum: return "Enum"; - case EcsOpBitmask: return "Bitmask"; - case EcsOpArray: return "Array"; - case EcsOpVector: return "Vector"; - case EcsOpOpaque: return "Opaque"; - case EcsOpPush: return "Push"; - case EcsOpPop: return "Pop"; - case EcsOpPrimitive: return "Primitive"; - case EcsOpBool: return "Bool"; - case EcsOpChar: return "Char"; - case EcsOpByte: return "Byte"; - case EcsOpU8: return "U8"; - case EcsOpU16: return "U16"; - case EcsOpU32: return "U32"; - case EcsOpU64: return "U64"; - case EcsOpI8: return "I8"; - case EcsOpI16: return "I16"; - case EcsOpI32: return "I32"; - case EcsOpI64: return "I64"; - case EcsOpF32: return "F32"; - case EcsOpF64: return "F64"; - case EcsOpUPtr: return "UPtr"; - case EcsOpIPtr: return "IPtr"; - case EcsOpString: return "String"; - case EcsOpEntity: return "Entity"; - case EcsOpId: return "Id"; - case EcsOpScope: return "Scope"; - default: return "<< invalid kind >>"; + ecs_vec_append_t(a, &v->with_type_info, const ecs_type_info_t*)[0] = ti; + + ecs_vec_append_t(a, &v->with, ecs_value_t); + ecs_value_t *last = ecs_vec_append_t(a, &v->with, ecs_value_t); + ecs_os_memset_t(last, 0, ecs_value_t); + return ecs_vec_get_t(&v->with, ecs_value_t, ecs_vec_count(&v->with) - 2); +} + +static +void flecs_script_with_set_count( + ecs_allocator_t *a, + ecs_script_eval_visitor_t *v, + int32_t count) +{ + int32_t i = count, until = ecs_vec_count(&v->with) - 1; + for (; i < until; i ++) { + ecs_value_t *val = ecs_vec_get_t(&v->with, ecs_value_t, i); + ecs_type_info_t *ti = ecs_vec_get_t( + &v->with_type_info, ecs_type_info_t*, i)[0]; + if (ti && ti->hooks.dtor) { + ti->hooks.dtor(val->ptr, 1, ti); + } + } + + if (count) { + ecs_value_t *last = ecs_vec_get_t(&v->with, ecs_value_t, count); + ecs_os_memset_t(last, 0, ecs_value_t); + ecs_vec_set_count_t(a, &v->with, ecs_value_t, count + 1); + } else { + ecs_vec_set_count_t(a, &v->with, ecs_value_t, 0); } + + ecs_vec_set_count_t(a, &v->with_type_info, ecs_type_info_t*, count); } -/* Get current scope */ static -ecs_meta_scope_t* flecs_meta_cursor_get_scope( - const ecs_meta_cursor_t *cursor) +ecs_value_t* flecs_script_with_last( + ecs_script_eval_visitor_t *v) { - ecs_check(cursor != NULL, ECS_INVALID_PARAMETER, NULL); - return ECS_CONST_CAST(ecs_meta_scope_t*, &cursor->scope[cursor->depth]); -error: + int32_t count = ecs_vec_count(&v->with); + if (count) { + return ecs_vec_get_t(&v->with, ecs_value_t, count - 2); + } return NULL; } -/* Restore scope, if dotmember was used */ static -ecs_meta_scope_t* flecs_meta_cursor_restore_scope( - ecs_meta_cursor_t *cursor, - const ecs_meta_scope_t* scope) +int32_t flecs_script_with_count( + ecs_script_eval_visitor_t *v) { - ecs_check(cursor != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(scope != NULL, ECS_INVALID_PARAMETER, NULL); - if (scope->prev_depth) { - cursor->depth = scope->prev_depth; + if (ecs_vec_count(&v->with)) { + ecs_assert(ecs_vec_last_t(&v->with, ecs_value_t)->type == 0, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_vec_last_t(&v->with, ecs_value_t)->ptr == NULL, + ECS_INTERNAL_ERROR, NULL); + return ecs_vec_count(&v->with) - 1; + } + return 0; +} + +const ecs_type_info_t* flecs_script_get_type_info( + ecs_script_eval_visitor_t *v, + void *node, + ecs_id_t id) +{ + ecs_id_record_t *idr = flecs_id_record_ensure(v->world, id); + if (!idr) { + goto error; + } + + if (!idr->type_info) { + goto error; } + + return idr->type_info; error: - return (ecs_meta_scope_t*)&cursor->scope[cursor->depth]; + { + char *idstr = ecs_id_str(v->world, id); + flecs_script_eval_error(v, node, + "cannot set value of '%s': not a component", idstr); + ecs_os_free(idstr); + } + return NULL; } -/* Get current operation for scope */ -static -ecs_meta_type_op_t* flecs_meta_cursor_get_op( - ecs_meta_scope_t *scope) +ecs_entity_t flecs_script_find_entity( + ecs_script_eval_visitor_t *v, + ecs_entity_t from, + const char *path) { - ecs_assert(scope->ops != NULL, ECS_INVALID_OPERATION, NULL); - return &scope->ops[scope->op_cur]; + if (!path) { + return 0; + } + + if (path[0] == '$') { + const ecs_script_var_t *var = ecs_script_vars_lookup(v->vars, &path[1]); + if (!var) { + return 0; + } + + if (var->value.type != ecs_id(ecs_entity_t)) { + char *type_str = ecs_id_str(v->world, var->value.type); + flecs_script_eval_error(v, NULL, + "variable '%s' must be of type entity, got '%s'", + path, type_str); + ecs_os_free(type_str); + return 0; + } + + if (var->value.ptr == NULL) { + flecs_script_eval_error(v, NULL, + "variable '%s' is not initialized", path); + return 0; + } + + ecs_entity_t result = *(ecs_entity_t*)var->value.ptr; + if (!result) { + flecs_script_eval_error(v, NULL, + "variable '%s' contains invalid entity id (0)", path); + return 0; + } + + return result; + } + + if (from) { + return ecs_lookup_path_w_sep(v->world, from, path, NULL, NULL, false); + } else { + int32_t i, using_count = ecs_vec_count(&v->using); + if (using_count) { + ecs_entity_t *using = ecs_vec_first(&v->using); + for (i = using_count - 1; i >= 0; i --) { + ecs_entity_t e = ecs_lookup_path_w_sep( + v->world, using[i], path, NULL, NULL, false); + if (e) { + return e; + } + } + } + + return ecs_lookup_path_w_sep( + v->world, v->parent, path, NULL, NULL, true); + } } -/* Get component for type in current scope */ -static -const EcsComponent* get_ecs_component( - const ecs_world_t *world, - ecs_meta_scope_t *scope) +ecs_entity_t flecs_script_create_entity( + ecs_script_eval_visitor_t *v, + const char *name) { - const EcsComponent *comp = scope->comp; - if (!comp) { - comp = scope->comp = ecs_get(world, scope->type, EcsComponent); - ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_value_t *with = NULL; + if (flecs_script_with_count(v)) { + with = ecs_vec_first_t(&v->with, ecs_value_t); } - return comp; + + ecs_entity_desc_t desc = {0}; + desc.name = name; + desc.parent = v->parent; + desc.set = with; + return ecs_entity_init(v->world, &desc); } -/* Get size for type in current scope */ static -ecs_size_t get_size( +ecs_entity_t flecs_script_find_entity_action( const ecs_world_t *world, - ecs_meta_scope_t *scope) + const char *path, + void *ctx) { - return get_ecs_component(world, scope)->size; + (void)world; + ecs_script_eval_visitor_t *v = ctx; + return flecs_script_find_entity(v, 0, path); } static -int32_t get_elem_count( - ecs_meta_scope_t *scope) +int flecs_script_find_template_entity( + ecs_script_eval_visitor_t *v, + const char *name) { - const EcsOpaque *opaque = scope->opaque; + /* Loop template scope to see if it declares an entity with requested name */ + ecs_script_template_t *t = v->template; + ecs_script_scope_t *scope = t->node->scope; + ecs_script_node_t **nodes = ecs_vec_first_t( + &scope->stmts, ecs_script_node_t*); - if (scope->vector) { - return ecs_vec_count(scope->vector); - } else if (opaque && opaque->count) { - return flecs_uto(int32_t, opaque->count(scope[-1].ptr)); + int32_t i, count = ecs_vec_count(&scope->stmts); + for (i = 0; i < count; i ++) { + ecs_script_node_t *node = nodes[i]; + if (node->kind == EcsAstEntity) { + ecs_script_entity_t *entity_node = (ecs_script_entity_t*)node; + if (!ecs_os_strcmp(entity_node->name, name)) { + return 0; + } + } } - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - return op->count; + return -1; } -/* Get pointer to current field/element */ static -ecs_meta_type_op_t* flecs_meta_cursor_get_ptr( - const ecs_world_t *world, - ecs_meta_scope_t *scope) +int flecs_script_eval_id( + ecs_script_eval_visitor_t *v, + void *node, + ecs_script_id_t *id) { - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - ecs_size_t size = get_size(world, scope); - const EcsOpaque *opaque = scope->opaque; + ecs_entity_t second_from = 0; + + if (!id->first) { + flecs_script_eval_error(v, node, + "invalid component/tag identifier"); + return -1; + } + + if (v->template) { + /* Can't resolve variables while preprocessing template scope */ + if (id->first[0] == '$') { + return 0; + } + if (id->second && id->second[0] == '$') { + return 0; + } + } + + ecs_entity_t first = flecs_script_find_entity(v, 0, id->first); + if (!first) { + if (id->first[0] == '$') { + flecs_script_eval_error(v, node, + "unresolved variable '%s'", id->first); + return -1; + } + + flecs_script_eval_error(v, node, + "unresolved identifier '%s'", id->first); + return -1; + } else if (id->second) { + second_from = flecs_get_oneof(v->world, first); + } - if (scope->vector) { - ecs_vec_set_min_count(NULL, scope->vector, size, scope->elem_cur + 1); - scope->ptr = ecs_vec_first(scope->vector); - } else if (opaque) { - if (scope->is_collection) { - if (!opaque->ensure_element) { - char *str = ecs_get_fullpath(world, scope->type); - ecs_err("missing ensure_element for opaque type %s", str); - ecs_os_free(str); - return NULL; + if (id->second) { + ecs_entity_t second = flecs_script_find_entity( + v, second_from, id->second); + if (!second) { + if (id->second[0] == '$') { + flecs_script_eval_error(v, node, + "unresolved variable '%s'", id->second); + return -1; } - scope->is_empty_scope = false; - void *opaque_ptr = opaque->ensure_element( - scope->ptr, flecs_ito(size_t, scope->elem_cur)); - ecs_assert(opaque_ptr != NULL, ECS_INVALID_OPERATION, - "ensure_element returned NULL"); - return opaque_ptr; - } else if (op->name) { - if (!opaque->ensure_member) { - char *str = ecs_get_fullpath(world, scope->type); - ecs_err("missing ensure_member for opaque type %s", str); - ecs_os_free(str); - return NULL; + /* Targets may be defined by the template */ + if (v->template) { + if (!flecs_script_find_template_entity(v, id->second)) { + return 0; + } else { + return -1; + } } - ecs_assert(scope->ptr != NULL, ECS_INTERNAL_ERROR, NULL); - return opaque->ensure_member(scope->ptr, op->name); - } else { - ecs_err("invalid operation for opaque type"); - return NULL; + + if (second_from) { + char *parent_str = ecs_id_str(v->world, second_from); + flecs_script_eval_error(v, node, "target '%s' not found in " + "parent '%s'", id->second, parent_str); + ecs_os_free(parent_str); + return -1; + } + + flecs_script_eval_error(v, node, + "unresolved identifier '%s'", id->second); + return -1; + } + + if (first == EcsAny || second == EcsAny) { + flecs_script_eval_error(v, node, + "cannot use anonymous entity as element of pair"); + return -1; } + + id->eval = id->flag | ecs_pair(first, second); + } else { + if (first == EcsAny) { + flecs_script_eval_error(v, node, + "cannot use anonymous entity as component or tag"); + return -1; + } + + id->eval = id->flag | first; } - return ECS_OFFSET(scope->ptr, size * scope->elem_cur + op->offset); + return 0; } -static -int flecs_meta_cursor_push_type( - const ecs_world_t *world, - ecs_meta_scope_t *scope, - ecs_entity_t type, - void *ptr) +int flecs_script_eval_expr( + ecs_script_eval_visitor_t *v, + const char *expr, + ecs_value_t *value) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - if (ser == NULL) { - char *str = ecs_id_str(world, type); - ecs_err("cannot open scope for entity '%s' which is not a type", str); - ecs_os_free(str); - return -1; + if (!value->type && expr[0] == '{') { + expr ++; } - scope[0] = (ecs_meta_scope_t) { - .type = type, - .ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t), - .op_count = ecs_vec_count(&ser->ops), - .ptr = ptr + ecs_script_expr_run_desc_t desc = { + .name = v->base.script->pub.name, + .expr = expr, + .lookup_action = flecs_script_find_entity_action, + .lookup_ctx = v, + .vars = v->vars }; + if (!ecs_script_expr_run(v->world, expr, value, &desc)) { + return -1; + } + return 0; } -ecs_meta_cursor_t ecs_meta_cursor( - const ecs_world_t *world, - ecs_entity_t type, - void *ptr) +static +int flecs_script_eval_scope( + ecs_script_eval_visitor_t *v, + ecs_script_scope_t *node) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(type != 0, ECS_INVALID_PARAMETER, NULL); + ecs_script_node_t *scope_parent = ecs_script_parent_node(v); + ecs_entity_t prev_eval_parent = v->parent; + int32_t prev_using_count = ecs_vec_count(&v->using); - ecs_meta_cursor_t result = { - .world = world, - .valid = true - }; + for (int i = v->base.depth - 2; i >= 0; i --) { + if (v->base.nodes[i]->kind == EcsAstScope) { + node->parent = (ecs_script_scope_t*)v->base.nodes[i]; + break; + } + } - if (flecs_meta_cursor_push_type(world, result.scope, type, ptr) != 0) { - result.valid = false; + ecs_allocator_t *a = v->allocator; + v->vars = flecs_script_vars_push(v->vars, &v->stack, a); + + if (scope_parent && (scope_parent->kind == EcsAstEntity)) { + if (!v->template) { + v->parent = ecs_script_node(entity, scope_parent)->eval; + } } + int result = ecs_script_visit_scope(v, node); + + ecs_vec_set_count_t(a, &v->using, ecs_entity_t, prev_using_count); + v->vars = ecs_script_vars_pop(v->vars); + v->parent = prev_eval_parent; + return result; -error: - return (ecs_meta_cursor_t){ 0 }; } -void* ecs_meta_get_ptr( - ecs_meta_cursor_t *cursor) +static +int flecs_script_apply_annot( + ecs_script_eval_visitor_t *v, + ecs_entity_t entity, + ecs_script_annot_t *node) { - return flecs_meta_cursor_get_ptr(cursor->world, - flecs_meta_cursor_get_scope(cursor)); + if (!ecs_os_strcmp(node->name, "name")) { + ecs_doc_set_name(v->world, entity, node->expr); + } else + if (!ecs_os_strcmp(node->name, "brief")) { + ecs_doc_set_brief(v->world, entity, node->expr); + } else + if (!ecs_os_strcmp(node->name, "detail")) { + ecs_doc_set_detail(v->world, entity, node->expr); + } else + if (!ecs_os_strcmp(node->name, "link")) { + ecs_doc_set_link(v->world, entity, node->expr); + } else + if (!ecs_os_strcmp(node->name, "color")) { + ecs_doc_set_color(v->world, entity, node->expr); + } else { + flecs_script_eval_error(v, node, "unknown annotation '%s'", + node->name); + return -1; + } + + return 0; } -int ecs_meta_next( - ecs_meta_cursor_t *cursor) +static +int flecs_script_eval_entity( + ecs_script_eval_visitor_t *v, + ecs_script_entity_t *node) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - scope = flecs_meta_cursor_restore_scope(cursor, scope); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + bool is_slot = false; + if (node->kind) { + ecs_script_id_t id = { + .first = node->kind + }; - if (scope->is_collection) { - scope->elem_cur ++; - scope->op_cur = 0; + if (!ecs_os_strcmp(node->kind, "prefab")) { + id.eval = EcsPrefab; + } else if (!ecs_os_strcmp(node->kind, "slot")) { + is_slot = true; + } else if (flecs_script_eval_id(v, node, &id)) { + return -1; + } - if (scope->opaque) { - return 0; + node->eval_kind = id.eval; + } else { + /* Inherit kind from parent kind's DefaultChildComponent, if it existst */ + ecs_script_scope_t *scope = ecs_script_current_scope(v); + if (scope && scope->default_component_eval) { + node->eval_kind = scope->default_component_eval; } + } - if (scope->elem_cur >= get_elem_count(scope)) { - ecs_err("out of collection bounds (%d)", scope->elem_cur); + if (v->template) { + if (ecs_script_visit_node(v, node->scope)) { return -1; } return 0; } - scope->op_cur += op->op_count; + node->eval = flecs_script_create_entity(v, node->name); + node->parent = v->entity; - if (scope->op_cur >= scope->op_count) { - ecs_err("out of bounds"); + if (is_slot) { + ecs_entity_t parent = ecs_get_target( + v->world, node->eval, EcsChildOf, 0); + if (!parent) { + flecs_script_eval_error(v, node, + "slot entity must have a parent"); + return -1; + } + + ecs_add_pair(v->world, node->eval, EcsSlotOf, parent); + } + + const EcsDefaultChildComponent *default_comp = NULL; + ecs_script_entity_t *old_entity = v->entity; + v->entity = node; + + if (node->eval_kind) { + ecs_add_id(v->world, node->eval, node->eval_kind); + + default_comp = + ecs_get(v->world, node->eval_kind, EcsDefaultChildComponent); + if (default_comp) { + if (!default_comp->component) { + flecs_script_eval_error(v, node, "entity '%s' has kind '%s' " + "with uninitialized DefaultChildComponent", + node->name, node->kind); + return -1; + } + + node->scope->default_component_eval = default_comp->component; + } + } + + int32_t i, count = ecs_vec_count(&v->annot); + if (count) { + ecs_script_annot_t **annots = ecs_vec_first(&v->annot); + for (i = 0; i < count ; i ++) { + flecs_script_apply_annot(v, node->eval, annots[i]); + } + ecs_vec_clear(&v->annot); + } + + if (ecs_script_visit_node(v, node->scope)) { return -1; } + if (node->eval_kind) { + if (!node->kind_w_expr) { + if (ecs_get_type_info(v->world, node->eval_kind) != NULL) { + ecs_modified_id(v->world, node->eval, node->eval_kind); + } + } + } + + v->entity = old_entity; + return 0; } -int ecs_meta_elem( - ecs_meta_cursor_t *cursor, - int32_t elem) +static +ecs_entity_t flecs_script_get_src( + ecs_script_eval_visitor_t *v, + ecs_entity_t entity, + ecs_id_t id) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - if (!scope->is_collection) { - ecs_err("ecs_meta_elem can be used for collections only"); + if (entity == EcsVariable) { // Singleton ($) + if (ECS_IS_PAIR(id)) { + return ecs_pair_first(v->world, id); + } else { + return id & ECS_COMPONENT_MASK; + } + } + return entity; +} + +static +int flecs_script_eval_tag( + ecs_script_eval_visitor_t *v, + ecs_script_tag_t *node) +{ + if (flecs_script_eval_id(v, node, &node->id)) { return -1; } - scope->elem_cur = elem; - scope->op_cur = 0; + if (v->template) { + return 0; + } - if (scope->elem_cur >= get_elem_count(scope) || (scope->elem_cur < 0)) { - ecs_err("out of collection bounds (%d)", scope->elem_cur); + if (!v->entity) { + if (node->id.second) { + flecs_script_eval_error( + v, node, "missing entity for pair (%s, %s)", + node->id.first, node->id.second); + } else { + flecs_script_eval_error(v, node, "missing entity for tag %s", + node->id.first); + } return -1; } - + + if (v->template) { + return 0; + } + + ecs_entity_t src = flecs_script_get_src( + v, v->entity->eval, node->id.eval); + ecs_add_id(v->world, src, node->id.eval); + return 0; } -int ecs_meta_member( - ecs_meta_cursor_t *cursor, - const char *name) +static +int flecs_script_eval_component( + ecs_script_eval_visitor_t *v, + ecs_script_component_t *node) { - if (cursor->depth == 0) { - ecs_err("cannot move to member in root scope"); + if (flecs_script_eval_id(v, node, &node->id)) { return -1; } - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - scope = flecs_meta_cursor_restore_scope(cursor, scope); - - ecs_hashmap_t *members = scope->members; - const ecs_world_t *world = cursor->world; + if (v->template) { + return 0; + } - if (!members) { - ecs_err("cannot move to member '%s' for non-struct type", name); + if (!v->entity) { + if (node->id.second) { + flecs_script_eval_error(v, node, "missing entity for pair (%s, %s)", + node->id.first, node->id.second); + } else { + flecs_script_eval_error(v, node, "missing entity for component %s", + node->id.first); + } return -1; } - const uint64_t *cur_ptr = flecs_name_index_find_ptr(members, name, 0, 0); - if (!cur_ptr) { - char *path = ecs_get_fullpath(world, scope->type); - ecs_err("unknown member '%s' for type '%s'", name, path); - ecs_os_free(path); - return -1; + if (v->template) { + return 0; } - scope->op_cur = flecs_uto(int32_t, cur_ptr[0]); + ecs_entity_t src = flecs_script_get_src(v, v->entity->eval, node->id.eval); - const EcsOpaque *opaque = scope->opaque; - if (opaque) { - if (!opaque->ensure_member) { - char *str = ecs_get_fullpath(world, scope->type); - ecs_err("missing ensure_member for opaque type %s", str); - ecs_os_free(str); + if (node->expr && node->expr[0]) { + const ecs_type_info_t *ti = flecs_script_get_type_info( + v, node, node->id.eval); + if (!ti) { + return -1; } - } - - return 0; -} -int ecs_meta_dotmember( - ecs_meta_cursor_t *cursor, - const char *name) -{ -#ifdef FLECS_PARSER - ecs_meta_scope_t *cur_scope = flecs_meta_cursor_get_scope(cursor); - flecs_meta_cursor_restore_scope(cursor, cur_scope); + const EcsType *type = ecs_get(v->world, ti->component, EcsType); + if (type) { + bool is_collection = false; - int32_t prev_depth = cursor->depth; - int dotcount = 0; + switch(type->kind) { + case EcsPrimitiveType: + case EcsBitmaskType: + case EcsEnumType: + case EcsStructType: + case EcsOpaqueType: + break; + case EcsArrayType: + case EcsVectorType: + is_collection = true; + break; + } - char token[ECS_MAX_TOKEN_SIZE]; - const char *ptr = name; - while ((ptr = ecs_parse_token(NULL, NULL, ptr, token, '.'))) { - if (ptr[0] != '.' && ptr[0]) { - ecs_parser_error(NULL, name, ptr - name, - "expected '.' or end of string"); - goto error; + if (node->is_collection != is_collection) { + char *id_str = ecs_id_str(v->world, ti->component); + if (node->is_collection && !is_collection) { + flecs_script_eval_error(v, node, + "type %s is not a collection (use '%s: {...}')", + id_str, id_str); + } else { + flecs_script_eval_error(v, node, + "type %s is a collection (use '%s: [...]')", + id_str, id_str); + } + ecs_os_free(id_str); + return -1; + } } + + ecs_value_t value = { + .ptr = ecs_ensure_id(v->world, src, node->id.eval), + .type = ti->component + }; - if (dotcount) { - ecs_meta_push(cursor); + /* Assign entire value, including members not set by expression. This + * prevents uninitialized or unexpected values. */ + if (!ti->hooks.ctor) { + ecs_os_memset(value.ptr, 0, ti->size); + } else if (ti->hooks.ctor) { + if (ti->hooks.dtor) { + ti->hooks.dtor(value.ptr, 1, ti); + } + ti->hooks.ctor(value.ptr, 1, ti); } - if (ecs_meta_member(cursor, token)) { - goto error; + if (ecs_os_strcmp(node->expr, "{}")) { + if (flecs_script_eval_expr(v, node->expr, &value)) { + return -1; + } } - if (!ptr[0]) { - break; - } + ecs_modified_id(v->world, src, node->id.eval); + } else { + ecs_add_id(v->world, src, node->id.eval); + } - ptr ++; /* Skip . */ + return 0; +} - dotcount ++; +static +int flecs_script_eval_var_component( + ecs_script_eval_visitor_t *v, + ecs_script_var_component_t *node) +{ + ecs_script_var_t *var = ecs_script_vars_lookup(v->vars, node->name); + if (!var) { + flecs_script_eval_error(v, node, + "unresolved variable '%s'", node->name); + return -1; } - cur_scope = flecs_meta_cursor_get_scope(cursor); - if (dotcount) { - cur_scope->prev_depth = prev_depth; + if (v->template) { + return 0; + } + + ecs_id_t var_id = var->value.type; + + if (var->value.ptr) { + const ecs_type_info_t *ti = flecs_script_get_type_info( + v, node, var_id); + if (!ti) { + return -1; + } + + ecs_value_t value = { + .ptr = ecs_ensure_id(v->world, v->entity->eval, var_id), + .type = var_id + }; + + ecs_value_copy_w_type_info(v->world, ti, value.ptr, var->value.ptr); + + ecs_modified_id(v->world, v->entity->eval, var_id); + } else { + ecs_add_id(v->world, v->entity->eval, var_id); } return 0; -error: - return -1; -#else - (void)cursor; - (void)name; - ecs_err("the FLECS_PARSER addon is required for ecs_meta_dotmember"); - return -1; -#endif } -int ecs_meta_push( - ecs_meta_cursor_t *cursor) +static +int flecs_script_eval_default_component( + ecs_script_eval_visitor_t *v, + ecs_script_default_component_t *node) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - const ecs_world_t *world = cursor->world; + if (!v->entity) { + flecs_script_eval_error(v, node, + "missing entity for default component"); + return -1; + } - if (cursor->depth == 0) { - if (!cursor->is_primitive_scope) { - if ((op->kind > EcsOpScope) && (op->count <= 1)) { - cursor->is_primitive_scope = true; - return 0; - } - } + if (v->template) { + return 0; } - void *ptr = flecs_meta_cursor_get_ptr(world, scope); - cursor->depth ++; - ecs_check(cursor->depth < ECS_META_MAX_SCOPE_DEPTH, - ECS_INVALID_PARAMETER, NULL); + ecs_script_scope_t *scope = ecs_script_current_scope(v); + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(scope->node.kind == EcsAstScope, ECS_INTERNAL_ERROR, NULL); + scope = scope->parent; - ecs_meta_scope_t *next_scope = flecs_meta_cursor_get_scope(cursor); + if (!scope) { + flecs_script_eval_error(v, node, + "entity '%s' is in root scope which cannot have a default type", + v->entity->name); + return -1; + } - /* If we're not already in an inline array and this operation is an inline - * array, push a frame for the array. - * Doing this first ensures that inline arrays take precedence over other - * kinds of push operations, such as for a struct element type. */ - if (!scope->is_inline_array && op->count > 1 && !scope->is_collection) { - /* Push a frame just for the element type, with inline_array = true */ - next_scope[0] = (ecs_meta_scope_t){ - .ops = op, - .op_count = op->op_count, - .ptr = scope->ptr, - .type = op->type, - .is_collection = true, - .is_inline_array = true - }; + ecs_id_t default_type = scope->default_component_eval; + if (!default_type) { + flecs_script_eval_error(v, node, + "scope for entity '%s' does not have a default type", + v->entity->name); + return -1; + } - /* With 'is_inline_array' set to true we ensure that we can never push - * the same inline array twice */ - return 0; + if (ecs_get_type_info(v->world, default_type) == NULL) { + char *id_str = ecs_id_str(v->world, default_type); + flecs_script_eval_error(v, node, + "cannot use tag '%s' as default type in assignment", + id_str); + ecs_os_free(id_str); + return -1; } - /* Operation-specific switch behavior */ - switch(op->kind) { + ecs_value_t value = { + .ptr = ecs_ensure_id(v->world, v->entity->eval, default_type), + .type = default_type + }; - /* Struct push: this happens when pushing a struct member. */ - case EcsOpPush: { - const EcsOpaque *opaque = scope->opaque; - if (opaque) { - /* If this is a nested push for an opaque type, push the type of the - * element instead of the next operation. This ensures that we won't - * use flattened offsets for nested members. */ - if (flecs_meta_cursor_push_type( - world, next_scope, op->type, ptr) != 0) - { - goto error; - } + if (flecs_script_eval_expr(v, node->expr, &value)) { + return -1; + } - /* Strip the Push operation since we already pushed */ - next_scope->members = next_scope->ops[0].members; - next_scope->ops = &next_scope->ops[1]; - next_scope->op_count --; - break; - } + ecs_modified_id(v->world, v->entity->eval, default_type); - /* The ops array contains a flattened list for all members and nested - * members of a struct, so we can use (ops + 1) to initialize the ops - * array of the next scope. */ - next_scope[0] = (ecs_meta_scope_t) { - .ops = &op[1], /* op after push */ - .op_count = op->op_count - 1, /* don't include pop */ - .ptr = scope->ptr, - .type = op->type, - .members = op->members - }; - break; - } + return 0; +} - /* Array push for an array type. Arrays can be encoded in 2 ways: either by - * setting the EcsMember::count member to a value >1, or by specifying an - * array type as member type. This is the latter case. */ - case EcsOpArray: { - if (flecs_meta_cursor_push_type(world, next_scope, op->type, ptr) != 0) { - goto error; - } +static +int flecs_script_eval_with_var( + ecs_script_eval_visitor_t *v, + ecs_script_var_node_t *node) +{ + ecs_script_var_t *var = ecs_script_vars_lookup(v->vars, node->name); + if (!var) { + flecs_script_eval_error(v, node, + "unresolved variable '%s'", node->name); + return -1; + } - const EcsArray *type_ptr = ecs_get(world, op->type, EcsArray); - next_scope->type = type_ptr->type; - next_scope->is_collection = true; - break; + if (v->template) { + return 0; } - /* Vector push */ - case EcsOpVector: { - next_scope->vector = ptr; - if (flecs_meta_cursor_push_type(world, next_scope, op->type, NULL) != 0) { - goto error; - } + ecs_allocator_t *a = v->allocator; + ecs_value_t *value = flecs_script_with_append(a, v, NULL); // TODO: vars of non trivial types + *value = var->value; - const EcsVector *type_ptr = ecs_get(world, op->type, EcsVector); - next_scope->type = type_ptr->type; - next_scope->is_collection = true; - break; + return 0; +} + +static +int flecs_script_eval_with_tag( + ecs_script_eval_visitor_t *v, + ecs_script_tag_t *node) +{ + if (flecs_script_eval_id(v, node, &node->id)) { + return -1; } - /* Opaque type push. Depending on the type the opaque type represents the - * scope will be pushed as a struct or collection type. The type information - * of the as_type is retained, as this is important for type checking and - * for nested opaque type support. */ - case EcsOpOpaque: { - const EcsOpaque *type_ptr = ecs_get(world, op->type, EcsOpaque); - ecs_entity_t as_type = type_ptr->as_type; - const EcsMetaType *mtype_ptr = ecs_get(world, as_type, EcsMetaType); + if (v->template) { + return 0; + } - /* Check what kind of type the opaque type represents */ - switch(mtype_ptr->kind) { + ecs_allocator_t *a = v->allocator; + ecs_value_t *value = flecs_script_with_append(a, v, NULL); + value->type = node->id.eval; + value->ptr = NULL; - /* Opaque vector support */ - case EcsVectorType: { - const EcsVector *vt = ecs_get(world, type_ptr->as_type, EcsVector); - next_scope->type = vt->type; + return 0; +} - /* Push the element type of the vector type */ - if (flecs_meta_cursor_push_type( - world, next_scope, vt->type, NULL) != 0) - { - goto error; - } +static +int flecs_script_eval_with_component( + ecs_script_eval_visitor_t *v, + ecs_script_component_t *node) +{ + if (flecs_script_eval_id(v, node, &node->id)) { + return -1; + } - /* This tracks whether any data was assigned inside the scope. When - * the scope is popped, and is_empty_scope is still true, the vector - * will be resized to 0. */ - next_scope->is_empty_scope = true; - next_scope->is_collection = true; - break; - } + if (v->template) { + return 0; + } - /* Opaque array support */ - case EcsArrayType: { - const EcsArray *at = ecs_get(world, type_ptr->as_type, EcsArray); - next_scope->type = at->type; + ecs_allocator_t *a = v->allocator; + const ecs_type_info_t *ti = flecs_script_get_type_info( + v, node, node->id.eval); - /* Push the element type of the array type */ - if (flecs_meta_cursor_push_type( - world, next_scope, at->type, NULL) != 0) - { - goto error; - } + ecs_value_t *value = flecs_script_with_append(a, v, ti); + value->type = node->id.eval; + value->ptr = NULL; - /* Arrays are always a fixed size */ - next_scope->is_empty_scope = false; - next_scope->is_collection = true; - break; + if (node->expr && node->expr[0]) { + if (!ti) { + return -1; } - /* Opaque struct support */ - case EcsStructType: - /* Push struct type that represents the opaque type. This ensures - * that the deserializer retains information about members and - * member types, which is necessary for nested opaque types, and - * allows for error checking. */ - if (flecs_meta_cursor_push_type( - world, next_scope, as_type, NULL) != 0) - { - goto error; - } + value->ptr = flecs_stack_alloc(&v->stack, ti->size, ti->alignment); + value->type = ti->component; // Expression parser needs actual type - /* Strip push op, since we already pushed */ - next_scope->members = next_scope->ops[0].members; - next_scope->ops = &next_scope->ops[1]; - next_scope->op_count --; - break; - - case EcsPrimitiveType: - case EcsEnumType: - case EcsBitmaskType: - case EcsOpaqueType: - default: - break; + if (ti->hooks.ctor) { + ti->hooks.ctor(value->ptr, 1, ti); } - next_scope->ptr = ptr; - next_scope->opaque = type_ptr; - break; + if (flecs_script_eval_expr(v, node->expr, value)) { + return -1; + } + + value->type = node->id.eval; // Restore so we're adding actual id } - case EcsOpPop: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpString: - case EcsOpEntity: - case EcsOpId: { - char *path = ecs_get_fullpath(world, scope->type); - ecs_err("invalid push for type '%s'", path); - ecs_os_free(path); + return 0; +} + +static +int flecs_script_eval_with( + ecs_script_eval_visitor_t *v, + ecs_script_with_t *node) +{ + ecs_allocator_t *a = v->allocator; + int32_t prev_with_count = flecs_script_with_count(v); + ecs_stack_cursor_t *prev_stack_cursor = flecs_stack_get_cursor(&v->stack); + int result = 0; + + if (ecs_script_visit_scope(v, node->expressions)) { + result = -1; goto error; } - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + + ecs_value_t *value = flecs_script_with_last(v); + if (!value->ptr) { + if (ecs_is_valid(v->world, value->type)) { + node->scope->default_component_eval = value->type; + } } - if (scope->is_collection && !scope->opaque) { - next_scope->ptr = ECS_OFFSET(next_scope->ptr, - scope->elem_cur * get_size(world, scope)); + if (ecs_script_visit_scope(v, node->scope)) { + result = -1; + goto error; } - return 0; error: - return -1; + flecs_script_with_set_count(a, v, prev_with_count); + flecs_stack_restore_cursor(&v->stack, prev_stack_cursor); + return result; } -int ecs_meta_pop( - ecs_meta_cursor_t *cursor) +static +int flecs_script_eval_using( + ecs_script_eval_visitor_t *v, + ecs_script_using_t *node) { - if (cursor->is_primitive_scope) { - cursor->is_primitive_scope = false; - return 0; - } + ecs_allocator_t *a = v->allocator; + int32_t len = ecs_os_strlen(node->name); - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - scope = flecs_meta_cursor_restore_scope(cursor, scope); - cursor->depth --; - if (cursor->depth < 0) { - ecs_err("unexpected end of scope"); - return -1; - } + if (len > 2 && !ecs_os_strcmp(&node->name[len - 2], ".*")) { + char *path = flecs_strdup(a, node->name); + path[len - 2] = '\0'; - ecs_meta_scope_t *next_scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(next_scope); + ecs_entity_t from = ecs_lookup(v->world, path); + if (!from) { + flecs_script_eval_error(v, node, + "unresolved path '%s' in using statement", path); + flecs_strfree(a, path); + return -1; + } - if (!scope->is_inline_array) { - if (op->kind == EcsOpPush) { - next_scope->op_cur += op->op_count - 1; + /* Add each child of the scope to using stack */ + ecs_iter_t it = ecs_children(v->world, from); + while (ecs_children_next(&it)) { + int32_t i, count = it.count; + for (i = 0; i < count; i ++) { + ecs_vec_append_t( + a, &v->using, ecs_entity_t)[0] = it.entities[i]; + } + } - /* push + op_count should point to the operation after pop */ - op = flecs_meta_cursor_get_op(next_scope); - ecs_assert(op->kind == EcsOpPop, ECS_INTERNAL_ERROR, NULL); - } else if (op->kind == EcsOpArray || op->kind == EcsOpVector) { - /* Collection type, nothing else to do */ - } else if (op->kind == EcsOpOpaque) { - const EcsOpaque *opaque = scope->opaque; - if (scope->is_collection) { - const EcsMetaType *mtype = ecs_get(cursor->world, - opaque->as_type, EcsMetaType); - ecs_assert(mtype != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_strfree(a, path); + } else { + ecs_entity_t from = ecs_lookup_path_w_sep( + v->world, 0, node->name, NULL, NULL, false); + if (!from) { + from = ecs_entity(v->world, { + .name = node->name, + .root_sep = "" + }); - /* When popping a opaque collection type, call resize to make - * sure the vector isn't larger than the number of elements we - * deserialized. - * If the opaque type represents an array, don't call resize. */ - if (mtype->kind != EcsArrayType) { - ecs_assert(opaque != NULL, ECS_INTERNAL_ERROR, NULL); - if (!opaque->resize) { - char *str = ecs_get_fullpath(cursor->world, scope->type); - ecs_err("missing resize for opaque type %s", str); - ecs_os_free(str); - return -1; - } - if (scope->is_empty_scope) { - /* If no values were serialized for scope, resize - * collection to 0 elements. */ - ecs_assert(!scope->elem_cur, ECS_INTERNAL_ERROR, NULL); - opaque->resize(scope->ptr, 0); - } else { - /* Otherwise resize collection to the index of the last - * deserialized element + 1 */ - opaque->resize(scope->ptr, - flecs_ito(size_t, scope->elem_cur + 1)); - } - } - } else { - /* Opaque struct type, nothing to be done */ + if (!from) { + return -1; } - } else { - /* should not have been able to push if the previous scope was not - * a complex or collection type */ - ecs_assert(false, ECS_INTERNAL_ERROR, NULL); } + + ecs_vec_append_t(a, &v->using, ecs_entity_t)[0] = from; } return 0; } -bool ecs_meta_is_collection( - const ecs_meta_cursor_t *cursor) +static +int flecs_script_eval_module( + ecs_script_eval_visitor_t *v, + ecs_script_module_t *node) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - return scope->is_collection; -} + ecs_entity_t m = flecs_script_create_entity(v, node->name); + if (!m) { + return -1; + } -ecs_entity_t ecs_meta_get_type( - const ecs_meta_cursor_t *cursor) -{ - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - return op->type; + ecs_add_id(v->world, m, EcsModule); + + v->module = m; + v->parent = m; + + return 0; } -ecs_entity_t ecs_meta_get_unit( - const ecs_meta_cursor_t *cursor) +static +int flecs_script_eval_const( + ecs_script_eval_visitor_t *v, + ecs_script_var_node_t *node) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_entity_t type = scope->type; - const EcsStruct *st = ecs_get(cursor->world, type, EcsStruct); - if (!st) { - return 0; + ecs_script_var_t *var = ecs_script_vars_declare(v->vars, node->name); + if (!var) { + flecs_script_eval_error(v, node, + "variable '%s' redeclared", node->name); + return -1; } - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - ecs_member_t *m = ecs_vec_get_t( - &st->members, ecs_member_t, op->member_index); + if (node->type) { + ecs_entity_t type = flecs_script_find_entity(v, 0, node->type); + if (!type) { + flecs_script_eval_error(v, node, + "unresolved type '%s' for const variable '%s'", + node->type, node->name); + return -1; + } - return m->unit; -} + const ecs_type_info_t *ti = flecs_script_get_type_info(v, node, type); + if (!ti) { + flecs_script_eval_error(v, node, + "failed to retrieve type info for '%s' for const variable '%s'", + node->type, node->name); + return -1; + } -const char* ecs_meta_get_member( - const ecs_meta_cursor_t *cursor) -{ - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - return op->name; -} + var->value.ptr = flecs_stack_calloc(&v->stack, ti->size, ti->alignment); + var->value.type = type; + var->type_info = ti; -ecs_entity_t ecs_meta_get_member_id( - const ecs_meta_cursor_t *cursor) -{ - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_entity_t type = scope->type; - const EcsStruct *st = ecs_get(cursor->world, type, EcsStruct); - if (!st) { - return 0; - } + if (ti->hooks.ctor) { + ti->hooks.ctor(var->value.ptr, 1, ti); + } - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - ecs_member_t *m = ecs_vec_get_t( - &st->members, ecs_member_t, op->member_index); + if (flecs_script_eval_expr(v, node->expr, &var->value)) { + flecs_script_eval_error(v, node, + "failed to evaluate expression for const variable '%s'", + node->name); + return -1; + } + } else { + /* We don't know the type yet, so we can't create a storage for it yet. + * Run the expression first to deduce the type. */ + ecs_value_t value = {0}; + if (flecs_script_eval_expr(v, node->expr, &value)) { + flecs_script_eval_error(v, node, + "failed to evaluate expression for const variable '%s'", + node->name); + return -1; + } - return m->member; -} + ecs_assert(value.type != 0, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *ti = ecs_get_type_info(v->world, value.type); + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); -/* Utilities for type conversions and bounds checking */ -static struct { - int64_t min, max; -} ecs_meta_bounds_signed[EcsMetaTypeOpKindLast + 1] = { - [EcsOpBool] = {false, true}, - [EcsOpChar] = {INT8_MIN, INT8_MAX}, - [EcsOpByte] = {0, UINT8_MAX}, - [EcsOpU8] = {0, UINT8_MAX}, - [EcsOpU16] = {0, UINT16_MAX}, - [EcsOpU32] = {0, UINT32_MAX}, - [EcsOpU64] = {0, INT64_MAX}, - [EcsOpI8] = {INT8_MIN, INT8_MAX}, - [EcsOpI16] = {INT16_MIN, INT16_MAX}, - [EcsOpI32] = {INT32_MIN, INT32_MAX}, - [EcsOpI64] = {INT64_MIN, INT64_MAX}, - [EcsOpUPtr] = {0, ((sizeof(void*) == 4) ? UINT32_MAX : INT64_MAX)}, - [EcsOpIPtr] = { - ((sizeof(void*) == 4) ? INT32_MIN : INT64_MIN), - ((sizeof(void*) == 4) ? INT32_MAX : INT64_MAX) - }, - [EcsOpEntity] = {0, INT64_MAX}, - [EcsOpId] = {0, INT64_MAX}, - [EcsOpEnum] = {INT32_MIN, INT32_MAX}, - [EcsOpBitmask] = {0, INT32_MAX} -}; + var->value.ptr = flecs_stack_calloc(&v->stack, ti->size, ti->alignment); + var->value.type = value.type; + var->type_info = ti; -static struct { - uint64_t min, max; -} ecs_meta_bounds_unsigned[EcsMetaTypeOpKindLast + 1] = { - [EcsOpBool] = {false, true}, - [EcsOpChar] = {0, INT8_MAX}, - [EcsOpByte] = {0, UINT8_MAX}, - [EcsOpU8] = {0, UINT8_MAX}, - [EcsOpU16] = {0, UINT16_MAX}, - [EcsOpU32] = {0, UINT32_MAX}, - [EcsOpU64] = {0, UINT64_MAX}, - [EcsOpI8] = {0, INT8_MAX}, - [EcsOpI16] = {0, INT16_MAX}, - [EcsOpI32] = {0, INT32_MAX}, - [EcsOpI64] = {0, INT64_MAX}, - [EcsOpUPtr] = {0, ((sizeof(void*) == 4) ? UINT32_MAX : UINT64_MAX)}, - [EcsOpIPtr] = {0, ((sizeof(void*) == 4) ? INT32_MAX : INT64_MAX)}, - [EcsOpEntity] = {0, UINT64_MAX}, - [EcsOpId] = {0, UINT64_MAX}, - [EcsOpEnum] = {0, INT32_MAX}, - [EcsOpBitmask] = {0, UINT32_MAX} -}; + if (ti->hooks.ctor) { + ti->hooks.ctor(var->value.ptr, 1, ti); + } -static struct { - double min, max; -} ecs_meta_bounds_float[EcsMetaTypeOpKindLast + 1] = { - [EcsOpBool] = {false, true}, - [EcsOpChar] = {INT8_MIN, INT8_MAX}, - [EcsOpByte] = {0, UINT8_MAX}, - [EcsOpU8] = {0, UINT8_MAX}, - [EcsOpU16] = {0, UINT16_MAX}, - [EcsOpU32] = {0, UINT32_MAX}, - [EcsOpU64] = {0, (double)UINT64_MAX}, - [EcsOpI8] = {INT8_MIN, INT8_MAX}, - [EcsOpI16] = {INT16_MIN, INT16_MAX}, - [EcsOpI32] = {INT32_MIN, INT32_MAX}, - [EcsOpI64] = {INT64_MIN, (double)INT64_MAX}, - [EcsOpUPtr] = {0, ((sizeof(void*) == 4) ? UINT32_MAX : (double)UINT64_MAX)}, - [EcsOpIPtr] = { - ((sizeof(void*) == 4) ? INT32_MIN : (double)INT64_MIN), - ((sizeof(void*) == 4) ? INT32_MAX : (double)INT64_MAX) - }, - [EcsOpEntity] = {0, (double)UINT64_MAX}, - [EcsOpId] = {0, (double)UINT64_MAX}, - [EcsOpEnum] = {INT32_MIN, INT32_MAX}, - [EcsOpBitmask] = {0, UINT32_MAX} -}; + ecs_value_copy_w_type_info(v->world, ti, var->value.ptr, value.ptr); + ecs_value_fini_w_type_info(v->world, ti, value.ptr); + flecs_free(&v->world->allocator, ti->size, value.ptr); + } -#define set_T(T, ptr, value)\ - ((T*)ptr)[0] = ((T)value) + return 0; +} -#define case_T(kind, T, dst, src)\ -case kind:\ - set_T(T, dst, src);\ - break +static +int flecs_script_eval_pair_scope( + ecs_script_eval_visitor_t *v, + ecs_script_pair_scope_t *node) +{ + ecs_entity_t first = flecs_script_find_entity(v, 0, node->id.first); + if (!first) { + first = flecs_script_create_entity(v, node->id.first); + if (!first) { + return -1; + } + } -#define case_T_checked(kind, T, dst, src, bounds)\ -case kind:\ - if ((src < bounds[kind].min) || (src > bounds[kind].max)){\ - ecs_err("value %.0f is out of bounds for type %s", (double)src,\ - flecs_meta_op_kind_str(kind));\ - return -1;\ - }\ - set_T(T, dst, src);\ - break + ecs_entity_t second = flecs_script_create_entity(v, node->id.second); + if (!second) { + return -1; + } -#define cases_T_float(dst, src)\ - case_T(EcsOpF32, ecs_f32_t, dst, src);\ - case_T(EcsOpF64, ecs_f64_t, dst, src) + ecs_allocator_t *a = v->allocator; + ecs_entity_t prev_first = v->with_relationship; + ecs_entity_t prev_second = 0; + int32_t prev_with_relationship_sp = v->with_relationship_sp; -#define cases_T_signed(dst, src, bounds)\ - case_T_checked(EcsOpChar, ecs_char_t, dst, src, bounds);\ - case_T_checked(EcsOpI8, ecs_i8_t, dst, src, bounds);\ - case_T_checked(EcsOpI16, ecs_i16_t, dst, src, bounds);\ - case_T_checked(EcsOpI32, ecs_i32_t, dst, src, bounds);\ - case_T_checked(EcsOpI64, ecs_i64_t, dst, src, bounds);\ - case_T_checked(EcsOpIPtr, ecs_iptr_t, dst, src, bounds);\ - case_T_checked(EcsOpEnum, ecs_i32_t, dst, src, bounds) + v->with_relationship = first; -#define cases_T_unsigned(dst, src, bounds)\ - case_T_checked(EcsOpByte, ecs_byte_t, dst, src, bounds);\ - case_T_checked(EcsOpU8, ecs_u8_t, dst, src, bounds);\ - case_T_checked(EcsOpU16, ecs_u16_t, dst, src, bounds);\ - case_T_checked(EcsOpU32, ecs_u32_t, dst, src, bounds);\ - case_T_checked(EcsOpU64, ecs_u64_t, dst, src, bounds);\ - case_T_checked(EcsOpUPtr, ecs_uptr_t, dst, src, bounds);\ - case_T_checked(EcsOpEntity, ecs_u64_t, dst, src, bounds);\ - case_T_checked(EcsOpId, ecs_u64_t, dst, src, bounds);\ - case_T_checked(EcsOpBitmask, ecs_u32_t, dst, src, bounds) + if (prev_first != first) { + /* Append new element to with stack */ + ecs_value_t *value = flecs_script_with_append(a, v, NULL); + value->type = ecs_pair(first, second); + value->ptr = NULL; + v->with_relationship_sp = flecs_script_with_count(v) - 1; + } else { + /* Get existing with element for current relationhip stack */ + ecs_value_t *value = ecs_vec_get_t( + &v->with, ecs_value_t, v->with_relationship_sp); + ecs_assert(ECS_PAIR_FIRST(value->type) == (uint32_t)first, + ECS_INTERNAL_ERROR, NULL); + prev_second = ECS_PAIR_SECOND(value->type); + value->type = ecs_pair(first, second); + value->ptr = NULL; + } -#define cases_T_bool(dst, src)\ -case EcsOpBool:\ - set_T(ecs_bool_t, dst, value != 0);\ - break + if (ecs_script_visit_scope(v, node->scope)) { + return -1; + } -static -void flecs_meta_conversion_error( - ecs_meta_cursor_t *cursor, - ecs_meta_type_op_t *op, - const char *from) -{ - if (op->kind == EcsOpPop) { - ecs_err("cursor: out of bounds"); + if (prev_second) { + ecs_value_t *value = ecs_vec_get_t( + &v->with, ecs_value_t, v->with_relationship_sp); + value->type = ecs_pair(first, prev_second); } else { - char *path = ecs_get_fullpath(cursor->world, op->type); - ecs_err("unsupported conversion from %s to '%s'", from, path); - ecs_os_free(path); + flecs_script_with_set_count(a, v, v->with_relationship_sp); } + + v->with_relationship = prev_first; + v->with_relationship_sp = prev_with_relationship_sp; + + return 0; } -int ecs_meta_set_bool( - ecs_meta_cursor_t *cursor, - bool value) +static +int flecs_script_eval_if( + ecs_script_eval_visitor_t *v, + ecs_script_if_t *node) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + ecs_value_t condval = { .type = 0, .ptr = NULL }; + if (flecs_script_eval_expr(v, node->expr, &condval)) { + return -1; + } - switch(op->kind) { - cases_T_bool(ptr, value); - cases_T_signed(ptr, value, ecs_meta_bounds_signed); - cases_T_unsigned(ptr, value, ecs_meta_bounds_unsigned); - case EcsOpOpaque: { - const EcsOpaque *ot = ecs_get(cursor->world, op->type, EcsOpaque); - if (ot && ot->assign_bool) { - ot->assign_bool(ptr, value); - break; - } + bool cond; + if (condval.type == ecs_id(ecs_bool_t)) { + cond = *(bool*)(condval.ptr); + } else { + ecs_meta_cursor_t cur = ecs_meta_cursor( + v->world, condval.type, condval.ptr); + cond = ecs_meta_get_bool(&cur); } - /* fall through */ - case EcsOpArray: - case EcsOpVector: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpPrimitive: - case EcsOpF32: - case EcsOpF64: - case EcsOpString: - flecs_meta_conversion_error(cursor, op, "bool"); + + ecs_value_free(v->world, condval.type, condval.ptr); + + if (flecs_script_eval_scope(v, cond ? node->if_true : node->if_false)) { return -1; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); } return 0; -error: - return -1; } -int ecs_meta_set_char( - ecs_meta_cursor_t *cursor, - char value) +static +int flecs_script_eval_annot( + ecs_script_eval_visitor_t *v, + ecs_script_annot_t *node) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - - switch(op->kind) { - cases_T_bool(ptr, value); - cases_T_signed(ptr, value, ecs_meta_bounds_signed); - case EcsOpOpaque: { - const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); - if (opaque->assign_char) { /* preferred operation */ - opaque->assign_char(ptr, value); - break; - } else if (opaque->assign_uint) { - opaque->assign_uint(ptr, (uint64_t)value); - break; - } else if (opaque->assign_int) { - opaque->assign_int(ptr, value); - break; - } + if (!v->base.next) { + flecs_script_eval_error(v, node, + "annotation '%s' is not applied to anything", node->name); + return -1; } - /* fall through */ - case EcsOpArray: - case EcsOpVector: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpString: - case EcsOpEntity: - case EcsOpId: - flecs_meta_conversion_error(cursor, op, "char"); + + ecs_script_node_kind_t kind = v->base.next->kind; + if (kind != EcsAstEntity && kind != EcsAstAnnotation) { + flecs_script_eval_error(v, node, + "annotation must be applied to an entity"); return -1; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); } + ecs_allocator_t *a = v->allocator; + ecs_vec_append_t(a, &v->annot, ecs_script_annot_t*)[0] = node; + return 0; -error: - return -1; } -int ecs_meta_set_int( - ecs_meta_cursor_t *cursor, - int64_t value) +int flecs_script_eval_node( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node) +{ + switch(node->kind) { + case EcsAstScope: + return flecs_script_eval_scope( + v, (ecs_script_scope_t*)node); + case EcsAstTag: + return flecs_script_eval_tag( + v, (ecs_script_tag_t*)node); + case EcsAstComponent: + return flecs_script_eval_component( + v, (ecs_script_component_t*)node); + case EcsAstVarComponent: + return flecs_script_eval_var_component( + v, (ecs_script_var_component_t*)node); + case EcsAstDefaultComponent: + return flecs_script_eval_default_component( + v, (ecs_script_default_component_t*)node); + case EcsAstWithVar: + return flecs_script_eval_with_var( + v, (ecs_script_var_node_t*)node); + case EcsAstWithTag: + return flecs_script_eval_with_tag( + v, (ecs_script_tag_t*)node); + case EcsAstWithComponent: + return flecs_script_eval_with_component( + v, (ecs_script_component_t*)node); + case EcsAstWith: + return flecs_script_eval_with( + v, (ecs_script_with_t*)node); + case EcsAstUsing: + return flecs_script_eval_using( + v, (ecs_script_using_t*)node); + case EcsAstModule: + return flecs_script_eval_module( + v, (ecs_script_module_t*)node); + case EcsAstAnnotation: + return flecs_script_eval_annot( + v, (ecs_script_annot_t*)node); + case EcsAstTemplate: + return flecs_script_eval_template( + v, (ecs_script_template_node_t*)node); + case EcsAstProp: + return 0; + case EcsAstConst: + return flecs_script_eval_const( + v, (ecs_script_var_node_t*)node); + case EcsAstEntity: + return flecs_script_eval_entity( + v, (ecs_script_entity_t*)node); + case EcsAstPairScope: + return flecs_script_eval_pair_scope( + v, (ecs_script_pair_scope_t*)node); + case EcsAstIf: + return flecs_script_eval_if( + v, (ecs_script_if_t*)node); + } + + ecs_abort(ECS_INTERNAL_ERROR, "corrupt AST node kind"); +} + +void flecs_script_eval_visit_init( + ecs_script_impl_t *script, + ecs_script_eval_visitor_t *v) +{ + *v = (ecs_script_eval_visitor_t){ + .base = { + .script = script, + .visit = (ecs_visit_action_t)flecs_script_eval_node + }, + .world = script->pub.world, + .allocator = &script->allocator + }; + + flecs_stack_init(&v->stack); + ecs_vec_init_t(v->allocator, &v->using, ecs_entity_t, 0); + ecs_vec_init_t(v->allocator, &v->with, ecs_value_t, 0); + ecs_vec_init_t(v->allocator, &v->with_type_info, ecs_type_info_t*, 0); + ecs_vec_init_t(v->allocator, &v->annot, ecs_script_annot_t*, 0); + + /* Always include flecs.meta */ + ecs_vec_append_t(v->allocator, &v->using, ecs_entity_t)[0] = + ecs_lookup(v->world, "flecs.meta"); +} + +void flecs_script_eval_visit_fini( + ecs_script_eval_visitor_t *v) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + ecs_vec_fini_t(v->allocator, &v->annot, ecs_script_annot_t*); + ecs_vec_fini_t(v->allocator, &v->with, ecs_value_t); + ecs_vec_fini_t(v->allocator, &v->with_type_info, ecs_type_info_t*); + ecs_vec_fini_t(v->allocator, &v->using, ecs_entity_t); + flecs_stack_fini(&v->stack); +} - switch(op->kind) { - cases_T_bool(ptr, value); - cases_T_signed(ptr, value, ecs_meta_bounds_signed); - cases_T_unsigned(ptr, value, ecs_meta_bounds_signed); - cases_T_float(ptr, value); - case EcsOpOpaque: { - const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); - if (opaque->assign_int) { /* preferred operation */ - opaque->assign_int(ptr, value); - break; - } else if (opaque->assign_float) { /* most expressive */ - opaque->assign_float(ptr, (double)value); - break; - } else if (opaque->assign_uint && (value > 0)) { - opaque->assign_uint(ptr, flecs_ito(uint64_t, value)); - break; - } else if (opaque->assign_char && (value > 0) && (value < 256)) { - opaque->assign_char(ptr, flecs_ito(char, value)); - break; - } - } - /* fall through */ - case EcsOpArray: - case EcsOpVector: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpPrimitive: - case EcsOpString: { - if(!value) return ecs_meta_set_null(cursor); - flecs_meta_conversion_error(cursor, op, "int"); - return -1; - } - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); +int ecs_script_eval( + ecs_script_t *script) +{ + ecs_script_eval_visitor_t v; + ecs_script_impl_t *impl = flecs_script_impl(script); + flecs_script_eval_visit_init(impl, &v); + int result = ecs_script_visit(impl, &v, flecs_script_eval_node); + flecs_script_eval_visit_fini(&v); + return result; +} + +#endif + +/** + * @file addons/script/visit_free.c + * @brief Script free visitor (frees AST resources). + */ + + +#ifdef FLECS_SCRIPT + +static +void flecs_script_scope_free( + ecs_script_visit_t *v, + ecs_script_scope_t *node) +{ + ecs_script_visit_scope(v, node); + ecs_vec_fini_t(&v->script->allocator, &node->stmts, ecs_script_node_t*); + flecs_free_t(&v->script->allocator, ecs_script_scope_t, node); +} + +static +void flecs_script_with_free( + ecs_script_visit_t *v, + ecs_script_with_t *node) +{ + flecs_script_scope_free(v, node->expressions); + flecs_script_scope_free(v, node->scope); +} + +static +void flecs_script_template_free( + ecs_script_visit_t *v, + ecs_script_template_node_t *node) +{ + flecs_script_scope_free(v, node->scope); +} + +static +void flecs_script_entity_free( + ecs_script_visit_t *v, + ecs_script_entity_t *node) +{ + flecs_script_scope_free(v, node->scope); +} + +static +void flecs_script_pair_scope_free( + ecs_script_visit_t *v, + ecs_script_pair_scope_t *node) +{ + flecs_script_scope_free(v, node->scope); +} + +static +void flecs_script_if_free( + ecs_script_visit_t *v, + ecs_script_if_t *node) +{ + flecs_script_scope_free(v, node->if_true); + flecs_script_scope_free(v, node->if_false); +} + +static +int flecs_script_stmt_free( + ecs_script_visit_t *v, + ecs_script_node_t *node) +{ + ecs_allocator_t *a = &v->script->allocator; + switch(node->kind) { + case EcsAstScope: + flecs_script_scope_free(v, (ecs_script_scope_t*)node); + break; + case EcsAstWith: + flecs_script_with_free(v, (ecs_script_with_t*)node); + flecs_free_t(a, ecs_script_with_t, node); + break; + case EcsAstTemplate: + flecs_script_template_free(v, (ecs_script_template_node_t*)node); + flecs_free_t(a, ecs_script_template_node_t, node); + break; + case EcsAstEntity: + flecs_script_entity_free(v, (ecs_script_entity_t*)node); + flecs_free_t(a, ecs_script_entity_t, node); + break; + case EcsAstPairScope: + flecs_script_pair_scope_free(v, (ecs_script_pair_scope_t*)node); + flecs_free_t(a, ecs_script_pair_scope_t, node); + break; + case EcsAstIf: + flecs_script_if_free(v, (ecs_script_if_t*)node); + flecs_free_t(a, ecs_script_if_t, node); + break; + case EcsAstTag: + flecs_free_t(a, ecs_script_tag_t, node); + break; + case EcsAstComponent: + flecs_free_t(a, ecs_script_component_t, node); + break; + case EcsAstDefaultComponent: + flecs_free_t(a, ecs_script_default_component_t, node); + break; + case EcsAstVarComponent: + flecs_free_t(a, ecs_script_var_component_t, node); + break; + case EcsAstWithVar: + flecs_free_t(a, ecs_script_var_component_t, node); + break; + case EcsAstWithTag: + flecs_free_t(a, ecs_script_tag_t, node); + break; + case EcsAstWithComponent: + flecs_free_t(a, ecs_script_component_t, node); + break; + case EcsAstUsing: + flecs_free_t(a, ecs_script_using_t, node); + break; + case EcsAstModule: + flecs_free_t(a, ecs_script_module_t, node); + break; + case EcsAstAnnotation: + flecs_free_t(a, ecs_script_annot_t, node); + break; + case EcsAstProp: + case EcsAstConst: + flecs_free_t(a, ecs_script_var_node_t, node); + break; } return 0; -error: - return -1; } -int ecs_meta_set_uint( - ecs_meta_cursor_t *cursor, - uint64_t value) +int flecs_script_visit_free( + ecs_script_t *script) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + ecs_check(script != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_script_visit_t v = { + .script = flecs_script_impl(script) + }; - switch(op->kind) { - cases_T_bool(ptr, value); - cases_T_signed(ptr, value, ecs_meta_bounds_unsigned); - cases_T_unsigned(ptr, value, ecs_meta_bounds_unsigned); - cases_T_float(ptr, value); - case EcsOpOpaque: { - const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); - if (opaque->assign_uint) { /* preferred operation */ - opaque->assign_uint(ptr, value); - break; - } else if (opaque->assign_float) { /* most expressive */ - opaque->assign_float(ptr, (double)value); - break; - } else if (opaque->assign_int && (value < INT64_MAX)) { - opaque->assign_int(ptr, flecs_uto(int64_t, value)); - break; - } else if (opaque->assign_char && (value < 256)) { - opaque->assign_char(ptr, flecs_uto(char, value)); - break; - } - } - /* fall through */ - case EcsOpArray: - case EcsOpVector: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpPrimitive: - case EcsOpString: - if(!value) return ecs_meta_set_null(cursor); - flecs_meta_conversion_error(cursor, op, "uint"); - return -1; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + if (ecs_script_visit( + flecs_script_impl(script), &v, flecs_script_stmt_free)) + { + goto error; } return 0; error: - return -1; + return - 1; } -int ecs_meta_set_float( - ecs_meta_cursor_t *cursor, - double value) +#endif + +/** + * @file addons/script/visit_to_str.c + * @brief Script AST to string visitor. + */ + + +#ifdef FLECS_SCRIPT + +typedef struct ecs_script_str_visitor_t { + ecs_script_visit_t base; + ecs_strbuf_t *buf; + int32_t depth; + bool newline; +} ecs_script_str_visitor_t; + +static +int flecs_script_scope_to_str( + ecs_script_str_visitor_t *v, + ecs_script_scope_t *scope); + +static +void flecs_scriptbuf_append( + ecs_script_str_visitor_t *v, + const char *fmt, + ...) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + if (v->newline) { + ecs_strbuf_append(v->buf, "%*s", v->depth * 2, ""); + v->newline = false; + } - switch(op->kind) { - case EcsOpBool: - if (ECS_EQZERO(value)) { - set_T(bool, ptr, false); - } else { - set_T(bool, ptr, true); - } - break; - cases_T_signed(ptr, value, ecs_meta_bounds_float); - cases_T_unsigned(ptr, value, ecs_meta_bounds_float); - cases_T_float(ptr, value); - case EcsOpOpaque: { - const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); - if (opaque->assign_float) { /* preferred operation */ - opaque->assign_float(ptr, value); - break; - } else if (opaque->assign_int && /* most expressive */ - (value <= (double)INT64_MAX) && (value >= (double)INT64_MIN)) - { - opaque->assign_int(ptr, (int64_t)value); - break; - } else if (opaque->assign_uint && (value >= 0)) { - opaque->assign_uint(ptr, (uint64_t)value); - break; - } else if (opaque->assign_entity && (value >= 0)) { - opaque->assign_entity( - ptr, ECS_CONST_CAST(ecs_world_t*, cursor->world), - (ecs_entity_t)value); - break; - } + va_list args; + va_start(args, fmt); + ecs_strbuf_vappend(v->buf, fmt, args); + va_end(args); + + if (fmt[strlen(fmt) - 1] == '\n') { + v->newline = true; } - /* fall through */ - case EcsOpArray: - case EcsOpVector: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpPrimitive: - case EcsOpString: - flecs_meta_conversion_error(cursor, op, "float"); - return -1; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); +} + +static +void flecs_scriptbuf_appendstr( + ecs_script_str_visitor_t *v, + const char *str) +{ + if (v->newline) { + ecs_strbuf_append(v->buf, "%*s", v->depth * 2, ""); + v->newline = false; } - return 0; -error: - return -1; + ecs_strbuf_appendstr(v->buf, str); + + if (str[strlen(str) - 1] == '\n') { + v->newline = true; + } } -int ecs_meta_set_value( - ecs_meta_cursor_t *cursor, - const ecs_value_t *value) +static +void flecs_script_id_to_str( + ecs_script_str_visitor_t *v, + ecs_script_id_t *id) { - ecs_check(value != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_entity_t type = value->type; - ecs_check(type != 0, ECS_INVALID_PARAMETER, NULL); - const EcsMetaType *mt = ecs_get(cursor->world, type, EcsMetaType); - if (!mt) { - ecs_err("type of value does not have reflection data"); - return -1; + if (id->flag) { + if (id->flag == ECS_AUTO_OVERRIDE) { + flecs_scriptbuf_appendstr(v, "auto_override | "); + } else { + flecs_scriptbuf_appendstr(v, "??? | "); + } } - if (mt->kind == EcsPrimitiveType) { - const EcsPrimitive *prim = ecs_get(cursor->world, type, EcsPrimitive); - ecs_check(prim != NULL, ECS_INTERNAL_ERROR, NULL); - switch(prim->kind) { - case EcsBool: return ecs_meta_set_bool(cursor, *(bool*)value->ptr); - case EcsChar: return ecs_meta_set_char(cursor, *(char*)value->ptr); - case EcsByte: return ecs_meta_set_uint(cursor, *(uint8_t*)value->ptr); - case EcsU8: return ecs_meta_set_uint(cursor, *(uint8_t*)value->ptr); - case EcsU16: return ecs_meta_set_uint(cursor, *(uint16_t*)value->ptr); - case EcsU32: return ecs_meta_set_uint(cursor, *(uint32_t*)value->ptr); - case EcsU64: return ecs_meta_set_uint(cursor, *(uint64_t*)value->ptr); - case EcsI8: return ecs_meta_set_int(cursor, *(int8_t*)value->ptr); - case EcsI16: return ecs_meta_set_int(cursor, *(int16_t*)value->ptr); - case EcsI32: return ecs_meta_set_int(cursor, *(int32_t*)value->ptr); - case EcsI64: return ecs_meta_set_int(cursor, *(int64_t*)value->ptr); - case EcsF32: return ecs_meta_set_float(cursor, (double)*(float*)value->ptr); - case EcsF64: return ecs_meta_set_float(cursor, *(double*)value->ptr); - case EcsUPtr: return ecs_meta_set_uint(cursor, *(uintptr_t*)value->ptr); - case EcsIPtr: return ecs_meta_set_int(cursor, *(intptr_t*)value->ptr); - case EcsString: return ecs_meta_set_string(cursor, *(char**)value->ptr); - case EcsEntity: return ecs_meta_set_entity(cursor, *(ecs_entity_t*)value->ptr); - case EcsId: return ecs_meta_set_id(cursor, *(ecs_id_t*)value->ptr); - default: - ecs_throw(ECS_INTERNAL_ERROR, "invalid type kind"); - goto error; - } - } else if (mt->kind == EcsEnumType) { - return ecs_meta_set_int(cursor, *(int32_t*)value->ptr); - } else if (mt->kind == EcsBitmaskType) { - return ecs_meta_set_int(cursor, *(uint32_t*)value->ptr); + if (id->second) { + flecs_scriptbuf_append(v, "(%s, %s)", + id->first, id->second); } else { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - if (op->type != value->type) { - char *type_str = ecs_get_fullpath(cursor->world, value->type); - flecs_meta_conversion_error(cursor, op, type_str); - ecs_os_free(type_str); - goto error; - } - return ecs_value_copy(cursor->world, value->type, ptr, value->ptr); + flecs_scriptbuf_appendstr(v, id->first); } - -error: - return -1; } static -int flecs_meta_add_bitmask_constant( - ecs_meta_cursor_t *cursor, - ecs_meta_type_op_t *op, - void *out, - const char *value) +void flecs_script_expr_to_str( + ecs_script_str_visitor_t *v, + const char *expr) { - ecs_assert(op->type != 0, ECS_INTERNAL_ERROR, NULL); + if (expr) { + flecs_scriptbuf_append(v, "%s%s%s", ECS_GREEN, expr, ECS_NORMAL); + } else { + flecs_scriptbuf_appendstr(v, "{}"); + } +} - if (!ecs_os_strcmp(value, "0")) { - return 0; +static +const char* flecs_script_node_to_str( + ecs_script_node_t *node) +{ + switch(node->kind) { + case EcsAstScope: return "scope"; + case EcsAstWithTag: + case EcsAstTag: return "tag"; + case EcsAstWithComponent: + case EcsAstComponent: return "component"; + case EcsAstWithVar: + case EcsAstVarComponent: return "var"; + case EcsAstDefaultComponent: return "default_component"; + case EcsAstWith: return "with"; + case EcsAstUsing: return "using"; + case EcsAstModule: return "module"; + case EcsAstAnnotation: return "annot"; + case EcsAstTemplate: return "template"; + case EcsAstProp: return "prop"; + case EcsAstConst: return "const"; + case EcsAstEntity: return "entity"; + case EcsAstPairScope: return "pair_scope"; + case EcsAstIf: return "if"; } + return "???"; +} - ecs_entity_t c = ecs_lookup_child(cursor->world, op->type, value); - if (!c) { - char *path = ecs_get_fullpath(cursor->world, op->type); - ecs_err("unresolved bitmask constant '%s' for type '%s'", value, path); - ecs_os_free(path); - return -1; +static +void flecs_scriptbuf_node( + ecs_script_str_visitor_t *v, + ecs_script_node_t *node) +{ + flecs_scriptbuf_append(v, "%s%s%s: ", + ECS_BLUE, flecs_script_node_to_str(node), ECS_NORMAL); +} + +static +void flecs_script_tag_to_str( + ecs_script_str_visitor_t *v, + ecs_script_tag_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_script_id_to_str(v, &node->id); + flecs_scriptbuf_appendstr(v, "\n"); +} + +static +void flecs_script_component_to_str( + ecs_script_str_visitor_t *v, + ecs_script_component_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_script_id_to_str(v, &node->id); + if (node->expr) { + flecs_scriptbuf_appendstr(v, ": "); + flecs_script_expr_to_str(v, node->expr); } + flecs_scriptbuf_appendstr(v, "\n"); +} - const ecs_u32_t *v = ecs_get_pair_object( - cursor->world, c, EcsConstant, ecs_u32_t); - if (v == NULL) { - char *path = ecs_get_fullpath(cursor->world, op->type); - ecs_err("'%s' is not an bitmask constant for type '%s'", value, path); - ecs_os_free(path); - return -1; +static +void flecs_script_default_component_to_str( + ecs_script_str_visitor_t *v, + ecs_script_default_component_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + if (node->expr) { + flecs_script_expr_to_str(v, node->expr); } + flecs_scriptbuf_appendstr(v, "\n"); +} - *(ecs_u32_t*)out |= v[0]; +static +void flecs_script_with_var_to_str( + ecs_script_str_visitor_t *v, + ecs_script_var_component_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s ", node->name); + flecs_scriptbuf_appendstr(v, "\n"); +} - return 0; +static +void flecs_script_with_to_str( + ecs_script_str_visitor_t *v, + ecs_script_with_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + + flecs_scriptbuf_appendstr(v, "{\n"); + v->depth ++; + flecs_scriptbuf_append(v, "%sexpressions%s: ", ECS_CYAN, ECS_NORMAL); + flecs_script_scope_to_str(v, node->expressions); + flecs_scriptbuf_append(v, "%sscope%s: ", ECS_CYAN, ECS_NORMAL); + flecs_script_scope_to_str(v, node->scope); + v->depth --; + flecs_scriptbuf_appendstr(v, "}\n"); } static -int flecs_meta_parse_bitmask( - ecs_meta_cursor_t *cursor, - ecs_meta_type_op_t *op, - void *out, - const char *value) +void flecs_script_using_to_str( + ecs_script_str_visitor_t *v, + ecs_script_using_t *node) { - char token[ECS_MAX_TOKEN_SIZE]; + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s\n", node->name); +} - const char *prev = value, *ptr = value; +static +void flecs_script_module_to_str( + ecs_script_str_visitor_t *v, + ecs_script_module_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s\n", node->name); +} - *(ecs_u32_t*)out = 0; +static +void flecs_script_annot_to_str( + ecs_script_str_visitor_t *v, + ecs_script_annot_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s = %s\"%s\"%s", node->name, + ECS_GREEN, node->expr, ECS_NORMAL); + flecs_scriptbuf_appendstr(v, "\n"); +} - while ((ptr = strchr(ptr, '|'))) { - ecs_os_memcpy(token, prev, ptr - prev); - token[ptr - prev] = '\0'; - if (flecs_meta_add_bitmask_constant(cursor, op, out, token) != 0) { - return -1; - } +static +void flecs_script_template_to_str( + ecs_script_str_visitor_t *v, + ecs_script_template_node_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s ", node->name); + flecs_script_scope_to_str(v, node->scope); +} - ptr ++; - prev = ptr; +static +void flecs_script_var_node_to_str( + ecs_script_str_visitor_t *v, + ecs_script_var_node_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + if (node->type) { + flecs_scriptbuf_append(v, "%s : %s = ", + node->name, + node->type); + } else { + flecs_scriptbuf_append(v, "%s = ", + node->name); } + flecs_script_expr_to_str(v, node->expr); + flecs_scriptbuf_appendstr(v, "\n"); +} - if (flecs_meta_add_bitmask_constant(cursor, op, out, prev) != 0) { - return -1; +static +void flecs_script_entity_to_str( + ecs_script_str_visitor_t *v, + ecs_script_entity_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + if (node->kind) { + flecs_scriptbuf_append(v, "%s ", node->kind); + } + if (node->name) { + flecs_scriptbuf_append(v, "%s ", node->name); + } else { + flecs_scriptbuf_appendstr(v, " "); } - return 0; + if (!flecs_scope_is_empty(node->scope)) { + flecs_script_scope_to_str(v, node->scope); + } else { + flecs_scriptbuf_appendstr(v, "\n"); + } } static -int flecs_meta_cursor_lookup( - ecs_meta_cursor_t *cursor, - const char *value, - ecs_entity_t *out) +void flecs_script_pair_scope_to_str( + ecs_script_str_visitor_t *v, + ecs_script_pair_scope_t *node) { - if (ecs_os_strcmp(value, "0")) { - if (cursor->lookup_action) { - *out = cursor->lookup_action( - cursor->world, value, - cursor->lookup_ctx); - } else { - *out = ecs_lookup_path(cursor->world, 0, value); - } - if (!*out) { - ecs_err("unresolved entity identifier '%s'", value); - return -1; - } + flecs_scriptbuf_node(v, &node->node); + flecs_script_id_to_str(v, &node->id); + flecs_scriptbuf_appendstr(v, " "); + flecs_script_scope_to_str(v, node->scope); +} + +static +void flecs_script_if_to_str( + ecs_script_str_visitor_t *v, + ecs_script_if_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_script_expr_to_str(v, node->expr); + + flecs_scriptbuf_appendstr(v, " {\n"); + v->depth ++; + flecs_scriptbuf_append(v, "%strue%s: ", ECS_CYAN, ECS_NORMAL); + flecs_script_scope_to_str(v, node->if_true); + flecs_scriptbuf_append(v, "%sfalse%s: ", ECS_CYAN, ECS_NORMAL); + flecs_script_scope_to_str(v, node->if_false); + v->depth --; + flecs_scriptbuf_appendstr(v, "}\n"); +} + +static +int flecs_script_scope_to_str( + ecs_script_str_visitor_t *v, + ecs_script_scope_t *scope) +{ + if (!ecs_vec_count(&scope->stmts)) { + flecs_scriptbuf_appendstr(v, "{}\n"); + return 0; + } + + flecs_scriptbuf_appendstr(v, "{\n"); + + v->depth ++; + + if (ecs_script_visit_scope(v, scope)) { + return -1; } + + v->depth --; + + flecs_scriptbuf_appendstr(v, "}\n"); + return 0; } -int ecs_meta_set_string( - ecs_meta_cursor_t *cursor, - const char *value) +static +int flecs_script_stmt_to_str( + ecs_script_str_visitor_t *v, + ecs_script_node_t *node) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - - switch(op->kind) { - case EcsOpBool: - if (!ecs_os_strcmp(value, "true")) { - set_T(ecs_bool_t, ptr, true); - } else if (!ecs_os_strcmp(value, "false")) { - set_T(ecs_bool_t, ptr, false); - } else if (isdigit(value[0])) { - if (!ecs_os_strcmp(value, "0")) { - set_T(ecs_bool_t, ptr, false); - } else { - set_T(ecs_bool_t, ptr, true); - } - } else { - ecs_err("invalid value for boolean '%s'", value); - goto error; + switch(node->kind) { + case EcsAstScope: + if (flecs_script_scope_to_str(v, (ecs_script_scope_t*)node)) { + return -1; } break; - case EcsOpI8: - case EcsOpU8: - case EcsOpByte: - set_T(ecs_i8_t, ptr, atol(value)); + case EcsAstTag: + case EcsAstWithTag: + flecs_script_tag_to_str(v, (ecs_script_tag_t*)node); break; - case EcsOpChar: - set_T(char, ptr, value[0]); + case EcsAstComponent: + case EcsAstWithComponent: + flecs_script_component_to_str(v, (ecs_script_component_t*)node); break; - case EcsOpI16: - case EcsOpU16: - set_T(ecs_i16_t, ptr, atol(value)); + case EcsAstVarComponent: + case EcsAstWithVar: + flecs_script_with_var_to_str(v, + (ecs_script_var_component_t*)node); break; - case EcsOpI32: - case EcsOpU32: - set_T(ecs_i32_t, ptr, atol(value)); + case EcsAstDefaultComponent: + flecs_script_default_component_to_str(v, + (ecs_script_default_component_t*)node); break; - case EcsOpI64: - case EcsOpU64: - set_T(ecs_i64_t, ptr, atoll(value)); + case EcsAstWith: + flecs_script_with_to_str(v, (ecs_script_with_t*)node); break; - case EcsOpIPtr: - case EcsOpUPtr: - set_T(ecs_iptr_t, ptr, atoll(value)); + case EcsAstUsing: + flecs_script_using_to_str(v, (ecs_script_using_t*)node); break; - case EcsOpF32: - set_T(ecs_f32_t, ptr, atof(value)); + case EcsAstModule: + flecs_script_module_to_str(v, (ecs_script_module_t*)node); break; - case EcsOpF64: - set_T(ecs_f64_t, ptr, atof(value)); + case EcsAstAnnotation: + flecs_script_annot_to_str(v, (ecs_script_annot_t*)node); break; - case EcsOpString: { - ecs_assert(*(ecs_string_t*)ptr != value, ECS_INVALID_PARAMETER, NULL); - ecs_os_free(*(ecs_string_t*)ptr); - char *result = ecs_os_strdup(value); - set_T(ecs_string_t, ptr, result); + case EcsAstTemplate: + flecs_script_template_to_str(v, (ecs_script_template_node_t*)node); break; - } - case EcsOpEnum: { - ecs_assert(op->type != 0, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t c = ecs_lookup_child(cursor->world, op->type, value); - if (!c) { - char *path = ecs_get_fullpath(cursor->world, op->type); - ecs_err("unresolved enum constant '%s' for type '%s'", value, path); - ecs_os_free(path); - goto error; - } - - const ecs_i32_t *v = ecs_get_pair_object( - cursor->world, c, EcsConstant, ecs_i32_t); - if (v == NULL) { - char *path = ecs_get_fullpath(cursor->world, op->type); - ecs_err("'%s' is not an enum constant for type '%s'", value, path); - ecs_os_free(path); - goto error; - } - - set_T(ecs_i32_t, ptr, v[0]); + case EcsAstConst: + case EcsAstProp: + flecs_script_var_node_to_str(v, (ecs_script_var_node_t*)node); break; - } - case EcsOpBitmask: - if (flecs_meta_parse_bitmask(cursor, op, ptr, value) != 0) { - goto error; - } + case EcsAstEntity: + flecs_script_entity_to_str(v, (ecs_script_entity_t*)node); break; - case EcsOpEntity: { - ecs_entity_t e = 0; - if (flecs_meta_cursor_lookup(cursor, value, &e)) { - goto error; - } - set_T(ecs_entity_t, ptr, e); + case EcsAstPairScope: + flecs_script_pair_scope_to_str(v, (ecs_script_pair_scope_t*)node); break; - } - case EcsOpId: { - ecs_id_t id = 0; -#ifdef FLECS_PARSER - ecs_term_t term = {0}; - if (ecs_parse_term(cursor->world, NULL, value, value, &term, NULL)) { - if (ecs_term_finalize(cursor->world, &term)) { - ecs_term_fini(&term); - goto error; - } - id = term.id; - ecs_term_fini(&term); - } else { - ecs_term_fini(&term); - goto error; - } -#endif - set_T(ecs_id_t, ptr, id); - + case EcsAstIf: + flecs_script_if_to_str(v, (ecs_script_if_t*)node); break; } - case EcsOpPop: - ecs_err("excess element '%s' in scope", value); - goto error; - case EcsOpOpaque: { - const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); - if (opaque->assign_string) { /* preferred */ - opaque->assign_string(ptr, value); - break; - } else if (opaque->assign_char && value[0] && !value[1]) { - opaque->assign_char(ptr, value[0]); - break; - } else if (opaque->assign_entity) { - ecs_entity_t e = 0; - if (flecs_meta_cursor_lookup(cursor, value, &e)) { - goto error; - } - opaque->assign_entity(ptr, - ECS_CONST_CAST(ecs_world_t*, cursor->world), e); - break; - } - } - /* fall through */ - case EcsOpArray: - case EcsOpVector: - case EcsOpPush: - case EcsOpScope: - case EcsOpPrimitive: - ecs_err("unsupported conversion from string '%s' to '%s'", - value, flecs_meta_op_kind_str(op->kind)); + + return 0; +} + +int ecs_script_ast_to_buf( + ecs_script_t *script, + ecs_strbuf_t *buf) +{ + ecs_check(script != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(buf != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_script_str_visitor_t v = { .buf = buf }; + if (ecs_script_visit(flecs_script_impl(script), &v, flecs_script_stmt_to_str)) { goto error; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - break; } return 0; error: - return -1; + ecs_strbuf_reset(buf); + return - 1; } -int ecs_meta_set_string_literal( - ecs_meta_cursor_t *cursor, - const char *value) +char* ecs_script_ast_to_str( + ecs_script_t *script) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - - ecs_size_t len = ecs_os_strlen(value); - if (value[0] != '\"' || value[len - 1] != '\"') { - ecs_err("invalid string literal '%s'", value); + ecs_check(script != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_strbuf_t buf = ECS_STRBUF_INIT; + if (ecs_script_ast_to_buf(script, &buf)) { goto error; } - switch(op->kind) { - case EcsOpChar: - set_T(ecs_char_t, ptr, value[1]); - break; + return ecs_strbuf_get(&buf); +error: + return NULL; +} - case EcsOpArray: - case EcsOpVector: - case EcsOpOpaque: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpString: - case EcsOpEntity: - case EcsOpId: - len -= 2; +#endif + +/** + * @file addons/monitor.c + * @brief Stats addon module. + */ + +/** + * @file addons/stats/stats.h + * @brief Internal functions/types for stats addon. + */ + +#ifndef FLECS_STATS_PRIVATE_H +#define FLECS_STATS_PRIVATE_H + + +typedef struct { + /* Statistics API interface */ + void (*copy_last)(void *stats, void *src); + void (*get)(ecs_world_t *world, ecs_entity_t res, void *stats); + void (*reduce)(void *stats, void *src); + void (*reduce_last)(void *stats, void *last, int32_t reduce_count); + void (*repeat_last)(void* stats); + void (*set_t)(void *stats, int32_t t); + void (*fini)(void *stats); + + /* Size of statistics type */ + ecs_size_t stats_size; + + /* Id of component that contains the statistics */ + ecs_entity_t monitor_component_id; + + /* Id of component used to query for monitored resources (optional) */ + ecs_id_t query_component_id; +} ecs_stats_api_t; + +void flecs_stats_api_import( + ecs_world_t *world, + ecs_stats_api_t *api); + +void FlecsWorldSummaryImport( + ecs_world_t *world); + +void FlecsWorldMonitorImport( + ecs_world_t *world); + +void FlecsSystemMonitorImport( + ecs_world_t *world); + +void FlecsPipelineMonitorImport( + ecs_world_t *world); + +#endif + + +#ifdef FLECS_STATS + +ECS_COMPONENT_DECLARE(FlecsStats); + +ecs_entity_t EcsPeriod1s = 0; +ecs_entity_t EcsPeriod1m = 0; +ecs_entity_t EcsPeriod1h = 0; +ecs_entity_t EcsPeriod1d = 0; +ecs_entity_t EcsPeriod1w = 0; + +#define FlecsDayIntervalCount (24) +#define FlecsWeekIntervalCount (168) + +typedef struct { + ecs_stats_api_t api; + ecs_query_t *query; +} ecs_monitor_stats_ctx_t; + +typedef struct { + ecs_stats_api_t api; +} ecs_reduce_stats_ctx_t; + +typedef struct { + ecs_stats_api_t api; + int32_t interval; +} ecs_aggregate_stats_ctx_t; + +static +void MonitorStats(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + ecs_monitor_stats_ctx_t *ctx = it->ctx; + + EcsStatsHeader *hdr = ecs_field_w_size(it, 0, 0); + + ecs_ftime_t elapsed = hdr->elapsed; + hdr->elapsed += it->delta_time; + + int32_t t_last = (int32_t)(elapsed * 60); + int32_t t_next = (int32_t)(hdr->elapsed * 60); + int32_t i, dif = t_next - t_last; + void *stats_storage = ecs_os_alloca(ctx->api.stats_size); + void *last = NULL; + + if (!dif) { + hdr->reduce_count ++; + } + + ecs_iter_t qit; + int32_t cur = -1, count = 0; + void *stats = NULL; + ecs_map_t *stats_map = NULL; + + if (ctx->query) { + /* Query results are stored in a map */ + qit = ecs_query_iter(it->world, ctx->query); + stats_map = ECS_OFFSET_T(hdr, EcsStatsHeader); + } else { + /* No query, so tracking stats for single element */ + stats = ECS_OFFSET_T(hdr, EcsStatsHeader); + } + + do { + ecs_entity_t res = 0; + if (ctx->query) { + /* Query, fetch resource entity & stats pointer */ + if (cur == (count - 1)) { + if (!ecs_query_next(&qit)) { + break; + } + + cur = 0; + count = qit.count; + if (!count) { + cur = -1; + continue; + } + } else { + cur ++; + } + + res = qit.entities[cur]; + stats = ecs_map_ensure_alloc(stats_map, ctx->api.stats_size, res); + ctx->api.set_t(stats, t_last % ECS_STAT_WINDOW); + } + + if (!dif) { + /* Copy last value so we can pass it to reduce_last */ + last = stats_storage; + ecs_os_memset(last, 0, ctx->api.stats_size); + ctx->api.copy_last(last, stats); + } + + ctx->api.get(world, res, stats); + + if (!dif) { + /* Still in same interval, combine with last measurement */ + ctx->api.reduce_last(stats, last, hdr->reduce_count); + } else if (dif > 1) { + /* More than 16ms has passed, backfill */ + for (i = 1; i < dif; i ++) { + ctx->api.repeat_last(stats); + } + } - char *result = ecs_os_malloc(len + 1); - ecs_os_memcpy(result, value + 1, len); - result[len] = '\0'; + if (last && ctx->api.fini) { + ctx->api.fini(last); + } - if (ecs_meta_set_string(cursor, result)) { - ecs_os_free(result); - return -1; + if (!ctx->query) { + break; } + } while (true); - ecs_os_free(result); - break; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - break; + if (dif > 1) { + hdr->reduce_count = 0; } - - return 0; -error: - return -1; } -int ecs_meta_set_entity( - ecs_meta_cursor_t *cursor, - ecs_entity_t value) -{ - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); +static +void ReduceStats(ecs_iter_t *it) { + ecs_reduce_stats_ctx_t *ctx = it->ctx; - switch(op->kind) { - case EcsOpEntity: - set_T(ecs_entity_t, ptr, value); - break; - case EcsOpId: - set_T(ecs_id_t, ptr, value); /* entities are valid ids */ - break; - case EcsOpOpaque: { - const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - if (opaque && opaque->assign_entity) { - opaque->assign_entity(ptr, - ECS_CONST_CAST(ecs_world_t*, cursor->world), value); - break; + void *dst = ecs_field_w_size(it, 0, 0); + void *src = ecs_field_w_size(it, 0, 1); + + dst = ECS_OFFSET_T(dst, EcsStatsHeader); + src = ECS_OFFSET_T(src, EcsStatsHeader); + + if (!ctx->api.query_component_id) { + ctx->api.reduce(dst, src); + } else { + ecs_map_iter_t mit = ecs_map_iter(src); + while (ecs_map_next(&mit)) { + void *src_el = ecs_map_ptr(&mit); + void *dst_el = ecs_map_ensure_alloc( + dst, ctx->api.stats_size, ecs_map_key(&mit)); + ctx->api.reduce(dst_el, src_el); } } - /* fall through */ - case EcsOpArray: - case EcsOpVector: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpString: - flecs_meta_conversion_error(cursor, op, "entity"); - goto error; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - break; +} + +static +void AggregateStats(ecs_iter_t *it) { + ecs_aggregate_stats_ctx_t *ctx = it->ctx; + int32_t interval = ctx->interval; + + EcsStatsHeader *dst_hdr = ecs_field_w_size(it, 0, 0); + EcsStatsHeader *src_hdr = ecs_field_w_size(it, 0, 1); + + void *dst = ECS_OFFSET_T(dst_hdr, EcsStatsHeader); + void *src = ECS_OFFSET_T(src_hdr, EcsStatsHeader); + void *dst_map = NULL; + void *src_map = NULL; + if (ctx->api.query_component_id) { + dst_map = dst; + src_map = src; + dst = NULL; + src = NULL; } - return 0; -error: - return -1; -} + void *stats_storage = ecs_os_alloca(ctx->api.stats_size); + void *last = NULL; -int ecs_meta_set_id( - ecs_meta_cursor_t *cursor, - ecs_entity_t value) -{ - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + ecs_map_iter_t mit; + if (src_map) { + mit = ecs_map_iter(src_map); + } - switch(op->kind) { - case EcsOpId: - set_T(ecs_id_t, ptr, value); - break; - case EcsOpOpaque: { - const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - if (opaque && opaque->assign_id) { - opaque->assign_id(ptr, - ECS_CONST_CAST(ecs_world_t*, cursor->world), value); + do { + if (src_map) { + if (!ecs_map_next(&mit)) { + break; + } + + src = ecs_map_ptr(&mit); + dst = ecs_map_ensure_alloc( + dst_map, ctx->api.stats_size, ecs_map_key(&mit)); + } + + if (dst_hdr->reduce_count != 0) { + /* Copy last value so we can pass it to reduce_last */ + last = stats_storage; + ecs_os_memset(last, 0, ctx->api.stats_size); + ctx->api.copy_last(last, dst); + } + + /* Reduce from minutes to the current day */ + ctx->api.reduce(dst, src); + + if (dst_hdr->reduce_count != 0) { + ctx->api.reduce_last(dst, last, dst_hdr->reduce_count); + } + + if (last && ctx->api.fini != NULL) { + ctx->api.fini(last); + } + + if (!src_map) { break; } + } while (true); + + /* A day has 60 24 minute intervals */ + dst_hdr->reduce_count ++; + if (dst_hdr->reduce_count >= interval) { + dst_hdr->reduce_count = 0; } - /* fall through */ - case EcsOpArray: - case EcsOpVector: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpString: - case EcsOpEntity: - flecs_meta_conversion_error(cursor, op, "id"); - goto error; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - break; +} + +static +void flecs_monitor_ctx_free( + void *ptr) +{ + ecs_monitor_stats_ctx_t *ctx = ptr; + if (ctx->query) { + ecs_query_fini(ctx->query); } + ecs_os_free(ctx); +} - return 0; -error: - return -1; +static +void flecs_reduce_ctx_free( + void *ptr) +{ + ecs_os_free(ptr); } -int ecs_meta_set_null( - ecs_meta_cursor_t *cursor) +static +void flecs_aggregate_ctx_free( + void *ptr) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - switch (op->kind) { - case EcsOpString: - ecs_os_free(*(char**)ptr); - set_T(ecs_string_t, ptr, NULL); - break; - case EcsOpOpaque: { - const EcsOpaque *ot = ecs_get(cursor->world, op->type, EcsOpaque); - if (ot && ot->assign_null) { - ot->assign_null(ptr); - break; - } + ecs_os_free(ptr); +} + +void flecs_stats_api_import( + ecs_world_t *world, + ecs_stats_api_t *api) +{ + ecs_entity_t kind = api->monitor_component_id; + ecs_entity_t prev = ecs_set_scope(world, kind); + + ecs_query_t *q = NULL; + if (api->query_component_id) { + q = ecs_query(world, { + .terms = {{ .id = api->query_component_id }}, + .cache_kind = EcsQueryCacheNone, + .flags = EcsQueryMatchDisabled + }); } - /* fall through */ - case EcsOpArray: - case EcsOpVector: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpEntity: - case EcsOpId: - flecs_meta_conversion_error(cursor, op, "null"); - goto error; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - break; + + // Called each frame, collects 60 measurements per second + { + ecs_monitor_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_monitor_stats_ctx_t); + ctx->api = *api; + ctx->query = q; + + ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1s", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1s), + .src.id = EcsWorld + }}, + .callback = MonitorStats, + .ctx = ctx, + .ctx_free = flecs_monitor_ctx_free + }); } - return 0; -error: - return -1; -} + // Called each second, reduces into 60 measurements per minute + ecs_entity_t mw1m; + { + ecs_reduce_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_reduce_stats_ctx_t); + ctx->api = *api; + + mw1m = ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1m", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1m), + .src.id = EcsWorld + }, { + .id = ecs_pair(kind, EcsPeriod1s), + .src.id = EcsWorld + }}, + .callback = ReduceStats, + .interval = 1.0, + .ctx = ctx, + .ctx_free = flecs_reduce_ctx_free + }); + } -bool ecs_meta_get_bool( - const ecs_meta_cursor_t *cursor) -{ - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - switch(op->kind) { - case EcsOpBool: return *(ecs_bool_t*)ptr; - case EcsOpI8: return *(ecs_i8_t*)ptr != 0; - case EcsOpU8: return *(ecs_u8_t*)ptr != 0; - case EcsOpChar: return *(ecs_char_t*)ptr != 0; - case EcsOpByte: return *(ecs_u8_t*)ptr != 0; - case EcsOpI16: return *(ecs_i16_t*)ptr != 0; - case EcsOpU16: return *(ecs_u16_t*)ptr != 0; - case EcsOpI32: return *(ecs_i32_t*)ptr != 0; - case EcsOpU32: return *(ecs_u32_t*)ptr != 0; - case EcsOpI64: return *(ecs_i64_t*)ptr != 0; - case EcsOpU64: return *(ecs_u64_t*)ptr != 0; - case EcsOpIPtr: return *(ecs_iptr_t*)ptr != 0; - case EcsOpUPtr: return *(ecs_uptr_t*)ptr != 0; - case EcsOpF32: return ECS_NEQZERO(*(ecs_f32_t*)ptr); - case EcsOpF64: return ECS_NEQZERO(*(ecs_f64_t*)ptr); - case EcsOpString: return *(const char**)ptr != NULL; - case EcsOpEnum: return *(ecs_i32_t*)ptr != 0; - case EcsOpBitmask: return *(ecs_u32_t*)ptr != 0; - case EcsOpEntity: return *(ecs_entity_t*)ptr != 0; - case EcsOpId: return *(ecs_id_t*)ptr != 0; - case EcsOpArray: - case EcsOpVector: - case EcsOpOpaque: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpPrimitive: - ecs_throw(ECS_INVALID_PARAMETER, "invalid element for bool"); - break; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - break; + // Called each minute, reduces into 60 measurements per hour + { + ecs_reduce_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_reduce_stats_ctx_t); + ctx->api = *api; + + ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1h", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1h), + .src.id = EcsWorld + }, { + .id = ecs_pair(kind, EcsPeriod1m), + .src.id = EcsWorld + }}, + .callback = ReduceStats, + .rate = 60, + .tick_source = mw1m, + .ctx = ctx, + .ctx_free = flecs_reduce_ctx_free + }); } -error: - return 0; -} + // Called each minute, reduces into 60 measurements per day + { + ecs_aggregate_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_aggregate_stats_ctx_t); + ctx->api = *api; + ctx->interval = FlecsDayIntervalCount; + + ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1d", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1d), + .src.id = EcsWorld + }, { + .id = ecs_pair(kind, EcsPeriod1m), + .src.id = EcsWorld + }}, + .callback = AggregateStats, + .rate = 60, + .tick_source = mw1m, + .ctx = ctx, + .ctx_free = flecs_aggregate_ctx_free + }); + } -char ecs_meta_get_char( - const ecs_meta_cursor_t *cursor) -{ - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - switch(op->kind) { - case EcsOpChar: - return *(ecs_char_t*)ptr != 0; - case EcsOpArray: - case EcsOpVector: - case EcsOpOpaque: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpString: - case EcsOpEntity: - case EcsOpId: - ecs_throw(ECS_INVALID_PARAMETER, "invalid element for char"); - break; + // Called each hour, reduces into 60 measurements per week + { + ecs_aggregate_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_aggregate_stats_ctx_t); + ctx->api = *api; + ctx->interval = FlecsWeekIntervalCount; + + ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1w", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1w), + .src.id = EcsWorld + }, { + .id = ecs_pair(kind, EcsPeriod1h), + .src.id = EcsWorld + }}, + .callback = AggregateStats, + .rate = 60, + .tick_source = mw1m, + .ctx = ctx, + .ctx_free = flecs_aggregate_ctx_free + }); } -error: - return 0; + ecs_set_scope(world, prev); + + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1s); + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1m); + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1h); + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1d); + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1w); } -int64_t ecs_meta_get_int( - const ecs_meta_cursor_t *cursor) +void FlecsStatsImport( + ecs_world_t *world) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - switch(op->kind) { - case EcsOpBool: return *(const ecs_bool_t*)ptr; - case EcsOpI8: return *(const ecs_i8_t*)ptr; - case EcsOpU8: return *(const ecs_u8_t*)ptr; - case EcsOpChar: return *(const ecs_char_t*)ptr; - case EcsOpByte: return *(const ecs_u8_t*)ptr; - case EcsOpI16: return *(const ecs_i16_t*)ptr; - case EcsOpU16: return *(const ecs_u16_t*)ptr; - case EcsOpI32: return *(const ecs_i32_t*)ptr; - case EcsOpU32: return *(const ecs_u32_t*)ptr; - case EcsOpI64: return *(const ecs_i64_t*)ptr; - case EcsOpU64: return flecs_uto(int64_t, *(const ecs_u64_t*)ptr); - case EcsOpIPtr: return *(const ecs_iptr_t*)ptr; - case EcsOpUPtr: return flecs_uto(int64_t, *(const ecs_uptr_t*)ptr); - case EcsOpF32: return (int64_t)*(const ecs_f32_t*)ptr; - case EcsOpF64: return (int64_t)*(const ecs_f64_t*)ptr; - case EcsOpString: return atoi(*(const char**)ptr); - case EcsOpEnum: return *(const ecs_i32_t*)ptr; - case EcsOpBitmask: return *(const ecs_u32_t*)ptr; - case EcsOpEntity: - ecs_throw(ECS_INVALID_PARAMETER, - "invalid conversion from entity to int"); - break; - case EcsOpId: - ecs_throw(ECS_INVALID_PARAMETER, - "invalid conversion from id to int"); - break; - case EcsOpArray: - case EcsOpVector: - case EcsOpOpaque: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpPrimitive: - ecs_throw(ECS_INVALID_PARAMETER, "invalid element for int"); - break; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - break; + ECS_MODULE_DEFINE(world, FlecsStats); + ECS_IMPORT(world, FlecsPipeline); + ECS_IMPORT(world, FlecsTimer); +#ifdef FLECS_META + ECS_IMPORT(world, FlecsMeta); +#endif +#ifdef FLECS_UNITS + ECS_IMPORT(world, FlecsUnits); +#endif +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsStats), + "Module that automatically monitors statistics for the world & systems"); +#endif + + ecs_set_name_prefix(world, "Ecs"); + + EcsPeriod1s = ecs_entity(world, { .name = "EcsPeriod1s" }); + EcsPeriod1m = ecs_entity(world, { .name = "EcsPeriod1m" }); + EcsPeriod1h = ecs_entity(world, { .name = "EcsPeriod1h" }); + EcsPeriod1d = ecs_entity(world, { .name = "EcsPeriod1d" }); + EcsPeriod1w = ecs_entity(world, { .name = "EcsPeriod1w" }); + + FlecsWorldSummaryImport(world); + FlecsWorldMonitorImport(world); + FlecsSystemMonitorImport(world); + FlecsPipelineMonitorImport(world); + + if (ecs_os_has_time()) { + ecs_measure_frame_time(world, true); + ecs_measure_system_time(world, true); } -error: - return 0; } -uint64_t ecs_meta_get_uint( - const ecs_meta_cursor_t *cursor) -{ - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - switch(op->kind) { - case EcsOpBool: return *(ecs_bool_t*)ptr; - case EcsOpI8: return flecs_ito(uint64_t, *(const ecs_i8_t*)ptr); - case EcsOpU8: return *(ecs_u8_t*)ptr; - case EcsOpChar: return flecs_ito(uint64_t, *(const ecs_char_t*)ptr); - case EcsOpByte: return flecs_ito(uint64_t, *(const ecs_u8_t*)ptr); - case EcsOpI16: return flecs_ito(uint64_t, *(const ecs_i16_t*)ptr); - case EcsOpU16: return *(ecs_u16_t*)ptr; - case EcsOpI32: return flecs_ito(uint64_t, *(const ecs_i32_t*)ptr); - case EcsOpU32: return *(ecs_u32_t*)ptr; - case EcsOpI64: return flecs_ito(uint64_t, *(const ecs_i64_t*)ptr); - case EcsOpU64: return *(ecs_u64_t*)ptr; - case EcsOpIPtr: return flecs_ito(uint64_t, *(const ecs_i64_t*)ptr); - case EcsOpUPtr: return *(ecs_uptr_t*)ptr; - case EcsOpF32: return flecs_ito(uint64_t, *(const ecs_f32_t*)ptr); - case EcsOpF64: return flecs_ito(uint64_t, *(const ecs_f64_t*)ptr); - case EcsOpString: return flecs_ito(uint64_t, atoi(*(const char**)ptr)); - case EcsOpEnum: return flecs_ito(uint64_t, *(const ecs_i32_t*)ptr); - case EcsOpBitmask: return *(const ecs_u32_t*)ptr; - case EcsOpEntity: return *(const ecs_entity_t*)ptr; - case EcsOpId: return *(const ecs_id_t*)ptr; - case EcsOpArray: - case EcsOpVector: - case EcsOpOpaque: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpPrimitive: - ecs_throw(ECS_INVALID_PARAMETER, "invalid element for uint"); - break; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - break; +#endif + +/** + * @file addons/stats/pipeline_monitor.c + * @brief Stats addon pipeline monitor + */ + + +#ifdef FLECS_STATS + +ECS_COMPONENT_DECLARE(EcsPipelineStats); + +static +void flecs_pipeline_monitor_dtor(EcsPipelineStats *ptr) { + ecs_map_iter_t it = ecs_map_iter(&ptr->stats); + while (ecs_map_next(&it)) { + ecs_pipeline_stats_t *stats = ecs_map_ptr(&it); + ecs_pipeline_stats_fini(stats); + ecs_os_free(stats); } -error: - return 0; + ecs_map_fini(&ptr->stats); } +static ECS_CTOR(EcsPipelineStats, ptr, { + ecs_os_zeromem(ptr); + ecs_map_init(&ptr->stats, NULL); +}) + +static ECS_COPY(EcsPipelineStats, dst, src, { + (void)dst; + (void)src; + ecs_abort(ECS_INVALID_OPERATION, "cannot copy pipeline stats component"); +}) + +static ECS_MOVE(EcsPipelineStats, dst, src, { + flecs_pipeline_monitor_dtor(dst); + ecs_os_memcpy_t(dst, src, EcsPipelineStats); + ecs_os_zeromem(src); +}) + +static ECS_DTOR(EcsPipelineStats, ptr, { + flecs_pipeline_monitor_dtor(ptr); +}) + +static +void flecs_pipeline_stats_set_t( + void *stats, int32_t t) +{ + ecs_assert(t >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(t < ECS_STAT_WINDOW, ECS_INTERNAL_ERROR, NULL); + ((ecs_pipeline_stats_t*)stats)->t = t; +} + + static -double flecs_meta_to_float( - ecs_meta_type_op_kind_t kind, - const void *ptr) +void flecs_pipeline_stats_copy_last( + void *stats, + void *src) { - switch(kind) { - case EcsOpBool: return *(const ecs_bool_t*)ptr; - case EcsOpI8: return *(const ecs_i8_t*)ptr; - case EcsOpU8: return *(const ecs_u8_t*)ptr; - case EcsOpChar: return *(const ecs_char_t*)ptr; - case EcsOpByte: return *(const ecs_u8_t*)ptr; - case EcsOpI16: return *(const ecs_i16_t*)ptr; - case EcsOpU16: return *(const ecs_u16_t*)ptr; - case EcsOpI32: return *(const ecs_i32_t*)ptr; - case EcsOpU32: return *(const ecs_u32_t*)ptr; - case EcsOpI64: return (double)*(const ecs_i64_t*)ptr; - case EcsOpU64: return (double)*(const ecs_u64_t*)ptr; - case EcsOpIPtr: return (double)*(const ecs_iptr_t*)ptr; - case EcsOpUPtr: return (double)*(const ecs_uptr_t*)ptr; - case EcsOpF32: return (double)*(const ecs_f32_t*)ptr; - case EcsOpF64: return *(const ecs_f64_t*)ptr; - case EcsOpString: return atof(*ECS_CONST_CAST(const char**, ptr)); - case EcsOpEnum: return *(const ecs_i32_t*)ptr; - case EcsOpBitmask: return *(const ecs_u32_t*)ptr; - case EcsOpEntity: - ecs_throw(ECS_INVALID_PARAMETER, - "invalid conversion from entity to float"); - break; - case EcsOpId: - ecs_throw(ECS_INVALID_PARAMETER, - "invalid conversion from id to float"); - break; - case EcsOpArray: - case EcsOpVector: - case EcsOpOpaque: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpPrimitive: - ecs_throw(ECS_INVALID_PARAMETER, "invalid element for float"); - break; - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); - break; - } -error: - return 0; + ecs_pipeline_stats_copy_last(stats, src); } -double ecs_meta_get_float( - const ecs_meta_cursor_t *cursor) +static +void flecs_pipeline_stats_get( + ecs_world_t *world, + ecs_entity_t res, + void *stats) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - return flecs_meta_to_float(op->kind, ptr); + ecs_pipeline_stats_get(world, res, stats); } -const char* ecs_meta_get_string( - const ecs_meta_cursor_t *cursor) +static +void flecs_pipeline_stats_reduce( + void *stats, + void *src) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - switch(op->kind) { - case EcsOpString: return *(const char**)ptr; - case EcsOpArray: - case EcsOpVector: - case EcsOpOpaque: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpChar: - case EcsOpBool: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpEntity: - case EcsOpId: - default: - ecs_throw(ECS_INVALID_PARAMETER, "invalid element for string"); - break; - } -error: - return 0; + ecs_pipeline_stats_reduce(stats, src); } -ecs_entity_t ecs_meta_get_entity( - const ecs_meta_cursor_t *cursor) +static +void flecs_pipeline_stats_reduce_last( + void *stats, + void *last, + int32_t reduce_count) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - switch(op->kind) { - case EcsOpEntity: return *(ecs_entity_t*)ptr; - case EcsOpArray: - case EcsOpVector: - case EcsOpOpaque: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpChar: - case EcsOpBool: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpString: - case EcsOpId: - ecs_throw(ECS_INVALID_PARAMETER, "invalid element for entity"); - break; - } -error: - return 0; + ecs_pipeline_stats_reduce_last(stats, last, reduce_count); } -ecs_entity_t ecs_meta_get_id( - const ecs_meta_cursor_t *cursor) +static +void flecs_pipeline_stats_repeat_last( + void* stats) { - ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); - ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); - void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); - switch(op->kind) { - case EcsOpEntity: return *(ecs_id_t*)ptr; /* Entities are valid ids */ - case EcsOpId: return *(ecs_id_t*)ptr; - case EcsOpArray: - case EcsOpVector: - case EcsOpOpaque: - case EcsOpPush: - case EcsOpPop: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpChar: - case EcsOpBool: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpString: - ecs_throw(ECS_INVALID_PARAMETER, "invalid element for entity"); - break; - } -error: - return 0; + ecs_pipeline_stats_repeat_last(stats); } -double ecs_meta_ptr_to_float( - ecs_primitive_kind_t type_kind, - const void *ptr) +static +void flecs_pipeline_stats_fini( + void *stats) { - ecs_meta_type_op_kind_t kind = flecs_meta_primitive_to_op_kind(type_kind); - return flecs_meta_to_float(kind, ptr); + ecs_pipeline_stats_fini(stats); +} + +void FlecsPipelineMonitorImport( + ecs_world_t *world) +{ + ECS_COMPONENT_DEFINE(world, EcsPipelineStats); + + ecs_set_hooks(world, EcsPipelineStats, { + .ctor = ecs_ctor(EcsPipelineStats), + .copy = ecs_copy(EcsPipelineStats), + .move = ecs_move(EcsPipelineStats), + .dtor = ecs_dtor(EcsPipelineStats) + }); + + ecs_stats_api_t api = { + .copy_last = flecs_pipeline_stats_copy_last, + .get = flecs_pipeline_stats_get, + .reduce = flecs_pipeline_stats_reduce, + .reduce_last = flecs_pipeline_stats_reduce_last, + .repeat_last = flecs_pipeline_stats_repeat_last, + .set_t = flecs_pipeline_stats_set_t, + .fini = flecs_pipeline_stats_fini, + .stats_size = ECS_SIZEOF(ecs_pipeline_stats_t), + .monitor_component_id = ecs_id(EcsPipelineStats), + .query_component_id = ecs_id(EcsPipeline) + }; + + flecs_stats_api_import(world, &api); } #endif /** - * @file meta/meta.c - * @brief Meta addon. + * @file addons/stats.c + * @brief Stats addon. */ -#ifdef FLECS_META -/* ecs_string_t lifecycle */ +#ifdef FLECS_STATS -static ECS_COPY(ecs_string_t, dst, src, { - ecs_os_free(*(ecs_string_t*)dst); - *(ecs_string_t*)dst = ecs_os_strdup(*(const ecs_string_t*)src); -}) +#define ECS_GAUGE_RECORD(m, t, value)\ + flecs_gauge_record(m, t, (ecs_float_t)(value)) -static ECS_MOVE(ecs_string_t, dst, src, { - ecs_os_free(*(ecs_string_t*)dst); - *(ecs_string_t*)dst = *(ecs_string_t*)src; - *(ecs_string_t*)src = NULL; -}) +#define ECS_COUNTER_RECORD(m, t, value)\ + flecs_counter_record(m, t, (double)(value)) -static ECS_DTOR(ecs_string_t, ptr, { - ecs_os_free(*(ecs_string_t*)ptr); - *(ecs_string_t*)ptr = NULL; -}) +#define ECS_METRIC_FIRST(stats)\ + ECS_CAST(ecs_metric_t*, ECS_OFFSET(&stats->first_, ECS_SIZEOF(int64_t))) +#define ECS_METRIC_LAST(stats)\ + ECS_CAST(ecs_metric_t*, ECS_OFFSET(&stats->last_, -ECS_SIZEOF(ecs_metric_t))) -/* EcsMetaTypeSerialized lifecycle */ +static +int32_t t_next( + int32_t t) +{ + return (t + 1) % ECS_STAT_WINDOW; +} -void ecs_meta_dtor_serialized( - EcsMetaTypeSerialized *ptr) +static +int32_t t_prev( + int32_t t) { - int32_t i, count = ecs_vec_count(&ptr->ops); - ecs_meta_type_op_t *ops = ecs_vec_first(&ptr->ops); - - for (i = 0; i < count; i ++) { - ecs_meta_type_op_t *op = &ops[i]; - if (op->members) { - flecs_name_index_free(op->members); - } + return (t - 1 + ECS_STAT_WINDOW) % ECS_STAT_WINDOW; +} + +static +void flecs_gauge_record( + ecs_metric_t *m, + int32_t t, + ecs_float_t value) +{ + m->gauge.avg[t] = value; + m->gauge.min[t] = value; + m->gauge.max[t] = value; +} + +static +double flecs_counter_record( + ecs_metric_t *m, + int32_t t, + double value) +{ + int32_t tp = t_prev(t); + double prev = m->counter.value[tp]; + m->counter.value[t] = value; + double gauge_value = value - prev; + if (gauge_value < 0) { + gauge_value = 0; /* Counters are monotonically increasing */ } + flecs_gauge_record(m, t, (ecs_float_t)gauge_value); + return gauge_value; +} - ecs_vec_fini_t(NULL, &ptr->ops, ecs_meta_type_op_t); +static +void flecs_metric_print( + const char *name, + ecs_float_t value) +{ + ecs_size_t len = ecs_os_strlen(name); + ecs_trace("%s: %*s %.2f", name, 32 - len, "", (double)value); } -static ECS_COPY(EcsMetaTypeSerialized, dst, src, { - ecs_meta_dtor_serialized(dst); +static +void flecs_gauge_print( + const char *name, + int32_t t, + const ecs_metric_t *m) +{ + flecs_metric_print(name, m->gauge.avg[t]); +} - dst->ops = ecs_vec_copy_t(NULL, &src->ops, ecs_meta_type_op_t); +static +void flecs_counter_print( + const char *name, + int32_t t, + const ecs_metric_t *m) +{ + flecs_metric_print(name, m->counter.rate.avg[t]); +} - int32_t o, count = ecs_vec_count(&dst->ops); - ecs_meta_type_op_t *ops = ecs_vec_first_t(&dst->ops, ecs_meta_type_op_t); - - for (o = 0; o < count; o ++) { - ecs_meta_type_op_t *op = &ops[o]; - if (op->members) { - op->members = flecs_name_index_copy(op->members); +void ecs_metric_reduce( + ecs_metric_t *dst, + const ecs_metric_t *src, + int32_t t_dst, + int32_t t_src) +{ + ecs_check(dst != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(src != NULL, ECS_INVALID_PARAMETER, NULL); + + bool min_set = false; + dst->gauge.avg[t_dst] = 0; + dst->gauge.min[t_dst] = 0; + dst->gauge.max[t_dst] = 0; + + ecs_float_t fwindow = (ecs_float_t)ECS_STAT_WINDOW; + + int32_t i; + for (i = 0; i < ECS_STAT_WINDOW; i ++) { + int32_t t = (t_src + i) % ECS_STAT_WINDOW; + dst->gauge.avg[t_dst] += src->gauge.avg[t] / fwindow; + + if (!min_set || (src->gauge.min[t] < dst->gauge.min[t_dst])) { + dst->gauge.min[t_dst] = src->gauge.min[t]; + min_set = true; + } + if ((src->gauge.max[t] > dst->gauge.max[t_dst])) { + dst->gauge.max[t_dst] = src->gauge.max[t]; } } -}) -static ECS_MOVE(EcsMetaTypeSerialized, dst, src, { - ecs_meta_dtor_serialized(dst); - dst->ops = src->ops; - src->ops = (ecs_vec_t){0}; -}) + dst->counter.value[t_dst] = src->counter.value[t_src]; -static ECS_DTOR(EcsMetaTypeSerialized, ptr, { - ecs_meta_dtor_serialized(ptr); -}) +error: + return; +} +void ecs_metric_reduce_last( + ecs_metric_t *m, + int32_t prev, + int32_t count) +{ + ecs_check(m != NULL, ECS_INVALID_PARAMETER, NULL); + int32_t t = t_next(prev); -/* EcsStruct lifecycle */ + if (m->gauge.min[t] < m->gauge.min[prev]) { + m->gauge.min[prev] = m->gauge.min[t]; + } -static void flecs_struct_dtor( - EcsStruct *ptr) -{ - ecs_member_t *members = ecs_vec_first_t(&ptr->members, ecs_member_t); - int32_t i, count = ecs_vec_count(&ptr->members); - for (i = 0; i < count; i ++) { - ecs_os_free(ECS_CONST_CAST(char*, members[i].name)); + if (m->gauge.max[t] > m->gauge.max[prev]) { + m->gauge.max[prev] = m->gauge.max[t]; } - ecs_vec_fini_t(NULL, &ptr->members, ecs_member_t); -} -static ECS_COPY(EcsStruct, dst, src, { - flecs_struct_dtor(dst); + ecs_float_t fcount = (ecs_float_t)(count + 1); + ecs_float_t cur = m->gauge.avg[prev]; + ecs_float_t next = m->gauge.avg[t]; - dst->members = ecs_vec_copy_t(NULL, &src->members, ecs_member_t); + cur *= ((fcount - 1) / fcount); + next *= 1 / fcount; - ecs_member_t *members = ecs_vec_first_t(&dst->members, ecs_member_t); - int32_t m, count = ecs_vec_count(&dst->members); + m->gauge.avg[prev] = cur + next; + m->counter.value[prev] = m->counter.value[t]; - for (m = 0; m < count; m ++) { - members[m].name = ecs_os_strdup(members[m].name); - } -}) +error: + return; +} -static ECS_MOVE(EcsStruct, dst, src, { - flecs_struct_dtor(dst); - dst->members = src->members; - src->members = (ecs_vec_t){0}; -}) +void ecs_metric_copy( + ecs_metric_t *m, + int32_t dst, + int32_t src) +{ + ecs_check(m != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(dst != src, ECS_INVALID_PARAMETER, NULL); -static ECS_DTOR(EcsStruct, ptr, { flecs_struct_dtor(ptr); }) + m->gauge.avg[dst] = m->gauge.avg[src]; + m->gauge.min[dst] = m->gauge.min[src]; + m->gauge.max[dst] = m->gauge.max[src]; + m->counter.value[dst] = m->counter.value[src]; +error: + return; +} -/* EcsEnum lifecycle */ +static +void flecs_stats_reduce( + ecs_metric_t *dst_cur, + ecs_metric_t *dst_last, + ecs_metric_t *src_cur, + int32_t t_dst, + int32_t t_src) +{ + for (; dst_cur <= dst_last; dst_cur ++, src_cur ++) { + ecs_metric_reduce(dst_cur, src_cur, t_dst, t_src); + } +} -static void flecs_constants_dtor( - ecs_map_t *constants) +static +void flecs_stats_reduce_last( + ecs_metric_t *dst_cur, + ecs_metric_t *dst_last, + ecs_metric_t *src_cur, + int32_t t_dst, + int32_t t_src, + int32_t count) { - ecs_map_iter_t it = ecs_map_iter(constants); - while (ecs_map_next(&it)) { - ecs_enum_constant_t *c = ecs_map_ptr(&it); - ecs_os_free(ECS_CONST_CAST(char*, c->name)); - ecs_os_free(c); + int32_t t_dst_next = t_next(t_dst); + for (; dst_cur <= dst_last; dst_cur ++, src_cur ++) { + /* Reduce into previous value */ + ecs_metric_reduce_last(dst_cur, t_dst, count); + + /* Restore old value */ + dst_cur->gauge.avg[t_dst_next] = src_cur->gauge.avg[t_src]; + dst_cur->gauge.min[t_dst_next] = src_cur->gauge.min[t_src]; + dst_cur->gauge.max[t_dst_next] = src_cur->gauge.max[t_src]; + dst_cur->counter.value[t_dst_next] = src_cur->counter.value[t_src]; } - ecs_map_fini(constants); } -static void flecs_constants_copy( - ecs_map_t *dst, - const ecs_map_t *src) +static +void flecs_stats_repeat_last( + ecs_metric_t *cur, + ecs_metric_t *last, + int32_t t) { - ecs_map_copy(dst, src); + int32_t prev = t_prev(t); + for (; cur <= last; cur ++) { + ecs_metric_copy(cur, t, prev); + } +} - ecs_map_iter_t it = ecs_map_iter(dst); - while (ecs_map_next(&it)) { - ecs_enum_constant_t **r = ecs_map_ref(&it, ecs_enum_constant_t); - ecs_enum_constant_t *src_c = r[0]; - ecs_enum_constant_t *dst_c = ecs_os_calloc_t(ecs_enum_constant_t); - *dst_c = *src_c; - dst_c->name = ecs_os_strdup(dst_c->name); - r[0] = dst_c; +static +void flecs_stats_copy_last( + ecs_metric_t *dst_cur, + ecs_metric_t *dst_last, + ecs_metric_t *src_cur, + int32_t t_dst, + int32_t t_src) +{ + for (; dst_cur <= dst_last; dst_cur ++, src_cur ++) { + dst_cur->gauge.avg[t_dst] = src_cur->gauge.avg[t_src]; + dst_cur->gauge.min[t_dst] = src_cur->gauge.min[t_src]; + dst_cur->gauge.max[t_dst] = src_cur->gauge.max[t_src]; + dst_cur->counter.value[t_dst] = src_cur->counter.value[t_src]; } } -static ECS_COPY(EcsEnum, dst, src, { - flecs_constants_dtor(&dst->constants); - flecs_constants_copy(&dst->constants, &src->constants); -}) +void ecs_world_stats_get( + const ecs_world_t *world, + ecs_world_stats_t *s) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); -static ECS_MOVE(EcsEnum, dst, src, { - flecs_constants_dtor(&dst->constants); - dst->constants = src->constants; - ecs_os_zeromem(&src->constants); -}) + world = ecs_get_world(world); -static ECS_DTOR(EcsEnum, ptr, { flecs_constants_dtor(&ptr->constants); }) + int32_t t = s->t = t_next(s->t); + + double delta_frame_count = + ECS_COUNTER_RECORD(&s->frame.frame_count, t, world->info.frame_count_total); + ECS_COUNTER_RECORD(&s->frame.merge_count, t, world->info.merge_count_total); + ECS_COUNTER_RECORD(&s->frame.rematch_count, t, world->info.rematch_count_total); + ECS_COUNTER_RECORD(&s->frame.pipeline_build_count, t, world->info.pipeline_build_count_total); + ECS_COUNTER_RECORD(&s->frame.systems_ran, t, world->info.systems_ran_frame); + ECS_COUNTER_RECORD(&s->frame.observers_ran, t, world->info.observers_ran_frame); + ECS_COUNTER_RECORD(&s->frame.event_emit_count, t, world->event_id); + double delta_world_time = + ECS_COUNTER_RECORD(&s->performance.world_time_raw, t, world->info.world_time_total_raw); + ECS_COUNTER_RECORD(&s->performance.world_time, t, world->info.world_time_total); + ECS_COUNTER_RECORD(&s->performance.frame_time, t, world->info.frame_time_total); + ECS_COUNTER_RECORD(&s->performance.system_time, t, world->info.system_time_total); + ECS_COUNTER_RECORD(&s->performance.emit_time, t, world->info.emit_time_total); + ECS_COUNTER_RECORD(&s->performance.merge_time, t, world->info.merge_time_total); + ECS_COUNTER_RECORD(&s->performance.rematch_time, t, world->info.rematch_time_total); + ECS_GAUGE_RECORD(&s->performance.delta_time, t, delta_world_time); + if (ECS_NEQZERO(delta_world_time) && ECS_NEQZERO(delta_frame_count)) { + ECS_GAUGE_RECORD(&s->performance.fps, t, (double)1 / (delta_world_time / (double)delta_frame_count)); + } else { + ECS_GAUGE_RECORD(&s->performance.fps, t, 0); + } -/* EcsBitmask lifecycle */ + ECS_GAUGE_RECORD(&s->entities.count, t, flecs_entities_count(world)); + ECS_GAUGE_RECORD(&s->entities.not_alive_count, t, flecs_entities_not_alive_count(world)); -static ECS_COPY(EcsBitmask, dst, src, { - /* bitmask constant & enum constant have the same layout */ - flecs_constants_dtor(&dst->constants); - flecs_constants_copy(&dst->constants, &src->constants); -}) + ECS_GAUGE_RECORD(&s->components.tag_count, t, world->info.tag_id_count); + ECS_GAUGE_RECORD(&s->components.component_count, t, world->info.component_id_count); + ECS_GAUGE_RECORD(&s->components.pair_count, t, world->info.pair_id_count); + ECS_GAUGE_RECORD(&s->components.type_count, t, ecs_sparse_count(&world->type_info)); + ECS_COUNTER_RECORD(&s->components.create_count, t, world->info.id_create_total); + ECS_COUNTER_RECORD(&s->components.delete_count, t, world->info.id_delete_total); -static ECS_MOVE(EcsBitmask, dst, src, { - flecs_constants_dtor(&dst->constants); - dst->constants = src->constants; - ecs_os_zeromem(&src->constants); -}) + ECS_GAUGE_RECORD(&s->queries.query_count, t, ecs_count_id(world, EcsQuery)); + ECS_GAUGE_RECORD(&s->queries.observer_count, t, ecs_count_id(world, EcsObserver)); + if (ecs_is_alive(world, EcsSystem)) { + ECS_GAUGE_RECORD(&s->queries.system_count, t, ecs_count_id(world, EcsSystem)); + } + ECS_COUNTER_RECORD(&s->tables.create_count, t, world->info.table_create_total); + ECS_COUNTER_RECORD(&s->tables.delete_count, t, world->info.table_delete_total); + ECS_GAUGE_RECORD(&s->tables.count, t, world->info.table_count); + ECS_GAUGE_RECORD(&s->tables.empty_count, t, world->info.empty_table_count); + + ECS_COUNTER_RECORD(&s->commands.add_count, t, world->info.cmd.add_count); + ECS_COUNTER_RECORD(&s->commands.remove_count, t, world->info.cmd.remove_count); + ECS_COUNTER_RECORD(&s->commands.delete_count, t, world->info.cmd.delete_count); + ECS_COUNTER_RECORD(&s->commands.clear_count, t, world->info.cmd.clear_count); + ECS_COUNTER_RECORD(&s->commands.set_count, t, world->info.cmd.set_count); + ECS_COUNTER_RECORD(&s->commands.ensure_count, t, world->info.cmd.ensure_count); + ECS_COUNTER_RECORD(&s->commands.modified_count, t, world->info.cmd.modified_count); + ECS_COUNTER_RECORD(&s->commands.other_count, t, world->info.cmd.other_count); + ECS_COUNTER_RECORD(&s->commands.discard_count, t, world->info.cmd.discard_count); + ECS_COUNTER_RECORD(&s->commands.batched_entity_count, t, world->info.cmd.batched_entity_count); + ECS_COUNTER_RECORD(&s->commands.batched_count, t, world->info.cmd.batched_command_count); + + int64_t outstanding_allocs = ecs_os_api_malloc_count + + ecs_os_api_calloc_count - ecs_os_api_free_count; + ECS_COUNTER_RECORD(&s->memory.alloc_count, t, ecs_os_api_malloc_count + ecs_os_api_calloc_count); + ECS_COUNTER_RECORD(&s->memory.realloc_count, t, ecs_os_api_realloc_count); + ECS_COUNTER_RECORD(&s->memory.free_count, t, ecs_os_api_free_count); + ECS_GAUGE_RECORD(&s->memory.outstanding_alloc_count, t, outstanding_allocs); -static ECS_DTOR(EcsBitmask, ptr, { flecs_constants_dtor(&ptr->constants); }) + outstanding_allocs = ecs_block_allocator_alloc_count - ecs_block_allocator_free_count; + ECS_COUNTER_RECORD(&s->memory.block_alloc_count, t, ecs_block_allocator_alloc_count); + ECS_COUNTER_RECORD(&s->memory.block_free_count, t, ecs_block_allocator_free_count); + ECS_GAUGE_RECORD(&s->memory.block_outstanding_alloc_count, t, outstanding_allocs); + outstanding_allocs = ecs_stack_allocator_alloc_count - ecs_stack_allocator_free_count; + ECS_COUNTER_RECORD(&s->memory.stack_alloc_count, t, ecs_stack_allocator_alloc_count); + ECS_COUNTER_RECORD(&s->memory.stack_free_count, t, ecs_stack_allocator_free_count); + ECS_GAUGE_RECORD(&s->memory.stack_outstanding_alloc_count, t, outstanding_allocs); -/* EcsUnit lifecycle */ +#ifdef FLECS_HTTP + ECS_COUNTER_RECORD(&s->http.request_received_count, t, ecs_http_request_received_count); + ECS_COUNTER_RECORD(&s->http.request_invalid_count, t, ecs_http_request_invalid_count); + ECS_COUNTER_RECORD(&s->http.request_handled_ok_count, t, ecs_http_request_handled_ok_count); + ECS_COUNTER_RECORD(&s->http.request_handled_error_count, t, ecs_http_request_handled_error_count); + ECS_COUNTER_RECORD(&s->http.request_not_handled_count, t, ecs_http_request_not_handled_count); + ECS_COUNTER_RECORD(&s->http.request_preflight_count, t, ecs_http_request_preflight_count); + ECS_COUNTER_RECORD(&s->http.send_ok_count, t, ecs_http_send_ok_count); + ECS_COUNTER_RECORD(&s->http.send_error_count, t, ecs_http_send_error_count); + ECS_COUNTER_RECORD(&s->http.busy_count, t, ecs_http_busy_count); +#endif -static void dtor_unit( - EcsUnit *ptr) +error: + return; +} + +void ecs_world_stats_reduce( + ecs_world_stats_t *dst, + const ecs_world_stats_t *src) { - ecs_os_free(ptr->symbol); + flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), + ECS_METRIC_FIRST(src), (dst->t = t_next(dst->t)), src->t); } -static ECS_COPY(EcsUnit, dst, src, { - dtor_unit(dst); - dst->symbol = ecs_os_strdup(src->symbol); - dst->base = src->base; - dst->over = src->over; - dst->prefix = src->prefix; - dst->translation = src->translation; -}) +void ecs_world_stats_reduce_last( + ecs_world_stats_t *dst, + const ecs_world_stats_t *src, + int32_t count) +{ + flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), + ECS_METRIC_FIRST(src), (dst->t = t_prev(dst->t)), src->t, count); +} -static ECS_MOVE(EcsUnit, dst, src, { - dtor_unit(dst); - dst->symbol = src->symbol; - dst->base = src->base; - dst->over = src->over; - dst->prefix = src->prefix; - dst->translation = src->translation; +void ecs_world_stats_repeat_last( + ecs_world_stats_t *stats) +{ + flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats), + (stats->t = t_next(stats->t))); +} - src->symbol = NULL; - src->base = 0; - src->over = 0; - src->prefix = 0; - src->translation = (ecs_unit_translation_t){0}; -}) +void ecs_world_stats_copy_last( + ecs_world_stats_t *dst, + const ecs_world_stats_t *src) +{ + flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), + ECS_METRIC_FIRST(src), dst->t, t_next(src->t)); +} -static ECS_DTOR(EcsUnit, ptr, { dtor_unit(ptr); }) +void ecs_query_stats_get( + const ecs_world_t *world, + const ecs_query_t *query, + ecs_query_stats_t *s) +{ + ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); + (void)world; + int32_t t = s->t = t_next(s->t); + ecs_query_count_t counts = ecs_query_count(query); + ECS_GAUGE_RECORD(&s->result_count, t, counts.results); + ECS_GAUGE_RECORD(&s->matched_table_count, t, counts.tables); + ECS_GAUGE_RECORD(&s->matched_entity_count, t, counts.entities); -/* EcsUnitPrefix lifecycle */ +error: + return; +} -static void dtor_unit_prefix( - EcsUnitPrefix *ptr) +void ecs_query_cache_stats_reduce( + ecs_query_stats_t *dst, + const ecs_query_stats_t *src) { - ecs_os_free(ptr->symbol); + flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), + ECS_METRIC_FIRST(src), (dst->t = t_next(dst->t)), src->t); } -static ECS_COPY(EcsUnitPrefix, dst, src, { - dtor_unit_prefix(dst); - dst->symbol = ecs_os_strdup(src->symbol); - dst->translation = src->translation; -}) - -static ECS_MOVE(EcsUnitPrefix, dst, src, { - dtor_unit_prefix(dst); - dst->symbol = src->symbol; - dst->translation = src->translation; - - src->symbol = NULL; - src->translation = (ecs_unit_translation_t){0}; -}) - -static ECS_DTOR(EcsUnitPrefix, ptr, { dtor_unit_prefix(ptr); }) +void ecs_query_cache_stats_reduce_last( + ecs_query_stats_t *dst, + const ecs_query_stats_t *src, + int32_t count) +{ + flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), + ECS_METRIC_FIRST(src), (dst->t = t_prev(dst->t)), src->t, count); +} -/* Type initialization */ +void ecs_query_cache_stats_repeat_last( + ecs_query_stats_t *stats) +{ + flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats), + (stats->t = t_next(stats->t))); +} -static -const char* flecs_type_kind_str( - ecs_type_kind_t kind) +void ecs_query_cache_stats_copy_last( + ecs_query_stats_t *dst, + const ecs_query_stats_t *src) { - switch(kind) { - case EcsPrimitiveType: return "Primitive"; - case EcsBitmaskType: return "Bitmask"; - case EcsEnumType: return "Enum"; - case EcsStructType: return "Struct"; - case EcsArrayType: return "Array"; - case EcsVectorType: return "Vector"; - case EcsOpaqueType: return "Opaque"; - default: return "unknown"; - } + flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), + ECS_METRIC_FIRST(src), dst->t, t_next(src->t)); } -static -int flecs_init_type( - ecs_world_t *world, - ecs_entity_t type, - ecs_type_kind_t kind, - ecs_size_t size, - ecs_size_t alignment) +#ifdef FLECS_SYSTEM + +bool ecs_system_stats_get( + const ecs_world_t *world, + ecs_entity_t system, + ecs_system_stats_t *s) { - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(type != 0, ECS_INTERNAL_ERROR, NULL); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(system != 0, ECS_INVALID_PARAMETER, NULL); - EcsMetaType *meta_type = ecs_get_mut(world, type, EcsMetaType); - if (meta_type->kind == 0) { - meta_type->existing = ecs_has(world, type, EcsComponent); + world = ecs_get_world(world); - /* Ensure that component has a default constructor, to prevent crashing - * serializers on uninitialized values. */ - ecs_type_info_t *ti = flecs_type_info_ensure(world, type); - if (!ti->hooks.ctor) { - ti->hooks.ctor = ecs_default_ctor; - } - } else { - if (meta_type->kind != kind) { - ecs_err("type '%s' reregistered as '%s' (was '%s')", - ecs_get_name(world, type), - flecs_type_kind_str(kind), - flecs_type_kind_str(meta_type->kind)); - return -1; - } + const ecs_system_t *ptr = flecs_poly_get(world, system, ecs_system_t); + if (!ptr) { + return false; } - if (!meta_type->existing) { - EcsComponent *comp = ecs_get_mut(world, type, EcsComponent); - comp->size = size; - comp->alignment = alignment; - ecs_modified(world, type, EcsComponent); - } else { - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - if (comp->size < size) { - ecs_err("computed size (%d) for '%s' is larger than actual type (%d)", - size, ecs_get_name(world, type), comp->size); - return -1; - } - if (comp->alignment < alignment) { - ecs_err("computed alignment (%d) for '%s' is larger than actual type (%d)", - alignment, ecs_get_name(world, type), comp->alignment); - return -1; - } - if (comp->size == size && comp->alignment != alignment) { - if (comp->alignment < alignment) { - ecs_err("computed size for '%s' matches with actual type but " - "alignment is different (%d vs. %d)", ecs_get_name(world, type), - alignment, comp->alignment); - } - } - - meta_type->partial = comp->size != size; - } + ecs_query_stats_get(world, ptr->query, &s->query); + int32_t t = s->query.t; - meta_type->kind = kind; - ecs_modified(world, type, EcsMetaType); + ECS_COUNTER_RECORD(&s->time_spent, t, ptr->time_spent); - return 0; -} + s->task = !(ptr->query->flags & EcsQueryMatchThis); -#define init_type_t(world, type, kind, T) \ - flecs_init_type(world, type, kind, ECS_SIZEOF(T), ECS_ALIGNOF(T)) + return true; +error: + return false; +} -static -void flecs_set_struct_member( - ecs_member_t *member, - ecs_entity_t entity, - const char *name, - ecs_entity_t type, - int32_t count, - int32_t offset, - ecs_entity_t unit, - EcsMemberRanges *ranges) +void ecs_system_stats_reduce( + ecs_system_stats_t *dst, + const ecs_system_stats_t *src) { - member->member = entity; - member->type = type; - member->count = count; - member->unit = unit; - member->offset = offset; - - if (!count) { - member->count = 1; - } - - ecs_os_strset(ECS_CONST_CAST(char**, &member->name), name); + ecs_query_cache_stats_reduce(&dst->query, &src->query); + dst->task = src->task; + flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), + ECS_METRIC_FIRST(src), dst->query.t, src->query.t); +} - if (ranges) { - member->range = ranges->value; - member->error_range = ranges->error; - member->warning_range = ranges->warning; - } else { - ecs_os_zeromem(&member->range); - ecs_os_zeromem(&member->error_range); - ecs_os_zeromem(&member->warning_range); - } +void ecs_system_stats_reduce_last( + ecs_system_stats_t *dst, + const ecs_system_stats_t *src, + int32_t count) +{ + ecs_query_cache_stats_reduce_last(&dst->query, &src->query, count); + dst->task = src->task; + flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), + ECS_METRIC_FIRST(src), dst->query.t, src->query.t, count); } -static -int flecs_add_member_to_struct( - ecs_world_t *world, - ecs_entity_t type, - ecs_entity_t member, - EcsMember *m, - EcsMemberRanges *ranges) +void ecs_system_stats_repeat_last( + ecs_system_stats_t *stats) { - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(type != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(member != 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(m != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_query_cache_stats_repeat_last(&stats->query); + flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats), + (stats->query.t)); +} - const char *name = ecs_get_name(world, member); - if (!name) { - char *path = ecs_get_fullpath(world, type); - ecs_err("member for struct '%s' does not have a name", path); - ecs_os_free(path); - return -1; - } +void ecs_system_stats_copy_last( + ecs_system_stats_t *dst, + const ecs_system_stats_t *src) +{ + ecs_query_cache_stats_copy_last(&dst->query, &src->query); + dst->task = src->task; + flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), + ECS_METRIC_FIRST(src), dst->query.t, t_next(src->query.t)); +} - if (!m->type) { - char *path = ecs_get_fullpath(world, member); - ecs_err("member '%s' does not have a type", path); - ecs_os_free(path); - return -1; - } +#endif - if (ecs_get_typeid(world, m->type) == 0) { - char *path = ecs_get_fullpath(world, member); - char *ent_path = ecs_get_fullpath(world, m->type); - ecs_err("member '%s.type' is '%s' which is not a type", path, ent_path); - ecs_os_free(path); - ecs_os_free(ent_path); - return -1; - } +#ifdef FLECS_PIPELINE - ecs_entity_t unit = m->unit; - if (unit) { - if (!ecs_has(world, unit, EcsUnit)) { - ecs_err("entity '%s' for member '%s' is not a unit", - ecs_get_name(world, unit), name); - return -1; - } +bool ecs_pipeline_stats_get( + ecs_world_t *stage, + ecs_entity_t pipeline, + ecs_pipeline_stats_t *s) +{ + ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(pipeline != 0, ECS_INVALID_PARAMETER, NULL); - if (ecs_has(world, m->type, EcsUnit) && m->type != unit) { - ecs_err("unit mismatch for type '%s' and unit '%s' for member '%s'", - ecs_get_name(world, m->type), ecs_get_name(world, unit), name); - return -1; - } - } else { - if (ecs_has(world, m->type, EcsUnit)) { - unit = m->type; - m->unit = unit; - } + const ecs_world_t *world = ecs_get_world(stage); + const EcsPipeline *pqc = ecs_get(world, pipeline, EcsPipeline); + if (!pqc) { + return false; } + ecs_pipeline_state_t *pq = pqc->state; + ecs_assert(pq != NULL, ECS_INTERNAL_ERROR, NULL); - EcsStruct *s = ecs_get_mut(world, type, EcsStruct); - ecs_assert(s != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t sys_count = 0, active_sys_count = 0; - /* First check if member is already added to struct */ - ecs_member_t *members = ecs_vec_first_t(&s->members, ecs_member_t); - int32_t i, count = ecs_vec_count(&s->members); - for (i = 0; i < count; i ++) { - if (members[i].member == member) { - flecs_set_struct_member(&members[i], member, name, m->type, - m->count, m->offset, unit, ranges); - break; + /* Count number of active systems */ + ecs_iter_t it = ecs_query_iter(stage, pq->query); + while (ecs_query_next(&it)) { + if (flecs_id_record_get_table(pq->idr_inactive, it.table) != NULL) { + continue; } + active_sys_count += it.count; } - /* If member wasn't added yet, add a new element to vector */ - if (i == count) { - ecs_vec_init_if_t(&s->members, ecs_member_t); - ecs_member_t *elem = ecs_vec_append_t(NULL, &s->members, ecs_member_t); - elem->name = NULL; - flecs_set_struct_member(elem, member, name, m->type, - m->count, m->offset, unit, ranges); + /* Count total number of systems in pipeline */ + it = ecs_query_iter(stage, pq->query); + while (ecs_query_next(&it)) { + sys_count += it.count; + } - /* Reobtain members array in case it was reallocated */ - members = ecs_vec_first_t(&s->members, ecs_member_t); - count ++; - } + /* Also count synchronization points */ + ecs_vec_t *ops = &pq->ops; + ecs_pipeline_op_t *op = ecs_vec_first_t(ops, ecs_pipeline_op_t); + ecs_pipeline_op_t *op_last = ecs_vec_last_t(ops, ecs_pipeline_op_t); + int32_t pip_count = active_sys_count + ecs_vec_count(ops); - bool explicit_offset = false; - if (m->offset) { - explicit_offset = true; + if (!sys_count) { + return false; } - /* Compute member offsets and size & alignment of struct */ - ecs_size_t size = 0; - ecs_size_t alignment = 0; - - if (!explicit_offset) { - for (i = 0; i < count; i ++) { - ecs_member_t *elem = &members[i]; + if (op) { + ecs_entity_t *systems = NULL; + if (pip_count) { + ecs_vec_init_if_t(&s->systems, ecs_entity_t); + ecs_vec_set_count_t(NULL, &s->systems, ecs_entity_t, pip_count); + systems = ecs_vec_first_t(&s->systems, ecs_entity_t); - ecs_assert(elem->name != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(elem->type != 0, ECS_INTERNAL_ERROR, NULL); + /* Populate systems vector, keep track of sync points */ + it = ecs_query_iter(stage, pq->query); + + int32_t i, i_system = 0, ran_since_merge = 0; + while (ecs_query_next(&it)) { + if (flecs_id_record_get_table(pq->idr_inactive, it.table) != NULL) { + continue; + } - /* Get component of member type to get its size & alignment */ - const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); - if (!mbr_comp) { - char *path = ecs_get_fullpath(world, elem->type); - ecs_err("member '%s' is not a type", path); - ecs_os_free(path); - return -1; + for (i = 0; i < it.count; i ++) { + systems[i_system ++] = it.entities[i]; + ran_since_merge ++; + if (op != op_last && ran_since_merge == op->count) { + ran_since_merge = 0; + op++; + systems[i_system ++] = 0; /* 0 indicates a merge point */ + } + } } - ecs_size_t member_size = mbr_comp->size; - ecs_size_t member_alignment = mbr_comp->alignment; - - if (!member_size || !member_alignment) { - char *path = ecs_get_fullpath(world, elem->type); - ecs_err("member '%s' has 0 size/alignment", path); - ecs_os_free(path); - return -1; - } + systems[i_system ++] = 0; /* Last merge */ + ecs_assert(pip_count == i_system, ECS_INTERNAL_ERROR, NULL); + } else { + ecs_vec_fini_t(NULL, &s->systems, ecs_entity_t); + } - member_size *= elem->count; - size = ECS_ALIGN(size, member_alignment); - elem->size = member_size; - elem->offset = size; + /* Get sync point statistics */ + int32_t i, count = ecs_vec_count(ops); + if (count) { + ecs_vec_init_if_t(&s->sync_points, ecs_sync_stats_t); + ecs_vec_set_min_count_zeromem_t(NULL, &s->sync_points, ecs_sync_stats_t, count); + op = ecs_vec_first_t(ops, ecs_pipeline_op_t); - /* Synchronize offset with Member component */ - if (elem->member == member) { - m->offset = elem->offset; - } else { - EcsMember *other = ecs_get_mut(world, elem->member, EcsMember); - other->offset = elem->offset; - } + for (i = 0; i < count; i ++) { + ecs_pipeline_op_t *cur = &op[i]; + ecs_sync_stats_t *el = ecs_vec_get_t(&s->sync_points, + ecs_sync_stats_t, i); - size += member_size; + ECS_COUNTER_RECORD(&el->time_spent, s->t, cur->time_spent); + ECS_COUNTER_RECORD(&el->commands_enqueued, s->t, + cur->commands_enqueued); - if (member_alignment > alignment) { - alignment = member_alignment; + el->system_count = cur->count; + el->multi_threaded = cur->multi_threaded; + el->immediate = cur->immediate; } } - } else { - /* If members have explicit offsets, we can't rely on computed - * size/alignment values. Calculate size as if this is the last member - * instead, since this will validate if the member fits in the struct. - * It doesn't matter if the size is smaller than the actual struct size - * because flecs_init_type function compares computed size with actual - * (component) size to determine if the type is partial. */ - ecs_member_t *elem = &members[i]; + } - ecs_assert(elem->name != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(elem->type != 0, ECS_INTERNAL_ERROR, NULL); + s->t = t_next(s->t); - /* Get component of member type to get its size & alignment */ - const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); - if (!mbr_comp) { - char *path = ecs_get_fullpath(world, elem->type); - ecs_err("member '%s' is not a type", path); - ecs_os_free(path); - return -1; - } + return true; +error: + return false; +} - ecs_size_t member_size = mbr_comp->size; - ecs_size_t member_alignment = mbr_comp->alignment; +void ecs_pipeline_stats_fini( + ecs_pipeline_stats_t *stats) +{ + ecs_vec_fini_t(NULL, &stats->systems, ecs_entity_t); + ecs_vec_fini_t(NULL, &stats->sync_points, ecs_sync_stats_t); +} - if (!member_size || !member_alignment) { - char *path = ecs_get_fullpath(world, elem->type); - ecs_err("member '%s' has 0 size/alignment", path); - ecs_os_free(path); - return -1; - } +void ecs_pipeline_stats_reduce( + ecs_pipeline_stats_t *dst, + const ecs_pipeline_stats_t *src) +{ + int32_t system_count = ecs_vec_count(&src->systems); + ecs_vec_init_if_t(&dst->systems, ecs_entity_t); + ecs_vec_set_count_t(NULL, &dst->systems, ecs_entity_t, system_count); + ecs_entity_t *dst_systems = ecs_vec_first_t(&dst->systems, ecs_entity_t); + ecs_entity_t *src_systems = ecs_vec_first_t(&src->systems, ecs_entity_t); + ecs_os_memcpy_n(dst_systems, src_systems, ecs_entity_t, system_count); - member_size *= elem->count; - elem->size = member_size; + int32_t i, sync_count = ecs_vec_count(&src->sync_points); + ecs_vec_init_if_t(&dst->sync_points, ecs_sync_stats_t); + ecs_vec_set_min_count_zeromem_t(NULL, &dst->sync_points, ecs_sync_stats_t, sync_count); + ecs_sync_stats_t *dst_syncs = ecs_vec_first_t(&dst->sync_points, ecs_sync_stats_t); + ecs_sync_stats_t *src_syncs = ecs_vec_first_t(&src->sync_points, ecs_sync_stats_t); + for (i = 0; i < sync_count; i ++) { + ecs_sync_stats_t *dst_el = &dst_syncs[i]; + ecs_sync_stats_t *src_el = &src_syncs[i]; + flecs_stats_reduce(ECS_METRIC_FIRST(dst_el), ECS_METRIC_LAST(dst_el), + ECS_METRIC_FIRST(src_el), dst->t, src->t); + dst_el->system_count = src_el->system_count; + dst_el->multi_threaded = src_el->multi_threaded; + dst_el->immediate = src_el->immediate; + } + + dst->t = t_next(dst->t); +} - size = elem->offset + member_size; +void ecs_pipeline_stats_reduce_last( + ecs_pipeline_stats_t *dst, + const ecs_pipeline_stats_t *src, + int32_t count) +{ + int32_t i, sync_count = ecs_vec_count(&src->sync_points); + ecs_sync_stats_t *dst_syncs = ecs_vec_first_t(&dst->sync_points, ecs_sync_stats_t); + ecs_sync_stats_t *src_syncs = ecs_vec_first_t(&src->sync_points, ecs_sync_stats_t); - const EcsComponent* comp = ecs_get(world, type, EcsComponent); - alignment = comp->alignment; + for (i = 0; i < sync_count; i ++) { + ecs_sync_stats_t *dst_el = &dst_syncs[i]; + ecs_sync_stats_t *src_el = &src_syncs[i]; + flecs_stats_reduce_last(ECS_METRIC_FIRST(dst_el), ECS_METRIC_LAST(dst_el), + ECS_METRIC_FIRST(src_el), dst->t, src->t, count); + dst_el->system_count = src_el->system_count; + dst_el->multi_threaded = src_el->multi_threaded; + dst_el->immediate = src_el->immediate; } - if (size == 0) { - ecs_err("struct '%s' has 0 size", ecs_get_name(world, type)); - return -1; - } + dst->t = t_prev(dst->t); +} - if (alignment == 0) { - ecs_err("struct '%s' has 0 alignment", ecs_get_name(world, type)); - return -1; +void ecs_pipeline_stats_repeat_last( + ecs_pipeline_stats_t *stats) +{ + int32_t i, sync_count = ecs_vec_count(&stats->sync_points); + ecs_sync_stats_t *syncs = ecs_vec_first_t(&stats->sync_points, ecs_sync_stats_t); + + for (i = 0; i < sync_count; i ++) { + ecs_sync_stats_t *el = &syncs[i]; + flecs_stats_repeat_last(ECS_METRIC_FIRST(el), ECS_METRIC_LAST(el), + (stats->t)); } - /* Align struct size to struct alignment */ - size = ECS_ALIGN(size, alignment); + stats->t = t_next(stats->t); +} - ecs_modified(world, type, EcsStruct); +void ecs_pipeline_stats_copy_last( + ecs_pipeline_stats_t *dst, + const ecs_pipeline_stats_t *src) +{ + int32_t i, sync_count = ecs_vec_count(&src->sync_points); + ecs_vec_init_if_t(&dst->sync_points, ecs_sync_stats_t); + ecs_vec_set_min_count_zeromem_t(NULL, &dst->sync_points, ecs_sync_stats_t, sync_count); + ecs_sync_stats_t *dst_syncs = ecs_vec_first_t(&dst->sync_points, ecs_sync_stats_t); + ecs_sync_stats_t *src_syncs = ecs_vec_first_t(&src->sync_points, ecs_sync_stats_t); - /* Do this last as it triggers the update of EcsMetaTypeSerialized */ - if (flecs_init_type(world, type, EcsStructType, size, alignment)) { - return -1; + for (i = 0; i < sync_count; i ++) { + ecs_sync_stats_t *dst_el = &dst_syncs[i]; + ecs_sync_stats_t *src_el = &src_syncs[i]; + flecs_stats_copy_last(ECS_METRIC_FIRST(dst_el), ECS_METRIC_LAST(dst_el), + ECS_METRIC_FIRST(src_el), dst->t, t_next(src->t)); + dst_el->system_count = src_el->system_count; + dst_el->multi_threaded = src_el->multi_threaded; + dst_el->immediate = src_el->immediate; } +} - /* If current struct is also a member, assign to itself */ - if (ecs_has(world, type, EcsMember)) { - EcsMember *type_mbr = ecs_get_mut(world, type, EcsMember); - ecs_assert(type_mbr != NULL, ECS_INTERNAL_ERROR, NULL); +#endif - type_mbr->type = type; - type_mbr->count = 1; +void ecs_world_stats_log( + const ecs_world_t *world, + const ecs_world_stats_t *s) +{ + int32_t t = s->t; - ecs_modified(world, type, EcsMember); - } + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); - return 0; + world = ecs_get_world(world); + + flecs_counter_print("Frame", t, &s->frame.frame_count); + ecs_trace("-------------------------------------"); + flecs_counter_print("pipeline rebuilds", t, &s->frame.pipeline_build_count); + flecs_counter_print("systems ran", t, &s->frame.systems_ran); + ecs_trace(""); + flecs_metric_print("target FPS", (ecs_float_t)world->info.target_fps); + flecs_metric_print("time scale", (ecs_float_t)world->info.time_scale); + ecs_trace(""); + flecs_gauge_print("actual FPS", t, &s->performance.fps); + flecs_counter_print("frame time", t, &s->performance.frame_time); + flecs_counter_print("system time", t, &s->performance.system_time); + flecs_counter_print("merge time", t, &s->performance.merge_time); + flecs_counter_print("simulation time elapsed", t, &s->performance.world_time); + ecs_trace(""); + flecs_gauge_print("tag id count", t, &s->components.tag_count); + flecs_gauge_print("component id count", t, &s->components.component_count); + flecs_gauge_print("pair id count", t, &s->components.pair_count); + flecs_gauge_print("type count", t, &s->components.type_count); + flecs_counter_print("id create count", t, &s->components.create_count); + flecs_counter_print("id delete count", t, &s->components.delete_count); + ecs_trace(""); + flecs_gauge_print("alive entity count", t, &s->entities.count); + flecs_gauge_print("not alive entity count", t, &s->entities.not_alive_count); + ecs_trace(""); + flecs_gauge_print("query count", t, &s->queries.query_count); + flecs_gauge_print("observer count", t, &s->queries.observer_count); + flecs_gauge_print("system count", t, &s->queries.system_count); + ecs_trace(""); + flecs_gauge_print("table count", t, &s->tables.count); + flecs_gauge_print("empty table count", t, &s->tables.empty_count); + flecs_counter_print("table create count", t, &s->tables.create_count); + flecs_counter_print("table delete count", t, &s->tables.delete_count); + ecs_trace(""); + flecs_counter_print("add commands", t, &s->commands.add_count); + flecs_counter_print("remove commands", t, &s->commands.remove_count); + flecs_counter_print("delete commands", t, &s->commands.delete_count); + flecs_counter_print("clear commands", t, &s->commands.clear_count); + flecs_counter_print("set commands", t, &s->commands.set_count); + flecs_counter_print("ensure commands", t, &s->commands.ensure_count); + flecs_counter_print("modified commands", t, &s->commands.modified_count); + flecs_counter_print("other commands", t, &s->commands.other_count); + flecs_counter_print("discarded commands", t, &s->commands.discard_count); + flecs_counter_print("batched entities", t, &s->commands.batched_entity_count); + flecs_counter_print("batched commands", t, &s->commands.batched_count); + ecs_trace(""); + +error: + return; } -static -int flecs_add_constant_to_enum( - ecs_world_t *world, - ecs_entity_t type, - ecs_entity_t e, - ecs_id_t constant_id) -{ - EcsEnum *ptr = ecs_get_mut(world, type, EcsEnum); +#endif - /* Remove constant from map if it was already added */ - ecs_map_iter_t it = ecs_map_iter(&ptr->constants); - while (ecs_map_next(&it)) { - ecs_enum_constant_t *c = ecs_map_ptr(&it); - if (c->constant == e) { - ecs_os_free(ECS_CONST_CAST(char*, c->name)); - ecs_map_remove_free(&ptr->constants, ecs_map_key(&it)); - } - } +/** + * @file addons/stats/system_monitor.c + * @brief Stats addon system monitor + */ - /* Check if constant sets explicit value */ - int32_t value = 0; - bool value_set = false; - if (ecs_id_is_pair(constant_id)) { - if (ecs_pair_second(world, constant_id) != ecs_id(ecs_i32_t)) { - char *path = ecs_get_fullpath(world, e); - ecs_err("expected i32 type for enum constant '%s'", path); - ecs_os_free(path); - return -1; - } - const int32_t *value_ptr = ecs_get_pair_object( - world, e, EcsConstant, ecs_i32_t); - ecs_assert(value_ptr != NULL, ECS_INTERNAL_ERROR, NULL); - value = *value_ptr; - value_set = true; - } +#ifdef FLECS_STATS - /* Make sure constant value doesn't conflict if set / find the next value */ - it = ecs_map_iter(&ptr->constants); +ECS_COMPONENT_DECLARE(EcsSystemStats); + +static +void flecs_system_monitor_dtor(EcsSystemStats *ptr) { + ecs_map_iter_t it = ecs_map_iter(&ptr->stats); while (ecs_map_next(&it)) { - ecs_enum_constant_t *c = ecs_map_ptr(&it); - if (value_set) { - if (c->value == value) { - char *path = ecs_get_fullpath(world, e); - ecs_err("conflicting constant value %d for '%s' (other is '%s')", - value, path, c->name); - ecs_os_free(path); - return -1; - } - } else { - if (c->value >= value) { - value = c->value + 1; - } - } + ecs_system_stats_t *stats = ecs_map_ptr(&it); + ecs_os_free(stats); } + ecs_map_fini(&ptr->stats); +} - ecs_map_init_if(&ptr->constants, &world->allocator); - ecs_enum_constant_t *c = ecs_map_insert_alloc_t(&ptr->constants, - ecs_enum_constant_t, (ecs_map_key_t)value); - c->name = ecs_os_strdup(ecs_get_name(world, e)); - c->value = value; - c->constant = e; +static ECS_CTOR(EcsSystemStats, ptr, { + ecs_os_zeromem(ptr); + ecs_map_init(&ptr->stats, NULL); +}) - ecs_i32_t *cptr = ecs_get_mut_pair_object( - world, e, EcsConstant, ecs_i32_t); - ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL); - cptr[0] = value; +static ECS_COPY(EcsSystemStats, dst, src, { + (void)dst; + (void)src; + ecs_abort(ECS_INVALID_OPERATION, "cannot copy system stats component"); +}) - cptr = ecs_get_mut_id(world, e, type); - cptr[0] = value; +static ECS_MOVE(EcsSystemStats, dst, src, { + flecs_system_monitor_dtor(dst); + ecs_os_memcpy_t(dst, src, EcsSystemStats); + ecs_os_zeromem(src); +}) - return 0; +static ECS_DTOR(EcsSystemStats, ptr, { + flecs_system_monitor_dtor(ptr); +}) + +static +void flecs_system_stats_set_t( + void *stats, int32_t t) +{ + ecs_assert(t >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(t < ECS_STAT_WINDOW, ECS_INTERNAL_ERROR, NULL); + ((ecs_system_stats_t*)stats)->query.t = t; } static -int flecs_add_constant_to_bitmask( - ecs_world_t *world, - ecs_entity_t type, - ecs_entity_t e, - ecs_id_t constant_id) +void flecs_system_stats_copy_last( + void *stats, + void *src) { - EcsBitmask *ptr = ecs_get_mut(world, type, EcsBitmask); - - /* Remove constant from map if it was already added */ - ecs_map_iter_t it = ecs_map_iter(&ptr->constants); - while (ecs_map_next(&it)) { - ecs_bitmask_constant_t *c = ecs_map_ptr(&it); - if (c->constant == e) { - ecs_os_free(ECS_CONST_CAST(char*, c->name)); - ecs_map_remove_free(&ptr->constants, ecs_map_key(&it)); - } - } + ecs_system_stats_copy_last(stats, src); +} - /* Check if constant sets explicit value */ - uint32_t value = 1; - if (ecs_id_is_pair(constant_id)) { - if (ecs_pair_second(world, constant_id) != ecs_id(ecs_u32_t)) { - char *path = ecs_get_fullpath(world, e); - ecs_err("expected u32 type for bitmask constant '%s'", path); - ecs_os_free(path); - return -1; - } +static +void flecs_system_stats_get( + ecs_world_t *world, + ecs_entity_t res, + void *stats) +{ + ecs_system_stats_get(world, res, stats); +} - const uint32_t *value_ptr = ecs_get_pair_object( - world, e, EcsConstant, ecs_u32_t); - ecs_assert(value_ptr != NULL, ECS_INTERNAL_ERROR, NULL); - value = *value_ptr; - } else { - value = 1u << (ecs_u32_t)ecs_map_count(&ptr->constants); - } +static +void flecs_system_stats_reduce( + void *stats, + void *src) +{ + ecs_system_stats_reduce(stats, src); +} - /* Make sure constant value doesn't conflict */ - it = ecs_map_iter(&ptr->constants); - while (ecs_map_next(&it)) { - ecs_bitmask_constant_t *c = ecs_map_ptr(&it); - if (c->value == value) { - char *path = ecs_get_fullpath(world, e); - ecs_err("conflicting constant value for '%s' (other is '%s')", - path, c->name); - ecs_os_free(path); - return -1; - } - } +static +void flecs_system_stats_reduce_last( + void *stats, + void *last, + int32_t reduce_count) +{ + ecs_system_stats_reduce_last(stats, last, reduce_count); +} - ecs_map_init_if(&ptr->constants, &world->allocator); +static +void flecs_system_stats_repeat_last( + void* stats) +{ + ecs_system_stats_repeat_last(stats); +} - ecs_bitmask_constant_t *c = ecs_map_insert_alloc_t(&ptr->constants, - ecs_bitmask_constant_t, value); - c->name = ecs_os_strdup(ecs_get_name(world, e)); - c->value = value; - c->constant = e; +void FlecsSystemMonitorImport( + ecs_world_t *world) +{ + ECS_COMPONENT_DEFINE(world, EcsSystemStats); - ecs_u32_t *cptr = ecs_get_mut_pair_object( - world, e, EcsConstant, ecs_u32_t); - ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL); - cptr[0] = value; + ecs_set_hooks(world, EcsSystemStats, { + .ctor = ecs_ctor(EcsSystemStats), + .copy = ecs_copy(EcsSystemStats), + .move = ecs_move(EcsSystemStats), + .dtor = ecs_dtor(EcsSystemStats) + }); - cptr = ecs_get_mut_id(world, e, type); - cptr[0] = value; + ecs_stats_api_t api = { + .copy_last = flecs_system_stats_copy_last, + .get = flecs_system_stats_get, + .reduce = flecs_system_stats_reduce, + .reduce_last = flecs_system_stats_reduce_last, + .repeat_last = flecs_system_stats_repeat_last, + .set_t = flecs_system_stats_set_t, + .stats_size = ECS_SIZEOF(ecs_system_stats_t), + .monitor_component_id = ecs_id(EcsSystemStats), + .query_component_id = EcsSystem + }; - return 0; + flecs_stats_api_import(world, &api); } +#endif + +/** + * @file addons/stats/world_monitor.c + * @brief Stats addon world monitor. + */ + + +#ifdef FLECS_STATS + +ECS_COMPONENT_DECLARE(EcsWorldStats); + static -void flecs_set_primitive(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsPrimitive *type = ecs_field(it, EcsPrimitive, 1); +void flecs_world_stats_get( + ecs_world_t *world, ecs_entity_t res, void *stats) +{ + (void)res; + ecs_world_stats_get(world, stats); +} - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - switch(type->kind) { - case EcsBool: - init_type_t(world, e, EcsPrimitiveType, bool); - break; - case EcsChar: - init_type_t(world, e, EcsPrimitiveType, char); - break; - case EcsByte: - init_type_t(world, e, EcsPrimitiveType, bool); - break; - case EcsU8: - init_type_t(world, e, EcsPrimitiveType, uint8_t); - break; - case EcsU16: - init_type_t(world, e, EcsPrimitiveType, uint16_t); - break; - case EcsU32: - init_type_t(world, e, EcsPrimitiveType, uint32_t); - break; - case EcsU64: - init_type_t(world, e, EcsPrimitiveType, uint64_t); - break; - case EcsI8: - init_type_t(world, e, EcsPrimitiveType, int8_t); - break; - case EcsI16: - init_type_t(world, e, EcsPrimitiveType, int16_t); - break; - case EcsI32: - init_type_t(world, e, EcsPrimitiveType, int32_t); - break; - case EcsI64: - init_type_t(world, e, EcsPrimitiveType, int64_t); - break; - case EcsF32: - init_type_t(world, e, EcsPrimitiveType, float); - break; - case EcsF64: - init_type_t(world, e, EcsPrimitiveType, double); - break; - case EcsUPtr: - init_type_t(world, e, EcsPrimitiveType, uintptr_t); - break; - case EcsIPtr: - init_type_t(world, e, EcsPrimitiveType, intptr_t); - break; - case EcsString: - init_type_t(world, e, EcsPrimitiveType, char*); - break; - case EcsEntity: - init_type_t(world, e, EcsPrimitiveType, ecs_entity_t); - break; - case EcsId: - init_type_t(world, e, EcsPrimitiveType, ecs_id_t); - break; - } - } +static +void flecs_world_stats_set_t( + void *stats, int32_t t) +{ + ecs_assert(t >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(t < ECS_STAT_WINDOW, ECS_INTERNAL_ERROR, NULL); + ((ecs_world_stats_t*)stats)->t = t; } static -void flecs_set_member(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsMember *member = ecs_field(it, EcsMember, 1); - EcsMemberRanges *ranges = ecs_table_get_id(world, it->table, - ecs_id(EcsMemberRanges), it->offset); +void flecs_world_stats_copy_last( + void *stats, + void *src) +{ + ecs_world_stats_copy_last(stats, src); +} - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); - if (!parent) { - ecs_err("missing parent for member '%s'", ecs_get_name(world, e)); - continue; - } +static +void flecs_world_stats_reduce( + void *stats, + void *src) +{ + ecs_world_stats_reduce(stats, src); +} - flecs_add_member_to_struct(world, parent, e, &member[i], - ranges ? &ranges[i] : NULL); - } +static +void flecs_world_stats_reduce_last( + void *stats, + void *last, + int32_t reduce_count) +{ + ecs_world_stats_reduce_last(stats, last, reduce_count); } static -void flecs_set_member_ranges(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsMemberRanges *ranges = ecs_field(it, EcsMemberRanges, 1); - EcsMember *member = ecs_table_get_id(world, it->table, - ecs_id(EcsMember), it->offset); - if (!member) { - return; - } +void flecs_world_stats_repeat_last( + void* stats) +{ + ecs_world_stats_repeat_last(stats); +} - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); - if (!parent) { - ecs_err("missing parent for member '%s'", ecs_get_name(world, e)); - continue; - } +void FlecsWorldMonitorImport( + ecs_world_t *world) +{ + ECS_COMPONENT_DEFINE(world, EcsWorldStats); - flecs_add_member_to_struct(world, parent, e, &member[i], - &ranges[i]); - } + ecs_set_hooks(world, EcsWorldStats, { + .ctor = flecs_default_ctor + }); + + ecs_stats_api_t api = { + .copy_last = flecs_world_stats_copy_last, + .get = flecs_world_stats_get, + .reduce = flecs_world_stats_reduce, + .reduce_last = flecs_world_stats_reduce_last, + .repeat_last = flecs_world_stats_repeat_last, + .set_t = flecs_world_stats_set_t, + .fini = NULL, + .stats_size = ECS_SIZEOF(ecs_world_stats_t), + .monitor_component_id = ecs_id(EcsWorldStats) + }; + + flecs_stats_api_import(world, &api); } -static -void flecs_add_enum(ecs_iter_t *it) { - ecs_world_t *world = it->world; +#endif - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; +/** + * @file addons/world_summary.c + * @brief Monitor addon. + */ - if (init_type_t(world, e, EcsEnumType, ecs_i32_t)) { - continue; - } - ecs_add_id(world, e, EcsExclusive); - ecs_add_id(world, e, EcsOneOf); - ecs_add_id(world, e, EcsTag); - } -} +#ifdef FLECS_STATS -static -void flecs_add_bitmask(ecs_iter_t *it) { - ecs_world_t *world = it->world; +ECS_COMPONENT_DECLARE(EcsWorldSummary); - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; +static +void flecs_copy_world_summary( + ecs_world_t *world, + EcsWorldSummary *dst) +{ + const ecs_world_info_t *info = ecs_get_world_info(world); - if (init_type_t(world, e, EcsBitmaskType, ecs_u32_t)) { - continue; - } - } -} + dst->target_fps = (double)info->target_fps; + dst->time_scale = (double)info->time_scale; -static -void flecs_add_constant(ecs_iter_t *it) { - ecs_world_t *world = it->world; + dst->frame_time_last = (double)info->frame_time_total - dst->frame_time_total; + dst->system_time_last = (double)info->system_time_total - dst->system_time_total; + dst->merge_time_last = (double)info->merge_time_total - dst->merge_time_total; - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); - if (!parent) { - ecs_err("missing parent for constant '%s'", ecs_get_name(world, e)); - continue; - } + dst->frame_time_total = (double)info->frame_time_total; + dst->system_time_total = (double)info->system_time_total; + dst->merge_time_total = (double)info->merge_time_total; - if (ecs_has(world, parent, EcsEnum)) { - flecs_add_constant_to_enum(world, parent, e, it->event_id); - } else if (ecs_has(world, parent, EcsBitmask)) { - flecs_add_constant_to_bitmask(world, parent, e, it->event_id); - } - } + dst->frame_count ++; + dst->command_count += + info->cmd.add_count + + info->cmd.remove_count + + info->cmd.delete_count + + info->cmd.clear_count + + info->cmd.set_count + + info->cmd.ensure_count + + info->cmd.modified_count + + info->cmd.discard_count + + info->cmd.event_count + + info->cmd.other_count; + + dst->build_info = *ecs_get_build_info(); } static -void flecs_set_array(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsArray *array = ecs_field(it, EcsArray, 1); +void UpdateWorldSummary(ecs_iter_t *it) { + EcsWorldSummary *summary = ecs_field(it, EcsWorldSummary, 0); - int i, count = it->count; + int32_t i, count = it->count; for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_entity_t elem_type = array[i].type; - int32_t elem_count = array[i].count; - - if (!elem_type) { - ecs_err("array '%s' has no element type", ecs_get_name(world, e)); - continue; - } - - if (!elem_count) { - ecs_err("array '%s' has size 0", ecs_get_name(world, e)); - continue; - } - - const EcsComponent *elem_ptr = ecs_get(world, elem_type, EcsComponent); - if (flecs_init_type(world, e, EcsArrayType, - elem_ptr->size * elem_count, elem_ptr->alignment)) - { - continue; - } + flecs_copy_world_summary(it->world, &summary[i]); } } static -void flecs_set_vector(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsVector *array = ecs_field(it, EcsVector, 1); +void OnSetWorldSummary(ecs_iter_t *it) { + EcsWorldSummary *summary = ecs_field(it, EcsWorldSummary, 0); - int i, count = it->count; + int32_t i, count = it->count; for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_entity_t elem_type = array[i].type; + ecs_set_target_fps(it->world, (ecs_ftime_t)summary[i].target_fps); + ecs_set_time_scale(it->world, (ecs_ftime_t)summary[i].time_scale); + } +} - if (!elem_type) { - ecs_err("vector '%s' has no element type", ecs_get_name(world, e)); - continue; - } +void FlecsWorldSummaryImport( + ecs_world_t *world) +{ + ECS_COMPONENT_DEFINE(world, EcsWorldSummary); - if (init_type_t(world, e, EcsVectorType, ecs_vec_t)) { - continue; +#if defined(FLECS_META) && defined(FLECS_UNITS) + ecs_entity_t build_info = ecs_lookup(world, "flecs.core.build_info_t"); + ecs_struct(world, { + .entity = ecs_id(EcsWorldSummary), + .members = { + { .name = "target_fps", .type = ecs_id(ecs_f64_t), .unit = EcsHertz }, + { .name = "time_scale", .type = ecs_id(ecs_f64_t) }, + { .name = "frame_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "system_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "merge_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "frame_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "system_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "merge_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "frame_count", .type = ecs_id(ecs_u64_t) }, + { .name = "command_count", .type = ecs_id(ecs_u64_t) }, + { .name = "build_info", .type = build_info } } - } + }); +#endif + const ecs_world_info_t *info = ecs_get_world_info(world); + + ecs_system(world, { + .entity = ecs_entity(world, { + .name = "UpdateWorldSummary", + .add = ecs_ids(ecs_pair(EcsDependsOn, EcsPreFrame)) + }), + .query.terms = {{ .id = ecs_id(EcsWorldSummary) }}, + .callback = UpdateWorldSummary + }); + + ecs_observer(world, { + .entity = ecs_entity(world, { + .name = "OnSetWorldSummary" + }), + .events = { EcsOnSet }, + .query.terms = {{ .id = ecs_id(EcsWorldSummary) }}, + .callback = OnSetWorldSummary + }); + + ecs_set(world, EcsWorld, EcsWorldSummary, { + .target_fps = (double)info->target_fps, + .time_scale = (double)info->time_scale + }); + + EcsWorldSummary *summary = ecs_ensure(world, EcsWorld, EcsWorldSummary); + flecs_copy_world_summary(world, summary); + ecs_modified(world, EcsWorld, EcsWorldSummary); } -static -void flecs_set_custom_type(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsOpaque *serialize = ecs_field(it, EcsOpaque, 1); +#endif - 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; +/** + * @file addons/system/system.c + * @brief System addon. + */ - 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; - } +#ifdef FLECS_SYSTEM - if (flecs_init_type(world, e, EcsOpaqueType, comp->size, comp->alignment)) { - continue; - } + +ecs_mixins_t ecs_system_t_mixins = { + .type_name = "ecs_system_t", + .elems = { + [EcsMixinWorld] = offsetof(ecs_system_t, world), + [EcsMixinEntity] = offsetof(ecs_system_t, entity), + [EcsMixinDtor] = offsetof(ecs_system_t, dtor) } -} +}; -bool flecs_unit_validate( +/* -- Public API -- */ + +ecs_entity_t flecs_run_intern( ecs_world_t *world, - ecs_entity_t t, - EcsUnit *data) + ecs_stage_t *stage, + ecs_entity_t system, + ecs_system_t *system_data, + int32_t stage_index, + int32_t stage_count, + ecs_ftime_t delta_time, + void *param) { - char *derived_symbol = NULL; - const char *symbol = data->symbol; - - ecs_entity_t base = data->base; - ecs_entity_t over = data->over; - ecs_entity_t prefix = data->prefix; - ecs_unit_translation_t translation = data->translation; + ecs_ftime_t time_elapsed = delta_time; + ecs_entity_t tick_source = system_data->tick_source; - if (base) { - if (!ecs_has(world, base, EcsUnit)) { - ecs_err("entity '%s' for unit '%s' used as base is not a unit", - ecs_get_name(world, base), ecs_get_name(world, t)); - goto error; - } + /* Support legacy behavior */ + if (!param) { + param = system_data->ctx; } - if (over) { - if (!base) { - ecs_err("invalid unit '%s': cannot specify over without base", - ecs_get_name(world, t)); - goto error; - } - if (!ecs_has(world, over, EcsUnit)) { - ecs_err("entity '%s' for unit '%s' used as over is not a unit", - ecs_get_name(world, over), ecs_get_name(world, t)); - goto error; - } - } + if (tick_source) { + const EcsTickSource *tick = ecs_get(world, tick_source, EcsTickSource); - if (prefix) { - if (!base) { - ecs_err("invalid unit '%s': cannot specify prefix without base", - ecs_get_name(world, t)); - goto error; - } - const EcsUnitPrefix *prefix_ptr = ecs_get(world, prefix, EcsUnitPrefix); - if (!prefix_ptr) { - ecs_err("entity '%s' for unit '%s' used as prefix is not a prefix", - ecs_get_name(world, over), ecs_get_name(world, t)); - goto error; - } + if (tick) { + time_elapsed = tick->time_elapsed; - if (translation.factor || translation.power) { - if (prefix_ptr->translation.factor != translation.factor || - prefix_ptr->translation.power != translation.power) - { - ecs_err( - "factor for unit '%s' is inconsistent with prefix '%s'", - ecs_get_name(world, t), ecs_get_name(world, prefix)); - goto error; + /* If timer hasn't fired we shouldn't run the system */ + if (!tick->tick) { + return 0; } } else { - translation = prefix_ptr->translation; + /* If a timer has been set but the timer entity does not have the + * EcsTimer component, don't run the system. This can be the result + * of a single-shot timer that has fired already. Not resetting the + * timer field of the system will ensure that the system won't be + * ran after the timer has fired. */ + return 0; } } - if (base) { - bool must_match = false; /* Must base symbol match symbol? */ - ecs_strbuf_t sbuf = ECS_STRBUF_INIT; - if (prefix) { - const EcsUnitPrefix *ptr = ecs_get(world, prefix, EcsUnitPrefix); - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); - if (ptr->symbol) { - ecs_strbuf_appendstr(&sbuf, ptr->symbol); - must_match = true; - } - } - - const EcsUnit *uptr = ecs_get(world, base, EcsUnit); - ecs_assert(uptr != NULL, ECS_INTERNAL_ERROR, NULL); - if (uptr->symbol) { - ecs_strbuf_appendstr(&sbuf, uptr->symbol); - } - - if (over) { - uptr = ecs_get(world, over, EcsUnit); - ecs_assert(uptr != NULL, ECS_INTERNAL_ERROR, NULL); - if (uptr->symbol) { - ecs_strbuf_appendch(&sbuf, '/'); - ecs_strbuf_appendstr(&sbuf, uptr->symbol); - must_match = true; - } - } + ecs_os_perf_trace_push(system_data->name); - derived_symbol = ecs_strbuf_get(&sbuf); - if (derived_symbol && !ecs_os_strlen(derived_symbol)) { - ecs_os_free(derived_symbol); - derived_symbol = NULL; - } + if (ecs_should_log_3()) { + char *path = ecs_get_path(world, system); + ecs_dbg_3("worker %d: %s", stage_index, path); + ecs_os_free(path); + } - if (derived_symbol && symbol && ecs_os_strcmp(symbol, derived_symbol)) { - if (must_match) { - ecs_err("symbol '%s' for unit '%s' does not match base" - " symbol '%s'", symbol, - ecs_get_name(world, t), derived_symbol); - goto error; - } - } - if (!symbol && derived_symbol && (prefix || over)) { - ecs_os_free(data->symbol); - data->symbol = derived_symbol; - } else { - ecs_os_free(derived_symbol); - } + ecs_time_t time_start; + bool measure_time = ECS_BIT_IS_SET(world->flags, EcsWorldMeasureSystemTime); + if (measure_time) { + ecs_os_get_time(&time_start); } - data->base = base; - data->over = over; - data->prefix = prefix; - data->translation = translation; + ecs_world_t *thread_ctx = world; + if (stage) { + thread_ctx = stage->thread_ctx; + } else { + stage = world->stages[0]; + } - return true; -error: - ecs_os_free(derived_symbol); - return false; -} + /* Prepare the query iterator */ + ecs_iter_t wit, qit = ecs_query_iter(thread_ctx, system_data->query); + ecs_iter_t *it = &qit; -static -void flecs_set_unit(ecs_iter_t *it) { - EcsUnit *u = ecs_field(it, EcsUnit, 1); + qit.system = system; + qit.delta_time = delta_time; + qit.delta_system_time = time_elapsed; + qit.param = param; + qit.ctx = system_data->ctx; + qit.callback_ctx = system_data->callback_ctx; + qit.run_ctx = system_data->run_ctx; - ecs_world_t *world = it->world; + flecs_defer_begin(world, stage); - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - flecs_unit_validate(world, e, &u[i]); + if (stage_count > 1 && system_data->multi_threaded) { + wit = ecs_worker_iter(it, stage_index, stage_count); + it = &wit; } -} -static -void flecs_unit_quantity_monitor(ecs_iter_t *it) { - ecs_world_t *world = it->world; + ecs_entity_t old_system = flecs_stage_set_system(stage, system); + ecs_iter_action_t action = system_data->action; + it->callback = action; - int i, count = it->count; - if (it->event == EcsOnAdd) { - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_add_pair(world, e, EcsQuantity, e); + ecs_run_action_t run = system_data->run; + if (run) { + /* If system query matches nothing, the system run callback doesn't have + * anything to iterate, so the iterator resources don't get cleaned up + * automatically, so clean it up here. */ + if (system_data->query->flags & EcsQueryMatchNothing) { + it->next = flecs_default_next_callback; /* Return once */ + run(it); + ecs_iter_fini(&qit); + } else { + run(it); } } else { - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_remove_pair(world, e, EcsQuantity, e); - } - } -} - -static -void ecs_meta_type_init_default_ctor(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsMetaType *type = ecs_field(it, EcsMetaType, 1); - - int i; - for (i = 0; i < it->count; i ++) { - /* If a component is defined from reflection data, configure it with the - * default constructor. This ensures that a new component value does not - * contain uninitialized memory, which could cause serializers to crash - * when for example inspecting string fields. */ - if (!type->existing) { - ecs_entity_t e = it->entities[i]; - const ecs_type_info_t *ti = ecs_get_type_info(world, e); - if (!ti || !ti->hooks.ctor) { - ecs_set_hooks_id(world, e, - &(ecs_type_hooks_t){ - .ctor = ecs_default_ctor - }); + if (system_data->query->term_count) { + if (it == &qit) { + while (ecs_query_next(&qit)) { + action(&qit); + } + } else { + while (ecs_iter_next(it)) { + action(it); + } } + } else { + action(&qit); + ecs_iter_fini(&qit); } } -} - -static -void flecs_member_on_set(ecs_iter_t *it) { - EcsMember *mbr = it->ptrs[0]; - if (!mbr->count) { - mbr->count = 1; - } -} - -void FlecsMetaImport( - ecs_world_t *world) -{ - ECS_MODULE(world, FlecsMeta); - - ecs_set_name_prefix(world, "Ecs"); - flecs_bootstrap_component(world, EcsMetaType); - flecs_bootstrap_component(world, EcsMetaTypeSerialized); - flecs_bootstrap_component(world, EcsPrimitive); - flecs_bootstrap_component(world, EcsEnum); - flecs_bootstrap_component(world, EcsBitmask); - flecs_bootstrap_component(world, EcsMember); - flecs_bootstrap_component(world, EcsMemberRanges); - 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); - - flecs_bootstrap_tag(world, EcsConstant); - flecs_bootstrap_tag(world, EcsQuantity); - - ecs_set_hooks(world, EcsMetaType, { .ctor = ecs_default_ctor }); - - ecs_set_hooks(world, EcsMetaTypeSerialized, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsMetaTypeSerialized), - .copy = ecs_copy(EcsMetaTypeSerialized), - .dtor = ecs_dtor(EcsMetaTypeSerialized) - }); + flecs_stage_set_system(stage, old_system); - ecs_set_hooks(world, EcsStruct, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsStruct), - .copy = ecs_copy(EcsStruct), - .dtor = ecs_dtor(EcsStruct) - }); + if (measure_time) { + system_data->time_spent += (ecs_ftime_t)ecs_time_measure(&time_start); + } - ecs_set_hooks(world, EcsMember, { - .ctor = ecs_default_ctor, - .on_set = flecs_member_on_set - }); + flecs_defer_end(world, stage); - ecs_set_hooks(world, EcsMemberRanges, { - .ctor = ecs_default_ctor - }); + ecs_os_perf_trace_pop(system_data->name); - ecs_set_hooks(world, EcsEnum, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsEnum), - .copy = ecs_copy(EcsEnum), - .dtor = ecs_dtor(EcsEnum) - }); + return it->interrupted_by; +} - ecs_set_hooks(world, EcsBitmask, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsBitmask), - .copy = ecs_copy(EcsBitmask), - .dtor = ecs_dtor(EcsBitmask) - }); +/* -- Public API -- */ - ecs_set_hooks(world, EcsUnit, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsUnit), - .copy = ecs_copy(EcsUnit), - .dtor = ecs_dtor(EcsUnit) - }); +ecs_entity_t ecs_run_worker( + ecs_world_t *world, + ecs_entity_t system, + int32_t stage_index, + int32_t stage_count, + ecs_ftime_t delta_time, + void *param) +{ + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_system_t *system_data = flecs_poly_get(world, system, ecs_system_t); + ecs_assert(system_data != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_set_hooks(world, EcsUnitPrefix, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsUnitPrefix), - .copy = ecs_copy(EcsUnitPrefix), - .dtor = ecs_dtor(EcsUnitPrefix) - }); + return flecs_run_intern( + world, stage, system, system_data, stage_index, stage_count, + delta_time, param); +} - /* Register triggers to finalize type information from component data */ - ecs_entity_t old_scope = ecs_set_scope( /* Keep meta scope clean */ - world, EcsFlecsInternals); - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsPrimitive), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = flecs_set_primitive - }); +ecs_entity_t ecs_run( + ecs_world_t *world, + ecs_entity_t system, + ecs_ftime_t delta_time, + void *param) +{ + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_system_t *system_data = flecs_poly_get(world, system, ecs_system_t); + ecs_assert(system_data != NULL, ECS_INVALID_PARAMETER, NULL); + return flecs_run_intern( + world, stage, system, system_data, 0, 0, delta_time, param); +} - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsMember), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = flecs_set_member - }); +/* System deinitialization */ +static +void flecs_system_fini(ecs_system_t *sys) { + if (sys->ctx_free) { + sys->ctx_free(sys->ctx); + } - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsMemberRanges), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = flecs_set_member_ranges - }); + if (sys->callback_ctx_free) { + sys->callback_ctx_free(sys->callback_ctx); + } - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsEnum), .src.flags = EcsSelf }, - .events = {EcsOnAdd}, - .callback = flecs_add_enum - }); + if (sys->run_ctx_free) { + sys->run_ctx_free(sys->run_ctx); + } - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsBitmask), .src.flags = EcsSelf }, - .events = {EcsOnAdd}, - .callback = flecs_add_bitmask - }); + /* Safe cast, type owns name */ + ecs_os_free(ECS_CONST_CAST(char*, sys->name)); - ecs_observer(world, { - .filter.terms[0] = { .id = EcsConstant, .src.flags = EcsSelf }, - .events = {EcsOnAdd}, - .callback = flecs_add_constant - }); + flecs_poly_free(sys, ecs_system_t); +} - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_pair(EcsConstant, EcsWildcard), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = flecs_add_constant - }); +/* ecs_poly_dtor_t-compatible wrapper */ +static +void flecs_system_poly_fini(void *sys) +{ + flecs_system_fini(sys); +} - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsArray), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = flecs_set_array - }); +static +int flecs_system_init_timer( + ecs_world_t *world, + ecs_entity_t entity, + const ecs_system_desc_t *desc) +{ + if (ECS_NEQZERO(desc->interval) && ECS_NEQZERO(desc->rate)) { + char *name = ecs_get_path(world, entity); + ecs_err("system %s cannot have both interval and rate set", name); + ecs_os_free(name); + return -1; + } - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsVector), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = flecs_set_vector - }); + if (ECS_NEQZERO(desc->interval) || ECS_NEQZERO(desc->rate) || + ECS_NEQZERO(desc->tick_source)) + { +#ifdef FLECS_TIMER + if (ECS_NEQZERO(desc->interval)) { + ecs_set_interval(world, entity, desc->interval); + } - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsOpaque), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = flecs_set_custom_type - }); + if (desc->rate) { + ecs_entity_t tick_source = desc->tick_source; + ecs_set_rate(world, entity, desc->rate, tick_source); + } else if (desc->tick_source) { + ecs_set_tick_source(world, entity, desc->tick_source); + } +#else + (void)world; + (void)entity; + ecs_abort(ECS_UNSUPPORTED, "timer module not available"); +#endif + } - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsUnit), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = flecs_set_unit - }); + return 0; +} - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsMetaType), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = ecs_meta_type_serialized_init - }); +ecs_entity_t ecs_system_init( + ecs_world_t *world, + const ecs_system_desc_t *desc) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_system_desc_t was not initialized to zero"); + ecs_assert(!(world->flags & EcsWorldReadonly), + ECS_INVALID_WHILE_READONLY, NULL); - ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsMetaType), .src.flags = EcsSelf }, - .events = {EcsOnSet}, - .callback = ecs_meta_type_init_default_ctor - }); + ecs_entity_t entity = desc->entity; + if (!entity) { + entity = ecs_entity(world, {0}); + } - ecs_observer(world, { - .filter.terms = { - { .id = ecs_id(EcsUnit) }, - { .id = EcsQuantity } - }, - .events = { EcsMonitor }, - .callback = flecs_unit_quantity_monitor - }); - ecs_set_scope(world, old_scope); + EcsPoly *poly = flecs_poly_bind(world, entity, ecs_system_t); + if (!poly->poly) { + ecs_system_t *system = flecs_poly_new(ecs_system_t); + ecs_assert(system != NULL, ECS_INTERNAL_ERROR, NULL); + + poly->poly = system; + system->world = world; + system->dtor = flecs_system_poly_fini; + system->entity = entity; - /* Initialize primitive types */ - #define ECS_PRIMITIVE(world, type, primitive_kind)\ - ecs_entity_init(world, &(ecs_entity_desc_t){\ - .id = ecs_id(ecs_##type##_t),\ - .name = #type,\ - .symbol = #type });\ - ecs_set(world, ecs_id(ecs_##type##_t), EcsPrimitive, {\ - .kind = primitive_kind\ - }); + ecs_query_desc_t query_desc = desc->query; + query_desc.entity = entity; - ECS_PRIMITIVE(world, bool, EcsBool); - ECS_PRIMITIVE(world, char, EcsChar); - ECS_PRIMITIVE(world, byte, EcsByte); - ECS_PRIMITIVE(world, u8, EcsU8); - ECS_PRIMITIVE(world, u16, EcsU16); - ECS_PRIMITIVE(world, u32, EcsU32); - ECS_PRIMITIVE(world, u64, EcsU64); - ECS_PRIMITIVE(world, uptr, EcsUPtr); - ECS_PRIMITIVE(world, i8, EcsI8); - ECS_PRIMITIVE(world, i16, EcsI16); - ECS_PRIMITIVE(world, i32, EcsI32); - ECS_PRIMITIVE(world, i64, EcsI64); - ECS_PRIMITIVE(world, iptr, EcsIPtr); - ECS_PRIMITIVE(world, f32, EcsF32); - ECS_PRIMITIVE(world, f64, EcsF64); - ECS_PRIMITIVE(world, string, EcsString); - ECS_PRIMITIVE(world, entity, EcsEntity); - ECS_PRIMITIVE(world, id, EcsId); + ecs_query_t *query = ecs_query_init(world, &query_desc); + if (!query) { + ecs_delete(world, entity); + return 0; + } - #undef ECS_PRIMITIVE + /* Prevent the system from moving while we're initializing */ + flecs_defer_begin(world, world->stages[0]); - ecs_set_hooks(world, ecs_string_t, { - .ctor = ecs_default_ctor, - .copy = ecs_copy(ecs_string_t), - .move = ecs_move(ecs_string_t), - .dtor = ecs_dtor(ecs_string_t) - }); + system->query = query; + system->query_entity = query->entity; - /* Set default child components */ - ecs_add_pair(world, ecs_id(EcsStruct), - EcsDefaultChildComponent, ecs_id(EcsMember)); + system->run = desc->run; + system->action = desc->callback; - ecs_add_pair(world, ecs_id(EcsMember), - EcsDefaultChildComponent, ecs_id(EcsMember)); + system->ctx = desc->ctx; + system->callback_ctx = desc->callback_ctx; + system->run_ctx = desc->run_ctx; - ecs_add_pair(world, ecs_id(EcsEnum), - EcsDefaultChildComponent, EcsConstant); + system->ctx_free = desc->ctx_free; + system->callback_ctx_free = desc->callback_ctx_free; + system->run_ctx_free = desc->run_ctx_free; - ecs_add_pair(world, ecs_id(EcsBitmask), - EcsDefaultChildComponent, EcsConstant); + system->tick_source = desc->tick_source; - /* Relationship properties */ - ecs_add_id(world, EcsQuantity, EcsExclusive); - ecs_add_id(world, EcsQuantity, EcsTag); + system->multi_threaded = desc->multi_threaded; + system->immediate = desc->immediate; - /* Initialize reflection data for meta components */ - ecs_entity_t type_kind = ecs_enum_init(world, &(ecs_enum_desc_t){ - .entity = ecs_entity(world, { .name = "TypeKind" }), - .constants = { - { .name = "PrimitiveType" }, - { .name = "BitmaskType" }, - { .name = "EnumType" }, - { .name = "StructType" }, - { .name = "ArrayType" }, - { .name = "VectorType" }, - { .name = "OpaqueType" } - } - }); + system->name = ecs_get_path(world, entity); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsMetaType), - .members = { - { .name = "kind", .type = type_kind } + if (flecs_system_init_timer(world, entity, desc)) { + ecs_delete(world, entity); + ecs_defer_end(world); + goto error; } - }); - ecs_entity_t primitive_kind = ecs_enum_init(world, &(ecs_enum_desc_t){ - .entity = ecs_entity(world, { .name = "PrimitiveKind" }), - .constants = { - { .name = "Bool", 1 }, - { .name = "Char" }, - { .name = "Byte" }, - { .name = "U8" }, - { .name = "U16" }, - { .name = "U32" }, - { .name = "U64 "}, - { .name = "I8" }, - { .name = "I16" }, - { .name = "I32" }, - { .name = "I64" }, - { .name = "F32" }, - { .name = "F64" }, - { .name = "UPtr "}, - { .name = "IPtr" }, - { .name = "String" }, - { .name = "Entity" }, - { .name = "Id" } + if (ecs_get_name(world, entity)) { + ecs_trace("#[green]system#[reset] %s created", + ecs_get_name(world, entity)); } - }); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsPrimitive), - .members = { - { .name = "kind", .type = primitive_kind } + ecs_defer_end(world); + } else { + flecs_poly_assert(poly->poly, ecs_system_t); + ecs_system_t *system = (ecs_system_t*)poly->poly; + + if (system->ctx_free) { + if (system->ctx && system->ctx != desc->ctx) { + system->ctx_free(system->ctx); + } } - }); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsMember), - .members = { - { .name = "type", .type = ecs_id(ecs_entity_t) }, - { .name = "count", .type = ecs_id(ecs_i32_t) }, - { .name = "unit", .type = ecs_id(ecs_entity_t) }, - { .name = "offset", .type = ecs_id(ecs_i32_t) } + if (system->callback_ctx_free) { + if (system->callback_ctx && system->callback_ctx != desc->callback_ctx) { + system->callback_ctx_free(system->callback_ctx); + system->callback_ctx_free = NULL; + system->callback_ctx = NULL; + } } - }); - ecs_entity_t vr = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, { .name = "value_range" }), - .members = { - { .name = "min", .type = ecs_id(ecs_f64_t) }, - { .name = "max", .type = ecs_id(ecs_f64_t) } + if (system->run_ctx_free) { + if (system->run_ctx && system->run_ctx != desc->run_ctx) { + system->run_ctx_free(system->run_ctx); + system->run_ctx_free = NULL; + system->run_ctx = NULL; + } } - }); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsMemberRanges), - .members = { - { .name = "value", .type = vr }, - { .name = "warning", .type = vr }, - { .name = "error", .type = vr } + if (desc->run) { + system->run = desc->run; + if (!desc->callback) { + system->action = NULL; + } } - }); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsArray), - .members = { - { .name = "type", .type = ecs_id(ecs_entity_t) }, - { .name = "count", .type = ecs_id(ecs_i32_t) }, + if (desc->callback) { + system->action = desc->callback; + if (!desc->run) { + system->run = NULL; + } } - }); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsVector), - .members = { - { .name = "type", .type = ecs_id(ecs_entity_t) } + if (desc->ctx) { + system->ctx = desc->ctx; } - }); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsOpaque), - .members = { - { .name = "as_type", .type = ecs_id(ecs_entity_t) } + if (desc->callback_ctx) { + system->callback_ctx = desc->callback_ctx; } - }); - ecs_entity_t ut = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, { .name = "unit_translation" }), - .members = { - { .name = "factor", .type = ecs_id(ecs_i32_t) }, - { .name = "power", .type = ecs_id(ecs_i32_t) } + if (desc->run_ctx) { + system->run_ctx = desc->run_ctx; } - }); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsUnit), - .members = { - { .name = "symbol", .type = ecs_id(ecs_string_t) }, - { .name = "prefix", .type = ecs_id(ecs_entity_t) }, - { .name = "base", .type = ecs_id(ecs_entity_t) }, - { .name = "over", .type = ecs_id(ecs_entity_t) }, - { .name = "translation", .type = ut } + if (desc->ctx_free) { + system->ctx_free = desc->ctx_free; } - }); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsUnitPrefix), - .members = { - { .name = "symbol", .type = ecs_id(ecs_string_t) }, - { .name = "translation", .type = ut } + if (desc->callback_ctx_free) { + system->callback_ctx_free = desc->callback_ctx_free; } - }); -} -#endif + if (desc->run_ctx_free) { + system->run_ctx_free = desc->run_ctx_free; + } -/** - * @file meta/serialized.c - * @brief Serialize type into flat operations array to speed up deserialization. - */ + if (desc->multi_threaded) { + system->multi_threaded = desc->multi_threaded; + } + if (desc->immediate) { + system->immediate = desc->immediate; + } -#ifdef FLECS_META + if (flecs_system_init_timer(world, entity, desc)) { + return 0; + } + } -static -int flecs_meta_serialize_type( - ecs_world_t *world, - ecs_entity_t type, - ecs_size_t offset, - ecs_vec_t *ops); + flecs_poly_modified(world, entity, ecs_system_t); -ecs_meta_type_op_kind_t flecs_meta_primitive_to_op_kind(ecs_primitive_kind_t kind) { - return EcsOpPrimitive + kind; + return entity; +error: + return 0; } -static -ecs_size_t flecs_meta_type_size(ecs_world_t *world, ecs_entity_t type) { - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); - return comp->size; +const ecs_system_t* ecs_system_get( + const ecs_world_t *world, + ecs_entity_t entity) +{ + return flecs_poly_get(world, entity, ecs_system_t); } -static -ecs_meta_type_op_t* flecs_meta_ops_add(ecs_vec_t *ops, ecs_meta_type_op_kind_t kind) { - ecs_meta_type_op_t *op = ecs_vec_append_t(NULL, ops, ecs_meta_type_op_t); - op->kind = kind; - op->offset = 0; - op->count = 1; - op->op_count = 1; - op->size = 0; - op->name = NULL; - op->members = NULL; - op->type = 0; - op->member_index = 0; - return op; -} +void FlecsSystemImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsSystem); +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsSystem), + "Module that implements Flecs systems"); +#endif -static -ecs_meta_type_op_t* flecs_meta_ops_get(ecs_vec_t *ops, int32_t index) { - ecs_meta_type_op_t* op = ecs_vec_get_t(ops, ecs_meta_type_op_t, index); - ecs_assert(op != NULL, ECS_INTERNAL_ERROR, NULL); - return op; -} + ecs_set_name_prefix(world, "Ecs"); -static -int flecs_meta_serialize_primitive( - ecs_world_t *world, - ecs_entity_t type, - ecs_size_t offset, - ecs_vec_t *ops) -{ - const EcsPrimitive *ptr = ecs_get(world, type, EcsPrimitive); - if (!ptr) { - char *name = ecs_get_fullpath(world, type); - ecs_err("entity '%s' is not a primitive type", name); - ecs_os_free(name); - return -1; - } + flecs_bootstrap_tag(world, EcsSystem); + flecs_bootstrap_component(world, EcsTickSource); - ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, flecs_meta_primitive_to_op_kind(ptr->kind)); - op->offset = offset; - op->type = type; - op->size = flecs_meta_type_size(world, type); - return 0; + /* Make sure to never inherit system component. This makes sure that any + * term created for the System component will default to 'self' traversal, + * which improves efficiency of the query. */ + ecs_add_pair(world, EcsSystem, EcsOnInstantiate, EcsDontInherit); } -static -int flecs_meta_serialize_enum( - ecs_world_t *world, - ecs_entity_t type, - ecs_size_t offset, - ecs_vec_t *ops) -{ - (void)world; - - ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpEnum); - op->offset = offset; - op->type = type; - op->size = ECS_SIZEOF(ecs_i32_t); - return 0; -} +#endif + +/** + * @file query/compiler/compile.c + * @brief Compile query program from query. + */ + static -int flecs_meta_serialize_bitmask( - ecs_world_t *world, - ecs_entity_t type, - ecs_size_t offset, - ecs_vec_t *ops) +bool flecs_query_var_is_anonymous( + const ecs_query_impl_t *query, + ecs_var_id_t var_id) { - (void)world; - - ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpBitmask); - op->offset = offset; - op->type = type; - op->size = ECS_SIZEOF(ecs_u32_t); - return 0; + ecs_query_var_t *var = &query->vars[var_id]; + return var->anonymous; } -static -int flecs_meta_serialize_array( - ecs_world_t *world, - ecs_entity_t type, - ecs_size_t offset, - ecs_vec_t *ops) +ecs_var_id_t flecs_query_add_var( + ecs_query_impl_t *query, + const char *name, + ecs_vec_t *vars, + ecs_var_kind_t kind) { - (void)world; + const char *dot = NULL; + if (name) { + dot = strchr(name, '.'); + if (dot) { + kind = EcsVarEntity; /* lookup variables are always entities */ + } + } - ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpArray); - op->offset = offset; - op->type = type; - op->size = flecs_meta_type_size(world, type); - return 0; -} + ecs_hashmap_t *var_index = NULL; + ecs_var_id_t var_id = EcsVarNone; + if (name) { + if (kind == EcsVarAny) { + var_id = flecs_query_find_var_id(query, name, EcsVarEntity); + if (var_id != EcsVarNone) { + return var_id; + } -static -int flecs_meta_serialize_array_component( - ecs_world_t *world, - ecs_entity_t type, - ecs_vec_t *ops) -{ - const EcsArray *ptr = ecs_get(world, type, EcsArray); - if (!ptr) { - return -1; /* Should never happen, will trigger internal error */ + var_id = flecs_query_find_var_id(query, name, EcsVarTable); + if (var_id != EcsVarNone) { + return var_id; + } + + kind = EcsVarTable; + } else { + var_id = flecs_query_find_var_id(query, name, kind); + if (var_id != EcsVarNone) { + return var_id; + } + } + + if (kind == EcsVarTable) { + var_index = &query->tvar_index; + } else { + var_index = &query->evar_index; + } + + /* If we're creating an entity var, check if it has a table variant */ + if (kind == EcsVarEntity && var_id == EcsVarNone) { + var_id = flecs_query_find_var_id(query, name, EcsVarTable); + } } - flecs_meta_serialize_type(world, ptr->type, 0, ops); + ecs_query_var_t *var; + ecs_var_id_t result; + if (vars) { + var = ecs_vec_append_t(NULL, vars, ecs_query_var_t); + result = var->id = flecs_itovar(ecs_vec_count(vars)); + } else { + ecs_dbg_assert(query->var_count < query->var_size, + ECS_INTERNAL_ERROR, NULL); + var = &query->vars[query->var_count]; + result = var->id = flecs_itovar(query->var_count); + query->var_count ++; + } - ecs_meta_type_op_t *first = ecs_vec_first(ops); - first->count = ptr->count; - return 0; -} + var->kind = flecs_ito(int8_t, kind); + var->name = name; + var->table_id = var_id; + var->base_id = 0; + var->lookup = NULL; + flecs_set_var_label(var, NULL); -static -int flecs_meta_serialize_vector( - ecs_world_t *world, - ecs_entity_t type, - ecs_size_t offset, - ecs_vec_t *ops) -{ - (void)world; - ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpVector); - op->offset = offset; - op->type = type; - op->size = flecs_meta_type_size(world, type); - return 0; + if (name) { + flecs_name_index_init_if(var_index, NULL); + flecs_name_index_ensure(var_index, var->id, name, 0, 0); + var->anonymous = name[0] == '_'; + + /* Handle variables that require a by-name lookup, e.g. $this.wheel */ + if (dot != NULL) { + ecs_assert(var->table_id == EcsVarNone, ECS_INTERNAL_ERROR, NULL); + var->lookup = dot + 1; + } + } + + return result; } static -int flecs_meta_serialize_custom_type( - ecs_world_t *world, - ecs_entity_t type, - ecs_size_t offset, - ecs_vec_t *ops) +ecs_var_id_t flecs_query_add_var_for_term_id( + ecs_query_impl_t *query, + ecs_term_ref_t *term_id, + ecs_vec_t *vars, + ecs_var_kind_t kind) { - (void)world; - ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpOpaque); - op->offset = offset; - op->type = type; - op->size = flecs_meta_type_size(world, type); - return 0; + const char *name = flecs_term_ref_var_name(term_id); + if (!name) { + return EcsVarNone; + } + + return flecs_query_add_var(query, name, vars, kind); } +/* This function walks over terms to discover which variables are used in the + * query. It needs to provide the following functionality: + * - create table vars for all variables used as source + * - create entity vars for all variables not used as source + * - create entity vars for all non-$this vars + * - create anonymous vars to store the content of wildcards + * - create anonymous vars to store result of lookups (for $var.child_name) + * - create anonymous vars for resolving component inheritance + * - create array that stores the source variable for each field + * - ensure table vars for non-$this variables are anonymous + * - ensure variables created inside scopes are anonymous + * - place anonymous variables after public variables in vars array + */ static -int flecs_meta_serialize_struct( - ecs_world_t *world, - ecs_entity_t type, - ecs_size_t offset, - ecs_vec_t *ops) +int flecs_query_discover_vars( + ecs_stage_t *stage, + ecs_query_impl_t *query) { - const EcsStruct *ptr = ecs_get(world, type, EcsStruct); - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_vec_t *vars = &stage->variables; /* Buffer to reduce allocs */ + ecs_vec_reset_t(NULL, vars, ecs_query_var_t); + + ecs_term_t *terms = query->pub.terms; + int32_t a, i, anonymous_count = 0, count = query->pub.term_count; + int32_t anonymous_table_count = 0, scope = 0, scoped_var_index = 0; + bool table_this = false, entity_before_table_this = false; + + /* For This table lookups during discovery. This will be overwritten after + * discovery with whether the query actually has a This table variable. */ + query->pub.flags |= EcsQueryHasTableThisVar; + + for (i = 0; i < count; i ++) { + ecs_term_t *term = &terms[i]; + ecs_term_ref_t *first = &term->first; + ecs_term_ref_t *second = &term->second; + ecs_term_ref_t *src = &term->src; + + if (ECS_TERM_REF_ID(first) == EcsScopeOpen) { + /* Keep track of which variables are first used in scope, so that we + * can mark them as anonymous. Terms inside a scope are collapsed + * into a single result, which means that outside of the scope the + * value of those variables is undefined. */ + if (!scope) { + scoped_var_index = ecs_vec_count(vars); + } + scope ++; + continue; + } else if (ECS_TERM_REF_ID(first) == EcsScopeClose) { + if (!--scope) { + /* Any new variables declared after entering a scope should be + * marked as anonymous. */ + int32_t v; + for (v = scoped_var_index; v < ecs_vec_count(vars); v ++) { + ecs_vec_get_t(vars, ecs_query_var_t, v)->anonymous = true; + } + } + continue; + } + + ecs_var_id_t first_var_id = flecs_query_add_var_for_term_id( + query, first, vars, EcsVarEntity); + if (first_var_id == EcsVarNone) { + /* If first is not a variable, check if we need to insert anonymous + * variable for resolving component inheritance */ + if (term->flags_ & EcsTermIdInherited) { + anonymous_count += 2; /* table & entity variable */ + } + + /* If first is a wildcard, insert anonymous variable */ + if (flecs_term_ref_is_wildcard(first)) { + anonymous_count ++; + } + } + + if ((src->id & EcsIsVariable) && (ECS_TERM_REF_ID(src) != EcsThis)) { + const char *var_name = flecs_term_ref_var_name(src); + if (var_name) { + ecs_var_id_t var_id = flecs_query_find_var_id( + query, var_name, EcsVarEntity); + if (var_id == EcsVarNone || var_id == first_var_id) { + var_id = flecs_query_add_var( + query, var_name, vars, EcsVarEntity); + } + + if (var_id != EcsVarNone) { + /* Mark variable as one for which we need to create a table + * variable. Don't create table variable now, so that we can + * store it in the non-public part of the variable array. */ + ecs_query_var_t *var = ecs_vec_get_t( + vars, ecs_query_var_t, (int32_t)var_id - 1); + ecs_assert(var != NULL, ECS_INTERNAL_ERROR, NULL); + if (!var->lookup) { + var->kind = EcsVarAny; + anonymous_table_count ++; + } + + if (((1llu << term->field_index) & query->pub.data_fields)) { + /* Can't have an anonymous variable as source of a term + * that returns a component. We need to return each + * instance of the component, whereas anonymous + * variables are not guaranteed to be resolved to + * individual entities. */ + if (var->anonymous) { + ecs_err( + "can't use anonymous variable '%s' as source of " + "data term", var->name); + goto error; + } + } + + /* Track which variable ids are used as field source */ + if (!query->src_vars) { + query->src_vars = flecs_calloc_n(&stage->allocator, + ecs_var_id_t, query->pub.field_count); + } + + query->src_vars[term->field_index] = var_id; + } + } else { + if (flecs_term_ref_is_wildcard(src)) { + anonymous_count ++; + } + } + } else if ((src->id & EcsIsVariable) && (ECS_TERM_REF_ID(src) == EcsThis)) { + if (flecs_term_is_builtin_pred(term) && term->oper == EcsOr) { + flecs_query_add_var(query, EcsThisName, vars, EcsVarEntity); + } + } + + if (flecs_query_add_var_for_term_id( + query, second, vars, EcsVarEntity) == EcsVarNone) + { + /* If second is a wildcard, insert anonymous variable */ + if (flecs_term_ref_is_wildcard(second)) { + anonymous_count ++; + } + } + + if (src->id & EcsIsVariable && second->id & EcsIsVariable) { + if (term->flags_ & EcsTermTransitive) { + /* Anonymous variable to store temporary id for finding + * targets for transitive relationship, see compile_term. */ + anonymous_count ++; + } + } + + /* If member term, make sure source is available as entity */ + if (term->flags_ & EcsTermIsMember) { + flecs_query_add_var_for_term_id(query, src, vars, EcsVarEntity); + } + + /* Track if a This entity variable is used before a potential This table + * variable. If this happens, the query has no This table variable */ + if (ECS_TERM_REF_ID(src) == EcsThis) { + table_this = true; + } + + if (ECS_TERM_REF_ID(first) == EcsThis || ECS_TERM_REF_ID(second) == EcsThis) { + if (!table_this) { + entity_before_table_this = true; + } + } + } + + int32_t var_count = ecs_vec_count(vars); + ecs_var_id_t placeholder = EcsVarNone - 1; + bool replace_placeholders = false; + + /* Ensure lookup variables have table and/or entity variables */ + for (i = 0; i < var_count; i ++) { + ecs_query_var_t *var = ecs_vec_get_t(vars, ecs_query_var_t, i); + if (var->lookup) { + char *var_name = ecs_os_strdup(var->name); + var_name[var->lookup - var->name - 1] = '\0'; + + ecs_var_id_t base_table_id = flecs_query_find_var_id( + query, var_name, EcsVarTable); + if (base_table_id != EcsVarNone) { + var->table_id = base_table_id; + } else if (anonymous_table_count) { + /* Scan for implicit anonymous table variables that haven't been + * inserted yet (happens after this step). Doing this here vs. + * ensures that anonymous variables are appended at the end of + * the variable array, while also ensuring that variable ids are + * stable (no swapping of table var ids that are in use). */ + for (a = 0; a < var_count; a ++) { + ecs_query_var_t *avar = ecs_vec_get_t( + vars, ecs_query_var_t, a); + if (avar->kind == EcsVarAny) { + if (!ecs_os_strcmp(avar->name, var_name)) { + base_table_id = (ecs_var_id_t)(a + 1); + break; + } + } + } + if (base_table_id != EcsVarNone) { + /* Set marker so we can set the new table id afterwards */ + var->table_id = placeholder; + replace_placeholders = true; + } + } + + ecs_var_id_t base_entity_id = flecs_query_find_var_id( + query, var_name, EcsVarEntity); + if (base_entity_id == EcsVarNone) { + /* Get name from table var (must exist). We can't use allocated + * name since variables don't own names. */ + const char *base_name = NULL; + if (base_table_id != EcsVarNone && base_table_id) { + ecs_query_var_t *base_table_var = ecs_vec_get_t( + vars, ecs_query_var_t, (int32_t)base_table_id - 1); + base_name = base_table_var->name; + } else { + base_name = EcsThisName; + } + + base_entity_id = flecs_query_add_var( + query, base_name, vars, EcsVarEntity); + var = ecs_vec_get_t(vars, ecs_query_var_t, i); + } + + var->base_id = base_entity_id; + + ecs_os_free(var_name); + } + } + + var_count = ecs_vec_count(vars); + + /* Add non-This table variables */ + if (anonymous_table_count) { + anonymous_table_count = 0; + for (i = 0; i < var_count; i ++) { + ecs_query_var_t *var = ecs_vec_get_t(vars, ecs_query_var_t, i); + if (var->kind == EcsVarAny) { + var->kind = EcsVarEntity; + + ecs_var_id_t var_id = flecs_query_add_var( + query, var->name, vars, EcsVarTable); + ecs_vec_get_t(vars, ecs_query_var_t, i)->table_id = var_id; + anonymous_table_count ++; + } + } + + var_count = ecs_vec_count(vars); + } + + /* If any forward references to newly added anonymous tables exist, replace + * them with the actual table variable ids. */ + if (replace_placeholders) { + for (i = 0; i < var_count; i ++) { + ecs_query_var_t *var = ecs_vec_get_t(vars, ecs_query_var_t, i); + if (var->table_id == placeholder) { + char *var_name = ecs_os_strdup(var->name); + var_name[var->lookup - var->name - 1] = '\0'; - int32_t cur, first = ecs_vec_count(ops); - ecs_meta_type_op_t *op = flecs_meta_ops_add(ops, EcsOpPush); - op->offset = offset; - op->type = type; - op->size = flecs_meta_type_size(world, type); + var->table_id = flecs_query_find_var_id( + query, var_name, EcsVarTable); + ecs_assert(var->table_id != EcsVarNone, + ECS_INTERNAL_ERROR, NULL); - ecs_member_t *members = ecs_vec_first(&ptr->members); - int32_t i, count = ecs_vec_count(&ptr->members); + ecs_os_free(var_name); + } + } + } - ecs_hashmap_t *member_index = NULL; - if (count) { - op->members = member_index = flecs_name_index_new( - world, &world->allocator); + /* Always include spot for This variable, even if query doesn't use it */ + var_count ++; + + ecs_query_var_t *query_vars = &flecs_this_array; + if ((var_count + anonymous_count) > 1) { + query_vars = flecs_alloc(&stage->allocator, + (ECS_SIZEOF(ecs_query_var_t) + ECS_SIZEOF(char*)) * + (var_count + anonymous_count)); } - for (i = 0; i < count; i ++) { - ecs_member_t *member = &members[i]; + query->vars = query_vars; + query->var_count = var_count; + query->pub.var_count = flecs_ito(int8_t, var_count); + ECS_BIT_COND(query->pub.flags, EcsQueryHasTableThisVar, + !entity_before_table_this); + query->var_size = var_count + anonymous_count; + + char **var_names; + if (query_vars != &flecs_this_array) { + query_vars[0].kind = EcsVarTable; + query_vars[0].name = NULL; + flecs_set_var_label(&query_vars[0], NULL); + query_vars[0].id = 0; + query_vars[0].table_id = EcsVarNone; + query_vars[0].lookup = NULL; + + var_names = ECS_ELEM(query_vars, ECS_SIZEOF(ecs_query_var_t), + var_count + anonymous_count); + var_names[0] = ECS_CONST_CAST(char*, query_vars[0].name); + } else { + var_names = &flecs_this_name_array; + } - cur = ecs_vec_count(ops); - flecs_meta_serialize_type(world, - member->type, offset + member->offset, ops); + query->pub.vars = (char**)var_names; - op = flecs_meta_ops_get(ops, cur); - if (!op->type) { - op->type = member->type; - } + query_vars ++; + var_names ++; + var_count --; - if (op->count <= 1) { - op->count = member->count; + if (var_count) { + ecs_query_var_t *user_vars = ecs_vec_first_t(vars, ecs_query_var_t); + ecs_os_memcpy_n(query_vars, user_vars, ecs_query_var_t, var_count); + for (i = 0; i < var_count; i ++) { + ecs_assert(&var_names[i] != &(&flecs_this_name_array)[i], + ECS_INTERNAL_ERROR, NULL); + var_names[i] = ECS_CONST_CAST(char*, query_vars[i].name); } + } - const char *member_name = member->name; - op->name = member_name; - op->op_count = ecs_vec_count(ops) - cur; - op->member_index = i; + /* Hide anonymous table variables from application */ + query->pub.var_count = + flecs_ito(int8_t, query->pub.var_count - anonymous_table_count); - flecs_name_index_ensure( - member_index, flecs_ito(uint64_t, cur - first - 1), - member_name, 0, 0); + /* Sanity check to make sure that the public part of the variable array only + * contains entity variables. */ +#ifdef FLECS_DEBUG + for (i = 1 /* first element = $this */; i < query->pub.var_count; i ++) { + ecs_assert(query->vars[i].kind == EcsVarEntity, ECS_INTERNAL_ERROR, NULL); } +#endif - flecs_meta_ops_add(ops, EcsOpPop); - flecs_meta_ops_get(ops, first)->op_count = ecs_vec_count(ops) - first; return 0; +error: + return -1; } static -int flecs_meta_serialize_type( - ecs_world_t *world, - ecs_entity_t type, - ecs_size_t offset, - ecs_vec_t *ops) +bool flecs_query_var_is_unknown( + ecs_query_impl_t *query, + ecs_var_id_t var_id, + ecs_query_compile_ctx_t *ctx) { - const EcsMetaType *ptr = ecs_get(world, type, EcsMetaType); - if (!ptr) { - char *path = ecs_get_fullpath(world, type); - ecs_err("missing EcsMetaType for type %s'", path); - ecs_os_free(path); - return -1; - } - - switch(ptr->kind) { - case EcsPrimitiveType: return flecs_meta_serialize_primitive(world, type, offset, ops); - case EcsEnumType: return flecs_meta_serialize_enum(world, type, offset, ops); - case EcsBitmaskType: return flecs_meta_serialize_bitmask(world, type, offset, ops); - case EcsStructType: return flecs_meta_serialize_struct(world, type, offset, ops); - case EcsArrayType: return flecs_meta_serialize_array(world, type, offset, ops); - case EcsVectorType: return flecs_meta_serialize_vector(world, type, offset, ops); - case EcsOpaqueType: return flecs_meta_serialize_custom_type(world, type, offset, ops); + ecs_query_var_t *vars = query->vars; + if (ctx->written & (1ull << var_id)) { + return false; + } else { + ecs_var_id_t table_var = vars[var_id].table_id; + if (table_var != EcsVarNone) { + return flecs_query_var_is_unknown(query, table_var, ctx); + } } - - return 0; + return true; } +/* Returns whether term is unknown. A term is unknown when it has variable + * elements (first, second, src) that are all unknown. */ static -int flecs_meta_serialize_component( - ecs_world_t *world, - ecs_entity_t type, - ecs_vec_t *ops) +bool flecs_query_term_is_unknown( + ecs_query_impl_t *query, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx) { - const EcsMetaType *ptr = ecs_get(world, type, EcsMetaType); - if (!ptr) { - char *path = ecs_get_fullpath(world, type); - ecs_err("missing EcsMetaType for type %s'", path); - ecs_os_free(path); - return -1; + ecs_query_op_t dummy = {0}; + flecs_query_compile_term_ref(NULL, query, &dummy, &term->first, + &dummy.first, EcsQueryFirst, EcsVarEntity, ctx, false); + flecs_query_compile_term_ref(NULL, query, &dummy, &term->second, + &dummy.second, EcsQuerySecond, EcsVarEntity, ctx, false); + flecs_query_compile_term_ref(NULL, query, &dummy, &term->src, + &dummy.src, EcsQuerySrc, EcsVarAny, ctx, false); + + bool has_vars = dummy.flags & + ((EcsQueryIsVar << EcsQueryFirst) | + (EcsQueryIsVar << EcsQuerySecond) | + (EcsQueryIsVar << EcsQuerySrc)); + if (!has_vars) { + /* If term has no variables (typically terms with a static src) there + * can't be anything that's unknown. */ + return false; } - if (ptr->kind == EcsArrayType) { - return flecs_meta_serialize_array_component(world, type, ops); - } else { - return flecs_meta_serialize_type(world, type, 0, ops); + if (dummy.flags & (EcsQueryIsVar << EcsQueryFirst)) { + if (!flecs_query_var_is_unknown(query, dummy.first.var, ctx)) { + return false; + } + } + if (dummy.flags & (EcsQueryIsVar << EcsQuerySecond)) { + if (!flecs_query_var_is_unknown(query, dummy.second.var, ctx)) { + return false; + } } + if (dummy.flags & (EcsQueryIsVar << EcsQuerySrc)) { + if (!flecs_query_var_is_unknown(query, dummy.src.var, ctx)) { + return false; + } + } + + return true; } -void ecs_meta_type_serialized_init( - ecs_iter_t *it) +/* Find the next known term from specified offset. This function is used to find + * a term that can be evaluated before a term that is unknown. Evaluating known + * before unknown terms can significantly decrease the search space. */ +static +int32_t flecs_query_term_next_known( + ecs_query_impl_t *query, + ecs_query_compile_ctx_t *ctx, + int32_t offset, + ecs_flags64_t compiled) { - ecs_world_t *world = it->world; + ecs_query_t *q = &query->pub; + ecs_term_t *terms = q->terms; + int32_t i, count = q->term_count; - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t e = it->entities[i]; - ecs_vec_t ops; - ecs_vec_init_t(NULL, &ops, ecs_meta_type_op_t, 0); - flecs_meta_serialize_component(world, e, &ops); + for (i = offset; i < count; i ++) { + ecs_term_t *term = &terms[i]; + if (compiled & (1ull << i)) { + continue; + } - EcsMetaTypeSerialized *ptr = ecs_get_mut( - world, e, EcsMetaTypeSerialized); - if (ptr->ops.array) { - ecs_meta_dtor_serialized(ptr); + /* Only evaluate And terms */ + if (term->oper != EcsAnd || flecs_term_is_or(q, term)){ + continue; } - ptr->ops = ops; + /* Don't reorder terms in scopes */ + if (term->flags_ & EcsTermIsScope) { + continue; + } + + if (flecs_query_term_is_unknown(query, term, ctx)) { + continue; + } + + return i; } + + return -1; } -#endif +/* If the first part of a query contains more than one trivial term, insert a + * special instruction which batch-evaluates multiple terms. */ +static +void flecs_query_insert_trivial_search( + ecs_query_impl_t *query, + ecs_flags64_t *compiled, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_t *q = &query->pub; + ecs_term_t *terms = q->terms; + int32_t i, term_count = q->term_count; + ecs_flags64_t trivial_set = 0; -/** - * @file addons/os_api_impl/os_api_impl.c - * @brief Builtin implementation for OS API. - */ + /* Trivial search always ignores prefabs and disabled entities */ + if (query->pub.flags & (EcsQueryMatchPrefab|EcsQueryMatchDisabled)) { + return; + } + /* Find trivial terms, which can be handled in single instruction */ + int32_t trivial_wildcard_terms = 0; + int32_t trivial_terms = 0; -#ifdef FLECS_OS_API_IMPL -#ifdef ECS_TARGET_WINDOWS -/** - * @file addons/os_api_impl/posix_impl.inl - * @brief Builtin Windows implementation for OS API. - */ + for (i = 0; i < term_count; i ++) { + /* Term is already compiled */ + if (*compiled & (1ull << i)) { + continue; + } -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include + ecs_term_t *term = &terms[i]; + if (!(term->flags_ & EcsTermIsTrivial)) { + continue; + } -typedef struct ecs_win_thread_t { - HANDLE thread; - ecs_os_thread_callback_t callback; - void *arg; -} ecs_win_thread_t; + /* We can only add trivial terms to plan if they no up traversal */ + if ((term->src.id & EcsTraverseFlags) != EcsSelf) { + continue; + } -static -DWORD flecs_win_thread(void *ptr) { - ecs_win_thread_t *thread = ptr; - thread->callback(thread->arg); - return 0; -} + /* Wildcards are not supported for trivial queries */ + if (ecs_id_is_wildcard(term->id)) { + continue; + } -static -ecs_os_thread_t win_thread_new( - ecs_os_thread_callback_t callback, - void *arg) -{ - ecs_win_thread_t *thread = ecs_os_malloc_t(ecs_win_thread_t); - thread->arg= arg; - thread->callback = callback; - thread->thread = CreateThread( - NULL, 0, (LPTHREAD_START_ROUTINE)flecs_win_thread, thread, 0, NULL); - return (ecs_os_thread_t)(uintptr_t)thread; -} + trivial_set |= (1llu << i); -static -void* win_thread_join( - ecs_os_thread_t thr) -{ - ecs_win_thread_t *thread = (ecs_win_thread_t*)(uintptr_t)thr; - DWORD r = WaitForSingleObject(thread->thread, INFINITE); - if (r == WAIT_FAILED) { - ecs_err("win_thread_join: WaitForSingleObject failed"); + trivial_terms ++; } - ecs_os_free(thread); - return NULL; -} -static -ecs_os_thread_id_t win_thread_self(void) -{ - return (ecs_os_thread_id_t)GetCurrentThreadId(); -} + if (trivial_terms >= 2) { + /* Mark terms as compiled & populated */ + for (i = 0; i < q->term_count; i ++) { + if (trivial_set & (1llu << i)) { + *compiled |= (1ull << i); + } + } -static -int32_t win_ainc( - int32_t *count) -{ - return InterlockedIncrement((volatile long*)count); -} + /* If there's more than 1 trivial term, batch them in trivial search */ + ecs_query_op_t trivial = {0}; + if (!trivial_wildcard_terms) { + trivial.kind = EcsQueryTriv; + } -static -int32_t win_adec( - int32_t *count) -{ - return InterlockedDecrement((volatile long*)count); -} + /* Store the bitset with trivial terms on the instruction */ + trivial.src.entity = trivial_set; + flecs_query_op_insert(&trivial, ctx); -static -int64_t win_lainc( - int64_t *count) -{ - return InterlockedIncrement64(count); + /* Mark $this as written */ + ctx->written |= (1llu << 0); + } } static -int64_t win_ladec( - int64_t *count) +void flecs_query_insert_cache_search( + ecs_query_impl_t *query, + ecs_flags64_t *compiled, + ecs_query_compile_ctx_t *ctx) { - return InterlockedDecrement64(count); -} + if (!query->cache) { + return; + } -static -ecs_os_mutex_t win_mutex_new(void) { - CRITICAL_SECTION *mutex = ecs_os_malloc_t(CRITICAL_SECTION); - InitializeCriticalSection(mutex); - return (ecs_os_mutex_t)(uintptr_t)mutex; + ecs_query_t *q = &query->pub; + + if (q->cache_kind == EcsQueryCacheAll) { + /* If all terms are cacheable, make sure no other terms are compiled */ + *compiled = 0xFFFFFFFFFFFFFFFF; + } else if (q->cache_kind == EcsQueryCacheAuto) { + /* The query is partially cacheable */ + ecs_term_t *terms = q->terms; + int32_t i, count = q->term_count; + + for (i = 0; i < count; i ++) { + if ((*compiled) & (1ull << i)) { + continue; + } + + ecs_term_t *term = &terms[i]; + if (!(term->flags_ & EcsTermIsCacheable)) { + continue; + } + + *compiled |= (1ull << i); + } + } + + /* Insert the operation for cache traversal */ + ecs_query_op_t op = {0}; + + if (q->flags & EcsQueryIsCacheable) { + op.kind = EcsQueryIsCache; + } else { + op.kind = EcsQueryCache; + } + + flecs_query_write(0, &op.written); + flecs_query_write_ctx(0, ctx, false); + flecs_query_op_insert(&op, ctx); } static -void win_mutex_free( - ecs_os_mutex_t m) +bool flecs_term_ref_match_multiple( + ecs_term_ref_t *ref) { - CRITICAL_SECTION *mutex = (CRITICAL_SECTION*)(intptr_t)m; - DeleteCriticalSection(mutex); - ecs_os_free(mutex); + return (ref->id & EcsIsVariable) && (ECS_TERM_REF_ID(ref) != EcsAny); } static -void win_mutex_lock( - ecs_os_mutex_t m) +bool flecs_term_match_multiple( + ecs_term_t *term) { - CRITICAL_SECTION *mutex = (CRITICAL_SECTION*)(intptr_t)m; - EnterCriticalSection(mutex); + return flecs_term_ref_match_multiple(&term->first) || + flecs_term_ref_match_multiple(&term->second); } static -void win_mutex_unlock( - ecs_os_mutex_t m) +int flecs_query_insert_toggle( + ecs_query_impl_t *impl, + ecs_query_compile_ctx_t *ctx) { - CRITICAL_SECTION *mutex = (CRITICAL_SECTION*)(intptr_t)m; - LeaveCriticalSection(mutex); -} + ecs_query_t *q = &impl->pub; + int32_t i, j, term_count = q->term_count; + ecs_term_t *terms = q->terms; + ecs_flags64_t fields_done = 0; -static -ecs_os_cond_t win_cond_new(void) { - CONDITION_VARIABLE *cond = ecs_os_malloc_t(CONDITION_VARIABLE); - InitializeConditionVariable(cond); - return (ecs_os_cond_t)(uintptr_t)cond; -} + for (i = 0; i < term_count; i ++) { + if (fields_done & (1llu << i)) { + continue; + } -static -void win_cond_free( - ecs_os_cond_t c) -{ - (void)c; -} + ecs_term_t *term = &terms[i]; + if (term->flags_ & EcsTermIsToggle) { + ecs_query_op_t cur = {0}; + flecs_query_compile_term_ref(NULL, impl, &cur, &term->src, + &cur.src, EcsQuerySrc, EcsVarAny, ctx, false); -static -void win_cond_signal( - ecs_os_cond_t c) -{ - CONDITION_VARIABLE *cond = (CONDITION_VARIABLE*)(intptr_t)c; - WakeConditionVariable(cond); -} + ecs_flags64_t and_toggles = 0; + ecs_flags64_t not_toggles = 0; + ecs_flags64_t optional_toggles = 0; -static -void win_cond_broadcast( - ecs_os_cond_t c) -{ - CONDITION_VARIABLE *cond = (CONDITION_VARIABLE*)(intptr_t)c; - WakeAllConditionVariable(cond); -} + for (j = i; j < term_count; j ++) { + if (fields_done & (1llu << j)) { + continue; + } -static -void win_cond_wait( - ecs_os_cond_t c, - ecs_os_mutex_t m) -{ - CRITICAL_SECTION *mutex = (CRITICAL_SECTION*)(intptr_t)m; - CONDITION_VARIABLE *cond = (CONDITION_VARIABLE*)(intptr_t)c; - SleepConditionVariableCS(cond, mutex, INFINITE); -} + /* Also includes term[i], so flags get set correctly */ + term = &terms[j]; -static bool win_time_initialized; -static double win_time_freq; -static LARGE_INTEGER win_time_start; -static ULONG win_current_resolution; + /* If term is not for the same src, skip */ + ecs_query_op_t next = {0}; + flecs_query_compile_term_ref(NULL, impl, &next, &term->src, + &next.src, EcsQuerySrc, EcsVarAny, ctx, false); + if (next.src.entity != cur.src.entity || + next.flags != cur.flags) + { + continue; + } -static -void win_time_setup(void) { - if ( win_time_initialized) { - return; + /* Source matches, set flag */ + if (term->oper == EcsNot) { + not_toggles |= (1llu << j); + } else if (term->oper == EcsOptional) { + optional_toggles |= (1llu << j); + } else { + and_toggles |= (1llu << j); + } + + fields_done |= (1llu << j); + } + + if (and_toggles || not_toggles) { + ecs_query_op_t op = {0}; + op.kind = EcsQueryToggle; + op.src = cur.src; + op.flags = cur.flags; + + if (op.flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_write(op.src.var, &op.written); + } + + /* Encode fields: + * - first.entity is the fields that match enabled bits + * - second.entity is the fields that match disabled bits + */ + op.first.entity = and_toggles; + op.second.entity = not_toggles; + flecs_query_op_insert(&op, ctx); + } + + /* Insert separate instructions for optional terms. To make sure + * entities are returned in batches where fields are never partially + * set or unset, the result must be split up into batches that have + * the exact same toggle masks. Instead of complicating the toggle + * instruction with code to scan for blocks that have the same bits + * set, separate instructions let the query engine backtrack to get + * the right results. */ + if (optional_toggles) { + for (j = i; j < term_count; j ++) { + uint64_t field_bit = 1ull << j; + if (!(optional_toggles & field_bit)) { + continue; + } + + ecs_query_op_t op = {0}; + op.kind = EcsQueryToggleOption; + op.src = cur.src; + op.first.entity = field_bit; + op.flags = cur.flags; + flecs_query_op_insert(&op, ctx); + } + } + } } - - win_time_initialized = true; - LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - QueryPerformanceCounter(&win_time_start); - win_time_freq = (double)freq.QuadPart / 1000000000.0; + return 0; } static -void win_sleep( - int32_t sec, - int32_t nanosec) +int flecs_query_insert_fixed_src_terms( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_flags64_t *compiled, + ecs_query_compile_ctx_t *ctx) { - HANDLE timer; - LARGE_INTEGER ft; + ecs_query_t *q = &impl->pub; + int32_t i, term_count = q->term_count; + ecs_term_t *terms = q->terms; - ft.QuadPart = -((int64_t)sec * 10000000 + (int64_t)nanosec / 100); + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; - timer = CreateWaitableTimer(NULL, TRUE, NULL); - SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); - WaitForSingleObject(timer, INFINITE); - CloseHandle(timer); + if (term->oper == EcsNot) { + /* If term has not operator and variables for first/second, we can't + * put the term first as this could prevent us from getting back + * valid results. For example: + * !$var(e), Tag($var) + * + * Here, the first term would evaluate to false (and cause the + * entire query not to match) if 'e' has any components. + * + * However, when reordering we get results: + * Tag($var), !$var(e) + * + * Now the query returns all entities with Tag, that 'e' does not + * have as component. For this reason, queries should never use + * unwritten variables in not terms- and we should also not reorder + * terms in a way that results in doing this. */ + if (flecs_term_match_multiple(term)) { + continue; + } + } + + /* Don't reorder terms in scopes */ + if (term->flags_ & EcsTermIsScope) { + continue; + } + + if (term->src.id & EcsIsEntity && ECS_TERM_REF_ID(&term->src)) { + if (flecs_query_compile_term(world, impl, term, ctx)) { + return -1; + } + + *compiled |= (1llu << i); + } + } + + return 0; } -static -void win_enable_high_timer_resolution(bool enable) -{ - HMODULE hntdll = GetModuleHandle(TEXT("ntdll.dll")); - if (!hntdll) { - return; +int flecs_query_compile( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_query_impl_t *query) +{ + /* Compile query to operations. Only necessary for non-trivial queries, as + * trivial queries use trivial iterators that don't use query ops. */ + bool needs_plan = true; + ecs_flags32_t flags = query->pub.flags; + ecs_flags32_t trivial_flags = EcsQueryIsTrivial|EcsQueryMatchOnlySelf; + if ((flags & trivial_flags) == trivial_flags) { + if (query->cache) { + if (flags & EcsQueryIsCacheable) { + needs_plan = false; + } + } else { + if (!(flags & EcsQueryMatchWildcards)) { + needs_plan = false; + } + } } - union { - LONG (__stdcall *f)( - ULONG desired, BOOLEAN set, ULONG * current); - FARPROC p; - } func; + if (!needs_plan) { + /* Initialize space for $this variable */ + query->pub.var_count = 1; + query->var_count = 1; + query->var_size = 1; + query->vars = &flecs_this_array; + query->pub.vars = &flecs_this_name_array; + query->pub.flags |= EcsQueryHasTableThisVar; + return 0; + } - func.p = GetProcAddress(hntdll, "NtSetTimerResolution"); - if(!func.p) { - return; + ecs_query_t *q = &query->pub; + ecs_term_t *terms = q->terms; + ecs_query_compile_ctx_t ctx = {0}; + ecs_vec_reset_t(NULL, &stage->operations, ecs_query_op_t); + ctx.ops = &stage->operations; + ctx.cur = ctx.ctrlflow; + ctx.cur->lbl_begin = -1; + ctx.cur->lbl_begin = -1; + ecs_vec_clear(ctx.ops); + + /* Find all variables defined in query */ + if (flecs_query_discover_vars(stage, query)) { + return -1; } - ULONG current, resolution = 10000; /* 1 ms */ + /* If query contains fixed source terms, insert operation to set sources */ + int32_t i, term_count = q->term_count; + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + if (term->src.id & EcsIsEntity) { + ecs_query_op_t set_fixed = {0}; + set_fixed.kind = EcsQuerySetFixed; + flecs_query_op_insert(&set_fixed, &ctx); + break; + } + } - if (!enable && win_current_resolution) { - func.f(win_current_resolution, 0, ¤t); - win_current_resolution = 0; - return; - } else if (!enable) { - return; + /* If the query contains terms with fixed ids (no wildcards, variables), + * insert instruction that initializes ecs_iter_t::ids. This allows for the + * insertion of simpler instructions later on. + * If the query is entirely cacheable, ids are populated by the cache. */ + if (q->cache_kind != EcsQueryCacheAll) { + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + if (flecs_term_is_fixed_id(q, term) || + (term->src.id & EcsIsEntity && + !(term->src.id & ~EcsTermRefFlags))) + { + ecs_query_op_t set_ids = {0}; + set_ids.kind = EcsQuerySetIds; + flecs_query_op_insert(&set_ids, &ctx); + break; + } + } } - if (resolution == win_current_resolution) { - return; + ecs_flags64_t compiled = 0; + + /* Always evaluate terms with fixed source before other terms */ + flecs_query_insert_fixed_src_terms( + world, query, &compiled, &ctx); + + /* Compile cacheable terms */ + flecs_query_insert_cache_search(query, &compiled, &ctx); + + /* Insert trivial term search if query allows for it */ + flecs_query_insert_trivial_search(query, &compiled, &ctx); + + /* If a query starts with one or more optional terms, first compile the non + * optional terms. This prevents having to insert an instruction that + * matches the query against every entity in the storage. + * Only skip optional terms at the start of the query so that any + * short-circuiting behavior isn't affected (a non-optional term can become + * optional if it uses a variable set in an optional term). */ + int32_t start_term = 0; + for (; start_term < term_count; start_term ++) { + if (terms[start_term].oper != EcsOptional) { + break; + } } - if (win_current_resolution) { - func.f(win_current_resolution, 0, ¤t); + do { + /* Compile remaining query terms to instructions */ + for (i = start_term; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + int32_t compile = i; + + if (compiled & (1ull << i)) { + continue; /* Already compiled */ + } + + if (term->oper == EcsOptional && start_term) { + /* Don't reorder past the first optional term that's not in the + * initial list of optional terms. This protects short + * circuiting branching in the query. + * A future algorithm could look at which variables are + * accessed by optional terms, and continue reordering terms + * that don't access those variables. */ + break; + } + + bool can_reorder = true; + if (term->oper != EcsAnd || flecs_term_is_or(q, term)){ + can_reorder = false; + } + + /* If variables have been written, but this term has no known variables, + * first try to resolve terms that have known variables. This can + * significantly reduce the search space. + * Only perform this optimization after at least one variable has been + * written to, as all terms are unknown otherwise. */ + if (can_reorder && ctx.written && + flecs_query_term_is_unknown(query, term, &ctx)) + { + int32_t term_index = flecs_query_term_next_known( + query, &ctx, i + 1, compiled); + if (term_index != -1) { + term = &q->terms[term_index]; + compile = term_index; + i --; /* Repeat current term */ + } + } + + if (flecs_query_compile_term(world, query, term, &ctx)) { + return -1; + } + + compiled |= (1ull << compile); + } + + if (start_term) { + start_term = 0; /* Repeat, now also insert optional terms */ + } else { + break; + } + } while (true); + + ecs_var_id_t this_id = flecs_query_find_var_id(query, "this", EcsVarEntity); + if (this_id != EcsVarNone) { + /* If This variable has been written as entity, insert an operation to + * assign it to it.entities for consistency. */ + if (ctx.written & (1ull << this_id)) { + ecs_query_op_t set_this = {0}; + set_this.kind = EcsQuerySetThis; + set_this.flags |= (EcsQueryIsVar << EcsQueryFirst); + set_this.first.var = this_id; + flecs_query_op_insert(&set_this, &ctx); + } } - if (func.f(resolution, 1, ¤t)) { - /* Try setting a lower resolution */ - resolution *= 2; - if(func.f(resolution, 1, ¤t)) return; + /* Make sure non-This variables are written as entities */ + if (query->vars) { + for (i = 0; i < query->var_count; i ++) { + ecs_query_var_t *var = &query->vars[i]; + if (var->id && var->kind == EcsVarTable && var->name) { + ecs_var_id_t var_id = flecs_query_find_var_id(query, var->name, + EcsVarEntity); + if (!flecs_query_is_written(var_id, ctx.written)) { + /* Skip anonymous variables */ + if (!flecs_query_var_is_anonymous(query, var_id)) { + flecs_query_insert_each(var->id, var_id, &ctx, false); + } + } + } + } } - win_current_resolution = resolution; -} + /* If query contains non-This variables as term source, build lookup array */ + if (query->src_vars) { + ecs_assert(query->vars != NULL, ECS_INTERNAL_ERROR, NULL); + bool only_anonymous = true; + + for (i = 0; i < q->field_count; i ++) { + ecs_var_id_t var_id = query->src_vars[i]; + if (!var_id) { + continue; + } + + if (!flecs_query_var_is_anonymous(query, var_id)) { + only_anonymous = false; + break; + } else { + /* Don't fetch component data for anonymous variables. Because + * not all metadata (such as it.sources) is initialized for + * anonymous variables, and because they may only be available + * as table variables (each is not guaranteed to be inserted for + * anonymous variables) the iterator may not have sufficient + * information to resolve component data. */ + for (int32_t t = 0; t < q->term_count; t ++) { + ecs_term_t *term = &q->terms[t]; + if (term->field_index == i) { + term->inout = EcsInOutNone; + } + } + } + } + + /* Don't insert setvar instruction if all vars are anonymous */ + if (!only_anonymous) { + ecs_query_op_t set_vars = {0}; + set_vars.kind = EcsQuerySetVars; + flecs_query_op_insert(&set_vars, &ctx); + } -static -uint64_t win_time_now(void) { - uint64_t now; + for (i = 0; i < q->field_count; i ++) { + ecs_var_id_t var_id = query->src_vars[i]; + if (!var_id) { + continue; + } - LARGE_INTEGER qpc_t; - QueryPerformanceCounter(&qpc_t); - now = (uint64_t)((double)qpc_t.QuadPart / win_time_freq); + if (query->vars[var_id].kind == EcsVarTable) { + var_id = flecs_query_find_var_id(query, query->vars[var_id].name, + EcsVarEntity); - return now; -} + /* Variables used as source that aren't This must be entities */ + ecs_assert(var_id != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + } -static -void win_fini(void) { - if (ecs_os_api.flags_ & EcsOsApiHighResolutionTimer) { - win_enable_high_timer_resolution(false); + query->src_vars[i] = var_id; + } } -} - -void ecs_set_os_api_impl(void) { - ecs_os_set_api_defaults(); - ecs_os_api_t api = ecs_os_api; + ecs_assert((term_count - ctx.skipped) >= 0, ECS_INTERNAL_ERROR, NULL); - api.thread_new_ = win_thread_new; - api.thread_join_ = win_thread_join; - api.thread_self_ = win_thread_self; - api.task_new_ = win_thread_new; - api.task_join_ = win_thread_join; - api.ainc_ = win_ainc; - api.adec_ = win_adec; - api.lainc_ = win_lainc; - api.ladec_ = win_ladec; - api.mutex_new_ = win_mutex_new; - api.mutex_free_ = win_mutex_free; - api.mutex_lock_ = win_mutex_lock; - api.mutex_unlock_ = win_mutex_unlock; - api.cond_new_ = win_cond_new; - api.cond_free_ = win_cond_free; - api.cond_signal_ = win_cond_signal; - api.cond_broadcast_ = win_cond_broadcast; - api.cond_wait_ = win_cond_wait; - api.sleep_ = win_sleep; - api.now_ = win_time_now; - api.fini_ = win_fini; + /* If query is empty, insert Nothing instruction */ + if (!(term_count - ctx.skipped)) { + ecs_vec_clear(ctx.ops); + ecs_query_op_t nothing = {0}; + nothing.kind = EcsQueryNothing; + flecs_query_op_insert(¬hing, &ctx); + } else { + /* If query contains terms for toggleable components, insert toggle */ + if (!(q->flags & EcsQueryTableOnly)) { + flecs_query_insert_toggle(query, &ctx); + } - win_time_setup(); + /* Insert yield. If program reaches this operation, a result was found */ + ecs_query_op_t yield = {0}; + yield.kind = EcsQueryYield; + flecs_query_op_insert(&yield, &ctx); + } - if (ecs_os_api.flags_ & EcsOsApiHighResolutionTimer) { - win_enable_high_timer_resolution(true); + int32_t op_count = ecs_vec_count(ctx.ops); + if (op_count) { + query->op_count = op_count; + query->ops = flecs_alloc_n(&stage->allocator, ecs_query_op_t, op_count); + ecs_query_op_t *query_ops = ecs_vec_first_t(ctx.ops, ecs_query_op_t); + ecs_os_memcpy_n(query->ops, query_ops, ecs_query_op_t, op_count); } - ecs_os_set_api(&api); + return 0; } -#else /** - * @file addons/os_api_impl/posix_impl.inl - * @brief Builtin POSIX implementation for OS API. + * @file query/compiler/compiler_term.c + * @brief Compile query term. */ -#include "pthread.h" - -#if defined(__APPLE__) && defined(__MACH__) -#include -#elif defined(__EMSCRIPTEN__) -#include -#else -#include -#endif -/* This mutex is used to emulate atomic operations when the gnu builtins are - * not supported. This is probably not very fast but if the compiler doesn't - * support the gnu built-ins, then speed is probably not a priority. */ -#ifndef __GNUC__ -static pthread_mutex_t atomic_mutex = PTHREAD_MUTEX_INITIALIZER; -#endif +#define FlecsRuleOrMarker ((int16_t)-2) /* Marks instruction in OR chain */ -static -ecs_os_thread_t posix_thread_new( - ecs_os_thread_callback_t callback, - void *arg) +ecs_var_id_t flecs_query_find_var_id( + const ecs_query_impl_t *query, + const char *name, + ecs_var_kind_t kind) { - pthread_t *thread = ecs_os_malloc(sizeof(pthread_t)); - - if (pthread_create (thread, NULL, callback, arg) != 0) { - ecs_os_abort(); - } - - return (ecs_os_thread_t)(uintptr_t)thread; -} + ecs_assert(name != NULL, ECS_INTERNAL_ERROR, NULL); -static -void* posix_thread_join( - ecs_os_thread_t thread) -{ - void *arg; - pthread_t *thr = (pthread_t*)(uintptr_t)thread; - pthread_join(*thr, &arg); - ecs_os_free(thr); - return arg; -} + if (kind == EcsVarTable) { + if (!ecs_os_strcmp(name, EcsThisName)) { + if (query->pub.flags & EcsQueryHasTableThisVar) { + return 0; + } else { + return EcsVarNone; + } + } -static -ecs_os_thread_id_t posix_thread_self(void) -{ - return (ecs_os_thread_id_t)pthread_self(); -} + if (!flecs_name_index_is_init(&query->tvar_index)) { + return EcsVarNone; + } -static -int32_t posix_ainc( - int32_t *count) -{ - int value; -#ifdef __GNUC__ - value = __sync_add_and_fetch (count, 1); - return value; -#else - if (pthread_mutex_lock(&atomic_mutex)) { - abort(); - } - value = (*count) += 1; - if (pthread_mutex_unlock(&atomic_mutex)) { - abort(); + uint64_t index = flecs_name_index_find( + &query->tvar_index, name, 0, 0); + if (index == 0) { + return EcsVarNone; + } + return flecs_utovar(index); } - return value; -#endif -} -static -int32_t posix_adec( - int32_t *count) -{ - int32_t value; -#ifdef __GNUC__ - value = __sync_sub_and_fetch (count, 1); - return value; -#else - if (pthread_mutex_lock(&atomic_mutex)) { - abort(); - } - value = (*count) -= 1; - if (pthread_mutex_unlock(&atomic_mutex)) { - abort(); - } - return value; -#endif -} + if (kind == EcsVarEntity) { + if (!flecs_name_index_is_init(&query->evar_index)) { + return EcsVarNone; + } -static -int64_t posix_lainc( - int64_t *count) -{ - int64_t value; -#ifdef __GNUC__ - value = __sync_add_and_fetch (count, 1); - return value; -#else - if (pthread_mutex_lock(&atomic_mutex)) { - abort(); - } - value = (*count) += 1; - if (pthread_mutex_unlock(&atomic_mutex)) { - abort(); + uint64_t index = flecs_name_index_find( + &query->evar_index, name, 0, 0); + if (index == 0) { + return EcsVarNone; + } + return flecs_utovar(index); } - return value; -#endif -} -static -int64_t posix_ladec( - int64_t *count) -{ - int64_t value; -#ifdef __GNUC__ - value = __sync_sub_and_fetch (count, 1); - return value; -#else - if (pthread_mutex_lock(&atomic_mutex)) { - abort(); - } - value = (*count) -= 1; - if (pthread_mutex_unlock(&atomic_mutex)) { - abort(); - } - return value; -#endif -} + ecs_assert(kind == EcsVarAny, ECS_INTERNAL_ERROR, NULL); -static -ecs_os_mutex_t posix_mutex_new(void) { - pthread_mutex_t *mutex = ecs_os_malloc(sizeof(pthread_mutex_t)); - if (pthread_mutex_init(mutex, NULL)) { - abort(); + /* If searching for any kind of variable, start with most specific */ + ecs_var_id_t index = flecs_query_find_var_id(query, name, EcsVarEntity); + if (index != EcsVarNone) { + return index; } - return (ecs_os_mutex_t)(uintptr_t)mutex; -} -static -void posix_mutex_free( - ecs_os_mutex_t m) -{ - pthread_mutex_t *mutex = (pthread_mutex_t*)(intptr_t)m; - pthread_mutex_destroy(mutex); - ecs_os_free(mutex); + return flecs_query_find_var_id(query, name, EcsVarTable); } static -void posix_mutex_lock( - ecs_os_mutex_t m) +ecs_var_id_t flecs_query_most_specific_var( + ecs_query_impl_t *query, + const char *name, + ecs_var_kind_t kind, + ecs_query_compile_ctx_t *ctx) { - pthread_mutex_t *mutex = (pthread_mutex_t*)(intptr_t)m; - if (pthread_mutex_lock(mutex)) { - abort(); + if (kind == EcsVarTable || kind == EcsVarEntity) { + return flecs_query_find_var_id(query, name, kind); } -} -static -void posix_mutex_unlock( - ecs_os_mutex_t m) -{ - pthread_mutex_t *mutex = (pthread_mutex_t*)(intptr_t)m; - if (pthread_mutex_unlock(mutex)) { - abort(); + ecs_var_id_t evar = flecs_query_find_var_id(query, name, EcsVarEntity); + if ((evar != EcsVarNone) && flecs_query_is_written(evar, ctx->written)) { + /* If entity variable is available and written to, it contains the most + * specific result and should be used. */ + return evar; } -} -static -ecs_os_cond_t posix_cond_new(void) { - pthread_cond_t *cond = ecs_os_malloc(sizeof(pthread_cond_t)); - if (pthread_cond_init(cond, NULL)) { - abort(); + ecs_var_id_t tvar = flecs_query_find_var_id(query, name, EcsVarTable); + if ((tvar != EcsVarNone) && !flecs_query_is_written(tvar, ctx->written)) { + /* If variable of any kind is requested and variable hasn't been written + * yet, write to table variable */ + return tvar; } - return (ecs_os_cond_t)(uintptr_t)cond; -} -static -void posix_cond_free( - ecs_os_cond_t c) -{ - pthread_cond_t *cond = (pthread_cond_t*)(intptr_t)c; - if (pthread_cond_destroy(cond)) { - abort(); + /* If table var is written, and entity var doesn't exist or is not written, + * return table var */ + if (tvar != EcsVarNone) { + return tvar; + } else { + return evar; } - ecs_os_free(cond); } -static -void posix_cond_signal( - ecs_os_cond_t c) +ecs_query_lbl_t flecs_query_op_insert( + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx) { - pthread_cond_t *cond = (pthread_cond_t*)(intptr_t)c; - if (pthread_cond_signal(cond)) { - abort(); + ecs_query_op_t *elem = ecs_vec_append_t(NULL, ctx->ops, ecs_query_op_t); + int32_t count = ecs_vec_count(ctx->ops); + *elem = *op; + if (count > 1) { + if (ctx->cur->lbl_begin == -1) { + /* Variables written by previous instruction can't be written by + * this instruction, except when this is part of an OR chain. */ + elem->written &= ~elem[-1].written; + } } -} -static -void posix_cond_broadcast( - ecs_os_cond_t c) -{ - pthread_cond_t *cond = (pthread_cond_t*)(intptr_t)c; - if (pthread_cond_broadcast(cond)) { - abort(); - } + elem->next = flecs_itolbl(count); + elem->prev = flecs_itolbl(count - 2); + return flecs_itolbl(count - 1); } -static -void posix_cond_wait( - ecs_os_cond_t c, - ecs_os_mutex_t m) +ecs_query_op_t* flecs_query_begin_block( + ecs_query_op_kind_t kind, + ecs_query_compile_ctx_t *ctx) { - pthread_cond_t *cond = (pthread_cond_t*)(intptr_t)c; - pthread_mutex_t *mutex = (pthread_mutex_t*)(intptr_t)m; - if (pthread_cond_wait(cond, mutex)) { - abort(); - } + ecs_query_op_t op = {0}; + op.kind = flecs_ito(uint8_t, kind); + ctx->cur->lbl_begin = flecs_query_op_insert(&op, ctx); + return ecs_vec_get_t(ctx->ops, ecs_query_op_t, ctx->cur->lbl_begin); } -static bool posix_time_initialized; - -#if defined(__APPLE__) && defined(__MACH__) -static mach_timebase_info_data_t posix_osx_timebase; -static uint64_t posix_time_start; -#else -static uint64_t posix_time_start; -#endif +void flecs_query_end_block( + ecs_query_compile_ctx_t *ctx, + bool reset) +{ + ecs_query_op_t new_op = {0}; + new_op.kind = EcsQueryEnd; + ecs_query_lbl_t end = flecs_query_op_insert(&new_op, ctx); + + ecs_query_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_query_op_t); + ops[ctx->cur->lbl_begin].next = end; -static -void posix_time_setup(void) { - if (posix_time_initialized) { - return; + ecs_query_op_t *end_op = &ops[end]; + if (reset && ctx->cur->lbl_query != -1) { + ecs_query_op_t *query_op = &ops[ctx->cur->lbl_query]; + end_op->prev = ctx->cur->lbl_begin; + end_op->src = query_op->src; + end_op->first = query_op->first; + end_op->second = query_op->second; + end_op->flags = query_op->flags; + end_op->field_index = query_op->field_index; + } else { + end_op->prev = ctx->cur->lbl_begin; + end_op->field_index = -1; } - - posix_time_initialized = true; - #if defined(__APPLE__) && defined(__MACH__) - mach_timebase_info(&posix_osx_timebase); - posix_time_start = mach_absolute_time(); - #else - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - posix_time_start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; - #endif + ctx->cur->lbl_begin = -1; } static -void posix_sleep( - int32_t sec, - int32_t nanosec) +void flecs_query_begin_block_cond_eval( + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx, + ecs_write_flags_t cond_write_state) { - struct timespec sleepTime; - ecs_assert(sec >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(nanosec >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_var_id_t first_var = EcsVarNone, second_var = EcsVarNone, src_var = EcsVarNone; + ecs_write_flags_t cond_mask = 0; - sleepTime.tv_sec = sec; - sleepTime.tv_nsec = nanosec; - if (nanosleep(&sleepTime, NULL)) { - ecs_err("nanosleep failed"); + if (flecs_query_ref_flags(op->flags, EcsQueryFirst) == EcsQueryIsVar) { + first_var = op->first.var; + ecs_assert(first_var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + cond_mask |= (1ull << first_var); + } + if (flecs_query_ref_flags(op->flags, EcsQuerySecond) == EcsQueryIsVar) { + second_var = op->second.var; + ecs_assert(second_var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + cond_mask |= (1ull << second_var); + } + if (flecs_query_ref_flags(op->flags, EcsQuerySrc) == EcsQueryIsVar) { + src_var = op->src.var; + ecs_assert(src_var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + cond_mask |= (1ull << src_var); } -} -/* prevent 64-bit overflow when computing relative timestamp - see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 -*/ -#if defined(ECS_TARGET_DARWIN) -static -int64_t posix_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { - int64_t q = value / denom; - int64_t r = value % denom; - return q * numer + r * numer / denom; -} -#endif + /* Variables set in an OR chain are marked as conditional writes. However, + * writes from previous terms in the current OR chain shouldn't be treated + * as variables that are conditionally set, so instead use the write mask + * from before the chain started. */ + if (ctx->ctrlflow->in_or) { + cond_write_state = ctx->ctrlflow->cond_written_or; + } -static -uint64_t posix_time_now(void) { - ecs_assert(posix_time_initialized != 0, ECS_INTERNAL_ERROR, NULL); + /* If this term uses conditionally set variables, insert instruction that + * jumps over the term if the variables weren't set yet. */ + if (cond_mask & cond_write_state) { + ctx->cur->lbl_cond_eval = flecs_itolbl(ecs_vec_count(ctx->ops)); - uint64_t now; + ecs_query_op_t jmp_op = {0}; + jmp_op.kind = EcsQueryIfVar; - #if defined(ECS_TARGET_DARWIN) - now = (uint64_t) posix_int64_muldiv( - (int64_t)mach_absolute_time(), - (int64_t)posix_osx_timebase.numer, - (int64_t)posix_osx_timebase.denom); - #elif defined(__EMSCRIPTEN__) - now = (long long)(emscripten_get_now() * 1000.0 * 1000); - #else - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - now = ((uint64_t)ts.tv_sec * 1000 * 1000 * 1000 + (uint64_t)ts.tv_nsec); - #endif + if ((first_var != EcsVarNone) && cond_write_state & (1ull << first_var)) { + jmp_op.flags |= (EcsQueryIsVar << EcsQueryFirst); + jmp_op.first.var = first_var; + } + if ((second_var != EcsVarNone) && cond_write_state & (1ull << second_var)) { + jmp_op.flags |= (EcsQueryIsVar << EcsQuerySecond); + jmp_op.second.var = second_var; + } + if ((src_var != EcsVarNone) && cond_write_state & (1ull << src_var)) { + jmp_op.flags |= (EcsQueryIsVar << EcsQuerySrc); + jmp_op.src.var = src_var; + } - return now; + flecs_query_op_insert(&jmp_op, ctx); + } else { + ctx->cur->lbl_cond_eval = -1; + } } -void ecs_set_os_api_impl(void) { - ecs_os_set_api_defaults(); - - ecs_os_api_t api = ecs_os_api; +static +void flecs_query_end_block_cond_eval( + ecs_query_compile_ctx_t *ctx) +{ + if (ctx->cur->lbl_cond_eval == -1) { + return; + } - api.thread_new_ = posix_thread_new; - api.thread_join_ = posix_thread_join; - api.thread_self_ = posix_thread_self; - api.task_new_ = posix_thread_new; - api.task_join_ = posix_thread_join; - api.ainc_ = posix_ainc; - api.adec_ = posix_adec; - api.lainc_ = posix_lainc; - api.ladec_ = posix_ladec; - api.mutex_new_ = posix_mutex_new; - api.mutex_free_ = posix_mutex_free; - api.mutex_lock_ = posix_mutex_lock; - api.mutex_unlock_ = posix_mutex_unlock; - api.cond_new_ = posix_cond_new; - api.cond_free_ = posix_cond_free; - api.cond_signal_ = posix_cond_signal; - api.cond_broadcast_ = posix_cond_broadcast; - api.cond_wait_ = posix_cond_wait; - api.sleep_ = posix_sleep; - api.now_ = posix_time_now; + ecs_assert(ctx->cur->lbl_query >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_query_op_t end_op = {0}; + end_op.kind = EcsQueryEnd; + ecs_query_lbl_t end = flecs_query_op_insert(&end_op, ctx); - posix_time_setup(); + ecs_query_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_query_op_t); + ops[ctx->cur->lbl_cond_eval].next = end; - ecs_os_set_api(&api); + ecs_query_op_t *end_op_ptr = &ops[end]; + ecs_query_op_t *query_op = &ops[ctx->cur->lbl_query]; + end_op_ptr->prev = ctx->cur->lbl_cond_eval; + end_op_ptr->src = query_op->src; + end_op_ptr->first = query_op->first; + end_op_ptr->second = query_op->second; + end_op_ptr->flags = query_op->flags; + end_op_ptr->field_index = query_op->field_index; } -#endif -#endif - -/** - * @file addons/ipeline/pipeline.c - * @brief Functions for building and running pipelines. - */ +static +void flecs_query_begin_block_or( + ecs_query_op_t *op, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t *or_op = flecs_query_begin_block(EcsQueryNot, ctx); + or_op->kind = EcsQueryOr; + or_op->field_index = term->field_index; + /* Set the source of the evaluate terms as source of the Or instruction. + * This lets the engine determine whether the variable has already been + * written. When the source is not yet written, an OR operation needs to + * take the union of all the terms in the OR chain. When the variable is + * known, it will return after the first matching term. + * + * In case a term in the OR expression is an equality predicate which + * compares the left hand side with a variable, the variable acts as an + * alias, so we can always assume that it's written. */ + bool add_src = true; + if (ECS_TERM_REF_ID(&term->first) == EcsPredEq && term->second.id & EcsIsVariable) { + if (!(flecs_query_is_written(op->src.var, ctx->written))) { + add_src = false; + } + } -#ifdef FLECS_PIPELINE + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + if (add_src) { + or_op->flags = (EcsQueryIsVar << EcsQuerySrc); + or_op->src = op->src; + ctx->cur->src_or = op->src; + } -static void flecs_pipeline_free( - ecs_pipeline_state_t *p) -{ - if (p) { - ecs_world_t *world = p->query->filter.world; - ecs_allocator_t *a = &world->allocator; - ecs_vec_fini_t(a, &p->ops, ecs_pipeline_op_t); - ecs_vec_fini_t(a, &p->systems, ecs_entity_t); - ecs_os_free(p->iters); - ecs_query_fini(p->query); - ecs_os_free(p); + ctx->cur->src_written_or = flecs_query_is_written( + op->src.var, ctx->written); } } -static ECS_MOVE(EcsPipeline, dst, src, { - flecs_pipeline_free(dst->state); - dst->state = src->state; - src->state = NULL; -}) - -static ECS_DTOR(EcsPipeline, ptr, { - flecs_pipeline_free(ptr->state); -}) - -typedef enum ecs_write_kind_t { - WriteStateNone = 0, - WriteStateToStage, -} ecs_write_kind_t; - -typedef struct ecs_write_state_t { - bool write_barrier; - ecs_map_t ids; - ecs_map_t wildcard_ids; -} ecs_write_state_t; - static -ecs_write_kind_t flecs_pipeline_get_write_state( - ecs_write_state_t *write_state, - ecs_id_t id) +void flecs_query_end_block_or( + ecs_query_impl_t *impl, + ecs_query_compile_ctx_t *ctx) { - ecs_write_kind_t result = WriteStateNone; + ecs_query_op_t op = {0}; + op.kind = EcsQueryEnd; + ecs_query_lbl_t end = flecs_query_op_insert(&op, ctx); - if (write_state->write_barrier) { - /* Any component could have been written */ - return WriteStateToStage; - } + ecs_query_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_query_op_t); + int32_t i, prev_or = ctx->cur->lbl_begin + 1; + for (i = ctx->cur->lbl_begin + 1; i < end; i ++) { + if (ops[i].next == FlecsRuleOrMarker) { + if (i == (end - 1)) { + ops[prev_or].prev = ctx->cur->lbl_begin; + } else { + ops[prev_or].prev = flecs_itolbl(i + 1); + } - if (id == EcsWildcard) { - /* Using a wildcard for id indicates read barrier. Return true if any - * components could have been staged */ - if (ecs_map_count(&write_state->ids) || - ecs_map_count(&write_state->wildcard_ids)) - { - return WriteStateToStage; + ops[i].next = flecs_itolbl(end); + + prev_or = i + 1; } } - if (!ecs_id_is_wildcard(id)) { - if (ecs_map_get(&write_state->ids, id)) { - result = WriteStateToStage; - } - } else { - ecs_map_iter_t it = ecs_map_iter(&write_state->ids); - while (ecs_map_next(&it)) { - if (ecs_id_match(ecs_map_key(&it), id)) { - return WriteStateToStage; + ecs_query_op_t *first = &ops[ctx->cur->lbl_begin]; + bool src_is_var = first->flags & (EcsQueryIsVar << EcsQuerySrc); + first->next = flecs_itolbl(end); + ops[end].prev = ctx->cur->lbl_begin; + ops[end - 1].prev = ctx->cur->lbl_begin; + + ctx->ctrlflow->in_or = false; + ctx->cur->lbl_begin = -1; + if (src_is_var) { + ecs_var_id_t src_var = first->src.var; + ctx->written |= (1llu << src_var); + + /* If src is a table variable, it is possible that this was resolved to + * an entity variable in all of the OR terms. If this is the case, mark + * entity variable as written as well. */ + ecs_query_var_t *var = &impl->vars[src_var]; + if (var->kind == EcsVarTable) { + const char *name = var->name; + if (!name) { + name = "this"; } - } - } - if (ecs_map_count(&write_state->wildcard_ids)) { - ecs_map_iter_t it = ecs_map_iter(&write_state->wildcard_ids); - while (ecs_map_next(&it)) { - if (ecs_id_match(id, ecs_map_key(&it))) { - return WriteStateToStage; + ecs_var_id_t evar = flecs_query_find_var_id( + impl, name, EcsVarEntity); + if (evar != EcsVarNone && (ctx->cond_written & (1llu << evar))) { + ctx->written |= (1llu << evar); + ctx->cond_written &= ~(1llu << evar); } } } + ctx->written |= ctx->cond_written; - return result; -} + /* Scan which variables were conditionally written in the OR chain and + * reset instructions after the OR chain. If a variable is set in part one + * of a chain but not part two, there would be nothing writing to the + * variable in part two, leaving it to the previous value. To address this + * a reset is inserted that resets the variable value on redo. */ + for (i = 1; i < (8 * ECS_SIZEOF(ecs_write_flags_t)); i ++) { + ecs_write_flags_t prev = 1 & (ctx->ctrlflow->cond_written_or >> i); + ecs_write_flags_t cur = 1 & (ctx->cond_written >> i); -static -void flecs_pipeline_set_write_state( - ecs_write_state_t *write_state, - ecs_id_t id) -{ - if (id == EcsWildcard) { - /* If writing to wildcard, flag all components as written */ - write_state->write_barrier = true; - return; - } + /* Skip variable if it's the source for the OR chain */ + if (src_is_var && (i == first->src.var)) { + continue; + } - ecs_map_t *ids; - if (ecs_id_is_wildcard(id)) { - ids = &write_state->wildcard_ids; - } else { - ids = &write_state->ids; + if (!prev && cur) { + ecs_query_op_t reset_op = {0}; + reset_op.kind = EcsQueryReset; + reset_op.flags |= (EcsQueryIsVar << EcsQuerySrc); + reset_op.src.var = flecs_itovar(i); + flecs_query_op_insert(&reset_op, ctx); + } } +} - ecs_map_ensure(ids, id)[0] = true; +void flecs_query_insert_each( + ecs_var_id_t tvar, + ecs_var_id_t evar, + ecs_query_compile_ctx_t *ctx, + bool cond_write) +{ + ecs_query_op_t each = {0}; + each.kind = EcsQueryEach; + each.src.var = evar; + each.first.var = tvar; + each.flags = (EcsQueryIsVar << EcsQuerySrc) | + (EcsQueryIsVar << EcsQueryFirst); + flecs_query_write_ctx(evar, ctx, cond_write); + flecs_query_write(evar, &each.written); + flecs_query_op_insert(&each, ctx); } static -void flecs_pipeline_reset_write_state( - ecs_write_state_t *write_state) +void flecs_query_insert_lookup( + ecs_var_id_t base_var, + ecs_var_id_t evar, + ecs_query_compile_ctx_t *ctx, + bool cond_write) { - ecs_map_clear(&write_state->ids); - ecs_map_clear(&write_state->wildcard_ids); - write_state->write_barrier = false; + ecs_query_op_t lookup = {0}; + lookup.kind = EcsQueryLookup; + lookup.src.var = evar; + lookup.first.var = base_var; + lookup.flags = (EcsQueryIsVar << EcsQuerySrc) | + (EcsQueryIsVar << EcsQueryFirst); + flecs_query_write_ctx(evar, ctx, cond_write); + flecs_query_write(evar, &lookup.written); + flecs_query_op_insert(&lookup, ctx); } static -bool flecs_pipeline_check_term( - ecs_world_t *world, - ecs_term_t *term, - bool is_active, - ecs_write_state_t *write_state) +void flecs_query_insert_unconstrained_transitive( + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx, + bool cond_write) { - (void)world; + /* Create anonymous variable to store the target ids. This will return the + * list of targets without constraining the variable of the term, which + * needs to stay variable to find all transitive relationships for a src. */ + ecs_var_id_t tgt = flecs_query_add_var(query, NULL, NULL, EcsVarEntity); + flecs_set_var_label(&query->vars[tgt], query->vars[op->second.var].name); - ecs_term_id_t *src = &term->src; - if (src->flags & EcsInOutNone) { - return false; - } + /* First, find ids to start traversal from. This fixes op.second. */ + ecs_query_op_t find_ids = {0}; + find_ids.kind = EcsQueryIdsRight; + find_ids.field_index = -1; + find_ids.first = op->first; + find_ids.second = op->second; + find_ids.flags = op->flags; + find_ids.flags &= (ecs_flags8_t)~((EcsQueryIsVar|EcsQueryIsEntity) << EcsQuerySrc); + find_ids.second.var = tgt; + flecs_query_write_ctx(tgt, ctx, cond_write); + flecs_query_write(tgt, &find_ids.written); + flecs_query_op_insert(&find_ids, ctx); - ecs_id_t id = term->id; - ecs_oper_kind_t oper = term->oper; - ecs_inout_kind_t inout = term->inout; - bool from_any = ecs_term_match_0(term); - bool from_this = ecs_term_match_this(term); - bool is_shared = !from_any && (!from_this || !(src->flags & EcsSelf)); + /* Next, iterate all tables for the ids. This fixes op.src */ + ecs_query_op_t and_op = {0}; + and_op.kind = EcsQueryAnd; + and_op.field_index = op->field_index; + and_op.first = op->first; + and_op.second = op->second; + and_op.src = op->src; + and_op.flags = op->flags | EcsQueryIsSelf; + and_op.second.var = tgt; + flecs_query_write_ctx(and_op.src.var, ctx, cond_write); + flecs_query_write(and_op.src.var, &and_op.written); + flecs_query_op_insert(&and_op, ctx); +} - ecs_write_kind_t ws = flecs_pipeline_get_write_state(write_state, id); +static +void flecs_query_insert_inheritance( + ecs_query_impl_t *query, + ecs_term_t *term, + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx, + bool cond_write) +{ + /* Anonymous variable to store the resolved component ids */ + ecs_var_id_t tvar = flecs_query_add_var(query, NULL, NULL, EcsVarTable); + ecs_var_id_t evar = flecs_query_add_var(query, NULL, NULL, EcsVarEntity); - if (from_this && ws >= WriteStateToStage) { - /* A staged write could have happened for an id that's matched on the - * main storage. Even if the id isn't read, still insert a merge so that - * a write to the main storage after the staged write doesn't get - * overwritten. */ - return true; - } + flecs_set_var_label(&query->vars[tvar], ecs_get_name(query->pub.world, + ECS_TERM_REF_ID(&term->first))); + flecs_set_var_label(&query->vars[evar], ecs_get_name(query->pub.world, + ECS_TERM_REF_ID(&term->first))); - if (inout == EcsInOutDefault) { - if (from_any) { - /* If no inout kind is specified for terms without a source, this is - * not interpreted as a read/write annotation but just a (component) - * id that's passed to a system. */ - return false; - } else if (is_shared) { - inout = EcsIn; - } else { - /* Default for owned terms is InOut */ - inout = EcsInOut; - } + ecs_query_op_t trav_op = {0}; + trav_op.kind = EcsQueryTrav; + trav_op.field_index = -1; + trav_op.first.entity = EcsIsA; + trav_op.second.entity = ECS_TERM_REF_ID(&term->first); + trav_op.src.var = tvar; + trav_op.flags = EcsQueryIsSelf; + trav_op.flags |= (EcsQueryIsEntity << EcsQueryFirst); + trav_op.flags |= (EcsQueryIsEntity << EcsQuerySecond); + trav_op.flags |= (EcsQueryIsVar << EcsQuerySrc); + trav_op.written |= (1ull << tvar); + if (term->first.id & EcsSelf) { + trav_op.match_flags |= EcsTermReflexive; } + flecs_query_op_insert(&trav_op, ctx); + flecs_query_insert_each(tvar, evar, ctx, cond_write); - if (oper == EcsNot && inout == EcsOut) { - /* If a Not term is combined with Out, it signals that the system - * intends to add a component that the entity doesn't yet have */ - from_any = true; + ecs_query_ref_t r = { .var = evar }; + op->first = r; + op->flags &= (ecs_flags8_t)~(EcsQueryIsEntity << EcsQueryFirst); + op->flags |= (EcsQueryIsVar << EcsQueryFirst); +} + +void flecs_query_compile_term_ref( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_term_ref_t *term_ref, + ecs_query_ref_t *ref, + ecs_flags8_t ref_kind, + ecs_var_kind_t kind, + ecs_query_compile_ctx_t *ctx, + bool create_wildcard_vars) +{ + (void)world; + + if (!ecs_term_ref_is_set(term_ref)) { + return; } - if (from_any) { - switch(inout) { - case EcsOut: - case EcsInOut: - if (is_active) { - /* Only flag component as written if system is active */ - flecs_pipeline_set_write_state(write_state, id); + if (term_ref->id & EcsIsVariable) { + op->flags |= (ecs_flags8_t)(EcsQueryIsVar << ref_kind); + const char *name = flecs_term_ref_var_name(term_ref); + if (name) { + ref->var = flecs_query_most_specific_var(query, name, kind, ctx); + ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + } else if (create_wildcard_vars) { + bool is_wildcard = flecs_term_ref_is_wildcard(term_ref); + if (is_wildcard && (kind == EcsVarAny)) { + ref->var = flecs_query_add_var(query, NULL, NULL, EcsVarTable); + } else { + ref->var = flecs_query_add_var(query, NULL, NULL, EcsVarEntity); } - break; - case EcsInOutDefault: - case EcsInOutNone: - case EcsIn: - break; - } - - switch(inout) { - case EcsIn: - case EcsInOut: - if (ws == WriteStateToStage) { - /* If a system does a get/get_mut, the component is fetched from - * the main store so it must be merged first */ - return true; + if (is_wildcard) { + flecs_set_var_label(&query->vars[ref->var], + ecs_get_name(world, ECS_TERM_REF_ID(term_ref))); } - /* fall through */ - case EcsInOutDefault: - case EcsInOutNone: - case EcsOut: - break; + ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); } } - return false; + if (term_ref->id & EcsIsEntity) { + op->flags |= (ecs_flags8_t)(EcsQueryIsEntity << ref_kind); + ref->entity = ECS_TERM_REF_ID(term_ref); + } } static -bool flecs_pipeline_check_terms( - ecs_world_t *world, - ecs_filter_t *filter, - bool is_active, - ecs_write_state_t *ws) +int flecs_query_compile_ensure_vars( + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + ecs_query_compile_ctx_t *ctx, + bool cond_write, + bool *written_out) { - bool needs_merge = false; - ecs_term_t *terms = filter->terms; - int32_t t, term_count = filter->term_count; + ecs_flags16_t flags = flecs_query_ref_flags(op->flags, ref_kind); + bool written = false; - /* Check This terms first. This way if a term indicating writing to a stage - * was added before the term, it won't cause merging. */ - for (t = 0; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; - if (ecs_term_match_this(term)) { - needs_merge |= flecs_pipeline_check_term(world, term, is_active, ws); + if (flags & EcsQueryIsVar) { + ecs_var_id_t var_id = ref->var; + ecs_query_var_t *var = &query->vars[var_id]; + + if (var->kind == EcsVarEntity && + !flecs_query_is_written(var_id, ctx->written)) + { + /* If entity variable is not yet written but a table variant exists + * that has been written, insert each operation to translate from + * entity variable to table */ + ecs_var_id_t tvar = var->table_id; + if ((tvar != EcsVarNone) && + flecs_query_is_written(tvar, ctx->written)) + { + if (var->lookup) { + if (!flecs_query_is_written(tvar, ctx->written)) { + ecs_err("dependent variable of '$%s' is not written", + var->name); + return -1; + } + + if (!flecs_query_is_written(var->base_id, ctx->written)) { + flecs_query_insert_each( + tvar, var->base_id, ctx, cond_write); + } + } else { + flecs_query_insert_each(tvar, var_id, ctx, cond_write); + } + + /* Variable was written, just not as entity */ + written = true; + } else if (var->lookup) { + if (!flecs_query_is_written(var->base_id, ctx->written)) { + ecs_err("dependent variable of '$%s' is not written", + var->name); + return -1; + } + } } + + written |= flecs_query_is_written(var_id, ctx->written); + } else { + /* If it's not a variable, it's always written */ + written = true; } - /* Now check staged terms */ - for (t = 0; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; - if (!ecs_term_match_this(term)) { - needs_merge |= flecs_pipeline_check_term(world, term, is_active, ws); - } + if (written_out) { + *written_out = written; } - return needs_merge; + return 0; } static -EcsPoly* flecs_pipeline_term_system( - ecs_iter_t *it) +bool flecs_query_compile_lookup( + ecs_query_impl_t *query, + ecs_var_id_t var_id, + ecs_query_compile_ctx_t *ctx, + bool cond_write) { - int32_t index = ecs_table_get_column_index( - it->real_world, it->table, ecs_poly_id(EcsSystem)); - ecs_assert(index != -1, ECS_INTERNAL_ERROR, NULL); - EcsPoly *poly = ecs_table_get_column(it->table, index, it->offset); - ecs_assert(poly != NULL, ECS_INTERNAL_ERROR, NULL); - return poly; + ecs_query_var_t *var = &query->vars[var_id]; + if (var->lookup) { + flecs_query_insert_lookup(var->base_id, var_id, ctx, cond_write); + return true; + } else { + return false; + } } static -bool flecs_pipeline_build( - ecs_world_t *world, - ecs_pipeline_state_t *pq) +void flecs_query_insert_contains( + ecs_query_impl_t *query, + ecs_var_id_t src_var, + ecs_var_id_t other_var, + ecs_query_compile_ctx_t *ctx) { - ecs_iter_t it = ecs_query_iter(world, pq->query); - - if (pq->match_count == pq->query->match_count) { - /* No need to rebuild the pipeline */ - ecs_iter_fini(&it); - return false; + ecs_query_op_t contains = {0}; + if ((src_var != other_var) && (src_var == query->vars[other_var].table_id)) { + contains.kind = EcsQueryContain; + contains.src.var = src_var; + contains.first.var = other_var; + contains.flags |= (EcsQueryIsVar << EcsQuerySrc) | + (EcsQueryIsVar << EcsQueryFirst); + flecs_query_op_insert(&contains, ctx); } +} - world->info.pipeline_build_count_total ++; - pq->rebuild_count ++; +static +void flecs_query_insert_pair_eq( + int32_t field_index, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t contains = {0}; + contains.kind = EcsQueryPairEq; + contains.field_index = flecs_ito(int8_t, field_index); + flecs_query_op_insert(&contains, ctx); +} - ecs_allocator_t *a = &world->allocator; - ecs_pipeline_op_t *op = NULL; - ecs_write_state_t ws = {0}; - ecs_map_init(&ws.ids, a); - ecs_map_init(&ws.wildcard_ids, a); +static +int flecs_query_compile_builtin_pred( + ecs_query_t *q, + ecs_term_t *term, + ecs_query_op_t *op, + ecs_write_flags_t write_state) +{ + ecs_entity_t id = ECS_TERM_REF_ID(&term->first); - ecs_vec_reset_t(a, &pq->ops, ecs_pipeline_op_t); - ecs_vec_reset_t(a, &pq->systems, ecs_entity_t); + ecs_query_op_kind_t eq[] = {EcsQueryPredEq, EcsQueryPredNeq}; + ecs_query_op_kind_t eq_name[] = {EcsQueryPredEqName, EcsQueryPredNeqName}; + ecs_query_op_kind_t eq_match[] = {EcsQueryPredEqMatch, EcsQueryPredNeqMatch}; + + ecs_flags16_t flags_src = flecs_query_ref_flags(op->flags, EcsQuerySrc); + ecs_flags16_t flags_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); - bool multi_threaded = false; - bool no_readonly = false; - bool first = true; + if (id == EcsPredEq) { + if (term->second.id & EcsIsName) { + op->kind = flecs_ito(uint8_t, eq_name[term->oper == EcsNot]); + } else { + op->kind = flecs_ito(uint8_t, eq[term->oper == EcsNot]); + } + } else if (id == EcsPredMatch) { + op->kind = flecs_ito(uint8_t, eq_match[term->oper == EcsNot]); + } - /* Iterate systems in pipeline, add ops for running / merging */ - while (ecs_query_next(&it)) { - EcsPoly *poly = flecs_pipeline_term_system(&it); - bool is_active = ecs_table_get_type_index(world, it.table, EcsEmpty) == -1; + if (flags_2nd & EcsQueryIsVar) { + if (!(write_state & (1ull << op->second.var))) { + ecs_err("uninitialized variable '%s' on right-hand side of " + "equality operator", ecs_query_var_name(q, op->second.var)); + return -1; + } + } - int32_t i; - for (i = 0; i < it.count; i ++) { - ecs_poly_assert(poly[i].poly, ecs_system_t); - ecs_system_t *sys = (ecs_system_t*)poly[i].poly; - ecs_query_t *q = sys->query; + ecs_assert(flags_src & EcsQueryIsVar, ECS_INTERNAL_ERROR, NULL); + (void)flags_src; - bool needs_merge = false; - needs_merge = flecs_pipeline_check_terms( - world, &q->filter, is_active, &ws); + if (!(write_state & (1ull << op->src.var))) { + /* If this is an == operator with a right-hand side that resolves to a + * single entity, the left-hand side is allowed to be undefined, as the + * instruction will be evaluated as an assignment. */ + if (op->kind != EcsQueryPredEq && op->kind != EcsQueryPredEqName) { + ecs_err("uninitialized variable '%s' on left-hand side of " + "equality operator", ecs_query_var_name(q, op->src.var)); + return -1; + } + } - if (is_active) { - if (first) { - multi_threaded = sys->multi_threaded; - no_readonly = sys->no_readonly; - first = false; - } + return 0; +} - if (sys->multi_threaded != multi_threaded) { - needs_merge = true; - multi_threaded = sys->multi_threaded; - } - if (sys->no_readonly != no_readonly) { - needs_merge = true; - no_readonly = sys->no_readonly; - } - } +static +int flecs_query_ensure_scope_var( + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + ecs_query_compile_ctx_t *ctx) +{ + ecs_var_id_t var = ref->var; - if (no_readonly) { - needs_merge = true; + if (query->vars[var].kind == EcsVarEntity && + !flecs_query_is_written(var, ctx->written)) + { + ecs_var_id_t table_var = query->vars[var].table_id; + if (table_var != EcsVarNone && + flecs_query_is_written(table_var, ctx->written)) + { + if (flecs_query_compile_ensure_vars( + query, op, ref, ref_kind, ctx, false, NULL)) + { + goto error; } + } + } - if (needs_merge) { - /* After merge all components will be merged, so reset state */ - flecs_pipeline_reset_write_state(&ws); - - /* An inactive system can insert a merge if one of its - * components got written, which could make the system - * active. If this is the only system in the pipeline operation, - * it results in an empty operation when we get here. If that's - * the case, reuse the empty operation for the next op. */ - if (op && op->count) { - op = NULL; - } - - /* Re-evaluate columns to set write flags if system is active. - * If system is inactive, it can't write anything and so it - * should not insert unnecessary merges. */ - needs_merge = false; - if (is_active) { - needs_merge = flecs_pipeline_check_terms( - world, &q->filter, true, &ws); - } - - /* The component states were just reset, so if we conclude that - * another merge is needed something is wrong. */ - ecs_assert(needs_merge == false, ECS_INTERNAL_ERROR, NULL); - } + return 0; +error: + return -1; +} - if (!op) { - op = ecs_vec_append_t(a, &pq->ops, ecs_pipeline_op_t); - op->offset = ecs_vec_count(&pq->systems); - op->count = 0; - op->multi_threaded = false; - op->no_readonly = false; - op->time_spent = 0; - op->commands_enqueued = 0; +static +int flecs_query_ensure_scope_vars( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_query_compile_ctx_t *ctx, + ecs_term_t *term) +{ + /* If the scope uses variables as entity that have only been written as + * table, resolve them as entities before entering the scope. */ + ecs_term_t *cur = term; + while(ECS_TERM_REF_ID(&cur->first) != EcsScopeClose) { + /* Dummy operation to obtain variable information for term */ + ecs_query_op_t op = {0}; + flecs_query_compile_term_ref(world, query, &op, &cur->first, + &op.first, EcsQueryFirst, EcsVarEntity, ctx, false); + flecs_query_compile_term_ref(world, query, &op, &cur->second, + &op.second, EcsQuerySecond, EcsVarEntity, ctx, false); + + if (op.flags & (EcsQueryIsVar << EcsQueryFirst)) { + if (flecs_query_ensure_scope_var( + query, &op, &op.first, EcsQueryFirst, ctx)) + { + goto error; } + } - /* Don't increase count for inactive systems, as they are ignored by - * the query used to run the pipeline. */ - if (is_active) { - ecs_vec_append_t(a, &pq->systems, ecs_entity_t)[0] = - it.entities[i]; - if (!op->count) { - op->multi_threaded = multi_threaded; - op->no_readonly = no_readonly; - } - op->count ++; + if (op.flags & (EcsQueryIsVar << EcsQuerySecond)) { + if (flecs_query_ensure_scope_var( + query, &op, &op.second, EcsQuerySecond, ctx)) + { + goto error; } } - } - if (op && !op->count && ecs_vec_count(&pq->ops) > 1) { - ecs_vec_remove_last(&pq->ops); + cur ++; } - ecs_map_fini(&ws.ids); - ecs_map_fini(&ws.wildcard_ids); + return 0; +error: + return -1; +} - op = ecs_vec_first_t(&pq->ops, ecs_pipeline_op_t); +static +void flecs_query_compile_push( + ecs_query_compile_ctx_t *ctx) +{ + ctx->cur = &ctx->ctrlflow[++ ctx->scope]; + ctx->cur->lbl_begin = -1; + ctx->cur->lbl_begin = -1; +} - if (!op) { - ecs_dbg("#[green]pipeline#[reset] is empty"); - return true; +static +void flecs_query_compile_pop( + ecs_query_compile_ctx_t *ctx) +{ + ctx->cur = &ctx->ctrlflow[-- ctx->scope]; +} + +static +int flecs_query_compile_0_src( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx) +{ + /* If the term has a 0 source, check if it's a scope open/close */ + if (ECS_TERM_REF_ID(&term->first) == EcsScopeOpen) { + if (flecs_query_ensure_scope_vars(world, impl, ctx, term)) { + goto error; + } + if (term->oper == EcsNot) { + ctx->scope_is_not |= (ecs_flags32_t)(1ull << ctx->scope); + flecs_query_begin_block(EcsQueryNot, ctx); + } else { + ctx->scope_is_not &= (ecs_flags32_t)~(1ull << ctx->scope); + } + flecs_query_compile_push(ctx); + } else if (ECS_TERM_REF_ID(&term->first) == EcsScopeClose) { + flecs_query_compile_pop(ctx); + if (ctx->scope_is_not & (ecs_flags32_t)(1ull << (ctx->scope))) { + flecs_query_end_block(ctx, false); + } } else { - /* Add schedule to debug tracing */ - ecs_dbg("#[bold]pipeline rebuild"); - ecs_log_push_1(); + /* Noop */ + } - ecs_dbg("#[green]schedule#[reset]: threading: %d, staging: %d:", - op->multi_threaded, !op->no_readonly); - ecs_log_push_1(); + return 0; +error: + return -1; +} - int32_t i, count = ecs_vec_count(&pq->systems); - int32_t op_index = 0, ran_since_merge = 0; - ecs_entity_t *systems = ecs_vec_first_t(&pq->systems, ecs_entity_t); - for (i = 0; i < count; i ++) { - ecs_entity_t system = systems[i]; - const EcsPoly *poly = ecs_get_pair(world, system, EcsPoly, EcsSystem); - ecs_poly_assert(poly->poly, ecs_system_t); - ecs_system_t *sys = (ecs_system_t*)poly->poly; +static +ecs_flags32_t flecs_query_to_table_flags( + const ecs_query_t *q) +{ + ecs_flags32_t query_flags = q->flags; + if (!(query_flags & EcsQueryMatchDisabled) || + !(query_flags & EcsQueryMatchPrefab)) + { + ecs_flags32_t table_flags = EcsTableNotQueryable; + if (!(query_flags & EcsQueryMatchDisabled)) { + table_flags |= EcsTableIsDisabled; + } + if (!(query_flags & EcsQueryMatchPrefab)) { + table_flags |= EcsTableIsPrefab; + } -#ifdef FLECS_LOG_1 - char *path = ecs_get_fullpath(world, system); - const char *doc_name = NULL; -#ifdef FLECS_DOC - const EcsDocDescription *doc_name_id = ecs_get_pair(world, system, - EcsDocDescription, EcsName); - if (doc_name_id) { - doc_name = doc_name_id->value; - } -#endif - if (doc_name) { - ecs_dbg("#[green]system#[reset] %s (%s)", path, doc_name); - } else { - ecs_dbg("#[green]system#[reset] %s", path); - } - ecs_os_free(path); -#endif + return table_flags; + } + return 0; +} - ecs_assert(op[op_index].offset + ran_since_merge == i, - ECS_INTERNAL_ERROR, NULL); +static +bool flecs_query_select_all( + const ecs_query_t *q, + ecs_term_t *term, + ecs_query_op_t *op, + ecs_var_id_t src_var, + ecs_query_compile_ctx_t *ctx) +{ + bool builtin_pred = flecs_term_is_builtin_pred(term); + bool pred_match = builtin_pred && ECS_TERM_REF_ID(&term->first) == EcsPredMatch; - ran_since_merge ++; - if (ran_since_merge == op[op_index].count) { - ecs_dbg("#[magenta]merge#[reset]"); - ecs_log_pop_1(); - ran_since_merge = 0; - op_index ++; - if (op_index < ecs_vec_count(&pq->ops)) { - ecs_dbg( - "#[green]schedule#[reset]: " - "threading: %d, staging: %d:", - op[op_index].multi_threaded, - !op[op_index].no_readonly); - } - ecs_log_push_1(); - } + if (term->oper == EcsNot || term->oper == EcsOptional || + term->oper == EcsNotFrom || pred_match) + { + ecs_query_op_t match_any = {0}; + match_any.kind = EcsAnd; + match_any.flags = EcsQueryIsSelf | (EcsQueryIsEntity << EcsQueryFirst); + match_any.flags |= (EcsQueryIsVar << EcsQuerySrc); + match_any.src = op->src; + match_any.field_index = -1; + if (!pred_match) { + match_any.first.entity = EcsAny; + } else { + /* If matching by name, instead of finding all tables, just find + * the ones with a name. */ + match_any.first.entity = ecs_id(EcsIdentifier); + match_any.second.entity = EcsName; + match_any.flags |= (EcsQueryIsEntity << EcsQuerySecond); + } + match_any.written = (1ull << src_var); + match_any.other = flecs_itolbl(flecs_query_to_table_flags(q)); + flecs_query_op_insert(&match_any, ctx); + flecs_query_write_ctx(op->src.var, ctx, false); + + /* Update write administration */ + return true; + } + return false; +} - if (sys->last_frame == (world->info.frame_count_total + 1)) { - if (op_index < ecs_vec_count(&pq->ops)) { - pq->cur_op = &op[op_index]; - pq->cur_i = i; - } else { - pq->cur_op = NULL; - pq->cur_i = 0; - } - } - } +#ifdef FLECS_META +static +int flecs_query_compile_begin_member_term( + ecs_world_t *world, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx, + ecs_entity_t first_id) +{ + ecs_assert(first_id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(first_id & EcsIsEntity, ECS_INTERNAL_ERROR, NULL); - ecs_log_pop_1(); - ecs_log_pop_1(); + first_id = ECS_TERM_REF_ID(&term->first); + + /* First compile as if it's a regular term, to match the component */ + term->flags_ &= (uint16_t)~EcsTermIsMember; + + /* Replace term id with member parent (the component) */ + ecs_entity_t component = ecs_get_parent(world, first_id); + if (!component) { + ecs_err("member without parent in query"); + return -1; } - pq->match_count = pq->query->match_count; + if (!ecs_has(world, component, EcsComponent)) { + ecs_err("parent of member is not a component"); + return -1; + } - ecs_assert(pq->cur_op <= ecs_vec_last_t(&pq->ops, ecs_pipeline_op_t), - ECS_INTERNAL_ERROR, NULL); + bool second_wildcard = + (ECS_TERM_REF_ID(&term->second) == EcsWildcard || + ECS_TERM_REF_ID(&term->second) == EcsAny) && + (term->second.id & EcsIsVariable) && !term->second.name; - return true; -} + term->first.id = component | ECS_TERM_REF_FLAGS(&term->first); + term->second.id = 0; + term->id = component; -static -void flecs_pipeline_next_system( - ecs_pipeline_state_t *pq) -{ - if (!pq->cur_op) { - return; + ctx->oper = (ecs_oper_kind_t)term->oper; + if (term->oper == EcsNot && !second_wildcard) { + /* When matching a member term with not operator, we need to cover both + * the case where an entity doesn't have the component, and where it + * does have the component, but doesn't match the member. */ + term->oper = EcsOptional; } - pq->cur_i ++; - if (pq->cur_i >= (pq->cur_op->offset + pq->cur_op->count)) { - pq->cur_op ++; - if (pq->cur_op > ecs_vec_last_t(&pq->ops, ecs_pipeline_op_t)) { - pq->cur_op = NULL; - } - } + return 0; } -bool flecs_pipeline_update( +static +int flecs_query_compile_end_member_term( ecs_world_t *world, - ecs_pipeline_state_t *pq, - bool start_of_frame) + ecs_query_impl_t *impl, + ecs_query_op_t *op, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx, + ecs_id_t term_id, + ecs_entity_t first_id, + ecs_entity_t second_id, + bool cond_write) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); + ecs_entity_t component = ECS_TERM_REF_ID(&term->first); + const EcsComponent *comp = ecs_get(world, component, EcsComponent); + ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); - /* If any entity mutations happened that could have affected query matching - * notify appropriate queries so caches are up to date. This includes the - * pipeline query. */ - if (start_of_frame) { - ecs_run_aperiodic(world, 0); - } + /* Restore term values */ + term->id = term_id; + term->first.id = first_id; + term->second.id = second_id; + term->flags_ |= EcsTermIsMember; + term->oper = flecs_ito(int16_t, ctx->oper); + + first_id = ECS_TERM_REF_ID(&term->first); + const EcsMember *member = ecs_get(world, first_id, EcsMember); + ecs_assert(member != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_query_var_t *var = &impl->vars[op->src.var]; + const char *var_name = flecs_term_ref_var_name(&term->src); + ecs_var_id_t evar = flecs_query_find_var_id( + impl, var_name, EcsVarEntity); + + bool second_wildcard = + (ECS_TERM_REF_ID(&term->second) == EcsWildcard || + ECS_TERM_REF_ID(&term->second) == EcsAny) && + (term->second.id & EcsIsVariable) && !term->second.name; + + if (term->oper == EcsOptional) { + second_wildcard = true; + } + + ecs_query_op_t mbr_op = *op; + mbr_op.kind = EcsQueryMemberEq; + mbr_op.first.entity = /* Encode type size and member offset */ + flecs_ito(uint32_t, member->offset) | + (flecs_ito(uint64_t, comp->size) << 32); + + /* If this is a term with a Not operator, conditionally evaluate member on + * whether term was set by previous operation (see begin_member_term). */ + if (ctx->oper == EcsNot || ctx->oper == EcsOptional) { + if (second_wildcard && ctx->oper == EcsNot) { + /* A !(T.value, *) term doesn't need special operations */ + return 0; + } - ecs_assert(pq != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(pq->query != NULL, ECS_INTERNAL_ERROR, NULL); + /* Resolve to entity variable before entering if block, so that we + * don't have different branches of the query working with different + * versions of the same variable. */ + if (var->kind == EcsVarTable) { + flecs_query_insert_each(op->src.var, evar, ctx, cond_write); + var = &impl->vars[evar]; + } - bool rebuilt = flecs_pipeline_build(world, pq); - if (start_of_frame) { - /* Initialize iterators */ - int32_t i, count = pq->iter_count; - for (i = 0; i < count; i ++) { - ecs_world_t *stage = ecs_get_stage(world, i); - pq->iters[i] = ecs_query_iter(stage, pq->query); + ecs_query_op_t *if_op = flecs_query_begin_block(EcsQueryIfSet, ctx); + if_op->other = term->field_index; + + if (ctx->oper == EcsNot) { + mbr_op.kind = EcsQueryMemberNeq; } - pq->cur_op = ecs_vec_first_t(&pq->ops, ecs_pipeline_op_t); - pq->cur_i = 0; + } + + if (var->kind == EcsVarTable) { + /* If MemberEq is called on table variable, store it on .other member. + * This causes MemberEq to do double duty as 'each' instruction, + * which is faster than having to go back & forth between instructions + * while finding matching values. */ + mbr_op.other = flecs_itolbl(op->src.var + 1); + + /* Mark entity variable as written */ + flecs_query_write_ctx(evar, ctx, cond_write); + flecs_query_write(evar, &mbr_op.written); + } + + flecs_query_compile_term_ref(world, impl, &mbr_op, &term->src, + &mbr_op.src, EcsQuerySrc, EcsVarEntity, ctx, true); + + if (second_wildcard) { + mbr_op.flags |= (EcsQueryIsEntity << EcsQuerySecond); + mbr_op.second.entity = EcsWildcard; } else { - flecs_pipeline_next_system(pq); + flecs_query_compile_term_ref(world, impl, &mbr_op, &term->second, + &mbr_op.second, EcsQuerySecond, EcsVarEntity, ctx, true); + + if (term->second.id & EcsIsVariable) { + if (flecs_query_compile_ensure_vars(impl, &mbr_op, &mbr_op.second, + EcsQuerySecond, ctx, cond_write, NULL)) + { + goto error; + } + + flecs_query_write_ctx(mbr_op.second.var, ctx, cond_write); + flecs_query_write(mbr_op.second.var, &mbr_op.written); + } } - return rebuilt; + flecs_query_op_insert(&mbr_op, ctx); + + if (ctx->oper == EcsNot || ctx->oper == EcsOptional) { + flecs_query_end_block(ctx, false); + } + + return 0; +error: + return -1; +} +#else +static +int flecs_query_compile_begin_member_term( + ecs_world_t *world, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx, + ecs_entity_t first_id) +{ + (void)world; (void)term; (void)ctx; (void)first_id; + return 0; } -void ecs_run_pipeline( +static +int flecs_query_compile_end_member_term( ecs_world_t *world, - ecs_entity_t pipeline, - ecs_ftime_t delta_time) + ecs_query_impl_t *impl, + ecs_query_op_t *op, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx, + ecs_id_t term_id, + ecs_entity_t first_id, + ecs_entity_t second_id, + bool cond_write) { - if (!pipeline) { - pipeline = world->pipeline; - } + (void)world; (void)impl; (void)op; (void)term; (void)ctx; (void)term_id; + (void)first_id; (void)second_id; (void)cond_write; + return 0; +} +#endif - /* create any worker task threads request */ - if (ecs_using_task_threads(world)) { - flecs_create_worker_threads(world); - } +static +void flecs_query_mark_last_or_op( + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t *op_ptr = ecs_vec_last_t(ctx->ops, ecs_query_op_t); + op_ptr->next = FlecsRuleOrMarker; +} - EcsPipeline *p = - ECS_CONST_CAST(EcsPipeline*, ecs_get(world, pipeline, EcsPipeline)); - flecs_workers_progress(world, p->state, delta_time); +static +void flecs_query_set_op_kind( + ecs_query_op_t *op, + ecs_term_t *term, + bool src_is_var) +{ + /* Default instruction for And operators. If the source is fixed (like for + * singletons or terms with an entity source), use With, which like And but + * just matches against a source (vs. finding a source). */ + op->kind = src_is_var ? EcsQueryAnd : EcsQueryWith; + + /* Ignore cascade flag */ + ecs_entity_t trav_flags = EcsTraverseFlags & ~(EcsCascade|EcsDesc); + + /* Handle *From operators */ + if (term->oper == EcsAndFrom) { + op->kind = EcsQueryAndFrom; + } else if (term->oper == EcsOrFrom) { + op->kind = EcsQueryOrFrom; + } else if (term->oper == EcsNotFrom) { + op->kind = EcsQueryNotFrom; + + /* If query is transitive, use Trav(ersal) instruction */ + } else if (term->flags_ & EcsTermTransitive) { + ecs_assert(ecs_term_ref_is_set(&term->second), ECS_INTERNAL_ERROR, NULL); + op->kind = EcsQueryTrav; + + /* If term queries for union pair, use union instruction */ + } else if (term->flags_ & EcsTermIsUnion) { + if (op->kind == EcsQueryAnd) { + op->kind = EcsQueryUnionEq; + if (term->oper == EcsNot) { + if (!ecs_id_is_wildcard(ECS_TERM_REF_ID(&term->second))) { + term->oper = EcsAnd; + op->kind = EcsQueryUnionNeq; + } + } + } else { + op->kind = EcsQueryUnionEqWith; + } - if (ecs_using_task_threads(world)) { - /* task threads were temporary and may now be joined */ - flecs_join_worker_threads(world); + if ((term->src.id & trav_flags) == EcsUp) { + if (op->kind == EcsQueryUnionEq) { + op->kind = EcsQueryUnionEqUp; + } + } else if ((term->src.id & trav_flags) == (EcsSelf|EcsUp)) { + if (op->kind == EcsQueryUnionEq) { + op->kind = EcsQueryUnionEqSelfUp; + } + } + } else { + if ((term->src.id & trav_flags) == EcsUp) { + op->kind = EcsQueryUp; + } else if ((term->src.id & trav_flags) == (EcsSelf|EcsUp)) { + op->kind = EcsQuerySelfUp; + } else if (term->flags_ & (EcsTermMatchAny|EcsTermMatchAnySrc)) { + op->kind = EcsQueryAndAny; + } } } -int32_t flecs_run_pipeline_ops( - ecs_world_t* world, - ecs_stage_t* stage, - int32_t stage_index, - int32_t stage_count, - ecs_ftime_t delta_time) +int flecs_query_compile_term( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx) { - ecs_pipeline_state_t* pq = world->pq; - ecs_pipeline_op_t* op = pq->cur_op; - int32_t i = pq->cur_i; + ecs_id_t term_id = term->id; + ecs_entity_t first_id = term->first.id; + ecs_entity_t second_id = term->second.id; + bool toggle_term = (term->flags_ & EcsTermIsToggle) != 0; + bool member_term = (term->flags_ & EcsTermIsMember) != 0; + if (member_term) { + flecs_query_compile_begin_member_term(world, term, ctx, first_id); + } + + ecs_query_t *q = &query->pub; + bool first_term = term == q->terms; + bool first_is_var = term->first.id & EcsIsVariable; + bool second_is_var = term->second.id & EcsIsVariable; + bool src_is_var = term->src.id & EcsIsVariable; + bool src_is_wildcard = src_is_var && + (ECS_TERM_REF_ID(&term->src) == EcsWildcard || + ECS_TERM_REF_ID(&term->src) == EcsAny); + bool src_is_lookup = false; + bool builtin_pred = flecs_term_is_builtin_pred(term); + bool is_optional = (term->oper == EcsOptional); + bool is_or = flecs_term_is_or(q, term); + bool first_or = false, last_or = false; + bool cond_write = term->oper == EcsOptional || is_or; + ecs_query_op_t op = {0}; - ecs_assert(!stage_index || op->multi_threaded, ECS_INTERNAL_ERROR, NULL); + if (is_or) { + first_or = first_term || (term[-1].oper != EcsOr); + last_or = term->oper != EcsOr; + } - int32_t count = ecs_vec_count(&pq->systems); - ecs_entity_t* systems = ecs_vec_first_t(&pq->systems, ecs_entity_t); - int32_t ran_since_merge = i - op->offset; + if (term->oper == EcsAndFrom || term->oper == EcsOrFrom || + term->oper == EcsNotFrom) + { + const ecs_type_t *type = ecs_get_type(world, term->id); + if (!type) { + /* Empty type for id in *From operation is a noop */ + ctx->skipped ++; + return 0; + } - for (; i < count; i++) { - ecs_entity_t system = systems[i]; - const EcsPoly* poly = ecs_get_pair(world, system, EcsPoly, EcsSystem); - ecs_poly_assert(poly->poly, ecs_system_t); - ecs_system_t* sys = (ecs_system_t*)poly->poly; + int32_t i, count = type->count; + ecs_id_t *ti_ids = type->array; - /* Keep track of the last frame for which the system has ran, so we - * know from where to resume the schedule in case the schedule - * changes during a merge. */ - sys->last_frame = world->info.frame_count_total + 1; + for (i = 0; i < count; i ++) { + ecs_id_t ti_id = ti_ids[i]; + ecs_id_record_t *idr = flecs_id_record_get(world, ti_id); + if (!(idr->flags & EcsIdOnInstantiateDontInherit)) { + break; + } + } - ecs_stage_t* s = NULL; - if (!op->no_readonly) { - /* If system is no_readonly it operates on the actual world, not - * the stage. Only pass stage to system if it's readonly. */ - s = stage; + if (i == count) { + /* Type did not contain any ids to perform operation on */ + ctx->skipped ++; + return 0; } + } - ecs_run_intern(world, s, system, sys, stage_index, - stage_count, delta_time, 0, 0, NULL); + /* !_ (don't match anything) terms always return nothing. */ + if (term->oper == EcsNot && term->id == EcsAny) { + op.kind = EcsQueryNothing; + flecs_query_op_insert(&op, ctx); + return 0; + } - world->info.systems_ran_frame++; - ran_since_merge++; + if (first_or) { + ctx->ctrlflow->cond_written_or = ctx->cond_written; + ctx->ctrlflow->in_or = true; + } else if (is_or) { + ctx->written = ctx->ctrlflow->written_or; + } - if (ran_since_merge == op->count) { - /* Merge */ - break; + if (!ECS_TERM_REF_ID(&term->src) && term->src.id & EcsIsEntity) { + if (flecs_query_compile_0_src(world, query, term, ctx)) { + goto error; } + return 0; } - return i; -} + if (builtin_pred) { + ecs_entity_t id_noflags = ECS_TERM_REF_ID(&term->second); + if (id_noflags == EcsWildcard || id_noflags == EcsAny) { + /* Noop */ + return 0; + } + } -void flecs_run_pipeline( - ecs_world_t *world, - ecs_pipeline_state_t *pq, - ecs_ftime_t delta_time) -{ - ecs_assert(world != NULL, ECS_INVALID_OPERATION, NULL); - ecs_assert(pq != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(pq->query != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_poly_assert(world, ecs_stage_t); + op.field_index = flecs_ito(int8_t, term->field_index); + op.term_index = flecs_ito(int8_t, term - q->terms); - ecs_stage_t *stage = flecs_stage_from_world(&world); - int32_t stage_index = ecs_get_stage_id(stage->thread_ctx); - int32_t stage_count = ecs_get_stage_count(world); + flecs_query_set_op_kind(&op, term, src_is_var); - ecs_assert(!stage_index, ECS_INVALID_OPERATION, NULL); + bool is_not = (term->oper == EcsNot) && !builtin_pred; - bool multi_threaded = ecs_get_stage_count(world) > 1; + /* Save write state at start of term so we can use it to reliably track + * variables got written by this term. */ + ecs_write_flags_t cond_write_state = ctx->cond_written; - // Update the pipeline the workers will execute - world->pq = pq; + /* Resolve variables and entities for operation arguments */ + flecs_query_compile_term_ref(world, query, &op, &term->first, + &op.first, EcsQueryFirst, EcsVarEntity, ctx, true); + flecs_query_compile_term_ref(world, query, &op, &term->second, + &op.second, EcsQuerySecond, EcsVarEntity, ctx, true); + flecs_query_compile_term_ref(world, query, &op, &term->src, + &op.src, EcsQuerySrc, EcsVarAny, ctx, true); + + bool src_written = true; + if (src_is_var) { + src_is_lookup = query->vars[op.src.var].lookup != NULL; + src_written = flecs_query_is_written(op.src.var, ctx->written); + } - // Update the pipeline before waking the workers. - flecs_pipeline_update(world, pq, true); + /* Insert each instructions for table -> entity variable if needed */ + bool first_written, second_written; + if (flecs_query_compile_ensure_vars( + query, &op, &op.first, EcsQueryFirst, ctx, cond_write, &first_written)) + { + goto error; + } - // If there are no operations to execute in the pipeline bail early, - // no need to wake the workers since they have nothing to do. - while (pq->cur_op != NULL) { - if (pq->cur_i == ecs_vec_count(&pq->systems)) { - flecs_pipeline_update(world, pq, false); - continue; - } + if (flecs_query_compile_ensure_vars( + query, &op, &op.second, EcsQuerySecond, ctx, cond_write, &second_written)) + { + goto error; + } - bool no_readonly = pq->cur_op->no_readonly; - bool op_multi_threaded = multi_threaded && pq->cur_op->multi_threaded; + /* Store write state of variables for first OR term in chain which will get + * restored for the other terms in the chain, so that all OR terms make the + * same assumptions about which variables were already written. */ + if (first_or) { + ctx->ctrlflow->written_or = ctx->written; + } - pq->no_readonly = no_readonly; + /* If an optional or not term is inserted for a source that's not been + * written to yet, insert instruction that selects all entities so we have + * something to match the optional/not against. */ + if (src_is_var && !src_written && !src_is_wildcard && !src_is_lookup) { + src_written = flecs_query_select_all(q, term, &op, op.src.var, ctx); + } - if (!no_readonly) { - ecs_readonly_begin(world); + /* A bit of special logic for OR expressions and equality predicates. If the + * left-hand of an equality operator is a table, and there are multiple + * operators in an Or expression, the Or chain should match all entities in + * the table that match the right hand sides of the operator expressions. + * For this to work, the src variable needs to be resolved as entity, as an + * Or chain would otherwise only yield the first match from a table. */ + if (src_is_var && src_written && (builtin_pred || member_term) && term->oper == EcsOr) { + if (query->vars[op.src.var].kind == EcsVarTable) { + flecs_query_compile_term_ref(world, query, &op, &term->src, + &op.src, EcsQuerySrc, EcsVarEntity, ctx, true); + ctx->ctrlflow->written_or |= (1llu << op.src.var); } + } - ECS_BIT_COND(world->flags, EcsWorldMultiThreaded, op_multi_threaded); - ecs_assert(world->workers_waiting == 0, ECS_INTERNAL_ERROR, NULL); + if (flecs_query_compile_ensure_vars( + query, &op, &op.src, EcsQuerySrc, ctx, cond_write, NULL)) + { + goto error; + } - if (op_multi_threaded) { - flecs_signal_workers(world); + /* If source is Any (_) and first and/or second are unconstrained, insert an + * ids instruction instead of an And */ + if (term->flags_ & EcsTermMatchAnySrc) { + op.kind = EcsQueryIds; + /* Use up-to-date written values after potentially inserting each */ + if (!first_written || !second_written) { + if (!first_written) { + /* If first is unknown, traverse left: <- (*, t) */ + if (ECS_TERM_REF_ID(&term->first) != EcsAny) { + op.kind = EcsQueryIdsLeft; + } + } else { + /* If second is wildcard, traverse right: (r, *) -> */ + if (ECS_TERM_REF_ID(&term->second) != EcsAny) { + op.kind = EcsQueryIdsRight; + } + } + op.src.entity = 0; + src_is_var = false; + op.flags &= (ecs_flags8_t)~(EcsQueryIsVar << EcsQuerySrc); /* ids has no src */ + op.flags &= (ecs_flags8_t)~(EcsQueryIsEntity << EcsQuerySrc); } - ecs_time_t st = { 0 }; - bool measure_time = world->flags & EcsWorldMeasureSystemTime; - if (measure_time) { - ecs_time_measure(&st); + /* If source variable is not written and we're querying just for Any, insert + * a dedicated instruction that uses the Any record in the id index. Any + * queries that are evaluated against written sources can use Wildcard + * records, which is what the AndAny instruction does. */ + } else if (!src_written && term->id == EcsAny && op.kind == EcsQueryAndAny) { + /* Lookup variables ($var.child_name) are always written */ + if (!src_is_lookup) { + op.kind = EcsQueryOnlyAny; /* Uses Any (_) id record */ } + } - const int32_t i = flecs_run_pipeline_ops( - world, stage, stage_index, stage_count, delta_time); - - if (measure_time) { - /* Don't include merge time in system time */ - world->info.system_time_total += (ecs_ftime_t)ecs_time_measure(&st); + /* If this is a transitive term and both the target and source are unknown, + * find the targets for the relationship first. This clusters together + * tables for the same target, which allows for more efficient usage of the + * traversal caches. */ + if (term->flags_ & EcsTermTransitive && src_is_var && second_is_var) { + if (!src_written && !second_written) { + flecs_query_insert_unconstrained_transitive( + query, &op, ctx, cond_write); } + } - if (op_multi_threaded) { - flecs_wait_for_sync(world); + /* Check if this term has variables that have been conditionally written, + * like variables written by an optional term. */ + if (ctx->cond_written) { + if (!is_or || first_or) { + flecs_query_begin_block_cond_eval(&op, ctx, cond_write_state); } + } - if (!no_readonly) { - ecs_time_t mt = { 0 }; - if (measure_time) { - ecs_time_measure(&mt); - } + /* If term can toggle and is Not, change operator to Optional as we + * have to match entities that have the component but disabled. */ + if (toggle_term && is_not) { + is_not = false; + is_optional = true; + } - int32_t si; - for (si = 0; si < stage_count; si ++) { - ecs_stage_t *s = &world->stages[si]; - pq->cur_op->commands_enqueued += ecs_vec_count(&s->cmd->queue); - } + /* Handle Not, Optional, Or operators */ + if (is_not) { + flecs_query_begin_block(EcsQueryNot, ctx); + } else if (is_optional) { + flecs_query_begin_block(EcsQueryOptional, ctx); + } else if (first_or) { + flecs_query_begin_block_or(&op, term, ctx); + } - ecs_readonly_end(world); - if (measure_time) { - pq->cur_op->time_spent += ecs_time_measure(&mt); - } + /* If term has component inheritance enabled, insert instruction to walk + * down the relationship tree of the id. */ + if (term->flags_ & EcsTermIdInherited) { + flecs_query_insert_inheritance(query, term, &op, ctx, cond_write); + } + + op.match_flags = term->flags_; + + ecs_write_flags_t write_state = ctx->written; + if (first_is_var) { + op.flags &= (ecs_flags8_t)~(EcsQueryIsEntity << EcsQueryFirst); + op.flags |= (EcsQueryIsVar << EcsQueryFirst); + } + + if (term->src.id & EcsSelf) { + op.flags |= EcsQueryIsSelf; + } + + /* Insert instructions for lookup variables */ + if (first_is_var) { + if (flecs_query_compile_lookup(query, op.first.var, ctx, cond_write)) { + write_state |= (1ull << op.first.var); // lookups are resolved inline + } + } + if (src_is_var) { + if (flecs_query_compile_lookup(query, op.src.var, ctx, cond_write)) { + write_state |= (1ull << op.src.var); // lookups are resolved inline + } + } + if (second_is_var) { + if (flecs_query_compile_lookup(query, op.second.var, ctx, cond_write)) { + write_state |= (1ull << op.second.var); // lookups are resolved inline } + } - /* Store the current state of the schedule after we synchronized the - * threads, to avoid race conditions. */ - pq->cur_i = i; + if (builtin_pred) { + if (flecs_query_compile_builtin_pred(q, term, &op, write_state)) { + goto error; + } + } - flecs_pipeline_update(world, pq, false); + /* If we're writing the $this variable, filter out disabled/prefab entities + * unless the query explicitly matches them. + * This could've been done with regular With instructions, but since + * filtering out disabled/prefab entities is the default and this check is + * cheap to perform on table flags, it's worth special casing. */ + if (!src_written && op.src.var == 0) { + op.other = flecs_itolbl(flecs_query_to_table_flags(q)); } -} -static -void flecs_run_startup_systems( - ecs_world_t *world) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_dependson(EcsOnStart)); - if (!idr || !flecs_table_cache_count(&idr->cache)) { - /* Don't bother creating startup pipeline if no systems exist */ - return; + /* After evaluating a term, a used variable is always written */ + if (src_is_var) { + flecs_query_write(op.src.var, &op.written); + flecs_query_write_ctx(op.src.var, ctx, cond_write); + } + if (first_is_var) { + flecs_query_write(op.first.var, &op.written); + flecs_query_write_ctx(op.first.var, ctx, cond_write); + } + if (second_is_var) { + flecs_query_write(op.second.var, &op.written); + flecs_query_write_ctx(op.second.var, ctx, cond_write); } + + flecs_query_op_insert(&op, ctx); - ecs_dbg_2("#[bold]startup#[reset]"); - ecs_log_push_2(); - int32_t stage_count = world->stage_count; - world->stage_count = 1; /* Prevents running startup systems on workers */ + ctx->cur->lbl_query = flecs_itolbl(ecs_vec_count(ctx->ops) - 1); + if (is_or && !member_term) { + flecs_query_mark_last_or_op(ctx); + } - /* Creating a pipeline is relatively expensive, but this only happens - * for the first frame. The startup pipeline is deleted afterwards, which - * eliminates the overhead of keeping its query cache in sync. */ - ecs_dbg_2("#[bold]create startup pipeline#[reset]"); - ecs_log_push_2(); - ecs_entity_t start_pip = ecs_pipeline_init(world, &(ecs_pipeline_desc_t){ - .query = { - .filter.terms = { - { .id = EcsSystem }, - { .id = EcsPhase, .src.flags = EcsCascade, .src.trav = EcsDependsOn }, - { .id = ecs_dependson(EcsOnStart), .src.trav = EcsDependsOn }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsDependsOn, .oper = EcsNot }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsChildOf, .oper = EcsNot } - }, - .order_by = flecs_entity_compare + /* Handle self-references between src and first/second variables */ + if (src_is_var) { + if (first_is_var) { + flecs_query_insert_contains(query, op.src.var, op.first.var, ctx); } - }); - ecs_log_pop_2(); + if (second_is_var && op.first.var != op.second.var) { + flecs_query_insert_contains(query, op.src.var, op.second.var, ctx); + } + } - /* Run & delete pipeline */ - ecs_dbg_2("#[bold]run startup systems#[reset]"); - ecs_log_push_2(); - ecs_assert(start_pip != 0, ECS_INTERNAL_ERROR, NULL); - const EcsPipeline *p = ecs_get(world, start_pip, EcsPipeline); - ecs_check(p != NULL, ECS_INVALID_OPERATION, NULL); - flecs_workers_progress(world, p->state, 0); - ecs_log_pop_2(); + /* Handle self references between first and second variables */ + if (!ecs_id_is_wildcard(first_id)) { + if (first_is_var && !first_written && (op.first.var == op.second.var)) { + flecs_query_insert_pair_eq(term->field_index, ctx); + } + } - ecs_dbg_2("#[bold]delete startup pipeline#[reset]"); - ecs_log_push_2(); - ecs_delete(world, start_pip); - ecs_log_pop_2(); + /* Handle closing of Not, Optional and Or operators */ + if (is_not) { + flecs_query_end_block(ctx, true); + } else if (is_optional) { + flecs_query_end_block(ctx, true); + } - world->stage_count = stage_count; - ecs_log_pop_2(); + /* Now that the term is resolved, evaluate member of component */ + if (member_term) { + flecs_query_compile_end_member_term(world, query, &op, term, ctx, + term_id, first_id, second_id, cond_write); + if (is_or) { + flecs_query_mark_last_or_op(ctx); + } + } + + if (last_or) { + flecs_query_end_block_or(query, ctx); + } + + /* Handle closing of conditional evaluation */ + if (ctx->cur->lbl_cond_eval && (first_is_var || second_is_var || src_is_var)) { + if (!is_or || last_or) { + flecs_query_end_block_cond_eval(ctx); + } + } + /* Ensure that term id is set after evaluating Not */ + if (term->flags_ & EcsTermIdInherited) { + if (is_not) { + ecs_query_op_t set_id = {0}; + set_id.kind = EcsQuerySetId; + set_id.first.entity = term->id; + set_id.flags = (EcsQueryIsEntity << EcsQueryFirst); + set_id.field_index = flecs_ito(int8_t, term->field_index); + flecs_query_op_insert(&set_id, ctx); + } + } + + return 0; error: - return; + return -1; } -bool ecs_progress( - ecs_world_t *world, - ecs_ftime_t user_delta_time) +/** + * @file query/engine/cache.c + * @brief Cached query implementation. + */ + + +int32_t flecs_query_cache_table_count( + ecs_query_cache_t *cache) { - ecs_ftime_t delta_time = ecs_frame_begin(world, user_delta_time); + ecs_run_aperiodic(cache->query->world, EcsAperiodicEmptyTables); + return cache->cache.tables.count; +} + +int32_t flecs_query_cache_empty_table_count( + ecs_query_cache_t *cache) +{ + ecs_run_aperiodic(cache->query->world, EcsAperiodicEmptyTables); + return cache->cache.empty_tables.count; +} + +int32_t flecs_query_cache_entity_count( + const ecs_query_cache_t *cache) +{ + ecs_run_aperiodic(cache->query->world, EcsAperiodicEmptyTables); - /* If this is the first frame, run startup systems */ - if (world->info.frame_count_total == 0) { - flecs_run_startup_systems(world); + int32_t result = 0; + ecs_table_cache_hdr_t *cur, *last = cache->cache.tables.last; + if (!last) { + return 0; } - /* create any worker task threads request */ - if (ecs_using_task_threads(world)) { - flecs_create_worker_threads(world); + for (cur = cache->cache.tables.first; cur != NULL; cur = cur->next) { + result += ecs_table_count(cur->table); } - ecs_dbg_3("#[bold]progress#[reset](dt = %.2f)", (double)delta_time); - ecs_log_push_3(); - const EcsPipeline *p = ecs_get(world, world->pipeline, EcsPipeline); - ecs_check(p != NULL, ECS_INVALID_OPERATION, NULL); - flecs_workers_progress(world, p->state, delta_time); - ecs_log_pop_3(); - - ecs_frame_end(world); + return result; +} - if (ecs_using_task_threads(world)) { - /* task threads were temporary and may now be joined */ - flecs_join_worker_threads(world); +static +uint64_t flecs_query_cache_get_group_id( + ecs_query_cache_t *cache, + ecs_table_t *table) +{ + if (cache->group_by_callback) { + return cache->group_by_callback(cache->query->world, table, + cache->group_by, cache->group_by_ctx); + } else { + return 0; } +} - return !ECS_BIT_IS_SET(world->flags, EcsWorldQuit); -error: - return false; +static +void flecs_query_cache_compute_group_id( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) +{ + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); + + if (cache->group_by_callback) { + ecs_table_t *table = match->table; + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + + match->group_id = flecs_query_cache_get_group_id(cache, table); + } else { + match->group_id = 0; + } } -void ecs_set_time_scale( - ecs_world_t *world, - ecs_ftime_t scale) +static +ecs_query_cache_table_list_t* flecs_query_cache_get_group( + const ecs_query_cache_t *cache, + uint64_t group_id) { - world->info.time_scale = scale; + return ecs_map_get_deref( + &cache->groups, ecs_query_cache_table_list_t, group_id); } -void ecs_reset_clock( - ecs_world_t *world) +static +ecs_query_cache_table_list_t* flecs_query_cache_ensure_group( + ecs_query_cache_t *cache, + uint64_t id) { - world->info.world_time_total = 0; - world->info.world_time_total_raw = 0; + ecs_query_cache_table_list_t *group = ecs_map_get_deref(&cache->groups, + ecs_query_cache_table_list_t, id); + + if (!group) { + group = ecs_map_insert_alloc_t(&cache->groups, + ecs_query_cache_table_list_t, id); + ecs_os_zeromem(group); + if (cache->on_group_create) { + group->info.ctx = cache->on_group_create( + cache->query->world, id, cache->group_by_ctx); + } + } + + return group; } -void ecs_set_pipeline( - ecs_world_t *world, - ecs_entity_t pipeline) +static +void flecs_query_cache_remove_group( + ecs_query_cache_t *cache, + uint64_t id) { - ecs_poly_assert(world, ecs_world_t); - ecs_check( ecs_get(world, pipeline, EcsPipeline) != NULL, - ECS_INVALID_PARAMETER, "not a pipeline"); + if (cache->on_group_delete) { + ecs_query_cache_table_list_t *group = ecs_map_get_deref(&cache->groups, + ecs_query_cache_table_list_t, id); + if (group) { + cache->on_group_delete(cache->query->world, id, + group->info.ctx, cache->group_by_ctx); + } + } - world->pipeline = pipeline; -error: - return; + ecs_map_remove_free(&cache->groups, id); } -ecs_entity_t ecs_get_pipeline( - const ecs_world_t *world) +static +uint64_t flecs_query_cache_default_group_by( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + void *ctx) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - world = ecs_get_world(world); - return world->pipeline; -error: + (void)ctx; + + ecs_id_t match; + if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &match) != -1) { + return ecs_pair_second(world, match); + } return 0; } -ecs_entity_t ecs_pipeline_init( - ecs_world_t *world, - const ecs_pipeline_desc_t *desc) +/* Find the last node of the group after which this group should be inserted */ +static +ecs_query_cache_table_match_t* flecs_query_cache_find_group_insertion_node( + ecs_query_cache_t *cache, + uint64_t group_id) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + /* Grouping must be enabled */ + ecs_assert(cache->group_by_callback != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t result = desc->entity; - if (!result) { - result = ecs_new(world, 0); + ecs_map_iter_t it = ecs_map_iter(&cache->groups); + ecs_query_cache_table_list_t *list, *closest_list = NULL; + uint64_t id, closest_id = 0; + + bool desc = false; + + if (cache->cascade_by) { + desc = (cache->query->terms[ + cache->cascade_by - 1].src.id & EcsDesc) != 0; } - ecs_query_desc_t qd = desc->query; - if (!qd.order_by) { - qd.order_by = flecs_entity_compare; + /* Find closest smaller group id */ + while (ecs_map_next(&it)) { + id = ecs_map_key(&it); + + if (!desc) { + if (id >= group_id) { + continue; + } + } else { + if (id <= group_id) { + continue; + } + } + + list = ecs_map_ptr(&it); + if (!list->last) { + ecs_assert(list->first == NULL, ECS_INTERNAL_ERROR, NULL); + continue; + } + + bool comp; + if (!desc) { + comp = ((group_id - id) < (group_id - closest_id)); + } else { + comp = ((group_id - id) > (group_id - closest_id)); + } + + if (!closest_list || comp) { + closest_id = id; + closest_list = list; + } } - qd.filter.entity = result; - ecs_query_t *query = ecs_query_init(world, &qd); - if (!query) { - ecs_delete(world, result); - return 0; + if (closest_list) { + return closest_list->last; + } else { + return NULL; /* Group should be first in query */ } +} - ecs_check(query->filter.terms != NULL, ECS_INVALID_PARAMETER, - "pipeline query cannot be empty"); - ecs_check(query->filter.terms[0].id == EcsSystem, - ECS_INVALID_PARAMETER, "pipeline must start with System term"); +/* Initialize group with first node */ +static +void flecs_query_cache_create_group( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) +{ + uint64_t group_id = match->group_id; - ecs_pipeline_state_t *pq = ecs_os_calloc_t(ecs_pipeline_state_t); - pq->query = query; - pq->match_count = -1; - pq->idr_inactive = flecs_id_record_ensure(world, EcsEmpty); - ecs_set(world, result, EcsPipeline, { pq }); + /* If query has grouping enabled & this is a new/empty group, find + * the insertion point for the group */ + ecs_query_cache_table_match_t *insert_after = + flecs_query_cache_find_group_insertion_node(cache, group_id); - return result; -error: - return 0; -} + if (!insert_after) { + /* This group should appear first in the query list */ + ecs_query_cache_table_match_t *query_first = cache->list.first; + if (query_first) { + /* If this is not the first match for the query, insert before it */ + match->next = query_first; + query_first->prev = match; + cache->list.first = match; + } else { + /* If this is the first match of the query, initialize its list */ + ecs_assert(cache->list.last == NULL, ECS_INTERNAL_ERROR, NULL); + cache->list.first = match; + cache->list.last = match; + } + } else { + ecs_assert(cache->list.first != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.last != NULL, ECS_INTERNAL_ERROR, NULL); -/* -- Module implementation -- */ + /* This group should appear after another group */ + ecs_query_cache_table_match_t *insert_before = insert_after->next; + match->prev = insert_after; + insert_after->next = match; + match->next = insert_before; + if (insert_before) { + insert_before->prev = match; + } else { + ecs_assert(cache->list.last == insert_after, + ECS_INTERNAL_ERROR, NULL); + + /* This group should appear last in the query list */ + cache->list.last = match; + } + } +} +/* Find the list the node should be part of */ static -void FlecsPipelineFini( - ecs_world_t *world, - void *ctx) +ecs_query_cache_table_list_t* flecs_query_cache_get_node_list( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) { - (void)ctx; - if (ecs_get_stage_count(world)) { - ecs_set_threads(world, 0); + if (cache->group_by_callback) { + return flecs_query_cache_get_group(cache, match->group_id); + } else { + return &cache->list; } - - ecs_assert(world->workers_running == 0, ECS_INTERNAL_ERROR, NULL); } -#define flecs_bootstrap_phase(world, phase, depends_on)\ - flecs_bootstrap_tag(world, phase);\ - flecs_bootstrap_phase_(world, phase, depends_on) +/* Find or create the list the node should be part of */ static -void flecs_bootstrap_phase_( - ecs_world_t *world, - ecs_entity_t phase, - ecs_entity_t depends_on) +ecs_query_cache_table_list_t* flecs_query_cache_ensure_node_list( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) { - ecs_add_id(world, phase, EcsPhase); - if (depends_on) { - ecs_add_pair(world, phase, EcsDependsOn, depends_on); + if (cache->group_by_callback) { + return flecs_query_cache_ensure_group(cache, match->group_id); + } else { + return &cache->list; } } -void FlecsPipelineImport( - ecs_world_t *world) +/* Remove node from list */ +static +void flecs_query_cache_remove_table_node( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) { - ECS_MODULE(world, FlecsPipeline); - ECS_IMPORT(world, FlecsSystem); + ecs_query_cache_table_match_t *prev = match->prev; + ecs_query_cache_table_match_t *next = match->next; - ecs_set_name_prefix(world, "Ecs"); + ecs_assert(prev != match, ECS_INTERNAL_ERROR, NULL); + ecs_assert(next != match, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!prev || prev != next, ECS_INTERNAL_ERROR, NULL); - flecs_bootstrap_component(world, EcsPipeline); - flecs_bootstrap_tag(world, EcsPhase); + ecs_query_cache_table_list_t *list = + flecs_query_cache_get_node_list(cache, match); - /* Create anonymous phases to which the builtin phases will have DependsOn - * relationships. This ensures that, for example, EcsOnUpdate doesn't have a - * direct DependsOn relationship on EcsPreUpdate, which ensures that when - * the EcsPreUpdate phase is disabled, EcsOnUpdate still runs. */ - ecs_entity_t phase_0 = ecs_new(world, 0); - ecs_entity_t phase_1 = ecs_new_w_pair(world, EcsDependsOn, phase_0); - ecs_entity_t phase_2 = ecs_new_w_pair(world, EcsDependsOn, phase_1); - ecs_entity_t phase_3 = ecs_new_w_pair(world, EcsDependsOn, phase_2); - ecs_entity_t phase_4 = ecs_new_w_pair(world, EcsDependsOn, phase_3); - ecs_entity_t phase_5 = ecs_new_w_pair(world, EcsDependsOn, phase_4); - ecs_entity_t phase_6 = ecs_new_w_pair(world, EcsDependsOn, phase_5); - ecs_entity_t phase_7 = ecs_new_w_pair(world, EcsDependsOn, phase_6); - ecs_entity_t phase_8 = ecs_new_w_pair(world, EcsDependsOn, phase_7); - - flecs_bootstrap_phase(world, EcsOnStart, 0); - flecs_bootstrap_phase(world, EcsPreFrame, 0); - flecs_bootstrap_phase(world, EcsOnLoad, phase_0); - flecs_bootstrap_phase(world, EcsPostLoad, phase_1); - flecs_bootstrap_phase(world, EcsPreUpdate, phase_2); - flecs_bootstrap_phase(world, EcsOnUpdate, phase_3); - flecs_bootstrap_phase(world, EcsOnValidate, phase_4); - flecs_bootstrap_phase(world, EcsPostUpdate, phase_5); - flecs_bootstrap_phase(world, EcsPreStore, phase_6); - flecs_bootstrap_phase(world, EcsOnStore, phase_7); - flecs_bootstrap_phase(world, EcsPostFrame, phase_8); + if (!list || !list->first) { + /* If list contains no matches, the match must be empty */ + ecs_assert(!list || list->last == NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(prev == NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(next == NULL, ECS_INTERNAL_ERROR, NULL); + return; + } - ecs_set_hooks(world, EcsPipeline, { - .ctor = ecs_default_ctor, - .dtor = ecs_dtor(EcsPipeline), - .move = ecs_move(EcsPipeline) - }); + ecs_assert(prev != NULL || cache->list.first == match, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(next != NULL || cache->list.last == match, + ECS_INTERNAL_ERROR, NULL); - world->pipeline = ecs_pipeline(world, { - .entity = ecs_entity(world, { .name = "BuiltinPipeline" }), - .query = { - .filter.terms = { - { .id = EcsSystem }, - { .id = EcsPhase, .src.flags = EcsCascade, .src.trav = EcsDependsOn }, - { .id = ecs_dependson(EcsOnStart), .src.trav = EcsDependsOn, .oper = EcsNot }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsDependsOn, .oper = EcsNot }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsChildOf, .oper = EcsNot } - }, - .order_by = flecs_entity_compare - } - }); + if (prev) { + prev->next = next; + } + if (next) { + next->prev = prev; + } - /* Cleanup thread administration when world is destroyed */ - ecs_atfini(world, FlecsPipelineFini, NULL); -} + ecs_assert(list->info.table_count > 0, ECS_INTERNAL_ERROR, NULL); + list->info.table_count --; -#endif + if (cache->group_by_callback) { + uint64_t group_id = match->group_id; -/** - * @file addons/pipeline/worker.c - * @brief Functions for running pipelines on one or more threads. - */ + /* Make sure query.list is updated if this is the first or last group */ + if (cache->list.first == match) { + ecs_assert(prev == NULL, ECS_INTERNAL_ERROR, NULL); + cache->list.first = next; + prev = next; + } + if (cache->list.last == match) { + ecs_assert(next == NULL, ECS_INTERNAL_ERROR, NULL); + cache->list.last = prev; + next = prev; + } + ecs_assert(cache->list.info.table_count > 0, ECS_INTERNAL_ERROR, NULL); + cache->list.info.table_count --; + list->info.match_count ++; -#ifdef FLECS_PIPELINE + /* Make sure group list only contains nodes that belong to the group */ + if (prev && prev->group_id != group_id) { + /* The previous node belonged to another group */ + prev = next; + } + if (next && next->group_id != group_id) { + /* The next node belonged to another group */ + next = prev; + } -/* Synchronize workers */ -static -void flecs_sync_worker( - ecs_world_t* world) -{ - int32_t stage_count = ecs_get_stage_count(world); - if (stage_count <= 1) { - return; + /* Do check again, in case both prev & next belonged to another group */ + if ((!prev && !next) || (prev && prev->group_id != group_id)) { + /* There are no more matches left in this group */ + flecs_query_cache_remove_group(cache, group_id); + list = NULL; + } } - /* Signal that thread is waiting */ - ecs_os_mutex_lock(world->sync_mutex); - if (++world->workers_waiting == (stage_count - 1)) { - /* Only signal main thread when all threads are waiting */ - ecs_os_cond_signal(world->sync_cond); + if (list) { + if (list->first == match) { + list->first = next; + } + if (list->last == match) { + list->last = prev; + } } - /* Wait until main thread signals that thread can continue */ - ecs_os_cond_wait(world->worker_cond, world->sync_mutex); - ecs_os_mutex_unlock(world->sync_mutex); + match->prev = NULL; + match->next = NULL; + + cache->match_count ++; } -/* Worker thread */ +/* Add node to list */ static -void* flecs_worker(void *arg) { - ecs_stage_t *stage = arg; - ecs_world_t *world = stage->world; +void flecs_query_cache_insert_table_node( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) +{ + /* Node should not be part of an existing list */ + ecs_assert(match->prev == NULL && match->next == NULL, + ECS_INTERNAL_ERROR, NULL); - ecs_poly_assert(world, ecs_world_t); - ecs_poly_assert(stage, ecs_stage_t); + /* If this is the first match, activate system */ + if (!cache->list.first && cache->entity) { + ecs_remove_id(cache->query->world, cache->entity, EcsEmpty); + } - ecs_dbg_2("worker %d: start", stage->id); + flecs_query_cache_compute_group_id(cache, match); - /* Start worker, increase counter so main thread knows how many - * workers are ready */ - ecs_os_mutex_lock(world->sync_mutex); - world->workers_running ++; + ecs_query_cache_table_list_t *list = + flecs_query_cache_ensure_node_list(cache, match); + if (list->last) { + ecs_assert(cache->list.first != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.last != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(list->first != NULL, ECS_INTERNAL_ERROR, NULL); - if (!(world->flags & EcsWorldQuitWorkers)) { - ecs_os_cond_wait(world->worker_cond, world->sync_mutex); - } + ecs_query_cache_table_match_t *last = list->last; + ecs_query_cache_table_match_t *last_next = last->next; - ecs_os_mutex_unlock(world->sync_mutex); + match->prev = last; + match->next = last_next; + last->next = match; - while (!(world->flags & EcsWorldQuitWorkers)) { - ecs_entity_t old_scope = ecs_set_scope((ecs_world_t*)stage, 0); + if (last_next) { + last_next->prev = match; + } - ecs_dbg_3("worker %d: run", stage->id); - flecs_run_pipeline_ops(world, stage, stage->id, world->stage_count, - world->info.delta_time); + list->last = match; - ecs_set_scope((ecs_world_t*)stage, old_scope); + if (cache->group_by_callback) { + /* Make sure to update query list if this is the last group */ + if (cache->list.last == last) { + cache->list.last = match; + } + } + } else { + ecs_assert(list->first == NULL, ECS_INTERNAL_ERROR, NULL); - flecs_sync_worker(world); + list->first = match; + list->last = match; + + if (cache->group_by_callback) { + /* Initialize group with its first node */ + flecs_query_cache_create_group(cache, match); + } } - ecs_dbg_2("worker %d: finalizing", stage->id); + if (cache->group_by_callback) { + list->info.table_count ++; + list->info.match_count ++; + } - ecs_os_mutex_lock(world->sync_mutex); - world->workers_running --; - ecs_os_mutex_unlock(world->sync_mutex); + cache->list.info.table_count ++; + cache->match_count ++; - ecs_dbg_2("worker %d: stop", stage->id); + ecs_assert(match->prev != match, ECS_INTERNAL_ERROR, NULL); + ecs_assert(match->next != match, ECS_INTERNAL_ERROR, NULL); - return NULL; + ecs_assert(list->first != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(list->last != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(list->last == match, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.first != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.last != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.first->prev == NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.last->next == NULL, ECS_INTERNAL_ERROR, NULL); } -/* Start threads */ -void flecs_create_worker_threads( - ecs_world_t *world) +static +ecs_query_cache_table_match_t* flecs_query_cache_cache_add( + ecs_world_t *world, + ecs_query_cache_table_t *elem) { - ecs_poly_assert(world, ecs_world_t); - int32_t stages = ecs_get_stage_count(world); - - for (int32_t i = 1; i < stages; i ++) { - ecs_stage_t *stage = (ecs_stage_t*)ecs_get_stage(world, i); - ecs_assert(stage != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_poly_assert(stage, ecs_stage_t); + ecs_query_cache_table_match_t *result = + flecs_bcalloc(&world->allocators.query_table_match); - ecs_assert(stage->thread == 0, ECS_INTERNAL_ERROR, NULL); - if (ecs_using_task_threads(world)) { - /* workers are using tasks in an external task manager provided to - * the OS API */ - stage->thread = ecs_os_task_new(flecs_worker, stage); - } else { - /* workers are using long-running os threads */ - stage->thread = ecs_os_thread_new(flecs_worker, stage); - } - ecs_assert(stage->thread != 0, ECS_OPERATION_FAILED, NULL); + if (!elem->first) { + elem->first = result; + elem->last = result; + } else { + ecs_assert(elem->last != NULL, ECS_INTERNAL_ERROR, NULL); + elem->last->next_match = result; + elem->last = result; } + + return result; } +/* The group by function for cascade computes the tree depth for the table type. + * This causes tables in the query cache to be ordered by depth, which ensures + * breadth-first iteration order. */ static -void flecs_start_workers( +uint64_t flecs_query_cache_group_by_cascade( ecs_world_t *world, - int32_t threads) + ecs_table_t *table, + ecs_id_t id, + void *ctx) { - ecs_set_stage_count(world, threads); - - ecs_assert(ecs_get_stage_count(world) == threads, ECS_INTERNAL_ERROR, NULL); - - if (!ecs_using_task_threads(world)) { - flecs_create_worker_threads(world); - } + (void)id; + ecs_term_t *term = ctx; + ecs_entity_t rel = term->trav; + int32_t depth = flecs_relation_depth(world, rel, table); + return flecs_ito(uint64_t, depth); } -/* Wait until all workers are running */ static -void flecs_wait_for_workers( - ecs_world_t *world) +ecs_query_cache_table_match_t* flecs_query_cache_add_table_match( + ecs_query_cache_t *cache, + ecs_query_cache_table_t *qt, + ecs_table_t *table) { - ecs_poly_assert(world, ecs_world_t); + /* Add match for table. One table can have more than one match, if + * the query contains wildcards. */ + ecs_query_cache_table_match_t *qm = flecs_query_cache_cache_add( + cache->query->world, qt); + + qm->table = table; + qm->trs = flecs_balloc(&cache->allocators.trs); - int32_t stage_count = ecs_get_stage_count(world); - if (stage_count <= 1) { - return; + /* Insert match to iteration list if table is not empty */ + if (!table || ecs_table_count(table) != 0 || + (cache->query->flags & EcsQueryCacheYieldEmptyTables)) + { + flecs_query_cache_insert_table_node(cache, qm); } - bool wait = true; - do { - ecs_os_mutex_lock(world->sync_mutex); - if (world->workers_running == (stage_count - 1)) { - wait = false; - } - ecs_os_mutex_unlock(world->sync_mutex); - } while (wait); + return qm; } -/* Wait until all threads are waiting on sync point */ -void flecs_wait_for_sync( - ecs_world_t *world) +static +void flecs_query_cache_set_table_match( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *qm, + ecs_iter_t *it) { - int32_t stage_count = ecs_get_stage_count(world); - if (stage_count <= 1) { - return; - } - - ecs_dbg_3("#[bold]pipeline: waiting for worker sync"); - - ecs_os_mutex_lock(world->sync_mutex); - if (world->workers_waiting != (stage_count - 1)) { - ecs_os_cond_wait(world->sync_cond, world->sync_mutex); - } - - /* We shouldn't have been signalled unless all workers are waiting on sync */ - ecs_assert(world->workers_waiting == (stage_count - 1), - ECS_INTERNAL_ERROR, NULL); + ecs_query_t *query = cache->query; + int8_t i, field_count = query->field_count; - world->workers_waiting = 0; - ecs_os_mutex_unlock(world->sync_mutex); + ecs_assert(field_count > 0, ECS_INTERNAL_ERROR, NULL); - ecs_dbg_3("#[bold]pipeline: workers synced"); -} + /* Reset resources in case this is an existing record */ + ecs_os_memcpy_n(ECS_CONST_CAST(ecs_table_record_t**, qm->trs), + it->trs, ecs_table_record_t*, field_count); -/* Signal workers that they can start/resume work */ -void flecs_signal_workers( - ecs_world_t *world) -{ - int32_t stage_count = ecs_get_stage_count(world); - if (stage_count <= 1) { - return; + /* Find out whether to store result-specific ids array or fixed array */ + ecs_id_t *ids = cache->query->ids; + for (i = 0; i < field_count; i ++) { + if (it->ids[i] != ids[i]) { + break; + } } - ecs_dbg_3("#[bold]pipeline: signal workers"); - ecs_os_mutex_lock(world->sync_mutex); - ecs_os_cond_broadcast(world->worker_cond); - ecs_os_mutex_unlock(world->sync_mutex); -} - -void flecs_join_worker_threads( - ecs_world_t *world) -{ - ecs_poly_assert(world, ecs_world_t); - bool threads_active = false; + if (i != field_count) { + if (qm->ids == ids || !qm->ids) { + qm->ids = flecs_balloc(&cache->allocators.ids); + } + ecs_os_memcpy_n(qm->ids, it->ids, ecs_id_t, field_count); + } else { + if (qm->ids != ids) { + flecs_bfree(&cache->allocators.ids, qm->ids); + qm->ids = ids; + } + } - /* Test if threads are created. Cannot use workers_running, since this is - * a potential race if threads haven't spun up yet. */ - ecs_stage_t *stages = world->stages; - int i, count = world->stage_count; - for (i = 1; i < count; i ++) { - ecs_stage_t *stage = &stages[i]; - if (stage->thread) { - threads_active = true; + /* Find out whether to store result-specific sources array or fixed array */ + for (i = 0; i < field_count; i ++) { + if (it->sources[i]) { break; } - }; + } - /* If no threads are active, just return */ - if (!threads_active) { - return; + if (i != field_count) { + if (qm->sources == cache->sources || !qm->sources) { + qm->sources = flecs_balloc(&cache->allocators.sources); + } + ecs_os_memcpy_n(qm->sources, it->sources, ecs_entity_t, field_count); + } else { + if (qm->sources != cache->sources) { + flecs_bfree(&cache->allocators.sources, qm->sources); + qm->sources = cache->sources; + } } - /* Make sure all threads are running, to ensure they catch the signal */ - flecs_wait_for_workers(world); + qm->set_fields = it->set_fields; + qm->up_fields = it->up_fields; +} - /* Signal threads should quit */ - world->flags |= EcsWorldQuitWorkers; - flecs_signal_workers(world); +static +ecs_query_cache_table_t* flecs_query_cache_table_insert( + ecs_world_t *world, + ecs_query_cache_t *cache, + ecs_table_t *table) +{ + ecs_query_cache_table_t *qt = flecs_bcalloc(&world->allocators.query_table); + if (table) { + qt->table_id = table->id; + } else { + qt->table_id = 0; + } - /* Join all threads with main */ - for (i = 1; i < count; i ++) { - if (ecs_using_task_threads(world)) { - ecs_os_task_join(stages[i].thread); - } else { - ecs_os_thread_join(stages[i].thread); - } - stages[i].thread = 0; + if (cache->query->flags & EcsQueryCacheYieldEmptyTables) { + ecs_table_cache_insert_w_empty(&cache->cache, table, &qt->hdr, false); + } else { + ecs_table_cache_insert(&cache->cache, table, &qt->hdr); } - world->flags &= ~EcsWorldQuitWorkers; - ecs_assert(world->workers_running == 0, ECS_INTERNAL_ERROR, NULL); + return qt; } -/* -- Private functions -- */ -void flecs_workers_progress( +/** Populate query cache with tables */ +static +void flecs_query_cache_match_tables( ecs_world_t *world, - ecs_pipeline_state_t *pq, - ecs_ftime_t delta_time) + ecs_query_cache_t *cache) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!ecs_is_deferred(world), ECS_INVALID_OPERATION, NULL); + ecs_table_t *table = NULL; + ecs_query_cache_table_t *qt = NULL; - /* Make sure workers are running and ready */ - flecs_wait_for_workers(world); + ecs_iter_t it = ecs_query_iter(world, cache->query); + ECS_BIT_SET(it.flags, EcsIterNoData); + ECS_BIT_SET(it.flags, EcsIterTableOnly); - /* Run pipeline on main thread */ - ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_entity_t old_scope = ecs_set_scope((ecs_world_t*)stage, 0); - flecs_run_pipeline(stage, pq, delta_time); - ecs_set_scope((ecs_world_t*)stage, old_scope); + while (ecs_query_next(&it)) { + if ((table != it.table) || (!it.table && !qt)) { + /* New table matched, add record to cache */ + table = it.table; + qt = flecs_query_cache_table_insert(world, cache, table); + } + + ecs_query_cache_table_match_t *qm = + flecs_query_cache_add_table_match(cache, qt, table); + flecs_query_cache_set_table_match(cache, qm, &it); + } } static -void flecs_set_threads_internal( +bool flecs_query_cache_match_table( ecs_world_t *world, - int32_t threads, - bool use_task_api) + ecs_query_cache_t *cache, + ecs_table_t *table) { - ecs_assert(threads <= 1 || (use_task_api - ? ecs_os_has_task_support() - : ecs_os_has_threading()), - ECS_MISSING_OS_API, NULL); + if (!ecs_map_is_init(&cache->cache.index)) { + return false; + } - int32_t stage_count = ecs_get_stage_count(world); - bool worker_method_changed = (use_task_api != world->workers_use_task_api); + ecs_query_cache_table_t *qt = NULL; + ecs_query_t *q = cache->query; - if ((stage_count != threads) || worker_method_changed) { - /* Stop existing threads */ - if (stage_count > 1) { - flecs_join_worker_threads(world); - ecs_set_stage_count(world, 1); + /* Iterate uncached query for table to check if it matches. If this is a + * wildcard query, a table can match multiple times. */ + ecs_iter_t it = flecs_query_iter(world, q); + it.flags |= EcsIterNoData; + ecs_iter_set_var_as_table(&it, 0, table); - if (world->worker_cond) { - ecs_os_cond_free(world->worker_cond); - } - if (world->sync_cond) { - ecs_os_cond_free(world->sync_cond); - } - if (world->sync_mutex) { - ecs_os_mutex_free(world->sync_mutex); - } + while (ecs_query_next(&it)) { + ecs_assert(it.table == table, ECS_INTERNAL_ERROR, NULL); + if (qt == NULL) { + table = it.table; + qt = flecs_query_cache_table_insert(world, cache, table); } - world->workers_use_task_api = use_task_api; + ecs_query_cache_table_match_t *qm = flecs_query_cache_add_table_match( + cache, qt, table); + flecs_query_cache_set_table_match(cache, qm, &it); + } - /* Start threads if number of threads > 1 */ - if (threads > 1) { - world->worker_cond = ecs_os_cond_new(); - world->sync_cond = ecs_os_cond_new(); - world->sync_mutex = ecs_os_mutex_new(); - flecs_start_workers(world, threads); + return qt != NULL; +} + +static +bool flecs_query_cache_has_refs( + ecs_query_cache_t *cache) +{ + ecs_term_t *terms = cache->query->terms; + int32_t i, count = cache->query->term_count; + for (i = 0; i < count; i ++) { + if (terms[i].src.id & (EcsUp | EcsIsEntity)) { + return true; } } -} -/* -- Public functions -- */ + return false; +} -void ecs_set_threads( +static +void flecs_query_cache_for_each_component_monitor( ecs_world_t *world, - int32_t threads) + ecs_query_impl_t *impl, + ecs_query_cache_t *cache, + void(*callback)( + ecs_world_t* world, + ecs_id_t id, + ecs_query_t *q)) { - flecs_set_threads_internal(world, threads, false /* use thread API */); + ecs_query_t *q = &impl->pub; + ecs_term_t *terms = cache->query->terms; + int32_t i, count = cache->query->term_count; + + for (i = 0; i < count; i++) { + ecs_term_t *term = &terms[i]; + ecs_term_ref_t *src = &term->src; + + if (src->id & EcsUp) { + callback(world, ecs_pair(term->trav, EcsWildcard), q); + if (term->trav != EcsIsA) { + callback(world, ecs_pair(EcsIsA, EcsWildcard), q); + } + callback(world, term->id, q); + + } else if (src->id & EcsSelf && !ecs_term_match_this(term)) { + callback(world, term->id, q); + } + } } -void ecs_set_task_threads( - ecs_world_t *world, - int32_t task_threads) +static +bool flecs_query_cache_is_term_ref_supported( + ecs_term_ref_t *ref) { - flecs_set_threads_internal(world, task_threads, true /* use task API */); + if (!(ref->id & EcsIsVariable)) { + return true; + } + if (ecs_id_is_wildcard(ref->id)) { + return true; + } + return false; } -bool ecs_using_task_threads( - ecs_world_t *world) +static +int flecs_query_cache_process_signature( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_query_cache_t *cache) { - return world->workers_use_task_api; -} + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_term_t *terms = cache->query->terms; + int32_t i, count = cache->query->term_count; -#endif + for (i = 0; i < count; i ++) { + ecs_term_t *term = &terms[i]; + ecs_term_ref_t *first = &term->first; + ecs_term_ref_t *src = &term->src; + ecs_term_ref_t *second = &term->second; - /** - * @file addons/rules/api.c - * @brief User facing API for rules. - */ + bool is_src_ok = flecs_query_cache_is_term_ref_supported(src); + bool is_first_ok = flecs_query_cache_is_term_ref_supported(first); + bool is_second_ok = flecs_query_cache_is_term_ref_supported(second); - /** - * @file addons/rules/rules.h - * @brief Internal types and functions for rules addon. - */ + (void)first; + (void)second; + (void)is_src_ok; + (void)is_first_ok; + (void)is_second_ok; + /* Queries do not support named variables */ + ecs_check(is_src_ok || ecs_term_match_this(term), + ECS_UNSUPPORTED, NULL); + ecs_check(is_first_ok, ECS_UNSUPPORTED, NULL); + ecs_check(is_second_ok, ECS_UNSUPPORTED, NULL); + ecs_check(term->inout != EcsInOutFilter, ECS_INVALID_PARAMETER, + "invalid usage of InOutFilter for query"); -#ifdef FLECS_RULES + if (src->id & EcsCascade) { + ecs_assert(cache->cascade_by == 0, ECS_INVALID_PARAMETER, + "query can only have one cascade term"); + cache->cascade_by = i + 1; + } + } -typedef uint8_t ecs_var_id_t; -typedef int16_t ecs_rule_lbl_t; -typedef ecs_flags64_t ecs_write_flags_t; + impl->pub.flags |= + (ecs_flags32_t)(flecs_query_cache_has_refs(cache) * EcsQueryHasRefs); -#define EcsRuleMaxVarCount (64) -#define EcsVarNone ((ecs_var_id_t)-1) -#define EcsThisName "this" + flecs_query_cache_for_each_component_monitor( + world, impl, cache, flecs_monitor_register); -/* -- Variable types -- */ -typedef enum { - EcsVarEntity, /* Variable that stores an entity id */ - EcsVarTable, /* Variable that stores a table */ - EcsVarAny /* Used when requesting either entity or table var */ -} ecs_var_kind_t; + return 0; +error: + return -1; +} -typedef struct ecs_rule_var_t { - int8_t kind; /* variable kind (EcsVarEntity or EcsVarTable) */ - bool anonymous; /* variable is anonymous */ - ecs_var_id_t id; /* variable id */ - ecs_var_id_t table_id; /* id to table variable, if any */ - ecs_var_id_t base_id; /* id to base entity variable, for lookups */ - const char *name; /* variable name */ - const char *lookup; /* Lookup string for variable */ -#ifdef FLECS_DEBUG - const char *label; /* for debugging */ -#endif -} ecs_rule_var_t; +/** When a table becomes empty remove it from the query list, or vice versa. */ +static +void flecs_query_cache_update_table( + ecs_query_cache_t *cache, + ecs_table_t *table, + bool empty) +{ + int32_t prev_count = flecs_query_cache_table_count(cache); + ecs_table_cache_set_empty(&cache->cache, table, empty); + int32_t cur_count = flecs_query_cache_table_count(cache); -/* -- Instruction kinds -- */ -typedef enum { - EcsRuleAnd, /* And operator: find or match id against variable source */ - EcsRuleAndId, /* And operator for fixed id (no wildcards/variables) */ - EcsRuleAndAny, /* And operator with support for matching Any src/id */ - EcsRuleTriv, /* Trivial search */ - EcsRuleTrivData, /* Trivial search with setting data fields */ - EcsRuleTrivWildcard, /* Trivial search with (exclusive) wildcard ids */ - EcsRuleSelectAny, /* Dedicated instruction for _ queries where the src is unknown */ - EcsRuleUp, /* Up traversal */ - EcsRuleUpId, /* Up traversal for fixed id (like AndId) */ - EcsRuleSelfUp, /* Self|up traversal */ - EcsRuleSelfUpId, /* Self|up traversal for fixed id (like AndId) */ - EcsRuleWith, /* Match id against fixed or variable source */ - EcsRuleTrav, /* Support for transitive/reflexive queries */ - EcsRuleIdsRight, /* Find ids in use that match (R, *) wildcard */ - EcsRuleIdsLeft, /* Find ids in use that match (*, T) wildcard */ - EcsRuleEach, /* Iterate entities in table, populate entity variable */ - EcsRuleStore, /* Store table or entity in variable */ - EcsRuleReset, /* Reset value of variable to wildcard (*) */ - EcsRuleOr, /* Or operator */ - EcsRuleOptional, /* Optional operator */ - EcsRuleIf, /* Conditional execution */ - EcsRuleNot, /* Sets iterator state after term was not matched */ - EcsRuleEnd, /* End of control flow block */ - EcsRulePredEq, /* Test if variable is equal to, or assign to if not set */ - EcsRulePredNeq, /* Test if variable is not equal to */ - EcsRulePredEqName, /* Same as EcsRulePredEq but with matching by name */ - EcsRulePredNeqName, /* Same as EcsRulePredNeq but with matching by name */ - EcsRulePredEqMatch, /* Same as EcsRulePredEq but with fuzzy matching by name */ - EcsRulePredNeqMatch, /* Same as EcsRulePredNeq but with fuzzy matching by name */ - EcsRuleLookup, /* Lookup relative to variable */ - EcsRuleSetVars, /* Populate it.sources from variables */ - EcsRuleSetThis, /* Populate This entity variable */ - EcsRuleSetFixed, /* Set fixed source entity ids */ - EcsRuleSetIds, /* Set fixed (component) ids */ - EcsRuleSetId, /* Set id if not set */ - EcsRuleContain, /* Test if table contains entity */ - EcsRulePairEq, /* Test if both elements of pair are the same */ - EcsRulePopulate, /* Populate any data fields */ - EcsRulePopulateSelf, /* Populate only self (owned) data fields */ - EcsRuleYield, /* Yield result back to application */ - EcsRuleNothing /* Must be last */ -} ecs_rule_op_kind_t; - -/* Op flags to indicate if ecs_rule_ref_t is entity or variable */ -#define EcsRuleIsEntity (1 << 0) -#define EcsRuleIsVar (1 << 1) -#define EcsRuleIsSelf (1 << 6) - -/* Op flags used to shift EcsRuleIsEntity and EcsRuleIsVar */ -#define EcsRuleSrc 0 -#define EcsRuleFirst 2 -#define EcsRuleSecond 4 + if (prev_count != cur_count) { + ecs_query_cache_table_t *qt = ecs_table_cache_get(&cache->cache, table); + ecs_assert(qt != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_query_cache_table_match_t *cur, *next; -/* References to variable or entity */ -typedef union { - ecs_var_id_t var; - ecs_entity_t entity; -} ecs_rule_ref_t; + for (cur = qt->first; cur != NULL; cur = next) { + next = cur->next_match; -/* Query instruction */ -typedef struct ecs_rule_op_t { - uint8_t kind; /* Instruction kind */ - ecs_flags8_t flags; /* Flags storing whether 1st/2nd are variables */ - int8_t field_index; /* Query field corresponding with operation */ - int8_t term_index; /* Query term corresponding with operation */ - ecs_rule_lbl_t prev; /* Backtracking label (no data) */ - ecs_rule_lbl_t next; /* Forwarding label. Must come after prev */ - ecs_rule_lbl_t other; /* Misc register used for control flow */ - ecs_flags16_t match_flags; /* Flags that modify matching behavior */ - ecs_rule_ref_t src; - ecs_rule_ref_t first; - ecs_rule_ref_t second; - ecs_flags64_t written; /* Bitset with variables written by op */ -} ecs_rule_op_t; + if (empty) { + ecs_assert(ecs_table_count(table) == 0, + ECS_INTERNAL_ERROR, NULL); - /* And context */ -typedef struct { - ecs_id_record_t *idr; - ecs_table_cache_iter_t it; - int16_t column; - int16_t remaining; -} ecs_rule_and_ctx_t; + flecs_query_cache_remove_table_node(cache, cur); + } else { + ecs_assert(ecs_table_count(table) != 0, + ECS_INTERNAL_ERROR, NULL); -/* Down traversal cache (for resolving up queries w/unknown source) */ -typedef struct { - ecs_table_t *table; - bool leaf; /* Table owns and inherits id (for Up queries without Self) */ -} ecs_trav_down_elem_t; + flecs_query_cache_insert_table_node(cache, cur); + } + } + } -typedef struct { - ecs_vec_t elems; /* vector */ - bool ready; -} ecs_trav_down_t; + ecs_assert(cur_count || cache->list.first == NULL, + ECS_INTERNAL_ERROR, NULL); +} -typedef struct { - ecs_entity_t src; - ecs_id_t id; - int32_t column; - bool ready; -} ecs_trav_up_t; +/* Remove table */ +static +void flecs_query_cache_table_match_free( + ecs_query_cache_t *cache, + ecs_query_cache_table_t *elem, + ecs_query_cache_table_match_t *first) +{ + ecs_query_cache_table_match_t *cur, *next; + ecs_world_t *world = cache->query->world; -typedef struct { - ecs_map_t src; /* map or map */ - ecs_id_t with; - ecs_flags32_t dir; -} ecs_trav_up_cache_t; + for (cur = first; cur != NULL; cur = next) { + flecs_bfree(&cache->allocators.trs, ECS_CONST_CAST(void*, cur->trs)); -/* And up context */ -typedef struct { - ecs_rule_and_ctx_t and; - ecs_table_t *table; - int32_t row; - int32_t end; - ecs_entity_t trav; - ecs_id_t with; - ecs_id_t matched; - ecs_id_record_t *idr_with; - ecs_id_record_t *idr_trav; - ecs_trav_down_t *down; - int32_t cache_elem; - ecs_trav_up_cache_t cache; -} ecs_rule_up_ctx_t; + if (cur->ids != cache->query->ids) { + flecs_bfree(&cache->allocators.ids, cur->ids); + } -/* Cache for storing results of upward/downward "all" traversal. This type of - * traversal iterates and caches the entire tree. */ -typedef struct { - ecs_entity_t entity; - ecs_id_record_t *idr; - int32_t column; -} ecs_trav_elem_t; + if (cur->sources != cache->sources) { + flecs_bfree(&cache->allocators.sources, cur->sources); + } -typedef struct { - ecs_id_t id; - ecs_id_record_t *idr; - ecs_vec_t entities; - bool up; -} ecs_trav_cache_t; + if (cur->monitor) { + flecs_bfree(&cache->allocators.monitors, cur->monitor); + } -/* Trav context */ -typedef struct { - ecs_rule_and_ctx_t and; - int32_t index; - int32_t offset; - int32_t count; - ecs_trav_cache_t cache; - bool yield_reflexive; -} ecs_rule_trav_ctx_t; + if (!elem->hdr.empty) { + flecs_query_cache_remove_table_node(cache, cur); + } - /* Eq context */ -typedef struct { - ecs_table_range_t range; - int32_t index; - int16_t name_col; - bool redo; -} ecs_rule_eq_ctx_t; + next = cur->next_match; - /* Each context */ -typedef struct { - int32_t row; -} ecs_rule_each_ctx_t; + flecs_bfree(&world->allocators.query_table_match, cur); + } +} - /* Setthis context */ -typedef struct { - ecs_table_range_t range; -} ecs_rule_setthis_ctx_t; +static +void flecs_query_cache_table_free( + ecs_query_cache_t *cache, + ecs_query_cache_table_t *elem) +{ + flecs_query_cache_table_match_free(cache, elem, elem->first); + flecs_bfree(&cache->query->world->allocators.query_table, elem); +} -/* Ids context */ -typedef struct { - ecs_id_record_t *cur; -} ecs_rule_ids_ctx_t; +static +void flecs_query_cache_unmatch_table( + ecs_query_cache_t *cache, + ecs_table_t *table, + ecs_query_cache_table_t *elem) +{ + if (!elem) { + elem = ecs_table_cache_get(&cache->cache, table); + } + if (elem) { + ecs_table_cache_remove(&cache->cache, elem->table_id, &elem->hdr); + flecs_query_cache_table_free(cache, elem); + } +} -/* Control flow context */ -typedef struct { - ecs_rule_lbl_t op_index; - ecs_id_t field_id; -} ecs_rule_ctrl_ctx_t; +/* Rematch system with tables after a change happened to a watched entity */ +static +void flecs_query_cache_rematch_tables( + ecs_world_t *world, + ecs_query_impl_t *impl) +{ + ecs_iter_t it; + ecs_table_t *table = NULL; + ecs_query_cache_table_t *qt = NULL; + ecs_query_cache_table_match_t *qm = NULL; + ecs_query_cache_t *cache = impl->cache; -/* Trivial context */ -typedef struct { - ecs_table_cache_iter_t it; - const ecs_table_record_t *tr; -} ecs_rule_trivial_ctx_t; + if (cache->monitor_generation == world->monitor_generation) { + return; + } -typedef struct ecs_rule_op_ctx_t { - union { - ecs_rule_and_ctx_t and; - ecs_rule_up_ctx_t up; - ecs_rule_trav_ctx_t trav; - ecs_rule_ids_ctx_t ids; - ecs_rule_eq_ctx_t eq; - ecs_rule_each_ctx_t each; - ecs_rule_setthis_ctx_t setthis; - ecs_rule_ctrl_ctx_t ctrl; - ecs_rule_trivial_ctx_t trivial; - } is; -} ecs_rule_op_ctx_t; + ecs_os_perf_trace_push("flecs.query.rematch"); -typedef struct { - /* Labels used for control flow */ - ecs_rule_lbl_t lbl_query; /* Used to find the op that does the actual searching */ - ecs_rule_lbl_t lbl_begin; - ecs_rule_lbl_t lbl_cond_eval; - ecs_write_flags_t cond_written_or; /* Cond written flags at start of or chain */ - bool in_or; /* Whether we're in an or chain */ -} ecs_rule_compile_ctrlflow_t; + cache->monitor_generation = world->monitor_generation; -/* Rule compiler state */ -typedef struct { - ecs_vec_t *ops; - ecs_write_flags_t written; /* Bitmask to check which variables have been written */ - ecs_write_flags_t cond_written; /* Track conditional writes (optional operators) */ + it = ecs_query_iter(world, cache->query); + ECS_BIT_SET(it.flags, EcsIterNoData); - /* Maintain control flow per scope */ - ecs_rule_compile_ctrlflow_t ctrlflow[FLECS_QUERY_SCOPE_NESTING_MAX]; - ecs_rule_compile_ctrlflow_t *cur; /* Current scope */ + world->info.rematch_count_total ++; + int32_t rematch_count = ++ cache->rematch_count; - int32_t scope; /* Nesting level of query scopes */ - ecs_flags32_t scope_is_not; /* Whether scope is prefixed with not */ -} ecs_rule_compile_ctx_t; + ecs_time_t t = {0}; + if (world->flags & EcsWorldMeasureFrameTime) { + ecs_time_measure(&t); + } -/* Rule run state */ -typedef struct { - uint64_t *written; /* Bitset to check which variables have been written */ - ecs_rule_lbl_t op_index; /* Currently evaluated operation */ - ecs_var_t *vars; /* Variable storage */ - ecs_iter_t *it; /* Iterator */ - ecs_rule_op_ctx_t *op_ctx; /* Operation context (stack) */ - ecs_world_t *world; /* Reference to world */ - const ecs_rule_t *rule; /* Reference to rule */ - const ecs_rule_var_t *rule_vars; /* Reference to rule variable array */ - ecs_flags32_t *source_set; /* Whether ecs_iter_t::sources is written by instruction */ - ecs_rule_iter_t *rit; -} ecs_rule_run_ctx_t; + while (ecs_query_next(&it)) { + if ((table != it.table) || (!it.table && !qt)) { + if (qm && qm->next_match) { + flecs_query_cache_table_match_free(cache, qt, qm->next_match); + qm->next_match = NULL; + } -typedef struct { - ecs_rule_var_t var; - const char *name; -} ecs_rule_var_cache_t; + table = it.table; -struct ecs_rule_t { - ecs_header_t hdr; /* Poly header */ - ecs_filter_t filter; /* Filter */ + qt = ecs_table_cache_get(&cache->cache, table); + if (!qt) { + qt = flecs_query_cache_table_insert(world, cache, table); + } - /* Variables */ - ecs_rule_var_t *vars; /* Variables */ - int32_t var_count; /* Number of variables */ - int32_t var_pub_count; /* Number of public variables */ - bool has_table_this; /* Does rule have [$this] */ - ecs_hashmap_t tvar_index; /* Name index for table variables */ - ecs_hashmap_t evar_index; /* Name index for entity variables */ - ecs_rule_var_cache_t vars_cache; /* For trivial rules with only This variables */ - char **var_names; /* Array with variable names for iterator */ - - ecs_var_id_t *src_vars; /* Array with ids to source variables for fields */ - ecs_rule_op_t *ops; /* Operations */ - int32_t op_count; /* Number of operations */ + ecs_assert(qt->hdr.table == table, ECS_INTERNAL_ERROR, NULL); + qt->rematch_count = rematch_count; + qm = NULL; + } + if (!qm) { + qm = qt->first; + } else { + qm = qm->next_match; + } + if (!qm) { + qm = flecs_query_cache_add_table_match(cache, qt, table); + } - /* Mixins */ - ecs_iterable_t iterable; - ecs_poly_dtor_t dtor; + flecs_query_cache_set_table_match(cache, qm, &it); -#ifdef FLECS_DEBUG - int32_t var_size; /* Used for out of bounds check during compilation */ -#endif -}; + if (table && ecs_table_count(table) && cache->group_by_callback) { + if (flecs_query_cache_get_group_id(cache, table) != qm->group_id) { + /* Update table group */ + flecs_query_cache_remove_table_node(cache, qm); + flecs_query_cache_insert_table_node(cache, qm); + } + } + } -/* Convert integer to label */ -ecs_rule_lbl_t flecs_itolbl( - int64_t val); + if (qm && qm->next_match) { + flecs_query_cache_table_match_free(cache, qt, qm->next_match); + qm->next_match = NULL; + } -/* Get ref flags (IsEntity) or IsVar) for ref (Src, First, Second) */ -ecs_flags16_t flecs_rule_ref_flags( - ecs_flags16_t flags, - ecs_flags16_t kind); + /* Iterate all tables in cache, remove ones that weren't just matched */ + ecs_table_cache_iter_t cache_it; + if (flecs_table_cache_all_iter(&cache->cache, &cache_it)) { + while ((qt = flecs_table_cache_next(&cache_it, ecs_query_cache_table_t))) { + if (qt->rematch_count != rematch_count) { + flecs_query_cache_unmatch_table(cache, qt->hdr.table, qt); + } + } + } -/* Check if variable is written */ -bool flecs_rule_is_written( - ecs_var_id_t var_id, - uint64_t written); + if (world->flags & EcsWorldMeasureFrameTime) { + world->info.rematch_time_total += (ecs_ftime_t)ecs_time_measure(&t); + } -/* Check if ref is written (calls flecs_rule_is_written)*/ -bool flecs_ref_is_written( - const ecs_rule_op_t *op, - const ecs_rule_ref_t *ref, - ecs_flags16_t kind, - uint64_t written); + ecs_os_perf_trace_pop("flecs.query.rematch"); +} -/* Compile filter to list of operations */ -int flecs_rule_compile( - ecs_world_t *world, - ecs_stage_t *stage, - ecs_rule_t *rule); +/* -- Private API -- */ -/* Get allocator from iterator */ -ecs_allocator_t* flecs_rule_get_allocator( - const ecs_iter_t *it); +void flecs_query_cache_notify( + ecs_world_t *world, + ecs_query_t *q, + ecs_query_cache_event_t *event) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_query_impl_t *impl = flecs_query_impl(q); + ecs_query_cache_t *cache = impl->cache; + switch(event->kind) { + case EcsQueryTableMatch: + /* Creation of new table */ + flecs_query_cache_match_table(world, cache, event->table); + break; + case EcsQueryTableUnmatch: + /* Deletion of table */ + flecs_query_cache_unmatch_table(cache, event->table, NULL); + break; + case EcsQueryTableRematch: + /* Rematch tables of query */ + flecs_query_cache_rematch_tables(world, impl); + break; + } +} -/* Traversal cache for transitive queries. Finds all reachable entities by - * following a relationship */ +static +int flecs_query_cache_order_by( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_entity_t order_by, + ecs_order_by_action_t order_by_callback, + ecs_sort_table_action_t action) +{ + ecs_check(impl != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_query_cache_t *cache = impl->cache; + ecs_check(cache != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(!ecs_id_is_wildcard(order_by), + ECS_INVALID_PARAMETER, NULL); -/* Find all entities when traversing downwards */ -void flecs_rule_get_trav_down_cache( - const ecs_rule_run_ctx_t *ctx, - ecs_trav_cache_t *cache, - ecs_entity_t trav, - ecs_entity_t entity); + /* Find order_by term & make sure it is queried for */ + const ecs_query_t *query = cache->query; + int32_t i, count = query->term_count; + int32_t order_by_term = -1; -/* Find all entities when traversing upwards */ -void flecs_rule_get_trav_up_cache( - const ecs_rule_run_ctx_t *ctx, - ecs_trav_cache_t *cache, - ecs_entity_t trav, - ecs_table_t *table); + if (order_by) { + for (i = 0; i < count; i ++) { + const ecs_term_t *term = &query->terms[i]; + + /* Only And terms are supported */ + if (term->id == order_by && term->oper == EcsAnd) { + order_by_term = i; + break; + } + } -/* Free traversal cache */ -void flecs_rule_trav_cache_fini( - ecs_allocator_t *a, - ecs_trav_cache_t *cache); + if (order_by_term == -1) { + char *id_str = ecs_id_str(world, order_by); + ecs_err("order_by component '%s' is not queried for", id_str); + ecs_os_free(id_str); + goto error; + } + } -/* Traversal caches for up traversal. Enables searching upwards until an entity - * with the queried for id has been found. */ + cache->order_by = order_by; + cache->order_by_callback = order_by_callback; + cache->order_by_term = order_by_term; + cache->order_by_table_callback = action; -/* Traverse downwards from starting entity to find all tables for which the - * specified entity is the source of the queried for id ('with'). */ -ecs_trav_down_t* flecs_rule_get_down_cache( - const ecs_rule_run_ctx_t *ctx, - ecs_trav_up_cache_t *cache, - ecs_entity_t trav, - ecs_entity_t entity, - ecs_id_record_t *idr_with, - bool self); + ecs_vec_fini_t(NULL, &cache->table_slices, ecs_query_cache_table_match_t); + flecs_query_cache_sort_tables(world, impl); -/* Free down traversal cache */ -void flecs_rule_down_cache_fini( - ecs_allocator_t *a, - ecs_trav_up_cache_t *cache); + if (!cache->table_slices.array) { + flecs_query_cache_build_sorted_tables(cache); + } -ecs_trav_up_t* flecs_rule_get_up_cache( - const ecs_rule_run_ctx_t *ctx, - ecs_trav_up_cache_t *cache, - ecs_table_t *table, - ecs_id_t with, - ecs_entity_t trav, - ecs_id_record_t *idr_with, - ecs_id_record_t *idr_trav); + return 0; +error: + return -1; +} -/* Free up traversal cache */ -void flecs_rule_up_cache_fini( - ecs_trav_up_cache_t *cache); +static +void flecs_query_cache_group_by( + ecs_query_cache_t *cache, + ecs_entity_t sort_component, + ecs_group_by_action_t group_by) +{ + ecs_check(cache->group_by == 0, ECS_INVALID_OPERATION, + "query is already grouped"); + ecs_check(cache->group_by_callback == 0, ECS_INVALID_OPERATION, + "query is already grouped"); -/* Convert instruction kind to string */ -const char* flecs_rule_op_str( - uint16_t kind); + if (!group_by) { + /* Builtin function that groups by relationship */ + group_by = flecs_query_cache_default_group_by; + } -/* Iterator for trivial queries. */ -bool flecs_rule_trivial_search( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t until); + cache->group_by = sort_component; + cache->group_by_callback = group_by; -/* Iterator for trivial queries. */ -bool flecs_rule_trivial_search_nodata( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t until); + ecs_map_init_w_params(&cache->groups, + &cache->query->world->allocators.query_table_list); +error: + return; +} -/* Iterator for trivial queries with wildcard matching. */ -bool flecs_rule_trivial_search_w_wildcards( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t until); +static +void flecs_query_cache_on_event( + ecs_iter_t *it) +{ + /* Because this is the observer::run callback, checking if this is event is + * already handled is not done for us. */ + ecs_world_t *world = it->world; + ecs_observer_t *o = it->ctx; + ecs_observer_impl_t *o_impl = flecs_observer_impl(o); + if (o_impl->last_event_id) { + if (o_impl->last_event_id[0] == world->event_id) { + return; + } + o_impl->last_event_id[0] = world->event_id; + } -/* Trivial test for constrained $this. */ -bool flecs_rule_trivial_test( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - bool first, - int32_t term_count); + ecs_query_impl_t *impl = o->ctx; + flecs_poly_assert(impl, ecs_query_t); + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = it->table; + ecs_entity_t event = it->event; -/* Trivial test for constrained $this with wildcard matching. */ -bool flecs_rule_trivial_test_w_wildcards( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - bool first, - int32_t term_count); + if (event == EcsOnTableCreate) { + /* Creation of new table */ + if (flecs_query_cache_match_table(world, cache, table)) { + if (ecs_should_log_3()) { + char *table_str = ecs_table_str(world, table); + ecs_dbg_3("query cache event: %s for [%s]", + ecs_get_name(world, event), + table_str); + ecs_os_free(table_str); + } + } + return; + } -#endif + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); -#include + /* The observer isn't doing the matching because the query can do it more + * efficiently by checking the table with the query cache. */ + if (ecs_table_cache_get(&cache->cache, table) == NULL) { + return; + } -#ifdef FLECS_RULES + if (ecs_should_log_3()) { + char *table_str = ecs_table_str(world, table); + ecs_dbg_3("query cache event: %s for [%s]", + ecs_get_name(world, event), + table_str); + ecs_os_free(table_str); + } -static ecs_mixins_t ecs_rule_t_mixins = { - .type_name = "ecs_rule_t", - .elems = { - [EcsMixinWorld] = offsetof(ecs_rule_t, filter.world), - [EcsMixinEntity] = offsetof(ecs_rule_t, filter.entity), - [EcsMixinIterable] = offsetof(ecs_rule_t, iterable), - [EcsMixinDtor] = offsetof(ecs_rule_t, dtor) + if (event == EcsOnTableEmpty) { + flecs_query_cache_update_table(cache, table, true); + } else + if (event == EcsOnTableFill) { + flecs_query_cache_update_table(cache, table, false); + } else if (event == EcsOnTableDelete) { + /* Deletion of table */ + flecs_query_cache_unmatch_table(cache, table, NULL); + return; } -}; +} -const char* flecs_rule_op_str( - uint16_t kind) -{ - switch(kind) { - case EcsRuleAnd: return "and "; - case EcsRuleAndId: return "andid "; - case EcsRuleAndAny: return "andany "; - case EcsRuleTriv: return "triv "; - case EcsRuleTrivData: return "trivpop "; - case EcsRuleTrivWildcard: return "trivwc "; - case EcsRuleSelectAny: return "any "; - case EcsRuleUp: return "up "; - case EcsRuleUpId: return "upid "; - case EcsRuleSelfUp: return "selfup "; - case EcsRuleSelfUpId: return "selfupid"; - case EcsRuleWith: return "with "; - case EcsRuleTrav: return "trav "; - case EcsRuleIdsRight: return "idsr "; - case EcsRuleIdsLeft: return "idsl "; - case EcsRuleEach: return "each "; - case EcsRuleStore: return "store "; - case EcsRuleReset: return "reset "; - case EcsRuleOr: return "or "; - case EcsRuleOptional: return "option "; - case EcsRuleIf: return "if "; - case EcsRuleEnd: return "end "; - case EcsRuleNot: return "not "; - case EcsRulePredEq: return "eq "; - case EcsRulePredNeq: return "neq "; - case EcsRulePredEqName: return "eq_nm "; - case EcsRulePredNeqName: return "neq_nm "; - case EcsRulePredEqMatch: return "eq_m "; - case EcsRulePredNeqMatch: return "neq_m "; - case EcsRuleLookup: return "lookup "; - case EcsRuleSetVars: return "setvars "; - case EcsRuleSetThis: return "setthis "; - case EcsRuleSetFixed: return "setfix "; - case EcsRuleSetIds: return "setids "; - case EcsRuleSetId: return "setid "; - case EcsRuleContain: return "contain "; - case EcsRulePairEq: return "pair_eq "; - case EcsRulePopulate: return "pop "; - case EcsRulePopulateSelf: return "popself "; - case EcsRuleYield: return "yield "; - case EcsRuleNothing: return "nothing "; - default: return "!invalid"; - } -} - -/* Implementation for iterable mixin */ -static -void flecs_rule_iter_mixin_init( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter) +static +void flecs_query_cache_table_cache_free( + ecs_query_cache_t *cache) { - ecs_poly_assert(poly, ecs_rule_t); + ecs_table_cache_iter_t it; + ecs_query_cache_table_t *qt; - if (filter) { - iter[1] = ecs_rule_iter(world, ECS_CONST_CAST(ecs_rule_t*, poly)); - iter[0] = ecs_term_chain_iter(&iter[1], filter); - } else { - iter[0] = ecs_rule_iter(world, ECS_CONST_CAST(ecs_rule_t*, poly)); + if (flecs_table_cache_all_iter(&cache->cache, &it)) { + while ((qt = flecs_table_cache_next(&it, ecs_query_cache_table_t))) { + flecs_query_cache_table_free(cache, qt); + } } + + ecs_table_cache_fini(&cache->cache); } static -void flecs_rule_fini( - ecs_rule_t *rule) +void flecs_query_cache_allocators_init( + ecs_query_cache_t *cache) { - if (rule->vars != &rule->vars_cache.var) { - ecs_os_free(rule->vars); + int32_t field_count = cache->query->field_count; + if (field_count) { + flecs_ballocator_init(&cache->allocators.trs, + field_count * ECS_SIZEOF(ecs_table_record_t*)); + flecs_ballocator_init(&cache->allocators.ids, + field_count * ECS_SIZEOF(ecs_id_t)); + flecs_ballocator_init(&cache->allocators.sources, + field_count * ECS_SIZEOF(ecs_entity_t)); + flecs_ballocator_init(&cache->allocators.monitors, + (1 + field_count) * ECS_SIZEOF(int32_t)); } - - ecs_os_free(rule->ops); - ecs_os_free(rule->src_vars); - flecs_name_index_fini(&rule->tvar_index); - flecs_name_index_fini(&rule->evar_index); - ecs_filter_fini(&rule->filter); - - ecs_poly_free(rule, ecs_rule_t); } -void ecs_rule_fini( - ecs_rule_t *rule) +static +void flecs_query_cache_allocators_fini( + ecs_query_cache_t *cache) { - if (rule->filter.entity) { - /* If filter is associated with entity, use poly dtor path */ - ecs_delete(rule->filter.world, rule->filter.entity); - } else { - flecs_rule_fini(rule); + int32_t field_count = cache->query->field_count; + if (field_count) { + flecs_ballocator_fini(&cache->allocators.trs); + flecs_ballocator_fini(&cache->allocators.ids); + flecs_ballocator_fini(&cache->allocators.sources); + flecs_ballocator_fini(&cache->allocators.monitors); } } -ecs_rule_t* ecs_rule_init( - ecs_world_t *world, - const ecs_filter_desc_t *const_desc) +void flecs_query_cache_fini( + ecs_query_impl_t *impl) { - ecs_rule_t *result = ecs_poly_new(ecs_rule_t); - ecs_stage_t *stage = flecs_stage_from_world(&world); - - /* Initialize the query */ - ecs_filter_desc_t desc = *const_desc; - desc.storage = &result->filter; /* Use storage of rule */ - result->filter = ECS_FILTER_INIT; - if (ecs_filter_init(world, &desc) == NULL) { - goto error; - } + ecs_world_t *world = impl->pub.world; + ecs_stage_t *stage = impl->stage; + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - result->iterable.init = flecs_rule_iter_mixin_init; + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - /* Compile filter to operations */ - if (flecs_rule_compile(world, stage, result)) { - goto error; + if (cache->observer) { + flecs_observer_fini(cache->observer); } - ecs_entity_t entity = const_desc->entity; - result->dtor = (ecs_poly_dtor_t)flecs_rule_fini; - - if (entity) { - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_rule_t); - poly->poly = result; - ecs_poly_modified(world, entity, ecs_rule_t); + ecs_group_delete_action_t on_delete = cache->on_group_delete; + if (on_delete) { + ecs_map_iter_t it = ecs_map_iter(&cache->groups); + while (ecs_map_next(&it)) { + ecs_query_cache_table_list_t *group = ecs_map_ptr(&it); + uint64_t group_id = ecs_map_key(&it); + on_delete(world, group_id, group->info.ctx, cache->group_by_ctx); + } + cache->on_group_delete = NULL; } - return result; -error: - ecs_rule_fini(result); - return NULL; -} - -static -int32_t flecs_rule_op_ref_str( - const ecs_rule_t *rule, - ecs_rule_ref_t *ref, - ecs_flags16_t flags, - ecs_strbuf_t *buf) -{ - int32_t color_chars = 0; - if (flags & EcsRuleIsVar) { - ecs_assert(ref->var < rule->var_count, ECS_INTERNAL_ERROR, NULL); - ecs_rule_var_t *var = &rule->vars[ref->var]; - ecs_strbuf_appendlit(buf, "#[green]$#[reset]"); - if (var->kind == EcsVarTable) { - ecs_strbuf_appendch(buf, '['); - } - ecs_strbuf_appendlit(buf, "#[green]"); - if (var->name) { - ecs_strbuf_appendstr(buf, var->name); - } else { - if (var->id) { -#ifdef FLECS_DEBUG - if (var->label) { - ecs_strbuf_appendstr(buf, var->label); - ecs_strbuf_appendch(buf, '\''); - } -#endif - ecs_strbuf_append(buf, "%d", var->id); - } else { - ecs_strbuf_appendlit(buf, "this"); - } - } - ecs_strbuf_appendlit(buf, "#[reset]"); - if (var->kind == EcsVarTable) { - ecs_strbuf_appendch(buf, ']'); + if (cache->group_by_ctx_free) { + if (cache->group_by_ctx) { + cache->group_by_ctx_free(cache->group_by_ctx); } - color_chars = ecs_os_strlen("#[green]#[reset]#[green]#[reset]"); - } else if (flags & EcsRuleIsEntity) { - char *path = ecs_get_fullpath(rule->filter.world, ref->entity); - ecs_strbuf_appendlit(buf, "#[blue]"); - ecs_strbuf_appendstr(buf, path); - ecs_strbuf_appendlit(buf, "#[reset]"); - ecs_os_free(path); - color_chars = ecs_os_strlen("#[blue]#[reset]"); } - return color_chars; + + flecs_query_cache_for_each_component_monitor(world, impl, cache, + flecs_monitor_unregister); + flecs_query_cache_table_cache_free(cache); + + ecs_map_fini(&cache->groups); + + ecs_vec_fini_t(NULL, &cache->table_slices, ecs_query_cache_table_match_t); + + if (cache->query->term_count) { + flecs_bfree(&cache->allocators.sources, cache->sources); + } + + flecs_query_cache_allocators_fini(cache); + ecs_query_fini(cache->query); + + flecs_bfree(&stage->allocators.query_cache, cache); } -char* ecs_rule_str_w_profile( - const ecs_rule_t *rule, - const ecs_iter_t *it) +/* -- Public API -- */ + +ecs_query_cache_t* flecs_query_cache_init( + ecs_query_impl_t *impl, + const ecs_query_desc_t *const_desc) { - ecs_poly_assert(rule, ecs_rule_t); + ecs_world_t *world = impl->pub.real_world; + ecs_stage_t *stage = impl->stage; + ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_check(const_desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(const_desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_query_desc_t was not initialized to zero"); + ecs_check(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, + "cannot create query during world fini"); + + /* Create private version of desc to create the uncached query that will + * populate the query cache. */ + ecs_query_desc_t desc = *const_desc; + ecs_entity_t entity = desc.entity; + desc.cache_kind = EcsQueryCacheNone; /* Don't create caches recursively */ + desc.group_by_callback = NULL; + desc.group_by = 0; + desc.order_by_callback = NULL; + desc.order_by = 0; + desc.entity = 0; + + /* Don't pass ctx/binding_ctx to uncached query */ + desc.ctx = NULL; + desc.binding_ctx = NULL; + desc.ctx_free = NULL; + desc.binding_ctx_free = NULL; + + ecs_query_cache_t *result = flecs_bcalloc(&stage->allocators.query_cache); + result->entity = entity; + impl->cache = result; + + ecs_observer_desc_t observer_desc = { .query = desc }; + observer_desc.query.flags |= EcsQueryNested; + + ecs_flags32_t query_flags = const_desc->flags | world->default_query_flags; + desc.flags |= EcsQueryMatchEmptyTables | EcsQueryTableOnly | EcsQueryNested; + + ecs_query_t *q = result->query = ecs_query_init(world, &desc); + if (!q) { + goto error; + } - ecs_strbuf_t buf = ECS_STRBUF_INIT; - ecs_rule_op_t *ops = rule->ops; - int32_t i, count = rule->op_count, indent = 0; - for (i = 0; i < count; i ++) { - ecs_rule_op_t *op = &ops[i]; - ecs_flags16_t flags = op->flags; - ecs_flags16_t src_flags = flecs_rule_ref_flags(flags, EcsRuleSrc); - ecs_flags16_t first_flags = flecs_rule_ref_flags(flags, EcsRuleFirst); - ecs_flags16_t second_flags = flecs_rule_ref_flags(flags, EcsRuleSecond); + /* The uncached query used to populate the cache always matches empty + * tables. This flag determines whether the empty tables are stored + * separately in the cache or are treated as regular tables. This is only + * enabled if the user requested that the query matches empty tables. */ + ECS_BIT_COND(q->flags, EcsQueryCacheYieldEmptyTables, + !!(query_flags & EcsQueryMatchEmptyTables)); - if (it) { -#ifdef FLECS_DEBUG - const ecs_rule_iter_t *rit = &it->priv.iter.rule; - ecs_strbuf_append(&buf, - "#[green]%4d -> #[red]%4d <- #[grey] | ", - rit->profile[i].count[0], - rit->profile[i].count[1]); -#endif - } + flecs_query_cache_allocators_init(result); - ecs_strbuf_append(&buf, - "#[normal]%2d. [#[grey]%2d#[reset], #[green]%2d#[reset]] ", - i, op->prev, op->next); - int32_t hidden_chars, start = ecs_strbuf_written(&buf); - if (op->kind == EcsRuleEnd) { - indent --; - } + /* Zero'd out sources array that's used for results that only match $this. + * This reduces the amount of memory used by the cache, and improves CPU + * cache locality during iteration when doing source checks. */ + if (result->query->term_count) { + result->sources = flecs_bcalloc(&result->allocators.sources); + } - ecs_strbuf_append(&buf, "%*s", indent, ""); - ecs_strbuf_appendstr(&buf, flecs_rule_op_str(op->kind)); - ecs_strbuf_appendstr(&buf, " "); + if (q->term_count) { + observer_desc.run = flecs_query_cache_on_event; + observer_desc.ctx = impl; - int32_t written = ecs_strbuf_written(&buf); - for (int32_t j = 0; j < (12 - (written - start)); j ++) { - ecs_strbuf_appendch(&buf, ' '); + int32_t event_index = 0; + if (!(q->flags & EcsQueryCacheYieldEmptyTables)) { + observer_desc.events[event_index ++] = EcsOnTableEmpty; + observer_desc.events[event_index ++] = EcsOnTableFill; } - - hidden_chars = flecs_rule_op_ref_str(rule, &op->src, src_flags, &buf); - if (op->kind == EcsRuleNot || - op->kind == EcsRuleOr || - op->kind == EcsRuleOptional || - op->kind == EcsRuleIf) - { - indent ++; - } + observer_desc.events[event_index ++] = EcsOnTableCreate; + observer_desc.events[event_index ++] = EcsOnTableDelete; + observer_desc.flags_ = EcsObserverBypassQuery; - if (!first_flags && !second_flags) { - ecs_strbuf_appendstr(&buf, "\n"); - continue; - } + /* ecs_query_init could have moved away resources from the terms array + * in the descriptor, so use the terms array from the query. */ + ecs_os_memcpy_n(observer_desc.query.terms, q->terms, + ecs_term_t, FLECS_TERM_COUNT_MAX); + observer_desc.query.expr = NULL; /* Already parsed */ - written = ecs_strbuf_written(&buf) - hidden_chars; - for (int32_t j = 0; j < (30 - (written - start)); j ++) { - ecs_strbuf_appendch(&buf, ' '); + result->observer = flecs_observer_init(world, entity, &observer_desc); + if (!result->observer) { + goto error; } + } - if (!first_flags && !second_flags) { - ecs_strbuf_appendstr(&buf, "\n"); - continue; - } + result->prev_match_count = -1; - ecs_strbuf_appendstr(&buf, "("); - flecs_rule_op_ref_str(rule, &op->first, first_flags, &buf); + if (ecs_should_log_1()) { + char *query_expr = ecs_query_str(result->query); + ecs_dbg_1("#[green]query#[normal] [%s] created", + query_expr ? query_expr : ""); + ecs_os_free(query_expr); + } - if (second_flags) { - ecs_strbuf_appendstr(&buf, ", "); - flecs_rule_op_ref_str(rule, &op->second, second_flags, &buf); - } else { - switch (op->kind) { - case EcsRulePredEqName: - case EcsRulePredNeqName: - case EcsRulePredEqMatch: - case EcsRulePredNeqMatch: { - int8_t term_index = op->term_index; - ecs_strbuf_appendstr(&buf, ", #[yellow]\""); - ecs_strbuf_appendstr(&buf, rule->filter.terms[term_index].second.name); - ecs_strbuf_appendstr(&buf, "\"#[reset]"); - break; - } - case EcsRuleLookup: { - ecs_var_id_t src_id = op->src.var; - ecs_strbuf_appendstr(&buf, ", #[yellow]\""); - ecs_strbuf_appendstr(&buf, rule->vars[src_id].lookup); - ecs_strbuf_appendstr(&buf, "\"#[reset]"); - break; - } - default: - break; - } - } + ecs_log_push_1(); + + if (flecs_query_cache_process_signature(world, impl, result)) { + goto error; + } + + /* Group before matching so we won't have to move tables around later */ + int32_t cascade_by = result->cascade_by; + if (cascade_by) { + flecs_query_cache_group_by(result, result->query->terms[cascade_by - 1].id, + flecs_query_cache_group_by_cascade); + result->group_by_ctx = &result->query->terms[cascade_by - 1]; + } + + if (const_desc->group_by_callback || const_desc->group_by) { + ecs_check(!result->cascade_by, ECS_INVALID_PARAMETER, + "cannot mix cascade and group_by"); + flecs_query_cache_group_by(result, + const_desc->group_by, const_desc->group_by_callback); + result->group_by_ctx = const_desc->group_by_ctx; + result->on_group_create = const_desc->on_group_create; + result->on_group_delete = const_desc->on_group_delete; + result->group_by_ctx_free = const_desc->group_by_ctx_free; + } - ecs_strbuf_appendch(&buf, ')'); + /* Ensure that while initially populating the query with tables, they are + * in the right empty/non-empty list. This ensures the query won't miss + * empty/non-empty events for tables that are currently out of sync, but + * change back to being in sync before processing pending events. */ + ecs_run_aperiodic(world, EcsAperiodicEmptyTables); + ecs_table_cache_init(world, &result->cache); + flecs_query_cache_match_tables(world, result); - ecs_strbuf_appendch(&buf, '\n'); + if (const_desc->order_by_callback) { + if (flecs_query_cache_order_by(world, impl, + const_desc->order_by, const_desc->order_by_callback, + const_desc->order_by_table_callback)) + { + goto error; + } } -#ifdef FLECS_LOG - char *str = ecs_strbuf_get(&buf); - flecs_colorize_buf(str, ecs_os_api.flags_ & EcsOsApiLogWithColors, &buf); - ecs_os_free(str); -#endif - return ecs_strbuf_get(&buf); -} + if (entity) { + if (!flecs_query_cache_table_count(result) && result->query->term_count){ + ecs_add_id(world, entity, EcsEmpty); + } + } -char* ecs_rule_str( - const ecs_rule_t *rule) -{ - return ecs_rule_str_w_profile(rule, NULL); + ecs_log_pop_1(); + + return result; +error: + return NULL; } -const ecs_filter_t* ecs_rule_get_filter( - const ecs_rule_t *rule) +ecs_query_cache_table_t* flecs_query_cache_get_table( + ecs_query_cache_t *cache, + ecs_table_t *table) { - return &rule->filter; + return ecs_table_cache_get(&cache->cache, table); } -const char* ecs_rule_parse_vars( - ecs_rule_t *rule, +void ecs_iter_set_group( ecs_iter_t *it, - const char *expr) + uint64_t group_id) { - ecs_poly_assert(rule, ecs_rule_t); ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(expr != NULL, ECS_INVALID_PARAMETER, NULL) - char token[ECS_MAX_TOKEN_SIZE]; - const char *ptr = expr; - bool paren = false; + ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); + ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_PARAMETER, + "cannot set group during iteration"); - const char *name = NULL; - if (rule->filter.entity) { - name = ecs_get_name(rule->filter.world, rule->filter.entity); + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_impl_t *q = flecs_query_impl(qit->query); + ecs_check(q != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_poly_assert(q, ecs_query_t); + ecs_query_cache_t *cache = q->cache; + ecs_check(cache != NULL, ECS_INVALID_PARAMETER, NULL); + + ecs_query_cache_table_list_t *node = flecs_query_cache_get_group( + cache, group_id); + if (!node) { + qit->node = NULL; + qit->last = NULL; + return; } - ptr = ecs_parse_ws_eol(ptr); - if (!ptr[0]) { - return ptr; + ecs_query_cache_table_match_t *first = node->first; + if (first) { + qit->node = node->first; + qit->last = node->last; + } else { + qit->node = NULL; + qit->last = NULL; } + +error: + return; +} - if (ptr[0] == '(') { - paren = true; - ptr = ecs_parse_ws_eol(ptr + 1); - if (ptr[0] == ')') { - return ptr + 1; - } +const ecs_query_group_info_t* ecs_query_get_group_info( + const ecs_query_t *query, + uint64_t group_id) +{ + flecs_poly_assert(query, ecs_query_t); + ecs_query_cache_table_list_t *node = flecs_query_cache_get_group( + flecs_query_impl(query)->cache, group_id); + if (!node) { + return NULL; + } + + return &node->info; +} + +void* ecs_query_get_group_ctx( + const ecs_query_t *query, + uint64_t group_id) +{ + flecs_poly_assert(query, ecs_query_t); + const ecs_query_group_info_t *info = ecs_query_get_group_info( + query, group_id); + if (!info) { + return NULL; + } else { + return info->ctx; } +} - do { - ptr = ecs_parse_ws_eol(ptr); - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } +/** + * @file query/engine/cache_iter.c + * @brief Compile query term. + */ - int var = ecs_rule_find_var(rule, token); - if (var == -1) { - ecs_parser_error(name, expr, (ptr - expr), - "unknown variable '%s'", token); - return NULL; - } - ptr = ecs_parse_ws_eol(ptr); - if (ptr[0] != ':') { - ecs_parser_error(name, expr, (ptr - expr), - "missing ':'"); - return NULL; - } +static +void flecs_query_update_node_up_trs( + const ecs_query_run_ctx_t *ctx, + ecs_query_cache_table_match_t *node) +{ + ecs_termset_t fields = node->up_fields & node->set_fields; + if (fields) { + const ecs_query_impl_t *impl = ctx->query; + const ecs_query_t *q = &impl->pub; + ecs_query_cache_t *cache = impl->cache; + int32_t i, field_count = q->field_count; + for (i = 0; i < field_count; i ++) { + if (!(fields & (1llu << i))) { + continue; + } - ptr = ecs_parse_ws_eol(ptr + 1); - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; + ecs_entity_t src = node->sources[i]; + if (src) { + const ecs_table_record_t *tr = node->trs[i]; + ecs_record_t *r = flecs_entities_get(ctx->world, src); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + if (r->table != tr->hdr.table) { + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_assert(idr->id == q->ids[i], ECS_INTERNAL_ERROR, NULL); + tr = node->trs[i] = flecs_id_record_get_table(idr, r->table); + if (cache->field_map) { + ctx->it->trs[cache->field_map[i]] = tr; + } + } + } } + } +} - ecs_entity_t val = ecs_lookup_fullpath(rule->filter.world, token); - if (!val) { - ecs_parser_error(name, expr, (ptr - expr), - "unresolved entity '%s'", token); - return NULL; - } +static +ecs_query_cache_table_match_t* flecs_query_cache_next( + const ecs_query_run_ctx_t *ctx) +{ + ecs_iter_t *it = ctx->it; + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_cache_table_match_t *node = qit->node; + ecs_query_cache_table_match_t *prev = qit->prev; - ecs_iter_set_var(it, var, val); + if (prev != qit->last) { + ecs_assert(node != NULL, ECS_INTERNAL_ERROR, NULL); + ctx->vars[0].range.table = node->table; + it->group_id = node->group_id; + qit->node = node->next; + qit->prev = node; + return node; + } - ptr = ecs_parse_ws_eol(ptr); - if (ptr[0] == ')') { - if (!paren) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected closing parenthesis"); - return NULL; - } + return NULL; +} - ptr ++; - break; - } else if (ptr[0] == ',') { - ptr ++; - } else if (!ptr[0]) { - if (paren) { - ecs_parser_error(name, expr, (ptr - expr), - "missing closing parenthesis"); - return NULL; - } - break; - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected , or end of string"); +static +ecs_query_cache_table_match_t* flecs_query_test( + const ecs_query_run_ctx_t *ctx, + bool redo) +{ + ecs_iter_t *it = ctx->it; + if (!redo) { + ecs_var_t *var = &ctx->vars[0]; + ecs_table_t *table = var->range.table; + ecs_assert(table != NULL, ECS_INVALID_OPERATION, + "the variable set on the iterator is missing a table"); + + ecs_query_cache_table_t *qt = flecs_query_cache_get_table( + ctx->query->cache, table); + if (!qt) { return NULL; } - } while (true); - return ptr; -error: - return NULL; -} + ecs_query_iter_t *qit = &it->priv_.iter.query; + qit->prev = NULL; + qit->node = qt->first; + qit->last = qt->last; + } -#endif + return flecs_query_cache_next(ctx); +} -/** - * @file addons/rules/compile.c - * @brief Compile rule program from filter. - */ +static +void flecs_query_cache_init_mapped_fields( + const ecs_query_run_ctx_t *ctx, + ecs_query_cache_table_match_t *node) +{ + ecs_iter_t *it = ctx->it; + const ecs_query_impl_t *impl = ctx->query; + ecs_query_cache_t *cache = impl->cache; + int32_t i, field_count = cache->query->field_count; + int8_t *field_map = cache->field_map; + for (i = 0; i < field_count; i ++) { + int8_t field_index = field_map[i]; + it->trs[field_index] = node->trs[i]; -#ifdef FLECS_RULES + it->ids[field_index] = node->ids[i]; + it->sources[field_index] = node->sources[i]; -#define FlecsRuleOrMarker ((int16_t)-2) /* Marks instruction in OR chain */ + ecs_termset_t bit = (ecs_termset_t)(1u << i); + ecs_termset_t field_bit = (ecs_termset_t)(1u << field_index); -ecs_rule_lbl_t flecs_itolbl(int64_t val) { - return flecs_ito(int16_t, val); + ECS_TERMSET_COND(it->set_fields, field_bit, node->set_fields & bit); + ECS_TERMSET_COND(it->up_fields, field_bit, node->up_fields & bit); + } } -static -ecs_var_id_t flecs_itovar(int64_t val) { - return flecs_ito(uint8_t, val); -} +/* Iterate cache for query that's partially cached */ +bool flecs_query_cache_search( + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_cache_table_match_t *node = flecs_query_cache_next(ctx); + if (!node) { + return false; + } -static -ecs_var_id_t flecs_utovar(uint64_t val) { - return flecs_uto(uint8_t, val); -} + flecs_query_cache_init_mapped_fields(ctx, node); + ctx->vars[0].range.count = node->count; + ctx->vars[0].range.offset = node->offset; -#ifdef FLECS_DEBUG -#define flecs_set_var_label(var, lbl) (var)->label = lbl -#else -#define flecs_set_var_label(var, lbl) -#endif + flecs_query_update_node_up_trs(ctx, node); -static -bool flecs_rule_is_builtin_pred( - ecs_term_t *term) + return true; +} + +/* Iterate cache for query that's entirely cached */ +bool flecs_query_is_cache_search( + const ecs_query_run_ctx_t *ctx) { - if (term->first.flags & EcsIsEntity) { - ecs_entity_t id = term->first.id; - if (id == EcsPredEq || id == EcsPredMatch || id == EcsPredLookup) { - return true; - } + ecs_query_cache_table_match_t *node = flecs_query_cache_next(ctx); + if (!node) { + return false; } - return false; + + ecs_iter_t *it = ctx->it; + it->trs = node->trs; + it->ids = node->ids; + it->sources = node->sources; + it->set_fields = node->set_fields; + it->up_fields = node->up_fields; + + flecs_query_update_node_up_trs(ctx, node); + + return true; } -bool flecs_rule_is_written( - ecs_var_id_t var_id, - uint64_t written) +/* Test if query that is entirely cached matches constrained $this */ +bool flecs_query_cache_test( + const ecs_query_run_ctx_t *ctx, + bool redo) { - if (var_id == EcsVarNone) { - return true; + ecs_query_cache_table_match_t *node = flecs_query_test(ctx, redo); + if (!node) { + return false; } - ecs_assert(var_id < EcsRuleMaxVarCount, ECS_INTERNAL_ERROR, NULL); - return (written & (1ull << var_id)) != 0; + flecs_query_cache_init_mapped_fields(ctx, node); + flecs_query_update_node_up_trs(ctx, node); + + return true; } -static -void flecs_rule_write( - ecs_var_id_t var_id, - uint64_t *written) +/* Test if query that is entirely cached matches constrained $this */ +bool flecs_query_is_cache_test( + const ecs_query_run_ctx_t *ctx, + bool redo) { - ecs_assert(var_id < EcsRuleMaxVarCount, ECS_INTERNAL_ERROR, NULL); - *written |= (1ull << var_id); + ecs_query_cache_table_match_t *node = flecs_query_test(ctx, redo); + if (!node) { + return false; + } + + ecs_iter_t *it = ctx->it; + it->trs = node->trs; + it->ids = node->ids; + it->sources = node->sources; + + flecs_query_update_node_up_trs(ctx, node); + + return true; } +/** + * @file query/engine/cache_order_by.c + * @brief Order by implementation + */ + + +ECS_SORT_TABLE_WITH_COMPARE(_, flecs_query_cache_sort_table_generic, order_by, static) + static -void flecs_rule_write_ctx( - ecs_var_id_t var_id, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) +void flecs_query_cache_sort_table( + ecs_world_t *world, + ecs_table_t *table, + int32_t column_index, + ecs_order_by_action_t compare, + ecs_sort_table_action_t sort) { - bool is_written = flecs_rule_is_written(var_id, ctx->written); - flecs_rule_write(var_id, &ctx->written); - if (!is_written) { - if (cond_write) { - flecs_rule_write(var_id, &ctx->cond_written); - } + int32_t count = ecs_table_count(table); + if (!count) { + /* Nothing to sort */ + return; + } + + if (count < 2) { + return; } -} -ecs_flags16_t flecs_rule_ref_flags( - ecs_flags16_t flags, - ecs_flags16_t kind) -{ - return (flags >> kind) & (EcsRuleIsVar | EcsRuleIsEntity); + ecs_entity_t *entities = table->data.entities; + void *ptr = NULL; + int32_t size = 0; + if (column_index != -1) { + ecs_column_t *column = &table->data.columns[column_index]; + ecs_type_info_t *ti = column->ti; + size = ti->size; + ptr = column->data; + } + + if (sort) { + sort(world, table, entities, ptr, size, 0, count - 1, compare); + } else { + flecs_query_cache_sort_table_generic( + world, table, entities, ptr, size, 0, count - 1, compare); + } } -bool flecs_ref_is_written( - const ecs_rule_op_t *op, - const ecs_rule_ref_t *ref, - ecs_flags16_t kind, - uint64_t written) +/* Helper struct for building sorted table ranges */ +typedef struct sort_helper_t { + ecs_query_cache_table_match_t *match; + ecs_entity_t *entities; + const void *ptr; + int32_t row; + int32_t elem_size; + int32_t count; + bool shared; +} sort_helper_t; + +static +const void* ptr_from_helper( + sort_helper_t *helper) { - ecs_flags16_t flags = flecs_rule_ref_flags(op->flags, kind); - if (flags & EcsRuleIsEntity) { - ecs_assert(!(flags & EcsRuleIsVar), ECS_INTERNAL_ERROR, NULL); - if (ref->entity) { - return true; - } - } else if (flags & EcsRuleIsVar) { - return flecs_rule_is_written(ref->var, written); + ecs_assert(helper->row < helper->count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(helper->elem_size >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(helper->row >= 0, ECS_INTERNAL_ERROR, NULL); + if (helper->shared) { + return helper->ptr; + } else { + return ECS_ELEM(helper->ptr, helper->elem_size, helper->row); } - return false; } static -bool flecs_rule_var_is_anonymous( - const ecs_rule_t *rule, - ecs_var_id_t var_id) +ecs_entity_t e_from_helper( + sort_helper_t *helper) { - ecs_rule_var_t *var = &rule->vars[var_id]; - return var->anonymous; + if (helper->row < helper->count) { + return helper->entities[helper->row]; + } else { + return 0; + } } static -ecs_var_id_t flecs_rule_find_var_id( - const ecs_rule_t *rule, - const char *name, - ecs_var_kind_t kind) +void flecs_query_cache_build_sorted_table_range( + ecs_query_cache_t *cache, + ecs_query_cache_table_list_t *list) { - ecs_assert(name != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_world_t *world = cache->query->world; + ecs_assert(!(world->flags & EcsWorldMultiThreaded), ECS_UNSUPPORTED, + "cannot sort query in multithreaded mode"); - /* Backwards compatibility */ - if (!ecs_os_strcmp(name, "This")) { - name = "this"; + ecs_entity_t id = cache->order_by; + ecs_order_by_action_t compare = cache->order_by_callback; + int32_t table_count = list->info.table_count; + if (!table_count) { + return; } - if (kind == EcsVarTable) { - if (!ecs_os_strcmp(name, EcsThisName)) { - if (rule->has_table_this) { - return 0; + ecs_vec_init_if_t(&cache->table_slices, ecs_query_cache_table_match_t); + int32_t to_sort = 0; + int32_t order_by_term = cache->order_by_term; + + sort_helper_t *helper = flecs_alloc_n( + &world->allocator, sort_helper_t, table_count); + ecs_query_cache_table_match_t *cur, *end = list->last->next; + for (cur = list->first; cur != end; cur = cur->next) { + ecs_table_t *table = cur->table; + + if (ecs_table_count(table) == 0) { + continue; + } + + if (id) { + const ecs_term_t *term = &cache->query->terms[order_by_term]; + int32_t field = term->field_index; + ecs_size_t size = cache->query->sizes[field]; + ecs_entity_t src = cur->sources[field]; + if (src == 0) { + int32_t column_index = cur->trs[field]->column; + ecs_column_t *column = &table->data.columns[column_index]; + helper[to_sort].ptr = column->data; + helper[to_sort].elem_size = size; + helper[to_sort].shared = false; } else { - return EcsVarNone; + ecs_record_t *r = flecs_entities_get(world, src); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + + if (term->src.id & EcsUp) { + ecs_entity_t base = 0; + ecs_search_relation(world, r->table, 0, id, + EcsIsA, term->src.id & EcsTraverseFlags, &base, 0, 0); + if (base && base != src) { /* Component could be inherited */ + r = flecs_entities_get(world, base); + } + } + + helper[to_sort].ptr = ecs_table_get_id( + world, r->table, id, ECS_RECORD_TO_ROW(r->row)); + helper[to_sort].elem_size = size; + helper[to_sort].shared = true; } + ecs_assert(helper[to_sort].ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(helper[to_sort].elem_size != 0, ECS_INTERNAL_ERROR, NULL); + } else { + helper[to_sort].ptr = NULL; + helper[to_sort].elem_size = 0; + helper[to_sort].shared = false; } - if (!flecs_name_index_is_init(&rule->tvar_index)) { - return EcsVarNone; + helper[to_sort].match = cur; + helper[to_sort].entities = table->data.entities; + helper[to_sort].row = 0; + helper[to_sort].count = ecs_table_count(table); + to_sort ++; + } + + ecs_assert(to_sort != 0, ECS_INTERNAL_ERROR, NULL); + + bool proceed; + do { + int32_t j, min = 0; + proceed = true; + + ecs_entity_t e1; + while (!(e1 = e_from_helper(&helper[min]))) { + min ++; + if (min == to_sort) { + proceed = false; + break; + } } - uint64_t index = flecs_name_index_find( - &rule->tvar_index, name, 0, 0); - if (index == 0) { - return EcsVarNone; + if (!proceed) { + break; } - return flecs_utovar(index); - } - if (kind == EcsVarEntity) { - if (!flecs_name_index_is_init(&rule->evar_index)) { - return EcsVarNone; + for (j = min + 1; j < to_sort; j++) { + ecs_entity_t e2 = e_from_helper(&helper[j]); + if (!e2) { + continue; + } + + const void *ptr1 = ptr_from_helper(&helper[min]); + const void *ptr2 = ptr_from_helper(&helper[j]); + + if (compare(e1, ptr1, e2, ptr2) > 0) { + min = j; + e1 = e_from_helper(&helper[min]); + } } - uint64_t index = flecs_name_index_find( - &rule->evar_index, name, 0, 0); - if (index == 0) { - return EcsVarNone; + sort_helper_t *cur_helper = &helper[min]; + if (!cur || cur->trs != cur_helper->match->trs) { + cur = ecs_vec_append_t(NULL, &cache->table_slices, + ecs_query_cache_table_match_t); + *cur = *(cur_helper->match); + cur->offset = cur_helper->row; + cur->count = 1; + } else { + cur->count ++; } - return flecs_utovar(index); - } - ecs_assert(kind == EcsVarAny, ECS_INTERNAL_ERROR, NULL); + cur_helper->row ++; + } while (proceed); - /* If searching for any kind of variable, start with most specific */ - ecs_var_id_t index = flecs_rule_find_var_id(rule, name, EcsVarEntity); - if (index != EcsVarNone) { - return index; + /* Iterate through the vector of slices to set the prev/next ptrs. This + * can't be done while building the vector, as reallocs may occur */ + int32_t i, count = ecs_vec_count(&cache->table_slices); + ecs_query_cache_table_match_t *nodes = ecs_vec_first(&cache->table_slices); + for (i = 0; i < count; i ++) { + nodes[i].prev = &nodes[i - 1]; + nodes[i].next = &nodes[i + 1]; } - return flecs_rule_find_var_id(rule, name, EcsVarTable); + nodes[0].prev = NULL; + nodes[i - 1].next = NULL; + + flecs_free_n(&world->allocator, sort_helper_t, table_count, helper); } -int32_t ecs_rule_var_count( - const ecs_rule_t *rule) +void flecs_query_cache_build_sorted_tables( + ecs_query_cache_t *cache) { - return rule->var_pub_count; + ecs_vec_clear(&cache->table_slices); + + if (cache->group_by_callback) { + /* Populate sorted node list in grouping order */ + ecs_query_cache_table_match_t *cur = cache->list.first; + if (cur) { + do { + /* Find list for current group */ + uint64_t group_id = cur->group_id; + ecs_query_cache_table_list_t *list = ecs_map_get_deref( + &cache->groups, ecs_query_cache_table_list_t, group_id); + ecs_assert(list != NULL, ECS_INTERNAL_ERROR, NULL); + + /* Sort tables in current group */ + flecs_query_cache_build_sorted_table_range(cache, list); + + /* Find next group to sort */ + cur = list->last->next; + } while (cur); + } + } else { + flecs_query_cache_build_sorted_table_range(cache, &cache->list); + } } -int32_t ecs_rule_find_var( - const ecs_rule_t *rule, - const char *name) +void flecs_query_cache_sort_tables( + ecs_world_t *world, + ecs_query_impl_t *impl) { - ecs_var_id_t var_id = flecs_rule_find_var_id(rule, name, EcsVarEntity); - if (var_id == EcsVarNone) { - if (rule->filter.flags & EcsFilterMatchThis) { - if (!ecs_os_strcmp(name, "This")) { - name = "this"; + ecs_query_cache_t *cache = impl->cache; + ecs_order_by_action_t compare = cache->order_by_callback; + if (!compare) { + return; + } + + ecs_sort_table_action_t sort = cache->order_by_table_callback; + + ecs_entity_t order_by = cache->order_by; + int32_t order_by_term = cache->order_by_term; + + /* Iterate over non-empty tables. Don't bother with empty tables as they + * have nothing to sort */ + + bool tables_sorted = false; + + ecs_id_record_t *idr = flecs_id_record_get(world, order_by); + ecs_table_cache_iter_t it; + ecs_query_cache_table_t *qt; + flecs_table_cache_iter(&cache->cache, &it); + + while ((qt = flecs_table_cache_next(&it, ecs_query_cache_table_t))) { + ecs_table_t *table = qt->hdr.table; + bool dirty = false; + + if (flecs_query_check_table_monitor(impl, qt, 0)) { + tables_sorted = true; + dirty = true; + } + + int32_t column = -1; + if (order_by) { + if (flecs_query_check_table_monitor(impl, qt, order_by_term + 1)) { + dirty = true; } - if (!ecs_os_strcmp(name, EcsThisName)) { - var_id = 0; + + if (dirty) { + column = -1; + + const ecs_table_record_t *tr = flecs_id_record_get_table( + idr, table); + if (tr) { + column = tr->column; + } + + if (column == -1) { + /* Component is shared, no sorting is needed */ + dirty = false; + } } } - if (var_id == EcsVarNone) { - return -1; + + if (!dirty) { + continue; } - } - return (int32_t)var_id; -} -const char* ecs_rule_var_name( - const ecs_rule_t *rule, - int32_t var_id) -{ - if (var_id) { - return rule->vars[var_id].name; - } else { - return EcsThisName; + /* Something has changed, sort the table. Prefers using + * flecs_query_cache_sort_table when available */ + flecs_query_cache_sort_table(world, table, column, compare, sort); + tables_sorted = true; } -} -bool ecs_rule_var_is_entity( - const ecs_rule_t *rule, - int32_t var_id) -{ - return rule->vars[var_id].kind == EcsVarEntity; + if (tables_sorted || cache->match_count != cache->prev_match_count) { + flecs_query_cache_build_sorted_tables(cache); + cache->match_count ++; /* Increase version if tables changed */ + } } -static -const char* flecs_term_id_var_name( - ecs_term_id_t *term_id) -{ - if (!(term_id->flags & EcsIsVariable)) { - return NULL; - } +/** + * @file query/engine/change_detection.c + * @brief Compile query term. + */ - if (term_id->id == EcsThis) { - return EcsThisName; - } - return term_id->name; -} +typedef struct { + ecs_table_t *table; + int32_t column; +} flecs_table_column_t; static -bool flecs_term_id_is_wildcard( - ecs_term_id_t *term_id) +void flecs_query_get_column_for_field( + const ecs_query_t *q, + ecs_query_cache_table_match_t *match, + int32_t field, + flecs_table_column_t *out) { - if ((term_id->flags & EcsIsVariable) && - ((term_id->id == EcsWildcard) || (term_id->id == EcsAny))) - { - return true; - } - return false; + ecs_assert(field >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(field < q->field_count, ECS_INTERNAL_ERROR, NULL); + (void)q; + + const ecs_table_record_t *tr = match->trs[field]; + ecs_table_t *table = tr->hdr.table; + int32_t column = tr->column; + + out->table = table; + out->column = column; } +/* Get match monitor. Monitors are used to keep track of whether components + * matched by the query in a table have changed. */ static -ecs_var_id_t flecs_rule_add_var( - ecs_rule_t *rule, - const char *name, - ecs_vec_t *vars, - ecs_var_kind_t kind) +bool flecs_query_get_match_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match) { - const char *dot = NULL; - if (name) { - dot = strchr(name, '.'); - if (dot) { - kind = EcsVarEntity; /* lookup variables are always entities */ - } + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); + if (match->monitor) { + return false; } - ecs_hashmap_t *var_index = NULL; - ecs_var_id_t var_id = EcsVarNone; - if (name) { - if (kind == EcsVarAny) { - var_id = flecs_rule_find_var_id(rule, name, EcsVarEntity); - if (var_id != EcsVarNone) { - return var_id; - } + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *monitor = flecs_balloc(&cache->allocators.monitors); + monitor[0] = 0; - var_id = flecs_rule_find_var_id(rule, name, EcsVarTable); - if (var_id != EcsVarNone) { - return var_id; - } + /* Mark terms that don't need to be monitored. This saves time when reading + * and/or updating the monitor. */ + const ecs_query_t *q = cache->query; + int32_t i, field = -1, term_count = q->term_count; + flecs_table_column_t tc; - kind = EcsVarTable; - } else { - var_id = flecs_rule_find_var_id(rule, name, kind); - if (var_id != EcsVarNone) { - return var_id; + for (i = 0; i < term_count; i ++) { + if (field == q->terms[i].field_index) { + if (monitor[field + 1] != -1) { + continue; } } - if (kind == EcsVarTable) { - var_index = &rule->tvar_index; - } else { - var_index = &rule->evar_index; - } + field = q->terms[i].field_index; + monitor[field + 1] = -1; - /* If we're creating an entity var, check if it has a table variant */ - if (kind == EcsVarEntity && var_id == EcsVarNone) { - var_id = flecs_rule_find_var_id(rule, name, EcsVarTable); + /* If term isn't read, don't monitor */ + if (q->terms[i].inout != EcsIn && + q->terms[i].inout != EcsInOut && + q->terms[i].inout != EcsInOutDefault) { + continue; } - } - - ecs_rule_var_t *var; - ecs_var_id_t result; - if (vars) { - var = ecs_vec_append_t(NULL, vars, ecs_rule_var_t); - result = var->id = flecs_itovar(ecs_vec_count(vars)); - } else { - ecs_dbg_assert(rule->var_count < rule->var_size, - ECS_INTERNAL_ERROR, NULL); - var = &rule->vars[rule->var_count]; - result = var->id = flecs_itovar(rule->var_count); - rule->var_count ++; - } - var->kind = flecs_ito(int8_t, kind); - var->name = name; - var->table_id = var_id; - var->base_id = 0; - var->lookup = NULL; - flecs_set_var_label(var, NULL); - - if (name) { - flecs_name_index_init_if(var_index, NULL); - flecs_name_index_ensure(var_index, var->id, name, 0, 0); - var->anonymous = name[0] == '_'; + /* Don't track fields that aren't set */ + if (!(match->set_fields & (1llu << field))) { + continue; + } - /* Handle variables that require a by-name lookup, e.g. $this.wheel */ - if (dot != NULL) { - ecs_assert(var->table_id == EcsVarNone, ECS_INTERNAL_ERROR, NULL); - var->lookup = dot + 1; + flecs_query_get_column_for_field(q, match, field, &tc); + if (tc.column == -1) { + continue; /* Don't track terms that aren't stored */ } + + monitor[field + 1] = 0; } - return result; -} + match->monitor = monitor; -static -ecs_var_id_t flecs_rule_add_var_for_term_id( - ecs_rule_t *rule, - ecs_term_id_t *term_id, - ecs_vec_t *vars, - ecs_var_kind_t kind) -{ - const char *name = flecs_term_id_var_name(term_id); - if (!name) { - return EcsVarNone; - } + impl->pub.flags |= EcsQueryHasMonitor; - return flecs_rule_add_var(rule, name, vars, kind); + return true; } -/* This function walks over terms to discover which variables are used in the - * query. It needs to provide the following functionality: - * - create table vars for all variables used as source - * - create entity vars for all variables not used as source - * - create entity vars for all non-$this vars - * - create anonymous vars to store the content of wildcards - * - create anonymous vars to store result of lookups (for $var.child_name) - * - create anonymous vars for resolving component inheritance - * - create array that stores the source variable for each field - * - ensure table vars for non-$this variables are anonymous - * - ensure variables created inside scopes are anonymous - * - place anonymous variables after public variables in vars array - */ +/* Get monitor for fixed query terms. Fixed terms are handled separately as they + * don't require a query cache, and fixed terms aren't stored in the cache. */ static -int flecs_rule_discover_vars( - ecs_stage_t *stage, - ecs_rule_t *rule) +bool flecs_query_get_fixed_monitor( + ecs_query_impl_t *impl, + bool check) { - ecs_vec_t *vars = &stage->variables; /* Buffer to reduce allocs */ - ecs_vec_reset_t(NULL, vars, ecs_rule_var_t); - - ecs_term_t *terms = rule->filter.terms; - int32_t a, i, anonymous_count = 0, count = rule->filter.term_count; - int32_t anonymous_table_count = 0, scope = 0, scoped_var_index = 0; - bool table_this = false, entity_before_table_this = false; + ecs_query_t *q = &impl->pub; + ecs_world_t *world = q->world; + ecs_term_t *terms = q->terms; + int32_t i, term_count = q->term_count; - /* For This table lookups during discovery. This will be overwritten after - * discovery with whether the rule actually has a This table variable. */ - rule->has_table_this = true; + if (!impl->monitor) { + impl->monitor = flecs_alloc_n(&impl->stage->allocator, + int32_t, q->field_count); + check = false; /* If the monitor is new, initialize it with dirty state */ + } - for (i = 0; i < count; i ++) { + for (i = 0; i < term_count; i ++) { ecs_term_t *term = &terms[i]; - ecs_term_id_t *first = &term->first; - ecs_term_id_t *second = &term->second; - ecs_term_id_t *src = &term->src; + int16_t field_index = term->field_index; - if (first->id == EcsScopeOpen) { - /* Keep track of which variables are first used in scope, so that we - * can mark them as anonymous. Terms inside a scope are collapsed - * into a single result, which means that outside of the scope the - * value of those variables is undefined. */ - if (!scope) { - scoped_var_index = ecs_vec_count(vars); - } - scope ++; - continue; - } else if (first->id == EcsScopeClose) { - if (!--scope) { - /* Any new variables declared after entering a scope should be - * marked as anonymous. */ - int32_t v; - for (v = scoped_var_index; v < ecs_vec_count(vars); v ++) { - ecs_vec_get_t(vars, ecs_rule_var_t, v)->anonymous = true; - } - } - continue; + if (!(q->read_fields & flecs_ito(uint32_t, 1 << field_index))) { + continue; /* If term doesn't read data there's nothing to track */ } - ecs_var_id_t first_var_id = flecs_rule_add_var_for_term_id( - rule, first, vars, EcsVarEntity); - if (first_var_id == EcsVarNone) { - /* If first is not a variable, check if we need to insert anonymous - * variable for resolving component inheritance */ - if (term->flags & EcsTermIdInherited) { - anonymous_count += 2; /* table & entity variable */ - } - - /* If first is a wildcard, insert anonymous variable */ - if (flecs_term_id_is_wildcard(first)) { - anonymous_count ++; - } + if (!(term->src.id & EcsIsEntity)) { + continue; /* Not a term with a fixed source */ } - if ((src->flags & EcsIsVariable) && (src->id != EcsThis)) { - const char *var_name = flecs_term_id_var_name(src); - if (var_name) { - ecs_var_id_t var_id = flecs_rule_find_var_id( - rule, var_name, EcsVarEntity); - if (var_id == EcsVarNone || var_id == first_var_id) { - var_id = flecs_rule_add_var( - rule, var_name, vars, EcsVarEntity); - } - - if (var_id != EcsVarNone) { - /* Mark variable as one for which we need to create a table - * variable. Don't create table variable now, so that we can - * store it in the non-public part of the variable array. */ - ecs_rule_var_t *var = ecs_vec_get_t( - vars, ecs_rule_var_t, (int32_t)var_id - 1); - ecs_assert(var != NULL, ECS_INTERNAL_ERROR, NULL); - if (!var->lookup) { - var->kind = EcsVarAny; - anonymous_table_count ++; - } - - if (!(term->flags & EcsTermNoData)) { - /* Can't have an anonymous variable as source of a term - * that returns a component. We need to return each - * instance of the component, whereas anonymous - * variables are not guaranteed to be resolved to - * individual entities. */ - if (var->anonymous) { - ecs_err( - "can't use anonymous variable '%s' as source of " - "data term", var->name); - goto error; - } - } - - /* Track which variable ids are used as field source */ - if (!rule->src_vars) { - rule->src_vars = ecs_os_calloc_n(ecs_var_id_t, - rule->filter.field_count); - } + ecs_entity_t src = ECS_TERM_REF_ID(&term->src); + ecs_assert(src != 0, ECS_INTERNAL_ERROR, NULL); - rule->src_vars[term->field_index] = var_id; - } - } else { - if (flecs_term_id_is_wildcard(src)) { - anonymous_count ++; - } - } - } else if ((src->flags & EcsIsVariable) && (src->id == EcsThis)) { - if (flecs_rule_is_builtin_pred(term) && term->oper == EcsOr) { - flecs_rule_add_var(rule, EcsThisName, vars, EcsVarEntity); - } + ecs_record_t *r = flecs_entities_get(world, src); + if (!r || !r->table) { + continue; /* Entity is empty, nothing to track */ } - if (flecs_rule_add_var_for_term_id( - rule, second, vars, EcsVarEntity) == EcsVarNone) - { - /* If second is a wildcard, insert anonymous variable */ - if (flecs_term_id_is_wildcard(second)) { - anonymous_count ++; - } + ecs_id_record_t *idr = flecs_id_record_get(world, term->id); + if (!idr) { + continue; /* If id doesn't exist, entity can't have it */ } - if (src->flags & EcsIsVariable && second->flags & EcsIsVariable) { - if (term->flags & EcsTermTransitive) { - /* Anonymous variable to store temporary id for finding - * targets for transitive relationship, see compile_term. */ - anonymous_count ++; - } + ecs_table_record_t *tr = flecs_id_record_get_table(idr, r->table); + if (!tr) { + continue; /* Entity doesn't have the component */ } - /* Track if a This entity variable is used before a potential This table - * variable. If this happens, the rule has no This table variable */ - if (src->id == EcsThis) { - table_this = true; - } - if (first->id == EcsThis || second->id == EcsThis) { - if (!table_this) { - entity_before_table_this = true; + /* Copy/check column dirty state from table */ + int32_t *dirty_state = flecs_table_get_dirty_state(world, r->table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + + if (!check) { + impl->monitor[field_index] = dirty_state[tr->column + 1]; + } else { + if (impl->monitor[field_index] != dirty_state[tr->column + 1]) { + return true; } } } - int32_t var_count = ecs_vec_count(vars); - ecs_var_id_t placeholder = EcsVarNone - 1; - bool replace_placeholders = false; + return !check; +} - /* Ensure lookup variables have table and/or entity variables */ - for (i = 0; i < var_count; i ++) { - ecs_rule_var_t *var = ecs_vec_get_t(vars, ecs_rule_var_t, i); - if (var->lookup) { - char *var_name = ecs_os_strdup(var->name); - var_name[var->lookup - var->name - 1] = '\0'; +bool flecs_query_update_fixed_monitor( + ecs_query_impl_t *impl) +{ + return flecs_query_get_fixed_monitor(impl, false); +} - ecs_var_id_t base_table_id = flecs_rule_find_var_id( - rule, var_name, EcsVarTable); - if (base_table_id != EcsVarNone) { - var->table_id = base_table_id; - } else if (anonymous_table_count) { - /* Scan for implicit anonymous table variables that haven't been - * inserted yet (happens after this step). Doing this here vs. - * ensures that anonymous variables are appended at the end of - * the variable array, while also ensuring that variable ids are - * stable (no swapping of table var ids that are in use). */ - for (a = 0; a < var_count; a ++) { - ecs_rule_var_t *avar = ecs_vec_get_t( - vars, ecs_rule_var_t, a); - if (avar->kind == EcsVarAny) { - if (!ecs_os_strcmp(avar->name, var_name)) { - base_table_id = (ecs_var_id_t)(a + 1); - break; - } - } - } - if (base_table_id != EcsVarNone) { - /* Set marker so we can set the new table id afterwards */ - var->table_id = placeholder; - replace_placeholders = true; - } - } +bool flecs_query_check_fixed_monitor( + ecs_query_impl_t *impl) +{ + return flecs_query_get_fixed_monitor(impl, true); +} - ecs_var_id_t base_entity_id = flecs_rule_find_var_id( - rule, var_name, EcsVarEntity); - if (base_entity_id == EcsVarNone) { - /* Get name from table var (must exist). We can't use allocated - * name since variables don't own names. */ - const char *base_name = NULL; - if (base_table_id) { - ecs_rule_var_t *base_table_var = ecs_vec_get_t( - vars, ecs_rule_var_t, (int32_t)base_table_id - 1); - base_name = base_table_var->name; - } else { - base_name = EcsThisName; - } - base_entity_id = flecs_rule_add_var( - rule, base_name, vars, EcsVarEntity); - var = ecs_vec_get_t(vars, ecs_rule_var_t, i); - } +/* Check if single match term has changed */ +static +bool flecs_query_check_match_monitor_term( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match, + int32_t field) +{ + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - var->base_id = base_entity_id; + if (flecs_query_get_match_monitor(impl, match)) { + return true; + } - ecs_os_free(var_name); + int32_t *monitor = match->monitor; + int32_t state = monitor[field]; + if (state == -1) { + return false; + } + + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = match->table; + if (table) { + int32_t *dirty_state = flecs_table_get_dirty_state( + cache->query->world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (!field) { + return monitor[0] != dirty_state[0]; } + } else if (!field) { + return false; } - var_count = ecs_vec_count(vars); - /* Add non-This table variables */ - if (anonymous_table_count) { - anonymous_table_count = 0; - for (i = 0; i < var_count; i ++) { - ecs_rule_var_t *var = ecs_vec_get_t(vars, ecs_rule_var_t, i); - if (var->kind == EcsVarAny) { - var->kind = EcsVarEntity; + flecs_table_column_t cur; + flecs_query_get_column_for_field( + &impl->pub, match, field - 1, &cur); + ecs_assert(cur.column != -1, ECS_INTERNAL_ERROR, NULL); - ecs_var_id_t var_id = flecs_rule_add_var( - rule, var->name, vars, EcsVarTable); - ecs_vec_get_t(vars, ecs_rule_var_t, i)->table_id = var_id; - anonymous_table_count ++; + return monitor[field] != flecs_table_get_dirty_state( + cache->query->world, cur.table)[cur.column + 1]; +} + +static +bool flecs_query_check_cache_monitor( + ecs_query_impl_t *impl) +{ + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + + /* If the match count changed, tables got matched/unmatched for the + * cache, so return that the query has changed. */ + if (cache->match_count != cache->prev_match_count) { + return true; + } + + ecs_table_cache_iter_t it; + if (flecs_table_cache_iter(&cache->cache, &it)) { + ecs_query_cache_table_t *qt; + while ((qt = flecs_table_cache_next(&it, ecs_query_cache_table_t))) { + if (flecs_query_check_table_monitor(impl, qt, -1)) { + return true; } } - - var_count = ecs_vec_count(vars); } - /* If any forward references to newly added anonymous tables exist, replace - * them with the actual table variable ids. */ - if (replace_placeholders) { - for (i = 0; i < var_count; i ++) { - ecs_rule_var_t *var = ecs_vec_get_t(vars, ecs_rule_var_t, i); - if (var->table_id == placeholder) { - char *var_name = ecs_os_strdup(var->name); - var_name[var->lookup - var->name - 1] = '\0'; + return false; +} - var->table_id = flecs_rule_find_var_id( - rule, var_name, EcsVarTable); - ecs_assert(var->table_id != EcsVarNone, - ECS_INTERNAL_ERROR, NULL); +static +void flecs_query_init_query_monitors( + ecs_query_impl_t *impl) +{ + /* Change monitor for cache */ + ecs_query_cache_t *cache = impl->cache; + if (cache) { + ecs_query_cache_table_match_t *cur = cache->list.first; - ecs_os_free(var_name); - } + /* Ensure each match has a monitor */ + for (; cur != NULL; cur = cur->next) { + ecs_query_cache_table_match_t *match = + (ecs_query_cache_table_match_t*)cur; + flecs_query_get_match_monitor(impl, match); } } +} - /* Always include spot for This variable, even if rule doesn't use it */ - var_count ++; +static +bool flecs_query_check_match_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match, + const ecs_iter_t *it) +{ + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_rule_var_t *rule_vars = &rule->vars_cache.var; - if ((var_count + anonymous_count) > 1) { - rule_vars = ecs_os_malloc( - (ECS_SIZEOF(ecs_rule_var_t) + ECS_SIZEOF(char*)) * - (var_count + anonymous_count)); + if (flecs_query_get_match_monitor(impl, match)) { + return true; } - rule->vars = rule_vars; - rule->var_count = var_count; - rule->var_pub_count = var_count; - rule->has_table_this = !entity_before_table_this; + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *monitor = match->monitor; + ecs_table_t *table = match->table; + int32_t *dirty_state = NULL; + if (table) { + dirty_state = flecs_table_get_dirty_state( + cache->query->world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (monitor[0] != dirty_state[0]) { + return true; + } + } -#ifdef FLECS_DEBUG - rule->var_size = var_count + anonymous_count; -#endif + const ecs_query_t *query = cache->query; + ecs_world_t *world = query->world; + int32_t i, field_count = query->field_count; + ecs_entity_t *sources = match->sources; + const ecs_table_record_t **trs = it ? it->trs : match->trs; + ecs_flags64_t set_fields = it ? it->set_fields : match->set_fields; + + ecs_assert(trs != NULL, ECS_INTERNAL_ERROR, NULL); - char **var_names = ECS_ELEM(rule_vars, ECS_SIZEOF(ecs_rule_var_t), - var_count + anonymous_count); - rule->var_names = (char**)var_names; - - rule_vars[0].kind = EcsVarTable; - rule_vars[0].name = NULL; - flecs_set_var_label(&rule_vars[0], NULL); - rule_vars[0].id = 0; - rule_vars[0].table_id = EcsVarNone; - rule_vars[0].lookup = NULL; - var_names[0] = ECS_CONST_CAST(char*, rule_vars[0].name); - rule_vars ++; - var_names ++; - var_count --; + for (i = 0; i < field_count; i ++) { + int32_t mon = monitor[i + 1]; + if (mon == -1) { + continue; + } - if (var_count) { - ecs_rule_var_t *user_vars = ecs_vec_first_t(vars, ecs_rule_var_t); - ecs_os_memcpy_n(rule_vars, user_vars, ecs_rule_var_t, var_count); - for (i = 0; i < var_count; i ++) { - var_names[i] = ECS_CONST_CAST(char*, rule_vars[i].name); + if (!(set_fields & (1llu << i))) { + continue; } - } - /* Hide anonymous table variables from application */ - rule->var_pub_count -= anonymous_table_count; + int32_t column = trs[i]->column; + ecs_entity_t src = sources[i]; + if (!src) { + if (column >= 0) { + /* owned component */ + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (mon != dirty_state[column + 1]) { + return true; + } + continue; + } else if (column == -1) { + continue; /* owned but not a component */ + } + } - /* Sanity check to make sure that the public part of the variable array only - * contains entity variables. */ -#ifdef FLECS_DEBUG - for (i = 1 /* first element = $this */; i < rule->var_pub_count; i ++) { - ecs_assert(rule->vars[i].kind == EcsVarEntity, ECS_INTERNAL_ERROR, NULL); + /* Component from non-this source */ + ecs_entity_t fixed_src = match->sources[i]; + ecs_table_t *src_table = ecs_get_table(world, fixed_src); + ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *src_dirty_state = flecs_table_get_dirty_state( + world, src_table); + if (mon != src_dirty_state[column + 1]) { + return true; + } } -#endif - return 0; -error: - return -1; + return false; } -static -ecs_var_id_t flecs_rule_most_specific_var( - ecs_rule_t *rule, - const char *name, - ecs_var_kind_t kind, - ecs_rule_compile_ctx_t *ctx) +/* Check if any term for matched table has changed */ +bool flecs_query_check_table_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_t *table, + int32_t field) { - if (kind == EcsVarTable || kind == EcsVarEntity) { - return flecs_rule_find_var_id(rule, name, kind); - } + ecs_query_cache_table_match_t *cur, *end = table->last->next; - ecs_var_id_t evar = flecs_rule_find_var_id(rule, name, EcsVarEntity); - if ((evar != EcsVarNone) && flecs_rule_is_written(evar, ctx->written)) { - /* If entity variable is available and written to, it contains the most - * specific result and should be used. */ - return evar; + for (cur = table->first; cur != end; cur = cur->next) { + ecs_query_cache_table_match_t *match = + (ecs_query_cache_table_match_t*)cur; + if (field == -1) { + if (flecs_query_check_match_monitor(impl, match, NULL)) { + return true; + } + } else { + if (flecs_query_check_match_monitor_term(impl, match, field)) { + return true; + } + } } - ecs_var_id_t tvar = flecs_rule_find_var_id(rule, name, EcsVarTable); - if ((tvar != EcsVarNone) && !flecs_rule_is_written(tvar, ctx->written)) { - /* If variable of any kind is requested and variable hasn't been written - * yet, write to table variable */ - return tvar; + return false; +} + +void flecs_query_mark_fields_dirty( + ecs_query_impl_t *impl, + ecs_iter_t *it) +{ + ecs_query_t *q = &impl->pub; + + /* Evaluate all writeable non-fixed fields, set fields */ + ecs_termset_t write_fields = + (ecs_termset_t)(q->write_fields & ~q->fixed_fields & it->set_fields); + if (!write_fields || (it->flags & EcsIterNoData)) { + return; } - /* If table var is written, and entity var doesn't exist or is not written, - * return table var */ - if (tvar != EcsVarNone) { - return tvar; - } else { - return evar; + ecs_world_t *world = q->world; + int16_t i, field_count = q->field_count; + for (i = 0; i < field_count; i ++) { + ecs_termset_t field_bit = (ecs_termset_t)(1u << i); + if (!(write_fields & field_bit)) { + continue; /* If term doesn't write data there's nothing to track */ + } + + ecs_entity_t src = it->sources[i]; + ecs_table_t *table; + if (!src) { + table = it->table; + } else { + ecs_record_t *r = flecs_entities_get(world, src); + if (!r || !(table = r->table)) { + continue; + } + + if (q->shared_readonly_fields & flecs_ito(uint32_t, 1 << i)) { + /* Shared fields that aren't marked explicitly as out/inout + * default to readonly */ + continue; + } + } + + int32_t type_index = it->trs[i]->index; + ecs_assert(type_index >= 0, ECS_INTERNAL_ERROR, NULL); + + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *dirty_state = table->dirty_state; + if (!dirty_state) { + continue; + } + + ecs_assert(type_index < table->type.count, ECS_INTERNAL_ERROR, NULL); + int32_t column = table->column_map[type_index]; + dirty_state[column + 1] ++; } } -static -ecs_rule_lbl_t flecs_rule_op_insert( - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx) +void flecs_query_mark_fixed_fields_dirty( + ecs_query_impl_t *impl, + ecs_iter_t *it) { - ecs_rule_op_t *elem = ecs_vec_append_t(NULL, ctx->ops, ecs_rule_op_t); - int32_t count = ecs_vec_count(ctx->ops); - *elem = *op; - if (count > 1) { - if (ctx->cur->lbl_begin == -1) { - /* Variables written by previous instruction can't be written by - * this instruction, except when this is a union. */ - elem->written &= ~elem[-1].written; - } + /* This function marks fields dirty for terms with fixed sources. */ + ecs_query_t *q = &impl->pub; + ecs_termset_t fixed_write_fields = q->write_fields & q->fixed_fields; + if (!fixed_write_fields) { + return; } - elem->next = flecs_itolbl(count); - elem->prev = flecs_itolbl(count - 2); - return flecs_itolbl(count - 1); -} + ecs_world_t *world = q->world; + int32_t i, field_count = q->field_count; + for (i = 0; i < field_count; i ++) { + if (!(fixed_write_fields & flecs_ito(uint32_t, 1 << i))) { + continue; /* If term doesn't write data there's nothing to track */ + } -static -ecs_rule_op_t* flecs_rule_begin_block( - ecs_rule_op_kind_t kind, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t op = {0}; - op.kind = flecs_ito(uint8_t, kind); - ctx->cur->lbl_begin = flecs_rule_op_insert(&op, ctx); - return ecs_vec_get_t(ctx->ops, ecs_rule_op_t, ctx->cur->lbl_begin); -} + ecs_entity_t src = it->sources[i]; + ecs_assert(src != 0, ECS_INTERNAL_ERROR, NULL); + ecs_record_t *r = flecs_entities_get(world, src); + ecs_table_t *table; + if (!r || !(table = r->table)) { + /* If the field is optional, it's possible that it didn't match */ + continue; + } -static -void flecs_rule_end_block( - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t new_op = {0}; - new_op.kind = EcsRuleEnd; - ecs_rule_lbl_t end = flecs_rule_op_insert(&new_op, ctx); - - ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - ops[ctx->cur->lbl_begin].next = end; + int32_t *dirty_state = table->dirty_state; + if (!dirty_state) { + continue; + } - ecs_rule_op_t *end_op = &ops[end]; - if (ctx->cur->lbl_query != -1) { - ecs_rule_op_t *query_op = &ops[ctx->cur->lbl_query]; - end_op->prev = ctx->cur->lbl_begin; - end_op->src = query_op->src; - end_op->first = query_op->first; - end_op->second = query_op->second; - end_op->flags = query_op->flags; - end_op->field_index = query_op->field_index; - } else { - end_op->prev = ctx->cur->lbl_begin; - end_op->field_index = -1; + ecs_assert(it->trs[i]->column >= 0, ECS_INTERNAL_ERROR, NULL); + int32_t column = table->column_map[it->trs[i]->column]; + dirty_state[column + 1] ++; } - - ctx->cur->lbl_begin = -1; } -static -void flecs_rule_begin_block_cond_eval( - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx, - ecs_write_flags_t cond_write_state) +/* Synchronize match monitor with table dirty state */ +void flecs_query_sync_match_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match) { - ecs_var_id_t first_var = EcsVarNone, second_var = EcsVarNone, src_var = EcsVarNone; - ecs_write_flags_t cond_mask = 0; + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - if (flecs_rule_ref_flags(op->flags, EcsRuleFirst) == EcsRuleIsVar) { - first_var = op->first.var; - cond_mask |= (1ull << first_var); - } - if (flecs_rule_ref_flags(op->flags, EcsRuleSecond) == EcsRuleIsVar) { - second_var = op->second.var; - cond_mask |= (1ull << second_var); - } - if (flecs_rule_ref_flags(op->flags, EcsRuleSrc) == EcsRuleIsVar) { - src_var = op->src.var; - cond_mask |= (1ull << src_var); + if (!match->monitor) { + if (impl->pub.flags & EcsQueryHasMonitor) { + flecs_query_get_match_monitor(impl, match); + } else { + return; + } } - /* Variables set in an OR chain are marked as conditional writes. However, - * writes from previous terms in the current OR chain shouldn't be treated - * as variables that are conditionally set, so instead use the write mask - * from before the chain started. */ - if (ctx->ctrlflow->in_or) { - cond_write_state = ctx->ctrlflow->cond_written_or; + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *monitor = match->monitor; + ecs_table_t *table = match->table; + if (table) { + int32_t *dirty_state = flecs_table_get_dirty_state( + cache->query->world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + monitor[0] = dirty_state[0]; /* Did table gain/lose entities */ } - /* If this term uses conditionally set variables, insert instruction that - * jumps over the term if the variables weren't set yet. */ - if (cond_mask & cond_write_state) { - ctx->cur->lbl_cond_eval = flecs_itolbl(ecs_vec_count(ctx->ops)); + ecs_query_t *q = cache->query; + { + flecs_table_column_t tc; + int32_t t, term_count = q->term_count; + for (t = 0; t < term_count; t ++) { + int32_t field = q->terms[t].field_index; + if (monitor[field + 1] == -1) { + continue; + } - ecs_rule_op_t jmp_op = {0}; - jmp_op.kind = EcsRuleIf; + flecs_query_get_column_for_field(q, match, field, &tc); - if ((first_var != EcsVarNone) && cond_write_state & (1ull << first_var)) { - jmp_op.flags |= (EcsRuleIsVar << EcsRuleFirst); - jmp_op.first.var = first_var; + monitor[field + 1] = flecs_table_get_dirty_state( + q->world, tc.table)[tc.column + 1]; } - if ((second_var != EcsVarNone) && cond_write_state & (1ull << second_var)) { - jmp_op.flags |= (EcsRuleIsVar << EcsRuleSecond); - jmp_op.second.var = second_var; + } + + cache->prev_match_count = cache->match_count; +} + +bool ecs_query_changed( + ecs_query_t *q) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_query_impl_t *impl = flecs_query_impl(q); + + ecs_assert(q->cache_kind != EcsQueryCacheNone, ECS_INVALID_OPERATION, + "change detection is only supported on cached queries"); + + /* If query reads terms with fixed sources, check those first as that's + * cheaper than checking entries in the cache. */ + if (impl->monitor) { + if (flecs_query_check_fixed_monitor(impl)) { + return true; } - if ((src_var != EcsVarNone) && cond_write_state & (1ull << src_var)) { - jmp_op.flags |= (EcsRuleIsVar << EcsRuleSrc); - jmp_op.src.var = src_var; + } + + /* Check cache for changes. We can't detect changes for terms that are not + * cached/cacheable and don't have a fixed source, since that requires + * storing state per result, which doesn't happen for uncached queries. */ + if (impl->cache) { + /* If we're checking the cache, make sure that tables are in the correct + * empty/non-empty lists. */ + flecs_process_pending_tables(q->world); + + if (!(impl->pub.flags & EcsQueryHasMonitor)) { + flecs_query_init_query_monitors(impl); } - flecs_rule_op_insert(&jmp_op, ctx); - } else { - ctx->cur->lbl_cond_eval = -1; + /* Check cache entries for changes */ + return flecs_query_check_cache_monitor(impl); } + + return false; } -static -void flecs_rule_end_block_cond_eval( - ecs_rule_compile_ctx_t *ctx) +bool ecs_iter_changed( + ecs_iter_t *it) { - if (ctx->cur->lbl_cond_eval == -1) { - return; - } + ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(it->next == ecs_query_next, ECS_UNSUPPORTED, NULL); + ecs_check(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), + ECS_INVALID_PARAMETER, NULL); - ecs_assert(ctx->cur->lbl_query >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_rule_op_t end_op = {0}; - end_op.kind = EcsRuleEnd; - ecs_rule_lbl_t end = flecs_rule_op_insert(&end_op, ctx); + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_impl_t *impl = flecs_query_impl(qit->query); + ecs_query_t *q = &impl->pub; - ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - ops[ctx->cur->lbl_cond_eval].next = end; + /* First check for changes for terms with fixed sources, if query has any */ + if (q->read_fields & q->fixed_fields) { + /* Detecting changes for uncached terms is costly, so only do it once + * per iteration. */ + if (!(it->flags & EcsIterFixedInChangeComputed)) { + it->flags |= EcsIterFixedInChangeComputed; + ECS_BIT_COND(it->flags, EcsIterFixedInChanged, + flecs_query_check_fixed_monitor(impl)); + } - ecs_rule_op_t *end_op_ptr = &ops[end]; - ecs_rule_op_t *query_op = &ops[ctx->cur->lbl_query]; - end_op_ptr->prev = ctx->cur->lbl_cond_eval; - end_op_ptr->src = query_op->src; - end_op_ptr->first = query_op->first; - end_op_ptr->second = query_op->second; - end_op_ptr->flags = query_op->flags; - end_op_ptr->field_index = query_op->field_index; + if (it->flags & EcsIterFixedInChanged) { + return true; + } + } + + /* If query has a cache, check for changes in current matched result */ + if (impl->cache) { + ecs_query_cache_table_match_t *qm = + (ecs_query_cache_table_match_t*)it->priv_.iter.query.prev; + ecs_check(qm != NULL, ECS_INVALID_PARAMETER, NULL); + return flecs_query_check_match_monitor(impl, qm, it); + } + +error: + return false; } -static -void flecs_rule_begin_block_or( - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx) +void ecs_iter_skip( + ecs_iter_t *it) { - ecs_rule_op_t *or_op = flecs_rule_begin_block(EcsRuleNot, ctx); - or_op->kind = EcsRuleOr; - if (op->flags & (EcsRuleIsVar << EcsRuleSrc)) { - or_op->flags = (EcsRuleIsVar << EcsRuleSrc); - or_op->src = op->src; - } + ecs_assert(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), + ECS_INVALID_PARAMETER, NULL); + it->flags |= EcsIterSkip; } +/** + * @file query/engine/eval.c + * @brief Query engine implementation. + */ + + +// #define FLECS_QUERY_TRACE + +#ifdef FLECS_QUERY_TRACE +static int flecs_query_trace_indent = 0; +#endif + static -void flecs_rule_end_block_or( - ecs_rule_compile_ctx_t *ctx) +bool flecs_query_dispatch( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_select_w_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_id_t id, + ecs_flags32_t filter_mask) { - ecs_rule_op_t op = {0}; - op.kind = EcsRuleEnd; - ecs_rule_lbl_t end = flecs_rule_op_insert(&op, ctx); - - ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - int32_t i, prev_or = -2; - for (i = ctx->cur->lbl_begin + 1; i < end; i ++) { - if (ops[i].next == FlecsRuleOrMarker) { - if (prev_or != -2) { - ops[prev_or].prev = flecs_itolbl(i); + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + ecs_id_record_t *idr = op_ctx->idr; + ecs_table_record_t *tr; + ecs_table_t *table; + + if (!redo) { + if (!idr || idr->id != id) { + idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); + if (!idr) { + return false; + } + } + + if (ctx->query->pub.flags & EcsQueryMatchEmptyTables) { + if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { + return false; + } + } else { + if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { + return false; } - ops[i].next = flecs_itolbl(end); - prev_or = i; } } - ops[ctx->cur->lbl_begin].next = flecs_itolbl(end); - ops[end].prev = ctx->cur->lbl_begin; - ops[end - 1].prev = ctx->cur->lbl_begin; +repeat: + if (!redo || !op_ctx->remaining) { + tr = flecs_table_cache_next(&op_ctx->it, ecs_table_record_t); + if (!tr) { + return false; + } - /* Scan which variables were conditionally written in the OR chain and - * reset instructions after the OR chain. If a variable is set in part one - * of a chain but not part two, there would be nothing writing to the - * variable in part two, leaving it to the previous value. To address this - * a reset is inserted that resets the variable value on redo. */ - for (i = 1; i < (8 * ECS_SIZEOF(ecs_write_flags_t)); i ++) { - ecs_write_flags_t prev = 1 & (ctx->ctrlflow->cond_written_or >> i); - ecs_write_flags_t cur = 1 & (ctx->cond_written >> i); + op_ctx->column = flecs_ito(int16_t, tr->index); + op_ctx->remaining = flecs_ito(int16_t, tr->count - 1); + table = tr->hdr.table; + flecs_query_var_set_range(op, op->src.var, table, 0, 0, ctx); + } else { + tr = (ecs_table_record_t*)op_ctx->it.cur; + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + table = tr->hdr.table; + op_ctx->column = flecs_query_next_column(table, idr->id, op_ctx->column); + op_ctx->remaining --; + } - if (!prev && cur) { - ecs_rule_op_t reset_op = {0}; - reset_op.kind = EcsRuleReset; - reset_op.flags |= (EcsRuleIsVar << EcsRuleSrc); - reset_op.src.var = flecs_itovar(i); - flecs_rule_op_insert(&reset_op, ctx); - } + if (flecs_query_table_filter(table, op->other, filter_mask)) { + goto repeat; } - ctx->ctrlflow->in_or = false; - ctx->cur->lbl_begin = -1; + flecs_query_set_match(op, table, op_ctx->column, ctx); + return true; } -static -void flecs_rule_insert_each( - ecs_var_id_t tvar, - ecs_var_id_t evar, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) +bool flecs_query_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - ecs_rule_op_t each = {0}; - each.kind = EcsRuleEach; - each.src.var = evar; - each.first.var = tvar; - each.flags = (EcsRuleIsVar << EcsRuleSrc) | - (EcsRuleIsVar << EcsRuleFirst); - flecs_rule_write_ctx(evar, ctx, cond_write); - flecs_rule_write(evar, &each.written); - flecs_rule_op_insert(&each, ctx); + ecs_id_t id = 0; + if (!redo) { + id = flecs_query_op_get_id(op, ctx); + } + return flecs_query_select_w_id(op, redo, ctx, id, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)); } -static -void flecs_rule_insert_lookup( - ecs_var_id_t base_var, - ecs_var_id_t evar, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) +bool flecs_query_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - ecs_rule_op_t lookup = {0}; - lookup.kind = EcsRuleLookup; - lookup.src.var = evar; - lookup.first.var = base_var; - lookup.flags = (EcsRuleIsVar << EcsRuleSrc) | - (EcsRuleIsVar << EcsRuleFirst); - flecs_rule_write_ctx(evar, ctx, cond_write); - flecs_rule_write(evar, &lookup.written); - flecs_rule_op_insert(&lookup, ctx); -} + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + ecs_id_record_t *idr = op_ctx->idr; + ecs_table_record_t *tr; -static -void flecs_rule_insert_unconstrained_transitive( - ecs_rule_t *rule, - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) -{ - /* Create anonymous variable to store the target ids. This will return the - * list of targets without constraining the variable of the term, which - * needs to stay variable to find all transitive relationships for a src. */ - ecs_var_id_t tgt = flecs_rule_add_var(rule, NULL, NULL, EcsVarEntity); - flecs_set_var_label(&rule->vars[tgt], rule->vars[op->second.var].name); + ecs_table_t *table = flecs_query_get_table(op, &op->src, EcsQuerySrc, ctx); + if (!table) { + return false; + } - /* First, find ids to start traversal from. This fixes op.second. */ - ecs_rule_op_t find_ids = {0}; - find_ids.kind = EcsRuleIdsRight; - find_ids.field_index = -1; - find_ids.first = op->first; - find_ids.second = op->second; - find_ids.flags = op->flags; - find_ids.flags &= (ecs_flags8_t)~((EcsRuleIsVar|EcsRuleIsEntity) << EcsRuleSrc); - find_ids.second.var = tgt; - flecs_rule_write_ctx(tgt, ctx, cond_write); - flecs_rule_write(tgt, &find_ids.written); - flecs_rule_op_insert(&find_ids, ctx); + if (!redo) { + ecs_id_t id = flecs_query_op_get_id(op, ctx); + if (!idr || idr->id != id) { + idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); + if (!idr) { + return false; + } + } - /* Next, iterate all tables for the ids. This fixes op.src */ - ecs_rule_op_t and_op = {0}; - and_op.kind = EcsRuleAnd; - and_op.field_index = op->field_index; - and_op.first = op->first; - and_op.second = op->second; - and_op.src = op->src; - and_op.flags = op->flags | EcsRuleIsSelf; - and_op.second.var = tgt; - flecs_rule_write_ctx(and_op.src.var, ctx, cond_write); - flecs_rule_write(and_op.src.var, &and_op.written); - flecs_rule_op_insert(&and_op, ctx); -} + tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return false; + } -static -void flecs_rule_insert_inheritance( - ecs_rule_t *rule, - ecs_term_t *term, - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) -{ - /* Anonymous variable to store the resolved component ids */ - ecs_var_id_t tvar = flecs_rule_add_var(rule, NULL, NULL, EcsVarTable); - ecs_var_id_t evar = flecs_rule_add_var(rule, NULL, NULL, EcsVarEntity); - flecs_set_var_label(&rule->vars[tvar], ecs_get_name(rule->filter.world, term->first.id)); - flecs_set_var_label(&rule->vars[evar], ecs_get_name(rule->filter.world, term->first.id)); + op_ctx->column = flecs_ito(int16_t, tr->index); + op_ctx->remaining = flecs_ito(int16_t, tr->count); + op_ctx->it.cur = &tr->hdr; + } else { + ecs_assert((op_ctx->remaining + op_ctx->column - 1) < table->type.count, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(op_ctx->remaining >= 0, ECS_INTERNAL_ERROR, NULL); + if (--op_ctx->remaining <= 0) { + return false; + } - ecs_rule_op_t trav_op = {0}; - trav_op.kind = EcsRuleTrav; - trav_op.field_index = -1; - trav_op.first.entity = term->first.trav; - trav_op.second.entity = term->first.id; - trav_op.src.var = tvar; - trav_op.flags = EcsRuleIsSelf; - trav_op.flags |= (EcsRuleIsEntity << EcsRuleFirst); - trav_op.flags |= (EcsRuleIsEntity << EcsRuleSecond); - trav_op.flags |= (EcsRuleIsVar << EcsRuleSrc); - trav_op.written |= (1ull << tvar); - if (term->first.flags & EcsSelf) { - trav_op.match_flags |= EcsTermReflexive; + op_ctx->column = flecs_query_next_column(table, idr->id, op_ctx->column); + ecs_assert(op_ctx->column != -1, ECS_INTERNAL_ERROR, NULL); } - flecs_rule_op_insert(&trav_op, ctx); - flecs_rule_insert_each(tvar, evar, ctx, cond_write); - ecs_rule_ref_t r = { .var = evar }; - op->first = r; - op->flags &= (ecs_flags8_t)~(EcsRuleIsEntity << EcsRuleFirst); - op->flags |= (EcsRuleIsVar << EcsRuleFirst); + flecs_query_set_match(op, table, op_ctx->column, ctx); + return true; } static -void flecs_rule_compile_term_id( - ecs_world_t *world, - ecs_rule_t *rule, - ecs_rule_op_t *op, - ecs_term_id_t *term_id, - ecs_rule_ref_t *ref, - ecs_flags8_t ref_kind, - ecs_var_kind_t kind, - ecs_rule_compile_ctx_t *ctx, - bool create_wildcard_vars) +bool flecs_query_and( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - (void)world; - - if (!ecs_term_id_is_set(term_id)) { - return; + uint64_t written = ctx->written[ctx->op_index]; + if (written & (1ull << op->src.var)) { + return flecs_query_with(op, redo, ctx); + } else { + return flecs_query_select(op, redo, ctx); } +} - if (term_id->flags & EcsIsVariable) { - op->flags |= (ecs_flags8_t)(EcsRuleIsVar << ref_kind); - const char *name = flecs_term_id_var_name(term_id); - if (name) { - ref->var = flecs_rule_most_specific_var(rule, name, kind, ctx); - ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); - } else if (create_wildcard_vars) { - bool is_wildcard = flecs_term_id_is_wildcard(term_id); - if (is_wildcard && (kind == EcsVarAny)) { - ref->var = flecs_rule_add_var(rule, NULL, NULL, EcsVarTable); - } else { - ref->var = flecs_rule_add_var(rule, NULL, NULL, EcsVarEntity); +bool flecs_query_select_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_flags32_t table_filter) +{ + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + ecs_iter_t *it = ctx->it; + int8_t field = op->field_index; + ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL); + + if (!redo) { + ecs_id_t id = it->ids[field]; + ecs_id_record_t *idr = op_ctx->idr; + if (!idr || idr->id != id) { + idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); + if (!idr) { + return false; } - if (is_wildcard) { - flecs_set_var_label(&rule->vars[ref->var], - ecs_get_name(world, term_id->id)); + } + + if (ctx->query->pub.flags & EcsQueryMatchEmptyTables) { + if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { + return false; + } + } else { + if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { + return false; } - ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); } } - if (term_id->flags & EcsIsEntity) { - op->flags |= (ecs_flags8_t)(EcsRuleIsEntity << ref_kind); - ref->entity = term_id->id; +repeat: {} + const ecs_table_record_t *tr = flecs_table_cache_next( + &op_ctx->it, ecs_table_record_t); + if (!tr) { + return false; + } + + ecs_table_t *table = tr->hdr.table; + if (flecs_query_table_filter(table, op->other, table_filter)) { + goto repeat; } + + flecs_query_var_set_range(op, op->src.var, table, 0, 0, ctx); + flecs_query_it_set_tr(it, field, tr); + return true; } -static -int flecs_rule_compile_ensure_vars( - ecs_rule_t *rule, - ecs_rule_op_t *op, - ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind, - ecs_rule_compile_ctx_t *ctx, - bool cond_write, - bool *written_out) +bool flecs_query_with_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - ecs_flags16_t flags = flecs_rule_ref_flags(op->flags, ref_kind); - bool written = false; - - if (flags & EcsRuleIsVar) { - ecs_var_id_t var_id = ref->var; - ecs_rule_var_t *var = &rule->vars[var_id]; + if (redo) { + return false; + } - if (var->kind == EcsVarEntity && - !flecs_rule_is_written(var_id, ctx->written)) - { - /* If entity variable is not yet written but a table variant exists - * that has been written, insert each operation to translate from - * entity variable to table */ - ecs_var_id_t tvar = var->table_id; - if ((tvar != EcsVarNone) && - flecs_rule_is_written(tvar, ctx->written)) - { - if (var->lookup) { - if (!flecs_rule_is_written(tvar, ctx->written)) { - ecs_err("dependent variable of '$%s' is not written", - var->name); - return -1; - } + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + ecs_iter_t *it = ctx->it; + int8_t field = op->field_index; + ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL); - if (!flecs_rule_is_written(var->base_id, ctx->written)) { - flecs_rule_insert_each( - tvar, var->base_id, ctx, cond_write); - } - } else { - flecs_rule_insert_each(tvar, var_id, ctx, cond_write); - } + ecs_table_t *table = flecs_query_get_table(op, &op->src, EcsQuerySrc, ctx); + if (!table) { + return false; + } - /* Variable was written, just not as entity */ - written = true; - } else if (var->lookup) { - if (!flecs_rule_is_written(var->base_id, ctx->written)) { - ecs_err("dependent variable of '$%s' is not written", - var->name); - return -1; - } - } + ecs_id_t id = it->ids[field]; + ecs_id_record_t *idr = op_ctx->idr; + if (!idr || idr->id != id) { + idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); + if (!idr) { + return false; } - - written |= flecs_rule_is_written(var_id, ctx->written); - - /* After evaluating a term, a used variable is always written */ - flecs_rule_write(var_id, &op->written); - flecs_rule_write_ctx(var_id, ctx, cond_write); - } else { - /* If it's not a variable, it's always written */ - written = true; } - if (written_out) { - *written_out = written; + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return false; } - return 0; + flecs_query_it_set_tr(it, field, tr); + return true; } static -bool flecs_rule_compile_lookup( - ecs_rule_t *rule, - ecs_var_id_t var_id, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) +bool flecs_query_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - ecs_rule_var_t *var = &rule->vars[var_id]; - if (var->lookup) { - flecs_rule_insert_lookup(var->base_id, var_id, ctx, cond_write); - return true; + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + return flecs_query_up_with(op, redo, ctx); } else { - return false; + return flecs_query_up_select(op, redo, ctx, + FlecsQueryUpSelectUp, FlecsQueryUpSelectDefault); } } static -void flecs_rule_insert_contains( - ecs_rule_t *rule, - ecs_var_id_t src_var, - ecs_var_id_t other_var, - ecs_rule_compile_ctx_t *ctx) +bool flecs_query_self_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - ecs_rule_op_t contains = {0}; - if ((src_var != other_var) && (src_var == rule->vars[other_var].table_id)) { - contains.kind = EcsRuleContain; - contains.src.var = src_var; - contains.first.var = other_var; - contains.flags |= (EcsRuleIsVar << EcsRuleSrc) | - (EcsRuleIsVar << EcsRuleFirst); - flecs_rule_op_insert(&contains, ctx); + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + return flecs_query_self_up_with(op, redo, ctx, false); + } else { + return flecs_query_up_select(op, redo, ctx, + FlecsQueryUpSelectSelfUp, FlecsQueryUpSelectDefault); } } static -void flecs_rule_insert_pair_eq( - int32_t field_index, - ecs_rule_compile_ctx_t *ctx) +bool flecs_query_and_any( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - ecs_rule_op_t contains = {0}; - contains.kind = EcsRulePairEq; - contains.field_index = flecs_ito(int8_t, field_index); - flecs_rule_op_insert(&contains, ctx); + ecs_flags16_t match_flags = op->match_flags; + if (redo) { + if (match_flags & EcsTermMatchAnySrc) { + return false; + } + } + + uint64_t written = ctx->written[ctx->op_index]; + int32_t remaining = 1; + bool result; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + result = flecs_query_with(op, redo, ctx); + } else { + result = flecs_query_select(op, redo, ctx); + remaining = 0; + } + + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + + if (match_flags & EcsTermMatchAny && op_ctx->remaining) { + op_ctx->remaining = flecs_ito(int16_t, remaining); + } + + int32_t field = op->field_index; + if (field != -1) { + ctx->it->ids[field] = flecs_query_op_get_id(op, ctx); + } + + ctx->it->trs[field] = (ecs_table_record_t*)op_ctx->it.cur; + + return result; } static -bool flecs_rule_term_fixed_id( - ecs_filter_t *filter, - ecs_term_t *term) +bool flecs_query_only_any( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - /* Transitive/inherited terms have variable ids */ - if (term->flags & (EcsTermTransitive|EcsTermIdInherited)) { - return false; + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + return flecs_query_and_any(op, redo, ctx); + } else { + return flecs_query_select_w_id(op, redo, ctx, EcsAny, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)); } +} - /* Or terms can match different ids */ - if (term->oper == EcsOr) { - return false; - } - if ((term != filter->terms) && term[-1].oper == EcsOr) { - return false; +static +bool flecs_query_triv( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); + ecs_flags64_t termset = op->src.entity; + uint64_t written = ctx->written[ctx->op_index]; + ctx->written[ctx->op_index + 1] |= 1ull; + if (written & 1ull) { + flecs_query_set_iter_this(ctx->it, ctx); + return flecs_query_trivial_test(ctx, redo, termset); + } else { + return flecs_query_trivial_search(ctx, op_ctx, redo, termset); } +} - /* Wildcards can assume different ids */ - if (ecs_id_is_wildcard(term->id)) { - return false; +static +bool flecs_query_cache( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + (void)op; + (void)redo; + + uint64_t written = ctx->written[ctx->op_index]; + ctx->written[ctx->op_index + 1] |= 1ull; + if (written & 1ull) { + return flecs_query_cache_test(ctx, redo); + } else { + return flecs_query_cache_search(ctx); } +} - /* Any terms can have fixed ids, but they require special handling */ - if (term->flags & (EcsTermMatchAny|EcsTermMatchAnySrc)) { - return false; +static +bool flecs_query_is_cache( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + (void)op; + + uint64_t written = ctx->written[ctx->op_index]; + ctx->written[ctx->op_index + 1] |= 1ull; + if (written & 1ull) { + return flecs_query_is_cache_test(ctx, redo); + } else { + return flecs_query_is_cache_search(ctx); } +} - /* First terms that are Not or Optional require special handling */ - if (term->oper == EcsNot || term->oper == EcsOptional) { - if (term == filter->terms) { - return false; +static +int32_t flecs_query_next_inheritable_id( + ecs_world_t *world, + ecs_type_t *type, + int32_t index) +{ + int32_t i; + for (i = index; i < type->count; i ++) { + ecs_id_record_t *idr = flecs_id_record_get(world, type->array[i]); + if (!(idr->flags & EcsIdOnInstantiateDontInherit)) { + return i; } } - - return true; + return -1; } static -int flecs_rule_compile_builtin_pred( - ecs_term_t *term, - ecs_rule_op_t *op, - ecs_write_flags_t write_state) +bool flecs_query_x_from( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_oper_kind_t oper) { - ecs_entity_t id = term->first.id; + ecs_query_xfrom_ctx_t *op_ctx = flecs_op_ctx(ctx, xfrom); + ecs_world_t *world = ctx->world; + ecs_type_t *type; + int32_t i; - ecs_rule_op_kind_t eq[] = {EcsRulePredEq, EcsRulePredNeq}; - ecs_rule_op_kind_t eq_name[] = {EcsRulePredEqName, EcsRulePredNeqName}; - ecs_rule_op_kind_t eq_match[] = {EcsRulePredEqMatch, EcsRulePredNeqMatch}; - - ecs_flags16_t flags_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); + if (!redo) { + /* Find entity that acts as the template from which we match the ids */ + ecs_id_t id = flecs_query_op_get_id(op, ctx); + ecs_assert(ecs_is_alive(world, id), ECS_INTERNAL_ERROR, NULL); + ecs_record_t *r = flecs_entities_get(world, id); + ecs_table_t *table; + if (!r || !(table = r->table)) { + /* Nothing to match */ + return false; + } - if (id == EcsPredEq) { - if (term->second.flags & EcsIsName) { - op->kind = flecs_ito(uint8_t, eq_name[term->oper == EcsNot]); - } else { - op->kind = flecs_ito(uint8_t, eq[term->oper == EcsNot]); + /* Find first id to test against. Skip ids with DontInherit flag. */ + type = op_ctx->type = &table->type; + op_ctx->first_id_index = flecs_query_next_inheritable_id( + world, type, 0); + op_ctx->cur_id_index = op_ctx->first_id_index; + + if (op_ctx->cur_id_index == -1) { + return false; /* No ids to filter on */ } - } else if (id == EcsPredMatch) { - op->kind = flecs_ito(uint8_t, eq_match[term->oper == EcsNot]); + } else { + type = op_ctx->type; } - op->first = op->src; - op->src = (ecs_rule_ref_t){0}; - op->flags &= (ecs_flags8_t)~((EcsRuleIsEntity|EcsRuleIsVar) << EcsRuleSrc); - op->flags &= (ecs_flags8_t)~((EcsRuleIsEntity|EcsRuleIsVar) << EcsRuleFirst); - op->flags |= EcsRuleIsVar << EcsRuleFirst; + ecs_id_t *ids = type->array; - if (flags_2nd & EcsRuleIsVar) { - if (!(write_state & (1ull << op->second.var))) { - ecs_err("uninitialized variable '%s' on right-hand side of " - "equality operator", term->second.name); - return -1; - } + /* Check if source is variable, and if it's already written */ + bool src_written = true; + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + uint64_t written = ctx->written[ctx->op_index]; + src_written = written & (1ull << op->src.var); } - return 0; -} + do { + int32_t id_index = op_ctx->cur_id_index; -static -int flecs_rule_ensure_scope_var( - ecs_rule_t *rule, - ecs_rule_op_t *op, - ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_var_id_t var = ref->var; + /* If source is not yet written, find tables with first id */ + if (!src_written) { + ecs_entity_t first_id = ids[id_index]; - if (rule->vars[var].kind == EcsVarEntity && - !flecs_rule_is_written(var, ctx->written)) - { - ecs_var_id_t table_var = rule->vars[var].table_id; - if (table_var != EcsVarNone && - flecs_rule_is_written(table_var, ctx->written)) - { - if (flecs_rule_compile_ensure_vars( - rule, op, ref, ref_kind, ctx, false, NULL)) + if (!flecs_query_select_w_id(op, redo, ctx, + first_id, (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) { - goto error; + if (oper == EcsOrFrom) { + id_index = flecs_query_next_inheritable_id( + world, type, id_index + 1); + if (id_index != -1) { + op_ctx->cur_id_index = id_index; + redo = false; + continue; + } + } + + return false; } + id_index ++; /* First id got matched */ + } else if (redo && src_written) { + return false; } - } - return 0; -error: - return -1; -} + ecs_table_t *src_table = flecs_query_get_table( + op, &op->src, EcsQuerySrc, ctx); + if (!src_table) { + continue; + } + + redo = true; + + if (!src_written && oper == EcsOrFrom) { + /* Eliminate duplicate matches from tables that have multiple + * components from the type list */ + if (op_ctx->cur_id_index != op_ctx->first_id_index) { + for (i = op_ctx->first_id_index; i < op_ctx->cur_id_index; i ++) { + ecs_id_record_t *idr = flecs_id_record_get(world, ids[i]); + if (!idr) { + continue; + } -static -int flecs_rule_ensure_scope_vars( - ecs_world_t *world, - ecs_rule_t *rule, - ecs_rule_compile_ctx_t *ctx, - ecs_term_t *term) -{ - /* If the scope uses variables as entity that have only been written as - * table, resolve them as entities before entering the scope. */ - ecs_term_t *cur = term; - while(cur->first.id != EcsScopeClose) { - /* Dummy operation to obtain variable information for term */ - ecs_rule_op_t op = {0}; - flecs_rule_compile_term_id(world, rule, &op, &cur->first, - &op.first, EcsRuleFirst, EcsVarEntity, ctx, false); - flecs_rule_compile_term_id(world, rule, &op, &cur->second, - &op.second, EcsRuleSecond, EcsVarEntity, ctx, false); - - if (op.flags & (EcsRuleIsVar << EcsRuleFirst)) { - if (flecs_rule_ensure_scope_var( - rule, &op, &op.first, EcsRuleFirst, ctx)) - { - goto error; + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + + if (flecs_id_record_get_table(idr, src_table) != NULL) { + /* Already matched */ + break; + } + } + if (i != op_ctx->cur_id_index) { + continue; + } } + return true; } - if (op.flags & (EcsRuleIsVar << EcsRuleSecond)) { - if (flecs_rule_ensure_scope_var( - rule, &op, &op.second, EcsRuleSecond, ctx)) - { - goto error; + + if (oper == EcsAndFrom || oper == EcsNotFrom || src_written) { + for (i = id_index; i < type->count; i ++) { + ecs_id_record_t *idr = flecs_id_record_get(world, ids[i]); + if (!idr) { + if (oper == EcsAndFrom) { + return false; + } else { + continue; + } + } + + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + + if (flecs_id_record_get_table(idr, src_table) == NULL) { + if (oper == EcsAndFrom) { + break; /* Must have all ids */ + } + } else { + if (oper == EcsNotFrom) { + break; /* Must have none of the ids */ + } else if (oper == EcsOrFrom) { + return true; /* Single match is enough */ + } + } } - } - cur ++; - } + if (i == type->count) { + if (oper == EcsAndFrom || oper == EcsNotFrom) { + break; /* All ids matched */ + } + } + } + } while (true); - return 0; -error: - return -1; + return true; } static -void flecs_rule_compile_push( - ecs_rule_compile_ctx_t *ctx) +bool flecs_query_and_from( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - ctx->cur = &ctx->ctrlflow[++ ctx->scope]; - ctx->cur->lbl_begin = -1; - ctx->cur->lbl_begin = -1; + return flecs_query_x_from(op, redo, ctx, EcsAndFrom); } static -void flecs_rule_compile_pop( - ecs_rule_compile_ctx_t *ctx) +bool flecs_query_not_from( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - ctx->cur = &ctx->ctrlflow[-- ctx->scope]; + return flecs_query_x_from(op, redo, ctx, EcsNotFrom); } static -bool flecs_rule_term_is_or( - const ecs_filter_t *filter, - const ecs_term_t *term) +bool flecs_query_or_from( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) { - bool first_term = term == filter->terms; - return (term->oper == EcsOr) || (!first_term && term[-1].oper == EcsOr); + return flecs_query_x_from(op, redo, ctx, EcsOrFrom); } static -int flecs_rule_compile_term( - ecs_world_t *world, - ecs_rule_t *rule, - ecs_term_t *term, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_filter_t *filter = &rule->filter; - bool first_term = term == filter->terms; - bool first_is_var = term->first.flags & EcsIsVariable; - bool second_is_var = term->second.flags & EcsIsVariable; - bool src_is_var = term->src.flags & EcsIsVariable; - bool builtin_pred = flecs_rule_is_builtin_pred(term); - bool is_not = (term->oper == EcsNot) && !builtin_pred; - bool is_or = flecs_rule_term_is_or(filter, term); - bool first_or = false, last_or = false; - bool cond_write = term->oper == EcsOptional || is_or; - ecs_rule_op_t op = {0}; - - if (is_or) { - first_or = first_term || (term[-1].oper != EcsOr); - last_or = term->oper != EcsOr; - } - - /* !_ (don't match anything) terms always return nothing. */ - if (is_not && term->id == EcsAny) { - op.kind = EcsRuleNothing; - flecs_rule_op_insert(&op, ctx); - return 0; +bool flecs_query_ids( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; } - if (is_or && (first_term || term[-1].oper != EcsOr)) { - ctx->ctrlflow->cond_written_or = ctx->cond_written; - ctx->ctrlflow->in_or = true; - } + ecs_id_record_t *cur; + ecs_id_t id = flecs_query_op_get_id(op, ctx); - if (!term->src.id && term->src.flags & EcsIsEntity) { - /* If the term has a 0 source, check if it's a scope open/close */ - if (term->first.id == EcsScopeOpen) { - if (flecs_rule_ensure_scope_vars(world, rule, ctx, term)) { - goto error; - } - if (term->oper == EcsNot) { - ctx->scope_is_not |= (ecs_flags32_t)(1ull << ctx->scope); - flecs_rule_begin_block(EcsRuleNot, ctx); - } else { - ctx->scope_is_not &= (ecs_flags32_t)~(1ull << ctx->scope); - } - flecs_rule_compile_push(ctx); - } else if (term->first.id == EcsScopeClose) { - flecs_rule_compile_pop(ctx); - if (ctx->scope_is_not & (ecs_flags32_t)(1ull << (ctx->scope))) { - ctx->cur->lbl_query = -1; - flecs_rule_end_block(ctx); - } - } else { - /* Noop */ + { + cur = flecs_id_record_get(ctx->world, id); + if (!cur || !cur->cache.tables.count) { + return false; } - return 0; } - if (builtin_pred) { - if (term->second.id == EcsWildcard || term->second.id == EcsAny) { - /* Noop */ - return 0; - } + flecs_query_set_vars(op, cur->id, ctx); + + if (op->field_index != -1) { + ecs_iter_t *it = ctx->it; + it->ids[op->field_index] = id; + it->sources[op->field_index] = EcsWildcard; + it->trs[op->field_index] = NULL; /* Mark field as set */ } - /* Default instruction for And operators. If the source is fixed (like for - * singletons or terms with an entity source), use With, which like And but - * just matches against a source (vs. finding a source). */ - op.kind = src_is_var ? EcsRuleAnd : EcsRuleWith; - op.field_index = flecs_ito(int8_t, term->field_index); - op.term_index = flecs_ito(int8_t, term - filter->terms); + return true; +} - /* If rule is transitive, use Trav(ersal) instruction */ - if (term->flags & EcsTermTransitive) { - ecs_assert(ecs_term_id_is_set(&term->second), ECS_INTERNAL_ERROR, NULL); - op.kind = EcsRuleTrav; - } else { - /* Ignore cascade & parent flags */ - ecs_flags32_t trav_flags = EcsTraverseFlags & ~(EcsCascade|EcsParent); - if (term->flags & (EcsTermMatchAny|EcsTermMatchAnySrc)) { - op.kind = EcsRuleAndAny; - } else if ((term->src.flags & trav_flags) == EcsUp) { - op.kind = EcsRuleUp; - } else if ((term->src.flags & trav_flags) == (EcsSelf|EcsUp)) { - op.kind = EcsRuleSelfUp; +static +bool flecs_query_idsright( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_ids_ctx_t *op_ctx = flecs_op_ctx(ctx, ids); + ecs_id_record_t *cur; + + if (!redo) { + ecs_id_t id = flecs_query_op_get_id(op, ctx); + if (!ecs_id_is_wildcard(id)) { + /* If id is not a wildcard, we can directly return it. This can + * happen if a variable was constrained by an iterator. */ + op_ctx->cur = NULL; + flecs_query_set_vars(op, id, ctx); + return true; } - } - /* If term has fixed id, insert simpler instruction that skips dealing with - * wildcard terms and variables */ - if (flecs_rule_term_fixed_id(filter, term)) { - if (op.kind == EcsRuleAnd) { - op.kind = EcsRuleAndId; - } else if (op.kind == EcsRuleSelfUp) { - op.kind = EcsRuleSelfUpId; - } else if (op.kind == EcsRuleUp) { - op.kind = EcsRuleUpId; + cur = op_ctx->cur = flecs_id_record_get(ctx->world, id); + if (!cur) { + return false; + } + } else { + if (!op_ctx->cur) { + return false; } } - /* Save write state at start of term so we can use it to reliably track - * variables got written by this term. */ - ecs_write_flags_t cond_write_state = ctx->cond_written; - ecs_write_flags_t write_state = ctx->written; - - /* Resolve component inheritance if necessary */ - ecs_var_id_t first_var = EcsVarNone, second_var = EcsVarNone, src_var = EcsVarNone; - - /* Resolve variables and entities for operation arguments */ - flecs_rule_compile_term_id(world, rule, &op, &term->first, - &op.first, EcsRuleFirst, EcsVarEntity, ctx, true); - flecs_rule_compile_term_id(world, rule, &op, &term->second, - &op.second, EcsRuleSecond, EcsVarEntity, ctx, true); - - if (first_is_var) first_var = op.first.var; - if (second_is_var) second_var = op.second.var; - - flecs_rule_compile_term_id(world, rule, &op, &term->src, - &op.src, EcsRuleSrc, EcsVarAny, ctx, true); - if (src_is_var) src_var = op.src.var; - bool src_written = flecs_rule_is_written(src_var, ctx->written); + do { + cur = op_ctx->cur = op_ctx->cur->first.next; + } while (cur && !cur->cache.tables.count); /* Skip empty ids */ - /* Insert each instructions for table -> entity variable if needed */ - bool first_written, second_written; - if (flecs_rule_compile_ensure_vars( - rule, &op, &op.first, EcsRuleFirst, ctx, cond_write, &first_written)) - { - goto error; - } - if (flecs_rule_compile_ensure_vars( - rule, &op, &op.second, EcsRuleSecond, ctx, cond_write, &second_written)) - { - goto error; + if (!cur) { + return false; } - /* If the query starts with a Not or Optional term, insert an operation that - * matches all entities. */ - if (first_term && src_is_var && !src_written) { - bool pred_match = builtin_pred && term->first.id == EcsPredMatch; - if (term->oper == EcsNot || term->oper == EcsOptional || pred_match) { - ecs_rule_op_t match_any = {0}; - match_any.kind = EcsAnd; - match_any.flags = EcsRuleIsSelf | (EcsRuleIsEntity << EcsRuleFirst); - match_any.flags |= (EcsRuleIsVar << EcsRuleSrc); - match_any.src = op.src; - match_any.field_index = -1; - if (!pred_match) { - match_any.first.entity = EcsAny; - } else { - /* If matching by name, instead of finding all tables, just find - * the ones with a name. */ - match_any.first.entity = ecs_id(EcsIdentifier); - match_any.second.entity = EcsName; - match_any.flags |= (EcsRuleIsEntity << EcsRuleSecond); - } - match_any.written = (1ull << src_var); - flecs_rule_op_insert(&match_any, ctx); - flecs_rule_write_ctx(op.src.var, ctx, false); + flecs_query_set_vars(op, cur->id, ctx); - /* Update write administration */ - src_written = true; - } + if (op->field_index != -1) { + ecs_iter_t *it = ctx->it; + ecs_id_t id = flecs_query_op_get_id_w_written(op, op->written, ctx); + it->ids[op->field_index] = id; + it->sources[op->field_index] = EcsWildcard; + ECS_TERMSET_SET(it->set_fields, 1u << op->field_index); } - /* A bit of special logic for OR expressions and equality predicates. If the - * left-hand of an equality operator is a table, and there are multiple - * operators in an Or expression, the Or chain should match all entities in - * the table that match the right hand sides of the operator expressions. - * For this to work, the src variable needs to be resolved as entity, as an - * Or chain would otherwise only yield the first match from a table. */ - if (src_is_var && src_written && builtin_pred && term->oper == EcsOr) { - /* Or terms are required to have the same source, so we don't have to - * worry about the last term in the chain. */ - if (rule->vars[src_var].kind == EcsVarTable) { - flecs_rule_compile_term_id(world, rule, &op, &term->src, - &op.src, EcsRuleSrc, EcsVarEntity, ctx, true); - src_var = op.src.var; - } - } + return true; +} - if (flecs_rule_compile_ensure_vars( - rule, &op, &op.src, EcsRuleSrc, ctx, cond_write, NULL)) - { - goto error; - } +static +bool flecs_query_idsleft( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_ids_ctx_t *op_ctx = flecs_op_ctx(ctx, ids); + ecs_id_record_t *cur; - /* If source is Any (_) and first and/or second are unconstrained, insert an - * ids instruction instead of an And */ - if (term->flags & EcsTermMatchAnySrc) { - /* Use up-to-date written values after potentially inserting each */ - if (!first_written || !second_written) { - if (!first_written) { - /* If first is unknown, traverse left: <- (*, t) */ - op.kind = EcsRuleIdsLeft; - } else { - /* If second is wildcard, traverse right: (r, *) -> */ - op.kind = EcsRuleIdsRight; - } - op.src.entity = 0; - op.flags &= (ecs_flags8_t)~(EcsRuleIsVar << EcsRuleSrc); /* ids has no src */ - op.flags &= (ecs_flags8_t)~(EcsRuleIsEntity << EcsRuleSrc); + if (!redo) { + ecs_id_t id = flecs_query_op_get_id(op, ctx); + if (!ecs_id_is_wildcard(id)) { + /* If id is not a wildcard, we can directly return it. This can + * happen if a variable was constrained by an iterator. */ + op_ctx->cur = NULL; + flecs_query_set_vars(op, id, ctx); + return true; } - /* If source variable is not written and we're querying just for Any, insert - * a dedicated instruction that uses the Any record in the id index. Any - * queries that are evaluated against written sources can use Wildcard - * records, which is what the AndAny instruction does. */ - } else if (!src_written && term->id == EcsAny && op.kind == EcsRuleAndAny) { - /* Lookup variables ($var.child_name) are always written */ - if (!rule->vars[src_var].lookup) { - op.kind = EcsRuleSelectAny; /* Uses Any (_) id record */ + cur = op_ctx->cur = flecs_id_record_get(ctx->world, id); + if (!cur) { + return false; } - } - - /* If this is a transitive term and both the target and source are unknown, - * find the targets for the relationship first. This clusters together - * tables for the same target, which allows for more efficient usage of the - * traversal caches. */ - if (term->flags & EcsTermTransitive && src_is_var && second_is_var) { - if (!src_written && !second_written) { - flecs_rule_insert_unconstrained_transitive( - rule, &op, ctx, cond_write); + } else { + if (!op_ctx->cur) { + return false; } } - /* Check if this term has variables that have been conditionally written, - * like variables written by an optional term. */ - if (ctx->cond_written) { - if (!is_or || first_or) { - flecs_rule_begin_block_cond_eval(&op, ctx, cond_write_state); - } - } + do { + cur = op_ctx->cur = op_ctx->cur->second.next; + } while (cur && !cur->cache.tables.count); /* Skip empty ids */ - /* Handle Not, Optional, Or operators */ - if (is_not) { - flecs_rule_begin_block(EcsRuleNot, ctx); - } else if (term->oper == EcsOptional) { - flecs_rule_begin_block(EcsRuleOptional, ctx); - } else if (first_or) { - flecs_rule_begin_block_or(&op, ctx); + if (!cur) { + return false; } - /* If term has component inheritance enabled, insert instruction to walk - * down the relationship tree of the id. */ - if (term->flags & EcsTermIdInherited) { - flecs_rule_insert_inheritance(rule, term, &op, ctx, cond_write); + flecs_query_set_vars(op, cur->id, ctx); + + if (op->field_index != -1) { + ecs_iter_t *it = ctx->it; + ecs_id_t id = flecs_query_op_get_id_w_written(op, op->written, ctx); + it->ids[op->field_index] = id; + it->sources[op->field_index] = EcsWildcard; + ECS_TERMSET_SET(it->set_fields, 1u << op->field_index); } - op.match_flags = term->flags; + return true; +} - if (first_is_var) { - op.first.var = first_var; - op.flags &= (ecs_flags8_t)~(EcsRuleIsEntity << EcsRuleFirst); - op.flags |= (EcsRuleIsVar << EcsRuleFirst); - } +static +bool flecs_query_each( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_each_ctx_t *op_ctx = flecs_op_ctx(ctx, each); + int32_t row; - if (term->src.flags & EcsSelf) { - op.flags |= EcsRuleIsSelf; + ecs_table_range_t range = flecs_query_var_get_range(op->first.var, ctx); + ecs_table_t *table = range.table; + if (!table) { + return false; } - /* Insert instructions for lookup variables */ - if (first_is_var) { - if (flecs_rule_compile_lookup(rule, first_var, ctx, cond_write)) { - write_state |= (1ull << first_var); // lookups are resolved inline + if (!redo) { + if (!ecs_table_count(table)) { + return false; } - } - if (src_is_var) { - if (flecs_rule_compile_lookup(rule, src_var, ctx, cond_write)) { - write_state |= (1ull << src_var); // lookups are resolved inline + row = op_ctx->row = range.offset; + } else { + int32_t end = range.count; + if (end) { + end += range.offset; + } else { + end = ecs_table_count(table); } - } - if (second_is_var) { - if (flecs_rule_compile_lookup(rule, second_var, ctx, cond_write)) { - write_state |= (1ull << second_var); // lookups are resolved inline + row = ++ op_ctx->row; + if (op_ctx->row >= end) { + return false; } } - if (builtin_pred) { - if (flecs_rule_compile_builtin_pred(term, &op, write_state)) { - goto error; - } - } + ecs_assert(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); + const ecs_entity_t *entities = ecs_table_entities(table); + flecs_query_var_set_entity(op, op->src.var, entities[row], ctx); - /* If we're writing the $this variable, filter out disabled/prefab entities - * unless the filter explicitly matches them. - * This could've been done with regular With instructions, but since - * filtering out disabled/prefab entities is the default and this check is - * cheap to perform on table flags, it's worth special casing. */ - if (!src_written && src_var == 0) { - ecs_flags32_t filter_flags = filter->flags; - if (!(filter_flags & EcsFilterMatchDisabled) || - !(filter_flags & EcsFilterMatchPrefab)) - { - ecs_flags32_t table_flags = 0; - if (!(filter_flags & EcsFilterMatchDisabled)) { - table_flags |= EcsTableIsDisabled; - } - if (!(filter_flags & EcsFilterMatchPrefab)) { - table_flags |= EcsTableIsPrefab; - } + return true; +} - op.other = flecs_itolbl(table_flags); - } +static +bool flecs_query_store( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + if (!redo) { + flecs_query_var_set_entity(op, op->src.var, op->first.entity, ctx); + return true; + } else { + return false; } +} - flecs_rule_op_insert(&op, ctx); - ctx->cur->lbl_query = flecs_itolbl(ecs_vec_count(ctx->ops) - 1); - if (is_or) { - ecs_rule_op_t *op_ptr = ecs_vec_get_t(ctx->ops, ecs_rule_op_t, - ctx->cur->lbl_query); - op_ptr->next = FlecsRuleOrMarker; +static +bool flecs_query_reset( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + if (!redo) { + return true; + } else { + flecs_query_var_reset(op->src.var, ctx); + return false; } +} - /* Handle self-references between src and first/second variables */ - if (src_is_var) { - if (first_is_var) { - flecs_rule_insert_contains(rule, src_var, first_var, ctx); - } - if (second_is_var && first_var != second_var) { - flecs_rule_insert_contains(rule, src_var, second_var, ctx); - } +static +bool flecs_query_lookup( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; } - /* Handle self references between first and second variables */ - if (first_is_var && !first_written && (first_var == second_var)) { - flecs_rule_insert_pair_eq(term->field_index, ctx); + const ecs_query_impl_t *query = ctx->query; + ecs_entity_t first = flecs_query_var_get_entity(op->first.var, ctx); + ecs_query_var_t *var = &query->vars[op->src.var]; + + ecs_entity_t result = ecs_lookup_path_w_sep(ctx->world, first, var->lookup, + NULL, NULL, false); + if (!result) { + flecs_query_var_set_entity(op, op->src.var, EcsWildcard, ctx); + return false; } - /* Handle closing of Not, Optional and Or operators */ - if (is_not) { - flecs_rule_end_block(ctx); - } else if (term->oper == EcsOptional) { - flecs_rule_end_block(ctx); - } else if (last_or) { - flecs_rule_end_block_or(ctx); + flecs_query_var_set_entity(op, op->src.var, result, ctx); + + return true; +} + +static +bool flecs_query_setvars( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + (void)op; + + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + ecs_var_id_t *src_vars = query->src_vars; + ecs_iter_t *it = ctx->it; + + if (redo) { + return false; } - /* Handle closing of conditional evaluation */ - if (ctx->cond_written && (first_is_var || second_is_var || src_is_var)) { - if (!is_or || last_or) { - flecs_rule_end_block_cond_eval(ctx); + int32_t i; + ecs_flags32_t up_fields = it->up_fields; + for (i = 0; i < q->field_count; i ++) { + ecs_var_id_t var_id = src_vars[i]; + if (!var_id) { + continue; } - } - /* Ensure that term id is set after evaluating Not */ - if (term->flags & EcsTermIdInherited) { - if (is_not) { - ecs_rule_op_t set_id = {0}; - set_id.kind = EcsRuleSetId; - set_id.first.entity = term->id; - set_id.flags = (EcsRuleIsEntity << EcsRuleFirst); - set_id.field_index = flecs_ito(int8_t, term->field_index); - flecs_rule_op_insert(&set_id, ctx); + if (up_fields & (1u << i)) { + continue; } + + it->sources[i] = flecs_query_var_get_entity(var_id, ctx); } - return 0; -error: - return -1; + return true; } static -bool flecs_rule_var_is_unknown( - ecs_rule_t *rule, - ecs_var_id_t var_id, - ecs_rule_compile_ctx_t *ctx) +bool flecs_query_setthis( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_rule_var_t *vars = rule->vars; - if (ctx->written & (1ull << var_id)) { - return false; + ecs_query_setthis_ctx_t *op_ctx = flecs_op_ctx(ctx, setthis); + ecs_var_t *vars = ctx->vars; + ecs_var_t *this_var = &vars[op->first.var]; + + if (!redo) { + /* Save values so we can restore them later */ + op_ctx->range = vars[0].range; + + /* Constrain This table variable to a single entity from the table */ + vars[0].range = flecs_range_from_entity(this_var->entity, ctx); + vars[0].entity = this_var->entity; + return true; } else { - ecs_var_id_t table_var = vars[var_id].table_id; - if (table_var != EcsVarNone) { - return flecs_rule_var_is_unknown(rule, table_var, ctx); - } + /* Restore previous values, so that instructions that are operating on + * the table variable use all the entities in the table. */ + vars[0].range = op_ctx->range; + vars[0].entity = 0; + return false; } - return true; } -/* Returns whether term is unkown. A term is unknown when it has variable - * elements (first, second, src) that are all unknown. */ static -bool flecs_rule_term_is_unknown( - ecs_rule_t *rule, - ecs_term_t *term, - ecs_rule_compile_ctx_t *ctx) +bool flecs_query_setfixed( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_rule_op_t dummy = {0}; - flecs_rule_compile_term_id(NULL, rule, &dummy, &term->first, - &dummy.first, EcsRuleFirst, EcsVarEntity, ctx, false); - flecs_rule_compile_term_id(NULL, rule, &dummy, &term->second, - &dummy.second, EcsRuleSecond, EcsVarEntity, ctx, false); - flecs_rule_compile_term_id(NULL, rule, &dummy, &term->src, - &dummy.src, EcsRuleSrc, EcsVarAny, ctx, false); + (void)op; + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + ecs_iter_t *it = ctx->it; - bool has_vars = dummy.flags & - ((EcsRuleIsVar << EcsRuleFirst) | - (EcsRuleIsVar << EcsRuleSecond) | - (EcsRuleIsVar << EcsRuleSrc)); - if (!has_vars) { - /* If term has no variables (typically terms with a static src) there - * can't be anything that's unknown. */ + if (redo) { return false; } - if (dummy.flags & (EcsRuleIsVar << EcsRuleFirst)) { - if (!flecs_rule_var_is_unknown(rule, dummy.first.var, ctx)) { - return false; - } - } - if (dummy.flags & (EcsRuleIsVar << EcsRuleSecond)) { - if (!flecs_rule_var_is_unknown(rule, dummy.second.var, ctx)) { - return false; - } - } - if (dummy.flags & (EcsRuleIsVar << EcsRuleSrc)) { - if (!flecs_rule_var_is_unknown(rule, dummy.src.var, ctx)) { - return false; + int32_t i; + for (i = 0; i < q->term_count; i ++) { + const ecs_term_t *term = &q->terms[i]; + const ecs_term_ref_t *src = &term->src; + if (src->id & EcsIsEntity) { + it->sources[term->field_index] = ECS_TERM_REF_ID(src); } } return true; } -/* Find the next known term from specified offset. This function is used to find - * a term that can be evaluated before a term that is unknown. Evaluating known - * before unknown terms can significantly decrease the search space. */ -static -int32_t flecs_rule_term_next_known( - ecs_rule_t *rule, - ecs_rule_compile_ctx_t *ctx, - int32_t offset, - ecs_flags64_t compiled) +bool flecs_query_setids( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_filter_t *filter = &rule->filter; - ecs_term_t *terms = filter->terms; - int32_t i, count = filter->term_count; - - for (i = offset; i < count; i ++) { - ecs_term_t *term = &terms[i]; - if (compiled & (1ull << i)) { - continue; - } - - /* Only evaluate And terms */ - if (term->oper != EcsAnd || flecs_rule_term_is_or(&rule->filter, term)){ - continue; - } - - /* Don't reorder terms before/after scopes */ - if (term->first.id == EcsScopeOpen || term->first.id == EcsScopeClose) { - return -1; - } + (void)op; + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + ecs_iter_t *it = ctx->it; - if (flecs_rule_term_is_unknown(rule, term, ctx)) { - continue; - } + if (redo) { + return false; + } - return i; + int32_t i; + for (i = 0; i < q->term_count; i ++) { + const ecs_term_t *term = &q->terms[i]; + it->ids[term->field_index] = term->id; } - return -1; + return true; } -/* If the first part of a query contains more than one trivial term, insert a - * special instruction which batch-evaluates multiple terms. */ static -int32_t flecs_rule_insert_trivial_search( - ecs_rule_t *rule, - ecs_rule_compile_ctx_t *ctx) +bool flecs_query_setid( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_filter_t *filter = &rule->filter; - ecs_term_t *terms = filter->terms; - int32_t i, term_count = filter->term_count; - - /* Find trivial terms, which can be handled in single instruction */ - int32_t trivial_wildcard_terms = 0; - int32_t trivial_data_terms = 0; - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (!(term->flags & EcsTermIsTrivial)) { - break; - } - - /* We can only add trivial terms to plan if they no up traversal */ - if ((term->src.flags & EcsTraverseFlags) != EcsSelf) { - break; - } + if (redo) { + return false; + } - if (ecs_id_is_wildcard(term->id)) { - trivial_wildcard_terms ++; - } + ecs_assert(op->field_index != -1, ECS_INTERNAL_ERROR, NULL); + ctx->it->ids[op->field_index] = op->first.entity; + return true; +} - if (!(term->flags & EcsTermNoData)) { - trivial_data_terms ++; - } +/* Check if entity is stored in table */ +static +bool flecs_query_contain( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; } - int32_t trivial_terms = i; - if (trivial_terms >= 2) { - /* If there's more than 1 trivial term, batch them in trivial search */ - ecs_rule_op_t trivial = {0}; - if (trivial_wildcard_terms) { - trivial.kind = EcsRuleTrivWildcard; - } else { - if (trivial_data_terms) { - /* Check to see if there are remaining data terms. If there are, - * we'll have to insert an instruction later that populates all - * fields, so don't do double work here. */ - for (i = trivial_terms; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (!(term->flags & EcsTermIsTrivial)) { - break; - } - } - if (trivial_terms == term_count || i != term_count) { - /* Nobody else is going to set the data fields, so we should - * do it here. */ - trivial.kind = EcsRuleTrivData; - } - } - if (!trivial.kind) { - trivial.kind = EcsRuleTriv; - } - } + ecs_var_id_t src_id = op->src.var; + ecs_var_id_t first_id = op->first.var; - /* Store on the operation how many trivial terms should be evaluated */ - trivial.other = (ecs_rule_lbl_t)trivial_terms; - flecs_rule_op_insert(&trivial, ctx); - } else { - /* If fewer than 1 trivial term, there's no point in batching them */ - trivial_terms = 0; - } + ecs_table_t *table = flecs_query_var_get_table(src_id, ctx); - return trivial_terms; + ecs_entity_t e = flecs_query_var_get_entity(first_id, ctx); + return table == ecs_get_table(ctx->world, e); } -/* Insert instruction to populate data fields. */ +/* Check if first and second id of pair from last operation are the same */ static -void flecs_rule_insert_populate( - ecs_rule_t *rule, - ecs_rule_compile_ctx_t *ctx, - int32_t trivial_terms) +bool flecs_query_pair_eq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_filter_t *filter = &rule->filter; - int32_t i, term_count = filter->term_count; + if (redo) { + return false; + } - /* Insert instruction that populates data. This instruction does not - * have to be inserted if the filter provides no data, or if all terms - * of the filter are trivial, in which case the trivial search operation - * also sets the data. */ - if (!(filter->flags & EcsFilterNoData) && (trivial_terms != term_count)) { - int32_t data_fields = 0; - bool only_self = true; + ecs_iter_t *it = ctx->it; + ecs_id_t id = it->ids[op->field_index]; + return ECS_PAIR_FIRST(id) == ECS_PAIR_SECOND(id); +} - /* There are two instructions for setting data fields, a fast one - * that only supports owned fields, and one that supports any kind - * of field. Loop through (remaining) terms to check which one we - * need to use. */ - for (i = trivial_terms; i < term_count; i ++) { - ecs_term_t *term = &filter->terms[i]; - if (term->flags & EcsTermNoData) { - /* Don't care about terms that have no data */ - continue; - } +static +void flecs_query_reset_after_block( + const ecs_query_op_t *start_op, + ecs_query_run_ctx_t *ctx, + ecs_query_ctrl_ctx_t *op_ctx, + bool result) +{ + ecs_query_lbl_t op_index = start_op->next; + const ecs_query_op_t *op = &ctx->qit->ops[op_index]; - data_fields ++; + int32_t field = op->field_index; + if (field == -1) { + goto done; + } - if (!ecs_term_match_this(term)) { - break; - } + /* Set/unset field */ + ecs_iter_t *it = ctx->it; + if (result) { + ECS_TERMSET_SET(it->set_fields, 1u << field); + return; + } - if (term->src.flags & EcsUp) { - break; - } - } + /* Reset state after a field was not matched */ + ctx->written[op_index] = ctx->written[ctx->op_index]; + ctx->op_index = op_index; + ECS_TERMSET_CLEAR(it->set_fields, 1u << field); - if (i != filter->term_count) { - only_self = false; /* Needs the more complex operation */ - } + /* Ignore variables written by Not operation */ + uint64_t *written = ctx->written; + uint64_t written_cur = written[op->prev + 1]; + ecs_flags16_t flags_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t flags_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); - if (data_fields) { - if (only_self) { - ecs_rule_op_t nothing = {0}; - nothing.kind = EcsRulePopulateSelf; - flecs_rule_op_insert(¬hing, ctx); - } else { - ecs_rule_op_t nothing = {0}; - nothing.kind = EcsRulePopulate; - flecs_rule_op_insert(¬hing, ctx); - } - } + /* Overwrite id with cleared out variables */ + ecs_id_t id = flecs_query_op_get_id(op, ctx); + if (id) { + it->ids[field] = id; } -} -int flecs_rule_compile( - ecs_world_t *world, - ecs_stage_t *stage, - ecs_rule_t *rule) -{ - ecs_filter_t *filter = &rule->filter; - ecs_term_t *terms = filter->terms; - ecs_rule_compile_ctx_t ctx = {0}; - ecs_vec_reset_t(NULL, &stage->operations, ecs_rule_op_t); - ctx.ops = &stage->operations; - ctx.cur = ctx.ctrlflow; - ctx.cur->lbl_begin = -1; - ctx.cur->lbl_begin = -1; - ecs_vec_clear(ctx.ops); + it->trs[field] = NULL; - /* Find all variables defined in query */ - if (flecs_rule_discover_vars(stage, rule)) { - return -1; + /* Reset variables */ + if (flags_1st & EcsQueryIsVar) { + if (!flecs_ref_is_written(op, &op->first, EcsQueryFirst, written_cur)){ + flecs_query_var_reset(op->first.var, ctx); + } } - - /* If rule contains fixed source terms, insert operation to set sources */ - int32_t i, term_count = filter->term_count; - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (term->src.flags & EcsIsEntity) { - ecs_rule_op_t set_fixed = {0}; - set_fixed.kind = EcsRuleSetFixed; - flecs_rule_op_insert(&set_fixed, &ctx); - break; + if (flags_2nd & EcsQueryIsVar) { + if (!flecs_ref_is_written(op, &op->second, EcsQuerySecond, written_cur)){ + flecs_query_var_reset(op->second.var, ctx); } } - /* If the rule contains terms with fixed ids (no wildcards, variables), - * insert instruction that initializes ecs_iter_t::ids. This allows for the - * insertion of simpler instructions later on. */ - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (flecs_rule_term_fixed_id(filter, term) || - (term->src.flags & EcsIsEntity && !term->src.id)) - { - ecs_rule_op_t set_ids = {0}; - set_ids.kind = EcsRuleSetIds; - flecs_rule_op_insert(&set_ids, &ctx); - break; - } + /* If term has entity src, set it because no other instruction might */ + if (op->flags & (EcsQueryIsEntity << EcsQuerySrc)) { + it->sources[field] = op->src.entity; } - /* Insert trivial term search if query allows for it */ - int32_t trivial_terms = flecs_rule_insert_trivial_search(rule, &ctx); +done: + op_ctx->op_index = op_index; +} - /* Compile remaining query terms to instructions */ - ecs_flags64_t compiled = 0; - for (i = trivial_terms; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - int32_t compile = i; +static +bool flecs_query_run_block( + bool redo, + ecs_query_run_ctx_t *ctx, + ecs_query_ctrl_ctx_t *op_ctx) +{ + ecs_iter_t *it = ctx->it; + ecs_query_iter_t *qit = &it->priv_.iter.query; - if (compiled & (1ull << i)) { - continue; /* Already compiled */ - } + if (!redo) { + op_ctx->op_index = flecs_itolbl(ctx->op_index + 1); + } else if (ctx->qit->ops[op_ctx->op_index].kind == EcsQueryEnd) { + return false; + } - bool can_reorder = true; - if (term->oper != EcsAnd || flecs_rule_term_is_or(&rule->filter, term)){ - can_reorder = false; - } + ctx->written[ctx->op_index + 1] = ctx->written[ctx->op_index]; - /* If variables have been written, but this term has no known variables, - * first try to resolve terms that have known variables. This can - * significantly reduce the search space. - * Only perform this optimization after at least one variable has been - * written to, as all terms are unknown otherwise. */ - if (can_reorder && ctx.written && - flecs_rule_term_is_unknown(rule, term, &ctx)) - { - int32_t term_index = flecs_rule_term_next_known( - rule, &ctx, i + 1, compiled); - if (term_index != -1) { - term = &rule->filter.terms[term_index]; - compile = term_index; - i --; /* Repeat current term */ - } - } + const ecs_query_op_t *op = &ctx->qit->ops[ctx->op_index]; + bool result = flecs_query_run_until( + redo, ctx, qit->ops, ctx->op_index, op_ctx->op_index, op->next); - if (flecs_rule_compile_term(world, rule, term, &ctx)) { - return -1; - } + op_ctx->op_index = flecs_itolbl(ctx->op_index - 1); + return result; +} - compiled |= (1ull << compile); - } +static +ecs_query_lbl_t flecs_query_last_op_for_or_cond( + const ecs_query_op_t *ops, + ecs_query_lbl_t cur, + ecs_query_lbl_t last) +{ + const ecs_query_op_t *cur_op, *last_op = &ops[last]; + + do { + cur_op = &ops[cur]; + cur ++; + } while (cur_op->next != last && cur_op != last_op); - ecs_var_id_t this_id = flecs_rule_find_var_id(rule, "This", EcsVarEntity); + return cur; +} - /* If This variable has been written as entity, insert an operation to - * assign it to it.entities for consistency. */ - if (this_id != EcsVarNone && (ctx.written & (1ull << this_id))) { - ecs_rule_op_t set_this = {0}; - set_this.kind = EcsRuleSetThis; - set_this.flags |= (EcsRuleIsVar << EcsRuleFirst); - set_this.first.var = this_id; - flecs_rule_op_insert(&set_this, &ctx); +static +bool flecs_query_run_until_for_select_or( + bool redo, + ecs_query_run_ctx_t *ctx, + const ecs_query_op_t *ops, + ecs_query_lbl_t first, + ecs_query_lbl_t cur, + int32_t last) +{ + ecs_query_lbl_t last_for_cur = flecs_query_last_op_for_or_cond( + ops, cur, flecs_itolbl(last)); + if (redo) { + /* If redoing, start from the last instruction of the last executed + * sequence */ + cur = flecs_itolbl(last_for_cur - 1); } - /* Make sure non-This variables are written as entities */ - if (rule->vars) { - for (i = 0; i < rule->var_count; i ++) { - ecs_rule_var_t *var = &rule->vars[i]; - if (var->id && var->kind == EcsVarTable && var->name) { - ecs_var_id_t var_id = flecs_rule_find_var_id(rule, var->name, - EcsVarEntity); - if (!flecs_rule_is_written(var_id, ctx.written)) { - /* Skip anonymous variables */ - if (!flecs_rule_var_is_anonymous(rule, var_id)) { - flecs_rule_insert_each(var->id, var_id, &ctx, false); - } - } - } - } + flecs_query_run_until(redo, ctx, ops, first, cur, last_for_cur); +#ifdef FLECS_QUERY_TRACE + printf("%*s%s (or)\n", (flecs_query_trace_indent + 1)*2, "", + ctx->op_index == last ? "true" : "false"); +#endif + return ctx->op_index == last; +} + +static +bool flecs_query_select_or( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + ecs_iter_t *it = ctx->it; + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + + ecs_query_lbl_t first = flecs_itolbl(ctx->op_index + 1); + if (!redo) { + op_ctx->op_index = first; } - /* If rule contains non-This variables as term source, build lookup array */ - if (rule->src_vars) { - ecs_assert(rule->vars != NULL, ECS_INTERNAL_ERROR, NULL); - bool only_anonymous = true; + const ecs_query_op_t *ops = qit->ops; + const ecs_query_op_t *first_op = &ops[first - 1]; + ecs_query_lbl_t last = first_op->next; + const ecs_query_op_t *last_op = &ops[last]; + const ecs_query_op_t *cur_op = &ops[op_ctx->op_index]; + bool result = false; - for (i = 0; i < filter->field_count; i ++) { - ecs_var_id_t var_id = rule->src_vars[i]; - if (!var_id) { - continue; - } + do { + ecs_query_lbl_t cur = op_ctx->op_index; + ctx->op_index = cur; + ctx->written[cur] = op->written; - if (!flecs_rule_var_is_anonymous(rule, var_id)) { - only_anonymous = false; + result = flecs_query_run_until_for_select_or( + redo, ctx, ops, flecs_itolbl(first - 1), cur, last); + + if (result) { + if (first == cur) { break; - } else { - /* Don't fetch component data for anonymous variables. Because - * not all metadata (such as it.sources) is initialized for - * anonymous variables, and because they may only be available - * as table variables (each is not guaranteed to be inserted for - * anonymous variables) the iterator may not have sufficient - * information to resolve component data. */ - for (int32_t t = 0; t < filter->term_count; t ++) { - ecs_term_t *term = &filter->terms[t]; - if (term->field_index == i) { - term->inout = EcsInOutNone; - } - } } - } - /* Don't insert setvar instruction if all vars are anonymous */ - if (!only_anonymous) { - ecs_rule_op_t set_vars = {0}; - set_vars.kind = EcsRuleSetVars; - flecs_rule_op_insert(&set_vars, &ctx); - } + /* If a previous operation in the OR chain returned a result for the + * same matched source, skip it so we don't yield for each matching + * element in the chain. */ - for (i = 0; i < filter->field_count; i ++) { - ecs_var_id_t var_id = rule->src_vars[i]; - if (!var_id) { - continue; + /* Copy written status so that the variables we just wrote will show + * up as written for the test. This ensures that the instructions + * match against the result we already found, vs. starting a new + * search (the difference between select & with). */ + ecs_query_lbl_t prev = first; + bool dup_found = false; + + /* While terms of an OR chain always operate on the same source, it + * is possible that a table variable is resolved to an entity + * variable. When checking for duplicates, copy the entity variable + * to the table, to ensure we're only testing the found entity. */ + const ecs_query_op_t *prev_op = &ops[cur - 1]; + ecs_var_t old_table_var; + ecs_os_memset_t(&old_table_var, 0, ecs_var_t); + bool restore_table_var = false; + + if (prev_op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + if (first_op->src.var != prev_op->src.var) { + restore_table_var = true; + old_table_var = ctx->vars[first_op->src.var]; + ctx->vars[first_op->src.var] = + ctx->vars[prev_op->src.var]; + } } - if (rule->vars[var_id].kind == EcsVarTable) { - var_id = flecs_rule_find_var_id(rule, rule->vars[var_id].name, - EcsVarEntity); + int16_t field_index = op->field_index; + ecs_id_t prev_id = it->ids[field_index]; + const ecs_table_record_t *prev_tr = it->trs[field_index]; - /* Variables used as source that aren't This must be entities */ - ecs_assert(var_id != EcsVarNone, ECS_INTERNAL_ERROR, NULL); - } + do { + ctx->written[prev] = ctx->written[last]; - rule->src_vars[i] = var_id; - } - } + flecs_query_run_until(false, ctx, ops, flecs_itolbl(first - 1), + prev, cur); - /* If filter is empty, insert Nothing instruction */ - if (!term_count) { - ecs_rule_op_t nothing = {0}; - nothing.kind = EcsRuleNothing; - flecs_rule_op_insert(¬hing, &ctx); - } else { - /* Insert instruction to populate data fields */ - flecs_rule_insert_populate(rule, &ctx, trivial_terms); + if (ctx->op_index == last) { + /* Duplicate match was found, find next result */ + redo = true; + dup_found = true; + break; + } - /* Insert yield. If program reaches this operation, a result was found */ - ecs_rule_op_t yield = {0}; - yield.kind = EcsRuleYield; - flecs_rule_op_insert(&yield, &ctx); - } + break; + } while (true); - int32_t op_count = ecs_vec_count(ctx.ops); - if (op_count) { - rule->op_count = op_count; - rule->ops = ecs_os_malloc_n(ecs_rule_op_t, op_count); - ecs_rule_op_t *rule_ops = ecs_vec_first_t(ctx.ops, ecs_rule_op_t); - ecs_os_memcpy_n(rule->ops, rule_ops, ecs_rule_op_t, op_count); - } + /* Restore table variable to full range for next result */ + if (restore_table_var) { + ctx->vars[first_op->src.var] = old_table_var; + } - return 0; -} + if (dup_found) { + continue; + } -#endif + /* Restore id in case op set it */ + it->ids[field_index] = prev_id; + it->trs[field_index] = prev_tr; + break; + } -/** - * @file addons/rules/engine.c - * @brief Rules engine implementation. - */ + /* No result was found, go to next operation in chain */ + op_ctx->op_index = flecs_query_last_op_for_or_cond( + ops, op_ctx->op_index, last); + cur_op = &qit->ops[op_ctx->op_index]; + redo = false; + } while (cur_op != last_op); -#ifdef FLECS_RULES + return result; +} static -bool flecs_rule_dispatch( - const ecs_rule_op_t *op, +bool flecs_query_with_or( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx); + ecs_query_run_ctx_t *ctx) +{ + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + + bool result = flecs_query_run_block(redo, ctx, op_ctx); + if (result) { + /* If a match was found, no need to keep searching for this source */ + op_ctx->op_index = op->next; + } + + return result; +} static -bool flecs_rule_run_until( +bool flecs_query_or( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx, - const ecs_rule_op_t *ops, - ecs_rule_lbl_t first, - ecs_rule_lbl_t cur, - ecs_rule_op_kind_t until); - -ecs_allocator_t* flecs_rule_get_allocator( - const ecs_iter_t *it) + ecs_query_run_ctx_t *ctx) { - ecs_world_t *world = it->world; - if (ecs_poly_is(world, ecs_world_t)) { - return &world->allocator; - } else { - ecs_assert(ecs_poly_is(world, ecs_stage_t), ECS_INTERNAL_ERROR, NULL); - return &((ecs_stage_t*)world)->allocator; + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + uint64_t written = ctx->written[ctx->op_index]; + if (!(written & (1ull << op->src.var))) { + return flecs_query_select_or(op, redo, ctx); + } } + + return flecs_query_with_or(op, redo, ctx); } static -ecs_rule_op_ctx_t* flecs_op_ctx_( - const ecs_rule_run_ctx_t *ctx) +bool flecs_query_run_block_w_reset( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - return &ctx->op_ctx[ctx->op_index]; -} + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); -#define flecs_op_ctx(ctx, op_kind) (&flecs_op_ctx_(ctx)->is.op_kind) + bool result = flecs_query_run_block(redo, ctx, op_ctx); + flecs_query_reset_after_block(op, ctx, op_ctx, result); + return result; +} static -void flecs_reset_source_set_flag( - const ecs_rule_run_ctx_t *ctx, - int32_t field_index) +bool flecs_query_not( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); - (*ctx->source_set) &= ~(1u << field_index); + if (redo) { + return false; + } + + return !flecs_query_run_block_w_reset(op, redo, ctx); } static -void flecs_set_source_set_flag( - const ecs_rule_run_ctx_t *ctx, - int32_t field_index) -{ - ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); - (*ctx->source_set) |= (1u << field_index); +bool flecs_query_optional( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + bool result = flecs_query_run_block_w_reset(op, redo, ctx); + if (!redo) { + return true; /* Return at least once */ + } else { + return result; + } } static -ecs_table_range_t flecs_range_from_entity( - ecs_entity_t e, - const ecs_rule_run_ctx_t *ctx) +bool flecs_query_eval_if( + const ecs_query_op_t *op, + ecs_query_run_ctx_t *ctx, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind) { - ecs_record_t *r = flecs_entities_get(ctx->world, e); - if (!r) { - return (ecs_table_range_t){ 0 }; + bool result = true; + if (flecs_query_ref_flags(op->flags, ref_kind) == EcsQueryIsVar) { + result = ctx->vars[ref->var].entity != EcsWildcard; + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + flecs_query_reset_after_block(op, ctx, op_ctx, result); + return result; } - return (ecs_table_range_t){ - .table = r->table, - .offset = ECS_RECORD_TO_ROW(r->row), - .count = 1 - }; + return true; } static -ecs_table_range_t flecs_rule_var_get_range( - int32_t var_id, - const ecs_rule_run_ctx_t *ctx) +bool flecs_query_if_var( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_assert(var_id < ctx->rule->var_count, ECS_INTERNAL_ERROR, NULL); - ecs_var_t *var = &ctx->vars[var_id]; - ecs_table_t *table = var->range.table; - if (table) { - return var->range; - } - - ecs_entity_t entity = var->entity; - if (entity && entity != EcsWildcard) { - var->range = flecs_range_from_entity(entity, ctx); - return var->range; + if (!redo) { + if (!flecs_query_eval_if(op, ctx, &op->src, EcsQuerySrc) || + !flecs_query_eval_if(op, ctx, &op->first, EcsQueryFirst) || + !flecs_query_eval_if(op, ctx, &op->second, EcsQuerySecond)) + { + return true; + } } - return (ecs_table_range_t){ 0 }; + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + return flecs_query_run_block(redo, ctx, op_ctx); } static -ecs_table_t* flecs_rule_var_get_table( - int32_t var_id, - const ecs_rule_run_ctx_t *ctx) +bool flecs_query_if_set( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_var_t *var = &ctx->vars[var_id]; - ecs_table_t *table = var->range.table; - if (table) { - return table; + ecs_iter_t *it = ctx->it; + int8_t field_index = flecs_ito(int8_t, op->other); + + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + if (!redo) { + op_ctx->is_set = ecs_field_is_set(it, field_index); } - ecs_entity_t entity = var->entity; - if (entity && entity != EcsWildcard) { - var->range = flecs_range_from_entity(entity, ctx); - return var->range.table; + if (!op_ctx->is_set) { + return !redo; } - return NULL; + return flecs_query_run_block(redo, ctx, op_ctx); } static -ecs_table_t* flecs_rule_get_table( - const ecs_rule_op_t *op, - const ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind, - const ecs_rule_run_ctx_t *ctx) +bool flecs_query_end( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_flags16_t flags = flecs_rule_ref_flags(op->flags, ref_kind); - if (flags & EcsRuleIsEntity) { - return ecs_get_table(ctx->world, ref->entity); - } else { - return flecs_rule_var_get_table(ref->var, ctx); - } + (void)op; (void)ctx; + return !redo; } static -ecs_table_range_t flecs_rule_get_range( - const ecs_rule_op_t *op, - const ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind, - const ecs_rule_run_ctx_t *ctx) +bool flecs_query_dispatch( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) { - ecs_flags16_t flags = flecs_rule_ref_flags(op->flags, ref_kind); - if (flags & EcsRuleIsEntity) { - ecs_assert(!(flags & EcsRuleIsVar), ECS_INTERNAL_ERROR, NULL); - return flecs_range_from_entity(ref->entity, ctx); - } else { - ecs_var_t *var = &ctx->vars[ref->var]; - if (var->range.table) { - return ctx->vars[ref->var].range; - } else if (var->entity) { - return flecs_range_from_entity(var->entity, ctx); - } + switch(op->kind) { + case EcsQueryAnd: return flecs_query_and(op, redo, ctx); + case EcsQueryAndAny: return flecs_query_and_any(op, redo, ctx); + case EcsQueryTriv: return flecs_query_triv(op, redo, ctx); + case EcsQueryCache: return flecs_query_cache(op, redo, ctx); + case EcsQueryIsCache: return flecs_query_is_cache(op, redo, ctx); + case EcsQueryOnlyAny: return flecs_query_only_any(op, redo, ctx); + case EcsQueryUp: return flecs_query_up(op, redo, ctx); + case EcsQuerySelfUp: return flecs_query_self_up(op, redo, ctx); + case EcsQueryWith: return flecs_query_with(op, redo, ctx); + case EcsQueryTrav: return flecs_query_trav(op, redo, ctx); + case EcsQueryAndFrom: return flecs_query_and_from(op, redo, ctx); + case EcsQueryNotFrom: return flecs_query_not_from(op, redo, ctx); + case EcsQueryOrFrom: return flecs_query_or_from(op, redo, ctx); + case EcsQueryIds: return flecs_query_ids(op, redo, ctx); + case EcsQueryIdsRight: return flecs_query_idsright(op, redo, ctx); + case EcsQueryIdsLeft: return flecs_query_idsleft(op, redo, ctx); + case EcsQueryEach: return flecs_query_each(op, redo, ctx); + case EcsQueryStore: return flecs_query_store(op, redo, ctx); + case EcsQueryReset: return flecs_query_reset(op, redo, ctx); + case EcsQueryOr: return flecs_query_or(op, redo, ctx); + case EcsQueryOptional: return flecs_query_optional(op, redo, ctx); + case EcsQueryIfVar: return flecs_query_if_var(op, redo, ctx); + case EcsQueryIfSet: return flecs_query_if_set(op, redo, ctx); + case EcsQueryEnd: return flecs_query_end(op, redo, ctx); + case EcsQueryNot: return flecs_query_not(op, redo, ctx); + case EcsQueryPredEq: return flecs_query_pred_eq(op, redo, ctx); + case EcsQueryPredNeq: return flecs_query_pred_neq(op, redo, ctx); + case EcsQueryPredEqName: return flecs_query_pred_eq_name(op, redo, ctx); + case EcsQueryPredNeqName: return flecs_query_pred_neq_name(op, redo, ctx); + case EcsQueryPredEqMatch: return flecs_query_pred_eq_match(op, redo, ctx); + case EcsQueryPredNeqMatch: return flecs_query_pred_neq_match(op, redo, ctx); + case EcsQueryMemberEq: return flecs_query_member_eq(op, redo, ctx); + case EcsQueryMemberNeq: return flecs_query_member_neq(op, redo, ctx); + case EcsQueryToggle: return flecs_query_toggle(op, redo, ctx); + case EcsQueryToggleOption: return flecs_query_toggle_option(op, redo, ctx); + case EcsQueryUnionEq: return flecs_query_union(op, redo, ctx); + case EcsQueryUnionEqWith: return flecs_query_union_with(op, redo, ctx, false); + case EcsQueryUnionNeq: return flecs_query_union_neq(op, redo, ctx); + case EcsQueryUnionEqUp: return flecs_query_union_up(op, redo, ctx); + case EcsQueryUnionEqSelfUp: return flecs_query_union_self_up(op, redo, ctx); + case EcsQueryLookup: return flecs_query_lookup(op, redo, ctx); + case EcsQuerySetVars: return flecs_query_setvars(op, redo, ctx); + case EcsQuerySetThis: return flecs_query_setthis(op, redo, ctx); + case EcsQuerySetFixed: return flecs_query_setfixed(op, redo, ctx); + case EcsQuerySetIds: return flecs_query_setids(op, redo, ctx); + case EcsQuerySetId: return flecs_query_setid(op, redo, ctx); + case EcsQueryContain: return flecs_query_contain(op, redo, ctx); + case EcsQueryPairEq: return flecs_query_pair_eq(op, redo, ctx); + case EcsQueryYield: return false; + case EcsQueryNothing: return false; } - return (ecs_table_range_t){0}; + return false; } -static -ecs_entity_t flecs_rule_var_get_entity( - ecs_var_id_t var_id, - const ecs_rule_run_ctx_t *ctx) +bool flecs_query_run_until( + bool redo, + ecs_query_run_ctx_t *ctx, + const ecs_query_op_t *ops, + ecs_query_lbl_t first, + ecs_query_lbl_t cur, + int32_t last) { - ecs_assert(var_id < (ecs_var_id_t)ctx->rule->var_count, - ECS_INTERNAL_ERROR, NULL); - ecs_var_t *var = &ctx->vars[var_id]; - ecs_entity_t entity = var->entity; - if (entity) { - return entity; - } + ecs_assert(first >= -1, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur > first, ECS_INTERNAL_ERROR, NULL); - ecs_assert(var->range.count == 1, ECS_INTERNAL_ERROR, NULL); - ecs_table_t *table = var->range.table; - ecs_entity_t *entities = table->data.entities.array; - var->entity = entities[var->range.offset]; - return var->entity; -} + ctx->op_index = cur; + const ecs_query_op_t *op = &ops[ctx->op_index]; + const ecs_query_op_t *last_op = &ops[last]; + ecs_assert(last > first, ECS_INTERNAL_ERROR, NULL); -static -void flecs_rule_var_reset( - ecs_var_id_t var_id, - const ecs_rule_run_ctx_t *ctx) -{ - ctx->vars[var_id].entity = EcsWildcard; - ctx->vars[var_id].range.table = NULL; -} +#ifdef FLECS_QUERY_TRACE + printf("%*sblock:\n", flecs_query_trace_indent*2, ""); + flecs_query_trace_indent ++; +#endif -static -void flecs_rule_var_set_table( - const ecs_rule_op_t *op, - ecs_var_id_t var_id, - ecs_table_t *table, - int32_t offset, - int32_t count, - const ecs_rule_run_ctx_t *ctx) -{ - (void)op; - ecs_assert(ctx->rule_vars[var_id].kind == EcsVarTable, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(flecs_rule_is_written(var_id, op->written), - ECS_INTERNAL_ERROR, NULL); - ecs_var_t *var = &ctx->vars[var_id]; - var->entity = 0; - var->range = (ecs_table_range_t){ - .table = table, - .offset = offset, - .count = count - }; + do { + #ifdef FLECS_DEBUG + ctx->qit->profile[ctx->op_index].count[redo] ++; + #endif + +#ifdef FLECS_QUERY_TRACE + printf("%*s%d: %s\n", flecs_query_trace_indent*2, "", + ctx->op_index, flecs_query_op_str(op->kind)); +#endif + + bool result = flecs_query_dispatch(op, redo, ctx); + cur = (&op->prev)[result]; + redo = cur < ctx->op_index; + + if (!redo) { + ctx->written[cur] |= ctx->written[ctx->op_index] | op->written; + } + + ctx->op_index = cur; + op = &ops[ctx->op_index]; + + if (cur <= first) { +#ifdef FLECS_QUERY_TRACE + printf("%*sfalse\n", flecs_query_trace_indent*2, ""); + flecs_query_trace_indent --; +#endif + return false; + } + } while (op < last_op); + +#ifdef FLECS_QUERY_TRACE + printf("%*strue\n", flecs_query_trace_indent*2, ""); + flecs_query_trace_indent --; +#endif + + return true; } +/** + * @file query/engine/eval_iter.c + * @brief Query iterator. + */ + + static -void flecs_rule_var_set_entity( - const ecs_rule_op_t *op, - ecs_var_id_t var_id, - ecs_entity_t entity, - const ecs_rule_run_ctx_t *ctx) +void flecs_query_iter_run_ctx_init( + ecs_iter_t *it, + ecs_query_run_ctx_t *ctx) { - (void)op; - ecs_assert(var_id < (ecs_var_id_t)ctx->rule->var_count, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(flecs_rule_is_written(var_id, op->written), - ECS_INTERNAL_ERROR, NULL); - ecs_var_t *var = &ctx->vars[var_id]; - var->range.table = NULL; - var->entity = entity; + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_impl_t *impl = ECS_CONST_CAST(ecs_query_impl_t*, qit->query); + ctx->world = it->real_world; + ctx->query = impl; + ctx->it = it; + ctx->vars = qit->vars; + ctx->query_vars = qit->query_vars; + ctx->written = qit->written; + ctx->op_ctx = qit->op_ctx; + ctx->qit = qit; } -static -void flecs_rule_set_vars( - const ecs_rule_op_t *op, - ecs_id_t id, - const ecs_rule_run_ctx_t *ctx) +void flecs_query_iter_constrain( + ecs_iter_t *it) { - ecs_flags16_t flags_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t flags_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); + ecs_query_run_ctx_t ctx; + flecs_query_iter_run_ctx_init(it, &ctx); + ecs_assert(ctx.written != NULL, ECS_INTERNAL_ERROR, NULL); - if (flags_1st & EcsRuleIsVar) { - ecs_var_id_t var = op->first.var; - if (op->written & (1ull << var)) { - if (ECS_IS_PAIR(id)) { - flecs_rule_var_set_entity( - op, var, ecs_get_alive(ctx->world, ECS_PAIR_FIRST(id)), ctx); - } else { - flecs_rule_var_set_entity(op, var, id, ctx); + const ecs_query_impl_t *query = ctx.query; + const ecs_query_t *q = &query->pub; + ecs_flags64_t it_written = it->constrained_vars; + ctx.written[0] = it_written; + if (it_written && ctx.query->src_vars) { + /* If variables were constrained, check if there are any table + * variables that have a constrained entity variable. */ + ecs_var_t *vars = ctx.vars; + int32_t i, count = q->field_count; + for (i = 0; i < count; i ++) { + ecs_var_id_t var_id = query->src_vars[i]; + ecs_query_var_t *var = &query->vars[var_id]; + + if (!(it_written & (1ull << var_id)) || + (var->kind == EcsVarTable) || (var->table_id == EcsVarNone)) + { + continue; } + + /* Initialize table variable with constrained entity variable */ + ecs_var_t *tvar = &vars[var->table_id]; + tvar->range = flecs_range_from_entity(vars[var_id].entity, &ctx); + ctx.written[0] |= (1ull << var->table_id); /* Mark as written */ } } - if (flags_2nd & EcsRuleIsVar) { - ecs_var_id_t var = op->second.var; - if (op->written & (1ull << var)) { - flecs_rule_var_set_entity( - op, var, ecs_get_alive(ctx->world, ECS_PAIR_SECOND(id)), ctx); + + /* This function can be called multiple times when setting variables, so + * reset flags before setting them. */ + it->flags &= ~(EcsIterTrivialTest|EcsIterTrivialCached| + EcsIterTrivialSearch); + + /* Figure out whether this query can utilize specialized iterator modes for + * improved performance. */ + ecs_flags32_t flags = q->flags; + ecs_query_cache_t *cache = query->cache; + if (flags & EcsQueryIsTrivial) { + if ((flags & EcsQueryMatchOnlySelf)) { + if (it_written) { + /* When we're testing against an entity or table, set the $this + * variable in advance since it won't change later on. This + * initializes it.count, it.entities and it.table. */ + flecs_query_set_iter_this(it, &ctx); + + if (!cache) { + if (!(flags & EcsQueryMatchWildcards)) { + it->flags |= EcsIterTrivialTest; + } + } else if (flags & EcsQueryIsCacheable) { + it->flags |= EcsIterTrivialTest|EcsIterTrivialCached; + } + } else { + if (!cache) { + if (!(flags & EcsQueryMatchWildcards)) { + it->flags |= EcsIterTrivialSearch; + } + } else if (flags & EcsQueryIsCacheable) { + if (!cache->order_by_callback) { + it->flags |= EcsIterTrivialSearch|EcsIterTrivialCached; + } + } + } + + /* If we're using a specialized iterator mode, make sure to + * initialize static component ids. Usually this is the first + * instruction of a query plan, but because we're not running the + * query plan when using a specialized iterator mode, manually call + * the operation on iterator init. */ + flecs_query_setids(NULL, false, &ctx); } } } -static -ecs_table_range_t flecs_get_ref_range( - const ecs_rule_ref_t *ref, - ecs_flags16_t flag, - const ecs_rule_run_ctx_t *ctx) +bool ecs_query_next( + ecs_iter_t *it) { - if (flag & EcsRuleIsEntity) { - return flecs_range_from_entity(ref->entity, ctx); - } else if (flag & EcsRuleIsVar) { - return flecs_rule_var_get_range(ref->var, ctx); - } - return (ecs_table_range_t){0}; -} + ecs_assert(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); -static -ecs_entity_t flecs_get_ref_entity( - const ecs_rule_ref_t *ref, - ecs_flags16_t flag, - const ecs_rule_run_ctx_t *ctx) -{ - if (flag & EcsRuleIsEntity) { - return ref->entity; - } else if (flag & EcsRuleIsVar) { - return flecs_rule_var_get_entity(ref->var, ctx); + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_impl_t *impl = ECS_CONST_CAST(ecs_query_impl_t*, qit->query); + ecs_query_run_ctx_t ctx; + flecs_query_iter_run_ctx_init(it, &ctx); + const ecs_query_op_t *ops = qit->ops; + + bool redo = it->flags & EcsIterIsValid; + if (redo) { + /* Change detection */ + if (!(it->flags & EcsIterSkip)) { + /* Mark table columns that are written to dirty */ + flecs_query_mark_fields_dirty(impl, it); + if (qit->prev) { + if (ctx.query->pub.flags & EcsQueryHasMonitor) { + /* If this query uses change detection, synchronize the + * monitor for the iterated table with the query */ + flecs_query_sync_match_monitor(impl, qit->prev); + } + } + } } - return 0; -} -static -ecs_id_t flecs_rule_op_get_id_w_written( - const ecs_rule_op_t *op, - uint64_t written, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_flags16_t flags_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t flags_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); - ecs_entity_t first = 0, second = 0; + it->flags &= ~(EcsIterSkip); + it->flags |= EcsIterIsValid; + it->frame_offset += it->count; - if (flags_1st) { - if (flecs_ref_is_written(op, &op->first, EcsRuleFirst, written)) { - first = flecs_get_ref_entity(&op->first, flags_1st, ctx); - } else if (flags_1st & EcsRuleIsVar) { - first = EcsWildcard; + /* Specialized iterator modes. When a query doesn't use any advanced + * features, it can call specialized iterator functions directly instead of + * going through the dispatcher of the query engine. + * The iterator mode is set during iterator initialization. Besides being + * determined by the query, there are different modes for searching and + * testing, where searching returns all matches for a query, whereas testing + * tests a single table or table range against the query. */ + + if (it->flags & EcsIterTrivialCached) { + /* Cached iterator modes */ + if (it->flags & EcsIterTrivialSearch) { + if (flecs_query_is_cache_search(&ctx)) { + goto trivial_search_yield; + } + } else if (it->flags & EcsIterTrivialTest) { + if (flecs_query_is_cache_test(&ctx, redo)) { + goto yield; + } } - } - if (flags_2nd) { - if (flecs_ref_is_written(op, &op->second, EcsRuleSecond, written)) { - second = flecs_get_ref_entity(&op->second, flags_2nd, ctx); - } else if (flags_2nd & EcsRuleIsVar) { - second = EcsWildcard; + } else { + /* Uncached iterator modes */ + if (it->flags & EcsIterTrivialSearch) { + ecs_query_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; + if (flecs_query_is_trivial_search(&ctx, op_ctx, redo)) { + goto yield; + } + } else if (it->flags & EcsIterTrivialTest) { + int32_t fields = ctx.query->pub.term_count; + ecs_flags64_t mask = (2llu << (fields - 1)) - 1; + if (flecs_query_trivial_test(&ctx, redo, mask)) { + goto yield; + } + } else { + /* Default iterator mode. This enters the query VM dispatch loop. */ + if (flecs_query_run_until( + redo, &ctx, ops, -1, qit->op, impl->op_count - 1)) + { + ecs_assert(ops[ctx.op_index].kind == EcsQueryYield, + ECS_INTERNAL_ERROR, NULL); + flecs_query_set_iter_this(it, &ctx); + ecs_assert(it->count >= 0, ECS_INTERNAL_ERROR, NULL); + qit->op = flecs_itolbl(ctx.op_index - 1); + goto yield; + } } } - if (flags_2nd & (EcsRuleIsVar | EcsRuleIsEntity)) { - return ecs_pair(first, second); - } else { - return flecs_entities_get_generation(ctx->world, first); + /* Done iterating */ + flecs_query_mark_fixed_fields_dirty(impl, it); + if (ctx.query->monitor) { + flecs_query_update_fixed_monitor( + ECS_CONST_CAST(ecs_query_impl_t*, ctx.query)); } -} -static -ecs_id_t flecs_rule_op_get_id( - const ecs_rule_op_t *op, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - return flecs_rule_op_get_id_w_written(op, written, ctx); -} + ecs_iter_fini(it); + return false; -static -int16_t flecs_rule_next_column( - ecs_table_t *table, - ecs_id_t id, - int32_t column) -{ - if (!ECS_IS_PAIR(id) || (ECS_PAIR_FIRST(id) != EcsWildcard)) { - column = column + 1; - } else { - ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); - column = ecs_search_offset(NULL, table, column + 1, id, NULL); - ecs_assert(column != -1, ECS_INTERNAL_ERROR, NULL); - } - return flecs_ito(int16_t, column); -} +trivial_search_yield: + it->table = ctx.vars[0].range.table; + it->count = ecs_table_count(it->table); + it->entities = ecs_table_entities(it->table); -static -void flecs_rule_it_set_column( - ecs_iter_t *it, - int32_t field_index, - int32_t column) -{ - ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(field_index >= 0, ECS_INTERNAL_ERROR, NULL); - it->columns[field_index] = column + 1; +yield: + return true; } static -ecs_id_t flecs_rule_it_set_id( +void flecs_query_iter_fini_ctx( ecs_iter_t *it, - ecs_table_t *table, - int32_t field_index, - int32_t column) + ecs_query_iter_t *qit) { - ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(field_index >= 0, ECS_INTERNAL_ERROR, NULL); - return it->ids[field_index] = table->type.array[column]; + const ecs_query_impl_t *query = flecs_query_impl(qit->query); + int32_t i, count = query->op_count; + ecs_query_op_t *ops = query->ops; + ecs_query_op_ctx_t *ctx = qit->op_ctx; + ecs_allocator_t *a = flecs_query_get_allocator(it); + + for (i = 0; i < count; i ++) { + ecs_query_op_t *op = &ops[i]; + switch(op->kind) { + case EcsQueryTrav: + flecs_query_trav_cache_fini(a, &ctx[i].is.trav.cache); + break; + case EcsQueryUp: + case EcsQuerySelfUp: + case EcsQueryUnionEqUp: + case EcsQueryUnionEqSelfUp: { + ecs_trav_up_cache_t *cache = &ctx[i].is.up.cache; + if (cache->dir == EcsTravDown) { + flecs_query_down_cache_fini(a, cache); + } else { + flecs_query_up_cache_fini(cache); + } + break; + } + default: + break; + } + } } static -void flecs_rule_set_match( - const ecs_rule_op_t *op, - ecs_table_t *table, - int32_t column, - const ecs_rule_run_ctx_t *ctx) +void flecs_query_iter_fini( + ecs_iter_t *it) { - ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); - int32_t field_index = op->field_index; - if (field_index == -1) { - return; + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_assert(qit->query != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(qit->query, ecs_query_t); + int32_t op_count = flecs_query_impl(qit->query)->op_count; + int32_t var_count = flecs_query_impl(qit->query)->var_count; + +#ifdef FLECS_DEBUG + if (it->flags & EcsIterProfile) { + char *str = ecs_query_plan_w_profile(qit->query, it); + printf("%s\n", str); + ecs_os_free(str); } - ecs_iter_t *it = ctx->it; - flecs_rule_it_set_column(it, field_index, column); - ecs_id_t matched = flecs_rule_it_set_id(it, table, field_index, column); - flecs_rule_set_vars(op, matched, ctx); + flecs_iter_free_n(qit->profile, ecs_query_op_profile_t, op_count); +#endif + + flecs_query_iter_fini_ctx(it, qit); + flecs_iter_free_n(qit->vars, ecs_var_t, var_count); + flecs_iter_free_n(qit->written, ecs_write_flags_t, op_count); + flecs_iter_free_n(qit->op_ctx, ecs_query_op_ctx_t, op_count); + + qit->vars = NULL; + qit->written = NULL; + qit->op_ctx = NULL; + qit->query = NULL; } -static -void flecs_rule_set_trav_match( - const ecs_rule_op_t *op, - int32_t column, - ecs_entity_t trav, - ecs_entity_t second, - const ecs_rule_run_ctx_t *ctx) +ecs_iter_t flecs_query_iter( + const ecs_world_t *world, + const ecs_query_t *q) { - int32_t field_index = op->field_index; - if (field_index == -1) { - return; + ecs_iter_t it = {0}; + ecs_query_iter_t *qit = &it.priv_.iter.query; + ecs_check(q != NULL, ECS_INVALID_PARAMETER, NULL); + + flecs_poly_assert(q, ecs_query_t); + ecs_query_impl_t *impl = flecs_query_impl(q); + + int32_t i, var_count = impl->var_count; + int32_t op_count = impl->op_count ? impl->op_count : 1; + it.world = ECS_CONST_CAST(ecs_world_t*, world); + + /* If world passed to iterator is the real world, but query was created from + * a stage, stage takes precedence. */ + if (flecs_poly_is(it.world, ecs_world_t) && + flecs_poly_is(q->world, ecs_stage_t)) + { + it.world = ECS_CONST_CAST(ecs_world_t*, q->world); } - ecs_iter_t *it = ctx->it; - ecs_id_t matched = ecs_pair(trav, second); - it->ids[op->field_index] = matched; - if (column != -1) { - flecs_rule_it_set_column(it, op->field_index, column); + it.real_world = q->real_world; + ecs_assert(flecs_poly_is(it.real_world, ecs_world_t), + ECS_INTERNAL_ERROR, NULL); + ecs_check(!(it.real_world->flags & EcsWorldMultiThreaded) || + it.world != it.real_world, ECS_INVALID_PARAMETER, + "create iterator for stage when world is in multithreaded mode"); + + it.query = q; + it.system = q->entity; + it.next = ecs_query_next; + it.fini = flecs_query_iter_fini; + it.field_count = q->field_count; + it.sizes = q->sizes; + it.set_fields = q->set_fields; + it.ref_fields = q->fixed_fields | q->row_fields | q->var_fields; + it.row_fields = q->row_fields; + it.up_fields = 0; + flecs_query_apply_iter_flags(&it, q); + + flecs_iter_init(it.world, &it, + flecs_iter_cache_ids | + flecs_iter_cache_trs | + flecs_iter_cache_sources | + flecs_iter_cache_ptrs); + + qit->query = q; + qit->query_vars = impl->vars; + qit->ops = impl->ops; + + ecs_query_cache_t *cache = impl->cache; + if (cache) { + qit->node = cache->list.first; + qit->last = cache->list.last; + + if (cache->order_by_callback && cache->list.info.table_count) { + flecs_query_cache_sort_tables(it.real_world, impl); + qit->node = ecs_vec_first(&cache->table_slices); + qit->last = ecs_vec_last_t( + &cache->table_slices, ecs_query_cache_table_match_t); + } + + cache->prev_match_count = cache->match_count; + } + + if (var_count) { + qit->vars = flecs_iter_calloc_n(&it, ecs_var_t, var_count); } - flecs_rule_set_vars(op, matched, ctx); + + if (op_count) { + qit->written = flecs_iter_calloc_n(&it, ecs_write_flags_t, op_count); + qit->op_ctx = flecs_iter_calloc_n(&it, ecs_query_op_ctx_t, op_count); + } + +#ifdef FLECS_DEBUG + qit->profile = flecs_iter_calloc_n(&it, ecs_query_op_profile_t, op_count); +#endif + + for (i = 1; i < var_count; i ++) { + qit->vars[i].entity = EcsWildcard; + } + + it.variables = qit->vars; + it.variable_count = impl->pub.var_count; + it.variable_names = impl->pub.vars; + + /* Set flags for unconstrained query iteration. Can be reinitialized when + * variables are constrained on iterator. */ + flecs_query_iter_constrain(&it); +error: + return it; } -static -bool flecs_rule_table_filter( - ecs_table_t *table, - ecs_rule_lbl_t other, - ecs_flags32_t filter_mask) +ecs_iter_t ecs_query_iter( + const ecs_world_t *world, + const ecs_query_t *q) { - uint32_t filter = flecs_ito(uint32_t, other); - return (table->flags & filter_mask & filter) != 0; + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(q != NULL, ECS_INVALID_PARAMETER, NULL); + + if (!(q->flags & EcsQueryCacheYieldEmptyTables)) { + ecs_run_aperiodic(q->real_world, EcsAperiodicEmptyTables); + } + + /* Ok, only for stats */ + ecs_os_linc(&ECS_CONST_CAST(ecs_query_t*, q)->eval_count); + + ecs_query_impl_t *impl = flecs_query_impl(q); + ecs_query_cache_t *cache = impl->cache; + if (cache) { + /* If monitors changed, do query rematching */ + ecs_flags32_t flags = q->flags; + if (!(world->flags & EcsWorldReadonly) && flags & EcsQueryHasRefs) { + flecs_eval_component_monitors(q->world); + } + } + + return flecs_query_iter(world, q); } +/** + * @file query/engine/eval_member.c + * @brief Component member evaluation. + */ + + static -bool flecs_rule_select_w_id( - const ecs_rule_op_t *op, +bool flecs_query_member_cmp( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx, - ecs_id_t id, - ecs_flags32_t filter_mask) + ecs_query_run_ctx_t *ctx, + bool neq) { - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - ecs_id_record_t *idr = op_ctx->idr; - ecs_table_record_t *tr; - ecs_table_t *table; + ecs_table_range_t range; + if (op->other) { + ecs_var_id_t table_var = flecs_itovar(op->other - 1); + range = flecs_query_var_get_range(table_var, ctx); + } else { + range = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + } + + ecs_table_t *table = range.table; + if (!table) { + return false; + } + + ecs_query_membereq_ctx_t *op_ctx = flecs_op_ctx(ctx, membereq); + ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; + + if (!range.count) { + range.count = ecs_table_count(range.table); + } + + int32_t row, end = range.count; + if (end) { + end += range.offset; + } else { + end = ecs_table_count(range.table); + } + void *data; if (!redo) { - if (!idr || idr->id != id) { - idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); - if (!idr) { - return false; - } + row = op_ctx->each.row = range.offset; + + /* Get data ptr starting from offset 0 so we can use row to index */ + range.offset = 0; + + /* Populate data field so we have the array we can compare the member + * value against. */ + data = op_ctx->data = + ecs_table_get_column(range.table, it->trs[field_index]->column, 0); + + it->ids[field_index] = ctx->query->pub.terms[op->term_index].id; + } else { + row = ++ op_ctx->each.row; + if (op_ctx->each.row >= end) { + return false; } - if (ctx->rule->filter.flags & EcsFilterMatchEmptyTables) { - if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { - return false; - } - } else { - if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { - return false; + data = op_ctx->data; + } + + int32_t offset = (int32_t)op->first.entity; + int32_t size = (int32_t)(op->first.entity >> 32); + const ecs_entity_t *entities = ecs_table_entities(table); + ecs_entity_t e = 0; + ecs_entity_t *val; + + ecs_assert(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); + ecs_assert(data != NULL, ECS_INTERNAL_ERROR, NULL); /* Must be written */ + ecs_assert(entities != NULL, ECS_INTERNAL_ERROR, NULL); + + bool second_written = true; + if (op->flags & (EcsQueryIsVar << EcsQuerySecond)) { + uint64_t written = ctx->written[ctx->op_index]; + second_written = written & (1ull << op->second.var); + } + + if (second_written) { + ecs_flags16_t second_flags = flecs_query_ref_flags( + op->flags, EcsQuerySecond); + ecs_entity_t second = flecs_get_ref_entity( + &op->second, second_flags, ctx); + + do { + e = entities[row]; + + val = ECS_OFFSET(ECS_ELEM(data, size, row), offset); + if (val[0] == second || second == EcsWildcard) { + if (!neq) { + goto match; + } + } else { + if (neq) { + goto match; + } } - } - } -repeat: - if (!redo || !op_ctx->remaining) { - tr = flecs_table_cache_next(&op_ctx->it, ecs_table_record_t); - if (!tr) { - return false; - } + row ++; + } while (row < end); - op_ctx->column = flecs_ito(int16_t, tr->index); - op_ctx->remaining = flecs_ito(int16_t, tr->count - 1); - table = tr->hdr.table; - flecs_rule_var_set_table(op, op->src.var, table, 0, 0, ctx); + return false; } else { - tr = (ecs_table_record_t*)op_ctx->it.cur; - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - table = tr->hdr.table; - op_ctx->column = flecs_rule_next_column(table, idr->id, op_ctx->column); - op_ctx->remaining --; + e = entities[row]; + val = ECS_OFFSET(ECS_ELEM(data, size, row), offset); + flecs_query_var_set_entity(op, op->second.var, val[0], ctx); } - if (flecs_rule_table_filter(table, op->other, filter_mask)) { - goto repeat; +match: + if (op->other) { + ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); + flecs_query_var_set_entity(op, op->src.var, e, ctx); } - flecs_rule_set_match(op, table, op_ctx->column, ctx); + ecs_entity_t mbr = ECS_PAIR_FIRST(it->ids[field_index]); + it->ids[field_index] = ecs_pair(mbr, val[0]); + + op_ctx->each.row = row; + return true; } -static -bool flecs_rule_select( - const ecs_rule_op_t *op, +bool flecs_query_member_eq( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + ecs_query_run_ctx_t *ctx) { - ecs_id_t id = 0; - if (!redo) { - id = flecs_rule_op_get_id(op, ctx); - } - return flecs_rule_select_w_id(op, redo, ctx, id, - (EcsTableIsPrefab|EcsTableIsDisabled)); + return flecs_query_member_cmp(op, redo, ctx, false); } -static -bool flecs_rule_with( - const ecs_rule_op_t *op, +bool flecs_query_member_neq( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + ecs_query_run_ctx_t *ctx) { - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - ecs_id_record_t *idr = op_ctx->idr; - const ecs_table_record_t *tr; + return flecs_query_member_cmp(op, redo, ctx, true); +} - ecs_table_t *table = flecs_rule_get_table(op, &op->src, EcsRuleSrc, ctx); - if (!table) { +/** + * @file query/engine/eval_pred.c + * @brief Equality predicate evaluation. + */ + + +static +const char* flecs_query_name_arg( + const ecs_query_op_t *op, + ecs_query_run_ctx_t *ctx) +{ + int8_t term_index = op->term_index; + const ecs_term_t *term = &ctx->query->pub.terms[term_index]; + return term->second.name; +} + +static +bool flecs_query_compare_range( + const ecs_table_range_t *l, + const ecs_table_range_t *r) +{ + if (l->table != r->table) { return false; } - if (!redo) { - ecs_id_t id = flecs_rule_op_get_id(op, ctx); - if (!idr || idr->id != id) { - idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); - if (!idr) { - return false; - } - } - - tr = flecs_id_record_get_table(idr, table); - if (!tr) { + if (l->count) { + int32_t l_end = l->offset + l->count; + int32_t r_end = r->offset + r->count; + if (r->offset < l->offset) { return false; } - - op_ctx->column = flecs_ito(int16_t, tr->index); - op_ctx->remaining = flecs_ito(int16_t, tr->count); - } else { - if (--op_ctx->remaining <= 0) { + if (r_end > l_end) { return false; } - - op_ctx->column = flecs_rule_next_column(table, idr->id, op_ctx->column); - ecs_assert(op_ctx->column != -1, ECS_INTERNAL_ERROR, NULL); + } else { + /* Entire table is matched */ } - flecs_rule_set_match(op, table, op_ctx->column, ctx); return true; } static -bool flecs_rule_and( - const ecs_rule_op_t *op, +bool flecs_query_pred_eq_w_range( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + ecs_query_run_ctx_t *ctx, + ecs_table_range_t r) { + if (redo) { + return false; + } + uint64_t written = ctx->written[ctx->op_index]; - if (written & (1ull << op->src.var)) { - return flecs_rule_with(op, redo, ctx); + ecs_var_id_t src_var = op->src.var; + if (!(written & (1ull << src_var))) { + /* left = unknown, right = known. Assign right-hand value to left */ + ecs_var_id_t l = src_var; + ctx->vars[l].range = r; + if (r.count == 1) { + ctx->vars[l].entity = ecs_table_entities(r.table)[r.offset]; + } + return true; } else { - return flecs_rule_select(op, redo, ctx); + ecs_table_range_t l = flecs_query_get_range( + op, &op->src, EcsQuerySrc, ctx); + + if (!flecs_query_compare_range(&l, &r)) { + return false; + } + + ctx->vars[src_var].range.offset = r.offset; + ctx->vars[src_var].range.count = r.count; + return true; } } -static -bool flecs_rule_select_id( - const ecs_rule_op_t *op, +bool flecs_query_pred_eq( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx, - ecs_flags32_t table_filter) + ecs_query_run_ctx_t *ctx) { - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - ecs_iter_t *it = ctx->it; - int8_t field = op->field_index; - ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL); - - if (!redo) { - ecs_id_t id = it->ids[field]; - ecs_id_record_t *idr = op_ctx->idr; - if (!idr || idr->id != id) { - idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); - if (!idr) { - return false; - } - } + uint64_t written = ctx->written[ctx->op_index]; (void)written; + ecs_assert(flecs_ref_is_written(op, &op->second, EcsQuerySecond, written), + ECS_INTERNAL_ERROR, + "invalid instruction sequence: uninitialized eq operand"); - if (ctx->rule->filter.flags & EcsFilterMatchEmptyTables) { - if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { - return false; - } - } else { - if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { - return false; - } - } - } + ecs_table_range_t r = flecs_query_get_range( + op, &op->second, EcsQuerySecond, ctx); + return flecs_query_pred_eq_w_range(op, redo, ctx, r); +} -repeat: {} - const ecs_table_record_t *tr = flecs_table_cache_next( - &op_ctx->it, ecs_table_record_t); - if (!tr) { +bool flecs_query_pred_eq_name( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + const char *name = flecs_query_name_arg(op, ctx); + ecs_entity_t e = ecs_lookup(ctx->world, name); + if (!e) { + /* Entity doesn't exist */ return false; } - ecs_table_t *table = tr->hdr.table; - if (flecs_rule_table_filter(table, op->other, table_filter)) { - goto repeat; - } - - flecs_rule_var_set_table(op, op->src.var, table, 0, 0, ctx); - flecs_rule_it_set_column(it, field, tr->index); - return true; + ecs_table_range_t r = flecs_range_from_entity(e, ctx); + return flecs_query_pred_eq_w_range(op, redo, ctx, r); } -static -bool flecs_rule_with_id( - const ecs_rule_op_t *op, +bool flecs_query_pred_neq_w_range( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + ecs_query_run_ctx_t *ctx, + ecs_table_range_t r) { - if (redo) { - return false; + ecs_query_eq_ctx_t *op_ctx = flecs_op_ctx(ctx, eq); + ecs_var_id_t src_var = op->src.var; + ecs_table_range_t l = flecs_query_get_range( + op, &op->src, EcsQuerySrc, ctx); + + /* If tables don't match, neq always returns once */ + if (l.table != r.table) { + return true && !redo; } - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - ecs_iter_t *it = ctx->it; - int8_t field = op->field_index; - ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL); + int32_t l_offset; + int32_t l_count; + if (!redo) { + /* Make sure we're working with the correct table count */ + if (!l.count && l.table) { + l.count = ecs_table_count(l.table); + } - ecs_table_t *table = flecs_rule_get_table(op, &op->src, EcsRuleSrc, ctx); - if (!table) { - return false; + l_offset = l.offset; + l_count = l.count; + + /* Cache old value */ + op_ctx->range = l; + } else { + l_offset = op_ctx->range.offset; + l_count = op_ctx->range.count; } - ecs_id_t id = it->ids[field]; - ecs_id_record_t *idr = op_ctx->idr; - if (!idr || idr->id != id) { - idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); - if (!idr) { + /* If the table matches, a Neq returns twice: once for the slice before the + * excluded slice, once for the slice after the excluded slice. If the right + * hand range starts & overlaps with the left hand range, there is only + * one slice. */ + ecs_var_t *var = &ctx->vars[src_var]; + if (!redo && r.offset > l_offset) { + int32_t end = r.offset; + if (end > l_count) { + end = l_count; + } + + /* Return first slice */ + var->range.table = l.table; + var->range.offset = l_offset; + var->range.count = end - l_offset; + op_ctx->redo = false; + return true; + } else if (!op_ctx->redo) { + int32_t l_end = op_ctx->range.offset + l_count; + int32_t r_end = r.offset + r.count; + + if (l_end <= r_end) { + /* If end of existing range falls inside the excluded range, there's + * nothing more to return */ + var->range = l; return false; } - } - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (!tr) { + /* Return second slice */ + var->range.table = l.table; + var->range.offset = r_end; + var->range.count = l_end - r_end; + + /* Flag so we know we're done the next redo */ + op_ctx->redo = true; + return true; + } else { + /* Restore previous value */ + var->range = l; return false; } - - flecs_rule_it_set_column(it, field, tr->index); - return true; } static -bool flecs_rule_and_id( - const ecs_rule_op_t *op, +bool flecs_query_pred_match( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + ecs_query_run_ctx_t *ctx, + bool is_neq) { + ecs_query_eq_ctx_t *op_ctx = flecs_op_ctx(ctx, eq); uint64_t written = ctx->written[ctx->op_index]; - if (written & (1ull << op->src.var)) { - return flecs_rule_with_id(op, redo, ctx); - } else { - return flecs_rule_select_id(op, redo, ctx, - (EcsTableIsPrefab|EcsTableIsDisabled)); - } -} + ecs_assert(flecs_ref_is_written(op, &op->src, EcsQuerySrc, written), + ECS_INTERNAL_ERROR, + "invalid instruction sequence: uninitialized match operand"); + (void)written; -static -bool flecs_rule_up_select( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx, - bool self, - bool id_only) -{ - ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); - ecs_world_t *world = ctx->world; - ecs_iter_t *it = ctx->it; - bool redo_select = redo; - const ecs_filter_t *filter = &ctx->rule->filter; + ecs_var_id_t src_var = op->src.var; + const char *match = flecs_query_name_arg(op, ctx); + ecs_table_range_t l; + if (!redo) { + l = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + if (!l.table) { + return false; + } - /* Early out if traversal relationship doesn't exist */ - op_ctx->trav = filter->terms[op->term_index].src.trav; - if (!op_ctx->idr_trav) { - op_ctx->idr_trav = flecs_id_record_get(ctx->world, - ecs_pair(op_ctx->trav, EcsWildcard)); - } - if (!op_ctx->idr_trav || !flecs_table_cache_count(&op_ctx->idr_trav->cache)){ - if (!self) { + if (!l.count) { + l.count = ecs_table_count(l.table); + } + + op_ctx->range = l; + op_ctx->index = l.offset; + op_ctx->name_col = flecs_ito(int16_t, + ecs_table_get_type_index(ctx->world, l.table, + ecs_pair(ecs_id(EcsIdentifier), EcsName))); + if (op_ctx->name_col == -1) { + return is_neq; + } + op_ctx->name_col = flecs_ito(int16_t, + l.table->column_map[op_ctx->name_col]); + ecs_assert(op_ctx->name_col != -1, ECS_INTERNAL_ERROR, NULL); + } else { + if (op_ctx->name_col == -1) { + /* Table has no name */ return false; - } else if (id_only) { - return flecs_rule_select_id(op, redo, ctx, - (EcsTableIsPrefab|EcsTableIsDisabled)); - } else { - return flecs_rule_select(op, redo, ctx); } + + l = op_ctx->range; } - if (!redo) { - op_ctx->with = flecs_rule_op_get_id(op, ctx); - op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); - if (!op_ctx->idr_with) { - return false; + const EcsIdentifier *names = l.table->data.columns[op_ctx->name_col].data; + int32_t count = l.offset + l.count, offset = -1; + for (; op_ctx->index < count; op_ctx->index ++) { + const char *name = names[op_ctx->index].value; + bool result = strstr(name, match); + if (is_neq) { + result = !result; } - op_ctx->down = NULL; - op_ctx->cache_elem = 0; + if (!result) { + if (offset != -1) { + break; + } + } else { + if (offset == -1) { + offset = op_ctx->index; + } + } } - ecs_trav_down_t *down = op_ctx->down; + if (offset == -1) { + ctx->vars[src_var].range = op_ctx->range; + return false; + } - do { - while (!down) { - ecs_table_t *table = op_ctx->table; - if (!table) { - ecs_table_range_t range; - it->sources[op->field_index] = 0; - do { - bool result; - if (id_only) { - result = flecs_rule_select_id(op, redo_select, ctx, 0); - } else { - result = flecs_rule_select_w_id(op, redo_select, ctx, - op_ctx->with, 0); - } - if (!result) { - return false; - } + ctx->vars[src_var].range.offset = offset; + ctx->vars[src_var].range.count = (op_ctx->index - offset); + return true; +} - redo_select = true; +bool flecs_query_pred_eq_match( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + return flecs_query_pred_match(op, redo, ctx, false); +} - range = flecs_rule_get_range( - op, &op->src, EcsRuleSrc, ctx); - ecs_assert(range.table != NULL, ECS_INTERNAL_ERROR, NULL); - } while (!self && range.table->_->traversable_count == 0); +bool flecs_query_pred_neq_match( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + return flecs_query_pred_match(op, redo, ctx, true); +} - if (!range.count) { - range.count = ecs_table_count(range.table); - } +bool flecs_query_pred_neq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; (void)written; + ecs_assert(flecs_ref_is_written(op, &op->second, EcsQuerySecond, written), + ECS_INTERNAL_ERROR, + "invalid instruction sequence: uninitialized neq operand"); - table = op_ctx->table = range.table; - op_ctx->row = range.offset; - op_ctx->end = range.offset + range.count; - op_ctx->matched = it->ids[op->field_index]; + ecs_table_range_t r = flecs_query_get_range( + op, &op->second, EcsQuerySecond, ctx); + return flecs_query_pred_neq_w_range(op, redo, ctx, r); +} - if (self) { - if (!flecs_rule_table_filter(table, op->other, - (EcsTableIsPrefab|EcsTableIsDisabled))) - { - flecs_reset_source_set_flag(ctx, op->field_index); - op_ctx->row --; - return true; - } - } +bool flecs_query_pred_neq_name( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + const char *name = flecs_query_name_arg(op, ctx); + ecs_entity_t e = ecs_lookup(ctx->world, name); + if (!e) { + /* Entity doesn't exist */ + return true && !redo; + } - redo_select = true; - } else { - op_ctx->row ++; - } + ecs_table_range_t r = flecs_range_from_entity(e, ctx); + return flecs_query_pred_neq_w_range(op, redo, ctx, r); +} - if (table->_->traversable_count == 0) { - op_ctx->table = NULL; - continue; - } else { - int32_t row; - ecs_entity_t entity = 0; - ecs_entity_t *entities = flecs_table_entities_array(table); - - for (row = op_ctx->row; row < op_ctx->end; row ++) { - entity = entities[row]; - ecs_record_t *record = flecs_entities_get(world, entity); - if (record->row & EcsEntityIsTraversable) { - it->sources[op->field_index] = entity; - break; - } - } +/** + * @file query/engine/eval_toggle.c + * @brief Bitset toggle evaluation. + */ - if (row == op_ctx->end) { - op_ctx->table = NULL; - continue; - } - down = op_ctx->down = flecs_rule_get_down_cache(ctx, &op_ctx->cache, - op_ctx->trav, entity, op_ctx->idr_with, self); - op_ctx->cache_elem = -1; - } +typedef struct { + ecs_flags64_t mask; + bool has_bitset; +} flecs_query_row_mask_t; + +static +flecs_query_row_mask_t flecs_query_get_row_mask( + ecs_iter_t *it, + ecs_table_t *table, + int32_t block_index, + ecs_flags64_t and_fields, + ecs_flags64_t not_fields, + ecs_query_toggle_ctx_t *op_ctx) +{ + ecs_flags64_t mask = UINT64_MAX; + int32_t i, field_count = it->field_count; + ecs_flags64_t fields = and_fields | not_fields; + bool has_bitset = false; + + for (i = 0; i < field_count; i ++) { + uint64_t field_bit = 1llu << i; + if (!(fields & field_bit)) { + continue; + } + + if (not_fields & field_bit) { + it->set_fields &= (ecs_termset_t)~field_bit; + } else if (and_fields & field_bit) { + ecs_assert(it->set_fields & field_bit, ECS_INTERNAL_ERROR, NULL); + } else { + ecs_abort(ECS_INTERNAL_ERROR, NULL); } -next_elem: - if ((++ op_ctx->cache_elem) >= ecs_vec_count(&down->elems)) { - down = NULL; + ecs_id_t id = it->ids[i]; + ecs_bitset_t *bs = flecs_table_get_toggle(table, id); + if (!bs) { + if (not_fields & field_bit) { + if (op_ctx->prev_set_fields & field_bit) { + has_bitset = false; + break; + } + } continue; } - ecs_trav_down_elem_t *elem = ecs_vec_get_t( - &down->elems, ecs_trav_down_elem_t, op_ctx->cache_elem); + ecs_assert((64 * block_index) < bs->size, ECS_INTERNAL_ERROR, NULL); + ecs_flags64_t block = bs->data[block_index]; + + if (not_fields & field_bit) { + block = ~block; + } + mask &= block; + has_bitset = true; + } - flecs_rule_var_set_table(op, op->src.var, elem->table, 0, 0, ctx); - flecs_rule_set_vars(op, op_ctx->matched, ctx); + return (flecs_query_row_mask_t){ mask, has_bitset }; +} - if (flecs_rule_table_filter(elem->table, op->other, - (EcsTableIsPrefab|EcsTableIsDisabled))) - { - goto next_elem; +static +bool flecs_query_toggle_for_up( + ecs_iter_t *it, + ecs_flags64_t and_fields, + ecs_flags64_t not_fields) +{ + int32_t i, field_count = it->field_count; + ecs_flags64_t fields = (and_fields | not_fields) & it->up_fields; + + for (i = 0; i < field_count; i ++) { + uint64_t field_bit = 1llu << i; + if (!(fields & field_bit)) { + continue; } - break; - } while (true); + bool match = false; + if ((it->set_fields & field_bit)) { + ecs_entity_t src = it->sources[i]; + ecs_assert(src != 0, ECS_INTERNAL_ERROR, NULL); + match = ecs_is_enabled_id(it->world, src, it->ids[i]); + } + + if (field_bit & not_fields) { + match = !match; + } - flecs_set_source_set_flag(ctx, op->field_index); + if (!match) { + return false; + } + } return true; } static -bool flecs_rule_up_with( - const ecs_rule_op_t *op, +bool flecs_query_toggle_cmp( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + ecs_query_run_ctx_t *ctx, + ecs_flags64_t and_fields, + ecs_flags64_t not_fields) { - const ecs_filter_t *filter = &ctx->rule->filter; - ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); ecs_iter_t *it = ctx->it; + ecs_query_toggle_ctx_t *op_ctx = flecs_op_ctx(ctx, toggle); + ecs_table_range_t range = flecs_query_get_range( + op, &op->src, EcsQuerySrc, ctx); + ecs_table_t *table = range.table; + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - /* Early out if traversal relationship doesn't exist */ - op_ctx->trav = filter->terms[op->term_index].src.trav; - if (!op_ctx->idr_trav) { - op_ctx->idr_trav = flecs_id_record_get(ctx->world, - ecs_pair(op_ctx->trav, EcsWildcard)); - } - if (!op_ctx->idr_trav || !flecs_table_cache_count(&op_ctx->idr_trav->cache)){ + if ((and_fields & op_ctx->prev_set_fields) != and_fields) { + /* If not all fields matching and toggles are set, table can't match */ return false; } + ecs_flags32_t up_fields = it->up_fields; if (!redo) { - op_ctx->trav = filter->terms[op->term_index].src.trav; - op_ctx->with = flecs_rule_op_get_id(op, ctx); - op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); + if (up_fields & (and_fields|not_fields)) { + /* If there are toggle fields that were matched with query + * traversal, evaluate those separately. */ + if (!flecs_query_toggle_for_up(it, and_fields, not_fields)) { + return false; + } - if (!op_ctx->idr_with) { - return false; + it->set_fields &= (ecs_termset_t)~(not_fields & up_fields); } + } - ecs_table_range_t range = flecs_rule_get_range( - op, &op->src, EcsRuleSrc, ctx); - if (!range.table) { + /* Shared fields are evaluated, can be ignored from now on */ + // and_fields &= ~up_fields; + not_fields &= ~up_fields; + + if (!(table->flags & EcsTableHasToggle)) { + if (not_fields) { + /* If any of the toggle fields with a not operator are for fields + * that are set, without a bitset those fields can't match. */ return false; + } else { + /* If table doesn't have toggles but query matched toggleable + * components, all entities match. */ + if (!redo) { + return true; + } else { + return false; + } } + } - ecs_trav_up_t *up = flecs_rule_get_up_cache(ctx, &op_ctx->cache, - range.table, op_ctx->with, op_ctx->trav, op_ctx->idr_with, - op_ctx->idr_trav); - - if (!up) { + if (table && !range.count) { + range.count = ecs_table_count(table); + if (!range.count) { return false; } - - it->sources[op->field_index] = flecs_entities_get_generation( - ctx->world, up->src); - it->columns[op->field_index] = up->column + 1; - it->ids[op->field_index] = up->id; - flecs_rule_set_vars(op, up->id, ctx); - flecs_set_source_set_flag(ctx, op->field_index); - return true; - } else { - return false; } -} -static -bool flecs_rule_self_up_with( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx, - bool id_only) -{ + int32_t i, j; + int32_t first, last, block_index, cur; + uint64_t block = 0; if (!redo) { - bool result; - if (id_only) { - result = flecs_rule_with_id(op, redo, ctx); - } else { - result = flecs_rule_with(op, redo, ctx); + op_ctx->range = range; + cur = op_ctx->cur = range.offset; + block_index = op_ctx->block_index = -1; + first = range.offset; + last = range.offset + range.count; + } else { + if (!op_ctx->has_bitset) { + goto done; } - if (result) { - ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); - op_ctx->trav = 0; - if (flecs_rule_ref_flags(op->flags, EcsRuleSrc) & EcsRuleIsVar) { - ecs_iter_t *it = ctx->it; - it->sources[op->field_index] = 0; - } - return true; + + last = op_ctx->range.offset + op_ctx->range.count; + cur = op_ctx->cur; + ecs_assert(cur <= last, ECS_INTERNAL_ERROR, NULL); + if (cur == last) { + goto done; } - flecs_reset_source_set_flag(ctx, op->field_index); + first = cur; + block_index = op_ctx->block_index; + block = op_ctx->block; + } - return flecs_rule_up_with(op, redo, ctx); - } else { - ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); - if (op_ctx->trav == 0) { - return flecs_rule_with(op, redo, ctx); + /* If end of last iteration is start of new block, compute new block */ + int32_t new_block_index = cur / 64, row = first; + if (new_block_index != block_index) { +compute_block: + block_index = op_ctx->block_index = new_block_index; + + flecs_query_row_mask_t row_mask = flecs_query_get_row_mask( + it, table, block_index, and_fields, not_fields, op_ctx); + + /* If table doesn't have bitset columns, all columns match */ + if (!(op_ctx->has_bitset = row_mask.has_bitset)) { + if (!not_fields) { + return true; + } else { + goto done; + } } - } - return false; -} + /* No enabled bits */ + block = row_mask.mask; + if (!block) { +next_block: + new_block_index ++; + cur = new_block_index * 64; + if (cur >= last) { + /* No more rows */ + goto done; + } -static -bool flecs_rule_up( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - return flecs_rule_up_with(op, redo, ctx); - } else { - return flecs_rule_up_select(op, redo, ctx, false, false); - } -} + op_ctx->cur = cur; + goto compute_block; + } -static -bool flecs_rule_self_up( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - return flecs_rule_self_up_with(op, redo, ctx, false); - } else { - return flecs_rule_up_select(op, redo, ctx, true, false); + op_ctx->block = block; } -} -static -bool flecs_rule_up_id( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - return flecs_rule_up_with(op, redo, ctx); - } else { - return flecs_rule_up_select(op, redo, ctx, false, true); + /* Find first enabled bit (TODO: use faster bitmagic) */ + int32_t first_bit = cur - (block_index * 64); + int32_t last_bit = ECS_MIN(64, last - (block_index * 64)); + ecs_assert(first_bit >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(first_bit < 64, ECS_INTERNAL_ERROR, NULL); + ecs_assert(last_bit >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(last_bit <= 64, ECS_INTERNAL_ERROR, NULL); + ecs_assert(last_bit >= first_bit, ECS_INTERNAL_ERROR, NULL); + + for (i = first_bit; i < last_bit; i ++) { + uint64_t bit = (1ull << i); + bool cond = 0 != (block & bit); + if (cond) { + /* Find last enabled bit */ + for (j = i; j < last_bit; j ++) { + bit = (1ull << j); + cond = !(block & bit); + if (cond) { + break; + } + } + + row = i + (block_index * 64); + cur = j + (block_index * 64); + break; + } } -} -static -bool flecs_rule_self_up_id( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - return flecs_rule_self_up_with(op, redo, ctx, true); - } else { - return flecs_rule_up_select(op, redo, ctx, true, true); + if (i == last_bit) { + goto next_block; } -} -static -bool flecs_rule_select_any( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - return flecs_rule_select_w_id(op, redo, ctx, EcsAny, - (EcsTableIsPrefab|EcsTableIsDisabled)); -} + ecs_assert(row >= first, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur <= last, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur >= first, ECS_INTERNAL_ERROR, NULL); -static -bool flecs_rule_and_any( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_flags16_t match_flags = op->match_flags; - if (redo) { - if (match_flags & EcsTermMatchAnySrc) { - return false; - } + if (!(cur - row)) { + goto done; } - uint64_t written = ctx->written[ctx->op_index]; - int32_t remaining = 1; - bool result; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - result = flecs_rule_with(op, redo, ctx); - } else { - result = flecs_rule_select(op, redo, ctx); - remaining = 0; + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, row, cur - row, ctx); } + op_ctx->cur = cur; - if (!redo) { - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - if (match_flags & EcsTermMatchAny && op_ctx->remaining) { - op_ctx->remaining = flecs_ito(int16_t, remaining); - } - } + return true; - int32_t field = op->field_index; - if (field != -1) { - ctx->it->ids[field] = flecs_rule_op_get_id(op, ctx); +done: + /* Restore range & set fields */ + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, + table, op_ctx->range.offset, op_ctx->range.count, ctx); } - return result; + it->set_fields = op_ctx->prev_set_fields; + return false; } -static -bool flecs_rule_triv( - const ecs_rule_op_t *op, +bool flecs_query_toggle( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + ecs_query_run_ctx_t *ctx) { - ecs_rule_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); - int32_t until = flecs_ito(int32_t, op->other); - uint64_t written = ctx->written[ctx->op_index]; - ctx->written[ctx->op_index] |= 1ull; - if (written & 1ull) { - return flecs_rule_trivial_test(ctx->rule, ctx, !redo, until); - } else { - return flecs_rule_trivial_search_nodata(ctx->rule, ctx, op_ctx, !redo, until); + ecs_iter_t *it = ctx->it; + ecs_query_toggle_ctx_t *op_ctx = flecs_op_ctx(ctx, toggle); + if (!redo) { + op_ctx->prev_set_fields = it->set_fields; } + + ecs_flags64_t and_fields = op->first.entity; + ecs_flags64_t not_fields = op->second.entity & op_ctx->prev_set_fields; + + return flecs_query_toggle_cmp( + op, redo, ctx, and_fields, not_fields); } -static -bool flecs_rule_triv_data( - const ecs_rule_op_t *op, +bool flecs_query_toggle_option( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + ecs_query_run_ctx_t *ctx) { - ecs_rule_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); - int32_t until = flecs_ito(int32_t, op->other); - uint64_t written = ctx->written[ctx->op_index]; - ctx->written[ctx->op_index] |= 1ull; - if (written & 1ull) { - return flecs_rule_trivial_test(ctx->rule, ctx, !redo, until); - } else { - return flecs_rule_trivial_search(ctx->rule, ctx, op_ctx, !redo, until); + ecs_iter_t *it = ctx->it; + ecs_query_toggle_ctx_t *op_ctx = flecs_op_ctx(ctx, toggle); + if (!redo) { + op_ctx->prev_set_fields = it->set_fields; + op_ctx->optional_not = false; + op_ctx->has_bitset = false; } -} -static -bool flecs_rule_triv_wildcard( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); - int32_t until = flecs_ito(int32_t, op->other); - uint64_t written = ctx->written[ctx->op_index]; - ctx->written[ctx->op_index] |= 1ull; - if (written & 1ull) { - return flecs_rule_trivial_test_w_wildcards(ctx->rule, ctx, !redo, until); +repeat: {} + ecs_flags64_t and_fields = 0, not_fields = 0; + if (op_ctx->optional_not) { + not_fields = op->first.entity & op_ctx->prev_set_fields; } else { - return flecs_rule_trivial_search_w_wildcards(ctx->rule, ctx, op_ctx, !redo, until); + and_fields = op->first.entity; + } + + bool result = flecs_query_toggle_cmp( + op, redo, ctx, and_fields, not_fields); + if (!result) { + if (!op_ctx->optional_not) { + /* Run the not-branch of optional fields */ + op_ctx->optional_not = true; + it->set_fields = op_ctx->prev_set_fields; + redo = false; + goto repeat; + } } + + return result; } + +/** + * @file query/engine/eval_trav.c + * @brief Transitive/reflexive relationship traversal. + */ + + static -bool flecs_rule_trav_fixed_src_reflexive( - const ecs_rule_op_t *op, - const ecs_rule_run_ctx_t *ctx, +bool flecs_query_trav_fixed_src_reflexive( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx, ecs_table_range_t *range, ecs_entity_t trav, ecs_entity_t second) { ecs_table_t *table = range->table; ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t *entities = table->data.entities.array; + const ecs_entity_t *entities = ecs_table_entities(table); int32_t count = range->count; if (!count) { count = ecs_table_count(table); @@ -64076,7 +71944,7 @@ bool flecs_rule_trav_fixed_src_reflexive( if (count > 1) { /* If the range contains more than one entity, set the range to * return only the entity matched by the reflexive property. */ - ecs_assert(flecs_rule_ref_flags(op->flags, EcsRuleSrc) & EcsRuleIsVar, + ecs_assert(flecs_query_ref_flags(op->flags, EcsQuerySrc) & EcsQueryIsVar, ECS_INTERNAL_ERROR, NULL); ecs_var_t *var = &ctx->vars[op->src.var]; ecs_table_range_t *var_range = &var->range; @@ -64085,71 +71953,83 @@ bool flecs_rule_trav_fixed_src_reflexive( var->entity = entities[i]; } - flecs_rule_set_trav_match(op, -1, trav, second, ctx); + flecs_query_set_trav_match(op, NULL, trav, second, ctx); return true; } static -bool flecs_rule_trav_unknown_src_reflexive( - const ecs_rule_op_t *op, - const ecs_rule_run_ctx_t *ctx, +bool flecs_query_trav_unknown_src_reflexive( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx, ecs_entity_t trav, ecs_entity_t second) { - ecs_assert(flecs_rule_ref_flags(op->flags, EcsRuleSrc) & EcsRuleIsVar, + ecs_assert(flecs_query_ref_flags(op->flags, EcsQuerySrc) & EcsQueryIsVar, ECS_INTERNAL_ERROR, NULL); ecs_var_id_t src_var = op->src.var; - flecs_rule_var_set_entity(op, src_var, second, ctx); - flecs_rule_var_get_table(src_var, ctx); - flecs_rule_set_trav_match(op, -1, trav, second, ctx); + flecs_query_var_set_entity(op, src_var, second, ctx); + flecs_query_var_get_table(src_var, ctx); + + ecs_table_t *table = ctx->vars[src_var].range.table; + if (table) { + if (flecs_query_table_filter(table, op->other, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) + { + return false; + } + } + + flecs_query_set_trav_match(op, NULL, trav, second, ctx); return true; } static -bool flecs_rule_trav_fixed_src_up_fixed_second( - const ecs_rule_op_t *op, +bool flecs_query_trav_fixed_src_up_fixed_second( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { if (redo) { return false; /* If everything's fixed, can only have a single result */ } - ecs_flags16_t f_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t f_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); - ecs_flags16_t f_src = flecs_rule_ref_flags(op->flags, EcsRuleSrc); + ecs_flags16_t f_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t f_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); + ecs_flags16_t f_src = flecs_query_ref_flags(op->flags, EcsQuerySrc); ecs_entity_t trav = flecs_get_ref_entity(&op->first, f_1st, ctx); ecs_entity_t second = flecs_get_ref_entity(&op->second, f_2nd, ctx); ecs_table_range_t range = flecs_get_ref_range(&op->src, f_src, ctx); ecs_table_t *table = range.table; /* Check if table has transitive relationship by traversing upwards */ - int32_t column = ecs_search_relation(ctx->world, table, 0, - ecs_pair(trav, second), trav, EcsSelf|EcsUp, NULL, NULL, NULL); - if (column == -1) { + ecs_table_record_t *tr = NULL; + ecs_search_relation(ctx->world, table, 0, + ecs_pair(trav, second), trav, EcsSelf|EcsUp, NULL, NULL, &tr); + + if (!tr) { if (op->match_flags & EcsTermReflexive) { - return flecs_rule_trav_fixed_src_reflexive(op, ctx, + return flecs_query_trav_fixed_src_reflexive(op, ctx, &range, trav, second); } else { return false; } } - flecs_rule_set_trav_match(op, column, trav, second, ctx); + flecs_query_set_trav_match(op, tr, trav, second, ctx); return true; } static -bool flecs_rule_trav_unknown_src_up_fixed_second( - const ecs_rule_op_t *op, +bool flecs_query_trav_unknown_src_up_fixed_second( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { - ecs_flags16_t f_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t f_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); + ecs_flags16_t f_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t f_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); ecs_entity_t trav = flecs_get_ref_entity(&op->first, f_1st, ctx); ecs_entity_t second = flecs_get_ref_entity(&op->second, f_2nd, ctx); - ecs_rule_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); + ecs_query_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); if (!redo) { ecs_record_t *r_second = flecs_entities_get(ctx->world, second); @@ -64160,22 +72040,27 @@ bool flecs_rule_trav_unknown_src_up_fixed_second( /* If there's no record for the entity, it can't have a subtree so * forward operation to a regular select. */ - return flecs_rule_select(op, redo, ctx); + return flecs_query_select(op, redo, ctx); } /* Entity is traversable, which means it could have a subtree */ - flecs_rule_get_trav_down_cache(ctx, &trav_ctx->cache, trav, second); + flecs_query_get_trav_down_cache(ctx, &trav_ctx->cache, trav, second); trav_ctx->index = 0; if (op->match_flags & EcsTermReflexive) { trav_ctx->index = -1; - return flecs_rule_trav_unknown_src_reflexive( - op, ctx, trav, second); + if(flecs_query_trav_unknown_src_reflexive( + op, ctx, trav, second)) + { + /* It's possible that we couldn't return the entity required for + * reflexive matching, like when it's a prefab or disabled. */ + return true; + } } } else { if (!trav_ctx->cache.id) { /* No traversal cache, which means this is a regular select */ - return flecs_rule_select(op, redo, ctx); + return flecs_query_select(op, redo, ctx); } } @@ -64190,8 +72075,8 @@ bool flecs_rule_trav_unknown_src_up_fixed_second( for (; trav_ctx->index < count; trav_ctx->index ++) { ecs_trav_elem_t *el = &elems[trav_ctx->index]; trav_ctx->and.idr = el->idr; /* prevents lookup by select */ - if (flecs_rule_select_w_id(op, redo, ctx, ecs_pair(trav, el->entity), - (EcsTableIsPrefab|EcsTableIsDisabled))) + if (flecs_query_select_w_id(op, redo, ctx, ecs_pair(trav, el->entity), + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) { return true; } @@ -64203,16 +72088,16 @@ bool flecs_rule_trav_unknown_src_up_fixed_second( } static -bool flecs_rule_trav_yield_reflexive_src( - const ecs_rule_op_t *op, - const ecs_rule_run_ctx_t *ctx, +bool flecs_query_trav_yield_reflexive_src( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx, ecs_table_range_t *range, ecs_entity_t trav) { ecs_var_t *vars = ctx->vars; - ecs_rule_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); + ecs_query_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); int32_t offset = trav_ctx->offset, count = trav_ctx->count; - bool src_is_var = op->flags & (EcsRuleIsVar << EcsRuleSrc); + bool src_is_var = op->flags & (EcsQueryIsVar << EcsQuerySrc); if (trav_ctx->index >= (offset + count)) { /* Restore previous offset, count */ @@ -64225,9 +72110,8 @@ bool flecs_rule_trav_yield_reflexive_src( return false; } - ecs_entity_t entity = ecs_vec_get_t( - &range->table->data.entities, ecs_entity_t, trav_ctx->index)[0]; - flecs_rule_set_trav_match(op, -1, trav, entity, ctx); + ecs_entity_t entity = ecs_table_entities(range->table)[trav_ctx->index]; + flecs_query_set_trav_match(op, NULL, trav, entity, ctx); /* Hijack existing variable to return one result at a time */ if (src_is_var) { @@ -64244,20 +72128,20 @@ bool flecs_rule_trav_yield_reflexive_src( } static -bool flecs_rule_trav_fixed_src_up_unknown_second( - const ecs_rule_op_t *op, +bool flecs_query_trav_fixed_src_up_unknown_second( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { - ecs_flags16_t f_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t f_src = flecs_rule_ref_flags(op->flags, EcsRuleSrc); + ecs_flags16_t f_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t f_src = flecs_query_ref_flags(op->flags, EcsQuerySrc); ecs_entity_t trav = flecs_get_ref_entity(&op->first, f_1st, ctx); ecs_table_range_t range = flecs_get_ref_range(&op->src, f_src, ctx); ecs_table_t *table = range.table; - ecs_rule_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); + ecs_query_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); if (!redo) { - flecs_rule_get_trav_up_cache(ctx, &trav_ctx->cache, trav, table); + flecs_query_get_trav_up_cache(ctx, &trav_ctx->cache, trav, table); trav_ctx->index = 0; if (op->match_flags & EcsTermReflexive) { trav_ctx->yield_reflexive = true; @@ -64270,7 +72154,7 @@ bool flecs_rule_trav_fixed_src_up_unknown_second( } if (trav_ctx->yield_reflexive) { - if (flecs_rule_trav_yield_reflexive_src(op, ctx, &range, trav)) { + if (flecs_query_trav_yield_reflexive_src(op, ctx, &range, trav)) { return true; } trav_ctx->yield_reflexive = false; @@ -64283,1488 +72167,1203 @@ bool flecs_rule_trav_fixed_src_up_unknown_second( ecs_trav_elem_t *el = ecs_vec_get_t( &trav_ctx->cache.entities, ecs_trav_elem_t, trav_ctx->index); - flecs_rule_set_trav_match(op, el->column, trav, el->entity, ctx); + flecs_query_set_trav_match(op, el->tr, trav, el->entity, ctx); return true; } -static -bool flecs_rule_trav( - const ecs_rule_op_t *op, +bool flecs_query_trav( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { uint64_t written = ctx->written[ctx->op_index]; - if (!flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - if (!flecs_ref_is_written(op, &op->second, EcsRuleSecond, written)) { + if (!flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + if (!flecs_ref_is_written(op, &op->second, EcsQuerySecond, written)) { /* This can't happen, src or second should have been resolved */ ecs_abort(ECS_INTERNAL_ERROR, "invalid instruction sequence: unconstrained traversal"); } else { - return flecs_rule_trav_unknown_src_up_fixed_second(op, redo, ctx); + return flecs_query_trav_unknown_src_up_fixed_second(op, redo, ctx); } } else { - if (!flecs_ref_is_written(op, &op->second, EcsRuleSecond, written)) { - return flecs_rule_trav_fixed_src_up_unknown_second(op, redo, ctx); + if (!flecs_ref_is_written(op, &op->second, EcsQuerySecond, written)) { + return flecs_query_trav_fixed_src_up_unknown_second(op, redo, ctx); } else { - return flecs_rule_trav_fixed_src_up_fixed_second(op, redo, ctx); + return flecs_query_trav_fixed_src_up_fixed_second(op, redo, ctx); } } } +/** + * @file query/engine/eval_union.c + * @brief Union relationship evaluation. + */ + + static -bool flecs_rule_idsright( - const ecs_rule_op_t *op, +bool flecs_query_union_with_wildcard( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx, + ecs_entity_t rel, + bool neq) { - ecs_rule_ids_ctx_t *op_ctx = flecs_op_ctx(ctx, ids); - ecs_id_record_t *cur; + ecs_query_union_ctx_t *op_ctx = flecs_op_ctx(ctx, union_); + ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; + ecs_table_range_t range; + ecs_table_t *table; if (!redo) { - ecs_id_t id = flecs_rule_op_get_id(op, ctx); - if (!ecs_id_is_wildcard(id)) { - /* If id is not a wildcard, we can directly return it. This can - * happen if a variable was constrained by an iterator. */ - op_ctx->cur = NULL; - flecs_rule_set_vars(op, id, ctx); - return true; + range = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + table = range.table; + if (!range.count) { + range.count = ecs_table_count(table); } - cur = op_ctx->cur = flecs_id_record_get(ctx->world, id); - if (!cur) { - return false; + op_ctx->range = range; + op_ctx->idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + if (!op_ctx->idr) { + return neq; + } + + if (neq) { + if (flecs_id_record_get_table(op_ctx->idr, table) != NULL) { + /* If table has (R, Union) none match !(R, _) */ + return false; + } else { + /* If table doesn't have (R, Union) all match !(R, _) */ + return true; + } } - cur = op_ctx->cur = cur->first.next; + op_ctx->row = 0; } else { - if (!op_ctx->cur) { + if (neq) { + /* !(R, _) terms only can have a single result */ return false; } - cur = op_ctx->cur = op_ctx->cur->first.next; + range = op_ctx->range; + table = range.table; + op_ctx->row ++; } - if (!cur) { +next_row: + if (op_ctx->row >= range.count) { + /* Restore range */ + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, + op_ctx->range.offset, op_ctx->range.count, ctx); + } return false; } - flecs_rule_set_vars(op, cur->id, ctx); + ecs_entity_t e = ecs_table_entities(range.table) + [range.offset + op_ctx->row]; + ecs_entity_t tgt = flecs_switch_get(op_ctx->idr->sparse, (uint32_t)e); + if (!tgt) { + op_ctx->row ++; + goto next_row; + } + + it->ids[field_index] = ecs_pair(rel, tgt); - if (op->field_index != -1) { - ecs_iter_t *it = ctx->it; - ecs_id_t id = flecs_rule_op_get_id_w_written(op, op->written, ctx); - it->ids[op->field_index] = id; + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, + range.offset + op_ctx->row, 1, ctx); } + flecs_query_set_vars(op, it->ids[field_index], ctx); return true; } static -bool flecs_rule_idsleft( - const ecs_rule_op_t *op, +bool flecs_query_union_with_tgt( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx, + ecs_entity_t rel, + ecs_entity_t tgt, + bool neq) { - ecs_rule_ids_ctx_t *op_ctx = flecs_op_ctx(ctx, ids); - ecs_id_record_t *cur; + ecs_query_union_ctx_t *op_ctx = flecs_op_ctx(ctx, union_); + ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; + ecs_table_range_t range; + ecs_table_t *table; if (!redo) { - ecs_id_t id = flecs_rule_op_get_id(op, ctx); - if (!ecs_id_is_wildcard(id)) { - /* If id is not a wildcard, we can directly return it. This can - * happen if a variable was constrained by an iterator. */ - op_ctx->cur = NULL; - flecs_rule_set_vars(op, id, ctx); - return true; + range = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + table = range.table; + if (!range.count) { + range.count = ecs_table_count(table); } - cur = op_ctx->cur = flecs_id_record_get(ctx->world, id); - if (!cur) { + op_ctx->range = range; + op_ctx->idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + if (!op_ctx->idr) { return false; } - cur = op_ctx->cur = cur->second.next; + op_ctx->row = 0; } else { - if (!op_ctx->cur) { - return false; - } - - cur = op_ctx->cur = op_ctx->cur->second.next; + range = op_ctx->range; + table = range.table; + op_ctx->row ++; } - if (!cur) { +next_row: + if (op_ctx->row >= range.count) { + /* Restore range */ + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, + op_ctx->range.offset, op_ctx->range.count, ctx); + } return false; } - flecs_rule_set_vars(op, cur->id, ctx); - - if (op->field_index != -1) { - ecs_iter_t *it = ctx->it; - ecs_id_t id = flecs_rule_op_get_id_w_written(op, op->written, ctx); - it->ids[op->field_index] = id; - } - - return true; -} - -static -bool flecs_rule_each( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_each_ctx_t *op_ctx = flecs_op_ctx(ctx, each); - int32_t row; - - ecs_table_range_t range = flecs_rule_var_get_range(op->first.var, ctx); - ecs_table_t *table = range.table; - if (!table) { - return false; + ecs_entity_t e = ecs_table_entities(range.table) + [range.offset + op_ctx->row]; + ecs_entity_t e_tgt = flecs_switch_get(op_ctx->idr->sparse, (uint32_t)e); + bool match = e_tgt == tgt; + if (neq) { + match = !match; } - if (!redo) { - row = op_ctx->row = range.offset; - } else { - int32_t end = range.count; - if (end) { - end += range.offset; - } else { - end = table->data.entities.count; - } - row = ++ op_ctx->row; - if (op_ctx->row >= end) { - return false; - } + if (!match) { + op_ctx->row ++; + goto next_row; } - ecs_assert(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); - - ecs_entity_t *entities = table->data.entities.array; - ecs_entity_t e; - do { - e = entities[row ++]; + it->ids[field_index] = ecs_pair(rel, tgt); - /* Exclude entities that are used as markers by rule engine */ - } while ((e == EcsWildcard) || (e == EcsAny) || - (e == EcsThis) || (e == EcsVariable)); + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, + range.offset + op_ctx->row, 1, ctx); + } - flecs_rule_var_set_entity(op, op->src.var, e, ctx); + flecs_query_set_vars(op, it->ids[field_index], ctx); return true; } -static -bool flecs_rule_store( - const ecs_rule_op_t *op, +bool flecs_query_union_with( + const ecs_query_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx, + bool neq) { - if (!redo) { - flecs_rule_var_set_entity(op, op->src.var, op->first.entity, ctx); - return true; - } else { - return false; - } -} + ecs_id_t id = flecs_query_op_get_id(op, ctx); + ecs_entity_t rel = ECS_PAIR_FIRST(id); + ecs_entity_t tgt = ecs_pair_second(ctx->world, id); -static -bool flecs_rule_reset( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - if (!redo) { - return true; + if (tgt == EcsWildcard) { + return flecs_query_union_with_wildcard(op, redo, ctx, rel, neq); } else { - flecs_rule_var_reset(op->src.var, ctx); - return false; + return flecs_query_union_with_tgt(op, redo, ctx, rel, tgt, neq); } } static -void flecs_rule_reset_after_block( - const ecs_rule_op_t *start_op, - ecs_rule_run_ctx_t *ctx, - ecs_rule_ctrl_ctx_t *op_ctx) +bool flecs_query_union_select_tgt( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_entity_t rel, + ecs_entity_t tgt) { - ecs_rule_lbl_t op_index = start_op->next; - const ecs_rule_op_t *op = &ctx->rit->ops[op_index]; - ctx->written[op_index] = ctx->written[ctx->op_index]; - ctx->op_index = op_index; - - int32_t field = op->field_index; - if (field == -1) { - goto done; - } - + ecs_query_union_ctx_t *op_ctx = flecs_op_ctx(ctx, union_); ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; - /* Not terms return no data */ - it->columns[field] = 0; - - /* Ignore variables written by Not operation */ - uint64_t *written = ctx->written; - uint64_t written_cur = written[op->prev + 1]; - ecs_flags16_t flags_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t flags_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); - - /* Overwrite id with cleared out variables */ - ecs_id_t id = flecs_rule_op_get_id(op, ctx); - if (id) { - it->ids[field] = id; - } - - /* Reset variables */ - if (flags_1st & EcsRuleIsVar) { - if (!flecs_ref_is_written(op, &op->first, EcsRuleFirst, written_cur)){ - flecs_rule_var_reset(op->first.var, ctx); - } - } - if (flags_2nd & EcsRuleIsVar) { - if (!flecs_ref_is_written(op, &op->second, EcsRuleSecond, written_cur)){ - flecs_rule_var_reset(op->second.var, ctx); - } - } - - /* If term has entity src, set it because no other instruction might */ - if (op->flags & (EcsRuleIsEntity << EcsRuleSrc)) { - it->sources[field] = op->src.entity; - } - -done: - op_ctx->op_index = op_index; -} - -static -const char* flecs_rule_name_arg( - const ecs_rule_op_t *op, - ecs_rule_run_ctx_t *ctx) -{ - int8_t term_index = op->term_index; - ecs_term_t *term = &ctx->rule->filter.terms[term_index]; - return term->second.name; -} - -static -bool flecs_rule_compare_range( - const ecs_table_range_t *l, - const ecs_table_range_t *r) -{ - if (l->table != r->table) { - return false; - } - - if (l->count) { - int32_t l_end = l->offset + l->count; - int32_t r_end = r->offset + r->count; - if (r->offset < l->offset) { - return false; - } - if (r_end > l_end) { + if (!redo) { + op_ctx->idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + if (!op_ctx->idr) { return false; } + + op_ctx->cur = flecs_switch_first(op_ctx->idr->sparse, tgt); } else { - /* Entire table is matched */ + op_ctx->cur = flecs_switch_next(op_ctx->idr->sparse, (uint32_t)op_ctx->cur); } - return true; -} - -static -bool flecs_rule_pred_eq_w_range( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx, - ecs_table_range_t r) -{ - if (redo) { + if (!op_ctx->cur) { return false; } - uint64_t written = ctx->written[ctx->op_index]; - ecs_var_id_t first_var = op->first.var; - if (!(written & (1ull << first_var))) { - /* left = unknown, right = known. Assign right-hand value to left */ - ecs_var_id_t l = first_var; - ctx->vars[l].range = r; - if (r.count == 1) { - ctx->vars[l].entity = ecs_vec_get_t(&r.table->data.entities, - ecs_entity_t, r.offset)[0]; - } - return true; - } else { - ecs_table_range_t l = flecs_rule_get_range( - op, &op->first, EcsRuleFirst, ctx); - - if (!flecs_rule_compare_range(&l, &r)) { - return false; - } - - ctx->vars[first_var].range.offset = r.offset; - ctx->vars[first_var].range.count = r.count; - return true; - } -} - -static -bool flecs_rule_pred_eq( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; (void)written; - ecs_assert(flecs_ref_is_written(op, &op->second, EcsRuleSecond, written), - ECS_INTERNAL_ERROR, - "invalid instruction sequence: uninitialized eq operand"); - - ecs_table_range_t r = flecs_rule_get_range( - op, &op->second, EcsRuleSecond, ctx); - return flecs_rule_pred_eq_w_range(op, redo, ctx, r); -} + ecs_table_range_t range = flecs_range_from_entity(op_ctx->cur, ctx); + flecs_query_var_set_range(op, op->src.var, + range.table, range.offset, range.count, ctx); + flecs_query_set_vars(op, it->ids[field_index], ctx); -static -bool flecs_rule_pred_eq_name( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - const char *name = flecs_rule_name_arg(op, ctx); - ecs_entity_t e = ecs_lookup_fullpath(ctx->world, name); - if (!e) { - /* Entity doesn't exist */ - return false; - } + it->ids[field_index] = ecs_pair(rel, tgt); - ecs_table_range_t r = flecs_range_from_entity(e, ctx); - return flecs_rule_pred_eq_w_range(op, redo, ctx, r); + return true; } static -bool flecs_rule_pred_neq_w_range( - const ecs_rule_op_t *op, +bool flecs_query_union_select_wildcard( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx, - ecs_table_range_t r) + const ecs_query_run_ctx_t *ctx, + ecs_entity_t rel) { - ecs_rule_eq_ctx_t *op_ctx = flecs_op_ctx(ctx, eq); - ecs_var_id_t first_var = op->first.var; - ecs_table_range_t l = flecs_rule_get_range( - op, &op->first, EcsRuleFirst, ctx); - - /* If tables don't match, neq always returns once */ - if (l.table != r.table) { - return true && !redo; - } + ecs_query_union_ctx_t *op_ctx = flecs_op_ctx(ctx, union_); + ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; - int32_t l_offset; - int32_t l_count; if (!redo) { - /* Make sure we're working with the correct table count */ - if (!l.count && l.table) { - l.count = ecs_table_count(l.table); - } - - l_offset = l.offset; - l_count = l.count; - - /* Cache old value */ - op_ctx->range = l; - } else { - l_offset = op_ctx->range.offset; - l_count = op_ctx->range.count; - } - - /* If the table matches, a Neq returns twice: once for the slice before the - * excluded slice, once for the slice after the excluded slice. If the right - * hand range starts & overlaps with the left hand range, there is only - * one slice. */ - ecs_var_t *var = &ctx->vars[first_var]; - if (!redo && r.offset > l_offset) { - int32_t end = r.offset; - if (end > l_count) { - end = l_count; - } - - /* Return first slice */ - var->range.table = l.table; - var->range.offset = l_offset; - var->range.count = end - l_offset; - op_ctx->redo = false; - return true; - } else if (!op_ctx->redo) { - int32_t l_end = op_ctx->range.offset + l_count; - int32_t r_end = r.offset + r.count; - - if (l_end <= r_end) { - /* If end of existing range falls inside the excluded range, there's - * nothing more to return */ - var->range = l; + op_ctx->idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + if (!op_ctx->idr) { return false; } - /* Return second slice */ - var->range.table = l.table; - var->range.offset = r_end; - var->range.count = l_end - r_end; - - /* Flag so we know we're done the next redo */ - op_ctx->redo = true; - return true; - } else { - /* Restore previous value */ - var->range = l; - return false; + op_ctx->tgt_iter = flecs_switch_targets(op_ctx->idr->sparse); + op_ctx->tgt = 0; } -} - -static -bool flecs_rule_pred_match( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx, - bool is_neq) -{ - ecs_rule_eq_ctx_t *op_ctx = flecs_op_ctx(ctx, eq); - uint64_t written = ctx->written[ctx->op_index]; - ecs_assert(flecs_ref_is_written(op, &op->first, EcsRuleFirst, written), - ECS_INTERNAL_ERROR, - "invalid instruction sequence: uninitialized match operand"); - (void)written; - ecs_var_id_t first_var = op->first.var; - const char *match = flecs_rule_name_arg(op, ctx); - ecs_table_range_t l; - if (!redo) { - l = flecs_rule_get_range(op, &op->first, EcsRuleFirst, ctx); - if (!l.table) { +next_tgt: + if (!op_ctx->tgt) { + if (!ecs_map_next(&op_ctx->tgt_iter)) { return false; } - if (!l.count) { - l.count = ecs_table_count(l.table); - } + op_ctx->tgt = ecs_map_key(&op_ctx->tgt_iter); + op_ctx->cur = 0; + it->ids[field_index] = ecs_pair(rel, op_ctx->tgt); + } - op_ctx->range = l; - op_ctx->index = l.offset; - op_ctx->name_col = flecs_ito(int16_t, - ecs_table_get_type_index(ctx->world, l.table, - ecs_pair(ecs_id(EcsIdentifier), EcsName))); - if (op_ctx->name_col == -1) { - return is_neq; - } - op_ctx->name_col = flecs_ito(int16_t, - l.table->column_map[op_ctx->name_col]); - ecs_assert(op_ctx->name_col != -1, ECS_INTERNAL_ERROR, NULL); + if (!op_ctx->cur) { + op_ctx->cur = flecs_switch_first(op_ctx->idr->sparse, op_ctx->tgt); } else { - if (op_ctx->name_col == -1) { - /* Table has no name */ - return false; - } - - l = op_ctx->range; + op_ctx->cur = flecs_switch_next(op_ctx->idr->sparse, (uint32_t)op_ctx->cur); } - const EcsIdentifier *names = l.table->data.columns[op_ctx->name_col].data.array; - int32_t count = l.offset + l.count, offset = -1; - for (; op_ctx->index < count; op_ctx->index ++) { - const char *name = names[op_ctx->index].value; - bool result = strstr(name, match); - if (is_neq) { - result = !result; - } - - if (!result) { - if (offset != -1) { - break; - } - } else { - if (offset == -1) { - offset = op_ctx->index; - } - } + if (!op_ctx->cur) { + op_ctx->tgt = 0; + goto next_tgt; } - if (offset == -1) { - ctx->vars[first_var].range = op_ctx->range; - return false; - } + ecs_table_range_t range = flecs_range_from_entity(op_ctx->cur, ctx); + flecs_query_var_set_range(op, op->src.var, + range.table, range.offset, range.count, ctx); + flecs_query_set_vars(op, it->ids[field_index], ctx); - ctx->vars[first_var].range.offset = offset; - ctx->vars[first_var].range.count = (op_ctx->index - offset); return true; } -static -bool flecs_rule_pred_eq_match( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - return flecs_rule_pred_match(op, redo, ctx, false); -} - -static -bool flecs_rule_pred_neq_match( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - return flecs_rule_pred_match(op, redo, ctx, true); -} - -static -bool flecs_rule_pred_neq( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; (void)written; - ecs_assert(flecs_ref_is_written(op, &op->second, EcsRuleSecond, written), - ECS_INTERNAL_ERROR, - "invalid instruction sequence: uninitialized neq operand"); - - ecs_table_range_t r = flecs_rule_get_range( - op, &op->second, EcsRuleSecond, ctx); - return flecs_rule_pred_neq_w_range(op, redo, ctx, r); -} - -static -bool flecs_rule_pred_neq_name( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - const char *name = flecs_rule_name_arg(op, ctx); - ecs_entity_t e = ecs_lookup_fullpath(ctx->world, name); - if (!e) { - /* Entity doesn't exist */ - return true && !redo; - } - - ecs_table_range_t r = flecs_range_from_entity(e, ctx); - return flecs_rule_pred_neq_w_range(op, redo, ctx, r); -} - -static -bool flecs_rule_lookup( - const ecs_rule_op_t *op, +bool flecs_query_union_select( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { - if (redo) { - return false; - } + ecs_id_t id = flecs_query_op_get_id(op, ctx); + ecs_entity_t rel = ECS_PAIR_FIRST(id); + ecs_entity_t tgt = ecs_pair_second(ctx->world, id); - const ecs_rule_t *rule = ctx->rule; - ecs_entity_t first = flecs_rule_var_get_entity(op->first.var, ctx); - ecs_rule_var_t *var = &rule->vars[op->src.var]; - - ecs_entity_t result = ecs_lookup_path_w_sep(ctx->world, first, var->lookup, - NULL, NULL, false); - if (!result) { - flecs_rule_var_set_entity(op, op->src.var, EcsWildcard, ctx); - return false; + if (tgt == EcsWildcard) { + return flecs_query_union_select_wildcard(op, redo, ctx, rel); + } else { + return flecs_query_union_select_tgt(op, redo, ctx, rel, tgt); } - - flecs_rule_var_set_entity(op, op->src.var, result, ctx); - - return true; } -static -bool flecs_rule_setvars( - const ecs_rule_op_t *op, +bool flecs_query_union( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { - (void)op; - - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; - ecs_var_id_t *src_vars = rule->src_vars; - ecs_iter_t *it = ctx->it; - - if (redo) { - return false; - } - - int32_t i; - ecs_flags32_t source_set = *ctx->source_set; - for (i = 0; i < filter->field_count; i ++) { - ecs_var_id_t var_id = src_vars[i]; - if (!var_id) { - continue; - } - - if (source_set & (1u << i)) { - continue; - } - - it->sources[i] = flecs_rule_var_get_entity(var_id, ctx); + uint64_t written = ctx->written[ctx->op_index]; + if (written & (1ull << op->src.var)) { + return flecs_query_union_with(op, redo, ctx, false); + } else { + return flecs_query_union_select(op, redo, ctx); } - - return true; } -static -bool flecs_rule_setthis( - const ecs_rule_op_t *op, +bool flecs_query_union_neq( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { - ecs_rule_setthis_ctx_t *op_ctx = flecs_op_ctx(ctx, setthis); - ecs_var_t *vars = ctx->vars; - ecs_var_t *this_var = &vars[op->first.var]; - - if (!redo) { - /* Save values so we can restore them later */ - op_ctx->range = vars[0].range; - - /* Constrain This table variable to a single entity from the table */ - vars[0].range = flecs_range_from_entity(this_var->entity, ctx); - vars[0].entity = this_var->entity; - return true; + uint64_t written = ctx->written[ctx->op_index]; + if (written & (1ull << op->src.var)) { + return flecs_query_union_with(op, redo, ctx, true); } else { - /* Restore previous values, so that instructions that are operating on - * the table variable use all the entities in the table. */ - vars[0].range = op_ctx->range; - vars[0].entity = 0; return false; } } static -bool flecs_rule_setfixed( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) +void flecs_query_union_set_shared( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx) { - (void)op; - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; - ecs_iter_t *it = ctx->it; - - if (redo) { - return false; - } - - int32_t i; - for (i = 0; i < filter->term_count; i ++) { - ecs_term_t *term = &filter->terms[i]; - ecs_term_id_t *src = &term->src; - if (src->flags & EcsIsEntity) { - it->sources[term->field_index] = src->id; - } - } + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_id_record_t *idr = op_ctx->idr_with; + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - return true; -} + ecs_entity_t rel = ECS_PAIR_FIRST(idr->id); + idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(idr->sparse != NULL, ECS_INTERNAL_ERROR, NULL); -static -bool flecs_rule_setids( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; + int8_t field_index = op->field_index; ecs_iter_t *it = ctx->it; - - if (redo) { - return false; - } - - int32_t i; - for (i = 0; i < filter->term_count; i ++) { - ecs_term_t *term = &filter->terms[i]; - it->ids[term->field_index] = term->id; - } - - return true; -} - -static -bool flecs_rule_setid( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (redo) { - return false; - } - - ecs_assert(op->field_index != -1, ECS_INTERNAL_ERROR, NULL); - ctx->it->ids[op->field_index] = op->first.entity; - return true; + ecs_entity_t src = it->sources[field_index]; + ecs_entity_t tgt = flecs_switch_get(idr->sparse, (uint32_t)src); + + it->ids[field_index] = ecs_pair(rel, tgt); } -/* Check if entity is stored in table */ -static -bool flecs_rule_contain( - const ecs_rule_op_t *op, +bool flecs_query_union_up( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { - if (redo) { - return false; - } - - ecs_var_id_t src_id = op->src.var; - ecs_var_id_t first_id = op->first.var; - - ecs_table_t *table = flecs_rule_var_get_table(src_id, ctx); - - ecs_entity_t e = flecs_rule_var_get_entity(first_id, ctx); - return table == ecs_get_table(ctx->world, e); -} + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + if (!redo) { + if (!flecs_query_up_with(op, redo, ctx)) { + return false; + } -/* Check if first and second id of pair from last operation are the same */ -static -bool flecs_rule_pair_eq( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (redo) { - return false; + flecs_query_union_set_shared(op, ctx); + return true; + } else { + return false; + } + } else { + return flecs_query_up_select(op, redo, ctx, + FlecsQueryUpSelectUp, FlecsQueryUpSelectUnion); } - - ecs_iter_t *it = ctx->it; - ecs_id_t id = it->ids[op->field_index]; - return ECS_PAIR_FIRST(id) == ECS_PAIR_SECOND(id); } -static -bool flecs_rule_select_or( - const ecs_rule_op_t *op, +bool flecs_query_union_self_up( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { - ecs_iter_t *it = ctx->it; - ecs_rule_iter_t *rit = &it->priv.iter.rule; - ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); - - ecs_rule_lbl_t first = flecs_itolbl(ctx->op_index + 1); - if (!redo) { - op_ctx->op_index = first; - } - - const ecs_rule_op_t *cur = &rit->ops[op_ctx->op_index]; - bool result = false; - - /* Evaluate operations in OR chain one by one. */ - - do { - ctx->op_index = op_ctx->op_index; - ctx->written[op_ctx->op_index] = op->written; - - result = flecs_rule_dispatch(cur, redo, ctx); - if (result) { - ctx->written[op_ctx->op_index] |= cur->written; + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + if (redo) { + goto next_for_union; + } - /* If a previous operation in the OR chain returned a result for the - * same matched source, skip it so we don't yield for each matching - * element in the chain. */ - ecs_rule_lbl_t prev_index; - for (prev_index = first; prev_index < op_ctx->op_index; - prev_index ++) - { - const ecs_rule_op_t *prev = &rit->ops[prev_index]; - ctx->op_index = prev_index; - ctx->written[prev_index] = ctx->written[op_ctx->op_index]; - if (flecs_rule_dispatch(prev, false, ctx)) { - break; - } - } +next_for_self_up_with: + if (!flecs_query_self_up_with(op, redo, ctx, false)) { + return false; + } - if (prev_index != op_ctx->op_index) { - /* Duplicate match was found, redo search */ - redo = true; - continue; - } - break; + int8_t field_index = op->field_index; + ecs_iter_t *it = ctx->it; + if (it->sources[field_index]) { + flecs_query_union_set_shared(op, ctx); + return true; } - /* No result was found, go to next operation in chain */ - op_ctx->op_index ++; - cur = &rit->ops[op_ctx->op_index]; - redo = false; - } while (cur->kind != EcsRuleEnd); +next_for_union: + if (!flecs_query_union_with(op, redo, ctx, false)) { + goto next_for_self_up_with; + } - return result; + return true; + } else { + return flecs_query_up_select(op, redo, ctx, + FlecsQueryUpSelectSelfUp, FlecsQueryUpSelectUnion); + } } +/** + * @file query/engine/eval.c + * @brief Query engine implementation. + */ + + +/* Find tables with requested component that has traversable entities. */ static -bool flecs_rule_run_block( +bool flecs_query_up_select_table( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx, - ecs_rule_ctrl_ctx_t *op_ctx) + const ecs_query_run_ctx_t *ctx, + ecs_query_up_select_trav_kind_t trav_kind, + ecs_query_up_select_kind_t kind) { + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); ecs_iter_t *it = ctx->it; - ecs_rule_iter_t *rit = &it->priv.iter.rule; + bool self = trav_kind == FlecsQueryUpSelectSelfUp; + ecs_table_range_t range; - if (!redo) { - op_ctx->op_index = flecs_itolbl(ctx->op_index + 1); - } else if (ctx->rit->ops[op_ctx->op_index].kind == EcsRuleEnd) { - return false; - } + do { + bool result; + if (kind == FlecsQueryUpSelectId) { + result = flecs_query_select_id(op, redo, ctx, 0); + } else if (kind == FlecsQueryUpSelectDefault) { + result = flecs_query_select_w_id(op, redo, ctx, + op_ctx->with, 0); + } else if (kind == FlecsQueryUpSelectUnion) { + result = flecs_query_union_select(op, redo, ctx); + } else { + ecs_abort(ECS_INTERNAL_ERROR, NULL); + } - ctx->written[ctx->op_index + 1] = ctx->written[ctx->op_index]; + if (!result) { + /* No remaining tables with component found. */ + return false; + } - return flecs_rule_run_until( - redo, ctx, rit->ops, ctx->op_index, op_ctx->op_index, EcsRuleEnd); -} + redo = true; -static -bool flecs_rule_with_or( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + range = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + ecs_assert(range.table != NULL, ECS_INTERNAL_ERROR, NULL); - bool result = flecs_rule_run_block(redo, ctx, op_ctx); - if (result) { - /* If a match was found, no need to keep searching for this source */ - op_ctx->op_index = op->next; + /* Keep searching until we find a table that has the requested component, + * with traversable entities */ + } while (!self && range.table->_->traversable_count == 0); + + if (!range.count) { + range.count = ecs_table_count(range.table); } - return result; + op_ctx->table = range.table; + op_ctx->row = range.offset; + op_ctx->end = range.offset + range.count; + op_ctx->matched = it->ids[op->field_index]; + + return true; } +/* Find next traversable entity in table. */ static -bool flecs_rule_or( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) +ecs_trav_down_t* flecs_query_up_find_next_traversable( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx, + ecs_query_up_select_trav_kind_t trav_kind) { - if (op->flags & (EcsRuleIsVar << EcsRuleSrc)) { - uint64_t written = ctx->written[ctx->op_index]; - if (!(written & (1ull << op->src.var))) { - return flecs_rule_select_or(op, redo, ctx); + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_world_t *world = ctx->world; + ecs_iter_t *it = ctx->it; + const ecs_query_t *q = &ctx->query->pub; + ecs_table_t *table = op_ctx->table; + bool self = trav_kind == FlecsQueryUpSelectSelfUp; + + if (table->_->traversable_count == 0) { + /* No traversable entities in table */ + op_ctx->table = NULL; + return NULL; + } else { + int32_t row; + ecs_entity_t entity = 0; + const ecs_entity_t *entities = ecs_table_entities(table); + + for (row = op_ctx->row; row < op_ctx->end; row ++) { + entity = entities[row]; + ecs_record_t *record = flecs_entities_get(world, entity); + if (record->row & EcsEntityIsTraversable) { + /* Found traversable entity */ + it->sources[op->field_index] = entity; + break; + } } - } - return flecs_rule_with_or(op, redo, ctx); -} + if (row == op_ctx->end) { + /* No traversable entities remaining in table */ + op_ctx->table = NULL; + return NULL; + } -static -bool flecs_rule_run_block_w_reset( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); - bool result = flecs_rule_run_block(redo, ctx, op_ctx); - if (!result) { - flecs_rule_reset_after_block(op, ctx, op_ctx); + op_ctx->row = row; + + /* Get down cache entry for traversable entity */ + bool match_empty = (q->flags & EcsQueryMatchEmptyTables) != 0; + op_ctx->down = flecs_query_get_down_cache(ctx, &op_ctx->cache, + op_ctx->trav, entity, op_ctx->idr_with, self, match_empty); + op_ctx->cache_elem = -1; } - return result; + + return op_ctx->down; } -static -bool flecs_rule_not( - const ecs_rule_op_t *op, +/* Select all tables that can reach the target component through the traversal + * relationship. */ +bool flecs_query_up_select( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx, + ecs_query_up_select_trav_kind_t trav_kind, + ecs_query_up_select_kind_t kind) { - if (redo) { - return false; - } + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_iter_t *it = ctx->it; + bool redo_select = redo; + const ecs_query_t *q = &ctx->query->pub; + bool self = trav_kind == FlecsQueryUpSelectSelfUp; - return !flecs_rule_run_block_w_reset(op, redo, ctx); -} + op_ctx->trav = q->terms[op->term_index].trav; -static -bool flecs_rule_optional( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - bool result = flecs_rule_run_block_w_reset(op, redo, ctx); - if (!redo) { - return true; /* Return at least once */ - } else { - return result; + /* Reuse id record from previous iteration if possible*/ + if (!op_ctx->idr_trav) { + op_ctx->idr_trav = flecs_id_record_get(ctx->world, + ecs_pair(op_ctx->trav, EcsWildcard)); } -} -static -bool flecs_rule_eval_if( - const ecs_rule_op_t *op, - ecs_rule_run_ctx_t *ctx, - const ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind) -{ - if (flecs_rule_ref_flags(op->flags, ref_kind) == EcsRuleIsVar) { - if (ctx->vars[ref->var].entity == EcsWildcard) { - ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); - flecs_rule_reset_after_block(op, ctx, op_ctx); + /* If id record is not found, or if it doesn't have any tables, revert to + * iterating owned components (no traversal) */ + if (!op_ctx->idr_trav || + !flecs_table_cache_all_count(&op_ctx->idr_trav->cache)) + { + if (!self) { + /* If operation does not match owned components, return false */ return false; + } else if (kind == FlecsQueryUpSelectId) { + return flecs_query_select_id(op, redo, ctx, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)); + } else if (kind == FlecsQueryUpSelectDefault) { + return flecs_query_select(op, redo, ctx); + } else if (kind == FlecsQueryUpSelectUnion) { + return flecs_query_union_select(op, redo, ctx); + } else { + /* Invalid select kind */ + ecs_abort(ECS_INTERNAL_ERROR, NULL); } } - return true; -} -static -bool flecs_rule_if( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ if (!redo) { - if (!flecs_rule_eval_if(op, ctx, &op->src, EcsRuleSrc) || - !flecs_rule_eval_if(op, ctx, &op->first, EcsRuleFirst) || - !flecs_rule_eval_if(op, ctx, &op->second, EcsRuleSecond)) - { - return true; - } - } + /* Get component id to match */ + op_ctx->with = flecs_query_op_get_id(op, ctx); - return flecs_rule_run_block_w_reset(op, redo, ctx); -} + /* Get id record for component to match */ + op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); + if (!op_ctx->idr_with) { + /* If id record does not exist, there can't be any results */ + return false; + } -static -bool flecs_rule_end( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; (void)ctx; - return !redo; -} + op_ctx->down = NULL; + op_ctx->cache_elem = 0; + } -static -bool flecs_rule_populate( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; - if (!redo) { - ecs_iter_t *it = ctx->it; - if (it->flags & EcsIterNoData) { - return true; - } + /* Get last used entry from down traversal cache. Cache entries in the down + * traversal cache contain a list of tables that can reach the requested + * component through the traversal relationship, for a traversable entity + * which acts as the key for the cache. */ + ecs_trav_down_t *down = op_ctx->down; - ECS_BIT_CLEAR(it->flags, EcsIterHasShared); +next_down_entry: + /* Get (next) entry in down traversal cache */ + while (!down) { + ecs_table_t *table = op_ctx->table; - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; - int32_t i, field_count = filter->field_count; - ecs_flags64_t data_fields = filter->data_fields; - ecs_table_range_t *range = &ctx->vars[0].range; - ecs_table_t *table = range->table; - if (table && !range->count) { - range->count = ecs_table_count(table); - } + /* Get (next) table with traversable entities that have the + * requested component. We'll traverse downwards from the + * traversable entities in the table to find all entities that can + * reach the component through the traversal relationship. */ + if (!table) { + /* Reset source, in case we have to return a component matched + * by the entity in the found table. */ + it->sources[op->field_index] = 0; - for (i = 0; i < field_count; i ++) { - if (!(data_fields & (1llu << i))) { - continue; + if (!flecs_query_up_select_table( + op, redo_select, ctx, trav_kind, kind)) + { + return false; } - int32_t index = it->columns[i]; - ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); - if (!index) { - continue; - } - - ecs_entity_t src = it->sources[i]; - if (!src) { - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - if (range->count && table->column_map) { - int32_t column = table->column_map[index - 1]; - if (column != -1) { - it->ptrs[i] = ECS_ELEM( - table->data.columns[column].data.array, - it->sizes[i], - range->offset); - continue; - } - } - } else { - ecs_record_t *r = flecs_entities_get(ctx->world, src); - ecs_table_t *src_table = r->table; - if (src_table->column_map) { - int32_t column = src_table->column_map[index - 1]; - if (column != -1) { - it->ptrs[i] = ecs_vec_get( - &src_table->data.columns[column].data, - it->sizes[i], - ECS_RECORD_TO_ROW(r->row)); - ECS_BIT_SET(it->flags, EcsIterHasShared); - continue; - } + table = op_ctx->table; + + /* If 'self' is true, we're evaluating a term with self|up. This + * means that before traversing downwards, we should also return + * the current table as result. */ + if (self) { + if (!flecs_query_table_filter(table, op->other, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) + { + flecs_reset_source_set_flag(it, op->field_index); + op_ctx->row --; + return true; } } + + redo_select = true; + } else { + /* Evaluate next entity in table */ + op_ctx->row ++; } - return true; - } else { - return false; + /* Get down cache entry for next traversable entity in table */ + down = flecs_query_up_find_next_traversable(op, ctx, trav_kind); + if (!down) { + goto next_down_entry; + } + } + +next_down_elem: + /* Get next element (table) in cache entry */ + if ((++ op_ctx->cache_elem) >= ecs_vec_count(&down->elems)) { + /* No more elements in cache entry, find next.*/ + down = NULL; + goto next_down_entry; + } + + ecs_trav_down_elem_t *elem = ecs_vec_get_t( + &down->elems, ecs_trav_down_elem_t, op_ctx->cache_elem); + flecs_query_var_set_range(op, op->src.var, elem->table, 0, 0, ctx); + flecs_query_set_vars(op, op_ctx->matched, ctx); + + if (flecs_query_table_filter(elem->table, op->other, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) + { + /* Go to next table if table contains prefabs, disabled entities or + * entities that are not queryable. */ + goto next_down_elem; } + + flecs_set_source_set_flag(it, op->field_index); + + return true; } -static -bool flecs_rule_populate_self( - const ecs_rule_op_t *op, +/* Check if a table can reach the target component through the traversal + * relationship. */ +bool flecs_query_up_with( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx) + const ecs_query_run_ctx_t *ctx) { - (void)op; + const ecs_query_t *q = &ctx->query->pub; + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_iter_t *it = ctx->it; + + op_ctx->trav = q->terms[op->term_index].trav; + if (!op_ctx->idr_trav) { + op_ctx->idr_trav = flecs_id_record_get(ctx->world, + ecs_pair(op_ctx->trav, EcsWildcard)); + } + + if (!op_ctx->idr_trav || + !flecs_table_cache_all_count(&op_ctx->idr_trav->cache)) + { + /* If there are no tables with traversable relationship, there are no + * matches. */ + return false; + } + if (!redo) { - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; - int32_t i, field_count = filter->field_count; - ecs_flags64_t data_fields = filter->data_fields; - ecs_iter_t *it = ctx->it; + op_ctx->trav = q->terms[op->term_index].trav; + op_ctx->with = flecs_query_op_get_id(op, ctx); + op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); - ecs_table_range_t *range = &ctx->vars[0].range; - ecs_table_t *table = range->table; - if (!table->column_map) { - return true; + /* If id record for component doesn't exist, there are no matches */ + if (!op_ctx->idr_with) { + return false; } - if (!ecs_table_count(table)) { - return true; + /* Get the range (table) that is currently being evaluated. In most + * cases the range will cover the entire table, but in some cases it + * can only cover a subset of the entities in the table. */ + ecs_table_range_t range = flecs_query_get_range( + op, &op->src, EcsQuerySrc, ctx); + if (!range.table) { + return false; } - for (i = 0; i < field_count; i ++) { - if (!(data_fields & (1llu << i))) { - continue; - } - - int32_t index = it->columns[i]; - ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); /* Only owned */ - if (!index) { - continue; - } + /* Get entry from up traversal cache. The up traversal cache contains + * the entity on which the component was found, with additional metadata + * on where it is stored. */ + ecs_trav_up_t *up = flecs_query_get_up_cache(ctx, &op_ctx->cache, + range.table, op_ctx->with, op_ctx->trav, op_ctx->idr_with, + op_ctx->idr_trav); - int32_t column = table->column_map[index - 1]; - if (column != -1) { - it->ptrs[i] = ECS_ELEM( - table->data.columns[column].data.array, - it->sizes[i], - range->offset); - } + if (!up) { + /* Component is not reachable from table */ + return false; } + it->sources[op->field_index] = flecs_entities_get_alive( + ctx->world, up->src); + it->trs[op->field_index] = up->tr; + it->ids[op->field_index] = up->id; + flecs_query_set_vars(op, up->id, ctx); + flecs_set_source_set_flag(it, op->field_index); return true; } else { + /* The table either can or can't reach the component, nothing to do for + * a second evaluation of this operation.*/ return false; } } -static -bool flecs_rule_dispatch( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - switch(op->kind) { - case EcsRuleAnd: return flecs_rule_and(op, redo, ctx); - case EcsRuleAndId: return flecs_rule_and_id(op, redo, ctx); - case EcsRuleAndAny: return flecs_rule_and_any(op, redo, ctx); - case EcsRuleTriv: return flecs_rule_triv(op, redo, ctx); - case EcsRuleTrivData: return flecs_rule_triv_data(op, redo, ctx); - case EcsRuleTrivWildcard: return flecs_rule_triv_wildcard(op, redo, ctx); - case EcsRuleSelectAny: return flecs_rule_select_any(op, redo, ctx); - case EcsRuleUp: return flecs_rule_up(op, redo, ctx); - case EcsRuleUpId: return flecs_rule_up_id(op, redo, ctx); - case EcsRuleSelfUp: return flecs_rule_self_up(op, redo, ctx); - case EcsRuleSelfUpId: return flecs_rule_self_up_id(op, redo, ctx); - case EcsRuleWith: return flecs_rule_with(op, redo, ctx); - case EcsRuleTrav: return flecs_rule_trav(op, redo, ctx); - case EcsRuleIdsRight: return flecs_rule_idsright(op, redo, ctx); - case EcsRuleIdsLeft: return flecs_rule_idsleft(op, redo, ctx); - case EcsRuleEach: return flecs_rule_each(op, redo, ctx); - case EcsRuleStore: return flecs_rule_store(op, redo, ctx); - case EcsRuleReset: return flecs_rule_reset(op, redo, ctx); - case EcsRuleOr: return flecs_rule_or(op, redo, ctx); - case EcsRuleOptional: return flecs_rule_optional(op, redo, ctx); - case EcsRuleIf: return flecs_rule_if(op, redo, ctx); - case EcsRuleEnd: return flecs_rule_end(op, redo, ctx); - case EcsRuleNot: return flecs_rule_not(op, redo, ctx); - case EcsRulePredEq: return flecs_rule_pred_eq(op, redo, ctx); - case EcsRulePredNeq: return flecs_rule_pred_neq(op, redo, ctx); - case EcsRulePredEqName: return flecs_rule_pred_eq_name(op, redo, ctx); - case EcsRulePredNeqName: return flecs_rule_pred_neq_name(op, redo, ctx); - case EcsRulePredEqMatch: return flecs_rule_pred_eq_match(op, redo, ctx); - case EcsRulePredNeqMatch: return flecs_rule_pred_neq_match(op, redo, ctx); - case EcsRuleLookup: return flecs_rule_lookup(op, redo, ctx); - case EcsRuleSetVars: return flecs_rule_setvars(op, redo, ctx); - case EcsRuleSetThis: return flecs_rule_setthis(op, redo, ctx); - case EcsRuleSetFixed: return flecs_rule_setfixed(op, redo, ctx); - case EcsRuleSetIds: return flecs_rule_setids(op, redo, ctx); - case EcsRuleSetId: return flecs_rule_setid(op, redo, ctx); - case EcsRuleContain: return flecs_rule_contain(op, redo, ctx); - case EcsRulePairEq: return flecs_rule_pair_eq(op, redo, ctx); - case EcsRulePopulate: return flecs_rule_populate(op, redo, ctx); - case EcsRulePopulateSelf: return flecs_rule_populate_self(op, redo, ctx); - case EcsRuleYield: return false; - case EcsRuleNothing: return false; - } - return false; -} - -static -bool flecs_rule_run_until( +/* Check if a table can reach the target component through the traversal + * relationship, or if the table has the target component itself. */ +bool flecs_query_self_up_with( + const ecs_query_op_t *op, bool redo, - ecs_rule_run_ctx_t *ctx, - const ecs_rule_op_t *ops, - ecs_rule_lbl_t first, - ecs_rule_lbl_t cur, - ecs_rule_op_kind_t until) + const ecs_query_run_ctx_t *ctx, + bool id_only) { - ecs_assert(first >= -1, ECS_INTERNAL_ERROR, NULL); - ecs_assert(cur >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(cur > first, ECS_INTERNAL_ERROR, NULL); + if (!redo) { + bool result; - ctx->op_index = cur; - const ecs_rule_op_t *op = &ops[ctx->op_index]; - ecs_assert(op->kind != until, ECS_INTERNAL_ERROR, NULL); + if (id_only) { + /* Simple id, no wildcards */ + result = flecs_query_with_id(op, redo, ctx); + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + op_ctx->remaining = 1; + } else { + result = flecs_query_with(op, redo, ctx); + } - do { - #ifdef FLECS_DEBUG - ctx->rit->profile[ctx->op_index].count[redo] ++; - #endif + flecs_reset_source_set_flag(ctx->it, op->field_index); - bool result = flecs_rule_dispatch(op, redo, ctx); - cur = (&op->prev)[result]; - redo = cur < ctx->op_index; + if (result) { + /* Table has component, no need to traverse*/ + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + op_ctx->trav = 0; + if (flecs_query_ref_flags(op->flags, EcsQuerySrc) & EcsQueryIsVar) { + /* Matching self, so set sources to 0 */ + ecs_iter_t *it = ctx->it; + it->sources[op->field_index] = 0; + } + return true; + } - if (!redo) { - ctx->written[cur] |= ctx->written[ctx->op_index] | op->written; + /* Table doesn't have component, traverse relationship */ + return flecs_query_up_with(op, redo, ctx); + } else { + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + if (op_ctx->trav == 0) { + /* If matching components without traversing, make sure to still + * match remaining components that match the id (wildcard). */ + return flecs_query_with(op, redo, ctx); } + } - ctx->op_index = cur; - op = &ops[ctx->op_index]; + return false; +} - if (cur <= first) { - return false; +/** + * @file query/engine/eval_utils.c + * @brief Query engine evaluation utilities. + */ + + +void flecs_query_set_iter_this( + ecs_iter_t *it, + const ecs_query_run_ctx_t *ctx) +{ + const ecs_var_t *var = &ctx->vars[0]; + const ecs_table_range_t *range = &var->range; + ecs_table_t *table = range->table; + int32_t count = range->count; + if (table) { + if (!count) { + count = ecs_table_count(table); } - if (op->kind == until) { - return true; + it->table = table; + it->offset = range->offset; + it->count = count; + it->entities = ecs_table_entities(table); + if (it->entities) { + it->entities += it->offset; } - } while (true); + } else if (count == 1) { + it->count = 1; + it->entities = &ctx->vars[0].entity; + } +} - return false; +ecs_query_op_ctx_t* flecs_op_ctx_( + const ecs_query_run_ctx_t *ctx) +{ + return &ctx->op_ctx[ctx->op_index]; } -static -void flecs_rule_iter_init( - ecs_rule_run_ctx_t *ctx) +#define flecs_op_ctx(ctx, op_kind) (&flecs_op_ctx_(ctx)->is.op_kind) + +void flecs_reset_source_set_flag( + ecs_iter_t *it, + int32_t field_index) { - ecs_assert(ctx->written != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_iter_t *it = ctx->it; + ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); + ECS_TERMSET_CLEAR(it->up_fields, 1u << field_index); +} - const ecs_rule_t *rule = ctx->rule; - ecs_flags64_t it_written = it->constrained_vars; - ctx->written[0] = it_written; - if (it_written && ctx->rule->src_vars) { - /* If variables were constrained, check if there are any table - * variables that have a constrained entity variable. */ - ecs_var_t *vars = ctx->vars; - int32_t i, count = rule->filter.field_count; - for (i = 0; i < count; i ++) { - ecs_var_id_t var_id = rule->src_vars[i]; - ecs_rule_var_t *var = &rule->vars[var_id]; +void flecs_set_source_set_flag( + ecs_iter_t *it, + int32_t field_index) +{ + ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); + ECS_TERMSET_SET(it->up_fields, 1u << field_index); +} - if (!(it_written & (1ull << var_id)) || - (var->kind == EcsVarTable) || (var->table_id == EcsVarNone)) - { - continue; - } +ecs_table_range_t flecs_range_from_entity( + ecs_entity_t e, + const ecs_query_run_ctx_t *ctx) +{ + ecs_record_t *r = flecs_entities_get(ctx->world, e); + if (!r) { + return (ecs_table_range_t){ 0 }; + } + return (ecs_table_range_t){ + .table = r->table, + .offset = ECS_RECORD_TO_ROW(r->row), + .count = 1 + }; +} - /* Initialize table variable with constrained entity variable */ - ecs_var_t *tvar = &vars[var->table_id]; - tvar->range = flecs_range_from_entity(vars[var_id].entity, ctx); - ctx->written[0] |= (1ull << var->table_id); /* Mark as written */ - } +ecs_table_range_t flecs_query_var_get_range( + int32_t var_id, + const ecs_query_run_ctx_t *ctx) +{ + ecs_assert(var_id < ctx->query->var_count, ECS_INTERNAL_ERROR, NULL); + ecs_var_t *var = &ctx->vars[var_id]; + ecs_table_t *table = var->range.table; + if (table) { + return var->range; } - ecs_flags32_t flags = rule->filter.flags; - if (flags & EcsFilterIsTrivial) { - if ((flags & EcsFilterMatchOnlySelf) || - !flecs_table_cache_count(&ctx->world->idr_isa_wildcard->cache)) - { - if (it_written) { - it->offset = ctx->vars[0].range.offset; - it->count = ctx->vars[0].range.count; - if (!it->count) { - ecs_assert(!it->offset, ECS_INVALID_PARAMETER, NULL); - it->count = ecs_table_count(ctx->vars[0].range.table); - } + ecs_entity_t entity = var->entity; + if (entity && entity != EcsWildcard) { + var->range = flecs_range_from_entity(entity, ctx); + return var->range; + } - it->flags |= EcsIterTrivialTest; - flecs_rule_setids(&rule->ops[0], false, ctx); - } else { - if (flags & EcsFilterHasWildcards) { - it->flags |= EcsIterTrivialSearchWildcard; - flecs_rule_setids(&rule->ops[0], false, ctx); - } else if (flags & EcsFilterNoData) { - it->flags |= EcsIterTrivialSearchNoData; - flecs_rule_setids(&rule->ops[0], false, ctx); - } else { - it->flags |= EcsIterTrivialSearch; - flecs_rule_setids(&rule->ops[0], false, ctx); - } - } - } + return (ecs_table_range_t){ 0 }; +} + +ecs_table_t* flecs_query_var_get_table( + int32_t var_id, + const ecs_query_run_ctx_t *ctx) +{ + ecs_var_t *var = &ctx->vars[var_id]; + ecs_table_t *table = var->range.table; + if (table) { + return table; + } + + ecs_entity_t entity = var->entity; + if (entity && entity != EcsWildcard) { + var->range = flecs_range_from_entity(entity, ctx); + return var->range.table; } - flecs_iter_validate(it); + return NULL; } -bool ecs_rule_next_instanced( - ecs_iter_t *it) +ecs_table_t* flecs_query_get_table( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + const ecs_query_run_ctx_t *ctx) { - ecs_assert(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(it->next == ecs_rule_next, ECS_INVALID_PARAMETER, NULL); - - ecs_rule_iter_t *rit = &it->priv.iter.rule; - ecs_rule_run_ctx_t ctx; - ctx.world = it->real_world; - ctx.rule = rit->rule; - ctx.it = it; - ctx.vars = rit->vars; - ctx.rule_vars = rit->rule_vars; - ctx.written = rit->written; - ctx.op_ctx = rit->op_ctx; - ctx.source_set = &rit->source_set; - ctx.rit = rit; - const ecs_rule_op_t *ops = rit->ops; - - bool redo = true; - if (!(it->flags & EcsIterIsValid)) { - ecs_assert(ctx.rule != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_rule_iter_init(&ctx); - redo = false; + ecs_flags16_t flags = flecs_query_ref_flags(op->flags, ref_kind); + if (flags & EcsQueryIsEntity) { + return ecs_get_table(ctx->world, ref->entity); } else { - it->frame_offset += it->count; + return flecs_query_var_get_table(ref->var, ctx); } +} - /* Specialized iterator modes for trivial queries */ - if (it->flags & EcsIterTrivialSearch) { - ecs_rule_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; - int32_t fields = ctx.rule->filter.term_count; - if (!flecs_rule_trivial_search(ctx.rule, &ctx, op_ctx, !redo, fields)) { - goto done; - } - it->table = ctx.vars[0].range.table; - it->count = ecs_table_count(it->table); - it->entities = flecs_table_entities_array(it->table); - return true; - } else if (it->flags & EcsIterTrivialSearchNoData) { - ecs_rule_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; - int32_t fields = ctx.rule->filter.term_count; - if (!flecs_rule_trivial_search_nodata(ctx.rule, &ctx, op_ctx, !redo, fields)) { - goto done; - } - it->table = ctx.vars[0].range.table; - it->count = ecs_table_count(it->table); - it->entities = flecs_table_entities_array(it->table); - return true; - } else if (it->flags & EcsIterTrivialTest) { - int32_t fields = ctx.rule->filter.term_count; - if (!flecs_rule_trivial_test(ctx.rule, &ctx, !redo, fields)) { - goto done; - } - return true; - } else if (it->flags & EcsIterTrivialSearchWildcard) { - ecs_rule_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; - int32_t fields = ctx.rule->filter.term_count; - if (!flecs_rule_trivial_search_w_wildcards(ctx.rule, &ctx, op_ctx, !redo, fields)) { - goto done; +ecs_table_range_t flecs_query_get_range( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + const ecs_query_run_ctx_t *ctx) +{ + ecs_flags16_t flags = flecs_query_ref_flags(op->flags, ref_kind); + if (flags & EcsQueryIsEntity) { + ecs_assert(!(flags & EcsQueryIsVar), ECS_INTERNAL_ERROR, NULL); + return flecs_range_from_entity(ref->entity, ctx); + } else { + ecs_var_t *var = &ctx->vars[ref->var]; + if (var->range.table) { + return ctx->vars[ref->var].range; + } else if (var->entity) { + return flecs_range_from_entity(var->entity, ctx); } - it->table = ctx.vars[0].range.table; - it->count = ecs_table_count(it->table); - it->entities = flecs_table_entities_array(it->table); - return true; } + return (ecs_table_range_t){0}; +} - /* Default iterator mode */ - if (flecs_rule_run_until(redo, &ctx, ops, -1, rit->op, EcsRuleYield)) { - ecs_assert(ops[ctx.op_index].kind == EcsRuleYield, - ECS_INTERNAL_ERROR, NULL); - ecs_table_range_t *range = &ctx.vars[0].range; - ecs_table_t *table = range->table; - int32_t count = range->count; - if (table) { - if (!count) { - count = ecs_table_count(table); - } - it->table = table; - it->offset = range->offset; - it->count = count; - it->entities = ECS_ELEM_T( - table->data.entities.array, ecs_entity_t, it->offset); - } else if (count == 1) { - it->count = 1; - it->entities = &ctx.vars[0].entity; - } - - rit->op = flecs_itolbl(ctx.op_index - 1); - return true; +ecs_entity_t flecs_query_var_get_entity( + ecs_var_id_t var_id, + const ecs_query_run_ctx_t *ctx) +{ + ecs_assert(var_id < (ecs_var_id_t)ctx->query->var_count, + ECS_INTERNAL_ERROR, NULL); + ecs_var_t *var = &ctx->vars[var_id]; + ecs_entity_t entity = var->entity; + if (entity) { + return entity; } -done: - ecs_iter_fini(it); - return false; + ecs_assert(var->range.count == 1, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = var->range.table; + const ecs_entity_t *entities = ecs_table_entities(table); + var->entity = entities[var->range.offset]; + return var->entity; } -bool ecs_rule_next( - ecs_iter_t *it) +void flecs_query_var_reset( + ecs_var_id_t var_id, + const ecs_query_run_ctx_t *ctx) { - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_rule_next, ECS_INVALID_PARAMETER, NULL); + ctx->vars[var_id].entity = EcsWildcard; + ctx->vars[var_id].range.table = NULL; +} - if (flecs_iter_next_row(it)) { - return true; +void flecs_query_var_set_range( + const ecs_query_op_t *op, + ecs_var_id_t var_id, + ecs_table_t *table, + int32_t offset, + int32_t count, + const ecs_query_run_ctx_t *ctx) +{ + (void)op; + ecs_assert(ctx->query_vars[var_id].kind == EcsVarTable, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_query_is_written(var_id, op->written), + ECS_INTERNAL_ERROR, NULL); + ecs_var_t *var = &ctx->vars[var_id]; + var->entity = 0; + var->range = (ecs_table_range_t){ + .table = table, + .offset = offset, + .count = count + }; +} + +void flecs_query_var_narrow_range( + ecs_var_id_t var_id, + ecs_table_t *table, + int32_t offset, + int32_t count, + const ecs_query_run_ctx_t *ctx) +{ + ecs_var_t *var = &ctx->vars[var_id]; + + var->entity = 0; + var->range = (ecs_table_range_t){ + .table = table, + .offset = offset, + .count = count + }; + + ecs_assert(var_id < ctx->query->var_count, ECS_INTERNAL_ERROR, NULL); + if (ctx->query_vars[var_id].kind != EcsVarTable) { + ecs_assert(count == 1, ECS_INTERNAL_ERROR, NULL); + var->entity = ecs_table_entities(table)[offset]; } +} - return flecs_iter_next_instanced(it, ecs_rule_next_instanced(it)); -error: - return false; +void flecs_query_var_set_entity( + const ecs_query_op_t *op, + ecs_var_id_t var_id, + ecs_entity_t entity, + const ecs_query_run_ctx_t *ctx) +{ + (void)op; + ecs_assert(var_id < (ecs_var_id_t)ctx->query->var_count, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_query_is_written(var_id, op->written), + ECS_INTERNAL_ERROR, NULL); + ecs_var_t *var = &ctx->vars[var_id]; + var->range.table = NULL; + var->entity = entity; } -static -void flecs_rule_iter_fini_ctx( - ecs_iter_t *it, - ecs_rule_iter_t *rit) +void flecs_query_set_vars( + const ecs_query_op_t *op, + ecs_id_t id, + const ecs_query_run_ctx_t *ctx) { - const ecs_rule_t *rule = rit->rule; - int32_t i, count = rule->op_count; - ecs_rule_op_t *ops = rule->ops; - ecs_rule_op_ctx_t *ctx = rit->op_ctx; - ecs_allocator_t *a = flecs_rule_get_allocator(it); + ecs_flags16_t flags_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t flags_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); - for (i = 0; i < count; i ++) { - ecs_rule_op_t *op = &ops[i]; - switch(op->kind) { - case EcsRuleTrav: - flecs_rule_trav_cache_fini(a, &ctx[i].is.trav.cache); - break; - case EcsRuleUp: - case EcsRuleSelfUp: - case EcsRuleUpId: - case EcsRuleSelfUpId: { - ecs_trav_up_cache_t *cache = &ctx[i].is.up.cache; - if (cache->dir == EcsDown) { - flecs_rule_down_cache_fini(a, cache); + if (flags_1st & EcsQueryIsVar) { + ecs_var_id_t var = op->first.var; + if (op->written & (1ull << var)) { + if (ECS_IS_PAIR(id)) { + flecs_query_var_set_entity( + op, var, ecs_get_alive(ctx->world, ECS_PAIR_FIRST(id)), ctx); } else { - flecs_rule_up_cache_fini(cache); + flecs_query_var_set_entity(op, var, id, ctx); } - break; } - default: - break; + } + + if (flags_2nd & EcsQueryIsVar) { + ecs_var_id_t var = op->second.var; + if (op->written & (1ull << var)) { + flecs_query_var_set_entity( + op, var, ecs_get_alive(ctx->world, ECS_PAIR_SECOND(id)), ctx); } } } -static -void flecs_rule_iter_fini( - ecs_iter_t *it) +ecs_table_range_t flecs_get_ref_range( + const ecs_query_ref_t *ref, + ecs_flags16_t flag, + const ecs_query_run_ctx_t *ctx) { - ecs_rule_iter_t *rit = &it->priv.iter.rule; - ecs_assert(rit->rule != NULL, ECS_INVALID_OPERATION, NULL); - ecs_poly_assert(rit->rule, ecs_rule_t); - int32_t op_count = rit->rule->op_count; - int32_t var_count = rit->rule->var_count; + if (flag & EcsQueryIsEntity) { + return flecs_range_from_entity(ref->entity, ctx); + } else if (flag & EcsQueryIsVar) { + return flecs_query_var_get_range(ref->var, ctx); + } + return (ecs_table_range_t){0}; +} -#ifdef FLECS_DEBUG - if (it->flags & EcsIterProfile) { - char *str = ecs_rule_str_w_profile(rit->rule, it); - printf("%s\n", str); - ecs_os_free(str); +ecs_entity_t flecs_get_ref_entity( + const ecs_query_ref_t *ref, + ecs_flags16_t flag, + const ecs_query_run_ctx_t *ctx) +{ + if (flag & EcsQueryIsEntity) { + return ref->entity; + } else if (flag & EcsQueryIsVar) { + return flecs_query_var_get_entity(ref->var, ctx); } + return 0; +} - flecs_iter_free_n(rit->profile, ecs_rule_op_profile_t, op_count); -#endif +ecs_id_t flecs_query_op_get_id_w_written( + const ecs_query_op_t *op, + uint64_t written, + const ecs_query_run_ctx_t *ctx) +{ + ecs_flags16_t flags_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t flags_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); + ecs_entity_t first = 0, second = 0; + + if (flags_1st) { + if (flecs_ref_is_written(op, &op->first, EcsQueryFirst, written)) { + first = flecs_get_ref_entity(&op->first, flags_1st, ctx); + } else if (flags_1st & EcsQueryIsVar) { + first = EcsWildcard; + } + } + if (flags_2nd) { + if (flecs_ref_is_written(op, &op->second, EcsQuerySecond, written)) { + second = flecs_get_ref_entity(&op->second, flags_2nd, ctx); + } else if (flags_2nd & EcsQueryIsVar) { + second = EcsWildcard; + } + } - flecs_rule_iter_fini_ctx(it, rit); - flecs_iter_free_n(rit->vars, ecs_var_t, var_count); - flecs_iter_free_n(rit->written, ecs_write_flags_t, op_count); - flecs_iter_free_n(rit->op_ctx, ecs_rule_op_ctx_t, op_count); - rit->vars = NULL; - rit->written = NULL; - rit->op_ctx = NULL; - rit->rule = NULL; + if (flags_2nd & (EcsQueryIsVar | EcsQueryIsEntity)) { + return ecs_pair(first, second); + } else { + return flecs_entities_get_alive(ctx->world, first); + } +} + +ecs_id_t flecs_query_op_get_id( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + return flecs_query_op_get_id_w_written(op, written, ctx); +} + +int16_t flecs_query_next_column( + ecs_table_t *table, + ecs_id_t id, + int32_t column) +{ + if (!ECS_IS_PAIR(id) || (ECS_PAIR_FIRST(id) != EcsWildcard)) { + column = column + 1; + } else { + ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); + column = ecs_search_offset(NULL, table, column + 1, id, NULL); + ecs_assert(column != -1, ECS_INTERNAL_ERROR, NULL); + } + return flecs_ito(int16_t, column); +} + +void flecs_query_it_set_tr( + ecs_iter_t *it, + int32_t field_index, + const ecs_table_record_t *tr) +{ + ecs_assert(field_index >= 0, ECS_INTERNAL_ERROR, NULL); + it->trs[field_index] = tr; +} + +ecs_id_t flecs_query_it_set_id( + ecs_iter_t *it, + ecs_table_t *table, + int32_t field_index, + int32_t column) +{ + ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(field_index >= 0, ECS_INTERNAL_ERROR, NULL); + return it->ids[field_index] = table->type.array[column]; } -ecs_iter_t ecs_rule_iter( - const ecs_world_t *world, - const ecs_rule_t *rule) +void flecs_query_set_match( + const ecs_query_op_t *op, + ecs_table_t *table, + int32_t column, + const ecs_query_run_ctx_t *ctx) { - ecs_iter_t it = {0}; - ecs_rule_iter_t *rit = &it.priv.iter.rule; - ecs_check(rule != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_run_aperiodic(rule->filter.world, EcsAperiodicEmptyTables); - - int32_t i, var_count = rule->var_count, op_count = rule->op_count; - it.world = ECS_CONST_CAST(ecs_world_t*, world); - it.real_world = rule->filter.world; - it.terms = rule->filter.terms; - it.next = ecs_rule_next; - it.fini = flecs_rule_iter_fini; - it.field_count = rule->filter.field_count; - it.sizes = rule->filter.sizes; - flecs_filter_apply_iter_flags(&it, &rule->filter); - - flecs_iter_init(world, &it, - flecs_iter_cache_ids | - flecs_iter_cache_columns | - flecs_iter_cache_sources | - flecs_iter_cache_ptrs); - - rit->rule = rule; - rit->rule_vars = rule->vars; - rit->ops = rule->ops; - rit->source_set = 0; - if (var_count) { - rit->vars = flecs_iter_calloc_n(&it, ecs_var_t, var_count); - } - if (op_count) { - rit->written = flecs_iter_calloc_n(&it, ecs_write_flags_t, op_count); - rit->op_ctx = flecs_iter_calloc_n(&it, ecs_rule_op_ctx_t, op_count); + ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); + int32_t field_index = op->field_index; + if (field_index == -1) { + return; } -#ifdef FLECS_DEBUG - rit->profile = flecs_iter_calloc_n(&it, ecs_rule_op_profile_t, op_count); -#endif + ecs_iter_t *it = ctx->it; + ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column < table->type.count, ECS_INTERNAL_ERROR, NULL); + const ecs_table_record_t *tr = &table->_->records[column]; + flecs_query_it_set_tr(it, field_index, tr); + ecs_id_t matched = flecs_query_it_set_id(it, table, field_index, tr->index); + flecs_query_set_vars(op, matched, ctx); +} - for (i = 1; i < var_count; i ++) { - rit->vars[i].entity = EcsWildcard; +void flecs_query_set_trav_match( + const ecs_query_op_t *op, + const ecs_table_record_t *tr, + ecs_entity_t trav, + ecs_entity_t second, + const ecs_query_run_ctx_t *ctx) +{ + int32_t field_index = op->field_index; + if (field_index == -1) { + return; } - it.variables = rit->vars; - it.variable_count = rule->var_pub_count; - it.variable_names = rule->var_names; - -error: - return it; + ecs_iter_t *it = ctx->it; + ecs_id_t matched = ecs_pair(trav, second); + it->ids[op->field_index] = matched; + flecs_query_it_set_tr(it, op->field_index, tr); + flecs_query_set_vars(op, matched, ctx); } -#endif +bool flecs_query_table_filter( + ecs_table_t *table, + ecs_query_lbl_t other, + ecs_flags32_t filter_mask) +{ + uint32_t filter = flecs_ito(uint32_t, other); + return (table->flags & filter_mask & filter) != 0; +} /** - * @file addons/rules/trav_cache.c + * @file query/engine/trav_cache.c * @brief Cache that stores the result of graph traversal. */ -#ifdef FLECS_RULES - static -void flecs_rule_build_down_cache( +void flecs_query_build_down_cache( ecs_world_t *world, ecs_allocator_t *a, - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, ecs_entity_t entity) @@ -65790,11 +73389,11 @@ void flecs_rule_build_down_cache( } int32_t i, count = ecs_table_count(table); - ecs_entity_t *entities = table->data.entities.array; + const ecs_entity_t *entities = ecs_table_entities(table); for (i = 0; i < count; i ++) { ecs_record_t *r = flecs_entities_get(world, entities[i]); if (r->row & EcsEntityIsTraversable) { - flecs_rule_build_down_cache( + flecs_query_build_down_cache( world, a, ctx, cache, trav, entities[i]); } } @@ -65803,10 +73402,10 @@ void flecs_rule_build_down_cache( } static -void flecs_rule_build_up_cache( +void flecs_query_build_up_cache( ecs_world_t *world, ecs_allocator_t *a, - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, ecs_table_t *table, @@ -65825,8 +73424,9 @@ void flecs_rule_build_up_cache( ecs_trav_elem_t *el = ecs_vec_append_t(a, &cache->entities, ecs_trav_elem_t); + el->entity = second; - el->column = root_column; + el->tr = &table->_->records[i]; el->idr = NULL; ecs_record_t *r = flecs_entities_get_any(world, second); @@ -65836,44 +73436,44 @@ void flecs_rule_build_up_cache( if (!r_tr) { return; } - flecs_rule_build_up_cache(world, a, ctx, cache, trav, r->table, + flecs_query_build_up_cache(world, a, ctx, cache, trav, r->table, r_tr, root_column); } } } -void flecs_rule_trav_cache_fini( +void flecs_query_trav_cache_fini( ecs_allocator_t *a, ecs_trav_cache_t *cache) { ecs_vec_fini_t(a, &cache->entities, ecs_trav_elem_t); } -void flecs_rule_get_trav_down_cache( - const ecs_rule_run_ctx_t *ctx, +void flecs_query_get_trav_down_cache( + const ecs_query_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, ecs_entity_t entity) { if (cache->id != ecs_pair(trav, entity) || cache->up) { ecs_world_t *world = ctx->it->real_world; - ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_allocator_t *a = flecs_query_get_allocator(ctx->it); ecs_vec_reset_t(a, &cache->entities, ecs_trav_elem_t); - flecs_rule_build_down_cache(world, a, ctx, cache, trav, entity); + flecs_query_build_down_cache(world, a, ctx, cache, trav, entity); cache->id = ecs_pair(trav, entity); cache->up = false; } } -void flecs_rule_get_trav_up_cache( - const ecs_rule_run_ctx_t *ctx, +void flecs_query_get_trav_up_cache( + const ecs_query_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, ecs_table_t *table) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_world_t *world = ctx->it->real_world; - ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_allocator_t *a = flecs_query_get_allocator(ctx->it); ecs_id_record_t *idr = cache->idr; if (!idr || idr->id != ecs_pair(trav, EcsWildcard)) { @@ -65895,16 +73495,17 @@ void flecs_rule_get_trav_up_cache( if (cache->id != id || !cache->up) { ecs_vec_reset_t(a, &cache->entities, ecs_trav_elem_t); - flecs_rule_build_up_cache(world, a, ctx, cache, trav, table, tr, -1); + flecs_query_build_up_cache(world, a, ctx, cache, trav, table, tr, -1); cache->id = id; cache->up = true; } } -#endif - +/** + * @file query/engine/trav_down_cache.c + * @brief Compile query term. + */ -#ifdef FLECS_RULES static void flecs_trav_entity_down_isa( @@ -65915,7 +73516,8 @@ void flecs_trav_entity_down_isa( ecs_entity_t trav, ecs_entity_t entity, ecs_id_record_t *idr_with, - bool self); + bool self, + bool empty); static ecs_trav_down_t* flecs_trav_entity_down( @@ -65927,11 +73529,12 @@ ecs_trav_down_t* flecs_trav_entity_down( ecs_entity_t entity, ecs_id_record_t *idr_trav, ecs_id_record_t *idr_with, - bool self); + bool self, + bool empty); static ecs_trav_down_t* flecs_trav_down_ensure( - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_trav_up_cache_t *cache, ecs_entity_t entity) { @@ -65954,7 +73557,8 @@ ecs_trav_down_t* flecs_trav_table_down( ecs_entity_t trav, const ecs_table_t *table, ecs_id_record_t *idr_with, - bool self) + bool self, + bool empty) { ecs_assert(table->id != 0, ECS_INTERNAL_ERROR, NULL); @@ -65964,7 +73568,7 @@ ecs_trav_down_t* flecs_trav_table_down( ecs_assert(idr_with != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + const ecs_entity_t *entities = ecs_table_entities(table); int32_t i, count = ecs_table_count(table); for (i = 0; i < count; i ++) { ecs_entity_t entity = entities[i]; @@ -65982,7 +73586,7 @@ ecs_trav_down_t* flecs_trav_table_down( } flecs_trav_entity_down(world, a, cache, dst, - trav, entity, idr_trav, idr_with, self); + trav, entity, idr_trav, idr_with, self, empty); } } @@ -65998,7 +73602,8 @@ void flecs_trav_entity_down_isa( ecs_entity_t trav, ecs_entity_t entity, ecs_id_record_t *idr_with, - bool self) + bool self, + bool empty) { if (trav == EcsIsA || !world->idr_isa_wildcard) { return; @@ -66011,7 +73616,7 @@ void flecs_trav_entity_down_isa( } ecs_table_cache_iter_t it; - if (flecs_table_cache_all_iter(&idr_isa->cache, &it)) { + if (flecs_table_cache_iter(&idr_isa->cache, &it)) { ecs_table_record_t *tr; while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { ecs_table_t *table = tr->hdr.table; @@ -66019,7 +73624,12 @@ void flecs_trav_entity_down_isa( continue; } - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + if (ecs_table_has_id(world, table, idr_with->id)) { + /* Table owns component */ + continue; + } + + const ecs_entity_t *entities = ecs_table_entities(table); int32_t i, count = ecs_table_count(table); for (i = 0; i < count; i ++) { ecs_entity_t e = entities[i]; @@ -66032,8 +73642,13 @@ void flecs_trav_entity_down_isa( if (flags & EcsEntityIsTraversable) { ecs_id_record_t *idr_trav = flecs_id_record_get(world, ecs_pair(trav, e)); - flecs_trav_entity_down(world, a, cache, dst, trav, e, - idr_trav, idr_with, self); + if (idr_trav) { + flecs_trav_entity_down(world, a, cache, dst, trav, e, + idr_trav, idr_with, self, empty); + } + + flecs_trav_entity_down_isa(world, a, cache, dst, trav, e, + idr_with, self, empty); } } } @@ -66047,21 +73662,30 @@ ecs_trav_down_t* flecs_trav_entity_down( ecs_trav_up_cache_t *cache, ecs_trav_down_t *dst, ecs_entity_t trav, - ecs_entity_t entity, + ecs_entity_t e, ecs_id_record_t *idr_trav, ecs_id_record_t *idr_with, - bool self) + bool self, + bool empty) { ecs_assert(dst != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(idr_with != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(idr_trav != NULL, ECS_INTERNAL_ERROR, NULL); flecs_trav_entity_down_isa( - world, a, cache, dst, trav, entity, idr_with, self); + world, a, cache, dst, trav, e, idr_with, self, empty); int32_t first = ecs_vec_count(&dst->elems); ecs_table_cache_iter_t it; - if (flecs_table_cache_iter(&idr_trav->cache, &it)) { + bool result; + if (empty) { + result = flecs_table_cache_all_iter(&idr_trav->cache, &it); + } else { + result = flecs_table_cache_iter(&idr_trav->cache, &it); + } + + if (result) { ecs_table_record_t *tr; while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { ecs_assert(tr->count == 1, ECS_INTERNAL_ERROR, NULL); @@ -66107,26 +73731,27 @@ ecs_trav_down_t* flecs_trav_entity_down( &dst->elems, ecs_trav_down_elem_t, t); if (!elem->leaf) { flecs_trav_table_down(world, a, cache, dst, trav, - elem->table, idr_with, self); + elem->table, idr_with, self, empty); } } return dst; } -ecs_trav_down_t* flecs_rule_get_down_cache( - const ecs_rule_run_ctx_t *ctx, +ecs_trav_down_t* flecs_query_get_down_cache( + const ecs_query_run_ctx_t *ctx, ecs_trav_up_cache_t *cache, ecs_entity_t trav, ecs_entity_t e, ecs_id_record_t *idr_with, - bool self) + bool self, + bool empty) { ecs_world_t *world = ctx->it->real_world; - ecs_assert(!(cache->dir & EcsUp), ECS_INTERNAL_ERROR, NULL); - cache->dir = EcsDown; + ecs_assert(cache->dir != EcsTravUp, ECS_INTERNAL_ERROR, NULL); + cache->dir = EcsTravDown; - ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_allocator_t *a = flecs_query_get_allocator(ctx->it); ecs_map_init_if(&cache->src, a); ecs_trav_down_t *result = flecs_trav_down_ensure(ctx, cache, e); @@ -66138,7 +73763,7 @@ ecs_trav_down_t* flecs_rule_get_down_cache( if (!idr_trav) { if (trav != EcsIsA) { flecs_trav_entity_down_isa( - world, a, cache, result, trav, e, idr_with, self); + world, a, cache, result, trav, e, idr_with, self, empty); } result->ready = true; return result; @@ -66146,13 +73771,13 @@ ecs_trav_down_t* flecs_rule_get_down_cache( ecs_vec_init_t(a, &result->elems, ecs_trav_down_elem_t, 0); flecs_trav_entity_down( - world, a, cache, result, trav, e, idr_trav, idr_with, self); + world, a, cache, result, trav, e, idr_trav, idr_with, self, empty); result->ready = true; return result; } -void flecs_rule_down_cache_fini( +void flecs_query_down_cache_fini( ecs_allocator_t *a, ecs_trav_up_cache_t *cache) { @@ -66164,14 +73789,15 @@ void flecs_rule_down_cache_fini( ecs_map_fini(&cache->src); } -#endif - +/** + * @file query/engine/trav_up_cache.c + * @brief Compile query term. + */ -#ifdef FLECS_RULES static ecs_trav_up_t* flecs_trav_up_ensure( - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_trav_up_cache_t *cache, uint64_t table_id) { @@ -66194,7 +73820,8 @@ int32_t flecs_trav_type_search( ecs_table_record_t *tr = ecs_table_cache_get(&idr_with->cache, table); if (tr) { up->id = type->array[tr->index]; - return up->column = tr->index; + up->tr = tr; + return tr->index; } return -1; @@ -66203,6 +73830,7 @@ int32_t flecs_trav_type_search( static int32_t flecs_trav_type_offset_search( ecs_trav_up_t *up, + const ecs_table_t *table, int32_t offset, ecs_id_t with, ecs_type_t *type) @@ -66213,8 +73841,9 @@ int32_t flecs_trav_type_offset_search( while (offset < type->count) { ecs_id_t type_id = type->array[offset ++]; if (ecs_id_match(type_id, with)) { - up->id = flecs_to_public_id(type_id); - return up->column = offset - 1; + up->id = type_id; + up->tr = &table->_->records[offset - 1]; + return offset - 1; } } @@ -66223,7 +73852,7 @@ int32_t flecs_trav_type_offset_search( static ecs_trav_up_t* flecs_trav_table_up( - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_allocator_t *a, ecs_trav_up_cache_t *cache, const ecs_world_t *world, @@ -66273,15 +73902,15 @@ ecs_trav_up_t* flecs_trav_table_up( ecs_trav_up_t *up_parent = flecs_trav_table_up(ctx, a, cache, world, tgt, with, rel, idr_with, idr_trav); - if (up_parent->column != -1) { + if (up_parent->tr) { up->src = up_parent->src; - up->column = up_parent->column; + up->tr = up_parent->tr; up->id = up_parent->id; goto found; } r_column = flecs_trav_type_offset_search( - &up_pair, r_column + 1, rel, &type); + &up_pair, table, r_column + 1, rel, &type); } if (!is_a) { @@ -66295,28 +73924,28 @@ ecs_trav_up_t* flecs_trav_table_up( ecs_trav_up_t *up_parent = flecs_trav_table_up(ctx, a, cache, world, tgt, with, rel, idr_with, idr_trav); - if (up_parent->column != -1) { + if (up_parent->tr) { up->src = up_parent->src; - up->column = up_parent->column; + up->tr = up_parent->tr; up->id = up_parent->id; goto found; } r_column = flecs_trav_type_offset_search( - &up_pair, r_column + 1, rel, &type); + &up_pair, table, r_column + 1, rel, &type); } } } not_found: - up->column = -1; + up->tr = NULL; found: up->ready = true; return up; } -ecs_trav_up_t* flecs_rule_get_up_cache( - const ecs_rule_run_ctx_t *ctx, +ecs_trav_up_t* flecs_query_get_up_cache( + const ecs_query_run_ctx_t *ctx, ecs_trav_up_cache_t *cache, ecs_table_t *table, ecs_id_t with, @@ -66325,15 +73954,15 @@ ecs_trav_up_t* flecs_rule_get_up_cache( ecs_id_record_t *idr_trav) { if (cache->with && cache->with != with) { - flecs_rule_up_cache_fini(cache); + flecs_query_up_cache_fini(cache); } ecs_world_t *world = ctx->it->real_world; - ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_allocator_t *a = flecs_query_get_allocator(ctx->it); ecs_map_init_if(&cache->src, a); - ecs_assert(!(cache->dir & EcsDown), ECS_INTERNAL_ERROR, NULL); - cache->dir = EcsUp; + ecs_assert(cache->dir != EcsTravDown, ECS_INTERNAL_ERROR, NULL); + cache->dir = EcsTravUp; cache->with = with; ecs_assert(idr_with != NULL, ECS_INTERNAL_ERROR, NULL); @@ -66358,128 +73987,82 @@ ecs_trav_up_t* flecs_rule_get_up_cache( return NULL; } -void flecs_rule_up_cache_fini( +void flecs_query_up_cache_fini( ecs_trav_up_cache_t *cache) { ecs_map_fini(&cache->src); } -#endif - /** - * @file addons/rules/engine.c + * @file query/engine/trivial_iter.c * @brief Iterator for trivial queries. */ -#ifdef FLECS_RULES - static -bool flecs_rule_trivial_init( - ecs_world_t *world, - const ecs_filter_t *filter) +bool flecs_query_trivial_search_init( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + const ecs_query_t *query, + bool redo, + ecs_flags64_t term_set) { - int32_t t, count = filter->term_count; - ecs_term_t *terms = filter->terms; - - for (t = 0; t < count; t ++) { - ecs_term_t *term = &terms[t]; - if (!term->idr) { - term->idr = flecs_id_record_get(world, term->id); - if (!term->idr) { - /* Id doesn't exist, so query can't match */ - return false; + if (!redo) { + /* Find first trivial term*/ + int32_t t = 0; + if (term_set) { + for (; t < query->term_count; t ++) { + if (term_set & (1llu << t)) { + break; + } } } - } - return true; -} - -bool flecs_rule_trivial_test( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - bool first, - int32_t term_count) -{ - if (first) { - const ecs_filter_t *filter = &rule->filter; - int32_t t; - ecs_term_t *terms = filter->terms; - ecs_iter_t *it = ctx->it; + ecs_assert(t != query->term_count, ECS_INTERNAL_ERROR, NULL); + op_ctx->start_from = t; - if (!flecs_rule_trivial_init(ctx->world, filter)) { + ecs_id_record_t *idr = flecs_id_record_get(ctx->world, query->ids[t]); + if (!idr) { return false; } - ecs_table_t *table = ctx->vars[0].range.table; - ecs_assert(table != NULL, ECS_INVALID_OPERATION, NULL); - - for (t = 0; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; - const ecs_table_record_t *tr = flecs_id_record_get_table( - term->idr, table); - if (!tr) { + if (query->flags & EcsQueryMatchEmptyTables) { + if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)){ return false; } - - it->columns[t] = tr->index + 1; - if (it->count && tr->column != -1) { - it->ptrs[t] = ecs_vec_get( - &table->data.columns[tr->column].data, - it->sizes[t], - it->offset); + } else { + if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { + return false; } } - it->table = table; - it->entities = &flecs_table_entities_array(table)[it->offset]; - return true; - } else { - return false; - } -} - -static -bool flecs_rule_trivial_search_init( - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - const ecs_filter_t *filter, - bool first) -{ - if (first) { - ecs_term_t *terms = filter->terms; - if (!flecs_rule_trivial_init(ctx->world, filter)) { - return false; - } - - if (filter->flags & EcsFilterMatchEmptyTables) { - if (!flecs_table_cache_all_iter(&terms[0].idr->cache, &op_ctx->it)){ - return false; - } - } else { - if (!flecs_table_cache_iter(&terms[0].idr->cache, &op_ctx->it)) { - return false; + /* Find next term to evaluate once */ + + for (t = t + 1; t < query->term_count; t ++) { + if (term_set & (1llu << t)) { + break; } } + + op_ctx->first_to_eval = t; } return true; } -bool flecs_rule_trivial_search( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t term_count) +bool flecs_query_trivial_search( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + bool redo, + ecs_flags64_t term_set) { - const ecs_filter_t *filter = &rule->filter; - int32_t t; - ecs_term_t *terms = filter->terms; + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + const ecs_term_t *terms = q->terms; ecs_iter_t *it = ctx->it; + int32_t t, term_count = query->pub.term_count; - if (!flecs_rule_trivial_search_init(ctx, op_ctx, filter, first)) { + if (!flecs_query_trivial_search_init(ctx, op_ctx, q, redo, term_set)) { return false; } @@ -66491,34 +74074,35 @@ bool flecs_rule_trivial_search( } ecs_table_t *table = tr->hdr.table; - if (table->flags & (EcsTableIsPrefab|EcsTableIsDisabled)) { + if (table->flags & (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)) { continue; } - for (t = 1; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; + for (t = op_ctx->first_to_eval; t < term_count; t ++) { + if (!(term_set & (1llu << t))) { + continue; + } + + const ecs_term_t *term = &terms[t]; + ecs_id_record_t *idr = flecs_id_record_get(ctx->world, term->id); + if (!idr) { + break; + } + const ecs_table_record_t *tr_with = flecs_id_record_get_table( - term->idr, table); + idr, table); if (!tr_with) { break; } - it->columns[t] = tr_with->index + 1; - if (tr_with->column != -1) { - it->ptrs[t] = ecs_vec_first( - &table->data.columns[tr_with->column].data); - } + it->trs[term->field_index] = tr_with; } if (t == term_count) { ctx->vars[0].range.table = table; ctx->vars[0].range.count = 0; ctx->vars[0].range.offset = 0; - it->columns[0] = tr->index + 1; - if (tr->column != -1) { - it->ptrs[0] = ecs_vec_first( - &table->data.columns[tr->column].data); - } + it->trs[op_ctx->start_from] = tr; break; } } while (true); @@ -66526,64 +74110,23 @@ bool flecs_rule_trivial_search( return true; } -bool flecs_rule_trivial_search_w_wildcards( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t term_count) -{ - bool result = flecs_rule_trivial_search( - rule, ctx, op_ctx, first, term_count); - if (result) { - ecs_iter_t *it = ctx->it; - ecs_table_t *table = ctx->vars[0].range.table; - int32_t t; - for (t = 0; t < term_count; t ++) { - it->ids[t] = table->type.array[it->columns[t] - 1]; - } - } - - return result; -} - -bool flecs_rule_trivial_test_w_wildcards( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - bool first, - int32_t term_count) -{ - bool result = flecs_rule_trivial_test( - rule, ctx, first, term_count); - if (result) { - ecs_iter_t *it = ctx->it; - ecs_table_t *table = ctx->vars[0].range.table; - int32_t t; - for (t = 0; t < term_count; t ++) { - it->ids[t] = table->type.array[it->columns[t] - 1]; - } - } - - return result; -} - -bool flecs_rule_trivial_search_nodata( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t term_count) +bool flecs_query_is_trivial_search( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + bool redo) { - const ecs_filter_t *filter = &rule->filter; - int32_t t; - ecs_term_t *terms = filter->terms; + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + const ecs_id_t *ids = q->ids; ecs_iter_t *it = ctx->it; + int32_t t, term_count = query->pub.term_count; - if (!flecs_rule_trivial_search_init(ctx, op_ctx, filter, first)) { + if (!flecs_query_trivial_search_init(ctx, op_ctx, q, redo, 0)) { return false; } - do { +next: + { const ecs_table_record_t *tr = flecs_table_cache_next( &op_ctx->it, ecs_table_record_t); if (!tr) { @@ -66591,424 +74134,77 @@ bool flecs_rule_trivial_search_nodata( } ecs_table_t *table = tr->hdr.table; - if (table->flags & (EcsTableIsPrefab|EcsTableIsDisabled)) { - continue; + if (table->flags & (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)) { + goto next; } for (t = 1; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; + ecs_id_record_t *idr = flecs_id_record_get(ctx->world, ids[t]); + if (!idr) { + return false; + } + const ecs_table_record_t *tr_with = flecs_id_record_get_table( - term->idr, table); + idr, table); if (!tr_with) { - break; + goto next; } - it->columns[t] = tr_with->index + 1; + it->trs[t] = tr_with; } - if (t == term_count) { - ctx->vars[0].range.table = table; - ctx->vars[0].range.count = 0; - ctx->vars[0].range.offset = 0; - it->columns[0] = tr->index + 1; - break; - } - } while (true); + it->table = table; + it->count = ecs_table_count(table); + it->entities = ecs_table_entities(table); + it->trs[0] = tr; + } return true; } -#endif - -/** - * @file addons/system/system.c - * @brief System addon. - */ - - -#ifdef FLECS_SYSTEM - - -ecs_mixins_t ecs_system_t_mixins = { - .type_name = "ecs_system_t", - .elems = { - [EcsMixinWorld] = offsetof(ecs_system_t, world), - [EcsMixinEntity] = offsetof(ecs_system_t, entity), - [EcsMixinDtor] = offsetof(ecs_system_t, dtor) - } -}; - -/* -- Public API -- */ - -ecs_entity_t ecs_run_intern( - ecs_world_t *world, - ecs_stage_t *stage, - ecs_entity_t system, - ecs_system_t *system_data, - int32_t stage_index, - int32_t stage_count, - ecs_ftime_t delta_time, - int32_t offset, - int32_t limit, - void *param) +bool flecs_query_trivial_test( + const ecs_query_run_ctx_t *ctx, + bool redo, + ecs_flags64_t term_set) { - ecs_ftime_t time_elapsed = delta_time; - ecs_entity_t tick_source = system_data->tick_source; - - /* Support legacy behavior */ - if (!param) { - param = system_data->ctx; - } - - if (tick_source) { - const EcsTickSource *tick = ecs_get(world, tick_source, EcsTickSource); - - if (tick) { - time_elapsed = tick->time_elapsed; - - /* If timer hasn't fired we shouldn't run the system */ - if (!tick->tick) { - return 0; - } - } else { - /* If a timer has been set but the timer entity does not have the - * EcsTimer component, don't run the system. This can be the result - * of a single-shot timer that has fired already. Not resetting the - * timer field of the system will ensure that the system won't be - * ran after the timer has fired. */ - return 0; - } - } - - if (ecs_should_log_3()) { - char *path = ecs_get_fullpath(world, system); - ecs_dbg_3("worker %d: %s", stage_index, path); - ecs_os_free(path); - } - - ecs_time_t time_start; - bool measure_time = ECS_BIT_IS_SET(world->flags, EcsWorldMeasureSystemTime); - if (measure_time) { - ecs_os_get_time(&time_start); - } - - ecs_world_t *thread_ctx = world; - if (stage) { - thread_ctx = stage->thread_ctx; + if (redo) { + return false; } else { - stage = &world->stages[0]; - } - - /* Prepare the query iterator */ - ecs_iter_t pit, wit, qit = ecs_query_iter(thread_ctx, system_data->query); - ecs_iter_t *it = &qit; - - qit.system = system; - qit.delta_time = delta_time; - qit.delta_system_time = time_elapsed; - qit.frame_offset = offset; - qit.param = param; - qit.ctx = system_data->ctx; - qit.binding_ctx = system_data->binding_ctx; - - flecs_defer_begin(world, stage); - - if (offset || limit) { - pit = ecs_page_iter(it, offset, limit); - it = &pit; - } + const ecs_query_impl_t *impl = ctx->query; + const ecs_query_t *q = &impl->pub; + const ecs_term_t *terms = q->terms; + ecs_iter_t *it = ctx->it; + int32_t t, term_count = impl->pub.term_count; - if (stage_count > 1 && system_data->multi_threaded) { - wit = ecs_worker_iter(it, stage_index, stage_count); - it = &wit; - } + ecs_table_t *table = it->table; + ecs_assert(table != NULL, ECS_INVALID_OPERATION, + "the variable set on the iterator is missing a table"); - ecs_iter_action_t action = system_data->action; - it->callback = action; - - ecs_run_action_t run = system_data->run; - if (run) { - run(it); - } else if (system_data->query->filter.term_count) { - if (it == &qit) { - while (ecs_query_next(&qit)) { - action(&qit); - } - } else { - while (ecs_iter_next(it)) { - action(it); + for (t = 0; t < term_count; t ++) { + if (!(term_set & (1llu << t))) { + continue; } - } - } else { - action(&qit); - ecs_iter_fini(&qit); - } - - if (measure_time) { - system_data->time_spent += (ecs_ftime_t)ecs_time_measure(&time_start); - } - - system_data->invoke_count ++; - - flecs_defer_end(world, stage); - - return it->interrupted_by; -} - -/* -- Public API -- */ - -ecs_entity_t ecs_run_w_filter( - ecs_world_t *world, - ecs_entity_t system, - ecs_ftime_t delta_time, - int32_t offset, - int32_t limit, - void *param) -{ - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_system_t *system_data = ecs_poly_get(world, system, ecs_system_t); - ecs_assert(system_data != NULL, ECS_INVALID_PARAMETER, NULL); - return ecs_run_intern(world, stage, system, system_data, 0, 0, delta_time, - offset, limit, param); -} - -ecs_entity_t ecs_run_worker( - ecs_world_t *world, - ecs_entity_t system, - int32_t stage_index, - int32_t stage_count, - ecs_ftime_t delta_time, - void *param) -{ - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_system_t *system_data = ecs_poly_get(world, system, ecs_system_t); - ecs_assert(system_data != NULL, ECS_INVALID_PARAMETER, NULL); - - return ecs_run_intern( - world, stage, system, system_data, stage_index, stage_count, - delta_time, 0, 0, param); -} - -ecs_entity_t ecs_run( - ecs_world_t *world, - ecs_entity_t system, - ecs_ftime_t delta_time, - void *param) -{ - return ecs_run_w_filter(world, system, delta_time, 0, 0, param); -} - -ecs_query_t* ecs_system_get_query( - const ecs_world_t *world, - ecs_entity_t system) -{ - const ecs_system_t *s = ecs_poly_get(world, system, ecs_system_t); - if (s) { - return s->query; - } else { - return NULL; - } -} - -void* ecs_system_get_ctx( - const ecs_world_t *world, - ecs_entity_t system) -{ - const ecs_system_t *s = ecs_poly_get(world, system, ecs_system_t); - if (s) { - return s->ctx; - } else { - return NULL; - } -} - -void* ecs_system_get_binding_ctx( - const ecs_world_t *world, - ecs_entity_t system) -{ - const ecs_system_t *s = ecs_poly_get(world, system, ecs_system_t); - if (s) { - return s->binding_ctx; - } else { - return NULL; - } -} - -/* System deinitialization */ -static -void flecs_system_fini(ecs_system_t *sys) { - if (sys->ctx_free) { - sys->ctx_free(sys->ctx); - } - - if (sys->binding_ctx_free) { - sys->binding_ctx_free(sys->binding_ctx); - } - - ecs_poly_free(sys, ecs_system_t); -} -static -void flecs_system_init_timer( - ecs_world_t *world, - ecs_entity_t entity, - const ecs_system_desc_t *desc) -{ - if (ECS_NEQZERO(desc->interval) || ECS_NEQZERO(desc->rate) || - ECS_NEQZERO(desc->tick_source)) - { -#ifdef FLECS_TIMER - if (ECS_NEQZERO(desc->interval)) { - ecs_set_interval(world, entity, desc->interval); - } - - if (desc->rate) { - ecs_entity_t tick_source = desc->tick_source; - if (!tick_source) { - tick_source = entity; + const ecs_term_t *term = &terms[t]; + ecs_id_record_t *idr = flecs_id_record_get(q->world, term->id); + if (!idr) { + return false; } - ecs_set_rate(world, entity, desc->rate, tick_source); - } else if (desc->tick_source) { - ecs_set_tick_source(world, entity, desc->tick_source); - } -#else - (void)world; - (void)entity; - ecs_abort(ECS_UNSUPPORTED, "timer module not available"); -#endif - } -} - -ecs_entity_t ecs_system_init( - ecs_world_t *world, - const ecs_system_desc_t *desc) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - ecs_assert(!(world->flags & EcsWorldReadonly), - ECS_INVALID_WHILE_READONLY, NULL); - - ecs_entity_t entity = desc->entity; - if (!entity) { - entity = ecs_new(world, 0); - } - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_system_t); - if (!poly->poly) { - ecs_system_t *system = ecs_poly_new(ecs_system_t); - ecs_assert(system != NULL, ECS_INTERNAL_ERROR, NULL); - - poly->poly = system; - system->world = world; - system->dtor = (ecs_poly_dtor_t)flecs_system_fini; - system->entity = entity; - - ecs_query_desc_t query_desc = desc->query; - query_desc.filter.entity = entity; - - ecs_query_t *query = ecs_query_init(world, &query_desc); - if (!query) { - ecs_delete(world, entity); - return 0; - } - - /* Prevent the system from moving while we're initializing */ - flecs_defer_begin(world, &world->stages[0]); - - system->query = query; - system->query_entity = query->filter.entity; - - system->run = desc->run; - system->action = desc->callback; - - system->ctx = desc->ctx; - system->binding_ctx = desc->binding_ctx; - - system->ctx_free = desc->ctx_free; - system->binding_ctx_free = desc->binding_ctx_free; - - system->tick_source = desc->tick_source; - - system->multi_threaded = desc->multi_threaded; - system->no_readonly = desc->no_readonly; - - flecs_system_init_timer(world, entity, desc); - - if (ecs_get_name(world, entity)) { - ecs_trace("#[green]system#[reset] %s created", - ecs_get_name(world, entity)); - } - - ecs_defer_end(world); - } else { - ecs_poly_assert(poly->poly, ecs_system_t); - ecs_system_t *system = (ecs_system_t*)poly->poly; - - if (desc->run) { - system->run = desc->run; - } - if (desc->callback) { - system->action = desc->callback; - } - if (system->ctx_free) { - if (system->ctx && system->ctx != desc->ctx) { - system->ctx_free(system->ctx); - } - } - if (system->binding_ctx_free) { - if (system->binding_ctx && system->binding_ctx != desc->binding_ctx) { - system->binding_ctx_free(system->binding_ctx); + const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return false; } - } - if (desc->ctx) { - system->ctx = desc->ctx; - } - if (desc->binding_ctx) { - system->binding_ctx = desc->binding_ctx; + it->trs[term->field_index] = tr; } - if (desc->ctx_free) { - system->ctx_free = desc->ctx_free; - } - if (desc->binding_ctx_free) { - system->binding_ctx_free = desc->binding_ctx_free; - } - if (desc->query.filter.instanced) { - ECS_BIT_SET(system->query->filter.flags, EcsFilterIsInstanced); - } - if (desc->multi_threaded) { - system->multi_threaded = desc->multi_threaded; - } - if (desc->no_readonly) { - system->no_readonly = desc->no_readonly; + + it->entities = ecs_table_entities(table); + if (it->entities) { + it->entities = &it->entities[it->offset]; } - flecs_system_init_timer(world, entity, desc); + return true; } - - ecs_poly_modified(world, entity, ecs_system_t); - - return entity; -error: - return 0; -} - -void FlecsSystemImport( - ecs_world_t *world) -{ - ECS_MODULE(world, FlecsSystem); - - ecs_set_name_prefix(world, "Ecs"); - - flecs_bootstrap_tag(world, EcsSystem); - flecs_bootstrap_component(world, EcsTickSource); - - /* Make sure to never inherit system component. This makes sure that any - * term created for the System component will default to 'self' traversal, - * which improves efficiency of the query. */ - ecs_add_id(world, EcsSystem, EcsDontInherit); } -#endif - diff --git a/vendors/flecs/distr/flecs.h b/vendors/flecs/distr/flecs.h new file mode 100644 index 000000000..1dfa1de15 --- /dev/null +++ b/vendors/flecs/distr/flecs.h @@ -0,0 +1,33034 @@ +// Comment out this line when using as DLL +#define flecs_STATIC +/** + * @file flecs.h + * @brief Flecs public API. + * + * This file contains the public API for Flecs. + */ + +#ifndef FLECS_H +#define FLECS_H + +/** + * @defgroup c C API + * + * @{ + * @} + */ + +/** + * @defgroup core Core + * @ingroup c + * Core ECS functionality (entities, storage, queries). + * + * @{ + */ + +/** + * @defgroup options API defines + * Defines for customizing compile time features. + * + * @{ + */ + +/* Flecs version macros */ +#define FLECS_VERSION_MAJOR 4 /**< Flecs major version. */ +#define FLECS_VERSION_MINOR 0 /**< Flecs minor version. */ +#define FLECS_VERSION_PATCH 3 /**< Flecs patch version. */ + +/** Flecs version. */ +#define FLECS_VERSION FLECS_VERSION_IMPL(\ + FLECS_VERSION_MAJOR, FLECS_VERSION_MINOR, FLECS_VERSION_PATCH) + +/** @def FLECS_CONFIG_HEADER + * Allows for including a user-customizable header that specifies compile-time + * features. */ +#ifdef FLECS_CONFIG_HEADER +#include "flecs_config.h" +#endif + +/** @def ecs_float_t + * Customizable precision for floating point operations */ +#ifndef ecs_float_t +#define ecs_float_t float +#endif + +/** @def ecs_ftime_t + * Customizable precision for scalar time values. Change to double precision for + * processes that can run for a long time (e.g. longer than a day). */ +#ifndef ecs_ftime_t +#define ecs_ftime_t ecs_float_t +#endif + +/** @def FLECS_LEGACY + * Define when building for C89 + */ +// #define FLECS_LEGACY + +/** @def FLECS_ACCURATE_COUNTERS + * Define to ensure that global counters used for statistics (such as the + * allocation counters in the OS API) are accurate in multithreaded + * applications, at the cost of increased overhead. + */ +// #define FLECS_ACCURATE_COUNTERS + +/** @def FLECS_DISABLE_COUNTERS + * Disables counters used for statistics. Improves performance, but + * will prevent some features that rely on statistics from working, + * like the statistics pages in the explorer. + */ +// #define FLECS_DISABLE_COUNTERS + +/* Make sure provided configuration is valid */ +#if defined(FLECS_DEBUG) && defined(FLECS_NDEBUG) +#error "invalid configuration: cannot both define FLECS_DEBUG and FLECS_NDEBUG" +#endif +#if defined(FLECS_DEBUG) && defined(NDEBUG) +#error "invalid configuration: cannot both define FLECS_DEBUG and NDEBUG" +#endif + +/** @def FLECS_DEBUG + * Used for input parameter checking and cheap sanity checks. There are lots of + * asserts in every part of the code, so this will slow down applications. + */ +#if !defined(FLECS_DEBUG) && !defined(FLECS_NDEBUG) +#if defined(NDEBUG) +#define FLECS_NDEBUG +#else +#define FLECS_DEBUG +#endif +#endif + +/** @def FLECS_SANITIZE + * Enables expensive checks that can detect issues early. Recommended for + * running tests or when debugging issues. This will severely slow down code. + */ +#ifdef FLECS_SANITIZE +#ifndef FLECS_DEBUG +#define FLECS_DEBUG /* If sanitized mode is enabled, so is debug mode */ +#endif +#endif + +/* Tip: if you see weird behavior that you think might be a bug, make sure to + * test with the FLECS_DEBUG or FLECS_SANITIZE flags enabled. There's a good + * chance that this gives you more information about the issue! */ + +/** @def FLECS_SOFT_ASSERT + * Define to not abort for recoverable errors, like invalid parameters. An error + * is still thrown to the console. This is recommended for when running inside a + * third party runtime, such as the Unreal editor. + * + * Note that internal sanity checks (ECS_INTERNAL_ERROR) will still abort a + * process, as this gives more information than a (likely) subsequent crash. + * + * When a soft assert occurs, the code will attempt to minimize the number of + * side effects of the failed operation, but this may not always be possible. + * Even though an application may still be able to continue running after a soft + * assert, it should be treated as if in an undefined state. + */ +// #define FLECS_SOFT_ASSERT + +/** @def FLECS_KEEP_ASSERT + * By default asserts are disabled in release mode, when either FLECS_NDEBUG or + * NDEBUG is defined. Defining FLECS_KEEP_ASSERT ensures that asserts are not + * disabled. This define can be combined with FLECS_SOFT_ASSERT. + */ +// #define FLECS_KEEP_ASSERT + +/** \def FLECS_CPP_NO_AUTO_REGISTRATION + * When set, the C++ API will require that components are registered before they + * are used. This is useful in multithreaded applications, where components need + * to be registered beforehand, and to catch issues in projects where component + * registration is mandatory. Disabling automatic component registration also + * slightly improves performance. + * The C API is not affected by this feature. + */ +// #define FLECS_CPP_NO_AUTO_REGISTRATION + +/** \def FLECS_CUSTOM_BUILD + * This macro lets you customize which addons to build flecs with. + * Without any addons Flecs is just a minimal ECS storage, but addons add + * features such as systems, scheduling and reflection. If an addon is disabled, + * it is excluded from the build, so that it consumes no resources. By default + * all addons are enabled. + * + * You can customize a build by either whitelisting or blacklisting addons. To + * whitelist addons, first define the FLECS_CUSTOM_BUILD macro, which disables + * all addons. You can then manually select the addons you need by defining + * their macro, like "FLECS_SYSTEM". + * + * To blacklist an addon, make sure to *not* define FLECS_CUSTOM_BUILD, and + * instead define the addons you don't need by defining FLECS_NO_, for + * example "FLECS_NO_SYSTEM". If there are any addons that depend on the + * blacklisted addon, an error will be thrown during the build. + * + * Note that addons can have dependencies on each other. Addons will + * automatically enable their dependencies. To see the list of addons that was + * compiled in a build, enable tracing before creating the world by doing: + * + * @code + * ecs_log_set_level(0); + * @endcode + * + * which outputs the full list of addons Flecs was compiled with. + */ +// #define FLECS_CUSTOM_BUILD + +/** @def FLECS_CPP_NO_AUTO_REGISTRATION + * When set, the C++ API will require that components are registered before they + * are used. This is useful in multithreaded applications, where components need + * to be registered beforehand, and to catch issues in projects where component + * registration is mandatory. Disabling automatic component registration also + * slightly improves performance. + * The C API is not affected by this feature. + */ +// #define FLECS_CPP_NO_AUTO_REGISTRATION + +#ifndef FLECS_CUSTOM_BUILD +// #define FLECS_C /**< C API convenience macros, always enabled */ +#define FLECS_CPP /**< C++ API */ +#define FLECS_MODULE /**< Module support */ +#define FLECS_SCRIPT /**< ECS data definition format */ +#define FLECS_STATS /**< Track runtime statistics */ +#define FLECS_METRICS /**< Expose component data as statistics */ +#define FLECS_ALERTS /**< Monitor conditions for errors */ +#define FLECS_SYSTEM /**< System support */ +#define FLECS_PIPELINE /**< Pipeline support */ +#define FLECS_TIMER /**< Timer support */ +#define FLECS_META /**< Reflection support */ +#define FLECS_UNITS /**< Builtin standard units */ +#define FLECS_JSON /**< Parsing JSON to/from component values */ +#define FLECS_DOC /**< Document entities & components */ +#define FLECS_LOG /**< When enabled ECS provides more detailed logs */ +#define FLECS_APP /**< Application addon */ +#define FLECS_OS_API_IMPL /**< Default implementation for OS API */ +#define FLECS_HTTP /**< Tiny HTTP server for connecting to remote UI */ +#define FLECS_REST /**< REST API for querying application data */ +// #define FLECS_JOURNAL /**< Journaling addon (disabled by default) */ +// #define FLECS_PERF_TRACE /**< Enable performance tracing (disabled by default) */ +#endif // ifndef FLECS_CUSTOM_BUILD + +/** @def FLECS_LOW_FOOTPRINT + * Set a number of constants to values that decrease memory footprint, at the + * cost of decreased performance. */ +// #define FLECS_LOW_FOOTPRINT +#ifdef FLECS_LOW_FOOTPRINT +#define FLECS_HI_COMPONENT_ID (16) +#define FLECS_HI_ID_RECORD_ID (16) +#define FLECS_SPARSE_PAGE_BITS (6) +#define FLECS_ENTITY_PAGE_BITS (6) +#define FLECS_USE_OS_ALLOC +#endif + +/** @def FLECS_HI_COMPONENT_ID + * This constant can be used to balance between performance and memory + * utilization. The constant is used in two ways: + * - Entity ids 0..FLECS_HI_COMPONENT_ID are reserved for component ids. + * - Used as lookup array size in table edges. + * + * Increasing this value increases the size of the lookup array, which allows + * fast table traversal, which improves performance of ECS add/remove + * operations. Component ids that fall outside of this range use a regular map + * lookup, which is slower but more memory efficient. */ +#ifndef FLECS_HI_COMPONENT_ID +#define FLECS_HI_COMPONENT_ID (256) +#endif + +/** @def FLECS_HI_ID_RECORD_ID + * This constant can be used to balance between performance and memory + * utilization. The constant is used to determine the size of the id record + * lookup array. Id values that fall outside of this range use a regular map + * lookup, which is slower but more memory efficient. + */ +#ifndef FLECS_HI_ID_RECORD_ID +#define FLECS_HI_ID_RECORD_ID (1024) +#endif + +/** @def FLECS_SPARSE_PAGE_BITS + * This constant is used to determine the number of bits of an id that is used + * to determine the page index when used with a sparse set. The number of bits + * determines the page size, which is (1 << bits). + * Lower values decrease memory utilization, at the cost of more allocations. */ +#ifndef FLECS_SPARSE_PAGE_BITS +#define FLECS_SPARSE_PAGE_BITS (12) +#endif + +/** @def FLECS_ENTITY_PAGE_BITS + * Same as FLECS_SPARSE_PAGE_BITS, but for the entity index. */ +#ifndef FLECS_ENTITY_PAGE_BITS +#define FLECS_ENTITY_PAGE_BITS (12) +#endif + +/** @def FLECS_USE_OS_ALLOC + * When enabled, Flecs will use the OS allocator provided in the OS API directly + * instead of the builtin block allocator. This can decrease memory utilization + * as memory will be freed more often, at the cost of decreased performance. */ +// #define FLECS_USE_OS_ALLOC + +/** @def FLECS_ID_DESC_MAX + * Maximum number of ids to add ecs_entity_desc_t / ecs_bulk_desc_t */ +#ifndef FLECS_ID_DESC_MAX +#define FLECS_ID_DESC_MAX (32) +#endif + +/** \def FLECS_EVENT_DESC_MAX + * Maximum number of events in ecs_observer_desc_t */ +#ifndef FLECS_EVENT_DESC_MAX +#define FLECS_EVENT_DESC_MAX (8) +#endif + +/** @def FLECS_VARIABLE_COUNT_MAX + * Maximum number of query variables per query */ +#define FLECS_VARIABLE_COUNT_MAX (64) + +/** \def FLECS_TERM_COUNT_MAX + * Maximum number of terms in queries. Should not exceed 64. */ +#ifndef FLECS_TERM_COUNT_MAX +#define FLECS_TERM_COUNT_MAX 32 +#endif + +/** \def FLECS_TERM_ARG_COUNT_MAX + * Maximum number of arguments for a term. */ +#ifndef FLECS_TERM_ARG_COUNT_MAX +#define FLECS_TERM_ARG_COUNT_MAX (16) +#endif + +/** \def FLECS_QUERY_VARIABLE_COUNT_MAX + * Maximum number of query variables per query. Should not exceed 128. */ +#ifndef FLECS_QUERY_VARIABLE_COUNT_MAX +#define FLECS_QUERY_VARIABLE_COUNT_MAX (64) +#endif + +/** @def FLECS_QUERY_SCOPE_NESTING_MAX + * Maximum nesting depth of query scopes */ +#ifndef FLECS_QUERY_SCOPE_NESTING_MAX +#define FLECS_QUERY_SCOPE_NESTING_MAX (8) +#endif + +/** @} */ + +/** + * @file api_defines.h + * @brief Supporting defines for the public API. + * + * This file contains constants / macros that are typically not used by an + * application but support the public API, and therefore must be exposed. This + * header should not be included by itself. + */ + +#ifndef FLECS_API_DEFINES_H +#define FLECS_API_DEFINES_H + +/** + * @file api_flags.h + * @brief Bitset flags used by internals. + */ + +#ifndef FLECS_API_FLAGS_H +#define FLECS_API_FLAGS_H + +#ifdef __cplusplus +extern "C" { +#endif + + +//////////////////////////////////////////////////////////////////////////////// +//// World flags +//////////////////////////////////////////////////////////////////////////////// + +#define EcsWorldQuitWorkers (1u << 0) +#define EcsWorldReadonly (1u << 1) +#define EcsWorldInit (1u << 2) +#define EcsWorldQuit (1u << 3) +#define EcsWorldFini (1u << 4) +#define EcsWorldMeasureFrameTime (1u << 5) +#define EcsWorldMeasureSystemTime (1u << 6) +#define EcsWorldMultiThreaded (1u << 7) +#define EcsWorldFrameInProgress (1u << 8) + +//////////////////////////////////////////////////////////////////////////////// +//// OS API flags +//////////////////////////////////////////////////////////////////////////////// + +#define EcsOsApiHighResolutionTimer (1u << 0) +#define EcsOsApiLogWithColors (1u << 1) +#define EcsOsApiLogWithTimeStamp (1u << 2) +#define EcsOsApiLogWithTimeDelta (1u << 3) + + +//////////////////////////////////////////////////////////////////////////////// +//// Entity flags (set in upper bits of ecs_record_t::row) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsEntityIsId (1u << 31) +#define EcsEntityIsTarget (1u << 30) +#define EcsEntityIsTraversable (1u << 29) + + +//////////////////////////////////////////////////////////////////////////////// +//// Id flags (used by ecs_id_record_t::flags) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsIdOnDeleteRemove (1u << 0) +#define EcsIdOnDeleteDelete (1u << 1) +#define EcsIdOnDeletePanic (1u << 2) +#define EcsIdOnDeleteMask\ + (EcsIdOnDeletePanic|EcsIdOnDeleteRemove|EcsIdOnDeleteDelete) + +#define EcsIdOnDeleteObjectRemove (1u << 3) +#define EcsIdOnDeleteObjectDelete (1u << 4) +#define EcsIdOnDeleteObjectPanic (1u << 5) +#define EcsIdOnDeleteObjectMask\ + (EcsIdOnDeleteObjectPanic|EcsIdOnDeleteObjectRemove|\ + EcsIdOnDeleteObjectDelete) + +#define EcsIdOnInstantiateOverride (1u << 6) +#define EcsIdOnInstantiateInherit (1u << 7) +#define EcsIdOnInstantiateDontInherit (1u << 8) +#define EcsIdOnInstantiateMask\ + (EcsIdOnInstantiateOverride|EcsIdOnInstantiateInherit|\ + EcsIdOnInstantiateDontInherit) + +#define EcsIdExclusive (1u << 9) +#define EcsIdTraversable (1u << 10) +#define EcsIdTag (1u << 11) +#define EcsIdWith (1u << 12) +#define EcsIdCanToggle (1u << 13) +#define EcsIdIsTransitive (1u << 14) + +#define EcsIdHasOnAdd (1u << 16) /* Same values as table flags */ +#define EcsIdHasOnRemove (1u << 17) +#define EcsIdHasOnSet (1u << 18) +#define EcsIdHasOnTableFill (1u << 19) +#define EcsIdHasOnTableEmpty (1u << 20) +#define EcsIdHasOnTableCreate (1u << 21) +#define EcsIdHasOnTableDelete (1u << 22) +#define EcsIdIsSparse (1u << 23) +#define EcsIdIsUnion (1u << 24) +#define EcsIdEventMask\ + (EcsIdHasOnAdd|EcsIdHasOnRemove|EcsIdHasOnSet|\ + EcsIdHasOnTableFill|EcsIdHasOnTableEmpty|EcsIdHasOnTableCreate|\ + EcsIdHasOnTableDelete|EcsIdIsSparse|EcsIdIsUnion) + +#define EcsIdMarkedForDelete (1u << 30) + +/* Utilities for converting from flags to delete policies and vice versa */ +#define ECS_ID_ON_DELETE(flags) \ + ((ecs_entity_t[]){0, EcsRemove, EcsDelete, 0, EcsPanic}\ + [((flags) & EcsIdOnDeleteMask)]) +#define ECS_ID_ON_DELETE_TARGET(flags) ECS_ID_ON_DELETE(flags >> 3) +#define ECS_ID_ON_DELETE_FLAG(id) (1u << ((id) - EcsRemove)) +#define ECS_ID_ON_DELETE_TARGET_FLAG(id) (1u << (3 + ((id) - EcsRemove))) + +/* Utilities for converting from flags to instantiate policies and vice versa */ +#define ECS_ID_ON_INSTANTIATE(flags) \ + ((ecs_entity_t[]){EcsOverride, EcsOverride, EcsInherit, 0, EcsDontInherit}\ + [(((flags) & EcsIdOnInstantiateMask) >> 6)]) +#define ECS_ID_ON_INSTANTIATE_FLAG(id) (1u << (6 + ((id) - EcsOverride))) + + +//////////////////////////////////////////////////////////////////////////////// +//// Iterator flags (used by ecs_iter_t::flags) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsIterIsValid (1u << 0u) /* Does iterator contain valid result */ +#define EcsIterNoData (1u << 1u) /* Does iterator provide (component) data */ +#define EcsIterNoResults (1u << 3u) /* Iterator has no results */ +#define EcsIterIgnoreThis (1u << 4u) /* Only evaluate non-this terms */ +#define EcsIterHasCondSet (1u << 6u) /* Does iterator have conditionally set fields */ +#define EcsIterProfile (1u << 7u) /* Profile iterator performance */ +#define EcsIterTrivialSearch (1u << 8u) /* Trivial iterator mode */ +#define EcsIterTrivialTest (1u << 11u) /* Trivial test mode (constrained $this) */ +#define EcsIterTrivialCached (1u << 14u) /* Trivial search for cached query */ +#define EcsIterCacheSearch (1u << 15u) /* Cache search */ +#define EcsIterFixedInChangeComputed (1u << 16u) /* Change detection for fixed in terms is done */ +#define EcsIterFixedInChanged (1u << 17u) /* Fixed in terms changed */ +#define EcsIterSkip (1u << 18u) /* Result was skipped for change detection */ +#define EcsIterCppEach (1u << 19u) /* Uses C++ 'each' iterator */ + +/* Same as event flags */ +#define EcsIterTableOnly (1u << 20u) /* Result only populates table */ + + +//////////////////////////////////////////////////////////////////////////////// +//// Event flags (used by ecs_event_decs_t::flags) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsEventTableOnly (1u << 20u) /* Table event (no data, same as iter flags) */ +#define EcsEventNoOnSet (1u << 16u) /* Don't emit OnSet for inherited ids */ + + +//////////////////////////////////////////////////////////////////////////////// +//// Query flags (used by ecs_query_t::flags) +//////////////////////////////////////////////////////////////////////////////// + +/* Flags that can only be set by the query implementation */ +#define EcsQueryMatchThis (1u << 11u) /* Query has terms with $this source */ +#define EcsQueryMatchOnlyThis (1u << 12u) /* Query only has terms with $this source */ +#define EcsQueryMatchOnlySelf (1u << 13u) /* Query has no terms with up traversal */ +#define EcsQueryMatchWildcards (1u << 14u) /* Query matches wildcards */ +#define EcsQueryMatchNothing (1u << 15u) /* Query matches nothing */ +#define EcsQueryHasCondSet (1u << 16u) /* Query has conditionally set fields */ +#define EcsQueryHasPred (1u << 17u) /* Query has equality predicates */ +#define EcsQueryHasScopes (1u << 18u) /* Query has query scopes */ +#define EcsQueryHasRefs (1u << 19u) /* Query has terms with static source */ +#define EcsQueryHasOutTerms (1u << 20u) /* Query has [out] terms */ +#define EcsQueryHasNonThisOutTerms (1u << 21u) /* Query has [out] terms with no $this source */ +#define EcsQueryHasMonitor (1u << 22u) /* Query has monitor for change detection */ +#define EcsQueryIsTrivial (1u << 23u) /* Query can use trivial evaluation function */ +#define EcsQueryHasCacheable (1u << 24u) /* Query has cacheable terms */ +#define EcsQueryIsCacheable (1u << 25u) /* All terms of query are cacheable */ +#define EcsQueryHasTableThisVar (1u << 26u) /* Does query have $this table var */ +#define EcsQueryCacheYieldEmptyTables (1u << 27u) /* Does query cache empty tables */ +#define EcsQueryNested (1u << 28u) /* Query created by a query (for observer, cache) */ + +//////////////////////////////////////////////////////////////////////////////// +//// Term flags (used by ecs_term_t::flags_) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsTermMatchAny (1u << 0) +#define EcsTermMatchAnySrc (1u << 1) +#define EcsTermTransitive (1u << 2) +#define EcsTermReflexive (1u << 3) +#define EcsTermIdInherited (1u << 4) +#define EcsTermIsTrivial (1u << 5) +#define EcsTermIsCacheable (1u << 7) +#define EcsTermIsScope (1u << 8) +#define EcsTermIsMember (1u << 9) +#define EcsTermIsToggle (1u << 10) +#define EcsTermKeepAlive (1u << 11) +#define EcsTermIsSparse (1u << 12) +#define EcsTermIsUnion (1u << 13) +#define EcsTermIsOr (1u << 14) + + +//////////////////////////////////////////////////////////////////////////////// +//// Observer flags (used by ecs_observer_t::flags) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsObserverIsMulti (1u << 1u) /* Does observer have multiple terms */ +#define EcsObserverIsMonitor (1u << 2u) /* Is observer a monitor */ +#define EcsObserverIsDisabled (1u << 3u) /* Is observer entity disabled */ +#define EcsObserverIsParentDisabled (1u << 4u) /* Is module parent of observer disabled */ +#define EcsObserverBypassQuery (1u << 5u) /* Don't evaluate query for multi-component observer*/ +#define EcsObserverYieldOnCreate (1u << 6u) /* Yield matching entities when creating observer */ +#define EcsObserverYieldOnDelete (1u << 7u) /* Yield matching entities when deleting observer */ + + +//////////////////////////////////////////////////////////////////////////////// +//// Table flags (used by ecs_table_t::flags) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsTableHasBuiltins (1u << 1u) /* Does table have builtin components */ +#define EcsTableIsPrefab (1u << 2u) /* Does the table store prefabs */ +#define EcsTableHasIsA (1u << 3u) /* Does the table have IsA relationship */ +#define EcsTableHasChildOf (1u << 4u) /* Does the table type ChildOf relationship */ +#define EcsTableHasName (1u << 5u) /* Does the table type have (Identifier, Name) */ +#define EcsTableHasPairs (1u << 6u) /* Does the table type have pairs */ +#define EcsTableHasModule (1u << 7u) /* Does the table have module data */ +#define EcsTableIsDisabled (1u << 8u) /* Does the table type has EcsDisabled */ +#define EcsTableNotQueryable (1u << 9u) /* Table should never be returned by queries */ +#define EcsTableHasCtors (1u << 10u) +#define EcsTableHasDtors (1u << 11u) +#define EcsTableHasCopy (1u << 12u) +#define EcsTableHasMove (1u << 13u) +#define EcsTableHasToggle (1u << 14u) +#define EcsTableHasOverrides (1u << 15u) + +#define EcsTableHasOnAdd (1u << 16u) /* Same values as id flags */ +#define EcsTableHasOnRemove (1u << 17u) +#define EcsTableHasOnSet (1u << 18u) +#define EcsTableHasOnTableFill (1u << 19u) +#define EcsTableHasOnTableEmpty (1u << 20u) +#define EcsTableHasOnTableCreate (1u << 21u) +#define EcsTableHasOnTableDelete (1u << 22u) +#define EcsTableHasSparse (1u << 23u) +#define EcsTableHasUnion (1u << 24u) + +#define EcsTableHasTraversable (1u << 26u) +#define EcsTableMarkedForDelete (1u << 30u) + +/* Composite table flags */ +#define EcsTableHasLifecycle (EcsTableHasCtors | EcsTableHasDtors) +#define EcsTableIsComplex (EcsTableHasLifecycle | EcsTableHasToggle | EcsTableHasSparse) +#define EcsTableHasAddActions (EcsTableHasIsA | EcsTableHasCtors | EcsTableHasOnAdd | EcsTableHasOnSet) +#define EcsTableHasRemoveActions (EcsTableHasIsA | EcsTableHasDtors | EcsTableHasOnRemove) +#define EcsTableEdgeFlags (EcsTableHasOnAdd | EcsTableHasOnRemove | EcsTableHasSparse | EcsTableHasUnion) +#define EcsTableAddEdgeFlags (EcsTableHasOnAdd | EcsTableHasSparse | EcsTableHasUnion) +#define EcsTableRemoveEdgeFlags (EcsTableHasOnRemove | EcsTableHasSparse | EcsTableHasUnion) + +//////////////////////////////////////////////////////////////////////////////// +//// Aperiodic action flags (used by ecs_run_aperiodic) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsAperiodicEmptyTables (1u << 1u) /* Process pending empty table events */ +#define EcsAperiodicComponentMonitors (1u << 2u) /* Process component monitors */ +#define EcsAperiodicEmptyQueries (1u << 4u) /* Process empty queries */ + +#ifdef __cplusplus +} +#endif + +#endif + + +#if defined(_WIN32) || defined(_MSC_VER) +#define ECS_TARGET_WINDOWS +#elif defined(__ANDROID__) +#define ECS_TARGET_ANDROID +#define ECS_TARGET_POSIX +#elif defined(__linux__) +#define ECS_TARGET_LINUX +#define ECS_TARGET_POSIX +#elif defined(__FreeBSD__) +#define ECS_TARGET_FREEBSD +#define ECS_TARGET_POSIX +#elif defined(__APPLE__) && defined(__MACH__) +#define ECS_TARGET_DARWIN +#define ECS_TARGET_POSIX +#elif defined(__EMSCRIPTEN__) +#define ECS_TARGET_EM +#define ECS_TARGET_POSIX +#endif + +#if defined(__MINGW32__) || defined(__MINGW64__) +#define ECS_TARGET_MINGW +#endif + +#if defined(_MSC_VER) +#ifndef __clang__ +#define ECS_TARGET_MSVC +#endif +#endif + +#if defined(__clang__) +#define ECS_TARGET_CLANG +#endif + +#if defined(__GNUC__) +#define ECS_TARGET_GNU +#endif + +/* Map between clang and apple clang versions, as version 13 has a difference in + * the format of __PRETTY_FUNCTION__ which enum reflection depends on. */ +#if defined(__clang__) + #if defined(__APPLE__) + #if __clang_major__ == 13 + #if __clang_minor__ < 1 + #define ECS_CLANG_VERSION 12 + #else + #define ECS_CLANG_VERSION 13 + #endif + #else + #define ECS_CLANG_VERSION __clang_major__ + #endif + #else + #define ECS_CLANG_VERSION __clang_major__ + #endif +#endif + +/* Ignored warnings */ +#if defined(ECS_TARGET_CLANG) +/* Ignore unknown options so we don't have to care about the compiler version */ +#pragma clang diagnostic ignored "-Wunknown-warning-option" +/* Warns for double or redundant semicolons. There are legitimate cases where a + * semicolon after an empty statement is useful, for example after a macro that + * is replaced with a code block. With this warning enabled, semicolons would + * only have to be added after macro's that are not code blocks, which in some + * cases isn't possible as the implementation of a macro can be different in + * debug/release mode. */ +#pragma clang diagnostic ignored "-Wextra-semi-stmt" +/* This is valid in C99, and Flecs must be compiled as C99. */ +#pragma clang diagnostic ignored "-Wdeclaration-after-statement" +/* Clang attribute to detect fallthrough isn't supported on older versions. + * Implicit fallthrough is still detected by gcc and ignored with "fall through" + * comments */ +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +/* This warning prevents adding a default case when all enum constants are part + * of the switch. In C however an enum type can assume any value in the range of + * the type, and this warning makes it harder to catch invalid enum values. */ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +/* This warning prevents some casts of function results to a different kind of + * type, e.g. casting an int result to double. Not very useful in practice, as + * it just forces the code to assign to a variable first, then cast. */ +#pragma clang diagnostic ignored "-Wbad-function-cast" +/* Format strings can be passed down from other functions. */ +#pragma clang diagnostic ignored "-Wformat-nonliteral" +/* Useful, but not reliable enough. It can incorrectly flag macro's as unused + * in standalone builds. */ +#pragma clang diagnostic ignored "-Wunused-macros" +#if __clang_major__ == 13 +/* clang 13 can throw this warning for a define in ctype.h */ +#pragma clang diagnostic ignored "-Wreserved-identifier" +#endif +/* Filenames aren't consistent across targets as they can use different casing + * (e.g. WinSock2 vs winsock2). */ +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" +/* Very difficult to workaround this warning in C, especially for an ECS. */ +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +/* This warning gets thrown when trying to cast pointer returned from dlproc */ +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +/* This warning can get thrown for expressions that evaluate to constants + * in debug/release mode. */ +#pragma clang diagnostic ignored "-Wconstant-logical-operand" +/* With soft asserts enabled the code won't abort, which in some cases means + * code paths are reached where values are uninitialized. */ +#ifdef FLECS_SOFT_ASSERT +#pragma clang diagnostic ignored "-Wsometimes-uninitialized" +#endif + +/* Allows for enum reflection support on legacy compilers */ +#if __clang_major__ < 16 +#pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +#endif + +#elif defined(ECS_TARGET_GNU) +#ifndef __cplusplus +#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" +#pragma GCC diagnostic ignored "-Wbad-function-cast" +#endif +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#pragma GCC diagnostic ignored "-Wunused-macros" +/* This warning gets thrown *sometimes* when not all members for a struct are + * provided in an initializer. Flecs heavily relies on descriptor structs that + * only require partly initialization, so this warning isn't useful. + * It doesn't introduce any safety issues (fields are guaranteed to be 0 + * initialized), and later versions of gcc (>=11) seem to no longer throw this + * warning. */ +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +/* Produces false positives in addons/cpp/delegate.hpp. */ +#pragma GCC diagnostic ignored "-Warray-bounds" +/* Produces false positives in queries/src/cache.c */ +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#pragma GCC diagnostic ignored "-Wrestrict" + +#elif defined(ECS_TARGET_MSVC) +/* recursive on all control paths, function will cause runtime stack overflow + * This warning is incorrectly thrown on enum reflection code. */ +#pragma warning(disable: 4717) +#endif + +/* Allows for enum reflection support on legacy compilers */ +#if defined(__GNUC__) && __GNUC__ <= 10 +#pragma GCC diagnostic ignored "-Wconversion" +#endif + +/* Standard library dependencies */ +#include +#include +#include + +/* Non-standard but required. If not provided by platform, add manually. */ +#include + +/* Contains macros for importing / exporting symbols */ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef FLECS_BAKE_CONFIG_H +#define FLECS_BAKE_CONFIG_H + +/* Headers of public dependencies */ +/* No dependencies */ + +/* Convenience macro for exporting symbols */ +#ifndef flecs_STATIC +#if defined(flecs_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) + #define FLECS_API __declspec(dllexport) +#elif defined(flecs_EXPORTS) + #define FLECS_API __attribute__((__visibility__("default"))) +#elif defined(_MSC_VER) + #define FLECS_API __declspec(dllimport) +#else + #define FLECS_API +#endif +#else + #define FLECS_API +#endif + +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __BAKE_LEGACY__ +#define FLECS_LEGACY +#endif + +/* Some symbols are only exported when building in debug build, to enable + * white-box testing of internal data structures */ +#ifndef FLECS_NDEBUG +#define FLECS_DBG_API FLECS_API +#else +#define FLECS_DBG_API +#endif + + +//////////////////////////////////////////////////////////////////////////////// +//// Language support defines +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FLECS_LEGACY +#include +#endif + +#ifndef NULL +#define NULL ((void*)0) +#endif + +/* The API uses the native bool type in C++, or a custom one in C */ +#if !defined(__cplusplus) && !defined(__bool_true_false_are_defined) +#undef bool +#undef true +#undef false +typedef char bool; +#define false 0 +#define true !false +#endif + +/* Utility types to indicate usage as bitmask */ +typedef uint8_t ecs_flags8_t; +typedef uint16_t ecs_flags16_t; +typedef uint32_t ecs_flags32_t; +typedef uint64_t ecs_flags64_t; + +/* Bitmask type with compile-time defined size */ +#define ecs_flagsn_t_(bits) ecs_flags##bits##_t +#define ecs_flagsn_t(bits) ecs_flagsn_t_(bits) + +/* Bitset type that can store exactly as many bits as there are terms */ +#define ecs_termset_t ecs_flagsn_t(FLECS_TERM_COUNT_MAX) + +/* Utility macro's for setting/clearing termset bits */ +#define ECS_TERMSET_SET(set, flag) ((set) |= (ecs_termset_t)(flag)) +#define ECS_TERMSET_CLEAR(set, flag) ((set) &= (ecs_termset_t)~(flag)) +#define ECS_TERMSET_COND(set, flag, cond) ((cond) \ + ? (ECS_TERMSET_SET(set, flag)) \ + : (ECS_TERMSET_CLEAR(set, flag))) + +/* Keep unsigned integers out of the codebase as they do more harm than good */ +typedef int32_t ecs_size_t; + +/* Allocator type */ +typedef struct ecs_allocator_t ecs_allocator_t; + +#define ECS_SIZEOF(T) ECS_CAST(ecs_size_t, sizeof(T)) + +/* Use alignof in C++, or a trick in C. */ +#ifdef __cplusplus +#define ECS_ALIGNOF(T) static_cast(alignof(T)) +#elif defined(ECS_TARGET_MSVC) +#define ECS_ALIGNOF(T) (int64_t)__alignof(T) +#elif defined(ECS_TARGET_GNU) +#define ECS_ALIGNOF(T) (int64_t)__alignof__(T) +#elif defined(ECS_TARGET_CLANG) +#define ECS_ALIGNOF(T) (int64_t)__alignof__(T) +#else +#define ECS_ALIGNOF(T) ((int64_t)&((struct { char c; T d; } *)0)->d) +#endif + +#ifndef FLECS_NO_DEPRECATED_WARNINGS +#if defined(ECS_TARGET_GNU) +#define ECS_DEPRECATED(msg) __attribute__((deprecated(msg))) +#elif defined(ECS_TARGET_MSVC) +#define ECS_DEPRECATED(msg) __declspec(deprecated(msg)) +#else +#define ECS_DEPRECATED(msg) +#endif +#else +#define ECS_DEPRECATED(msg) +#endif + +#define ECS_ALIGN(size, alignment) (ecs_size_t)((((((size_t)size) - 1) / ((size_t)alignment)) + 1) * ((size_t)alignment)) + +/* Simple utility for determining the max of two values */ +#define ECS_MAX(a, b) (((a) > (b)) ? a : b) +#define ECS_MIN(a, b) (((a) < (b)) ? a : b) + +/* Abstraction on top of C-style casts so that C functions can be used in C++ + * code without producing warnings */ +#ifndef __cplusplus +#define ECS_CAST(T, V) ((T)(V)) +#else +#define ECS_CAST(T, V) (static_cast(V)) +#endif + +/* Utility macro for doing const casts without warnings */ +#ifndef __cplusplus +#define ECS_CONST_CAST(type, value) ((type)(uintptr_t)(value)) +#else +#define ECS_CONST_CAST(type, value) (const_cast(value)) +#endif + +/* Utility macro for doing pointer casts without warnings */ +#ifndef __cplusplus +#define ECS_PTR_CAST(type, value) ((type)(uintptr_t)(value)) +#else +#define ECS_PTR_CAST(type, value) (reinterpret_cast(value)) +#endif + +/* Utility macro's to do bitwise comparisons between floats without warnings */ +#define ECS_EQ(a, b) (ecs_os_memcmp(&(a), &(b), sizeof(a)) == 0) +#define ECS_NEQ(a, b) (!ECS_EQ(a, b)) +#define ECS_EQZERO(a) ECS_EQ(a, (uint64_t){0}) +#define ECS_NEQZERO(a) ECS_NEQ(a, (uint64_t){0}) + +/* Utilities to convert flecs version to string */ +#define FLECS_VERSION_IMPLSTR(major, minor, patch) #major "." #minor "." #patch +#define FLECS_VERSION_IMPL(major, minor, patch) \ + FLECS_VERSION_IMPLSTR(major, minor, patch) + +#define ECS_CONCAT(a, b) a ## b + +//////////////////////////////////////////////////////////////////////////////// +//// Magic numbers for sanity checking +//////////////////////////////////////////////////////////////////////////////// + +/* Magic number to identify the type of the object */ +#define ecs_world_t_magic (0x65637377) +#define ecs_stage_t_magic (0x65637373) +#define ecs_query_t_magic (0x65637375) +#define ecs_observer_t_magic (0x65637362) + + +//////////////////////////////////////////////////////////////////////////////// +//// Entity id macros +//////////////////////////////////////////////////////////////////////////////// + +#define ECS_ROW_MASK (0x0FFFFFFFu) +#define ECS_ROW_FLAGS_MASK (~ECS_ROW_MASK) +#define ECS_RECORD_TO_ROW(v) (ECS_CAST(int32_t, (ECS_CAST(uint32_t, v) & ECS_ROW_MASK))) +#define ECS_RECORD_TO_ROW_FLAGS(v) (ECS_CAST(uint32_t, v) & ECS_ROW_FLAGS_MASK) +#define ECS_ROW_TO_RECORD(row, flags) (ECS_CAST(uint32_t, (ECS_CAST(uint32_t, row) | (flags)))) + +#define ECS_ID_FLAGS_MASK (0xFFull << 60) +#define ECS_ENTITY_MASK (0xFFFFFFFFull) +#define ECS_GENERATION_MASK (0xFFFFull << 32) +#define ECS_GENERATION(e) ((e & ECS_GENERATION_MASK) >> 32) +#define ECS_GENERATION_INC(e) ((e & ~ECS_GENERATION_MASK) | ((0xFFFF & (ECS_GENERATION(e) + 1)) << 32)) +#define ECS_COMPONENT_MASK (~ECS_ID_FLAGS_MASK) +#define ECS_HAS_ID_FLAG(e, flag) ((e) & ECS_##flag) +#define ECS_IS_PAIR(id) (((id) & ECS_ID_FLAGS_MASK) == ECS_PAIR) +#define ECS_PAIR_FIRST(e) (ecs_entity_t_hi(e & ECS_COMPONENT_MASK)) +#define ECS_PAIR_SECOND(e) (ecs_entity_t_lo(e)) +#define ECS_HAS_RELATION(e, rel) (ECS_HAS_ID_FLAG(e, PAIR) && (ECS_PAIR_FIRST(e) == rel)) + +#define ECS_TERM_REF_FLAGS(ref) ((ref)->id & EcsTermRefFlags) +#define ECS_TERM_REF_ID(ref) ((ref)->id & ~EcsTermRefFlags) + +//////////////////////////////////////////////////////////////////////////////// +//// Convert between C typenames and variables +//////////////////////////////////////////////////////////////////////////////// + +/** Translate C type to id. */ +#define ecs_id(T) FLECS_ID##T##ID_ + + +//////////////////////////////////////////////////////////////////////////////// +//// Utilities for working with pair identifiers +//////////////////////////////////////////////////////////////////////////////// + +#define ecs_entity_t_lo(value) ECS_CAST(uint32_t, value) +#define ecs_entity_t_hi(value) ECS_CAST(uint32_t, (value) >> 32) +#define ecs_entity_t_comb(lo, hi) ((ECS_CAST(uint64_t, hi) << 32) + ECS_CAST(uint32_t, lo)) + +#define ecs_pair(pred, obj) (ECS_PAIR | ecs_entity_t_comb(obj, pred)) +#define ecs_pair_t(pred, obj) (ECS_PAIR | ecs_entity_t_comb(obj, ecs_id(pred))) +#define ecs_pair_first(world, pair) ecs_get_alive(world, ECS_PAIR_FIRST(pair)) +#define ecs_pair_second(world, pair) ecs_get_alive(world, ECS_PAIR_SECOND(pair)) +#define ecs_pair_relation ecs_pair_first +#define ecs_pair_target ecs_pair_second + +#define flecs_poly_id(tag) ecs_pair(ecs_id(EcsPoly), tag) + + +//////////////////////////////////////////////////////////////////////////////// +//// Debug macros +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FLECS_NDEBUG +#define ECS_TABLE_LOCK(world, table) ecs_table_lock(world, table) +#define ECS_TABLE_UNLOCK(world, table) ecs_table_unlock(world, table) +#else +#define ECS_TABLE_LOCK(world, table) +#define ECS_TABLE_UNLOCK(world, table) +#endif + + +//////////////////////////////////////////////////////////////////////////////// +//// Actions that drive iteration +//////////////////////////////////////////////////////////////////////////////// + +#define EcsIterNextYield (0) /* Move to next table, yield current */ +#define EcsIterYield (-1) /* Stay on current table, yield */ +#define EcsIterNext (1) /* Move to next table, don't yield */ + +//////////////////////////////////////////////////////////////////////////////// +//// Convenience macros for ctor, dtor, move and copy +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FLECS_LEGACY + +/* Constructor/Destructor convenience macro */ +#define ECS_XTOR_IMPL(type, postfix, var, ...)\ + void type##_##postfix(\ + void *_ptr,\ + int32_t _count,\ + const ecs_type_info_t *type_info)\ + {\ + (void)_ptr;\ + (void)_count;\ + (void)type_info;\ + for (int32_t i = 0; i < _count; i ++) {\ + type *var = &((type*)_ptr)[i];\ + (void)var;\ + __VA_ARGS__\ + }\ + } + +/* Copy convenience macro */ +#define ECS_COPY_IMPL(type, dst_var, src_var, ...)\ + void type##_##copy(\ + void *_dst_ptr,\ + const void *_src_ptr,\ + int32_t _count,\ + const ecs_type_info_t *type_info)\ + {\ + (void)_dst_ptr;\ + (void)_src_ptr;\ + (void)_count;\ + (void)type_info;\ + for (int32_t i = 0; i < _count; i ++) {\ + type *dst_var = &((type*)_dst_ptr)[i];\ + const type *src_var = &((const type*)_src_ptr)[i];\ + (void)dst_var;\ + (void)src_var;\ + __VA_ARGS__\ + }\ + } + +/* Move convenience macro */ +#define ECS_MOVE_IMPL(type, dst_var, src_var, ...)\ + void type##_##move(\ + void *_dst_ptr,\ + void *_src_ptr,\ + int32_t _count,\ + const ecs_type_info_t *type_info)\ + {\ + (void)_dst_ptr;\ + (void)_src_ptr;\ + (void)_count;\ + (void)type_info;\ + for (int32_t i = 0; i < _count; i ++) {\ + type *dst_var = &((type*)_dst_ptr)[i];\ + type *src_var = &((type*)_src_ptr)[i];\ + (void)dst_var;\ + (void)src_var;\ + __VA_ARGS__\ + }\ + } + +#define ECS_HOOK_IMPL(type, func, var, ...)\ + void func(ecs_iter_t *_it)\ + {\ + for (int32_t i = 0; i < _it->count; i ++) {\ + ecs_entity_t entity = _it->entities[i];\ + type *var = ecs_field(_it, type, 0);\ + (void)entity;\ + (void)var;\ + __VA_ARGS__\ + }\ + } + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/** + * @file vec.h + * @brief Vector with allocator support. + */ + +#ifndef FLECS_VEC_H +#define FLECS_VEC_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/** A component column. */ +typedef struct ecs_vec_t { + void *array; + int32_t count; + int32_t size; +#ifdef FLECS_SANITIZE + ecs_size_t elem_size; +#endif +} ecs_vec_t; + +FLECS_API +void ecs_vec_init( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count); + +#define ecs_vec_init_t(allocator, vec, T, elem_count) \ + ecs_vec_init(allocator, vec, ECS_SIZEOF(T), elem_count) + +FLECS_API +void ecs_vec_init_if( + ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_init_if_t(vec, T) \ + ecs_vec_init_if(vec, ECS_SIZEOF(T)) + +FLECS_API +void ecs_vec_fini( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_fini_t(allocator, vec, T) \ + ecs_vec_fini(allocator, vec, ECS_SIZEOF(T)) + +FLECS_API +ecs_vec_t* ecs_vec_reset( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_reset_t(allocator, vec, T) \ + ecs_vec_reset(allocator, vec, ECS_SIZEOF(T)) + +FLECS_API +void ecs_vec_clear( + ecs_vec_t *vec); + +FLECS_API +void* ecs_vec_append( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_append_t(allocator, vec, T) \ + ECS_CAST(T*, ecs_vec_append(allocator, vec, ECS_SIZEOF(T))) + +FLECS_API +void ecs_vec_remove( + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem); + +#define ecs_vec_remove_t(vec, T, elem) \ + ecs_vec_remove(vec, ECS_SIZEOF(T), elem) + +FLECS_API +void ecs_vec_remove_last( + ecs_vec_t *vec); + +FLECS_API +ecs_vec_t ecs_vec_copy( + struct ecs_allocator_t *allocator, + const ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_copy_t(allocator, vec, T) \ + ecs_vec_copy(allocator, vec, ECS_SIZEOF(T)) + +FLECS_API +ecs_vec_t ecs_vec_copy_shrink( + struct ecs_allocator_t *allocator, + const ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_copy_shrink_t(allocator, vec, T) \ + ecs_vec_copy_shrink(allocator, vec, ECS_SIZEOF(T)) + +FLECS_API +void ecs_vec_reclaim( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_reclaim_t(allocator, vec, T) \ + ecs_vec_reclaim(allocator, vec, ECS_SIZEOF(T)) + +FLECS_API +void ecs_vec_set_size( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count); + +#define ecs_vec_set_size_t(allocator, vec, T, elem_count) \ + ecs_vec_set_size(allocator, vec, ECS_SIZEOF(T), elem_count) + +FLECS_API +void ecs_vec_set_min_size( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count); + +#define ecs_vec_set_min_size_t(allocator, vec, T, elem_count) \ + ecs_vec_set_min_size(allocator, vec, ECS_SIZEOF(T), elem_count) + +FLECS_API +void ecs_vec_set_min_count( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count); + +#define ecs_vec_set_min_count_t(allocator, vec, T, elem_count) \ + ecs_vec_set_min_count(allocator, vec, ECS_SIZEOF(T), elem_count) + +FLECS_API +void ecs_vec_set_min_count_zeromem( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count); + +#define ecs_vec_set_min_count_zeromem_t(allocator, vec, T, elem_count) \ + ecs_vec_set_min_count_zeromem(allocator, vec, ECS_SIZEOF(T), elem_count) + +FLECS_API +void ecs_vec_set_count( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count); + +#define ecs_vec_set_count_t(allocator, vec, T, elem_count) \ + ecs_vec_set_count(allocator, vec, ECS_SIZEOF(T), elem_count) + +FLECS_API +void* ecs_vec_grow( + struct ecs_allocator_t *allocator, + ecs_vec_t *vec, + ecs_size_t size, + int32_t elem_count); + +#define ecs_vec_grow_t(allocator, vec, T, elem_count) \ + ecs_vec_grow(allocator, vec, ECS_SIZEOF(T), elem_count) + +FLECS_API +int32_t ecs_vec_count( + const ecs_vec_t *vec); + +FLECS_API +int32_t ecs_vec_size( + const ecs_vec_t *vec); + +FLECS_API +void* ecs_vec_get( + const ecs_vec_t *vec, + ecs_size_t size, + int32_t index); + +#define ecs_vec_get_t(vec, T, index) \ + ECS_CAST(T*, ecs_vec_get(vec, ECS_SIZEOF(T), index)) + +FLECS_API +void* ecs_vec_first( + const ecs_vec_t *vec); + +#define ecs_vec_first_t(vec, T) \ + ECS_CAST(T*, ecs_vec_first(vec)) + +FLECS_API +void* ecs_vec_last( + const ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_last_t(vec, T) \ + ECS_CAST(T*, ecs_vec_last(vec, ECS_SIZEOF(T))) + +#ifdef __cplusplus +} +#endif + +#endif + +/** + * @file sparse.h + * @brief Sparse set data structure. + */ + +#ifndef FLECS_SPARSE_H +#define FLECS_SPARSE_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/** The number of elements in a single page */ +#define FLECS_SPARSE_PAGE_SIZE (1 << FLECS_SPARSE_PAGE_BITS) + +/** Compute the page index from an id by stripping the first 12 bits */ +#define FLECS_SPARSE_PAGE(index) ((int32_t)((uint32_t)index >> FLECS_SPARSE_PAGE_BITS)) + +/** This computes the offset of an index inside a page */ +#define FLECS_SPARSE_OFFSET(index) ((int32_t)index & (FLECS_SPARSE_PAGE_SIZE - 1)) + +typedef struct ecs_sparse_t { + ecs_vec_t dense; /* Dense array with indices to sparse array. The + * dense array stores both alive and not alive + * sparse indices. The 'count' member keeps + * track of which indices are alive. */ + + ecs_vec_t pages; /* Chunks with sparse arrays & data */ + ecs_size_t size; /* Element size */ + int32_t count; /* Number of alive entries */ + uint64_t max_id; /* Local max index (if no global is set) */ + struct ecs_allocator_t *allocator; + struct ecs_block_allocator_t *page_allocator; +} ecs_sparse_t; + +/** Initialize sparse set */ +FLECS_DBG_API +void flecs_sparse_init( + ecs_sparse_t *result, + struct ecs_allocator_t *allocator, + struct ecs_block_allocator_t *page_allocator, + ecs_size_t size); + +#define flecs_sparse_init_t(result, allocator, page_allocator, T)\ + flecs_sparse_init(result, allocator, page_allocator, ECS_SIZEOF(T)) + +FLECS_DBG_API +void flecs_sparse_fini( + ecs_sparse_t *sparse); + +/** Remove all elements from sparse set */ +FLECS_DBG_API +void flecs_sparse_clear( + ecs_sparse_t *sparse); + +/** Add element to sparse set, this generates or recycles an id */ +FLECS_DBG_API +void* flecs_sparse_add( + ecs_sparse_t *sparse, + ecs_size_t elem_size); + +#define flecs_sparse_add_t(sparse, T)\ + ECS_CAST(T*, flecs_sparse_add(sparse, ECS_SIZEOF(T))) + +/** Get last issued id. */ +FLECS_DBG_API +uint64_t flecs_sparse_last_id( + const ecs_sparse_t *sparse); + +/** Generate or recycle a new id. */ +FLECS_DBG_API +uint64_t flecs_sparse_new_id( + ecs_sparse_t *sparse); + +/** Remove an element */ +FLECS_DBG_API +void flecs_sparse_remove( + ecs_sparse_t *sparse, + ecs_size_t elem_size, + uint64_t id); + +#define flecs_sparse_remove_t(sparse, T, id)\ + flecs_sparse_remove(sparse, ECS_SIZEOF(T), id) + +/** Remove an element without liveliness checking */ +FLECS_DBG_API +void* flecs_sparse_remove_fast( + ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index); + +/** Test if id is alive, which requires the generation count to match. */ +FLECS_DBG_API +bool flecs_sparse_is_alive( + const ecs_sparse_t *sparse, + uint64_t id); + +/** Get value from sparse set by dense id. This function is useful in + * combination with flecs_sparse_count for iterating all values in the set. */ +FLECS_DBG_API +void* flecs_sparse_get_dense( + const ecs_sparse_t *sparse, + ecs_size_t elem_size, + int32_t index); + +#define flecs_sparse_get_dense_t(sparse, T, index)\ + ECS_CAST(T*, flecs_sparse_get_dense(sparse, ECS_SIZEOF(T), index)) + +/** Get the number of alive elements in the sparse set. */ +FLECS_DBG_API +int32_t flecs_sparse_count( + const ecs_sparse_t *sparse); + +/** Get element by (sparse) id. The returned pointer is stable for the duration + * of the sparse set, as it is stored in the sparse array. */ +FLECS_DBG_API +void* flecs_sparse_get( + const ecs_sparse_t *sparse, + ecs_size_t elem_size, + uint64_t id); + +#define flecs_sparse_get_t(sparse, T, index)\ + ECS_CAST(T*, flecs_sparse_get(sparse, ECS_SIZEOF(T), index)) + +/** Same as flecs_sparse_get, but doesn't assert if id is not alive. */ +FLECS_DBG_API +void* flecs_sparse_try( + const ecs_sparse_t *sparse, + ecs_size_t elem_size, + uint64_t id); + +#define flecs_sparse_try_t(sparse, T, index)\ + ECS_CAST(T*, flecs_sparse_try(sparse, ECS_SIZEOF(T), index)) + +/** Like get_sparse, but don't care whether element is alive or not. */ +FLECS_DBG_API +void* flecs_sparse_get_any( + const ecs_sparse_t *sparse, + ecs_size_t elem_size, + uint64_t id); + +#define flecs_sparse_get_any_t(sparse, T, index)\ + ECS_CAST(T*, flecs_sparse_get_any(sparse, ECS_SIZEOF(T), index)) + +/** Get or create element by (sparse) id. */ +FLECS_DBG_API +void* flecs_sparse_ensure( + ecs_sparse_t *sparse, + ecs_size_t elem_size, + uint64_t id); + +#define flecs_sparse_ensure_t(sparse, T, index)\ + ECS_CAST(T*, flecs_sparse_ensure(sparse, ECS_SIZEOF(T), index)) + +/** Fast version of ensure, no liveliness checking */ +FLECS_DBG_API +void* flecs_sparse_ensure_fast( + ecs_sparse_t *sparse, + ecs_size_t elem_size, + uint64_t id); + +#define flecs_sparse_ensure_fast_t(sparse, T, index)\ + ECS_CAST(T*, flecs_sparse_ensure_fast(sparse, ECS_SIZEOF(T), index)) + +/** Get pointer to ids (alive and not alive). Use with count() or size(). */ +FLECS_DBG_API +const uint64_t* flecs_sparse_ids( + const ecs_sparse_t *sparse); + +/* Publicly exposed APIs + * These APIs are not part of the public API and as a result may change without + * notice (though they haven't changed in a long time). */ + +FLECS_API +void ecs_sparse_init( + ecs_sparse_t *sparse, + ecs_size_t elem_size); + +#define ecs_sparse_init_t(sparse, T)\ + ecs_sparse_init(sparse, ECS_SIZEOF(T)) + +FLECS_API +void* ecs_sparse_add( + ecs_sparse_t *sparse, + ecs_size_t elem_size); + +#define ecs_sparse_add_t(sparse, T)\ + ECS_CAST(T*, ecs_sparse_add(sparse, ECS_SIZEOF(T))) + +FLECS_API +uint64_t ecs_sparse_last_id( + const ecs_sparse_t *sparse); + +FLECS_API +int32_t ecs_sparse_count( + const ecs_sparse_t *sparse); + +FLECS_API +void* ecs_sparse_get_dense( + const ecs_sparse_t *sparse, + ecs_size_t elem_size, + int32_t index); + +#define ecs_sparse_get_dense_t(sparse, T, index)\ + ECS_CAST(T*, ecs_sparse_get_dense(sparse, ECS_SIZEOF(T), index)) + +FLECS_API +void* ecs_sparse_get( + const ecs_sparse_t *sparse, + ecs_size_t elem_size, + uint64_t id); + +#define ecs_sparse_get_t(sparse, T, index)\ + ECS_CAST(T*, ecs_sparse_get(sparse, ECS_SIZEOF(T), index)) + +#ifdef __cplusplus +} +#endif + +#endif + +/** + * @file block_allocator.h + * @brief Block allocator. + */ + +#ifndef FLECS_BLOCK_ALLOCATOR_H +#define FLECS_BLOCK_ALLOCATOR_H + + +typedef struct ecs_block_allocator_block_t { + void *memory; + struct ecs_block_allocator_block_t *next; +} ecs_block_allocator_block_t; + +typedef struct ecs_block_allocator_chunk_header_t { + struct ecs_block_allocator_chunk_header_t *next; +} ecs_block_allocator_chunk_header_t; + +typedef struct ecs_block_allocator_t { + ecs_block_allocator_chunk_header_t *head; + ecs_block_allocator_block_t *block_head; + ecs_block_allocator_block_t *block_tail; + int32_t chunk_size; + int32_t data_size; + int32_t chunks_per_block; + int32_t block_size; + int32_t alloc_count; +} ecs_block_allocator_t; + +FLECS_API +void flecs_ballocator_init( + ecs_block_allocator_t *ba, + ecs_size_t size); + +#define flecs_ballocator_init_t(ba, T)\ + flecs_ballocator_init(ba, ECS_SIZEOF(T)) +#define flecs_ballocator_init_n(ba, T, count)\ + flecs_ballocator_init(ba, ECS_SIZEOF(T) * count) + +FLECS_API +ecs_block_allocator_t* flecs_ballocator_new( + ecs_size_t size); + +#define flecs_ballocator_new_t(T)\ + flecs_ballocator_new(ECS_SIZEOF(T)) +#define flecs_ballocator_new_n(T, count)\ + flecs_ballocator_new(ECS_SIZEOF(T) * count) + +FLECS_API +void flecs_ballocator_fini( + ecs_block_allocator_t *ba); + +FLECS_API +void flecs_ballocator_free( + ecs_block_allocator_t *ba); + +FLECS_API +void* flecs_balloc( + ecs_block_allocator_t *allocator); + +FLECS_API +void* flecs_bcalloc( + ecs_block_allocator_t *allocator); + +FLECS_API +void flecs_bfree( + ecs_block_allocator_t *allocator, + void *memory); + +FLECS_API +void flecs_bfree_w_dbg_info( + ecs_block_allocator_t *allocator, + void *memory, + const char *type_name); + +FLECS_API +void* flecs_brealloc( + ecs_block_allocator_t *dst, + ecs_block_allocator_t *src, + void *memory); + +FLECS_API +void* flecs_bdup( + ecs_block_allocator_t *ba, + void *memory); + +#endif + +/** + * @file datastructures/stack_allocator.h + * @brief Stack allocator. + */ + +#ifndef FLECS_STACK_ALLOCATOR_H +#define FLECS_STACK_ALLOCATOR_H + +/** Stack allocator for quick allocation of small temporary values */ +#define ECS_STACK_PAGE_SIZE (4096) + +typedef struct ecs_stack_page_t { + void *data; + struct ecs_stack_page_t *next; + int16_t sp; + uint32_t id; +} ecs_stack_page_t; + +typedef struct ecs_stack_cursor_t { + struct ecs_stack_cursor_t *prev; + struct ecs_stack_page_t *page; + int16_t sp; + bool is_free; +#ifdef FLECS_DEBUG + struct ecs_stack_t *owner; +#endif +} ecs_stack_cursor_t; + +typedef struct ecs_stack_t { + ecs_stack_page_t first; + ecs_stack_page_t *tail_page; + ecs_stack_cursor_t *tail_cursor; +#ifdef FLECS_DEBUG + int32_t cursor_count; +#endif +} ecs_stack_t; + +FLECS_DBG_API +void flecs_stack_init( + ecs_stack_t *stack); + +FLECS_DBG_API +void flecs_stack_fini( + ecs_stack_t *stack); + +FLECS_DBG_API +void* flecs_stack_alloc( + ecs_stack_t *stack, + ecs_size_t size, + ecs_size_t align); + +#define flecs_stack_alloc_t(stack, T)\ + flecs_stack_alloc(stack, ECS_SIZEOF(T), ECS_ALIGNOF(T)) + +#define flecs_stack_alloc_n(stack, T, count)\ + flecs_stack_alloc(stack, ECS_SIZEOF(T) * count, ECS_ALIGNOF(T)) + +FLECS_DBG_API +void* flecs_stack_calloc( + ecs_stack_t *stack, + ecs_size_t size, + ecs_size_t align); + +#define flecs_stack_calloc_t(stack, T)\ + flecs_stack_calloc(stack, ECS_SIZEOF(T), ECS_ALIGNOF(T)) + +#define flecs_stack_calloc_n(stack, T, count)\ + flecs_stack_calloc(stack, ECS_SIZEOF(T) * count, ECS_ALIGNOF(T)) + +FLECS_DBG_API +void flecs_stack_free( + void *ptr, + ecs_size_t size); + +#define flecs_stack_free_t(ptr, T)\ + flecs_stack_free(ptr, ECS_SIZEOF(T)) + +#define flecs_stack_free_n(ptr, T, count)\ + flecs_stack_free(ptr, ECS_SIZEOF(T) * count) + +void flecs_stack_reset( + ecs_stack_t *stack); + +FLECS_DBG_API +ecs_stack_cursor_t* flecs_stack_get_cursor( + ecs_stack_t *stack); + +FLECS_DBG_API +void flecs_stack_restore_cursor( + ecs_stack_t *stack, + ecs_stack_cursor_t *cursor); + +#endif + +/** + * @file map.h + * @brief Map data structure. + */ + +#ifndef FLECS_MAP_H +#define FLECS_MAP_H + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint64_t ecs_map_data_t; +typedef ecs_map_data_t ecs_map_key_t; +typedef ecs_map_data_t ecs_map_val_t; + +/* Map type */ +typedef struct ecs_bucket_entry_t { + ecs_map_key_t key; + ecs_map_val_t value; + struct ecs_bucket_entry_t *next; +} ecs_bucket_entry_t; + +typedef struct ecs_bucket_t { + ecs_bucket_entry_t *first; +} ecs_bucket_t; + +typedef struct ecs_map_t { + uint8_t bucket_shift; + bool shared_allocator; + ecs_bucket_t *buckets; + int32_t bucket_count; + int32_t count; + struct ecs_block_allocator_t *entry_allocator; + struct ecs_allocator_t *allocator; +} ecs_map_t; + +typedef struct ecs_map_iter_t { + const ecs_map_t *map; + ecs_bucket_t *bucket; + ecs_bucket_entry_t *entry; + ecs_map_data_t *res; +} ecs_map_iter_t; + +typedef struct ecs_map_params_t { + struct ecs_allocator_t *allocator; + struct ecs_block_allocator_t entry_allocator; +} ecs_map_params_t; + +/* Function/macro postfixes meaning: + * _ptr: access ecs_map_val_t as void* + * _ref: access ecs_map_val_t* as T** + * _deref: dereferences a _ref + * _alloc: if _ptr is NULL, alloc + * _free: if _ptr is not NULL, free + */ + +FLECS_API +void ecs_map_params_init( + ecs_map_params_t *params, + struct ecs_allocator_t *allocator); + +FLECS_API +void ecs_map_params_fini( + ecs_map_params_t *params); + +/** Initialize new map. */ +FLECS_API +void ecs_map_init( + ecs_map_t *map, + struct ecs_allocator_t *allocator); + +/** Initialize new map. */ +FLECS_API +void ecs_map_init_w_params( + ecs_map_t *map, + ecs_map_params_t *params); + +/** Initialize new map if uninitialized, leave as is otherwise */ +FLECS_API +void ecs_map_init_if( + ecs_map_t *map, + struct ecs_allocator_t *allocator); + +FLECS_API +void ecs_map_init_w_params_if( + ecs_map_t *result, + ecs_map_params_t *params); + +/** Deinitialize map. */ +FLECS_API +void ecs_map_fini( + ecs_map_t *map); + +/** Get element for key, returns NULL if they key doesn't exist. */ +FLECS_API +ecs_map_val_t* ecs_map_get( + const ecs_map_t *map, + ecs_map_key_t key); + +/* Get element as pointer (auto-dereferences _ptr) */ +FLECS_API +void* ecs_map_get_deref_( + const ecs_map_t *map, + ecs_map_key_t key); + +/** Get or insert element for key. */ +FLECS_API +ecs_map_val_t* ecs_map_ensure( + ecs_map_t *map, + ecs_map_key_t key); + +/** Get or insert pointer element for key, allocate if the pointer is NULL */ +FLECS_API +void* ecs_map_ensure_alloc( + ecs_map_t *map, + ecs_size_t elem_size, + ecs_map_key_t key); + +/** Insert element for key. */ +FLECS_API +void ecs_map_insert( + ecs_map_t *map, + ecs_map_key_t key, + ecs_map_val_t value); + +/** Insert pointer element for key, populate with new allocation. */ +FLECS_API +void* ecs_map_insert_alloc( + ecs_map_t *map, + ecs_size_t elem_size, + ecs_map_key_t key); + +/** Remove key from map. */ +FLECS_API +ecs_map_val_t ecs_map_remove( + ecs_map_t *map, + ecs_map_key_t key); + +/* Remove pointer element, free if not NULL */ +FLECS_API +void ecs_map_remove_free( + ecs_map_t *map, + ecs_map_key_t key); + +/** Remove all elements from map. */ +FLECS_API +void ecs_map_clear( + ecs_map_t *map); + +/** Return number of elements in map. */ +#define ecs_map_count(map) ((map) ? (map)->count : 0) + +/** Is map initialized */ +#define ecs_map_is_init(map) ((map) ? (map)->bucket_shift != 0 : false) + +/** Return iterator to map contents. */ +FLECS_API +ecs_map_iter_t ecs_map_iter( + const ecs_map_t *map); + +/** Obtain next element in map from iterator. */ +FLECS_API +bool ecs_map_next( + ecs_map_iter_t *iter); + +/** Copy map. */ +FLECS_API +void ecs_map_copy( + ecs_map_t *dst, + const ecs_map_t *src); + +#define ecs_map_get_ref(m, T, k) ECS_CAST(T**, ecs_map_get(m, k)) +#define ecs_map_get_deref(m, T, k) ECS_CAST(T*, ecs_map_get_deref_(m, k)) +#define ecs_map_ensure_ref(m, T, k) ECS_CAST(T**, ecs_map_ensure(m, k)) + +#define ecs_map_insert_ptr(m, k, v) ecs_map_insert(m, k, ECS_CAST(ecs_map_val_t, ECS_PTR_CAST(uintptr_t, v))) +#define ecs_map_insert_alloc_t(m, T, k) ECS_CAST(T*, ecs_map_insert_alloc(m, ECS_SIZEOF(T), k)) +#define ecs_map_ensure_alloc_t(m, T, k) ECS_PTR_CAST(T*, (uintptr_t)ecs_map_ensure_alloc(m, ECS_SIZEOF(T), k)) +#define ecs_map_remove_ptr(m, k) (ECS_PTR_CAST(void*, ECS_CAST(uintptr_t, (ecs_map_remove(m, k))))) + +#define ecs_map_key(it) ((it)->res[0]) +#define ecs_map_value(it) ((it)->res[1]) +#define ecs_map_ptr(it) ECS_PTR_CAST(void*, ECS_CAST(uintptr_t, ecs_map_value(it))) +#define ecs_map_ref(it, T) (ECS_CAST(T**, &((it)->res[1]))) + +#ifdef __cplusplus +} +#endif + +#endif + +/** + * @file switch_list.h + * @brief Interleaved linked list for storing mutually exclusive values. + */ + +#ifndef FLECS_SWITCH_LIST_H +#define FLECS_SWITCH_LIST_H + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ecs_switch_node_t { + uint32_t next; /* Next node in list */ + uint32_t prev; /* Prev node in list */ +} ecs_switch_node_t; + +typedef struct ecs_switch_page_t { + ecs_vec_t nodes; /* vec */ + ecs_vec_t values; /* vec */ +} ecs_switch_page_t; + +typedef struct ecs_switch_t { + ecs_map_t hdrs; /* map */ + ecs_vec_t pages; /* vec */ +} ecs_switch_t; + +/** Init new switch. */ +FLECS_DBG_API +void flecs_switch_init( + ecs_switch_t* sw, + ecs_allocator_t *allocator); + +/** Fini switch. */ +FLECS_DBG_API +void flecs_switch_fini( + ecs_switch_t *sw); + +/** Set value of element. */ +FLECS_DBG_API +bool flecs_switch_set( + ecs_switch_t *sw, + uint32_t element, + uint64_t value); + +/** Reset value of element. */ +FLECS_DBG_API +bool flecs_switch_reset( + ecs_switch_t *sw, + uint32_t element); + +/** Get value for element. */ +FLECS_DBG_API +uint64_t flecs_switch_get( + const ecs_switch_t *sw, + uint32_t element); + +/** Get first element for value. */ +FLECS_DBG_API +uint32_t flecs_switch_first( + const ecs_switch_t *sw, + uint64_t value); + +/** Get next element. */ +FLECS_DBG_API +uint32_t flecs_switch_next( + const ecs_switch_t *sw, + uint32_t previous); + +/** Get target iterator. */ +FLECS_DBG_API +ecs_map_iter_t flecs_switch_targets( + const ecs_switch_t *sw); + +#ifdef __cplusplus +} +#endif + +#endif + +/** + * @file allocator.h + * @brief Allocator that returns memory objects of any size. + */ + +#ifndef FLECS_ALLOCATOR_H +#define FLECS_ALLOCATOR_H + + +FLECS_DBG_API extern int64_t ecs_block_allocator_alloc_count; +FLECS_DBG_API extern int64_t ecs_block_allocator_free_count; +FLECS_DBG_API extern int64_t ecs_stack_allocator_alloc_count; +FLECS_DBG_API extern int64_t ecs_stack_allocator_free_count; + +struct ecs_allocator_t { + ecs_block_allocator_t chunks; + struct ecs_sparse_t sizes; /* */ +}; + +FLECS_API +void flecs_allocator_init( + ecs_allocator_t *a); + +FLECS_API +void flecs_allocator_fini( + ecs_allocator_t *a); + +FLECS_API +ecs_block_allocator_t* flecs_allocator_get( + ecs_allocator_t *a, + ecs_size_t size); + +FLECS_API +char* flecs_strdup( + ecs_allocator_t *a, + const char* str); + +FLECS_API +void flecs_strfree( + ecs_allocator_t *a, + char* str); + +FLECS_API +void* flecs_dup( + ecs_allocator_t *a, + ecs_size_t size, + const void *src); + +#define flecs_allocator(obj) (&obj->allocators.dyn) + +#define flecs_alloc(a, size) flecs_balloc(flecs_allocator_get(a, size)) +#define flecs_alloc_t(a, T) flecs_alloc(a, ECS_SIZEOF(T)) +#define flecs_alloc_n(a, T, count) flecs_alloc(a, ECS_SIZEOF(T) * (count)) + +#define flecs_calloc(a, size) flecs_bcalloc(flecs_allocator_get(a, size)) +#define flecs_calloc_t(a, T) flecs_calloc(a, ECS_SIZEOF(T)) +#define flecs_calloc_n(a, T, count) flecs_calloc(a, ECS_SIZEOF(T) * (count)) + +#define flecs_free(a, size, ptr)\ + flecs_bfree((ptr) ? flecs_allocator_get(a, size) : NULL, ptr) +#define flecs_free_t(a, T, ptr)\ + flecs_bfree_w_dbg_info((ptr) ? flecs_allocator_get(a, ECS_SIZEOF(T)) : NULL, ptr, #T) +#define flecs_free_n(a, T, count, ptr)\ + flecs_bfree_w_dbg_info((ptr) ? flecs_allocator_get(a, ECS_SIZEOF(T) * (count)) : NULL\ + , ptr, #T) + +#define flecs_realloc(a, size_dst, size_src, ptr)\ + flecs_brealloc(flecs_allocator_get(a, size_dst),\ + flecs_allocator_get(a, size_src),\ + ptr) +#define flecs_realloc_n(a, T, count_dst, count_src, ptr)\ + flecs_realloc(a, ECS_SIZEOF(T) * (count_dst), ECS_SIZEOF(T) * (count_src), ptr) + +#define flecs_dup_n(a, T, count, ptr) flecs_dup(a, ECS_SIZEOF(T) * (count), ptr) + +#endif + +/** + * @file strbuf.h + * @brief Utility for constructing strings. + */ + +#ifndef FLECS_STRBUF_H_ +#define FLECS_STRBUF_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +/* Fixes missing field initializer warning on g++ */ +#define ECS_STRBUF_INIT (ecs_strbuf_t){} +#else +#define ECS_STRBUF_INIT (ecs_strbuf_t){0} +#endif + +#define ECS_STRBUF_SMALL_STRING_SIZE (512) +#define ECS_STRBUF_MAX_LIST_DEPTH (32) + +typedef struct ecs_strbuf_list_elem { + int32_t count; + const char *separator; +} ecs_strbuf_list_elem; + +typedef struct ecs_strbuf_t { + char *content; + ecs_size_t length; + ecs_size_t size; + + ecs_strbuf_list_elem list_stack[ECS_STRBUF_MAX_LIST_DEPTH]; + int32_t list_sp; + + char small_string[ECS_STRBUF_SMALL_STRING_SIZE]; +} ecs_strbuf_t; + +/* Append format string to a buffer. + * Returns false when max is reached, true when there is still space */ +FLECS_API +void ecs_strbuf_append( + ecs_strbuf_t *buffer, + const char *fmt, + ...); + +/* Append format string with argument list to a buffer. + * Returns false when max is reached, true when there is still space */ +FLECS_API +void ecs_strbuf_vappend( + ecs_strbuf_t *buffer, + const char *fmt, + va_list args); + +/* Append string to buffer. + * Returns false when max is reached, true when there is still space */ +FLECS_API +void ecs_strbuf_appendstr( + ecs_strbuf_t *buffer, + const char *str); + +/* Append character to buffer. + * Returns false when max is reached, true when there is still space */ +FLECS_API +void ecs_strbuf_appendch( + ecs_strbuf_t *buffer, + char ch); + +/* Append int to buffer. + * Returns false when max is reached, true when there is still space */ +FLECS_API +void ecs_strbuf_appendint( + ecs_strbuf_t *buffer, + int64_t v); + +/* Append float to buffer. + * Returns false when max is reached, true when there is still space */ +FLECS_API +void ecs_strbuf_appendflt( + ecs_strbuf_t *buffer, + double v, + char nan_delim); + +/* Append boolean to buffer. + * Returns false when max is reached, true when there is still space */ +FLECS_API +void ecs_strbuf_appendbool( + ecs_strbuf_t *buffer, + bool v); + +/* Append source buffer to destination buffer. + * Returns false when max is reached, true when there is still space */ +FLECS_API +void ecs_strbuf_mergebuff( + ecs_strbuf_t *dst_buffer, + ecs_strbuf_t *src_buffer); + +/* Append n characters to buffer. + * Returns false when max is reached, true when there is still space */ +FLECS_API +void ecs_strbuf_appendstrn( + ecs_strbuf_t *buffer, + const char *str, + int32_t n); + +/* Return result string */ +FLECS_API +char* ecs_strbuf_get( + ecs_strbuf_t *buffer); + +/* Return small string from first element (appends \0) */ +FLECS_API +char* ecs_strbuf_get_small( + ecs_strbuf_t *buffer); + +/* Reset buffer without returning a string */ +FLECS_API +void ecs_strbuf_reset( + ecs_strbuf_t *buffer); + +/* Push a list */ +FLECS_API +void ecs_strbuf_list_push( + ecs_strbuf_t *buffer, + const char *list_open, + const char *separator); + +/* Pop a new list */ +FLECS_API +void ecs_strbuf_list_pop( + ecs_strbuf_t *buffer, + const char *list_close); + +/* Insert a new element in list */ +FLECS_API +void ecs_strbuf_list_next( + ecs_strbuf_t *buffer); + +/* Append character to as new element in list. */ +FLECS_API +void ecs_strbuf_list_appendch( + ecs_strbuf_t *buffer, + char ch); + +/* Append formatted string as a new element in list */ +FLECS_API +void ecs_strbuf_list_append( + ecs_strbuf_t *buffer, + const char *fmt, + ...); + +/* Append string as a new element in list */ +FLECS_API +void ecs_strbuf_list_appendstr( + ecs_strbuf_t *buffer, + const char *str); + +/* Append string as a new element in list */ +FLECS_API +void ecs_strbuf_list_appendstrn( + ecs_strbuf_t *buffer, + const char *str, + int32_t n); + +FLECS_API +int32_t ecs_strbuf_written( + const ecs_strbuf_t *buffer); + +#define ecs_strbuf_appendlit(buf, str)\ + ecs_strbuf_appendstrn(buf, str, (int32_t)(sizeof(str) - 1)) + +#define ecs_strbuf_list_appendlit(buf, str)\ + ecs_strbuf_list_appendstrn(buf, str, (int32_t)(sizeof(str) - 1)) + +#ifdef __cplusplus +} +#endif + +#endif + +/** + * @file os_api.h + * @brief Operating system abstraction API. + * + * This file contains the operating system abstraction API. The flecs core + * library avoids OS/runtime specific API calls as much as possible. Instead it + * provides an interface that can be implemented by applications. + * + * Examples for how to implement this interface can be found in the + * examples/os_api folder. + */ + +#ifndef FLECS_OS_API_H +#define FLECS_OS_API_H + +/** + * @defgroup c_os_api OS API + * @ingroup c + * Interface for providing OS specific functionality. + * + * @{ + */ + +#include +#include +#include + +#if defined(ECS_TARGET_WINDOWS) +#include +#elif defined(ECS_TARGET_FREEBSD) +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** Time type. */ +typedef struct ecs_time_t { + uint32_t sec; /**< Second part. */ + uint32_t nanosec; /**< Nanosecond part. */ +} ecs_time_t; + +/* Allocation counters */ +extern int64_t ecs_os_api_malloc_count; /**< malloc count. */ +extern int64_t ecs_os_api_realloc_count; /**< realloc count. */ +extern int64_t ecs_os_api_calloc_count; /**< calloc count. */ +extern int64_t ecs_os_api_free_count; /**< free count. */ + +/* Use handle types that _at least_ can store pointers */ +typedef uintptr_t ecs_os_thread_t; /**< OS thread. */ +typedef uintptr_t ecs_os_cond_t; /**< OS cond. */ +typedef uintptr_t ecs_os_mutex_t; /**< OS mutex. */ +typedef uintptr_t ecs_os_dl_t; /**< OS dynamic library. */ +typedef uintptr_t ecs_os_sock_t; /**< OS socket. */ + +/** 64 bit thread id. */ +typedef uint64_t ecs_os_thread_id_t; + +/** Generic function pointer type. */ +typedef void (*ecs_os_proc_t)(void); + +/** OS API init. */ +typedef +void (*ecs_os_api_init_t)(void); + +/** OS API deinit. */ +typedef +void (*ecs_os_api_fini_t)(void); + +/** OS API malloc function type. */ +typedef +void* (*ecs_os_api_malloc_t)( + ecs_size_t size); + +/** OS API free function type. */ +typedef +void (*ecs_os_api_free_t)( + void *ptr); + +/** OS API realloc function type. */ +typedef +void* (*ecs_os_api_realloc_t)( + void *ptr, + ecs_size_t size); + +/** OS API calloc function type. */ +typedef +void* (*ecs_os_api_calloc_t)( + ecs_size_t size); + +/** OS API strdup function type. */ +typedef +char* (*ecs_os_api_strdup_t)( + const char *str); + +/** OS API thread_callback function type. */ +typedef +void* (*ecs_os_thread_callback_t)( + void*); + +/** OS API thread_new function type. */ +typedef +ecs_os_thread_t (*ecs_os_api_thread_new_t)( + ecs_os_thread_callback_t callback, + void *param); + +/** OS API thread_join function type. */ +typedef +void* (*ecs_os_api_thread_join_t)( + ecs_os_thread_t thread); + +/** OS API thread_self function type. */ +typedef +ecs_os_thread_id_t (*ecs_os_api_thread_self_t)(void); + +/** OS API task_new function type. */ +typedef +ecs_os_thread_t (*ecs_os_api_task_new_t)( + ecs_os_thread_callback_t callback, + void *param); + +/** OS API task_join function type. */ +typedef +void* (*ecs_os_api_task_join_t)( + ecs_os_thread_t thread); + +/* Atomic increment / decrement */ +/** OS API ainc function type. */ +typedef +int32_t (*ecs_os_api_ainc_t)( + int32_t *value); + +/** OS API lainc function type. */ +typedef +int64_t (*ecs_os_api_lainc_t)( + int64_t *value); + +/* Mutex */ +/** OS API mutex_new function type. */ +typedef +ecs_os_mutex_t (*ecs_os_api_mutex_new_t)( + void); + +/** OS API mutex_lock function type. */ +typedef +void (*ecs_os_api_mutex_lock_t)( + ecs_os_mutex_t mutex); + +/** OS API mutex_unlock function type. */ +typedef +void (*ecs_os_api_mutex_unlock_t)( + ecs_os_mutex_t mutex); + +/** OS API mutex_free function type. */ +typedef +void (*ecs_os_api_mutex_free_t)( + ecs_os_mutex_t mutex); + +/* Condition variable */ +/** OS API cond_new function type. */ +typedef +ecs_os_cond_t (*ecs_os_api_cond_new_t)( + void); + +/** OS API cond_free function type. */ +typedef +void (*ecs_os_api_cond_free_t)( + ecs_os_cond_t cond); + +/** OS API cond_signal function type. */ +typedef +void (*ecs_os_api_cond_signal_t)( + ecs_os_cond_t cond); + +/** OS API cond_broadcast function type. */ +typedef +void (*ecs_os_api_cond_broadcast_t)( + ecs_os_cond_t cond); + +/** OS API cond_wait function type. */ +typedef +void (*ecs_os_api_cond_wait_t)( + ecs_os_cond_t cond, + ecs_os_mutex_t mutex); + +/** OS API sleep function type. */ +typedef +void (*ecs_os_api_sleep_t)( + int32_t sec, + int32_t nanosec); + +/** OS API enable_high_timer_resolution function type. */ +typedef +void (*ecs_os_api_enable_high_timer_resolution_t)( + bool enable); + +/** OS API get_time function type. */ +typedef +void (*ecs_os_api_get_time_t)( + ecs_time_t *time_out); + +/** OS API now function type. */ +typedef +uint64_t (*ecs_os_api_now_t)(void); + +/** OS API log function type. */ +typedef +void (*ecs_os_api_log_t)( + int32_t level, /* Logging level */ + const char *file, /* File where message was logged */ + int32_t line, /* Line it was logged */ + const char *msg); + +/** OS API abort function type. */ +typedef +void (*ecs_os_api_abort_t)( + void); + +/** OS API dlopen function type. */ +typedef +ecs_os_dl_t (*ecs_os_api_dlopen_t)( + const char *libname); + +/** OS API dlproc function type. */ +typedef +ecs_os_proc_t (*ecs_os_api_dlproc_t)( + ecs_os_dl_t lib, + const char *procname); + +/** OS API dlclose function type. */ +typedef +void (*ecs_os_api_dlclose_t)( + ecs_os_dl_t lib); + +/** OS API module_to_path function type. */ +typedef +char* (*ecs_os_api_module_to_path_t)( + const char *module_id); + +/* Performance tracing */ +typedef void (*ecs_os_api_perf_trace_t)( + const char *filename, + size_t line, + const char *name); + +/* Prefix members of struct with 'ecs_' as some system headers may define + * macros for functions like "strdup", "log" or "_free" */ + +/** OS API interface. */ +typedef struct ecs_os_api_t { + /* API init / deinit */ + ecs_os_api_init_t init_; /**< init callback. */ + ecs_os_api_fini_t fini_; /**< fini callback. */ + + /* Memory management */ + ecs_os_api_malloc_t malloc_; /**< malloc callback. */ + ecs_os_api_realloc_t realloc_; /**< realloc callback. */ + ecs_os_api_calloc_t calloc_; /**< calloc callback. */ + ecs_os_api_free_t free_; /**< free callback. */ + + /* Strings */ + ecs_os_api_strdup_t strdup_; /**< strdup callback. */ + + /* Threads */ + ecs_os_api_thread_new_t thread_new_; /**< thread_new callback. */ + ecs_os_api_thread_join_t thread_join_; /**< thread_join callback. */ + ecs_os_api_thread_self_t thread_self_; /**< thread_self callback. */ + + /* Tasks */ + ecs_os_api_thread_new_t task_new_; /**< task_new callback. */ + ecs_os_api_thread_join_t task_join_; /**< task_join callback. */ + + /* Atomic increment / decrement */ + ecs_os_api_ainc_t ainc_; /**< ainc callback. */ + ecs_os_api_ainc_t adec_; /**< adec callback. */ + ecs_os_api_lainc_t lainc_; /**< lainc callback. */ + ecs_os_api_lainc_t ladec_; /**< ladec callback. */ + + /* Mutex */ + ecs_os_api_mutex_new_t mutex_new_; /**< mutex_new callback. */ + ecs_os_api_mutex_free_t mutex_free_; /**< mutex_free callback. */ + ecs_os_api_mutex_lock_t mutex_lock_; /**< mutex_lock callback. */ + ecs_os_api_mutex_lock_t mutex_unlock_; /**< mutex_unlock callback. */ + + /* Condition variable */ + ecs_os_api_cond_new_t cond_new_; /**< cond_new callback. */ + ecs_os_api_cond_free_t cond_free_; /**< cond_free callback. */ + ecs_os_api_cond_signal_t cond_signal_; /**< cond_signal callback. */ + ecs_os_api_cond_broadcast_t cond_broadcast_; /**< cond_broadcast callback. */ + ecs_os_api_cond_wait_t cond_wait_; /**< cond_wait callback. */ + + /* Time */ + ecs_os_api_sleep_t sleep_; /**< sleep callback. */ + ecs_os_api_now_t now_; /**< now callback. */ + ecs_os_api_get_time_t get_time_; /**< get_time callback. */ + + /* Logging */ + ecs_os_api_log_t log_; /**< log callback. + * The level should be interpreted as: + * >0: Debug tracing. Only enabled in debug builds. + * 0: Tracing. Enabled in debug/release builds. + * -2: Warning. An issue occurred, but operation was successful. + * -3: Error. An issue occurred, and operation was unsuccessful. + * -4: Fatal. An issue occurred, and application must quit. */ + + /* Application termination */ + ecs_os_api_abort_t abort_; /**< abort callback. */ + + /* Dynamic library loading */ + ecs_os_api_dlopen_t dlopen_; /**< dlopen callback. */ + ecs_os_api_dlproc_t dlproc_; /**< dlproc callback. */ + ecs_os_api_dlclose_t dlclose_; /**< dlclose callback. */ + + /* Overridable function that translates from a logical module id to a + * shared library filename */ + ecs_os_api_module_to_path_t module_to_dl_; /**< module_to_dl callback. */ + + /* Overridable function that translates from a logical module id to a + * path that contains module-specif resources or assets */ + ecs_os_api_module_to_path_t module_to_etc_; /**< module_to_etc callback. */ + + /* Performance tracing */ + ecs_os_api_perf_trace_t perf_trace_push_; + + /* Performance tracing */ + ecs_os_api_perf_trace_t perf_trace_pop_; + + int32_t log_level_; /**< Tracing level. */ + int32_t log_indent_; /**< Tracing indentation level. */ + int32_t log_last_error_; /**< Last logged error code. */ + int64_t log_last_timestamp_; /**< Last logged timestamp. */ + + ecs_flags32_t flags_; /**< OS API flags */ + + FILE *log_out_; /**< File used for logging output + * (hint, log_ decides where to write) */ +} ecs_os_api_t; + +/** Static OS API variable with configured callbacks. */ +FLECS_API +extern ecs_os_api_t ecs_os_api; + +/** Initialize OS API. + * This operation is not usually called by an application. To override callbacks + * of the OS API, use the following pattern: + * + * @code + * ecs_os_set_api_defaults(); + * ecs_os_api_t os_api = ecs_os_get_api(); + * os_api.abort_ = my_abort; + * ecs_os_set_api(&os_api); + * @endcode + */ +FLECS_API +void ecs_os_init(void); + +/** Deinitialize OS API. + * This operation is not usually called by an application. + */ +FLECS_API +void ecs_os_fini(void); + +/** Override OS API. + * This overrides the OS API struct with new values for callbacks. See + * ecs_os_init() on how to use the function. + * + * @param os_api Pointer to struct with values to set. + */ +FLECS_API +void ecs_os_set_api( + ecs_os_api_t *os_api); + +/** Get OS API. + * + * @return A value with the current OS API callbacks + * @see ecs_os_init() + */ +FLECS_API +ecs_os_api_t ecs_os_get_api(void); + +/** Set default values for OS API. + * This initializes the OS API struct with default values for callbacks like + * malloc and free. + * + * @see ecs_os_init() + */ +FLECS_API +void ecs_os_set_api_defaults(void); + +/** Macro utilities + * \cond + */ + +/* Memory management */ +#ifndef ecs_os_malloc +#define ecs_os_malloc(size) ecs_os_api.malloc_(size) +#endif +#ifndef ecs_os_free +#define ecs_os_free(ptr) ecs_os_api.free_(ptr) +#endif +#ifndef ecs_os_realloc +#define ecs_os_realloc(ptr, size) ecs_os_api.realloc_(ptr, size) +#endif +#ifndef ecs_os_calloc +#define ecs_os_calloc(size) ecs_os_api.calloc_(size) +#endif +#if defined(ECS_TARGET_WINDOWS) +#define ecs_os_alloca(size) _alloca((size_t)(size)) +#else +#define ecs_os_alloca(size) alloca((size_t)(size)) +#endif + +#define ecs_os_malloc_t(T) ECS_CAST(T*, ecs_os_malloc(ECS_SIZEOF(T))) +#define ecs_os_malloc_n(T, count) ECS_CAST(T*, ecs_os_malloc(ECS_SIZEOF(T) * (count))) +#define ecs_os_calloc_t(T) ECS_CAST(T*, ecs_os_calloc(ECS_SIZEOF(T))) +#define ecs_os_calloc_n(T, count) ECS_CAST(T*, ecs_os_calloc(ECS_SIZEOF(T) * (count))) + +#define ecs_os_realloc_t(ptr, T) ECS_CAST(T*, ecs_os_realloc(ptr, ECS_SIZEOF(T))) +#define ecs_os_realloc_n(ptr, T, count) ECS_CAST(T*, ecs_os_realloc(ptr, ECS_SIZEOF(T) * (count))) +#define ecs_os_alloca_t(T) ECS_CAST(T*, ecs_os_alloca(ECS_SIZEOF(T))) +#define ecs_os_alloca_n(T, count) ECS_CAST(T*, ecs_os_alloca(ECS_SIZEOF(T) * (count))) + +/* Strings */ +#ifndef ecs_os_strdup +#define ecs_os_strdup(str) ecs_os_api.strdup_(str) +#endif + +#ifdef __cplusplus +#define ecs_os_strlen(str) static_cast(strlen(str)) +#define ecs_os_strncmp(str1, str2, num) strncmp(str1, str2, static_cast(num)) +#define ecs_os_memcmp(ptr1, ptr2, num) memcmp(ptr1, ptr2, static_cast(num)) +#define ecs_os_memcpy(ptr1, ptr2, num) memcpy(ptr1, ptr2, static_cast(num)) +#define ecs_os_memset(ptr, value, num) memset(ptr, value, static_cast(num)) +#define ecs_os_memmove(dst, src, size) memmove(dst, src, static_cast(size)) +#else +#define ecs_os_strlen(str) (ecs_size_t)strlen(str) +#define ecs_os_strncmp(str1, str2, num) strncmp(str1, str2, (size_t)(num)) +#define ecs_os_memcmp(ptr1, ptr2, num) memcmp(ptr1, ptr2, (size_t)(num)) +#define ecs_os_memcpy(ptr1, ptr2, num) memcpy(ptr1, ptr2, (size_t)(num)) +#define ecs_os_memset(ptr, value, num) memset(ptr, value, (size_t)(num)) +#define ecs_os_memmove(dst, src, size) memmove(dst, src, (size_t)(size)) +#endif + +#define ecs_os_memcpy_t(ptr1, ptr2, T) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T)) +#define ecs_os_memcpy_n(ptr1, ptr2, T, count) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T) * (size_t)count) +#define ecs_os_memcmp_t(ptr1, ptr2, T) ecs_os_memcmp(ptr1, ptr2, ECS_SIZEOF(T)) + +#define ecs_os_memmove_t(ptr1, ptr2, T) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T)) +#define ecs_os_memmove_n(ptr1, ptr2, T, count) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T) * (size_t)count) +#define ecs_os_memmove_t(ptr1, ptr2, T) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T)) + +#define ecs_os_strcmp(str1, str2) strcmp(str1, str2) +#define ecs_os_memset_t(ptr, value, T) ecs_os_memset(ptr, value, ECS_SIZEOF(T)) +#define ecs_os_memset_n(ptr, value, T, count) ecs_os_memset(ptr, value, ECS_SIZEOF(T) * (size_t)count) +#define ecs_os_zeromem(ptr) ecs_os_memset(ptr, 0, ECS_SIZEOF(*ptr)) + +#define ecs_os_memdup_t(ptr, T) ecs_os_memdup(ptr, ECS_SIZEOF(T)) +#define ecs_os_memdup_n(ptr, T, count) ecs_os_memdup(ptr, ECS_SIZEOF(T) * count) + +#define ecs_offset(ptr, T, index)\ + ECS_CAST(T*, ECS_OFFSET(ptr, ECS_SIZEOF(T) * index)) + +#if !defined(ECS_TARGET_POSIX) && !defined(ECS_TARGET_MINGW) +#define ecs_os_strcat(str1, str2) strcat_s(str1, INT_MAX, str2) +#define ecs_os_snprintf(ptr, len, ...) sprintf_s(ptr, ECS_CAST(size_t, len), __VA_ARGS__) +#define ecs_os_vsnprintf(ptr, len, fmt, args) vsnprintf(ptr, ECS_CAST(size_t, len), fmt, args) +#define ecs_os_strcpy(str1, str2) strcpy_s(str1, INT_MAX, str2) +#define ecs_os_strncpy(str1, str2, len) strncpy_s(str1, INT_MAX, str2, ECS_CAST(size_t, len)) +#else +#define ecs_os_strcat(str1, str2) strcat(str1, str2) +#define ecs_os_snprintf(ptr, len, ...) snprintf(ptr, ECS_CAST(size_t, len), __VA_ARGS__) +#define ecs_os_vsnprintf(ptr, len, fmt, args) vsnprintf(ptr, ECS_CAST(size_t, len), fmt, args) +#define ecs_os_strcpy(str1, str2) strcpy(str1, str2) +#define ecs_os_strncpy(str1, str2, len) strncpy(str1, str2, ECS_CAST(size_t, len)) +#endif + +/* Files */ +#ifndef ECS_TARGET_POSIX +#define ecs_os_fopen(result, file, mode) fopen_s(result, file, mode) +#else +#define ecs_os_fopen(result, file, mode) (*(result)) = fopen(file, mode) +#endif + +/* Threads */ +#define ecs_os_thread_new(callback, param) ecs_os_api.thread_new_(callback, param) +#define ecs_os_thread_join(thread) ecs_os_api.thread_join_(thread) +#define ecs_os_thread_self() ecs_os_api.thread_self_() + +/* Tasks */ +#define ecs_os_task_new(callback, param) ecs_os_api.task_new_(callback, param) +#define ecs_os_task_join(thread) ecs_os_api.task_join_(thread) + +/* Atomic increment / decrement */ +#define ecs_os_ainc(value) ecs_os_api.ainc_(value) +#define ecs_os_adec(value) ecs_os_api.adec_(value) +#define ecs_os_lainc(value) ecs_os_api.lainc_(value) +#define ecs_os_ladec(value) ecs_os_api.ladec_(value) + +/* Mutex */ +#define ecs_os_mutex_new() ecs_os_api.mutex_new_() +#define ecs_os_mutex_free(mutex) ecs_os_api.mutex_free_(mutex) +#define ecs_os_mutex_lock(mutex) ecs_os_api.mutex_lock_(mutex) +#define ecs_os_mutex_unlock(mutex) ecs_os_api.mutex_unlock_(mutex) + +/* Condition variable */ +#define ecs_os_cond_new() ecs_os_api.cond_new_() +#define ecs_os_cond_free(cond) ecs_os_api.cond_free_(cond) +#define ecs_os_cond_signal(cond) ecs_os_api.cond_signal_(cond) +#define ecs_os_cond_broadcast(cond) ecs_os_api.cond_broadcast_(cond) +#define ecs_os_cond_wait(cond, mutex) ecs_os_api.cond_wait_(cond, mutex) + +/* Time */ +#define ecs_os_sleep(sec, nanosec) ecs_os_api.sleep_(sec, nanosec) +#define ecs_os_now() ecs_os_api.now_() +#define ecs_os_get_time(time_out) ecs_os_api.get_time_(time_out) + +#ifndef FLECS_DISABLE_COUNTERS +#ifdef FLECS_ACCURATE_COUNTERS +#define ecs_os_inc(v) (ecs_os_ainc(v)) +#define ecs_os_linc(v) (ecs_os_lainc(v)) +#define ecs_os_dec(v) (ecs_os_adec(v)) +#define ecs_os_ldec(v) (ecs_os_ladec(v)) +#else +#define ecs_os_inc(v) (++(*v)) +#define ecs_os_linc(v) (++(*v)) +#define ecs_os_dec(v) (--(*v)) +#define ecs_os_ldec(v) (--(*v)) +#endif +#else +#define ecs_os_inc(v) +#define ecs_os_linc(v) +#define ecs_os_dec(v) +#define ecs_os_ldec(v) +#endif + + +#ifdef ECS_TARGET_MINGW +/* mingw bug: without this a conversion error is thrown, but isnan/isinf should + * accept float, double and long double. */ +#define ecs_os_isnan(val) (isnan((float)val)) +#define ecs_os_isinf(val) (isinf((float)val)) +#else +#define ecs_os_isnan(val) (isnan(val)) +#define ecs_os_isinf(val) (isinf(val)) +#endif + +/* Application termination */ +#define ecs_os_abort() ecs_os_api.abort_() + +/* Dynamic libraries */ +#define ecs_os_dlopen(libname) ecs_os_api.dlopen_(libname) +#define ecs_os_dlproc(lib, procname) ecs_os_api.dlproc_(lib, procname) +#define ecs_os_dlclose(lib) ecs_os_api.dlclose_(lib) + +/* Module id translation */ +#define ecs_os_module_to_dl(lib) ecs_os_api.module_to_dl_(lib) +#define ecs_os_module_to_etc(lib) ecs_os_api.module_to_etc_(lib) + +/** Macro utilities + * \endcond + */ + + +/* Logging */ + +/** Log at debug level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_dbg( + const char *file, + int32_t line, + const char *msg); + +/** Log at trace level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_trace( + const char *file, + int32_t line, + const char *msg); + +/** Log at warning level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_warn( + const char *file, + int32_t line, + const char *msg); + +/** Log at error level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_err( + const char *file, + int32_t line, + const char *msg); + +/** Log at fatal level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_fatal( + const char *file, + int32_t line, + const char *msg); + +/** Convert errno to string. + * + * @param err The error number. + * @return A string describing the error. + */ +FLECS_API +const char* ecs_os_strerror( + int err); + +/** Utility for assigning strings. + * This operation frees an existing string and duplicates the input string. + * + * @param str Pointer to a string value. + * @param value The string value to assign. + */ +FLECS_API +void ecs_os_strset( + char **str, + const char *value); + +/* Profile tracing */ +#ifdef FLECS_PERF_TRACE +#define ecs_os_perf_trace_push(name) ecs_os_perf_trace_push_(__FILE__, __LINE__, name) +#define ecs_os_perf_trace_pop(name) ecs_os_perf_trace_pop_(__FILE__, __LINE__, name) +#else +#define ecs_os_perf_trace_push(name) +#define ecs_os_perf_trace_pop(name) +#endif + +void ecs_os_perf_trace_push_( + const char *file, + size_t line, + const char *name); + +void ecs_os_perf_trace_pop_( + const char *file, + size_t line, + const char *name); + +/** Sleep with floating point time. + * + * @param t The time in seconds. + */ +FLECS_API +void ecs_sleepf( + double t); + +/** Measure time since provided timestamp. + * Use with a time value initialized to 0 to obtain the number of seconds since + * the epoch. The operation will write the current timestamp in start. + * + * Usage: + * @code + * ecs_time_t t = {}; + * ecs_time_measure(&t); + * // code + * double elapsed = ecs_time_measure(&t); + * @endcode + * + * @param start The starting timestamp. + * @return The time elapsed since start. + */ +FLECS_API +double ecs_time_measure( + ecs_time_t *start); + +/** Calculate difference between two timestamps. + * + * @param t1 The first timestamp. + * @param t2 The first timestamp. + * @return The difference between timestamps. + */ +FLECS_API +ecs_time_t ecs_time_sub( + ecs_time_t t1, + ecs_time_t t2); + +/** Convert time value to a double. + * + * @param t The timestamp. + * @return The timestamp converted to a double. + */ +FLECS_API +double ecs_time_to_double( + ecs_time_t t); + +/** Return newly allocated memory that contains a copy of src. + * + * @param src The source pointer. + * @param size The number of bytes to copy. + * @return The duplicated memory. + */ +FLECS_API +void* ecs_os_memdup( + const void *src, + ecs_size_t size); + +/** Are heap functions available? */ +FLECS_API +bool ecs_os_has_heap(void); + +/** Are threading functions available? */ +FLECS_API +bool ecs_os_has_threading(void); + +/** Are task functions available? */ +FLECS_API +bool ecs_os_has_task_support(void); + +/** Are time functions available? */ +FLECS_API +bool ecs_os_has_time(void); + +/** Are logging functions available? */ +FLECS_API +bool ecs_os_has_logging(void); + +/** Are dynamic library functions available? */ +FLECS_API +bool ecs_os_has_dl(void); + +/** Are module path functions available? */ +FLECS_API +bool ecs_os_has_modules(void); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup api_types API types + * Public API types. + * + * @{ + */ + +/** + * @defgroup core_types Core API Types + * Types for core API objects. + * + * @{ + */ + +/** Ids are the things that can be added to an entity. + * An id can be an entity or pair, and can have optional id flags. */ +typedef uint64_t ecs_id_t; + +/** An entity identifier. + * Entity ids consist out of a number unique to the entity in the lower 32 bits, + * and a counter used to track entity liveliness in the upper 32 bits. When an + * id is recycled, its generation count is increased. This causes recycled ids + * to be very large (>4 billion), which is normal. */ +typedef ecs_id_t ecs_entity_t; + +/** A type is a list of (component) ids. + * Types are used to communicate the "type" of an entity. In most type systems a + * typeof operation returns a single type. In ECS however, an entity can have + * multiple components, which is why an ECS type consists of a vector of ids. + * + * The component ids of a type are sorted, which ensures that it doesn't matter + * in which order components are added to an entity. For example, if adding + * Position then Velocity would result in type [Position, Velocity], first + * adding Velocity then Position would also result in type [Position, Velocity]. + * + * Entities are grouped together by type in the ECS storage in tables. The + * storage has exactly one table per unique type that is created by the + * application that stores all entities and components for that type. This is + * also referred to as an archetype. + */ +typedef struct { + ecs_id_t *array; /**< Array with ids. */ + int32_t count; /**< Number of elements in array. */ +} ecs_type_t; + +/** A world is the container for all ECS data and supporting features. + * Applications can have multiple worlds, though in most cases will only need + * one. Worlds are isolated from each other, and can have separate sets of + * systems, components, modules etc. + * + * If an application has multiple worlds with overlapping components, it is + * common (though not strictly required) to use the same component ids across + * worlds, which can be achieved by declaring a global component id variable. + * To do this in the C API, see the entities/fwd_component_decl example. The + * C++ API automatically synchronizes component ids between worlds. + * + * Component id conflicts between worlds can occur when a world has already used + * an id for something else. There are a few ways to avoid this: + * + * - Ensure to register the same components in each world, in the same order. + * - Create a dummy world in which all components are preregistered which + * initializes the global id variables. + * + * In some use cases, typically when writing tests, multiple worlds are created + * and deleted with different components, registered in different order. To + * ensure isolation between tests, the C++ API has a `flecs::reset` function + * that forces the API to ignore the old component ids. */ +typedef struct ecs_world_t ecs_world_t; + +/** A stage enables modification while iterating and from multiple threads */ +typedef struct ecs_stage_t ecs_stage_t; + +/** A table stores entities and components for a specific type. */ +typedef struct ecs_table_t ecs_table_t; + +/** A term is a single element in a query. */ +typedef struct ecs_term_t ecs_term_t; + +/** A query returns entities matching a list of constraints. */ +typedef struct ecs_query_t ecs_query_t; + +/** An observer is a system that is invoked when an event matches its query. + * Observers allow applications to respond to specific events, such as adding or + * removing a component. Observers are created by both specifying a query and + * a list of event kinds that should be listened for. An example of an observer + * that triggers when a Position component is added to an entity (in C++): + * + * @code + * world.observer() + * .event(flecs::OnAdd) + * .each([](Position& p) { + * // called when Position is added to an entity + * }); + * @endcode + * + * Observers only trigger when the source of the event matches the full observer + * query. For example, an OnAdd observer for Position, Velocity will only + * trigger after both components have been added to the entity. */ +typedef struct ecs_observer_t ecs_observer_t; + +/** An observable produces events that can be listened for by an observer. + * Currently only the world is observable. In the future, queries will become + * observable objects as well. */ +typedef struct ecs_observable_t ecs_observable_t; + +/** Type used for iterating iterable objects. + * Iterators are objects that provide applications with information + * about the currently iterated result, and store any state required for the + * iteration. */ +typedef struct ecs_iter_t ecs_iter_t; + +/** A ref is a fast way to fetch a component for a specific entity. + * Refs are a faster alternative to repeatedly calling ecs_get() for the same + * entity/component combination. When comparing the performance of getting a ref + * to calling ecs_get(), a ref is typically 3-5x faster. + * + * Refs achieve this performance by caching internal data structures associated + * with the entity and component on the ecs_ref_t object that otherwise would + * have to be looked up. */ +typedef struct ecs_ref_t ecs_ref_t; + +/** Type hooks are callbacks associated with component lifecycle events. + * Typical examples of lifecycle events are construction, destruction, copying + * and moving of components. */ +typedef struct ecs_type_hooks_t ecs_type_hooks_t; + +/** Type information. + * Contains information about a (component) type, such as its size and + * alignment and type hooks. */ +typedef struct ecs_type_info_t ecs_type_info_t; + +/** Information about an entity, like its table and row. */ +typedef struct ecs_record_t ecs_record_t; + +/** Information about a (component) id, such as type info and tables with the id */ +typedef struct ecs_id_record_t ecs_id_record_t; + +/** A poly object. + * A poly (short for polymorph) object is an object that has a variable list of + * capabilities, determined by a mixin table. This is the current list of types + * in the flecs API that can be used as an ecs_poly_t: + * + * - ecs_world_t + * - ecs_stage_t + * - ecs_query_t + * + * Functions that accept an ecs_poly_t argument can accept objects of these + * types. If the object does not have the requested mixin the API will throw an + * assert. + * + * The poly/mixin framework enables partially overlapping features to be + * implemented once, and enables objects of different types to interact with + * each other depending on what mixins they have, rather than their type + * (in some ways it's like a mini-ECS). Additionally, each poly object has a + * header that enables the API to do sanity checking on the input arguments. + */ +typedef void ecs_poly_t; + +/** Type that stores poly mixins */ +typedef struct ecs_mixins_t ecs_mixins_t; + +/** Header for ecs_poly_t objects. */ +typedef struct ecs_header_t { + int32_t magic; /**< Magic number verifying it's a flecs object */ + int32_t type; /**< Magic number indicating which type of flecs object */ + int32_t refcount; /**< Refcount, to enable RAII handles */ + ecs_mixins_t *mixins; /**< Table with offsets to (optional) mixins */ +} ecs_header_t; + +/** Record for entity index */ +struct ecs_record_t { + ecs_id_record_t *idr; /**< Id record to (*, entity) for target entities */ + ecs_table_t *table; /**< Identifies a type (and table) in world */ + uint32_t row; /**< Table row of the entity */ + int32_t dense; /**< Index in dense array of entity index */ +}; + +/** Header for table cache elements. */ +typedef struct ecs_table_cache_hdr_t { + struct ecs_table_cache_t *cache; /**< Table cache of element. Of type ecs_id_record_t* for component index elements. */ + ecs_table_t *table; /**< Table associated with element. */ + struct ecs_table_cache_hdr_t *prev, *next; /**< Next/previous elements for id in table cache. */ + bool empty; /**< Whether element is in empty list. */ +} ecs_table_cache_hdr_t; + +/** Metadata describing where a component id is stored in a table. + * This type is used as element type for the component index table cache. One + * record exists per table/component in the table. Only records for wildcard ids + * can have a count > 1. */ +typedef struct ecs_table_record_t { + ecs_table_cache_hdr_t hdr; /**< Table cache header */ + int16_t index; /**< First type index where id occurs in table */ + int16_t count; /**< Number of times id occurs in table */ + int16_t column; /**< First column index where id occurs */ +} ecs_table_record_t; + +/** @} */ + +/** + * @defgroup function_types Function types. + * Function callback types. + * + * @{ + */ + +/** Function prototype for runnables (systems, observers). + * The run callback overrides the default behavior for iterating through the + * results of a runnable object. + * + * The default runnable iterates the iterator, and calls an iter_action (see + * below) for each returned result. + * + * @param it The iterator to be iterated by the runnable. + */ +typedef void (*ecs_run_action_t)( + ecs_iter_t *it); + +/** Function prototype for iterables. + * A system may invoke a callback multiple times, typically once for each + * matched table. + * + * @param it The iterator containing the data for the current match. + */ +typedef void (*ecs_iter_action_t)( + ecs_iter_t *it); + +/** Function prototype for iterating an iterator. + * Stored inside initialized iterators. This allows an application to iterate + * an iterator without needing to know what created it. + * + * @param it The iterator to iterate. + * @return True if iterator has no more results, false if it does. + */ +typedef bool (*ecs_iter_next_action_t)( + ecs_iter_t *it); + +/** Function prototype for freeing an iterator. + * Free iterator resources. + * + * @param it The iterator to free. + */ +typedef void (*ecs_iter_fini_action_t)( + ecs_iter_t *it); + +/** Callback used for comparing components */ +typedef int (*ecs_order_by_action_t)( + ecs_entity_t e1, + const void *ptr1, + ecs_entity_t e2, + const void *ptr2); + +/** Callback used for sorting the entire table of components */ +typedef void (*ecs_sort_table_action_t)( + ecs_world_t* world, + ecs_table_t* table, + ecs_entity_t* entities, + void* ptr, + int32_t size, + int32_t lo, + int32_t hi, + ecs_order_by_action_t order_by); + +/** Callback used for grouping tables in a query */ +typedef uint64_t (*ecs_group_by_action_t)( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t group_id, + void *ctx); + +/** Callback invoked when a query creates a new group. */ +typedef void* (*ecs_group_create_action_t)( + ecs_world_t *world, + uint64_t group_id, + void *group_by_ctx); /* from ecs_query_desc_t */ + +/** Callback invoked when a query deletes an existing group. */ +typedef void (*ecs_group_delete_action_t)( + ecs_world_t *world, + uint64_t group_id, + void *group_ctx, /* return value from ecs_group_create_action_t */ + void *group_by_ctx); /* from ecs_query_desc_t */ + +/** Initialization action for modules */ +typedef void (*ecs_module_action_t)( + ecs_world_t *world); + +/** Action callback on world exit */ +typedef void (*ecs_fini_action_t)( + ecs_world_t *world, + void *ctx); + +/** Function to cleanup context data */ +typedef void (*ecs_ctx_free_t)( + void *ctx); + +/** Callback used for sorting values */ +typedef int (*ecs_compare_action_t)( + const void *ptr1, + const void *ptr2); + +/** Callback used for hashing values */ +typedef uint64_t (*ecs_hash_value_action_t)( + const void *ptr); + +/** Constructor/destructor callback */ +typedef void (*ecs_xtor_t)( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info); + +/** Copy is invoked when a component is copied into another component. */ +typedef void (*ecs_copy_t)( + void *dst_ptr, + const void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info); + +/** Move is invoked when a component is moved to another component. */ +typedef void (*ecs_move_t)( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info); + +/** Destructor function for poly objects. */ +typedef void (*flecs_poly_dtor_t)( + ecs_poly_t *poly); + +/** @} */ + +/** + * @defgroup query_types Query descriptor types. + * Types used to describe queries. + * + * @{ + */ + +/** Specify read/write access for term */ +typedef enum ecs_inout_kind_t { + EcsInOutDefault, /**< InOut for regular terms, In for shared terms */ + EcsInOutNone, /**< Term is neither read nor written */ + EcsInOutFilter, /**< Same as InOutNone + prevents term from triggering observers */ + EcsInOut, /**< Term is both read and written */ + EcsIn, /**< Term is only read */ + EcsOut, /**< Term is only written */ +} ecs_inout_kind_t; + +/** Specify operator for term */ +typedef enum ecs_oper_kind_t { + EcsAnd, /**< The term must match */ + EcsOr, /**< One of the terms in an or chain must match */ + EcsNot, /**< The term must not match */ + EcsOptional, /**< The term may match */ + EcsAndFrom, /**< Term must match all components from term id */ + EcsOrFrom, /**< Term must match at least one component from term id */ + EcsNotFrom, /**< Term must match none of the components from term id */ +} ecs_oper_kind_t; + +/** Specify cache policy for query */ +typedef enum ecs_query_cache_kind_t { + EcsQueryCacheDefault, /**< Behavior determined by query creation context */ + EcsQueryCacheAuto, /**< Cache query terms that are cacheable */ + EcsQueryCacheAll, /**< Require that all query terms can be cached */ + EcsQueryCacheNone, /**< No caching */ +} ecs_query_cache_kind_t; + +/* Term id flags */ + +/** Match on self. + * Can be combined with other term flags on the ecs_term_t::flags_ field. + * \ingroup queries + */ +#define EcsSelf (1llu << 63) + +/** Match by traversing upwards. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsUp (1llu << 62) + +/** Traverse relationship transitively. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsTrav (1llu << 61) + +/** Sort results breadth first. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsCascade (1llu << 60) + +/** Iterate groups in descending order. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsDesc (1llu << 59) + +/** Term id is a variable. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsIsVariable (1llu << 58) + +/** Term id is an entity. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsIsEntity (1llu << 57) + +/** Term id is a name (don't attempt to lookup as entity). + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsIsName (1llu << 56) + +/** All term traversal flags. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsTraverseFlags (EcsSelf|EcsUp|EcsTrav|EcsCascade|EcsDesc) + +/** All term reference kind flags. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsTermRefFlags (EcsTraverseFlags|EcsIsVariable|EcsIsEntity|EcsIsName) + +/** Type that describes a reference to an entity or variable in a term. */ +typedef struct ecs_term_ref_t { + ecs_entity_t id; /**< Entity id. If left to 0 and flags does not + * specify whether id is an entity or a variable + * the id will be initialized to #EcsThis. + * To explicitly set the id to 0, leave the id + * member to 0 and set #EcsIsEntity in flags. */ + + const char *name; /**< Name. This can be either the variable name + * (when the #EcsIsVariable flag is set) or an + * entity name. When ecs_term_t::move is true, + * the API assumes ownership over the string and + * will free it when the term is destroyed. */ +} ecs_term_ref_t; + +/** Type that describes a term (single element in a query). */ +struct ecs_term_t { + ecs_id_t id; /**< Component id to be matched by term. Can be + * set directly, or will be populated from the + * first/second members, which provide more + * flexibility. */ + + ecs_term_ref_t src; /**< Source of term */ + ecs_term_ref_t first; /**< Component or first element of pair */ + ecs_term_ref_t second; /**< Second element of pair */ + + ecs_entity_t trav; /**< Relationship to traverse when looking for the + * component. The relationship must have + * the `Traversable` property. Default is `IsA`. */ + + int16_t inout; /**< Access to contents matched by term */ + int16_t oper; /**< Operator of term */ + + int8_t field_index; /**< Index of field for term in iterator */ + ecs_flags16_t flags_; /**< Flags that help eval, set by ecs_query_init() */ +}; + +/** Queries are lists of constraints (terms) that match entities. + * Created with ecs_query_init(). + */ +struct ecs_query_t { + ecs_header_t hdr; /**< Object header */ + + ecs_term_t terms[FLECS_TERM_COUNT_MAX]; /**< Query terms */ + int32_t sizes[FLECS_TERM_COUNT_MAX]; /**< Component sizes. Indexed by field */ + ecs_id_t ids[FLECS_TERM_COUNT_MAX]; /**< Component ids. Indexed by field */ + + ecs_flags32_t flags; /**< Query flags */ + int8_t var_count; /**< Number of query variables */ + int8_t term_count; /**< Number of query terms */ + int8_t field_count; /**< Number of fields returned by query */ + + /* Bitmasks for quick field information lookups */ + ecs_termset_t fixed_fields; /**< Fields with a fixed source */ + ecs_termset_t var_fields; /**< Fields with non-$this variable source */ + ecs_termset_t static_id_fields; /**< Fields with a static (component) id */ + ecs_termset_t data_fields; /**< Fields that have data */ + ecs_termset_t write_fields; /**< Fields that write data */ + ecs_termset_t read_fields; /**< Fields that read data */ + ecs_termset_t row_fields; /**< Fields that must be acquired with field_at */ + ecs_termset_t shared_readonly_fields; /**< Fields that don't write shared data */ + ecs_termset_t set_fields; /**< Fields that will be set */ + + ecs_query_cache_kind_t cache_kind; /**< Caching policy of query */ + + char **vars; /**< Array with variable names for iterator */ + + void *ctx; /**< User context to pass to callback */ + void *binding_ctx; /**< Context to be used for language bindings */ + + ecs_entity_t entity; /**< Entity associated with query (optional) */ + ecs_world_t *real_world; /**< Actual world. */ + ecs_world_t *world; /**< World or stage query was created with. */ + + int32_t eval_count; /**< Number of times query is evaluated */ +}; + +/** An observer reacts to events matching a query. + * Created with ecs_observer_init(). + */ +struct ecs_observer_t { + ecs_header_t hdr; /**< Object header */ + + ecs_query_t *query; /**< Observer query */ + + /** Observer events */ + ecs_entity_t events[FLECS_EVENT_DESC_MAX]; + int32_t event_count; /**< Number of events */ + + ecs_iter_action_t callback; /**< See ecs_observer_desc_t::callback */ + ecs_run_action_t run; /**< See ecs_observer_desc_t::run */ + + void *ctx; /**< Observer context */ + void *callback_ctx; /**< Callback language binding context */ + void *run_ctx; /**< Run language binding context */ + + ecs_ctx_free_t ctx_free; /**< Callback to free ctx */ + ecs_ctx_free_t callback_ctx_free; /**< Callback to free callback_ctx */ + ecs_ctx_free_t run_ctx_free; /**< Callback to free run_ctx */ + + ecs_observable_t *observable; /**< Observable for observer */ + + ecs_world_t *world; /**< The world */ + ecs_entity_t entity; /**< Entity associated with observer */ +}; + +/** @} */ + +/** Type that contains component lifecycle callbacks. + * + * @ingroup components + */ +struct ecs_type_hooks_t { + ecs_xtor_t ctor; /**< ctor */ + ecs_xtor_t dtor; /**< dtor */ + ecs_copy_t copy; /**< copy assignment */ + ecs_move_t move; /**< move assignment */ + + /** Ctor + copy */ + ecs_copy_t copy_ctor; + + /** Ctor + move */ + ecs_move_t move_ctor; + + /** Ctor + move + dtor (or move_ctor + dtor). + * This combination is typically used when a component is moved from one + * location to a new location, like when it is moved to a new table. If + * not set explicitly it will be derived from other callbacks. */ + ecs_move_t ctor_move_dtor; + + /** Move + dtor. + * This combination is typically used when a component is moved from one + * location to an existing location, like what happens during a remove. If + * not set explicitly it will be derived from other callbacks. */ + ecs_move_t move_dtor; + + /** Callback that is invoked when an instance of a component is added. This + * callback is invoked before triggers are invoked. */ + ecs_iter_action_t on_add; + + /** Callback that is invoked when an instance of the component is set. This + * callback is invoked before triggers are invoked, and enable the component + * to respond to changes on itself before others can. */ + ecs_iter_action_t on_set; + + /** Callback that is invoked when an instance of the component is removed. + * This callback is invoked after the triggers are invoked, and before the + * destructor is invoked. */ + ecs_iter_action_t on_remove; + + void *ctx; /**< User defined context */ + void *binding_ctx; /**< Language binding context */ + void *lifecycle_ctx; /**< Component lifecycle context (see meta add-on)*/ + + ecs_ctx_free_t ctx_free; /**< Callback to free ctx */ + ecs_ctx_free_t binding_ctx_free; /**< Callback to free binding_ctx */ + ecs_ctx_free_t lifecycle_ctx_free; /**< Callback to free lifecycle_ctx */ + +}; + +/** Type that contains component information (passed to ctors/dtors/...) + * + * @ingroup components + */ +struct ecs_type_info_t { + ecs_size_t size; /**< Size of type */ + ecs_size_t alignment; /**< Alignment of type */ + ecs_type_hooks_t hooks; /**< Type hooks */ + ecs_entity_t component; /**< Handle to component (do not set) */ + const char *name; /**< Type name. */ +}; + +/** + * @file api_types.h + * @brief Supporting types for the public API. + * + * This file contains types that are typically not used by an application but + * support the public API, and therefore must be exposed. This header should not + * be included by itself. + */ + +#ifndef FLECS_API_TYPES_H +#define FLECS_API_TYPES_H + + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////////////// +//// Opaque types +//////////////////////////////////////////////////////////////////////////////// + +/** Table data */ +typedef struct ecs_data_t ecs_data_t; + +/* Cached query table data */ +typedef struct ecs_query_cache_table_match_t ecs_query_cache_table_match_t; + +//////////////////////////////////////////////////////////////////////////////// +//// Non-opaque types +//////////////////////////////////////////////////////////////////////////////// + +/** All observers for a specific event */ +typedef struct ecs_event_record_t { + struct ecs_event_id_record_t *any; + struct ecs_event_id_record_t *wildcard; + struct ecs_event_id_record_t *wildcard_pair; + ecs_map_t event_ids; /* map */ + ecs_entity_t event; +} ecs_event_record_t; + +struct ecs_observable_t { + ecs_event_record_t on_add; + ecs_event_record_t on_remove; + ecs_event_record_t on_set; + ecs_event_record_t on_wildcard; + ecs_sparse_t events; /* sparse */ +}; + +/** Range in table */ +typedef struct ecs_table_range_t { + ecs_table_t *table; + int32_t offset; /* Leave both members to 0 to cover entire table */ + int32_t count; +} ecs_table_range_t; + +/** Value of query variable */ +typedef struct ecs_var_t { + ecs_table_range_t range; /* Set when variable stores a range of entities */ + ecs_entity_t entity; /* Set when variable stores single entity */ + + /* Most entities can be stored as a range by setting range.count to 1, + * however in order to also be able to store empty entities in variables, + * a separate entity member is needed. Both range and entity may be set at + * the same time, as long as they are consistent. */ +} ecs_var_t; + +/** Cached reference. */ +struct ecs_ref_t { + ecs_entity_t entity; /* Entity */ + ecs_entity_t id; /* Component id */ + uint64_t table_id; /* Table id for detecting ABA issues */ + struct ecs_table_record_t *tr; /* Table record for component */ + ecs_record_t *record; /* Entity index record */ +}; + + +/* Page-iterator specific data */ +typedef struct ecs_page_iter_t { + int32_t offset; + int32_t limit; + int32_t remaining; +} ecs_page_iter_t; + +/* Worker-iterator specific data */ +typedef struct ecs_worker_iter_t { + int32_t index; + int32_t count; +} ecs_worker_iter_t; + +/* Convenience struct to iterate table array for id */ +typedef struct ecs_table_cache_iter_t { + struct ecs_table_cache_hdr_t *cur, *next; + struct ecs_table_cache_hdr_t *next_list; +} ecs_table_cache_iter_t; + +/** Each iterator */ +typedef struct ecs_each_iter_t { + ecs_table_cache_iter_t it; + + /* Storage for iterator fields */ + ecs_id_t ids; + ecs_entity_t sources; + ecs_size_t sizes; + int32_t columns; + const ecs_table_record_t* trs; +} ecs_each_iter_t; + +typedef struct ecs_query_op_profile_t { + int32_t count[2]; /* 0 = enter, 1 = redo */ +} ecs_query_op_profile_t; + +/** Query iterator */ +typedef struct ecs_query_iter_t { + const ecs_query_t *query; + struct ecs_var_t *vars; /* Variable storage */ + const struct ecs_query_var_t *query_vars; + const struct ecs_query_op_t *ops; + struct ecs_query_op_ctx_t *op_ctx; /* Operation-specific state */ + ecs_query_cache_table_match_t *node, *prev, *last; /* For cached iteration */ + uint64_t *written; + int32_t skip_count; + + ecs_query_op_profile_t *profile; + + int16_t op; + int16_t sp; +} ecs_query_iter_t; + +/* Bits for tracking whether a cache was used/whether the array was allocated. + * Used by flecs_iter_init, flecs_iter_validate and ecs_iter_fini. + * Constants are named to enable easy macro substitution. */ +#define flecs_iter_cache_ids (1u << 0u) +#define flecs_iter_cache_trs (1u << 1u) +#define flecs_iter_cache_sources (1u << 2u) +#define flecs_iter_cache_ptrs (1u << 3u) +#define flecs_iter_cache_variables (1u << 4u) +#define flecs_iter_cache_all (255) + +/* Inline iterator arrays to prevent allocations for small array sizes */ +typedef struct ecs_iter_cache_t { + ecs_stack_cursor_t *stack_cursor; /* Stack cursor to restore to */ + ecs_flags8_t used; /* For which fields is the cache used */ + ecs_flags8_t allocated; /* Which fields are allocated */ +} ecs_iter_cache_t; + +/* Private iterator data. Used by iterator implementations to keep track of + * progress & to provide builtin storage. */ +typedef struct ecs_iter_private_t { + union { + ecs_query_iter_t query; + ecs_page_iter_t page; + ecs_worker_iter_t worker; + ecs_each_iter_t each; + } iter; /* Iterator specific data */ + + void *entity_iter; /* Query applied after matching a table */ + ecs_iter_cache_t cache; /* Inline arrays to reduce allocations */ +} ecs_iter_private_t; + +#ifdef __cplusplus +} +#endif + +#endif + + +/** + * @file api_support.h + * @brief Support functions and constants. + * + * Supporting types and functions that need to be exposed either in support of + * the public API or for unit tests, but that may change between minor / patch + * releases. + */ + +#ifndef FLECS_API_SUPPORT_H +#define FLECS_API_SUPPORT_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/** This is the largest possible component id. Components for the most part + * occupy the same id range as entities, however they are not allowed to overlap + * with (8) bits reserved for id flags. */ +#define ECS_MAX_COMPONENT_ID (~((uint32_t)(ECS_ID_FLAGS_MASK >> 32))) + +/** The maximum number of nested function calls before the core will throw a + * cycle detected error */ +#define ECS_MAX_RECURSION (512) + +/** Maximum length of a parser token (used by parser-related addons) */ +#define ECS_MAX_TOKEN_SIZE (256) + +FLECS_API +char* flecs_module_path_from_c( + const char *c_name); + +bool flecs_identifier_is_0( + const char *id); + +/* Constructor that zeromem's a component value */ +FLECS_API +void flecs_default_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *ctx); + +/* Create allocated string from format */ +FLECS_DBG_API +char* flecs_vasprintf( + const char *fmt, + va_list args); + +/* Create allocated string from format */ +FLECS_API +char* flecs_asprintf( + const char *fmt, + ...); + +/** Write an escaped character. + * Write a character to an output string, insert escape character if necessary. + * + * @param out The string to write the character to. + * @param in The input character. + * @param delimiter The delimiter used (for example '"') + * @return Pointer to the character after the last one written. + */ +FLECS_API +char* flecs_chresc( + char *out, + char in, + char delimiter); + +/** Parse an escaped character. + * Parse a character with a potential escape sequence. + * + * @param in Pointer to character in input string. + * @param out Output string. + * @return Pointer to the character after the last one read. + */ +const char* flecs_chrparse( + const char *in, + char *out); + +/** Write an escaped string. + * Write an input string to an output string, escape characters where necessary. + * To determine the size of the output string, call the operation with a NULL + * argument for 'out', and use the returned size to allocate a string that is + * large enough. + * + * @param out Pointer to output string (must be). + * @param size Maximum number of characters written to output. + * @param delimiter The delimiter used (for example '"'). + * @param in The input string. + * @return The number of characters that (would) have been written. + */ +FLECS_API +ecs_size_t flecs_stresc( + char *out, + ecs_size_t size, + char delimiter, + const char *in); + +/** Return escaped string. + * Return escaped version of input string. Same as flecs_stresc(), but returns an + * allocated string of the right size. + * + * @param delimiter The delimiter used (for example '"'). + * @param in The input string. + * @return Escaped string. + */ +FLECS_API +char* flecs_astresc( + char delimiter, + const char *in); + +/** Skip whitespace and newline characters. + * This function skips whitespace characters. + * + * @param ptr Pointer to (potential) whitespaces to skip. + * @return Pointer to the next non-whitespace character. + */ +FLECS_API +const char* flecs_parse_ws_eol( + const char *ptr); + +/** Parse digit. + * This function will parse until the first non-digit character is found. The + * provided expression must contain at least one digit character. + * + * @param ptr The expression to parse. + * @param token The output buffer. + * @return Pointer to the first non-digit character. + */ +FLECS_API +const char* flecs_parse_digit( + const char *ptr, + char *token); + +/* Convert identifier to snake case */ +FLECS_API +char* flecs_to_snake_case( + const char *str); + +FLECS_DBG_API +int32_t flecs_table_observed_count( + const ecs_table_t *table); + +FLECS_DBG_API +void flecs_dump_backtrace( + void *stream); + +/* Suspend/resume readonly state. To fully support implicit registration of + * components, it should be possible to register components while the world is + * in readonly mode. It is not uncommon that a component is used first from + * within a system, which are often ran while in readonly mode. + * + * Suspending readonly mode is only allowed when the world is not multithreaded. + * When a world is multithreaded, it is not safe to (even temporarily) leave + * readonly mode, so a multithreaded application should always explicitly + * register components in advance. + * + * These operations also suspend deferred mode. + */ +typedef struct ecs_suspend_readonly_state_t { + bool is_readonly; + bool is_deferred; + int32_t defer_count; + ecs_entity_t scope; + ecs_entity_t with; + ecs_vec_t commands; + ecs_stack_t defer_stack; + ecs_stage_t *stage; +} ecs_suspend_readonly_state_t; + +FLECS_API +ecs_world_t* flecs_suspend_readonly( + const ecs_world_t *world, + ecs_suspend_readonly_state_t *state); + +FLECS_API +void flecs_resume_readonly( + ecs_world_t *world, + ecs_suspend_readonly_state_t *state); + +FLECS_API +int32_t flecs_poly_claim_( + ecs_poly_t *poly); + +FLECS_API +int32_t flecs_poly_release_( + ecs_poly_t *poly); + +FLECS_API +int32_t flecs_poly_refcount( + ecs_poly_t *poly); + +#define flecs_poly_claim(poly) \ + flecs_poly_claim_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) +#define flecs_poly_release(poly) \ + flecs_poly_release_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) + + +/** Calculate offset from address */ +#ifdef __cplusplus +#define ECS_OFFSET(o, offset) reinterpret_cast((reinterpret_cast(o)) + (static_cast(offset))) +#else +#define ECS_OFFSET(o, offset) (void*)(((uintptr_t)(o)) + ((uintptr_t)(offset))) +#endif +#define ECS_OFFSET_T(o, T) ECS_OFFSET(o, ECS_SIZEOF(T)) + +#define ECS_ELEM(ptr, size, index) ECS_OFFSET(ptr, (size) * (index)) +#define ECS_ELEM_T(o, T, index) ECS_ELEM(o, ECS_SIZEOF(T), index) + +/** Enable/disable bitsets */ +#define ECS_BIT_SET(flags, bit) (flags) |= (bit) +#define ECS_BIT_CLEAR(flags, bit) (flags) &= ~(bit) +#define ECS_BIT_COND(flags, bit, cond) ((cond) \ + ? (ECS_BIT_SET(flags, bit)) \ + : (ECS_BIT_CLEAR(flags, bit))) + +#define ECS_BIT_CLEAR16(flags, bit) (flags) &= (ecs_flags16_t)~(bit) +#define ECS_BIT_COND16(flags, bit, cond) ((cond) \ + ? (ECS_BIT_SET(flags, bit)) \ + : (ECS_BIT_CLEAR16(flags, bit))) + +#define ECS_BIT_IS_SET(flags, bit) ((flags) & (bit)) + +#define ECS_BIT_SETN(flags, n) ECS_BIT_SET(flags, 1llu << n) +#define ECS_BIT_CLEARN(flags, n) ECS_BIT_CLEAR(flags, 1llu << n) +#define ECS_BIT_CONDN(flags, n, cond) ECS_BIT_COND(flags, 1llu << n, cond) + +#ifdef __cplusplus +} +#endif + +#endif + +/** + * @file hashmap.h + * @brief Hashmap data structure. + */ + +#ifndef FLECS_HASHMAP_H +#define FLECS_HASHMAP_H + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + ecs_vec_t keys; + ecs_vec_t values; +} ecs_hm_bucket_t; + +typedef struct { + ecs_hash_value_action_t hash; + ecs_compare_action_t compare; + ecs_size_t key_size; + ecs_size_t value_size; + ecs_block_allocator_t *hashmap_allocator; + ecs_block_allocator_t bucket_allocator; + ecs_map_t impl; +} ecs_hashmap_t; + +typedef struct { + ecs_map_iter_t it; + ecs_hm_bucket_t *bucket; + int32_t index; +} flecs_hashmap_iter_t; + +typedef struct { + void *key; + void *value; + uint64_t hash; +} flecs_hashmap_result_t; + +FLECS_DBG_API +void flecs_hashmap_init_( + ecs_hashmap_t *hm, + ecs_size_t key_size, + ecs_size_t value_size, + ecs_hash_value_action_t hash, + ecs_compare_action_t compare, + ecs_allocator_t *allocator); + +#define flecs_hashmap_init(hm, K, V, hash, compare, allocator)\ + flecs_hashmap_init_(hm, ECS_SIZEOF(K), ECS_SIZEOF(V), hash, compare, allocator) + +FLECS_DBG_API +void flecs_hashmap_fini( + ecs_hashmap_t *map); + +FLECS_DBG_API +void* flecs_hashmap_get_( + const ecs_hashmap_t *map, + ecs_size_t key_size, + const void *key, + ecs_size_t value_size); + +#define flecs_hashmap_get(map, key, V)\ + (V*)flecs_hashmap_get_(map, ECS_SIZEOF(*key), key, ECS_SIZEOF(V)) + +FLECS_DBG_API +flecs_hashmap_result_t flecs_hashmap_ensure_( + ecs_hashmap_t *map, + ecs_size_t key_size, + const void *key, + ecs_size_t value_size); + +#define flecs_hashmap_ensure(map, key, V)\ + flecs_hashmap_ensure_(map, ECS_SIZEOF(*key), key, ECS_SIZEOF(V)) + +FLECS_DBG_API +void flecs_hashmap_set_( + ecs_hashmap_t *map, + ecs_size_t key_size, + void *key, + ecs_size_t value_size, + const void *value); + +#define flecs_hashmap_set(map, key, value)\ + flecs_hashmap_set_(map, ECS_SIZEOF(*key), key, ECS_SIZEOF(*value), value) + +FLECS_DBG_API +void flecs_hashmap_remove_( + ecs_hashmap_t *map, + ecs_size_t key_size, + const void *key, + ecs_size_t value_size); + +#define flecs_hashmap_remove(map, key, V)\ + flecs_hashmap_remove_(map, ECS_SIZEOF(*key), key, ECS_SIZEOF(V)) + +FLECS_DBG_API +void flecs_hashmap_remove_w_hash_( + ecs_hashmap_t *map, + ecs_size_t key_size, + const void *key, + ecs_size_t value_size, + uint64_t hash); + +#define flecs_hashmap_remove_w_hash(map, key, V, hash)\ + flecs_hashmap_remove_w_hash_(map, ECS_SIZEOF(*key), key, ECS_SIZEOF(V), hash) + +FLECS_DBG_API +ecs_hm_bucket_t* flecs_hashmap_get_bucket( + const ecs_hashmap_t *map, + uint64_t hash); + +FLECS_DBG_API +void flecs_hm_bucket_remove( + ecs_hashmap_t *map, + ecs_hm_bucket_t *bucket, + uint64_t hash, + int32_t index); + +FLECS_DBG_API +void flecs_hashmap_copy( + ecs_hashmap_t *dst, + const ecs_hashmap_t *src); + +FLECS_DBG_API +flecs_hashmap_iter_t flecs_hashmap_iter( + ecs_hashmap_t *map); + +FLECS_DBG_API +void* flecs_hashmap_next_( + flecs_hashmap_iter_t *it, + ecs_size_t key_size, + void *key_out, + ecs_size_t value_size); + +#define flecs_hashmap_next(map, V)\ + (V*)flecs_hashmap_next_(map, 0, NULL, ECS_SIZEOF(V)) + +#define flecs_hashmap_next_w_key(map, K, key, V)\ + (V*)flecs_hashmap_next_(map, ECS_SIZEOF(K), key, ECS_SIZEOF(V)) + +#ifdef __cplusplus +} +#endif + +#endif + + +/** Utility to hold a value of a dynamic type. */ +typedef struct ecs_value_t { + ecs_entity_t type; /**< Type of value. */ + void *ptr; /**< Pointer to value. */ +} ecs_value_t; + +/** Used with ecs_entity_init(). + * + * @ingroup entities + */ +typedef struct ecs_entity_desc_t { + int32_t _canary; /**< Used for validity testing. Must be 0. */ + + ecs_entity_t id; /**< Set to modify existing entity (optional) */ + + ecs_entity_t parent; /**< Parent entity. */ + + const char *name; /**< Name of the entity. If no entity is provided, an + * entity with this name will be looked up first. When + * an entity is provided, the name will be verified + * with the existing entity. */ + + const char *sep; /**< Optional custom separator for hierarchical names. + * Leave to NULL for default ('.') separator. Set to + * an empty string to prevent tokenization of name. */ + + const char *root_sep; /**< Optional, used for identifiers relative to root */ + + const char *symbol; /**< Optional entity symbol. A symbol is an unscoped + * identifier that can be used to lookup an entity. The + * primary use case for this is to associate the entity + * with a language identifier, such as a type or + * function name, where these identifiers differ from + * the name they are registered with in flecs. For + * example, C type "EcsPosition" might be registered + * as "flecs.components.transform.Position", with the + * symbol set to "EcsPosition". */ + + bool use_low_id; /**< When set to true, a low id (typically reserved for + * components) will be used to create the entity, if + * no id is specified. */ + + /** 0-terminated array of ids to add to the entity. */ + const ecs_id_t *add; + + /** 0-terminated array of values to set on the entity. */ + const ecs_value_t *set; + + /** String expression with components to add */ + const char *add_expr; +} ecs_entity_desc_t; + +/** Used with ecs_bulk_init(). + * + * @ingroup entities + */ +typedef struct ecs_bulk_desc_t { + int32_t _canary; /**< Used for validity testing. Must be 0. */ + + ecs_entity_t *entities; /**< Entities to bulk insert. Entity ids provided by + * the application must be empty (cannot + * have components). If no entity ids are provided, the + * operation will create 'count' new entities. */ + + int32_t count; /**< Number of entities to create/populate */ + + ecs_id_t ids[FLECS_ID_DESC_MAX]; /**< Ids to create the entities with */ + + void **data; /**< Array with component data to insert. Each element in + * the array must correspond with an element in the ids + * array. If an element in the ids array is a tag, the + * data array must contain a NULL. An element may be + * set to NULL for a component, in which case the + * component will not be set by the operation. */ + + ecs_table_t *table; /**< Table to insert the entities into. Should not be set + * at the same time as ids. When 'table' is set at the + * same time as 'data', the elements in the data array + * must correspond with the ids in the table's type. */ + +} ecs_bulk_desc_t; + +/** Used with ecs_component_init(). + * + * @ingroup components + */ +typedef struct ecs_component_desc_t { + int32_t _canary; /**< Used for validity testing. Must be 0. */ + + /** Existing entity to associate with observer (optional) */ + ecs_entity_t entity; + + /** Parameters for type (size, hooks, ...) */ + ecs_type_info_t type; +} ecs_component_desc_t; + +/** Iterator. + * Used for iterating queries. The ecs_iter_t type contains all the information + * that is provided by a query, and contains all the state required for the + * iterator code. + * + * Functions that create iterators accept as first argument the world, and as + * second argument the object they iterate. For example: + * + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * @endcode + * + * When this code is called from a system, it is important to use the world + * provided by its iterator object to ensure thread safety. For example: + * + * @code + * void Collide(ecs_iter_t *it) { + * ecs_iter_t qit = ecs_query_iter(it->world, Colliders); + * } + * @endcode + * + * An iterator contains resources that need to be released. By default this + * is handled by the last call to next() that returns false. When iteration is + * ended before iteration has completed, an application has to manually call + * ecs_iter_fini() to release the iterator resources: + * + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * while (ecs_query_next(&it)) { + * if (cond) { + * ecs_iter_fini(&it); + * break; + * } + * } + * @endcode + * + * @ingroup queries + */ +struct ecs_iter_t { + /* World */ + ecs_world_t *world; /**< The world. Can point to stage when in deferred/readonly mode. */ + ecs_world_t *real_world; /**< Actual world. Never points to a stage. */ + + /* Matched data */ + const ecs_entity_t *entities; /**< Entity identifiers */ + const ecs_size_t *sizes; /**< Component sizes */ + ecs_table_t *table; /**< Current table */ + ecs_table_t *other_table; /**< Prev or next table when adding/removing */ + ecs_id_t *ids; /**< (Component) ids */ + ecs_var_t *variables; /**< Values of variables (if any) */ + const ecs_table_record_t **trs; /**< Info on where to find field in table */ + ecs_entity_t *sources; /**< Entity on which the id was matched (0 if same as entities) */ + ecs_flags64_t constrained_vars; /**< Bitset that marks constrained variables */ + uint64_t group_id; /**< Group id for table, if group_by is used */ + ecs_termset_t set_fields; /**< Fields that are set */ + ecs_termset_t ref_fields; /**< Bitset with fields that aren't component arrays */ + ecs_termset_t row_fields; /**< Fields that must be obtained with field_at */ + ecs_termset_t up_fields; /**< Bitset with fields matched through up traversal */ + + /* Input information */ + ecs_entity_t system; /**< The system (if applicable) */ + ecs_entity_t event; /**< The event (if applicable) */ + ecs_id_t event_id; /**< The (component) id for the event */ + int32_t event_cur; /**< Unique event id. Used to dedup observer calls */ + + /* Query information */ + int8_t field_count; /**< Number of fields in iterator */ + int8_t term_index; /**< Index of term that emitted an event. + * This field will be set to the 'index' field + * of an observer term. */ + int8_t variable_count; /**< Number of variables for query */ + const ecs_query_t *query; /**< Query being evaluated */ + char **variable_names; /**< Names of variables (if any) */ + + /* Context */ + void *param; /**< Param passed to ecs_run */ + void *ctx; /**< System context */ + void *binding_ctx; /**< System binding context */ + void *callback_ctx; /**< Callback language binding context */ + void *run_ctx; /**< Run language binding context */ + + /* Time */ + ecs_ftime_t delta_time; /**< Time elapsed since last frame */ + ecs_ftime_t delta_system_time;/**< Time elapsed since last system invocation */ + + /* Iterator counters */ + int32_t frame_offset; /**< Offset relative to start of iteration */ + int32_t offset; /**< Offset relative to current table */ + int32_t count; /**< Number of entities to iterate */ + + /* Misc */ + ecs_flags32_t flags; /**< Iterator flags */ + ecs_entity_t interrupted_by; /**< When set, system execution is interrupted */ + ecs_iter_private_t priv_; /**< Private data */ + + /* Chained iterators */ + ecs_iter_next_action_t next; /**< Function to progress iterator */ + ecs_iter_action_t callback; /**< Callback of system or observer */ + ecs_iter_fini_action_t fini; /**< Function to cleanup iterator resources */ + ecs_iter_t *chain_it; /**< Optional, allows for creating iterator chains */ +}; + + +/** Query must match prefabs. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryMatchPrefab (1u << 1u) + +/** Query must match disabled entities. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryMatchDisabled (1u << 2u) + +/** Query must match empty tables. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryMatchEmptyTables (1u << 3u) + +/** Query may have unresolved entity identifiers. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryAllowUnresolvedByName (1u << 6u) + +/** Query only returns whole tables (ignores toggle/member fields). + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryTableOnly (1u << 7u) + + +/** Used with ecs_query_init(). + * + * \ingroup queries + */ +typedef struct ecs_query_desc_t { + /** Used for validity testing. Must be 0. */ + int32_t _canary; + + /** Query terms */ + ecs_term_t terms[FLECS_TERM_COUNT_MAX]; + + /** Query DSL expression (optional) */ + const char *expr; + + /** Caching policy of query */ + ecs_query_cache_kind_t cache_kind; + + /** Flags for enabling query features */ + ecs_flags32_t flags; + + /** Callback used for ordering query results. If order_by_id is 0, the + * pointer provided to the callback will be NULL. If the callback is not + * set, results will not be ordered. */ + ecs_order_by_action_t order_by_callback; + + /** Callback used for ordering query results. Same as order_by_callback, + * but more efficient. */ + ecs_sort_table_action_t order_by_table_callback; + + /** Component to sort on, used together with order_by_callback or + * order_by_table_callback. */ + ecs_entity_t order_by; + + /** Component id to be used for grouping. Used together with the + * group_by_callback. */ + ecs_id_t group_by; + + /** Callback used for grouping results. If the callback is not set, results + * will not be grouped. When set, this callback will be used to calculate a + * "rank" for each entity (table) based on its components. This rank is then + * used to sort entities (tables), so that entities (tables) of the same + * rank are "grouped" together when iterated. */ + ecs_group_by_action_t group_by_callback; + + /** Callback that is invoked when a new group is created. The return value of + * the callback is stored as context for a group. */ + ecs_group_create_action_t on_group_create; + + /** Callback that is invoked when an existing group is deleted. The return + * value of the on_group_create callback is passed as context parameter. */ + ecs_group_delete_action_t on_group_delete; + + /** Context to pass to group_by */ + void *group_by_ctx; + + /** Function to free group_by_ctx */ + ecs_ctx_free_t group_by_ctx_free; + + /** User context to pass to callback */ + void *ctx; + + /** Context to be used for language bindings */ + void *binding_ctx; + + /** Callback to free ctx */ + ecs_ctx_free_t ctx_free; + + /** Callback to free binding_ctx */ + ecs_ctx_free_t binding_ctx_free; + + /** Entity associated with query (optional) */ + ecs_entity_t entity; +} ecs_query_desc_t; + +/** Used with ecs_observer_init(). + * + * @ingroup observers + */ +typedef struct ecs_observer_desc_t { + /** Used for validity testing. Must be 0. */ + int32_t _canary; + + /** Existing entity to associate with observer (optional) */ + ecs_entity_t entity; + + /** Query for observer */ + ecs_query_desc_t query; + + /** Events to observe (OnAdd, OnRemove, OnSet) */ + ecs_entity_t events[FLECS_EVENT_DESC_MAX]; + + /** When observer is created, generate events from existing data. For example, + * #EcsOnAdd `Position` would match all existing instances of `Position`. */ + bool yield_existing; + + /** Callback to invoke on an event, invoked when the observer matches. */ + ecs_iter_action_t callback; + + /** Callback invoked on an event. When left to NULL the default runner + * is used which matches the event with the observer's query, and calls + * 'callback' when it matches. + * A reason to override the run function is to improve performance, if there + * are more efficient way to test whether an event matches the observer than + * the general purpose query matcher. */ + ecs_run_action_t run; + + /** User context to pass to callback */ + void *ctx; + + /** Callback to free ctx */ + ecs_ctx_free_t ctx_free; + + /** Context associated with callback (for language bindings). */ + void *callback_ctx; + + /** Callback to free callback ctx. */ + ecs_ctx_free_t callback_ctx_free; + + /** Context associated with run (for language bindings). */ + void *run_ctx; + + /** Callback to free run ctx. */ + ecs_ctx_free_t run_ctx_free; + + /** Observable with which to register the observer */ + ecs_poly_t *observable; + + /** Optional shared last event id for multiple observers. Ensures only one + * of the observers with the shared id gets triggered for an event */ + int32_t *last_event_id; + + /** Used for internal purposes */ + int8_t term_index_; + ecs_flags32_t flags_; +} ecs_observer_desc_t; + +/** Used with ecs_emit(). + * + * @ingroup observers + */ +typedef struct ecs_event_desc_t { + /** The event id. Only observers for the specified event will be notified */ + ecs_entity_t event; + + /** Component ids. Only observers with a matching component id will be + * notified. Observers are guaranteed to get notified once, even if they + * match more than one id. */ + const ecs_type_t *ids; + + /** The table for which to notify. */ + ecs_table_t *table; + + /** Optional 2nd table to notify. This can be used to communicate the + * previous or next table, in case an entity is moved between tables. */ + ecs_table_t *other_table; + + /** Limit notified entities to ones starting from offset (row) in table */ + int32_t offset; + + /** Limit number of notified entities to count. offset+count must be less + * than the total number of entities in the table. If left to 0, it will be + * automatically determined by doing `ecs_table_count(table) - offset`. */ + int32_t count; + + /** Single-entity alternative to setting table / offset / count */ + ecs_entity_t entity; + + /** Optional context. + * The type of the param must be the event, where the event is a component. + * When an event is enqueued, the value of param is coped to a temporary + * storage of the event type. */ + void *param; + + /** Same as param, but with the guarantee that the value won't be modified. + * When an event with a const parameter is enqueued, the value of the param + * is copied to a temporary storage of the event type. */ + const void *const_param; + + /** Observable (usually the world) */ + ecs_poly_t *observable; + + /** Event flags */ + ecs_flags32_t flags; +} ecs_event_desc_t; + + +/** + * @defgroup misc_types Miscellaneous types + * Types used to create entities, observers, queries and more. + * + * @{ + */ + +/** Type with information about the current Flecs build */ +typedef struct ecs_build_info_t { + const char *compiler; /**< Compiler used to compile flecs */ + const char **addons; /**< Addons included in build */ + const char *version; /**< Stringified version */ + int16_t version_major; /**< Major flecs version */ + int16_t version_minor; /**< Minor flecs version */ + int16_t version_patch; /**< Patch flecs version */ + bool debug; /**< Is this a debug build */ + bool sanitize; /**< Is this a sanitize build */ + bool perf_trace; /**< Is this a perf tracing build */ +} ecs_build_info_t; + +/** Type that contains information about the world. */ +typedef struct ecs_world_info_t { + ecs_entity_t last_component_id; /**< Last issued component entity id */ + ecs_entity_t min_id; /**< First allowed entity id */ + ecs_entity_t max_id; /**< Last allowed entity id */ + + ecs_ftime_t delta_time_raw; /**< Raw delta time (no time scaling) */ + ecs_ftime_t delta_time; /**< Time passed to or computed by ecs_progress() */ + ecs_ftime_t time_scale; /**< Time scale applied to delta_time */ + ecs_ftime_t target_fps; /**< Target fps */ + ecs_ftime_t frame_time_total; /**< Total time spent processing a frame */ + ecs_ftime_t system_time_total; /**< Total time spent in systems */ + ecs_ftime_t emit_time_total; /**< Total time spent notifying observers */ + ecs_ftime_t merge_time_total; /**< Total time spent in merges */ + ecs_ftime_t rematch_time_total; /**< Time spent on query rematching */ + double world_time_total; /**< Time elapsed in simulation */ + double world_time_total_raw; /**< Time elapsed in simulation (no scaling) */ + + int64_t frame_count_total; /**< Total number of frames */ + int64_t merge_count_total; /**< Total number of merges */ + int64_t eval_comp_monitors_total; /**< Total number of monitor evaluations */ + int64_t rematch_count_total; /**< Total number of rematches */ + + int64_t id_create_total; /**< Total number of times a new id was created */ + int64_t id_delete_total; /**< Total number of times an id was deleted */ + int64_t table_create_total; /**< Total number of times a table was created */ + int64_t table_delete_total; /**< Total number of times a table was deleted */ + int64_t pipeline_build_count_total; /**< Total number of pipeline builds */ + int64_t systems_ran_frame; /**< Total number of systems ran in last frame */ + int64_t observers_ran_frame; /**< Total number of times observer was invoked */ + + int32_t tag_id_count; /**< Number of tag (no data) ids in the world */ + int32_t component_id_count; /**< Number of component (data) ids in the world */ + int32_t pair_id_count; /**< Number of pair ids in the world */ + + int32_t table_count; /**< Number of tables */ + int32_t empty_table_count; /**< Number of tables without entities */ + + /* -- Command counts -- */ + struct { + int64_t add_count; /**< Add commands processed */ + int64_t remove_count; /**< Remove commands processed */ + int64_t delete_count; /**< Selete commands processed */ + int64_t clear_count; /**< Clear commands processed */ + int64_t set_count; /**< Set commands processed */ + int64_t ensure_count; /**< Ensure/emplace commands processed */ + int64_t modified_count; /**< Modified commands processed */ + int64_t discard_count; /**< Commands discarded, happens when entity is no longer alive when running the command */ + int64_t event_count; /**< Enqueued custom events */ + int64_t other_count; /**< Other commands processed */ + int64_t batched_entity_count; /**< Entities for which commands were batched */ + int64_t batched_command_count; /**< Commands batched */ + } cmd; /**< Command statistics. */ + + const char *name_prefix; /**< Value set by ecs_set_name_prefix(). Used + * to remove library prefixes of symbol + * names (such as `Ecs`, `ecs_`) when + * registering them as names. */ +} ecs_world_info_t; + +/** Type that contains information about a query group. */ +typedef struct ecs_query_group_info_t { + int32_t match_count; /**< How often tables have been matched/unmatched */ + int32_t table_count; /**< Number of tables in group */ + void *ctx; /**< Group context, returned by on_group_create */ +} ecs_query_group_info_t; + +/** @} */ + +/** + * @defgroup builtin_components Builtin component types. + * Types that represent builtin components. + * + * @{ + */ + +/** A (string) identifier. Used as pair with #EcsName and #EcsSymbol tags */ +typedef struct EcsIdentifier { + char *value; /**< Identifier string */ + ecs_size_t length; /**< Length of identifier */ + uint64_t hash; /**< Hash of current value */ + uint64_t index_hash; /**< Hash of existing record in current index */ + ecs_hashmap_t *index; /**< Current index */ +} EcsIdentifier; + +/** Component information. */ +typedef struct EcsComponent { + ecs_size_t size; /**< Component size */ + ecs_size_t alignment; /**< Component alignment */ +} EcsComponent; + +/** Component for storing a poly object */ +typedef struct EcsPoly { + ecs_poly_t *poly; /**< Pointer to poly object */ +} EcsPoly; + +/** When added to an entity this informs serialization formats which component + * to use when a value is assigned to an entity without specifying the + * component. This is intended as a hint, serialization formats are not required + * to use it. Adding this component does not change the behavior of core ECS + * operations. */ +typedef struct EcsDefaultChildComponent { + ecs_id_t component; /**< Default component id. */ +} EcsDefaultChildComponent; + +/** @} */ +/** @} */ + +/* Only include deprecated definitions if deprecated addon is required */ +#ifdef FLECS_DEPRECATED +/** + * @file addons/deprecated.h + * @brief The deprecated addon contains deprecated operations. + */ + +#ifdef FLECS_DEPRECATED + +#ifndef FLECS_DEPRECATED_H +#define FLECS_DEPRECATED_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +#endif + +/** + * @defgroup api_constants API Constants + * Public API constants. + * + * @{ + */ + +/** + * @defgroup id_flags Component id flags. + * Id flags are bits that can be set on an id (ecs_id_t). + * + * @{ + */ + +/** Indicates that the id is a pair. */ +FLECS_API extern const ecs_id_t ECS_PAIR; + +/** Automatically override component when it is inherited */ +FLECS_API extern const ecs_id_t ECS_AUTO_OVERRIDE; + +/** Adds bitset to storage which allows component to be enabled/disabled */ +FLECS_API extern const ecs_id_t ECS_TOGGLE; + +/** @} */ + +/** + * @defgroup builtin_tags Builtin component ids. + * @{ + */ + +/* Builtin component ids */ + +/** Component component id. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsComponent); + +/** Identifier component id. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsIdentifier); + +/** Poly component id. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsPoly); + +/** DefaultChildComponent component id. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsDefaultChildComponent); + +/** Tag added to queries. */ +FLECS_API extern const ecs_entity_t EcsQuery; + +/** Tag added to observers. */ +FLECS_API extern const ecs_entity_t EcsObserver; + +/** Tag added to systems. */ +FLECS_API extern const ecs_entity_t EcsSystem; + +/** TickSource component id. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsTickSource); + +/** Pipeline module component ids */ +FLECS_API extern const ecs_entity_t ecs_id(EcsPipelineQuery); + +/** Timer component id. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsTimer); + +/** RateFilter component id. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsRateFilter); + +/** Root scope for builtin flecs entities */ +FLECS_API extern const ecs_entity_t EcsFlecs; + +/** Core module scope */ +FLECS_API extern const ecs_entity_t EcsFlecsCore; + +/** Entity associated with world (used for "attaching" components to world) */ +FLECS_API extern const ecs_entity_t EcsWorld; + +/** Wildcard entity ("*"). Matches any id, returns all matches. */ +FLECS_API extern const ecs_entity_t EcsWildcard; + +/** Any entity ("_"). Matches any id, returns only the first. */ +FLECS_API extern const ecs_entity_t EcsAny; + +/** This entity. Default source for queries. */ +FLECS_API extern const ecs_entity_t EcsThis; + +/** Variable entity ("$"). Used in expressions to prefix variable names */ +FLECS_API extern const ecs_entity_t EcsVariable; + +/** Shortcut as EcsVariable is typically used as source for singleton terms */ +#define EcsSingleton EcsVariable + +/** Marks a relationship as transitive. + * Behavior: + * + * @code + * if R(X, Y) and R(Y, Z) then R(X, Z) + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsTransitive; + +/** Marks a relationship as reflexive. + * Behavior: + * + * @code + * R(X, X) == true + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsReflexive; + +/** Ensures that entity/component cannot be used as target in `IsA` relationship. + * Final can improve the performance of queries as they will not attempt to + * substitute a final component with its subsets. + * + * Behavior: + * + * @code + * if IsA(X, Y) and Final(Y) throw error + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsFinal; + +/** Relationship that specifies component inheritance behavior. */ +FLECS_API extern const ecs_entity_t EcsOnInstantiate; + +/** Override component on instantiate. + * This will copy the component from the base entity `(IsA target)` to the + * instance. The base component will never be inherited from the prefab. */ +FLECS_API extern const ecs_entity_t EcsOverride; + +/** Inherit component on instantiate. + * This will inherit (share) the component from the base entity `(IsA target)`. + * The component can be manually overridden by adding it to the instance. */ +FLECS_API extern const ecs_entity_t EcsInherit; + +/** Never inherit component on instantiate. + * This will not copy or share the component from the base entity `(IsA target)`. + * When the component is added to an instance, its value will never be copied + * from the base entity. */ +FLECS_API extern const ecs_entity_t EcsDontInherit; + +/** Marks relationship as commutative. + * Behavior: + * + * @code + * if R(X, Y) then R(Y, X) + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsSymmetric; + +/** Can be added to relationship to indicate that the relationship can only occur + * once on an entity. Adding a 2nd instance will replace the 1st. + * + * Behavior: + * + * @code + * R(X, Y) + R(X, Z) = R(X, Z) + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsExclusive; + +/** Marks a relationship as acyclic. Acyclic relationships may not form cycles. */ +FLECS_API extern const ecs_entity_t EcsAcyclic; + +/** Marks a relationship as traversable. Traversable relationships may be + * traversed with "up" queries. Traversable relationships are acyclic. */ +FLECS_API extern const ecs_entity_t EcsTraversable; + +/** Ensure that a component always is added together with another component. + * + * Behavior: + * + * @code + * If With(R, O) and R(X) then O(X) + * If With(R, O) and R(X, Y) then O(X, Y) + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsWith; + +/** Ensure that relationship target is child of specified entity. + * + * Behavior: + * + * @code + * If OneOf(R, O) and R(X, Y), Y must be a child of O + * If OneOf(R) and R(X, Y), Y must be a child of R + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsOneOf; + +/** Mark a component as toggleable with ecs_enable_id(). */ +FLECS_API extern const ecs_entity_t EcsCanToggle; + +/** Can be added to components to indicate it is a trait. Traits are components + * and/or tags that are added to other components to modify their behavior. + */ +FLECS_API extern const ecs_entity_t EcsTrait; + +/** Ensure that an entity is always used in pair as relationship. + * + * Behavior: + * + * @code + * e.add(R) panics + * e.add(X, R) panics, unless X has the "Trait" trait + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsRelationship; + +/** Ensure that an entity is always used in pair as target. + * + * Behavior: + * + * @code + * e.add(T) panics + * e.add(T, X) panics + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsTarget; + +/** Can be added to relationship to indicate that it should never hold data, + * even when it or the relationship target is a component. */ +FLECS_API extern const ecs_entity_t EcsPairIsTag; + +/** Tag to indicate name identifier */ +FLECS_API extern const ecs_entity_t EcsName; + +/** Tag to indicate symbol identifier */ +FLECS_API extern const ecs_entity_t EcsSymbol; + +/** Tag to indicate alias identifier */ +FLECS_API extern const ecs_entity_t EcsAlias; + +/** Used to express parent-child relationships. */ +FLECS_API extern const ecs_entity_t EcsChildOf; + +/** Used to express inheritance relationships. */ +FLECS_API extern const ecs_entity_t EcsIsA; + +/** Used to express dependency relationships */ +FLECS_API extern const ecs_entity_t EcsDependsOn; + +/** Used to express a slot (used with prefab inheritance) */ +FLECS_API extern const ecs_entity_t EcsSlotOf; + +/** Tag added to module entities */ +FLECS_API extern const ecs_entity_t EcsModule; + +/** Tag to indicate an entity/component/system is private to a module */ +FLECS_API extern const ecs_entity_t EcsPrivate; + +/** Tag added to prefab entities. Any entity with this tag is automatically + * ignored by queries, unless #EcsPrefab is explicitly queried for. */ +FLECS_API extern const ecs_entity_t EcsPrefab; + +/** When this tag is added to an entity it is skipped by queries, unless + * #EcsDisabled is explicitly queried for. */ +FLECS_API extern const ecs_entity_t EcsDisabled; + +/** Trait added to entities that should never be returned by queries. Reserved + * for internal entities that have special meaning to the query engine, such as + * #EcsThis, #EcsWildcard, #EcsAny. */ +FLECS_API extern const ecs_entity_t EcsNotQueryable; + +/** Event that triggers when an id is added to an entity */ +FLECS_API extern const ecs_entity_t EcsOnAdd; + +/** Event that triggers when an id is removed from an entity */ +FLECS_API extern const ecs_entity_t EcsOnRemove; + +/** Event that triggers when a component is set for an entity */ +FLECS_API extern const ecs_entity_t EcsOnSet; + +/** Event that triggers observer when an entity starts/stops matching a query */ +FLECS_API extern const ecs_entity_t EcsMonitor; + +/** Event that triggers when a table is created. */ +FLECS_API extern const ecs_entity_t EcsOnTableCreate; + +/** Event that triggers when a table is deleted. */ +FLECS_API extern const ecs_entity_t EcsOnTableDelete; + +/** Event that triggers when a table becomes empty (doesn't emit on creation). */ +FLECS_API extern const ecs_entity_t EcsOnTableEmpty; + +/** Event that triggers when a table becomes non-empty. */ +FLECS_API extern const ecs_entity_t EcsOnTableFill; + +/** Relationship used for specifying cleanup behavior. */ +FLECS_API extern const ecs_entity_t EcsOnDelete; + +/** Relationship used to define what should happen when a target entity (second + * element of a pair) is deleted. */ +FLECS_API extern const ecs_entity_t EcsOnDeleteTarget; + +/** Remove cleanup policy. Must be used as target in pair with #EcsOnDelete or + * #EcsOnDeleteTarget. */ +FLECS_API extern const ecs_entity_t EcsRemove; + +/** Delete cleanup policy. Must be used as target in pair with #EcsOnDelete or + * #EcsOnDeleteTarget. */ +FLECS_API extern const ecs_entity_t EcsDelete; + +/** Panic cleanup policy. Must be used as target in pair with #EcsOnDelete or + * #EcsOnDeleteTarget. */ +FLECS_API extern const ecs_entity_t EcsPanic; + +/** Mark component as sparse */ +FLECS_API extern const ecs_entity_t EcsSparse; + +/** Mark relationship as union */ +FLECS_API extern const ecs_entity_t EcsUnion; + +/** Marker used to indicate `$var == ...` matching in queries. */ +FLECS_API extern const ecs_entity_t EcsPredEq; + +/** Marker used to indicate `$var == "name"` matching in queries. */ +FLECS_API extern const ecs_entity_t EcsPredMatch; + +/** Marker used to indicate `$var ~= "pattern"` matching in queries. */ +FLECS_API extern const ecs_entity_t EcsPredLookup; + +/** Marker used to indicate the start of a scope (`{`) in queries. */ +FLECS_API extern const ecs_entity_t EcsScopeOpen; + +/** Marker used to indicate the end of a scope (`}`) in queries. */ +FLECS_API extern const ecs_entity_t EcsScopeClose; + +/** Tag used to indicate query is empty. + * This tag is removed automatically when a query becomes non-empty, and is not + * automatically re-added when it becomes empty. + */ +FLECS_API extern const ecs_entity_t EcsEmpty; + +FLECS_API extern const ecs_entity_t ecs_id(EcsPipeline); /**< Pipeline component id. */ +FLECS_API extern const ecs_entity_t EcsOnStart; /**< OnStart pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPreFrame; /**< PreFrame pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnLoad; /**< OnLoad pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPostLoad; /**< PostLoad pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPreUpdate; /**< PreUpdate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnUpdate; /**< OnUpdate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnValidate; /**< OnValidate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPostUpdate; /**< PostUpdate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPreStore; /**< PreStore pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnStore; /**< OnStore pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPostFrame; /**< PostFrame pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPhase; /**< Phase pipeline phase. */ + +/** Value used to quickly check if component is builtin. This is used to quickly + * filter out tables with builtin components (for example for ecs_delete()) */ +#define EcsLastInternalComponentId (ecs_id(EcsPoly)) + +/** The first user-defined component starts from this id. Ids up to this number + * are reserved for builtin components */ +#define EcsFirstUserComponentId (8) + +/** The first user-defined entity starts from this id. Ids up to this number + * are reserved for builtin entities */ +#define EcsFirstUserEntityId (FLECS_HI_COMPONENT_ID + 128) + +/* When visualized the reserved id ranges look like this: + * - [1..8]: Builtin components + * - [9..FLECS_HI_COMPONENT_ID]: Low ids reserved for application components + * - [FLECS_HI_COMPONENT_ID + 1..EcsFirstUserEntityId]: Builtin entities + */ + +/** @} */ +/** @} */ + +/** + * @defgroup world_api World + * Functions for working with `ecs_world_t`. + * + * @{ + */ + +/** + * @defgroup world_creation_deletion Creation & Deletion + * @{ + */ + +/** Create a new world. + * This operation automatically imports modules from addons Flecs has been built + * with, except when the module specifies otherwise. + * + * @return A new world + */ +FLECS_API +ecs_world_t* ecs_init(void); + +/** Create a new world with just the core module. + * Same as ecs_init(), but doesn't import modules from addons. This operation is + * faster than ecs_init() and results in less memory utilization. + * + * @return A new tiny world + */ +FLECS_API +ecs_world_t* ecs_mini(void); + +/** Create a new world with arguments. + * Same as ecs_init(), but allows passing in command line arguments. Command line + * arguments are used to: + * - automatically derive the name of the application from argv[0] + * + * @return A new world + */ +FLECS_API +ecs_world_t* ecs_init_w_args( + int argc, + char *argv[]); + +/** Delete a world. + * This operation deletes the world, and everything it contains. + * + * @param world The world to delete. + * @return Zero if successful, non-zero if failed. + */ +FLECS_API +int ecs_fini( + ecs_world_t *world); + +/** Returns whether the world is being deleted. + * This operation can be used in callbacks like type hooks or observers to + * detect if they are invoked while the world is being deleted. + * + * @param world The world. + * @return True if being deleted, false if not. + */ +FLECS_API +bool ecs_is_fini( + const ecs_world_t *world); + +/** Register action to be executed when world is destroyed. + * Fini actions are typically used when a module needs to clean up before a + * world shuts down. + * + * @param world The world. + * @param action The function to execute. + * @param ctx Userdata to pass to the function */ +FLECS_API +void ecs_atfini( + ecs_world_t *world, + ecs_fini_action_t action, + void *ctx); + +/** Type returned by ecs_get_entities(). */ +typedef struct ecs_entities_t { + const ecs_entity_t *ids; /**< Array with all entity ids in the world. */ + int32_t count; /**< Total number of entity ids. */ + int32_t alive_count; /**< Number of alive entity ids. */ +} ecs_entities_t; + +/** Return entity identifiers in world. + * This operation returns an array with all entity ids that exist in the world. + * Note that the returned array will change and may get invalidated as a result + * of entity creation & deletion. + * + * To iterate all alive entity ids, do: + * @code + * ecs_entities_t entities = ecs_get_entities(world); + * for (int i = 0; i < entities.alive_count; i ++) { + * ecs_entity_t id = entities.ids[i]; + * } + * @endcode + * + * To iterate not-alive ids, do: + * @code + * for (int i = entities.alive_count + 1; i < entities.count; i ++) { + * ecs_entity_t id = entities.ids[i]; + * } + * @endcode + * + * The returned array does not need to be freed. Mutating the returned array + * will return in undefined behavior (and likely crashes). + * + * @param world The world. + * @return Struct with entity id array. + */ +FLECS_API +ecs_entities_t ecs_get_entities( + const ecs_world_t *world); + +/** Get flags set on the world. + * This operation returns the internal flags (see api_flags.h) that are + * set on the world. + * + * @param world The world. + * @return Flags set on the world. + */ +FLECS_API +ecs_flags32_t ecs_world_get_flags( + const ecs_world_t *world); + +/** @} */ + +/** + * @defgroup world_frame Frame functions + * @{ + */ + +/** Begin frame. + * When an application does not use ecs_progress() to control the main loop, it + * can still use Flecs features such as FPS limiting and time measurements. This + * operation needs to be invoked whenever a new frame is about to get processed. + * + * Calls to ecs_frame_begin() must always be followed by ecs_frame_end(). + * + * The function accepts a delta_time parameter, which will get passed to + * systems. This value is also used to compute the amount of time the function + * needs to sleep to ensure it does not exceed the target_fps, when it is set. + * When 0 is provided for delta_time, the time will be measured. + * + * This function should only be ran from the main thread. + * + * @param world The world. + * @param delta_time Time elapsed since the last frame. + * @return The provided delta_time, or measured time if 0 was provided. + */ +FLECS_API +ecs_ftime_t ecs_frame_begin( + ecs_world_t *world, + ecs_ftime_t delta_time); + +/** End frame. + * This operation must be called at the end of the frame, and always after + * ecs_frame_begin(). + * + * @param world The world. + */ +FLECS_API +void ecs_frame_end( + ecs_world_t *world); + +/** Register action to be executed once after frame. + * Post frame actions are typically used for calling operations that cannot be + * invoked during iteration, such as changing the number of threads. + * + * @param world The world. + * @param action The function to execute. + * @param ctx Userdata to pass to the function */ +FLECS_API +void ecs_run_post_frame( + ecs_world_t *world, + ecs_fini_action_t action, + void *ctx); + +/** Signal exit + * This operation signals that the application should quit. It will cause + * ecs_progress() to return false. + * + * @param world The world to quit. + */ +FLECS_API +void ecs_quit( + ecs_world_t *world); + +/** Return whether a quit has been requested. + * + * @param world The world. + * @return Whether a quit has been requested. + * @see ecs_quit() + */ +FLECS_API +bool ecs_should_quit( + const ecs_world_t *world); + +/** Measure frame time. + * Frame time measurements measure the total time passed in a single frame, and + * how much of that time was spent on systems and on merging. + * + * Frame time measurements add a small constant-time overhead to an application. + * When an application sets a target FPS, frame time measurements are enabled by + * default. + * + * @param world The world. + * @param enable Whether to enable or disable frame time measuring. + */ +FLECS_API void ecs_measure_frame_time( + ecs_world_t *world, + bool enable); + +/** Measure system time. + * System time measurements measure the time spent in each system. + * + * System time measurements add overhead to every system invocation and + * therefore have a small but measurable impact on application performance. + * System time measurements must be enabled before obtaining system statistics. + * + * @param world The world. + * @param enable Whether to enable or disable system time measuring. + */ +FLECS_API void ecs_measure_system_time( + ecs_world_t *world, + bool enable); + +/** Set target frames per second (FPS) for application. + * Setting the target FPS ensures that ecs_progress() is not invoked faster than + * the specified FPS. When enabled, ecs_progress() tracks the time passed since + * the last invocation, and sleeps the remaining time of the frame (if any). + * + * This feature ensures systems are ran at a consistent interval, as well as + * conserving CPU time by not running systems more often than required. + * + * Note that ecs_progress() only sleeps if there is time left in the frame. Both + * time spent in flecs as time spent outside of flecs are taken into + * account. + * + * @param world The world. + * @param fps The target FPS. + */ +FLECS_API +void ecs_set_target_fps( + ecs_world_t *world, + ecs_ftime_t fps); + +/** Set default query flags. + * Set a default value for the ecs_filter_desc_t::flags field. Default flags + * are applied in addition to the flags provided in the descriptor. For a + * list of available flags, see include/flecs/private/api_flags.h. Typical flags + * to use are: + * + * - `EcsQueryMatchEmptyTables` + * - `EcsQueryMatchDisabled` + * - `EcsQueryMatchPrefab` + * + * @param world The world. + * @param flags The query flags. + */ +FLECS_API +void ecs_set_default_query_flags( + ecs_world_t *world, + ecs_flags32_t flags); + +/** @} */ + +/** + * @defgroup commands Commands + * @{ + */ + +/** Begin readonly mode. + * This operation puts the world in readonly mode, which disallows mutations on + * the world. Readonly mode exists so that internal mechanisms can implement + * optimizations that certain aspects of the world to not change, while also + * providing a mechanism for applications to prevent accidental mutations in, + * for example, multithreaded applications. + * + * Readonly mode is a stronger version of deferred mode. In deferred mode + * ECS operations such as add/remove/set/delete etc. are added to a command + * queue to be executed later. In readonly mode, operations that could break + * scheduler logic (such as creating systems, queries) are also disallowed. + * + * Readonly mode itself has a single threaded and a multi threaded mode. In + * single threaded mode certain mutations on the world are still allowed, for + * example: + * - Entity liveliness operations (such as new, make_alive), so that systems are + * able to create new entities. + * - Implicit component registration, so that this works from systems + * - Mutations to supporting data structures for the evaluation of uncached + * queries (filters), so that these can be created on the fly. + * + * These mutations are safe in a single threaded applications, but for + * multithreaded applications the world needs to be entirely immutable. For this + * purpose multi threaded readonly mode exists, which disallows all mutations on + * the world. This means that in multi threaded applications, entity liveliness + * operations, implicit component registration, and on-the-fly query creation + * are not guaranteed to work. + * + * While in readonly mode, applications can still enqueue ECS operations on a + * stage. Stages are managed automatically when using the pipeline addon and + * ecs_progress(), but they can also be configured manually as shown here: + * + * @code + * // Number of stages typically corresponds with number of threads + * ecs_set_stage_count(world, 2); + * ecs_stage_t *stage = ecs_get_stage(world, 1); + * + * ecs_readonly_begin(world); + * ecs_add(world, e, Tag); // readonly assert + * ecs_add(stage, e, Tag); // OK + * @endcode + * + * When an attempt is made to perform an operation on a world in readonly mode, + * the code will throw an assert saying that the world is in readonly mode. + * + * A call to ecs_readonly_begin() must be followed up with ecs_readonly_end(). + * When ecs_readonly_end() is called, all enqueued commands from configured + * stages are merged back into the world. Calls to ecs_readonly_begin() and + * ecs_readonly_end() should always happen from a context where the code has + * exclusive access to the world. The functions themselves are not thread safe. + * + * In a typical application, a (non-exhaustive) call stack that uses + * ecs_readonly_begin() and ecs_readonly_end() will look like this: + * + * @code + * ecs_progress() + * ecs_readonly_begin() + * ecs_defer_begin() + * + * // user code + * + * ecs_readonly_end() + * ecs_defer_end() + *@endcode + * + * @param world The world + * @param multi_threaded Whether to enable readonly/multi threaded mode. + * @return Whether world is in readonly mode. + */ +FLECS_API +bool ecs_readonly_begin( + ecs_world_t *world, + bool multi_threaded); + +/** End readonly mode. + * This operation ends readonly mode, and must be called after + * ecs_readonly_begin(). Operations that were deferred while the world was in + * readonly mode will be flushed. + * + * @param world The world + */ +FLECS_API +void ecs_readonly_end( + ecs_world_t *world); + +/** Merge world or stage. + * When automatic merging is disabled, an application can call this + * operation on either an individual stage, or on the world which will merge + * all stages. This operation may only be called when staging is not enabled + * (either after ecs_progress() or after ecs_readonly_end()). + * + * This operation may be called on an already merged stage or world. + * + * @param world The world. + */ +FLECS_API +void ecs_merge( + ecs_world_t *world); + +/** Defer operations until end of frame. + * When this operation is invoked while iterating, operations inbetween the + * ecs_defer_begin() and ecs_defer_end() operations are executed at the end + * of the frame. + * + * This operation is thread safe. + * + * @param world The world. + * @return true if world changed from non-deferred mode to deferred mode. + * + * @see ecs_defer_end() + * @see ecs_is_deferred() + * @see ecs_defer_resume() + * @see ecs_defer_suspend() + */ +FLECS_API +bool ecs_defer_begin( + ecs_world_t *world); + +/** Test if deferring is enabled for current stage. + * + * @param world The world. + * @return True if deferred, false if not. + * + * @see ecs_defer_begin() + * @see ecs_defer_end() + * @see ecs_defer_resume() + * @see ecs_defer_suspend() + */ +FLECS_API +bool ecs_is_deferred( + const ecs_world_t *world); + +/** End block of operations to defer. + * See ecs_defer_begin(). + * + * This operation is thread safe. + * + * @param world The world. + * @return true if world changed from deferred mode to non-deferred mode. + * + * @see ecs_defer_begin() + * @see ecs_defer_is_deferred() + * @see ecs_defer_resume() + * @see ecs_defer_suspend() + */ +FLECS_API +bool ecs_defer_end( + ecs_world_t *world); + +/** Suspend deferring but do not flush queue. + * This operation can be used to do an undeferred operation while not flushing + * the operations in the queue. + * + * An application should invoke ecs_defer_resume() before ecs_defer_end() is called. + * The operation may only be called when deferring is enabled. + * + * @param world The world. + * + * @see ecs_defer_begin() + * @see ecs_defer_end() + * @see ecs_defer_is_deferred() + * @see ecs_defer_resume() + */ +FLECS_API +void ecs_defer_suspend( + ecs_world_t *world); + +/** Resume deferring. + * See ecs_defer_suspend(). + * + * @param world The world. + * + * @see ecs_defer_begin() + * @see ecs_defer_end() + * @see ecs_defer_is_deferred() + * @see ecs_defer_suspend() + */ +FLECS_API +void ecs_defer_resume( + ecs_world_t *world); + +/** Configure world to have N stages. + * This initializes N stages, which allows applications to defer operations to + * multiple isolated defer queues. This is typically used for applications with + * multiple threads, where each thread gets its own queue, and commands are + * merged when threads are synchronized. + * + * Note that the ecs_set_threads() function already creates the appropriate + * number of stages. The ecs_set_stage_count() operation is useful for applications + * that want to manage their own stages and/or threads. + * + * @param world The world. + * @param stages The number of stages. + */ +FLECS_API +void ecs_set_stage_count( + ecs_world_t *world, + int32_t stages); + +/** Get number of configured stages. + * Return number of stages set by ecs_set_stage_count(). + * + * @param world The world. + * @return The number of stages used for threading. + */ +FLECS_API +int32_t ecs_get_stage_count( + const ecs_world_t *world); + +/** Get stage-specific world pointer. + * Flecs threads can safely invoke the API as long as they have a private + * context to write to, also referred to as the stage. This function returns a + * pointer to a stage, disguised as a world pointer. + * + * Note that this function does not(!) create a new world. It simply wraps the + * existing world in a thread-specific context, which the API knows how to + * unwrap. The reason the stage is returned as an ecs_world_t is so that it + * can be passed transparently to the existing API functions, vs. having to + * create a dedicated API for threading. + * + * @param world The world. + * @param stage_id The index of the stage to retrieve. + * @return A thread-specific pointer to the world. + */ +FLECS_API +ecs_world_t* ecs_get_stage( + const ecs_world_t *world, + int32_t stage_id); + +/** Test whether the current world is readonly. + * This function allows the code to test whether the currently used world + * is readonly or whether it allows for writing. + * + * @param world A pointer to a stage or the world. + * @return True if the world or stage is readonly. + */ +FLECS_API +bool ecs_stage_is_readonly( + const ecs_world_t *world); + +/** Create unmanaged stage. + * Create a stage whose lifecycle is not managed by the world. Must be freed + * with ecs_stage_free(). + * + * @param world The world. + * @return The stage. + */ +FLECS_API +ecs_world_t* ecs_stage_new( + ecs_world_t *world); + +/** Free unmanaged stage. + * + * @param stage The stage to free. + */ +FLECS_API +void ecs_stage_free( + ecs_world_t *stage); + +/** Get stage id. + * The stage id can be used by an application to learn about which stage it is + * using, which typically corresponds with the worker thread id. + * + * @param world The world. + * @return The stage id. + */ +FLECS_API +int32_t ecs_stage_get_id( + const ecs_world_t *world); + +/** @} */ + +/** + * @defgroup world_misc Misc + * @{ + */ + +/** Set a world context. + * This operation allows an application to register custom data with a world + * that can be accessed anywhere where the application has the world. + * + * @param world The world. + * @param ctx A pointer to a user defined structure. + * @param ctx_free A function that is invoked with ctx when the world is freed. + */ +FLECS_API +void ecs_set_ctx( + ecs_world_t *world, + void *ctx, + ecs_ctx_free_t ctx_free); + +/** Set a world binding context. + * Same as ecs_set_ctx() but for binding context. A binding context is intended + * specifically for language bindings to store binding specific data. + * + * @param world The world. + * @param ctx A pointer to a user defined structure. + * @param ctx_free A function that is invoked with ctx when the world is freed. + */ +FLECS_API +void ecs_set_binding_ctx( + ecs_world_t *world, + void *ctx, + ecs_ctx_free_t ctx_free); + +/** Get the world context. + * This operation retrieves a previously set world context. + * + * @param world The world. + * @return The context set with ecs_set_ctx(). If no context was set, the + * function returns NULL. + */ +FLECS_API +void* ecs_get_ctx( + const ecs_world_t *world); + +/** Get the world binding context. + * This operation retrieves a previously set world binding context. + * + * @param world The world. + * @return The context set with ecs_set_binding_ctx(). If no context was set, the + * function returns NULL. + */ +FLECS_API +void* ecs_get_binding_ctx( + const ecs_world_t *world); + +/** Get build info. + * Returns information about the current Flecs build. + * + * @return A struct with information about the current Flecs build. + */ +FLECS_API +const ecs_build_info_t* ecs_get_build_info(void); + +/** Get world info. + * + * @param world The world. + * @return Pointer to the world info. Valid for as long as the world exists. + */ +FLECS_API +const ecs_world_info_t* ecs_get_world_info( + const ecs_world_t *world); + +/** Dimension the world for a specified number of entities. + * This operation will preallocate memory in the world for the specified number + * of entities. Specifying a number lower than the current number of entities in + * the world will have no effect. + * + * @param world The world. + * @param entity_count The number of entities to preallocate. + */ +FLECS_API +void ecs_dim( + ecs_world_t *world, + int32_t entity_count); + +/** Set a range for issuing new entity ids. + * This function constrains the entity identifiers returned by ecs_new_w() to the + * specified range. This operation can be used to ensure that multiple processes + * can run in the same simulation without requiring a central service that + * coordinates issuing identifiers. + * + * If `id_end` is set to 0, the range is infinite. If `id_end` is set to a non-zero + * value, it has to be larger than `id_start`. If `id_end` is set and ecs_new() is + * invoked after an id is issued that is equal to `id_end`, the application will + * abort. + * + * @param world The world. + * @param id_start The start of the range. + * @param id_end The end of the range. + */ +FLECS_API +void ecs_set_entity_range( + ecs_world_t *world, + ecs_entity_t id_start, + ecs_entity_t id_end); + +/** Enable/disable range limits. + * When an application is both a receiver of range-limited entities and a + * producer of range-limited entities, range checking needs to be temporarily + * disabled when inserting received entities. Range checking is disabled on a + * stage, so setting this value is thread safe. + * + * @param world The world. + * @param enable True if range checking should be enabled, false to disable. + * @return The previous value. + */ +FLECS_API +bool ecs_enable_range_check( + ecs_world_t *world, + bool enable); + +/** Get the largest issued entity id (not counting generation). + * + * @param world The world. + * @return The largest issued entity id. + */ +FLECS_API +ecs_entity_t ecs_get_max_id( + const ecs_world_t *world); + +/** Force aperiodic actions. + * The world may delay certain operations until they are necessary for the + * application to function correctly. This may cause observable side effects + * such as delayed triggering of events, which can be inconvenient when for + * example running a test suite. + * + * The flags parameter specifies which aperiodic actions to run. Specify 0 to + * run all actions. Supported flags start with 'EcsAperiodic'. Flags identify + * internal mechanisms and may change unannounced. + * + * @param world The world. + * @param flags The flags specifying which actions to run. + */ +FLECS_API +void ecs_run_aperiodic( + ecs_world_t *world, + ecs_flags32_t flags); + +/** Cleanup empty tables. + * This operation cleans up empty tables that meet certain conditions. Having + * large amounts of empty tables does not negatively impact performance of the + * ECS, but can take up considerable amounts of memory, especially in + * applications with many components, and many components per entity. + * + * The generation specifies the minimum number of times this operation has + * to be called before an empty table is cleaned up. If a table becomes non + * empty, the generation is reset. + * + * The operation allows for both a "clear" generation and a "delete" + * generation. When the clear generation is reached, the table's + * resources are freed (like component arrays) but the table itself is not + * deleted. When the delete generation is reached, the empty table is deleted. + * + * By specifying a non-zero id the cleanup logic can be limited to tables with + * a specific (component) id. The operation will only increase the generation + * count of matching tables. + * + * The min_id_count specifies a lower bound for the number of components a table + * should have. Often the more components a table has, the more specific it is + * and therefore less likely to be reused. + * + * The time budget specifies how long the operation should take at most. + * + * @param world The world. + * @param id Optional component filter for the tables to evaluate. + * @param clear_generation Free table data when generation > clear_generation. + * @param delete_generation Delete table when generation > delete_generation. + * @param min_id_count Minimum number of component ids the table should have. + * @param time_budget_seconds Amount of time operation is allowed to spend. + * @return Number of deleted tables. + */ +FLECS_API +int32_t ecs_delete_empty_tables( + ecs_world_t *world, + ecs_id_t id, + uint16_t clear_generation, + uint16_t delete_generation, + int32_t min_id_count, + double time_budget_seconds); + +/** Get world from poly. + * + * @param poly A pointer to a poly object. + * @return The world. + */ +FLECS_API +const ecs_world_t* ecs_get_world( + const ecs_poly_t *poly); + +/** Get entity from poly. + * + * @param poly A pointer to a poly object. + * @return Entity associated with the poly object. + */ +FLECS_API +ecs_entity_t ecs_get_entity( + const ecs_poly_t *poly); + +/** Test if pointer is of specified type. + * Usage: + * + * @code + * flecs_poly_is(ptr, ecs_world_t) + * @endcode + * + * This operation only works for poly types. + * + * @param object The object to test. + * @param type The id of the type. + * @return True if the pointer is of the specified type. + */ +FLECS_API +bool flecs_poly_is_( + const ecs_poly_t *object, + int32_t type); + +/** Test if pointer is of specified type. + * @see flecs_poly_is_() + */ +#define flecs_poly_is(object, type)\ + flecs_poly_is_(object, type##_magic) + +/** Make a pair id. + * This function is equivalent to using the ecs_pair() macro, and is added for + * convenience to make it easier for non C/C++ bindings to work with pairs. + * + * @param first The first element of the pair of the pair. + * @param second The target of the pair. + * @return A pair id. + */ +FLECS_API +ecs_id_t ecs_make_pair( + ecs_entity_t first, + ecs_entity_t second); + +/** @} */ + +/** @} */ + +/** + * @defgroup entities Entities + * Functions for working with `ecs_entity_t`. + * + * @{ + */ + +/** + * @defgroup creating_entities Creating & Deleting + * Functions for creating and deleting entities. + * + * @{ + */ + +/** Create new entity id. + * This operation returns an unused entity id. This operation is guaranteed to + * return an empty entity as it does not use values set by ecs_set_scope() or + * ecs_set_with(). + * + * @param world The world. + * @return The new entity id. + */ +FLECS_API +ecs_entity_t ecs_new( + ecs_world_t *world); + +/** Create new low id. + * This operation returns a new low id. Entity ids start after the + * FLECS_HI_COMPONENT_ID constant. This reserves a range of low ids for things + * like components, and allows parts of the code to optimize operations. + * + * Note that FLECS_HI_COMPONENT_ID does not represent the maximum number of + * components that can be created, only the maximum number of components that + * can take advantage of these optimizations. + * + * This operation is guaranteed to return an empty entity as it does not use + * values set by ecs_set_scope() or ecs_set_with(). + * + * This operation does not recycle ids. + * + * @param world The world. + * @return The new component id. + */ +FLECS_API +ecs_entity_t ecs_new_low_id( + ecs_world_t *world); + +/** Create new entity with (component) id. + * This operation creates a new entity with an optional (component) id. When 0 + * is passed to the id parameter, no component is added to the new entity. + * + * @param world The world. + * @param id The component id to initialize the new entity with. + * @return The new entity. + */ +FLECS_API +ecs_entity_t ecs_new_w_id( + ecs_world_t *world, + ecs_id_t id); + +/** Create new entity in table. + * This operation creates a new entity in the specified table. + * + * @param world The world. + * @param table The table to which to add the new entity. + * @return The new entity. + */ +FLECS_API +ecs_entity_t ecs_new_w_table( + ecs_world_t *world, + ecs_table_t *table); + +/** Find or create an entity. + * This operation creates a new entity, or modifies an existing one. When a name + * is set in the ecs_entity_desc_t::name field and ecs_entity_desc_t::entity is + * not set, the operation will first attempt to find an existing entity by that + * name. If no entity with that name can be found, it will be created. + * + * If both a name and entity handle are provided, the operation will check if + * the entity name matches with the provided name. If the names do not match, + * the function will fail and return 0. + * + * If an id to a non-existing entity is provided, that entity id become alive. + * + * See the documentation of ecs_entity_desc_t for more details. + * + * @param world The world. + * @param desc Entity init parameters. + * @return A handle to the new or existing entity, or 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_entity_init( + ecs_world_t *world, + const ecs_entity_desc_t *desc); + +/** Bulk create/populate new entities. + * This operation bulk inserts a list of new or predefined entities into a + * single table. + * + * The operation does not take ownership of component arrays provided by the + * application. Components that are non-trivially copyable will be moved into + * the storage. + * + * The operation will emit OnAdd events for each added id, and OnSet events for + * each component that has been set. + * + * If no entity ids are provided by the application, the returned array of ids + * points to an internal data structure which changes when new entities are + * created/deleted. + * + * If as a result of the operation triggers are invoked that deletes + * entities and no entity ids were provided by the application, the returned + * array of identifiers may be incorrect. To avoid this problem, an application + * can first call ecs_bulk_init() to create empty entities, copy the array to one + * that is owned by the application, and then use this array to populate the + * entities. + * + * @param world The world. + * @param desc Bulk creation parameters. + * @return Array with the list of entity ids created/populated. + */ +FLECS_API +const ecs_entity_t* ecs_bulk_init( + ecs_world_t *world, + const ecs_bulk_desc_t *desc); + +/** Create N new entities. + * This operation is the same as ecs_new_w_id(), but creates N entities + * instead of one. + * + * @param world The world. + * @param id The component id to create the entities with. + * @param count The number of entities to create. + * @return The first entity id of the newly created entities. + */ +FLECS_API +const ecs_entity_t* ecs_bulk_new_w_id( + ecs_world_t *world, + ecs_id_t id, + int32_t count); + +/** Clone an entity + * This operation clones the components of one entity into another entity. If + * no destination entity is provided, a new entity will be created. Component + * values are not copied unless copy_value is true. + * + * If the source entity has a name, it will not be copied to the destination + * entity. This is to prevent having two entities with the same name under the + * same parent, which is not allowed. + * + * @param world The world. + * @param dst The entity to copy the components to. + * @param src The entity to copy the components from. + * @param copy_value If true, the value of components will be copied to dst. + * @return The destination entity. + */ +FLECS_API +ecs_entity_t ecs_clone( + ecs_world_t *world, + ecs_entity_t dst, + ecs_entity_t src, + bool copy_value); + +/** Delete an entity. + * This operation will delete an entity and all of its components. The entity id + * will be made available for recycling. If the entity passed to ecs_delete() is + * not alive, the operation will have no side effects. + * + * @param world The world. + * @param entity The entity. + */ +FLECS_API +void ecs_delete( + ecs_world_t *world, + ecs_entity_t entity); + +/** Delete all entities with the specified id. + * This will delete all entities (tables) that have the specified id. The id + * may be a wildcard and/or a pair. + * + * @param world The world. + * @param id The id. + */ +FLECS_API +void ecs_delete_with( + ecs_world_t *world, + ecs_id_t id); + +/** @} */ + +/** + * @defgroup adding_removing Adding & Removing + * Functions for adding and removing components. + * + * @{ + */ + +/** Add a (component) id to an entity. + * This operation adds a single (component) id to an entity. If the entity + * already has the id, this operation will have no side effects. + * + * @param world The world. + * @param entity The entity. + * @param id The id to add. + */ +FLECS_API +void ecs_add_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Remove a (component) id from an entity. + * This operation removes a single (component) id to an entity. If the entity + * does not have the id, this operation will have no side effects. + * + * @param world The world. + * @param entity The entity. + * @param id The id to remove. + */ +FLECS_API +void ecs_remove_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Add auto override for (component) id. + * An auto override is a component that is automatically added to an entity when + * it is instantiated from a prefab. Auto overrides are added to the entity that + * is inherited from (usually a prefab). For example: + * + * @code + * ecs_entity_t prefab = ecs_insert(world, + * ecs_value(Position, {10, 20}), + * ecs_value(Mass, {100})); + * + * ecs_auto_override(world, prefab, Position); + * + * ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + * assert(ecs_owns(world, inst, Position)); // true + * assert(ecs_owns(world, inst, Mass)); // false + * @endcode + * + * An auto override is equivalent to a manual override: + * + * @code + * ecs_entity_t prefab = ecs_insert(world, + * ecs_value(Position, {10, 20}), + * ecs_value(Mass, {100})); + * + * ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + * assert(ecs_owns(world, inst, Position)); // false + * ecs_add(world, inst, Position); // manual override + * assert(ecs_owns(world, inst, Position)); // true + * assert(ecs_owns(world, inst, Mass)); // false + * @endcode + * + * This operation is equivalent to manually adding the id with the AUTO_OVERRIDE + * bit applied: + * + * @code + * ecs_add_id(world, entity, ECS_AUTO_OVERRIDE | id); + * @endcode + * + * When a component is overridden and inherited from a prefab, the value from + * the prefab component is copied to the instance. When the component is not + * inherited from a prefab, it is added to the instance as if using ecs_add_id(). + * + * Overriding is the default behavior on prefab instantiation. Auto overriding + * is only useful for components with the `(OnInstantiate, Inherit)` trait. + * When a component has the `(OnInstantiate, DontInherit)` trait and is overridden + * the component is added, but the value from the prefab will not be copied. + * + * @param world The world. + * @param entity The entity. + * @param id The (component) id to auto override. + */ +FLECS_API +void ecs_auto_override_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Clear all components. + * This operation will remove all components from an entity. + * + * @param world The world. + * @param entity The entity. + */ +FLECS_API +void ecs_clear( + ecs_world_t *world, + ecs_entity_t entity); + +/** Remove all instances of the specified (component) id. + * This will remove the specified id from all entities (tables). The id may be + * a wildcard and/or a pair. + * + * @param world The world. + * @param id The id. + */ +FLECS_API +void ecs_remove_all( + ecs_world_t *world, + ecs_id_t id); + +/** Set current with id. + * New entities are automatically created with the specified id. + * + * @param world The world. + * @param id The id. + * @return The previous id. + */ +FLECS_API +ecs_entity_t ecs_set_with( + ecs_world_t *world, + ecs_id_t id); + +/** Get current with id. + * Get the id set with ecs_set_with(). + * + * @param world The world. + * @return The last id provided to ecs_set_with(). + */ +FLECS_API +ecs_id_t ecs_get_with( + const ecs_world_t *world); + +/** @} */ + +/** + * @defgroup enabling_disabling Enabling & Disabling + * Functions for enabling/disabling entities and components. + * + * @{ + */ + +/** Enable or disable entity. + * This operation enables or disables an entity by adding or removing the + * #EcsDisabled tag. A disabled entity will not be matched with any systems, + * unless the system explicitly specifies the #EcsDisabled tag. + * + * @param world The world. + * @param entity The entity to enable or disable. + * @param enabled true to enable the entity, false to disable. + */ +FLECS_API +void ecs_enable( + ecs_world_t *world, + ecs_entity_t entity, + bool enabled); + +/** Enable or disable component. + * Enabling or disabling a component does not add or remove a component from an + * entity, but prevents it from being matched with queries. This operation can + * be useful when a component must be temporarily disabled without destroying + * its value. It is also a more performant operation for when an application + * needs to add/remove components at high frequency, as enabling/disabling is + * cheaper than a regular add or remove. + * + * @param world The world. + * @param entity The entity. + * @param id The component. + * @param enable True to enable the component, false to disable. + */ +FLECS_API +void ecs_enable_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id, + bool enable); + +/** Test if component is enabled. + * Test whether a component is currently enabled or disabled. This operation + * will return true when the entity has the component and if it has not been + * disabled by ecs_enable_component(). + * + * @param world The world. + * @param entity The entity. + * @param id The component. + * @return True if the component is enabled, otherwise false. + */ +FLECS_API +bool ecs_is_enabled_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** @} */ + +/** + * @defgroup getting Getting & Setting + * Functions for getting/setting components. + * + * @{ + */ + +/** Get an immutable pointer to a component. + * This operation obtains a const pointer to the requested component. The + * operation accepts the component entity id. + * + * This operation can return inherited components reachable through an `IsA` + * relationship. + * + * @param world The world. + * @param entity The entity. + * @param id The id of the component to get. + * @return The component pointer, NULL if the entity does not have the component. + * + * @see ecs_get_mut_id() + */ +FLECS_API +const void* ecs_get_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Get a mutable pointer to a component. + * This operation obtains a mutable pointer to the requested component. The + * operation accepts the component entity id. + * + * Unlike ecs_get_id(), this operation does not return inherited components. + * + * @param world The world. + * @param entity The entity. + * @param id The id of the component to get. + * @return The component pointer, NULL if the entity does not have the component. + */ +FLECS_API +void* ecs_get_mut_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Get a mutable pointer to a component. + * This operation returns a mutable pointer to a component. If the component did + * not yet exist, it will be added. + * + * If ensure 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. + * @param id The entity id of the component to obtain. + * @return The component pointer. + * + * @see ecs_ensure_modified_id() + * @see ecs_emplace_id() + */ +FLECS_API +void* ecs_ensure_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Combines ensure + modified in single operation. + * This operation is a more efficient alternative to calling ecs_ensure_id() and + * ecs_modified_id() separately. This operation is only valid when the world is in + * deferred mode, which ensures that the Modified event is not emitted before + * the modification takes place. + * + * @param world The world. + * @param entity The entity. + * @param id The id of the component to obtain. + * @return The component pointer. + */ +FLECS_API +void* ecs_ensure_modified_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Create a component ref. + * A ref is a handle to an entity + component which caches a small amount of + * data to reduce overhead of repeatedly accessing the component. Use + * ecs_ref_get() to get the component data. + * + * @param world The world. + * @param entity The entity. + * @param id The id of the component. + * @return The reference. + */ +FLECS_API +ecs_ref_t ecs_ref_init_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Get component from ref. + * Get component pointer from ref. The ref must be created with ecs_ref_init(). + * + * @param world The world. + * @param ref The ref. + * @param id The component id. + * @return The component pointer, NULL if the entity does not have the component. + */ +FLECS_API +void* ecs_ref_get_id( + const ecs_world_t *world, + ecs_ref_t *ref, + ecs_id_t id); + +/** Update ref. + * Ensures contents of ref are up to date. Same as ecs_ref_get_id(), but does not + * return pointer to component id. + * + * @param world The world. + * @param ref The ref. + */ +FLECS_API +void ecs_ref_update( + const ecs_world_t *world, + ecs_ref_t *ref); + +/** Find record for entity. + * An entity record contains the table and row for the entity. + * + * @param world The world. + * @param entity The entity. + * @return The record, NULL if the entity does not exist. + */ +FLECS_API +ecs_record_t* ecs_record_find( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Begin exclusive write access to entity. + * This operation provides safe exclusive access to the components of an entity + * without the overhead of deferring operations. + * + * When this operation is called simultaneously for the same entity more than + * once it will throw an assert. Note that for this to happen, asserts must be + * enabled. It is up to the application to ensure that access is exclusive, for + * example by using a read-write mutex. + * + * Exclusive access is enforced at the table level, so only one entity can be + * exclusively accessed per table. The exclusive access check is thread safe. + * + * This operation must be followed up with ecs_write_end(). + * + * @param world The world. + * @param entity The entity. + * @return A record to the entity. + */ +FLECS_API +ecs_record_t* ecs_write_begin( + ecs_world_t *world, + ecs_entity_t entity); + +/** End exclusive write access to entity. + * This operation ends exclusive access, and must be called after + * ecs_write_begin(). + * + * @param record Record to the entity. + */ +FLECS_API +void ecs_write_end( + ecs_record_t *record); + +/** Begin read access to entity. + * This operation provides safe read access to the components of an entity. + * Multiple simultaneous reads are allowed per entity. + * + * This operation ensures that code attempting to mutate the entity's table will + * throw an assert. Note that for this to happen, asserts must be enabled. It is + * up to the application to ensure that this does not happen, for example by + * using a read-write mutex. + * + * This operation does *not* provide the same guarantees as a read-write mutex, + * as it is possible to call ecs_read_begin() after calling ecs_write_begin(). It is + * up to application has to ensure that this does not happen. + * + * This operation must be followed up with ecs_read_end(). + * + * @param world The world. + * @param entity The entity. + * @return A record to the entity. + */ +FLECS_API +const ecs_record_t* ecs_read_begin( + ecs_world_t *world, + ecs_entity_t entity); + +/** End read access to entity. + * This operation ends read access, and must be called after ecs_read_begin(). + * + * @param record Record to the entity. + */ +FLECS_API +void ecs_read_end( + const ecs_record_t *record); + +/** Get entity corresponding with record. + * This operation only works for entities that are not empty. + * + * @param record The record for which to obtain the entity id. + * @return The entity id for the record. + */ +FLECS_API +ecs_entity_t ecs_record_get_entity( + const ecs_record_t *record); + +/** Get component from entity record. + * This operation returns a pointer to a component for the entity + * associated with the provided record. For safe access to the component, obtain + * the record with ecs_read_begin() or ecs_write_begin(). + * + * Obtaining a component from a record is faster than obtaining it from the + * entity handle, as it reduces the number of lookups required. + * + * @param world The world. + * @param record Record to the entity. + * @param id The (component) id. + * @return Pointer to component, or NULL if entity does not have the component. + * + * @see ecs_record_ensure_id() + */ +FLECS_API +const void* ecs_record_get_id( + const ecs_world_t *world, + const ecs_record_t *record, + ecs_id_t id); + +/** Same as ecs_record_get_id(), but returns a mutable pointer. + * For safe access to the component, obtain the record with ecs_write_begin(). + * + * @param world The world. + * @param record Record to the entity. + * @param id The (component) id. + * @return Pointer to component, or NULL if entity does not have the component. + */ +FLECS_API +void* ecs_record_ensure_id( + ecs_world_t *world, + ecs_record_t *record, + ecs_id_t id); + +/** Test if entity for record has a (component) id. + * + * @param world The world. + * @param record Record to the entity. + * @param id The (component) id. + * @return Whether the entity has the component. + */ +FLECS_API +bool ecs_record_has_id( + ecs_world_t *world, + const ecs_record_t *record, + ecs_id_t id); + +/** Get component pointer from column/record. + * This returns a pointer to the component using a table column index. The + * table's column index can be found with ecs_table_get_column_index(). + * + * Usage: + * @code + * ecs_record_t *r = ecs_record_find(world, entity); + * int32_t column = ecs_table_get_column_index(world, table, ecs_id(Position)); + * Position *ptr = ecs_record_get_by_column(r, column, sizeof(Position)); + * @endcode + * + * @param record The record. + * @param column The column index in the entity's table. + * @param size The component size. + * @return The component pointer. + */ +FLECS_API +void* ecs_record_get_by_column( + const ecs_record_t *record, + int32_t column, + size_t size); + +/** Emplace a component. + * Emplace is similar to ecs_ensure_id() except that the component constructor + * is not invoked for the returned pointer, allowing the component to be + * constructed directly in the storage. + * + * When the `is_new` parameter is not provided, the operation will assert when the + * component already exists. When the `is_new` parameter is provided, it will + * indicate whether the returned storage has been constructed. + * + * When `is_new` indicates that the storage has not yet been constructed, it must + * be constructed by the code invoking this operation. Not constructing the + * component will result in undefined behavior. + * + * @param world The world. + * @param entity The entity. + * @param id The component to obtain. + * @param is_new Whether this is an existing or new component. + * @return The (uninitialized) component pointer. + */ +FLECS_API +void* ecs_emplace_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id, + bool *is_new); + +/** Signal that a component has been modified. + * This operation is usually used after modifying a component value obtained by + * ecs_ensure_id(). The operation will mark the component as dirty, and invoke + * OnSet observers and hooks. + * + * @param world The world. + * @param entity The entity. + * @param id The id of the component that was modified. + */ +FLECS_API +void ecs_modified_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Set the value of a component. + * This operation allows an application to set the value of a component. The + * operation is equivalent to calling ecs_ensure_id() followed by + * ecs_modified_id(). The operation will not modify the value of the passed in + * component. If the component has a copy hook registered, it will be used to + * copy in the component. + * + * If the provided entity is 0, a new entity will be created. + * + * @param world The world. + * @param entity The entity. + * @param id The id of the component to set. + * @param size The size of the pointed-to value. + * @param ptr The pointer to the value. + */ +FLECS_API +void ecs_set_id( + ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id, + size_t size, + const void *ptr); + +/** @} */ + +/** + * @defgroup liveliness Entity Liveliness + * Functions for testing and modifying entity liveliness. + * + * @{ + */ + +/** Test whether an entity is valid. + * Entities that are valid can be used with API functions. Using invalid + * entities with API operations will cause the function to panic. + * + * An entity is valid if it is not 0 and if it is alive. + * + * ecs_is_valid() will return true for ids that don't exist (alive or not alive). This + * allows for using ids that have never been created by ecs_new_w() or similar. In + * this the function differs from ecs_is_alive(), which will return false for + * entities that do not yet exist. + * + * The operation will return false for an id that exists and is not alive, as + * using this id with an API operation would cause it to assert. + * + * @param world The world. + * @param e The entity. + * @return True if the entity is valid, false if the entity is not valid. + */ +FLECS_API +bool ecs_is_valid( + const ecs_world_t *world, + ecs_entity_t e); + +/** Test whether an entity is alive. + * Entities are alive after they are created, and become not alive when they are + * deleted. Operations that return alive ids are (amongst others) ecs_new(), + * ecs_new_low_id() and ecs_entity_init(). Ids can be made alive with the ecs_make_alive() + * function. + * + * After an id is deleted it can be recycled. Recycled ids are different from + * the original id in that they have a different generation count. This makes it + * possible for the API to distinguish between the two. An example: + * + * @code + * ecs_entity_t e1 = ecs_new(world); + * ecs_is_alive(world, e1); // true + * ecs_delete(world, e1); + * ecs_is_alive(world, e1); // false + * + * ecs_entity_t e2 = ecs_new(world); // recycles e1 + * ecs_is_alive(world, e2); // true + * ecs_is_alive(world, e1); // false + * @endcode + * + * @param world The world. + * @param e The entity. + * @return True if the entity is alive, false if the entity is not alive. + */ +FLECS_API +bool ecs_is_alive( + const ecs_world_t *world, + ecs_entity_t e); + +/** Remove generation from entity id. + * + * @param e The entity id. + * @return The entity id without the generation count. + */ +FLECS_API +ecs_id_t ecs_strip_generation( + ecs_entity_t e); + +/** Get alive identifier. + * In some cases an application may need to work with identifiers from which + * the generation has been stripped. A typical scenario in which this happens is + * when iterating relationships in an entity type. + * + * For example, when obtaining the parent id from a `ChildOf` relationship, the parent + * (second element of the pair) will have been stored in a 32 bit value, which + * cannot store the entity generation. This function can retrieve the identifier + * with the current generation for that id. + * + * If the provided identifier is not alive, the function will return 0. + * + * @param world The world. + * @param e The for which to obtain the current alive entity id. + * @return The alive entity id if there is one, or 0 if the id is not alive. + */ +FLECS_API +ecs_entity_t ecs_get_alive( + const ecs_world_t *world, + ecs_entity_t e); + +/** Ensure id is alive. + * This operation ensures that the provided id is alive. This is useful in + * scenarios where an application has an existing id that has not been created + * with ecs_new_w() (such as a global constant or an id from a remote application). + * + * When this operation is successful it guarantees that the provided id exists, + * is valid and is alive. + * + * Before this operation the id must either not be alive or have a generation + * that is equal to the passed in entity. + * + * If the provided id has a non-zero generation count and the id does not exist + * in the world, the id will be created with the specified generation. + * + * If the provided id is alive and has a generation count that does not match + * the provided id, the operation will fail. + * + * @param world The world. + * @param entity The entity id to make alive. + * + * @see ecs_make_alive_id() + */ +FLECS_API +void ecs_make_alive( + ecs_world_t *world, + ecs_entity_t entity); + +/** Same as ecs_make_alive(), but for (component) ids. + * An id can be an entity or pair, and can contain id flags. This operation + * ensures that the entity (or entities, for a pair) are alive. + * + * When this operation is successful it guarantees that the provided id can be + * used in operations that accept an id. + * + * Since entities in a pair do not encode their generation ids, this operation + * will not fail when an entity with non-zero generation count already exists in + * the world. + * + * This is different from ecs_make_alive(), which will fail if attempted with an id + * that has generation 0 and an entity with a non-zero generation is currently + * alive. + * + * @param world The world. + * @param id The id to make alive. + */ +FLECS_API +void ecs_make_alive_id( + ecs_world_t *world, + ecs_id_t id); + +/** Test whether an entity exists. + * Similar as ecs_is_alive(), but ignores entity generation count. + * + * @param world The world. + * @param entity The entity. + * @return True if the entity exists, false if the entity does not exist. + */ +FLECS_API +bool ecs_exists( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Override the generation of an entity. + * The generation count of an entity is increased each time an entity is deleted + * and is used to test whether an entity id is alive. + * + * This operation overrides the current generation of an entity with the + * specified generation, which can be useful if an entity is externally managed, + * like for external pools, savefiles or netcode. + * + * This operation is similar to ecs_make_alive(), except that it will also + * override the generation of an alive entity. + * + * @param world The world. + * @param entity Entity for which to set the generation with the new generation. + */ +FLECS_API +void ecs_set_version( + ecs_world_t *world, + ecs_entity_t entity); + +/** @} */ + +/** + * @defgroup entity_info Entity Information. + * Get information from entity. + * + * @{ + */ + +/** Get the type of an entity. + * + * @param world The world. + * @param entity The entity. + * @return The type of the entity, NULL if the entity has no components. + */ +FLECS_API +const ecs_type_t* ecs_get_type( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Get the table of an entity. + * + * @param world The world. + * @param entity The entity. + * @return The table of the entity, NULL if the entity has no components/tags. + */ +FLECS_API +ecs_table_t* ecs_get_table( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Convert type to string. + * The result of this operation must be freed with ecs_os_free(). + * + * @param world The world. + * @param type The type. + * @return The stringified type. + */ +FLECS_API +char* ecs_type_str( + const ecs_world_t *world, + const ecs_type_t* type); + +/** Convert table to string. + * Same as `ecs_type_str(world, ecs_table_get_type(table))`. The result of this + * operation must be freed with ecs_os_free(). + * + * @param world The world. + * @param table The table. + * @return The stringified table type. + * + * @see ecs_table_get_type() + * @see ecs_type_str() + */ +FLECS_API +char* ecs_table_str( + const ecs_world_t *world, + const ecs_table_t *table); + +/** Convert entity to string. + * Same as combining: + * - ecs_get_path(world, entity) + * - ecs_type_str(world, ecs_get_type(world, entity)) + * + * The result of this operation must be freed with ecs_os_free(). + * + * @param world The world. + * @param entity The entity. + * @return The entity path with stringified type. + * + * @see ecs_get_path() + * @see ecs_type_str() + */ +FLECS_API +char* ecs_entity_str( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Test if an entity has an id. + * This operation returns true if the entity has or inherits the specified id. + * + * @param world The world. + * @param entity The entity. + * @param id The id to test for. + * @return True if the entity has the id, false if not. + * + * @see ecs_owns_id() + */ +FLECS_API +bool ecs_has_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Test if an entity owns an id. + * This operation returns true if the entity has the specified id. The operation + * behaves the same as ecs_has_id(), except that it will return false for + * components that are inherited through an `IsA` relationship. + * + * @param world The world. + * @param entity The entity. + * @param id The id to test for. + * @return True if the entity has the id, false if not. + */ +FLECS_API +bool ecs_owns_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Get the target of a relationship. + * This will return a target (second element of a pair) of the entity for the + * specified relationship. The index allows for iterating through the targets, + * if a single entity has multiple targets for the same relationship. + * + * If the index is larger than the total number of instances the entity has for + * the relationship, the operation will return 0. + * + * @param world The world. + * @param entity The entity. + * @param rel The relationship between the entity and the target. + * @param index The index of the relationship instance. + * @return The target for the relationship at the specified index. + */ +FLECS_API +ecs_entity_t ecs_get_target( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t rel, + int32_t index); + +/** Get parent (target of `ChildOf` relationship) for entity. + * This operation is the same as calling: + * + * @code + * ecs_get_target(world, entity, EcsChildOf, 0); + * @endcode + * + * @param world The world. + * @param entity The entity. + * @return The parent of the entity, 0 if the entity has no parent. + * + * @see ecs_get_target() + */ +FLECS_API +ecs_entity_t ecs_get_parent( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Get the target of a relationship for a given id. + * This operation returns the first entity that has the provided id by following + * the specified relationship. If the entity itself has the id then entity will + * be returned. If the id cannot be found on the entity or by following the + * relationship, the operation will return 0. + * + * This operation can be used to lookup, for example, which prefab is providing + * a component by specifying the `IsA` relationship: + * + * @code + * // Is Position provided by the entity or one of its base entities? + * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) + * @endcode + * + * @param world The world. + * @param entity The entity. + * @param rel The relationship to follow. + * @param id The id to lookup. + * @return The entity for which the target has been found. + */ +FLECS_API +ecs_entity_t ecs_get_target_for_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t rel, + ecs_id_t id); + +/** Return depth for entity in tree for the specified relationship. + * Depth is determined by counting the number of targets encountered while + * traversing up the relationship tree for rel. Only acyclic relationships are + * supported. + * + * @param world The world. + * @param entity The entity. + * @param rel The relationship. + * @return The depth of the entity in the tree. + */ +FLECS_API +int32_t ecs_get_depth( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t rel); + +/** Count entities that have the specified id. + * Returns the number of entities that have the specified id. + * + * @param world The world. + * @param entity The id to search for. + * @return The number of entities that have the id. + */ +FLECS_API +int32_t ecs_count_id( + const ecs_world_t *world, + ecs_id_t entity); + +/** @} */ + + +/** + * @defgroup paths Entity Names + * Functions for working with entity names and paths. + * + * @{ + */ + +/** Get the name of an entity. + * This will return the name stored in `(EcsIdentifier, EcsName)`. + * + * @param world The world. + * @param entity The entity. + * @return The type of the entity, NULL if the entity has no name. + * + * @see ecs_set_name() + */ +FLECS_API +const char* ecs_get_name( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Get the symbol of an entity. + * This will return the symbol stored in `(EcsIdentifier, EcsSymbol)`. + * + * @param world The world. + * @param entity The entity. + * @return The type of the entity, NULL if the entity has no name. + * + * @see ecs_set_symbol() + */ +FLECS_API +const char* ecs_get_symbol( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Set the name of an entity. + * This will set or overwrite the name of an entity. If no entity is provided, + * a new entity will be created. + * + * The name is stored in `(EcsIdentifier, EcsName)`. + * + * @param world The world. + * @param entity The entity. + * @param name The name. + * @return The provided entity, or a new entity if 0 was provided. + * + * @see ecs_get_name() + */ +FLECS_API +ecs_entity_t ecs_set_name( + ecs_world_t *world, + ecs_entity_t entity, + const char *name); + +/** Set the symbol of an entity. + * This will set or overwrite the symbol of an entity. If no entity is provided, + * a new entity will be created. + * + * The symbol is stored in (EcsIdentifier, EcsSymbol). + * + * @param world The world. + * @param entity The entity. + * @param symbol The symbol. + * @return The provided entity, or a new entity if 0 was provided. + * + * @see ecs_get_symbol() + */ +FLECS_API +ecs_entity_t ecs_set_symbol( + ecs_world_t *world, + ecs_entity_t entity, + const char *symbol); + +/** Set alias for entity. + * An entity can be looked up using its alias from the root scope without + * providing the fully qualified name if its parent. An entity can only have + * a single alias. + * + * The symbol is stored in `(EcsIdentifier, EcsAlias)`. + * + * @param world The world. + * @param entity The entity. + * @param alias The alias. + */ +FLECS_API +void ecs_set_alias( + ecs_world_t *world, + ecs_entity_t entity, + const char *alias); + +/** Lookup an entity by it's path. + * This operation is equivalent to calling: + * + * @code + * ecs_lookup_path_w_sep(world, 0, path, ".", NULL, true); + * @endcode + * + * @param world The world. + * @param path The entity path. + * @return The entity with the specified path, or 0 if no entity was found. + * + * @see ecs_lookup_child() + * @see ecs_lookup_path_w_sep() + * @see ecs_lookup_symbol() + */ +FLECS_API +ecs_entity_t ecs_lookup( + const ecs_world_t *world, + const char *path); + +/** Lookup a child entity by name. + * Returns an entity that matches the specified name. Only looks for entities in + * the provided parent. If no parent is provided, look in the current scope ( + * root if no scope is provided). + * + * @param world The world. + * @param parent The parent for which to lookup the child. + * @param name The entity name. + * @return The entity with the specified name, or 0 if no entity was found. + * + * @see ecs_lookup() + * @see ecs_lookup_path_w_sep() + * @see ecs_lookup_symbol() + */ +FLECS_API +ecs_entity_t ecs_lookup_child( + const ecs_world_t *world, + ecs_entity_t parent, + const char *name); + +/** Lookup an entity from a path. + * Lookup an entity from a provided path, relative to the provided parent. The + * operation will use the provided separator to tokenize the path expression. If + * the provided path contains the prefix, the search will start from the root. + * + * If the entity is not found in the provided parent, the operation will + * continue to search in the parent of the parent, until the root is reached. If + * the entity is still not found, the lookup will search in the flecs.core + * scope. If the entity is not found there either, the function returns 0. + * + * @param world The world. + * @param parent The entity from which to resolve the path. + * @param path The path to resolve. + * @param sep The path separator. + * @param prefix The path prefix. + * @param recursive Recursively traverse up the tree until entity is found. + * @return The entity if found, else 0. + * + * @see ecs_lookup() + * @see ecs_lookup_child() + * @see ecs_lookup_symbol() + */ +FLECS_API +ecs_entity_t ecs_lookup_path_w_sep( + const ecs_world_t *world, + ecs_entity_t parent, + const char *path, + const char *sep, + const char *prefix, + bool recursive); + +/** Lookup an entity by its symbol name. + * This looks up an entity by symbol stored in `(EcsIdentifier, EcsSymbol)`. The + * operation does not take into account hierarchies. + * + * This operation can be useful to resolve, for example, a type by its C + * identifier, which does not include the Flecs namespacing. + * + * @param world The world. + * @param symbol The symbol. + * @param lookup_as_path If not found as a symbol, lookup as path. + * @param recursive If looking up as path, recursively traverse up the tree. + * @return The entity if found, else 0. + * + * @see ecs_lookup() + * @see ecs_lookup_child() + * @see ecs_lookup_path_w_sep() + */ +FLECS_API +ecs_entity_t ecs_lookup_symbol( + const ecs_world_t *world, + const char *symbol, + bool lookup_as_path, + bool recursive); + +/** Get a path identifier for an entity. + * This operation creates a path that contains the names of the entities from + * the specified parent to the provided entity, separated by the provided + * separator. If no parent is provided the path will be relative to the root. If + * a prefix is provided, the path will be prefixed by the prefix. + * + * If the parent is equal to the provided child, the operation will return an + * empty string. If a nonzero component is provided, the path will be created by + * looking for parents with that component. + * + * The returned path should be freed by the application. + * + * @param world The world. + * @param parent The entity from which to create the path. + * @param child The entity to which to create the path. + * @param sep The separator to use between path elements. + * @param prefix The initial character to use for root elements. + * @return The relative entity path. + * + * @see ecs_get_path_w_sep_buf() + */ +FLECS_API +char* ecs_get_path_w_sep( + const ecs_world_t *world, + ecs_entity_t parent, + ecs_entity_t child, + const char *sep, + const char *prefix); + +/** Write path identifier to buffer. + * Same as ecs_get_path_w_sep(), but writes result to an ecs_strbuf_t. + * + * @param world The world. + * @param parent The entity from which to create the path. + * @param child The entity to which to create the path. + * @param sep The separator to use between path elements. + * @param prefix The initial character to use for root elements. + * @param buf The buffer to write to. + * + * @see ecs_get_path_w_sep() + */ +void ecs_get_path_w_sep_buf( + const ecs_world_t *world, + ecs_entity_t parent, + ecs_entity_t child, + const char *sep, + const char *prefix, + ecs_strbuf_t *buf, + bool escape); + +/** Find or create entity from path. + * This operation will find or create an entity from a path, and will create any + * intermediate entities if required. If the entity already exists, no entities + * will be created. + * + * If the path starts with the prefix, then the entity will be created from the + * root scope. + * + * @param world The world. + * @param parent The entity relative to which the entity should be created. + * @param path The path to create the entity for. + * @param sep The separator used in the path. + * @param prefix The prefix used in the path. + * @return The entity. + */ +FLECS_API +ecs_entity_t ecs_new_from_path_w_sep( + ecs_world_t *world, + ecs_entity_t parent, + const char *path, + const char *sep, + const char *prefix); + +/** Add specified path to entity. + * This operation is similar to ecs_new_from_path(), but will instead add the path + * to an existing entity. + * + * If an entity already exists for the path, it will be returned instead. + * + * @param world The world. + * @param entity The entity to which to add the path. + * @param parent The entity relative to which the entity should be created. + * @param path The path to create the entity for. + * @param sep The separator used in the path. + * @param prefix The prefix used in the path. + * @return The entity. + */ +FLECS_API +ecs_entity_t ecs_add_path_w_sep( + ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t parent, + const char *path, + const char *sep, + const char *prefix); + +/** Set the current scope. + * This operation sets the scope of the current stage to the provided entity. + * As a result new entities will be created in this scope, and lookups will be + * relative to the provided scope. + * + * It is considered good practice to restore the scope to the old value. + * + * @param world The world. + * @param scope The entity to use as scope. + * @return The previous scope. + * + * @see ecs_get_scope() + */ +FLECS_API +ecs_entity_t ecs_set_scope( + ecs_world_t *world, + ecs_entity_t scope); + +/** Get the current scope. + * Get the scope set by ecs_set_scope(). If no scope is set, this operation will + * return 0. + * + * @param world The world. + * @return The current scope. + */ +FLECS_API +ecs_entity_t ecs_get_scope( + const ecs_world_t *world); + +/** Set a name prefix for newly created entities. + * This is a utility that lets C modules use prefixed names for C types and + * C functions, while using names for the entity names that do not have the + * prefix. The name prefix is currently only used by ECS_COMPONENT. + * + * @param world The world. + * @param prefix The name prefix to use. + * @return The previous prefix. + */ +FLECS_API +const char* ecs_set_name_prefix( + ecs_world_t *world, + const char *prefix); + +/** Set search path for lookup operations. + * This operation accepts an array of entity ids that will be used as search + * scopes by lookup operations. The operation returns the current search path. + * It is good practice to restore the old search path. + * + * The search path will be evaluated starting from the last element. + * + * The default search path includes flecs.core. When a custom search path is + * provided it overwrites the existing search path. Operations that rely on + * looking up names from flecs.core without providing the namespace may fail if + * the custom search path does not include flecs.core (EcsFlecsCore). + * + * The search path array is not copied into managed memory. The application must + * ensure that the provided array is valid for as long as it is used as the + * search path. + * + * The provided array must be terminated with a 0 element. This enables an + * application to push/pop elements to an existing array without invoking the + * ecs_set_lookup_path() operation again. + * + * @param world The world. + * @param lookup_path 0-terminated array with entity ids for the lookup path. + * @return Current lookup path array. + * + * @see ecs_get_lookup_path() + */ +FLECS_API +ecs_entity_t* ecs_set_lookup_path( + ecs_world_t *world, + const ecs_entity_t *lookup_path); + +/** Get current lookup path. + * Returns value set by ecs_set_lookup_path(). + * + * @param world The world. + * @return The current lookup path. + */ +FLECS_API +ecs_entity_t* ecs_get_lookup_path( + const ecs_world_t *world); + +/** @} */ + +/** @} */ + +/** + * @defgroup components Components + * Functions for registering and working with components. + * + * @{ + */ + +/** Find or create a component. + * This operation creates a new component, or finds an existing one. The find or + * create behavior is the same as ecs_entity_init(). + * + * When an existing component is found, the size and alignment are verified with + * the provided values. If the values do not match, the operation will fail. + * + * See the documentation of ecs_component_desc_t for more details. + * + * @param world The world. + * @param desc Component init parameters. + * @return A handle to the new or existing component, or 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_component_init( + ecs_world_t *world, + const ecs_component_desc_t *desc); + +/** Get the type for an id. + * This function returns the type information for an id. The specified id can be + * any valid id. For the rules on how type information is determined based on + * id, see ecs_get_typeid(). + * + * @param world The world. + * @param id The id. + * @return The type information of the id. + */ +FLECS_API +const ecs_type_info_t* ecs_get_type_info( + const ecs_world_t *world, + ecs_id_t id); + +/** Register hooks for component. + * Hooks allow for the execution of user code when components are constructed, + * copied, moved, destructed, added, removed or set. Hooks can be assigned as + * as long as a component has not yet been used (added to an entity). + * + * The hooks that are currently set can be accessed with ecs_get_type_info(). + * + * @param world The world. + * @param id The component id for which to register the actions + * @param hooks Type that contains the component actions. + */ +FLECS_API +void ecs_set_hooks_id( + ecs_world_t *world, + ecs_entity_t id, + const ecs_type_hooks_t *hooks); + +/** Get hooks for component. + * + * @param world The world. + * @param id The component id for which to retrieve the hooks. + * @return The hooks for the component, or NULL if not registered. + */ +FLECS_API +const ecs_type_hooks_t* ecs_get_hooks_id( + const ecs_world_t *world, + ecs_entity_t id); + +/** @} */ + +/** + * @defgroup ids Ids + * Functions for working with `ecs_id_t`. + * + * @{ + */ + +/** Returns whether specified id a tag. + * This operation returns whether the specified type is a tag (a component + * without data/size). + * + * An id is a tag when: + * - it is an entity without the EcsComponent component + * - it has an EcsComponent with size member set to 0 + * - it is a pair where both elements are a tag + * - it is a pair where the first element has the #EcsPairIsTag tag + * + * @param world The world. + * @param id The id. + * @return Whether the provided id is a tag. + */ +FLECS_API +bool ecs_id_is_tag( + const ecs_world_t *world, + ecs_id_t id); + +/** Returns whether specified id is in use. + * This operation returns whether an id is in use in the world. An id is in use + * if it has been added to one or more tables. + * + * @param world The world. + * @param id The id. + * @return Whether the id is in use. + */ +FLECS_API +bool ecs_id_in_use( + const ecs_world_t *world, + ecs_id_t id); + +/** Get the type for an id. + * This operation returns the component id for an id, if the id is associated + * with a type. For a regular component with a non-zero size (an entity with the + * EcsComponent component) the operation will return the entity itself. + * + * For an entity that does not have the EcsComponent component, or with an + * EcsComponent value with size 0, the operation will return 0. + * + * For a pair id the operation will return the type associated with the pair, by + * applying the following queries in order: + * - The first pair element is returned if it is a component + * - 0 is returned if the relationship entity has the Tag property + * - The second pair element is returned if it is a component + * - 0 is returned. + * + * @param world The world. + * @param id The id. + * @return The type id of the id. + */ +FLECS_API +ecs_entity_t ecs_get_typeid( + const ecs_world_t *world, + ecs_id_t id); + +/** Utility to match an id with a pattern. + * This operation returns true if the provided pattern matches the provided + * id. The pattern may contain a wildcard (or wildcards, when a pair). + * + * @param id The id. + * @param pattern The pattern to compare with. + * @return Whether the id matches the pattern. + */ +FLECS_API +bool ecs_id_match( + ecs_id_t id, + ecs_id_t pattern); + +/** Utility to check if id is a pair. + * + * @param id The id. + * @return True if id is a pair. + */ +FLECS_API +bool ecs_id_is_pair( + ecs_id_t id); + +/** Utility to check if id is a wildcard. + * + * @param id The id. + * @return True if id is a wildcard or a pair containing a wildcard. + */ +FLECS_API +bool ecs_id_is_wildcard( + ecs_id_t id); + +/** Utility to check if id is valid. + * A valid id is an id that can be added to an entity. Invalid ids are: + * - ids that contain wildcards + * - ids that contain invalid entities + * - ids that are 0 or contain 0 entities + * + * Note that the same rules apply to removing from an entity, with the exception + * of wildcards. + * + * @param world The world. + * @param id The id. + * @return True if the id is valid. + */ +FLECS_API +bool ecs_id_is_valid( + const ecs_world_t *world, + ecs_id_t id); + +/** Get flags associated with id. + * This operation returns the internal flags (see api_flags.h) that are + * associated with the provided id. + * + * @param world The world. + * @param id The id. + * @return Flags associated with the id, or 0 if the id is not in use. + */ +FLECS_API +ecs_flags32_t ecs_id_get_flags( + const ecs_world_t *world, + ecs_id_t id); + +/** Convert id flag to string. + * This operation converts an id flag to a string. + * + * @param id_flags The id flag. + * @return The id flag string, or NULL if no valid id is provided. + */ +FLECS_API +const char* ecs_id_flag_str( + ecs_id_t id_flags); + +/** Convert (component) id to string. + * This operation interprets the structure of an id and converts it to a string. + * + * @param world The world. + * @param id The id to convert to a string. + * @return The id converted to a string. + */ +FLECS_API +char* ecs_id_str( + const ecs_world_t *world, + ecs_id_t id); + +/** Write (component) id string to buffer. + * Same as ecs_id_str() but writes result to ecs_strbuf_t. + * + * @param world The world. + * @param id The id to convert to a string. + * @param buf The buffer to write to. + */ +FLECS_API +void ecs_id_str_buf( + const ecs_world_t *world, + ecs_id_t id, + ecs_strbuf_t *buf); + +/** Convert string to a (component) id. + * This operation is the reverse of ecs_id_str(). The FLECS_SCRIPT addon + * is required for this operation to work. + * + * @param world The world. + * @param expr The string to convert to an id. + */ +FLECS_API +ecs_id_t ecs_id_from_str( + const ecs_world_t *world, + const char *expr); + +/** @} */ + +/** + * @defgroup queries Queries + * @brief Functions for working with `ecs_term_t` and `ecs_query_t`. + * @{ + */ + +/** Test whether term id is set. + * + * @param id The term id. + * @return True when set, false when not set. + */ +FLECS_API +bool ecs_term_ref_is_set( + const ecs_term_ref_t *id); + +/** Test whether a term is set. + * This operation can be used to test whether a term has been initialized with + * values or whether it is empty. + * + * An application generally does not need to invoke this operation. It is useful + * when initializing a 0-initialized array of terms (like in ecs_term_desc_t) as + * this operation can be used to find the last initialized element. + * + * @param term The term. + * @return True when set, false when not set. + */ +FLECS_API +bool ecs_term_is_initialized( + const ecs_term_t *term); + +/** Is term matched on $this variable. + * This operation checks whether a term is matched on the $this variable, which + * is the default source for queries. + * + * A term has a $this source when: + * - ecs_term_t::src::id is EcsThis + * - ecs_term_t::src::flags is EcsIsVariable + * + * If ecs_term_t::src is not populated, it will be automatically initialized to + * the $this source for the created query. + * + * @param term The term. + * @return True if term matches $this, false if not. + */ +FLECS_API +bool ecs_term_match_this( + const ecs_term_t *term); + +/** Is term matched on 0 source. + * This operation checks whether a term is matched on a 0 source. A 0 source is + * a term that isn't matched against anything, and can be used just to pass + * (component) ids to a query iterator. + * + * A term has a 0 source when: + * - ecs_term_t::src::id is 0 + * - ecs_term_t::src::flags has EcsIsEntity set + * + * @param term The term. + * @return True if term has 0 source, false if not. + */ +FLECS_API +bool ecs_term_match_0( + const ecs_term_t *term); + +/** Convert term to string expression. + * Convert term to a string expression. The resulting expression is equivalent + * to the same term, with the exception of And & Or operators. + * + * @param world The world. + * @param term The term. + * @return The term converted to a string. + */ +FLECS_API +char* ecs_term_str( + const ecs_world_t *world, + const ecs_term_t *term); + +/** Convert query to string expression. + * Convert query to a string expression. The resulting expression can be + * parsed to create the same query. + * + * @param query The query. + * @return The query converted to a string. + */ +FLECS_API +char* ecs_query_str( + const ecs_query_t *query); + +/** @} */ + +/** + * @defgroup each_iter Each iterator + * @brief Find all entities that have a single (component) id. + * @{ + */ + +/** Iterate all entities with specified (component id). + * This returns an iterator that yields all entities with a single specified + * component. This is a much lighter weight operation than creating and + * iterating a query. + * + * Usage: + * @code + * ecs_iter_t it = ecs_each(world, Player); + * while (ecs_each_next(&it)) { + * for (int i = 0; i < it.count; i ++) { + * // Iterate as usual. + * } + * } + * @endcode + * + * If the specified id is a component, it is possible to access the component + * pointer with ecs_field just like with regular queries: + * + * @code + * ecs_iter_t it = ecs_each(world, Position); + * while (ecs_each_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * for (int i = 0; i < it.count; i ++) { + * // Iterate as usual. + * } + * } + * @endcode + * + * @param world The world. + * @param id The (component) id to iterate. + * @return An iterator that iterates all entities with the (component) id. +*/ +FLECS_API +ecs_iter_t ecs_each_id( + const ecs_world_t *world, + ecs_id_t id); + +/** Progress an iterator created with ecs_each_id(). + * + * @param it The iterator. + * @return True if the iterator has more results, false if not. + */ +FLECS_API +bool ecs_each_next( + ecs_iter_t *it); + +/** Iterate children of parent. + * Equivalent to: + * @code + * ecs_iter_t it = ecs_each_id(world, ecs_pair(EcsChildOf, parent)); + * @endcode + * + * @param world The world. + * @param parent The parent. + * @return An iterator that iterates all children of the parent. + * + * @see ecs_each_id() +*/ +FLECS_API +ecs_iter_t ecs_children( + const ecs_world_t *world, + ecs_entity_t parent); + +/** Progress an iterator created with ecs_children(). + * + * @param it The iterator. + * @return True if the iterator has more results, false if not. + */ +FLECS_API +bool ecs_children_next( + ecs_iter_t *it); + +/** @} */ + +/** + * @defgroup queries Queries + * Functions for working with `ecs_query_t`. + * + * @{ + */ + +/** Create a query. + * + * @param world The world. + * @param desc The descriptor (see ecs_query_desc_t) + * @return The query. + */ +FLECS_API +ecs_query_t* ecs_query_init( + ecs_world_t *world, + const ecs_query_desc_t *desc); + +/** Delete a query. + * + * @param query The query. + */ +FLECS_API +void ecs_query_fini( + ecs_query_t *query); + +/** Find variable index. + * This operation looks up the index of a variable in the query. This index can + * be used in operations like ecs_iter_set_var() and ecs_iter_get_var(). + * + * @param query The query. + * @param name The variable name. + * @return The variable index. + */ +FLECS_API +int32_t ecs_query_find_var( + const ecs_query_t *query, + const char *name); + +/** Get variable name. + * This operation returns the variable name for an index. + * + * @param query The query. + * @param var_id The variable index. + * @return The variable name. + */ +FLECS_API +const char* ecs_query_var_name( + const ecs_query_t *query, + int32_t var_id); + +/** Test if variable is an entity. + * Internally the query engine has entity variables and table variables. When + * iterating through query variables (by using ecs_query_variable_count()) only + * the values for entity variables are accessible. This operation enables an + * application to check if a variable is an entity variable. + * + * @param query The query. + * @param var_id The variable id. + * @return Whether the variable is an entity variable. + */ +FLECS_API +bool ecs_query_var_is_entity( + const ecs_query_t *query, + int32_t var_id); + +/** Create a query iterator. + * Use an iterator to iterate through the entities that match an entity. Queries + * can return multiple results, and have to be iterated by repeatedly calling + * ecs_query_next() until the operation returns false. + * + * Depending on the query, a single result can contain an entire table, a range + * of entities in a table, or a single entity. Iteration code has an inner and + * an outer loop. The outer loop loops through the query results, and typically + * corresponds with a table. The inner loop loops entities in the result. + * + * Example: + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * + * while (ecs_query_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * Velocity *v = ecs_field(&it, Velocity, 1); + * + * for (int i = 0; i < it.count; i ++) { + * p[i].x += v[i].x; + * p[i].y += v[i].y; + * } + * } + * @endcode + * + * The world passed into the operation must be either the actual world or the + * current stage, when iterating from a system. The stage is accessible through + * the it.world member. + * + * Example: + * @code + * void MySystem(ecs_iter_t *it) { + * ecs_query_t *q = it->ctx; // Query passed as system context + * + * // Create query iterator from system stage + * ecs_iter_t qit = ecs_query_iter(it->world, q); + * while (ecs_query_next(&qit)) { + * // Iterate as usual + * } + * } + * @endcode + * + * If query iteration is stopped without the last call to ecs_query_next() + * returning false, iterator resources need to be cleaned up explicitly + * with ecs_iter_fini(). + * + * Example: + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * + * while (ecs_query_next(&it)) { + * if (!ecs_field_is_set(&it, 0)) { + * ecs_iter_fini(&it); // Free iterator resources + * break; + * } + * + * for (int i = 0; i < it.count; i ++) { + * // ... + * } + * } + * @endcode + * + * @param world The world. + * @param query The query. + * @return An iterator. + * + * @see ecs_query_next() + */ +FLECS_API +ecs_iter_t ecs_query_iter( + const ecs_world_t *world, + const ecs_query_t *query); + +/** Progress query iterator. + * + * @param it The iterator. + * @return True if the iterator has more results, false if not. + * + * @see ecs_query_iter() + */ +FLECS_API +bool ecs_query_next( + ecs_iter_t *it); + +/** Match entity with query. + * This operation matches an entity with a query and returns the result of the + * match in the "it" out parameter. An application should free the iterator + * resources with ecs_iter_fini() if this function returns true. + * + * Usage: + * @code + * ecs_iter_t it; + * if (ecs_query_has(q, e, &it)) { + * ecs_iter_fini(&it); + * } + * @endcode + * + * @param query The query. + * @param entity The entity to match + * @param it The iterator with matched data. + * @return True if entity matches the query, false if not. + */ +FLECS_API +bool ecs_query_has( + ecs_query_t *query, + ecs_entity_t entity, + ecs_iter_t *it); + +/** Match table with query. + * This operation matches a table with a query and returns the result of the + * match in the "it" out parameter. An application should free the iterator + * resources with ecs_iter_fini() if this function returns true. + * + * Usage: + * @code + * ecs_iter_t it; + * if (ecs_query_has_table(q, t, &it)) { + * ecs_iter_fini(&it); + * } + * @endcode + * + * @param query The query. + * @param table The table to match + * @param it The iterator with matched data. + * @return True if table matches the query, false if not. + */ +FLECS_API +bool ecs_query_has_table( + ecs_query_t *query, + ecs_table_t *table, + ecs_iter_t *it); + +/** Match range with query. + * This operation matches a range with a query and returns the result of the + * match in the "it" out parameter. An application should free the iterator + * resources with ecs_iter_fini() if this function returns true. + * + * The entire range must match the query for the operation to return true. + * + * Usage: + * @code + * ecs_table_range_t range = { + * .table = table, + * .offset = 1, + * .count = 2 + * }; + * + * ecs_iter_t it; + * if (ecs_query_has_range(q, &range, &it)) { + * ecs_iter_fini(&it); + * } + * @endcode + * + * @param query The query. + * @param range The range to match + * @param it The iterator with matched data. + * @return True if range matches the query, false if not. + */ +FLECS_API +bool ecs_query_has_range( + ecs_query_t *query, + ecs_table_range_t *range, + ecs_iter_t *it); + +/** Returns how often a match event happened for a cached query. + * This operation can be used to determine whether the query cache has been + * updated with new tables. + * + * @param query The query. + * @return The number of match events happened. + */ +FLECS_API +int32_t ecs_query_match_count( + const ecs_query_t *query); + +/** Convert query to a string. + * This will convert the query program to a string which can aid in debugging + * the behavior of a query. + * + * The returned string must be freed with ecs_os_free(). + * + * @param query The query. + * @return The query plan. + */ +FLECS_API +char* ecs_query_plan( + const ecs_query_t *query); + +/** Convert query to string with profile. + * To use this you must set the EcsIterProfile flag on an iterator before + * starting iteration: + * + * @code + * it.flags |= EcsIterProfile + * @endcode + * + * The returned string must be freed with ecs_os_free(). + * + * @param query The query. + * @param it The iterator with profile data. + * @return The query plan with profile data. + */ +FLECS_API +char* ecs_query_plan_w_profile( + const ecs_query_t *query, + const ecs_iter_t *it); + +/** Populate variables from key-value string. + * Convenience function to set query variables from a key-value string separated + * by comma's. The string must have the following format: + * + * @code + * var_a: value, var_b: value + * @endcode + * + * The key-value list may optionally be enclosed in parenthesis. + * + * This function uses the script addon. + * + * @param query The query. + * @param it The iterator for which to set the variables. + * @param expr The key-value expression. + * @return Pointer to the next character after the last parsed one. + */ +FLECS_API +const char* ecs_query_args_parse( + ecs_query_t *query, + ecs_iter_t *it, + const char *expr); + +/** Returns whether the query data changed since the last iteration. + * The operation will return true after: + * - new entities have been matched with + * - new tables have been matched/unmatched with + * - matched entities were deleted + * - matched components were changed + * + * The operation will not return true after a write-only (EcsOut) or filter + * (EcsInOutNone) term has changed, when a term is not matched with the + * current table (This subject) or for tag terms. + * + * The changed state of a table is reset after it is iterated. If an iterator was + * not iterated until completion, tables may still be marked as changed. + * + * If no iterator is provided the operation will return the changed state of the + * all matched tables of the query. + * + * If an iterator is provided, the operation will return the changed state of + * the currently returned iterator result. The following preconditions must be + * met before using an iterator with change detection: + * + * - The iterator is a query iterator (created with ecs_query_iter()) + * - The iterator must be valid (ecs_query_next() must have returned true) + * + * @param query The query (optional if 'it' is provided). + * @return true if entities changed, otherwise false. + */ +FLECS_API +bool ecs_query_changed( + ecs_query_t *query); + +/** Skip a table while iterating. + * This operation lets the query iterator know that a table was skipped while + * iterating. A skipped table will not reset its changed state, and the query + * will not update the dirty flags of the table for its out columns. + * + * Only valid iterators must be provided (next has to be called at least once & + * return true) and the iterator must be a query iterator. + * + * @param it The iterator result to skip. + */ +FLECS_API +void ecs_iter_skip( + ecs_iter_t *it); + +/** Set group to iterate for query iterator. + * This operation limits the results returned by the query to only the selected + * group id. The query must have a group_by function, and the iterator must + * be a query iterator. + * + * Groups are sets of tables that are stored together in the query cache based + * on a group id, which is calculated per table by the group_by function. To + * iterate a group, an iterator only needs to know the first and last cache node + * for that group, which can both be found in a fast O(1) operation. + * + * As a result, group iteration is one of the most efficient mechanisms to + * filter out large numbers of entities, even if those entities are distributed + * across many tables. This makes it a good fit for things like dividing up + * a world into cells, and only iterating cells close to a player. + * + * The group to iterate must be set before the first call to ecs_query_next(). No + * operations that can add/remove components should be invoked between calling + * ecs_iter_set_group() and ecs_query_next(). + * + * @param it The query iterator. + * @param group_id The group to iterate. + */ +FLECS_API +void ecs_iter_set_group( + ecs_iter_t *it, + uint64_t group_id); + +/** Get context of query group. + * This operation returns the context of a query group as returned by the + * on_group_create callback. + * + * @param query The query. + * @param group_id The group for which to obtain the context. + * @return The group context, NULL if the group doesn't exist. + */ +FLECS_API +void* ecs_query_get_group_ctx( + const ecs_query_t *query, + uint64_t group_id); + +/** Get information about query group. + * This operation returns information about a query group, including the group + * context returned by the on_group_create callback. + * + * @param query The query. + * @param group_id The group for which to obtain the group info. + * @return The group info, NULL if the group doesn't exist. + */ +FLECS_API +const ecs_query_group_info_t* ecs_query_get_group_info( + const ecs_query_t *query, + uint64_t group_id); + +/** Struct returned by ecs_query_count(). */ +typedef struct ecs_query_count_t { + int32_t results; /**< Number of results returned by query. */ + int32_t entities; /**< Number of entities returned by query. */ + int32_t tables; /**< Number of tables returned by query. */ + int32_t empty_tables; /**< Number of empty tables returned by query. */ +} ecs_query_count_t; + +/** Returns number of entities and results the query matches with. + * Only entities matching the $this variable as source are counted. + * + * @param query The query. + * @return The number of matched entities. + */ +FLECS_API +ecs_query_count_t ecs_query_count( + const ecs_query_t *query); + +/** Does query return one or more results. + * + * @param query The query. + * @return True if query matches anything, false if not. + */ +FLECS_API +bool ecs_query_is_true( + const ecs_query_t *query); + +/** Get query used to populate cache. + * This operation returns the query that is used to populate the query cache. + * For queries that are can be entirely cached, the returned query will be + * equivalent to the query passed to ecs_query_get_cache_query(). + * + * @param query The query. + * @return The query used to populate the cache, NULL if query is not cached. + */ +FLECS_API +const ecs_query_t* ecs_query_get_cache_query( + const ecs_query_t *query); + +/** @} */ + +/** + * @defgroup observers Observers + * Functions for working with events and observers. + * + * @{ + */ + +/** Send event. + * This sends an event to matching triggers & is the mechanism used by flecs + * itself to send `OnAdd`, `OnRemove`, etc events. + * + * Applications can use this function to send custom events, where a custom + * event can be any regular entity. + * + * Applications should not send builtin flecs events, as this may violate + * assumptions the code makes about the conditions under which those events are + * sent. + * + * Triggers are invoked synchronously. It is therefore safe to use stack-based + * data as event context, which can be set in the "param" member. + * + * @param world The world. + * @param desc Event parameters. + * + * @see ecs_enqueue() + */ +FLECS_API +void ecs_emit( + ecs_world_t *world, + ecs_event_desc_t *desc); + +/** Enqueue event. + * Same as ecs_emit(), but enqueues an event in the command queue instead. The + * event will be emitted when ecs_defer_end() is called. + * + * If this operation is called when the provided world is not in deferred mode + * it behaves just like ecs_emit(). + * + * @param world The world. + * @param desc Event parameters. +*/ +FLECS_API +void ecs_enqueue( + ecs_world_t *world, + ecs_event_desc_t *desc); + +/** Create observer. + * Observers are like triggers, but can subscribe for multiple terms. An + * observer only triggers when the source of the event meets all terms. + * + * See the documentation for ecs_observer_desc_t for more details. + * + * @param world The world. + * @param desc The observer creation parameters. + * @return The observer, or 0 if the operation failed. + */ +FLECS_API +ecs_entity_t ecs_observer_init( + ecs_world_t *world, + const ecs_observer_desc_t *desc); + +/** Get observer object. + * Returns the observer object. Can be used to access various information about + * the observer, like the query and context. + * + * @param world The world. + * @param observer The observer. + * @return The observer object. + */ +FLECS_API +const ecs_observer_t* ecs_observer_get( + const ecs_world_t *world, + ecs_entity_t observer); + +/** @} */ + +/** + * @defgroup iterator Iterators + * Functions for working with `ecs_iter_t`. + * + * @{ + */ + +/** Progress any iterator. + * This operation is useful in combination with iterators for which it is not + * known what created them. Example use cases are functions that should accept + * any kind of iterator (such as serializers) or iterators created from poly + * objects. + * + * This operation is slightly slower than using a type-specific iterator (e.g. + * ecs_query_next, ecs_query_next) as it has to call a function pointer which + * introduces a level of indirection. + * + * @param it The iterator. + * @return True if iterator has more results, false if not. + */ +FLECS_API +bool ecs_iter_next( + ecs_iter_t *it); + +/** Cleanup iterator resources. + * This operation cleans up any resources associated with the iterator. + * + * This operation should only be used when an iterator is not iterated until + * completion (next has not yet returned false). When an iterator is iterated + * until completion, resources are automatically freed. + * + * @param it The iterator. + */ +FLECS_API +void ecs_iter_fini( + ecs_iter_t *it); + +/** Count number of matched entities in query. + * This operation returns the number of matched entities. If a query contains no + * matched entities but still yields results (e.g. it has no terms with This + * sources) the operation will return 0. + * + * To determine the number of matched entities, the operation iterates the + * iterator until it yields no more results. + * + * @param it The iterator. + * @return True if iterator has more results, false if not. + */ +FLECS_API +int32_t ecs_iter_count( + ecs_iter_t *it); + +/** Test if iterator is true. + * This operation will return true if the iterator returns at least one result. + * This is especially useful in combination with fact-checking queries (see the + * queries addon). + * + * The operation requires a valid iterator. After the operation is invoked, the + * application should no longer invoke next on the iterator and should treat it + * as if the iterator is iterated until completion. + * + * @param it The iterator. + * @return true if the iterator returns at least one result. + */ +FLECS_API +bool ecs_iter_is_true( + ecs_iter_t *it); + +/** Get first matching entity from iterator. + * After this operation the application should treat the iterator as if it has + * been iterated until completion. + * + * @param it The iterator. + * @return The first matching entity, or 0 if no entities were matched. + */ +FLECS_API +ecs_entity_t ecs_iter_first( + ecs_iter_t *it); + +/** Set value for iterator variable. + * This constrains the iterator to return only results for which the variable + * equals the specified value. The default value for all variables is + * EcsWildcard, which means the variable can assume any value. + * + * Example: + * + * @code + * // Query that matches (Eats, *) + * ecs_query_t *q = ecs_query(world, { + * .terms = { + * { .first.id = Eats, .second.name = "$food" } + * } + * }); + * + * int food_var = ecs_query_find_var(r, "food"); + * + * // Set Food to Apples, so we're only matching (Eats, Apples) + * ecs_iter_t it = ecs_query_iter(world, q); + * ecs_iter_set_var(&it, food_var, Apples); + * + * while (ecs_query_next(&it)) { + * for (int i = 0; i < it.count; i ++) { + * // iterate as usual + * } + * } + * @endcode + * + * The variable must be initialized after creating the iterator and before the + * first call to next. + * + * @param it The iterator. + * @param var_id The variable index. + * @param entity The entity variable value. + * + * @see ecs_iter_set_var_as_range() + * @see ecs_iter_set_var_as_table() + */ +FLECS_API +void ecs_iter_set_var( + ecs_iter_t *it, + int32_t var_id, + ecs_entity_t entity); + +/** Same as ecs_iter_set_var(), but for a table. + * This constrains the variable to all entities in a table. + * + * @param it The iterator. + * @param var_id The variable index. + * @param table The table variable value. + * + * @see ecs_iter_set_var() + * @see ecs_iter_set_var_as_range() + */ +FLECS_API +void ecs_iter_set_var_as_table( + ecs_iter_t *it, + int32_t var_id, + const ecs_table_t *table); + +/** Same as ecs_iter_set_var(), but for a range of entities + * This constrains the variable to a range of entities in a table. + * + * @param it The iterator. + * @param var_id The variable index. + * @param range The range variable value. + * + * @see ecs_iter_set_var() + * @see ecs_iter_set_var_as_table() + */ +FLECS_API +void ecs_iter_set_var_as_range( + ecs_iter_t *it, + int32_t var_id, + const ecs_table_range_t *range); + +/** Get value of iterator variable as entity. + * A variable can be interpreted as entity if it is set to an entity, or if it + * is set to a table range with count 1. + * + * This operation can only be invoked on valid iterators. The variable index + * must be smaller than the total number of variables provided by the iterator + * (as set in ecs_iter_t::variable_count). + * + * @param it The iterator. + * @param var_id The variable index. + * @return The variable value. + */ +FLECS_API +ecs_entity_t ecs_iter_get_var( + ecs_iter_t *it, + int32_t var_id); + +/** Get value of iterator variable as table. + * A variable can be interpreted as table if it is set as table range with + * both offset and count set to 0, or if offset is 0 and count matches the + * number of elements in the table. + * + * This operation can only be invoked on valid iterators. The variable index + * must be smaller than the total number of variables provided by the iterator + * (as set in ecs_iter_t::variable_count). + * + * @param it The iterator. + * @param var_id The variable index. + * @return The variable value. + */ +FLECS_API +ecs_table_t* ecs_iter_get_var_as_table( + ecs_iter_t *it, + int32_t var_id); + +/** Get value of iterator variable as table range. + * A value can be interpreted as table range if it is set as table range, or if + * it is set to an entity with a non-empty type (the entity must have at least + * one component, tag or relationship in its type). + * + * This operation can only be invoked on valid iterators. The variable index + * must be smaller than the total number of variables provided by the iterator + * (as set in ecs_iter_t::variable_count). + * + * @param it The iterator. + * @param var_id The variable index. + * @return The variable value. + */ +FLECS_API +ecs_table_range_t ecs_iter_get_var_as_range( + ecs_iter_t *it, + int32_t var_id); + +/** Returns whether variable is constrained. + * This operation returns true for variables set by one of the ecs_iter_set_var* + * operations. + * + * A constrained variable is guaranteed not to change values while results are + * being iterated. + * + * @param it The iterator. + * @param var_id The variable index. + * @return Whether the variable is constrained to a specified value. + */ +FLECS_API +bool ecs_iter_var_is_constrained( + ecs_iter_t *it, + int32_t var_id); + +/** Returns whether current iterator result has changed. + * This operation must be used in combination with a query that supports change + * detection (e.g. is cached). The operation returns whether the currently + * iterated result has changed since the last time it was iterated by the query. + * + * Change detection works on a per-table basis. Changes to individual entities + * cannot be detected this way. + * + * @param it The iterator. + * @return True if the result changed, false if it didn't. +*/ +FLECS_API +bool ecs_iter_changed( + ecs_iter_t *it); + +/** Convert iterator to string. + * Prints the contents of an iterator to a string. Useful for debugging and/or + * testing the output of an iterator. + * + * The function only converts the currently iterated data to a string. To + * convert all data, the application has to manually call the next function and + * call ecs_iter_str() on each result. + * + * @param it The iterator. + * @return A string representing the contents of the iterator. + */ +FLECS_API +char* ecs_iter_str( + const ecs_iter_t *it); + +/** Create a paged iterator. + * Paged iterators limit the results to those starting from 'offset', and will + * return at most 'limit' results. + * + * The iterator must be iterated with ecs_page_next(). + * + * A paged iterator acts as a passthrough for data exposed by the parent + * iterator, so that any data provided by the parent will also be provided by + * the paged iterator. + * + * @param it The source iterator. + * @param offset The number of entities to skip. + * @param limit The maximum number of entities to iterate. + * @return A page iterator. + */ +FLECS_API +ecs_iter_t ecs_page_iter( + const ecs_iter_t *it, + int32_t offset, + int32_t limit); + +/** Progress a paged iterator. + * Progresses an iterator created by ecs_page_iter(). + * + * @param it The iterator. + * @return true if iterator has more results, false if not. + */ +FLECS_API +bool ecs_page_next( + ecs_iter_t *it); + +/** Create a worker iterator. + * Worker iterators can be used to equally divide the number of matched entities + * across N resources (usually threads). Each resource will process the total + * number of matched entities divided by 'count'. + * + * Entities are distributed across resources such that the distribution is + * stable between queries. Two queries that match the same table are guaranteed + * to match the same entities in that table. + * + * The iterator must be iterated with ecs_worker_next(). + * + * A worker iterator acts as a passthrough for data exposed by the parent + * iterator, so that any data provided by the parent will also be provided by + * the worker iterator. + * + * @param it The source iterator. + * @param index The index of the current resource. + * @param count The total number of resources to divide entities between. + * @return A worker iterator. + */ +FLECS_API +ecs_iter_t ecs_worker_iter( + const ecs_iter_t *it, + int32_t index, + int32_t count); + +/** Progress a worker iterator. + * Progresses an iterator created by ecs_worker_iter(). + * + * @param it The iterator. + * @return true if iterator has more results, false if not. + */ +FLECS_API +bool ecs_worker_next( + ecs_iter_t *it); + +/** Get data for field. + * This operation retrieves a pointer to an array of data that belongs to the + * term in the query. The index refers to the location of the term in the query, + * and starts counting from zero. + * + * For example, the query `"Position, Velocity"` will return the `Position` array + * for index 0, and the `Velocity` array for index 1. + * + * When the specified field is not owned by the entity this function returns a + * pointer instead of an array. This happens when the source of a field is not + * the entity being iterated, such as a shared component (from a prefab), a + * component from a parent, or another entity. The ecs_field_is_self() operation + * can be used to test dynamically if a field is owned. + * + * When a field contains a sparse component, use the ecs_field_at function. When + * a field is guaranteed to be set and owned, the ecs_field_self() function can be + * used. ecs_field_self() has slightly better performance, and provides stricter + * validity checking. + * + * The provided size must be either 0 or must match the size of the type + * of the returned array. If the size does not match, the operation may assert. + * The size can be dynamically obtained with ecs_field_size(). + * + * An example: + * + * @code + * while (ecs_query_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * Velocity *v = ecs_field(&it, Velocity, 1); + * for (int32_t i = 0; i < it->count; i ++) { + * p[i].x += v[i].x; + * p[i].y += v[i].y; + * } + * } + * @endcode + * + * @param it The iterator. + * @param size The size of the field type. + * @param index The index of the field. + * @return A pointer to the data of the field. + */ +FLECS_API +void* ecs_field_w_size( + const ecs_iter_t *it, + size_t size, + int8_t index); + +/** Get data for field at specified row. + * This operation should be used instead of ecs_field_w_size for sparse + * component fields. This operation should be called for each returned row in a + * result. In the following example the Velocity component is sparse: + * + * @code + * while (ecs_query_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * for (int32_t i = 0; i < it->count; i ++) { + * Velocity *v = ecs_field_at(&it, Velocity, 1); + * p[i].x += v->x; + * p[i].y += v->y; + * } + * } + * @endcode + * + * @param it the iterator. + * @param size The size of the field type. + * @param index The index of the field. + * @return A pointer to the data of the field. + */ +FLECS_API +void* ecs_field_at_w_size( + const ecs_iter_t *it, + size_t size, + int8_t index, + int32_t row); + +/** Test whether the field is readonly. + * This operation returns whether the field is readonly. Readonly fields are + * annotated with [in], or are added as a const type in the C++ API. + * + * @param it The iterator. + * @param index The index of the field in the iterator. + * @return Whether the field is readonly. + */ +FLECS_API +bool ecs_field_is_readonly( + const ecs_iter_t *it, + int8_t index); + +/** Test whether the field is writeonly. + * This operation returns whether this is a writeonly field. Writeonly terms are + * annotated with [out]. + * + * Serializers are not required to serialize the values of a writeonly field. + * + * @param it The iterator. + * @param index The index of the field in the iterator. + * @return Whether the field is writeonly. + */ +FLECS_API +bool ecs_field_is_writeonly( + const ecs_iter_t *it, + int8_t index); + +/** Test whether field is set. + * + * @param it The iterator. + * @param index The index of the field in the iterator. + * @return Whether the field is set. + */ +FLECS_API +bool ecs_field_is_set( + const ecs_iter_t *it, + int8_t index); + +/** Return id matched for field. + * + * @param it The iterator. + * @param index The index of the field in the iterator. + * @return The id matched for the field. + */ +FLECS_API +ecs_id_t ecs_field_id( + const ecs_iter_t *it, + int8_t index); + +/** Return index of matched table column. + * This function only returns column indices for fields that have been matched + * on the $this variable. Fields matched on other tables will return -1. + * + * @param it The iterator. + * @param index The index of the field in the iterator. + * @return The index of the matched column, -1 if not matched. + */ +FLECS_API +int32_t ecs_field_column( + const ecs_iter_t *it, + int8_t index); + +/** Return field source. + * The field source is the entity on which the field was matched. + * + * @param it The iterator. + * @param index The index of the field in the iterator. + * @return The source for the field. + */ +FLECS_API +ecs_entity_t ecs_field_src( + const ecs_iter_t *it, + int8_t index); + +/** Return field type size. + * Return type size of the field. Returns 0 if the field has no data. + * + * @param it The iterator. + * @param index The index of the field in the iterator. + * @return The type size for the field. + */ +FLECS_API +size_t ecs_field_size( + const ecs_iter_t *it, + int8_t index); + +/** Test whether the field is matched on self. + * This operation returns whether the field is matched on the currently iterated + * entity. This function will return false when the field is owned by another + * entity, such as a parent or a prefab. + * + * When this operation returns false, the field must be accessed as a single + * value instead of an array. Fields for which this operation returns true + * return arrays with it->count values. + * + * @param it The iterator. + * @param index The index of the field in the iterator. + * @return Whether the field is matched on self. + */ +FLECS_API +bool ecs_field_is_self( + const ecs_iter_t *it, + int8_t index); + +/** @} */ + +/** + * @defgroup tables Tables + * Functions for working with `ecs_table_t`. + * + * @{ + */ + +/** Get type for table. + * The table type is a vector that contains all component, tag and pair ids. + * + * @param table The table. + * @return The type of the table. + */ +FLECS_API +const ecs_type_t* ecs_table_get_type( + const ecs_table_t *table); + +/** Get type index for id. + * This operation returns the index for an id in the table's type. + * + * @param world The world. + * @param table The table. + * @param id The id. + * @return The index of the id in the table type, or -1 if not found. + * + * @see ecs_table_has_id() + */ +FLECS_API +int32_t ecs_table_get_type_index( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id); + +/** Get column index for id. + * This operation returns the column index for an id in the table's type. If the + * id is not a component, the function will return -1. + * + * @param world The world. + * @param table The table. + * @param id The component id. + * @return The column index of the id, or -1 if not found/not a component. + */ +FLECS_API +int32_t ecs_table_get_column_index( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id); + +/** Return number of columns in table. + * Similar to `ecs_table_get_type(table)->count`, except that the column count + * only counts the number of components in a table. + * + * @param table The table. + * @return The number of columns in the table. + */ +FLECS_API +int32_t ecs_table_column_count( + const ecs_table_t *table); + +/** Convert type index to column index. + * Tables have an array of columns for each component in the table. This array + * does not include elements for tags, which means that the index for a + * component in the table type is not necessarily the same as the index in the + * column array. This operation converts from an index in the table type to an + * index in the column array. + * + * @param table The table. + * @param index The index in the table type. + * @return The index in the table column array. + * + * @see ecs_table_column_to_type_index() + */ +FLECS_API +int32_t ecs_table_type_to_column_index( + const ecs_table_t *table, + int32_t index); + +/** Convert column index to type index. + * Same as ecs_table_type_to_column_index(), but converts from an index in the + * column array to an index in the table type. + * + * @param table The table. + * @param index The column index. + * @return The index in the table type. + */ +FLECS_API +int32_t ecs_table_column_to_type_index( + const ecs_table_t *table, + int32_t index); + +/** Get column from table by column index. + * This operation returns the component array for the provided index. + * + * @param table The table. + * @param index The column index. + * @param offset The index of the first row to return (0 for entire column). + * @return The component array, or NULL if the index is not a component. + */ +FLECS_API +void* ecs_table_get_column( + const ecs_table_t *table, + int32_t index, + int32_t offset); + +/** Get column from table by component id. + * This operation returns the component array for the provided component id. + * + * @param world The world. + * @param table The table. + * @param id The component id for the column. + * @param offset The index of the first row to return (0 for entire column). + * @return The component array, or NULL if the index is not a component. + */ +FLECS_API +void* ecs_table_get_id( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id, + int32_t offset); + +/** Get column size from table. + * This operation returns the component size for the provided index. + * + * @param table The table. + * @param index The column index. + * @return The component size, or 0 if the index is not a component. + */ +FLECS_API +size_t ecs_table_get_column_size( + const ecs_table_t *table, + int32_t index); + +/** Returns the number of entities in the table. + * This operation returns the number of entities in the table. + * + * @param table The table. + * @return The number of entities in the table. + */ +FLECS_API +int32_t ecs_table_count( + const ecs_table_t *table); + +/** Returns allocated size of table. + * This operation returns the number of elements allocated in the table + * per column. + * + * @param table The table. + * @return The number of allocated elements in the table. + */ +FLECS_API +int32_t ecs_table_size( + const ecs_table_t *table); + +/** Returns array with entity ids for table. + * The size of the returned array is the result of ecs_table_count(). + * + * @param table The table. + * @return Array with entity ids for table. + */ +FLECS_API +const ecs_entity_t* ecs_table_entities( + const ecs_table_t *table); + +/** Test if table has id. + * Same as `ecs_table_get_type_index(world, table, id) != -1`. + * + * @param world The world. + * @param table The table. + * @param id The id. + * @return True if the table has the id, false if the table doesn't. + * + * @see ecs_table_get_type_index() + */ +FLECS_API +bool ecs_table_has_id( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id); + +/** Return depth for table in tree for relationship rel. + * Depth is determined by counting the number of targets encountered while + * traversing up the relationship tree for rel. Only acyclic relationships are + * supported. + * + * @param world The world. + * @param table The table. + * @param rel The relationship. + * @return The depth of the table in the tree. + */ +FLECS_API +int32_t ecs_table_get_depth( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_entity_t rel); + +/** Get table that has all components of current table plus the specified id. + * If the provided table already has the provided id, the operation will return + * the provided table. + * + * @param world The world. + * @param table The table. + * @param id The id to add. + * @result The resulting table. + */ +FLECS_API +ecs_table_t* ecs_table_add_id( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id); + +/** Find table from id array. + * This operation finds or creates a table with the specified array of + * (component) ids. The ids in the array must be sorted, and it may not contain + * duplicate elements. + * + * @param world The world. + * @param ids The id array. + * @param id_count The number of elements in the id array. + * @return The table with the specified (component) ids. + */ +FLECS_API +ecs_table_t* ecs_table_find( + ecs_world_t *world, + const ecs_id_t *ids, + int32_t id_count); + +/** Get table that has all components of current table minus the specified id. + * If the provided table doesn't have the provided id, the operation will return + * the provided table. + * + * @param world The world. + * @param table The table. + * @param id The id to remove. + * @result The resulting table. + */ +FLECS_API +ecs_table_t* ecs_table_remove_id( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id); + +/** Lock a table. + * When a table is locked, modifications to it will throw an assert. When the + * table is locked recursively, it will take an equal amount of unlock + * operations to actually unlock the table. + * + * Table locks can be used to build safe iterators where it is guaranteed that + * the contents of a table are not modified while it is being iterated. + * + * The operation only works when called on the world, and has no side effects + * when called on a stage. The assumption is that when called on a stage, + * operations are deferred already. + * + * @param world The world. + * @param table The table to lock. + */ +FLECS_API +void ecs_table_lock( + ecs_world_t *world, + ecs_table_t *table); + +/** Unlock a table. + * Must be called after calling ecs_table_lock(). + * + * @param world The world. + * @param table The table to unlock. + */ +FLECS_API +void ecs_table_unlock( + ecs_world_t *world, + ecs_table_t *table); + +/** Test table for flags. + * Test if table has all of the provided flags. See + * include/flecs/private/api_flags.h for a list of table flags that can be used + * with this function. + * + * @param table The table. + * @param flags The flags to test for. + * @return Whether the specified flags are set for the table. + */ +FLECS_API +bool ecs_table_has_flags( + ecs_table_t *table, + ecs_flags32_t flags); + +/** Swaps two elements inside the table. This is useful for implementing custom + * table sorting algorithms. + * @param world The world + * @param table The table to swap elements in + * @param row_1 Table element to swap with row_2 + * @param row_2 Table element to swap with row_1 +*/ +FLECS_API +void ecs_table_swap_rows( + ecs_world_t* world, + ecs_table_t* table, + int32_t row_1, + int32_t row_2); + +/** Commit (move) entity to a table. + * This operation moves an entity from its current table to the specified + * table. This may cause the following actions: + * - Ctor for each component in the target table + * - Move for each overlapping component + * - Dtor for each component in the source table. + * - `OnAdd` triggers for non-overlapping components in the target table + * - `OnRemove` triggers for non-overlapping components in the source table. + * + * This operation is a faster than adding/removing components individually. + * + * The application must explicitly provide the difference in components between + * tables as the added/removed parameters. This can usually be derived directly + * from the result of ecs_table_add_id() and ecs_table_remove_id(). These arrays are + * required to properly execute `OnAdd`/`OnRemove` triggers. + * + * @param world The world. + * @param entity The entity to commit. + * @param record The entity's record (optional, providing it saves a lookup). + * @param table The table to commit the entity to. + * @return True if the entity got moved, false otherwise. + */ +FLECS_API +bool ecs_commit( + ecs_world_t *world, + ecs_entity_t entity, + ecs_record_t *record, + ecs_table_t *table, + const ecs_type_t *added, + const ecs_type_t *removed); + + +/** Search for component id in table type. + * This operation returns the index of first occurrence of the id in the table + * type. The id may be a wildcard. + * + * When id_out is provided, the function will assign it with the found id. The + * found id may be different from the provided id if it is a wildcard. + * + * This is a constant time operation. + * + * @param world The world. + * @param table The table. + * @param id The id to search for. + * @param id_out If provided, it will be set to the found id (optional). + * @return The index of the id in the table type. + * + * @see ecs_search_offset() + * @see ecs_search_relation() + */ +FLECS_API +int32_t ecs_search( + const ecs_world_t *world, + const ecs_table_t *table, + ecs_id_t id, + ecs_id_t *id_out); + +/** Search for component id in table type starting from an offset. + * This operation is the same as ecs_search(), but starts searching from an offset + * in the table type. + * + * This operation is typically called in a loop where the resulting index is + * used in the next iteration as offset: + * + * @code + * int32_t index = -1; + * while ((index = ecs_search_offset(world, table, offset, id, NULL))) { + * // do stuff + * } + * @endcode + * + * Depending on how the operation is used it is either linear or constant time. + * When the id has the form `(id)` or `(rel, *)` and the operation is invoked as + * in the above example, it is guaranteed to be constant time. + * + * If the provided id has the form `(*, tgt)` the operation takes linear time. The + * reason for this is that ids for an target are not packed together, as they + * are sorted relationship first. + * + * If the id at the offset does not match the provided id, the operation will do + * a linear search to find a matching id. + * + * @param world The world. + * @param table The table. + * @param offset Offset from where to start searching. + * @param id The id to search for. + * @param id_out If provided, it will be set to the found id (optional). + * @return The index of the id in the table type. + * + * @see ecs_search() + * @see ecs_search_relation() + */ +FLECS_API +int32_t ecs_search_offset( + const ecs_world_t *world, + const ecs_table_t *table, + int32_t offset, + ecs_id_t id, + ecs_id_t *id_out); + +/** Search for component/relationship id in table type starting from an offset. + * This operation is the same as ecs_search_offset(), but has the additional + * capability of traversing relationships to find a component. For example, if + * an application wants to find a component for either the provided table or a + * prefab (using the `IsA` relationship) of that table, it could use the operation + * like this: + * + * @code + * int32_t index = ecs_search_relation( + * world, // the world + * table, // the table + * 0, // offset 0 + * ecs_id(Position), // the component id + * EcsIsA, // the relationship to traverse + * 0, // start at depth 0 (the table itself) + * 0, // no depth limit + * NULL, // (optional) entity on which component was found + * NULL, // see above + * NULL); // internal type with information about matched id + * @endcode + * + * The operation searches depth first. If a table type has 2 `IsA` relationships, the + * operation will first search the `IsA` tree of the first relationship. + * + * When choosing between ecs_search(), ecs_search_offset() and ecs_search_relation(), + * the simpler the function the better its performance. + * + * @param world The world. + * @param table The table. + * @param offset Offset from where to start searching. + * @param id The id to search for. + * @param rel The relationship to traverse (optional). + * @param flags Whether to search EcsSelf and/or EcsUp. + * @param subject_out If provided, it will be set to the matched entity. + * @param id_out If provided, it will be set to the found id (optional). + * @param tr_out Internal datatype. + * @return The index of the id in the table type. + * + * @see ecs_search() + * @see ecs_search_offset() + */ +FLECS_API +int32_t ecs_search_relation( + const ecs_world_t *world, + const ecs_table_t *table, + int32_t offset, + ecs_id_t id, + ecs_entity_t rel, + ecs_flags64_t flags, /* EcsSelf and/or EcsUp */ + ecs_entity_t *subject_out, + ecs_id_t *id_out, + struct ecs_table_record_t **tr_out); + +/** Remove all entities in a table. Does not deallocate table memory. + * Retaining table memory can be efficient when planning + * to refill the table with operations like ecs_bulk_init + * + * @param world The world. + * @param table The table to clear. + */ +FLECS_API +void ecs_table_clear_entities( + ecs_world_t* world, + ecs_table_t* table); + +/** @} */ + +/** + * @defgroup values Values + * Construct, destruct, copy and move dynamically created values. + * + * @{ + */ + +/** Construct a value in existing storage + * + * @param world The world. + * @param type The type of the value to create. + * @param ptr Pointer to a value of type 'type' + * @return Zero if success, nonzero if failed. + */ +FLECS_API +int ecs_value_init( + const ecs_world_t *world, + ecs_entity_t type, + void *ptr); + +/** Construct a value in existing storage + * + * @param world The world. + * @param ti The type info of the type to create. + * @param ptr Pointer to a value of type 'type' + * @return Zero if success, nonzero if failed. + */ +FLECS_API +int ecs_value_init_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void *ptr); + +/** Construct a value in new storage + * + * @param world The world. + * @param type The type of the value to create. + * @return Pointer to type if success, NULL if failed. + */ +FLECS_API +void* ecs_value_new( + ecs_world_t *world, + ecs_entity_t type); + +/** Construct a value in new storage + * + * @param world The world. + * @param ti The type info of the type to create. + * @return Pointer to type if success, NULL if failed. + */ +void* ecs_value_new_w_type_info( + ecs_world_t *world, + const ecs_type_info_t *ti); + +/** Destruct a value + * + * @param world The world. + * @param ti Type info of the value to destruct. + * @param ptr Pointer to constructed value of type 'type'. + * @return Zero if success, nonzero if failed. + */ +int ecs_value_fini_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void *ptr); + +/** Destruct a value + * + * @param world The world. + * @param type The type of the value to destruct. + * @param ptr Pointer to constructed value of type 'type'. + * @return Zero if success, nonzero if failed. + */ +FLECS_API +int ecs_value_fini( + const ecs_world_t *world, + ecs_entity_t type, + void* ptr); + +/** Destruct a value, free storage + * + * @param world The world. + * @param type The type of the value to destruct. + * @param ptr A pointer to the value. + * @return Zero if success, nonzero if failed. + */ +FLECS_API +int ecs_value_free( + ecs_world_t *world, + ecs_entity_t type, + void* ptr); + +/** Copy value. + * + * @param world The world. + * @param ti Type info of the value to copy. + * @param dst Pointer to the storage to copy to. + * @param src Pointer to the value to copy. + * @return Zero if success, nonzero if failed. + */ +FLECS_API +int ecs_value_copy_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void* dst, + const void *src); + +/** Copy value. + * + * @param world The world. + * @param type The type of the value to copy. + * @param dst Pointer to the storage to copy to. + * @param src Pointer to the value to copy. + * @return Zero if success, nonzero if failed. + */ +FLECS_API +int ecs_value_copy( + const ecs_world_t *world, + ecs_entity_t type, + void* dst, + const void *src); + +/** Move value. + * + * @param world The world. + * @param ti Type info of the value to move. + * @param dst Pointer to the storage to move to. + * @param src Pointer to the value to move. + * @return Zero if success, nonzero if failed. + */ +int ecs_value_move_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void* dst, + void *src); + +/** Move value. + * + * @param world The world. + * @param type The type of the value to move. + * @param dst Pointer to the storage to move to. + * @param src Pointer to the value to move. + * @return Zero if success, nonzero if failed. + */ +int ecs_value_move( + const ecs_world_t *world, + ecs_entity_t type, + void* dst, + void *src); + +/** Move construct value. + * + * @param world The world. + * @param ti Type info of the value to move. + * @param dst Pointer to the storage to move to. + * @param src Pointer to the value to move. + * @return Zero if success, nonzero if failed. + */ +int ecs_value_move_ctor_w_type_info( + const ecs_world_t *world, + const ecs_type_info_t *ti, + void* dst, + void *src); + +/** Move construct value. + * + * @param world The world. + * @param type The type of the value to move. + * @param dst Pointer to the storage to move to. + * @param src Pointer to the value to move. + * @return Zero if success, nonzero if failed. + */ +int ecs_value_move_ctor( + const ecs_world_t *world, + ecs_entity_t type, + void* dst, + void *src); + +/** @} */ + +/** @} */ + +/** + * @defgroup c_addons Addons + * @ingroup c + * C APIs for addons. + * + * @{ + * @} + */ + +/** + * @file addons/flecs_c.h + * @brief Extends the core API with convenience macros for C applications. + */ + +#ifndef FLECS_C_ +#define FLECS_C_ + +/** + * @defgroup flecs_c Macro API + * @ingroup c + * Convenience macro's for C API + * + * @{ + */ + +/** + * @defgroup flecs_c_creation Creation macro's + * Convenience macro's for creating entities, components and observers + * + * @{ + */ + +/* Use for declaring entity, tag, prefab / any other entity identifier */ +#define ECS_DECLARE(id)\ + ecs_entity_t id, ecs_id(id) + +/** Forward declare an entity. */ +#define ECS_ENTITY_DECLARE ECS_DECLARE + +/** Define a forward declared entity. + * + * Example: + * + * @code + * ECS_ENTITY_DEFINE(world, MyEntity, Position, Velocity); + * @endcode + */ +#define ECS_ENTITY_DEFINE(world, id_, ...) \ + { \ + ecs_entity_desc_t desc = {0}; \ + desc.id = id_; \ + desc.name = #id_; \ + desc.add_expr = #__VA_ARGS__; \ + id_ = ecs_entity_init(world, &desc); \ + ecs_id(id_) = id_; \ + ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, "failed to create entity %s", #id_); \ + } \ + (void)id_; \ + (void)ecs_id(id_) + +/** Declare & define an entity. + * + * Example: + * + * @code + * ECS_ENTITY(world, MyEntity, Position, Velocity); + * @endcode + */ +#define ECS_ENTITY(world, id, ...) \ + ecs_entity_t ecs_id(id); \ + ecs_entity_t id = 0; \ + ECS_ENTITY_DEFINE(world, id, __VA_ARGS__) + +/** Forward declare a tag. */ +#define ECS_TAG_DECLARE ECS_DECLARE + +/** Define a forward declared tag. + * + * Example: + * + * @code + * ECS_TAG_DEFINE(world, MyTag); + * @endcode + */ +#define ECS_TAG_DEFINE(world, id) ECS_ENTITY_DEFINE(world, id, 0) + +/** Declare & define a tag. + * + * Example: + * + * @code + * ECS_TAG(world, MyTag); + * @endcode + */ +#define ECS_TAG(world, id) ECS_ENTITY(world, id, 0) + +/** Forward declare a prefab. */ +#define ECS_PREFAB_DECLARE ECS_DECLARE + +/** Define a forward declared prefab. + * + * Example: + * + * @code + * ECS_PREFAB_DEFINE(world, MyPrefab, Position, Velocity); + * @endcode + */ +#define ECS_PREFAB_DEFINE(world, id, ...) ECS_ENTITY_DEFINE(world, id, Prefab, __VA_ARGS__) + +/** Declare & define a prefab. + * + * Example: + * + * @code + * ECS_PREFAB(world, MyPrefab, Position, Velocity); + * @endcode + */ +#define ECS_PREFAB(world, id, ...) ECS_ENTITY(world, id, Prefab, __VA_ARGS__) + +/** Forward declare a component. */ +#define ECS_COMPONENT_DECLARE(id) ecs_entity_t ecs_id(id) + +/** Define a forward declared component. + * + * Example: + * + * @code + * ECS_COMPONENT_DEFINE(world, Position); + * @endcode + */ +#define ECS_COMPONENT_DEFINE(world, id_) \ + {\ + ecs_component_desc_t desc = {0}; \ + ecs_entity_desc_t edesc = {0}; \ + edesc.id = ecs_id(id_); \ + edesc.use_low_id = true; \ + edesc.name = #id_; \ + edesc.symbol = #id_; \ + desc.entity = ecs_entity_init(world, &edesc); \ + desc.type.size = ECS_SIZEOF(id_); \ + desc.type.alignment = ECS_ALIGNOF(id_); \ + ecs_id(id_) = ecs_component_init(world, &desc);\ + }\ + ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, "failed to create component %s", #id_) + +/** Declare & define a component. + * + * Example: + * + * @code + * ECS_COMPONENT(world, Position); + * @endcode + */ +#define ECS_COMPONENT(world, id)\ + ecs_entity_t ecs_id(id) = 0;\ + ECS_COMPONENT_DEFINE(world, id);\ + (void)ecs_id(id) + +/* Forward declare an observer. */ +#define ECS_OBSERVER_DECLARE(id) ecs_entity_t ecs_id(id) + +/** Define a forward declared observer. + * + * Example: + * + * @code + * ECS_OBSERVER_DEFINE(world, AddPosition, EcsOnAdd, Position); + * @endcode + */ +#define ECS_OBSERVER_DEFINE(world, id_, kind, ...)\ + {\ + ecs_observer_desc_t desc = {0};\ + ecs_entity_desc_t edesc = {0}; \ + edesc.id = ecs_id(id_); \ + edesc.name = #id_; \ + desc.entity = ecs_entity_init(world, &edesc); \ + desc.callback = id_;\ + desc.query.expr = #__VA_ARGS__;\ + desc.events[0] = kind;\ + ecs_id(id_) = ecs_observer_init(world, &desc);\ + ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, "failed to create observer %s", #id_);\ + } + +/** Declare & define an observer. + * + * Example: + * + * @code + * ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); + * @endcode + */ +#define ECS_OBSERVER(world, id, kind, ...)\ + ecs_entity_t ecs_id(id) = 0; \ + ECS_OBSERVER_DEFINE(world, id, kind, __VA_ARGS__);\ + ecs_entity_t id = ecs_id(id);\ + (void)ecs_id(id);\ + (void)id + +/* Forward declare a query. */ +#define ECS_QUERY_DECLARE(name) ecs_query_t* name + +/** Define a forward declared observer. + * + * Example: + * + * @code + * ECS_QUERY_DEFINE(world, AddPosition, Position); + * @endcode + */ +#define ECS_QUERY_DEFINE(world, name_, ...)\ + {\ + ecs_query_desc_t desc = {0};\ + ecs_entity_desc_t edesc = {0}; \ + edesc.name = #name_; \ + desc.entity = ecs_entity_init(world, &edesc); \ + desc.expr = #__VA_ARGS__;\ + name_ = ecs_query_init(world, &desc);\ + ecs_assert(name_ != NULL, ECS_INVALID_PARAMETER, "failed to create query %s", #name_);\ + } + +/** Declare & define an observer. + * + * Example: + * + * @code + * ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); + * @endcode + */ +#define ECS_QUERY(world, name, ...)\ + ecs_query_t* name = NULL; \ + ECS_QUERY_DEFINE(world, name, __VA_ARGS__);\ + (void)name + +/** Shorthand for creating an entity with ecs_entity_init(). + * + * Example: + * + * @code + * ecs_entity(world, { + * .name = "MyEntity" + * }); + * @endcode + */ +#define ecs_entity(world, ...)\ + ecs_entity_init(world, &(ecs_entity_desc_t) __VA_ARGS__ ) + +/** Shorthand for creating a component with ecs_component_init(). + * + * Example: + * + * @code + * ecs_component(world, { + * .type.size = 4, + * .type.alignment = 4 + * }); + * @endcode + */ +#define ecs_component(world, ...)\ + ecs_component_init(world, &(ecs_component_desc_t) __VA_ARGS__ ) + +/** Shorthand for creating a component from a type. + * + * Example: + * + * @code + * ecs_component_t(world, Position); + * @endcode + */ +#define ecs_component_t(world, T)\ + ecs_component_init(world, &(ecs_component_desc_t) { \ + .entity = ecs_entity(world, { \ + .name = #T, \ + .symbol = #T, \ + .use_low_id = true \ + }), \ + .type.size = ECS_SIZEOF(T), \ + .type.alignment = ECS_ALIGNOF(T) \ + }) + +/** Shorthand for creating a query with ecs_query_cache_init. + * + * Example: + * ecs_query(world, { + * .terms = {{ ecs_id(Position) }} + * }); + */ +#define ecs_query(world, ...)\ + ecs_query_init(world, &(ecs_query_desc_t) __VA_ARGS__ ) + +/** Shorthand for creating an observer with ecs_observer_init(). + * + * Example: + * + * @code + * ecs_observer(world, { + * .terms = {{ ecs_id(Position) }}, + * .events = { EcsOnAdd }, + * .callback = AddPosition + * }); + * @endcode + */ +#define ecs_observer(world, ...)\ + ecs_observer_init(world, &(ecs_observer_desc_t) __VA_ARGS__ ) + +/** @} */ + +/** + * @defgroup flecs_c_type_safe Type Safe API + * Macro's that wrap around core functions to provide a "type safe" API in C + * + * @{ + */ + +/** + * @defgroup flecs_c_entities Entity API + * @{ + */ + +/** + * @defgroup flecs_c_creation_deletion Creation & Deletion + * @{ + */ + +#define ecs_new_w(world, T) ecs_new_w_id(world, ecs_id(T)) + +#define ecs_new_w_pair(world, first, second)\ + ecs_new_w_id(world, ecs_pair(first, second)) + +#define ecs_bulk_new(world, component, count)\ + ecs_bulk_new_w_id(world, ecs_id(component), count) + +/** @} */ + +/** + * @defgroup flecs_c_adding_removing Adding & Removing + * @{ + */ + +#define ecs_add(world, entity, T)\ + ecs_add_id(world, entity, ecs_id(T)) + +#define ecs_add_pair(world, subject, first, second)\ + ecs_add_id(world, subject, ecs_pair(first, second)) + + +#define ecs_remove(world, entity, T)\ + ecs_remove_id(world, entity, ecs_id(T)) + +#define ecs_remove_pair(world, subject, first, second)\ + ecs_remove_id(world, subject, ecs_pair(first, second)) + + +#define ecs_auto_override(world, entity, T)\ + ecs_auto_override_id(world, entity, ecs_id(T)) + +#define ecs_auto_override_pair(world, subject, first, second)\ + ecs_auto_override_id(world, subject, ecs_pair(first, second)) + +/** @} */ + +/** + * @defgroup flecs_c_getting_setting Getting & Setting + * @{ + */ + +/* insert */ +#define ecs_insert(world, ...)\ + ecs_entity(world, { .set = ecs_values(__VA_ARGS__)}) + +/* set */ + +#define ecs_set_ptr(world, entity, component, ptr)\ + ecs_set_id(world, entity, ecs_id(component), sizeof(component), ptr) + +#define ecs_set(world, entity, component, ...)\ + ecs_set_id(world, entity, ecs_id(component), sizeof(component), &(component)__VA_ARGS__) + +#define ecs_set_pair(world, subject, First, second, ...)\ + ecs_set_id(world, subject,\ + ecs_pair(ecs_id(First), second),\ + sizeof(First), &(First)__VA_ARGS__) + +#define ecs_set_pair_second(world, subject, first, Second, ...)\ + ecs_set_id(world, subject,\ + ecs_pair(first, ecs_id(Second)),\ + sizeof(Second), &(Second)__VA_ARGS__) + +#define ecs_set_override(world, entity, T, ...)\ + ecs_add_id(world, entity, ECS_AUTO_OVERRIDE | ecs_id(T));\ + ecs_set(world, entity, T, __VA_ARGS__) + +/* emplace */ + +#define ecs_emplace(world, entity, T, is_new)\ + (ECS_CAST(T*, ecs_emplace_id(world, entity, ecs_id(T), is_new))) + +#define ecs_emplace_pair(world, entity, First, second, is_new)\ + (ECS_CAST(First*, ecs_emplace_id(world, entity, ecs_pair_t(First, second), is_new))) + +/* get */ + +#define ecs_get(world, entity, T)\ + (ECS_CAST(const T*, ecs_get_id(world, entity, ecs_id(T)))) + +#define ecs_get_pair(world, subject, First, second)\ + (ECS_CAST(const First*, ecs_get_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_get_pair_second(world, subject, first, Second)\ + (ECS_CAST(const Second*, ecs_get_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +/* get_mut */ + +#define ecs_get_mut(world, entity, T)\ + (ECS_CAST(T*, ecs_get_mut_id(world, entity, ecs_id(T)))) + +#define ecs_get_mut_pair(world, subject, First, second)\ + (ECS_CAST(First*, ecs_get_mut_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_get_mut_pair_second(world, subject, first, Second)\ + (ECS_CAST(Second*, ecs_get_mut_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +#define ecs_get_mut(world, entity, T)\ + (ECS_CAST(T*, ecs_get_mut_id(world, entity, ecs_id(T)))) + +/* ensure */ + +#define ecs_ensure(world, entity, T)\ + (ECS_CAST(T*, ecs_ensure_id(world, entity, ecs_id(T)))) + +#define ecs_ensure_pair(world, subject, First, second)\ + (ECS_CAST(First*, ecs_ensure_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_ensure_pair_second(world, subject, first, Second)\ + (ECS_CAST(Second*, ecs_ensure_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +#define ecs_ensure(world, entity, T)\ + (ECS_CAST(T*, ecs_ensure_id(world, entity, ecs_id(T)))) + +#define ecs_ensure_pair(world, subject, First, second)\ + (ECS_CAST(First*, ecs_ensure_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_ensure_pair_second(world, subject, first, Second)\ + (ECS_CAST(Second*, ecs_ensure_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +/* modified */ + +#define ecs_modified(world, entity, component)\ + ecs_modified_id(world, entity, ecs_id(component)) + +#define ecs_modified_pair(world, subject, first, second)\ + ecs_modified_id(world, subject, ecs_pair(first, second)) + +/* record */ + +#define ecs_record_get(world, record, T)\ + (ECS_CAST(const T*, ecs_record_get_id(world, record, ecs_id(T)))) + +#define ecs_record_has(world, record, T)\ + (ecs_record_has_id(world, record, ecs_id(T))) + +#define ecs_record_get_pair(world, record, First, second)\ + (ECS_CAST(const First*, ecs_record_get_id(world, record, \ + ecs_pair(ecs_id(First), second)))) + +#define ecs_record_get_pair_second(world, record, first, Second)\ + (ECS_CAST(const Second*, ecs_record_get_id(world, record,\ + ecs_pair(first, ecs_id(Second))))) + +#define ecs_record_ensure(world, record, T)\ + (ECS_CAST(T*, ecs_record_ensure_id(world, record, ecs_id(T)))) + +#define ecs_record_ensure_pair(world, record, First, second)\ + (ECS_CAST(First*, ecs_record_ensure_id(world, record, \ + ecs_pair(ecs_id(First), second)))) + +#define ecs_record_ensure_pair_second(world, record, first, Second)\ + (ECS_CAST(Second*, ecs_record_ensure_id(world, record,\ + ecs_pair(first, ecs_id(Second))))) + +#define ecs_ref_init(world, entity, T)\ + ecs_ref_init_id(world, entity, ecs_id(T)) + +#define ecs_ref_get(world, ref, T)\ + (ECS_CAST(T*, ecs_ref_get_id(world, ref, ecs_id(T)))) + +/** @} */ + +/** + * @defgroup flecs_c_singletons Singletons + * @{ + */ + +#define ecs_singleton_add(world, comp)\ + ecs_add(world, ecs_id(comp), comp) + +#define ecs_singleton_remove(world, comp)\ + ecs_remove(world, ecs_id(comp), comp) + +#define ecs_singleton_get(world, comp)\ + ecs_get(world, ecs_id(comp), comp) + +#define ecs_singleton_set_ptr(world, comp, ptr)\ + ecs_set_ptr(world, ecs_id(comp), comp, ptr) + +#define ecs_singleton_set(world, comp, ...)\ + ecs_set(world, ecs_id(comp), comp, __VA_ARGS__) + +#define ecs_singleton_ensure(world, comp)\ + ecs_ensure(world, ecs_id(comp), comp) + +#define ecs_singleton_modified(world, comp)\ + ecs_modified(world, ecs_id(comp), comp) + +/** @} */ + +/** + * @defgroup flecs_c_has Has, Owns, Shares + * @{ + */ + +#define ecs_has(world, entity, T)\ + ecs_has_id(world, entity, ecs_id(T)) + +#define ecs_has_pair(world, entity, first, second)\ + ecs_has_id(world, entity, ecs_pair(first, second)) + +#define ecs_owns_pair(world, entity, first, second)\ + ecs_owns_id(world, entity, ecs_pair(first, second)) + +#define ecs_owns(world, entity, T)\ + ecs_owns_id(world, entity, ecs_id(T)) + +#define ecs_shares_id(world, entity, id)\ + (ecs_search_relation(world, ecs_get_table(world, entity), 0, ecs_id(id), \ + EcsIsA, 1, 0, 0, 0, 0) != -1) + +#define ecs_shares_pair(world, entity, first, second)\ + (ecs_shares_id(world, entity, ecs_pair(first, second))) + +#define ecs_shares(world, entity, T)\ + (ecs_shares_id(world, entity, ecs_id(T))) + +#define ecs_get_target_for(world, entity, rel, T)\ + ecs_get_target_for_id(world, entity, rel, ecs_id(T)) + +/** @} */ + +/** + * @defgroup flecs_c_enable_disable Enabling & Disabling + * @{ + */ + +#define ecs_enable_component(world, entity, T, enable)\ + ecs_enable_id(world, entity, ecs_id(T), enable) + +#define ecs_is_enabled(world, entity, T)\ + ecs_is_enabled_id(world, entity, ecs_id(T)) + +#define ecs_enable_pair(world, entity, First, second, enable)\ + ecs_enable_id(world, entity, ecs_pair(ecs_id(First), second), enable) + +#define ecs_is_enabled_pair(world, entity, First, second)\ + ecs_is_enabled_id(world, entity, ecs_pair(ecs_id(First), second)) + +/** @} */ + +/** + * @defgroup flecs_c_entity_names Entity Names + * @{ + */ + +#define ecs_lookup_from(world, parent, path)\ + ecs_lookup_path_w_sep(world, parent, path, ".", NULL, true) + +#define ecs_get_path_from(world, parent, child)\ + ecs_get_path_w_sep(world, parent, child, ".", NULL) + +#define ecs_get_path(world, child)\ + ecs_get_path_w_sep(world, 0, child, ".", NULL) + +#define ecs_get_path_buf(world, child, buf)\ + ecs_get_path_w_sep_buf(world, 0, child, ".", NULL, buf, false) + +#define ecs_new_from_path(world, parent, path)\ + ecs_new_from_path_w_sep(world, parent, path, ".", NULL) + +#define ecs_add_path(world, entity, parent, path)\ + ecs_add_path_w_sep(world, entity, parent, path, ".", NULL) + +#define ecs_add_fullpath(world, entity, path)\ + ecs_add_path_w_sep(world, entity, 0, path, ".", NULL) + +/** @} */ + +/** @} */ + +/** + * @defgroup flecs_c_components Component API + * @{ + */ + +#define ecs_set_hooks(world, T, ...)\ + ecs_set_hooks_id(world, ecs_id(T), &(ecs_type_hooks_t)__VA_ARGS__) + +#define ecs_get_hooks(world, T)\ + ecs_get_hooks_id(world, ecs_id(T)); + +/** Declare a constructor. + * Example: + * + * @code + * ECS_CTOR(MyType, ptr, { ptr->value = NULL; }); + * @endcode + */ +#define ECS_CTOR(type, var, ...)\ + ECS_XTOR_IMPL(type, ctor, var, __VA_ARGS__) + +/** Declare a destructor. + * Example: + * + * @code + * ECS_DTOR(MyType, ptr, { free(ptr->value); }); + * @endcode + */ +#define ECS_DTOR(type, var, ...)\ + ECS_XTOR_IMPL(type, dtor, var, __VA_ARGS__) + +/** Declare a copy action. + * Example: + * + * @code + * ECS_COPY(MyType, dst, src, { dst->value = strdup(src->value); }); + * @endcode + */ +#define ECS_COPY(type, dst_var, src_var, ...)\ + ECS_COPY_IMPL(type, dst_var, src_var, __VA_ARGS__) + +/** Declare a move action. + * Example: + * + * @code + * ECS_MOVE(MyType, dst, src, { dst->value = src->value; src->value = 0; }); + * @endcode + */ +#define ECS_MOVE(type, dst_var, src_var, ...)\ + ECS_MOVE_IMPL(type, dst_var, src_var, __VA_ARGS__) + +/** Declare component hooks. + * Example: + * + * @code + * ECS_ON_SET(MyType, ptr, { printf("%d\n", ptr->value); }); + * @endcode + */ +#define ECS_ON_ADD(type, ptr, ...)\ + ECS_HOOK_IMPL(type, ecs_on_add(type), ptr, __VA_ARGS__) +#define ECS_ON_REMOVE(type, ptr, ...)\ + ECS_HOOK_IMPL(type, ecs_on_remove(type), ptr, __VA_ARGS__) +#define ECS_ON_SET(type, ptr, ...)\ + ECS_HOOK_IMPL(type, ecs_on_set(type), ptr, __VA_ARGS__) + +/* Map from typename to function name of component lifecycle action */ +#define ecs_ctor(type) type##_ctor +#define ecs_dtor(type) type##_dtor +#define ecs_copy(type) type##_copy +#define ecs_move(type) type##_move +#define ecs_on_set(type) type##_on_set +#define ecs_on_add(type) type##_on_add +#define ecs_on_remove(type) type##_on_remove + +/** @} */ + +/** + * @defgroup flecs_c_ids Id API + * @{ + */ + +#define ecs_count(world, type)\ + ecs_count_id(world, ecs_id(type)) + +/** @} */ + +/** + * @defgroup flecs_c_iterators Iterator API + * @{ + */ + +#define ecs_field(it, T, index)\ + (ECS_CAST(T*, ecs_field_w_size(it, sizeof(T), index))) + +#define ecs_field_self(it, T, index)\ + (ECS_CAST(T*, ecs_field_self_w_size(it, sizeof(T), index))) + +#define ecs_field_at(it, T, index, row)\ + (ECS_CAST(T*, ecs_field_at_w_size(it, sizeof(T), index, row))) + +/** @} */ + +/** + * @defgroup flecs_c_tables Table API + * @{ + */ + +#define ecs_table_get(world, table, T, offset)\ + (ECS_CAST(T*, ecs_table_get_id(world, table, ecs_id(T), offset))) + +#define ecs_table_get_pair(world, table, First, second, offset)\ + (ECS_CAST(First*, ecs_table_get_id(world, table, ecs_pair(ecs_id(First), second), offset))) + +#define ecs_table_get_pair_second(world, table, first, Second, offset)\ + (ECS_CAST(Second*, ecs_table_get_id(world, table, ecs_pair(first, ecs_id(Second)), offset))) + +/** @} */ + +/** + * @defgroup flecs_c_values Value API + * @{ + */ + +/** Convenience macro for creating compound literal id array */ +#define ecs_ids(...) (ecs_id_t[]){ __VA_ARGS__, 0 } + +/** Convenience macro for creating compound literal values array */ +#define ecs_values(...) (ecs_value_t[]){ __VA_ARGS__, {0, 0}} + +/** Convenience macro for creating compound literal value */ +#define ecs_value_ptr(T, ptr) ((ecs_value_t){ecs_id(T), ptr}) + +/** Convenience macro for creating compound literal pair value */ +#define ecs_value_pair(R, t, ...) ((ecs_value_t){ecs_pair_t(R, t), &(R)__VA_ARGS__}) + +/** Convenience macro for creating compound literal pair value */ +#define ecs_value_pair_2nd(r, T, ...) ((ecs_value_t){ecs_pair(r, ecs_id(T)), &(T)__VA_ARGS__}) + +/** Convenience macro for creating heap allocated value */ +#define ecs_value_new_t(world, T) ecs_value_new(world, ecs_id(T)) + +/** Convenience macro for creating compound literal value literal */ +#define ecs_value(T, ...) ((ecs_value_t){ecs_id(T), &(T)__VA_ARGS__}) + +/** @} */ + +/** @} */ + +/** + * @defgroup flecs_c_table_sorting Table sorting + * Convenience macro's for sorting tables. + * + * @{ + */ +#define ecs_sort_table(id) ecs_id(id##_sort_table) + +#define ecs_compare(id) ecs_id(id##_compare_fn) + +/* Declare efficient table sorting operation that uses provided compare function. + * For best results use LTO or make the function body visible in the same compilation unit. + * Variadic arguments are prepended before generated functions, use it to declare static + * or exported functions. + * Parameters of the comparison function: + * ecs_entity_t e1, const void* ptr1, + * ecs_entity_t e2, const void* ptr2 + * Parameters of the sort functions: + * ecs_world_t *world + * ecs_table_t *table + * ecs_entity_t *entities + * void *ptr + * int32_t elem_size + * int32_t lo + * int32_t hi + * ecs_order_by_action_t order_by - Pointer to the original comparison function. You are not supposed to use it. + * Example: + * + * @code + * int CompareMyType(ecs_entity_t e1, const void* ptr1, ecs_entity_t e2, const void* ptr2) { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; } + * ECS_SORT_TABLE_WITH_COMPARE(MyType, MyCustomCompare, CompareMyType) + * @endcode + */ +#define ECS_SORT_TABLE_WITH_COMPARE(id, op_name, compare_fn, ...) \ + static int32_t ECS_CONCAT(op_name, _partition)( \ + ecs_world_t *world, \ + ecs_table_t *table, \ + ecs_entity_t *entities, \ + void *ptr, \ + int32_t elem_size, \ + int32_t lo, \ + int32_t hi, \ + ecs_order_by_action_t order_by) \ + { \ + (void)(order_by); \ + int32_t p = (hi + lo) / 2; \ + void *pivot = ECS_ELEM(ptr, elem_size, p); \ + ecs_entity_t pivot_e = entities[p]; \ + int32_t i = lo - 1, j = hi + 1; \ + void *el; \ + repeat: \ + { \ + do { \ + i ++; \ + el = ECS_ELEM(ptr, elem_size, i); \ + } while ( compare_fn(entities[i], el, pivot_e, pivot) < 0); \ + do { \ + j --; \ + el = ECS_ELEM(ptr, elem_size, j); \ + } while ( compare_fn(entities[j], el, pivot_e, pivot) > 0); \ + if (i >= j) { \ + return j; \ + } \ + ecs_table_swap_rows(world, table, i, j); \ + if (p == i) { \ + pivot = ECS_ELEM(ptr, elem_size, j); \ + pivot_e = entities[j]; \ + } else if (p == j) { \ + pivot = ECS_ELEM(ptr, elem_size, i); \ + pivot_e = entities[i]; \ + } \ + goto repeat; \ + } \ + } \ + __VA_ARGS__ void op_name( \ + ecs_world_t *world, \ + ecs_table_t *table, \ + ecs_entity_t *entities, \ + void *ptr, \ + int32_t size, \ + int32_t lo, \ + int32_t hi, \ + ecs_order_by_action_t order_by) \ + { \ + if ((hi - lo) < 1) { \ + return; \ + } \ + int32_t p = ECS_CONCAT(op_name, _partition)(world, table, entities, ptr, size, lo, hi, order_by); \ + op_name(world, table, entities, ptr, size, lo, p, order_by); \ + op_name(world, table, entities, ptr, size, p + 1, hi, order_by); \ + } + +/* Declare efficient table sorting operation that uses default component comparison operator. + * For best results use LTO or make the comparison operator visible in the same compilation unit. + * Variadic arguments are prepended before generated functions, use it to declare static + * or exported functions. + * Example: + * + * @code + * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); + * ECS_SORT_TABLE(MyType) + * @endcode + */ +#define ECS_SORT_TABLE(id, ...) \ + ECS_SORT_TABLE_WITH_COMPARE(id, ecs_sort_table(id), ecs_compare(id), __VA_ARGS__) + +/* Declare component comparison operations. + * Parameters: + * ecs_entity_t e1, const void* ptr1, + * ecs_entity_t e2, const void* ptr2 + * Example: + * + * @code + * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); + * @endcode + */ +#define ECS_COMPARE(id, ...) \ + int ecs_compare(id)(ecs_entity_t e1, const void* ptr1, ecs_entity_t e2, const void* ptr2) { \ + __VA_ARGS__ \ + } + +/** @} */ + +/** + * @defgroup flecs_c_misc Misc + * Misc convenience macro's. + * + * @{ + */ + +#define ecs_isa(e) ecs_pair(EcsIsA, e) +#define ecs_childof(e) ecs_pair(EcsChildOf, e) +#define ecs_dependson(e) ecs_pair(EcsDependsOn, e) +#define ecs_with(e) ecs_pair(EcsWith, e) + +#define ecs_each(world, id) ecs_each_id(world, ecs_id(id)) +#define ecs_each_pair(world, r, t) ecs_each_id(world, ecs_pair(r, t)) +#define ecs_each_pair_t(world, R, t) ecs_each_id(world, ecs_pair(ecs_id(R), t)) + +/** @} */ + +/** @} */ + +#endif // FLECS_C_ + + +#ifdef __cplusplus +} +#endif + +/** + * @file addons.h + * @brief Include enabled addons. + * + * This file should only be included by the main flecs.h header. + */ + +#ifndef FLECS_ADDONS_H +#define FLECS_ADDONS_H + +/* Blacklist macros */ +#ifdef FLECS_NO_CPP +#undef FLECS_CPP +#endif +#ifdef FLECS_NO_MODULE +#undef FLECS_MODULE +#endif +#ifdef FLECS_NO_SCRIPT +#undef FLECS_SCRIPT +#endif +#ifdef FLECS_NO_STATS +#undef FLECS_STATS +#endif +#ifdef FLECS_NO_SYSTEM +#undef FLECS_SYSTEM +#endif +#ifdef FLECS_NO_ALERTS +#undef FLECS_ALERTS +#endif +#ifdef FLECS_NO_PIPELINE +#undef FLECS_PIPELINE +#endif +#ifdef FLECS_NO_TIMER +#undef FLECS_TIMER +#endif +#ifdef FLECS_NO_META +#undef FLECS_META +#endif +#ifdef FLECS_NO_UNITS +#undef FLECS_UNITS +#endif +#ifdef FLECS_NO_JSON +#undef FLECS_JSON +#endif +#ifdef FLECS_NO_DOC +#undef FLECS_DOC +#endif +#ifdef FLECS_NO_LOG +#undef FLECS_LOG +#endif +#ifdef FLECS_NO_APP +#undef FLECS_APP +#endif +#ifdef FLECS_NO_OS_API_IMPL +#undef FLECS_OS_API_IMPL +#endif +#ifdef FLECS_NO_HTTP +#undef FLECS_HTTP +#endif +#ifdef FLECS_NO_REST +#undef FLECS_REST +#endif +#ifdef FLECS_NO_JOURNAL +#undef FLECS_JOURNAL +#endif + +/* Always included, if disabled functions are replaced with dummy macros */ +/** + * @file addons/journal.h + * @brief Journaling addon that logs API functions. + * + * The journaling addon traces API calls. The trace is formatted as runnable + * C code, which allows for (partially) reproducing the behavior of an app + * with the journaling trace. + * + * The journaling addon is disabled by default. Enabling it can have a + * significant impact on performance. + */ + +#ifdef FLECS_JOURNAL + +#ifndef FLECS_LOG +#define FLECS_LOG +#endif + +#ifndef FLECS_JOURNAL_H +#define FLECS_JOURNAL_H + +/** + * @defgroup c_addons_journal Journal + * @ingroup c_addons + * Journaling addon (disabled by default). + * + * + * @{ + */ + +/* Trace when log level is at or higher than level */ +#define FLECS_JOURNAL_LOG_LEVEL (0) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Journaling API, meant to be used by internals. */ + +typedef enum ecs_journal_kind_t { + EcsJournalNew, + EcsJournalMove, + EcsJournalClear, + EcsJournalDelete, + EcsJournalDeleteWith, + EcsJournalRemoveAll, + EcsJournalTableEvents +} ecs_journal_kind_t; + +FLECS_DBG_API +void flecs_journal_begin( + ecs_world_t *world, + ecs_journal_kind_t kind, + ecs_entity_t entity, + ecs_type_t *add, + ecs_type_t *remove); + +FLECS_DBG_API +void flecs_journal_end(void); + +#define flecs_journal(...)\ + flecs_journal_begin(__VA_ARGS__);\ + flecs_journal_end(); + +#ifdef __cplusplus +} +#endif // __cplusplus +/** @} */ +#endif // FLECS_JOURNAL_H +#else +#define flecs_journal_begin(...) +#define flecs_journal_end(...) +#define flecs_journal(...) + +#endif // FLECS_JOURNAL + +/** + * @file addons/log.h + * @brief Logging addon. + * + * The logging addon provides an API for (debug) tracing and reporting errors + * at various levels. When enabled, the logging addon can provide more detailed + * information about the state of the ECS and any errors that may occur. + * + * The logging addon can be disabled to reduce footprint of the library, but + * limits information logged to only file, line and error code. + * + * When enabled the logging addon can be configured to exclude levels of tracing + * from the build to reduce the impact on performance. By default all debug + * tracing is enabled for debug builds, tracing is enabled at release builds. + * + * Applications can change the logging level at runtime with ecs_log_set_level(), + * but what is actually logged depends on what is compiled (when compiled + * without debug tracing, setting the runtime level to debug won't have an + * effect). + * + * The logging addon uses the OS API log_ function for all tracing. + * + * Note that even when the logging addon is not enabled, its header/source must + * be included in a build. To prevent unused variable warnings in the code, some + * API functions are included when the addon is disabled, but have empty bodies. + */ + +#ifndef FLECS_LOG_H +#define FLECS_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef FLECS_LOG + +/** + * @defgroup c_addons_log Log + * @ingroup c_addons + * Logging functions. + * + * @{ + */ + +//////////////////////////////////////////////////////////////////////////////// +//// Tracing +//////////////////////////////////////////////////////////////////////////////// + +/** Log message indicating an operation is deprecated. */ +FLECS_API +void ecs_deprecated_( + const char *file, + int32_t line, + const char *msg); + +/** Increase log stack. + * This operation increases the indent_ value of the OS API and can be useful to + * make nested behavior more visible. + * + * @param level The log level. + */ +FLECS_API +void ecs_log_push_(int32_t level); + +/** Decrease log stack. + * This operation decreases the indent_ value of the OS API and can be useful to + * make nested behavior more visible. + * + * @param level The log level. + */ +FLECS_API +void ecs_log_pop_(int32_t level); + +/** Should current level be logged. + * This operation returns true when the specified log level should be logged + * with the current log level. + * + * @param level The log level to check for. + * @return Whether logging is enabled for the current level. + */ +FLECS_API +bool ecs_should_log(int32_t level); + +//////////////////////////////////////////////////////////////////////////////// +//// Error reporting +//////////////////////////////////////////////////////////////////////////////// + +/** Get description for error code */ +FLECS_API +const char* ecs_strerror( + int32_t error_code); + +#else // FLECS_LOG + +//////////////////////////////////////////////////////////////////////////////// +//// Dummy macros for when logging is disabled +//////////////////////////////////////////////////////////////////////////////// + +#define ecs_deprecated_(file, line, msg)\ + (void)file;\ + (void)line;\ + (void)msg + +#define ecs_log_push_(level) +#define ecs_log_pop_(level) +#define ecs_should_log(level) false + +#define ecs_strerror(error_code)\ + (void)error_code + +#endif // FLECS_LOG + + +//////////////////////////////////////////////////////////////////////////////// +//// Logging functions (do nothing when logging is enabled) +//////////////////////////////////////////////////////////////////////////////// + +FLECS_API +void ecs_print_( + int32_t level, + const char *file, + int32_t line, + const char *fmt, + ...); + +FLECS_API +void ecs_printv_( + int level, + const char *file, + int32_t line, + const char *fmt, + va_list args); + +FLECS_API +void ecs_log_( + int32_t level, + const char *file, + int32_t line, + const char *fmt, + ...); + +FLECS_API +void ecs_logv_( + int level, + const char *file, + int32_t line, + const char *fmt, + va_list args); + +FLECS_API +void ecs_abort_( + int32_t error_code, + const char *file, + int32_t line, + const char *fmt, + ...); + +FLECS_API +void ecs_assert_log_( + int32_t error_code, + const char *condition_str, + const char *file, + int32_t line, + const char *fmt, + ...); + +FLECS_API +void ecs_parser_error_( + const char *name, + const char *expr, + int64_t column, + const char *fmt, + ...); + +FLECS_API +void ecs_parser_errorv_( + const char *name, + const char *expr, + int64_t column, + const char *fmt, + va_list args); + + +//////////////////////////////////////////////////////////////////////////////// +//// Logging macros +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FLECS_LEGACY /* C89 doesn't support variadic macros */ + +/* Base logging function. Accepts a custom level */ +#define ecs_print(level, ...)\ + ecs_print_(level, __FILE__, __LINE__, __VA_ARGS__) + +#define ecs_printv(level, fmt, args)\ + ecs_printv_(level, __FILE__, __LINE__, fmt, args) + +#define ecs_log(level, ...)\ + ecs_log_(level, __FILE__, __LINE__, __VA_ARGS__) + +#define ecs_logv(level, fmt, args)\ + ecs_logv_(level, __FILE__, __LINE__, fmt, args) + +/* Tracing. Used for logging of infrequent events */ +#define ecs_trace_(file, line, ...) ecs_log_(0, file, line, __VA_ARGS__) +#define ecs_trace(...) ecs_trace_(__FILE__, __LINE__, __VA_ARGS__) + +/* Warning. Used when an issue occurs, but operation is successful */ +#define ecs_warn_(file, line, ...) ecs_log_(-2, file, line, __VA_ARGS__) +#define ecs_warn(...) ecs_warn_(__FILE__, __LINE__, __VA_ARGS__) + +/* Error. Used when an issue occurs, and operation failed. */ +#define ecs_err_(file, line, ...) ecs_log_(-3, file, line, __VA_ARGS__) +#define ecs_err(...) ecs_err_(__FILE__, __LINE__, __VA_ARGS__) + +/* Fatal. Used when an issue occurs, and the application cannot continue. */ +#define ecs_fatal_(file, line, ...) ecs_log_(-4, file, line, __VA_ARGS__) +#define ecs_fatal(...) ecs_fatal_(__FILE__, __LINE__, __VA_ARGS__) + +/* Optionally include warnings about using deprecated features */ +#ifndef FLECS_NO_DEPRECATED_WARNINGS +#define ecs_deprecated(...)\ + ecs_deprecated_(__FILE__, __LINE__, __VA_ARGS__) +#else +#define ecs_deprecated(...) +#endif // FLECS_NO_DEPRECATED_WARNINGS + +/* If no tracing verbosity is defined, pick default based on build config */ +#if !(defined(FLECS_LOG_0) || defined(FLECS_LOG_1) || defined(FLECS_LOG_2) || defined(FLECS_LOG_3)) +#if !defined(FLECS_NDEBUG) +#define FLECS_LOG_3 /* Enable all tracing in debug mode. May slow things down */ +#else +#define FLECS_LOG_0 /* Only enable infrequent tracing in release mode */ +#endif // !defined(FLECS_NDEBUG) +#endif // !(defined(FLECS_LOG_0) || defined(FLECS_LOG_1) || defined(FLECS_LOG_2) || defined(FLECS_LOG_3)) + + +/* Define/undefine macros based on compiled-in tracing level. This can optimize + * out tracing statements from a build, which improves performance. */ + +#if defined(FLECS_LOG_3) /* All debug tracing enabled */ +#define ecs_dbg_1(...) ecs_log(1, __VA_ARGS__); +#define ecs_dbg_2(...) ecs_log(2, __VA_ARGS__); +#define ecs_dbg_3(...) ecs_log(3, __VA_ARGS__); + +#define ecs_log_push_1() ecs_log_push_(1); +#define ecs_log_push_2() ecs_log_push_(2); +#define ecs_log_push_3() ecs_log_push_(3); + +#define ecs_log_pop_1() ecs_log_pop_(1); +#define ecs_log_pop_2() ecs_log_pop_(2); +#define ecs_log_pop_3() ecs_log_pop_(3); + +#define ecs_should_log_1() ecs_should_log(1) +#define ecs_should_log_2() ecs_should_log(2) +#define ecs_should_log_3() ecs_should_log(3) + +#define FLECS_LOG_2 +#define FLECS_LOG_1 +#define FLECS_LOG_0 + +#elif defined(FLECS_LOG_2) /* Level 2 and below debug tracing enabled */ +#define ecs_dbg_1(...) ecs_log(1, __VA_ARGS__); +#define ecs_dbg_2(...) ecs_log(2, __VA_ARGS__); +#define ecs_dbg_3(...) + +#define ecs_log_push_1() ecs_log_push_(1); +#define ecs_log_push_2() ecs_log_push_(2); +#define ecs_log_push_3() + +#define ecs_log_pop_1() ecs_log_pop_(1); +#define ecs_log_pop_2() ecs_log_pop_(2); +#define ecs_log_pop_3() + +#define ecs_should_log_1() ecs_should_log(1) +#define ecs_should_log_2() ecs_should_log(2) +#define ecs_should_log_3() false + +#define FLECS_LOG_1 +#define FLECS_LOG_0 + +#elif defined(FLECS_LOG_1) /* Level 1 debug tracing enabled */ +#define ecs_dbg_1(...) ecs_log(1, __VA_ARGS__); +#define ecs_dbg_2(...) +#define ecs_dbg_3(...) + +#define ecs_log_push_1() ecs_log_push_(1); +#define ecs_log_push_2() +#define ecs_log_push_3() + +#define ecs_log_pop_1() ecs_log_pop_(1); +#define ecs_log_pop_2() +#define ecs_log_pop_3() + +#define ecs_should_log_1() ecs_should_log(1) +#define ecs_should_log_2() false +#define ecs_should_log_3() false + +#define FLECS_LOG_0 + +#elif defined(FLECS_LOG_0) /* No debug tracing enabled */ +#define ecs_dbg_1(...) +#define ecs_dbg_2(...) +#define ecs_dbg_3(...) + +#define ecs_log_push_1() +#define ecs_log_push_2() +#define ecs_log_push_3() + +#define ecs_log_pop_1() +#define ecs_log_pop_2() +#define ecs_log_pop_3() + +#define ecs_should_log_1() false +#define ecs_should_log_2() false +#define ecs_should_log_3() false + +#else /* No tracing enabled */ +#undef ecs_trace +#define ecs_trace(...) +#define ecs_dbg_1(...) +#define ecs_dbg_2(...) +#define ecs_dbg_3(...) + +#define ecs_log_push_1() +#define ecs_log_push_2() +#define ecs_log_push_3() + +#define ecs_log_pop_1() +#define ecs_log_pop_2() +#define ecs_log_pop_3() + +#endif // defined(FLECS_LOG_3) + +/* Default debug tracing is at level 1 */ +#define ecs_dbg ecs_dbg_1 + +/* Default level for push/pop is 0 */ +#define ecs_log_push() ecs_log_push_(0) +#define ecs_log_pop() ecs_log_pop_(0) + +/** Abort. + * Unconditionally aborts process. */ +#define ecs_abort(error_code, ...)\ + ecs_abort_(error_code, __FILE__, __LINE__, __VA_ARGS__);\ + ecs_os_abort(); abort(); /* satisfy compiler/static analyzers */ + +/** Assert. + * Aborts if condition is false, disabled in debug mode. */ +#if defined(FLECS_NDEBUG) && !defined(FLECS_KEEP_ASSERT) +#define ecs_assert(condition, error_code, ...) +#else +#define ecs_assert(condition, error_code, ...)\ + if (!(condition)) {\ + ecs_assert_log_(error_code, #condition, __FILE__, __LINE__, __VA_ARGS__);\ + ecs_os_abort();\ + }\ + assert(condition) /* satisfy compiler/static analyzers */ +#endif // FLECS_NDEBUG + +#define ecs_assert_var(var, error_code, ...)\ + ecs_assert(var, error_code, __VA_ARGS__);\ + (void)var + +/** Debug assert. + * Assert that is only valid in debug mode (ignores FLECS_KEEP_ASSERT) */ +#ifndef FLECS_NDEBUG +#define ecs_dbg_assert(condition, error_code, ...) ecs_assert(condition, error_code, __VA_ARGS__) +#else +#define ecs_dbg_assert(condition, error_code, ...) +#endif + +/** Sanitize assert. + * Assert that is only valid in sanitized mode (ignores FLECS_KEEP_ASSERT) */ +#ifdef FLECS_SANITIZE +#define ecs_san_assert(condition, error_code, ...) ecs_assert(condition, error_code, __VA_ARGS__) +#else +#define ecs_san_assert(condition, error_code, ...) +#endif + + +/* Silence dead code/unused label warnings when compiling without checks. */ +#define ecs_dummy_check\ + if ((false)) {\ + goto error;\ + } + +/** Check. + * goto error if condition is false. */ +#if defined(FLECS_NDEBUG) && !defined(FLECS_KEEP_ASSERT) +#define ecs_check(condition, error_code, ...) ecs_dummy_check +#else +#ifdef FLECS_SOFT_ASSERT +#define ecs_check(condition, error_code, ...)\ + if (!(condition)) {\ + ecs_assert_log_(error_code, #condition, __FILE__, __LINE__, __VA_ARGS__);\ + goto error;\ + } +#else // FLECS_SOFT_ASSERT +#define ecs_check(condition, error_code, ...)\ + ecs_assert(condition, error_code, __VA_ARGS__);\ + ecs_dummy_check +#endif +#endif // FLECS_NDEBUG + +/** Panic. + * goto error when FLECS_SOFT_ASSERT is defined, otherwise abort */ +#if defined(FLECS_NDEBUG) && !defined(FLECS_KEEP_ASSERT) +#define ecs_throw(error_code, ...) ecs_dummy_check +#else +#ifdef FLECS_SOFT_ASSERT +#define ecs_throw(error_code, ...)\ + ecs_abort_(error_code, __FILE__, __LINE__, __VA_ARGS__);\ + goto error; +#else +#define ecs_throw(error_code, ...)\ + ecs_abort(error_code, __VA_ARGS__);\ + ecs_dummy_check +#endif +#endif // FLECS_NDEBUG + +/** Parser error */ +#define ecs_parser_error(name, expr, column, ...)\ + ecs_parser_error_(name, expr, column, __VA_ARGS__) + +#define ecs_parser_errorv(name, expr, column, fmt, args)\ + ecs_parser_errorv_(name, expr, column, fmt, args) + +#endif // FLECS_LEGACY + + +//////////////////////////////////////////////////////////////////////////////// +//// Functions that are always available +//////////////////////////////////////////////////////////////////////////////// + +/** Enable or disable log. + * This will enable builtin log. For log to work, it will have to be + * compiled in which requires defining one of the following macros: + * + * FLECS_LOG_0 - All log is disabled + * FLECS_LOG_1 - Enable log level 1 + * FLECS_LOG_2 - Enable log level 2 and below + * FLECS_LOG_3 - Enable log level 3 and below + * + * If no log level is defined and this is a debug build, FLECS_LOG_3 will + * have been automatically defined. + * + * The provided level corresponds with the log level. If -1 is provided as + * value, warnings are disabled. If -2 is provided, errors are disabled as well. + * + * @param level Desired tracing level. + * @return Previous log level. + */ +FLECS_API +int ecs_log_set_level( + int level); + +/** Get current log level. + * + * @return Previous log level. + */ +FLECS_API +int ecs_log_get_level(void); + +/** Enable/disable tracing with colors. + * By default colors are enabled. + * + * @param enabled Whether to enable tracing with colors. + * @return Previous color setting. + */ +FLECS_API +bool ecs_log_enable_colors( + bool enabled); + +/** Enable/disable logging timestamp. + * By default timestamps are disabled. Note that enabling timestamps introduces + * overhead as the logging code will need to obtain the current time. + * + * @param enabled Whether to enable tracing with timestamps. + * @return Previous timestamp setting. + */ +FLECS_API +bool ecs_log_enable_timestamp( + bool enabled); + +/** Enable/disable logging time since last log. + * By default deltatime is disabled. Note that enabling timestamps introduces + * overhead as the logging code will need to obtain the current time. + * + * When enabled, this logs the amount of time in seconds passed since the last + * log, when this amount is non-zero. The format is a '+' character followed by + * the number of seconds: + * + * +1 trace: log message + * + * @param enabled Whether to enable tracing with timestamps. + * @return Previous timestamp setting. + */ +FLECS_API +bool ecs_log_enable_timedelta( + bool enabled); + +/** Get last logged error code. + * Calling this operation resets the error code. + * + * @return Last error, 0 if none was logged since last call to last_error. + */ +FLECS_API +int ecs_log_last_error(void); + + +//////////////////////////////////////////////////////////////////////////////// +//// Error codes +//////////////////////////////////////////////////////////////////////////////// + +#define ECS_INVALID_OPERATION (1) +#define ECS_INVALID_PARAMETER (2) +#define ECS_CONSTRAINT_VIOLATED (3) +#define ECS_OUT_OF_MEMORY (4) +#define ECS_OUT_OF_RANGE (5) +#define ECS_UNSUPPORTED (6) +#define ECS_INTERNAL_ERROR (7) +#define ECS_ALREADY_DEFINED (8) +#define ECS_MISSING_OS_API (9) +#define ECS_OPERATION_FAILED (10) +#define ECS_INVALID_CONVERSION (11) +#define ECS_ID_IN_USE (12) +#define ECS_CYCLE_DETECTED (13) +#define ECS_LEAK_DETECTED (14) +#define ECS_DOUBLE_FREE (15) + +#define ECS_INCONSISTENT_NAME (20) +#define ECS_NAME_IN_USE (21) +#define ECS_NOT_A_COMPONENT (22) +#define ECS_INVALID_COMPONENT_SIZE (23) +#define ECS_INVALID_COMPONENT_ALIGNMENT (24) +#define ECS_COMPONENT_NOT_REGISTERED (25) +#define ECS_INCONSISTENT_COMPONENT_ID (26) +#define ECS_INCONSISTENT_COMPONENT_ACTION (27) +#define ECS_MODULE_UNDEFINED (28) +#define ECS_MISSING_SYMBOL (29) +#define ECS_ALREADY_IN_USE (30) + +#define ECS_ACCESS_VIOLATION (40) +#define ECS_COLUMN_INDEX_OUT_OF_RANGE (41) +#define ECS_COLUMN_IS_NOT_SHARED (42) +#define ECS_COLUMN_IS_SHARED (43) +#define ECS_COLUMN_TYPE_MISMATCH (45) + +#define ECS_INVALID_WHILE_READONLY (70) +#define ECS_LOCKED_STORAGE (71) +#define ECS_INVALID_FROM_WORKER (72) + + +//////////////////////////////////////////////////////////////////////////////// +//// Used when logging with colors is enabled +//////////////////////////////////////////////////////////////////////////////// + +#define ECS_BLACK "\033[1;30m" +#define ECS_RED "\033[0;31m" +#define ECS_GREEN "\033[0;32m" +#define ECS_YELLOW "\033[0;33m" +#define ECS_BLUE "\033[0;34m" +#define ECS_MAGENTA "\033[0;35m" +#define ECS_CYAN "\033[0;36m" +#define ECS_WHITE "\033[1;37m" +#define ECS_GREY "\033[0;37m" +#define ECS_NORMAL "\033[0;49m" +#define ECS_BOLD "\033[1;49m" + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif // FLECS_LOG_H + + +/* Handle addon dependencies that need declarations to be visible in header */ +#ifdef FLECS_STATS +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE +#endif +#ifndef FLECS_TIMER +#define FLECS_TIMER +#endif +#endif + +#ifdef FLECS_REST +#ifndef FLECS_HTTP +#define FLECS_HTTP +#endif +#endif + +#ifdef FLECS_APP +#ifdef FLECS_NO_APP +#error "FLECS_NO_APP failed: APP is required by other addons" +#endif +/** + * @file addons/app.h + * @brief App addon. + * + * The app addon is a wrapper around the application's main loop. Its main + * purpose is to provide a hook to modules that need to take control of the + * main loop, as is for example the case with native applications that use + * emscripten with webGL. + */ + +#ifdef FLECS_APP + +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE +#endif + +#ifndef FLECS_APP_H +#define FLECS_APP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup c_addons_app App + * @ingroup c_addons + * Optional addon for running the main application loop. + * + * @{ + */ + +/** Callback type for init action. */ +typedef int(*ecs_app_init_action_t)( + ecs_world_t *world); + +/** Used with ecs_app_run(). */ +typedef struct ecs_app_desc_t { + ecs_ftime_t target_fps; /**< Target FPS. */ + ecs_ftime_t delta_time; /**< Frame time increment (0 for measured values) */ + int32_t threads; /**< Number of threads. */ + int32_t frames; /**< Number of frames to run (0 for infinite) */ + bool enable_rest; /**< Enables ECS access over HTTP, necessary for explorer */ + bool enable_stats; /**< Periodically collect statistics */ + uint16_t port; /**< HTTP port used by REST API */ + + ecs_app_init_action_t init; /**< If set, function is ran before starting the + * main loop. */ + + void *ctx; /**< Reserved for custom run/frame actions */ +} ecs_app_desc_t; + +/** Callback type for run action. */ +typedef int(*ecs_app_run_action_t)( + ecs_world_t *world, + ecs_app_desc_t *desc); + +/** Callback type for frame action. */ +typedef int(*ecs_app_frame_action_t)( + ecs_world_t *world, + const ecs_app_desc_t *desc); + +/** Run application. + * This will run the application with the parameters specified in desc. After + * the application quits (ecs_quit() is called) the world will be cleaned up. + * + * If a custom run action is set, it will be invoked by this operation. The + * default run action calls the frame action in a loop until it returns a + * non-zero value. + * + * @param world The world. + * @param desc Application parameters. + */ +FLECS_API +int ecs_app_run( + ecs_world_t *world, + ecs_app_desc_t *desc); + +/** Default frame callback. + * This operation will run a single frame. By default this operation will invoke + * ecs_progress() directly, unless a custom frame action is set. + * + * @param world The world. + * @param desc The desc struct passed to ecs_app_run(). + * @return value returned by ecs_progress() + */ +FLECS_API +int ecs_app_run_frame( + ecs_world_t *world, + const ecs_app_desc_t *desc); + +/** Set custom run action. + * See ecs_app_run(). + * + * @param callback The run action. + */ +FLECS_API +int ecs_app_set_run_action( + ecs_app_run_action_t callback); + +/** Set custom frame action. + * See ecs_app_run_frame(). + * + * @param callback The frame action. + */ +FLECS_API +int ecs_app_set_frame_action( + ecs_app_frame_action_t callback); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif + +#endif // FLECS_APP + +#endif + +#ifdef FLECS_HTTP +#ifdef FLECS_NO_HTTP +#error "FLECS_NO_HTTP failed: HTTP is required by other addons" +#endif +/** + * @file addons/http.h + * @brief HTTP addon. + * + * Minimalistic HTTP server that can receive and reply to simple HTTP requests. + * The main goal of this addon is to enable remotely connecting to a running + * Flecs application (for example, with a web-based UI) and request/visualize + * data from the ECS world. + * + * Each server instance creates a single thread used for receiving requests. + * Receiving requests are enqueued and handled when the application calls + * ecs_http_server_dequeue(). This increases latency of request handling vs. + * responding directly in the receive thread, but is better suited for + * retrieving data from ECS applications, as requests can be processed by an ECS + * system without having to lock the world. + * + * This server is intended to be used in a development environment. + */ + +#ifdef FLECS_HTTP + +/** + * @defgroup c_addons_http Http + * @ingroup c_addons + * Simple HTTP server used for serving up REST API. + * + * @{ + */ + +#if !defined(FLECS_OS_API_IMPL) && !defined(FLECS_NO_OS_API_IMPL) +#define FLECS_OS_API_IMPL +#endif + +#ifndef FLECS_HTTP_H +#define FLECS_HTTP_H + +/** Maximum number of headers in request. */ +#define ECS_HTTP_HEADER_COUNT_MAX (32) + +/** Maximum number of query parameters in request. */ +#define ECS_HTTP_QUERY_PARAM_COUNT_MAX (32) + +#ifdef __cplusplus +extern "C" { +#endif + +/** HTTP server. */ +typedef struct ecs_http_server_t ecs_http_server_t; + +/** A connection manages communication with the remote host. */ +typedef struct { + uint64_t id; + ecs_http_server_t *server; + + char host[128]; + char port[16]; +} ecs_http_connection_t; + +/** Helper type used for headers & URL query parameters. */ +typedef struct { + const char *key; + const char *value; +} ecs_http_key_value_t; + +/** Supported request methods. */ +typedef enum { + EcsHttpGet, + EcsHttpPost, + EcsHttpPut, + EcsHttpDelete, + EcsHttpOptions, + EcsHttpMethodUnsupported +} ecs_http_method_t; + +/** An HTTP request. */ +typedef struct { + uint64_t id; + + ecs_http_method_t method; + char *path; + char *body; + ecs_http_key_value_t headers[ECS_HTTP_HEADER_COUNT_MAX]; + ecs_http_key_value_t params[ECS_HTTP_HEADER_COUNT_MAX]; + int32_t header_count; + int32_t param_count; + + ecs_http_connection_t *conn; +} ecs_http_request_t; + +/** An HTTP reply. */ +typedef struct { + int code; /**< default = 200 */ + ecs_strbuf_t body; /**< default = "" */ + const char* status; /**< default = OK */ + const char* content_type; /**< default = application/json */ + ecs_strbuf_t headers; /**< default = "" */ +} ecs_http_reply_t; + +#define ECS_HTTP_REPLY_INIT \ + (ecs_http_reply_t){200, ECS_STRBUF_INIT, "OK", "application/json", ECS_STRBUF_INIT} + +/* Global HTTP statistics. */ +extern int64_t ecs_http_request_received_count; /**< Total number of HTTP requests received. */ +extern int64_t ecs_http_request_invalid_count; /**< Total number of invalid HTTP requests. */ +extern int64_t ecs_http_request_handled_ok_count; /**< Total number of successful HTTP requests. */ +extern int64_t ecs_http_request_handled_error_count; /**< Total number of HTTP requests with errors. */ +extern int64_t ecs_http_request_not_handled_count; /**< Total number of HTTP requests with an unknown endpoint. */ +extern int64_t ecs_http_request_preflight_count; /**< Total number of preflight HTTP requests received. */ +extern int64_t ecs_http_send_ok_count; /**< Total number of HTTP replies successfully sent. */ +extern int64_t ecs_http_send_error_count; /**< Total number of HTTP replies that failed to send. */ +extern int64_t ecs_http_busy_count; /**< Total number of HTTP busy replies. */ + +/** Request callback. + * Invoked for each valid request. The function should populate the reply and + * return true. When the function returns false, the server will reply with a + * 404 (Not found) code. */ +typedef bool (*ecs_http_reply_action_t)( + const ecs_http_request_t* request, + ecs_http_reply_t *reply, + void *ctx); + +/** Used with ecs_http_server_init(). */ +typedef struct { + ecs_http_reply_action_t callback; /**< Function called for each request */ + void *ctx; /**< Passed to callback (optional) */ + uint16_t port; /**< HTTP port */ + const char *ipaddr; /**< Interface to listen on (optional) */ + int32_t send_queue_wait_ms; /**< Send queue wait time when empty */ + double cache_timeout; /**< Cache invalidation timeout (0 disables caching) */ + double cache_purge_timeout; /**< Cache purge timeout (for purging cache entries) */ +} ecs_http_server_desc_t; + +/** Create server. + * Use ecs_http_server_start() to start receiving requests. + * + * @param desc Server configuration parameters. + * @return The new server, or NULL if creation failed. + */ +FLECS_API +ecs_http_server_t* ecs_http_server_init( + const ecs_http_server_desc_t *desc); + +/** Destroy server. + * This operation will stop the server if it was still running. + * + * @param server The server to destroy. + */ +FLECS_API +void ecs_http_server_fini( + ecs_http_server_t* server); + +/** Start server. + * After this operation the server will be able to accept requests. + * + * @param server The server to start. + * @return Zero if successful, non-zero if failed. + */ +FLECS_API +int ecs_http_server_start( + ecs_http_server_t* server); + +/** Process server requests. + * This operation invokes the reply callback for each received request. No new + * requests will be enqueued while processing requests. + * + * @param server The server for which to process requests. + */ +FLECS_API +void ecs_http_server_dequeue( + ecs_http_server_t* server, + ecs_ftime_t delta_time); + +/** Stop server. + * After this operation no new requests can be received. + * + * @param server The server. + */ +FLECS_API +void ecs_http_server_stop( + ecs_http_server_t* server); + +/** Emulate a request. + * The request string must be a valid HTTP request. A minimal example: + * + * GET /entity/flecs/core/World?label=true HTTP/1.1 + * + * @param srv The server. + * @param req The request. + * @param len The length of the request (optional). + * @return The reply. + */ +FLECS_API +int ecs_http_server_http_request( + ecs_http_server_t* srv, + const char *req, + ecs_size_t len, + ecs_http_reply_t *reply_out); + +/** Convenience wrapper around ecs_http_server_http_request(). */ +FLECS_API +int ecs_http_server_request( + ecs_http_server_t* srv, + const char *method, + const char *req, + ecs_http_reply_t *reply_out); + +/** Get context provided in ecs_http_server_desc_t */ +FLECS_API +void* ecs_http_server_ctx( + ecs_http_server_t* srv); + +/** Find header in request. + * + * @param req The request. + * @param name name of the header to find + * @return The header value, or NULL if not found. +*/ +FLECS_API +const char* ecs_http_get_header( + const ecs_http_request_t* req, + const char* name); + +/** Find query parameter in request. + * + * @param req The request. + * @param name The parameter name. + * @return The decoded parameter value, or NULL if not found. + */ +FLECS_API +const char* ecs_http_get_param( + const ecs_http_request_t* req, + const char* name); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif // FLECS_HTTP_H + +#endif // FLECS_HTTP + +#endif + +#ifdef FLECS_REST +#ifdef FLECS_NO_REST +#error "FLECS_NO_REST failed: REST is required by other addons" +#endif +/** + * @file addons/rest.h + * @brief REST API addon. + * + * A small REST API that uses the HTTP server and JSON serializer to provide + * access to application data for remote applications. + * + * A description of the API can be found in docs/FlecsRemoteApi.md + */ + +#ifdef FLECS_REST + +/** + * @defgroup c_addons_rest Rest + * @ingroup c_addons + * REST API for querying and mutating entities. + * + * @{ + */ + +/* Used for the HTTP server */ +#ifndef FLECS_HTTP +#define FLECS_HTTP +#endif + +/* Used for building the JSON replies */ +#ifndef FLECS_JSON +#define FLECS_JSON +#endif + +/* For the REST system */ +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE +#endif + +#ifndef FLECS_REST_H +#define FLECS_REST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define ECS_REST_DEFAULT_PORT (27750) + +/** Component that instantiates the REST API. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsRest); + +/** Component that creates a REST API server when instantiated. */ +typedef struct { + uint16_t port; /**< Port of server (optional, default = 27750) */ + char *ipaddr; /**< Interface address (optional, default = 0.0.0.0) */ + void *impl; +} EcsRest; + +/** Create HTTP server for REST API. + * This allows for the creation of a REST server that can be managed by the + * application without using Flecs systems. + * + * @param world The world. + * @param desc The HTTP server descriptor. + * @return The HTTP server, or NULL if failed. + */ +FLECS_API +ecs_http_server_t* ecs_rest_server_init( + ecs_world_t *world, + const ecs_http_server_desc_t *desc); + +/** Cleanup REST HTTP server. + * The server must have been created with ecs_rest_server_init(). + */ +FLECS_API +void ecs_rest_server_fini( + ecs_http_server_t *srv); + +/** Rest module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsRest) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsRestImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_TIMER +#ifdef FLECS_NO_TIMER +#error "FLECS_NO_TIMER failed: TIMER is required by other addons" +#endif +/** + * @file addons/timer.h + * @brief Timer module. + * + * Timers can be used to trigger actions at periodic or one-shot intervals. They + * are typically used together with systems and pipelines. + */ + +#ifdef FLECS_TIMER + +/** + * @defgroup c_addons_timer Timer + * @ingroup c_addons + * Run systems at a time interval. + * + * @{ + */ + +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif + +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE +#endif + +#ifndef FLECS_TIMER_H +#define FLECS_TIMER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Component used for one shot/interval timer functionality */ +typedef struct EcsTimer { + ecs_ftime_t timeout; /**< Timer timeout period */ + ecs_ftime_t time; /**< Incrementing time value */ + ecs_ftime_t overshoot; /**< Used to correct returned interval time */ + int32_t fired_count; /**< Number of times ticked */ + bool active; /**< Is the timer active or not */ + bool single_shot; /**< Is this a single shot timer */ +} EcsTimer; + +/** Apply a rate filter to a tick source */ +typedef struct EcsRateFilter { + ecs_entity_t src; /**< Source of the rate filter */ + int32_t rate; /**< Rate of the rate filter */ + int32_t tick_count; /**< Number of times the rate filter ticked */ + ecs_ftime_t time_elapsed; /**< Time elapsed since last tick */ +} EcsRateFilter; + + +/** Set timer timeout. + * This operation executes any systems associated with the timer after the + * specified timeout value. If the entity contains an existing timer, the + * timeout value will be reset. The timer can be started and stopped with + * ecs_start_timer() and ecs_stop_timer(). + * + * The timer is synchronous, and is incremented each frame by delta_time. + * + * The tick_source entity will be a tick source after this operation. Tick + * sources can be read by getting the EcsTickSource component. If the tick + * source ticked this frame, the 'tick' member will be true. When the tick + * source is a system, the system will tick when the timer ticks. + * + * @param world The world. + * @param tick_source The timer for which to set the timeout (0 to create one). + * @param timeout The timeout value. + * @return The timer entity. + */ +FLECS_API +ecs_entity_t ecs_set_timeout( + ecs_world_t *world, + ecs_entity_t tick_source, + ecs_ftime_t timeout); + +/** Get current timeout value for the specified timer. + * This operation returns the value set by ecs_set_timeout(). If no timer is + * active for this entity, the operation returns 0. + * + * After the timeout expires the EcsTimer component is removed from the entity. + * This means that if ecs_get_timeout() is invoked after the timer is expired, the + * operation will return 0. + * + * The timer is synchronous, and is incremented each frame by delta_time. + * + * The tick_source entity will be a tick source after this operation. Tick + * sources can be read by getting the EcsTickSource component. If the tick + * source ticked this frame, the 'tick' member will be true. When the tick + * source is a system, the system will tick when the timer ticks. + * + * @param world The world. + * @param tick_source The timer. + * @return The current timeout value, or 0 if no timer is active. + */ +FLECS_API +ecs_ftime_t ecs_get_timeout( + const ecs_world_t *world, + ecs_entity_t tick_source); + +/** Set timer interval. + * This operation will continuously invoke systems associated with the timer + * after the interval period expires. If the entity contains an existing timer, + * the interval value will be reset. + * + * The timer is synchronous, and is incremented each frame by delta_time. + * + * The tick_source entity will be a tick source after this operation. Tick + * sources can be read by getting the EcsTickSource component. If the tick + * source ticked this frame, the 'tick' member will be true. When the tick + * source is a system, the system will tick when the timer ticks. + * + * @param world The world. + * @param tick_source The timer for which to set the interval (0 to create one). + * @param interval The interval value. + * @return The timer entity. + */ +FLECS_API +ecs_entity_t ecs_set_interval( + ecs_world_t *world, + ecs_entity_t tick_source, + ecs_ftime_t interval); + +/** Get current interval value for the specified timer. + * This operation returns the value set by ecs_set_interval(). If the entity is + * not a timer, the operation will return 0. + * + * @param world The world. + * @param tick_source The timer for which to set the interval. + * @return The current interval value, or 0 if no timer is active. + */ +FLECS_API +ecs_ftime_t ecs_get_interval( + const ecs_world_t *world, + ecs_entity_t tick_source); + +/** Start timer. + * This operation resets the timer and starts it with the specified timeout. + * + * @param world The world. + * @param tick_source The timer to start. + */ +FLECS_API +void ecs_start_timer( + ecs_world_t *world, + ecs_entity_t tick_source); + +/** Stop timer + * This operation stops a timer from triggering. + * + * @param world The world. + * @param tick_source The timer to stop. + */ +FLECS_API +void ecs_stop_timer( + ecs_world_t *world, + ecs_entity_t tick_source); + +/** Reset time value of timer to 0. + * This operation resets the timer value to 0. + * + * @param world The world. + * @param tick_source The timer to reset. + */ +FLECS_API +void ecs_reset_timer( + ecs_world_t *world, + ecs_entity_t tick_source); + +/** Enable randomizing initial time value of timers. + * Initializes timers with a random time value, which can improve scheduling as + * systems/timers for the same interval don't all happen on the same tick. + * + * @param world The world. + */ +FLECS_API +void ecs_randomize_timers( + ecs_world_t *world); + +/** Set rate filter. + * This operation initializes a rate filter. Rate filters sample tick sources + * and tick at a configurable multiple. A rate filter is a tick source itself, + * which means that rate filters can be chained. + * + * Rate filters enable deterministic system execution which cannot be achieved + * with interval timers alone. For example, if timer A has interval 2.0 and + * timer B has interval 4.0, it is not guaranteed that B will tick at exactly + * twice the multiple of A. This is partly due to the indeterministic nature of + * timers, and partly due to floating point rounding errors. + * + * Rate filters can be combined with timers (or other rate filters) to ensure + * that a system ticks at an exact multiple of a tick source (which can be + * another system). If a rate filter is created with a rate of 1 it will tick + * at the exact same time as its source. + * + * If no tick source is provided, the rate filter will use the frame tick as + * source, which corresponds with the number of times ecs_progress() is called. + * + * The tick_source entity will be a tick source after this operation. Tick + * sources can be read by getting the EcsTickSource component. If the tick + * source ticked this frame, the 'tick' member will be true. When the tick + * source is a system, the system will tick when the timer ticks. + * + * @param world The world. + * @param tick_source The rate filter entity (0 to create one). + * @param rate The rate to apply. + * @param source The tick source (0 to use frames) + * @return The filter entity. + */ +FLECS_API +ecs_entity_t ecs_set_rate( + ecs_world_t *world, + ecs_entity_t tick_source, + int32_t rate, + ecs_entity_t source); + +/** Assign tick source to system. + * Systems can be their own tick source, which can be any of the tick sources + * (one shot timers, interval times and rate filters). However, in some cases it + * is must be guaranteed that different systems tick on the exact same frame. + * + * This cannot be guaranteed by giving two systems the same interval/rate filter + * as it is possible that one system is (for example) disabled, which would + * cause the systems to go out of sync. To provide these guarantees, systems + * must use the same tick source, which is what this operation enables. + * + * When two systems share the same tick source, it is guaranteed that they tick + * in the same frame. The provided tick source can be any entity that is a tick + * source, including another system. If the provided entity is not a tick source + * the system will not be ran. + * + * To disassociate a tick source from a system, use 0 for the tick_source + * parameter. + * + * @param world The world. + * @param system The system to associate with the timer. + * @param tick_source The tick source to associate with the system. + */ +FLECS_API +void ecs_set_tick_source( + ecs_world_t *world, + ecs_entity_t system, + ecs_entity_t tick_source); + + +//////////////////////////////////////////////////////////////////////////////// +//// Module +//////////////////////////////////////////////////////////////////////////////// + +/** Timer module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsTimer) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsTimerImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_PIPELINE +#ifdef FLECS_NO_PIPELINE +#error "FLECS_NO_PIPELINE failed: PIPELINE is required by other addons" +#endif +/** + * @file addons/pipeline.h + * @brief Pipeline module. + * + * The pipeline module provides support for running systems automatically and + * on multiple threads. A pipeline is a collection of tags that can be added to + * systems. When ran, a pipeline will query for all systems that have the tags + * that belong to a pipeline, and run them. + * + * The module defines a number of builtin tags (EcsPreUpdate, EcsOnUpdate, + * EcsPostUpdate etc.) that are registered with the builtin pipeline. The + * builtin pipeline is ran by default when calling ecs_progress(). An + * application can set a custom pipeline with the ecs_set_pipeline() function. + */ + +#ifdef FLECS_PIPELINE + +/** + * @defgroup c_addons_pipeline Pipeline + * @ingroup c_addons + * Pipelines order and schedule systems for execution. + * + * @{ + */ + +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif + +#ifndef FLECS_SYSTEM +#define FLECS_SYSTEM +#endif + +#if !defined(FLECS_OS_API_IMPL) && !defined(FLECS_NO_OS_API_IMPL) +#define FLECS_OS_API_IMPL +#endif + +#ifndef FLECS_PIPELINE_H +#define FLECS_PIPELINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef FLECS_LEGACY + +/** Convenience macro to create a predeclared pipeline. + * Usage: + * @code + * ECS_ENTITY_DECLARE(MyPipeline); + * ECS_PIPELINE_DEFINE(world, MyPipeline, Update || Physics || Render) + * @endcode + */ +#define ECS_PIPELINE_DEFINE(world, id_, ...) \ + { \ + ecs_pipeline_desc_t desc = {0}; \ + ecs_entity_desc_t edesc = {0}; \ + edesc.id = id_;\ + edesc.name = #id_;\ + desc.entity = ecs_entity_init(world, &edesc);\ + desc.query.expr = #__VA_ARGS__; \ + id_ = ecs_pipeline_init(world, &desc); \ + ecs_id(id_) = id_;\ + } \ + ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, "failed to create pipeline"); + +/** Convenience macro to create a pipeline. + * Usage: + * @code + * ECS_PIPELINE(world, MyPipeline, Update || Physics || Render) + * @endcode + * + */ +#define ECS_PIPELINE(world, id, ...) \ + ecs_entity_t id = 0, ecs_id(id) = 0; ECS_PIPELINE_DEFINE(world, id, __VA_ARGS__);\ + (void)id;\ + (void)ecs_id(id); + +/** Convenience macro to create a pipeline. + * See ecs_pipeline_init(). + */ +#define ecs_pipeline(world, ...)\ + ecs_pipeline_init(world, &(ecs_pipeline_desc_t) __VA_ARGS__ ) + +#endif + +/** Pipeline descriptor, used with ecs_pipeline_init(). */ +typedef struct ecs_pipeline_desc_t { + /** Existing entity to associate with pipeline (optional). */ + ecs_entity_t entity; + + /** The pipeline query. + * Pipelines are queries that are matched with system entities. Pipeline + * queries are the same as regular queries, which means the same query rules + * apply. A common mistake is to try a pipeline that matches systems in a + * list of phases by specifying all the phases, like: + * OnUpdate, OnPhysics, OnRender + * + * That however creates a query that matches entities with OnUpdate _and_ + * OnPhysics _and_ OnRender tags, which is likely undesired. Instead, a + * query could use the or operator match a system that has one of the + * specified phases: + * OnUpdate || OnPhysics || OnRender + * + * This will return the correct set of systems, but they likely won't be in + * the correct order. To make sure systems are returned in the correct order + * two query ordering features can be used: + * - group_by + * - order_by + * + * Take a look at the system manual for a more detailed explanation of + * how query features can be applied to pipelines, and how the builtin + * pipeline query works. + */ + ecs_query_desc_t query; +} ecs_pipeline_desc_t; + +/** Create a custom pipeline. + * + * @param world The world. + * @param desc The pipeline descriptor. + * @return The pipeline, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_pipeline_init( + ecs_world_t *world, + const ecs_pipeline_desc_t *desc); + +/** Set a custom pipeline. + * This operation sets the pipeline to run when ecs_progress() is invoked. + * + * @param world The world. + * @param pipeline The pipeline to set. + */ +FLECS_API +void ecs_set_pipeline( + ecs_world_t *world, + ecs_entity_t pipeline); + +/** Get the current pipeline. + * This operation gets the current pipeline. + * + * @param world The world. + * @return The current pipeline. + */ +FLECS_API +ecs_entity_t ecs_get_pipeline( + const ecs_world_t *world); + +/** Progress a world. + * This operation progresses the world by running all systems that are both + * enabled and periodic on their matching entities. + * + * An application can pass a delta_time into the function, which is the time + * passed since the last frame. This value is passed to systems so they can + * update entity values proportional to the elapsed time since their last + * invocation. + * + * When an application passes 0 to delta_time, ecs_progress() will automatically + * measure the time passed since the last frame. If an application does not uses + * time management, it should pass a non-zero value for delta_time (1.0 is + * recommended). That way, no time will be wasted measuring the time. + * + * @param world The world to progress. + * @param delta_time The time passed since the last frame. + * @return false if ecs_quit() has been called, true otherwise. + */ +FLECS_API +bool ecs_progress( + ecs_world_t *world, + ecs_ftime_t delta_time); + +/** Set time scale. + * Increase or decrease simulation speed by the provided multiplier. + * + * @param world The world. + * @param scale The scale to apply (default = 1). + */ +FLECS_API +void ecs_set_time_scale( + ecs_world_t *world, + ecs_ftime_t scale); + +/** Reset world clock. + * Reset the clock that keeps track of the total time passed in the simulation. + * + * @param world The world. + */ +FLECS_API +void ecs_reset_clock( + ecs_world_t *world); + +/** Run pipeline. + * This will run all systems in the provided pipeline. This operation may be + * invoked from multiple threads, and only when staging is disabled, as the + * pipeline manages staging and, if necessary, synchronization between threads. + * + * If 0 is provided for the pipeline id, the default pipeline will be ran (this + * is either the builtin pipeline or the pipeline set with set_pipeline()). + * + * When using progress() this operation will be invoked automatically for the + * default pipeline (either the builtin pipeline or the pipeline set with + * set_pipeline()). An application may run additional pipelines. + * + * @param world The world. + * @param pipeline The pipeline to run. + * @param delta_time The delta_time to pass to systems. + */ +FLECS_API +void ecs_run_pipeline( + ecs_world_t *world, + ecs_entity_t pipeline, + ecs_ftime_t delta_time); + + +//////////////////////////////////////////////////////////////////////////////// +//// Threading +//////////////////////////////////////////////////////////////////////////////// + +/** Set number of worker threads. + * Setting this value to a value higher than 1 will start as many threads and + * will cause systems to evenly distribute matched entities across threads. The + * operation may be called multiple times to reconfigure the number of threads + * used, but never while running a system / pipeline. + * Calling ecs_set_threads() will also end the use of task threads setup with + * ecs_set_task_threads() and vice-versa. + * + * @param world The world. + * @param threads The number of threads to create. + */ +FLECS_API +void ecs_set_threads( + ecs_world_t *world, + int32_t threads); + +/** Set number of worker task threads. + * ecs_set_task_threads() is similar to ecs_set_threads(), except threads are treated + * as short-lived tasks and will be created and joined around each update of the world. + * Creation and joining of these tasks will use the os_api_t tasks APIs rather than the + * the standard thread API functions, although they may be the same if desired. + * This function is useful for multithreading world updates using an external + * asynchronous job system rather than long running threads by providing the APIs + * to create tasks for your job system and then wait on their conclusion. + * The operation may be called multiple times to reconfigure the number of task threads + * used, but never while running a system / pipeline. + * Calling ecs_set_task_threads() will also end the use of threads setup with + * ecs_set_threads() and vice-versa + * + * @param world The world. + * @param task_threads The number of task threads to create. + */ +FLECS_API +void ecs_set_task_threads( + ecs_world_t *world, + int32_t task_threads); + +/** Returns true if task thread use have been requested. + * + * @param world The world. + * @result Whether the world is using task threads. + */ +FLECS_API +bool ecs_using_task_threads( + ecs_world_t *world); + +//////////////////////////////////////////////////////////////////////////////// +//// Module +//////////////////////////////////////////////////////////////////////////////// + +/** Pipeline module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsPipeline) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsPipelineImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_SYSTEM +#ifdef FLECS_NO_SYSTEM +#error "FLECS_NO_SYSTEM failed: SYSTEM is required by other addons" +#endif +/** + * @file addons/system.h + * @brief System module. + * + * The system module allows for creating and running systems. A system is a + * query in combination with a callback function. In addition systems have + * support for time management and can be monitored by the stats addon. + */ + +#ifdef FLECS_SYSTEM + +/** + * @defgroup c_addons_system System + * @ingroup c_addons + * Systems are a query + function that can be ran manually or by a pipeline. + * + * @{ + */ + +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif + +#ifndef FLECS_SYSTEM_H +#define FLECS_SYSTEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Component used to provide a tick source to systems */ +typedef struct EcsTickSource { + bool tick; /**< True if providing tick */ + ecs_ftime_t time_elapsed; /**< Time elapsed since last tick */ +} EcsTickSource; + +/** Use with ecs_system_init() to create or update a system. */ +typedef struct ecs_system_desc_t { + int32_t _canary; + + /** Existing entity to associate with system (optional) */ + ecs_entity_t entity; + + /** System query parameters */ + ecs_query_desc_t query; + + /** Callback that is ran for each result returned by the system's query. This + * means that this callback can be invoked multiple times per system per + * frame, typically once for each matching table. */ + ecs_iter_action_t callback; + + /** Callback that is invoked when a system is ran. + * When left to NULL, the default system runner is used, which calls the + * "callback" action for each result returned from the system's query. + * + * It should not be assumed that the input iterator can always be iterated + * with ecs_query_next(). When a system is multithreaded and/or paged, the + * iterator can be either a worker or paged iterator. The correct function + * to use for iteration is ecs_iter_next(). + * + * An implementation can test whether the iterator is a query iterator by + * testing whether the it->next value is equal to ecs_query_next(). */ + ecs_run_action_t run; + + /** Context to be passed to callback (as ecs_iter_t::param) */ + void *ctx; + + /** Callback to free ctx. */ + ecs_ctx_free_t ctx_free; + + /** Context associated with callback (for language bindings). */ + void *callback_ctx; + + /** Callback to free callback ctx. */ + ecs_ctx_free_t callback_ctx_free; + + /** Context associated with run (for language bindings). */ + void *run_ctx; + + /** Callback to free run ctx. */ + ecs_ctx_free_t run_ctx_free; + + /** Interval in seconds at which the system should run */ + ecs_ftime_t interval; + + /** Rate at which the system should run */ + int32_t rate; + + /** External tick source that determines when system ticks */ + ecs_entity_t tick_source; + + /** If true, system will be ran on multiple threads */ + bool multi_threaded; + + /** If true, system will have access to the actual world. Cannot be true at the + * same time as multi_threaded. */ + bool immediate; +} ecs_system_desc_t; + +/** Create a system */ +FLECS_API +ecs_entity_t ecs_system_init( + ecs_world_t *world, + const ecs_system_desc_t *desc); + +/** System type, get with ecs_system_get() */ +typedef struct ecs_system_t { + ecs_header_t hdr; + + /** See ecs_system_desc_t */ + ecs_run_action_t run; + + /** See ecs_system_desc_t */ + ecs_iter_action_t action; + + /** System query */ + ecs_query_t *query; + + /** Entity associated with query */ + ecs_entity_t query_entity; + + /** Tick source associated with system */ + ecs_entity_t tick_source; + + /** Is system multithreaded */ + bool multi_threaded; + + /** Is system ran in immediate mode */ + bool immediate; + + /** Cached system name (for perf tracing) */ + const char *name; + + /** Userdata for system */ + void *ctx; + + /** Callback language binding context */ + void *callback_ctx; + + /** Run language binding context */ + void *run_ctx; + + /** Callback to free ctx. */ + ecs_ctx_free_t ctx_free; + + /** Callback to free callback ctx. */ + ecs_ctx_free_t callback_ctx_free; + + /** Callback to free run ctx. */ + ecs_ctx_free_t run_ctx_free; + + /** Time spent on running system */ + ecs_ftime_t time_spent; + + /** Time passed since last invocation */ + ecs_ftime_t time_passed; + + /** Last frame for which the system was considered */ + int64_t last_frame; + + /* Mixins */ + ecs_world_t *world; + ecs_entity_t entity; + flecs_poly_dtor_t dtor; +} ecs_system_t; + +/** Get system object. + * Returns the system object. Can be used to access various information about + * the system, like the query and context. + * + * @param world The world. + * @param system The system. + * @return The system object. + */ +FLECS_API +const ecs_system_t* ecs_system_get( + const ecs_world_t *world, + ecs_entity_t system); + +#ifndef FLECS_LEGACY + +/** Forward declare a system. */ +#define ECS_SYSTEM_DECLARE(id) ecs_entity_t ecs_id(id) + +/** Define a forward declared system. + * + * Example: + * + * @code + * ECS_SYSTEM_DEFINE(world, Move, EcsOnUpdate, Position, Velocity); + * @endcode + */ +#define ECS_SYSTEM_DEFINE(world, id_, phase, ...) \ + { \ + ecs_system_desc_t desc = {0}; \ + ecs_entity_desc_t edesc = {0}; \ + ecs_id_t add_ids[3] = {\ + ((phase) ? ecs_pair(EcsDependsOn, (phase)) : 0), \ + (phase), \ + 0 \ + };\ + edesc.id = ecs_id(id_);\ + edesc.name = #id_;\ + edesc.add = add_ids;\ + desc.entity = ecs_entity_init(world, &edesc);\ + desc.query.expr = #__VA_ARGS__; \ + desc.callback = id_; \ + ecs_id(id_) = ecs_system_init(world, &desc); \ + } \ + ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, "failed to create system %s", #id_) + +/** Declare & define a system. + * + * Example: + * + * @code + * ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); + * @endcode + */ +#define ECS_SYSTEM(world, id, phase, ...) \ + ecs_entity_t ecs_id(id) = 0; ECS_SYSTEM_DEFINE(world, id, phase, __VA_ARGS__);\ + ecs_entity_t id = ecs_id(id);\ + (void)ecs_id(id);\ + (void)id + +/** Shorthand for creating a system with ecs_system_init(). + * + * Example: + * + * @code + * ecs_system(world, { + * .entity = ecs_entity(world, { + * .name = "MyEntity", + * .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) + * }), + * .query.terms = { + * { ecs_id(Position) }, + * { ecs_id(Velocity) } + * }, + * .callback = Move + * }); + * @endcode + */ +#define ecs_system(world, ...)\ + ecs_system_init(world, &(ecs_system_desc_t) __VA_ARGS__ ) + +#endif + +/** Run a specific system manually. + * This operation runs a single system manually. It is an efficient way to + * invoke logic on a set of entities, as manual systems are only matched to + * tables at creation time or after creation time, when a new table is created. + * + * Manual systems are useful to evaluate lists of pre-matched entities at + * application defined times. Because none of the matching logic is evaluated + * before the system is invoked, manual systems are much more efficient than + * manually obtaining a list of entities and retrieving their components. + * + * An application may pass custom data to a system through the param parameter. + * This data can be accessed by the system through the param member in the + * ecs_iter_t value that is passed to the system callback. + * + * Any system may interrupt execution by setting the interrupted_by member in + * the ecs_iter_t value. This is particularly useful for manual systems, where + * the value of interrupted_by is returned by this operation. This, in + * combination with the param argument lets applications use manual systems + * to lookup entities: once the entity has been found its handle is passed to + * interrupted_by, which is then subsequently returned. + * + * @param world The world. + * @param system The system to run. + * @param delta_time The time passed since the last system invocation. + * @param param A user-defined parameter to pass to the system. + * @return handle to last evaluated entity if system was interrupted. + */ +FLECS_API +ecs_entity_t ecs_run( + ecs_world_t *world, + ecs_entity_t system, + ecs_ftime_t delta_time, + void *param); + +/** Same as ecs_run(), but subdivides entities across number of provided stages. + * + * @param world The world. + * @param system The system to run. + * @param stage_current The id of the current stage. + * @param stage_count The total number of stages. + * @param delta_time The time passed since the last system invocation. + * @param param A user-defined parameter to pass to the system. + * @return handle to last evaluated entity if system was interrupted. + */ +FLECS_API +ecs_entity_t ecs_run_worker( + ecs_world_t *world, + ecs_entity_t system, + int32_t stage_current, + int32_t stage_count, + ecs_ftime_t delta_time, + void *param); + +/** System module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsSystem) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsSystemImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_STATS +#ifdef FLECS_NO_STATS +#error "FLECS_NO_STATS failed: STATS is required by other addons" +#endif +/** + * @file addons/stats.h + * @brief Statistics addon. + * + * The stats addon tracks high resolution statistics for the world, systems and + * pipelines. The addon can be used as an API where an application calls + * functions to obtain statistics directly and as a module where statistics are + * automatically tracked. The latter is required for statistics tracking in the + * explorer. + * + * When the addon is imported as module, statistics are tracked for each frame, + * second, minute, hour, day and week with 60 datapoints per tier. + */ + +#ifdef FLECS_STATS + +/** + * @defgroup c_addons_stats Stats + * @ingroup c_addons + * Collection of statistics for world, queries, systems and pipelines. + * + * @{ + */ + +#ifndef FLECS_STATS_H +#define FLECS_STATS_H + +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif + +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ECS_STAT_WINDOW (60) + +/** Simple value that indicates current state */ +typedef struct ecs_gauge_t { + ecs_float_t avg[ECS_STAT_WINDOW]; + ecs_float_t min[ECS_STAT_WINDOW]; + ecs_float_t max[ECS_STAT_WINDOW]; +} ecs_gauge_t; + +/** Monotonically increasing counter */ +typedef struct ecs_counter_t { + ecs_gauge_t rate; /**< Keep track of deltas too */ + double value[ECS_STAT_WINDOW]; +} ecs_counter_t; + +/** Make all metrics the same size, so we can iterate over fields */ +typedef union ecs_metric_t { + ecs_gauge_t gauge; + ecs_counter_t counter; +} ecs_metric_t; + +typedef struct ecs_world_stats_t { + int64_t first_; + + /* Entities */ + struct { + ecs_metric_t count; /**< Number of entities */ + ecs_metric_t not_alive_count; /**< Number of not alive (recyclable) entity ids */ + } entities; + + /* Component ids */ + struct { + ecs_metric_t tag_count; /**< Number of tag ids (ids without data) */ + ecs_metric_t component_count; /**< Number of components ids (ids with data) */ + ecs_metric_t pair_count; /**< Number of pair ids */ + ecs_metric_t type_count; /**< Number of registered types */ + ecs_metric_t create_count; /**< Number of times id has been created */ + ecs_metric_t delete_count; /**< Number of times id has been deleted */ + } components; + + /* Tables */ + struct { + ecs_metric_t count; /**< Number of tables */ + ecs_metric_t empty_count; /**< Number of empty tables */ + ecs_metric_t create_count; /**< Number of times table has been created */ + ecs_metric_t delete_count; /**< Number of times table has been deleted */ + } tables; + + /* Queries & events */ + struct { + ecs_metric_t query_count; /**< Number of queries */ + ecs_metric_t observer_count; /**< Number of observers */ + ecs_metric_t system_count; /**< Number of systems */ + } queries; + + /* Commands */ + struct { + ecs_metric_t add_count; + ecs_metric_t remove_count; + ecs_metric_t delete_count; + ecs_metric_t clear_count; + ecs_metric_t set_count; + ecs_metric_t ensure_count; + ecs_metric_t modified_count; + ecs_metric_t other_count; + ecs_metric_t discard_count; + ecs_metric_t batched_entity_count; + ecs_metric_t batched_count; + } commands; + + /* Frame data */ + struct { + ecs_metric_t frame_count; /**< Number of frames processed. */ + ecs_metric_t merge_count; /**< Number of merges executed. */ + ecs_metric_t rematch_count; /**< Number of query rematches */ + ecs_metric_t pipeline_build_count; /**< Number of system pipeline rebuilds (occurs when an inactive system becomes active). */ + ecs_metric_t systems_ran; /**< Number of systems ran. */ + ecs_metric_t observers_ran; /**< Number of times an observer was invoked. */ + ecs_metric_t event_emit_count; /**< Number of events emitted */ + } frame; + + /* Timing */ + struct { + ecs_metric_t world_time_raw; /**< Actual time passed since simulation start (first time progress() is called) */ + ecs_metric_t world_time; /**< Simulation time passed since simulation start. Takes into account time scaling */ + ecs_metric_t frame_time; /**< Time spent processing a frame. Smaller than world_time_total when load is not 100% */ + ecs_metric_t system_time; /**< Time spent on running systems. */ + ecs_metric_t emit_time; /**< Time spent on notifying observers. */ + ecs_metric_t merge_time; /**< Time spent on merging commands. */ + ecs_metric_t rematch_time; /**< Time spent on rematching. */ + ecs_metric_t fps; /**< Frames per second. */ + ecs_metric_t delta_time; /**< Delta_time. */ + } performance; + + struct { + /* Memory allocation data */ + ecs_metric_t alloc_count; /**< Allocs per frame */ + ecs_metric_t realloc_count; /**< Reallocs per frame */ + ecs_metric_t free_count; /**< Frees per frame */ + ecs_metric_t outstanding_alloc_count; /**< Difference between allocs & frees */ + + /* Memory allocator data */ + ecs_metric_t block_alloc_count; /**< Block allocations per frame */ + ecs_metric_t block_free_count; /**< Block frees per frame */ + ecs_metric_t block_outstanding_alloc_count; /**< Difference between allocs & frees */ + ecs_metric_t stack_alloc_count; /**< Page allocations per frame */ + ecs_metric_t stack_free_count; /**< Page frees per frame */ + ecs_metric_t stack_outstanding_alloc_count; /**< Difference between allocs & frees */ + } memory; + + /* HTTP statistics */ + struct { + ecs_metric_t request_received_count; + ecs_metric_t request_invalid_count; + ecs_metric_t request_handled_ok_count; + ecs_metric_t request_handled_error_count; + ecs_metric_t request_not_handled_count; + ecs_metric_t request_preflight_count; + ecs_metric_t send_ok_count; + ecs_metric_t send_error_count; + ecs_metric_t busy_count; + } http; + + int64_t last_; + + /** Current position in ring buffer */ + int32_t t; +} ecs_world_stats_t; + +/** Statistics for a single query (use ecs_query_cache_stats_get) */ +typedef struct ecs_query_stats_t { + int64_t first_; + ecs_metric_t result_count; /**< Number of query results */ + ecs_metric_t matched_table_count; /**< Number of matched tables */ + ecs_metric_t matched_entity_count; /**< Number of matched entities */ + int64_t last_; + + /** Current position in ringbuffer */ + int32_t t; +} ecs_query_stats_t; + +/** Statistics for a single system (use ecs_system_stats_get()) */ +typedef struct ecs_system_stats_t { + int64_t first_; + ecs_metric_t time_spent; /**< Time spent processing a system */ + int64_t last_; + + bool task; /**< Is system a task */ + + ecs_query_stats_t query; +} ecs_system_stats_t; + +/** Statistics for sync point */ +typedef struct ecs_sync_stats_t { + int64_t first_; + ecs_metric_t time_spent; + ecs_metric_t commands_enqueued; + int64_t last_; + + int32_t system_count; + bool multi_threaded; + bool immediate; +} ecs_sync_stats_t; + +/** Statistics for all systems in a pipeline. */ +typedef struct ecs_pipeline_stats_t { + /* Allow for initializing struct with {0} */ + int8_t canary_; + + /** Vector with system ids of all systems in the pipeline. The systems are + * stored in the order they are executed. Merges are represented by a 0. */ + ecs_vec_t systems; + + /** Vector with sync point stats */ + ecs_vec_t sync_points; + + /** Current position in ring buffer */ + int32_t t; + + int32_t system_count; /**< Number of systems in pipeline */ + int32_t active_system_count; /**< Number of active systems in pipeline */ + int32_t rebuild_count; /**< Number of times pipeline has rebuilt */ +} ecs_pipeline_stats_t; + +/** Get world statistics. + * + * @param world The world. + * @param stats Out parameter for statistics. + */ +FLECS_API +void ecs_world_stats_get( + const ecs_world_t *world, + ecs_world_stats_t *stats); + +/** Reduce source measurement window into single destination measurement. */ +FLECS_API +void ecs_world_stats_reduce( + ecs_world_stats_t *dst, + const ecs_world_stats_t *src); + +/** Reduce last measurement into previous measurement, restore old value. */ +FLECS_API +void ecs_world_stats_reduce_last( + ecs_world_stats_t *stats, + const ecs_world_stats_t *old, + int32_t count); + +/** Repeat last measurement. */ +FLECS_API +void ecs_world_stats_repeat_last( + ecs_world_stats_t *stats); + +/** Copy last measurement from source to destination. */ +FLECS_API +void ecs_world_stats_copy_last( + ecs_world_stats_t *dst, + const ecs_world_stats_t *src); + +FLECS_API +void ecs_world_stats_log( + const ecs_world_t *world, + const ecs_world_stats_t *stats); + +/** Get query statistics. + * Obtain statistics for the provided query. + * + * @param world The world. + * @param query The query. + * @param stats Out parameter for statistics. + */ +FLECS_API +void ecs_query_stats_get( + const ecs_world_t *world, + const ecs_query_t *query, + ecs_query_stats_t *stats); + +/** Reduce source measurement window into single destination measurement. */ +FLECS_API +void ecs_query_cache_stats_reduce( + ecs_query_stats_t *dst, + const ecs_query_stats_t *src); + +/** Reduce last measurement into previous measurement, restore old value. */ +FLECS_API +void ecs_query_cache_stats_reduce_last( + ecs_query_stats_t *stats, + const ecs_query_stats_t *old, + int32_t count); + +/** Repeat last measurement. */ +FLECS_API +void ecs_query_cache_stats_repeat_last( + ecs_query_stats_t *stats); + +/** Copy last measurement from source to destination. */ +FLECS_API +void ecs_query_cache_stats_copy_last( + ecs_query_stats_t *dst, + const ecs_query_stats_t *src); + +/** Get system statistics. + * Obtain statistics for the provided system. + * + * @param world The world. + * @param system The system. + * @param stats Out parameter for statistics. + * @return true if success, false if not a system. + */ +FLECS_API +bool ecs_system_stats_get( + const ecs_world_t *world, + ecs_entity_t system, + ecs_system_stats_t *stats); + +/** Reduce source measurement window into single destination measurement */ +FLECS_API +void ecs_system_stats_reduce( + ecs_system_stats_t *dst, + const ecs_system_stats_t *src); + +/** Reduce last measurement into previous measurement, restore old value. */ +FLECS_API +void ecs_system_stats_reduce_last( + ecs_system_stats_t *stats, + const ecs_system_stats_t *old, + int32_t count); + +/** Repeat last measurement. */ +FLECS_API +void ecs_system_stats_repeat_last( + ecs_system_stats_t *stats); + +/** Copy last measurement from source to destination. */ +FLECS_API +void ecs_system_stats_copy_last( + ecs_system_stats_t *dst, + const ecs_system_stats_t *src); + +/** Get pipeline statistics. + * Obtain statistics for the provided pipeline. + * + * @param world The world. + * @param pipeline The pipeline. + * @param stats Out parameter for statistics. + * @return true if success, false if not a pipeline. + */ +FLECS_API +bool ecs_pipeline_stats_get( + ecs_world_t *world, + ecs_entity_t pipeline, + ecs_pipeline_stats_t *stats); + +/** Free pipeline stats. + * + * @param stats The stats to free. + */ +FLECS_API +void ecs_pipeline_stats_fini( + ecs_pipeline_stats_t *stats); + +/** Reduce source measurement window into single destination measurement */ +FLECS_API +void ecs_pipeline_stats_reduce( + ecs_pipeline_stats_t *dst, + const ecs_pipeline_stats_t *src); + +/** Reduce last measurement into previous measurement, restore old value. */ +FLECS_API +void ecs_pipeline_stats_reduce_last( + ecs_pipeline_stats_t *stats, + const ecs_pipeline_stats_t *old, + int32_t count); + +/** Repeat last measurement. */ +FLECS_API +void ecs_pipeline_stats_repeat_last( + ecs_pipeline_stats_t *stats); + +/** Copy last measurement to destination. + * This operation copies the last measurement into the destination. It does not + * modify the cursor. + * + * @param dst The metrics. + * @param src The metrics to copy. + */ +FLECS_API +void ecs_pipeline_stats_copy_last( + ecs_pipeline_stats_t *dst, + const ecs_pipeline_stats_t *src); + +/** Reduce all measurements from a window into a single measurement. */ +FLECS_API +void ecs_metric_reduce( + ecs_metric_t *dst, + const ecs_metric_t *src, + int32_t t_dst, + int32_t t_src); + +/** Reduce last measurement into previous measurement */ +FLECS_API +void ecs_metric_reduce_last( + ecs_metric_t *m, + int32_t t, + int32_t count); + +/** Copy measurement */ +FLECS_API +void ecs_metric_copy( + ecs_metric_t *m, + int32_t dst, + int32_t src); + +FLECS_API extern ECS_COMPONENT_DECLARE(FlecsStats); /**< Flecs stats module. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldStats); /**< Component id for EcsWorldStats. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldSummary); /**< Component id for EcsWorldSummary. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsSystemStats); /**< Component id for EcsSystemStats. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsPipelineStats); /**< Component id for EcsPipelineStats. */ + +FLECS_API extern ecs_entity_t EcsPeriod1s; /**< Tag used for metrics collected in last second. */ +FLECS_API extern ecs_entity_t EcsPeriod1m; /**< Tag used for metrics collected in last minute. */ +FLECS_API extern ecs_entity_t EcsPeriod1h; /**< Tag used for metrics collected in last hour. */ +FLECS_API extern ecs_entity_t EcsPeriod1d; /**< Tag used for metrics collected in last day. */ +FLECS_API extern ecs_entity_t EcsPeriod1w; /**< Tag used for metrics collected in last week. */ + +/** Common data for statistics. */ +typedef struct { + ecs_ftime_t elapsed; + int32_t reduce_count; +} EcsStatsHeader; + +/** Component that stores world statistics. */ +typedef struct { + EcsStatsHeader hdr; + ecs_world_stats_t stats; +} EcsWorldStats; + +/** Component that stores system statistics. */ +typedef struct { + EcsStatsHeader hdr; + ecs_map_t stats; +} EcsSystemStats; + +/** Component that stores pipeline statistics. */ +typedef struct { + EcsStatsHeader hdr; + ecs_map_t stats; +} EcsPipelineStats; + +/** Component that stores a summary of world statistics. */ +typedef struct { + /* Time */ + double target_fps; /**< Target FPS */ + double time_scale; /**< Simulation time scale */ + + /* Total time */ + double frame_time_total; /**< Total time spent processing a frame */ + double system_time_total; /**< Total time spent in systems */ + double merge_time_total; /**< Total time spent in merges */ + + /* Last frame time */ + double frame_time_last; /**< Time spent processing a frame */ + double system_time_last; /**< Time spent in systems */ + double merge_time_last; /**< Time spent in merges */ + + int64_t frame_count; /**< Number of frames processed */ + int64_t command_count; /**< Number of commands processed */ + + /* Build info */ + ecs_build_info_t build_info; /**< Build info */ +} EcsWorldSummary; + +/** Stats module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsStats) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsStatsImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_METRICS +#ifdef FLECS_NO_METRICS +#error "FLECS_NO_METRICS failed: METRICS is required by other addons" +#endif +/** + * @file addons/metrics.h + * @brief Metrics module. + * + * The metrics module extracts metrics from components and makes them available + * through a unified component interface. + */ + +#ifdef FLECS_METRICS + +/** + * @defgroup c_addons_metrics Metrics + * @ingroup c_addons + * Collect user-defined metrics from ECS data. + * + * @{ + */ + +#ifndef FLECS_METRICS_H +#define FLECS_METRICS_H + +#ifndef FLECS_META +#define FLECS_META +#endif + +#ifndef FLECS_UNITS +#define FLECS_UNITS +#endif + +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** Flecs metrics module. */ +FLECS_API extern ECS_COMPONENT_DECLARE(FlecsMetrics); + +/** Tag added to metrics, and used as first element of metric kind pair. */ +FLECS_API extern ECS_TAG_DECLARE(EcsMetric); + +/** Metric that has monotonically increasing value. */ +FLECS_API extern ECS_TAG_DECLARE(EcsCounter); + +/** Counter metric that is auto-incremented by source value. */ +FLECS_API extern ECS_TAG_DECLARE(EcsCounterIncrement); + +/** Counter metric that counts the number of entities with an id. */ +FLECS_API extern ECS_TAG_DECLARE(EcsCounterId); + +/** Metric that represents current value. */ +FLECS_API extern ECS_TAG_DECLARE(EcsGauge); + +/** Tag added to metric instances. */ +FLECS_API extern ECS_TAG_DECLARE(EcsMetricInstance); + +/** Component with metric instance value. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsMetricValue); + +/** Component with entity source of metric instance. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsMetricSource); + +/** Component that stores metric value. */ +typedef struct EcsMetricValue { + double value; +} EcsMetricValue; + +/** Component that stores metric source. */ +typedef struct EcsMetricSource { + ecs_entity_t entity; +} EcsMetricSource; + +/** Used with ecs_metric_init to create metric. */ +typedef struct ecs_metric_desc_t { + int32_t _canary; + + /** Entity associated with metric */ + ecs_entity_t entity; + + /** Entity associated with member that stores metric value. Must not be set + * at the same time as id. Cannot be combined with EcsCounterId. */ + ecs_entity_t member; + + /* Member dot expression. Can be used instead of member and supports nested + * members. Must be set together with id and should not be set at the same + * time as member. */ + const char *dotmember; + + /** Tracks whether entities have the specified component id. Must not be set + * at the same time as member. */ + ecs_id_t id; + + /** If id is a (R, *) wildcard and relationship R has the OneOf property, + * setting this value to true will track individual targets. + * If the kind is EcsCountId and the id is a (R, *) wildcard, this value + * will create a metric per target. */ + bool targets; + + /** Must be EcsGauge, EcsCounter, EcsCounterIncrement or EcsCounterId */ + ecs_entity_t kind; + + /** Description of metric. Will only be set if FLECS_DOC addon is enabled */ + const char *brief; +} ecs_metric_desc_t; + +/** Create a new metric. + * Metrics are entities that store values measured from a range of different + * properties in the ECS storage. Metrics provide a single unified interface to + * discovering and reading these values, which can be useful for monitoring + * utilities, or for debugging. + * + * Examples of properties that can be measured by metrics are: + * - Component member values + * - How long an entity has had a specific component + * - How long an entity has had a specific target for a relationship + * - How many entities have a specific component + * + * Metrics can either be created as a "gauge" or "counter". A gauge is a metric + * that represents the value of something at a specific point in time, for + * example "velocity". A counter metric represents a value that is monotonically + * increasing, for example "miles driven". + * + * There are three different kinds of counter metric kinds: + * - EcsCounter + * When combined with a member, this will store the actual value of the member + * in the metric. This is useful for values that are already counters, such as + * a MilesDriven component. + * This kind creates a metric per entity that has the member/id. + * + * - EcsCounterIncrement + * When combined with a member, this will increment the value of the metric by + * the value of the member * delta_time. This is useful for values that are + * not counters, such as a Velocity component. + * This kind creates a metric per entity that has the member. + * + * - EcsCounterId + * This metric kind will count the number of entities with a specific + * (component) id. This kind creates a single metric instance for regular ids, + * and a metric instance per target for wildcard ids when targets is set. + * + * @param world The world. + * @param desc Metric description. + * @return The metric entity. + */ +FLECS_API +ecs_entity_t ecs_metric_init( + ecs_world_t *world, + const ecs_metric_desc_t *desc); + +/** Shorthand for creating a metric with ecs_metric_init(). + * + * Example: + * + * @code + * ecs_metric(world, { + * .member = ecs_lookup(world, "Position.x") + * .kind = EcsGauge + * }); + * @endcode + */ +#define ecs_metric(world, ...)\ + ecs_metric_init(world, &(ecs_metric_desc_t) __VA_ARGS__ ) + +/** Metrics module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsMetrics) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsMetricsImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_ALERTS +#ifdef FLECS_NO_ALERTS +#error "FLECS_NO_ALERTS failed: ALERTS is required by other addons" +#endif +/** + * @file addons/alerts.h + * @brief Alerts module. + * + * The alerts module enables applications to register alerts for when certain + * conditions are met. Alerts are registered as queries, and automatically + * become active when entities match the alert query. + */ + +#ifdef FLECS_ALERTS + +/** + * @defgroup c_addons_alerts Alerts + * @ingroup c_addons + * Create alerts from monitoring queries. + * + * @{ + */ + +#ifndef FLECS_ALERTS_H +#define FLECS_ALERTS_H + +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ECS_ALERT_MAX_SEVERITY_FILTERS (4) + +/** Module id. */ +FLECS_API extern ECS_COMPONENT_DECLARE(FlecsAlerts); + +/* Module components */ + +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlert); /**< Component added to alert, and used as first element of alert severity pair. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertInstance); /**< Component added to alert instance. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertsActive); /**< Component added to alert source which tracks how many active alerts there are. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertTimeout); /**< Component added to alert which tracks how long an alert has been inactive. */ + +/* Alert severity tags */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertInfo); /**< Info alert severity. */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertWarning); /**< Warning alert severity. */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertError); /**< Error alert severity. */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertCritical); /**< Critical alert severity. */ + +/** Component added to alert instance. */ +typedef struct EcsAlertInstance { + char *message; /**< Generated alert message */ +} EcsAlertInstance; + +/** Map with active alerts for entity. */ +typedef struct EcsAlertsActive { + int32_t info_count; /**< Number of alerts for source with info severity */ + int32_t warning_count; /**< Number of alerts for source with warning severity */ + int32_t error_count; /**< Number of alerts for source with error severity */ + ecs_map_t alerts; +} EcsAlertsActive; + +/** Alert severity filter. + * A severity filter can adjust the severity of an alert based on whether an + * entity in the alert query has a specific component. For example, a filter + * could check if an entity has the "Production" tag, and increase the default + * severity of an alert from Warning to Error. + */ +typedef struct ecs_alert_severity_filter_t { + ecs_entity_t severity; /* Severity kind */ + ecs_id_t with; /* Component to match */ + const char *var; /* Variable to match component on. Do not include the + * '$' character. Leave to NULL for $this. */ + int32_t _var_index; /* Index of variable in filter (do not set) */ +} ecs_alert_severity_filter_t; + +/** Alert descriptor, used with ecs_alert_init(). */ +typedef struct ecs_alert_desc_t { + int32_t _canary; + + /** Entity associated with alert */ + ecs_entity_t entity; + + /** Alert query. An alert will be created for each entity that matches the + * specified query. The query must have at least one term that uses the + * $this variable (default). */ + ecs_query_desc_t query; + + /** Template for alert message. This string is used to generate the alert + * message and may refer to variables in the query result. The format for + * the template expressions is as specified by ecs_script_string_interpolate(). + * + * Examples: + * + * "$this has Position but not Velocity" + * "$this has a parent entity $parent without Position" + */ + const char *message; + + /** User friendly name. Will only be set if FLECS_DOC addon is enabled. */ + const char *doc_name; + + /** Description of alert. Will only be set if FLECS_DOC addon is enabled */ + const char *brief; + + /** Metric kind. Must be EcsAlertInfo, EcsAlertWarning, EcsAlertError or + * EcsAlertCritical. Defaults to EcsAlertError. */ + ecs_entity_t severity; + + /** Severity filters can be used to assign different severities to the same + * alert. This prevents having to create multiple alerts, and allows + * entities to transition between severities without resetting the + * alert duration (optional). */ + ecs_alert_severity_filter_t severity_filters[ECS_ALERT_MAX_SEVERITY_FILTERS]; + + /** The retain period specifies how long an alert must be inactive before it + * is cleared. This makes it easier to track noisy alerts. While an alert is + * inactive its duration won't increase. + * When the retain period is 0, the alert will clear immediately after it no + * longer matches the alert query. */ + ecs_ftime_t retain_period; + + /** Alert when member value is out of range. Uses the warning/error ranges + * assigned to the member in the MemberRanges component (optional). */ + ecs_entity_t member; + + /** (Component) id of member to monitor. If left to 0 this will be set to + * the parent entity of the member (optional). */ + ecs_id_t id; + + /** Variable from which to fetch the member (optional). When left to NULL + * 'id' will be obtained from $this. */ + const char *var; +} ecs_alert_desc_t; + +/** Create a new alert. + * An alert is a query that is evaluated periodically and creates alert + * instances for each entity that matches the query. Alerts can be used to + * automate detection of errors in an application. + * + * Alerts are automatically cleared when a query is no longer true for an alert + * instance. At most one alert instance will be created per matched entity. + * + * Alert instances have three components: + * - AlertInstance: contains the alert message for the instance + * - MetricSource: contains the entity that triggered the alert + * - MetricValue: contains how long the alert has been active + * + * Alerts reuse components from the metrics addon so that alert instances can be + * tracked and discovered as metrics. Just like metrics, alert instances are + * created as children of the alert. + * + * When an entity has active alerts, it will have the EcsAlertsActive component + * which contains a map with active alerts for the entity. This component + * will be automatically removed once all alerts are cleared for the entity. + * + * @param world The world. + * @param desc Alert description. + * @return The alert entity. + */ +FLECS_API +ecs_entity_t ecs_alert_init( + ecs_world_t *world, + const ecs_alert_desc_t *desc); + +/** Create a new alert. + * @see ecs_alert_init() + */ +#define ecs_alert(world, ...)\ + ecs_alert_init(world, &(ecs_alert_desc_t)__VA_ARGS__) + +/** Return number of active alerts for entity. + * When a valid alert entity is specified for the alert parameter, the operation + * will return whether the specified alert is active for the entity. When no + * alert is specified, the operation will return the total number of active + * alerts for the entity. + * + * @param world The world. + * @param entity The entity. + * @param alert The alert to test for (optional). + * @return The number of active alerts for the entity. + */ +FLECS_API +int32_t ecs_get_alert_count( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t alert); + +/** Return alert instance for specified alert. + * This operation returns the alert instance for the specified alert. If the + * alert is not active for the entity, the operation will return 0. + * + * @param world The world. + * @param entity The entity. + * @param alert The alert to test for. + * @return The alert instance for the specified alert. + */ +FLECS_API +ecs_entity_t ecs_get_alert( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_entity_t alert); + +/** Alert module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsAlerts) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsAlertsImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_JSON +#ifdef FLECS_NO_JSON +#error "FLECS_NO_JSON failed: JSON is required by other addons" +#endif +/** + * @file addons/json.h + * @brief JSON parser addon. + * + * Parse expression strings into component values. Entity identifiers, + * enumerations and bitmasks are encoded as strings. + * + * See docs/FlecsRemoteApi.md for a description of the JSON format. + */ + +#ifdef FLECS_JSON + +#ifndef FLECS_META +#define FLECS_META +#endif + +#ifndef FLECS_SCRIPT +#define FLECS_SCRIPT +#endif + +#ifndef FLECS_JSON_H +#define FLECS_JSON_H + +/** + * @defgroup c_addons_json Json + * @ingroup c_addons + * Functions for serializing to/from JSON. + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Used with ecs_ptr_from_json(), ecs_entity_from_json(). */ +typedef struct ecs_from_json_desc_t { + const char *name; /**< Name of expression (used for logging) */ + const char *expr; /**< Full expression (used for logging) */ + + /** Callback that allows for specifying a custom lookup function. The + * default behavior uses ecs_lookup() */ + ecs_entity_t (*lookup_action)( + const ecs_world_t*, + const char *value, + void *ctx); + void *lookup_ctx; + + /** Require components to be registered with reflection data. When not + * in strict mode, values for components without reflection are ignored. */ + bool strict; +} ecs_from_json_desc_t; + +/** Parse JSON string into value. + * This operation parses a JSON expression into the provided pointer. The + * memory pointed to must be large enough to contain a value of the used type. + * + * @param world The world. + * @param type The type of the expression to parse. + * @param ptr Pointer to the memory to write to. + * @param json The JSON expression to parse. + * @param desc Configuration parameters for deserializer. + * @return Pointer to the character after the last one read, or NULL if failed. + */ +FLECS_API +const char* ecs_ptr_from_json( + const ecs_world_t *world, + ecs_entity_t type, + void *ptr, + const char *json, + const ecs_from_json_desc_t *desc); + +/** Parse JSON object with multiple component values into entity. The format + * is the same as the one outputted by ecs_entity_to_json(), but at the moment + * only supports the "ids" and "values" member. + * + * @param world The world. + * @param entity The entity to serialize to. + * @param json The JSON expression to parse (see entity in JSON format manual). + * @param desc Configuration parameters for deserializer. + * @return Pointer to the character after the last one read, or NULL if failed. + */ +FLECS_API +const char* ecs_entity_from_json( + ecs_world_t *world, + ecs_entity_t entity, + const char *json, + const ecs_from_json_desc_t *desc); + +/** Parse JSON object with multiple entities into the world. The format is the + * same as the one outputted by ecs_world_to_json(). + * + * @param world The world. + * @param json The JSON expression to parse (see iterator in JSON format manual). + * @param desc Deserialization parameters. + * @return Last deserialized character, NULL if failed. + */ +FLECS_API +const char* ecs_world_from_json( + ecs_world_t *world, + const char *json, + const ecs_from_json_desc_t *desc); + +/** Same as ecs_world_from_json(), but loads JSON from file. + * + * @param world The world. + * @param filename The file from which to load the JSON. + * @param desc Deserialization parameters. + * @return Last deserialized character, NULL if failed. + */ +FLECS_API +const char* ecs_world_from_json_file( + ecs_world_t *world, + const char *filename, + const ecs_from_json_desc_t *desc); + +/** Serialize array into JSON string. + * This operation serializes a value of the provided type to a JSON string. The + * memory pointed to must be large enough to contain a value of the used type. + * + * If count is 0, the function will serialize a single value, not wrapped in + * array brackets. If count is >= 1, the operation will serialize values to a + * a comma-separated list inside of array brackets. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @param count The number of elements to serialize. + * @return String with JSON expression, or NULL if failed. + */ +FLECS_API +char* ecs_array_to_json( + const ecs_world_t *world, + ecs_entity_t type, + const void *data, + int32_t count); + +/** Serialize array into JSON string buffer. + * Same as ecs_array_to_json(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @param count The number of elements to serialize. + * @param buf_out The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_array_to_json_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *data, + int32_t count, + ecs_strbuf_t *buf_out); + +/** Serialize value into JSON string. + * Same as ecs_array_to_json(), with count = 0. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @return String with JSON expression, or NULL if failed. + */ +FLECS_API +char* ecs_ptr_to_json( + const ecs_world_t *world, + ecs_entity_t type, + const void *data); + +/** Serialize value into JSON string buffer. + * Same as ecs_ptr_to_json(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @param buf_out The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_ptr_to_json_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *data, + ecs_strbuf_t *buf_out); + +/** Serialize type info to JSON. + * This serializes type information to JSON, and can be used to store/transmit + * the structure of a (component) value. + * + * If the provided type does not have reflection data, "0" will be returned. + * + * @param world The world. + * @param type The type to serialize to JSON. + * @return A JSON string with the serialized type info, or NULL if failed. + */ +FLECS_API +char* ecs_type_info_to_json( + const ecs_world_t *world, + ecs_entity_t type); + +/** Serialize type info into JSON string buffer. + * Same as ecs_type_info_to_json(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param type The type to serialize. + * @param buf_out The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_type_info_to_json_buf( + const ecs_world_t *world, + ecs_entity_t type, + ecs_strbuf_t *buf_out); + +/** Used with ecs_iter_to_json(). */ +typedef struct ecs_entity_to_json_desc_t { + bool serialize_entity_id; /**< Serialize entity id */ + bool serialize_doc; /**< Serialize doc attributes */ + bool serialize_full_paths; /**< Serialize full paths for tags, components and pairs */ + bool serialize_inherited; /**< Serialize base components */ + bool serialize_values; /**< Serialize component values */ + bool serialize_builtin; /**< Serialize builtin data as components (e.g. "name", "parent") */ + bool serialize_type_info; /**< Serialize type info (requires serialize_values) */ + bool serialize_alerts; /**< Serialize active alerts for entity */ + ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */ + bool serialize_matches; /**< Serialize which queries entity matches with */ +} ecs_entity_to_json_desc_t; + +/** Utility used to initialize JSON entity serializer. */ +#define ECS_ENTITY_TO_JSON_INIT (ecs_entity_to_json_desc_t){\ + .serialize_doc = false, \ + .serialize_full_paths = true, \ + .serialize_inherited = false, \ + .serialize_values = true, \ + .serialize_builtin = false, \ + .serialize_type_info = false, \ + .serialize_alerts = false, \ + .serialize_refs = 0, \ + .serialize_matches = false, \ +} + +/** Serialize entity into JSON string. + * This creates a JSON object with the entity's (path) name, which components + * and tags the entity has, and the component values. + * + * The operation may fail if the entity contains components with invalid values. + * + * @param world The world. + * @param entity The entity to serialize to JSON. + * @return A JSON string with the serialized entity data, or NULL if failed. + */ +FLECS_API +char* ecs_entity_to_json( + const ecs_world_t *world, + ecs_entity_t entity, + const ecs_entity_to_json_desc_t *desc); + +/** Serialize entity into JSON string buffer. + * Same as ecs_entity_to_json(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param entity The entity to serialize. + * @param buf_out The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_entity_to_json_buf( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_strbuf_t *buf_out, + const ecs_entity_to_json_desc_t *desc); + +/** Used with ecs_iter_to_json(). */ +typedef struct ecs_iter_to_json_desc_t { + bool serialize_entity_ids; /**< Serialize entity ids */ + bool serialize_values; /**< Serialize component values */ + bool serialize_builtin; /**< Serialize builtin data as components (e.g. "name", "parent") */ + bool serialize_doc; /**< Serialize doc attributes */ + bool serialize_full_paths; /**< Serialize full paths for tags, components and pairs */ + bool serialize_fields; /**< Serialize field data */ + bool serialize_inherited; /**< Serialize inherited components */ + bool serialize_table; /**< Serialize entire table vs. matched components */ + bool serialize_type_info; /**< Serialize type information */ + bool serialize_field_info; /**< Serialize metadata for fields returned by query */ + bool serialize_query_info; /**< Serialize query terms */ + bool serialize_query_plan; /**< Serialize query plan */ + bool serialize_query_profile; /**< Profile query performance */ + bool dont_serialize_results; /**< If true, query won't be evaluated */ + bool serialize_alerts; /**< Serialize active alerts for entity */ + ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */ + bool serialize_matches; /**< Serialize which queries entity matches with */ + ecs_poly_t *query; /**< Query object (required for serialize_query_[plan|profile]). */ +} ecs_iter_to_json_desc_t; + +/** Utility used to initialize JSON iterator serializer. */ +#define ECS_ITER_TO_JSON_INIT (ecs_iter_to_json_desc_t){\ + .serialize_entity_ids = false, \ + .serialize_values = true, \ + .serialize_builtin = false, \ + .serialize_doc = false, \ + .serialize_full_paths = true, \ + .serialize_fields = true, \ + .serialize_inherited = false, \ + .serialize_table = false, \ + .serialize_type_info = false, \ + .serialize_field_info = false, \ + .serialize_query_info = false, \ + .serialize_query_plan = false, \ + .serialize_query_profile = false, \ + .dont_serialize_results = false, \ + .serialize_alerts = false, \ + .serialize_refs = false, \ + .serialize_matches = false, \ +} + +/** Serialize iterator into JSON string. + * This operation will iterate the contents of the iterator and serialize them + * to JSON. The function accepts iterators from any source. + * + * @param iter The iterator to serialize to JSON. + * @return A JSON string with the serialized iterator data, or NULL if failed. + */ +FLECS_API +char* ecs_iter_to_json( + ecs_iter_t *iter, + const ecs_iter_to_json_desc_t *desc); + +/** Serialize iterator into JSON string buffer. + * Same as ecs_iter_to_json(), but serializes to an ecs_strbuf_t instance. + * + * @param iter The iterator to serialize. + * @param buf_out The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_iter_to_json_buf( + ecs_iter_t *iter, + ecs_strbuf_t *buf_out, + const ecs_iter_to_json_desc_t *desc); + +/** Used with ecs_iter_to_json(). */ +typedef struct ecs_world_to_json_desc_t { + bool serialize_builtin; /**< Exclude flecs modules & contents */ + bool serialize_modules; /**< Exclude modules & contents */ +} ecs_world_to_json_desc_t; + +/** Serialize world into JSON string. + * This operation iterates the contents of the world to JSON. The operation is + * equivalent to the following code: + * + * @code + * ecs_query_t *f = ecs_query(world, { + * .terms = {{ .id = EcsAny }} + * }); + * + * ecs_iter_t it = ecs_query_init(world, &f); + * ecs_iter_to_json_desc_t desc = { .serialize_table = true }; + * ecs_iter_to_json(iter, &desc); + * @endcode + * + * @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, + const ecs_world_to_json_desc_t *desc); + +/** 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, + const ecs_world_to_json_desc_t *desc); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_UNITS +#ifdef FLECS_NO_UNITS +#error "FLECS_NO_UNITS failed: UNITS is required by other addons" +#endif +/** + * @file addons/units.h + * @brief Units module. + * + * Builtin standard units. The units addon is not imported by default, even if + * the addon is included in the build. To import the module, do: + * + * In C: + * + * @code + * ECS_IMPORT(world, FlecsUnits); + * @endcode + * + * In C++: + * + * @code + * world.import(); + * @endcode + * + * As a result this module behaves just like an application-defined module, + * which means that the ids generated for the entities inside the module are not + * fixed, and depend on the order in which the module is imported. + */ + +#ifdef FLECS_UNITS + +/** + * @defgroup c_addons_units Units. + * @ingroup c_addons + * Common unit annotations for reflection framework. + * + * @{ + */ + +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif + +#ifndef FLECS_META +#define FLECS_META +#endif + +#ifndef FLECS_UNITS_H +#define FLECS_UNITS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup c_addons_units_prefixes Prefixes + * @ingroup c_addons_units + * Prefixes to indicate unit count (e.g. Kilo, Mega) + * + * @{ + */ + +FLECS_API extern ecs_entity_t EcsUnitPrefixes; /**< Parent scope for prefixes. */ + +FLECS_API extern ecs_entity_t EcsYocto; /**< Yocto unit prefix. */ +FLECS_API extern ecs_entity_t EcsZepto; /**< Zepto unit prefix. */ +FLECS_API extern ecs_entity_t EcsAtto; /**< Atto unit prefix. */ +FLECS_API extern ecs_entity_t EcsFemto; /**< Femto unit prefix. */ +FLECS_API extern ecs_entity_t EcsPico; /**< Pico unit prefix. */ +FLECS_API extern ecs_entity_t EcsNano; /**< Nano unit prefix. */ +FLECS_API extern ecs_entity_t EcsMicro; /**< Micro unit prefix. */ +FLECS_API extern ecs_entity_t EcsMilli; /**< Milli unit prefix. */ +FLECS_API extern ecs_entity_t EcsCenti; /**< Centi unit prefix. */ +FLECS_API extern ecs_entity_t EcsDeci; /**< Deci unit prefix. */ +FLECS_API extern ecs_entity_t EcsDeca; /**< Deca unit prefix. */ +FLECS_API extern ecs_entity_t EcsHecto; /**< Hecto unit prefix. */ +FLECS_API extern ecs_entity_t EcsKilo; /**< Kilo unit prefix. */ +FLECS_API extern ecs_entity_t EcsMega; /**< Mega unit prefix. */ +FLECS_API extern ecs_entity_t EcsGiga; /**< Giga unit prefix. */ +FLECS_API extern ecs_entity_t EcsTera; /**< Tera unit prefix. */ +FLECS_API extern ecs_entity_t EcsPeta; /**< Peta unit prefix. */ +FLECS_API extern ecs_entity_t EcsExa; /**< Exa unit prefix. */ +FLECS_API extern ecs_entity_t EcsZetta; /**< Zetta unit prefix. */ +FLECS_API extern ecs_entity_t EcsYotta; /**< Yotta unit prefix. */ + +FLECS_API extern ecs_entity_t EcsKibi; /**< Kibi unit prefix. */ +FLECS_API extern ecs_entity_t EcsMebi; /**< Mebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsGibi; /**< Gibi unit prefix. */ +FLECS_API extern ecs_entity_t EcsTebi; /**< Tebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsPebi; /**< Pebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsExbi; /**< Exbi unit prefix. */ +FLECS_API extern ecs_entity_t EcsZebi; /**< Zebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsYobi; /**< Yobi unit prefix. */ + +/** @} */ + +/** + * @defgroup c_addons_units_duration Duration + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsDuration; /**< Duration quantity. */ +FLECS_API extern ecs_entity_t EcsPicoSeconds; /**< PicoSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsNanoSeconds; /**< NanoSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsMicroSeconds; /**< MicroSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsMilliSeconds; /**< MilliSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsSeconds; /**< Seconds duration unit. */ +FLECS_API extern ecs_entity_t EcsMinutes; /**< Minutes duration unit. */ +FLECS_API extern ecs_entity_t EcsHours; /**< Hours duration unit. */ +FLECS_API extern ecs_entity_t EcsDays; /**< Days duration unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_time Time + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsTime; /**< Time quantity. */ +FLECS_API extern ecs_entity_t EcsDate; /**< Date unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_mass Mass + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsMass; /**< Mass quantity. */ +FLECS_API extern ecs_entity_t EcsGrams; /**< Grams unit. */ +FLECS_API extern ecs_entity_t EcsKiloGrams; /**< KiloGrams unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_electric_Current Electric Current + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsElectricCurrent; /**< ElectricCurrent quantity. */ +FLECS_API extern ecs_entity_t EcsAmpere; /**< Ampere unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_amount Amount + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsAmount; /**< Amount quantity. */ +FLECS_API extern ecs_entity_t EcsMole; /**< Mole unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_luminous_intensity Luminous Intensity + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsLuminousIntensity; /**< LuminousIntensity quantity. */ +FLECS_API extern ecs_entity_t EcsCandela; /**< Candela unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_force Force + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsForce; /**< Force quantity. */ +FLECS_API extern ecs_entity_t EcsNewton; /**< Newton unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_length Length + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsLength; /**< Length quantity. */ +FLECS_API extern ecs_entity_t EcsMeters; /**< Meters unit. */ +FLECS_API extern ecs_entity_t EcsPicoMeters; /**< PicoMeters unit. */ +FLECS_API extern ecs_entity_t EcsNanoMeters; /**< NanoMeters unit. */ +FLECS_API extern ecs_entity_t EcsMicroMeters; /**< MicroMeters unit. */ +FLECS_API extern ecs_entity_t EcsMilliMeters; /**< MilliMeters unit. */ +FLECS_API extern ecs_entity_t EcsCentiMeters; /**< CentiMeters unit. */ +FLECS_API extern ecs_entity_t EcsKiloMeters; /**< KiloMeters unit. */ +FLECS_API extern ecs_entity_t EcsMiles; /**< Miles unit. */ +FLECS_API extern ecs_entity_t EcsPixels; /**< Pixels unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_pressure Pressure + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsPressure; /**< Pressure quantity. */ +FLECS_API extern ecs_entity_t EcsPascal; /**< Pascal unit. */ +FLECS_API extern ecs_entity_t EcsBar; /**< Bar unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_speed Speed + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsSpeed; /**< Speed quantity. */ +FLECS_API extern ecs_entity_t EcsMetersPerSecond; /**< MetersPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloMetersPerSecond; /**< KiloMetersPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloMetersPerHour; /**< KiloMetersPerHour unit. */ +FLECS_API extern ecs_entity_t EcsMilesPerHour; /**< MilesPerHour unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_temperature Temperature + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsTemperature; /**< Temperature quantity. */ +FLECS_API extern ecs_entity_t EcsKelvin; /**< Kelvin unit. */ +FLECS_API extern ecs_entity_t EcsCelsius; /**< Celsius unit. */ +FLECS_API extern ecs_entity_t EcsFahrenheit; /**< Fahrenheit unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_data Data + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsData; /**< Data quantity. */ +FLECS_API extern ecs_entity_t EcsBits; /**< Bits unit. */ +FLECS_API extern ecs_entity_t EcsKiloBits; /**< KiloBits unit. */ +FLECS_API extern ecs_entity_t EcsMegaBits; /**< MegaBits unit. */ +FLECS_API extern ecs_entity_t EcsGigaBits; /**< GigaBits unit. */ +FLECS_API extern ecs_entity_t EcsBytes; /**< Bytes unit. */ +FLECS_API extern ecs_entity_t EcsKiloBytes; /**< KiloBytes unit. */ +FLECS_API extern ecs_entity_t EcsMegaBytes; /**< MegaBytes unit. */ +FLECS_API extern ecs_entity_t EcsGigaBytes; /**< GigaBytes unit. */ +FLECS_API extern ecs_entity_t EcsKibiBytes; /**< KibiBytes unit. */ +FLECS_API extern ecs_entity_t EcsMebiBytes; /**< MebiBytes unit. */ +FLECS_API extern ecs_entity_t EcsGibiBytes; /**< GibiBytes unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_datarate Data Rate + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsDataRate; /**< DataRate quantity. */ +FLECS_API extern ecs_entity_t EcsBitsPerSecond; /**< BitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloBitsPerSecond; /**< KiloBitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsMegaBitsPerSecond; /**< MegaBitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsGigaBitsPerSecond; /**< GigaBitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsBytesPerSecond; /**< BytesPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloBytesPerSecond; /**< KiloBytesPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsMegaBytesPerSecond; /**< MegaBytesPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsGigaBytesPerSecond; /**< GigaBytesPerSecond unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_duration Duration + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsAngle; /**< Angle quantity. */ +FLECS_API extern ecs_entity_t EcsRadians; /**< Radians unit. */ +FLECS_API extern ecs_entity_t EcsDegrees; /**< Degrees unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_angle Angle + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsFrequency; /**< Frequency quantity. */ +FLECS_API extern ecs_entity_t EcsHertz; /**< Hertz unit. */ +FLECS_API extern ecs_entity_t EcsKiloHertz; /**< KiloHertz unit. */ +FLECS_API extern ecs_entity_t EcsMegaHertz; /**< MegaHertz unit. */ +FLECS_API extern ecs_entity_t EcsGigaHertz; /**< GigaHertz unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_uri Uri + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsUri; /**< URI quantity. */ +FLECS_API extern ecs_entity_t EcsUriHyperlink; /**< UriHyperlink unit. */ +FLECS_API extern ecs_entity_t EcsUriImage; /**< UriImage unit. */ +FLECS_API extern ecs_entity_t EcsUriFile; /**< UriFile unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_color Color + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsColor; /**< Color quantity. */ +FLECS_API extern ecs_entity_t EcsColorRgb; /**< ColorRgb unit. */ +FLECS_API extern ecs_entity_t EcsColorHsl; /**< ColorHsl unit. */ +FLECS_API extern ecs_entity_t EcsColorCss; /**< ColorCss unit. */ + +/** @} */ + + +FLECS_API extern ecs_entity_t EcsAcceleration; /**< Acceleration unit. */ +FLECS_API extern ecs_entity_t EcsPercentage; /**< Percentage unit. */ +FLECS_API extern ecs_entity_t EcsBel; /**< Bel unit. */ +FLECS_API extern ecs_entity_t EcsDeciBel; /**< DeciBel unit. */ + +//////////////////////////////////////////////////////////////////////////////// +//// Module +//////////////////////////////////////////////////////////////////////////////// + +/** Units module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsUnits) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsUnitsImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_SCRIPT +#ifdef FLECS_NO_SCRIPT +#error "FLECS_NO_SCRIPT failed: SCRIPT is required by other addons" +#endif +/** + * @file addons/script.h + * @brief Flecs script module. + * + * For script, see examples/script. + */ + +#ifdef FLECS_SCRIPT + +/** + * @defgroup c_addons_script Flecs script + * @ingroup c_addons + * DSL for loading scenes, assets and configuration. + * + * @{ + */ + +#ifndef FLECS_META +#define FLECS_META +#endif + +#ifndef FLECS_DOC +#define FLECS_DOC +#endif + + +#ifndef FLECS_SCRIPT_H +#define FLECS_SCRIPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +FLECS_API +extern ECS_COMPONENT_DECLARE(EcsScript); + +typedef struct ecs_script_template_t ecs_script_template_t; + +/** Script variable. */ +typedef struct ecs_script_var_t { + const char *name; + ecs_value_t value; + const ecs_type_info_t *type_info; +} ecs_script_var_t; + +/** Script variable scope. */ +typedef struct ecs_script_vars_t { + struct ecs_script_vars_t *parent; + ecs_hashmap_t var_index; + ecs_vec_t vars; + + const ecs_world_t *world; + struct ecs_stack_t *stack; + ecs_stack_cursor_t *cursor; + ecs_allocator_t *allocator; +} ecs_script_vars_t; + +/** Script object. */ +typedef struct ecs_script_t { + ecs_world_t *world; + const char *name; + const char *code; +} ecs_script_t; + +/** Script component. + * This component is added to the entities of managed scripts and templates. + */ +typedef struct EcsScript { + ecs_script_t *script; + ecs_script_template_t *template_; /* Only set for template scripts */ +} EcsScript; + + +/* Parsing & running scripts */ + +/** Parse script. + * This operation parses a script and returns a script object upon success. To + * run the script, call ecs_script_eval(). + * + * @param world The world. + * @param name Name of the script (typically a file/module name). + * @param code The script code. + * @return Script object if success, NULL if failed. +*/ +FLECS_API +ecs_script_t* ecs_script_parse( + ecs_world_t *world, + const char *name, + const char *code); + +/** Evaluate script. + * This operation evaluates (runs) a parsed script. + * + * @param script The script. + * @return Zero if success, non-zero if failed. +*/ +FLECS_API +int ecs_script_eval( + ecs_script_t *script); + +/** Free script. + * This operation frees a script object. + * + * Templates created by the script rely upon resources in the script object, + * and for that reason keep the script alive until all templates created by the + * script are deleted. + * + * @param script The script. + */ +FLECS_API +void ecs_script_free( + ecs_script_t *script); + +/** Parse script. + * This parses a script and instantiates the entities in the world. + * This operation is the equivalent to doing: + * + * @code + * ecs_script_t *script = ecs_script_parse(world, name, code); + * ecs_script_eval(script); + * ecs_script_free(script); + * @endcode + * + * @param world The world. + * @param name The script name (typically the file). + * @param code The script. + * @return Zero if success, non-zero otherwise. + */ +FLECS_API +int ecs_script_run( + ecs_world_t *world, + const char *name, + const char *code); + +/** Parse script file. + * This parses a script file and instantiates the entities in the world. This + * operation is equivalent to loading the file contents and passing it to + * ecs_script_run(). + * + * @param world The world. + * @param filename The script file name. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_script_run_file( + ecs_world_t *world, + const char *filename); + +/** Convert script AST to string. + * This operation converts the script abstract syntax tree to a string, which + * can be used to debug a script. + * + * @param script The script. + * @param buf The buffer to write to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_script_ast_to_buf( + ecs_script_t *script, + ecs_strbuf_t *buf); + +/** Convert script AST to string. + * This operation converts the script abstract syntax tree to a string, which + * can be used to debug a script. + * + * @param script The script. + * @return The string if success, NULL if failed. + */ +FLECS_API +char* ecs_script_ast_to_str( + ecs_script_t *script); + + +/* Managed scripts (script associated with entity that outlives the function) */ + +/** Used with ecs_script_init() */ +typedef struct ecs_script_desc_t { + ecs_entity_t entity; /* Set to customize entity handle associated with script */ + const char *filename; /* Set to load script from file */ + const char *code; /* Set to parse script from string */ +} ecs_script_desc_t; + +/** Load managed script. + * A managed script tracks which entities it creates, and keeps those entities + * synchronized when the contents of the script are updated. When the script is + * updated, entities that are no longer in the new version will be deleted. + * + * This feature is experimental. + * + * @param world The world. + * @param desc Script descriptor. + */ +FLECS_API +ecs_entity_t ecs_script_init( + ecs_world_t *world, + const ecs_script_desc_t *desc); + +#define ecs_script(world, ...)\ + ecs_script_init(world, &(ecs_script_desc_t) __VA_ARGS__) + +/** Update script with new code. + * + * @param world The world. + * @param script The script entity. + * @param instance An template instance (optional). + * @param code The script code. + */ +FLECS_API +int ecs_script_update( + ecs_world_t *world, + ecs_entity_t script, + ecs_entity_t instance, + const char *code); + +/** Clear all entities associated with script. + * + * @param world The world. + * @param script The script entity. + * @param instance The script instance. + */ +FLECS_API +void ecs_script_clear( + ecs_world_t *world, + ecs_entity_t script, + ecs_entity_t instance); + + +/* Script variables */ + +/** Create new variable scope. + * Create root variable scope. A variable scope contains one or more variables. + * Scopes can be nested, which allows variables in different scopes to have the + * same name. Variables from parent scopes will be shadowed by variables in + * child scopes with the same name. + * + * Use the `ecs_script_vars_push()` and `ecs_script_vars_pop()` functions to + * push and pop variable scopes. + * + * When a variable contains allocated resources (e.g. a string), its resources + * will be freed when `ecs_script_vars_pop()` is called on the scope, the + * ecs_script_vars_t::type_info field is initialized for the variable, and + * `ecs_type_info_t::hooks::dtor` is set. + * + * @param world The world. + */ +FLECS_API +ecs_script_vars_t* ecs_script_vars_init( + ecs_world_t *world); + +/** Free variable scope. + * Free root variable scope. The provided scope should not have a parent. This + * operation calls `ecs_script_vars_pop()` on the scope. + * + * @param vars The variable scope. + */ +FLECS_API +void ecs_script_vars_fini( + ecs_script_vars_t *vars); + +/** Push new variable scope. + * + * Scopes created with ecs_script_vars_push() must be cleaned up with + * ecs_script_vars_pop(). + * + * If the stack and allocator arguments are left to NULL, their values will be + * copied from the parent. + * + * @param parent The parent scope (provide NULL for root scope). + * @return The new variable scope. + */ +FLECS_API +ecs_script_vars_t* ecs_script_vars_push( + ecs_script_vars_t *parent); + +/** Pop variable scope. + * This frees up the resources for a variable scope. The scope must be at the + * top of a vars stack. Calling ecs_script_vars_pop() on a scope that is not the + * last scope causes undefined behavior. + * + * @param vars The scope to free. + * @return The parent scope. + */ +FLECS_API +ecs_script_vars_t* ecs_script_vars_pop( + ecs_script_vars_t *vars); + +/** Declare a variable. + * This operation declares a new variable in the current scope. If a variable + * with the specified name already exists, the operation will fail. + * + * This operation does not allocate storage for the variable. This is done to + * allow for variables that point to existing storage, which prevents having + * to copy existing values to a variable scope. + * + * @param vars The variable scope. + * @param name The variable name. + * @return The new variable, or NULL if the operation failed. + */ +FLECS_API +ecs_script_var_t* ecs_script_vars_declare( + ecs_script_vars_t *vars, + const char *name); + +/** Define a variable. + * This operation calls `ecs_script_vars_declare()` and allocates storage for + * the variable. If the type has a ctor, it will be called on the new storage. + * + * The scope's stack allocator will be used to allocate the storage. After + * `ecs_script_vars_pop()` is called on the scope, the variable storage will no + * longer be valid. + * + * The operation will fail if the type argument is not a type. + * + * @param vars The variable scope. + * @param name The variable name. + * @param type The variable type. + * @return The new variable, or NULL if the operation failed. + */ +FLECS_API +ecs_script_var_t* ecs_script_vars_define_id( + ecs_script_vars_t *vars, + const char *name, + ecs_entity_t type); + +#define ecs_script_vars_define(vars, name, type)\ + ecs_script_vars_define_id(vars, name, ecs_id(type)) + +/** Lookup a variable. + * This operation looks up a variable in the current scope. If the variable + * can't be found in the current scope, the operation will recursively search + * the parent scopes. + * + * @param vars The variable scope. + * @param name The variable name. + * @return The variable, or NULL if one with the provided name does not exist. + */ +FLECS_API +ecs_script_var_t* ecs_script_vars_lookup( + const ecs_script_vars_t *vars, + const char *name); + +/** Convert iterator to vars + * This operation converts an iterator to a variable array. This allows for + * using iterator results in expressions. The operation only converts a + * single result at a time, and does not progress the iterator. + * + * Iterator fields with data will be made available as variables with as name + * the field index (e.g. "$1"). The operation does not check if reflection data + * is registered for a field type. If no reflection data is registered for the + * type, using the field variable in expressions will fail. + * + * Field variables will only contain single elements, even if the iterator + * returns component arrays. The offset parameter can be used to specify which + * element in the component arrays to return. The offset parameter must be + * smaller than it->count. + * + * The operation will create a variable for query variables that contain a + * single entity. + * + * The operation will attempt to use existing variables. If a variable does not + * yet exist, the operation will create it. If an existing variable exists with + * a mismatching type, the operation will fail. + * + * Accessing variables after progressing the iterator or after the iterator is + * destroyed will result in undefined behavior. + * + * If vars contains a variable that is not present in the iterator, the variable + * will not be modified. + * + * @param it The iterator to convert to variables. + * @param vars The variables to write to. + * @param offset The offset to the current element. + */ +FLECS_API +void ecs_script_vars_from_iter( + const ecs_iter_t *it, + ecs_script_vars_t *vars, + int offset); + + +/* Standalone expression evaluation */ + +/** Used with ecs_script_expr_run(). */ +typedef struct ecs_script_expr_run_desc_t { + const char *name; + const char *expr; + ecs_entity_t (*lookup_action)( + const ecs_world_t*, + const char *value, + void *ctx); + void *lookup_ctx; + ecs_script_vars_t *vars; +} ecs_script_expr_run_desc_t; + +/** Parse standalone expression into value. + * This operation parses a flecs expression into the provided pointer. The + * memory pointed to must be large enough to contain a value of the used type. + * + * If no type and pointer are provided for the value argument, the operation + * will discover the type from the expression and allocate storage for the + * value. The allocated value must be freed with ecs_value_free(). + * + * @param world The world. + * @param ptr The pointer to the expression to parse. + * @param value The value containing type & pointer to write to. + * @param desc Configuration parameters for deserializer. + * @return Pointer to the character after the last one read, or NULL if failed. + */ +FLECS_API +const char* ecs_script_expr_run( + ecs_world_t *world, + const char *ptr, + ecs_value_t *value, + const ecs_script_expr_run_desc_t *desc); + +/** Evaluate interpolated expressions in string. + * This operation evaluates expressions in a string, and replaces them with + * their evaluated result. Supported expression formats are: + * - $variable_name + * - {expression} + * + * The $, { and } characters can be escaped with a backslash (\). + * + * @param world The world. + * @param str The string to evaluate. + * @param vars The variables to use for evaluation. + */ +FLECS_API +char* ecs_script_string_interpolate( + ecs_world_t *world, + const char *str, + const ecs_script_vars_t *vars); + + +/* Value serialization */ + +/** Serialize value into expression string. + * This operation serializes a value of the provided type to a string. The + * memory pointed to must be large enough to contain a value of the used type. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @return String with expression, or NULL if failed. + */ +FLECS_API +char* ecs_ptr_to_expr( + const ecs_world_t *world, + ecs_entity_t type, + const void *data); + +/** Serialize value into expression buffer. + * Same as ecs_ptr_to_expr(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @param buf The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_ptr_to_expr_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *data, + ecs_strbuf_t *buf); + +/** Similar as ecs_ptr_to_expr(), but serializes values to string. + * Whereas the output of ecs_ptr_to_expr() is a valid expression, the output of + * ecs_ptr_to_str() is a string representation of the value. In most cases the + * output of the two operations is the same, but there are some differences: + * - Strings are not quoted + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @return String with result, or NULL if failed. + */ +FLECS_API +char* ecs_ptr_to_str( + const ecs_world_t *world, + ecs_entity_t type, + const void *data); + +/** Serialize value into string buffer. + * Same as ecs_ptr_to_str(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @param buf The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_ptr_to_str_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *data, + ecs_strbuf_t *buf); + +/** Script module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsScript) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsScriptImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_DOC +#ifdef FLECS_NO_DOC +#error "FLECS_NO_DOC failed: DOC is required by other addons" +#endif +/** + * @file addons/doc.h + * @brief Doc module. + * + * The doc module allows for documenting entities (and thus components, systems) + * by adding brief and/or detailed descriptions as components. Documentation + * added with the doc module can be retrieved at runtime, and can be used by + * tooling such as UIs or documentation frameworks. + */ + +#ifdef FLECS_DOC + +#ifndef FLECS_DOC_H +#define FLECS_DOC_H + +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup c_addons_doc Doc + * @ingroup c_addons + * Utilities for documenting entities, components and systems. + * + * @{ + */ + +FLECS_API extern const ecs_entity_t ecs_id(EcsDocDescription); /**< Component id for EcsDocDescription. */ + +/** Tag for adding a UUID to entities. + * Added to an entity as (EcsDocDescription, EcsUuid) by ecs_doc_set_uuid(). + */ +FLECS_API extern const ecs_entity_t EcsDocUuid; + +/** Tag for adding brief descriptions to entities. + * Added to an entity as (EcsDocDescription, EcsBrief) by ecs_doc_set_brief(). + */ +FLECS_API extern const ecs_entity_t EcsDocBrief; + +/** Tag for adding detailed descriptions to entities. + * Added to an entity as (EcsDocDescription, EcsDocDetail) by ecs_doc_set_detail(). + */ +FLECS_API extern const ecs_entity_t EcsDocDetail; + +/** Tag for adding a link to entities. + * Added to an entity as (EcsDocDescription, EcsDocLink) by ecs_doc_set_link(). + */ +FLECS_API extern const ecs_entity_t EcsDocLink; + +/** Tag for adding a color to entities. + * Added to an entity as (EcsDocDescription, EcsDocColor) by ecs_doc_set_link(). + */ +FLECS_API extern const ecs_entity_t EcsDocColor; + +/** Component that stores description. + * Used as pair together with the following tags to store entity documentation: + * - EcsName + * - EcsDocBrief + * - EcsDocDetail + * - EcsDocLink + * - EcsDocColor + */ +typedef struct EcsDocDescription { + char *value; +} EcsDocDescription; + +/** Add UUID to entity. + * Associate entity with an (external) UUID. + * + * @param world The world. + * @param entity The entity to which to add the UUID. + * @param uuid The UUID to add. + * + * @see ecs_doc_get_uuid() + * @see flecs::doc::set_uuid() + * @see flecs::entity_builder::set_doc_uuid() + */ +FLECS_API +void ecs_doc_set_uuid( + ecs_world_t *world, + ecs_entity_t entity, + const char *uuid); + +/** Add human-readable name to entity. + * Contrary to entity names, human readable names do not have to be unique and + * can contain special characters used in the query language like '*'. + * + * @param world The world. + * @param entity The entity to which to add the name. + * @param name The name to add. + * + * @see ecs_doc_get_name() + * @see flecs::doc::set_name() + * @see flecs::entity_builder::set_doc_name() + */ +FLECS_API +void ecs_doc_set_name( + ecs_world_t *world, + ecs_entity_t entity, + const char *name); + +/** Add brief description to entity. + * + * @param world The world. + * @param entity The entity to which to add the description. + * @param description The description to add. + * + * @see ecs_doc_get_brief() + * @see flecs::doc::set_brief() + * @see flecs::entity_builder::set_doc_brief() + */ +FLECS_API +void ecs_doc_set_brief( + ecs_world_t *world, + ecs_entity_t entity, + const char *description); + +/** Add detailed description to entity. + * + * @param world The world. + * @param entity The entity to which to add the description. + * @param description The description to add. + * + * @see ecs_doc_get_detail() + * @see flecs::doc::set_detail() + * @see flecs::entity_builder::set_doc_detail() + */ +FLECS_API +void ecs_doc_set_detail( + ecs_world_t *world, + ecs_entity_t entity, + const char *description); + +/** Add link to external documentation to entity. + * + * @param world The world. + * @param entity The entity to which to add the link. + * @param link The link to add. + * + * @see ecs_doc_get_link() + * @see flecs::doc::set_link() + * @see flecs::entity_builder::set_doc_link() + */ +FLECS_API +void ecs_doc_set_link( + ecs_world_t *world, + ecs_entity_t entity, + const char *link); + +/** Add color to entity. + * UIs can use color as hint to improve visualizing entities. + * + * @param world The world. + * @param entity The entity to which to add the link. + * @param color The color to add. + * + * @see ecs_doc_get_color() + * @see flecs::doc::set_color() + * @see flecs::entity_builder::set_doc_color() + */ +FLECS_API +void ecs_doc_set_color( + ecs_world_t *world, + ecs_entity_t entity, + const char *color); + +/** Get UUID from entity. + * @param world The world. + * @param entity The entity from which to get the UUID. + * @return The UUID. + * + * @see ecs_doc_set_uuid() + * @see flecs::doc::get_uuid() + * @see flecs::entity_view::get_doc_uuid() + */ +FLECS_API +const char* ecs_doc_get_uuid( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Get human readable name from entity. + * If entity does not have an explicit human readable name, this operation will + * return the entity name. + * + * To test if an entity has a human readable name, use: + * + * @code + * ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsName); + * @endcode + * + * Or in C++: + * + * @code + * e.has(flecs::Name); + * @endcode + * + * @param world The world. + * @param entity The entity from which to get the name. + * @return The name. + * + * @see ecs_doc_set_name() + * @see flecs::doc::get_name() + * @see flecs::entity_view::get_doc_name() + */ +FLECS_API +const char* ecs_doc_get_name( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Get brief description from entity. + * + * @param world The world. + * @param entity The entity from which to get the description. + * @return The description. + * + * @see ecs_doc_set_brief() + * @see flecs::doc::get_brief() + * @see flecs::entity_view::get_doc_brief() + */ +FLECS_API +const char* ecs_doc_get_brief( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Get detailed description from entity. + * + * @param world The world. + * @param entity The entity from which to get the description. + * @return The description. + * + * @see ecs_doc_set_detail() + * @see flecs::doc::get_detail() + * @see flecs::entity_view::get_doc_detail() + */ +FLECS_API +const char* ecs_doc_get_detail( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Get link to external documentation from entity. + * + * @param world The world. + * @param entity The entity from which to get the link. + * @return The link. + * + * @see ecs_doc_set_link() + * @see flecs::doc::get_link() + * @see flecs::entity_view::get_doc_link() + */ +FLECS_API +const char* ecs_doc_get_link( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Get color from entity. + * + * @param world The world. + * @param entity The entity from which to get the color. + * @return The color. + * + * @see ecs_doc_set_color() + * @see flecs::doc::get_color() + * @see flecs::entity_view::get_doc_color() + */ +FLECS_API +const char* ecs_doc_get_color( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Doc module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsDoc) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsDocImport( + ecs_world_t *world); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +#endif + +#ifdef FLECS_META +#ifdef FLECS_NO_META +#error "FLECS_NO_META failed: META is required by other addons" +#endif +/** + * @file addons/meta.h + * @brief Meta addon. + * + * The meta addon enables reflecting on component data. Types are stored as + * entities, with components that store the reflection data. A type has at least + * two components: + * + * - EcsComponent: core component, contains size & alignment + * - EcsType: component that indicates what kind of type the entity is + * + * Additionally the type may have an additional component that contains the + * reflection data for the type. For example, structs have these components: + * + * - EcsComponent + * - EcsType + * - EcsStruct + * + * Structs can be populated by adding child entities with the EcsMember + * component. Adding a child with a Member component to an entity will + * automatically add the EcsStruct component to the parent. + * + * Enums/bitmasks can be populated by adding child entities with the Constant + * tag. By default constants are automatically assigned values when they are + * added to the enum/bitmask. The parent entity must have the EcsEnum or + * EcsBitmask component before adding the constants. + * + * To create enum constants with a manual value, set (Constant, i32) to the + * desired value. To create bitmask constants with a manual value, set + * (Constant, u32) to the desired value. Constants with manual values should not + * conflict with other constants. + * + * The _init APIs are convenience wrappers around creating the entities and + * components for the types. + * + * When a type is created it automatically receives the EcsComponent and + * EcsType components. The former means that the resulting type can be + * used as a regular component: + * + * @code + * // Create Position type + * ecs_entity_t pos = ecs_struct_init(world, &(ecs_struct_desc_t){ + * .entity.name = "Position", + * .members = { + * {"x", ecs_id(ecs_f32_t)}, + * {"y", ecs_id(ecs_f32_t)} + * } + * }); + * + * // Create entity with Position component + * ecs_entity_t e = ecs_new_w_id(world, pos); + * @endcode + * + * Type entities do not have to be named. + */ + +#ifdef FLECS_META + +/** + * @defgroup c_addons_meta Meta + * @ingroup c_addons + * Flecs reflection framework. + * + * @{ + */ + +#include + +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif + +#ifndef FLECS_META_H +#define FLECS_META_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Max number of constants/members that can be specified in desc structs. */ +#define ECS_MEMBER_DESC_CACHE_SIZE (32) + +/** Primitive type definitions. + * These typedefs allow the builtin primitives to be used as regular components: + * + * @code + * ecs_set(world, e, ecs_i32_t, {10}); + * @endcode + * + * Or a more useful example (create an enum constant with a manual value): + * + * @code + * ecs_set_pair_second(world, e, EcsConstant, ecs_i32_t, {10}); + * @endcode + */ + +typedef bool ecs_bool_t; /**< Builtin bool type */ +typedef char ecs_char_t; /**< Builtin char type */ +typedef unsigned char ecs_byte_t; /**< Builtin ecs_byte type */ +typedef uint8_t ecs_u8_t; /**< Builtin u8 type */ +typedef uint16_t ecs_u16_t; /**< Builtin u16 type */ +typedef uint32_t ecs_u32_t; /**< Builtin u32 type */ +typedef uint64_t ecs_u64_t; /**< Builtin u64 type */ +typedef uintptr_t ecs_uptr_t; /**< Builtin uptr type */ +typedef int8_t ecs_i8_t; /**< Builtin i8 type */ +typedef int16_t ecs_i16_t; /**< Builtin i16 type */ +typedef int32_t ecs_i32_t; /**< Builtin i32 type */ +typedef int64_t ecs_i64_t; /**< Builtin i64 type */ +typedef intptr_t ecs_iptr_t; /**< Builtin iptr type */ +typedef float ecs_f32_t; /**< Builtin f32 type */ +typedef double ecs_f64_t; /**< Builtin f64 type */ +typedef char* ecs_string_t; /**< Builtin string type */ + +/* Meta module component ids */ +FLECS_API extern const ecs_entity_t ecs_id(EcsType); /**< Id for component added to all types with reflection data. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsTypeSerializer); /**< Id for component that stores a type specific serializer. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsPrimitive); /**< Id for component that stores reflection data for a primitive type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsEnum); /**< Id for component that stores reflection data for an enum type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsBitmask); /**< Id for component that stores reflection data for a bitmask type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsMember); /**< Id for component that stores reflection data for struct members. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsMemberRanges); /**< Id for component that stores min/max ranges for member values. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsStruct); /**< Id for component that stores reflection data for a struct type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsArray); /**< Id for component that stores reflection data for an array type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsVector); /**< Id for component that stores reflection data for a vector type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsOpaque); /**< Id for component that stores reflection data for an opaque type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsUnit); /**< Id for component that stores unit data. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsUnitPrefix); /**< Id for component that stores unit prefix data. */ +FLECS_API extern const ecs_entity_t EcsConstant; /**< Tag added to enum/bitmask constants. */ +FLECS_API extern const ecs_entity_t EcsQuantity; /**< Tag added to unit quantities. */ + +/* Primitive type component ids */ + +FLECS_API extern const ecs_entity_t ecs_id(ecs_bool_t); /**< Builtin boolean type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_char_t); /**< Builtin char type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_byte_t); /**< Builtin byte type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u8_t); /**< Builtin 8 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u16_t); /**< Builtin 16 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u32_t); /**< Builtin 32 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u64_t); /**< Builtin 64 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_uptr_t); /**< Builtin pointer sized unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i8_t); /**< Builtin 8 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i16_t); /**< Builtin 16 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i32_t); /**< Builtin 32 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i64_t); /**< Builtin 64 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_iptr_t); /**< Builtin pointer sized signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_f32_t); /**< Builtin 32 bit floating point type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_f64_t); /**< Builtin 64 bit floating point type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_string_t); /**< Builtin string type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_entity_t); /**< Builtin entity type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_id_t); /**< Builtin (component) id type. */ + +/** Type kinds supported by meta addon */ +typedef enum ecs_type_kind_t { + EcsPrimitiveType, + EcsBitmaskType, + EcsEnumType, + EcsStructType, + EcsArrayType, + EcsVectorType, + EcsOpaqueType, + EcsTypeKindLast = EcsOpaqueType +} ecs_type_kind_t; + +/** Component that is automatically added to every type with the right kind. */ +typedef struct EcsType { + ecs_type_kind_t kind; /**< Type kind. */ + bool existing; /**< Did the type exist or is it populated from reflection */ + bool partial; /**< Is the reflection data a partial type description */ +} EcsType; + +/** Primitive type kinds supported by meta addon */ +typedef enum ecs_primitive_kind_t { + EcsBool = 1, + EcsChar, + EcsByte, + EcsU8, + EcsU16, + EcsU32, + EcsU64, + EcsI8, + EcsI16, + EcsI32, + EcsI64, + EcsF32, + EcsF64, + EcsUPtr, + EcsIPtr, + EcsString, + EcsEntity, + EcsId, + EcsPrimitiveKindLast = EcsId +} ecs_primitive_kind_t; + +/** Component added to primitive types */ +typedef struct EcsPrimitive { + ecs_primitive_kind_t kind; /**< Primitive type kind. */ +} EcsPrimitive; + +/** Component added to member entities */ +typedef struct EcsMember { + ecs_entity_t type; /**< Member type. */ + int32_t count; /**< Number of elements (for inline arrays). */ + ecs_entity_t unit; /**< Member unit. */ + int32_t offset; /**< Member offset. */ + bool use_offset; /**< If offset should be explicitly used. */ +} EcsMember; + +/** Type expressing a range for a member value */ +typedef struct ecs_member_value_range_t { + double min; /**< Min member value. */ + double max; /**< Max member value. */ +} ecs_member_value_range_t; + +/** Component added to member entities to express valid value ranges */ +typedef struct EcsMemberRanges { + ecs_member_value_range_t value; /**< Member value range. */ + ecs_member_value_range_t warning; /**< Member value warning range. */ + ecs_member_value_range_t error; /**< Member value error range. */ +} EcsMemberRanges; + +/** Element type of members vector in EcsStruct */ +typedef struct ecs_member_t { + /** Must be set when used with ecs_struct_desc_t */ + const char *name; + + /** Member type. */ + ecs_entity_t type; + + /** Element count (for inline arrays). May be set when used with ecs_struct_desc_t */ + int32_t count; + + /** May be set when used with ecs_struct_desc_t. Member offset. */ + int32_t offset; + + /** May be set when used with ecs_struct_desc_t, will be auto-populated if + * type entity is also a unit */ + ecs_entity_t unit; + + /** Set to true to prevent automatic offset computation. This option should + * be used when members are registered out of order or where calculation of + * member offsets doesn't match C type offsets. */ + bool use_offset; + + /** Numerical range that specifies which values member can assume. This + * range may be used by UI elements such as a progress bar or slider. The + * value of a member should not exceed this range. */ + ecs_member_value_range_t range; + + /** Numerical range outside of which the value represents an error. This + * range may be used by UI elements to style a value. */ + ecs_member_value_range_t error_range; + + /** Numerical range outside of which the value represents an warning. This + * range may be used by UI elements to style a value. */ + ecs_member_value_range_t warning_range; + + /** Should not be set by ecs_struct_desc_t */ + ecs_size_t size; + + /** Should not be set by ecs_struct_desc_t */ + ecs_entity_t member; +} ecs_member_t; + +/** Component added to struct type entities */ +typedef struct EcsStruct { + /** Populated from child entities with Member component */ + ecs_vec_t members; /* vector */ +} EcsStruct; + +/** Type that describes an enum constant */ +typedef struct ecs_enum_constant_t { + /** Must be set when used with ecs_enum_desc_t */ + const char *name; + + /** May be set when used with ecs_enum_desc_t */ + int32_t value; + + /** Should not be set by ecs_enum_desc_t */ + ecs_entity_t constant; +} ecs_enum_constant_t; + +/** Component added to enum type entities */ +typedef struct EcsEnum { + /** Populated from child entities with Constant component */ + ecs_map_t constants; /**< map */ +} EcsEnum; + +/** Type that describes an bitmask constant */ +typedef struct ecs_bitmask_constant_t { + /** Must be set when used with ecs_bitmask_desc_t */ + const char *name; + + /** May be set when used with ecs_bitmask_desc_t */ + ecs_flags32_t value; + + /** Should not be set by ecs_bitmask_desc_t */ + ecs_entity_t constant; +} ecs_bitmask_constant_t; + +/** Component added to bitmask type entities */ +typedef struct EcsBitmask { + /* Populated from child entities with Constant component */ + ecs_map_t constants; /**< map */ +} EcsBitmask; + +/** Component added to array type entities */ +typedef struct EcsArray { + ecs_entity_t type; /**< Element type */ + int32_t count; /**< Number of elements */ +} EcsArray; + +/** Component added to vector type entities */ +typedef struct EcsVector { + ecs_entity_t type; /**< Element type */ +} EcsVector; + + +/* Opaque type support */ + +#if !defined(__cplusplus) || !defined(FLECS_CPP) + +/** Serializer interface */ +typedef struct ecs_serializer_t { + /* Serialize value */ + int (*value)( + const struct ecs_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_serializer_t *ser, /**< Serializer */ + const char *member); /**< Member name */ + + const ecs_world_t *world; /**< The world. */ + void *ctx; /**< Serializer context. */ +} ecs_serializer_t; + +#elif defined(__cplusplus) + +} /* extern "C" { */ + +/** Serializer interface (same layout as C, but with convenience methods) */ +typedef struct ecs_serializer_t { + /* Serialize value */ + int (*value_)( + const struct ecs_serializer_t *ser, + ecs_entity_t type, + const void *value); + + /* Serialize member */ + int (*member_)( + const struct ecs_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_serializer_t; + +extern "C" { +#endif + +/** Callback invoked serializing an opaque type. */ +typedef int (*ecs_meta_serialize_t)( + const ecs_serializer_t *ser, + const void *src); /**< Pointer to value to serialize */ + +/** Opaque type reflection data. + * An opaque type is a type with an unknown layout that can be mapped to a type + * known to the reflection framework. See the opaque type reflection examples. + */ +typedef struct EcsOpaque { + ecs_entity_t as_type; /**< Type that describes the serialized output */ + ecs_meta_serialize_t serialize; /**< Serialize action */ + + /* Deserializer interface + * Only override the callbacks that are valid for the opaque type. If a + * deserializer attempts to assign a value type that is not supported by the + * interface, a conversion error is thrown. + */ + + /** Assign bool value */ + void (*assign_bool)( + void *dst, + bool value); + + /** Assign char value */ + void (*assign_char)( + void *dst, + char value); + + /** Assign int value */ + void (*assign_int)( + void *dst, + int64_t value); + + /** Assign unsigned int value */ + void (*assign_uint)( + void *dst, + uint64_t value); + + /** Assign float value */ + void (*assign_float)( + void *dst, + double value); + + /** Assign string value */ + void (*assign_string)( + void *dst, + const char *value); + + /** Assign entity value */ + void (*assign_entity)( + void *dst, + ecs_world_t *world, + ecs_entity_t entity); + + /** Assign (component) id value */ + void (*assign_id)( + void *dst, + ecs_world_t *world, + ecs_id_t id); + + /** Assign null value */ + void (*assign_null)( + void *dst); + + /** Clear collection elements */ + void (*clear)( + void *dst); + + /** Ensure & get collection element */ + void* (*ensure_element)( + void *dst, + size_t elem); + + /** Ensure & get element */ + void* (*ensure_member)( + void *dst, + const char *member); + + /** Return number of elements */ + size_t (*count)( + const void *dst); + + /** Resize to number of elements */ + void (*resize)( + void *dst, + size_t count); +} EcsOpaque; + + +/* Units */ + +/** Helper type to describe translation between two units. Note that this + * is not intended as a generic approach to unit conversions (e.g. from celsius + * to fahrenheit) but to translate between units that derive from the same base + * (e.g. meters to kilometers). + * + * Note that power is applied to the factor. When describing a translation of + * 1000, either use {factor = 1000, power = 1} or {factor = 1, power = 3}. */ +typedef struct ecs_unit_translation_t { + int32_t factor; /**< Factor to apply (e.g. "1000", "1000000", "1024") */ + int32_t power; /**< Power to apply to factor (e.g. "1", "3", "-9") */ +} ecs_unit_translation_t; + +/** Component that stores unit data. */ +typedef struct EcsUnit { + char *symbol; /**< Unit symbol. */ + ecs_entity_t prefix; /**< Order of magnitude prefix relative to derived */ + ecs_entity_t base; /**< Base unit (e.g. "meters") */ + ecs_entity_t over; /**< Over unit (e.g. "per second") */ + ecs_unit_translation_t translation; /**< Translation for derived unit */ +} EcsUnit; + +/** Component that stores unit prefix data. */ +typedef struct EcsUnitPrefix { + char *symbol; /**< Symbol of prefix (e.g. "K", "M", "Ki") */ + ecs_unit_translation_t translation; /**< Translation of prefix */ +} EcsUnitPrefix; + + +/* Serializer utilities */ + +/** Serializer instruction opcodes. + * The meta type serializer works by generating a flattened array with + * instructions that tells a serializer what kind of fields can be found in a + * type at which offsets. +*/ +typedef enum ecs_meta_type_op_kind_t { + EcsOpArray, + EcsOpVector, + EcsOpOpaque, + EcsOpPush, + EcsOpPop, + + EcsOpScope, /**< Marks last constant that can open/close a scope */ + + EcsOpEnum, + EcsOpBitmask, + + EcsOpPrimitive, /**< Marks first constant that's a primitive */ + + EcsOpBool, + EcsOpChar, + EcsOpByte, + EcsOpU8, + EcsOpU16, + EcsOpU32, + EcsOpU64, + EcsOpI8, + EcsOpI16, + EcsOpI32, + EcsOpI64, + EcsOpF32, + EcsOpF64, + EcsOpUPtr, + EcsOpIPtr, + EcsOpString, + EcsOpEntity, + EcsOpId, + EcsMetaTypeOpKindLast = EcsOpId +} ecs_meta_type_op_kind_t; + +/** Meta type serializer instruction data. */ +typedef struct ecs_meta_type_op_t { + ecs_meta_type_op_kind_t kind; /**< Instruction opcode. */ + ecs_size_t offset; /**< Offset of current field */ + int32_t count; /**< Number of elements (for inline arrays). */ + const char *name; /**< Name of value (only used for struct members) */ + int32_t op_count; /**< Number of operations until next field or end */ + ecs_size_t size; /**< Size of type of operation */ + ecs_entity_t type; /**< Type entity */ + int32_t member_index; /**< Index of member in struct */ + ecs_hashmap_t *members; /**< string -> member index (structs only) */ +} ecs_meta_type_op_t; + +/** Component that stores the type serializer. + * Added to all types with reflection data. + */ +typedef struct EcsTypeSerializer { + ecs_vec_t ops; /**< vector */ +} EcsTypeSerializer; + + +/* Deserializer utilities */ + +/** Maximum level of type nesting. + * >32 levels of nesting is not sane. + */ +#define ECS_META_MAX_SCOPE_DEPTH (32) + +/** Type with information about currently serialized scope. */ +typedef struct ecs_meta_scope_t { + ecs_entity_t type; /**< The type being iterated */ + ecs_meta_type_op_t *ops; /**< The type operations (see ecs_meta_type_op_t) */ + int32_t op_count; /**< Number of operations in ops array to process */ + int32_t op_cur; /**< Current operation */ + int32_t elem_cur; /**< Current element (for collections) */ + int32_t prev_depth; /**< Depth to restore, in case dotmember was used */ + void *ptr; /**< Pointer to the value being iterated */ + const EcsComponent *comp; /**< Pointer to component, in case size/alignment is needed */ + const EcsOpaque *opaque; /**< Opaque type interface */ + ecs_vec_t *vector; /**< Current vector, in case a vector is iterated */ + ecs_hashmap_t *members; /**< string -> member index */ + bool is_collection; /**< Is the scope iterating elements? */ + bool is_inline_array; /**< Is the scope iterating an inline array? */ + bool is_empty_scope; /**< Was scope populated (for collections) */ +} ecs_meta_scope_t; + +/** Type that enables iterating/populating a value using reflection data. */ +typedef struct ecs_meta_cursor_t { + const ecs_world_t *world; /**< The world. */ + ecs_meta_scope_t scope[ECS_META_MAX_SCOPE_DEPTH]; /**< Cursor scope stack. */ + int32_t depth; /**< Current scope depth. */ + bool valid; /**< Does the cursor point to a valid field. */ + bool is_primitive_scope; /**< If in root scope, this allows for a push for primitive types */ + + /** Custom entity lookup action for overriding default ecs_lookup */ + ecs_entity_t (*lookup_action)(const ecs_world_t*, const char*, void*); + void *lookup_ctx; /**< Context for lookup_action */ +} ecs_meta_cursor_t; + +/** Create meta cursor. + * A meta cursor allows for walking over, reading and writing a value without + * having to know its type at compile time. + * + * When a value is assigned through the cursor API, it will get converted to + * the actual value of the underlying type. This allows the underlying type to + * change without having to update the serialized data. For example, an integer + * field can be set by a string, a floating point can be set as integer etc. + * + * @param world The world. + * @param type The type of the value. + * @param ptr Pointer to the value. + * @return A meta cursor for the value. + */ +FLECS_API +ecs_meta_cursor_t ecs_meta_cursor( + const ecs_world_t *world, + ecs_entity_t type, + void *ptr); + +/** Get pointer to current field. + * + * @param cursor The cursor. + * @return A pointer to the current field. + */ +FLECS_API +void* ecs_meta_get_ptr( + ecs_meta_cursor_t *cursor); + +/** Move cursor to next field. + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_next( + ecs_meta_cursor_t *cursor); + +/** Move cursor to a field. + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_elem( + ecs_meta_cursor_t *cursor, + int32_t elem); + +/** Move cursor to member. + * + * @param cursor The cursor. + * @param name The name of the member. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_member( + ecs_meta_cursor_t *cursor, + const char *name); + +/** Move cursor to member. + * Same as ecs_meta_member(), but with support for "foo.bar" syntax. + * + * @param cursor The cursor. + * @param name The name of the member. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_dotmember( + ecs_meta_cursor_t *cursor, + const char *name); + +/** Push a scope (required/only valid for structs & collections). + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_push( + ecs_meta_cursor_t *cursor); + +/** Pop a struct or collection scope (must follow a push). + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_pop( + ecs_meta_cursor_t *cursor); + +/** Is the current scope a collection?. + * + * @param cursor The cursor. + * @return True if current scope is a collection, false if not. + */ +FLECS_API +bool ecs_meta_is_collection( + const ecs_meta_cursor_t *cursor); + +/** Get type of current field. + * + * @param cursor The cursor. + * @return The type of the current field. + */ +FLECS_API +ecs_entity_t ecs_meta_get_type( + const ecs_meta_cursor_t *cursor); + +/** Get unit of current field. + * + * @param cursor The cursor. + * @return The unit of the current field. + */ +FLECS_API +ecs_entity_t ecs_meta_get_unit( + const ecs_meta_cursor_t *cursor); + +/** Get member name of current field. + * + * @param cursor The cursor. + * @return The member name of the current field. + */ +FLECS_API +const char* ecs_meta_get_member( + const ecs_meta_cursor_t *cursor); + +/** Get member entity of current field. + * + * @param cursor The cursor. + * @return The member entity of the current field. + */ +FLECS_API +ecs_entity_t ecs_meta_get_member_id( + const ecs_meta_cursor_t *cursor); + +/* The set functions assign the field with the specified value. If the value + * does not have the same type as the field, it will be cased to the field type. + * If no valid conversion is available, the operation will fail. */ + +/** Set field with boolean value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_bool( + ecs_meta_cursor_t *cursor, + bool value); + +/** Set field with char value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_char( + ecs_meta_cursor_t *cursor, + char value); + +/** Set field with int value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_int( + ecs_meta_cursor_t *cursor, + int64_t value); + +/** Set field with uint value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_uint( + ecs_meta_cursor_t *cursor, + uint64_t value); + +/** Set field with float value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_float( + ecs_meta_cursor_t *cursor, + double value); + +/** Set field with string value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_string( + ecs_meta_cursor_t *cursor, + const char *value); + +/** Set field with string literal value (has enclosing ""). + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_string_literal( + ecs_meta_cursor_t *cursor, + const char *value); + +/** Set field with entity value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_entity( + ecs_meta_cursor_t *cursor, + ecs_entity_t value); + +/** Set field with (component) id value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_id( + ecs_meta_cursor_t *cursor, + ecs_id_t value); + +/** Set field with null value. + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_null( + ecs_meta_cursor_t *cursor); + +/** Set field with dynamic value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_value( + ecs_meta_cursor_t *cursor, + const ecs_value_t *value); + +/* Functions for getting members. */ + +/** Get field value as boolean. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +bool ecs_meta_get_bool( + const ecs_meta_cursor_t *cursor); + +/** Get field value as char. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +char ecs_meta_get_char( + const ecs_meta_cursor_t *cursor); + +/** Get field value as signed integer. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +int64_t ecs_meta_get_int( + const ecs_meta_cursor_t *cursor); + +/** Get field value as unsigned integer. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +uint64_t ecs_meta_get_uint( + const ecs_meta_cursor_t *cursor); + +/** Get field value as float. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +double ecs_meta_get_float( + const ecs_meta_cursor_t *cursor); + +/** Get field value as string. + * This operation does not perform conversions. If the field is not a string, + * this operation will fail. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +const char* ecs_meta_get_string( + const ecs_meta_cursor_t *cursor); + +/** Get field value as entity. + * This operation does not perform conversions. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +ecs_entity_t ecs_meta_get_entity( + const ecs_meta_cursor_t *cursor); + +/** Get field value as (component) id. + * This operation can convert from an entity. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +ecs_id_t ecs_meta_get_id( + const ecs_meta_cursor_t *cursor); + +/** Convert pointer of primitive kind to float. + * + * @param type_kind The primitive type kind of the value. + * @param ptr Pointer to a value of a primitive type. + * @return The value in floating point format. + */ +FLECS_API +double ecs_meta_ptr_to_float( + ecs_primitive_kind_t type_kind, + const void *ptr); + +/* API functions for creating meta types */ + +/** Used with ecs_primitive_init(). */ +typedef struct ecs_primitive_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_primitive_kind_t kind; /**< Primitive type kind. */ +} ecs_primitive_desc_t; + +/** Create a new primitive type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_primitive_init( + ecs_world_t *world, + const ecs_primitive_desc_t *desc); + + +/** Used with ecs_enum_init(). */ +typedef struct ecs_enum_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_enum_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Enum constants. */ +} ecs_enum_desc_t; + +/** Create a new enum type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_enum_init( + ecs_world_t *world, + const ecs_enum_desc_t *desc); + + +/** Used with ecs_bitmask_init(). */ +typedef struct ecs_bitmask_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_bitmask_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Bitmask constants. */ +} ecs_bitmask_desc_t; + +/** Create a new bitmask type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_bitmask_init( + ecs_world_t *world, + const ecs_bitmask_desc_t *desc); + + +/** Used with ecs_array_init(). */ +typedef struct ecs_array_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_entity_t type; /**< Element type. */ + int32_t count; /**< Number of elements. */ +} ecs_array_desc_t; + +/** Create a new array type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_array_init( + ecs_world_t *world, + const ecs_array_desc_t *desc); + + +/** Used with ecs_vector_init(). */ +typedef struct ecs_vector_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_entity_t type; /**< Element type. */ +} ecs_vector_desc_t; + +/** Create a new vector type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_vector_init( + ecs_world_t *world, + const ecs_vector_desc_t *desc); + + +/** Used with ecs_struct_init(). */ +typedef struct ecs_struct_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_member_t members[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Struct members. */ +} ecs_struct_desc_t; + +/** Create a new struct type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ +FLECS_API +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; /**< Existing entity to use for type (optional). */ + EcsOpaque type; /**< Type that the opaque type maps to. */ +} 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) + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ +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). */ + ecs_entity_t entity; + + /** Unit symbol, e.g. "m", "%", "g". (optional). */ + const char *symbol; + + /** Unit quantity, e.g. distance, percentage, weight. (optional). */ + ecs_entity_t quantity; + + /** Base unit, e.g. "meters" (optional). */ + ecs_entity_t base; + + /** Over unit, e.g. "per second" (optional). */ + ecs_entity_t over; + + /** Translation to apply to derived unit (optional). */ + ecs_unit_translation_t translation; + + /** Prefix indicating order of magnitude relative to the derived unit. If set + * together with "translation", the values must match. If translation is not + * set, setting prefix will auto-populate it. + * Additionally, setting the prefix will enforce that the symbol (if set) + * is consistent with the prefix symbol + symbol of the derived unit. If the + * symbol is not set, it will be auto populated. */ + ecs_entity_t prefix; +} ecs_unit_desc_t; + +/** Create a new unit. + * + * @param world The world. + * @param desc The unit descriptor. + * @return The new unit, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_unit_init( + ecs_world_t *world, + const ecs_unit_desc_t *desc); + + +/** Used with ecs_unit_prefix_init(). */ +typedef struct ecs_unit_prefix_desc_t { + /** Existing entity to associate with unit prefix (optional). */ + ecs_entity_t entity; + + /** Unit symbol, e.g. "m", "%", "g". (optional). */ + const char *symbol; + + /** Translation to apply to derived unit (optional). */ + ecs_unit_translation_t translation; +} ecs_unit_prefix_desc_t; + +/** Create a new unit prefix. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new unit prefix, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_unit_prefix_init( + ecs_world_t *world, + const ecs_unit_prefix_desc_t *desc); + + +/** Create a new quantity. + * + * @param world The world. + * @param desc The quantity descriptor. + * @return The new quantity, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_quantity_init( + ecs_world_t *world, + const ecs_entity_desc_t *desc); + +/* Convenience macros */ + +/** Create a primitive type. */ +#define ecs_primitive(world, ...)\ + ecs_primitive_init(world, &(ecs_primitive_desc_t) __VA_ARGS__ ) + +/** Create an enum type. */ +#define ecs_enum(world, ...)\ + ecs_enum_init(world, &(ecs_enum_desc_t) __VA_ARGS__ ) + +/** Create a bitmask type. */ +#define ecs_bitmask(world, ...)\ + ecs_bitmask_init(world, &(ecs_bitmask_desc_t) __VA_ARGS__ ) + +/** Create an array type. */ +#define ecs_array(world, ...)\ + ecs_array_init(world, &(ecs_array_desc_t) __VA_ARGS__ ) + +/** Create a vector type. */ +#define ecs_vector(world, ...)\ + ecs_vector_init(world, &(ecs_vector_desc_t) __VA_ARGS__ ) + +/** Create an opaque type. */ +#define ecs_opaque(world, ...)\ + ecs_opaque_init(world, &(ecs_opaque_desc_t) __VA_ARGS__ ) + +/** Create a struct type. */ +#define ecs_struct(world, ...)\ + ecs_struct_init(world, &(ecs_struct_desc_t) __VA_ARGS__ ) + +/** Create a unit. */ +#define ecs_unit(world, ...)\ + ecs_unit_init(world, &(ecs_unit_desc_t) __VA_ARGS__ ) + +/** Create a unit prefix. */ +#define ecs_unit_prefix(world, ...)\ + ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t) __VA_ARGS__ ) + +/** Create a unit quantity. */ +#define ecs_quantity(world, ...)\ + ecs_quantity_init(world, &(ecs_entity_desc_t) __VA_ARGS__ ) + + +/** Meta module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsMeta) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsMetaImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +/** + * @file addons/meta_c.h + * @brief Utility macros for populating reflection data in C. + */ + +#ifdef FLECS_META + +/** + * @defgroup c_addons_meta_c Meta Utilities + * @ingroup c_addons + * Macro utilities to automatically insert reflection data. + * + * @{ + */ + +#ifndef FLECS_META_C_H +#define FLECS_META_C_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro that controls behavior of API. Usually set in module header. When the + * macro is not defined, it defaults to IMPL. */ + +/* Define variables used by reflection utilities. This should only be defined + * by the module itself, not by the code importing the module */ +/* #define ECS_META_IMPL IMPL */ + +/* Don't define variables used by reflection utilities but still declare the + * variable for the component id. This enables the reflection utilities to be + * used for global component variables, even if no reflection is used. */ +/* #define ECS_META_IMPL DECLARE */ + +/* Don't define variables used by reflection utilities. This generates an extern + * variable for the component identifier. */ +/* #define ECS_META_IMPL EXTERN */ + +/** Declare component with descriptor. */ +#define ECS_META_COMPONENT(world, name)\ + ECS_COMPONENT_DEFINE(world, name);\ + ecs_meta_from_desc(world, ecs_id(name),\ + FLECS__##name##_kind, FLECS__##name##_desc) + +/** ECS_STRUCT(name, body). */ +#define ECS_STRUCT(name, ...)\ + ECS_META_IMPL_CALL(ECS_STRUCT_, ECS_META_IMPL, name, #__VA_ARGS__);\ + ECS_STRUCT_TYPE(name, __VA_ARGS__) + +/** ECS_ENUM(name, body). */ +#define ECS_ENUM(name, ...)\ + ECS_META_IMPL_CALL(ECS_ENUM_, ECS_META_IMPL, name, #__VA_ARGS__);\ + ECS_ENUM_TYPE(name, __VA_ARGS__) + +/** ECS_BITMASK(name, body). */ +#define ECS_BITMASK(name, ...)\ + ECS_META_IMPL_CALL(ECS_BITMASK_, ECS_META_IMPL, name, #__VA_ARGS__);\ + ECS_ENUM_TYPE(name, __VA_ARGS__) + +/** Macro used to mark part of type for which no reflection data is created. */ +#define ECS_PRIVATE + +/** Populate meta information from type descriptor. */ +FLECS_API +int ecs_meta_from_desc( + ecs_world_t *world, + ecs_entity_t component, + ecs_type_kind_t kind, + const char *desc); + + +/** \cond + * Private utilities to switch between meta IMPL, DECLARE and EXTERN variants. + */ + +#define ECS_META_IMPL_CALL_INNER(base, impl, name, type_desc)\ + base ## impl(name, type_desc) + +#define ECS_META_IMPL_CALL(base, impl, name, type_desc)\ + ECS_META_IMPL_CALL_INNER(base, impl, name, type_desc) + +/* ECS_STRUCT implementation */ +#define ECS_STRUCT_TYPE(name, ...)\ + typedef struct __VA_ARGS__ name + +#define ECS_STRUCT_ECS_META_IMPL ECS_STRUCT_IMPL + +#define ECS_STRUCT_IMPL(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + static const char *FLECS__##name##_desc = type_desc;\ + static ecs_type_kind_t FLECS__##name##_kind = EcsStructType;\ + ECS_COMPONENT_DECLARE(name) = 0 + +#define ECS_STRUCT_DECLARE(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + ECS_COMPONENT_DECLARE(name) = 0 + +#define ECS_STRUCT_EXTERN(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name) + + +/* ECS_ENUM implementation */ +#define ECS_ENUM_TYPE(name, ...)\ + typedef enum __VA_ARGS__ name + +#define ECS_ENUM_ECS_META_IMPL ECS_ENUM_IMPL + +#define ECS_ENUM_IMPL(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + static const char *FLECS__##name##_desc = type_desc;\ + static ecs_type_kind_t FLECS__##name##_kind = EcsEnumType;\ + ECS_COMPONENT_DECLARE(name) = 0 + +#define ECS_ENUM_DECLARE(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + ECS_COMPONENT_DECLARE(name) = 0 + +#define ECS_ENUM_EXTERN(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name) + + +/* ECS_BITMASK implementation */ +#define ECS_BITMASK_TYPE(name, ...)\ + typedef enum __VA_ARGS__ name + +#define ECS_BITMASK_ECS_META_IMPL ECS_BITMASK_IMPL + +#define ECS_BITMASK_IMPL(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + static const char *FLECS__##name##_desc = type_desc;\ + static ecs_type_kind_t FLECS__##name##_kind = EcsBitmaskType;\ + ECS_COMPONENT_DECLARE(name) = 0 + +#define ECS_BITMASK_DECLARE(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + ECS_COMPONENT_DECLARE(name) = 0 + +#define ECS_BITMASK_EXTERN(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name) + +/** \endcond */ + +#ifdef __cplusplus +} +#endif + +#endif // FLECS_META_H + +/** @} */ + +#endif // FLECS_META + + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_OS_API_IMPL +#ifdef FLECS_NO_OS_API_IMPL +#error "FLECS_NO_OS_API_IMPL failed: OS_API_IMPL is required by other addons" +#endif +/** + * @file addons/os_api_impl.h + * @brief Default OS API implementation. + */ + +#ifdef FLECS_OS_API_IMPL + +/** + * @defgroup c_addons_os_api_impl OS API Implementation + * @ingroup c_addons + * Default implementation for OS API interface. + * + * @{ + */ + +#ifndef FLECS_OS_API_IMPL_H +#define FLECS_OS_API_IMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +FLECS_API +void ecs_set_os_api_impl(void); + +#ifdef __cplusplus +} +#endif + +#endif // FLECS_OS_API_IMPL_H + +/** @} */ + +#endif // FLECS_OS_API_IMPL + +#endif + +#ifdef FLECS_MODULE +#ifdef FLECS_NO_MODULE +#error "FLECS_NO_MODULE failed: MODULE is required by other addons" +#endif +/** + * @file addons/module.h + * @brief Module addon. + * + * The module addon allows for creating and importing modules. Flecs modules + * enable applications to organize components and systems into reusable units of + * code that can easily be across projects. + */ + +#ifdef FLECS_MODULE + +/** + * @defgroup c_addons_module Module + * @ingroup c_addons + * Modules organize components, systems and more in reusable units of code. + * + * @{ + */ + +#ifndef FLECS_MODULE_H +#define FLECS_MODULE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Import a module. + * This operation will load a modules and store the public module handles in the + * handles_out out parameter. The module name will be used to verify if the + * module was already loaded, in which case it won't be reimported. The name + * will be translated from PascalCase to an entity path (pascal.case) before the + * lookup occurs. + * + * Module contents will be stored as children of the module entity. This + * prevents modules from accidentally defining conflicting identifiers. This is + * enforced by setting the scope before and after loading the module to the + * module entity id. + * + * A more convenient way to import a module is by using the ECS_IMPORT macro. + * + * @param world The world. + * @param module The module import function. + * @param module_name The name of the module. + * @return The module entity. + */ +FLECS_API +ecs_entity_t ecs_import( + ecs_world_t *world, + ecs_module_action_t module, + const char *module_name); + +/** Same as ecs_import(), but with name to scope conversion. + * PascalCase names are automatically converted to scoped names. + * + * @param world The world. + * @param module The module import function. + * @param module_name_c The name of the module. + * @return The module entity. + */ +FLECS_API +ecs_entity_t ecs_import_c( + ecs_world_t *world, + ecs_module_action_t module, + const char *module_name_c); + +/** Import a module from a library. + * Similar to ecs_import(), except that this operation will attempt to load the + * module from a dynamic library. + * + * A library may contain multiple modules, which is why both a library name and + * a module name need to be provided. If only a library name is provided, the + * library name will be reused for the module name. + * + * The library will be looked up using a canonical name, which is in the same + * form as a module, like `flecs.components.transform`. To transform this + * identifier to a platform specific library name, the operation relies on the + * module_to_dl callback of the os_api which the application has to override if + * the default does not yield the correct library name. + * + * @param world The world. + * @param library_name The name of the library to load. + * @param module_name The name of the module to load. + */ +FLECS_API +ecs_entity_t ecs_import_from_library( + ecs_world_t *world, + const char *library_name, + const char *module_name); + +/** Register a new module. */ +FLECS_API +ecs_entity_t ecs_module_init( + ecs_world_t *world, + const char *c_name, + const ecs_component_desc_t *desc); + +/** Define module. */ +#define ECS_MODULE_DEFINE(world, id)\ + {\ + ecs_component_desc_t desc = {0};\ + desc.entity = ecs_id(id);\ + ecs_id(id) = ecs_module_init(world, #id, &desc);\ + ecs_set_scope(world, ecs_id(id));\ + } + +/** Create a module. */ +#define ECS_MODULE(world, id)\ + ecs_entity_t ecs_id(id) = 0; ECS_MODULE_DEFINE(world, id)\ + (void)ecs_id(id) + +/** Wrapper around ecs_import(). + * This macro provides a convenient way to load a module with the world. It can + * be used like this: + * + * @code + * ECS_IMPORT(world, FlecsSystemsPhysics); + * @endcode + */ +#define ECS_IMPORT(world, id) ecs_import_c(world, id##Import, #id) + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + +#ifdef FLECS_CPP +#ifdef FLECS_NO_CPP +#error "FLECS_NO_CPP failed: CPP is required by other addons" +#endif +/** + * @file addons/flecs_cpp.h + * @brief C++ utility functions + * + * This header contains utility functions that are accessible from both C and + * C++ code. These functions are not part of the public API and are not meant + * to be used directly by applications. + */ + +#ifdef FLECS_CPP + +#ifndef FLECS_CPP_H +#define FLECS_CPP_H + +#ifdef __cplusplus +extern "C" { +#endif + +// The functions in this file can be used from C or C++, but these macros are only relevant to C++. +#ifdef __cplusplus + +#if defined(__clang__) +#define ECS_FUNC_NAME_FRONT(type, name) ((sizeof(#type) + sizeof(" flecs::_::() [T = ") + sizeof(#name)) - 3u) +#define ECS_FUNC_NAME_BACK (sizeof("]") - 1u) +#define ECS_FUNC_NAME __PRETTY_FUNCTION__ +#elif defined(__GNUC__) +#define ECS_FUNC_NAME_FRONT(type, name) ((sizeof(#type) + sizeof(" flecs::_::() [with T = ") + sizeof(#name)) - 3u) +#define ECS_FUNC_NAME_BACK (sizeof("]") - 1u) +#define ECS_FUNC_NAME __PRETTY_FUNCTION__ +#elif defined(_WIN32) +#define ECS_FUNC_NAME_FRONT(type, name) ((sizeof(#type) + sizeof(" __cdecl flecs::_::<") + sizeof(#name)) - 3u) +#define ECS_FUNC_NAME_BACK (sizeof(">(void)") - 1u) +#define ECS_FUNC_NAME __FUNCSIG__ +#else +#error "implicit component registration not supported" +#endif + +#define ECS_FUNC_TYPE_LEN(type, name, str)\ + (flecs::string::length(str) - (ECS_FUNC_NAME_FRONT(type, name) + ECS_FUNC_NAME_BACK)) + +#endif + +FLECS_API +char* ecs_cpp_get_type_name( + char *type_name, + const char *func_name, + size_t len, + size_t front_len); + +FLECS_API +char* ecs_cpp_get_symbol_name( + char *symbol_name, + const char *type_name, + size_t len); + +FLECS_API +char* ecs_cpp_get_constant_name( + char *constant_name, + const char *func_name, + size_t len, + size_t back_len); + +FLECS_API +const char* ecs_cpp_trim_module( + ecs_world_t *world, + const char *type_name); + +FLECS_API +void ecs_cpp_component_validate( + ecs_world_t *world, + ecs_entity_t id, + const char *name, + const char *symbol, + size_t size, + size_t alignment, + bool implicit_name); + +FLECS_API +ecs_entity_t ecs_cpp_component_register( + ecs_world_t *world, + ecs_entity_t id, + const char *name, + const char *symbol, + ecs_size_t size, + ecs_size_t alignment, + bool implicit_name, + bool *existing_out); + +FLECS_API +ecs_entity_t ecs_cpp_component_register_explicit( + ecs_world_t *world, + ecs_entity_t s_id, + ecs_entity_t id, + const char *name, + const char *type_name, + const char *symbol, + size_t size, + size_t alignment, + bool is_component, + bool *existing_out); + +FLECS_API +void ecs_cpp_enum_init( + ecs_world_t *world, + ecs_entity_t id); + +FLECS_API +ecs_entity_t ecs_cpp_enum_constant_register( + ecs_world_t *world, + ecs_entity_t parent, + ecs_entity_t id, + const char *name, + int value); + +FLECS_API +int32_t ecs_cpp_reset_count_get(void); + +FLECS_API +int32_t ecs_cpp_reset_count_inc(void); + +#ifdef FLECS_META +FLECS_API +const ecs_member_t* ecs_cpp_last_member( + const ecs_world_t *world, + ecs_entity_t type); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // FLECS_CPP_H + +#endif // FLECS_CPP + + +#ifdef __cplusplus +/** + * @file addons/cpp/flecs.hpp + * @brief Flecs C++11 API. + */ + +#pragma once + +// STL includes +#include + +/** + * @defgroup cpp C++ API + * @{ + */ + +namespace flecs +{ + +struct world; +struct world_async_stage; +struct iter; +struct entity_view; +struct entity; +struct type; +struct table; +struct table_range; +struct untyped_component; + +template +struct component; + +template +struct ref; + +namespace _ +{ +template +struct type; + +template +struct each_delegate; + +} // namespace _ +} // namespace flecs + +// Types imported from C API +/** + * @file addons/cpp/c_types.hpp + * @brief Aliases for types/constants from C API + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_globals API Types & Globals + * @ingroup cpp_core + * Types & constants bridged from C API. + * + * @{ + */ + +using world_t = ecs_world_t; +using world_info_t = ecs_world_info_t; +using id_t = ecs_id_t; +using entity_t = ecs_entity_t; +using type_t = ecs_type_t; +using table_t = ecs_table_t; +using term_t = ecs_term_t; +using query_t = ecs_query_t; +using query_group_info_t = ecs_query_group_info_t; +using observer_t = ecs_observer_t; +using iter_t = ecs_iter_t; +using ref_t = ecs_ref_t; +using type_info_t = ecs_type_info_t; +using type_hooks_t = ecs_type_hooks_t; +using flags32_t = ecs_flags32_t; + +enum inout_kind_t { + InOutDefault = EcsInOutDefault, + InOutNone = EcsInOutNone, + InOutFilter = EcsInOutFilter, + InOut = EcsInOut, + In = EcsIn, + Out = EcsOut +}; + +enum oper_kind_t { + And = EcsAnd, + Or = EcsOr, + Not = EcsNot, + Optional = EcsOptional, + AndFrom = EcsAndFrom, + OrFrom = EcsOrFrom, + NotFrom = EcsNotFrom +}; + +enum query_cache_kind_t { + QueryCacheDefault = EcsQueryCacheDefault, + QueryCacheAuto = EcsQueryCacheAuto, + QueryCacheAll = EcsQueryCacheAll, + QueryCacheNone = EcsQueryCacheNone +}; + +/** Id bit flags */ +static const flecs::entity_t PAIR = ECS_PAIR; +static const flecs::entity_t AUTO_OVERRIDE = ECS_AUTO_OVERRIDE; +static const flecs::entity_t TOGGLE = ECS_TOGGLE; + +//////////////////////////////////////////////////////////////////////////////// +//// Builtin components and tags +//////////////////////////////////////////////////////////////////////////////// + +/* Builtin components */ +using Component = EcsComponent; +using Identifier = EcsIdentifier; +using Poly = EcsPoly; +using DefaultChildComponent = EcsDefaultChildComponent; + +/* Builtin tags */ +static const flecs::entity_t Query = EcsQuery; +static const flecs::entity_t Observer = EcsObserver; +static const flecs::entity_t Private = EcsPrivate; +static const flecs::entity_t Module = EcsModule; +static const flecs::entity_t Prefab = EcsPrefab; +static const flecs::entity_t Disabled = EcsDisabled; +static const flecs::entity_t Empty = EcsEmpty; +static const flecs::entity_t Monitor = EcsMonitor; +static const flecs::entity_t System = EcsSystem; +static const flecs::entity_t Pipeline = ecs_id(EcsPipeline); +static const flecs::entity_t Phase = EcsPhase; + +/* Builtin event tags */ +static const flecs::entity_t OnAdd = EcsOnAdd; +static const flecs::entity_t OnRemove = EcsOnRemove; +static const flecs::entity_t OnSet = EcsOnSet; +static const flecs::entity_t OnTableCreate = EcsOnTableCreate; +static const flecs::entity_t OnTableDelete = EcsOnTableDelete; + +/* Builtin term flags */ +static const uint64_t Self = EcsSelf; +static const uint64_t Up = EcsUp; +static const uint64_t Trav = EcsTrav; +static const uint64_t Cascade = EcsCascade; +static const uint64_t Desc = EcsDesc; +static const uint64_t IsVariable = EcsIsVariable; +static const uint64_t IsEntity = EcsIsEntity; +static const uint64_t IsName = EcsIsName; +static const uint64_t TraverseFlags = EcsTraverseFlags; +static const uint64_t TermRefFlags = EcsTermRefFlags; + +/* Builtin entity ids */ +static const flecs::entity_t Flecs = EcsFlecs; +static const flecs::entity_t FlecsCore = EcsFlecsCore; +static const flecs::entity_t World = EcsWorld; + +/* Component traits */ +static const flecs::entity_t Wildcard = EcsWildcard; +static const flecs::entity_t Any = EcsAny; +static const flecs::entity_t This = EcsThis; +static const flecs::entity_t Transitive = EcsTransitive; +static const flecs::entity_t Reflexive = EcsReflexive; +static const flecs::entity_t Final = EcsFinal; +static const flecs::entity_t PairIsTag = EcsPairIsTag; +static const flecs::entity_t Exclusive = EcsExclusive; +static const flecs::entity_t Acyclic = EcsAcyclic; +static const flecs::entity_t Traversable = EcsTraversable; +static const flecs::entity_t Symmetric = EcsSymmetric; +static const flecs::entity_t With = EcsWith; +static const flecs::entity_t OneOf = EcsOneOf; +static const flecs::entity_t Trait = EcsTrait; +static const flecs::entity_t Relationship = EcsRelationship; +static const flecs::entity_t Target = EcsTarget; +static const flecs::entity_t CanToggle = EcsCanToggle; + +/* OnInstantiate trait */ +static const flecs::entity_t OnInstantiate = EcsOnInstantiate; +static const flecs::entity_t Override = EcsOverride; +static const flecs::entity_t Inherit = EcsInherit; +static const flecs::entity_t DontInherit = EcsDontInherit; + +/* OnDelete/OnDeleteTarget traits */ +static const flecs::entity_t OnDelete = EcsOnDelete; +static const flecs::entity_t OnDeleteTarget = EcsOnDeleteTarget; +static const flecs::entity_t Remove = EcsRemove; +static const flecs::entity_t Delete = EcsDelete; +static const flecs::entity_t Panic = EcsPanic; + +/* Builtin relationships */ +static const flecs::entity_t IsA = EcsIsA; +static const flecs::entity_t ChildOf = EcsChildOf; +static const flecs::entity_t DependsOn = EcsDependsOn; +static const flecs::entity_t SlotOf = EcsSlotOf; + +/* Builtin identifiers */ +static const flecs::entity_t Name = EcsName; +static const flecs::entity_t Symbol = EcsSymbol; + +/* Storage */ +static const flecs::entity_t Sparse = EcsSparse; +static const flecs::entity_t Union = EcsUnion; + +/* Builtin predicates for comparing entity ids in queries. */ +static const flecs::entity_t PredEq = EcsPredEq; +static const flecs::entity_t PredMatch = EcsPredMatch; +static const flecs::entity_t PredLookup = EcsPredLookup; + +/* Builtin marker entities for query scopes */ +static const flecs::entity_t ScopeOpen = EcsScopeOpen; +static const flecs::entity_t ScopeClose = EcsScopeClose; + +/** @} */ + +} + + +// C++ utilities +/** + * @file addons/cpp/utils/utils.hpp + * @brief Flecs STL (FTL?) + * + * Flecs STL (FTL?) + * Minimalistic utilities that allow for STL like functionality without having + * to depend on the actual STL. + */ + +// Macros so that C++ new calls can allocate using ecs_os_api memory allocation functions +// Rationale: +// - Using macros here instead of a templated function bc clients might override ecs_os_malloc +// to contain extra debug info like source tracking location. Using a template function +// in that scenario would collapse all source location into said function vs. the +// actual call site +// - FLECS_PLACEMENT_NEW(): exists to remove any naked new calls/make it easy to identify any regressions +// by grepping for new/delete + +#define FLECS_PLACEMENT_NEW(_ptr, _type) ::new(flecs::_::placement_new_tag, _ptr) _type +#define FLECS_NEW(_type) FLECS_PLACEMENT_NEW(ecs_os_malloc(sizeof(_type)), _type) +#define FLECS_DELETE(_ptr) \ + do { \ + if (_ptr) { \ + flecs::_::destruct_obj(_ptr); \ + ecs_os_free(_ptr); \ + } \ + } while (false) + +/* Faster (compile time) alternatives to std::move / std::forward. From: + * https://www.foonathan.net/2020/09/move-forward/ + */ + +#define FLECS_MOV(...) \ + static_cast&&>(__VA_ARGS__) + +#define FLECS_FWD(...) \ + static_cast(__VA_ARGS__) + +namespace flecs +{ + +namespace _ +{ + +// Dummy Placement new tag to disambiguate from any other operator new overrides +struct placement_new_tag_t{}; +constexpr placement_new_tag_t placement_new_tag{}; +template inline void destruct_obj(Ty* _ptr) { _ptr->~Ty(); } +template inline void free_obj(Ty* _ptr) { + if (_ptr) { + destruct_obj(_ptr); + ecs_os_free(_ptr); + } +} + +} // namespace _ + +} // namespace flecs + +// Allows overriding flecs_static_assert, which is useful when testing +#ifndef flecs_static_assert +#define flecs_static_assert(cond, str) static_assert(cond, str) +#endif + +inline void* operator new(size_t, flecs::_::placement_new_tag_t, void* _ptr) noexcept { return _ptr; } +inline void operator delete(void*, flecs::_::placement_new_tag_t, void*) noexcept { } + +namespace flecs +{ + +// C++11/C++14 convenience template replacements + +template +using conditional_t = typename std::conditional::type; + +template +using decay_t = typename std::decay::type; + +template +using enable_if_t = typename std::enable_if::type; + +template +using remove_pointer_t = typename std::remove_pointer::type; + +template +using remove_reference_t = typename std::remove_reference::type; + +template +using underlying_type_t = typename std::underlying_type::type; + +using std::is_base_of; +using std::is_empty; +using std::is_const; +using std::is_pointer; +using std::is_reference; +using std::is_volatile; +using std::is_same; +using std::is_enum; + +// Determine constness even if T is a pointer type +template +using is_const_p = is_const< remove_pointer_t >; + +// Apply cv modifiers from source type to destination type +// (from: https://stackoverflow.com/questions/52559336/add-const-to-type-if-template-arg-is-const) +template +using transcribe_const_t = conditional_t::value, Dst const, Dst>; + +template +using transcribe_volatile_t = conditional_t::value, Dst volatile, Dst>; + +template +using transcribe_cv_t = transcribe_const_t< Src, transcribe_volatile_t< Src, Dst> >; + +template +using transcribe_pointer_t = conditional_t::value, Dst*, Dst>; + +template +using transcribe_cvp_t = transcribe_cv_t< Src, transcribe_pointer_t< Src, Dst> >; + + +// More convenience templates. The if_*_t templates use int as default type +// instead of void. This enables writing code that's a bit less cluttered when +// the templates are used in a template declaration: +// +// enable_if_t* = nullptr +// vs: +// if_t = 0 + +template +using if_t = enable_if_t; + +template +using if_not_t = enable_if_t; + +namespace _ +{ + +// Utility to prevent static assert from immediately triggering +template +struct always_false { + static const bool value = false; +}; + +} // namespace _ + +} // namespace flecs + +#include +/** + * @file addons/cpp/utils/array.hpp + * @brief Array class. + * + * Array class. Simple std::array like utility that is mostly there to aid + * template code where template expansion would lead to an array with size 0. + */ + +namespace flecs { + +template +struct array_iterator +{ + explicit array_iterator(T* value, int index) { + value_ = value; + index_ = index; + } + + bool operator!=(array_iterator const& other) const + { + return index_ != other.index_; + } + + T & operator*() const + { + return value_[index_]; + } + + array_iterator& operator++() + { + ++index_; + return *this; + } + +private: + T* value_; + int index_; +}; + +template +struct array final { }; + +template +struct array > final { + array() {}; + + array(const T (&elems)[Size]) { + int i = 0; + for (auto it = this->begin(); it != this->end(); ++ it) { + *it = elems[i ++]; + } + } + + T& operator[](int index) { + return array_[index]; + } + + T& operator[](size_t index) { + return array_[index]; + } + + array_iterator begin() { + return array_iterator(array_, 0); + } + + array_iterator end() { + return array_iterator(array_, Size); + } + + size_t size() { + return Size; + } + + T* ptr() { + return array_; + } + + template + void each(const Func& func) { + for (auto& elem : *this) { + func(elem); + } + } + +private: + T array_[Size]; +}; + +template +array to_array(const T (&elems)[Size]) { + return array(elems); +} + +// Specialized class for zero-sized array +template +struct array> final { + array() {}; + array(const T* (&elems)) { (void)elems; } + T operator[](size_t index) { ecs_os_abort(); (void)index; return T(); } + array_iterator begin() { return array_iterator(nullptr, 0); } + array_iterator end() { return array_iterator(nullptr, 0); } + + size_t size() { + return 0; + } + + T* ptr() { + return NULL; + } +}; + +} + +/** + * @file addons/cpp/utils/string.hpp + * @brief String utility that doesn't implicitly allocate memory. + */ + +namespace flecs { + +struct string_view; + +// This removes dependencies on std::string (and therefore STL) and allows the +// API to return allocated strings without incurring additional allocations when +// wrapping in an std::string. +struct string { + explicit string() + : str_(nullptr) + , const_str_("") + , length_(0) { } + + explicit string(char *str) + : str_(str) + , const_str_(str ? str : "") + , length_(str ? ecs_os_strlen(str) : 0) { } + + ~string() { + // If flecs is included in a binary but is not used, it is possible that + // the OS API is not initialized. Calling ecs_os_free in that case could + // crash the application during exit. However, if a string has been set + // flecs has been used, and OS API should have been initialized. + if (str_) { + ecs_os_free(str_); + } + } + + string(string&& str) noexcept { + ecs_os_free(str_); + str_ = str.str_; + const_str_ = str.const_str_; + length_ = str.length_; + str.str_ = nullptr; + } + + operator const char*() const { + return const_str_; + } + + string& operator=(string&& str) noexcept { + ecs_os_free(str_); + str_ = str.str_; + const_str_ = str.const_str_; + length_ = str.length_; + str.str_ = nullptr; + return *this; + } + + // Ban implicit copies/allocations + string& operator=(const string& str) = delete; + string(const string& str) = delete; + + bool operator==(const flecs::string& str) const { + if (str.const_str_ == const_str_) { + return true; + } + + if (!const_str_ || !str.const_str_) { + return false; + } + + if (str.length_ != length_) { + return false; + } + + return ecs_os_strcmp(str, const_str_) == 0; + } + + bool operator!=(const flecs::string& str) const { + return !(*this == str); + } + + bool operator==(const char *str) const { + if (const_str_ == str) { + return true; + } + + if (!const_str_ || !str) { + return false; + } + + return ecs_os_strcmp(str, const_str_) == 0; + } + + bool operator!=(const char *str) const { + return !(*this == str); + } + + const char* c_str() const { + return const_str_; + } + + std::size_t length() const { + return static_cast(length_); + } + + template + static constexpr size_t length( char const (&)[N] ) { + return N - 1; + } + + std::size_t size() const { + return length(); + } + + void clear() { + ecs_os_free(str_); + str_ = nullptr; + const_str_ = nullptr; + } + + bool contains(const char *substr) { + if (const_str_) { + return strstr(const_str_, substr) != nullptr; + } else { + return false; + } + } + +protected: + // Must be constructed through string_view. This allows for using the string + // class for both owned and non-owned strings, which can reduce allocations + // when code conditionally should store a literal or an owned string. + // Making this constructor private forces the code to explicitly create a + // string_view which emphasizes that the string won't be freed by the class. + string(const char *str) + : str_(nullptr) + , const_str_(str ? str : "") + , length_(str ? ecs_os_strlen(str) : 0) { } + + char *str_ = nullptr; + const char *const_str_; + ecs_size_t length_; +}; + +// For consistency, the API returns a string_view where it could have returned +// a const char*, so an application won't have to think about whether to call +// c_str() or not. The string_view is a thin wrapper around a string that forces +// the API to indicate explicitly when a string is owned or not. +struct string_view : string { + explicit string_view(const char *str) + : string(str) { } +}; + +} + +/** + * @file addons/cpp/utils/enum.hpp + * @brief Compile time enum reflection utilities. + * + * Discover at compile time valid enumeration constants for an enumeration type + * and their names. This is used to automatically register enum constants. + */ + +#include +#include + +#define FLECS_ENUM_MAX(T) _::to_constant::value +#define FLECS_ENUM_MAX_COUNT (FLECS_ENUM_MAX(int) + 1) + +#ifndef FLECS_CPP_ENUM_REFLECTION_SUPPORT +#if !defined(__clang__) && defined(__GNUC__) +#if __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 5) +#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1 +#else +#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 0 +#endif +#else +#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1 +#endif +#endif + +#if defined(__clang__) && __clang_major__ >= 16 +// https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 +#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v) +#elif defined(__GNUC__) && __GNUC__ > 10 +#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v) +#else +#define flecs_enum_cast(T, v) static_cast(v) +#endif + +namespace flecs { + +/** Int to enum */ +namespace _ { +template Value> +struct to_constant { + static constexpr E value = flecs_enum_cast(E, Value); +}; + +template Value> +constexpr E to_constant::value; +} + +/** Convenience type with enum reflection data */ +template +struct enum_data; + +template +static enum_data enum_type(flecs::world_t *world); + +template +struct enum_last { + static constexpr E value = FLECS_ENUM_MAX(E); +}; + +/* Utility macro to override enum_last trait */ +#define FLECS_ENUM_LAST(T, Last)\ + namespace flecs {\ + template<>\ + struct enum_last {\ + static constexpr T value = Last;\ + };\ + } + +namespace _ { + +#if INTPTR_MAX == INT64_MAX + #ifdef ECS_TARGET_MSVC + #if _MSC_VER >= 1929 + #define ECS_SIZE_T_STR "unsigned __int64" + #else + #define ECS_SIZE_T_STR "unsigned int" + #endif + #elif defined(__clang__) + #define ECS_SIZE_T_STR "size_t" + #else + #ifdef ECS_TARGET_WINDOWS + #define ECS_SIZE_T_STR "constexpr size_t; size_t = long long unsigned int" + #else + #define ECS_SIZE_T_STR "constexpr size_t; size_t = long unsigned int" + #endif + #endif +#else + #ifdef ECS_TARGET_MSVC + #if _MSC_VER >= 1929 + #define ECS_SIZE_T_STR "unsigned __int32" + #else + #define ECS_SIZE_T_STR "unsigned int" + #endif + #elif defined(__clang__) + #define ECS_SIZE_T_STR "size_t" + #else + #ifdef ECS_TARGET_WINDOWS + #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int" + #else + #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int" + #endif + #endif +#endif + +template +constexpr size_t enum_type_len() { + return ECS_FUNC_TYPE_LEN(, enum_type_len, ECS_FUNC_NAME) + - (sizeof(ECS_SIZE_T_STR) - 1u); +} + +/** Test if value is valid for enumeration. + * This function leverages that when a valid value is provided, + * __PRETTY_FUNCTION__ contains the enumeration name, whereas if a value is + * invalid, the string contains a number or a negative (-) symbol. */ +#if defined(ECS_TARGET_CLANG) +#if ECS_CLANG_VERSION < 13 +template +constexpr bool enum_constant_is_valid() { + return !(( + (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + + enum_type_len() + 6 /* ', C = ' */] >= '0') && + (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + + enum_type_len() + 6 /* ', C = ' */] <= '9')) || + (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + + enum_type_len() + 6 /* ', C = ' */] == '-')); +} +#else +template +constexpr bool enum_constant_is_valid() { + return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + + enum_type_len() + 6 /* ', E C = ' */] != '('); +} +#endif +#elif defined(ECS_TARGET_GNU) +template +constexpr bool enum_constant_is_valid() { + return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constexpr bool, enum_constant_is_valid) + + enum_type_len() + 8 /* ', E C = ' */] != '('); +} +#else +/* Use different trick on MSVC, since it uses hexadecimal representation for + * invalid enum constants. We can leverage that msvc inserts a C-style cast + * into the name, and the location of its first character ('(') is known. */ +template +constexpr bool enum_constant_is_valid() { + return ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + + enum_type_len() + 1] != '('; +} +#endif + +/* Without this wrapper __builtin_bit_cast doesn't work */ +template C> +constexpr bool enum_constant_is_valid_wrap() { + return enum_constant_is_valid(); +} + +template +struct enum_is_valid { + static constexpr bool value = enum_constant_is_valid(); +}; + +/** Extract name of constant from string */ +template +static const char* enum_constant_to_name() { + static const size_t len = ECS_FUNC_TYPE_LEN(const char*, enum_constant_to_name, ECS_FUNC_NAME); + static char result[len + 1] = {}; + return ecs_cpp_get_constant_name( + result, ECS_FUNC_NAME, string::length(ECS_FUNC_NAME), + ECS_FUNC_NAME_BACK); +} + +/** Enumeration constant data */ +template +struct enum_constant_data { + flecs::entity_t id; + T offset; +}; + +/** + * @brief Provides utilities for enum reflection. + * + * This struct provides static functions for enum reflection, including conversion + * between enum values and their underlying integral types, and iteration over enum + * values. + * + * @tparam E The enum type. + * @tparam Handler The handler for enum reflection operations. + */ +template +struct enum_reflection { + using U = underlying_type_t; + + /** + * @brief Iterates over the range [Low, High] of enum values between Low and High. + * + * Recursively divide and conquers the search space to reduce the template-depth. Once + * recursive division is complete, calls Handle::handle_constant in ascending order, + * passing the values computed up the chain. + * + * @tparam Low The lower bound of the search range, inclusive. + * @tparam High The upper bound of the search range, inclusive. + * @tparam Args Additional arguments to be passed through to Handler::handle_constant + * @param last_value The last value processed in the iteration. + * @param args Additional arguments to be passed through to Handler::handle_constant + * @return constexpr U The result of the iteration. + */ + template + static constexpr U each_enum_range(U last_value, Args... args) { + return High - Low <= 1 + ? High == Low + ? Handler::template handle_constant(last_value, args...) + : Handler::template handle_constant(Handler::template handle_constant(last_value, args...), args...) + : each_enum_range<(Low + High) / 2 + 1, High>( + each_enum_range(last_value, args...), + args... + ); + } + + /** + * @brief Iterates over the mask range (Low, High] of enum values between Low and High. + * + * Recursively iterates the search space, looking for enums defined as multiple-of-2 + * bitmasks. Each iteration, shifts bit to the right until it hits Low, then calls + * Handler::handle_constant for each bitmask in ascending order. + * + * @tparam Low The lower bound of the search range, not inclusive + * @tparam High The upper bound of the search range, inclusive. + * @tparam Args Additional arguments to be passed through to Handler::handle_constant + * @param last_value The last value processed in the iteration. + * @param args Additional arguments to be passed through to Handler::handle_constant + * @return constexpr U The result of the iteration. + */ + template + static constexpr U each_mask_range(U last_value, Args... args) { + // If Low shares any bits with Current Flag, or if High is less than/equal to Low (and High isn't negative because max-flag signed) + return (Low & High) || (High <= Low && High != high_bit) + ? last_value + : Handler::template handle_constant( + each_mask_range> 1) & ~high_bit)>(last_value, args...), + args... + ); + } + + /** + * @brief Handles enum iteration for gathering reflection data. + * + * Iterates over all enum values up to a specified maximum value + * (each_enum_range<0, Value>), then iterates the rest of the possible bitmasks + * (each_mask_range). + * + * @tparam Value The maximum enum value to iterate up to. + * @tparam Args Additional arguments to be passed through to Handler::handle_constant + * @param args Additional arguments to be passed through to Handler::handle_constant + * @return constexpr U The result of the iteration. + */ + template (FLECS_ENUM_MAX(E)), typename... Args> + static constexpr U each_enum(Args... args) { + return each_mask_range(each_enum_range<0, Value>(0, args...), args...); + } + + static const U high_bit = static_cast(1) << (sizeof(U) * 8 - 1); +}; + +/** Enumeration type data */ +template +struct enum_data_impl { +private: + using U = underlying_type_t; + + /** + * @brief Handler struct for generating compile-time count of enum constants. + */ + struct reflection_count { + template () > = 0> + static constexpr U handle_constant(U last_value) { + return last_value; + } + + template () > = 0> + static constexpr U handle_constant(U last_value) { + return 1 + last_value; + } + }; + +public: + flecs::entity_t id; + int min; + int max; + bool has_contiguous; + // If enum constants start not-sparse, contiguous_until will be the index of the first sparse value, or end of the constants array + U contiguous_until; + // Compile-time generated count of enum constants. + static constexpr unsigned int constants_size = enum_reflection::template each_enum< static_cast(enum_last::value) >(); + // Constants array is sized to the number of found-constants, or 1 (to avoid 0-sized array) + enum_constant_data constants[constants_size? constants_size: 1]; +}; + +/** Class that scans an enum for constants, extracts names & creates entities */ +template +struct enum_type { +private: + using U = underlying_type_t; + + /** + * @brief Helper struct for filling enum_type's static `enum_data_impl` member with reflection data. + * + * Because reflection occurs in-order, we can use current value/last value to determine continuity, and + * use that as a lookup heuristic later on. + */ + struct reflection_init { + template () > = 0> + static U handle_constant(U last_value, flecs::world_t*) { + // Search for constant failed. Pass last valid value through. + return last_value; + } + + template () > = 0> + static U handle_constant(U last_value, flecs::world_t *world) { + // Constant is valid, so fill reflection data. + auto v = Value; + const char *name = enum_constant_to_name(); + + ++enum_type::data.max; // Increment cursor as we build constants array. + + // If the enum was previously contiguous, and continues to be through the current value... + if (enum_type::data.has_contiguous && static_cast(enum_type::data.max) == v && enum_type::data.contiguous_until == v) { + ++enum_type::data.contiguous_until; + } + // else, if the enum was never contiguous and hasn't been set as not contiguous... + else if (!enum_type::data.contiguous_until && enum_type::data.has_contiguous) { + enum_type::data.has_contiguous = false; + } + + ecs_assert(!(last_value > 0 && v < std::numeric_limits::min() + last_value), ECS_UNSUPPORTED, + "Signed integer enums causes integer overflow when recording offset from high positive to" + " low negative. Consider using unsigned integers as underlying type."); + enum_type::data.constants[enum_type::data.max].offset = v - last_value; + enum_type::data.constants[enum_type::data.max].id = ecs_cpp_enum_constant_register( + world, enum_type::data.id, 0, name, static_cast(v)); + return v; + } + }; +public: + + static enum_data_impl data; + + static enum_type& get() { + static _::enum_type instance; + return instance; + } + + flecs::entity_t entity(E value) const { + int index = index_by_value(value); + if (index >= 0) { + return data.constants[index].id; + } + return 0; + } + + void init(flecs::world_t *world, flecs::entity_t id) { +#if !FLECS_CPP_ENUM_REFLECTION_SUPPORT + ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher") +#endif + // Initialize/reset reflection data values to default state. + data.min = 0; + data.max = -1; + data.has_contiguous = true; + data.contiguous_until = 0; + + ecs_log_push(); + ecs_cpp_enum_init(world, id); + data.id = id; + + // Generate reflection data + enum_reflection::template each_enum< static_cast(enum_last::value) >(world); + ecs_log_pop(); + } +}; + +template +enum_data_impl enum_type::data; + +template ::value > = 0> +inline static void init_enum(flecs::world_t *world, flecs::entity_t id) { + _::enum_type::get().init(world, id); +} + +template ::value > = 0> +inline static void init_enum(flecs::world_t*, flecs::entity_t) { } + +} // namespace _ + +/** Enumeration type data wrapper with world pointer */ +template +struct enum_data { + using U = underlying_type_t; + + enum_data(flecs::world_t *world, _::enum_data_impl& impl) + : world_(world) + , impl_(impl) { } + + /** + * @brief Checks if a given integral value is a valid enum value. + * + * @param value The integral value. + * @return true If the value is a valid enum value. + * @return false If the value is not a valid enum value. + */ + bool is_valid(U value) { + int index = index_by_value(value); + if (index < 0) { + return false; + } + return impl_.constants[index].id != 0; + } + + /** + * @brief Checks if a given enum value is valid. + * + * @param value The enum value. + * @return true If the value is valid. + * @return false If the value is not valid. + */ + bool is_valid(E value) { + return is_valid(static_cast(value)); + } + + /** + * @brief Finds the index into the constants array for a value, if one exists + * + * @param value The enum value. + * @return int The index of the enum value. + */ + int index_by_value(U value) const { + if (!impl_.max) { + return -1; + } + // Check if value is in contiguous lookup section + if (impl_.has_contiguous && value < impl_.contiguous_until && value >= 0) { + return static_cast(value); + } + U accumulator = impl_.contiguous_until? impl_.contiguous_until - 1: 0; + for (int i = static_cast(impl_.contiguous_until); i <= impl_.max; ++i) { + accumulator += impl_.constants[i].offset; + if (accumulator == value) { + return i; + } + } + return -1; + } + + /** + * @brief Finds the index into the constants array for an enum value, if one exists + * + * @param value The enum value. + * @return int The index of the enum value. + */ + int index_by_value(E value) const { + return index_by_value(static_cast(value)); + } + + int first() const { + return impl_.min; + } + + int last() const { + return impl_.max; + } + + int next(int cur) const { + return cur + 1; + } + + flecs::entity entity() const; + flecs::entity entity(U value) const; + flecs::entity entity(E value) const; + + flecs::world_t *world_; + _::enum_data_impl& impl_; +}; + +/** Convenience function for getting enum reflection data */ +template +enum_data enum_type(flecs::world_t *world) { + _::type::id(world); // Ensure enum is registered + auto& ref = _::enum_type::get(); + return enum_data(world, ref.data); +} + +} // namespace flecs + +/** + * @file addons/cpp/utils/stringstream.hpp + * @brief Wrapper around ecs_strbuf_t that provides a simple stringstream like API. + */ + +namespace flecs { + +struct stringstream { + explicit stringstream() + : buf_({}) { } + + ~stringstream() { + ecs_strbuf_reset(&buf_); + } + + stringstream(stringstream&& str) noexcept { + ecs_strbuf_reset(&buf_); + buf_ = str.buf_; + str.buf_ = {}; + } + + stringstream& operator=(stringstream&& str) noexcept { + ecs_strbuf_reset(&buf_); + buf_ = str.buf_; + str.buf_ = {}; + return *this; + } + + // Ban implicit copies/allocations + stringstream& operator=(const stringstream& str) = delete; + stringstream(const stringstream& str) = delete; + + stringstream& operator<<(const char* str) { + ecs_strbuf_appendstr(&buf_, str); + return *this; + } + + flecs::string str() { + return flecs::string(ecs_strbuf_get(&buf_)); + } + +private: + ecs_strbuf_t buf_; +}; + +} + +/** + * @file addons/cpp/utils/function_traits.hpp + * @brief Compile time utilities to inspect properties of functions. + * + * Code from: https://stackoverflow.com/questions/27024238/c-template-mechanism-to-get-the-number-of-function-arguments-which-would-work + */ + +namespace flecs { +namespace _ { + +template +struct arg_list { }; + +// Base type that contains the traits +template +struct function_traits_defs +{ + static constexpr bool is_callable = true; + static constexpr size_t arity = sizeof...(Args); + using return_type = ReturnType; + using args = arg_list; +}; + +// Primary template for function_traits_impl +template +struct function_traits_impl { + static constexpr bool is_callable = false; +}; + +// Template specializations for the different kinds of function types (whew) +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +template +struct function_traits_impl + : function_traits_defs {}; + +// Primary template for function_traits_no_cv. If T is not a function, the +// compiler will attempt to instantiate this template and fail, because its base +// is undefined. +template +struct function_traits_no_cv + : function_traits_impl {}; + +// Specialized template for function types +template +struct function_traits_no_cv + : function_traits_impl {}; + +// Front facing template that decays T before ripping it apart. +template +struct function_traits + : function_traits_no_cv< decay_t > {}; + +} // _ + + +template +struct is_callable { + static constexpr bool value = _::function_traits::is_callable; +}; + +template +struct arity { + static constexpr int value = _::function_traits::arity; +}; + +template +using return_type_t = typename _::function_traits::return_type; + +template +using arg_list_t = typename _::function_traits::args; + +// First arg +template +struct first_arg_impl; + +template +struct first_arg_impl > { + using type = T; +}; + +template +struct first_arg { + using type = typename first_arg_impl>::type; +}; + +template +using first_arg_t = typename first_arg::type; + +// Last arg +template +struct second_arg_impl; + +template +struct second_arg_impl > { + using type = T; +}; + +template +struct second_arg { + using type = typename second_arg_impl>::type; +}; + +template +using second_arg_t = typename second_arg::type; + +} // flecs + + + +// Mixin forward declarations +/** + * @file addons/cpp/mixins/id/decl.hpp + * @brief Id class. + */ + +#pragma once + +namespace flecs { + +struct id; +struct entity; + +/** + * @defgroup cpp_ids Ids + * @ingroup cpp_core + * Class for working with entity, component, tag and pair ids. + * + * @{ + */ + +/** Class that wraps around a flecs::id_t. + * A flecs id is an identifier that can be added to entities. Ids can be: + * - entities (including components, tags) + * - pair ids + * - entities with id flags set (like flecs::AUTO_OVERRIDE, flecs::TOGGLE) + */ +struct id { + id() + : world_(nullptr) + , id_(0) { } + + explicit id(flecs::id_t value) + : world_(nullptr) + , id_(value) { } + + explicit id(flecs::world_t *world, flecs::id_t value = 0) + : world_(world) + , id_(value) { } + + explicit id(flecs::world_t *world, flecs::id_t first, flecs::id_t second) + : world_(world) + , id_(ecs_pair(first, second)) { } + + explicit id(flecs::world_t *world, const char *expr) + : world_(world) + , id_(ecs_id_from_str(world, expr)) { } + + explicit id(flecs::id_t first, flecs::id_t second) + : world_(nullptr) + , id_(ecs_pair(first, second)) { } + + explicit id(const flecs::id& first, const flecs::id& second) + : world_(first.world_) + , id_(ecs_pair(first.id_, second.id_)) { } + + /** Test if id is pair (has first, second) */ + bool is_pair() const { + return (id_ & ECS_ID_FLAGS_MASK) == flecs::PAIR; + } + + /** Test if id is a wildcard */ + bool is_wildcard() const { + return ecs_id_is_wildcard(id_); + } + + /** Test if id is entity */ + bool is_entity() const { + return !(id_ & ECS_ID_FLAGS_MASK); + } + + /** Return id as entity (only allowed when id is valid entity) */ + flecs::entity entity() const; + + /** Return id with role added */ + flecs::entity add_flags(flecs::id_t flags) const; + + /** Return id with role removed */ + flecs::entity remove_flags(flecs::id_t flags) const; + + /** Return id without role */ + flecs::entity remove_flags() const; + + /** Return id without role */ + flecs::entity remove_generation() const; + + /** Return component type of id */ + flecs::entity type_id() const; + + /** Test if id has specified role */ + bool has_flags(flecs::id_t flags) const { + return ((id_ & flags) == flags); + } + + /** Test if id has any role */ + bool has_flags() const { + return (id_ & ECS_ID_FLAGS_MASK) != 0; + } + + /** Return id flags set on id */ + flecs::entity flags() const; + + /** Test if id has specified first */ + bool has_relation(flecs::id_t first) const { + if (!is_pair()) { + return false; + } + return ECS_PAIR_FIRST(id_) == first; + } + + /** Get first element from a pair. + * If the id is not a pair, this operation will fail. When the id has a + * world, the operation will ensure that the returned id has the correct + * generation count. */ + flecs::entity first() const; + + /** Get second element from a pair. + * If the id is not a pair, this operation will fail. When the id has a + * world, the operation will ensure that the returned id has the correct + * generation count. */ + flecs::entity second() const; + + /* Convert id to string */ + flecs::string str() const { + return flecs::string(ecs_id_str(world_, id_)); + } + + /** Convert role of id to string. */ + flecs::string flags_str() const { + return flecs::string_view( ecs_id_flag_str(id_ & ECS_ID_FLAGS_MASK)); + } + + /** Return flecs::id_t value */ + flecs::id_t raw_id() const { + return id_; + } + + operator flecs::id_t() const { + return id_; + } + + flecs::world world() const; + +protected: + /* World is optional, but guarantees that entity identifiers extracted from + * the id are valid */ + flecs::world_t *world_; + flecs::id_t id_; +}; + +/** @} */ + +} + +/** + * @file addons/cpp/mixins/term/decl.hpp + * @brief Term declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @ingroup cpp_core_queries + * + * @{ + */ + +struct term; +struct term_builder; + +/** @} */ + +} + +/** + * @file addons/cpp/mixins/query/decl.hpp + * @brief Query declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_core_queries Queries + * @ingroup cpp_core + * + * @{ + */ + +struct query_base; + +template +struct query; + +template +struct query_builder; + +/** @} */ + +} + +/** + * @file addons/cpp/mixins/event/decl.hpp + * @brief Event declarations. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/event/builder.hpp + * @brief Event builder. + */ + +#pragma once + +#define ECS_EVENT_DESC_ID_COUNT_MAX (8) + +namespace flecs { + +/** + * @ingroup cpp_addons_event + * @{ + */ + +/** Event builder interface */ +template +struct event_builder_base { + event_builder_base(flecs::world_t *world, flecs::entity_t event) + : world_(world) + , desc_{} + , ids_{} + , ids_array_{} + { + desc_.event = event; + } + + /** Add component to emit for */ + template + Base& id() { + ids_.array = ids_array_; + ids_.array[ids_.count] = _::type().id(world_); + ids_.count ++; + return *this; + } + + /** + * Add pair to emit for + * @tparam First The first element of the pair. + * @tparam Second the second element of a pair. + */ + template + Base& id() { + return id( + ecs_pair(_::type::id(this->world_), + _::type::id(this->world_))); + } + + /** + * Add pair to emit for + * @tparam First The first element of the pair. + * @param second The second element of the pair id. + */ + template + Base& id(entity_t second) { + return id(ecs_pair(_::type::id(this->world_), second)); + } + + /** + * Add pair to emit for + * @param first The first element of the pair type. + * @param second The second element of the pair id. + */ + Base& id(entity_t first, entity_t second) { + return id(ecs_pair(first, second)); + } + + template ::value> = 0> + Base& id(Enum value) { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(value); + return id(et.entity(), target); + } + + /** Add (component) id to emit for */ + Base& id(flecs::id_t id) { + ids_.array = ids_array_; + ids_.array[ids_.count] = id; + ids_.count ++; + return *this; + } + + /** Set entity for which to emit event */ + Base& entity(flecs::entity_t e) { + desc_.entity = e; + return *this; + } + + /* Set table for which to emit event */ + Base& table(flecs::table_t *t, int32_t offset = 0, int32_t count = 0) { + desc_.table = t; + desc_.offset = offset; + desc_.count = count; + return *this; + } + + /* Set event data */ + Base& ctx(const E* ptr) { + desc_.const_param = ptr; + return *this; + } + + /* Set event data */ + Base& ctx(E* ptr) { + desc_.param = ptr; + return *this; + } + + void emit() { + ids_.array = ids_array_; + desc_.ids = &ids_; + desc_.observable = const_cast(ecs_get_world(world_)); + ecs_emit(world_, &desc_); + } + + void enqueue() { + ids_.array = ids_array_; + desc_.ids = &ids_; + desc_.observable = const_cast(ecs_get_world(world_)); + ecs_enqueue(world_, &desc_); + } + +protected: + flecs::world_t *world_; + ecs_event_desc_t desc_; + flecs::type_t ids_; + flecs::id_t ids_array_[ECS_EVENT_DESC_ID_COUNT_MAX]; + +private: + operator Base&() { + return *static_cast(this); + } +}; + +struct event_builder : event_builder_base { + using event_builder_base::event_builder_base; +}; + +template +struct event_builder_typed : event_builder_base, E> { +private: + using Class = event_builder_typed; + +public: + using event_builder_base::event_builder_base; + + /* Set event data */ + Class& ctx(const E& ptr) { + this->desc_.const_param = &ptr; + return *this; + } + + /* Set event data */ + Class& ctx(E&& ptr) { + this->desc_.param = &ptr; + return *this; + } +}; + +/** @} */ + +} + + +namespace flecs { +namespace _ { + +// Utility to derive event type from function +template +struct event_from_func; + +// Specialization for observer callbacks with a single argument +template +struct event_from_func::value == 1>> { + using type = decay_t>; +}; + +// Specialization for observer callbacks with an initial entity src argument +template +struct event_from_func::value == 2>> { + using type = decay_t>; +}; + +template +using event_from_func_t = typename event_from_func::type; + +} +} + +/** + * @file addons/cpp/mixins/observer/decl.hpp + * @brief Observer declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_observers Observers + * @ingroup cpp_core + * Observers let applications register callbacks for ECS events. + * + * @{ + */ + +struct observer; + +template +struct observer_builder; + +/** @} */ + +} + +#ifdef FLECS_SYSTEM +/** + * @file addons/cpp/mixins/system/decl.hpp + * @brief System module declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_systems Systems + * @ingroup cpp_addons + * Systems are a query + function that can be ran manually or by a pipeline. + * + * @{ + */ + +using TickSource = EcsTickSource; + +struct system; + +template +struct system_builder; + +namespace _ { + +void system_init(flecs::world& world); + +/** @} */ + +} // namespace _ +} // namespace flecs + +#endif +#ifdef FLECS_PIPELINE +/** + * @file addons/cpp/mixins/pipeline/decl.hpp + * @brief Pipeline module declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_pipelines Pipelines + * @ingroup cpp_addons + * Pipelines order and schedule systems for execution. + * + * @{ + */ + +template +struct pipeline; + +template +struct pipeline_builder; + +/* Builtin pipeline tags */ +static const flecs::entity_t OnStart = EcsOnStart; +static const flecs::entity_t PreFrame = EcsPreFrame; +static const flecs::entity_t OnLoad = EcsOnLoad; +static const flecs::entity_t PostLoad = EcsPostLoad; +static const flecs::entity_t PreUpdate = EcsPreUpdate; +static const flecs::entity_t OnUpdate = EcsOnUpdate; +static const flecs::entity_t OnValidate = EcsOnValidate; +static const flecs::entity_t PostUpdate = EcsPostUpdate; +static const flecs::entity_t PreStore = EcsPreStore; +static const flecs::entity_t OnStore = EcsOnStore; +static const flecs::entity_t PostFrame = EcsPostFrame; + +/** @} */ + +} + +#endif +#ifdef FLECS_TIMER +/** + * @file addons/cpp/mixins/timer/decl.hpp + * @brief Timer module declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_timer Timer + * @ingroup cpp_addons + * Run systems at a time interval. + * + * @{ + */ + +using Timer = EcsTimer; +using RateFilter = EcsRateFilter; + +struct timer; + +/** @} */ + +namespace _ { + +void timer_init(flecs::world& world); + +} // namespace _ +} // namespace flecs + +#endif +#ifdef FLECS_DOC +/** + * @file addons/cpp/mixins/doc/decl.hpp + * @brief Doc mixin declarations. + */ + +#pragma once + +namespace flecs { +namespace doc { + +/** + * @defgroup cpp_addons_doc Doc + * @ingroup cpp_addons + * Utilities for documenting entities, components and systems. + * + * @{ + */ + +/** flecs.doc.Description component */ +using Description = EcsDocDescription; + +/** flecs.doc.Uuid component */ +static const flecs::entity_t Uuid = EcsDocUuid; + +/** flecs.doc.Brief component */ +static const flecs::entity_t Brief = EcsDocBrief; + +/** flecs.doc.Detail component */ +static const flecs::entity_t Detail = EcsDocDetail; + +/** flecs.doc.Link component */ +static const flecs::entity_t Link = EcsDocLink; + +/** flecs.doc.Color component */ +static const flecs::entity_t Color = EcsDocColor; + +/** @private */ +namespace _ { +/** @private */ +void init(flecs::world& world); +} + +/** @} */ + +} +} + +#endif +#ifdef FLECS_REST +/** + * @file addons/cpp/mixins/rest/decl.hpp + * @brief Rest module declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_rest Rest + * @ingroup cpp_addons + * REST API for querying and mutating entities. + * + * @{ + */ + +using Rest = EcsRest; + +namespace rest { + +namespace _ { + +void init(flecs::world& world); + +} +} + +/** @} */ + +} + +#endif +#ifdef FLECS_META +/** + * @file addons/cpp/mixins/meta/decl.hpp + * @brief Meta declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_meta Meta + * @ingroup cpp_addons + * Flecs reflection framework. + * + * @{ + */ + +/* Primitive type aliases */ +using bool_t = ecs_bool_t; +using char_t = ecs_char_t; +using u8_t = ecs_u8_t; +using u16_t = ecs_u16_t; +using u32_t = ecs_u32_t; +using u64_t = ecs_u64_t; +using uptr_t = ecs_uptr_t; +using i8_t = ecs_i8_t; +using i16_t = ecs_i16_t; +using i32_t = ecs_i32_t; +using i64_t = ecs_i64_t; +using iptr_t = ecs_iptr_t; +using f32_t = ecs_f32_t; +using f64_t = ecs_f64_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 Type = EcsType; +using TypeSerializer = EcsTypeSerializer; +using Primitive = EcsPrimitive; +using Enum = EcsEnum; +using Bitmask = EcsBitmask; +using Member = EcsMember; +using MemberRanges = EcsMemberRanges; +using Struct = EcsStruct; +using Array = EcsArray; +using Vector = EcsVector; +using Unit = EcsUnit; + +/** Base type for bitmasks */ +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); +static const flecs::entity_t U8 = ecs_id(ecs_u8_t); +static const flecs::entity_t U16 = ecs_id(ecs_u16_t); +static const flecs::entity_t U32 = ecs_id(ecs_u32_t); +static const flecs::entity_t U64 = ecs_id(ecs_u64_t); +static const flecs::entity_t Uptr = ecs_id(ecs_uptr_t); +static const flecs::entity_t I8 = ecs_id(ecs_i8_t); +static const flecs::entity_t I16 = ecs_id(ecs_i16_t); +static const flecs::entity_t I32 = ecs_id(ecs_i32_t); +static const flecs::entity_t I64 = ecs_id(ecs_i64_t); +static const flecs::entity_t Iptr = ecs_id(ecs_iptr_t); +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; + +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; + +/** @} */ + +namespace _ { + +void init(flecs::world& world); + +} // namespace _ +} // namespace meta +} // namespace flecs + +/** + * @file addons/cpp/mixins/meta/opaque.hpp + * @brief Helpers for opaque type registration. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_meta Meta + * @ingroup cpp_addons + * Flecs reflection framework. + * + * @{ + */ + +/** Class for reading/writing dynamic values. + * + * @ingroup cpp_addons_meta + */ +struct cursor { + cursor(flecs::world_t *world, flecs::entity_t type_id, void *ptr) { + cursor_ = ecs_meta_cursor(world, type_id, ptr); + } + + /** Push value scope (such as a nested struct) */ + int push() { + return ecs_meta_push(&cursor_); + } + + /** Pop value scope */ + int pop() { + return ecs_meta_pop(&cursor_); + } + + /** Move to next member/element */ + int next() { + return ecs_meta_next(&cursor_); + } + + /** Move to member by name */ + int member(const char *name) { + return ecs_meta_member(&cursor_, name); + } + + /** Move to element by index */ + int elem(int32_t elem) { + return ecs_meta_elem(&cursor_, elem); + } + + /** Test if current scope is a collection type */ + bool is_collection() { + return ecs_meta_is_collection(&cursor_); + } + + /** Get member name */ + flecs::string_view get_member() const { + return flecs::string_view(ecs_meta_get_member(&cursor_)); + } + + /** Get type of value */ + flecs::entity get_type() const; + + /** Get unit of value */ + flecs::entity get_unit() const; + + /** Get untyped pointer to value */ + void* get_ptr() { + return ecs_meta_get_ptr(&cursor_); + } + + /** Set boolean value */ + int set_bool(bool value) { + return ecs_meta_set_bool(&cursor_, value); + } + + /** Set char value */ + int set_char(char value) { + return ecs_meta_set_char(&cursor_, value); + } + + /** Set signed int value */ + int set_int(int64_t value) { + return ecs_meta_set_int(&cursor_, value); + } + + /** Set unsigned int value */ + int set_uint(uint64_t value) { + return ecs_meta_set_uint(&cursor_, value); + } + + /** Set float value */ + int set_float(double value) { + return ecs_meta_set_float(&cursor_, value); + } + + /** Set string value */ + int set_string(const char *value) { + return ecs_meta_set_string(&cursor_, value); + } + + /** Set string literal value */ + int set_string_literal(const char *value) { + return ecs_meta_set_string_literal(&cursor_, value); + } + + /** Set entity value */ + int set_entity(flecs::entity_t value) { + return ecs_meta_set_entity(&cursor_, value); + } + + /** Set (component) id value */ + int set_id(flecs::id_t value) { + return ecs_meta_set_id(&cursor_, value); + } + + /** Set null value */ + int set_null() { + return ecs_meta_set_null(&cursor_); + } + + /** Get boolean value */ + bool get_bool() const { + return ecs_meta_get_bool(&cursor_); + } + + /** Get char value */ + char get_char() const { + return ecs_meta_get_char(&cursor_); + } + + /** Get signed int value */ + int64_t get_int() const { + return ecs_meta_get_int(&cursor_); + } + + /** Get unsigned int value */ + uint64_t get_uint() const { + return ecs_meta_get_uint(&cursor_); + } + + /** Get float value */ + double get_float() const { + return ecs_meta_get_float(&cursor_); + } + + /** Get string value */ + const char *get_string() const { + return ecs_meta_get_string(&cursor_); + } + + /** Get entity value */ + flecs::entity get_entity() const; + + /** Cursor object */ + ecs_meta_cursor_t cursor_; +}; + +/** @} */ + +} + +/** + * @file addons/cpp/mixins/meta/opaque.hpp + * @brief Helpers for opaque type registration. + */ + +#pragma once + +#include + +namespace flecs { + +/** + * @defgroup cpp_addons_meta Meta + * @ingroup cpp_addons + * Flecs reflection framework. + * + * @{ + */ + +/** Serializer object, used for serializing opaque types */ +using serializer = ecs_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*); + +/** Type safe interface for opaque types */ +template +struct opaque { + opaque(flecs::world_t *w = nullptr) : world(w) { + if (world) { + desc.entity = _::type::id(world); + } + } + + /** Type that describes the type kind/structure of the opaque type */ + opaque& as_type(flecs::id_t func) { + this->desc.type.as_type = func; + return *this; + } + + /** Serialize function */ + opaque& serialize(flecs::serialize func) { + this->desc.type.serialize = + reinterpret_castdesc.type.serialize)>(func); + return *this; + } + + /** Assign bool value */ + opaque& assign_bool(void (*func)(T *dst, bool value)) { + this->desc.type.assign_bool = + reinterpret_castdesc.type.assign_bool)>(func); + return *this; + } + + /** Assign char value */ + opaque& assign_char(void (*func)(T *dst, char value)) { + this->desc.type.assign_char = + reinterpret_castdesc.type.assign_char)>(func); + return *this; + } + + /** Assign int value */ + opaque& assign_int(void (*func)(T *dst, int64_t value)) { + this->desc.type.assign_int = + reinterpret_castdesc.type.assign_int)>(func); + return *this; + } + + /** Assign unsigned int value */ + opaque& assign_uint(void (*func)(T *dst, uint64_t value)) { + this->desc.type.assign_uint = + reinterpret_castdesc.type.assign_uint)>(func); + return *this; + } + + /** Assign float value */ + opaque& assign_float(void (*func)(T *dst, double value)) { + this->desc.type.assign_float = + reinterpret_castdesc.type.assign_float)>(func); + return *this; + } + + /** Assign string value */ + opaque& assign_string(void (*func)(T *dst, const char *value)) { + this->desc.type.assign_string = + reinterpret_castdesc.type.assign_string)>(func); + return *this; + } + + /** Assign entity value */ + opaque& assign_entity( + void (*func)(T *dst, ecs_world_t *world, ecs_entity_t entity)) + { + this->desc.type.assign_entity = + reinterpret_castdesc.type.assign_entity)>(func); + return *this; + } + + /** Assign (component) id value */ + opaque& assign_id( + void (*func)(T *dst, ecs_world_t *world, ecs_id_t id)) + { + this->desc.type.assign_id = + reinterpret_castdesc.type.assign_id)>(func); + return *this; + } + + /** Assign null value */ + opaque& assign_null(void (*func)(T *dst)) { + this->desc.type.assign_null = + reinterpret_castdesc.type.assign_null)>(func); + return *this; + } + + /** Clear collection elements */ + opaque& clear(void (*func)(T *dst)) { + this->desc.type.clear = + reinterpret_castdesc.type.clear)>(func); + return *this; + } + + /** Ensure & get collection element */ + opaque& ensure_element(ElemType* (*func)(T *dst, size_t elem)) { + this->desc.type.ensure_element = + reinterpret_castdesc.type.ensure_element)>(func); + return *this; + } + + /** Ensure & get element */ + opaque& ensure_member(void* (*func)(T *dst, const char *member)) { + this->desc.type.ensure_member = + reinterpret_castdesc.type.ensure_member)>(func); + return *this; + } + + /** Return number of elements */ + opaque& count(size_t (*func)(const T *dst)) { + this->desc.type.count = + reinterpret_castdesc.type.count)>(func); + return *this; + } + + /** Resize to number of elements */ + opaque& resize(void (*func)(T *dst, size_t count)) { + this->desc.type.resize = + reinterpret_castdesc.type.resize)>(func); + return *this; + } + + ~opaque() { + if (world) { + ecs_opaque_init(world, &desc); + } + } + + /** Opaque type descriptor */ + flecs::world_t *world = nullptr; + ecs_opaque_desc_t desc = {}; +}; + +/** @} */ + +} + + +#endif +#ifdef FLECS_UNITS +/** + * @file addons/cpp/mixins/units/decl.hpp + * @brief Units module declarations. + */ + +#pragma once + +namespace flecs { +struct units { + +/** + * @defgroup cpp_addons_units Units + * @ingroup cpp_addons + * Common unit annotations for reflection framework. + * + * @{ + */ + +struct Prefixes { }; + +/** + * @defgroup cpp_addons_units_prefixes Prefixes + * @ingroup cpp_addons_units + * Prefixes to indicate unit count (e.g. Kilo, Mega) + * + * @{ + */ + +struct Yocto { }; +struct Zepto { }; +struct Atto { }; +struct Femto { }; +struct Pico { }; +struct Nano { }; +struct Micro { }; +struct Milli { }; +struct Centi { }; +struct Deci { }; +struct Deca { }; +struct Hecto { }; +struct Kilo { }; +struct Mega { }; +struct Giga { }; +struct Tera { }; +struct Peta { }; +struct Exa { }; +struct Zetta { }; +struct Yotta { }; +struct Kibi { }; +struct Mebi { }; +struct Gibi { }; +struct Tebi { }; +struct Pebi { }; +struct Exbi { }; +struct Zebi { }; +struct Yobi { }; + +/** @} */ + +/** + * @defgroup cpp_addons_units_quantities Quantities + * @ingroup cpp_addons_units + * Quantities that group units (e.g. Length) + * + * @{ + */ + +struct Duration { }; +struct Time { }; +struct Mass { }; +struct ElectricCurrent { }; +struct LuminousIntensity { }; +struct Force { }; +struct Amount { }; +struct Length { }; +struct Pressure { }; +struct Speed { }; +struct Temperature { }; +struct Data { }; +struct DataRate { }; +struct Angle { }; +struct Frequency { }; +struct Uri { }; +struct Color { }; + +/** @} */ + +struct duration { +/** + * @defgroup cpp_addons_units_duration Duration + * @ingroup cpp_addons_units + * @{ + */ + +struct PicoSeconds { }; +struct NanoSeconds { }; +struct MicroSeconds { }; +struct MilliSeconds { }; +struct Seconds { }; +struct Minutes { }; +struct Hours { }; +struct Days { }; + +/** @} */ +}; + +struct angle { +/** + * @defgroup cpp_addons_units_angle Angle + * @ingroup cpp_addons_units + * @{ + */ + +struct Radians { }; +struct Degrees { }; + +/** @} */ +}; + + +struct time { +/** + * @defgroup cpp_addons_units_time Time + * @ingroup cpp_addons_units + * @{ + */ + +struct Date { }; + +/** @} */ +}; + + +struct mass { +/** + * @defgroup cpp_addons_units_mass Mass + * @ingroup cpp_addons_units + * @{ + */ + +struct Grams { }; +struct KiloGrams { }; + +/** @} */ +}; + + +struct electric_current { +/** + * @defgroup cpp_addons_units_electric_current Electric Current + * @ingroup cpp_addons_units + * @{ + */ + +struct Ampere { }; + +/** @} */ +}; + + +struct amount { +/** + * @defgroup cpp_addons_units_amount Amount + * @ingroup cpp_addons_units + * @{ + */ + +struct Mole { }; + +/** @} */ +}; + + +struct luminous_intensity { +/** + * @defgroup cpp_addons_units_luminous_intensity Luminous Intensity + * @ingroup cpp_addons_units + * @{ + */ + +struct Candela { }; + +/** @} */ +}; + + +struct force { +/** + * @defgroup cpp_addons_units_force Force + * @ingroup cpp_addons_units + * @{ + */ + +struct Newton { }; + +/** @} */ +}; + + +struct length { +/** + * @defgroup cpp_addons_units_length Length + * @ingroup cpp_addons_units + * @{ + */ + +struct Meters { }; +struct PicoMeters { }; +struct NanoMeters { }; +struct MicroMeters { }; +struct MilliMeters { }; +struct CentiMeters { }; +struct KiloMeters { }; +struct Miles { }; +struct Pixels { }; + +/** @} */ +}; + + +struct pressure { +/** + * @defgroup cpp_addons_units_pressure Pressure + * @ingroup cpp_addons_units + * @{ + */ + +struct Pascal { }; +struct Bar { }; + +/** @} */ +}; + + +struct speed { +/** + * @defgroup cpp_addons_units_speed Speed + * @ingroup cpp_addons_units + * @{ + */ + +struct MetersPerSecond { }; +struct KiloMetersPerSecond { }; +struct KiloMetersPerHour { }; +struct MilesPerHour { }; + +/** @} */ +}; + + +struct temperature { +/** + * @defgroup cpp_addons_units_temperature Temperature + * @ingroup cpp_addons_units + * @{ + */ + +struct Kelvin { }; +struct Celsius { }; +struct Fahrenheit { }; + +/** @} */ +}; + + +struct data { +/** + * @defgroup cpp_addons_units_data Data + * @ingroup cpp_addons_units + * @{ + */ + +struct Bits { }; +struct KiloBits { }; +struct MegaBits { }; +struct GigaBits { }; +struct Bytes { }; +struct KiloBytes { }; +struct MegaBytes { }; +struct GigaBytes { }; +struct KibiBytes { }; +struct MebiBytes { }; +struct GibiBytes { }; + +/** @} */ +}; + +struct datarate { +/** + * @defgroup cpp_addons_units_datarate Data Rate + * @ingroup cpp_addons_units + * @{ + */ + +struct BitsPerSecond { }; +struct KiloBitsPerSecond { }; +struct MegaBitsPerSecond { }; +struct GigaBitsPerSecond { }; +struct BytesPerSecond { }; +struct KiloBytesPerSecond { }; +struct MegaBytesPerSecond { }; +struct GigaBytesPerSecond { }; + +/** @} */ +}; + + +struct frequency { +/** + * @defgroup cpp_addons_units_frequency Frequency + * @ingroup cpp_addons_units + * @{ + */ + +struct Hertz { }; +struct KiloHertz { }; +struct MegaHertz { }; +struct GigaHertz { }; + +/** @} */ +}; + + +struct uri { +/** + * @defgroup cpp_addons_units_uri Uri + * @ingroup cpp_addons_units + * @{ + */ + +struct Hyperlink { }; +struct Image { }; +struct File { }; + +/** @} */ +}; + + +struct color { +/** + * @defgroup cpp_addons_units_color Color + * @ingroup cpp_addons_units + * @{ + */ + +struct Rgb { }; +struct Hsl { }; +struct Css { }; + +/** @} */ +}; + +struct Percentage { }; +struct Bel { }; +struct DeciBel { }; + +units(flecs::world& world); + +/** @} */ + +}; +} + +#endif +#ifdef FLECS_STATS +/** + * @file addons/cpp/mixins/stats/decl.hpp + * @brief Stats module declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_stats Stats + * @ingroup cpp_addons + * The stats addon tracks statistics for the world and systems. + * + * @{ + */ + +/** Component that stores world statistics */ +using WorldStats = EcsWorldStats; + +/** Component that stores system/pipeline statistics */ +using PipelineStats = EcsPipelineStats; + +/** Component with world summary stats */ +using WorldSummary = EcsWorldSummary; + +struct stats { + stats(flecs::world& world); +}; + +/** @} */ + +} + +#endif +#ifdef FLECS_METRICS +/** + * @file addons/cpp/mixins/metrics/decl.hpp + * @brief Metrics declarations. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/metrics/builder.hpp + * @brief Metric builder. + */ + +#pragma once + +#define ECS_EVENT_DESC_ID_COUNT_MAX (8) + +namespace flecs { + +/** + * @ingroup cpp_addons_metrics + * @{ + */ + +/** Event builder interface */ +struct metric_builder { + metric_builder(flecs::world_t *world, flecs::entity_t entity) + : world_(world) + { + desc_.entity = entity; + } + + ~metric_builder(); + + metric_builder& member(flecs::entity_t e) { + desc_.member = e; + return *this; + } + + metric_builder& member(const char *name); + + template + metric_builder& member(const char *name); + + metric_builder& dotmember(const char *name); + + template + metric_builder& dotmember(const char *name); + + metric_builder& id(flecs::id_t the_id) { + desc_.id = the_id; + return *this; + } + + metric_builder& id(flecs::entity_t first, flecs::entity_t second) { + desc_.id = ecs_pair(first, second); + return *this; + } + + template + metric_builder& id() { + return id(_::type::id(world_)); + } + + template + metric_builder& id(flecs::entity_t second) { + return id(_::type::id(world_), second); + } + + template + metric_builder& id_second(flecs::entity_t first) { + return id(first, _::type::id(world_)); + } + + template + metric_builder& id() { + return id(_::type::id(world_)); + } + + metric_builder& targets(bool value = true) { + desc_.targets = value; + return *this; + } + + metric_builder& kind(flecs::entity_t the_kind) { + desc_.kind = the_kind; + return *this; + } + + template + metric_builder& kind() { + return kind(_::type::id(world_)); + } + + metric_builder& brief(const char *b) { + desc_.brief = b; + return *this; + } + + operator flecs::entity(); + +protected: + flecs::world_t *world_; + ecs_metric_desc_t desc_ = {}; + bool created_ = false; +}; + +/** + * @} + */ + +} + + +namespace flecs { + +/** + * @defgroup cpp_addons_metrics Metrics + * @ingroup cpp_addons + * The metrics module extracts metrics from components and makes them available + * through a unified component interface. + * + * @{ + */ + +struct metrics { + using Value = EcsMetricValue; + using Source = EcsMetricSource; + + struct Instance { }; + struct Metric { }; + struct Counter { }; + struct CounterIncrement { }; + struct CounterId { }; + struct Gauge { }; + + metrics(flecs::world& world); +}; + +/** @} */ + +} + +#endif +#ifdef FLECS_ALERTS +/** + * @file addons/cpp/mixins/alerts/decl.hpp + * @brief Alert declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_alerts Alerts + * @ingroup cpp_addons + * Alert implementation. + * + * @{ + */ + +/** Module */ +struct alerts { + using AlertsActive = EcsAlertsActive; + using Instance = EcsAlertInstance; + + struct Alert { }; + struct Info { }; + struct Warning { }; + struct Error { }; + + alerts(flecs::world& world); +}; + +template +struct alert; + +template +struct alert_builder; + +/** @} */ + +} + +#endif +#ifdef FLECS_JSON +/** + * @file addons/cpp/mixins/json/decl.hpp + * @brief JSON addon declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_json Json + * @ingroup cpp_addons + * Functions for serializing to/from JSON. + * + * @{ + */ + +using from_json_desc_t = ecs_from_json_desc_t; +using entity_to_json_desc_t = ecs_entity_to_json_desc_t; +using iter_to_json_desc_t = ecs_iter_to_json_desc_t; + +/** @} */ + +} + +#endif +#ifdef FLECS_APP +/** + * @file addons/cpp/mixins/app/decl.hpp + * @brief App addon declarations. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/app/builder.hpp + * @brief App builder. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_app App + * @ingroup cpp_addons + * Optional addon for running the main application loop. + * + * @{ + */ + +/** App builder interface */ +struct app_builder { + app_builder(flecs::world_t *world) + : world_(world) + , desc_{} + { + const ecs_world_info_t *stats = ecs_get_world_info(world); + desc_.target_fps = stats->target_fps; + ecs_ftime_t t_zero = 0.0; + if (ECS_EQ(desc_.target_fps, t_zero)) { + desc_.target_fps = 60; + } + } + + app_builder& target_fps(ecs_ftime_t value) { + desc_.target_fps = value; + return *this; + } + + app_builder& delta_time(ecs_ftime_t value) { + desc_.delta_time = value; + return *this; + } + + app_builder& threads(int32_t value) { + desc_.threads = value; + return *this; + } + + app_builder& frames(int32_t value) { + desc_.frames = value; + return *this; + } + + app_builder& enable_rest(uint16_t port = 0) { + desc_.enable_rest = true; + desc_.port = port; + return *this; + } + + app_builder& enable_stats(bool value = true) { + desc_.enable_stats = value; + return *this; + } + + app_builder& init(ecs_app_init_action_t value) { + desc_.init = value; + return *this; + } + + app_builder& ctx(void *value) { + desc_.ctx = value; + return *this; + } + + int run() { + int result = ecs_app_run(world_, &desc_); + if (ecs_should_quit(world_)) { + // Only free world if quit flag is set. This ensures that we won't + // try to cleanup the world if the app is used in an environment + // that takes over the main loop, like with emscripten. + if (!flecs_poly_release(world_)) { + ecs_fini(world_); + } + } + return result; + } + +private: + flecs::world_t *world_; + ecs_app_desc_t desc_; +}; + +/** @} */ + +} + + +#endif +#ifdef FLECS_SCRIPT +/** + * @file addons/cpp/mixins/script/decl.hpp + * @brief Script declarations. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/script/builder.hpp + * @brief Script builder. + */ + +#pragma once + +namespace flecs { + +/** + * @ingroup cpp_addons_script + * @{ + */ + +/** Script builder interface */ +struct script_builder { + script_builder(flecs::world_t *world, const char *name = nullptr) + : world_(world) + , desc_{} + { + if (name != nullptr) { + ecs_entity_desc_t entity_desc = {}; + entity_desc.name = name; + entity_desc.sep = "::"; + entity_desc.root_sep = "::"; + this->desc_.entity = ecs_entity_init(world, &entity_desc); + } + } + + script_builder& code(const char *str) { + desc_.code = str; + return *this; + } + + script_builder& filename(const char *str) { + desc_.filename = str; + return *this; + } + + flecs::entity run() const; + +protected: + flecs::world_t *world_; + ecs_script_desc_t desc_; +}; + +} + + +namespace flecs { + +/** + * @defgroup cpp_addons_script Script + * @ingroup cpp_addons + * + * @{ + */ + +struct script_builder; + +/** @} */ + +} + +#endif + +/** + * @file addons/cpp/log.hpp + * @brief Logging functions. + */ + +#pragma once + +namespace flecs { +namespace log { + +/** + * @defgroup cpp_log Logging + * @ingroup cpp_addons + * Logging functions. + * + * @{ + */ + +/** Set log level */ +inline void set_level(int level) { + ecs_log_set_level(level); +} + +inline int get_level() { + return ecs_log_get_level(); +} + +/** Enable colors in logging */ +inline void enable_colors(bool enabled = true) { + ecs_log_enable_colors(enabled); +} + +/** Enable timestamps in logging */ +inline void enable_timestamp(bool enabled = true) { + ecs_log_enable_timestamp(enabled); +} + +/** Enable time delta in logging */ +inline void enable_timedelta(bool enabled = true) { + ecs_log_enable_timedelta(enabled); +} + +/** Debug trace (level 1) */ +inline void dbg(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + ecs_logv(1, fmt, args); + va_end(args); +} + +/** Trace (level 0) */ +inline void trace(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + ecs_logv(0, fmt, args); + va_end(args); +} + +/** Trace (level -2) */ +inline void warn(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + ecs_logv(-2, fmt, args); + va_end(args); +} + +/** Trace (level -3) */ +inline void err(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + ecs_logv(-3, fmt, args); + va_end(args); +} + +/** Increase log indentation */ +inline void push(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + ecs_logv(0, fmt, args); + va_end(args); + ecs_log_push(); +} + +/** Increase log indentation */ +inline void push() { + ecs_log_push(); +} + +/** Increase log indentation */ +inline void pop() { + ecs_log_pop(); +} + +/** @} */ + +} +} + +/** + * @file addons/cpp/pair.hpp + * @brief Utilities for working with compile time pairs. + */ + +#pragma once + +namespace flecs { + +namespace _ { + struct pair_base { }; +} // _ + + +/** + * @defgroup cpp_pair_type Pair type + * @ingroup cpp_core + * Compile time utilities for working with relationship pairs. + * + * @{ + */ + +/** Type that represents a pair. + * The pair type can be used to represent a pair at compile time, and is able + * to automatically derive the storage type associated with the pair, accessible + * through pair::type. + * + * The storage type is derived using the following rules: + * - if pair::first is non-empty, the storage type is pair::first + * - if pair::first is empty and pair::second is non-empty, the storage type is pair::second + * + * The pair type can hold a temporary value so that it can be used in the + * signatures of queries + */ +template +struct pair : _::pair_base { + using type = conditional_t::value || is_empty::value, First, Second>; + using first = First; + using second = Second; + + pair(type& v) : ref_(v) { } + + // This allows the class to be used as a temporary object + pair(const type& v) : ref_(const_cast(v)) { } + + operator type&() { + return ref_; + } + + operator const type&() const { + return ref_; + } + + type* operator->() { + return &ref_; + } + + const type* operator->() const { + return &ref_; + } + + type& operator*() { + return ref_; + } + + const type& operator*() const { + return ref_; + } + +private: + type& ref_; +}; + +template ::value> = 0> +using pair_object = pair; + +template +using raw_type_t = remove_pointer_t>; + +/** Test if type is a pair. */ +template +struct is_pair { + static constexpr bool value = is_base_of<_::pair_base, raw_type_t >::value; +}; + +/** Get pair::first from pair while preserving cv qualifiers. */ +template +using pair_first_t = transcribe_cv_t, typename raw_type_t

::first>; + +/** Get pair::second from pair while preserving cv qualifiers. */ +template +using pair_second_t = transcribe_cv_t, typename raw_type_t

::second>; + +/** Get pair::type type from pair while preserving cv qualifiers and pointer type. */ +template +using pair_type_t = transcribe_cvp_t, typename raw_type_t

::type>; + +/** Get actual type from a regular type or pair. */ +template +struct actual_type; + +template +struct actual_type::value >> { + using type = T; +}; + +template +struct actual_type::value >> { + using type = pair_type_t; +}; + +template +using actual_type_t = typename actual_type::type; + + +// Get type without const, *, & +template +struct base_type { + using type = decay_t< actual_type_t >; +}; + +template +using base_type_t = typename base_type::type; + + +// Get type without *, & (retains const which is useful for function args) +template +struct base_arg_type { + using type = remove_pointer_t< remove_reference_t< actual_type_t > >; +}; + +template +using base_arg_type_t = typename base_arg_type::type; + + +// Test if type is the same as its actual type +template +struct is_actual { + static constexpr bool value = + std::is_same >::value && !is_enum::value; +}; + +} // flecs + +/** + * @file addons/cpp/lifecycle_traits.hpp + * @brief Utilities for discovering and registering component lifecycle hooks. + */ + +#pragma once + +namespace flecs +{ + +namespace _ +{ + +inline void ecs_ctor_illegal(void *, int32_t, const ecs_type_info_t *ti) { + ecs_abort(ECS_INVALID_OPERATION, "invalid constructor for %s", ti->name); +} + +inline void ecs_dtor_illegal(void *, int32_t, const ecs_type_info_t *ti) { + ecs_abort(ECS_INVALID_OPERATION, "invalid destructor for %s", ti->name); +} + +inline void ecs_copy_illegal( + void *, const void *, int32_t, const ecs_type_info_t *ti) +{ + ecs_abort(ECS_INVALID_OPERATION, "invalid copy assignment for %s", ti->name); +} + +inline void ecs_move_illegal(void *, void *, int32_t, const ecs_type_info_t *ti) { + ecs_abort(ECS_INVALID_OPERATION, "invalid move assignment for %s", ti->name); +} + +inline void ecs_copy_ctor_illegal( + void *, const void *, int32_t, const ecs_type_info_t *ti) +{ + ecs_abort(ECS_INVALID_OPERATION, "invalid copy construct for %s", ti->name); +} + +inline void ecs_move_ctor_illegal( + void *, void *, int32_t, const ecs_type_info_t *ti) +{ + ecs_abort(ECS_INVALID_OPERATION, "invalid move construct for %s", ti->name); +} + + +// T() +// Can't coexist with T(flecs::entity) or T(flecs::world, flecs::entity) +template +void ctor_impl(void *ptr, int32_t count, const ecs_type_info_t *info) { + (void)info; ecs_assert(info->size == ECS_SIZEOF(T), + ECS_INTERNAL_ERROR, NULL); + T *arr = static_cast(ptr); + for (int i = 0; i < count; i ++) { + FLECS_PLACEMENT_NEW(&arr[i], T); + } +} + +// ~T() +template +void dtor_impl(void *ptr, int32_t count, const ecs_type_info_t *info) { + (void)info; ecs_assert(info->size == ECS_SIZEOF(T), + ECS_INTERNAL_ERROR, NULL); + T *arr = static_cast(ptr); + for (int i = 0; i < count; i ++) { + arr[i].~T(); + } +} + +// T& operator=(const T&) +template +void copy_impl(void *dst_ptr, const void *src_ptr, int32_t count, + const ecs_type_info_t *info) +{ + (void)info; ecs_assert(info->size == ECS_SIZEOF(T), + ECS_INTERNAL_ERROR, NULL); + T *dst_arr = static_cast(dst_ptr); + const T *src_arr = static_cast(src_ptr); + for (int i = 0; i < count; i ++) { + dst_arr[i] = src_arr[i]; + } +} + +// T& operator=(T&&) +template +void move_impl(void *dst_ptr, void *src_ptr, int32_t count, + const ecs_type_info_t *info) +{ + (void)info; ecs_assert(info->size == ECS_SIZEOF(T), + ECS_INTERNAL_ERROR, NULL); + T *dst_arr = static_cast(dst_ptr); + T *src_arr = static_cast(src_ptr); + for (int i = 0; i < count; i ++) { + dst_arr[i] = FLECS_MOV(src_arr[i]); + } +} + +// T(T&) +template +void copy_ctor_impl(void *dst_ptr, const void *src_ptr, int32_t count, + const ecs_type_info_t *info) +{ + (void)info; ecs_assert(info->size == ECS_SIZEOF(T), + ECS_INTERNAL_ERROR, NULL); + T *dst_arr = static_cast(dst_ptr); + const T *src_arr = static_cast(src_ptr); + for (int i = 0; i < count; i ++) { + FLECS_PLACEMENT_NEW(&dst_arr[i], T(src_arr[i])); + } +} + +// T(T&&) +template +void move_ctor_impl(void *dst_ptr, void *src_ptr, int32_t count, + const ecs_type_info_t *info) +{ + (void)info; ecs_assert(info->size == ECS_SIZEOF(T), + ECS_INTERNAL_ERROR, NULL); + T *dst_arr = static_cast(dst_ptr); + T *src_arr = static_cast(src_ptr); + for (int i = 0; i < count; i ++) { + FLECS_PLACEMENT_NEW(&dst_arr[i], T(FLECS_MOV(src_arr[i]))); + } +} + +// T(T&&), ~T() +// Typically used when moving to a new table, and removing from the old table +template +void ctor_move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count, + const ecs_type_info_t *info) +{ + (void)info; ecs_assert(info->size == ECS_SIZEOF(T), + ECS_INTERNAL_ERROR, NULL); + T *dst_arr = static_cast(dst_ptr); + T *src_arr = static_cast(src_ptr); + for (int i = 0; i < count; i ++) { + FLECS_PLACEMENT_NEW(&dst_arr[i], T(FLECS_MOV(src_arr[i]))); + src_arr[i].~T(); + } +} + +// Move assign + dtor (non-trivial move assignment) +// Typically used when moving a component to a deleted component +template ::value > = 0> +void move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count, + const ecs_type_info_t *info) +{ + (void)info; ecs_assert(info->size == ECS_SIZEOF(T), + ECS_INTERNAL_ERROR, NULL); + T *dst_arr = static_cast(dst_ptr); + T *src_arr = static_cast(src_ptr); + for (int i = 0; i < count; i ++) { + // Move assignment should free dst & assign dst to src + dst_arr[i] = FLECS_MOV(src_arr[i]); + // Destruct src. Move should have left object in a state where it no + // longer holds resources, but it still needs to be destructed. + src_arr[i].~T(); + } +} + +// Move assign + dtor (trivial move assignment) +// Typically used when moving a component to a deleted component +template ::value > = 0> +void move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count, + const ecs_type_info_t *info) +{ + (void)info; ecs_assert(info->size == ECS_SIZEOF(T), + ECS_INTERNAL_ERROR, NULL); + T *dst_arr = static_cast(dst_ptr); + T *src_arr = static_cast(src_ptr); + for (int i = 0; i < count; i ++) { + // Cleanup resources of dst + dst_arr[i].~T(); + // Copy src to dst + dst_arr[i] = FLECS_MOV(src_arr[i]); + // No need to destruct src. Since this is a trivial move the code + // should be agnostic to the address of the component which means we + // can pretend nothing got destructed. + } +} + +} // _ + +// Trait to test if type is constructible by flecs +template +struct is_flecs_constructible { + static constexpr bool value = + std::is_default_constructible>::value; +}; + +namespace _ +{ + +// Trivially constructible +template ::value > = 0> +ecs_xtor_t ctor() { + return nullptr; +} + +// Not constructible by flecs +template ::value > = 0> +ecs_xtor_t ctor() { + return ecs_ctor_illegal; +} + +// Default constructible +template ::value && + std::is_default_constructible::value > = 0> +ecs_xtor_t ctor() { + return ctor_impl; +} + +// No dtor +template ::value > = 0> +ecs_xtor_t dtor() { + return nullptr; +} + +// Dtor +template ::value && + ! std::is_trivially_destructible::value > = 0> +ecs_xtor_t dtor() { + return dtor_impl; +} + +// Assert when the type cannot be destructed +template ::value > = 0> +ecs_xtor_t dtor() { + flecs_static_assert(always_false::value, + "component type must be destructible"); + return ecs_dtor_illegal; +} + +// Trivially copyable +template ::value > = 0> +ecs_copy_t copy() { + return nullptr; +} + +// Not copyable +template ::value && + ! std::is_copy_assignable::value > = 0> +ecs_copy_t copy() { + return ecs_copy_illegal; +} + +// Copy assignment +template ::value && + ! std::is_trivially_copyable::value > = 0> +ecs_copy_t copy() { + return copy_impl; +} + +// Trivially move assignable +template ::value > = 0> +ecs_move_t move() { + return nullptr; +} + +// Component types must be move assignable +template ::value > = 0> +ecs_move_t move() { + return ecs_move_illegal; +} + +// Move assignment +template ::value && + ! std::is_trivially_move_assignable::value > = 0> +ecs_move_t move() { + return move_impl; +} + +// Trivially copy constructible +template ::value > = 0> +ecs_copy_t copy_ctor() { + return nullptr; +} + +// No copy ctor +template ::value > = 0> +ecs_copy_t copy_ctor() { + return ecs_copy_ctor_illegal; +} + +// Copy ctor +template ::value && + ! std::is_trivially_copy_constructible::value > = 0> +ecs_copy_t copy_ctor() { + return copy_ctor_impl; +} + +// Trivially move constructible +template ::value > = 0> +ecs_move_t move_ctor() { + return nullptr; +} + +// Component types must be move constructible +template ::value > = 0> +ecs_move_t move_ctor() { + return ecs_move_ctor_illegal; +} + +// Move ctor +template ::value && + ! std::is_trivially_move_constructible::value > = 0> +ecs_move_t move_ctor() { + return move_ctor_impl; +} + +// Trivial merge (move assign + dtor) +template ::value && + std::is_trivially_destructible::value > = 0> +ecs_move_t ctor_move_dtor() { + return nullptr; +} + +// Component types must be move constructible and destructible +template ::value || + ! std::is_destructible::value > = 0> +ecs_move_t ctor_move_dtor() { + return ecs_move_ctor_illegal; +} + +// Merge ctor + dtor +template ::value && + std::is_trivially_destructible::value) && + std::is_move_constructible::value && + std::is_destructible::value > = 0> +ecs_move_t ctor_move_dtor() { + return ctor_move_dtor_impl; +} + +// Trivial merge (move assign + dtor) +template ::value && + std::is_trivially_destructible::value > = 0> +ecs_move_t move_dtor() { + return nullptr; +} + +// Component types must be move constructible and destructible +template ::value || + ! std::is_destructible::value > = 0> +ecs_move_t move_dtor() { + return ecs_move_ctor_illegal; +} + +// Merge assign + dtor +template ::value && + std::is_trivially_destructible::value) && + std::is_move_assignable::value && + std::is_destructible::value > = 0> +ecs_move_t move_dtor() { + return move_dtor_impl; +} + +} // _ +} // flecs + +/** + * @file addons/cpp/world.hpp + * @brief World class. + */ + +#pragma once + +namespace flecs +{ + +/* Static helper functions to assign a component value */ + +// set(T&&), T = constructible +template ::value > = 0> +inline void set(world_t *world, flecs::entity_t entity, T&& value, flecs::id_t id) { + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + + if (!ecs_is_deferred(world)) { + T& dst = *static_cast(ecs_ensure_id(world, entity, id)); + dst = FLECS_MOV(value); + + ecs_modified_id(world, entity, id); + } else { + T& dst = *static_cast(ecs_ensure_modified_id(world, entity, id)); + dst = FLECS_MOV(value); + } +} + +// set(const T&), T = constructible +template ::value > = 0> +inline void set(world_t *world, flecs::entity_t entity, const T& value, flecs::id_t id) { + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + + if (!ecs_is_deferred(world)) { + T& dst = *static_cast(ecs_ensure_id(world, entity, id)); + dst = FLECS_MOV(value); + + ecs_modified_id(world, entity, id); + } else { + T& dst = *static_cast(ecs_ensure_modified_id(world, entity, id)); + dst = FLECS_MOV(value); + } +} + +// set(T&&), T = not constructible +template ::value > = 0> +inline void set(world_t *world, flecs::entity_t entity, T&& value, flecs::id_t id) { + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + + if (!ecs_is_deferred(world)) { + T& dst = *static_cast*>(ecs_ensure_id(world, entity, id)); + dst = FLECS_MOV(value); + + ecs_modified_id(world, entity, id); + } else { + T& dst = *static_cast*>(ecs_ensure_modified_id(world, entity, id)); + dst = FLECS_MOV(value); + } +} + +// set(const T&), T = not constructible +template ::value > = 0> +inline void set(world_t *world, flecs::entity_t entity, const T& value, flecs::id_t id) { + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + + if (!ecs_is_deferred(world)) { + T& dst = *static_cast*>(ecs_ensure_id(world, entity, id)); + dst = FLECS_MOV(value); + + ecs_modified_id(world, entity, id); + } else { + T& dst = *static_cast*>(ecs_ensure_modified_id(world, entity, id)); + dst = FLECS_MOV(value); + } +} + +// emplace for T(Args...) +template , Args...>::value || + std::is_default_constructible>::value > = 0> +inline void emplace(world_t *world, flecs::entity_t entity, flecs::id_t id, Args&&... args) { + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + T& dst = *static_cast(ecs_emplace_id(world, entity, id, nullptr)); + + FLECS_PLACEMENT_NEW(&dst, T{FLECS_FWD(args)...}); + + ecs_modified_id(world, entity, id); +} + +// set(T&&) +template +inline void set(world_t *world, entity_t entity, A&& value) { + id_t id = _::type::id(world); + flecs::set(world, entity, FLECS_FWD(value), id); +} + +// set(const T&) +template +inline void set(world_t *world, entity_t entity, const A& value) { + id_t id = _::type::id(world); + flecs::set(world, entity, value, id); +} + +/** Return id without generation. + * + * @see ecs_strip_generation() + */ +inline flecs::id_t strip_generation(flecs::entity_t e) { + return ecs_strip_generation(e); +} + +/** Return entity generation. + */ +inline uint32_t get_generation(flecs::entity_t e) { + return ECS_GENERATION(e); +} + +struct scoped_world; + +/** + * @defgroup cpp_world World + * @ingroup cpp_core + * World operations. + * + * @{ + */ + +/** The world. + * The world is the container of all ECS data and systems. If the world is + * deleted, all data in the world will be deleted as well. + */ +struct world { + /** Create world. + */ + explicit world() + : world_( ecs_init() ) { + init_builtin_components(); + } + + /** Create world with command line arguments. + * Currently command line arguments are not interpreted, but they may be + * used in the future to configure Flecs parameters. + */ + explicit world(int argc, char *argv[]) + : world_( ecs_init_w_args(argc, argv) ) { + init_builtin_components(); + } + + /** Create world from C world. + */ + explicit world(world_t *w) + : world_( w ) { + if (w) { + flecs_poly_claim(w); + } + } + + /** Not allowed to copy a world. May only take a reference. + */ + world(const world& obj) { + this->world_ = obj.world_; + flecs_poly_claim(this->world_); + } + + world& operator=(const world& obj) noexcept { + release(); + this->world_ = obj.world_; + flecs_poly_claim(this->world_); + return *this; + } + + world(world&& obj) noexcept { + world_ = obj.world_; + obj.world_ = nullptr; + } + + world& operator=(world&& obj) noexcept { + release(); + world_ = obj.world_; + obj.world_ = nullptr; + return *this; + } + + /* Releases the underlying world object. If this is the last handle, the world + will be finalized. */ + void release() { + if (world_) { + if (!flecs_poly_release(world_)) { + if (ecs_stage_get_id(world_) == -1) { + ecs_stage_free(world_); + } else { + // before we call ecs_fini(), we increment the reference count back to 1 + // otherwise, copies of this object created during ecs_fini (e.g. a component on_remove hook) + // would call again this destructor and ecs_fini(). + flecs_poly_claim(world_); + ecs_fini(world_); + } + } + world_ = nullptr; + } + } + + ~world() { + release(); + } + + /* Implicit conversion to world_t* */ + operator world_t*() const { return world_; } + + /** Make current world object owner of the world. This may only be called on + * one flecs::world object, an may only be called once. Failing to do so + * will result in undefined behavior. + * + * This operation allows a custom (C) world to be wrapped by a C++ object, + * and transfer ownership so that the world is automatically cleaned up. + */ + void make_owner() { + flecs_poly_release(world_); + } + + /** Deletes and recreates the world. */ + void reset() { + /* Make sure there's only one reference to the world */ + ecs_assert(flecs_poly_refcount(world_) == 1, ECS_INVALID_OPERATION, + "reset would invalidate other handles"); + ecs_fini(world_); + world_ = ecs_init(); + } + + /** Obtain pointer to C world object. + */ + world_t* c_ptr() const { + return world_; + } + + /** Signal application should quit. + * After calling this operation, the next call to progress() returns false. + */ + void quit() const { + ecs_quit(world_); + } + + /** Register action to be executed when world is destroyed. + */ + void atfini(ecs_fini_action_t action, void *ctx = nullptr) const { + ecs_atfini(world_, action, ctx); + } + + /** Test if quit() has been called. + */ + bool should_quit() const { + return ecs_should_quit(world_); + } + + /** Begin frame. + * When an application does not use progress() to control the main loop, it + * can still use Flecs features such as FPS limiting and time measurements. + * This operation needs to be invoked whenever a new frame is about to get + * processed. + * + * Calls to frame_begin() must always be followed by frame_end(). + * + * The function accepts a delta_time parameter, which will get passed to + * systems. This value is also used to compute the amount of time the + * function needs to sleep to ensure it does not exceed the target_fps, when + * it is set. When 0 is provided for delta_time, the time will be measured. + * + * This function should only be ran from the main thread. + * + * @param delta_time Time elapsed since the last frame. + * @return The provided delta_time, or measured time if 0 was provided. + * + * @see ecs_frame_begin() + * @see flecs::world::frame_end() + */ + ecs_ftime_t frame_begin(float delta_time = 0) const { + return ecs_frame_begin(world_, delta_time); + } + + /** End frame. + * This operation must be called at the end of the frame, and always after + * frame_begin(). + * + * This function should only be ran from the main thread. + * + * @see ecs_frame_end() + * @see flecs::world::frame_begin() + */ + void frame_end() const { + ecs_frame_end(world_); + } + + /** Begin readonly mode. + * + * @param multi_threaded Whether to enable readonly/multi threaded mode. + * + * @return Whether world is currently readonly. + * + * @see ecs_readonly_begin() + * @see flecs::world::is_readonly() + * @see flecs::world::readonly_end() + */ + bool readonly_begin(bool multi_threaded = false) const { + return ecs_readonly_begin(world_, multi_threaded); + } + + /** End readonly mode. + * + * @see ecs_readonly_end() + * @see flecs::world::is_readonly() + * @see flecs::world::readonly_begin() + */ + void readonly_end() const { + ecs_readonly_end(world_); + } + + /** Defer operations until end of frame. + * When this operation is invoked while iterating, operations inbetween the + * defer_begin() and defer_end() operations are executed at the end of the frame. + * + * This operation is thread safe. + * + * @return true if world changed from non-deferred mode to deferred mode. + * + * @see ecs_defer_begin() + * @see flecs::world::defer() + * @see flecs::world::defer_end() + * @see flecs::world::is_deferred() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() + */ + bool defer_begin() const { + return ecs_defer_begin(world_); + } + + /** End block of operations to defer. + * See defer_begin(). + * + * This operation is thread safe. + * + * @return true if world changed from deferred mode to non-deferred mode. + * + * @see ecs_defer_end() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::is_deferred() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() + */ + bool defer_end() const { + return ecs_defer_end(world_); + } + + /** Test whether deferring is enabled. + * + * @return True if deferred, false if not. + * + * @see ecs_is_deferred() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() + */ + bool is_deferred() const { + return ecs_is_deferred(world_); + } + + /** Configure world to have N stages. + * This initializes N stages, which allows applications to defer operations to + * multiple isolated defer queues. This is typically used for applications with + * multiple threads, where each thread gets its own queue, and commands are + * merged when threads are synchronized. + * + * Note that set_threads() already creates the appropriate number of stages. + * The set_stage_count() operation is useful for applications that want to manage + * their own stages and/or threads. + * + * @param stages The number of stages. + * + * @see ecs_set_stage_count() + * @see flecs::world::get_stage_count() + */ + void set_stage_count(int32_t stages) const { + ecs_set_stage_count(world_, stages); + } + + /** Get number of configured stages. + * Return number of stages set by set_stage_count(). + * + * @return The number of stages used for threading. + * + * @see ecs_get_stage_count() + * @see flecs::world::set_stage_count() + */ + int32_t get_stage_count() const { + return ecs_get_stage_count(world_); + } + + /** Get current stage id. + * The stage id can be used by an application to learn about which stage it + * is using, which typically corresponds with the worker thread id. + * + * @return The stage id. + */ + int32_t get_stage_id() const { + return ecs_stage_get_id(world_); + } + + /** Test if is a stage. + * If this function returns false, it is guaranteed that this is a valid + * world object. + * + * @return True if the world is a stage, false if not. + */ + bool is_stage() const { + ecs_assert( + flecs_poly_is(world_, ecs_world_t) || + flecs_poly_is(world_, ecs_stage_t), + ECS_INVALID_PARAMETER, + "flecs::world instance contains invalid reference to world or stage"); + return flecs_poly_is(world_, ecs_stage_t); + } + + /** Merge world or stage. + * When automatic merging is disabled, an application can call this + * operation on either an individual stage, or on the world which will merge + * all stages. This operation may only be called when staging is not enabled + * (either after progress() or after readonly_end()). + * + * This operation may be called on an already merged stage or world. + * + * @see ecs_merge() + */ + void merge() const { + ecs_merge(world_); + } + + /** Get stage-specific world pointer. + * Flecs threads can safely invoke the API as long as they have a private + * context to write to, also referred to as the stage. This function returns a + * pointer to a stage, disguised as a world pointer. + * + * Note that this function does not(!) create a new world. It simply wraps the + * existing world in a thread-specific context, which the API knows how to + * unwrap. The reason the stage is returned as an ecs_world_t is so that it + * can be passed transparently to the existing API functions, vs. having to + * create a dediated API for threading. + * + * @param stage_id The index of the stage to retrieve. + * @return A thread-specific pointer to the world. + */ + flecs::world get_stage(int32_t stage_id) const { + return flecs::world(ecs_get_stage(world_, stage_id)); + } + + /** Create asynchronous stage. + * An asynchronous stage can be used to asynchronously queue operations for + * later merging with the world. An asynchronous stage is similar to a regular + * stage, except that it does not allow reading from the world. + * + * Asynchronous stages are never merged automatically, and must therefore be + * manually merged with the ecs_merge function. It is not necessary to call + * defer_begin or defer_end before and after enqueuing commands, as an + * asynchronous stage unconditionally defers operations. + * + * The application must ensure that no commands are added to the stage while the + * stage is being merged. + * + * @return The stage. + */ + flecs::world async_stage() const { + ecs_world_t *as = ecs_stage_new(world_); + flecs_poly_release(as); // world object will claim + return flecs::world(as); + } + + /** Get actual world. + * If the current object points to a stage, this operation will return the + * actual world. + * + * @return The actual world. + */ + flecs::world get_world() const { + /* Safe cast, mutability is checked */ + return flecs::world( + world_ ? const_cast(ecs_get_world(world_)) : nullptr); + } + + /** Test whether the current world object is readonly. + * This function allows the code to test whether the currently used world + * object is readonly or whether it allows for writing. + * + * @return True if the world or stage is readonly. + * + * @see ecs_stage_is_readonly() + * @see flecs::world::readonly_begin() + * @see flecs::world::readonly_end() + */ + bool is_readonly() const { + return ecs_stage_is_readonly(world_); + } + + /** Set world context. + * Set a context value that can be accessed by anyone that has a reference + * to the world. + * + * @param ctx A pointer to a user defined structure. + * @param ctx_free A function that is invoked with ctx when the world is freed. + * + * + * @see ecs_set_ctx() + * @see flecs::world::get_ctx() + */ + void set_ctx(void* ctx, ecs_ctx_free_t ctx_free = nullptr) const { + ecs_set_ctx(world_, ctx, ctx_free); + } + + /** Get world context. + * This operation retrieves a previously set world context. + * + * @return The context set with set_binding_ctx(). If no context was set, the + * function returns NULL. + * + * @see ecs_get_ctx() + * @see flecs::world::set_ctx() + */ + void* get_ctx() const { + return ecs_get_ctx(world_); + } + + /** Set world binding context. + * + * Same as set_ctx() but for binding context. A binding context is intended + * specifically for language bindings to store binding specific data. + * + * @param ctx A pointer to a user defined structure. + * @param ctx_free A function that is invoked with ctx when the world is freed. + * + * @see ecs_set_binding_ctx() + * @see flecs::world::get_binding_ctx() + */ + void set_binding_ctx(void* ctx, ecs_ctx_free_t ctx_free = nullptr) const { + ecs_set_binding_ctx(world_, ctx, ctx_free); + } + + /** Get world binding context. + * This operation retrieves a previously set world binding context. + * + * @return The context set with set_binding_ctx(). If no context was set, the + * function returns NULL. + * + * @see ecs_get_binding_ctx() + * @see flecs::world::set_binding_ctx() + */ + void* get_binding_ctx() const { + return ecs_get_binding_ctx(world_); + } + + /** Preallocate memory for number of entities. + * This function preallocates memory for the entity index. + * + * @param entity_count Number of entities to preallocate memory for. + * + * @see ecs_dim() + */ + void dim(int32_t entity_count) const { + ecs_dim(world_, entity_count); + } + + /** Set entity range. + * This function limits the range of issued entity ids between min and max. + * + * @param min Minimum entity id issued. + * @param max Maximum entity id issued. + * + * @see ecs_set_entity_range() + */ + void set_entity_range(entity_t min, entity_t max) const { + ecs_set_entity_range(world_, min, max); + } + + /** Enforce that operations cannot modify entities outside of range. + * This function ensures that only entities within the specified range can + * be modified. Use this function if specific parts of the code only are + * allowed to modify a certain set of entities, as could be the case for + * networked applications. + * + * @param enabled True if range check should be enabled, false if not. + * + * @see ecs_enable_range_check() + */ + void enable_range_check(bool enabled = true) const { + ecs_enable_range_check(world_, enabled); + } + + /** Set current scope. + * + * @param scope The scope to set. + * @return The current scope; + * + * @see ecs_set_scope() + * @see flecs::world::get_scope() + */ + flecs::entity set_scope(const flecs::entity_t scope) const; + + /** Get current scope. + * + * @return The current scope. + * + * @see ecs_get_scope() + * @see flecs::world::set_scope() + */ + flecs::entity get_scope() const; + + /** Same as set_scope but with type. + * + * @see ecs_set_scope() + * @see flecs::world::get_scope() + */ + template + flecs::entity set_scope() const; + + /** Set search path. + * + * @see ecs_set_lookup_path() + * @see flecs::world::lookup() + */ + flecs::entity_t* set_lookup_path(const flecs::entity_t *search_path) const { + return ecs_set_lookup_path(world_, search_path); + } + + /** Lookup entity by name. + * + * @param name Entity name. + * @param recursive When false, only the current scope is searched. + * @result The entity if found, or 0 if not found. + */ + flecs::entity lookup(const char *name, const char *sep = "::", const char *root_sep = "::", bool recursive = true) const; + + /** Set singleton component. + */ + template ::value > = 0> + void set(const T& value) const { + flecs::set(world_, _::type::id(world_), value); + } + + /** Set singleton component. + */ + template ::value > = 0> + void set(T&& value) const { + flecs::set(world_, _::type::id(world_), + FLECS_FWD(value)); + } + + /** Set singleton pair. + */ + template , + typename A = actual_type_t

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

(world_, _::type::id(world_), value); + } + + /** Set singleton pair. + */ + template , + typename A = actual_type_t

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

(world_, _::type::id(world_), FLECS_FWD(value)); + } + + /** Set singleton pair. + */ + template + void set(Second second, const First& value) const; + + /** Set singleton pair. + */ + template + void set(Second second, First&& value) const; + + /** Set singleton component inside a callback. + */ + template ::value > = 0 > + void set(const Func& func) const; + + template + void emplace(Args&&... args) const { + flecs::id_t component_id = _::type::id(world_); + flecs::emplace(world_, component_id, component_id, FLECS_FWD(args)...); + } + + /** Ensure singleton component. + */ + #ifndef ensure + template + T& ensure() const; + #endif + + /** Mark singleton component as modified. + */ + template + void modified() const; + + /** Get ref singleton component. + */ + template + ref get_ref() const; + + /** Get singleton component. + */ + template + const T* get() const; + + /** Get singleton pair. + */ + template , + typename A = actual_type_t

> + const A* get() const; + + /** Get singleton pair. + */ + template + const First* get(Second second) const; + + /** Get singleton component inside a callback. + */ + template ::value > = 0 > + void get(const Func& func) const; + + /** Get mutable singleton component. + */ + template + T* get_mut() const; + + /** Get mutable singleton pair. + */ + template , + typename A = actual_type_t

> + A* get_mut() const; + + /** Get mutable singleton pair. + */ + template + First* get_mut(Second second) const; + + /** Test if world has singleton component. + */ + template + bool has() const; + + /** Test if world has the provided pair. + * + * @tparam First The first element of the pair + * @tparam Second The second element of the pair + */ + template + bool has() const; + + /** Test if world has the provided pair. + * + * @tparam First The first element of the pair + * @param second The second element of the pair. + */ + template + bool has(flecs::id_t second) const; + + /** Test if world has the provided pair. + * + * @param first The first element of the pair + * @param second The second element of the pair + */ + bool has(flecs::id_t first, flecs::id_t second) const; + + /** Add singleton component. + */ + template + void add() const; + + /** Adds a pair to the singleton component. + * + * @tparam First The first element of the pair + * @tparam Second The second element of the pair + */ + template + void add() const; + + /** Adds a pair to the singleton component. + * + * @tparam First The first element of the pair + * @param second The second element of the pair. + */ + template + void add(flecs::entity_t second) const; + + /** Adds a pair to the singleton entity. + * + * @param first The first element of the pair + * @param second The second element of the pair + */ + void add(flecs::entity_t first, flecs::entity_t second) const; + + /** Remove singleton component. + */ + template + void remove() const; + + /** Removes the pair singleton component. + * + * @tparam First The first element of the pair + * @tparam Second The second element of the pair + */ + template + void remove() const; + + /** Removes the pair singleton component. + * + * @tparam First The first element of the pair + * @param second The second element of the pair. + */ + template + void remove(flecs::entity_t second) const; + + /** Removes the pair singleton component. + * + * @param first The first element of the pair + * @param second The second element of the pair + */ + void remove(flecs::entity_t first, flecs::entity_t second) const; + + /** Iterate entities in root of world + * Accepts a callback with the following signature: + * + * @code + * void(*)(flecs::entity e); + * @endcode + */ + template + void children(Func&& f) const; + + /** Get singleton entity for type. + */ + template + flecs::entity singleton() const; + + /** Get target for a given pair from a singleton entity. + * This operation returns the target for a given pair. The optional + * index can be used to iterate through targets, in case the entity has + * multiple instances for the same relationship. + * + * @tparam First The first element of the pair. + * @param index The index (0 for the first instance of the relationship). + */ + template + flecs::entity target(int32_t index = 0) const; + + /** Get target for a given pair from a singleton entity. + * This operation returns the target for a given pair. The optional + * index can be used to iterate through targets, in case the entity has + * multiple instances for the same relationship. + * + * @param first The first element of the pair for which to retrieve the target. + * @param index The index (0 for the first instance of the relationship). + */ + template + flecs::entity target(flecs::entity_t first, int32_t index = 0) const; + + /** Get target for a given pair from a singleton entity. + * This operation returns the target for a given pair. The optional + * index can be used to iterate through targets, in case the entity has + * multiple instances for the same relationship. + * + * @param first The first element of the pair for which to retrieve the target. + * @param index The index (0 for the first instance of the relationship). + */ + flecs::entity target(flecs::entity_t first, int32_t index = 0) const; + + /** Create alias for component. + * + * @tparam T to create an alias for. + * @param alias Alias for the component. + * @return Entity representing the component. + */ + template + flecs::entity use(const char *alias = nullptr) const; + + /** Create alias for entity. + * + * @param name Name of the entity. + * @param alias Alias for the entity. + */ + flecs::entity use(const char *name, const char *alias = nullptr) const; + + /** Create alias for entity. + * + * @param entity Entity for which to create the alias. + * @param alias Alias for the entity. + */ + void use(flecs::entity entity, const char *alias = nullptr) const; + + /** Count entities matching a component. + * + * @param component_id The component id. + */ + int count(flecs::id_t component_id) const { + return ecs_count_id(world_, component_id); + } + + /** Count entities matching a pair. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + int count(flecs::entity_t first, flecs::entity_t second) const { + return ecs_count_id(world_, ecs_pair(first, second)); + } + + /** Count entities matching a component. + * + * @tparam T The component type. + */ + template + int count() const { + return count(_::type::id(world_)); + } + + /** Count entities matching a pair. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + */ + template + int count(flecs::entity_t second) const { + return count(_::type::id(world_), second); + } + + /** Count entities matching a pair. + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + */ + template + int count() const { + return count( + _::type::id(world_), + _::type::id(world_)); + } + + /** All entities created in function are created with id. + */ + template + void with(id_t with_id, const Func& func) const { + ecs_id_t prev = ecs_set_with(world_, with_id); + func(); + ecs_set_with(world_, prev); + } + + /** All entities created in function are created with type. + */ + template + void with(const Func& func) const { + with(this->id(), func); + } + + /** All entities created in function are created with pair. + */ + template + void with(const Func& func) const { + with(ecs_pair(this->id(), this->id()), func); + } + + /** All entities created in function are created with pair. + */ + template + void with(id_t second, const Func& func) const { + with(ecs_pair(this->id(), second), func); + } + + /** All entities created in function are created with pair. + */ + template + void with(id_t first, id_t second, const Func& func) const { + with(ecs_pair(first, second), func); + } + + /** All entities created in function are created in scope. All operations + * called in function (such as lookup) are relative to scope. + */ + template + void scope(id_t parent, const Func& func) const { + ecs_entity_t prev = ecs_set_scope(world_, parent); + func(); + ecs_set_scope(world_, prev); + } + + /** Same as scope(parent, func), but with T as parent. + */ + template + void scope(const Func& func) const { + flecs::id_t parent = _::type::id(world_); + scope(parent, func); + } + + /** Use provided scope for operations ran on returned world. + * Operations need to be ran in a single statement. + */ + flecs::scoped_world scope(id_t parent) const; + + template + flecs::scoped_world scope() const; + + flecs::scoped_world scope(const char* name) const; + + /** Delete all entities with specified id. */ + void delete_with(id_t the_id) const { + ecs_delete_with(world_, the_id); + } + + /** Delete all entities with specified pair. */ + void delete_with(entity_t first, entity_t second) const { + delete_with(ecs_pair(first, second)); + } + + /** Delete all entities with specified component. */ + template + void delete_with() const { + delete_with(_::type::id(world_)); + } + + /** Delete all entities with specified pair. */ + template + void delete_with() const { + delete_with(_::type::id(world_), _::type::id(world_)); + } + + /** Delete all entities with specified pair. */ + template + void delete_with(entity_t second) const { + delete_with(_::type::id(world_), second); + } + + /** Remove all instances of specified id. */ + void remove_all(id_t the_id) const { + ecs_remove_all(world_, the_id); + } + + /** Remove all instances of specified pair. */ + void remove_all(entity_t first, entity_t second) const { + remove_all(ecs_pair(first, second)); + } + + /** Remove all instances of specified component. */ + template + void remove_all() const { + remove_all(_::type::id(world_)); + } + + /** Remove all instances of specified pair. */ + template + void remove_all() const { + remove_all(_::type::id(world_), _::type::id(world_)); + } + + /** Remove all instances of specified pair. */ + template + void remove_all(entity_t second) const { + remove_all(_::type::id(world_), second); + } + + /** Defer all operations called in function. + * + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_is_deferred() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() + */ + template + void defer(const Func& func) const { + ecs_defer_begin(world_); + func(); + ecs_defer_end(world_); + } + + /** Suspend deferring operations. + * + * @see ecs_defer_suspend() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_is_deferred() + * @see flecs::world::defer_resume() + */ + void defer_suspend() const { + ecs_defer_suspend(world_); + } + + /** Resume deferring operations. + * + * @see ecs_defer_resume() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_is_deferred() + * @see flecs::world::defer_suspend() + */ + void defer_resume() const { + ecs_defer_resume(world_); + } + + /** Check if entity id exists in the world. + * + * @see ecs_exists() + * @see flecs::world::is_alive() + * @see flecs::world::is_valid() + */ + bool exists(flecs::entity_t e) const { + return ecs_exists(world_, e); + } + + /** Check if entity id exists in the world. + * + * @see ecs_is_alive() + * @see flecs::world::exists() + * @see flecs::world::is_valid() + */ + bool is_alive(flecs::entity_t e) const { + return ecs_is_alive(world_, e); + } + + /** Check if entity id is valid. + * Invalid entities cannot be used with API functions. + * + * @see ecs_is_valid() + * @see flecs::world::exists() + * @see flecs::world::is_alive() + */ + bool is_valid(flecs::entity_t e) const { + return ecs_is_valid(world_, e); + } + + /** Get alive entity for id. + * Returns the entity with the current generation. + * + * @see ecs_get_alive() + */ + flecs::entity get_alive(flecs::entity_t e) const; + + /** + * @see ecs_make_alive() + */ + flecs::entity make_alive(flecs::entity_t e) const; + + /* Run callback after completing frame */ + void run_post_frame(ecs_fini_action_t action, void *ctx) const { + ecs_run_post_frame(world_, action, ctx); + } + + /** Get the world info. + * + * @see ecs_get_world_info() + */ + const flecs::world_info_t* get_info() const{ + return ecs_get_world_info(world_); + } + + /** Get delta_time */ + ecs_ftime_t delta_time() const { + return get_info()->delta_time; + } + +/** + * @file addons/cpp/mixins/id/mixin.inl + * @brief Id world mixin. + */ + +/** Get id from a type. + * + * @memberof flecs::world + */ +template +flecs::id id() const; + +/** Id factory. + * + * @memberof flecs::world + */ +template +flecs::id id(Args&&... args) const; + +/** Get pair id from relationship, object. + * + * @memberof flecs::world + */ +template +flecs::id pair() const; + +/** Get pair id from relationship, object. + * + * @memberof flecs::world + */ +template +flecs::id pair(entity_t o) const; + +/** Get pair id from relationship, object. + * + * @memberof flecs::world + */ +flecs::id pair(entity_t r, entity_t o) const; + +/** + * @file addons/cpp/mixins/component/mixin.inl + * @brief Component mixin. + */ + +/** Find or register component. + * + * @ingroup cpp_components + * @memberof flecs::world + */ +template +flecs::component component(Args &&... args) const; + +/** Find or register untyped component. + * Method available on flecs::world class. + * + * @ingroup cpp_components + * @memberof flecs::world + */ +template +flecs::untyped_component component(Args &&... args) const; + +/** + * @file addons/cpp/mixins/entity/mixin.inl + * @brief Entity world mixin. + */ + +/** Create an entity. + * + * @memberof flecs::world + * @ingroup cpp_entities + */ +template +flecs::entity entity(Args &&... args) const; + +/** Convert enum constant to entity. + * + * @memberof flecs::world + * @ingroup cpp_entities + */ +template ::value > = 0> +flecs::id id(E value) const; + +/** Convert enum constant to entity. + * + * @memberof flecs::world + * @ingroup cpp_entities + */ +template ::value > = 0> +flecs::entity entity(E value) const; + +/** Create a prefab. + * + * @memberof flecs::world + * @ingroup cpp_entities + */ +template +flecs::entity prefab(Args &&... args) const; + +/** Create an entity that's associated with a type. + * + * @memberof flecs::world + * @ingroup cpp_entities + */ +template +flecs::entity entity(const char *name = nullptr) const; + +/** Create a prefab that's associated with a type. + * + * @memberof flecs::world + * @ingroup cpp_entities + */ +template +flecs::entity prefab(const char *name = nullptr) const; + +/** + * @file addons/cpp/mixins/event/mixin.inl + * @brief Event world mixin. + */ + +/** + * @defgroup cpp_addons_event Events + * @ingroup cpp_addons + * API for emitting events. + * + * @{ + */ + +/** Create a new event. + * + * @memberof flecs::world + * + * @param evt The event id. + * @return Event builder. + */ +flecs::event_builder event(flecs::entity_t evt) const; + +/** Create a new event. + * + * @memberof flecs::world + * + * @tparam E The event type. + * @return Event builder. + */ +template +flecs::event_builder_typed event() const; + +/** @} */ + +/** + * @file addons/cpp/mixins/term/mixin.inl + * @brief Term world mixin. + */ + +/** + * @memberof flecs::world + * @ingroup cpp_core_queries + * + * @{ + */ + +/** Create a term. + * + */ +template +flecs::term term(Args &&... args) const; + +/** Create a term for a (component) type. + */ +template +flecs::term term() const; + +/** Create a term for a pair. + */ +template +flecs::term term() const; + +/** @} */ + +/** + * @file addons/cpp/mixins/observer/mixin.inl + * @brief Observer world mixin. + */ + +/** Observer builder. + * + * @memberof flecs::world + * @ingroup cpp_observers + * + * @{ + */ + +/** Upcast entity to an observer. + * The provided entity must be an observer. + * + * @param e The entity. + * @return An observer object. + */ +flecs::observer observer(flecs::entity e) const; + +/** Create a new observer. + * + * @tparam Components The components to match on. + * @tparam Args Arguments passed to the constructor of flecs::observer_builder. + * @return Observer builder. + */ +template +flecs::observer_builder observer(Args &&... args) const; + +/** @} */ + +/** + * @file addons/cpp/mixins/query/mixin.inl + * @brief Query world mixin. + */ + +/** + * @memberof flecs::world + * @ingroup cpp_core_queries + * + * @{ + */ + +/** Create a query. + * + * @see ecs_query_init + */ +template +flecs::query query(Args &&... args) const; + +/** Create a query from entity. + * + * @see ecs_query_init + */ +flecs::query<> query(flecs::entity query_entity) const; + +/** Create a query builder. + * + * @see ecs_query_init + */ +template +flecs::query_builder query_builder(Args &&... args) const; + +/** Iterate over all entities with components in argument list of function. + * The function parameter must match the following signature: + * + * @code + * void(*)(T&, U&, ...) + * @endcode + * + * or: + * + * @code + * void(*)(flecs::entity, T&, U&, ...) + * @endcode + * + */ +template +void each(Func&& func) const; + +/** Iterate over all entities with provided component. + * The function parameter must match the following signature: + * + * @code + * void(*)(T&) + * @endcode + * + * or: + * + * @code + * void(*)(flecs::entity, T&) + * @endcode + * + */ +template +void each(Func&& func) const; + +/** Iterate over all entities with provided (component) id. */ +template +void each(flecs::id_t term_id, Func&& func) const; + +/** @} */ + +/** + * @file addons/cpp/mixins/enum/mixin.inl + * @brief Enum world mixin. + */ + +/** Convert enum constant to entity. + * + * @memberof flecs::world + * @ingroup cpp_entities + */ +template ::value > = 0> +flecs::entity to_entity(E constant) const; + + +# ifdef FLECS_MODULE +/** + * @file addons/cpp/mixins/module/mixin.inl + * @brief Module world mixin. + */ + +/** + * @memberof flecs::world + * @ingroup cpp_addons_modules + * + * @{ + */ + +/** Define a module. + * This operation is not mandatory, but can be called inside the module ctor to + * obtain the entity associated with the module, or override the module name. + * + * @tparam Module module class. + * @return Module entity. + */ +template +flecs::entity module(const char *name = nullptr) const; + +/** Import a module. + * + * @tparam Module module class. + * @return Module entity. + */ +template +flecs::entity import(); + +/** @} */ + +# endif +# ifdef FLECS_PIPELINE +/** + * @file addons/cpp/mixins/pipeline/mixin.inl + * @brief Pipeline world mixin. + */ + +/** + * @memberof flecs::world + * @ingroup cpp_pipelines + * + * @{ + */ + +/** Create a new pipeline. + * + * @return A pipeline builder. + */ +flecs::pipeline_builder<> pipeline() const; + +/** Create a new pipeline. + * + * @tparam Pipeline Type associated with pipeline. + * @return A pipeline builder. + */ +template ::value > = 0> +flecs::pipeline_builder<> pipeline() const; + +/** Set pipeline. + * @see ecs_set_pipeline + */ +void set_pipeline(const flecs::entity pip) const; + +/** Set pipeline. + * @see ecs_set_pipeline + */ +template +void set_pipeline() const; + +/** Get pipeline. + * @see ecs_get_pipeline + */ +flecs::entity get_pipeline() const; + +/** Progress world one tick. + * @see ecs_progress + */ +bool progress(ecs_ftime_t delta_time = 0.0) const; + +/** Run pipeline. + * @see ecs_run_pipeline + */ +void run_pipeline(const flecs::entity_t pip, ecs_ftime_t delta_time = 0.0) const; + +/** Run pipeline. + * @tparam Pipeline Type associated with pipeline. + * @see ecs_run_pipeline + */ +template ::value > = 0> +void run_pipeline(ecs_ftime_t delta_time = 0.0) const; + +/** Set timescale. + * @see ecs_set_time_scale + */ +void set_time_scale(ecs_ftime_t mul) const; + +/** Set target FPS. + * @see ecs_set_target_fps + */ +void set_target_fps(ecs_ftime_t target_fps) const; + +/** Reset simulation clock. + * @see ecs_reset_clock + */ +void reset_clock() const; + +/** Set number of threads. + * @see ecs_set_threads + */ +void set_threads(int32_t threads) const; + +/** Set number of threads. + * @see ecs_get_stage_count + */ +int32_t get_threads() const; + +/** Set number of task threads. + * @see ecs_set_task_threads + */ +void set_task_threads(int32_t task_threads) const; + +/** Returns true if task thread use has been requested. + * @see ecs_using_task_threads + */ +bool using_task_threads() const; + +/** @} */ + +# endif +# ifdef FLECS_SYSTEM +/** + * @file addons/cpp/mixins/system/mixin.inl + * @brief System module world mixin. + */ + +/** + * @memberof flecs::world + * @ingroup cpp_addons_systems + * + * @{ +*/ + +/** Upcast entity to a system. + * The provided entity must be a system. + * + * @param e The entity. + * @return A system object. + */ +flecs::system system(flecs::entity e) const; + +/** Create a new system. + * + * @tparam Components The components to match on. + * @tparam Args Arguments passed to the constructor of flecs::system_builder. + * @return System builder. + */ +template +flecs::system_builder system(Args &&... args) const; + +/** @} */ + +# endif +# ifdef FLECS_TIMER +/** + * @file addons/cpp/mixins/timer/mixin.inl + * @brief Timer module mixin. + */ + +/** + * @memberof flecs::world + * @ingroup cpp_addons_timer + */ + +/** Find or register a singleton timer. */ +template +flecs::timer timer() const; + +/** Find or register a timer. */ +template +flecs::timer timer(Args &&... args) const; + +/** Enable randomization of initial time values for timers. + * @see ecs_randomize_timers + */ +void randomize_timers() const; + +# endif +# ifdef FLECS_SCRIPT +/** + * @file addons/cpp/mixins/script/mixin.inl + * @brief Script world mixin. + */ + +/** + * @defgroup cpp_addons_script Script + * @ingroup cpp_addons + * Data definition format for loading entity data. + * + * @{ + */ + +/** Run script. + * @see ecs_script_run + */ +int script_run(const char *name, const char *str) const { + return ecs_script_run(world_, name, str); +} + +/** Run script from file. + * @see ecs_script_run_file + */ +int script_run_file(const char *filename) const { + return ecs_script_run_file(world_, filename); +} + +/** Build script. + * @see ecs_script_init + */ +script_builder script(const char *name = nullptr) const { + return script_builder(world_, name); +} + +/** Convert value to string */ +flecs::string to_expr(flecs::entity_t tid, const void* value) { + char *expr = ecs_ptr_to_expr(world_, tid, value); + return flecs::string(expr); +} + +/** Convert value to string */ +template +flecs::string to_expr(const T* value) { + flecs::entity_t tid = _::type::id(world_); + return to_expr(tid, value); +} + + +/** @} */ + +# endif +# ifdef FLECS_META +/** + * @file addons/cpp/mixins/meta/world.inl + * @brief Meta world mixin. + */ + +/** + * @memberof flecs::world + * @ingroup cpp_addons_meta + * + * @{ + */ + +/** Return meta cursor to value */ +flecs::cursor cursor(flecs::entity_t tid, void *ptr) { + return flecs::cursor(world_, tid, ptr); +} + +/** Return meta cursor to value */ +template +flecs::cursor cursor(void *ptr) { + flecs::entity_t tid = _::type::id(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 +# ifdef FLECS_JSON +/** + * @file addons/cpp/mixins/json/world.inl + * @brief JSON world mixin. + */ + +/** Serialize untyped value to JSON. + * + * @memberof flecs::world + * @ingroup cpp_addons_json + */ +flecs::string to_json(flecs::entity_t tid, const void* value) { + char *json = ecs_ptr_to_json(world_, tid, value); + return flecs::string(json); +} + +/** Serialize value to JSON. + * + * @memberof flecs::world + * @ingroup cpp_addons_json + */ +template +flecs::string to_json(const T* value) { + flecs::entity_t tid = _::type::id(world_); + 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(world_, nullptr) ); +} + +/** Deserialize value from JSON. + * + * @memberof flecs::world + * @ingroup cpp_addons_json + */ +const char* from_json(flecs::entity_t tid, void* value, const char *json, flecs::from_json_desc_t *desc = nullptr) { + return ecs_ptr_from_json(world_, tid, value, json, desc); +} + +/** Deserialize value from JSON. + * + * @memberof flecs::world + * @ingroup cpp_addons_json + */ +template +const char* from_json(T* value, const char *json, flecs::from_json_desc_t *desc = nullptr) { + return ecs_ptr_from_json(world_, _::type::id(world_), + value, json, desc); +} + +/** Deserialize JSON into world. + * + * @memberof flecs::world + * @ingroup cpp_addons_json + */ +const char* from_json(const char *json, flecs::from_json_desc_t *desc = nullptr) { + return ecs_world_from_json(world_, json, desc); +} + +/** Deserialize JSON file into world. + * + * @memberof flecs::world + * @ingroup cpp_addons_json + */ +const char* from_json_file(const char *json, flecs::from_json_desc_t *desc = nullptr) { + return ecs_world_from_json_file(world_, json, desc); +} + +# endif +# ifdef FLECS_APP +/** + * @file addons/cpp/mixins/app/mixin.inl + * @brief App world addon mixin. + */ + +/** + * @ingroup cpp_addons_app + * @memberof flecs::world + * + * @{ + */ + +/** Return app builder. + * The app builder is a convenience wrapper around a loop that runs + * world::progress. An app allows for writing platform agnostic code, + * as it provides hooks to modules for overtaking the main loop which is + * required for frameworks like emscripten. + */ +flecs::app_builder app() { + flecs::world_t *w = world_; + world_ = nullptr; // Take ownership + return flecs::app_builder(w); +} + +/** @} */ + +# endif +# ifdef FLECS_METRICS + +/** Create metric. + * + * @ingroup cpp_addons_metrics + * @memberof flecs::world + */ +template +flecs::metric_builder metric(Args &&... args) const; + +# endif +# ifdef FLECS_ALERTS + +/** Create alert. + * + * @ingroup cpp_addons_alerts + * @memberof flecs::world + */ +template +flecs::alert_builder alert(Args &&... args) const; + +# endif + +public: + void init_builtin_components(); + + world_t *world_; +}; + +/** Scoped world. + * Utility class used by the world::scope method to create entities in a scope. + */ +struct scoped_world : world { + scoped_world( + flecs::world_t *w, + flecs::entity_t s) : world(w) + { + prev_scope_ = ecs_set_scope(w, s); + } + + ~scoped_world() { + ecs_set_scope(world_, prev_scope_); + } + + scoped_world(const scoped_world& obj) : world(nullptr) { + prev_scope_ = obj.prev_scope_; + world_ = obj.world_; + flecs_poly_claim(world_); + } + + flecs::entity_t prev_scope_; +}; + +/** @} */ + +} // namespace flecs + + +/** + * @file addons/cpp/field.hpp + * @brief Wrapper classes for fields returned by flecs::iter. + */ + +#pragma once + +/** + * @defgroup cpp_field Fields + * @ingroup cpp_core + * Field helper types. + * + * @{ + */ + +namespace flecs +{ + +/** Unsafe wrapper class around a field. + * This class can be used when a system does not know the type of a field at + * compile time. + * + * @ingroup cpp_iterator + */ +struct untyped_field { + untyped_field(void* array, size_t size, size_t count, bool is_shared = false) + : data_(array) + , size_(size) + , count_(count) + , is_shared_(is_shared) {} + + /** Return element in component array. + * This operator may only be used if the field is not shared. + * + * @param index Index of element. + * @return Reference to element. + */ + void* operator[](size_t index) const { + ecs_assert(!is_shared_, ECS_INVALID_PARAMETER, + "invalid usage of [] operator for shared component field"); + ecs_assert(index < count_, ECS_COLUMN_INDEX_OUT_OF_RANGE, + "index %d out of range for field", index); + return ECS_OFFSET(data_, size_ * index); + } + +protected: + void* data_; + size_t size_; + size_t count_; + bool is_shared_; +}; + +/** Wrapper class around a field. + * + * @tparam T component type of the field. + * + * @ingroup cpp_iterator + */ +template +struct field { + static_assert(std::is_empty::value == false, + "invalid type for field, cannot iterate empty type"); + + /** Create field from component array. + * + * @param array Pointer to the component array. + * @param count Number of elements in component array. + * @param is_shared Is the component shared or not. + */ + field(T* array, size_t count, bool is_shared = false) + : data_(array) + , count_(count) + , is_shared_(is_shared) {} + + /** Create field from iterator. + * + * @param iter Iterator object. + * @param field Index of the signature of the query being iterated over. + */ + field(iter &iter, int field); + + /** Return element in component array. + * This operator may only be used if the field is not shared. + * + * @param index Index of element. + * @return Reference to element. + */ + T& operator[](size_t index) const; + + /** Return first element of component array. + * This operator is typically used when the field is shared. + * + * @return Reference to the first element. + */ + T& operator*() const; + + /** Return first element of component array. + * This operator is typically used when the field is shared. + * + * @return Pointer to the first element. + */ + T* operator->() const; + +protected: + T* data_; + size_t count_; + bool is_shared_; +}; + +} // namespace flecs + +/** @} */ + +/** + * @file addons/cpp/iter.hpp + * @brief Wrapper classes for ecs_iter_t and component arrays. + */ + +#pragma once + +/** + * @defgroup cpp_iterator Iterators + * @ingroup cpp_core + * Iterator operations. + * + * @{ + */ + +namespace flecs +{ + +//////////////////////////////////////////////////////////////////////////////// + +namespace _ { + +//////////////////////////////////////////////////////////////////////////////// + +/** Iterate over an integer range (used to iterate over entity range). + * + * @tparam T of the iterator + */ +template +struct range_iterator +{ + explicit range_iterator(T value) + : value_(value){} + + bool operator!=(range_iterator const& other) const + { + return value_ != other.value_; + } + + T const& operator*() const + { + return value_; + } + + range_iterator& operator++() + { + ++value_; + return *this; + } + +private: + T value_; +}; + +} // namespace _ + +} // namespace flecs + +namespace flecs +{ + +//////////////////////////////////////////////////////////////////////////////// + +/** Class for iterating over query results. + * + * @ingroup cpp_iterator + */ +struct iter { +private: + using row_iterator = _::range_iterator; + +public: + /** Construct iterator from C iterator object. + * This operation is typically not invoked directly by the user. + * + * @param it Pointer to C iterator. + */ + iter(ecs_iter_t *it) : iter_(it) { } + + row_iterator begin() const { + return row_iterator(0); + } + + row_iterator end() const { + return row_iterator(static_cast(iter_->count)); + } + + flecs::entity system() const; + + flecs::entity event() const; + + flecs::id event_id() const; + + flecs::world world() const; + + const flecs::iter_t* c_ptr() const { + return iter_; + } + + size_t count() const { + ecs_check(iter_->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + return static_cast(iter_->count); + error: + return 0; + } + + ecs_ftime_t delta_time() const { + return iter_->delta_time; + } + + ecs_ftime_t delta_system_time() const { + return iter_->delta_system_time; + } + + flecs::type type() const; + + flecs::table table() const; + + flecs::table other_table() const; + + flecs::table_range range() const; + + /** Access ctx. + * ctx contains the context pointer assigned to a system. + */ + void* ctx() { + return iter_->ctx; + } + + /** Access ctx. + * ctx contains the context pointer assigned to a system. + */ + template + T* ctx() { + return static_cast(iter_->ctx); + } + + /** Access param. + * param contains the pointer passed to the param argument of system::run + */ + void* param() { + return iter_->param; + } + + /** Access param. + * param contains the pointer passed to the param argument of system::run + */ + template + T* param() { + /* TODO: type check */ + return static_cast(iter_->param); + } + + /** Obtain mutable handle to entity being iterated over. + * + * @param row Row being iterated over. + */ + flecs::entity entity(size_t row) const; + + /** Returns whether field is matched on self. + * + * @param index The field index. + */ + bool is_self(int8_t index) const { + return ecs_field_is_self(iter_, index); + } + + /** Returns whether field is set. + * + * @param index The field index. + */ + bool is_set(int8_t index) const { + return ecs_field_is_set(iter_, index); + } + + /** Returns whether field is readonly. + * + * @param index The field index. + */ + bool is_readonly(int8_t index) const { + return ecs_field_is_readonly(iter_, index); + } + + /** Number of fields in iterator. + */ + int32_t field_count() const { + return iter_->field_count; + } + + /** Size of field data type. + * + * @param index The field id. + */ + size_t size(int8_t index) const { + return ecs_field_size(iter_, index); + } + + /** Obtain field source (0 if This). + * + * @param index The field index. + */ + flecs::entity src(int8_t index) const; + + /** Obtain id matched for field. + * + * @param index The field index. + */ + flecs::id id(int8_t index) const; + + /** Obtain pair id matched for field. + * This operation will fail if the id is not a pair. + * + * @param index The field index. + */ + flecs::id pair(int8_t index) const; + + /** Obtain column index for field. + * + * @param index The field index. + */ + int32_t column_index(int8_t index) const { + return ecs_field_column(iter_, index); + } + + /** Obtain term that triggered an observer + */ + int8_t term_index() const { + return iter_->term_index; + } + + /** Convert current iterator result to string. + */ + flecs::string str() const { + char *s = ecs_iter_str(iter_); + return flecs::string(s); + } + + /** Get readonly access to field data. + * If the specified field index does not match with the provided type, the + * function will assert. + * + * @tparam T Type of the field. + * @param index The field index. + * @return The field data. + */ + template , + typename std::enable_if::value, void>::type* = nullptr> + flecs::field field(int8_t index) const; + + /** Get read/write access to field data. + * If the matched id for the specified field does not match with the provided + * type or if the field is readonly, the function will assert. + * + * @tparam T Type of the field. + * @param index The field index. + * @return The field data. + */ + template , + typename std::enable_if< + std::is_const::value == false, void>::type* = nullptr> + flecs::field field(int8_t index) const; + + /** Get unchecked access to field data. + * Unchecked access is required when a system does not know the type of a + * field at compile time. + * + * @param index The field index. + */ + flecs::untyped_field field(int8_t index) const { + ecs_assert(!(iter_->flags & EcsIterCppEach), ECS_INVALID_OPERATION, + "cannot .field from .each, use .field_at(%d, row) instead", index); + return get_unchecked_field(index); + } + + /** Get pointer to field at row. */ + void* field_at(int8_t index, size_t row) const { + if (iter_->row_fields & (1llu << index)) { + return get_unchecked_field_at(index, row)[0]; + } else { + return get_unchecked_field(index)[row]; + } + } + + /** Get reference to field at row. */ + template , + typename std::enable_if::value, void>::type* = nullptr> + const A& field_at(int8_t index, size_t row) const { + if (iter_->row_fields & (1llu << index)) { + return get_field_at(index, row)[0]; + } else { + return get_field(index)[row]; + } + } + + /** Get reference to field at row. */ + template , + typename std::enable_if< + std::is_const::value == false, void>::type* = nullptr> + A& field_at(int8_t index, size_t row) const { + ecs_assert(!ecs_field_is_readonly(iter_, index), + ECS_ACCESS_VIOLATION, NULL); + if (iter_->row_fields & (1llu << index)) { + return get_field_at(index, row)[0]; + } else { + return get_field(index)[row]; + } + } + + /** Get readonly access to entity ids. + * + * @return The entity ids. + */ + flecs::field entities() const { + return flecs::field( + iter_->entities, static_cast(iter_->count), false); + } + + /** Check if the current table has changed since the last iteration. + * Can only be used when iterating queries and/or systems. */ + bool changed() { + return ecs_iter_changed(iter_); + } + + /** Skip current table. + * This indicates to the query that the data in the current table is not + * modified. By default, iterating a table with a query will mark the + * iterated components as dirty if they are annotated with InOut or Out. + * + * When this operation is invoked, the components of the current table will + * not be marked dirty. */ + void skip() { + ecs_iter_skip(iter_); + } + + /* Return group id for current table (grouped queries only) */ + uint64_t group_id() const { + return iter_->group_id; + } + + /** Get value of variable by id. + * Get value of a query variable for current result. + */ + flecs::entity get_var(int var_id) const; + + /** Get value of variable by name. + * Get value of a query variable for current result. + */ + flecs::entity get_var(const char *name) const; + + /** Progress iterator. + * This operation should only be called from a context where the iterator is + * not being progressed automatically. An example of a valid context is + * inside of a run() callback. An example of an invalid context is inside of + * an each() callback. + */ + bool next() { + if (iter_->flags & EcsIterIsValid && iter_->table) { + ECS_TABLE_UNLOCK(iter_->world, iter_->table); + } + bool result = iter_->next(iter_); + iter_->flags |= EcsIterIsValid; + if (result && iter_->table) { + ECS_TABLE_LOCK(iter_->world, iter_->table); + } + return result; + } + + /** Forward to each. + * If a system has an each callback registered, this operation will forward + * the current iterator to the each callback. + */ + void each() { + iter_->callback(iter_); + } + + /** Iterate targets for pair field. + * + * @param index The field index. + * @param func Callback invoked for each target + */ + template + void targets(int8_t index, const Func& func); + + /** Free iterator resources. + * This operation only needs to be called when the iterator is not iterated + * until completion (e.g. the last call to next() did not return false). + * + * Failing to call this operation on an unfinished iterator will throw a + * fatal LEAK_DETECTED error. + * + * @see ecs_iter_fini() + */ + void fini() { + if (iter_->flags & EcsIterIsValid && iter_->table) { + ECS_TABLE_UNLOCK(iter_->world, iter_->table); + } + ecs_iter_fini(iter_); + } + +private: + /* Get field, check if correct type is used */ + template > + flecs::field get_field(int8_t index) const { + +#ifndef FLECS_NDEBUG + ecs_entity_t term_id = ecs_field_id(iter_, index); + ecs_assert(ECS_HAS_ID_FLAG(term_id, PAIR) || + term_id == _::type::id(iter_->world), + ECS_COLUMN_TYPE_MISMATCH, NULL); +#endif + + size_t count; + bool is_shared = !ecs_field_is_self(iter_, index); + + /* If a shared column is retrieved with 'column', there will only be a + * single value. Ensure that the application does not accidentally read + * out of bounds. */ + if (is_shared) { + count = 1; + } else { + /* If column is owned, there will be as many values as there are + * entities. */ + count = static_cast(iter_->count); + } + + return flecs::field( + static_cast(ecs_field_w_size(iter_, sizeof(A), index)), + count, is_shared); + } + + /* Get field, check if correct type is used */ + template > + flecs::field get_field_at(int8_t index, int32_t row) const { + +#ifndef FLECS_NDEBUG + ecs_entity_t term_id = ecs_field_id(iter_, index); + ecs_assert(ECS_HAS_ID_FLAG(term_id, PAIR) || + term_id == _::type::id(iter_->world), + ECS_COLUMN_TYPE_MISMATCH, NULL); +#endif + + return flecs::field( + static_cast(ecs_field_at_w_size(iter_, sizeof(A), index, row)), + 1, false); + } + + flecs::untyped_field get_unchecked_field(int8_t index) const { + size_t count; + size_t size = ecs_field_size(iter_, index); + bool is_shared = !ecs_field_is_self(iter_, index); + + /* If a shared column is retrieved with 'column', there will only be a + * single value. Ensure that the application does not accidentally read + * out of bounds. */ + if (is_shared) { + count = 1; + } else { + /* If column is owned, there will be as many values as there are + * entities. */ + count = static_cast(iter_->count); + } + + return flecs::untyped_field( + ecs_field_w_size(iter_, 0, index), size, count, is_shared); + } + + flecs::untyped_field get_unchecked_field_at(int8_t index, size_t row) const { + size_t size = ecs_field_size(iter_, index); + return flecs::untyped_field( + ecs_field_at_w_size(iter_, 0, index, static_cast(row)), + size, 1, false); + } + + flecs::iter_t *iter_; +}; + +} // namespace flecs + +/** @} */ + +/** + * @file addons/cpp/entity.hpp + * @brief Entity class. + * + * This class provides read/write access to entities. + */ + +#pragma once + +/** + * @file addons/cpp/entity_view.hpp + * @brief Entity class with only readonly operations. + * + * This class provides readonly access to entities. Using this class to store + * entities in components ensures valid handles, as this class will always store + * the actual world vs. a stage. The constructors of this class will never + * create a new entity. + * + * To obtain a mutable handle to the entity, use the "mut" function. + */ + +#pragma once + +/** + * @ingroup cpp_entities + * @{ + */ + +namespace flecs +{ + +/** Entity view. + * Class with read operations for entities. Base for flecs::entity. + * + * @ingroup cpp_entities + */ +struct entity_view : public id { + + entity_view() : flecs::id() { } + + /** Wrap an existing entity id. + * + * @param world The world in which the entity is created. + * @param id The entity id. + */ + explicit entity_view(flecs::world_t *world, flecs::id_t id) + : flecs::id(world + ? const_cast(ecs_get_world(world)) + : nullptr + , id ) { } + + /** Implicit conversion from flecs::entity_t to flecs::entity_view. */ + entity_view(entity_t id) + : flecs::id( nullptr, id ) { } + + /** Get entity id. + * @return The integer entity id. + */ + entity_t id() const { + return id_; + } + + /** Check if entity is valid. + * + * @return True if the entity is alive, false otherwise. + */ + bool is_valid() const { + return world_ && ecs_is_valid(world_, id_); + } + + explicit operator bool() const { + return is_valid(); + } + + /** Check if entity is alive. + * + * @return True if the entity is alive, false otherwise. + */ + bool is_alive() const { + return world_ && ecs_is_alive(world_, id_); + } + + /** Return the entity name. + * + * @return The entity name. + */ + flecs::string_view name() const { + return flecs::string_view(ecs_get_name(world_, id_)); + } + + /** Return the entity symbol. + * + * @return The entity symbol. + */ + flecs::string_view symbol() const { + return flecs::string_view(ecs_get_symbol(world_, id_)); + } + + /** Return the entity path. + * + * @return The hierarchical entity path. + */ + flecs::string path(const char *sep = "::", const char *init_sep = "::") const { + return path_from(0, sep, init_sep); + } + + /** Return the entity path relative to a parent. + * + * @return The relative hierarchical entity path. + */ + flecs::string path_from(flecs::entity_t parent, const char *sep = "::", const char *init_sep = "::") const { + char *path = ecs_get_path_w_sep(world_, parent, id_, sep, init_sep); + return flecs::string(path); + } + + /** Return the entity path relative to a parent. + * + * @return The relative hierarchical entity path. + */ + template + flecs::string path_from(const char *sep = "::", const char *init_sep = "::") const { + return path_from(_::type::id(world_), sep, init_sep); + } + + bool enabled() const { + return !ecs_has_id(world_, id_, flecs::Disabled); + } + + /** Get the entity's type. + * + * @return The entity's type. + */ + flecs::type type() const; + + /** Get the entity's table. + * + * @return Returns the entity's table. + */ + flecs::table table() const; + + /** Get table range for the entity. + * Returns a range with the entity's row as offset and count set to 1. If + * the entity is not stored in a table, the function returns a range with + * count 0. + * + * @return Returns the entity's table range. + */ + flecs::table_range range() const; + + /** Iterate (component) ids of an entity. + * The function parameter must match the following signature: + * + * @code + * void(*)(flecs::id id) + * @endcode + * + * @param func The function invoked for each id. + */ + template + void each(const Func& func) const; + + /** Iterate matching pair ids of an entity. + * The function parameter must match the following signature: + * + * @code + * void(*)(flecs::id id) + * @endcode + * + * @param func The function invoked for each id. + */ + template + void each(flecs::id_t first, flecs::id_t second, const Func& func) const; + + /** Iterate targets for a given relationship. + * The function parameter must match the following signature: + * + * @code + * void(*)(flecs::entity target) + * @endcode + * + * @param rel The relationship for which to iterate the targets. + * @param func The function invoked for each target. + */ + template + void each(const flecs::entity_view& rel, const Func& func) const; + + /** Iterate targets for a given relationship. + * The function parameter must match the following signature: + * + * @code + * void(*)(flecs::entity target) + * @endcode + * + * @tparam First The relationship for which to iterate the targets. + * @param func The function invoked for each target. + */ + template + void each(const Func& func) const { + return each(_::type::id(world_), func); + } + + /** Iterate children for entity. + * The function parameter must match the following signature: + * + * @code + * void(*)(flecs::entity target) + * @endcode + * + * @param rel The relationship to follow. + * @param func The function invoked for each child. + */ + template + void children(flecs::entity_t rel, Func&& func) const { + /* When the entity is a wildcard, this would attempt to query for all + * entities with (ChildOf, *) or (ChildOf, _) instead of querying for + * the children of the wildcard entity. */ + if (id_ == flecs::Wildcard || id_ == flecs::Any) { + /* This is correct, wildcard entities don't have children */ + return; + } + + flecs::world world(world_); + + ecs_iter_t it = ecs_each_id(world_, ecs_pair(rel, id_)); + while (ecs_each_next(&it)) { + _::each_delegate(FLECS_MOV(func)).invoke(&it); + } + } + + /** Iterate children for entity. + * The function parameter must match the following signature: + * + * @code + * void(*)(flecs::entity target) + * @endcode + * + * @tparam Rel The relationship to follow. + * @param func The function invoked for each child. + */ + template + void children(Func&& func) const { + children(_::type::id(world_), FLECS_MOV(func)); + } + + /** Iterate children for entity. + * The function parameter must match the following signature: + * + * @code + * void(*)(flecs::entity target) + * @endcode + * + * This operation follows the ChildOf relationship. + * + * @param func The function invoked for each child. + */ + template + void children(Func&& func) const { + children(flecs::ChildOf, FLECS_MOV(func)); + } + + /** Get component value. + * + * @tparam T The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + template ::value > = 0> + const T* get() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_id(world_, id_, comp_id)); + } + + /** Get component value. + * Overload for when T is not the same as the actual type, which happens + * when using pair types. + * + * @tparam T The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + template , + if_t< flecs::is_pair::value > = 0> + const A* get() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_id(world_, id_, comp_id)); + } + + /** Get a pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @tparam Second the second element of a pair. + */ + template , + typename A = actual_type_t

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

(); + } + + /** Get a pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + */ + template::value> = 0> + const First* get(Second second) const { + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast( + ecs_get_id(world_, id_, ecs_pair(first, second))); + } + + /** Get a pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @param constant the enum constant. + */ + template::value> = 0> + const First* get(Second constant) const { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(constant); + return get(target); + } + + /** Get component value (untyped). + * + * @param comp The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + const void* get(flecs::id_t comp) const { + return ecs_get_id(world_, id_, comp); + } + + /** Get a pair (untyped). + * This operation gets the value for a pair from the entity. If neither the + * first nor the second part of the pair are components, the operation + * will fail. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + const void* get(flecs::entity_t first, flecs::entity_t second) const { + return ecs_get_id(world_, id_, ecs_pair(first, second)); + } + + /** Get 1..N components. + * This operation accepts a callback with as arguments the components to + * retrieve. The callback will only be invoked when the entity has all + * the components. + * + * This operation is faster than individually calling get for each component + * as it only obtains entity metadata once. + * + * While the callback is invoked the table in which the components are + * stored is locked, which prevents mutations that could cause invalidation + * of the component references. Note that this is not an actual lock: + * invalid access causes a runtime panic and so it is still up to the + * application to ensure access is protected. + * + * The component arguments must be references and can be either const or + * non-const. When all arguments are const, the function will read-lock the + * table (see ecs_read_begin). If one or more arguments are non-const the + * function will write-lock the table (see ecs_write_begin). + * + * Example: + * + * @code + * e.get([](Position& p, Velocity& v) { // write lock + * p.x += v.x; + * }); + * + * e.get([](const Position& p) { // read lock + * std::cout << p.x << std::endl; + * }); + * @endcode + * + * @param func The callback to invoke. + * @return True if the entity has all components, false if not. + */ + template ::value > = 0> + bool get(const Func& func) const; + + /** Get enum constant. + * + * @tparam T The enum type for which to get the constant + * @return Constant entity if found, 0 entity if not. + */ + template ::value > = 0> + const T* get() const; + + /** Get the second part for a pair. + * This operation gets the value for a pair from the entity. The first + * part of the pair should not be a component. + * + * @tparam Second the second element of a pair. + * @param first The first part of the pair. + */ + template + const Second* get_second(flecs::entity_t first) const { + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast( + ecs_get_id(world_, id_, ecs_pair(first, second))); + } + + /** Get the second part for a pair. + * This operation gets the value for a pair from the entity. The first + * part of the pair should not be a component. + * + * @tparam First The first element of the pair. + * @tparam Second the second element of a pair. + */ + template + const Second* get_second() const { + return get>(); + } + + /** Get mutable component value. + * + * @tparam T The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + template ::value > = 0> + T* get_mut() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_mut_id(world_, id_, comp_id)); + } + + /** Get mutable component value. + * Overload for when T is not the same as the actual type, which happens + * when using pair types. + * + * @tparam T The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + template , + if_t< flecs::is_pair::value > = 0> + A* get_mut() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_mut_id(world_, id_, comp_id)); + } + + /** Get a mutable pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @tparam Second the second element of a pair. + */ + template , + typename A = actual_type_t

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

(); + } + + /** Get a mutable pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + */ + template::value> = 0> + First* get_mut(Second second) const { + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast( + ecs_get_mut_id(world_, id_, ecs_pair(first, second))); + } + + /** Get a mutable pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @param constant the enum constant. + */ + template::value> = 0> + First* get_mut(Second constant) const { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(constant); + return get_mut(target); + } + + /** Get mutable component value (untyped). + * + * @param comp The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + void* get_mut(flecs::id_t comp) const { + return ecs_get_mut_id(world_, id_, comp); + } + + /** Get a mutable pair (untyped). + * This operation gets the value for a pair from the entity. If neither the + * first nor the second part of the pair are components, the operation + * will fail. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + void* get_mut(flecs::entity_t first, flecs::entity_t second) const { + return ecs_get_mut_id(world_, id_, ecs_pair(first, second)); + } + + /** Get the second part for a pair. + * This operation gets the value for a pair from the entity. The first + * part of the pair should not be a component. + * + * @tparam Second the second element of a pair. + * @param first The first part of the pair. + */ + template + Second* get_mut_second(flecs::entity_t first) const { + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast( + ecs_get_mut_id(world_, id_, ecs_pair(first, second))); + } + + /** Get the second part for a pair. + * This operation gets the value for a pair from the entity. The first + * part of the pair should not be a component. + * + * @tparam First The first element of the pair. + * @tparam Second the second element of a pair. + */ + template + Second* get_mut_second() const { + return get_mut>(); + } + + /** Get target for a given pair. + * This operation returns the target for a given pair. The optional + * index can be used to iterate through targets, in case the entity has + * multiple instances for the same relationship. + * + * @tparam First The first element of the pair. + * @param index The index (0 for the first instance of the relationship). + */ + template + flecs::entity target(int32_t index = 0) const; + + /** Get target for a given pair. + * This operation returns the target for a given pair. The optional + * index can be used to iterate through targets, in case the entity has + * multiple instances for the same relationship. + * + * @param first The first element of the pair for which to retrieve the target. + * @param index The index (0 for the first instance of the relationship). + */ + flecs::entity target(flecs::entity_t first, int32_t index = 0) const; + + /** Get the target of a pair for a given relationship id. + * This operation returns the first entity that has the provided id by following + * the specified relationship. If the entity itself has the id then entity will + * be returned. If the id cannot be found on the entity or by following the + * relationship, the operation will return 0. + * + * This operation can be used to lookup, for example, which prefab is providing + * a component by specifying the IsA pair: + * + * @code + * // Is Position provided by the entity or one of its base entities? + * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) + * @endcode + * + * @param relationship The relationship to follow. + * @param id The id to lookup. + * @return The entity for which the target has been found. + */ + flecs::entity target_for(flecs::entity_t relationship, flecs::id_t id) const; + + template + flecs::entity target_for(flecs::entity_t relationship) const; + + template + flecs::entity target_for(flecs::entity_t relationship) const; + + /** Get depth for given relationship. + * + * @param rel The relationship. + * @return The depth. + */ + int32_t depth(flecs::entity_t rel) const { + return ecs_get_depth(world_, id_, rel); + } + + /** Get depth for given relationship. + * + * @tparam Rel The relationship. + * @return The depth. + */ + template + int32_t depth() const { + return this->depth(_::type::id(world_)); + } + + /** Get parent of entity. + * Short for target(flecs::ChildOf). + * + * @return The parent of the entity. + */ + flecs::entity parent() const; + + /** Lookup an entity by name. + * Lookup an entity in the scope of this entity. The provided path may + * contain double colons as scope separators, for example: "Foo::Bar". + * + * @param path The name of the entity to lookup. + * @param search_path When false, only the entity's scope is searched. + * @return The found entity, or entity::null if no entity matched. + */ + flecs::entity lookup(const char *path, bool search_path = false) const; + + /** Check if entity has the provided entity. + * + * @param e The entity to check. + * @return True if the entity has the provided entity, false otherwise. + */ + bool has(flecs::id_t e) const { + return ecs_has_id(world_, id_, e); + } + + /** Check if entity has the provided component. + * + * @tparam T The component to check. + * @return True if the entity has the provided component, false otherwise. + */ + template + bool has() const { + flecs::id_t cid = _::type::id(world_); + bool result = ecs_has_id(world_, id_, cid); + if (result) { + return result; + } + + if (is_enum::value) { + return ecs_has_pair(world_, id_, cid, flecs::Wildcard); + } + + return false; + } + + /** Check if entity has the provided enum constant. + * + * @tparam E The enum type (can be deduced). + * @param value The enum constant to check. + * @return True if the entity has the provided constant, false otherwise. + */ + template ::value > = 0> + bool has(E value) const { + auto r = _::type::id(world_); + auto o = enum_type(world_).entity(value); + ecs_assert(o, ECS_INVALID_PARAMETER, + "Constant was not found in Enum reflection data." + " Did you mean to use has() instead of has(E)?"); + return ecs_has_pair(world_, id_, r, o); + } + + /** Check if entity has the provided pair. + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + * @return True if the entity has the provided component, false otherwise. + */ + template + bool has() const { + return this->has(_::type::id(world_)); + } + + /** Check if entity has the provided pair. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + * @return True if the entity has the provided component, false otherwise. + */ + template::value > = 0> + bool has(Second second) const { + auto comp_id = _::type::id(world_); + return ecs_has_id(world_, id_, ecs_pair(comp_id, second)); + } + + /** Check if entity has the provided pair. + * + * @tparam Second The second element of the pair. + * @param first The first element of the pair. + * @return True if the entity has the provided component, false otherwise. + */ + template + bool has_second(flecs::entity_t first) const { + return this->has(first, _::type::id(world_)); + } + + /** Check if entity has the provided pair. + * + * @tparam First The first element of the pair. + * @param value The enum constant. + * @return True if the entity has the provided component, false otherwise. + */ + template::value > = 0> + bool has(E value) const { + const auto& et = enum_type(this->world_); + flecs::entity_t second = et.entity(value); + return has(second); + } + + /** Check if entity has the provided pair. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + * @return True if the entity has the provided component, false otherwise. + */ + bool has(flecs::id_t first, flecs::id_t second) const { + return ecs_has_id(world_, id_, ecs_pair(first, second)); + } + + /** Check if entity owns the provided entity. + * An entity is owned if it is not shared from a base entity. + * + * @param e The entity to check. + * @return True if the entity owns the provided entity, false otherwise. + */ + bool owns(flecs::id_t e) const { + return ecs_owns_id(world_, id_, e); + } + + /** Check if entity owns the provided pair. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + * @return True if the entity owns the provided component, false otherwise. + */ + template + bool owns(flecs::id_t second) const { + auto comp_id = _::type::id(world_); + return owns(ecs_pair(comp_id, second)); + } + + /** Check if entity owns the provided pair. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + * @return True if the entity owns the provided component, false otherwise. + */ + bool owns(flecs::id_t first, flecs::id_t second) const { + return owns(ecs_pair(first, second)); + } + + /** Check if entity owns the provided component. + * An component is owned if it is not shared from a base entity. + * + * @tparam T The component to check. + * @return True if the entity owns the provided component, false otherwise. + */ + template + bool owns() const { + return owns(_::type::id(world_)); + } + + /** Check if entity owns the provided pair. + * An pair is owned if it is not shared from a base entity. + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + * @return True if the entity owns the provided pair, false otherwise. + */ + template + bool owns() const { + return owns( + _::type::id(world_), + _::type::id(world_)); + } + + /** Test if id is enabled. + * + * @param id The id to test. + * @return True if enabled, false if not. + */ + bool enabled(flecs::id_t id) const { + return ecs_is_enabled_id(world_, id_, id); + } + + /** Test if component is enabled. + * + * @tparam T The component to test. + * @return True if enabled, false if not. + */ + template + bool enabled() const { + return this->enabled(_::type::id(world_)); + } + + /** Test if pair is enabled. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + * @return True if enabled, false if not. + */ + bool enabled(flecs::id_t first, flecs::id_t second) const { + return this->enabled(ecs_pair(first, second)); + } + + /** Test if pair is enabled. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + * @return True if enabled, false if not. + */ + template + bool enabled(flecs::id_t second) const { + return this->enabled(_::type::id(world_), second); + } + + /** Test if pair is enabled. + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + * @return True if enabled, false if not. + */ + template + bool enabled() const { + return this->enabled(_::type::id(world_)); + } + + flecs::entity clone(bool clone_value = true, flecs::entity_t dst_id = 0) const; + + /** Return mutable entity handle for current stage + * When an entity handle created from the world is used while the world is + * in staged mode, it will only allow for readonly operations since + * structural changes are not allowed on the world while in staged mode. + * + * To do mutations on the entity, this operation provides a handle to the + * entity that uses the stage instead of the actual world. + * + * Note that staged entity handles should never be stored persistently, in + * components or elsewhere. An entity handle should always point to the + * main world. + * + * Also note that this operation is not necessary when doing mutations on an + * entity outside of a system. It is allowed to do entity operations + * directly on the world, as long as the world is not in staged mode. + * + * @param stage The current stage. + * @return An entity handle that allows for mutations in the current stage. + */ + flecs::entity mut(const flecs::world& stage) const; + + /** Same as mut(world), but for iterator. + * This operation allows for the construction of a mutable entity handle + * from an iterator. + * + * @param it An iterator that contains a reference to the world or stage. + * @return An entity handle that allows for mutations in the current stage. + */ + flecs::entity mut(const flecs::iter& it) const; + + /** Same as mut(world), but for entity. + * This operation allows for the construction of a mutable entity handle + * from another entity. This is useful in each() functions, which only + * provide a handle to the entity being iterated over. + * + * @param e Another mutable entity. + * @return An entity handle that allows for mutations in the current stage. + */ + flecs::entity mut(const flecs::entity_view& e) const; + +# ifdef FLECS_JSON +/** + * @file addons/cpp/mixins/json/entity_view.inl + * @brief JSON entity mixin. + */ + +/** Serialize entity to JSON. + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_json + */ +flecs::string to_json(const flecs::entity_to_json_desc_t *desc = nullptr) const { + char *json = ecs_entity_to_json(world_, id_, desc); + return flecs::string(json); +} + +# endif +# ifdef FLECS_DOC +/** + * @file addons/cpp/mixins/doc/entity_view.inl + * @brief Doc entity view mixin. + */ + +/** Get human readable name. + * + * @see ecs_doc_get_name() + * @see flecs::doc::get_name() + * @see flecs::entity_builder::set_doc_name() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_name() const { + return ecs_doc_get_name(world_, id_); +} + +/** Get brief description. + * + * @see ecs_doc_get_brief() + * @see flecs::doc::get_brief() + * @see flecs::entity_builder::set_doc_brief() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_brief() const { + return ecs_doc_get_brief(world_, id_); +} + +/** Get detailed description. + * + * @see ecs_doc_get_detail() + * @see flecs::doc::get_detail() + * @see flecs::entity_builder::set_doc_detail() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_detail() const { + return ecs_doc_get_detail(world_, id_); +} + +/** Get link to external documentation. + * + * @see ecs_doc_get_link() + * @see flecs::doc::get_link() + * @see flecs::entity_builder::set_doc_link() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_link() const { + return ecs_doc_get_link(world_, id_); +} + +/** Get color. + * + * @see ecs_doc_get_color() + * @see flecs::doc::get_color() + * @see flecs::entity_builder::set_doc_color() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_color() const { + return ecs_doc_get_color(world_, id_); +} + +/** Get UUID. + * + * @see ecs_doc_get_uuid() + * @see flecs::doc::get_uuid() + * @see flecs::entity_builder::set_doc_uuid() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_uuid() const { + return ecs_doc_get_uuid(world_, id_); +} + +# endif +# ifdef FLECS_ALERTS +/** + * @file addons/cpp/mixins/alerts/entity_view.inl + * @brief Alerts entity mixin. + */ + +/** Return number of alerts for entity. + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_alerts + */ +int32_t alert_count(flecs::entity_t alert = 0) const { + return ecs_get_alert_count(world_, id_, alert); +} + +# endif + +/** + * @file addons/cpp/mixins/enum/entity_view.inl + * @brief Enum entity view mixin. + */ + +/** Convert entity to enum constant. + * + * @memberof flecs::entity_view + * @ingroup cpp_entities + */ +template +E to_constant() const; + + +/** + * @file addons/cpp/mixins/event/entity_view.inl + * @brief Event entity mixin. + */ + +/** Emit event for entity. + * + * @memberof flecs::entity_view + * + * @param evt The event to emit. + */ +void emit(flecs::entity_t evt) const { + flecs::world(world_) + .event(evt) + .entity(id_) + .emit(); +} + +/** Emit event for entity. + * + * @memberof flecs::entity_view + * + * @param evt The event to emit. + */ +void emit(flecs::entity evt) const; + +/** Emit event for entity. + * + * @memberof flecs::entity_view + * + * @tparam Evt The event to emit. + */ +template ::value> = 0> +void emit() const { + this->emit(_::type::id(world_)); +} + +/** Emit event with payload for entity. + * + * @memberof flecs::entity_view + * + * @tparam Evt The event to emit. + */ +template ::value> = 0> +void emit(const Evt& payload) const { + flecs::world(world_) + .event(_::type::id(world_)) + .entity(id_) + .ctx(&payload) + .emit(); +} + + +/** Enqueue event for entity. + * + * @memberof flecs::entity_view + * + * @param evt The event to enqueue. + */ +void enqueue(flecs::entity_t evt) const { + flecs::world(world_) + .event(evt) + .entity(id_) + .enqueue(); +} + +/** Enqueue event for entity. + * + * @memberof flecs::entity_view + * + * @param evt The event to enqueue. + */ +void enqueue(flecs::entity evt) const; + +/** Enqueue event for entity. + * + * @memberof flecs::entity_view + * + * @tparam Evt The event to enqueue. + */ +template ::value> = 0> +void enqueue() const { + this->enqueue(_::type::id(world_)); +} + +/** Enqueue event with payload for entity. + * + * @memberof flecs::entity_view + * + * @tparam Evt The event to enqueue. + */ +template ::value> = 0> +void enqueue(const Evt& payload) const { + flecs::world(world_) + .event(_::type::id(world_)) + .entity(id_) + .ctx(&payload) + .enqueue(); +} + + +private: + flecs::entity set_stage(world_t *stage); +}; + +} + +/** @} */ + +/** + * @file addons/cpp/mixins/entity/builder.hpp + * @brief Entity builder. + */ + +#pragma once + +namespace flecs +{ + +/** Entity builder. + * @ingroup cpp_entities + */ +template +struct entity_builder : entity_view { + + using entity_view::entity_view; + + /** Add a component to an entity. + * To ensure the component is initialized, it should have a constructor. + * + * @tparam T the component type to add. + */ + template + const Self& add() const { + flecs_static_assert(is_flecs_constructible::value, + "cannot default construct type: add T::T() or use emplace()"); + ecs_add_id(this->world_, this->id_, _::type::id(this->world_)); + return to_base(); + } + + /** Add pair for enum constant. + * This operation will add a pair to the entity where the first element is + * the enumeration type, and the second element the enumeration constant. + * + * The operation may be used with regular (C style) enumerations as well as + * enum classes. + * + * @param value The enumeration value. + */ + template ::value > = 0> + const Self& add(E value) const { + flecs::entity_t first = _::type::id(this->world_); + const auto& et = enum_type(this->world_); + flecs::entity_t second = et.entity(value); + + ecs_assert(second, ECS_INVALID_PARAMETER, "Component was not found in reflection data."); + return this->add(first, second); + } + + /** Add an entity to an entity. + * Add an entity to the entity. This is typically used for tagging. + * + * @param component The component to add. + */ + const Self& add(id_t component) const { + ecs_add_id(this->world_, this->id_, component); + return to_base(); + } + + /** Add a pair. + * This operation adds a pair to the entity. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + const Self& add(entity_t first, entity_t second) const { + ecs_add_pair(this->world_, this->id_, first, second); + return to_base(); + } + + /** Add a pair. + * This operation adds a pair to the entity. + * + * @tparam First The first element of the pair + * @tparam Second The second element of the pair + */ + template + const Self& add() const { + return this->add(_::type::id(this->world_)); + } + + /** Add a pair. + * This operation adds a pair to the entity. + * + * @tparam First The first element of the pair + * @param second The second element of the pair. + */ + template::value > = 0> + const Self& add(Second second) const { + flecs_static_assert(is_flecs_constructible::value, + "cannot default construct type: add T::T() or use emplace()"); + return this->add(_::type::id(this->world_), second); + } + + /** Add a pair. + * This operation adds a pair to the entity that consists out of a tag + * combined with an enum constant. + * + * @tparam First The first element of the pair + * @param constant the enum constant. + */ + template::value > = 0> + const Self& add(Second constant) const { + flecs_static_assert(is_flecs_constructible::value, + "cannot default construct type: add T::T() or use emplace()"); + const auto& et = enum_type(this->world_); + return this->add(et.entity(constant)); + } + + /** Add a pair. + * This operation adds a pair to the entity. + * + * @param first The first element of the pair + * @tparam Second The second element of the pair + */ + template + const Self& add_second(flecs::entity_t first) const { + return this->add(first, _::type::id(this->world_)); + } + + /** Conditional add. + * This operation adds if condition is true, removes if condition is false. + * + * @param cond The condition to evaluate. + * @param component The component to add. + */ + const Self& add_if(bool cond, flecs::id_t component) const { + if (cond) { + return this->add(component); + } else { + return this->remove(component); + } + } + + /** Conditional add. + * This operation adds if condition is true, removes if condition is false. + * + * @tparam T The component to add. + * @param cond The condition to evaluate. + */ + template + const Self& add_if(bool cond) const { + if (cond) { + return this->add(); + } else { + return this->remove(); + } + } + + /** Conditional add. + * This operation adds if condition is true, removes if condition is false. + * + * @param cond The condition to evaluate. + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + const Self& add_if(bool cond, flecs::entity_t first, flecs::entity_t second) const { + if (cond) { + return this->add(first, second); + } else { + /* If second is 0 or if relationship is exclusive, use wildcard for + * second which will remove all instances of the relationship. + * Replacing 0 with Wildcard will make it possible to use the second + * as the condition. */ + if (!second || ecs_has_id(this->world_, first, flecs::Exclusive)) { + second = flecs::Wildcard; + } + return this->remove(first, second); + } + } + + /** Conditional add. + * This operation adds if condition is true, removes if condition is false. + * + * @tparam First The first element of the pair + * @param cond The condition to evaluate. + * @param second The second element of the pair. + */ + template + const Self& add_if(bool cond, flecs::entity_t second) const { + return this->add_if(cond, _::type::id(this->world_), second); + } + + /** Conditional add. + * This operation adds if condition is true, removes if condition is false. + * + * @tparam First The first element of the pair + * @tparam Second The second element of the pair + * @param cond The condition to evaluate. + */ + template + const Self& add_if(bool cond) const { + return this->add_if(cond, _::type::id(this->world_)); + } + + /** Conditional add. + * This operation adds if condition is true, removes if condition is false. + * + * @param cond The condition to evaluate. + * @param constant The enumeration constant. + */ + template ::value > = 0> + const Self& add_if(bool cond, E constant) const { + const auto& et = enum_type(this->world_); + return this->add_if(cond, et.entity(constant)); + } + + /** Shortcut for `add(IsA, entity)`. + * + * @param second The second element of the pair. + */ + const Self& is_a(entity_t second) const { + return this->add(flecs::IsA, second); + } + + /** Shortcut for `add(IsA, entity)`. + * + * @tparam T the type associated with the entity. + */ + template + const Self& is_a() const { + return this->add(flecs::IsA, _::type::id(this->world_)); + } + + /** Shortcut for `add(ChildOf, entity)`. + * + * @param second The second element of the pair. + */ + const Self& child_of(entity_t second) const { + return this->add(flecs::ChildOf, second); + } + + /** Shortcut for `add(DependsOn, entity)`. + * + * @param second The second element of the pair. + */ + const Self& depends_on(entity_t second) const { + return this->add(flecs::DependsOn, second); + } + + /** Shortcut for `add(DependsOn, entity)`. + * + * @param second The second element of the pair. + */ + template ::value> = 0> + const Self& depends_on(E second) const { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(second); + return depends_on(target); + } + + /** Shortcut for `add(SlotOf, entity)`. + * + * @param second The second element of the pair. + */ + const Self& slot_of(entity_t second) const { + return this->add(flecs::SlotOf, second); + } + + /** Shortcut for `add(SlotOf, target(ChildOf))`. + */ + const Self& slot() const { + ecs_check(ecs_get_target(world_, id_, flecs::ChildOf, 0), + ECS_INVALID_PARAMETER, "add ChildOf pair before using slot()"); + return this->slot_of(this->target(flecs::ChildOf)); + error: + return to_base(); + } + + /** Shortcut for `add(ChildOf, entity)`. + * + * @tparam T the type associated with the entity. + */ + template + const Self& child_of() const { + return this->child_of(_::type::id(this->world_)); + } + + /** Shortcut for `add(DependsOn, entity)`. + * + * @tparam T the type associated with the entity. + */ + template + const Self& depends_on() const { + return this->depends_on(_::type::id(this->world_)); + } + + /** Shortcut for `add(SlotOf, entity)`. + * + * @tparam T the type associated with the entity. + */ + template + const Self& slot_of() const { + return this->slot_of(_::type::id(this->world_)); + } + + /** Remove a component from an entity. + * + * @tparam T the type of the component to remove. + */ + template ::value > = 0> + const Self& remove() const { + ecs_remove_id(this->world_, this->id_, _::type::id(this->world_)); + return to_base(); + } + + /** Remove pair for enum. + * This operation will remove any `(Enum, *)` pair from the entity. + * + * @tparam E The enumeration type. + */ + template ::value > = 0> + const Self& remove() const { + flecs::entity_t first = _::type::id(this->world_); + return this->remove(first, flecs::Wildcard); + } + + /** Remove an entity from an entity. + * + * @param entity The entity to remove. + */ + const Self& remove(entity_t entity) const { + ecs_remove_id(this->world_, this->id_, entity); + return to_base(); + } + + /** Remove a pair. + * This operation removes a pair from the entity. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + const Self& remove(entity_t first, entity_t second) const { + ecs_remove_pair(this->world_, this->id_, first, second); + return to_base(); + } + + /** Removes a pair. + * This operation removes a pair from the entity. + * + * @tparam First The first element of the pair + * @tparam Second The second element of the pair + */ + template + const Self& remove() const { + return this->remove(_::type::id(this->world_)); + } + + /** Remove a pair. + * This operation removes the pair from the entity. + * + * @tparam First The first element of the pair + * @param second The second element of the pair. + */ + template::value > = 0> + const Self& remove(Second second) const { + return this->remove(_::type::id(this->world_), second); + } + + /** Removes a pair. + * This operation removes a pair from the entity. + * + * @tparam Second The second element of the pair + * @param first The first element of the pair + */ + template + const Self& remove_second(flecs::entity_t first) const { + return this->remove(first, _::type::id(this->world_)); + } + + /** Remove a pair. + * This operation removes the pair from the entity. + * + * @tparam First The first element of the pair + * @param constant the enum constant. + */ + template::value > = 0> + const Self& remove(Second constant) const { + const auto& et = enum_type(this->world_); + flecs::entity_t second = et.entity(constant); + return this->remove(second); + } + + /** Mark id for auto-overriding. + * When an entity inherits from a base entity (using the `IsA` relationship) + * any ids marked for auto-overriding on the base will be overridden + * automatically by the entity. + * + * @param id The id to mark for overriding. + */ + const Self& auto_override(flecs::id_t id) const { + return this->add(ECS_AUTO_OVERRIDE | id); + } + + /** Mark pair for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + const Self& auto_override(flecs::entity_t first, flecs::entity_t second) const { + return this->auto_override(ecs_pair(first, second)); + } + + /** Mark component for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam T The component to mark for overriding. + */ + template + const Self& auto_override() const { + return this->auto_override(_::type::id(this->world_)); + } + + /** Mark pair for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + */ + template + const Self& auto_override(flecs::entity_t second) const { + return this->auto_override(_::type::id(this->world_), second); + } + + /** Mark pair for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + */ + template + const Self& auto_override() const { + return this->auto_override(_::type::id(this->world_)); + } + + /** Set component, mark component for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam T The component to set and for which to add the OVERRIDE flag + * @param val The value to set. + */ + template + const Self& set_auto_override(const T& val) const { + this->auto_override(); + return this->set(val); + } + + /** Set component, mark component for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam T The component to set and for which to add the OVERRIDE flag + * @param val The value to set. + */ + template + const Self& set_auto_override(T&& val) const { + this->auto_override(); + return this->set(FLECS_FWD(val)); + } + + /** Set pair, mark component for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + * @param val The value to set. + */ + template + const Self& set_auto_override(flecs::entity_t second, const First& val) const { + this->auto_override(second); + return this->set(second, val); + } + + /** Set pair, mark component for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + * @param val The value to set. + */ + template + const Self& set_auto_override(flecs::entity_t second, First&& val) const { + this->auto_override(second); + return this->set(second, FLECS_FWD(val)); + } + + /** Set component, mark component for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + * @param val The value to set. + */ + template , + typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> + const Self& set_auto_override(const A& val) const { + this->auto_override(); + return this->set(val); + } + + /** Set component, mark component for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + * @param val The value to set. + */ + template , + typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> + const Self& set_auto_override(A&& val) const { + this->auto_override(); + return this->set(FLECS_FWD(val)); + } + + /** Emplace component, mark component for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam T The component to emplace and override. + * @param args The arguments to pass to the constructor of `T`. + */ + template + const Self& emplace_auto_override(Args&&... args) const { + this->auto_override(); + + flecs::emplace(this->world_, this->id_, + _::type::id(this->world_), FLECS_FWD(args)...); + + return to_base(); + } + + /** Emplace pair, mark pair for auto-overriding. + * @see auto_override(flecs::id_t) const + * + * @tparam First The first element of the pair to emplace and override. + * @tparam Second The second element of the pair to emplace and override. + * @param args The arguments to pass to the constructor of `Second`. + */ + template , + typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0, + typename ... Args> + const Self& emplace_auto_override(Args&&... args) const { + this->auto_override(); + + flecs::emplace(this->world_, this->id_, + ecs_pair(_::type::id(this->world_), + _::type::id(this->world_)), + FLECS_FWD(args)...); + + return to_base(); + } + + /** Enable an entity. + * Enabled entities are matched with systems and can be searched with + * queries. + */ + const Self& enable() const { + ecs_enable(this->world_, this->id_, true); + return to_base(); + } + + /** Disable an entity. + * Disabled entities are not matched with systems and cannot be searched + * with queries, unless explicitly specified in the query expression. + */ + const Self& disable() const { + ecs_enable(this->world_, this->id_, false); + return to_base(); + } + + /** Enable an id. + * This sets the enabled bit for this component. If this is the first time + * the component is enabled or disabled, the bitset is added. + * + * @param id The id to enable. + * @param toggle True to enable, false to disable (default = true). + * + * @see ecs_enable_id() + */ + const Self& enable(flecs::id_t id, bool toggle = true) const { + ecs_enable_id(this->world_, this->id_, id, toggle); + return to_base(); + } + + /** Enable a component. + * @see enable(flecs::id_t) const + * + * @tparam T The component to enable. + */ + template + const Self& enable() const { + return this->enable(_::type::id(this->world_)); + } + + /** Enable a pair. + * @see enable(flecs::id_t) const + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + const Self& enable(flecs::id_t first, flecs::id_t second) const { + return this->enable(ecs_pair(first, second)); + } + + /** Enable a pair. + * @see enable(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + */ + template + const Self& enable(flecs::id_t second) const { + return this->enable(_::type::id(), second); + } + + /** Enable a pair. + * @see enable(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + */ + template + const Self& enable() const { + return this->enable(_::type::id()); + } + + /** Disable an id. + * This sets the enabled bit for this id. If this is the first time + * the id is enabled or disabled, the bitset is added. + * + * @param id The id to disable. + * + * @see ecs_enable_id() + * @see enable(flecs::id_t) const + */ + const Self& disable(flecs::id_t id) const { + return this->enable(id, false); + } + + /** Disable a component. + * @see disable(flecs::id_t) const + * + * @tparam T The component to enable. + */ + template + const Self& disable() const { + return this->disable(_::type::id()); + } + + /** Disable a pair. + * @see disable(flecs::id_t) const + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + const Self& disable(flecs::id_t first, flecs::id_t second) const { + return this->disable(ecs_pair(first, second)); + } + + /** Disable a pair. + * @see disable(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + */ + template + const Self& disable(flecs::id_t second) const { + return this->disable(_::type::id(), second); + } + + /** Disable a pair. + * @see disable(flecs::id_t) const + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + */ + template + const Self& disable() const { + return this->disable(_::type::id()); + } + + const Self& set_ptr(entity_t comp, size_t size, const void *ptr) const { + ecs_set_id(this->world_, this->id_, comp, size, ptr); + return to_base(); + } + + const Self& set_ptr(entity_t comp, const void *ptr) const { + const flecs::Component *cptr = ecs_get( + this->world_, comp, EcsComponent); + + /* Can't set if it's not a component */ + ecs_assert(cptr != NULL, ECS_INVALID_PARAMETER, NULL); + + return set_ptr(comp, cptr->size, ptr); + } + + template::value> = 0 > + const Self& set(T&& value) const { + flecs::set(this->world_, this->id_, FLECS_FWD(value)); + return to_base(); + } + + template::value > = 0> + const Self& set(const T& value) const { + flecs::set(this->world_, this->id_, value); + return to_base(); + } + + template, if_not_t< + is_actual::value > = 0> + const Self& set(A&& value) const { + flecs::set(this->world_, this->id_, FLECS_FWD(value)); + return to_base(); + } + + template, if_not_t< + is_actual::value > = 0> + const Self& set(const A& value) const { + flecs::set(this->world_, this->id_, value); + return to_base(); + } + + /** Set a pair for an entity. + * This operation sets the pair value, and uses First as type. If the + * entity did not yet have the pair, it will be added. + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair + * @param value The value to set. + */ + template , + typename A = actual_type_t

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

(this->world_, this->id_, FLECS_FWD(value)); + return to_base(); + } + + /** Set a pair for an entity. + * This operation sets the pair value, and uses First as type. If the + * entity did not yet have the pair, it will be added. + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair + * @param value The value to set. + */ + template , + typename A = actual_type_t

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

(this->world_, this->id_, value); + return to_base(); + } + + /** Set a pair for an entity. + * This operation sets the pair value, and uses First as type. If the + * entity did not yet have the pair, it will be added. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + * @param value The value to set. + */ + template ::value > = 0> + const Self& set(Second second, const First& value) const { + auto first = _::type::id(this->world_); + flecs::set(this->world_, this->id_, value, + ecs_pair(first, second)); + return to_base(); + } + + /** Set a pair for an entity. + * This operation sets the pair value, and uses First as type. If the + * entity did not yet have the pair, it will be added. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + * @param value The value to set. + */ + template ::value > = 0> + const Self& set(Second second, First&& value) const { + auto first = _::type::id(this->world_); + flecs::set(this->world_, this->id_, FLECS_FWD(value), + ecs_pair(first, second)); + return to_base(); + } + + /** Set a pair for an entity. + * This operation sets the pair value, and uses First as type. If the + * entity did not yet have the pair, it will be added. + * + * @tparam First The first element of the pair. + * @param constant The enum constant. + * @param value The value to set. + */ + template ::value > = 0> + const Self& set(Second constant, const First& value) const { + const auto& et = enum_type(this->world_); + flecs::entity_t second = et.entity(constant); + return set(second, value); + } + + /** Set a pair for an entity. + * This operation sets the pair value, and uses Second as type. If the + * entity did not yet have the pair, it will be added. + * + * @tparam Second The second element of the pair + * @param first The first element of the pair. + * @param value The value to set. + */ + template + const Self& set_second(entity_t first, const Second& value) const { + auto second = _::type::id(this->world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + flecs::set(this->world_, this->id_, value, + ecs_pair(first, second)); + return to_base(); + } + + /** Set a pair for an entity. + * This operation sets the pair value, and uses Second as type. If the + * entity did not yet have the pair, it will be added. + * + * @tparam Second The second element of the pair + * @param first The first element of the pair. + * @param value The value to set. + */ + template + const Self& set_second(entity_t first, Second&& value) const { + auto second = _::type::id(this->world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + flecs::set(this->world_, this->id_, FLECS_FWD(value), + ecs_pair(first, second)); + return to_base(); + } + + template + const Self& set_second(const Second& value) const { + flecs::set>(this->world_, this->id_, value); + return to_base(); + } + + /** Set 1..N components. + * This operation accepts a callback with as arguments the components to + * set. If the entity does not have all of the provided components, they + * will be added. + * + * This operation is faster than individually calling get for each component + * as it only obtains entity metadata once. When this operation is called + * while deferred, its performance is equivalent to that of calling ensure + * for each component separately. + * + * The operation will invoke modified for each component after the callback + * has been invoked. + * + * @param func The callback to invoke. + */ + template + const Self& insert(const Func& func) const; + + /** Emplace component. + * Emplace constructs a component in the storage, which prevents calling the + * destructor on the value passed into the function. + * + * Emplace attempts the following signatures to construct the component: + * + * @code + * T{Args...} + * T{flecs::entity, Args...} + * @endcode + * + * If the second signature matches, emplace will pass in the current entity + * as argument to the constructor, which is useful if the component needs + * to be aware of the entity to which it has been added. + * + * Emplace may only be called for components that have not yet been added + * to the entity. + * + * @tparam T the component to emplace + * @param args The arguments to pass to the constructor of T + */ + template> + const Self& emplace(Args&&... args) const { + flecs::emplace(this->world_, this->id_, + _::type::id(this->world_), FLECS_FWD(args)...); + return to_base(); + } + + template , + typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> + const Self& emplace(Args&&... args) const { + flecs::emplace(this->world_, this->id_, + ecs_pair(_::type::id(this->world_), + _::type::id(this->world_)), + FLECS_FWD(args)...); + return to_base(); + } + + template + const Self& emplace_first(flecs::entity_t second, Args&&... args) const { + auto first = _::type::id(this->world_); + flecs::emplace(this->world_, this->id_, + ecs_pair(first, second), + FLECS_FWD(args)...); + return to_base(); + } + + template + const Self& emplace_second(flecs::entity_t first, Args&&... args) const { + auto second = _::type::id(this->world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + flecs::emplace(this->world_, this->id_, + ecs_pair(first, second), + FLECS_FWD(args)...); + return to_base(); + } + + /** Entities created in function will have the current entity. + * This operation is thread safe. + * + * @param func The function to call. + */ + template + const Self& with(const Func& func) const { + ecs_id_t prev = ecs_set_with(this->world_, this->id_); + func(); + ecs_set_with(this->world_, prev); + return to_base(); + } + + /** Entities created in function will have `(First, this)`. + * This operation is thread safe. + * + * @tparam First The first element of the pair + * @param func The function to call. + */ + template + const Self& with(const Func& func) const { + with(_::type::id(this->world_), func); + return to_base(); + } + + /** Entities created in function will have `(first, this)`. + * This operation is thread safe. + * + * @param first The first element of the pair. + * @param func The function to call. + */ + template + const Self& with(entity_t first, const Func& func) const { + ecs_id_t prev = ecs_set_with(this->world_, + ecs_pair(first, this->id_)); + func(); + ecs_set_with(this->world_, prev); + return to_base(); + } + + /** The function will be ran with the scope set to the current entity. */ + template + const Self& scope(const Func& func) const { + ecs_entity_t prev = ecs_set_scope(this->world_, this->id_); + func(); + ecs_set_scope(this->world_, prev); + return to_base(); + } + + /** Return world scoped to entity */ + scoped_world scope() const { + return scoped_world(world_, id_); + } + + /* Set the entity name. + */ + const Self& set_name(const char *name) const { + ecs_set_name(this->world_, this->id_, name); + return to_base(); + } + + /* Set entity alias. + */ + const Self& set_alias(const char *name) const { + ecs_set_alias(this->world_, this->id_, name); + return to_base(); + } + +# ifdef FLECS_DOC +/** + * @file addons/cpp/mixins/doc/entity_builder.inl + * @brief Doc entity builder mixin. + */ + +/** Set human readable name. + * This adds `(flecs.doc.Description, flecs.Name)` to the entity. + * + * @see ecs_doc_set_name() + * @see flecs::doc::set_name() + * @see flecs::entity_view::doc_name() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc + */ +const Self& set_doc_name(const char *name) const { + ecs_doc_set_name(world_, id_, name); + return to_base(); +} + +/** Set brief description. + * This adds `(flecs.doc.Description, flecs.doc.Brief)` to the entity. + * + * @see ecs_doc_set_brief() + * @see flecs::doc::set_brief() + * @see flecs::entity_view::doc_brief() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc + */ +const Self& set_doc_brief(const char *brief) const { + ecs_doc_set_brief(world_, id_, brief); + return to_base(); +} + +/** Set detailed description. + * This adds `(flecs.doc.Description, flecs.doc.Detail)` to the entity. + * + * @see ecs_doc_set_detail() + * @see flecs::doc::set_detail() + * @see flecs::entity_view::doc_detail() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc + */ +const Self& set_doc_detail(const char *detail) const { + ecs_doc_set_detail(world_, id_, detail); + return to_base(); +} + +/** Set link to external documentation. + * This adds `(flecs.doc.Description, flecs.doc.Link)` to the entity. + * + * @see ecs_doc_set_link() + * @see flecs::doc::set_link() + * @see flecs::entity_view::doc_link() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc + */ +const Self& set_doc_link(const char *link) const { + ecs_doc_set_link(world_, id_, link); + return to_base(); +} + +/** Set doc color. + * This adds `(flecs.doc.Description, flecs.doc.Color)` to the entity. + * + * @see ecs_doc_set_color() + * @see flecs::doc::set_color() + * @see flecs::entity_view::doc_color() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc + */ +const Self& set_doc_color(const char *color) const { + ecs_doc_set_color(world_, id_, color); + return to_base(); +} + +/** Set doc UUID. + * This adds `(flecs.doc.Description, flecs.doc.Uuid)` to the entity. + * + * @see ecs_doc_set_uuid() + * @see flecs::doc::set_uuid() + * @see flecs::entity_view::doc_uuid() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc + */ +const Self& set_doc_uuid(const char *uuid) const { + ecs_doc_set_uuid(world_, id_, uuid); + return to_base(); +} + +# endif + +# ifdef FLECS_META +/** + * @file addons/cpp/mixins/meta/entity_builder.inl + * @brief Meta entity builder mixin. + */ + +/** + * @memberof flecs::entity_view + * @ingroup cpp_addons_meta + * + * @{ + */ + +/** Make entity a unit */ +const Self& unit( + const char *symbol, + flecs::entity_t prefix = 0, + flecs::entity_t base = 0, + flecs::entity_t over = 0, + int32_t factor = 0, + int32_t power = 0) const +{ + ecs_unit_desc_t desc = {}; + desc.entity = this->id_; + desc.symbol = const_cast(symbol); /* safe, will be copied in */ + desc.base = base; + desc.over = over; + desc.prefix = prefix; + desc.translation.factor = factor; + desc.translation.power = power; + ecs_unit_init(this->world(), &desc); + + return to_base(); +} + +/** Make entity a derived unit */ +const Self& unit( + flecs::entity_t prefix = 0, + flecs::entity_t base = 0, + flecs::entity_t over = 0, + int32_t factor = 0, + int32_t power = 0) const +{ + ecs_unit_desc_t desc = {}; + desc.entity = this->id_; + desc.base = base; + desc.over = over; + desc.prefix = prefix; + desc.translation.factor = factor; + desc.translation.power = power; + ecs_unit_init(this->world(), &desc); + + return to_base(); +} + +/** Make entity a derived unit */ +const Self& unit_prefix( + const char *symbol, + int32_t factor = 0, + int32_t power = 0) const +{ + ecs_unit_prefix_desc_t desc = {}; + desc.entity = this->id_; + desc.symbol = const_cast(symbol); /* safe, will be copied in */ + desc.translation.factor = factor; + desc.translation.power = power; + ecs_unit_prefix_init(this->world(), &desc); + + return to_base(); +} + +/** Add quantity to unit */ +const Self& quantity(flecs::entity_t quantity) const { + ecs_add_pair(this->world(), this->id(), flecs::Quantity, quantity); + return to_base(); +} + +/** Make entity a unity prefix */ +template +const Self& quantity() const { + return this->quantity(_::type::id(this->world())); +} + +/** Make entity a quantity */ +const Self& quantity() const { + ecs_add_id(this->world(), this->id(), flecs::Quantity); + return to_base(); +} + +/** @} */ + +# endif + +# ifdef FLECS_JSON +/** + * @file addons/cpp/mixins/json/entity_builder.inl + * @brief JSON entity mixin. + */ + +/** Set component from JSON. + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json + */ +const Self& set_json( + flecs::id_t e, + const char *json, + flecs::from_json_desc_t *desc = nullptr) const +{ + flecs::entity_t type = ecs_get_typeid(world_, e); + if (!type) { + ecs_err("id is not a type"); + return to_base(); + } + + void *ptr = ecs_ensure_id(world_, id_, e); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_ptr_from_json(world_, type, ptr, json, desc); + ecs_modified_id(world_, id_, e); + + return to_base(); +} + +/** Set pair from JSON. + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json + */ +const Self& set_json( + flecs::entity_t r, + flecs::entity_t t, + const char *json, + flecs::from_json_desc_t *desc = nullptr) const +{ + return set_json(ecs_pair(r, t), json, desc); +} + +/** Set component from JSON. + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json + */ +template +const Self& set_json( + const char *json, + flecs::from_json_desc_t *desc = nullptr) const +{ + return set_json(_::type::id(world_), json, desc); +} + +/** Set pair from JSON. + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json + */ +template +const Self& set_json( + const char *json, + flecs::from_json_desc_t *desc = nullptr) const +{ + return set_json( + _::type::id(world_), + _::type::id(world_), + json, desc); +} + +/** Set pair from JSON. + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json + */ +template +const Self& set_json( + flecs::entity_t t, + const char *json, + flecs::from_json_desc_t *desc = nullptr) const +{ + return set_json( + _::type::id(world_), t, + json, desc); +} + +/** Set pair from JSON. + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json + */ +template +const Self& set_json_second( + flecs::entity_t r, + const char *json, + flecs::from_json_desc_t *desc = nullptr) const +{ + return set_json( + r, _::type::id(world_), + json, desc); +} + +# endif + +/** + * @file addons/cpp/mixins/event/entity_builder.inl + * @brief Event entity mixin. + */ + +/** Observe event on entity + * + * @memberof flecs::entity_builder + * + * @param evt The event id. + * @param callback The observer callback. + * @return Event builder. + */ +template +const Self& observe(flecs::entity_t evt, Func&& callback) const; + +/** Observe event on entity + * + * @memberof flecs::entity_builder + * + * @tparam Evt The event type. + * @param callback The observer callback. + * @return Event builder. + */ +template +const Self& observe(Func&& callback) const; + +/** Observe event on entity + * + * @memberof flecs::entity_builder + * + * @param callback The observer callback. + * @return Event builder. + */ +template +const Self& observe(Func&& callback) const; + + + + +protected: + const Self& to_base() const { + return *static_cast(this); + } +}; + +} + + +/** + * @defgroup cpp_entities Entities + * @ingroup cpp_core + * Entity operations. + * + * @{ + */ + +namespace flecs +{ + +/** Entity. + * Class with read/write operations for entities. + * + * @ingroup cpp_entities +*/ +struct entity : entity_builder +{ + entity() : entity_builder() { } + + /** Create entity. + * + * @param world The world in which to create the entity. + */ + explicit entity(world_t *world) + : entity_builder() + { + world_ = world; + if (!ecs_get_scope(world_) && !ecs_get_with(world_)) { + id_ = ecs_new(world); + } else { + ecs_entity_desc_t desc = {}; + id_ = ecs_entity_init(world_, &desc); + } + } + + /** Wrap an existing entity id. + * + * @param world The world in which the entity is created. + * @param id The entity id. + */ + explicit entity(const flecs::world_t *world, flecs::entity_t id) { + world_ = const_cast(world); + id_ = id; + } + + /** Create a named entity. + * Named entities can be looked up with the lookup functions. Entity names + * may be scoped, where each element in the name is separated by "::". + * For example: "Foo::Bar". If parts of the hierarchy in the scoped name do + * not yet exist, they will be automatically created. + * + * @param world The world in which to create the entity. + * @param name The entity name. + */ + explicit entity(world_t *world, const char *name) + : entity_builder() + { + world_ = world; + + ecs_entity_desc_t desc = {}; + desc.name = name; + desc.sep = "::"; + desc.root_sep = "::"; + id_ = ecs_entity_init(world, &desc); + } + + /** Conversion from flecs::entity_t to flecs::entity. + * + * @param id The entity_t value to convert. + */ + explicit entity(entity_t id) + : entity_builder( nullptr, id ) { } + + #ifndef ensure + + /** Get mutable component value. + * This operation returns a mutable pointer to the component. If the entity + * did not yet have the component, it will be added. If a base entity had + * the component, it will be overridden, and the value of the base component + * will be copied to the entity before this function returns. + * + * @tparam T The component to get. + * @return Pointer to the component value. + */ + template + T& ensure() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return *static_cast(ecs_ensure_id(world_, id_, comp_id)); + } + + /** Get mutable component value (untyped). + * This operation returns a mutable pointer to the component. If the entity + * did not yet have the component, it will be added. If a base entity had + * the component, it will be overridden, and the value of the base component + * will be copied to the entity before this function returns. + * + * @param comp The component to get. + * @return Pointer to the component value. + */ + void* ensure(entity_t comp) const { + return ecs_ensure_id(world_, id_, comp); + } + + /** Get mutable pointer for a pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first part of the pair. + * @tparam Second the second part of the pair. + */ + template , + typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> + A& ensure() const { + return *static_cast(ecs_ensure_id(world_, id_, ecs_pair( + _::type::id(world_), + _::type::id(world_)))); + } + + /** Get mutable pointer for the first element of a pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first part of the pair. + * @param second The second element of the pair. + */ + template + First& ensure(entity_t second) const { + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return *static_cast( + ecs_ensure_id(world_, id_, ecs_pair(first, second))); + } + + /** Get mutable pointer for a pair (untyped). + * This operation gets the value for a pair from the entity. If neither the + * first nor second element of the pair is a component, the operation will + * fail. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + void* ensure(entity_t first, entity_t second) const { + return ecs_ensure_id(world_, id_, ecs_pair(first, second)); + } + + /** Get mutable pointer for the second element of a pair. + * This operation gets the value for a pair from the entity. + * + * @tparam Second The second element of the pair. + * @param first The first element of the pair. + */ + template + Second& ensure_second(entity_t first) const { + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return *static_cast( + ecs_ensure_id(world_, id_, ecs_pair(first, second))); + } + + #endif + + /** Signal that component was modified. + * + * @tparam T component that was modified. + */ + template + void modified() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + this->modified(comp_id); + } + + /** Signal that the first element of a pair was modified. + * + * @tparam First The first part of the pair. + * @tparam Second the second part of the pair. + */ + template >> + void modified() const { + auto first = _::type::id(world_); + auto second = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + this->modified(first, second); + } + + /** Signal that the first part of a pair was modified. + * + * @tparam First The first part of the pair. + * @param second The second element of the pair. + */ + template + void modified(entity_t second) const { + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + this->modified(first, second); + } + + /** Signal that a pair has modified (untyped). + * If neither the first or second element of the pair are a component, the + * operation will fail. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + void modified(entity_t first, entity_t second) const { + this->modified(ecs_pair(first, second)); + } + + /** Signal that component was modified. + * + * @param comp component that was modified. + */ + void modified(entity_t comp) const { + ecs_modified_id(world_, id_, comp); + } + + /** Get reference to component. + * A reference allows for quick and safe access to a component value, and is + * a faster alternative to repeatedly calling 'get' for the same component. + * + * @tparam T component for which to get a reference. + * @return The reference. + */ + template ::value > = 0> + ref get_ref() const { + return ref(world_, id_, _::type::id(world_)); + } + + /** Get reference to component. + * Overload for when T is not the same as the actual type, which happens + * when using pair types. + * A reference allows for quick and safe access to a component value, and is + * a faster alternative to repeatedly calling 'get' for the same component. + * + * @tparam T component for which to get a reference. + * @return The reference. + */ + template , if_t< flecs::is_pair::value > = 0> + ref get_ref() const { + return ref(world_, id_, + ecs_pair(_::type::id(world_), + _::type::id(world_))); + } + + + template , + typename A = actual_type_t

> + ref get_ref() const { + return ref(world_, id_, + ecs_pair(_::type::id(world_), _::type::id(world_))); + } + + template + ref get_ref(flecs::entity_t second) const { + auto first = _::type::id(world_); + return ref(world_, id_, ecs_pair(first, second)); + } + + template + ref get_ref_second(flecs::entity_t first) const { + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + return ref(world_, id_, ecs_pair(first, second)); + } + + /** Clear an entity. + * This operation removes all components from an entity without recycling + * the entity id. + * + * @see ecs_clear() + */ + void clear() const { + ecs_clear(world_, id_); + } + + /** Delete an entity. + * Entities have to be deleted explicitly, and are not deleted when the + * entity object goes out of scope. + * + * @see ecs_delete() + */ + void destruct() const { + ecs_delete(world_, id_); + } + + /** Return entity as entity_view. + * This returns an entity_view instance for the entity which is a readonly + * version of the entity class. + * + * This is similar to a regular upcast, except that this method ensures that + * the entity_view instance is instantiated with a world vs. a stage, which + * a regular upcast does not guarantee. + */ + flecs::entity_view view() const { + return flecs::entity_view( + const_cast(ecs_get_world(world_)), id_); + } + + /** Entity id 0. + * This function is useful when the API must provide an entity that + * belongs to a world, but the entity id is 0. + * + * @param world The world. + */ + static + flecs::entity null(const flecs::world_t *world) { + flecs::entity result; + result.world_ = const_cast(world); + return result; + } + + static + flecs::entity null() { + return flecs::entity(); + } + +# ifdef FLECS_JSON + +/** Deserialize entity to JSON. + * + * @memberof flecs::entity + * @ingroup cpp_addons_json + */ +const char* from_json(const char *json) { + return ecs_entity_from_json(world_, id_, json, nullptr); +} + +# endif +}; + +} // namespace flecs + +/** @} */ + +/** + * @file addons/cpp/delegate.hpp + * @brief Wrappers around C++ functions that provide callbacks for C APIs. + */ + +#pragma once + +#include // std::declval + +namespace flecs +{ + +namespace _ +{ + +// Binding ctx for component hooks +struct component_binding_ctx { + void *on_add = nullptr; + void *on_remove = nullptr; + void *on_set = nullptr; + ecs_ctx_free_t free_on_add = nullptr; + ecs_ctx_free_t free_on_remove = nullptr; + ecs_ctx_free_t free_on_set = nullptr; + + ~component_binding_ctx() { + if (on_add && free_on_add) { + free_on_add(on_add); + } + if (on_remove && free_on_remove) { + free_on_remove(on_remove); + } + if (on_set && free_on_set) { + free_on_set(on_set); + } + } +}; + +// Utility to convert template argument pack to array of term ptrs +struct field_ptr { + void *ptr = nullptr; + int8_t index = 0; + bool is_ref = false; + bool is_row = false; +}; + +template +struct field_ptrs { + using array = flecs::array<_::field_ptr, sizeof...(Components)>; + + void populate(const ecs_iter_t *iter) { + populate(iter, 0, static_cast< + remove_reference_t< + remove_pointer_t> + *>(nullptr)...); + } + + void populate_self(const ecs_iter_t *iter) { + populate_self(iter, 0, static_cast< + remove_reference_t< + remove_pointer_t> + *>(nullptr)...); + } + + array fields_; + +private: + void populate(const ecs_iter_t*, size_t) { } + + template >, + if_not_t< is_empty::value > = 0> + void populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + if (iter->row_fields & (1llu << index)) { + /* Need to fetch the value with ecs_field_at() */ + fields_[index].is_row = true; + fields_[index].is_ref = true; + fields_[index].index = static_cast(index); + } else { + fields_[index].ptr = ecs_field_w_size(iter, sizeof(A), + static_cast(index)); + fields_[index].is_ref = iter->sources[index] != 0; + } + + populate(iter, index + 1, comps ...); + } + + template >, + if_t< is_empty::value > = 0> + void populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + populate(iter, index + 1, comps ...); + } + + void populate_self(const ecs_iter_t*, size_t) { } + + template >, + if_not_t< is_empty::value > = 0> + void populate_self(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + fields_[index].ptr = ecs_field_w_size(iter, sizeof(A), + static_cast(index)); + fields_[index].is_ref = false; + ecs_assert(iter->sources[index] == 0, ECS_INTERNAL_ERROR, NULL); + populate_self(iter, index + 1, comps ...); + } + + template >, + if_t< is_empty::value > = 0> + void populate_self(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + populate(iter, index + 1, comps ...); + } +}; + +struct delegate { }; + +// Template that figures out from the template parameters of a query/system +// how to pass the value to the each callback +template +struct each_field { }; + +// Base class +struct each_column_base { + each_column_base(const _::field_ptr& field, size_t row) + : field_(field), row_(row) { + } + +protected: + const _::field_ptr& field_; + size_t row_; +}; + +// If type is not a pointer, return a reference to the type (default case) +template +struct each_field::value && + !is_empty>::value && is_actual::value > > + : each_column_base +{ + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } + + T& get_row() { + return static_cast(this->field_.ptr)[this->row_]; + } +}; + +// If argument type is not the same as actual component type, return by value. +// This requires that the actual type can be converted to the type. +// A typical scenario where this happens is when using flecs::pair types. +template +struct each_field::value && + !is_empty>::value && !is_actual::value> > + : each_column_base +{ + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } + + T get_row() { + return static_cast*>(this->field_.ptr)[this->row_]; + } +}; + +// If type is empty (indicating a tag) the query will pass a nullptr. To avoid +// returning nullptr to reference arguments, return a temporary value. +template +struct each_field>::value && + !is_pointer::value > > + : each_column_base +{ + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } + + T get_row() { + return actual_type_t(); + } +}; + +// If type is a pointer (indicating an optional value) don't index with row if +// the field is not set. +template +struct each_field::value && + !is_empty>::value > > + : each_column_base +{ + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } + + actual_type_t get_row() { + if (this->field_.ptr) { + return &static_cast>(this->field_.ptr)[this->row_]; + } else { + // optional argument doesn't have a value + return nullptr; + } + } +}; + +// If the query contains component references to other entities, check if the +// current argument is one. +template +struct each_ref_field : public each_field { + each_ref_field(const flecs::iter_t *iter, _::field_ptr& field, size_t row) + : each_field(iter, field, row) { + + if (field.is_ref) { + // If this is a reference, set the row to 0 as a ref always is a + // single value, not an array. This prevents the application from + // having to do an if-check on whether the column is owned. + // + // This check only happens when the current table being iterated + // over caused the query to match a reference. The check is + // performed once per iterated table. + this->row_ = 0; + } + + if (field.is_row) { + field.ptr = ecs_field_at_w_size(iter, sizeof(T), field.index, + static_cast(row)); + } + } +}; + +// Type that handles passing components to each callbacks +template +struct each_delegate : public delegate { + using Terms = typename field_ptrs::array; + + template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> + explicit each_delegate(Func&& func) noexcept + : func_(FLECS_MOV(func)) { } + + explicit each_delegate(const Func& func) noexcept + : func_(func) { } + + // Invoke object directly. This operation is useful when the calling + // function has just constructed the delegate, such as what happens when + // iterating a query. + void invoke(ecs_iter_t *iter) const { + field_ptrs terms; + + iter->flags |= EcsIterCppEach; + + if (iter->ref_fields | iter->up_fields) { + terms.populate(iter); + invoke_unpack< each_ref_field >(iter, func_, 0, terms.fields_); + } else { + terms.populate_self(iter); + invoke_unpack< each_field >(iter, func_, 0, terms.fields_); + } + } + + // Static function that can be used as callback for systems/triggers + static void run(ecs_iter_t *iter) { + auto self = static_cast(iter->callback_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + self->invoke(iter); + } + + // Create instance of delegate + static each_delegate* make(const Func& func) { + return FLECS_NEW(each_delegate)(func); + } + + // Function that can be used as callback to free delegate + static void destruct(void *obj) { + _::free_obj(static_cast(obj)); + } + + // Static function to call for component on_add hook + static void run_add(ecs_iter_t *iter) { + component_binding_ctx *ctx = reinterpret_cast( + iter->callback_ctx); + iter->callback_ctx = ctx->on_add; + run(iter); + } + + // Static function to call for component on_remove hook + static void run_remove(ecs_iter_t *iter) { + component_binding_ctx *ctx = reinterpret_cast( + iter->callback_ctx); + iter->callback_ctx = ctx->on_remove; + run(iter); + } + + // Static function to call for component on_set hook + static void run_set(ecs_iter_t *iter) { + component_binding_ctx *ctx = reinterpret_cast( + iter->callback_ctx); + iter->callback_ctx = ctx->on_set; + run(iter); + } + +private: + // func(flecs::entity, Components...) + template class ColumnType, + typename... Args, + typename Fn = Func, + decltype(std::declval()( + std::declval(), + std::declval > >().get_row()...), 0) = 0> + static void invoke_callback( + ecs_iter_t *iter, const Func& func, size_t i, Args... comps) + { + ecs_assert(iter->count > 0, ECS_INVALID_OPERATION, + "no entities returned, use each() without flecs::entity argument"); + + func(flecs::entity(iter->world, iter->entities[i]), + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...); + } + + // func(flecs::iter&, size_t row, Components...) + template class ColumnType, + typename... Args, + typename Fn = Func, + decltype(std::declval()( + std::declval(), + std::declval(), + std::declval > >().get_row()...), 0) = 0> + static void invoke_callback( + ecs_iter_t *iter, const Func& func, size_t i, Args... comps) + { + flecs::iter it(iter); + func(it, i, (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...); + } + + // func(Components...) + template class ColumnType, + typename... Args, + typename Fn = Func, + decltype(std::declval()( + std::declval > >().get_row()...), 0) = 0> + static void invoke_callback( + ecs_iter_t *iter, const Func& func, size_t i, Args... comps) + { + func((ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...); + } + + template class ColumnType, + typename... Args, if_t< + sizeof...(Components) == sizeof...(Args)> = 0> + static void invoke_unpack( + ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) + { + ECS_TABLE_LOCK(iter->world, iter->table); + + size_t count = static_cast(iter->count); + if (count == 0 && !iter->table) { + // If query has no This terms, count can be 0. Since each does not + // have an entity parameter, just pass through components + count = 1; + } + + for (size_t i = 0; i < count; i ++) { + invoke_callback(iter, func, i, comps...); + } + + ECS_TABLE_UNLOCK(iter->world, iter->table); + } + + template class ColumnType, + typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0> + static void invoke_unpack(ecs_iter_t *iter, const Func& func, + size_t index, Terms& columns, Args... comps) + { + invoke_unpack( + iter, func, index + 1, columns, comps..., columns[index]); + } + +public: + Func func_; +}; + +template +struct find_delegate : public delegate { + using Terms = typename field_ptrs::array; + + template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> + explicit find_delegate(Func&& func) noexcept + : func_(FLECS_MOV(func)) { } + + explicit find_delegate(const Func& func) noexcept + : func_(func) { } + + // Invoke object directly. This operation is useful when the calling + // function has just constructed the delegate, such as what happens when + // iterating a query. + flecs::entity invoke(ecs_iter_t *iter) const { + field_ptrs terms; + + iter->flags |= EcsIterCppEach; + + if (iter->ref_fields | iter->up_fields) { + terms.populate(iter); + return invoke_callback< each_ref_field >(iter, func_, 0, terms.fields_); + } else { + terms.populate_self(iter); + return invoke_callback< each_field >(iter, func_, 0, terms.fields_); + } + } + +private: + // Number of function arguments is one more than number of components, pass + // entity as argument. + template class ColumnType, + typename... Args, + typename Fn = Func, + if_t = 0, + decltype(bool(std::declval()( + std::declval(), + std::declval > >().get_row()...))) = true> + static flecs::entity invoke_callback( + ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) + { + ECS_TABLE_LOCK(iter->world, iter->table); + + ecs_world_t *world = iter->world; + size_t count = static_cast(iter->count); + flecs::entity result; + + ecs_assert(count > 0, ECS_INVALID_OPERATION, + "no entities returned, use find() without flecs::entity argument"); + + for (size_t i = 0; i < count; i ++) { + if (func(flecs::entity(world, iter->entities[i]), + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...)) + { + result = flecs::entity(world, iter->entities[i]); + break; + } + } + + ECS_TABLE_UNLOCK(iter->world, iter->table); + + return result; + } + + // Number of function arguments is two more than number of components, pass + // iter + index as argument. + template class ColumnType, + typename... Args, + typename Fn = Func, + if_t = 0, + decltype(bool(std::declval()( + std::declval(), + std::declval(), + std::declval > >().get_row()...))) = true> + static flecs::entity invoke_callback( + ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) + { + size_t count = static_cast(iter->count); + if (count == 0) { + // If query has no This terms, count can be 0. Since each does not + // have an entity parameter, just pass through components + count = 1; + } + + flecs::iter it(iter); + flecs::entity result; + + ECS_TABLE_LOCK(iter->world, iter->table); + + for (size_t i = 0; i < count; i ++) { + if (func(it, i, + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...)) + { + result = flecs::entity(iter->world, iter->entities[i]); + break; + } + } + + ECS_TABLE_UNLOCK(iter->world, iter->table); + + return result; + } + + // Number of function arguments is equal to number of components, no entity + template class ColumnType, + typename... Args, + typename Fn = Func, + if_t = 0, + decltype(bool(std::declval()( + std::declval > >().get_row()...))) = true> + static flecs::entity invoke_callback( + ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) + { + size_t count = static_cast(iter->count); + if (count == 0) { + // If query has no This terms, count can be 0. Since each does not + // have an entity parameter, just pass through components + count = 1; + } + + flecs::iter it(iter); + flecs::entity result; + + ECS_TABLE_LOCK(iter->world, iter->table); + + for (size_t i = 0; i < count; i ++) { + if (func( + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...)) + { + result = flecs::entity(iter->world, iter->entities[i]); + break; + } + } + + ECS_TABLE_UNLOCK(iter->world, iter->table); + + return result; + } + + template class ColumnType, + typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0> + static flecs::entity invoke_callback(ecs_iter_t *iter, const Func& func, + size_t index, Terms& columns, Args... comps) + { + return invoke_callback( + iter, func, index + 1, columns, comps..., columns[index]); + } + + Func func_; +}; + +//////////////////////////////////////////////////////////////////////////////// +//// Utility class to invoke a system iterate action +//////////////////////////////////////////////////////////////////////////////// + +template +struct run_delegate : delegate { + template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> + explicit run_delegate(Func&& func) noexcept + : func_(FLECS_MOV(func)) { } + + explicit run_delegate(const Func& func) noexcept + : func_(func) { } + + // Invoke object directly. This operation is useful when the calling + // function has just constructed the delegate, such as what happens when + // iterating a query. + void invoke(ecs_iter_t *iter) const { + flecs::iter it(iter); + iter->flags &= ~EcsIterIsValid; + func_(it); + } + + // Static function that can be used as callback for systems/triggers + static void run(ecs_iter_t *iter) { + auto self = static_cast(iter->run_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + self->invoke(iter); + } + + Func func_; +}; + + +//////////////////////////////////////////////////////////////////////////////// +//// Utility class to invoke an entity observer delegate +//////////////////////////////////////////////////////////////////////////////// + +template +struct entity_observer_delegate : delegate { + explicit entity_observer_delegate(Func&& func) noexcept + : func_(FLECS_MOV(func)) { } + + // Static function that can be used as callback for systems/triggers + static void run(ecs_iter_t *iter) { + invoke(iter); + } + +private: + template ()(std::declval()), 0) = 0> + static void invoke(ecs_iter_t *iter) { + auto self = static_cast(iter->callback_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0))); + } + + template ()(), 0) = 0> + static void invoke(ecs_iter_t *iter) { + auto self = static_cast(iter->callback_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + self->func_(); + } + + Func func_; +}; + +template +struct entity_payload_observer_delegate : delegate { + explicit entity_payload_observer_delegate(Func&& func) noexcept + : func_(FLECS_MOV(func)) { } + + // Static function that can be used as callback for systems/triggers + static void run(ecs_iter_t *iter) { + invoke(iter); + } + +private: + template ()( + std::declval()), 0) = 0> + static void invoke(ecs_iter_t *iter) { + auto self = static_cast( + iter->callback_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION, + "entity observer invoked without payload"); + + Event *data = static_cast(iter->param); + self->func_(*data); + } + + template ()( + std::declval(), + std::declval()), 0) = 0> + static void invoke(ecs_iter_t *iter) { + auto self = static_cast( + iter->callback_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION, + "entity observer invoked without payload"); + + Event *data = static_cast(iter->param); + self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0)), *data); + } + + Func func_; +}; + + +//////////////////////////////////////////////////////////////////////////////// +//// Utility to invoke callback on entity if it has components in signature +//////////////////////////////////////////////////////////////////////////////// + +template +struct entity_with_delegate_impl; + +template +struct entity_with_delegate_impl> { + using ColumnArray = flecs::array; + using ArrayType = flecs::array; + using DummyArray = flecs::array; + using IdArray = flecs::array; + + static bool const_args() { + static flecs::array is_const_args ({ + flecs::is_const>::value... + }); + + for (auto is_const : is_const_args) { + if (!is_const) { + return false; + } + } + return true; + } + + static + bool get_ptrs(world_t *world, flecs::entity_t e, const ecs_record_t *r, ecs_table_t *table, + ArrayType& ptrs) + { + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + if (!ecs_table_column_count(table) && + !ecs_table_has_flags(table, EcsTableHasSparse)) + { + return false; + } + + /* table_index_of needs real world */ + const flecs::world_t *real_world = ecs_get_world(world); + + IdArray ids ({ + _::type().id(world)... + }); + + /* Get column indices for components */ + ColumnArray columns ({ + ecs_table_get_column_index(real_world, table, + _::type().id(world))... + }); + + /* Get pointers for columns for entity */ + size_t i = 0; + for (int32_t column : columns) { + if (column == -1) { + /* Component could be sparse */ + void *ptr = ecs_get_mut_id(world, e, ids[i]); + if (!ptr) { + return false; + } + + ptrs[i ++] = ptr; + continue; + } + + ptrs[i ++] = ecs_record_get_by_column(r, column, 0); + } + + return true; + } + + static bool ensure_ptrs(world_t *world, ecs_entity_t e, ArrayType& ptrs) { + /* Get pointers w/ensure */ + size_t i = 0; + DummyArray dummy ({ + (ptrs[i ++] = ecs_ensure_id(world, e, + _::type().id(world)), 0)... + }); + + return true; + } + + template + static bool invoke_read(world_t *world, entity_t e, const Func& func) { + const ecs_record_t *r = ecs_read_begin(world, e); + if (!r) { + return false; + } + + ecs_table_t *table = r->table; + if (!table) { + return false; + } + + ArrayType ptrs; + bool has_components = get_ptrs(world, e, r, table, ptrs); + if (has_components) { + invoke_callback(func, 0, ptrs); + } + + ecs_read_end(r); + + return has_components; + } + + template + static bool invoke_write(world_t *world, entity_t e, const Func& func) { + ecs_record_t *r = ecs_write_begin(world, e); + if (!r) { + return false; + } + + ecs_table_t *table = r->table; + if (!table) { + return false; + } + + ArrayType ptrs; + bool has_components = get_ptrs(world, e, r, table, ptrs); + if (has_components) { + invoke_callback(func, 0, ptrs); + } + + ecs_write_end(r); + + return has_components; + } + + template + static bool invoke_get(world_t *world, entity_t e, const Func& func) { + if (const_args()) { + return invoke_read(world, e, func); + } else { + return invoke_write(world, e, func); + } + } + + // Utility for storing id in array in pack expansion + static size_t store_added(IdArray& added, size_t elem, ecs_table_t *prev, + ecs_table_t *next, id_t id) + { + // Array should only contain ids for components that are actually added, + // so check if the prev and next tables are different. + if (prev != next) { + added[elem] = id; + elem ++; + } + return elem; + } + + template + static bool invoke_ensure(world_t *world, entity_t id, const Func& func) { + flecs::world w(world); + + ArrayType ptrs; + ecs_table_t *table = NULL; + + // When not deferred take the fast path. + if (!w.is_deferred()) { + // Bit of low level code so we only do at most one table move & one + // entity lookup for the entire operation. + + // Make sure the object is not a stage. Operations on a stage are + // only allowed when the stage is in deferred mode, which is when + // the world is in readonly mode. + ecs_assert(!w.is_stage(), ECS_INVALID_PARAMETER, NULL); + + // Find table for entity + ecs_record_t *r = ecs_record_find(world, id); + if (r) { + table = r->table; + } + + // Find destination table that has all components + ecs_table_t *prev = table, *next; + size_t elem = 0; + IdArray added; + + // Iterate components, only store added component ids in added array + DummyArray dummy_before ({ ( + next = ecs_table_add_id(world, prev, w.id()), + elem = store_added(added, elem, prev, next, w.id()), + prev = next, 0 + )... }); + + (void)dummy_before; + + // If table is different, move entity straight to it + if (table != next) { + ecs_type_t ids; + ids.array = added.ptr(); + ids.count = static_cast(elem); + ecs_commit(world, id, r, next, &ids, NULL); + table = next; + } + + if (!get_ptrs(w, id, r, table, ptrs)) { + ecs_abort(ECS_INTERNAL_ERROR, NULL); + } + + ECS_TABLE_LOCK(world, table); + + // When deferred, obtain pointers with regular ensure + } else { + ensure_ptrs(world, id, ptrs); + } + + invoke_callback(func, 0, ptrs); + + if (!w.is_deferred()) { + ECS_TABLE_UNLOCK(world, table); + } + + // Call modified on each component + DummyArray dummy_after ({ + ( ecs_modified_id(world, id, w.id()), 0)... + }); + (void)dummy_after; + + return true; + } + +private: + template = 0> + static void invoke_callback( + const Func& f, size_t, ArrayType&, TArgs&& ... comps) + { + f(*static_cast::type*>(comps)...); + } + + template = 0> + static void invoke_callback(const Func& f, size_t arg, ArrayType& ptrs, + TArgs&& ... comps) + { + invoke_callback(f, arg + 1, ptrs, comps..., ptrs[arg]); + } +}; + +template +struct entity_with_delegate { + static_assert(function_traits::value, "type is not callable"); +}; + +template +struct entity_with_delegate::value > > + : entity_with_delegate_impl< arg_list_t > +{ + static_assert(function_traits::arity > 0, + "function must have at least one argument"); +}; + +} // namespace _ + +// Experimental: allows using the each delegate for use cases outside of flecs +template +using delegate = _::each_delegate::type, Args...>; + +} // namespace flecs + +/** + * @file addons/cpp/component.hpp + * @brief Registering/obtaining info from components. + */ + +#pragma once + +#include +#include + +/** + * @defgroup cpp_components Components + * @ingroup cpp_core + * Registering and working with components. + * + * @{ + */ + +namespace flecs { + +namespace _ { + +// Trick to obtain typename from type, as described here +// https://blog.molecular-matters.com/2015/12/11/getting-the-type-of-a-template-argument-as-string-without-rtti/ +// +// The code from the link has been modified to work with more types, and across +// multiple compilers. The resulting string should be the same on all platforms +// for all compilers. +// + +#if defined(__GNUC__) || defined(_WIN32) +template +inline const char* type_name() { + static const size_t len = ECS_FUNC_TYPE_LEN(const char*, type_name, ECS_FUNC_NAME); + static char result[len + 1] = {}; + static const size_t front_len = ECS_FUNC_NAME_FRONT(const char*, type_name); + static const char* cppTypeName = ecs_cpp_get_type_name(result, ECS_FUNC_NAME, len, front_len); + return cppTypeName; +} +#else +#error "implicit component registration not supported" +#endif + +// Translate a typename into a language-agnostic identifier. This allows for +// registration of components/modules across language boundaries. +template +inline const char* symbol_name() { + static const size_t len = ECS_FUNC_TYPE_LEN(const char*, symbol_name, ECS_FUNC_NAME); + static char result[len + 1] = {}; + static const char* cppSymbolName = ecs_cpp_get_symbol_name(result, type_name(), len); + return cppSymbolName; +} + +template <> inline const char* symbol_name() { + return "u8"; +} +template <> inline const char* symbol_name() { + return "u16"; +} +template <> inline const char* symbol_name() { + return "u32"; +} +template <> inline const char* symbol_name() { + return "u64"; +} +template <> inline const char* symbol_name() { + return "i8"; +} +template <> inline const char* symbol_name() { + return "i16"; +} +template <> inline const char* symbol_name() { + return "i32"; +} +template <> inline const char* symbol_name() { + return "i64"; +} +template <> inline const char* symbol_name() { + return "f32"; +} +template <> inline const char* symbol_name() { + return "f64"; +} + +// If type is trivial, don't register lifecycle actions. While the functions +// that obtain the lifecycle callback do detect whether the callback is required +// adding a special case for trivial types eases the burden a bit on the +// compiler as it reduces the number of templates to evaluate. +template::value == true + >* = nullptr> +void register_lifecycle_actions(ecs_world_t*, ecs_entity_t) { } + +// If the component is non-trivial, register component lifecycle actions. +// Depending on the type not all callbacks may be available. +template::value == false + >* = nullptr> +void register_lifecycle_actions( + ecs_world_t *world, + ecs_entity_t component) +{ + ecs_type_hooks_t cl{}; + cl.ctor = ctor(); + cl.dtor = dtor(); + + cl.copy = copy(); + cl.copy_ctor = copy_ctor(); + cl.move = move(); + cl.move_ctor = move_ctor(); + + cl.ctor_move_dtor = ctor_move_dtor(); + cl.move_dtor = move_dtor(); + + ecs_set_hooks_id( world, component, &cl); + + if (cl.move == ecs_move_illegal || cl.move_ctor == ecs_move_ctor_illegal) { + ecs_add_id(world, component, flecs::Sparse); + } +} + +// Class that manages component ids across worlds & binaries. +// The type class stores the component id for a C++ type in a static global +// variable that is shared between worlds. Whenever a component is used this +// class will check if it already has been registered (has the global id been +// set), and if not, register the component with the world. +// +// If the id has been set, the class will ensure it is known by the world. If it +// is not known the component has been registered by another world and will be +// registered with the world using the same id. If the id does exist, the class +// will register it as a component, and verify whether the input is consistent. +template +struct type_impl { + static_assert(is_pointer::value == false, + "pointer types are not allowed for components"); + + // Initialize component identifier + static void init( + entity_t entity, + bool allow_tag = true) + { + if (s_reset_count != ecs_cpp_reset_count_get()) { + reset(); + } + + // If an identifier was already set, check for consistency + if (s_id) { + ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, + type_name()); + ecs_assert(allow_tag == s_allow_tag, ECS_INVALID_PARAMETER, NULL); + + // Component was already registered and data is consistent with new + // identifier, so nothing else to be done. + return; + } + + // Component wasn't registered yet, set the values. Register component + // name as the fully qualified flecs path. + s_id = entity; + s_allow_tag = allow_tag; + s_size = sizeof(T); + s_alignment = alignof(T); + if (is_empty::value && allow_tag) { + s_size = 0; + s_alignment = 0; + } + + s_reset_count = ecs_cpp_reset_count_get(); + } + + // Obtain a component identifier for explicit component registration. + static entity_t id_explicit(world_t *world = nullptr, + const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0, + bool is_component = true, bool *existing = nullptr) + { + if (!s_id) { + // If no world was provided the component cannot be registered + ecs_assert(world != nullptr, ECS_COMPONENT_NOT_REGISTERED, name); + } else { + ecs_assert(!id || s_id == id, ECS_INCONSISTENT_COMPONENT_ID, NULL); + } + + // If no id has been registered yet for the component (indicating the + // component has not yet been registered, or the component is used + // across more than one binary), or if the id does not exists in the + // world (indicating a multi-world application), register it. + if (!s_id || (world && !ecs_exists(world, s_id))) { + init(s_id ? s_id : id, allow_tag); + + ecs_assert(!id || s_id == id, ECS_INTERNAL_ERROR, NULL); + + const char *symbol = nullptr; + if (id) { + symbol = ecs_get_symbol(world, id); + } + if (!symbol) { + symbol = symbol_name(); + } + + entity_t entity = ecs_cpp_component_register_explicit( + world, s_id, id, name, type_name(), symbol, + s_size, s_alignment, is_component, existing); + + s_id = entity; + + // If component is enum type, register constants + #if FLECS_CPP_ENUM_REFLECTION_SUPPORT + _::init_enum(world, entity); + #endif + } + + // By now the identifier must be valid and known with the world. + ecs_assert(s_id != 0 && ecs_exists(world, s_id), + ECS_INTERNAL_ERROR, NULL); + + return s_id; + } + + // Obtain a component identifier for implicit component registration. This + // is almost the same as id_explicit, except that this operation + // automatically registers lifecycle callbacks. + // Additionally, implicit registration temporarily resets the scope & with + // state of the world, so that the component is not implicitly created with + // the scope/with of the code it happens to be first used by. + static id_t id(world_t *world = nullptr, const char *name = nullptr, + bool allow_tag = true) + { + // If no id has been registered yet, do it now. +#ifndef FLECS_CPP_NO_AUTO_REGISTRATION + if (!registered(world)) { + ecs_entity_t prev_scope = 0; + ecs_id_t prev_with = 0; + + if (world) { + prev_scope = ecs_set_scope(world, 0); + prev_with = ecs_set_with(world, 0); + } + + // This will register a component id, but will not register + // lifecycle callbacks. + bool existing; + id_explicit(world, name, allow_tag, 0, true, &existing); + + // Register lifecycle callbacks, but only if the component has a + // size. Components that don't have a size are tags, and tags don't + // require construction/destruction/copy/move's. + if (size() && !existing) { + register_lifecycle_actions(world, s_id); + } + + if (prev_with) { + ecs_set_with(world, prev_with); + } + if (prev_scope) { + ecs_set_scope(world, prev_scope); + } + } +#else + (void)world; + (void)name; + (void)allow_tag; + + ecs_assert(registered(world), ECS_INVALID_OPERATION, + "component '%s' was not registered before use", + type_name()); +#endif + + // By now we should have a valid identifier + ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + + return s_id; + } + + // Return the size of a component. + static size_t size() { + ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + return s_size; + } + + // Return the alignment of a component. + static size_t alignment() { + ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + return s_alignment; + } + + // Was the component already registered. + static bool registered(flecs::world_t *world) { + if (s_reset_count != ecs_cpp_reset_count_get()) { + reset(); + } + if (s_id == 0) { + return false; + } + if (world && !ecs_exists(world, s_id)) { + return false; + } + return true; + } + + // This function is only used to test cross-translation unit features. No + // code other than test cases should invoke this function. + static void reset() { + s_id = 0; + s_size = 0; + s_alignment = 0; + s_allow_tag = true; + } + + static entity_t s_id; + static size_t s_size; + static size_t s_alignment; + static bool s_allow_tag; + static int32_t s_reset_count; +}; + +// Global templated variables that hold component identifier and other info +template entity_t type_impl::s_id; +template size_t type_impl::s_size; +template size_t type_impl::s_alignment; +template bool type_impl::s_allow_tag( true ); +template int32_t type_impl::s_reset_count; + +// Front facing class for implicitly registering a component & obtaining +// static component data + +// Regular type +template +struct type::value >> + : type_impl> { }; + +// Pair type +template +struct type::value >> +{ + // Override id method to return id of pair + static id_t id(world_t *world = nullptr) { + return ecs_pair( + type< pair_first_t >::id(world), + type< pair_second_t >::id(world)); + } +}; + +} // namespace _ + +/** Untyped component class. + * Generic base class for flecs::component. + * + * @ingroup cpp_components + */ +struct untyped_component : entity { + using entity::entity; + +# ifdef FLECS_META +/** + * @file addons/cpp/mixins/meta/untyped_component.inl + * @brief Meta component mixin. + */ + +/** + * @memberof flecs::component + * @ingroup cpp_addons_meta + * + * @{ + */ + +private: + +/** Private method that adds member to component. */ +untyped_component& internal_member( + flecs::entity_t type_id, + flecs::entity_t unit, + const char *name, + int32_t count = 0, + size_t offset = 0, + bool use_offset = false) +{ + ecs_entity_desc_t desc = {}; + desc.name = name; + desc.parent = id_; + ecs_entity_t eid = ecs_entity_init(world_, &desc); + ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); + + flecs::entity e(world_, eid); + + Member m = {}; + m.type = type_id; + m.unit = unit; + m.count = count; + m.offset = static_cast(offset); + m.use_offset = use_offset; + e.set(m); + + return *this; +} + +public: + +/** Add member with unit. */ +untyped_component& member( + flecs::entity_t type_id, + flecs::entity_t unit, + const char *name, + int32_t count = 0) +{ + return internal_member(type_id, unit, name, count, 0, false); +} + +/** Add member with unit, count and offset. */ +untyped_component& member( + flecs::entity_t type_id, + flecs::entity_t unit, + const char *name, + int32_t count, + size_t offset) +{ + return internal_member(type_id, unit, name, count, offset, true); +} + +/** Add member. */ +untyped_component& member( + flecs::entity_t type_id, + const char* name, + int32_t count = 0) +{ + return member(type_id, 0, name, count); +} + +/** Add member with count and offset. */ +untyped_component& member( + flecs::entity_t type_id, + const char* name, + int32_t count, + size_t offset) +{ + return member(type_id, 0, name, count, offset); +} + +/** Add member. */ +template +untyped_component& member( + const char *name, + int32_t count = 0) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, name, count); +} + +/** Add member. */ +template +untyped_component& member( + const char *name, + int32_t count, + size_t offset) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, name, count, offset); +} + +/** Add member with unit. */ +template +untyped_component& member( + flecs::entity_t unit, + const char *name, + int32_t count = 0) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, unit, name, count); +} + +/** Add member with unit. */ +template +untyped_component& member( + flecs::entity_t unit, + const char *name, + int32_t count, + size_t offset) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, unit, name, count, offset); +} + +/** Add member with unit. */ +template +untyped_component& member( + const char *name, + int32_t count = 0) +{ + flecs::entity_t type_id = _::type::id(world_); + flecs::entity_t unit_id = _::type::id(world_); + return member(type_id, unit_id, name, count); +} + +/** Add member with unit. */ +template +untyped_component& member( + const char *name, + int32_t count, + size_t offset) +{ + flecs::entity_t type_id = _::type::id(world_); + flecs::entity_t unit_id = _::type::id(world_); + return member(type_id, unit_id, name, count, offset); +} + +/** Add member using pointer-to-member. */ +template ::type> +untyped_component& member( + const char* name, + const MemberType ComponentType::* ptr) +{ + flecs::entity_t type_id = _::type::id(world_); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, name, std::extent::value, offset); +} + +/** Add member with unit using pointer-to-member. */ +template ::type> +untyped_component& member( + flecs::entity_t unit, + const char* name, + const MemberType ComponentType::* ptr) +{ + flecs::entity_t type_id = _::type::id(world_); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, unit, name, std::extent::value, offset); +} + +/** Add member with unit using pointer-to-member. */ +template ::type> +untyped_component& member( + const char* name, + const MemberType ComponentType::* ptr) +{ + flecs::entity_t type_id = _::type::id(world_); + flecs::entity_t unit_id = _::type::id(world_); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, unit_id, name, std::extent::value, offset); +} + +/** Add constant. */ +untyped_component& constant( + const char *name, + int32_t value) +{ + ecs_add_id(world_, id_, _::type::id(world_)); + + ecs_entity_desc_t desc = {}; + desc.name = name; + desc.parent = id_; + ecs_entity_t eid = ecs_entity_init(world_, &desc); + ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); + + ecs_set_id(world_, eid, + ecs_pair(flecs::Constant, flecs::I32), sizeof(int32_t), + &value); + + return *this; +} + +/** Add bitmask constant. */ +untyped_component& bit( + const char *name, + uint32_t value) +{ + ecs_add_id(world_, id_, _::type::id(world_)); + + ecs_entity_desc_t desc = {}; + desc.name = name; + desc.parent = id_; + ecs_entity_t eid = ecs_entity_init(world_, &desc); + ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); + + ecs_set_id(world_, eid, + ecs_pair(flecs::Constant, flecs::U32), sizeof(uint32_t), + &value); + + return *this; +} + +/** Register array metadata for component */ +template +untyped_component& array( + int32_t elem_count) +{ + ecs_array_desc_t desc = {}; + desc.entity = id_; + desc.type = _::type::id(world_); + desc.count = elem_count; + ecs_array_init(world_, &desc); + return *this; +} + +/** Add member value range */ +untyped_component& range( + double min, + double max) +{ + const flecs::member_t *m = ecs_cpp_last_member(world_, id_); + if (!m) { + return *this; + } + + flecs::world w(world_); + flecs::entity me = w.entity(m->member); + + // Don't use C++ ensure because Unreal defines a macro called ensure + flecs::MemberRanges *mr = static_cast( + ecs_ensure_id(w, me, w.id())); + mr->value.min = min; + mr->value.max = max; + me.modified(); + return *this; +} + +/** Add member warning range */ +untyped_component& warning_range( + double min, + double max) +{ + const flecs::member_t *m = ecs_cpp_last_member(world_, id_); + if (!m) { + return *this; + } + + flecs::world w(world_); + flecs::entity me = w.entity(m->member); + + // Don't use C++ ensure because Unreal defines a macro called ensure + flecs::MemberRanges *mr = static_cast( + ecs_ensure_id(w, me, w.id())); + mr->warning.min = min; + mr->warning.max = max; + me.modified(); + return *this; +} + +/** Add member error range */ +untyped_component& error_range( + double min, + double max) +{ + const flecs::member_t *m = ecs_cpp_last_member(world_, id_); + if (!m) { + return *this; + } + + flecs::world w(world_); + flecs::entity me = w.entity(m->member); + + // Don't use C++ ensure because Unreal defines a macro called ensure + flecs::MemberRanges *mr = static_cast(ecs_ensure_id( + w, me, w.id())); + mr->error.min = min; + mr->error.max = max; + me.modified(); + return *this; +} + +/** @} */ + +# endif +# ifdef FLECS_METRICS +/** + * @file addons/cpp/mixins/meta/untyped_component.inl + * @brief Metrics component mixin. + */ + +/** + * @memberof flecs::component + * @ingroup cpp_addons_metrics + * + * @{ + */ + +/** Register member as metric. + * When no explicit name is provided, this operation will derive the metric name + * from the member name. When the member name is "value", the operation will use + * the name of the component. + * + * When the brief parameter is provided, it is set on the metric as if + * set_doc_brief is used. The brief description can be obtained with + * get_doc_brief. + * + * @tparam Kind Metric kind (Counter, CounterIncrement or Gauge). + * @param parent Parent entity of the metric (optional). + * @param brief Description for metric (optional). + * @param name Name of metric (optional). + */ +template +untyped_component& metric( + flecs::entity_t parent = 0, + const char *brief = nullptr, + const char *name = nullptr); + +/** @} */ + +# endif +}; + +/** Component class. + * Class used to register components and component metadata. + * + * @ingroup cpp_components + */ +template +struct component : untyped_component { + /** Register a component. + * If the component was already registered, this operation will return a handle + * to the existing component. + * + * @param world The world for which to register the component. + * @param name Optional name (overrides typename). + * @param allow_tag If true, empty types will be registered with size 0. + * @param id Optional id to register component with. + */ + component( + flecs::world_t *world, + const char *name = nullptr, + bool allow_tag = true, + flecs::id_t id = 0) + { + const char *n = name; + bool implicit_name = false; + if (!n) { + n = _::type_name(); + + /* Keep track of whether name was explicitly set. If not, and the + * component was already registered, just use the registered name. + * + * The registered name may differ from the typename as the registered + * name includes the flecs scope. This can in theory be different from + * the C++ namespace though it is good practice to keep them the same */ + implicit_name = true; + } + + if (_::type::registered(world)) { + /* Obtain component id. Because the component is already registered, + * this operation does nothing besides returning the existing id */ + id = _::type::id_explicit(world, name, allow_tag, id); + + ecs_cpp_component_validate(world, id, n, _::symbol_name(), + _::type::size(), + _::type::alignment(), + implicit_name); + } else { + /* If component is registered from an existing scope, ignore the + * namespace in the name of the component. */ + if (implicit_name && (ecs_get_scope(world) != 0)) { + /* If the type is a template type, make sure to ignore ':' + * inside the template parameter list. */ + const char *start = strchr(n, '<'), *last_elem = NULL; + if (start) { + const char *ptr = start; + while (ptr[0] && (ptr[0] != ':') && (ptr > n)) { + ptr --; + } + if (ptr[0] == ':') { + last_elem = ptr; + } + } + + if (last_elem) { + name = last_elem + 1; + } + } + + /* Find or register component */ + bool existing; + id = ecs_cpp_component_register(world, id, n, _::symbol_name(), + ECS_SIZEOF(T), ECS_ALIGNOF(T), implicit_name, &existing); + + /* Initialize static component data */ + id = _::type::id_explicit(world, name, allow_tag, id); + + /* Initialize lifecycle actions (ctor, dtor, copy, move) */ + if (_::type::size() && !existing) { + _::register_lifecycle_actions(world, id); + } + } + + world_ = world; + id_ = id; + } + + /** Register on_add hook. */ + template + component& on_add(Func&& func) { + using Delegate = typename _::each_delegate::type, T>; + flecs::type_hooks_t h = get_hooks(); + ecs_assert(h.on_add == nullptr, ECS_INVALID_OPERATION, + "on_add hook is already set"); + BindingCtx *ctx = get_binding_ctx(h); + h.on_add = Delegate::run_add; + ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func)); + ctx->free_on_add = reinterpret_cast( + _::free_obj); + ecs_set_hooks_id(world_, id_, &h); + return *this; + } + + /** Register on_remove hook. */ + template + component& on_remove(Func&& func) { + using Delegate = typename _::each_delegate< + typename std::decay::type, T>; + flecs::type_hooks_t h = get_hooks(); + ecs_assert(h.on_remove == nullptr, ECS_INVALID_OPERATION, + "on_remove hook is already set"); + BindingCtx *ctx = get_binding_ctx(h); + h.on_remove = Delegate::run_remove; + ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func)); + ctx->free_on_remove = reinterpret_cast( + _::free_obj); + ecs_set_hooks_id(world_, id_, &h); + return *this; + } + + /** Register on_set hook. */ + template + component& on_set(Func&& func) { + using Delegate = typename _::each_delegate< + typename std::decay::type, T>; + flecs::type_hooks_t h = get_hooks(); + ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION, + "on_set hook is already set"); + BindingCtx *ctx = get_binding_ctx(h); + h.on_set = Delegate::run_set; + ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func)); + ctx->free_on_set = reinterpret_cast( + _::free_obj); + ecs_set_hooks_id(world_, id_, &h); + return *this; + } + +# ifdef FLECS_META + +/** Register opaque type interface */ +template +component& opaque(const Func& type_support) { + flecs::world world(world_); + auto ts = type_support(world); + ts.desc.entity = _::type::id(world_); + ecs_opaque_init(world_, &ts.desc); + return *this; +} + +flecs::opaque opaque(flecs::entity_t as_type) { + return flecs::opaque(world_).as_type(as_type); +} + +flecs::opaque opaque(flecs::entity as_type) { + return this->opaque(as_type.id()); +} + +flecs::opaque opaque(flecs::untyped_component as_type) { + return this->opaque(as_type.id()); +} + +/** Return opaque type builder for collection type */ +template +flecs::opaque opaque(flecs::id_t as_type) { + return flecs::opaque(world_).as_type(as_type); +} + +/** Add constant. */ +component& constant(const char *name, T value) { + int32_t v = static_cast(value); + untyped_component::constant(name, v); + return *this; +} + +# endif + +private: + using BindingCtx = _::component_binding_ctx; + + BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){ + BindingCtx *result = static_cast(h.binding_ctx); + if (!result) { + result = FLECS_NEW(BindingCtx); + h.binding_ctx = result; + h.binding_ctx_free = reinterpret_cast( + _::free_obj); + } + return result; + } + + flecs::type_hooks_t get_hooks() { + const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_); + if (h) { + return *h; + } else { + return {}; + } + } +}; + +/** Get id currently assigned to component. If no world has registered the + * component yet, this operation will return 0. */ +template +flecs::entity_t type_id() { + if (_::type::s_reset_count == ecs_cpp_reset_count_get()) { + return _::type::s_id; + } else { + return 0; + } +} + +/** Reset static component ids. + * When components are registered their component ids are stored in a static + * type specific variable. This stored id is passed into component registration + * functions to ensure consistent ids across worlds. + * + * In some cases this can be undesirable, like when a process repeatedly creates + * worlds with different components. A typical example where this can happen is + * when running multiple tests in a single process, where each test registers + * its own set of components. + * + * This operation can be used to prevent reusing of component ids and force + * generating a new ids upon registration. + * + * Note that this operation should *never* be called while there are still + * alive worlds in a process. Doing so results in undefined behavior. + * + * Also note that this operation does not actually change the static component + * variables. It only ensures that the next time a component id is requested, a + * new id will be generated. + * + * @ingroup cpp_components + */ +inline void reset() { + ecs_cpp_reset_count_inc(); +} + +} + +/** @} */ + +/** + * @file addons/cpp/ref.hpp + * @brief Class that caches data to speedup get operations. + */ + +#pragma once + +namespace flecs +{ + +/** + * @defgroup cpp_ref Refs + * @ingroup cpp_core + * Refs are a fast mechanism for referring to a specific entity/component. + * + * @{ + */ + +/** Component reference. + * Reference to a component from a specific entity. + */ +template +struct ref { + ref() : world_(nullptr), ref_{} { } + + ref(world_t *world, entity_t entity, flecs::id_t id = 0) + : ref_() + { + // the world we were called with may be a stage; convert it to a world + // here if that is the case + world_ = world ? const_cast(ecs_get_world(world)) + : nullptr; + if (!id) { + id = _::type::id(world); + } + + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + + ref_ = ecs_ref_init_id(world_, entity, id); + } + + ref(flecs::entity entity, flecs::id_t id = 0) + : ref(entity.world(), entity.id(), id) { } + + T* operator->() { + T* result = static_cast(ecs_ref_get_id( + world_, &ref_, this->ref_.id)); + + ecs_assert(result != NULL, ECS_INVALID_PARAMETER, + "nullptr dereference by flecs::ref"); + + return result; + } + + T* get() { + return static_cast(ecs_ref_get_id( + world_, &ref_, this->ref_.id)); + } + + T* try_get() { + if (!world_ || !ref_.entity) { + return nullptr; + } + + return get(); + } + + bool has() { + return !!try_get(); + } + + /** implicit conversion to bool. return true if there is a valid T* being referred to **/ + operator bool() { + return has(); + } + + flecs::entity entity() const; + +private: + world_t *world_; + flecs::ref_t ref_; +}; + +/** @} */ + +} + +/** + * @file addons/cpp/type.hpp + * @brief Utility functions for id vector. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_types Types + * @ingroup cpp_core + * @brief Type operations. + * + * @{ + */ + +/** Type class. + * A type is a vector of component ids which can be requested from entities or tables. + */ +struct type { + type() : world_(nullptr), type_(nullptr) { } + + type(world_t *world, const type_t *t) + : world_(world) + , type_(t) { } + + /** Convert type to comma-separated string */ + flecs::string str() const { + return flecs::string(ecs_type_str(world_, type_)); + } + + /** Return number of ids in type */ + int32_t count() const { + if (!type_) { + return 0; + } + return type_->count; + } + + /** Return pointer to array. */ + flecs::id_t* array() const { + if (!type_) { + return nullptr; + } + return type_->array; + } + + /** Get id at specified index in type */ + flecs::id get(int32_t index) const { + ecs_assert(type_ != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(type_->count > index, ECS_OUT_OF_RANGE, NULL); + if (!type_) { + return flecs::id(); + } + return flecs::id(world_, type_->array[index]); + } + + flecs::id_t* begin() const { + return type_->array; + } + + flecs::id_t* end() const { + return &type_->array[type_->count]; + } + + /** Implicit conversion to type_t */ + operator const type_t*() const { + return type_; + } +private: + world_t *world_; + const type_t *type_; +}; + +/** #} */ + +} + +/** + * @file addons/cpp/table.hpp + * @brief Direct access to table data. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_tables Tables + * @ingroup cpp_core + * Table operations. + * + * @{ + */ + +struct table { + table() : world_(nullptr), table_(nullptr) { } + + table(world_t *world, table_t *t) + : world_(world) + , table_(t) { } + + virtual ~table() { } + + /** Convert table type to string. */ + flecs::string str() const { + return flecs::string(ecs_table_str(world_, table_)); + } + + /** Get table type. */ + flecs::type type() const { + return flecs::type(world_, ecs_table_get_type(table_)); + } + + /** Get table count. */ + int32_t count() const { + return ecs_table_count(table_); + } + + /** Find type index for (component) id. + * + * @param id The (component) id. + * @return The index of the id in the table type, -1 if not found/ + */ + int32_t type_index(flecs::id_t id) const { + return ecs_table_get_type_index(world_, table_, id); + } + + /** Find type index for type. + * + * @tparam T The type. + * @return True if the table has the type, false if not. + */ + template + int32_t type_index() const { + return type_index(_::type::id(world_)); + } + + /** Find type index for pair. + * @param first First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + int32_t type_index(flecs::entity_t first, flecs::entity_t second) const { + return type_index(ecs_pair(first, second)); + } + + /** Find type index for pair. + * @tparam First First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + int32_t type_index(flecs::entity_t second) const { + return type_index(_::type::id(world_), second); + } + + /** Find type index for pair. + * @tparam First First element of pair. + * @tparam Second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + int32_t type_index() const { + return type_index(_::type::id(world_)); + } + + /** Find column index for (component) id. + * + * @param id The (component) id. + * @return The index of the id in the table type, -1 if not found/ + */ + int32_t column_index(flecs::id_t id) const { + return ecs_table_get_column_index(world_, table_, id); + } + + /** Find column index for type. + * + * @tparam T The type. + * @return True if the table has the type, false if not. + */ + template + int32_t column_index() const { + return column_index(_::type::id(world_)); + } + + /** Find column index for pair. + * @param first First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + int32_t column_index(flecs::entity_t first, flecs::entity_t second) const { + return column_index(ecs_pair(first, second)); + } + + /** Find column index for pair. + * @tparam First First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + int32_t column_index(flecs::entity_t second) const { + return column_index(_::type::id(world_), second); + } + + /** Find column index for pair. + * @tparam First First element of pair. + * @tparam Second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + int32_t column_index() const { + return column_index(_::type::id(world_)); + } + + /** Test if table has (component) id. + * + * @param id The (component) id. + * @return True if the table has the id, false if not. + */ + bool has(flecs::id_t id) const { + return type_index(id) != -1; + } + + /** Test if table has the type. + * + * @tparam T The type. + * @return True if the table has the type, false if not. + */ + template + bool has() const { + return type_index() != -1; + } + + /** Test if table has the pair. + * + * @param first First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + bool has(flecs::entity_t first, flecs::entity_t second) const { + return type_index(first, second) != -1; + } + + /** Test if table has the pair. + * + * @tparam First First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + bool has(flecs::entity_t second) const { + return type_index(second) != -1; + } + + /** Test if table has the pair. + * + * @tparam First First element of pair. + * @tparam Second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + bool has() const { + return type_index() != -1; + } + + /** Get pointer to component array by column index. + * + * @param index The column index. + * @return Pointer to the column, NULL if not a component. + */ + virtual void* get_column(int32_t index) const { + return ecs_table_get_column(table_, index, 0); + } + + /** Get pointer to component array by component. + * + * @param id The component id. + * @return Pointer to the column, NULL if not found. + */ + void* get(flecs::id_t id) const { + int32_t index = column_index(id); + if (index == -1) { + return NULL; + } + return get_column(index); + } + + /** Get pointer to component array by pair. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + * @return Pointer to the column, NULL if not found. + */ + void* get(flecs::entity_t first, flecs::entity_t second) const { + return get(ecs_pair(first, second)); + } + + /** Get pointer to component array by component. + * + * @tparam T The component. + * @return Pointer to the column, NULL if not found. + */ + template ::value > = 0> + T* get() const { + return static_cast(get(_::type::id(world_))); + } + + /** Get pointer to component array by (enum) component. + * + * @tparam T The (enum) component. + * @return Pointer to the column, NULL if not found. + */ + template ::value > = 0> + T* get() const { + return static_cast(get(_::type::id(world_))); + } + + /** Get pointer to component array by component. + * + * @tparam T The component. + * @return Pointer to the column, NULL if not found. + */ + template , + if_t< flecs::is_pair::value > = 0> + A* get() const { + return static_cast(get(_::type::id(world_))); + } + + /** Get pointer to component array by pair. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + * @return Pointer to the column, NULL if not found. + */ + template + First* get(flecs::entity_t second) const { + return static_cast(get(_::type::id(world_), second)); + } + + /** Get pointer to component array by pair. + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + * @return Pointer to the column, NULL if not found. + */ + template , + typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> + A* get() const { + return static_cast(get(_::type::id(world_))); + } + + /** Get column size */ + size_t column_size(int32_t index) { + return ecs_table_get_column_size(table_, index); + } + + /** Get depth for given relationship. + * + * @param rel The relationship. + * @return The depth. + */ + int32_t depth(flecs::entity_t rel) { + return ecs_table_get_depth(world_, table_, rel); + } + + /** Get depth for given relationship. + * + * @tparam Rel The relationship. + * @return The depth. + */ + template + int32_t depth() { + return depth(_::type::id(world_)); + } + + /** Get table. + * + * @return The table. + */ + table_t* get_table() const { + return table_; + } + + /* Implicit conversion to table_t */ + operator table_t*() const { + return table_; + } + +protected: + world_t *world_; + table_t *table_; +}; + +struct table_range : table { + table_range() + : table() + , offset_(0) + , count_(0) { } + + table_range(world_t *world, table_t *t, int32_t offset, int32_t count) + : table(world, t) + , offset_(offset) + , count_(count) { } + + int32_t offset() const { + return offset_; + } + + int32_t count() const { + return count_; + } + + /** Get pointer to component array by column index. + * + * @param index The column index. + * @return Pointer to the column, NULL if not a component. + */ + void* get_column(int32_t index) const override { + return ecs_table_get_column(table_, index, offset_); + } + +private: + int32_t offset_ = 0; + int32_t count_ = 0; +}; + +/** @} */ + +} + +/** + * @file addons/cpp/utils/iterable.hpp + * @brief Base class for iterable objects, like queries. + */ + +namespace flecs { + +template +struct iter_iterable; + +template +struct page_iterable; + +template +struct worker_iterable; + +template +struct iterable { + + /** Each iterator. + * The "each" iterator accepts a function that is invoked for each matching + * entity. The following function signatures are valid: + * - func(flecs::entity e, Components& ...) + * - func(flecs::iter& it, size_t index, Components& ....) + * - func(Components& ...) + */ + template + void each(Func&& func) const { + ecs_iter_t it = this->get_iter(nullptr); + ecs_iter_next_action_t next = this->next_action(); + while (next(&it)) { + _::each_delegate(func).invoke(&it); + } + } + + /** Run iterator. + * The "each" iterator accepts a function that is invoked once for a query + * with a valid iterator. The following signature is valid: + * - func(flecs::iter&) + */ + template + void run(Func&& func) const { + ecs_iter_t it = this->get_iter(nullptr); + _::run_delegate(func).invoke(&it); + } + + template + flecs::entity find(Func&& func) const { + ecs_iter_t it = this->get_iter(nullptr); + ecs_iter_next_action_t next = this->next_action(); + + flecs::entity result; + while (!result && next(&it)) { + result = _::find_delegate(func).invoke(&it); + } + + if (result) { + ecs_iter_fini(&it); + } + + return result; + } + + /** Create iterator. + * Create an iterator object that can be modified before iterating. + */ + iter_iterable iter(flecs::world_t *world = nullptr) const; + + /** Create iterator. + * Create an iterator object that can be modified before iterating. + */ + iter_iterable iter(flecs::iter& iter) const; + + /** Create iterator. + * Create an iterator object that can be modified before iterating. + */ + iter_iterable iter(flecs::entity e) const; + + /** Page iterator. + * Create an iterator that limits the returned entities with offset/limit. + * + * @param offset How many entities to skip. + * @param limit The maximum number of entities to return. + * @return Iterable that can be iterated with each/iter. + */ + page_iterable page(int32_t offset, int32_t limit); + + /** Worker iterator. + * Create an iterator that divides the number of matched entities across + * a number of resources. + * + * @param index The index of the current resource. + * @param count The total number of resources to divide entities between. + * @return Iterable that can be iterated with each/iter. + */ + worker_iterable worker(int32_t index, int32_t count); + + /** Return number of entities matched by iterable. */ + int32_t count() const { + return this->iter().count(); + } + + /** Return whether iterable has any matches. */ + bool is_true() const { + return this->iter().is_true(); + } + + /** Return first entity matched by iterable. */ + flecs::entity first() const { + return this->iter().first(); + } + + iter_iterable set_var(int var_id, flecs::entity_t value) { + return this->iter().set_var(var_id, value); + } + + iter_iterable set_var(const char *name, flecs::entity_t value) { + return this->iter().set_var(name, value); + } + + iter_iterable set_var(const char *name, flecs::table_t *value) { + return this->iter().set_var(name, value); + } + + iter_iterable set_var(const char *name, ecs_table_range_t value) { + return this->iter().set_var(name, value); + } + + iter_iterable set_var(const char *name, flecs::table_range value) { + return this->iter().set_var(name, value); + } + + // Limit results to tables with specified group id (grouped queries only) + iter_iterable set_group(uint64_t group_id) { + return this->iter().set_group(group_id); + } + + // Limit results to tables with specified group id (grouped queries only) + template + iter_iterable set_group() { + return this->iter().template set_group(); + } + + virtual ~iterable() { } +protected: + friend iter_iterable; + friend page_iterable; + friend worker_iterable; + + virtual ecs_iter_t get_iter(flecs::world_t *stage) const = 0; + virtual ecs_iter_next_action_t next_action() const = 0; +}; + +template +struct iter_iterable final : iterable { + template + iter_iterable(Iterable *it, flecs::world_t *world) + { + it_ = it->get_iter(world); + next_ = it->next_action(); + next_each_ = it->next_action(); + ecs_assert(next_ != nullptr, ECS_INTERNAL_ERROR, NULL); + ecs_assert(next_each_ != nullptr, ECS_INTERNAL_ERROR, NULL); + } + + iter_iterable& set_var(int var_id, flecs::entity_t value) { + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, 0); + ecs_iter_set_var(&it_, var_id, value); + return *this; + } + + iter_iterable& set_var(const char *name, flecs::entity_t value) { + ecs_query_iter_t *qit = &it_.priv_.iter.query; + int var_id = ecs_query_find_var(qit->query, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + ecs_iter_set_var(&it_, var_id, value); + return *this; + } + + iter_iterable& set_var(const char *name, flecs::table_t *value) { + ecs_query_iter_t *qit = &it_.priv_.iter.query; + int var_id = ecs_query_find_var(qit->query, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + ecs_iter_set_var_as_table(&it_, var_id, value); + return *this; + } + + iter_iterable& set_var(const char *name, ecs_table_range_t value) { + ecs_query_iter_t *qit = &it_.priv_.iter.query; + int var_id = ecs_query_find_var(qit->query, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + ecs_iter_set_var_as_range(&it_, var_id, &value); + return *this; + } + + iter_iterable& set_var(const char *name, flecs::table_range value) { + ecs_table_range_t range; + range.table = value.get_table(); + range.offset = value.offset(); + range.count = value.count(); + return set_var(name, range); + } + +# ifdef FLECS_JSON +/** + * @file addons/cpp/mixins/json/iterable.inl + * @brief JSON iterable mixin. + */ + +/** Serialize iterator result to JSON. + * + * @memberof flecs::iter + * @ingroup cpp_addons_json + */ +flecs::string to_json(flecs::iter_to_json_desc_t *desc = nullptr) { + char *json = ecs_iter_to_json(&it_, desc); + return flecs::string(json); +} + +# endif + + // Return total number of entities in result. + int32_t count() { + int32_t result = 0; + while (next_each_(&it_)) { + result += it_.count; + } + return result; + } + + // Returns true if iterator yields at least once result. + bool is_true() { + bool result = next_each_(&it_); + if (result) { + ecs_iter_fini(&it_); + } + return result; + } + + // Return first matching entity. + flecs::entity first() { + flecs::entity result; + if (next_each_(&it_) && it_.count) { + result = flecs::entity(it_.world, it_.entities[0]); + ecs_iter_fini(&it_); + } + return result; + } + + // Limit results to tables with specified group id (grouped queries only) + iter_iterable& set_group(uint64_t group_id) { + ecs_iter_set_group(&it_, group_id); + return *this; + } + + // Limit results to tables with specified group id (grouped queries only) + template + iter_iterable& set_group() { + ecs_iter_set_group(&it_, _::type().id(it_.real_world)); + return *this; + } + +protected: + ecs_iter_t get_iter(flecs::world_t *world) const override { + if (world) { + ecs_iter_t result = it_; + result.world = world; + return result; + } + return it_; + } + + ecs_iter_next_action_t next_action() const override { + return next_; + } + +private: + ecs_iter_t it_; + ecs_iter_next_action_t next_; + ecs_iter_next_action_t next_each_; +}; + +template +iter_iterable iterable::iter(flecs::world_t *world) const +{ + return iter_iterable(this, world); +} + +template +iter_iterable iterable::iter(flecs::iter& it) const +{ + return iter_iterable(this, it.world()); +} + +template +iter_iterable iterable::iter(flecs::entity e) const +{ + return iter_iterable(this, e.world()); +} + +template +struct page_iterable final : iterable { + template + page_iterable(int32_t offset, int32_t limit, Iterable *it) + : offset_(offset) + , limit_(limit) + { + chain_it_ = it->get_iter(nullptr); + } + +protected: + ecs_iter_t get_iter(flecs::world_t*) const { + return ecs_page_iter(&chain_it_, offset_, limit_); + } + + ecs_iter_next_action_t next_action() const { + return ecs_page_next; + } + +private: + ecs_iter_t chain_it_; + int32_t offset_; + int32_t limit_; +}; + +template +page_iterable iterable::page( + int32_t offset, + int32_t limit) +{ + return page_iterable(offset, limit, this); +} + +template +struct worker_iterable final : iterable { + worker_iterable(int32_t offset, int32_t limit, iterable *it) + : offset_(offset) + , limit_(limit) + { + chain_it_ = it->get_iter(nullptr); + } + +protected: + ecs_iter_t get_iter(flecs::world_t*) const { + return ecs_worker_iter(&chain_it_, offset_, limit_); + } + + ecs_iter_next_action_t next_action() const { + return ecs_worker_next; + } + +private: + ecs_iter_t chain_it_; + int32_t offset_; + int32_t limit_; +}; + +template +worker_iterable iterable::worker( + int32_t index, + int32_t count) +{ + return worker_iterable(index, count, this); +} + +} + + +// Mixin implementations +/** + * @file addons/cpp/mixins/id/impl.hpp + * @brief Id class implementation. + */ + +#pragma once + +namespace flecs { + +inline flecs::entity id::entity() const { + ecs_assert(!is_pair(), ECS_INVALID_OPERATION, NULL); + ecs_assert(!flags(), ECS_INVALID_OPERATION, NULL); + return flecs::entity(world_, id_); +} + +inline flecs::entity id::flags() const { + return flecs::entity(world_, id_ & ECS_ID_FLAGS_MASK); +} + +inline flecs::entity id::first() const { + ecs_assert(is_pair(), ECS_INVALID_OPERATION, NULL); + + flecs::entity_t e = ECS_PAIR_FIRST(id_); + if (world_) { + return flecs::entity(world_, ecs_get_alive(world_, e)); + } else { + return flecs::entity(e); + } +} + +inline flecs::entity id::second() const { + flecs::entity_t e = ECS_PAIR_SECOND(id_); + if (world_) { + return flecs::entity(world_, ecs_get_alive(world_, e)); + } else { + return flecs::entity(e); + } +} + +inline flecs::entity id::add_flags(flecs::id_t flags) const { + return flecs::entity(world_, id_ | flags); +} + +inline flecs::entity id::remove_flags(flecs::id_t flags) const { + (void)flags; + ecs_assert((id_ & ECS_ID_FLAGS_MASK) == flags, ECS_INVALID_PARAMETER, NULL); + return flecs::entity(world_, id_ & ECS_COMPONENT_MASK); +} + +inline flecs::entity id::remove_flags() const { + return flecs::entity(world_, id_ & ECS_COMPONENT_MASK); +} + +inline flecs::entity id::remove_generation() const { + return flecs::entity(world_, static_cast(id_)); +} + +inline flecs::world id::world() const { + return flecs::world(world_); +} + +inline flecs::entity id::type_id() const { + return flecs::entity(world_, ecs_get_typeid(world_, id_)); +} + + +// Id mixin implementation + +template +inline flecs::id world::id() const { + return flecs::id(world_, _::type::id(world_)); +} + +template +inline flecs::id world::id(Args&&... args) const { + return flecs::id(world_, FLECS_FWD(args)...); +} + +template +inline flecs::id world::pair() const { + return flecs::id( + world_, + ecs_pair( + _::type::id(world_), + _::type::id(world_))); +} + +template +inline flecs::id world::pair(entity_t o) const { + ecs_assert(!ECS_IS_PAIR(o), ECS_INVALID_PARAMETER, + "cannot create nested pairs"); + + return flecs::id( + world_, + ecs_pair( + _::type::id(world_), + o)); +} + +inline flecs::id world::pair(entity_t r, entity_t o) const { + ecs_assert(!ECS_IS_PAIR(r) && !ECS_IS_PAIR(o), ECS_INVALID_PARAMETER, + "cannot create nested pairs"); + + return flecs::id( + world_, + ecs_pair(r, o)); +} + +} + +/** + * @file addons/cpp/mixins/entity/impl.hpp + * @brief Entity implementation. + */ + +#pragma once + +namespace flecs { + +template +flecs::entity ref::entity() const { + return flecs::entity(world_, ref_.entity); +} + +template +template +inline const Self& entity_builder::insert(const Func& func) const { + _::entity_with_delegate::invoke_ensure( + this->world_, this->id_, func); + return to_base(); +} + +template ::value > > +const T* entity_view::get() const { + entity_t r = _::type::id(world_); + entity_t c = ecs_get_target(world_, id_, r, 0); + + if (c) { + // Get constant value from constant entity + const T* v = static_cast(ecs_get_id(world_, c, r)); + ecs_assert(v != NULL, ECS_INTERNAL_ERROR, + "missing enum constant value"); + return v; + } else { + // If there is no matching pair for (r, *), try just r + return static_cast(ecs_get_id(world_, id_, r)); + } +} + +template +inline flecs::entity entity_view::target(int32_t index) const +{ + return flecs::entity(world_, + ecs_get_target(world_, id_, _::type::id(world_), index)); +} + +inline flecs::entity entity_view::target( + flecs::entity_t relationship, + int32_t index) const +{ + return flecs::entity(world_, + ecs_get_target(world_, id_, relationship, index)); +} + +inline flecs::entity entity_view::target_for( + flecs::entity_t relationship, + flecs::id_t id) const +{ + return flecs::entity(world_, + ecs_get_target_for_id(world_, id_, relationship, id)); +} + +template +inline flecs::entity entity_view::target_for(flecs::entity_t relationship) const { + return target_for(relationship, _::type::id(world_)); +} + +template +inline flecs::entity entity_view::target_for(flecs::entity_t relationship) const { + return target_for(relationship, _::type::id(world_)); +} + +inline flecs::entity entity_view::parent() const { + return target(flecs::ChildOf); +} + +inline flecs::entity entity_view::mut(const flecs::world& stage) const { + ecs_assert(!stage.is_readonly(), ECS_INVALID_PARAMETER, + "cannot use readonly world/stage to create mutable handle"); + return flecs::entity(id_).set_stage(stage.c_ptr()); +} + +inline flecs::entity entity_view::mut(const flecs::iter& it) const { + ecs_assert(!it.world().is_readonly(), ECS_INVALID_PARAMETER, + "cannot use iterator created for readonly world/stage to create mutable handle"); + return flecs::entity(id_).set_stage(it.world().c_ptr()); +} + +inline flecs::entity entity_view::mut(const flecs::entity_view& e) const { + ecs_assert(!e.world().is_readonly(), ECS_INVALID_PARAMETER, + "cannot use entity created for readonly world/stage to create mutable handle"); + return flecs::entity(id_).set_stage(e.world_); +} + +inline flecs::entity entity_view::set_stage(world_t *stage) { + return flecs::entity(stage, id_); +} + +inline flecs::type entity_view::type() const { + return flecs::type(world_, ecs_get_type(world_, id_)); +} + +inline flecs::table entity_view::table() const { + return flecs::table(world_, ecs_get_table(world_, id_)); +} + +inline flecs::table_range entity_view::range() const { + ecs_record_t *r = ecs_record_find(world_, id_); + if (r) { + return flecs::table_range(world_, r->table, + ECS_RECORD_TO_ROW(r->row), 1); + } + return flecs::table_range(); +} + +template +inline void entity_view::each(const Func& func) const { + const ecs_type_t *type = ecs_get_type(world_, id_); + if (!type) { + return; + } + + const ecs_id_t *ids = type->array; + int32_t count = type->count; + + for (int i = 0; i < count; i ++) { + ecs_id_t id = ids[i]; + flecs::id ent(world_, id); + func(ent); + } +} + +template +inline void entity_view::each(flecs::id_t pred, flecs::id_t obj, const Func& func) const { + flecs::world_t *real_world = const_cast( + ecs_get_world(world_)); + + const ecs_table_t *table = ecs_get_table(world_, id_); + if (!table) { + return; + } + + const ecs_type_t *type = ecs_table_get_type(table); + if (!type) { + return; + } + + flecs::id_t pattern = pred; + if (obj) { + pattern = ecs_pair(pred, obj); + } + + int32_t cur = 0; + id_t *ids = type->array; + + while (-1 != (cur = ecs_search_offset(real_world, table, cur, pattern, 0))) + { + flecs::id ent(world_, ids[cur]); + func(ent); + cur ++; + } +} + +template +inline void entity_view::each(const flecs::entity_view& rel, const Func& func) const { + return this->each(rel, flecs::Wildcard, [&](flecs::id id) { + flecs::entity obj = id.second(); + func(obj); + }); +} + +template ::value > > +inline bool entity_view::get(const Func& func) const { + return _::entity_with_delegate::invoke_get(world_, id_, func); +} + +inline flecs::entity entity_view::lookup(const char *path, bool search_path) const { + ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, "invalid lookup from null handle"); + auto id = ecs_lookup_path_w_sep(world_, id_, path, "::", "::", search_path); + return flecs::entity(world_, id); +} + +inline flecs::entity entity_view::clone(bool copy_value, flecs::entity_t dst_id) const { + if (!dst_id) { + dst_id = ecs_new(world_); + } + + flecs::entity dst = flecs::entity(world_, dst_id); + ecs_clone(world_, dst_id, id_, copy_value); + return dst; +} + +// Entity mixin implementation +template +inline flecs::entity world::entity(Args &&... args) const { + return flecs::entity(world_, FLECS_FWD(args)...); +} + +template ::value >> +inline flecs::id world::id(E value) const { + flecs::entity_t constant = enum_type(world_).entity(value); + return flecs::id(world_, constant); +} + +template ::value >> +inline flecs::entity world::entity(E value) const { + flecs::entity_t constant = enum_type(world_).entity(value); + return flecs::entity(world_, constant); +} + +template +inline flecs::entity world::entity(const char *name) const { + return flecs::entity(world_, + _::type::id_explicit(world_, name, true, 0, false) ); +} + +template +inline flecs::entity world::prefab(Args &&... args) const { + flecs::entity result = flecs::entity(world_, FLECS_FWD(args)...); + result.add(flecs::Prefab); + return result; +} + +template +inline flecs::entity world::prefab(const char *name) const { + flecs::entity result = flecs::component(world_, name, true); + result.add(flecs::Prefab); + return result; +} + +} + +/** + * @file addons/cpp/mixins/component/impl.hpp + * @brief Component mixin implementation + */ + +#pragma once + +namespace flecs { + +template +inline flecs::component world::component(Args &&... args) const { + return flecs::component(world_, FLECS_FWD(args)...); +} + +template +inline flecs::untyped_component world::component(Args &&... args) const { + return flecs::untyped_component(world_, FLECS_FWD(args)...); +} + +} // namespace flecs + +/** + * @file addons/cpp/mixins/term/impl.hpp + * @brief Term implementation. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/term/builder_i.hpp + * @brief Term builder interface. + */ + +#pragma once + +/** + * @file addons/cpp/utils/signature.hpp + * @brief Compile time utilities for deriving query attributes from param pack. + */ + +#pragma once + +#include + +namespace flecs { +namespace _ { + + template ::value > = 0> + constexpr flecs::inout_kind_t type_to_inout() { + return flecs::In; + } + + template ::value > = 0> + constexpr flecs::inout_kind_t type_to_inout() { + return flecs::InOut; + } + + template ::value || is_reference::value > = 0> + constexpr flecs::inout_kind_t type_to_inout() { + return flecs::InOutDefault; + } + + template ::value > = 0> + constexpr flecs::oper_kind_t type_to_oper() { + return flecs::Optional; + } + + template ::value > = 0> + constexpr flecs::oper_kind_t type_to_oper() { + return flecs::And; + } + + template + struct sig { + sig(flecs::world_t *world) + : world_(world) + , ids({ (_::type>::id(world))... }) + , inout ({ (type_to_inout())... }) + , oper ({ (type_to_oper())... }) + { } + + flecs::world_t *world_; + flecs::array ids; + flecs::array inout; + flecs::array oper; + + template + void populate(const Builder& b) { + size_t i = 0; + for (auto id : ids) { + if (!(id & ECS_ID_FLAGS_MASK)) { + const flecs::type_info_t *ti = ecs_get_type_info(world_, id); + if (ti) { + // Union relationships always return a value of type + // flecs::entity_t which holds the target id of the + // union relationship. + // If a union component with a non-zero size (like an + // enum) is added to the query signature, the each/iter + // functions would accept a parameter of the component + // type instead of flecs::entity_t, which would cause + // an assert. + ecs_assert( + !ti->size || !ecs_has_id(world_, id, flecs::Union), + ECS_INVALID_PARAMETER, + "use with() method to add union relationship"); + } + } + + b->with(id).inout(inout[i]).oper(oper[i]); + i ++; + } + } + }; + +} // namespace _ +} // namespace flecs + + +namespace flecs +{ + +/** Term identifier builder. + * A term identifier describes a single identifier in a term. Identifier + * descriptions can reference entities by id, name or by variable, which means + * the entity will be resolved when the term is evaluated. + * + * @ingroup cpp_core_queries + */ +template +struct term_ref_builder_i { + term_ref_builder_i() : term_ref_(nullptr) { } + + virtual ~term_ref_builder_i() { } + + /* The self flag indicates the term identifier itself is used */ + Base& self() { + this->assert_term_ref(); + term_ref_->id |= flecs::Self; + return *this; + } + + /* Specify value of identifier by id */ + Base& id(flecs::entity_t id) { + this->assert_term_ref(); + term_ref_->id = id; + return *this; + } + + /* Specify value of identifier by id. Almost the same as id(entity), but this + * operation explicitly sets the flecs::IsEntity flag. This forces the id to + * be interpreted as entity, whereas not setting the flag would implicitly + * convert ids for builtin variables such as flecs::This to a variable. + * + * This function can also be used to disambiguate id(0), which would match + * both id(entity_t) and id(const char*). + */ + Base& entity(flecs::entity_t entity) { + this->assert_term_ref(); + term_ref_->id = entity | flecs::IsEntity; + return *this; + } + + /* Specify value of identifier by name */ + Base& name(const char *name) { + this->assert_term_ref(); + term_ref_->id |= flecs::IsEntity; + term_ref_->name = const_cast(name); + return *this; + } + + /* Specify identifier is a variable (resolved at query evaluation time) */ + Base& var(const char *var_name) { + this->assert_term_ref(); + term_ref_->id |= flecs::IsVariable; + term_ref_->name = const_cast(var_name); + return *this; + } + + /* Override term id flags */ + Base& flags(flecs::flags32_t flags) { + this->assert_term_ref(); + term_ref_->id = flags; + return *this; + } + + ecs_term_ref_t *term_ref_; + +protected: + virtual flecs::world_t* world_v() = 0; + + void assert_term_ref() { + ecs_assert(term_ref_ != NULL, ECS_INVALID_PARAMETER, + "no active term (call .with() first)"); + } + +private: + operator Base&() { + return *static_cast(this); + } +}; + +/** Term builder interface. + * A term is a single element of a query expression. + * + * @ingroup cpp_core_queries + */ +template +struct term_builder_i : term_ref_builder_i { + term_builder_i() : term_(nullptr) { } + + term_builder_i(ecs_term_t *term_ptr) { + set_term(term_ptr); + } + + Base& term(id_t id) { + return this->id(id); + } + + /* Call prior to setting values for src identifier */ + Base& src() { + this->assert_term(); + this->term_ref_ = &term_->src; + return *this; + } + + /* Call prior to setting values for first identifier. This is either the + * component identifier, or first element of a pair (in case second is + * populated as well). */ + Base& first() { + this->assert_term(); + this->term_ref_ = &term_->first; + return *this; + } + + /* Call prior to setting values for second identifier. This is the second + * element of a pair. Requires that first() is populated as well. */ + Base& second() { + this->assert_term(); + this->term_ref_ = &term_->second; + return *this; + } + + /* Select src identifier, initialize it with entity id */ + Base& src(flecs::entity_t id) { + this->src(); + this->id(id); + return *this; + } + + /* Select src identifier, initialize it with id associated with type */ + template + Base& src() { + this->src(_::type::id(this->world_v())); + return *this; + } + + /* Select src identifier, initialize it with name. If name starts with a $ + * the name is interpreted as a variable. */ + Base& src(const char *name) { + ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL); + this->src(); + if (name[0] == '$') { + this->var(&name[1]); + } else { + this->name(name); + } + return *this; + } + + /* Select first identifier, initialize it with entity id */ + Base& first(flecs::entity_t id) { + this->first(); + this->id(id); + return *this; + } + + /* Select first identifier, initialize it with id associated with type */ + template + Base& first() { + this->first(_::type::id(this->world_v())); + return *this; + } + + /* Select first identifier, initialize it with name. If name starts with a $ + * the name is interpreted as a variable. */ + Base& first(const char *name) { + ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL); + this->first(); + if (name[0] == '$') { + this->var(&name[1]); + } else { + this->name(name); + } + return *this; + } + + /* Select second identifier, initialize it with entity id */ + Base& second(flecs::entity_t id) { + this->second(); + this->id(id); + return *this; + } + + /* Select second identifier, initialize it with id associated with type */ + template + Base& second() { + this->second(_::type::id(this->world_v())); + return *this; + } + + /* Select second identifier, initialize it with name. If name starts with a $ + * the name is interpreted as a variable. */ + Base& second(const char *name) { + ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL); + this->second(); + if (name[0] == '$') { + this->var(&name[1]); + } else { + this->name(name); + } + return *this; + } + + /* The up flag indicates that the term identifier may be substituted by + * traversing a relationship upwards. For example: substitute the identifier + * with its parent by traversing the ChildOf relationship. */ + Base& up(flecs::entity_t trav = 0) { + this->assert_term_ref(); + ecs_check(this->term_ref_ != &term_->first, ECS_INVALID_PARAMETER, + "up traversal can only be applied to term source"); + ecs_check(this->term_ref_ != &term_->second, ECS_INVALID_PARAMETER, + "up traversal can only be applied to term source"); + this->term_ref_->id |= flecs::Up; + if (trav) { + term_->trav = trav; + } + error: + return *this; + } + + template + Base& up() { + return this->up(_::type::id(this->world_v())); + } + + /* The cascade flag is like up, but returns results in breadth-first order. + * Only supported for flecs::query */ + Base& cascade(flecs::entity_t trav = 0) { + this->assert_term_ref(); + this->up(); + this->term_ref_->id |= flecs::Cascade; + if (trav) { + term_->trav = trav; + } + return *this; + } + + template + Base& cascade() { + return this->cascade(_::type::id(this->world_v())); + } + + /* Use with cascade to iterate results in descending (bottom -> top) order */ + Base& desc() { + this->assert_term_ref(); + this->term_ref_->id |= flecs::Desc; + return *this; + } + + /* Same as up(), exists for backwards compatibility */ + Base& parent() { + return this->up(); + } + + /* Specify relationship to traverse, and flags to indicate direction */ + Base& trav(flecs::entity_t trav, flecs::flags32_t flags = 0) { + this->assert_term_ref(); + term_->trav = trav; + this->term_ref_->id |= flags; + return *this; + } + + /** Set id flags for term. */ + Base& id_flags(id_t flags) { + this->assert_term(); + term_->id |= flags; + return *this; + } + + /** Set read/write access of term. */ + Base& inout(flecs::inout_kind_t inout) { + this->assert_term(); + term_->inout = static_cast(inout); + return *this; + } + + /** Set read/write access for stage. Use this when a system reads or writes + * components other than the ones provided by the query. This information + * can be used by schedulers to insert sync/merge points between systems + * where deferred operations are flushed. + * + * Setting this is optional. If not set, the value of the accessed component + * may be out of sync for at most one frame. + */ + Base& inout_stage(flecs::inout_kind_t inout) { + this->assert_term(); + term_->inout = static_cast(inout); + if (term_->oper != EcsNot) { + this->src().entity(0); + } + return *this; + } + + /** Short for inout_stage(flecs::Out). + * Use when system uses add, remove or set. + */ + Base& write() { + return this->inout_stage(flecs::Out); + } + + /** Short for inout_stage(flecs::In). + * Use when system uses get. + */ + Base& read() { + return this->inout_stage(flecs::In); + } + + /** Short for inout_stage(flecs::InOut). + * Use when system uses ensure. + */ + Base& read_write() { + return this->inout_stage(flecs::InOut); + } + + /** Short for inout(flecs::In) */ + Base& in() { + return this->inout(flecs::In); + } + + /** Short for inout(flecs::Out) */ + Base& out() { + return this->inout(flecs::Out); + } + + /** Short for inout(flecs::InOut) */ + Base& inout() { + return this->inout(flecs::InOut); + } + + /** Short for inout(flecs::In) */ + Base& inout_none() { + return this->inout(flecs::InOutNone); + } + + /** Set operator of term. */ + Base& oper(flecs::oper_kind_t oper) { + this->assert_term(); + term_->oper = static_cast(oper); + return *this; + } + + /* Short for oper(flecs::And) */ + Base& and_() { + return this->oper(flecs::And); + } + + /* Short for oper(flecs::Or) */ + Base& or_() { + return this->oper(flecs::Or); + } + + /* Short for oper(flecs::Or) */ + Base& not_() { + return this->oper(flecs::Not); + } + + /* Short for oper(flecs::Or) */ + Base& optional() { + return this->oper(flecs::Optional); + } + + /* Short for oper(flecs::AndFrom) */ + Base& and_from() { + return this->oper(flecs::AndFrom); + } + + /* Short for oper(flecs::OrFrom) */ + Base& or_from() { + return this->oper(flecs::OrFrom); + } + + /* Short for oper(flecs::NotFrom) */ + Base& not_from() { + return this->oper(flecs::NotFrom); + } + + /** Match singleton. */ + Base& singleton() { + this->assert_term(); + ecs_assert(term_->id || term_->first.id, ECS_INVALID_PARAMETER, + "no component specified for singleton"); + + flecs::id_t sid = term_->id; + if (!sid) { + sid = term_->first.id; + } + + ecs_assert(sid != 0, ECS_INVALID_PARAMETER, NULL); + + if (!ECS_IS_PAIR(sid)) { + term_->src.id = sid; + } else { + term_->src.id = ecs_pair_first(world(), sid); + } + return *this; + } + + /* Query terms are not triggered on by observers */ + Base& filter() { + term_->inout = EcsInOutFilter; + return *this; + } + + ecs_term_t *term_; + +protected: + virtual flecs::world_t* world_v() override = 0; + + void set_term(ecs_term_t *term) { + term_ = term; + if (term) { + this->term_ref_ = &term_->src; // default to subject + } else { + this->term_ref_ = nullptr; + } + } + +private: + void assert_term() { + ecs_assert(term_ != NULL, ECS_INVALID_PARAMETER, + "no active term (call .with() first)"); + } + + operator Base&() { + return *static_cast(this); + } +}; + +} + + +namespace flecs { + +/** Class that describes a term. + * + * @ingroup cpp_core_queries + */ +struct term final : term_builder_i { + term() + : term_builder_i(&value) + , value({}) + , world_(nullptr) { } + + term(flecs::world_t *world_ptr) + : term_builder_i(&value) + , value({}) + , world_(world_ptr) { } + + term(flecs::world_t *world_ptr, ecs_term_t t) + : term_builder_i(&value) + , value({}) + , world_(world_ptr) { + value = t; + this->set_term(&value); + } + + term(flecs::world_t *world_ptr, id_t id) + : term_builder_i(&value) + , value({}) + , world_(world_ptr) { + if (id & ECS_ID_FLAGS_MASK) { + value.id = id; + } else { + value.first.id = id; + } + this->set_term(&value); + } + + term(flecs::world_t *world_ptr, entity_t r, entity_t o) + : term_builder_i(&value) + , value({}) + , world_(world_ptr) { + value.id = ecs_pair(r, o); + this->set_term(&value); + } + + term(id_t id) + : term_builder_i(&value) + , value({}) + , world_(nullptr) { + if (id & ECS_ID_FLAGS_MASK) { + value.id = id; + } else { + value.first.id = id; + } + } + + term(id_t r, id_t o) + : term_builder_i(&value) + , value({}) + , world_(nullptr) { + value.id = ecs_pair(r, o); + } + + void reset() { + value = {}; + this->set_term(nullptr); + } + + bool is_set() { + return ecs_term_is_initialized(&value); + } + + flecs::id id() { + return flecs::id(world_, value.id); + } + + flecs::inout_kind_t inout() { + return static_cast(value.inout); + } + + flecs::oper_kind_t oper() { + return static_cast(value.oper); + } + + flecs::entity get_src() { + return flecs::entity(world_, ECS_TERM_REF_ID(&value.src)); + } + + flecs::entity get_first() { + return flecs::entity(world_, ECS_TERM_REF_ID(&value.first)); + } + + flecs::entity get_second() { + return flecs::entity(world_, ECS_TERM_REF_ID(&value.second)); + } + + operator flecs::term_t() const { + return value; + } + + flecs::term_t value; + +protected: + flecs::world_t* world_v() override { return world_; } + +private: + flecs::world_t *world_; +}; + +// Term mixin implementation +template +inline flecs::term world::term(Args &&... args) const { + return flecs::term(world_, FLECS_FWD(args)...); +} + +template +inline flecs::term world::term() const { + return flecs::term(world_, _::type::id(world_)); +} + +template +inline flecs::term world::term() const { + return flecs::term(world_, ecs_pair( + _::type::id(world_), + _::type::id(world_))); +} + +} + +/** + * @file addons/cpp/mixins/query/impl.hpp + * @brief Query implementation. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/query/builder.hpp + * @brief Query builder. + */ + +#pragma once + +/** + * @file addons/cpp/utils/builder.hpp + * @brief Builder base class. + * + * Generic functionality for builder classes. + */ + +#pragma once + +namespace flecs { +namespace _ { + +// Macros for template types so we don't go cross-eyed +#define FLECS_TBUILDER template class +#define FLECS_IBUILDER template class + +template +struct builder : IBuilder +{ + using IBase = IBuilder; + +public: + builder(flecs::world_t *world) + : IBase(&desc_) + , desc_{} + , world_(world) { } + + builder(const builder& f) + : IBase(&desc_, f.term_index_) + { + world_ = f.world_; + desc_ = f.desc_; + } + + builder(builder&& f) noexcept + : builder(f) { } + + operator TDesc*() { + return &desc_; + } + + T build() { + return T(world_, *static_cast(this)); + } + +protected: + flecs::world_t* world_v() override { return world_; } + TDesc desc_; + flecs::world_t *world_; +}; + +#undef FLECS_TBUILDER +#undef FLECS_IBUILDER + +} // namespace _ +} // namespace flecs + +/** + * @file addons/cpp/mixins/query/builder_i.hpp + * @brief Query builder interface. + */ + +#pragma once + + +namespace flecs +{ + +/** Query builder interface. + * + * @ingroup cpp_core_queries + */ +template +struct query_builder_i : term_builder_i { + query_builder_i(ecs_query_desc_t *desc, int32_t term_index = 0) + : term_index_(term_index) + , expr_count_(0) + , desc_(desc) { } + + Base& query_flags(ecs_flags32_t flags) { + desc_->flags |= flags; + return *this; + } + + Base& cache_kind(query_cache_kind_t kind) { + desc_->cache_kind = static_cast(kind); + return *this; + } + + Base& cached() { + return cache_kind(flecs::QueryCacheAuto); + } + + Base& expr(const char *expr) { + ecs_check(expr_count_ == 0, ECS_INVALID_OPERATION, + "query_builder::expr() called more than once"); + desc_->expr = expr; + expr_count_ ++; + + error: + return *this; + } + + /* With methods */ + + template + Base& with() { + this->term(); + *this->term_ = flecs::term(_::type::id(this->world_v())); + this->term_->inout = static_cast( + _::type_to_inout()); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(id_t id) { + this->term(); + *this->term_ = flecs::term(id); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(const char *name) { + this->term(); + *this->term_ = flecs::term().first(name); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(const char *first, const char *second) { + this->term(); + *this->term_ = flecs::term().first(first).second(second); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(entity_t r, entity_t o) { + this->term(); + *this->term_ = flecs::term(r, o); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(entity_t r, const char *o) { + this->term(); + *this->term_ = flecs::term(r).second(o); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(const char *r, entity_t o) { + this->term(); + *this->term_ = flecs::term().first(r).second(o); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + template + Base& with(id_t o) { + return this->with(_::type::id(this->world_v()), o); + } + + template + Base& with(const char *second) { + return this->with(_::type::id(this->world_v())).second(second); + } + + template + Base& with() { + return this->with(_::type::id(this->world_v())); + } + + template ::value > = 0> + Base& with(E value) { + flecs::entity_t r = _::type::id(this->world_v()); + auto o = enum_type(this->world_v()).entity(value); + return this->with(r, o); + } + + Base& with(flecs::term& term) { + this->term(); + *this->term_ = term; + return *this; + } + + Base& with(flecs::term&& term) { + this->term(); + *this->term_ = term; + return *this; + } + + /* Without methods, shorthand for .with(...).not_(). */ + + template + Base& without(Args&&... args) { + return this->with(FLECS_FWD(args)...).not_(); + } + + template + Base& without(Args&&... args) { + return this->with(FLECS_FWD(args)...).not_(); + } + + template + Base& without() { + return this->with().not_(); + } + + /* Write/read methods */ + + Base& write() { + term_builder_i::write(); + return *this; + } + + template + Base& write(Args&&... args) { + return this->with(FLECS_FWD(args)...).write(); + } + + template + Base& write(Args&&... args) { + return this->with(FLECS_FWD(args)...).write(); + } + + template + Base& write() { + return this->with().write(); + } + + Base& read() { + term_builder_i::read(); + return *this; + } + + template + Base& read(Args&&... args) { + return this->with(FLECS_FWD(args)...).read(); + } + + template + Base& read(Args&&... args) { + return this->with(FLECS_FWD(args)...).read(); + } + + template + Base& read() { + return this->with().read(); + } + + /* Scope_open/scope_close shorthand notation. */ + Base& scope_open() { + return this->with(flecs::ScopeOpen).entity(0); + } + + Base& scope_close() { + return this->with(flecs::ScopeClose).entity(0); + } + + /* Term notation for more complex query features */ + + Base& term() { + if (this->term_) { + ecs_check(ecs_term_is_initialized(this->term_), + ECS_INVALID_OPERATION, + "query_builder::term() called without initializing term"); + } + + ecs_check(term_index_ < FLECS_TERM_COUNT_MAX, + ECS_INVALID_PARAMETER, "maximum number of terms exceeded"); + + this->set_term(&desc_->terms[term_index_]); + + term_index_ ++; + + error: + return *this; + } + + Base& term_at(int32_t term_index) { + ecs_assert(term_index >= 0, ECS_INVALID_PARAMETER, NULL); + int32_t prev_index = term_index_; + term_index_ = term_index; + this->term(); + term_index_ = prev_index; + ecs_assert(ecs_term_is_initialized(this->term_), + ECS_INVALID_PARAMETER, NULL); + return *this; + } + + /** Sort the output of a query. + * This enables sorting of entities across matched tables. As a result of this + * operation, the order of entities in the matched tables may be changed. + * Resorting happens when a query iterator is obtained, and only if the table + * data has changed. + * + * If multiple queries that match the same (down)set of tables specify different + * sorting functions, resorting is likely to happen every time an iterator is + * obtained, which can significantly slow down iterations. + * + * The sorting function will be applied to the specified component. Resorting + * only happens if that component has changed, or when the entity order in the + * table has changed. If no component is provided, resorting only happens when + * the entity order changes. + * + * @tparam T The component used to sort. + * @param compare The compare function used to sort the components. + */ + template + Base& order_by(int(*compare)(flecs::entity_t, const T*, flecs::entity_t, const T*)) { + ecs_order_by_action_t cmp = reinterpret_cast(compare); + return this->order_by(_::type::id(this->world_v()), cmp); + } + + /** Sort the output of a query. + * Same as order_by, but with component identifier. + * + * @param component The component used to sort. + * @param compare The compare function used to sort the components. + */ + Base& order_by(flecs::entity_t component, int(*compare)(flecs::entity_t, const void*, flecs::entity_t, const void*)) { + desc_->order_by_callback = reinterpret_cast(compare); + desc_->order_by = component; + return *this; + } + + /** Group and sort matched tables. + * Similar to ecs_query_order_by(), but instead of sorting individual entities, this + * operation only sorts matched tables. This can be useful of a query needs to + * enforce a certain iteration order upon the tables it is iterating, for + * example by giving a certain component or tag a higher priority. + * + * The sorting function assigns a "rank" to each type, which is then used to + * sort the tables. Tables with higher ranks will appear later in the iteration. + * + * Resorting happens when a query iterator is obtained, and only if the set of + * matched tables for a query has changed. If table sorting is enabled together + * with entity sorting, table sorting takes precedence, and entities will be + * sorted within each set of tables that are assigned the same rank. + * + * @tparam T The component used to determine the group rank. + * @param group_by_action Callback that determines group id for table. + */ + template + Base& group_by(uint64_t(*group_by_action)(flecs::world_t*, flecs::table_t *table, flecs::id_t id, void* ctx)) { + ecs_group_by_action_t action = reinterpret_cast(group_by_action); + return this->group_by(_::type::id(this->world_v()), action); + } + + /** Group and sort matched tables. + * Same as group_by, but with component identifier. + * + * @param component The component used to determine the group rank. + * @param group_by_action Callback that determines group id for table. + */ + Base& group_by(flecs::entity_t component, uint64_t(*group_by_action)(flecs::world_t*, flecs::table_t *table, flecs::id_t id, void* ctx)) { + desc_->group_by_callback = reinterpret_cast(group_by_action); + desc_->group_by = component; + return *this; + } + + /** Group and sort matched tables. + * Same as group_by, but with default group_by action. + * + * @tparam T The component used to determine the group rank. + */ + template + Base& group_by() { + return this->group_by(_::type::id(this->world_v()), nullptr); + } + + /** Group and sort matched tables. + * Same as group_by, but with default group_by action. + * + * @param component The component used to determine the group rank. + */ + Base& group_by(flecs::entity_t component) { + return this->group_by(component, nullptr); + } + + /** Specify context to be passed to group_by function. + * + * @param ctx Context to pass to group_by function. + * @param ctx_free Function to cleanup context (called when query is deleted). + */ + Base& group_by_ctx(void *ctx, ecs_ctx_free_t ctx_free = nullptr) { + desc_->group_by_ctx = ctx; + desc_->group_by_ctx_free = ctx_free; + return *this; + } + + /** Specify on_group_create action. + */ + Base& on_group_create(ecs_group_create_action_t action) { + desc_->on_group_create = action; + return *this; + } + + /** Specify on_group_delete action. + */ + Base& on_group_delete(ecs_group_delete_action_t action) { + desc_->on_group_delete = action; + return *this; + } + +protected: + virtual flecs::world_t* world_v() override = 0; + int32_t term_index_; + int32_t expr_count_; + +private: + operator Base&() { + return *static_cast(this); + } + + ecs_query_desc_t *desc_; +}; + +} + + +namespace flecs { +namespace _ { + template + using query_builder_base = builder< + query, ecs_query_desc_t, query_builder, + query_builder_i, Components ...>; +} + +/** Query builder. + * + * @ingroup cpp_core_queries + */ +template +struct query_builder final : _::query_builder_base { + query_builder(flecs::world_t* world, flecs::entity query_entity) + : _::query_builder_base(world) + { + _::sig(world).populate(this); + this->desc_.entity = query_entity.id(); + } + + query_builder(flecs::world_t* world, const char *name = nullptr) + : _::query_builder_base(world) + { + _::sig(world).populate(this); + if (name != nullptr) { + ecs_entity_desc_t entity_desc = {}; + entity_desc.name = name; + entity_desc.sep = "::"; + entity_desc.root_sep = "::"; + this->desc_.entity = ecs_entity_init(world, &entity_desc); + } + } + + template + void each(Func&& func) { + this->build().each(FLECS_FWD(func)); + } +}; + +} + + +namespace flecs +{ + +struct query_base { + query_base() { } + + query_base(query_t *q) + : query_(q) { + flecs_poly_claim(q); + } + + query_base(const query_t *q) + : query_(ECS_CONST_CAST(query_t*, q)) { + flecs_poly_claim(q); + } + + query_base(world_t *world, ecs_query_desc_t *desc) { + if (desc->entity && desc->terms[0].id == 0) { + const flecs::Poly *query_poly = ecs_get_pair( + world, desc->entity, EcsPoly, EcsQuery); + if (query_poly) { + query_ = static_cast(query_poly->poly); + flecs_poly_claim(query_); + return; + } + } + + query_ = ecs_query_init(world, desc); + } + + query_base(const query_base& obj) { + this->query_ = obj.query_; + flecs_poly_claim(this->query_); + } + + query_base& operator=(const query_base& obj) { + this->query_ = obj.query_; + flecs_poly_claim(this->query_); + return *this; + } + + query_base(query_base&& obj) noexcept { + this->query_ = obj.query_; + obj.query_ = nullptr; + } + + query_base& operator=(query_base&& obj) noexcept { + this->query_ = obj.query_; + obj.query_ = nullptr; + return *this; + } + + flecs::entity entity() { + return flecs::entity(query_->world, query_->entity); + } + + const flecs::query_t* c_ptr() const { + return query_; + } + + operator const flecs::query_t*() const { + return query_; + } + + operator bool() const { + return query_ != nullptr; + } + + /** Free persistent query. + * A persistent query is a query that is associated with an entity, such as + * system queries and named queries. Persistent queries must be deleted with + * destruct(), or will be deleted automatically at world cleanup. + */ + void destruct() { + ecs_assert(query_->entity != 0, ECS_INVALID_OPERATION, "destruct() " + "should only be called on queries associated with entities"); + ecs_query_fini(query_); + query_ = nullptr; + } + + ~query_base() { + /* Only free if query is not associated with entity, such as system + * queries and named queries. Named queries have to be either explicitly + * deleted with the .destruct() method, or will be deleted when the + * world is deleted. */ + if (query_ && !query_->entity) { + if (!flecs_poly_release(query_)) { + ecs_query_fini(query_); + query_ = nullptr; + } + } + } + + /** Returns whether the query data changed since the last iteration. + * This operation must be invoked before obtaining the iterator, as this will + * reset the changed state. The operation will return true after: + * - new entities have been matched with + * - matched entities were deleted + * - matched components were changed + * + * @return true if entities changed, otherwise false. + */ + bool changed() const { + return ecs_query_changed(query_); + } + + /** Get info for group. + * + * @param group_id The group id for which to retrieve the info. + * @return The group info. + */ + const flecs::query_group_info_t* group_info(uint64_t group_id) const { + return ecs_query_get_group_info(query_, group_id); + } + + /** Get context for group. + * + * @param group_id The group id for which to retrieve the context. + * @return The group context. + */ + void* group_ctx(uint64_t group_id) const { + const flecs::query_group_info_t *gi = group_info(group_id); + if (gi) { + return gi->ctx; + } else { + return NULL; + } + } + + template + void each_term(const Func& func) { + for (int i = 0; i < query_->term_count; i ++) { + flecs::term t(query_->world, query_->terms[i]); + func(t); + t.reset(); // prevent freeing resources + } + } + + flecs::term term(int32_t index) { + return flecs::term(query_->world, query_->terms[index]); + } + + int32_t term_count() { + return query_->term_count; + } + + int32_t field_count() { + return query_->field_count; + } + + int32_t find_var(const char *name) { + return ecs_query_find_var(query_, name); + } + + flecs::string str() { + char *result = ecs_query_str(query_); + return flecs::string(result); + } + + /** Returns a string representing the query plan. + * This can be used to analyze the behavior & performance of the query. + * @see ecs_query_plan + */ + flecs::string plan() const { + char *result = ecs_query_plan(query_); + return flecs::string(result); + } + + operator query<>() const; + +protected: + query_t *query_ = nullptr; +}; + +template +struct query : query_base, iterable { +private: + using Fields = typename _::field_ptrs::array; + +public: + using query_base::query_base; + + query() : query_base() { } // necessary not to confuse msvc + + query(const query& obj) : query_base(obj) { } + + query& operator=(const query& obj) { + query_base::operator=(obj); + return *this; + } + + query(query&& obj) noexcept : query_base(FLECS_MOV(obj)) { } + + query& operator=(query&& obj) noexcept { + query_base::operator=(FLECS_FWD(obj)); + return *this; + } + +private: + ecs_iter_t get_iter(flecs::world_t *world) const override { + ecs_assert(query_ != nullptr, ECS_INVALID_PARAMETER, + "cannot iterate invalid query"); + if (!world) { + world = query_->world; + } + return ecs_query_iter(world, query_); + } + + ecs_iter_next_action_t next_action() const override { + return ecs_query_next; + } +}; + +// World mixin implementation +template +inline flecs::query world::query(Args &&... args) const { + return flecs::query_builder(world_, FLECS_FWD(args)...) + .build(); +} + +inline flecs::query<> world::query(flecs::entity query_entity) const { + ecs_query_desc_t desc = {}; + desc.entity = query_entity; + return flecs::query<>(world_, &desc); +} + +template +inline flecs::query_builder world::query_builder(Args &&... args) const { + return flecs::query_builder(world_, FLECS_FWD(args)...); +} + +// world::each +namespace _ { + +// Each with entity parameter +template +struct query_delegate_w_ent; + +template +struct query_delegate_w_ent > +{ + query_delegate_w_ent(const flecs::world& world, Func&& func) { + auto f = world.query(); + f.each(FLECS_MOV(func)); + } +}; + +// Each without entity parameter +template +struct query_delegate_no_ent; + +template +struct query_delegate_no_ent > +{ + query_delegate_no_ent(const flecs::world& world, Func&& func) { + auto f = world.query(); + f.each(FLECS_MOV(func)); + } +}; + +// Switch between function with & without entity parameter +template +struct query_delegate; + +template +struct query_delegate, flecs::entity>::value> > { + query_delegate(const flecs::world& world, Func&& func) { + query_delegate_w_ent>(world, FLECS_MOV(func)); + } +}; + +template +struct query_delegate, flecs::entity>::value> > { + query_delegate(const flecs::world& world, Func&& func) { + query_delegate_no_ent>(world, FLECS_MOV(func)); + } +}; + +} + +template +inline void world::each(Func&& func) const { + _::query_delegate f_delegate(*this, FLECS_MOV(func)); +} + +template +inline void world::each(Func&& func) const { + ecs_iter_t it = ecs_each_id(world_, _::type::id()); + + while (ecs_each_next(&it)) { + _::each_delegate(func).invoke(&it); + } +} + +template +inline void world::each(flecs::id_t each_id, Func&& func) const { + ecs_iter_t it = ecs_each_id(world_, each_id); + + while (ecs_each_next(&it)) { + _::each_delegate(func).invoke(&it); + } +} + +// query_base implementation +inline query_base::operator flecs::query<> () const { + return flecs::query<>(query_); +} + +} + +/** + * @file addons/cpp/mixins/observer/impl.hpp + * @brief Observer implementation. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/observer/builder.hpp + * @brief Observer builder. + */ + +#pragma once + +/** + * @file addons/cpp/utils/node_builder.hpp + * @brief Base builder class for node objects, like systems, observers. + */ + +#pragma once + +namespace flecs { +namespace _ { + +// Macros for template types so we don't go cross-eyed +#define FLECS_IBUILDER template class + +template +struct node_builder : IBuilder +{ + using IBase = IBuilder; + +public: + explicit node_builder(flecs::world_t* world, const char *name = nullptr) + : IBase(&desc_) + , desc_{} + , world_(world) + { + ecs_entity_desc_t entity_desc = {}; + entity_desc.name = name; + entity_desc.sep = "::"; + entity_desc.root_sep = "::"; + desc_.entity = ecs_entity_init(world_, &entity_desc); + } + + template + T run(Func&& func) { + using Delegate = typename _::run_delegate< + typename std::decay::type>; + + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); + desc_.run = Delegate::run; + desc_.run_ctx = ctx; + desc_.run_ctx_free = reinterpret_cast< + ecs_ctx_free_t>(_::free_obj); + return T(world_, &desc_); + } + + template + T run(Func&& func, EachFunc&& each_func) { + using Delegate = typename _::run_delegate< + typename std::decay::type>; + + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); + desc_.run = Delegate::run; + desc_.run_ctx = ctx; + desc_.run_ctx_free = reinterpret_cast< + ecs_ctx_free_t>(_::free_obj); + return each(FLECS_FWD(each_func)); + } + + template + T each(Func&& func) { + using Delegate = typename _::each_delegate< + typename std::decay::type, Components...>; + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); + desc_.callback = Delegate::run; + desc_.callback_ctx = ctx; + desc_.callback_ctx_free = reinterpret_cast< + ecs_ctx_free_t>(_::free_obj); + return T(world_, &desc_); + } + +protected: + flecs::world_t* world_v() override { return world_; } + TDesc desc_; + flecs::world_t *world_; +}; + +#undef FLECS_IBUILDER + +} // namespace _ +} // namespace flecs + +/** + * @file addons/cpp/mixins/observer/builder_i.hpp + * @brief Observer builder interface. + */ + +#pragma once + + +namespace flecs { + +/** Observer builder interface. + * + * @ingroup cpp_observers + */ +template +struct observer_builder_i : query_builder_i { + using BaseClass = query_builder_i; + observer_builder_i() + : BaseClass(nullptr) + , desc_(nullptr) + , event_count_(0) { } + + observer_builder_i(ecs_observer_desc_t *desc) + : BaseClass(&desc->query) + , desc_(desc) + , event_count_(0) { } + + /** Specify the event(s) for when the observer should run. + * @param evt The event. + */ + Base& event(entity_t evt) { + desc_->events[event_count_ ++] = evt; + return *this; + } + + /** Specify the event(s) for when the observer should run. + * @tparam E The event. + */ + template + Base& event() { + desc_->events[event_count_ ++] = _::type().id(world_v()); + return *this; + } + + /** Invoke observer for anything that matches its query on creation */ + Base& yield_existing(bool value = true) { + desc_->yield_existing = value; + return *this; + } + + /** Set observer flags */ + Base& observer_flags(ecs_flags32_t flags) { + desc_->flags_ |= flags; + return *this; + } + + /** Set observer context */ + Base& ctx(void *ptr) { + desc_->ctx = ptr; + return *this; + } + + /** Set observer run callback */ + Base& run(ecs_iter_action_t action) { + desc_->run = action; + return *this; + } + +protected: + virtual flecs::world_t* world_v() override = 0; + +private: + operator Base&() { + return *static_cast(this); + } + + ecs_observer_desc_t *desc_; + int32_t event_count_; +}; + +} + + +namespace flecs { +namespace _ { + template + using observer_builder_base = node_builder< + observer, ecs_observer_desc_t, observer_builder, + observer_builder_i, Components ...>; +} + +/** Observer builder. + * + * @ingroup cpp_observers + */ +template +struct observer_builder final : _::observer_builder_base { + observer_builder(flecs::world_t* world, const char *name = nullptr) + : _::observer_builder_base(world, name) + { + _::sig(world).populate(this); + } +}; + +} + + +namespace flecs +{ + +struct observer final : entity +{ + using entity::entity; + + explicit observer() : entity() { } + + observer(flecs::world_t *world, ecs_observer_desc_t *desc) { + world_ = world; + id_ = ecs_observer_init(world, desc); + } + + void ctx(void *ctx) { + ecs_observer_desc_t desc = {}; + desc.entity = id_; + desc.ctx = ctx; + ecs_observer_init(world_, &desc); + } + + void* ctx() const { + return ecs_observer_get(world_, id_)->ctx; + } + + flecs::query<> query() const { + return flecs::query<>(ecs_observer_get(world_, id_)->query); + } +}; + +// Mixin implementation +inline observer world::observer(flecs::entity e) const { + return flecs::observer(world_, e); +} + +template +inline observer_builder world::observer(Args &&... args) const { + return flecs::observer_builder(world_, FLECS_FWD(args)...); +} + +} // namespace flecs + +/** + * @file addons/cpp/mixins/event/impl.hpp + * @brief Event implementation. + */ + +#pragma once + + +namespace flecs +{ + +// Mixin implementation + +inline flecs::event_builder world::event(flecs::entity_t evt) const { + return flecs::event_builder(world_, evt); +} + +template +inline flecs::event_builder_typed world::event() const { + return flecs::event_builder_typed(world_, _::type().id(world_)); +} + +namespace _ { + inline void entity_observer_create( + flecs::world_t *world, + flecs::entity_t event, + flecs::entity_t entity, + ecs_iter_action_t callback, + void *callback_ctx, + ecs_ctx_free_t callback_ctx_free) + { + ecs_observer_desc_t desc = {}; + desc.events[0] = event; + desc.query.terms[0].id = EcsAny; + desc.query.terms[0].src.id = entity; + desc.callback = callback; + desc.callback_ctx = callback_ctx; + desc.callback_ctx_free = callback_ctx_free; + + flecs::entity_t o = ecs_observer_init(world, &desc); + ecs_add_pair(world, o, EcsChildOf, entity); + } + + template + struct entity_observer_factory { + template ::value> = 0> + static void create( + flecs::world_t *world, + flecs::entity_t entity, + Func&& f) + { + using Delegate = _::entity_observer_delegate; + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); + entity_observer_create(world, _::type::id(world), entity, Delegate::run, ctx, + reinterpret_cast(_::free_obj)); + } + + template ::value> = 0> + static void create( + flecs::world_t *world, + flecs::entity_t entity, + Func&& f) + { + using Delegate = _::entity_payload_observer_delegate; + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); + entity_observer_create(world, _::type::id(world), entity, Delegate::run, ctx, + reinterpret_cast(_::free_obj)); + } + }; +} + +template +template +inline const Self& entity_builder::observe(flecs::entity_t evt, Func&& f) const { + using Delegate = _::entity_observer_delegate; + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); + + _::entity_observer_create(world_, evt, id_, Delegate::run, ctx, + reinterpret_cast(_::free_obj)); + + return to_base(); +} + +template +template +inline const Self& entity_builder::observe(Func&& f) const { + _::entity_observer_factory::template create( + world_, id_, FLECS_FWD(f)); + return to_base(); +} + +template +template +inline const Self& entity_builder::observe(Func&& f) const { + return this->observe<_::event_from_func_t>(FLECS_FWD(f)); +} + +inline void entity_view::emit(flecs::entity evt) const { + this->emit(evt.id()); +} + +inline void entity_view::enqueue(flecs::entity evt) const { + this->enqueue(evt.id()); +} + +} // namespace flecs + +/** + * @file addons/cpp/mixins/enum/impl.hpp + * @brief Enum implementation. + */ + +#pragma once + +namespace flecs { + +template +inline E entity_view::to_constant() const { + const E* ptr = this->get(); + ecs_assert(ptr != NULL, ECS_INVALID_PARAMETER, "entity is not a constant"); + return ptr[0]; +} + +template ::value >> +inline flecs::entity world::to_entity(E constant) const { + const auto& et = enum_type(world_); + return flecs::entity(world_, et.entity(constant)); +} + +} +#ifdef FLECS_MODULE +/** + * @file addons/cpp/mixins/module/impl.hpp + * @brief Module implementation. + */ + +#pragma once + +namespace flecs { + +namespace _ { + +template +ecs_entity_t do_import(world& world, const char *symbol) { + ecs_trace("#[magenta]import#[reset] %s", _::type_name()); + ecs_log_push(); + + ecs_entity_t scope = ecs_set_scope(world, 0); + + // Initialize module component type & don't allow it to be registered as a + // tag, as this would prevent calling emplace() + auto c_ = component(world, nullptr, false); + + // Make module component sparse so that it'll never move in memory. This + // guarantees that a module destructor can be reliably used to cleanup + // module resources. + c_.add(flecs::Sparse); + + ecs_set_scope(world, c_); + world.emplace(world); + ecs_set_scope(world, scope); + + ecs_add_id(world, c_, EcsModule); + + // It should now be possible to lookup the module + ecs_entity_t m = ecs_lookup_symbol(world, symbol, false, false); + ecs_assert(m != 0, ECS_MODULE_UNDEFINED, symbol); + ecs_assert(m == c_, ECS_INTERNAL_ERROR, NULL); + + ecs_log_pop(); + + return m; +} + +template +flecs::entity import(world& world) { + const char *symbol = _::symbol_name(); + + ecs_entity_t m = ecs_lookup_symbol(world, symbol, true, false); + + if (!_::type::registered(world)) { + + /* Module is registered with world, initialize static data */ + if (m) { + _::type::init(m, false); + + /* Module is not yet registered, register it now */ + } else { + m = _::do_import(world, symbol); + } + + /* Module has been registered, but could have been for another world. Import + * if module hasn't been registered for this world. */ + } else if (!m) { + m = _::do_import(world, symbol); + } + + return flecs::entity(world, m); +} + +} + +/** + * @defgroup cpp_addons_modules Modules + * @ingroup cpp_addons + * Modules organize components, systems and more in reusable units of code. + * + * @{ + */ + +template +inline flecs::entity world::module(const char *name) const { + flecs::entity result = this->entity(_::type::id( + world_, nullptr, false)); + + if (name) { + flecs::entity prev_parent = result.parent(); + ecs_add_path_w_sep(world_, result, 0, name, "::", "::"); + flecs::entity parent = result.parent(); + if (prev_parent != parent) { + // Module was reparented, cleanup old parent(s) + flecs::entity cur = prev_parent, next; + while (cur) { + next = cur.parent(); + + ecs_iter_t it = ecs_each_id(world_, ecs_pair(EcsChildOf, cur)); + if (!ecs_iter_is_true(&it)) { + cur.destruct(); + } + + cur = next; + } + } + } + + return result; +} + +template +inline flecs::entity world::import() { + return flecs::_::import(*this); +} + +/** @} */ + +} + +#endif +#ifdef FLECS_SYSTEM +/** + * @file addons/cpp/mixins/system/impl.hpp + * @brief System module implementation. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/system/builder.hpp + * @brief System builder. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/system/builder_i.hpp + * @brief System builder interface. + */ + +#pragma once + + +namespace flecs +{ + +/** System builder interface. + * + * @ingroup cpp_addons_systems + */ +template +struct system_builder_i : query_builder_i { +private: + using BaseClass = query_builder_i; + +public: + system_builder_i(ecs_system_desc_t *desc) + : BaseClass(&desc->query) + , desc_(desc) { } + + /** Specify in which phase the system should run. + * + * @param phase The phase. + */ + Base& kind(entity_t phase) { + flecs::entity_t cur_phase = ecs_get_target( + world_v(), desc_->entity, EcsDependsOn, 0); + if (cur_phase) { + ecs_remove_id(world_v(), desc_->entity, ecs_dependson(cur_phase)); + ecs_remove_id(world_v(), desc_->entity, cur_phase); + } + if (phase) { + ecs_add_id(world_v(), desc_->entity, ecs_dependson(phase)); + ecs_add_id(world_v(), desc_->entity, phase); + } + return *this; + } + + template ::value> = 0> + Base& kind(E phase) + { + const auto& et = enum_type(this->world_v()); + flecs::entity_t target = et.entity(phase); + return this->kind(target); + } + + /** Specify in which phase the system should run. + * + * @tparam Phase The phase. + */ + template + Base& kind() { + return this->kind(_::type::id(world_v())); + } + + /** Specify whether system can run on multiple threads. + * + * @param value If false system will always run on a single thread. + */ + Base& multi_threaded(bool value = true) { + desc_->multi_threaded = value; + return *this; + } + + /** Specify whether system should be ran in staged context. + * + * @param value If false system will always run staged. + */ + Base& immediate(bool value = true) { + desc_->immediate = value; + return *this; + } + + /** Set system interval. + * This operation will cause the system to be ran at the specified interval. + * + * The timer is synchronous, and is incremented each frame by delta_time. + * + * @param interval The interval value. + */ + Base& interval(ecs_ftime_t interval) { + desc_->interval = interval; + return *this; + } + + /** Set system rate. + * This operation will cause the system to be ran at a multiple of the + * provided tick source. The tick source may be any entity, including + * another system. + * + * @param tick_source The tick source. + * @param rate The multiple at which to run the system. + */ + Base& rate(const entity_t tick_source, int32_t rate) { + desc_->rate = rate; + desc_->tick_source = tick_source; + return *this; + } + + /** Set system rate. + * This operation will cause the system to be ran at a multiple of the + * frame tick frequency. If a tick source was provided, this just updates + * the rate of the system. + * + * @param rate The multiple at which to run the system. + */ + Base& rate(int32_t rate) { + desc_->rate = rate; + return *this; + } + + /** Set tick source. + * This operation sets a shared tick source for the system. + * + * @tparam T The type associated with the singleton tick source to use for the system. + */ + template + Base& tick_source() { + desc_->tick_source = _::type::id(world_v()); + return *this; + } + + /** Set tick source. + * This operation sets a shared tick source for the system. + * + * @param tick_source The tick source to use for the system. + */ + Base& tick_source(flecs::entity_t tick_source) { + desc_->tick_source = tick_source; + return *this; + } + + /** Set system context */ + Base& ctx(void *ptr) { + desc_->ctx = ptr; + return *this; + } + + /** Set system run callback */ + Base& run(ecs_iter_action_t action) { + desc_->run = action; + return *this; + } + +protected: + virtual flecs::world_t* world_v() override = 0; + +private: + operator Base&() { + return *static_cast(this); + } + + ecs_system_desc_t *desc_; +}; + +} + + +namespace flecs { +namespace _ { + template + using system_builder_base = node_builder< + system, ecs_system_desc_t, system_builder, + system_builder_i, Components ...>; +} + +/** System builder. + * + * @ingroup cpp_addons_systems + */ +template +struct system_builder final : _::system_builder_base { + system_builder(flecs::world_t* world, const char *name = nullptr) + : _::system_builder_base(world, name) + { + _::sig(world).populate(this); + +#ifdef FLECS_PIPELINE + ecs_add_id(world, this->desc_.entity, ecs_dependson(flecs::OnUpdate)); + ecs_add_id(world, this->desc_.entity, flecs::OnUpdate); +#endif + } +}; + +} + + +namespace flecs +{ + +struct system_runner_fluent { + system_runner_fluent( + world_t *world, + entity_t id, + int32_t stage_current, + int32_t stage_count, + ecs_ftime_t delta_time, + void *param) + : stage_(world) + , id_(id) + , delta_time_(delta_time) + , param_(param) + , stage_current_(stage_current) + , stage_count_(stage_count) { } + + system_runner_fluent& offset(int32_t offset) { + offset_ = offset; + return *this; + } + + system_runner_fluent& limit(int32_t limit) { + limit_ = limit; + return *this; + } + + system_runner_fluent& stage(flecs::world& stage) { + stage_ = stage.c_ptr(); + return *this; + } + + ~system_runner_fluent() { + if (stage_count_) { + ecs_run_worker( + stage_, id_, stage_current_, stage_count_, delta_time_, + param_); + } else { + ecs_run(stage_, id_, delta_time_, param_); + } + } + +private: + world_t *stage_; + entity_t id_; + ecs_ftime_t delta_time_; + void *param_; + int32_t offset_; + int32_t limit_; + int32_t stage_current_; + int32_t stage_count_; +}; + +struct system final : entity +{ + using entity::entity; + + explicit system() { + id_ = 0; + world_ = nullptr; + } + + explicit system(flecs::world_t *world, ecs_system_desc_t *desc) { + world_ = world; + id_ = ecs_system_init(world, desc); + } + + void ctx(void *ctx) { + ecs_system_desc_t desc = {}; + desc.entity = id_; + desc.ctx = ctx; + ecs_system_init(world_, &desc); + } + + void* ctx() const { + return ecs_system_get(world_, id_)->ctx; + } + + flecs::query<> query() const { + return flecs::query<>(ecs_system_get(world_, id_)->query); + } + + system_runner_fluent run(ecs_ftime_t delta_time = 0.0f, void *param = nullptr) const { + return system_runner_fluent(world_, id_, 0, 0, delta_time, param); + } + + system_runner_fluent run_worker( + int32_t stage_current, + int32_t stage_count, + ecs_ftime_t delta_time = 0.0f, + void *param = nullptr) const + { + return system_runner_fluent( + world_, id_, stage_current, stage_count, delta_time, param); + } + +# ifdef FLECS_TIMER +/** + * @file addons/cpp/mixins/timer/system_mixin.inl + * @brief Timer module system mixin. + */ + +/** + * @memberof flecs::system + * @ingroup cpp_addons_timer + * + * @{ + */ + +/** Set interval. + * @see ecs_set_interval + */ +void interval(ecs_ftime_t interval); + +/** Get interval. + * @see ecs_get_interval. + */ +ecs_ftime_t interval(); + +/** Set timeout. + * @see ecs_set_timeout + */ +void timeout(ecs_ftime_t timeout); + +/** Get timeout. + * @see ecs_get_timeout + */ +ecs_ftime_t timeout(); + +/** Set system rate (system is its own tick source). + * @see ecs_set_rate + */ +void rate(int32_t rate); + +/** Start timer. + * @see ecs_start_timer + */ +void start(); + +/** Stop timer. + * @see ecs_start_timer + */ +void stop(); + +/** Set external tick source. + * @see ecs_set_tick_source + */ +template +void set_tick_source(); + +/** Set external tick source. + * @see ecs_set_tick_source + */ +void set_tick_source(flecs::entity e); + +/** @} */ + +# endif + +}; + +// Mixin implementation +inline system world::system(flecs::entity e) const { + return flecs::system(world_, e); +} + +template +inline system_builder world::system(Args &&... args) const { + return flecs::system_builder(world_, FLECS_FWD(args)...); +} + +namespace _ { + +inline void system_init(flecs::world& world) { + world.component("flecs::system::TickSource"); +} + +} // namespace _ +} // namespace flecs + +#endif +#ifdef FLECS_PIPELINE +/** + * @file addons/cpp/mixins/pipeline/impl.hpp + * @brief Pipeline module implementation. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/pipeline/builder.hpp + * @brief Pipeline builder. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/pipeline/builder_i.hpp + * @brief Pipeline builder interface. + */ + +#pragma once + + +namespace flecs { + +/** Pipeline builder interface. + * + * @ingroup cpp_pipelines + */ +template +struct pipeline_builder_i : query_builder_i { + pipeline_builder_i(ecs_pipeline_desc_t *desc, int32_t term_index = 0) + : query_builder_i(&desc->query, term_index) + , desc_(desc) { } + +private: + ecs_pipeline_desc_t *desc_; +}; + +} + + +namespace flecs { +namespace _ { + template + using pipeline_builder_base = builder< + pipeline, ecs_pipeline_desc_t, pipeline_builder, + pipeline_builder_i, Components ...>; +} + +/** Pipeline builder. + * + * @ingroup cpp_pipelines + */ +template +struct pipeline_builder final : _::pipeline_builder_base { + pipeline_builder(flecs::world_t* world, flecs::entity_t id = 0) + : _::pipeline_builder_base(world) + { + _::sig(world).populate(this); + this->desc_.entity = id; + } +}; + +} + + +namespace flecs { + +template +struct pipeline : entity { + pipeline(world_t *world, ecs_pipeline_desc_t *desc) + : entity(world) + { + id_ = ecs_pipeline_init(world, desc); + + if (!id_) { + ecs_abort(ECS_INVALID_PARAMETER, NULL); + } + } +}; + +inline flecs::pipeline_builder<> world::pipeline() const { + return flecs::pipeline_builder<>(world_); +} + +template ::value >> +inline flecs::pipeline_builder<> world::pipeline() const { + return flecs::pipeline_builder<>(world_, _::type::id(world_)); +} + +inline void world::set_pipeline(const flecs::entity pip) const { + return ecs_set_pipeline(world_, pip); +} + +template +inline void world::set_pipeline() const { + return ecs_set_pipeline(world_, _::type::id(world_)); +} + +inline flecs::entity world::get_pipeline() const { + return flecs::entity(world_, ecs_get_pipeline(world_)); +} + +inline bool world::progress(ecs_ftime_t delta_time) const { + return ecs_progress(world_, delta_time); +} + +inline void world::run_pipeline(const flecs::entity_t pip, ecs_ftime_t delta_time) const { + return ecs_run_pipeline(world_, pip, delta_time); +} + +template ::value >> +inline void world::run_pipeline(ecs_ftime_t delta_time) const { + return ecs_run_pipeline(world_, _::type::id(world_), delta_time); +} + +inline void world::set_time_scale(ecs_ftime_t mul) const { + ecs_set_time_scale(world_, mul); +} + +inline void world::set_target_fps(ecs_ftime_t target_fps) const { + ecs_set_target_fps(world_, target_fps); +} + +inline void world::reset_clock() const { + ecs_reset_clock(world_); +} + +inline void world::set_threads(int32_t threads) const { + ecs_set_threads(world_, threads); +} + +inline int32_t world::get_threads() const { + return ecs_get_stage_count(world_); +} + +inline void world::set_task_threads(int32_t task_threads) const { + ecs_set_task_threads(world_, task_threads); +} + +inline bool world::using_task_threads() const { + return ecs_using_task_threads(world_); +} + +} + +#endif +#ifdef FLECS_TIMER +/** + * @file addons/cpp/mixins/timer/impl.hpp + * @brief Timer module implementation. + */ + +#pragma once + +namespace flecs { + +// Timer class +struct timer final : entity { + using entity::entity; + + timer& interval(ecs_ftime_t interval) { + ecs_set_interval(world_, id_, interval); + return *this; + } + + ecs_ftime_t interval() { + return ecs_get_interval(world_, id_); + } + + timer& timeout(ecs_ftime_t timeout) { + ecs_set_timeout(world_, id_, timeout); + return *this; + } + + ecs_ftime_t timeout() { + return ecs_get_timeout(world_, id_); + } + + timer& rate(int32_t rate, flecs::entity_t tick_source = 0) { + ecs_set_rate(world_, id_, rate, tick_source); + return *this; + } + + void start() { + ecs_start_timer(world_, id_); + } + + void stop() { + ecs_stop_timer(world_, id_); + } +}; + +template +inline flecs::timer world::timer() const { + return flecs::timer(world_, _::type::id(world_)); +} + +template +inline flecs::timer world::timer(Args &&... args) const { + return flecs::timer(world_, FLECS_FWD(args)...); +} + +inline void world::randomize_timers() const { + ecs_randomize_timers(world_); +} + +inline void system::interval(ecs_ftime_t interval) { + ecs_set_interval(world_, id_, interval); +} + +inline ecs_ftime_t system::interval() { + return ecs_get_interval(world_, id_); +} + +inline void system::timeout(ecs_ftime_t timeout) { + ecs_set_timeout(world_, id_, timeout); +} + +inline ecs_ftime_t system::timeout() { + return ecs_get_timeout(world_, id_); +} + +inline void system::rate(int32_t rate) { + ecs_set_rate(world_, id_, rate, 0); +} + +inline void system::start() { + ecs_start_timer(world_, id_); +} + +inline void system::stop() { + ecs_stop_timer(world_, id_); +} + +template +inline void system::set_tick_source() { + ecs_set_tick_source(world_, id_, _::type::id(world_)); +} + +inline void system::set_tick_source(flecs::entity e) { + ecs_set_tick_source(world_, id_, e); +} + +namespace _ { + +inline void timer_init(flecs::world& world) { + world.component("flecs::timer::RateFilter"); + world.component("flecs::timer::Timer"); +} + +} +} + +#endif +#ifdef FLECS_DOC +/** + * @file addons/cpp/mixins/doc/impl.hpp + * @brief Doc mixin implementation. + */ + +#pragma once + +namespace flecs { +namespace doc { + +/** Get UUID for an entity. + * + * @see ecs_doc_get_uuid() + * @see flecs::doc::set_uuid() + * @see flecs::entity_view::doc_uuid() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_uuid(const flecs::entity_view& e) { + return ecs_doc_get_uuid(e.world(), e); +} + +/** Get human readable name for an entity. + * + * @see ecs_doc_get_name() + * @see flecs::doc::set_name() + * @see flecs::entity_view::doc_name() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_name(const flecs::entity_view& e) { + return ecs_doc_get_name(e.world(), e); +} + +/** Get brief description for an entity. + * + * @see ecs_doc_get_brief() + * @see flecs::doc::set_brief() + * @see flecs::entity_view::doc_brief() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_brief(const flecs::entity_view& e) { + return ecs_doc_get_brief(e.world(), e); +} + +/** Get detailed description for an entity. + * + * @see ecs_doc_get_detail() + * @see flecs::doc::set_detail() + * @see flecs::entity_view::doc_detail() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_detail(const flecs::entity_view& e) { + return ecs_doc_get_detail(e.world(), e); +} + +/** Get link to external documentation for an entity. + * + * @see ecs_doc_get_link() + * @see flecs::doc::set_link() + * @see flecs::entity_view::doc_link() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_link(const flecs::entity_view& e) { + return ecs_doc_get_link(e.world(), e); +} + +/** Get color for an entity. + * + * @see ecs_doc_get_color() + * @see flecs::doc::set_color() + * @see flecs::entity_view::doc_color() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_color(const flecs::entity_view& e) { + return ecs_doc_get_color(e.world(), e); +} + +/** Set UUID for an entity. + * + * @see ecs_doc_set_uuid() + * @see flecs::doc::get_uuid() + * @see flecs::entity_builder::set_doc_uuid() + * + * @ingroup cpp_addons_doc + */ +inline void set_uuid(flecs::entity& e, const char *uuid) { + ecs_doc_set_uuid(e.world(), e, uuid); +} + +/** Set human readable name for an entity. + * + * @see ecs_doc_set_name() + * @see flecs::doc::get_name() + * @see flecs::entity_builder::set_doc_name() + * + * @ingroup cpp_addons_doc + */ +inline void set_name(flecs::entity& e, const char *name) { + ecs_doc_set_name(e.world(), e, name); +} + +/** Set brief description for an entity. + * + * @see ecs_doc_set_brief() + * @see flecs::doc::get_brief() + * @see flecs::entity_builder::set_doc_brief() + * + * @ingroup cpp_addons_doc + */ +inline void set_brief(flecs::entity& e, const char *description) { + ecs_doc_set_brief(e.world(), e, description); +} + +/** Set detailed description for an entity. + * + * @see ecs_doc_set_detail() + * @see flecs::doc::get_detail() + * @see flecs::entity_builder::set_doc_detail() + * + * @ingroup cpp_addons_doc + */ +inline void set_detail(flecs::entity& e, const char *description) { + ecs_doc_set_detail(e.world(), e, description); +} + +/** Set link to external documentation for an entity. + * + * @see ecs_doc_set_link() + * @see flecs::doc::get_link() + * @see flecs::entity_builder::set_doc_link() + * + * @ingroup cpp_addons_doc + */ +inline void set_link(flecs::entity& e, const char *link) { + ecs_doc_set_link(e.world(), e, link); +} + +/** Set color for an entity. + * + * @see ecs_doc_set_color() + * @see flecs::doc::get_color() + * @see flecs::entity_builder::set_doc_color() + * + * @ingroup cpp_addons_doc + */ +inline void set_color(flecs::entity& e, const char *color) { + ecs_doc_set_color(e.world(), e, color); +} + +/** @private */ +namespace _ { + +/** @private */ +inline void init(flecs::world& world) { + world.component("flecs::doc::Description"); +} + +} // namespace _ +} // namespace doc +} // namespace flecs + +#endif +#ifdef FLECS_DOC +#endif +#ifdef FLECS_REST +/** + * @file addons/cpp/mixins/rest/impl.hpp + * @brief Rest module implementation. + */ + +#pragma once + +namespace flecs { +namespace rest { +namespace _ { + +inline void init(flecs::world& world) { + world.component("flecs::rest::Rest"); +} + +} // namespace _ +} // namespace rest +} // namespace flecs + +#endif +#ifdef FLECS_META +/** + * @file addons/cpp/mixins/meta/impl.hpp + * @brief Meta implementation. + */ + +#pragma once + +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 { +namespace _ { + +/* Type support for entity wrappers */ +template +inline flecs::opaque flecs_entity_support(flecs::world&) { + return flecs::opaque() + .as_type(flecs::Entity) + .serialize([](const flecs::serializer *ser, const EntityType *data) { + flecs::entity_t id = data->id(); + return ser->value(flecs::Entity, &id); + }) + .assign_entity( + [](EntityType *dst, flecs::world_t *world, flecs::entity_t e) { + *dst = EntityType(world, e); + }); +} + +inline void init(flecs::world& world) { + world.component("flecs::meta::bool"); + world.component("flecs::meta::char"); + world.component("flecs::meta::u8"); + world.component("flecs::meta::u16"); + world.component("flecs::meta::u32"); + world.component("flecs::meta::u64"); + world.component("flecs::meta::i8"); + world.component("flecs::meta::i16"); + world.component("flecs::meta::i32"); + world.component("flecs::meta::i64"); + world.component("flecs::meta::f32"); + world.component("flecs::meta::f64"); + + world.component("flecs::meta::type_kind"); + world.component("flecs::meta::primitive_kind"); + world.component("flecs::meta::member_t"); + world.component("flecs::meta::enum_constant"); + world.component("flecs::meta::bitmask_constant"); + + world.component("flecs::meta::type"); + world.component("flecs::meta::TypeSerializer"); + world.component("flecs::meta::primitive"); + world.component("flecs::meta::enum"); + world.component("flecs::meta::bitmask"); + world.component("flecs::meta::member"); + world.component("flecs::meta::member_ranges"); + world.component("flecs::meta::struct"); + world.component("flecs::meta::array"); + world.component("flecs::meta::vector"); + + world.component("flecs::meta::unit"); + + // To support member and member register components + // (that do not have conflicting symbols with builtin ones) for platform + // specific types. + + if (!flecs::is_same() && !flecs::is_same()) { + flecs::_::type::init(flecs::Iptr, true); + ecs_assert(flecs::type_id() == flecs::Iptr, + ECS_INTERNAL_ERROR, NULL); + // Remove symbol to prevent validation errors, as it doesn't match with + // the typename + ecs_remove_pair(world, flecs::Iptr, ecs_id(EcsIdentifier), EcsSymbol); + } + + if (!flecs::is_same() && !flecs::is_same()) { + flecs::_::type::init(flecs::Uptr, true); + ecs_assert(flecs::type_id() == flecs::Uptr, + ECS_INTERNAL_ERROR, NULL); + // Remove symbol to prevent validation errors, as it doesn't match with + // the typename + ecs_remove_pair(world, flecs::Uptr, ecs_id(EcsIdentifier), EcsSymbol); + } + + // Register opaque type support for C++ entity wrappers + world.entity("::flecs::cpp").add(flecs::Module).scope([&]{ + world.component() + .opaque(flecs_entity_support); + world.component() + .opaque(flecs_entity_support); + }); +} + +} // namespace _ + +} // namespace meta + + +inline flecs::entity cursor::get_type() const { + return flecs::entity(cursor_.world, ecs_meta_get_type(&cursor_)); +} + +inline flecs::entity cursor::get_unit() const { + return flecs::entity(cursor_.world, ecs_meta_get_unit(&cursor_)); +} + +inline flecs::entity cursor::get_entity() const { + return flecs::entity(cursor_.world, ecs_meta_get_entity(&cursor_)); +} + +/** 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(world_, &desc); + ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); + return flecs::entity(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(world_, &desc); + ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); + return flecs::entity(world_, eid); +} + +/** Create array type. */ +template +inline flecs::entity world::array(int32_t array_count) { + return this->array(_::type::id(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(world_, &desc); + ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); + return flecs::entity(world_, eid); +} + +template +inline flecs::entity world::vector() { + return this->vector(_::type::id(world_)); +} + +} // namespace flecs + +inline int ecs_serializer_t::value(ecs_entity_t type, const void *v) const { + return this->value_(this, type, v); +} + +template +inline int ecs_serializer_t::value(const T& v) const { + return this->value(flecs::_::type::id( + const_cast(this->world)), &v); +} + +inline int ecs_serializer_t::member(const char *name) const { + return this->member_(this, name); +} + +#endif +#ifdef FLECS_UNITS +/** + * @file addons/cpp/mixins/units/impl.hpp + * @brief Units module implementation. + */ + +#pragma once + +namespace flecs { + +inline units::units(flecs::world& world) { + /* Import C module */ + FlecsUnitsImport(world); + + /* Bridge between C++ types and flecs.units entities */ + world.module(); + + // Initialize world.entity(prefixes) scope + world.entity("::flecs::units::prefixes"); + + // Initialize prefixes + world.entity("::flecs::units::prefixes::Yocto"); + world.entity("::flecs::units::prefixes::Zepto"); + world.entity("::flecs::units::prefixes::Atto"); + world.entity("::flecs::units::prefixes::Femto"); + world.entity("::flecs::units::prefixes::Pico"); + world.entity("::flecs::units::prefixes::Nano"); + world.entity("::flecs::units::prefixes::Micro"); + world.entity("::flecs::units::prefixes::Milli"); + world.entity("::flecs::units::prefixes::Centi"); + world.entity("::flecs::units::prefixes::Deci"); + world.entity("::flecs::units::prefixes::Deca"); + world.entity("::flecs::units::prefixes::Hecto"); + world.entity("::flecs::units::prefixes::Kilo"); + world.entity("::flecs::units::prefixes::Mega"); + world.entity("::flecs::units::prefixes::Giga"); + world.entity("::flecs::units::prefixes::Tera"); + world.entity("::flecs::units::prefixes::Peta"); + world.entity("::flecs::units::prefixes::Exa"); + world.entity("::flecs::units::prefixes::Zetta"); + world.entity("::flecs::units::prefixes::Yotta"); + world.entity("::flecs::units::prefixes::Kibi"); + world.entity("::flecs::units::prefixes::Mebi"); + world.entity("::flecs::units::prefixes::Gibi"); + world.entity("::flecs::units::prefixes::Tebi"); + world.entity("::flecs::units::prefixes::Pebi"); + world.entity("::flecs::units::prefixes::Exbi"); + world.entity("::flecs::units::prefixes::Zebi"); + world.entity("::flecs::units::prefixes::Yobi"); + + // Initialize quantities + world.entity("::flecs::units::Duration"); + world.entity iter::field(int8_t index) const { + ecs_assert(!(iter_->flags & EcsIterCppEach), ECS_INVALID_OPERATION, + "cannot .field from .each, use .field_at(%d, row) instead", + _::type_name(), index); + return get_field(index); +} + +template ::value == false, void>::type*> +inline flecs::field iter::field(int8_t index) const { + ecs_assert(!(iter_->flags & EcsIterCppEach), ECS_INVALID_OPERATION, + "cannot .field from .each, use .field_at<%s>(%d, row) instead", + _::type_name(), index); + ecs_assert(!ecs_field_is_readonly(iter_, index), + ECS_ACCESS_VIOLATION, NULL); + return get_field(index); +} + +inline flecs::entity iter::get_var(int var_id) const { + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, 0); + return flecs::entity(iter_->world, ecs_iter_get_var(iter_, var_id)); +} + +/** Get value of variable by name. + * Get value of a query variable for current result. + */ +inline flecs::entity iter::get_var(const char *name) const { + ecs_query_iter_t *qit = &iter_->priv_.iter.query; + const flecs::query_t *q = qit->query; + int var_id = ecs_query_find_var(q, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + return flecs::entity(iter_->world, ecs_iter_get_var(iter_, var_id)); +} + +template +void iter::targets(int8_t index, const Func& func) { + ecs_assert(iter_->table != nullptr, ECS_INVALID_OPERATION, NULL); + ecs_assert(index < iter_->field_count, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ecs_field_is_set(iter_, index), ECS_INVALID_PARAMETER, NULL); + const ecs_type_t *table_type = ecs_table_get_type(iter_->table); + const ecs_table_record_t *tr = iter_->trs[index]; + int32_t i = tr->index, end = i + tr->count; + for (; i < end; i ++) { + ecs_id_t id = table_type->array[i]; + ecs_assert(ECS_IS_PAIR(id), ECS_INVALID_PARAMETER, + "field does not match a pair"); + flecs::entity tgt(iter_->world, + ecs_pair_second(iter_->real_world, id)); + func(tgt); + } +} + +} // namespace flecs + +/** + * @file addons/cpp/impl/world.hpp + * @brief World implementation. + */ + +#pragma once + +namespace flecs +{ + +inline void world::init_builtin_components() { + this->component(); + this->component(); + this->component(); + +# ifdef FLECS_SYSTEM + _::system_init(*this); +# endif +# ifdef FLECS_TIMER + _::timer_init(*this); +# endif +# ifdef FLECS_DOC + doc::_::init(*this); +# endif +# ifdef FLECS_REST + rest::_::init(*this); +# endif +# ifdef FLECS_META + meta::_::init(*this); +# endif +} + +template +inline flecs::entity world::use(const char *alias) const { + entity_t e = _::type::id(world_); + const char *name = alias; + if (!name) { + // If no name is defined, use the entity name without the scope + name = ecs_get_name(world_, e); + } + ecs_set_alias(world_, e, name); + return flecs::entity(world_, e); +} + +inline flecs::entity world::use(const char *name, const char *alias) const { + entity_t e = ecs_lookup_path_w_sep(world_, 0, name, "::", "::", true); + ecs_assert(e != 0, ECS_INVALID_PARAMETER, NULL); + + ecs_set_alias(world_, e, alias); + return flecs::entity(world_, e); +} + +inline void world::use(flecs::entity e, const char *alias) const { + entity_t eid = e.id(); + const char *name = alias; + if (!name) { + // If no name is defined, use the entity name without the scope + name = ecs_get_name(world_, eid); + } + ecs_set_alias(world_, eid, name); +} + +inline flecs::entity world::set_scope(const flecs::entity_t s) const { + return flecs::entity(ecs_set_scope(world_, s)); +} + +inline flecs::entity world::get_scope() const { + return flecs::entity(world_, ecs_get_scope(world_)); +} + +template +inline flecs::entity world::set_scope() const { + return set_scope( _::type::id(world_) ); +} + +inline entity world::lookup(const char *name, const char *sep, const char *root_sep, bool recursive) const { + auto e = ecs_lookup_path_w_sep(world_, 0, name, sep, root_sep, recursive); + return flecs::entity(*this, e); +} + +#ifndef ensure +template +inline T& world::ensure() const { + flecs::entity e(world_, _::type::id(world_)); + return e.ensure(); +} +#endif + +template +inline void world::modified() const { + flecs::entity e(world_, _::type::id(world_)); + e.modified(); +} + +template +inline void world::set(Second second, const First& value) const { + flecs::entity e(world_, _::type::id(world_)); + e.set(second, value); +} + +template +inline void world::set(Second second, First&& value) const { + flecs::entity e(world_, _::type::id(world_)); + e.set(second, value); +} + +template +inline ref world::get_ref() const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_ref(); +} + +template +inline const T* world::get() const { + flecs::entity e(world_, _::type::id(world_)); + return e.get(); +} + +template +const A* world::get() const { + flecs::entity e(world_, _::type::id(world_)); + return e.get(); +} + +template +const First* world::get(Second second) const { + flecs::entity e(world_, _::type::id(world_)); + return e.get(second); +} + +template +T* world::get_mut() const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_mut(); +} + +template +A* world::get_mut() const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_mut(); +} + +template +First* world::get_mut(Second second) const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_mut(second); +} + +template +inline bool world::has() const { + flecs::entity e(world_, _::type::id(world_)); + return e.has(); +} + +template +inline bool world::has() const { + flecs::entity e(world_, _::type::id(world_)); + return e.has(); +} + +template +inline bool world::has(flecs::id_t second) const { + flecs::entity e(world_, _::type::id(world_)); + return e.has(second); +} + +inline bool world::has(flecs::id_t first, flecs::id_t second) const { + flecs::entity e(world_, first); + return e.has(first, second); +} + +template +inline void world::add() const { + flecs::entity e(world_, _::type::id(world_)); + e.add(); +} + +template +inline void world::add() const { + flecs::entity e(world_, _::type::id(world_)); + e.add(); +} + +template +inline void world::add(flecs::entity_t second) const { + flecs::entity e(world_, _::type::id(world_)); + e.add(second); +} + +inline void world::add(flecs::entity_t first, flecs::entity_t second) const { + flecs::entity e(world_, first); + e.add(first, second); +} + +template +inline void world::remove() const { + flecs::entity e(world_, _::type::id(world_)); + e.remove(); +} + +template +inline void world::remove() const { + flecs::entity e(world_, _::type::id(world_)); + e.remove(); +} + +template +inline void world::remove(flecs::entity_t second) const { + flecs::entity e(world_, _::type::id(world_)); + e.remove(second); +} + +inline void world::remove(flecs::entity_t first, flecs::entity_t second) const { + flecs::entity e(world_, first); + e.remove(first, second); +} + +template +inline void world::children(Func&& f) const { + this->entity(0).children(FLECS_FWD(f)); +} + +template +inline flecs::entity world::singleton() const { + return flecs::entity(world_, _::type::id(world_)); +} + +template +inline flecs::entity world::target(int32_t index) const +{ + return flecs::entity(world_, + ecs_get_target(world_, _::type::id(world_), _::type::id(world_), index)); +} + +template +inline flecs::entity world::target( + flecs::entity_t relationship, + int32_t index) const +{ + return flecs::entity(world_, + ecs_get_target(world_, _::type::id(world_), relationship, index)); +} + +inline flecs::entity world::target( + flecs::entity_t relationship, + int32_t index) const +{ + return flecs::entity(world_, + ecs_get_target(world_, relationship, relationship, index)); +} + +template ::value > > +inline void world::get(const Func& func) const { + static_assert(arity::value == 1, "singleton component must be the only argument"); + _::entity_with_delegate::invoke_get( + this->world_, this->singleton>(), func); +} + +template ::value > > +inline void world::set(const Func& func) const { + static_assert(arity::value == 1, "singleton component must be the only argument"); + _::entity_with_delegate::invoke_ensure( + this->world_, this->singleton>(), func); +} + +inline flecs::entity world::get_alive(flecs::entity_t e) const { + e = ecs_get_alive(world_, e); + return flecs::entity(world_, e); +} + +inline flecs::entity world::make_alive(flecs::entity_t e) const { + ecs_make_alive(world_, e); + return flecs::entity(world_, e); +} + +template +inline flecs::entity enum_data::entity() const { + return flecs::entity(world_, impl_.id); +} + +template +inline flecs::entity enum_data::entity(underlying_type_t value) const { + int index = index_by_value(value); + if (index >= 0) { + return flecs::entity(world_, impl_.constants[index].id); + } +#ifdef FLECS_META + // Reflection data lookup failed. Try value lookup amongst flecs::Constant relationships + flecs::world world = flecs::world(world_); + return world.query_builder() + .with(flecs::ChildOf, world.id()) + .with(flecs::Constant, world.id()) + .build() + .find([value](flecs::entity constant) { + const int32_t *constant_value = constant.get_second(flecs::Constant); + ecs_assert(constant_value, ECS_INTERNAL_ERROR, NULL); + return value == static_cast>(*constant_value); + }); +#else + return flecs::entity::null(world_); +#endif +} + +template +inline flecs::entity enum_data::entity(E value) const { + return entity(static_cast>(value)); +} + +/** Use provided scope for operations ran on returned world. + * Operations need to be ran in a single statement. + */ +inline flecs::scoped_world world::scope(id_t parent) const { + return scoped_world(world_, parent); +} + +template +inline flecs::scoped_world world::scope() const { + flecs::id_t parent = _::type::id(world_); + return scoped_world(world_, parent); +} + +inline flecs::scoped_world world::scope(const char* name) const { + return scope(entity(name)); +} + +} // namespace flecs + + +/** + * @defgroup cpp_core Core + * Core ECS functionality (entities, storage, queries) + * + * @{ + * @} + */ + +/** + * @defgroup cpp_addons Addons + * C++ APIs for addons. + * + * @{ + * @} + */ + +/** @} */ + +#endif // __cplusplus + +#endif // FLECS_CPP + +#endif + + +#endif + diff --git a/vendors/flecs/docs/ComponentTraits.md b/vendors/flecs/docs/ComponentTraits.md new file mode 100644 index 000000000..dbbdce04b --- /dev/null +++ b/vendors/flecs/docs/ComponentTraits.md @@ -0,0 +1,1922 @@ +# Component Traits +Component traits are tags and pairs that can be added to components to modify their behavior. This manual contains an overview of all component traits supported by Flecs. + +## Cleanup traits +When entities that are used as tags, components, relationships or relationship targets are deleted, cleanup traits ensure that the store does not contain any dangling references. Any cleanup policy provides this guarantee, so while they are configurable, applications cannot configure traits that allows for dangling references. + +**Note**: this only applies to entities (like tags, components, relationships) that are added _to_ other entities. It does not apply to components that store an entity value, so: + +

+ +The default policy is that any references to the entity will be **removed**. For example, when the tag `Archer` is deleted, it will be removed from all entities that have it, which is similar to invoking the `remove_all` operation: + +
+
    +
  • C + +```c +ecs_remove_all(world, Archer); +``` + +
  • +
  • C++ + +```cpp +world.remove_all(Archer); +``` + +
  • +
  • C# + +```cs +world.RemoveAll(archer); +``` + +
  • +
  • Rust + +```rust +world.remove_all_id(archer); +``` + +
  • +
+
+ +Since entities can be used in relationship pairs, just calling `remove_all` on just the entity itself does not guarantee that no dangling references are left. A more comprehensive description of what happens is: + +
+
    +
  • C + +```c +ecs_remove_all(world, Archer); +ecs_remove_all(world, ecs_pair(Archer, EcsWildcard)); +ecs_remove_all(world, ecs_pair(EcsWildcard, Archer)); +``` + +
  • +
  • C++ + +```cpp +world.remove_all(Archer); +world.remove_all(Archer, flecs::Wildcard); +world.remove_all(flecs::Wildcard, Archer); +``` + +
  • +
  • C# + +```cs +world.RemoveAll(archer); +world.RemoveAll(archer, Ecs.Wildcard); +world.RemoveAll(Ecs.Wildcard, archer); +``` + +
  • +
  • Rust + +```rust +world.remove_all_id(archer); +world.remove_all_id((archer, flecs::Wildcard::ID)); +world.remove_all_id((flecs::Wildcard::ID, archer)); +``` + +
  • +
+
+ +This succeeds in removing all possible references to `Archer`. Sometimes this behavior is not what we want however. Consider a parent-child hierarchy, where we want to delete the child entities when the parent is deleted. Instead of removing `(ChildOf, parent)` from all children, we need to _delete_ the children. + +We also want to specify this per relationship. If an entity has `(Likes, parent)` we may not want to delete that entity, meaning the cleanup we want to perform for `Likes` and `ChildOf` may not be the same. + +This is what cleanup traits are for: to specify which action needs to be executed under which condition. They are applied _to_ entities that have a reference to the entity being deleted: if I delete the `Archer` tag I remove the tag _from_ all entities that have it. + +To configure a cleanup policy for an entity, a `(Condition, Action)` pair can be added to it. If no policy is specified, the default cleanup action (`Remove`) is performed. + +There are three cleanup actions: + +- `Remove`: as if doing `remove_all(entity)` (default) +- `Delete`: as if doing `delete_with(entity)` +- `Panic`: throw a fatal error (default for components) + +There are two cleanup conditions: + +- `OnDelete`: the component, tag or relationship is deleted +- `OnDeleteTarget`: a target used with the relationship is deleted + +Policies apply to both regular and pair instances, so to all entities with `T` as well as `(T, *)`. + +### Examples +The following examples show how to use cleanup traits + +#### (OnDelete, Remove) + +
+
    +
  • C + +```c +// Remove Archer from entities when Archer is deleted +ECS_TAG(world, Archer); +ecs_add_pair(world, Archer, EcsOnDelete, EcsRemove); + +ecs_entity_t e = ecs_new_w_id(world, Archer); + +// This will remove Archer from e +ecs_delete(world, Archer); +``` + +
  • +
  • C++ + +```cpp +// Remove Archer from entities when Archer is deleted +world.component() + .add(flecs::OnDelete, flecs::Remove); + +auto e = world.entity().add(); + +// This will remove Archer from e +world.component().destruct(); +``` + +
  • +
  • C# + +```cs +// Remove Archer from entities when Archer is deleted +world.Component().Entity + .Add(Ecs.OnDelete, Ecs.Remove); + +Entity e = world.Entity().Add(); + +// This will remove Archer from e +world.Component().Entity.Destruct(); +``` + +
  • +
  • Rust + +```rust +// Remove Archer from entities when Archer is deleted +world + .component::() + .add_trait::<(flecs::OnDelete, flecs::Remove)>(); + +let e = world.entity().add::(); +``` +
+
+ +#### (OnDelete, Delete) + +
+
    +
  • C + +```c +// Delete entities with Archer when Archer is deleted +ECS_TAG(world, Archer); +ecs_add_pair(world, Archer, EcsOnDelete, EcsDelete); + +ecs_entity_t e = ecs_new_w_id(world, Archer); + +// This will delete e +ecs_delete(world, Archer); +``` + +
  • +
  • C++ + +```cpp +// Delete entities with Archer when Archer is deleted +world.component() + .add(flecs::OnDelete, flecs::Delete); + +auto e = world.entity().add(); + +// This will delete e +world.component().destruct(); +``` + +
  • +
  • C# + +```cs +// Delete entities with Archer when Archer is deleted +world.Component() + .Add(Ecs.OnDelete, Ecs.Delete); + +Entity e = world.Entity().Add(); + +// This will delete e +world.Component().Entity.Destruct(); +``` + +
  • +
  • Rust + +```rust +// Remove Archer from entities when Archer is deleted +world + .component::() + .add_trait::<(flecs::OnDelete, flecs::Remove)>(); + +let e = world.entity().add::(); + +// This will remove Archer from e +world.component::().destruct(); +``` + +
  • +
+
+ +#### (OnDeleteTarget, Delete) + +
+
    +
  • C + +```c +// Delete children when deleting parent +ECS_TAG(world, ChildOf); +ecs_add_pair(world, ChildOf, EcsOnDeleteTarget, EcsDelete); + +ecs_entity_t p = ecs_new(world); +ecs_entity_t e = ecs_new_w_pair(world, ChildOf, p); + +// This will delete both p and e +ecs_delete(world, p); +``` + +
  • +
  • C++ + +```cpp +// Delete children when deleting parent +world.component() + .add(flecs::OnDeleteTarget, flecs::Delete); + +auto p = world.entity(); +auto e = world.entity().add(p); + +// This will delete both p and e +p.destruct(); +``` + +
  • +
  • C# + +```cs +// Delete children when deleting parent +world.Component().Entity + .Add(Ecs.OnDeleteTarget, Ecs.Delete); + +Entity p = world.Entity(); +Entity e = world.Entity().Add(p); + +// This will delete both p and e +p.Destruct(); +``` + +
  • +
  • Rust + +```rust +// Delete children when deleting parent +world + .component::() + .add_trait::<(flecs::OnDeleteTarget, flecs::Delete)>(); + +let p = world.entity(); +let e = world.entity().add_first::(p); + +// This will delete both p and e +p.destruct(); +``` + +
  • +
+
+ +### Cleanup order +While cleanup actions allow for specifying what needs to happen when a particular entity is deleted, or when an entity used with a particular relationship is deleted, they do not enforce a strict cleanup _order_. The reason for this is that there can be many orderings that satisfy the cleanup traits. + +This is important to consider especially when writing `OnRemove` triggers or hooks, as the order in which they are invoked highly depends on the order in which entities are cleaned up. + +Take an example with a parent and a child that both have the `Node` tag: + +
+
    +
  • C + +```c +ecs_observer(world, { + .query.terms = {{ Node }}, + .events = { EcsOnRemove }, + .callback = ... +}); + +ecs_entity_t p = ecs_new_w(world, Node); +ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); +``` + +
  • +
  • C++ + +```cpp +world.observer() + .event(flecs::OnRemove) + .each([](flecs::entity e) { }); + +flecs::entity p = world.entity().add(); +flecs::entity c = world.entity().add().child_of(p); +``` + +
  • +
  • C# + +```cs +world.Observer() + .Event(Ecs.OnRemove) + .Each((Entity e) => { }); + +Entity p = world.Entity().Add(); +Entity c = world.Entity().Add().ChildOf(p); +``` + +
  • +
  • Rust + +```rust +world + .observer::() + .each_entity(|e, node| { +// This observer will be invoked when a Node is removed +}); + +let p = world.entity().add::(); +let c = world.entity().add::().child_of_id(p); +``` + +
  • +
+
+ +In this example, when calling `p.destruct()` the observer is first invoked for the child, and then for the parent, which is to be expected as the child is deleted before the parent. Cleanup traits do not however guarantee that this is always the case. + +An application could also call `world.component().destruct()` which would delete the `Node` component and all of its instances. In this scenario the cleanup traits for the `ChildOf` relationship are not considered, and therefore the ordering is undefined. Another typical scenario in which ordering is undefined is when an application has cyclical relationships with a `Delete` cleanup action. + +#### Cleanup order during world teardown +Cleanup issues often show up during world teardown as the ordering in which entities are deleted is controlled by the application. While world teardown respects cleanup traits, there can be many entity delete orderings that are valid according to the cleanup traits, but not all of them are equally useful. There are ways to organize entities that helps world cleanup to do the right thing. These are: + +**Organize components, triggers, observers and systems in modules.** +Storing these entities in modules ensures that they stay alive for as long as possible. This leads to more predictable cleanup ordering as components will be deleted as their entities are, vs. when the component is deleted. It also ensures that triggers and observers are not deleted while matching events are still being generated. + +**Avoid organizing components, triggers, observers and systems under entities that are not modules**. If a non-module entity with children is stored in the root, it will get cleaned up along with other regular entities. If you have entities such as these organized in a non-module scope, consider adding the `EcsModule`/`flecs::Module`/`Ecs.Module` tag to the root of that scope. + +The next section goes into more detail on why this improves cleanup behavior and what happens during world teardown. + +#### World teardown sequence +To understand why some ways to organize entities work better than others, having an overview of what happens during world teardown is useful. Here is a list of the steps that happen when a world is deleted: + +1. **Find all root entities** +World teardown starts by finding all root entities, which are entities that do not have the builtin `ChildOf` relationship. Note that empty entities (entities without any components) are not found during this step. + +2. **Query out modules, components, observers and systems** +This ensures that components are not cleaned up before the entities that use them, and triggers, observers and systems are not cleaned up while there are still conditions under which they could be invoked. + +3. **Query out entities that have no children** +If entities have no children they cannot cause complex cleanup logic. This also decreases the likelihood of initiating cleanup actions that could impact other entities. + +4. **Delete root entities** +The root entities that were not filtered out will be deleted. + +5. **Delete everything else** +The last step will delete all remaining entities. At this point cleanup traits are no longer considered and cleanup order is undefined. + +## Trait trait +The trait trait marks an entity as a trait, which is any tag that is added to another tag/component/relationship to modify its behavior. All traits in this manual are marked as trait. It is not required to mark a trait as a trait before adding it to another tag/component/relationship. The main reason for the trait trait is to ease some of the constraints on relationships (see the Relationship trait). + +
+
    +
  • C + +```c +ECS_TAG(world, Serializable); + +ecs_add_id(world, Serializable, EcsTrait); +``` + +
  • +
  • C++ + +```cpp +struct Serializable { }; + +world.component().add(flecs::Trait); +``` + +
  • +
  • C# + +```cs +public struct Serializable { } + +world.Component().Entity.Add(Ecs.Trait); +``` + +
  • +
  • Rust + +```rust +#[derive(Component)] +struct Serializable; + +world + .component::() + .add_trait::(); +``` +} +
  • +
+
+ +## Relationship trait +The relationship trait enforces that an entity can only be used as relationship. Consider the following example: + +
+
    +
  • C + +```c +ECS_TAG(world, Likes); +ECS_TAG(world, Apples); + +ecs_add_id(world, Likes, EcsRelationship); + +ecs_entity_t e = ecs_new_id(world); +ecs_add(world, Likes); // Panic, 'Likes' is not used as relationship +ecs_add_pair(world, Apples, Likes); // Panic, 'Likes' is not used as relationship +ecs_add_pair(world, Likes, Apples); // OK +``` + +
  • +
  • C++ + +```cpp +struct Likes { }; +struct Apples { }; + +world.component().add(flecs::Relationship); + +flecs::entity e = world.entity() + .add() // Panic, 'Likes' is not used as relationship + .add() // Panic, 'Likes' is not used as relationship + .add(); // OK +``` + +
  • +
  • C# + +```cs +public struct Likes { } +public struct Apples { } + +world.Component().Entity.Add(Ecs.Relationship); + +Entity e = ecs.Entity() + .Add() // Panic, 'Likes' is not used as relationship + .Add() // Panic, 'Likes' is not used as relationship + .add(); // OK +``` + +
  • +
  • Rust + +```rust +#[derive(Component)] +struct Likes; + +#[derive(Component)] +struct Apples; + +world + .component::() + .add_trait::(); + +let e = world + .entity() + .add::() // Panic, 'Likes' is not used as relationship + .add::<(Apples, Likes)>() // Panic, 'Likes' is not used as relationship, but as target + .add::<(Likes, Apples)>(); // OK +``` + +
  • +
+
+ +Entities marked with `Relationship` may still be used as target if the relationship part of the pair has the `Trait` trait. This ensures the relationship can still be used to configure the behavior of other entities. Consider the following code example: + +
+
    +
  • C + +```c +ECS_TAG(world, Likes); +ECS_TAG(world, Loves); + +ecs_add_id(world, Likes, EcsRelationship); + +// Even though Likes is marked as relationship and used as target here, this +// won't panic as With is marked as trait. +ecs_add_pair(world, Loves, EcsWith, Likes); +``` + +
  • +
  • C++ + +```cpp +struct Likes { }; +struct Loves { }; + +world.component().add(flecs::Relationship); + +// Even though Likes is marked as relationship and used as target here, this +// won't panic as With is marked as trait. +world.component().add(flecs::With, world.component()); +``` + +
  • +
  • C# + +```cs +public struct Likes { } +public struct Loves { } + +world.Component().Entity.Add(Ecs.Relationship); + +world.Component().Entity.Add(Ecs.With, world.Component()); +``` + +
  • +
  • Rust + +```rust +#[derive(Component)] +struct Likes; + +#[derive(Component)] +struct Loves; + +world + .component::() + .add_trait::(); + +// Even though Likes is marked as relationship and used as target here, this +// won't panic as With is marked as trait. +world + .component::() + .add_trait::<(flecs::With, Likes)>(); +``` + +
  • +
+
+ +## Target trait +The target trait enforces that an entity can only be used as relationship target. Consider the following example: + +
+
    +
  • C + +```c +ECS_TAG(world, Likes); +ECS_TAG(world, Apples); + +ecs_add_id(world, Apples, EcsTarget); + +ecs_entity_t e = ecs_new_id(world); +ecs_add(world, Apples); // Panic, 'Apples' is not used as target +ecs_add_pair(world, Apples, Likes); // Panic, 'Apples' is not used as target +ecs_add_pair(world, Likes, Apples); // OK +``` + +
  • +
  • C++ + +```cpp +struct Likes { }; +struct Apples { }; + +world.component().add(flecs::Target); + +flecs::entity e = world.entity() + .add() // Panic, 'Apples' is not used as target + .add() // Panic, 'Apples' is not used as target + .add(); // OK +``` + +
  • +
  • C# + +```cs +public struct Likes { } +public struct Apples { } + +world.Component().Entity.Add(Ecs.Target); + +Entity e = ecs.Entity() + .Add() // Panic, 'Apples' is not used as target + .Add() // Panic, 'Apples' is not used as target + .Add(); // OK +``` + +
  • +
  • Rust + +```rust +#[derive(Component)] +struct Likes; + +#[derive(Component)] +struct Apples; + +world.component::().add_trait::(); + +let e = world + .entity() + .add::() // Panic, 'Apples' is not used as target + .add::<(Apples, Likes)>() // Panic, 'Apples' is not used as target, but as relationship + .add::<(Likes, Apples)>(); // OK +``` + +
  • +
+
+ +## PairIsTag trait +A relationship can be marked with PairIsTag in which case a pair with the relationship will never contain data. By default the data associated with a pair is determined by whether either the relationship or target are components. For some relationships however, even if the target is a component, no data should be added to the relationship. Consider the following example: + +
+
    +
  • C + +```c +typedef struct { + float x; + float y; +} Position; + +ECS_TAG(world, Serializable); +ECS_COMPONENT(world, Position); + +ecs_entity_t e = ecs_new(world); +ecs_set(world, e, Position, {10, 20}); +ecs_add_pair(world, e, Serializable, ecs_id(Position)); + +// Gets value from Position component +const Position *p = ecs_get(world, e, Position); + +// Gets (unintended) value from (Serializable, Position) pair +const Position *p = ecs_get_pair_second(world, e, Serializable, Position); +``` + +
  • +
  • C++ + +```cpp +struct Serializable { }; // Tag, contains no data + +struct Position { + float x, y; +}; + +auto e = ecs.entity() + .set({10, 20}) + .add(); // Because Serializable is a tag, the pair + // has a value of type Position + +// Gets value from Position component +const Position *p = e.get(); + +// Gets (unintended) value from (Serializable, Position) pair +const Position *p = e.get(); +``` + +
  • +
  • C# + +```cs +public struct Serializable { } // Tag, contains no data + +public record struct Position(float X, float Y); + +Entity e = ecs.Entity() + .Set(new(10, 20)) + .Add(); // Because Serializable is a tag, the pair + // has a value of type Position + +// Gets value from Position component +ref readonly Position p = ref e.Get(); + +// Gets (unintended) value from (Serializable, Position) pair +ref readonly Position p = ref e.GetSecond(); +``` + +
  • +
  • Rust + +```rust +#[derive(Component)] +struct Serializable; // Tag, contains no data + +impl flecs::FlecsTrait for Serializable {} + +#[derive(Component)] +struct Position { + x: f32, + y: f32, +} + +let e = world + .entity() + .set(Position { x: 10.0, y: 20.9 }) + .add_trait::<(Serializable, Position)>(); // Because Serializable is a tag, the pair +// has a value of type Position + +// Gets value from Position component +e.get::<&Position>(|pos| { + println!("Position: ({}, {})", pos.x, pos.y); +}); +// Gets (unintended) value from (Serializable, Position) pair +e.get::<&(Serializable, Position)>(|pos| { + println!("Serializable Position: ({}, {})", pos.x, pos.y); +}); +``` + +
  • +
+
+ +To prevent data from being associated with pairs that can apply to components, the `Tag` trait can be added to relationships: + +
+
    +
  • C + +```c +// Ensure that Serializable never contains data +ecs_add_id(world, Serializable, EcsPairIsTag); + +// Because Serializable is marked as a Tag, no data is added for the pair +// even though Position is a component +ecs_add_pair(world, e, Serializable, ecs_id(Position)); + +// This is still OK +const Position *p = ecs_get(world, e, Position); + +// This no longer works, the pair has no data +const Position *p = ecs_get_pair_second(world, e, Serializable, Position); +``` + +
  • +
  • C++ + +```cpp +// Ensure that Serializable never contains data +ecs.component() + .add(); + +auto e = ecs.entity() + .set({10, 20}) + .add(); // Because Serializable marked as a Tag, no + // data is added for the pair even though + // Position is a component + +// Gets value from Position component +const Position *p = e.get(); + +// This no longer works, the pair has no data +const Position *p = e.get(); +``` + +
  • +
  • C# + +```cs +// Ensure that Serializable never contains data +ecs.Component().Entity + .Add(Ecs.PairIsTag); + +Entity e = ecs.Entity() + .Set(new(10, 20)) + .Add(); // Because Serializable marked as a Tag, no + // data is added for the pair even though + // Position is a component + +// Gets value from Position component +ref readonly Position p = ref e.Get(); + +// This no longer works, the pair has no data +ref readonly Position p = ref e.GetSecond(); +``` + +
  • +
  • Rust + +```rust +// This is currently not supported in Rust due to safety concerns. +``` + +
  • +
+
+ +The `Tag` trait is only interpreted when it is added to the relationship part of a pair. + +## Final trait +Entities can be annotated with the `Final` trait, which prevents using them with `IsA` relationship. This is similar to the concept of a final class as something that cannot be extended. The following example shows how use `Final`: + +
+
    +
  • C + +```c +ecs_entity_t e = ecs_new(world); +ecs_add_id(world, e, EcsFinal); + +ecs_entity_t i = ecs_new(world); +ecs_add_pair(world, e, i, EcsIsA, e); // not allowed +``` + +
  • +
  • C++ + +```cpp +auto e = ecs.entity() + .add(flecs::Final); + +auto i = ecs.entity() + .is_a(e); // not allowed +``` + +
  • +
  • C# + +```cs +Entity e = ecs.Entity() + .Add(Ecs.Final); + +Entity i = ecs.Entity() + .IsA(e); // not allowed +``` + +
  • +
  • Rust + +```rust +let e = world.entity().add_trait::(); + +let i = world.entity().is_a_id(e); // not allowed +``` + +
  • +
+
+ +Queries may use the final trait to optimize, as they do not have to explore subsets of a final entity. For more information on how queries interpret final, see the [Query manual](Queries.md). By default, all components are created as final. + +## OnInstantiate trait +The `OnInstantiate` trait configures the behavior of components when an entity is instantiated from another entity (usually a prefab). Instantiation happens when an `IsA` pair is added to an entity. + +By default, when an entity is instantiated, the components from the base entity (the `IsA` target) are copied to the instance. This behavior can be modified with the `OnInstantiate` trait, which can be used as pair in combination with three targets: + +| Target | C | C++ | C# | Description | +|-------------|------------------|----------------------|-------------------|-------------| +| Override | `EcsOverride` | `flecs::Override` | `Ecs.Override` | Copy component from base to instance (default) | +| Inherit | `EcsInherit` | `flecs::Inherit` | `Ecs.Inherit` | Inherit component from base | +| DontInherit | `EcsDontInherit` | `flecs::DontInherit` | `Ecs.DontInherit` | Don't inherit (and don't copy) component from base | + +### Override +The default behavior for `OnInstantiate` is `Override`, which means that the component is copied to the instance. This means that after instantiation, the instance has an owned copy for the component that masks the base component (the "override"). + +Note that for an override to work correctly, a component has to be copyable. + +The following example shows how to use the `Override` trait: + +
+
    +
  • C + +```c +ECS_COMPONENT(ecs, Mass); + +// Set trait on Mass. Optional, since this is the default behavior +ecs_add_pair(ecs, ecs_id(Mass), EcsOnInstantiate, EcsOverride); + +ecs_entity_t base = ecs_insert(ecs, ecs_value(Mass, { 100 })); +ecs_entity_t inst = ecs_insert(ecs, { ecs_isa(base) }); // Mass is copied to inst + +assert(ecs_owns(ecs, inst, Mass)); +assert(ecs_get(ecs, base, Mass) != ecs_get(ecs, inst, Mass)); +``` + +
  • +
  • C++ + +```cpp +// Register component with trait. Optional, since this is the default behavior. +ecs.component().add(flecs::OnInstantiate, flecs::Override); + +ecs_entity_t base = ecs.entity().set(Mass, { 100 }); +ecs_entity_t inst = ecs.entity().is_a(base); // Mass is copied to inst + +assert(inst.owns()); +assert(base.get() != inst.get()); +``` + +
  • +
  • C# + +```cs +// Register component with trait. Optional, since this is the default behavior. +ecs.Component().Entity + .Add(Ecs.OnInstantiate, Ecs.Override); + +Entity base = ecs.Entity() + .Set(new(100)); + +Entity inst = ecs.Entity() + .IsA(base); // Mass is copied to inst + +Debug.Assert(inst.Owns()); +``` + +
  • +
  • Rust + +```rust +// Register component with trait. Optional, since this is the default behavior. +world +.component::() +.add_trait::<(flecs::OnInstantiate, flecs::Override)>(); + +let base = world.entity().set(Mass { value: 100.0 }); +let inst = world.entity().is_a_id(base); // Mass is copied to inst + +assert!(inst.owns::()); +assert!(base.cloned::<&Mass>() != inst.cloned::<&Mass>()); +``` + +
  • +
+
+ +### Inherit +Components with the `Inherit` trait are inherited from a base entity (the `IsA` target) on instantiation. Inherited components are not copied to the instance, and are only stored once in memory. Operations such as `get` and `has`, and queries will automatically lookup inheritable components by following the `IsA` relationship. + +Inheritable components can be overridden manually by adding the component to the instance. This results in the same behavior as the `Override` trait, where the component is copied from the base entity. + +The following example shows how to use the `Inherit` trait: + +
+
    +
  • C + +```c +ECS_COMPONENT(ecs, Mass); + +// Set trait on Mass +ecs_add_pair(ecs, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + +ecs_entity_t base = ecs_insert(ecs, ecs_value(Mass, { 100 })); +ecs_entity_t inst = ecs_insert(ecs, { ecs_isa(base) }); // Mass is copied to inst + +assert(ecs_has(ecs, inst, Mass)); +assert(!ecs_owns(ecs, inst, Mass)); +assert(ecs_get(ecs, base, Mass) != ecs_get(ecs, inst, Mass)); +``` + +
  • +
  • C++ + +```cpp +// Register component with trait +ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + +ecs_entity_t base = ecs.entity().set(Mass, { 100 }); +ecs_entity_t inst = ecs.entity().is_a(base); + +assert(inst.has()); +assert(!inst.owns()); +assert(base.get() != inst.get()); +``` + +
  • +
  • C# + +```cs +// Register component with trait +ecs.Component().Entity + .Add(Ecs.OnInstantiate, Ecs.Inherit); + +Entity base = ecs.Entity() + .Set(new(100)); + +Entity inst = ecs.Entity() + .IsA(base); + +Debug.Assert(inst.Has()); +Debug.Assert(!inst.Owns()); +``` + +
  • +
  • Rust + +```rust +// Register component with trait +world +.component::() +.add_trait::<(flecs::OnInstantiate, flecs::Inherit)>(); + +let base = world.entity().set(Mass { value: 100.0 }); +let inst = world.entity().is_a_id(base); + +assert!(inst.has::()); +assert!(!inst.owns::()); +assert!(base.cloned::<&Mass>() != inst.cloned::<&Mass>()); +``` + +
  • +
+
+ +### DontInherit +Components with the `DontInherit` trait are not inherited from a base entity (the `IsA` target) on instantiation, and are not copied to the instance. Operations such as `has` and `get` will not find the component, and queries will not match it. + +Components with the `DontInherit` cannot be overridden manually. When a component is added to an instance and the base also has the component, the base component is ignored and its value is not copied to the instance. + +The following example shows how to use the `DontInherit` trait: + +
+
    +
  • C + +```c +ECS_COMPONENT(ecs, Mass); + +// Set trait on Mass +ecs_add_pair(ecs, ecs_id(Mass), EcsOnInstantiate, EcsDontInherit); + +ecs_entity_t base = ecs_insert(ecs, ecs_value(Mass, { 100 })); +ecs_entity_t inst = ecs_insert(ecs, { ecs_isa(base) }); // Mass is copied to inst + +assert(!ecs_has(ecs, inst, Mass)); +assert(!ecs_owns(ecs, inst, Mass)); +assert(ecs_get(ecs, inst, Mass) == NULL); +``` + +
  • +
  • C++ + +```cpp +// Register component with trait +ecs.component().add(flecs::OnInstantiate, flecs::DontInherit); + +ecs_entity_t base = ecs.entity().set(Mass, { 100 }); +ecs_entity_t inst = ecs.entity().is_a(base); + +assert(!inst.has()); +assert(!inst.owns()); +assert(inst.get() == nullptr); +``` + +
  • +
  • C# + +```cs +// Register component with trait +ecs.Component().Entity + .Add(Ecs.OnInstantiate, Ecs.DontInherit); + +Entity base = ecs.Entity() + .Set(new(100)); + +Entity inst = ecs.Entity() + .IsA(base); + +Debug.Assert(!inst.Has()); +Debug.Assert(!inst.Owns()); +``` + +
  • +
  • Rust + +```rust +// Register component with trait +world +.component::() +.add_trait::<(flecs::OnInstantiate, flecs::DontInherit)>(); + +let base = world.entity().set(Mass { value: 100.0 }); +let inst = world.entity().is_a_id(base); + +assert!(!inst.has::()); +assert!(!inst.owns::()); +assert!(!inst.try_get::<&Mass>(|mass| {})); +``` + +
  • +
+
+ +## Transitive trait +Relationships can be marked as transitive. A formal-ish definition if transitivity in the context of relationships is: + +``` +If Relationship(EntityA, EntityB) +And Relationship(EntityB, EntityC) +Then Relationship(EntityA, EntityC) +``` + +What this means becomes more obvious when translated to a real-life example: + +> If Manhattan is located in New York, and New York is located in the USA, then Manhattan is located in the USA. + +In this example, `LocatedIn` is the relationship and `Manhattan`, `New York` and `USA` are entities `A`, `B` and `C`. Another common example of transitivity is found in OOP inheritance: + +> If a Square is a Rectangle and a Rectangle is a Shape, then a Square is a Shape. + +In this example `IsA` is the relationship and `Square`, `Rectangle` and `Shape` are the entities. + +When relationships in Flecs are marked as transitive, queries can follow the transitive relationship to see if an entity matches. Consider this example dataset: + +
+
    +
  • C + +```c +ecs_entity_t LocatedIn = ecs_new(world); +ecs_entity_t Manhattan = ecs_new(world); +ecs_entity_t NewYork = ecs_new(world); +ecs_entity_t USA = ecs_new(world); + +ecs_add_pair(world, Manhattan, LocatedIn, NewYork); +ecs_add_pair(world, NewYork, LocatedIn, USA); +``` + +
  • +
  • C++ + +```cpp +auto LocatedIn = world.entity(); +auto Manhattan = world.entity(); +auto NewYork = world.entity(); +auto USA = world.entity(); + +Manhattan.add(LocatedIn, NewYork); +NewYork.add(LocatedIn, USA); +``` + +
  • +
  • C# + +```cs +Entity locatedin = world.Entity(); +Entity manhattan = world.Entity(); +Entity newyork = world.Entity(); +Entity usa = world.Entity(); + +Manhattan.Add(locatedin, newyork); +NewYork.Add(locatedin, usa); +``` + +
  • +
  • Rust + +```rust +let locatedin = world.entity(); +let manhattan = world.entity(); +let newyork = world.entity(); +let usa = world.entity(); + +manhattan.add_id((locatedin, newyork)); +newyork.add_id((locatedin, usa)); +``` + +
  • +
+
+ +If we were now to query for `(LocatedIn, USA)` we would only match `NewYork`, because we never added `(LocatedIn, USA)` to `Manhattan`. To make sure queries `Manhattan` as well we have to make the `LocatedIn` relationship transitive. We can simply do this by adding the transitive trait to the relationship entity: + +
+
    +
  • C + +```c +ecs_add_id(world, LocatedIn, Transitive); +``` + +
  • +
  • C++ + +```cpp +LocatedIn.add(flecs::Transitive); +``` + +
  • +
  • C# + +```cs +locatedIn.Add(Ecs.Transitive); +``` + +
  • +
  • Rust + +```rust +locatedin.add_trait::(); +``` + +
  • +
+
+ +When now querying for `(LocatedIn, USA)`, the query will follow the `LocatedIn` relationship and return both `NewYork` and `Manhattan`. For more details on how queries use transitivity, see the [Transitive Relationships section in the query manual](Queries.md#transitive-relationships). + +## Reflexive trait +A relationship can be marked reflexive which means that a query like `Relationship(Entity, Entity)` should evaluate to true. The utility of `Reflexive` becomes more obvious with an example: + +Given this dataset: +``` +IsA(Oak, Tree) +``` + +we can ask whether an oak is a tree: +``` +IsA(Oak, Tree) +- Yes, an Oak is a tree (Oak has (IsA, Tree)) +``` + +We can also ask whether a tree is a tree, which it obviously is: +``` +IsA(Tree, Tree) +- Yes, even though Tree does not have (IsA, Tree) +``` + +However, this does not apply to all relationships. Consider a dataset with a `LocatedIn` relationship: + +``` +LocatedIn(SanFrancisco, UnitedStates) +``` + +we can now ask whether SanFrancisco is located in SanFrancisco, which it is not: +``` +LocatedIn(SanFrancisco, SanFrancisco) +- No +``` + +In these examples, `IsA` is a reflexive relationship, whereas `LocatedIn` is not. + +## Acyclic trait +A relationship can be marked with the `Acyclic` trait to indicate that it cannot contain cycles. Both the builtin `ChildOf` and `IsA` relationships are marked acyclic. Knowing whether a relationship is acyclic allows the storage to detect and throw errors when a cyclic relationship is introduced by accident. + +Note that because cycle detection requires expensive algorithms, adding `Acyclic` to a relationship does not guarantee that an error will be thrown when a cycle is accidentally introduced. While detection may improve over time, an application that runs without errors is no guarantee that it does not contain acyclic relationships with cycles. + +## Traversable trait +Traversable relationships are allowed to be traversed automatically by queries, for example using the `up` bit flag (upwards traversal, see [query traversal flags](Queries.md#traversal-flags)). Traversable relationships are also marked as `Acyclic`, which ensures a query won't accidentally attempt to traverse a relationship that contains cycles. + +Events are propagated along the edges of traversable relationships. A typical example of this is when a component value is changed on a prefab. The event of this change will be propagated by traversing the `IsA` relationship downwards, for all instances of the prefab. Event propagation does not happen for relationships that are not marked with `Traversable`. + +## Exclusive trait +The `Exclusive` trait enforces that an entity can have only a single instance of a relationship. When a second instance is added, it replaces the first instance. An example of a relationship with the `Exclusive` trait is the builtin `ChildOf` relationship: + +
+
    +
  • C + +```c +ecs_add_pair(world, child, EcsChildOf, parent_a); +ecs_add_pair(world, child, EcsChildOf, parent_b); // replaces (ChildOf, parent_a) +``` + +
  • +
  • C++ + +```cpp +e.child_of(parent_a); +e.child_of(parent_b); // replaces (ChildOf, parent_a) +``` + +
  • +
  • C# + +```cs +e.ChildOf(parentA); +e.ChildOf(parentB); // replaces (ChildOf, parentA) +``` + +
  • +
  • Rust + +```rust +let parent_a = world.entity(); +let parent_b = world.entity(); +e.child_of_id(parent_a); +e.child_of_id(parent_b); // replaces (ChildOf, parent_a) +``` + +
  • +
+
+ +To create a custom exclusive relationship, add the `Exclusive` trait: + +
+
    +
  • C + +```c +ecs_entity_t MarriedTo = ecs_new(world); +ecs_add_id(world, MarriedTo, EcsExclusive); +``` + +
  • +
  • C++ + +```cpp +flecs::entity MarriedTo = world.entity() + .add(flecs::Exclusive); +``` + +
  • +
  • C# + +```cs +Entity marriedTo = world.Entity() + .Add(Ecs.Exclusive); +``` + +
  • +
  • Rust + +```rust +let married_to = world.entity().add_trait::(); +``` + +
  • +
+
+ +## CanToggle trait +The `CanToggle` trait allows a component to be toggled. Component toggling can (temporarily) disable a component, which excludes it from queries. Component toggling can be used as a cheaper alternative to adding/removing as toggling relies on setting a bitset, and doesn't require the entity to be moved between tables. Component toggling can also be used to restore a component with its old value. + +Queries treat a disabled component as if the entity doesn't have it. `CanToggle` components add a small amount of overhead to query evaluation, even for entities that did not toggle their component. + +The following example shows how to use the `CanToggle` trait: + +
+
    +
  • C + +```c +ECS_COMPONENT(world, Position); +ecs_add_id(world, ecs_id(Position), EcsCanToggle); + +ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + +ecs_enable_component(world, e, Position, false); // Disable component +assert(!ecs_is_enabled(world, e, Position)); + +ecs_enable_component(world, e, Position, true); // Enable component +assert(ecs_is_enabled(world, e, Position)); +``` + +
  • +
  • C++ + +```cpp +world.component().add(flecs::CanToggle); + +flecs::entity e = world.entity().set(Position{10, 20}); + +e.disable(); // Disable component +assert(!e.is_enabled()); + +e.enable(); // Enable component +assert(e.is_enabled()); +``` + +
  • +
  • C# + +```cs +ecs.Component().Entity + .Add(Ecs.CanToggle); + +Entity e = world.Entity() + .Set(new(10, 20)); + +e.Disable(); // Disable component +Debug.Assert(!e.IsEnabled()); + +e.Enable(); // Enable component +Debug.Assert(e.IsEnabled()); +``` + +
  • +
  • Rust + +```rust +world +.component::() +.add_trait::(); + +let e = world.entity().set(Position { x: 10.0, y: 20.0 }); + +e.disable::(); // Disable component +assert!(!e.is_enabled::()); + +e.enable::(); // Enable component +assert!(e.is_enabled::()); +``` + +
  • +
+
+ +## Union trait +The `Union` is similar to `Exclusive` in that it enforces that an entity can have only a single instance of a relationship. The difference between `Exclusive` and `Union` is that `Union` combines different relationship targets in a single table. This reduces table fragmentation, and as a result speeds up add/remove operations. This increase in add/remove speed does come at a cost: iterating a query with union terms is more expensive than iterating a regular relationship. + +The API for using the `Union` trait is similar to regular relationships, as this example shows: + +
+
    +
  • C + +```c +ecs_entity_t Movement = ecs_new(world); +ecs_add_id(world, Movement, EcsUnion); + +ecs_entity_t Walking = ecs_new(world); +ecs_entity_t Running = ecs_new(world); + +ecs_entity_t e = ecs_new(world); +ecs_add_pair(world, e, Movement, Running); +ecs_add_pair(world, e, Movement, Walking); // replaces (Movement, Running) +``` + +
  • +
  • C++ + +```cpp +flecs::entity Movement = world.entity().add(flecs::Union); +flecs::entity Walking = world.entity(); +flecs::entity Running = world.entity(); + +flecs::entity e = world.entity().add(Movement, Running); +e.add(Movement, Walking); // replaces (Movement, Running) +``` + +
  • +
  • C# + +```cs +Entity movement = world.Entity().Add(Ecs.Union); +Entity walking = world.Entity(); +Entity running = world.Entity(); + +Entity e = world.Entity().Add(movement, running); +e.Add(movement, walking); // replaces (Movement, Running) +``` + +
  • +
  • Rust + +```rust +let movement = world.entity().add_trait::(); +let walking = world.entity(); +let running = world.entity(); + +let e = world.entity().add_id((movement, running)); +e.add_id((movement, walking)); // replaces (Movement, Running) +``` + +
  • +
+
+ +When compared to regular relationships, union relationships have some differences and limitations: +- Relationship cleanup does not work yet for union relationships +- Removing a union relationship removes any target, even if the specified target is different +- Union relationships cannot have data + +## Sparse trait +The `Sparse` trait configures a component to use sparse storage. Sparse components are stored outside of tables, which means they do not have to be moved. Sparse components are also guaranteed to have stable pointers, which means that a component pointer is not invalidated when an entity moves to a new table. ECS operations and queries work as expected with sparse components. + +Sparse components trade in query speed for component add/remove speed. Adding and removing sparse components still requires an archetype change. + +They also enable storage of non-movable components. Non-movable components in the C++ API are automatically marked as sparse. + +The following code example shows how to mark a component as sparse: + +
+
    +
  • C + +```c +ECS_COMPONENT(world, Position); +ecs_add_id(world, ecs_id(Position), EcsSparse); +``` + +
  • +
  • C++ + +```cpp +world.component().add(flecs::Sparse); +``` + +
  • +
  • C# + +```cs +ecs.Component().Entity + .Add(Ecs.Sparse); +``` + +
  • +
  • Rust + +```rust +world.component::().add_trait::(); +``` + +
  • +
+
+ +## Symmetric trait +The `Symmetric` trait enforces that when a relationship `(R, Y)` is added to entity `X`, the relationship `(R, X)` will be added to entity `Y`. The reverse is also true, if relationship `(R, Y)` is removed from `X`, relationship `(R, X)` will be removed from `Y`. + +The symmetric trait is useful for relationships that do not make sense unless they are bidirectional. Examples of such relationships are `AlliesWith`, `MarriedTo`, `TradingWith` and so on. An example: + +
+
    +
  • C + +```c +ecs_entity_t MarriedTo = ecs_new_w_id(world, EcsSymmetric); +ecs_entity_t Bob = ecs_new(world); +ecs_entity_t Alice = ecs_new(world); +ecs_add_pair(world, Bob, MarriedTo, Alice); // Also adds (MarriedTo, Bob) to Alice +``` + +
  • +
  • C++ + +```cpp +auto MarriedTo = world.entity().add(flecs::Symmetric); +auto Bob = ecs.entity(); +auto Alice = ecs.entity(); +Bob.add(MarriedTo, Alice); // Also adds (MarriedTo, Bob) to Alice +``` + +
  • +
  • C# + +```cs +Entity marriedTo = world.Entity().Add(Ecs.Symmetric); +Entity bob = ecs.Entity(); +Entity alice = ecs.Entity(); +Bob.Add(marriedTo, alice); // Also adds (MarriedTo, Bob) to Alice +``` + +
  • +
  • Rust + +```rust +let married_to = world.entity().add_trait::(); +let bob = world.entity(); +let alice = world.entity(); +bob.add_id((married_to, alice)); // Also adds (MarriedTo, Bob) to Alice +``` + +
  • +
+
+ +## With trait +The `With` relationship can be added to components to indicate that it must always come together with another component. The following example shows how `With` can be used with regular components/tags: + +
+
    +
  • C + +```c +ecs_entity_t Responsibility = ecs_new(world); +ecs_entity_t Power = ecs_new_w_pair(world, EcsWith, Responsibility); + +// Create new entity that has both Power and Responsibility +ecs_entity_t e = ecs_new_w_id(world, Power); +``` + +
  • +
  • C++ + +```cpp +auto Responsibility = world.entity(); +auto Power = world.entity().add(flecs::With, Responsibility); + +// Create new entity that has both Power and Responsibility +auto e = world.entity().add(Power); +``` + +
  • +
  • C# + +```cs +Entity responsibility = world.Entity(); +Entity power = world.Entity().Add(Ecs.With, responsibility); + +// Create new entity that has both Power and Responsibility +Entity e = world.Entity().Add(power); +``` + +
  • +
  • Rust + +```rust +let responsibility = world.entity(); +let power = world.entity().add_first::(responsibility); + +// Create new entity that has both Power and Responsibility +let e = world.entity().add_id(power); +``` + +
  • +
+
+ +When the `With` relationship is added to a relationship, the additional id added to the entity will be a relationship pair as well, with the same target as the original relationship: + +
+
    +
  • C + +```c +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Loves = ecs_new_w_pair(world, EcsWith, Likes); +ecs_entity_t Pears = ecs_new(world); + +// Create new entity with both (Loves, Pears) and (Likes, Pears) +ecs_entity_t e = ecs_new_w_pair(world, Loves, Pears); +``` + +
  • +
  • C++ + +```cpp +auto Likes = world.entity(); +auto Loves = world.entity().add(flecs::With, Likes); +auto Pears = world.entity(); + +// Create new entity with both (Loves, Pears) and (Likes, Pears) +auto e = world.entity().add(Loves, Pears); +``` + +
  • +
  • C# + +```cs +Entity likes = world.Entity(); +Entity loves = world.Entity().Add(Ecs.With, likes); +Entity pears = world.Entity(); + +// Create new entity with both (Loves, Pears) and (Likes, Pears) +Entity e = world.Entity().Add(loves, pears); +``` + +
  • +
  • Rust + +```rust +let likes = world.entity(); +let loves = world.entity().add_trait::<(flecs::With, Likes)>(); +let pears = world.entity(); + +// Create new entity with both (Loves, Pears) and (Likes, Pears) +let e = world.entity().add_id((loves, pears)); +``` + +
  • +
+
+ +## OneOf trait +The `OneOf` trait enforces that the target of the relationship is a child of a specified entity. `OneOf` can be used to indicate that the target needs to be either a child of the relationship (common for enum relationships), or of another entity. + +The following example shows how to constrain the relationship target to a child of the relationship: + +
+
    +
  • C + +```c +ecs_entity_t Food = ecs_new(world); + +// Enforce that target of relationship is child of Food +ecs_add_id(world, Food, EcsOneOf); + +ecs_entity_t Apples = ecs_new_w_pair(world, EcsChildOf, Food); +ecs_entity_t Fork = ecs_new(world); + +// This is ok, Apples is a child of Food +ecs_entity_t a = ecs_new_w_pair(world, Food, Apples); + +// This is not ok, Fork is not a child of Food +ecs_entity_t b = ecs_new_w_pair(world, Food, Fork); +``` + +
  • +
  • C++ + +```cpp +// Enforce that target of relationship is child of Food +auto Food = world.entity().add(flecs::OneOf); +auto Apples = world.entity().child_of(Food); +auto Fork = world.entity(); + +// This is ok, Apples is a child of Food +auto a = world.entity().add(Food, Apples); + +// This is not ok, Fork is not a child of Food +auto b = world.entity().add(Food, Fork); +``` + +
  • +
  • C# + +```cs +// Enforce that target of relationship is child of Food +Entity food = world.Entity().Add(Ecs.OneOf); +Entity apples = world.Entity().ChildOf(food); +Entity fork = world.Entity(); + +// This is ok, Apples is a child of Food +Entity a = world.Entity().Add(food, apples); + +// This is not ok, Fork is not a child of Food +Entity b = world.Entity().Add(food, fork); +``` + +
  • +
  • Rust + +```rust +// Enforce that target of relationship is child of Food +let food = world.entity().add_trait::(); +let apples = world.entity().child_of_id(food); +let fork = world.entity(); + +// This is ok, Apples is a child of Food +let a = world.entity().add_id((food, apples)); + +// This is not ok, Fork is not a child of Food +let b = world.entity().add_id((food, fork)); +``` + +
  • +
+
+ +The following example shows how `OneOf` can be used to enforce that the relationship target is the child of an entity other than the relationship: + +
+
    +
  • C + +```c +ecs_entity_t Food = ecs_new(world); +ecs_entity_t Eats = ecs_new(world); + +// Enforce that target of relationship is child of Food +ecs_add_pair(world, Eats, EcsOneOf, Food); + +ecs_entity_t Apples = ecs_new_w_pair(world, EcsChildOf, Food); +ecs_entity_t Fork = ecs_new(world); + +// This is ok, Apples is a child of Food +ecs_entity_t a = ecs_new_w_pair(world, Eats, Apples); + +// This is not ok, Fork is not a child of Food +ecs_entity_t b = ecs_new_w_pair(world, Eats, Fork); +``` + +
  • +
  • C++ + +```cpp +// Enforce that target of relationship is child of Food +auto Food = world.entity(); +auto Eats = world.entity().add(flecs::OneOf, Food); +auto Apples = world.entity().child_of(Food); +auto Fork = world.entity(); + +// This is ok, Apples is a child of Food +auto a = world.entity().add(Eats, Apples); + +// This is not ok, Fork is not a child of Food +auto b = world.entity().add(Eats, Fork); +``` + +
  • +
  • C# + +```cs +// Enforce that target of relationship is child of Food +Entity food = world.Entity(); +Entity eats = world.Entity().Add(Ecs.OneOf, food); +Entity apples = world.Entity().ChildOf(food); +Entity fork = world.Entity(); + +// This is ok, Apples is a child of Food +Entity a = world.Entity().Add(eats, apples); + +// This is not ok, Fork is not a child of Food +Entity b = world.Entity().Add(eats, fork); +``` + +
  • +
  • Rust + +```rust +// Enforce that target of relationship is child of Food +let food = world.entity(); +let eats = world.entity().add_first::(food); +let apples = world.entity().child_of_id(food); +let fork = world.entity(); + +// This is ok, Apples is a child of Food +let a = world.entity().add_id((eats, apples)); + +// This is not ok, Fork is not a child of Food +let b = world.entity().add_id((eats, fork)); +``` + +
  • +
+
diff --git a/vendors/flecs/docs/DesignWithFlecs.md b/vendors/flecs/docs/DesignWithFlecs.md index 406ca86d4..546f51633 100644 --- a/vendors/flecs/docs/DesignWithFlecs.md +++ b/vendors/flecs/docs/DesignWithFlecs.md @@ -17,7 +17,7 @@ Entities can be created and deleted dynamically. When entities are deleted, the ### Entity Names Flecs entities can be named. This makes it easy to identify entities in editors or while debugging, and also allows you to lookup entities by name. Names must be unique inside a scope, which is determined by the `ChildOf` relationship. For example, two entities with the same parent must have different names. -Names can be looked up using a relative path or absolute path using the `ecs_lookup_fullpath` or `entity::lookup` functions. The default scope separator for C applications is a dot (`.`), whereas in C++ it is a double colon (`::`). Lookups use a hashmap internally, which provides O(1) performance. +Names can be looked up using a relative path or absolute path using the `ecs_lookup` or `entity::lookup` functions. The default scope separator for C applications is a dot (`.`), whereas in C++ it is a double colon (`::`). Lookups use a hashmap internally, which provides O(1) performance. Entities can be assigned user friendly names with the doc addon, using the `ecs_doc_set_name` or `entity::set_doc_name` functions. Names assigned by the doc framework do not have to be unique. @@ -44,16 +44,16 @@ There is a misconception that ECS components can only be plain data types, and s Queries are the primary method in Flecs for finding the entities for a set of components (or more specifically: a component expression). Queries are easy to use, but there a few things to keep in mind. ### Use the right query -Flecs has cached queries and uncached queries. Cached queries (`ecs_query_t` and `flecs::query`) are expensive to create but very cheap to iterate. Uncached queries (`ecs_filter_t`, `flecs::filter`) are fast to create, but more expensive to iterate. If you need to do a quick ad-hoc query for which you couldn't know in advance what you had to query for, an uncached query is the best option. If you have a query that you know in advance and need to iterate many times, a cached query is preferred. +Flecs has cached queries and uncached queries. Cached queries (`ecs_query_cache_t` and `flecs::query`) are expensive to create but very cheap to iterate. Uncached queries (`ecs_query_t`, `flecs::query`) are fast to create, but more expensive to iterate. If you need to do a quick ad-hoc query for which you couldn't know in advance what you had to query for, an uncached query is the best option. If you have a query that you know in advance and need to iterate many times, a cached query is preferred. Another difference is that uncached queries can be created from systems, while cached queries cannot. If you need a cached query in a system, it has to be created in advance and passed into the system, either by setting it as system context, adding a component to the system with the query, or passing it in the lambda capture list (C++ only). Systems themselves use cached queries. -Make sure to not repeatedly create and destroy cached queries! For more information, see [the query manual](Queries.md#types) for more details. +Make sure to not repeatedly create and destroy cached queries! For more information, see [the query manual](https://www.flecs.dev/flecs/md_docs_2Queries.html#performance-and-caching) for more details. ### Use in/inout/out annotations Flecs analyzes how components are read and written by queries and uses this for things like change tracking and deciding when to merge command buffers. By default components are marked as `inout`. If a system only reads a component, make sure to mark it as `in`, as this can reduce the time spent by other parts of the application that rely on change detection and sync points. -For more information, see [the query manual](Queries.md#access-modifiers). +For more information, see [the query manual](https://www.flecs.dev/flecs/md_docs_2Queries.html#access-modifiers). ### Annotations You can further annotate queries with components that are not matched with the query, but that are written using ECS operations (like add, remove, set etc.). Such operations are automatically deferred and merged at the end of the frame. With annotations you can enforce merging at an earlier point, by specifying that a component modification has been queued. When Flecs sees this, it will merge back the modifications before the next read. @@ -83,7 +83,7 @@ The ordering information consists out of a phase (see phases and pipelines) and On the other hand, if you are working with an existing framework or engine, you may not have the luxury of scheduling everything yourself. The engine may for example provide you with callbacks in which you need to do certain logic. Maybe you want to build your own threading system. In those situations it can make sense to take control of running systems yourself. -Sometimes you may even not use systems at all, and just run queries. In this case you may want to disable the system addon (see the addsons section in the README). Note that some Flecs features depend on systems, like the REST API and timers. +Sometimes you may even not use systems at all, and just run queries. In this case you may want to disable the system addon (see the addons section in the README). Note that some Flecs features depend on systems, like the REST API and timers. ## Phases and Pipelines Phases and pipelines are the primitives that Flecs uses to order systems. A pipeline is a set of ordered phases. Systems can be assigned to those phases. When using phases and pipelines correctly, it allows you to write plug & play systems that are easy to reuse in different projects. @@ -108,16 +108,16 @@ There are some conventions around the builtin phases, and following them helps t This phase contains all the systems that load data into your ECS. This would be a good place to load keyboard and mouse inputs. ### PostLoad -Often the imported data needs to be processed. Maybe you want to associate your keypresses with high level actions rather than comparing explicitly in your game code if the user pressed the 'K' key. The `PostLoad` phase is a good place for this. +Often the imported data needs to be processed. Maybe you want to associate your key presses with high level actions rather than comparing explicitly in your game code if the user pressed the 'K' key. The `PostLoad` phase is a good place for this. ### PreUpdate Now that the input is loaded and processed, it's time to get ready to start processing our game logic. Anything that needs to happen after input processing but before processing the game logic can happen here. This can be a good place to prepare the frame, maybe clean up some things from the previous frame, etcetera. ### OnUpdate -This is usually where the magic happens! This is where you put all of your gameplay systems. By default systems are added to this phase. +This is usually where the magic happens! This is where you put all of your game play systems. By default systems are added to this phase. ### OnValidate -This phase was introduced to deal with validating the state of the game after processing the gameplay systems. Sometimes you moved entities too close to each other, or the speed of an entity is increased too much. This phase is for righting that wrong. A typical feature to implement in this phase would be collision detection. +This phase was introduced to deal with validating the state of the game after processing the game play systems. Sometimes you moved entities too close to each other, or the speed of an entity is increased too much. This phase is for righting that wrong. A typical feature to implement in this phase would be collision detection. ### PostUpdate When your game logic has been updated, and your validation pass has ran, you may want to apply some corrections. For example, if your collision detection system detected collisions in the `OnValidate` phase, you may want to move the entities so that they no longer overlap. @@ -144,7 +144,7 @@ An application can add phases to the existing list, or define a pipeline from sc Large applications can often contain many components and systems. Some large commercial projects have reported up to 800 components! Managing all those components and systems becomes important on that scale, and that is what modules are for. Modules are one of those features that you usually don't think about when selecting an ECS, but they can make your life a lot easier. ### Defining Modules -The purpose of modules is really to enable reusability. A well written module can be imported into any project, and will do its thing without any tweaking or tinkering. To achieve this, make sure to define your modules around features. Features seldomly consist out of a single system or component, but can have many. Examples are rendering, collision detection, input management, and so on. +The purpose of modules is really to enable reusability. A well written module can be imported into any project, and will do its thing without any tweaking or tinkering. To achieve this, make sure to define your modules around features. Features seldom consist out of a single system or component, but can have many. Examples are rendering, collision detection, input management, and so on. ### Module Dependencies and Ordering Modules can depend on each other. In fact, often do! Importing a module twice has no penalties in Flecs, it will not define your systems and components twice. This enables your application code to import the modules it needs, without having to worry about whether they were already loaded. @@ -172,4 +172,4 @@ In many cases you might want to use your own relationships. Here are a few signs - You are looking to group entities by something like world cells or layers, and want to be able to lookup all entities for a cell. - You're adding an enumeration type as component, but want to query for enumeration constants. -See the [relationships blog](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c) and [relationships manual](Relationships.md) for more information. +See the [relationships blog](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c) and [relationships manual](https://www.flecs.dev/flecs/md_docs_2Relationships.html) for more information. diff --git a/vendors/flecs/docs/Docs.md b/vendors/flecs/docs/Docs.md index 85a2f5e17..e53e734bd 100644 --- a/vendors/flecs/docs/Docs.md +++ b/vendors/flecs/docs/Docs.md @@ -1,37 +1,70 @@ # Documentation ## Getting Started -- [FAQ](/flecs/md_docs_FAQ.html) -- [Quickstart](/flecs/md_docs_Quickstart.html) -- [Flecs Script Tutorial](/flecs/md_docs_FlecsScriptTutorial.html) -- [Designing with Flecs](/flecs/md_docs_DesignWithFlecs.html) +- [FAQ](FAQ.md) +- [Quickstart](Quickstart.md) +- [Designing with Flecs](DesignWithFlecs.md) - [Getting Started with Unreal Engine](https://github.com/PreyK/Unreal-Minimum-Viable-Flecs) +- [Getting Started with Godot](https://github.com/paulfigiel/godot-flecs-sample) +- [Getting Started with Raylib](https://github.com/kranzky/raylib-flecs-starter-kit) ## Manuals -- [Manual](/flecs/md_docs_Manual.html) -- [Query Manual](/flecs/md_docs_Queries.html) -- [Systems Manual](/flecs/md_docs_Systems.html) -- [Relationships Manual](/flecs/md_docs_Relationships.html) -- [JSON Format Manual](/flecs/md_docs_JsonFormat.html) -- [REST API Manual](/flecs/md_docs_RestApi.html) +- [Manual](Manual.md) +- [Entities and Components Manual](EntitiesComponents.md) +- [Query Manual](Queries.md) +- [Query Language Manual](FlecsQueryLanguage.md) +- [Systems Manual](Systems.md) +- [Observers Manual](ObserversManual.md) +- [Prefabs Manual](PrefabsManual.md) +- [Component Traits Manual](ComponentTraits.md) +- [Relationships Manual](Relationships.md) +- [Flecs Script Manual](FlecsScript.md) +- [Flecs Remote API Manual](FlecsRemoteApi.md) +- [v3 Migration Guide](MigrationGuide.md) ## API reference - - [C API](/flecs/group__c.html) - - [C++ API](/flecs/group__cpp.html) + - [C API](@ref c) + - [C++ API](@ref cpp) ## Examples - [C examples](https://github.com/SanderMertens/flecs/tree/master/examples/c) - [C++ examples](https://github.com/SanderMertens/flecs/tree/master/examples/cpp) - +- [C# examples](https://github.com/BeanCheeseBurrito/Flecs.NET/tree/main/src/Flecs.NET.Examples) +- [Rust examples](https://github.com/Indra-db/Flecs-Rust/tree/main/flecs_ecs/examples/flecs) + ## Demos +- [Playground (Flecs Script)](https://www.flecs.dev/explorer/?host=flecs_explorer.wasm) - [Tower Defense (C++)](https://github.com/SanderMertens/tower_defense) - [City (C)](https://github.com/flecs-hub/city) ## Articles - [Where are my entities and components](https://ajmmertens.medium.com/building-an-ecs-1-where-are-my-entities-and-components-63d07c7da742) - [Archetypes and vectorization](https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9) +- [Storage in pictures](https://ajmmertens.medium.com/building-an-ecs-storage-in-pictures-642b8bfd6e04) - [Making the most of entity identifiers](https://ajmmertens.medium.com/doing-a-lot-with-a-little-ecs-identifiers-25a72bd2647) - [Building games in ECS with entity relationships](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c) +- [Why it is time to start thinking of games as databases](https://ajmmertens.medium.com/why-it-is-time-to-start-thinking-of-games-as-databases-e7971da33ac3) +- [A Roadmap to ECS relationships](https://ajmmertens.medium.com/a-roadmap-to-entity-relationships-5b1d11ebb4eb) - [Why storing state machines in ECS is a bad idea](https://ajmmertens.medium.com/why-storing-state-machines-in-ecs-is-a-bad-idea-742de7a18e59) - [Why vanilla ECS is not enough](https://ajmmertens.medium.com/why-vanilla-ecs-is-not-enough-d7ed4e3bebe5) - [ECS: From tool to paradigm](https://ajmmertens.medium.com/ecs-from-tool-to-paradigm-350587cdf216) + +## Resources + +### Resources provided by the community +Some of these are quite old, and might contain information that is out of date! + +- [Extermination Shock Source](https://github.com/Wizard-Of-Chaos/ExShock_code) +- [Bgfx/Imgui module](https://github.com/flecs-hub/flecs-systems-bgfx/tree/bgfx_imgui) +- [Tower defense example](https://gist.github.com/oldmanauz/b4ced44737bf9d248233538fa06a989e) +- [Unreal + Flecs example](https://github.com/PreyK/Unreal-Minimum-Viable-Flecs) +- [Building a space battle with Flecs in UE4](https://twitter.com/ajmmertens/status/1361070033334456320) +- [Flecs + SDL + Web ASM example](https://github.com/HeatXD/flecs_web_demo) ([live demo](https://heatxd.github.io/flecs_web_demo/)) +- [Flecs + Raylib example](https://github.com/Lexxicon/FlecsRaylib) +- [Flecs + gunslinger example](https://github.com/MrFrenik/gs_examples/blob/main/ex_demos/flecs/source/main.c) + +### Flecs around the web +- [Discord](https://discord.gg/BEzP5Rgrrp) +- [Medium](https://ajmmertens.medium.com) +- [Twitter](https://twitter.com/ajmmertens) +- [Reddit](https://www.reddit.com/r/flecs) diff --git a/vendors/flecs/docs/EntitiesComponents.md b/vendors/flecs/docs/EntitiesComponents.md new file mode 100644 index 000000000..5f175eaca --- /dev/null +++ b/vendors/flecs/docs/EntitiesComponents.md @@ -0,0 +1,1971 @@ +# Entities and Components + +## Introduction +Entities and components are the main building blocks of any ECS application. This manual describes the behavior of entities and components in Flecs. + +## Entities +Entities are uniquely identifiable objects in a game or simulation. In a real time strategy game, there may be entities for the different units, buildings, UI elements and particle effects, but also for example the camera, world and player. + +By itself, an entity is just a unique identifier that does not contain any state. You cannot tell from the entity's type what kind of entity it is, as the type is just "entity". + +In Flecs, an entity is represented by a 64 bit integer, which is also how it is exposed in the C API: + +```c +typedef uint64_t ecs_entity_t; +``` + +The first 32 bits of the entity are the "identifying" part: it is the number that uniquely identifies an object in the game. The remaining 32 bits are used by Flecs for liveliness tracking and various other purposes. [This article](https://ajmmertens.medium.com/doing-a-lot-with-a-little-ecs-identifiers-25a72bd2647) has more details on the exact bit representation of entities. + +Zero ("`0`") is the value that is reserved for invalid ids. Functions that return an entity id may return `0` to indicate that the function failed. `0` can also be used to indicate the absence of an entity, for example when a lookup by name didn't find anything. + +### Creation +The following example shows how to create a new entity without any components: + +
+
    +
  • C + +```c +ecs_entity_t my_entity = ecs_new(world); +``` + +
  • +
  • C++ + +```cpp +flecs::entity my_entity = world.entity(); +``` + +
  • +
  • C# + +```cs +Entity myEntity world.Entity(); +``` + +
  • +
  • Rust + +```rust +let my_entity = world.entity(); +``` +
  • +
+
+ +Empty entities don't have any components, and are therefore not matched by any queries or systems. They won't show up in the tree view of the [explorer](https://flecs.dev/explorer), as it is query based. + +The id of the first returned entity is not `1`, but likely a much higher number like `500`. The reason for this is that Flecs creates a bunch of builtin entities, and reserves some of the id space for components (more on that later). + +### Deletion +The following examples show how to delete entities: + +
+
    +
  • C + +```c +ecs_delete(world, my_entity); +``` + +
  • +
  • C++ + +```cpp +my_entity.destruct(); +``` + +
  • +
  • C# + +```cs +myEntity.Destruct(); +``` + +
  • +
  • Rust + +```rust +my_entity.destruct(); +``` +
  • +
+
+ +After an entity is deleted, it can no longer be used with most ECS operations. The deleted entity will be made available for reuse, so that the next time a new entity is created the deleted id can be recycled. + +Whenever an id is recycled, Flecs increases a "version" counter in the upper 32 bits of the entity identifier. This can look strange, as it makes recycled entity ids large, often larger than 4 billion!. It is however 100% expected behavior, and happens as soon after the first entity is deleted. + +The reason this happens is so that the Flecs API can tell whether an entity id is alive or not. Consider the following example, where "v" denotes the version part of the entity id: + +
+
    +
  • C + +```c +ecs_entity_t e1 = ecs_new(world); // Returns 500v0 +ecs_delete(world, e1); // Recycle 500 + +ecs_entity_t e2 = ecs_new(world); // Returns 500v1 + +ecs_add(world, e1, Npc); // Fails, 500v0 is not alive +ecs_add(world, e2, Npc); // OK, 500v1 is alive +``` + +
  • +
  • C++ + +```cpp +flecs::entity e1 = world.entity(); // Returns 500v0 +e1.destruct(); // Recycles 500 + +flecs::entity e2 = world.entity(); // Returns 500v1 + +e1.add(); // Fails, 500v0 is not alive +e2.add(); // OK, 500v1 is alive +``` + +
  • +
  • C# + +```cs +Entity e1 = world.Entity(); // Returns 500v0 +e1.Destruct(); // Recycles 500 + +Entity e2 = world.Entity(); // Returns 500v1 + +e1.Add(); // Fails, 500v0 is not alive +e2.Add(); // OK, 500v1 is alive +``` + +
  • +
  • Rust + +```rust +let e1 = world.entity(); // Returns 500v0 +e1.destruct(); // Recycles 500 +let e2 = world.entity(); // Returns 500v1 +// Fails, 500v0 is not alive +e1.add::(); +// OK, 500v1 is alive +e2.add::(); +``` +
  • +
+
+ +It is valid to delete an already deleted entity: + +
+
    +
  • C + +```c +ecs_entity_t e1 = ecs_new(world); +ecs_delete(world, e1); +ecs_delete(world, e1); // OK: post condition is satisfied +``` + +
  • +
  • C++ + +```cpp +flecs::entity e1 = world.entity(); +e1.destruct(); +e1.destruct(); // OK: post condition is satisfied +``` + +
  • +
  • C# + +```cs +Entity e1 = world.Entity(); +e1.Destruct(); +e1.Destruct(); // OK: post condition is satisfied +``` + +
  • +
  • Rust + +```rust +let e1 = world.entity(); +e1.destruct(); +e1.destruct(); // OK: post condition is satisfied +``` +
  • +
+
+ +### Clearing +An entity can be cleared, which means all components are removed without making the entity not alive. Clearing an entity is more efficient than removing components one by one. An example: + +
+
    +
  • C + +```c +ecs_clear(world, my_entity); +``` + +
  • +
  • C++ + +```cpp +my_entity.clear(); +``` + +
  • +
  • C# + +```cs +myEntity.Clear(); +``` + +
  • +
  • Rust + +```rust +my_entity.clear(); +``` +
  • +
+
+ +### Liveliness Checking +Applications can use the `is_alive` operation to test if an entity is alive or not. The entity passed to `is_alive` must be a valid entity, passing `0` will throw an error. An example: + +
+
    +
  • C + +```c +ecs_entity_t e1 = ecs_new(world); +ecs_entity_t e2 = ecs_new(world); +ecs_delete(world, e1); + +ecs_is_alive(world, e1); // False +ecs_is_alive(world, e2); // True +``` + +
  • +
  • C++ + +```cpp +flecs::entity e1 = world.entity(); +flecs::entity e2 = world.entity(); +e1.destruct(); + +e1.is_alive(); // False +e2.is_alive(); // True +``` + +
  • +
  • C# + +```cs +Entity e1 = world.Entity(); +Entity e2 = world.Entity(); +e1.Destruct(); + +e1.IsAlive(); // False +e2.IsAlive(); // True +``` + +
  • +
  • Rust + +```rust +let e1 = world.entity(); +let e2 = world.entity(); +e1.destruct(); +e1.is_alive(); // False +e2.is_alive(); // True +``` +
  • +
+
+ +The API also provides an `is_valid` operation. Whereas `is_alive` tests if an entity is alive, `is_valid` tests if the entity can be used with operations, and may be used with invalid (`0`) entity ids. An example: + +
+
    +
  • C + +```c +ecs_entity_t e1 = ecs_new(world); +ecs_entity_t e2 = ecs_new(world); +ecs_delete(world, e1); + +ecs_is_valid(world, e1); // False +ecs_is_valid(world, e2); // True +ecs_is_valid(world, 0); // False +``` + +
  • +
  • C++ + +```cpp +flecs::entity e1 = world.entity(); +flecs::entity e2 = world.entity(); +e1.destruct(); + +e1.is_valid(); // False +world.entity(0).is_valid(); // False +``` + +
  • +
  • C# + +```cs +Entity e1 = world.Entity(); +Entity e2 = world.Entity(); +e1.Destruct(); + +e1.IsValid(); // False +e2.IsValid(); // True +world.Entity(0).IsValid(); // False +``` + +
  • +
  • Rust + +```rust +let e1 = world.entity(); +let e2 = world.entity(); +e1.destruct(); +e1.is_valid(); // False +world.entity_from_id(0).is_valid(); // False +``` +
  • +
+
+ +### Manual Ids +Sometimes it can be useful to have direct control over which id an entity assumes. One example of this is to make sure that both sides of a networked application use the same entity id. Applications are allowed to do this, as long as the entity id is not yet in use, and it is made alive. The following example shows how to do this: + +
+
    +
  • C + +```c +ecs_make_alive(world, 1000); // Make id 1000 alive +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.make_alive(1000); +``` + +
  • +
  • C# + +```cs +Entity e = world.MakeAlive(1000); +``` + +
  • +
  • Rust + +```rust +let e = world.make_alive(1000); +``` +
  • +
+
+ +The `make_alive` operation guarantees that the provided entity id will be alive after the operation finishes. The operation will fail if the provided entity id is already in use with a different version. + +### Manual Versioning +Applications can manually override the version of an entity with the `set_version` operation. This can be useful when for example synchronizing networked ids. An example: + +
+
    +
  • C + +```c +ecs_set_version(world, versioned_id); +``` + +
  • +
  • C++ + +```cpp +world.set_version(versioned_id); +``` + +
  • +
  • C# + +```cs +world.SetVersion(versionedId); +``` + +
  • +
  • Rust + +```rust +//TODO does not exist yet +//world.set_version(versioned_id); +``` +
  • +
+
+ +### Ranges +An application can instruct Flecs to issue ids from a specific offset and up to a certain limit with the `ecs_set_entity_range` operation. This example ensures that id generation starts from id 5000: + +
+
    +
  • C + +```c +ecs_set_entity_range(world, 5000, 0); +``` + +
  • +
  • C++ + +```cpp +world.set_entity_range(5000, 0); +``` + +
  • +
  • C# + +```cs +world.SetEntityRange(5000, 0); +``` + +
  • +
  • Rust + +```rust +world.set_entity_range(5000, 0); +``` +
  • +
+
+ +If the last issued id was higher than 5000, the operation will not cause the last id to be reset to 5000. An application can also specify the highest id that can be generated: + +
+
    +
  • C + +```c +ecs_set_entity_range(world, 5000, 10000); +``` + +
  • +
  • C++ + +```cpp +world.set_entity_range(5000, 10000); +``` + +
  • +
  • C# + +```cs +world.SetEntityRange(5000, 10000); +``` + +
  • +
  • Rust + +```rust +world.set_entity_range(5000, 10000); +``` +
  • +
+
+ +If invoking `ecs_new` would result in an id higher than `10000`, the application would assert. If `0` is provided for the maximum id, no upper bound will be enforced. + +Note that at the moment setting the range _does not_ affect recycled ids. It is therefore possible that `ecs_new` returns an id outside of the specified range if a recycle-able id is available. This is an issue that will be addressed in future versions. + +It is possible for an application to enforce that entity operations are only allowed for the configured range with the `ecs_enable_range_check` operation: + +
+
    +
  • C + +```c +ecs_enable_range_check(world, true); +``` + +
  • +
  • C++ + +```cpp +world.enable_range_check(); +``` + +
  • +
  • C# + +```cs +world.EnableRangeCheck(true); +``` + +
  • +
  • Rust + +```rust +world.enable_range_check(true); +``` +
  • +
+
+ +This can be useful for enforcing simple ownership behavior, where different id ranges are mapped out for different owners (often game clients or servers). + +### Names +Entity can be given names, which allows them to be looked up on the world. An example: + +
+
    +
  • C + +```c +ecs_entity_t e = ecs_entity(world, { .name = "MyEntity" }); + +if (e == ecs_lookup(world, "MyEntity")) { + // true +} + +printf("%s\n", ecs_get_name(world, e)); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.entity("MyEntity"); + +if (e == world.lookup("MyEntity")) { + // true +} + +std::cout << e.name() << std::endl; +``` + +
  • +
  • C# + +```cs +Entity e = world.Entity("MyEntity"); + +if (e == world.Lookup("MyEntity")) { + // true +} + +Console.WriteLine(e.Name()); +``` + +
  • +
  • Rust + +```rust +let e = world.entity_named("MyEntity"); +if e == world.lookup("MyEntity") { + // true +} +println!("{}", e.name()); +``` +
  • +
+
+ +Names are namespaced. They are similar to namespaced types in a programming language, or to files on a file system. If an entity is a child of a parent, a lookup will have to include the name of the parent: + +
+
    +
  • C + +```c +ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); +ecs_entity_t e = ecs_entity(world, { .name = "Child", .parent = p }); + +if (e == ecs_lookup(world, "Parent.Child")) { + // true +} +``` + +
  • +
  • C++ + +```cpp +flecs::entity p = world.entity("Parent"); +flecs::entity e = world.entity("Child").child_of(p); + +if (e == world.lookup("Parent::Child")) { + // true +} +``` + +
  • +
  • C# + +```cs +Entity p = world.Entity("Parent"); +Entity e = world.Entity("Child").ChildOf(p); + +if (e == world.Lookup("Parent.Child")) { + // true +} +``` + +
  • +
  • Rust + +```rust +let p = world.entity_named("Parent"); +let e = world.entity_named("Child").child_of_id(p); +if e == world.lookup("Parent::Child") { + // true +} +``` +
  • +
+
+ +Lookups can be relative to an entity. The following example shows how to lookup an entity relative to a parent: + +
+
    +
  • C + +```c +ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); +ecs_entity_t e = ecs_entity(world, { .name = "Child", .parent = p }); + +if (e == ecs_lookup_from(world, p, "Child")) { + // true +} +``` + +
  • +
  • C++ + +```cpp +flecs::entity p = world.entity("Parent"); +flecs::entity e = world.entity("Child").child_of(p); + +if (e == p.lookup("Child")) { + // true +} +``` + +
  • +
  • C# + +```cs +Entity p = world.Entity("Parent"); +Entity e = world.Entity("Child").ChildOf(p); + +if (e == p.Lookup("Child")) { + // true +} +``` + +
  • +
  • Rust + +```rust +let p = world.entity_named("Parent"); +let e = world.entity_named("Child").child_of_id(p); +if e == p.lookup("Child") { + // true +} +``` +
  • +
+
+ +An application can request the name and path of an entity. The name is the direct name given to an entity, without the names of the parent. The path is the name and names of the entity's parent, separated by a separator. If an entity has no parents, the name and the path are the same. An example: + +
+
    +
  • C + +```c +ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); +ecs_entity_t e = ecs_entity(world, { .name = "Child", .parent = p }); + +// Returns name, result does not need to be freed +const char *name = ecs_get_name(world, e); +printf("%s\n", name); // Child + +// Returns path, result must be freed +char *path = ecs_get_path(world, e); +printf("%s\n", path) // Parent.Child +ecs_os_free(path); +``` + +
  • +
  • C++ + +```cpp +flecs::entity p = world.entity("Parent"); +flecs::entity e = world.entity("Child").child_of(p); + +// Returns entity name, does not allocate +std::cout << e.name() << std::endl; // Child + +// Returns entity path, does allocate +std::cout << e.path() << std::endl; // Parent.Child +``` + +
  • +
  • C# + +```cs +Entity p = world.Entity("Parent"); +Entity e = world.Entity("Child").ChildOf(p); + +// Returns entity name +Console.WriteLine(e.Name()); // Child + +// Returns entity path +Console.WriteLine(e.Path()); // Parent.Child +``` + +
  • +
  • Rust + +```rust +let p = world.entity_named("Parent"); +let e = world.entity_named("Child").child_of_id(p); +// Returns entity name, does not allocate +println!("{}", e.name()); // Child +// Returns entity path, does allocate +println!("{}", e.path().unwrap()); // Parent.Child +``` +
  • +
+
+ +Names must be unique. There can only be one entity with a specific name in the scope of a parent. Entities can be annotated with a doc name, which does not need to be unique. See the doc addon for more details. + +When the name for an existing entity is used during the creation of a named entity, the existing entity is returned. An example: + +
+
    +
  • C + +```c +ecs_entity_t e1 = ecs_entity(world, { .name = "Parent.Child" }); +ecs_entity_t e2 = ecs_entity(world, { .name = "Parent.Child" }); + +if (e1 == e2) { + // true +} +``` + +
  • +
  • C++ + +```cpp +flecs::entity e1 = world.entity("Parent::Child"); +flecs::entity e2 = world.entity("Parent::Child"); + +if (e1 == e2) { + // true +} +``` + +
  • +
  • C# + +```cs +Entity e1 = world.Entity("Parent.Child"); +Entity e2 = world.Entity("Parent.Child"); + +if (e1 == e2) { + // true +} +``` + +
  • +
  • Rust + +```rust +let e1 = world.entity_named("Parent::Child"); +let e2 = world.entity_named("Parent::Child"); +if e1 == e2 { + // true +} +``` +
  • +
+
+ +Entity names can be changed after the entity is created, as long as the specified name is unique (e.g. there isn't a sibling entity with the same name). An example: + +
+
    +
  • C + +```c +ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + +// Change name +ecs_set_name(world, e, "Bar"); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.entity("Foo"); + +// Change name +e.set_name("Bar"); +``` + +
  • +
  • C# + +```cs +Entity e = world.Entity("Foo"); + +e.SetName("Bar"); +``` + +
  • +
  • Rust + +```rust +let e = world.entity_named("Foo"); +// Change name +e.set_name("Bar"); +``` +
  • +
+
+ +Entity names can be plain numbers: + +
+
    +
  • C + +```c +ecs_entity_t ten = ecs_entity(world, { .name = "10" }); +ecs_entity_t twenty = ecs_entity(world, { .name = "20" }); +``` + +
  • +
  • C++ + +```cpp +flecs::entity ten = world.entity("10"); +flecs::entity twenty = world.entity("20"); +``` + +
  • +
  • C# + +```cs +Entity ten = world.Entity("10"); +Entity twenty = world.Entity("20"); +``` + +
  • +
  • Rust + +```rust +let ten = world.entity_named("10"); +let twenty = world.entity_named("20"); +``` +
  • +
+
+ +Entity names can be used to refer directly to an entity id by prefixing them with a `#`. For example, looking up `#1` will return the entity with id 1. This allows applications that work with names to treat anonymous entities the same as named entities. + +### Disabling +Entities can be disabled which prevents them from being matched with queries. This can be used to temporarily turn off a feature in a scene during game play. It is also used for Flecs systems, which can be disabled to temporarily remove them from a schedule. The following example shows how to disable and reenable an entity: + +
+
    +
  • C + +```c +ecs_entity_t e = ecs_new(world); + +// Enable entity +ecs_enable(world, e, true); + +// Disable entity +ecs_enable(world, e, false); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.entity(); + +// Enable entity +e.enable(); + +// Disable entity +e.disable(); +``` + +
  • +
  • C# + +```cs +Entity e = world.Entity(); + +// Enable entity +e.Enable(); + +// Disable entity +e.Disable(); +``` + +
  • +
  • Rust + +```rust +let e = world.entity(); +// Enable entity +e.enable_self(); +// Disable entity +e.disable_self(); +``` +
  • +
+
+ +Entity disabling can be combined with prefabs to create lists of entities that can be disabled with a single operation. The following example shows how to disable three entities with a single operation: + +
+
    +
  • C + +```c +// Three entities to disable +ecs_entity_t e1 = ecs_new(world); +ecs_entity_t e2 = ecs_new(world); +ecs_entity_t e3 = ecs_new(world); + +// Create prefab that has the three entities +ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); +ecs_add_id(world, p, e1); +ecs_add_id(world, p, e2); +ecs_add_id(world, p, e3); + +// Disable entities +ecs_enable(world, p, false); + +// Enable entities +ecs_enable(world, p, true); +``` + +
  • +
  • C++ + +```cpp +// Three entities to disable +flecs::entity e1 = world.entity(); +flecs::entity e2 = world.entity(); +flecs::entity e3 = world.entity(); + +// Create prefab that has the three entities +flecs::entity p = world.prefab(); +p.add(e1); +p.add(e2); +p.add(e3); + +// Disable entities +p.disable(); + +// Enable entities +p.enable(); +``` + +
  • +
  • C# + +```cs +// Three entities to disable +Entity e1 = world.Entity(); +Entity e2 = world.Entity(); +Entity e3 = world.Entity(); + +// Create prefab that has the three entities +Entity p = world.Prefab(); +p.Add(e1); +p.Add(e2); +p.Add(e3); + +// Disable entities +p.Disable(); + +// Enable entities +p.Enable(); +``` + +
  • +
  • Rust + +```rust +// Three entities to disable +let e1 = world.entity(); +let e2 = world.entity(); +let e3 = world.entity(); + +// Create prefab that has the three entities +let p = world.prefab(); +p.add_id(e1); +p.add_id(e2); +p.add_id(e3); + +// Disable entities +p.disable_self(); + +// Enable entities +p.enable_self(); +``` +
  • +
+
+ +This also works with prefab hierarchies, as shown in the following example: + +
+
    +
  • C + +```c +// Three entities to disable +ecs_entity_t e1 = ecs_new(world); +ecs_entity_t e2 = ecs_new(world); +ecs_entity_t e3 = ecs_new(world); + +// Create prefab hierarchy with the three entities +ecs_entity_t p1 = ecs_new_w_id(world, EcsPrefab); +ecs_add_id(world, p1, e1); + +ecs_entity_t p2 = ecs_new_w_id(world, EcsPrefab); +ecs_add_pair(world, p2, EcsIsA, p1); +ecs_add_id(world, p2, e2); +ecs_add_id(world, p2, e3); + +// Disable e1, e2, e3 +ecs_enable(world, p2, false); + +// Enable e1 +ecs_enable(world, p1, true); +``` + +
  • +
  • C++ + +```cpp +// Three entities to disable +flecs::entity e1 = world.entity(); +flecs::entity e2 = world.entity(); +flecs::entity e3 = world.entity(); + +// Create prefab hierarchy with the three entities +flecs::entity p1 = world.prefab() + .add(e1); + +flecs::entity p2 = world.prefab() + .is_a(p1) + .add(e2) + .add(e3); + +// Disable e1, e2, e3 +p2.disable(); + +// Enable e1 +p1.enable(); +``` + +
  • +
  • C# + +```cs +// Three entities to disable +Entity e1 = world.Entity(); +Entity e2 = world.Entity(); +Entity e3 = world.Entity(); + +// Create prefab hierarchy with the three entities +Entity p1 = world.Prefab() + .Add(e1); + +Entity p2 = world.Prefab() + .IsA(p1) + .Add(e2) + .Add(e3); + +// Disable e1, e2, e3 +p2.Disable(); + +// Enable e1 +p1.Enable(); +``` + +
  • +
  • Rust + +```rust +// Three entities to disable +let e1 = world.entity(); +let e2 = world.entity(); +let e3 = world.entity(); + +// Create prefab hierarchy with the three entities +let p1 = world.prefab().add_id(e1); +let p2 = world.prefab().is_a_id(p1).add_id(e2).add_id(e3); + +// Disable e1, e2, e3 +p2.disable_self(); + +// Enable e1 +p1.enable_self(); +``` +
  • +
+
+ +Entity disabling works by adding a `Disabled` tag to the entity, which can also be added manually with regular add/remove operations. An example: + +
+
    +
  • C + +```c +ecs_add_id(world, e, EcsDisabled); +``` + +
  • +
  • C++ + +```cpp +e.add(flecs::Disabled); +``` + +
  • +
  • C# + +```cs +e.Add(Ecs.Disabled); +``` + +
  • +
  • Rust + +```rust +e.add::(); +``` +
  • +
+
+ + + +## Components +A component is something that is added to an entity. Components can simply tag an entity ("this entity is an `Npc`"), attach data to an entity ("this entity is at `Position` `{10, 20}`") and create relationships between entities ("bob `Likes` alice") that may also contain data ("bob `Eats` `{10}` apples"). + +To disambiguate between the different kinds of components in Flecs, they are named separately in Flecs: + +| Name | Has Data | Is Pair | +|--------------|----------|--------------| +| Tag | No | No | +| Component | Yes | No | +| Relationship | No | Yes | +| Relationship component | Yes | Yes | + +Here, "has data" indicates whether a component attaches data to the entity. This means that in addition to asking whether an entity has a component, we can also ask what the _value_ of a component is. + +A pair is a component that's composed out of two elements, such as "Likes, alice" or "Eats, apples". See the [Relationship manual](Relationships.md) for more details. + +### Operations +The following table provides the base set of operations that Flecs offers for components: + +| Operation | Description | +|-----------|-------------| +| `add` | Adds component to entity. If entity already has the component, `add` does nothing. Requires that the component is default constructible. | +| `remove` | Removes component from entity. If entity doesn't have the component, `remove` does nothing. | +| `get` | Returns a immutable reference to the component. If the entity doesn't have the component, `get` returns `nullptr`. | +| `get_mut` | Returns a mutable reference to the component. If the entity doesn't have the component, `get_mut` returns `nullptr`. | +| `ensure` | Returns a mutable reference to the component. `ensure` behaves as a combination of `add` and `get_mut`. | +| `emplace` | Returns a mutable reference to the component. If the entity doesn't have the component, `emplace` returns a reference to unconstructed memory. This enables adding components that are not default constructible. | +| `modified` | Emits a modified event for a component. This ensures that `OnSet` observers and `on_set` hooks are invoked, and updates change detection administration. | +| `set` | Sets the value of a component. `set` behaves as a combination of `ensure` and `modified`. `set` does not take ownership of the provided value. | + +The following component lifecycle diagram shows how the different operations mutate the storage and cause hooks and observers to be invoked: + +![Component Lifecycle](img/component_lifecycle_flow.png) + +### Components are Entities +In an ECS framework, components need to be uniquely identified. In Flecs this is done by making each component is its own unique entity. If an application has a component `Position` and `Velocity`, there will be two entities, one for each component. Component entities can be distinguished from "regular" entities as they have a `Component` component. An example: + +
+
    +
  • C + +```c +// Register component (see below) +ECS_COMPONENT(world, Position); + +// The ecs_id() macro can be used in C to obtain the component id from a type +ecs_entity_t pos = ecs_id(Position); + +// Component entities have the Component component +const EcsComponent *comp_data = ecs_get(world, pos, EcsComponent); + +printf("{size: %d, alignment: %d}\n", + comp_data->size, + comp_data->alignment); +``` + +
  • +
  • C++ + +```cpp +// Get the entity for the Position component +flecs::entity pos = world.component(); + +// Component entities have the Component component +const flecs::Component *comp_data = pos.get(); + +std::cout << "{size: " << comp_data->size << ", " + << comp_data->alignment << "}\n"; +``` + +
  • +
  • C# + +```cs +// Get the entity for the Position component +Entity pos = world.Component(); + +// Component entities have the Component component +ref readonly flecs.EcsComponent compData = ref e.Get(); + +Console.WriteLine($"Size: {compData.size}, Alignment: {compData.alignment}"); +``` + +
  • +
  • Rust + +```rust +// Get the entity for the Position component +let pos = world.component::(); +// Component entities have the Component component +pos.get::<&flecs::Component>(|comp_data| { + println!( + "size: {}, alignment: {}", + comp_data.size, comp_data.alignment + ); +}); +``` +
  • +
+
+ +All of the APIs that apply to regular entities also apply to component entities. Many Flecs features leverage this. It is for example possible to customize component behavior by adding tags to components. The following example shows how a component can be customized to use sparse storage by adding a `Sparse` tag to the component entity: + +
+
    +
  • C + +```c +// Register a sparse component +ECS_COMPONENT(world, Position); + +ecs_add_id(world, ecs_id(Position), EcsSparse); +``` + +
  • +
  • C++ + +```cpp +// Register a sparse component +world.component().add(flecs::Sparse); +``` + +
  • +
  • C# + +```cs +// Register a sparse component +world.Component().Entity.add(Ecs.Sparse); +``` + +
  • +
  • Rust + +```rust +// Register a sparse component +world.component::().add_trait::(); +``` +
  • +
+
+ +These kinds of tags are called "traits". To see which kinds of traits you can add to components to customize their behavior, see the [component traits manual](ComponentTraits.md). + +### Registration +Components must be registered before they can be used. The following sections describe how component registration works for the different language bindings. + +
+
    +
  • C + +In C, the easiest way to register a component is with the `ECS_COMPONENT` macro: + +```c +ECS_COMPONENT(world, Position); + +ecs_entity_t e = ecs_new_w(world, Position); // OK +``` + +However, if you try to use the component from another function or across files, you will find that this doesn't work. To fix this, the component has to be forward declared. The following example shows how to forward declare a component that is used from multiple C files: + +```c +// position.h +extern ECS_COMPONENT_DECLARE(Position); +``` + +```c +// position.c +#include "position.h" + +ECS_COMPONENT_DECLARE(Position); + +void register_components(ecs_world_t *world) { + ECS_COMPONENT_DEFINE(world, Position); +} +``` + +```c +// something.c +#include "position.h" + +void do_something(ecs_world_t *world) { + ecs_entity_t e = ecs_new_w(world, Position); // OK +} + +int main(int argc, char *argv[]) { + ecs_world_t *world = ecs_init(); + + register_components(world); + + do_something(world); + + ecs_fini(world); +} +``` + +Note that this is just an example, and that typically you would not create a file per component. A more common way to organize components is with modules, which often register multiple components, amongst others. The following code shows an example with a module setup: + +```c +// movement.h +extern ECS_COMPONENT_DECLARE(Position); +extern ECS_COMPONENT_DECLARE(Velocity); + +void MovementImport(ecs_world_t *world); +``` + +```c +// movement.c +#include "movement.h" + +ECS_COMPONENT_DECLARE(Position); +ECS_COMPONENT_DECLARE(Velocity); + +void MovementImport(ecs_world_t *world) { + ECS_MODULE(world, Movement); + + ECS_COMPONENT_DEFINE(world, Position); + ECS_COMPONENT_DEFINE(world, Velocity); +} +``` + +```c +// something.c +#include "movement.h" + +void do_something(ecs_world_t *world) { + ecs_new_w(world, Position); // OK +} + +int main(int argc, char *argv[]) { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, Movement); + + do_something(world); + + ecs_fini(world); +} +``` + +
  • +
  • C++ + +In C++ components are automatically registered upon first usage. The following example shows how: + +```cpp +int main(int argc, char *argv[]) { + flecs::world world; + + flecs::entity e1 = world.entity() + .set(Position{10, 20}) // Position registered here + .set(Velocity{1, 2}); // Velocity registered here + + flecs::entity e1 = world.entity() + .set(Position{10, 20}) // Position already registered + .set(Velocity{1, 2}); // Velocity already registered +} +``` + +Components can be registered in advance, which can be done for several reasons: + +- Makes it easier to see which components are used by an application +- No unexpected registration code that suddenly runs in the middle of a frame +- Component needs to be setup with traits, reflection data, hooks etc. + +To register a component in advance, do: + +```cpp +world.component(); +``` + +In general it is recommended to register components in advance, and to only use automatic registration during prototyping. Applications can enforce manual registration by defining the `FLECS_CPP_NO_AUTO_REGISTRATION` macro at compile time, when building Flecs. This will cause a panic whenever a component is used that was not yet registered. Disabling auto registration also improves performance of the C++ API. + +A convenient way to organize component registration code is to use Flecs modules. An example: + +```cpp +struct movement { + movement(flecs::world& world) { + world.component(); + world.component(); + } +}; + +int main(int argc, char *argv[]) { + flecs::world world; + + world.import(); +} + +``` + +
  • +
  • C# + +In C# components are automatically registered upon first usage. The following example shows how: + +```cs +public static void Main() +{ + using World world = World.Create(); + + Entity e1 = world.Entity() + .Set(new Position(10, 20)) // Position registered here + .Set(new Velocity(1, 2)); // Velocity registered here + + Entity e2 = world.Entity() + .Set(new Position(10, 20)) // Position already registered + .Set(new Velocity(1, 2)); // Velocity already registered +} + +``` + +Components can be registered in advance, which can be done for several reasons: + +- Makes it easier to see which components are used by an application +- No unexpected registration code that suddenly runs in the middle of a frame +- Component needs to be setup with traits, reflection data, hooks etc. + +To register a component in advance, do: + +```cs +world.Component(); +``` + +In general it is recommended to register components in advance, and to only use automatic registration during prototyping. + +A convenient way to organize component registration code is to use Flecs modules. An example: + +```cs +public struct Movement : IFlecsModule +{ + public void InitModule(World world) + { + world.Component(); + world.Component(); + } +} + +public static void Main() +{ + using World world = World.Create(); + + world.Import(); +} +``` + +
  • +
  • Rust + +In Rust components are automatically registered upon first usage. The following example shows how: + +```rust +fn main() { + let world = World::new(); + let e1 = world + .entity() + .set(Position { x: 10.0, y: 20.0 }) // Position registered here + .set(Velocity { x: 1.0, y: 2.0 }); // Velocity registered here + + let e2 = world + .entity() + .set(Position { x: 10.0, y: 20.0 }) // Position already registered + .set(Velocity { x: 1.0, y: 2.0 }); // Velocity already registered +} +``` + +Components can be registered in advance, which can be done for several reasons: + +- Makes it easier to see which components are used by an application +- No unexpected registration code that suddenly runs in the middle of a frame +- Component needs to be setup with traits, reflection data, hooks etc. + +To register a component in advance, do: + +```rust +world.component::(); +``` + +In general it is recommended to register components in advance, and to only use automatic registration during prototyping. Applications can enforce manual registration by activating the rust feature `flecs_manual_registration`. This will cause a panic whenever a component is used that was not yet registered. Disabling auto registration also improves performance of the Rust API. + +A convenient way to organize component registration code is to use Flecs modules. An example: + +```rust + +use flecs_ecs::prelude::*; + +#[derive(Component)] +struct Movement; + +impl Module for Movement { + fn module(world: &World) { + world.module::("Movement"); + // Define components, systems, triggers, ... as usual. They will be + // automatically created inside the scope of the module. + } +} + +let world = World::new(); +world.import::(); +``` + +
  • +
+
+ +#### Runtime Type Registration +In some cases, typically when using scripting, components must be registered for types that do not exist at compile time. In Flecs this is possible by calling the `ecs_component_init` function. This function returns a component entity, which can then be used with regular ECS operations. An example: + +
+
    +
  • C + +```c +// Register component +ecs_entity_t comp = ecs_component(world, { + .type = { + .size = 8, + .alignment = 8 + } +}); + +ecs_entity_t e = ecs_new(world); + +// Add component +ecs_add_id(world, e, comp); + +// Get component +const void *ptr = ecs_get_id(world, e, comp); +``` + +
  • +
  • C++ + +```cpp +ecs_component_desc_t desc = {0}; +desc.type.size = 8; +desc.type.alignment = 8; +flecs::entity_t comp = ecs_component_init(world, &desc); + +flecs::entity e = world.entity(); + +// Add component +e.add(comp); + +// Get component +const void *ptr = e.get(comp); +``` + +
  • +
  • C# + +```cs +TODO +``` + +
  • +
  • Rust + +```rust +TODO +``` + +
  • +
+
+ +Often when registering a component at runtime, it needs to be described with reflection data so it can be serialized & deserialized. See the "reflection/runtime_component" example on how to do this. + +When registering a component, it is good practice to give it a name. This makes it much easier to debug the application with tools like the explorer. To create a component with a name, we can pass a named entity to the `ecs_component_init` function. An example: + +
+
    +
  • C + +```c +// Register named component +ecs_entity_t comp = ecs_component(world, { + .entity = ecs_entity(world, { .name = "MyComponent" }), + .type = { + .size = 8, + .alignment = 8 + } +}); + +ecs_entity_t e = ecs_new(world); + +// Add component +ecs_add_id(world, e, comp); + +// Get component +const void *ptr = ecs_get_id(world, e, comp); +``` + +
  • +
  • C++ + +```cpp +ecs_component_desc_t desc = {0}; +desc.entity = world.entity("MyComponent"); +desc.type.size = 8; +desc.type.alignment = 8; +flecs::entity_t comp = ecs_component_init(world, &desc); + +flecs::entity e = world.entity(); + +// Add component +e.add(comp); + +// Get component +const void *ptr = e.get(comp); +``` + +
  • +
  • C# + +```cs +TODO +``` + +
  • +
  • Rust + +```rust +TODO +``` + +
  • +
+
+ +See the documentation for `ecs_component_desc_t` for more component registration options. + +### Unregistration +A component can be unregistered from a world by deleting its entity. When a component is deleted, by default it will be removed from all entities that have the component. This behavior is customizable, see the "cleanup traits" section in the [component trait manual](ComponentTraits.md). + +The following example shows how to unregister a component: + +
+
    +
  • C + +```c +ECS_COMPONENT(world, Position); + +// Create entity with Position +ecs_entity_t e = ecs_new_w(world, Position); + +// Unregister the component +ecs_delete(world, ecs_id(Position)); + +// Position is removed from e +``` + +
  • +
  • C++ + +```cpp +flecs::entity pos = world.component(); + +// Create entity with Position +flecs::entity e = world.entity().add(); + +// Unregister the component +pos.destruct(); + +// Position is removed from e +``` + +
  • +
  • C# + +```cs +Entity pos = world.Component(); + +// Create entity with Position +Entity e = world.Entity().Add(); + +// Unregister the component +pos.Destruct(); + +// Position is removed from e +``` + +
  • +
  • Rust + +```rust +let pos = world.component::(); + +// Create entity with Position +let e = world.entity().add::(); + +// Unregister the component +pos.destruct(); + +// Position is removed from e +``` + +
  • +
+
+ +### Singletons +Singletons are components for which only a single instance exists on the world. They can be accessed on the world directly and do not require providing an entity. Singletons are useful for global game resources, such as game state, a handle to a physics engine or a network socket. An example: + +
+
    +
  • C + +```c +ECS_COMPONENT(world, TimeOfDay); + +// Set singleton +ecs_singleton_set(world, TimeOfDay, { 0.5 }); + +// Unregister the component +const TimeOfDay *t = ecs_singleton_get(world, TimeOfDay); +``` + +
  • +
  • C++ + +```cpp +// Set singleton +world.set({ 0.5 }); + +// Get singleton +const TimeOfDay *t = world.get(); +``` + +
  • +
  • C# + +```cs +// Set singleton +world.Set(new TimeOfDay(0.5)); + +// Get singleton +ref readonly TimeOfDay t = ref world.Get(); +``` + +
  • +
  • Rust + +```rust +// Set singleton +world.set(TimeOfDay { value: 0.5 }); + +// Get singleton +world.get::<&TimeOfDay>(|time| println!("{}", time.value)); +``` + +
  • +
+
+ +Singletons are implemented as components that are added to themselves. The rationale for this design is that it allows for reusing all of the existing logic implemented for entities and components, as for the storage there is no fundamental difference between a singleton and a regular component. Additionally, it spreads out the storage of singletons across multiple entities which is favorable for performance when compared to a single entity that contains all singleton components. Since the component's entity is known during the singleton lookup, it makes most sense to use this as the entity on which to store the data. + +The following example shows how the singleton APIs are equivalent to using the regular APIs with the component entity: + +
+
    +
  • C + +```c +// Set singleton +ecs_singleton_set(world, TimeOfDay, { 0.5 }); + +// Equivalent to: +ecs_set(world, ecs_id(TimeOfDay), TimeOfDay, {0.5}); +``` + +
  • +
  • C++ + +```cpp +// Set singleton +world.set({ 0.5 }); + +// Equivalent to: +world.component().set(TimeOfDay{ 0.5 }) +``` + +
  • +
  • C# + +```cs +// Set singleton +world.Set(new TimeOfDay(0.5)); + +// Equivalent to: +world.Component().Entity.Set(new TimeOfDay(0.5)); +``` + +
  • +
  • Rust + +```rust +// Set singleton +world.set(TimeOfDay { value: 0.5 }); + +// Equivalent to: +world.component::().set(TimeOfDay { value: 0.5 }); +``` + +
  • +
+
+ +### Disabling +Components can be disabled, which prevents them from being matched by queries. When a component is disabled, it behaves as if the entity doesn't have it. Only components that have the `CanToggle` trait can be disabled (see the [component traits manual](ComponentTraits.md) for more details). The following example shows how to disable a component: + +
+
    +
  • C + +```c +// Register toggle-able component +ECS_COMPONENT(world, Position); +ecs_add_id(world, ecs_id(Position), EcsCanToggle); + +ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + +// Disable component +ecs_enable_component(world, e, Position, false); +ecs_is_enabled(world, e, Position); // False + +// Enable component +ecs_enable_component(world, e, Position, true); +ecs_is_enabled(world, e, Position); // True +``` + +
  • +
  • C++ + +```cpp +// Register toggle-able component +world.component().add(flecs::CanToggle); + +flecs::entity e = world.entity().set(Position{10, 20}); + +// Disable component +e.disable(); +e.is_enabled(); // False + +// Enable component +e.enable(); +e.is_enabled() // True +``` + +
  • +
  • C# + +```cs +ecs.Component().Entity + .Add(Ecs.CanToggle); + +Entity e = world.Entity() + .Set(new(10, 20)); + +// Disable component +e.Disable(); +e.IsEnabled(); // False + +// Enable component +e.Enable(); +e.IsEnabled(); // True +``` + +
  • +
  • Rust + +```rust +// Register toggle-able component +world + .component::() + .add_trait::(); + +let e = world.entity().set(Position { x: 10.0, y: 20.0 }); + +// Disable component +e.disable::(); + +e.is_enabled::(); // False + +// Enable component +e.enable::(); + +e.is_enabled::(); // True +``` +
  • +
+
+ +Disabling a component is a cheaper alternative to removing it from an entity, as it relies on setting a bit vs. moving an entity to another table. Component toggling can also be used to restore a component with its old value. diff --git a/vendors/flecs/docs/FAQ.md b/vendors/flecs/docs/FAQ.md index 69caee25c..42d948746 100644 --- a/vendors/flecs/docs/FAQ.md +++ b/vendors/flecs/docs/FAQ.md @@ -48,7 +48,7 @@ This is likely because queries (`flecs::query`) are created repeatedly in a loop flecs::query q = world.query(); world.system() - .iter([=](flecs::iter& it) { + .run([=](flecs::iter& it) { q.each([&](Position& p) { // ... }); @@ -57,7 +57,7 @@ world.system() ```cpp // BAD world.system() - .iter([](flecs::iter& it) { + .run([](flecs::iter& it) { flecs::query q = world.query(); q.each([&](Position& p) { // ... @@ -111,11 +111,11 @@ Yes it can! See the reflection examples: - https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection ## Why is Flecs so large? -If you look at the size of the flecs.c and flecs.h files you might wonder why they are so large. There are a few reasons: +If you look at the size of the [distr/flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.c) and [distr/flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.c) files you might wonder why they are so large. There are a few reasons: - The files contain a _lot_ of comments and in-code documentation! -- Flecs has a small core with a lot of addons. The flecs.c and flecs.h files are the full source code, including addons. -- The flecs.h file contains the full C++ API. +- Flecs has a small core with a lot of addons. The [distr/flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.c) and [distr/flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.c) files are the full source code, including addons. +- The [distr/flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.c) file contains the full C++ API. - Flecs implements its own data structures like vectors and maps, vs. depending on something like the STL. - C tends to be a bit more verbose than other languages. @@ -123,7 +123,7 @@ Not all addons are useful in any project. You can customize a Flecs build to onl ## Why does the explorer not work? Make sure that: -- The REST API is enabled (see the [REST manual](https://www.flecs.dev/flecs/md_docs_RestApi.html)) +- The REST API is enabled (see the [Remote API manual](FlecsRemoteApi.md)) - You can reach the REST API by testing http://localhost:27750/entity/flecs - You call `ecs_progress`/`world::progress` in your main loop @@ -154,7 +154,7 @@ You can! Systems are an optional addon that can be disabled. You can build appli This is likely because the entity has a parent. A lookup by name requires you to provide the full path to an entity, like: ```c -ecs_lookup_fullpath(world, "parent.child"); +ecs_lookup(world, "parent.child"); ``` or in C++: @@ -165,3 +165,22 @@ world.lookup("parent::child"); ## Can I add or remove components from within a system? You can! By default ECS operations are deferred when called from inside a system, which means that they are added to a queue and processed later when it's safe to do so. Flecs does not have a dedicated command API, if you call an operation from a system it will be automatically added to the command queue! + +## What does a LOCKED_STORAGE error mean? +A LOCKED_STORAGE error means that you're trying to add or remove an entity to an archetype that is currently being accessed, usually during query iteration. An example: + +```cpp +q.each([](flecs::entity e) { + e.destruct(); // LOCKED_STORAGE error: removes entity from table +}); +``` + +Most LOCKED_STORAGE errors can be resolved by putting `defer_begin` and `defer_end` around the iteration, which postpones the table-changing operations until after the iteration: + +```cpp +world.defer_begin(); +q.each([](flecs::entity e) { + e.destruct(); +}); +world.defer_end(); // OK: entities are deleted here +``` diff --git a/vendors/flecs/docs/FlecsQueryLanguage.md b/vendors/flecs/docs/FlecsQueryLanguage.md new file mode 100644 index 000000000..623ad9ca8 --- /dev/null +++ b/vendors/flecs/docs/FlecsQueryLanguage.md @@ -0,0 +1,518 @@ +# Flecs Query Language + +## Introduction +The Flecs Query Language is a string-based representation of queries. The query language makes it easy to create queries at runtime, which makes it useful for tooling, modding or for requesting data from game servers. + +This manual is primarily focused on describing the query syntax. For more details on specific query feature, see the query manual. + +## Example +```c +// Match spaceship entities that are docked to a planet +SpaceShip, (DockedTo, $planet), Planet($planet) +``` + +## The Basics +An expression in the Flecs Query Language consists of a list of comma separated "terms", where each term in the query is a condition that entities matching the query must satisfy. + +### Components +The most basic kind of condition is "the entity must have this component". The following expression is an example of a query that matches all entities that have both `Position` and `Velocity`: + +```c +// Match entities that have Position and Velocity +Position, Velocity +``` + +Note that this query matches all entities that _at least_ have the `Position` and `Velocity` components; entities with components in addition to `Position` and `Velocity` also match the query. + +### Pairs +The following expression is an example of a query that matches two pairs: +```c +// Match entities that have (Likes, Bob) and (Eats, Apples) +(Likes, Bob), (Eats, Apples) +``` + +## Operators +Query operators change how a term is matched against entities. Only a single operator can be applied to a term at a time. The following sections go over the different operators in the query language. + +### Not +The `not` operator, specified with the `!` character, inverts the result of a term and makes it possible to match entities that _do not_ satisfy a condition. The following expression is an example of a query with a `not` operator: + +```c +// Match entities that have Position but not Velocity +Position, !Velocity +``` + +### Or +The `or` operator, specified with the `||` character, makes it possible to chain together a list of terms of which at least one term must be true. The following expression is an example of a query with an `or` operator: + +```c +// Match entities that have Position and Velocity or Mass +Position, Velocity || Mass +``` + +### Optional +The `optional` operator, specified with the `?` character, optionally matches a component. Optional terms do not change which entities are matched by a query, but can be useful for various reasons: + +- Fetching a component in a query is more efficient than `get` +- It allows for a single query to do what would otherwise have to be split up across several queries + +The following expression is an example of a query with an `optional` operator: + +```c +// Match entities that have Position and optionally Velocity +Position, ?Velocity +``` + +### AndFrom +the `andfrom` operator allows a query to match a list of components that another entity has. The entities used for the component list are typically prefabs, as they are not matched with queries themselves. + +The following expression is an example of a query with an `andfrom` operator: + +```c +// Match entities with Position and all components that MyType has +Position, and|MyType +``` + +The MyType entity could be created as shown in the following example: + +```cpp +flecs::entity my_type = world.prefab("MyType") + .add() + .add(); +``` + +This would cause the above query to be equivalent to: + +```c +Position, Velocity, Mass +``` + +### NotFrom +the `notfrom` operator allows a query to not match a list of components that another entity has. The entities used for the component list are typically prefabs, as they are not matched with queries themselves. + +The following expression is an example of a query with an `notfrom` operator: + +```c +// Match entities with Position and not any of the components that MyType has +Position, not|MyType +``` + +The MyType entity could be created as shown in the following example: + +```cpp +flecs::entity my_type = world.prefab("MyType") + .add() + .add(); +``` + +This would cause the above query to be equivalent to: + +```c +Position, !Velocity, !Mass +``` + +### OrFrom +the `orfrom` operator allows a query to match at least one of a list of components that another entity has. The entities used for the component list are typically prefabs, as they are not matched with queries themselves. + +The following expression is an example of a query with an `orfrom` operator: + +```c +// Match entities with Position and at least one of the components that MyType has +Position, or|MyType +``` + +The MyType entity could be created as shown in the following example: + +```cpp +flecs::entity my_type = world.prefab("MyType") + .add() + .add(); +``` + +This would cause the above query to be equivalent to: + +```c +Position, Velocity || Mass +``` + +## Access Modifiers +The following access modifiers can be added to terms to specify how term data is accessed: + +| Modifier | Description | +| -------- | ----------- | +| `[in]` | Matched component is read-only | +| `[out]` | Matched component is read-write | +| `[inout]` | Matched component is read-write | +| `[none]` | Matched component is not accessed | +| `[filter]` | Term does not produce events (for use with observers only) | + +An example: + +```c +[inout] Position, [in] Velocity +``` + +When no access modifier is specified, the term defaults to `InOutDefault`. This defaults to `[inout]` for terms that match owned components, and `[in]` for terms that match shared components (like singleton terms or terms matched through `up` traversal). + +## Wildcards +Query expressions can use wildcards to match any or all instances of a matching component or pair. Wildcards may appear in all parts of a term. The following examples are all valid wildcard queries: + +```c +Position, (Likes, *) +Position, (*, Dogs) +Position, (*, *) +Position, * +``` + +Query results contain information about the exact component or pair that was matched with the `ecs_field_id()` and `flecs::iter::id()` functions. This allows an application to inspect what was actually matched by a wildcard. + +### Wildcard wildcard (*) +The following expression is an example of a query that uses a wildcard: + +```c +// Match entities with a (Likes, *) pair +// Return all matching pairs +(Likes, *) +``` + +The `*` wildcard returns all matching instances of the wildcard. If an entity has both `(Likes, Dogs)` and `(Likes, Cats)`, it will be returned twice by the query, once for each pair. + +If a query has multiple wildcards, each permutation of the matched results will be returned. The following expression is an example of a query that has multiple wildcards: + +```c +// Match entities with (Likes, *) and (Eats, *) pairs +// Return all pair permutations +(Likes, *), (Eats, *) +``` + +If a single entity has `(Likes, Dogs)` and `(Likes, Cats)`, and has `(Eats, Pizza)` and `(Eats, Salad)`, that entity will yield four results: + +- `(Likes, Dogs)`, `(Eats, Pizza)` +- `(Likes, Dogs)`, `(Eats, Salad)` +- `(Likes, Cats)`, `(Eats, Pizza)` +- `(Likes, Cats)`, `(Eats, Salad)` + +### Any wildcard (_) +The `any` (`_`) wildcard returns at most one result per wildcard. The following expression is an example of a query that uses an `any` wildcard: + +```c +// Match entities with a (Likes, *) pair +// Return at most one result per entity +(Likes, _) +``` + +If an entity has both `(Likes, Dogs)` and `(Likes, Cats)`, the query will return only one result. The location of the `any` wildcard in the matched id will be replaced with `*`, indicating that no specific pair was matched. The above query would return the following id: + +- `(Likes, *)` + +If a query has multiple `any` wildcards, only a single result is returned. The following expression is an example of a query that has multiple wildcards: + +```c +// Match entities with (Likes, *) and (Eats, *) pairs +// Return at most one result per entity +(Likes, _), (Eats, _) +``` + +If a single entity has `(Likes, Dogs)` and `(Likes, Cats)`, and has `(Eats, Pizza)` and `(Eats, Salad)`, that entity will yield one result: + +- `(Likes, *)`, `(Eats, *)` + +## Variables +Query variables constrain which values a wildcard can assume by ensuring that the value that was matched by a wildcard in one term is used in all other terms. The following expression is an example of a query that uses variables: + +```c +// Match all entities that eat what they like +(Likes, $food), (Eats, $food) +``` + +If a single entity has `(Likes, Dogs)` and `(Likes, Pizza)`, and has `(Eats, Pizza)` and `(Eats, Salad)`, that entity will yield only one result: + +- `(Likes, Pizza)`, `(Eats, Pizza)` + +Note how this is a strict subset of the results that would be returned by the following query: + +```c +(Likes, *), (Eats, *) +``` + +Which would return: + +- `(Likes, Dogs)`, `(Eats, Pizza)` +- `(Likes, Dogs)`, `(Eats, Salad)` +- `(Likes, Pizza)`, `(Eats, Pizza)` +- `(Likes, Pizza)`, `(Eats, Salad)` + +Variables with names that that start with a `_` are treated as anonymous, and are not accessible when a query is iterated. + +## Source +All query terms have a "source", which is the entity on which the term is matched. If no term source is specified, it defaults to the `$this` variable. The following expressions show the same query without and with explicit source: + +```c +// Implicit source +Position, Velocity +``` +```c +// Explicit source +Position($this), Velocity($this) +``` + +Note how both terms have the same `$this` source. Using the same variable ensures that both components are matched on the same entity. + +The following expressions show how to use pair queries without and with explicit source: + +```c +// Implicit source +(Likes, Dogs), (Eats, Salad) +``` +```c +// Explicit source +Likes($this, Dogs), Eats($this, Salad) +``` + +A single query can have terms that are matched on more than one source. The following sections describe the supported source kinds. + +### Static source +A static source is a term that is always matched on an entity that is known at query creation time. A static source is specified by just using the name of the entity on which the component should be matched: + +```c +// Match TimeOfDay component on 'Game' entity +TimeOfDay(Game) +``` + +### Singleton source +A singleton is a special case of a static source, where the source is the same as the component. The following expression shows an example of a singleton source: + + +```c +TimeOfDay($) +``` + +This query is equivalent to: + +```c +TimeOfDay(TimeOfDay) +``` + +A singleton may also be used as the second element in a pair as shown in the next example: + +```c +TimeOfDay($this, $) +``` + +This query is equivalent to: + +```c +TimeOfDay($this, TimeOfDay) +``` + +### Variable source +A variable source is a variable that is used as term source. As mentioned already, when no source is specified, a term implicitly uses the builtin `$this` variable as source: + +```c +// Match entities with both Position and Velocity +Position($this), Velocity($this) +``` + +A variable used as source may appear in a different location in other terms. For example, the following expression uses a variable to match all entities that have components with the `Serializable` component: + +```c +Serializable($component), $component($this) +``` + +The following example matches all spaceship entities that are docked to a planet: + +```c +SpaceShip($this), DockedTo($this, $planet), Planet($planet) +``` + +The following example matches all entities that are eating healthy, but do not like what they are eating: + +```c +Eats($this, $food), !Likes($this, $food), Healthy($food) +``` + +## Traversal +Query traversal makes it possible to match a component by traversing a relationship until an entity with the component has been found. A common use case for this is a transform system, where a `Transform` component is matched both on an entity and its parent. + +The following expression shows an example of a query that matches a `Transform` component both on an entity and its parent: + +```c +Transform($this), Transform($this|up ChildOf) +``` + +The same query can be specified without the `$this` variable: + +```c +Transform, Transform(up ChildOf) +``` + +As `ChildOf` is the default traversal relationship, this query can be further shortened to: + +```c +Transform, Transform(up) +``` + +The `cascade` modifier is similar to `up` but returns results in breadth-first order. This is typically used in transform systems to ensure parents are transformed before children. The following expression shows an example with `cascade`: + +```c +Transform, Transform(cascade) +``` + +The `desc` modifier can be used in combination with `cascade` to return results in reverse order: + +```c +Transform, Transform(cascade|desc) +``` + +The `self` traversal modifier can be used in combination with `up` to first test if the entity itself has the component before traversing the hierarchy: + +```c +// First try matching Style on self, find on parent if not found +Position, Style(self|up) +``` + +When a component has the `(OnInstantiate, Inherit)` trait, queries will automatically insert `self|up` traversal for the `IsA` relationship. The following two queries are equivalent, if `Style` has the `(OnInstantiate, Inherit)` trait: + +```cpp +Position, Style +``` +```cpp +Position, Style(self|up IsA) +``` + +Traversal modifiers can be used with any relationship that has the `Traversable` trait: + +```cpp +world.component().add(flecs::Traversable); +``` + +When a query matches a component that is inherited from, a query will automatically traverse the `IsA` relationship downwards to find all subclasses. For example, if `MeleeUnit` has an `IsA` relationship to `Unit`, the following query matches entities with `Unit` and `MeleeUnit`: + +```c +Unit +``` + +To prevent queries from evaluating component inheritance, the `self` modifier can be added to the component: + +```c +Unit|self +``` + +For terms with an explicit source, the `self` modifier comes before the parentheses: + +```c +Unit|self($this) +``` + +When a query matches a relationship that has the `Transitive` trait, it will traverse the relationship up or down depending on which parts of the query are variable. To prevent a query from matching results transitively, add the `self` modifier to the second element of a pair: + +```c +LocatedIn($this, SanFrancisco|self) +``` + +This will only match entities that have `(LocatedIn, SanFrancisco)` and not, for example, entities with `(LocatedIn, GoldenGateBridge)`. + +## Advanced + +### Equality operators +Equality operators allow queries to match variables with specific values or names. The following example shows a query that matches a variable against with a specific entity: + +```c +SpaceShip($this), $this == UssEnterprise || $this == Voyager +``` + +The `!=` operator can be used to negate a result: + +```c +SpaceShip($this), $this != UssEnterprise +``` + +Queries may also compare two variables: + +```c +PoweredBy($this, $source), $this != $source +``` + +When a string is used as operand, the operation will test if the name of the entity matches: + +```c +SpaceShip($this), $this == "UssEnterprise" +``` + +The `~=` operator can be used to do a fuzzy comparison, equivalent to the behavior of the `substr` function: + +```c +SpaceShip($this), $this ~= "Uss" +``` + +The result of `~=` can be negated by prefixing the expression with a `!`: + +```c +SpaceShip($this), $this ~= "!Uss" +``` + +When an equality operator is the first term that populates a variable, it will assign the variable: + +```c +$this == "UssEnterprise", SpaceShip($this) +``` + +### Lookup variables +Variables can be used as the starting point of a by-name lookup. This can be useful when matching hierarchies that have a well-defined structure. The following expression is an example of a query with a lookup variable: + +```c +// Match all spaceship entities where the cockpit has no power +SpaceShip($this), !Powered($this.cockpit) +``` + +This query will look for an child entity named `cockpit` in the scope of the matched entity for `$this`, and use that entity to match with `Powered`. If no entity with the name `cockpit` is found, the term will evaluate to false. + +### Member matching +Queries can match against the values of component members if they are of the `ecs_entity_t` type. The following expression shows an example of how to match against a `direction` member in a `Movement` component: + +```c +Movement.direction($this, Left) +``` + +The same query with an implicit source: + +```c +(Movement.direction, Left) +``` + +A member expression can be used in combination with variables: + +```c +(Thrusters.left, $thruster), Active($thruster) +``` + +### Dependent variables +When a variable is used first in a term that is conditionally evaluated, any subsequent terms that use the variable will only be evaluated if the variable was set. This allows for the creation of simple branches within queries. The following expression shows an example of dependent variables: + +```c +// $animal and $food are set conditionally +(Likes, $animal) || (Eats, $food), +Friendly($animal), // Evaluated if (Likes, $animal) matched +Healthy($food) // Evaluated if (Eats, $food) matched +``` + +Dependent variables can also be created from optional terms: + +```c +// Planet($object) is only evaluated if (DockedTo, $object) +// returned a result. +SpaceShip, ?(DockedTo, $object), Planet($object) +``` + +### Query scopes +Query scopes can be used to apply an operator to the result of more than one term. Currently query scopes are only supported in combination with `not` operators. The following expressions show examples of query scopes: + +```c +// Match spaceships where none of the engines are healthy +SpaceShip, !{ (Engine, $engine), Healthy($engine) } +``` + +```c +// Match spaceships where all of the engines are healthy +SpaceShip, !{ (Engine, $engine), !Healthy($engine) } +``` diff --git a/vendors/flecs/docs/FlecsRemoteApi.md b/vendors/flecs/docs/FlecsRemoteApi.md new file mode 100644 index 000000000..882aaa66e --- /dev/null +++ b/vendors/flecs/docs/FlecsRemoteApi.md @@ -0,0 +1,1913 @@ +# Flecs Remote API +The Flecs Remote API makes it possible to remotely interface with a Flecs application. Possible applications of the Flecs Remote API are: + +- Remote tooling such as the [Flecs Explorer](https://www.flecs.dev/explorer/) +- Web applications that use Flecs as a backend store +- Web applications that connect to a Flecs wasm image +- Saving (and loading) ECS data to a file +- Saving (and loading) ECS data to a database + +The Flecs Remote API consists out of these components: + +- A JSON serializer/deserializer +- A REST API +- A client-side JavaScript library that wraps around the REST API +- The [Flecs Explorer](https://www.flecs.dev/explorer/) + +## Explorer Quickstart +The most common usage of the Flecs Remote API is the [Flecs Explorer](https://www.flecs.dev/explorer/). To allow the Flecs explorer to access an application, add this code: + +
+
    +
  • C + +```c +// Optional, gather statistics for explorer +ECS_IMPORT(world, FlecsStats); + +// Creates REST server on default port (27750) +ecs_singleton_set(world, EcsRest, {0}); + +// Runs the system serving up REST requests +while (ecs_progress(world, 0)) { } +``` + +
  • +
  • C++ + +```cpp +// Optional, gather statistics for explorer +world.import(); + +// Creates REST server on default port (27750) +world.set({}); + +// Runs the system serving up REST requests +while (world.progress()) { } +``` + +
  • +
  • C# + +```cs +// Optional, gather statistics for explorer +world.Import(); + +// Creates REST server on default port (27750) +world.Set(default); + +// Runs the system serving up REST requests +while (world.Progress()) { } +``` + +
  • +
  • Rust + +```rust +// Optional, gather statistics for explorer +world.import::(); + +// Creates REST server on default port (27750) +world.set(flecs::rest::Rest::default()); + +// Runs the system serving up REST requests +while world.progress() {} +``` +
  • +
+
+ +or, using the app addon: + +
+
    +
  • C + +```c +ecs_world_t *world = ecs_init(); + +ecs_app_run(world, &(ecs_app_desc_t) { + // Optional, gather statistics for explorer + .enable_stats = true + .enable_rest = true, +}); +``` + +
  • +
  • C++ + +```cpp +world.app() + // Optional, gather statistics for explorer + .enable_stats() + .enable_rest() + .run(); +``` + +
  • +
  • C# + +```cs +world.App() + // Optional, gather statistics for explorer + .EnableStats() + .EnableRest() + .Run(); +``` + +
  • +
  • Rust + +```rust +world + .app() + // Optional, gather statistics for explorer + .enable_stats(true) + .enable_rest(0) + .run(); +``` +
  • +
+
+ +When the application is running, navigate to this URL: https://flecs.dev/explorer . The explorer should connect automatically to the application. + +## Usage +The following sections describe how the Flecs Remote API can be used by applications. + +### JSON serialization +The Flecs JSON addon provides an easy API for serializing the world, queries and entities to JSON. The following example shows how to serialize the world to JSON: + +
+
    +
  • C + +```c +ecs_world_t *world = ecs_init(); + +char *json = ecs_world_to_json(world, NULL); +if (!json) { + // error +} +``` + +
  • +
  • C++ + +```cpp +flecs::world world; + +char *json = world.to_json(); +if (!json) { + // error +} +``` + +
  • +
+
+ +The resulting JSON can be deserialized back into the world: + +
+
    +
  • C + +```c +int res = ecs_world_from_json(world, json, NULL); +if (res) { + // error +} +``` + +
  • +
  • C++ + +```cpp +int res = world.from_json(json); +if (res) { + // error +} +``` + +
  • +
+
+ +Similarly, entities can be serialized to and from JSON: + +
+
    +
  • C + +```c +ecs_entity_t e = ecs_insert(...); + +char *json = ecs_entity_to_json(world, e, NULL); +if (!json) { + // error +} + +int res = ecs_entity_from_json(world, e, json, NULL); +if (res) { + // error +} + +``` +
  • +
  • C++ + +```cpp +flecs::entity e = world.entity().set(...); + +char *json = e.to_json(); +if (!json) { + // error +} + +int res = e.from_json(json); +if (res) { + // error +} +``` + +
  • +
+
+ +Queries can also be serialized to JSON: + +
+
    +
  • C + +```c +ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity" +}); + +ecs_iter_t it = ecs_query_iter(world, q); + +char *json = ecs_iter_to_json(it, NULL); +if (!json) { + // error +} +``` + +
  • +
  • C++ + +```cpp +auto q = world.query(); + +char *json = q.to_json(); +if (!json) { + // error +} +``` + +
  • +
+
+ +For more details on the JSON format and the various serialization options, see the Remote API reference. + +### REST API +The Flecs REST API enables remote access to running Flecs applications. The REST API can be used in various different ways: + +- Using the embedded HTTP server +- Using a 3rd party HTTP server +- By calling `ecs_http_server_request` directly +- To communicate with a WASM image + +The embedded HTTP server is a minimal implementation that only provides enough features to be able to serve up the REST API. It is sufficient for development purposes, but should not be used in production environments. + +The Flecs REST API can be used in production environments to use Flecs as a backend datastore that can be dynamically queried. This requires forwarding requests to a 3rd party HTTP library (such as civetweb) using the `ecs_http_server_request` function. + +The REST API can also be used to communicate with wasm images. By default when Flecs is built with `emscripten`, a function called `flecs_explorer_request` is exported as public symbol which enables doing REST requests. This symbol is used by the explorer to communicate with Flecs wasm images. + +The following examples show how to enable the embedded REST API, which is required for using Flecs with the explorer: + +
+
    +
  • C + +```c +// Optional, gather statistics for explorer +ECS_IMPORT(world, FlecsStats); + +// Creates REST server on default port (27750) +ecs_singleton_set(world, EcsRest, {0}); + +// Runs the system serving up REST requests +while (ecs_progress(world, 0)) { } +``` + +
  • +
  • C++ + +```cpp +// Optional, gather statistics for explorer +world.import(); + +// Creates REST server on default port (27750) +world.set({}); + +// Runs the system serving up REST requests +while (world.progress()) { } +``` + +
  • +
  • C# + +```cs +// Optional, gather statistics for explorer +world.Import(); + +// Creates REST server on default port (27750) +world.Set(default); + +// Runs the system serving up REST requests +while (world.Progress()) { } +``` + +
  • +
  • Rust + +```rust +// Optional, gather statistics for explorer +world.import::(); + +// Creates REST server on default port (27750) +world.set(flecs::rest::Rest::default()); + +// Runs the system serving up REST requests +while world.progress() {} +``` +
  • +
+
+ +Alternatively applications can also use the app addon to enable and run the REST API, which provides a portable way to run applications across desktop, mobile and web applications: + +
+
    +
  • C + +```c +ecs_world_t *world = ecs_init(); + +ecs_app_run(world, &(ecs_app_desc_t) { + // Optional, gather statistics for explorer + .enable_stats = true + .enable_rest = true, +}); +``` + +
  • +
  • C++ + +```cpp +world.app() + // Optional, gather statistics for explorer + .enable_stats() + .enable_rest() + .run(); +``` + +
  • +
+
+ +Alternatively the REST API can be called directly with the `ecs_http_server_request` C function. This makes it easy to use the REST API with a 3rd party HTTP server, or to run the API over a different protocol. + +The following example shows how to use the `ecs_http_server_request` function: + +```c +// Create a REST server. Don't specify connectivity +// parameters which ensures no HTTP server is created. +ecs_http_server_t *srv = ecs_rest_server_init( + world, NULL); + +ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; +int res = ecs_http_server_request(srv, + "GET", "/entity/my_entity", &reply); +if (res || reply.code != 200) { + // error +} + +char *body = ecs_strbuf_get(&reply.body); +// Use reply body +ecs_os_free(body); + +// Free server +ecs_rest_server_fini(srv); +``` + +For more details on the REST API, see the API reference. + +## JavaScript library +The JavaScript library is a client-side wrapper that makes it easy to build web applications with the Flecs REST API. To use the client library, include it like this: + +```html + +``` + +The latest version of the client library can be downloaded on this URL: +https://www.flecs.dev/explorer/flecs.js + +The client can then create a connection to a running application like this: + +```js +// Connect to localhost on the default port (27750) +let conn = flecs.connect("localhost"); +``` + +Clients can also connect to a remote host: + +```js +let conn = flecs.connect("https://my-game.com/api:9000"); +``` + +Clients can also connect directly to a wasm image: + +```js +let conn = flecs.connect("my-game.wasm"); +``` + +Once the connection is created, the client can make requests: + +```js +conn.query("Position, Velocity, (ChildOf, scene)", + {}, (reply) => { + for (let entity of reply.results) { + console.log(`entity ${entity.name} matched!`); + } + }); +``` + +## Reference +The following sections describe the different REST endpoints of the Flecs Remote API. Where applicable the corresponding JSON serialization code will be mentioned. The endpoint options map one to one to JSON (de)serializer options, prefixed by `serialize_`. For example, REST API option `values` maps to JSON serializer option `serialize_values`. + +### GET entity +Retrieve an entity and its tags, pairs and components. + +``` +GET entity/ +``` + +#### Options + +| Option | Type | Description | +|---------------|----------|-------------------------------------------------| +| entity_id | bool | Serialize numeric entity id. | +| doc | bool | Serialize flecs doc components | +| full_paths | bool | Serialize full tag/pair/component paths | +| inherited | bool | Serialize inherited components | +| values | bool | Serialize component values | +| builtin | bool | Serialize builtin components for parent & name | +| type_info | bool | Serialize type info for components | +| matches | bool | Serialize matched with queries | +| alerts | bool | Serialize active alerts for entity & children | +| refs | entity | Serialize relationship back references | +| try | bool | Don't throw HTTP error on failure (REST only) | + +#### Examples + +In this example, `Mass` is a component with reflection data, `Position` is a pair component without reflection data, and `(Description, Color)` is a pair component with reflection data. + +
+
    +
  • HTTP + +``` +GET /entity/Sun/Earth +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.entity("Sun.Earth", {}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); + +char *json = ecs_entity_to_json(world, e, NULL); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); + +auto json = e.to_json(); +// ... +``` + +
  • +
+
+ +```json +{ + "parent": "Sun", + "name": "Earth", + "tags": [ + "Planet" + ], + "components": { + "Mass": { + "value": "5.9722e24" + }, + "Position": null, + "(flecs.doc.Description,flecs.doc.Color)": { + "value": "#6b93d6" + } + } +} +``` +--- +This example shows how `type_info` can be used to obtain the component schema's. If no schema is available a `null` placeholder is sent. Note how the member name used in the `type_info` object is the same as used in the `components` object. The example also shows the `entity_id` option, which adds the `id` member. + +
+
    +
  • HTTP + +``` +GET /entity/Sun/Earth?id=true&type_info=true +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.entity("Sun.Earth", {id: true, type_info: true}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); + +ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; +desc.serialize_entity_id = true; +desc.serialize_type_info = true; +char *json = ecs_entity_to_json(world, e, &desc); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); + +ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; +desc.serialize_entity_id = true; +desc.serialize_type_info = true; +auto json = e.to_json(&desc); +``` + +
  • +
+
+ +```json +{ + "parent": "Sun", + "name": "Earth", + "id": 775, + "type_info": { + "Mass": { + "value": [ + "float", + { + "unit": "flecs.units.Mass.KiloGrams", + "symbol": "kg", + "quantity": "flecs.units.Mass" + } + ] + }, + "Position": 0, + "(flecs.doc.Description,flecs.doc.Color)": { + "value": [ + "text" + ] + } + }, + "tags": [ + "Planet" + ], + "components": { + "Mass": { + "value": "5.9722e24" + }, + "Position": null, + "(flecs.doc.Description,flecs.doc.Color)": { + "value": "#6b93d6" + } + } +} +``` +--- +This example demonstrates serializing inherited tags, pairs and components. Prefabs are added in the order they are encountered when traversing the `IsA` relationship. If multiple prefabs have the same component, the component will show up multiple times in the reply. A client must only consider the first occurrence of the component. + +
+
    +
  • HTTP + +``` +GET /entity/Sun/Earth?inherited=true +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.entity("Sun.Earth", {inherited: true}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); + +ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; +desc.serialize_inherited = true; +char *json = ecs_entity_to_json(world, e, &desc); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); + +ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; +desc.serialize_inherited = true; +auto json = e.to_json(&desc); +``` + +
  • +
+
+ +```json +{ + "parent": "Sun", + "name": "Earth", + "tags": [ + "Planet" + ], + "pairs": { + "flecs.core.IsA": "HabitablePlanet" + }, + "inherited": { + "planets.HabitablePlanet": { + "tags": [ + "HasWater", + "HasBreathableAir" + ] + } + }, + "components": { + "Mass": { + "value": "5.9722e24" + } + } +} +``` +--- +The following example shows the effect of disabling serialization of component values. + +
+
    +
  • HTTP + +``` +GET /entity/Sun/Earth?values=false +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.entity("Sun.Earth", {values: false}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); + +ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; +desc.serialize_values = false; +char *json = ecs_entity_to_json(world, e, &desc); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); + +ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; +desc.serialize_values = false; +auto json = e.to_json(&desc); +``` + +
  • +
+
+ +```json +{ + "parent": "Sun", + "name": "Earth", + "tags": [ + "Planet" + ], + "components": { + "Mass": null, + } +} +``` + +### PUT entity +Create an entity. + +``` +PUT /entity/ +``` + +#### Examples +The following example creates entity Sun.Earth. + +
+
    +
  • HTTP + +``` +PUT /entity/Sun/Earth +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.create("Sun.Earth"); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_entity(world, { .name = "Sun.Earth" }); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.entity("Sun.Earth"); +``` + +
  • +
+
+ +### DELETE entity +Delete an entity. + +``` +DELETE /entity/ +``` + +#### Examples +The following example deletes entity Sun.Earth. + +
+
    +
  • HTTP + +``` +DELETE /entity/Sun/Earth +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.delete("Sun.Earth"); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); +ecs_delete(world, e); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); +e.destruct(); +``` + +
  • +
+
+ + +### GET component +Retrieve a single component from an entity. + +``` +GET /component/?component= +``` + +#### Examples + +
+
    +
  • HTTP + +``` +GET /component/Sun/Earth?component=planets.Mass +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.get("Sun.Earth", {component: "planets.Mass"}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); +ecs_entity_t c = ecs_lookup(world, "planets.Mass"); +const void *ptr = ecs_get_id(world, e, c); +char *json = ecs_ptr_to_json(world, c, ptr); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); +flecs::entity c = world.lookup("planets::Mass"); +const void *ptr = e.get(c); +auto json = world.to_json(c, ptr); +``` + +
  • +
+
+ +```json +{ "value": "5.9722e24" } +``` + +### PUT component +Add or set a component. + +``` +PUT /component/?component=[&value=] +``` + +#### Examples +The following example adds the component `Position` to `Sun.Earth`: + +
+
    +
  • HTTP + +``` +PUT /component/Sun/Earth?component=Position +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.add("Sun.Earth", "Position"); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); +ecs_entity_t c = ecs_lookup(world, "Position"); +ecs_add_id(world, e, c); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); +flecs::entity c = world.lookup("planets::Mass"); +e.add(c); +``` + +
  • +
+
+ +--- +The following example writes the value `{"x":10}` to the `Position` component of entity `Sun.Earth`: + +
+
    +
  • HTTP + +``` +PUT /component/Sun/Earth?component=Position&value=%7B%22x%22%3A%2210%22%7D +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.set("Sun.Earth", "Position", {x: 10}); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); +ecs_entity_t c = ecs_lookup(world, "Position"); +void *ptr = ecs_get_mut_id(world, e, c); +ecs_ptr_from_json(world, c, ptr, "{\"x\": 10}", NULL); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); +flecs::entity c = world.lookup("planets::Mass"); +void *ptr = e.get_mut(c); +world.from_json(c, ptr, "{\"x\": 10}"); +``` + +
  • +
+
+ + +### DELETE component +Remove a component from an entity. + +``` +DELETE /component/?component= +``` + +#### Examples +The following example removes the component `Position` from `Sun.Earth`: + +
+
    +
  • HTTP + +``` +DELETE /component/Sun/Earth?component=Position +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.remove("Sun.Earth", "Position"); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); +ecs_entity_t c = ecs_lookup(world, "Position"); +ecs_remove_id(world, e, c); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); +flecs::entity c = world.lookup("planets::Mass"); +e.remove(c); +``` + +
  • +
+
+ +### PUT toggle +Toggle an entity or component. + +``` +PUT /toggle/?enable=[true|false][&component=] +``` + +When an entity is toggled, the `Disabled` tag is added or removed. When a component is toggled, the `ecs_enable_id` operation is invoked. A component toggle request will fail if the component does not have the `CanToggle` trait. + +#### Examples +The following example disables entity `Sun.Earth`: + +
+
    +
  • HTTP + +``` +PUT /toggle/Sun/Earth?enable=false +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.disable("Sun.Earth"); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); +ecs_enable(world, e, false); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); +e.disable(); +``` + +
  • +
+
+ +--- +The following example disables component `Position` for entity `Sun.Earth`: + +
+
    +
  • HTTP + +``` +PUT /toggle/Sun/Earth?enable=false&component=Position +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.disable("Sun.Earth", "Position"); +``` + +
  • +
  • C + +```c +ecs_entity_t e = ecs_lookup(world, "Sun.Earth"); +ecs_entity_t c = ecs_lookup(world, "Position"); +ecs_enable_id(world, e, c, false); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.lookup("Sun::Earth"); +flecs::entity c = world.lookup("Position"); +e.disable(c); +``` + +
  • +
+
+ +### GET query +Retrieve matching results for a query. + +``` +GET /query/?expr= +``` + +#### Options + +| Option | Type | Description | +|---------------|----------|-------------------------------------------------| +| name | string | Evaluate existing named query. | +| expr | string | Evaluate query expression. | +| entity_id | bool | Serialize numeric entity id. | +| doc | bool | Serialize flecs doc components | +| full_paths | bool | Serialize full tag/pair/component paths | +| inherited | bool | Serialize inherited components | +| values | bool | Serialize component values | +| builtin | bool | Serialize builtin components for parent & name | +| fields | bool | Serialize query fields | +| table | bool | Serialize table (all components for match) | +| result | bool | Serialize results (disable for metadata-only request) | +| type_info | bool | Serialize type info for components | +| field_info | bool | Serialize field info | +| query_info | bool | Serialize query info | +| query_plan | bool | Serialize query plan | +| query_profile | bool | Serialize query profiling information | +| try | bool | Don't throw HTTP error on failure (REST only) | + +#### Examples +The following example returns the results for query Position, Velocity. Note how each of the query terms results in a field in the reply. The mapping between query terms and fields is the same as in the regular flecs API. + +
+
    +
  • HTTP + +``` +GET /query?expr=Position%2CVelocity +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.query("Position, Velocity", {}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity" +}); + +ecs_iter_t it = ecs_query_iter(world, q); +char *json = ecs_iter_to_json(&it, NULL); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::query<> q = world.query_builder() + .expr("Position, Velocity") + .build(); + +auto json = q.iter().to_json(); +``` + +
  • +
+
+ +```json +{ + "results": [ + { + "parent": "Earth.shipyard", + "name": "USS Enterprise", + "fields": { + "values": [ + { + "x": 10, + "y": 20 + }, + { + "x": 1, + "y": 2 + } + ] + } + }, + { + "parent": "Earth.shipyard", + "name": "USS Voyager", + "fields": { + "values": [ + { + "x": 30, + "y": 40 + }, + { + "x": 3, + "y": 4 + } + ] + } + } + ] +} +``` +--- +The following example shows the result of setting `table` to `true`, which serializes all components (the table) of each matched entity. The format of the individually returned results is the same as the `/entity` endpoint. + +
+
    +
  • HTTP + +``` +GET /query?expr=Position%2CVelocity?table=true +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.query("Position, Velocity", {table: true}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity" +}); + +ecs_iter_t it = ecs_query_iter(world, q); +ecs_iter_to_json_t desc = ECS_ITER_TO_JSON_INIT; +desc.serialize_table = true; +char *json = ecs_iter_to_json(&it, &desc); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::query<> q = world.query_builder() + .expr("Position, Velocity") + .build(); + +ecs_iter_to_json_t desc = ECS_ITER_TO_JSON_INIT; +desc.serialize_table = true; +auto json = q.iter().to_json(&desc); +``` + +
  • +
+
+ +```json +{ + "results": [ + { + "parent": "Earth.shipyard", + "name": "USS Enterprise", + "tags": [ + "Broken", + "FtlEnabled" + ], + "pairs": { + "flecs.core.IsA": "Freighter" + }, + "components": { + "Position": { + "x": 10, + "y": 20 + }, + "Velocity": { + "x": 1, + "y": 1 + } + } + }, + { + "parent": "Earth.shipyard", + "name": "USS Voyager", + "tags": [ + "FtlEnabled" + ], + "pairs": { + "IsA": "Freighter" + }, + "components": { + "Position": { + "x": 30, + "y": 40 + }, + "Velocity": { + "x": 3, + "y": 4 + } + } + } + ] +} +``` + +--- +The following example shows the result for query `Position, ?Position(up)`. This shows the information that gets added to the query result to describe what the source of a field is, and whether a field is set or not. + +The `is_set` member is only added to the result if one of the fields in the result is not set. The `sources` member is only added to the result if one of the fields in the result is not matched on the `$this` query variable. + +
+
    +
  • HTTP + +``` +GET /query?expr=Position%2C%3FPosition(up) +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.query("Position, ?Position(up)", {}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Position(up)" +}); + +ecs_iter_t it = ecs_query_iter(world, q); +char *json = ecs_iter_to_json(&it, NULL); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::query<> q = world.query_builder() + .expr("Position, ?Position(up)") + .build(); + +auto json = q.iter().to_json(); +``` + +
  • +
+
+ +```json +{ + "results": [ + { + "name": "Earth.shipyard", + "fields": { + "is_set": [true, false], + "sources": [0, 0], + "values": [ + { + "x": 100, + "y": 200 + }, + {} + ] + } + }, + { + "parent": "Earth.shipyard", + "name": "USS Voyager", + "fields": { + "sources": [0, "Earth.shipyard"], + "values": [ + { + "x": 30, + "y": 40 + }, + { + "x": 3, + "y": 4 + } + ] + } + }, + { + "parent": "Earth.shipyard", + "name": "USS Voyager", + "fields": { + "sources": [0, "Earth.shipyard"], + "values": [ + { + "x": 30, + "y": 40 + }, + { + "x": 3, + "y": 4 + } + ] + } + } + ] +} +``` +--- +The following example shows the same query (`Position, ?Position(up)`) with `fields` set to `false` and `entity_ids` set to `true`. + +
+
    +
  • HTTP + +``` +GET /query?expr=Position%2C%3FPosition(up)&fields=false&entity_ids=true +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.query("Position, ?Position(up)", {fields: false, entity_ids: true}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Position(up)" +}); + +ecs_iter_t it = ecs_query_iter(world, q); +ecs_iter_to_json_t desc = ECS_ITER_TO_JSON_INIT; +desc.serialize_fields = true; +desc.serialize_entity_ids = true; +char *json = ecs_iter_to_json(&it, &desc); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::query<> q = world.query_builder() + .expr("Position, ?Position(up)") + .build(); + +ecs_iter_to_json_t desc = ECS_ITER_TO_JSON_INIT; +desc.serialize_fields = true; +desc.serialize_entity_ids = true; +auto json = q.iter().to_json(&desc); +``` + +
  • +
+
+ +```json +{ + "results": [ + { + "name": "Earth.shipyard", + "id": 700 + }, + { + "parent": "Earth.shipyard", + "name": "USS Voyager", + "id": 701 + }, + { + "parent": "Earth.shipyard", + "name": "USS Voyager", + "id": 702 + } + ] +} +``` + +--- +The following example shows how variables are serialized with the query `Position, (ChildOf, $p)`. The `ids` member is added to the serialized data, as the second term of this query has a variable (pair) id. Only fields with variable component ids are serialized to the `ids` array. + +
+
    +
  • HTTP + +``` +GET /query?expr=Position%2C(ChildOf%2C%24p) +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.query("Position, (ChildOf, $p)", {}, (reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +ecs_query_t *q = ecs_query(world, { + .expr = "Position, (ChildOf, $p)" +}); + +ecs_iter_t it = ecs_query_iter(world, q); +char *json = ecs_iter_to_json(&it, NULL); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::query<> q = world.query_builder() + .expr("Position, (ChildOf, $p)") + .build(); + +auto json = q.iter().to_json(); +``` + +
  • +
+
+ +```json +{ + "results": [ + { + "parent": "Earth", + "name": "shipyard", + "vars": { + "p": "Earth" + }, + "fields": { + "ids": [0, ["flecs.core.ChildOf", "Earth"]], + "values": [ + { + "x": 100, + "y": 200 + }, + 0] + } + }, + { + "parent": "Earth.shipyard", + "name": "USS Enterprise", + "vars": { + "p": "shipyard" + }, + "fields": { + "ids": [0, ["flecs.core.ChildOf", "Earth.shipyard"]], + "values": [ + { + "x": 10, + "y": 20 + }, + 0] + } + }, + { + "parent": "Earth.shipyard", + "name": "USS Voyager", + "vars": { + "p": "shipyard" + }, + "fields": { + "ids": [0, ["flecs.core.ChildOf", "Earth.shipyard"]], + "values": [ + { + "x": 10, + "y": 20 + }, + 0] + } + } + ] +} +``` + +--- +The following example shows the result of setting `query_info` and `field_info` to true, and setting `results` to false. This can be used to obtain meta information about a query. `query_info` returns a result per term, whereas `field_info` returns a result per field. The query is `Position, ?Mass(up)`. + +
+
    +
  • HTTP + +``` +GET /query?expr=Position%2C%3FMass(up)?query_info=true&field_info=true&results=false +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.query("Position, ?Mass(up)", { + query_info: true, + field_info: true, + results: false + }, (reply) => { + // ... + }); +``` + +
  • +
  • C + +```c +ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Mass(up)" +}); + +ecs_iter_t it = ecs_query_iter(world, q); +ecs_iter_to_json_t desc = ECS_ITER_TO_JSON_INIT; +desc.serialize_query_info = true; +desc.serialize_field_info = true; +desc.serialize_results = true; +char *json = ecs_iter_to_json(&it, &desc); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +flecs::query<> q = world.query_builder() + .expr("Position, ?Mass(up)") + .build(); + +ecs_iter_to_json_t desc = ECS_ITER_TO_JSON_INIT; +desc.serialize_query_info = true; +desc.serialize_field_info = true; +desc.serialize_results = true; +auto json = q.iter().to_json(&desc); +``` + +
  • +
+
+ +```json +{ + "field_info": [ + { + "id": "Position", + "type": "Position", + "symbol": "Position", + "schema": { + "x": [ + "float" + ], + "y": [ + "float" + ] + } + }, + { + "id": "Mass", + "optional": true, + "type": "Mass", + "symbol": "Mass", + "schema": { + "value": [ + "float" + ] + } + } + ], + "query_info": { + "vars": [ + "this" + ], + "terms": [ + { + "inout": "default", + "has_value": true, + "oper": "and", + "src": { + "var": "this" + }, + "first": { + "entity": "Position", + "symbol": "Position", + "type": true + }, + "flags": [ + "self" + ] + }, + { + "inout": "default", + "has_value": true, + "oper": "optional", + "src": { + "var": "this" + }, + "first": { + "entity": "Mass", + "symbol": "Mass", + "type": true + }, + "trav": { + "entity": "flecs.core.ChildOf", + "symbol": "EcsChildOf" + }, + "flags": [ + "up" + ] + } + ] + } +} +``` + +### GET world +Retrieve all serializable data in the world. + +``` +GET /world +``` + +#### Example + +
+
    +
  • HTTP + +``` +GET /world +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.world((reply) => { + // ... +}); +``` + +
  • +
  • C + +```c +char *json = ecs_world_to_json(NULL); +// ... +ecs_os_free(json); +``` + +
  • +
  • C++ + +```cpp +auto json = world.to_json(); +``` + +
  • +
+
+ +The output of this endpoint is equivalent to requesting the following query on the query endpoint with `table=true`: + +``` +!ChildOf(self|up, flecs), +!Module(self|up), +?Prefab, +?Disabled +``` + +This excludes modules and builtin entities from the result, effectively just returning entities that are active in a scene. + +### PUT script +Update code for Flecs script. + +``` +PUT /script/?code= +``` + +When the code failed to parse a response is sent back with the error: + +```json +{ + "error": "58: unexpected end of script\n}\n ^" +} +``` + +#### Example + +
+
    +
  • HTTP + +``` +PUT /script/main.flecs?code=SpaceShip{} +``` + +
  • +
  • JavaScript + +```js +const conn = flecs.connect("localhost"); + +conn.scriptUpdate("main\\.flecs", "SpaceShip{}"); +``` + +
  • +
  • C + +```c +ecs_entity_t s = ecs_lookup(world, "main\\.flecs"); +ecs_script_update(world, s, 0, "SpaceShip{}"); +``` + +
  • +
  • C++ + +```cpp +flecs::entity s = world.lookup("main.flecs"); +ecs_script_update(world, s, 0, "SpaceShip{}"); +``` + +
  • +
+
diff --git a/vendors/flecs/docs/FlecsScript.md b/vendors/flecs/docs/FlecsScript.md new file mode 100644 index 000000000..d095b1a23 --- /dev/null +++ b/vendors/flecs/docs/FlecsScript.md @@ -0,0 +1,777 @@ +# Flecs Script + +## Introduction +Flecs Script is a runtime interpreted DSL for creating entities and components that is optimized for defining scenes, assets and configuration. In a nutshell, Flecs Script is to ECS what HTML/JSX is to a browser. + +Some of the features of Flecs Script are: + +- Native support for named entities, hierarchies and inheritance +- Assign component values +- Expressions and variables (`$var + 10`) +- Conditionals (`if $var > 10`) +- Native integration with templates (procedural assets) + +To learn Flecs Script, check out the [Tutorial](FlecsScriptTutorial.md)! + +## Example + +```c +using flecs.meta + +struct MaxSpeed { + value = f32 +} + +struct Position { + x = f32 + y = f32 +} + +Prefab SpaceShip { + MaxSpeed: {value: 100} + + cockpit { + Position: {x: -10, y: 0} + } +} + +my_spaceship : SpaceShip { + Position: {x: 10, y: 20} +} +``` + +## The Basics +This section goes over the basic syntax over Flecs Script. + +### Entities +An entity is created by specifying an identifier followed by a scope. Example: + +```c +my_entity {} +``` + +An entity scope can contain components and child entities. The following example shows how to add a child entity: + +```c +my_parent { + my_child {} +} +``` + +Note how a scope is also added to the child entity. + +To create anonymous entities, use the `_` identifier: + +```c +_ { + my_child {} // named child with anonymous parent +} +``` + +### Tags +A tag can be added to an entity by simply specifying the tag's identifier in an entity scope. Example: + +```c +SpaceShip {} // Define SpaceShip tag + +my_entity { + SpaceShip // Add SpaceShip to my_entity +} +``` + +### Pairs +Pairs are added to entities by adding them to an entity scope, just like tags: + +```c +Likes {} +Pizza {} + +my_entity { + (Likes, Pizza) +} +``` + +### Components +Components are specified like tags, but with an additional value: + +```c +my_entity { + Position: {x: 10, y: 20} +} +``` + +For a component to be assignable with a value, it also needs to be described in the reflection framework. + +A component can also be added without a value. This will create a default constructed component. Example: + +```c +my_entity { + Position +} +``` + +Components can be defined in a script: + +```c +using flecs.meta + +struct Position { + x = f32 + y = f32 +} + +my_entity { + Position: {x: 10, y: 20} +} +``` + +Components can be pairs: + +```c +my_entity { + (Start, Position): {x: 0, y: 0} + (Stop, Position): {x: 10, y: 20} +} +``` + +### Namespacing +When referring to child entities or components, identifiers need to include the parent path as well as the entity name. Paths are provided as lists of identifiers separated by a dot (`.`): + +```c +Sun.Earth { + solarsystem.Planet +} +``` + +To avoid having to repeatedly type the same paths, use the `using` statement (see below). + +### Singletons +To create a singleton component, use `$` as the entity identifier: + +```c +$ { + TimeOfDay: { t: 0.5 } +} +``` + +Multiple singleton components can be specified in the same scope: + +```c +$ { + TimeOfDay: { t: 0.5 } + Player: { name: "bob" } +} + +``` + +### Entity kinds +An entity can be created with a "kind", which is a component specified before the entity name. This is similar to adding a tag or component in a scope, but can provide a more natural way to describe things. For example: + +```c +SpaceShip my_spaceship {} +``` + +This is equivalent to doing: + +```c +my_spaceship { + SpaceShip +} +``` + +When using the entity kind syntax, the scope is optional: + +```c +SpaceShip my_spaceship // no {} +``` + +If the specified kind is a component, a value can be specified between parentheses: + +```c +CheckBox my_checkbox(checked: true) +``` + +When the entity kind is a component, a value will always be assigned even if none is specified. This is different from component assignments in a scope. Example: + +```c +CheckBox my_checkbox(checked: true) + +// is equivalent to + +my_checkbox { + CheckBox: {checked: true} +} +``` + +```c +CheckBox my_checkbox + +// is equivalent to + +my_checkbox { + CheckBox: {} +} +``` + +#### Builtin kinds +Applications can specify the following builtin kinds which provide convenience shortcuts to commonly used features: + +```c +prefab SpaceShip + +// is equivalent to + +Prefab spaceship +``` + +```c +prefab SpaceShip { + slot CockPit +} + +// is equivalent to + +prefab SpaceShip { + CockPit { + (SlotOf, SpaceShip) + } +} +``` + +### Inheritance +Scripts can natively specify inheritance relationships between entities, which is useful in particular for prefabs. Example: + +```c +Prefab SpaceShip { + MaxSpeed: {value: 100} +} + +my_spaceship : SpaceShip {} +``` + +When specifying inheritance, the scope is optional: + +```c +my_spaceship : SpaceShip // no {} +``` + +This is equivalent to doing: + +```c +my_spaceship { + (IsA, SpaceShip) +} +``` + +### Relationship hierarchies +By default entity hierarchies are created with the `ChildOf` relationship. Other relationships can also be used to create hierarchies by combining a pair with a scope. Example: + +```c +(IsA, Thing) { + (IsA, Organism) { + (IsA, Plant) { + Tree {} + } + (IsA, Animal) { + Human {} + } + } +} +``` + +## Advanced Features + +### Module statement +The `module` statement puts all contents of a script in a module. Example: + +```c +module components.transform + +// Creates components.transform.Position +struct Position { + x = f32 + y = f32 +} +``` + +The `components.transform` entity will be created with the `Module` tag. + +### Using statement +The `using` keyword imports a namespace into the current namespace. Example: + +```c +// Without using +flecs.meta.struct Position { + x = flecs.meta.f32 + y = flecs.meta.f32 +} +``` +```c +// With using +using flecs.meta + +struct Position { + x = f32 + y = f32 +} +``` + +The `using` keyword only applies to the scope in which it is specified. Example: + +```c +// Scope without using +my_engine { + game.engines.FtlEngine: {active: true} +} +``` +```c +// Scope with using +my_spaceship { + using game.engines + + FtlEngine: {active: true} +} +``` + +A `using` statement may end with a wildcard (`*`). This will import all namespaces matching the path. Example: + +```c +using flecs.* + +struct Position { + x = f32 + y = f32 +} +``` + +### With statement +When you're building a scene or asset you may find yourself often repeating the same components for multiple entities. To avoid this, a `with` statement can be used. For example: + +```c +with SpaceShip { + MillenniumFalcon {} + UssEnterprise {} + UssVoyager {} + Rocinante {} +} +``` + +This is equivalent to doing: + +```c +MillenniumFalcon { + SpaceShip +} + +UssEnterprise { + SpaceShip +} + +UssVoyager { + SpaceShip +} + +Rocinante { + SpaceShip +} +``` + +With statements can contain multiple tags: + +```c +with SpaceShip, HasWeapons { + MillenniumFalcon {} + UssEnterprise {} + UssVoyager {} + Rocinante {} +} +``` + +With statements can contain component values, specified between parentheses: + +```c +with Color(38, 25, 13) { + pillar_1 {} + pillar_2 {} + pillar_3 {} +} +``` + +### Variables +Scripts can contain variables, which are useful for often repeated values. Variables are created with the `const` keyword. Example: + +```c +const pi = 3.1415926 + +my_entity { + Rotation: {angle: $pi} +} +``` + +Variables can be combined with expressions: + +```c +const pi = 3.1415926 +const pi_2 = $pi * 2 + +my_entity { + Rotation: {angle: $pi / 2} +} +``` + +In the above examples, the type of the variable is inferred. Variables can also be provided with an explicit type: + +```c +const wood = Color: {38, 25, 13} +``` + +Variables can be used in component values as shown in the previous examples, or can be used directly as component. Example: + +```c +const wood = Color: {38, 25, 13} + +my_entity { + $wood +} + +// is equivalent to + +my_entity { + Color: {38, 25, 13} +} +``` + +Additionally, variables can also be used in combination with `with` statements: + +```c +const wood = Color: {38, 25, 13} + +with $color { + pillar_1 {} + pillar_2 {} + pillar_3 {} +} +``` + +### Component values +A script can use the value of a component that is looked up on a specific entity. The following example fetches the `width` and `depth` members from the `Level` component, that is fetched from the `Game` entity: + +```c +grid { + Grid: { Game[Level].width, Game[Level].depth } +} +``` + +To reduce the number of component lookups in a script, the component value can be stored in a variable: + +```c +const level = Game[Level] + +tiles { + Grid: { width: $level.width, $level.depth, prefab: Tile } +} +``` + +The requested component is stored by value, not by reference. Adding or removing components to the entity will not invalidate the component data. If the requested component does not exist on the entity, script execution will fail. + +### If statement +Parts of a script can be conditionally executed with an if statement. Example: + +```c +const daytime = bool: false + +lantern { + Color: {210, 255, 200} + + if $daytime { + Emissive: { value: 0 } + } else { + Emissive: { value: 1 } + } +} +``` + +### Default components +A scope can have a default component, which means entities in that scope can assign values of that component without having to specify the component name. + +There are different ways to specify a default component. One way is to use a `with` statement. Default component values are assigned with the `=` operator, and don't need a `{}` surrounding the value. Example: + +```c +with Position { + ent_a = 10, 20 + ent_b = 20, 30 +} +``` + +Another way a default components are derived is from the entity kind. If an entity is specified with a kind, a `DefaultChildComponent` component will be looked up on the kind to find the default component for the scope, if any. For example: + +```c +// Create a PositionList tag with a DefaultChildComponent +PositionList { + DefaultChildComponent: {Position} +} + +// Derive default component for scope from PositionList +PositionList plist { + ent_a = 10, 20 + ent_b = 10, 20 + ent_c = 10, 20 +} +``` + +A common use of default components is when creating structs. `struct` is a component with `member` as default child component. Example: + +```c +struct Position { + x = f32 + y = f32 +} + +// is equivalent to + +struct Position { + member x(f32) + member y(f32) +} +``` + +Note how `member` is also used as kind for the children. This means that children of `x` and `y` derive their default child component from `member`, which is set to `member`. This makes it easy to create nested members: + +```c +struct Line { + start { + x = f32 + y = f32 + } + stop { + x = f32 + y = f32 + } +} + +// is equivalent to + +struct Line { + member start { + member x(f32) + member y(f32) + } + member stop { + member x(f32) + member y(f32) + } +} +``` + +### Semicolon operator +Multiple statements can be combined on a single line when using the semicolon operator. Example: + +```c +my_spaceship { + SpaceShip; HasFtl +} +``` + +## Comma operator +The comma operator can be used as a shortcut to create multiple entities in a scope. Example: + +```c +my_spaceship { + pilot_a, + pilot_b, + pilot_c +} + +// is equivalent to + +my_spaceship { + pilot_a {} + pilot_b {} + pilot_c {} +} +``` + +This allows for a more natural way to describe things like enum types: + +```c +enum Color { + Red, + Green, + Blue +} + +// is equivalent to + +enum Color { + constant Red + constant Green + constant Blue +} +``` + +## Templates +Templates are parametrized scripts that can be used to create procedural assets. Templates can be created with the `template` keyword. Example: + +```c +template Square { + Color: {255, 0, 0} + Rectangle: {width: 100, height: 100} +} +``` + +The script contents of an template are not ran immediately. Instead they are ran whenever an template is _instantiated_. To instantiate an template, add it as a regular component to an entity: + +```c +my_entity { + Square +} + +// is equivalent to + +my_entity { + Color: {255, 0, 0} + Rectangle: {width: 100, height: 100} +} +``` + +Templates are commonly used in combination with the kind syntax: + +```c +Square my_entity +``` + +Templates can be parametrized with properties. Properties are variables that are exposed as component members. To create a property, use the `prop` keyword. Example: + +```c +template Square { + prop size = i32: 10 + prop color = Color: {255, 0, 0} + + $color + Rectangle: {width: $size, height: $size} +} + +Square my_entity(size: 20, color: {38, 25, 13}) +``` + +Template scripts can do anything a regular script can do, including creating child entities. The following example shows how to create an template that uses a nested template to create children: + +```c +template Tree { + prop height = f32: 10 + + const wood_color = Color: {38, 25, 13} + const leaves_color = Color: {51, 76, 38} + + const canopy_height = 2 + const trunk_height = $height - $canopy_height + const trunk_width = 2 + + Trunk { + Position: {0, ($height / 2), 0} + Rectangle: {$trunk_width, $trunk_height} + $wood_color + } + + Canopy { + const canopy_y = $trunk_height + ($canopy_height / 2) + + Position3: {0, $canopy_y, 0} + Box: {$canopy_width, $canopy_height} + $leaves_color + } +} + +template Forest { + Tree(height: 5) { + Position: {x: -10} + } + + Tree(height: 10) { + Position: {x: 0} + } + + Tree(height: 7) { + Position: {x: 10} + } +} + +Forest my_forest +``` + +## API +This section goes over how to run scripts in an application. + +### Run once +To run a script once, use the `ecs_script_run` function. Example: + +```c +const char *code = "my_spaceship {}"; + +if (ecs_script_run(world, "my_script_name", code)) { + // error +} +``` + +Alternatively a script can be ran directly from a file: + +```c +if (ecs_script_run_file(world, "my_script.flecs")) { + // error +} +``` + +If a script fails, the entities created by the script will not be automatically deleted. When a script contains templates, script resources will not get cleaned up until the entities associated with the templates are deleted. + +### Run multiple times +A script can be ran multiple times by using the `ecs_script_parse` and `ecs_script_eval` functions. Example: + +```c +const char *code = "my_spaceship {}"; + +ecs_script_t *script = ecs_script_parse( + world, "my_script_name", code); +if (!script) { + // error +} + +if (ecs_script_eval(script)) { + // error +} + +// Run again +if (ecs_script_eval(script)) { +} + +// Free script resources +ecs_script_free(script); +``` + +If a script fails, the entities created by the script will not be automatically deleted. When a script contains templates, script resources will not get cleaned up until the entities associated with the templates are deleted. + +### Managed script +Managed scripts are scripts that are associated with an entity, and can be ran multiple times. Entities created by a managed script are tagged with the script. When script execution fails, the entities associated with the script will be deleted. Additionally, if after executing the script again an entity is no longer created by the script, it will also be deleted. + +To run a managed script, do: + +```c +const char *code = "my_spaceship {}"; + +ecs_entity_t s = ecs_script(world, { + .code = code +}); + +if (!s) { + // error +} +``` + +To update the code of a managed script, use the `ecs_script_update` function: + +```c +if (ecs_script_update(world, s, 0, new_code)) { + // error +} +``` + +When a script contains templates, script resources will not get cleaned up until the entities associated with the templates are deleted. diff --git a/vendors/flecs/docs/FlecsScriptTutorial.md b/vendors/flecs/docs/FlecsScriptTutorial.md index adcd017b5..248703a3d 100644 --- a/vendors/flecs/docs/FlecsScriptTutorial.md +++ b/vendors/flecs/docs/FlecsScriptTutorial.md @@ -1,11 +1,13 @@ +This tutorial hasn't been updated for v4 yet! Use the [Script Manual](FlecsScript.md) for now. + # Flecs Script Tutorial This tutorial shows you how to use Flecs script, which is a declarative language for creating entities without having to write and compile code. Flecs script can be used for different things, such as building scenes, assets, or as a language for configuration files. In this tutorial we will be designing a simple fence asset from scratch, and make it parameterizable so it can be easily reused across scenes. By the end of the tutorial we'll have an asset that looks like this: -[![preview of fence asset](img/script_tutorial/tut_playground_preview.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Enclosing%0A%0Afence_a%20%7B%0A%20%20-%20Enclosing%7B%7D%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B21%2C%2019%2C%20-37%7D%0A%20%20-%20Rotation3%7B-0.42%2C%20-0.52%7D%0A%7D%0A) +[![preview of fence asset](img/script_tutorial/tut_playground_preview.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atemplate%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20template%20Fence%0A%0A%0Atemplate%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%7D%20%2F%2F%20template%20Enclosing%0A%0Afence_a%20%7B%0A%20%20-%20Enclosing%7B%7D%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B21%2C%2019%2C%20-37%7D%0A%20%20-%20Rotation3%7B-0.42%2C%20-0.52%7D%0A%7D%0A) -Note: in order to use Flecs script an app needs to be built with the `FLECS_PLECS` addon. +Note: in order to use Flecs script an app needs to be built with the `FLECS_SCRIPT` addon. ## Getting Started with the Explorer The Flecs explorer is a web-based application that lets us write scripts and see the results directly. In the tutorial we will use the explorer in combination with the Flecs playground, which has a rendering canvas and comes preloaded with a number of modules and assets. @@ -145,7 +147,7 @@ Now add these lines into the editor to create our ground `plane`: ```js plane { - Position3{} - - Rectangle{100, 100} + Rectangle: {100, 100} - Rgb{0.9, 0.9, 0.9} } ``` @@ -181,8 +183,8 @@ Let's now add a cube to the scene. The code for this looks similar to the plane: ```js box { - Position3{} - - Box{10, 10, 10} - - Rgb{1, 0, 0} + Box: {10, 10, 10} + Rgb: {1, 0, 0} } ``` @@ -195,8 +197,8 @@ To fix this, we can move it up by setting the `y` member of `Position3` to half ```js box { - Position3{y: 5} - - Box{10, 10, 10} - - Rgb{1, 0, 0} + Box: {10, 10, 10} + Rgb: {1, 0, 0} } ``` @@ -223,11 +225,11 @@ Inside the `with` statement we can now create two box entities for the left and with Rgb{0.15, 0.1, 0.05} { left_pillar { - Position3{x: -10, y: 5} - - Box{2, 10, 2} + Box: {2, 10, 2} } right_pillar { - Position3{x: 10, y: 5} - - Box{2, 10, 2} + Box: {2, 10, 2} } } ``` @@ -241,7 +243,7 @@ That works, but the code is starting to look a bit unwieldy. There are lots of m First lets define two variables for the color and box shape of the pillars. We already saw an example of a variable when we defined `PI`. These looks similar, except that because they are composite values, we also define their type: ```js -const color : Rgb = {0.15, 0.1, 0.05} +const color = Rgb: {0.15, 0.1, 0.05} const pillar_box : Box = {2, 10, 2} ``` @@ -249,7 +251,7 @@ This is better, but `pillar_box` still contains values that we have to update ea ```js const height = 10 -const color : Rgb = {0.15, 0.1, 0.05} +const color = Rgb: {0.15, 0.1, 0.05} const pillar_width = 2 const pillar_box : Box = { @@ -287,7 +289,7 @@ This is no fence yet. We're still missing the crossbars. Let's add another entit ```js bar { - Position3{y: $height / 2} - - Box{$width, 2, 1} + Box: {$width, 2, 1} - $color } ``` @@ -299,12 +301,12 @@ Let's add a second entity and space the two entities out a bit so they don't ove ```js top_bar { - Position3{y: $height/2 + 2} - - Box{$width, 2, 1} + Box: {$width, 2, 1} - $color } bottom_bar { - Position3{y: $height/2 - 2} - - Box{$width, 2, 1} + Box: {$width, 2, 1} - $color } ``` @@ -411,9 +413,9 @@ We could save this script as is, load it into our game, and instantiate the pref ```c // In C -ecs_plecs_from_file(world, "fence.flecs"); +ecs_script_run_file(world, "fence.flecs"); -ecs_entity_t fence = ecs_lookup_fullpath(world, "Fence"); +ecs_entity_t fence = ecs_lookup(world, "Fence"); ecs_entity_t fence_a = ecs_new_w_pair(world, EcsIsA, fence); ecs_entity_t fence_b = ecs_new_w_pair(world, EcsIsA, fence); @@ -424,7 +426,7 @@ ecs_set(world, fence_b, EcsPosition3, {10}); // In C++ using namespace flecs::components::transform; -ecs_plecs_from_file(world, "fence.flecs"); +ecs_script_run_file(world, "fence.flecs"); auto fence = world.lookup("Fence"); auto fence_a = world.entity().is_a(fence); @@ -438,10 +440,10 @@ We still have some problems, which we'll address in the next section: - Our prefab is static, we cannot change the parameters of the fence - The pillars and bars don't increase when the fence size increases -## Assemblies -Prefabs are static collections of entities and components that we can instantiate multiple times. Assemblies are _prefab compositions_ that can be parameterized. In other words, assemblies let us to create our Fence, while also specifying the width and height. +## Templates +Prefabs are static collections of entities and components that we can instantiate multiple times. Templates are _prefab compositions_ that can be parameterized. In other words, templates let us to create our Fence, while also specifying the width and height. -Changing the above code into an assembly is easy. Just change this line: +Changing the above code into an template is easy. Just change this line: ```js Prefab Fence { @@ -452,7 +454,7 @@ Prefab Fence { into this: ```js -assembly Fence { +template Fence { // fence code } ``` @@ -460,22 +462,22 @@ assembly Fence { The editor will throw the following error: ``` -assembly 'Fence' has no properties +template 'Fence' has no properties ``` -[![a fence assembly](img/script_tutorial/tut_playground_assembly_error.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20const%20width%20%3D%2020%0A%20%20const%20height%20%3D%2010%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B10%7D%0A%7D%0A) +[![a fence template](img/script_tutorial/tut_playground_template_error.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atemplate%20Fence%20%7B%0A%20%20const%20width%20%3D%2020%0A%20%20const%20height%20%3D%2010%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B10%7D%0A%7D%0A) -What does this mean? Assemblies, unlike prefabs, need _properties_, which are like the inputs to the assembly. For our fence, these could be `width` and `heigth` values. +What does this mean? Templates, unlike prefabs, need _properties_, which are like the inputs to the template. For our fence, these could be `width` and `height` values. -To add properties to the assembly, we can take some of our existing `const` variables, and change `const` to `prop`. This will make the variables visible to the outside. Another thing we must do for properties is explicitly specify their type. +To add properties to the template, we can take some of our existing `const` variables, and change `const` to `prop`. This will make the variables visible to the outside. Another thing we must do for properties is explicitly specify their type. -Let's take the existing `width`, `height` and `color` variables, and change them to properties. It is also good practice to put the props at the top of the assembly code, so it's easy to see which inputs it has: +Let's take the existing `width`, `height` and `color` variables, and change them to properties. It is also good practice to put the props at the top of the template code, so it's easy to see which inputs it has: ```js -assembly Fence { - prop width : flecs.meta.f32 = 20 - prop height : flecs.meta.f32 = 10 - prop color : Rgb = {0.15, 0.1, 0.05} +template Fence { + prop width = flecs.meta.f32: 20 + prop height = flecs.meta.f32: 10 + prop color = Rgb: {0.15, 0.1, 0.05} // fence code } @@ -490,16 +492,16 @@ using flecs.meta The code can now be changed to this: ```js -assembly Fence { - prop width : f32 = 20 - prop height : f32 = 10 - prop color : Rgb = {0.15, 0.1, 0.05} +template Fence { + prop width = f32: 20 + prop height = f32: 10 + prop color = Rgb: {0.15, 0.1, 0.05} // fence code } ``` -Note that while we were doing this, the fence disappeared again from the canvas. This happened because while we _inherit_ a prefab, we _assign_ an assembly. To make the fences visible again, change the code that creates the fences to this: +Note that while we were doing this, the fence disappeared again from the canvas. This happened because while we _inherit_ a prefab, we _assign_ an template. To make the fences visible again, change the code that creates the fences to this: ```js fence_a { @@ -516,20 +518,20 @@ The fences are back, with the default values we provided to our props. But we no ```js fence_a { - - Fence{width: 10, height: 20} + Fence: {width: 10, height: 20} - Position3{-10} } fence_b { - - Fence{width: 25, height: 10} + Fence: {width: 25, height: 10} - Position3{10} } ``` -[![two instantiated fence assemblies](img/script_tutorial/tut_playground_assembly.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2020%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%7B%0A%20%20-%20Fence%7Bwidth%3A%2010%2C%20height%3A%2020%7D%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%7B%0A%20%20-%20Fence%7Bwidth%3A%2025%2C%20height%3A%2010%7D%0A%20%20-%20Position3%7B10%7D%0A%7D%0A) +[![two instantiated fence templates](img/script_tutorial/tut_playground_template.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atemplate%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2020%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%7B%0A%20%20-%20Fence%7Bwidth%3A%2010%2C%20height%3A%2020%7D%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%7B%0A%20%20-%20Fence%7Bwidth%3A%2025%2C%20height%3A%2010%7D%0A%20%20-%20Position3%7B10%7D%0A%7D%0A) -We are now well on our way to define a procedural fence. Note how similar assigning an assembly looks to assigning a component. This is no coincidence: an assembly is translated to a component, where each property becomes a member of that component. +We are now well on our way to define a procedural fence. Note how similar assigning an template looks to assigning a component. This is no coincidence: an template is translated to a component, where each property becomes a member of that component. -This is something we can exploit in C/C++ code to make assigning assemblies to entities as easy as assigning a component: +This is something we can exploit in C/C++ code to make assigning templates to entities as easy as assigning a component: ```c // In C @@ -542,13 +544,13 @@ typedef struct { // Register a regular component ECS_COMPONENT(world, Fence); -// Because the Fence assembly has the same name as the +// Because the Fence template has the same name as the // component it will "bind" to it. -ecs_plecs_from_file(world, "fence.flecs"); +ecs_script_run_file(world, "fence.flecs"); // Set the component as usual -ecs_entity_t fence_a = ecs_set(world, 0, Fence, {10, 20}); -ecs_entity_t fence_b = ecs_set(world, 0, Fence, {25, 10}); +ecs_entity_t fence_a = ecs_insert(world, ecs_value(Fence, {10, 20})); +ecs_entity_t fence_b = ecs_insert(world, ecs_value(Fence, {25, 10})); ecs_set(world, fence_a, EcsPosition3, {-10}); ecs_set(world, fence_b, EcsPosition3, {10}); @@ -563,9 +565,9 @@ struct Fence { flecs::components::graphics::Rgb color; } -// Because the Fence assembly has the same name as the +// Because the Fence template has the same name as the // component it will "bind" to it. -ecs_plecs_from_file(world, "fence.flecs"); +ecs_script_run_file(world, "fence.flecs"); auto fence_a = world.entity().set({10, 20}); auto fence_b = world.entity().set({25, 10}); @@ -644,12 +646,12 @@ fence :- Fence{} And increase the `width` of the fence to `60`: ```js - prop width : f32 = 60 + prop width = f32: 60 ``` We now get a number of pillars that matches the fence length: -[![a fence with multiple pillars](img/script_tutorial/tut_playground_grid.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2060%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20Prefab%20Pillar%20%7B%0A%20%20%20%20-%20%24color%0A%20%20%20%20-%20%24pillar_box%0A%20%20%7D%0A%20%20%20%20%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%0A%20%20%20%20%20%20x.spacing%3A%20%24pillar_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%20%20%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence%20%3A-%20Fence%7B%7D%0A) +[![a fence with multiple pillars](img/script_tutorial/tut_playground_grid.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atemplate%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2060%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20Prefab%20Pillar%20%7B%0A%20%20%20%20-%20%24color%0A%20%20%20%20-%20%24pillar_box%0A%20%20%7D%0A%20%20%20%20%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%0A%20%20%20%20%20%20x.spacing%3A%20%24pillar_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%20%20%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence%20%3A-%20Fence%7B%7D%0A) There's still something off though, which is that the pillars don't exactly line up with the ends up of the fence. We can fix this with a simple calculation that takes the number of pillars, and uses that to recompute the grid spacing. @@ -659,30 +661,30 @@ There's still something off though, which is that the pillars don't exactly line Note that this takes into account that there is one more pillar than there are spaces between pillars. When we replace `pillar_spacing` in the grid with `grid_spacing`, we get the correct pillar alignment: -[![a fence with correctly aligned pillars](img/script_tutorial/tut_playground_pillar_grid.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2060%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20Prefab%20Pillar%20%7B%0A%20%20%20%20-%20%24color%0A%20%20%20%20-%20%24pillar_box%0A%20%20%7D%0A%20%20%20%20%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%0A%20%20%20%20%20%20x.spacing%3A%20%24grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%20%20%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%7B%0A%20%20-%20Fence%7B%7D%0A%20%20-%20Position3%7B-10%7D%0A%7D%0A) +[![a fence with correctly aligned pillars](img/script_tutorial/tut_playground_pillar_grid.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atemplate%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2060%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20Prefab%20Pillar%20%7B%0A%20%20%20%20-%20%24color%0A%20%20%20%20-%20%24pillar_box%0A%20%20%7D%0A%20%20%20%20%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%0A%20%20%20%20%20%20x.spacing%3A%20%24grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%20%20%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%7B%0A%20%20-%20Fence%7B%7D%0A%20%20-%20Position3%7B-10%7D%0A%7D%0A) We can do the same thing for the bars. I'll skip right to the result since it involves very similar steps: -[![a fence with multiple pillars and bars](img/script_tutorial/tut_playground_grids.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0A%2F%2F%20The%20ground%20plane%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%2F2%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0Afence%20%3A-%20Fence%7B%7D) +[![a fence with multiple pillars and bars](img/script_tutorial/tut_playground_grids.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0A%2F%2F%20The%20ground%20plane%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%2F2%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atemplate%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20template%20Fence%0A%0Afence%20%3A-%20Fence%7B%7D) The values got tweaked a bit to get a more aesthetically pleasing result when scaling up. Feel free to tune the variables to values that produce something you like! -We're almost done! There is only one thing left, which is to combine the fence assembly in a nested assembly so we can create an enclosing. +We're almost done! There is only one thing left, which is to combine the fence template in a nested template so we can create an enclosing. -## Nested Assemblies -Now that we have our Fence assembly ready to go, we can build a higher level assemblies which reuses existing the `Fence` assembly. One typical thing we could do is instantiate a `Fence` four times, so that it creates an enclosed space. +## Nested Templates +Now that we have our Fence template ready to go, we can build a higher level templates which reuses existing the `Fence` template. One typical thing we could do is instantiate a `Fence` four times, so that it creates an enclosed space. -The code for this doesn't introduce anything beyond what we've already learned. Let's first setup a new assembly at the bottom of our script. -Inside the assembly, lets create `width` and `depth` properties, so we can define a rectangular area. Let's also add a `color` and `height` property that we can passthrough to our `Fence` assembly. +The code for this doesn't introduce anything beyond what we've already learned. Let's first setup a new template at the bottom of our script. +Inside the template, lets create `width` and `depth` properties, so we can define a rectangular area. Let's also add a `color` and `height` property that we can passthrough to our `Fence` template. When put together, this is what it looks like: ```js -assembly Enclosing { - prop width: f32 = 40 - prop height: f32 = 10 - prop depth: f32 = 40 - prop color: Rgb = {0.15, 0.1, 0.05} +template Enclosing { + prop width = f32: 40 + prop height = f32: 10 + prop depth = f32: 40 + prop color = Rgb: {0.15, 0.1, 0.05} // enclosing code goes here } @@ -699,26 +701,26 @@ up writing the same divisions multiple times. left { - Position3{x: -$width_half} - Rotation3{y: $PI/2} - - Fence{width: $depth, height:$, color:$} + Fence: {width: $depth, height:$, color:$} } right { - Position3{x: $width_half} - Rotation3{y: $PI/2} - - Fence{width: $depth, height:$, color:$} + Fence: {width: $depth, height:$, color:$} } back { - Position3{z: -$depth_half} - - Fence{width: $width, height:$, color:$} + Fence: {width: $width, height:$, color:$} } front { - Position3{z: $depth_half} - - Fence{width: $width, height:$, color:$} + Fence: {width: $width, height:$, color:$} } ``` -Note how the passthrough parameters are specified as `height:$`. This is a shorthand notation for `height: $height`, and can save a lot of typing when working with nested assemblies. +Note how the passthrough parameters are specified as `height:$`. This is a shorthand notation for `height: $height`, and can save a lot of typing when working with nested templates. -Let's now use our new `Enclosing` assembly by changing +Let's now use our new `Enclosing` template by changing ```js fence :- Fence{} @@ -732,17 +734,17 @@ enclosing :- Enclosing{} Here is what that looks like. You might need to zoom out a bit with the camera to see the full fence. Remember that you can do this by first clicking on the canvas to give it focus, then using the WASD keys to move the camera around. Don't forget to click on the canvas again to give focus back to the explorer. -[![an enclosing](img/script_tutorial/tut_playground_pasture.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Enclosing%0A%0Afence_a%20%7B%0A%20%20-%20Enclosing%7B%7D%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B21%2C%2019%2C%20-37%7D%0A%20%20-%20Rotation3%7B-0.42%2C%20-0.52%7D%0A%7D%0A) +[![an enclosing](img/script_tutorial/tut_playground_pasture.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atemplate%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20template%20Fence%0A%0A%0Atemplate%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%7D%20%2F%2F%20template%20Enclosing%0A%0Afence_a%20%7B%0A%20%20-%20Enclosing%7B%7D%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B21%2C%2019%2C%20-37%7D%0A%20%20-%20Rotation3%7B-0.42%2C%20-0.52%7D%0A%7D%0A) We can now easily modify our enclosing by passing in parameters for width and height: ``` -enclosing :- Enclosing{width: 100, height: 30} +enclosing { Enclosing: {width: 100, height: 30} } ``` -[![a tall enclosing](img/script_tutorial/tut_playground_pasture_2.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0A%2F%2F%20The%20ground%20plane%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%2F2%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%7D%0A%0Apasture%20%3A-%20Enclosing%7B%0A%20%20width%3A%20100%2C%20%0A%20%20depth%3A%20100%0A%20%20height%3A%2030%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B52%2C%2041%2C%20-82%7D%0A%20%20-%20Rotation3%7B-0.45%2C%20-0.54%7D%0A%7D%0A) +[![a tall enclosing](img/script_tutorial/tut_playground_pasture_2.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0A%2F%2F%20The%20ground%20plane%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%2F2%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atemplate%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Atemplate%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%7D%0A%0Apasture%20%3A-%20Enclosing%7B%0A%20%20width%3A%20100%2C%20%0A%20%20depth%3A%20100%0A%20%20height%3A%2030%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B52%2C%2041%2C%20-82%7D%0A%20%20-%20Rotation3%7B-0.45%2C%20-0.54%7D%0A%7D%0A) -Congratulations! You now know how to create assets in Flecs Script! If you'd like to see more examples of how assemblies can be used to create complex compositions of assets, take a look at the assemblies in the Flecs playground project: +Congratulations! You now know how to create assets in Flecs Script! If you'd like to see more examples of how templates can be used to create complex compositions of assets, take a look at the templates in the Flecs playground project: https://github.com/flecs-hub/playground/tree/main/etc/assets @@ -750,20 +752,20 @@ Additionally you can also try instantiating one of the assets that come preloade ```js using flecs.components.* -using assemblies +using templates const PI = 3.1415926 plane { - Position3{} - Rotation3{$PI / 2} - - Rectangle{10000, 10000} + Rectangle: {10000, 10000} - Rgb{0.9, 0.9, 0.9} } town :- Town{} ``` -[![a town](img/script_tutorial/tut_playground_town.png)](https://www.flecs.dev/explorer/?show=plecs,explorer_canvas&local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20assemblies%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atown%20%3A-%20Town%7B%7D) +[![a town](img/script_tutorial/tut_playground_town.png)](https://www.flecs.dev/explorer/?show=flecs script,explorer_canvas&local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20templates%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atown%20%3A-%20Town%7B%7D) That's all for this tutorial. Have fun creating, and don't hesitate to share the results in the `#showcase` channel on Discord! (link: https://discord.gg/caR2WmY) diff --git a/vendors/flecs/docs/JsonFormat.md b/vendors/flecs/docs/JsonFormat.md deleted file mode 100644 index 01ff01b31..000000000 --- a/vendors/flecs/docs/JsonFormat.md +++ /dev/null @@ -1,638 +0,0 @@ -# JSON format -This document provides an overview of the JSON serializer format. For an overview of how to use the JSON serializer API, [see the JSON addon documentation](https://www.flecs.dev/flecs/group__c__addons__json.html). - -## Value kinds -This section describes value kinds that are used by the JSON serializers. - -### Name -A name field returns the name of an entity as returned by `ecs_get_name` or `flecs::entity::name`. - -**Example**: -```json -"Earth" -``` - -### Path -A path field returns the path identifier of an entity as returned by `ecs_get_fullpath` or `flecs::entity::path`. The path contains the entity's name and the names of its parent and grandparents separated by a dot (`.`). - -**Example**: -```json -"SolarSystem.Sun.Earth" -``` - -### Label -A label field returns the doc name of the entity as returned by `ecs_doc_get_name` or `flecs::entity::doc_name`. The doc name is a user-friendly name for the entity that does not need to be unique and may contain any character. - -Serializing labels requires the [doc](https://www.flecs.dev/flecs/group__c__addons__doc.html) addon. - -**Example**: -```json -"The Earth" -``` - -### Id -An id field returns a component id or pair. It is formatted as an array of at least one and at most three elements where the elements have the following meaning: - -- Component _or_ First element of pair -- Second element of pair -- Type flag - -Each element is formatted as a [Path](#path). - -**Example**: -```json -["transform.Position"] -["Likes", "Apples"] -["Movement", 0, "SWITCH"] -``` - -### Id** -Same as [Id](#id) but with [Label](#label)'s instead of paths. - -**Example**: -```json -["Entity position"] -["Located in", "San Francisco"] -``` - -### Value -A JSON object or scalar representing the value of a component. Component values can only be serialized if the component is described in the reflection framework (see the [meta addon](https://www.flecs.dev/flecs/group__c__addons__meta.html)). - -When a component has no value (e.g. a tag) or is not described by the reflection framework, `0` will be used as placeholder. - -**Example**: -```json -{"x": 10, "y": 20} -``` - -### Type** -A JSON object that represents the structure of a component type. - -**Example**: -```json -{ - "type_info": [{ - "x": ["float"], - "y": ["float"] - }, 0, 0] -} -``` - -### Entity -The entity kind is an object that contains metadata and data of a single serialized entity, and is returned by the [Entity serializer](#entity-serializer). - -The following sections describe the fields that an object of the entity kind may contain: - -#### "path -The entity path. - -Type: [Path](#path), optional - -#### "label -The entity doc name. - -Type: [Label](#label), optional - -#### "brief -Brief description (as returned by `ecs_doc_get_brief`). - -Type: string, optional - -#### "link -Link to URL (as returned by `ecs_doc_get_link`). - -Type: string, optional - -#### "is_a -Base entities. - -Type: Array([Entity](#entity)), optional - -#### "ids -Component ids. - -Type: Array([Id](#id)) - -#### "id_labels -Component labels. - -Type: Array([Id label](#id-label)), optional - -#### "hidden -Array indicating whether a component is hidden. Only applies to components of base entities (in the ["is_a"](is_a) array). - -A base component is hidden when it is overridden by an entity higher in the inheritance hierarchy. - -Type: Array(bool), optional - -#### "values -Component values. - -Type: Array([Value](#value)), optional - -#### "type_info -Array with objects that describe the component types of the terms. - -Type: Array([Type info](#type-info)), optional - -#### Example -Default serializer options: -```json -{ - "path":"Sun.Earth", - "ids":[ - ["Position"], - ["flecs.core.ChildOf", "Sun"], - ["flecs.core.Identifier", "flecs.core.Name"] - ] -} -``` - -This example shows an entity with a base (an `IsA` relationship) with default serializer options: -```json -{ - "path":"Sun.Earth", - "is_a": [{ - "path":"planets.RockyPlanet", - "ids": [ - ["planets.HasRocks"] - ] - }], - "ids":[ - ["Position"], - ["flecs.core.ChildOf", "Sun"], - ["flecs.core.Identifier", "flecs.core.Name"] - ] -} -``` - -This example shows an entity with a base with all serializer options enabled: -```json -{ - "path":"Sun.Earth", - "label": "The Earth", - "brief": "The planet you call home", - "link": "www.earth.com", - "is_a": [{ - "path":"planets.RockyPlanet", - "label":"A rocky planet", - "ids": [ - ["Position"], - ["planets.HasRocks"] - ], - "id_labels": [ - ["Position"], - ["HasRocks"] - ], - "values":[0, 0], - "hidden":[true, false] - }], - "ids":[ - ["Position"], - ["flecs.core.ChildOf", "Sun"], - ["flecs.core.Identifier", "flecs.core.Name"] - ], - "ids_labels":[ - ["Position"], - ["ChildOf", "Sun"], - ["Identifier", "Name"] - ], - "values":[ - {"x": 10, "y": 20}, - 0, 0 - ], - "type_info": [{ - "x": ["float"], - "y": ["float"] - }, 0, 0] -} -``` - -### Result -The result kind is an object that contains the metadata and data of a single result returned by an iterator (see [Iterator](#iterator)). - -The following sections describe the fields that an object of the entity kind may contain: - -#### parent -Parent of the entity in current result. - -Type: Array([Path](#path)), optional - -#### entities -Array with paths of the returned entities. - -Type: Array([Name](#name)), optional - -#### entity_labels -Same as [entities](#entities), but with [Label](#label)'s instead of paths. - -Type: Array([Label](#label)), optional - -#### entity_ids -Same as [entities](#entities), but with numerical ids instead of paths. - -Type: Array(Number), optional - -#### sources -Array with paths of sources for each term. A subject indicates the actual entity on which a component is stored. If this is the matched entity (default) the array will contain a `0` element. - -Type: Array([Path](#path)), optional - -#### variables -Array with variable values for current result (see [query variables](Queries.md#variables)). - -Type: Array([Path](#path)), optional - -#### variable_labels -Same as [variables](#variables), but with [Label](#label)'s instead of paths. - -Type: Array([Label](#label)), optional - -#### variable_ids -Same as [variables](#variables), but with numerical ids instead of paths. - -Type: Array(Number), optional - -#### ids -Array with component ids. This list is more specific than the ids array provided by the top-level iterator object. The arrays can be different in the case of terms with an `Or` operator, or terms with wildcard ids. - -Type: Array(string), optional - -#### is_set -Array with booleans that can be used to determine whether terms with the `Optional` operator are set. - -Type: Array(bool), optional - -#### values -Array with component values. The array contains an element for each term. If a component has no value, or no value could be serialized for the component a `[]` element is added. - -Each element in the array can be either an array with component values when the component is from the matched entity, or a single component value when the component is from another entity (like a parent, prefab, singleton). - -Type: Array(Array([Value](#value))), optional - -### Iterator -The iterator kind is an object that contains metadata and data of all the entities yielded by an iterator. - -#### "ids -Array with ids for each term. - -Type: Array(string), optional - -#### "variables -Array with variable names (see [query variables](https://www.flecs.dev/flecs/md_docs_Queries.html/#variables)). - -Type: Array(string), optional - -#### "results -Array with elements for each returned result. - -Type: Array([Result](#result)) - -#### "eval_duration -Time it took to serialize the iterator. - -Type: Number - -#### "type_info -Array with objects that describe the component types of the terms. - -Type: Array([Type info](#type-info)), optional - -#### Example - -```json -{ - "ids": ["Position", "Jedi"], - "results": [{ - "entities": ["Luke", "Yoda"], - "values": [ - [{ "x": 10, "y": 20 }, - { "x": 20, "y": 30 }], - 0 - ] - }] -} -``` - -## Entity serializer -The entity serializer returns a JSON string of the [Entity](#entity) type. This format is returned by `ecs_entity_to_json` and `flecs::entity::to_json`. - -### Serializer options -The following options (found in `ecs_entity_to_json_desc_t`) customize the output of the entity serializer: - -#### serialize_path -Serialize the ["path"](#path-1) member. - -**Default**: `true` - -**Example**: -```json -{"path": "SolarSystem.Sun.Earth"} -``` - -#### serialize_label -Serialize the ["label"](#label-1) member. - -**Default**: `false` - -**Example**: -```json -{"label": "Rocky planet"} -``` - -#### serialize_brief -Serialize the ["brief"](#brief) member. - -**Default**: `false` - -**Example**: -```json -{"brief": "A rocky planet"} -``` - -#### serialize_link -Serialize the ["link"](#link) member. - -**Default**: `false` - -**Example**: -```json -{"brief": "www.rocky-planet.com"} -``` - -#### serialize_id_labels -Serializes the ["id_labels"](#id_labels) member. - -**Default**: `false` - -**Example**: -```json -{"id_labels": [["A Rocky Planet"], ["Has surface water"]]} -``` - -#### serialize_base -Serializes the ["is_a"](#is_a) member. - -**Default**: `true` - -**Example**: -```json -{"is_a": [{ - "path":"planets.RockyPlanet", - "ids": [ - ["planets.HasRocks"] - ] -}]} -``` - -#### serialize_private -Serialize private components. Private components are regular components with the `EcsPrivate` (or `flecs::Private`) tag. When `serialize_private` is false, private components will be hidden from all arrays. - -**Example**: -```json -{"ids": ["Position", "PrivateComponent"]} -``` - -**Default**: `false` - -#### serialize_hidden -Serializes the ["hidden"](#hidden) member. - -**Example**: -```json -{"hidden":[true, false]} -``` - -Full example: -```json -{ - "path":"Sun.Earth", - "is_a": [{ - "path":"planets.RockyPlanet", - "ids": [ - ["Position"], - ["planets.HasRocks"] - ], - "hidden":[true, false] - }], - "ids":[ - ["Position"], - ["flecs.core.ChildOf", "Sun"], - ["flecs.core.Identifier", "flecs.core.Name"] - ] -} -``` -Note that `hidden[0]` is `true` in this example, because the `Position` component from `planets.RockyPlanet` is overridden by the entity. - -**Default**: `false` - -#### serialize_values -Serializes the ["values"](#values) member. - -**Example**: -```json -{"values":[{"x":10, "y": 20}, 0]} -``` - -**Default**: `false` - -#### serialize_type_info -Serialize the ["type_info"](#type_info) member. - -**Example**: -```json -{ - "type_info": [{ - "x": ["float"], - "y": ["float"] - }, 0, 0] -} -``` - -## Iterator serializer -The entity serializer returns a JSON string of the [Iterator](#iterator) type. This format is returned by `ecs_iter_to_json`. - -### Serializer options - -#### serialize_term_ids -Serialize the top level ["ids"](#term_ids) member. - -**Default**: `true` - -**Example**: - -Example result for query `Position, Jedi` -```json -{ - "ids": ["Position", "Jedi"], - "results": [ ] -} -``` - -#### serialize_ids -Serialize the result specific ["ids"](#ids-1) member. - -If the iterated query does not contain variable ids (either an `Or` term or a term with a wildcard id) the result specific `ids` member will exactly match the top-level `ids` member. - -**Default**: `true` - -**Example**: - -Example result for query `Position, (Likes, *)` -```json -{ - "ids": ["Position", "(Likes,*)"], - "results": [{ - "ids": ["Position", "(Likes,Apples)"] - }] -} -``` - -#### serialize_sources -Serialize the ["sources"](#sources) member. - -**Default**: `true` - -**Example**: - -Example result for query `Position, Position(parent)` -```json -{ - "ids": ["Position", "Position"], - "results": [{ - "entities": ["Parent.A", "Parent.B"], - "sources": [0, "Parent"] - }] -} -``` - -#### serialize_variables -Serialize the ["variables"](#variables) member. - -**Default**: `true` - -**Example**: - -Example result for query `Position, (Likes, _Food)` -```json -{ - "variables": ["Food"], - "results": [{ - "ids": ["Position", "(Likes,Apples)"], - "variables": ["Apples"], - }] -} -``` - -#### serialize_is_set -Serialize the ["is_set"](#is_set) member. - -**Default**: `true` - -**Example**: - -Example result for query `Position, ?Velocity` -```json -{ - "ids": ["Position", "Velocity"], - "results": [{ - "is_set": [true, false] - }] -} -``` - -#### serialize_values -Serialize the ["values"](#values) member. - -**Default**: `true` - -**Example**: - -Example result for query `Position` -```json -{ - "ids": ["Position"], - "results": [{ - "values": [ - [{ - "x": 10, - "y": 20 - }] - ] - }] -} -``` - -#### serialize_entities -Serialize the ["entities"](#entities) member. - -**Default**: `true` - -**Example**: -```json -{ - "results": [{ - "entities": ["MyEntity"] - }] -} -``` - -#### serialize_entity_labels -Serialize the ["entity_labels"](#entity_labels) member. - -**Default**: `false` - -**Example**: -```json -{ - "results": [{ - "entities": ["Parent.MyEntity"], - "entity_labels": ["My entity"] - }] -} -``` - -#### serialize_variable_labels -Serialize the ["variable_labels"](#variable_labels) member. - -**Default**: `false` - -**Example**: -```json -{ - "results": [{ - "variables": ["GrannySmith"], - "variable_labels": ["Granny smith"] - }] -} -``` - -#### measure_eval_duration -Serialize the ["eval_duration"](#eval_duration) member. - -**Default**: `false` - -**Example**: -```json -{ - "eval_duration": 0.001 -} -``` - -#### serialize_type_info -Serialize the ["type_info"](#type_info_1) member. - -**Default**: `false` - -**Example**: -```json -{ - "ids": ["Position", "Jedi"], - "type_info": [{ - "x": ["float"], - "y": ["float"] - }, 0], - "results": [{ - }] -} -``` diff --git a/vendors/flecs/docs/Manual.md b/vendors/flecs/docs/Manual.md index 0fd8c9454..791a36d0d 100644 --- a/vendors/flecs/docs/Manual.md +++ b/vendors/flecs/docs/Manual.md @@ -23,24 +23,6 @@ Flecs is used with other frameworks and game engines, and as such not all of its ### 6. Have fun! There are few things as satisfying as building games. If nothing else, Flecs has been built to enable creative visions both big and small. I'm having a lot of fun building Flecs, I hope you will have fun using it, and that your users will have fun playing your games :) -## Diagrams - -### High level architecture -This diagram provides an overview of how entities, components, tables, queries, filters and systems are wired together. -![Architecture diagram](img/flecs-architecture-overview.png) - -### Component add flow -This diagram provides an overview of the different steps that occur when adding a component to an entity. The diagram shows when component lifecycle callbacks, OnAdd triggers, OnSet systems, UnSet systems and monitors are invoked. Additionally the diagram shows how the defer mechanism is integrated with the listed Flecs operations. -![Component add flow](img/flecs-add-component-flow.png) - -### Component remove flow -This diagram provides an overview of the different steps that occur when removing a component from an entity. The diagram shows when component lifecycle callbacks, OnRemove triggers, OnSet systems, UnSet systems and monitors are invoked. Additionally the diagram shows how the defer mechanism is integrated with the listed Flecs operations. -![Component remove flow](img/flecs-remove-component-flow.png) - -### Staging flow -This diagram provides an overview of what happens when an application uses staging. Staging is a lockless mechanism that lets threads concurrently read & perform structural changes on the store. Changes are temporarily stored in a command queue per stage, which can be merged with the store when convenient. -![Staging flow](img/flecs-staging-flow.png) - ## API design ### Naming conventions @@ -60,8 +42,8 @@ typedef struct Velocity { // System names ('Move') use PascalCase. API types use snake_case_t void Move(ecs_iter_t *it) { // Functions use snake_case - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i++) { p[i].x += v[i].x; @@ -83,7 +65,7 @@ int main(int argc, char *argv[]) { ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); // Function wrapper macros use snake_case - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); // Builtin entities use PascalCase ecs_add(world, EcsWorld, Position); @@ -154,10 +136,10 @@ The `ecs_get_name` operation is an example where the framework retains ownership const char *name = ecs_get_name(world, e); ``` -The `ecs_get_fullpath` operation is an example where the ownership is transferred to the application: +The `ecs_get_path` operation is an example where the ownership is transferred to the application: ```c -char *path = ecs_get_fullpath(world, e); +char *path = ecs_get_path(world, e); ``` Memory for which ownership has been transferred to the application will need to be freed by the application. This should be done by the `ecs_os_free` operation: @@ -166,79 +148,6 @@ Memory for which ownership has been transferred to the application will need to ecs_os_free(path); ``` -### Entity names -An application can assign names to entities. Names can be assigned at entity creation, with the `ecs_entity_init` function: - -```c -ecs_entity_t e = ecs_entity(world, { .name = "MyEntity" }); -``` - -Alternatively, names can be assigned afterwards with the `ecs_set_name` function: - -```c -ecs_set_name(world, e, "MyEntity"); -``` - -The `ecs_set_name` function may be used as a shortcut to create a new named entity by providing 0 for the entity argument: - -```c -ecs_entity_t e = ecs_set_name(world, 0, "MyEntity"); -``` - -The name of an entity can be retrieved with the `ecs_get_name` function: - -```c -printf("Name = %s\n", ecs_get_name(world, e)); -``` - -The entity name is stored in `(EcsIdentifier, EcsName)`. Alternatively, the name can be retrieved with `ecs_get_pair`: -```c -const EcsIdentifier *ptr = ecs_get_pair(world, e, EcsIdentifier, EcsName); -printf("Name = %s\n", ptr->value); -``` - -Names can be used to lookup entities: - -```c -ecs_entity_t e = ecs_lookup(world, "MyEntity"); -``` - -When an entity is part of a hierarchy, names can be used to form a path: - -```c -ecs_entity_t parent = ecs_new_id(world); -ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); -ecs_entity_t grandchild = ecs_new_w_pair(world, EcsChildOf, child); - -ecs_set_name(world, parent, "Parent"); -ecs_set_name(world, child, "Child"); -ecs_set_name(world, grandchild, "GrandChild"); - -char *path = ecs_get_fullpath(world, grandchild); -printf("Path = %s\n", path); // prints Parent.Child.GrandChild -ecs_os_free(path); -``` - -A path can be created relative to a parent: - -```c -char *path = ecs_get_path(world, parent, grandchild); -printf("Path = %s\n", path); // prints Child.GrandChild -ecs_os_free(path); -``` - -Paths can be used to lookup an entity: - -```c -ecs_entity_t e = ecs_lookup_fullpath(world, "Parent.Child.GrandChild"); -``` - -Path lookups may be relative: - -```c -ecs_entity_t e = ecs_lookup_path(world, parent, "Child.GrandChild"); -``` - ### Macros The C99 API heavily relies on function-style macros, probably more than you would see in other libraries. The number one reason for this is that an ECS framework needs to work with user-defined types, and C does not provide out of the box support for generics. A few strategies have been employed in the API to improve its overall ergonomics, type safety and readability. Let's start with a simple example: @@ -250,7 +159,7 @@ typedef struct Position { ECS_COMPONENT(world, Position); -ecs_entity_t e = ecs_new(world, Position); +ecs_entity_t e = ecs_new_w(world, Position); ``` From a readability perspective this code looks fine as we can easily tell what is happening here. Though if we take a closer look, we can see that a typename is used where we expect an expression, and that is not possible in plain C. So what is going on? @@ -302,424 +211,6 @@ In addition to casting the value to the right type and passing in the component, Understanding how the macros work will go a long way in being able to write effective code in Flecs, and will lead to less surprises when debugging the code. -## Entities -Entities are uniquely identifiable objects in a game or simulation. In a real time strategy game, there may be entities for the different units, buildings, UI elements and particle effects, but also for example the camera, world and player. An entity does not contain any state, and is not of a particular type. In a traditional OOP-based game, you may expect a tank in the game is of class "Tank". In ECS, an entity is simply a unique identifier, and any data and behavior associated with that entity is implemented with components and systems. - -In Flecs, an entity is represented by a 64 bit integer, which is also how it is exposed on the API: - -```c -typedef uint64_t ecs_entity_t; -``` - -Zero indicates an invalid entity. Applications can create new entities with the `ecs_new` operation: - -```c -ecs_entity_t e = ecs_new(world, 0); -``` - -This operation guarantees to return an unused entity identifier. The first entity returned is not 1, as Flecs creates a number of builtin entities during the initialization of the world. The identifier of the first returned entity is stored in the `EcsFirstUserEntityId` constant. - -### Id recycling -Entity identifiers are reused when deleted. The `ecs_new` operation will first attempt to recycle a deleted identifier before producing a new one. If no identifier can be recycled, it will return the last issued identifier + 1. - -Entity identifiers can only be recycled if they have been deleted with `ecs_delete`. When `ecs_delete` is invoked, the generation count of the entity is increased. The generation is encoded in the entity identifier, which means that any existing entity identifiers with the old generation encoded in it will be considered not alive. Calling a delete multiple times on an entity that is not alive has no effect. - -When using multiple threads, the `ecs_new` operation guarantees that the returned identifiers are unique, by using atomic increments instead of a simple increment operation. New ids generated from a thread will not be recycled ids, since this would require taking a lock on the administration. While this does not represent a memory leak, it could cause ids to rise over time. If this happens and is an issue, an application should pre-create the ids. - -### Generations -When an entity is deleted, the generation count for that entity id is increased. The entity generation count enables an application to test whether an entity is still alive or whether it has been deleted, even after the id has been recycled. Consider: - -```c -ecs_entity_t e = ecs_new(world, 0); -ecs_delete(world, e); // Increases generation - -e = ecs_new(world, 0); // Recycles id, but with new generation -``` - -The generation is encoded in the entity id, which means that even though the base id is the same in the above example, the value returned by the second `ecs_new` is different than the first. - -To test whether an entity is alive, an application can use the `ecs_is_alive` call: - -```c -ecs_entity_t e1 = ecs_new(world, 0); -ecs_delete(world, e1); - -ecs_entity_t e2 = ecs_new(world, 0); -ecs_is_alive(world, e1); // false -ecs_is_alive(world, e2); // true -``` - -It is not allowed to invoke operations on an entity that is not alive, and doing so may result in an assert. The only operation that is allowed on an entity that is not alive is `ecs_delete`. Calling delete multiple times on an entity that is not alive will not increase the generation. Additionally, it is also not allowed to add child entities to an entity that is not alive. This will also result in an assert. - -There are 16 bits reserved for generation in the entity id, which means that an application can delete the same id 65536 times before the generation resets to 0. To get the current generation of an entity, applications can use the `ECS_GENERATION` macro. To extract the entity id without the generation, an application can apply the `ECS_ENTITY_MASK` with a bitwise and: - -```c -ecs_entity_t generation = ECS_GENERATION(e); -ecs_entity_t id = e & ECS_ENTITY_MASK; -``` - -### Manual id generation -Applications do not have to rely on `ecs_new` and `ecs_delete` to create and delete entity identifiers. Entity ids may be used directly, like in this example: - -```c -ecs_add(world, 42, Position); -``` - -This is particularly useful when the lifecycle of an entity is managed by another data source (like a multiplayer server) and prevents networking code from having to check whether the entity exists. This also allows applications to reuse existing identifiers, as long as these fit inside a 64 bit integer. - -When not using manual ids, id recycling mechanisms are bypassed as these are only invoked by the `ecs_new` and `ecs_delete` operations. Combining manual ids with `ecs_new` and `ecs_delete` can result in unexpected behavior, as `ecs_new` may return an identifier that an application has already used. - -### Id ranges -An application can instruct Flecs to issue ids from a specific offset and up to a certain limit with the `ecs_set_entity_range` operation. This example ensures that id generation starts from id 5000: - -```c -ecs_set_entity_range(world, 5000, 0); -``` - -If the last issued id was higher than 5000, the operation will not cause the last id to be reset to 5000. An application can also specify the highest id that can be generated: - -```c -ecs_set_entity_range(world, 5000, 10000); -``` - -If invoking `ecs_new` would result in an id higher than `10000`, the application would assert. If `0` is provided for the maximum id, no upper bound will be enforced. - -It is possible for an application to enforce that entity operations (`ecs_add`, `ecs_remove`, `ecs_delete`) are only allowed for the configured range with the `ecs_enable_range_check` operation: - -```c -ecs_enable_range_check(world, true); -``` - -This can be useful for enforcing that an application is not modifying entities that are owned by another data source. - - -## Types - -### Basic usage -A type is typically used to describe the contents (components) of an entity. A simple example: - -```c -// Create entity with type Position -ecs_entity_t e = ecs_new(world, Position); - -// Add Velocity to the entity -ecs_add(world, e, Velocity); -``` - -After running this code, the type can be printed: - -```c -// Print the type of the entity -const ecs_type_t *type = ecs_get_type(world, e); -char *str = ecs_type_str(world, type); -``` - -Which will produce: - -``` -Position, Velocity -``` - -### Advanced usage -A type is stored as a vector of identifiers. Because components are stored as entities in Flecs, a type is defined as (pseudo, not actual definition): - -```cpp -typedef vector ecs_type_t; -``` - -As a result, an application is able to do this: - -```c -ecs_entity_t tag_1 = ecs_new(world, 0); -ecs_entity_t tag_2 = ecs_new(world, 0); - -ecs_entity_t e = ecs_new(world, 0); -ecs_add_id(world, e, tag_1); -ecs_add_id(world, e, tag_2); -``` - -Printing the contents of the type of `e` now would produce something similar to: - -``` -256, 257 -``` - -When the type contained components the names of the components were printed. This is because the component entities have a name. The following example sets the names for `tag_1` and `tag_2`: - -```c -ecs_set_name(world, tag_1, "tag_1"); -ecs_set_name(world, tag_2, "tag_2"); -``` - -Printing the type again will now produce: - -``` -tag_1, tag_2 -``` - -## Components -A component is a plain datatype that can be attached to an entity. An entity can contain any number of components, and each component can be added only once per entity. Components are registered with a world using the `ECS_COMPONENT` macro, after which they can be added and removed to and from entities. Components can be of any datatype. The following example shows how to register and use components: - -```c -// Components can be defined from regular types -typedef struct Position { - float x, y; -} Position; - -int main() { - ecs_world_t *world = ecs_init(); - - // Register the component with the world - ECS_COMPONENT(world, Position); - - // Create a new entity with the component - ecs_entity_t e = ecs_new(world, Position); - - // Remove the component from the entity - ecs_remove(world, e, Position); - - // Add the component again - ecs_add(world, e, Position); -} -``` - -Component values can be set with the `ecs_set` operation. If the entity did not yet have the component, it will be added: - -```c -ecs_set(world, e, Position, {10, 20}); -``` - -Applications can get the value of a component with the `ecs_get` function: - -The value of a component can be requested with `ecs_get`, which will return `NULL` if the entity does not have the component: - -```c -const Position *p = ecs_get(world, e, Position); -``` - -The `ecs_get` operation returns a const pointer which should not be modified by the application. An application can obtain a mutable pointer with `ecs_get_mut`. The `ecs_get_mut` operation ensures that, even when using multiple threads, an application obtains a pointer to a component that can be safely modified, whereas the `ecs_get` operation might return a pointer to memory that is shared between threads. When an application modified a component obtained with `ecs_get_mut`, it should invoke `ecs_modified` to let the framework know the component value was changed. An example: - -```c -Position *p = ecs_get_mut(world, e, Position); -p->x++; -ecs_modified(world, e, Position); -``` - -### Component handles -In order to be able to add, remove and set components on an entity, the API needs access to the component handle. A component handle uniquely identifies a component and is passed to API functions. There are two types of handles that are accepted by API functions, a type handle and an entity handle. These handles are automatically defined as variables by the `ECS_COMPONENT` macro. If an application wants to use the component in another scope, the handle will have to be either declared globally or passed to that scope explicitly. - -#### Global component handles -To globally declare a component, an application can use the `ECS_COMPONENT_DECLARE` and `ECS_COMPONENT_DEFINE` macros: - -```c -// Declare component variable in the global scope -ECS_COMPONENT_DECLARE(Position); - -// Function that uses the global component variable -ecs_entity_t create_entity(ecs_world_t *world) { - return ecs_new(world, Position); -} - -int main(int argc, char *argv[]) { - ecs_world_t *world = ecs_init(); - - // Register component, assign id to the global component variable - ECS_COMPONENT_DEFINE(world, Position); - - ecs_entity_t e = create_entity(world); - - return ecs_fini(world); -} -``` - -To make a component available for other source files, an application can use the regular `extern` keyword: - -```c -extern ECS_COMPONENT_DECLARE(Position); -``` - -Declaring components globally works with multiple worlds, as the second time a component is registered it will use the same id. There is one caveat: an application should not define a component in world 2 that is not defined in world 1 _before_ defining the shared components. The reason for this is that if world 2 does not know that the shared component exists, it may assign its id to another component, which can cause a conflict. - -If this is something you cannot guarantee in an application, a better (though more verbose) way is to use local component handles. - -#### Local component handles -When an application cannot declare component handles globally, it can pass component handles manually. Manually passing component handles takes the variables that are declared by the `ECS_COMPONENT` macro and passes them to other functions. This section describes how to pass those handles around. - -Some operations can process multiple components in a single operation, like `ecs_add` and `ecs_remove`. Such operations require a handle of `ecs_type_t`. The `ECS_COMPONENT` macro defines a variable of `ecs_type_t` that contains only the id of the component. The variable defined by `ECS_COMPONENT` can be accessed with `ecs_type(ComponentName)`. This escapes the component name, which is necessary as it would otherwise conflict with the C type name. The following example shows how to pass a type handle to another function: - -```c -typedef struct Position { - float x, y; -} Position; - -void new_w_position(ecs_world_t *t, ecs_id_t ecs_id(Position)) { - // ecs_new uses an ecs_id_t - ecs_new(world, Position); -} - -int main() { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - new_w_position(world, ecs_id(Position)); - - ecs_fini(world); -} -``` - -The `ecs_new`, `ecs_add` and `ecs_remove` (not exhaustive) functions are wrapper macros around functions that accept a component id. The following code is equivalent to the previous example: - -```c -typedef struct Position { - float x, y; -} Position; - -void new_w_position(ecs_world_t *t, ecs_id_t p_id) { - // Use plain variable name with the ecs_new_w_id operation - ecs_new_w_id(world, p_id); -} - -int main() { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - new_w_position(world, ecs_id(Position)); - - ecs_fini(world); -} -``` - -### Component disabling -Components can be disabled, which prevents them from being matched with queries. Contrary to removing a component, disabling a component does not remove it from an entity. When a component is enabled after disabling it, the original value of the component is restored. - -To enable or disable a component, use the `ecs_enable_component` function: - -```c -typedef struct Position { - float x, y; -} Position; - -int main() { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - - /* Component is enabled by default */ - - /* Disable the component */ - ecs_enable_component(world, e, Position, false); - - /* Will return false */ - printf("%d\n", ecs_is_enabled_component(world, e, Position)); - - /* Re-enable the component */ - ecs_enable_component(world, e, Position, true); - - ecs_fini(world); -} -``` - -Component disabling works by maintaining a bitset alongside the component array. When a component is enabled or disabled, the bit that corresponds with the entity is set to 1 or 0. Bitsets are not created by default. Only after invoking the `ecs_enable_component` operation for an entity will be entity be moved to a table that keeps track of a bitset for that component. - -When a query is matched with a table that has a bitset for a component, it will automatically use the bitset to skip disabled values. If an entity contains multiple components tracked by a bitset, the query will evaluate each bitset and only yield entities for which all components are enabled. To ensure optimal performance, the query will always return the largest range of enabled components. Nonetheless, iterating a table with a bitset is slower than a regular table. - -If a query is matched with a table that has one or more bitsets, but the query does not match with components tracked by a bitset, there is no performance penalty. - -Component disabling can be used to temporarily suspend and resume a component value. It can also be used as a faster alternative to `ecs_add`/`ecs_remove`. Since the operation only needs to set a bit, it is a significantly faster alternative to adding/removing components, at the cost of a slightly slower iteration speed. If a component needs to be added or removed frequently, enabling/disabling is recommended. - -#### Limitations -Component disabling does not work for components not matched with the entity. If a query matches with a component from a base (prefab) or parent entity and the component is disabled for that entity, the query will not take this into account. If entities with disabled components from a base or parent entity need to be skipped. a query should manually check this. - -Another limitation is that currently the query NOT (!) operator does not take into account disabled entities. The optional operator (?) technically works, but a query is unable to see whether a component has been set or not as both the enabled and disabled values are returned to the application in a single array. - -## Tagging -Tags are much like components, but they are not associated with a data type. Tags are typically used to add a flag to an entity, for example to indicate that an entity is an Enemy: - -```c -int main() { - ecs_world_t *world = ecs_init(); - - // Register the tag with the world. There is no Enemy type - ECS_TAG(world, Enemy); - - // Add the Enemy tag - ecs_add(world, e, Enemy); - - // Remove the Enemy tag - ecs_remove(world, e, Enemy); -} -``` - -### Tag handles -Just like components, the API needs a handle to a tag before it can use it, and just like `ECS_COMPONENT`, the `ECS_TAG` macro defines two variables, one of type `ecs_type_t` and one of `ecs_entity_t`. Passing a handle of an `ecs_type_t` into a function looks similar to a component: - -```c -void new_w_tag(ecs_world_t *t, ecs_type_t ecs_type(Tag)) { - // ecs_new uses an ecs_type_t - ecs_new(world, Tag); -} - -int main() { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - new_w_tag(world, ecs_type(Tag)); - - ecs_fini(world); -} -``` - -For functions that require an `ecs_entity_t` handle, the tag variable names are not escaped, since they do not clash with a C type name. An example: - -```c -void add_tag(ecs_world_t *t, ecs_entity_t e, ecs_entity_t Tag) { - ecs_add_id(world, e, Tag); -} - -int main() { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new(world, 0); - add_tag(world, e, Tag); - - ecs_fini(world); -} -``` - -Anyone who paid careful attention to this example will notice that the `ecs_add_id` operation accepts two regular entities. - -## Observers -Observers are callbacks that are invoked when one or more events matches the query of an observer. Events can be either user defined or builtin. Examples of builtin events are `OnAdd`, `OnRemove` and `OnSet`. - -When an observer has a query with more than one component, the observer will not match until the entity for which the event is emitted satisfies the entire query. - -An example of an observer with a single component: - -```c -ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); -``` - -The implementation of the observer looks similar to a system: - -```c -void AddPosition(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - - for (int i = 0; i < it->count; i++) { - p[i].x = 10; - p[i].y = 20; - printf("Position added\n"); - } -} -``` - ## Modules Modules allow an application to split up systems and components into separate decoupled units. The purpose of modules is to make it easier to organize systems and components for large projects. Additionally, modules also make it easier to split off functionality into separate compilation units. @@ -784,13 +275,13 @@ ecs_world_t *world = ecs_init(); ECS_IMPORT(world, Vehicles); /* The module contents can now be used */ -ecs_entity_t e = ecs_new(world, Car); +ecs_entity_t e = ecs_new_w(world, Car); ``` Module contents are namespaced, which means that the identifiers of the content of the module (components, tags, systems) are stored in the scope of the module. For the above example module, everything would be stored in the `vehicles` scope. To resolve the `Car` component by name, an application would have to do: ```c -ecs_entity_t car_entity = ecs_lookup_fullpath(world, "vehicles.Car"); +ecs_entity_t car_entity = ecs_lookup(world, "vehicles.Car"); ``` Note that even though the module name is specified with uppercase, the name is stored with lowercase. This is because the naming convention for modules in C is PascalCase, whereas the stored identifiers use snake_case. If a module name contains several uppercase letters, this will be translated to a nested module. For example, the C module name `MySimpleModule` will be translated to `my.simple.module`. @@ -825,384 +316,12 @@ flecs::world world; world.import(); ``` -## Hierarchies -Entities in Flecs can be organized in hierarchies, which is useful when for example constructing a scene graph. To create hierarchies, applications can add `ChildOf` relationships to entities. This creates a relationship between a parent entity and a child entity that the application can later traverse. This is an example of a simple hierarchy: - -```c -ecs_entity_t parent = ecs_new(world, 0); -ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); -``` - -`ChildOf` relationships can be added and removed dynamically, similar to how components can be added and removed: - -```c -ecs_add_pair(world, child, EcsChildOf, parent); -ecs_remove_pair(world, child, EcsChildOf, parent); -``` - -`ChildOf` relationships can also be created through the `ECS_ENTITY` macro: - -```c -ECS_ENTITY(world, parent, 0); -ECS_ENTITY(world, child, (ChildOf, parent)); -``` - -### Iteration -Applications can iterate hierarchies depth first with the `ecs_children` API in C, and the `children()` iterator in C++. This example shows how to iterate all the children of an entity: - -In C: -```c -ecs_iter_t it = ecs_children(world, parent); - -while(ecs_children_next(&it)) { - for (int i = 0; i < it.count; i++) { - ecs_entity_t child = it.entities[i]; - char *path = ecs_get_fullpath(world, child); - printf("%s\n", path); - free(path); - } -} -``` - -In C++: -```cpp -e.children([&](flecs::entity child) { - std::cout << child.path() << std::endl; -}); -``` - -### Hierarchical queries -Queries and systems can request data from parents of the entity being iterated over with the `parent` modifier: - -```c -// Iterate all entities with Position that have a parent that also has Position -ecs_query_t *q = ecs_query_new(world, "Position(parent), Position"); -``` - -Additionally, a query can iterate the hierarchy in breadth-first order by providing the `cascade` modifier: - -```c -// Iterate all entities with Position that have a parent that also has Position -ecs_query_t *q = ecs_query_new(world, "Position(parent|cascade), Position"); -``` - -This does two things. First, it will iterate over all entities that have Position and that _optionally_ have a parent that has `Position`. By making the parent component optional, it is ensured that if an application is iterating a tree of entities, the root is also included. Secondly, the query iterates over the children in breadth-first order. This is particularly useful when writing transform systems, as they require parent entities to be transformed before child entities. - -The above query does not match root entities, as they do not have a parent with `Position`. To also match root entities, add `?` to make the term optional: - -```c -ecs_query_t *q = ecs_query_new(world, "?Position(parent|cascade), Position"); -``` - -See the [query manual](Queries.md) section for more details. - -### Path identifiers -When entities in a hierarchy have names assigned to them, they can be looked up with path expressions. A path expression is a list of entity names, separated by a scope separator character (by default a `.`, and `::` in the C++ API). This example shows how to request the path expression from an entity: - -```c -ECS_ENTITY(world, parent, 0); -ECS_ENTITY(world, child, (ChildOf, parent)); - -char *path = ecs_get_fullpath(world, child); -printf("%s\n", path); // Prints "parent.child" -free(path); -``` - -To lookup an entity using a path, use `ecs_lookup_fullpath`: - -```c -ecs_entity_t e = ecs_lookup_fullpath(world, "parent.child"); -``` - -Applications can also lookup entities using a relative path expression: - -```c -ecs_entity_t e = ecs_lookup_path(world, parent, "child.grand_child"); -``` - -Additionally, applications can specify a custom path separator when looking up or requesting paths: - -```c -// Lookup child::grand_child relative to parent -ecs_entity_t e = ecs_lookup_path_w_sep( - world, parent, "child::grand_child", "::", "::"); - -// Get path of child relative to parent -char *path = ecs_get_path_w_sep(world, parent, child, "::", "::"); -``` - -Note that the path separator is provided twice, once for the prefix and once for the separator. This lets the API correctly handle expressions like `::parent::child::grand_child`. - -### Scoping -Applications can set a default scope with the `ecs_set_scope` function, so that all operations are evaluated relative to a scope. The scope is set on a stage, which makes it thread safe when executed from within a flecs worker thread. This example shows how to set the scope: - -```c -ecs_entity_t parent = ecs_new(world, 0); - -// Set the current scope to the parent -ecs_entity_t prev_scope = ecs_set_scope(world, parent); - -// This entity is created as child of parent -ecs_entity_t child = ecs_new(world, 0); - -// Look for "child" relative to parent -ecs_entity_t e = ecs_lookup_fullpath(world, "child"); - -// It's good practice to restore the previous scope -ecs_set_scope(prev_scope); -``` - -Modules automatically set the scope to the module itself, so that the module acts as a namespace for its contents. - -### Paths and signatures -When referencing entities or components in a signature or type expression that are not stored in the root, an application will have to provide the path. Signatures and type expressions always use the dot (`.`) as separator. For example, if a component "Position" is defined in the module "transform", a system subscribing for the component would have to be defined like this: - -```c -ECS_SYSTEM(world, Move, EcsOnUpdate, transform.Position); -``` - -The same goes for other parts of the API that accept a type expression, like `ECS_ENTITY`: - -```c -ECS_ENTITY(world, Movable, transform.Position); -``` - -If the system would be defined in the same scope as the `Position` component, it would not need to specify the path: - -```c -ECS_ENTITY(world, transform, 0); - -ecs_entity_t prev_scope = ecs_set_scope(world, transform); - -ECS_COMPONENT(world, Position); - -// System is in the same scope, no need to add "transform" -ECS_SYSTEM(world, MoveInScope, EcsOnUpdate, Position); - -ecs_set_scope(world, prev_scope); - -// This system is not in the same scope, and needs to add transform -ECS_SYSTEM(world, MoveNotInScope, EcsOnUpdate, transform.Position); -``` - -## Inheritance -Inheritance is the ability to share components between entities by _inheriting_ from them, by using the `IsA` relationship. This is a simple example in the C API: - -```c -// Create a base entity -ecs_entity_t base = ecs_new(world, 0); -ecs_set(world, base, Position, {10, 20}); - -// Derive from base -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); - -// e now shares Position with base -ecs_get(world, base, Position) == ecs_get(world, e, Position); // 1 -``` - -`IsA` relationships can be added and removed dynamically, similar to how components can be added and removed: - -```c -ecs_add_id(world, e, (IsA, base)); -ecs_remove_id(world, e, (IsA, base)); -``` - -`IsA` relationships can also be created through the `ECS_ENTITY` macro: - -```c -ECS_ENTITY(world, base, Position); -ECS_ENTITY(world, e, (IsA, base)); -``` - -`IsA` relationships can be nested: - -```c -ecs_entity_t base = ecs_new(world, 0); -ecs_set(world, base, Position, {10, 20}); - -ecs_entity_t derived = ecs_new_w_pair(world, EcsIsA, base); - -// Derive from "derived" which is itself derived from base -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, derived); - -// All three entities now share Position -ecs_get(world, base, Position) == ecs_get(world, e, Position); // 1 -ecs_get(world, base, Position) == ecs_get(world, derived, Position); // 1 -``` - -### Overriding -Derived entities can override components from their base by adding the component as they would normally. When overriding a component, the value of the base component is copied to the entity. This example shows how a derived entity overrides the Position component: - -```c -// Shortcut for creating a base entity and setting Position -ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - -// Derive from the base -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); - -// Override Position -ecs_add(world, e, Position); - -// Position component no longer matches with base -ecs_get(world, base, Position) != ecs_get(world, e, Position); // 1 - -// Prints {10, 20} -const Position *p = ecs_get(world, e, Position); -printf("{%f, %f}\n", p->x, p->y); -``` - -When an entity shared a component from a base entity, we say that the component is "shared". If the component is not shared, it is "owned". After an entity overrides a component, it will own the component. - -It is possible to remove an override, in which case the component will be shared with the base entity again: - -```c -// Removes override on Position -ecs_remove(world, e, Position); - -// Position is again shared with base -ecs_get(world, base, Position) == ecs_get(world, e, Position); // 1 -``` - -Overrides work with nested `IsA` relationships: - -```c -// Shortcut for creating a base entity and setting Position -ecs_entity_t base = ecs_new(world, 0); -ecs_set(world, base, Position, {10, 20}); -ecs_set(world, base, Velocity, {1, 1}); - -// Create derived entity, override Position -ecs_entity_t derived = ecs_new_w_pair(world, EcsIsA, base); -ecs_add(world, base, Position); - -// Derive from 'derived', which is derived from base -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, derived); - -// The entity now shares Position from derived, and Velocity from base -``` - -### Automatic overriding -In some scenarios it is desirable that an entity is initialized with a specific set of values, yet does not share the components from the base entity. In this case the derived entity can override each component individually, but this can become hard to maintain as components are added or removed to the base. This can be achieved by marking components as owned. Consider the following example: - -```c -// Create a base. Simply deriving the base will share the component, but not override it. -ecs_entity_t Base = ecs_set(world, 0, Position, {10, 20}); - -// Mark as OVERRIDE. This ensures that when base is derived from, Position is overridden -ecs_add_id(world, world, Base, ECS_OVERRIDE | ecs_id(Position)); - -// Create entity from BaseType. This adds the IsA relationship in addition -// to overriding Position, effectively initializing the Position component for the entity. -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Base); -``` - -The combination of instancing, overriding and OVERRIDE is one of the fastest and easiest ways to create an entity with a set of initialized components. The OVERRIDE relationship can also be specified inside type expressions. The following example is equivalent to the previous one: - -```c -ECS_ENTITY(world, Base, Position, OVERRIDE | Position); - -ecs_set(world, Base, Position, {10, 20}); - -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Base); -``` - -### Inheritance hierarchies -If a base entity has children, derived entities of that base entity will, when the `IsA` relationship is added, acquire the same set of children. Take this example: - -```c -ecs_entity_t parent = ecs_new(world, 0); -ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent); -ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent); - -// Derive from parent, two children are added to the entity -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, parent); -``` - -The children that are copied to the entity will have exactly the same set of components as the children of the base. For example, if the base child has components `Position, Velocity`, the derived child will also have `Position, Velocity`. Furthermore, the values of the base child components will be copied to the entity child: - -```c -ecs_entity_t parent = ecs_new(world, 0); -ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); -ecs_set_name(world, child, "Child"); // Give child a name, so we can look it up -ecs_set(world, child, Position, {10, 20}); - -// Derive from parent, two children are added to the derived entity -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, parent); -ecs_entity_t e_child = ecs_lookup_path(world, e, "Child"); -const Position *p = ecs_get(world, e_child, Position); -printf("{%f, %f}\n", p->x, p->y); // Prints {10, 20} - -// The components are not shared with the derived child! -ecs_get(world, child, Position) != ecs_get(world, e_child, Position); // 1 -``` - -Since the children of the derived entity have the exact same components as the base children, their components are not shared. Component sharing between children is possible however, as `IsA` relationships are also copied over to the child of the derived entity: - -```c -ecs_entity_t parent = ecs_new(world, 0); - -// Create child base from which we will share components -ecs_entity_t child_base = ecs_new(world, 0); -ecs_set(world, child_base, Position, {10, 20}); -ecs_set_name(world, child, "Child"); - -// Create actual child that inherits from the child base -ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); -ecs_add_pair(world, child, EcsIsA, child_base); - -// Inherit from parent, two children are added to the entity -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, parent); -ecs_entity_t e_child = ecs_lookup_path(world, e, "Child"); - -// The component is now shared with the child and child_base -ecs_get(world, child, Position) == ecs_get(world, e_child, Position); // 1 -``` - -### Prefabs -Prefabs are entities that can be used as templates for other entities. Prefabs are regular entities, except that they are not matched by default with systems. To create a prefab, add the `EcsPrefab` tag when creating an entity: - -```c -ecs_entity_t prefab = ecs_new_w_id(world, EcsPrefab); -``` - -The `EcsPrefab` tag can also be added or removed dynamically: - -```c -ecs_add_id(world, prefab, EcsPrefab); -ecs_remove_id(world, prefab, EcsPrefab); -``` - -Prefabs can also be created with the `ECS_PREFAB` macro: - -```c -ECS_PREFAB(world, prefab, Position, Velocity); -``` - -To instantiate a prefab, an application can use the `IsA` relationship: - -```c -ecs_entity_t e = ecs_new_w_pair(world, IsA, prefab); -``` - -To ensure that entities that inherit from a prefab don't also inherit the `Prefab` tag (which would cause them to not get matched with systems), the `Prefab` tag does not propagate to derived entities. This is illustrated in the following example: - -```c -ECS_PREFAB(world, prefab, Position); - -ecs_has(world, prefab, EcsPrefab); // true -ecs_has(world, prefab, Position); // true - -ecs_entity_t e = ecs_new_w_pair(world, IsA, prefab); -ecs_has(world, e, EcsPrefab); // false -ecs_has(world, e, Position); // true -``` - ## Deferred operations Applications can defer entity with the `ecs_defer_begin` and `ecs_defer_end` functions. This records all operations that happen inside the begin - end block, and executes them when `ecs_defer_end` is called. Deferred operations are useful when an application wants to make modifications to an entity while iterating, as doing this without deferring an operation could modify the underlying data structure. An example: ```c ecs_defer_begin(world); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_set(world, e, Velocity, {1, 1}); ecs_defer_end(world); @@ -1212,7 +331,7 @@ The effects of these operations will not be visible until the `ecs_defer_end` op There are a few things to keep in mind when deferring: - creating a new entity will always return a new id which increases the last used id counter of the world -- `ecs_get_mut` returns a pointer initialized with the current component value, and does not take into account deferred set or get_mut operations +- `ecs_ensure` returns a pointer initialized with the current component value, and does not take into account deferred set or ensure operations - if an operation is called on an entity which was deleted while deferred, the operation will ignored by `ecs_defer_end` - if a child entity is created for a deleted parent while deferred, the child entity will be deleted by `ecs_defer_end` diff --git a/vendors/flecs/docs/MigrationGuide.md b/vendors/flecs/docs/MigrationGuide.md new file mode 100644 index 000000000..901c315b4 --- /dev/null +++ b/vendors/flecs/docs/MigrationGuide.md @@ -0,0 +1,521 @@ +# Migration guide +This guide will help migrating Flecs v3 code bases over to Flecs v4. While the guide attempts to be as complete as possible, v4 is a big release, so some things are inevitably missed. If you see something that you think belongs in the migration guide, feel free to create a PR! + +Note that this is _not_ a comprehensive list of things that changed in v4. This document only intends to document the breaking changes between v3 and v4. + +## Queries +The three query implementations in v3 (`filter`, `query`, `rule`) have been merged into a single `query` API in v4. This means that any usage of `filter` and `rule` should now be replaced with `query`. + +Additionally the following things have changed: + +- **Query fields now start from 0 instead of 1(!)** +- `ecs_term_id_t` is now called `ecs_term_ref_t`. +- Term ref flags (such as `self`, `up`) are now applied with a bitwise OR mask to `ecs_term_ref_t::id`. +- The `trav` field has been moved from `ecs_term_ref_t` to `ecs_term_t`. +- When query traversal flags such as `up` are provided, the traversal relationship defaults to `ChildOf` (this used to be `IsA`). +- If no traversal flags are provided, the default is still `self|up(IsA)` for inheritable components. +- The `ecs_query_desc_t::instanced` member no longer exists. Instancing must be specified with a query flag (`.flags = EcsQueryIsInstanced`). +- Stack-allocated query (filter) objects are no longer supported. +- It is no longer possible to provide user-allocated term arrays. The max number of terms is now 32 and can be configured with `FLECS_TERM_COUNT_MAX`. +- The `ecs_query_changed` function has been split up into a function that only accepts a query (`ecs_query_changed`) and one that only accepts an iterator (`ecs_iter_changed`). +- The `ecs_query_skip` function has been renamed to `ecs_iter_skip`. +- The `ecs_query_set_group` function has been renamed to `ecs_iter_set_group`. +- The values in the `ecs_iter_t::columns` array have changed, and may change again in the near future. Applications should not directly depend on it. +- The `EcsParent` convenience constant is gone, and can now be replaced with just `EcsUp`. +- `ecs_query_desc_t::group_by_id` has been renamed to `ecs_query_desc_t::group_by` +- `ecs_query_desc_t::group_by` has been renamed to `ecs_query_desc_t::group_by_callback` +- `ecs_query_desc_t::order_by_component` has been renamed to `ecs_query_desc_t::order_by` +- `ecs_query_desc_t::order_by` has been renamed to `ecs_query_desc_t::order_by_callback` +- The `ecs_query_next_table` and `ecs_query_populate` functions have been removed. +- The `ecs_query_table_count` and `ecs_query_empty_table_count` functions have been replaced with `ecs_query_count`, which now returns a struct. +- The `ecs_query_t` struct is now public, which means that many of the old accessor functions (like `ecs_query_get_ctx`, `ecs_query_get_binding_ctx`) are no longer necessary. +- The subquery feature has been removed. + +A before/after example: + +
+
    +
  • C + +```c +// v3 +ecs_filter_t *q = ecs_filter(world, { + .terms = { + { .id = ecs_id(Position) } + { .id = ecs_id(Gravity), .src.id = Game, .src.flags = EcsSelf|EcsUp } + } +}); + +ecs_iter_t it = ecs_filter_iter(world, q); +while (ecs_filter_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + Gravity *g = ecs_field(&it, Gravity, 2); + + for (int i = 0; i < it.count; i ++) { + // ... + } +} + +ecs_filter_fini(q); +``` + +```c +// v4 +ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position) } + { .id = ecs_id(Gravity), .src.id = Game|EcsSelf|EcsUp } + } +}); + +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Gravity *g = ecs_field(&it, Gravity, 1); + + for (int i = 0; i < it.count; i ++) { + // ... + } +} + +ecs_query_fini(q); +``` + +
  • +
  • C++ + +```cpp +// v3 +auto q = world.filter_builder() + .term_at(2).src(Game).self().up() + .build(); + +q.each([](Position& p, Gravity& v) { + // ... +}); +``` + +```cpp +// v4 +auto q = world.query_builder() + .term_at(1).src(Game).self().up() + .build(); + +q.each([](Position& p, Gravity& v) { + // ... +}); +``` + +
  • +
+
+ +### Caching +In v3, a `query` was always cached. In v4, by default queries are created _uncached_. There are three conditions under which queries become cached: + +- When a cache kind is configured that specifies caching +- When the query is associated with an entity +- When `order_by`, `group_by` or `cascade` is used + +System queries are cached by default (they are always associated with the system entity). See the query manual for more details. The following example shows the difference between creating a cached query in v3 and v4: + +
+
    +
  • C + +```c +// v3 +ecs_query_t *q = ecs_query(world, { + .filter.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) }, + } +}); +``` + +```c +// v4 + +// Create cached query with cache kind +ecs_query_t *q = ecs_query(world, { + .filter.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) }, + }, + .cache_kind = EcsQueryCacheAuto // cache terms that are cacheable +}); + +// Create cached query with associated entity +ecs_query_t *q = ecs_query(world, { + .entity = ecs_entity(world, { .name = "MyQuery" }), + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) }, + } +}); +``` + +
  • +
  • C++ + +```cpp +// v3 +auto q = world.query(); +``` + +```cpp +// v4 + +// Create cached query with cache kind +auto q = world.query_builder() + .cache_kind(flecs::QueryCacheAuto) + .build(); + +// Create cached query with associated entity +auto q = world.query("MyQuery"); +``` + +
  • +
+
+ +### Iter function +In C++, the `iter` iteration function no longer exists, and the signature of the `run` function has changed. An example: + +```cpp +// v3 +auto q = world.query(); + +q.iter([](flecs::iter& it, Position *p) { + for (auto i : it) { + p[i].x ++; + } +}); +``` + +```cpp +// v4 +auto q = world.query(); + +q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + for (auto i : it) { + p[i].x ++; + } + } +}); +``` + +### Query DSL +The query DSL has remained mostly the same but there are a few small changes. + +Traversal flags are specified differently in v4: + +```c +// v3 +Gravity(game:self|up(ChildOf)) +``` +```c +// v4 +Gravity(game|self|up ChildOf) +``` + +Component id flags are now specified with lower case: + +```c +// v3 +OVERRIDE | Position +``` +```c +// v4 +auto_override | Position +``` + +In v4 the `$this` variable must be specified with a lower case: + +```c +// v3 +Position($This) +``` +```c +// v4 +Position($this) +``` + +The `parent` traversal flag has been removed. Because `ChildOf` is now the default relationship when a traversal flag is specified, it can be replaced with just `up`: + +```c +// v3 +Position(parent) +``` +```c +// v4 +Position(up) +``` + +The `$(Relationship)` notation was removed from the DSL in v4: +```c +// v3 +$(Movement) +``` +```c +// v4 +(Movement, $Movement) +``` + +## Systems & Pipelines +Systems have remained mostly the same. A list of the things that changed: + +- `ecs_readonly_begin` got a new `multi_threaded` parameter (added in 3.2.12) +- `no_readonly` has been renamed to `immediate` + +## Observers +Observers have remained mostly the same. A list of the things that changed: + +- The `UnSet` event no longer exists. `OnRemove` can in v4 be used instead. +- The `EcsIterable` component/iterable interface no longer exist. `yield_existing` is now only supported for builtin events. +- The observer `run` callback now behaves like a regular system runner and no longer requires calling `ecs_observer_default_run_action`. To get the old behavior where `run` is called before the observer query is evaluated, specify the `EcsObserverBypassQuery` observer flag. + +## Term iterators +Term iterators have been removed from v4, and have been replaced with the easier to use `ecs_each` API. An example: + +```c +// v3 +ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ .id = ecs_id(Position) }); +while (ecs_term_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + for (int i = 0; i < it.count; i ++) { + // ... + } +} +``` + +```c +// v4 +ecs_iter_t it = ecs_each(world, Position); +while (ecs_each_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + for (int i = 0; i < it.count; i ++) { + // ... + } +} +``` + +## Entity Names +Entities can now be created with names that are numbers, e.g. `1000`. Lookups for numbers now result in a regular by name lookup for an entity with the number as name. To lookup an entity with a specific id using the lookup functions, use the `#` prefix. An example: + +
+
    +
  • C + +```c +// v3 +ecs_entity_t e = ecs_lookup(world, "1000"); +``` + +```c +// v4 +ecs_entity_t e = ecs_lookup(world, "#1000"); +``` + +
  • +
  • C++ + +```cpp +// v3 +flecs::entity e = world.lookup("1000"); +``` + +```cpp +// v4 +flecs::entity e = world.lookup("#1000"); +``` + +
  • +
+
+ +Additionally, entity name functions have changed: + +- `ecs_get_fullpath` is now `ecs_get_path` +- `ecs_get_path` is now `ecs_get_path_from` +- `ecs_lookup_fullpath` is now `ecs_lookup` +- `ecs_lookup_path` is now `ecs_lookup_from` + +## ensure / get_mut +These changes got introduced in 3.2.12, but are mentioned here as many users will likely see these changes first when upgrading to v4: + + - `ensure` is renamed to `make_alive` + - `ensure_id` is renamed to `make_alive_id` + - the return type of `get_mut` in the C++ API is changed from a T* to a T& + - `get_mut` is renamed to `ensure` + - a new `get_mut` function is added that doesn't add the component + +## ecs_new +The following changes were made to the `new` family of functions in the C API: + +- `ecs_new(world, T)` got renamed to `ecs_new_w` +- `ecs_new_id` got renamed to `ecs_new` + +In v3, `ecs_new` took into account values set by `ecs_set_scope` and `ecs_set_with`. In v4 this is no longer the case, and the `ecs_new_w` function will only return the entity with the specified component. To get the old behavior that takes into account scope and with, use `ecs_entity_init`. + +## Flecs Script +The Flecs script syntax changed how components and entities are specified: + +```c +// v3 +ent { + - Position{10, 20} + child +} +``` +```c +// v4 +ent { + Position: {10, 20} + child {} +} +``` + +For more details, see the [Flecs script manual](FlecsScript.md). + +## Inheritance +In v4, components no longer inherit by default when instantiating a prefab. Instead, components by default override, which is equivalent to how auto overriding worked in v3. To inherit a component, add the `(OnInstantiate, Inherit)` trait to the component. + +The `EcsDontInherit` v3 trait is equivalent to the `(OnInstantiate, DontInherit)` v4 trait. The `EcsAlwaysOverride` v3 trait is now the default in v4. It can also be explicitly specified as `(OnInstantiate, Override)`. + +An example: + +
+
    +
  • C + +```c +// v3 +ecs_entity_t p = ecs_new_prefab(world, "SpaceShip"); +ecs_set(world, p, MaxSpeed, {100}); +ecs_set(world, p, Damage, {0}); +ecs_override(world, p, Damage); + +ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); +``` + +```c +// v4 +ecs_add_pair(world, ecs_id(MaxSpeed), EcsOnInstantiate, EcsInherit); + +ecs_entity_t p = ecs_entity(world, { + .name = "SpaceShip", .add = ecs_ids(EcsPrefab) }); +ecs_set(world, p, MaxSpeed, {100}); +ecs_set(world, p, Damage, {0}); + +ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); +``` + +
  • +
  • C++ + +```cpp +// v3 +flecs::entity p = world.prefab("SpaceShip") + .set(MaxSpeed{100}) + .set_override(Damage{0}); + +flecs::entity i = world.entity().is_a(p); +``` + +```cpp +// v4 +world.component().add(flecs::OnInstantiate, flecs::Inherit); + +flecs::entity p = world.prefab("SpaceShip") + .set(MaxSpeed{100}) + .set(Damage{0}); + +flecs::entity i = world.entity().is_a(p); +``` + +
  • +
+
+ +Additionally, the `override` operation has been renamed to `auto_override`. + +## Component disabling +In v4, components can only be disabled if they have the `CanToggle` trait. An example: + +
+
    +
  • C + +```c +// v3 +ecs_entity_t e = ecs_new_w(world, Position); +ecs_enable_component(world, e, Position, false); +``` + +```c +// v4 +ecs_add_id(world, ecs_id(Position), EcsCanToggle); + +ecs_entity_t e = ecs_new_w(world, Position); +ecs_enable_component(world, e, Position, false); +``` + +
  • +
  • C++ + +```cpp +// v3 +world.entity() + .set(Position{10, 20}) + .disable(); +``` + +```cpp +// v4 +world.component().add(flecs::CanToggle); + +world.entity() + .set(Position{10, 20}) + .disable(); +``` + +
  • +
+
+ +## Union relationships +In v3, Union relationships where encoded on the entity type with a `(Union, Relationship)` pair. In v4, union relationships are encoded with a `(Relationship, Union)` pair. + +The API for unions has not changed meaningfully. However, the union storage has changed, which might impact performance (positively or negatively). + +## Snapshots +The snapshot feature has been removed from v4. + +## Tree flattening +The tree flattening feature (`ecs_flatten`) has been removed. It will be replaced eventually with a new implementation that addresses the shortcomings of the old feature. + +## Addons +The following addons have been removed/merged with other addons: + +- `FLECS_META_C` (merged with `FLECS_META`) +- `FLECS_PLECS` (renamed to `FLECS_SCRIPT`) +- `FLECS_EXPR` (merged with `FLECS_SCRIPT`) +- `FLECS_PARSER` (merged with `FLECS_SCRIPT`) +- `FLECS_SNAPSHOT` (feature got removed) +- `FLECS_RULES` (merged with queries) +- `MONITOR` (merged with `STATS`) + +## Misc + +- The `EcsTag` trait has been renamed to `EcsPairIsTag`. +- `DefaultChildComponent` is now a component (was a tag in v3). +- The `ecs_set_automerge` functionality has been removed from v4. +- The `ecs_async_stage_new` function has been renamed to `ecs_stage_new`. +- `ecs_set` no longer returns a new entity if 0 is passed (use `ecs_insert` instead). +- `ecs_set_entity_generation` has been renamed to `ecs_set_version`. +- The `ecs_term_copy`/`ecs_term_move`/`ecs_term_fini` functions have been removed. Terms no longer exist by themselves, and term resources are now owned by the query object. +- The `ecs_iter_poly` function has been removed. To iterate all entities in the world, now use `ecs_get_entities`. +- `ecs_field_column` has been renamed to `ecs_field_column_index`. diff --git a/vendors/flecs/docs/ObserversManual.md b/vendors/flecs/docs/ObserversManual.md new file mode 100644 index 000000000..aaa7a4f1d --- /dev/null +++ b/vendors/flecs/docs/ObserversManual.md @@ -0,0 +1,2252 @@ +# Observers + +## Introduction +Observers are a mechanism that allows applications to react to events. Events can be either user defined or builtin, where the latter communicates changes in the ECS such as adding and removing components. Observers are similar to systems, in that they are queries that are combined with a callback. The difference between systems and observers is that systems are executed periodically for all matching _entities_, whereas observers are executed whenever a matching _event_ occurs. + +## Example + +
+
    +
  • C + +```c +// Observer callback +void OnSetPosition(ecs_iter_t *it) { + Position *p = ecs_field(it, Position, 0); + + for (int i = 0; i < it->count; i ++) { + printf("Position set: {%f, %f}\n", p[i].x, p[i].y); + } +} + +// Create observer that is invoked whenever Position is set +ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }}, + .events = { EcsOnSet }, + .callback = OnSetPosition +}); + +// Alternatively this macro shortcut can be used: +// ECS_OBSERVER(world, OnSetPosition, EcsOnSet, Position); + +ecs_entity_t e = ecs_new(world); +ecs_set(world, e, Position, {10, 20}); // Invokes observer +``` + +
  • +
  • C++ + +```cpp +// Create observer that is invoked whenever Position is set +world.observer() + .event(flecs::OnSet) + .each([](flecs::entity e, Position& p) { + std::cout << "Position set: {" << p.x << ", " << p.y << "}\n"; + }); + +world.entity().set(Position{10, 20}); // Invokes observer +``` + +
  • +
  • C# + +```cs +// Create observer that is invoked whenever Position is set +world.Observer() + .Event(Ecs.OnSet) + .Each((Iter it, int i, ref Position p) => + { + Console.WriteLine($"OnSet: ({p.X}, {p.Y})"); + }); + +world.Entity().Set(new Position(10, 20)); // Invokes observer +``` + +
  • +
  • Rust + +```rust +// Create observer that is invoked whenever Position is set +world + .observer::() + .each_entity(|e, p| { + println!("Position set: {{ {}, {} }}", p.x, p.y); + }); + +world.entity().set(Position { x: 10.0, y: 20.0 }); // Invokes observer +``` + +
  • +
+
+ +## The Basics +This section goes over basic observer features. + +### Usage +Observers provide a flexible out of the box event delivery mechanism for applications. It is not a one-size-fits all feature however, and this section provides information that helps with making a decision on whether observers are a good fit, or not so much. + +The first thing that is important to know about is that observers are primarily a mechanism for delivering events that match _queries_. In a way they can be considered as the reactive counterpart to systems. This means that almost all of the flexibility and features that Flecs queries provide are also available to observers, which goes far beyond what typical event implementations provide. + +That flexibility does come at a cost however. For most non-trivial observers, a query has to be evaluated before the observer is invoked. This means that for simple use cases, a basic event queue is always going to outperform observers. + +#### Limitations +Observers may not provide the features that are required for an event implementation. Here are a few things that observers can't (easily) do: + +- Peek ahead in the event queue +- Have different event queues per entity (possible, but inefficient/difficult) +- Sort events before processing them +- Process events on multiple threads +- Bubble up events (event propagation - see below - pushes events downwards) +- Stop event propagation + +#### Good Use Cases for Observers +Good use cases for observers are scenarios where you need to respond to a structural change in the ECS, like a component that is being added or removed to an entity. Another good use case for observers is if you need to respond to changes in a component that is always assigned through a `set` operation. A typical example is a `Window` component, where you can resize a window by setting the component. + +Another good application for observers is when you have events that are infrequent (like a window resize) and the builtin observer API provides everything that's needed. + +#### Bad Use Cases for Observers +If you find yourself adding or removing components just to trigger observer events, that's a bad application for observers. Not only would that be an expensive solution for a simple problem, it would also be unreliable because features like command batching impact how and when events are emitted. + +Another rule of thumb is that if you can solve something with a system, it should probably be solved with a system. Running something every frame may sound expensive when compared to reacting to aperiodic events, but systems are much more efficient to run, and have more predictable performance. You can also use marker tags in combination with a not operator to prevent a system from running repeatedly for the same entity. + +#### Observers vs. Hooks +Hooks at face value appear to provide functionality that is similar to observers. There are `on_add`, `on_remove` and `on_set` hooks, just as there are observers that are invoked for `OnAdd`, `OnRemove` and `OnSet` events. The intended use cases for hooks and observers are almost opposites of each other. What gives? + +Hooks are part of the "interface" of a component, just like how constructors and destructors are. You could consider hooks as the counterpart to OOP methods in ECS. They define the behavior of a component, but can only be invoked through mutations on the component data. You can only configure a single `on_add`, `on_remove` and `on_set` hook per component, just like you can only have a single constructor and destructor. Hooks also receive priority treatment: they are always invoked before observers -or in the case of a remove operation- after observers. + +Observers on the other hand are a mechanism that enable _other_ parts of the application to respond to events related to a component. There can be many observers for a single component, registered by different parts of the application. + +Here's a list with the differences between hooks and observers: + +- There can be only one hook per event/component +- There can be many observers for a single event/component +- Hooks can only match a single event/component +- Observers match queries which can have multiple terms +- Hooks can only be configured for components, not tags +- Hooks are much more efficient than observers +- Hooks can mutate a component +- Observers should never mutate a component +- Once a component is in use, hooks cannot be changed +- Observers can be dynamically added & removed + +### OnAdd Events +Observers can subscribe to `OnAdd` events to get notified whenever a component, tag or pair is added to an entity. An event only fires when the component is actually added to the entity, so not on each `add` operation. An example: + +
+
    +
  • C + +```c +ecs_entity_t e = ecs_new(world); + +// OnAdd observer fires +ecs_add(world, e, Position); + +// OnAdd observer doesn't fire, entity already has component +ecs_add(world, e, Position); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.entity(); + +// OnAdd observer fires +e.add(); + +// OnAdd observer doesn't fire, entity already has component +e.add(); +``` + +
  • +
  • C# + +```cs +Entity e = world.Entity(); + +// OnAdd observer fires +e.Add(); + +// OnAdd observer doesn't fire, entity already has component +e.Add(); +``` + +
  • +
  • Rust + +```rust +let e = world.entity(); + +// OnAdd observer fires +e.add::(); + +// OnAdd observer doesn't fire, entity already has component +e.add::(); +``` + +
  • +
+
+ +An `OnAdd` observer is invoked after a component constructor and `on_add` hook is invoked. If an observer accesses the value of a component it will be a valid constructed object. However, if an `OnAdd` observer was invoked as part of a `set` operation, the value assigned to the component in the `set` operation will not(!) be visible on the `OnAdd` observer. This means that for components that do not have a constructor, the component value passed to the observer will be uninitialized. + +### OnSet Events +Observers can subscribe to an `OnSet` event to get notified whenever a component is assigned with a new value. An `OnSet` event will be generated each time the `set` operation is called, or when `modified` is called. `OnSet` observers are _not_ invoked when a system directly modifies a component. An application will have to manually call `modified` to make sure observers `OnSet` are invoked. An example: + +
+
    +
  • C + +```c +ecs_entity_t e = ecs_new(world); + +// OnAdd observer fires first, then OnSet observer fires +ecs_set(world, e, Position, {10, 20}); + +// OnAdd observer doesn't fire, OnSet observer fires +ecs_set(world, e, Position, {10, 20}); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.entity(); + +// OnAdd observer fires first, then OnSet observer fires +e.set(Position{10, 20}); + +// OnAdd observer doesn't fire, OnSet observer fires +e.set(Position{10, 20}); +``` + +
  • +
  • C# + +```cs +Entity e = world.Entity(); + +// OnAdd observer fires first, then OnSet observer fires +e.Set(new Position(10, 20)); + +// OnAdd observer doesn't fire, OnSet observer fires +e.Set(new Position(10, 20)); +``` + +
  • +
  • Rust + +```rust +let e = world.entity(); + +// OnAdd observer fires first, then OnSet observer fires +e.set(Position { x: 10.0, y: 20.0 }); + +// OnAdd observer doesn't fire, OnSet observer fires +e.set(Position { x: 10.0, y: 20.0 }); +``` + +
  • +
+
+ +### OnSet and Inheritance +To ensure that OnSet events can be used reliably to detect component changes, events can be produced by operations that change inheritance relationships or operate on inherited from components. This is enabled by default for components with the `(OnInstantiate, Inherit)` trait. To prevent this behavior, add the `self` modifier to an observer term. The following inheritance scenarios produce OnSet events. All scenarios assume that the component has the `(OnInstantiate, Inherit)` trait. + +#### Adding an IsA pair +When an IsA pair is added to an entity, an OnSet event is generated for each newly inherited component: + +
+
    +
  • C + +```c +ecs_entity_t p = ecs_insert(world, {EcsPrefab}, ecs_value(Position, {10, 20})); +ecs_entity_t i = ecs_new(world); + +// Produces OnSet event for Position +ecs_add_pair(world, i, EcsIsA, p); +``` + +
  • +
  • C++ + +```cpp +flecs::entity p = world.prefab().set(Position{10, 20}); + +// Produces OnSet event for Position +flecs::entity i = world.entity().is_a(p); +``` + +
  • +
  • C# + +```cs +Entity p = world.Prefab().Set(new Position(10, 20)); + +// Produces OnSet event for Position +Entity i = world.Entity().IsA(p); +``` + +
  • +
  • Rust + +```rust +let p = world.prefab().set(Position { x: 10.0, y: 20.0 }); + +// Produces OnSet event for Position +let i = world.entity().is_a_id(p); +``` + +
  • +
+
+ +If the base entity has a component that the entity already had no event is generated. Similarly, if a component from a base entity is already provided by another base entity, and the new base entity does not become the primary source for the component, no OnSet event is generated. A base entity is the primary source for a component if: + +- There are no other base entities that provide the component, or +- It has the lowest entity id of all base entities that provide the component + +#### Removing an Override +When an overridden component is removed, the inherited component is reexposed which effectively changes the value of the component for the entity. An OnSet event will be produced for the inherited component: + +
+
    +
  • C + +```c +ecs_entity_t p = ecs_insert(world, {EcsPrefab}, ecs_value(Position, {10, 20})); + +// Produces OnSet event for inherited Position component +ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + +// Override component. Produces regular OnSet event. +ecs_set(world, i, Position, {20, 30}); + +// Reexposes inherited component, produces OnSet event +ecs_remove(world, i, Position); +``` + +
  • +
  • C++ + +```cpp +flecs::entity p = world.prefab().set(Position{10, 20}); + +// Produces OnSet event for inherited Position component +flecs::entity i = world.entity().is_a(p); + +// Override component. Produces regular OnSet event. +i.set(Position{20, 30}); + +// Reexposes inherited component, produces OnSet event +i.remove(); +``` + +
  • +
  • C# + +```cs +Entity p = world.Prefab().Set(new Position(10, 20)); + +// Produces OnSet event for inherited Position component +Entity i = world.Entity().IsA(p); + +// Override component. Produces regular OnSet event. +i.Set(new Position(20, 30)); + +// Reexposes inherited component, produces OnSet event +i.Remove(); +``` + +
  • +
  • Rust + +```rust +let p = world.prefab().set(Position { x: 10.0, y: 20.0 }); + +// Produces OnSet event for inherited Position component +let i = world.entity().is_a_id(p); + +// Override component. Produces regular OnSet event. +i.set(Position { x: 20.0, y: 30.0 }); + +// Reexposes inherited component, produces OnSet event +i.remove::(); +``` + +
  • +
+
+ +#### Setting an Inherited Component +When an inherited component is modified, an OnSet event is propagated to all entities that inherit the component: + +
+
    +
  • C + +```c +ecs_entity_t p = ecs_insert(world, {EcsPrefab}, ecs_value(Position, {10, 20})); + +// Produces OnSet event for inherited Position component +ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + +// Produces OnSet event for inherited Position +ecs_set(world, p, Position, {20, 30}); +``` + +
  • +
  • C++ + +```cpp +flecs::entity p = world.prefab().set(Position{10, 20}); + +// Produces OnSet event for Position +flecs::entity i = world.entity().is_a(p); +``` + +
  • +
  • C# + +```cs +Entity p = world.Prefab().Set(new Position(10, 20)); + +// Produces OnSet event for Position +Entity i = world.Entity().IsA(p); +``` + +
  • +
  • Rust + +```rust +let p = world.prefab().set(Position { x: 10.0, y: 20.0 }); + +// Produces OnSet event for Position +let i = world.entity().is_a_id(p); +``` + +
  • +
+
+ +### OnRemove Events +Observers can subscribe to `OnRemove` events to get notified whenever a component, tag or pair is removed from an entity. An event only fires when the component is actually removed from the entity, so not on each `remove` operation. An example: + +
+
    +
  • C + +```c +ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + +// OnRemove observer fires +ecs_remove(world, e, Position); + +// OnRemove observer doesn't fire, entity doesn't have the component +ecs_add(world, e, Position); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.entity().set(Position{10, 20}); + +// OnRemove observer fires +e.remove(); + +// OnRemove observer doesn't fire, entity doesn't have the component +e.remove(); +``` + +
  • +
  • C# + +```cs +Entity e = world.Entity().Set(new Position(10, 20)); + +// OnRemove observer fires +e.Remove(); + +// OnRemove observer doesn't fire, entity doesn't have the component +e.Remove(); +``` + +
  • +
  • Rust + +```rust +let e = world.entity().set(Position { x: 10.0, y: 20.0 }); + +// OnRemove observer fires +e.remove::(); + +// OnRemove observer doesn't fire, entity doesn't have the component +e.remove::(); +``` + +
  • +
+
+ +## Multi-event Observers +A single observer can subscribe for multiple events: + +
+
    +
  • C + +```c +// Observer that listens for both OnAdd and OnRemove events +ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }}, + .events = { EcsOnAdd, EcsOnRemove }, + .callback = OnPosition +}); +``` + +
  • +
  • C++ + +```cpp +// Observer that listens for both OnAdd and OnRemove events +world.observer() + .event(flecs::OnAdd) + .event(flecs::OnRemove) + .each([](flecs::entity e, Position& p) { + // ... + }); +``` + +
  • +
  • C# + +```cs +// Observer that listens for both OnAdd and OnRemove events +world.Observer() + .Event(Ecs.OnAdd) + .Event(Ecs.OnRemove) + .Each((Iter it, int i, ref Position p) => + { + // ... + }); +``` + +
  • +
  • Rust + +```rust +// Observer that listens for both OnAdd and OnRemove events +world + .observer::() + .add_event::() + .each_entity(|e, p| { +// ... +}); +``` + +
  • +
+
+ +The iterator object provided to the observer callback provides information on which event triggered. An example: + +
+
    +
  • C + +```c +void OnPosition(ecs_iter_t *it) { + if (it->event == EcsOnAdd) { + // ... + } else if (it->event == EcsOnRemove) { + // ... + } +} +``` + +
  • +
  • C++ + +```cpp +world.observer() + .event(flecs::OnAdd) + .event(flecs::OnRemove) + .each([](flecs::iter& it, size_t i, Position&) { + if (it.event() == flecs::OnAdd) { + // ... + } else if (it.event() == flecs::OnRemove) { + // ... + } + }); +``` + +
  • +
  • C# + +```cs +world.Observer() + .Event(Ecs.OnAdd) + .Event(Ecs.OnRemove) + .Each((Iter it, int i, ref Position p) => + { + if (it.Event() == Ecs.OnAdd) { + // ... + } else if (it.Event() == Ecs.OnRemove) { + // ... + } + }); +``` + +
  • +
  • Rust + +```rust +world + .observer::() + .add_event::() + .each_iter(|it, i, p| { + if it.event() == flecs::OnAdd::ID { + // ... + } else if it.event() == flecs::OnRemove::ID { + // ... + } + }); +``` + +
  • +
+
+ +Alternatively, an observer can also use `Wildcard` as event, which will create an observer that listens for any kind of event that matches the observer. Wildcard event observers do add significant overhead to ECS operations for a specific component, so they should be used sparingly. A typical use case for wildcard event observers is logging or debugging. An example: + +
+
    +
  • C + +```c +// Observer that listens for all events for Position +ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }}, + .events = { EcsWildcard }, + .callback = OnPosition +}); +``` + +
  • +
  • C++ + +```cpp +// Observer that listens for all events for Position +world.observer() + .event(flecs::Wildcard) + .each([](flecs::entity e, Position& p) { + // ... + }); +``` + +
  • +
  • C# + +```cs +// Observer that listens for all events for Position +world.Observer() + .Event(Ecs.Wildcard) + .Each((Iter it, int i, ref Position p) => + { + // ... + }); +``` + +
  • +
  • Rust + +```rust +// Observer that listens for all events for Position +world + .observer::() + .each_entity(|e, p| { + // ... + }); +``` + +
  • +
+
+ +## Multi-term Observers +Observers use queries to match events. This makes observers similar to systems, which are also callbacks invoked for matching entities, except that observers match their query against events. This means that observers can match multiple components, use operators, query traversal and more. A simple example: + +
+
    +
  • C + +```c +// Observer that listens for entities with both Position and Velocity +void OnSetPosition(ecs_iter_t *it) { + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); + + for (int i = 0; i < it->count; i ++) { + // ... + } +} + +ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, + .events = { EcsOnAdd }, + .callback = OnPosition +}); +``` + +
  • +
  • C++ + +```cpp +// Observer that listens for entities with both Position and Velocity +world.observer() + .event(flecs::OnAdd) + .each([](flecs::entity e, Position& p, Velocity& v) { + // ... + }); +``` + +
  • +
  • C# + +```cs +// Observer that listens for entities with both Position and Velocity +world.Observer() + .Event(Ecs.OnAdd) + .Each((Iter it, int i, ref Position p, ref Velocity v) => + { + // ... + }); +``` + +
  • +
  • Rust + +```rust +// Observer that listens for entities with both Position and Velocity +world + .observer::() + .each_entity(|e, (p, v)| { + // ... + }); +``` + +
  • +
+
+ +Observers with multiple terms will only be invoked for entities that match all terms. An example: + +
+
    +
  • C + +```c +ecs_entity_t e = ecs_new(world); + +// Does not trigger "Position, Velocity" observer +ecs_add(world, e, Position); + +// Entity now matches "Position, Velocity" query, triggers observer +ecs_add(world, e, Velocity); +``` + +
  • +
  • C++ + +```cpp +flecs::entity e = world.entity(); + +// Does not trigger "Position, Velocity" observer +e.add(); + +// Entity now matches "Position, Velocity" query, triggers observer +e.add(); +``` + +
  • +
  • C# + +```cs +Entity e = world.Entity(); + +// Does not trigger "Position, Velocity" observer +e.Add(); + +// Entity now matches "Position, Velocity" query, triggers observer +e.Add(); +``` + +
  • +
  • Rust + +```rust +let e = world.entity(); + +// Does not trigger "Position, Velocity" observer +e.add::(); + +// Entity now matches "Position, Velocity" query, triggers observer +e.add::(); +``` + +
  • +
+
+ +Internally observers with multiple terms are implemented with multiple single-term observers. Whenever a single-term observer triggers, the observer query is evaluated against the source of the event. Something to consider is that single-term observers do not have the query evaluation step, which makes them more performant than multi-term observers. + +### Filter Terms +A multi-term observer by default will trigger for events on any term, as long as the event source matches the observer query. In some scenarios this is not desirable, and an observer should only trigger on one or more specific terms, while applying the other terms only as a filter. This can be accomplished with filter terms. The following example shows how: + +
+
    +
  • C + +```c +// Observer that only triggers on Position, not on Velocity +ecs_observer(world, { + .query.terms = { + { ecs_id(Position) }, + { ecs_id(Velocity), .inout = EcsInOutFilter } + }, + .events = { EcsOnSet }, + .callback = OnPosition +}); + +ecs_entity_t e = ecs_new(world); + +// Doesn't trigger, entity doesn't have Velocity +ecs_set(world, e, Position, {10, 20}); + +// Doesn't trigger, Velocity is a filter term +ecs_set(world, e, Velocity, {1, 2}); + +// Triggers, entity now matches observer query +ecs_set(world, e, Position, {20, 30}); +``` + +
  • +
  • C++ + +```cpp +// Observer that only triggers on Position, not on Velocity +world.observer() + .with().filter() + .event(flecs::OnAdd) + .each([](flecs::entity e, Position& p) { + // ... + }); + +flecs::entity e = world.entity(); + +// Doesn't trigger, entity doesn't have Velocity +e.set(Position{10, 20}); + +// Doesn't trigger, Velocity is a filter term +e.set(Velocity{1, 2}); + +// Triggers, entity now matches observer query +e.set(Position{20, 30}); +``` + +
  • +
  • C# + +```cs +// Observer that only triggers on Position, not on Velocity +world.Observer() + .With().Filter() + .Event(Ecs.OnAdd) + .Each((Iter it, int i, ref Position p) => + { + // ... + }); + +Entity e = world.Entity(); + +// Doesn't trigger, entity doesn't have Velocity +e.Set(new Position(10, 20)); + +// Doesn't trigger, Velocity is a filter term +e.Set(new Velocity(1, 2)); + +// Triggers, entity now matches observer query +e.Set(new Velocity(20, 30)); +``` + +
  • +
  • Rust + +```rust +// Observer that only triggers on Position, not on Velocity +world + .observer::() + .with::() + .filter() + .each_entity(|e, p| { + // ... + }); + +let e = world.entity(); + +// Doesn't trigger, entity doesn't have Velocity +e.set(Position { x: 10.0, y: 20.0 }); + +// Doesn't trigger, Velocity is a filter term +e.set(Velocity { x: 1.0, y: 2.0 }); + +// Triggers, entity now matches observer query +e.set(Position { x: 20.0, y: 30.0 }); +``` + +
  • +
+
+ +### Event Downgrading +When an OnSet observer requests both components and tags, the events for the tag terms are "downgraded" to an OnAdd event. The reason this happens is because tags cannot be set, and can therefore not produce OnSet events. Downgrading the event (as opposed to failing to create the observer) allows for OnSet observers that have both components and tags. An example: + +
+
    +
  • C + +```c +// OnSet observer with both component and tag +ecs_observer(world, { + .query.terms = { + { ecs_id(Position) }, + { Npc } // Tag + }, + .events = { EcsOnSet }, + .callback = MyObserver +}); + +ecs_entity_t e = ecs_new(world); + +// Doesn't trigger, entity doesn't have Npc +ecs_set(world, e, Position, {10, 20}); + +// Produces and OnAdd event & triggers observer +ecs_add(world, e, Npc); + +// Produces an OnSet event & triggers observer +ecs_set(world, e, Position, {20, 30}); +``` + +
  • +
  • C++ + +```cpp +// OnSet observer with both component and tag +world.observer() + .with() // Tag + .event(flecs::OnSet) + .each([](flecs::entity e, Position& p) { + // ... + }); + +flecs::entity e = world.entity(); + +// Doesn't trigger, entity doesn't have Npc +e.set(Position{10, 20}); + +// Produces and OnAdd event & triggers observer +e.add(); + +// Produces an OnSet event & triggers observer +e.set(Position{20, 30}); +``` + +
  • +
  • C# + +```cs +// OnSet observer with both component and tag +world.Observer() + .With() // Tag + .Event(Ecs.OnSet) + .Each((Iter it, int i, ref Position p) => + { + // ... + }); + +Entity e = world.Entity(); + +// Doesn't trigger, entity doesn't have Npc +e.Set(new Position(10, 20)); + +// Produces and OnAdd event & triggers observer +e.Add(); + +// Produces an OnSet event & triggers observer +e.Set(new Position(20, 30)); +``` + +
  • +
  • Rust + +```rust +// OnSet observer with both component and tag +world + .observer::() + .with::() // Tag + .each_entity(|e, p| { + // ... + }); + +let e = world.entity(); + +// Doesn't trigger, entity doesn't have Npc +e.set(Position { x: 10.0, y: 20.0 }); + +// Produces and OnAdd event & triggers observer +e.add::(); + +// Produces an OnSet event & triggers observer +e.set(Position { x: 20.0, y: 30.0 }); +``` + +
  • +
+
+ +### Event Inversion +Because observers match events against queries, this also means they support all of the query operators such as `and`, `optional`, `or` and `not`. The `not` operator is noteworthy, as it needs to invert the event to make sure the observer is triggered correctly. An example: + +
+
    +
  • C + +```c +// Observer with a Not term +ecs_observer(world, { + .query.terms = { + { ecs_id(Position) }, + { ecs_id(Velocity), .oper = EcsNot } + }, + .events = { EcsOnAdd }, + .callback = MyObserver +}); + +ecs_entity_t e = ecs_new(world); + +// Triggers the observer +ecs_set(world, e, Position, {10, 20}); + +// Doesn't trigger the observer, entity doesn't match the observer query +ecs_set(world, e, Velocity, {1, 2}); + +// Triggers the observer, as the Velocity term was inverted to OnRemove +ecs_remove(world, e, Velocity); +``` + +
  • +
  • C++ + +```cpp +// Observer with a Not term +world.observer() + .without() + .event(flecs::OnAdd) + .each([](flecs::entity e, Position& p) { + // ... + }); + +flecs::entity e = world.entity(); + +// Triggers the observer +e.set(Position{10, 20}); + +// Doesn't trigger the observer, entity doesn't match the observer query +e.set(Velocity{1, 2}); + +// Triggers the observer, as the Velocity term was inverted to OnRemove +e.remove(); +``` + +
  • +
  • C# + +```cs +// Observer with a Not term +world.Observer() + .Without() + .Event(Ecs.OnAdd) + .Each((Iter it, int i, ref Position p) => + { + // ... + }); + +Entity e = world.Entity(); + +// Triggers the observer +e.Set(new Position(10, 20)); + +// Doesn't trigger the observer, entity doesn't match the observer query +e.Set(new Velocity(1, 2)); + +// Triggers the observer, as the Velocity term was inverted to OnRemove +e.Remove(); +``` + +
  • +
  • Rust + +```rust +// Observer with a Not term +world + .observer::() + .without::() + .each_entity(|e, p| { + // ... + }); + +let e = world.entity(); + +// Triggers the observer +e.set(Position { x: 10.0, y: 20.0 }); + +// Doesn't trigger the observer, entity doesn't match the observer query +e.set(Velocity { x: 1.0, y: 2.0 }); + +// Triggers the observer, as the Velocity term was inverted to OnRemove +e.remove::(); +``` + +
  • +
+
+ +Inversion also works the other way around: a `not` term will be inverted to use an `OnAdd` event for an `OnRemove` observer. Note that in either case, the observer will be invoked with the observer event, e.g. `OnAdd` for an `OnAdd` observer, and `OnRemove` for an `OnRemove` observer. + +Inversion also applies to `OnSet` events: an `OnSet` event will be inverted to `OnRemove` when it is used in combination with a `not` term. + +## Monitors +A monitor is an observer that fires when an entity starts and stops matching a query. Whether an entity starts or stops matching is communicated with an `OnAdd` or `OnRemove` event. Monitors can only specify a single `Monitor` event. An example: + +
+
    +
  • C + +```c +void MyMonitor(ecs_iter_t *it) { + if (it->event == EcsOnAdd) { + for (int i = 0; i < it->count; i ++) { + // Entity started matching query + } + } else if (it->event == EcsOnRemove) { + for (int i = 0; i < it->count; i ++) { + // Entity stopped matching query + } + } +} + +// Monitor observer +ecs_observer(world, { + .query.terms = { + { ecs_id(Position) }, + { ecs_id(Velocity) } + }, + .events = { EcsMonitor }, + .callback = MyMonitor +}); + +ecs_entity_t e = ecs_new(world); + +// Doesn't trigger the monitor, entity doesn't match +ecs_set(world, e, Position, {10, 20}); + +// Entity now matches, triggers monitor with OnAdd event +ecs_set(world, e, Velocity, {1, 2}); + +// Entity no longer matches, triggers monitor with OnRemove event +ecs_remove(world, e, Position); +``` + +
  • +
  • C++ + +```cpp +// Monitor observer +world.observer() + .event(flecs::Monitor) + .each([](flecs::iter& it, size_t i, Position& p, Velocity& v) { + if (it.event() == flecs::OnAdd) { + // Entity started matching query + } else if (it.event() == flecs::OnRemove) { + // Entity stopped matching query + } + }); + +flecs::entity e = world.entity(); + +// Doesn't trigger the monitor, entity doesn't match +e.set(Position{10, 20}); + +// Entity now matches, triggers monitor with OnAdd event +e.set(Velocity{1, 2}); + +// Entity no longer matches, triggers monitor with OnRemove event +e.remove(); +``` + +
  • +
  • C# + +```cs +// Monitor observer +world.Observer() + .Event(Ecs.Monitor) + .Each((Iter it, int i, ref Position p, ref Velocity v) => + { + if (it.Event() == Ecs.OnAdd) { + // Entity started matching query + } else if (it.Event() == Ecs.OnRemove) { + // Entity stopped matching query + } + }); + +Entity e = world.Entity(); + +// Doesn't trigger the monitor, entity doesn't match +e.set(new Position(10, 20)); + +// Entity now matches, triggers monitor with OnAdd event +e.Set(new Velocity(1, 2)); + +// Entity no longer matches, triggers monitor with OnRemove event +e.Remove(); +``` + +
  • +
  • Rust + +```rust +// Monitor observer +world + .observer::() + .each_iter(|it, i, (p, v)| { + if it.event() == flecs::OnAdd::ID { + // Entity started matching query + } else if it.event() == flecs::OnRemove::ID { + // Entity stopped matching query + } + }); + +let e = world.entity(); + +// Doesn't trigger the monitor, entity doesn't match +e.set(Position { x: 10.0, y: 20.0 }); + +// Entity now matches, triggers monitor with OnAdd event +e.set(Velocity { x: 1.0, y: 2.0 }); + +// Entity no longer matches, triggers monitor with OnRemove event +e.remove::(); +``` + +
  • +
+
+ +Monitors are implemented by evaluating the observer query twice: once on the previous archetype of the entity, and one on the current archetype of the entity. The following table shows when the monitor observer is invoked: + +| Previous matches | Current matches | Invoked with event | +|------------------|-----------------|--------------------| +| No | No | - | +| No | Yes | `OnAdd` | +| Yes | Yes | - | +| Yes | No | `OnRemove` | + +Note that because monitors have to evaluate the query twice, they are more expensive to evaluate than regular observers. + +## Yield Existing +Observers can be created with the "yield existing" property, which invokes the observer with all entities that already match the observer. This can make it easier to make code order-independent, as entities created before the observer will still trigger the observer. Yield existing only works with `OnAdd`, `OnSet` and `OnRemove` events. An example: + +
+
    +
  • C + +```c +// Entity created before the observer +ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + +// Yield existing observer +ecs_observer(world, { + .query.terms = { + { ecs_id(Position) } + }, + .events = { EcsOnAdd }, + .callback = MyObserver, + .yield_existing = true +}); + +// Observer is invoked for e1 + +// Fires observer as usual +ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); +``` + +
  • +
  • C++ + +```cpp +// Entity created before the observer +flecs::entity e1 = world.entity().set(Position{10, 20}); + +// Yield existing observer +world.observer() + .event(flecs::OnAdd) + .yield_existing() + .each([](flecs::iter& it, size_t i, Position& p, Velocity& v) { + // ... + }); + +// Observer is invoked for e1 + +// Fires observer as usual +flecs::entity e2 = world.entity().set(Position{10, 20}); +``` + +
  • +
  • C# + +```cs +// Entity created before the observer +Entity e1 = world.Entity().Set(new Position(10, 20)); + +// Yield existing observer +world.Observer() + .Event(Ecs.OnAdd) + .YieldExisting() + .Each((Iter it, int i, ref Position p, ref Velocity v) => + { + // ... + }); + +// Observer is invoked for e1 + +// Fires observer as usual +Entity e2 = world.Entity().Set(new Position(10, 20)); +``` + +
  • +
  • Rust + +```rust +// Entity created before the observer +let e1 = world.entity().set(Position { x: 10.0, y: 20.0 }); + +// Yield existing observer +world + .observer::() + .yield_existing() + .each_iter(|it, i, (p, v)| { + // ... + }); + +// Observer is invoked for e1 + +// Fires observer as usual +let e2 = world.entity().set(Position { x: 10.0, y: 20.0 }); +``` + +
  • +
+
+ +When `yield_existing` is enabled on an `OnRemove` observer, the observer will be invoked with matching entities when the observer is deleted. This makes symmetric event handling (each `OnAdd` is matched by an `OnRemove`) easier in scenarios where entities outlive the observer. + +### Yield_existing flags +Applications can customize the behavior of yield_existing with the following observer flags: + +| Flag | Description | +|------|-------------| +| `EcsObserverYieldOnCreate` | Yield results on observer creation | +| `EcsObserverYieldOnDelete` | Yield results on observer deletion | + +These flags can be set on the `flags_` member of `ecs_observer_desc_t`. These flags should not be set at the same time as `.yield_existing`. An example: + +
+
    +
  • C + +```c +ecs_observer(world, { + .query.terms = { + { ecs_id(Position) } + }, + .events = { EcsOnAdd }, + .callback = MyObserver, + .flags_ = EcsObserverYieldOnDelete // only yield on observer deletion +}); +``` + +
  • +
  • C++ + +```cpp +// Yield existing observer +world.observer() + .event(flecs::OnAdd) + .observer_flags(EcsObserverYieldOnDelete) + .each([](flecs::iter& it, size_t i, Position& p, Velocity& v) { + // ... + }); +``` + +
  • +
  • C# + +```cs +// TODO +``` + +
  • +
  • Rust + +```rust +// TODO +``` + +
  • +
+
+ +## Fixed Source Terms +Observers can be created with fixed source terms, which are terms that are matched on a single entity. An example: + +
+
    +
  • C + +```c +// Entity used for fixed source +ecs_entity_t Game = ecs_insert(world, ecs_value(TimeOfDay, {0})); + +// Observer with fixed source +ecs_observer(world, { + .query.terms = { + { ecs_id(TimeOfDay), .src.id = Game } // Match TimeOfDay on Game + }, + .events = { EcsOnSet }, + .callback = MyObserver +}); + +// Triggers observer +ecs_set(world, Game, TimeOfDay, {1}); + +// Does not trigger observer +ecs_entity_t e = ecs_insert(world, ecs_value(TimeOfDay, {0})); +``` + +
  • +
  • C++ + +```cpp +// Entity used for fixed source +flecs::entity Game = world.entity().set(TimeOfDay{0}); + +// Observer with fixed source +world.observer() + .term_at(0).src(Game) // Match TimeOfDay on Game + .event(flecs::OnSet) + .each([](flecs::iter& it, size_t i, TimeOfDay& t) { + // ... + }); + +// Triggers observer +Game.set(TimeOfDay{1}); + +// Does not trigger observer +flecs::entity e = world.entity().set(TimeOfDay{0}); +``` + +
  • +
  • C# + +```cs +// Entity used for fixed source +Entity game = world.Entity().Set(new TimeOfDay(0)); + +// Observer with fixed source +world.Observer() + .TermAt(0).Src(game) // Match TimeOfDay on Game + .Event(Ecs.OnSet) + .Each((Iter it, int i, ref Position p, ref Velocity v) => + { + // ... + }); + +// Triggers observer +game.Set(new TimeOfDay(1)); + +// Does not trigger observer +Entity e = world.Entity().Set(new TimeOfDay(0)); +``` + +
  • +
  • Rust + +```rust +// Entity used for fixed source +let game = world.entity().set(TimeOfDay { value: 0.0 }); + +// Observer with fixed source +world + .observer::() + .term_at(0) + .set_src_id(game) // Match TimeOfDay on game + .each_iter(|it, i, time| { + // ... + }); + +// Triggers observer +game.set(TimeOfDay { value: 1.0 }); + +// Does not trigger observer +let e = world.entity().set(TimeOfDay { value: 0.0 }); +``` + +
  • +
+
+ +Observers may match terms on multiple different sources. However, when an observer matches components both on the `$this` source (default) and on a fixed source, the fixed source terms will not match events. The reason for this is that otherwise emitting an event for a fixed source term would mean iterating all matching entities for the `$this` term. If an observer only has fixed source terms, events will be matched for each of the terms. + +### Singletons +Singletons are a special case of fixed source term, where the component is matched on itself. An example: + +
+
    +
  • C + +```c +ecs_singleton_set(world, TimeOfDay, {0}); + +// Observer with singleton source +ecs_observer(world, { + .query.terms = { + { ecs_id(TimeOfDay), .src.id = ecs_id(TimeOfDay) } + }, + .events = { EcsOnSet }, + .callback = MyObserver +}); + +// Triggers observer +ecs_singleton_set(world, TimeOfDay, {1}); + +// Does not trigger observer +ecs_entity_t e = ecs_insert(world, ecs_value(TimeOfDay, {0})); +``` + +
  • +
  • C++ + +```cpp +world.set(TimeOfDay{0}); + +// Observer with singleton source +world.observer() + .term_at(0).singleton() + .event(flecs::OnSet) + .each([](flecs::iter& it, size_t i, TimeOfDay& t) { + // ... + }); + +// Triggers observer +world.set(TimeOfDay{1}); + +// Does not trigger observer +flecs::entity e = world.entity().set(TimeOfDay{0}); +``` + +
  • +
  • C# + +```cs +world.Set(new TimeOfDay(0)); + +// Observer with singleton source +world.Observer() + .TermAt(0).Singleton() + .Event(Ecs.OnSet) + .Each((Iter it, int i, ref TimeOfDay t) => + { + // ... + }); + +// Triggers observer +world.Set(new TimeOfDay(1)); + +// Does not trigger observer +Entity e = world.Entity().Set(new TimeOfDay(0)); +``` + +
  • +
  • Rust + +```rust +world.set(TimeOfDay { value: 0.0 }); + +// Observer with singleton source +world + .observer::() + .term_at(0) + .singleton() + .each_iter(|it, i, time| { + // ... + }); + +// Triggers observer +world.set(TimeOfDay { value: 1.0 }); + +// Does not trigger observer +let e = world.entity().set(TimeOfDay { value: 0.0 }); +``` + +
  • +
+
+ +## Event Propagation +When an observer has a query that uses (up) relationship traversal, events are propagated along the relationship edge. For example, when an observer requests component `Position` from a parent entity, setting `Position` on the parent will propagate an `OnSet` event along the `ChildOf` edge, notifying all child entities of the parent. + +Events propagate until a leaf entity is found, or an entity with the propagated component is found. For example, if an event for `Position` is propagated from a parent to a child with `Position`, that event will not be propagated to the child's children. This ensures that the results are consistent with up traversal, where a relationship is traversed upwards until the first entity with the component is found. An example: + +
+
    +
  • C + +```c +// Create an observer that matches OnSet(Position) events on self and a parent +ecs_observer(world, { + .query.terms = {{ ecs_id(Position), .src.id = EcsSelf|EcsUp /*, .trav = EcsChildOf (default) */ }}, + .events = { EcsOnSet }, + .callback = OnSetPosition +}); + +ecs_entity_t parent = ecs_new(world); +ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + +// Invokes observer twice: once for the parent and once for the child +ecs_set(world, parent, Position, {10, 20}); +``` + +
  • +
  • C++ + +```cpp +// Create an observer that matches OnSet(Position) events on self and a parent +world.observer() + .term_at(0).self().up() // .trav(flecs::ChildOf) (default) + .event(flecs::OnSet) + .each([](flecs::entity e, Position& p) { + // ... + }); + +flecs::entity parent = world.entity(); +flecs::entity child = world.entity().child_of(parent); + +// Invokes observer twice: once for the parent and once for the child +parent.set(Position{10, 20}); +``` + +
  • +
  • C# + +```cs +// Create an observer that matches OnSet(Position) events on self and a parent +world.Observer() + .TermAt(0).Self().Up() // .Trav(Ecs.ChildOf) (default) + .Event(Ecs.OnSet) + .Each((Iter it, int i, ref Position p) => + { + // ... + }); + +Entity parent = world.Entity(); +Entity child = world.Entity().ChildOf(parent); + +// Invokes observer twice: once for the parent and once for the child +parent.Set(new Position(10, 20)); +``` + +
  • +
  • Rust + +```rust +// Create an observer that matches OnSet(Position) events on self and a parent +world + .observer::() + .term_at(0) + .self_() + .up() // .trav(flecs::ChildOf) (default) + .each_entity(|e, p| { + // ... + }); + +let parent = world.entity(); +let child = world.entity().child_of_id(parent); + +// Invokes observer twice: once for the parent and once for the child +parent.set(Position { x: 10.0, y: 20.0 }); +``` + +
  • +
+
+ +## Event Forwarding +Event forwarding, like event propagation, is a mechanism that propagates events along relationship edges. The difference between propagation and forwarding is that event forwarding produces events when a relationship pair is added to an entity. For example, if a `(ChildOf, my_parent)` pair is added to an entity, and `my_parent` has components `Position` and `Velocity`, an `OnAdd` event is produced for each component and emitted for the child. + +Event forwarding allows applications to write order independent code, where it doesn't matter whether a relationship pair was added before or after the target of that pair received new components. It is the opposite of event propagation: where event propagation "pushes" an existing event downwards, event forwarding "pulls" new events from parent entities. + +Only reachable components are forwarded. If an entity has a parent and grandparent that both have `Position`, only the `Position` component from the parent will result in an event. Just like with event propagation, this ensures consistency with the behavior of up traversal in queries. + +Both adding and removing pairs can result in event forwarding, where adding a pair results in forwarded `OnAdd` events, and removing a pair results in forwarded `OnRemove` events. `OnSet` events can also be forwarded, but is only supported for `IsA` pairs, where adding an `(IsA, my_prefab)` pair will result in an `OnSet` event for all reachable components of that prefab. + +The following code shows an example of event forwarding: + +
+
    +
  • C + +```c +// Create an observer that matches OnAdd(Position) events on a parent +ecs_observer(world, { + .query.terms = {{ ecs_id(Position), .src.id = EcsUp /*, .trav = EcsChildOf (default) */ }}, + .events = { EcsOnAdd }, + .callback = OnSetPosition +}); + +ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {10, 20})); + +// Forwards OnAdd event for Position to child +ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); +``` + +
  • +
  • C++ + +```cpp +// Create an observer that matches OnAdd(Position) events on a parent +world.observer() + .term_at(0).up() // .trav(flecs::ChildOf) (default) + .event(flecs::OnAdd) + .each([](flecs::entity e, Position& p) { + // ... + }); + +flecs::entity parent = world.entity().set(Position{10, 20}); + +// Forwards OnAdd event for Position to child +flecs::entity child = world.entity().child_of(parent); +``` + +
  • +
  • C# + +```cs +// Create an observer that matches OnAdd(Position) events on a parent +world.Observer() + .TermAt(0).Up() // .Trav(Ecs.ChildOf) (default) + .Event(Ecs.OnAdd) + .Each((Iter it, int i, ref Position p) => + { + // ... + }); + +Entity parent = world.Entity().Set(new Position(10, 20)); + +// Forwards OnAdd event for Position to child +Entity child = world.Entity().ChildOf(parent); +``` + +
  • +
  • Rust + +```rust +// Create an observer that matches OnAdd(Position) events on a parent +world + .observer::() + .term_at(0) + .up() // .trav(flecs::ChildOf) (default) + .each_entity(|e, p| { + // ... + }); + +let parent = world.entity().set(Position { x: 10.0, y: 20.0 }); + +// Forwards OnAdd event for Position to child +let child = world.entity().child_of_id(parent); +``` + +
  • +
+
+ +## Custom Events +Applications can register custom events to reuse the observer mechanism for purposes other than monitoring ECS events. Custom events, just like builtin events, require three pieces of information: + +- An event +- One or more components +- A single event source + +Consider adding `Position` to entity `my_entity`. This event would look like: + +- Event: `OnAdd` +- Component: `Position` +- Source: `my_entity` + +The difference for a custom event is that it replaces the event with a custom entity id that is created by the application. This is what a custom event could look like: + +- Event: `Synchronized` +- Component: `Position` +- Source: `my_entity` + +Just like with regular events, the entity must have the component that is emitted. This ensures that we can safely pass a reference to the component to an observer callback. + +Custom events are emitted with the `emit` or `enqueue` functions (more later on the latter). The following example shows how to emit and listen for a custom event: + +
+
    +
  • C + +```c +// Create a custom event +ecs_entity_t Synchronized = ecs_new(world); + +// Create an observer that matches a custom event +ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }}, + .events = { Synchronized }, + .callback = OnSynchronizedPosition +}); + +ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + +// Emit custom event +ecs_emit(world, &(ecs_event_desc_t) { + .event = Synchronized, + .entity = e, + .ids = &(ecs_type_t){ + .array = (ecs_id_t[]){ ecs_id(Position) }, + .count = 1 + } +}); +``` + +
  • +
  • C++ + +```cpp +// Create a custom event +struct Synchronized { }; + +// Alternatively, an plain entity could also be used as event +// flecs::entity Synchronized = world.entity(); + +// Create an observer that matches a custom event +world.observer() + .event() + .each([](flecs::entity e, Position& p) { + // ... + }); + +flecs::entity e = world.entity().set(Position{10, 20}); + +// Emit custom event +world.event() + .id() + .entity(e) + .emit(); +``` + +
  • +
  • C# + +```cs +// Create a custom event +public struct Synchronized; + +// Alternatively, an plain entity could also be used as event +// Entity Synchronized = world.Entity(); + +// Create an observer that matches a custom event +world.Observer() + .Event() + .Each((Iter it, int i, ref Position p) => + { + // ... + }); + +Entity e = world.Entity().Set(new Position(10, 20)); + +// Emit custom event +world.Emit() + .Id() + .Entity(e) + .Emit(); +``` + +
  • +
  • Rust + +```rust +// Create a custom event +#[derive(Component)] +struct Synchronized; + +// Alternatively, an plain entity could also be used as event +// let Synchronized = world.entity(); + +// Create an observer that matches a custom event +world + .observer::() + .each_entity(|e, p| { +// ... +}); + +let e = world.entity().set(Position { x: 10.0, y: 20.0 }); + +// Emit custom event +world + .event() + .add::() + .entity(e) + .emit(&Synchronized); +``` + +
  • +
+
+ +### Entity Observers +In many cases an application may want to emit an event for a specific entity without also specifying a component. This is enabled by entity observers. Entity observers are regular observers with the `Any` wildcard specified as the component, essentially expressing that an observer is interested in the source and the event but not in the component. + +The following code shows an example of how to use entity observers: + +
+
    +
  • C + +```c +// Create a custom event +ecs_entity_t Clicked = ecs_new(world); + +// Create entity +ecs_entity_t widget = ecs_entity(world, { .name = "widget" }); + +// Create an entity observer +ecs_observer(world, { + // Not interested in any specific component + .query.terms = {{ EcsAny, .src.id = widget }} + .events = { Clicked }, + .callback = OnClick +}); + +// Emit entity event. Note how no component ids are provided. +ecs_emit(world, &(ecs_event_desc_t) { + .event = Clicked, + .entity = widget +}); +``` + +
  • +
  • C++ + +```cpp +// Create a custom event +struct Clicked { }; + +// Create entity +flecs::entity widget = world.entity("widget"); + +// Create an entity observer +widget.observe([]() { + // ... +}); + +// Emit entity event +widget.emit(); +``` + +
  • +
  • C# + +```cs +// Create a custom event +public struct Clicked; + +// Create entity +Entity widget = world.Entity("widget"); + +// Create an entity observer +widget.Observe(() => +{ + // ... +}); + +widget.Emit(); +``` + +
  • +
  • Rust + +```rust +// Create a custom event +#[derive(Component)] +struct Clicked; + +// Create entity +let widget = world.entity_named("widget"); + +// Create an entity observer +widget.observe::(|| { + // ... +}); + +// Emit entity event +widget.emit(&Clicked); +``` + +
  • +
+
+ +Events can be components, which makes it possible to add event-specific data: + +
+
    +
  • C + +```c +void OnResize(ecs_iter_t *it) { + Resize *p = it->param; // Obtain event data from it->param member + // ... +} + +// Register a component that will be used as event +ECS_COMPONENT(world, Resize); + +// Create entity +ecs_entity_t widget = ecs_entity(world, { .name = "widget" }); + +// Create an entity observer +ecs_observer(world, { + // Not interested in any specific component + .query.terms = {{ EcsAny, .src.id = widget }} + .events = { ecs_id(Resize) }, + .callback = OnResize +}); + +// Emit entity event. +ecs_emit(world, &(ecs_event_desc_t) { + .event = ecs_id(Resize), + .entity = widget, + .param = &(Resize){100, 200} +}); +``` + +
  • +
  • C++ + +```cpp +// Create a custom event +struct Resize { }; + +// Create entity +flecs::entity widget = world.entity("widget"); + +// Create an entity observer +widget.observe([](Resize& r) { + // ... +}); + +// Emit entity event +widget.emit({100, 200}); +``` + +
  • +
  • C# + +```cs +// Create a custom event +public record struct Resize(double Width, double Height); + +// Create entity +Entity widget = world.Entity("widget"); + +// Create an entity observer +widget.Observe((ref Resize r) => +{ + // ... +}); + +widget.Emit(new(100, 200)); +``` + +
  • +
  • Rust + +```rust +// Create a custom event +#[derive(Component)] +struct Resize { + width: u32, + height: u32, +} + +// Create entity +let widget = world.entity_named("widget"); + +// Create an entity observer +widget.observe_payload::<&Resize>(|r| { +// ... +}); + +// Emit entity event +widget.emit(&Resize { +width: 100, +height: 200, +}); +``` + +
  • +
+
+ +### Enqueue vs. Emit +Events can be emitted with either the `emit` or `enqueue` operation. The `emit` operation invokes observers directly, whereas `enqueue` will enqueue the event in the command queue if the world is in deferred mode. When the world is not in deferred mode, `enqueue` defaults to the behavior of `emit`. + +When `enqueue` adds an event to the command queue, the event data is copied in, meaning that the application does not need to keep the event data alive. This is done using the regular `copy` hook that can be registered using `ecs_set_hooks`. In C++ the copy assignment operator is used. Note that as a result, for an event with data to be enqueued in C++, the type has to be copyable. If no copy hook is registered, the behavior defaults to a `memcpy`. + +## Observer Execution +Observers are always executed when the operation that triggered the observer happens, on the thread where the operation is executed. This means that when a component is added to an entity, all `OnAdd` observers will have been invoked by the time the operation is executed. When operations are deferred, because observers are always executed when the operation is executed, invoking the observer will also be delayed. In practice this often means that since most operations are deferred, most observers will also be invoked during sync points. + +An example: + +
+
    +
  • C + +```c +ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }}, + .events = { EcsOnSet }, + .callback = OnSetPosition +}); + +// Observer is invoked as part of operation +ecs_set(world, e, Position, {10, 20}); + +ecs_defer_begin(world); +ecs_add(world, e, Position, {30, 40}); +// Operation is delayed until here, observer is also invoked here +ecs_defer_end(world); +``` + +
  • +
  • C++ + +```cpp +world.observer() + .event(flecs::OnSet) + .each([](flecs::entity e, Position& p) { + // ... + }); + +// Observer is invoked as part of operation +e.set(Position{10, 20}); + +world.defer_begin(); +e.set(Position{20, 30}); +// Operation is delayed until here, observer is also invoked here +world.defer_end(); +``` + +
  • +
  • C# + +```cs +world.Observer() + .Event(Ecs.OnSet) + .Each((Iter it, int i, ref Position p) => + { + // ... + }); + +// Observer is invoked as part of operation +e.Set(new Position(10, 20)); + +world.DeferBegin(); +e.Set(new Position(20, 30)); +// Operation is delayed until here, observer is also invoked here +world.DeferEnd(); +``` + +
  • +
  • Rust + +```rust +world + .observer::() + .each_entity(|e, p| { + // ... + }); + +// Observer is invoked as part of operation +e.set(Position { x: 10.0, y: 20.0 }); + +world.defer_begin(); +e.set(Position { x: 20.0, y: 30.0 }); +// Operation is delayed until here, observer is also invoked here +world.defer_end(); +``` + +
  • +
+
+ +### Observer disabling +Just like systems, observers can be disabled which prevents them from being invoked. Additionally, when the module in which an observer is stored is disabled, all observers are disabled as well. The same happens for systems (when using the default pipeline). This makes it easy to disable all logic in a module with a single operation. + +### Observer Ordering +When observers are invoked, there are a few things to keep in mind when considering the order in which things happen: + +#### Observer order is undefined +When two observers match the same event, the order in which they are executed is undefined. Applications should never rely on observer order, not even if the observed order is apparently "correct" for the application logic. The order in which observers, while deterministic, depends on many different things, and it is easy to break the order. + +#### Event order is undefined between entities +No assumptions should be made about the order in which events are emitted for different entities. This allows the implementation to batch commands for a single entity together, which can greatly improve efficiency. + +#### OnAdd & OnRemove order is undefined +OnAdd and OnRemove observers may be triggered in an order that is different from the order in which the events were emitted, even within the same entity. This is also done to allow the implementation to batch commands. + +#### OnSet order is maintained +The order in which OnSet events are delivered is the same as in which they were emitted. This allows applications to make assumptions about the (component) state of other entities in the observer code and makes it easier to write code that is agnostic to whether operations are deferred or executed immediately. + +#### Custom event order is maintained +Just like for OnSet events, the ordering for custom events is maintained. + +#### Hooks and Events +Hooks always have a well defined order with respect to events: + +- `on_add` hooks are invoked before `OnAdd` events +- `on_set` hooks are invoked before `OnSet` events +- `on_remove` hooks are invoked after `OnRemove` events. + +#### Children are cleaned up before parents +When a parent and its children are deleted, `OnRemove` observers will be invoked for children first, under the condition that there are no cycles in the relationship graph of the deleted entities. This order is maintained for any relationship that has the `(OnDeleteTarget, Delete)` trait (see the Component Traits manual for more details). + +When an entity graph contains cycles, order is undefined. This includes cycles that can be formed using different relationships. diff --git a/vendors/flecs/docs/PrefabsManual.md b/vendors/flecs/docs/PrefabsManual.md new file mode 100644 index 000000000..d6582ff80 --- /dev/null +++ b/vendors/flecs/docs/PrefabsManual.md @@ -0,0 +1,1119 @@ +# Prefabs + +## Introduction +Prefabs are entities that can be used as templates for other entities. They can provide a convenient API for creating assets natively in the ECS. Prefabs have the following features: + +- Prefab components can be shared across instances +- Inherited components can be overridden on a per-instance basis +- Inherited components can be auto-overridden on a per-prefab basis +- Prefab inheritance makes creating variations easy +- Prefabs can have children that are instantiated for instances +- Prefab children can be easily identified with prefab slots +- Prefabs are runtime accessible and modifiable +- Prefabs can be mapped to types for easy access in the C++ API +- Natively supported by Flecs Script & the JSON serializer + +The following example shows how to instantiate a simple prefab: + +
+
    +
  • C + +```c +ECS_COMPONENT(ecs, Defense); + +// Create a SpaceShip prefab with a Defense component. +ecs_entity_t SpaceShip = ecs_entity(ecs, { + .name = "SpaceShip", + .add = ecs_ids( EcsPrefab ) +}); + +ecs_set(ecs, SpaceShip, Defense, {50}); + +// Create two prefab instances +ecs_entity_t inst_1 = ecs_new_w_pair(world, EcsIsA, SpaceShip); +ecs_entity_t inst_2 = ecs_new_w_pair(world, EcsIsA, SpaceShip); + +// Get instantiated component +const Defense *d = ecs_get(world, inst_1, Defense); +``` + +
  • +
  • C++ + +```cpp +struct Defense { + float value; +}; + +// Create a SpaceShip prefab with a Defense component. +flecs::entity SpaceShip = world.prefab("SpaceShip") + .set(Defense{50}); + +// Create two prefab instances +flecs::entity inst_1 = world.entity().is_a(SpaceShip); +flecs::entity inst_2 = world.entity().is_a(SpaceShip); + +// Get instantiated component +const Defense *d = inst_1.get(); +``` + +
  • +
  • C# + +```cs +public record struct Defense(double Value); + +// Create a SpaceShip prefab with a Defense component. +Entity spaceship = world.Entity("Spaceship") + .Set(new Defense(50)); + +// Create two prefab instances +Entity inst1 = world.Entity().IsA(spaceship); +Entity inst2 = world.Entity().IsA(spaceship); + +// Get instantiated component +ref readonly Defense attack = ref inst1.Get(); +``` + +
  • +
  • Rust + +```rust +#[derive(Component)] +struct Defense { +value: u32, +} + +// Create a spaceship prefab with a Defense component. +let spaceship = world.prefab_named("spaceship").set(Defense { value: 50 }); + +// Create two prefab instances +let inst_1 = world.entity().is_a_id(spaceship); +let inst_2 = world.entity().is_a_id(spaceship); + +// Get instantiated component +inst_1.get::<&Defense>(|defense| { +println!("Defense value: {}", defense.value); +}); +``` + +
  • +
+
+ +The following sections go over the different aspects of the prefab feature. + +## The Prefab tag +Prefabs are regular entities with as only difference that prefabs by default are not matched by queries. This allows prefab entities to coexist with regular entities in the same world without impacting game logic. The mechanism used to exclude prefabs from queries is a builtin `Prefab` tag. The following example shows how to create a prefab: + +
+
    +
  • C + +```c +ecs_entity_t myPrefab = ecs_new(world); +ecs_add_id(world, myPrefab, EcsPrefab); +``` + +
  • +
  • C++ + +```cpp +flecs::entity myPrefab = world.entity() + .add(flecs::Prefab); + +// or the shortcut + +flecs::entity myPrefab = world.prefab(); +``` + +
  • +
  • C# + +```cs +Entity myPrefab = world.Prefab(); +``` + +
  • +
  • Rust + +```rust +let myprefab = world.entity().add::(); + +// or the shortcut + +let myprefab = world.prefab(); +``` + +
  • +
+
+ +Queries don't match prefab entities unless the `Prefab` tag is explicitly queried for. The following example shows how to match only prefabs by adding the `Prefab` tag to a query: + +
+
    +
  • C + +```c +// Only match prefab entities +ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, + { EcsPrefab } + } +}); +``` + +
  • +
  • C++ + +```cpp +// Only match prefab entities +world.query_builder() + .with(flecs::Prefab) + .build(); +``` + +
  • +
  • C# + +```cs +// Only match prefab entities +world.QueryBuilder() + .With(Ecs.Prefab) + .Build(); +``` + +
  • +
  • Rust + +```rust +// Only match prefab entities +world.query::<&Position>().with::().build(); +``` + +
  • +
+
+ +> Prefabs are only ignored by queries when they are matched on the `$this` variable, which is the default for query terms. If a prefab component is matched through query traversal, a fixed term source or variable non-`$this` source, the query will not ignore the prefab. + +To match both regular and prefab entities, make the prefab term optional: + +
+
    +
  • C + +```c +// Only match prefab entities +ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, + { EcsPrefab, .oper = EcsOptional } + } +}); +``` + +
  • +
  • C++ + +```cpp +// Only match prefab entities +world.query_builder() + .with(flecs::Prefab).optional() + .build(); +``` + +
  • +
  • C# + +```cs +// Only match prefab entities +world.QueryBuilder() + .With(Ecs.Prefab).Optional() + .Build(); +``` + +
  • +
  • Rust + +```rust +// Only match prefab entities +world +.query::<&Position>() +.with::() +.optional() +.build(); +``` + +
  • +
+
+ +Instead of an optional term, queries can also specify a `MatchPrefabs` flag. This matches the same entities as an optional term, but since the term doesn't need to populate a field slightly improves performance: + +
+
    +
  • C + +```c +// Only match prefab entities +ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) } + }, + .flags = EcsQueryMatchPrefab +}); +``` + +
  • +
  • C++ + +```cpp +// Only match prefab entities +world.query_builder() + .query_flags(EcsQueryMatchPrefab) + .build(); +``` + +
  • +
  • C# + +```cs +world.QueryBuilder() + .QueryFlags(Ecs.QueryMatchPrefab) + .Build(); +``` + +
  • +
  • Rust + +```rust +// Only match prefab entities +world +.query::<&Position>() +.query_flags(QueryFlags::MatchPrefab) +.build(); +``` + +
  • +
+
+ +## Component Inheritance +Entities can inherit components from prefabs. Inherited components are only stored once in memory, and shared across instances. This can be useful for static data that's shared across instances, such as material data, textures or meshes. + +For a component to be inheritable, it needs to have the `(OnInstantiate, Inherit)` trait (for more details see the ComponentTraits manual). The following example shows what happens when a prefab with one inheritable and one non-inheritable component is instantiated: + +
+
    +
  • C + +```c +ECS_COMPONENT(ecs, Health); +ECS_COMPONENT(ecs, Defense); + +// Make Defense component inheritable +ecs_add_pair(world, ecs_id(Defense), + EcsOnInstantiate, EcsInherit); + +// Create prefab +ecs_entity_t SpaceShip = ecs_entity(ecs, { + .name = "SpaceShip", + .add = ecs_ids( EcsPrefab ) +}); + +ecs_set(ecs, SpaceShip, Defense, {50}); +ecs_set(ecs, SpaceShip, Health, {100}); + +// Create prefab instance +ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, SpaceShip); + +// Component is retrieved from instance +const Health *health = ecs_get(world, inst, Health); + +// Component is retrieved from prefab +const Defense *defense = ecs_get(world, inst, Defense); +``` + +
  • +
  • C++ + +```cpp +// Make Defense component inheritable +world.component() + .add(flecs::OnInstantiate, flecs::Inherit); + +// Create prefab +flecs::entity SpaceShip = world.prefab() + .set(Health{100}) + .set(Defense{50}); + +// Create prefab instance +flecs::entity inst = world.entity().is_a(SpaceShip); + +// Component is retrieved from instance +const Health *health = inst.get(); + +// Component is retrieved from prefab +const Defense *defense = inst.get(); +``` + +
  • +
  • C# + +```cs +// Make Defense component inheritable +world.Component() + .Add(Ecs.OnInstantiate, Ecs.Inherit); + +// Create prefab +Entity spaceship = world.Prefab("Spaceship") + .Set(new(100)) + .Set(new(50)); + +// Create a prefab instance +Entity inst = world.Entity().IsA(spaceship); + +// Component is retrieved from instance +ref readonly Attack attack = ref inst.Get(); + +// Component is retrieved from prefab +ref readonly Defense defense = ref inst.Get(); +``` + +
  • +
  • Rust + +```rust +// Make Defense component inheritable +world +.component::() +.add_trait::<(flecs::OnInstantiate, flecs::Inherit)>(); + +// Create prefab +let spaceship = world +.prefab() +.set(Health { value: 100 }) +.set(Defense { value: 50 }); + +// Create prefab instance +let inst = world.entity().is_a_id(spaceship); + +// Component is retrieved from instance +inst.get::<&Health>(|health| { +println!("Health value: {}", health.value); +}); + +// Component is retrieved from prefab +inst.get::<&Defense>(|defense| { +println!("Defense value: {}", defense.value); +}); +``` + +
  • +
+
+ +The `owns` operation can be used to tell whether a component is owned by the instance or inherited from a prefab: + +
+
    +
  • C + +```c +if (ecs_owns(world, inst, Defense)) { + // not inherited +} +``` + +
  • +
  • C++ + +```cpp +if (inst.owns()) { + // not inherited +} +``` + +
  • +
  • C# + +```cs +if (inst.Owns()) { + // not inherited +} +``` +
  • +
  • Rust + +```rust +if inst.owns::() { +// not inherited +} +``` + +
  • +
+
+ +The `target_for` operation can be used to determine from which prefab (or regular entity) a component is inherited: + +
+
    +
  • C +```c +ecs_entity_t inherited_from = + ecs_target_for(world, inst, Defense); +if (!inherited_from) { + // not inherited +} +``` + +
  • +
  • C++ + +```cpp +flecs::entity inherited_from = + inst.target_for(); +if (!inherited_from) { + // not inherited +} +``` + +
  • +
  • C# + +```cs +Entity inheritedFrom = inst.TargetFor(); +if (inheritedFrom == 0) { + // not inherited +} +``` + +
  • +
  • Rust + +```rust +let inherited_from = inst.target::(0); +if inherited_from.is_none() { +// not inherited +} +``` + +
  • +
+
+ +## Component Overriding +When an instance inherits a component from a prefab, it can be overridden with a value that's specific to the instance. To override a component, simply add or set it on the instance. The following example shows how: + +
+
    +
  • C + +```c +ECS_COMPONENT(ecs, Defense); + +// Make Defense component inheritable +ecs_add_pair(world, ecs_id(Defense), + EcsOnInstantiate, EcsInherit); + +// Create prefab +ecs_entity_t SpaceShip = ecs_entity(ecs, { + .name = "SpaceShip", + .add = ecs_ids( EcsPrefab ) +}); + +ecs_set(ecs, SpaceShip, Defense, {50}); + +// Create prefab instances +ecs_entity_t inst_a = ecs_new_w_pair(world, EcsIsA, SpaceShip); +ecs_entity_t inst_b = ecs_new_w_pair(world, EcsIsA, SpaceShip); + +// Override Defense only for inst_a +ecs_set(world, inst_a, Defense, {75}); +``` + +
  • +
  • C++ + +```cpp +// Make Defense component inheritable +world.component() + .add(flecs::OnInstantiate, flecs::Inherit); + +// Create prefab +flecs::entity SpaceShip = world.prefab() + .set(Defense{50}); + +// Create prefab instance +flecs::entity inst_a = world.entity().is_a(SpaceShip); +flecs::entity inst_b = world.entity().is_a(SpaceShip); + +// Override Defense only for inst_a +inst_a.set(Defense{75}); +``` + +
  • +
  • C# + +```cs +// Make Defense component inheritable +world.Component() + .Add(Ecs.OnInstantiate, Ecs.Inherit); + +// Create prefab +Entity spaceship = world.Prefab("Spaceship") + .Set(new(50)); + +// Create a prefab instance +Entity instA = world.Entity().IsA(spaceship); +Entity instB = world.Entity().IsA(spaceship); + +// Override Defense only for instA +instA.Set(new Defense(75)); +``` + +
  • +
  • Rust + +```rust +// Make Defense component inheritable +world +.component::() +.add_trait::<(flecs::OnInstantiate, flecs::Inherit)>(); + +// Create prefab +let spaceship = world.prefab().set(Defense { value: 50 }); + +// Create prefab instance +let inst_a = world.entity().is_a_id(spaceship); +let inst_b = world.entity().is_a_id(spaceship); + +// Override Defense only for inst_a +inst_a.set(Defense { value: 75 }); +``` + +
  • +
+
+ +When a component is overridden by adding it to an instance, the component on the instance is initialized with the value from the prefab component. An example: + +
+
    +
  • C + +```c +ECS_COMPONENT(ecs, Defense); + +// Make Defense component inheritable +ecs_add_pair(world, ecs_id(Defense), + EcsOnInstantiate, EcsInherit); + +// Create prefab +ecs_entity_t SpaceShip = ecs_entity(ecs, { + .name = "SpaceShip", + .add = ecs_ids( EcsPrefab ) +}); + +ecs_set(ecs, SpaceShip, Defense, {50}); + +// Create prefab instances +ecs_entity_t inst_a = ecs_new_w_pair(world, EcsIsA, SpaceShip); +ecs_entity_t inst_b = ecs_new_w_pair(world, EcsIsA, SpaceShip); + +// Override Defense only for inst_a +ecs_add(world, inst_a, Defense); // Initialized with value 50 +``` + +
  • +
  • C++ + +```cpp +// Make Defense component inheritable +world.component() + .add(flecs::OnInstantiate, flecs::Inherit); + +// Create prefab +flecs::entity SpaceShip = world.prefab() + .set(Defense{50}); + +// Create prefab instance +flecs::entity inst_a = world.entity().is_a(SpaceShip); +flecs::entity inst_b = world.entity().is_a(SpaceShip); + +// Override Defense only for inst_a +inst_a.add(); // Initialized with value 50 +``` + +
  • +
  • C# + +```cs +// Make Defense component inheritable +world.Component() + .Add(Ecs.OnInstantiate, Ecs.Inherit); + +// Create prefab +Entity spaceship = world.Prefab("Spaceship") + .Set(new(100)) + .Set(new(50)); + +// Create a prefab instance +Entity instA = world.Entity().IsA(spaceship); +Entity instB = world.Entity().IsA(spaceship); + +// Override Defense only for instA +instA.Add(); // Initialized with value 50 +``` + +
  • +
  • Rust + +```rust +// Make Defense component inheritable +world +.component::() +.add_trait::<(flecs::OnInstantiate, flecs::Inherit)>(); + +// Create prefab +let spaceship = world.prefab().set(Defense { value: 50 }); + +// Create prefab instance +let inst_a = world.entity().is_a_id(spaceship); +let inst_b = world.entity().is_a_id(spaceship); + +// Override Defense only for inst_a +inst_a.add::(); // Initialized with value 50 +``` + +
  • +
+
+ +When an override is removed, the original value of the prefab is reexposed. + +## Auto Overriding +When a component is configured to be inheritable, sometimes it can still be useful for a specific prefab to have the instances always own the component. This can be achieved with an auto override, which flags a component on the prefab to be always overridden on instantiation: + +
+
    +
  • C + +```c +ECS_COMPONENT(ecs, Defense); + +// Make Defense component inheritable +ecs_add_pair(world, ecs_id(Defense), + EcsOnInstantiate, EcsInherit); + +// Create prefab +ecs_entity_t SpaceShip = ecs_entity(ecs, { + .name = "SpaceShip", + .add = ecs_ids( EcsPrefab ) +}); + +// Set & auto override Defense +ecs_set(ecs, SpaceShip, Defense, {50}); +ecs_auto_override(world, SpaceShip, Defense); + +// Create prefab instances +ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, SpaceShip); +ecs_owns(world, inst, Defense); // true +``` + +
  • +
  • C++ + +```cpp +// Make Defense component inheritable +world.component() + .add(flecs::OnInstantiate, flecs::Inherit); + +// Create prefab +flecs::entity SpaceShip = world.prefab() + .set_auto_override(Defense{50}); // Set & auto override Defense + +// Create prefab instance +flecs::entity inst = world.entity().is_a(SpaceShip); +inst.owns(); // true +``` + +
  • +
  • C# + +```cs +// Make Defense component inheritable +world.Component() + .Add(Ecs.OnInstantiate, Ecs.Inherit); + +// Create prefab +Entity spaceship = world.Prefab() + .SetAutoOverride(new(50)); // Set & auto override Defense + +// Create prefab instance +Entity inst = world.Entity().IsA(spaceship); +inst.Owns(); // true +``` + +
  • +
  • Rust + +```rust +// Make Defense component inheritable +world +.component::() +.add_trait::<(flecs::OnInstantiate, flecs::Inherit)>(); + +// Create prefab +let spaceship = world.prefab().set_auto_override(Defense { value: 50 }); // Set & auto override Defense + +// Create prefab instance +let inst = world.entity().is_a_id(spaceship); +inst.owns::(); // true +``` + +
  • +
+
+ +Auto overrides can also be added to prefabs that don't have the actual component. In this case the component is not copied from the prefab (there is nothing to copy), but is added & default constructed on the instance. + +This also works for prefab children. Adding an auto override to a child entity without adding the component will add & default construct the component on the instance child. + +## Prefab Variants +Prefabs can inherit from each other to create prefab variations without duplicating the prefab data. The following example shows a prefab and a prefab variant. + +
+
    +
  • C + +```c +ECS_COMPONENT(ecs, Health); +ECS_COMPONENT(ecs, Defense); + +// Create prefab +ecs_entity_t SpaceShip = ecs_entity(ecs, { + .name = "SpaceShip", + .add = ecs_ids( EcsPrefab ) +}); + +ecs_set(ecs, SpaceShip, Defense, {50}); +ecs_set(ecs, SpaceShip, Health, {100}); + +// Create prefab variant +ecs_entity_t freighter = ecs_entity(ecs, { + .name = "Freighter", + .add = ecs_ids( EcsPrefab, ecs_isa(SpaceShip) ) +}); + +// Override the Health component of the freighter +ecs_set(ecs, freighter, Health, {150}); + +// Create prefab instance +ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, Freighter); +const Health *health = ecs_get(world, inst, Health); // 150 +const Defense *defense = ecs_get(world, inst, Defense); // 50 +``` + +
  • +
  • C++ + +```cpp +// Create prefab +flecs::entity SpaceShip = world.prefab("SpaceShip") + .set(Defense{50}) + .set(Health{100}); + +// Create prefab variant +flecs::entity Freighter = world.prefab("Freighter") + .is_a(SpaceShip) + .set(Health{150}); // Override the Health component of the freighter + +// Create prefab instance +flecs::entity inst = world.entity().is_a(Freighter); +const Health *health = inst.get(); // 150 +const Defense *defense = inst.get(); // 50 +``` + +
  • +
  • C# + +```cs +// Create prefab +Entity spaceship = world.Prefab("Spaceship") + .Set(new(50)) + .Set(new(100)); + +// Create prefab variant +Entity freighter = world.Prefab("Freighter") + .IsA(spaceShip) + .Set(new(150)); // Override the Health component of the freighter + +// Create prefab instance +Entity inst = world.Entity().IsA(freighter); +ref readonly Health health = ref inst.Get(); // 150 +ref readonly Defense defense = ref inst.Get(); // 50 +``` + +
  • +
  • Rust + +```rust +// Create prefab +let spaceship = world +.prefab_named("spaceship") +.set(Defense { value: 50 }) +.set(Health { value: 100 }); + +// Create prefab variant +let freighter = world +.prefab_named("Freighter") +.is_a_id(spaceship) +.set(Health { value: 150 }); // Override the Health component of the freighter + +// Create prefab instance +let inst = world.entity().is_a_id(freighter); +inst.get::<&Health>(|health| { +println!("Health value: {}", health.value); // 150 +}); +inst.get::<&Defense>(|defense| { +println!("Defense value: {}", defense.value); // 50 +}); + +``` + +
  • +
+
+ +## Prefab Hierarchies +When a prefab has children, the entire subtree of a prefab is copied to the instance. Other than with the instance itself where components can be inherited from a prefab, child entities never inherit components from prefab children. Prefab hierarchies are created in the same way as entity hierarchies: + +
+
    +
  • C + +```c +// Create a prefab hierarchy. +ecs_entity_t SpaceShip = ecs_entity(ecs, { + .name = "SpaceShip", + .add = ecs_ids( EcsPrefab ) +}); + +ecs_entity_t Cockpit = ecs_entity(ecs, { + .name = "Cockpit", + .parent = SpaceShip, // shortcut to add_pair(EcsChildOf) + .add = ecs_ids( EcsPrefab ) +}); + +// Instantiate the prefab hierarchy +ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, SpaceShip); + +// Lookup instantiated child +ecs_entity_t inst_cockpit = ecs_lookup_child(ecs, inst, "Cockpit"); +``` + +
  • +
  • C++ + +```cpp +flecs::entity SpaceShip = world.prefab("SpaceShip"); +flecs::entity Cockpit = world.prefab("Cockpit") + .child_of(SpaceShip); + +// Instantiate the prefab hierarchy +flecs::entity inst = ecs.entity().is_a(SpaceShip); + +// Lookup instantiated child +flecs::entity inst_cockpit = inst.lookup("Cockpit"); +``` + +
  • +
  • C# + +```cs +Entity spaceship = world.Prefab("Spaceship"); +Entity cockpit = world.Prefab("Cockpit") + .ChildOf(spaceship); + +// Instantiate the prefab hierarchy +Entity inst = ecs.Entity().IsA(spaceship); + +// Lookup instantiated child +Entity instCockpit = inst.Lookup("Cockpit"); +``` + +
  • +
  • Rust + +```rust +let spaceship = world.prefab_named("spaceship"); +let cockpit = world.prefab_named("Cockpit").child_of_id(spaceship); + +// Instantiate the prefab hierarchy +let inst = world.entity().is_a_id(spaceship); + +// Lookup instantiated child +let inst_cockpit = inst.lookup("Cockpit"); +``` + +
  • +
+
+ +## Prefab Slots +When a prefab hierarchy is instantiated often code will want to refer to a specific instantiated child. A typical example is a turret prefab with a turret head that needs to rotate. + +While it is possible to lookup a child by name and store it on a component, this adds boilerplate and reduces efficiency. Prefab slots make this easier. + +A prefab child can be created as a slot. Slots are created as relationships on the instance, with as target of the relationship the instantiated child. The slot is added as a union relationship which doesn't fragment archetypes. + +The following example shows how to create and use a prefab slot: + +
+
    +
  • C + +```c +// Create a prefab hierarchy. +ecs_entity_t SpaceShip = ecs_entity(ecs, { + .name = "SpaceShip", + .add = ecs_ids( EcsPrefab ) +}); + +ecs_entity_t Cockpit = ecs_entity(ecs, { + .name = "Cockpit", + .parent = SpaceShip, + .add = ecs_ids( + EcsPrefab, + ecs_pair(EcsSlotOf, SpaceShip) // mark as slot + ) +}); + +// Instantiate the prefab hierarchy +ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, SpaceShip); + +// Lookup instantiated child +ecs_entity_t inst_cockpit = ecs_target(world, inst, Cockpit, 0); +``` + +
  • +
  • C++ + +```cpp +flecs::entity SpaceShip = world.prefab("SpaceShip"); +flecs::entity Cockpit = world.prefab("Cockpit") + .child_of(SpaceShip) + .slot(); // Defaults to (SlotOf, SpaceShip) + +// Instantiate the prefab hierarchy +flecs::entity inst = ecs.entity().is_a(SpaceShip); + +// Lookup instantiated child +flecs::entity inst_cockpit = inst.target(CockPit); +``` + +
  • +
  • C# + +```cs +Entity spaceship = world.Prefab("Spaceship"); +Entity cockpit = world.Prefab("Cockpit") + .ChildOf(spaceship) + .SlotOf(spaceship); + +// Instantiate the prefab hierarchy +Entity inst = ecs.Entity().IsA(spaceship); + +// Lookup instantiated child +Entity instCockpit = inst.Target(cockpit); +``` + +
  • +
  • Rust + +```rust +let spaceship = world.prefab_named("Spaceship"); +let cockpit = world.prefab_named("Cockpit").child_of_id(spaceship).slot(); // Defaults to (SlotOf, spaceship) + +// Instantiate the prefab hierarchy +let inst = world.entity().is_a_id(spaceship); + +// Lookup instantiated child +let inst_cockpit = inst.target_id(cockpit, 0); +``` + +
  • +
+
+ +## Prefab Types (C++, C#) +Like entities and components, prefabs can be associated with a C++ type. This makes it easier to instantiate prefabs as it is not necessary to pass prefab handles around in the application. The following example shows how to associate types with prefabs: + +
+
    +
  • C++ + +```cpp +struct SpaceShip {}; + +// Create prefab associated with the SpaceShip type +world.prefab() + .set({ 50 }) + .set({ 100 }); + +// Instantiate prefab with type +flecs::entity inst = world.entity() + .is_a(); + +// Lookup prefab handle +flecs::entity prefab = world.lookup("SpaceShip"); +``` + +
  • +
  • C# + +```cs +public struct Spaceship { } + +// Create prefab associated with the Spaceship type +world.Prefab() + .Set(new(50)) + .Set(new(100)); + +// Instantiate prefab with type +Entity inst = world.Entity() + .IsA(); + +// Lookup prefab handle +Entity prefab = world.Lookup("Spaceship"); +``` + +
  • +
  • Rust + +```rust +#[derive(Component)] +struct Spaceship; + +// Create prefab associated with the spaceship type +world +.prefab_type::() +.set(Defense { value: 50 }) +.set(Health { value: 100 }); + +// Instantiate prefab with type +let inst = world.entity().is_a::(); + +// Lookup prefab handle +let prefab = world.lookup("spaceship"); +``` + +
  • +
+
diff --git a/vendors/flecs/docs/Queries.md b/vendors/flecs/docs/Queries.md index 80567981b..30c4bb2b4 100644 --- a/vendors/flecs/docs/Queries.md +++ b/vendors/flecs/docs/Queries.md @@ -1,32 +1,25 @@ # Queries -At the core of an Entity Component System are queries, which make it possible to find entities matching a list of conditions in realtime, for example: -``` -Position, Velocity -``` - -This query returns all entities that at least have the `Position` and `Velocity` components. Queries provide direct access to cache efficient storages of matched components, which gives applications the ability to process large numbers (think millions) of entities each frame. +## Introduction +Queries enable games to quickly find entities that match a list of conditions, and are at the core of many Flecs features like systems, observers, tooling and serialization. -## Highlights -Here are some of the highlights of Flecs queries: +Flecs queries can do anything from returning entities that match a simple list of components, to matching complex patterns against entity graphs. -- Queries can [cache results](#cached-queries), which removes search overhead from time critical game loops +This manual contains a full overview of the query features available in Flecs. Some of the features of Flecs queries are: -- Queries can efficiently traverse [entity relationship](#Relationships) graphs, reducing the need for building custom hard to maintain data structures ([blog](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c)). +- Queries can be cached, uncached or a mix of both, which lets games pick the ideal balance between iteration performance, query creation performance and administration overhead. -- Queries can be created at runtime and match components that are created at runtime. +- Queries have advanced features for matching patterns against entity graphs which can be used to build immersive game play without having to build and maintain complex custom data structures ([blog](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c)). - Queries support `and`, `or`, `not` and `optional` [operators](#operator-overview). -- Queries can combine components from multiple [sources](#source), like a transform system that uses `Position` component from an entity and its parent. - -- Queries can be observed, allowing applications to get notified when entities start or stop matching a query. +- Queries can combine components from multiple [sources](#source), like matching entities with a `Transform` component and a parent that have a `Transform` component. -- Query results are self describing, which means iterators can be passed to generic code which can then do things like [serializing it to JSON](https://github.com/SanderMertens/flecs/blob/master/include/flecs/addons/json.h). +- Queries can be created at runtime and match components that are created at runtime. -- Queries can be created with the regular API or by parsing a query string, making it possible to [create tools that create queries at runtime](https://www.flecs.dev/explorer/). +- Queries can be created with the Flecs APIs or by using the Flecs Query Language, which enables query creation in [tools like the explorer](https://www.flecs.dev/explorer/). -- Queries support [component inheritance](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules/component_inheritance). +- The Flecs REST API has a query endpoint which can be used to build remote game servers. ## Definitions @@ -40,124 +33,181 @@ Here are some of the highlights of Flecs queries: | Target | Used to refer to second element of pair | | Source | Entity on which a term is matched | | Iterator | Object used to iterate a query | +| Term | An element of a query that expresses a condition to match | | Field | A single value or array of values made available by an iterator. An iterator usually provides a field per query term. | ## Examples -Make sure to check out the code examples in the repository: - -https://github.com/SanderMertens/flecs/tree/master/examples +Make sure to check out the query code examples in the repository: - [queries (C)](https://github.com/SanderMertens/flecs/tree/master/examples/c/queries) - [queries (C++)](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/queries) - - [rules (C)](https://github.com/SanderMertens/flecs/tree/master/examples/c/rules) - - [rules (C++)](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules) + - [queries (Rust)](https://github.com/Indra-db/Flecs-Rust/tree/main/flecs_ecs/examples/flecs/queries) -## Types -Flecs has different query types, which are optimized for different kinds of use cases. This section provides a brief overview of each kind: +## Performance and Caching +Understanding the basic architecture of queries helps to make the right tradeoffs when using queries in games. The biggest impact on query performance is whether a query is cached or not. This section goes over what caching is, how it can be used and when it makes sense to use it. - ### Filters - Filters are cheap to create, low overhead, reasonably efficient to iterate. They are good for ad-hoc queries with runtime-defined conditions. An example: - - ```c - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { ecs_id(Position) }, { ecs_id(Velocity) } - } -}); +### Caching: what is it? +Flecs is an archetype ECS, which means that entities with exactly the same components are grouped together in an "archetype" (also called a "table"). Archetypes are created on the fly whenever a new component _combination_ is created in the ECS. For example: -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - for (int i = 0; i < it.count; i ++) { - p[i].x += v[i].x; - p[i].y += v[i].y; - } -} -``` ```cpp -flecs::filter f = - world.filter(); +flecs::entity e1 = world.entity(); +e1.set(Position{10, 20}); // create archetype [Position] +e1.set(Velocity{1, 2}); // create archetype [Position, Velocity] -f.each([](Position& p, Velocity& v) { - p.x += v.x; - p.y += v.y; -}); +flecs::entity e2 = world.entity(); +e2.set(Position{10, 20}); // archetype [Position] already exists +e2.set(Velocity{1, 2}); // archetype [Position, Velocity] already exists +e2.set(Mass{100}); // create archetype [Position, Velocity, Mass] + +// e1 is now in archetype [Position, Velocity] +// e2 is now in archetype [Position, Velocity, Mass] ``` -### Cached Queries -Cached queries cache the output of a filter. They are more expensive to create and have higher overhead, but are the fastest to iterate. Cached queries are the default for systems. An example: +Archetypes are important for queries. Since all entities in an archetype have the same components, and a query matches entities with specific components, a query can often match entire archetypes instead of individual entities. This is one of the main reasons why queries in an archetype ECS are fast. -```c -ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_id(Position) }, { ecs_id(Velocity) } - } -}); +The second reason that queries in an archetype ECS are fast is that they are cheap to cache. While an archetype is created for each unique component combination, games typically only use a finite set of component combinations which are created quickly after game assets are loaded. -ecs_iter_t it = ecs_query_iter(world, q); -while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); +This means that instead of searching for archetypes each time a query is evaluated, a query can instead cache the list of matching archetypes. This is a cheap cache to maintain: even though entities can move in and out of archetypes, the archetypes themselves are often stable. - for (int i = 0; i < it.count; i ++) { - p[i].x += v[i].x; - p[i].y += v[i].y; - } -} -``` -```cpp -flecs::query q = - world.query(); +If none of that made sense, the main thing to remember is that a cached query does not actually have to search for entities. Iterating a cached query just means iterating a list of prematched results, and this is really, really fast. -q.each([](Position& p, Velocity& v) { - p.x += v.x; - p.y += v.y; -}); -``` +### Tradeoffs +Flecs has both cached and uncached queries. If cached queries are so fast, why even bother with uncached queries? There are four main reasons: + +- Cached queries are really fast to iterate, but take more time to create because the cache must be initialized first. + +- Cached queries have a higher RAM utilization, whereas uncached queries have very little overhead and are stateless. + +- Cached queries add overhead to archetype creation/deletion, as these changes have to get propagated to caches. + +- While caching archetypes is fast, some query features require matching individual entities, which are not efficient to cache (and aren't cached). + +As a rule of thumb, if you have a query that is evaluated each frame (as is typically the case with systems), they will benefit from being cached. If you need to create a query ad-hoc, an uncached query makes more sense. + +Ad-hoc queries are often necessary when a game needs to find entities that match a condition that is only known at runtime, for example to find all child entities for a specific parent. + +### Cache kinds +Queries can be created with a "cache kind", which specifies the caching behavior for a query. Flecs has four different caching kinds: + +| Kind | C | C++ | Description | +|---------|---|-----|-------------| +| Default | `EcsQueryCacheDefault` | `flecs::QueryCacheDefault` | Behavior determined by query creation context | +| Auto | `EcsQueryCacheAuto` | `flecs::QueryCacheAuto` | Cache query terms that are cacheable | +| All | `EcsQueryCacheAll` | `flecs::QueryCacheAll` | Require that all query terms are cached | +| None | `EcsQueryCacheNone` | `flecs::QueryCacheNone` | No caching | + +The following sections describe each of the kinds. + +#### Default +When no cache kind is specified, queries will be created with the Default caching kind. A query with the Default kind will be created as cached (using the Auto kind, see next section) if a query is associated with an entity, and uncached if it isn't. + +What does it mean for a query to be associated with an entity? When a query is created, an application can provide an entity to associate with the query. This binds the lifecycle of the query with that of the entity, and makes it possible to lookup the query (by name) in tools like the explorer. + +The most common case of this is systems. Flecs systems have entity handles, which are associated with system queries. This means that all system queries by default are cached, unless specified otherwise. + +The rationale behind this is that if a query is associated with an entity, it will likely be reused and outlive the scope in which the query is created. Queries that are reused many times across frames are good candidates for caching. + +#### Auto +When the Auto kind is specified, all query terms that can be matched will be matched. Query features that rely on matching entire archetypes can typically be cached, whereas features that return individual entities cannot be cached. The following query features are cacheable: + +- Components, Tags and Pairs +- Terms with a `$this` source (default behavior, see variables) +- Wildcards +- Operators +- Query traversal + +There are scenarios that are cacheable in theory but aren't cached yet by the current implementation. Over time the query engine will be extended to cache terms with variables, as long as they match entire archetypes. + +Queries with the Auto kind can mix terms that are cached and uncached. + +#### All +Queries with the All kind require all terms to be cacheable. This forces a query to only use features that can be cached. If a query with kind All uses features that cannot be cached, query creation will fail. + +#### None +Queries with the None kind will not use any caching. + +### Performance tips & tricks + +#### Rematching +Queries that use traversal (either `up` or `cascade`) can trigger query "rematching". This is a process that ensures that a query cache that matches components on an entity reached through traversal stays up to date. + +A typical example of this is a query that matched a `Transform` component on a parent entity. If the `Transform` component is removed from the parent it invalidates the cache, and rematching will happen. + +Rematching can be an expensive process, especially in games with lots of archetypes. To learn if an application is slowed down by rematching, connect the explorer to it with the `flecs::stats` module imported (see the REST API manual), and inspect the world statistics page. + +If rematching is taking up a significant amount of time, consider changing cached queries with traversal to uncached. This will increase query evaluation time, but should get rid of the query rematching cost. + +Rematching is a temporary solution to a complex problem that will eventually be solved with a much cheaper mechanism. For now however, rematching is something that needs to be monitored for queries that use query traversal features. + +#### Empty archetype optimization +Cached queries have an optimization where they store empty archetypes in a separate list from non-empty archetypes. This generally improves query iteration speed, as games can have large numbers of empty archetypes that could waste time when iterated by queries. + +However, to keep empty archetypes and non-empty archetypes in separate lists, events have to be emitted from archetypes to queries whenever their state changes. When emitting these events becomes too expensive, games can opt out of empty archetype optimization, and instead periodically cleanup empty archetypes. + +To do this, a query should be created with the `EcsQueryMatchEmptyTables` flag, and the `ecs_delete_empty_tables` function should be called periodically. An example: -### Rules -Rules are a constraint-based query engine capable of traversing graphs. They are more expensive to create than filters, have low overhead, and their iteration performance depends on query complexity. An example: +
+
    +
  • C ```c -ecs_rule_t *r = ecs_rule(world, { - .terms = { - { ecs_id(Position) }, { ecs_id(Velocity) } - } +// Create Position, Velocity query that matches empty archetypes. +ecs_query(world, { + .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } }, + .cache_kind = EcsQueryCacheAuto, + .flags = EcsQueryMatchEmptyTables }); -ecs_iter_t it = ecs_rule_iter(world, r); -while (ecs_rule_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - for (int i = 0; i < it.count; i ++) { - p[i].x += v[i].x; - p[i].y += v[i].y; - } -} +// Delete empty archetypes that have been empty for 10 calls to this function. +ecs_delete_empty_tables(world, 0, 0, 10, 0, 0); ``` + +
  • +
  • C++ + ```cpp -flecs::rule r = - world.rule(); +// Create Position, Velocity query that matches empty archetypes. +flecs::query q = world.query_builder() + .cached() + .query_flags(EcsQueryMatchEmptyTables) + .build(); -r.each([](Position& p, Velocity& v) { - p.x += v.x; - p.y += v.y; -}); +// Delete empty archetypes that have been empty for 10 calls to this function. +ecs_delete_empty_tables(world, 0, 0, 10, 0, 0); +``` + +
  • +
  • Rust + +```rust +// Create Position, Velocity query that matches empty archetypes. +let q = world + .query::<(&mut Position, &Velocity)>() + .set_cached() + .query_flags(QueryFlags::MatchEmptyTables) + .build(); + +// Delete empty archetypes that have been empty for 10 calls to this function. +world.delete_empty_tables(0, 0, 10, 0, 0.0); ``` -For more information on how each implementation performs, see [Performance](#performance). +
  • +
+
+ +This will cause queries to return empty archetypes (iterators with count set to 0) which is something the application code will have to handle correctly. -## Creation -This section explains how to create queries in the different language bindings and the flecs query DSL. +## Creating queries +This section explains how to create queries in the different language bindings and the flecs Flecs Query Language. -### Query Descriptors (C) -Query descriptors are the C API for creating queries. The API uses a type called `ecs_filter_desc_t`, to describe the structure of a query. This type is used to create all query kinds (`ecs_filter_t`, `ecs_query_t`, `ecs_rule_t`). An example: +
+
    +
  • C + +Query descriptors are the C API for creating queries. An example: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) }, } @@ -167,44 +217,60 @@ ecs_filter_t *f = ecs_filter(world, { The example shows the short notation, which looks like this when expanded: ```c -ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ +ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .terms = { { ecs_id(Position) }, { ecs_id(Velocity) }, } }); ``` +Note how component types are added with the `ecs_id()` macro. This translates the component type to the component id that queries require. Tags and pairs do not require the `ecs_id()` macro: + +```c +ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .terms = { + { Npc }, { ecs_pair(Likes, Bob) }, + } +}); +``` + Query descriptors can also be used by the C++ API. However because C++ does not support taking the address of a temporary, and not all language revisions support designated initializers, query descriptors in C++ should be used like this: ```c -ecs_filter_desc_t desc = {}; // Zero-initialize the struct +ecs_query_desc_t desc = {}; // Zero-initialize the struct desc.terms[0].id = ecs_id(Position); desc.terms[1].id = ecs_id(Velocity); -ecs_filter_t *f = ecs_filter_init(world, &desc); +ecs_query_t *q = ecs_query_init(world, &desc); ``` -The following table provides an overview of the query types with the init/fini functions: +Queries have to be deleted with the `ecs_query_fini` function, except when a query is associated with an entity. An example: + +```c +ecs_entity_t query_entity = ecs_new(world); +ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .entity = query_entity, + .terms = { + { ecs_id(Position) }, { ecs_id(Velocity) }, + } +}); -| Kind | Type | Init | Fini | Descriptor type | -|--------|----------------|-------------------|-------------------|---------------------| -| Filter | `ecs_filter_t` | `ecs_filter_init` | `ecs_filter_fini` | `ecs_filter_desc_t` | -| Query | `ecs_query_t` | `ecs_query_init` | `ecs_query_fini` | `ecs_query_desc_t` | -| Rule | `ecs_rule_t` | `ecs_rule_init` | `ecs_rule_fini` | `ecs_filter_desc_t` | +ecs_delete(world, query_entity); // Also deletes query +``` -Additionally the descriptor types for systems (`ecs_system_desc_t`) and observers (`ecs_observer_desc_t`) embed the `ecs_filter_desc_t` descriptor type. +
  • +
  • C++ -### Query Builder (C++) -Query builders are the C++ API for creating queries. The builder API is built on top of the descriptor API, and adds a layer of convenience and type safety that matches modern idiomatic C++. The builder API is implemented for all query kinds (filters, cached queries, rules). An example of a simple query: +Query builders are the C++ API for creating queries. The builder API is built on top of the descriptor API, and adds a layer of convenience and type safety that matches modern idiomatic C++. An example of a simple query: ```cpp -flecs::filter f = - world.filter(); +flecs::query q = + world.query(); ``` Queries created with template arguments provide a type safe way to iterate components: ```cpp -f.each([](Position& p, const Velocity& v) { +q.each([](Position& p, const Velocity& v) { p.x += v.x; p.y += v.y; }); @@ -213,35 +279,52 @@ f.each([](Position& p, const Velocity& v) { The builder API allows for incrementally constructing queries: ```cpp -flecs::filter q = world.filter_builder(); -f.term(); +flecs::query q = world.query_builder(); +q.with(); if (add_npc) { - f.term(); // Conditionally add + q.with(); // Conditionally add } -f.build(); // Create query +q.build(); // Create query +``` + +
  • +
  • Rust + +The query builder API is built on top of the term builder API, and adds a layer of convenience and type safety that matches modern idiomatic Rust. An example of a simple query: + +```rust +// new_query is a convenience function that creates a query with the default builder +let q = world.new_query::<(&mut Position, &Velocity)>(); +``` + +Queries created with generic arguments provide a type safe way to iterate components: + +```rust +q.each(|(p, v)| { + p.x += v.x; + p.y += v.y; +}); ``` -The following table provides an overview of the query types with the factory functions: +The builder API allows for incrementally constructing queries, but also gives you access to more advanced features (see later sections): -| Kind | Type | Factory | -|--------|-----------------|-------------------------| -| Filter | `flecs::filter` | `world::filter_builder` | -| Query | `flecs::query` | `world::query_builder` | -| Rule | `flecs::rule` | `world::rule_builder` | +```rust +let mut q = world.query::<(&mut Position, &Velocity)>(); +q.with::<&Velocity>(); -Additional helper methods have been added to the C++ API to replace combinations of the `term` method with other methods. They are the following: +if add_npc { + q.with::<&Foo>(); // Conditionally add +} + +q.build(); // Create query +``` -| Term | Equivalent | -|------------------------|------------------------------| -| `.with()` | `.term()` | -| `.without()`| `.term().not_()` | -| `.read()` | `.term().read()` | -| `.write()` | `.term().write()` | +
  • +
  • Flecs Query Language -### Query DSL -The query DSL (domain specific language) is a string format that can represent a query. The query DSL is used by convenience macros in the C API like `ECS_SYSTEM` and `ECS_OBSERVER`, and makes it easier to create queries at runtime for tools like https://www.flecs.dev/explorer/. An example of a simple query in the DSL: +The Flecs Query Language is a string format that can be parsed into a query. The format is used by convenience macros in the C API like `ECS_SYSTEM` and `ECS_OBSERVER`, and makes it easier to create queries at runtime for tools like https://www.flecs.dev/explorer/. An example of a simple query in the DSL: ``` Position, [in] Velocity @@ -256,41 +339,48 @@ ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity); Queries can be created from expressions with both the descriptor and builder APIs: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .expr = "Position, [in] Velocity" }); ``` ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> q = world.query_builder() .expr("Position, [in] Velocity") .build(); ``` -The query DSL requires the `FLECS_PARSER` addon to be included in a build. +For more details on the syntax, see the Flecs Query Language manual. + +
  • +
+
## Iteration -This section describes the different ways queries can be iterated. The code examples use filters, but also apply to cached queries and rules. +This section describes the different ways queries can be iterated. -### Iterators (C) -In the C API an iterator object of type `ecs_iter_t` can be created for each of the query kinds, using the `ecs_filter_iter`, `ecs_query_iter` and `ecs_rule_iter` functions. This iterator can then be iterated with the respective `next` functions: `ecs_filter_next`, `ecs_query_next` and `ecs_rule_next`. +
+
    +
  • C + +In the C API an iterator object of type `ecs_iter_t` can be created using the `ecs_query_iter` function. This iterator can then be iterated with `ecs_query_next`. An iterator can also be iterated with the `ecs_iter_next` function which is slightly slower, but does not require knowledge about the source the iterator was created for. An example: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) }, } }); -ecs_iter_t it = ecs_filter_iter(world, f); +ecs_iter_t it = ecs_query_iter(world, q); // Outer loop: matching tables -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); // 1st term - Velocity *v = ecs_field(&it, Velocity, 2); // 2nd term +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); // 1st term + Velocity *v = ecs_field(&it, Velocity, 1); // 2nd term // Inner loop: entities in table for (int i = 0; i < it.count; i ++) { @@ -302,15 +392,17 @@ while (ecs_filter_next(&it)) { Iteration is split up into two loops: the outer loop which iterates tables, and the inner loop which iterates the entities in that table. This approach provides direct access to component arrays, which allows compilers to do performance optimizations like auto-vectorization. -The indices provided to the `ecs_field` function must correspond with the order in which terms have been specified in the query. This index starts counting from `1`, with index `0` reserved for the array containing entity ids. +The indices provided to the `ecs_field()` function must correspond with the order in which terms have been specified in the query. This index starts counting from `0`. -### Each (C++) -The `each` function is the default and often fastest approach for iterating a query in C++. `each` can be called directly on a `flecs::filter`, `flecs::query` and `flecs::rule`. An example: +
  • +
  • C++ + +C++ has two iteration functions, `each` and `run`. The `each` function is the default and often fastest approach for iterating a query in C++. An example: ```cpp -auto f = world.filter(); +auto q = world.query(); -f.each([](Position& p, const Velocity& v) { +q.each([](Position& p, const Velocity& v) { p.x += v.x; p.y += v.y; }); @@ -319,9 +411,9 @@ f.each([](Position& p, const Velocity& v) { A `flecs::entity` can be added as first argument: ```cpp -auto f = world.filter(); +auto q = world.query(); -f.each([](flecs::entity e, Position& p) { +q.each([](flecs::entity e, Position& p) { std::cout << e.name() << ": " << p.x << ", " << p.y << std::endl; @@ -331,14 +423,14 @@ f.each([](flecs::entity e, Position& p) { A `flecs::iter` and `size_t` argument can be added as first arguments. This variant of `each` provides access to the `flecs::iter` object, which contains more information about the object being iterated. The `size_t` argument contains the index of the entity being iterated, which can be used to obtain entity-specific data from the `flecs::iter` object. An example: ```cpp -auto f = world.filter_builder() - .term(Likes, flecs::Wildcard) +auto q = world.query_builder() + .with(Likes, flecs::Wildcard) .build(); -f.each([](flecs::iter& it, size_t index, Position& p) { +q.each([](flecs::iter& it, size_t index, Position& p) { flecs::entity e = it.entity(index); std::cout << e.name() << ": " - << it.id(2).str() // prints pair + << it.id(1).str() // prints pair << std::endl; }); ``` @@ -348,9 +440,9 @@ When a query contains a template argument that is an empty type (a struct withou ```cpp struct Tag { }; -auto f = world.filter(); +auto q = world.query(); -f.each([](flecs::entity e, Tag) { +q.each([](flecs::entity e, Tag) { std::cout << e.name() << std::endl; }); ``` @@ -360,109 +452,197 @@ Alternatively an empty type can be specified outside of the query type, which re ```cpp struct Tag { }; -auto f = world.filter_builder() - .term() +auto q = world.query_builder() + .with() .build(); -f.each([](flecs::entity e) { +q.each([](flecs::entity e) { std::cout << e.name() << std::endl; }); ``` -### Iter (C++) -The `iter` function has an outer and inner loop (similar to C iterators) which provides more control over how to iterate entities in a table than `each`. The `iter` function makes it possible, for example, to run code only once per table, or iterate entities multiple times. +The `run` function provides an initialized iterator to a callback, and leaves iteration up to the callback implementation. Similar to C query iteration, the run callback has an outer and an inner loop. An example: ```cpp -auto f = world.filter(); - -f.iter([](flecs::iter& it, Position *p, Velocity *v) { - // Inner loop - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - std::cout << it.entity(i).name() << ": " - << p.x << ", " << p.y - << std::endl; +auto q = world.query(); + +q.run([](flecs::iter& it) { + // Outer loop + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + // Inner loop + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + std::cout << it.entity(i).name() << ": " + << p.x << ", " << p.y + << std::endl; + } } }); ``` -Instead of using `flecs::iter` as iterator directly, an application can also use the `flecs::iter::count` method which provides more flexibility: +Entities can be moved between tables when components are added or removed. This can cause unwanted side effects while iterating a table, like iterating an entity twice, or missing an entity. To prevent this from happening, a table is locked by the C++ `each` and `run` functions, meaning no entities can be moved from or to it. + +When an application attempts to add or remove components to an entity in a table being iterated over, this can throw a runtime assert. An example: ```cpp -f.iter([](flecs::iter& it, Position *p, Velocity *v) { - // Inner loop with manual iteration - for (size_t i = 0; i < it.count(); i ++) { - // ... - } +auto q = world.query(); + +q.each([](flecs::entity e, Position&) { + e.add(); // throws locked table assert }); ``` -The component arguments may be omitted, and can be obtained from the iterator object: +This can be addressed by deferring operations while the query is being iterated: + +```cpp +auto q = world.query(); + +world.defer([&]{ + q.each([](flecs::entity e, Position&) { + e.add(); // OK + }); +}); // operations are executed here +``` + +An application can also use the `defer_begin` and `defer_end` functions which achieve the same goal: ```cpp -auto f = world.filter(); +auto q = world.query(); -f.iter([](flecs::iter& it) { - auto p = it.field(1); - auto v = it.field(2); +world.defer_begin(); - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - } +q.each([](flecs::entity e, Position&) { + e.add(); // OK }); + +world.defer_end(); // operations are executed here ``` -This can be combined with an untyped variant of the `field` method to access component data without having to know component types at compile time. This can be useful for generic code, like serializers: +Code ran by a system is deferred by default. + +
  • +
  • Rust -```cpp -auto f = world.filter(); +Rust has two main iteration functions, `each` and `run`. The `each` function is the default and often fastest approach for iterating a query in Rust. Both `each` and `run` have some variances that offer slightly different functionality. +These variances are `each_entity`, `each_iter` and `run_each` and `run_iter`. -f.iter([](flecs::iter& it) { - void *ptr = it.field(1); - flecs::id type_id = it.id(1); +An example: - // ... +```rust +let q = world.new_query::<(&mut Position, &Velocity)>(); +q.each(|(p, v)| { + p.x += v.x; + p.y += v.y; }); ``` -### Iteration safety -Entities can be moved between tables when components are added or removed. This can cause unwanted side effects while iterating a table, like iterating an entity twice, or missing an entity. To prevent this from happening, a table is locked by the C++ `each` and `iter` functions, meaning no entities can be moved from or to it. +A `EntityView` can be added as by using `each_entity`: -When an application attempts to add or remove components to an entity in a table being iterated over, this can throw a runtime assert. An example: +```rust +let q = world.new_query::<(&mut Position, &Velocity)>(); +q.each_entity(|e, (p, v)| { + println!("Entity: {}", e.name()); + p.x += v.x; + p.y += v.y; +}); +``` -```cpp -auto f = world.filter(); +Using `each_iter` a `TableIter` and `usize` argument are added as first arguments. This variant of `each` provides access to the `TableIter` object, which contains more information about the object being iterated. The `usize` argument contains the index of the entity being iterated, which can be used to obtain entity-specific data from the `TableIter` object. An example: -f.each([](flecs::entity e, Position&) { - e.add(); // throws locked table assert +```rust +let q = world + .query::<&Position>() + .with::<(&Likes, &flecs::Wildcard)>() + .build(); + +q.each_iter(|it, index, p| { + println!("Entity: {}: {}", it.entity(index).name(), it.id(1).to_str()); }); ``` -This can be addressed by deferring operations while the query is being iterated: +A query can also contain generic arguments that is an empty type (a struct without any members). -```cpp -auto f = world.filter(); +```rust +#[derive(Component)] +struct Tag; -world.defer([&]{ - f.each([](flecs::entity e, Position&) { - e.add(); // OK +world.new_query::<&Tag>().each_entity(|e, tag| { + /* */ +}); +``` + +Alternatively an empty type can be specified outside of the query type, which removes it from the signature of `each`: + +```rust +#[derive(Component)] +struct Tag; + +world + .query::<()>() + .with::<&Tag>() + .build() + .each_entity(|e, _| { /* */ }); +``` + +The `run` function provides an initialized iterator to a callback, and leaves iteration up to the callback implementation. The run callback has an outer and an inner loop. + +An example: + +```rust +let q = world.new_query::<(&Position, &Velocity)>(); + +q.run(|mut it| { + while it.next() { + let mut p = it.field::(0).unwrap(); + let v = it.field::(1).unwrap(); + for i in it.iter() { + p[i].x += v[i].x; + p[i].y += v[i].y; + println!("Entity: {}", it.entity(i).name()); + } + } +}); +``` + +Entities can be moved between tables when components are added or removed. This can cause unwanted side effects while iterating a table, like iterating an entity twice, or missing an entity. To prevent this from happening, a table is locked in the Rust `each` and `run` functions, meaning no entities can be moved from or to it. + +When an application attempts to add or remove components to an entity in a table being iterated over in not deferred context, this can throw a runtime assert. An example: + +```rust +let q = world.new_query::<&Position>(); + +q.each_entity(|e, p| { + e.add::(); // throws locked table assert +}); +``` + +This can be addressed by deferring operations while the query is being iterated: + +```rust +let q = world.new_query::<&Position>(); + +world.defer(|| { + q.each_entity(|e, p| { + e.add::(); // OK }); }); // operations are executed here ``` An application can also use the `defer_begin` and `defer_end` functions which achieve the same goal: -```cpp -auto f = world.filter(); +```rust +let q = world.new_query::<&Position>(); world.defer_begin(); -f.each([](flecs::entity e, Position&) { - e.add(); // OK +q.each_entity(|e, p| { + e.add::(); // OK }); world.defer_end(); // operations are executed here @@ -470,12 +650,14 @@ world.defer_end(); // operations are executed here Code ran by a system is deferred by default. +
  • +
+
+ ## Reference -This section goes over the different features of queries and how they can be expressed by the query descriptor API, query builder API and in the query DSL. +This section goes over the different features of queries and how they can be expressed by the query descriptor API, query builder API and in the Flecs Query Language. ### Components -> *Supported by: filters, cached queries, rules* - A component is any single id that can be added to an entity. This includes tags and regular entities, which are ids that are not associated with a datatype. To match a query, an entity must have all the requested components. An example: @@ -496,16 +678,19 @@ flecs::entity e3 = world.entity() Only entities `e2` and `e3` match the query `Position, Velocity`. -The following sections describe how to create queries for components in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following sections describe how to create queries for components in the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) To query for a component in C, the `id` field of a term can be set: ```c ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { .id = ecs_id(Position) }, { .id = ecs_id(Velocity) } @@ -516,7 +701,7 @@ ecs_filter_t *f = ecs_filter(world, { The `id` field is guaranteed to be the first member of a term, which allows the previous code to be rewritten in this shorter form: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } @@ -528,9 +713,9 @@ The `ecs_id` macro converts the component typename into the variable name that h ```c ECS_TAG(world, Npc); -ecs_entity_t Platoon_01 = ecs_new_id(world); +ecs_entity_t Platoon_01 = ecs_new(world); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { Npc }, { Platoon_01 } @@ -544,7 +729,7 @@ Components can also be queried for by name by setting the `.first.name` member i ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { .first.name = "Position" }, { .first.name = "Velocity" } @@ -552,26 +737,28 @@ ecs_filter_t *f = ecs_filter(world, { }); ``` -#### Query Builder (C++) +
  • +
  • C++ + An easy way to query for components in C++ is to pass them as template arguments to the query factory function: ```cpp -flecs::filter f = - world.filter(); +flecs::query q = + world.query(); ``` This changes the returned query type, which determines the type of the function used to iterate the query: ```cpp -f.each([](Position& p, const Velocity& v) { }); +q.each([](Position& p, const Velocity& v) { }); ``` The builder API makes it possible to add components to a query without modifying the query type: ```cpp -flecs::filter f = - world.filter_builder() - .term() +flecs::query q = + world.query_builder() + .with() .build(); ``` @@ -583,9 +770,9 @@ The builder API makes it possible to query for regular entity ids created at run flecs::entity Npc = world.entity(); flecs::entity Platoon_01 = world.entity(); -flecs::filter<> f = world.filter_builder() - .term(Npc) - .term(Platoon_01) +flecs::query<> q = world.query_builder() + .with(Npc) + .with(Platoon_01) .build(); ``` @@ -598,14 +785,65 @@ world.component(); // Create entity with name so we can look it up flecs::entity Npc = world.entity("Npc"); -flecs::filter<> f = world.filter_builder() - .term("Position") - .term("Npc") +flecs::query<> q = world.query_builder() + .with("Position") + .with("Npc") .build(); ``` -#### Query DSL -To query for a components in the query DSL they can be specified in a comma separated list of identifiers. The rules for resolving identifiers are the same as the `ecs_lookup_fullpath` / `world.lookup` functions. An example: +
  • +
  • Rust + +An easy way to query for components in Rust is to pass them as generic arguments to the query `new` function: + +```rust +let q = world.new_query::<(&mut Position, &Velocity)>(); +``` + +This changes the returned query type, which determines the type of the function used to iterate the query: + +```rust +q.each(|(p, v)| { /* */ }); +``` + +The builder API makes it possible to add components to a query without modifying the query type: + +```rust +let q = world.query::<&mut Position>().with::<&Velocity>().build(); +``` + +When generic arguments are mixed with the builder API, the components added by the `term` function will be placed after the components provided as generic arguments. + +The builder API makes it possible to query for regular entity ids created at runtime: + +```rust +let npc = world.entity(); +let platoon_01 = world.entity(); + +let q = world + .query::<(&mut Position, &Velocity)>() + .with_id(npc) + .with_id(platoon_01) + .build(); +``` + +Components can also be queried for by name. To query for component types by name, they have to be used or registered first. + +```rust +// Create entity with name so we can look it up +let npc = world.entity_named("npc"); + +let q = world + .query::<(&Position, &Npc)>() + .with_name("npc") + .with_name("Position") + .build(); +``` + +
  • +
  • Flecs Query Language + +To query for a components in the Flecs Query Language they can be specified in a comma separated list of identifiers. The rules for resolving identifiers are the same as the `ecs_lookup` / `world.lookup` functions. An example: ``` Position, Velocity @@ -632,17 +870,19 @@ The entity `e` from this example will be matched by this query: Npc, Platoon_01 ``` -When an identifier in the query DSL consists purely out of numeric characters it is converted to an entity id. If in the previous example `Npc` has id `100` and `Platoon_01` has id `101`, the following query string would be equivalent: +When an identifier in the Flecs Query Language consists purely out of numeric characters it is converted to an entity id. If in the previous example `Npc` has id `100` and `Platoon_01` has id `101`, the following query string would be equivalent: ``` 100, 101 ``` -The `,` symbol in the query DSL is referred to as the `and` operator, as an entity must have all comma-separated components in order to match the query. +The `,` symbol in the Flecs Query Language is referred to as the `and` operator, as an entity must have all comma-separated components in order to match the query. -### Wildcards -> *Supported by: filters, cached queries, rules* +
  • +
+
+### Wildcards Wildcards allow a single query term to match with more than one (component) ids. Flecs supports two kinds of wildcards: | Name | DSL Symbol | C identifier | C++ identifier | Description | @@ -652,77 +892,129 @@ Wildcards allow a single query term to match with more than one (component) ids. The `Wildcard` wildcard returns an individual result for anything that it matches. The query in the following example will return twice for entity `e`, once for component `Position` and once for component `Velocity`: +
+
    +
  • C + ```c ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { EcsWildcard } } }); ``` + +
  • +
  • C++ + ```cpp flecs::entity e = world.entity() .add() .add(); -flecs::filter<> f = world.filter_builder() - .term(flecs::Wildcard) +flecs::query<> q = world.query_builder() + .with(flecs::Wildcard) + .build(); +``` +
  • +
  • Rust + +```rust +let e = world + .entity() + .add::() + .add::(); + +let q = world + .query::<()>() + .with::() .build(); ``` +
  • +
+
The `Any` wildcard returns a single result for the first component that it matches. The query in the following example will return once for entity `e`: +
+
    +
  • C + ```c ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { EcsAny } } }); ``` + +
  • +
  • C++ + ```cpp flecs::entity e = world.entity() .add() .add(); -flecs::filter<> f = world.filter_builder() - .term(flecs::Any) +flecs::query<> q = world.query_builder() + .with(flecs::Any) + .build(); +``` + +
  • +
  • Rust + +```rust +let e = world + .entity() + .add::().add::(); + +let q = world + .query::<()>() + .with::() .build(); ``` +
  • +
+
+ When using the `Any` wildcard it is undefined which component will be matched, as this can be influenced by other parts of the query. It is guaranteed that iterating the same query twice on the same dataset will produce the same result. Wildcards are particularly useful when used in combination with pairs (next section). ### Pairs -> *Supported by: filters, cached queries, rules* - A pair is an id that encodes two elements. Pairs, like components, can be added to entities and are the foundation for [Relationships](Relationships.md). The elements of a pair are allowed to be wildcards. When a query pair contains the `Wildcard` wildcard, a query returns a result for each matching pair on an entity. When a query pair returns an `Any` wildcard, the query returns at most a single matching pair on an entity. -The following sections describe how to create queries for pairs in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following sections describe how to create queries for pairs in the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) To query for a pair in C, the `id` field of a term can be set to a pair using the `ecs_pair` macro: ```c -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Bob = ecs_new(world); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { .id = ecs_pair(Likes, Bob) } } @@ -732,10 +1024,10 @@ ecs_filter_t *f = ecs_filter(world, { The `id` field is guaranteed to be the first member of a term, which allows the previous code to be rewritten in this shorter form: ```c -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Bob = ecs_new(world); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_pair(Likes, Bob) } } @@ -746,9 +1038,9 @@ When an element of the pair is a component type, use the `ecs_id` macro to obtai ```c ECS_COMPONENT(world, Eats); -ecs_entity_t Apples = ecs_new_id(world); +ecs_entity_t Apples = ecs_new(world); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_pair(ecs_id(Eats), Apples) } } @@ -758,13 +1050,13 @@ ecs_filter_t *f = ecs_filter(world, { The `ecs_isa`, `ecs_childof` and `ecs_dependson` convenience macros can be used to create pairs for builtin relationships. The two queries in the next example are equivalent: ```c -ecs_filter_t *f_1 = ecs_filter(world, { +ecs_query_t *q1 = ecs_query(world, { .terms = { { ecs_pair(EcsChildOf, parent) } } }); -ecs_filter_t *f_2 = ecs_filter(world, { +ecs_query_t *q2 = ecs_query(world, { .terms = { { ecs_childof(parent) } } @@ -774,7 +1066,7 @@ ecs_filter_t *f_2 = ecs_filter(world, { Pair queries can be created by setting their individual elements in the `first.id` and `second.id` members of a term: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { .first.id = Eats, .second.id = Apples } } @@ -788,13 +1080,13 @@ Alternatively, one or both elements of a pair can be resolved by name. The two q ECS_TAG(world, Eats); ECS_TAG(world, Apples); -ecs_filter_t *f_1 = ecs_filter(world, { +ecs_query_t *q1 = ecs_query(world, { .terms = { { .first.name = "Eats", .second.id = Apples } } }); -ecs_filter_t *f_2 = ecs_filter(world, { +ecs_query_t *q2 = ecs_query(world, { .terms = { { .first.name = "Eats", .second.name = "Apples" } } @@ -804,15 +1096,15 @@ ecs_filter_t *f_2 = ecs_filter(world, { When a query pair contains a wildcard, the `ecs_field_id` function can be used to determine the id of the pair element that matched the query: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_pair(Likes, EcsWildcard) } } }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - ecs_id_t id = ecs_field_id(&it, 1); +ecs_iter_t it = ecs_query_iter(world, q); +while (ecs_query_next(&it)) { + ecs_id_t id = ecs_field_id(&it, 0); ecs_entity_t second = ecs_pair_second(world, id); for (int i = 0; i < it.count; i ++) { @@ -823,7 +1115,9 @@ while (ecs_filter_next(&it)) { } ``` -#### Query Builder (C++) +
  • +
  • C++ + When both parts of a pair are types, a `flecs::pair` template can be used. Pair templates can be made part of the query type, which makes them part of the argument list of the iterator functions. An example: ```cpp @@ -832,29 +1126,16 @@ struct Apples { }; // Alias to save typing using EatsApples = flecs::pair; -flecs::filter f = - world.filter(); +flecs::query q = + world.query(); // Do not use reference argument for pair -f.each([](EatsApples v) { +q.each([](EatsApples v) { // Use -> operator to access value of pair v->value ++; }); ``` -When using the `iter` function to iterate a query with a pair template, the argument type assumes the type of the pair. This is required as the component array being passed directly to the `iter` function. An example: - -```cpp -flecs::filter f = - world.filter(); - -f.iter([](flecs::iter& it, Eats *v) { - for (auto i : it) { - v[i].value ++; - } -}) -``` - Pairs can also be added to queries using the builder API. This allows for the pair to be composed out of both types and regular entities. The three queries in the following example are equivalent: ```cpp @@ -864,44 +1145,44 @@ struct Apples { }; flecs::entity eats = world.component(); flecs::entity apples = world.component(); -flecs::filter<> f_1 = world.filter_builder() - .term() +flecs::query<> q1 = world.query_builder() + .with() .build(); -flecs::filter<> f_2 = world.filter_builder() - .term(apples) +flecs::query<> q2 = world.query_builder() + .with(apples) .build(); -flecs::filter<> f_3 = world.filter_builder() - .term(eats, apples) +flecs::query<> q_3 = world.query_builder() + .with(eats, apples) .build(); ``` Individual elements of a pair can be specified with the `first` and `second` methods. The methods apply to the last added term. An example: ```cpp -flecs::filter<> f = world.filter_builder() - .term().first().second(apples) +flecs::query<> q = world.query_builder() + .with().first().second(apples) .build(); ``` Individual elements of a pair can be resolved by name by using the `first` and `second` methods: ```cpp -flecs::filter<> f = world.filter_builder() - .term().first("Eats").second("Apples") +flecs::query<> q = world.query_builder() + .with().first("Eats").second("Apples") .build(); ``` When a query pair contains a wildcard, the `flecs::iter::pair` method can be used to determine the id of the pair element that matched the query: ```cpp -flecs::filter<> f = world.filter_builder() - .term(flecs::Wildcard) +flecs::query<> q = world.query_builder() + .with(flecs::Wildcard) .build(); -f.each([](flecs::iter& it, size_t index) { - flecs::entity second = it.pair(1).second(); +q.each([](flecs::iter& it, size_t index) { + flecs::entity second = it.pair(0).second(); flecs::entity e = it.entity(index); std::cout << "entity " << e.name() @@ -910,8 +1191,87 @@ f.each([](flecs::iter& it, size_t index) { }); ``` -#### Query DSL -To query for a pair in the query DSL, the elements of a pair are a comma separated list surrounded by parentheses. An example: +
  • +
  • Rust + +When both parts of a pair are types, a tuple can be used. tuple pairs can be made part of the query type, which makes them part of the argument list of the iterator functions. An example: + +```rust + #[derive(Component)] + struct Eats { + value: f32, + } + + #[derive(Component)] + struct Apples; + + let q = world.new_query::<&mut (Eats, Apples)>(); + + q.each(|eats| { + eats.value += 1.0; + }); +``` + +Tuple pairs can also be added to queries using the builder API. This allows for the pair to be composed out of both types and regular entities. The three queries in the following example are equivalent: + +```rust +#[derive(Component)] +struct Eats { + value: f32, +} + +#[derive(Component)] +struct Apples; + +let eats = world.component::(); +let apples = world.component::(); +let q1 = world.query::<()>().with::<(Eats, Apples)>().build(); // tuple types +let q2 = world.query::<()>().with_first::(apples).build(); +let q3 = world.query::<()>().with_id((eats, apples)).build(); // tuple ids +``` + +Individual elements of a tuple pair can be specified with the `first` and `second` methods. The methods apply to the last added term. An example: + +```rust +let q = world + .query::<()>() + .term() + .set_first::() + .set_second_id(apples) + .build(); +``` + +Individual elements of a pair can be resolved by name by using the `first` and `second` methods: + +```rust +let q = world + .query::<()>() + .term() + .set_first_name("Eats") + .set_second_name("Apples") + .build(); +``` + +When a query pair contains a wildcard, the `TableIter::pair` method can be used to determine the id of the pair element that matched the query: + +```rust +let q = world + .query::<()>() + .with::<(Eats, flecs::Wildcard)>() + .build(); + +q.each_iter(|it, index, _| { + let pair = it.pair(0).unwrap(); + let second = pair.second_id(); + let e = it.entity(index); + println!("Entity {} likes {}", e.name(), second.name()); +}); +``` + +
  • +
  • Flecs Query Language + +To query for a pair in the Flecs Query Language, the elements of a pair are a comma separated list surrounded by parentheses. An example: ``` (Likes, Apples) @@ -922,7 +1282,7 @@ A query may contain multiple pairs: ``` (Likes, Apples), (Likes, Pairs) ``` - + Queries for pairs that contain wildcards should use the symbols for either the `Wildcard` or `Any` wildcards: ``` @@ -938,9 +1298,11 @@ A pair may contain two wildcards: (*, *) ``` -### Access modifiers -> *Supported by: filters, cached queries, rules* +
  • +
+
+### Access modifiers Access modifiers specify which components of a query can be read and/or written. The different access modifiers are: | Name | DSL identifier | C identifier | C++ identifier | Description | @@ -963,13 +1325,16 @@ When a query term can either match a component from the matched entity or anothe When a query term matches a tag (a component not associated with data) with a `Default` modifier, the `None` modifier is selected. -The following sections show how to use access modifiers in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following sections show how to use access modifiers in the different language bindings. + +
+
    +
  • C -#### Query Descriptors (C) Access modifiers can be set using the `inout` member: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity), .inout = EcsIn } @@ -977,13 +1342,21 @@ ecs_filter_t *f = ecs_filter(world, { }); ``` -#### Query Builder (C++) +
  • +
  • C++ + Access modifiers can be set using the `inout` method: ```cpp -flecs::filter<> f = world.filter_builder() - .term() - .term().inout(flecs::In) +// The following two queries are the same: +flecs::query<> q = world.query_builder() + .with() + .with().inout(flecs::In) + .build(); + +flecs::query<> q = world.query_builder() + .with() + .with().in() // shorthand for .inout(flecs::In) .build(); ``` @@ -991,54 +1364,126 @@ When the `const` modifier is added to a type, the `flecs::In` modifier is automa ```c // Velocity term will be added with flecs::In modifier -flecs::filter f = - world.filter(); +flecs::query q = + world.query(); ``` This also applies to types added with `term`: ```c -flecs::filter<> f = world.filter_builder() - .term() - .term() // uses flecs::In modifier +flecs::query<> q = world.query_builder() + .with() + .with() // uses flecs::In modifier .build(); ``` When a component is added by the `term` method and retrieved from a `flecs::iter` object during iteration, it must meet the constraints of the access modifiers. If the constraints are not met, a runtime assert may be thrown: ```cpp -flecs::filter<> f = world.filter_builder() - .term() - .term().inout(flecs::In) +flecs::query<> q = world.query_builder() + .with() + .with().inout(flecs::In) .build(); -f.iter([](flecs::iter& it) { - auto p = it.field(1); // OK - auto p = it.field(1); // OK - auto v = it.field(2); // OK - auto v = it.field(2); // Throws assert +q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); // OK + auto p = it.field(0); // OK + auto v = it.field(1); // OK + auto v = it.field(1); // Throws assert + } }); ``` The builder API has `in()`, `inout()`, `out()` and `inout_none()` convenience methods: ```cpp -flecs::filter<> f = world.filter_builder() - .term().inout() - .term().in() +flecs::query<> q = world.query_builder() + .with().inout() + .with().in() + .build(); +``` + +
  • +
  • Rust + +Access modifiers can be set using the `set_inout_kind` method: + +```rust +// The following two queries are the same: +let q = world + .query::<()>() + .with::() + .with::() + .set_inout_kind(InOutKind::In) + .build(); + +let q = world + .query::<()>() + .with::() + .with::() + .set_in() // shorthand for .set_inout_kind(InOutKind::In) + .build(); +``` + +When the `const` / `immutable reference` modifier is added to a type, the `InOutKind::In` modifier is automatically set: + +```rust +// Velocity term will be added with InOutKind::In modifier due to `&` +let q = world.new_query::<(&mut Position, &Velocity)>(); +``` + +This also applies to types added with `term` / `with`: + +```rust +let q = world + .query::<()>() + .with::<&mut Position>() + .with::<&Velocity>() // uses InOutKind::In modifier + .build(); +``` + +When a component is added by the `term` method or generic terms with the `run` method, you can retrieve it from a `TableIter` object during iteration with the `field` method. + +```rust +let q = world + .query::<()>() + .with::<&mut Position>() + .with::<&Velocity>() + .build(); + +q.run(|mut it| { + while it.next() { + let p = it.field::(0).unwrap(); + let v = it.field::(1).unwrap(); + } +}); +``` + +The builder API has `set_in()`, `set_inout()`, `set_out()` and `set_inout_none()` convenience methods: + +```rust +let q = world + .query::<()>() + .with::().set_inout() + .with::().set_in() .build(); ``` -#### Query DSL -Access modifiers in the query DSL can be specified inside of angular brackets before the component identifier: +
  • +
  • Flecs Query Language + +Access modifiers in the Flecs Query Language can be specified inside of angular brackets before the component identifier: ``` Position, [in] Velocity ``` -### Operator Overview -> *Supported by: filters, cached queries, rules* +
  • +
+
+### Operator Overview The following operators are supported by queries: | Name | DSL operator | C identifier | C++ identifier | Description | @@ -1048,29 +1493,29 @@ The following operators are supported by queries: | Not | `!` | `EcsNot` | `flecs::Not` | Must not match with term | | Optional | `?` | `EcsOptional` | `flecs::Optional` | May match with term | | Equal | `==` | `EcsPredEq` | `flecs::PredEq` | Equals entity/entity name | -| Not equal | `!=` | `EcsPredNeq` | `flecs::PredNeq` | Not equals entity/entity name | | Match | `~=` | `EcsPredMatch`| `flecs::PredMatch`| Match entity name with substring | -| AndFrom | `AND \|` | `EcsAndFrom` | `flecs::AndFrom` | Match all components from id at least once | -| OrFrom | `OR \|` | `EcsOrFrom` | `flecs::OrFrom` | Match at least one component from id at least once | -| NotFrom | `NOT \|` | `EcsNotFrom` | `flecs::NotFrom` | Don't match any components from id | +| AndFrom | `and \|` | `EcsAndFrom` | `flecs::AndFrom` | Match all components from id at least once | +| OrFrom | `or \|` | `EcsOrFrom` | `flecs::OrFrom` | Match at least one component from id at least once | +| NotFrom | `not \|` | `EcsNotFrom` | `flecs::NotFrom` | Don't match any components from id | ### And Operator -> *Supported by: filters, cached queries, rules* +The `And` operator is used when no other operators are specified. The following sections show how to use the `And` operator in the different language bindings. -The `And` operator is used when no other operators are specified. The following sections show how to use the `And` operator in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +
+
    +
  • C -#### Query Descriptor (C) When no operator is specified, `And` is assumed. The following two queries are equivalent: ```c -ecs_filter_t *f_1 = ecs_filter(world, { +ecs_query_t *q1 = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } } }); -ecs_filter_t *f_2 = ecs_filter(world, { +ecs_query_t *q2 = ecs_query(world, { .terms = { { ecs_id(Position), .oper = EcsAnd }, { ecs_id(Velocity), .oper = EcsAnd } @@ -1078,42 +1523,83 @@ ecs_filter_t *f_2 = ecs_filter(world, { }); ``` -#### Query Builder (C++) +
  • +
  • C++ + When no operator is specified, `And` is assumed. The following two queries are equivalent: ```cpp -flecs::filter f_1 = world.filter(); +flecs::query q1 = world.query(); -flecs::filter<> f_2 = world.filter_builder() - .term() - .term() +flecs::query<> q2 = world.query_builder() + .with() + .with() .build(); -flecs::filter<> f_2 = world.filter_builder() - .term().oper(flecs::And) - .term().oper(flecs::And) +flecs::query<> q2 = world.query_builder() + .with().oper(flecs::And) + .with().oper(flecs::And) .build(); ``` The builder API has a `and_` convenience method: ```cpp -flecs::filter<> f = world.filter_builder() - .term().and_(); // note escaping, 'and' is a C++ keyword - .term().and_(); +flecs::query<> q = world.query_builder() + .with().and_(); // note escaping, 'and' is a C++ keyword + .with().and_(); + .build(); +``` + +
  • +
  • Rust + +When no operator is specified, `And` is assumed. The following two queries are equivalent: + +```rust +let q = world.new_query::<(&mut Position, &Velocity)>(); + +let q2 = world + .query::<()>() + .with::() + .with::() + .build(); + +let q3 = world + .query::<()>() + .with::() + .set_oper(OperKind::And) + .with::() + .set_oper(OperKind::And) + .build(); +``` + +The builder API has a `and` convenience method: + +```rust +let q = world + .query::<()>() + .with::() + .and() + .with::() + .and() .build(); ``` -#### Query DSL +
  • +
  • Flecs Query Language + Query expressions with comma separated lists use the `And` operator: ``` Position, Velocity ``` -### Or Operator -> *Supported by: filters, cached queries, rules* +
  • +
+
+### Or Operator The `Or` operator allows for matching a single component from a list. Using the `Or` operator means that a single term can return results of multiple types. When the value of a component is used while iterating the results of an `Or` operator, an application has to make sure that it is working with the expected type. When using the `Or` operator, the terms participating in the `Or` expression are made available as a single field. Field indices obtained from an iterator need to account for this. Consider the following query: @@ -1124,14 +1610,17 @@ Position, Velocity || Speed, Mass This query has 4 terms, while an iterator for the query returns results with 3 fields. This is important to consider when retrieving the field for a term, as its index has to be adjusted. In this example, `Position` has index 1, `Velocity || Speed` has index 2, and `Mass` has index 3. -The following sections show how to use the `Or` operator in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following sections show how to use the `Or` operator in the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) To create a query with `Or` terms, set `oper` to `EcsOr`: ```c // Position, Velocity || Speed, Mass -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity), .oper = EcsOr }, @@ -1140,12 +1629,12 @@ ecs_filter_t *f = ecs_filter(world, { } }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Mass *m = ecs_field(&it, Mass, 3); // not 4, because of the Or expression +ecs_iter_t it = ecs_query_iter(world, q); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Mass *m = ecs_field(&it, Mass, 2); // not 3, because of the Or expression - ecs_id_t vs_id = ecs_field_id(&it, 2); + ecs_id_t vs_id = ecs_field_id(&it, 1); if (vs_id == ecs_id(Velocity)) { // We can only use ecs_field if the field type is the same for all results, // but we can get the table column directly. @@ -1158,31 +1647,35 @@ while (ecs_filter_next(&it)) { } ``` -#### Query Builder (C++) +
  • +
  • C++ + To create a query with `Or` terms, use the `oper` method with `flecs::Or`: ```cpp // Position, Velocity || Speed, Mass -flecs::filter<> f = world.filter_builder() - .term() - .term().oper(flecs::Or) - .term() - .term() +flecs::query<> q = world.query_builder() + .with() + .with().oper(flecs::Or) + .with() + .with() .build(); -f.iter([&](flecs::iter& it) { - auto p = it.field(1); - auto v = it.field(3); // not 4, because of the Or expression - - flecs::id vs_id = it.id(2); - if (vs_id == world.id()) { - // We can only use ecs_field if the field type is the same for all results, - // but we can use range() to get the table column directly. - auto v = it.range().get(); - // iterate as usual - } else if (vs_id == world.id()) { - auto s = it.range().get(); - // iterate as usual +q.run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(2); // not 4, because of the Or expression + + flecs::id vs_id = it.id(1); + if (vs_id == world.id()) { + // We can only use ecs_field if the field type is the same for all results, + // but we can use range() to get the table column directly. + auto v = it.range().get(); + // iterate as usual + } else if (vs_id == world.id()) { + auto s = it.range().get(); + // iterate as usual + } } }); ``` @@ -1190,35 +1683,91 @@ f.iter([&](flecs::iter& it) { The builder API has a `or_` convenience method: ```cpp -flecs::filter<> f = world.filter_builder() - .term() - .term().or_(); // note escaping, 'or' is a C++ keyword - .term() - .term() +flecs::query<> q = world.query_builder() + .with() + .with().or_(); // note escaping, 'or' is a C++ keyword + .with() + .with() + .build(); +``` + + +
  • +
  • Rust + +To create a query with `Or` terms, use the `oper` method with enum `OperKind::Or`: + +```rust +// Position, Velocity || Speed, Mass +let q = world + .query::<()>() + .with::() + .with::() + .set_oper(OperKind::Or) + .with::() + .with::() + .build(); + +q.run(|mut it| { + while it.next() { + let p = it.field::(0).unwrap(); + let v = it.field::(2).unwrap(); // not 4, because of the Or expression + let vs_id = it.id(1); + if vs_id == world.component_id::() { + // We can only use ecs_field if the field type is the same for all results, + // but we can use range() to get the table column directly. + let v = it.range().unwrap().get_mut::(); + // iterate as usual + } else if vs_id == world.component_id::() { + let s = it.range().unwrap().get_mut::(); + // iterate as usual + } + } +}); +``` + +The builder API has a `or` convenience method: + +```rust +let q = world + .query::<()>() + .with::() + .with::() + .or() + .with::() + .with::() .build(); ``` -#### Query DSL + +
  • +
  • Flecs Query Language + To create a query with `Or` terms, use the `||` symbol: ``` Position, Velocity || Speed, Mass ``` -### Not Operator -> *Supported by: filters, cached queries, rules* +
  • +
+
+### Not Operator The `Not` operator makes it possible to exclude entities with a specified component. Fields for terms that uses the `Not` operator will never provide data. A note on performance: `Not` terms are efficient to evaluate when combined with other terms, but queries that only have `Not` terms (or [`Optional`](#optional-operator)) can be expensive. This is because the storage only maintains indices for tables that _have_ a component, not for tables that do _not have_ a component. -The following sections show how to use the `Not` operator in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following sections show how to use the `Not` operator in the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) To create a query with `Not` terms, set `oper` to `EcsNot`: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity), .oper = EcsNot } @@ -1226,40 +1775,91 @@ ecs_filter_t *f = ecs_filter(world, { }); ``` -#### Query Builder (C++) +
  • +
  • C++ + To create a query with `Not` terms, use the `oper` method with `flecs::Not`: ```cpp -flecs::filter<> f = world.filter_builder() - .term() - .term().oper(flecs::Not) +flecs::query<> q = world.query_builder() + .with() + .with().oper(flecs::Not) .build(); ``` The builder API has a `not_` convenience method: ```cpp -flecs::filter<> f = world.filter_builder() - .term() - .term().not_(); // note escaping, 'not' is a C++ keyword +flecs::query<> q = world.query_builder() + .with() + .with().not_(); // note escaping, 'not' is a C++ keyword + .build(); +``` + +An application can also use the `without` method: + +```cpp +flecs::query<> q = world.query_builder() + .with() + .without(); + .build(); +``` + +
  • +
  • Rust + +To create a query with `Not` terms, use the `oper` method with enum `OperKind::Not`: + +```rust +let q = world + .query::<()>() + .with::() + .with::() + .set_oper(OperKind::Not) + .build(); +``` + +The builder API has a `not_` convenience method: + +```rust +let q = world + .query::<()>() + .with::() + .with::() + .not() + .build(); +``` + +An application can also use the `without` method: + +```rust +let q = world + .query::<()>() + .with::() + .without::() .build(); ``` -#### Query DSL +
  • +
  • Flecs Query Language + To create a query with `Not` terms, use the `!` symbol: ``` Position, !Velocity ``` -### Optional Operator -> *Supported by: filters, cached queries, rules* +
  • +
+
+ +### Optional Operator The `Optional` operator optionally matches with a component. While this operator does not affect the entities that are matched by a query, it can provide more efficient access to a component when compared to conditionally getting the component in user code. Before accessing the value provided by an optional term, code must first check if the term was set. A note on performance: just like the `Not` operator `Optional` terms are efficient to evaluate when combined with other terms, but queries that only have `Optional` terms can be expensive. Because the `Optional` operator does not restrict query results, a query that only has `Optional` terms will match all entities. -When an optional operator is used in a rule, and a variable written by the optional term is read by a subsequent term, the subsequent term becomes a _dependent term_. This means that if the optional term does not match, the dependent term will be ignored. For example: +When an optional operator is used in a query, and a variable written by the optional term is read by a subsequent term, the subsequent term becomes a _dependent term_. This means that if the optional term does not match, the dependent term will be ignored. For example: ``` SpaceShip, ?(DockedTo, $planet), Planet($planet) @@ -1267,24 +1867,27 @@ SpaceShip, ?(DockedTo, $planet), Planet($planet) Because the second term is optional, the variable `$planet` may or may not be set depending on whether the term was matched. As a result the third term becomes dependent: if `$planet` was not set, the term will be ignored. -The following sections show how to use the `Optional` operator in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following sections show how to use the `Optional` operator in the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) To create a query with `Optional` terms, set `oper` to `EcsOptional`: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity), .oper = EcsOptional } } }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - if (ecs_field_is_set(&it, 2)) { - Velocity *v = ecs_field(&it, Velocity, 2); +ecs_iter_t it = ecs_query_iter(world, q); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + if (ecs_field_is_set(&it, 1)) { + Velocity *v = ecs_field(&it, Velocity, 1); // iterate as usual } else { // iterate as usual @@ -1292,23 +1895,37 @@ while (ecs_filter_next(&it)) { } ``` -#### Query Builder (C++) -To create a query with `Optional` terms, call the `oper` method with `flecs::Optional`: +
  • +
  • C++ + +To create a query with `Optional` terms, a component can be specified as a pointer type: + +```cpp +flecs::query<> q = world.query() + +q.each([](Position& p, Velocity* v) { + if (v) { + // ... + } +}) +``` + +Alternatively, an application can call the `oper` method with `flecs::Optional`: ```cpp -flecs::filter<> f = world.filter_builder() - .term() - .term().oper(flecs::Optional) +flecs::query<> q = world.query_builder() + .with() + .with().oper(flecs::Optional) .build(); -f.iter([&](flecs::iter& it) { - auto p = it.field(1); - - if (it.is_set(2)) { - auto v = it.field(2); - // iterate as usual - } else if (vs_id == world.id()) { - // iterate as usual +q.run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + + if (it.is_set(1)) { + auto v = it.field(1); + // iterate as usual + } } }); ``` @@ -1316,22 +1933,72 @@ f.iter([&](flecs::iter& it) { The builder API has an `optional` convenience method: ```cpp -flecs::filter<> f = world.filter_builder() - .term() - .term().optional(); +flecs::query<> q = world.query_builder() + .with() + .with().optional(); + .build(); +``` + +
  • +
  • Rust + +To create a query with `Optional` terms, a component can be specified as an Option type: + +```rust +let q = world.new_query::<(&Position, Option<&Velocity>)>(); + +q.each(|(p, v)| { + if let Some(v) = v { + // ... + } +}); +``` + +Alternatively, an application can call the `oper` method with enum `OperKind::Optional`: + +```rust +let q = world + .query::<()>() + .with::() + .with::() + .set_oper(OperKind::Optional) + .build(); + +q.run(|mut it| { + while it.next() { + let p = it.field::(0).unwrap(); + if let Some(v) = it.field::(1) { + // iterate as usual + } + } +}); +``` + +The builder API has an `optional` convenience method: + +```rust +let q = world + .query::<()>() + .with::() + .with::() + .optional() .build(); ``` -#### Query DSL +
  • +
  • Flecs Query Language + To create a query with `Optional` terms, use the `?` symbol: ``` Position, ?Velocity ``` -### Equality operators -> *Supported by: rules* +
  • +
+
+### Equality operators Equality operators (`==`, `!=`, `~=`) allow a query to ensure that a variable equals a specific entity, that the entity it stores has a specific name, or that the entity name partially matches a substring. The left hand side of an equality operator must be a variable. The right hand side of an operator can be an entity identifier or a string for the `==` and `!=` operators, and must be a string in case of the `~=` operator. For example: @@ -1354,31 +2021,36 @@ Test if variable `$this` stores an entity with a name that has substring `Fo`: $this ~= "Fo" ``` -When the equals operator (`==`) is used with a variable that has not yet been initialized, the right-hand side of the operator will be assigned to the variable. +When the equals operator (`==`) is used with a variable that has not yet been initialized, the right-hand side of the operator will be assigned to the variable. If the right hand side is a string, it will be used to lookup an entity by name. If the lookup fails, the term will not match. Other than regular operators, equality operators are set as `first`, with the left hand being `src` and the right hand being `second`. Equality operators can be combined with `And`, `Not` and `Or` terms. Terms with equality operators return no data. -#### Query Descriptor (C) +
+
    +
  • C + ```c -ecs_rule_t *r = ecs_rule(world, { +ecs_query_t *q = ecs_query(world, { .terms = { // $this == Foo { .first.id = EcsPredEq, .second.id = Foo }, // $this != Bar { .first.id = EcsPredEq, .second.id = Bar, .oper = EcsNot }, // $this == "Foo" - { .first.id = EcsPredEq, .second = { .name = "Foo", .flags = EcsIsName }}, + { .first.id = EcsPredEq, .second = { .name = "Foo", .id = EcsIsName }}, // $this ~= "Fo" - { .first.id = EcsPredMatch, .second = { .name = "Fo", .flags = EcsIsName }}, + { .first.id = EcsPredMatch, .second = { .name = "Fo", .id = EcsIsName }}, } }); ``` -#### Query Builder (C++) +
  • +
  • C++ + ```cpp -world.rule_builder() +world.query_builder() // $this == Foo .with(flecs::PredEq, Foo) // $this != Foo @@ -1390,7 +2062,30 @@ world.rule_builder() .build(); ``` -#### Query DSL +
  • +
  • Rust + +```rust +world + .query::<()>() + // $this == Foo + .with::<(flecs::PredEq, Foo)>() + // $this != Foo + .without::<(flecs::PredEq, Bar)>() + // $this == "Foo" + .with::() + .set_second_name("Foo") + .flags(sys::EcsIsName) + // $this ~= "Fo" + .with::() + .set_second_name("Fo") + .flags(sys::EcsIsName) + .build(); +``` + +
  • +
  • Flecs Query Language + ```js $this == Foo $this != Foo @@ -1398,20 +2093,25 @@ $this == "Foo" $this != "Fo" ``` -### AndFrom, OrFrom, NotFrom Operators -> *Supported by: filters, cached queries* +
  • +
+
+### AndFrom, OrFrom, NotFrom Operators The `AndFrom`, `OrFrom` and `NotFrom` operators make it possible to match a list of components that is defined outside of the query. Instead of matching the id provided in the term, the operators match the list of components _of_ the provided id as if they were provided as a list of terms with `And`, `Or` or `Not` operators. For example, if entity `e` has components `Position, Velocity` and is combined in a query with the `AndFrom` operator, entities matching the query must have both `Position` and `Velocity`. -The `AndFrom`, `OrFrom` and `NotFrom` operators are especially useful when combined with prefab entities, which by default are not matched with queries themselves. Components that have the `DontInherit` property are ignored while matching the operators, which means that using a prefab in combination with `AndFrom`, `OrFrom` and `NotFrom` will not cause components like `Prefab` or `ChildOf` to be considered. +The `AndFrom`, `OrFrom` and `NotFrom` operators are especially useful when combined with prefab entities, which by default are not matched with queries themselves. Components that have the `(OnInstantiate, DontInherit)` property are ignored while matching the operators, which means that using a prefab in combination with `AndFrom`, `OrFrom` and `NotFrom` will not cause components like `Prefab` or `ChildOf` to be considered. Component lists can be organized recursively by adding an id to an entity with the `AND` and `OR` id flags. Fields for terms that use the `AndFrom`, `OrFrom` or `NotFrom` operators never provide data. Access modifiers for these operators default to `InOutNone`. When a `AndFrom`, `OrFrom` or `NotFrom` operator is combined with an access modifier other than `InOutDefault` or `InOutNone` query creation will fail. -The following sections show how to use the operators in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following sections show how to use the operators in the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) To use the `AndFrom`, `OrFrom` and `NotFrom` operators, set `oper` to `EcsAndFrom`, `EcsOrFrom` or `EcsNotFrom` ```c @@ -1419,7 +2119,7 @@ ecs_entity_t type_list = ecs_new_w_id(world, EcsPrefab); ecs_add(world, type_list, Position); ecs_add(world, type_list, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { type_list, .oper = EcsAndFrom }, // match Position, Velocity { type_list, .oper = EcsOrFrom }, // match Position || Velocity @@ -1428,7 +2128,9 @@ ecs_filter_t *f = ecs_filter(world, { }); ``` -#### Query Builder (C++) +
  • +
  • C++ + To use the `AndFrom`, `OrFrom` and `NotFrom` operators, call the `oper` method with `flecs::AndFrom`, `flecs::OrFrom` or `flecs::NotFrom`. ```cpp @@ -1436,33 +2138,68 @@ flecs::entity type_list = world.prefab() .add() .add(); -flecs::filter<> f = world.filter_builder() - .term(type_list).oper(flecs::AndFrom) // match Position, Velocity - .term(type_list).oper(flecs::OrFrom) // match Position || Velocity - .term(type_list).oper(flecs::NotFrom) // match !Position, !Velocity +flecs::query<> q = world.query_builder() + .with(type_list).oper(flecs::AndFrom) // match Position, Velocity + .with(type_list).oper(flecs::OrFrom) // match Position || Velocity + .with(type_list).oper(flecs::NotFrom) // match !Position, !Velocity .build(); ``` The builder API has the `and_from`, `or_from` and `not_from` convenience methods: ```cpp -flecs::filter<> f = world.filter_builder() - .term(type_list).and_from() - .term(type_list).or_from() - .term(type_list).not_from() +flecs::query<> q = world.query_builder() + .with(type_list).and_from() + .with(type_list).or_from() + .with(type_list).not_from() .build(); ``` -#### Query DSL -To create a query with the `AndFrom`, `OrFrom` and `NotFrom` operators in the C API, use `AND`, `OR` and `NOT` in combination with the bitwise OR operator (`|`): +
  • +
  • Rust + +To use the `AndFrom`, `OrFrom` and `NotFrom` operators, call the `oper` method with enum `OperKind::AndFrom`, `OperKind::OrFrom` or `flecs::NotFrom`. +```rust +let q = world + .query::<()>() + .with_id(type_list) + .set_oper(OperKind::AndFrom) // match Position, Velocity + .with_id(type_list) + .set_oper(OperKind::OrFrom) // match Position || Velocity + .with_id(type_list) + .set_oper(OperKind::NotFrom) // match !Position, !Velocity + .build(); ``` -AND | type_list, OR | type_list, NOT | type_list + +The builder API has the `and_from`, `or_from` and `not_from` convenience methods: + +```rust +let q = world + .query::<()>() + .with_id(type_list) + .and_from() + .with_id(type_list) + .or_from() + .with_id(type_list) + .not_from() + .build(); ``` -### Query scopes -> *Supported by: rules* +
  • +
  • Flecs Query Language + +To create a query with the `AndFrom`, `OrFrom` and `NotFrom` operators in the C API, use `and`, `or` and `not` in combination with the bitwise OR operator (`|`): + +``` +and | type_list, or | type_list, not | type_list +``` +
  • +
+
+ +### Query scopes Query scopes are a mechanism that allows for treating the output of a number of terms as a single condition. For example, the following query has two terms with an `Or` operator that are negated by a `Not` operator: ```c @@ -1472,19 +2209,19 @@ Position, !{ Velocity || Speed } A query scope can contain any number of terms and operators. The following query has a scope with mixed operators: -``` +```c Position, !{ Mass, Velocity || Speed, !Rotation } ``` Query scopes allow for the creation of complex queries when combined with variables and relationships. The following query finds all entities that have no children with `Position`: -``` +```c Position($this), !{ ChildOf($child, $this), Position($child) } ``` Note how this is different from this query, which finds all children that don't have `Position`: -``` +```c Position($this), ChildOf($child, $this), !Position($child) ``` @@ -1492,7 +2229,7 @@ Whereas the first query only returns parents without children with `Position`, t When a scope is evaluated, the entire result set of the scope is treated as a single term. This has as side effect that any variables first declared inside the scope are not available outside of the scope. For example, in the following query the value for variable `$child` is undefined, as it is first used inside a scope: -``` +```c Position($this), !{ ChildOf($child, $this), Position($child) } ``` @@ -1502,23 +2239,28 @@ Scopes currently have the following limitations: The following examples show how to use scopes in the different language bindings: -#### Query Descriptor (C) +
+
    +
  • C + ```c -ecs_rule_t *r = ecs_rule(world, { +ecs_query_t *q = ecs_query(world, { .terms = { // Position, !{ Velocity || Speed } { .id = ecs_id(Position) }, - { .id = EcsScopeOpen, .src.flags = EcsIsEntity, .oper = EcsNot }, + { .id = EcsScopeOpen, .src.id = EcsIsEntity, .oper = EcsNot }, { .id = ecs_id(Velocity), .oper = EcsOr }, { .id = ecs_id(Speed) }, - { .id = EcsScopeClose, .src.flags = EcsIsEntity } + { .id = EcsScopeClose, .src.id = EcsIsEntity } } }); ``` -#### Query Builder (C++) +
  • +
  • C++ + ```cpp -world.rule_builder() +world.query_builder() // Position, !{ Velocity || Speed } .with() .scope_open().not_() @@ -1528,42 +2270,66 @@ world.rule_builder() .build(); ``` -#### Query DSL -```js -Position, !{ Velocity || Speed } -``` +
  • +
  • Rust -### Source -> *Supported by: filters, cached queries, rules* +```rust +world + .query::<()>() + // Position, !{ Velocity || Speed } + .with::() + .scope_open() + .not() + .with::() + .or() + .with::() + .scope_close() + .build(); +``` -Source is a property of a term that specifies the entity on which the term should be matched. Queries support two kinds of sources: static and variable. A static source is known when the query is created (for example: match `SimTime` on entity `Game`), whereas a variable source is resolved while the query is evaluated. When no explicit source is specified, a default variable source called `$This` is used (see [Variables](#variables)). +
  • +
  • Flecs Query Language + +``` +Position, !{ Velocity || Speed } +``` + +
  • +
+
+ +### Source +Source is a property of a term that specifies the entity on which the term should be matched. Queries support two kinds of sources: static and variable. A static source is known when the query is created (for example: match `SimTime` on entity `Game`), whereas a variable source is resolved while the query is evaluated. When no explicit source is specified, a default variable source called `$this` is used (see [Variables](#variables)). When a query only has terms with fixed sources, iterating the query will return a result at least once when it matches, and at most once if the query terms do not match wildcards. If a query has one or more terms with a fixed source that do not match the entity, the query will return no results. A source does not need to match the query when the query is created. When a term has a fixed source and the [access modifiers](#access-modifiers) are not explicitly set, the access modifier defaults to `In`, instead of `InOut`. The rationale behind this is that it encourages code to only makes local changes (changes to components owned by the matched entity) which is easier to maintain and multithread. This default can be overridden by explicitly setting access modifiers. -The following sections show how to use variable and fixed sources with the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following sections show how to use variable and fixed sources with the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) To specify a fixed source, set the `src.id` member to the entity to match. The following example shows how to set a source, and how to access the value provided by a term with a fixed source: ```c -ecs_entity_t Game = ecs_new_id(world); +ecs_entity_t Game = ecs_new(world); ecs_add(world, Game, SimTime); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { - { ecs_id(Position) }, // normal term, uses $This source - { ecs_id(Velocity) }, // normal term, also uses $This source + { ecs_id(Position) }, // normal term, uses $this source + { ecs_id(Velocity) }, // normal term, also uses $this source { ecs_id(SimTime), .src.id = Game } // fixed source, match SimTime on Game } }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - SimTime *st = ecs_field(&it, SimTime, 3); +ecs_iter_t it = ecs_query_iter(world, q); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + SimTime *st = ecs_field(&it, SimTime, 2); for (int i = 0; i < it.count; i ++) { p[i].x += v[i].x * st[i].value; @@ -1582,58 +2348,62 @@ A source may also be specified by name by setting the `src.name` member: ecs_entity_t Game = ecs_entity(world, { .name = "Game" }); ecs_add(world, Game, SimTime); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_id(SimTime), .src.name = "Game" } } }); ``` -This examples shows how to access the entities matched by the default `$This` source and a fixed source: +This examples shows how to access the entities matched by the default `$this` source and a fixed source: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { - { ecs_id(Position) }, // normal term, uses $This source + { ecs_id(Position) }, // normal term, uses $this source { ecs_id(SimTime), .src.id = Game } // fixed source, match SimTime on Game } }); -while (ecs_filter_next(&it)) { - ecs_entity_t src_1 = ecs_field_src(&it, 1); // Returns 0, meaning entity is stored in it.entities - ecs_entity_t src_2 = ecs_field_src(&it, 2); // Returns Game +while (ecs_query_next(&it)) { + ecs_entity_t src_1 = ecs_field_src(&it, 0); // Returns 0, meaning entity is stored in it.entities + ecs_entity_t src_2 = ecs_field_src(&it, 1); // Returns Game for (int i = 0; i < it.count; i ++) { - printf("$This = %s, src_2 = %s\n", + printf("$this = %s, src_2 = %s\n", ecs_get_name(world, it.entities[i]), ecs_get_name(world, src_2)); } } ``` -The `entities` and `count` member are solely populated by the number of entities matched by the default `$This` source. If a query only contains fixed sources, `count` will be set to 0. This is important to keep in mind, as the inner for loop from the last example would never be iterated for a query that only has fixed sources. +The `entities` and `count` member are solely populated by the number of entities matched by the default `$this` source. If a query only contains fixed sources, `count` will be set to 0. This is important to keep in mind, as the inner for loop from the last example would never be iterated for a query that only has fixed sources. + +
  • +
  • C++ -#### Query Builder (C++) To specify a fixed source, call the `src` method to the entity to match. The following example shows how to set a source, and how to access the value provided by a term with a fixed source: ```cpp flecs::entity Game = world.entity() .add(); -flecs::filter<> f = world.filter_builder() - .term() // normal term, uses $This source - .term() // normal term, also uses $This source - .term().src(Game) // fixed source, match SimTime on Game +flecs::query<> q = world.query_builder() + .with() // normal term, uses $this source + .with() // normal term, also uses $this source + .with().src(Game) // fixed source, match SimTime on Game .build(); -f.iter([](flecs::iter& it) { - auto p = it.field(1); - auto v = it.field(2); - auto st = it.field(3); - - for (auto i : it) { - p[i].x += v[i].x * st[i].value; - p[i].y += v[i].y * st[i].value; +q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + auto st = it.field(2); + + for (auto i : it) { + p[i].x += v[i].x * st[i].value; + p[i].y += v[i].y * st[i].value; + } } }); ``` @@ -1642,47 +2412,51 @@ Note how in this example all components can be accessed as arrays. When a query Returning entities one at a time can negatively affect performance, especially for large tables. To learn more about why this behavior exists and how to ensure that mixed results use table-based iteration, see [Instancing](#instancing). -The next example shows how queries with mixed `$This` and fixed sources can be iterated with `each`. The `each` function does not have the performance drawback of the last `iter` example, as it uses [instancing](#instancing) by default. +The next example shows how queries with mixed `$this` and fixed sources can be iterated with `each`. The `each` function does not have the performance drawback of the last `run` example, as it uses [instancing](#instancing) by default. ```cpp -flecs::filter f = - world.filter_builder() - .arg(3).src(Game) // set fixed source for 3rd template argument (SimTime) +flecs::query q = + world.query_builder() + .term_at(2).src(Game) // set fixed source for 3rd template argument (SimTime) .build(); -// Because all components are now part of the filter type, we can use each -f.each([](flecs::entity e, Position& p, Velocity& v, SimTime& st) { +// Because all components are now part of the query type, we can use each +q.each([](flecs::entity e, Position& p, Velocity& v, SimTime& st) { p.x += v.x * st.value; p.y += v.y * st.value; }); ``` -When a query has no terms for the `$This` source, it must be iterated with the `iter` function or with a variant of `each` that does not have a signature with `flecs::entity` as first argument: +When a query has no terms for the `$this` source, it must be iterated with the `run` function or with a variant of `each` that does not have a signature with `flecs::entity` as first argument: ```cpp -flecs::filter f = - world.filter_builder() - .arg(1).src(Cfg) - .arg(2).src(Game) +flecs::query q = + world.query_builder() + .term_at(0).src(Cfg) + .term_at(1).src(Game) .build(); // Ok (note that it.count() will be 0) -f.iter([](flecs::iter& it, SimConfig *sc, SimTime *st) { - st->value += sc->sim_speed; +q.run([](flecs::iter& it) { + while (it.next()) { + auto sc = it.field(0); + auto st = it.field(1); + st->value += sc->sim_speed; + } }); // Ok -f.each([](SimConfig& sc, SimTime& st) { +q.each([](SimConfig& sc, SimTime& st) { st.value += sc.sim_speed; }); // Ok -f.each([](flecs::iter& it, size_t index, SimConfig& sc, SimTime& st) { +q.each([](flecs::iter& it, size_t index, SimConfig& sc, SimTime& st) { st.value += sc.sim_speed; }); // Not ok: there is no entity to pass to first argument -f.each([](flecs::entity e, SimConfig& sc, SimTime& st) { +q.each([](flecs::entity e, SimConfig& sc, SimTime& st) { st.value += sc.sim_speed; }); ``` @@ -1690,15 +2464,112 @@ f.each([](flecs::entity e, SimConfig& sc, SimTime& st) { A source may also be specified by name: ```cpp -flecs::filter f = - world.filter_builder() - .arg(1).src("Cfg") - .arg(2).src("Game") +flecs::query q = + world.query_builder() + .term_at(0).src("Cfg") + .term_at(1).src("Game") .build(); ``` -#### Query DSL -To specify a source in the DSL, use parenthesis after the component identifier. The following example uses the default `$This` source for `Position` and `Velocity`, and `Game` as source for `SimTime`. +
  • +
  • Rust + +To specify a fixed source, call the `set_src_id` method to the entity to match. The following example shows how to set a source, and how to access the value provided by a term with a fixed source: + +```rust +let q = world + .query::<()>() + .with::() // normal term, uses $this source + .with::() // normal term, uses $this source + .with::() + .set_src_id(game) // fixed source, match SimTime on Game + .build(); + +q.run(|mut it| { + while it.next() { + let mut p = it.field::(0).unwrap(); + let v = it.field::(1).unwrap(); + let st = it.field::(2).unwrap(); + for i in it.iter() { + p[i].x += v[i].x * st[i].value; + p[i].y += v[i].y * st[i].value; + } + } +}); +``` + +Note how in this example all components can be accessed as arrays. When a query has mixed fields (fields with both arrays and single values), behavior defaults to entity-based iteration where entities are returned one at a time. As a result, `i` in the previous example will never be larger than `0`, which is why this code works even though there is only a single instance of the `SimTime` component. + +Returning entities one at a time can negatively affect performance, especially for large tables. To learn more about why this behavior exists and how to ensure that mixed results use table-based iteration, see [Instancing](#instancing). + +The next example shows how queries with mixed `$this` and fixed sources can be iterated with `each`. The `each` function does not have the performance drawback of the last `run` example, as it uses [instancing](#instancing) by default. + +```rust +let q = world + .query::<(&mut Position, &Velocity, &SimTime)>() + .term_at(2) + .set_src_id(game) // fixed source for 3rd template argument (SimTime) + .build(); + +// Because all components are now part of the query type, we can use each +q.each_entity(|e, (p, v, st)| { + p.x += v.x * st.value; + p.y += v.y * st.value; +}); +``` + +When a query has no terms for the `$this` source, it must be iterated with the `run` function or with a variant of `each` that does not have a signature with `flecs::entity` as first argument: + +```rust +let q = world + .query::<(&SimConfig, &mut SimTime)>() + .term_at(0) + .set_src_id(cfg) + .term_at(1) + .set_src_id(game) + .build(); + +// Ok (note that it.count() will be 0) +q.run(|mut it| { + while it.next() { + let sc = it.field::(0).unwrap(); + let mut st = it.field::(1).unwrap(); + st[0].value += sc[0].sim_speed; // 0 because it's a single source element + } +}); + +// Ok +q.each(|(sc, st)| { + st.value += sc.sim_speed; +}); + +// Ok +q.each_iter(|it, index, (sc, st)| { + st.value += sc.sim_speed; +}); + +// Not ok: there is no entity to pass to first argument +q.each_entity(|e, (sc, st)| { + st.value += sc.sim_speed; +}); +``` + +A source may also be specified by name: + +```rust +let q = world + .query::<(&SimConfig, &SimTime)>() + .term_at(0) + .set_src_name("Cfg") + .term_at(1) + .set_src_name("Game") + .build(); +``` + +
  • +
  • Flecs Query Language + +To specify a source in the DSL, use parenthesis after the component identifier. The following example uses the default `$this` source for `Position` and `Velocity`, and `Game` as source for `SimTime`. ``` Position, Velocity, SimTime(Game) @@ -1707,10 +2578,10 @@ Position, Velocity, SimTime(Game) In the previous example the source for `Position` and `Velocity` is implicit. The following example shows the same query with explicit sources for all terms: ``` -Position($This), Velocity($This), SimTime(Game) +Position($this), Velocity($this), SimTime(Game) ``` -To specify a source for a pair, the second element of the pair is placed inside the parenthesis after the source. The following query uses the default `$This` source for the `(Color, Diffuse)` pair, and `Game` as source for the `(Color, Sky)` pair. +To specify a source for a pair, the second element of the pair is placed inside the parenthesis after the source. The following query uses the default `$this` source for the `(Color, Diffuse)` pair, and `Game` as source for the `(Color, Sky)` pair. ``` (Color, Diffuse), Color(Game, Sky) @@ -1719,21 +2590,26 @@ To specify a source for a pair, the second element of the pair is placed inside In the previous example the source for `(Color, Diffuse)` is implicit. The following example shows the same query with explicit sources for all terms: ``` -Color($This, Diffuse), Color(Game, Sky) +Color($this, Diffuse), Color(Game, Sky) ``` -### Singletons -> *Supported by: filters, cached queries, rules* +
  • +
+
+### Singletons Singletons are components that are added to themselves, which can be matched by providing the component id as [source](#source). -The following sections show how to use singletons in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following sections show how to use singletons in the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) A singleton query is created by specifying the same id as component and source: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { Player }, { ecs_id(Position) }, @@ -1744,26 +2620,65 @@ ecs_filter_t *f = ecs_filter(world, { The singleton component data is accessed in the same way a component from a static [source](#source) is accessed. -#### Query Builder (C++) +
  • +
  • C++ + A singleton query can be created by specifying the same id as component and source: ```cpp -flecs::filter f = world.filter_builder() - .term().src() // match Input on itself +flecs::query q = world.query_builder() + .with().src() // match Input on itself .build(); ``` The builder API provides a `singleton` convenience function: ```cpp -flecs::filter f = world.filter_builder() - .term().singleton() // match Input on itself +flecs::query q = world.query_builder() + .with().singleton() // match Input on itself .build(); ``` The singleton component data is accessed in the same way a component from a static [source](#source) is accessed. -#### Query DSL +
  • +
  • Rust + +A singleton query can be created by specifying the same id as component and source: + +```rust +let q = world + .query::<(&Player, &Position)>() + .with::() + .set_src::() // match Input on itself + .build(); +``` + +The builder API provides a `singleton` convenience function: + +```rust +let q = world + .query::<(&Player, &Position)>() + .with::() + .singleton() // match Input on itself + .build(); +``` + +The singleton component data is accessed in the same way a component from a static [source](#source) is accessed. + +The builder API provides a `term_at` function which allows specifying characteristics of a term at a specific index in the generic terms. + +```rust +let q = world + .query::<(&Player, &Position, &Input)>() + .term_at(2) + .singleton() // match Input on itself + .build(); +``` + +
  • +
  • Flecs Query Language + A singleton query can be created by specifying the same id as component and source: ``` @@ -1776,509 +2691,458 @@ For convenience the `$` character may be used as source, which resolves to the c Player, Position, Input($) ``` -### Relationship Traversal -> *Supported by: filters, cached queries, rules* +
  • +
+
+### Relationship Traversal Relationship traversal enables a query to search for a component by traversing a relationship. One of the most common examples of where this is useful is a Transform system, which matches `Position` on an entity and the entity's parent. To find the `Position` component on a parent entity, a query traverses the `ChildOf` relationship upwards: ![filter diagram](img/relationship_traversal.png) The arrows in this diagram indicate the direction in which the query is traversing the `ChildOf` relationship to find the component. A query will continue traversing until it has found an entity with the component, or until a root (an entity without the relationship) has been found. The traversal is depth-first. If an entity has multiple instances of a relationship a query will first traverse the first instance until its root entity before continuing with the second instance. -Using the relationship traversal feature will in most cases provide better performance than doing the traversal in user code. This is especially true for cached queries, where the results of traversal are cached. Relationship traversal can in some edge cases cause performance degradation, especially in applications with large numbers of cached queries and deep hierarchies. See the [Performance](#performance) section for more details. +Using the relationship traversal feature will in most cases provide better performance than doing the traversal in user code. This is especially true for cached queries, where the results of traversal are cached. Relationship traversal can in some edge cases cause performance degradation, especially in applications with large numbers of cached queries and deep hierarchies. See the section on performance & rematching for more details. Any relationship used for traversal must have the [Traversable](Relationships.md#traversable-property) property. Attempting to create a query that traverses a relationship that does not have the `Traversable` property will cause query creation to fail. This safeguards against creating queries that could end up in an infinite traversal loop when a cyclic relationship is encountered. -Components that have the [DontInherit](Relationships.md#dontinherit-property) property cannot be matched through traversal. Examples of builtin components that have the `DontInherit` property are `Prefab` (instances of prefabs should not be considered prefabs) and `ChildOf` (a child of a parent is not a child of the parent's parent). - Relationship traversal works for both variable and fixed [sources](#source). #### Traversal Flags -Traversal behavior can be customized with the following bitflags, in addition to the relationship being traversed: +Traversal behavior can be customized with the following bit flags, in addition to the relationship being traversed: | Name | DSL identifier | C identifier | C++ identifier | Description | |----------|----------------|---------------|-------------------|-------------| | Self | `self` | `EcsSelf` | `flecs::Self` | Match self | | Up | `up` | `EcsUp` | `flecs::Up` | Match by traversing upwards | -| Down | `down` | `EcsDown` | `flecs::Down` | Match by traversing downwards (derived, cannot be set) | -| Parent | `parent` | `EcsParent` | `flecs::Parent` | Short for up(ChildOf) | | Cascade | `cascade` | `EcsCascade` | `flecs::Cascade` | Same as Up, but iterate in breadth-first order | | Desc | `desc` | `EcsDesc` | `flecs::Desc` | Combine with Cascade to iterate hierarchy bottom to top | If just `Self` is set a query will only match components on the matched entity (no traversal). If just `Up` is set, a query will only match components that can be reached by following the relationship and ignore components from the matched entity. If both `Self` and `Up` are set, the query will first look on the matched entity, and if it does not have the component the query will continue searching by traverse the relationship. -Query terms default to `Self|Up` for the `IsA` relationship. This means that components inherited from prefabs will be matched automatically by queries, unless specified otherwise. When a relationship that is not `IsA` is traversed, the entities visited while traversing will still be tested for inherited components. This means that an entity with a parent that inherits the `Mass` component from a prefab will match a query that traverses the `ChildOf` relationship to match the `Mass` component. A code example: +When an `Up` traversal flag is set, but no traversal relationship is provided, the traversal relationship defaults to `ChildOf`. An example: -```cpp -flecs::entity base = world.entity() - .add(); +
+
    +
  • C -flecs::entity parent = world.entity() - .add(flecs::IsA, base); // inherits Mass +```c +// These two queries are the same: +ecs_query_t *q1 = ecs_query(world, { + .terms = { ecs_id(Mass), .src.id = EcsUp, .trav = EcsChildOf } +}); -flecs::entity child = world.entity() - .add(flecs::ChildOf, parent); +ecs_query_t *q2 = ecs_query(world, { + .terms = { ecs_id(Mass), .src.id = EcsUp } // defaults to .trav = EcsChildOf +}); +``` -// This filter matches 'child', because it has a parent that inherits Mass -flecs::filter<> f = world.filter_builder() - .term().up(flecs::ChildOf) +
  • +
  • C++ + +```cpp +// These three queries are the same: +flecs::query<> q1 = world.query_builder() + .with().up(flecs::ChildOf) .build(); -``` -When a component is matched through traversal and its [access modifier](#access-modifiers) is not explicitly set, it defaults to `flecs::In`. This behavior is consistent with terms that have a fixed [source](#source). +flecs::query<> q2 = world.query_builder() + .with().up() // defaults to .up(flecs::ChildOf) + .build(); -#### Iteration -When a component is matched through traversal, the behavior is the same as though the component was matched through a fixed [source](#source): iteration will switch from table-based to entity-based. This happens on a per-result basis: if all terms are matched on the matched entity the entire table will be returned by the iterator. If one of the terms was matched through traversal, entities are returned one by one. +flecs::query<> q3 = world.query_builder() + .with().parent() // shortcut for .up(flecs::ChildOf) + .build(); +``` -While returning entities one by one is measurably slower than iterating entire tables, this default behavior enables matching inherited components by default without requiring the user code to be explicitly aware of the difference between a regular component and an inherited component. An advantage of this approach is that applications that use inherited components can interoperate with third party systems that do not explicitly handle them. +
  • +
  • Rust -To ensure fast table-based iteration an application can enable [instancing](#instancing). Instanced iteration is as fast as, and often faster than regular iteration. Using inherited components that are shared across many entities can improve cache efficiency as less data needs to be loaded from main RAM, and values are more likely to already be stored in the cache. +```rust +// These three queries are the same: +let q1 = world + .query::<()>() + .with::() + .up_type::() + .build(); -> Note: the C++ `each` API always uses [instancing](#instancing), which guarantees fast table-based iteration. +let q2 = world + .query::<()>() + .with::() + .up() // defaults to .up(flecs::ChildOf) + .build(); -#### Limitations -This list is an overview of current relationship traversal limitations: +let q3 = world + .query::<()>() + .with::() + .parent() // shortcut for .up(flecs::ChildOf) + .build(); +``` -- The `Down` flag is currently reserved for internal purposes and should not be set when creating a query. -- The `Cascade` and `Desc` flag only works for cached queries. -- Traversal flags can currently only be specified for the term source. -- Union relationships are not supported for traversal. +
  • +
+
-The following sections show how to use traversal in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +Query terms default to `Self`, except for components that have the `(OnInstantiate, Inherit)` trait. In that case, terms default to `Self|Up` with the `IsA` relationship, which matches components inherited from `IsA` targets (typically prefabs). An example: -#### Query Descriptor (C) -By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. In the following example, both `base` and `inst` match the query: +
+
    +
  • C ```c -ecs_entity_t base = ecs_new(world, Position); -ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); // Inherits Position +// Register an inheritable component 'Mass' +ECS_COMPONENT(world, Mass); +ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); -ecs_filter_t *f = ecs_filter(world, { - .terms = { - { ecs_id(Position) } - } +// These two queries are the same: +ecs_query_t *q1 = ecs_query(world, { + .terms = { ecs_id(Mass), .src.id = EcsSelf|EcsUp, .trav = EcsIsA } }); -``` - -Implicit traversal can be disabled by setting the `flags` member to `EcsSelf`. The following example only matches `base`: -```c -ecs_filter_t *f = ecs_filter(world, { - .terms = { - { ecs_id(Position), .src.flags = EcsSelf } - } +ecs_query_t *q2 = ecs_query(world, { + .terms = { ecs_id(Mass) } // defaults to , .src.id = EcsSelf|EcsUp, .trav = EcsIsA }); ``` -To use a different relationship for traversal, use the `trav` member in combination with the `EcsUp` flag. The following example only matches `child`: +
  • +
  • C++ -```c -ecs_entity_t parent = ecs_new(world, Position); -ecs_entity_t child = ecs_new(world, Position); +```cpp +// Register an inheritable component 'Mass' +world.component().add(flecs::OnInstantiate, flecs::Inherit); -ecs_add_pair(world, child, EcsChildOf, parent); +// These two queries are the same: +flecs::query<> q1 = world.query_builder() + .with().self().up(flecs::IsA) + .build(); -ecs_filter_t *f = ecs_filter(world, { - .terms = { - // term matches parent & child - { ecs_id(Position) }, - // term just matches child, parent does not have a parent with Position - { ecs_id(Position), .src.flags = EcsUp, src.trav = EcsChildOf } - } -}); +flecs::query<> q2 = world.query_builder() + .with() // defaults to .self().up(flecs::IsA) + .build(); ``` -The `EcsParent` flag can be used which is shorthand for `EcsUp` with `EcsChildOf`. The query in the following example is equivalent to the one in the previous example: +
  • +
  • Rust -```c -ecs_filter_t *f = ecs_filter(world, { - .terms = { - { ecs_id(Position) }, - { ecs_id(Position), .src.flags = EcsParent } - } -}); -``` +```rust + // Register an inheritable component 'Mass' + world + .component::() + .add_trait::<(flecs::OnInstantiate, flecs::Inherit)>(); -If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional. The following example matches both `parent` and `child`. The second term is not set for the result that contains `parent`. + // These two queries are the same: + let q1 = world + .query::<()>() + .with::() + .self_() + .up_type::() + .build(); -```c -ecs_filter_t *f = ecs_filter(world, { - .terms = { - { ecs_id(Position) }, - { ecs_id(Position), .src.flags = EcsParent, .oper = EcsOptional } - } -}); + let q2 = world + .query::<()>() + .with::() // defaults to .self().up(flecs::IsA) + .build(); ``` -By adding the `EcsCascade` flag, a query will iterate the hierarchy top-down. This is only supported for cached queries: +
  • +
+
-```c -ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_id(Position) }, - { ecs_id(Position), .src.flags = EcsCascade|EcsParent, .oper = EcsOptional } - } -}); -``` +When a relationship that is not `IsA` is traversed, the entities visited while traversing will still be tested for inherited components. This means that an entity with a parent that inherits the `Mass` component from a prefab will match a query that traverses the `ChildOf` relationship to match the `Mass` component. An example: -Relationship traversal can be combined with fixed [source](#source) terms. The following query matches if the `my_widget` entity has a parent with the `Window` component: +
+
    +
  • C ```c -ecs_filter_t *f = ecs_filter(world, { - .terms = { - { ecs_id(Window), .src.flags = EcsParent, .src.id = my_widget } - } -}); -``` +// Register an inheritable component 'Mass' +ECS_COMPONENT(world, Mass); +ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); -The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: - -```cpp -ecs_filter_t *f_1 = ecs_filter(world, { - .terms = { - { ecs_id(Position) } - } -}); +// Create a prefab, prefab instance and a child +ecs_entity_t prefab = ecs_insert(world, { EcsPrefab }, ecs_value(Mass, 100)); +ecs_entity_t parent = ecs_insert(world, { ecs_isa(prefab) }); // inherits Mass +ecs_entity_t child = ecs_entity(world, { .parent = parent }); -ecs_filter_t *f_2 = ecs_filter(world, { - .terms = {{ - .id = ecs_id(Position), // match Position - .src.flags = EcsSelf | EcsUp // first match self, traverse upwards while not found - .src.trav = EcsIsA, // traverse using the IsA relationship - }} +// Matches 'child', because parent inherits Mass from prefab +ecs_query_t *q = ecs_query(world, { + .terms = { ecs_id(Mass), .src.id = EcsUp } // traverses ChildOf upwards }); ``` -#### Query Builder (C++) -By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. In the following example, both `base` and `inst` match the query: +
  • +
  • C++ ```cpp -flecs::entity base = world.entity() - .add(); - -flecs::entity inst = world.entity() - .is_a(base); // short for .add(flecs::IsA, base) - -flecs::filter<> f = world.filter(); -``` - -Implicit traversal can be disabled by calling the `self` method for the term. The following example only matches `base`: - -```cpp -flecs::filter<> f = world.filter_builder() - .term().self() - .build(); -``` +// Register an inheritable component 'Mass' +world.component().add(flecs::OnInstantiate, flecs::Inherit); -To use a different relationship for traversal, use the `up` method with the relationship as argument. The following example only matches `child`: +flecs::entity base = world.entity() + .add(); -```cpp flecs::entity parent = world.entity() - .add(); + .add(flecs::IsA, base); // inherits Mass flecs::entity child = world.entity() - .child_of(parent); // short for .add(flecs::ChildOf, parent) + .add(flecs::ChildOf, parent); -flecs::filter<> f = world.filter_builder() - // term matches parent & child - .term() - // term just matches child, parent does not have a parent with Position - .term().up(flecs::ChildOf) +// Matches 'child', because parent inherits Mass from prefab +flecs::query<> q = world.query_builder() + .with().up() // traverses ChildOf upwards .build(); ``` -The `parent` method can be used which is shorthand for `up(flecs::ChildOf)`. The query in the following example is equivalent to the one in the previous example: +
  • +
  • Rust -```cpp -flecs::filter<> f = world.filter_builder() - .term() - .term().parent() - .build(); -``` +```rust +// Register an inheritable component 'Mass' +world + .component::() + .add_trait::<(flecs::OnInstantiate, flecs::Inherit)>(); -If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional. The following example matches both `parent` and `child`. The second term is not set for the result that contains `parent`. +let base = world.entity().add::(); -```cpp -flecs::filter<> f = world.filter_builder() - .term() - .term().parent().optional() - .build(); -``` +let parent = world.entity().is_a_id(base); // inherits Mass -By calling the `cascade` method, a query will iterate the hierarchy top-down. Note that the example could also have called both `parent()` and `cascade()`. The `cascade` feature is only supported for cached queries: +let child = world.entity().child_of_id(parent); -```cpp -flecs::query<> q = world.query_builder() - .term() - .term().cascade(flecs::ChildOf).optional() - .build(); -``` - -Relationship traversal can be combined with fixed [source](#source) terms. The following query matches if the `my_widget` entity has a parent with the `Window` component: - -```cpp -flecs::filter<> f = world.filter_builder() - .term().src(my_widget).parent() - .build(); +// Matches 'child', because parent inherits Mass from prefab +let q = world + .query::<()>() + .with::() + .up() // traverses ChildOf upwards + .build(); ``` -The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: +
  • +
+
-```cpp -flecs::filter<> f = world.filter(); +When a component is matched through traversal and its [access modifier](#access-modifiers) is not explicitly set, it defaults to `flecs::In`. This behavior is consistent with terms that have a fixed [source](#source). -flecs::filter<> f = world.filter_builder() - .term() // match Position - .self() // first match self - .up(flecs::IsA) // traverse IsA upwards while not found - .build(); -``` +#### Iteration +When a component is matched through traversal, the behavior is the same as though the component was matched through a fixed [source](#source): iteration will switch from table-based to entity-based. This happens on a per-result basis: if all terms are matched on the matched entity the entire table will be returned by the iterator. If one of the terms was matched through traversal, entities are returned one by one. -#### Query DSL -By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. The following query matches `Position` on entities that either have the component or inherit it: +While returning entities one by one is measurably slower than iterating entire tables, this default behavior enables matching inherited components by default without requiring the user code to be explicitly aware of the difference between a regular component and an inherited component. An advantage of this approach is that applications that use inherited components can interoperate with third party systems that do not explicitly handle them. -``` -Position -``` +To ensure fast table-based iteration an application can enable [instancing](#instancing). Instanced iteration is as fast as, and often faster than regular iteration. Using inherited components that are shared across many entities can improve cache efficiency as less data needs to be loaded from main RAM, and values are more likely to already be stored in the cache. -Implicit traversal can be disabled by adding `self` enclosed by parentheses after the component identifier: +> Note: the C++ `each` API always uses [instancing](#instancing), which guarantees fast table-based iteration. -``` -Position(self) -``` +#### Limitations +This list is an overview of current relationship traversal limitations: -To use a different relationship for traversal, specify `up` with the relationship as argument: +- The `Cascade` and `Desc` flags require caching. +- Traversal flags can only be specified for the term source. +- Union relationship queries that are not wildcards are not supported for traversal. -``` -Position(up(ChildOf)) -``` +The following sections show how to use traversal in the different language bindings. -The `parent` keyword can be used which is shorthand for `up(ChildOf)`. The query in the following example is equivalent to the one in the previous example: +
+
    +
  • C -``` -Position(parent) -``` +The following example shows a query that matches an inherited component: -If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional: +```c +// Register inheritable 'Position' component +ECS_COMPONENT(world, Position); +ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); -``` -Position, ?Position(parent) -``` +ecs_entity_t base = ecs_new_w(world, Position); +ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); // Inherits Position -By adding the `cascade` keyword, a query will iterate the `ChildOf` hierarchy top-down. The `cascade` feature is only supported by cached queries: +// The following two queries are the same: +ecs_query_t *q1 = ecs_query(world, { + .terms = { + { ecs_id(Position) } + } +}); -``` -Position, ?Position(parent|cascade) +ecs_query_t *q2 = ecs_query(world, { + .terms = { + { ecs_id(Position), .src.id = EcsSelf|EcsUp, .trav = EcsIsA } + } +}); ``` -Relationship traversal can be combined with fixed [source](#source) terms, by using a colon (`:`) to separate the traversal flags and the source identifier. The following query matches if the `my_widget` entity has a parent with the `Window` component: - -``` -Window(parent:my_widget) -``` +The following example shows a query that matches a component from a parent: -The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: +```c +ecs_entity_t parent = ecs_new_w(world, Position); +ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, parent); -``` -Position -Position(self|up(IsA)) +ecs_query_t *q1 = ecs_query(world, { + .terms = { + { ecs_id(Position), .self.id = EcsUp } + } +}); ``` -### Instancing -> *Supported by: filters, cached queries, rules* +The following example shows a query that traverses a custom relationship: -> **Note**: this section is useful when optimizing code, but is not required knowledge for using queries. +```c +// Create a new traversable relationship +ecs_entity_t ContainedBy = ecs_insert(world, { EcsTraversable }); -Instancing is the ability to return results with fields that have different numbers of elements. An example of where this is relevant is a query with terms that have both variable and fixed [sources](#source): +ecs_entity_t parent = ecs_new_w(world, Position); +ecs_entity_t inst = ecs_new_w_pair(world, ContainedBy, parent); -``` -Position, Velocity, SimTime(Game) +ecs_query_t *q1 = ecs_query(world, { + .terms = { + { ecs_id(Position), .self.id = EcsUp, .trav = ContainedBy } + } +}); ``` -This query may return a result with a table that has many entities, therefore resulting in many component instances being available for `Position` and `Velocity`, while only having a single instance for the `SimTime` component. +
  • +
  • C++ -Another much more common cause of mixed results is if a matched component is inherited from a prefab. Consider this example: +The following example shows a query that matches an inherited component: ```cpp -flecs::entity base = world.prefab("base") - .set({10}); +// Register inheritable 'Position' component +world.component().add(flecs::OnInstantiate, flecs::Inherit); -// Entities that share Mass and own Position -flecs::entity inst_1 = world.entity("inst_1") - .is_a(base) // short for .add(flecs::IsA, base) - .set({10, 20}); - -flecs::entity inst_2 = world.entity("inst_2") - .is_a(base) - .set({30, 40}); +flecs::entity base = world.entity() + .add(); -// Entities that own Mass and Position -flecs::entity ent_1 = world.entity("ent_1") - .set({20}) - .set({40, 50}); +flecs::entity inst = world.entity() + .is_a(base); // short for .add(flecs::IsA, base) -flecs::entity ent_2 = world.entity("ent_2") - .set({30}) - .set({50, 60}); +// The following two queries are the same: +flecs::query<> q1 = world.query(); -flecs::filter f = world.filter(); +flecs::query<> q2 = world.query_builder() + .term_at(0).self().up(flecs::IsA) + .build(); ``` -The filter in this example will match both `inst_1`, `inst_2` because they inherit `Mass`, and `ent_1` and `ent_2` because they own `Mass`. The following example shows an example of code that iterates the filter: +The following example shows a query that matches a component from a parent: ```cpp -f.iter([](flecs::iter& it, Position *p, Mass *m) { - std::cout << "Result" << std::endl; - for (auto i : it) { - std::cout << " - " << it.entity(i).name() << ": " - << m[i].value - << std::endl; - } -}); -``` - -With iteration being table-based the expectation is that the result looks something like this, where all entities in the same table are iterated in the same result: - -``` -Result - - inst_1: 10 - - inst_2: 10 -Result - - ent_1: 20 - - ent_2: 30 -``` +flecs::entity parent = world.entity() + .add(); -Instead, when ran this code produces the following output: +flecs::entity child = world.entity() + .child_of(parent); // short for .add(flecs::ChildOf, base) -``` -Result - - inst_1: 10 -Result - - inst_2: 10 -Result - - ent_1: 20 - - ent_2: 30 +flecs::query<> q = world.query_builder() + .term_at(0).up() + .build(); ``` -The prefab instances are returned one at a time, even though they are stored in the same table. The rationale behind this behavior is that it prevents a common error that was present in the previous code example, specifically where the `Mass` component was read: +The following example shows a query that traverses a custom relationship: ```cpp - << m[i].value -``` +// Create a new traversable relationship +flecs::entity ContainedBy = world.entity().add(flecs::Traversable); -If `inst_1` and `inst_2` would have been returned in the same result it would have caused the inner for loop to loop twice. This means `i` would have become `1` and `m[1].value` would have caused an out of bounds violation, likely crashing the code. Why does this happen? +flecs::entity parent = world.entity() + .add(); -The reason is that both entities inherit the same instance of the `Mass` component. The `Mass` pointer provided by the iterator is not an array to multiple values, in this case it is _a pointer to a single value_. To fix this error, the component would have to be accessed as: +flecs::entity child = world.entity() + .add(ContainedBy, parent); -```cpp - << m->value +flecs::query<> q = world.query_builder() + .term_at(0).up(ContainedBy) + .build(); ``` -To prevent subtle errors like this from happening, iterators will switch to entity-based iteration when a result has fields of mixed lengths. This returns entities one by one, and guarantees that the loop variable `i` always has value `0`. The expression `m[0].value` is equivalent to `m->value`, which is the right way to access the component. +
  • +
  • Rust -> **Note**: As long as all fields in a result are of equal length, the iterator switches to table-based iteration. This is shown in the previous example: both `ent_1` and `ent_2` are returned in the same result. +The following example shows a query that matches an inherited component: -While this provides a safe default to iterate results with shared components, it does come at a performance cost. Iterating entities one at a time can be much slower than iterating an entire table, especially if tables are large. When this cost is too great, iteration can be *instanced*, which prevents switching from table-based to entity-based iteration. +```rust + // Register inheritable 'Position' component + world + .component::() + .add_trait::<(flecs::OnInstantiate, flecs::Inherit)>(); -> **Note**: The implementation of the C++ `each` function always uses instancing. + let base = world.entity().add::(); + let inst = world.entity().is_a_id(base); // short for .add_id((flecs::IsA::ID, base)); -The following diagram shows the difference in how results are returned between instanced iteration and non-instanced iteration. Each result block corresponds with a call to the `iter` method in the above example: + // The following two queries are the same: + let q1 = world.new_query::<&Position>(); -![filter diagram](img/query_instancing.png) + let q2 = world + .query::<&Position>() + .term_at(0) + .self_() + .up_id(flecs::IsA::ID) + .build(); +``` -Note that when iteration is not instanced, `inst_1` and `inst_2` are returned in separate results, whereas with instanced iteration they are both combined in the same result. Iterating the right result is faster for a few reasons: +The following example shows a query that matches a component from a parent: -- Fewer results means less overhead from iteration code -- Direct access to component arrays allows for optimizations like auto-vectorization +```rust +let parent = world.entity().add::(); +let child = world.entity().child_of_id(parent); // short for .add_id((flecs::ChildOf::ID, base)); -However, what the diagram also shows is that code for instanced iterators must handle results where `Mass` is either an array or a single value. This is the tradeoff of instancing: faster iteration at the cost of more complex iteration code. +let q = world +.query::<&Position>() +.term_at(0).up() +.build(); +``` -The following sections show how to use instancing in the different language bindings. The code examples use filter queries, but also apply to queries and rules. +The following example shows a query that traverses a custom relationship: -#### Query Descriptor (C) -Queries can be instanced by setting the `instanced` member to true: +```rust +// Create a new traversable relationship +let contained_by = world.entity().add::(); -```c -ecs_filter_t *f = ecs_filter(world, { - .terms = { - { ecs_id(Position), src.flags = EcsSelf }, // Never inherit Position - { ecs_id(Mass) } - }, +let parent = world.entity().add::(); - // Instancing is a property of the iterator, but by setting it on the query - // all iterators created for the query will be instanced. - .instanced = true -}); +let child = world.entity().add_id((contained_by, parent)); -ecs_iter_t it = ecs_filter_iter(world, &it); -while (ecs_filter_next(&it)) { - // Fetch components as usual - Position *p = ecs_field(&it, Position, 1); - Mass *m = ecs_field(&it, Mass, 2); +let q = world + .query::<&Position>() + .term_at(0) + .up_id(contained_by) + .build(); +``` - if (ecs_field_is_self(&it, 2)) { - // Mass is matched on self, access as array - for (int i = 0; i < it.count; i ++) { - p[i].x += 1.0 / m[i].value; - p[i].y += 1.0 / m[i].value; - } - } else { - // Mass is matched on other entity, access as single value - for (int i = 0; i < it.count; i ++) { - p[i].x += 1.0 / m->value; - p[i].y += 1.0 / m->value; - } - } -} +
  • +
  • Flecs Query Language + +The following example shows a query that matches a component with the `(OnInstantiate, Inherit)` trait: + +```c +// The following two queries are the same: +Position +Position(self|up) ``` -Note how the `ecs_field_is_self` test is moved outside of the for loops. This keeps conditional statements outside of the core loop, which enables optimizations like auto-vectorization. +The following example shows a query that matches a component from a parent: -#### Query Builder (C++) -Queries can be instanced by calling the `instanced` method: +```c +Position(up) +``` -```cpp -flecs::filter f = world.filter_builder() - // Never inherit Position - .arg(1).self() - // Instancing is a property of the iterator, but by setting it on the query - // all iterators created for the query will be instanced. - .instanced() - .build(); +The following example shows a query that traverses a custom relationship: -f.iter([](flecs::iter& it, Position *p, Mass *v) { - if (it.is_self(2)) { - // Mass is matched on self, access as array - for (auto i : it) { - p[i].x += 1.0 / m[i].value; - p[i].y += 1.0 / m[i].value; - } - } else { - // Mass is matched on other entity, access as single value - for (auto i : it) { - p[i].x += 1.0 / m->value; - p[i].y += 1.0 / m->value; - } - } -}); +```c +Position(up ContainedBy) ``` -Note how the `it.is_self()` test is moved outside of the for loops. This keeps conditional statements outside of the core loop, which enables optimizations like auto-vectorization. +
  • +
+
### Variables -> *Partial support: filters, cached queries. Full support: rules* - Query variables represent the state of a query while it is being evaluated. The most common form of state is "the entity (or table) against which the query is evaluated". While a query is evaluating an entity or table, it has to store it somewhere. In flecs, that "somewhere" is a query variable. Consider this query example, written down with explicit term [sources](#source): ``` -Position($This), Velocity($This) +Position($this), Velocity($this) ``` The first term to encounter a variable is usually the one to populate it with all candidates that could match that term. Subsequent terms then use the already populated variable to test if it matches. If the condition matches, the query moves on to the next term. If the condition fails, the query moves back to the previous term and, if necessary, populates the variable with the next candidate. These kinds of conditions are usually referred to as [predicates](https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)), and this evaluation process is called [backtracking](https://en.wikipedia.org/wiki/Backtracking). -This process effectively _constrains_ the possible results that a term could yield. By itself, the `Velocity` term would return all entities with the `Velocity` component, but because `$This` has been assigned already with entities that have `Position`, the term only feeds forward entities that have both `Position` and `Velocity`. +This process effectively _constrains_ the possible results that a term could yield. By itself, the `Velocity` term would return all entities with the `Velocity` component, but because `$this` has been assigned already with entities that have `Position`, the term only feeds forward entities that have both `Position` and `Velocity`. While using variables as [source](#source) is the most common application for variables, variables can be used in any part of the term. Consider constructing a query for all spaceships that are docked to a planet. A first attempt could look like this: @@ -2289,24 +3153,24 @@ SpaceShip, (DockedTo, *) When rewritten with explicit sources, the query looks like this: ``` -SpaceShip($This), DockedTo($This, *) +SpaceShip($this), DockedTo($this, *) ``` This returns all spaceships that are docked to _anything_, instead of docked to planets. To constrain the result of this query, the wildcard used as target for the `DockedTo` relationship can be replaced with a variable. An example: ``` -SpaceShip($This), DockedTo($This, $Location) +SpaceShip($this), DockedTo($this, $Location) ``` When the second term is evaluated for the first time, `$Location` will not yet be populated. This causes the term to do two things: -1. Test if the entity/table populated in `$This` has `(DockedTo, *)` +1. Test if the entity/table populated in `$this` has `(DockedTo, *)` 2. If so, populate `$Location` with the id matched by `*`. After evaluating the second term, the `$Location` variable is populated with the location the spaceship is docked to. We can now use this variable in a new term, that constrains the location to only entities that have `Planet`: ``` -SpaceShip($This), DockedTo($This, $Location), Planet($Location) +SpaceShip($this), DockedTo($this, $Location), Planet($Location) ``` This query returns the desired result ("return all spaceships docked to a planet"). @@ -2314,7 +3178,7 @@ This query returns the desired result ("return all spaceships docked to a planet Variables can also be used to constrain matched components. Consider the following example query: ``` -Serializable($Component), $Component($This) +Serializable($Component), $Component($this) ``` This query returns serializable components for all entities that have at least one. @@ -2323,33 +3187,69 @@ This query returns serializable components for all entities that have at least o By default variables are assigned while the query is being iterated, but variables can be set before query iteration to constrain the results of a query. Consider the previous example: ``` -SpaceShip($This), DockedTo($This, $Location) +SpaceShip($this), DockedTo($this, $Location) ``` -An application can set the `$This` variable or `$Location` variables, or both, before starting iteration to constrain the results returned by the query. This makes it possible to reuse a single query for multiple purposes, which provides better performance when compared to creating multiple queries. +An application can set the `$this` variable or `$Location` variables, or both, before starting iteration to constrain the results returned by the query. This makes it possible to reuse a single query for multiple purposes, which provides better performance when compared to creating multiple queries. -The following sections show how to use queries in the different language bindings. The code examples use rules queries, which currently are the only queries that support using variables other than `$This`. +#### Lookup variables +Variables can be used as the starting point of a by-name lookup. This can be useful when matching hierarchies that have a well-defined structure. An example: + +```c +// Match all spaceship entities where the cockpit has no power +SpaceShip($this), !Powered($this.cockpit) +``` + +This query will look for an child entity named `cockpit` in the scope of the matched entity for `$this`, and use that entity to match with `Powered`. If no entity with the name `cockpit` is found, the term will evaluate to false. + +The following sections show how to use queries in the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) Query variables can be specified by setting the `name` member in combination with setting the `EcsIsVariable` bit in the `flags` member: ```c // SpaceShip, (DockedTo, $Location), Planet($Location) -ecs_rule_t *r = ecs_rule(world, { +ecs_query_t *q = ecs_query(world, { .terms = { { .id = SpaceShip }, { .first.id = DockedTo, .second = { .name = "Location", - .flags = EcsIsVariable + .id = EcsIsVariable } }, { .id = Planet, .src = { .name = "Location", - .flags = EcsIsVariable + .id = EcsIsVariable + } + } + } +}); +``` + +Alternatively an application can specify a name with the `$` prefix to indicate it is a variable: + +```c +// SpaceShip, (DockedTo, $Location), Planet($Location) +ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = SpaceShip }, + { + .first.id = DockedTo, + .second = { + .name = "$Location" + } + }, + { + .id = Planet, + .src = { + .name = "$Location" } } } @@ -2359,68 +3259,219 @@ ecs_rule_t *r = ecs_rule(world, { An application can constrain the results of the query by setting the variable before starting iteration: ```c -ecs_entity_t earth = ecs_new(world, Planet); +ecs_entity_t earth = ecs_new_w(world, Planet); // Find index for Location variable -int32_t location_var = ecs_rule_find_var(r, "Location"); +int32_t location_var = ecs_query_find_var(q, "Location"); // Constrain results of iterator to return spaceships docked to Earth -ecs_iter_t it = ecs_rule_iter(world, r); +ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_set_var(&it, location_var, earth); // Iterate as usual ``` -#### Query Builder (C++) +
  • +
  • C++ + Query variables can be specified by specifying a name with a `$` prefix: ```cpp -auto r = world.rule_builder() - .term() - .term().second("$Location") - .term().src("$Location") +auto q = world.query_builder() + .with() + .with().second("$Location") + .with().src("$Location") .build(); ``` Alternatively, variables can also be specified using the `var` method: ```cpp -auto r = world.rule_builder() - .term() - .term().second().var("Location") - .term().src().var("Location") +auto q = world.query_builder() + .with() + .with().second().var("Location") + .with().src().var("Location") + .build(); +``` + +An application can constrain the results of the query by setting the variable before starting iteration: + +```cpp +flecs::entity earth = world.entity(); + +int32_t location_var = q.find_var("Location"); +q.iter().set_var(location_var, earth).each([]{ + // iterate as usual +}); +``` + +Alternatively the variable name can be provided to `set_var` directly: + +```cpp +flecs::entity earth = world.entity(); + +q.iter().set_var("Location", earth).each([]{ + // iterate as usual +}); +``` + +
  • +
  • Rust + +Query variables can be specified by specifying a name with a `$` prefix: + +```rust +let q = world + .query::<()>() + .with::() + .with::() + .set_second_name("$Location") + .with::() + .set_src_name("$Location") + .build(); +``` + +Alternatively, variables can also be specified using the `var` method: + +```rust +let q = world + .query::<()>() + .with::() + .with::() + .second() + .set_var("$Location") + .with::() + .src() + .set_var("$Location") + .build(); +``` + +An application can constrain the results of the query by setting the variable before starting iteration: + +```rust +let earth = world.entity(); +let location_var = q.find_var("$Location").unwrap(); + +q.iterable().set_var(location_var, earth).each(|it| { + // iterate as usual +}); +``` + +Alternatively the variable name can be provided to `set_var` directly: + +```rust +let earth = world.entity(); + +q.iterable().set_var_expr("$Location", earth).each(|it| { + // iterate as usual +}); +``` + +
  • +
+
+ +### Member Value Queries +Queries can match against the values of component members if they are of the `ecs_entity_t` type, and the component type is described with the reflection framework. Member value queries make it possible to query on values that can change rapidly without requiring structural changes in the ECS. The tradeoff is that other than with the regular, union and toggle storage options there are no acceleration structures to speed up query evaluation, which means that a query has to evaluate each instance of the component. + +The following sections show how to use queries in the different language bindings. + +
+
    +
  • C + +```c +typedef struct { + ecs_entity_t value; +} Movement; + +// Register 'Movement' component and reflection data +ECS_COMPONENT(world, Movement); + +ecs_struct(world, { + .entity = ecs_id(Movement), + .members = { + { "value", ecs_id(ecs_entity_t) } + } +}); + +// Create two entities for the direction +ecs_entity_t Left = ecs_new(world); +ecs_entity_t Right = ecs_new(world); + +// Create two entities with different directions +ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Left })); +ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Right })); + +// Create query that only matches e1 +ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.name = "Movement.value", .second.id = Left }} +}); +``` + +
  • +
  • C++ + +```cpp +struct Movement { + flecs::entity_t value; +}; + +// Register 'Movement' component and reflection data +world.component() + .member("value", &Movement::value); + +// Create two entities for the direction +flecs::entity Left = world.entity(); +flecs::entity Right = world.entity(); + +// Create two entities with different directions +flecs::entity e1 = world.entity().set(Movement{ Left }); +flecs::entity e2 = world.entity().set(Movement{ Right }); + +// Create query that only matches e1 +flecs::query<> q = world.query() + .with("Movement.value", Left) .build(); ``` -An application can constrain the results of the query by setting the variable before starting iteration: +
  • +
  • Rust + +```rust +// Rust API does not support member value queries until reflection is implemented. This is the Meta addon. +``` + +
  • +
  • Flecs Query Language + +``` +Movement.direction($this, Left) +``` + +
  • +
+
-```cpp -flecs::entity earth = world.entity(); +Member value queries can be used in combination with wildcards: -int32_t location_var = r.find_var("Location"); -r.iter().set_var(location_var, earth).each([]{ - // iterate as usual -}); +``` +Movement.value($this, *) ``` -Alternatively the variable name can be provided to `set_var` directly: - -```cpp -flecs::entity earth = world.entity(); +Member value queries can be used in combination with variables: -r.iter().set_var("Location", earth).each([]{ - // iterate as usual -}); +``` +Movement.value($this, $direction), $direction != Left ``` ### Change Detection -> *Supported by: cached queries* - Change detection makes it possible for applications to know whether data matching a query has changed. Changes are tracked at the table level, for each component in the table. While this is less granular than per entity tracking, the mechanism has minimal overhead, and can be used to skip entities in bulk. Change detection works by storing a list of counters on tracked tables, where each counter tracks changes for a component in the table. When a component in the table changes, the corresponding counter is increased. An additional counter is stored for changes that add or remove entities to the table. Queries with change detection store a copy of the list of counters for each table in the cache, and compare counters to detect changes. To reduce overhead, counters are only tracked for tables matched with queries that use change detection. The change detection feature cannot detect all changes. The following scenarios are detected by change detection: + - Anything that causes adding or removing a component, tag or pair - Deleting an entity - Setting a component value with `set` @@ -2429,7 +3480,8 @@ The change detection feature cannot detect all changes. The following scenarios - A change in tables matched by the query The following scenarios are not detected by change detection: -- Modifying a component obtained by `get_mut` without calling `modified` + +- Modifying a component obtained by `ensure` without calling `modified` - Modifying the value of a ref (`ecs_ref_t` or `flecs::ref`) without calling `modified` A query with change detection enabled will only report a change for the components it matched with, or when an entity got added/removed to a matched table. A change to a component in a matched table that is not matched by the query will not be reported by the query. @@ -2444,19 +3496,22 @@ When a query iterates a table for which changes are tracked and the query has `i The following sections show how to use change detection in the different language bindings. The code examples use cached queries, which is the only kind of query for which change detection is supported. -#### Query Descriptor (C) +
+
    +
  • C + The following example shows how the change detection API is used in C: ```c // Query used for change detection. Note that change detection is not enabled on // the query itself, but by calling change detection functions for the query. -ecs_query_t *q_read = ecs_query(world, { - .filter.terms = {{ .id = ecs_id(Position), .inout = EcsIn }} +ecs_query_cache_t *q_read = ecs_query(world, { + .terms = {{ .id = ecs_id(Position), .inout = EcsIn }} }); // Query used to create changes -ecs_query_t *q_write = ecs_query(world, { - .filter.terms = {{ .id = ecs_id(Position) }} // defaults to inout +ecs_query_cache_t *q_write = ecs_query(world, { + .terms = {{ .id = ecs_id(Position) }} // defaults to inout }); // Test if changes have occurred for anything matching the query. If this is the @@ -2464,7 +3519,7 @@ ecs_query_t *q_write = ecs_query(world, { bool changed = ecs_query_changed(q_read, NULL); // Setting a component will update the changed state -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {10, 20}); // Iterating a query with inout/out terms will update the change state @@ -2473,7 +3528,7 @@ while (ecs_query_next(&it)) { if (dont_change) { // If no changes are made to the iterated table, the skip function can be // called to prevent marking the matched components as dirty. - ecs_query_skip(&it); + ecs_iter_skip(&it); } else { // Iterate as usual. It does not matter whether the code actually writes the // components or not: when a table is not skipped, components matched with @@ -2492,7 +3547,9 @@ while (ecs_query_next(&it)) { } ``` -#### Query Builder (C++) +
  • +
  • C++ + The following example shows how the change detection API is used in C++: ```cpp @@ -2511,30 +3568,81 @@ bool changed = q_read.changed(); flecs::entity e = world.entity() .set({10, 20}); -q_write.iter([](flecs::iter& it, Position *p) { - if (dont_change) { - // If no changes are made to the iterated table, the skip function can be - // called to prevent marking the matched components as dirty. - it.skip(); - } else { - // Iterate as usual. It does not matter whether the code actually writes the - // components or not: when a table is not skipped, components matched with - // inout or out terms will be marked dirty by the iterator. +q_write.run([](flecs::iter& it) { + if (it.next()) { + if !changed { + // If no changes are made to the iterated table, the skip function can be + // called to prevent marking the matched components as dirty. + it.skip(); + } else { + // Iterate as usual. It does not matter whether the code actually writes the + // components or not: when a table is not skipped, components matched with + // inout or out terms will be marked dirty by the iterator. + } } }); -q_read.iter([](flecs::iter& it, Position *p) { - if (it.changed()) { - // Check if the current table has changed. The change state will be reset - // after the table is iterated, so code can respond to changes in individual - // tables. +q_read.run([](flecs::iter& it) { + if (it.next()) { + if (it.changed()) { + // Check if the current table has changed. The change state will be reset + // after the table is iterated, so code can respond to changes in individual + // tables. + } } }); ``` -### Sorting -> *Supported by: cached queries* +
  • +
  • Rust +The following example shows how the change detection API is used in C++: + +```rust +// Query used for change detection. Note that change detection is not enabled on +// the query itself, but by calling change detection functions for the query. +let q_read = world.new_query::<&Position>(); + +// Query used to create changes +let q_write = world.new_query::<&mut Position>(); // defaults to inout + +// Test if changes have occurred for anything matching the query. If this is the +// first call to the function, it will enable change detection for the query. +let changed = q_read.is_changed(); + +// Setting a component will update the changed state +let e = world.entity().set(Position { x: 10.0, y: 20.0 }); + +q_write.run(|mut it| { + while it.next() { + if !changed { + // If no changes are made to the iterated table, the skip function can be + // called to prevent marking the matched components as dirty. + it.skip(); + } else { + // Iterate as usual. It does not matter whether the code actually writes the + // components or not: when a table is not skipped, components matched with + // inout or out terms will be marked dirty by the iterator. + } + } +}); + +q_read.run(|mut it| { + while it.next() { + if it.is_changed() { + // Check if the current table has changed. The change state will be reset + // after the table is iterated, so code can respond to changes in individual + // tables. + } + } +}); +``` + +
  • +
+
+ +### Sorting Sorted queries allow an application to specify a component that entities should be sorted on. Sorting is enabled by setting the `order_by` function in combination with the component to order on. Sorted queries sort the tables they match with when necessary. To determine whether a table needs to be sorted, sorted queries use [change detection](#change-detection). A query determines whether a sort operation is needed when an iterator is created for it. > Because sorting relies on change detection, it has the same limitations with respect to detecting changes. When using sorted queries, make sure a query is able to detect the changes necessary for knowing when to (re)sort. @@ -2574,39 +3682,41 @@ To minimize time spent on sorting, the results of a sort are cached. The perform The following sections show how to use sorting in the different language bindings. The code examples use cached queries, which is the only kind of query for which change detection is supported. -#### Query Descriptor (C) +
+
    +
  • C + The following example shows how to use sorted queries in C: ```c ecs_query_t *q = ecs_query(world, { - .filter.terms = { + .terms = { // Use readonly term for component used for sorting { ecs_id(Depth), .inout = EcsIn } - { ecs_id(Position) }, }, - .order_by_component = ecs_id(Position), // The component to use for sorting - .order_by = compare_position, + .order_by = ecs_id(Position), // The component to use for sorting + .order_by_callback = compare_position, }); ``` The function signature of the `order_by` function should look like the following example: ```c -int compare_position(ecs_entity_t e1, const void *v1, ecs_entity_t e2, const void *v2) { +int compare_depth(ecs_entity_t e1, const void *v1, ecs_entity_t e2, const void *v2) { const Depth *d1 = v1; const Depth *d2 = v2; return (d1->value > d2->value) - (d1->value < d2->value); } ``` -A query may only use entity identifiers to sort by not setting the `order_by_component` member: +A query may only use entity identifiers to sort by not setting the `order_by` member: ```c ecs_query_t *q = ecs_query(world, { - .filter.terms = { + .terms = { { ecs_id(Position) }, }, - .order_by = compare_entity, + .order_by_callback = compare_entity, }); ``` @@ -2618,7 +3728,9 @@ int compare_entity(ecs_entity_t e1, const void *v1, ecs_entity_t e2, const void } ``` -#### Query Builder (C++) +
  • +
  • C++ + The following example shows how to use sorted queries in C++: ```cpp @@ -2636,7 +3748,7 @@ Queries may specify a component id if the component is not known at compile time flecs::entity depth_id = world.component(); auto q = world.query_builder() - .term(depth_id).in() + .with(depth_id).in() .order_by(depth_id, [](flecs::entity_t e1, const void *d1, flecs::entity_t e2, const void *d2) { // Generic sort code ... }) @@ -2653,9 +3765,54 @@ auto q = world.query_builder() .build(); ``` -### Grouping -> *Supported by: cached queries* +
  • +
  • Rust + +The following example shows how to use sorted queries in Rust: + +```rust +// Use readonly term for component used for sorting +let q = world + .query::<(&Depth, &Position)>() + .order_by::(|e1, d1: &Depth, e2, d2: &Depth| { + (d1.value > d2.value) as i32 - (d1.value < d2.value) as i32 + }) + .build(); +``` +Queries may specify a component id if the component is not known at compile time: + +```rust +let depth_id = world.component::(); + +let q = world + .query::<&Position>() + .with_id(depth_id) + .set_in() + .order_by_id(depth_id, |e1, d1: *const c_void, e2, d2: *const c_void| { + let d1 = unsafe { &*(d1 as *const Depth) }; + let d2 = unsafe { &*(d2 as *const Depth) }; + (d1.value > d2.value) as i32 - (d1.value < d2.value) as i32 + }) + .build(); +``` + +Queries may specify zero for component id to sort on entity ids: + +```rust +let q = world + .query::<&Position>() + .order_by_id(0, |e1, _d1: *const c_void, e2, _d2: *const c_void| { + (e1 > e2) as i32 - (e1 < e2) as i32 + }) + .build(); +``` + +
  • +
+
+ +### Grouping Grouping is the ability of queries to assign an id ("group id") to a set of tables. Grouped tables are iterated together, as they are stored together in the query cache. Additionally, groups in the query cache are sorted by group id, which guarantees that tables with a lower group id are iterated after tables with a higher group id. Grouping is only supported for cached queries. Group ids are local to a query, and as a result queries with grouping do not modify the tables they match with. @@ -2675,17 +3832,20 @@ One example is that of an open game which is divided up into world cells. Even t Grouped iterators, when used in combination with a good group_by function are one of the fastest available mechanisms for finding entities in Flecs. The feature provides the iteration performance of having a cached query per group, but without the overhead of having to maintain multiple caches. Whether a group has ten or ten thousand tables does not matter, which makes the feature an enabler for games with large numbers of entities. -The following sections show how to use sorting in the different language bindings. The code examples use cached queries, which is the only kind of query for which change detection is supported. +The following sections show how to use grouping in the different language bindings. The code examples use cached queries, which is the only kind of query for which change detection is supported. + +
+
    +
  • C -#### Query Descriptor (C) The following example shows how grouping can be used to group entities that are in the same game region. ```c -ecs_entity_t Region = ecs_new_id(world); -ecs_entity_t Unit = ecs_new_id(world); +ecs_entity_t Region = ecs_new(world); +ecs_entity_t Unit = ecs_new(world); -ecs_entity_t Region_01 = ecs_new_id(world); -ecs_entity_t Region_02 = ecs_new_id(world); +ecs_entity_t Region_01 = ecs_new(world); +ecs_entity_t Region_02 = ecs_new(world); // Example of entities created in different regions ecs_entity_t unit_01 = ecs_new_w_id(world, Unit); @@ -2696,9 +3856,9 @@ ecs_add_pair(world, unit_02, Region, Region_02); // Create query that groups entities that are in the same region ecs_query(world, { - .filter.terms = {{ Unit }}, - .group_by = group_by_target, // function that groups by relationship target - .group_by_id = Region // optional, passed to group_by function + .terms = {{ Unit }}, + .group_by_callback = group_by_target, // function that groups by relationship target + .group_by = Region // optional, passed to group_by function }); ``` @@ -2718,7 +3878,30 @@ uint64_t group_by_target( } ``` -#### Query Builder (C++) +When no value for `group_by_callback` is provided, it will default to an internal function with the same behavior as `group_by_target`. An example: + +```c +// Create query that groups entities that are in the same region +ecs_query(world, { + .terms = {{ Unit }}, + .group_by = Region +}); +``` + +To iterate entities in a single group, use the `ecs_iter_set_group` function: + +```c +ecs_iter_t it = ecs_query_iter(world, q); +ecs_iter_set_group(&it, Region_01); + +while (ecs_query_next(&it)) { + // iterate as usual +} +``` + +
  • +
  • C++ + The following example shows how grouping can be used to group entities that are in the same game region. ```cpp @@ -2739,7 +3922,7 @@ flecs::entity unit_02 = world.entity() // Create query that groups entities that are in the same region flecs::query<> q = world.query_builder() - .term() + .with() .group_by([]( flecs::world_t *world, flecs::table_t *table, @@ -2759,22 +3942,68 @@ flecs::query<> q = world.query_builder() .build(); ``` -### Component Inheritance -> *Supported by: rules* +When no `group_by` functions, it will default to an internal function with the same behavior as the previous example. An example: + +```cpp +// Create query that groups entities that are in the same region +flecs::query<> q = world.query_builder() + .with() + .group_by() + .build(); +``` + +To iterate entities in a single group, use the `set_group` function: + +```cpp +q.set_group(Region_01).each([](flecs::entity e) { + // iterate as usual +}); +``` + +
  • +
  • Rust + +This section for Rust is unfinished. For code examples, see the group_by examples in the `examples/flecs/queries` folder. Note, this still partially uses the C API. Until the Rust API is updated, this section will remain unfinished. +The following example shows how grouping can be used to group entities that are in the same game region. + +```rust +// see example in examples folder under query/group_by +``` + +When no `group_by` functions, it will default to an internal function with the same behavior as the previous example. An example: + +```rust +// see example in examples folder under query/group_by +``` + +To iterate entities in a single group, use the `set_group` function: + +```rust +// see example in examples folder under query/group_by +``` + +
  • +
+
+ +### Component Inheritance Component inheritance allows for a query to match entities with a component and all subsets of that component, as defined by the `IsA` relationship. Component inheritance is enabled for all queries by default, for components where it applies. It is possible to prevent inheriting from a component from by adding the [Final](Relationships.md#final-property) property. Queries for components with the `Final` property will not attempt to resolve component inheritance. Inheritance relationships can, but are not required to mirror inheritance of the types used as long as it does not impact the layout of the type. Component inheritance is most often used with tags. -The following sections show how to use component inheritance in the different language bindings. The code examples use rules, which is the only kind of query for which component inheritance is currently supported. +The following sections show how to use component inheritance in the different language bindings. -#### Query Descriptor (C) -The following example shows a rule that uses component inheritance to match entities: +
+
    +
  • C + +The following example shows a query that uses component inheritance to match entities: ```c -ecs_entity_t Unit = ecs_new_id(world); +ecs_entity_t Unit = ecs_new(world); ecs_entity_t MeleeUnit = ecs_new_w_pair(world, EcsIsA, Unit); ecs_entity_t RangedUnit = ecs_new_w_pair(world, EcsIsA, Unit); @@ -2782,18 +4011,20 @@ ecs_entity_t unit_01 = ecs_new_w_id(world, MeleeUnit); ecs_entity_t unit_02 = ecs_new_w_id(world, RangedUnit); // Matches entities with Unit, MeleeUnit and RangedUnit -ecs_rule_t *r = ecs_rule(world, { +ecs_query_t *q = ecs_query(world, { .terms = {{ Unit }} }); // Iterate as usual ``` -#### Query Builder (C++) -The following example shows a rule that uses component inheritance to match entities: +
  • +
  • C++ + +The following example shows a query that uses component inheritance to match entities: ```cpp -flecs::entity Unit = ecs_new_id(world); +flecs::entity Unit = ecs_new(world); flecs::entity MeleeUnit = world.entity().is_a(Unit); flecs::entity RangedUnit = world.entity().is_a(Unit); @@ -2801,14 +4032,39 @@ flecs::entity unit_01 = world.entity().add(MeleeUnit); flecs::entity unit_02 = world.entity().add(RangedUnit); // Matches entities with Unit, MeleeUnit and RangedUnit -flecs::rule r = world.rule(); +flecs::query q = world.query(); // Iterate as usual ``` -### Transitive Relationships -> *Supported by: rules* +
  • +
  • Rust +The following example shows a query that uses component inheritance to match entities: + +```rust +#[derive(Component)] +struct Unit; + +let unit = world.component::(); + +let melee_unit = world.entity().is_a::(); +let ranged_unit = world.entity().is_a::(); + +let unit_01 = world.entity().add_id(melee_unit); +let unit_02 = world.entity().add_id(ranged_unit); + +// Matches entities with Unit, MeleeUnit and RangedUnit +let q = world.query::<&Unit>(); + +// Iterate as usual +``` + +
  • +
+
+ +### Transitive Relationships When a [transitive relationship](Relationships.md#transitive-relationships) is used by a query, a query will automatically look up or test against pairs that satisfy the transitive property. Transitivity is usually defined as: > If R(X, Y) and R(Y, Z) then R(X, Z) @@ -2819,22 +4075,27 @@ A relationship can be made transitive by adding the [transitive](Relationships.m An example of a builtin relationship that has the `Transitive` property is the `IsA` relationship. -The following sections show how to use transitive relationships in the different language bindings. The code examples use rules, which is the only kind of query for which transitive relationships are currently supported. +Transitive traversal can be disabled for a term with a transitive relationship by adding the `Self` flag to the second element of the pair. -#### Query Descriptor (C) -The following example shows a rule that uses transitivity to match entities that are located in New York: +The following sections show how to use transitive relationships in the different language bindings. + +
+
    +
  • C + +The following example shows a query that uses transitivity to match entities that are located in New York: ```c // Create LocatedIn relationship with transitive property ecs_entity_t LocatedIn = ecs_new_w_id(world, EcsTransitive); -ecs_entity_t NewYork = ecs_new_id(world); +ecs_entity_t NewYork = ecs_new(world); ecs_entity_t Manhattan = ecs_new_w_pair(world, LocatedIn, NewYork); ecs_entity_t CentralPark = ecs_new_w_pair(world, LocatedIn, Manhattan); ecs_entity_t Bob = ecs_new_w_pair(world, LocatedIn, CentralPark); // Matches ManHattan, CentralPark, Bob -ecs_rule_t *r = ecs_rule(world, { +ecs_query_t *q = ecs_query(world, { .terms = {{ ecs_pair(LocatedIn, NewYork) }} }); @@ -2848,8 +4109,8 @@ Queries for transitive relationships can be compared with variables. This query // - ManHattan (Place = NewYork) // - CentralPark (Place = ManHattan, NewYork) // - Bob (Place = CentralPark, ManHattan, NewYork) -ecs_rule_t *r = ecs_rule(world, { - .terms = {{ .first.id = LocatedIn, .second.name = "Place", .second.flags = EcsIsVariable }} +ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = LocatedIn, .second.name = "$Place" }} }); ``` @@ -2857,23 +4118,25 @@ Variables can be used to constrain the results of a transitive query. The follow ```c // Add City property to NewYork -ecs_entity_t City = ecs_new_id(world); +ecs_entity_t City = ecs_new(world); ecs_add_id(world, NewYork, City); // Matches: // - ManHattan (Place = NewYork) // - CentralPark (Place = NewYork) // - Bob (Place = NewYork) -ecs_rule_t *r = ecs_rule(world, { +ecs_query_t *q = ecs_query(world, { .terms = { - { .first.id = LocatedIn, .second.name = "Place", .second.flags = EcsIsVariable }, - { .id = City, .src.name = "Place", .src.flags = EcsIsVariable } + { .first.id = LocatedIn, .second.name = "$Place" }, + { .id = City, .src.name = "$Place" } } }); ``` -#### Query Builder (C++) -The following example shows a rule that uses transitivity to match entities that are located in New York: +
  • +
  • C++ + +The following example shows a query that uses transitivity to match entities that are located in New York: ```cpp // Create LocatedIn relationship with transitive property @@ -2887,8 +4150,8 @@ flecs::entity CentralPark = world.entity().add(Manhattan); flecs::entity Bob = world.entity().add(CentralPark); // Matches ManHattan, CentralPark, Bob -flecs::rule<> r = world.rule_builder() - .term(NewYork) +flecs::query<> q = world.query_builder() + .with(NewYork) .build(); // Iterate as usual @@ -2901,8 +4164,8 @@ Queries for transitive relationships can be compared with variables. This query // - ManHattan (Place = NewYork) // - CentralPark (Place = ManHattan, NewYork) // - Bob (Place = CentralPark, ManHattan, NewYork) -flecs::rule<> r = world.rule_builder() - .term().second("$Place") +flecs::query<> q = world.query_builder() + .with().second("$Place") .build(); ``` @@ -2918,13 +4181,79 @@ NewYork.add(City); // - ManHattan (Place = NewYork) // - CentralPark (Place = NewYork) // - Bob (Place = NewYork) -flecs::rule<> r = world.rule_builder() - .term().second("$Place") - .term().src("$Place") +flecs::query<> q = world.query_builder() + .with().second("$Place") + .with().src("$Place") .build(); ``` -#### Query DSL +
  • + +
  • Rust + +The following example shows a query that uses transitivity to match entities that are located in New York: + +```rust +// Create LocatedIn relationship with transitive property +#[derive(Component)] +struct LocatedIn; + +world.component::().add::(); + +let new_york = world.entity(); +let manhattan = world.entity().add_first::(new_york); +let central_park = world.entity().add_first::(manhattan); +let bob = world.entity().add_first::(central_park); + +// Matches ManHattan, CentralPark, Bob +let q = world + .query::<()>() + .with_first::(new_york) + .build(); + +// Iterate as usual +``` + +Queries for transitive relationships can be compared with variables. This query returns all locations an entity is in: + +```rust +// Matches: +// - ManHattan (Place = NewYork) +// - CentralPark (Place = ManHattan, NewYork) +// - Bob (Place = CentralPark, ManHattan, NewYork) +let q = world + .query::<()>() + .with::() + .set_second_name("$Place") + .build(); +``` + +Variables can be used to constrain the results of a transitive query. The following query returns locations an entity is in that are a city: + +```rust +#[derive(Component)] +struct City; + +// Add City property to NewYork +new_york.add::(); + +// Matches: +// - ManHattan (Place = NewYork) +// - CentralPark (Place = NewYork) +// - Bob (Place = NewYork) + +let q = world + .query::<()>() + .with::() + .set_second_name("$Place") + .with::() + .set_src_name("$Place") + .build(); +``` + +
  • +
  • Flecs Query Language + Transitivity in a query is enabled by adding the `Transitive` property to a relationship. As a result, a query for a transitive relationship looks the same as a query for a non-transitive relationship. The following examples show the queries used in the C/C++ examples: Match all entities located in New York: @@ -2945,174 +4274,77 @@ Return the city entities are in: (LocatedIn, $Place), City($Place) ``` -### Reflexive Relationships -> *Supported by: rules* +
  • +
+
+### Reflexive Relationships When a query matches a [reflexive](Relationships.md#reflexive-property) relationship, a query term will evaluate to true if the source and target are equal. Reflexivity can be defined as: > R(X, X) For example, if relationship IsA (R) is reflexive, then a Tree (X) is a Tree (X). An example of a builtin relationship that has the `Reflexive` property is the `IsA` relationship. -The following sections show how to use transitive relationships in the different language bindings. The code examples use rules, which is the only kind of query for which transitive relationships are currently supported. +The following sections show how to use transitive relationships in the different language bindings. + +
+
    +
  • C -#### Query Descriptor (C) -The following example shows a rule that uses the `IsA` reflexive relationship: +The following example shows a query that uses the `IsA` reflexive relationship: ```c -ecs_entity_t Tree = ecs_new_id(world); +ecs_entity_t Tree = ecs_new(world); ecs_entity_t Oak = ecs_new_w_pair(world, EcsIsA, Tree); // Matches Tree, Oak -ecs_rule_t *r = ecs_rule(world, { +ecs_query_t *q = ecs_query(world, { .terms = {{ ecs_pair(EcsIsA, Tree )}} }); ``` -#### Query Builder (C++) -The following example shows a rule that uses the `IsA` reflexive relationship: +
  • +
  • C++ + +The following example shows a query that uses the `IsA` reflexive relationship: ```cpp flecs::entity Tree = world.entity(); flecs::entity Oak = world.entity().is_a(Tree); // Matches Tree, Oak -flecs::rule<> r = world.rule_builder() - .term(flecs::IsA, Tree) +flecs::query<> q = world.query_builder() + .with(flecs::IsA, Tree) .build(); ``` -#### Query DSL -Reflexivity in a query is enabled by adding the `Reflexive` property to a relationship. As a result, a query for a reflexive relationship looks the same as a query for a non-reflexive relationship. The following example shows the query used in the C/C++ examples: - -``` -(IsA, Tree) -``` - -## Performance -This section describes performance characteristics for each query type. - -### Filter -A filter is a data type that describes the structure of a query in Flecs. Filters are the cheapest to create, as creating one just requires initializing the value of the filter object. - -Filters can serve different purposes, like iterating all matching entities, or testing if a specific entity matches with a filter. Filters can also be used to filter the output of other queries, including other filters. - -While the exact way a filter is evaluated depends on what kind of filter it is, almost all filter evaluation follows steps similar to the ones in this diagram: - -![filter diagram](img/filter_diagram.png) - -Each node in the diagram represents a function that can return either true or false. When a node returns true, evaluation moves on to the next node. When it returns false, evaluation goes back one node. These kinds of functions are called [predicates](https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)), and this evaluation process is called [backtracking](https://en.wikipedia.org/wiki/Backtracking). - -Whether a node returns true or false depends on what its function does: - -- The first node invokes the function `select(Position)`. This function finds all *tables* with `Position`, and will forward these tables to the next node. As long as `select` is able to find more tables with `Position`, the node will keep returning true. When no more tables can be found the node returns false. - -- The second node invokes the function `with(Velocity)`. This function takes the table found by the previous node and returns true if the table has `Velocity`, and false if it does not. - -Both functions are O(1) operations. - -When a table reaches the `yield` node it means that it matched all parts of the filter, and it will be returned by the iterator doing the evaluation. - -> A table groups all entities that have _exactly_ the same components. Thus if one entity in a table matches a node, all entities in the table match the node. This is one of the main reasons queries are fast: instead of checking each individual entity for components, we can eliminate a table with thousands of entities in a single operation. +
  • +
  • Rust -Because filters are fast to create, have low overhead, and are reasonably efficient to iterate, they are the goto solution for when an application cannot know in advance what it needs to query for, like finding all children for a specific entity: +The following example shows a query that uses the `IsA` reflexive relationship: -```c -ecs_filter_f fs = ECS_FILTER_INIT; -ecs_filter_t *f = ecs_filter(world, { - .storage = &fs, // optional, but prevents allocation - .terms = {{ ecs_childof(e) }} -}); - -ecs_iter_t child_it = ecs_filter_iter(f); -while (ecs_filter_next(&child_it)) { - for (int c = 0; c < child_it.count; c ++) { - printf("child %s\n", ecs_get_name(world, - child_it.entities[c])); - } -} +```rust +let tree = world.entity(); +let oak = world.entity().is_a_id(tree); -ecs_filter_fini(f); -``` -```cpp -auto f = world.filter_builder() - .term(flecs::ChildOf, e) - .build(); +// Matches Tree, Oak +let q = world +.query::<()>() +.with_first::(tree) +.build(); -f.each([](flecs::entity child) { - std::cout << child.path().str() << "\n"; -}); +// Iterate as usual ``` -While filter evaluation for a single component is very fast, each additional component adds some overhead. This is not just because of the time it takes to do an additional check, but because more tables will get discarded during evaluation. The cost of discarded tables adds up as they pass through one or more nodes before getting rejected, consuming resources while not contributing to the query result. - -This cost can be particularly significant for filters that match a large (5-10) number of components in applications with many (>1000s) tables. While Flecs implements several strategies to limit this overhead, like storing empty tables separately from non-empty tables, this is something to keep in mind when using filters. - -### Cached Queries -A cached query is an object that caches the output of a filter. Cached queries are significantly faster to iterate than a filter, as an iterator just needs to walk the list of cache entries and return each one to the application. - -This also makes the performance of cached queries more predictable. The overhead per returned table for a filter depends on how many components it matches with, and how many tables it had to discard. For a cached query the cost per table is much lower, and is constant time. - -> [A benchmark showing the difference between cached queries and filters](https://twitter.com/ajmmertens/status/1509473999205507080). - -This predictable behavior is why cached queries are the default for Flecs systems. Once tables are created and caches are initialized, the (often time critical) main loop of an application does not have to spend time matching entities with systems, which frees up resources for application logic. +
  • +
  • Flecs Query Language -Another benefit of cached queries is that they are compatible with good code practices. Cached queries add minimal overhead to systems, which means applications do not have to compromise on design where the cost of evaluating a query has to be weighed against the benefit of having many small decoupled systems. - -The following example shows how cached queries are used: - -```c -// Creates query, populates the cache with existing tables -ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_id(Position) }, { ecs_id(Velocity) } - } -}); - -// When a new table is created that matches the query, it is -// added to the cache -ecs_entity_t e = ecs_new_id(world); -ecs_add(world, e, Position); -ecs_add(world, e, Velocity); - -// Iterate the tables in the cache -ecs_iter_t it = ecs_query_iter(world, q); -while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); +Reflexivity in a query is enabled by adding the `Reflexive` property to a relationship. As a result, a query for a reflexive relationship looks the same as a query for a non-reflexive relationship. The following example shows the query used in the C/C++ examples: - for (int i = 0; i < it.count; i ++) { - p[i].x += v[i].x; - p[i].y += v[i].y; - } -} ``` -```cpp -// Creates query, populates the cache with existing tables -auto q = world.query(); - -// When a new table is created that matches the query, it is -// added to the cache -auto e = world.entity() - .add() - .add(); - -// Iterate the tables in the cache -q.each([](Position& p, Velocity& v) { - p.x += v.x; - p.y += v.y; -}); +(IsA, Tree) ``` -The scenarios described so far are best case scenarios for cached queries, where their performance is amongst the fastest of any ECS implementation. To build games that perform well however, it also helps to know when cached queries perform badly: - -- Cached queries do not perform well when they are repeatedly created and destroyed in loops, or when they are only used a handful of times. The overhead of initializing the query cache and the cost of keeping it up to date could in those cases be higher than using a filter. - -- The cost of table creation/deletion increases with the number of queries in the world. Queries use observers to get notified of new tables. That means that for each table event, the number of queries notified is the number of queries with at least one component in common with the table. - -- ECS operations can cause matched tables to no longer match. A simple example is that of a query matching `Position` on a parent entity, where the component is removed from the parent. This triggers cache revalidation, where a query reevaluates its filter to correct invalid entries. When this happens for a large number of queries and tables, this can be time consuming. - -- Performance can degrade when a query needs to traverse a deep (>50 levels) hierarchy in order to determine if a table matches. If a hierarchy has deeply nested children and a parent component can only be found on the root, a query will have to traverse this tree likely for multiple tables, which is expensive. - - - +
  • +
+
diff --git a/vendors/flecs/docs/Quickstart.md b/vendors/flecs/docs/Quickstart.md index 3a5420988..76137696e 100644 --- a/vendors/flecs/docs/Quickstart.md +++ b/vendors/flecs/docs/Quickstart.md @@ -2,9 +2,9 @@ This document provides a quick overview of the different features and concepts in Flecs with short examples. This is a good resource if you're just getting started or just want to get a better idea of what kind of features are available in Flecs! ## Building Flecs -To use Flecs, copy the [flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c) and [flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.h) files from the repository root to your project's source folder. When building, make sure your build system is setup to do the following: +To use Flecs, copy the [distr/flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.c) and [distr/flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.h) files from the [distr folder](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr) to your project's source folder. When building, make sure your build system is setup to do the following: -- If it is a C++ project, make sure to compile [flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c) as C code, for example by using `gcc`/`clang` instead of `g++`/`clang++`. +- If it is a C++ project, make sure to compile [distr/flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.c) as C code, for example by using `gcc`/`clang` instead of `g++`/`clang++`. - If you are building on Windows and you're not using the Microsoft Visual Studio compiler, make sure to add `-lWs2_32` to **the end(!)** of the linker command. The socket API is used for connecting to Flecs explorer. @@ -13,13 +13,13 @@ To use Flecs, copy the [flecs.c](https://raw.githubusercontent.com/SanderMertens - C++ files that use Flecs must be compiled with `-std=c++0x` (C++11) or higher. ### Dynamic linking -To build Flecs as a dynamic library, remove this line from the top of the [flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.h) file: +To build Flecs as a dynamic library, remove this line from the top of the [distr/flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.h) file: ```c #define flecs_STATIC ``` -When compiling [flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c), make sure to define `flecs_EXPORTS`, for example by adding `-Dflecs_EXPORTS` to the compiler command. +When compiling [distr/flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/distr/flecs.c), make sure to define `flecs_EXPORTS`, for example by adding `-Dflecs_EXPORTS` to the compiler command. Alternatively Flecs can also be built as a dynamic library with the cmake, meson, bazel or [bake](https://github.com/SanderMertens/bake) build files provided in the repository. These use the files from `src` to build as opposed to the amalgamated files, which is better suited for development. @@ -46,7 +46,7 @@ Run the following commands to run all tests (use `-j` to specify the number of t ```bash # Core test suite -bake run test/api -- -j 4 +bake run test/core -- -j 4 # Addon tests bake run test/addons -- -j 4 @@ -55,7 +55,7 @@ bake run test/addons -- -j 4 bake run test/meta -- -j 4 # C++ tests -bake run test/cpp_api -- -j 4 +bake run test/cpp -- -j 4 ``` To run tests with asan enabled, add `--cfg sanitize` to the command: @@ -110,24 +110,17 @@ Addon | Description | Define --------------|--------------------------------------------------|---------------------| [Cpp](/flecs/group__cpp.html) | C++11 API | FLECS_CPP | [Module](/flecs/group__c__addons__module.html) | Organize game logic into reusable modules | FLECS_MODULE | -[System](flecs/group__c__addons__system.html) | Create & run systems | FLECS_SYSTEM | +[System](/flecs/group__c__addons__system.html) | Create & run systems | FLECS_SYSTEM | [Pipeline](/flecs/group__c__addons__pipeline.html) | Automatically schedule & multithread systems | FLECS_PIPELINE | [Timer](/flecs/group__c__addons__timer.html) | Run systems at time intervals or at a rate | FLECS_TIMER | [Meta](/flecs/group__c__addons__meta.html) | Flecs reflection system | FLECS_META | -[Meta_C](/flecs/group__c__addons__meta__c.html) | (C) Utilities for auto-inserting reflection data | FLECS_META_C | [Units](/flecs/group__c__addons__units.html) | Builtin unit types | FLECS_UNITS | -[Expr](/flecs/group__c__addons__expr.html) | String format optimized for ECS data | FLECS_EXPR | [JSON](/flecs/group__c__addons__json.html) | JSON format | FLECS_JSON | [Doc](/flecs/group__c__addons__doc.html) | Add documentation to components, systems & more | FLECS_DOC | -[Coredoc](/flecs/group__c__addons__coredoc.html) | Documentation for builtin components & modules | FLECS_COREDOC | [Http](/flecs/group__c__addons__http.html) | Tiny HTTP server for processing simple requests | FLECS_HTTP | [Rest](/flecs/group__c__addons__rest.html) | REST API for showing entities in the browser | FLECS_REST | -[Parser](/flecs/group__c__addons__parser.html) | Create entities & queries from strings | FLECS_PARSER | -[Plecs](/flecs/group__c__addons__plecs.html) | Small utility language for asset/scene loading | FLECS_PLECS | -[Rules](/flecs/group__c__addons__rules.html) | Powerful prolog-like query language | FLECS_RULES | -[Snapshot](/flecs/group__c__addons__snapshot.html) | Take snapshots of the world & restore them | FLECS_SNAPSHOT | +[Script](/flecs/group__c__addons__script.html) | DSL for assets, scenes and configuration | FLECS_SCRIPT | [Stats](/flecs/group__c__addons__stats.html) | Functions for collecting statistics | FLECS_STATS | -[Monitor](/flecs/group__c__addons__monitor.html) | Periodically collect & store flecs statistics | FLECS_MONITOR | [Metrics](/flecs/group__c__addons__metrics.html) | Create metrics from user-defined components | FLECS_METRICS | [Alerts](/flecs/group__c__addons__alerts.html) | Create alerts from user-defined queries | FLECS_ALERTS | [Log](/flecs/group__c__addons__log.html) | Extended tracing and error logging | FLECS_LOG | @@ -143,6 +136,10 @@ This section contains an overview of all the different concepts in Flecs and how ### World The world is the container for all ECS data. It stores the entities and their components, does queries and runs systems. Typically there is only a single world, but there is no limit on the number of worlds an application can create. +
+
    +
  • C + ```c ecs_world_t *world = ecs_init(); @@ -150,22 +147,50 @@ ecs_world_t *world = ecs_init(); ecs_fini(world); ``` +
  • +
  • C++ + ```cpp flecs::world world; // Do the ECS stuff ``` +
  • +
  • C# + +```cs +using World world = World.Create(); + +// Do the ECS stuff +``` +
  • +
  • Rust + +```rust +let world = World::new(); + +// Do the ECS stuff +``` +
  • +
+
### Entity An entity is a unique thing in the world, and is represented by a 64 bit id. Entities can be created and deleted. If an entity is deleted it is no longer considered "alive". A world can contain up to 4 billion(!) alive entities. Entity identifiers contain a few bits that make it possible to check whether an entity is alive or not. +
+
    +
  • C ```c -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_is_alive(world, e); // true! ecs_delete(world, e); ecs_is_alive(world, e); // false! ``` +
  • +
  • C++ + ```cpp auto e = world.entity(); e.is_alive(); // true! @@ -173,28 +198,98 @@ e.is_alive(); // true! e.destruct(); e.is_alive(); // false! ``` +
  • +
  • C# -Entities can have names which makes it easier to identify them in an application. In C++ the name can be passed to the constructor. In C a name can be assigned with the `ecs_entity_init` function/`ecs_entity` macro. If a name is provided during entity creation time and an entity with that name already exists, the existing entity will be returned. +```cs +Entity e = world.Entity(); +e.IsAlive(); // true! +e.Destruct(); +e.IsAlive(); // false! +``` +
  • +
  • Rust + +```rust +let e = world.entity(); +e.is_alive(); // true! + +e.destruct(); +e.is_alive(); // false! +``` +
  • +
+
+ +Entities can have names which makes it easier to identify them in an application. In C++ the name can be passed to the constructor. If a name is provided during entity creation time and an entity with that name already exists, the existing entity will be returned. +
+
    +
  • C + +In C a name can be assigned with the `ecs_entity_init` function or `ecs_entity` macro. ```c ecs_entity_t e = ecs_entity(world, { .name = "Bob" }); printf("Entity name: %s\n", ecs_get_name(world, e)); ``` +
  • +
  • C++ + ```cpp auto e = world.entity("Bob"); std::cout << "Entity name: " << e.name() << std::endl; ``` +
  • +
  • C# + +```cs +Entity e = world.Entity("Bob"); + +Console.WriteLine($"Entity name: {e.Name()}"); +``` +
  • +
  • Rust + +```rust +let e = world.entity_named("bob"); + +println!("Entity name: {}", e.name()); +``` +
  • +
+
Entities can be looked up by name with the `lookup` function: +
+
    +
  • C ```c ecs_entity_t e = ecs_lookup(world, "Bob"); ``` +
  • +
  • C++ + ```cpp auto e = world.lookup("Bob"); ``` +
  • +
  • C# + +```cs +Entity e = world.Lookup("Bob"); +``` +
  • +
  • Rust + +```rust +let e = world.lookup("bob"); +``` +
  • +
+
### Id An id is a 64 bit number that can encode anything that can be added to an entity. In flecs this can be either a component, tag or a pair. A component is data that can be added to an entity. A tag is an "empty" component. A pair is a combination of two component/tag ids which is used to encode entity relationships. All entity/component/tag identifiers are valid ids, but not all ids are valid entity identifier. @@ -202,13 +297,16 @@ An id is a 64 bit number that can encode anything that can be added to an entity The following sections describe components, tags and pairs in more detail. ### Component -A component is a type of which instances can be added and removed to entities. Each component can be added only once to an entity (though not really, see [Pair](#pair)). In C applications components must be registered before use. In C++ this happens automatically. +A component is a type of which instances can be added and removed to entities. Each component can be added only once to an entity (though not really, see [Pair](#pair)). In C applications components must be registered before use. By default in C++ this happens automatically. +
+
    +
  • C ```c ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); // Add a component. This creates the component in the ECS storage, but does not // assign it with a value. @@ -225,6 +323,9 @@ const Position *p = ecs_get(world, e, Position); // Remove component ecs_remove(world, e, Position); ``` +
  • +
  • C++ + ```cpp auto e = world.entity(); @@ -243,9 +344,60 @@ const Position *p = e.get(); // Remove component e.remove(); ``` +
  • +
  • C# + +```cs +Entity e = world.Entity(); + +// Add a component. This creates the component in the ECS storage, but does not +// assign it with a value. +e.Add(); + +// Set the value for the Position & Velocity components. A component will be +// added if the entity doesn't have it yet. +e.Set(new(10, 20)) + .Set(new(1, 2)); + +// Get a component +ref readonly Position p = ref e.Get(); + +// Remove component +e.Remove(); +``` +
  • +
  • Rust + +```rust +let e = world.entity(); + +// Add a component. This creates the component in the ECS storage, but does not +// assign it with a value. +e.add::(); + +// Set the value for the Position & Velocity components. A component will be +// added if the entity doesn't have it yet. +e.set(Position { x: 10.0, y: 20.0 }) + .set(Velocity { x: 1.0, y: 2.0 }); + +// Get a component +e.get::<&Position>(|p| { + println!("Position: ({}, {})", p.x, p.y); +}); + +// Remove component +e.remove::(); +``` +
  • +
+
-Each component is associated by a unique entity identifier by Flecs. This makes it possible to inspect component data, or attach your own data to components. C applications can use the `ecs_id` macro to get the entity id for a component. C++ applications can use the `world::id` function: +Each component is associated by a unique entity identifier by Flecs. This makes it possible to inspect component data, or attach your own data to components. +
+
    +
  • C +C applications can use the `ecs_id` macro to get the entity id for a component. ```c ECS_COMPONENT(world, Position); @@ -255,6 +407,10 @@ printf("Name: %s\n", ecs_get_name(world, pos_e)); // outputs 'Name: Position' // It's possible to add components like you would for any entity ecs_add(world, pos_e, Serializable); ``` +
  • +
  • C++ + +C++ applications can use the `world::entity` function. ```cpp flecs::entity pos_e = world.entity(); std::cout << "Name: " << pos_e.name() << std::endl; // outputs 'Name: Position' @@ -262,8 +418,37 @@ std::cout << "Name: " << pos_e.name() << std::endl; // outputs 'Name: Position' // It's possible to add components like you would for any entity pos_e.add(); ``` +
  • +
  • C# + +C# applications can use the `World.Entity()` function. +```cs +Entity posE = world.Entity(); +Console.WriteLine($"Name: {posE.Name()}"); // outputs 'Name: Position' + +// It's possible to add components like you would for any entity +posE.Add(); +``` +
  • +
  • Rust + +Rust applications can use the `world::entity_from` function. +```rust +let pos_e = world.entity_from::(); + +println!("Name: {}", pos_e.name()); // outputs 'Name: Position' + +// It's possible to add components like you would for any entity +pos_e.add::(); +``` +
  • +
+
The thing that makes an ordinary entity a component is the `EcsComponent` (or `flecs::Component`, in C++) component. This is a builtin component that tells Flecs how much space is needed to store a component, and can be inspected by applications: +
+
    +
  • C ```c ECS_COMPONENT(world, Position); @@ -273,24 +458,52 @@ ecs_entity_t pos_e = ecs_id(Position); const EcsComponent *c = ecs_get(world, pos_e, EcsComponent); printf("Component size: %u\n", c->size); ``` +
  • +
  • C++ + ```cpp flecs::entity pos_e = world.entity(); const EcsComponent *c = pos_e.get(); std::cout << "Component size: " << c->size << std::endl; ``` +
  • +
  • C# + +```cs +Entity posE = world.Entity(); + +ref readonly EcsComponent c = ref posE.Get(); +Console.WriteLine($"Component size: {c.size}"); +``` +
  • +
  • Rust + +```rust +let pos_e = world.entity_from::(); + +pos_e.get::<&flecs::Component>(|c| { + println!("Component size: {}", c.size); +}); +``` +
  • +
+
Because components are stored as regular entities, they can in theory also be deleted. To prevent unexpected accidents however, by default components are registered with a tag that prevents them from being deleted. If this tag were to be removed, deleting a component would cause it to be removed from all entities. For more information on these policies, see [Relationship cleanup properties](Relationships.md#cleanup-properties). ### Tag A tag is a component that does not have any data. In Flecs tags can be either empty types (in C++) or regular entities (C & C++) that do not have the `EcsComponent` component (or have an `EcsComponent` component with size 0). Tags can be added & removed using the same APIs as adding & removing components, but because tags have no data, they cannot be assigned a value. Because tags (like components) are regular entities, they can be created & deleted at runtime. +
+
    +
  • C ```c // Create Enemy tag -ecs_entity_t Enemy = ecs_new_id(world); +ecs_entity_t Enemy = ecs_new(world); // Create entity, add Enemy tag -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, Enemy); ecs_has_id(world, e, Enemy); // true! @@ -298,6 +511,9 @@ ecs_has_id(world, e, Enemy); // true! ecs_remove_id(world, e, Enemy); ecs_has_id(world, e, Enemy); // false! ``` +
  • +
  • C++ + ```cpp // Option 1: create Tag as empty struct struct Enemy { }; @@ -320,6 +536,59 @@ e.has(Enemy); // true! e.remove(Enemy); e.has(Enemy); // false! ``` +
  • +
  • C# + +```cs +// Option 1: create Tag as empty struct +public struct Enemy { } + +// Create entity, add Enemy tag +Entity e = world.Entity().Add(); +e.Has(); // true! + +e.Remove(); +e.Has(); // false! + + +// Option 2: create Tag as entity +Entity Enemy = world.Entity(); + +// Create entity, add Enemy tag +Entity e = world.Entity().Add(Enemy); +e.Has(Enemy); // true! + +e.Remove(Enemy); +e.Has(Enemy); // false! +``` +
  • +
  • Rust + +```rust +// Option 1: create Tag as empty struct +#[derive(Component)] +struct Enemy; + +// Create entity, add Enemy tag +let e = world.entity().add::(); +e.has::(); // true! + +e.remove::(); +e.has::(); // false! + +// Option 2: create Tag as entity +let enemy = world.entity(); + +// Create entity, add Enemy tag +let e = world.entity().add_id(enemy); +e.has_id(enemy); // true! + +e.remove_id(enemy); +e.has_id(enemy); // false! +``` +
  • +
+
Note that both options in the C++ example achieve the same effect. The only difference is that in option 1 the tag is fixed at compile time, whereas in option 2 the tag can be created dynamically at runtime. @@ -327,14 +596,17 @@ When a tag is deleted, the same rules apply as for components (see [Relationship ### Pair A pair is a combination of two entity ids. Pairs can be used to store entity relationships, where the first id represents the relationship kind and the second id represents the relationship target (called "object"). This is best explained by an example: +
+
    +
  • C ```c // Create Likes relationship -ecs_entity_t Likes = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); // Create a small graph with two entities that like each other -ecs_entity_t Bob = ecs_new_id(world); -ecs_entity_t Alice = ecs_new_id(world); +ecs_entity_t Bob = ecs_new(world); +ecs_entity_t Alice = ecs_new(world); ecs_add_pair(world, Bob, Likes, Alice); // Bob likes Alice ecs_add_pair(world, Alice, Likes, Bob); // Alice likes Bob @@ -343,6 +615,9 @@ ecs_has_pair(world, Bob, Likes, Alice); // true! ecs_remove_pair(world, Bob, Likes, Alice); ecs_has_pair(world, Bob, Likes, Alice); // false! ``` +
  • +
  • C++ + ```cpp // Create Likes relationship as empty type (tag) struct Likes { }; @@ -358,17 +633,81 @@ Bob.has(Alice); // true! Bob.remove(Alice); Bob.has(Alice); // false! ``` +
  • +
  • C# + +```cs +// Create Likes relationship as empty type (tag) +public struct Likes { } + +// Create a small graph with two entities that like each other +Entity Bob = world.Entity(); +Entity Alice = world.Entity(); + +Bob.Add(Alice); // Bob likes Alice +Alice.Add(Bob); // Alice likes Bob +Bob.Has(Alice); // true! + +Bob.Remove(Alice); +Bob.Has(Alice); // false! +``` +
  • +
  • Rust + +```rust +// Create Likes relationship as empty type (tag) +#[derive(Component)] +struct Likes; + +// Create a small graph with two entities that like each other +let bob = world.entity(); +let alice = world.entity(); + +bob.add_first::(alice); // bob likes alice +alice.add_first::(bob); // alice likes bob +bob.has_first::(alice); // true! + +bob.remove_first::(alice); +bob.has_first::(alice); // false! +``` +
  • +
+
A pair can be encoded in a single 64 bit identifier by using the `ecs_pair` macro in C, or the `world.pair` function in C++: +
+
    +
  • C ```c ecs_id_t id = ecs_pair(Likes, Bob); ``` +
  • +
  • C++ + ```cpp flecs::id id = world.pair(Bob); ``` +
  • +
  • C# + +```cs +Id id = world.Pair(bob); +``` +
  • +
  • Rust + +```rust +let id = world.id_first::(bob); +``` +
  • +
+
The following examples show how to get back the elements from a pair: +
+
    +
  • C ```c if (ecs_id_is_pair(id)) { @@ -376,14 +715,45 @@ if (ecs_id_is_pair(id)) { ecs_entity_t target = ecs_pair_second(world, id); } ``` +
  • +
  • C++ + ```cpp +flecs::id id = ...; if (id.is_pair()) { auto relationship = id.first(); auto target = id.second(); } ``` +
  • +
  • C# + +```cs +Id id = ...; +if (id.IsPair()) +{ + Entity relationship = id.First(); + Entity target = id.Second(); +} +``` +
  • +
  • Rust + +```rust +let id = world.id_from::<(Likes, Apples)>(); +if id.is_pair() { + let relationship = id.first_id(); + let target = id.second_id(); +} +``` +
  • +
+
A component or tag can be added multiple times to the same entity as long as it is part of a pair, and the pair itself is unique: +
+
    +
  • C ```c ecs_add_pair(world, Bob, Eats, Apples); @@ -394,32 +764,92 @@ ecs_has_pair(world, Bob, Eats, Apples); // true! ecs_has_pair(world, Bob, Eats, Pears); // true! ecs_has_pair(world, Bob, Grows, Pears); // true! ``` +
  • +
  • C++ + ```cpp -Bob.add(Eats, Apples); -Bob.add(Eats, Pears); -Bob.add(Grows, Pears); +flecs::entity bob = ...; +bob.add(Eats, Apples); +bob.add(Eats, Pears); +bob.add(Grows, Pears); + +bob.has(Eats, Apples); // true! +bob.has(Eats, Pears); // true! +bob.has(Grows, Pears); // true! +``` +
  • +
  • C# + +```cs +Entity Bob = ...; +Bob.Add(Eats, Apples); +Bob.Add(Eats, Pears); +Bob.Add(Grows, Pears); + +Bob.Has(Eats, Apples); // true! +Bob.Has(Eats, Pears); // true! +Bob.Has(Grows, Pears); // true! +``` +
  • +
  • Rust + +```rust +let bob = world.entity(); +bob.add_id((eats, apples)); +bob.add_id((eats, pears)); +bob.add_id((grows, pears)); -Bob.has(Eats, Apples); // true! -Bob.has(Eats, Pears); // true! -Bob.has(Grows, Pears); // true! +bob.has_id((eats, apples)); // true! +bob.has_id((eats, pears)); // true! +bob.has_id((grows, pears)); // true! ``` +
  • +
+
The `target` function can be used in C and C++ to get the object for a relationship: +
+
    +
  • C ```c ecs_entity_t o = ecs_get_target(world, Alice, Likes, 0); // Returns Bob ``` +
  • +
  • C++ + ```cpp -auto o = Alice.target(); // Returns Bob +flecs::entity alice = ...; +auto o = alice.target(); // Returns Bob ``` +
  • +
  • C# + +```cs +Entity Alice = ...; +Entity o = Alice.Target(); // Returns Bob +``` +
  • +
  • Rust + +```rust +let alice = world.entity().add_first::(bob); +let o = alice.target::(0); // Returns bob +``` +
  • +
+
Entity relationships enable lots of interesting patterns and possibilities. Make sure to check out the [Relationships manual](Relationships.md). ### Hierarchies Flecs has builtin support for hierarchies with the builtin `EcsChildOf` (or `flecs::ChildOf`, in C++) relationship. A hierarchy can be created with the regular relationship API, or with the `child_of` shortcut in C++: +
+
    +
  • C ```c -ecs_entity_t parent = ecs_new_id(world); +ecs_entity_t parent = ecs_new(world); // ecs_new_w_pair is the same as ecs_new_id + ecs_add_pair ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -427,6 +857,9 @@ ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); // Deleting the parent also deletes its children ecs_delete(world, parent); ``` +
  • +
  • C++ + ```cpp auto parent = world.entity(); auto child = world.entity().child_of(parent); @@ -434,8 +867,34 @@ auto child = world.entity().child_of(parent); // Deleting the parent also deletes its children parent.destruct(); ``` +
  • +
  • C# + +```cs +Entity parent = world.Entity(); +Entity child = world.Entity().ChildOf(parent); + +// Deleting the parent also deletes its children +parent.Destruct(); +``` +
  • +
  • Rust + +```rust +let parent = world.entity(); +let child = world.entity().child_of_id(parent); + +// Deleting the parent also deletes its children +parent.destruct(); +``` +
  • +
+
When entities have names, they can be used together with hierarchies to generate path names or do relative lookups: +
+
    +
  • C ```c ecs_entity_t parent = ecs_entity(world, { @@ -448,13 +907,16 @@ ecs_entity_t child = ecs_entity(world, { ecs_add_pair(world, child, EcsChildOf, parent); -char *path = ecs_get_fullpath(world, child); +char *path = ecs_get_path(world, child); printf("%s\n", path); // output: 'parent.child' ecs_os_free(path); -ecs_lookup_path(world, 0, "parent.child"); // returns child -ecs_lookup_path(world, parent, "child"); // returns child +ecs_lookup(world, "parent.child"); // returns child +ecs_lookup_from(world, parent, "child"); // returns child ``` +
  • +
  • C++ + ```cpp auto parent = world.entity("parent"); auto child = world.entity("child").child_of(parent); @@ -463,12 +925,41 @@ std::cout << child.path() << std::endl; // output: 'parent::child' world.lookup("parent::child"); // returns child parent.lookup("child"); // returns child ``` +
  • +
  • C# + +```cs +Entity parent = world.Entity("parent"); +Entity child = world.Entity("child").ChildOf(parent); +Console.WriteLine(child.Path()); // output: 'parent.child' + +world.Lookup("parent.child"); // returns child +parent.Lookup("child"); // returns child +``` +
  • +
  • Rust + +```rust +let parent = world.entity_named("parent"); +let child = world.entity_named("child").child_of_id(parent); + +println!("Child path: {}", child.path().unwrap()); // output: 'parent::child' + +world.lookup("parent::child"); // returns child +parent.lookup("child"); // returns child +``` +
  • +
+
Queries (see below) can use hierarchies to order data breadth-first, which can come in handy when you're implementing a transform system: +
+
    +
  • C ```c -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { +ecs_query_t *q = ecs_query(world, { + .terms = { { ecs_id(Position) }, { ecs_id(Position), .src = { .flags = EcsCascade, // Breadth-first order @@ -479,63 +970,69 @@ ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Position *p_parent = ecs_field(&it, Position, 2); + Position *p = ecs_field(&it, Position, 0); + Position *p_parent = ecs_field(&it, Position, 1); for (int i = 0; i < it.count; i++) { // Do the thing } } ``` +
  • +
  • C++ + ```cpp auto q = world.query_builder() - .term_at(2).parent().cascade() + .term_at(1).parent().cascade() .build(); q.each([](Position& p, Position& p_parent) { // Do the thing }); ``` +
  • +
  • C# -### Instancing -Flecs has builtin support for instancing (sharing a single component with multiple entities) through the builtin `EcsIsA` relationship (`flecs::IsA` in C++). An entity with an `IsA` relationship to a base entity "inherits" all entities from that base: +```cs +Query q = world.QueryBuilder() + .TermAt(1).Parent().Cascade() + .Build(); -```c -// Shortcut to create entity & set a component -ecs_entity_t base = ecs_set(world, 0, Triangle, {{0, 0}, {1, 1}, {-1, -1}}); - -// Create entity that shares components with base -ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); -const Triangle *t = ecs_get(world, e, Triangle); // gets Triangle from base -``` -```cpp -auto base = world.entity().set({{0, 0}, {1, 1}, {-1, -1}}); - -// Create entity that shares components with base -auto e = world.entity().is_a(base); -const Triangle *t = e.get(); // gets Triangle from base +q.Each((ref Position p, ref Position pParent) => +{ + // Do the thing +}); ``` +
  • +
  • Rust -Entities can override components from their base: +```rust +let q = world + .query::<(&Position, &mut Position)>() + .term_at(1) + .parent() + .cascade() + .build(); -```c -// Add private instance of Triangle to e, copy value from base -ecs_add(world, e, Triangle); -``` -```cpp -// Add private instance of Triangle to e, copy value from base -e.add(); +q.each(|(p, p_parent)| { + // Do the thing +}); ``` - -Instancing can be used to build modular prefab hierarchies, as the foundation of a batched renderer with instancing support, or just to reduce memory footprint by sharing common data across entities. +
  • +
+
### Type The type (often referred to as "archetype") is the list of ids an entity has. Types can be used for introspection which is useful when debugging, or when for example building an entity editor. The most common thing to do with a type is to convert it to text and print it: +
+
    +
  • C + ```c ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); @@ -544,6 +1041,9 @@ char *type_str = ecs_type_str(world, type); printf("Type: %s\n", type_str); // output: 'Position,Velocity' ecs_os_free(type_str); ``` +
  • +
  • C++ + ```cpp auto e = ecs.entity() .add() @@ -551,8 +1051,33 @@ auto e = ecs.entity() std::cout << e.type().str() << std::endl; // output: 'Position,Velocity' ``` +
  • +
  • C# + +```cs +Entity e = ecs.Entity() + .Add() + .Add(); + +Console.WriteLine(e.Type().Str()); // output: 'Position,Velocity' +``` +
  • +
  • Rust + +```rust +let e = world.entity().add::().add::(); + +println!("Components: {}", e.archetype().to_string().unwrap()); // output: 'Position,Velocity' +``` +
  • +
+
A type can also be iterated by an application: +
+
    +
  • C + ```c const ecs_type_t *type = ecs_get_type(world, e); for (int i = 0; i < type->count; i++) { @@ -561,6 +1086,9 @@ for (int i = 0; i < type->count; i++) { } } ``` +
  • +
  • C++ + ```cpp e.each([&](flecs::id id) { if (id == world.id()) { @@ -568,10 +1096,39 @@ e.each([&](flecs::id id) { } }); ``` +
  • +
  • C# + +```cs +e.Each((Id id) => +{ + if (id == world.Id()) + { + // Found Position component! + } +}); +``` +
  • +
  • Rust + +```rust +e.each_component(|id| { + if id == world.component_id::() { + // Found Position component! + } +}); +``` +
  • +
+
### Singleton A singleton is a single instance of a component that can be retrieved without an entity. The functions for singletons are very similar to the regular API: +
+
    +
  • C + ```c // Set singleton component ecs_singleton_set(world, Gravity, { 9.81 }); @@ -579,6 +1136,9 @@ ecs_singleton_set(world, Gravity, { 9.81 }); // Get singleton component const Gravity *g = ecs_singleton_get(world, Gravity); ``` +
  • +
  • C++ + ```cpp // Set singleton component world.set({ 9.81 }); @@ -586,14 +1146,46 @@ world.set({ 9.81 }); // Get singleton component const Gravity *g = world.get(); ``` +
  • +
  • C# + +```cs +// Set singleton component +world.Set(new(9.81)); + +// Get singleton component +ref readonly Gravity g = ref world.Get(); +``` +
  • +
  • Rust + +```rust +// Set singleton component +world.set(Gravity { x: 10, y: 20 }); + +// Get singleton component +world.get::<&Gravity>(|g| { + println!("Gravity: {}, {}", g.x, g.y); +}); +``` +
  • +
+
Singleton components are created by adding the component to its own entity id. The above code examples are shortcuts for these regular API calls: +
+
    +
  • C + ```c ecs_set(world, ecs_id(Gravity), Gravity, {10, 20}); const Gravity *g = ecs_get(world, ecs_id(Gravity), Gravity); ``` +
  • +
  • C++ + ```cpp flecs::entity grav_e = world.entity(); @@ -601,13 +1193,41 @@ grav_e.set({10, 20}); const Gravity *g = grav_e.get(); ``` +
  • +
  • C# + +```cs +Entity gravE = world.Entity(); + +gravE.Set(new(10, 20)); + +ref readonly Gravity g = ref gravE.Get(); +``` +
  • +
  • Rust + +```rust +let grav_e = world.entity_from::(); + +grav_e.set(Gravity { x: 10, y: 20 }); + +grav_e.get::<&Gravity>(|g| { + println!("Gravity: {}, {}", g.x, g.y); +}); +``` +
  • +
+
The following examples show how to query for a singleton component: +
+
    +
  • C ```c // Create query that matches Gravity as singleton ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { // Regular component { .id = ecs_id(Velocity) }, // A singleton is a component matched on itself @@ -618,32 +1238,57 @@ ecs_query_t *q = ecs_query(ecs, { // Create a system using the query DSL with a singleton: ECS_SYSTEM(world, ApplyGravity, EcsOnUpdate, Velocity, Gravity($)); ``` +
  • +
  • C++ ```cpp world.query_builder() - .term_at(2).singleton() + .term_at(1).singleton() .build(); ``` +
  • +
  • C# -### Filter -Filters are a kind of uncached query that are cheap to create. This makes them a good fit for scenarios where an application doesn't know in advance what it has to query for, like when finding the children for a parent. The following example shows a simple filter: +```cs +world.QueryBuilder() + .TermAt(1).Singleton() + .Build(); +``` +
  • +
  • Rust + +```rust +world + .query::<(&Velocity, &Gravity)>() + .term_at(1) + .singleton() + .build(); +``` +
  • +
+
+ +### Query +Queries are the main mechanism for finding and iterating through entities. Queries are used in many parts of the API, such as for systems and observers. The following example shows a simple query: +
+
    +
  • C ```c -// Initialize a filter with 2 terms on the stack -ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_pair(EcsChildOf, parent) } } }); -// Iterate the filter results. Because entities are grouped by their type there +// Iterate the query results. Because entities are grouped by their type there // are two loops: an outer loop for the type, and an inner loop for the entities // for that type. -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { +ecs_iter_t it = ecs_query_iter(world, q); +while (ecs_query_next(&it)) { // Each type has its own set of component arrays - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); // Iterate all entities for the type for (int i = 0; i < it.count; i++) { @@ -652,40 +1297,116 @@ while (ecs_filter_next(&it)) { } } -ecs_filter_fini(f); +ecs_query_fini(f); ``` +
  • +
  • C++ + ```cpp -// For simple queries the each function can be used +// For simple queries the world::each function can be used world.each([](Position& p, Velocity& v) { // flecs::entity argument is optional p.x += v.x; p.y += v.y; }); -// More complex filters can first be created, then iterated -auto f = world.filter_builder() - .term(flecs::ChildOf, parent) +// More complex queries can first be created, then iterated +auto q = world.query_builder() + .with(flecs::ChildOf, parent) .build(); -// Option 1: each() function that iterates each entity -f.each([](flecs::entity e, Position& p) { +// Option 1: the each() callback iterates over each entity +q.each([](flecs::entity e, Position& p) { std::cout << e.name() << ": {" << p.x << ", " << p.y << "}" << std::endl; }); -// Option 2: iter() function that iterates each archetype -f.iter([](flecs::iter& it, Position *p) { - for (int i : it) { - std::cout << it.entity(i).name() - << ": {" << p[i].x << ", " << p[i].y << "}" << std::endl; +// Option 2: the run() callback offers more control over the iteration +q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + + for (auto i : it) { + std::cout << it.entity(i).name() + << ": {" << p[i].x << ", " << p[i].y << "}" << std::endl; + } } }); ``` +
  • +
  • C# -Filters can use operators to exclude components, optionally match components or match one out of a list of components. Additionally filters may contain wildcards for terms which is especially useful when combined with pairs. +```cs +// For simple queries the each function can be used +world.Each((ref Position p, ref Velocity v) => // Entity argument is optional +{ + p.X += v.X; + p.Y += v.Y; +}); -The following example shows a filter that matches all entities with a parent that do not have `Position`: +// More complex filters can first be created, then iterated +using Query q = world.QueryBuilder() + .With(Ecs.ChildOf, parent) + .Build(); + +// Option 1: The Each() callback that iterates each entity +q.Each((Entity e, ref Position p) => +{ + Console.WriteLine($"{e.Name()}: ({p.X}, {p.Y})") +}); + +// Option 2: The Iter() callback provides more control over the iteration +q.Iter((Iter it, Field p) => +{ + foreach (int i in it) + Console.WriteLine($"{it.Entity(i).Name()}: ({p[i].X}, {p[i].Y})") +}); +``` +
  • +
  • Rust + +```rust +// For simple queries the world::each function can be used +world.each::<(&mut Position, &Velocity)>(|(p, v)| { + // EntityView argument is optional, use each_entity to get it + p.x += v.x; + p.y += v.y; +}); + +// More complex queries can first be created, then iterated +let q = world + .query::<&Position>() + .with_id((flecs::ChildOf::ID, parent)) + .build(); + +// Option 1: the each() callback iterates over each entity +q.each_entity(|e, p| { + println!("{}: ({}, {})", e.name(), p.x, p.y); +}); + +// Option 2: the run() callback offers more control over the iteration +q.run(|mut it| { + while it.next() { + let p = it.field::(0).unwrap(); + + for i in it.iter() { + println!("{}: ({}, {})", it.entity(i).name(), p[i].x, p[i].y); + } + } +}); +``` +
  • +
+
+ +Queries can use operators to exclude components, optionally match components or match one out of a list of components. Additionally filters may contain wildcards for terms which is especially useful when combined with pairs. + +The following example shows a query that matches all entities with a parent that do not have `Position`: + +
+
    +
  • C ```c -ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ +ecs_query_t *q = ecs_query(world, { .terms = { { ecs_pair(EcsChildOf, EcsWildcard) } { ecs_id(Position), .oper = EcsNot }, @@ -694,50 +1415,54 @@ ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ // Iteration code is the same ``` +
  • +
  • C++ + ```cpp -auto f = world.filter_builder<>() - .term(flecs::ChildOf, flecs::Wildcard) - .term().oper(flecs::Not) +flecs::query<> q = world.query_builder() + .with(flecs::ChildOf, flecs::Wildcard) + .with().oper(flecs::Not) .build(); // Iteration code is the same ``` +
  • +
  • C# -### Query -Queries are cached versions of filters. They are slower to create than filters, but much faster to iterate since this just means iterating their cache. - -The API for queries is similar to filters: +```cs +using Query q = world.QueryBuilder() + .With(Ecs.ChildOf, Ecs.Wildcard) + .With().Oper(Ecs.Not) + .Build(); -```c -// Create a query with 2 terms -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { ecs_id(Position) }, - { ecs_pair(EcsChildOf, EcsWildcard) } - } -}); - -ecs_iter_t it = ecs_query_iter(world, q); -while (ecs_query_next(&it)) { - // Same as for filters -} +// Iteration code is the same ``` -```cpp -// Create a query with two terms -auto q = world.query_builder() - .term(flecs::ChildOf, flecs::Wildcard) +
  • +
  • Rust + +```rust +let q = world + .query::<()>() + .with::<(flecs::ChildOf, flecs::Wildcard)>() + .with::() + .set_oper(OperKind::Not) .build(); -// Iteration is the same as filters +// Iteration code is the same ``` - -When using queries, make sure to reuse a query object instead of creating a new one each time you need it. Query creation is expensive, and many of the performance benefits of queries are lost when they are created in loops. +
  • +
+
See the [query manual](Queries.md) for more details. ### System A system is a query combined with a callback. Systems can be either ran manually or ran as part of an ECS-managed main loop (see [Pipeline](#pipeline)). The system API looks similar to queries: +
+
    +
  • C + ```c // Option 1, use the ECS_SYSTEM convenience macro ECS_SYSTEM(world, Move, 0, Position, Velocity); @@ -745,9 +1470,9 @@ ecs_run(world, Move, delta_time, NULL); // Run system // Option 2, use the ecs_system_init function/ecs_system macro ecs_entity_t move_sys = ecs_system(world, { - .query.filter.terms = { - {ecs_id(Position)}, - {ecs_id(Velocity)}, + .query.terms = { + { ecs_id(Position) }, + { ecs_id(Velocity) }, }, .callback = Move }); @@ -756,8 +1481,8 @@ ecs_run(world, move_sys, delta_time, NULL); // Run system // The callback code (same for both options) void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i++) { p[i].x += v[i].x * it->delta_time; @@ -765,38 +1490,106 @@ void Move(ecs_iter_t *it) { } } ``` +
  • +
  • C++ + ```cpp // Use each() function that iterates each individual entity auto move_sys = world.system() - .iter([](flecs::iter it, Position *p, Velocity *v) { - for (int i : it) { - p[i].x += v[i].x * it.delta_time(); - p[i].y += v[i].y * it.delta_time(); - } + .each([](flecs::iter& it, size_t, Position& p, Velocity& v) { + p.x += v.x * it.delta_time(); + p.y += v.y * it.delta_time(); }); - // Just like with filters & queries, systems have both the iter() and + // Just like with queries, systems have both the run() and // each() methods to iterate entities. move_sys.run(); ``` +
  • +
  • C# + +```cs +// Use Each() function that iterates each individual entity +System moveSys = world.System() + .Each((Entity e, ref Position p, ref Velocity v) => + { + p.X += v.X * it.DeltaTime(); + p.Y += v.Y * it.DeltaTime(); + }); + + // Just like with queries, systems have both the Iter() and + // Each() methods to iterate entities. + +moveSys.Run(); +``` +
  • +
  • Rust + +```rust +// Use each_entity() function that iterates each individual entity +let move_sys = world + .system::<(&mut Position, &Velocity)>() + .each_iter(|it, i, (p, v)| { + p.x += v.x * it.delta_time(); + p.y += v.y * it.delta_time(); + }); + +// Just like with queries, systems have both the run() and +// each() methods to iterate entities. + +move_sys.run(); +``` +
  • +
+
Systems are stored as entities with an `EcsSystem` component (`flecs::System` in C++), similar to components. That means that an application can use a system as a regular entity: +
+
    +
  • C + ```c printf("System: %s\n", ecs_get_name(world, move_sys)); ecs_add(world, move_sys, EcsOnUpdate); ecs_delete(world, move_sys); ``` +
  • +
  • C++ + ```cpp std::cout << "System: " << move_sys.name() << std::endl; move_sys.add(flecs::OnUpdate); move_sys.destruct(); ``` +
  • +
  • C# + +```cs +Console.WriteLine($"System: {moveSys.Name()}"); +moveSys.Entity.Add(Ecs.OnUpdate); +moveSys.Entity.Destruct(); +``` +
  • +
  • Rust + +```rust +println!("System: {}", move_sys.name()); +move_sys.add::(); +move_sys.destruct(); +``` +
  • +
+
### Pipeline A pipeline is a list of tags that when matched, produces a list of systems to run. These tags are also referred to as a system "phase". Flecs comes with a default pipeline that has the following phases: +
+
    +
  • C + ```c EcsOnLoad EcsPostLoad @@ -807,6 +1600,9 @@ EcsPostUpdate EcsPreStore EcsOnStore ``` +
  • +
  • C++ + ```cpp flecs::OnLoad flecs::PostLoad @@ -817,9 +1613,42 @@ flecs::PostUpdate flecs::PreStore flecs::OnStore ``` +
  • +
  • C# + +```cs +Ecs.OnLoad +Ecs.PostLoad +Ecs.PreUpdate +Ecs.OnUpdate +Ecs.OnValidate +Ecs.PostUpdate +Ecs.PreStore +Ecs.OnStore +``` +
  • +
  • Rust + +```rust +flecs::pipeline::OnLoad; +flecs::pipeline::PostLoad; +flecs::pipeline::PreUpdate; +flecs::pipeline::OnUpdate; +flecs::pipeline::OnValidate; +flecs::pipeline::PostUpdate; +flecs::pipeline::PreStore; +flecs::pipeline::OnStore; +``` +
  • +
+
When a pipeline is executed, systems are ran in the order of the phases. This makes pipelines and phases the primary mechanism for defining ordering between systems. The following code shows how to assign systems to a pipeline, and how to run the pipeline with the `progress()` function: +
+
    +
  • C + ```c ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); ECS_SYSTEM(world, Transform, EcsPostUpdate, Position, Transform); @@ -827,6 +1656,9 @@ ECS_SYSTEM(world, Render, EcsOnStore, Transform, Mesh); ecs_progress(world, 0); // run systems in default pipeline ``` +
  • +
  • C++ + ```cpp world.system("Move").kind(flecs::OnUpdate).each( ... ); world.system("Transform").kind(flecs::PostUpdate).each( ... ); @@ -834,16 +1666,75 @@ world.system("Render").kind(flecs::OnStore).each( ... ); world.progress(); ``` +
  • +
  • C# + +```cs +world.System("Move").Kind(Ecs.OnUpdate).Each( ... ); +world.System("Transform").Kind(Ecs.PostUpdate).Each( ... ); +world.System("Render").Kind(Ecs.OnStore).Each( ... ); + +world.Progress(); +``` +
  • +
  • Rust + +```rust +world + .system_named::<(&mut Position, &Velocity)>("Move") + .kind::() + .each(|(p, v)| {}); + +world + .system_named::<(&mut Position, &Transform)>("Transform") + .kind::() + .each(|(p, t)| {}); + +world + .system_named::<(&Transform, &mut Mesh)>("Render") + .kind::() + .each(|(t, m)| {}); + +world.progress(); +``` +
  • +
+
Because phases are just tags that are added to systems, applications can use the regular API to add/remove systems to a phase: + +
+
    +
  • C + ```c ecs_remove_id(world, Move, EcsOnUpdate); ecs_add_id(world, Move, EcsPostUpdate); ``` +
  • +
  • C++ + ```cpp move_sys.add(flecs::OnUpdate); move_sys.remove(flecs::PostUpdate); ``` +
  • +
  • C# + +```cs +moveSys.Add(Ecs.OnUpdate); +moveSys.Remove(Ecs.PostUpdate); +``` +
  • +
  • Rust + +```rust +move_sys.add::(); +move_sys.remove::(); +``` +
  • +
+
Inside a phase, systems are guaranteed to be ran in their declaration order. @@ -853,33 +1744,74 @@ Observers are callbacks that are invoked when one or more events matches the que When an observer has a query with more than one component, the observer will not be invoked until the entity for which the event is emitted satisfies the entire query. An example of an observer with two components: +
+
    +
  • C ```c ecs_observer(world, { - .filter.terms = { { ecs_id(Position) }, { ecs_id(Velocity) }}, - .event = EcsOnSet, + .query.terms = { { ecs_id(Position) }, { ecs_id(Velocity) }}, + .event = { EcsOnSet }, .callback = OnSetPosition }); // Callback code is same as system -ecs_entity_t e = ecs_new_id(world); // Doesn't invoke the observer +ecs_entity_t e = ecs_new(world); // Doesn't invoke the observer ecs_set(world, e, Position, {10, 20}); // Doesn't invoke the observer ecs_set(world, e, Velocity, {1, 2}); // Invokes the observer ecs_set(world, e, Position, {20, 40}); // Invokes the observer ``` +
  • +
  • C++ + ```cpp -world.observer("OnSetPosition").event(flecs::OnSet).each( ... ); +world.observer("OnSetPosition") + .event(flecs::OnSet) + .each( ... ); // Callback code is same as system auto e = ecs.entity(); // Doesn't invoke the observer e.set({10, 20}); // Doesn't invoke the observer e.set({1, 2}); // Invokes the observer e.set({20, 30}); // Invokes the observer ``` +
  • +
  • C# + +```cs +world.Observer("OnSetPosition") + .Event(Ecs.OnSet) + .Each( ... ); // Callback code is same as system + +Entity e = ecs.Entity(); // Doesn't invoke the observer +e.Set(new(10, 20)); // Doesn't invoke the observer +e.Set(new(1, 2)); // Invokes the observer +e.Set(new(20, 30)); // Invokes the observer +``` +
  • +
  • Rust + +```rust +world + .observer_named::("OnSetPosition") + .each(|(p, v)| {}); // Callback code is same as system + +let e = world.entity(); // Doesn't invoke the observer +e.set(Position { x: 10.0, y: 20.0 }); // Doesn't invoke the observer +e.set(Velocity { x: 1.0, y: 2.0 }); // Invokes the observer +e.set(Position { x: 30.0, y: 40.0 }); // Invokes the observer +``` +
  • +
+
### Module A module is a function that imports and organizes components, systems, triggers, observers, prefabs into the world as reusable units of code. A well designed module has no code that directly relies on code of another module, except for components definitions. All module contents are stored as child entities inside the module scope with the `ChildOf` relationship. The following examples show how to define a module in C and C++: +
+
    +
  • C + ```c // Module header (e.g. MyModule.h) typedef struct { @@ -903,6 +1835,9 @@ void MyModuleImport(ecs_world_t *world) { // Import code ECS_IMPORT(world, MyModule); ``` +
  • +
  • C++ + ```cpp struct my_module { my_module(flecs::world& world) { @@ -916,3 +1851,43 @@ struct my_module { // Import code world.import(); ``` +
  • +
  • C# + +```cs +public struct MyModule : IFlecsModule +{ + public void InitModule(ref World world) + { + world.Module(); + + // Define components, systems, triggers, ... as usual. They will be + // automatically created inside the scope of the module. + } +}; + +// Import code +world.Import(); +``` +
  • +
  • Rust + +```rust +#[derive(Component)] +struct MyModule; + +impl Module for MyModule { + fn module(world: &World) { + world.module::("MyModule"); + // Define components, systems, triggers, ... as usual. They will be + // automatically created inside the scope of the module. + } +} + +// Import code +world.import::(); +``` + +
  • +
+
diff --git a/vendors/flecs/docs/Relationships.md b/vendors/flecs/docs/Relationships.md index 828be813c..60ebe0ba6 100644 --- a/vendors/flecs/docs/Relationships.md +++ b/vendors/flecs/docs/Relationships.md @@ -22,14 +22,19 @@ Make sure to check out the code examples in the repository: - [relationships (C)](https://github.com/SanderMertens/flecs/tree/master/examples/c/relationships) - [relationships (C++)](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/relationships) + - [relationships (C#)](https://github.com/BeanCheeseBurrito/Flecs.NET/tree/main/src/Flecs.NET.Examples/Cpp/Relationships) ## Introduction The following code is a simple example that uses relationships: +
+
    +
  • C + ```c -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Bob = ecs_new_id(world); -ecs_entity_t Alice = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Bob = ecs_new(world); +ecs_entity_t Alice = ecs_new(world); // Bob Likes Alice ecs_add_pair(world, Bob, Likes, Alice); @@ -37,6 +42,10 @@ ecs_add_pair(world, Bob, Likes, Alice); // Bob Likes Alice no more ecs_remove_pair(world, Bob, Likes, Alice); ``` + +
  • +
  • C++ + ```cpp auto Likes = world.entity(); auto Bob = world.entity(); @@ -49,15 +58,53 @@ Bob.add(Likes, Alice); Bob.remove(Likes, Alice); ``` +
  • +
  • C# + +```cs +Entity Likes = world.Entity(); +Entity Bob = world.Entity(); +Entity Alice = world.Entity(); + +// Bob Likes Alice +Bob.Add(Likes, Alice); + +// Bob Likes Alice no more +Bob.Remove(Likes, Alice); +``` + +
  • +
  • Rust + +```rust +let likes = world.entity(); +let bob = world.entity(); +let alice = world.entity(); + +// bob likes alice +bob.add_id((likes, alice)); + +// bob likes alice no more +bob.remove_id((likes, alice)); +``` + +
  • +
+
+ In this example, we refer to `Bob` as the "source", `Likes` as the "relationship" and `Alice` as the "target". A relationship when combined with an target is called a "relationship pair". The same relationship can be added multiple times to an entity, as long as its target is different: +
+
    +
  • C + ```c -ecs_entity_t Bob = ecs_new_id(world); -ecs_entity_t Eats = ecs_new_id(world); -ecs_entity_t Apples = ecs_new_id(world); -ecs_entity_t Pears = ecs_new_id(world); +ecs_entity_t Bob = ecs_new(world); +ecs_entity_t Eats = ecs_new(world); +ecs_entity_t Apples = ecs_new(world); +ecs_entity_t Pears = ecs_new(world); ecs_add_pair(world, Bob, Eats, Apples); ecs_add_pair(world, Bob, Eats, Pears); @@ -65,6 +112,10 @@ ecs_add_pair(world, Bob, Eats, Pears); ecs_has_pair(world, Bob, Eats, Apples); // true ecs_has_pair(world, Bob, Eats, Pears); // true ``` + +
  • +
  • C++ + ```cpp auto Bob = world.entity(); auto Eats = world.entity(); @@ -78,80 +129,295 @@ Bob.has(Eats, Apples); // true Bob.has(Eats, Pears); // true ``` +
  • +
  • C# + +```cs +Entity Bob = world.Entity(); +Entity Eats = world.Entity(); +Entity Apples = world.Entity(); +Entity Pears = world.Entity(); + +Bob.Add(Eats, Apples); +Bob.Add(Eats, Pears); + +Bob.Has(Eats, Apples); // true +Bob.Has(Eats, Pears); // true +``` + +
  • +
  • Rust + +```rust +let bob = world.entity(); + +let eats = world.entity(); +let apples = world.entity(); +let pears = world.entity(); + +bob.add_id((eats, apples)); +bob.add_id((eats, pears)); + +bob.has_id((eats, apples)); // true +bob.has_id((eats, pears)); // true +``` + +
  • +
+
+ An application can query for relationships with the `(Relationship, Target)` notation: +
+
    +
  • C + ```c // Find all entities that eat apples -ecs_query_t *q = ecs_query_new(world, "(Eats, Apples)"); +ecs_query_t *q = ecs_query(world, { .expr = "(Eats, Apples)" }); // Find all entities that eat anything -ecs_query_t *q = ecs_query_new(world, "(Eats, *)"); +ecs_query_t *q = ecs_query(world, { .expr = "(Eats, *)" }); -// Or with the ecs_query_init function: -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ecs_pair(Eats, Apples)}} +// Or with the ecs_query_cache_init function: +ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_pair(Eats, Apples)}} }); ``` + +
  • +
  • C++ + ```cpp // Find all entities that eat apples -auto q = world.query("(Eats, Apples)"); +auto q = world.query_builder<>() + .expr("(Eats, Apples)") + .build(); // Find all entities that eat anything -auto q = world.query("(Eats, *)"); +auto q = world.query_builder<>() + .expr("(Eats, *)") + .build(); // With the query builder API: auto q = world.query_builder<>() - .term(Eats, Apples) + .with(Eats, Apples) .build(); // Or when using pair types, when both relationship & target are compile time types: auto q = world.query>(); ``` +
  • +
  • C# + +```cs +// Find all entities that eat apples +Query q = world.QueryBuilder() + .Expr("(Eats, Apples)") + .Build(); + +// Find all entities that eat anything +Query q = world.QueryBuilder() + .Expr("(Eats, *)") + .Build(); + +// With the query builder API: +Query q = world.QueryBuilder() + .Expr(Eats, Apples) + .Build(); +``` + +
  • +
  • Rust + +```rust +// Find all entities that eat apples +let q = world.query::<()>().expr("(Eats, Apples)").build(); + +// Find all entities that eat anything +let q = world.query::<()>().expr("(Eats, *)").build(); + +// With the query builder API: +let q = world.query::<()>().with_id((eats, apples)).build(); + +// Or when using pair types, when both relationship & target are compile time types, they can be represented as a tuple: + +let q = world.new_query::<&(Eats, Apples)>(); +``` + +
  • +
+
+ This example just shows a simple relationship query. Relationship queries are much more powerful than this as they provide the ability to match against entity graphs of arbitrary size. For more information on relationship queries see the [query manual](Queries.md). ## Relationship queries There are a number of ways an application can query for relationships. The following kinds of queries are available for all (unidirectional) relationships, and are all constant time: ### Test if entity has a relationship pair + +
+
    +
  • C + ```c ecs_has_pair(world, Bob, Eats, Apples); ``` + +
  • +
  • C++ + ```cpp Bob.has(Eats, Apples); ``` +
  • +
  • C# + +```cs +Bob.Has(Eats, Apples); +``` + +
  • +
  • Rust + +```rust +bob.has_id((eats, apples)); +``` + +
  • +
+
+ ### Test if entity has a relationship wildcard + +
+
    +
  • C + ```c ecs_has_pair(world, Bob, Eats, EcsWildcard); ``` + +
  • +
  • C++ + ```cpp Bob.has(Eats, flecs::Wildcard); ``` +
  • +
  • C# + +```cs +Bob.Has(Eats, Ecs.Wildcard); +``` + +
  • +
  • Rust + +```rust +bob.has_id((eats, flecs::Wildcard::ID)); +``` + +
  • +
+
+ ### Get parent for entity + +
+
    +
  • C + ```c ecs_entity_t parent = ecs_get_parent(world, Bob); ``` + +
  • +
  • C++ + ```cpp flecs::entity parent = Bob.parent(); ``` +
  • +
  • C# + +```cs +Entity parent = Bob.Parent(); +``` + +
  • +
  • Rust + +```rust +let parent = bob.parent(); +``` + +
  • +
  • Rust + +```rust +let parent = bob.parent(); +``` + +
  • +
+
+ ### Find first target of a relationship for entity + +
+
    +
  • C + ```c ecs_entity_t food = ecs_get_target(world, Bob, Eats, 0); ``` + +
  • +
  • C++ + ```cpp flecs::entity food = Bob.target(Eats); ``` +
  • +
  • C# + +```cs +Entity food = Bob.Target(Eats); +``` + +
  • +
  • Rust + +```rust +let food = bob.target_id(eats, 0); // first target +``` + +
  • +
+
+ ### Find all targets of a relationship for entity + +
+
    +
  • C + ```c int32_t index = 0; while ((food = ecs_get_target(world, Bob, Eats, index ++))) { // ... } ``` + +
  • +
  • C++ + ```cpp int32_t index = 0; while ((food = Bob.target(Eats, index ++))) { @@ -159,15 +425,72 @@ while ((food = Bob.target(Eats, index ++))) { } ``` +
  • +
  • C# + +```cs +int index = 0; +while ((food = Bob.Target(Eats, index++)) != 0) +{ + // ... +} +``` + +
  • +
  • Rust + +```rust +let mut index = 0; +while bob.target_id(eats, index).is_some() { + index += 1; +} +``` + +
  • +
+
+ ### Find target of a relationship with component + +
+
    +
  • C + ```c ecs_entity_t parent = ecs_get_target_for(world, Bob, EcsChildOf, Position); ``` + +
  • +
  • C++ + ```cpp flecs::entity parent = Bob.target_for(flecs::ChildOf); ``` +
  • +
  • C# + +```cs +Entity parent = Bob.TargetFor(Ecs.ChildOf); +``` + +
  • +
  • Rust + +```rust +let parent = bob.target_for::(flecs::ChildOf::ID); +``` + +
  • +
+
+ ### Iterate all pairs for entity + +
+
    +
  • C + ```c const ecs_type_t *type = ecs_get_type(world, Bob); for (int i = 0; i < type->count; i ++) { @@ -178,67 +501,189 @@ for (int i = 0; i < type->count; i ++) { } } ``` + +
  • +
  • C++ + ```cpp Bob.each([](flecs::id id) { if (id.is_pair()) { - flecs::entity first = id.pair().first(); - flecs::entity second = id.pair().second(); + flecs::entity first = id.first(); + flecs::entity second = id.second(); } }); ``` +
  • +
  • C# + +```cs +Bob.Each((Id id) => +{ + if (id.IsPair()) + { + Entity first = id.First(); + Entity second = id.Second(); + } +}); +``` + +
  • +
  • Rust + +```rust +bob.each_component(|id| { + if id.is_pair() { + let first = id.first_id(); + let second = id.second_id(); + } +}); +``` + +
  • +
+
+ ### Find all entities with a pair + +
+
    +
  • C + ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms[0] = ecs_pair(Eats, Apples) }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { for (int i = 0; i < it.count; i ++) { // Iterate as usual } } -ecs_filter_fini(f); +ecs_query_fini(f); ``` + +
  • +
  • C++ + ```cpp -world.filter_builder() - .term(Eats, Apples) +world.query_builder() + .with(Eats, Apples) .build() .each([](flecs::entity e) { // Iterate as usual }); ``` +
  • +
  • C# + +```cs +world.FilterBuilder() + .With(Eats, Apples) + .Build() + .Each((Entity e) => + { + // Iterate as usual + }); +``` + +
  • +
  • Rust + +```rust +world + .query::<()>() + .with_id((eats, apples)) + .build() + .each_entity(|e, _| { + // Iterate as usual + }); +``` + +
  • +
+
+ ### Find all entities with a pair wildcard + +
+
    +
  • C + ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms[0] = ecs_pair(Eats, EcsWildcard) }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - ecs_id_t pair_id = ecs_field_id(&it, 1); +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { + ecs_id_t pair_id = ecs_field_id(&it, 0); ecs_entity_t food = ecs_pair_second(world, pair_id); // Apples, ... for (int i = 0; i < it.count; i ++) { // Iterate as usual } } -ecs_filter_fini(f); +ecs_query_fini(f); ``` + +
  • +
  • C++ + ```cpp -world.filter_builder() - .term(Eats, flecs::Wildcard) +world.query_builder() + .with(Eats, flecs::Wildcard) .build() .each([](flecs::iter& it, size_t i) { - flecs::entity food = it.pair(1).second(); // Apples, ... + flecs::entity food = it.pair(0).second(); // Apples, ... flecs::entity e = it.entity(i); // Iterate as usual }); ``` +
  • +
  • C# + +```cs +world.FilterBuilder() + .With(Eats, Ecs.Wildcard) + .Build() + .Each((Iter it, int i) => + { + Entity food = it.Pair(1).Second(); // Apples, ... + Entity e = it.Entity(i); + // Iterate as usual + }); +``` + +
  • +
  • Rust + +```rust +world + .query::<()>() + .with_id((eats, flecs::Wildcard::ID)) + .build() + .each_iter(|it, i, _| { + let food = it.pair(0).unwrap().second_id(); // Apples, ... + let e = it.entity(i); + // Iterate as usual + }); +``` + +
  • + +
+
+ ### Iterate all children for a parent + +
+
    +
  • C + ```c ecs_iter_t it = ecs_children(world, parent); while (ecs_children_next(&it)) { @@ -248,24 +693,55 @@ while (ecs_children_next(&it)) { } } ``` + +
  • +
  • C++ + ```cpp parent.children([](flecs::entity child) { // ... }); ``` -More advanced queries are possible with filters, queries and rules. See the [Queries manual](Queries.md) for more details. +
  • +
  • C# + +```cs +parent.Children((Entity child) => +{ + // ... +}); +``` + +
  • +
  • Rust + +```rust +parent.each_child(|child| { + // ... +}); +``` + +
  • +
+
+ +More advanced queries are possible with Flecs queries. See the [Queries manual](Queries.md) for more details. ## Relationship components Relationship pairs, just like regular component, can be associated with data. To associate data with a relationship pair, at least one of its elements needs to be a component. A pair can be associated with at most one type. To determine which type is associated with a relationship pair, the following rules are followed in order: - If neither the first nor second elements are a type, the pair is a tag -- If the first element has the [tag](Relationships.md#tag-property) property, the pair is a tag +- If the first element has the [PairIsTag](https://www.flecs.dev/flecs/md_docs_2ComponentTraits.html#pairistag-trait) trait, the pair is a tag - If the first element is a type, the pair type is the first element - If the second element is a type, the pair type is the second element The following examples show how these rules can be used: +
+
    +
  • C + ```c typedef struct { float x, y; @@ -280,12 +756,12 @@ ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Eats); // Tags -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Begin = ecs_new_id(world); -ecs_entity_t End = ecs_new_id(world); -ecs_entity_t Apples = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Begin = ecs_new(world); +ecs_entity_t End = ecs_new(world); +ecs_entity_t Apples = ecs_new(world); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); // Both Likes and Apples are tags, so (Likes, Apples) is a tag ecs_add_pair(world, e, Likes, Apples); @@ -301,6 +777,10 @@ ecs_set_pair_second(world, e, End, Position, {10, 20}); // same for End // does not assume the Position type ecs_add_pair(world, e, EcsChildOf, Position); ``` + +
  • +
  • C++ + ```cpp struct Position { float x, y; @@ -335,26 +815,104 @@ e.set({10, 20}); // Same for End e.add(flecs::ChildOf, world.id()); ``` +
  • +
  • C# + +```cs +public record struct Position(float X, float Y); +public record struct Eats(float Amount); + +// Empty types (types without members) are automatically interpreted as tags +public struct Begin { } +public struct End { } + +// Tags +Entity Likes = world.Entity(); +Entity Apples = world.Entity(); + +Entity e = world.Entity(); + +// Both Likes and Apples are tags, so (Likes, Apples) is a tag +e.Add(Likes, Apples); + +// Eats is a type and Apples is a tag, so (Eats, Apples) has type Eats +e.Set(Apples, new Eats(1)); + +// Begin is a tags and Position is a type, so (Begin, Position) has type Position +e.Set(new Position(0, 0)); +e.Set(new Position(10, 20)); // Same for End + +// ChildOf has the Tag property, so even though Position is a type, the pair +// does not assume the Position type +e.Add(Ecs.ChildOf, world.Id()); +``` + +
  • +
  • Rust + +```rust +// Empty types (types without members) are letmatically interpreted as tags + +#[derive(Component)] +struct Begin; + +#[derive(Component)] +struct End; + +// Tags +let likes = world.entity(); +let apples = world.entity(); + +let e = world.entity(); + +// Both likes and Apples are tags, so (likes, Apples) is a tag +e.add_id((likes, apples)); + +// Eats is a type and Apples is a tag, so (Eats, Apples) has type Eats +e.set_pair::(Eats { amount: 1 }); + +// Begin is a tags and Position is a type, so (Begin, Position) has type Position +e.set_pair::(Position { x: 10.0, y: 20.0 }); +e.set_pair::(Position { x: 100.0, y: 20.0 }); // Same for End + +// ChildOf has the Tag property, so even though Position is a type, the pair +// does not assume the Position type +e.add_id((flecs::ChildOf::ID, world.component_id::())); +e.add::<(flecs::ChildOf, Position)>(); +``` + +
  • +
+
+ ### Using relationships to add components multiple times A limitation of components is that they can only be added once to an entity. Relationships make it possible to get around this limitation, as a component _can_ be added multiple times, as long as the pair is unique. Pairs can be constructed on the fly from new entity identifiers, which means this is possible: +
+
    +
  • C + ```c typedef struct { float x; float y; } Position; -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); -ecs_entity_t first = ecs_new_id(world); -ecs_entity_t second = ecs_new_id(world); -ecs_entity_t third = ecs_new_id(world); +ecs_entity_t first = ecs_new(world); +ecs_entity_t second = ecs_new(world); +ecs_entity_t third = ecs_new(world); // Add component position 3 times, for 3 different objects ecs_add_pair(world, e, Position, first, {1, 2}); ecs_add_pair(world, e, Position, second, {3, 4}); ecs_add_pair(world, e, Position, third, {5, 6}); ``` + +
  • +
  • C++ + ```cpp struct Position { float x; @@ -373,12 +931,54 @@ e.set(second, {3, 4}); e.set(third, {5, 6}); ``` +
  • +
  • C# + +```cs +public record struct Position(float X, float Y); + +Entity e = world.Entity(); + +Entity first = world.Entity(); +Entity second = world.Entity(); +Entity third = world.Entity(); + +// Add component position 3 times, for 3 different objects +e.Set(first, new(1, 2)); +e.Set(second, new(3, 4)); +e.Set(third, new(5, 6)); +``` + +
  • +
  • Rust + +```rust +let e = world.entity(); + +let first = world.entity(); +let second = world.entity(); +let third = world.entity(); + +// Add component position 3 times, for 3 different objects +e.set_first::(Position { x: 1.0, y: 2.0 }, first); +e.set_first::(Position { x: 3.0, y: 4.0 }, second); +e.set_first::(Position { x: 5.0, y: 6.0 }, third); +``` + +
  • +
+
+ ## Relationship wildcards When querying for relationship pairs, it is often useful to be able to find all instances for a given relationship or target. To accomplish this, an application can use wildcard expressions. Consider the following example, that queries for all entities with a `Likes` relationship: +
+
    +
  • C + ```c -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { +ecs_query_t *q = ecs_query(world, { + .terms = { {ecs_pair(Likes, EcsWildcard)} } }); @@ -386,7 +986,7 @@ ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - ecs_id_t id = ecs_field_id(&it, 1); // Obtain pair id + ecs_id_t id = ecs_field_id(&it, 0); // Obtain pair id // Get relationship & target ecs_entity_t rel = ecs_pair_first(world, id); @@ -400,36 +1000,102 @@ while (ecs_query_next(&it)) { } } ``` + +
  • +
  • C++ + ```cpp auto q = world.query_builder() - .term(Likes, flecs::Wildcard) + .with(Likes, flecs::Wildcard) .build(); -q.iter([](flecs::iter& it) { - auto id = it.pair(1); +q.each([](flecs::iter& it, size_t i) { + cout << "entity " << it.entity(i) << " has relationship " + << it.pair(0).first().name() << ", " + << it.pair(0).second().name() << endl; +}); +``` + +
  • +
  • C# - for (auto i : it) { - cout << "entity " << it.entity(i) << " has relationship " - << id.first().name() << ", " - << id.second().name() << endl; - } +```cs +Query q = world.QueryBuilder() + .With(Likes, Ecs.Wildcard) + .Build(); + +q.Iter((Iter it) => +{ + Id id = it.Pair(1); + + foreach (int i in it) + Console.WriteLine($"entity {it.Entity(i)} has relationship {id.First()}, {id.Second()}"); +}); +``` + +
  • +
  • Rust + +```rust +let q = world + .query::<()>() + .with_id((likes, flecs::Wildcard::ID)) + .build(); + +q.each_iter(|it, i, _| { + println!( + "entity {} has relationship {} {}", + it.entity(i), + it.pair(0).unwrap().first_id().name(), + it.pair(0).unwrap().second_id().name() + ); }); ``` +
  • +
+
+ Wildcards may appear in query expressions, using the `*` character: +
+
    +
  • C + ```c -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "(Likes, *)" +ecs_query_t *q = ecs_query(world, { + .query.expr = "(Likes, *)" }); ``` + +
  • +
  • C++ + ```cpp -auto q = world.query("(Likes, *)"); +auto q = world.query_builder<>().expr("(Likes, *)").build(); ``` -Wildcards may used for the relationship or target part of a pair, or both: +
  • +
  • C# -```cpp +```cs +Query q = world.QueryBuilder().Expr("(Likes, *)").Build(); +``` + +
  • +
  • Rust + +```rust +let q = world.query::<()>().expr("(likes, *)").build(); +``` + +
  • +
+
+ +Wildcards may used for the relationship or target part of a pair, or both: + +```cpp "(Likes, *)" // Matches all Likes relationships "(*, Alice)" // Matches all relationships with Alice as target "(*, *)" // Matches all relationships @@ -438,13 +1104,17 @@ Wildcards may used for the relationship or target part of a pair, or both: ## Inspecting relationships An application can use pair wildcard expressions to find all instances of a relationship for an entity. The following example shows how to find all `Eats` relationships for an entity: +
+
    +
  • C + ```c // Bob eats apples and pears -ecs_entity_t Eats = ecs_new_entity(world, "Eats"); -ecs_entity_t Apples = ecs_new_entity(world, "Apples"); -ecs_entity_t Pears = ecs_new_entity(world, "Pears"); +ecs_entity_t Eats = ecs_entity(world, { .name = "Eats" }); +ecs_entity_t Apples = ecs_entity(world, { .name = "Apples" }); +ecs_entity_t Pears = ecs_entity(world, { .name = "Pears" }); -ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Bob = ecs_new(world); ecs_add_pair(world, Bob, Eats, Apples); ecs_add_pair(world, Bob, Eats, Pears); @@ -460,6 +1130,10 @@ while (-1 != (cur = ecs_search_offset(world, bob_table, cur + 1, wildcard, 0))){ printf("Bob eats %s\n", ecs_get_name(world, obj)); } ``` + +
  • +
  • C++ + ```cpp // Bob eats apples and pears auto Bob = world.entity(); @@ -481,56 +1155,194 @@ bob.each(Eats, [](flecs::entity obj) { }) ``` +
  • +
  • C# + +```cs +// Bob eats apples and pears +Entity Bob = world.Entity(); +Entity Eats = world.Entity(); +Entity Apples = world.Entity(); +Entity Pears = world.Entity(); + +Bob.Add(Eats, Apples); +Bob.Add(Eats, Pears); + +// For target wildcard pairs, each() can be used: +bob.Each(Eats, (Entity obj) => +{ + Console.WriteLine($"Bob eats {obj}"); +}) +``` + +
  • +
  • Rust + +```rust +// bob eats apples and pears +let bob = world.entity(); + +let eats = world.entity(); +let apples = world.entity(); +let pears = world.entity(); + +bob.add_id((eats, apples)); +bob.add_id((eats, pears)); + +// Find all (Eats, *) relationships in bob's type +bob.each_pair(eats, flecs::Wildcard::ID, |id| { + println!("bob eats {}", id.second_id().name()); +}); + +// For target wildcard pairs, each_target_id() can be used: +bob.each_target_id(eats, |entity| { + println!("bob eats {}", entity.name()); +}); +``` + +
  • +
+
+ ## Builtin relationships Flecs comes with a few builtin relationships that have special meaning within the framework. While they are implemented as regular relationships and therefore obey the same rules as any custom relationship, they are used to enhance the features of different parts of the framework. The following two sections describe the builtin relationships of Flecs. ### The IsA relationship The `IsA` relationship is a builtin relationship that allows applications to express that one entity is equivalent to another. This relationship is at the core of component sharing and plays a large role in queries. The `IsA` relationship can be used like any other relationship, as is shown here: +
+
    +
  • C + ```c -ecs_entity_t Apple = ecs_new_id(world); -ecs_entity_t Fruit = ecs_new_id(world); +ecs_entity_t Apple = ecs_new(world); +ecs_entity_t Fruit = ecs_new(world); ecs_add_pair(world, Apple, EcsIsA, Fruit); ``` + +
  • +
  • C++ + ```cpp auto Apple = world.entity(); auto Fruit = world.entity(); Apple.add(flecs::IsA, Fruit); ``` -In C++, adding an `IsA` relationship has a shortcut: +
  • +
  • C# + +```cs +Entity Apple = world.Entity(); +Entity Fruit = world.Entity(); +Apple.Add(Ecs.IsA, Fruit); +``` + +
  • +
  • Rust + +```rust +let apple = world.entity(); +let fruit = world.entity(); + +apple.add_id((flecs::IsA::ID, fruit)); +``` + +
  • +
+
+ +In C++ and C#, adding an `IsA` relationship has a shortcut: + +
+
    +
  • C++ ```cpp Apple.is_a(Fruit); ``` +
  • +
  • C# + +```cs +Apple.IsA(Fruit); +``` + +
  • +
  • Rust + +```rust +apple.is_a_id(fruit); +``` + +
  • +
+
+ This indicates to Flecs that an `Apple` is equivalent to a `Fruit` and should be treated as such. This equivalence is one-way, as a `Fruit` is not equivalent to an `Apple`. Another way to think about this is that `IsA` allows an application to express subsets and supersets. An `Apple` is a subset of `Fruit`. `Fruit` is a superset of `Apple`. We can also add `IsA` relationships to `Apple`: +
+
    +
  • C + ```c -ecs_entity_t GrannySmith = ecs_new_id(world); +ecs_entity_t GrannySmith = ecs_new(world); ecs_add_pair(world, GrannySmith, EcsIsA, Apple); ``` + +
  • +
  • C++ + ```cpp auto GrannySmith = world.entity(); GrannySmith.add(flecs::IsA, Apple); ``` +
  • +
  • C# + +```cs +Entity GrannySmith = world.Entity(); +GrannySmith.Add(Ecs.IsA, Apple); +``` + +
  • +
  • Rust + +```rust +let granny_smith = world.entity(); +granny_smith.add_id((flecs::IsA::ID, apple)); +``` + +
  • +
+
+ This specifies that `GrannySmith` is a subset of `Apple`. A key thing to note here is that because `Apple` is a subset of `Fruit`, `GrannySmith` is a subset of `Fruit` as well. This means that if an application were to query for `(IsA, Fruit)` it would both match `Apple` and `GrannySmith`. This property of the `IsA` relationship is called "transitivity" and it is a feature that can be applied to any relationship. See the [section on Transitivity](#transitive-property) for more details. #### Component sharing An entity with an `IsA` relationship to another entity is equivalent to the other entity. So far the examples showed how querying for an `IsA` relationship will find the subsets of the thing that was queried for. In order for entities to be treated as true equivalents though, everything the superset contains (its components, tags, relationships) must also be found on the subsets. Consider: +
+
    +
  • C + ```c -ecs_entity_t Spaceship = ecs_new_id(world); +ecs_entity_t Spaceship = ecs_new(world); ecs_set(world, Spaceship, MaxSpeed, {100}); ecs_set(world, SpaceShip, Defense, {50}); -ecs_entity_t Frigate = ecs_new_id(world); +ecs_entity_t Frigate = ecs_new(world); ecs_add(world, Frigate, EcsIsA, Spaceship); ecs_set(world, Frigate, Defense, {100}); ``` + +
  • +
  • C++ + ```cpp auto Spaceship = world.entity() .set({100}) @@ -541,36 +1353,133 @@ auto Frigate = world.entity() .set({75}); ``` +
  • +
  • C# + +```cs +Entity Spaceship = world.Entity() + .Set(new(100)) + .Set(new(50)); + +Entity Frigate = world.Entity() + .IsA(SpaceShip) // shorthand for .Add(Ecs.IsA, Spaceship) + .Set(new(75)); +``` + +
  • +
  • Rust + +```rust +let spaceship = world + .entity() + .set(MaxSpeed { value: 100 }) + .set(Defense { value: 50 }); + +let frigate = world + .entity() + .is_a_id(spaceship) // shorthand for .add(flecs::IsA, Spaceship) + .set(Defense { value: 75 }); +``` + +
  • +
+
+ Here, the `Frigate` "inherits" the contents of `SpaceShip`. Even though `MaxSpeed` was never added directly to `Frigate`, an application can do this: +
+
    +
  • C + ```c // Obtain the inherited component from Spaceship const MaxSpeed *v = ecs_get(world, Frigate, MaxSpeed); v->value == 100; // true ``` + +
  • +
  • C++ + ```cpp // Obtain the inherited component from Spaceship const MaxSpeed *v = Frigate.get(); v->value == 100; // true ``` +
  • +
  • C# + +```cs +// Obtain the inherited component from Spaceship +ref readonly MaxSpeed v = ref Frigate.Get(); +v.Value == 100; // true +``` + +
  • +
  • Rust + +```rust +// Obtain the inherited component from Spaceship +let is_100 = frigate.map::<&mut MaxSpeed, _>(|v| { + v.value == 100 // True +}); +``` + +
  • +
+
+ While the `Frigate` entity also inherited the `Defense` component, it overrode this with its own value, so that the following example works: +
+
    +
  • C + ```c // Obtain the overridden component from Frigate const Defense *v = ecs_get(world, Frigate, Defense); v->value == 75; // true ``` + +
  • +
  • C++ + ```cpp // Obtain the overridden component from Frigate const Defense *v = Frigate.get(); v->value == 75; // true ``` +
  • +
  • C# + +```cs +ref readonly Defense v = ref Frigate.get(); +v.Value == 75; // true +``` + +
  • +
  • Rust + +```rust +// Obtain the overridden component from Frigate +let is_75 = frigate.map::<&mut Defense, _>(|v| { + v.value == 75 // True +}); +``` + +
  • +
+
+ The ability to share components is also applied transitively, so `Frigate` could be specialized further into a `FastFrigate`: +
+
    +
  • C + ```c -ecs_entity_t FastFrigate = ecs_new_id(world); +ecs_entity_t FastFrigate = ecs_new(world); ecs_add(world, FastFrigate, EcsIsA, Frigate); ecs_set(world, FastFrigate, MaxSpeed, {200}); @@ -582,6 +1491,10 @@ s->value == 200; // true const Defense *d = Frigate.get(); d->value == 75; // true ``` + +
  • +
  • C++ + ```cpp auto FastFrigate = world.entity() .is_a(Frigate) @@ -596,721 +1509,314 @@ const Defense *d = Frigate.get(); d->value == 75; // true ``` -This ability to inherit and override components is one of the key enabling features of Flecs prefabs, and is further explained in the [Inheritance section](Manual.md#Inheritance) of the manual. - -### The ChildOf relationship -The `ChildOf` relationship is the builtin relationship that allows for the creation of entity hierarchies. The following example shows how hierarchies can be created with `ChildOf`: +
  • +
  • C# -```c -ecs_entity_t Spaceship = ecs_new_id(world); -ecs_entity_t Cockpit = ecs_new_id(world); +```cs +Entity FastFrigate = world.Entity() + .IsA(Frigate) + .Set(new(200)); -ecs_add_pair(world, Cockpit, EcsChildOf, Spaceship); -``` -```cpp -auto Spaceship = world.entity(); -auto Cockpit = world.entity(); - -Cockpit.add(flecs::ChildOf, Spaceship); -``` - -In C++, adding a `ChildOf` relationship has a shortcut: +// Obtain the overridden component from FastFrigate +ref readonly MaxSpeed s = ref Frigate.Get(); +s.Value == 200; // true -```cpp -Cockpit.child_of(Spaceship); +// Obtain the inherited component from Frigate +ref readonly Defense d = ref Frigate.Get(); +d.Value == 75; // true ``` -The `ChildOf` relationship is defined so that when a parent is deleted, its children are also deleted. For more information on specifying cleanup behavior for relationships, see the [Relationship cleanup properties](#cleanup-properties) section. +
  • +
  • Rust -The `ChildOf` relationship is defined as a regular relationship in Flecs. There are however a number of features that interact with `ChildOf`. The following sections describe these features. +```rust +let fast_frigate = world.entity().is_a_id(frigate).set(MaxSpeed { value: 200 }); -#### Namespacing -Entities in flecs can have names, and name lookups can be relative to a parent. Relative name lookups can be used as a namespacing mechanism to prevent clashes between entity names. This example shows a few examples of name lookups in combination with hierarchies: - -```c -// Create two entities with a parent/child name -ecs_entity_t parent = ecs_entity(world, { - .name = "Parent" +// Obtain the overridden component from FastFrigate +let is_200 = fast_frigate.map::<&mut MaxSpeed, _>(|v| { + v.value == 200 // True }); -ecs_entity_t child = ecs_entity(world, { - .name = "Child" +// Obtain the inherited component from Frigate +let is_75 = fast_frigate.map::<&mut Defense, _>(|v| { + v.value == 75 // True }); - -// Create the hierarchy -ecs_add_pair(world, child, EcsChildOf, parent); - -child = ecs_lookup_fullpath(world, "Parent::Child"); // true -child = ecs_lookup_path(world, parent, "Child"); // true ``` -```cpp -auto parent = world.entity("Parent"); -auto child = world.entity("Child") - .child_of(parent); - -child == world.lookup("Parent::Child"); // true -child == parent.lookup("Child"); // true -``` - -#### Scoping -In some scenarios a number of entities all need to be created with the same parent. Rather than adding the relationship to each entity, it is possible to configure the parent as a scope, which ensures that all entities created afterwards are created in the scope. The following example shows how: -```c -ecs_entity_t parent = ecs_new_id(world); -ecs_entity_t prev = ecs_set_scope(world, parent); - -// Note that we're not using the ecs_new_id function for the children. This -// function only generates a new id, and does not add the scope to the entity. -ecs_entity_t child_a = ecs_new(world, 0); -ecs_entity_t child_b = ecs_new(world, 0); +
  • +
+
-// Restore the previous scope -ecs_set_scope(world, prev); +This ability to inherit and override components is one of the key enabling features of Flecs prefabs, and is further explained in the [Inheritance section](Manual.md#Inheritance) of the manual. -ecs_has_pair(world, child_a, EcsChildOf, parent); // true -ecs_has_pair(world, child_b, EcsChildOf, parent); // true -``` -```cpp -auto parent = world.entity(); -auto prev = world.set_scope(parent); +### The ChildOf relationship +The `ChildOf` relationship is the builtin relationship that allows for the creation of entity hierarchies. The following example shows how hierarchies can be created with `ChildOf`: -auto child_a = world.entity(); -auto child_b = world.entity(); +
+
    +
  • C -// Restore the previous scope -world.set_scope(prev); +```c +ecs_entity_t Spaceship = ecs_new(world); +ecs_entity_t Cockpit = ecs_new(world); -child_a.has(flecs::ChildOf, parent); // true -child_b.has(flecs::ChildOf, parent); // true +ecs_add_pair(world, Cockpit, EcsChildOf, Spaceship); ``` -Scopes in C++ can also be used with the `scope` function on an entity, which accepts a (typically lambda) function: +
  • +
  • C++ ```cpp -auto parent = world.entity().scope([&]{ - auto child_a = world.entity(); - auto child_b = world.entity(); - - child_a.has(flecs::ChildOf, parent); // true - child_b.has(flecs::ChildOf, parent); // true -}); -``` - -Scopes are the mechanism that ensure contents of a module are created as children of the module, without having to explicitly add the module as a parent. - -## Cleanup properties -When entities that are used as tags, components, relationships or relationship targets are deleted, cleanup policies ensure that the store does not contain any dangling references. Any cleanup policy provides this guarantee, so while they are configurable, applications cannot configure policies that allows for dangling references. - -**Note**: this only applies to entities (like tags, components, relationships) that are added _to_ other entities. It does not apply to components that store an entity value, so: +auto Spaceship = world.entity(); +auto Cockpit = world.entity(); -```c -struct MyComponent { - entity e; // not covered by cleanup policies -} -``` -```c -e.add(ChildOf, parent); // covered by cleanup policies +Cockpit.add(flecs::ChildOf, Spaceship); ``` -The default policy is that any references to the entity will be **removed**. For example, when the tag `Archer` is deleted, it will be removed from all entities that have it, which is similar to invoking the `remove_all` operation: +
  • +
  • C# -```c -ecs_remove_all(world, Archer); -``` -```cpp -world.remove_all(Archer); -``` +```cs +Entity Spaceship = world.Entity(); +Entity Cockpit = world.Entity(); -Since entities can be used in relationship pairs, just calling `remove_all` on just the entity itself does not guarantee that no dangling references are left. A more comprehensive description of what happens is: - -```c -ecs_remove_all(world, Archer); -ecs_remove_all(world, ecs_pair(Archer, EcsWildcard)); -ecs_remove_all(world, ecs_pair(EcsWildcard, Archer)); +Cockpit.Add(Ecs.ChildOf, Spaceship); ``` -```cpp -world.remove_all(Archer); -world.remove_all(Archer, flecs::Wildcard); -world.remove_all(flecs::Wildcard, Archer); -``` - -This succeeds in removing all possible references to `Archer`. Sometimes this behavior is not what we want however. Consider a parent-child hierarchy, where we want to delete the child entities when the parent is deleted. Instead of removing `(ChildOf, parent)` from all children, we need to _delete_ the children. - -We also want to specify this per relationship. If an entity has `(Likes, parent)` we may not want to delete that entity, meaning the cleanup we want to perform for `Likes` and `ChildOf` may not be the same. - -This is what cleanup policies are for: to specify which action needs to be executed under which condition. They are applied _to_ entities that have a reference to the entity being deleted: if I delete the `Archer` tag I remove the tag _from_ all entities that have it. - -To configure a cleanup policy for an entity, a `(Condition, Action)` pair can be added to it. If no policy is specified, the default cleanup action (`Remove`) is performed. - -There are three cleanup actions: - -- `Remove`: as if doing `remove_all(entity)` (default) -- `Delete`: as if doing `delete_with(entity)` -- `Panic`: throw a fatal error (default for components) - -There are two cleanup conditions: - -- `OnDelete`: the component, tag or relationship is deleted -- `OnDeleteTarget`: a target used with the relationship is deleted - -Policies apply to both regular and pair instances, so to all entities with `T` as well as `(T, *)`. - -### Examples -The following examples show how to use cleanup policies - -**(OnDelete, Remove)** -```c -// Remove Archer from entities when Archer is deleted -ECS_TAG(world, Archer); -ecs_add_pair(world, Archer, EcsOnDelete, EcsRemove); -ecs_entity_t e = ecs_new_w_id(world, Archer); +
  • +
  • Rust -// This will remove Archer from e -ecs_delete(world, Archer); +```rust +let spaceship = world.entity(); +let cockpit = world.entity(); +cockpit.add_id((flecs::ChildOf::ID, spaceship)); ``` -```cpp -// Remove Archer from entities when Archer is deleted -world.component() - .add(flecs::OnDelete, flecs::Remove); -auto e = world.entity().add(); +
  • +
+
-// This will remove Archer from e -world.component().destruct(); -``` +In C++, C# and Rust, adding a `ChildOf` relationship has a shortcut: -**(OnDelete, Delete)** -```c -// Delete entities with Archer when Archer is deleted -ECS_TAG(world, Archer); -ecs_add_pair(world, Archer, EcsOnDelete, EcsDelete); +
+
    +
  • C++ -ecs_entity_t e = ecs_new_w_id(world, Archer); - -// This will delete e -ecs_delete(world, Archer); -``` ```cpp -// Delete entities with Archer when Archer is deleted -world.component() - .add(flecs::OnDelete, flecs::Delete); - -auto e = world.entity().add(); - -// This will delete e -world.component().destruct(); -``` - -**(OnDeleteTarget, Delete)** -```c -// Delete children when deleting parent -ECS_TAG(world, ChildOf); -ecs_add_pair(world, ChildOf, EcsOnDeleteTarget, EcsDelete); - -ecs_entity_t p = ecs_new_id(world); -ecs_entity_t e = ecs_new_w_pair(world, ChildOf, p); - -// This will delete both p and e -ecs_delete(world, p); +Cockpit.child_of(Spaceship); ``` -```cpp -// Delete children when deleting parent -world.component() - .add(flecs::OnDeleteTarget, flecs::Delete); -auto p = world.entity(); -auto e = world.entity().add(p); +
  • +
  • C# -// This will delete both p and e -p.destruct(); +```cs +Cockpit.ChildOf(Spaceship); ``` -### Cleanup order -While cleanup actions allow for specifying what needs to happen when a particular entity is deleted, or when an entity used with a particular relationship is deleted, they do not enforce a strict cleanup _order_. The reason for this is that there can be many orderings that satisfy the cleanup policies. - -This is important to consider especially when writing `OnRemove` triggers or hooks, as the order in which they are invoked highly depends on the order in which entities are cleaned up. - -Take an example with a parent and a child that both have the `Node` tag: +
  • +
  • Rust -```cpp -world.observer() - .event(flecs::OnRemove) - .each([](flecs::entity e) { }); - -flecs::entity p = world.entity().add(); -flecs::entity c = world.entity().add().child_of(p); +```rust +cockpit.child_of_id(spaceship); ``` -In this example, when calling `p.destruct()` the observer is first invoked for the child, and then for the parent, which is to be expected as the child is deleted before the parent. Cleanup policies do not however guarantee that this is always the case. - -An application could also call `world.component().destruct()` which would delete the `Node` component and all of its instances. In this scenario the cleanup policies for the `ChildOf` relationship are not considered, and therefore the ordering is undefined. Another typical scenario in which ordering is undefined is when an application has cyclical relationships with a `Delete` cleanup action. - -#### Cleanup order during world teardown -Cleanup issues often show up during world teardown as the ordering in which entities are deleted is controlled by the application. While world teardown respects cleanup policies, there can be many entity delete orderings that are valid according to the cleanup policies, but not all of them are equally useful. There are ways to organize entities that helps world cleanup to do the right thing. These are: - -**Organize components, triggers, observers and systems in modules.** -Storing these entities in modules ensures that they stay alive for as long as possible. This leads to more predictable cleanup ordering as components will be deleted as their entities are, vs. when the component is deleted. It also ensures that triggers and observers are not deleted while matching events are still being generated. - -**Avoid organizing components, triggers, observers and systems under entities that are not modules**. If a non-module entity with children is stored in the root, it will get cleaned up along with other regular entities. If you have entities such as these organized in a non-module scope, consider adding the `EcsModule`/`flecs::Module` tag to the root of that scope. - -The next section goes into more detail on why this improves cleanup behavior and what happens during world teardown. - -#### World teardown sequence -To understand why some ways to organize entities work better than others, having an overview of what happens during world teardown is useful. Here is a list of the steps that happen when a world is deleted: - -1. **Find all root entities** -World teardown starts by finding all root entities, which are entities that do not have the builtin `ChildOf` relationship. Note that empty entities (entities without any components) are not found during this step. +
  • +
+
-2. **Filter out modules, components, observers and systems** -This ensures that components are not cleaned up before the entities that use them, and triggers, observers and systems are not cleaned up while there are still conditions under which they could be invoked. - -3. **Filter out entities that have no children** -If entities have no children they cannot cause complex cleanup logic. This also decreases the likelihood of initiating cleanup actions that could impact other entities. - -4. **Delete root entities** -The root entities that were not filtered out will be deleted. +The `ChildOf` relationship is defined so that when a parent is deleted, its children are also deleted. For more information on specifying cleanup behavior for relationships, see the [Relationship cleanup properties](#cleanup-properties) section. -5. **Delete everything else** -The last step will delete all remaining entities. At this point cleanup policies are no longer considered and cleanup order is undefined. +The `ChildOf` relationship is defined as a regular relationship in Flecs. There are however a number of features that interact with `ChildOf`. The following sections describe these features. -## Relationship properties -Relationship properties are tags that can be added to relationships to modify their behavior. +#### Namespacing +Entities in flecs can have names, and name lookups can be relative to a parent. Relative name lookups can be used as a namespacing mechanism to prevent clashes between entity names. This example shows a few examples of name lookups in combination with hierarchies: -### Tag property -A relationship can be marked as a tag in which case it will never contain data. By default the data associated with a pair is determined by whether either the relationship or target are components. For some relationships however, even if the target is a component, no data should be added to the relationship. Consider the following example: +
+
    +
  • C ```c -typedef struct { - float x; - float y; -} Position; - -ECS_TAG(world, Serializable); -ECS_COMPONENT(world, Position); - -ecs_entity_t e = ecs_new_id(world); -ecs_set(world, e, Position, {10, 20}); -ecs_add_pair(world, e, Serializable, ecs_id(Position)); - -// Gets value from Position component -const Position *p = ecs_get(world, e, Position); - -// Gets (unintended) value from (Serializable, Position) pair -const Position *p = ecs_get_pair_object(world, e, Serializable, Position); -``` -```cpp -struct Serializable { }; // Tag, contains no data - -struct Position { - float x, y; -}; +// Create two entities with a parent/child name +ecs_entity_t parent = ecs_entity(world, { + .name = "Parent" +}); -auto e = ecs.entity() - .set({10, 20}) - .add(); // Because Serializable is a tag, the pair - // has a value of type Position +ecs_entity_t child = ecs_entity(world, { + .name = "Child" +}); -// Gets value from Position component -const Position *p = e.get(); +// Create the hierarchy +ecs_add_pair(world, child, EcsChildOf, parent); -// Gets (unintended) value from (Serializable, Position) pair -const Position *p = e.get(); +child = ecs_lookup(world, "Parent::Child"); // true +child = ecs_lookup_from(world, parent, "Child"); // true ``` -To prevent data from being associated with pairs that can apply to components, the `Tag` property can be added to relationships: - -```c -// Ensure that Serializable never contains data -ecs_add_id(world, Serializable, EcsTag); - -// Because Serializable is marked as a Tag, no data is added for the pair -// even though Position is a component -ecs_add_pair(world, e, Serializable, ecs_id(Position)); +
  • +
  • C++ -// This is still OK -const Position *p = ecs_get(world, e, Position); - -// This no longer works, the pair has no data -const Position *p = ecs_get_pair_object(world, e, Serializable, Position); -``` ```cpp -// Ensure that Serializable never contains data -ecs.component() - .add(); - -auto e = ecs.entity() - .set({10, 20}) - .add(); // Because Serializable marked as a Tag, no - // data is added for the pair even though - // Position is a component - -// Gets value from Position component -const Position *p = e.get(); +auto parent = world.entity("Parent"); +auto child = world.entity("Child") + .child_of(parent); -// This no longer works, the pair has no data -const Position *p = e.get(); +child == world.lookup("Parent::Child"); // true +child == parent.lookup("Child"); // true ``` -The `Tag` property is only interpreted when it is added to the relationship part of a pair. +
  • +
  • C# -### Final property -Entities can be annotated with the `Final` property, which prevents using them with `IsA` relationship. This is similar to the concept of a final class as something that cannot be extended. The following example shows how use `Final`: +```cs +Entity parent = world.Entity("Parent"); +Entity child = world.Entity("Child") + .ChildOf(parent); -```c -ecs_entity_t e = ecs_new_id(world); -ecs_add_id(world, e, EcsFinal); - -ecs_entity_t i = ecs_new_id(world); -ecs_add_pair(world, e, i, EcsIsA, e); // not allowed +child == world.Lookup("Parent.Child"); // true +child == parent.Lookup("Child"); // true ``` -```cpp -auto e = ecs.entity() - .add(flecs::Final); -auto i = ecs.entity() - .is_a(e); // not allowed -``` +
  • +
  • Rust -Queries may use the final property to optimize, as they do not have to explore subsets of a final entity. For more information on how queries interpret final, see the [Query manual](Queries.md). By default, all components are created as final. +```rust +let parent = world.entity_named("Parent"); +let child = world.entity_named("Child").child_of_id(parent); -### DontInherit property -The `DontInherit` property prevents inheriting a component from a base entity (`IsA` target). Consider the following example: - -```c -ecs_entity_t TagA = ecs_new_id(world); -ecs_entity_t TagB = ecs_new_id(world); -ecs_add_id(world, TagB, EcsDontInherit); - -ecs_entity_t base = ecs_new_id(world); -ecs_add_id(world, base, TagA); -ecs_add_id(world, base, TagB); - -ecs_entity_t inst = ecs_new_w_pair(world, base); -ecs_has_id(world, inst, TagA); // true -ecs_has_id(world, inst, TagB); // false +child == world.lookup("Parent::Child"); // true +child == parent.lookup("Child"); // true ``` -```cpp -struct TagA = { }; -struct TagB = { }; - -world.component().add(flecs::DontInherit); -auto base = world.entity() - .add() - .add(); - -auto inst = world.entity().is_a(base); -inst.has(); // true -inst.has(); // false -``` +
  • +
+
-The builtin `Prefab`, `Disabled`, `Identifier` and `ChildOf` tags/relationships are marked as `DontInherit`. +#### Scoping +In some scenarios a number of entities all need to be created with the same parent. Rather than adding the relationship to each entity, it is possible to configure the parent as a scope, which ensures that all entities created afterwards are created in the scope. The following example shows how: -### AlwaysOverride property -The `AlwaysOverride` property ensures that a component is always automatically overridden when an inheritance (`IsA`) relationship is added. The behavior of this property is as if `OVERRIDE | Component` is always added together with `Component`. +
+
    +
  • C ```c -ECS_COMPONENT(world, Position); -ECS_COMPONENT(world, Velocity); - -ecs_add_id(world, ecs_id(Position), EcsAlwaysOverride); - -ecs_entity_t base = ecs_new_id(world); -ecs_set(world, base, Position, {10, 20}); -ecs_set(world, base, Velocity, {1, 2}); - -ecs_entity_t inst = ecs_new_w_pair(world, base); -ecs_has(world, inst, Position); // true -ecs_has(world, inst, Velocity); // true -ecs_owns(world, inst, Position); // true -ecs_owns(world, inst, Velocity); // false -``` -```cpp -world.component().add(flecs::AlwaysOverride); - -auto base = world.entity() - .set({10, 20}) - .add({1, 2}) - -auto inst = world.entity().is_a(base); -inst.has(); // true -inst.has(); // true -inst.owns(); // true -inst.owns(); // false -``` - -### Transitive property -Relationships can be marked as transitive. A formal-ish definition if transitivity in the context of relationships is: - -``` -If Relationship(EntityA, EntityB) And Relationship(EntityB, EntityC) Then Relationship(EntityA, EntityC) -``` - -What this means becomes more obvious when translated to a real-life example: - -``` -If Manhattan is located in New York, and New York is located in the USA, then Manhattan is located in the USA. -``` - -In this example, `LocatedIn` is the relationship and `Manhattan`, `New York` and `USA` are entities `A`, `B` and `C`. Another common example of transitivity is found in OOP inheritance: - -``` -If a Square is a Rectangle and a Rectangle is a Shape, then a Square is a Shape. -``` - -In this example `IsA` is the relationship and `Square`, `Rectangle` and `Shape` are the entities. - -When relationships in Flecs are marked as transitive, queries can follow the transitive relationship to see if an entity matches. Consider this example dataset: +ecs_entity_t parent = ecs_new(world); +ecs_entity_t prev = ecs_set_scope(world, parent); -```c -ecs_entity_t LocatedIn = ecs_new_id(world); -ecs_entity_t Manhattan = ecs_new_id(world); -ecs_entity_t NewYork = ecs_new_id(world); -ecs_entity_t USA = ecs_new_id(world); +// Note that we're not using the ecs_new_id function for the children. This +// function only generates a new id, and does not add the scope to the entity. +ecs_entity_t child_a = ecs_new(world); +ecs_entity_t child_b = ecs_new(world); -ecs_add_pair(world, Manhattan, LocatedIn, NewYork); -ecs_add_pair(world, NewYork, LocatedIn, USA); -``` -```cpp -auto LocatedIn = world.entity(); -auto Manhattan = world.entity(); -auto NewYork = world.entity(); -auto USA = world.entity(); +// Restore the previous scope +ecs_set_scope(world, prev); -Manhattan.add(LocatedIn, NewYork); -NewYork.add(LocatedIn, USA); +ecs_has_pair(world, child_a, EcsChildOf, parent); // true +ecs_has_pair(world, child_b, EcsChildOf, parent); // true ``` -If we were now to query for `(LocatedIn, USA)` we would only match `NewYork`, because we never added `(LocatedIn, USA)` to `Manhattan`. To make sure queries `Manhattan` as well we have to make the `LocatedIn` relationship transitive. We can simply do this by adding the transitive property to the relationship entity: +
  • +
  • C++ -```c -ecs_add_id(world, LocatedIn, Transitive); -``` ```cpp -LocatedIn.add(flecs::Transitive); -``` - -When now querying for `(LocatedIn, USA)`, the query will follow the `LocatedIn` relationship and return both `NewYork` and `Manhattan`. For more details on how queries use transitivity, see the [Transitive Relationships section in the query manual](Queries.md#transitive-relationships). - -### Reflexive property -A relationship can be marked reflexive which means that a query like `Relationship(Entity, Entity)` should evaluate to true. The utility of `Reflexive` becomes more obvious with an example: - -Given this dataset: -``` -IsA(Oak, Tree) -``` - -we can ask whether an oak is a tree: -``` -IsA(Oak, Tree) -- Yes, an Oak is a tree (Oak has (IsA, Tree)) -``` - -We can also ask whether a tree is a tree, which it obviously is: -``` -IsA(Tree, Tree) -- Yes, even though Tree does not have (IsA, Tree) -``` - -However, this does not apply to all relationships. Consider a dataset with a `LocatedIn` relationship: - -``` -LocatedIn(SanFrancisco, UnitedStates) -``` - -we can now ask whether SanFrancisco is located in SanFrancisco, which it is not: -``` -LocatedIn(SanFrancisco, SanFrancisco) -- No -``` - -In these examples, `IsA` is a reflexive relationship, whereas `LocatedIn` is not. - -### Acyclic property -A relationship can be marked with the `Acyclic` property to indicate that it cannot contain cycles. Both the builtin `ChildOf` and `IsA` relationships are marked acyclic. Knowing whether a relationship is acyclic allows the storage to detect and throw errors when a cyclic relationship is introduced by accident. - -Note that because cycle detection requires expensive algorithms, adding `Acyclic` to a relationship does not guarantee that an error will be thrown when a cycle is accidentally introduced. While detection may improve over time, an application that runs without errors is no guarantee that it does not contain acyclic relationships with cycles. - -### Traversable property -Traversable relationships are allowed to be traversed automatically by queries, for example using the `up` bitflag (upwards traversal, see [query traversal flags](Queries.md#traversal-flags)). Traversable relationships are also marked as `Acyclic`, which ensures a query won't accidentally attempt to traverse a relationship that contains cycles. - -Events are propagated along the edges of traversable relationships. A typical example of this is when a component value is changed on a prefab. The event of this change will be propagated by traversing the `IsA` relationship downwards, for all instances of the prefab. Event propagation does not happen for relationships that are not marked with `Traversable`. +auto parent = world.entity(); +auto prev = world.set_scope(parent); -### Exclusive property -The `Exclusive` property enforces that an entity can have only a single instance of a relationship. When a second instance is added, it replaces the first instance. An example of a relationship with the `Exclusive` property is the builtin `ChildOf` relationship: +auto child_a = world.entity(); +auto child_b = world.entity(); -```c -ecs_add_pair(world, child, EcsChildOf, parent_a); -ecs_add_pair(world, child, EcsChildOf, parent_b); // replaces (ChildOf, parent_a) -``` -```cpp -e.child_of(parent_a); -e.child_of(parent_b); // replaces (ChildOf, parent_a) -``` +// Restore the previous scope +world.set_scope(prev); -To create a custom exclusive relationship, add the `Exclusive` property: -```c -ecs_entity_t MarriedTo = ecs_new_id(world); -ecs_add_id(world, MarriedTo, EcsExclusive); -``` -```cpp -flecs::entity MarriedTo = world.entity() - .add(flecs::Exclusive); +child_a.has(flecs::ChildOf, parent); // true +child_b.has(flecs::ChildOf, parent); // true ``` -### Union property -The `Union` is similar to `Exclusive` in that it enforces that an entity can have only a single instance of a relationship. The difference between `Exclusive` and `Union` is that `Union` combines different relationship targets in a single table. This reduces table fragmentation, and as a result speeds up add/remove operations. This increase in add/remove speed does come at a cost: iterating a query with union terms is more expensive than iterating a regular relationship. +
  • +
  • C# -The API for using the `Union` property is similar to regular relationships, as this example shows: +```cs +Entity parent = world.Entity(); +Entity prev = world.SetScope(parent); -```c -ecs_entity_t Movement = ecs_new_id(world); -ecs_add_id(world, Movement, EcsUnion); +Entity childA = world.Entity(); +Entity childB = world.Entity(); -ecs_entity_t Walking = ecs_new_id(world); -ecs_entity_t Running = ecs_new_id(world); +// Restore the previous scope +world.SetScope(prev); -ecs_entity_t e = ecs_new_id(world); -ecs_add_pair(world, e, Movement, Running); -ecs_add_pair(world, e, Movement, Walking); // replaces (Movement, Running) +childA.Has(Ecs.ChildOf, parent); // true +childB.Has(Ecs.ChildOf, parent); // true ``` -```cpp -flecs::entity Movement = world.entity().add(flecs::Union); -flecs::entity Walking = world.entity(); -flecs::entity Running = world.entity(); -flecs::entity e = world.entity().add(Movement, Running); -e.add(Movement, Walking); // replaces (Movement, Running) -``` +
  • +
  • Rust -When compared to regular relationships, union relationships have some differences and limitations: -- Relationship cleanup does not work yet for union relationships -- Removing a union relationship removes any target, even if the specified target is different -- Filters and rules do not support union relationships -- Union relationships cannot have data -- Union relationship query terms can use only the `And` operator -- Queries with a `(R, *)` term will return `(R, *)` as term id for each entity +```rust +let parent = world.entity(); -### Symmetric property -The `Symmetric` property enforces that when a relationship `(R, Y)` is added to entity `X`, the relationship `(R, X)` will be added to entity `Y`. The reverse is also true, if relationship `(R, Y)` is removed from `X`, relationship `(R, X)` will be removed from `Y`. +let prev = world.set_scope_id(parent); -The symmetric property is useful for relationships that do not make sense unless they are bidirectional. Examples of such relationships are `AlliesWith`, `MarriedTo`, `TradingWith` and so on. An example: +let child_a = world.entity(); +let child_b = world.entity(); -```c -ecs_entity_t MarriedTo = ecs_new_w_id(world, EcsSymmetric); -ecs_entity_t Bob = ecs_new_id(world); -ecs_entity_t Alice = ecs_new_id(world); -ecs_add_pair(world, Bob, MarriedTo, Alice); // Also adds (MarriedTo, Bob) to Alice -``` -```cpp -auto MarriedTo = world.entity().add(flecs::Symmetric); -auto Bob = ecs.entity(); -auto Alice = ecs.entity(); -Bob.add(MarriedTo, Alice); // Also adds (MarriedTo, Bob) to Alice -``` - -### With property -The `With` relationship can be added to components to indicate that it must always come together with another component. The following example shows how `With` can be used with regular components/tags: - -```c -ecs_entity_t Responsibility = ecs_new_id(world); -ecs_entity_t Power = ecs_new_w_pair(world, EcsWith, Responsibility); +// Restore the previous scope +world.set_scope_id(prev); -// Create new entity that has both Power and Responsibility -ecs_entity_t e = ecs_new_w_id(world, Power); +child_a.has_id((flecs::ChildOf::ID, parent)); // true +child_b.has_id((flecs::ChildOf::ID, parent)); // true ``` -```cpp -auto Responsibility = world.entity(); -auto Power = world.entity().add(flecs::With, Responsibility); -// Create new entity that has both Power and Responsibility -auto e = world.entity().add(Power); -``` +
  • +
+
-When the `With` relationship is added to a relationship, the additional id added to the entity will be a relationship pair as well, with the same target as the original relationship: +Scopes in C++, C# and Rust can also be used with the `scope`/`Scope`/`run_in_scope` function on an entity, which accepts a (typically lambda) function: -```c -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Loves = ecs_new_w_pair(world, EcsWith, Likes); -ecs_entity_t Pears = ecs_new_id(world); +
+
    +
  • C++ -// Create new entity with both (Loves, Pears) and (Likes, Pears) -ecs_entity_t e = ecs_new_w_pair(world, Loves, Pears); -``` ```cpp -auto Likes = world.entity(); -auto Loves = world.entity().add(flecs::With, Likes); -auto Pears = world.entity(); +auto parent = world.entity().scope([&]{ + auto child_a = world.entity(); + auto child_b = world.entity(); -// Create new entity with both (Loves, Pears) and (Likes, Pears) -auto e = world.entity().add(Loves, Pears); + child_a.has(flecs::ChildOf, parent); // true + child_b.has(flecs::ChildOf, parent); // true +}); ``` -### OneOf property -The `OneOf` property enforces that the target of the relationship is a child of a specified entity. `OneOf` can be used to indicate that the target needs to be either a child of the relationship (common for enum relationships), or of another entity. +
  • +
  • C# -The following example shows how to constrain the relationship target to a child of the relationship: +```cs +Entity parent = world.Entity().Scope(() => +{ + Entity childA = world.Entity(); + Entity childB = world.Entity(); -```c -ecs_entity_t Food = ecs_new_id(world); - -// Enforce that target of relationship is child of Food -ecs_add_id(world, Food, EcsOneOf); - -ecs_entity_t Apples = ecs_new_w_pair(world, EcsChildOf, Food); -ecs_entity_t Fork = ecs_new_id(world); - -// This is ok, Apples is a child of Food -ecs_entity_t a = ecs_new_w_pair(world, Food, Apples); - -// This is not ok, Fork is not a child of Food -ecs_entity_t b = ecs_new_w_pair(world, Food, Fork); -``` -```cpp -// Enforce that target of relationship is child of Food -auto Food = world.entity().add(flecs::OneOf); -auto Apples = world.entity().child_of(Food); -auto Fork = world.entity(); - -// This is ok, Apples is a child of Food -auto a = world.entity().add(Food, Apples); - -// This is not ok, Fork is not a child of Food -auto b = world.entity().add(Food, Fork); + childA.Has(Ecs.ChildOf, parent); // true + childB.Has(Ecs.ChildOf, parent); // true +}); ``` -The following example shows how `OneOf` can be used to enforce that the relationship target is the child of an entity other than the relationship: - -```c -ecs_entity_t Food = ecs_new_id(world); -ecs_entity_t Eats = ecs_new_id(world); - -// Enforce that target of relationship is child of Food -ecs_add_pair(world, Eats, EcsOneOf, Food); +
  • +<
  • Rust -ecs_entity_t Apples = ecs_new_w_pair(world, EcsChildOf, Food); -ecs_entity_t Fork = ecs_new_id(world); - -// This is ok, Apples is a child of Food -ecs_entity_t a = ecs_new_w_pair(world, Eats, Apples); - -// This is not ok, Fork is not a child of Food -ecs_entity_t b = ecs_new_w_pair(world, Eats, Fork); +```rust +let parent = world.entity().run_in_scope(|| { + let child_a = world.entity(); + let child_b = world.entity(); + child_a.has_id((flecs::ChildOf::ID, parent)); // true + child_b.has_id((flecs::ChildOf::ID, parent)); // true +}); ``` -```cpp -// Enforce that target of relationship is child of Food -auto Food = world.entity(); -auto Eats = world.entity().add(flecs::OneOf, Food); -auto Apples = world.entity().child_of(Food); -auto Fork = world.entity(); -// This is ok, Apples is a child of Food -auto a = world.entity().add(Eats, Apples); +
  • +
+
-// This is not ok, Fork is not a child of Food -auto b = world.entity().add(Eats, Fork); -``` +Scopes are the mechanism that ensure contents of a module are created as children of the module, without having to explicitly add the module as a parent. ## Relationship performance This section goes over the performance implications of using relationships. @@ -1333,11 +1839,11 @@ Because of this, adding/removing relationships to entities has the same performa ECS_COMPONENT(world, Position); // Tags -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Apples = ecs_new_id(world); -ecs_entity_t Npc = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Apples = ecs_new(world); +ecs_entity_t Npc = ecs_new(world); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); // The ecs_add_id function can be used to add one id to another ecs_add_id(world, e, Npc); @@ -1372,19 +1878,6 @@ Applications that make extensive use of relationships might observe high levels Fragmentation can be reduced by using [union relationships](#union-property). There are additional storage improvements on the roadmap that will decrease the overhead of fragmentation introduced by relationships. -### Relationship flattening -To reduce fragmentation, a relationship can be flattened by calling the `ecs_flatten` operation. This combines entities from different relationship targets in the same table, provided that they otherwise have the same components. The operation is recursive, meaning that for a specified relationship, the entire subtree is flattened. - -Flattening a subtree can be done with or without preserving depth information of the original tree. Preserving depth information ensures that features like `cascade` still work as usual, whereas not preserving depth information further decreases fragmentation. - -The flattening operation by default removes names of the flattened entities. This behavior can be overridden with an option. When flattening for the builtin `ChildOf` relationship, flattening with preservation of names can cause the operation to fail, if entities from different subtrees have the same name. - -After the flatten operation, entities for the same target are stored in contiguous slices in the new table. The new table will have a `(Target, Relationship)` pair, which contains data about the original target, and the start and number of entities in the contiguous block. This information is used by queries when iterating to ensure entities for the same target can still be iterated in bulk. - -Relationship flattening is an experimental feature, and some limitations apply for the current implementation. After a subtree has been flattened, the entities in that subtree can no longer be individually deleted from a target, and cannot be moved to another parent. Additionally, no components can be added/removed to entities in a flattened subtree. Relationship flattening is currently supported only for exclusive, acyclic relationships. - -For additional information, see the API documentation for the `ecs_flatten` operation. - ### Table Creation When an id added to an entity is deleted, all references to that id are deleted from the storage (see [cleanup properties](#cleanup-properties)). For example, when the component `Position` is deleted it is removed from all entities, and all tables with the `Position` component are deleted. While not unique to relationships, it is more common for relationships to trigger cleanup actions, as relationship pairs contain regular entities. diff --git a/vendors/flecs/docs/RestApi.md b/vendors/flecs/docs/RestApi.md deleted file mode 100644 index 0f1c9098d..000000000 --- a/vendors/flecs/docs/RestApi.md +++ /dev/null @@ -1,355 +0,0 @@ -# REST API -This document provides an overview of the REST API. The Flecs REST API enables (web) clients to inspect the contents of the ECS store, by remotely running queries and requesting entities. - -## Enable the REST API -The REST API can be enabled in an application by instantiating the `EcsRest`/`flecs::Rest` component: - -```c -// Start REST API with default parameters -ecs_singleton_set(world, EcsRest, {0}); -``` -```cpp -// Start REST API with default parameters -world.set({}); -``` - -When an application uses the app addon [FLECS_APP](https://www.flecs.dev/flecs/group__c__addons__app.html) the REST interface can be enabled like this: - -```c -// Start application main loop, enable REST interface -ecs_app_run(world, &(ecs_app_desc_t){ - .enable_rest = true -}); -``` -```cpp -// Start application main loop, enable REST interface -world.app() - .enable_rest() - .run(); -``` - -To test if the REST API is working, navigate to http://localhost:27750/entity/flecs/core/World. Upon success this request should return a reply that looks like: -```json -{"path":"World", "ids":[["flecs.rest.Rest"], ["flecs.core.Identifier", "flecs.core.Name"], ["flecs.core.Identifier", "flecs.core.Symbol"], ["flecs.core.ChildOf", "flecs.core"], ["flecs.doc.Description", "flecs.core.Name"], ["flecs.doc.Description", "flecs.doc.Brief"]]} -``` - -When the monitor module is imported, the REST API provides a `stats` endpoint with statistics for different time intervals: - -```c -// Import monitor addon -ECS_IMPORT(world, FlecsMonitor); -``` -```cpp -world.import(); -``` - -When an application uses the app addon [FLECS_APP](https://www.flecs.dev/flecs/group__c__addons__app.html) the monitoring can be enabled like this: -```c -// Start application main loop, enable REST interface and monitoring -ecs_app_run(world, &(ecs_app_desc_t){ - .enable_rest = true, - .enable_monitor = true -}); -``` -```cpp -// Start application main loop, enable REST interface and monitoring -world.app() - .enable_rest() - .enable_monitor() - .run(); -``` - -For the full C/C++ API reference [see the REST addon documentation](https://www.flecs.dev/flecs/group__c__addons__rest.html). - -## Explorer -An application with REST enabled can be remotely monitored with the [Flecs Explorer](https://github.com/flecs-hub/explorer). When the application is running with REST enabled, navigate to https://flecs.dev/explorer. This should connect the Explorer to the application. - -When the connection is successful, the Explorer should look similar to this: - -![Remote Explorer](img/explorer-remote.png) - -The remote icon next to the title should be visible. If the connection is not successful it could be that the explorer did not receive a response fast enough. To force the explorer to connect remotely, add `?remote=true` to the request: https://flecs.dev/explorer?remote=true. - -If connection issues persist, the browser could be preventing connections to a local application. See the [Explorer repository README](https://github.com/flecs-hub/explorer) for more information on how to workaround this and other issues. - -**Tip**: To show the application title in the explorer, pass the command line arguments to `ecs_init`/`flecs::world::world`: - -```c -ecs_world_t *world = ecs_init_w_args(argc, argv); -``` -```c++ -flecs::world world(argc, argc); -``` - -### Queries -The search bar in the explorer makes it possible to directly query the ECS storage. A query can be entered in the "Search" box, and needs to be specified in the query DSL (see [query manual](Queries.md#query-dsl)). An example: - -![Remote Explorer](img/explorer-query.png) - -The default query engine used by the search bar is the rules engine, which means it is possible to use features like query variables: - -![Remote Explorer](img/explorer-rules.png) - -It is also possible to inspect the results of existing queries. This can be done by entering a `?-`, followed by the name of the query. Queries can be given a name by setting the `.entity` field to a named entity in C: - -```c -ecs_query_t *q = ecs_query(world, { - .entity = ecs_entity(world, { .name = "Move" }), - .filter.terms = { - { ecs_id(Position) }, - { ecs_id(Velocity) }, - } -}); -``` - -In C++ a name can be provided to the query factory method: - -```cpp -auto q = world.query("Move"); -``` - -This also works for filters and rules. Because systems are queries, the name of a system can be entered to inspect the entities matched by the system: - -![Remote Explorer](img/explorer-system.png) - -When a named rule query has variables, variables can be optionally provided as arguments to the query. The following example provides the value `Apples` to the query variable `food` for query `eats_query`, which constrains the results to only show results with `Apples`: - -![Remote Explorer](img/explorer-arguments.png) - -The underlying query that was used for this screenshot was created like this: - -```c -ecs_rule(world, { - .entity = ecs_entity(world, { .name = "eats_query" }), - .expr = "(Eats, $food)" -}); -``` - -## Endpoints -This section describes the endpoints of the REST API. - -### entity -``` -GET /entity/ -``` -The entity endpoint requests data from an entity. The path is the entity path or name of the entity to query for. The reply is formatted according to the [JSON serializer Entity](https://www.flecs.dev/flecs/md_docs_JsonFormat.html#autotoc_md405) type. - -The following parameters can be provided to the endpoint: - -#### path -Add path (name) for entity. - -**Default**: true - -#### label -Add label (from doc framework) for entity. - -**Default**: false - -#### brief -Add brief description (from doc framework) for entity. - -**Default**: false - -#### link -Add link (from doc framework) for entity. - -**Default**: false - -#### id_labels -Add labels (from doc framework) for (component) ids. - -**Default**: false - -#### base -Add base components. - -**Default**: true - -#### values -Add component values. - -**Default**: false - -#### private -Add private components. - -**Default**: false - -#### type_info -Add reflection data for component types. - -**Default**: false - -#### alerts -Add active alerts for entity. - -**Default**: false - -#### Example -``` -/entity/my_entity -/entity/my_entity?values=true -/entity/parent/child -``` - -### enable -``` -PUT /enable/ -``` -The enable endpoint enables the entity specified by the path by removing the `flecs.core.Disabled` component. If the entity does not have the `flecs.core.Disabled` component, the endpoint has no side effects. - -### disable -``` -PUT /disable/ -``` -The disable endpoint disables the entity specified by the path by adding the `flecs.core.Disabled` component. If the entity already has the `flecs.core.Disabled` component, the endpoint has no side effects. - -### delete -``` -PUT /delete/ -``` -The delete endpoint deletes the entity specified by the path. - -### set -``` -PUT /set/&data={...} -``` -The set endpoint sets components for the entity specified by the path. The data argument uses the same layout as the `ids` and `values` fields produced by the `ecs_entity_to_json` function. An example: - -```json -{ - "ids": [["Position"], ["Velocity"], ["Eats", "Apples"]], - "values": [ - {"x": 10, "y": 20}, - {"x": 1, "y": 1}, - {"amount": 3} - ] -} -``` - -### query -``` -GET /query?q= -``` -The query endpoint requests data for a query. The implementation uses the -rules query engine. The reply is formatted as an [JSON serializer Iterator](JsonFormat.md#iterator) type. - -The following parameters can be provided to the endpoint: - -#### name -Specify the name of an existing query or system. - -#### offset -Skip the first _offset_ number of entities. - -**Default**: 0 - -#### limit -Return at most _limit_ number of entities. - -**Default**: 100 - -#### term_ids -Add top-level "ids" array with components as specified by query. - -**Default**: false - -#### term_labels -Add top-level "id_labels" array with doc names of components as specified by query. - -**Default**: false - -#### ids -Add result-specific "ids" array with components as matched. Can be different from top-level "ids" array for queries with wildcards. - -**Default**: false - -#### id_labels -Add result-specific "id_labels" array with doc names of components as matched. Can be different from top-level "term_labels" array for queries with wildcards. - -**Default**: false - -#### sources -Add result-specific "sources" array with component source. A 0 element indicates the component is matched on the current (This) entity. - -**Default**: false - -#### variables -Add result-specific "variables" array with values for variables, if any. - -**Default**: true - -#### is_set -Add result-specific "is_set" array with boolean elements indicating whether component was matched (used for optional terms). - -**Default**: false - -#### values -Add result-specific "values" array with component values. A 0 element indicates a component that could not be serialized, which can be either -because no reflection data was registered, because the component has no -data, or because the query didn't request it. - -**Default**: false - -#### entities -Add result-specific "entities" array with matched entities. - -**Default**: true - -#### entity_labels -Include doc name for entities. - -**Default**: false - -#### entity_ids -Include numerical ids for entities. - -**Default**: false - -#### variable_labels -Include doc name for variables. - -**Default**: false - -#### variable_ids -Include numerical ids for variables. - -**Default**: false - -#### duration -Include measurement on how long it took to serialize result. - -**Default**: false - -#### type_info -Add top-level "type_info" array with reflection data on the type in -the query. If a query element has a component that has no reflection -data, a 0 element is added to the array. - -**Default**: false - -#### Example: -``` -/query?q=Position -/query?q=Position&values=true -/query?q=Position%2CVelocity -/query?name=systems.Move -``` - -### stats -``` -GET /stats// -``` -The stats endpoint returns statistics for a specified category or period. This endpoint requires the monitor module to be imported (see above). The supported categories are: - -- `world` -- `pipeline` - -The supported periods are: - -- 1s -- 1m -- 1h -- 1d -- 1w diff --git a/vendors/flecs/docs/Systems.md b/vendors/flecs/docs/Systems.md index 47f20505d..9fcfd1281 100644 --- a/vendors/flecs/docs/Systems.md +++ b/vendors/flecs/docs/Systems.md @@ -2,16 +2,19 @@ Systems are queries + a function that can be ran manually or get scheduled as part of a pipeline. To use systems, applications must build Flecs with the `FLECS_SYSTEM` addon (enabled by default). An example of a simple system: +
+
    +
  • C ```c // System implementation void Move(ecs_iter_t *it) { // Get fields from system query - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); // Iterate matched entities - for (int i = 0; i < it->count, i++) { + for (int i = 0; i < it->count; i++) { p[i].x += v[i].x; p[i].y += v[i].y; } @@ -20,15 +23,6 @@ void Move(ecs_iter_t *it) { // System declaration ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity); ``` -```cpp -// System declaration -flecs::system sys = world.system("Move") - .each([](Position& p, const Velocity &v) { - // Each is invoked for each entity - p.x += v.x; - p.y += v.y; - }); -``` In C, a system can also be created with the `ecs_system_init` function / `ecs_system` shorthand which provides more flexibility. The same system can be created like this: @@ -36,47 +30,164 @@ In C, a system can also be created with the `ecs_system_init` function / `ecs_sy ecs_entity_t ecs_id(Move) = ecs_system(world, { .entity = ecs_entity(world, { /* ecs_entity_desc_t */ .name = "Move", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { /* ecs_filter_desc_t::terms */ + .query.terms = { /* ecs_query_desc_t::terms */ { ecs_id(Position) }, { ecs_id(Velocity), .inout = EcsIn } } .callback = Move }) ``` +
  • +
  • C++ + +```cpp +// System declaration +flecs::system sys = world.system("Move") + .each([](Position& p, const Velocity &v) { + // Each is invoked for each entity + p.x += v.x; + p.y += v.y; + }); +``` +
  • +
  • C# + +```cs +// System declaration +System sys = world.System("Move") + .Each((ref Position p, ref Velocity v) => + { + // Each is invoked for each entity + p.X += v.X; + p.Y += v.Y; + }); +``` +
  • +
  • Rust + +```rust +// System declaration +world + .system_named::<(&mut Position, &Velocity)>("Move") + .each(|(p, v)| { + p.x += v.x; + p.y += v.y; + }); +``` +
  • +
+
To manually run a system, do: +
+
    +
  • C ```c ecs_run(world, ecs_id(Move), 0.0 /* delta_time */, NULL /* param */) ``` +
  • +
  • C++ + ```cpp +flecs::system sys = ...; sys.run(); ``` +
  • +
  • C# + +```cs +System_ sys = ...; +sys.Run(); +``` +
  • +
  • Rust + +```rust +let sys = ...; +sys.run(); +``` +
  • +
+
By default systems are registered for a pipeline which orders systems by their "phase" (`EcsOnUpdate`). To run all systems in a pipeline, do: +
+
    +
  • C ```c ecs_progress(world, 0 /* delta_time */); ``` +
  • +
  • C++ + ```cpp +flecs::world world = ...; world.progress(); ``` +
  • +
  • C# + +```cs +using World world = World.Create(); +world.Progress(); +``` +
  • +
  • Rust + +```rust +let world = World::new(); +world.progress(); +``` +
  • +
+
To run systems as part of a pipeline, applications must build Flecs with the `FLECS_PIPELINE` addon (enabled by default). To prevent a system from being registered as part of a pipeline, specify 0 as phase: +
+
    +
  • C ```c ECS_SYSTEM(world, Move, 0, Position, [in] Velocity); ``` +
  • +
  • C++ + ```cpp flecs::system sys = world.system("Move") .kind(0) .each([](Position& p, const Velocity &v) { /* ... */ }); ``` +
  • +
  • C# + +```cs +System sys = world.System("Move") + .Kind(0) + .Each((ref Position p, ref Velocity v) => { /* ... */ }); +``` +
  • +
  • Rust + +```rust +world + .system_named::<(&mut Position, &Velocity)>("Move") + .kind_id(0) + .each(|(p, v)| { /* ... */ }); +``` +
  • +
+
## System Iteration Because systems use queries, the iterating code looks similar: +
+
    +
  • C ```c // Query iteration @@ -85,11 +196,11 @@ ecs_iter_t it = ecs_query_iter(world, q); // Iterate tables matched by query while (ecs_query_next(&it)) { // Get fields from query - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); // Iterate matched entities - for (int i = 0; i < it->count, i++) { + for (int i = 0; i < it->count; i++) { p[i].x += v[i].x; p[i].y += v[i].y; } @@ -98,20 +209,18 @@ while (ecs_query_next(&it)) { // System code void Move(ecs_iter_t *it) { // Get fields from system query - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); // Iterate matched entities - for (int i = 0; i < it->count, i++) { + for (int i = 0; i < it->count; i++) { p[i].x += v[i].x; p[i].y += v[i].y; } } ``` - -Note how query iteration has an outer and an inner loop, whereas system iteration only has the inner loop. The outer loop for systems is iterated by the `ecs_run` function, which invokes the system function. When running a pipeline, this means that a system callback can be invoked multiple times per frame, once for each matched table. - -In C++ system and query iteration uses the same `each`/`iter` functions: +
  • +
  • C++ ```cpp // Query iteration (each) @@ -122,78 +231,301 @@ world.system("Move") .each([](Position& p, const Velocity &v) { /* ... */ }); ``` ```cpp -// Query iteration (iter) -q.iter([](flecs::iter& it, Position *p, const Velocity *v) { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; +// Query iteration (run) +q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); // System iteration (iter) world.system("Move") - .iter([](flecs::iter& it, Position *p, const Velocity *v) { - for (auto i : it) { + .run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } + } + }); +``` + +The `run()` function can be invoked multiple times per frame, once for each matched table. The `each` function is called once per matched entity. + +Note that there is no significant performance difference between `iter()` and `each`, which can both be vectorized by the compiler. + +
  • +
  • C# + +```cs +// Query iteration (Each) +q.Each((ref Position p, ref Velocity v) => { /* ... */ }); + +// System iteration (Each) +world.System("Move") + .Each((ref Position p, ref Velocity v) => { /* ... */ }); +``` +```cs +// Query iteration (Iter) +q.Iter((Iter it, Field p, Field v) => +{ + foreach (int i in it) + { + p[i].X += v[i].X; + p[i].Y += v[i].Y; + } +}); + +// System iteration (Iter) +world.System("Move") + .Iter((Iter it, Field p, Field v) => + { + foreach (int i in it) + { + p[i].X += v[i].X; + p[i].Y += v[i].Y; + } + }); +``` + +The `Iter` function can be invoked multiple times per frame, once for each matched table. The `Each` function is called once per matched entity. +
  • +
  • Rust + +```rust +// `.each_entity` if you need the associated entity. + +// Query iteration (each) +q.each(|(p, v)| { /* ... */ }); + +// System iteration (each) +world + .system_named::<(&mut Position, &Velocity)>("Move") + .each(|(p, v)| { /* ... */ }); +``` +```rust +// Query iteration (run) +q.run(|mut it| { + while it.next() { + let mut p = it + .field::(0) + .expect("query term changed and not at the same index anymore"); + let v = it + .field::(1) + .expect("query term changed and not at the same index anymore"); + for i in it.iter() { + p[i].x += v[i].x; + p[i].y += v[i].y; + } + } +}); + +// System iteration (run) +world + .system_named::<(&mut Position, &Velocity)>("Move") + .run(|mut it| { + while it.next() { + let mut p = it + .field::(0) + .expect("query term changed and not at the same index anymore"); + let v = it + .field::(1) + .expect("query term changed and not at the same index anymore"); + for i in it.iter() { + p[i].x += v[i].x; + p[i].y += v[i].y; + } + } + }); +``` +```rust +// Query iteration (run_iter) +q.run_iter(|it, (p, v)| { + for i in it.iter() { p[i].x += v[i].x; p[i].y += v[i].y; } - }); +}); + +// System iteration (run_iter) +world + .system_named::<(&mut Position, &Velocity)>("Move") + .run_iter(|it, (p, v)| { + for i in it.iter() { + p[i].x += v[i].x; + p[i].y += v[i].y; + } + }); ``` -Just like in the C API, the `iter` function can be invoked multiple times per frame, once for each matched table. The `each` function is called once per matched entity. +The `run` function can be invoked multiple times per frame, once for each matched table. The `each` function is called once per matched entity. -Note that there is no significant performance difference between `iter` and `each`, which can both be vectorized by the compiler. By default `each` can actually end up being faster, as it is instanced (see [query manual](Queries.md#each-c)). +Note that there is no significant performance difference between `run` and `each`, which can both be vectorized by the compiler. +
  • +
+
+ +Note how query iteration has an outer and an inner loop, whereas system iteration only has the inner loop. The outer loop for systems is iterated by the `ecs_run` function, which invokes the system function. When running a pipeline, this means that a system callback can be invoked multiple times per frame, once for each matched table. ## Using delta_time A system provides a `delta_time` which contains the time passed since the last frame: +
+
    +
  • C ```c -Position *p = ecs_field(it, Position, 1); -Velocity *v = ecs_field(it, Velocity, 2); +Position *p = ecs_field(it, Position, 0); +Velocity *v = ecs_field(it, Velocity, 1); -for (int i = 0; i < it->count, i++) { +for (int i = 0; i < it->count; i++) { p[i].x += v[i].x * it->delta_time; p[i].y += v[i].y * it->delta_time; } ``` +
  • +
  • C++ + ```cpp world.system("Move") - .iter([](flecs::iter& it, size_t, Position& p, const Velocity &v) { + .each([](flecs::iter& it, size_t, Position& p, const Velocity &v) { p.x += v.x * it.delta_time(); p.y += v.y * it.delta_time(); }); world.system("Move") - .iter([](flecs::iter& it, Position *p, const Velocity *v) { - for (auto i : it) { + .run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + for (auto i : it) { + p[i].x += v[i].x * it.delta_time(); + p[i].y += v[i].y * it.delta_time(); + } + } + }); +``` +
  • +
  • C# + +```cs +world.System("Move") + .Each((Iter it, int i, ref Position p, ref Velocity v) => + { + p.X += v.X * it.DeltaTime(); + p.Y += v.Y * it.DeltaTime(); + }); + +world.System("Move") + .Iter((Iter it, Field p, Field v) => + { + foreach (int i in it) + { + p[i].X += v[i].X * it.DeltaTime(); + p[i].Y += v[i].Y * it.DeltaTime(); + } + }); +``` +
  • +
  • Rust + +```rust +world + .system_named::<(&mut Position, &Velocity)>("Move") + .each_iter(|it, i, (p, v)| { + p.x += v.x * it.delta_time(); + p.y += v.y * it.delta_time(); + }); + +world + .system_named::<(&mut Position, &Velocity)>("Move") + .run_iter(|it, (p, v)| { + for i in it.iter() { p[i].x += v[i].x * it.delta_time(); p[i].y += v[i].y * it.delta_time(); } }); ``` +
  • +
+
This is the value passed into `ecs_progress`: +
+
    +
  • C ```c ecs_progress(world, delta_time); ``` +
  • +
  • C++ + ```cpp world.progress(delta_time); ``` +
  • +
  • C# + +```cs +world.Progress(deltaTime); +``` +
  • +
  • Rust + +```rust +world.progress_time(delta_time); +``` +
  • +
+
Passing a value for `delta_time` is useful if you're running `progress()` from within an existing game loop that already has time management. Providing 0 for the argument, or omitting it in the C++ API will cause `progress()` to measure the time since the last call and use that as value for `delta_time`: +
+
    +
  • C ```c ecs_progress(world, 0); ``` +
  • +
  • C++ + ```cpp world.progress(); ``` +
  • +
  • C# + +```cs +world.Progress(); +``` +
  • +
  • Rust + +```rust +world.progress(); +``` +
  • +
+
A system may also use `delta_system_time`, which is the time elapsed since the last time the system was invoked. This can be useful when a system is not invoked each frame, for example when using a timer. ## Tasks A task is a system that matches no entities. Tasks are ran once per frame, and are useful for running code that is not related to entities. An example of a task system: +
+
    +
  • C ```c // System function @@ -208,7 +540,7 @@ ECS_SYSTEM(world, PrintTime, EcsOnUpdate, 0); ecs_system(world, { .entity = ecs_entity(world, { .name = "PrintTime", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = PrintTime }); @@ -216,24 +548,60 @@ ecs_system(world, { // Runs PrintTime ecs_progress(world, 0); ``` +
  • +
  • C++ + ```cpp world.system("PrintTime") .kind(flecs::OnUpdate) - .iter([](flecs::iter& it) { + .run([](flecs::iter& it) { printf("Time: %f\n", it.delta_time()); }); // Runs PrintTime world.progress(); ``` +
  • +
  • C# + +```cs +world.System("PrintTime") + .Kind(Ecs.OnUpdate) + .Iter((Iter it) => + { + Console.WriteLine($"Time: {it.DeltaTime()}"); + }); + +// Runs PrintTime +world.progress(); +``` +
  • +
  • Rust + +```rust +world.system_named::<()>("PrintTime").run(|mut it| { + while it.next() { + println!("Time: {}", it.delta_time()); + } +}); + +// Runs PrintTime +world.progress(); +``` +
  • +
+
Tasks may query for components from a fixed source or singleton: +
+
    +
  • C ```c // System function void PrintTime(ecs_iter_t *it) { // Get singleton component - Game *g = ecs_field(it, Game, 1); + Game *g = ecs_field(it, Game, 0); printf("Time: %f\n", g->time); } @@ -245,24 +613,53 @@ ECS_SYSTEM(world, PrintTime, EcsOnUpdate, Game($)); ecs_system(world, { .entity = ecs_entity(world, { .name = "PrintTime", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { .id = ecs_id(Game), .src.id = ecs_id(Game) } // Singleton source }, .callback = PrintTime }); ``` +
  • +
  • C++ + ```cpp world.system("PrintTime") - .term_at(1).singleton() + .term_at(0).singleton() .kind(flecs::OnUpdate) - .iter([](flecs::iter& it, Game *g) { - printf("Time: %f\n", g->time); + .each([](Game& g) { + printf("Time: %f\n", g.time); }); ``` +
  • +
  • C# -Note that `it->count` is always 0 for tasks, as they don't match any entities. In C++ this means that the function passed to `each` is never invoked for tasks. Applications will have to use `iter` instead when using tasks in C++. +```cs +world.System("PrintTime") + .TermAt(0).Singleton() + .Kind(Ecs.OnUpdate) + .Each((ref Game g) => + { + Console.WriteLine($"Time: {g.Time}"); + }); +``` +
  • +
  • Rust + +```rust +world + .system_named::<&Game>("PrintTime") + .term_at(0) + .singleton() + .kind::() + .run_iter(|it, game| { + println!("Time: {}", game[0].time); + }); +``` +
  • +
+
## Pipelines A pipeline is a list of systems that is executed when the `ecs_progress`/`world::progress` function is invoked. Which systems are part of the pipeline is determined by a pipeline query. A pipeline query is a regular ECS query, which matches system entities. Flecs has a builtin pipeline with a predefined query, in addition to offering the ability to specify a custom pipeline query. @@ -275,11 +672,17 @@ In addition to a system query, pipelines also analyze the components that system ### Builtin Pipeline The builtin pipeline matches systems that depend on a _phase_. A phase is any entity with the `EcsPhase`/`flecs::Phase` tag. To add a dependency on a phase, the `DependsOn` relationship is used. This happens automatically when using the `ECS_SYSTEM` macro/`flecs::system::kind` method: +
+
    +
  • C ```c // System is created with (DependsOn, OnUpdate) ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); ``` +
  • +
  • C++ + ```cpp // System is created with (DependsOn, OnUpdate) world.system("Move") @@ -288,6 +691,33 @@ world.system("Move") // ... }); ``` +
  • +
  • C# + +```cs +// System is created with (DependsOn, OnUpdate) +world.System("Move") + .Kind(Ecs.OnUpdate) + .Each((ref Position p, ref Velocity v) => + { + // ... + }); +``` +
  • +
  • Rust + +```rust + // System is created with (DependsOn, OnUpdate) + world + .system_named::<(&mut Position, &Velocity)>("Move") + .kind::() + .each(|(p, v)| { + // ... + }); +``` +
  • +
+
Systems are ordered using a topology sort on the `DependsOn` relationship. Systems higher up in the topology are ran first. In the following example the order of systems is `InputSystem, MoveSystem, CollisionSystem`: @@ -302,6 +732,9 @@ Systems are ordered using a topology sort on the `DependsOn` relationship. Syste ``` Flecs has the following builtin phases, listed in topology order: +
+
    +
  • C - `EcsOnStart` - `EcsOnLoad` @@ -312,8 +745,8 @@ Flecs has the following builtin phases, listed in topology order: - `EcsPostUpdate` - `EcsPreStore` - `EcsOnStore` - -In C++ the phase identifiers are namespaced: +
  • +
  • C++ - `flecs::OnStart` - `flecs::OnLoad` @@ -324,6 +757,33 @@ In C++ the phase identifiers are namespaced: - `flecs::PostUpdate` - `flecs::PreStore` - `flecs::OnStore` +
  • +
  • C# + +- `Ecs.OnStart` +- `Ecs.OnLoad` +- `Ecs.PostLoad` +- `Ecs.PreUpdate` +- `Ecs.OnUpdate` +- `Ecs.OnValidate` +- `Ecs.PostUpdate` +- `Ecs.PreStore` +- `Ecs.OnStore` +
  • +
  • Rust + +- `flecs::pipeline::OnStart` +- `flecs::pipeline::OnLoad` +- `flecs::pipeline::PostLoad` +- `flecs::pipeline::PreUpdate` +- `flecs::pipeline::OnUpdate` +- `flecs::pipeline::OnValidate` +- `flecs::pipeline::PostUpdate` +- `flecs::pipeline::PreStore` +- `flecs::pipeline::OnStore` +
  • +
+
The `EcsOnStart`/`flecs::OnStart` phase is a special phase that is only ran the first time `progress()` is called. @@ -345,25 +805,24 @@ ECS_SYSTEM(world, Collide, Collisions, Position, Velocity); #### Builtin Pipeline Query The builtin pipeline query looks like this: -Query DSL: -``` -flecs.system.System, Phase(cascade(DependsOn)), !Disabled(up(DependsOn)), !Disabled(up(ChildOf)) -``` +
+
    +
  • C -C descriptor API: ```c ecs_pipeline_init(world, &(ecs_pipeline_desc_t){ - .query.filter.terms = { + .query.terms = { { .id = EcsSystem }, - { .id = EcsPhase, .src.flags = EcsCascade, .src.trav = EcsDependsOn }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsDependsOn, .oper = EcsNot }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsChildOf, .oper = EcsNot } + { .id = EcsPhase, .src.flags = EcsCascade, .trav = EcsDependsOn }, + { .id = EcsDisabled, .src.flags = EcsUp, .trav = EcsDependsOn, .oper = EcsNot }, + { .id = EcsDisabled, .src.flags = EcsUp, .trav = EcsChildOf, .oper = EcsNot } } }); ``` +
  • +
  • C++ -C++ builder API: -```c +```cpp world.pipeline() .with(flecs::System) .with(flecs::Phase).cascade(flecs::DependsOn) @@ -371,16 +830,55 @@ world.pipeline() .without(flecs::Disabled).up(flecs::ChildOf) .build(); ``` +
  • +
  • Query DSL + +``` +flecs.system.System, Phase(cascade(DependsOn)), !Disabled(up(DependsOn)), !Disabled(up(ChildOf)) +``` +
  • +
  • C# + +```cs +world.Pipeline() + .With(Ecs.System) + .With(Ecs.Phase).Cascade(Ecs.DependsOn) + .Without(Ecs.Disabled).Up(Ecs.DependsOn) + .Without(Ecs.Disabled).Up(Ecs.ChildOf) + .Build(); +``` +
  • +
  • Rust + +```rust +world + .pipeline() + .with::() + .with::() + .cascade_type::() + .without::() + .up_type::() + .without::() + .up_type::() + .build(); +``` + +
  • +
+
### Custom pipeline Applications can create their own pipelines which fully customize which systems are matched, and in which order they are executed. Custom pipelines can use phases and `DependsOn`, or they may use a completely different approach. This example shows how to create a pipeline that matches all systems with the `Foo` tag: +
+
    +
  • C ```c ECS_TAG(world, Foo); // Create custom pipeline ecs_entity_t pipeline = ecs_pipeline_init(world, &(ecs_pipeline_desc_t){ - .query.filter.terms = { + .query.terms = { { .id = EcsSystem }, // mandatory { .id = Foo } } @@ -395,6 +893,9 @@ ECS_SYSTEM(world, Move, Foo, Position, Velocity); // Runs the pipeline & system ecs_progress(world, 0); ``` +
  • +
  • C++ + ```cpp // Create custom pipeline flecs::entity pipeline = world.pipeline() @@ -413,6 +914,55 @@ auto move = world.system("Move") // Runs the pipeline & system world.progress(); ``` +
  • +
  • C# + +```cs +// Create custom pipeline +Pipeline pipeline = world.Pipeline() + .With(Ecs.System) + .With(foo) // or .With() if a type + .Build(); + +// Configure the world to use the custom pipeline +world.SetPipeline(pipeline); + +// Create system +System move = world.System("Move") + .Kind(foo) // or .Kind() if a type + .Each(...); + +// Runs the pipeline & system +world.Progress(); +``` +
  • +
  • Rust + +```rust +// Create custom pipeline +let pipeline = world + .pipeline() + .with::() + .with::() // or `.with_id(foo) if an id` + .build(); + +// Configure the world to use the custom pipeline +world.set_pipeline_id(pipeline); + +// Create system +world + .system_named::<(&mut Position, &Velocity)>("Move") + .kind::() // or `.kind_id(foo) if an id` + .each(|(p, v)| { + p.x += v.x; + p.y += v.y; + }); +// Runs the pipeline & system +world.progress(); +``` +
  • +
+
Note that `ECS_SYSTEM` kind parameter/`flecs::system::kind` add the provided entity both by itself as well as with a `DependsOn` relationship. As a result, the above `Move` system ends up with both: @@ -420,13 +970,33 @@ Note that `ECS_SYSTEM` kind parameter/`flecs::system::kind` add the provided ent - `(DependsOn, Foo)` This allows applications to still use the macro/builder API with custom pipelines, even if the custom pipeline does not use the `DependsOn` relationship. To avoid adding the `DependsOn` relationship, `0` can be passed to `ECS_SYSTEM` or `flecs::system::kind` followed by adding the tag manually: +
+
    +
  • C ```c ecs_add(world, Move, Foo); ``` +
  • +
  • C++ + ```cpp move.add(Foo); ``` +
  • +
  • C# + +```cs +move.Entity.Add(foo); +``` +
  • +
  • Rust + +```rust +move_sys.add::(); +``` +
+
#### Pipeline switching performance When running a multithreaded application, switching pipelines can be an expensive operation. The reason for this is that it requires tearing down and recreating the worker threads with the new pipeline context. For this reason it can be more efficient to use queries that allow for enabling/disabling groups of systems vs. switching pipelines. @@ -438,6 +1008,9 @@ For example, the builtin pipeline excludes groups of systems from the schedule t ### Disabling systems Because pipelines use regular ECS queries, adding the `EcsDisabled`/`flecs::Disabled` tag to a system entity will exclude the system from the pipeline. An application can use the `ecs_enable` function or `entity::enable`/`entity::disable` methods to enable/disable a system: +
+
    +
  • C ```c // Disable system in C @@ -445,21 +1018,66 @@ ecs_enable(world, Move, false); // Enable system in C ecs_enable(world, Move, true); ``` +
  • +
  • C++ + ```cpp // Disable system in C++ s.disable(); // Enable system in C++ s.enable(); ``` +
  • +
  • C# + +```cs +// Disable system in C# +s.Entity.Disable(); +// Enable system in C# +s.Entity.Enable(); +``` +
  • +
  • Rust + +```rust +// Disable system +s.disable_self(); +// Enable system +s.enable_self(); +``` +
  • +
+
Additionally the `EcsDisabled`/`flecs::Disabled` tag can be added/removed directly: +
+
    +
  • C ```c ecs_add_id(world, Move, EcsDisabled); ``` -```c +
  • +
  • C++ + +```cpp s.add(flecs::Disabled); ``` +
  • +
  • C# + +```cs +s.Entity.Add(Ecs.Disabled); +``` +
  • +
  • Rust + +```rust +sys.add::(); +``` +
  • +
+
Note that this applies both to builtin pipelines and custom pipelines, as entities with the `Disabled` tag are ignored by default by queries. @@ -486,7 +1104,7 @@ There are a number of things applications can do to force merging of operations, 1. Commands to be merged before another system 2. Operations not to be enqueued as commands. -The mechanisms to accomplish this are sync points for 1), and `no_readonly` systems for 2). +The mechanisms to accomplish this are sync points for 1), and `immediate` systems for 2). ### Sync points Sync points are moments during the frame where all commands are flushed to the storage. Systems that run after a sync point will be able to see all operations that happened up until the sync point. Sync points are inserted automatically by analyzing which commands could have been inserted and which components are being read by systems. @@ -500,6 +1118,9 @@ A pipeline tracks on a per-component basis whether commands could have been inse - Different combinations of modules have different sync requirements, automatic sync point insertion ensures that sync points are only inserted for the set of systems that are imported and are active. To make the scheduler aware that a system can enqueue commands for a component, use the `out` modifier in combination with matching a component on an empty entity (`0`). This tells the scheduler that even though a system is not matching with the component, it is still "writing" it: +
+
    +
  • C ```c // The '()' means, don't match this component on an entity, while `[out]` indicates @@ -511,7 +1132,7 @@ ECS_SYSTEM(world, SetTransform, EcsOnUpdate, Position, [out] Transform()); // When using the descriptor API for creating the system, set the EcsIsEntity // flag while leaving the id field to 0. This is equivalent to doing `()` in the DSL. ecs_system(world, { - .query.filter.terms = { + .query.terms = { { ecs_id(Position) }, { .inout = EcsOut, @@ -523,21 +1144,48 @@ ecs_system(world, { /* ... */ }); ``` +
  • +
  • C++ + ```cpp // In the C++ API, use the write method to indicate commands could be inserted. world.system() .write() .each( /* ... */); ``` +
  • +
  • C# + +```cs +// In the C# API, use the write method to indicate commands could be inserted. +world.System() + .Write() + .Each( /* ... */); +``` +
  • +
  • Rust + +```rust +// In the Rust API, use the write method to indicate commands could be inserted. +world.system::<&Position>().write::().each(|p| { + // ... +}); +``` +
  • +
+
This will cause insertion of a sync point before the next system that reads `Transform`. Similarly, a system can also be annotated with reading a component that it doesn't match with, which is useful when a system calls `get`: +
+
    +
  • C ```c ECS_SYSTEM(world, SetTransform, EcsOnUpdate, Position, [in] Transform()); ``` ```c ecs_system(world, { - .query.filter.terms = { + .query.terms = { { ecs_id(Position) }, { .inout = EcsIn, @@ -549,49 +1197,108 @@ ecs_system(world, { /* ... */ }); ``` +
  • +
  • C++ + ```cpp // In the C++ API, use the read method to indicate a component is read using .get world.system() .read() .each( /* ... */); ``` +
  • +
  • C# -### No readonly systems -By default systems are ran while the world is in "readonly" mode, where all ECS operations are enqueued as commands. Note that readonly mode only applies to "structural" changes, such as changing the components of an entity or other operations that mutate ECS data structures. Systems can still write component values while in readonly mode. +```cs +// In the C# API, use the read method to indicate a component is read using .Get +world.System() + .Read() + .Each( /* ... */); +``` +
  • +
  • Rust + +```rust +// In the Rust API, use the read method to indicate a component is read using .get +world.system::<&Position>().read::().each(|p| { + // ... +}); +``` +
  • +
+
+ +### Immediate systems +By default systems are ran while the world is in "readonly" mode, where all ECS operations are enqueued as commands. Readonly here means that structural changes, such as changing the components of an entity are deferred. Systems can still write component values while in readonly mode. In some cases however, operations need to be immediately visible to a system. A typical example is a system that assigns tasks to resources, like assigning plates to a waiter. A system should only assign plates to a waiter that hasn't been assigned any plates yet, but to know which waiters are free, the operation that assigns a plate to a waiter must be immediately visible. -To accomplish this, systems can be marked with the `no_readonly` flag, which signals that a system should be ran while the world is not in readonly mode. This causes ECS operations to not get enqueued, and allows the system to directly see the results of operations. There are a few limitations to `no_readonly` systems: +To accomplish this, systems can be marked with the `immediate` flag, which signals that a system should be ran while the world is not in readonly mode. This causes ECS operations to not get enqueued, and allows the system to directly see the results of operations. There are a few limitations to `immediate` systems: -- `no_readonly` systems are always single threaded +- `immediate` systems are always single threaded - operations on the iterated over entity must still be deferred The reason for the latter limitation is that allowing for operations on the iterated over entity would cause the system to modify the storage it is iterating, which could cause undefined behavior similar to what you'd see when changing a vector while iterating it. -The following example shows how to create a `no_readonly` system: +The following example shows how to create a `immediate` system: +
+
    +
  • C ```c ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "AssignPlate", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { .id = Plate }, }, .callback = AssignPlate, - .no_readonly = true // disable readonly mode for this system + .immediate = true // disable readonly mode for this system }); ``` +
  • +
  • C++ + ```cpp ecs.system("AssignPlate") .with() - .no_readonly() // disable readonly mode for this system - .iter([&](flecs::iter& it) { /* ... */ }) + .immediate() // disable readonly mode for this system + .run([&](flecs::iter& it) { /* ... */ }) +``` +
  • +
  • C# + +```cs +ecs.System("AssignPlate") + .With() + .NoReadonly() // disable readonly mode for this system + .Iter((Iter it) => { /* ... */ }) +``` +
  • +
  • Rust + +```rust +world + .system_named::<&Plate>("AssignPlate") + .immediate(true) // disable readonly mode for this system + .run(|mut it| { + while it.next() { + // ... + } + }); ``` +
  • +
+
This ensures the world is not in readonly mode when the system is ran. Operations are however still enqueued as commands, which ensures that the system can enqueue commands for the entity that is being iterated over. To prevent commands from being enqueued, a system needs to suspend and resume command enqueueing. This is an extra step, but makes it possible for a system to both enqueue commands for the iterated over entity, as well as do operations that are immediately visible. An example: +
+
    +
  • C + ```c void AssignPlate(ecs_iter_t *it) { for (int i = 0; i < it->count; i ++) { @@ -603,9 +1310,44 @@ void AssignPlate(ecs_iter_t *it) { } } ``` +
  • +
  • C++ + ```cpp -.iter([](flecs::iter& it) { - for (auto i : it) { +.run([](flecs::iter& it) { + while (it.next()) { + for (auto i : it) { + // ECS operations ran here are visible after running the system + it.world().defer_suspend(); + // ECS operations ran here are immediately visible + it.world().defer_resume(); + // ECS operations ran here are visible after running the system + } + } +}); +``` +
  • +
  • C# + +```cs +.Iter((Iter it) => +{ + foreach (int i in it) + { + // ECS operations ran here are visible after running the system + it.World().DeferSuspend(); + // ECS operations ran here are immediately visible + it.World().DeferResume(); + // ECS operations ran here are visible after running the system + } +}); +``` +
  • +
  • Rust + +```rust +.run(|mut it| { + while it.next() { // ECS operations ran here are visible after running the system it.world().defer_suspend(); // ECS operations ran here are immediately visible @@ -614,38 +1356,87 @@ void AssignPlate(ecs_iter_t *it) { } }); ``` +
  • +
+
-Note that `defer_suspend` and `defer_resume` may only be called from within a `no_readonly` system. +Note that `defer_suspend` and `defer_resume` may only be called from within a `immediate` system. ### Threading Systems in Flecs can be multithreaded. This requires both the system to be created as a multithreaded system, as well as configuring the world to have a number of worker threads. To create worker threads, use the `set_threads` function: +
+
    +
  • C ```c ecs_set_threads(world, 4); // Create 4 worker threads ``` +
  • +
  • C++ + ```cpp world.set_threads(4); ``` +
  • +
  • C# + +```cs +world.SetThreads(4); +``` +
  • +
  • Rust + +```rust +world.set_threads(4); +``` +
  • +
+
To create a multithreaded system, use the `multi_threaded` flag: +
+
    +
  • C ```c ecs_system(ecs, { .entity = ecs_entity(ecs, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { .id = ecs_id(Position) } }, .callback = Dummy, .multi_threaded = true // run system on multiple threads }); ``` +
  • +
  • C++ + ```cpp world.system() .multi_threaded() .each( /* ... */ ); ``` +
  • +
  • C# + +```cs +world.System() + .MultiThreaded() + .Each( /* ... */ ); +``` +
  • +
  • Rust + +```rust +world.system::<&Position>().multi_threaded().each(|p| { + // ... +}); +``` +
  • +
+
By default systems are created as single threaded. Single threaded systems are always ran on the main thread. Multithreaded systems are ran on all worker threads. The scheduler runs each multithreaded system on all threads, and divides the number of matched entities across the threads. The way entities are allocated to threads ensures that the same entity is always processed by the same thread, until the next sync point. This means that in an ideal case, all systems in a frame can run until completion without having to synchronize. @@ -654,13 +1445,34 @@ The way the scheduler ensures that the same entities are processed by the same t ### Threading with Async Tasks Systems in Flecs can also be multithreaded using an external asynchronous task system. Instead of creating regular worker threads using `set_threads`, use the `set_task_threads` function and provide the OS API callbacks to create and wait for task completion using your job system. This can be helpful when using Flecs within an application which already has a job queue system to handle multithreaded tasks. +
+
    +
  • C ```c -ecs_set_tasks_threads(world, 4); // Create 4 worker task threads for the duration of each ecs_progress update +ecs_set_task_threads(world, 4); // Create 4 worker task threads for the duration of each ecs_progress update ``` +
  • +
  • C++ + ```cpp world.set_task_threads(4); ``` +
  • +
  • C# + +```cs +world.SetTaskThreads(4); +``` +
  • +
  • Rust + +```rust +world.set_task_threads(4); +``` +
  • +
+
For simplicity, these task callbacks use the same format as Flecs `ecs_os_api_t` thread APIs. In fact, you could provide your regular os thread api functions to create short-duration threads for multithreaded system processing. Create multithreaded systems using the `multi_threaded` flag as with `ecs_set_threads` above. @@ -674,6 +1486,9 @@ When running a pipeline, systems are ran each time `progress()` is called. The ` ### Interval The following example shows how to run systems at a time interval: +
+
    +
  • C ```c // Using the ECS_SYSTEM macro @@ -685,9 +1500,9 @@ ecs_set_interval(world, ecs_id(Move), 1.0); // Run at 1Hz ecs_system(world, { .entity = ecs_entity(world, { .name = "Move", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { ecs_id(Position) }, { ecs_id(Velocity), .inout = EcsIn } }, @@ -695,14 +1510,41 @@ ecs_system(world, { .interval = 1.0 // Run at 1Hz }); ``` +
  • +
  • C++ + ```cpp world.system() .interval(1.0) // Run at 1Hz .each(...); ``` +
  • +
  • C# + +```cs +world.System() + .Interval(1.0f) // Run at 1Hz + .Each(...); +``` +
  • +
  • Rust + +```rust +world.system::<&Position>() + .interval(1.0) // Run at 1Hz + .each(|p| { + // ... +}); +``` +
  • +
+
### Rate The following example shows how to run systems every Nth tick with a rate filter: +
+
    +
  • C ```c // Using the ECS_SYSTEM macro @@ -714,9 +1556,9 @@ ecs_set_rate(world, ecs_id(Move), 2); // Run every other frame ecs_system(world, { .entity = ecs_entity(world, { .name = "Move", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { ecs_id(Position) }, { ecs_id(Velocity), .inout = EcsIn } }, @@ -724,14 +1566,42 @@ ecs_system(world, { .rate = 2.0 // Run every other frame }); ``` +
  • +
  • C++ + ```cpp world.system() .rate(2) // Run every other frame .each(...); ``` +
  • +
  • C# + +```cs +world.System() + .Rate(2) // Run every other frame + .Each(...); +``` +
  • +
  • Rust + +```rust +world + .system::<&Position>() + .rate(2) // Run every other frame + .each(|p| { + // ... + }); +``` +
  • +
+
### Tick source Instead of setting the interval or rate directly on the system, an application may also create a separate entity that holds the time or rate filter, and use that as a "tick source" for a system. This makes it easier to use the same settings across groups of systems: +
+
    +
  • C ```c // Using the ECS_SYSTEM macro @@ -753,9 +1623,9 @@ ecs_entity_t tick_source = ecs_set_interval(world, 0, 1.0); ecs_system(world, { .entity = ecs_entity(world, { .name = "Move", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { ecs_id(Position) }, { ecs_id(Velocity), .inout = EcsIn } }, @@ -763,6 +1633,9 @@ ecs_system(world, { .tick_source = tick_source // Set tick source for system }); ``` +
  • +
  • C++ + ```cpp // A rate filter can be created with .rate(2) flecs::entity tick_source = world.timer() @@ -772,8 +1645,33 @@ world.system() .tick_source(tick_source) // Set tick source for system .each(...); ``` +
  • +
  • C# + +```cs +// A rate filter can be created with .Rate(2) +TimerEntity tickSource = world.Timer() + .Interval(1.0f); + +world.System() + .TickSource(tickSource) // Set tick source for system + .Each(...); +``` +
  • +
  • Rust + +```rust +// Timer not yet implemented in Rust +``` +
  • +
+
Interval filters can be paused and resumed: +
+
    +
  • C + ```c // Pause timer ecs_stop_timer(world, tick_source); @@ -781,6 +1679,9 @@ ecs_stop_timer(world, tick_source); // Resume timer ecs_start_timer(world, tick_source); ``` +
  • +
  • C++ + ```cpp // Pause timer tick_source.stop(); @@ -788,11 +1689,33 @@ tick_source.stop(); // Resume timer tick_source.start(); ``` +
  • +
  • C# + +```cs +// Pause timer +tickSource.Stop(); + +// Resume timer +tickSource.Start(); +``` +
  • +
  • Rust + +```rust +// Timer addon yet to be implemented in rust +``` +
  • +
+
An additional advantage of using shared interval/rate filter between systems is that it guarantees that systems are ran at the same tick. When a system is disabled, its interval/rate filters aren't updated, which means that when the system is reenabled again it would be out of sync with other systems that had the same interval/rate specified. When using a shared tick source however the system is guaranteed to run at the same tick as other systems with the same tick source, even after the system is reenabled. ### Nested tick sources One tick source can be used as the input of another (rate) tick source. The rate tick source will run at each Nth tick of the input tick source. This can be used to create nested tick sources, like in the following example: +
+
    +
  • C ```c // Tick at 1Hz @@ -806,6 +1729,9 @@ ecs_set_tick_source(world, each_minute, each_second); ecs_entity_t each_hour = ecs_set_rate(world, 0, 60); ecs_set_tick_source(world, each_hour, each_minute); ``` +
  • +
  • C++ + ```cpp // Tick at 1Hz flecs::entity each_second = world.timer() @@ -819,15 +1745,43 @@ flecs::entity each_minute = world.timer() flecs::entity each_hour = world.timer() .rate(60, each_minute); ``` +
  • +
  • C# + +```cs +// Tick at 1Hz +TimerEntity eachSecond = world.Timer() + .Interval(1.0f); + +// Tick each minute +TimerEntity eachMinute = world.Timer() + .Rate(60, eachSecond); + +// Tick each hour +TimerEntity eachHour = world.Timer() + .Rate(60, eachMinute); +``` +
  • +
  • Rust + +```rust +// Timer not yet implemented in Rust +``` +
  • +
+
Systems can also act as each others tick source: +
+
    +
  • C ```c // Tick at 1Hz ecs_entity_t each_second = ecs_system(world, { .entity = ecs_entity(world, { .name = "EachSecond", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = Dummy, .interval = 1.0 @@ -837,7 +1791,7 @@ ecs_entity_t each_second = ecs_system(world, { ecs_entity_t each_minute = ecs_system(world, { .entity = ecs_entity(world, { .name = "EachMinute", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = Dummy, .tick_source = each_second, @@ -848,28 +1802,61 @@ ecs_entity_t each_minute = ecs_system(world, { ecs_entity_t each_hour = ecs_system(world, { .entity = ecs_entity(world, { .name = "EachHour", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = Dummy, .tick_source = each_minute, .rate = 60 }); ``` +
  • +
  • C++ + ```cpp // Tick at 1Hz flecs::entity each_second = world.system("EachSecond") .interval(1.0) - .iter([](flecs::iter& it) { /* ... */ }); + .run([](flecs::iter& it) { /* ... */ }); // Tick each minute flecs::entity each_minute = world.system("EachMinute") .tick_source(each_second) .rate(60) - .iter([](flecs::iter& it) { /* ... */ }); + .run([](flecs::iter& it) { /* ... */ }); // Tick each hour flecs::entity each_hour = world.system("EachHour") .tick_source(each_minute) .rate(60) - .iter([](flecs::iter& it) { /* ... */ }); + .run([](flecs::iter& it) { /* ... */ }); +``` +
  • +
  • C# + +```cs +// Tick at 1Hz +System_ eachSecond = world.System("EachSecond") + .Interval(1.0f) + .Iter((Iter it) => { /* ... */ }); + +// Tick each minute +System_ eachMinute = world.System("EachMinute") + .TickSource(eachSecond) + .Rate(60) + .Iter((Iter it) => { /* ... */ }); + +// Tick each hour +System_ eachHour = world.System("EachHour") + .TickSource(eachMinute) + .Rate(60) + .Iter((Iter it) => { /* ... */ }); +``` +
  • +
  • Rust + +```rust +// Timer not yet implemented in Rust ``` +
  • +
+
diff --git a/vendors/flecs/docs/cfg/Doxyfile b/vendors/flecs/docs/cfg/Doxyfile index 85bc17a2f..1e3f76f40 100644 --- a/vendors/flecs/docs/cfg/Doxyfile +++ b/vendors/flecs/docs/cfg/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.9.5 +# Doxyfile 1.10.0 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -48,13 +48,13 @@ PROJECT_NAME = Flecs # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = v3.2 +PROJECT_NUMBER = v4.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = A fast entity component system (ECS) for C & C++ +PROJECT_BRIEF = "A fast entity component system (ECS) for C & C++" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -63,6 +63,12 @@ PROJECT_BRIEF = A fast entity component system (ECS) for C & C++ PROJECT_LOGO = docs/img/logo_small.png +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If @@ -86,7 +92,7 @@ CREATE_SUBDIRS = NO # level increment doubles the number of directories, resulting in 4096 # directories at level 8 which is the default and also the maximum value. The # sub-directories are organized in 2 levels, the first level always has a fixed -# numer of 16 directories. +# number of 16 directories. # Minimum value: 0, maximum value: 8, default value: 8. # This tag requires that the tag CREATE_SUBDIRS is set to YES. @@ -353,6 +359,17 @@ MARKDOWN_SUPPORT = YES TOC_INCLUDE_HEADINGS = 5 +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = GITHUB + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -477,6 +494,14 @@ LOOKUP_CACHE_SIZE = 0 NUM_PROC_THREADS = 1 +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -558,7 +583,8 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO @@ -849,11 +875,26 @@ WARN_IF_INCOMPLETE_DOC = YES WARN_NO_PARAMDOC = YES +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. -# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO @@ -903,16 +944,22 @@ INPUT = include/flecs.h \ include/flecs/addons \ README.md \ docs/Docs.md \ - docs/Quickstart.md \ docs/FAQ.md \ + docs/Quickstart.md \ docs/DesignWithFlecs.md \ - docs/FlecsScriptTutorial.md \ - docs/Manual.md \ + docs/EntitiesComponents.md \ docs/Queries.md \ + docs/FlecsQueryLanguage.md \ docs/Systems.md \ + docs/ObserversManual.md \ + docs/PrefabsManual.md \ + docs/ComponentTraits.md \ docs/Relationships.md \ - docs/RestApi.md \ - docs/JsonFormat.md + docs/FlecsScript.md \ + docs/FlecsScriptTutorial.md \ + docs/FlecsRemoteApi.md \ + docs/Manual.md \ + docs/MigrationGuide.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -945,12 +992,12 @@ INPUT_FILE_ENCODING = # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, -# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C -# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, -# *.vhdl, *.ucf, *.qsf and *.ice. +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h \ *.hpp \ @@ -994,9 +1041,6 @@ EXCLUDE_PATTERNS = # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # ANamespace::AClass, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = @@ -1110,7 +1154,8 @@ FORTRAN_COMMENT_AFTER = 72 SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. +# multi-line macros, enums or list initialized variables directly into the +# documentation. # The default value is: NO. INLINE_SOURCES = NO @@ -1182,6 +1227,46 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1193,10 +1278,11 @@ VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = YES -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1275,12 +1361,18 @@ HTML_STYLESHEET = # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = docs/cfg/doxygen-awesome.css \ docs/cfg/doxygen-awesome-sidebar-only.css \ docs/cfg/doxygen-awesome-sidebar-only-darkmode-toggle.css \ + docs/cfg/flecs-snippet-tabs.css \ docs/cfg/custom.css # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or @@ -1295,20 +1387,18 @@ HTML_EXTRA_FILES = docs/cfg/doxygen-awesome-darkmode-toggle.js \ docs/cfg/doxygen-awesome-fragment-copy-button.js \ docs/cfg/doxygen-awesome-paragraph-link.js \ docs/cfg/doxygen-awesome-interactive-toc.js \ + docs/cfg/doxygen-awesome-tabs.js \ + docs/cfg/flecs-snippet-tabs.js \ docs/img/logo_small_dark.png # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output -# should be rendered with a dark or light theme. Default setting AUTO_LIGHT -# enables light output unless the user preference is dark output. Other options -# are DARK to always use dark mode, LIGHT to always use light mode, AUTO_DARK to -# default to dark mode unless the user prefers light mode, and TOGGLE to let the -# user toggle between dark and light mode via a button. -# Possible values are: LIGHT Always generate light output., DARK Always generate -# dark output., AUTO_LIGHT Automatically set the mode according to the user -# preference, use light mode if no preference is set (the default)., AUTO_DARK -# Automatically set the mode according to the user preference, use dark mode if -# no preference is set. and TOGGLE Allow to user to switch between light and -# dark mode via a button.. +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. # The default value is: AUTO_LIGHT. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1344,15 +1434,6 @@ HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will @@ -1372,6 +1453,33 @@ HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = NO +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1502,6 +1610,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1553,7 +1671,7 @@ QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: +# project's filter section matches. Qt Help Project / Query Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1990,9 +2108,16 @@ PDF_HYPERLINKS = YES USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2013,14 +2138,6 @@ LATEX_HIDE_INDICES = NO LATEX_BIB_STYLE = plain -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, # it will be relative to the LATEX_OUTPUT directory. If left blank the @@ -2186,13 +2303,39 @@ DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- + +# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 +# database with symbols found by doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each doxygen run. If set to NO, doxygen +# will warn if a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- @@ -2289,12 +2432,24 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = FLECS_APP FLECS_COREDOC FLECS_DOC FLECS_EXPR FLECS_HTTP \ - FLECS_JOURNAL FLECS_JSON FLECS_LOG FLECS_META FLECS_META_C \ - FLECS_MODULE FLECS_MONITOR FLECS_OS_API_IMPL FLECS_PARSER \ - FLECS_PIPELINE FLECS_PLECS FLECS_REST FLECS_RULES \ - FLECS_SNAPSHOT FLECS_STATS FLECS_SYSTEM FLECS_TIMER \ - FLECS_UNITS FLECS_ALERTS FLECS_METRICS +PREDEFINED = FLECS_APP \ + FLECS_DOC \ + FLECS_HTTP \ + FLECS_JOURNAL \ + FLECS_JSON \ + FLECS_LOG \ + FLECS_META \ + FLECS_MODULE \ + FLECS_OS_API_IMPL \ + FLECS_PIPELINE \ + FLECS_SCRIPT \ + FLECS_REST \ + FLECS_STATS \ + FLECS_SYSTEM \ + FLECS_TIMER \ + FLECS_UNITS \ + FLECS_ALERTS \ + FLECS_METRICS # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2340,15 +2495,15 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will be +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. @@ -2362,16 +2517,9 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2380,12 +2528,12 @@ HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. -HAVE_DOT = NO +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2433,13 +2581,19 @@ DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a -# graph for each documented class showing the direct and indirect inheritance -# relations. In case HAVE_DOT is set as well dot will be used to draw the graph, -# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set -# to TEXT the direct and indirect inheritance relations will be shown as texts / -# links. -# Possible values are: NO, YES, TEXT and GRAPH. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. Explicit enabling an inheritance +# graph or choosing a different representation for an inheritance graph of a +# specific class, can be accomplished by means of the command \inheritancegraph. +# Disabling an inheritance graph can be accomplished by means of the command +# \hideinheritancegraph. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. CLASS_GRAPH = YES @@ -2447,15 +2601,21 @@ CLASS_GRAPH = YES # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -COLLABORATION_GRAPH = YES +COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. See also the chapter Grouping -# in the manual. +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2497,8 +2657,8 @@ DOT_UML_DETAILS = NO # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # to display on a single line. If the actual line length exceeds this threshold -# significantly it will wrapped across multiple lines. Some heuristics are apply -# to avoid ugly line breaks. +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. # Minimum value: 0, maximum value: 1000, default value: 17. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2515,7 +2675,9 @@ TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2524,7 +2686,10 @@ INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2564,7 +2729,10 @@ GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2580,7 +2748,7 @@ DIR_GRAPH_MAX_DEPTH = 1 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). +# https://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). @@ -2590,7 +2758,7 @@ DIR_GRAPH_MAX_DEPTH = 1 # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_IMAGE_FORMAT = png +DOT_IMAGE_FORMAT = svg # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. @@ -2617,11 +2785,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2698,3 +2867,19 @@ GENERATE_LEGEND = YES # The default value is: YES. DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/vendors/flecs/docs/cfg/custom.css b/vendors/flecs/docs/cfg/custom.css index 654ccbe99..6dfe20d44 100644 --- a/vendors/flecs/docs/cfg/custom.css +++ b/vendors/flecs/docs/cfg/custom.css @@ -46,9 +46,9 @@ html.dark-mode .bordered_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ } html { - --primary-color: #42b983; - --fragment-link: #42b983; - --fragment-keywordflow: #ff7b72; + --primary-color: rgb(66, 185, 131); + --fragment-link: rgb(66, 185, 131); + --fragment-keywordflow: rgb(188, 137, 189); --spacing-small: 8px; --content-maxwidth: 800px; --font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif; @@ -58,14 +58,15 @@ html { html.dark-mode { --primary-color: #42b983; - --page-background-color: #0f1116; - --side-nav-background: #1a1d23; + --page-background-color: hsl(210, 12%, 16%); + --page-foreground-color: hsl(195, 15%, 85%); + --side-nav-background: hsl(214, 10%, 13%); --separator-color: rgba(255,255,255,0.07); - --tablehead-background: #1a1d23; - --code-background: #1a1d23; - --fragment-background: #1a1d23; - --fragment-link: #42b983; - --fragment-keywordflow: #ff7b72; + --tablehead-background: hsl(214, 10%, 13%); + --code-background: hsl(214, 10%, 13%); + --fragment-background: hsl(214, 10%, 13%); + --fragment-link: rgb(66, 185, 131); + --fragment-keywordflow: rgb(188, 137, 189); } div.fragment { @@ -88,3 +89,10 @@ code { html.light-mode #projectlogo img { content: url(logo_small_dark.png); } + +#nav-tree a:hover { + background: none !important; +} +#nav-tree a:hover > span.arrow { + background: none !important; +} diff --git a/vendors/flecs/docs/cfg/doxygen-awesome-darkmode-toggle.js b/vendors/flecs/docs/cfg/doxygen-awesome-darkmode-toggle.js index f2c5853f7..40fe2d38e 100644 --- a/vendors/flecs/docs/cfg/doxygen-awesome-darkmode-toggle.js +++ b/vendors/flecs/docs/cfg/doxygen-awesome-darkmode-toggle.js @@ -5,7 +5,7 @@ https://github.com/jothepro/doxygen-awesome-css MIT License -Copyright (c) 2021 - 2022 jothepro +Copyright (c) 2021 - 2023 jothepro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendors/flecs/docs/cfg/doxygen-awesome-fragment-copy-button.js b/vendors/flecs/docs/cfg/doxygen-awesome-fragment-copy-button.js index 7d06b348d..86c16fd93 100644 --- a/vendors/flecs/docs/cfg/doxygen-awesome-fragment-copy-button.js +++ b/vendors/flecs/docs/cfg/doxygen-awesome-fragment-copy-button.js @@ -5,7 +5,7 @@ https://github.com/jothepro/doxygen-awesome-css MIT License -Copyright (c) 2022 jothepro +Copyright (c) 2022 - 2023 jothepro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendors/flecs/docs/cfg/doxygen-awesome-interactive-toc.js b/vendors/flecs/docs/cfg/doxygen-awesome-interactive-toc.js index b049f5733..20a9669d7 100644 --- a/vendors/flecs/docs/cfg/doxygen-awesome-interactive-toc.js +++ b/vendors/flecs/docs/cfg/doxygen-awesome-interactive-toc.js @@ -5,7 +5,7 @@ https://github.com/jothepro/doxygen-awesome-css MIT License -Copyright (c) 2022 jothepro +Copyright (c) 2022 - 2023 jothepro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendors/flecs/docs/cfg/doxygen-awesome-paragraph-link.js b/vendors/flecs/docs/cfg/doxygen-awesome-paragraph-link.js index 6424dbd42..e53d132ce 100644 --- a/vendors/flecs/docs/cfg/doxygen-awesome-paragraph-link.js +++ b/vendors/flecs/docs/cfg/doxygen-awesome-paragraph-link.js @@ -5,7 +5,7 @@ https://github.com/jothepro/doxygen-awesome-css MIT License -Copyright (c) 2022 jothepro +Copyright (c) 2022 - 2023 jothepro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendors/flecs/docs/cfg/doxygen-awesome-sidebar-only-darkmode-toggle.css b/vendors/flecs/docs/cfg/doxygen-awesome-sidebar-only-darkmode-toggle.css index b988b6f05..d207446e0 100644 --- a/vendors/flecs/docs/cfg/doxygen-awesome-sidebar-only-darkmode-toggle.css +++ b/vendors/flecs/docs/cfg/doxygen-awesome-sidebar-only-darkmode-toggle.css @@ -6,7 +6,7 @@ https://github.com/jothepro/doxygen-awesome-css MIT License -Copyright (c) 2021 jothepro +Copyright (c) 2021 - 2023 jothepro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendors/flecs/docs/cfg/doxygen-awesome-sidebar-only.css b/vendors/flecs/docs/cfg/doxygen-awesome-sidebar-only.css index 0e2766480..853f6d692 100644 --- a/vendors/flecs/docs/cfg/doxygen-awesome-sidebar-only.css +++ b/vendors/flecs/docs/cfg/doxygen-awesome-sidebar-only.css @@ -5,7 +5,7 @@ https://github.com/jothepro/doxygen-awesome-css MIT License -Copyright (c) 2021 jothepro +Copyright (c) 2021 - 2023 jothepro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -81,6 +81,7 @@ html { .ui-resizable-handle { cursor: default; width: 1px !important; + background: var(--separator-color); box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color); } @@ -93,7 +94,7 @@ html { } #doc-content { - height: calc(100vh) !important; + height: calc(100vh - 31px) !important; padding-bottom: calc(3 * var(--spacing-large)); padding-top: calc(var(--top-height) - 80px); box-sizing: border-box; diff --git a/vendors/flecs/docs/cfg/doxygen-awesome-tabs.js b/vendors/flecs/docs/cfg/doxygen-awesome-tabs.js new file mode 100644 index 000000000..8a7c3f18a --- /dev/null +++ b/vendors/flecs/docs/cfg/doxygen-awesome-tabs.js @@ -0,0 +1,90 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeTabs { + + static init() { + window.addEventListener("load", () => { + document.querySelectorAll(".tabbed:not(:empty)").forEach((tabbed, tabbedIndex) => { + let tabLinkList = [] + tabbed.querySelectorAll("li").forEach((tab, tabIndex) => { + tab.id = "tab_" + tabbedIndex + "_" + tabIndex + let header = tab.querySelector(".tab-title") + let tabLink = document.createElement("button") + tabLink.classList.add("tab-button") + tabLink.appendChild(header) + header.title = header.textContent + tabLink.addEventListener("click", () => { + tabbed.querySelectorAll("li").forEach((tab) => { + tab.classList.remove("selected") + }) + tabLinkList.forEach((tabLink) => { + tabLink.classList.remove("active") + }) + tab.classList.add("selected") + tabLink.classList.add("active") + }) + tabLinkList.push(tabLink) + if(tabIndex == 0) { + tab.classList.add("selected") + tabLink.classList.add("active") + } + }) + let tabsOverview = document.createElement("div") + tabsOverview.classList.add("tabs-overview") + let tabsOverviewContainer = document.createElement("div") + tabsOverviewContainer.classList.add("tabs-overview-container") + tabLinkList.forEach((tabLink) => { + tabsOverview.appendChild(tabLink) + }) + tabsOverviewContainer.appendChild(tabsOverview) + tabbed.before(tabsOverviewContainer) + + function resize() { + let maxTabHeight = 0 + tabbed.querySelectorAll("li").forEach((tab, tabIndex) => { + let visibility = tab.style.display + tab.style.display = "block" + maxTabHeight = Math.max(tab.offsetHeight, maxTabHeight) + tab.style.display = visibility + }) + tabbed.style.height = `${maxTabHeight + 10}px` + } + + resize() + new ResizeObserver(resize).observe(tabbed) + }) + }) + + } + + static resize(tabbed) { + + } +} \ No newline at end of file diff --git a/vendors/flecs/docs/cfg/doxygen-awesome.css b/vendors/flecs/docs/cfg/doxygen-awesome.css index 3bb7a2901..ac7f0608e 100644 --- a/vendors/flecs/docs/cfg/doxygen-awesome.css +++ b/vendors/flecs/docs/cfg/doxygen-awesome.css @@ -5,7 +5,7 @@ https://github.com/jothepro/doxygen-awesome-css MIT License -Copyright (c) 2021 - 2022 jothepro +Copyright (c) 2021 - 2023 jothepro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -80,21 +80,21 @@ html { --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 85px); /* colors for various content boxes: @warning, @note, @deprecated @bug */ - --warning-color: #f8d1cc; - --warning-color-dark: #b61825; - --warning-color-darker: #75070f; - --note-color: #faf3d8; - --note-color-dark: #f3a600; - --note-color-darker: #5f4204; - --todo-color: #e4f3ff; - --todo-color-dark: #1879C4; - --todo-color-darker: #274a5c; + --warning-color: #faf3d8; + --warning-color-dark: #f3a600; + --warning-color-darker: #5f4204; + --note-color: #e4f3ff; + --note-color-dark: #1879C4; + --note-color-darker: #274a5c; + --todo-color: #e4dafd; + --todo-color-dark: #5b2bdd; + --todo-color-darker: #2a0d72; --deprecated-color: #ecf0f3; --deprecated-color-dark: #5b6269; --deprecated-color-darker: #43454a; - --bug-color: #e4dafd; - --bug-color-dark: #5b2bdd; - --bug-color-darker: #2a0d72; + --bug-color: #f8d1cc; + --bug-color-dark: #b61825; + --bug-color-darker: #75070f; --invariant-color: #d8f1e3; --invariant-color-dark: #44b86f; --invariant-color-darker: #265532; @@ -160,7 +160,7 @@ html { --toc-background: var(--side-nav-background); --toc-foreground: var(--side-nav-foreground); - /* height of an item in any tree / collapsable table */ + /* height of an item in any tree / collapsible table */ --tree-item-height: 30px; --memname-font-size: var(--code-font-size); @@ -169,6 +169,8 @@ html { --webkit-scrollbar-size: 7px; --webkit-scrollbar-padding: 4px; --webkit-scrollbar-color: var(--separator-color); + + --animation-duration: .12s } @media screen and (max-width: 767px) { @@ -208,21 +210,21 @@ html { --blockquote-background: #222325; --blockquote-foreground: #7e8c92; - --warning-color: #2e1917; - --warning-color-dark: #ad2617; - --warning-color-darker: #f5b1aa; - --note-color: #3b2e04; - --note-color-dark: #f1b602; - --note-color-darker: #ceb670; - --todo-color: #163750; - --todo-color-dark: #1982D2; - --todo-color-darker: #dcf0fa; + --warning-color: #3b2e04; + --warning-color-dark: #f1b602; + --warning-color-darker: #ceb670; + --note-color: #163750; + --note-color-dark: #1982D2; + --note-color-darker: #dcf0fa; + --todo-color: #2a2536; + --todo-color-dark: #7661b3; + --todo-color-darker: #ae9ed6; --deprecated-color: #2e323b; --deprecated-color-dark: #738396; --deprecated-color-darker: #abb0bd; - --bug-color: #2a2536; - --bug-color-dark: #7661b3; - --bug-color-darker: #ae9ed6; + --bug-color: #2e1917; + --bug-color-dark: #ad2617; + --bug-color-darker: #f5b1aa; --invariant-color: #303a35; --invariant-color-dark: #76ce96; --invariant-color-darker: #cceed5; @@ -269,21 +271,21 @@ html.dark-mode { --blockquote-background: #222325; --blockquote-foreground: #7e8c92; - --warning-color: #2e1917; - --warning-color-dark: #ad2617; - --warning-color-darker: #f5b1aa; - --note-color: #3b2e04; - --note-color-dark: #f1b602; - --note-color-darker: #ceb670; - --todo-color: #163750; - --todo-color-dark: #1982D2; - --todo-color-darker: #dcf0fa; + --warning-color: #3b2e04; + --warning-color-dark: #f1b602; + --warning-color-darker: #ceb670; + --note-color: #163750; + --note-color-dark: #1982D2; + --note-color-darker: #dcf0fa; + --todo-color: #2a2536; + --todo-color-dark: #7661b3; + --todo-color-darker: #ae9ed6; --deprecated-color: #2e323b; --deprecated-color-dark: #738396; --deprecated-color-darker: #abb0bd; - --bug-color: #2a2536; - --bug-color-dark: #7661b3; - --bug-color-darker: #ae9ed6; + --bug-color: #2e1917; + --bug-color-dark: #ad2617; + --bug-color-darker: #f5b1aa; --invariant-color: #303a35; --invariant-color-dark: #76ce96; --invariant-color-darker: #cceed5; @@ -316,7 +318,7 @@ body, table, div, p, dl, #nav-tree .label, .title, } h1, h2, h3, h4, h5 { - margin-top: .9em; + margin-top: 1em; font-weight: 600; line-height: initial; } @@ -776,6 +778,8 @@ html.dark-mode iframe#MSearchResults { #side-nav { padding: 0 !important; background: var(--side-nav-background); + min-width: 8px; + max-width: 50vw; } @media screen and (max-width: 767px) { @@ -790,6 +794,7 @@ html.dark-mode iframe#MSearchResults { #nav-tree { background: transparent; + margin-right: 1px; } #nav-tree .label { @@ -862,8 +867,9 @@ html.dark-mode iframe#MSearchResults { } .ui-resizable-e { - background: var(--separator-color); - width: 1px; + width: 4px; + background: transparent; + box-shadow: inset -1px 0 0 0 var(--separator-color); } /* @@ -956,8 +962,9 @@ div.contents div.dyncontent { html:not(.light-mode) div.contents > table img, html:not(.light-mode) div.contents div.dyncontent iframe, html:not(.light-mode) div.contents center iframe, - html:not(.light-mode) div.contents table iframe { - filter: hue-rotate(180deg) invert(); + html:not(.light-mode) div.contents table iframe, + html:not(.light-mode) div.contents .dotgraph iframe { + filter: brightness(89%) hue-rotate(180deg) invert(); } } @@ -966,8 +973,10 @@ html.dark-mode div.contents center img, html.dark-mode div.contents > table img, html.dark-mode div.contents div.dyncontent iframe, html.dark-mode div.contents center iframe, -html.dark-mode div.contents table iframe { - filter: hue-rotate(180deg) invert(); +html.dark-mode div.contents table iframe, +html.dark-mode div.contents .dotgraph iframe + { + filter: brightness(89%) hue-rotate(180deg) invert(); } h2.groupheader { @@ -1056,6 +1065,21 @@ h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { color: var(--page-foreground-color); } +.dotgraph { + max-width: 100%; + overflow-x: scroll; +} + +.dotgraph .caption { + position: sticky; + left: 0; +} + +/* Wrap Graphviz graphs with the `interactive_dotgraph` class if `INTERACTIVE_SVG = YES` */ +.interactive_dotgraph .dotgraph iframe { + max-width: 100%; +} + /* Table of Contents */ @@ -1152,7 +1176,7 @@ div.toc li a.aboveActive { margin-right: var(--spacing-small); margin-bottom: calc(var(--navigation-font-size) / 4); transform: rotate(-90deg); - transition: transform 0.25s ease-out; + transition: transform var(--animation-duration) ease-out; } div.contents .toc.interactive.open > h3::before { @@ -1209,9 +1233,13 @@ div.fragment, pre.fragment { .contents > div.fragment, .textblock > div.fragment, .textblock > pre.fragment, + .textblock > .tabbed > ul > li > div.fragment, + .textblock > .tabbed > ul > li > pre.fragment, .contents > .doxygen-awesome-fragment-wrapper > div.fragment, .textblock > .doxygen-awesome-fragment-wrapper > div.fragment, - .textblock > .doxygen-awesome-fragment-wrapper > pre.fragment { + .textblock > .doxygen-awesome-fragment-wrapper > pre.fragment, + .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > div.fragment, + .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > pre.fragment { margin: var(--spacing-medium) calc(0px - var(--spacing-large)); border-radius: 0; border-left: 0; @@ -1301,8 +1329,9 @@ div.fragment span.lineno a { color: var(--fragment-link) !important; } -div.fragment .line:first-child .lineno { +div.fragment > .line:first-child .lineno { box-shadow: -999999px 0px 0 999999px var(--fragment-linenumber-background), -999998px 0px 0 999999px var(--fragment-linenumber-border); + background-color: var(--fragment-linenumber-background) !important; } div.line { @@ -1361,8 +1390,8 @@ dl.todo { color: var(--todo-color-darker); } -dl.todo dt { - color: var(--todo-color-dark); +dl.todo dt a { + color: var(--todo-color-dark) !important; } dl.bug dt a { @@ -1603,6 +1632,10 @@ table.doxtable tbody { border-radius: var(--border-radius-small); } +table.markdownTable, table.doxtable, table.fieldtable { + padding: 1px; +} + table.doxtable caption { display: block; } @@ -1693,6 +1726,15 @@ table.markdownTable tr:last-child, table.doxtable tr:last-child { border-bottom: none; } +.full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) { + display: block; +} + +.full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody { + display: table; + width: 100%; +} + table.fieldtable th { font-size: var(--page-font-size); font-weight: 600; @@ -1831,7 +1873,7 @@ div.dynheader img[src="closed.png"] { display: block; float: left; margin-left: -10px; - transition: transform 0.25s ease-out; + transition: transform var(--animation-duration) ease-out; } table.memberdecls img { @@ -2195,7 +2237,9 @@ div.memproto::-webkit-scrollbar, .contents center::-webkit-scrollbar, .contents .center::-webkit-scrollbar, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar, -div.contents .toc::-webkit-scrollbar { +div.contents .toc::-webkit-scrollbar, +.contents .dotgraph::-webkit-scrollbar, +.contents .tabs-overview-container::-webkit-scrollbar { background: transparent; width: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); height: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); @@ -2208,7 +2252,9 @@ div.memproto::-webkit-scrollbar-thumb, .contents center::-webkit-scrollbar-thumb, .contents .center::-webkit-scrollbar-thumb, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-thumb, -div.contents .toc::-webkit-scrollbar-thumb { +div.contents .toc::-webkit-scrollbar-thumb, +.contents .dotgraph::-webkit-scrollbar-thumb, +.contents .tabs-overview-container::-webkit-scrollbar-thumb { background-color: transparent; border: var(--webkit-scrollbar-padding) solid transparent; border-radius: calc(var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); @@ -2222,7 +2268,9 @@ div.memproto:hover::-webkit-scrollbar-thumb, .contents center:hover::-webkit-scrollbar-thumb, .contents .center:hover::-webkit-scrollbar-thumb, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody:hover::-webkit-scrollbar-thumb, -div.contents .toc:hover::-webkit-scrollbar-thumb { +div.contents .toc:hover::-webkit-scrollbar-thumb, +.contents .dotgraph:hover::-webkit-scrollbar-thumb, +.contents .tabs-overview-container:hover::-webkit-scrollbar-thumb { background-color: var(--webkit-scrollbar-color); } @@ -2233,7 +2281,9 @@ div.memproto::-webkit-scrollbar-track, .contents center::-webkit-scrollbar-track, .contents .center::-webkit-scrollbar-track, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-track, -div.contents .toc::-webkit-scrollbar-track { +div.contents .toc::-webkit-scrollbar-track, +.contents .dotgraph::-webkit-scrollbar-track, +.contents .tabs-overview-container::-webkit-scrollbar-track { background: transparent; } @@ -2270,7 +2320,9 @@ div.memproto, .contents center, .contents .center, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody, -div.contents .toc { +div.contents .toc, +.contents .dotgraph, +.contents .tabs-overview-container { scrollbar-width: thin; } @@ -2299,7 +2351,7 @@ doxygen-awesome-dark-mode-toggle { } doxygen-awesome-dark-mode-toggle > svg { - transition: transform .1s ease-in-out; + transition: transform var(--animation-duration) ease-in-out; } doxygen-awesome-dark-mode-toggle:active > svg { @@ -2384,7 +2436,7 @@ a.anchorlink { text-decoration: none; opacity: .15; display: none; - transition: opacity .1s ease-in-out, color .1s ease-in-out; + transition: opacity var(--animation-duration) ease-in-out, color var(--animation-duration) ease-in-out; } a.anchorlink svg { @@ -2403,3 +2455,215 @@ a.anchorlink:hover { h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a.anchorlink { display: inline-block; } + +/* + Optional tab feature +*/ + +.tabbed ul { + padding-inline-start: 0px; + margin: 0; + padding: var(--spacing-small) 0; +} + +.tabbed li { + display: none; +} + +.tabbed li.selected { + display: block; +} + +.tabs-overview-container { + overflow-x: auto; + display: block; + overflow-y: visible; +} + +.tabs-overview { + border-bottom: 1px solid var(--separator-color); + display: flex; + flex-direction: row; +} + +@media screen and (max-width: 767px) { + .tabs-overview-container { + margin: 0 calc(0px - var(--spacing-large)); + } + .tabs-overview { + padding: 0 var(--spacing-large) + } +} + +.tabs-overview button.tab-button { + color: var(--page-foreground-color); + margin: 0; + border: none; + background: transparent; + padding: calc(var(--spacing-large) / 2) 0; + display: inline-block; + font-size: var(--page-font-size); + cursor: pointer; + box-shadow: 0 1px 0 0 var(--separator-color); + position: relative; + + -webkit-tap-highlight-color: transparent; +} + +.tabs-overview button.tab-button .tab-title::before { + display: block; + content: attr(title); + font-weight: 600; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.tabs-overview button.tab-button .tab-title { + float: left; + white-space: nowrap; + font-weight: normal; + padding: calc(var(--spacing-large) / 2) var(--spacing-large); + border-radius: var(--border-radius-medium); + transition: background-color var(--animation-duration) ease-in-out, font-weight var(--animation-duration) ease-in-out; +} + +.tabs-overview button.tab-button:not(:last-child) .tab-title { + box-shadow: 8px 0 0 -7px var(--separator-color); +} + +.tabs-overview button.tab-button:hover .tab-title { + background: var(--separator-color); + box-shadow: none; +} + +.tabs-overview button.tab-button.active .tab-title { + font-weight: 600; +} + +.tabs-overview button.tab-button::after { + content: ''; + display: block; + position: absolute; + left: 0; + bottom: 0; + right: 0; + height: 0; + width: 0%; + margin: 0 auto; + border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; + background-color: var(--primary-color); + transition: width var(--animation-duration) ease-in-out, height var(--animation-duration) ease-in-out; +} + +.tabs-overview button.tab-button.active::after { + width: 100%; + box-sizing: border-box; + height: 3px; +} + + +/* + Navigation Buttons +*/ + +.section_buttons:not(:empty) { + margin-top: calc(var(--spacing-large) * 3); +} + +.section_buttons table.markdownTable { + display: block; + width: 100%; +} + +.section_buttons table.markdownTable tbody { + display: table !important; + width: 100%; + box-shadow: none; + border-spacing: 10px; +} + +.section_buttons table.markdownTable td { + padding: 0; +} + +.section_buttons table.markdownTable th { + display: none; +} + +.section_buttons table.markdownTable tr.markdownTableHead { + border: none; +} + +.section_buttons tr th, .section_buttons tr td { + background: none; + border: none; + padding: var(--spacing-large) 0 var(--spacing-small); +} + +.section_buttons a { + display: inline-block; + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium); + color: var(--page-secondary-foreground-color) !important; + text-decoration: none; + transition: color var(--animation-duration) ease-in-out, background-color var(--animation-duration) ease-in-out; +} + +.section_buttons a:hover { + color: var(--page-foreground-color) !important; + background-color: var(--odd-color); +} + +.section_buttons tr td.markdownTableBodyLeft a { + padding: var(--spacing-medium) var(--spacing-large) var(--spacing-medium) calc(var(--spacing-large) / 2); +} + +.section_buttons tr td.markdownTableBodyRight a { + padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large); +} + +.section_buttons tr td.markdownTableBodyLeft a::before, +.section_buttons tr td.markdownTableBodyRight a::after { + color: var(--page-secondary-foreground-color) !important; + display: inline-block; + transition: color .08s ease-in-out, transform .09s ease-in-out; +} + +.section_buttons tr td.markdownTableBodyLeft a::before { + content: '〈'; + padding-right: var(--spacing-large); +} + + +.section_buttons tr td.markdownTableBodyRight a::after { + content: '〉'; + padding-left: var(--spacing-large); +} + + +.section_buttons tr td.markdownTableBodyLeft a:hover::before { + color: var(--page-foreground-color) !important; + transform: translateX(-3px); +} + +.section_buttons tr td.markdownTableBodyRight a:hover::after { + color: var(--page-foreground-color) !important; + transform: translateX(3px); +} + +@media screen and (max-width: 450px) { + .section_buttons a { + width: 100%; + box-sizing: border-box; + } + + .section_buttons tr td:nth-of-type(1).markdownTableBodyLeft a { + border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium); + border-right: none; + } + + .section_buttons tr td:nth-of-type(2).markdownTableBodyRight a { + border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0; + } +} diff --git a/vendors/flecs/docs/cfg/flecs-snippet-tabs.css b/vendors/flecs/docs/cfg/flecs-snippet-tabs.css new file mode 100644 index 000000000..0e7cf5e1b --- /dev/null +++ b/vendors/flecs/docs/cfg/flecs-snippet-tabs.css @@ -0,0 +1,109 @@ +.flecs-snippet-tabs > ul { + padding-inline-start: 0px; + margin: 0; + padding: 0; +} + +.flecs-snippet-tabs-tab { + display: none; +} + +.flecs-snippet-tabs-tab.selected { + display: block; +} + +.flecs-snippet-tabs-tab p.startli:first-child { + display: none; +} + +.flecs-snippet-tabs-tab .doxygen-awesome-fragment-wrapper:first-of-type .fragment { + margin-top: 0; +} + +.flecs-snippet-tabs-overview-container { + overflow-x: auto; + display: block; + overflow-y: visible; +} + +.flecs-snippet-tabs-overview { + border-bottom: 1px solid var(--separator-color); + display: flex; + flex-direction: row; +} + +@media screen and (max-width: 767px) { + .flecs-snippet-tabs-overview-container { + margin: 0 calc(0px - var(--spacing-large)); + } + .flecs-snippet-tabs-overview { + padding: 0 var(--spacing-large) + } +} + +.flecs-snippet-tabs-overview button.tab-button { + color: var(--page-foreground-color); + margin: 0; + border: none; + background: transparent; + padding: calc(var(--spacing-large) / 2) 0; + display: inline-block; + font-size: var(--page-font-size); + cursor: pointer; + box-shadow: 0 1px 0 0 var(--separator-color); + position: relative; + + -webkit-tap-highlight-color: transparent; +} + +.flecs-snippet-tabs-overview button.tab-button .tab-title::before { + display: block; + content: attr(title); + font-weight: 600; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.flecs-snippet-tabs-overview button.tab-button .tab-title { + float: left; + white-space: nowrap; + font-weight: normal; + padding: calc(var(--spacing-large) / 2) var(--spacing-large); + border-radius: var(--border-radius-medium); + transition: background-color var(--animation-duration) ease-in-out, font-weight var(--animation-duration) ease-in-out; +} + +.flecs-snippet-tabs-overview button.tab-button:not(:last-child) .tab-title { + box-shadow: 8px 0 0 -7px var(--separator-color); +} + +.flecs-snippet-tabs-overview button.tab-button:hover .tab-title { + background: var(--separator-color); + box-shadow: none; +} + +.flecs-snippet-tabs-overview button.tab-button.active .tab-title { + font-weight: 600; +} + +.flecs-snippet-tabs-overview button.tab-button::after { + content: ''; + display: block; + position: absolute; + left: 0; + bottom: 0; + right: 0; + height: 0; + width: 0%; + margin: 0 auto; + border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; + background-color: var(--primary-color); + transition: width var(--animation-duration) ease-in-out, height var(--animation-duration) ease-in-out; +} + +.flecs-snippet-tabs-overview button.tab-button.active::after { + width: 100%; + box-sizing: border-box; + height: 3px; +} diff --git a/vendors/flecs/docs/cfg/flecs-snippet-tabs.js b/vendors/flecs/docs/cfg/flecs-snippet-tabs.js new file mode 100644 index 000000000..9d8068e03 --- /dev/null +++ b/vendors/flecs/docs/cfg/flecs-snippet-tabs.js @@ -0,0 +1,102 @@ +// Heavily modified version of DoxygenAwesomeTabs +// https://github.com/jothepro/doxygen-awesome-css +class FlecsSnippetTabs { + static init() { + window.addEventListener("load", () => { + let changeTab = (tabName) => { + // Save active tab to local storage + let activeTabHistory = JSON.parse(localStorage.getItem("activeTabHistory")); + if (activeTabHistory) { + // Remove if already exists and wasn't the last one + let index = activeTabHistory.indexOf(tabName); + if (index < activeTabHistory.length - 1) { + if (index > -1) { + activeTabHistory.splice(index, 1); + } + activeTabHistory.push(tabName); + localStorage.setItem("activeTabHistory", JSON.stringify(activeTabHistory)); + } + } else { + // Create if doesn't exist + localStorage.setItem("activeTabHistory", JSON.stringify([tabName])); + } + + // TODO: Don't switch tabs if this tabbed doesn't have the tab + document.querySelectorAll(".flecs-snippet-tabs:not(:empty)").forEach((tabbed, tabbedIndex) => { + // Find the last active tab that exists in this group of tabs + let lastActiveIndex = -1; + let tabButtonId = "tab_button_" + tabbedIndex + "_0"; // Default to first tab + tabbed.querySelectorAll(".tab-button").forEach((tabLink) => { + let index = activeTabHistory.indexOf(tabLink.textContent); + if (index > -1 && index > lastActiveIndex) { + lastActiveIndex = index; + tabButtonId = tabLink.id; + } + }); + + // Update active tab link + tabbed.querySelectorAll(".tab-button").forEach((tabLink) => { + if (tabLink.id == tabButtonId) { + tabLink.classList.add("active"); + } else { + tabLink.classList.remove("active"); + } + }); + + let tabId = tabButtonId.replace("tab_button_", "tab_"); + + // Update active tab + tabbed.querySelectorAll(".flecs-snippet-tabs:not(:empty) > ul > li").forEach((tab, tabIndex) => { + if (tab.id == tabId) { + tab.classList.add("selected"); + } else { + tab.classList.remove("selected"); + } + }); + }); + }; + + document.querySelectorAll(".flecs-snippet-tabs:not(:empty)").forEach((tabbed, tabbedIndex) => { + let tabsOverview = document.createElement("div"); + tabsOverview.classList.add("flecs-snippet-tabs-overview"); + + tabbed.querySelectorAll(".flecs-snippet-tabs:not(:empty) > ul > li").forEach((tab, tabIndex) => { + tab.classList.add("flecs-snippet-tabs-tab"); + tab.id = "tab_" + tabbedIndex + "_" + tabIndex; + + let header = tab.querySelector(".tab-title"); + if (!header) { + console.error("Tabbed element doesn't have a header", tab); + return; + } + header.title = header.textContent; + + let tabLink = document.createElement("button"); + tabLink.id = "tab_button_" + tabbedIndex + "_" + tabIndex; + tabLink.classList.add("tab-button"); + tabLink.appendChild(header); + tabLink.addEventListener("click", () => changeTab(header.textContent)); + + tabsOverview.appendChild(tabLink); + + if (tabIndex == 0) { + tab.classList.add("selected"); + tabLink.classList.add("active"); + } + }); + + let tabsOverviewContainer = document.createElement("div"); + tabsOverviewContainer.classList.add("flecs-snippet-tabs-overview-container"); + tabsOverviewContainer.appendChild(tabsOverview); + + tabbed.prepend(tabsOverviewContainer); + }); + + // Restore active tab from local storage + let activeTabHistory = JSON.parse(localStorage.getItem("activeTabHistory")); + if (activeTabHistory) { + changeTab(activeTabHistory[activeTabHistory.length - 1]); + } + }); + } +} diff --git a/vendors/flecs/docs/cfg/header.html b/vendors/flecs/docs/cfg/header.html index 1208d7da3..ff2b5e404 100644 --- a/vendors/flecs/docs/cfg/header.html +++ b/vendors/flecs/docs/cfg/header.html @@ -6,6 +6,22 @@ + + + + + + + + + + + + + + + + $projectname: $title $title @@ -27,11 +43,15 @@ + + diff --git a/vendors/flecs/docs/cfg/languages/javascript.min.js b/vendors/flecs/docs/cfg/languages/javascript.min.js new file mode 100644 index 000000000..3fa119329 --- /dev/null +++ b/vendors/flecs/docs/cfg/languages/javascript.min.js @@ -0,0 +1,80 @@ +/*! `javascript` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s) +;return o=>{const l=o.regex,b=e,d={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,t=e.input[a] +;if("<"===t||","===t)return void n.ignoreMatch();let s +;">"===t&&(((e,{after:n})=>{const a="",$={ +match:[/const|var|let/,/\s+/,b,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{ +PARAMS_CONTAINS:w,CLASS_REFERENCE:k},illegal:/#(?![$_A-z])/, +contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,N,_,f,v,{match:/\$\d+/},A,k,{ +className:"attr",begin:b+l.lookahead(":"),relevance:0},$,{ +begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[v,o.REGEXP_MODE,{ +className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:d.begin, +"on:begin":d.isTrulyOpeningTag,end:d.end}],subLanguage:"xml",contains:[{ +begin:d.begin,end:d.end,skip:!0,contains:["self"]}]}]},I,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:b, +className:"title.function"})]},{match:/\.\.\./,relevance:0},C,{match:"\\$"+b, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[R]},x,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},O,M,{match:/\$[(.]/}]}}})() +;hljs.registerLanguage("javascript",e)})(); \ No newline at end of file diff --git a/vendors/flecs/docs/img/component_lifecycle_flow.png b/vendors/flecs/docs/img/component_lifecycle_flow.png new file mode 100644 index 000000000..d7b042233 Binary files /dev/null and b/vendors/flecs/docs/img/component_lifecycle_flow.png differ diff --git a/vendors/flecs/docs/img/explorer-arguments.png b/vendors/flecs/docs/img/explorer-arguments.png deleted file mode 100644 index 828cd702e..000000000 Binary files a/vendors/flecs/docs/img/explorer-arguments.png and /dev/null differ diff --git a/vendors/flecs/docs/img/explorer-query.png b/vendors/flecs/docs/img/explorer-query.png deleted file mode 100644 index b58f77ee7..000000000 Binary files a/vendors/flecs/docs/img/explorer-query.png and /dev/null differ diff --git a/vendors/flecs/docs/img/explorer-remote.png b/vendors/flecs/docs/img/explorer-remote.png deleted file mode 100644 index 8c40e2647..000000000 Binary files a/vendors/flecs/docs/img/explorer-remote.png and /dev/null differ diff --git a/vendors/flecs/docs/img/explorer-rules.png b/vendors/flecs/docs/img/explorer-rules.png deleted file mode 100644 index 5dc200130..000000000 Binary files a/vendors/flecs/docs/img/explorer-rules.png and /dev/null differ diff --git a/vendors/flecs/docs/img/explorer-system.png b/vendors/flecs/docs/img/explorer-system.png deleted file mode 100644 index 4cc98aa31..000000000 Binary files a/vendors/flecs/docs/img/explorer-system.png and /dev/null differ diff --git a/vendors/flecs/docs/img/explorer.png b/vendors/flecs/docs/img/explorer.png index c7db32334..02da1ab47 100644 Binary files a/vendors/flecs/docs/img/explorer.png and b/vendors/flecs/docs/img/explorer.png differ diff --git a/vendors/flecs/docs/img/flecs-add-component-flow.png b/vendors/flecs/docs/img/flecs-add-component-flow.png deleted file mode 100644 index 3115ef137..000000000 Binary files a/vendors/flecs/docs/img/flecs-add-component-flow.png and /dev/null differ diff --git a/vendors/flecs/docs/img/flecs-architecture-overview.png b/vendors/flecs/docs/img/flecs-architecture-overview.png deleted file mode 100644 index 4431de7a0..000000000 Binary files a/vendors/flecs/docs/img/flecs-architecture-overview.png and /dev/null differ diff --git a/vendors/flecs/docs/img/flecs-remove-component-flow.png b/vendors/flecs/docs/img/flecs-remove-component-flow.png deleted file mode 100644 index a59a322ab..000000000 Binary files a/vendors/flecs/docs/img/flecs-remove-component-flow.png and /dev/null differ diff --git a/vendors/flecs/docs/img/flecs-staging-flow.png b/vendors/flecs/docs/img/flecs-staging-flow.png deleted file mode 100644 index ef5e9dc12..000000000 Binary files a/vendors/flecs/docs/img/flecs-staging-flow.png and /dev/null differ diff --git a/vendors/flecs/docs/img/projects/after_sun.png b/vendors/flecs/docs/img/projects/after_sun.png index 3f58215eb..653ac515a 100644 Binary files a/vendors/flecs/docs/img/projects/after_sun.png and b/vendors/flecs/docs/img/projects/after_sun.png differ diff --git a/vendors/flecs/docs/img/projects/age_of_respair.png b/vendors/flecs/docs/img/projects/age_of_respair.png new file mode 100644 index 000000000..6e344fe74 Binary files /dev/null and b/vendors/flecs/docs/img/projects/age_of_respair.png differ diff --git a/vendors/flecs/docs/img/projects/city.png b/vendors/flecs/docs/img/projects/city.png index 6165b3391..088ad68fe 100644 Binary files a/vendors/flecs/docs/img/projects/city.png and b/vendors/flecs/docs/img/projects/city.png differ diff --git a/vendors/flecs/docs/img/projects/equilibrium_engine.png b/vendors/flecs/docs/img/projects/equilibrium_engine.png index d7436c5a2..d2c38faba 100644 Binary files a/vendors/flecs/docs/img/projects/equilibrium_engine.png and b/vendors/flecs/docs/img/projects/equilibrium_engine.png differ diff --git a/vendors/flecs/docs/img/projects/extermination_shock.png b/vendors/flecs/docs/img/projects/extermination_shock.png new file mode 100644 index 000000000..62d534d60 Binary files /dev/null and b/vendors/flecs/docs/img/projects/extermination_shock.png differ diff --git a/vendors/flecs/docs/img/projects/gravitas.png b/vendors/flecs/docs/img/projects/gravitas.png deleted file mode 100644 index 68d0bdebc..000000000 Binary files a/vendors/flecs/docs/img/projects/gravitas.png and /dev/null differ diff --git a/vendors/flecs/docs/img/projects/hyperion.png b/vendors/flecs/docs/img/projects/hyperion.png new file mode 100644 index 000000000..550c10a00 Binary files /dev/null and b/vendors/flecs/docs/img/projects/hyperion.png differ diff --git a/vendors/flecs/docs/img/projects/hytale.png b/vendors/flecs/docs/img/projects/hytale.png new file mode 100644 index 000000000..607d6e46a Binary files /dev/null and b/vendors/flecs/docs/img/projects/hytale.png differ diff --git a/vendors/flecs/docs/img/projects/sol_survivor.png b/vendors/flecs/docs/img/projects/sol_survivor.png index f8e7ec438..b2f8d6e89 100644 Binary files a/vendors/flecs/docs/img/projects/sol_survivor.png and b/vendors/flecs/docs/img/projects/sol_survivor.png differ diff --git a/vendors/flecs/docs/img/projects/tempest_rising.png b/vendors/flecs/docs/img/projects/tempest_rising.png index de4c2584c..cf427e4e3 100644 Binary files a/vendors/flecs/docs/img/projects/tempest_rising.png and b/vendors/flecs/docs/img/projects/tempest_rising.png differ diff --git a/vendors/flecs/docs/img/projects/territory_control.jpeg b/vendors/flecs/docs/img/projects/territory_control.jpeg deleted file mode 100644 index 2af578325..000000000 Binary files a/vendors/flecs/docs/img/projects/territory_control.jpeg and /dev/null differ diff --git a/vendors/flecs/docs/img/projects/territory_control.jpg b/vendors/flecs/docs/img/projects/territory_control.jpg new file mode 100644 index 000000000..9139e4003 Binary files /dev/null and b/vendors/flecs/docs/img/projects/territory_control.jpg differ diff --git a/vendors/flecs/docs/img/projects/the_forge.jpg b/vendors/flecs/docs/img/projects/the_forge.jpg index 61956b99e..1a7cb25f9 100644 Binary files a/vendors/flecs/docs/img/projects/the_forge.jpg and b/vendors/flecs/docs/img/projects/the_forge.jpg differ diff --git a/vendors/flecs/docs/img/projects/tome_tumble.png b/vendors/flecs/docs/img/projects/tome_tumble.png new file mode 100644 index 000000000..d63745f0b Binary files /dev/null and b/vendors/flecs/docs/img/projects/tome_tumble.png differ diff --git a/vendors/flecs/docs/img/projects/tower_defense.png b/vendors/flecs/docs/img/projects/tower_defense.png index 240c222f8..c971cee69 100644 Binary files a/vendors/flecs/docs/img/projects/tower_defense.png and b/vendors/flecs/docs/img/projects/tower_defense.png differ diff --git a/vendors/flecs/docs/img/query_instancing.png b/vendors/flecs/docs/img/query_instancing.png index 2a2a41940..f1e1d7339 100644 Binary files a/vendors/flecs/docs/img/query_instancing.png and b/vendors/flecs/docs/img/query_instancing.png differ diff --git a/vendors/flecs/docs/img/relationship_traversal.png b/vendors/flecs/docs/img/relationship_traversal.png index 9085a0d7f..33f2d1ba4 100644 Binary files a/vendors/flecs/docs/img/relationship_traversal.png and b/vendors/flecs/docs/img/relationship_traversal.png differ diff --git a/vendors/flecs/examples/c/entities/basics/BUILD b/vendors/flecs/examples/c/entities/basics/BUILD new file mode 100644 index 000000000..3b0fc157c --- /dev/null +++ b/vendors/flecs/examples/c/entities/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/entities/basics/src/main.c b/vendors/flecs/examples/c/entities/basics/src/main.c index 4db7f4c1f..bd97625d6 100644 --- a/vendors/flecs/examples/c/entities/basics/src/main.c +++ b/vendors/flecs/examples/c/entities/basics/src/main.c @@ -42,9 +42,9 @@ int main(int argc, char *argv[]) { ecs_remove(ecs, alice, Walking); // Iterate all entities with Position - ecs_iter_t it = ecs_term_iter(ecs, &(ecs_term_t){ .id = ecs_id(Position) }); - while (ecs_term_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + ecs_iter_t it = ecs_each(ecs, Position); + while (ecs_each_next(&it)) { + Position *p = ecs_field(&it, Position, 0); for (int i = 0; i < it.count; i ++) { printf("%s: {%f, %f}\n", ecs_get_name(ecs, it.entities[i]), p[i].x, p[i].y); diff --git a/vendors/flecs/examples/c/entities/fwd_declare_component/BUILD b/vendors/flecs/examples/c/entities/fwd_declare_component/BUILD new file mode 100644 index 000000000..1045e147a --- /dev/null +++ b/vendors/flecs/examples/c/entities/fwd_declare_component/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "fwd_declare_component", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/entities/fwd_declare_component/src/main.c b/vendors/flecs/examples/c/entities/fwd_declare_component/src/main.c index 0c12fa8a8..469caba20 100644 --- a/vendors/flecs/examples/c/entities/fwd_declare_component/src/main.c +++ b/vendors/flecs/examples/c/entities/fwd_declare_component/src/main.c @@ -38,7 +38,7 @@ ECS_DECLARE(Wizard); ecs_entity_t Platoon_1; ecs_entity_t create_npc(ecs_world_t *world) { - ecs_entity_t result = ecs_new(world, 0); + ecs_entity_t result = ecs_new(world); // Without the forward declaration, this would have thrown a compiler error ecs_set(world, result, Position, {10, 20}); @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) { ECS_TAG_DEFINE(world, Wizard); // A forward declared entity can be assigned as any variable - Platoon_1 = ecs_new_id(world); + Platoon_1 = ecs_new(world); // Create new entity with Position ecs_entity_t e = create_npc(world); diff --git a/vendors/flecs/examples/c/entities/hierarchy/BUILD b/vendors/flecs/examples/c/entities/hierarchy/BUILD new file mode 100644 index 000000000..f0a25f132 --- /dev/null +++ b/vendors/flecs/examples/c/entities/hierarchy/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hierarchy", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/entities/hierarchy/src/main.c b/vendors/flecs/examples/c/entities/hierarchy/src/main.c index 552dd86e9..1a58d250d 100644 --- a/vendors/flecs/examples/c/entities/hierarchy/src/main.c +++ b/vendors/flecs/examples/c/entities/hierarchy/src/main.c @@ -10,7 +10,7 @@ ECS_COMPONENT_DECLARE(Position); void iterate_tree(ecs_world_t *ecs, ecs_entity_t e, Position p_parent) { // Print hierarchical name of entity & the entity type - char *path_str = ecs_get_fullpath(ecs, e); + char *path_str = ecs_get_path(ecs, e); char *type_str = ecs_type_str(ecs, ecs_get_type(ecs, e)); printf("%s [%s]\n", path_str, type_str); ecs_os_free(type_str); @@ -44,35 +44,61 @@ int main(int argc, char *argv[]) { // Hierarchies use ECS relationships and the builtin flecs::ChildOf relationship to // create entities as children of other entities. - ecs_entity_t sun = ecs_new_entity(ecs, "Sun"); + ecs_entity_t sun = ecs_entity(ecs, { .name = "Sun" }); ecs_add(ecs, sun, Star); ecs_set(ecs, sun, Position, {1, 1}); - ecs_entity_t mercury = ecs_new_entity(ecs, "Mercury"); + ecs_entity_t mercury = ecs_entity(ecs, { .name = "Mercury" }); ecs_add_pair(ecs, mercury, EcsChildOf, sun); ecs_add(ecs, mercury, Planet); ecs_set(ecs, mercury, Position, {1, 1}); - ecs_entity_t venus = ecs_new_entity(ecs, "Venus"); + ecs_entity_t venus = ecs_entity(ecs, { .name = "Venus" }); ecs_add_pair(ecs, venus, EcsChildOf, sun); ecs_add(ecs, venus, Planet); ecs_set(ecs, venus, Position, {2, 2}); - ecs_entity_t earth = ecs_new_entity(ecs, "Earth"); + ecs_entity_t earth = ecs_entity(ecs, { .name = "Earth" }); ecs_add_pair(ecs, earth, EcsChildOf, sun); ecs_add(ecs, earth, Planet); ecs_set(ecs, earth, Position, {3, 3}); - ecs_entity_t moon = ecs_new_entity(ecs, "Moon"); + ecs_entity_t moon = ecs_entity(ecs, { .name = "Moon" }); ecs_add_pair(ecs, moon, EcsChildOf, earth); ecs_add(ecs, moon, Moon); ecs_set(ecs, moon, Position, {0.1, 0.1}); // Is the Moon a child of Earth? - printf("Child of Earth? %d\n", ecs_has_pair(ecs, moon, EcsChildOf, earth)); + printf("Child of Earth? %d\n\n", ecs_has_pair(ecs, moon, EcsChildOf, earth)); + + // Lookup moon by name + ecs_entity_t e = ecs_lookup(ecs, "Sun.Earth.Moon"); + char *path = ecs_get_path(ecs, e); + printf("Moon found: %s\n\n", path); + ecs_os_free(path); // Do a depth-first walk of the tree iterate_tree(ecs, sun, (Position){0, 0}); return ecs_fini(ecs); + + // Output: + // Child of Earth? 1 + // + // Moon found: Sun.Earth.Moon + // + // Sun [Position, Star, (Identifier,Name)] + // {1.000000, 1.000000} + // + // Sun.Mercury [Position, Planet, (Identifier,Name), (ChildOf,Sun)] + // {2.000000, 2.000000} + // + // Sun.Venus [Position, Planet, (Identifier,Name), (ChildOf,Sun)] + // {3.000000, 3.000000} + // + // Sun.Earth [Position, Planet, (Identifier,Name), (ChildOf,Sun)] + // {4.000000, 4.000000} + // + // Sun.Earth.Moon [Position, Sun.Earth.Moon, (Identifier,Name), (ChildOf,Sun.Earth)] + // {4.100000, 4.100000} } diff --git a/vendors/flecs/examples/c/entities/hooks/BUILD b/vendors/flecs/examples/c/entities/hooks/BUILD new file mode 100644 index 000000000..f95798365 --- /dev/null +++ b/vendors/flecs/examples/c/entities/hooks/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hooks", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/entities/hooks/src/main.c b/vendors/flecs/examples/c/entities/hooks/src/main.c index 18513f533..68716242c 100644 --- a/vendors/flecs/examples/c/entities/hooks/src/main.c +++ b/vendors/flecs/examples/c/entities/hooks/src/main.c @@ -75,7 +75,7 @@ int main(int argc, char *argv[]) { ecs_log_set_level(0); - ecs_entity_t e = ecs_new_entity(world, "Entity"); + ecs_entity_t e = ecs_entity(world, { .name = "Entity" }); ecs_trace("ecs_add(world, e, String)"); ecs_log_push(); diff --git a/vendors/flecs/examples/c/entities/iterate_components/BUILD b/vendors/flecs/examples/c/entities/iterate_components/BUILD new file mode 100644 index 000000000..77966fe1b --- /dev/null +++ b/vendors/flecs/examples/c/entities/iterate_components/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "iterate_components", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/entities/iterate_components/src/main.c b/vendors/flecs/examples/c/entities/iterate_components/src/main.c index 054ac25db..09fdfe142 100644 --- a/vendors/flecs/examples/c/entities/iterate_components/src/main.c +++ b/vendors/flecs/examples/c/entities/iterate_components/src/main.c @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) { ECS_TAG(ecs, Apples); // Create an entity which all of the above - ecs_entity_t Bob = ecs_new_id(ecs); + ecs_entity_t Bob = ecs_new(ecs); ecs_set(ecs, Bob, Position, {10, 20}); ecs_set(ecs, Bob, Velocity, {1, 1}); diff --git a/vendors/flecs/examples/c/explorer/BUILD b/vendors/flecs/examples/c/explorer/BUILD new file mode 100644 index 000000000..73ec94e7d --- /dev/null +++ b/vendors/flecs/examples/c/explorer/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "explorer", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/explorer/src/main.c b/vendors/flecs/examples/c/explorer/src/main.c index 7f6417a6b..e64c9ec37 100644 --- a/vendors/flecs/examples/c/explorer/src/main.c +++ b/vendors/flecs/examples/c/explorer/src/main.c @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { ecs_world_t *world = ecs_init_w_args(argc, argv); ECS_IMPORT(world, FlecsUnits); - ECS_IMPORT(world, FlecsMonitor); // Collect statistics periodically + ECS_IMPORT(world, FlecsStats); // Collect statistics periodically // Mass component ECS_COMPONENT(world, Mass); @@ -23,15 +23,15 @@ int main(int argc, char *argv[]) { }); // Simple hierarchy - ecs_entity_t Sun = ecs_new_entity(world, "Sun"); + ecs_entity_t Sun = ecs_entity(world, { .name = "Sun" }); ecs_set(world, Sun, Mass, {1.988500e31}); ecs_set_scope(world, Sun); - ecs_entity_t Earth = ecs_new_entity(world, "Earth"); + ecs_entity_t Earth = ecs_entity(world, { .name = "Earth" }); ecs_set(world, Earth, Mass, {5.9722e24}); ecs_set_scope(world, Earth); - ecs_entity_t Moon = ecs_new_entity(world, "Moon"); + ecs_entity_t Moon = ecs_entity(world, { .name = "Moon" }); ecs_set(world, Moon, Mass, {7.34767309e22}); // Restore default hierarchy scope to root @@ -40,7 +40,7 @@ int main(int argc, char *argv[]) { // Run application with REST interface. When the application is running, // navigate to https://flecs.dev/explorer to inspect it! // - // See docs/RestApi.md#explorer for more information. + // See docs/FlecsRemoteApi.md#explorer for more information. return ecs_app_run(world, &(ecs_app_desc_t){ .enable_rest = true }); diff --git a/vendors/flecs/examples/c/hello_world/BUILD b/vendors/flecs/examples/c/hello_world/BUILD new file mode 100644 index 000000000..56eb90b43 --- /dev/null +++ b/vendors/flecs/examples/c/hello_world/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hello_world", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/hello_world/src/main.c b/vendors/flecs/examples/c/hello_world/src/main.c index 812c420d5..97d6c14f8 100644 --- a/vendors/flecs/examples/c/hello_world/src/main.c +++ b/vendors/flecs/examples/c/hello_world/src/main.c @@ -10,8 +10,8 @@ typedef struct { * as entities are grouped in tables by which components they have, and each * table has its own set of component arrays. */ void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); /* Print the set of components for the iterated over entities */ char *type_str = ecs_table_str(it->world, it->table); @@ -42,7 +42,7 @@ int main(int argc, char *argv[]) { ECS_TAG(world, Pears); /* Create an entity with name Bob, add Position and food preference */ - ecs_entity_t Bob = ecs_new_entity(world, "Bob"); + ecs_entity_t Bob = ecs_entity(world, { .name = "Bob" }); ecs_set(world, Bob, Position, {0, 0}); ecs_set(world, Bob, Velocity, {1, 2}); ecs_add_pair(world, Bob, Eats, Apples); diff --git a/vendors/flecs/examples/c/modules/simple_module/BUILD b/vendors/flecs/examples/c/modules/simple_module/BUILD new file mode 100644 index 000000000..24ec7a63a --- /dev/null +++ b/vendors/flecs/examples/c/modules/simple_module/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "simple_module", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/modules/simple_module/src/main.c b/vendors/flecs/examples/c/modules/simple_module/src/main.c index 6ee2b169d..642ecf930 100644 --- a/vendors/flecs/examples/c/modules/simple_module/src/main.c +++ b/vendors/flecs/examples/c/modules/simple_module/src/main.c @@ -2,7 +2,7 @@ #include void PrintPosition(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i ++) { printf("p = {%f, %f} (system)\n", p[i].x, p[i].y); @@ -20,7 +20,7 @@ int main(int argc, char *argv[]) { ECS_SYSTEM(world, PrintPosition, EcsOnUpdate, simple.module.Position); // Create entity with components imported from module - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_set(world, e, Velocity, {1, 2}); // Call progress which runs imported Move system diff --git a/vendors/flecs/examples/c/modules/simple_module/src/simple_module.c b/vendors/flecs/examples/c/modules/simple_module/src/simple_module.c index 2c1dbf3ad..1d48babaa 100644 --- a/vendors/flecs/examples/c/modules/simple_module/src/simple_module.c +++ b/vendors/flecs/examples/c/modules/simple_module/src/simple_module.c @@ -4,8 +4,8 @@ ECS_COMPONENT_DECLARE(Position); ECS_COMPONENT_DECLARE(Velocity); void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; diff --git a/vendors/flecs/examples/c/observers/basics/BUILD b/vendors/flecs/examples/c/observers/basics/BUILD new file mode 100644 index 000000000..3b0fc157c --- /dev/null +++ b/vendors/flecs/examples/c/observers/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/observers/basics/src/main.c b/vendors/flecs/examples/c/observers/basics/src/main.c index 0b6f12699..712fdbf69 100644 --- a/vendors/flecs/examples/c/observers/basics/src/main.c +++ b/vendors/flecs/examples/c/observers/basics/src/main.c @@ -37,7 +37,7 @@ void Observer(ecs_iter_t *it) { ecs_entity_t event_id = it->event_id; // Get component values as usual - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; @@ -68,8 +68,8 @@ int main(int argc, char *argv[]) { // The ecs_observer macro (which calls the ecs_observer_init function) can // be used to create an observer. ecs_observer(ecs, { - // Observer filter. Uses same ecs_filter_desc_t as systems/queries - .filter = { .terms = {{ .id = ecs_id(Position) }}}, + // Observer query. Uses same ecs_query_desc_t as systems/queries + .query = { .terms = {{ .id = ecs_id(Position) }}}, // Events the observer will listen for. Can contain multiple events .events = { EcsOnAdd, EcsOnRemove }, // Observer callback @@ -80,7 +80,7 @@ int main(int argc, char *argv[]) { ECS_OBSERVER(ecs, Observer, EcsOnSet, Position); // Create entity - ecs_entity_t e = ecs_new_entity(ecs, "e"); + ecs_entity_t e = ecs_entity(ecs, { .name = "e" }); // Set Position (emits EcsOnAdd and EcsOnSet) ecs_set(ecs, e, Position, {10, 20}); diff --git a/vendors/flecs/examples/c/observers/custom_event/BUILD b/vendors/flecs/examples/c/observers/custom_event/BUILD new file mode 100644 index 000000000..d0f8dd9c1 --- /dev/null +++ b/vendors/flecs/examples/c/observers/custom_event/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_event", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/observers/custom_event/src/main.c b/vendors/flecs/examples/c/observers/custom_event/src/main.c index f46c40ac6..c0288cb78 100644 --- a/vendors/flecs/examples/c/observers/custom_event/src/main.c +++ b/vendors/flecs/examples/c/observers/custom_event/src/main.c @@ -39,15 +39,15 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, Position); // Create custom event - ecs_entity_t MyEvent = ecs_new_entity(ecs, "MyEvent"); + ecs_entity_t MyEvent = ecs_entity(ecs, { .name = "MyEvent" }); // Create observer for custom event ECS_OBSERVER(ecs, Observer, MyEvent, Position); // Create entity - ecs_entity_t e = ecs_new_entity(ecs, "e"); + ecs_entity_t e = ecs_entity(ecs, { .name = "e" }); - // The observer filter can be matched against the entity, so make sure it + // The observer query can be matched against the entity, so make sure it // has the Position component before emitting the event. This does not // trigger the observer yet. ecs_set(ecs, e, Position, {10, 20}); diff --git a/vendors/flecs/examples/c/observers/enqueue_entity_event/BUILD b/vendors/flecs/examples/c/observers/enqueue_entity_event/BUILD new file mode 100644 index 000000000..6d857f783 --- /dev/null +++ b/vendors/flecs/examples/c/observers/enqueue_entity_event/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "enqueue_entity_event", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/observers/enqueue_entity_event/src/main.c b/vendors/flecs/examples/c/observers/enqueue_entity_event/src/main.c index acb60702a..86694daa3 100644 --- a/vendors/flecs/examples/c/observers/enqueue_entity_event/src/main.c +++ b/vendors/flecs/examples/c/observers/enqueue_entity_event/src/main.c @@ -19,7 +19,7 @@ ECS_COMPONENT_DECLARE(Resize); void OnClick(ecs_iter_t *it) { // The event source can be obtained with ecs_field_src(1). This allows the // same event function to be used for different entities. - char *path = ecs_get_fullpath(it->world, ecs_field_src(it, 1)); + char *path = ecs_get_path(it->world, ecs_field_src(it, 0)); printf("clicked on %s!\n", path); ecs_os_free(path); } @@ -27,7 +27,7 @@ void OnClick(ecs_iter_t *it) { void OnResize(ecs_iter_t *it) { // Event payload can be obtained from the it->param member Resize *p = it->param; - char *path = ecs_get_fullpath(it->world, ecs_field_src(it, 1)); + char *path = ecs_get_path(it->world, ecs_field_src(it, 0)); printf("resized %s to {%.0f, %.0f}!\n", path, p->width, p->height); ecs_os_free(path); } @@ -39,19 +39,19 @@ int main(int argc, char *argv[]) { ECS_COMPONENT_DEFINE(ecs, Resize); // Create a widget entity - ecs_entity_t widget = ecs_new_entity(ecs, "MyWidget"); + ecs_entity_t widget = ecs_entity(ecs, { .name = "MyWidget" }); // Create entity observer. Use EcsAny to indicate we're not interested in // matching specific components. ecs_observer(ecs, { - .filter.terms = {{ .id = EcsAny, .src.id = widget }}, + .query.terms = {{ .id = EcsAny, .src.id = widget }}, .events = { Click }, .callback = OnClick }); // Create another one for the Resize event ecs_observer(ecs, { - .filter.terms = {{ .id = EcsAny, .src.id = widget }}, + .query.terms = {{ .id = EcsAny, .src.id = widget }}, .events = { ecs_id(Resize) }, .callback = OnResize }); diff --git a/vendors/flecs/examples/c/observers/enqueue_event/BUILD b/vendors/flecs/examples/c/observers/enqueue_event/BUILD new file mode 100644 index 000000000..f4f29f856 --- /dev/null +++ b/vendors/flecs/examples/c/observers/enqueue_event/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "enqueue_event", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/observers/enqueue_event/src/main.c b/vendors/flecs/examples/c/observers/enqueue_event/src/main.c index e528ba060..7978cd9d1 100644 --- a/vendors/flecs/examples/c/observers/enqueue_event/src/main.c +++ b/vendors/flecs/examples/c/observers/enqueue_event/src/main.c @@ -35,15 +35,15 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, Position); // Create custom event - ecs_entity_t MyEvent = ecs_new_entity(ecs, "MyEvent"); + ecs_entity_t MyEvent = ecs_entity(ecs, { .name = "MyEvent" }); // Create observer for custom event ECS_OBSERVER(ecs, Observer, MyEvent, Position); // Create entity - ecs_entity_t e = ecs_new_entity(ecs, "e"); + ecs_entity_t e = ecs_entity(ecs, { .name = "e" }); - // The observer filter can be matched against the entity, so make sure it + // The observer query can be matched against the entity, so make sure it // has the Position component before emitting the event. This does not // trigger the observer yet. ecs_set(ecs, e, Position, {10, 20}); diff --git a/vendors/flecs/examples/c/observers/entity_event/BUILD b/vendors/flecs/examples/c/observers/entity_event/BUILD new file mode 100644 index 000000000..00fbec43a --- /dev/null +++ b/vendors/flecs/examples/c/observers/entity_event/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "entity_event", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/observers/entity_event/src/main.c b/vendors/flecs/examples/c/observers/entity_event/src/main.c index f4205c515..6134ef60d 100644 --- a/vendors/flecs/examples/c/observers/entity_event/src/main.c +++ b/vendors/flecs/examples/c/observers/entity_event/src/main.c @@ -26,7 +26,7 @@ ECS_COMPONENT_DECLARE(Resize); void OnClick(ecs_iter_t *it) { // The event source can be obtained with ecs_field_src(1). This allows the // same event function to be used for different entities. - char *path = ecs_get_fullpath(it->world, ecs_field_src(it, 1)); + char *path = ecs_get_path(it->world, ecs_field_src(it, 0)); printf("clicked on %s!\n", path); ecs_os_free(path); } @@ -34,7 +34,7 @@ void OnClick(ecs_iter_t *it) { void OnResize(ecs_iter_t *it) { // Event payload can be obtained from the it->param member Resize *p = it->param; - char *path = ecs_get_fullpath(it->world, ecs_field_src(it, 1)); + char *path = ecs_get_path(it->world, ecs_field_src(it, 0)); printf("resized %s to {%.0f, %.0f}!\n", path, p->width, p->height); ecs_os_free(path); } @@ -46,19 +46,19 @@ int main(int argc, char *argv[]) { ECS_COMPONENT_DEFINE(ecs, Resize); // Create a widget entity - ecs_entity_t widget = ecs_new_entity(ecs, "MyWidget"); + ecs_entity_t widget = ecs_entity(ecs, { .name = "MyWidget" }); // Create entity observer. Use EcsAny to indicate we're not interested in // matching specific components. ecs_observer(ecs, { - .filter.terms = {{ .id = EcsAny, .src.id = widget }}, + .query.terms = {{ .id = EcsAny, .src.id = widget }}, .events = { Click }, .callback = OnClick }); // Create another one for the Resize event ecs_observer(ecs, { - .filter.terms = {{ .id = EcsAny, .src.id = widget }}, + .query.terms = {{ .id = EcsAny, .src.id = widget }}, .events = { ecs_id(Resize) }, .callback = OnResize }); diff --git a/vendors/flecs/examples/c/observers/monitor/BUILD b/vendors/flecs/examples/c/observers/monitor/BUILD new file mode 100644 index 000000000..6d65a9651 --- /dev/null +++ b/vendors/flecs/examples/c/observers/monitor/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "monitor", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/observers/monitor/src/main.c b/vendors/flecs/examples/c/observers/monitor/src/main.c index 9113b7f36..02a76bc05 100644 --- a/vendors/flecs/examples/c/observers/monitor/src/main.c +++ b/vendors/flecs/examples/c/observers/monitor/src/main.c @@ -2,7 +2,7 @@ #include // A monitor observer triggers when an entity starts/stop matching the observer -// filter. The observer communicates whether an entity is "entering/leaving" the +// query. The observer communicates whether an entity is "entering/leaving" the // monitor by setting ecs_iter_t::event to EcsOnAdd (for entering) or // EcsOnRemove (for leaving). // @@ -42,13 +42,13 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, Velocity); ecs_observer(ecs, { - .filter = { .terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}}, + .query = { .terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}}, .events = { EcsMonitor }, // Monitor entities entering/leaving the query .callback = Observer, }); // Create entity - ecs_entity_t e = ecs_new_entity(ecs, "e"); + ecs_entity_t e = ecs_entity(ecs, { .name = "e" }); // This does not yet trigger the monitor, as the entity does not yet match. ecs_set(ecs, e, Position, {10, 20}); diff --git a/vendors/flecs/examples/c/observers/propagate/BUILD b/vendors/flecs/examples/c/observers/propagate/BUILD new file mode 100644 index 000000000..d3dd58142 --- /dev/null +++ b/vendors/flecs/examples/c/observers/propagate/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "propagate", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/observers/propagate/src/main.c b/vendors/flecs/examples/c/observers/propagate/src/main.c index 530335306..16f09cc38 100644 --- a/vendors/flecs/examples/c/observers/propagate/src/main.c +++ b/vendors/flecs/examples/c/observers/propagate/src/main.c @@ -5,7 +5,7 @@ // listen for events from a parent or prefab, like triggering when a component // inherited from a prefab was set. // -// Event propagation happens automatically when an observer contains a filter +// Event propagation happens automatically when an observer contains a query // with the EcsUp flag set (indicating upwards traversal). Observers use the // same matching logic as queries: if a query with upwards traversal matches an // entity, so will an observer. @@ -26,8 +26,8 @@ void Observer(ecs_iter_t *it) { ecs_entity_t event_id = it->event_id; // Grab Position from self and parent - Position *p_self = ecs_field(it, Position, 1); - Position *p_parent = ecs_field(it, Position, 2); + Position *p_self = ecs_field(it, Position, 0); + Position *p_parent = ecs_field(it, Position, 1); for (int i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; @@ -48,19 +48,19 @@ int main(int argc, char *argv[]) { // Create observer that listens for events from both self and parent ecs_observer(ecs, { - .filter = { .terms = { + .query = { .terms = { // Listen for Position events from self { .id = ecs_id(Position) }, // Listen for Position events from parent - { .id = ecs_id(Position), .src.flags = EcsUp, .src.trav = EcsChildOf } + { .id = ecs_id(Position), .src.id = EcsUp, .trav = EcsChildOf } }}, .events = { EcsOnSet }, .callback = Observer }); // Create entity and parent - ecs_entity_t p = ecs_new_entity(ecs, "p"); - ecs_entity_t e = ecs_new_entity(ecs, "p.e"); // Create as child of p + ecs_entity_t p = ecs_entity(ecs, { .name = "p" }); + ecs_entity_t e = ecs_entity(ecs, { .name = "p.e" }); // Create as child of p // Set Position on entity. This doesn't trigger the observer yet, since the // parent doesn't have Position yet. diff --git a/vendors/flecs/examples/c/observers/two_components/BUILD b/vendors/flecs/examples/c/observers/two_components/BUILD new file mode 100644 index 000000000..5ad9dab4d --- /dev/null +++ b/vendors/flecs/examples/c/observers/two_components/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "two_components", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/observers/two_components/src/main.c b/vendors/flecs/examples/c/observers/two_components/src/main.c index 214ea4c57..4e0fbdbef 100644 --- a/vendors/flecs/examples/c/observers/two_components/src/main.c +++ b/vendors/flecs/examples/c/observers/two_components/src/main.c @@ -2,7 +2,7 @@ #include // An observer can match multiple components/tags. Only entities that match the -// entire observer filter will be forwarded to the callback. For example, an +// entire observer query will be forwarded to the callback. For example, an // observer for Position,Velocity won't match an entity that only has Position. typedef struct { @@ -15,8 +15,8 @@ void Observer(ecs_iter_t *it) { ecs_entity_t event = it->event; ecs_entity_t event_id = it->event_id; - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; @@ -38,7 +38,7 @@ int main(int argc, char *argv[]) { ECS_OBSERVER(ecs, Observer, EcsOnSet, Position, Velocity); // Create entity - ecs_entity_t e = ecs_new_entity(ecs, "e"); + ecs_entity_t e = ecs_entity(ecs, { .name = "e" }); // Set Position (emits EcsOnSet, does not yet match observer) ecs_set(ecs, e, Position, {10, 20}); diff --git a/vendors/flecs/examples/c/observers/yield_existing/BUILD b/vendors/flecs/examples/c/observers/yield_existing/BUILD new file mode 100644 index 000000000..7057232df --- /dev/null +++ b/vendors/flecs/examples/c/observers/yield_existing/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "yield_existing", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/observers/yield_existing/src/main.c b/vendors/flecs/examples/c/observers/yield_existing/src/main.c index 5cd79d9b4..c55406a14 100644 --- a/vendors/flecs/examples/c/observers/yield_existing/src/main.c +++ b/vendors/flecs/examples/c/observers/yield_existing/src/main.c @@ -4,9 +4,6 @@ // Observers can enable a "yield_existing" feature that upon creation of the // observer produces events for all entities that match the observer query. The // feature is only implemented for the builtin EcsOnAdd and EcsOnSet events. -// -// Custom events can also implement behavior for yield_existing by adding the -// Iterable component to the event (see EcsIterable for more details). typedef struct { double x, y; @@ -17,7 +14,7 @@ void Observer(ecs_iter_t *it) { ecs_entity_t event = it->event; ecs_entity_t event_id = it->event_id; - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; @@ -35,13 +32,13 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, Position); // Create existing entities with Position component - ecs_entity_t e1 = ecs_new_entity(ecs, "e1"); + ecs_entity_t e1 = ecs_entity(ecs, { .name = "e1" }); ecs_set(ecs, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_entity(ecs, "e2"); + ecs_entity_t e2 = ecs_entity(ecs, { .name = "e2" }); ecs_set(ecs, e2, Position, {20, 30}); ecs_observer(ecs, { - .filter = { .terms = {{ .id = ecs_id(Position) }}}, + .query.terms = {{ .id = ecs_id(Position) }}, .events = { EcsOnSet }, .callback = Observer, .yield_existing = true // Trigger for existing matching entities diff --git a/vendors/flecs/examples/c/prefabs/basics/BUILD b/vendors/flecs/examples/c/prefabs/basics/BUILD new file mode 100644 index 000000000..3b0fc157c --- /dev/null +++ b/vendors/flecs/examples/c/prefabs/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/prefabs/basics/src/main.c b/vendors/flecs/examples/c/prefabs/basics/src/main.c index d51c4308a..53227d6f9 100644 --- a/vendors/flecs/examples/c/prefabs/basics/src/main.c +++ b/vendors/flecs/examples/c/prefabs/basics/src/main.c @@ -27,12 +27,17 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, Defense); + // Make Defense component inheritable. By default components are copied from + // the instance to the prefab. An inherited component is only stored on the + // prefab, and is shared across all instances. + ecs_add_pair(ecs, ecs_id(Defense), EcsOnInstantiate, EcsInherit); + // Create a SpaceShip prefab with a Defense component. - ecs_entity_t SpaceShip = ecs_new_prefab(ecs, "SpaceShip"); + ecs_entity_t SpaceShip = ecs_entity(ecs, { .name = "SpaceShip", .add = ecs_ids( EcsPrefab ) }); ecs_set(ecs, SpaceShip, Defense, {50}); // Create a prefab instance - ecs_entity_t inst = ecs_new_entity(ecs, "my_spaceship"); + ecs_entity_t inst = ecs_entity(ecs, { .name = "my_spaceship" }); ecs_add_pair(ecs, inst, EcsIsA, SpaceShip); // Because of the IsA relationship, the instance now shares the Defense @@ -47,14 +52,14 @@ int main(int argc, char *argv[]) { // Prefab components can be iterated like regular components: ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_id(Defense) } } }); ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { - Defense *d = ecs_field(&it, Defense, 1); + Defense *d = ecs_field(&it, Defense, 0); for (int i = 0; i < it.count; i ++) { printf("%s: defense: %f\n", ecs_get_name(ecs, it.entities[i]), d[i].value); diff --git a/vendors/flecs/examples/c/prefabs/hierarchy/BUILD b/vendors/flecs/examples/c/prefabs/hierarchy/BUILD new file mode 100644 index 000000000..f0a25f132 --- /dev/null +++ b/vendors/flecs/examples/c/prefabs/hierarchy/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hierarchy", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/prefabs/hierarchy/src/main.c b/vendors/flecs/examples/c/prefabs/hierarchy/src/main.c index 2e8648acc..eb2935170 100644 --- a/vendors/flecs/examples/c/prefabs/hierarchy/src/main.c +++ b/vendors/flecs/examples/c/prefabs/hierarchy/src/main.c @@ -8,25 +8,25 @@ int main(int argc, char *argv[]) { ecs_world_t *ecs = ecs_init_w_args(argc, argv); // Create a prefab hierarchy. - ecs_entity_t SpaceShip = ecs_new_prefab(ecs, "SpaceShip"); - ecs_entity_t SpaceShipEngine = ecs_new_prefab(ecs, "Engine"); + ecs_entity_t SpaceShip = ecs_entity(ecs, { .name = "SpaceShip", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t SpaceShipEngine = ecs_entity(ecs, { .name = "Engine", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, SpaceShipEngine, EcsChildOf, SpaceShip); - ecs_entity_t SpaceShipCockpit = ecs_new_prefab(ecs, "Cockpit"); + ecs_entity_t SpaceShipCockpit = ecs_entity(ecs, { .name = "Cockpit", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, SpaceShipCockpit, EcsChildOf, SpaceShip); // Instantiate the prefab. This also creates an Engine and Cockpit child // for the instance. - ecs_entity_t inst = ecs_new_entity(ecs, "my_spaceship"); + ecs_entity_t inst = ecs_entity(ecs, { .name = "my_spaceship" }); ecs_add_pair(ecs, inst, EcsIsA, SpaceShip); ecs_entity_t inst_engine = ecs_lookup_child(ecs, inst, "Engine"); ecs_entity_t inst_cockpit = ecs_lookup_child(ecs, inst, "Cockpit"); - char *path = ecs_get_fullpath(ecs, inst_engine); + char *path = ecs_get_path(ecs, inst_engine); printf("instance engine: %s\n", path); ecs_os_free(path); - path = ecs_get_fullpath(ecs, inst_cockpit); + path = ecs_get_path(ecs, inst_cockpit); printf("instance cockpit: %s\n", path); ecs_os_free(path); diff --git a/vendors/flecs/examples/c/prefabs/nested_prefabs/BUILD b/vendors/flecs/examples/c/prefabs/nested_prefabs/BUILD new file mode 100644 index 000000000..718fa26e8 --- /dev/null +++ b/vendors/flecs/examples/c/prefabs/nested_prefabs/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "nested_prefabs", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/prefabs/nested_prefabs/src/main.c b/vendors/flecs/examples/c/prefabs/nested_prefabs/src/main.c index 23eefeb08..b6b7cfaaa 100644 --- a/vendors/flecs/examples/c/prefabs/nested_prefabs/src/main.c +++ b/vendors/flecs/examples/c/prefabs/nested_prefabs/src/main.c @@ -14,10 +14,6 @@ // prefab. The reason for this is that an instantiated child is an exact copy // of the prefab child, and the prefab child only has an IsA relationship to the // nested prefab. -// -// This example shows how auto overriding (see the auto override example) can be -// used to give instantiated children from a nested prefab a private copy of an -// inherited component. typedef struct { double value; @@ -28,30 +24,28 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, TirePressure); - // Create a Wheel prefab, make sure each instantiated wheel has a private - // copy of the TirePressure component. - ecs_entity_t Wheel = ecs_new_prefab(ecs, "Wheel"); + // Create a Wheel prefab. + ecs_entity_t Wheel = ecs_entity(ecs, { .name = "Wheel", .add = ecs_ids( EcsPrefab ) }); ecs_set(ecs, Wheel, TirePressure, { 32 }); - ecs_override(ecs, Wheel, TirePressure); // Create a Car prefab with four wheels. Note how the wheel names are // prefixed with 'Car.', this is has the same effect as adding the // (ChildOf, Car) relationship. - ecs_entity_t Car = ecs_new_prefab(ecs, "Car"); - ecs_entity_t WheelFrontLeft = ecs_new_prefab(ecs, "Car.FrontLeft"); + ecs_entity_t Car = ecs_entity(ecs, { .name = "Car", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t WheelFrontLeft = ecs_entity(ecs, { .name = "Car.FrontLeft", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, WheelFrontLeft, EcsIsA, Wheel); - ecs_entity_t WheelFrontRight = ecs_new_prefab(ecs, "Car.FrontRight"); + ecs_entity_t WheelFrontRight = ecs_entity(ecs, { .name = "Car.FrontRight", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, WheelFrontRight, EcsIsA, Wheel); - ecs_entity_t WheelBackLeft = ecs_new_prefab(ecs, "Car.BackLeft"); + ecs_entity_t WheelBackLeft = ecs_entity(ecs, { .name = "Car.BackLeft", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, WheelBackLeft, EcsIsA, Wheel); - ecs_entity_t WheelBackRight = ecs_new_prefab(ecs, "Car.BackRight"); + ecs_entity_t WheelBackRight = ecs_entity(ecs, { .name = "Car.BackRight", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, WheelBackRight, EcsIsA, Wheel); // Create a prefab instance. - ecs_entity_t inst = ecs_new_entity(ecs, "my_car"); + ecs_entity_t inst = ecs_entity(ecs, { .name = "my_car" }); ecs_add_pair(ecs, inst, EcsIsA, Car); // Lookup one of the wheels diff --git a/vendors/flecs/examples/c/prefabs/override/BUILD b/vendors/flecs/examples/c/prefabs/override/BUILD new file mode 100644 index 000000000..b00394b43 --- /dev/null +++ b/vendors/flecs/examples/c/prefabs/override/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "override", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/prefabs/override/src/main.c b/vendors/flecs/examples/c/prefabs/override/src/main.c index 020439a9a..6fc8a9d42 100644 --- a/vendors/flecs/examples/c/prefabs/override/src/main.c +++ b/vendors/flecs/examples/c/prefabs/override/src/main.c @@ -1,15 +1,21 @@ #include #include -// Overriding makes it possible for a prefab instance to obtain a private copy -// of an inherited component. To override a component the regular add operation -// is used. The overridden component will be initialized with the value of the -// inherited component. +// When an entity is instantiated from a prefab, components are by default +// copied from the prefab to the instance. This behavior can be customized with +// the OnInstantiate trait, which has three options: +// +// - Override (copy to instance) +// - Inherit (inherit from prefab) +// - DontInherit (don't copy or inherit) // -// In some cases a prefab instance should always have a private copy of an -// inherited component. This can be achieved with an auto override which can be -// added to a prefab. Components with an auto override are automatically -// overridden when the prefab is instantiated. +// When a component is inheritable, it can be overridden manually by adding the +// component to the instance, which also copies the value from the prefab +// component. Additionally, when creating a prefab it is possible to flag a +// component as "auto override", which can change the behavior for a specific +// prefab from "inherit" to "override". +// +// This example shows how these different features can be used. typedef struct { double value; @@ -22,22 +28,19 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, Defense); ECS_COMPONENT(ecs, Damage); - ecs_entity_t SpaceShip = ecs_new_prefab(ecs, "SpaceShip"); + // Change the instantiation behavior for Attack and Defense to inherit. + ecs_add_pair(ecs, ecs_id(Attack), EcsOnInstantiate, EcsInherit); + ecs_add_pair(ecs, ecs_id(Defense), EcsOnInstantiate, EcsInherit); - // Attack and Defense are properties that can be shared across many - // spaceships. This saves memory, and speeds up prefab creation as we don't - // have to copy the values of Attack and Defense to private components. - ecs_set(ecs, SpaceShip, Attack, { 75 }); - ecs_set(ecs, SpaceShip, Defense, { 100 }); + ecs_entity_t SpaceShip = ecs_entity(ecs, { + .name = "SpaceShip", .add = ecs_ids( EcsPrefab ) }); - // Damage is a property that is private to a spaceship, so add an auto - // override for it. This ensures that each prefab instance will have a - // private copy of the component. - ecs_set(ecs, SpaceShip, Damage, { 0 }); - ecs_override(ecs, SpaceShip, Damage); + ecs_set(ecs, SpaceShip, Attack, { 75 }); + ecs_set(ecs, SpaceShip, Defense, { 100 }); + ecs_set(ecs, SpaceShip, Damage, { 50 }); // Create a prefab instance. - ecs_entity_t inst = ecs_new_entity(ecs, "my_instance"); + ecs_entity_t inst = ecs_entity(ecs, { .name = "my_instance" }); ecs_add_pair(ecs, inst, EcsIsA, SpaceShip); // The entity will now have a private copy of the Damage component, but not @@ -47,11 +50,11 @@ int main(int argc, char *argv[]) { printf("instance type = [%s]\n", type); ecs_os_free(type); - // Even though Attack was not automatically overridden, we can always - // override it manually afterwards by adding it: + // Even though Attack was not copied to the instance when instantiated, we + // can still override it manually afterwards by adding it to the instance: ecs_add(ecs, inst, Attack); - // The Attack component now shows up in the entity type: + // The Attack component is now owned and shows up in the entity type: type = ecs_type_str(ecs, ecs_get_type(ecs, inst)); printf("instance type = [%s]\n", type); ecs_os_free(type); @@ -72,7 +75,7 @@ int main(int argc, char *argv[]) { // instance type = [Attack, Damage, (Identifier,Name), (IsA,SpaceShip)] // attack: 75.000000 // defense: 100.000000 - // damage: 0.000000 + // damage: 50.000000 return ecs_fini(ecs); } diff --git a/vendors/flecs/examples/c/prefabs/slots/BUILD b/vendors/flecs/examples/c/prefabs/slots/BUILD new file mode 100644 index 000000000..c5714b720 --- /dev/null +++ b/vendors/flecs/examples/c/prefabs/slots/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "slots", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/prefabs/slots/src/main.c b/vendors/flecs/examples/c/prefabs/slots/src/main.c index 9a541f5cc..e73c6a8a3 100644 --- a/vendors/flecs/examples/c/prefabs/slots/src/main.c +++ b/vendors/flecs/examples/c/prefabs/slots/src/main.c @@ -29,12 +29,12 @@ int main(int argc, char *argv[]) { // Create the same prefab hierarchy as from the hierarchy example, but now // with the SlotOf relationship. - ecs_entity_t SpaceShip = ecs_new_prefab(ecs, "SpaceShip"); - ecs_entity_t Engine = ecs_new_prefab(ecs, "Engine"); + ecs_entity_t SpaceShip = ecs_entity(ecs, { .name = "SpaceShip", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t Engine = ecs_entity(ecs, { .name = "Engine", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, Engine, EcsChildOf, SpaceShip); ecs_add_pair(ecs, Engine, EcsSlotOf, SpaceShip); - ecs_entity_t Cockpit = ecs_new_prefab(ecs, "Cockpit"); + ecs_entity_t Cockpit = ecs_entity(ecs, { .name = "Cockpit", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, Cockpit, EcsChildOf, SpaceShip); ecs_add_pair(ecs, Cockpit, EcsSlotOf, SpaceShip); @@ -42,12 +42,12 @@ int main(int argc, char *argv[]) { // slots can be different from the parent. This slot could have been // added to the Cockpit prefab, but instead we register it on the top // level SpaceShip prefab. - ecs_entity_t PilotSeat = ecs_new_prefab(ecs, "PilotSeat"); + ecs_entity_t PilotSeat = ecs_entity(ecs, { .name = "PilotSeat", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, PilotSeat, EcsChildOf, Cockpit); ecs_add_pair(ecs, PilotSeat, EcsSlotOf, SpaceShip); // Create a prefab instance. - ecs_entity_t inst = ecs_new_entity(ecs, "my_spaceship"); + ecs_entity_t inst = ecs_entity(ecs, { .name = "my_spaceship" }); ecs_add_pair(ecs, inst, EcsIsA, SpaceShip); // Get the instantiated entities for the prefab slots @@ -55,15 +55,15 @@ int main(int argc, char *argv[]) { ecs_entity_t inst_cockpit = ecs_get_target(ecs, inst, Cockpit, 0); ecs_entity_t inst_seat = ecs_get_target(ecs, inst, PilotSeat, 0); - char *path = ecs_get_fullpath(ecs, inst_engine); + char *path = ecs_get_path(ecs, inst_engine); printf("instance engine: %s\n", path); ecs_os_free(path); - path = ecs_get_fullpath(ecs, inst_cockpit); + path = ecs_get_path(ecs, inst_cockpit); printf("instance cockpit: %s\n", path); ecs_os_free(path); - path = ecs_get_fullpath(ecs, inst_seat); + path = ecs_get_path(ecs, inst_seat); printf("instance seat: %s\n", path); ecs_os_free(path); diff --git a/vendors/flecs/examples/c/prefabs/variant/BUILD b/vendors/flecs/examples/c/prefabs/variant/BUILD new file mode 100644 index 000000000..605e37c57 --- /dev/null +++ b/vendors/flecs/examples/c/prefabs/variant/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "variant", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/prefabs/variant/src/main.c b/vendors/flecs/examples/c/prefabs/variant/src/main.c index b21359464..10e82d67e 100644 --- a/vendors/flecs/examples/c/prefabs/variant/src/main.c +++ b/vendors/flecs/examples/c/prefabs/variant/src/main.c @@ -2,7 +2,7 @@ #include /* Prefabs can inherit from each other, which creates prefab variants. With - * variants applications can reuse a commmon set of components and specialize it + * variants applications can reuse a common set of components and specialize it * by adding or overriding components on the variant. */ typedef struct { double value; } Attack; @@ -25,23 +25,23 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, ImpulseSpeed); // Create a base prefab for SpaceShips. - ecs_entity_t SpaceShip = ecs_new_prefab(ecs, "SpaceShip"); + ecs_entity_t SpaceShip = ecs_entity(ecs, { .name = "SpaceShip", .add = ecs_ids( EcsPrefab ) }); ecs_set(ecs, SpaceShip, ImpulseSpeed, {50}); ecs_set(ecs, SpaceShip, Defense, {25}); // Create a Freighter variant which inherits from SpaceShip - ecs_entity_t Freighter = ecs_new_prefab(ecs, "Freighter"); + ecs_entity_t Freighter = ecs_entity(ecs, { .name = "Freighter", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, Freighter, EcsIsA, SpaceShip); ecs_set(ecs, Freighter, FreightCapacity, {100}); ecs_set(ecs, Freighter, Defense, {50}); // Create a MammothFreighter variant which inherits from Freighter - ecs_entity_t MammothFreighter = ecs_new_prefab(ecs, "MammothFreighter"); + ecs_entity_t MammothFreighter = ecs_entity(ecs, { .name = "MammothFreighter", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, MammothFreighter, EcsIsA, Freighter); ecs_set(ecs, MammothFreighter, FreightCapacity, {500}); // Create a Frigate variant which inherits from SpaceShip - ecs_entity_t Frigate = ecs_new_prefab(ecs, "Frigate"); + ecs_entity_t Frigate = ecs_entity(ecs, { .name = "Frigate", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(ecs, Frigate, EcsIsA, SpaceShip); ecs_set(ecs, Frigate, Attack, {100}); ecs_set(ecs, Frigate, Defense, {75}); @@ -51,7 +51,7 @@ int main(int argc, char *argv[]) { // Create an instance of the MammothFreighter. This entity will inherit the // ImpulseSpeed from SpaceShip, Defense from Freighter and FreightCapacity // from MammothFreighter. - ecs_entity_t inst = ecs_new_entity(ecs, "my_freighter"); + ecs_entity_t inst = ecs_entity(ecs, { .name = "my_freighter" }); ecs_add_pair(ecs, inst, EcsIsA, MammothFreighter); // Add a private Position component. @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) { // Queries can match components from multiple levels of inheritance ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_id(Position) }, { .id = ecs_id(ImpulseSpeed) }, { .id = ecs_id(Defense) }, @@ -73,10 +73,10 @@ int main(int argc, char *argv[]) { ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - ImpulseSpeed *s = ecs_field(&it, ImpulseSpeed, 2); - Defense *d = ecs_field(&it, Defense, 3); - FreightCapacity *c = ecs_field(&it, FreightCapacity, 4); + Position *p = ecs_field(&it, Position, 0); + ImpulseSpeed *s = ecs_field(&it, ImpulseSpeed, 1); + Defense *d = ecs_field(&it, Defense, 2); + FreightCapacity *c = ecs_field(&it, FreightCapacity, 3); for (int i = 0; i < it.count; i ++) { printf("%s:\n", ecs_get_name(ecs, it.entities[i])); @@ -87,9 +87,6 @@ int main(int argc, char *argv[]) { } } - // Note that when matching tables with shared components, entities are - // returned one by one. See the queries/instancing example for more details. - // Output: // my_freighter: // - position: 10.000000, 20.000000 diff --git a/vendors/flecs/examples/c/queries/basics/BUILD b/vendors/flecs/examples/c/queries/basics/BUILD new file mode 100644 index 000000000..3b0fc157c --- /dev/null +++ b/vendors/flecs/examples/c/queries/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/basics/src/main.c b/vendors/flecs/examples/c/queries/basics/src/main.c index 55e78999f..46eeb400e 100644 --- a/vendors/flecs/examples/c/queries/basics/src/main.c +++ b/vendors/flecs/examples/c/queries/basics/src/main.c @@ -12,26 +12,29 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, Position); ECS_COMPONENT(ecs, Velocity); - // Create a query for Position, Velocity. Queries are the fastest way to - // iterate entities as they cache results. + // Create a cached query for Position, Velocity. Cached queries are the + // fastest way to iterate entities as they cache results. ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_id(Position) }, { .id = ecs_id(Velocity), .inout = EcsIn} - } + }, + + // QueryCache Auto automatically caches all terms that can be cached. + .cache_kind = EcsQueryCacheAuto }); // Create a few test entities for a Position, Velocity query - ecs_entity_t e1 = ecs_new_entity(ecs, "e1"); + ecs_entity_t e1 = ecs_entity(ecs, { .name = "e1" }); ecs_set(ecs, e1, Position, {10, 20}); ecs_set(ecs, e1, Velocity, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(ecs, "e2"); + ecs_entity_t e2 = ecs_entity(ecs, { .name = "e2" }); ecs_set(ecs, e2, Position, {10, 20}); ecs_set(ecs, e2, Velocity, {3, 4}); // This entity will not match as it does not have Position, Velocity - ecs_entity_t e3 = ecs_new_entity(ecs, "e3"); + ecs_entity_t e3 = ecs_entity(ecs, { .name = "e3" }); ecs_set(ecs, e3, Position, {10, 20}); // Iterate entities matching the query @@ -39,8 +42,8 @@ int main(int argc, char *argv[]) { // Outer loop, iterates archetypes while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - const Velocity *v = ecs_field(&it, Velocity, 2); + Position *p = ecs_field(&it, Position, 0); + const Velocity *v = ecs_field(&it, Velocity, 1); // Inner loop, iterates entities in archetype for (int i = 0; i < it.count; i ++) { @@ -51,35 +54,8 @@ int main(int argc, char *argv[]) { } } - // Filters are uncached queries. They are a bit slower to iterate but faster - // to create & have lower overhead as they don't have to maintain a cache. - ecs_filter_t *f = ecs_filter(ecs, { - .terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Velocity), .inout = EcsIn} - } - }); - - // Filter iteration looks the same as query iteration - it = ecs_filter_iter(ecs, f); - - while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - const Velocity *v = ecs_field(&it, Velocity, 2); - - for (int i = 0; i < it.count; i ++) { - p[i].x += v[i].x; - p[i].y += v[i].y; - printf("%s: {%f, %f}\n", ecs_get_name(ecs, it.entities[i]), - p[i].x, p[i].y); - } - } - - // Cleanup filter. Filters can allocate memory if the number of terms - // exceeds their internal buffer, or when terms have names. In this case the - // filter didn't allocate, so while fini isn't strictly necessary here, it's - // still good practice to add it. - ecs_filter_fini(f); + // Cleanup query resources. + ecs_query_fini(q); return ecs_fini(ecs); } diff --git a/vendors/flecs/examples/c/queries/change_tracking/BUILD b/vendors/flecs/examples/c/queries/change_tracking/BUILD new file mode 100644 index 000000000..9287755e4 --- /dev/null +++ b/vendors/flecs/examples/c/queries/change_tracking/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "change_tracking", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/change_tracking/src/main.c b/vendors/flecs/examples/c/queries/change_tracking/src/main.c index 4ece6726c..148abe0b4 100644 --- a/vendors/flecs/examples/c/queries/change_tracking/src/main.c +++ b/vendors/flecs/examples/c/queries/change_tracking/src/main.c @@ -5,9 +5,8 @@ // is a cheap way of eliminating redundant work, as many entities can be skipped // with a single check. // -// This example shows how to use change tracking in combination with a few other -// techniques, like using prefabs to store a single dirty state for multiple -// entities and instanced queries. +// This example shows how to use change tracking in combination with using +// prefabs to store a single dirty state for multiple entities. typedef struct { bool value; @@ -23,54 +22,58 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(world, Dirty); ECS_COMPONENT(world, Position); + // Make Dirty inheritable so that queries can match it on prefabs + ecs_add_pair(world, ecs_id(Dirty), EcsOnInstantiate, EcsInherit); + // Create a query that just reads a component. We'll use this query for // change tracking. Change tracking for a query is automatically enabled // when query::changed() is called. // Each query has its own private dirty state which is reset only when the // query is iterated. ecs_query_t *q_read = ecs_query(world, { - .filter.terms = {{ .id = ecs_id(Position), .inout = EcsIn }} + .terms = {{ .id = ecs_id(Position), .inout = EcsIn }}, + + // Change detection is only supported on cached queries. + .cache_kind = EcsQueryCacheAuto }); // Create a query that writes the component based on a Dirty state. ecs_query_t *q_write = ecs_query(world, { - .filter = { - .terms = { - // Only match if Dirty is shared from prefab - { - .id = ecs_id(Dirty), - .inout = EcsIn, - .src.flags = EcsUp - }, - { .id = ecs_id(Position) } + .terms = { + // Only match if Dirty is shared from prefab + { + .id = ecs_id(Dirty), + .inout = EcsIn, + .src.id = EcsUp, + .trav = EcsIsA }, - .instanced = true // Instanced iteration is faster (see example) + { .id = ecs_id(Position) } } }); // Create two prefabs with a Dirty component. We can use this to share a // single Dirty value for all entities in a table. - ecs_entity_t p1 = ecs_new_prefab(world, "p1"); + ecs_entity_t p1 = ecs_entity(world, { .name = "p1", .add = ecs_ids( EcsPrefab ) }); ecs_set(world, p1, Dirty, {false}); - ecs_entity_t p2 = ecs_new_prefab(world, "p2"); + ecs_entity_t p2 = ecs_entity(world, { .name = "p2", .add = ecs_ids( EcsPrefab ) }); ecs_set(world, p2, Dirty, {true}); // Create instances of p1 and p2. Because the entities have different // prefabs, they end up in different tables. - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add_pair(world, e1, EcsIsA, p1); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_pair(world, e2, EcsIsA, p1); ecs_set(world, e2, Position, {30, 40}); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_pair(world, e3, EcsIsA, p2); ecs_set(world, e3, Position, {50, 60}); - ecs_entity_t e4 = ecs_new_entity(world, "e4"); + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); ecs_add_pair(world, e4, EcsIsA, p2); ecs_set(world, e4, Position, {70, 80}); @@ -78,7 +81,7 @@ int main(int argc, char *argv[]) { // tables it is matched with has changed. Since this is the first time that // we check this and the query is matched with the tables we just created, // the function will return true. - printf("q_read changed: %d\n", ecs_query_changed(q_read, NULL)); + printf("q_read changed: %d\n", ecs_query_changed(q_read)); // The changed state will remain true until we have iterated each table. ecs_iter_t it = ecs_query_iter(world, q_read); @@ -89,18 +92,18 @@ int main(int argc, char *argv[]) { // will show up as changed. char *table_str = ecs_table_str(world, it.table); printf("it.changed for table [%s]: %d\n", table_str, - ecs_query_changed(q_read, &it)); + ecs_iter_changed(&it)); ecs_os_free(table_str); } // Now that we have iterated all tables, the dirty state is reset. - printf("q_read changed: %d\n\n", ecs_query_changed(q_read, NULL)); + printf("q_read changed: %d\n\n", ecs_query_changed(q_read)); // Iterate the write query. Because the Position term is InOut (default) // iterating the query will write to the dirty state of iterated tables. it = ecs_query_iter(world, q_write); while (ecs_query_next(&it)) { - Dirty *dirty = ecs_field(&it, Dirty, 1); + Dirty *dirty = ecs_field(&it, Dirty, 0); char *table_str = ecs_table_str(world, it.table); printf("iterate table [%s]\n", table_str); @@ -109,14 +112,14 @@ int main(int argc, char *argv[]) { // Because we enforced that Dirty is a shared component, we can check // a single value for the entire table. if (!dirty->value) { - ecs_query_skip(&it); + ecs_iter_skip(&it); table_str = ecs_table_str(world, it.table); printf("it.skip for table [%s]\n", table_str); ecs_os_free(table_str); continue; } - Position *p = ecs_field(&it, Position, 2); + Position *p = ecs_field(&it, Position, 1); // For all other tables the dirty state will be set. for (int i = 0; i < it.count; i ++) { @@ -126,14 +129,14 @@ int main(int argc, char *argv[]) { } // One of the tables has changed, so q_read.changed() will return true - printf("\nq_read changed: %d\n", ecs_query_changed(q_read, NULL)); + printf("\nq_read changed: %d\n", ecs_query_changed(q_read)); // When we iterate the read query, we'll see that one table has changed. it = ecs_query_iter(world, q_read); while (ecs_query_next(&it)) { char *table_str = ecs_table_str(world, it.table); printf("it.changed for table [%s]: %d\n", table_str, - ecs_query_changed(q_read, &it)); + ecs_iter_changed(&it)); ecs_os_free(table_str); } diff --git a/vendors/flecs/examples/c/queries/component_inheritance/BUILD b/vendors/flecs/examples/c/queries/component_inheritance/BUILD new file mode 100644 index 000000000..86aa2fd3e --- /dev/null +++ b/vendors/flecs/examples/c/queries/component_inheritance/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "component_inheritance", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/rules/component_inheritance/include/component_inheritance.h b/vendors/flecs/examples/c/queries/component_inheritance/include/component_inheritance.h similarity index 100% rename from vendors/flecs/examples/c/rules/component_inheritance/include/component_inheritance.h rename to vendors/flecs/examples/c/queries/component_inheritance/include/component_inheritance.h diff --git a/vendors/flecs/examples/c/rules/component_inheritance/include/component_inheritance/bake_config.h b/vendors/flecs/examples/c/queries/component_inheritance/include/component_inheritance/bake_config.h similarity index 100% rename from vendors/flecs/examples/c/rules/component_inheritance/include/component_inheritance/bake_config.h rename to vendors/flecs/examples/c/queries/component_inheritance/include/component_inheritance/bake_config.h diff --git a/vendors/flecs/examples/c/rules/component_inheritance/project.json b/vendors/flecs/examples/c/queries/component_inheritance/project.json similarity index 100% rename from vendors/flecs/examples/c/rules/component_inheritance/project.json rename to vendors/flecs/examples/c/queries/component_inheritance/project.json diff --git a/vendors/flecs/examples/c/rules/component_inheritance/src/main.c b/vendors/flecs/examples/c/queries/component_inheritance/src/main.c similarity index 63% rename from vendors/flecs/examples/c/rules/component_inheritance/src/main.c rename to vendors/flecs/examples/c/queries/component_inheritance/src/main.c index ba4c1563e..e5899768d 100644 --- a/vendors/flecs/examples/c/rules/component_inheritance/src/main.c +++ b/vendors/flecs/examples/c/queries/component_inheritance/src/main.c @@ -1,6 +1,8 @@ #include #include +// This example shows how queries can be used to match simple inheritance trees. + int main(int argc, char *argv[]) { ecs_world_t *ecs = ecs_init_w_args(argc, argv); @@ -23,40 +25,40 @@ int main(int argc, char *argv[]) { ECS_ENTITY(ecs, Builder, (IsA, Unit)); // Create a few units - ecs_entity_t warrior_1 = ecs_new_entity(ecs, "warrior_1"); + ecs_entity_t warrior_1 = ecs_entity(ecs, { .name = "warrior_1" }); ecs_add(ecs, warrior_1, Warrior); - ecs_entity_t warrior_2 = ecs_new_entity(ecs, "warrior_2"); + ecs_entity_t warrior_2 = ecs_entity(ecs, { .name = "warrior_2" }); ecs_add(ecs, warrior_2, Warrior); - ecs_entity_t marksman_1 = ecs_new_entity(ecs, "marksman_1"); + ecs_entity_t marksman_1 = ecs_entity(ecs, { .name = "marksman_1" }); ecs_add(ecs, marksman_1, Marksman); - ecs_entity_t marksman_2 = ecs_new_entity(ecs, "marksman_2"); + ecs_entity_t marksman_2 = ecs_entity(ecs, { .name = "marksman_2" }); ecs_add(ecs, marksman_2, Marksman); - ecs_entity_t wizard_1 = ecs_new_entity(ecs, "wizard_1"); + ecs_entity_t wizard_1 = ecs_entity(ecs, { .name = "wizard_1" }); ecs_add(ecs, wizard_1, Wizard); - ecs_entity_t wizard_2 = ecs_new_entity(ecs, "wizard_2"); + ecs_entity_t wizard_2 = ecs_entity(ecs, { .name = "wizard_2" }); ecs_add(ecs, wizard_2, Wizard); - ecs_entity_t builder_1 = ecs_new_entity(ecs, "builder_1"); + ecs_entity_t builder_1 = ecs_entity(ecs, { .name = "builder_1" }); ecs_add(ecs, builder_1, Builder); - ecs_entity_t builder_2 = ecs_new_entity(ecs, "builder_2"); + ecs_entity_t builder_2 = ecs_entity(ecs, { .name = "builder_2" }); ecs_add(ecs, builder_2, Builder); - // Create a rule to find all ranged units - ecs_rule_t *r = ecs_rule(ecs, { + // Create a query to find all ranged units + ecs_query_t *q = ecs_query(ecs, { .terms = {{ .id = RangedUnit }} }); - // Iterate the rule - ecs_iter_t it = ecs_rule_iter(ecs, r); - while (ecs_rule_next(&it)) { + // Iterate the query + ecs_iter_t it = ecs_query_iter(ecs, q); + while (ecs_query_next(&it)) { for (int i = 0; i < it.count; i ++) { printf("Unit %s found\n", ecs_get_name(ecs, it.entities[i])); } } - ecs_rule_fini(r); + ecs_query_fini(q); // Output // Unit wizard_1 found diff --git a/vendors/flecs/examples/c/rules/cyclic_variables/.gitignore b/vendors/flecs/examples/c/queries/cyclic_variables/.gitignore similarity index 100% rename from vendors/flecs/examples/c/rules/cyclic_variables/.gitignore rename to vendors/flecs/examples/c/queries/cyclic_variables/.gitignore diff --git a/vendors/flecs/examples/c/queries/cyclic_variables/BUILD b/vendors/flecs/examples/c/queries/cyclic_variables/BUILD new file mode 100644 index 000000000..45dcef7fa --- /dev/null +++ b/vendors/flecs/examples/c/queries/cyclic_variables/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "cyclic_variables", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/rules/cyclic_variables/include/cyclic_variables.h b/vendors/flecs/examples/c/queries/cyclic_variables/include/cyclic_variables.h similarity index 100% rename from vendors/flecs/examples/c/rules/cyclic_variables/include/cyclic_variables.h rename to vendors/flecs/examples/c/queries/cyclic_variables/include/cyclic_variables.h diff --git a/vendors/flecs/examples/c/rules/cyclic_variables/include/cyclic_variables/bake_config.h b/vendors/flecs/examples/c/queries/cyclic_variables/include/cyclic_variables/bake_config.h similarity index 100% rename from vendors/flecs/examples/c/rules/cyclic_variables/include/cyclic_variables/bake_config.h rename to vendors/flecs/examples/c/queries/cyclic_variables/include/cyclic_variables/bake_config.h diff --git a/vendors/flecs/examples/c/rules/cyclic_variables/project.json b/vendors/flecs/examples/c/queries/cyclic_variables/project.json similarity index 100% rename from vendors/flecs/examples/c/rules/cyclic_variables/project.json rename to vendors/flecs/examples/c/queries/cyclic_variables/project.json diff --git a/vendors/flecs/examples/c/rules/cyclic_variables/src/main.c b/vendors/flecs/examples/c/queries/cyclic_variables/src/main.c similarity index 53% rename from vendors/flecs/examples/c/rules/cyclic_variables/src/main.c rename to vendors/flecs/examples/c/queries/cyclic_variables/src/main.c index 3d3c78d18..11cd63fb7 100644 --- a/vendors/flecs/examples/c/rules/cyclic_variables/src/main.c +++ b/vendors/flecs/examples/c/queries/cyclic_variables/src/main.c @@ -1,7 +1,7 @@ #include #include -// This example shows how a rule may have terms with cyclic dependencies on +// This example shows how a query may have terms with cyclic dependencies on // variables. int main(int argc, char *argv[]) { @@ -9,10 +9,10 @@ int main(int argc, char *argv[]) { ECS_TAG(ecs, Likes); // Likes relationship - ecs_entity_t bob = ecs_new_entity(ecs, "Bob"); - ecs_entity_t alice = ecs_new_entity(ecs, "Alice"); - ecs_entity_t john = ecs_new_entity(ecs, "John"); - ecs_entity_t jane = ecs_new_entity(ecs, "Jane"); + ecs_entity_t bob = ecs_entity(ecs, { .name = "Bob" }); + ecs_entity_t alice = ecs_entity(ecs, { .name = "Alice" }); + ecs_entity_t john = ecs_entity(ecs, { .name = "John" }); + ecs_entity_t jane = ecs_entity(ecs, { .name = "Jane" }); ecs_add_pair(ecs, bob, Likes, alice); ecs_add_pair(ecs, alice, Likes, bob); @@ -20,49 +20,41 @@ int main(int argc, char *argv[]) { ecs_add_pair(ecs, jane, Likes, john); ecs_add_pair(ecs, bob, Likes, jane); // inserting a bit of drama - // The following rule will only return entities that have a cyclic Likes + // The following query will only return entities that have a cyclic Likes // relationship- that is they must both like each other. // // The equivalent query in the DSL is: // Likes($X, $Y), Likes($Y, $X) // // This is also an example of a query where all sources are variables. By - // default queries use the builtin "This" variable as subject, which is what + // default queries use the builtin "this" variable as subject, which is what // populates the entities array in the query result (accessed by the // iter::entity function). // // Because this query does not use This at all, the entities array will not // be populated, and it.count() will always be 0. - // Create a rule to find all ranged units - ecs_rule_t *r = ecs_rule(ecs, { + // Create a query to find all ranged units + ecs_query_t *q = ecs_query(ecs, { .terms = { - { - .first.id = Likes, - .src = { .name = "X", .flags = EcsIsVariable }, - .second = { .name = "Y", .flags = EcsIsVariable } - }, - { - .first.id = Likes, - .src = { .name = "Y", .flags = EcsIsVariable }, - .second = { .name = "X", .flags = EcsIsVariable } - } + { .first.id = Likes, .src.name = "$x", .second.name = "$y" }, + { .first.id = Likes, .src.name = "$y", .second.name = "$x" } } }); // Lookup the index of the variables. This will let us quickly lookup their // values while we're iterating. - int x_var = ecs_rule_find_var(r, "X"); - int y_var = ecs_rule_find_var(r, "Y"); + int x_var = ecs_query_find_var(q, "x"); + int y_var = ecs_query_find_var(q, "y"); - // Iterate the rule - ecs_iter_t it = ecs_rule_iter(ecs, r); - while (ecs_rule_next(&it)) { + // Iterate the query + ecs_iter_t it = ecs_query_iter(ecs, q); + while (ecs_query_next(&it)) { ecs_entity_t x = ecs_iter_get_var(&it, x_var); ecs_entity_t y = ecs_iter_get_var(&it, y_var); printf("%s likes %s\n", ecs_get_name(ecs, x), ecs_get_name(ecs, y)); } - ecs_rule_fini(r); + ecs_query_fini(q); // Output // Bob likes Alice @@ -70,10 +62,10 @@ int main(int argc, char *argv[]) { // John likes Jane // Alice likes Bob - // Note that the rule returns each pair twice. The reason for this is that - // the goal of the rule engine is to return all "facts" that are true + // Note that the query returns each pair twice. The reason for this is that + // the goal of the query engine is to return all "facts" that are true // within the given constraints. Since we did not give it any constraints - // that would favor a person being matched by X or Y, the rule engine + // that would favor a person being matched by X or Y, the query // returns both. return ecs_fini(ecs); diff --git a/vendors/flecs/examples/c/queries/facts/BUILD b/vendors/flecs/examples/c/queries/facts/BUILD new file mode 100644 index 000000000..c8c93bc18 --- /dev/null +++ b/vendors/flecs/examples/c/queries/facts/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "facts", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/rules/facts/include/facts.h b/vendors/flecs/examples/c/queries/facts/include/facts.h similarity index 100% rename from vendors/flecs/examples/c/rules/facts/include/facts.h rename to vendors/flecs/examples/c/queries/facts/include/facts.h diff --git a/vendors/flecs/examples/c/rules/facts/include/facts/bake_config.h b/vendors/flecs/examples/c/queries/facts/include/facts/bake_config.h similarity index 100% rename from vendors/flecs/examples/c/rules/facts/include/facts/bake_config.h rename to vendors/flecs/examples/c/queries/facts/include/facts/bake_config.h diff --git a/vendors/flecs/examples/c/rules/facts/project.json b/vendors/flecs/examples/c/queries/facts/project.json similarity index 100% rename from vendors/flecs/examples/c/rules/facts/project.json rename to vendors/flecs/examples/c/queries/facts/project.json diff --git a/vendors/flecs/examples/c/rules/facts/src/main.c b/vendors/flecs/examples/c/queries/facts/src/main.c similarity index 56% rename from vendors/flecs/examples/c/rules/facts/src/main.c rename to vendors/flecs/examples/c/queries/facts/src/main.c index 0bbcc24c4..ce7a25781 100644 --- a/vendors/flecs/examples/c/rules/facts/src/main.c +++ b/vendors/flecs/examples/c/queries/facts/src/main.c @@ -1,14 +1,14 @@ #include #include -// This example shows how to use rules for testing facts. A fact is a query that -// has no variable elements. Consider a regular ECS query like this: +// This example shows how to use queries for testing facts. A fact is a query +// that has no variable elements. Consider a regular ECS query like this: // Position, Velocity // // When written out in full, this query looks like: -// Position($This), Velocity($This) +// Position($this), Velocity($this) // -// "This" is a (builtin) query variable that is unknown before we evaluate the +// "this" is a (builtin) query variable that is unknown before we evaluate the // query. Therefore this query does not test a fact, we can't know which values // This will assume. // @@ -16,7 +16,7 @@ // IsA(Cat, Animal) // // This is a fact: the query has no elements that are unknown before evaluating -// the query. A rule that checks a fact does not return entities, but will +// the query. A query that checks a fact does not return entities, but will // instead return the reasons why a fact is true (if it is true). int main(int argc, char *argv[]) { @@ -24,10 +24,10 @@ int main(int argc, char *argv[]) { ECS_TAG(ecs, Likes); // Likes relationship - ecs_entity_t bob = ecs_new_entity(ecs, "Bob"); - ecs_entity_t alice = ecs_new_entity(ecs, "Alice"); - ecs_entity_t john = ecs_new_entity(ecs, "John"); - ecs_entity_t jane = ecs_new_entity(ecs, "Jane"); + ecs_entity_t bob = ecs_entity(ecs, { .name = "Bob" }); + ecs_entity_t alice = ecs_entity(ecs, { .name = "Alice" }); + ecs_entity_t john = ecs_entity(ecs, { .name = "John" }); + ecs_entity_t jane = ecs_entity(ecs, { .name = "Jane" }); ecs_add_pair(ecs, bob, Likes, alice); ecs_add_pair(ecs, alice, Likes, bob); @@ -35,50 +35,42 @@ int main(int argc, char *argv[]) { ecs_add_pair(ecs, jane, Likes, john); ecs_add_pair(ecs, bob, Likes, jane); // inserting a bit of drama - // Create a rule that checks if two entities like each other. By itself this - // rule is not a fact, but we can use it to check facts by populating both + // Create a query that checks if two entities like each other. By itself this + // query is not a fact, but we can use it to check facts by populating both // of its variables. // // The equivalent of this query in the DSL is: // Likes($X, $Y), Likes($Y, $X) // - // Instead of using variables we could have created a rule that referred the - // entities directly, but then we would have to create a rule for each - // fact, vs reusing a single rule for multiple facts. + // Instead of using variables we could have created a query that referred the + // entities directly, but then we would have to create a query for each + // fact, vs reusing a single query for multiple facts. // // See the setting_variables example for more details - ecs_rule_t *friends = ecs_rule(ecs, { + ecs_query_t *friends = ecs_query(ecs, { .terms = { - { - .first.id = Likes, - .src = { .name = "X", .flags = EcsIsVariable }, - .second = { .name = "Y", .flags = EcsIsVariable } - }, - { - .first.id = Likes, - .src = { .name = "Y", .flags = EcsIsVariable }, - .second = { .name = "X", .flags = EcsIsVariable } - } + { .first.id = Likes, .src.name = "$x", .second.name = "$y" }, + { .first.id = Likes, .src.name = "$x", .second.name = "$x" } } }); // Lookup the index of the variables. - int x_var = ecs_rule_find_var(friends, "X"); - int y_var = ecs_rule_find_var(friends, "Y"); + int x_var = ecs_query_find_var(friends, "x"); + int y_var = ecs_query_find_var(friends, "y"); - ecs_iter_t it = ecs_rule_iter(ecs, friends); + ecs_iter_t it = ecs_query_iter(ecs, friends); ecs_iter_set_var(&it, x_var, bob); ecs_iter_set_var(&it, y_var, alice); printf("Are Bob and Alice friends? %s\n", ecs_iter_is_true(&it) ? "Yes" : "No"); - it = ecs_rule_iter(ecs, friends); + it = ecs_query_iter(ecs, friends); ecs_iter_set_var(&it, x_var, bob); ecs_iter_set_var(&it, y_var, john); printf("Are Bob and John friends? %s\n", ecs_iter_is_true(&it) ? "Yes" : "No"); - it = ecs_rule_iter(ecs, friends); + it = ecs_query_iter(ecs, friends); ecs_iter_set_var(&it, x_var, jane); ecs_iter_set_var(&it, y_var, john); printf("Are Jane and John friends? %s\n", @@ -86,13 +78,13 @@ int main(int argc, char *argv[]) { // It doesn't matter who we assign to X or Y. After the variables are // substituted, either yields a fact that is true. - it = ecs_rule_iter(ecs, friends); + it = ecs_query_iter(ecs, friends); ecs_iter_set_var(&it, x_var, john); ecs_iter_set_var(&it, y_var, jane); printf("Are John and Jane friends? %s\n", ecs_iter_is_true(&it) ? "Yes" : "No"); - ecs_rule_fini(friends); + ecs_query_fini(friends); // Output // Are Bob and Alice friends? Yes diff --git a/vendors/flecs/examples/c/queries/group_by/BUILD b/vendors/flecs/examples/c/queries/group_by/BUILD new file mode 100644 index 000000000..68d6469e0 --- /dev/null +++ b/vendors/flecs/examples/c/queries/group_by/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "group_by", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/group_by/src/main.c b/vendors/flecs/examples/c/queries/group_by/src/main.c index acd4647ad..b931407f6 100644 --- a/vendors/flecs/examples/c/queries/group_by/src/main.c +++ b/vendors/flecs/examples/c/queries/group_by/src/main.c @@ -40,13 +40,13 @@ int main(int argc, char *argv[]) { // Grouped query ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_id(Position) } }, // Group tables by the relationship target of the "Group" relationship. // A custom group_by function can be provided to derive a group id from // a table, see the group_by_custom example for more details. - .group_by_id = Group + .group_by = Group }); // Create entities in 6 different tables with 3 group ids @@ -74,23 +74,23 @@ int main(int argc, char *argv[]) { // The query cache now looks like this: // - group First: // - table [Position, (Group, First)] - // - table [Postion, Tag, (Group, First)] + // - table [Position, Tag, (Group, First)] // // - group Second: // - table [Position, (Group, Second)] - // - table [Postion, Tag, (Group, Second)] + // - table [Position, Tag, (Group, Second)] // // - group Third: // - table [Position, (Group, Third)] - // - table [Postion, Tag, (Group, Third)] + // - table [Position, Tag, (Group, Third)] // // Iterate query, print position & table components ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); char *table_str = ecs_table_str(ecs, it.table); - char *group_str = ecs_get_fullpath(ecs, it.group_id); + char *group_str = ecs_get_path(ecs, it.group_id); printf(" - group %s: table [%s]\n", group_str, table_str); diff --git a/vendors/flecs/examples/c/queries/group_by_callbacks/BUILD b/vendors/flecs/examples/c/queries/group_by_callbacks/BUILD new file mode 100644 index 000000000..e642c39af --- /dev/null +++ b/vendors/flecs/examples/c/queries/group_by_callbacks/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "group_by_callbacks", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/group_by_callbacks/src/main.c b/vendors/flecs/examples/c/queries/group_by_callbacks/src/main.c index 2aaafb56f..63433f433 100644 --- a/vendors/flecs/examples/c/queries/group_by_callbacks/src/main.c +++ b/vendors/flecs/examples/c/queries/group_by_callbacks/src/main.c @@ -62,10 +62,10 @@ int main(int argc, char *argv[]) { // Grouped query ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_id(Position) } }, - .group_by_id = Group, + .group_by = Group, .on_group_create = on_group_create, .on_group_delete = on_group_delete }); @@ -95,15 +95,15 @@ int main(int argc, char *argv[]) { // The query cache now looks like this: // - group First: // - table [Position, (Group, First)] - // - table [Postion, Tag, (Group, First)] + // - table [Position, Tag, (Group, First)] // // - group Second: // - table [Position, (Group, Second)] - // - table [Postion, Tag, (Group, Second)] + // - table [Position, Tag, (Group, Second)] // // - group Third: // - table [Position, (Group, Third)] - // - table [Postion, Tag, (Group, Third)] + // - table [Position, Tag, (Group, Third)] // printf("\n"); @@ -111,9 +111,9 @@ int main(int argc, char *argv[]) { // Iterate query, print position & table components ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); char *table_str = ecs_table_str(ecs, it.table); - char *group_str = ecs_get_fullpath(ecs, it.group_id); + char *group_str = ecs_get_path(ecs, it.group_id); group_ctx *ctx = ecs_query_get_group_ctx(q, it.group_id); printf(" - group %s: table [%s]\n", group_str, table_str); diff --git a/vendors/flecs/examples/c/queries/group_by_custom/BUILD b/vendors/flecs/examples/c/queries/group_by_custom/BUILD new file mode 100644 index 000000000..914afd1b8 --- /dev/null +++ b/vendors/flecs/examples/c/queries/group_by_custom/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "group_by_custom", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/group_by_custom/src/main.c b/vendors/flecs/examples/c/queries/group_by_custom/src/main.c index fe36ec469..2ee3471db 100644 --- a/vendors/flecs/examples/c/queries/group_by_custom/src/main.c +++ b/vendors/flecs/examples/c/queries/group_by_custom/src/main.c @@ -40,11 +40,11 @@ int main(int argc, char *argv[]) { // Grouped query ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_id(Position) } }, - .group_by = group_by_relation, - .group_by_id = Group // Passed to id argument of group_by function + .group_by_callback = group_by_relation, + .group_by = Group // Passed to id argument of group_by function }); // Create entities in 6 different tables with 3 group ids @@ -72,23 +72,23 @@ int main(int argc, char *argv[]) { // The query cache now looks like this: // - group First: // - table [Position, (Group, First)] - // - table [Postion, Tag, (Group, First)] + // - table [Position, Tag, (Group, First)] // // - group Second: // - table [Position, (Group, Second)] - // - table [Postion, Tag, (Group, Second)] + // - table [Position, Tag, (Group, Second)] // // - group Third: // - table [Position, (Group, Third)] - // - table [Postion, Tag, (Group, Third)] + // - table [Position, Tag, (Group, Third)] // // Iterate query, print position & table components ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); char *table_str = ecs_table_str(ecs, it.table); - char *group_str = ecs_get_fullpath(ecs, it.group_id); + char *group_str = ecs_get_path(ecs, it.group_id); printf(" - group %s: table [%s]\n", group_str, table_str); diff --git a/vendors/flecs/examples/c/queries/group_iter/BUILD b/vendors/flecs/examples/c/queries/group_iter/BUILD new file mode 100644 index 000000000..ef6b66858 --- /dev/null +++ b/vendors/flecs/examples/c/queries/group_iter/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "group_iter", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/group_iter/src/main.c b/vendors/flecs/examples/c/queries/group_iter/src/main.c index f596769fb..f82ffaac6 100644 --- a/vendors/flecs/examples/c/queries/group_iter/src/main.c +++ b/vendors/flecs/examples/c/queries/group_iter/src/main.c @@ -77,11 +77,11 @@ int main(int argc, char *argv[]) { // Create a query that matches all Npc's, grouped by WorldCell ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = Npc } }, - .group_by = group_by_relation, - .group_by_id = WorldCell + .group_by_callback = group_by_relation, + .group_by = WorldCell }); // Iterate all tables @@ -89,7 +89,7 @@ int main(int argc, char *argv[]) { ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { char *table_str = ecs_table_str(ecs, it.table); - char *group_str = ecs_get_fullpath(ecs, it.group_id); + char *group_str = ecs_get_path(ecs, it.group_id); printf(" - group %s: table [%s]\n", group_str, table_str); @@ -104,10 +104,10 @@ int main(int argc, char *argv[]) { // Only iterate entities in cell 1_0 printf("Tables for cell 1_0:\n"); it = ecs_query_iter(ecs, q); - ecs_query_set_group(&it, Cell_1_0); + ecs_iter_set_group(&it, Cell_1_0); while (ecs_query_next(&it)) { char *table_str = ecs_table_str(ecs, it.table); - char *group_str = ecs_get_fullpath(ecs, it.group_id); + char *group_str = ecs_get_path(ecs, it.group_id); printf(" - group %s: table [%s]\n", group_str, table_str); diff --git a/vendors/flecs/examples/c/queries/hierarchies/BUILD b/vendors/flecs/examples/c/queries/hierarchies/BUILD new file mode 100644 index 000000000..36fc59689 --- /dev/null +++ b/vendors/flecs/examples/c/queries/hierarchies/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hierarchies", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/hierarchies/src/main.c b/vendors/flecs/examples/c/queries/hierarchies/src/main.c index 0be14962a..f7be591f2 100644 --- a/vendors/flecs/examples/c/queries/hierarchies/src/main.c +++ b/vendors/flecs/examples/c/queries/hierarchies/src/main.c @@ -16,26 +16,26 @@ int main(int argc, char *argv[]) { ECS_TAG(ecs, World); // Create a hierarchy. For an explanation see the entities/hierarchy example - ecs_entity_t sun = ecs_new_entity(ecs, "Sun"); + ecs_entity_t sun = ecs_entity(ecs, { .name = "Sun" }); ecs_add_pair(ecs, sun, ecs_id(Position), World); ecs_set_pair(ecs, sun, Position, Local, {1, 1}); - ecs_entity_t mercury = ecs_new_entity(ecs, "Mercury"); + ecs_entity_t mercury = ecs_entity(ecs, { .name = "Mercury" }); ecs_add_pair(ecs, mercury, EcsChildOf, sun); ecs_add_pair(ecs, mercury, ecs_id(Position), World); ecs_set_pair(ecs, mercury, Position, Local, {1, 1}); - ecs_entity_t venus = ecs_new_entity(ecs, "Venus"); + ecs_entity_t venus = ecs_entity(ecs, { .name = "Venus" }); ecs_add_pair(ecs, venus, EcsChildOf, sun); ecs_add_pair(ecs, venus, ecs_id(Position), World); ecs_set_pair(ecs, venus, Position, Local, {2, 2}); - ecs_entity_t earth = ecs_new_entity(ecs, "Earth"); + ecs_entity_t earth = ecs_entity(ecs, { .name = "Earth" }); ecs_add_pair(ecs, earth, EcsChildOf, sun); ecs_add_pair(ecs, earth, ecs_id(Position), World); ecs_set_pair(ecs, earth, Position, Local, {3, 3}); - ecs_entity_t moon = ecs_new_entity(ecs, "Moon"); + ecs_entity_t moon = ecs_entity(ecs, { .name = "Moon" }); ecs_add_pair(ecs, moon, EcsChildOf, earth); ecs_add_pair(ecs, moon, ecs_id(Position), World); ecs_set_pair(ecs, moon, Position, Local, {0.1, 0.1}); @@ -43,7 +43,7 @@ int main(int argc, char *argv[]) { // Create a hierarchical query to compute the global position from the // local position and the parent position. ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { // Read from entity's Local position { .id = ecs_pair(ecs_id(Position), Local), .inout = EcsIn }, // Write to entity's World position @@ -53,8 +53,8 @@ int main(int argc, char *argv[]) { { .id = ecs_pair(ecs_id(Position), World), .inout = EcsIn, - // Get from the parent, in breadth-first order (cascade) - .src.flags = EcsParent | EcsCascade, + // Get from the parent in breadth-first order (cascade) + .src.id = EcsCascade, // Make parent term optional so we also match the root (sun) .oper = EcsOptional } @@ -64,9 +64,9 @@ int main(int argc, char *argv[]) { // Do the transform ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { - const Position *p = ecs_field(&it, Position, 1); - Position *p_out = ecs_field(&it, Position, 2); - const Position *p_parent = ecs_field(&it, Position, 3); + const Position *p = ecs_field(&it, Position, 0); + Position *p_out = ecs_field(&it, Position, 1); + const Position *p_parent = ecs_field(&it, Position, 2); // Inner loop, iterates entities in archetype for (int i = 0; i < it.count; i ++) { @@ -80,12 +80,9 @@ int main(int argc, char *argv[]) { } // Print ecs positions - it = ecs_term_iter(ecs, &(ecs_term_t) { - .id = ecs_pair(ecs_id(Position), World) - }); - - while (ecs_term_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + it = ecs_each_pair_t(ecs, Position, World); + while (ecs_each_next(&it)) { + Position *p = ecs_field(&it, Position, 0); for (int i = 0; i < it.count; i ++) { printf("%s: {%f, %f}\n", ecs_get_name(ecs, it.entities[i]), p[i].x, p[i].y); diff --git a/vendors/flecs/examples/c/queries/instancing/src/main.c b/vendors/flecs/examples/c/queries/instancing/src/main.c deleted file mode 100644 index 887a98cfd..000000000 --- a/vendors/flecs/examples/c/queries/instancing/src/main.c +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include - -/* Instancing is the ability of queries to iterate results with fields that have - * different numbers of elements. The term "instancing" is borrowed from - * graphics APIs, where it means reusing the same data for multiple "instances". - * - * Query instancing works in much the same way. By default queries match all - * components on the same entity. It is however possible to request data from - * other entities, like getting the Position from the entity's parent. - * - * Instancing refers to the ability of queries to iterate components for - * multiple entities while at the same time providing "instanced" components, - * which are always provided one element at a time. - * - * Instancing is often used in combination with parent-child relationships and - * prefabs, but is applicable to any kind of query where some of the terms are - * matched on N entities, and some on a single entity. - * - * By default queries are not instanced, which means that if a result contains - * mixed fields, entities will be iterated one by one instead of in batches. - * This is safer, as code doesn't have to do anything different for owned and - * shared fields, but does come at a performance penalty. - * - * The each() iterator function always uses an instanced iterator under the - * hood. This is transparent to the application, but improves performance. For - * this reason using each() can be faster than using uninstanced iter(). - */ - -typedef struct { - double x, y; -} Position, Velocity; - -int main(int argc, char *argv[]) { - ecs_world_t *world = ecs_init_w_args(argc, argv); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - // Create a query for Position, Velocity. We'll create a few entities that - // have Velocity as owned and shared component. - ecs_query_t *q = ecs_query(world, { - .filter = { - .terms = { - // Position must always be owned by the entity - { .id = ecs_id(Position), .src.flags = EcsSelf }, - { .id = ecs_id(Velocity) } // Velocity may be shared (default) - }, - .instanced = true - } - }); - - // Create a prefab with Velocity. Prefabs are not matched with queries. - ecs_entity_t prefab = ecs_new_prefab(world, "p"); - ecs_set(world, prefab, Velocity, {1, 2}); - - // Create a few entities that own Position & share Velocity from the prefab. - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, EcsIsA, prefab); - ecs_set(world, e1, Position, {10, 20}); - - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_add_pair(world, e2, EcsIsA, prefab); - ecs_set(world, e2, Position, {10, 20}); - - // Create a few entities that own all components - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - ecs_set(world, e3, Position, {10, 20}); - ecs_set(world, e3, Velocity, {3, 4}); - - ecs_entity_t e4 = ecs_new_entity(world, "e4"); - ecs_set(world, e4, Position, {10, 20}); - ecs_set(world, e4, Velocity, {4, 5}); - - // Iterate the instanced query. Note how when a query is instanced, it needs - // to check whether a field is owned or not in order to know how to access - // it. In the case of an owned field it is iterated as an array, whereas - // in the case of a shared field, it is accessed as a pointer. - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - // Check if Velocity is owned, in which case it's accessed as array. - // Position will always be owned, since we set the term to Self. - if (ecs_field_is_self(&it, 2)) { // Velocity is term 2 - printf("Velocity is owned\n"); - for (int i = 0; i < it.count; i ++) { - // If Velocity is shared, access the field as an array. - p[i].x += v[i].x; - p[i].y += v[i].y; - printf("%s: {%f, %f}\n", ecs_get_name(it.world, it.entities[i]), - p[i].x, p[i].y); - } - } else { - printf("Velocity is shared\n"); - for (int i = 0; i < it.count; i ++) { - // If Velocity is shared, access the field as a pointer. - p[i].x += v->x; - p[i].y += v->y; - printf("%s: {%f, %f}\n", ecs_get_name(it.world, it.entities[i]), - p[i].x, p[i].y); - } - } - } - - return ecs_fini(world); - - // Output - // Velocity is shared - // e1: {11.000000, 22.000000} - // e2: {11.000000, 22.000000} - // Velocity is owned - // e3: {13.000000, 24.000000} - // e4: {14.000000, 25.000000} -} diff --git a/vendors/flecs/examples/cpp/queries/instancing/include/instancing.h b/vendors/flecs/examples/c/queries/iter_targets/include/iter_targets.h similarity index 63% rename from vendors/flecs/examples/cpp/queries/instancing/include/instancing.h rename to vendors/flecs/examples/c/queries/iter_targets/include/iter_targets.h index e1620a5bb..aac2db5b8 100644 --- a/vendors/flecs/examples/cpp/queries/instancing/include/instancing.h +++ b/vendors/flecs/examples/c/queries/iter_targets/include/iter_targets.h @@ -1,8 +1,8 @@ -#ifndef INSTANCING_H -#define INSTANCING_H +#ifndef ITER_TARGETS_H +#define ITER_TARGETS_H /* This generated file contains includes for project dependencies */ -#include "instancing/bake_config.h" +#include "iter_targets/bake_config.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/examples/c/queries/instancing/include/instancing/bake_config.h b/vendors/flecs/examples/c/queries/iter_targets/include/iter_targets/bake_config.h similarity index 90% rename from vendors/flecs/examples/c/queries/instancing/include/instancing/bake_config.h rename to vendors/flecs/examples/c/queries/iter_targets/include/iter_targets/bake_config.h index f1866b26b..12785a4f0 100644 --- a/vendors/flecs/examples/c/queries/instancing/include/instancing/bake_config.h +++ b/vendors/flecs/examples/c/queries/iter_targets/include/iter_targets/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef INSTANCING_BAKE_CONFIG_H -#define INSTANCING_BAKE_CONFIG_H +#ifndef ITER_TARGETS_BAKE_CONFIG_H +#define ITER_TARGETS_BAKE_CONFIG_H /* Headers of public dependencies */ #include diff --git a/vendors/flecs/examples/c/queries/instancing/project.json b/vendors/flecs/examples/c/queries/iter_targets/project.json similarity index 82% rename from vendors/flecs/examples/c/queries/instancing/project.json rename to vendors/flecs/examples/c/queries/iter_targets/project.json index a64c1486d..568db1538 100644 --- a/vendors/flecs/examples/c/queries/instancing/project.json +++ b/vendors/flecs/examples/c/queries/iter_targets/project.json @@ -1,5 +1,5 @@ { - "id": "instancing", + "id": "iter_targets", "type": "application", "value": { "use": [ diff --git a/vendors/flecs/examples/c/queries/iter_targets/src/main.c b/vendors/flecs/examples/c/queries/iter_targets/src/main.c new file mode 100644 index 000000000..8e5e5bd26 --- /dev/null +++ b/vendors/flecs/examples/c/queries/iter_targets/src/main.c @@ -0,0 +1,51 @@ +#include +#include + +// This example shows how to iterate matching targets of a relationship without +// iterating the same entity multiple times. +// +// When creating a (Relationship, *) query, the query will return one result per +// matching relationship pair, which returns the same entity multiple times. +// This example uses a (Relationship, _) query which at most returns a single +// result per matching pair, and the returned table record to iterate the +// targets for the current result. + +int main(int argc, char *argv[]) { + ecs_world_t *ecs = ecs_init_w_args(argc, argv); + + ECS_TAG(ecs, Eats); + ECS_TAG(ecs, Pizza); + ECS_TAG(ecs, Salad); + + ecs_entity_t bob = ecs_entity(ecs, { .name = "Bob" }); + ecs_add_pair(ecs, bob, Eats, Pizza); + ecs_add_pair(ecs, bob, Eats, Salad); + + ecs_query_t *q = ecs_query(ecs, { + // EcsAny ensures that only a single result is returned per entity, as + // opposed to EcsWildcard which returns a result per matched pair. + .terms = {{ .id = ecs_pair(Eats, EcsAny) }} + }); + + ecs_iter_t it = ecs_query_iter(ecs, q); + while (ecs_query_next(&it)) { + const ecs_type_t *type = ecs_table_get_type(it.table); + const ecs_table_record_t *tr = it.trs[0]; + + for (int i = 0; i < it.count; i ++) { + printf("%s eats:\n", ecs_get_name(ecs, it.entities[i])); + for (int t = 0; t < tr->count; t ++) { + ecs_entity_t tgt = ecs_pair_second( + ecs, type->array[t + tr->index]); + printf(" - %s\n", ecs_get_name(ecs, tgt)); + } + } + } + + return ecs_fini(ecs); + + // Output: + // Bob eats: + // - Pizza + // - Salad +} diff --git a/vendors/flecs/examples/c/rules/setting_variables/.gitignore b/vendors/flecs/examples/c/queries/setting_variables/.gitignore similarity index 100% rename from vendors/flecs/examples/c/rules/setting_variables/.gitignore rename to vendors/flecs/examples/c/queries/setting_variables/.gitignore diff --git a/vendors/flecs/examples/c/queries/setting_variables/BUILD b/vendors/flecs/examples/c/queries/setting_variables/BUILD new file mode 100644 index 000000000..77e186613 --- /dev/null +++ b/vendors/flecs/examples/c/queries/setting_variables/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "setting_variables", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/rules/setting_variables/include/setting_variables.h b/vendors/flecs/examples/c/queries/setting_variables/include/setting_variables.h similarity index 100% rename from vendors/flecs/examples/c/rules/setting_variables/include/setting_variables.h rename to vendors/flecs/examples/c/queries/setting_variables/include/setting_variables.h diff --git a/vendors/flecs/examples/c/rules/setting_variables/include/setting_variables/bake_config.h b/vendors/flecs/examples/c/queries/setting_variables/include/setting_variables/bake_config.h similarity index 100% rename from vendors/flecs/examples/c/rules/setting_variables/include/setting_variables/bake_config.h rename to vendors/flecs/examples/c/queries/setting_variables/include/setting_variables/bake_config.h diff --git a/vendors/flecs/examples/c/rules/setting_variables/project.json b/vendors/flecs/examples/c/queries/setting_variables/project.json similarity index 100% rename from vendors/flecs/examples/c/rules/setting_variables/project.json rename to vendors/flecs/examples/c/queries/setting_variables/project.json diff --git a/vendors/flecs/examples/c/rules/setting_variables/src/main.c b/vendors/flecs/examples/c/queries/setting_variables/src/main.c similarity index 69% rename from vendors/flecs/examples/c/rules/setting_variables/src/main.c rename to vendors/flecs/examples/c/queries/setting_variables/src/main.c index 9a5b04e77..5d8a7abe5 100644 --- a/vendors/flecs/examples/c/rules/setting_variables/src/main.c +++ b/vendors/flecs/examples/c/queries/setting_variables/src/main.c @@ -2,7 +2,7 @@ #include // This example extends the component_inheritance example, and shows how -// we can use a single rule to match units from different players and platoons +// we can use a single query to match units from different players and platoons // by setting query variables before we iterate. // // The units in this example belong to a platoon, with the platoons belonging @@ -35,9 +35,9 @@ int main(int argc, char *argv[]) { // Give first player a name so we can look it up later if (p == 0) { - player = ecs_new_entity(ecs, "MyPlayer"); + player = ecs_entity(ecs, { .name = "MyPlayer" }); } else { - player = ecs_new_id(ecs); + player = ecs_new(ecs); } // Add player tag so we can query for all players if we want to @@ -50,59 +50,52 @@ int main(int argc, char *argv[]) { ecs_add(ecs, platoon, Platoon); // Add warriors, wizards and marksmen to the platoon - ecs_entity_t warrior = ecs_new(ecs, Warrior); + ecs_entity_t warrior = ecs_new_w(ecs, Warrior); ecs_add_pair(ecs, warrior, Platoon, platoon); - ecs_entity_t marksman = ecs_new(ecs, Marksman); + ecs_entity_t marksman = ecs_new_w(ecs, Marksman); ecs_add_pair(ecs, marksman, Platoon, platoon); - ecs_entity_t wizard = ecs_new(ecs, Wizard); + ecs_entity_t wizard = ecs_new_w(ecs, Wizard); ecs_add_pair(ecs, wizard, Platoon, platoon); } } - // Create a rule to find all RangedUnits for a platoon/player. The + // Create a query to find all RangedUnits for a platoon/player. The // equivalent query in the query DSL would look like this: // (Platoon, $Platoon), Player($Platoon, $Player) // // The way to read how this query is evaluated is: - // - find all entities with (Platoon, *), store * in _Platoon - // - check if _Platoon has (Player, *), store * in _Player - ecs_rule_t *r = ecs_rule(ecs, { + // - find all entities with (Platoon, *), store * in $platoon + // - check if $platoon has (Player, *), store * in $player + ecs_query_t *q = ecs_query(ecs, { .terms = { { .first.id = RangedUnit }, - { - .first.id = Platoon, - .second = { .name = "Platoon", .flags = EcsIsVariable }, - }, - { - .first.id = Player, - .src = { .name = "Platoon", .flags = EcsIsVariable }, - .second = { .name = "Player", .flags = EcsIsVariable }, - } + { .first.id = Platoon, .second.name = "$platoon" }, + { .first.id = Player, .src.name = "$platoon", .second.name = "$player" } } }); - // If we would iterate this rule it would return all ranged units for all + // If we would iterate this query it would return all ranged units for all // platoons & for all players. We can limit the results to just a single // platoon or a single player setting a variable beforehand. In this example // we'll just find all platoons & ranged units for a single player. - int player_var = ecs_rule_find_var(r, "Player"); - int platoon_var = ecs_rule_find_var(r, "Platoon"); + int player_var = ecs_query_find_var(q, "player"); + int platoon_var = ecs_query_find_var(q, "platoon"); - // Iterate rule, limit the results to units of MyPlayer - ecs_iter_t it = ecs_rule_iter(ecs, r); + // Iterate query, limit the results to units of MyPlayer + ecs_iter_t it = ecs_query_iter(ecs, q); ecs_iter_set_var(&it, player_var, ecs_lookup(ecs, "MyPlayer")); - while (ecs_rule_next(&it)) { + while (ecs_query_next(&it)) { ecs_entity_t player = ecs_iter_get_var(&it, player_var); ecs_entity_t platoon = ecs_iter_get_var(&it, platoon_var); - char *platoon_str = ecs_get_fullpath(ecs, platoon); - char *class_str = ecs_id_str(ecs, ecs_field_id(&it, 1)); + char *platoon_str = ecs_get_path(ecs, platoon); + char *class_str = ecs_id_str(ecs, ecs_field_id(&it, 0)); for (int i = 0; i < it.count; i ++) { - char *unit_str = ecs_get_fullpath(ecs, it.entities[i]); + char *unit_str = ecs_get_path(ecs, it.entities[i]); printf("Unit %s of class %s in platoon %s for player %s\n", unit_str, class_str, platoon_str, ecs_get_name(ecs, player)); @@ -113,7 +106,7 @@ int main(int argc, char *argv[]) { ecs_os_free(platoon_str); } - ecs_rule_fini(r); + ecs_query_fini(q); // Output // Unit 513 of class Wizard in platoon 510 for player MyPlayer diff --git a/vendors/flecs/examples/c/queries/singleton/BUILD b/vendors/flecs/examples/c/queries/singleton/BUILD new file mode 100644 index 000000000..75d0f3ebf --- /dev/null +++ b/vendors/flecs/examples/c/queries/singleton/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "singleton", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/singleton/src/main.c b/vendors/flecs/examples/c/queries/singleton/src/main.c index 49ec36acb..de8247115 100644 --- a/vendors/flecs/examples/c/queries/singleton/src/main.c +++ b/vendors/flecs/examples/c/queries/singleton/src/main.c @@ -22,9 +22,9 @@ int main(int argc, char *argv[]) { // Set singleton ecs_singleton_set(ecs, Gravity, { 9.81 }); - ecs_entity_t e1 = ecs_new_entity(ecs, "e1"); - ecs_entity_t e2 = ecs_new_entity(ecs, "e2"); - ecs_entity_t e3 = ecs_new_entity(ecs, "e3"); + ecs_entity_t e1 = ecs_entity(ecs, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(ecs, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(ecs, { .name = "e3" }); // Set Velocity ecs_set(ecs, e1, Velocity, {0, 0}); @@ -33,15 +33,11 @@ int main(int argc, char *argv[]) { // Create query that matches Gravity as singleton ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_id(Velocity) }, // A singleton is a component matched on itself { .id = ecs_id(Gravity), .src.id = ecs_id(Gravity) } - }, - - // Optional, but lets us iterate entities in bulk even though the query - // returns fields with mixed counts (see query manual). - .filter.instanced = true + } }); // In a query string expression you can use the $ shortcut for singletons: @@ -49,8 +45,8 @@ int main(int argc, char *argv[]) { ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { - Velocity *v = ecs_field(&it, Velocity, 1); - Gravity *g = ecs_field(&it, Gravity, 2); + Velocity *v = ecs_field(&it, Velocity, 0); + Gravity *g = ecs_field(&it, Gravity, 1); for (int i = 0; i < it.count; i ++) { // Make sure to access g as a pointer, as we only have a single diff --git a/vendors/flecs/examples/c/queries/sorting/BUILD b/vendors/flecs/examples/c/queries/sorting/BUILD new file mode 100644 index 000000000..12b2bd6bf --- /dev/null +++ b/vendors/flecs/examples/c/queries/sorting/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "sorting", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/sorting/src/main.c b/vendors/flecs/examples/c/queries/sorting/src/main.c index 7d771d5cf..c07b2d2ee 100644 --- a/vendors/flecs/examples/c/queries/sorting/src/main.c +++ b/vendors/flecs/examples/c/queries/sorting/src/main.c @@ -18,7 +18,7 @@ int compare_position( } void print_position(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i ++) { printf("{%.1f, %.1f}\n", p[i].x, p[i].y); } @@ -37,27 +37,27 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(ecs, Position); - ecs_entity_t e = ecs_set(ecs, 0, Position, {1, 0}); - ecs_set(ecs, 0, Position, {6, 0}); - ecs_set(ecs, 0, Position, {2, 0}); - ecs_set(ecs, 0, Position, {5, 0}); - ecs_set(ecs, 0, Position, {4, 0}); + ecs_entity_t e = ecs_insert(ecs, ecs_value(Position, {1, 0})); + ecs_insert(ecs, ecs_value(Position, {6, 0})); + ecs_insert(ecs, ecs_value(Position, {2, 0})); + ecs_insert(ecs, ecs_value(Position, {5, 0})); + ecs_insert(ecs, ecs_value(Position, {4, 0})); // Create a sorted system ecs_entity_t sys = ecs_system(ecs, { .query = { - .filter.terms = {{ .id = ecs_id(Position) }}, - .order_by = (ecs_order_by_action_t)compare_position, - .order_by_component = ecs_id(Position) + .terms = {{ .id = ecs_id(Position) }}, + .order_by_callback = (ecs_order_by_action_t)compare_position, + .order_by = ecs_id(Position) }, .callback = print_position }); // Create sorted query ecs_query_t *q = ecs_query(ecs, { - .filter.terms = {{ .id = ecs_id(Position) }}, - .order_by = (ecs_order_by_action_t)compare_position, - .order_by_component = ecs_id(Position) + .terms = {{ .id = ecs_id(Position) }}, + .order_by_callback = (ecs_order_by_action_t)compare_position, + .order_by = ecs_id(Position) }); // Iterate query, print values of Position @@ -72,7 +72,7 @@ int main(int argc, char *argv[]) { print_query(ecs, q); // Create new entity to show that data is also sorted for system - ecs_set(ecs, 0, Position, {3, 0}); + ecs_insert(ecs, ecs_value(Position, {3, 0})); printf("\n-- System iteration\n"); ecs_run(ecs, sys, 0, NULL); diff --git a/vendors/flecs/examples/c/queries/transitive_queries/BUILD b/vendors/flecs/examples/c/queries/transitive_queries/BUILD new file mode 100644 index 000000000..21cc0b045 --- /dev/null +++ b/vendors/flecs/examples/c/queries/transitive_queries/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "transitive_queries", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/rules/transitive_queries/include/transitive_queries.h b/vendors/flecs/examples/c/queries/transitive_queries/include/transitive_queries.h similarity index 100% rename from vendors/flecs/examples/c/rules/transitive_queries/include/transitive_queries.h rename to vendors/flecs/examples/c/queries/transitive_queries/include/transitive_queries.h diff --git a/vendors/flecs/examples/c/rules/transitive_queries/include/transitive_queries/bake_config.h b/vendors/flecs/examples/c/queries/transitive_queries/include/transitive_queries/bake_config.h similarity index 100% rename from vendors/flecs/examples/c/rules/transitive_queries/include/transitive_queries/bake_config.h rename to vendors/flecs/examples/c/queries/transitive_queries/include/transitive_queries/bake_config.h diff --git a/vendors/flecs/examples/c/rules/transitive_queries/project.json b/vendors/flecs/examples/c/queries/transitive_queries/project.json similarity index 100% rename from vendors/flecs/examples/c/rules/transitive_queries/project.json rename to vendors/flecs/examples/c/queries/transitive_queries/project.json diff --git a/vendors/flecs/examples/c/rules/transitive_queries/src/main.c b/vendors/flecs/examples/c/queries/transitive_queries/src/main.c similarity index 67% rename from vendors/flecs/examples/c/rules/transitive_queries/src/main.c rename to vendors/flecs/examples/c/queries/transitive_queries/src/main.c index 122aee7dd..57ff2596a 100644 --- a/vendors/flecs/examples/c/rules/transitive_queries/src/main.c +++ b/vendors/flecs/examples/c/queries/transitive_queries/src/main.c @@ -18,7 +18,7 @@ int main(int argc, char *argv[]) { ecs_world_t *ecs = ecs_init_w_args(argc, argv); // LocatedIn relationship - ecs_entity_t LocatedIn = ecs_new_id(ecs); + ecs_entity_t LocatedIn = ecs_new(ecs); ecs_add_id(ecs, LocatedIn, EcsTransitive); // Tags @@ -30,94 +30,88 @@ int main(int argc, char *argv[]) { ECS_TAG(ecs, Person); // Populate the store with locations - ecs_entity_t earth = ecs_new_entity(ecs, "Earth"); + ecs_entity_t earth = ecs_entity(ecs, { .name = "Earth" }); ecs_add(ecs, earth, Planet); // Continents - ecs_entity_t north_america = ecs_new_entity(ecs, "NorthAmerica"); + ecs_entity_t north_america = ecs_entity(ecs, { .name = "NorthAmerica" }); ecs_add(ecs, north_america, Continent); ecs_add_pair(ecs, north_america, LocatedIn, earth); - ecs_entity_t europe = ecs_new_entity(ecs, "Europe"); + ecs_entity_t europe = ecs_entity(ecs, { .name = "Europe" }); ecs_add(ecs, europe, Continent); ecs_add_pair(ecs, europe, LocatedIn, earth); // Countries - ecs_entity_t united_states = ecs_new_entity(ecs, "UnitedStates"); + ecs_entity_t united_states = ecs_entity(ecs, { .name = "UnitedStates" }); ecs_add(ecs, united_states, Country); ecs_add_pair(ecs, united_states, LocatedIn, north_america); - ecs_entity_t netherlands = ecs_new_entity(ecs, "Netherlands"); + ecs_entity_t netherlands = ecs_entity(ecs, { .name = "Netherlands" }); ecs_add(ecs, netherlands, Country); ecs_add_pair(ecs, netherlands, LocatedIn, europe); // States - ecs_entity_t california = ecs_new_entity(ecs, "California"); + ecs_entity_t california = ecs_entity(ecs, { .name = "California" }); ecs_add(ecs, california, State); ecs_add_pair(ecs, california, LocatedIn, united_states); - ecs_entity_t washington = ecs_new_entity(ecs, "Washington"); + ecs_entity_t washington = ecs_entity(ecs, { .name = "Washington" }); ecs_add(ecs, washington, State); ecs_add_pair(ecs, washington, LocatedIn, united_states); - ecs_entity_t noord_holland = ecs_new_entity(ecs, "NoordHolland"); + ecs_entity_t noord_holland = ecs_entity(ecs, { .name = "NoordHolland" }); ecs_add(ecs, noord_holland, State); ecs_add_pair(ecs, noord_holland, LocatedIn, netherlands); // Cities - ecs_entity_t san_francisco = ecs_new_entity(ecs, "SanFrancisco"); + ecs_entity_t san_francisco = ecs_entity(ecs, { .name = "SanFrancisco" }); ecs_add(ecs, san_francisco, City); ecs_add_pair(ecs, san_francisco, LocatedIn, california); - ecs_entity_t seattle = ecs_new_entity(ecs, "Seattle"); + ecs_entity_t seattle = ecs_entity(ecs, { .name = "Seattle" }); ecs_add(ecs, seattle, City); ecs_add_pair(ecs, seattle, LocatedIn, washington); - ecs_entity_t amsterdam = ecs_new_entity(ecs, "Amsterdam"); + ecs_entity_t amsterdam = ecs_entity(ecs, { .name = "Amsterdam" }); ecs_add(ecs, amsterdam, City); ecs_add_pair(ecs, amsterdam, LocatedIn, noord_holland); // Inhabitants - ecs_entity_t bob = ecs_new_entity(ecs, "Bob"); + ecs_entity_t bob = ecs_entity(ecs, { .name = "Bob" }); ecs_add(ecs, bob, Person); ecs_add_pair(ecs, bob, LocatedIn, san_francisco); - ecs_entity_t alice = ecs_new_entity(ecs, "Alice"); + ecs_entity_t alice = ecs_entity(ecs, { .name = "Alice" }); ecs_add(ecs, alice, Person); ecs_add_pair(ecs, alice, LocatedIn, seattle); - ecs_entity_t job = ecs_new_entity(ecs, "Job"); + ecs_entity_t job = ecs_entity(ecs, { .name = "Job" }); ecs_add(ecs, job, Person); ecs_add_pair(ecs, job, LocatedIn, amsterdam); // Create a query that finds the countries persons live in. Note that these // have not been explicitly added to the Person entities, but because the - // LocatedIn is transitive, the rule engine will traverse the relationship + // LocatedIn is transitive, the query engine will traverse the relationship // until it found something that is a country. // // The equivalent of this query in the DSL is: - // Person, (LocatedIn, $Location), Country($Location) - ecs_rule_t *r = ecs_rule(ecs, { + // Person, (LocatedIn, $location), Country($location) + ecs_query_t *q = ecs_query(ecs, { .terms = { { .id = Person }, - { - .first.id = LocatedIn, - .second = { .name = "Location", .flags = EcsIsVariable }, - }, - { - .first.id = Country, - .src = { .name = "Location", .flags = EcsIsVariable }, - }, + { .first.id = LocatedIn, .second.name = "$location" }, + { .first.id = Country, .src.name = "$location" } } }); // Lookup the index of the variable. This will let us quickly lookup its // value while we're iterating. - int location_var = ecs_rule_find_var(r, "Location"); + int location_var = ecs_query_find_var(q, "location"); - // Iterate the rule - ecs_iter_t it = ecs_rule_iter(ecs, r); - while (ecs_rule_next(&it)) { + // Iterate the query + ecs_iter_t it = ecs_query_iter(ecs, q); + while (ecs_query_next(&it)) { ecs_entity_t location = ecs_iter_get_var(&it, location_var); for (int i = 0; i < it.count; i ++) { printf("%s lives in %s\n", @@ -126,7 +120,7 @@ int main(int argc, char *argv[]) { } } - ecs_rule_fini(r); + ecs_query_fini(q); // Output: // Bob lives in UnitedStates diff --git a/vendors/flecs/examples/c/queries/variables/BUILD b/vendors/flecs/examples/c/queries/variables/BUILD new file mode 100644 index 000000000..5cbc1470d --- /dev/null +++ b/vendors/flecs/examples/c/queries/variables/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "variables", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/rules/basics/include/basics.h b/vendors/flecs/examples/c/queries/variables/include/variables.h similarity index 66% rename from vendors/flecs/examples/c/rules/basics/include/basics.h rename to vendors/flecs/examples/c/queries/variables/include/variables.h index 25fdbeffa..7d3167254 100644 --- a/vendors/flecs/examples/c/rules/basics/include/basics.h +++ b/vendors/flecs/examples/c/queries/variables/include/variables.h @@ -1,8 +1,8 @@ -#ifndef BASICS_H -#define BASICS_H +#ifndef VARIABLES_H +#define VARIABLES_H /* This generated file contains includes for project dependencies */ -#include "basics/bake_config.h" +#include "variables/bake_config.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/examples/cpp/rules/basics/include/basics/bake_config.h b/vendors/flecs/examples/c/queries/variables/include/variables/bake_config.h similarity index 91% rename from vendors/flecs/examples/cpp/rules/basics/include/basics/bake_config.h rename to vendors/flecs/examples/c/queries/variables/include/variables/bake_config.h index 0fcd5d6e7..f4997a774 100644 --- a/vendors/flecs/examples/cpp/rules/basics/include/basics/bake_config.h +++ b/vendors/flecs/examples/c/queries/variables/include/variables/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef BASICS_BAKE_CONFIG_H -#define BASICS_BAKE_CONFIG_H +#ifndef VARIABLES_BAKE_CONFIG_H +#define VARIABLES_BAKE_CONFIG_H /* Headers of public dependencies */ #include diff --git a/vendors/flecs/examples/c/rules/basics/project.json b/vendors/flecs/examples/c/queries/variables/project.json similarity index 84% rename from vendors/flecs/examples/c/rules/basics/project.json rename to vendors/flecs/examples/c/queries/variables/project.json index bfc33fb8f..8f6803c21 100644 --- a/vendors/flecs/examples/c/rules/basics/project.json +++ b/vendors/flecs/examples/c/queries/variables/project.json @@ -1,5 +1,5 @@ { - "id": "basics", + "id": "variables", "type": "application", "value": { "use": [ diff --git a/vendors/flecs/examples/c/rules/basics/src/main.c b/vendors/flecs/examples/c/queries/variables/src/main.c similarity index 51% rename from vendors/flecs/examples/c/rules/basics/src/main.c rename to vendors/flecs/examples/c/queries/variables/src/main.c index c33c928f6..8a71b398b 100644 --- a/vendors/flecs/examples/c/rules/basics/src/main.c +++ b/vendors/flecs/examples/c/queries/variables/src/main.c @@ -1,4 +1,4 @@ -#include +#include #include int main(int argc, char *argv[]) { @@ -7,31 +7,28 @@ int main(int argc, char *argv[]) { ECS_TAG(ecs, Eats); ECS_TAG(ecs, Healthy); - ecs_entity_t Apples = ecs_new_entity(ecs, "Apples"); - ecs_entity_t Salad = ecs_new_entity(ecs, "Salad"); - ecs_entity_t Burgers = ecs_new_entity(ecs, "Burgers"); - ecs_entity_t Pizza = ecs_new_entity(ecs, "Pizza"); - ecs_entity_t Chocolate = ecs_new_entity(ecs, "Chocolate"); + ecs_entity_t Apples = ecs_entity(ecs, { .name = "Apples" }); + ecs_entity_t Salad = ecs_entity(ecs, { .name = "Salad" }); + ecs_entity_t Burgers = ecs_entity(ecs, { .name = "Burgers" }); + ecs_entity_t Pizza = ecs_entity(ecs, { .name = "Pizza" }); + ecs_entity_t Chocolate = ecs_entity(ecs, { .name = "Chocolate" }); ecs_add(ecs, Apples, Healthy); ecs_add(ecs, Salad, Healthy); - ecs_entity_t bob = ecs_new_entity(ecs, "Bob"); + ecs_entity_t bob = ecs_entity(ecs, { .name = "Bob" }); ecs_add_pair(ecs, bob, Eats, Apples); ecs_add_pair(ecs, bob, Eats, Burgers); ecs_add_pair(ecs, bob, Eats, Pizza); - ecs_entity_t alice = ecs_new_entity(ecs, "Alice"); + ecs_entity_t alice = ecs_entity(ecs, { .name = "Alice" }); ecs_add_pair(ecs, alice, Eats, Salad); ecs_add_pair(ecs, alice, Eats, Chocolate); ecs_add_pair(ecs, alice, Eats, Apples); - // Here we're creating a rule that in the query DSL would look like this: - // Eats($This, $Food), Healthy($Food) - // - // Rules are similar to queries, but support more advanced features. This - // example shows how the basics of how to use rules & variables. - ecs_rule_t *r = ecs_rule(ecs, { + // Here we're creating a query that in the query DSL would look like this: + // Eats($this, $Food), Healthy($Food) + ecs_query_t *q = ecs_query(ecs, { .terms = { // Query variables are like wildcards, but enforce that the entity // substituted by the wildcard is the same across terms. @@ -40,24 +37,20 @@ int main(int argc, char *argv[]) { // substituted by the * for Eats is the same as for Healthy: // (Eats, *), Healthy(*) // - // By replacing * with _Food, both terms are constrained to use the - // same entity. - { .first.id = Eats, .second = { - .name = "Food", .flags = EcsIsVariable }, - }, - { .first.id = Healthy, .src = { - .name = "Food", .flags = EcsIsVariable } - } + // By replacing * with $food, both terms will be constrained to + // match the same entity for the wildcard. + { .first.id = Eats, .second.name = "$food" }, + { .first.id = Healthy, .src.name = "$food" } } }); // Lookup the index of the variable. This will let us quickly lookup its // value while we're iterating. - int food_var = ecs_rule_find_var(r, "Food"); + int food_var = ecs_query_find_var(q, "food"); - // Iterate the rule - ecs_iter_t it = ecs_rule_iter(ecs, r); - while (ecs_rule_next(&it)) { + // Iterate the query + ecs_iter_t it = ecs_query_iter(ecs, q); + while (ecs_query_next(&it)) { ecs_entity_t food = ecs_iter_get_var(&it, food_var); for (int i = 0; i < it.count; i ++) { printf("%s eats %s\n", ecs_get_name(ecs, it.entities[i]), @@ -65,8 +58,7 @@ int main(int argc, char *argv[]) { } } - // Rules need to be explicitly deleted - ecs_rule_fini(r); + ecs_query_fini(q); // Output // Bob eats Apples diff --git a/vendors/flecs/examples/c/queries/wildcards/BUILD b/vendors/flecs/examples/c/queries/wildcards/BUILD new file mode 100644 index 000000000..9d4a12177 --- /dev/null +++ b/vendors/flecs/examples/c/queries/wildcards/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "wildcards", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/queries/wildcards/src/main.c b/vendors/flecs/examples/c/queries/wildcards/src/main.c index e48cb20ae..ded606147 100644 --- a/vendors/flecs/examples/c/queries/wildcards/src/main.c +++ b/vendors/flecs/examples/c/queries/wildcards/src/main.c @@ -17,22 +17,22 @@ int main(int argc, char *argv[]) { // Create a query that matches edible components ecs_query_t *q = ecs_query(ecs, { - .filter.terms = {{ .id = ecs_pair(ecs_id(Eats), EcsWildcard ) }} + .terms = {{ .id = ecs_pair(ecs_id(Eats), EcsWildcard ) }} }); // Create a few entities that match the query - ecs_entity_t bob = ecs_new_entity(ecs, "Bob"); + ecs_entity_t bob = ecs_entity(ecs, { .name = "Bob" }); ecs_set_pair(ecs, bob, Eats, Apples, {10}); ecs_set_pair(ecs, bob, Eats, Pears, {5}); - ecs_entity_t alice = ecs_new_entity(ecs, "Alice"); + ecs_entity_t alice = ecs_entity(ecs, { .name = "Alice" }); ecs_set_pair(ecs, alice, Eats, Apples, {4}); // Iterate the query ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { - Eats *eats = ecs_field(&it, Eats, 1); - ecs_id_t pair = ecs_field_id(&it, 1); + Eats *eats = ecs_field(&it, Eats, 0); + ecs_id_t pair = ecs_field_id(&it, 0); ecs_entity_t food = ecs_pair_second(ecs, pair); for (int i = 0; i < it.count; i ++) { diff --git a/vendors/flecs/examples/c/reflection/auto_define_enum/BUILD b/vendors/flecs/examples/c/reflection/auto_define_enum/BUILD new file mode 100644 index 000000000..a933dccdf --- /dev/null +++ b/vendors/flecs/examples/c/reflection/auto_define_enum/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "auto_define_enum", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/auto_define_nested_struct/BUILD b/vendors/flecs/examples/c/reflection/auto_define_nested_struct/BUILD new file mode 100644 index 000000000..173ff2897 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/auto_define_nested_struct/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "auto_define_nested_struct", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/auto_define_struct/BUILD b/vendors/flecs/examples/c/reflection/auto_define_struct/BUILD new file mode 100644 index 000000000..16dc83925 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/auto_define_struct/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "auto_define_struct", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/basics/BUILD b/vendors/flecs/examples/c/reflection/basics/BUILD new file mode 100644 index 000000000..3b0fc157c --- /dev/null +++ b/vendors/flecs/examples/c/reflection/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/basics/src/main.c b/vendors/flecs/examples/c/reflection/basics/src/main.c index 9c5899942..65d5db73b 100644 --- a/vendors/flecs/examples/c/reflection/basics/src/main.c +++ b/vendors/flecs/examples/c/reflection/basics/src/main.c @@ -21,7 +21,7 @@ int main(int argc, char *argv[]) { }); // Create entity with Position as usual - ecs_entity_t ent = ecs_new_id(ecs); + ecs_entity_t ent = ecs_new(ecs); ecs_set(ecs, ent, Position, {10, 20}); // Convert position component to flecs expression string diff --git a/vendors/flecs/examples/c/reflection/basics_bitmask/BUILD b/vendors/flecs/examples/c/reflection/basics_bitmask/BUILD new file mode 100644 index 000000000..8fe28539d --- /dev/null +++ b/vendors/flecs/examples/c/reflection/basics_bitmask/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics_bitmask", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/basics_bitmask/src/main.c b/vendors/flecs/examples/c/reflection/basics_bitmask/src/main.c index 1e4c27a86..d92bb98ca 100644 --- a/vendors/flecs/examples/c/reflection/basics_bitmask/src/main.c +++ b/vendors/flecs/examples/c/reflection/basics_bitmask/src/main.c @@ -34,11 +34,11 @@ int main(int argc, char *argv[]) { } }); - // Create entity with Position as usual - ecs_entity_t ent = ecs_new_id(ecs); + // Create entity with Sandwich as usual + ecs_entity_t ent = ecs_new(ecs); ecs_set(ecs, ent, Sandwich, {Bacon | Lettuce}); - // Convert position component to flecs expression string + // Convert Sandwich component to flecs expression string const Sandwich *ptr = ecs_get(ecs, ent, Sandwich); char *str = ecs_ptr_to_expr(ecs, ecs_id(Sandwich), ptr); printf("%s\n", str); // {toppings: Bacon|Lettuce} diff --git a/vendors/flecs/examples/c/reflection/basics_deserialize/BUILD b/vendors/flecs/examples/c/reflection/basics_deserialize/BUILD new file mode 100644 index 000000000..d602567d0 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/basics_deserialize/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics_deserialize", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/basics_deserialize/src/main.c b/vendors/flecs/examples/c/reflection/basics_deserialize/src/main.c index 3d7f0b961..c7741593c 100644 --- a/vendors/flecs/examples/c/reflection/basics_deserialize/src/main.c +++ b/vendors/flecs/examples/c/reflection/basics_deserialize/src/main.c @@ -21,8 +21,8 @@ int main(int argc, char *argv[]) { }); // Create entity, set value of position using reflection API - ecs_entity_t ent = ecs_new_entity(ecs, "ent"); - Position *ptr = ecs_get_mut(ecs, ent, Position); + ecs_entity_t ent = ecs_entity(ecs, { .name = "ent" }); + Position *ptr = ecs_ensure(ecs, ent, Position); ecs_meta_cursor_t cur = ecs_meta_cursor(ecs, ecs_id(Position), ptr); ecs_meta_push(&cur); // { diff --git a/vendors/flecs/examples/c/reflection/basics_enum/BUILD b/vendors/flecs/examples/c/reflection/basics_enum/BUILD new file mode 100644 index 000000000..a85a54e43 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/basics_enum/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics_enum", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/basics_enum/src/main.c b/vendors/flecs/examples/c/reflection/basics_enum/src/main.c index 107b5365f..368fb6fbe 100644 --- a/vendors/flecs/examples/c/reflection/basics_enum/src/main.c +++ b/vendors/flecs/examples/c/reflection/basics_enum/src/main.c @@ -35,11 +35,11 @@ int main(int argc, char *argv[]) { } }); - // Create entity with Position as usual - ecs_entity_t ent = ecs_new_id(ecs); + // Create entity with TypeWithEnum as usual + ecs_entity_t ent = ecs_new(ecs); ecs_set(ecs, ent, TypeWithEnum, {Green}); - // Convert position component to flecs expression string + // Convert TypeWithEnum component to flecs expression string const TypeWithEnum *ptr = ecs_get(ecs, ent, TypeWithEnum); char *str = ecs_ptr_to_expr(ecs, ecs_id(TypeWithEnum), ptr); printf("%s\n", str); // {color: Green} diff --git a/vendors/flecs/examples/c/reflection/basics_json/BUILD b/vendors/flecs/examples/c/reflection/basics_json/BUILD new file mode 100644 index 000000000..ada5546d3 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/basics_json/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics_json", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/basics_json/src/main.c b/vendors/flecs/examples/c/reflection/basics_json/src/main.c index e755e92ae..1caa77af4 100644 --- a/vendors/flecs/examples/c/reflection/basics_json/src/main.c +++ b/vendors/flecs/examples/c/reflection/basics_json/src/main.c @@ -21,7 +21,7 @@ int main(int argc, char *argv[]) { }); // Create entity with Position as usual - ecs_entity_t ent = ecs_new_entity(ecs, "ent"); + ecs_entity_t ent = ecs_entity(ecs, { .name = "ent" }); ecs_set(ecs, ent, Position, {10, 20}); // Convert position component to JSON @@ -31,10 +31,7 @@ int main(int argc, char *argv[]) { ecs_os_free(str); // Convert entity & all its components to json - str = ecs_entity_to_json(ecs, ent, &(ecs_entity_to_json_desc_t) { - .serialize_path = true, - .serialize_values = true - }); + str = ecs_entity_to_json(ecs, ent, NULL); printf("ent = %s\n", str); ecs_os_free(str); diff --git a/vendors/flecs/examples/c/reflection/entity_type/BUILD b/vendors/flecs/examples/c/reflection/entity_type/BUILD new file mode 100644 index 000000000..73a6ea571 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/entity_type/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "entity_type", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/entity_type/src/main.c b/vendors/flecs/examples/c/reflection/entity_type/src/main.c index 9c1e5c9c4..2d8ef0cfa 100644 --- a/vendors/flecs/examples/c/reflection/entity_type/src/main.c +++ b/vendors/flecs/examples/c/reflection/entity_type/src/main.c @@ -11,7 +11,7 @@ int main(int argc, char *argv[]) { ECS_META_COMPONENT(ecs, TypeWithEntity); ecs_entity_t foo = ecs_set_name(ecs, 0, "Foo"); - ecs_entity_t e = ecs_set(ecs, 0, TypeWithEntity, {foo}); + ecs_entity_t e = ecs_insert(ecs, ecs_value(TypeWithEntity, {foo})); // Convert component to flecs expression string const TypeWithEntity *ptr = ecs_get(ecs, e, TypeWithEntity); diff --git a/vendors/flecs/examples/c/reflection/member_ranges/BUILD b/vendors/flecs/examples/c/reflection/member_ranges/BUILD new file mode 100644 index 000000000..e16d0574c --- /dev/null +++ b/vendors/flecs/examples/c/reflection/member_ranges/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "member_ranges", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/member_ranges/src/main.c b/vendors/flecs/examples/c/reflection/member_ranges/src/main.c index 8b8f6ffa4..1342b61db 100644 --- a/vendors/flecs/examples/c/reflection/member_ranges/src/main.c +++ b/vendors/flecs/examples/c/reflection/member_ranges/src/main.c @@ -18,21 +18,23 @@ int main(int argc, char *argv[]) { .name = "value", .type = ecs_id(ecs_f64_t), .range = {0, 100}, // Specifics values that the member can assume - .warning_range = {0, 60}, // Values outside this range are considerd a warning - .error_range = {0, 80} // Values outside this range are considerd an error + .warning_range = {0, 60}, // Values outside this range are considered a warning + .error_range = {0, 80} // Values outside this range are considered an error }} }); - ecs_entity_t m1 = ecs_new_entity(ecs, "MachineA"); + ecs_entity_t m1 = ecs_entity(ecs, { .name = "MachineA" }); ecs_set(ecs, m1, CpuUtilization, {50}); - ecs_entity_t m2 = ecs_new_entity(ecs, "MachineB"); + ecs_entity_t m2 = ecs_entity(ecs, { .name = "MachineB" }); ecs_set(ecs, m2, CpuUtilization, {75}); - ecs_entity_t m3 = ecs_new_entity(ecs, "MachineC"); + ecs_entity_t m3 = ecs_entity(ecs, { .name = "MachineC" }); ecs_set(ecs, m3, CpuUtilization, {90}); - // Open https://www.flecs.dev/explorer?show=query&query=CpuUtilization to - // see how ranges affect visualization. - return ecs_app_run(ecs, &(ecs_app_desc_t){ .enable_rest = true }); + // Uncomment this line and open + // https://www.flecs.dev/explorer?show=query&query=CpuUtilization + // to see how ranges affect visualization: + // return ecs_app_run(ecs, &(ecs_app_desc_t){ .enable_rest = true }); + return ecs_fini(ecs); } diff --git a/vendors/flecs/examples/c/reflection/nested_set_member/BUILD b/vendors/flecs/examples/c/reflection/nested_set_member/BUILD new file mode 100644 index 000000000..816239f4d --- /dev/null +++ b/vendors/flecs/examples/c/reflection/nested_set_member/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "nested_set_member", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/nested_set_member/src/main.c b/vendors/flecs/examples/c/reflection/nested_set_member/src/main.c index 3aff3b9f3..d286d6646 100644 --- a/vendors/flecs/examples/c/reflection/nested_set_member/src/main.c +++ b/vendors/flecs/examples/c/reflection/nested_set_member/src/main.c @@ -17,8 +17,8 @@ int main(int argc, char *argv[]) { ECS_META_COMPONENT(ecs, Point); ECS_META_COMPONENT(ecs, Line); - ecs_entity_t ent = ecs_new_id(ecs); - Line *ptr = ecs_get_mut(ecs, ent, Line); + ecs_entity_t ent = ecs_new(ecs); + Line *ptr = ecs_ensure(ecs, ent, Line); ecs_meta_cursor_t cur = ecs_meta_cursor(ecs, ecs_id(Line), ptr); ecs_meta_push(&cur); // { diff --git a/vendors/flecs/examples/c/reflection/nested_struct/BUILD b/vendors/flecs/examples/c/reflection/nested_struct/BUILD new file mode 100644 index 000000000..d6a214d4a --- /dev/null +++ b/vendors/flecs/examples/c/reflection/nested_struct/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "nested_struct", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/nested_struct/src/main.c b/vendors/flecs/examples/c/reflection/nested_struct/src/main.c index e314d5b8f..9d23ba7c5 100644 --- a/vendors/flecs/examples/c/reflection/nested_struct/src/main.c +++ b/vendors/flecs/examples/c/reflection/nested_struct/src/main.c @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) { } }); - ecs_entity_t ent = ecs_new_id(ecs); + ecs_entity_t ent = ecs_new(ecs); ecs_set(ecs, ent, Line, {{10, 20}, {30, 40}}); const Line *ptr = ecs_get(ecs, ent, Line); diff --git a/vendors/flecs/examples/c/reflection/query_to_custom_json/BUILD b/vendors/flecs/examples/c/reflection/query_to_custom_json/BUILD new file mode 100644 index 000000000..14f0d0397 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/query_to_custom_json/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "query_to_custom_json", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/query_to_custom_json/src/main.c b/vendors/flecs/examples/c/reflection/query_to_custom_json/src/main.c index 34c3ac080..e18c41eeb 100644 --- a/vendors/flecs/examples/c/reflection/query_to_custom_json/src/main.c +++ b/vendors/flecs/examples/c/reflection/query_to_custom_json/src/main.c @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) { // Query for components ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_id(Position) }, { .id = ecs_id(Velocity) } } }); @@ -73,53 +73,85 @@ int main(int argc, char *argv[]) { // Serialize query to JSON. Customize serializer to only serialize entity // names and component values. ecs_iter_to_json_desc_t desc = { - .serialize_entities = true, .serialize_values = true }; - char *json = ecs_iter_to_json(ecs, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); printf("%s\n", json); ecs_os_free(json); // Iterator returns 2 sets of results, one for each table. // { - // "results": [{ - // "entities": ["a", "b"], - // "values": [ - // [{ - // "x": 10.00, - // "y": 20.00 - // }, { - // "x": 20.00, - // "y": 30.00 - // }], - // [{ - // "x": 1.00, - // "y": 2.00 - // }, { - // "x": 2.00, - // "y": 3.00 - // }] - // ] - // }, { - // "entities": ["c", "d"], - // "values": [ - // [{ - // "x": 30.00, - // "y": 40.00 - // }, { - // "x": 40.00, - // "y": 50.00 - // }], - // [{ - // "x": 3.00, - // "y": 4.00 - // }, { - // "x": 4.00, - // "y": 5.00 - // }] - // ] - // }] + // "results": [ + // { + // "name": "a", + // "fields": [ + // { + // "data": { + // "x": 10, + // "y": 20 + // } + // }, + // { + // "data": { + // "x": 1, + // "y": 2 + // } + // } + // ] + // }, + // { + // "name": "b", + // "fields": [ + // { + // "data": { + // "x": 20, + // "y": 30 + // } + // }, + // { + // "data": { + // "x": 2, + // "y": 3 + // } + // } + // ] + // }, + // { + // "name": "c", + // "fields": [ + // { + // "data": { + // "x": 30, + // "y": 40 + // } + // }, + // { + // "data": { + // "x": 3, + // "y": 4 + // } + // } + // ] + // }, + // { + // "name": "d", + // "fields": [ + // { + // "data": { + // "x": 30, + // "y": 40 + // } + // }, + // { + // "data": { + // "x": 4, + // "y": 5 + // } + // } + // ] + // } + // ] // } ecs_fini(ecs); diff --git a/vendors/flecs/examples/c/reflection/query_to_json/BUILD b/vendors/flecs/examples/c/reflection/query_to_json/BUILD new file mode 100644 index 000000000..7abb25683 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/query_to_json/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "query_to_json", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/query_to_json/src/main.c b/vendors/flecs/examples/c/reflection/query_to_json/src/main.c index ba6bf8dc5..d7c70007b 100644 --- a/vendors/flecs/examples/c/reflection/query_to_json/src/main.c +++ b/vendors/flecs/examples/c/reflection/query_to_json/src/main.c @@ -61,15 +61,14 @@ int main(int argc, char *argv[]) { // Query for components ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_id(Position) }, { .id = ecs_id(Velocity) } } }); - // Serialize query to JSON. Note that this works for iterators from any - // source, including filters & rules. + // Serialize query to JSON. This works for any iterator. ecs_iter_t it = ecs_query_iter(ecs, q); - char *json = ecs_iter_to_json(ecs, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); printf("%s\n", json); ecs_os_free(json); diff --git a/vendors/flecs/examples/c/reflection/runtime_component/BUILD b/vendors/flecs/examples/c/reflection/runtime_component/BUILD new file mode 100644 index 000000000..c90f811c8 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/runtime_component/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "runtime_component", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/runtime_component/src/main.c b/vendors/flecs/examples/c/reflection/runtime_component/src/main.c index 35906584c..df7620e6f 100644 --- a/vendors/flecs/examples/c/reflection/runtime_component/src/main.c +++ b/vendors/flecs/examples/c/reflection/runtime_component/src/main.c @@ -13,8 +13,8 @@ int main(int argc, char *argv[]) { }); // Create entity, set value of position using reflection API - ecs_entity_t ent = ecs_new_entity(ecs, "ent"); - void *ptr = ecs_get_mut_id(ecs, ent, Position); + ecs_entity_t ent = ecs_entity(ecs, { .name = "ent" }); + void *ptr = ecs_ensure_id(ecs, ent, Position); ecs_meta_cursor_t cur = ecs_meta_cursor(ecs, Position, ptr); ecs_meta_push(&cur); // { diff --git a/vendors/flecs/examples/c/reflection/runtime_nested_component/BUILD b/vendors/flecs/examples/c/reflection/runtime_nested_component/BUILD new file mode 100644 index 000000000..6495a62a5 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/runtime_nested_component/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "runtime_nested_component", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/runtime_nested_component/src/main.c b/vendors/flecs/examples/c/reflection/runtime_nested_component/src/main.c index 0b07b296c..26b878b07 100644 --- a/vendors/flecs/examples/c/reflection/runtime_nested_component/src/main.c +++ b/vendors/flecs/examples/c/reflection/runtime_nested_component/src/main.c @@ -20,8 +20,8 @@ int main(int argc, char *argv[]) { }); // Create entity, set value of position using reflection API - ecs_entity_t ent = ecs_new_entity(ecs, "ent"); - void *ptr = ecs_get_mut_id(ecs, ent, Line); + ecs_entity_t ent = ecs_entity(ecs, { .name = "ent" }); + void *ptr = ecs_ensure_id(ecs, ent, Line); ecs_meta_cursor_t cur = ecs_meta_cursor(ecs, Line, ptr); ecs_meta_push(&cur); // { diff --git a/vendors/flecs/examples/c/reflection/ser_opaque_string/BUILD b/vendors/flecs/examples/c/reflection/ser_opaque_string/BUILD new file mode 100644 index 000000000..f37d2e21b --- /dev/null +++ b/vendors/flecs/examples/c/reflection/ser_opaque_string/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "ser_opaque_string", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/ser_opaque_type/BUILD b/vendors/flecs/examples/c/reflection/ser_opaque_type/BUILD new file mode 100644 index 000000000..15bdfc974 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/ser_opaque_type/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "ser_opaque_type", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/ser_opaque_vector/BUILD b/vendors/flecs/examples/c/reflection/ser_opaque_vector/BUILD new file mode 100644 index 000000000..0d72fca25 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/ser_opaque_vector/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "ser_opaque_vector", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/units/BUILD b/vendors/flecs/examples/c/reflection/units/BUILD new file mode 100644 index 000000000..ee1b59bd8 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/units/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "units", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/units/src/main.c b/vendors/flecs/examples/c/reflection/units/src/main.c index 94fc4a595..6f55bd761 100644 --- a/vendors/flecs/examples/c/reflection/units/src/main.c +++ b/vendors/flecs/examples/c/reflection/units/src/main.c @@ -48,10 +48,10 @@ int main(int argc, char *argv[]) { } }); - ecs_entity_t e = ecs_set(ecs, 0, WeatherStation, {24, 1.2, 0.5}); + ecs_entity_t e = ecs_insert(ecs, ecs_value(WeatherStation, {24, 1.2, 0.5})); // Use cursor API to print values with units - WeatherStation *ptr = ecs_get_mut(ecs, e, WeatherStation); + WeatherStation *ptr = ecs_ensure(ecs, e, WeatherStation); ecs_meta_cursor_t cur = ecs_meta_cursor(ecs, ecs_id(WeatherStation), ptr); ecs_meta_push(&cur); diff --git a/vendors/flecs/examples/c/reflection/world_ser_deser/BUILD b/vendors/flecs/examples/c/reflection/world_ser_deser/BUILD new file mode 100644 index 000000000..d5b61f9a7 --- /dev/null +++ b/vendors/flecs/examples/c/reflection/world_ser_deser/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "world_ser_deser", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/reflection/world_ser_deser/src/main.c b/vendors/flecs/examples/c/reflection/world_ser_deser/src/main.c index bbb79c9cf..4cb0064fd 100644 --- a/vendors/flecs/examples/c/reflection/world_ser_deser/src/main.c +++ b/vendors/flecs/examples/c/reflection/world_ser_deser/src/main.c @@ -9,8 +9,8 @@ ECS_COMPONENT_DECLARE(Position); ECS_COMPONENT_DECLARE(Velocity); void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - const Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + const Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -49,11 +49,11 @@ void MoveModuleImport(ecs_world_t *world) { int main(int argc, char *argv[]) { ecs_world_t *world_a = ecs_init_w_args(argc, argv); { - ECS_IMPORT(world_a, MoveModule); // put in a scope so variable doens't overlap + ECS_IMPORT(world_a, MoveModule); // put in a scope so variable doesn't overlap } - ecs_entity_t ent_1 = ecs_new_entity(world_a, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world_a, "ent_2"); + ecs_entity_t ent_1 = ecs_entity(world_a, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world_a, { .name = "ent_2" }); ecs_set(world_a, ent_1, Position, {10, 20}); ecs_set(world_a, ent_2, Position, {30, 40}); ecs_set(world_a, ent_1, Velocity, {1, -1}); diff --git a/vendors/flecs/examples/c/relationships/basics/BUILD b/vendors/flecs/examples/c/relationships/basics/BUILD new file mode 100644 index 000000000..3b0fc157c --- /dev/null +++ b/vendors/flecs/examples/c/relationships/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/relationships/basics/src/main.c b/vendors/flecs/examples/c/relationships/basics/src/main.c index c352e905c..77f7e473a 100644 --- a/vendors/flecs/examples/c/relationships/basics/src/main.c +++ b/vendors/flecs/examples/c/relationships/basics/src/main.c @@ -7,15 +7,15 @@ int main(int argc, char *argv[]) { ECS_TAG(ecs, Eats); // Entity used for Grows relationship - ecs_entity_t grows = ecs_new_entity(ecs, "Grows"); + ecs_entity_t grows = ecs_entity(ecs, { .name = "Grows" }); // Relationship objects - ecs_entity_t apples = ecs_new_entity(ecs, "Apples"); - ecs_entity_t pears = ecs_new_entity(ecs, "Pears"); + ecs_entity_t apples = ecs_entity(ecs, { .name = "Apples" }); + ecs_entity_t pears = ecs_entity(ecs, { .name = "Pears" }); // Create an entity with 3 relationships. Relationships are like regular components, // but combine two types/identifiers into an (relationship, object) pair. - ecs_entity_t bob = ecs_new_entity(ecs, "Bob"); + ecs_entity_t bob = ecs_entity(ecs, { .name = "Bob" }); // Pairs can be constructed from a type and entity ecs_add_pair(ecs, bob, Eats, apples); ecs_add_pair(ecs, bob, Eats, pears); diff --git a/vendors/flecs/examples/c/relationships/exclusive_relations/BUILD b/vendors/flecs/examples/c/relationships/exclusive_relations/BUILD new file mode 100644 index 000000000..ff72cfa78 --- /dev/null +++ b/vendors/flecs/examples/c/relationships/exclusive_relations/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "exclusive_relations", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/relationships/exclusive_relations/src/main.c b/vendors/flecs/examples/c/relationships/exclusive_relations/src/main.c index 4b268c71d..e63d93f43 100644 --- a/vendors/flecs/examples/c/relationships/exclusive_relations/src/main.c +++ b/vendors/flecs/examples/c/relationships/exclusive_relations/src/main.c @@ -11,11 +11,11 @@ int main(int argc, char *argv[]) { ecs_add_id(ecs, Platoon, EcsExclusive); // Create two platoons - ecs_entity_t platoon_1 = ecs_new_id(ecs); - ecs_entity_t platoon_2 = ecs_new_id(ecs); + ecs_entity_t platoon_1 = ecs_new(ecs); + ecs_entity_t platoon_2 = ecs_new(ecs); // Create a unit - ecs_entity_t unit = ecs_new_id(ecs); + ecs_entity_t unit = ecs_new(ecs); // Add unit to platoon 1 ecs_add_pair(ecs, unit, Platoon, platoon_1); diff --git a/vendors/flecs/examples/c/relationships/relation_component/BUILD b/vendors/flecs/examples/c/relationships/relation_component/BUILD new file mode 100644 index 000000000..414e921ea --- /dev/null +++ b/vendors/flecs/examples/c/relationships/relation_component/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "relation_component", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/relationships/relation_component/src/main.c b/vendors/flecs/examples/c/relationships/relation_component/src/main.c index 0fedfd11a..d51831abd 100644 --- a/vendors/flecs/examples/c/relationships/relation_component/src/main.c +++ b/vendors/flecs/examples/c/relationships/relation_component/src/main.c @@ -31,13 +31,13 @@ int main(int argc, char *argv[]) { // When one element of a pair is a component and the other element is a tag, // the pair assumes the type of the component. - ecs_entity_t e1 = ecs_new_id(ecs); + ecs_entity_t e1 = ecs_new(ecs); ecs_set_pair(ecs, e1, Requires, Gigawatts, {1.21}); const Requires *r = ecs_get_pair(ecs, e1, Requires, Gigawatts); printf("requires: %.2f\n", r->amount); // The component can be either the first or second part of a pair: - ecs_entity_t e2 = ecs_new_id(ecs); + ecs_entity_t e2 = ecs_new(ecs); ecs_set_pair_second(ecs, e2, Gigawatts, Requires, {1.21}); r = ecs_get_pair_second(ecs, e2, Gigawatts, Requires); printf("requires: %.2f\n", r->amount); @@ -47,14 +47,14 @@ int main(int argc, char *argv[]) { // If both parts of a pair are components, the pair assumes the type of // the first element: - ecs_entity_t e3 = ecs_new_id(ecs); + ecs_entity_t e3 = ecs_new(ecs); ecs_set_pair(ecs, e3, Expires, ecs_id(Position), {0.5}); const Expires *e = ecs_get_pair(ecs, e3, Expires, ecs_id(Position)); printf("expires: %.1f\n", e->timeout); // You can prevent a pair from assuming the type of a component by adding // the Tag property to a relationship: - ecs_add_id(ecs, MustHave, EcsTag); + ecs_add_id(ecs, MustHave, EcsPairIsTag); // Even though Position is a component, contains no // data because MustHave has the Tag property. @@ -65,28 +65,28 @@ int main(int argc, char *argv[]) { char *type_str; type_id = ecs_get_typeid(ecs, ecs_pair(ecs_id(Requires), Gigawatts)); - type_str = ecs_get_fullpath(ecs, type_id); + type_str = ecs_get_path(ecs, type_id); printf("%s\n", type_str); ecs_os_free(type_str); type_id = ecs_get_typeid(ecs, ecs_pair(Gigawatts, ecs_id(Requires))); - type_str = ecs_get_fullpath(ecs, type_id); + type_str = ecs_get_path(ecs, type_id); printf("%s\n", type_str); ecs_os_free(type_str); type_id = ecs_get_typeid(ecs, ecs_pair(ecs_id(Expires), ecs_id(Position))); - type_str = ecs_get_fullpath(ecs, type_id); + type_str = ecs_get_path(ecs, type_id); printf("%s\n", type_str); ecs_os_free(type_str); type_id = ecs_get_typeid(ecs, ecs_pair(MustHave, ecs_id(Position))); - type_str = ecs_get_fullpath(ecs, type_id); + type_str = ecs_get_path(ecs, type_id); printf("%s\n", type_str); ecs_os_free(type_str); // When querying for a relationship, provide both parts of the pair: ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = ecs_pair(ecs_id(Requires), Gigawatts) } } }); @@ -94,7 +94,7 @@ int main(int argc, char *argv[]) { // When iterating, always use the pair type: ecs_iter_t it = ecs_query_iter(ecs, q); while (ecs_query_next(&it)) { - r = ecs_field(&it, Requires, 1); + r = ecs_field(&it, Requires, 0); for (int i = 0; i < it.count; i ++) { printf("requires %.2f gigawatts\n", r[i].amount); } diff --git a/vendors/flecs/examples/c/relationships/symmetric_relations/BUILD b/vendors/flecs/examples/c/relationships/symmetric_relations/BUILD new file mode 100644 index 000000000..49f6e92ca --- /dev/null +++ b/vendors/flecs/examples/c/relationships/symmetric_relations/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "symmetric_relations", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/relationships/symmetric_relations/src/main.c b/vendors/flecs/examples/c/relationships/symmetric_relations/src/main.c index 1fead29ad..8aa06869d 100644 --- a/vendors/flecs/examples/c/relationships/symmetric_relations/src/main.c +++ b/vendors/flecs/examples/c/relationships/symmetric_relations/src/main.c @@ -11,8 +11,8 @@ int main(int argc, char *argv[]) { ecs_add_id(ecs, TradesWith, EcsSymmetric); // Create two players - ecs_entity_t player_1 = ecs_new_id(ecs); - ecs_entity_t player_2 = ecs_new_id(ecs); + ecs_entity_t player_1 = ecs_new(ecs); + ecs_entity_t player_2 = ecs_new(ecs); // Add (TradesWith, player_2) to player_1. This also adds // (TradesWith, player_1) to player_2. diff --git a/vendors/flecs/examples/c/relationships/union/BUILD b/vendors/flecs/examples/c/relationships/union/BUILD new file mode 100644 index 000000000..27171e62f --- /dev/null +++ b/vendors/flecs/examples/c/relationships/union/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "union", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/relationships/union/src/main.c b/vendors/flecs/examples/c/relationships/union/src/main.c index 21ba924a8..dd69e3e84 100644 --- a/vendors/flecs/examples/c/relationships/union/src/main.c +++ b/vendors/flecs/examples/c/relationships/union/src/main.c @@ -32,22 +32,22 @@ int main(int argc, char *argv[]) { // Create a query that subscribers for all entities that have a Direction // and that are walking. ecs_query_t *q = ecs_query(world, { - .filter.terms = { + .terms = { { .id = ecs_pair(Movement, Walking) }, { .id = ecs_pair(Direction, EcsWildcard) } } }); // Create a few entities with various state combinations - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add_pair(world, e1, Movement, Walking); ecs_add_pair(world, e1, Direction, Front); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_pair(world, e2, Movement, Running); ecs_add_pair(world, e2, Direction, Left); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_pair(world, e3, Movement, Running); ecs_add_pair(world, e3, Direction, Right); @@ -57,14 +57,16 @@ int main(int argc, char *argv[]) { // Iterate query as usual ecs_iter_t it = ecs_query_iter(world, q); while (ecs_iter_next(&it)) { - ecs_entity_t *movement = ecs_field(&it, ecs_entity_t, 1); - ecs_entity_t *direction = ecs_field(&it, ecs_entity_t, 2); + ecs_id_t movement_pair = ecs_field_id(&it, 0); + ecs_id_t direction_pair = ecs_field_id(&it, 1); + ecs_entity_t movement = ecs_pair_second(world, movement_pair); + ecs_entity_t direction = ecs_pair_second(world, direction_pair); for (int i = 0; i < it.count; i ++) { printf("%s: Movement: %s, Direction: %s\n", ecs_get_name(world, it.entities[i]), - ecs_get_name(world, movement[i]), - ecs_get_name(world, direction[i])); + ecs_get_name(world, movement), + ecs_get_name(world, direction)); } } diff --git a/vendors/flecs/examples/c/systems/basics/BUILD b/vendors/flecs/examples/c/systems/basics/BUILD new file mode 100644 index 000000000..3b0fc157c --- /dev/null +++ b/vendors/flecs/examples/c/systems/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/basics/src/main.c b/vendors/flecs/examples/c/systems/basics/src/main.c index c9d644a2b..73fc4d23a 100644 --- a/vendors/flecs/examples/c/systems/basics/src/main.c +++ b/vendors/flecs/examples/c/systems/basics/src/main.c @@ -6,8 +6,8 @@ typedef struct { } Position, Velocity; void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - const Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + const Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -32,7 +32,7 @@ int main(int argc, char *argv[]) { .entity = ecs_entity(ecs, { .name = "Move" }), - .query.filter.terms = { + .query.terms = { { .id = ecs_id(Position) }, { .id = ecs_id(Velocity), .inout = EcsIn } }, @@ -43,16 +43,16 @@ int main(int argc, char *argv[]) { // ECS_SYSTEM(ecs, Move, 0, Position, [in] Velocity); // Create a few test entities for a Position, Velocity query - ecs_entity_t e1 = ecs_new_entity(ecs, "e1"); + ecs_entity_t e1 = ecs_entity(ecs, { .name = "e1" }); ecs_set(ecs, e1, Position, {10, 20}); ecs_set(ecs, e1, Velocity, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(ecs, "e2"); + ecs_entity_t e2 = ecs_entity(ecs, { .name = "e2" }); ecs_set(ecs, e2, Position, {10, 20}); ecs_set(ecs, e2, Velocity, {3, 4}); // This entity will not match as it does not have Position, Velocity - ecs_entity_t e3 = ecs_new_entity(ecs, "e3"); + ecs_entity_t e3 = ecs_entity(ecs, { .name = "e3" }); ecs_set(ecs, e3, Position, {10, 20}); // Run the system diff --git a/vendors/flecs/examples/c/systems/custom_phases/BUILD b/vendors/flecs/examples/c/systems/custom_phases/BUILD new file mode 100644 index 000000000..84b410fad --- /dev/null +++ b/vendors/flecs/examples/c/systems/custom_phases/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_phases", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/custom_phases/src/main.c b/vendors/flecs/examples/c/systems/custom_phases/src/main.c index 938f85167..7637a1cb8 100644 --- a/vendors/flecs/examples/c/systems/custom_phases/src/main.c +++ b/vendors/flecs/examples/c/systems/custom_phases/src/main.c @@ -25,7 +25,7 @@ int main(int argc, char *argv[]) { ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "CollisionSystem", - .add = { ecs_dependson(Collisions) } + .add = ecs_ids( ecs_dependson(Collisions) ) }), .callback = Sys }); @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "PhysicsSystem", - .add = { ecs_dependson(Physics) } + .add = ecs_ids( ecs_dependson(Physics) ) }), .callback = Sys }); @@ -41,7 +41,7 @@ int main(int argc, char *argv[]) { ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "GameSystem", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = Sys }); diff --git a/vendors/flecs/examples/c/systems/custom_phases_no_builtin/BUILD b/vendors/flecs/examples/c/systems/custom_phases_no_builtin/BUILD new file mode 100644 index 000000000..db203d591 --- /dev/null +++ b/vendors/flecs/examples/c/systems/custom_phases_no_builtin/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_phases_no_builtin", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/custom_phases_no_builtin/src/main.c b/vendors/flecs/examples/c/systems/custom_phases_no_builtin/src/main.c index bf26b21a1..c9d04ee08 100644 --- a/vendors/flecs/examples/c/systems/custom_phases_no_builtin/src/main.c +++ b/vendors/flecs/examples/c/systems/custom_phases_no_builtin/src/main.c @@ -26,7 +26,7 @@ int main(int argc, char *argv[]) { ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "CollisionSystem", - .add = { ecs_dependson(Collisions) } + .add = ecs_ids( ecs_dependson(Collisions) ) }), .callback = Sys }); @@ -34,7 +34,7 @@ int main(int argc, char *argv[]) { ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "PhysicsSystem", - .add = { ecs_dependson(Physics) } + .add = ecs_ids( ecs_dependson(Physics) ) }), .callback = Sys }); @@ -42,7 +42,7 @@ int main(int argc, char *argv[]) { ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "GameSystem", - .add = { ecs_dependson(Update) } + .add = ecs_ids( ecs_dependson(Update) ) }), .callback = Sys }); diff --git a/vendors/flecs/examples/c/systems/custom_pipeline/BUILD b/vendors/flecs/examples/c/systems/custom_pipeline/BUILD new file mode 100644 index 000000000..9f3a9e28c --- /dev/null +++ b/vendors/flecs/examples/c/systems/custom_pipeline/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_pipeline", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/custom_pipeline/src/main.c b/vendors/flecs/examples/c/systems/custom_pipeline/src/main.c index b678ef94d..968394f27 100644 --- a/vendors/flecs/examples/c/systems/custom_pipeline/src/main.c +++ b/vendors/flecs/examples/c/systems/custom_pipeline/src/main.c @@ -25,7 +25,7 @@ int main(int argc, char *argv[]) { // pipeline does not require the use of phases (see custom_phases) or of the // DependsOn relationship. ecs_entity_t pipeline = ecs_pipeline(ecs, { - .query.filter.terms = { + .query.terms = { { .id = EcsSystem }, // Mandatory, must always match systems { .id = Physics } } diff --git a/vendors/flecs/examples/c/systems/custom_runner/BUILD b/vendors/flecs/examples/c/systems/custom_runner/BUILD new file mode 100644 index 000000000..9002c7741 --- /dev/null +++ b/vendors/flecs/examples/c/systems/custom_runner/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_runner", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/custom_runner/src/main.c b/vendors/flecs/examples/c/systems/custom_runner/src/main.c index 156d41fcb..3afb02cd0 100644 --- a/vendors/flecs/examples/c/systems/custom_runner/src/main.c +++ b/vendors/flecs/examples/c/systems/custom_runner/src/main.c @@ -19,8 +19,8 @@ void Move(ecs_iter_t *it) { // Note that this code looks the same as iterating a query. while (ecs_query_next(it)) { // Inside the loop the code looks the same as for a regular system - Position *p = ecs_field(it, Position, 1); - const Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + const Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -41,7 +41,7 @@ int main(int argc, char *argv[]) { // Create a system for Position, Velocity ecs_entity_t move = ecs_system(ecs, { - .query.filter.terms = { + .query.terms = { { .id = ecs_id(Position) }, { .id = ecs_id(Velocity), .inout = EcsIn } }, @@ -52,16 +52,16 @@ int main(int argc, char *argv[]) { // ECS_SYSTEM(ecs, Move, 0, Position, [in] Velocity); // Create a few test entities for a Position, Velocity query - ecs_entity_t e1 = ecs_new_entity(ecs, "e1"); + ecs_entity_t e1 = ecs_entity(ecs, { .name = "e1" }); ecs_set(ecs, e1, Position, {10, 20}); ecs_set(ecs, e1, Velocity, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(ecs, "e2"); + ecs_entity_t e2 = ecs_entity(ecs, { .name = "e2" }); ecs_set(ecs, e2, Position, {10, 20}); ecs_set(ecs, e2, Velocity, {3, 4}); // This entity will not match as it does not have Position, Velocity - ecs_entity_t e3 = ecs_new_entity(ecs, "e3"); + ecs_entity_t e3 = ecs_entity(ecs, { .name = "e3" }); ecs_set(ecs, e3, Position, {10, 20}); // Run the system diff --git a/vendors/flecs/examples/c/systems/delta_time/BUILD b/vendors/flecs/examples/c/systems/delta_time/BUILD new file mode 100644 index 000000000..74a352b5e --- /dev/null +++ b/vendors/flecs/examples/c/systems/delta_time/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "delta_time", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/immediate/BUILD b/vendors/flecs/examples/c/systems/immediate/BUILD new file mode 100644 index 000000000..b8fc026c5 --- /dev/null +++ b/vendors/flecs/examples/c/systems/immediate/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "immediate", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/test/custom_builds/c/meta_c/include/meta_c.h b/vendors/flecs/examples/c/systems/immediate/include/immediate.h similarity index 66% rename from vendors/flecs/test/custom_builds/c/meta_c/include/meta_c.h rename to vendors/flecs/examples/c/systems/immediate/include/immediate.h index 683c0b3d4..1aecd4de2 100644 --- a/vendors/flecs/test/custom_builds/c/meta_c/include/meta_c.h +++ b/vendors/flecs/examples/c/systems/immediate/include/immediate.h @@ -1,8 +1,8 @@ -#ifndef META_C_H -#define META_C_H +#ifndef IMMEDIATE_H +#define IMMEDIATE_H /* This generated file contains includes for project dependencies */ -#include "meta_c/bake_config.h" +#include "immediate/bake_config.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/examples/c/rules/basics/include/basics/bake_config.h b/vendors/flecs/examples/c/systems/immediate/include/immediate/bake_config.h similarity index 91% rename from vendors/flecs/examples/c/rules/basics/include/basics/bake_config.h rename to vendors/flecs/examples/c/systems/immediate/include/immediate/bake_config.h index 0fcd5d6e7..25f8e5513 100644 --- a/vendors/flecs/examples/c/rules/basics/include/basics/bake_config.h +++ b/vendors/flecs/examples/c/systems/immediate/include/immediate/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef BASICS_BAKE_CONFIG_H -#define BASICS_BAKE_CONFIG_H +#ifndef IMMEDIATE_BAKE_CONFIG_H +#define IMMEDIATE_BAKE_CONFIG_H /* Headers of public dependencies */ #include diff --git a/vendors/flecs/examples/cpp/queries/instancing/include/instancing/bake_config.h b/vendors/flecs/examples/c/systems/immediate/include/no_readonly/bake_config.h similarity index 91% rename from vendors/flecs/examples/cpp/queries/instancing/include/instancing/bake_config.h rename to vendors/flecs/examples/c/systems/immediate/include/no_readonly/bake_config.h index f1866b26b..25f8e5513 100644 --- a/vendors/flecs/examples/cpp/queries/instancing/include/instancing/bake_config.h +++ b/vendors/flecs/examples/c/systems/immediate/include/no_readonly/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef INSTANCING_BAKE_CONFIG_H -#define INSTANCING_BAKE_CONFIG_H +#ifndef IMMEDIATE_BAKE_CONFIG_H +#define IMMEDIATE_BAKE_CONFIG_H /* Headers of public dependencies */ #include diff --git a/vendors/flecs/examples/c/systems/no_readonly/project.json b/vendors/flecs/examples/c/systems/immediate/project.json similarity index 83% rename from vendors/flecs/examples/c/systems/no_readonly/project.json rename to vendors/flecs/examples/c/systems/immediate/project.json index 8a3386207..2253884c3 100644 --- a/vendors/flecs/examples/c/systems/no_readonly/project.json +++ b/vendors/flecs/examples/c/systems/immediate/project.json @@ -1,5 +1,5 @@ { - "id": "no_readonly", + "id": "immediate", "type": "application", "value": { "use": [ diff --git a/vendors/flecs/examples/c/systems/no_readonly/src/main.c b/vendors/flecs/examples/c/systems/immediate/src/main.c similarity index 80% rename from vendors/flecs/examples/c/systems/no_readonly/src/main.c rename to vendors/flecs/examples/c/systems/immediate/src/main.c index 7942f81ed..abed3962d 100644 --- a/vendors/flecs/examples/c/systems/no_readonly/src/main.c +++ b/vendors/flecs/examples/c/systems/immediate/src/main.c @@ -1,4 +1,4 @@ -#include +#include #include // When an application calls ecs_progress(), the world is put in readonly mode. @@ -8,16 +8,16 @@ // or removing components) are not visible until the end of the frame (see the // sync_point example for more details). // Sometimes this is not what you want, and you need a change to be visible -// immediately. For these use cases, applications can use a no_readonly system. +// immediately. For these use cases, applications can use an immediate system. // This temporarily takes the world out of readonly mode, so a system can make // changes that are directly visible. -// Because they mutate the world directly, no_readonly systems are never ran on +// Because they mutate the world directly, immediate systems are never ran on // more than one thread, and no other systems are ran at the same time. ECS_DECLARE(Waiter); ECS_DECLARE(Plate); -// System that assigns plates to waiter. By making this system no_readonly +// System that assigns plates to waiter. By making this system immediate // plate assignments are assigned directly (not deferred) to waiters, which // ensures that we won't assign plates to the same waiter more than once. void AssignPlate(ecs_iter_t *it) { @@ -36,15 +36,15 @@ void AssignPlate(ecs_iter_t *it) { // next plate will no longer find it. // The defer_suspend function temporarily suspends deferring // operations, which ensures that our plate is assigned immediately. - // Even though this is a no_readonly system, defering is still + // Even though this is an immediate system, deferring is still // enabled by default, as adding/removing components to the entities - // being iterated would intefere with the system iterator. + // being iterated would interfere with the system iterator. ecs_defer_suspend(ecs); ecs_add_pair(ecs, waiter, Plate, plate); ecs_defer_resume(ecs); - // Now that defering is resumed, we can safely also add the waiter - // to the plate. We can't do this while defering is suspended, + // Now that deferring is resumed, we can safely also add the waiter + // to the plate. We can't do this while deferring is suspended, // because the plate is the entity we're currently iterating, and // we don't want to move it to a different table while we're // iterating it. @@ -71,7 +71,7 @@ int main(int argc, char *argv[]) { // Create query to find all waiters without a plate ecs_query_t *q_waiter = ecs_query(ecs, { - .filter.terms = { + .terms = { { .id = Waiter }, { .id = ecs_pair(Plate, EcsWildcard), .oper = EcsNot } } @@ -81,42 +81,42 @@ int main(int argc, char *argv[]) { ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "AssignPlate", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { .id = Plate }, { .id = ecs_pair(Waiter, EcsWildcard), .oper = EcsNot } }, .callback = AssignPlate, .ctx = q_waiter, - .no_readonly = true // disable readonly mode for this system + .immediate = true // disable readonly mode for this system }); // Create a few plates and waiters ecs_entity_t waiter_1 = ecs_entity(ecs, { .name = "waiter_1", - .add = { Waiter } + .add = ecs_ids( Waiter ) }); /* ecs_entity_t waiter_2 = */ ecs_entity(ecs, { // silence unused warning .name = "waiter_2", - .add = { Waiter } + .add = ecs_ids( Waiter ) }); /* ecs_entity_t waiter_3 = */ ecs_entity(ecs, { .name = "waiter_3", - .add = { Waiter } + .add = ecs_ids( Waiter ) }); /* ecs_entity_t plate_1 = */ ecs_entity(ecs, { .name = "plate_1", - .add = { Plate } + .add = ecs_ids( Plate ) }); ecs_entity_t plate_2 = ecs_entity(ecs, { .name = "plate_2", - .add = { Plate, ecs_pair(Waiter, waiter_1) } // already assigned + .add = ecs_ids( Plate, ecs_pair(Waiter, waiter_1) ) // already assigned }); /* ecs_entity_t plate_3 = */ ecs_entity(ecs, { .name = "plate_3", - .add = { Plate } + .add = ecs_ids( Plate ) }); // Also assign plate_2 to waiter_1 diff --git a/vendors/flecs/examples/c/systems/mutate_entity/BUILD b/vendors/flecs/examples/c/systems/mutate_entity/BUILD new file mode 100644 index 000000000..e04e2d419 --- /dev/null +++ b/vendors/flecs/examples/c/systems/mutate_entity/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "mutate_entity", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/mutate_entity/src/main.c b/vendors/flecs/examples/c/systems/mutate_entity/src/main.c index e3e8e154f..119128de6 100644 --- a/vendors/flecs/examples/c/systems/mutate_entity/src/main.c +++ b/vendors/flecs/examples/c/systems/mutate_entity/src/main.c @@ -7,7 +7,7 @@ typedef struct { // System that deletes an entity after a timeout expires void Expire(ecs_iter_t *it) { - Timeout *t = ecs_field(it, Timeout, 1); + Timeout *t = ecs_field(it, Timeout, 0); for (int i = 0; i < it->count; i ++) { t[i].value -= (double)it->delta_time; @@ -31,7 +31,7 @@ void Expire(ecs_iter_t *it) { // System that prints remaining expiry time void PrintExpire(ecs_iter_t *it) { - Timeout *t = ecs_field(it, Timeout, 1); + Timeout *t = ecs_field(it, Timeout, 0); for (int i = 0; i < it->count; i ++) { printf("PrintExpire: %s has %.2f seconds left\n", ecs_get_name( diff --git a/vendors/flecs/examples/c/systems/pipeline/BUILD b/vendors/flecs/examples/c/systems/pipeline/BUILD new file mode 100644 index 000000000..bd898d338 --- /dev/null +++ b/vendors/flecs/examples/c/systems/pipeline/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "pipeline", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/pipeline/src/main.c b/vendors/flecs/examples/c/systems/pipeline/src/main.c index 70092c789..99467321f 100644 --- a/vendors/flecs/examples/c/systems/pipeline/src/main.c +++ b/vendors/flecs/examples/c/systems/pipeline/src/main.c @@ -6,8 +6,8 @@ typedef struct { } Position, Velocity; void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - const Velocity *v = ecs_field(it, const Velocity, 2); + Position *p = ecs_field(it, Position, 0); + const Velocity *v = ecs_field(it, const Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -16,7 +16,7 @@ void Move(ecs_iter_t *it) { } void PrintPosition(ecs_iter_t *it) { - const Position *p = ecs_field(it, const Position, 1); + const Position *p = ecs_field(it, const Position, 0); for (int i = 0; i < it->count; i ++) { printf("%s: {%f, %f}\n", ecs_get_name(it->world, it->entities[i]), @@ -37,11 +37,11 @@ int main(int argc, char *argv[]) { ECS_SYSTEM(ecs, PrintPosition, EcsPostUpdate, [in] Position); // Create a few test entities for a Position, Velocity query - ecs_entity_t e1 = ecs_new_entity(ecs, "e1"); + ecs_entity_t e1 = ecs_entity(ecs, { .name = "e1" }); ecs_set(ecs, e1, Position, {10, 20}); ecs_set(ecs, e1, Velocity, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(ecs, "e2"); + ecs_entity_t e2 = ecs_entity(ecs, { .name = "e2" }); ecs_set(ecs, e2, Position, {10, 20}); ecs_set(ecs, e2, Velocity, {3, 4}); diff --git a/vendors/flecs/examples/c/systems/startup_system/BUILD b/vendors/flecs/examples/c/systems/startup_system/BUILD new file mode 100644 index 000000000..b15408d39 --- /dev/null +++ b/vendors/flecs/examples/c/systems/startup_system/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "startup_system", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/sync_point/BUILD b/vendors/flecs/examples/c/systems/sync_point/BUILD new file mode 100644 index 000000000..70371cc06 --- /dev/null +++ b/vendors/flecs/examples/c/systems/sync_point/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "sync_point", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/sync_point/src/main.c b/vendors/flecs/examples/c/systems/sync_point/src/main.c index a092848dc..dbf636f17 100644 --- a/vendors/flecs/examples/c/systems/sync_point/src/main.c +++ b/vendors/flecs/examples/c/systems/sync_point/src/main.c @@ -16,8 +16,8 @@ void SetVelocity(ecs_iter_t *it) { } void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - const Velocity *v = ecs_field(it, const Velocity, 2); + Position *p = ecs_field(it, Position, 0); + const Velocity *v = ecs_field(it, const Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -26,7 +26,7 @@ void Move(ecs_iter_t *it) { } void PrintPosition(ecs_iter_t *it) { - const Position *p = ecs_field(it, const Position, 1); + const Position *p = ecs_field(it, const Position, 0); for (int i = 0; i < it->count; i ++) { printf("%s: {%f, %f}\n", ecs_get_name(it->world, it->entities[i]), @@ -67,11 +67,11 @@ int main(int argc, char *argv[]) { ECS_SYSTEM(ecs, PrintPosition, EcsPostUpdate, [in] Position); // Create a few test entities for a Position, Velocity query - ecs_entity_t e1 = ecs_new_entity(ecs, "e1"); + ecs_entity_t e1 = ecs_entity(ecs, { .name = "e1" }); ecs_set(ecs, e1, Position, {10, 20}); ecs_set(ecs, e1, Velocity, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(ecs, "e2"); + ecs_entity_t e2 = ecs_entity(ecs, { .name = "e2" }); ecs_set(ecs, e2, Position, {10, 20}); ecs_set(ecs, e2, Velocity, {3, 4}); @@ -99,7 +99,7 @@ int main(int argc, char *argv[]) { // // To create the same system with ecs_system_init, do: // ecs_system_init(ecs, &(ecs_system_desc_t){ - // .query.filter.terms = { + // .query.terms = { // { // .id = ecs_id(Position), // .inout = EcsInOutNone @@ -107,12 +107,12 @@ int main(int argc, char *argv[]) { // { // .id = ecs_id(Velocity), // .inout = EcsOut, - // .src.flags = EcsIsEntity + // .src.id = EcsIsEntity // } // }, // .entity = { // .name = "SetVelocity", - // .add = {ecs_dependson(EcsOnUpdate)} + // .add = ecs_ids(ecs_dependson(EcsOnUpdate)) // }, // .callback = SetVelocity // }); diff --git a/vendors/flecs/examples/c/systems/sync_point_delete/BUILD b/vendors/flecs/examples/c/systems/sync_point_delete/BUILD new file mode 100644 index 000000000..6eb0c6699 --- /dev/null +++ b/vendors/flecs/examples/c/systems/sync_point_delete/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "sync_point_delete", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/sync_point_delete/src/main.c b/vendors/flecs/examples/c/systems/sync_point_delete/src/main.c index 857e95e2d..0aa936716 100644 --- a/vendors/flecs/examples/c/systems/sync_point_delete/src/main.c +++ b/vendors/flecs/examples/c/systems/sync_point_delete/src/main.c @@ -6,8 +6,8 @@ typedef struct { } Position, Velocity; void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - const Velocity *v = ecs_field(it, const Velocity, 2); + Position *p = ecs_field(it, Position, 0); + const Velocity *v = ecs_field(it, const Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -16,7 +16,7 @@ void Move(ecs_iter_t *it) { } void DeleteEntity(ecs_iter_t *it) { - const Position *p = ecs_field(it, Position, 1); + const Position *p = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i ++) { if (p[i].x >= 3) { @@ -28,7 +28,7 @@ void DeleteEntity(ecs_iter_t *it) { } void PrintPosition(ecs_iter_t *it) { - const Position *p = ecs_field(it, const Position, 1); + const Position *p = ecs_field(it, const Position, 0); for (int i = 0; i < it->count; i ++) { printf("%s: {%f, %f}\n", ecs_get_name(it->world, it->entities[i]), @@ -69,11 +69,11 @@ int main(int argc, char *argv[]) { ECS_SYSTEM(ecs, PrintPosition, EcsPostUpdate, [in] Position); // Create a few test entities for a Position, Velocity query - ecs_entity_t e1 = ecs_new_entity(ecs, "e1"); + ecs_entity_t e1 = ecs_entity(ecs, { .name = "e1" }); ecs_set(ecs, e1, Position, {0, 0}); ecs_set(ecs, e1, Velocity, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(ecs, "e2"); + ecs_entity_t e2 = ecs_entity(ecs, { .name = "e2" }); ecs_set(ecs, e2, Position, {1, 2}); ecs_set(ecs, e2, Velocity, {1, 2}); @@ -114,7 +114,7 @@ int main(int argc, char *argv[]) { // // To create the same system with ecs_system_init, do: // ecs_system_init(ecs, &(ecs_system_desc_t){ - // .query.filter.terms = { + // .query.terms = { // { // .id = ecs_id(Position), // .inout = EcsIn @@ -122,12 +122,12 @@ int main(int argc, char *argv[]) { // { // .id = EcsWildcard, // .inout = EcsOut, - // .src.flags = EcsIsEntity + // .src.id = EcsIsEntity // } // }, // .entity = { // .name = "DeleteEntity", - // .add = {ecs_dependson(EcsOnUpdate)} + // .add = ecs_ids(ecs_dependson(EcsOnUpdate)) // }, // .callback = DeleteEntity // }); diff --git a/vendors/flecs/examples/c/systems/system_ctx/BUILD b/vendors/flecs/examples/c/systems/system_ctx/BUILD new file mode 100644 index 000000000..a3f8fb1f1 --- /dev/null +++ b/vendors/flecs/examples/c/systems/system_ctx/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "system_ctx", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/system_ctx/src/main.c b/vendors/flecs/examples/c/systems/system_ctx/src/main.c index 78c6dc0ee..14f9064dd 100644 --- a/vendors/flecs/examples/c/systems/system_ctx/src/main.c +++ b/vendors/flecs/examples/c/systems/system_ctx/src/main.c @@ -26,8 +26,8 @@ double distance_sqr(const Position *p1, const Position *p2) { void Collide(ecs_iter_t *it) { ecs_query_t *q_collide = it->ctx; // Get query from system context - const Position *p1 = ecs_field(it, Position, 1); - const Radius *r1 = ecs_field(it, Radius, 2); + const Position *p1 = ecs_field(it, Position, 0); + const Radius *r1 = ecs_field(it, Radius, 1); for (int i = 0; i < it->count; i ++) { ecs_entity_t e1 = it->entities[i]; @@ -35,8 +35,8 @@ void Collide(ecs_iter_t *it) { // For each matching entity, iterate the query ecs_iter_t qit = ecs_query_iter(it->world, q_collide); while (ecs_query_next(&qit)) { - const Position *p2 = ecs_field(&qit, Position, 1); - const Radius *r2 = ecs_field(&qit, Radius, 2); + const Position *p2 = ecs_field(&qit, Position, 0); + const Radius *r2 = ecs_field(&qit, Radius, 1); for (int j = 0; j < qit.count; j ++) { ecs_entity_t e2 = qit.entities[j]; if (e1 == e2) { @@ -70,7 +70,7 @@ int main(int argc, char *argv[]) { // Create a query for Position that we can use inside the collide system to // check each entity with each other entity. ecs_query_t *q_position = ecs_query(ecs, { - .filter.terms = { + .terms = { { ecs_id(Position), .inout = EcsIn }, { ecs_id(Radius), .inout = EcsIn } } @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) { // Create collide system that passes query as context ecs_entity_t collide = ecs_system(ecs, { - .query.filter.terms = { + .query.terms = { { .id = ecs_id(Position), .inout = EcsIn }, { ecs_id(Radius), .inout = EcsIn } }, @@ -88,7 +88,7 @@ int main(int argc, char *argv[]) { // Create a few test entities for (int i = 0; i < 10; i ++) { - ecs_entity_t e = ecs_new_id(ecs); + ecs_entity_t e = ecs_new(ecs); ecs_set(ecs, e, Position, { .x = rand() % 100, .y = rand() % 100 }); ecs_set(ecs, e, Radius, { rand() % 10 + 1 }); } diff --git a/vendors/flecs/examples/c/systems/target_fps/BUILD b/vendors/flecs/examples/c/systems/target_fps/BUILD new file mode 100644 index 000000000..8d3fc950f --- /dev/null +++ b/vendors/flecs/examples/c/systems/target_fps/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "target_fps", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/time_interval/BUILD b/vendors/flecs/examples/c/systems/time_interval/BUILD new file mode 100644 index 000000000..ccceb4e69 --- /dev/null +++ b/vendors/flecs/examples/c/systems/time_interval/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "time_interval", + srcs = glob([ + "src/*.c", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/c/systems/time_interval/src/main.c b/vendors/flecs/examples/c/systems/time_interval/src/main.c index e324f8981..cc9b9d802 100644 --- a/vendors/flecs/examples/c/systems/time_interval/src/main.c +++ b/vendors/flecs/examples/c/systems/time_interval/src/main.c @@ -14,7 +14,7 @@ int main(int argc, char *argv[]) { ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "Tick", - .add = { ecs_dependson(EcsOnUpdate) } // run in OnUpdate phase + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) // run in OnUpdate phase }), .callback = Tick, .interval = 1.0 // time in seconds @@ -23,7 +23,7 @@ int main(int argc, char *argv[]) { ecs_system(ecs, { .entity = ecs_entity(ecs, { .name = "FastTick", - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = Tick, .interval = 0.5 diff --git a/vendors/flecs/examples/cpp/entities/basics/BUILD b/vendors/flecs/examples/cpp/entities/basics/BUILD new file mode 100644 index 000000000..637002bd6 --- /dev/null +++ b/vendors/flecs/examples/cpp/entities/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/entities/emplace/BUILD b/vendors/flecs/examples/cpp/entities/emplace/BUILD new file mode 100644 index 000000000..8c96389a7 --- /dev/null +++ b/vendors/flecs/examples/cpp/entities/emplace/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "emplace", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/entities/hierarchy/BUILD b/vendors/flecs/examples/cpp/entities/hierarchy/BUILD new file mode 100644 index 000000000..109c9882c --- /dev/null +++ b/vendors/flecs/examples/cpp/entities/hierarchy/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hierarchy", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/entities/hierarchy/src/main.cpp b/vendors/flecs/examples/cpp/entities/hierarchy/src/main.cpp index 99bc6ac80..766527484 100644 --- a/vendors/flecs/examples/cpp/entities/hierarchy/src/main.cpp +++ b/vendors/flecs/examples/cpp/entities/hierarchy/src/main.cpp @@ -60,6 +60,30 @@ int main(int, char *[]) { // Is the Moon a child of Earth? std::cout << "Child of Earth? " << moon.has(flecs::ChildOf, earth) << "\n\n"; + // Lookup the moon by name + flecs::entity e = ecs.lookup("Sun::Earth::Moon"); + std::cout << "Moon found: " << e.path() << "\n\n"; + // Do a depth-first walk of the tree iterate_tree(sun); + + // Output: + // Child of Earth? 1 + // + // Moon found: ::Sun::Earth::Moon + // + // ::Sun [Star, Position, (Identifier,Name)] + // {1, 1} + // + // ::Sun::Mercury [Position, Planet, (Identifier,Name), (ChildOf,Sun)] + // {2, 2} + // + // ::Sun::Venus [Position, Planet, (Identifier,Name), (ChildOf,Sun)] + // {3, 3} + // + // ::Sun::Earth [Position, Planet, (Identifier,Name), (ChildOf,Sun)] + // {4, 4} + // + // ::Sun::Earth::Moon [Position, Moon, (Identifier,Name), (ChildOf,Sun.Earth)] + // {4.1, 4.1} } diff --git a/vendors/flecs/examples/cpp/entities/hooks/BUILD b/vendors/flecs/examples/cpp/entities/hooks/BUILD new file mode 100644 index 000000000..8fe234dd7 --- /dev/null +++ b/vendors/flecs/examples/cpp/entities/hooks/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hooks", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/entities/iterate_components/BUILD b/vendors/flecs/examples/cpp/entities/iterate_components/BUILD new file mode 100644 index 000000000..da28cfdf4 --- /dev/null +++ b/vendors/flecs/examples/cpp/entities/iterate_components/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "iterate_components", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/entities/multi_set_get/BUILD b/vendors/flecs/examples/cpp/entities/multi_set_get/BUILD new file mode 100644 index 000000000..704b4d728 --- /dev/null +++ b/vendors/flecs/examples/cpp/entities/multi_set_get/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "multi_set_get", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/entities/multi_set_get/src/main.cpp b/vendors/flecs/examples/cpp/entities/multi_set_get/src/main.cpp index 2f0b03c9e..2a73cd9a2 100644 --- a/vendors/flecs/examples/cpp/entities/multi_set_get/src/main.cpp +++ b/vendors/flecs/examples/cpp/entities/multi_set_get/src/main.cpp @@ -16,7 +16,7 @@ int main(int, char *[]) { // Create new entity, set Position and Mass component flecs::entity e = ecs.entity() - .set([](Position& p, Mass& m) { + .insert([](Position& p, Mass& m) { p.x = 10; p.y = 20; m.value = 100; diff --git a/vendors/flecs/examples/cpp/entities/prefab/BUILD b/vendors/flecs/examples/cpp/entities/prefab/BUILD new file mode 100644 index 000000000..3f6c614cd --- /dev/null +++ b/vendors/flecs/examples/cpp/entities/prefab/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "prefab", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/entities/prefab/src/main.cpp b/vendors/flecs/examples/cpp/entities/prefab/src/main.cpp index 3d04d3334..39802be54 100644 --- a/vendors/flecs/examples/cpp/entities/prefab/src/main.cpp +++ b/vendors/flecs/examples/cpp/entities/prefab/src/main.cpp @@ -25,7 +25,7 @@ int main(int, char *[]) { // By default components in an inheritance hierarchy are shared between // entities. The override function ensures that instances have a private // copy of the component. - .override(); + .auto_override(); flecs::entity freighter = ecs.prefab("Freighter") // Short for .add(flecs::IsA, spaceship). This ensures the entity diff --git a/vendors/flecs/examples/cpp/explorer/BUILD b/vendors/flecs/examples/cpp/explorer/BUILD new file mode 100644 index 000000000..aa758da64 --- /dev/null +++ b/vendors/flecs/examples/cpp/explorer/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "explorer", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/explorer/src/main.cpp b/vendors/flecs/examples/cpp/explorer/src/main.cpp index 237fb0d80..3e02e568c 100644 --- a/vendors/flecs/examples/cpp/explorer/src/main.cpp +++ b/vendors/flecs/examples/cpp/explorer/src/main.cpp @@ -13,7 +13,7 @@ int main(int argc, char *argv[]) { flecs::world world(argc, argv); world.import(); - world.import(); // Collect statistics periodically + world.import(); // Collect statistics periodically // Mass component world.component() @@ -32,6 +32,6 @@ int main(int argc, char *argv[]) { // Run application with REST interface. When the application is running, // navigate to https://flecs.dev/explorer to inspect it! // - // See docs/RestApi.md#explorer for more information. - return world.app().enable_rest().run(); + // See docs/FlecsRemoteApi.md#explorer for more information. + // return world.app().enable_rest().run(); } diff --git a/vendors/flecs/examples/cpp/game_mechanics/factory/resources.flecs b/vendors/flecs/examples/cpp/game_mechanics/factory/resources.flecs index e20bb17a7..54e3dd216 100644 --- a/vendors/flecs/examples/cpp/game_mechanics/factory/resources.flecs +++ b/vendors/flecs/examples/cpp/game_mechanics/factory/resources.flecs @@ -2,52 +2,52 @@ using factories resources { // Ore stacks can contain 50 items - with StackCount{50} { + with StackCount(50) { // Raw materials can't be produced - Copper - Iron - Aluminium + Copper, + Iron, + Aluminium, Steel { - - Requires[{Iron, 1}] - - TimeToProduce{2} - - StackCount{50} + Requires: [{Iron, 1}] + TimeToProduce: {2} + StackCount: {50} } } Gear { - - Requires[{Iron, 1}] - - TimeToProduce{1} - - StackCount{100} + Requires: [{Iron, 1}] + TimeToProduce: {1} + StackCount: {100} } Circuit { - - Requires[{Iron, 1}, {Copper, 3}] - - TimeToProduce{2} - - StackCount{100} + Requires: [{Iron, 1}, {Copper, 3}] + TimeToProduce: {2} + StackCount: {100} } SolarPanel { - - Requires[{Copper, 5}, {Circuit, 15}, {Steel, 5}] - - TimeToProduce{10} - - StackCount{20} + Requires: [{Copper, 5}, {Circuit, 15}, {Steel, 5}] + TimeToProduce: {10} + StackCount: {20} } HullMaterial { - - Requires[{Aluminium, 10}, {Copper, 5}, {Steel, 20}] - - TimeToProduce{10} - - StackCount{20} + Requires: [{Aluminium, 10}, {Copper, 5}, {Steel, 20}] + TimeToProduce: {10} + StackCount: {20} } Radar { - - Requires[{Gear, 5}, {Circuit, 5}, {Iron, 10}] - - TimeToProduce{20} - - StackCount{1} + Requires: [{Gear, 5}, {Circuit, 5}, {Iron, 10}] + TimeToProduce: {20} + StackCount: {1} } Satellite { - - Requires[{HullMaterial, 10}, {SolarPanel, 5}, {Radar, 1}] - - TimeToProduce{30} - - StackCount{1} + Requires: [{HullMaterial, 10}, {SolarPanel, 5}, {Radar, 1}] + TimeToProduce: {30} + StackCount: {1} } } diff --git a/vendors/flecs/examples/cpp/game_mechanics/factory/scene.flecs b/vendors/flecs/examples/cpp/game_mechanics/factory/scene.flecs index 59757fb13..44d0da680 100644 --- a/vendors/flecs/examples/cpp/game_mechanics/factory/scene.flecs +++ b/vendors/flecs/examples/cpp/game_mechanics/factory/scene.flecs @@ -4,74 +4,74 @@ using resources // Resource depots depot { iron { - - (Stores, Iron){amount: 5000} + (Stores, Iron): {amount: 5000} } copper { - - (Stores, Copper){amount: 5000} + (Stores, Copper): {amount: 5000} } aluminium { - - (Stores, Aluminium){amount: 5000} + (Stores, Aluminium): {amount: 5000} } gear { - - (Stores, Gear){amount: 0} + (Stores, Gear): {amount: 0} } steel { - - (Stores, Steel){amount: 0} + (Stores, Steel): {amount: 0} } circuit { - - (Stores, Circuit){amount: 0} + (Stores, Circuit): {amount: 0} } } // Factories factory { - with Factory{ recipe: Gear, inputs: [depot.iron], output: depot.gear } { - gear_1 + with Factory(recipe: Gear, inputs: [depot.iron], output: depot.gear) { + gear_1, gear_2 } - with Factory{recipe: Steel, inputs: [depot.iron], output: depot.steel} { - steel_1 - steel_2 - steel_3 - steel_4 - steel_5 + with Factory(recipe: Steel, inputs: [depot.iron], output: depot.steel) { + steel_1, + steel_2, + steel_3, + steel_4, + steel_5, steel_6 } - with Factory{recipe: Circuit, inputs: [depot.iron, depot.copper], output: depot.circuit } { - circuit_1 + with Factory(recipe: Circuit, inputs: [depot.iron, depot.copper], output: depot.circuit) { + circuit_1, circuit_2 } radar { - - Factory{ + Factory: { recipe: Radar, inputs: [depot.gear, depot.circuit, depot.iron] } } solar_panel { - - Factory{ + Factory: { recipe: SolarPanel, inputs: [depot.copper, depot.circuit, depot.steel] } } hull { - - Factory{ + Factory: { recipe: HullMaterial, inputs: [depot.aluminium, depot.copper, depot.steel] } } satellite { - - Factory{ + Factory: { recipe: Satellite, inputs: [hull, solar_panel, radar] } diff --git a/vendors/flecs/examples/cpp/game_mechanics/factory/src/main.cpp b/vendors/flecs/examples/cpp/game_mechanics/factory/src/main.cpp index a2536b673..e4490aade 100644 --- a/vendors/flecs/examples/cpp/game_mechanics/factory/src/main.cpp +++ b/vendors/flecs/examples/cpp/game_mechanics/factory/src/main.cpp @@ -199,7 +199,7 @@ struct factories { // System that transfers resource and resets factory for next item world.system("Transfer") - .term_at(3).second(Wildcard) + .term_at(2).second(Wildcard) .with(FactoryState::TransferResource) .interval(1.0f) .each([](entity factory, FactorySupply& s, FactoryProduction& p, Stores& out) { @@ -289,7 +289,7 @@ struct factories { factory.add(FactoryState::WaitingForResources); // Initialize supply component - factory.set([&](FactorySupply& s) { + factory.insert([&](FactorySupply& s) { for (int i = 0; i < MaxInputs; i ++) { entity resource = world.entity(r.items[i].resource); entity input = world.entity(config.inputs[i]); @@ -332,8 +332,8 @@ int main(int argc, char *argv[]) { world.import(); - ecs_plecs_from_file(world, "resources.flecs"); - ecs_plecs_from_file(world, "scene.flecs"); + ecs_script_run_file(world, "resources.flecs"); + ecs_script_run_file(world, "scene.flecs"); return world.app() .enable_rest() diff --git a/vendors/flecs/examples/cpp/game_mechanics/inventory_system/BUILD b/vendors/flecs/examples/cpp/game_mechanics/inventory_system/BUILD new file mode 100644 index 000000000..64c68ce69 --- /dev/null +++ b/vendors/flecs/examples/cpp/game_mechanics/inventory_system/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "inventory_system", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/game_mechanics/inventory_system/src/main.cpp b/vendors/flecs/examples/cpp/game_mechanics/inventory_system/src/main.cpp index b485eb3d2..f4cbc9630 100644 --- a/vendors/flecs/examples/cpp/game_mechanics/inventory_system/src/main.cpp +++ b/vendors/flecs/examples/cpp/game_mechanics/inventory_system/src/main.cpp @@ -98,9 +98,8 @@ flecs::entity get_container(flecs::entity container) { // Iterate all items in an inventory template void for_each_item(flecs::entity container, const Func& func) { - container.world().filter_builder() + container.world().query_builder() .with(container) - .build() .each(func); } @@ -141,8 +140,8 @@ void transfer_item(flecs::entity container, flecs::entity item) { flecs::entity dst_item = find_item_w_kind(container, ik); if (dst_item) { // If a matching item was found, increase its amount - Amount *dst_amt = dst_item.get_mut(); - dst_amt->value += amt->value; + Amount& dst_amt = dst_item.ensure(); + dst_amt.value += amt->value; item.destruct(); // Remove the src item return; } else { @@ -218,24 +217,24 @@ void attack(flecs::entity player, flecs::entity weapon) { } // For each usage of the weapon, subtract one from its health - Health *weapon_health = weapon.get_mut(); - if (!--weapon_health->value) { + Health& weapon_health = weapon.ensure(); + if (!--weapon_health.value) { std::cout << " - " << item_name(weapon) << " is destroyed!\n"; weapon.destruct(); } else { std::cout << " - " << item_name(weapon) << " has " - << weapon_health->value << " uses left"; + << weapon_health.value << " uses left"; } // If armor didn't counter the whole attack, subtract from the player health if (att_value) { - Health *player_health = player.get_mut(); - if (!(player_health->value -= att_value)) { + Health& player_health = player.ensure(); + if (!(player_health.value -= att_value)) { std::cout << " - " << player.name() << " died!\n"; player.destruct(); } else { std::cout << " - " << player.name() << " has " - << player_health->value << " health left after taking " + << player_health.value << " health left after taking " << att_value << " damage\n"; } } @@ -289,17 +288,17 @@ int main(int, char *[]) { // Register item prefabs ecs.prefab().add() .set({ 1 }) - .set_override({ 5 }); // copy to instance, don't share + .set_auto_override({ 5 }); // copy to instance, don't share ecs.prefab().add() .set({ 2 }) - .set_override({ 10 }); + .set_auto_override({ 10 }); ecs.prefab().add() - .set_override({ 10 }); + .set_auto_override({ 10 }); ecs.prefab().add() - .set_override({ 20 }); + .set_auto_override({ 20 }); // Create a loot box with items flecs::entity loot_box = ecs.entity("Chest").add().with([&]{ diff --git a/vendors/flecs/examples/cpp/game_mechanics/scene_management/BUILD b/vendors/flecs/examples/cpp/game_mechanics/scene_management/BUILD new file mode 100644 index 000000000..2f88aa97c --- /dev/null +++ b/vendors/flecs/examples/cpp/game_mechanics/scene_management/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "scene_management", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/hello_world/BUILD b/vendors/flecs/examples/cpp/hello_world/BUILD new file mode 100644 index 000000000..5b156afe2 --- /dev/null +++ b/vendors/flecs/examples/cpp/hello_world/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hello_world", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/hello_world/project.json b/vendors/flecs/examples/cpp/hello_world/project.json index 5af4caa68..254debc4a 100644 --- a/vendors/flecs/examples/cpp/hello_world/project.json +++ b/vendors/flecs/examples/cpp/hello_world/project.json @@ -8,5 +8,8 @@ "flecs" ], "language": "c++" + }, + "lang.cpp": { + "cpp-standard": "c++11" } } \ No newline at end of file diff --git a/vendors/flecs/examples/cpp/modules/simple_module/BUILD b/vendors/flecs/examples/cpp/modules/simple_module/BUILD new file mode 100644 index 000000000..932401541 --- /dev/null +++ b/vendors/flecs/examples/cpp/modules/simple_module/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "simple_module", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/modules/simple_module/src/simple_module.cpp b/vendors/flecs/examples/cpp/modules/simple_module/src/simple_module.cpp index 0e8c4c551..511827892 100644 --- a/vendors/flecs/examples/cpp/modules/simple_module/src/simple_module.cpp +++ b/vendors/flecs/examples/cpp/modules/simple_module/src/simple_module.cpp @@ -4,11 +4,11 @@ namespace simple { module::module(flecs::world& ecs) { // Register module with world. The module entity will be created with the - // same hierarchy as the C++ namespaces (e.g. simple.module) + // same hierarchy as the C++ namespaces (e.g. simple::module) ecs.module(); // All contents of the module are created inside the module's namespace, so - // the Position component will be created as simple.module.Position + // the Position component will be created as simple::module::Position // Component registration is optional, however by registering components // inside the module constructor, they will be created inside the scope diff --git a/vendors/flecs/examples/cpp/observers/basics/BUILD b/vendors/flecs/examples/cpp/observers/basics/BUILD new file mode 100644 index 000000000..637002bd6 --- /dev/null +++ b/vendors/flecs/examples/cpp/observers/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/observers/custom_event/BUILD b/vendors/flecs/examples/cpp/observers/custom_event/BUILD new file mode 100644 index 000000000..a39356ab9 --- /dev/null +++ b/vendors/flecs/examples/cpp/observers/custom_event/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_event", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/observers/custom_event/src/main.cpp b/vendors/flecs/examples/cpp/observers/custom_event/src/main.cpp index 7700d7868..fc99e0ae0 100644 --- a/vendors/flecs/examples/cpp/observers/custom_event/src/main.cpp +++ b/vendors/flecs/examples/cpp/observers/custom_event/src/main.cpp @@ -29,7 +29,7 @@ int main(int, char *[]) { << it.entity(i).name() << "\n"; }); - // The observer filter can be matched against the entity, so make sure it + // The observer query can be matched against the entity, so make sure it // has the Position component before emitting the event. This does not // trigger the observer yet. flecs::entity e = ecs.entity("e") diff --git a/vendors/flecs/examples/cpp/observers/enqueue_entity_event/BUILD b/vendors/flecs/examples/cpp/observers/enqueue_entity_event/BUILD new file mode 100644 index 000000000..dfb8d0d72 --- /dev/null +++ b/vendors/flecs/examples/cpp/observers/enqueue_entity_event/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "enqueue_entity_event", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/observers/enqueue_event/BUILD b/vendors/flecs/examples/cpp/observers/enqueue_event/BUILD new file mode 100644 index 000000000..ed30d81c3 --- /dev/null +++ b/vendors/flecs/examples/cpp/observers/enqueue_event/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "enqueue_event", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/observers/enqueue_event/src/main.cpp b/vendors/flecs/examples/cpp/observers/enqueue_event/src/main.cpp index 9e45f6645..2943f59ed 100644 --- a/vendors/flecs/examples/cpp/observers/enqueue_event/src/main.cpp +++ b/vendors/flecs/examples/cpp/observers/enqueue_event/src/main.cpp @@ -25,7 +25,7 @@ int main(int, char *[]) { << it.entity(i).name() << "\n"; }); - // The observer filter can be matched against the entity, so make sure it + // The observer query can be matched against the entity, so make sure it // has the Position component before emitting the event. This does not // trigger the observer yet. flecs::entity e = ecs.entity("e") diff --git a/vendors/flecs/examples/cpp/observers/entity_event/BUILD b/vendors/flecs/examples/cpp/observers/entity_event/BUILD new file mode 100644 index 000000000..810a4676a --- /dev/null +++ b/vendors/flecs/examples/cpp/observers/entity_event/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "entity_event", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/observers/monitor/BUILD b/vendors/flecs/examples/cpp/observers/monitor/BUILD new file mode 100644 index 000000000..aa0b5a21c --- /dev/null +++ b/vendors/flecs/examples/cpp/observers/monitor/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "monitor", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/observers/monitor/src/main.cpp b/vendors/flecs/examples/cpp/observers/monitor/src/main.cpp index 5c6bc4f87..c6917ad9b 100644 --- a/vendors/flecs/examples/cpp/observers/monitor/src/main.cpp +++ b/vendors/flecs/examples/cpp/observers/monitor/src/main.cpp @@ -2,7 +2,7 @@ #include // A monitor observer triggers when an entity starts/stop matching the observer -// filter. The observer communicates whether an entity is "entering/leaving" the +// query. The observer communicates whether an entity is "entering/leaving" the // monitor by setting ecs_iter_t::event to EcsOnAdd (for entering) or // EcsOnRemove (for leaving). // diff --git a/vendors/flecs/examples/cpp/observers/propagate/BUILD b/vendors/flecs/examples/cpp/observers/propagate/BUILD new file mode 100644 index 000000000..902f663b4 --- /dev/null +++ b/vendors/flecs/examples/cpp/observers/propagate/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "propagate", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/observers/propagate/src/main.cpp b/vendors/flecs/examples/cpp/observers/propagate/src/main.cpp index 77d6ee2ba..ad3465343 100644 --- a/vendors/flecs/examples/cpp/observers/propagate/src/main.cpp +++ b/vendors/flecs/examples/cpp/observers/propagate/src/main.cpp @@ -5,7 +5,7 @@ // listen for events from a parent or prefab, like triggering when a component // inherited from a prefab was set. // -// Event propagation happens automatically when an observer contains a filter +// Event propagation happens automatically when an observer contains a query // with the EcsUp flag set (indicating upwards traversal). Observers use the // same matching logic as queries: if a query with upwards traversal matches an // entity, so will an observer. @@ -21,7 +21,7 @@ int main(int, char *[]) { // Create observer that listens for events from both self and parent ecs.observer() - .term_at(2).parent() // select 2nd Position from parent + .term_at(1).parent() // select 2nd Position from parent .event(flecs::OnSet) .each([](flecs::iter& it, size_t i, Position& p_self, Position& p_parent) { std::cout << " - " << it.event().name() << ": " diff --git a/vendors/flecs/examples/cpp/observers/two_components/BUILD b/vendors/flecs/examples/cpp/observers/two_components/BUILD new file mode 100644 index 000000000..d32f3e616 --- /dev/null +++ b/vendors/flecs/examples/cpp/observers/two_components/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "two_components", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/observers/two_components/src/main.cpp b/vendors/flecs/examples/cpp/observers/two_components/src/main.cpp index 26c1ed9ba..49d8357eb 100644 --- a/vendors/flecs/examples/cpp/observers/two_components/src/main.cpp +++ b/vendors/flecs/examples/cpp/observers/two_components/src/main.cpp @@ -2,7 +2,7 @@ #include // An observer can match multiple components/tags. Only entities that match the -// entire observer filter will be forwarded to the callback. For example, an +// entire observer query will be forwarded to the callback. For example, an // observer for Position,Velocity won't match an entity that only has Position. struct Position { diff --git a/vendors/flecs/examples/cpp/observers/yield_existing/BUILD b/vendors/flecs/examples/cpp/observers/yield_existing/BUILD new file mode 100644 index 000000000..d6939c076 --- /dev/null +++ b/vendors/flecs/examples/cpp/observers/yield_existing/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "yield_existing", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/observers/yield_existing/src/main.cpp b/vendors/flecs/examples/cpp/observers/yield_existing/src/main.cpp index de7b908e6..b34c97d54 100644 --- a/vendors/flecs/examples/cpp/observers/yield_existing/src/main.cpp +++ b/vendors/flecs/examples/cpp/observers/yield_existing/src/main.cpp @@ -4,9 +4,6 @@ // Observers can enable a "yield_existing" feature that upon creation of the // observer produces events for all entities that match the observer query. The // feature is only implemented for the builtin EcsOnAdd and EcsOnSet events. -// -// Custom events can also implement behavior for yield_existing by adding the -// Iterable component to the event (see EcsIterable for more details). struct Position { double x, y; diff --git a/vendors/flecs/examples/cpp/prefabs/basics/BUILD b/vendors/flecs/examples/cpp/prefabs/basics/BUILD new file mode 100644 index 000000000..637002bd6 --- /dev/null +++ b/vendors/flecs/examples/cpp/prefabs/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/prefabs/basics/src/main.cpp b/vendors/flecs/examples/cpp/prefabs/basics/src/main.cpp index adb9df244..e1a6c5fcc 100644 --- a/vendors/flecs/examples/cpp/prefabs/basics/src/main.cpp +++ b/vendors/flecs/examples/cpp/prefabs/basics/src/main.cpp @@ -27,6 +27,11 @@ struct Defense { int main() { flecs::world ecs; + // Make Defense component inheritable. By default components are copied from + // the instance to the prefab. An inherited component is only stored on the + // prefab, and is shared across all instances. + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + // Create a SpaceShip prefab with a Defense component. flecs::entity SpaceShip = ecs.prefab("SpaceShip") .set({ 50 }); diff --git a/vendors/flecs/examples/cpp/prefabs/hierarchy/BUILD b/vendors/flecs/examples/cpp/prefabs/hierarchy/BUILD new file mode 100644 index 000000000..109c9882c --- /dev/null +++ b/vendors/flecs/examples/cpp/prefabs/hierarchy/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hierarchy", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/prefabs/nested_prefabs/BUILD b/vendors/flecs/examples/cpp/prefabs/nested_prefabs/BUILD new file mode 100644 index 000000000..57033f6aa --- /dev/null +++ b/vendors/flecs/examples/cpp/prefabs/nested_prefabs/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "nested_prefabs", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/prefabs/nested_prefabs/src/main.cpp b/vendors/flecs/examples/cpp/prefabs/nested_prefabs/src/main.cpp index c1d96bb4a..38ef8f325 100644 --- a/vendors/flecs/examples/cpp/prefabs/nested_prefabs/src/main.cpp +++ b/vendors/flecs/examples/cpp/prefabs/nested_prefabs/src/main.cpp @@ -14,10 +14,6 @@ // prefab. The reason for this is that an instantiated child is an exact copy // of the prefab child, and the prefab child only has an IsA relationship to the // nested prefab. -// -// This example shows how auto overriding (see the auto override example) can be -// used to give instantiated children from a nested prefab a private copy of an -// inherited component. struct TirePressure { double value; @@ -26,10 +22,9 @@ struct TirePressure { int main() { flecs::world ecs; - // Create a Wheel prefab, make sure each instantiated wheel has a private - // copy of the TirePressure component. + // Create a Wheel prefab. flecs::entity Wheel = ecs.prefab("Wheel") - .set_override({ 32 }); + .set({ 32 }); // Create a Car prefab with four wheels. Note how we're using the scope // method, which has the same effect as adding the (ChildOf, Car) pair. diff --git a/vendors/flecs/examples/cpp/prefabs/override/BUILD b/vendors/flecs/examples/cpp/prefabs/override/BUILD new file mode 100644 index 000000000..7a439c54f --- /dev/null +++ b/vendors/flecs/examples/cpp/prefabs/override/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "override", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/prefabs/override/src/main.cpp b/vendors/flecs/examples/cpp/prefabs/override/src/main.cpp index 13092e516..feccdace2 100644 --- a/vendors/flecs/examples/cpp/prefabs/override/src/main.cpp +++ b/vendors/flecs/examples/cpp/prefabs/override/src/main.cpp @@ -1,15 +1,21 @@ #include #include -// Overriding makes it possible for a prefab instance to obtain a private copy -// of an inherited component. To override a component the regular add operation -// is used. The overridden component will be initialized with the value of the -// inherited component. +// When an entity is instantiated from a prefab, components are by default +// copied from the prefab to the instance. This behavior can be customized with +// the OnInstantiate trait, which has three options: +// +// - Override (copy to instance) +// - Inherit (inherit from prefab) +// - DontInherit (don't copy or inherit) // -// In some cases a prefab instance should always have a private copy of an -// inherited component. This can be achieved with an auto override which can be -// added to a prefab. Components with an auto override are automatically -// overridden when the prefab is instantiated. +// When a component is inheritable, it can be overridden manually by adding the +// component to the instance, which also copies the value from the prefab +// component. Additionally, when creating a prefab it is possible to flag a +// component as "auto override", which can change the behavior for a specific +// prefab from "inherit" to "override". +// +// This example shows how these different features can be used. struct Attack { double value; @@ -26,17 +32,17 @@ struct Damage { int main() { flecs::world ecs; - // Attack and Damage are properties that can be shared across many + // Change the instantiation behavior for Attack and Defense to inherit. + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + // Attack and Defense are properties that can be shared across many // spaceships. This saves memory, and speeds up prefab creation as we don't // have to copy the values of Attack and Defense to private components. flecs::entity SpaceShip = ecs.prefab("SpaceShip") .set({ 75 }) - .set({ 100 }); - - // Damage is a property that is private to a spaceship, so add an auto - // override for it. This ensures that each prefab instance will have a - // private copy of the component. - SpaceShip.set_override({ 0 }); + .set({ 100 }) + .set({ 50 }); // Create a prefab instance. flecs::entity inst = ecs.entity("my_spaceship").is_a(SpaceShip); @@ -69,5 +75,5 @@ int main() { // Attack, Damage, (Identifier,Name), (IsA,SpaceShip) // attack: 75 // defense: 100 - // damage: 0 + // damage: 50 } diff --git a/vendors/flecs/examples/cpp/prefabs/slots/BUILD b/vendors/flecs/examples/cpp/prefabs/slots/BUILD new file mode 100644 index 000000000..636218ef9 --- /dev/null +++ b/vendors/flecs/examples/cpp/prefabs/slots/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "slots", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/prefabs/typed_prefabs/BUILD b/vendors/flecs/examples/cpp/prefabs/typed_prefabs/BUILD new file mode 100644 index 000000000..dc1e2e20c --- /dev/null +++ b/vendors/flecs/examples/cpp/prefabs/typed_prefabs/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "typed_prefabs", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/prefabs/variant/BUILD b/vendors/flecs/examples/cpp/prefabs/variant/BUILD new file mode 100644 index 000000000..75efa9ac7 --- /dev/null +++ b/vendors/flecs/examples/cpp/prefabs/variant/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "variant", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/prefabs/variant/src/main.cpp b/vendors/flecs/examples/cpp/prefabs/variant/src/main.cpp index 5ed954996..0eac67dc1 100644 --- a/vendors/flecs/examples/cpp/prefabs/variant/src/main.cpp +++ b/vendors/flecs/examples/cpp/prefabs/variant/src/main.cpp @@ -2,7 +2,7 @@ #include /* Prefabs can inherit from each other, which creates prefab variants. With - * variants applications can reuse a commmon set of components and specialize it + * variants applications can reuse a common set of components and specialize it * by adding or overriding components on the variant. */ struct Attack { @@ -40,7 +40,7 @@ int main() { .set({ 100 }) .set({ 50 }); - // Create a MammotFreighter variant which inherits from Freighter + // Create a MammothFreighter variant which inherits from Freighter flecs::entity MammothFreighter = ecs.prefab("MammothFreighter") .is_a(Freighter) .set({ 500 }); diff --git a/vendors/flecs/examples/cpp/queries/basics/BUILD b/vendors/flecs/examples/cpp/queries/basics/BUILD new file mode 100644 index 000000000..637002bd6 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/basics/src/main.cpp b/vendors/flecs/examples/cpp/queries/basics/src/main.cpp index 8afe14d50..e116ac27a 100644 --- a/vendors/flecs/examples/cpp/queries/basics/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/basics/src/main.cpp @@ -12,9 +12,12 @@ struct Velocity { int main(int, char *[]) { flecs::world ecs; - // Create a query for Position, Velocity. Queries are the fastest way to - // iterate entities as they cache results. - flecs::query q = ecs.query(); + // Create a cached query for Position, Velocity. Cached queries are the + // fastest way to iterate entities as they cache results. + flecs::query q = + ecs.query_builder() + .cached() + .build(); // Create a few test entities for a Position, Velocity query ecs.entity("e1") @@ -48,7 +51,7 @@ int main(int, char *[]) { std::cout << "{" << p.x << ", " << p.y << "}\n"; }); - // Each also accepts flecs::iter + index (for the iterated entity) arguemnts + // Each also accepts flecs::iter + index (for the iterated entity) arguments // currently being iterated. A flecs::iter has lots of information on what // is being iterated, which is demonstrated in the "iter" example. q.each([](flecs::iter& it, size_t i, Position& p, const Velocity& v) { @@ -57,14 +60,19 @@ int main(int, char *[]) { std::cout << it.entity(i).name() << ": {" << p.x << ", " << p.y << "}\n"; }); - // Iter is a bit more verbose, but allows for more control over how entities + // Run is a bit more verbose, but allows for more control over how entities // are iterated as it provides multiple entities in the same callback. - q.iter([](flecs::iter& it, Position *p, const Velocity *v) { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - std::cout << it.entity(i).name() << - ": {" << p[i].x << ", " << p[i].y << "}\n"; + q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + std::cout << it.entity(i).name() << + ": {" << p[i].x << ", " << p[i].y << "}\n"; + } } }); } diff --git a/vendors/flecs/examples/cpp/queries/change_tracking/BUILD b/vendors/flecs/examples/cpp/queries/change_tracking/BUILD new file mode 100644 index 000000000..394ea7689 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/change_tracking/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "change_tracking", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/change_tracking/src/main.cpp b/vendors/flecs/examples/cpp/queries/change_tracking/src/main.cpp index 096bca59a..0a0229f04 100644 --- a/vendors/flecs/examples/cpp/queries/change_tracking/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/change_tracking/src/main.cpp @@ -5,9 +5,8 @@ // is a cheap way of eliminating redundant work, as many entities can be skipped // with a single check. // -// This example shows how to use change tracking in combination with a few other -// techniques, like using prefabs to store a single dirty state for multiple -// entities and instanced queries. +// This example shows how to use change tracking in combination with using +// prefabs to store a single dirty state for multiple entities. struct Dirty { bool value; @@ -20,18 +19,22 @@ struct Position { int main(int, char *[]) { flecs::world ecs; + // Make Dirty inheritable so that queries can match it on prefabs + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + // Create a query that just reads a component. We'll use this query for // change tracking. Change tracking for a query is automatically enabled // when query::changed() is called. // Each query has its own private dirty state which is reset only when the // query is iterated. - flecs::query q_read = ecs.query(); + flecs::query q_read = ecs.query_builder() + .cached() + .build(); // Create a query that writes the component based on a Dirty state. flecs::query q_write = ecs.query_builder() - .term_at(1).up() // Only match Dirty from prefab - .instanced() // Instanced iteration is faster (see example) + .term_at(0).up(flecs::IsA) // Only match Dirty from prefab .build(); // Create two prefabs with a Dirty component. We can use this to share a @@ -60,13 +63,15 @@ int main(int, char *[]) { std::cout << "q_read.changed(): " << q_read.changed() << "\n"; // The changed state will remain true until we have iterated each table. - q_read.iter([](flecs::iter& it) { - // With the it.changed() function we can check if the table we're - // currently iterating has changed since last iteration. - // Because this is the first time the query is iterated, all tables - // will show up as changed. - std::cout << "it.changed() for table [" << it.type().str() << "]: " - << it.changed() << "\n"; + q_read.run([](flecs::iter& it) { + while (it.next()) { + // With the it.changed() function we can check if the table we're + // currently iterating has changed since last iteration. + // Because this is the first time the query is iterated, all tables + // will show up as changed. + std::cout << "it.changed() for table [" << it.type().str() << "]: " + << it.changed() << "\n"; + } }); // Now that we have iterated all tables, the dirty state is reset. @@ -74,23 +79,31 @@ int main(int, char *[]) { // Iterate the write query. Because the Position term is InOut (default) // iterating the query will write to the dirty state of iterated tables. - q_write.iter([](flecs::iter& it, const Dirty *dirty, Position *p) { - std::cout << "iterate table [" << it.type().str() << "]\n"; - - // Because we enforced that Dirty is a shared component, we can check - // a single value for the entire table. - if (!dirty->value) { - // If the dirty flag is false, skip the table. This way the table's - // dirty state is not updated by the query. - it.skip(); - std::cout << "it.skip() for table [" << it.type().str() << "]\n"; - return; - } - - // For all other tables the dirty state will be set. - for (auto i : it) { - p[i].x ++; - p[i].y ++; + q_write.run([](flecs::iter& it) { + while (it.next()) { + auto dirty = it.field(0); + auto p = it.field(1); + + std::cout << "iterate table [" << it.type().str() << "]\n"; + + // Because we enforced that Dirty is a shared component, we can check + // a single value for the entire table. + if (!dirty->value) { + // If the dirty flag is false, skip the table. This way the table's + // dirty state is not updated by the query. + it.skip(); + + // Cleanup iterator resources since iterator wasn't done yet + it.fini(); + std::cout << "it.skip() for table [" << it.type().str() << "]\n"; + break; + } + + // For all other tables the dirty state will be set. + for (auto i : it) { + p[i].x ++; + p[i].y ++; + } } }); @@ -98,10 +111,12 @@ int main(int, char *[]) { std::cout << "\nq_read.changed(): " << q_read.changed() << "\n"; // When we iterate the read query, we'll see that one table has changed. - q_read.iter([](flecs::iter& it) { - std::cout << "it.changed() for table [" << it.type().str() << "]: " - << it.changed() << "\n"; - }); + q_read.run([](flecs::iter& it) { + while (it.next()) { + std::cout << "it.changed() for table [" << it.type().str() << "]: " + << it.changed() << "\n"; + } + }); // Output: // q_read.changed(): 1 diff --git a/vendors/flecs/examples/cpp/queries/component_inheritance/BUILD b/vendors/flecs/examples/cpp/queries/component_inheritance/BUILD new file mode 100644 index 000000000..f4289e59a --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/component_inheritance/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "component_inheritance", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/rules/component_inheritance/include/component_inheritance.h b/vendors/flecs/examples/cpp/queries/component_inheritance/include/component_inheritance.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/component_inheritance/include/component_inheritance.h rename to vendors/flecs/examples/cpp/queries/component_inheritance/include/component_inheritance.h diff --git a/vendors/flecs/examples/cpp/rules/component_inheritance/include/component_inheritance/bake_config.h b/vendors/flecs/examples/cpp/queries/component_inheritance/include/component_inheritance/bake_config.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/component_inheritance/include/component_inheritance/bake_config.h rename to vendors/flecs/examples/cpp/queries/component_inheritance/include/component_inheritance/bake_config.h diff --git a/vendors/flecs/examples/cpp/rules/component_inheritance/project.json b/vendors/flecs/examples/cpp/queries/component_inheritance/project.json similarity index 100% rename from vendors/flecs/examples/cpp/rules/component_inheritance/project.json rename to vendors/flecs/examples/cpp/queries/component_inheritance/project.json diff --git a/vendors/flecs/examples/cpp/rules/component_inheritance/src/main.cpp b/vendors/flecs/examples/cpp/queries/component_inheritance/src/main.cpp similarity index 84% rename from vendors/flecs/examples/cpp/rules/component_inheritance/src/main.cpp rename to vendors/flecs/examples/cpp/queries/component_inheritance/src/main.cpp index 2b6d718ad..a8eb4afbc 100644 --- a/vendors/flecs/examples/cpp/rules/component_inheritance/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/component_inheritance/src/main.cpp @@ -1,7 +1,7 @@ #include #include -// This example shows how rules can be used to match simple inheritance trees. +// This example shows how queries can be used to match simple inheritance trees. struct Unit { }; struct CombatUnit : Unit { }; @@ -40,16 +40,14 @@ int main(int, char *[]) { ecs.entity("builder_1").add(); ecs.entity("builder_2").add(); - // Create a rule to find all ranged units - flecs::rule r = ecs.rule(); + // Create a query to find all ranged units + flecs::query q = ecs.query(); - // Iterate the rule - r.each([](flecs::entity e, RangedUnit) { + // Iterate the query + q.each([](flecs::entity e, RangedUnit) { std::cout << "Unit " << e.name() << " found\n"; }); - r.destruct(); - // Output: // Unit wizard_1 found // Unit wizard_2 found diff --git a/vendors/flecs/examples/cpp/queries/cyclic_variables/BUILD b/vendors/flecs/examples/cpp/queries/cyclic_variables/BUILD new file mode 100644 index 000000000..a15fb9871 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/cyclic_variables/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "cyclic_variables", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/rules/cyclic_variables/include/cyclic_variables.h b/vendors/flecs/examples/cpp/queries/cyclic_variables/include/cyclic_variables.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/cyclic_variables/include/cyclic_variables.h rename to vendors/flecs/examples/cpp/queries/cyclic_variables/include/cyclic_variables.h diff --git a/vendors/flecs/examples/cpp/rules/cyclic_variables/include/cyclic_variables/bake_config.h b/vendors/flecs/examples/cpp/queries/cyclic_variables/include/cyclic_variables/bake_config.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/cyclic_variables/include/cyclic_variables/bake_config.h rename to vendors/flecs/examples/cpp/queries/cyclic_variables/include/cyclic_variables/bake_config.h diff --git a/vendors/flecs/examples/cpp/rules/cyclic_variables/project.json b/vendors/flecs/examples/cpp/queries/cyclic_variables/project.json similarity index 100% rename from vendors/flecs/examples/cpp/rules/cyclic_variables/project.json rename to vendors/flecs/examples/cpp/queries/cyclic_variables/project.json diff --git a/vendors/flecs/examples/cpp/rules/cyclic_variables/src/main.cpp b/vendors/flecs/examples/cpp/queries/cyclic_variables/src/main.cpp similarity index 67% rename from vendors/flecs/examples/cpp/rules/cyclic_variables/src/main.cpp rename to vendors/flecs/examples/cpp/queries/cyclic_variables/src/main.cpp index 6817ccb56..917e5feba 100644 --- a/vendors/flecs/examples/cpp/rules/cyclic_variables/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/cyclic_variables/src/main.cpp @@ -1,7 +1,7 @@ #include #include -// This example shows how a rule may have terms with cyclic dependencies on +// This example shows how a query may have terms with cyclic dependencies on // variables. struct Likes { }; @@ -20,32 +20,30 @@ int main(int, char *[]) { jane.add(john); bob.add(jane); // inserting a bit of drama - // The following rule will only return entities that have a cyclic Likes + // The following query will only return entities that have a cyclic Likes // relationship- that is they must both like each other. // // The equivalent query in the DSL is: // Likes($X, $Y), Likes($Y, $X) // // This is also an example of a query where all sources are variables. By - // default queries use the builtin "This" variable as subject, which is what + // default queries use the builtin "this" variable as subject, which is what // populates the entities array in the query result (accessed by the // iter::entity function). // // Because this query does not use This at all, the entities array will not // be populated, and it.count() will always be 0. - flecs::rule<> r = ecs.rule_builder() + flecs::query<> q = ecs.query_builder() .with("$Y").src("$X") .with("$X").src("$Y") .build(); // Lookup the index of the variables. This will let us quickly lookup their // values while we're iterating. - int x_var = r.find_var("X"); - int y_var = r.find_var("Y"); + int x_var = q.find_var("X"); + int y_var = q.find_var("Y"); - // Because the query doesn't use the This variable we cannot use "each" - // which iterates the entities array. Instead we can use iter like this: - r.iter([&](flecs::iter& it) { + q.each([&](flecs::iter& it, size_t) { flecs::entity x = it.get_var(x_var); flecs::entity y = it.get_var(y_var); std::cout << x.name() << " likes " << y.name() << "\n"; @@ -57,11 +55,9 @@ int main(int, char *[]) { // Jane likes John // John likes Jane - // Note that the rule returns each pair twice. The reason for this is that - // the goal of the rule engine is to return all "facts" that are true + // Note that the query returns each pair twice. The reason for this is that + // the goal of the query engine is to return all "facts" that are true // within the given constraints. Since we did not give it any constraints - // that would favor a person being matched by X or Y, the rule engine + // that would favor a person being matched by X or Y, the query engine // returns both. - - r.destruct(); } diff --git a/vendors/flecs/examples/cpp/rules/facts/.gitignore b/vendors/flecs/examples/cpp/queries/facts/.gitignore similarity index 100% rename from vendors/flecs/examples/cpp/rules/facts/.gitignore rename to vendors/flecs/examples/cpp/queries/facts/.gitignore diff --git a/vendors/flecs/examples/cpp/queries/facts/BUILD b/vendors/flecs/examples/cpp/queries/facts/BUILD new file mode 100644 index 000000000..4427cb755 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/facts/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "facts", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/rules/facts/include/facts.h b/vendors/flecs/examples/cpp/queries/facts/include/facts.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/facts/include/facts.h rename to vendors/flecs/examples/cpp/queries/facts/include/facts.h diff --git a/vendors/flecs/examples/cpp/rules/facts/include/facts/bake_config.h b/vendors/flecs/examples/cpp/queries/facts/include/facts/bake_config.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/facts/include/facts/bake_config.h rename to vendors/flecs/examples/cpp/queries/facts/include/facts/bake_config.h diff --git a/vendors/flecs/examples/cpp/rules/facts/project.json b/vendors/flecs/examples/cpp/queries/facts/project.json similarity index 100% rename from vendors/flecs/examples/cpp/rules/facts/project.json rename to vendors/flecs/examples/cpp/queries/facts/project.json diff --git a/vendors/flecs/examples/cpp/rules/facts/src/main.cpp b/vendors/flecs/examples/cpp/queries/facts/src/main.cpp similarity index 68% rename from vendors/flecs/examples/cpp/rules/facts/src/main.cpp rename to vendors/flecs/examples/cpp/queries/facts/src/main.cpp index 910d9a9d8..376b7c6cc 100644 --- a/vendors/flecs/examples/cpp/rules/facts/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/facts/src/main.cpp @@ -1,14 +1,14 @@ #include #include -// This example shows how to use rules for testing facts. A fact is a query that -// has no variable elements. Consider a regular ECS query like this: +// This example shows how to use queries for testing facts. A fact is a query +// that has no variable elements. Consider a regular ECS query like this: // Position, Velocity // // When written out in full, this query looks like: -// Position($This), Velocity($This) +// Position($this), Velocity($this) // -// "This" is a (builtin) query variable that is unknown before we evaluate the +// "this" is a (builtin) query variable that is unknown before we evaluate the // query. Therefore this query does not test a fact, we can't know which values // This will assume. // @@ -16,7 +16,7 @@ // IsA(Cat, Animal) // // This is a fact: the query has no elements that are unknown before evaluating -// the query. A rule that checks a fact does not return entities, but will +// the query. A query that checks a fact does not return entities, but will // instead return the reasons why a fact is true (if it is true). struct Likes { }; @@ -36,23 +36,23 @@ int main(int, char *[]) { bob.add(john); // bit of drama - // Create a rule that checks if two entities like each other. By itself this - // rule is not a fact, but we can use it to check facts by populating both + // Create a query that checks if two entities like each other. By itself this + // query is not a fact, but we can use it to check facts by populating both // of its variables. // // The equivalent query in the DSL is: - // Likes($X, $Y), Likes($Y, $X) + // Likes($x, $y), Likes($y, $x) // - // Instead of using variables we could have created a rule that referred the - // entities directly, but then we would have to create a rule for each - // fact, vs reusing a single rule for multiple facts. - flecs::rule<> friends = ecs.rule_builder() - .with("$Y").src("$X") - .with("$X").src("$Y") + // Instead of using variables we could have created a query that referred the + // entities directly, but then we would have to create a query for each + // fact, vs reusing a single query for multiple facts. + flecs::query<> friends = ecs.query_builder() + .with("$y").src("$x") + .with("$x").src("$y") .build(); - int x_var = friends.find_var("X"); - int y_var = friends.find_var("Y"); + int x_var = friends.find_var("x"); + int y_var = friends.find_var("y"); // Check a few facts diff --git a/vendors/flecs/examples/cpp/queries/find_entity/BUILD b/vendors/flecs/examples/cpp/queries/find_entity/BUILD new file mode 100644 index 000000000..acdf05cb7 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/find_entity/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "find_entity", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/group_by/BUILD b/vendors/flecs/examples/cpp/queries/group_by/BUILD new file mode 100644 index 000000000..702299536 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/group_by/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "group_by", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/group_by/src/main.cpp b/vendors/flecs/examples/cpp/queries/group_by/src/main.cpp index da11af143..45f9aa150 100644 --- a/vendors/flecs/examples/cpp/queries/group_by/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/group_by/src/main.cpp @@ -69,27 +69,31 @@ int main() { // The query cache now looks like this: // - group First: // - table [Position, (Group, First)] - // - table [Postion, Tag, (Group, First)] + // - table [Position, Tag, (Group, First)] // // - group Second: // - table [Position, (Group, Second)] - // - table [Postion, Tag, (Group, Second)] + // - table [Position, Tag, (Group, Second)] // // - group Third: // - table [Position, (Group, Third)] - // - table [Postion, Tag, (Group, Third)] + // - table [Position, Tag, (Group, Third)] // - q.iter([&](flecs::iter& it, Position *p) { - flecs::entity group = ecs.entity(it.group_id()); - std::cout << " - group " << group.path() << ": table [" - << it.table().str() << "]\n"; + q.run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); - for (auto i : it) { - std::cout << " {" << p[i].x << ", " << p[i].y << "}\n"; - } + flecs::entity group = ecs.entity(it.group_id()); + std::cout << " - group " << group.path() << ": " + << "table [" << it.table().str() << "]\n"; + + for (auto i : it) { + std::cout << " {" << p[i].x << ", " << p[i].y << "}\n"; + } - std::cout << "\n"; + std::cout << "\n"; + } }); // Output: diff --git a/vendors/flecs/examples/cpp/queries/group_by_callbacks/BUILD b/vendors/flecs/examples/cpp/queries/group_by_callbacks/BUILD new file mode 100644 index 000000000..809234291 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/group_by_callbacks/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "group_by_callbacks", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/group_by_callbacks/src/main.cpp b/vendors/flecs/examples/cpp/queries/group_by_callbacks/src/main.cpp index 072414505..d2597f897 100644 --- a/vendors/flecs/examples/cpp/queries/group_by_callbacks/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/group_by_callbacks/src/main.cpp @@ -92,31 +92,36 @@ int main() { // The query cache now looks like this: // - group First: // - table [Position, (Group, First)] - // - table [Postion, Tag, (Group, First)] + // - table [Position, Tag, (Group, First)] // // - group Second: // - table [Position, (Group, Second)] - // - table [Postion, Tag, (Group, Second)] + // - table [Position, Tag, (Group, Second)] // // - group Third: // - table [Position, (Group, Third)] - // - table [Postion, Tag, (Group, Third)] + // - table [Position, Tag, (Group, Third)] // std::cout << "\n"; - q.iter([&](flecs::iter& it, Position *p) { - flecs::entity group = ecs.entity(it.group_id()); - group_ctx *ctx = static_cast(q.group_ctx(group)); - std::cout << " - group " << group.path() << ": table [" - << it.table().str() << "]\n"; - std::cout << " counter: " << ctx->counter << "\n"; + q.run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); - for (auto i : it) { - std::cout << " {" << p[i].x << ", " << p[i].y << "}\n"; - } + flecs::entity group = ecs.entity(it.group_id()); + group_ctx *ctx = static_cast(q.group_ctx(group)); + + std::cout << " - group " << group.path() << ": " + << "table [" << it.table().str() << "]\n"; + std::cout << " counter: " << ctx->counter << "\n"; - std::cout << "\n"; + for (auto i : it) { + std::cout << " {" << p[i].x << ", " << p[i].y << "}\n"; + } + + std::cout << "\n"; + } }); // Deleting the query will call the on_group_deleted callback diff --git a/vendors/flecs/examples/cpp/queries/group_by_custom/BUILD b/vendors/flecs/examples/cpp/queries/group_by_custom/BUILD new file mode 100644 index 000000000..b280b42b5 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/group_by_custom/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "group_by_custom", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/group_by_custom/src/main.cpp b/vendors/flecs/examples/cpp/queries/group_by_custom/src/main.cpp index fbf7bb496..704c5e758 100644 --- a/vendors/flecs/examples/cpp/queries/group_by_custom/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/group_by_custom/src/main.cpp @@ -66,27 +66,31 @@ int main() { // The query cache now looks like this: // - group First: // - table [Position, (Group, First)] - // - table [Postion, Tag, (Group, First)] + // - table [Position, Tag, (Group, First)] // // - group Second: // - table [Position, (Group, Second)] - // - table [Postion, Tag, (Group, Second)] + // - table [Position, Tag, (Group, Second)] // // - group Third: // - table [Position, (Group, Third)] - // - table [Postion, Tag, (Group, Third)] + // - table [Position, Tag, (Group, Third)] // - q.iter([&](flecs::iter& it, Position *p) { - flecs::entity group = ecs.entity(it.group_id()); - std::cout << " - group " << group.path() << ": table [" - << it.table().str() << "]\n"; + q.run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); - for (auto i : it) { - std::cout << " {" << p[i].x << ", " << p[i].y << "}\n"; - } + flecs::entity group = ecs.entity(it.group_id()); + std::cout << " - group " << group.path() << ": [" + << "table [" << it.table().str() << "]\n"; + + for (auto i : it) { + std::cout << " {" << p[i].x << ", " << p[i].y << "}\n"; + } - std::cout << "\n"; + std::cout << "\n"; + } }); // Output: diff --git a/vendors/flecs/examples/cpp/queries/group_iter/BUILD b/vendors/flecs/examples/cpp/queries/group_iter/BUILD new file mode 100644 index 000000000..362a95e42 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/group_iter/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "group_iter", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/group_iter/src/main.cpp b/vendors/flecs/examples/cpp/queries/group_iter/src/main.cpp index 3ab78a75f..f84c591f7 100644 --- a/vendors/flecs/examples/cpp/queries/group_iter/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/group_iter/src/main.cpp @@ -67,20 +67,24 @@ int main() { // Iterate all tables std::cout << "All tables:\n"; - q.iter([&](flecs::iter& it) { - flecs::entity group = ecs.entity(it.group_id()); - std::cout << " - group " << group.path() << ": table [" - << it.table().str() << "]\n"; + q.run([&](flecs::iter& it) { + while (it.next()) { + flecs::entity group = ecs.entity(it.group_id()); + std::cout << " - group " << group.path() << ": table [" + << it.table().str() << "]\n"; + } }); std::cout << "\n"; // Only iterate entities in cell 1_0 std::cout << "Tables for cell 1_0:\n"; - q.iter().set_group().iter([&](flecs::iter& it) { - flecs::entity group = ecs.entity(it.group_id()); - std::cout << " - group " << group.path() << ": table [" - << it.table().str() << "]\n"; + q.set_group().run([&](flecs::iter& it) { + while (it.next()) { + flecs::entity group = ecs.entity(it.group_id()); + std::cout << " - group " << group.path() << ": table [" + << it.table().str() << "]\n"; + } }); // Output: diff --git a/vendors/flecs/examples/cpp/queries/hierarchy/BUILD b/vendors/flecs/examples/cpp/queries/hierarchy/BUILD new file mode 100644 index 000000000..109c9882c --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/hierarchy/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "hierarchy", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/hierarchy/src/main.cpp b/vendors/flecs/examples/cpp/queries/hierarchy/src/main.cpp index 3f454705a..1cfb4103d 100644 --- a/vendors/flecs/examples/cpp/queries/hierarchy/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/hierarchy/src/main.cpp @@ -38,35 +38,32 @@ int main(int, char *[]) { .set({0.1, 0.1}); // Create a hierarchical query to compute the global position from the - // local position and the parent position. - flecs::query q = - ecs.query_builder() + // local position and the parent position. The three template arguments are: + // - Local position + // - Local parent position (* = optional, so we match root entities) + // - World position + flecs::query q = + ecs.query_builder() // Modify terms from template to make sure the query selects the // local, world and parent position components. - .term_at(1).second() + .term_at(0).second() + .term_at(1).second() .term_at(2).second() - .term_at(3).second() // Extend the 2nd query argument to select it from the parent - .term_at(2) + .term_at(1) // Get from the parent, in breadth-first order (cascade) .parent().cascade() - // Make term component optional so we also match the root (sun) - .optional() // Finalize the query .build(); // Do the transform - q.iter([](flecs::iter& it, - const Position *p, const Position *p_parent, Position *p_out) - { - for (auto i : it) { - p_out[i].x = p[i].x; - p_out[i].y = p[i].y; - if (p_parent) { - p_out[i].x += p_parent->x; - p_out[i].y += p_parent->y; - } + q.each([](const Position& p, const Position *p_parent, Position& p_out) { + p_out.x = p.x; + p_out.y = p.y; + if (p_parent) { + p_out.x += p_parent->x; + p_out.y += p_parent->y; } }); @@ -74,4 +71,11 @@ int main(int, char *[]) { ecs.each([](flecs::entity e, flecs::pair p) { std::cout << e.name() << ": {" << p->x << ", " << p->y << "}\n"; }); + + // Output: + // Sun: {1, 1} + // Mercury: {2, 2} + // Venus: {3, 3} + // Earth: {4, 4} + // Moon: {4.1, 4.1} } diff --git a/vendors/flecs/examples/cpp/queries/instancing/src/main.cpp b/vendors/flecs/examples/cpp/queries/instancing/src/main.cpp deleted file mode 100644 index a153d495a..000000000 --- a/vendors/flecs/examples/cpp/queries/instancing/src/main.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -/* Instancing is the ability of queries to iterate results with fields that have - * different numbers of elements. The term "instancing" is borrowed from - * graphics APIs, where it means reusing the same data for multiple "instances". - * - * Query instancing works in much the same way. By default queries match all - * components on the same entity. It is however possible to request data from - * other entities, like getting the Position from the entity's parent. - * - * Instancing refers to the ability of queries to iterate components for - * multiple entities while at the same time providing "instanced" components, - * which are always provided one element at a time. - * - * Instancing is often used in combination with parent-child relationships and - * prefabs, but is applicable to any kind of query where some of the terms are - * matched on N entities, and some on a single entity. - * - * By default queries are not instanced, which means that if a result contains - * mixed fields, entities will be iterated one by one instead of in batches. - * This is safer, as code doesn't have to do anything different for owned and - * shared fields, but does come at a performance penalty. - * - * The each() iterator function always uses an instanced iterator under the - * hood. This is transparent to the application, but improves performance. For - * this reason using each() can be faster than using uninstanced iter(). - */ - -struct Position { - double x, y; -}; - -struct Velocity { - double x, y; -}; - -int main(int, char *[]) { - flecs::world ecs; - - // Create a query for Position, Velocity. We'll create a few entities that - // have Velocity as owned and shared component. - flecs::query q = - ecs.query_builder() - .term_at(1).self() // Position must always be owned by the entity - .instanced() // create instanced query - .build(); - - // Create a prefab with Velocity. Prefabs are not matched with queries. - flecs::entity prefab = ecs.prefab("p") - .set({1, 2}); - - // Create a few entities that own Position & share Velocity from the prefab. - ecs.entity("e1").is_a(prefab) - .set({10, 20}); - - ecs.entity("e2").is_a(prefab) - .set({10, 20}); - - // Create a few entities that own all components - ecs.entity("e3") - .set({10, 20}) - .set({3, 4}); - - ecs.entity("e4") - .set({10, 20}) - .set({4, 5}); - - - // Iterate the instanced query. Note how when a query is instanced, it needs - // to check whether a field is owned or not in order to know how to access - // it. In the case of an owned field it is iterated as an array, whereas - // in the case of a shared field, it is accessed as a pointer. - q.iter([](flecs::iter& it, Position *p, const Velocity *v) { - - // Check if Velocity is owned, in which case it's accessed as array. - // Position will always be owned, since we set the term to Self. - if (it.is_self(2)) { // Velocity is term 2 - std::cout << "Velocity is owned\n"; - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - std::cout << it.entity(i).name() << - ": {" << p[i].x << ", " << p[i].y << "}\n"; - } - - // If Velocity is shared, access the field as a pointer. - } else { - std::cout << "Velocity is shared\n"; - for (auto i : it) { - p[i].x += v->x; - p[i].y += v->y; - std::cout << it.entity(i).name() << - ": {" << p[i].x << ", " << p[i].y << "}\n"; - } - } - }); -} diff --git a/vendors/flecs/examples/cpp/queries/iter/src/main.cpp b/vendors/flecs/examples/cpp/queries/iter/src/main.cpp deleted file mode 100644 index 5427c9373..000000000 --- a/vendors/flecs/examples/cpp/queries/iter/src/main.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -struct Position { - double x, y; -}; - -struct Velocity { - double x, y; -}; - -struct Mass { - double value; -}; - -int main(int, char *[]) { - flecs::world ecs; - - // Create a query for Position, Velocity. - flecs::query q = - ecs.query(); - - // Create a few test entities for a Position, Velocity query - ecs.entity("e1") - .set({10, 20}) - .set({1, 2}); - - ecs.entity("e2") - .set({10, 20}) - .set({3, 4}); - - ecs.entity("e3") - .set({10, 20}) - .set({4, 5}) - .set({50}); - - // The iter function provides a flecs::iter object which contains all sorts - // of information on the entities currently being iterated. - // The function passed to iter is by default called for each table the query - // is matched with. - q.iter([&](flecs::iter& it, Position *p, const Velocity *v) { - // Print the table & number of entities matched in current callback - std::cout << "Table [" << it.type().str() << "]" << std::endl; - std::cout << " - number of entities: " << it.count() << std::endl; - - // Print information about the components being matched - for (int i = 1; i <= it.field_count(); i ++) { - std::cout << " - term " << i << ": " << std::endl; - std::cout << " - component: " << it.id(i).str() << std::endl; - std::cout << " - type size: " << it.size(i) << std::endl; - } - - std::cout << std::endl; - - // Iterate entities - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - std::cout << " - " << it.entity(i).name() << - ": {" << p[i].x << ", " << p[i].y << "}\n"; - } - - std::cout << std::endl; - }); -} diff --git a/vendors/flecs/examples/c/systems/no_readonly/include/no_readonly.h b/vendors/flecs/examples/cpp/queries/iter_targets/include/iter_targets.h similarity index 63% rename from vendors/flecs/examples/c/systems/no_readonly/include/no_readonly.h rename to vendors/flecs/examples/cpp/queries/iter_targets/include/iter_targets.h index 6aee24f3e..aac2db5b8 100644 --- a/vendors/flecs/examples/c/systems/no_readonly/include/no_readonly.h +++ b/vendors/flecs/examples/cpp/queries/iter_targets/include/iter_targets.h @@ -1,8 +1,8 @@ -#ifndef NO_READONLY_H -#define NO_READONLY_H +#ifndef ITER_TARGETS_H +#define ITER_TARGETS_H /* This generated file contains includes for project dependencies */ -#include "no_readonly/bake_config.h" +#include "iter_targets/bake_config.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/examples/c/systems/no_readonly/include/no_readonly/bake_config.h b/vendors/flecs/examples/cpp/queries/iter_targets/include/iter_targets/bake_config.h similarity index 90% rename from vendors/flecs/examples/c/systems/no_readonly/include/no_readonly/bake_config.h rename to vendors/flecs/examples/cpp/queries/iter_targets/include/iter_targets/bake_config.h index 8e0ab56ea..12785a4f0 100644 --- a/vendors/flecs/examples/c/systems/no_readonly/include/no_readonly/bake_config.h +++ b/vendors/flecs/examples/cpp/queries/iter_targets/include/iter_targets/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef NO_READONLY_BAKE_CONFIG_H -#define NO_READONLY_BAKE_CONFIG_H +#ifndef ITER_TARGETS_BAKE_CONFIG_H +#define ITER_TARGETS_BAKE_CONFIG_H /* Headers of public dependencies */ #include diff --git a/vendors/flecs/examples/cpp/queries/iter_targets/project.json b/vendors/flecs/examples/cpp/queries/iter_targets/project.json new file mode 100644 index 000000000..32c811744 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/iter_targets/project.json @@ -0,0 +1,11 @@ +{ + "id": "iter_targets", + "type": "application", + "value": { + "use": [ + "flecs" + ], + "public": false, + "language": "c++" + } +} \ No newline at end of file diff --git a/vendors/flecs/examples/cpp/queries/iter_targets/src/main.cpp b/vendors/flecs/examples/cpp/queries/iter_targets/src/main.cpp new file mode 100644 index 000000000..2523d5ea9 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/iter_targets/src/main.cpp @@ -0,0 +1,43 @@ +#include +#include + +// This example shows how to iterate matching targets of a relationship without +// iterating the same entity multiple times. +// +// When creating a (Relationship, *) query, the query will return one result per +// matching relationship pair, which returns the same entity multiple times. +// This example uses a (Relationship, _) query which at most returns a single +// result per matching pair, and a targets() function that iterates the targets +// for the current entity. + +struct Eats { }; +struct Pizza { }; +struct Salad { }; + +int main(int, char *[]) { + flecs::world ecs; + + ecs.entity("Bob") + .add() + .add(); + + ecs.query_builder() + .with(flecs::Any) // flecs::Any ensures that only a single result + // is returned per entity, as opposed to + // flecs::Wildcard which returns a result per + // matched pair. + + .each([](flecs::iter& it, size_t row) { + flecs::entity e = it.entity(row); + std::cout << e.name() << " eats: \n"; + + it.targets(0, [](flecs::entity tgt) { + std::cout << " - " << tgt.name() << "\n"; + }); + }); + + // Output: + // Bob eats: + // - Pizza + // - Salad +} diff --git a/vendors/flecs/examples/cpp/queries/run/BUILD b/vendors/flecs/examples/cpp/queries/run/BUILD new file mode 100644 index 000000000..efe0c8b0f --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/run/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "run", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/iter/include/iter.h b/vendors/flecs/examples/cpp/queries/run/include/run.h similarity index 85% rename from vendors/flecs/examples/cpp/queries/iter/include/iter.h rename to vendors/flecs/examples/cpp/queries/run/include/run.h index 3e829472e..9f5d106b1 100644 --- a/vendors/flecs/examples/cpp/queries/iter/include/iter.h +++ b/vendors/flecs/examples/cpp/queries/run/include/run.h @@ -2,7 +2,7 @@ #define ITER_H /* This generated file contains includes for project dependencies */ -#include "iter/bake_config.h" +#include "run/bake_config.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/examples/cpp/queries/iter/include/iter/bake_config.h b/vendors/flecs/examples/cpp/queries/run/include/run/bake_config.h similarity index 92% rename from vendors/flecs/examples/cpp/queries/iter/include/iter/bake_config.h rename to vendors/flecs/examples/cpp/queries/run/include/run/bake_config.h index 4852734b6..5df9cd07a 100644 --- a/vendors/flecs/examples/cpp/queries/iter/include/iter/bake_config.h +++ b/vendors/flecs/examples/cpp/queries/run/include/run/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef ITER_BAKE_CONFIG_H -#define ITER_BAKE_CONFIG_H +#ifndef RUN_BAKE_CONFIG_H +#define RUN_BAKE_CONFIG_H /* Headers of public dependencies */ #include diff --git a/vendors/flecs/examples/cpp/queries/iter/project.json b/vendors/flecs/examples/cpp/queries/run/project.json similarity index 89% rename from vendors/flecs/examples/cpp/queries/iter/project.json rename to vendors/flecs/examples/cpp/queries/run/project.json index 4be968041..ba89e84bd 100644 --- a/vendors/flecs/examples/cpp/queries/iter/project.json +++ b/vendors/flecs/examples/cpp/queries/run/project.json @@ -1,5 +1,5 @@ { - "id": "iter", + "id": "run", "type": "application", "value": { "use": [ diff --git a/vendors/flecs/examples/cpp/queries/run/src/main.cpp b/vendors/flecs/examples/cpp/queries/run/src/main.cpp new file mode 100644 index 000000000..7897b8dd6 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/run/src/main.cpp @@ -0,0 +1,68 @@ +#include +#include + +struct Position { + double x, y; +}; + +struct Velocity { + double x, y; +}; + +struct Mass { + double value; +}; + +int main(int, char *[]) { + flecs::world ecs; + + // Create a query for Position, Velocity. + flecs::query q = + ecs.query(); + + // Create a few test entities for a Position, Velocity query + ecs.entity("e1") + .set({10, 20}) + .set({1, 2}); + + ecs.entity("e2") + .set({10, 20}) + .set({3, 4}); + + ecs.entity("e3") + .set({10, 20}) + .set({4, 5}) + .set({50}); + + // The run() function provides a flecs::iter object which contains all sorts + // of information on the entities currently being iterated. + q.run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + // Print the table & number of entities matched in current callback + std::cout << "Table [" << it.type().str() << "]" << std::endl; + std::cout << " - number of entities: " << it.count() << std::endl; + + // Print information about the components being matched + for (int8_t i = 0; i < it.field_count(); i ++) { + std::cout << " - term " << i << ": " << std::endl; + std::cout << " - component: " << it.id(i).str() << std::endl; + std::cout << " - type size: " << it.size(i) << std::endl; + } + + std::cout << std::endl; + + // Iterate entities + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + std::cout << " - " << it.entity(i).name() << + ": {" << p[i].x << ", " << p[i].y << "}\n"; + } + + std::cout << std::endl; + } + }); +} diff --git a/vendors/flecs/examples/cpp/queries/setting_variables/BUILD b/vendors/flecs/examples/cpp/queries/setting_variables/BUILD new file mode 100644 index 000000000..4c0da4f33 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/setting_variables/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "setting_variables", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/rules/setting_variables/include/setting_variables.h b/vendors/flecs/examples/cpp/queries/setting_variables/include/setting_variables.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/setting_variables/include/setting_variables.h rename to vendors/flecs/examples/cpp/queries/setting_variables/include/setting_variables.h diff --git a/vendors/flecs/examples/cpp/rules/setting_variables/include/setting_variables/bake_config.h b/vendors/flecs/examples/cpp/queries/setting_variables/include/setting_variables/bake_config.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/setting_variables/include/setting_variables/bake_config.h rename to vendors/flecs/examples/cpp/queries/setting_variables/include/setting_variables/bake_config.h diff --git a/vendors/flecs/examples/cpp/rules/setting_variables/project.json b/vendors/flecs/examples/cpp/queries/setting_variables/project.json similarity index 100% rename from vendors/flecs/examples/cpp/rules/setting_variables/project.json rename to vendors/flecs/examples/cpp/queries/setting_variables/project.json diff --git a/vendors/flecs/examples/cpp/rules/setting_variables/src/main.cpp b/vendors/flecs/examples/cpp/queries/setting_variables/src/main.cpp similarity index 79% rename from vendors/flecs/examples/cpp/rules/setting_variables/src/main.cpp rename to vendors/flecs/examples/cpp/queries/setting_variables/src/main.cpp index b33fe7129..21d7d85de 100644 --- a/vendors/flecs/examples/cpp/rules/setting_variables/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/setting_variables/src/main.cpp @@ -2,7 +2,7 @@ #include // This example extends the component_inheritance example, and shows how -// we can use a single rule to match units from different players and platoons +// we can use a single query to match units from different players and platoons // by setting query variables before we iterate. // // The units in this example belong to a platoon, with the platoons belonging @@ -63,38 +63,36 @@ int main(int, char *[]) { } } - // Create a rule to find all RangedUnits for a platoon/player. The + // Create a query to find all RangedUnits for a platoon/player. The // equivalent query in the query DSL would look like this: - // (Platoon, $Platoon), Player($Platoon, $Player) + // (Platoon, $platoon), Player($platoon, $player) // // The way to read how this query is evaluated is: - // - find all entities with (Platoon, *), store * in _Platoon - // - check if _Platoon has (Player, *), store * in _Player - flecs::rule r = ecs.rule_builder() - .with().second("$Platoon") - .with("$Player").src("$Platoon") + // - find all entities with (Platoon, *), store * in $platoon + // - check if _Platoon has (Player, *), store * in $player + flecs::query q = ecs.query_builder() + .with("$platoon") + .with("$player").src("$platoon") .build(); - // If we would iterate this rule it would return all ranged units for all + // If we would iterate this query it would return all ranged units for all // platoons & for all players. We can limit the results to just a single // platoon or a single player setting a variable beforehand. In this example // we'll just find all platoons & ranged units for a single player. - int player_var = r.find_var("Player"); - int platoon_var = r.find_var("Platoon"); + int player_var = q.find_var("player"); + int platoon_var = q.find_var("platoon"); - // Iterate rule, limit the results to units of MyPlayer - r.iter().set_var(player_var, ecs.lookup("MyPlayer")) + // Iterate query, limit the results to units of MyPlayer + q.set_var(player_var, ecs.lookup("MyPlayer")) .each([&](flecs::iter& it, size_t index, RangedUnit) { flecs::entity unit = it.entity(index); std::cout << "Unit " << unit.path() << " of class " - << it.id(1).str() << " in platoon " + << it.id(0).str() << " in platoon " << it.get_var(platoon_var).path() << " for player " << it.get_var(player_var).path() << "\n"; }); - r.destruct(); - // Output: // Unit ::499 of class Wizard in platoon ::496 for player ::MyPlayer // Unit ::503 of class Wizard in platoon ::500 for player ::MyPlayer diff --git a/vendors/flecs/examples/cpp/queries/singleton/BUILD b/vendors/flecs/examples/cpp/queries/singleton/BUILD new file mode 100644 index 000000000..727f63c80 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/singleton/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "singleton", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/singleton/src/main.cpp b/vendors/flecs/examples/cpp/queries/singleton/src/main.cpp index 0746458d0..84d893e11 100644 --- a/vendors/flecs/examples/cpp/queries/singleton/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/singleton/src/main.cpp @@ -28,7 +28,7 @@ int main(int, char *[]) { // Create query that matches Gravity as singleton flecs::query q = world.query_builder() - .term_at(2).singleton() + .term_at(1).singleton() .build(); // In a query string expression you can use the $ shortcut for singletons: diff --git a/vendors/flecs/examples/cpp/queries/sorting/BUILD b/vendors/flecs/examples/cpp/queries/sorting/BUILD new file mode 100644 index 000000000..ae11ec50f --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/sorting/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "sorting", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/transitive_queries/BUILD b/vendors/flecs/examples/cpp/queries/transitive_queries/BUILD new file mode 100644 index 000000000..590d3856d --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/transitive_queries/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "transitive_queries", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/rules/transitive_queries/include/transitive_queries.h b/vendors/flecs/examples/cpp/queries/transitive_queries/include/transitive_queries.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/transitive_queries/include/transitive_queries.h rename to vendors/flecs/examples/cpp/queries/transitive_queries/include/transitive_queries.h diff --git a/vendors/flecs/examples/cpp/rules/transitive_queries/include/transitive_queries/bake_config.h b/vendors/flecs/examples/cpp/queries/transitive_queries/include/transitive_queries/bake_config.h similarity index 100% rename from vendors/flecs/examples/cpp/rules/transitive_queries/include/transitive_queries/bake_config.h rename to vendors/flecs/examples/cpp/queries/transitive_queries/include/transitive_queries/bake_config.h diff --git a/vendors/flecs/examples/cpp/rules/transitive_queries/project.json b/vendors/flecs/examples/cpp/queries/transitive_queries/project.json similarity index 100% rename from vendors/flecs/examples/cpp/rules/transitive_queries/project.json rename to vendors/flecs/examples/cpp/queries/transitive_queries/project.json diff --git a/vendors/flecs/examples/cpp/rules/transitive_queries/src/main.cpp b/vendors/flecs/examples/cpp/queries/transitive_queries/src/main.cpp similarity index 88% rename from vendors/flecs/examples/cpp/rules/transitive_queries/src/main.cpp rename to vendors/flecs/examples/cpp/queries/transitive_queries/src/main.cpp index 58437443e..5ea522837 100644 --- a/vendors/flecs/examples/cpp/rules/transitive_queries/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/transitive_queries/src/main.cpp @@ -91,32 +91,29 @@ int main(int, char *[]) { // Create a query that finds the countries persons live in. Note that these // have not been explicitly added to the Person entities, but because the - // LocatedIn is transitive, the rule engine will traverse the relationship + // LocatedIn is transitive, the query engine will traverse the relationship // until it found something that is a country. // // The equivalent of this query in the DSL is: - // Person, (LocatedIn, $Location), Country($Location) - flecs::rule<> r = ecs.rule_builder() + // Person, (LocatedIn, $location), Country($location) + flecs::query<> q = ecs.query_builder() .with() - .with("$Location") - .with().src("$Location") + .with("$location") + .with().src("$location") .build(); // Lookup the index of the variable. This will let us quickly lookup its // value while we're iterating. - int location_var = r.find_var("Location"); + int location_var = q.find_var("location"); - // Iterate the rule - r.each([&](flecs::iter& it, size_t index) { + // Iterate the query + q.each([&](flecs::iter& it, size_t index) { std::cout << it.entity(index).name() << " lives in " << it.get_var(location_var).name() << "\n"; }); - // Rules need to be explicitly deleted. - r.destruct(); - // Output: // Bob lives in UnitedStates // Alice lives in UnitedStates diff --git a/vendors/flecs/examples/cpp/queries/variables/BUILD b/vendors/flecs/examples/cpp/queries/variables/BUILD new file mode 100644 index 000000000..4549426d2 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/variables/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "variables", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/variables/include/variables.h b/vendors/flecs/examples/cpp/queries/variables/include/variables.h new file mode 100644 index 000000000..7d3167254 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/variables/include/variables.h @@ -0,0 +1,16 @@ +#ifndef VARIABLES_H +#define VARIABLES_H + +/* This generated file contains includes for project dependencies */ +#include "variables/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/examples/cpp/queries/variables/include/variables/bake_config.h b/vendors/flecs/examples/cpp/queries/variables/include/variables/bake_config.h new file mode 100644 index 000000000..f4997a774 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/variables/include/variables/bake_config.h @@ -0,0 +1,24 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef VARIABLES_BAKE_CONFIG_H +#define VARIABLES_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include + +#endif + diff --git a/vendors/flecs/examples/cpp/rules/basics/project.json b/vendors/flecs/examples/cpp/queries/variables/project.json similarity index 86% rename from vendors/flecs/examples/cpp/rules/basics/project.json rename to vendors/flecs/examples/cpp/queries/variables/project.json index 12fa5ded8..665330200 100644 --- a/vendors/flecs/examples/cpp/rules/basics/project.json +++ b/vendors/flecs/examples/cpp/queries/variables/project.json @@ -1,5 +1,5 @@ { - "id": "basics", + "id": "variables", "type": "application", "value": { "use": [ diff --git a/vendors/flecs/examples/cpp/rules/basics/src/main.cpp b/vendors/flecs/examples/cpp/queries/variables/src/main.cpp similarity index 63% rename from vendors/flecs/examples/cpp/rules/basics/src/main.cpp rename to vendors/flecs/examples/cpp/queries/variables/src/main.cpp index df065589d..8ace3537e 100644 --- a/vendors/flecs/examples/cpp/rules/basics/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/variables/src/main.cpp @@ -1,4 +1,4 @@ -#include +#include #include struct Eats { }; @@ -23,13 +23,10 @@ int main(int, char *[]) { .add(Chocolate) .add(Apples); - // Here we're creating a rule that in the query DSL would look like this: - // Eats($This, $Food), Healthy($Food) - // - // Rules are similar to queries, but support more advanced features. This - // example shows how the basics of how to use rules & variables. - flecs::rule<> r = ecs.rule_builder() - // Identifiers that start with _ are query variables. Query variables + // Here we're creating a query that in the query DSL would look like this: + // Eats($this, $Food), Healthy($Food) + flecs::query<> q = ecs.query_builder() + // Identifiers that start with $ are query variables. Query variables // are like wildcards, but enforce that the entity substituted by the // wildcard is the same across terms. // @@ -37,27 +34,24 @@ int main(int, char *[]) { // substituted by the * for Eats is the same as for Healthy: // (Eats, *), Healthy(*) // - // By replacing * with _Food, both terms are constrained to use the - // same entity. - .with("$Food") - .with().src("$Food") + // By replacing * with $food, both terms will be constrained to + // match the same entity for the wildcard. + .with("$food") + .with().src("$food") .build(); // Lookup the index of the variable. This will let us quickly lookup its // value while we're iterating. - int food_var = r.find_var("Food"); + int food_var = q.find_var("food"); - // Iterate the rule - r.each([&](flecs::iter& it, size_t index) { + // Iterate the query + q.each([&](flecs::iter& it, size_t index) { std::cout << it.entity(index).name() << " eats " << it.get_var(food_var).name() << "\n"; }); - // Rules need to be explicitly deleted. - r.destruct(); - // Output: // Bob eats Apples // Alice eats Apples diff --git a/vendors/flecs/examples/cpp/queries/wildcards/BUILD b/vendors/flecs/examples/cpp/queries/wildcards/BUILD new file mode 100644 index 000000000..bb8109511 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/wildcards/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "wildcards", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/wildcards/src/main.cpp b/vendors/flecs/examples/cpp/queries/wildcards/src/main.cpp index 7a767980f..a37801ed7 100644 --- a/vendors/flecs/examples/cpp/queries/wildcards/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/wildcards/src/main.cpp @@ -16,7 +16,7 @@ int main(int, char *[]) { // Create a query that matches edible components flecs::query q = ecs.query_builder() - .term_at(1).second(flecs::Wildcard) // Change first argument to (Eats, *) + .term_at(0).second(flecs::Wildcard) // Change first argument to (Eats, *) .build(); // Create a few entities that match the query @@ -31,7 +31,7 @@ int main(int, char *[]) { // the pair that we are currently matched with. q.each([](flecs::iter& it, size_t index, Eats& eats) { flecs::entity e = it.entity(index); - flecs::entity food = it.pair(1).second(); + flecs::entity food = it.pair(0).second(); std::cout << e.name() << " eats " << eats.amount << " " << food.name() << std::endl; diff --git a/vendors/flecs/examples/cpp/queries/with/BUILD b/vendors/flecs/examples/cpp/queries/with/BUILD new file mode 100644 index 000000000..a8ea97681 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/with/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "with", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/without/BUILD b/vendors/flecs/examples/cpp/queries/without/BUILD new file mode 100644 index 000000000..109936b77 --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/without/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "without", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/world_query/BUILD b/vendors/flecs/examples/cpp/queries/world_query/BUILD new file mode 100644 index 000000000..feb58a1da --- /dev/null +++ b/vendors/flecs/examples/cpp/queries/world_query/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "world_query", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/queries/world_query/src/main.cpp b/vendors/flecs/examples/cpp/queries/world_query/src/main.cpp index d7574d71f..3f0a9eba9 100644 --- a/vendors/flecs/examples/cpp/queries/world_query/src/main.cpp +++ b/vendors/flecs/examples/cpp/queries/world_query/src/main.cpp @@ -25,10 +25,7 @@ int main(int, char *[]) { ecs.entity("e3") .set({10, 20}); - // Ad hoc queries are bit slower to iterate than flecs::query, but are - // faster to create, and in most cases require no allocations. Under the - // hood this API uses flecs::filter, which can be used directly for more - // complex queries. + // world::each is a quick way to run simple component queries. ecs.each([](flecs::entity e, Position& p, Velocity& v) { p.x += v.x; p.y += v.y; diff --git a/vendors/flecs/examples/cpp/reflection/basics/BUILD b/vendors/flecs/examples/cpp/reflection/basics/BUILD new file mode 100644 index 000000000..637002bd6 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/basics_bitmask/BUILD b/vendors/flecs/examples/cpp/reflection/basics_bitmask/BUILD new file mode 100644 index 000000000..9e1e43f22 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/basics_bitmask/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics_bitmask", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/basics_bitmask/src/main.cpp b/vendors/flecs/examples/cpp/reflection/basics_bitmask/src/main.cpp index 35c5f289f..deee8e887 100644 --- a/vendors/flecs/examples/cpp/reflection/basics_bitmask/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/basics_bitmask/src/main.cpp @@ -23,11 +23,11 @@ int main(int, char *[]) { ecs.component() .member("toppings"); - // Create entity with Position as usual + // Create entity with Sandwich as usual flecs::entity e = ecs.entity() .set({Toppings::Bacon | Toppings::Lettuce}); - // Convert position component to flecs expression string + // Convert Sandwich component to flecs expression string const Sandwich *ptr = e.get(); std::cout << ecs.to_expr(ptr).c_str() << "\n"; // {toppings: Lettuce|Bacon} } diff --git a/vendors/flecs/examples/cpp/reflection/basics_deserialize/BUILD b/vendors/flecs/examples/cpp/reflection/basics_deserialize/BUILD new file mode 100644 index 000000000..936d46fbf --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/basics_deserialize/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics_deserialize", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/basics_deserialize/src/main.cpp b/vendors/flecs/examples/cpp/reflection/basics_deserialize/src/main.cpp index 71c48fe5d..5ba2fe6fa 100644 --- a/vendors/flecs/examples/cpp/reflection/basics_deserialize/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/basics_deserialize/src/main.cpp @@ -16,19 +16,19 @@ int main(int, char *[]) { // Create entity, set value of position using reflection API flecs::entity e = ecs.entity(); - Position *ptr = e.get_mut(); + Position& ptr = e.ensure(); - flecs::cursor cur = ecs.cursor(ptr); + flecs::cursor cur = ecs.cursor(&ptr); cur.push(); // { cur.set_float(10.0); // 10 cur.next(); // , cur.set_float(20.0); // 20 cur.pop(); // } - std::cout << ecs.to_expr(ptr).c_str() << "\n"; // {x: 10.00, y: 20.00} + std::cout << ecs.to_expr(&ptr).c_str() << "\n"; // {x: 10.00, y: 20.00} // Use member names before assigning values - cur = ecs.cursor(ptr); + cur = ecs.cursor(&ptr); cur.push(); // { cur.member("y"); // y: cur.set_float(10); // 10 @@ -36,5 +36,5 @@ int main(int, char *[]) { cur.set_float(20); // 20 cur.pop(); // } - std::cout << ecs.to_expr(ptr).c_str() << "\n"; // {x: 20.00, y: 10.00} + std::cout << ecs.to_expr(&ptr).c_str() << "\n"; // {x: 20.00, y: 10.00} } diff --git a/vendors/flecs/examples/cpp/reflection/basics_enum/BUILD b/vendors/flecs/examples/cpp/reflection/basics_enum/BUILD new file mode 100644 index 000000000..fe9f86e70 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/basics_enum/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics_enum", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/basics_enum/src/main.cpp b/vendors/flecs/examples/cpp/reflection/basics_enum/src/main.cpp index 7c44ce4a8..fac84c78d 100644 --- a/vendors/flecs/examples/cpp/reflection/basics_enum/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/basics_enum/src/main.cpp @@ -23,11 +23,11 @@ int main(int, char *[]) { ecs.component() .member("color"); - // Create entity with Position as usual + // Create entity with TypeWithEnum as usual flecs::entity e = ecs.entity() .set({Green}); - // Convert position component to flecs expression string + // Convert TypeWithEnum component to flecs expression string const TypeWithEnum *ptr = e.get(); std::cout << ecs.to_expr(ptr).c_str() << "\n"; // {color: Green} } diff --git a/vendors/flecs/examples/cpp/reflection/basics_json/BUILD b/vendors/flecs/examples/cpp/reflection/basics_json/BUILD new file mode 100644 index 000000000..9f1ea3161 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/basics_json/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics_json", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/basics_json/src/main.cpp b/vendors/flecs/examples/cpp/reflection/basics_json/src/main.cpp index 3a1c5a690..f86655389 100644 --- a/vendors/flecs/examples/cpp/reflection/basics_json/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/basics_json/src/main.cpp @@ -23,10 +23,7 @@ int main(int, char *[]) { std::cout << ecs.to_json(ptr) << "\n"; // {"x":10, "y":20} // Convert entity to JSON - flecs::entity_to_json_desc_t desc; - desc.serialize_path = true; - desc.serialize_values = true; - std::cout << e.to_json(&desc) << "\n"; + std::cout << e.to_json() << "\n"; // { // "path":"ent", diff --git a/vendors/flecs/examples/cpp/reflection/entity_type/BUILD b/vendors/flecs/examples/cpp/reflection/entity_type/BUILD new file mode 100644 index 000000000..607a0b48a --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/entity_type/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "entity_type", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/member_ranges/BUILD b/vendors/flecs/examples/cpp/reflection/member_ranges/BUILD new file mode 100644 index 000000000..376621cb4 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/member_ranges/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "member_ranges", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/member_ranges/src/main.cpp b/vendors/flecs/examples/cpp/reflection/member_ranges/src/main.cpp index 1d4c8939c..01388a694 100644 --- a/vendors/flecs/examples/cpp/reflection/member_ranges/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/member_ranges/src/main.cpp @@ -11,14 +11,15 @@ int main() { ecs.component() .member("value") .range(0.0, 100.0) // Specifics values that the member can assume - .warning_range(0.0, 60.0) // Values outside this range are considerd a warning - .error_range(0.0, 80.0); // Values outside this range are considerd an error + .warning_range(0.0, 60.0) // Values outside this range are considered a warning + .error_range(0.0, 80.0); // Values outside this range are considered an error ecs.entity("MachineA").set({ 50.0 }); ecs.entity("MachineB").set({ 75.0 }); ecs.entity("MachineC").set({ 90.0 }); - // Open https://www.flecs.dev/explorer?show=query&query=CpuUtilization to - // see how ranges affect visualization. - ecs.app().enable_rest().run(); + // Uncomment this line and open + // https://www.flecs.dev/explorer?show=query&query=CpuUtilization + // to see how ranges affect visualization: + // ecs.app().enable_rest().run(); } diff --git a/vendors/flecs/examples/cpp/reflection/nested_set_member/BUILD b/vendors/flecs/examples/cpp/reflection/nested_set_member/BUILD new file mode 100644 index 000000000..392e46d0c --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/nested_set_member/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "nested_set_member", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/nested_set_member/src/main.cpp b/vendors/flecs/examples/cpp/reflection/nested_set_member/src/main.cpp index 877bce504..c28f47024 100644 --- a/vendors/flecs/examples/cpp/reflection/nested_set_member/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/nested_set_member/src/main.cpp @@ -24,9 +24,9 @@ int main(int, char *[]) { // Create entity, set value of Line using reflection API flecs::entity e = ecs.entity(); - Line *ptr = e.get_mut(); + Line& ptr = e.ensure(); - flecs::cursor cur = ecs.cursor(ptr); + flecs::cursor cur = ecs.cursor(&ptr); cur.push(); // { cur.member("start"); // start: cur.push(); // { @@ -45,6 +45,6 @@ int main(int, char *[]) { cur.pop(); // } // Convert component to string - std::cout << ecs.to_expr(ptr).c_str() << "\n"; + std::cout << ecs.to_expr(&ptr).c_str() << "\n"; // {start: {x: 10.00, y: 20.00}, stop: {x: 30.00, y: 40.00}} } diff --git a/vendors/flecs/examples/cpp/reflection/nested_struct/BUILD b/vendors/flecs/examples/cpp/reflection/nested_struct/BUILD new file mode 100644 index 000000000..93be6a101 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/nested_struct/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "nested_struct", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/nested_struct/src/main.cpp b/vendors/flecs/examples/cpp/reflection/nested_struct/src/main.cpp index ff150d71b..69f7e3bf5 100644 --- a/vendors/flecs/examples/cpp/reflection/nested_struct/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/nested_struct/src/main.cpp @@ -23,11 +23,11 @@ int main(int, char *[]) { .member("start") .member("stop"); - // Create entity with Position as usual + // Create entity with Line as usual flecs::entity e = ecs.entity() .set({{10, 20}, {30, 40}}); - // Convert position component to flecs expression string + // Convert Line component to flecs expression string const Line *ptr = e.get(); std::cout << ecs.to_expr(ptr).c_str() << std::endl; // {start: {x: 10.00, y: 20.00}, stop: {x: 30.00, y: 40.00}} diff --git a/vendors/flecs/examples/cpp/reflection/portable_type/BUILD b/vendors/flecs/examples/cpp/reflection/portable_type/BUILD new file mode 100644 index 000000000..0126ac288 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/portable_type/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "portable_type", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/query_to_custom_json/BUILD b/vendors/flecs/examples/cpp/reflection/query_to_custom_json/BUILD new file mode 100644 index 000000000..c61db125e --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/query_to_custom_json/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "query_to_custom_json", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/query_to_custom_json/src/main.cpp b/vendors/flecs/examples/cpp/reflection/query_to_custom_json/src/main.cpp index 63fd4ed7a..de186132a 100644 --- a/vendors/flecs/examples/cpp/reflection/query_to_custom_json/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/query_to_custom_json/src/main.cpp @@ -44,49 +44,81 @@ int main(int, char *[]) { // Serialize query to JSON. Customize serializer to only serialize entity // names and component values. flecs::iter_to_json_desc_t desc = {}; - desc.serialize_entities = true; desc.serialize_values = true; std::cout << q.iter().to_json(&desc).c_str() << "\n"; // Iterator returns 2 sets of results, one for each table. // { - // "results": [{ - // "entities": ["a", "b"], - // "values": [ - // [{ - // "x": 10.00, - // "y": 20.00 - // }, { - // "x": 20.00, - // "y": 30.00 - // }], - // [{ - // "x": 1.00, - // "y": 2.00 - // }, { - // "x": 2.00, - // "y": 3.00 - // }] - // ] - // }, { - // "entities": ["c", "d"], - // "values": [ - // [{ - // "x": 30.00, - // "y": 40.00 - // }, { - // "x": 30.00, - // "y": 40.00 - // }], - // [{ - // "x": 3.00, - // "y": 4.00 - // }, { - // "x": 4.00, - // "y": 5.00 - // }] - // ] - // }] + // "results": [ + // { + // "name": "a", + // "fields": [ + // { + // "data": { + // "x": 10, + // "y": 20 + // } + // }, + // { + // "data": { + // "x": 1, + // "y": 2 + // } + // } + // ] + // }, + // { + // "name": "b", + // "fields": [ + // { + // "data": { + // "x": 20, + // "y": 30 + // } + // }, + // { + // "data": { + // "x": 2, + // "y": 3 + // } + // } + // ] + // }, + // { + // "name": "c", + // "fields": [ + // { + // "data": { + // "x": 30, + // "y": 40 + // } + // }, + // { + // "data": { + // "x": 3, + // "y": 4 + // } + // } + // ] + // }, + // { + // "name": "d", + // "fields": [ + // { + // "data": { + // "x": 30, + // "y": 40 + // } + // }, + // { + // "data": { + // "x": 4, + // "y": 5 + // } + // } + // ] + // } + // ] // } } diff --git a/vendors/flecs/examples/cpp/reflection/query_to_json/BUILD b/vendors/flecs/examples/cpp/reflection/query_to_json/BUILD new file mode 100644 index 000000000..a1e8f2872 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/query_to_json/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "query_to_json", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/query_to_json/src/main.cpp b/vendors/flecs/examples/cpp/reflection/query_to_json/src/main.cpp index 4b73eed2d..2bac9d060 100644 --- a/vendors/flecs/examples/cpp/reflection/query_to_json/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/query_to_json/src/main.cpp @@ -39,8 +39,7 @@ int main(int, char *[]) { flecs::query q = ecs.query(); - // Serialize query to JSON. Note that this works for any iterable object, - // including filters & rules. + // Serialize query to JSON. Note that this works for any iterable object. std::cout << q.iter().to_json().c_str() << "\n"; // Iterator returns 2 sets of results, one for each table. diff --git a/vendors/flecs/examples/cpp/reflection/runtime_component/BUILD b/vendors/flecs/examples/cpp/reflection/runtime_component/BUILD new file mode 100644 index 000000000..1c80412a2 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/runtime_component/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "runtime_component", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/runtime_component/src/main.cpp b/vendors/flecs/examples/cpp/reflection/runtime_component/src/main.cpp index cfd8a0c15..3b8f16598 100644 --- a/vendors/flecs/examples/cpp/reflection/runtime_component/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/runtime_component/src/main.cpp @@ -11,7 +11,7 @@ int main(int, char *[]) { // Create entity, set value of position using reflection API flecs::entity e = ecs.entity(); - void *ptr = e.get_mut(position); + void *ptr = e.ensure(position); flecs::cursor cur = ecs.cursor(position, ptr); cur.push(); diff --git a/vendors/flecs/examples/cpp/reflection/runtime_nested_component/BUILD b/vendors/flecs/examples/cpp/reflection/runtime_nested_component/BUILD new file mode 100644 index 000000000..fbd0862a8 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/runtime_nested_component/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "runtime_nested_component", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/runtime_nested_component/src/main.cpp b/vendors/flecs/examples/cpp/reflection/runtime_nested_component/src/main.cpp index 9e41c41a3..8778782a7 100644 --- a/vendors/flecs/examples/cpp/reflection/runtime_nested_component/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/runtime_nested_component/src/main.cpp @@ -15,7 +15,7 @@ int main(int, char *[]) { // Create entity, set value of position using reflection API flecs::entity e = ecs.entity(); - void *ptr = e.get_mut(line); + void *ptr = e.ensure(line); flecs::cursor cur = ecs.cursor(line, ptr); cur.push(); // { diff --git a/vendors/flecs/examples/cpp/reflection/ser_opaque_type/BUILD b/vendors/flecs/examples/cpp/reflection/ser_opaque_type/BUILD new file mode 100644 index 000000000..8f56bc46e --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/ser_opaque_type/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "ser_opaque_type", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/ser_std_string/BUILD b/vendors/flecs/examples/cpp/reflection/ser_std_string/BUILD new file mode 100644 index 000000000..57ff6de44 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/ser_std_string/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "ser_std_string", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/ser_std_vector/BUILD b/vendors/flecs/examples/cpp/reflection/ser_std_vector/BUILD new file mode 100644 index 000000000..a26ed915c --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/ser_std_vector/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "ser_std_vector", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/units/BUILD b/vendors/flecs/examples/cpp/reflection/units/BUILD new file mode 100644 index 000000000..421b50ce9 --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/units/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "units", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/reflection/units/src/main.cpp b/vendors/flecs/examples/cpp/reflection/units/src/main.cpp index b3b2b9d7a..64f0b508f 100644 --- a/vendors/flecs/examples/cpp/reflection/units/src/main.cpp +++ b/vendors/flecs/examples/cpp/reflection/units/src/main.cpp @@ -33,8 +33,8 @@ int main(int, char *[]) { flecs::entity e = ecs.entity().set({24, 1.2, 0.5}); // Use cursor API to print values with units - WeatherStation *ptr = e.get_mut(); - flecs::cursor cur = ecs.cursor(ptr); + WeatherStation& ptr = e.ensure(); + flecs::cursor cur = ecs.cursor(&ptr); cur.push(); print_value(cur); cur.next(); diff --git a/vendors/flecs/examples/cpp/reflection/world_ser_deser/BUILD b/vendors/flecs/examples/cpp/reflection/world_ser_deser/BUILD new file mode 100644 index 000000000..5bb28b11d --- /dev/null +++ b/vendors/flecs/examples/cpp/reflection/world_ser_deser/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "world_ser_deser", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/relationships/basics/BUILD b/vendors/flecs/examples/cpp/relationships/basics/BUILD new file mode 100644 index 000000000..637002bd6 --- /dev/null +++ b/vendors/flecs/examples/cpp/relationships/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/relationships/enum_relations/BUILD b/vendors/flecs/examples/cpp/relationships/enum_relations/BUILD new file mode 100644 index 000000000..06626b953 --- /dev/null +++ b/vendors/flecs/examples/cpp/relationships/enum_relations/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "enum_relations", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/relationships/enum_relations/src/main.cpp b/vendors/flecs/examples/cpp/relationships/enum_relations/src/main.cpp index b65cbf289..97073eeb7 100644 --- a/vendors/flecs/examples/cpp/relationships/enum_relations/src/main.cpp +++ b/vendors/flecs/examples/cpp/relationships/enum_relations/src/main.cpp @@ -58,11 +58,11 @@ int main(int, char *[]) { ecs.entity().add(Tile::Sand).add(TileStatus::Occupied); // Iterate all entities with a Tile relationship - ecs.filter_builder() + ecs.query_builder() .with(flecs::Wildcard) .build() .each([&](flecs::iter& it, size_t) { - flecs::entity tile_constant = it.pair(1).second(); + flecs::entity tile_constant = it.pair(0).second(); printf("%s\n", tile_constant.path().c_str()); }); @@ -72,12 +72,12 @@ int main(int, char *[]) { // ::Tile::Sand // Iterate only occupied tiles - ecs.filter_builder() + ecs.query_builder() .with(flecs::Wildcard) .with(TileStatus::Occupied) .build() .each([&](flecs::iter& it, size_t) { - flecs::entity tile_constant = it.pair(1).second(); + flecs::entity tile_constant = it.pair(0).second(); printf("%s\n", tile_constant.path().c_str()); }); diff --git a/vendors/flecs/examples/cpp/relationships/exclusive_relations/BUILD b/vendors/flecs/examples/cpp/relationships/exclusive_relations/BUILD new file mode 100644 index 000000000..45e9a3bd9 --- /dev/null +++ b/vendors/flecs/examples/cpp/relationships/exclusive_relations/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "exclusive_relations", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/relationships/relation_component/BUILD b/vendors/flecs/examples/cpp/relationships/relation_component/BUILD new file mode 100644 index 000000000..54d2dbb4e --- /dev/null +++ b/vendors/flecs/examples/cpp/relationships/relation_component/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "relation_component", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/relationships/relation_component/src/main.cpp b/vendors/flecs/examples/cpp/relationships/relation_component/src/main.cpp index 73d61c044..c5ecc5960 100644 --- a/vendors/flecs/examples/cpp/relationships/relation_component/src/main.cpp +++ b/vendors/flecs/examples/cpp/relationships/relation_component/src/main.cpp @@ -48,7 +48,7 @@ int main(int, char*[]) { // You can prevent a pair from assuming the type of a component by adding // the Tag property to a relationship: - ecs.component().add(flecs::Tag); + ecs.component().add(flecs::PairIsTag); // Even though Position is a component, contains no // data because MustHave has the Tag property. @@ -63,7 +63,7 @@ int main(int, char*[]) { // When querying for a relationship component, add the pair type as template // argument to the builder: flecs::query q = ecs.query_builder() - .term_at(1).second() // set second part of pair for first term + .term_at(0).second() // set second part of pair for first term .build(); // When iterating, always use the pair type: diff --git a/vendors/flecs/examples/cpp/relationships/symmetric_relations/BUILD b/vendors/flecs/examples/cpp/relationships/symmetric_relations/BUILD new file mode 100644 index 000000000..5baf5b0c1 --- /dev/null +++ b/vendors/flecs/examples/cpp/relationships/symmetric_relations/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "symmetric_relations", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/relationships/union/BUILD b/vendors/flecs/examples/cpp/relationships/union/BUILD new file mode 100644 index 000000000..6d1627e5c --- /dev/null +++ b/vendors/flecs/examples/cpp/relationships/union/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "union", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/relationships/union/src/main.cpp b/vendors/flecs/examples/cpp/relationships/union/src/main.cpp index 2007d7dca..e6ce814a8 100644 --- a/vendors/flecs/examples/cpp/relationships/union/src/main.cpp +++ b/vendors/flecs/examples/cpp/relationships/union/src/main.cpp @@ -35,9 +35,9 @@ int main(int argc, char *argv[]) { ecs.component().add(flecs::Union); // Create a query that subscribes for all entities that have a Direction - // and that are walking + // and that are walking. flecs::query<> q = ecs.query_builder() - .with(Walking) + .with(Walking).in() .with(flecs::Wildcard) .build(); @@ -58,21 +58,16 @@ int main(int argc, char *argv[]) { e3.add(Walking); // Iterate the query - q.iter([&](const flecs::iter& it) { - // Get the column with direction states. This is stored as an array - // with identifiers to the individual states - auto movement = it.field(1); - auto direction = it.field(2); + q.each([&](const flecs::iter& it, size_t i) { + flecs::entity e = it.entity(i); - for (auto i : it) { - // Movement will always be Walking, Direction can be any state - std::cout << it.entity(i).name() - << ": Movement: " - << it.world().get_alive(movement[i]).name() - << ", Direction: " - << it.world().get_alive(direction[i]).name() - << std::endl; - } + // Movement will always be Walking, Direction can be any state + std::cout << e.name() + << ": Movement: " + << it.pair(0).second().name() + << ", Direction: " + << it.pair(1).second().name() + << std::endl; }); // Output: diff --git a/vendors/flecs/examples/cpp/systems/basics/BUILD b/vendors/flecs/examples/cpp/systems/basics/BUILD new file mode 100644 index 000000000..637002bd6 --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/basics/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "basics", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/custom_phases/BUILD b/vendors/flecs/examples/cpp/systems/custom_phases/BUILD new file mode 100644 index 000000000..45ad00b8d --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/custom_phases/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_phases", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/custom_phases/src/main.cpp b/vendors/flecs/examples/cpp/systems/custom_phases/src/main.cpp index c7aee7789..ee54e0ef0 100644 --- a/vendors/flecs/examples/cpp/systems/custom_phases/src/main.cpp +++ b/vendors/flecs/examples/cpp/systems/custom_phases/src/main.cpp @@ -27,15 +27,15 @@ int main(int argc, char *argv[]) { // Create 3 dummy systems. ecs.system("CollisionSystem") .kind(Collisions) - .iter(Sys); + .run(Sys); ecs.system("PhysicsSystem") .kind(Physics) - .iter(Sys); + .run(Sys); ecs.system("GameSystem") .kind(flecs::OnUpdate) - .iter(Sys); + .run(Sys); // Run pipeline ecs.progress(); diff --git a/vendors/flecs/examples/cpp/systems/custom_phases_no_builtin/BUILD b/vendors/flecs/examples/cpp/systems/custom_phases_no_builtin/BUILD new file mode 100644 index 000000000..5abe6af89 --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/custom_phases_no_builtin/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_phases_no_builtin", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/custom_phases_no_builtin/src/main.cpp b/vendors/flecs/examples/cpp/systems/custom_phases_no_builtin/src/main.cpp index af8d19fe6..3afe15f6d 100644 --- a/vendors/flecs/examples/cpp/systems/custom_phases_no_builtin/src/main.cpp +++ b/vendors/flecs/examples/cpp/systems/custom_phases_no_builtin/src/main.cpp @@ -30,15 +30,15 @@ int main(int argc, char *argv[]) { // Create 3 dummy systems. ecs.system("CollisionSystem") .kind(Collisions) - .iter(Sys); + .run(Sys); ecs.system("PhysicsSystem") .kind(Physics) - .iter(Sys); + .run(Sys); ecs.system("GameSystem") .kind(Update) - .iter(Sys); + .run(Sys); // Run pipeline ecs.progress(); diff --git a/vendors/flecs/examples/cpp/systems/custom_pipeline/BUILD b/vendors/flecs/examples/cpp/systems/custom_pipeline/BUILD new file mode 100644 index 000000000..7868155cc --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/custom_pipeline/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_pipeline", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/custom_pipeline/src/main.cpp b/vendors/flecs/examples/cpp/systems/custom_pipeline/src/main.cpp index 9beef8e10..145ec7af6 100644 --- a/vendors/flecs/examples/cpp/systems/custom_pipeline/src/main.cpp +++ b/vendors/flecs/examples/cpp/systems/custom_pipeline/src/main.cpp @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { // Create system with Physics tag ecs.system() .kind() - .iter([](flecs::iter&) { + .run([](flecs::iter&) { std::cout << "System ran!\n"; }); diff --git a/vendors/flecs/examples/cpp/systems/custom_runner/BUILD b/vendors/flecs/examples/cpp/systems/custom_runner/BUILD new file mode 100644 index 000000000..33ef7f387 --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/custom_runner/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "custom_runner", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/custom_runner/src/main.cpp b/vendors/flecs/examples/cpp/systems/custom_runner/src/main.cpp index 158e6936d..36e6f8057 100644 --- a/vendors/flecs/examples/cpp/systems/custom_runner/src/main.cpp +++ b/vendors/flecs/examples/cpp/systems/custom_runner/src/main.cpp @@ -19,21 +19,19 @@ struct Velocity { int main(int, char *[]) { flecs::world ecs; - flecs::system s = ecs.system() - // The run function has a signature that accepts a C iterator. By - // forwarding the iterator to it->callback, the each function of the - // system is invoked. - .run([](flecs::iter_t *it) { + flecs::system s = ecs.system() + // Forward each result from the run callback to the each callback. + .run([](flecs::iter& it) { std::cout << "Move begin\n"; // Walk over the iterator, forward to the system callback - while (ecs_iter_next(it)) { - it->callback(it); + while (it.next()) { + it.each(); } std::cout << "Move end\n"; - }) - .each([](flecs::entity e, Position& p, const Velocity& v) { + }, + [](flecs::entity e, Position& p, const Velocity& v) { p.x += v.x; p.y += v.y; std::cout << e.name() << ": {" << p.x << ", " << p.y << "}\n"; diff --git a/vendors/flecs/examples/cpp/systems/delta_time/BUILD b/vendors/flecs/examples/cpp/systems/delta_time/BUILD new file mode 100644 index 000000000..05531e36b --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/delta_time/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "delta_time", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/delta_time/src/main.cpp b/vendors/flecs/examples/cpp/systems/delta_time/src/main.cpp index 03fd5af24..428fd525f 100644 --- a/vendors/flecs/examples/cpp/systems/delta_time/src/main.cpp +++ b/vendors/flecs/examples/cpp/systems/delta_time/src/main.cpp @@ -8,7 +8,7 @@ int main(int, char *[]) { // components which means it won't match any entities, but will still be ran // once for each call to ecs_progress. ecs.system() - .iter([](flecs::iter& it) { + .run([](flecs::iter& it) { std::cout << "delta_time: " << it.delta_time() << std::endl; }); diff --git a/vendors/flecs/examples/cpp/systems/immediate/BUILD b/vendors/flecs/examples/cpp/systems/immediate/BUILD new file mode 100644 index 000000000..b48754b1d --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/immediate/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "immediate", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/immediate/include/immediate.h b/vendors/flecs/examples/cpp/systems/immediate/include/immediate.h new file mode 100644 index 000000000..1aecd4de2 --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/immediate/include/immediate.h @@ -0,0 +1,16 @@ +#ifndef IMMEDIATE_H +#define IMMEDIATE_H + +/* This generated file contains includes for project dependencies */ +#include "immediate/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/examples/cpp/systems/immediate/include/immediate/bake_config.h b/vendors/flecs/examples/cpp/systems/immediate/include/immediate/bake_config.h new file mode 100644 index 000000000..25f8e5513 --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/immediate/include/immediate/bake_config.h @@ -0,0 +1,24 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef IMMEDIATE_BAKE_CONFIG_H +#define IMMEDIATE_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include + +#endif + diff --git a/vendors/flecs/examples/cpp/systems/no_readonly/project.json b/vendors/flecs/examples/cpp/systems/immediate/project.json similarity index 85% rename from vendors/flecs/examples/cpp/systems/no_readonly/project.json rename to vendors/flecs/examples/cpp/systems/immediate/project.json index 47e026263..c6708e2ed 100644 --- a/vendors/flecs/examples/cpp/systems/no_readonly/project.json +++ b/vendors/flecs/examples/cpp/systems/immediate/project.json @@ -1,5 +1,5 @@ { - "id": "no_readonly", + "id": "immediate", "type": "application", "value": { "use": [ diff --git a/vendors/flecs/examples/cpp/systems/immediate/src/main.cpp b/vendors/flecs/examples/cpp/systems/immediate/src/main.cpp new file mode 100644 index 000000000..18c5dbf14 --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/immediate/src/main.cpp @@ -0,0 +1,87 @@ +#include +#include + +// When an application calls world.progress(), the world is put in readonly mode. +// This ensures that systems (on multiple threads) can safely iterate +// components, without having to worry about components moving around while +// they're being read. This has as side effect that any operations (like adding +// or removing components) are not visible until the end of the frame (see the +// sync_point example for more details). +// Sometimes this is not what you want, and you need a change to be visible +// immediately. For these use cases, applications can use an immediate system. +// This temporarily takes the world out of readonly mode, so a system can make +// changes that are directly visible. +// Because they mutate the world directly, immediate systems are never ran on +// more than one thread, and no other systems are ran at the same time. + +struct Waiter { }; +struct Plate { }; + +int main(int, char *[]) { + flecs::world ecs; + + // Create query to find all waiters without a plate + flecs::query<> q_waiter = ecs.query_builder() + .with() + .without(flecs::Wildcard) + .build(); + + // System that assigns plates to waiter. By making this system immediate + // plate assignments are assigned directly (not deferred) to waiters, which + // ensures that we won't assign plates to the same waiter more than once. + ecs.system("AssignPlate") + .with() + .without(flecs::Wildcard) + .immediate() + .each([&](flecs::iter& it, size_t i) { + flecs::entity plate = it.entity(i); + + // Find an available waiter + flecs::entity waiter = q_waiter.first(); + if (waiter) { + // An available waiter was found, assign a plate to it so + // that the next plate will no longer find it. + // The defer_suspend function temporarily suspends deferring + // operations, which ensures that our plate is assigned + // immediately. Even though this is an immediate system, + // deferring is still enabled by default, as adding/removing + // components to the entities being iterated would interfere + // with the system iterator. + it.world().defer_suspend(); + waiter.add(plate); + it.world().defer_resume(); + + // Now that deferring is resumed, we can safely also add the + // waiter to the plate. We can't do this while deferring is + // suspended, because the plate is the entity we're + // currently iterating, and we don't want to move it to a + // different table while we're iterating it. + plate.add(waiter); + + std::cout << "Assigned " + << waiter.name() << " to " + << plate.name() << "!\n"; + } else { + // No available waiters, can't assign the plate + } + }); + + // Create a few plates and waiters + flecs::entity waiter_1 = ecs.entity("waiter_1").add(); + ecs.entity("waiter_2").add(); + ecs.entity("waiter_3").add(); + + ecs.entity("plate_1").add(); + flecs::entity plate_2 = ecs.entity("plate_2").add(); + ecs.entity("plate_3").add(); + + waiter_1.add(plate_2); + plate_2.add(waiter_1); + + // run systems + ecs.progress(); + + // Output: + // Assigned waiter_3 to plate_1! + // Assigned waiter_2 to plate_3! +} diff --git a/vendors/flecs/examples/cpp/systems/mutate_entity/BUILD b/vendors/flecs/examples/cpp/systems/mutate_entity/BUILD new file mode 100644 index 000000000..2b4a17bee --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/mutate_entity/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "mutate_entity", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/mutate_entity_handle/BUILD b/vendors/flecs/examples/cpp/systems/mutate_entity_handle/BUILD new file mode 100644 index 000000000..0e1e7e838 --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/mutate_entity_handle/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "mutate_entity_handle", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/no_readonly/include/no_readonly.h b/vendors/flecs/examples/cpp/systems/no_readonly/include/no_readonly.h deleted file mode 100644 index 6aee24f3e..000000000 --- a/vendors/flecs/examples/cpp/systems/no_readonly/include/no_readonly.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef NO_READONLY_H -#define NO_READONLY_H - -/* This generated file contains includes for project dependencies */ -#include "no_readonly/bake_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/vendors/flecs/examples/cpp/systems/no_readonly/include/no_readonly/bake_config.h b/vendors/flecs/examples/cpp/systems/no_readonly/include/no_readonly/bake_config.h deleted file mode 100644 index 8e0ab56ea..000000000 --- a/vendors/flecs/examples/cpp/systems/no_readonly/include/no_readonly/bake_config.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - ) - (.) - .|. - | | - _.--| |--._ - .-'; ;`-'& ; `&. - \ & ; & &_/ - |"""---...---"""| - \ | | | | | | | / - `---.|.|.|.---' - - * This file is generated by bake.lang.c for your convenience. Headers of - * dependencies will automatically show up in this file. Include bake_config.h - * in your main project file. Do not edit! */ - -#ifndef NO_READONLY_BAKE_CONFIG_H -#define NO_READONLY_BAKE_CONFIG_H - -/* Headers of public dependencies */ -#include - -#endif - diff --git a/vendors/flecs/examples/cpp/systems/no_readonly/src/main.cpp b/vendors/flecs/examples/cpp/systems/no_readonly/src/main.cpp deleted file mode 100644 index dd4f3b75d..000000000 --- a/vendors/flecs/examples/cpp/systems/no_readonly/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -// When an application calls world.progress(), the world is put in readonly mode. -// This ensures that systems (on multiple threads) can safely iterate -// components, without having to worry about components moving around while -// they're being read. This has as side effect that any operations (like adding -// or removing components) are not visible until the end of the frame (see the -// sync_point example for more details). -// Sometimes this is not what you want, and you need a change to be visible -// immediately. For these use cases, applications can use a no_readonly system. -// This temporarily takes the world out of readonly mode, so a system can make -// changes that are directly visible. -// Because they mutate the world directly, no_readonly systems are never ran on -// more than one thread, and no other systems are ran at the same time. - -struct Waiter { }; -struct Plate { }; - -int main(int, char *[]) { - flecs::world ecs; - - // Create query to find all waiters without a plate - flecs::query<> q_waiter = ecs.query_builder() - .with() - .without(flecs::Wildcard) - .build(); - - // System that assigns plates to waiter. By making this system no_readonly - // plate assignments are assigned directly (not deferred) to waiters, which - // ensures that we won't assign plates to the same waiter more than once. - ecs.system("AssignPlate") - .with() - .without(flecs::Wildcard) - .no_readonly() - .iter([&](flecs::iter& it) { - for (auto i : it) { - flecs::entity plate = it.entity(i); - - // Find an available waiter - flecs::entity waiter = q_waiter.first(); - if (waiter) { - // An available waiter was found, assign a plate to it so - // that the next plate will no longer find it. - // The defer_suspend function temporarily suspends deferring - // operations, which ensures that our plate is assigned - // immediately. Even though this is a no_readonly system, - // defering is still enabled by default, as adding/removing - // components to the entities being iterated would intefere - // with the system iterator. - it.world().defer_suspend(); - waiter.add(plate); - it.world().defer_resume(); - - // Now that defering is resumed, we can safely also add the - // waiter to the plate. We can't do this while defering is - // suspended, because the plate is the entity we're - // currently iterating, and we don't want to move it to a - // different table while we're iterating it. - plate.add(waiter); - - std::cout << "Assigned " - << waiter.name() << " to " - << plate.name() << "!\n"; - } else { - // No available waiters, can't assign the plate - } - } - }); - - // Create a few plates and waiters - flecs::entity waiter_1 = ecs.entity("waiter_1").add(); - ecs.entity("waiter_2").add(); - ecs.entity("waiter_3").add(); - - ecs.entity("plate_1").add(); - flecs::entity plate_2 = ecs.entity("plate_2").add(); - ecs.entity("plate_3").add(); - - waiter_1.add(plate_2); - plate_2.add(waiter_1); - - // run systems - ecs.progress(); - - // Output: - // Assigned waiter_3 to plate_1! - // Assigned waiter_2 to plate_3! -} diff --git a/vendors/flecs/examples/cpp/systems/pipeline/BUILD b/vendors/flecs/examples/cpp/systems/pipeline/BUILD new file mode 100644 index 000000000..fb6ebf2ef --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/pipeline/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "pipeline", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/startup_system/BUILD b/vendors/flecs/examples/cpp/systems/startup_system/BUILD new file mode 100644 index 000000000..db457d6ca --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/startup_system/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "startup_system", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/startup_system/src/main.cpp b/vendors/flecs/examples/cpp/systems/startup_system/src/main.cpp index 00cff82da..35c8f43b1 100644 --- a/vendors/flecs/examples/cpp/systems/startup_system/src/main.cpp +++ b/vendors/flecs/examples/cpp/systems/startup_system/src/main.cpp @@ -16,13 +16,13 @@ int main(int, char *[]) { // Startup system ecs.system("Startup") .kind(flecs::OnStart) - .iter([](flecs::iter& it) { + .run([](flecs::iter& it) { std::cout << it.system().name() << "\n"; }); // Regular system ecs.system("Update") - .iter([](flecs::iter& it) { + .run([](flecs::iter& it) { std::cout << it.system().name() << "\n"; }); diff --git a/vendors/flecs/examples/cpp/systems/sync_point/BUILD b/vendors/flecs/examples/cpp/systems/sync_point/BUILD new file mode 100644 index 000000000..072d1395d --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/sync_point/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "sync_point", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/sync_point_delete/BUILD b/vendors/flecs/examples/cpp/systems/sync_point_delete/BUILD new file mode 100644 index 000000000..01ded312c --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/sync_point_delete/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "sync_point_delete", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/system_ctx/BUILD b/vendors/flecs/examples/cpp/systems/system_ctx/BUILD new file mode 100644 index 000000000..08f375c8b --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/system_ctx/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "system_ctx", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/target_fps/BUILD b/vendors/flecs/examples/cpp/systems/target_fps/BUILD new file mode 100644 index 000000000..7f0450bd8 --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/target_fps/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "target_fps", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/target_fps/src/main.cpp b/vendors/flecs/examples/cpp/systems/target_fps/src/main.cpp index 84bf7ce47..f0a21fb21 100644 --- a/vendors/flecs/examples/cpp/systems/target_fps/src/main.cpp +++ b/vendors/flecs/examples/cpp/systems/target_fps/src/main.cpp @@ -8,7 +8,7 @@ int main(int, char *[]) { // components which means it won't match any entities, but will still be ran // once for each call to ecs_progress. ecs.system() - .iter([](flecs::iter& it) { + .run([](flecs::iter& it) { std::cout << "delta_time: " << it.delta_time() << std::endl; }); diff --git a/vendors/flecs/examples/cpp/systems/time_interval/BUILD b/vendors/flecs/examples/cpp/systems/time_interval/BUILD new file mode 100644 index 000000000..498a50cb6 --- /dev/null +++ b/vendors/flecs/examples/cpp/systems/time_interval/BUILD @@ -0,0 +1,11 @@ +cc_binary( + name = "time_interval", + srcs = glob([ + "src/*.cpp", + "include/**/*.h", + ]), + includes = ["include"], + deps = [ + "//:flecs", + ], +) diff --git a/vendors/flecs/examples/cpp/systems/time_interval/src/main.cpp b/vendors/flecs/examples/cpp/systems/time_interval/src/main.cpp index 9bcdefadb..9cb049801 100644 --- a/vendors/flecs/examples/cpp/systems/time_interval/src/main.cpp +++ b/vendors/flecs/examples/cpp/systems/time_interval/src/main.cpp @@ -12,11 +12,11 @@ int main(int, char *[]) { ecs.system("Tick") .interval(1.0) // time in seconds - .iter(Tick); + .run(Tick); ecs.system("FastTick") .interval(0.5) - .iter(Tick); + .run(Tick); // Run the main loop at 60 FPS ecs.set_target_fps(60); diff --git a/vendors/flecs/examples/os_api/stdcpp/src/flecs-os_api-stdcpp.cpp b/vendors/flecs/examples/os_api/stdcpp/src/flecs-os_api-stdcpp.cpp index 24b095942..5d8b3d4d3 100644 --- a/vendors/flecs/examples/os_api/stdcpp/src/flecs-os_api-stdcpp.cpp +++ b/vendors/flecs/examples/os_api/stdcpp/src/flecs-os_api-stdcpp.cpp @@ -4,6 +4,9 @@ #include #ifdef _MSC_VER +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #include "windows.h" #endif diff --git a/vendors/flecs/examples/plecs/anonymous_entity.flecs b/vendors/flecs/examples/script/anonymous_entity.flecs similarity index 75% rename from vendors/flecs/examples/plecs/anonymous_entity.flecs rename to vendors/flecs/examples/script/anonymous_entity.flecs index cf8913ef9..b34e79ceb 100644 --- a/vendors/flecs/examples/plecs/anonymous_entity.flecs +++ b/vendors/flecs/examples/script/anonymous_entity.flecs @@ -1,12 +1,14 @@ // To see what the result of parsing this file looks like, copy the code and // paste it into the editor at https://flecs.dev/explorer // -// To load this file yourself, call ecs_plecs_from_file("anonymous_entity.flecs"); +// To load this file yourself, call ecs_script_run_file("anonymous_entity.flecs"); // To create an entity without a name, use the _ character -_ :- SpaceShip +_ { + SpaceShip +} // Anonymous entities can be used as parents and children SpaceShip _ { - _ :- Engine + _ { Engine } } diff --git a/vendors/flecs/examples/plecs/docs.flecs b/vendors/flecs/examples/script/docs.flecs similarity index 77% rename from vendors/flecs/examples/plecs/docs.flecs rename to vendors/flecs/examples/script/docs.flecs index 264feb6ff..1dc029ea4 100644 --- a/vendors/flecs/examples/plecs/docs.flecs +++ b/vendors/flecs/examples/script/docs.flecs @@ -1,15 +1,15 @@ // To see what the result of parsing this file looks like, copy the code and // paste it into the editor at https://flecs.dev/explorer // -// To load this file yourself, call ecs_plecs_from_file("docs.flecs"); +// To load this file yourself, call ecs_script_run_file("docs.flecs"); -// Plecs files can be used to annotate entities and components with +// Flecs script files can be used to annotate entities and components with // documentation. These annotations use the flecs.doc addon. @brief A brief description of planet Earth @name The Earth Earth { - - Planet + Planet } // Annotations can be created for entities definfed elsewhere. This makes it diff --git a/vendors/flecs/examples/plecs/expressions.flecs b/vendors/flecs/examples/script/expressions.flecs similarity index 59% rename from vendors/flecs/examples/plecs/expressions.flecs rename to vendors/flecs/examples/script/expressions.flecs index e2c2c1f24..8bbf0273e 100644 --- a/vendors/flecs/examples/plecs/expressions.flecs +++ b/vendors/flecs/examples/script/expressions.flecs @@ -1,22 +1,22 @@ // To see what the result of parsing this file looks like, copy the code and // paste it into the editor at https://flecs.dev/explorer // -// To load this file yourself, call ecs_plecs_from_file("expressions.flecs"); +// To load this file yourself, call ecs_script_run_file("expressions.flecs"); using flecs.meta // Create component types, see reflection example -Struct Position { - x :- {f32} - y :- {f32} +struct Position { + x = f32 + y = f32 } -Struct Rectangle { - width :- {f32} - height :- {f32} +struct Rectangle { + width = f32 + height = f32 } -// Plecs files can contain variables that can be referenced later on when +// Flecs script files can contain variables that can be referenced later on when // assigning values to components const width = 5 @@ -25,6 +25,6 @@ const width = 5 const height = $width * 2 e { - - Position{0, -($height / 2)} - - Rectangle{$width, $height} + Position: {0, -($height / 2)} + Rectangle: {$width, $height} } diff --git a/vendors/flecs/examples/plecs/hello_world.flecs b/vendors/flecs/examples/script/hello_world.flecs similarity index 72% rename from vendors/flecs/examples/plecs/hello_world.flecs rename to vendors/flecs/examples/script/hello_world.flecs index 2b430859f..71fdec6f6 100644 --- a/vendors/flecs/examples/plecs/hello_world.flecs +++ b/vendors/flecs/examples/script/hello_world.flecs @@ -1,4 +1,4 @@ -// Plecs is a simple language for loading entities into flecs. It has as +// Flecs script is a simple language for loading entities into flecs. It has as // advantage that it natively integrates with Flecs features such as the // reflection addon (meta), prefabs, hierarchies and relationships. // @@ -10,34 +10,36 @@ // To see what the result of parsing this file looks like, copy the code and // paste it into the editor at https://flecs.dev/explorer // -// To load this file yourself, call ecs_plecs_from_file("hello_world.flecs"); +// To load this file yourself, call ecs_script_run_file("hello_world.flecs"); // This creates an entity my_spaceship with the SpaceShip tag. Note how neither // the entity nor tag need to be defined in advance, if an entity did not yet // exist, it will be created on the spot. -my_spaceship :- SpaceShip +my_spaceship { SpaceShip } // An entity can be declared in advance, which can be useful to ensure it is -// placed in a specific scope. To do this just specify the name: -Engine +// placed in a specific scope. To do this just specify the name with a scope: +Engine {} // By opening a scope multiple components/tags can be added to the same entity // without having to repeat the entity name. my_spaceship { - - Freighter - - (Faction, Earth) // Relationships use the same syntax as the query DSL + Freighter + (Faction, Earth) // Relationships use the same syntax as the query DSL } // A scope can also be used to add child entities. Child entities and components // can be added in the same scope. my_spaceship { - - FasterThanLight + FasterThanLight // This creates an engine entity with my_spaceship as parent engine { - - Engine // The Engine tag is added to my_spaceship.engine + Engine // The Engine tag is added to my_spaceship.engine } } // The dot notation can be used to refer to nested entities -my_spaceship.engine :- Ftl +my_spaceship.engine { + Ftl +} diff --git a/vendors/flecs/examples/plecs/prefabs.flecs b/vendors/flecs/examples/script/prefabs.flecs similarity index 67% rename from vendors/flecs/examples/plecs/prefabs.flecs rename to vendors/flecs/examples/script/prefabs.flecs index 30dd727e1..eb48624d9 100644 --- a/vendors/flecs/examples/plecs/prefabs.flecs +++ b/vendors/flecs/examples/script/prefabs.flecs @@ -1,35 +1,37 @@ // To see what the result of parsing this file looks like, copy the code and // paste it into the editor at https://flecs.dev/explorer // -// To load this file yourself, call ecs_plecs_from_file("prefabs.flecs"); +// To load this file yourself, call ecs_script_run_file("prefabs.flecs"); // Create a component types (see reflection.flecs example) using flecs.meta -Struct Attack { - value :- {f32} +struct Attack { + value : f32 } -Struct Defense { - value :- {f32} +struct Defense { + value : f32 } // To create a prefab, you can create an entity with the Prefab tag: -SpaceShip :- Prefab +SpaceShip { + prefab +} // Alternatively you can use the shorthand declaration syntax which has the same // result, and allows you to open a scope after the statement: -Prefab SpaceShip { - - Attack{50} - - Defense{50} +prefab SpaceShip { + Attack: {50} + Defense: {50} } // To create a prefab that inherits from SpaceShip, we can use the : operator -Prefab Freighter : SpaceShip { - - Defense{100} +prefab Freighter : SpaceShip { + Defense: {100} } // We can create a prefab instance with the same : operator. my_spaceship : Freighter { - - Attack{75} // Override component from SpaceShip + Attack: {75} // Override component from SpaceShip } diff --git a/vendors/flecs/examples/plecs/reflection.flecs b/vendors/flecs/examples/script/reflection.flecs similarity index 59% rename from vendors/flecs/examples/plecs/reflection.flecs rename to vendors/flecs/examples/script/reflection.flecs index c07afa28e..7a9e937c5 100644 --- a/vendors/flecs/examples/plecs/reflection.flecs +++ b/vendors/flecs/examples/script/reflection.flecs @@ -1,15 +1,15 @@ // To see what the result of parsing this file looks like, copy the code and // paste it into the editor at https://flecs.dev/explorer // -// To load this file yourself, call ecs_plecs_from_file("reflection.flecs"); +// To load this file yourself, call ecs_script_run_file("reflection.flecs"); -// It is possible to set component values in a plecs file as long as the +// It is possible to set component values in a Flecs script file as long as the // component is described with reflection data. Since we don't have a component // yet, we'll first have to create one. -// Plecs does not have dedicated syntax for describing types, but since the +// Flecs script does not have dedicated syntax for describing types, but since the // reflection addon (flecs.meta) uses regular entities and components to store -// reflection data, we can use existing plecs syntax to create a type. +// reflection data, we can use existing Flecs script syntax to create a type. // First we'll add flecs.meta to the list of namespaces to search. This is not // strictly necessary, but lets us write Struct instead of flecs.meta.Struct @@ -18,31 +18,31 @@ using flecs.meta // We can now create a struct like this. "Struct Position" is the shorthand // declaration syntax for "Position :- Struct", and has as benefit that we can // open a scope after the statement -Struct Position { +struct Position { // Add two child entities with the Member component - x :- Member{type: f32} - y :- Member{type: f32} + x { member: {type: f32} } + y { member: {type: f32} } } -// Plecs has a feature which makes it possible to specify a default type for a +// Flecs script has a feature which makes it possible to specify a default type for a // scope. The Struct component has Member as default type. This means we can // also create a type like this, which is a bit shorter: -Struct Mass { - value :- {f32} // 'type' can be ommitted since it's the first member +struct Mass { + value = f32 // 'type' can be omitted since it's the first member } // Similarly we can also define an enumeration type. The default scope type for // Enum is Constant, which ensures amongst others that the child entities will -// be assigned with an incremenenting constant value automatically. -Enum Color { - Red - Green +// be assigned with an incrementing constant value automatically. +enum Color { + Red, + Green, Blue } // We can now create an entity that uses the types my_entity { - - Position {x: 10, y: 20} - - Mass {100} // Member names are optional - - Color {Green} // Green will be automatically looked up in the Color scope + Position: {x: 10, y: 20} + Mass: {100} // Member names are optional + Color: {Green} // Green will be automatically looked up in the Color scope } diff --git a/vendors/flecs/examples/plecs/strings.flecs b/vendors/flecs/examples/script/strings.flecs similarity index 68% rename from vendors/flecs/examples/plecs/strings.flecs rename to vendors/flecs/examples/script/strings.flecs index aea4b147f..926101596 100644 --- a/vendors/flecs/examples/plecs/strings.flecs +++ b/vendors/flecs/examples/script/strings.flecs @@ -1,20 +1,20 @@ // To see what the result of parsing this file looks like, copy the code and // paste it into the editor at https://flecs.dev/explorer // -// To load this file yourself, call ecs_plecs_from_file("strings.flecs"); +// To load this file yourself, call ecs_script_run_file("strings.flecs"); -// Plecs component values can be populated with strings. To see how this works, +// Flecs script component values can be populated with strings. To see how this works, // we first need to create a component type (see reflection example). using flecs.meta -Struct Shader { - filename :- {string} - code :- {string} +struct Shader { + filename = string + code = string } // Create component values with strings my_pipeline { - - (Shader, Vertex) { + (Shader, Vertex): { // Normal string filename: "vert.glsl", @@ -25,7 +25,7 @@ my_pipeline { }` } - - (Shader, Fragment) { + (Shader, Fragment): { filename: "frag.glsl", code: ` void main() { diff --git a/vendors/flecs/examples/plecs/with.flecs b/vendors/flecs/examples/script/with.flecs similarity index 73% rename from vendors/flecs/examples/plecs/with.flecs rename to vendors/flecs/examples/script/with.flecs index b40e368e6..66e9efe0a 100644 --- a/vendors/flecs/examples/plecs/with.flecs +++ b/vendors/flecs/examples/script/with.flecs @@ -1,36 +1,36 @@ // To see what the result of parsing this file looks like, copy the code and // paste it into the editor at https://flecs.dev/explorer // -// To load this file yourself, call ecs_plecs_from_file("with.flecs"); +// To load this file yourself, call ecs_script_run_file("with.flecs"); // Sometimes you want to add the same component to a lot of entities. To avoid // repeating yourself, you can use the "with" keyword: with Planet { // With statements can be nested, which adds to the list of components to add with InnerPlanet { - Mercury - Venus + Mercury {} + Venus {} Earth { // A with scope contains regular statements so we can do anything we can // do normally, like assign components and open scopes. - - SupportsLife + SupportsLife } - Mars + Mars {} } with OuterPlanet { - Jupiter - Saturn - Neptune - Uranus + Jupiter {} + Saturn {} + Neptune {} + Uranus {} } } // A with statement may be placed inside of a scope Jupiter { with Moon { - Io - Europa - Ganymede - Callisto + Io {} + Europa {} + Ganymede {} + Callisto {} } } diff --git a/vendors/flecs/flecs.h b/vendors/flecs/flecs.h index 4274b3e48..582cc34b1 100644 --- a/vendors/flecs/flecs.h +++ b/vendors/flecs/flecs.h @@ -1,3 +1,6 @@ +#undef min +#undef max + // Comment out this line when using as DLL #define flecs_STATIC /** @@ -12,55 +15,74 @@ /** * @defgroup c C API - * + * * @{ * @} */ /** * @defgroup core Core - * @brief Core ECS functionality (entities, storage, queries). - * - * \ingroup c + * @ingroup c + * Core ECS functionality (entities, storage, queries). + * * @{ */ /** * @defgroup options API defines - * @brief Defines for customizing compile time features. + * Defines for customizing compile time features. + * * @{ */ -/** \def ecs_float_t +/* Flecs version macros */ +#define FLECS_VERSION_MAJOR 4 /**< Flecs major version. */ +#define FLECS_VERSION_MINOR 0 /**< Flecs minor version. */ +#define FLECS_VERSION_PATCH 3 /**< Flecs patch version. */ + +/** Flecs version. */ +#define FLECS_VERSION FLECS_VERSION_IMPL(\ + FLECS_VERSION_MAJOR, FLECS_VERSION_MINOR, FLECS_VERSION_PATCH) + +/** @def FLECS_CONFIG_HEADER + * Allows for including a user-customizable header that specifies compile-time + * features. */ +#ifdef FLECS_CONFIG_HEADER +#include "flecs_config.h" +#endif + +/** @def ecs_float_t * Customizable precision for floating point operations */ #ifndef ecs_float_t #define ecs_float_t float #endif -/** \def ecs_ftime_t - * Customizable precision for scalar time values. Change to double precision for +/** @def ecs_ftime_t + * Customizable precision for scalar time values. Change to double precision for * processes that can run for a long time (e.g. longer than a day). */ #ifndef ecs_ftime_t #define ecs_ftime_t ecs_float_t #endif -/** \def FLECS_LEGACY - * Define when building for C89 +/** @def FLECS_LEGACY + * Define when building for C89 */ // #define FLECS_LEGACY -/** \def FLECS_NO_DEPRECATED_WARNINGS - * disables deprecated warnings - */ -#define FLECS_NO_DEPRECATED_WARNINGS - -/** \def FLECS_ACCURATE_COUNTERS - * Define to ensure that global counters used for statistics (such as the +/** @def FLECS_ACCURATE_COUNTERS + * Define to ensure that global counters used for statistics (such as the * allocation counters in the OS API) are accurate in multithreaded - * applications, at the cost of increased overhead. + * applications, at the cost of increased overhead. */ // #define FLECS_ACCURATE_COUNTERS +/** @def FLECS_DISABLE_COUNTERS + * Disables counters used for statistics. Improves performance, but + * will prevent some features that rely on statistics from working, + * like the statistics pages in the explorer. + */ +// #define FLECS_DISABLE_COUNTERS + /* Make sure provided configuration is valid */ #if defined(FLECS_DEBUG) && defined(FLECS_NDEBUG) #error "invalid configuration: cannot both define FLECS_DEBUG and FLECS_NDEBUG" @@ -69,11 +91,11 @@ #error "invalid configuration: cannot both define FLECS_DEBUG and NDEBUG" #endif -/** \def FLECS_DEBUG - * Used for input parameter checking and cheap sanity checks. There are lots of - * asserts in every part of the code, so this will slow down applications. +/** @def FLECS_DEBUG + * Used for input parameter checking and cheap sanity checks. There are lots of + * asserts in every part of the code, so this will slow down applications. */ -#if !defined(FLECS_DEBUG) && !defined(FLECS_NDEBUG) +#if !defined(FLECS_DEBUG) && !defined(FLECS_NDEBUG) #if defined(NDEBUG) #define FLECS_NDEBUG #else @@ -81,7 +103,7 @@ #endif #endif -/** \def FLECS_SANITIZE +/** @def FLECS_SANITIZE * Enables expensive checks that can detect issues early. Recommended for * running tests or when debugging issues. This will severely slow down code. */ @@ -95,84 +117,102 @@ * test with the FLECS_DEBUG or FLECS_SANITIZE flags enabled. There's a good * chance that this gives you more information about the issue! */ -/** \def FLECS_SOFT_ASSERT +/** @def FLECS_SOFT_ASSERT * Define to not abort for recoverable errors, like invalid parameters. An error * is still thrown to the console. This is recommended for when running inside a * third party runtime, such as the Unreal editor. - * + * * Note that internal sanity checks (ECS_INTERNAL_ERROR) will still abort a * process, as this gives more information than a (likely) subsequent crash. - * - * When a soft assert occurs, the code will attempt to minimize the number of + * + * When a soft assert occurs, the code will attempt to minimize the number of * side effects of the failed operation, but this may not always be possible. - * Even though an application may still be able to continue running after a soft - * assert, it should be treated as if in an undefined state. + * Even though an application may still be able to continue running after a soft + * assert, it should be treated as if in an undefined state. */ // #define FLECS_SOFT_ASSERT -/** \def FLECS_KEEP_ASSERT +/** @def FLECS_KEEP_ASSERT * By default asserts are disabled in release mode, when either FLECS_NDEBUG or * NDEBUG is defined. Defining FLECS_KEEP_ASSERT ensures that asserts are not - * disabled. This define can be combined with FLECS_SOFT_ASSERT. + * disabled. This define can be combined with FLECS_SOFT_ASSERT. */ // #define FLECS_KEEP_ASSERT +/** \def FLECS_CPP_NO_AUTO_REGISTRATION + * When set, the C++ API will require that components are registered before they + * are used. This is useful in multithreaded applications, where components need + * to be registered beforehand, and to catch issues in projects where component + * registration is mandatory. Disabling automatic component registration also + * slightly improves performance. + * The C API is not affected by this feature. + */ +// #define FLECS_CPP_NO_AUTO_REGISTRATION + /** \def FLECS_CUSTOM_BUILD * This macro lets you customize which addons to build flecs with. - * Without any addons Flecs is just a minimal ECS storage, but addons add + * Without any addons Flecs is just a minimal ECS storage, but addons add * features such as systems, scheduling and reflection. If an addon is disabled, * it is excluded from the build, so that it consumes no resources. By default * all addons are enabled. - * + * * You can customize a build by either whitelisting or blacklisting addons. To * whitelist addons, first define the FLECS_CUSTOM_BUILD macro, which disables * all addons. You can then manually select the addons you need by defining * their macro, like "FLECS_SYSTEM". - * + * * To blacklist an addon, make sure to *not* define FLECS_CUSTOM_BUILD, and * instead define the addons you don't need by defining FLECS_NO_, for - * example "FLECS_NO_SYSTEM". If there are any addons that depend on the + * example "FLECS_NO_SYSTEM". If there are any addons that depend on the * blacklisted addon, an error will be thrown during the build. - * - * Note that addons can have dependencies on each other. Addons will + * + * Note that addons can have dependencies on each other. Addons will * automatically enable their dependencies. To see the list of addons that was * compiled in a build, enable tracing before creating the world by doing: - * ecs_log_set_level(0); + * + * @code + * ecs_log_set_level(0); + * @endcode + * * which outputs the full list of addons Flecs was compiled with. */ // #define FLECS_CUSTOM_BUILD +/** @def FLECS_CPP_NO_AUTO_REGISTRATION + * When set, the C++ API will require that components are registered before they + * are used. This is useful in multithreaded applications, where components need + * to be registered beforehand, and to catch issues in projects where component + * registration is mandatory. Disabling automatic component registration also + * slightly improves performance. + * The C API is not affected by this feature. + */ +// #define FLECS_CPP_NO_AUTO_REGISTRATION + #ifndef FLECS_CUSTOM_BUILD // #define FLECS_C /**< C API convenience macros, always enabled */ #define FLECS_CPP /**< C++ API */ #define FLECS_MODULE /**< Module support */ -#define FLECS_PARSER /**< String parser for queries */ -#define FLECS_PLECS /**< ECS data definition format */ -#define FLECS_RULES /**< Constraint solver for advanced queries */ -#define FLECS_SNAPSHOT /**< Snapshot & restore ECS data */ -#define FLECS_STATS /**< Access runtime statistics */ -#define FLECS_MONITOR /**< Track runtime statistics periodically */ +#define FLECS_SCRIPT /**< ECS data definition format */ +#define FLECS_STATS /**< Track runtime statistics */ #define FLECS_METRICS /**< Expose component data as statistics */ #define FLECS_ALERTS /**< Monitor conditions for errors */ #define FLECS_SYSTEM /**< System support */ #define FLECS_PIPELINE /**< Pipeline support */ #define FLECS_TIMER /**< Timer support */ #define FLECS_META /**< Reflection support */ -#define FLECS_META_C /**< Utilities for populating reflection data */ #define FLECS_UNITS /**< Builtin standard units */ -#define FLECS_EXPR /**< Parsing strings to/from component values */ #define FLECS_JSON /**< Parsing JSON to/from component values */ #define FLECS_DOC /**< Document entities & components */ -#define FLECS_COREDOC /**< Documentation for core entities & components */ #define FLECS_LOG /**< When enabled ECS provides more detailed logs */ #define FLECS_APP /**< Application addon */ #define FLECS_OS_API_IMPL /**< Default implementation for OS API */ #define FLECS_HTTP /**< Tiny HTTP server for connecting to remote UI */ #define FLECS_REST /**< REST API for querying application data */ // #define FLECS_JOURNAL /**< Journaling addon (disabled by default) */ +// #define FLECS_PERF_TRACE /**< Enable performance tracing (disabled by default) */ #endif // ifndef FLECS_CUSTOM_BUILD -/** \def FLECS_LOW_FOOTPRINT +/** @def FLECS_LOW_FOOTPRINT * Set a number of constants to values that decrease memory footprint, at the * cost of decreased performance. */ // #define FLECS_LOW_FOOTPRINT @@ -184,22 +224,22 @@ #define FLECS_USE_OS_ALLOC #endif -/** \def FLECS_HI_COMPONENT_ID - * This constant can be used to balance between performance and memory +/** @def FLECS_HI_COMPONENT_ID + * This constant can be used to balance between performance and memory * utilization. The constant is used in two ways: * - Entity ids 0..FLECS_HI_COMPONENT_ID are reserved for component ids. * - Used as lookup array size in table edges. - * + * * Increasing this value increases the size of the lookup array, which allows - * fast table traversal, which improves performance of ECS add/remove + * fast table traversal, which improves performance of ECS add/remove * operations. Component ids that fall outside of this range use a regular map * lookup, which is slower but more memory efficient. */ #ifndef FLECS_HI_COMPONENT_ID #define FLECS_HI_COMPONENT_ID (256) #endif -/** \def FLECS_HI_ID_RECORD_ID - * This constant can be used to balance between performance and memory +/** @def FLECS_HI_ID_RECORD_ID + * This constant can be used to balance between performance and memory * utilization. The constant is used to determine the size of the id record * lookup array. Id values that fall outside of this range use a regular map * lookup, which is slower but more memory efficient. @@ -208,7 +248,7 @@ #define FLECS_HI_ID_RECORD_ID (1024) #endif -/** \def FLECS_SPARSE_PAGE_BITS +/** @def FLECS_SPARSE_PAGE_BITS * This constant is used to determine the number of bits of an id that is used * to determine the page index when used with a sparse set. The number of bits * determines the page size, which is (1 << bits). @@ -217,39 +257,57 @@ #define FLECS_SPARSE_PAGE_BITS (12) #endif -/** \def FLECS_ENTITY_PAGE_BITS +/** @def FLECS_ENTITY_PAGE_BITS * Same as FLECS_SPARSE_PAGE_BITS, but for the entity index. */ #ifndef FLECS_ENTITY_PAGE_BITS #define FLECS_ENTITY_PAGE_BITS (12) #endif -/** \def FLECS_USE_OS_ALLOC +/** @def FLECS_USE_OS_ALLOC * When enabled, Flecs will use the OS allocator provided in the OS API directly * instead of the builtin block allocator. This can decrease memory utilization * as memory will be freed more often, at the cost of decreased performance. */ // #define FLECS_USE_OS_ALLOC -/** \def FLECS_ID_DESC_MAX +/** @def FLECS_ID_DESC_MAX * Maximum number of ids to add ecs_entity_desc_t / ecs_bulk_desc_t */ #ifndef FLECS_ID_DESC_MAX #define FLECS_ID_DESC_MAX (32) #endif -/** \def FLECS_TERM_DESC_MAX - * Maximum number of terms in ecs_filter_desc_t */ -#define FLECS_TERM_DESC_MAX (16) - /** \def FLECS_EVENT_DESC_MAX * Maximum number of events in ecs_observer_desc_t */ +#ifndef FLECS_EVENT_DESC_MAX #define FLECS_EVENT_DESC_MAX (8) +#endif -/** \def FLECS_VARIABLE_COUNT_MAX +/** @def FLECS_VARIABLE_COUNT_MAX * Maximum number of query variables per query */ #define FLECS_VARIABLE_COUNT_MAX (64) -/** \def FLECS_QUERY_SCOPE_NESTING_MAX +/** \def FLECS_TERM_COUNT_MAX + * Maximum number of terms in queries. Should not exceed 64. */ +#ifndef FLECS_TERM_COUNT_MAX +#define FLECS_TERM_COUNT_MAX 32 +#endif + +/** \def FLECS_TERM_ARG_COUNT_MAX + * Maximum number of arguments for a term. */ +#ifndef FLECS_TERM_ARG_COUNT_MAX +#define FLECS_TERM_ARG_COUNT_MAX (16) +#endif + +/** \def FLECS_QUERY_VARIABLE_COUNT_MAX + * Maximum number of query variables per query. Should not exceed 128. */ +#ifndef FLECS_QUERY_VARIABLE_COUNT_MAX +#define FLECS_QUERY_VARIABLE_COUNT_MAX (64) +#endif + +/** @def FLECS_QUERY_SCOPE_NESTING_MAX * Maximum nesting depth of query scopes */ +#ifndef FLECS_QUERY_SCOPE_NESTING_MAX #define FLECS_QUERY_SCOPE_NESTING_MAX (8) +#endif /** @} */ @@ -290,7 +348,7 @@ extern "C" { #define EcsWorldMeasureFrameTime (1u << 5) #define EcsWorldMeasureSystemTime (1u << 6) #define EcsWorldMultiThreaded (1u << 7) - +#define EcsWorldFrameInProgress (1u << 8) //////////////////////////////////////////////////////////////////////////////// //// OS API flags @@ -328,26 +386,33 @@ extern "C" { (EcsIdOnDeleteObjectPanic|EcsIdOnDeleteObjectRemove|\ EcsIdOnDeleteObjectDelete) -#define EcsIdExclusive (1u << 6) -#define EcsIdDontInherit (1u << 7) -#define EcsIdTraversable (1u << 8) -#define EcsIdTag (1u << 9) -#define EcsIdWith (1u << 10) -#define EcsIdUnion (1u << 11) -#define EcsIdAlwaysOverride (1u << 12) +#define EcsIdOnInstantiateOverride (1u << 6) +#define EcsIdOnInstantiateInherit (1u << 7) +#define EcsIdOnInstantiateDontInherit (1u << 8) +#define EcsIdOnInstantiateMask\ + (EcsIdOnInstantiateOverride|EcsIdOnInstantiateInherit|\ + EcsIdOnInstantiateDontInherit) + +#define EcsIdExclusive (1u << 9) +#define EcsIdTraversable (1u << 10) +#define EcsIdTag (1u << 11) +#define EcsIdWith (1u << 12) +#define EcsIdCanToggle (1u << 13) +#define EcsIdIsTransitive (1u << 14) #define EcsIdHasOnAdd (1u << 16) /* Same values as table flags */ #define EcsIdHasOnRemove (1u << 17) #define EcsIdHasOnSet (1u << 18) -#define EcsIdHasUnSet (1u << 19) -#define EcsIdHasOnTableFill (1u << 20) -#define EcsIdHasOnTableEmpty (1u << 21) -#define EcsIdHasOnTableCreate (1u << 22) -#define EcsIdHasOnTableDelete (1u << 23) +#define EcsIdHasOnTableFill (1u << 19) +#define EcsIdHasOnTableEmpty (1u << 20) +#define EcsIdHasOnTableCreate (1u << 21) +#define EcsIdHasOnTableDelete (1u << 22) +#define EcsIdIsSparse (1u << 23) +#define EcsIdIsUnion (1u << 24) #define EcsIdEventMask\ - (EcsIdHasOnAdd|EcsIdHasOnRemove|EcsIdHasOnSet|EcsIdHasUnSet|\ + (EcsIdHasOnAdd|EcsIdHasOnRemove|EcsIdHasOnSet|\ EcsIdHasOnTableFill|EcsIdHasOnTableEmpty|EcsIdHasOnTableCreate|\ - EcsIdHasOnTableDelete) + EcsIdHasOnTableDelete|EcsIdIsSparse|EcsIdIsUnion) #define EcsIdMarkedForDelete (1u << 30) @@ -359,6 +424,12 @@ extern "C" { #define ECS_ID_ON_DELETE_FLAG(id) (1u << ((id) - EcsRemove)) #define ECS_ID_ON_DELETE_TARGET_FLAG(id) (1u << (3 + ((id) - EcsRemove))) +/* Utilities for converting from flags to instantiate policies and vice versa */ +#define ECS_ID_ON_INSTANTIATE(flags) \ + ((ecs_entity_t[]){EcsOverride, EcsOverride, EcsInherit, 0, EcsDontInherit}\ + [(((flags) & EcsIdOnInstantiateMask) >> 6)]) +#define ECS_ID_ON_INSTANTIATE_FLAG(id) (1u << (6 + ((id) - EcsOverride))) + //////////////////////////////////////////////////////////////////////////////// //// Iterator flags (used by ecs_iter_t::flags) @@ -366,49 +437,87 @@ extern "C" { #define EcsIterIsValid (1u << 0u) /* Does iterator contain valid result */ #define EcsIterNoData (1u << 1u) /* Does iterator provide (component) data */ -#define EcsIterIsInstanced (1u << 2u) /* Is iterator instanced */ -#define EcsIterHasShared (1u << 3u) /* Does result have shared terms */ -#define EcsIterTableOnly (1u << 4u) /* Result only populates table */ -#define EcsIterEntityOptional (1u << 5u) /* Treat terms with entity subject as optional */ -#define EcsIterNoResults (1u << 6u) /* Iterator has no results */ -#define EcsIterIgnoreThis (1u << 7u) /* Only evaluate non-this terms */ -#define EcsIterMatchVar (1u << 8u) -#define EcsIterHasCondSet (1u << 10u) /* Does iterator have conditionally set fields */ -#define EcsIterProfile (1u << 11u) /* Profile iterator performance */ -#define EcsIterTrivialSearch (1u << 12u) /* Trivial iterator mode */ -#define EcsIterTrivialSearchNoData (1u << 13u) /* Trivial iterator w/no data */ -#define EcsIterTrivialTest (1u << 14u) /* Trivial test mode (constrained $this) */ -#define EcsIterTrivialSearchWildcard (1u << 15u) /* Trivial search with wildcard ids */ +#define EcsIterNoResults (1u << 3u) /* Iterator has no results */ +#define EcsIterIgnoreThis (1u << 4u) /* Only evaluate non-this terms */ +#define EcsIterHasCondSet (1u << 6u) /* Does iterator have conditionally set fields */ +#define EcsIterProfile (1u << 7u) /* Profile iterator performance */ +#define EcsIterTrivialSearch (1u << 8u) /* Trivial iterator mode */ +#define EcsIterTrivialTest (1u << 11u) /* Trivial test mode (constrained $this) */ +#define EcsIterTrivialCached (1u << 14u) /* Trivial search for cached query */ +#define EcsIterCacheSearch (1u << 15u) /* Cache search */ +#define EcsIterFixedInChangeComputed (1u << 16u) /* Change detection for fixed in terms is done */ +#define EcsIterFixedInChanged (1u << 17u) /* Fixed in terms changed */ +#define EcsIterSkip (1u << 18u) /* Result was skipped for change detection */ +#define EcsIterCppEach (1u << 19u) /* Uses C++ 'each' iterator */ + +/* Same as event flags */ +#define EcsIterTableOnly (1u << 20u) /* Result only populates table */ + //////////////////////////////////////////////////////////////////////////////// //// Event flags (used by ecs_event_decs_t::flags) //////////////////////////////////////////////////////////////////////////////// -#define EcsEventTableOnly (1u << 4u) /* Table event (no data, same as iter flags) */ -#define EcsEventNoOnSet (1u << 16u) /* Don't emit OnSet/UnSet for inherited ids */ +#define EcsEventTableOnly (1u << 20u) /* Table event (no data, same as iter flags) */ +#define EcsEventNoOnSet (1u << 16u) /* Don't emit OnSet for inherited ids */ + + +//////////////////////////////////////////////////////////////////////////////// +//// Query flags (used by ecs_query_t::flags) +//////////////////////////////////////////////////////////////////////////////// + +/* Flags that can only be set by the query implementation */ +#define EcsQueryMatchThis (1u << 11u) /* Query has terms with $this source */ +#define EcsQueryMatchOnlyThis (1u << 12u) /* Query only has terms with $this source */ +#define EcsQueryMatchOnlySelf (1u << 13u) /* Query has no terms with up traversal */ +#define EcsQueryMatchWildcards (1u << 14u) /* Query matches wildcards */ +#define EcsQueryMatchNothing (1u << 15u) /* Query matches nothing */ +#define EcsQueryHasCondSet (1u << 16u) /* Query has conditionally set fields */ +#define EcsQueryHasPred (1u << 17u) /* Query has equality predicates */ +#define EcsQueryHasScopes (1u << 18u) /* Query has query scopes */ +#define EcsQueryHasRefs (1u << 19u) /* Query has terms with static source */ +#define EcsQueryHasOutTerms (1u << 20u) /* Query has [out] terms */ +#define EcsQueryHasNonThisOutTerms (1u << 21u) /* Query has [out] terms with no $this source */ +#define EcsQueryHasMonitor (1u << 22u) /* Query has monitor for change detection */ +#define EcsQueryIsTrivial (1u << 23u) /* Query can use trivial evaluation function */ +#define EcsQueryHasCacheable (1u << 24u) /* Query has cacheable terms */ +#define EcsQueryIsCacheable (1u << 25u) /* All terms of query are cacheable */ +#define EcsQueryHasTableThisVar (1u << 26u) /* Does query have $this table var */ +#define EcsQueryCacheYieldEmptyTables (1u << 27u) /* Does query cache empty tables */ +#define EcsQueryNested (1u << 28u) /* Query created by a query (for observer, cache) */ + +//////////////////////////////////////////////////////////////////////////////// +//// Term flags (used by ecs_term_t::flags_) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsTermMatchAny (1u << 0) +#define EcsTermMatchAnySrc (1u << 1) +#define EcsTermTransitive (1u << 2) +#define EcsTermReflexive (1u << 3) +#define EcsTermIdInherited (1u << 4) +#define EcsTermIsTrivial (1u << 5) +#define EcsTermIsCacheable (1u << 7) +#define EcsTermIsScope (1u << 8) +#define EcsTermIsMember (1u << 9) +#define EcsTermIsToggle (1u << 10) +#define EcsTermKeepAlive (1u << 11) +#define EcsTermIsSparse (1u << 12) +#define EcsTermIsUnion (1u << 13) +#define EcsTermIsOr (1u << 14) + //////////////////////////////////////////////////////////////////////////////// -//// Filter flags (used by ecs_filter_t::flags) +//// Observer flags (used by ecs_observer_t::flags) //////////////////////////////////////////////////////////////////////////////// -#define EcsFilterMatchThis (1u << 1u) /* Has terms that match This */ -#define EcsFilterMatchOnlyThis (1u << 2u) /* Has only terms that match This */ -#define EcsFilterMatchPrefab (1u << 3u) /* Does filter match prefabs */ -#define EcsFilterMatchDisabled (1u << 4u) /* Does filter match disabled entities */ -#define EcsFilterMatchEmptyTables (1u << 5u) /* Does filter return empty tables */ -#define EcsFilterMatchAnything (1u << 6u) /* False if filter has no/only Not terms */ -#define EcsFilterNoData (1u << 7u) /* When true, data fields won't be populated */ -#define EcsFilterIsInstanced (1u << 8u) /* Is filter instanced (see ecs_filter_desc_t) */ -#define EcsFilterPopulate (1u << 9u) /* Populate data, ignore non-matching fields */ -#define EcsFilterHasCondSet (1u << 10u) /* Does filter have conditionally set fields */ -#define EcsFilterUnresolvedByName (1u << 11u) /* Use by-name matching for unresolved entity identifiers */ -#define EcsFilterHasPred (1u << 12u) /* Filter has equality predicates */ -#define EcsFilterHasScopes (1u << 13u) /* Filter has query scopes */ -#define EcsFilterIsTrivial (1u << 14u) /* Trivial filter */ -#define EcsFilterMatchOnlySelf (1u << 15u) /* Filter has no up traversal */ -#define EcsFilterHasWildcards (1u << 16u) /* Filter has no up traversal */ -#define EcsFilterOwnsStorage (1u << 17u) /* Is ecs_filter_t object owned by filter */ -#define EcsFilterOwnsTermsStorage (1u << 18u) /* Is terms array owned by filter */ +#define EcsObserverIsMulti (1u << 1u) /* Does observer have multiple terms */ +#define EcsObserverIsMonitor (1u << 2u) /* Is observer a monitor */ +#define EcsObserverIsDisabled (1u << 3u) /* Is observer entity disabled */ +#define EcsObserverIsParentDisabled (1u << 4u) /* Is module parent of observer disabled */ +#define EcsObserverBypassQuery (1u << 5u) /* Don't evaluate query for multi-component observer*/ +#define EcsObserverYieldOnCreate (1u << 6u) /* Yield matching entities when creating observer */ +#define EcsObserverYieldOnDelete (1u << 7u) /* Yield matching entities when deleting observer */ + //////////////////////////////////////////////////////////////////////////////// //// Table flags (used by ecs_table_t::flags) @@ -422,47 +531,35 @@ extern "C" { #define EcsTableHasPairs (1u << 6u) /* Does the table type have pairs */ #define EcsTableHasModule (1u << 7u) /* Does the table have module data */ #define EcsTableIsDisabled (1u << 8u) /* Does the table type has EcsDisabled */ -#define EcsTableHasCtors (1u << 9u) -#define EcsTableHasDtors (1u << 10u) -#define EcsTableHasCopy (1u << 11u) -#define EcsTableHasMove (1u << 12u) -#define EcsTableHasUnion (1u << 13u) +#define EcsTableNotQueryable (1u << 9u) /* Table should never be returned by queries */ +#define EcsTableHasCtors (1u << 10u) +#define EcsTableHasDtors (1u << 11u) +#define EcsTableHasCopy (1u << 12u) +#define EcsTableHasMove (1u << 13u) #define EcsTableHasToggle (1u << 14u) #define EcsTableHasOverrides (1u << 15u) #define EcsTableHasOnAdd (1u << 16u) /* Same values as id flags */ #define EcsTableHasOnRemove (1u << 17u) #define EcsTableHasOnSet (1u << 18u) -#define EcsTableHasUnSet (1u << 19u) -#define EcsTableHasOnTableFill (1u << 20u) -#define EcsTableHasOnTableEmpty (1u << 21u) -#define EcsTableHasOnTableCreate (1u << 22u) -#define EcsTableHasOnTableDelete (1u << 23u) - -#define EcsTableHasTraversable (1u << 25u) -#define EcsTableHasTarget (1u << 26u) - +#define EcsTableHasOnTableFill (1u << 19u) +#define EcsTableHasOnTableEmpty (1u << 20u) +#define EcsTableHasOnTableCreate (1u << 21u) +#define EcsTableHasOnTableDelete (1u << 22u) +#define EcsTableHasSparse (1u << 23u) +#define EcsTableHasUnion (1u << 24u) + +#define EcsTableHasTraversable (1u << 26u) #define EcsTableMarkedForDelete (1u << 30u) /* Composite table flags */ -#define EcsTableHasLifecycle (EcsTableHasCtors | EcsTableHasDtors) -#define EcsTableIsComplex (EcsTableHasLifecycle | EcsTableHasUnion | EcsTableHasToggle) -#define EcsTableHasAddActions (EcsTableHasIsA | EcsTableHasUnion | EcsTableHasCtors | EcsTableHasOnAdd | EcsTableHasOnSet) -#define EcsTableHasRemoveActions (EcsTableHasIsA | EcsTableHasDtors | EcsTableHasOnRemove | EcsTableHasUnSet) - - -//////////////////////////////////////////////////////////////////////////////// -//// Query flags (used by ecs_query_t::flags) -//////////////////////////////////////////////////////////////////////////////// - -#define EcsQueryHasRefs (1u << 1u) /* Does query have references */ -#define EcsQueryIsSubquery (1u << 2u) /* Is query a subquery */ -#define EcsQueryIsOrphaned (1u << 3u) /* Is subquery orphaned */ -#define EcsQueryHasOutTerms (1u << 4u) /* Does query have out terms */ -#define EcsQueryHasNonThisOutTerms (1u << 5u) /* Does query have non-this out terms */ -#define EcsQueryHasMonitor (1u << 6u) /* Does query track changes */ -#define EcsQueryTrivialIter (1u << 7u) /* Does the query require special features to iterate */ - +#define EcsTableHasLifecycle (EcsTableHasCtors | EcsTableHasDtors) +#define EcsTableIsComplex (EcsTableHasLifecycle | EcsTableHasToggle | EcsTableHasSparse) +#define EcsTableHasAddActions (EcsTableHasIsA | EcsTableHasCtors | EcsTableHasOnAdd | EcsTableHasOnSet) +#define EcsTableHasRemoveActions (EcsTableHasIsA | EcsTableHasDtors | EcsTableHasOnRemove) +#define EcsTableEdgeFlags (EcsTableHasOnAdd | EcsTableHasOnRemove | EcsTableHasSparse | EcsTableHasUnion) +#define EcsTableAddEdgeFlags (EcsTableHasOnAdd | EcsTableHasSparse | EcsTableHasUnion) +#define EcsTableRemoveEdgeFlags (EcsTableHasOnRemove | EcsTableHasSparse | EcsTableHasUnion) //////////////////////////////////////////////////////////////////////////////// //// Aperiodic action flags (used by ecs_run_aperiodic) @@ -571,9 +668,6 @@ extern "C" { /* Filenames aren't consistent across targets as they can use different casing * (e.g. WinSock2 vs winsock2). */ #pragma clang diagnostic ignored "-Wnonportable-system-include-path" -/* Enum reflection relies on testing constant values that may not be valid for - * the enumeration. */ -#pragma clang diagnostic ignored "-Wenum-constexpr-conversion" /* Very difficult to workaround this warning in C, especially for an ECS. */ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" /* This warning gets thrown when trying to cast pointer returned from dlproc */ @@ -581,6 +675,17 @@ extern "C" { /* This warning can get thrown for expressions that evaluate to constants * in debug/release mode. */ #pragma clang diagnostic ignored "-Wconstant-logical-operand" +/* With soft asserts enabled the code won't abort, which in some cases means + * code paths are reached where values are uninitialized. */ +#ifdef FLECS_SOFT_ASSERT +#pragma clang diagnostic ignored "-Wsometimes-uninitialized" +#endif + +/* Allows for enum reflection support on legacy compilers */ +#if __clang_major__ < 16 +#pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +#endif + #elif defined(ECS_TARGET_GNU) #ifndef __cplusplus #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" @@ -595,6 +700,21 @@ extern "C" { * initialized), and later versions of gcc (>=11) seem to no longer throw this * warning. */ #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +/* Produces false positives in addons/cpp/delegate.hpp. */ +#pragma GCC diagnostic ignored "-Warray-bounds" +/* Produces false positives in queries/src/cache.c */ +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#pragma GCC diagnostic ignored "-Wrestrict" + +#elif defined(ECS_TARGET_MSVC) +/* recursive on all control paths, function will cause runtime stack overflow + * This warning is incorrectly thrown on enum reflection code. */ +#pragma warning(disable: 4717) +#endif + +/* Allows for enum reflection support on legacy compilers */ +#if defined(__GNUC__) && __GNUC__ <= 10 +#pragma GCC diagnostic ignored "-Wconversion" #endif /* Standard library dependencies */ @@ -656,7 +776,7 @@ extern "C" { #endif /* Some symbols are only exported when building in debug build, to enable - * whitebox testing of internal datastructures */ + * white-box testing of internal data structures */ #ifndef FLECS_NDEBUG #define FLECS_DBG_API FLECS_API #else @@ -692,6 +812,20 @@ typedef uint16_t ecs_flags16_t; typedef uint32_t ecs_flags32_t; typedef uint64_t ecs_flags64_t; +/* Bitmask type with compile-time defined size */ +#define ecs_flagsn_t_(bits) ecs_flags##bits##_t +#define ecs_flagsn_t(bits) ecs_flagsn_t_(bits) + +/* Bitset type that can store exactly as many bits as there are terms */ +#define ecs_termset_t ecs_flagsn_t(FLECS_TERM_COUNT_MAX) + +/* Utility macro's for setting/clearing termset bits */ +#define ECS_TERMSET_SET(set, flag) ((set) |= (ecs_termset_t)(flag)) +#define ECS_TERMSET_CLEAR(set, flag) ((set) &= (ecs_termset_t)~(flag)) +#define ECS_TERMSET_COND(set, flag, cond) ((cond) \ + ? (ECS_TERMSET_SET(set, flag)) \ + : (ECS_TERMSET_CLEAR(set, flag))) + /* Keep unsigned integers out of the codebase as they do more harm than good */ typedef int32_t ecs_size_t; @@ -707,6 +841,8 @@ typedef struct ecs_allocator_t ecs_allocator_t; #define ECS_ALIGNOF(T) (int64_t)__alignof(T) #elif defined(ECS_TARGET_GNU) #define ECS_ALIGNOF(T) (int64_t)__alignof__(T) +#elif defined(ECS_TARGET_CLANG) +#define ECS_ALIGNOF(T) (int64_t)__alignof__(T) #else #define ECS_ALIGNOF(T) ((int64_t)&((struct { char c; T d; } *)0)->d) #endif @@ -757,6 +893,11 @@ typedef struct ecs_allocator_t ecs_allocator_t; #define ECS_EQZERO(a) ECS_EQ(a, (uint64_t){0}) #define ECS_NEQZERO(a) ECS_NEQ(a, (uint64_t){0}) +/* Utilities to convert flecs version to string */ +#define FLECS_VERSION_IMPLSTR(major, minor, patch) #major "." #minor "." #patch +#define FLECS_VERSION_IMPL(major, minor, patch) \ + FLECS_VERSION_IMPLSTR(major, minor, patch) + #define ECS_CONCAT(a, b) a ## b //////////////////////////////////////////////////////////////////////////////// @@ -766,11 +907,7 @@ typedef struct ecs_allocator_t ecs_allocator_t; /* Magic number to identify the type of the object */ #define ecs_world_t_magic (0x65637377) #define ecs_stage_t_magic (0x65637373) -#define ecs_query_t_magic (0x65637371) -#define ecs_rule_t_magic (0x65637375) -#define ecs_table_t_magic (0x65637374) -#define ecs_filter_t_magic (0x65637366) -#define ecs_trigger_t_magic (0x65637372) +#define ecs_query_t_magic (0x65637375) #define ecs_observer_t_magic (0x65637362) @@ -796,6 +933,8 @@ typedef struct ecs_allocator_t ecs_allocator_t; #define ECS_PAIR_SECOND(e) (ecs_entity_t_lo(e)) #define ECS_HAS_RELATION(e, rel) (ECS_HAS_ID_FLAG(e, PAIR) && (ECS_PAIR_FIRST(e) == rel)) +#define ECS_TERM_REF_FLAGS(ref) ((ref)->id & EcsTermRefFlags) +#define ECS_TERM_REF_ID(ref) ((ref)->id & ~EcsTermRefFlags) //////////////////////////////////////////////////////////////////////////////// //// Convert between C typenames and variables @@ -818,9 +957,9 @@ typedef struct ecs_allocator_t ecs_allocator_t; #define ecs_pair_first(world, pair) ecs_get_alive(world, ECS_PAIR_FIRST(pair)) #define ecs_pair_second(world, pair) ecs_get_alive(world, ECS_PAIR_SECOND(pair)) #define ecs_pair_relation ecs_pair_first -#define ecs_pair_object ecs_pair_second +#define ecs_pair_target ecs_pair_second -#define ecs_poly_id(tag) ecs_pair(ecs_id(EcsPoly), tag) +#define flecs_poly_id(tag) ecs_pair(ecs_id(EcsPoly), tag) //////////////////////////////////////////////////////////////////////////////// @@ -828,7 +967,7 @@ typedef struct ecs_allocator_t ecs_allocator_t; //////////////////////////////////////////////////////////////////////////////// #ifndef FLECS_NDEBUG -#define ECS_TABLE_LOCK(world, table) ecs_table_lock(world, table) +#define ECS_TABLE_LOCK(world, table) ecs_table_lock(world, table) #define ECS_TABLE_UNLOCK(world, table) ecs_table_unlock(world, table) #else #define ECS_TABLE_LOCK(world, table) @@ -914,7 +1053,7 @@ typedef struct ecs_allocator_t ecs_allocator_t; {\ for (int32_t i = 0; i < _it->count; i ++) {\ ecs_entity_t entity = _it->entities[i];\ - type *var = &((type*)_it->ptrs[0])[i];\ + type *var = ecs_field(_it, type, 0);\ (void)entity;\ (void)var;\ __VA_ARGS__\ @@ -953,7 +1092,7 @@ typedef struct ecs_vec_t { } ecs_vec_t; FLECS_API -ecs_vec_t* ecs_vec_init( +void ecs_vec_init( struct ecs_allocator_t *allocator, ecs_vec_t *vec, ecs_size_t size, @@ -1023,6 +1162,15 @@ ecs_vec_t ecs_vec_copy( #define ecs_vec_copy_t(allocator, vec, T) \ ecs_vec_copy(allocator, vec, ECS_SIZEOF(T)) +FLECS_API +ecs_vec_t ecs_vec_copy_shrink( + struct ecs_allocator_t *allocator, + const ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_copy_shrink_t(allocator, vec, T) \ + ecs_vec_copy_shrink(allocator, vec, ECS_SIZEOF(T)) + FLECS_API void ecs_vec_reclaim( struct ecs_allocator_t *allocator, @@ -1128,7 +1276,7 @@ void* ecs_vec_last( } #endif -#endif +#endif /** * @file sparse.h @@ -1146,6 +1294,12 @@ extern "C" { /** The number of elements in a single page */ #define FLECS_SPARSE_PAGE_SIZE (1 << FLECS_SPARSE_PAGE_BITS) +/** Compute the page index from an id by stripping the first 12 bits */ +#define FLECS_SPARSE_PAGE(index) ((int32_t)((uint32_t)index >> FLECS_SPARSE_PAGE_BITS)) + +/** This computes the offset of an index inside a page */ +#define FLECS_SPARSE_OFFSET(index) ((int32_t)index & (FLECS_SPARSE_PAGE_SIZE - 1)) + typedef struct ecs_sparse_t { ecs_vec_t dense; /* Dense array with indices to sparse array. The * dense array stores both alive and not alive @@ -1163,13 +1317,13 @@ typedef struct ecs_sparse_t { /** Initialize sparse set */ FLECS_DBG_API void flecs_sparse_init( - ecs_sparse_t *sparse, + ecs_sparse_t *result, struct ecs_allocator_t *allocator, struct ecs_block_allocator_t *page_allocator, - ecs_size_t elem_size); + ecs_size_t size); -#define flecs_sparse_init_t(sparse, allocator, page_allocator, T)\ - flecs_sparse_init(sparse, allocator, page_allocator, ECS_SIZEOF(T)) +#define flecs_sparse_init_t(result, allocator, page_allocator, T)\ + flecs_sparse_init(result, allocator, page_allocator, ECS_SIZEOF(T)) FLECS_DBG_API void flecs_sparse_fini( @@ -1209,6 +1363,13 @@ void flecs_sparse_remove( #define flecs_sparse_remove_t(sparse, T, id)\ flecs_sparse_remove(sparse, ECS_SIZEOF(T), id) +/** Remove an element without liveliness checking */ +FLECS_DBG_API +void* flecs_sparse_remove_fast( + ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index); + /** Test if id is alive, which requires the generation count to match. */ FLECS_DBG_API bool flecs_sparse_is_alive( @@ -1288,8 +1449,8 @@ const uint64_t* flecs_sparse_ids( const ecs_sparse_t *sparse); /* Publicly exposed APIs - * The flecs_ functions aren't exposed directly as this can cause some - * optimizers to not consider them for link time optimization. */ + * These APIs are not part of the public API and as a result may change without + * notice (though they haven't changed in a long time). */ FLECS_API void ecs_sparse_init( @@ -1315,12 +1476,6 @@ FLECS_API int32_t ecs_sparse_count( const ecs_sparse_t *sparse); -/** Override the generation count for a specific id */ -FLECS_API -void flecs_sparse_set_generation( - ecs_sparse_t *sparse, - uint64_t id); - FLECS_API void* ecs_sparse_get_dense( const ecs_sparse_t *sparse, @@ -1414,6 +1569,12 @@ void flecs_bfree( ecs_block_allocator_t *allocator, void *memory); +FLECS_API +void flecs_bfree_w_dbg_info( + ecs_block_allocator_t *allocator, + void *memory, + const char *type_name); + FLECS_API void* flecs_brealloc( ecs_block_allocator_t *dst, @@ -1427,6 +1588,100 @@ void* flecs_bdup( #endif +/** + * @file datastructures/stack_allocator.h + * @brief Stack allocator. + */ + +#ifndef FLECS_STACK_ALLOCATOR_H +#define FLECS_STACK_ALLOCATOR_H + +/** Stack allocator for quick allocation of small temporary values */ +#define ECS_STACK_PAGE_SIZE (4096) + +typedef struct ecs_stack_page_t { + void *data; + struct ecs_stack_page_t *next; + int16_t sp; + uint32_t id; +} ecs_stack_page_t; + +typedef struct ecs_stack_cursor_t { + struct ecs_stack_cursor_t *prev; + struct ecs_stack_page_t *page; + int16_t sp; + bool is_free; +#ifdef FLECS_DEBUG + struct ecs_stack_t *owner; +#endif +} ecs_stack_cursor_t; + +typedef struct ecs_stack_t { + ecs_stack_page_t first; + ecs_stack_page_t *tail_page; + ecs_stack_cursor_t *tail_cursor; +#ifdef FLECS_DEBUG + int32_t cursor_count; +#endif +} ecs_stack_t; + +FLECS_DBG_API +void flecs_stack_init( + ecs_stack_t *stack); + +FLECS_DBG_API +void flecs_stack_fini( + ecs_stack_t *stack); + +FLECS_DBG_API +void* flecs_stack_alloc( + ecs_stack_t *stack, + ecs_size_t size, + ecs_size_t align); + +#define flecs_stack_alloc_t(stack, T)\ + flecs_stack_alloc(stack, ECS_SIZEOF(T), ECS_ALIGNOF(T)) + +#define flecs_stack_alloc_n(stack, T, count)\ + flecs_stack_alloc(stack, ECS_SIZEOF(T) * count, ECS_ALIGNOF(T)) + +FLECS_DBG_API +void* flecs_stack_calloc( + ecs_stack_t *stack, + ecs_size_t size, + ecs_size_t align); + +#define flecs_stack_calloc_t(stack, T)\ + flecs_stack_calloc(stack, ECS_SIZEOF(T), ECS_ALIGNOF(T)) + +#define flecs_stack_calloc_n(stack, T, count)\ + flecs_stack_calloc(stack, ECS_SIZEOF(T) * count, ECS_ALIGNOF(T)) + +FLECS_DBG_API +void flecs_stack_free( + void *ptr, + ecs_size_t size); + +#define flecs_stack_free_t(ptr, T)\ + flecs_stack_free(ptr, ECS_SIZEOF(T)) + +#define flecs_stack_free_n(ptr, T, count)\ + flecs_stack_free(ptr, ECS_SIZEOF(T) * count) + +void flecs_stack_reset( + ecs_stack_t *stack); + +FLECS_DBG_API +ecs_stack_cursor_t* flecs_stack_get_cursor( + ecs_stack_t *stack); + +FLECS_DBG_API +void flecs_stack_restore_cursor( + ecs_stack_t *stack, + ecs_stack_cursor_t *cursor); + +#endif + /** * @file map.h * @brief Map data structure. @@ -1620,6 +1875,87 @@ void ecs_map_copy( #endif +/** + * @file switch_list.h + * @brief Interleaved linked list for storing mutually exclusive values. + */ + +#ifndef FLECS_SWITCH_LIST_H +#define FLECS_SWITCH_LIST_H + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ecs_switch_node_t { + uint32_t next; /* Next node in list */ + uint32_t prev; /* Prev node in list */ +} ecs_switch_node_t; + +typedef struct ecs_switch_page_t { + ecs_vec_t nodes; /* vec */ + ecs_vec_t values; /* vec */ +} ecs_switch_page_t; + +typedef struct ecs_switch_t { + ecs_map_t hdrs; /* map */ + ecs_vec_t pages; /* vec */ +} ecs_switch_t; + +/** Init new switch. */ +FLECS_DBG_API +void flecs_switch_init( + ecs_switch_t* sw, + ecs_allocator_t *allocator); + +/** Fini switch. */ +FLECS_DBG_API +void flecs_switch_fini( + ecs_switch_t *sw); + +/** Set value of element. */ +FLECS_DBG_API +bool flecs_switch_set( + ecs_switch_t *sw, + uint32_t element, + uint64_t value); + +/** Reset value of element. */ +FLECS_DBG_API +bool flecs_switch_reset( + ecs_switch_t *sw, + uint32_t element); + +/** Get value for element. */ +FLECS_DBG_API +uint64_t flecs_switch_get( + const ecs_switch_t *sw, + uint32_t element); + +/** Get first element for value. */ +FLECS_DBG_API +uint32_t flecs_switch_first( + const ecs_switch_t *sw, + uint64_t value); + +/** Get next element. */ +FLECS_DBG_API +uint32_t flecs_switch_next( + const ecs_switch_t *sw, + uint32_t previous); + +/** Get target iterator. */ +FLECS_DBG_API +ecs_map_iter_t flecs_switch_targets( + const ecs_switch_t *sw); + +#ifdef __cplusplus +} +#endif + +#endif + /** * @file allocator.h * @brief Allocator that returns memory objects of any size. @@ -1678,9 +2014,13 @@ void* flecs_dup( #define flecs_calloc_t(a, T) flecs_calloc(a, ECS_SIZEOF(T)) #define flecs_calloc_n(a, T, count) flecs_calloc(a, ECS_SIZEOF(T) * (count)) -#define flecs_free(a, size, ptr) flecs_bfree(flecs_allocator_get(a, size), ptr) -#define flecs_free_t(a, T, ptr) flecs_free(a, ECS_SIZEOF(T), ptr) -#define flecs_free_n(a, T, count, ptr) flecs_free(a, ECS_SIZEOF(T) * (count), ptr) +#define flecs_free(a, size, ptr)\ + flecs_bfree((ptr) ? flecs_allocator_get(a, size) : NULL, ptr) +#define flecs_free_t(a, T, ptr)\ + flecs_bfree_w_dbg_info((ptr) ? flecs_allocator_get(a, ECS_SIZEOF(T)) : NULL, ptr, #T) +#define flecs_free_n(a, T, count, ptr)\ + flecs_bfree_w_dbg_info((ptr) ? flecs_allocator_get(a, ECS_SIZEOF(T) * (count)) : NULL\ + , ptr, #T) #define flecs_realloc(a, size_dst, size_src, ptr)\ flecs_brealloc(flecs_allocator_get(a, size_dst),\ @@ -1712,25 +2052,9 @@ extern "C" { #else #define ECS_STRBUF_INIT (ecs_strbuf_t){0} #endif -#define ECS_STRBUF_ELEMENT_SIZE (511) -#define ECS_STRBUF_MAX_LIST_DEPTH (32) - -typedef struct ecs_strbuf_element { - bool buffer_embedded; - int32_t pos; - char *buf; - struct ecs_strbuf_element *next; -} ecs_strbuf_element; -typedef struct ecs_strbuf_element_embedded { - ecs_strbuf_element super; - char buf[ECS_STRBUF_ELEMENT_SIZE + 1]; -} ecs_strbuf_element_embedded; - -typedef struct ecs_strbuf_element_str { - ecs_strbuf_element super; - char *alloc_str; -} ecs_strbuf_element_str; +#define ECS_STRBUF_SMALL_STRING_SIZE (512) +#define ECS_STRBUF_MAX_LIST_DEPTH (32) typedef struct ecs_strbuf_list_elem { int32_t count; @@ -1738,40 +2062,20 @@ typedef struct ecs_strbuf_list_elem { } ecs_strbuf_list_elem; typedef struct ecs_strbuf_t { - /* When set by an application, append will write to this buffer */ - char *buf; - - /* The maximum number of characters that may be printed */ - int32_t max; - - /* Size of elements minus current element */ - int32_t size; - - /* The number of elements in use */ - int32_t elementCount; - - /* Always allocate at least one element */ - ecs_strbuf_element_embedded firstElement; - - /* The current element being appended to */ - ecs_strbuf_element *current; + char *content; + ecs_size_t length; + ecs_size_t size; - /* Stack that keeps track of number of list elements, used for conditionally - * inserting a separator */ ecs_strbuf_list_elem list_stack[ECS_STRBUF_MAX_LIST_DEPTH]; int32_t list_sp; - /* This is set to the output string after calling ecs_strbuf_get */ - char *content; - - /* This is set to the output string length after calling ecs_strbuf_get */ - int32_t length; + char small_string[ECS_STRBUF_SMALL_STRING_SIZE]; } ecs_strbuf_t; /* Append format string to a buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_append( +void ecs_strbuf_append( ecs_strbuf_t *buffer, const char *fmt, ...); @@ -1779,7 +2083,7 @@ bool ecs_strbuf_append( /* Append format string with argument list to a buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_vappend( +void ecs_strbuf_vappend( ecs_strbuf_t *buffer, const char *fmt, va_list args); @@ -1787,28 +2091,28 @@ bool ecs_strbuf_vappend( /* Append string to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendstr( +void ecs_strbuf_appendstr( ecs_strbuf_t *buffer, const char *str); /* Append character to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendch( +void ecs_strbuf_appendch( ecs_strbuf_t *buffer, char ch); /* Append int to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendint( +void ecs_strbuf_appendint( ecs_strbuf_t *buffer, int64_t v); /* Append float to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendflt( +void ecs_strbuf_appendflt( ecs_strbuf_t *buffer, double v, char nan_delim); @@ -1816,63 +2120,33 @@ bool ecs_strbuf_appendflt( /* Append boolean to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendbool( +void ecs_strbuf_appendbool( ecs_strbuf_t *buffer, bool v); /* Append source buffer to destination buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_mergebuff( +void ecs_strbuf_mergebuff( ecs_strbuf_t *dst_buffer, ecs_strbuf_t *src_buffer); -/* Append string to buffer, transfer ownership to buffer. - * Returns false when max is reached, true when there is still space */ -FLECS_API -bool ecs_strbuf_appendstr_zerocpy( - ecs_strbuf_t *buffer, - char *str); - -/* Append string to buffer, transfer ownership to buffer. - * Returns false when max is reached, true when there is still space */ -FLECS_API -bool ecs_strbuf_appendstr_zerocpyn( - ecs_strbuf_t *buffer, - char *str, - int32_t n); - -/* Append string to buffer, do not free/modify string. - * Returns false when max is reached, true when there is still space */ -FLECS_API -bool ecs_strbuf_appendstr_zerocpy_const( - ecs_strbuf_t *buffer, - const char *str); - -/* Append string to buffer, transfer ownership to buffer. - * Returns false when max is reached, true when there is still space */ -FLECS_API -bool ecs_strbuf_appendstr_zerocpyn_const( - ecs_strbuf_t *buffer, - const char *str, - int32_t n); - /* Append n characters to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendstrn( +void ecs_strbuf_appendstrn( ecs_strbuf_t *buffer, const char *str, int32_t n); /* Return result string */ FLECS_API -char *ecs_strbuf_get( +char* ecs_strbuf_get( ecs_strbuf_t *buffer); /* Return small string from first element (appends \0) */ FLECS_API -char *ecs_strbuf_get_small( +char* ecs_strbuf_get_small( ecs_strbuf_t *buffer); /* Reset buffer without returning a string */ @@ -1900,26 +2174,26 @@ void ecs_strbuf_list_next( /* Append character to as new element in list. */ FLECS_API -bool ecs_strbuf_list_appendch( +void ecs_strbuf_list_appendch( ecs_strbuf_t *buffer, char ch); /* Append formatted string as a new element in list */ FLECS_API -bool ecs_strbuf_list_append( +void ecs_strbuf_list_append( ecs_strbuf_t *buffer, const char *fmt, ...); /* Append string as a new element in list */ FLECS_API -bool ecs_strbuf_list_appendstr( +void ecs_strbuf_list_appendstr( ecs_strbuf_t *buffer, const char *str); /* Append string as a new element in list */ FLECS_API -bool ecs_strbuf_list_appendstrn( +void ecs_strbuf_list_appendstrn( ecs_strbuf_t *buffer, const char *str, int32_t n); @@ -1944,11 +2218,11 @@ int32_t ecs_strbuf_written( * @file os_api.h * @brief Operating system abstraction API. * - * This file contains the operating system abstraction API. The flecs core + * This file contains the operating system abstraction API. The flecs core * library avoids OS/runtime specific API calls as much as possible. Instead it * provides an interface that can be implemented by applications. * - * Examples for how to implement this interface can be found in the + * Examples for how to implement this interface can be found in the * examples/os_api folder. */ @@ -1957,14 +2231,15 @@ int32_t ecs_strbuf_written( /** * @defgroup c_os_api OS API - * @brief Interface for providing OS specific functionality. - * - * \ingroup c + * @ingroup c + * Interface for providing OS specific functionality. + * * @{ */ #include #include +#include #if defined(ECS_TARGET_WINDOWS) #include @@ -1978,152 +2253,176 @@ int32_t ecs_strbuf_written( extern "C" { #endif +/** Time type. */ typedef struct ecs_time_t { - uint32_t sec; - uint32_t nanosec; + uint32_t sec; /**< Second part. */ + uint32_t nanosec; /**< Nanosecond part. */ } ecs_time_t; /* Allocation counters */ -extern int64_t ecs_os_api_malloc_count; -extern int64_t ecs_os_api_realloc_count; -extern int64_t ecs_os_api_calloc_count; -extern int64_t ecs_os_api_free_count; +extern int64_t ecs_os_api_malloc_count; /**< malloc count. */ +extern int64_t ecs_os_api_realloc_count; /**< realloc count. */ +extern int64_t ecs_os_api_calloc_count; /**< calloc count. */ +extern int64_t ecs_os_api_free_count; /**< free count. */ /* Use handle types that _at least_ can store pointers */ -typedef uintptr_t ecs_os_thread_t; -typedef uintptr_t ecs_os_cond_t; -typedef uintptr_t ecs_os_mutex_t; -typedef uintptr_t ecs_os_dl_t; -typedef uintptr_t ecs_os_sock_t; +typedef uintptr_t ecs_os_thread_t; /**< OS thread. */ +typedef uintptr_t ecs_os_cond_t; /**< OS cond. */ +typedef uintptr_t ecs_os_mutex_t; /**< OS mutex. */ +typedef uintptr_t ecs_os_dl_t; /**< OS dynamic library. */ +typedef uintptr_t ecs_os_sock_t; /**< OS socket. */ -/* 64 bit thread id */ +/** 64 bit thread id. */ typedef uint64_t ecs_os_thread_id_t; -/* Generic function pointer type */ +/** Generic function pointer type. */ typedef void (*ecs_os_proc_t)(void); -/* OS API init */ -typedef +/** OS API init. */ +typedef void (*ecs_os_api_init_t)(void); -/* OS API deinit */ -typedef +/** OS API deinit. */ +typedef void (*ecs_os_api_fini_t)(void); -/* Memory management */ -typedef +/** OS API malloc function type. */ +typedef void* (*ecs_os_api_malloc_t)( ecs_size_t size); -typedef +/** OS API free function type. */ +typedef void (*ecs_os_api_free_t)( void *ptr); +/** OS API realloc function type. */ typedef void* (*ecs_os_api_realloc_t)( - void *ptr, + void *ptr, ecs_size_t size); +/** OS API calloc function type. */ typedef void* (*ecs_os_api_calloc_t)( ecs_size_t size); +/** OS API strdup function type. */ typedef char* (*ecs_os_api_strdup_t)( const char *str); -/* Threads */ +/** OS API thread_callback function type. */ typedef void* (*ecs_os_thread_callback_t)( void*); +/** OS API thread_new function type. */ typedef ecs_os_thread_t (*ecs_os_api_thread_new_t)( ecs_os_thread_callback_t callback, void *param); +/** OS API thread_join function type. */ typedef void* (*ecs_os_api_thread_join_t)( ecs_os_thread_t thread); +/** OS API thread_self function type. */ typedef ecs_os_thread_id_t (*ecs_os_api_thread_self_t)(void); -/* Tasks */ +/** OS API task_new function type. */ typedef ecs_os_thread_t (*ecs_os_api_task_new_t)( ecs_os_thread_callback_t callback, void *param); +/** OS API task_join function type. */ typedef void* (*ecs_os_api_task_join_t)( ecs_os_thread_t thread); /* Atomic increment / decrement */ +/** OS API ainc function type. */ typedef int32_t (*ecs_os_api_ainc_t)( int32_t *value); +/** OS API lainc function type. */ typedef int64_t (*ecs_os_api_lainc_t)( int64_t *value); /* Mutex */ +/** OS API mutex_new function type. */ typedef ecs_os_mutex_t (*ecs_os_api_mutex_new_t)( void); +/** OS API mutex_lock function type. */ typedef void (*ecs_os_api_mutex_lock_t)( ecs_os_mutex_t mutex); +/** OS API mutex_unlock function type. */ typedef void (*ecs_os_api_mutex_unlock_t)( ecs_os_mutex_t mutex); +/** OS API mutex_free function type. */ typedef void (*ecs_os_api_mutex_free_t)( ecs_os_mutex_t mutex); /* Condition variable */ +/** OS API cond_new function type. */ typedef ecs_os_cond_t (*ecs_os_api_cond_new_t)( void); +/** OS API cond_free function type. */ typedef void (*ecs_os_api_cond_free_t)( ecs_os_cond_t cond); +/** OS API cond_signal function type. */ typedef void (*ecs_os_api_cond_signal_t)( ecs_os_cond_t cond); +/** OS API cond_broadcast function type. */ typedef void (*ecs_os_api_cond_broadcast_t)( ecs_os_cond_t cond); +/** OS API cond_wait function type. */ typedef void (*ecs_os_api_cond_wait_t)( ecs_os_cond_t cond, ecs_os_mutex_t mutex); -typedef +/** OS API sleep function type. */ +typedef void (*ecs_os_api_sleep_t)( int32_t sec, int32_t nanosec); -typedef +/** OS API enable_high_timer_resolution function type. */ +typedef void (*ecs_os_api_enable_high_timer_resolution_t)( bool enable); +/** OS API get_time function type. */ typedef void (*ecs_os_api_get_time_t)( ecs_time_t *time_out); +/** OS API now function type. */ typedef uint64_t (*ecs_os_api_now_t)(void); -/* Logging */ +/** OS API log function type. */ typedef void (*ecs_os_api_log_t)( int32_t level, /* Logging level */ @@ -2131,139 +2430,187 @@ void (*ecs_os_api_log_t)( int32_t line, /* Line it was logged */ const char *msg); -/* Application termination */ +/** OS API abort function type. */ typedef void (*ecs_os_api_abort_t)( void); -/* Dynamic libraries */ +/** OS API dlopen function type. */ typedef ecs_os_dl_t (*ecs_os_api_dlopen_t)( const char *libname); +/** OS API dlproc function type. */ typedef ecs_os_proc_t (*ecs_os_api_dlproc_t)( ecs_os_dl_t lib, const char *procname); +/** OS API dlclose function type. */ typedef void (*ecs_os_api_dlclose_t)( ecs_os_dl_t lib); +/** OS API module_to_path function type. */ typedef char* (*ecs_os_api_module_to_path_t)( const char *module_id); -/* Prefix members of struct with 'ecs_' as some system headers may define +/* Performance tracing */ +typedef void (*ecs_os_api_perf_trace_t)( + const char *filename, + size_t line, + const char *name); + +/* Prefix members of struct with 'ecs_' as some system headers may define * macros for functions like "strdup", "log" or "_free" */ +/** OS API interface. */ typedef struct ecs_os_api_t { /* API init / deinit */ - ecs_os_api_init_t init_; - ecs_os_api_fini_t fini_; + ecs_os_api_init_t init_; /**< init callback. */ + ecs_os_api_fini_t fini_; /**< fini callback. */ /* Memory management */ - ecs_os_api_malloc_t malloc_; - ecs_os_api_realloc_t realloc_; - ecs_os_api_calloc_t calloc_; - ecs_os_api_free_t free_; + ecs_os_api_malloc_t malloc_; /**< malloc callback. */ + ecs_os_api_realloc_t realloc_; /**< realloc callback. */ + ecs_os_api_calloc_t calloc_; /**< calloc callback. */ + ecs_os_api_free_t free_; /**< free callback. */ /* Strings */ - ecs_os_api_strdup_t strdup_; + ecs_os_api_strdup_t strdup_; /**< strdup callback. */ /* Threads */ - ecs_os_api_thread_new_t thread_new_; - ecs_os_api_thread_join_t thread_join_; - ecs_os_api_thread_self_t thread_self_; + ecs_os_api_thread_new_t thread_new_; /**< thread_new callback. */ + ecs_os_api_thread_join_t thread_join_; /**< thread_join callback. */ + ecs_os_api_thread_self_t thread_self_; /**< thread_self callback. */ /* Tasks */ - ecs_os_api_thread_new_t task_new_; - ecs_os_api_thread_join_t task_join_; + ecs_os_api_thread_new_t task_new_; /**< task_new callback. */ + ecs_os_api_thread_join_t task_join_; /**< task_join callback. */ /* Atomic increment / decrement */ - ecs_os_api_ainc_t ainc_; - ecs_os_api_ainc_t adec_; - ecs_os_api_lainc_t lainc_; - ecs_os_api_lainc_t ladec_; + ecs_os_api_ainc_t ainc_; /**< ainc callback. */ + ecs_os_api_ainc_t adec_; /**< adec callback. */ + ecs_os_api_lainc_t lainc_; /**< lainc callback. */ + ecs_os_api_lainc_t ladec_; /**< ladec callback. */ /* Mutex */ - ecs_os_api_mutex_new_t mutex_new_; - ecs_os_api_mutex_free_t mutex_free_; - ecs_os_api_mutex_lock_t mutex_lock_; - ecs_os_api_mutex_lock_t mutex_unlock_; + ecs_os_api_mutex_new_t mutex_new_; /**< mutex_new callback. */ + ecs_os_api_mutex_free_t mutex_free_; /**< mutex_free callback. */ + ecs_os_api_mutex_lock_t mutex_lock_; /**< mutex_lock callback. */ + ecs_os_api_mutex_lock_t mutex_unlock_; /**< mutex_unlock callback. */ /* Condition variable */ - ecs_os_api_cond_new_t cond_new_; - ecs_os_api_cond_free_t cond_free_; - ecs_os_api_cond_signal_t cond_signal_; - ecs_os_api_cond_broadcast_t cond_broadcast_; - ecs_os_api_cond_wait_t cond_wait_; + ecs_os_api_cond_new_t cond_new_; /**< cond_new callback. */ + ecs_os_api_cond_free_t cond_free_; /**< cond_free callback. */ + ecs_os_api_cond_signal_t cond_signal_; /**< cond_signal callback. */ + ecs_os_api_cond_broadcast_t cond_broadcast_; /**< cond_broadcast callback. */ + ecs_os_api_cond_wait_t cond_wait_; /**< cond_wait callback. */ /* Time */ - ecs_os_api_sleep_t sleep_; - ecs_os_api_now_t now_; - ecs_os_api_get_time_t get_time_; + ecs_os_api_sleep_t sleep_; /**< sleep callback. */ + ecs_os_api_now_t now_; /**< now callback. */ + ecs_os_api_get_time_t get_time_; /**< get_time callback. */ /* Logging */ - ecs_os_api_log_t log_; /* Logging function. The level should be interpreted as: */ - /* >0: Debug tracing. Only enabled in debug builds. */ - /* 0: Tracing. Enabled in debug/release builds. */ - /* -2: Warning. An issue occurred, but operation was successful. */ - /* -3: Error. An issue occurred, and operation was unsuccessful. */ - /* -4: Fatal. An issue occurred, and application must quit. */ + ecs_os_api_log_t log_; /**< log callback. + * The level should be interpreted as: + * >0: Debug tracing. Only enabled in debug builds. + * 0: Tracing. Enabled in debug/release builds. + * -2: Warning. An issue occurred, but operation was successful. + * -3: Error. An issue occurred, and operation was unsuccessful. + * -4: Fatal. An issue occurred, and application must quit. */ /* Application termination */ - ecs_os_api_abort_t abort_; + ecs_os_api_abort_t abort_; /**< abort callback. */ /* Dynamic library loading */ - ecs_os_api_dlopen_t dlopen_; - ecs_os_api_dlproc_t dlproc_; - ecs_os_api_dlclose_t dlclose_; + ecs_os_api_dlopen_t dlopen_; /**< dlopen callback. */ + ecs_os_api_dlproc_t dlproc_; /**< dlproc callback. */ + ecs_os_api_dlclose_t dlclose_; /**< dlclose callback. */ /* Overridable function that translates from a logical module id to a * shared library filename */ - ecs_os_api_module_to_path_t module_to_dl_; + ecs_os_api_module_to_path_t module_to_dl_; /**< module_to_dl callback. */ /* Overridable function that translates from a logical module id to a * path that contains module-specif resources or assets */ - ecs_os_api_module_to_path_t module_to_etc_; + ecs_os_api_module_to_path_t module_to_etc_; /**< module_to_etc callback. */ - /* Trace level */ - int32_t log_level_; + /* Performance tracing */ + ecs_os_api_perf_trace_t perf_trace_push_; - /* Trace indentation */ - int32_t log_indent_; + /* Performance tracing */ + ecs_os_api_perf_trace_t perf_trace_pop_; - /* Last error code */ - int32_t log_last_error_; + int32_t log_level_; /**< Tracing level. */ + int32_t log_indent_; /**< Tracing indentation level. */ + int32_t log_last_error_; /**< Last logged error code. */ + int64_t log_last_timestamp_; /**< Last logged timestamp. */ - /* Last recorded timestamp */ - int64_t log_last_timestamp_; + ecs_flags32_t flags_; /**< OS API flags */ - /* OS API flags */ - ecs_flags32_t flags_; + FILE *log_out_; /**< File used for logging output + * (hint, log_ decides where to write) */ } ecs_os_api_t; +/** Static OS API variable with configured callbacks. */ FLECS_API extern ecs_os_api_t ecs_os_api; +/** Initialize OS API. + * This operation is not usually called by an application. To override callbacks + * of the OS API, use the following pattern: + * + * @code + * ecs_os_set_api_defaults(); + * ecs_os_api_t os_api = ecs_os_get_api(); + * os_api.abort_ = my_abort; + * ecs_os_set_api(&os_api); + * @endcode + */ FLECS_API void ecs_os_init(void); +/** Deinitialize OS API. + * This operation is not usually called by an application. + */ FLECS_API void ecs_os_fini(void); +/** Override OS API. + * This overrides the OS API struct with new values for callbacks. See + * ecs_os_init() on how to use the function. + * + * @param os_api Pointer to struct with values to set. + */ FLECS_API void ecs_os_set_api( ecs_os_api_t *os_api); +/** Get OS API. + * + * @return A value with the current OS API callbacks + * @see ecs_os_init() + */ FLECS_API ecs_os_api_t ecs_os_get_api(void); +/** Set default values for OS API. + * This initializes the OS API struct with default values for callbacks like + * malloc and free. + * + * @see ecs_os_init() + */ FLECS_API void ecs_os_set_api_defaults(void); -/* Memory management */ +/** Macro utilities + * \cond + */ + +/* Memory management */ #ifndef ecs_os_malloc #define ecs_os_malloc(size) ecs_os_api.malloc_(size) #endif @@ -2314,16 +2661,16 @@ void ecs_os_set_api_defaults(void); #endif #define ecs_os_memcpy_t(ptr1, ptr2, T) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T)) -#define ecs_os_memcpy_n(ptr1, ptr2, T, count) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T) * count) +#define ecs_os_memcpy_n(ptr1, ptr2, T, count) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T) * (size_t)count) #define ecs_os_memcmp_t(ptr1, ptr2, T) ecs_os_memcmp(ptr1, ptr2, ECS_SIZEOF(T)) #define ecs_os_memmove_t(ptr1, ptr2, T) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T)) -#define ecs_os_memmove_n(ptr1, ptr2, T, count) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T) * count) +#define ecs_os_memmove_n(ptr1, ptr2, T, count) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T) * (size_t)count) #define ecs_os_memmove_t(ptr1, ptr2, T) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T)) #define ecs_os_strcmp(str1, str2) strcmp(str1, str2) #define ecs_os_memset_t(ptr, value, T) ecs_os_memset(ptr, value, ECS_SIZEOF(T)) -#define ecs_os_memset_n(ptr, value, T, count) ecs_os_memset(ptr, value, ECS_SIZEOF(T) * count) +#define ecs_os_memset_n(ptr, value, T, count) ecs_os_memset(ptr, value, ECS_SIZEOF(T) * (size_t)count) #define ecs_os_zeromem(ptr) ecs_os_memset(ptr, 0, ECS_SIZEOF(*ptr)) #define ecs_os_memdup_t(ptr, T) ecs_os_memdup(ptr, ECS_SIZEOF(T)) @@ -2334,24 +2681,16 @@ void ecs_os_set_api_defaults(void); #if !defined(ECS_TARGET_POSIX) && !defined(ECS_TARGET_MINGW) #define ecs_os_strcat(str1, str2) strcat_s(str1, INT_MAX, str2) -#define ecs_os_sprintf(ptr, ...) sprintf_s(ptr, INT_MAX, __VA_ARGS__) -#define ecs_os_vsprintf(ptr, fmt, args) vsprintf_s(ptr, INT_MAX, fmt, args) +#define ecs_os_snprintf(ptr, len, ...) sprintf_s(ptr, ECS_CAST(size_t, len), __VA_ARGS__) +#define ecs_os_vsnprintf(ptr, len, fmt, args) vsnprintf(ptr, ECS_CAST(size_t, len), fmt, args) #define ecs_os_strcpy(str1, str2) strcpy_s(str1, INT_MAX, str2) -#ifdef __cplusplus -#define ecs_os_strncpy(str1, str2, num) strncpy_s(str1, INT_MAX, str2, static_cast(num)) -#else -#define ecs_os_strncpy(str1, str2, num) strncpy_s(str1, INT_MAX, str2, (size_t)(num)) -#endif +#define ecs_os_strncpy(str1, str2, len) strncpy_s(str1, INT_MAX, str2, ECS_CAST(size_t, len)) #else #define ecs_os_strcat(str1, str2) strcat(str1, str2) -#define ecs_os_sprintf(ptr, ...) sprintf(ptr, __VA_ARGS__) -#define ecs_os_vsprintf(ptr, fmt, args) vsprintf(ptr, fmt, args) +#define ecs_os_snprintf(ptr, len, ...) snprintf(ptr, ECS_CAST(size_t, len), __VA_ARGS__) +#define ecs_os_vsnprintf(ptr, len, fmt, args) vsnprintf(ptr, ECS_CAST(size_t, len), fmt, args) #define ecs_os_strcpy(str1, str2) strcpy(str1, str2) -#ifdef __cplusplus -#define ecs_os_strncpy(str1, str2, num) strncpy(str1, str2, static_cast(num)) -#else -#define ecs_os_strncpy(str1, str2, num) strncpy(str1, str2, (size_t)(num)) -#endif +#define ecs_os_strncpy(str1, str2, len) strncpy(str1, str2, ECS_CAST(size_t, len)) #endif /* Files */ @@ -2394,28 +2733,7 @@ void ecs_os_set_api_defaults(void); #define ecs_os_now() ecs_os_api.now_() #define ecs_os_get_time(time_out) ecs_os_api.get_time_(time_out) -/* Logging */ -FLECS_API -void ecs_os_dbg(const char *file, int32_t line, const char *msg); - -FLECS_API -void ecs_os_trace(const char *file, int32_t line, const char *msg); - -FLECS_API -void ecs_os_warn(const char *file, int32_t line, const char *msg); - -FLECS_API -void ecs_os_err(const char *file, int32_t line, const char *msg); - -FLECS_API -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); - +#ifndef FLECS_DISABLE_COUNTERS #ifdef FLECS_ACCURATE_COUNTERS #define ecs_os_inc(v) (ecs_os_ainc(v)) #define ecs_os_linc(v) (ecs_os_lainc(v)) @@ -2427,6 +2745,13 @@ void ecs_os_strset(char **str, const char *value); #define ecs_os_dec(v) (--(*v)) #define ecs_os_ldec(v) (--(*v)) #endif +#else +#define ecs_os_inc(v) +#define ecs_os_linc(v) +#define ecs_os_dec(v) +#define ecs_os_ldec(v) +#endif + #ifdef ECS_TARGET_MINGW /* mingw bug: without this a conversion error is thrown, but isnan/isinf should @@ -2450,30 +2775,168 @@ void ecs_os_strset(char **str, const char *value); #define ecs_os_module_to_dl(lib) ecs_os_api.module_to_dl_(lib) #define ecs_os_module_to_etc(lib) ecs_os_api.module_to_etc_(lib) -/* Sleep with floating point time */ +/** Macro utilities + * \endcond + */ + + +/* Logging */ + +/** Log at debug level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_dbg( + const char *file, + int32_t line, + const char *msg); + +/** Log at trace level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_trace( + const char *file, + int32_t line, + const char *msg); + +/** Log at warning level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_warn( + const char *file, + int32_t line, + const char *msg); + +/** Log at error level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_err( + const char *file, + int32_t line, + const char *msg); + +/** Log at fatal level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_fatal( + const char *file, + int32_t line, + const char *msg); + +/** Convert errno to string. + * + * @param err The error number. + * @return A string describing the error. + */ +FLECS_API +const char* ecs_os_strerror( + int err); + +/** Utility for assigning strings. + * This operation frees an existing string and duplicates the input string. + * + * @param str Pointer to a string value. + * @param value The string value to assign. + */ +FLECS_API +void ecs_os_strset( + char **str, + const char *value); + +/* Profile tracing */ +#ifdef FLECS_PERF_TRACE +#define ecs_os_perf_trace_push(name) ecs_os_perf_trace_push_(__FILE__, __LINE__, name) +#define ecs_os_perf_trace_pop(name) ecs_os_perf_trace_pop_(__FILE__, __LINE__, name) +#else +#define ecs_os_perf_trace_push(name) +#define ecs_os_perf_trace_pop(name) +#endif + +void ecs_os_perf_trace_push_( + const char *file, + size_t line, + const char *name); + +void ecs_os_perf_trace_pop_( + const char *file, + size_t line, + const char *name); + +/** Sleep with floating point time. + * + * @param t The time in seconds. + */ FLECS_API void ecs_sleepf( double t); -/* Measure time since provided timestamp */ +/** Measure time since provided timestamp. + * Use with a time value initialized to 0 to obtain the number of seconds since + * the epoch. The operation will write the current timestamp in start. + * + * Usage: + * @code + * ecs_time_t t = {}; + * ecs_time_measure(&t); + * // code + * double elapsed = ecs_time_measure(&t); + * @endcode + * + * @param start The starting timestamp. + * @return The time elapsed since start. + */ FLECS_API double ecs_time_measure( ecs_time_t *start); -/* Calculate difference between two timestamps */ +/** Calculate difference between two timestamps. + * + * @param t1 The first timestamp. + * @param t2 The first timestamp. + * @return The difference between timestamps. + */ FLECS_API ecs_time_t ecs_time_sub( ecs_time_t t1, ecs_time_t t2); -/* Convert time value to a double */ +/** Convert time value to a double. + * + * @param t The timestamp. + * @return The timestamp converted to a double. + */ FLECS_API double ecs_time_to_double( ecs_time_t t); +/** Return newly allocated memory that contains a copy of src. + * + * @param src The source pointer. + * @param size The number of bytes to copy. + * @return The duplicated memory. + */ FLECS_API void* ecs_os_memdup( - const void *src, + const void *src, ecs_size_t size); /** Are heap functions available? */ @@ -2519,140 +2982,102 @@ extern "C" { /** * @defgroup api_types API types - * @brief Public API types. + * Public API types. + * * @{ */ /** * @defgroup core_types Core API Types - * @brief Types for core API objects. + * Types for core API objects. + * * @{ */ -/** Ids are the things that can be added to an entity. +/** Ids are the things that can be added to an entity. * An id can be an entity or pair, and can have optional id flags. */ typedef uint64_t ecs_id_t; /** An entity identifier. - * Entity ids consist out of a number unique to the entity in the lower 32 bits, - * and a counter used to track entity liveliness in the upper 32 bits. When an - * id is recycled, its generation count is increased. This causes recycled ids + * Entity ids consist out of a number unique to the entity in the lower 32 bits, + * and a counter used to track entity liveliness in the upper 32 bits. When an + * id is recycled, its generation count is increased. This causes recycled ids * to be very large (>4 billion), which is normal. */ typedef ecs_id_t ecs_entity_t; /** A type is a list of (component) ids. - * Types are used to communicate the "type" of an entity. In most type systems a - * typeof operation returns a single type. In ECS however, an entity can have + * Types are used to communicate the "type" of an entity. In most type systems a + * typeof operation returns a single type. In ECS however, an entity can have * multiple components, which is why an ECS type consists of a vector of ids. - * + * * The component ids of a type are sorted, which ensures that it doesn't matter * in which order components are added to an entity. For example, if adding - * Position then Velocity would result in type [Position, Velocity], first + * Position then Velocity would result in type [Position, Velocity], first * adding Velocity then Position would also result in type [Position, Velocity]. - * - * Entities are grouped together by type in the ECS storage in tables. The - * storage has exactly one table per unique type that is created by the + * + * Entities are grouped together by type in the ECS storage in tables. The + * storage has exactly one table per unique type that is created by the * application that stores all entities and components for that type. This is * also referred to as an archetype. */ typedef struct { - ecs_id_t *array; - int32_t count; + ecs_id_t *array; /**< Array with ids. */ + int32_t count; /**< Number of elements in array. */ } ecs_type_t; -/** A world is the container for all ECS data and supporting features. +/** A world is the container for all ECS data and supporting features. * Applications can have multiple worlds, though in most cases will only need * one. Worlds are isolated from each other, and can have separate sets of * systems, components, modules etc. - * - * If an application has multiple worlds with overlapping components, it is + * + * If an application has multiple worlds with overlapping components, it is * common (though not strictly required) to use the same component ids across * worlds, which can be achieved by declaring a global component id variable. * To do this in the C API, see the entities/fwd_component_decl example. The * C++ API automatically synchronizes component ids between worlds. - * + * * Component id conflicts between worlds can occur when a world has already used * an id for something else. There are a few ways to avoid this: - * + * * - Ensure to register the same components in each world, in the same order. - * - Create a dummy world in which all components are preregistered which + * - Create a dummy world in which all components are preregistered which * initializes the global id variables. - * - * In some use cases, typically when writing tests, multiple worlds are created - * and deleted with different components, registered in different order. To + * + * In some use cases, typically when writing tests, multiple worlds are created + * and deleted with different components, registered in different order. To * ensure isolation between tests, the C++ API has a `flecs::reset` function * that forces the API to ignore the old component ids. */ typedef struct ecs_world_t ecs_world_t; +/** A stage enables modification while iterating and from multiple threads */ +typedef struct ecs_stage_t ecs_stage_t; + /** A table stores entities and components for a specific type. */ typedef struct ecs_table_t ecs_table_t; /** A term is a single element in a query. */ typedef struct ecs_term_t ecs_term_t; -/** A filter is an iterable data structure that describes a query. - * Filters are used by the various query implementations in Flecs, like queries, - * observers and rules, to describe a query. Filters themselves can also be - * iterated. */ -typedef struct ecs_filter_t ecs_filter_t; - -/** A query that caches its results. - * Queries are the fastest mechanism for finding and iterating over entities. - * Queries cache results as a list of matching tables (vs. individual entities). - * - * This has several advantages: - * - Matching is only performed when new tables are created, which is infrequent - * - Iterating a query just walks over the cache, no actual searching is needed - * - Iteration is table-based, which allows for direct iteration of underlying - * component arrays, providing good cache locality. - * - * While queries are the fastest mechanism to iterate entiites, they are slower - * to create than other mechanisms, as a result of having to build the cache - * first. For this reason queries are best suited for use cases where a single - * query can be reused many times (like is the case for systems). - * - * For ad-hoc queries it is recommended to use filters or rules instead, which - * are slower to iterate, but much faster to create. Applications should at all - * times avoid frequent creation/deletion of queries. */ +/** A query returns entities matching a list of constraints. */ typedef struct ecs_query_t ecs_query_t; -/** A rule is a query with advanced graph traversal features. - * Rules are fast uncached queries with support for advanced graph features such - * as the usage of query variables. A simple example of a rule that matches all - * spaceship entities docked to a planet: - * SpaceShip, (DockedTo, $planet), Planet($planet) - * - * Here, the rule traverses the DockedTo relationship, and matches Planet on the - * target of this relationship. Through the usage of variables rules can match - * arbitrary patterns against entity graphs. Other features supported - * exclusively by rules are: - * - Component inheritance - * - Transitivity - * - * Rules have similar iteration performance to filters, but are slower than - * queries. Rules and filters will eventually be merged into a single query - * implementation. Features still lacking for rules are: - * - Up traversal - * - AndFrom, OrFrom, NotFrom operators - */ -typedef struct ecs_rule_t ecs_rule_t; - /** An observer is a system that is invoked when an event matches its query. * Observers allow applications to respond to specific events, such as adding or * removing a component. Observers are created by both specifying a query and * a list of event kinds that should be listened for. An example of an observer * that triggers when a Position component is added to an entity (in C++): - * - * world.observer() - * .event(flecs::OnAdd) - * .each([](Position& p) { - * // called when Position is added to an entity - * }); - * - * Observer queries can be as complex as filters. Observers only trigger when - * the source of the event matches the full observer query. For example, an - * OnAdd observer for Position, Velocity will only trigger after both components - * have been added to the entity. */ + * + * @code + * world.observer() + * .event(flecs::OnAdd) + * .each([](Position& p) { + * // called when Position is added to an entity + * }); + * @endcode + * + * Observers only trigger when the source of the event matches the full observer + * query. For example, an OnAdd observer for Position, Velocity will only + * trigger after both components have been added to the entity. */ typedef struct ecs_observer_t ecs_observer_t; /** An observable produces events that can be listened for by an observer. @@ -2660,30 +3085,29 @@ typedef struct ecs_observer_t ecs_observer_t; * observable objects as well. */ typedef struct ecs_observable_t ecs_observable_t; -/* Type used for iterating iterable objects. - * Iterators are a common interface across iterable objects (world, filters, - * rules, queries, systems, observers) to provide applications with information - * about the currently iterated result, and to store any state required for the +/** Type used for iterating iterable objects. + * Iterators are objects that provide applications with information + * about the currently iterated result, and store any state required for the * iteration. */ typedef struct ecs_iter_t ecs_iter_t; /** A ref is a fast way to fetch a component for a specific entity. - * Refs are a faster alternative to repeatedly calling ecs_get for the same + * Refs are a faster alternative to repeatedly calling ecs_get() for the same * entity/component combination. When comparing the performance of getting a ref - * to calling ecs_get, a ref is typically 3-5x faster. - * + * to calling ecs_get(), a ref is typically 3-5x faster. + * * Refs achieve this performance by caching internal data structures associated - * with the entity and component on the ecs_ref_t object that otherwise would + * with the entity and component on the ecs_ref_t object that otherwise would * have to be looked up. */ typedef struct ecs_ref_t ecs_ref_t; -/** Type hooks are callbacks associated with component lifecycle events. +/** Type hooks are callbacks associated with component lifecycle events. * Typical examples of lifecycle events are construction, destruction, copying * and moving of components. */ typedef struct ecs_type_hooks_t ecs_type_hooks_t; /** Type information. - * Contains information about a (component) type, such as its size and + * Contains information about a (component) type, such as its size and * alignment and type hooks. */ typedef struct ecs_type_info_t ecs_type_info_t; @@ -2693,25 +3117,19 @@ typedef struct ecs_record_t ecs_record_t; /** Information about a (component) id, such as type info and tables with the id */ typedef struct ecs_id_record_t ecs_id_record_t; -/** Information about where in a table a specific (component) id is stored. */ -typedef struct ecs_table_record_t ecs_table_record_t; - /** A poly object. * A poly (short for polymorph) object is an object that has a variable list of * capabilities, determined by a mixin table. This is the current list of types * in the flecs API that can be used as an ecs_poly_t: - * + * * - ecs_world_t * - ecs_stage_t * - ecs_query_t - * - ecs_filter_t - * - ecs_rule_t - * - (more to come) - * + * * Functions that accept an ecs_poly_t argument can accept objects of these * types. If the object does not have the requested mixin the API will throw an * assert. - * + * * The poly/mixin framework enables partially overlapping features to be * implemented once, and enables objects of different types to interact with * each other depending on what mixins they have, rather than their type @@ -2725,26 +3143,55 @@ typedef struct ecs_mixins_t ecs_mixins_t; /** Header for ecs_poly_t objects. */ typedef struct ecs_header_t { - int32_t magic; /* Magic number verifying it's a flecs object */ - int32_t type; /* Magic number indicating which type of flecs object */ - ecs_mixins_t *mixins; /* Table with offsets to (optional) mixins */ + int32_t magic; /**< Magic number verifying it's a flecs object */ + int32_t type; /**< Magic number indicating which type of flecs object */ + int32_t refcount; /**< Refcount, to enable RAII handles */ + ecs_mixins_t *mixins; /**< Table with offsets to (optional) mixins */ } ecs_header_t; +/** Record for entity index */ +struct ecs_record_t { + ecs_id_record_t *idr; /**< Id record to (*, entity) for target entities */ + ecs_table_t *table; /**< Identifies a type (and table) in world */ + uint32_t row; /**< Table row of the entity */ + int32_t dense; /**< Index in dense array of entity index */ +}; + +/** Header for table cache elements. */ +typedef struct ecs_table_cache_hdr_t { + struct ecs_table_cache_t *cache; /**< Table cache of element. Of type ecs_id_record_t* for component index elements. */ + ecs_table_t *table; /**< Table associated with element. */ + struct ecs_table_cache_hdr_t *prev, *next; /**< Next/previous elements for id in table cache. */ + bool empty; /**< Whether element is in empty list. */ +} ecs_table_cache_hdr_t; + +/** Metadata describing where a component id is stored in a table. + * This type is used as element type for the component index table cache. One + * record exists per table/component in the table. Only records for wildcard ids + * can have a count > 1. */ +typedef struct ecs_table_record_t { + ecs_table_cache_hdr_t hdr; /**< Table cache header */ + int16_t index; /**< First type index where id occurs in table */ + int16_t count; /**< Number of times id occurs in table */ + int16_t column; /**< First column index where id occurs */ +} ecs_table_record_t; + /** @} */ /** * @defgroup function_types Function types. - * @brief Function callback types. + * Function callback types. + * * @{ */ /** Function prototype for runnables (systems, observers). * The run callback overrides the default behavior for iterating through the * results of a runnable object. - * + * * The default runnable iterates the iterator, and calls an iter_action (see * below) for each returned result. - * + * * @param it The iterator to be iterated by the runnable. */ typedef void (*ecs_run_action_t)( @@ -2753,46 +3200,29 @@ typedef void (*ecs_run_action_t)( /** Function prototype for iterables. * A system may invoke a callback multiple times, typically once for each * matched table. - * + * * @param it The iterator containing the data for the current match. */ typedef void (*ecs_iter_action_t)( ecs_iter_t *it); -/** Function prototype for creating an iterator from a poly. - * Used to create iterators from poly objects with the iterable mixin. When a - * filter is provided, an array of two iterators must be passed to the function. - * This allows the mixin implementation to create a chained iterator when - * necessary, which requires two iterator objects. - * - * @param world The world or stage for which to create the iterator. - * @param iterable An iterable poly object. - * @param it The iterator to create (out parameter) - * @param filter Optional term to filter results. - */ -typedef void (*ecs_iter_init_action_t)( - const ecs_world_t *world, - const ecs_poly_t *iterable, - ecs_iter_t *it, - ecs_term_t *filter); - /** Function prototype for iterating an iterator. - * Stored inside initialized iterators. This allows an application to * iterate + * Stored inside initialized iterators. This allows an application to iterate * an iterator without needing to know what created it. - * + * * @param it The iterator to iterate. * @return True if iterator has no more results, false if it does. */ typedef bool (*ecs_iter_next_action_t)( - ecs_iter_t *it); + ecs_iter_t *it); /** Function prototype for freeing an iterator. * Free iterator resources. - * + * * @param it The iterator to free. */ typedef void (*ecs_iter_fini_action_t)( - ecs_iter_t *it); + ecs_iter_t *it); /** Callback used for comparing components */ typedef int (*ecs_order_by_action_t)( @@ -2819,13 +3249,13 @@ typedef uint64_t (*ecs_group_by_action_t)( ecs_id_t group_id, void *ctx); -/* Callback invoked when a query creates a new group. */ +/** Callback invoked when a query creates a new group. */ typedef void* (*ecs_group_create_action_t)( ecs_world_t *world, uint64_t group_id, void *group_by_ctx); /* from ecs_query_desc_t */ -/* Callback invoked when a query deletes an existing group. */ +/** Callback invoked when a query deletes an existing group. */ typedef void (*ecs_group_delete_action_t)( ecs_world_t *world, uint64_t group_id, @@ -2834,7 +3264,7 @@ typedef void (*ecs_group_delete_action_t)( /** Initialization action for modules */ typedef void (*ecs_module_action_t)( - ecs_world_t *world); + ecs_world_t *world); /** Action callback on world exit */ typedef void (*ecs_fini_action_t)( @@ -2852,7 +3282,7 @@ typedef int (*ecs_compare_action_t)( /** Callback used for hashing values */ typedef uint64_t (*ecs_hash_value_action_t)( - const void *ptr); + const void *ptr); /** Constructor/destructor callback */ typedef void (*ecs_xtor_t)( @@ -2874,29 +3304,16 @@ typedef void (*ecs_move_t)( int32_t count, const ecs_type_info_t *type_info); -/* Destructor function for poly objects */ -typedef void (*ecs_poly_dtor_t)( +/** Destructor function for poly objects. */ +typedef void (*flecs_poly_dtor_t)( ecs_poly_t *poly); /** @} */ -/** - * @defgroup mixins Poly mixin types. - * @brief Mixin types for poly mechanism. - * @{ - */ - -/** Iterable mixin. - * Allows its container to be iterated. */ -typedef struct ecs_iterable_t { - ecs_iter_init_action_t init; /**< Callback that creates iterator. */ -} ecs_iterable_t; - -/** @} */ - /** * @defgroup query_types Query descriptor types. - * @brief Types used to describe queries. + * Types used to describe queries. + * * @{ */ @@ -2904,6 +3321,7 @@ typedef struct ecs_iterable_t { typedef enum ecs_inout_kind_t { EcsInOutDefault, /**< InOut for regular terms, In for shared terms */ EcsInOutNone, /**< Term is neither read nor written */ + EcsInOutFilter, /**< Same as InOutNone + prevents term from triggering observers */ EcsInOut, /**< Term is both read and written */ EcsIn, /**< Term is only read */ EcsOut, /**< Term is only written */ @@ -2920,147 +3338,187 @@ typedef enum ecs_oper_kind_t { EcsNotFrom, /**< Term must match none of the components from term id */ } ecs_oper_kind_t; +/** Specify cache policy for query */ +typedef enum ecs_query_cache_kind_t { + EcsQueryCacheDefault, /**< Behavior determined by query creation context */ + EcsQueryCacheAuto, /**< Cache query terms that are cacheable */ + EcsQueryCacheAll, /**< Require that all query terms can be cached */ + EcsQueryCacheNone, /**< No caching */ +} ecs_query_cache_kind_t; + /* Term id flags */ -#define EcsSelf (1u << 1) /**< Match on self */ -#define EcsUp (1u << 2) /**< Match by traversing upwards */ -#define EcsDown (1u << 3) /**< Match by traversing downwards (derived, cannot be set) */ -#define EcsTraverseAll (1u << 4) /**< Match all entities encountered through traversal */ -#define EcsCascade (1u << 5) /**< Sort results breadth first */ -#define EcsDesc (1u << 6) /**< Iterate groups in descending order */ -#define EcsParent (1u << 7) /**< Short for up(ChildOf) */ -#define EcsIsVariable (1u << 8) /**< Term id is a variable */ -#define EcsIsEntity (1u << 9) /**< Term id is an entity */ -#define EcsIsName (1u << 10) /**< Term id is a name (don't attempt to lookup as entity) */ -#define EcsFilter (1u << 11) /**< Prevent observer from triggering on term */ -#define EcsTraverseFlags (EcsUp|EcsDown|EcsTraverseAll|EcsSelf|EcsCascade|EcsDesc|EcsParent) - -/* Term flags discovered & set during filter creation. Mostly used internally to - * store information relevant to queries. */ -#define EcsTermMatchAny (1u << 0) -#define EcsTermMatchAnySrc (1u << 1) -#define EcsTermSrcFirstEq (1u << 2) -#define EcsTermSrcSecondEq (1u << 3) -#define EcsTermTransitive (1u << 4) -#define EcsTermReflexive (1u << 5) -#define EcsTermIdInherited (1u << 6) -#define EcsTermIsTrivial (1u << 7) -#define EcsTermNoData (1u << 8) - -/* Term flags used for term iteration */ -#define EcsTermMatchDisabled (1u << 7) -#define EcsTermMatchPrefab (1u << 8) - -/** Type that describes a single identifier in a term */ -typedef struct ecs_term_id_t { + +/** Match on self. + * Can be combined with other term flags on the ecs_term_t::flags_ field. + * \ingroup queries + */ +#define EcsSelf (1llu << 63) + +/** Match by traversing upwards. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsUp (1llu << 62) + +/** Traverse relationship transitively. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsTrav (1llu << 61) + +/** Sort results breadth first. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsCascade (1llu << 60) + +/** Iterate groups in descending order. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsDesc (1llu << 59) + +/** Term id is a variable. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsIsVariable (1llu << 58) + +/** Term id is an entity. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsIsEntity (1llu << 57) + +/** Term id is a name (don't attempt to lookup as entity). + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsIsName (1llu << 56) + +/** All term traversal flags. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsTraverseFlags (EcsSelf|EcsUp|EcsTrav|EcsCascade|EcsDesc) + +/** All term reference kind flags. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsTermRefFlags (EcsTraverseFlags|EcsIsVariable|EcsIsEntity|EcsIsName) + +/** Type that describes a reference to an entity or variable in a term. */ +typedef struct ecs_term_ref_t { ecs_entity_t id; /**< Entity id. If left to 0 and flags does not * specify whether id is an entity or a variable - * the id will be initialized to EcsThis. + * the id will be initialized to #EcsThis. * To explicitly set the id to 0, leave the id - * member to 0 and set EcsIsEntity in flags. */ + * member to 0 and set #EcsIsEntity in flags. */ const char *name; /**< Name. This can be either the variable name - * (when the EcsIsVariable flag is set) or an + * (when the #EcsIsVariable flag is set) or an * entity name. When ecs_term_t::move is true, * the API assumes ownership over the string and * will free it when the term is destroyed. */ +} ecs_term_ref_t; - ecs_entity_t trav; /**< Relationship to traverse when looking for the - * component. The relationship must have - * the Traversable property. Default is IsA. */ - - ecs_flags32_t flags; /**< Term flags */ -} ecs_term_id_t; - -/** Type that describes a term (single element in a query) */ +/** Type that describes a term (single element in a query). */ struct ecs_term_t { ecs_id_t id; /**< Component id to be matched by term. Can be * set directly, or will be populated from the * first/second members, which provide more * flexibility. */ - ecs_term_id_t src; /**< Source of term */ - ecs_term_id_t first; /**< Component or first element of pair */ - ecs_term_id_t second; /**< Second element of pair */ - - ecs_inout_kind_t inout; /**< Access to contents matched by term */ - ecs_oper_kind_t oper; /**< Operator of term */ - - ecs_id_t id_flags; /**< Id flags of term id */ - char *name; /**< Name of term */ + ecs_term_ref_t src; /**< Source of term */ + ecs_term_ref_t first; /**< Component or first element of pair */ + ecs_term_ref_t second; /**< Second element of pair */ - int32_t field_index; /**< Index of field for term in iterator */ - ecs_id_record_t *idr; /**< Cached pointer to internal index */ + ecs_entity_t trav; /**< Relationship to traverse when looking for the + * component. The relationship must have + * the `Traversable` property. Default is `IsA`. */ - ecs_flags16_t flags; /**< Flags that help eval, set by ecs_filter_init */ + int16_t inout; /**< Access to contents matched by term */ + int16_t oper; /**< Operator of term */ - bool move; /**< Used by internals */ + int8_t field_index; /**< Index of field for term in iterator */ + ecs_flags16_t flags_; /**< Flags that help eval, set by ecs_query_init() */ }; -/** Use $this variable to initialize user-allocated filter object */ -FLECS_API extern ecs_filter_t ECS_FILTER_INIT; - -/** Filters alllow for ad-hoc quick filtering of entity tables. */ -struct ecs_filter_t { - ecs_header_t hdr; +/** Queries are lists of constraints (terms) that match entities. + * Created with ecs_query_init(). + */ +struct ecs_query_t { + ecs_header_t hdr; /**< Object header */ + + ecs_term_t terms[FLECS_TERM_COUNT_MAX]; /**< Query terms */ + int32_t sizes[FLECS_TERM_COUNT_MAX]; /**< Component sizes. Indexed by field */ + ecs_id_t ids[FLECS_TERM_COUNT_MAX]; /**< Component ids. Indexed by field */ + + ecs_flags32_t flags; /**< Query flags */ + int8_t var_count; /**< Number of query variables */ + int8_t term_count; /**< Number of query terms */ + int8_t field_count; /**< Number of fields returned by query */ + + /* Bitmasks for quick field information lookups */ + ecs_termset_t fixed_fields; /**< Fields with a fixed source */ + ecs_termset_t var_fields; /**< Fields with non-$this variable source */ + ecs_termset_t static_id_fields; /**< Fields with a static (component) id */ + ecs_termset_t data_fields; /**< Fields that have data */ + ecs_termset_t write_fields; /**< Fields that write data */ + ecs_termset_t read_fields; /**< Fields that read data */ + ecs_termset_t row_fields; /**< Fields that must be acquired with field_at */ + ecs_termset_t shared_readonly_fields; /**< Fields that don't write shared data */ + ecs_termset_t set_fields; /**< Fields that will be set */ + + ecs_query_cache_kind_t cache_kind; /**< Caching policy of query */ - int8_t term_count; /**< Number of elements in terms array */ - int8_t field_count; /**< Number of fields in iterator for filter */ - ecs_flags32_t flags; /**< Filter flags */ - ecs_flags64_t data_fields; /**< Bitset with fields that have data */ - - ecs_term_t *terms; /**< Array containing terms for filter */ - char *variable_names[1]; /**< Placeholder variable names array */ - int32_t *sizes; /**< Field size (same for each result) */ + char **vars; /**< Array with variable names for iterator */ - /* Mixins */ - ecs_entity_t entity; /**< Entity associated with filter (optional) */ - ecs_iterable_t iterable; /**< Iterable mixin */ - ecs_poly_dtor_t dtor; /**< Dtor mixin */ - ecs_world_t *world; /**< World mixin */ + void *ctx; /**< User context to pass to callback */ + void *binding_ctx; /**< Context to be used for language bindings */ + + ecs_entity_t entity; /**< Entity associated with query (optional) */ + ecs_world_t *real_world; /**< Actual world. */ + ecs_world_t *world; /**< World or stage query was created with. */ + + int32_t eval_count; /**< Number of times query is evaluated */ }; -/* An observer reacts to events matching a filter */ +/** An observer reacts to events matching a query. + * Created with ecs_observer_init(). + */ struct ecs_observer_t { - ecs_header_t hdr; + ecs_header_t hdr; /**< Object header */ - ecs_filter_t filter; /**< Query for observer */ + ecs_query_t *query; /**< Observer query */ - /* Observer events */ + /** Observer events */ ecs_entity_t events[FLECS_EVENT_DESC_MAX]; - int32_t event_count; - + int32_t event_count; /**< Number of events */ + ecs_iter_action_t callback; /**< See ecs_observer_desc_t::callback */ ecs_run_action_t run; /**< See ecs_observer_desc_t::run */ - void *ctx; /**< Callback context */ - void *binding_ctx; /**< Binding context (for language bindings) */ + void *ctx; /**< Observer context */ + void *callback_ctx; /**< Callback language binding context */ + void *run_ctx; /**< Run language binding context */ ecs_ctx_free_t ctx_free; /**< Callback to free ctx */ - ecs_ctx_free_t binding_ctx_free; /**< Callback to free binding_ctx */ + ecs_ctx_free_t callback_ctx_free; /**< Callback to free callback_ctx */ + ecs_ctx_free_t run_ctx_free; /**< Callback to free run_ctx */ 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) */ - - bool is_monitor; /**< If true, the observer only triggers when the - * filter did not match with the entity before - * the event happened. */ - - bool is_multi; /**< If true, the observer triggers on more than one term */ - - /* Mixins */ - ecs_poly_dtor_t dtor; + ecs_world_t *world; /**< The world */ + ecs_entity_t entity; /**< Entity associated with observer */ }; /** @} */ -/** Type that contains component lifecycle callbacks. - * - * \ingroup components +/** Type that contains component lifecycle callbacks. + * + * @ingroup components */ struct ecs_type_hooks_t { ecs_xtor_t ctor; /**< ctor */ @@ -3095,21 +3553,24 @@ struct ecs_type_hooks_t { * to respond to changes on itself before others can. */ ecs_iter_action_t on_set; - /** Callback that is invoked when an instance of the component is removed. + /** Callback that is invoked when an instance of the component is removed. * This callback is invoked after the triggers are invoked, and before the * destructor is invoked. */ ecs_iter_action_t on_remove; - void *ctx; /**< User defined context */ - void *binding_ctx; /**< Language binding context */ + void *ctx; /**< User defined context */ + void *binding_ctx; /**< Language binding context */ + void *lifecycle_ctx; /**< Component lifecycle context (see meta add-on)*/ + + ecs_ctx_free_t ctx_free; /**< Callback to free ctx */ + ecs_ctx_free_t binding_ctx_free; /**< Callback to free binding_ctx */ + ecs_ctx_free_t lifecycle_ctx_free; /**< Callback to free lifecycle_ctx */ - ecs_ctx_free_t ctx_free; /**< Callback to free ctx */ - ecs_ctx_free_t binding_ctx_free; /**< Callback to free binding_ctx */ }; -/** Type that contains component information (passed to ctors/dtors/...) - * - * \ingroup components +/** Type that contains component information (passed to ctors/dtors/...) + * + * @ingroup components */ struct ecs_type_info_t { ecs_size_t size; /**< Size of type */ @@ -3140,23 +3601,16 @@ extern "C" { //// Opaque types //////////////////////////////////////////////////////////////////////////////// -/** A stage enables modification while iterating and from multiple threads */ -typedef struct ecs_stage_t ecs_stage_t; - /** Table data */ typedef struct ecs_data_t ecs_data_t; -/* Switch list */ -typedef struct ecs_switch_t ecs_switch_t; - /* Cached query table data */ -typedef struct ecs_query_table_match_t ecs_query_table_match_t; +typedef struct ecs_query_cache_table_match_t ecs_query_cache_table_match_t; //////////////////////////////////////////////////////////////////////////////// //// Non-opaque types //////////////////////////////////////////////////////////////////////////////// -/** Mixin for emitting events to triggers/observers */ /** All observers for a specific event */ typedef struct ecs_event_record_t { struct ecs_event_id_record_t *any; @@ -3170,19 +3624,10 @@ struct ecs_observable_t { ecs_event_record_t on_add; ecs_event_record_t on_remove; ecs_event_record_t on_set; - ecs_event_record_t un_set; ecs_event_record_t on_wildcard; ecs_sparse_t events; /* sparse */ }; -/** Record for entity index */ -struct ecs_record_t { - ecs_id_record_t *idr; /* Id record to (*, entity) for target entities */ - ecs_table_t *table; /* Identifies a type (and table) in world */ - uint32_t row; /* Table row of the entity */ - int32_t dense; /* Index in dense array */ -}; - /** Range in table */ typedef struct ecs_table_range_t { ecs_table_t *table; @@ -3205,22 +3650,11 @@ typedef struct ecs_var_t { struct ecs_ref_t { ecs_entity_t entity; /* Entity */ ecs_entity_t id; /* Component id */ + uint64_t table_id; /* Table id for detecting ABA issues */ struct ecs_table_record_t *tr; /* Table record for component */ ecs_record_t *record; /* Entity index record */ }; -/* Cursor to stack allocator. Type is public to allow for white box testing. */ -struct ecs_stack_page_t; - -typedef struct ecs_stack_cursor_t { - struct ecs_stack_cursor_t *prev; - struct ecs_stack_page_t *page; - int16_t sp; - bool is_free; -#ifdef FLECS_DEBUG - struct ecs_stack_t *owner; -#endif -} ecs_stack_cursor_t; /* Page-iterator specific data */ typedef struct ecs_page_iter_t { @@ -3241,96 +3675,47 @@ typedef struct ecs_table_cache_iter_t { struct ecs_table_cache_hdr_t *next_list; } ecs_table_cache_iter_t; -/** Term-iterator specific data */ -typedef struct ecs_term_iter_t { - ecs_term_t term; - ecs_id_record_t *self_index; - ecs_id_record_t *set_index; - - ecs_id_record_t *cur; +/** Each iterator */ +typedef struct ecs_each_iter_t { ecs_table_cache_iter_t it; - int32_t index; - int32_t observed_table_count; - - ecs_table_t *table; - int32_t cur_match; - int32_t match_count; - int32_t last_column; - bool empty_tables; + /* Storage for iterator fields */ + ecs_id_t ids; + ecs_entity_t sources; + ecs_size_t sizes; + int32_t columns; + const ecs_table_record_t* trs; +} ecs_each_iter_t; - /* Storage */ - ecs_id_t id; - int32_t column; - ecs_entity_t subject; - ecs_size_t size; - void *ptr; -} ecs_term_iter_t; - -typedef enum ecs_iter_kind_t { - EcsIterEvalCondition, - EcsIterEvalTables, - EcsIterEvalChain, - EcsIterEvalNone -} ecs_iter_kind_t; - -/** Filter-iterator specific data */ -typedef struct ecs_filter_iter_t { - const ecs_filter_t *filter; - ecs_iter_kind_t kind; - ecs_term_iter_t term_iter; - int32_t matches_left; - int32_t pivot_term; -} ecs_filter_iter_t; - -/** Query-iterator specific data */ +typedef struct ecs_query_op_profile_t { + int32_t count[2]; /* 0 = enter, 1 = redo */ +} ecs_query_op_profile_t; + +/** Query iterator */ typedef struct ecs_query_iter_t { - ecs_query_t *query; - ecs_query_table_match_t *node, *prev, *last; - int32_t sparse_smallest; - int32_t sparse_first; - int32_t bitset_first; + const ecs_query_t *query; + struct ecs_var_t *vars; /* Variable storage */ + const struct ecs_query_var_t *query_vars; + const struct ecs_query_op_t *ops; + struct ecs_query_op_ctx_t *op_ctx; /* Operation-specific state */ + ecs_query_cache_table_match_t *node, *prev, *last; /* For cached iteration */ + uint64_t *written; int32_t skip_count; -} ecs_query_iter_t; - -/** Snapshot-iterator specific data */ -typedef struct ecs_snapshot_iter_t { - ecs_filter_t filter; - ecs_vec_t tables; /* ecs_table_leaf_t */ - int32_t index; -} ecs_snapshot_iter_t; - -typedef struct ecs_rule_op_profile_t { - int32_t count[2]; /* 0 = enter, 1 = redo */ -} ecs_rule_op_profile_t; - -/** Rule-iterator specific data */ -typedef struct ecs_rule_iter_t { - const ecs_rule_t *rule; - struct ecs_var_t *vars; /* Variable storage */ - const struct ecs_rule_var_t *rule_vars; - const struct ecs_rule_op_t *ops; - struct ecs_rule_op_ctx_t *op_ctx; /* Operation-specific state */ - uint64_t *written; - ecs_flags32_t source_set; -#ifdef FLECS_DEBUG - ecs_rule_op_profile_t *profile; -#endif + ecs_query_op_profile_t *profile; int16_t op; int16_t sp; -} ecs_rule_iter_t; +} ecs_query_iter_t; /* Bits for tracking whether a cache was used/whether the array was allocated. * Used by flecs_iter_init, flecs_iter_validate and ecs_iter_fini. * Constants are named to enable easy macro substitution. */ #define flecs_iter_cache_ids (1u << 0u) -#define flecs_iter_cache_columns (1u << 1u) +#define flecs_iter_cache_trs (1u << 1u) #define flecs_iter_cache_sources (1u << 2u) #define flecs_iter_cache_ptrs (1u << 3u) -#define flecs_iter_cache_match_indices (1u << 4u) -#define flecs_iter_cache_variables (1u << 5u) +#define flecs_iter_cache_variables (1u << 4u) #define flecs_iter_cache_all (255) /* Inline iterator arrays to prevent allocations for small array sizes */ @@ -3344,86 +3729,16 @@ typedef struct ecs_iter_cache_t { * progress & to provide builtin storage. */ typedef struct ecs_iter_private_t { union { - ecs_term_iter_t term; - ecs_filter_iter_t filter; ecs_query_iter_t query; - ecs_rule_iter_t rule; - ecs_snapshot_iter_t snapshot; ecs_page_iter_t page; ecs_worker_iter_t worker; + ecs_each_iter_t each; } iter; /* Iterator specific data */ - void *entity_iter; /* Filter applied after matching a table */ + void *entity_iter; /* Query applied after matching a table */ ecs_iter_cache_t cache; /* Inline arrays to reduce allocations */ } ecs_iter_private_t; -/** Iterator */ -struct ecs_iter_t { - /* World */ - ecs_world_t *world; /* The world */ - ecs_world_t *real_world; /* Actual world. This differs from world when in readonly mode */ - - /* Matched data */ - ecs_entity_t *entities; /* Entity identifiers */ - void **ptrs; /* Pointers to components. Array if from this, pointer if not. */ - ecs_size_t *sizes; /* Component sizes */ - ecs_table_t *table; /* Current table */ - ecs_table_t *other_table; /* Prev or next table when adding/removing */ - ecs_id_t *ids; /* (Component) ids */ - ecs_var_t *variables; /* Values of variables (if any) */ - int32_t *columns; /* Query term to table column mapping */ - ecs_entity_t *sources; /* Entity on which the id was matched (0 if same as entities) */ - int32_t *match_indices; /* Indices of current match for term. Allows an iterator to iterate - * all permutations of wildcards in query. */ - ecs_ref_t *references; /* Cached refs to components (if iterating a cache) */ - ecs_flags64_t constrained_vars; /* Bitset that marks constrained variables */ - uint64_t group_id; /* Group id for table, if group_by is used */ - int32_t field_count; /* Number of fields in iterator */ - - /* Input information */ - ecs_entity_t system; /* The system (if applicable) */ - ecs_entity_t event; /* The event (if applicable) */ - ecs_id_t event_id; /* The (component) id for the event */ - - /* Query information */ - ecs_term_t *terms; /* Terms of query being evaluated */ - int32_t table_count; /* Active table count for query */ - int32_t term_index; /* Index of term that emitted an event. - * This field will be set to the 'index' field - * of an observer term. */ - int32_t variable_count; /* Number of variables for query */ - char **variable_names; /* Names of variables (if any) */ - - /* Context */ - void *param; /* Param passed to ecs_run */ - void *ctx; /* System context */ - void *binding_ctx; /* Binding context */ - - /* Time */ - ecs_ftime_t delta_time; /* Time elapsed since last frame */ - ecs_ftime_t delta_system_time;/* Time elapsed since last system invocation */ - - /* Iterator counters */ - int32_t frame_offset; /* Offset relative to start of iteration */ - int32_t offset; /* Offset relative to current table */ - int32_t count; /* Number of entities to iterate */ - int32_t instance_count; /* Number of entities to iterate before next table */ - - /* Iterator flags */ - ecs_flags32_t flags; - - ecs_entity_t interrupted_by; /* When set, system execution is interrupted */ - - ecs_iter_private_t priv; /* Private data */ - - /* Chained iterators */ - ecs_iter_next_action_t next; /* Function to progress iterator */ - ecs_iter_action_t callback; /* Callback of system or observer */ - ecs_iter_action_t set_var; /* Invoked after setting variable (optionally set) */ - ecs_iter_fini_action_t fini; /* Function to cleanup iterator resources */ - ecs_iter_t *chain_it; /* Optional, allows for creating iterator chains */ -}; - #ifdef __cplusplus } #endif @@ -3460,39 +3775,112 @@ extern "C" { /** Maximum length of a parser token (used by parser-related addons) */ #define ECS_MAX_TOKEN_SIZE (256) -//////////////////////////////////////////////////////////////////////////////// -//// Global type handles -//////////////////////////////////////////////////////////////////////////////// - -/** This allows passing 0 as type to functions that accept ids */ -#define FLECS_ID0ID_ 0 - FLECS_API -char* ecs_module_path_from_c( +char* flecs_module_path_from_c( const char *c_name); -bool ecs_identifier_is_0( +bool flecs_identifier_is_0( const char *id); /* Constructor that zeromem's a component value */ FLECS_API -void ecs_default_ctor( +void flecs_default_ctor( void *ptr, int32_t count, const ecs_type_info_t *ctx); /* Create allocated string from format */ FLECS_DBG_API -char* ecs_vasprintf( +char* flecs_vasprintf( const char *fmt, va_list args); /* Create allocated string from format */ FLECS_API -char* ecs_asprintf( +char* flecs_asprintf( const char *fmt, ...); +/** Write an escaped character. + * Write a character to an output string, insert escape character if necessary. + * + * @param out The string to write the character to. + * @param in The input character. + * @param delimiter The delimiter used (for example '"') + * @return Pointer to the character after the last one written. + */ +FLECS_API +char* flecs_chresc( + char *out, + char in, + char delimiter); + +/** Parse an escaped character. + * Parse a character with a potential escape sequence. + * + * @param in Pointer to character in input string. + * @param out Output string. + * @return Pointer to the character after the last one read. + */ +const char* flecs_chrparse( + const char *in, + char *out); + +/** Write an escaped string. + * Write an input string to an output string, escape characters where necessary. + * To determine the size of the output string, call the operation with a NULL + * argument for 'out', and use the returned size to allocate a string that is + * large enough. + * + * @param out Pointer to output string (must be). + * @param size Maximum number of characters written to output. + * @param delimiter The delimiter used (for example '"'). + * @param in The input string. + * @return The number of characters that (would) have been written. + */ +FLECS_API +ecs_size_t flecs_stresc( + char *out, + ecs_size_t size, + char delimiter, + const char *in); + +/** Return escaped string. + * Return escaped version of input string. Same as flecs_stresc(), but returns an + * allocated string of the right size. + * + * @param delimiter The delimiter used (for example '"'). + * @param in The input string. + * @return Escaped string. + */ +FLECS_API +char* flecs_astresc( + char delimiter, + const char *in); + +/** Skip whitespace and newline characters. + * This function skips whitespace characters. + * + * @param ptr Pointer to (potential) whitespaces to skip. + * @return Pointer to the next non-whitespace character. + */ +FLECS_API +const char* flecs_parse_ws_eol( + const char *ptr); + +/** Parse digit. + * This function will parse until the first non-digit character is found. The + * provided expression must contain at least one digit character. + * + * @param ptr The expression to parse. + * @param token The output buffer. + * @return Pointer to the first non-digit character. + */ +FLECS_API +const char* flecs_parse_digit( + const char *ptr, + char *token); + /* Convert identifier to snake case */ FLECS_API char* flecs_to_snake_case( @@ -3506,6 +3894,57 @@ FLECS_DBG_API void flecs_dump_backtrace( void *stream); +/* Suspend/resume readonly state. To fully support implicit registration of + * components, it should be possible to register components while the world is + * in readonly mode. It is not uncommon that a component is used first from + * within a system, which are often ran while in readonly mode. + * + * Suspending readonly mode is only allowed when the world is not multithreaded. + * When a world is multithreaded, it is not safe to (even temporarily) leave + * readonly mode, so a multithreaded application should always explicitly + * register components in advance. + * + * These operations also suspend deferred mode. + */ +typedef struct ecs_suspend_readonly_state_t { + bool is_readonly; + bool is_deferred; + int32_t defer_count; + ecs_entity_t scope; + ecs_entity_t with; + ecs_vec_t commands; + ecs_stack_t defer_stack; + ecs_stage_t *stage; +} ecs_suspend_readonly_state_t; + +FLECS_API +ecs_world_t* flecs_suspend_readonly( + const ecs_world_t *world, + ecs_suspend_readonly_state_t *state); + +FLECS_API +void flecs_resume_readonly( + ecs_world_t *world, + ecs_suspend_readonly_state_t *state); + +FLECS_API +int32_t flecs_poly_claim_( + ecs_poly_t *poly); + +FLECS_API +int32_t flecs_poly_release_( + ecs_poly_t *poly); + +FLECS_API +int32_t flecs_poly_refcount( + ecs_poly_t *poly); + +#define flecs_poly_claim(poly) \ + flecs_poly_claim_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) +#define flecs_poly_release(poly) \ + flecs_poly_release_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) + + /** Calculate offset from address */ #ifdef __cplusplus #define ECS_OFFSET(o, offset) reinterpret_cast((reinterpret_cast(o)) + (static_cast(offset))) @@ -3523,8 +3962,18 @@ void flecs_dump_backtrace( #define ECS_BIT_COND(flags, bit, cond) ((cond) \ ? (ECS_BIT_SET(flags, bit)) \ : (ECS_BIT_CLEAR(flags, bit))) + +#define ECS_BIT_CLEAR16(flags, bit) (flags) &= (ecs_flags16_t)~(bit) +#define ECS_BIT_COND16(flags, bit, cond) ((cond) \ + ? (ECS_BIT_SET(flags, bit)) \ + : (ECS_BIT_CLEAR16(flags, bit))) + #define ECS_BIT_IS_SET(flags, bit) ((flags) & (bit)) +#define ECS_BIT_SETN(flags, n) ECS_BIT_SET(flags, 1llu << n) +#define ECS_BIT_CLEARN(flags, n) ECS_BIT_CLEAR(flags, 1llu << n) +#define ECS_BIT_CONDN(flags, n, cond) ECS_BIT_COND(flags, 1llu << n, cond) + #ifdef __cplusplus } #endif @@ -3680,15 +4129,23 @@ void* flecs_hashmap_next_( #endif -/** Used with ecs_entity_init - * - * \ingroup entities - */ +/** Utility to hold a value of a dynamic type. */ +typedef struct ecs_value_t { + ecs_entity_t type; /**< Type of value. */ + void *ptr; /**< Pointer to value. */ +} ecs_value_t; + +/** Used with ecs_entity_init(). + * + * @ingroup entities + */ typedef struct ecs_entity_desc_t { - int32_t _canary; + int32_t _canary; /**< Used for validity testing. Must be 0. */ ecs_entity_t id; /**< Set to modify existing entity (optional) */ + ecs_entity_t parent; /**< Parent entity. */ + const char *name; /**< Name of the entity. If no entity is provided, an * entity with this name will be looked up first. When * an entity is provided, the name will be verified @@ -3714,19 +4171,22 @@ typedef struct ecs_entity_desc_t { * components) will be used to create the entity, if * no id is specified. */ - /** Array of ids to add to the new or existing entity. */ - ecs_id_t add[FLECS_ID_DESC_MAX]; + /** 0-terminated array of ids to add to the entity. */ + const ecs_id_t *add; + + /** 0-terminated array of values to set on the entity. */ + const ecs_value_t *set; /** String expression with components to add */ const char *add_expr; } ecs_entity_desc_t; -/** Used with ecs_bulk_init - * - * \ingroup entities +/** Used with ecs_bulk_init(). + * + * @ingroup entities */ -typedef struct ecs_bulk_desc_t { - int32_t _canary; +typedef struct ecs_bulk_desc_t { + int32_t _canary; /**< Used for validity testing. Must be 0. */ ecs_entity_t *entities; /**< Entities to bulk insert. Entity ids provided by * the application must be empty (cannot @@ -3737,7 +4197,7 @@ typedef struct ecs_bulk_desc_t { ecs_id_t ids[FLECS_ID_DESC_MAX]; /**< Ids to create the entities with */ - void **data; /**< Array with component data to insert. Each element in + void **data; /**< Array with component data to insert. Each element in * the array must correspond with an element in the ids * array. If an element in the ids array is a tag, the * data array must contain a NULL. An element may be @@ -3751,13 +4211,13 @@ typedef struct ecs_bulk_desc_t { } ecs_bulk_desc_t; -/** Used with ecs_component_init. - * - * \ingroup components +/** Used with ecs_component_init(). + * + * @ingroup components */ typedef struct ecs_component_desc_t { - int32_t _canary; - + int32_t _canary; /**< Used for validity testing. Must be 0. */ + /** Existing entity to associate with observer (optional) */ ecs_entity_t entity; @@ -3765,82 +4225,189 @@ typedef struct ecs_component_desc_t { ecs_type_info_t type; } ecs_component_desc_t; -/** Used with ecs_filter_init. +/** Iterator. + * Used for iterating queries. The ecs_iter_t type contains all the information + * that is provided by a query, and contains all the state required for the + * iterator code. + * + * Functions that create iterators accept as first argument the world, and as + * second argument the object they iterate. For example: + * + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * @endcode * - * \ingroup filters + * When this code is called from a system, it is important to use the world + * provided by its iterator object to ensure thread safety. For example: + * + * @code + * void Collide(ecs_iter_t *it) { + * ecs_iter_t qit = ecs_query_iter(it->world, Colliders); + * } + * @endcode + * + * An iterator contains resources that need to be released. By default this + * is handled by the last call to next() that returns false. When iteration is + * ended before iteration has completed, an application has to manually call + * ecs_iter_fini() to release the iterator resources: + * + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * while (ecs_query_next(&it)) { + * if (cond) { + * ecs_iter_fini(&it); + * break; + * } + * } + * @endcode + * + * @ingroup queries */ -typedef struct ecs_filter_desc_t { - int32_t _canary; +struct ecs_iter_t { + /* World */ + ecs_world_t *world; /**< The world. Can point to stage when in deferred/readonly mode. */ + ecs_world_t *real_world; /**< Actual world. Never points to a stage. */ + + /* Matched data */ + const ecs_entity_t *entities; /**< Entity identifiers */ + const ecs_size_t *sizes; /**< Component sizes */ + ecs_table_t *table; /**< Current table */ + ecs_table_t *other_table; /**< Prev or next table when adding/removing */ + ecs_id_t *ids; /**< (Component) ids */ + ecs_var_t *variables; /**< Values of variables (if any) */ + const ecs_table_record_t **trs; /**< Info on where to find field in table */ + ecs_entity_t *sources; /**< Entity on which the id was matched (0 if same as entities) */ + ecs_flags64_t constrained_vars; /**< Bitset that marks constrained variables */ + uint64_t group_id; /**< Group id for table, if group_by is used */ + ecs_termset_t set_fields; /**< Fields that are set */ + ecs_termset_t ref_fields; /**< Bitset with fields that aren't component arrays */ + ecs_termset_t row_fields; /**< Fields that must be obtained with field_at */ + ecs_termset_t up_fields; /**< Bitset with fields matched through up traversal */ + + /* Input information */ + ecs_entity_t system; /**< The system (if applicable) */ + ecs_entity_t event; /**< The event (if applicable) */ + ecs_id_t event_id; /**< The (component) id for the event */ + int32_t event_cur; /**< Unique event id. Used to dedup observer calls */ - /** Terms of the filter. If a filter has more terms than - * FLECS_TERM_DESC_MAX use terms_buffer */ - ecs_term_t terms[FLECS_TERM_DESC_MAX]; + /* Query information */ + int8_t field_count; /**< Number of fields in iterator */ + int8_t term_index; /**< Index of term that emitted an event. + * This field will be set to the 'index' field + * of an observer term. */ + int8_t variable_count; /**< Number of variables for query */ + const ecs_query_t *query; /**< Query being evaluated */ + char **variable_names; /**< Names of variables (if any) */ - /** For filters with lots of terms an outside array can be provided. */ - ecs_term_t *terms_buffer; + /* Context */ + void *param; /**< Param passed to ecs_run */ + void *ctx; /**< System context */ + void *binding_ctx; /**< System binding context */ + void *callback_ctx; /**< Callback language binding context */ + void *run_ctx; /**< Run language binding context */ - /** Number of terms in array provided in terms_buffer. */ - int32_t terms_buffer_count; + /* Time */ + ecs_ftime_t delta_time; /**< Time elapsed since last frame */ + ecs_ftime_t delta_system_time;/**< Time elapsed since last system invocation */ - /** External storage to prevent allocation of the filter object */ - ecs_filter_t *storage; + /* Iterator counters */ + int32_t frame_offset; /**< Offset relative to start of iteration */ + int32_t offset; /**< Offset relative to current table */ + int32_t count; /**< Number of entities to iterate */ - /** When true, terms returned by an iterator may either contain 1 or N - * elements, where terms with N elements are owned, and terms with 1 element - * are shared, for example from a parent or base entity. When false, the - * iterator will at most return 1 element when the result contains both - * owned and shared terms. */ - bool instanced; + /* Misc */ + ecs_flags32_t flags; /**< Iterator flags */ + ecs_entity_t interrupted_by; /**< When set, system execution is interrupted */ + ecs_iter_private_t priv_; /**< Private data */ - /** Flags for advanced usage */ - ecs_flags32_t flags; + /* Chained iterators */ + ecs_iter_next_action_t next; /**< Function to progress iterator */ + ecs_iter_action_t callback; /**< Callback of system or observer */ + ecs_iter_fini_action_t fini; /**< Function to cleanup iterator resources */ + ecs_iter_t *chain_it; /**< Optional, allows for creating iterator chains */ +}; - /** Filter expression. Should not be set at the same time as terms array */ - const char *expr; - /** Entity associated with query (optional) */ - ecs_entity_t entity; -} ecs_filter_desc_t; +/** Query must match prefabs. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryMatchPrefab (1u << 1u) + +/** Query must match disabled entities. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryMatchDisabled (1u << 2u) + +/** Query must match empty tables. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryMatchEmptyTables (1u << 3u) + +/** Query may have unresolved entity identifiers. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryAllowUnresolvedByName (1u << 6u) + +/** Query only returns whole tables (ignores toggle/member fields). + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryTableOnly (1u << 7u) -/** Used with ecs_query_init. + +/** Used with ecs_query_init(). * * \ingroup queries */ typedef struct ecs_query_desc_t { + /** Used for validity testing. Must be 0. */ int32_t _canary; - /** Filter for the query */ - ecs_filter_desc_t filter; + /** Query terms */ + ecs_term_t terms[FLECS_TERM_COUNT_MAX]; + + /** Query DSL expression (optional) */ + const char *expr; + + /** Caching policy of query */ + ecs_query_cache_kind_t cache_kind; - /** Component to be used by order_by */ - ecs_entity_t order_by_component; + /** Flags for enabling query features */ + ecs_flags32_t flags; - /** Callback used for ordering query results. If order_by_id is 0, the + /** Callback used for ordering query results. If order_by_id is 0, the * pointer provided to the callback will be NULL. If the callback is not * set, results will not be ordered. */ - ecs_order_by_action_t order_by; + ecs_order_by_action_t order_by_callback; - /** Callback used for ordering query results. Same as order_by, + /** Callback used for ordering query results. Same as order_by_callback, * but more efficient. */ - ecs_sort_table_action_t sort_table; + ecs_sort_table_action_t order_by_table_callback; + + /** Component to sort on, used together with order_by_callback or + * order_by_table_callback. */ + ecs_entity_t order_by; - /** Id to be used by group_by. This id is passed to the group_by function and - * can be used identify the part of an entity type that should be used for - * grouping. */ - ecs_id_t group_by_id; + /** Component id to be used for grouping. Used together with the + * group_by_callback. */ + ecs_id_t group_by; /** Callback used for grouping results. If the callback is not set, results * will not be grouped. When set, this callback will be used to calculate a * "rank" for each entity (table) based on its components. This rank is then * used to sort entities (tables), so that entities (tables) of the same * rank are "grouped" together when iterated. */ - ecs_group_by_action_t group_by; + ecs_group_by_action_t group_by_callback; /** Callback that is invoked when a new group is created. The return value of * the callback is stored as context for a group. */ ecs_group_create_action_t on_group_create; - /** Callback that is invoked when an existing group is deleted. The return + /** Callback that is invoked when an existing group is deleted. The return * value of the on_group_create callback is passed as context parameter. */ ecs_group_delete_action_t on_group_delete; @@ -3850,54 +4417,49 @@ typedef struct ecs_query_desc_t { /** Function to free group_by_ctx */ ecs_ctx_free_t group_by_ctx_free; - /** If set, the query will be created as a subquery. A subquery matches at - * most a subset of its parent query. Subqueries do not directly receive - * (table) notifications from the world. Instead parent queries forward - * results to subqueries. This can improve matching performance, as fewer - * queries need to be matched with new tables. - * Subqueries can be nested. */ - ecs_query_t *parent; - /** User context to pass to callback */ void *ctx; /** Context to be used for language bindings */ void *binding_ctx; - + /** Callback to free ctx */ ecs_ctx_free_t ctx_free; - /** Callback to free binding_ctx */ + /** Callback to free binding_ctx */ ecs_ctx_free_t binding_ctx_free; + + /** Entity associated with query (optional) */ + ecs_entity_t entity; } ecs_query_desc_t; -/** Used with ecs_observer_init. - * - * \ingroup observers +/** Used with ecs_observer_init(). + * + * @ingroup observers */ typedef struct ecs_observer_desc_t { + /** Used for validity testing. Must be 0. */ int32_t _canary; /** Existing entity to associate with observer (optional) */ ecs_entity_t entity; - /** Filter for observer */ - ecs_filter_desc_t filter; + /** Query for observer */ + ecs_query_desc_t query; - /** Events to observe (OnAdd, OnRemove, OnSet, UnSet) */ + /** Events to observe (OnAdd, OnRemove, OnSet) */ ecs_entity_t events[FLECS_EVENT_DESC_MAX]; /** When observer is created, generate events from existing data. For example, - * EcsOnAdd Position would match all existing instances of Position. - * This is only supported for events that are iterable (see EcsIterable) */ + * #EcsOnAdd `Position` would match all existing instances of `Position`. */ bool yield_existing; /** Callback to invoke on an event, invoked when the observer matches. */ ecs_iter_action_t callback; /** Callback invoked on an event. When left to NULL the default runner - * is used which matches the event with the observer's filter, and calls - * 'callback' when it matches. + * is used which matches the event with the observer's query, and calls + * 'callback' when it matches. * A reason to override the run function is to improve performance, if there * are more efficient way to test whether an event matches the observer than * the general purpose query matcher. */ @@ -3906,14 +4468,20 @@ typedef struct ecs_observer_desc_t { /** User context to pass to callback */ void *ctx; - /** Context to be used for language bindings */ - void *binding_ctx; - /** Callback to free ctx */ ecs_ctx_free_t ctx_free; - /** Callback to free binding_ctx */ - ecs_ctx_free_t binding_ctx_free; + /** Context associated with callback (for language bindings). */ + void *callback_ctx; + + /** Callback to free callback ctx. */ + ecs_ctx_free_t callback_ctx_free; + + /** Context associated with run (for language bindings). */ + void *run_ctx; + + /** Callback to free run ctx. */ + ecs_ctx_free_t run_ctx_free; /** Observable with which to register the observer */ ecs_poly_t *observable; @@ -3923,12 +4491,13 @@ typedef struct ecs_observer_desc_t { int32_t *last_event_id; /** Used for internal purposes */ - int32_t term_index; + int8_t term_index_; + ecs_flags32_t flags_; } ecs_observer_desc_t; -/** Used with ecs_emit. - * - * \ingroup observers +/** Used with ecs_emit(). + * + * @ingroup observers */ typedef struct ecs_event_desc_t { /** The event id. Only observers for the specified event will be notified */ @@ -3951,7 +4520,7 @@ typedef struct ecs_event_desc_t { /** Limit number of notified entities to count. offset+count must be less * than the total number of entities in the table. If left to 0, it will be - * automatically determined by doing ecs_table_count(table) - offset. */ + * automatically determined by doing `ecs_table_count(table) - offset`. */ int32_t count; /** Single-entity alternative to setting table / offset / count */ @@ -3963,7 +4532,7 @@ typedef struct ecs_event_desc_t { * storage of the event type. */ void *param; - /* Same as param, but with the guarantee that the value won't be modified. + /** Same as param, but with the guarantee that the value won't be modified. * When an event with a const parameter is enqueued, the value of the param * is copied to a temporary storage of the event type. */ const void *const_param; @@ -3978,15 +4547,23 @@ typedef struct ecs_event_desc_t { /** * @defgroup misc_types Miscellaneous types - * @brief Types used to create entities, observers, queries and more. + * Types used to create entities, observers, queries and more. + * * @{ */ -/* Utility to hold a value of a dynamic type */ -typedef struct ecs_value_t { - ecs_entity_t type; - void *ptr; -} ecs_value_t; +/** Type with information about the current Flecs build */ +typedef struct ecs_build_info_t { + const char *compiler; /**< Compiler used to compile flecs */ + const char **addons; /**< Addons included in build */ + const char *version; /**< Stringified version */ + int16_t version_major; /**< Major flecs version */ + int16_t version_minor; /**< Minor flecs version */ + int16_t version_patch; /**< Patch flecs version */ + bool debug; /**< Is this a debug build */ + bool sanitize; /**< Is this a sanitize build */ + bool perf_trace; /**< Is this a perf tracing build */ +} ecs_build_info_t; /** Type that contains information about the world. */ typedef struct ecs_world_info_t { @@ -3995,19 +4572,20 @@ typedef struct ecs_world_info_t { ecs_entity_t max_id; /**< Last allowed entity id */ ecs_ftime_t delta_time_raw; /**< Raw delta time (no time scaling) */ - ecs_ftime_t delta_time; /**< Time passed to or computed by ecs_progress */ + ecs_ftime_t delta_time; /**< Time passed to or computed by ecs_progress() */ ecs_ftime_t time_scale; /**< Time scale applied to delta_time */ ecs_ftime_t target_fps; /**< Target fps */ ecs_ftime_t frame_time_total; /**< Total time spent processing a frame */ ecs_ftime_t system_time_total; /**< Total time spent in systems */ ecs_ftime_t emit_time_total; /**< Total time spent notifying observers */ ecs_ftime_t merge_time_total; /**< Total time spent in merges */ - ecs_ftime_t world_time_total; /**< Time elapsed in simulation */ - ecs_ftime_t world_time_total_raw; /**< Time elapsed in simulation (no scaling) */ ecs_ftime_t rematch_time_total; /**< Time spent on query rematching */ - + double world_time_total; /**< Time elapsed in simulation */ + double world_time_total_raw; /**< Time elapsed in simulation (no scaling) */ + int64_t frame_count_total; /**< Total number of frames */ int64_t merge_count_total; /**< Total number of merges */ + int64_t eval_comp_monitors_total; /**< Total number of monitor evaluations */ int64_t rematch_count_total; /**< Total number of rematches */ int64_t id_create_total; /**< Total number of times a new id was created */ @@ -4027,22 +4605,23 @@ typedef struct ecs_world_info_t { /* -- Command counts -- */ struct { - int64_t add_count; /**< add commands processed */ - int64_t remove_count; /**< remove commands processed */ - int64_t delete_count; /**< delete commands processed */ - int64_t clear_count; /**< clear commands processed */ - int64_t set_count; /**< set commands processed */ - int64_t get_mut_count; /**< get_mut/emplace commands processed */ - int64_t modified_count; /**< modified commands processed */ - int64_t other_count; /**< other commands processed */ - int64_t discard_count; /**< commands discarded, happens when entity is no longer alive when running the command */ - int64_t batched_entity_count; /**< entities for which commands were batched */ - int64_t batched_command_count; /**< commands batched */ - } cmd; - - const char *name_prefix; /**< Value set by ecs_set_name_prefix. Used + int64_t add_count; /**< Add commands processed */ + int64_t remove_count; /**< Remove commands processed */ + int64_t delete_count; /**< Selete commands processed */ + int64_t clear_count; /**< Clear commands processed */ + int64_t set_count; /**< Set commands processed */ + int64_t ensure_count; /**< Ensure/emplace commands processed */ + int64_t modified_count; /**< Modified commands processed */ + int64_t discard_count; /**< Commands discarded, happens when entity is no longer alive when running the command */ + int64_t event_count; /**< Enqueued custom events */ + int64_t other_count; /**< Other commands processed */ + int64_t batched_entity_count; /**< Entities for which commands were batched */ + int64_t batched_command_count; /**< Commands batched */ + } cmd; /**< Command statistics. */ + + const char *name_prefix; /**< Value set by ecs_set_name_prefix(). Used * to remove library prefixes of symbol - * names (such as Ecs, ecs_) when + * names (such as `Ecs`, `ecs_`) when * registering them as names. */ } ecs_world_info_t; @@ -4057,11 +4636,12 @@ typedef struct ecs_query_group_info_t { /** * @defgroup builtin_components Builtin component types. - * @brief Types that represent builtin components. + * Types that represent builtin components. + * * @{ */ -/** A (string) identifier. Used as pair with EcsName and EcsSymbol tags */ +/** A (string) identifier. Used as pair with #EcsName and #EcsSymbol tags */ typedef struct EcsIdentifier { char *value; /**< Identifier string */ ecs_size_t length; /**< Length of identifier */ @@ -4081,14 +4661,14 @@ typedef struct EcsPoly { ecs_poly_t *poly; /**< Pointer to poly object */ } EcsPoly; -/** Target data for flattened relationships. */ -typedef struct EcsTarget { - int32_t count; - ecs_record_t *target; -} EcsTarget; - -/** Component for iterable entities */ -typedef ecs_iterable_t EcsIterable; +/** When added to an entity this informs serialization formats which component + * to use when a value is assigned to an entity without specifying the + * component. This is intended as a hint, serialization formats are not required + * to use it. Adding this component does not change the behavior of core ECS + * operations. */ +typedef struct EcsDefaultChildComponent { + ecs_id_t component; /**< Default component id. */ +} EcsDefaultChildComponent; /** @} */ /** @} */ @@ -4121,13 +4701,15 @@ extern "C" { /** * @defgroup api_constants API Constants - * @brief Public API constants. + * Public API constants. + * * @{ */ /** * @defgroup id_flags Component id flags. - * @brief Id flags are bits that can be set on an id (ecs_id_t). + * Id flags are bits that can be set on an id (ecs_id_t). + * * @{ */ @@ -4135,14 +4717,11 @@ extern "C" { FLECS_API extern const ecs_id_t ECS_PAIR; /** Automatically override component when it is inherited */ -FLECS_API extern const ecs_id_t ECS_OVERRIDE; +FLECS_API extern const ecs_id_t ECS_AUTO_OVERRIDE; /** Adds bitset to storage which allows component to be enabled/disabled */ FLECS_API extern const ecs_id_t ECS_TOGGLE; -/** Include all components from entity to which AND is applied */ -FLECS_API extern const ecs_id_t ECS_AND; - /** @} */ /** @@ -4151,23 +4730,38 @@ FLECS_API extern const ecs_id_t ECS_AND; */ /* Builtin component ids */ + +/** Component component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsComponent); + +/** Identifier component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsIdentifier); -FLECS_API extern const ecs_entity_t ecs_id(EcsIterable); + +/** Poly component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsPoly); +/** DefaultChildComponent component id. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsDefaultChildComponent); + +/** Tag added to queries. */ FLECS_API extern const ecs_entity_t EcsQuery; + +/** Tag added to observers. */ FLECS_API extern const ecs_entity_t EcsObserver; -/* System module component ids */ +/** Tag added to systems. */ FLECS_API extern const ecs_entity_t EcsSystem; + +/** TickSource component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsTickSource); -/* Pipeline module component ids */ +/** Pipeline module component ids */ FLECS_API extern const ecs_entity_t ecs_id(EcsPipelineQuery); -/* Timer module component ids */ +/** Timer component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsTimer); + +/** RateFilter component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsRateFilter); /** Root scope for builtin flecs entities */ @@ -4191,86 +4785,140 @@ FLECS_API extern const ecs_entity_t EcsThis; /** Variable entity ("$"). Used in expressions to prefix variable names */ FLECS_API extern const ecs_entity_t EcsVariable; -/** Marks a relationship as transitive. - * Behavior: +/** Shortcut as EcsVariable is typically used as source for singleton terms */ +#define EcsSingleton EcsVariable + +/** Marks a relationship as transitive. + * Behavior: + * + * @code * if R(X, Y) and R(Y, Z) then R(X, Z) + * @endcode */ FLECS_API extern const ecs_entity_t EcsTransitive; -/** Marks a relatoinship as reflexive. - * Behavior: +/** Marks a relationship as reflexive. + * Behavior: + * + * @code * R(X, X) == true + * @endcode */ FLECS_API extern const ecs_entity_t EcsReflexive; -/** Ensures that entity/component cannot be used as target in IsA relationship. - * Final can improve the performance of rule-based queries, as they will not - * attempt to substitute a final component with its subsets. - * - * Behavior: +/** Ensures that entity/component cannot be used as target in `IsA` relationship. + * Final can improve the performance of queries as they will not attempt to + * substitute a final component with its subsets. + * + * Behavior: + * + * @code * if IsA(X, Y) and Final(Y) throw error + * @endcode */ FLECS_API extern const ecs_entity_t EcsFinal; -/** Ensures that component is never inherited from an IsA target. - * - * Behavior: - * if DontInherit(X) and X(B) and IsA(A, B) then X(A) is false. - */ -FLECS_API extern const ecs_entity_t EcsDontInherit; +/** Relationship that specifies component inheritance behavior. */ +FLECS_API extern const ecs_entity_t EcsOnInstantiate; -/** Ensures a component is always overridden. - * - * Behavior: - * As if the component is added together with OVERRIDE | T - */ -FLECS_API extern const ecs_entity_t EcsAlwaysOverride; +/** Override component on instantiate. + * This will copy the component from the base entity `(IsA target)` to the + * instance. The base component will never be inherited from the prefab. */ +FLECS_API extern const ecs_entity_t EcsOverride; + +/** Inherit component on instantiate. + * This will inherit (share) the component from the base entity `(IsA target)`. + * The component can be manually overridden by adding it to the instance. */ +FLECS_API extern const ecs_entity_t EcsInherit; + +/** Never inherit component on instantiate. + * This will not copy or share the component from the base entity `(IsA target)`. + * When the component is added to an instance, its value will never be copied + * from the base entity. */ +FLECS_API extern const ecs_entity_t EcsDontInherit; /** Marks relationship as commutative. * Behavior: + * + * @code * if R(X, Y) then R(Y, X) + * @endcode */ FLECS_API extern const ecs_entity_t EcsSymmetric; /** Can be added to relationship to indicate that the relationship can only occur - * once on an entity. Adding a 2nd instance will replace the 1st. + * once on an entity. Adding a 2nd instance will replace the 1st. * * Behavior: + * + * @code * R(X, Y) + R(X, Z) = R(X, Z) + * @endcode */ FLECS_API extern const ecs_entity_t EcsExclusive; /** Marks a relationship as acyclic. Acyclic relationships may not form cycles. */ FLECS_API extern const ecs_entity_t EcsAcyclic; -/** Marks a relationship as traversable. Traversable relationships may be +/** Marks a relationship as traversable. Traversable relationships may be * traversed with "up" queries. Traversable relationships are acyclic. */ FLECS_API extern const ecs_entity_t EcsTraversable; /** Ensure that a component always is added together with another component. - * + * * Behavior: + * + * @code * If With(R, O) and R(X) then O(X) * If With(R, O) and R(X, Y) then O(X, Y) + * @endcode */ FLECS_API extern const ecs_entity_t EcsWith; /** Ensure that relationship target is child of specified entity. - * + * * Behavior: + * + * @code * If OneOf(R, O) and R(X, Y), Y must be a child of O * If OneOf(R) and R(X, Y), Y must be a child of R + * @endcode */ FLECS_API extern const ecs_entity_t EcsOneOf; +/** Mark a component as toggleable with ecs_enable_id(). */ +FLECS_API extern const ecs_entity_t EcsCanToggle; + +/** Can be added to components to indicate it is a trait. Traits are components + * and/or tags that are added to other components to modify their behavior. + */ +FLECS_API extern const ecs_entity_t EcsTrait; + +/** Ensure that an entity is always used in pair as relationship. + * + * Behavior: + * + * @code + * e.add(R) panics + * e.add(X, R) panics, unless X has the "Trait" trait + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsRelationship; + +/** Ensure that an entity is always used in pair as target. + * + * Behavior: + * + * @code + * e.add(T) panics + * e.add(T, X) panics + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsTarget; + /** Can be added to relationship to indicate that it should never hold data, * even when it or the relationship target is a component. */ -FLECS_API extern const ecs_entity_t EcsTag; - -/** Tag to indicate that relationship is stored as union. Union relationships - * enable changing the target of a union without switching tables. Union - * relationships are also marked as exclusive. */ -FLECS_API extern const ecs_entity_t EcsUnion; +FLECS_API extern const ecs_entity_t EcsPairIsTag; /** Tag to indicate name identifier */ FLECS_API extern const ecs_entity_t EcsName; @@ -4300,13 +4948,18 @@ FLECS_API extern const ecs_entity_t EcsModule; FLECS_API extern const ecs_entity_t EcsPrivate; /** Tag added to prefab entities. Any entity with this tag is automatically - * ignored by queries, unless EcsPrefab is explicitly queried for. */ + * ignored by queries, unless #EcsPrefab is explicitly queried for. */ FLECS_API extern const ecs_entity_t EcsPrefab; -/** When this tag is added to an entity it is skipped by queries, unless - * EcsDisabled is explicitly queried for. */ +/** When this tag is added to an entity it is skipped by queries, unless + * #EcsDisabled is explicitly queried for. */ FLECS_API extern const ecs_entity_t EcsDisabled; +/** Trait added to entities that should never be returned by queries. Reserved + * for internal entities that have special meaning to the query engine, such as + * #EcsThis, #EcsWildcard, #EcsAny. */ +FLECS_API extern const ecs_entity_t EcsNotQueryable; + /** Event that triggers when an id is added to an entity */ FLECS_API extern const ecs_entity_t EcsOnAdd; @@ -4316,9 +4969,6 @@ FLECS_API extern const ecs_entity_t EcsOnRemove; /** Event that triggers when a component is set for an entity */ FLECS_API extern const ecs_entity_t EcsOnSet; -/** Event that triggers when a component is unset for an entity */ -FLECS_API extern const ecs_entity_t EcsUnSet; - /** Event that triggers observer when an entity starts/stops matching a query */ FLECS_API extern const ecs_entity_t EcsMonitor; @@ -4341,61 +4991,61 @@ FLECS_API extern const ecs_entity_t EcsOnDelete; * element of a pair) is deleted. */ FLECS_API extern const ecs_entity_t EcsOnDeleteTarget; -/** Remove cleanup policy. Must be used as target in pair with EcsOnDelete or - * EcsOnDeleteTarget. */ +/** Remove cleanup policy. Must be used as target in pair with #EcsOnDelete or + * #EcsOnDeleteTarget. */ FLECS_API extern const ecs_entity_t EcsRemove; -/** Delete cleanup policy. Must be used as target in pair with EcsOnDelete or - * EcsOnDeleteTarget. */ +/** Delete cleanup policy. Must be used as target in pair with #EcsOnDelete or + * #EcsOnDeleteTarget. */ FLECS_API extern const ecs_entity_t EcsDelete; -/** Panic cleanup policy. Must be used as target in pair with EcsOnDelete or - * EcsOnDeleteTarget. */ +/** Panic cleanup policy. Must be used as target in pair with #EcsOnDelete or + * #EcsOnDeleteTarget. */ FLECS_API extern const ecs_entity_t EcsPanic; -/** Component that stores data for flattened relationships */ -FLECS_API extern const ecs_entity_t ecs_id(EcsTarget); +/** Mark component as sparse */ +FLECS_API extern const ecs_entity_t EcsSparse; -/** Tag added to root entity to indicate its subtree should be flattened. Used - * together with assemblies. */ -FLECS_API extern const ecs_entity_t EcsFlatten; - -/** Used like (EcsDefaultChildComponent, Component). When added to an entity, - * this informs serialization formats which component to use when a value is - * assigned to an entity without specifying the component. This is intended as - * a hint, serialization formats are not required to use it. Adding this - * component does not change the behavior of core ECS operations. */ -FLECS_API extern const ecs_entity_t EcsDefaultChildComponent; +/** Mark relationship as union */ +FLECS_API extern const ecs_entity_t EcsUnion; -/* Builtin predicates for comparing entity ids in queries. Only supported by rules */ +/** Marker used to indicate `$var == ...` matching in queries. */ FLECS_API extern const ecs_entity_t EcsPredEq; + +/** Marker used to indicate `$var == "name"` matching in queries. */ FLECS_API extern const ecs_entity_t EcsPredMatch; + +/** Marker used to indicate `$var ~= "pattern"` matching in queries. */ FLECS_API extern const ecs_entity_t EcsPredLookup; -/* Builtin marker entities for opening/closing query scopes */ +/** Marker used to indicate the start of a scope (`{`) in queries. */ FLECS_API extern const ecs_entity_t EcsScopeOpen; + +/** Marker used to indicate the end of a scope (`}`) in queries. */ FLECS_API extern const ecs_entity_t EcsScopeClose; -/** Tag used to indicate query is empty */ +/** Tag used to indicate query is empty. + * This tag is removed automatically when a query becomes non-empty, and is not + * automatically re-added when it becomes empty. + */ FLECS_API extern const ecs_entity_t EcsEmpty; -/* Pipeline module tags */ -FLECS_API extern const ecs_entity_t ecs_id(EcsPipeline); -FLECS_API extern const ecs_entity_t EcsOnStart; -FLECS_API extern const ecs_entity_t EcsPreFrame; -FLECS_API extern const ecs_entity_t EcsOnLoad; -FLECS_API extern const ecs_entity_t EcsPostLoad; -FLECS_API extern const ecs_entity_t EcsPreUpdate; -FLECS_API extern const ecs_entity_t EcsOnUpdate; -FLECS_API extern const ecs_entity_t EcsOnValidate; -FLECS_API extern const ecs_entity_t EcsPostUpdate; -FLECS_API extern const ecs_entity_t EcsPreStore; -FLECS_API extern const ecs_entity_t EcsOnStore; -FLECS_API extern const ecs_entity_t EcsPostFrame; -FLECS_API extern const ecs_entity_t EcsPhase; +FLECS_API extern const ecs_entity_t ecs_id(EcsPipeline); /**< Pipeline component id. */ +FLECS_API extern const ecs_entity_t EcsOnStart; /**< OnStart pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPreFrame; /**< PreFrame pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnLoad; /**< OnLoad pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPostLoad; /**< PostLoad pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPreUpdate; /**< PreUpdate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnUpdate; /**< OnUpdate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnValidate; /**< OnValidate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPostUpdate; /**< PostUpdate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPreStore; /**< PreStore pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnStore; /**< OnStore pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPostFrame; /**< PostFrame pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPhase; /**< Phase pipeline phase. */ /** Value used to quickly check if component is builtin. This is used to quickly - * filter out tables with builtin components (for example for ecs_delete) */ + * filter out tables with builtin components (for example for ecs_delete()) */ #define EcsLastInternalComponentId (ecs_id(EcsPoly)) /** The first user-defined component starts from this id. Ids up to this number @@ -4407,9 +5057,9 @@ FLECS_API extern const ecs_entity_t EcsPhase; #define EcsFirstUserEntityId (FLECS_HI_COMPONENT_ID + 128) /* When visualized the reserved id ranges look like this: - * [1..8]: Builtin components - * [9..FLECS_HI_COMPONENT_ID]: Low ids reserved for application components - * [FLECS_HI_COMPONENT_ID + 1..EcsFirstUserEntityId]: Builtin entities + * - [1..8]: Builtin components + * - [9..FLECS_HI_COMPONENT_ID]: Low ids reserved for application components + * - [FLECS_HI_COMPONENT_ID + 1..EcsFirstUserEntityId]: Builtin entities */ /** @} */ @@ -4417,7 +5067,8 @@ FLECS_API extern const ecs_entity_t EcsPhase; /** * @defgroup world_api World - * @brief Functions for working with `ecs_world_t`. + * Functions for working with `ecs_world_t`. + * * @{ */ @@ -4436,8 +5087,8 @@ FLECS_API ecs_world_t* ecs_init(void); /** Create a new world with just the core module. - * Same as ecs_init, but doesn't import modules from addons. This operation is - * faster than ecs_init and results in less memory utilization. + * Same as ecs_init(), but doesn't import modules from addons. This operation is + * faster than ecs_init() and results in less memory utilization. * * @return A new tiny world */ @@ -4445,7 +5096,7 @@ FLECS_API ecs_world_t* ecs_mini(void); /** Create a new world with arguments. - * Same as ecs_init, but allows passing in command line arguments. Command line + * Same as ecs_init(), but allows passing in command line arguments. Command line * arguments are used to: * - automatically derive the name of the application from argv[0] * @@ -4467,7 +5118,7 @@ int ecs_fini( ecs_world_t *world); /** Returns whether the world is being deleted. - * This operation can be used in callbacks like type hooks or observers to + * This operation can be used in callbacks like type hooks or observers to * detect if they are invoked while the world is being deleted. * * @param world The world. @@ -4480,7 +5131,7 @@ bool ecs_is_fini( /** Register action to be executed when world is destroyed. * Fini actions are typically used when a module needs to clean up before a * world shuts down. - * + * * @param world The world. * @param action The function to execute. * @param ctx Userdata to pass to the function */ @@ -4490,6 +5141,54 @@ void ecs_atfini( ecs_fini_action_t action, void *ctx); +/** Type returned by ecs_get_entities(). */ +typedef struct ecs_entities_t { + const ecs_entity_t *ids; /**< Array with all entity ids in the world. */ + int32_t count; /**< Total number of entity ids. */ + int32_t alive_count; /**< Number of alive entity ids. */ +} ecs_entities_t; + +/** Return entity identifiers in world. + * This operation returns an array with all entity ids that exist in the world. + * Note that the returned array will change and may get invalidated as a result + * of entity creation & deletion. + * + * To iterate all alive entity ids, do: + * @code + * ecs_entities_t entities = ecs_get_entities(world); + * for (int i = 0; i < entities.alive_count; i ++) { + * ecs_entity_t id = entities.ids[i]; + * } + * @endcode + * + * To iterate not-alive ids, do: + * @code + * for (int i = entities.alive_count + 1; i < entities.count; i ++) { + * ecs_entity_t id = entities.ids[i]; + * } + * @endcode + * + * The returned array does not need to be freed. Mutating the returned array + * will return in undefined behavior (and likely crashes). + * + * @param world The world. + * @return Struct with entity id array. + */ +FLECS_API +ecs_entities_t ecs_get_entities( + const ecs_world_t *world); + +/** Get flags set on the world. + * This operation returns the internal flags (see api_flags.h) that are + * set on the world. + * + * @param world The world. + * @return Flags set on the world. + */ +FLECS_API +ecs_flags32_t ecs_world_get_flags( + const ecs_world_t *world); + /** @} */ /** @@ -4497,14 +5196,14 @@ void ecs_atfini( * @{ */ -/** Begin frame. - * When an application does not use ecs_progress to control the main loop, it +/** Begin frame. + * When an application does not use ecs_progress() to control the main loop, it * can still use Flecs features such as FPS limiting and time measurements. This * operation needs to be invoked whenever a new frame is about to get processed. * - * Calls to ecs_frame_begin must always be followed by ecs_frame_end. + * Calls to ecs_frame_begin() must always be followed by ecs_frame_end(). * - * The function accepts a delta_time parameter, which will get passed to + * The function accepts a delta_time parameter, which will get passed to * systems. This value is also used to compute the amount of time the function * needs to sleep to ensure it does not exceed the target_fps, when it is set. * When 0 is provided for delta_time, the time will be measured. @@ -4520,9 +5219,9 @@ ecs_ftime_t ecs_frame_begin( ecs_world_t *world, ecs_ftime_t delta_time); -/** End frame. +/** End frame. * This operation must be called at the end of the frame, and always after - * ecs_frame_begin. + * ecs_frame_begin(). * * @param world The world. */ @@ -4533,7 +5232,7 @@ void ecs_frame_end( /** Register action to be executed once after frame. * Post frame actions are typically used for calling operations that cannot be * invoked during iteration, such as changing the number of threads. - * + * * @param world The world. * @param action The function to execute. * @param ctx Userdata to pass to the function */ @@ -4541,11 +5240,11 @@ FLECS_API void ecs_run_post_frame( ecs_world_t *world, ecs_fini_action_t action, - void *ctx); + void *ctx); /** Signal exit * This operation signals that the application should quit. It will cause - * ecs_progress to return false. + * ecs_progress() to return false. * * @param world The world to quit. */ @@ -4553,16 +5252,18 @@ FLECS_API void ecs_quit( ecs_world_t *world); -/** Return whether a quit has been signaled. +/** Return whether a quit has been requested. * * @param world The world. + * @return Whether a quit has been requested. + * @see ecs_quit() */ -FLECS_API +FLECS_API bool ecs_should_quit( const ecs_world_t *world); -/** Measure frame time. - * Frame time measurements measure the total time passed in a single frame, and +/** Measure frame time. + * Frame time measurements measure the total time passed in a single frame, and * how much of that time was spent on systems and on merging. * * Frame time measurements add a small constant-time overhead to an application. @@ -4576,10 +5277,10 @@ FLECS_API void ecs_measure_frame_time( ecs_world_t *world, bool enable); -/** Measure system time. +/** Measure system time. * System time measurements measure the time spent in each system. * - * System time measurements add overhead to every system invocation and + * System time measurements add overhead to every system invocation and * therefore have a small but measurable impact on application performance. * System time measurements must be enabled before obtaining system statistics. * @@ -4588,17 +5289,17 @@ FLECS_API void ecs_measure_frame_time( */ FLECS_API void ecs_measure_system_time( ecs_world_t *world, - bool enable); + bool enable); /** Set target frames per second (FPS) for application. - * Setting the target FPS ensures that ecs_progress is not invoked faster than - * the specified FPS. When enabled, ecs_progress tracks the time passed since + * Setting the target FPS ensures that ecs_progress() is not invoked faster than + * the specified FPS. When enabled, ecs_progress() tracks the time passed since * the last invocation, and sleeps the remaining time of the frame (if any). * * This feature ensures systems are ran at a consistent interval, as well as * conserving CPU time by not running systems more often than required. * - * Note that ecs_progress only sleeps if there is time left in the frame. Both + * Note that ecs_progress() only sleeps if there is time left in the frame. Both * time spent in flecs as time spent outside of flecs are taken into * account. * @@ -4610,6 +5311,24 @@ void ecs_set_target_fps( ecs_world_t *world, ecs_ftime_t fps); +/** Set default query flags. + * Set a default value for the ecs_filter_desc_t::flags field. Default flags + * are applied in addition to the flags provided in the descriptor. For a + * list of available flags, see include/flecs/private/api_flags.h. Typical flags + * to use are: + * + * - `EcsQueryMatchEmptyTables` + * - `EcsQueryMatchDisabled` + * - `EcsQueryMatchPrefab` + * + * @param world The world. + * @param flags The query flags. + */ +FLECS_API +void ecs_set_default_query_flags( + ecs_world_t *world, + ecs_flags32_t flags); + /** @} */ /** @@ -4618,41 +5337,82 @@ void ecs_set_target_fps( */ /** Begin readonly mode. - * Readonly mode guarantees that no mutations will occur on the world, which - * makes the world safe to access from multiple threads. While the world is in - * readonly mode, operations are deferred. + * This operation puts the world in readonly mode, which disallows mutations on + * the world. Readonly mode exists so that internal mechanisms can implement + * optimizations that certain aspects of the world to not change, while also + * providing a mechanism for applications to prevent accidental mutations in, + * for example, multithreaded applications. + * + * Readonly mode is a stronger version of deferred mode. In deferred mode + * ECS operations such as add/remove/set/delete etc. are added to a command + * queue to be executed later. In readonly mode, operations that could break + * scheduler logic (such as creating systems, queries) are also disallowed. + * + * Readonly mode itself has a single threaded and a multi threaded mode. In + * single threaded mode certain mutations on the world are still allowed, for + * example: + * - Entity liveliness operations (such as new, make_alive), so that systems are + * able to create new entities. + * - Implicit component registration, so that this works from systems + * - Mutations to supporting data structures for the evaluation of uncached + * queries (filters), so that these can be created on the fly. + * + * These mutations are safe in a single threaded applications, but for + * multithreaded applications the world needs to be entirely immutable. For this + * purpose multi threaded readonly mode exists, which disallows all mutations on + * the world. This means that in multi threaded applications, entity liveliness + * operations, implicit component registration, and on-the-fly query creation + * are not guaranteed to work. + * + * While in readonly mode, applications can still enqueue ECS operations on a + * stage. Stages are managed automatically when using the pipeline addon and + * ecs_progress(), but they can also be configured manually as shown here: + * + * @code + * // Number of stages typically corresponds with number of threads + * ecs_set_stage_count(world, 2); + * ecs_stage_t *stage = ecs_get_stage(world, 1); + * + * ecs_readonly_begin(world); + * ecs_add(world, e, Tag); // readonly assert + * ecs_add(stage, e, Tag); // OK + * @endcode * - * Note that while similar to ecs_defer_begin, deferring only does not guarantee - * the world is not mutated. Operations that are not deferred (like creating a - * query) update data structures on the world and are allowed when deferring is - * enabled, but not when the world is in readonly mode. + * When an attempt is made to perform an operation on a world in readonly mode, + * the code will throw an assert saying that the world is in readonly mode. * - * A call to ecs_readonly_begin must be followed up with ecs_readonly_end. + * A call to ecs_readonly_begin() must be followed up with ecs_readonly_end(). + * When ecs_readonly_end() is called, all enqueued commands from configured + * stages are merged back into the world. Calls to ecs_readonly_begin() and + * ecs_readonly_end() should always happen from a context where the code has + * exclusive access to the world. The functions themselves are not thread safe. * - * The ecs_progress() function automatically enables readonly mode while systems - * are executed. + * In a typical application, a (non-exhaustive) call stack that uses + * ecs_readonly_begin() and ecs_readonly_end() will look like this: * - * When a world has more than one stage, the specific stage must be provided to - * mutating ECS operations. Failing to do so will throw a readonly assert. A - * world typically has more than one stage when using threads. An example: + * @code + * ecs_progress() + * ecs_readonly_begin() + * ecs_defer_begin() * - * ecs_set_stage_count(world, 2); - * ecs_stage_t *stage = ecs_get_stage(world, 1); + * // user code * - * ecs_readonly_begin(world); - * ecs_add(world, e, Tag); // readonly assert - * ecs_add(stage, e, Tag); // OK + * ecs_readonly_end() + * ecs_defer_end() + *@endcode * * @param world The world + * @param multi_threaded Whether to enable readonly/multi threaded mode. * @return Whether world is in readonly mode. */ FLECS_API bool ecs_readonly_begin( - ecs_world_t *world); + ecs_world_t *world, + bool multi_threaded); /** End readonly mode. - * This operation ends readonly mode, and must be called after - * ecs_readonly_begin. Operations that were deferred while the world was in + * This operation ends readonly mode, and must be called after + * ecs_readonly_begin(). Operations that were deferred while the world was in * readonly mode will be flushed. * * @param world The world @@ -4665,7 +5425,7 @@ void ecs_readonly_end( * When automatic merging is disabled, an application can call this * operation on either an individual stage, or on the world which will merge * all stages. This operation may only be called when staging is not enabled - * (either after progress() or after readonly_end()). + * (either after ecs_progress() or after ecs_readonly_end()). * * This operation may be called on an already merged stage or world. * @@ -4675,35 +5435,51 @@ FLECS_API void ecs_merge( ecs_world_t *world); -/** Defer operations until end of frame. +/** Defer operations until end of frame. * When this operation is invoked while iterating, operations inbetween the - * defer_begin and defer_end operations are executed at the end of the frame. + * ecs_defer_begin() and ecs_defer_end() operations are executed at the end + * of the frame. * * This operation is thread safe. - * + * * @param world The world. * @return true if world changed from non-deferred mode to deferred mode. + * + * @see ecs_defer_end() + * @see ecs_is_deferred() + * @see ecs_defer_resume() + * @see ecs_defer_suspend() */ FLECS_API bool ecs_defer_begin( ecs_world_t *world); /** Test if deferring is enabled for current stage. - * + * * @param world The world. * @return True if deferred, false if not. + * + * @see ecs_defer_begin() + * @see ecs_defer_end() + * @see ecs_defer_resume() + * @see ecs_defer_suspend() */ FLECS_API bool ecs_is_deferred( const ecs_world_t *world); -/** End block of operations to defer. - * See defer_begin. +/** End block of operations to defer. + * See ecs_defer_begin(). * * This operation is thread safe. * * @param world The world. * @return true if world changed from deferred mode to non-deferred mode. + * + * @see ecs_defer_begin() + * @see ecs_defer_is_deferred() + * @see ecs_defer_resume() + * @see ecs_defer_suspend() */ FLECS_API bool ecs_defer_end( @@ -4712,56 +5488,45 @@ bool ecs_defer_end( /** Suspend deferring but do not flush queue. * This operation can be used to do an undeferred operation while not flushing * the operations in the queue. - * - * An application should invoke ecs_defer_resume before ecs_defer_end is called. + * + * An application should invoke ecs_defer_resume() before ecs_defer_end() is called. * The operation may only be called when deferring is enabled. - * + * * @param world The world. + * + * @see ecs_defer_begin() + * @see ecs_defer_end() + * @see ecs_defer_is_deferred() + * @see ecs_defer_resume() */ FLECS_API void ecs_defer_suspend( ecs_world_t *world); /** Resume deferring. - * See ecs_defer_suspend. - * + * See ecs_defer_suspend(). + * * @param world The world. + * + * @see ecs_defer_begin() + * @see ecs_defer_end() + * @see ecs_defer_is_deferred() + * @see ecs_defer_suspend() */ FLECS_API void ecs_defer_resume( ecs_world_t *world); -/** Enable/disable automerging for world or stage. - * When automerging is enabled, staged data will automatically be merged with - * the world when staging ends. This happens at the end of progress(), at a - * sync point or when readonly_end() is called. - * - * Applications can exercise more control over when data from a stage is merged - * by disabling automerging. This requires an application to explicitly call - * merge() on the stage. - * - * When this function is invoked on the world, it sets all current stages to - * the provided value and sets the default for new stages. When this function is - * invoked on a stage, automerging is only set for that specific stage. - * - * @param world The world. - * @param automerge Whether to enable or disable automerging. - */ -FLECS_API -void ecs_set_automerge( - ecs_world_t *world, - bool automerge); - /** Configure world to have N stages. * This initializes N stages, which allows applications to defer operations to * multiple isolated defer queues. This is typically used for applications with * multiple threads, where each thread gets its own queue, and commands are * merged when threads are synchronized. * - * Note that the ecs_set_threads function already creates the appropriate - * number of stages. The set_stage_count() operation is useful for applications that - * want to manage their own stages and/or threads. - * + * Note that the ecs_set_threads() function already creates the appropriate + * number of stages. The ecs_set_stage_count() operation is useful for applications + * that want to manage their own stages and/or threads. + * * @param world The world. * @param stages The number of stages. */ @@ -4771,7 +5536,7 @@ void ecs_set_stage_count( int32_t stages); /** Get number of configured stages. - * Return number of stages set by ecs_set_stage_count. + * Return number of stages set by ecs_set_stage_count(). * * @param world The world. * @return The number of stages used for threading. @@ -4780,31 +5545,20 @@ FLECS_API int32_t ecs_get_stage_count( const ecs_world_t *world); -/** Get current stage id. - * The stage id can be used by an application to learn about which stage it is - * using, which typically corresponds with the worker thread id. - * - * @param world The world. - * @return The stage id. - */ -FLECS_API -int32_t ecs_get_stage_id( - const ecs_world_t *world); - /** Get stage-specific world pointer. - * Flecs threads can safely invoke the API as long as they have a private + * Flecs threads can safely invoke the API as long as they have a private * context to write to, also referred to as the stage. This function returns a * pointer to a stage, disguised as a world pointer. * * Note that this function does not(!) create a new world. It simply wraps the * existing world in a thread-specific context, which the API knows how to * unwrap. The reason the stage is returned as an ecs_world_t is so that it - * can be passed transparently to the existing API functions, vs. having to - * create a dediated API for threading. + * can be passed transparently to the existing API functions, vs. having to + * create a dedicated API for threading. * * @param world The world. * @param stage_id The index of the stage to retrieve. - * @return A thread-specific pointer to the world. + * @return A thread-specific pointer to the world. */ FLECS_API ecs_world_t* ecs_get_stage( @@ -4813,7 +5567,7 @@ ecs_world_t* ecs_get_stage( /** Test whether the current world is readonly. * This function allows the code to test whether the currently used world - * is readonly or whether it allows for writing. + * is readonly or whether it allows for writing. * * @param world A pointer to a stage or the world. * @return True if the world or stage is readonly. @@ -4822,47 +5576,35 @@ FLECS_API bool ecs_stage_is_readonly( const ecs_world_t *world); -/** Create asynchronous stage. - * An asynchronous stage can be used to asynchronously queue operations for - * later merging with the world. An asynchronous stage is similar to a regular - * stage, except that it does not allow reading from the world. - * - * Asynchronous stages are never merged automatically, and must therefore be - * manually merged with the ecs_merge function. It is not necessary to call - * defer_begin or defer_end before and after enqueuing commands, as an - * asynchronous stage unconditionally defers operations. - * - * The application must ensure that no commands are added to the stage while the - * stage is being merged. - * - * An asynchronous stage must be cleaned up by ecs_async_stage_free. +/** Create unmanaged stage. + * Create a stage whose lifecycle is not managed by the world. Must be freed + * with ecs_stage_free(). * * @param world The world. * @return The stage. */ FLECS_API -ecs_world_t* ecs_async_stage_new( +ecs_world_t* ecs_stage_new( ecs_world_t *world); -/** Free asynchronous stage. - * The provided stage must be an asynchronous stage. If a non-asynchronous stage - * is provided, the operation will fail. +/** Free unmanaged stage. * * @param stage The stage to free. */ FLECS_API -void ecs_async_stage_free( +void ecs_stage_free( ecs_world_t *stage); -/** Test whether provided stage is asynchronous. +/** Get stage id. + * The stage id can be used by an application to learn about which stage it is + * using, which typically corresponds with the worker thread id. * - * @param stage The stage. - * @return True when the stage is asynchronous, false for a regular stage or - * world. + * @param world The world. + * @return The stage id. */ FLECS_API -bool ecs_stage_is_async( - ecs_world_t *stage); +int32_t ecs_stage_get_id( + const ecs_world_t *world); /** @} */ @@ -4886,7 +5628,7 @@ void ecs_set_ctx( ecs_ctx_free_t ctx_free); /** Set a world binding context. - * Same as ecs_set_ctx but for binding context. A binding context is intended + * Same as ecs_set_ctx() but for binding context. A binding context is intended * specifically for language bindings to store binding specific data. * * @param world The world. @@ -4903,7 +5645,7 @@ void ecs_set_binding_ctx( * This operation retrieves a previously set world context. * * @param world The world. - * @return The context set with ecs_set_ctx. If no context was set, the + * @return The context set with ecs_set_ctx(). If no context was set, the * function returns NULL. */ FLECS_API @@ -4914,13 +5656,21 @@ void* ecs_get_ctx( * This operation retrieves a previously set world binding context. * * @param world The world. - * @return The context set with ecs_set_binding_ctx. If no context was set, the + * @return The context set with ecs_set_binding_ctx(). If no context was set, the * function returns NULL. */ FLECS_API void* ecs_get_binding_ctx( const ecs_world_t *world); +/** Get build info. + * Returns information about the current Flecs build. + * + * @return A struct with information about the current Flecs build. + */ +FLECS_API +const ecs_build_info_t* ecs_get_build_info(void); + /** Get world info. * * @param world The world. @@ -4943,17 +5693,17 @@ void ecs_dim( ecs_world_t *world, int32_t entity_count); -/** Set a range for issueing new entity ids. - * This function constrains the entity identifiers returned by ecs_new to the +/** Set a range for issuing new entity ids. + * This function constrains the entity identifiers returned by ecs_new_w() to the * specified range. This operation can be used to ensure that multiple processes * can run in the same simulation without requiring a central service that - * coordinates issueing identifiers. - * - * If id_end is set to 0, the range is infinite. If id_end is set to a non-zero - * value, it has to be larger than id_start. If id_end is set and ecs_new is - * invoked after an id is issued that is equal to id_end, the application will + * coordinates issuing identifiers. + * + * If `id_end` is set to 0, the range is infinite. If `id_end` is set to a non-zero + * value, it has to be larger than `id_start`. If `id_end` is set and ecs_new() is + * invoked after an id is issued that is equal to `id_end`, the application will * abort. - * + * * @param world The world. * @param id_start The start of the range. * @param id_end The end of the range. @@ -4967,7 +5717,7 @@ void ecs_set_entity_range( /** Enable/disable range limits. * When an application is both a receiver of range-limited entities and a * producer of range-limited entities, range checking needs to be temporarily - * disabled when inserting received entities. Range checking is disabled on a + * disabled when inserting received entities. Range checking is disabled on a * stage, so setting this value is thread safe. * * @param world The world. @@ -4980,8 +5730,9 @@ bool ecs_enable_range_check( bool enable); /** Get the largest issued entity id (not counting generation). - * + * * @param world The world. + * @return The largest issued entity id. */ FLECS_API ecs_entity_t ecs_get_max_id( @@ -4992,11 +5743,11 @@ ecs_entity_t ecs_get_max_id( * application to function correctly. This may cause observable side effects * such as delayed triggering of events, which can be inconvenient when for * example running a test suite. - * + * * The flags parameter specifies which aperiodic actions to run. Specify 0 to * run all actions. Supported flags start with 'EcsAperiodic'. Flags identify * internal mechanisms and may change unannounced. - * + * * @param world The world. * @param flags The flags specifying which actions to run. */ @@ -5008,28 +5759,28 @@ void ecs_run_aperiodic( /** Cleanup empty tables. * This operation cleans up empty tables that meet certain conditions. Having * large amounts of empty tables does not negatively impact performance of the - * ECS, but can take up considerable amounts of memory, especially in + * ECS, but can take up considerable amounts of memory, especially in * applications with many components, and many components per entity. - * + * * The generation specifies the minimum number of times this operation has * to be called before an empty table is cleaned up. If a table becomes non * empty, the generation is reset. - * + * * The operation allows for both a "clear" generation and a "delete" - * generation. When the clear generation is reached, the table's + * generation. When the clear generation is reached, the table's * resources are freed (like component arrays) but the table itself is not * deleted. When the delete generation is reached, the empty table is deleted. - * + * * By specifying a non-zero id the cleanup logic can be limited to tables with * a specific (component) id. The operation will only increase the generation * count of matching tables. - * + * * The min_id_count specifies a lower bound for the number of components a table - * should have. Often the more components a table has, the more specific it is + * should have. Often the more components a table has, the more specific it is * and therefore less likely to be reused. - * + * * The time budget specifies how long the operation should take at most. - * + * * @param world The world. * @param id Optional component filter for the tables to evaluate. * @param clear_generation Free table data when generation > clear_generation. @@ -5067,28 +5818,35 @@ ecs_entity_t ecs_get_entity( /** Test if pointer is of specified type. * Usage: - * ecs_poly_is(ptr, ecs_world_t) - * + * + * @code + * flecs_poly_is(ptr, ecs_world_t) + * @endcode + * * This operation only works for poly types. - * + * * @param object The object to test. * @param type The id of the type. * @return True if the pointer is of the specified type. */ FLECS_API -bool ecs_poly_is_( +bool flecs_poly_is_( const ecs_poly_t *object, int32_t type); -#define ecs_poly_is(object, type)\ - ecs_poly_is_(object, type##_magic) +/** Test if pointer is of specified type. + * @see flecs_poly_is_() + */ +#define flecs_poly_is(object, type)\ + flecs_poly_is_(object, type##_magic) /** Make a pair id. - * This function is equivalent to using the ecs_pair macro, and is added for + * This function is equivalent to using the ecs_pair() macro, and is added for * convenience to make it easier for non C/C++ bindings to work with pairs. * * @param first The first element of the pair of the pair. * @param second The target of the pair. + * @return A pair id. */ FLECS_API ecs_id_t ecs_make_pair( @@ -5101,40 +5859,42 @@ ecs_id_t ecs_make_pair( /** * @defgroup entities Entities - * @brief Functions for working with `ecs_entity_t`. + * Functions for working with `ecs_entity_t`. + * * @{ */ /** * @defgroup creating_entities Creating & Deleting - * @brief Functions for creating and deleting entities. + * Functions for creating and deleting entities. + * * @{ */ /** Create new entity id. * This operation returns an unused entity id. This operation is guaranteed to - * return an empty entity as it does not use values set by ecs_set_scope or - * ecs_set_with. + * return an empty entity as it does not use values set by ecs_set_scope() or + * ecs_set_with(). * * @param world The world. * @return The new entity id. */ FLECS_API -ecs_entity_t ecs_new_id( +ecs_entity_t ecs_new( ecs_world_t *world); /** Create new low id. * This operation returns a new low id. Entity ids start after the - * FLECS_HI_COMPONENT_ID constant. This reserves a range of low ids for things + * FLECS_HI_COMPONENT_ID constant. This reserves a range of low ids for things * like components, and allows parts of the code to optimize operations. * - * Note that FLECS_HI_COMPONENT_ID does not represent the maximum number of + * Note that FLECS_HI_COMPONENT_ID does not represent the maximum number of * components that can be created, only the maximum number of components that * can take advantage of these optimizations. - * - * This operation is guaranteed to return an empty entity as it does not use - * values set by ecs_set_scope or ecs_set_with. - * + * + * This operation is guaranteed to return an empty entity as it does not use + * values set by ecs_set_scope() or ecs_set_with(). + * * This operation does not recycle ids. * * @param world The world. @@ -5147,7 +5907,7 @@ ecs_entity_t ecs_new_low_id( /** Create new entity with (component) id. * This operation creates a new entity with an optional (component) id. When 0 * is passed to the id parameter, no component is added to the new entity. - * + * * @param world The world. * @param id The component id to initialize the new entity with. * @return The new entity. @@ -5159,7 +5919,7 @@ ecs_entity_t ecs_new_w_id( /** Create new entity in table. * This operation creates a new entity in the specified table. - * + * * @param world The world. * @param table The table to which to add the new entity. * @return The new entity. @@ -5169,7 +5929,7 @@ ecs_entity_t ecs_new_w_table( ecs_world_t *world, ecs_table_t *table); -/** Find or create an entity. +/** Find or create an entity. * This operation creates a new entity, or modifies an existing one. When a name * is set in the ecs_entity_desc_t::name field and ecs_entity_desc_t::entity is * not set, the operation will first attempt to find an existing entity by that @@ -5180,8 +5940,8 @@ ecs_entity_t ecs_new_w_table( * the function will fail and return 0. * * If an id to a non-existing entity is provided, that entity id become alive. - * - * See the documentation of ecs_entity_desc_t for more details. + * + * See the documentation of ecs_entity_desc_t for more details. * * @param world The world. * @param desc Entity init parameters. @@ -5195,25 +5955,25 @@ ecs_entity_t ecs_entity_init( /** Bulk create/populate new entities. * This operation bulk inserts a list of new or predefined entities into a * single table. - * + * * The operation does not take ownership of component arrays provided by the * application. Components that are non-trivially copyable will be moved into * the storage. - * + * * The operation will emit OnAdd events for each added id, and OnSet events for * each component that has been set. - * + * * If no entity ids are provided by the application, the returned array of ids - * points to an internal datastructure which changes when new entities are + * points to an internal data structure which changes when new entities are * created/deleted. - * + * * If as a result of the operation triggers are invoked that deletes * entities and no entity ids were provided by the application, the returned * array of identifiers may be incorrect. To avoid this problem, an application - * can first call ecs_bulk_init to create empty entities, copy the array to one + * can first call ecs_bulk_init() to create empty entities, copy the array to one * that is owned by the application, and then use this array to populate the * entities. - * + * * @param world The world. * @param desc Bulk creation parameters. * @return Array with the list of entity ids created/populated. @@ -5224,9 +5984,9 @@ const ecs_entity_t* ecs_bulk_init( const ecs_bulk_desc_t *desc); /** Create N new entities. - * This operation is the same as ecs_new_w_id, but creates N entities + * This operation is the same as ecs_new_w_id(), but creates N entities * instead of one. - * + * * @param world The world. * @param id The component id to create the entities with. * @param count The number of entities to create. @@ -5242,7 +6002,7 @@ const ecs_entity_t* ecs_bulk_new_w_id( * This operation clones the components of one entity into another entity. If * no destination entity is provided, a new entity will be created. Component * values are not copied unless copy_value is true. - * + * * If the source entity has a name, it will not be copied to the destination * entity. This is to prevent having two entities with the same name under the * same parent, which is not allowed. @@ -5262,7 +6022,7 @@ ecs_entity_t ecs_clone( /** Delete an entity. * This operation will delete an entity and all of its components. The entity id - * will be made available for recycling. If the entity passed to ecs_delete is + * will be made available for recycling. If the entity passed to ecs_delete() is * not alive, the operation will have no side effects. * * @param world The world. @@ -5274,9 +6034,9 @@ void ecs_delete( ecs_entity_t entity); /** Delete all entities with the specified id. - * This will delete all entities (tables) that have the specified id. The id + * This will delete all entities (tables) that have the specified id. The id * may be a wildcard and/or a pair. - * + * * @param world The world. * @param id The id. */ @@ -5289,12 +6049,13 @@ void ecs_delete_with( /** * @defgroup adding_removing Adding & Removing - * @brief Functions for adding and removing components. + * Functions for adding and removing components. + * * @{ */ /** Add a (component) id to an entity. - * This operation adds a single (component) id to an entity. If the entity + * This operation adds a single (component) id to an entity. If the entity * already has the id, this operation will have no side effects. * * @param world The world. @@ -5308,7 +6069,7 @@ void ecs_add_id( ecs_id_t id); /** Remove a (component) id from an entity. - * This operation removes a single (component) id to an entity. If the entity + * This operation removes a single (component) id to an entity. If the entity * does not have the id, this operation will have no side effects. * * @param world The world. @@ -5321,30 +6082,59 @@ void ecs_remove_id( ecs_entity_t entity, ecs_id_t id); -/** Add override for (component) id. - * Adding an override to an entity ensures that when the entity is instantiated - * (by adding an IsA relationship to it) the component with the override is - * copied to a component that is private to the instance. By default components - * reachable through an IsA relationship are shared. +/** Add auto override for (component) id. + * An auto override is a component that is automatically added to an entity when + * it is instantiated from a prefab. Auto overrides are added to the entity that + * is inherited from (usually a prefab). For example: + * + * @code + * ecs_entity_t prefab = ecs_insert(world, + * ecs_value(Position, {10, 20}), + * ecs_value(Mass, {100})); * - * Adding an override does not add the component. If an override is added to an - * entity that does not have the component, it will still be added to the - * instance, but with an uninitialized value (unless the component has a ctor). - * When the entity does have the entity, the component of the instance will be - * initialized with the value of the component on the entity. + * ecs_auto_override(world, prefab, Position); * - * This is the same as what happens when calling ecs_add_id for an id that is - * inherited (reachable through an IsA relationship). + * ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + * assert(ecs_owns(world, inst, Position)); // true + * assert(ecs_owns(world, inst, Mass)); // false + * @endcode * - * This operation is equivalent to doing: - * ecs_add_id(world, entity, ECS_OVERRIDE | id); + * An auto override is equivalent to a manual override: * + * @code + * ecs_entity_t prefab = ecs_insert(world, + * ecs_value(Position, {10, 20}), + * ecs_value(Mass, {100})); + * + * ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + * assert(ecs_owns(world, inst, Position)); // false + * ecs_add(world, inst, Position); // manual override + * assert(ecs_owns(world, inst, Position)); // true + * assert(ecs_owns(world, inst, Mass)); // false + * @endcode + * + * This operation is equivalent to manually adding the id with the AUTO_OVERRIDE + * bit applied: + * + * @code + * ecs_add_id(world, entity, ECS_AUTO_OVERRIDE | id); + * @endcode + * + * When a component is overridden and inherited from a prefab, the value from + * the prefab component is copied to the instance. When the component is not + * inherited from a prefab, it is added to the instance as if using ecs_add_id(). + * + * Overriding is the default behavior on prefab instantiation. Auto overriding + * is only useful for components with the `(OnInstantiate, Inherit)` trait. + * When a component has the `(OnInstantiate, DontInherit)` trait and is overridden + * the component is added, but the value from the prefab will not be copied. + * * @param world The world. * @param entity The entity. - * @param id The id to override. + * @param id The (component) id to auto override. */ FLECS_API -void ecs_override_id( +void ecs_auto_override_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); @@ -5363,7 +6153,7 @@ void ecs_clear( /** Remove all instances of the specified (component) id. * This will remove the specified id from all entities (tables). The id may be * a wildcard and/or a pair. - * + * * @param world The world. * @param id The id. */ @@ -5385,10 +6175,10 @@ ecs_entity_t ecs_set_with( ecs_id_t id); /** Get current with id. - * Get the id set with ecs_set_with. + * Get the id set with ecs_set_with(). * * @param world The world. - * @return The last id provided to ecs_set_with. + * @return The last id provided to ecs_set_with(). */ FLECS_API ecs_id_t ecs_get_with( @@ -5398,14 +6188,15 @@ ecs_id_t ecs_get_with( /** * @defgroup enabling_disabling Enabling & Disabling - * @brief Functions for enabling/disabling entities and components. + * Functions for enabling/disabling entities and components. + * * @{ */ /** Enable or disable entity. * This operation enables or disables an entity by adding or removing the - * EcsDisabled tag. A disabled entity will not be matched with any systems, - * unless the system explicitly specifies the EcsDisabled tag. + * #EcsDisabled tag. A disabled entity will not be matched with any systems, + * unless the system explicitly specifies the #EcsDisabled tag. * * @param world The world. * @param entity The entity to enable or disable. @@ -5430,7 +6221,7 @@ void ecs_enable( * @param id The component. * @param enable True to enable the component, false to disable. */ -FLECS_API +FLECS_API void ecs_enable_id( ecs_world_t *world, ecs_entity_t entity, @@ -5440,14 +6231,14 @@ void ecs_enable_id( /** Test if component is enabled. * Test whether a component is currently enabled or disabled. This operation * will return true when the entity has the component and if it has not been - * disabled by ecs_enable_component. + * disabled by ecs_enable_component(). * * @param world The world. * @param entity The entity. * @param id The component. * @return True if the component is enabled, otherwise false. */ -FLECS_API +FLECS_API bool ecs_is_enabled_id( const ecs_world_t *world, ecs_entity_t entity, @@ -5457,18 +6248,24 @@ bool ecs_is_enabled_id( /** * @defgroup getting Getting & Setting - * @brief Functions for getting/setting components. + * Functions for getting/setting components. + * * @{ */ /** Get an immutable pointer to a component. * This operation obtains a const pointer to the requested component. The * operation accepts the component entity id. + * + * This operation can return inherited components reachable through an `IsA` + * relationship. * * @param world The world. * @param entity The entity. * @param id The id of the component to get. * @return The component pointer, NULL if the entity does not have the component. + * + * @see ecs_get_mut_id() */ FLECS_API const void* ecs_get_id( @@ -5476,53 +6273,28 @@ const void* ecs_get_id( ecs_entity_t entity, ecs_id_t id); -/** Create a component ref. - * A ref is a handle to an entity + component which caches a small amount of - * data to reduce overhead of repeatedly accessing the component. Use - * ecs_ref_get to get the component data. +/** Get a mutable pointer to a component. + * This operation obtains a mutable pointer to the requested component. The + * operation accepts the component entity id. + * + * Unlike ecs_get_id(), this operation does not return inherited components. * * @param world The world. * @param entity The entity. - * @param id The id of the component. - * @return The reference. - */ -FLECS_API -ecs_ref_t ecs_ref_init_id( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id); - -/** Get component from ref. - * Get component pointer from ref. The ref must be created with ecs_ref_init. - * - * @param world The world. - * @param ref The ref. - * @param id The component id. + * @param id The id of the component to get. * @return The component pointer, NULL if the entity does not have the component. */ FLECS_API -void* ecs_ref_get_id( +void* ecs_get_mut_id( const ecs_world_t *world, - ecs_ref_t *ref, + ecs_entity_t entity, ecs_id_t id); -/** Update ref. - * Ensures contents of ref are up to date. Same as ecs_ref_get_id, but does not - * return pointer to component id. - * - * @param world The world. - * @param ref The ref. - */ -FLECS_API -void ecs_ref_update( - const ecs_world_t *world, - ecs_ref_t *ref); - /** Get a mutable pointer to a component. * 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 + * + * If ensure 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 @@ -5531,57 +6303,114 @@ void ecs_ref_update( * @param entity The entity. * @param id The entity id of the component to obtain. * @return The component pointer. + * + * @see ecs_ensure_modified_id() + * @see ecs_emplace_id() */ FLECS_API -void* ecs_get_mut_id( +void* ecs_ensure_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); -/** Combines get_mut + modifed in single operation. - * This operation is a more efficient alternative to calling ecs_get_mut_id and - * ecs_modified_id separately. This operation is only valid when the world is in +/** Combines ensure + modified in single operation. + * This operation is a more efficient alternative to calling ecs_ensure_id() and + * ecs_modified_id() separately. This operation is only valid when the world is in * deferred mode, which ensures that the Modified event is not emitted before * the modification takes place. - * + * * @param world The world. * @param entity The entity. * @param id The id of the component to obtain. * @return The component pointer. */ FLECS_API -void* ecs_get_mut_modified_id( +void* ecs_ensure_modified_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); -/** Begin exclusive write access to entity. - * This operation provides safe exclusive access to the components of an entity - * without the overhead of deferring operations. - * - * When this operation is called simultaneously for the same entity more than - * once it will throw an assert. Note that for this to happen, asserts must be - * enabled. It is up to the application to ensure that access is exclusive, for - * example by using a read-write mutex. - * - * Exclusive access is enforced at the table level, so only one entity can be - * exclusively accessed per table. The exclusive access check is thread safe. - * - * This operation must be followed up with ecs_write_end. - * +/** Create a component ref. + * A ref is a handle to an entity + component which caches a small amount of + * data to reduce overhead of repeatedly accessing the component. Use + * ecs_ref_get() to get the component data. + * * @param world The world. * @param entity The entity. - * @return A record to the entity. + * @param id The id of the component. + * @return The reference. */ FLECS_API -ecs_record_t* ecs_write_begin( - ecs_world_t *world, - ecs_entity_t entity); - -/** End exclusive write access to entity. - * This operation ends exclusive access, and must be called after - * ecs_write_begin. +ecs_ref_t ecs_ref_init_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Get component from ref. + * Get component pointer from ref. The ref must be created with ecs_ref_init(). + * + * @param world The world. + * @param ref The ref. + * @param id The component id. + * @return The component pointer, NULL if the entity does not have the component. + */ +FLECS_API +void* ecs_ref_get_id( + const ecs_world_t *world, + ecs_ref_t *ref, + ecs_id_t id); + +/** Update ref. + * Ensures contents of ref are up to date. Same as ecs_ref_get_id(), but does not + * return pointer to component id. + * + * @param world The world. + * @param ref The ref. + */ +FLECS_API +void ecs_ref_update( + const ecs_world_t *world, + ecs_ref_t *ref); + +/** Find record for entity. + * An entity record contains the table and row for the entity. * + * @param world The world. + * @param entity The entity. + * @return The record, NULL if the entity does not exist. + */ +FLECS_API +ecs_record_t* ecs_record_find( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Begin exclusive write access to entity. + * This operation provides safe exclusive access to the components of an entity + * without the overhead of deferring operations. + * + * When this operation is called simultaneously for the same entity more than + * once it will throw an assert. Note that for this to happen, asserts must be + * enabled. It is up to the application to ensure that access is exclusive, for + * example by using a read-write mutex. + * + * Exclusive access is enforced at the table level, so only one entity can be + * exclusively accessed per table. The exclusive access check is thread safe. + * + * This operation must be followed up with ecs_write_end(). + * + * @param world The world. + * @param entity The entity. + * @return A record to the entity. + */ +FLECS_API +ecs_record_t* ecs_write_begin( + ecs_world_t *world, + ecs_entity_t entity); + +/** End exclusive write access to entity. + * This operation ends exclusive access, and must be called after + * ecs_write_begin(). + * * @param record Record to the entity. */ FLECS_API @@ -5591,17 +6420,17 @@ void ecs_write_end( /** Begin read access to entity. * This operation provides safe read access to the components of an entity. * Multiple simultaneous reads are allowed per entity. - * + * * This operation ensures that code attempting to mutate the entity's table will * throw an assert. Note that for this to happen, asserts must be enabled. It is * up to the application to ensure that this does not happen, for example by * using a read-write mutex. - * + * * This operation does *not* provide the same guarantees as a read-write mutex, - * as it is possible to call ecs_read_begin after calling ecs_write_begin. It is + * as it is possible to call ecs_read_begin() after calling ecs_write_begin(). It is * up to application has to ensure that this does not happen. - * - * This operation must be followed up with ecs_read_end. + * + * This operation must be followed up with ecs_read_end(). * * @param world The world. * @param entity The entity. @@ -5613,7 +6442,7 @@ const ecs_record_t* ecs_read_begin( ecs_entity_t entity); /** End read access to entity. - * This operation ends read access, and must be called after ecs_read_begin. + * This operation ends read access, and must be called after ecs_read_begin(). * * @param record Record to the entity. */ @@ -5623,51 +6452,55 @@ void ecs_read_end( /** Get entity corresponding with record. * This operation only works for entities that are not empty. - * + * * @param record The record for which to obtain the entity id. + * @return The entity id for the record. */ FLECS_API ecs_entity_t ecs_record_get_entity( const ecs_record_t *record); /** Get component from entity record. - * This operation returns a pointer to a component for the entity + * This operation returns a pointer to a component for the entity * associated with the provided record. For safe access to the component, obtain - * the record with ecs_read_begin or ecs_write_begin. - * + * the record with ecs_read_begin() or ecs_write_begin(). + * * Obtaining a component from a record is faster than obtaining it from the * entity handle, as it reduces the number of lookups required. - * + * * @param world The world. * @param record Record to the entity. * @param id The (component) id. * @return Pointer to component, or NULL if entity does not have the component. + * + * @see ecs_record_ensure_id() */ FLECS_API const void* ecs_record_get_id( - ecs_world_t *world, + const ecs_world_t *world, const ecs_record_t *record, ecs_id_t id); -/** Same as ecs_record_get_id, but returns a mutable pointer. - * For safe access to the component, obtain the record with ecs_write_begin. - * +/** Same as ecs_record_get_id(), but returns a mutable pointer. + * For safe access to the component, obtain the record with ecs_write_begin(). + * * @param world The world. * @param record Record to the entity. * @param id The (component) id. * @return Pointer to component, or NULL if entity does not have the component. */ FLECS_API -void* ecs_record_get_mut_id( +void* ecs_record_ensure_id( ecs_world_t *world, ecs_record_t *record, ecs_id_t id); -/** Test if entity for record has component. - * +/** Test if entity for record has a (component) id. + * * @param world The world. * @param record Record to the entity. * @param id The (component) id. + * @return Whether the entity has the component. */ FLECS_API bool ecs_record_has_id( @@ -5675,35 +6508,64 @@ bool ecs_record_has_id( const ecs_record_t *record, ecs_id_t id); +/** Get component pointer from column/record. + * This returns a pointer to the component using a table column index. The + * table's column index can be found with ecs_table_get_column_index(). + * + * Usage: + * @code + * ecs_record_t *r = ecs_record_find(world, entity); + * int32_t column = ecs_table_get_column_index(world, table, ecs_id(Position)); + * Position *ptr = ecs_record_get_by_column(r, column, sizeof(Position)); + * @endcode + * + * @param record The record. + * @param column The column index in the entity's table. + * @param size The component size. + * @return The component pointer. + */ +FLECS_API +void* ecs_record_get_by_column( + const ecs_record_t *record, + int32_t column, + size_t size); + /** Emplace a component. - * Emplace is similar to get_mut except that the component constructor is not - * invoked for the returned pointer, allowing the component to be "constructed" - * directly in the storage. - * - * Emplace can only be used if the entity does not yet have the component. If - * the entity has the component, the operation will fail. + * Emplace is similar to ecs_ensure_id() except that the component constructor + * is not invoked for the returned pointer, allowing the component to be + * constructed directly in the storage. + * + * When the `is_new` parameter is not provided, the operation will assert when the + * component already exists. When the `is_new` parameter is provided, it will + * indicate whether the returned storage has been constructed. + * + * When `is_new` indicates that the storage has not yet been constructed, it must + * be constructed by the code invoking this operation. Not constructing the + * component will result in undefined behavior. * * @param world The world. * @param entity The entity. * @param id The component to obtain. + * @param is_new Whether this is an existing or new component. * @return The (uninitialized) component pointer. */ FLECS_API void* ecs_emplace_id( ecs_world_t *world, ecs_entity_t entity, - ecs_id_t id); + ecs_id_t id, + bool *is_new); /** Signal that a component has been modified. * This operation is usually used after modifying a component value obtained by - * ecs_get_mut_id. The operation will mark the component as dirty, and invoke + * ecs_ensure_id(). The operation will mark the component as dirty, and invoke * OnSet observers and hooks. * * @param world The world. * @param entity The entity. * @param id The id of the component that was modified. */ -FLECS_API +FLECS_API void ecs_modified_id( ecs_world_t *world, ecs_entity_t entity, @@ -5711,8 +6573,8 @@ void ecs_modified_id( /** Set the value of a component. * This operation allows an application to set the value of a component. The - * operation is equivalent to calling ecs_get_mut_id followed by - * ecs_modified_id. The operation will not modify the value of the passed in + * operation is equivalent to calling ecs_ensure_id() followed by + * ecs_modified_id(). The operation will not modify the value of the passed in * component. If the component has a copy hook registered, it will be used to * copy in the component. * @@ -5723,10 +6585,9 @@ void ecs_modified_id( * @param id The id of the component to set. * @param size The size of the pointed-to value. * @param ptr The pointer to the value. - * @return The entity. A new entity if no entity was provided. */ FLECS_API -ecs_entity_t ecs_set_id( +void ecs_set_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, @@ -5737,19 +6598,20 @@ ecs_entity_t ecs_set_id( /** * @defgroup liveliness Entity Liveliness - * @brief Functions for testing and modifying entity liveliness. + * Functions for testing and modifying entity liveliness. + * * @{ */ /** Test whether an entity is valid. - * Entities that are valid can be used with API functions. Using invalid + * Entities that are valid can be used with API functions. Using invalid * entities with API operations will cause the function to panic. * * An entity is valid if it is not 0 and if it is alive. * - * is_valid will return true for ids that don't exist (alive or not alive). This - * allows for using ids that have never been created by ecs_new or similar. In - * this the function differs from ecs_is_alive, which will return false for + * ecs_is_valid() will return true for ids that don't exist (alive or not alive). This + * allows for using ids that have never been created by ecs_new_w() or similar. In + * this the function differs from ecs_is_alive(), which will return false for * entities that do not yet exist. * * The operation will return false for an id that exists and is not alive, as @@ -5766,22 +6628,24 @@ bool ecs_is_valid( /** Test whether an entity is alive. * Entities are alive after they are created, and become not alive when they are - * deleted. Operations that return alive ids are (amongst others) ecs_new_id, - * ecs_new_low_id and ecs_entity_init. Ids can be made alive with the ecs_ensure + * deleted. Operations that return alive ids are (amongst others) ecs_new(), + * ecs_new_low_id() and ecs_entity_init(). Ids can be made alive with the ecs_make_alive() * function. - * + * * After an id is deleted it can be recycled. Recycled ids are different from * the original id in that they have a different generation count. This makes it * possible for the API to distinguish between the two. An example: - * - * ecs_entity_t e1 = ecs_new_id(world); - * ecs_is_alive(world, e1); // true - * ecs_delete(world, e1); - * ecs_is_alive(world, e1); // false - * - * ecs_entity_t e2 = ecs_new_id(world); // recycles e1 - * ecs_is_alive(world, e2); // true - * ecs_is_alive(world, e1); // false + * + * @code + * ecs_entity_t e1 = ecs_new(world); + * ecs_is_alive(world, e1); // true + * ecs_delete(world, e1); + * ecs_is_alive(world, e1); // false + * + * ecs_entity_t e2 = ecs_new(world); // recycles e1 + * ecs_is_alive(world, e2); // true + * ecs_is_alive(world, e1); // false + * @endcode * * @param world The world. * @param e The entity. @@ -5801,29 +6665,13 @@ FLECS_API ecs_id_t ecs_strip_generation( ecs_entity_t e); -/** Override the generation of an entity. - * The generation count of an entity is increased each time an entity is deleted - * and is used to test whether an entity id is alive. - * - * This operation overrides the current generation of an entity with the - * specified generation, which can be useful if an entity is externally managed, - * like for external pools, savefiles or netcode. - * - * @param world The world. - * @param entity Entity for which to set the generation with the new generation. - */ -FLECS_API -void ecs_set_entity_generation( - ecs_world_t *world, - ecs_entity_t entity); - /** Get alive identifier. * In some cases an application may need to work with identifiers from which * the generation has been stripped. A typical scenario in which this happens is * when iterating relationships in an entity type. * - * For example, when obtaining the parent id from a ChildOf relationship, the parent - * (second element of the pair) will have been stored in a 32 bit value, which + * For example, when obtaining the parent id from a `ChildOf` relationship, the parent + * (second element of the pair) will have been stored in a 32 bit value, which * cannot store the entity generation. This function can retrieve the identifier * with the current generation for that id. * @@ -5841,9 +6689,9 @@ ecs_entity_t ecs_get_alive( /** Ensure id is alive. * This operation ensures that the provided id is alive. This is useful in * scenarios where an application has an existing id that has not been created - * with ecs_new (such as a global constant or an id from a remote application). - * - * When this operation is successful it guarantees that the provided id exists, + * with ecs_new_w() (such as a global constant or an id from a remote application). + * + * When this operation is successful it guarantees that the provided id exists, * is valid and is alive. * * Before this operation the id must either not be alive or have a generation @@ -5851,43 +6699,45 @@ ecs_entity_t ecs_get_alive( * * If the provided id has a non-zero generation count and the id does not exist * in the world, the id will be created with the specified generation. - * + * * If the provided id is alive and has a generation count that does not match * the provided id, the operation will fail. * * @param world The world. * @param entity The entity id to make alive. + * + * @see ecs_make_alive_id() */ FLECS_API -void ecs_ensure( +void ecs_make_alive( ecs_world_t *world, ecs_entity_t entity); -/** Same as ecs_ensure, but for (component) ids. +/** Same as ecs_make_alive(), but for (component) ids. * An id can be an entity or pair, and can contain id flags. This operation * ensures that the entity (or entities, for a pair) are alive. - * + * * When this operation is successful it guarantees that the provided id can be * used in operations that accept an id. - * + * * Since entities in a pair do not encode their generation ids, this operation * will not fail when an entity with non-zero generation count already exists in - * the world. - * - * This is different from ecs_ensure, which will fail if attempted with an id - * that has generation 0 and an entity with a non-zero generation is currently + * the world. + * + * This is different from ecs_make_alive(), which will fail if attempted with an id + * that has generation 0 and an entity with a non-zero generation is currently * alive. - * + * * @param world The world. * @param id The id to make alive. */ FLECS_API -void ecs_ensure_id( +void ecs_make_alive_id( ecs_world_t *world, ecs_id_t id); /** Test whether an entity exists. - * Similar as ecs_is_alive, but ignores entity generation count. + * Similar as ecs_is_alive(), but ignores entity generation count. * * @param world The world. * @param entity The entity. @@ -5898,11 +6748,31 @@ bool ecs_exists( const ecs_world_t *world, ecs_entity_t entity); +/** Override the generation of an entity. + * The generation count of an entity is increased each time an entity is deleted + * and is used to test whether an entity id is alive. + * + * This operation overrides the current generation of an entity with the + * specified generation, which can be useful if an entity is externally managed, + * like for external pools, savefiles or netcode. + * + * This operation is similar to ecs_make_alive(), except that it will also + * override the generation of an alive entity. + * + * @param world The world. + * @param entity Entity for which to set the generation with the new generation. + */ +FLECS_API +void ecs_set_version( + ecs_world_t *world, + ecs_entity_t entity); + /** @} */ /** * @defgroup entity_info Entity Information. - * @brief Get information from entity. + * Get information from entity. + * * @{ */ @@ -5929,8 +6799,8 @@ ecs_table_t* ecs_get_table( ecs_entity_t entity); /** Convert type to string. - * The result of this operation must be freed with ecs_os_free. - * + * The result of this operation must be freed with ecs_os_free(). + * * @param world The world. * @param type The type. * @return The stringified type. @@ -5941,12 +6811,15 @@ char* ecs_type_str( const ecs_type_t* type); /** Convert table to string. - * Same as ecs_type_str(world, ecs_table_get_type(table)). The result of this - * operation must be freed with ecs_os_free. + * Same as `ecs_type_str(world, ecs_table_get_type(table))`. The result of this + * operation must be freed with ecs_os_free(). * * @param world The world. * @param table The table. * @return The stringified table type. + * + * @see ecs_table_get_type() + * @see ecs_type_str() */ FLECS_API char* ecs_table_str( @@ -5955,14 +6828,17 @@ char* ecs_table_str( /** Convert entity to string. * Same as combining: - * - ecs_get_fullpath(world, entity) + * - ecs_get_path(world, entity) * - ecs_type_str(world, ecs_get_type(world, entity)) - * - * The result of this operation must be freed with ecs_os_free. + * + * The result of this operation must be freed with ecs_os_free(). * * @param world The world. * @param entity The entity. * @return The entity path with stringified type. + * + * @see ecs_get_path() + * @see ecs_type_str() */ FLECS_API char* ecs_entity_str( @@ -5976,6 +6852,8 @@ char* ecs_entity_str( * @param entity The entity. * @param id The id to test for. * @return True if the entity has the id, false if not. + * + * @see ecs_owns_id() */ FLECS_API bool ecs_has_id( @@ -5985,8 +6863,8 @@ bool ecs_has_id( /** Test if an entity owns an id. * This operation returns true if the entity has the specified id. The operation - * behaves the same as ecs_has_id, except that it will return false for - * components that are inherited through an IsA relationship. + * behaves the same as ecs_has_id(), except that it will return false for + * components that are inherited through an `IsA` relationship. * * @param world The world. * @param entity The entity. @@ -6000,8 +6878,8 @@ bool ecs_owns_id( ecs_id_t id); /** Get the target of a relationship. - * This will return a target (second element of a pair) of the entity for the - * specified relationship. The index allows for iterating through the targets, + * This will return a target (second element of a pair) of the entity for the + * specified relationship. The index allows for iterating through the targets, * if a single entity has multiple targets for the same relationship. * * If the index is larger than the total number of instances the entity has for @@ -6020,13 +6898,18 @@ ecs_entity_t ecs_get_target( ecs_entity_t rel, int32_t index); -/** Get parent (target of ChildOf relationship) for entity. +/** Get parent (target of `ChildOf` relationship) for entity. * This operation is the same as calling: - * ecs_get_target(world, entity, EcsChildOf, 0); - * + * + * @code + * ecs_get_target(world, entity, EcsChildOf, 0); + * @endcode + * * @param world The world. * @param entity The entity. * @return The parent of the entity, 0 if the entity has no parent. + * + * @see ecs_get_target() */ FLECS_API ecs_entity_t ecs_get_parent( @@ -6038,13 +6921,15 @@ ecs_entity_t ecs_get_parent( * the specified relationship. If the entity itself has the id then entity will * be returned. If the id cannot be found on the entity or by following the * relationship, the operation will return 0. - * + * * This operation can be used to lookup, for example, which prefab is providing - * a component by specifying the IsA relationship: - * - * // Is Position provided by the entity or one of its base entities? - * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) - * + * a component by specifying the `IsA` relationship: + * + * @code + * // Is Position provided by the entity or one of its base entities? + * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) + * @endcode + * * @param world The world. * @param entity The entity. * @param rel The relationship to follow. @@ -6059,10 +6944,10 @@ ecs_entity_t ecs_get_target_for_id( ecs_id_t id); /** Return depth for entity in tree for the specified relationship. - * Depth is determined by counting the number of targets encountered while + * Depth is determined by counting the number of targets encountered while * traversing up the relationship tree for rel. Only acyclic relationships are * supported. - * + * * @param world The world. * @param entity The entity. * @param rel The relationship. @@ -6074,48 +6959,6 @@ int32_t ecs_get_depth( ecs_entity_t entity, ecs_entity_t rel); -typedef struct ecs_flatten_desc_t { - /* When true, the flatten operation will not remove names from entities in - * the flattened tree. This may fail if entities from different subtrees - * have the same name. */ - bool keep_names; - - /* When true, the flattened tree won't contain information about the - * original depth of the entities. This can reduce fragmentation, but may - * cause existing code, such as cascade queries, to no longer work. */ - bool lose_depth; -} ecs_flatten_desc_t; - -/** Recursively flatten relationship for target entity (experimental). - * This operation combines entities in the subtree of the specified pair from - * different parents in the same table. This can reduce memory fragmentation - * and reduces the number of tables in the storage, which improves RAM - * utilization and various other operations, such as entity cleanup. - * - * The lifecycle of entities in a fixed subtree are bound to the specified - * parent. Entities in a fixed subtree cannot be deleted individually. Entities - * can also not change the target of the fixed relationship, which includes - * removing the relationship. - * - * Entities in a fixed subtree are still fragmented on subtree depth. This - * ensures that entities can still be iterated in breadth-first order with the - * cascade query modifier. - * - * The current implementation is limited to exclusive acyclic relationships, and - * does not allow for adding/removing to entities in flattened tables. An entity - * may only be flattened for a single relationship. Future iterations of the - * feature may remove these limitations. - * - * @param world The world. - * @param pair The relationship pair from which to start flattening. - * @param desc Options for flattening the tree. - */ -FLECS_API -void ecs_flatten( - ecs_world_t *world, - ecs_id_t pair, - const ecs_flatten_desc_t *desc); - /** Count entities that have the specified id. * Returns the number of entities that have the specified id. * @@ -6133,16 +6976,19 @@ int32_t ecs_count_id( /** * @defgroup paths Entity Names - * @brief Functions for working with entity names and paths. + * Functions for working with entity names and paths. + * * @{ */ /** Get the name of an entity. - * This will return the name stored in (EcsIdentifier, EcsName). + * This will return the name stored in `(EcsIdentifier, EcsName)`. * * @param world The world. * @param entity The entity. * @return The type of the entity, NULL if the entity has no name. + * + * @see ecs_set_name() */ FLECS_API const char* ecs_get_name( @@ -6150,11 +6996,13 @@ const char* ecs_get_name( ecs_entity_t entity); /** Get the symbol of an entity. - * This will return the symbol stored in (EcsIdentifier, EcsSymbol). + * This will return the symbol stored in `(EcsIdentifier, EcsSymbol)`. * * @param world The world. * @param entity The entity. * @return The type of the entity, NULL if the entity has no name. + * + * @see ecs_set_symbol() */ FLECS_API const char* ecs_get_symbol( @@ -6165,12 +7013,14 @@ const char* ecs_get_symbol( * This will set or overwrite the name of an entity. If no entity is provided, * a new entity will be created. * - * The name is stored in (EcsIdentifier, EcsName). + * The name is stored in `(EcsIdentifier, EcsName)`. * * @param world The world. * @param entity The entity. * @param name The name. * @return The provided entity, or a new entity if 0 was provided. + * + * @see ecs_get_name() */ FLECS_API ecs_entity_t ecs_set_name( @@ -6188,6 +7038,8 @@ ecs_entity_t ecs_set_name( * @param entity The entity. * @param symbol The symbol. * @return The provided entity, or a new entity if 0 was provided. + * + * @see ecs_get_symbol() */ FLECS_API ecs_entity_t ecs_set_symbol( @@ -6195,13 +7047,13 @@ ecs_entity_t ecs_set_symbol( ecs_entity_t entity, const char *symbol); -/** Set alias for entity. - * An entity can be looked up using its alias from the root scope without +/** Set alias for entity. + * An entity can be looked up using its alias from the root scope without * providing the fully qualified name if its parent. An entity can only have * a single alias. - * - * The symbol is stored in (EcsIdentifier, EcsAlias). - * + * + * The symbol is stored in `(EcsIdentifier, EcsAlias)`. + * * @param world The world. * @param entity The entity. * @param alias The alias. @@ -6212,18 +7064,25 @@ void ecs_set_alias( ecs_entity_t entity, const char *alias); -/** Lookup an entity by name. - * Returns an entity that matches the specified name. Only looks for entities in - * the current scope (root if no scope is provided). +/** Lookup an entity by it's path. + * This operation is equivalent to calling: + * + * @code + * ecs_lookup_path_w_sep(world, 0, path, ".", NULL, true); + * @endcode * * @param world The world. - * @param name The entity name. - * @return The entity with the specified name, or 0 if no entity was found. + * @param path The entity path. + * @return The entity with the specified path, or 0 if no entity was found. + * + * @see ecs_lookup_child() + * @see ecs_lookup_path_w_sep() + * @see ecs_lookup_symbol() */ FLECS_API ecs_entity_t ecs_lookup( const ecs_world_t *world, - const char *name); + const char *path); /** Lookup a child entity by name. * Returns an entity that matches the specified name. Only looks for entities in @@ -6231,8 +7090,13 @@ ecs_entity_t ecs_lookup( * root if no scope is provided). * * @param world The world. + * @param parent The parent for which to lookup the child. * @param name The entity name. * @return The entity with the specified name, or 0 if no entity was found. + * + * @see ecs_lookup() + * @see ecs_lookup_path_w_sep() + * @see ecs_lookup_symbol() */ FLECS_API ecs_entity_t ecs_lookup_child( @@ -6245,7 +7109,7 @@ ecs_entity_t ecs_lookup_child( * operation will use the provided separator to tokenize the path expression. If * the provided path contains the prefix, the search will start from the root. * - * If the entity is not found in the provided parent, the operation will + * If the entity is not found in the provided parent, the operation will * continue to search in the parent of the parent, until the root is reached. If * the entity is still not found, the lookup will search in the flecs.core * scope. If the entity is not found there either, the function returns 0. @@ -6257,6 +7121,10 @@ ecs_entity_t ecs_lookup_child( * @param prefix The path prefix. * @param recursive Recursively traverse up the tree until entity is found. * @return The entity if found, else 0. + * + * @see ecs_lookup() + * @see ecs_lookup_child() + * @see ecs_lookup_symbol() */ FLECS_API ecs_entity_t ecs_lookup_path_w_sep( @@ -6268,17 +7136,21 @@ ecs_entity_t ecs_lookup_path_w_sep( bool recursive); /** Lookup an entity by its symbol name. - * This looks up an entity by symbol stored in (EcsIdentifier, EcsSymbol). The + * This looks up an entity by symbol stored in `(EcsIdentifier, EcsSymbol)`. The * operation does not take into account hierarchies. * - * This operation can be useful to resolve, for example, a type by its C + * This operation can be useful to resolve, for example, a type by its C * identifier, which does not include the Flecs namespacing. - * + * * @param world The world. * @param symbol The symbol. * @param lookup_as_path If not found as a symbol, lookup as path. * @param recursive If looking up as path, recursively traverse up the tree. * @return The entity if found, else 0. + * + * @see ecs_lookup() + * @see ecs_lookup_child() + * @see ecs_lookup_path_w_sep() */ FLECS_API ecs_entity_t ecs_lookup_symbol( @@ -6289,12 +7161,12 @@ ecs_entity_t ecs_lookup_symbol( /** Get a path identifier for an entity. * This operation creates a path that contains the names of the entities from - * the specified parent to the provided entity, separated by the provided + * the specified parent to the provided entity, separated by the provided * separator. If no parent is provided the path will be relative to the root. If * a prefix is provided, the path will be prefixed by the prefix. * * If the parent is equal to the provided child, the operation will return an - * empty string. If a nonzero component is provided, the path will be created by + * empty string. If a nonzero component is provided, the path will be created by * looking for parents with that component. * * The returned path should be freed by the application. @@ -6305,6 +7177,8 @@ ecs_entity_t ecs_lookup_symbol( * @param sep The separator to use between path elements. * @param prefix The initial character to use for root elements. * @return The relative entity path. + * + * @see ecs_get_path_w_sep_buf() */ FLECS_API char* ecs_get_path_w_sep( @@ -6315,14 +7189,16 @@ char* ecs_get_path_w_sep( const char *prefix); /** Write path identifier to buffer. - * Same as ecs_get_path_w_sep, but writes result to an ecs_strbuf_t. - * + * Same as ecs_get_path_w_sep(), but writes result to an ecs_strbuf_t. + * * @param world The world. * @param parent The entity from which to create the path. * @param child The entity to which to create the path. * @param sep The separator to use between path elements. * @param prefix The initial character to use for root elements. * @param buf The buffer to write to. + * + * @see ecs_get_path_w_sep() */ void ecs_get_path_w_sep_buf( const ecs_world_t *world, @@ -6330,7 +7206,8 @@ void ecs_get_path_w_sep_buf( ecs_entity_t child, const char *sep, const char *prefix, - ecs_strbuf_t *buf); + ecs_strbuf_t *buf, + bool escape); /** Find or create entity from path. * This operation will find or create an entity from a path, and will create any @@ -6356,7 +7233,7 @@ ecs_entity_t ecs_new_from_path_w_sep( const char *prefix); /** Add specified path to entity. - * This operation is similar to ecs_new_from_path, but will instead add the path + * This operation is similar to ecs_new_from_path(), but will instead add the path * to an existing entity. * * If an entity already exists for the path, it will be returned instead. @@ -6368,7 +7245,7 @@ ecs_entity_t ecs_new_from_path_w_sep( * @param sep The separator used in the path. * @param prefix The prefix used in the path. * @return The entity. - */ + */ FLECS_API ecs_entity_t ecs_add_path_w_sep( ecs_world_t *world, @@ -6388,6 +7265,8 @@ ecs_entity_t ecs_add_path_w_sep( * @param world The world. * @param scope The entity to use as scope. * @return The previous scope. + * + * @see ecs_get_scope() */ FLECS_API ecs_entity_t ecs_set_scope( @@ -6395,7 +7274,7 @@ ecs_entity_t ecs_set_scope( ecs_entity_t scope); /** Get the current scope. - * Get the scope set by ecs_set_scope. If no scope is set, this operation will + * Get the scope set by ecs_set_scope(). If no scope is set, this operation will * return 0. * * @param world The world. @@ -6407,7 +7286,7 @@ ecs_entity_t ecs_get_scope( /** Set a name prefix for newly created entities. * This is a utility that lets C modules use prefixed names for C types and - * C functions, while using names for the entity names that do not have the + * C functions, while using names for the entity names that do not have the * prefix. The name prefix is currently only used by ECS_COMPONENT. * * @param world The world. @@ -6417,31 +7296,33 @@ ecs_entity_t ecs_get_scope( FLECS_API const char* ecs_set_name_prefix( ecs_world_t *world, - const char *prefix); + const char *prefix); /** Set search path for lookup operations. * This operation accepts an array of entity ids that will be used as search * scopes by lookup operations. The operation returns the current search path. * It is good practice to restore the old search path. - * + * * The search path will be evaluated starting from the last element. - * + * * The default search path includes flecs.core. When a custom search path is * provided it overwrites the existing search path. Operations that rely on * looking up names from flecs.core without providing the namespace may fail if * the custom search path does not include flecs.core (EcsFlecsCore). - * + * * The search path array is not copied into managed memory. The application must * ensure that the provided array is valid for as long as it is used as the * search path. - * + * * The provided array must be terminated with a 0 element. This enables an * application to push/pop elements to an existing array without invoking the - * ecs_set_lookup_path operation again. - * + * ecs_set_lookup_path() operation again. + * * @param world The world. * @param lookup_path 0-terminated array with entity ids for the lookup path. * @return Current lookup path array. + * + * @see ecs_get_lookup_path() */ FLECS_API ecs_entity_t* ecs_set_lookup_path( @@ -6449,8 +7330,8 @@ ecs_entity_t* ecs_set_lookup_path( const ecs_entity_t *lookup_path); /** Get current lookup path. - * Returns value set by ecs_set_lookup_path. - * + * Returns value set by ecs_set_lookup_path(). + * * @param world The world. * @return The current lookup path. */ @@ -6464,18 +7345,19 @@ ecs_entity_t* ecs_get_lookup_path( /** * @defgroup components Components - * @brief Functions for registering and working with components. + * Functions for registering and working with components. + * * @{ */ -/** Find or create a component. +/** Find or create a component. * This operation creates a new component, or finds an existing one. The find or - * create behavior is the same as ecs_entity_init. + * create behavior is the same as ecs_entity_init(). * * When an existing component is found, the size and alignment are verified with * the provided values. If the values do not match, the operation will fail. * - * See the documentation of ecs_component_desc_t for more details. + * See the documentation of ecs_component_desc_t for more details. * * @param world The world. * @param desc Component init parameters. @@ -6484,13 +7366,13 @@ ecs_entity_t* ecs_get_lookup_path( FLECS_API ecs_entity_t ecs_component_init( ecs_world_t *world, - const ecs_component_desc_t *desc); + const ecs_component_desc_t *desc); /** Get the type for an id. - * This function returnsthe type information for an id. The specified id can be + * This function returns the type information for an id. The specified id can be * any valid id. For the rules on how type information is determined based on - * id, see ecs_get_typeid. - * + * id, see ecs_get_typeid(). + * * @param world The world. * @param id The id. * @return The type information of the id. @@ -6504,8 +7386,8 @@ const ecs_type_info_t* ecs_get_type_info( * Hooks allow for the execution of user code when components are constructed, * copied, moved, destructed, added, removed or set. Hooks can be assigned as * as long as a component has not yet been used (added to an entity). - * - * The hooks that are currently set can be accessed with ecs_get_type_info. + * + * The hooks that are currently set can be accessed with ecs_get_type_info(). * * @param world The world. * @param id The component id for which to register the actions @@ -6518,34 +7400,35 @@ void ecs_set_hooks_id( const ecs_type_hooks_t *hooks); /** Get hooks for component. - * + * * @param world The world. * @param id The component id for which to retrieve the hooks. * @return The hooks for the component, or NULL if not registered. */ FLECS_API const ecs_type_hooks_t* ecs_get_hooks_id( - ecs_world_t *world, + const ecs_world_t *world, ecs_entity_t id); /** @} */ /** * @defgroup ids Ids - * @brief Functions for working with `ecs_id_t`. + * Functions for working with `ecs_id_t`. + * * @{ */ /** Returns whether specified id a tag. - * This operation returns whether the specified type is a tag (a component + * This operation returns whether the specified type is a tag (a component * without data/size). - * + * * An id is a tag when: * - it is an entity without the EcsComponent component * - it has an EcsComponent with size member set to 0 * - it is a pair where both elements are a tag - * - it is a pair where the first element has the EcsTag tag - * + * - it is a pair where the first element has the #EcsPairIsTag tag + * * @param world The world. * @param id The id. * @return Whether the provided id is a tag. @@ -6555,27 +7438,10 @@ bool ecs_id_is_tag( const ecs_world_t *world, ecs_id_t id); -/** Return whether represents a union. - * This operation returns whether the specified type represents a union. Only - * pair ids can be unions. - * - * An id represents a union when: - * - The first element of the pair is EcsUnion/flecs::Union - * - The first element of the pair has EcsUnion/flecs::Union - * - * @param world The world. - * @param id The id. - * @return Whether the provided id represents a union. - */ -FLECS_API -bool ecs_id_is_union( - const ecs_world_t *world, - ecs_id_t id); - /** Returns whether specified id is in use. * This operation returns whether an id is in use in the world. An id is in use * if it has been added to one or more tables. - * + * * @param world The world. * @param id The id. * @return Whether the id is in use. @@ -6589,12 +7455,12 @@ bool ecs_id_in_use( * This operation returns the component id for an id, if the id is associated * with a type. For a regular component with a non-zero size (an entity with the * EcsComponent component) the operation will return the entity itself. - * + * * For an entity that does not have the EcsComponent component, or with an * EcsComponent value with size 0, the operation will return 0. - * + * * For a pair id the operation will return the type associated with the pair, by - * applying the following rules in order: + * applying the following queries in order: * - The first pair element is returned if it is a component * - 0 is returned if the relationship entity has the Tag property * - The second pair element is returned if it is a component @@ -6615,6 +7481,7 @@ ecs_entity_t ecs_get_typeid( * * @param id The id. * @param pattern The pattern to compare with. + * @return Whether the id matches the pattern. */ FLECS_API bool ecs_id_match( @@ -6658,9 +7525,9 @@ bool ecs_id_is_valid( ecs_id_t id); /** Get flags associated with id. - * This operation returns the internal flags (see api_flags.h) that are + * This operation returns the internal flags (see api_flags.h) that are * associated with the provided id. - * + * * @param world The world. * @param id The id. * @return Flags associated with the id, or 0 if the id is not in use. @@ -6671,8 +7538,8 @@ ecs_flags32_t ecs_id_get_flags( ecs_id_t id); /** Convert id flag to string. - * This operation converts a id flag to a string. - * + * This operation converts an id flag to a string. + * * @param id_flags The id flag. * @return The id flag string, or NULL if no valid id is provided. */ @@ -6680,7 +7547,7 @@ FLECS_API const char* ecs_id_flag_str( ecs_id_t id_flags); -/** Convert id to string. +/** Convert (component) id to string. * This operation interprets the structure of an id and converts it to a string. * * @param world The world. @@ -6692,8 +7559,8 @@ char* ecs_id_str( const ecs_world_t *world, ecs_id_t id); -/** Write id string to buffer. - * Same as ecs_id_str but writes result to ecs_strbuf_t. +/** Write (component) id string to buffer. + * Same as ecs_id_str() but writes result to ecs_strbuf_t. * * @param world The world. * @param id The id to convert to a string. @@ -6705,85 +7572,34 @@ void ecs_id_str_buf( ecs_id_t id, ecs_strbuf_t *buf); +/** Convert string to a (component) id. + * This operation is the reverse of ecs_id_str(). The FLECS_SCRIPT addon + * is required for this operation to work. + * + * @param world The world. + * @param expr The string to convert to an id. + */ +FLECS_API +ecs_id_t ecs_id_from_str( + const ecs_world_t *world, + const char *expr); + /** @} */ /** - * @defgroup filters Filters - * @brief Functions for working with `ecs_term_t` and `ecs_filter_t`. + * @defgroup queries Queries + * @brief Functions for working with `ecs_term_t` and `ecs_query_t`. * @{ */ -/** Iterator for a single (component) id. - * A term iterator returns all entities (tables) that match a single (component) - * id. The search for the matching set of entities (tables) is performed in - * constant time. - * - * @param world The world. - * @param term The term. - * @return The iterator. - */ -FLECS_API -ecs_iter_t ecs_term_iter( - const ecs_world_t *world, - ecs_term_t *term); - -/** Return a chained term iterator. - * A chained iterator applies a filter to the results of the input iterator. The - * resulting iterator must be iterated with ecs_term_next. - * - * @param it The input iterator - * @param term The term filter to apply to the iterator. - * @return The chained iterator. - */ -FLECS_API -ecs_iter_t ecs_term_chain_iter( - const ecs_iter_t *it, - ecs_term_t *term); - -/** Progress a term iterator. - * This operation progresses the term iterator to the next table. The - * iterator must have been initialized with `ecs_term_iter`. This operation - * must be invoked at least once before interpreting the contents of the - * iterator. - * - * @param it The iterator. - * @returns True if more data is available, false if not. - */ -FLECS_API -bool ecs_term_next( - ecs_iter_t *it); - -/** Iterator for a parent's children. - * This operation is equivalent to a term iterator for (ChildOf, parent). - * Iterate the result with ecs_children_next. - * - * @param world The world. - * @param parent The parent for which to iterate the children. - * @return The iterator. - */ -FLECS_API -ecs_iter_t ecs_children( - const ecs_world_t *world, - ecs_entity_t parent); - -/** Progress a children iterator. - * Equivalent to ecs_term_next. - * - * @param it The iterator. - * @returns True if more data is available, false if not. - */ -FLECS_API -bool ecs_children_next( - ecs_iter_t *it); - -/** Test whether term id is set. +/** Test whether term id is set. * * @param id The term id. * @return True when set, false when not set. */ FLECS_API -bool ecs_term_id_is_set( - const ecs_term_id_t *id); +bool ecs_term_ref_is_set( + const ecs_term_ref_t *id); /** Test whether a term is set. * This operation can be used to test whether a term has been initialized with @@ -6803,14 +7619,14 @@ bool ecs_term_is_initialized( /** Is term matched on $this variable. * This operation checks whether a term is matched on the $this variable, which * is the default source for queries. - * + * * A term has a $this source when: * - ecs_term_t::src::id is EcsThis * - ecs_term_t::src::flags is EcsIsVariable - * + * * If ecs_term_t::src is not populated, it will be automatically initialized to * the $this source for the created query. - * + * * @param term The term. * @return True if term matches $this, false if not. */ @@ -6820,13 +7636,13 @@ bool ecs_term_match_this( /** Is term matched on 0 source. * This operation checks whether a term is matched on a 0 source. A 0 source is - * a term that isn't matched against anything, and can be used just to pass + * a term that isn't matched against anything, and can be used just to pass * (component) ids to a query iterator. - * + * * A term has a 0 source when: * - ecs_term_t::src::id is 0 * - ecs_term_t::src::flags has EcsIsEntity set - * + * * @param term The term. * @return True if term has 0 source, false if not. */ @@ -6834,139 +7650,10 @@ FLECS_API bool ecs_term_match_0( const ecs_term_t *term); -/** Finalize term. - * Ensure that all fields of a term are consistent and filled out. This - * operation should be invoked before using and after assigning members to, or - * parsing a term. When a term contains unresolved identifiers, this operation - * will resolve and assign the identifiers. If the term contains any identifiers - * that cannot be resolved, the operation will fail. - * - * An application generally does not need to invoke this operation as the APIs - * that use terms (such as filters, queries and triggers) will finalize terms - * when they are created. - * - * The name and expr parameters are optional, and only used for giving more - * descriptive error messages. - * - * @param world The world. - * @param term The term to finalize. - * @return Zero if success, nonzero if an error occurred. - */ -FLECS_API -int ecs_term_finalize( - const ecs_world_t *world, - ecs_term_t *term); - -/** Copy resources of a term to another term. - * This operation copies one term to another term. If the source term contains - * allocated resources (such as identifiers), they will be duplicated so that - * no memory is shared between the terms. - * - * @param src The term to copy from. - * @return The destination term. - */ -FLECS_API -ecs_term_t ecs_term_copy( - const ecs_term_t *src); - -/** Move resources of a term to another term. - * Same as copy, but moves resources from src, if src->move is set to true. If - * src->move is not set to true, this operation will do a copy. - * - * The conditional move reduces redundant allocations in scenarios where a list - * of terms is partially created with allocated resources. - * - * @param src The term to move from. - * @return The destination term. - */ -FLECS_API -ecs_term_t ecs_term_move( - ecs_term_t *src); - -/** Free resources of term. - * This operation frees all resources (such as identifiers) of a term. The term - * itself is not freed. - * - * @param term The term to free. - */ -FLECS_API -void ecs_term_fini( - ecs_term_t *term); - -/** Initialize filter - * A filter is a lightweight object that can be used to query for entities in - * a world. Filters, as opposed to queries, do not cache results. They are - * therefore slower to iterate, but are faster to create. - * - * When a filter is copied by value, make sure to use "ecs_filter_move" to - * ensure that the terms pointer still points to the inline array: - * - * ecs_filter_move(&dst_filter, &src_filter) - * - * Alternatively, the ecs_filter_move function can be called with both arguments - * set to the same filter, to ensure the pointer is valid: - * - * ecs_filter_move(&f, &f) - * - * It is possible to create a filter without allocating any memory, by setting - * the .storage member in ecs_filter_desc_t. See the documentation for the - * member for more details. - * - * @param world The world. - * @param desc Properties for the filter to create. - * @return The filter if successful, NULL if not successful. - */ -FLECS_API -ecs_filter_t * ecs_filter_init( - ecs_world_t *world, - const ecs_filter_desc_t *desc); - -/** Deinitialize filter. - * Free resources associated with filter. - * - * @param filter The filter to deinitialize. - */ -FLECS_API -void ecs_filter_fini( - ecs_filter_t *filter); - -/** Finalize filter. - * When manually assigning an array of terms to the filter struct (so not when - * using ecs_filter_init), this operation should be used to ensure that all - * terms are assigned properly and all (derived) fields have been set. - * - * When ecs_filter_init is used to create the filter, this function should not - * be called. The purpose of this operation is to support creation of filters - * without allocating memory. - * - * @param filter The filter to finalize. - * @return Zero if filter is valid, non-zero if it contains errors. - * @ - */ -FLECS_API -int ecs_filter_finalize( - const ecs_world_t *world, - ecs_filter_t *filter); - -/** Find index for $this variable. - * This operation looks up the index of the $this variable. This index can - * be used in operations like ecs_iter_set_var and ecs_iter_get_var. - * - * The operation will return -1 if the variable was not found. This happens when - * a filter only has terms that are not matched on the $this variable, like a - * filter that exclusively matches singleton components. - * - * @param filter The rule. - * @return The index of the $this variable. - */ -FLECS_API -int32_t ecs_filter_find_this_var( - const ecs_filter_t *filter); - /** Convert term to string expression. * Convert term to a string expression. The resulting expression is equivalent * to the same term, with the exception of And & Or operators. - * + * * @param world The world. * @param term The term. * @return The term converted to a string. @@ -6976,163 +7663,118 @@ char* ecs_term_str( const ecs_world_t *world, const ecs_term_t *term); -/** Convert filter to string expression. - * Convert filter terms to a string expression. The resulting expression can be - * parsed to create the same filter. +/** Convert query to string expression. + * Convert query to a string expression. The resulting expression can be + * parsed to create the same query. * - * @param world The world. - * @param filter The filter. - * @return The filter converted to a string. + * @param query The query. + * @return The query converted to a string. */ FLECS_API -char* ecs_filter_str( - const ecs_world_t *world, - const ecs_filter_t *filter); +char* ecs_query_str( + const ecs_query_t *query); -/** Return a filter iterator. - * A filter iterator lets an application iterate over entities that match the - * specified filter. - * - * @param world The world. - * @param filter The filter. - * @return An iterator that can be used with ecs_filter_next. - */ -FLECS_API -ecs_iter_t ecs_filter_iter( - const ecs_world_t *world, - const ecs_filter_t *filter); +/** @} */ -/** Return a chained filter iterator. - * A chained iterator applies a filter to the results of the input iterator. The - * resulting iterator must be iterated with ecs_filter_next. - * - * @param it The input iterator - * @param filter The filter to apply to the iterator. - * @return The chained iterator. +/** + * @defgroup each_iter Each iterator + * @brief Find all entities that have a single (component) id. + * @{ */ -FLECS_API -ecs_iter_t ecs_filter_chain_iter( - const ecs_iter_t *it, - const ecs_filter_t *filter); -/** Get pivot term for filter. - * The pivot term is the term that matches the smallest set of tables, and is - * a good default starting point for a search. +/** Iterate all entities with specified (component id). + * This returns an iterator that yields all entities with a single specified + * component. This is a much lighter weight operation than creating and + * iterating a query. * - * The following conditions must be met for a term to be considered as pivot: - * - It must have a This subject - * - It must have the And operator + * Usage: + * @code + * ecs_iter_t it = ecs_each(world, Player); + * while (ecs_each_next(&it)) { + * for (int i = 0; i < it.count; i ++) { + * // Iterate as usual. + * } + * } + * @endcode * - * When a filter does not have any terms that match those conditions, it will - * return -1. + * If the specified id is a component, it is possible to access the component + * pointer with ecs_field just like with regular queries: * - * If one or more terms in the filter have no matching tables the filter won't - * yield any results. In this case the operation will return -2 which gives a - * search function the option to early out. + * @code + * ecs_iter_t it = ecs_each(world, Position); + * while (ecs_each_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * for (int i = 0; i < it.count; i ++) { + * // Iterate as usual. + * } + * } + * @endcode * * @param world The world. - * @param filter The filter. - * @return Index of the pivot term (use with filter->terms) - */ + * @param id The (component) id to iterate. + * @return An iterator that iterates all entities with the (component) id. +*/ FLECS_API -int32_t ecs_filter_pivot_term( +ecs_iter_t ecs_each_id( const ecs_world_t *world, - const ecs_filter_t *filter); - -/** Iterate tables matched by filter. - * This operation progresses the filter iterator to the next table. The - * iterator must have been initialized with `ecs_filter_iter`. This operation - * must be invoked at least once before interpreting the contents of the - * iterator. - * - * @param it The iterator - * @return True if more data is available, false if not. - */ -FLECS_API -bool ecs_filter_next( - ecs_iter_t *it); + ecs_id_t id); -/** Same as ecs_filter_next, but always instanced. - * See instanced property of ecs_filter_desc_t. +/** Progress an iterator created with ecs_each_id(). * - * @param it The iterator - * @return True if more data is available, false if not. + * @param it The iterator. + * @return True if the iterator has more results, false if not. */ FLECS_API -bool ecs_filter_next_instanced( +bool ecs_each_next( ecs_iter_t *it); -/** Move resources of one filter to another. +/** Iterate children of parent. + * Equivalent to: + * @code + * ecs_iter_t it = ecs_each_id(world, ecs_pair(EcsChildOf, parent)); + * @endcode * - * @param dst The destination filter. - * @param src The source filter. - */ + * @param world The world. + * @param parent The parent. + * @return An iterator that iterates all children of the parent. + * + * @see ecs_each_id() +*/ FLECS_API -void ecs_filter_move( - ecs_filter_t *dst, - ecs_filter_t *src); +ecs_iter_t ecs_children( + const ecs_world_t *world, + ecs_entity_t parent); -/** Copy resources of one filter to another. +/** Progress an iterator created with ecs_children(). * - * @param dst The destination filter. - * @param src The source filter. + * @param it The iterator. + * @return True if the iterator has more results, false if not. */ FLECS_API -void ecs_filter_copy( - ecs_filter_t *dst, - const ecs_filter_t *src); +bool ecs_children_next( + ecs_iter_t *it); /** @} */ /** * @defgroup queries Queries - * @brief Functions for working with `ecs_query_t`. + * Functions for working with `ecs_query_t`. + * * @{ */ /** Create a query. - * This operation creates a query. Queries are used to iterate over entities - * that match a filter and are the fastest way to find and iterate over entities - * and their components. - * - * Queries should be created once, and reused multiple times. While iterating a - * query is a cheap operation, creating and deleting a query is expensive. The - * reason for this is that queries are "prematched", which means that a query - * stores state about which entities (or rather, tables) match with the query. - * Building up this state happens during query creation. - * - * Once a query is created, matching only happens when new tables are created. - * In most applications this is an infrequent process, since it only occurs when - * a new combination of components is introduced. While matching is expensive, - * it is importent to note that matching does not happen on a per-entity basis, - * but on a per-table basis. This means that the average time spent on matching - * per frame should rapidly approach zero over the lifetime of an application. - * - * A query provides direct access to the component arrays. When an application - * creates/deletes entities or adds/removes components, these arrays can shift - * component values around, or may grow in size. This can cause unexpected or - * undefined behavior to occur if these operations are performed while - * iterating. To prevent this from happening an application should either not - * perform these operations while iterating, or use deferred operations (see - * ecs_defer_begin and ecs_defer_end). - * - * Queries can be created and deleted dynamically. If a query was not deleted - * (using ecs_query_fini) before the world is deleted, it will be deleted - * automatically. - * + * * @param world The world. - * @param desc A structure describing the query properties. - * @return The new query. + * @param desc The descriptor (see ecs_query_desc_t) + * @return The query. */ FLECS_API ecs_query_t* ecs_query_init( - ecs_world_t *world, + ecs_world_t *world, const ecs_query_desc_t *desc); -/** Destroy a query. - * This operation destroys a query and its resources. If the query is used as - * the parent of subqueries, those subqueries will be orphaned and must be - * deinitialized as well. +/** Delete a query. * * @param query The query. */ @@ -7140,111 +7782,275 @@ FLECS_API void ecs_query_fini( ecs_query_t *query); -/** Get filter from a query. - * This operation obtains a pointer to the internally constructed filter - * of the query and can be used to introspect the query terms. +/** Find variable index. + * This operation looks up the index of a variable in the query. This index can + * be used in operations like ecs_iter_set_var() and ecs_iter_get_var(). * * @param query The query. - * @return The filter. + * @param name The variable name. + * @return The variable index. */ FLECS_API -const ecs_filter_t* ecs_query_get_filter( - const ecs_query_t *query); +int32_t ecs_query_find_var( + const ecs_query_t *query, + const char *name); -/** Return a query iterator. - * A query iterator lets an application iterate over entities that match the - * specified query. If a sorting function is specified, the query will check - * whether a resort is required upon creating the iterator. - * - * Creating a query iterator is a cheap operation that does not allocate any - * resources. An application does not need to deinitialize or free a query - * iterator before it goes out of scope. +/** Get variable name. + * This operation returns the variable name for an index. * - * To iterate the iterator, an application should use ecs_query_next to progress - * the iterator and test if it has data. + * @param query The query. + * @param var_id The variable index. + * @return The variable name. + */ +FLECS_API +const char* ecs_query_var_name( + const ecs_query_t *query, + int32_t var_id); + +/** Test if variable is an entity. + * Internally the query engine has entity variables and table variables. When + * iterating through query variables (by using ecs_query_variable_count()) only + * the values for entity variables are accessible. This operation enables an + * application to check if a variable is an entity variable. * - * Query iteration requires an outer and an inner loop. The outer loop uses - * ecs_query_next to test if new tables are available. The inner loop iterates - * the entities in the table, and is usually a for loop that uses iter.count to - * loop through the entities and component arrays. + * @param query The query. + * @param var_id The variable id. + * @return Whether the variable is an entity variable. + */ +FLECS_API +bool ecs_query_var_is_entity( + const ecs_query_t *query, + int32_t var_id); + +/** Create a query iterator. + * Use an iterator to iterate through the entities that match an entity. Queries + * can return multiple results, and have to be iterated by repeatedly calling + * ecs_query_next() until the operation returns false. + * + * Depending on the query, a single result can contain an entire table, a range + * of entities in a table, or a single entity. Iteration code has an inner and + * an outer loop. The outer loop loops through the query results, and typically + * corresponds with a table. The inner loop loops entities in the result. + * + * Example: + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * + * while (ecs_query_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * Velocity *v = ecs_field(&it, Velocity, 1); + * + * for (int i = 0; i < it.count; i ++) { + * p[i].x += v[i].x; + * p[i].y += v[i].y; + * } + * } + * @endcode + * + * The world passed into the operation must be either the actual world or the + * current stage, when iterating from a system. The stage is accessible through + * the it.world member. + * + * Example: + * @code + * void MySystem(ecs_iter_t *it) { + * ecs_query_t *q = it->ctx; // Query passed as system context + * + * // Create query iterator from system stage + * ecs_iter_t qit = ecs_query_iter(it->world, q); + * while (ecs_query_next(&qit)) { + * // Iterate as usual + * } + * } + * @endcode + * + * If query iteration is stopped without the last call to ecs_query_next() + * returning false, iterator resources need to be cleaned up explicitly + * with ecs_iter_fini(). + * + * Example: + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * + * while (ecs_query_next(&it)) { + * if (!ecs_field_is_set(&it, 0)) { + * ecs_iter_fini(&it); // Free iterator resources + * break; + * } + * + * for (int i = 0; i < it.count; i ++) { + * // ... + * } + * } + * @endcode * - * The two loops are necessary because of how data is stored internally. - * Entities are grouped by the components they have, in tables. A single query - * can (and often does) match with multiple tables. Because each table has its - * own set of arrays, an application has to reobtain pointers to those arrays - * for each matching table. + * @param world The world. + * @param query The query. + * @return An iterator. * - * @param world The world or stage, when iterating in readonly mode. - * @param query The query to iterate. - * @return The query iterator. + * @see ecs_query_next() */ FLECS_API ecs_iter_t ecs_query_iter( const ecs_world_t *world, - ecs_query_t *query); + const ecs_query_t *query); -/** Progress the query iterator. - * This operation progresses the query iterator to the next table. The - * iterator must have been initialized with `ecs_query_iter`. This operation - * must be invoked at least once before interpreting the contents of the - * iterator. +/** Progress query iterator. + * + * @param it The iterator. + * @return True if the iterator has more results, false if not. * - * @param iter The iterator. - * @returns True if more data is available, false if not. + * @see ecs_query_iter() */ FLECS_API bool ecs_query_next( - ecs_iter_t *iter); + ecs_iter_t *it); -/** Same as ecs_query_next, but always instanced. - * See "instanced" property of ecs_filter_desc_t. +/** Match entity with query. + * This operation matches an entity with a query and returns the result of the + * match in the "it" out parameter. An application should free the iterator + * resources with ecs_iter_fini() if this function returns true. + * + * Usage: + * @code + * ecs_iter_t it; + * if (ecs_query_has(q, e, &it)) { + * ecs_iter_fini(&it); + * } + * @endcode * - * @param iter The iterator. - * @returns True if more data is available, false if not. + * @param query The query. + * @param entity The entity to match + * @param it The iterator with matched data. + * @return True if entity matches the query, false if not. */ FLECS_API -bool ecs_query_next_instanced( - ecs_iter_t *iter); +bool ecs_query_has( + ecs_query_t *query, + ecs_entity_t entity, + ecs_iter_t *it); -/** Fast alternative to ecs_query_next that only returns matched tables. - * This operation only populates the ecs_iter_t::table field. To access the - * matched components, call ecs_query_populate. +/** Match table with query. + * This operation matches a table with a query and returns the result of the + * match in the "it" out parameter. An application should free the iterator + * resources with ecs_iter_fini() if this function returns true. * - * If this operation is used with a query that has inout/out terms, those terms - * will not be marked dirty unless ecs_query_populate is called. + * Usage: + * @code + * ecs_iter_t it; + * if (ecs_query_has_table(q, t, &it)) { + * ecs_iter_fini(&it); + * } + * @endcode * - * @param iter The iterator. - * @returns True if more data is available, false if not. + * @param query The query. + * @param table The table to match + * @param it The iterator with matched data. + * @return True if table matches the query, false if not. */ FLECS_API -bool ecs_query_next_table( - ecs_iter_t *iter); +bool ecs_query_has_table( + ecs_query_t *query, + ecs_table_t *table, + ecs_iter_t *it); -/** Populate iterator fields. - * This operation can be combined with ecs_query_next_table to populate the - * iterator fields for the current table. +/** Match range with query. + * This operation matches a range with a query and returns the result of the + * match in the "it" out parameter. An application should free the iterator + * resources with ecs_iter_fini() if this function returns true. + * + * The entire range must match the query for the operation to return true. * - * Populating fields conditionally can save time when a query uses change - * detection, and only needs iterator data when the table has changed. When this - * operation is called, inout/out terms will be marked dirty. + * Usage: + * @code + * ecs_table_range_t range = { + * .table = table, + * .offset = 1, + * .count = 2 + * }; + * + * ecs_iter_t it; + * if (ecs_query_has_range(q, &range, &it)) { + * ecs_iter_fini(&it); + * } + * @endcode * - * In cases where inout/out terms are conditionally written and no changes - * were made after calling ecs_query_populate, the ecs_query_skip function can - * be called to prevent the matched table components from being marked dirty. + * @param query The query. + * @param range The range to match + * @param it The iterator with matched data. + * @return True if range matches the query, false if not. + */ +FLECS_API +bool ecs_query_has_range( + ecs_query_t *query, + ecs_table_range_t *range, + ecs_iter_t *it); + +/** Returns how often a match event happened for a cached query. + * This operation can be used to determine whether the query cache has been + * updated with new tables. * - * This operation does should not be used with queries that match disabled - * components, union relationships, or with queries that use order_by. + * @param query The query. + * @return The number of match events happened. + */ +FLECS_API +int32_t ecs_query_match_count( + const ecs_query_t *query); + +/** Convert query to a string. + * This will convert the query program to a string which can aid in debugging + * the behavior of a query. + * + * The returned string must be freed with ecs_os_free(). + * + * @param query The query. + * @return The query plan. + */ +FLECS_API +char* ecs_query_plan( + const ecs_query_t *query); + +/** Convert query to string with profile. + * To use this you must set the EcsIterProfile flag on an iterator before + * starting iteration: + * + * @code + * it.flags |= EcsIterProfile + * @endcode * - * When the when_changed argument is set to true, the iterator data will only - * populate when the data has changed, using query change detection. + * The returned string must be freed with ecs_os_free(). + * + * @param query The query. + * @param it The iterator with profile data. + * @return The query plan with profile data. + */ +FLECS_API +char* ecs_query_plan_w_profile( + const ecs_query_t *query, + const ecs_iter_t *it); + +/** Populate variables from key-value string. + * Convenience function to set query variables from a key-value string separated + * by comma's. The string must have the following format: + * + * @code + * var_a: value, var_b: value + * @endcode + * + * The key-value list may optionally be enclosed in parenthesis. * - * @param iter The iterator. - * @param when_changed Only populate data when result has changed. + * This function uses the script addon. + * + * @param query The query. + * @param it The iterator for which to set the variables. + * @param expr The key-value expression. + * @return Pointer to the next character after the last parsed one. */ FLECS_API -int ecs_query_populate( - ecs_iter_t *iter, - bool when_changed); +const char* ecs_query_args_parse( + ecs_query_t *query, + ecs_iter_t *it, + const char *expr); /** Returns whether the query data changed since the last iteration. * The operation will return true after: @@ -7252,79 +8058,76 @@ int ecs_query_populate( * - new tables have been matched/unmatched with * - matched entities were deleted * - matched components were changed - * + * * The operation will not return true after a write-only (EcsOut) or filter * (EcsInOutNone) term has changed, when a term is not matched with the * current table (This subject) or for tag terms. - * - * The changed state of a table is reset after it is iterated. If a iterator was + * + * The changed state of a table is reset after it is iterated. If an iterator was * not iterated until completion, tables may still be marked as changed. - * + * * If no iterator is provided the operation will return the changed state of the - * all matched tables of the query. - * - * If an iterator is provided, the operation will return the changed state of + * all matched tables of the query. + * + * If an iterator is provided, the operation will return the changed state of * the currently returned iterator result. The following preconditions must be * met before using an iterator with change detection: - * - * - The iterator is a query iterator (created with ecs_query_iter) - * - The iterator must be valid (ecs_query_next must have returned true) - * - The iterator must be instanced - * + * + * - The iterator is a query iterator (created with ecs_query_iter()) + * - The iterator must be valid (ecs_query_next() must have returned true) + * * @param query The query (optional if 'it' is provided). - * @param it The iterator result to test (optional if 'query' is provided). * @return true if entities changed, otherwise false. */ FLECS_API bool ecs_query_changed( - ecs_query_t *query, - const ecs_iter_t *it); + ecs_query_t *query); /** Skip a table while iterating. * This operation lets the query iterator know that a table was skipped while * iterating. A skipped table will not reset its changed state, and the query * will not update the dirty flags of the table for its out columns. - * + * * Only valid iterators must be provided (next has to be called at least once & * return true) and the iterator must be a query iterator. - * + * * @param it The iterator result to skip. */ FLECS_API -void ecs_query_skip( +void ecs_iter_skip( ecs_iter_t *it); /** Set group to iterate for query iterator. * This operation limits the results returned by the query to only the selected * group id. The query must have a group_by function, and the iterator must * be a query iterator. - * + * * Groups are sets of tables that are stored together in the query cache based - * on a group id, which is calculated per table by the group_by function. To + * on a group id, which is calculated per table by the group_by function. To * iterate a group, an iterator only needs to know the first and last cache node * for that group, which can both be found in a fast O(1) operation. - * - * As a result, group iteration is one of the most efficient mechanisms to + * + * As a result, group iteration is one of the most efficient mechanisms to * filter out large numbers of entities, even if those entities are distributed * across many tables. This makes it a good fit for things like dividing up * a world into cells, and only iterating cells close to a player. - * - * The group to iterate must be set before the first call to ecs_query_next. No - * operations that can add/remove components should be invoked between calling - * ecs_query_set_group and ecs_query_next. - * + * + * The group to iterate must be set before the first call to ecs_query_next(). No + * operations that can add/remove components should be invoked between calling + * ecs_iter_set_group() and ecs_query_next(). + * * @param it The query iterator. * @param group_id The group to iterate. */ FLECS_API -void ecs_query_set_group( +void ecs_iter_set_group( ecs_iter_t *it, uint64_t group_id); /** Get context of query group. - * This operation returns the context of a query group as returned by the + * This operation returns the context of a query group as returned by the * on_group_create callback. - * + * * @param query The query. * @param group_id The group for which to obtain the context. * @return The group context, NULL if the group doesn't exist. @@ -7337,7 +8140,7 @@ void* ecs_query_get_group_ctx( /** Get information about query group. * This operation returns information about a query group, including the group * context returned by the on_group_create callback. - * + * * @param query The query. * @param group_id The group for which to obtain the group info. * @return The group info, NULL if the group doesn't exist. @@ -7347,159 +8150,118 @@ const ecs_query_group_info_t* ecs_query_get_group_info( const ecs_query_t *query, uint64_t group_id); -/** Returns whether query is orphaned. - * When the parent query of a subquery is deleted, it is left in an orphaned - * state. The only valid operation on an orphaned query is deleting it. Only - * subqueries can be orphaned. - * - * @param query The query. - * @return true if query is orphaned, otherwise false. - */ -FLECS_API -bool ecs_query_orphaned( - const ecs_query_t *query); - -/** Convert query to string. - * - * @param query The query. - * @return The query string. - */ -FLECS_API -char* ecs_query_str( - const ecs_query_t *query); - -/** Returns number of tables query matched with. - * - * @param query The query. - * @return The number of matched tables. - */ -FLECS_API -int32_t ecs_query_table_count( - const ecs_query_t *query); - -/** Returns number of empty tables query matched with. - * - * @param query The query. - * @return The number of matched empty tables. - */ -FLECS_API -int32_t ecs_query_empty_table_count( - const ecs_query_t *query); +/** Struct returned by ecs_query_count(). */ +typedef struct ecs_query_count_t { + int32_t results; /**< Number of results returned by query. */ + int32_t entities; /**< Number of entities returned by query. */ + int32_t tables; /**< Number of tables returned by query. */ + int32_t empty_tables; /**< Number of empty tables returned by query. */ +} ecs_query_count_t; -/** Returns number of entities query matched with. - * This operation iterates all non-empty tables in the query cache to find the - * total number of entities. +/** Returns number of entities and results the query matches with. + * Only entities matching the $this variable as source are counted. * * @param query The query. * @return The number of matched entities. */ FLECS_API -int32_t ecs_query_entity_count( +ecs_query_count_t ecs_query_count( const ecs_query_t *query); -/** Get query ctx. - * Return the value set in ecs_query_desc_t::ctx. +/** Does query return one or more results. * * @param query The query. - * @return The context. + * @return True if query matches anything, false if not. */ FLECS_API -void* ecs_query_get_ctx( +bool ecs_query_is_true( const ecs_query_t *query); -/** Get query binding ctx. - * Return the value set in ecs_query_desc_t::binding_ctx. - * +/** Get query used to populate cache. + * This operation returns the query that is used to populate the query cache. + * For queries that are can be entirely cached, the returned query will be + * equivalent to the query passed to ecs_query_get_cache_query(). + * * @param query The query. - * @return The context. + * @return The query used to populate the cache, NULL if query is not cached. */ FLECS_API -void* ecs_query_get_binding_ctx( +const ecs_query_t* ecs_query_get_cache_query( const ecs_query_t *query); /** @} */ /** - * @defgroup observer Observers - * @brief Functions for working with events and observers. + * @defgroup observers Observers + * Functions for working with events and observers. + * * @{ */ /** Send event. * This sends an event to matching triggers & is the mechanism used by flecs - * itself to send OnAdd, OnRemove, etc events. - * + * itself to send `OnAdd`, `OnRemove`, etc events. + * * Applications can use this function to send custom events, where a custom * event can be any regular entity. - * + * * Applications should not send builtin flecs events, as this may violate * assumptions the code makes about the conditions under which those events are * sent. - * + * * Triggers are invoked synchronously. It is therefore safe to use stack-based * data as event context, which can be set in the "param" member. - * + * * @param world The world. * @param desc Event parameters. + * + * @see ecs_enqueue() */ FLECS_API -void ecs_emit( +void ecs_emit( ecs_world_t *world, ecs_event_desc_t *desc); +/** Enqueue event. + * Same as ecs_emit(), but enqueues an event in the command queue instead. The + * event will be emitted when ecs_defer_end() is called. + * + * If this operation is called when the provided world is not in deferred mode + * it behaves just like ecs_emit(). + * + * @param world The world. + * @param desc Event parameters. +*/ FLECS_API void ecs_enqueue( ecs_world_t *world, ecs_event_desc_t *desc); /** Create observer. - * Observers are like triggers, but can subscribe for multiple terms. An + * Observers are like triggers, but can subscribe for multiple terms. An * observer only triggers when the source of the event meets all terms. * * See the documentation for ecs_observer_desc_t for more details. * * @param world The world. * @param desc The observer creation parameters. + * @return The observer, or 0 if the operation failed. */ FLECS_API ecs_entity_t ecs_observer_init( ecs_world_t *world, const ecs_observer_desc_t *desc); -/** Default run action for observer. - * This function can be called from a custom observer run action (see - * ecs_observer_desc_t::run for more details). This function ensures that the - * observer's filter is applied to the iterator's table, filters out duplicate - * events and implements EcsMonitor logic. - * - * @param it The iterator. - * @return True if the observer was invoked. - */ -FLECS_API -bool ecs_observer_default_run_action( - ecs_iter_t *it); - -/** Get observer ctx. - * Return the value set in ecs_observer_desc_t::ctx. - * - * @param world The world. - * @param observer The observer. - * @return The context. - */ -FLECS_API -void* ecs_observer_get_ctx( - const ecs_world_t *world, - ecs_entity_t observer); - -/** Get observer binding ctx. - * Return the value set in ecs_observer_desc_t::binding_ctx. - * +/** Get observer object. + * Returns the observer object. Can be used to access various information about + * the observer, like the query and context. + * * @param world The world. * @param observer The observer. - * @return The context. + * @return The observer object. */ FLECS_API -void* ecs_observer_get_binding_ctx( +const ecs_observer_t* ecs_observer_get( const ecs_world_t *world, ecs_entity_t observer); @@ -7507,48 +8269,21 @@ void* ecs_observer_get_binding_ctx( /** * @defgroup iterator Iterators - * @brief Functions for working with `ecs_iter_t`. + * Functions for working with `ecs_iter_t`. + * * @{ */ -/** Create iterator from poly object. - * The provided poly object must have the iterable mixin. If an object is - * provided that does not have the mixin, the function will assert. - * - * When a filter is provided, an array of two iterators must be passed to the - * function. This allows the mixin implementation to create a chained iterator - * when necessary, which requires two iterator objects. - * - * If a filter is provided, the first element in the array of two iterators is - * the one that should be iterated. The mixin implementation may or may not set - * the second element, depending on whether an iterator chain is required. - * - * Additionally, when a filter is provided the returned iterator will be for a - * single term with the provided filter id. If the iterator is chained, the - * previous iterator in the chain can be accessed through it->chain_it. - * - * @param world The world or stage for which to create the iterator. - * @param poly The poly object from which to create the iterator. - * @param iter The iterator (out, ecs_iter_t[2] when filter is set). - * @param filter Optional term used for filtering the results. - */ -FLECS_API -void ecs_iter_poly( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter); - /** Progress any iterator. * This operation is useful in combination with iterators for which it is not * known what created them. Example use cases are functions that should accept * any kind of iterator (such as serializers) or iterators created from poly * objects. - * + * * This operation is slightly slower than using a type-specific iterator (e.g. - * ecs_filter_next, ecs_query_next) as it has to call a function pointer which + * ecs_query_next, ecs_query_next) as it has to call a function pointer which * introduces a level of indirection. - * + * * @param it The iterator. * @return True if iterator has more results, false if not. */ @@ -7558,11 +8293,11 @@ bool ecs_iter_next( /** Cleanup iterator resources. * This operation cleans up any resources associated with the iterator. - * + * * This operation should only be used when an iterator is not iterated until * completion (next has not yet returned false). When an iterator is iterated * until completion, resources are automatically freed. - * + * * @param it The iterator. */ FLECS_API @@ -7573,10 +8308,10 @@ void ecs_iter_fini( * This operation returns the number of matched entities. If a query contains no * matched entities but still yields results (e.g. it has no terms with This * sources) the operation will return 0. - * + * * To determine the number of matched entities, the operation iterates the * iterator until it yields no more results. - * + * * @param it The iterator. * @return True if iterator has more results, false if not. */ @@ -7586,13 +8321,13 @@ int32_t ecs_iter_count( /** Test if iterator is true. * This operation will return true if the iterator returns at least one result. - * This is especially useful in combination with fact-checking rules (see the - * rules addon). - * + * This is especially useful in combination with fact-checking queries (see the + * queries addon). + * * The operation requires a valid iterator. After the operation is invoked, the * application should no longer invoke next on the iterator and should treat it * as if the iterator is iterated until completion. - * + * * @param it The iterator. * @return true if the iterator returns at least one result. */ @@ -7603,7 +8338,7 @@ bool ecs_iter_is_true( /** Get first matching entity from iterator. * After this operation the application should treat the iterator as if it has * been iterated until completion. - * + * * @param it The iterator. * @return The first matching entity, or 0 if no entities were matched. */ @@ -7613,36 +8348,41 @@ ecs_entity_t ecs_iter_first( /** Set value for iterator variable. * This constrains the iterator to return only results for which the variable - * equals the specified value. The default value for all variables is + * equals the specified value. The default value for all variables is * EcsWildcard, which means the variable can assume any value. - * + * * Example: - * - * // Rule that matches (Eats, *) - * ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + * + * @code + * // Query that matches (Eats, *) + * ecs_query_t *q = ecs_query(world, { * .terms = { * { .first.id = Eats, .second.name = "$food" } * } * }); * - * int food_var = ecs_rule_find_var(r, "food"); + * int food_var = ecs_query_find_var(r, "food"); * * // Set Food to Apples, so we're only matching (Eats, Apples) - * ecs_iter_t it = ecs_rule_iter(world, r); + * ecs_iter_t it = ecs_query_iter(world, q); * ecs_iter_set_var(&it, food_var, Apples); * - * while (ecs_rule_next(&it)) { + * while (ecs_query_next(&it)) { * for (int i = 0; i < it.count; i ++) { * // iterate as usual * } * } - * + * @endcode + * * The variable must be initialized after creating the iterator and before the * first call to next. - * + * * @param it The iterator. * @param var_id The variable index. * @param entity The entity variable value. + * + * @see ecs_iter_set_var_as_range() + * @see ecs_iter_set_var_as_table() */ FLECS_API void ecs_iter_set_var( @@ -7650,12 +8390,15 @@ void ecs_iter_set_var( int32_t var_id, ecs_entity_t entity); -/** Same as ecs_iter_set_var, but for a table. +/** Same as ecs_iter_set_var(), but for a table. * This constrains the variable to all entities in a table. - * + * * @param it The iterator. * @param var_id The variable index. * @param table The table variable value. + * + * @see ecs_iter_set_var() + * @see ecs_iter_set_var_as_range() */ FLECS_API void ecs_iter_set_var_as_table( @@ -7663,12 +8406,15 @@ void ecs_iter_set_var_as_table( int32_t var_id, const ecs_table_t *table); -/** Same as ecs_iter_set_var, but for a range of entities +/** Same as ecs_iter_set_var(), but for a range of entities * This constrains the variable to a range of entities in a table. - * + * * @param it The iterator. * @param var_id The variable index. * @param range The range variable value. + * + * @see ecs_iter_set_var() + * @see ecs_iter_set_var_as_table() */ FLECS_API void ecs_iter_set_var_as_range( @@ -7679,11 +8425,11 @@ void ecs_iter_set_var_as_range( /** Get value of iterator variable as entity. * A variable can be interpreted as entity if it is set to an entity, or if it * is set to a table range with count 1. - * + * * This operation can only be invoked on valid iterators. The variable index * must be smaller than the total number of variables provided by the iterator * (as set in ecs_iter_t::variable_count). - * + * * @param it The iterator. * @param var_id The variable index. * @return The variable value. @@ -7697,11 +8443,11 @@ ecs_entity_t ecs_iter_get_var( * A variable can be interpreted as table if it is set as table range with * both offset and count set to 0, or if offset is 0 and count matches the * number of elements in the table. - * + * * This operation can only be invoked on valid iterators. The variable index * must be smaller than the total number of variables provided by the iterator * (as set in ecs_iter_t::variable_count). - * + * * @param it The iterator. * @param var_id The variable index. * @return The variable value. @@ -7715,11 +8461,11 @@ ecs_table_t* ecs_iter_get_var_as_table( * A value can be interpreted as table range if it is set as table range, or if * it is set to an entity with a non-empty type (the entity must have at least * one component, tag or relationship in its type). - * + * * This operation can only be invoked on valid iterators. The variable index * must be smaller than the total number of variables provided by the iterator * (as set in ecs_iter_t::variable_count). - * + * * @param it The iterator. * @param var_id The variable index. * @return The variable value. @@ -7732,10 +8478,10 @@ ecs_table_range_t ecs_iter_get_var_as_range( /** Returns whether variable is constrained. * This operation returns true for variables set by one of the ecs_iter_set_var* * operations. - * + * * A constrained variable is guaranteed not to change values while results are * being iterated. - * + * * @param it The iterator. * @param var_id The variable index. * @return Whether the variable is constrained to a specified value. @@ -7745,14 +8491,29 @@ bool ecs_iter_var_is_constrained( ecs_iter_t *it, int32_t var_id); +/** Returns whether current iterator result has changed. + * This operation must be used in combination with a query that supports change + * detection (e.g. is cached). The operation returns whether the currently + * iterated result has changed since the last time it was iterated by the query. + * + * Change detection works on a per-table basis. Changes to individual entities + * cannot be detected this way. + * + * @param it The iterator. + * @return True if the result changed, false if it didn't. +*/ +FLECS_API +bool ecs_iter_changed( + ecs_iter_t *it); + /** Convert iterator to string. * Prints the contents of an iterator to a string. Useful for debugging and/or * testing the output of an iterator. - * - * The function only converts the currently iterated data to a string. To + * + * The function only converts the currently iterated data to a string. To * convert all data, the application has to manually call the next function and - * call ecs_iter_str on each result. - * + * call ecs_iter_str() on each result. + * * @param it The iterator. * @return A string representing the contents of the iterator. */ @@ -7763,13 +8524,13 @@ char* ecs_iter_str( /** Create a paged iterator. * Paged iterators limit the results to those starting from 'offset', and will * return at most 'limit' results. - * - * The iterator must be iterated with ecs_page_next. - * + * + * The iterator must be iterated with ecs_page_next(). + * * A paged iterator acts as a passthrough for data exposed by the parent * iterator, so that any data provided by the parent will also be provided by * the paged iterator. - * + * * @param it The source iterator. * @param offset The number of entities to skip. * @param limit The maximum number of entities to iterate. @@ -7782,8 +8543,8 @@ ecs_iter_t ecs_page_iter( int32_t limit); /** Progress a paged iterator. - * Progresses an iterator created by ecs_page_iter. - * + * Progresses an iterator created by ecs_page_iter(). + * * @param it The iterator. * @return true if iterator has more results, false if not. */ @@ -7792,20 +8553,20 @@ bool ecs_page_next( ecs_iter_t *it); /** Create a worker iterator. - * Worker iterators can be used to equally divide the number of matched entities + * Worker iterators can be used to equally divide the number of matched entities * across N resources (usually threads). Each resource will process the total * number of matched entities divided by 'count'. - * + * * Entities are distributed across resources such that the distribution is * stable between queries. Two queries that match the same table are guaranteed * to match the same entities in that table. - * - * The iterator must be iterated with ecs_worker_next. - * + * + * The iterator must be iterated with ecs_worker_next(). + * * A worker iterator acts as a passthrough for data exposed by the parent * iterator, so that any data provided by the parent will also be provided by * the worker iterator. - * + * * @param it The source iterator. * @param index The index of the current resource. * @param count The total number of resources to divide entities between. @@ -7818,8 +8579,8 @@ ecs_iter_t ecs_worker_iter( int32_t count); /** Progress a worker iterator. - * Progresses an iterator created by ecs_worker_iter. - * + * Progresses an iterator created by ecs_worker_iter(). + * * @param it The iterator. * @return true if iterator has more results, false if not. */ @@ -7827,34 +8588,80 @@ FLECS_API bool ecs_worker_next( ecs_iter_t *it); -/** Obtain data for a query field. +/** Get data for field. * This operation retrieves a pointer to an array of data that belongs to the * term in the query. The index refers to the location of the term in the query, - * and starts counting from one. + * and starts counting from zero. * - * For example, the query "Position, Velocity" will return the Position array - * for index 1, and the Velocity array for index 2. + * For example, the query `"Position, Velocity"` will return the `Position` array + * for index 0, and the `Velocity` array for index 1. * * When the specified field is not owned by the entity this function returns a * pointer instead of an array. This happens when the source of a field is not * the entity being iterated, such as a shared component (from a prefab), a - * component from a parent, or another entity. The ecs_field_is_self operation + * component from a parent, or another entity. The ecs_field_is_self() operation * can be used to test dynamically if a field is owned. + * + * When a field contains a sparse component, use the ecs_field_at function. When + * a field is guaranteed to be set and owned, the ecs_field_self() function can be + * used. ecs_field_self() has slightly better performance, and provides stricter + * validity checking. * - * The provided size must be either 0 or must match the size of the datatype + * The provided size must be either 0 or must match the size of the type * of the returned array. If the size does not match, the operation may assert. - * The size can be dynamically obtained with ecs_field_size. + * The size can be dynamically obtained with ecs_field_size(). + * + * An example: + * + * @code + * while (ecs_query_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * Velocity *v = ecs_field(&it, Velocity, 1); + * for (int32_t i = 0; i < it->count; i ++) { + * p[i].x += v[i].x; + * p[i].y += v[i].y; + * } + * } + * @endcode * * @param it The iterator. - * @param size The type size of the requested data. - * @param index The index of the field in the iterator. + * @param size The size of the field type. + * @param index The index of the field. * @return A pointer to the data of the field. */ FLECS_API void* ecs_field_w_size( const ecs_iter_t *it, size_t size, - int32_t index); + int8_t index); + +/** Get data for field at specified row. + * This operation should be used instead of ecs_field_w_size for sparse + * component fields. This operation should be called for each returned row in a + * result. In the following example the Velocity component is sparse: + * + * @code + * while (ecs_query_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * for (int32_t i = 0; i < it->count; i ++) { + * Velocity *v = ecs_field_at(&it, Velocity, 1); + * p[i].x += v->x; + * p[i].y += v->y; + * } + * } + * @endcode + * + * @param it the iterator. + * @param size The size of the field type. + * @param index The index of the field. + * @return A pointer to the data of the field. + */ +FLECS_API +void* ecs_field_at_w_size( + const ecs_iter_t *it, + size_t size, + int8_t index, + int32_t row); /** Test whether the field is readonly. * This operation returns whether the field is readonly. Readonly fields are @@ -7867,12 +8674,12 @@ void* ecs_field_w_size( FLECS_API bool ecs_field_is_readonly( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Test whether the field is writeonly. * This operation returns whether this is a writeonly field. Writeonly terms are * annotated with [out]. - * + * * Serializers are not required to serialize the values of a writeonly field. * * @param it The iterator. @@ -7882,10 +8689,10 @@ bool ecs_field_is_readonly( FLECS_API bool ecs_field_is_writeonly( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Test whether field is set. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return Whether the field is set. @@ -7893,10 +8700,10 @@ bool ecs_field_is_writeonly( FLECS_API bool ecs_field_is_set( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Return id matched for field. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return The id matched for the field. @@ -7904,24 +8711,24 @@ bool ecs_field_is_set( FLECS_API ecs_id_t ecs_field_id( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Return index of matched table column. * This function only returns column indices for fields that have been matched * on the $this variable. Fields matched on other tables will return -1. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return The index of the matched column, -1 if not matched. */ FLECS_API -int32_t ecs_field_column_index( +int32_t ecs_field_column( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Return field source. * The field source is the entity on which the field was matched. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return The source for the field. @@ -7929,11 +8736,11 @@ int32_t ecs_field_column_index( FLECS_API ecs_entity_t ecs_field_src( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Return field type size. * Return type size of the field. Returns 0 if the field has no data. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return The type size for the field. @@ -7941,14 +8748,14 @@ ecs_entity_t ecs_field_src( FLECS_API size_t ecs_field_size( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Test whether the field is matched on self. * This operation returns whether the field is matched on the currently iterated * entity. This function will return false when the field is owned by another * entity, such as a parent or a prefab. - * - * When this operation returns false, the field must be accessed as a single + * + * When this operation returns false, the field must be accessed as a single * value instead of an array. Fields for which this operation returns true * return arrays with it->count values. * @@ -7959,13 +8766,14 @@ size_t ecs_field_size( FLECS_API bool ecs_field_is_self( const ecs_iter_t *it, - int32_t index); + int8_t index); /** @} */ /** * @defgroup tables Tables - * @brief Functions for working with `ecs_table_t`. + * Functions for working with `ecs_table_t`. + * * @{ */ @@ -7981,11 +8789,13 @@ const ecs_type_t* ecs_table_get_type( /** Get type index for id. * This operation returns the index for an id in the table's type. - * + * * @param world The world. * @param table The table. * @param id The id. * @return The index of the id in the table type, or -1 if not found. + * + * @see ecs_table_has_id() */ FLECS_API int32_t ecs_table_get_type_index( @@ -7996,7 +8806,7 @@ int32_t ecs_table_get_type_index( /** Get column index for id. * This operation returns the column index for an id in the table's type. If the * id is not a component, the function will return -1. - * + * * @param world The world. * @param table The table. * @param id The component id. @@ -8008,10 +8818,10 @@ int32_t ecs_table_get_column_index( const ecs_table_t *table, ecs_id_t id); -/** Return number of columns in table. - * Similar to ecs_table_get_type(table)->count, except that the column count +/** Return number of columns in table. + * Similar to `ecs_table_get_type(table)->count`, except that the column count * only counts the number of components in a table. - * + * * @param table The table. * @return The number of columns in the table. */ @@ -8019,16 +8829,18 @@ FLECS_API int32_t ecs_table_column_count( const ecs_table_t *table); -/** Convert type index to column index. +/** Convert type index to column index. * Tables have an array of columns for each component in the table. This array - * does not include elements for tags, which means that the index for a + * does not include elements for tags, which means that the index for a * component in the table type is not necessarily the same as the index in the * column array. This operation converts from an index in the table type to an * index in the column array. - * + * * @param table The table. * @param index The index in the table type. * @return The index in the table column array. + * + * @see ecs_table_column_to_type_index() */ FLECS_API int32_t ecs_table_type_to_column_index( @@ -8036,9 +8848,9 @@ int32_t ecs_table_type_to_column_index( int32_t index); /** Convert column index to type index. - * Same as ecs_table_type_to_column_index, but converts from an index in the + * Same as ecs_table_type_to_column_index(), but converts from an index in the * column array to an index in the table type. - * + * * @param table The table. * @param index The column index. * @return The index in the table type. @@ -8050,7 +8862,7 @@ int32_t ecs_table_column_to_type_index( /** Get column from table by column index. * This operation returns the component array for the provided index. - * + * * @param table The table. * @param index The column index. * @param offset The index of the first row to return (0 for entire column). @@ -8064,7 +8876,8 @@ void* ecs_table_get_column( /** Get column from table by component id. * This operation returns the component array for the provided component id. - * + * + * @param world The world. * @param table The table. * @param id The component id for the column. * @param offset The index of the first row to return (0 for entire column). @@ -8079,7 +8892,7 @@ void* ecs_table_get_id( /** Get column size from table. * This operation returns the component size for the provided index. - * + * * @param table The table. * @param index The column index. * @return The component size, or 0 if the index is not a component. @@ -8089,25 +8902,46 @@ size_t ecs_table_get_column_size( const ecs_table_t *table, int32_t index); -/** Returns the number of records in the table. - * This operation returns the number of records that have been populated through - * the regular (entity) API as well as the number of records that have been - * inserted using the direct access API. +/** Returns the number of entities in the table. + * This operation returns the number of entities in the table. * * @param table The table. - * @return The number of records in a table. + * @return The number of entities in the table. */ FLECS_API int32_t ecs_table_count( const ecs_table_t *table); -/** Test if table has id. - * Same as ecs_table_get_type_index(world, table, id) != -1. +/** Returns allocated size of table. + * This operation returns the number of elements allocated in the table + * per column. + * + * @param table The table. + * @return The number of allocated elements in the table. + */ +FLECS_API +int32_t ecs_table_size( + const ecs_table_t *table); + +/** Returns array with entity ids for table. + * The size of the returned array is the result of ecs_table_count(). * + * @param table The table. + * @return Array with entity ids for table. + */ +FLECS_API +const ecs_entity_t* ecs_table_entities( + const ecs_table_t *table); + +/** Test if table has id. + * Same as `ecs_table_get_type_index(world, table, id) != -1`. + * * @param world The world. * @param table The table. * @param id The id. * @return True if the table has the id, false if the table doesn't. + * + * @see ecs_table_get_type_index() */ FLECS_API bool ecs_table_has_id( @@ -8116,10 +8950,10 @@ bool ecs_table_has_id( ecs_id_t id); /** Return depth for table in tree for relationship rel. - * Depth is determined by counting the number of targets encountered while + * Depth is determined by counting the number of targets encountered while * traversing up the relationship tree for rel. Only acyclic relationships are * supported. - * + * * @param world The world. * @param table The table. * @param rel The relationship. @@ -8146,11 +8980,11 @@ ecs_table_t* ecs_table_add_id( ecs_table_t *table, ecs_id_t id); -/** Find table from id array. - * This operation finds or creates a table with the specified array of +/** Find table from id array. + * This operation finds or creates a table with the specified array of * (component) ids. The ids in the array must be sorted, and it may not contain * duplicate elements. - * + * * @param world The world. * @param ids The id array. * @param id_count The number of elements in the id array. @@ -8177,15 +9011,15 @@ ecs_table_t* ecs_table_remove_id( ecs_table_t *table, ecs_id_t id); -/** Lock or unlock table. - * When a table is locked, modifications to it will throw an assert. When the +/** Lock a table. + * When a table is locked, modifications to it will throw an assert. When the * table is locked recursively, it will take an equal amount of unlock * operations to actually unlock the table. * * Table locks can be used to build safe iterators where it is guaranteed that * the contents of a table are not modified while it is being iterated. * - * The operation only works when called on the world, and has no side effects + * The operation only works when called on the world, and has no side effects * when called on a stage. The assumption is that when called on a stage, * operations are deferred already. * @@ -8198,7 +9032,7 @@ void ecs_table_lock( ecs_table_t *table); /** Unlock a table. - * Must be called after calling ecs_table_lock. + * Must be called after calling ecs_table_lock(). * * @param world The world. * @param table The table to unlock. @@ -8206,11 +9040,11 @@ void ecs_table_lock( FLECS_API void ecs_table_unlock( ecs_world_t *world, - ecs_table_t *table); + ecs_table_t *table); /** Test table for flags. - * Test if table has all of the provided flags. See - * include/flecs/private/api_flags.h for a list of table flags that can be used + * Test if table has all of the provided flags. See + * include/flecs/private/api_flags.h for a list of table flags that can be used * with this function. * * @param table The table. @@ -8242,15 +9076,15 @@ void ecs_table_swap_rows( * - Ctor for each component in the target table * - Move for each overlapping component * - Dtor for each component in the source table. - * - OnAdd triggers for non-overlapping components in the target table - * - OnRemove triggers for non-overlapping components in the source table. + * - `OnAdd` triggers for non-overlapping components in the target table + * - `OnRemove` triggers for non-overlapping components in the source table. * * This operation is a faster than adding/removing components individually. * * The application must explicitly provide the difference in components between * tables as the added/removed parameters. This can usually be derived directly - * from the result of ecs_table_add_id and esc_table_remove_id. These arrays are - * required to properly execute OnAdd/OnRemove triggers. + * from the result of ecs_table_add_id() and ecs_table_remove_id(). These arrays are + * required to properly execute `OnAdd`/`OnRemove` triggers. * * @param world The world. * @param entity The entity to commit. @@ -8267,33 +9101,24 @@ bool ecs_commit( const ecs_type_t *added, const ecs_type_t *removed); -/** Find record for entity. */ -FLECS_API -ecs_record_t* ecs_record_find( - const ecs_world_t *world, - ecs_entity_t entity); - -/** Get component pointer from column/record. */ -FLECS_API -void* ecs_record_get_column( - const ecs_record_t *r, - int32_t column, - size_t c_size); /** Search for component id in table type. - * This operation returns the index of first occurrance of the id in the table + * This operation returns the index of first occurrence of the id in the table * type. The id may be a wildcard. - * + * * When id_out is provided, the function will assign it with the found id. The * found id may be different from the provided id if it is a wildcard. - * + * * This is a constant time operation. - * + * * @param world The world. * @param table The table. * @param id The id to search for. * @param id_out If provided, it will be set to the found id (optional). * @return The index of the id in the table type. + * + * @see ecs_search_offset() + * @see ecs_search_relation() */ FLECS_API int32_t ecs_search( @@ -8303,34 +9128,39 @@ int32_t ecs_search( ecs_id_t *id_out); /** Search for component id in table type starting from an offset. - * This operation is the same as ecs_search, but starts searching from an offset + * This operation is the same as ecs_search(), but starts searching from an offset * in the table type. - * + * * This operation is typically called in a loop where the resulting index is * used in the next iteration as offset: - * + * + * @code * int32_t index = -1; * while ((index = ecs_search_offset(world, table, offset, id, NULL))) { * // do stuff * } - * + * @endcode + * * Depending on how the operation is used it is either linear or constant time. - * When the id has the form (id) or (rel, *) and the operation is invoked as + * When the id has the form `(id)` or `(rel, *)` and the operation is invoked as * in the above example, it is guaranteed to be constant time. - * - * If the provided id has the form (*, tgt) the operation takes linear time. The + * + * If the provided id has the form `(*, tgt)` the operation takes linear time. The * reason for this is that ids for an target are not packed together, as they * are sorted relationship first. - * + * * If the id at the offset does not match the provided id, the operation will do * a linear search to find a matching id. - * + * * @param world The world. * @param table The table. * @param offset Offset from where to start searching. * @param id The id to search for. * @param id_out If provided, it will be set to the found id (optional). * @return The index of the id in the table type. + * + * @see ecs_search() + * @see ecs_search_relation() */ FLECS_API int32_t ecs_search_offset( @@ -8341,12 +9171,13 @@ int32_t ecs_search_offset( ecs_id_t *id_out); /** Search for component/relationship id in table type starting from an offset. - * This operation is the same as ecs_search_offset, but has the additional + * This operation is the same as ecs_search_offset(), but has the additional * capability of traversing relationships to find a component. For example, if * an application wants to find a component for either the provided table or a - * prefab (using the IsA relationship) of that table, it could use the operation + * prefab (using the `IsA` relationship) of that table, it could use the operation * like this: - * + * + * @code * int32_t index = ecs_search_relation( * world, // the world * table, // the table @@ -8358,13 +9189,14 @@ int32_t ecs_search_offset( * NULL, // (optional) entity on which component was found * NULL, // see above * NULL); // internal type with information about matched id - * - * The operation searches depth first. If a table type has 2 IsA relationships, the - * operation will first search the IsA tree of the first relationship. - * - * When choosing betwen ecs_search, ecs_search_offset and ecs_search_relation, + * @endcode + * + * The operation searches depth first. If a table type has 2 `IsA` relationships, the + * operation will first search the `IsA` tree of the first relationship. + * + * When choosing between ecs_search(), ecs_search_offset() and ecs_search_relation(), * the simpler the function the better its performance. - * + * * @param world The world. * @param table The table. * @param offset Offset from where to start searching. @@ -8375,6 +9207,9 @@ int32_t ecs_search_offset( * @param id_out If provided, it will be set to the found id (optional). * @param tr_out Internal datatype. * @return The index of the id in the table type. + * + * @see ecs_search() + * @see ecs_search_offset() */ FLECS_API int32_t ecs_search_relation( @@ -8383,20 +9218,33 @@ int32_t ecs_search_relation( int32_t offset, ecs_id_t id, ecs_entity_t rel, - ecs_flags32_t flags, /* EcsSelf and/or EcsUp */ + ecs_flags64_t flags, /* EcsSelf and/or EcsUp */ ecs_entity_t *subject_out, ecs_id_t *id_out, struct ecs_table_record_t **tr_out); +/** Remove all entities in a table. Does not deallocate table memory. + * Retaining table memory can be efficient when planning + * to refill the table with operations like ecs_bulk_init + * + * @param world The world. + * @param table The table to clear. + */ +FLECS_API +void ecs_table_clear_entities( + ecs_world_t* world, + ecs_table_t* table); + /** @} */ /** * @defgroup values Values - * @brief Construct, destruct, copy and move dynamically created values. + * Construct, destruct, copy and move dynamically created values. + * * @{ */ -/** Construct a value in existing storage +/** Construct a value in existing storage * * @param world The world. * @param type The type of the value to create. @@ -8409,7 +9257,7 @@ int ecs_value_init( ecs_entity_t type, void *ptr); -/** Construct a value in existing storage +/** Construct a value in existing storage * * @param world The world. * @param ti The type info of the type to create. @@ -8422,8 +9270,8 @@ int ecs_value_init_w_type_info( const ecs_type_info_t *ti, void *ptr); -/** Construct a value in new storage - * +/** Construct a value in new storage + * * @param world The world. * @param type The type of the value to create. * @return Pointer to type if success, NULL if failed. @@ -8433,8 +9281,8 @@ void* ecs_value_new( ecs_world_t *world, ecs_entity_t type); -/** Construct a value in new storage - * +/** Construct a value in new storage + * * @param world The world. * @param ti The type info of the type to create. * @return Pointer to type if success, NULL if failed. @@ -8443,24 +9291,24 @@ void* ecs_value_new_w_type_info( ecs_world_t *world, const ecs_type_info_t *ti); -/** Destruct a value - * +/** Destruct a value + * * @param world The world. * @param ti Type info of the value to destruct. * @param ptr Pointer to constructed value of type 'type'. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_fini_w_type_info( const ecs_world_t *world, const ecs_type_info_t *ti, void *ptr); -/** Destruct a value - * +/** Destruct a value + * * @param world The world. * @param type The type of the value to destruct. * @param ptr Pointer to constructed value of type 'type'. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ FLECS_API int ecs_value_fini( @@ -8469,10 +9317,11 @@ int ecs_value_fini( void* ptr); /** Destruct a value, free storage - * + * * @param world The world. * @param type The type of the value to destruct. - * @return Zero if success, nonzero if failed. + * @param ptr A pointer to the value. + * @return Zero if success, nonzero if failed. */ FLECS_API int ecs_value_free( @@ -8481,12 +9330,12 @@ int ecs_value_free( void* ptr); /** Copy value. - * + * * @param world The world. * @param ti Type info of the value to copy. * @param dst Pointer to the storage to copy to. * @param src Pointer to the value to copy. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ FLECS_API int ecs_value_copy_w_type_info( @@ -8496,12 +9345,12 @@ int ecs_value_copy_w_type_info( const void *src); /** Copy value. - * + * * @param world The world. * @param type The type of the value to copy. * @param dst Pointer to the storage to copy to. * @param src Pointer to the value to copy. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ FLECS_API int ecs_value_copy( @@ -8511,12 +9360,12 @@ int ecs_value_copy( const void *src); /** Move value. - * + * * @param world The world. * @param ti Type info of the value to move. * @param dst Pointer to the storage to move to. * @param src Pointer to the value to move. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_move_w_type_info( const ecs_world_t *world, @@ -8525,12 +9374,12 @@ int ecs_value_move_w_type_info( void *src); /** Move value. - * + * * @param world The world. * @param type The type of the value to move. * @param dst Pointer to the storage to move to. * @param src Pointer to the value to move. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_move( const ecs_world_t *world, @@ -8539,12 +9388,12 @@ int ecs_value_move( void *src); /** Move construct value. - * + * * @param world The world. * @param ti Type info of the value to move. * @param dst Pointer to the storage to move to. * @param src Pointer to the value to move. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_move_ctor_w_type_info( const ecs_world_t *world, @@ -8553,12 +9402,12 @@ int ecs_value_move_ctor_w_type_info( void *src); /** Move construct value. - * + * * @param world The world. * @param type The type of the value to move. * @param dst Pointer to the storage to move to. * @param src Pointer to the value to move. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_move_ctor( const ecs_world_t *world, @@ -8572,10 +9421,9 @@ int ecs_value_move_ctor( /** * @defgroup c_addons Addons - * @brief C APIs for addons. - * - * \ingroup c - * + * @ingroup c + * C APIs for addons. + * * @{ * @} */ @@ -8590,15 +9438,16 @@ int ecs_value_move_ctor( /** * @defgroup flecs_c Macro API - * @brief Convenience macro's for C API - * - * \ingroup c + * @ingroup c + * Convenience macro's for C API + * * @{ */ /** * @defgroup flecs_c_creation Creation macro's - * @brief Convenience macro's for creating entities, components and observers + * Convenience macro's for creating entities, components and observers + * * @{ */ @@ -8610,9 +9459,12 @@ int ecs_value_move_ctor( #define ECS_ENTITY_DECLARE ECS_DECLARE /** Define a forward declared entity. - * + * * Example: - * ECS_ENTITY_DEFINE(world, MyEntity, Position, Velocity); + * + * @code + * ECS_ENTITY_DEFINE(world, MyEntity, Position, Velocity); + * @endcode */ #define ECS_ENTITY_DEFINE(world, id_, ...) \ { \ @@ -8622,7 +9474,7 @@ int ecs_value_move_ctor( desc.add_expr = #__VA_ARGS__; \ id_ = ecs_entity_init(world, &desc); \ ecs_id(id_) = id_; \ - ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, NULL); \ + ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, "failed to create entity %s", #id_); \ } \ (void)id_; \ (void)ecs_id(id_) @@ -8630,7 +9482,10 @@ int ecs_value_move_ctor( /** Declare & define an entity. * * Example: - * ECS_ENTITY(world, MyEntity, Position, Velocity); + * + * @code + * ECS_ENTITY(world, MyEntity, Position, Velocity); + * @endcode */ #define ECS_ENTITY(world, id, ...) \ ecs_entity_t ecs_id(id); \ @@ -8641,16 +9496,22 @@ int ecs_value_move_ctor( #define ECS_TAG_DECLARE ECS_DECLARE /** Define a forward declared tag. - * + * * Example: - * ECS_TAG_DEFINE(world, MyTag); + * + * @code + * ECS_TAG_DEFINE(world, MyTag); + * @endcode */ #define ECS_TAG_DEFINE(world, id) ECS_ENTITY_DEFINE(world, id, 0) /** Declare & define a tag. * * Example: - * ECS_TAG(world, MyTag); + * + * @code + * ECS_TAG(world, MyTag); + * @endcode */ #define ECS_TAG(world, id) ECS_ENTITY(world, id, 0) @@ -8658,16 +9519,22 @@ int ecs_value_move_ctor( #define ECS_PREFAB_DECLARE ECS_DECLARE /** Define a forward declared prefab. - * + * * Example: - * ECS_PREFAB_DEFINE(world, MyPrefab, Position, Velocity); + * + * @code + * ECS_PREFAB_DEFINE(world, MyPrefab, Position, Velocity); + * @endcode */ #define ECS_PREFAB_DEFINE(world, id, ...) ECS_ENTITY_DEFINE(world, id, Prefab, __VA_ARGS__) /** Declare & define a prefab. * * Example: - * ECS_PREFAB(world, MyPrefab, Position, Velocity); + * + * @code + * ECS_PREFAB(world, MyPrefab, Position, Velocity); + * @endcode */ #define ECS_PREFAB(world, id, ...) ECS_ENTITY(world, id, Prefab, __VA_ARGS__) @@ -8675,9 +9542,12 @@ int ecs_value_move_ctor( #define ECS_COMPONENT_DECLARE(id) ecs_entity_t ecs_id(id) /** Define a forward declared component. - * + * * Example: - * ECS_COMPONENT_DEFINE(world, Position); + * + * @code + * ECS_COMPONENT_DEFINE(world, Position); + * @endcode */ #define ECS_COMPONENT_DEFINE(world, id_) \ {\ @@ -8692,12 +9562,15 @@ int ecs_value_move_ctor( desc.type.alignment = ECS_ALIGNOF(id_); \ ecs_id(id_) = ecs_component_init(world, &desc);\ }\ - ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, NULL) + ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, "failed to create component %s", #id_) /** Declare & define a component. * * Example: - * ECS_COMPONENT(world, Position); + * + * @code + * ECS_COMPONENT(world, Position); + * @endcode */ #define ECS_COMPONENT(world, id)\ ecs_entity_t ecs_id(id) = 0;\ @@ -8708,9 +9581,12 @@ int ecs_value_move_ctor( #define ECS_OBSERVER_DECLARE(id) ecs_entity_t ecs_id(id) /** Define a forward declared observer. - * + * * Example: - * ECS_OBSERVER_DEFINE(world, AddPosition, EcsOnAdd, Position); + * + * @code + * ECS_OBSERVER_DEFINE(world, AddPosition, EcsOnAdd, Position); + * @endcode */ #define ECS_OBSERVER_DEFINE(world, id_, kind, ...)\ {\ @@ -8720,16 +9596,19 @@ int ecs_value_move_ctor( edesc.name = #id_; \ desc.entity = ecs_entity_init(world, &edesc); \ desc.callback = id_;\ - desc.filter.expr = #__VA_ARGS__;\ + desc.query.expr = #__VA_ARGS__;\ desc.events[0] = kind;\ ecs_id(id_) = ecs_observer_init(world, &desc);\ - ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, NULL);\ + ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, "failed to create observer %s", #id_);\ } /** Declare & define an observer. * * Example: - * ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); + * + * @code + * ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); + * @endcode */ #define ECS_OBSERVER(world, id, kind, ...)\ ecs_entity_t ecs_id(id) = 0; \ @@ -8738,23 +9617,64 @@ int ecs_value_move_ctor( (void)ecs_id(id);\ (void)id -/** Shorthand for creating an entity with ecs_entity_init. +/* Forward declare a query. */ +#define ECS_QUERY_DECLARE(name) ecs_query_t* name + +/** Define a forward declared observer. * * Example: - * ecs_entity(world, { - * .name = "MyEntity" - * }); + * + * @code + * ECS_QUERY_DEFINE(world, AddPosition, Position); + * @endcode + */ +#define ECS_QUERY_DEFINE(world, name_, ...)\ + {\ + ecs_query_desc_t desc = {0};\ + ecs_entity_desc_t edesc = {0}; \ + edesc.name = #name_; \ + desc.entity = ecs_entity_init(world, &edesc); \ + desc.expr = #__VA_ARGS__;\ + name_ = ecs_query_init(world, &desc);\ + ecs_assert(name_ != NULL, ECS_INVALID_PARAMETER, "failed to create query %s", #name_);\ + } + +/** Declare & define an observer. + * + * Example: + * + * @code + * ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); + * @endcode + */ +#define ECS_QUERY(world, name, ...)\ + ecs_query_t* name = NULL; \ + ECS_QUERY_DEFINE(world, name, __VA_ARGS__);\ + (void)name + +/** Shorthand for creating an entity with ecs_entity_init(). + * + * Example: + * + * @code + * ecs_entity(world, { + * .name = "MyEntity" + * }); + * @endcode */ #define ecs_entity(world, ...)\ ecs_entity_init(world, &(ecs_entity_desc_t) __VA_ARGS__ ) -/** Shorthand for creating a component with ecs_component_init. +/** Shorthand for creating a component with ecs_component_init(). * * Example: - * ecs_component(world, { - * .type.size = 4, - * .type.alignment = 4 - * }); + * + * @code + * ecs_component(world, { + * .type.size = 4, + * .type.alignment = 4 + * }); + * @endcode */ #define ecs_component(world, ...)\ ecs_component_init(world, &(ecs_component_desc_t) __VA_ARGS__ ) @@ -8762,7 +9682,10 @@ int ecs_value_move_ctor( /** Shorthand for creating a component from a type. * * Example: - * ecs_component_t(world, Position); + * + * @code + * ecs_component_t(world, Position); + * @endcode */ #define ecs_component_t(world, T)\ ecs_component_init(world, &(ecs_component_desc_t) { \ @@ -8775,34 +9698,27 @@ int ecs_value_move_ctor( .type.alignment = ECS_ALIGNOF(T) \ }) -/** Shorthand for creating a filter with ecs_filter_init. - * - * Example: - * ecs_filter(world, { - * .terms = {{ ecs_id(Position) }} - * }); - */ -#define ecs_filter(world, ...)\ - ecs_filter_init(world, &(ecs_filter_desc_t) __VA_ARGS__ ) - -/** Shorthand for creating a query with ecs_query_init. +/** Shorthand for creating a query with ecs_query_cache_init. * * Example: * ecs_query(world, { - * .filter.terms = {{ ecs_id(Position) }} + * .terms = {{ ecs_id(Position) }} * }); */ #define ecs_query(world, ...)\ ecs_query_init(world, &(ecs_query_desc_t) __VA_ARGS__ ) -/** Shorthand for creating an observer with ecs_observer_init. +/** Shorthand for creating an observer with ecs_observer_init(). * * Example: - * ecs_observer(world, { - * .filter.terms = {{ ecs_id(Position) }}, - * .events = { EcsOnAdd }, - * .callback = AddPosition - * }); + * + * @code + * ecs_observer(world, { + * .terms = {{ ecs_id(Position) }}, + * .events = { EcsOnAdd }, + * .callback = AddPosition + * }); + * @endcode */ #define ecs_observer(world, ...)\ ecs_observer_init(world, &(ecs_observer_desc_t) __VA_ARGS__ ) @@ -8811,7 +9727,8 @@ int ecs_value_move_ctor( /** * @defgroup flecs_c_type_safe Type Safe API - * @brief Macro's that wrap around core functions to provide a "type safe" API in C + * Macro's that wrap around core functions to provide a "type safe" API in C + * * @{ */ @@ -8825,7 +9742,7 @@ int ecs_value_move_ctor( * @{ */ -#define ecs_new(world, T) ecs_new_w_id(world, ecs_id(T)) +#define ecs_new_w(world, T) ecs_new_w_id(world, ecs_id(T)) #define ecs_new_w_pair(world, first, second)\ ecs_new_w_id(world, ecs_pair(first, second)) @@ -8833,20 +9750,6 @@ int ecs_value_move_ctor( #define ecs_bulk_new(world, component, count)\ ecs_bulk_new_w_id(world, ecs_id(component), count) -#define ecs_new_entity(world, n)\ - ecs_entity_init(world, &(ecs_entity_desc_t){\ - .name = n,\ - }) - -#define ecs_new_prefab(world, n)\ - ecs_entity_init(world, &(ecs_entity_desc_t){\ - .name = n,\ - .add = {EcsPrefab}\ - }) - -#define ecs_delete_children(world, parent)\ - ecs_delete_with(world, ecs_pair(EcsChildOf, parent)) - /** @} */ /** @@ -8868,11 +9771,11 @@ int ecs_value_move_ctor( ecs_remove_id(world, subject, ecs_pair(first, second)) -#define ecs_override(world, entity, T)\ - ecs_override_id(world, entity, ecs_id(T)) +#define ecs_auto_override(world, entity, T)\ + ecs_auto_override_id(world, entity, ecs_id(T)) -#define ecs_override_pair(world, subject, first, second)\ - ecs_override_id(world, subject, ecs_pair(first, second)) +#define ecs_auto_override_pair(world, subject, first, second)\ + ecs_auto_override_id(world, subject, ecs_pair(first, second)) /** @} */ @@ -8881,6 +9784,12 @@ int ecs_value_move_ctor( * @{ */ +/* insert */ +#define ecs_insert(world, ...)\ + ecs_entity(world, { .set = ecs_values(__VA_ARGS__)}) + +/* set */ + #define ecs_set_ptr(world, entity, component, ptr)\ ecs_set_id(world, entity, ecs_id(component), sizeof(component), ptr) @@ -8897,17 +9806,19 @@ int ecs_value_move_ctor( ecs_pair(first, ecs_id(Second)),\ sizeof(Second), &(Second)__VA_ARGS__) -#define ecs_set_pair_object ecs_set_pair_second - #define ecs_set_override(world, entity, T, ...)\ - ecs_add_id(world, entity, ECS_OVERRIDE | ecs_id(T));\ + ecs_add_id(world, entity, ECS_AUTO_OVERRIDE | ecs_id(T));\ ecs_set(world, entity, T, __VA_ARGS__) -#define ecs_emplace(world, entity, T)\ - (ECS_CAST(T*, ecs_emplace_id(world, entity, ecs_id(T)))) +/* emplace */ + +#define ecs_emplace(world, entity, T, is_new)\ + (ECS_CAST(T*, ecs_emplace_id(world, entity, ecs_id(T), is_new))) -#define ecs_emplace_pair(world, entity, First, second)\ - (ECS_CAST(First*, ecs_emplace_id(world, entity, ecs_pair_t(First, second)))) +#define ecs_emplace_pair(world, entity, First, second, is_new)\ + (ECS_CAST(First*, ecs_emplace_id(world, entity, ecs_pair_t(First, second), is_new))) + +/* get */ #define ecs_get(world, entity, T)\ (ECS_CAST(const T*, ecs_get_id(world, entity, ecs_id(T)))) @@ -8920,7 +9831,55 @@ int ecs_value_move_ctor( (ECS_CAST(const Second*, ecs_get_id(world, subject,\ ecs_pair(first, ecs_id(Second))))) -#define ecs_get_pair_object ecs_get_pair_second +/* get_mut */ + +#define ecs_get_mut(world, entity, T)\ + (ECS_CAST(T*, ecs_get_mut_id(world, entity, ecs_id(T)))) + +#define ecs_get_mut_pair(world, subject, First, second)\ + (ECS_CAST(First*, ecs_get_mut_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_get_mut_pair_second(world, subject, first, Second)\ + (ECS_CAST(Second*, ecs_get_mut_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +#define ecs_get_mut(world, entity, T)\ + (ECS_CAST(T*, ecs_get_mut_id(world, entity, ecs_id(T)))) + +/* ensure */ + +#define ecs_ensure(world, entity, T)\ + (ECS_CAST(T*, ecs_ensure_id(world, entity, ecs_id(T)))) + +#define ecs_ensure_pair(world, subject, First, second)\ + (ECS_CAST(First*, ecs_ensure_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_ensure_pair_second(world, subject, first, Second)\ + (ECS_CAST(Second*, ecs_ensure_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +#define ecs_ensure(world, entity, T)\ + (ECS_CAST(T*, ecs_ensure_id(world, entity, ecs_id(T)))) + +#define ecs_ensure_pair(world, subject, First, second)\ + (ECS_CAST(First*, ecs_ensure_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_ensure_pair_second(world, subject, first, Second)\ + (ECS_CAST(Second*, ecs_ensure_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +/* modified */ + +#define ecs_modified(world, entity, component)\ + ecs_modified_id(world, entity, ecs_id(component)) + +#define ecs_modified_pair(world, subject, first, second)\ + ecs_modified_id(world, subject, ecs_pair(first, second)) + +/* record */ #define ecs_record_get(world, record, T)\ (ECS_CAST(const T*, ecs_record_get_id(world, record, ecs_id(T)))) @@ -8936,43 +9895,22 @@ int ecs_value_move_ctor( (ECS_CAST(const Second*, ecs_record_get_id(world, record,\ ecs_pair(first, ecs_id(Second))))) -#define ecs_record_get_mut(world, record, T)\ - (ECS_CAST(T*, ecs_record_get_mut_id(world, record, ecs_id(T)))) +#define ecs_record_ensure(world, record, T)\ + (ECS_CAST(T*, ecs_record_ensure_id(world, record, ecs_id(T)))) -#define ecs_record_get_mut_pair(world, record, First, second)\ - (ECS_CAST(First*, ecs_record_get_mut_id(world, record, \ +#define ecs_record_ensure_pair(world, record, First, second)\ + (ECS_CAST(First*, ecs_record_ensure_id(world, record, \ ecs_pair(ecs_id(First), second)))) -#define ecs_record_get_mut_pair_second(world, record, first, Second)\ - (ECS_CAST(Second*, ecs_record_get_mut_id(world, record,\ +#define ecs_record_ensure_pair_second(world, record, first, Second)\ + (ECS_CAST(Second*, ecs_record_ensure_id(world, record,\ ecs_pair(first, ecs_id(Second))))) -#define ecs_record_get_mut_pair_object ecs_record_get_mut_pair_second - #define ecs_ref_init(world, entity, T)\ ecs_ref_init_id(world, entity, ecs_id(T)) #define ecs_ref_get(world, ref, T)\ - (ECS_CAST(const T*, ecs_ref_get_id(world, ref, ecs_id(T)))) - -#define ecs_get_mut(world, entity, T)\ - (ECS_CAST(T*, ecs_get_mut_id(world, entity, ecs_id(T)))) - -#define ecs_get_mut_pair(world, subject, First, second)\ - (ECS_CAST(First*, ecs_get_mut_id(world, subject,\ - ecs_pair(ecs_id(First), second)))) - -#define ecs_get_mut_pair_second(world, subject, first, Second)\ - (ECS_CAST(Second*, ecs_get_mut_id(world, subject,\ - ecs_pair(first, ecs_id(Second))))) - -#define ecs_get_mut_pair_object ecs_get_mut_pair_second - -#define ecs_modified(world, entity, component)\ - ecs_modified_id(world, entity, ecs_id(component)) - -#define ecs_modified_pair(world, subject, first, second)\ - ecs_modified_id(world, subject, ecs_pair(first, second)) + (ECS_CAST(T*, ecs_ref_get_id(world, ref, ecs_id(T)))) /** @} */ @@ -8996,8 +9934,8 @@ int ecs_value_move_ctor( #define ecs_singleton_set(world, comp, ...)\ ecs_set(world, ecs_id(comp), comp, __VA_ARGS__) -#define ecs_singleton_get_mut(world, comp)\ - ecs_get_mut(world, ecs_id(comp), comp) +#define ecs_singleton_ensure(world, comp)\ + ecs_ensure(world, ecs_id(comp), comp) #define ecs_singleton_modified(world, comp)\ ecs_modified(world, ecs_id(comp), comp) @@ -9031,6 +9969,9 @@ int ecs_value_move_ctor( #define ecs_shares(world, entity, T)\ (ecs_shares_id(world, entity, ecs_id(T))) +#define ecs_get_target_for(world, entity, rel, T)\ + ecs_get_target_for_id(world, entity, rel, ecs_id(T)) + /** @} */ /** @@ -9041,7 +9982,7 @@ int ecs_value_move_ctor( #define ecs_enable_component(world, entity, T, enable)\ ecs_enable_id(world, entity, ecs_id(T), enable) -#define ecs_is_enabled_component(world, entity, T)\ +#define ecs_is_enabled(world, entity, T)\ ecs_is_enabled_id(world, entity, ecs_id(T)) #define ecs_enable_pair(world, entity, First, second, enable)\ @@ -9057,27 +9998,21 @@ int ecs_value_move_ctor( * @{ */ -#define ecs_lookup_path(world, parent, path)\ +#define ecs_lookup_from(world, parent, path)\ ecs_lookup_path_w_sep(world, parent, path, ".", NULL, true) -#define ecs_lookup_fullpath(world, path)\ - ecs_lookup_path_w_sep(world, 0, path, ".", NULL, true) - -#define ecs_get_path(world, parent, child)\ +#define ecs_get_path_from(world, parent, child)\ ecs_get_path_w_sep(world, parent, child, ".", NULL) -#define ecs_get_fullpath(world, child)\ +#define ecs_get_path(world, child)\ ecs_get_path_w_sep(world, 0, child, ".", NULL) -#define ecs_get_fullpath_buf(world, child, buf)\ - ecs_get_path_w_sep_buf(world, 0, child, ".", NULL, buf) +#define ecs_get_path_buf(world, child, buf)\ + ecs_get_path_w_sep_buf(world, 0, child, ".", NULL, buf, false) #define ecs_new_from_path(world, parent, path)\ ecs_new_from_path_w_sep(world, parent, path, ".", NULL) -#define ecs_new_from_fullpath(world, path)\ - ecs_new_from_path_w_sep(world, 0, path, ".", NULL) - #define ecs_add_path(world, entity, parent, path)\ ecs_add_path_w_sep(world, entity, parent, path, ".", NULL) @@ -9101,35 +10036,50 @@ int ecs_value_move_ctor( /** Declare a constructor. * Example: - * ECS_CTOR(MyType, ptr, { ptr->value = NULL; }); + * + * @code + * ECS_CTOR(MyType, ptr, { ptr->value = NULL; }); + * @endcode */ #define ECS_CTOR(type, var, ...)\ ECS_XTOR_IMPL(type, ctor, var, __VA_ARGS__) /** Declare a destructor. * Example: - * ECS_DTOR(MyType, ptr, { free(ptr->value); }); + * + * @code + * ECS_DTOR(MyType, ptr, { free(ptr->value); }); + * @endcode */ #define ECS_DTOR(type, var, ...)\ ECS_XTOR_IMPL(type, dtor, var, __VA_ARGS__) /** Declare a copy action. * Example: - * ECS_COPY(MyType, dst, src, { dst->value = strdup(src->value); }); + * + * @code + * ECS_COPY(MyType, dst, src, { dst->value = strdup(src->value); }); + * @endcode */ #define ECS_COPY(type, dst_var, src_var, ...)\ ECS_COPY_IMPL(type, dst_var, src_var, __VA_ARGS__) /** Declare a move action. * Example: - * ECS_MOVE(MyType, dst, src, { dst->value = src->value; src->value = 0; }); + * + * @code + * ECS_MOVE(MyType, dst, src, { dst->value = src->value; src->value = 0; }); + * @endcode */ #define ECS_MOVE(type, dst_var, src_var, ...)\ ECS_MOVE_IMPL(type, dst_var, src_var, __VA_ARGS__) /** Declare component hooks. * Example: - * ECS_ON_SET(MyType, ptr, { printf("%d\n", ptr->value); }); + * + * @code + * ECS_ON_SET(MyType, ptr, { printf("%d\n", ptr->value); }); + * @endcode */ #define ECS_ON_ADD(type, ptr, ...)\ ECS_HOOK_IMPL(type, ecs_on_add(type), ptr, __VA_ARGS__) @@ -9167,6 +10117,12 @@ int ecs_value_move_ctor( #define ecs_field(it, T, index)\ (ECS_CAST(T*, ecs_field_w_size(it, sizeof(T), index))) +#define ecs_field_self(it, T, index)\ + (ECS_CAST(T*, ecs_field_self_w_size(it, sizeof(T), index))) + +#define ecs_field_at(it, T, index, row)\ + (ECS_CAST(T*, ecs_field_at_w_size(it, sizeof(T), index, row))) + /** @} */ /** @@ -9190,17 +10146,35 @@ int ecs_value_move_ctor( * @{ */ -#define ecs_value(T, ptr) ((ecs_value_t){ecs_id(T), ptr}) +/** Convenience macro for creating compound literal id array */ +#define ecs_ids(...) (ecs_id_t[]){ __VA_ARGS__, 0 } + +/** Convenience macro for creating compound literal values array */ +#define ecs_values(...) (ecs_value_t[]){ __VA_ARGS__, {0, 0}} + +/** Convenience macro for creating compound literal value */ +#define ecs_value_ptr(T, ptr) ((ecs_value_t){ecs_id(T), ptr}) + +/** Convenience macro for creating compound literal pair value */ +#define ecs_value_pair(R, t, ...) ((ecs_value_t){ecs_pair_t(R, t), &(R)__VA_ARGS__}) + +/** Convenience macro for creating compound literal pair value */ +#define ecs_value_pair_2nd(r, T, ...) ((ecs_value_t){ecs_pair(r, ecs_id(T)), &(T)__VA_ARGS__}) + +/** Convenience macro for creating heap allocated value */ #define ecs_value_new_t(world, T) ecs_value_new(world, ecs_id(T)) +/** Convenience macro for creating compound literal value literal */ +#define ecs_value(T, ...) ((ecs_value_t){ecs_id(T), &(T)__VA_ARGS__}) + /** @} */ /** @} */ /** * @defgroup flecs_c_table_sorting Table sorting - * @brief Convenience macro's for sorting tables. - * + * Convenience macro's for sorting tables. + * * @{ */ #define ecs_sort_table(id) ecs_id(id##_sort_table) @@ -9224,8 +10198,11 @@ int ecs_value_move_ctor( * int32_t hi * ecs_order_by_action_t order_by - Pointer to the original comparison function. You are not supposed to use it. * Example: - * int CompareMyType(ecs_entity_t e1, const void* ptr1, ecs_entity_t e2, const void* ptr2) { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; } - * ECS_SORT_TABLE_WITH_COMPARE(MyType, MyCustomCompare, CompareMyType) + * + * @code + * int CompareMyType(ecs_entity_t e1, const void* ptr1, ecs_entity_t e2, const void* ptr2) { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; } + * ECS_SORT_TABLE_WITH_COMPARE(MyType, MyCustomCompare, CompareMyType) + * @endcode */ #define ECS_SORT_TABLE_WITH_COMPARE(id, op_name, compare_fn, ...) \ static int32_t ECS_CONCAT(op_name, _partition)( \ @@ -9291,8 +10268,11 @@ int ecs_value_move_ctor( * Variadic arguments are prepended before generated functions, use it to declare static * or exported functions. * Example: - * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); - * ECS_SORT_TABLE(MyType) + * + * @code + * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); + * ECS_SORT_TABLE(MyType) + * @endcode */ #define ECS_SORT_TABLE(id, ...) \ ECS_SORT_TABLE_WITH_COMPARE(id, ecs_sort_table(id), ecs_compare(id), __VA_ARGS__) @@ -9302,7 +10282,10 @@ int ecs_value_move_ctor( * ecs_entity_t e1, const void* ptr1, * ecs_entity_t e2, const void* ptr2 * Example: - * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); + * + * @code + * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); + * @endcode */ #define ECS_COMPARE(id, ...) \ int ecs_compare(id)(ecs_entity_t e1, const void* ptr1, ecs_entity_t e2, const void* ptr2) { \ @@ -9313,24 +10296,19 @@ int ecs_value_move_ctor( /** * @defgroup flecs_c_misc Misc - * @brief Misc convenience macro's. - * + * Misc convenience macro's. + * * @{ */ #define ecs_isa(e) ecs_pair(EcsIsA, e) #define ecs_childof(e) ecs_pair(EcsChildOf, e) #define ecs_dependson(e) ecs_pair(EcsDependsOn, e) +#define ecs_with(e) ecs_pair(EcsWith, e) -#define ecs_query_new(world, q_expr)\ - ecs_query_init(world, &(ecs_query_desc_t){\ - .filter.expr = q_expr\ - }) - -#define ecs_rule_new(world, q_expr)\ - ecs_rule_init(world, &(ecs_filter_desc_t){\ - .expr = q_expr\ - }) +#define ecs_each(world, id) ecs_each_id(world, ecs_id(id)) +#define ecs_each_pair(world, r, t) ecs_each_id(world, ecs_pair(r, t)) +#define ecs_each_pair_t(world, R, t) ecs_each_id(world, ecs_pair(ecs_id(R), t)) /** @} */ @@ -9360,20 +10338,8 @@ int ecs_value_move_ctor( #ifdef FLECS_NO_MODULE #undef FLECS_MODULE #endif -#ifdef FLECS_NO_PARSER -#undef FLECS_PARSER -#endif -#ifdef FLECS_NO_PLECS -#undef FLECS_PLECS -#endif -#ifdef FLECS_NO_RULES -#undef FLECS_RULES -#endif -#ifdef FLECS_NO_SNAPSHOT -#undef FLECS_SNAPSHOT -#endif -#ifdef FLECS_NO_MONITOR -#undef FLECS_MONITOR +#ifdef FLECS_NO_SCRIPT +#undef FLECS_SCRIPT #endif #ifdef FLECS_NO_STATS #undef FLECS_STATS @@ -9381,6 +10347,9 @@ int ecs_value_move_ctor( #ifdef FLECS_NO_SYSTEM #undef FLECS_SYSTEM #endif +#ifdef FLECS_NO_ALERTS +#undef FLECS_ALERTS +#endif #ifdef FLECS_NO_PIPELINE #undef FLECS_PIPELINE #endif @@ -9390,24 +10359,15 @@ int ecs_value_move_ctor( #ifdef FLECS_NO_META #undef FLECS_META #endif -#ifdef FLECS_NO_META_C -#undef FLECS_META_C -#endif #ifdef FLECS_NO_UNITS #undef FLECS_UNITS #endif -#ifdef FLECS_NO_EXPR -#undef FLECS_EXPR -#endif #ifdef FLECS_NO_JSON #undef FLECS_JSON #endif #ifdef FLECS_NO_DOC #undef FLECS_DOC #endif -#ifdef FLECS_NO_COREDOC -#undef FLECS_COREDOC -#endif #ifdef FLECS_NO_LOG #undef FLECS_LOG #endif @@ -9435,8 +10395,8 @@ int ecs_value_move_ctor( * The journaling addon traces API calls. The trace is formatted as runnable * C code, which allows for (partially) reproducing the behavior of an app * with the journaling trace. - * - * The journaling addon is disabled by default. Enabling it can have a + * + * The journaling addon is disabled by default. Enabling it can have a * significant impact on performance. */ @@ -9451,9 +10411,10 @@ int ecs_value_move_ctor( /** * @defgroup c_addons_journal Journal - * @brief Journaling addon (disabled by default). - * - * \ingroup c_addons + * @ingroup c_addons + * Journaling addon (disabled by default). + * + * * @{ */ @@ -9494,38 +10455,37 @@ void flecs_journal_end(void); #ifdef __cplusplus } #endif // __cplusplus +/** @} */ #endif // FLECS_JOURNAL_H #else #define flecs_journal_begin(...) #define flecs_journal_end(...) #define flecs_journal(...) -/** @} */ - #endif // FLECS_JOURNAL /** * @file addons/log.h * @brief Logging addon. - * + * * The logging addon provides an API for (debug) tracing and reporting errors * at various levels. When enabled, the logging addon can provide more detailed * information about the state of the ECS and any errors that may occur. - * + * * The logging addon can be disabled to reduce footprint of the library, but * limits information logged to only file, line and error code. - * + * * When enabled the logging addon can be configured to exclude levels of tracing - * from the build to reduce the impact on performance. By default all debug + * from the build to reduce the impact on performance. By default all debug * tracing is enabled for debug builds, tracing is enabled at release builds. - * - * Applications can change the logging level at runtime with ecs_log_set_level, - * but what is actually logged depends on what is compiled (when compiled - * without debug tracing, setting the runtime level to debug won't have an + * + * Applications can change the logging level at runtime with ecs_log_set_level(), + * but what is actually logged depends on what is compiled (when compiled + * without debug tracing, setting the runtime level to debug won't have an * effect). - * + * * The logging addon uses the OS API log_ function for all tracing. - * + * * Note that even when the logging addon is not enabled, its header/source must * be included in a build. To prevent unused variable warnings in the code, some * API functions are included when the addon is disabled, but have empty bodies. @@ -9542,9 +10502,9 @@ extern "C" { /** * @defgroup c_addons_log Log - * @brief Logging functions. - * - * \ingroup c_addons + * @ingroup c_addons + * Logging functions. + * * @{ */ @@ -9552,16 +10512,17 @@ extern "C" { //// Tracing //////////////////////////////////////////////////////////////////////////////// +/** Log message indicating an operation is deprecated. */ FLECS_API void ecs_deprecated_( - const char *file, - int32_t line, + const char *file, + int32_t line, const char *msg); /** Increase log stack. * This operation increases the indent_ value of the OS API and can be useful to * make nested behavior more visible. - * + * * @param level The log level. */ FLECS_API @@ -9570,14 +10531,14 @@ void ecs_log_push_(int32_t level); /** Decrease log stack. * This operation decreases the indent_ value of the OS API and can be useful to * make nested behavior more visible. - * + * * @param level The log level. */ FLECS_API void ecs_log_pop_(int32_t level); /** Should current level be logged. - * This operation returns true when the specified log level should be logged + * This operation returns true when the specified log level should be logged * with the current log level. * * @param level The log level to check for. @@ -9672,7 +10633,7 @@ void ecs_assert_log_( FLECS_API void ecs_parser_error_( const char *name, - const char *expr, + const char *expr, int64_t column, const char *fmt, ...); @@ -9680,7 +10641,7 @@ void ecs_parser_error_( FLECS_API void ecs_parser_errorv_( const char *name, - const char *expr, + const char *expr, int64_t column, const char *fmt, va_list args); @@ -9849,7 +10810,7 @@ void ecs_parser_errorv_( ecs_abort_(error_code, __FILE__, __LINE__, __VA_ARGS__);\ ecs_os_abort(); abort(); /* satisfy compiler/static analyzers */ -/** Assert. +/** Assert. * Aborts if condition is false, disabled in debug mode. */ #if defined(FLECS_NDEBUG) && !defined(FLECS_KEEP_ASSERT) #define ecs_assert(condition, error_code, ...) @@ -9959,8 +10920,8 @@ FLECS_API int ecs_log_set_level( int level); -/** Get current log level. - * +/** Get current log level. + * * @return Previous log level. */ FLECS_API @@ -9990,12 +10951,12 @@ bool ecs_log_enable_timestamp( /** Enable/disable logging time since last log. * By default deltatime is disabled. Note that enabling timestamps introduces * overhead as the logging code will need to obtain the current time. - * + * * When enabled, this logs the amount of time in seconds passed since the last * log, when this amount is non-zero. The format is a '+' character followed by * the number of seconds: - * - * +1 trace: log message + * + * +1 trace: log message * * @param enabled Whether to enable tracing with timestamps. * @return Previous timestamp setting. @@ -10082,12 +11043,9 @@ int ecs_log_last_error(void); /* Handle addon dependencies that need declarations to be visible in header */ -#ifdef FLECS_MONITOR -#ifndef FLECS_STATS -#define FLECS_STATS -#endif -#ifndef FLECS_SYSTEM -#define FLECS_SYSTEM +#ifdef FLECS_STATS +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE #endif #ifndef FLECS_TIMER #define FLECS_TIMER @@ -10095,11 +11053,9 @@ int ecs_log_last_error(void); #endif #ifdef FLECS_REST +#ifndef FLECS_HTTP #define FLECS_HTTP #endif - -#ifdef FLECS_PLECS -#define FLECS_EXPR #endif #ifdef FLECS_APP @@ -10131,9 +11087,9 @@ extern "C" { /** * @defgroup c_addons_app App - * @brief Optional addon for running the main application loop. - * - * \ingroup c_addons + * @ingroup c_addons + * Optional addon for running the main application loop. + * * @{ */ @@ -10141,14 +11097,14 @@ extern "C" { typedef int(*ecs_app_init_action_t)( ecs_world_t *world); -/** Used with ecs_app_run. */ +/** Used with ecs_app_run(). */ typedef struct ecs_app_desc_t { ecs_ftime_t target_fps; /**< Target FPS. */ ecs_ftime_t delta_time; /**< Frame time increment (0 for measured values) */ int32_t threads; /**< Number of threads. */ int32_t frames; /**< Number of frames to run (0 for infinite) */ bool enable_rest; /**< Enables ECS access over HTTP, necessary for explorer */ - bool enable_monitor; /**< Periodically collect statistics */ + bool enable_stats; /**< Periodically collect statistics */ uint16_t port; /**< HTTP port used by REST API */ ecs_app_init_action_t init; /**< If set, function is ran before starting the @@ -10159,22 +11115,22 @@ typedef struct ecs_app_desc_t { /** Callback type for run action. */ typedef int(*ecs_app_run_action_t)( - ecs_world_t *world, + ecs_world_t *world, ecs_app_desc_t *desc); /** Callback type for frame action. */ typedef int(*ecs_app_frame_action_t)( - ecs_world_t *world, + ecs_world_t *world, const ecs_app_desc_t *desc); /** Run application. * This will run the application with the parameters specified in desc. After - * the application quits (ecs_quit is called) the world will be cleaned up. - * + * the application quits (ecs_quit() is called) the world will be cleaned up. + * * If a custom run action is set, it will be invoked by this operation. The * default run action calls the frame action in a loop until it returns a * non-zero value. - * + * * @param world The world. * @param desc Application parameters. */ @@ -10185,11 +11141,11 @@ int ecs_app_run( /** Default frame callback. * This operation will run a single frame. By default this operation will invoke - * ecs_progress directly, unless a custom frame action is set. - * + * ecs_progress() directly, unless a custom frame action is set. + * * @param world The world. - * @param desc The desc struct passed to ecs_app_run. - * @return value returned by ecs_progress + * @param desc The desc struct passed to ecs_app_run(). + * @return value returned by ecs_progress() */ FLECS_API int ecs_app_run_frame( @@ -10197,8 +11153,8 @@ int ecs_app_run_frame( const ecs_app_desc_t *desc); /** Set custom run action. - * See ecs_app_run. - * + * See ecs_app_run(). + * * @param callback The run action. */ FLECS_API @@ -10206,8 +11162,8 @@ int ecs_app_set_run_action( ecs_app_run_action_t callback); /** Set custom frame action. - * See ecs_app_run_frame. - * + * See ecs_app_run_frame(). + * * @param callback The frame action. */ FLECS_API @@ -10233,19 +11189,19 @@ int ecs_app_set_frame_action( /** * @file addons/http.h * @brief HTTP addon. - * + * * Minimalistic HTTP server that can receive and reply to simple HTTP requests. * The main goal of this addon is to enable remotely connecting to a running * Flecs application (for example, with a web-based UI) and request/visualize * data from the ECS world. - * + * * Each server instance creates a single thread used for receiving requests. * Receiving requests are enqueued and handled when the application calls - * ecs_http_server_dequeue. This increases latency of request handling vs. - * responding directly in the receive thread, but is better suited for + * ecs_http_server_dequeue(). This increases latency of request handling vs. + * responding directly in the receive thread, but is better suited for * retrieving data from ECS applications, as requests can be processed by an ECS * system without having to lock the world. - * + * * This server is intended to be used in a development environment. */ @@ -10253,9 +11209,9 @@ int ecs_app_set_frame_action( /** * @defgroup c_addons_http Http - * @brief Simple HTTP server used for serving up REST API. - * - * \ingroup c_addons + * @ingroup c_addons + * Simple HTTP server used for serving up REST API. + * * @{ */ @@ -10266,20 +11222,20 @@ int ecs_app_set_frame_action( #ifndef FLECS_HTTP_H #define FLECS_HTTP_H -/* Maximum number of headers in request */ +/** Maximum number of headers in request. */ #define ECS_HTTP_HEADER_COUNT_MAX (32) -/* Maximum number of query parameters in request */ +/** Maximum number of query parameters in request. */ #define ECS_HTTP_QUERY_PARAM_COUNT_MAX (32) #ifdef __cplusplus extern "C" { #endif -/** HTTP server */ +/** HTTP server. */ typedef struct ecs_http_server_t ecs_http_server_t; -/** A connection manages communication with the remote host */ +/** A connection manages communication with the remote host. */ typedef struct { uint64_t id; ecs_http_server_t *server; @@ -10288,13 +11244,13 @@ typedef struct { char port[16]; } ecs_http_connection_t; -/** Helper type used for headers & URL query parameters */ +/** Helper type used for headers & URL query parameters. */ typedef struct { const char *key; const char *value; } ecs_http_key_value_t; -/** Supported request methods */ +/** Supported request methods. */ typedef enum { EcsHttpGet, EcsHttpPost, @@ -10304,7 +11260,7 @@ typedef enum { EcsHttpMethodUnsupported } ecs_http_method_t; -/** A request */ +/** An HTTP request. */ typedef struct { uint64_t id; @@ -10319,7 +11275,7 @@ typedef struct { ecs_http_connection_t *conn; } ecs_http_request_t; -/** A reply */ +/** An HTTP reply. */ typedef struct { int code; /**< default = 200 */ ecs_strbuf_t body; /**< default = "" */ @@ -10331,38 +11287,40 @@ typedef struct { #define ECS_HTTP_REPLY_INIT \ (ecs_http_reply_t){200, ECS_STRBUF_INIT, "OK", "application/json", ECS_STRBUF_INIT} -/* Global statistics. */ -extern int64_t ecs_http_request_received_count; -extern int64_t ecs_http_request_invalid_count; -extern int64_t ecs_http_request_handled_ok_count; -extern int64_t ecs_http_request_handled_error_count; -extern int64_t ecs_http_request_not_handled_count; -extern int64_t ecs_http_request_preflight_count; -extern int64_t ecs_http_send_ok_count; -extern int64_t ecs_http_send_error_count; -extern int64_t ecs_http_busy_count; +/* Global HTTP statistics. */ +extern int64_t ecs_http_request_received_count; /**< Total number of HTTP requests received. */ +extern int64_t ecs_http_request_invalid_count; /**< Total number of invalid HTTP requests. */ +extern int64_t ecs_http_request_handled_ok_count; /**< Total number of successful HTTP requests. */ +extern int64_t ecs_http_request_handled_error_count; /**< Total number of HTTP requests with errors. */ +extern int64_t ecs_http_request_not_handled_count; /**< Total number of HTTP requests with an unknown endpoint. */ +extern int64_t ecs_http_request_preflight_count; /**< Total number of preflight HTTP requests received. */ +extern int64_t ecs_http_send_ok_count; /**< Total number of HTTP replies successfully sent. */ +extern int64_t ecs_http_send_error_count; /**< Total number of HTTP replies that failed to send. */ +extern int64_t ecs_http_busy_count; /**< Total number of HTTP busy replies. */ /** Request callback. * Invoked for each valid request. The function should populate the reply and - * return true. When the function returns false, the server will reply with a + * return true. When the function returns false, the server will reply with a * 404 (Not found) code. */ typedef bool (*ecs_http_reply_action_t)( - const ecs_http_request_t* request, + const ecs_http_request_t* request, ecs_http_reply_t *reply, void *ctx); -/** Used with ecs_http_server_init. */ +/** Used with ecs_http_server_init(). */ typedef struct { ecs_http_reply_action_t callback; /**< Function called for each request */ void *ctx; /**< Passed to callback (optional) */ uint16_t port; /**< HTTP port */ const char *ipaddr; /**< Interface to listen on (optional) */ int32_t send_queue_wait_ms; /**< Send queue wait time when empty */ + double cache_timeout; /**< Cache invalidation timeout (0 disables caching) */ + double cache_purge_timeout; /**< Cache purge timeout (for purging cache entries) */ } ecs_http_server_desc_t; -/** Create server. - * Use ecs_http_server_start to start receiving requests. - * +/** Create server. + * Use ecs_http_server_start() to start receiving requests. + * * @param desc Server configuration parameters. * @return The new server, or NULL if creation failed. */ @@ -10370,18 +11328,18 @@ FLECS_API ecs_http_server_t* ecs_http_server_init( const ecs_http_server_desc_t *desc); -/** Destroy server. +/** Destroy server. * This operation will stop the server if it was still running. - * + * * @param server The server to destroy. */ FLECS_API void ecs_http_server_fini( ecs_http_server_t* server); -/** Start server. +/** Start server. * After this operation the server will be able to accept requests. - * + * * @param server The server to start. * @return Zero if successful, non-zero if failed. */ @@ -10389,10 +11347,10 @@ FLECS_API int ecs_http_server_start( ecs_http_server_t* server); -/** Process server requests. +/** Process server requests. * This operation invokes the reply callback for each received request. No new * requests will be enqueued while processing requests. - * + * * @param server The server for which to process requests. */ FLECS_API @@ -10400,9 +11358,9 @@ void ecs_http_server_dequeue( ecs_http_server_t* server, ecs_ftime_t delta_time); -/** Stop server. +/** Stop server. * After this operation no new requests can be received. - * + * * @param server The server. */ FLECS_API @@ -10411,7 +11369,8 @@ void ecs_http_server_stop( /** Emulate a request. * The request string must be a valid HTTP request. A minimal example: - * GET /entity/flecs/core/World?label=true HTTP/1.1 + * + * GET /entity/flecs/core/World?label=true HTTP/1.1 * * @param srv The server. * @param req The request. @@ -10425,7 +11384,7 @@ int ecs_http_server_http_request( ecs_size_t len, ecs_http_reply_t *reply_out); -/** Convenience wrapper around ecs_http_server_request. */ +/** Convenience wrapper around ecs_http_server_http_request(). */ FLECS_API int ecs_http_server_request( ecs_http_server_t* srv, @@ -10438,8 +11397,8 @@ FLECS_API void* ecs_http_server_ctx( ecs_http_server_t* srv); -/** Find header in request. - * +/** Find header in request. + * * @param req The request. * @param name name of the header to find * @return The header value, or NULL if not found. @@ -10449,8 +11408,8 @@ const char* ecs_http_get_header( const ecs_http_request_t* req, const char* name); -/** Find query parameter in request. - * +/** Find query parameter in request. + * * @param req The request. * @param name The parameter name. * @return The decoded parameter value, or NULL if not found. @@ -10482,17 +11441,17 @@ const char* ecs_http_get_param( * * A small REST API that uses the HTTP server and JSON serializer to provide * access to application data for remote applications. - * - * A description of the API can be found in docs/RestApi.md + * + * A description of the API can be found in docs/FlecsRemoteApi.md */ #ifdef FLECS_REST /** * @defgroup c_addons_rest Rest - * @brief REST API for querying and mutating entities. - * - * \ingroup c_addons + * @ingroup c_addons + * REST API for querying and mutating entities. + * * @{ */ @@ -10506,11 +11465,6 @@ const char* ecs_http_get_param( #define FLECS_JSON #endif -/* Query engine used */ -#ifndef FLECS_RULES -#define FLECS_RULES -#endif - /* For the REST system */ #ifndef FLECS_PIPELINE #define FLECS_PIPELINE @@ -10525,19 +11479,20 @@ extern "C" { #define ECS_REST_DEFAULT_PORT (27750) -/** Component that instantiates the REST API */ +/** Component that instantiates the REST API. */ FLECS_API extern const ecs_entity_t ecs_id(EcsRest); +/** Component that creates a REST API server when instantiated. */ typedef struct { uint16_t port; /**< Port of server (optional, default = 27750) */ char *ipaddr; /**< Interface address (optional, default = 0.0.0.0) */ void *impl; } EcsRest; -/** Create HTTP server for REST API. +/** Create HTTP server for REST API. * This allows for the creation of a REST server that can be managed by the * application without using Flecs systems. - * + * * @param world The world. * @param desc The HTTP server descriptor. * @return The HTTP server, or NULL if failed. @@ -10547,14 +11502,21 @@ ecs_http_server_t* ecs_rest_server_init( ecs_world_t *world, const ecs_http_server_desc_t *desc); -/** Cleanup REST HTTP server. - * The server must have been created with ecs_rest_server_init. +/** Cleanup REST HTTP server. + * The server must have been created with ecs_rest_server_init(). */ FLECS_API void ecs_rest_server_fini( ecs_http_server_t *srv); -/* Module import */ +/** Rest module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsRest) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsRestImport( ecs_world_t *world); @@ -10587,9 +11549,9 @@ void FlecsRestImport( /** * @defgroup c_addons_timer Timer - * @brief Run systems at a time interval. - * - * \ingroup c_addons + * @ingroup c_addons + * Run systems at a time interval. + * * @{ */ @@ -10629,15 +11591,15 @@ typedef struct EcsRateFilter { /** Set timer timeout. * This operation executes any systems associated with the timer after the - * specified timeout value. If the entity contains an existing timer, the - * timeout value will be reset. The timer can be started and stopped with - * ecs_start_timer and ecs_stop_timer. + * specified timeout value. If the entity contains an existing timer, the + * timeout value will be reset. The timer can be started and stopped with + * ecs_start_timer() and ecs_stop_timer(). * * The timer is synchronous, and is incremented each frame by delta_time. * * The tick_source entity will be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick - * source ticked this frame, the 'tick' member will be true. When the tick + * source ticked this frame, the 'tick' member will be true. When the tick * source is a system, the system will tick when the timer ticks. * * @param world The world. @@ -10652,18 +11614,18 @@ ecs_entity_t ecs_set_timeout( ecs_ftime_t timeout); /** Get current timeout value for the specified timer. - * This operation returns the value set by ecs_set_timeout. If no timer is + * This operation returns the value set by ecs_set_timeout(). If no timer is * active for this entity, the operation returns 0. * * After the timeout expires the EcsTimer component is removed from the entity. - * This means that if ecs_get_timeout is invoked after the timer is expired, the + * This means that if ecs_get_timeout() is invoked after the timer is expired, the * operation will return 0. * * The timer is synchronous, and is incremented each frame by delta_time. * * The tick_source entity will be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick - * source ticked this frame, the 'tick' member will be true. When the tick + * source ticked this frame, the 'tick' member will be true. When the tick * source is a system, the system will tick when the timer ticks. * * @param world The world. @@ -10684,8 +11646,8 @@ ecs_ftime_t ecs_get_timeout( * * The tick_source entity will be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick - * source ticked this frame, the 'tick' member will be true. When the tick - * source is a system, the system will tick when the timer ticks. + * source ticked this frame, the 'tick' member will be true. When the tick + * source is a system, the system will tick when the timer ticks. * * @param world The world. * @param tick_source The timer for which to set the interval (0 to create one). @@ -10696,10 +11658,10 @@ FLECS_API ecs_entity_t ecs_set_interval( ecs_world_t *world, ecs_entity_t tick_source, - ecs_ftime_t interval); + ecs_ftime_t interval); /** Get current interval value for the specified timer. - * This operation returns the value set by ecs_set_interval. If the entity is + * This operation returns the value set by ecs_set_interval(). If the entity is * not a timer, the operation will return 0. * * @param world The world. @@ -10735,7 +11697,7 @@ void ecs_stop_timer( /** Reset time value of timer to 0. * This operation resets the timer value to 0. - * + * * @param world The world. * @param tick_source The timer to reset. */ @@ -10744,10 +11706,10 @@ void ecs_reset_timer( ecs_world_t *world, ecs_entity_t tick_source); -/** Enable randomizing initial time value of timers. +/** Enable randomizing initial time value of timers. * Initializes timers with a random time value, which can improve scheduling as * systems/timers for the same interval don't all happen on the same tick. - * + * * @param world The world. */ FLECS_API @@ -10763,20 +11725,20 @@ void ecs_randomize_timers( * with interval timers alone. For example, if timer A has interval 2.0 and * timer B has interval 4.0, it is not guaranteed that B will tick at exactly * twice the multiple of A. This is partly due to the indeterministic nature of - * timers, and partly due to floating point rounding errors. + * timers, and partly due to floating point rounding errors. * - * Rate filters can be combined with timers (or other rate filters) to ensure + * Rate filters can be combined with timers (or other rate filters) to ensure * that a system ticks at an exact multiple of a tick source (which can be * another system). If a rate filter is created with a rate of 1 it will tick * at the exact same time as its source. * * If no tick source is provided, the rate filter will use the frame tick as - * source, which corresponds with the number of times ecs_progress is called. + * source, which corresponds with the number of times ecs_progress() is called. * * The tick_source entity will be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick - * source ticked this frame, the 'tick' member will be true. When the tick - * source is a system, the system will tick when the timer ticks. + * source ticked this frame, the 'tick' member will be true. When the tick + * source is a system, the system will tick when the timer ticks. * * @param world The world. * @param tick_source The rate filter entity (0 to create one). @@ -10795,7 +11757,7 @@ ecs_entity_t ecs_set_rate( * Systems can be their own tick source, which can be any of the tick sources * (one shot timers, interval times and rate filters). However, in some cases it * is must be guaranteed that different systems tick on the exact same frame. - * + * * This cannot be guaranteed by giving two systems the same interval/rate filter * as it is possible that one system is (for example) disabled, which would * cause the systems to go out of sync. To provide these guarantees, systems @@ -10806,13 +11768,13 @@ ecs_entity_t ecs_set_rate( * source, including another system. If the provided entity is not a tick source * the system will not be ran. * - * To disassociate a tick source from a system, use 0 for the tick_source + * To disassociate a tick source from a system, use 0 for the tick_source * parameter. * * @param world The world. * @param system The system to associate with the timer. * @param tick_source The tick source to associate with the system. - */ + */ FLECS_API void ecs_set_tick_source( ecs_world_t *world, @@ -10824,6 +11786,14 @@ void ecs_set_tick_source( //// Module //////////////////////////////////////////////////////////////////////////////// +/** Timer module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsTimer) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsTimerImport( ecs_world_t *world); @@ -10853,19 +11823,19 @@ void FlecsTimerImport( * systems. When ran, a pipeline will query for all systems that have the tags * that belong to a pipeline, and run them. * - * The module defines a number of builtin tags (EcsPreUpdate, EcsOnUpdate, - * EcsPostUpdate etc.) that are registered with the builtin pipeline. The - * builtin pipeline is ran by default when calling ecs_progress(). An - * application can set a custom pipeline with the ecs_set_pipeline function. + * The module defines a number of builtin tags (EcsPreUpdate, EcsOnUpdate, + * EcsPostUpdate etc.) that are registered with the builtin pipeline. The + * builtin pipeline is ran by default when calling ecs_progress(). An + * application can set a custom pipeline with the ecs_set_pipeline() function. */ #ifdef FLECS_PIPELINE /** * @defgroup c_addons_pipeline Pipeline - * @brief Pipelines order and schedule systems for execution. - * - * \ingroup c_addons + * @ingroup c_addons + * Pipelines order and schedule systems for execution. + * * @{ */ @@ -10890,6 +11860,13 @@ extern "C" { #ifndef FLECS_LEGACY +/** Convenience macro to create a predeclared pipeline. + * Usage: + * @code + * ECS_ENTITY_DECLARE(MyPipeline); + * ECS_PIPELINE_DEFINE(world, MyPipeline, Update || Physics || Render) + * @endcode + */ #define ECS_PIPELINE_DEFINE(world, id_, ...) \ { \ ecs_pipeline_desc_t desc = {0}; \ @@ -10897,33 +11874,68 @@ extern "C" { edesc.id = id_;\ edesc.name = #id_;\ desc.entity = ecs_entity_init(world, &edesc);\ - desc.query.filter.expr = #__VA_ARGS__; \ + desc.query.expr = #__VA_ARGS__; \ id_ = ecs_pipeline_init(world, &desc); \ ecs_id(id_) = id_;\ } \ - ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, "failed to create pipeline"); +/** Convenience macro to create a pipeline. + * Usage: + * @code + * ECS_PIPELINE(world, MyPipeline, Update || Physics || Render) + * @endcode + * + */ #define ECS_PIPELINE(world, id, ...) \ ecs_entity_t id = 0, ecs_id(id) = 0; ECS_PIPELINE_DEFINE(world, id, __VA_ARGS__);\ (void)id;\ (void)ecs_id(id); +/** Convenience macro to create a pipeline. + * See ecs_pipeline_init(). + */ #define ecs_pipeline(world, ...)\ ecs_pipeline_init(world, &(ecs_pipeline_desc_t) __VA_ARGS__ ) #endif -/* Pipeline descriptor (used with ecs_pipeline_init) */ +/** Pipeline descriptor, used with ecs_pipeline_init(). */ typedef struct ecs_pipeline_desc_t { - /* Existing entity to associate with pipeline (optional) */ + /** Existing entity to associate with pipeline (optional). */ ecs_entity_t entity; - - /* Query descriptor. The first term of the query must match the EcsSystem - * component. */ + + /** The pipeline query. + * Pipelines are queries that are matched with system entities. Pipeline + * queries are the same as regular queries, which means the same query rules + * apply. A common mistake is to try a pipeline that matches systems in a + * list of phases by specifying all the phases, like: + * OnUpdate, OnPhysics, OnRender + * + * That however creates a query that matches entities with OnUpdate _and_ + * OnPhysics _and_ OnRender tags, which is likely undesired. Instead, a + * query could use the or operator match a system that has one of the + * specified phases: + * OnUpdate || OnPhysics || OnRender + * + * This will return the correct set of systems, but they likely won't be in + * the correct order. To make sure systems are returned in the correct order + * two query ordering features can be used: + * - group_by + * - order_by + * + * Take a look at the system manual for a more detailed explanation of + * how query features can be applied to pipelines, and how the builtin + * pipeline query works. + */ ecs_query_desc_t query; } ecs_pipeline_desc_t; /** Create a custom pipeline. + * + * @param world The world. + * @param desc The pipeline descriptor. + * @return The pipeline, 0 if failed. */ FLECS_API ecs_entity_t ecs_pipeline_init( @@ -10931,7 +11943,7 @@ ecs_entity_t ecs_pipeline_init( const ecs_pipeline_desc_t *desc); /** Set a custom pipeline. - * This operation sets the pipeline to run when ecs_progress is invoked. + * This operation sets the pipeline to run when ecs_progress() is invoked. * * @param world The world. * @param pipeline The pipeline to set. @@ -10939,7 +11951,7 @@ ecs_entity_t ecs_pipeline_init( FLECS_API void ecs_set_pipeline( ecs_world_t *world, - ecs_entity_t pipeline); + ecs_entity_t pipeline); /** Get the current pipeline. * This operation gets the current pipeline. @@ -10949,7 +11961,7 @@ void ecs_set_pipeline( */ FLECS_API ecs_entity_t ecs_get_pipeline( - const ecs_world_t *world); + const ecs_world_t *world); /** Progress a world. * This operation progresses the world by running all systems that are both @@ -10960,19 +11972,19 @@ ecs_entity_t ecs_get_pipeline( * update entity values proportional to the elapsed time since their last * invocation. * - * When an application passes 0 to delta_time, ecs_progress will automatically + * When an application passes 0 to delta_time, ecs_progress() will automatically * measure the time passed since the last frame. If an application does not uses * time management, it should pass a non-zero value for delta_time (1.0 is * recommended). That way, no time will be wasted measuring the time. * * @param world The world to progress. * @param delta_time The time passed since the last frame. - * @return false if ecs_quit has been called, true otherwise. + * @return false if ecs_quit() has been called, true otherwise. */ FLECS_API bool ecs_progress( ecs_world_t *world, - ecs_ftime_t delta_time); + ecs_ftime_t delta_time); /** Set time scale. * Increase or decrease simulation speed by the provided multiplier. @@ -10980,7 +11992,7 @@ bool ecs_progress( * @param world The world. * @param scale The scale to apply (default = 1). */ -FLECS_API +FLECS_API void ecs_set_time_scale( ecs_world_t *world, ecs_ftime_t scale); @@ -11000,16 +12012,17 @@ void ecs_reset_clock( * pipeline manages staging and, if necessary, synchronization between threads. * * If 0 is provided for the pipeline id, the default pipeline will be ran (this - * is either the builtin pipeline or the pipeline set with set_pipeline()). + * is either the builtin pipeline or the pipeline set with set_pipeline()). * * When using progress() this operation will be invoked automatically for the - * default pipeline (either the builtin pipeline or the pipeline set with + * default pipeline (either the builtin pipeline or the pipeline set with * set_pipeline()). An application may run additional pipelines. * * @param world The world. * @param pipeline The pipeline to run. + * @param delta_time The delta_time to pass to systems. */ -FLECS_API +FLECS_API void ecs_run_pipeline( ecs_world_t *world, ecs_entity_t pipeline, @@ -11024,33 +12037,44 @@ void ecs_run_pipeline( * Setting this value to a value higher than 1 will start as many threads and * will cause systems to evenly distribute matched entities across threads. The * operation may be called multiple times to reconfigure the number of threads - * used, but never while running a system / pipeline. - * Calling ecs_set_threads will also end the use of task threads setup with - * ecs_set_task_threads and vice-versa */ + * used, but never while running a system / pipeline. + * Calling ecs_set_threads() will also end the use of task threads setup with + * ecs_set_task_threads() and vice-versa. + * + * @param world The world. + * @param threads The number of threads to create. + */ FLECS_API void ecs_set_threads( ecs_world_t *world, int32_t threads); /** Set number of worker task threads. - * ecs_set_task_threads is similar to ecs_set_threads, except threads are treated - * as short-lived tasks and will be created and joined around each update of the world. + * ecs_set_task_threads() is similar to ecs_set_threads(), except threads are treated + * as short-lived tasks and will be created and joined around each update of the world. * Creation and joining of these tasks will use the os_api_t tasks APIs rather than the * the standard thread API functions, although they may be the same if desired. * This function is useful for multithreading world updates using an external * asynchronous job system rather than long running threads by providing the APIs - * to create tasks for your job system and then wait on their conclusion. + * to create tasks for your job system and then wait on their conclusion. * The operation may be called multiple times to reconfigure the number of task threads - * used, but never while running a system / pipeline. - * Calling ecs_set_task_threads will also end the use of threads setup with - * ecs_set_threads and vice-versa */ - + * used, but never while running a system / pipeline. + * Calling ecs_set_task_threads() will also end the use of threads setup with + * ecs_set_threads() and vice-versa + * + * @param world The world. + * @param task_threads The number of task threads to create. + */ FLECS_API void ecs_set_task_threads( ecs_world_t *world, int32_t task_threads); -/** Returns true if task thread use have been requested. */ +/** Returns true if task thread use have been requested. + * + * @param world The world. + * @result Whether the world is using task threads. + */ FLECS_API bool ecs_using_task_threads( ecs_world_t *world); @@ -11059,6 +12083,14 @@ bool ecs_using_task_threads( //// Module //////////////////////////////////////////////////////////////////////////////// +/** Pipeline module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsPipeline) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsPipelineImport( ecs_world_t *world); @@ -11092,9 +12124,9 @@ void FlecsPipelineImport( /** * @defgroup c_addons_system System - * @brief Systems are a query + function that can be ran manually or by a pipeline. - * - * \ingroup c_addons + * @ingroup c_addons + * Systems are a query + function that can be ran manually or by a pipeline. + * * @{ */ @@ -11115,7 +12147,7 @@ typedef struct EcsTickSource { ecs_ftime_t time_elapsed; /**< Time elapsed since last tick */ } EcsTickSource; -/** Use with ecs_system_init */ +/** Use with ecs_system_init() to create or update a system. */ typedef struct ecs_system_desc_t { int32_t _canary; @@ -11125,37 +12157,42 @@ typedef struct ecs_system_desc_t { /** System query parameters */ ecs_query_desc_t query; - /** Callback that is invoked when a system is ran. - * When left to NULL, the default system runner is used, which calls the - * "callback" action for each result returned from the system's query. - * - * It should not be assumed that the input iterator can always be iterated - * with ecs_query_next. When a system is multithreaded and/or paged, the - * iterator can be either a worker or paged iterator. Future use cases may - * introduce additional inputs for a system, such as rules and filters. The - * correct function to use for iteration is ecs_iter_next. - * - * An implementation can test whether the iterator is a query iterator by - * testing whether the it->next value is equal to ecs_query_next. */ - ecs_run_action_t run; - /** Callback that is ran for each result returned by the system's query. This * means that this callback can be invoked multiple times per system per * frame, typically once for each matching table. */ ecs_iter_action_t callback; + /** Callback that is invoked when a system is ran. + * When left to NULL, the default system runner is used, which calls the + * "callback" action for each result returned from the system's query. + * + * It should not be assumed that the input iterator can always be iterated + * with ecs_query_next(). When a system is multithreaded and/or paged, the + * iterator can be either a worker or paged iterator. The correct function + * to use for iteration is ecs_iter_next(). + * + * An implementation can test whether the iterator is a query iterator by + * testing whether the it->next value is equal to ecs_query_next(). */ + ecs_run_action_t run; + /** Context to be passed to callback (as ecs_iter_t::param) */ void *ctx; - /** Binding context, for when system is implemented in other language */ - void *binding_ctx; - - /** Functions that are invoked during system cleanup to free context data. - * When set, functions are called unconditionally, even when the ctx - * pointers are NULL. */ + /** Callback to free ctx. */ ecs_ctx_free_t ctx_free; - ecs_ctx_free_t binding_ctx_free; + /** Context associated with callback (for language bindings). */ + void *callback_ctx; + + /** Callback to free callback ctx. */ + ecs_ctx_free_t callback_ctx_free; + + /** Context associated with run (for language bindings). */ + void *run_ctx; + + /** Callback to free run ctx. */ + ecs_ctx_free_t run_ctx_free; + /** Interval in seconds at which the system should run */ ecs_ftime_t interval; @@ -11170,7 +12207,7 @@ typedef struct ecs_system_desc_t { /** If true, system will have access to the actual world. Cannot be true at the * same time as multi_threaded. */ - bool no_readonly; + bool immediate; } ecs_system_desc_t; /** Create a system */ @@ -11179,35 +12216,119 @@ ecs_entity_t ecs_system_init( ecs_world_t *world, const ecs_system_desc_t *desc); +/** System type, get with ecs_system_get() */ +typedef struct ecs_system_t { + ecs_header_t hdr; + + /** See ecs_system_desc_t */ + ecs_run_action_t run; + + /** See ecs_system_desc_t */ + ecs_iter_action_t action; + + /** System query */ + ecs_query_t *query; + + /** Entity associated with query */ + ecs_entity_t query_entity; + + /** Tick source associated with system */ + ecs_entity_t tick_source; + + /** Is system multithreaded */ + bool multi_threaded; + + /** Is system ran in immediate mode */ + bool immediate; + + /** Cached system name (for perf tracing) */ + const char *name; + + /** Userdata for system */ + void *ctx; + + /** Callback language binding context */ + void *callback_ctx; + + /** Run language binding context */ + void *run_ctx; + + /** Callback to free ctx. */ + ecs_ctx_free_t ctx_free; + + /** Callback to free callback ctx. */ + ecs_ctx_free_t callback_ctx_free; + + /** Callback to free run ctx. */ + ecs_ctx_free_t run_ctx_free; + + /** Time spent on running system */ + ecs_ftime_t time_spent; + + /** Time passed since last invocation */ + ecs_ftime_t time_passed; + + /** Last frame for which the system was considered */ + int64_t last_frame; + + /* Mixins */ + ecs_world_t *world; + ecs_entity_t entity; + flecs_poly_dtor_t dtor; +} ecs_system_t; + +/** Get system object. + * Returns the system object. Can be used to access various information about + * the system, like the query and context. + * + * @param world The world. + * @param system The system. + * @return The system object. + */ +FLECS_API +const ecs_system_t* ecs_system_get( + const ecs_world_t *world, + ecs_entity_t system); + #ifndef FLECS_LEGACY /** Forward declare a system. */ #define ECS_SYSTEM_DECLARE(id) ecs_entity_t ecs_id(id) /** Define a forward declared system. - * + * * Example: - * ECS_SYSTEM_DEFINE(world, Move, EcsOnUpdate, Position, Velocity); + * + * @code + * ECS_SYSTEM_DEFINE(world, Move, EcsOnUpdate, Position, Velocity); + * @endcode */ #define ECS_SYSTEM_DEFINE(world, id_, phase, ...) \ { \ ecs_system_desc_t desc = {0}; \ ecs_entity_desc_t edesc = {0}; \ + ecs_id_t add_ids[3] = {\ + ((phase) ? ecs_pair(EcsDependsOn, (phase)) : 0), \ + (phase), \ + 0 \ + };\ edesc.id = ecs_id(id_);\ edesc.name = #id_;\ - edesc.add[0] = ((phase) ? ecs_pair(EcsDependsOn, (phase)) : 0); \ - edesc.add[1] = (phase); \ + edesc.add = add_ids;\ desc.entity = ecs_entity_init(world, &edesc);\ - desc.query.filter.expr = #__VA_ARGS__; \ + desc.query.expr = #__VA_ARGS__; \ desc.callback = id_; \ ecs_id(id_) = ecs_system_init(world, &desc); \ } \ - ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, NULL) + ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, "failed to create system %s", #id_) /** Declare & define a system. - * + * * Example: - * ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); + * + * @code + * ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); + * @endcode */ #define ECS_SYSTEM(world, id, phase, ...) \ ecs_entity_t ecs_id(id) = 0; ECS_SYSTEM_DEFINE(world, id, phase, __VA_ARGS__);\ @@ -11215,20 +12336,23 @@ ecs_entity_t ecs_system_init( (void)ecs_id(id);\ (void)id -/** Shorthand for creating a system with ecs_system_init. +/** Shorthand for creating a system with ecs_system_init(). * * Example: - * ecs_system(world, { - * .entity = ecs_entity(world, { - * .name = "MyEntity", - * .add = { ecs_dependson(EcsOnUpdate) } - * }), - * .query.filter.terms = { - * { ecs_id(Position) }, - * { ecs_id(Velocity) } - * }, - * .callback = Move - * }); + * + * @code + * ecs_system(world, { + * .entity = ecs_entity(world, { + * .name = "MyEntity", + * .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) + * }), + * .query.terms = { + * { ecs_id(Position) }, + * { ecs_id(Velocity) } + * }, + * .callback = Move + * }); + * @endcode */ #define ecs_system(world, ...)\ ecs_system_init(world, &(ecs_system_desc_t) __VA_ARGS__ ) @@ -11240,7 +12364,7 @@ ecs_entity_t ecs_system_init( * invoke logic on a set of entities, as manual systems are only matched to * tables at creation time or after creation time, when a new table is created. * - * Manual systems are useful to evaluate lists of prematched entities at + * Manual systems are useful to evaluate lists of pre-matched entities at * application defined times. Because none of the matching logic is evaluated * before the system is invoked, manual systems are much more efficient than * manually obtaining a list of entities and retrieving their components. @@ -11269,8 +12393,8 @@ ecs_entity_t ecs_run( ecs_ftime_t delta_time, void *param); -/** Same as ecs_run, but subdivides entities across number of provided stages. - * +/** Same as ecs_run(), but subdivides entities across number of provided stages. + * * @param world The world. * @param system The system to run. * @param stage_current The id of the current stage. @@ -11288,77 +12412,15 @@ ecs_entity_t ecs_run_worker( ecs_ftime_t delta_time, void *param); -/** Run system with offset/limit and type filter. - * This operation is the same as ecs_run, but filters the entities that will be - * iterated by the system. - * - * Entities can be filtered in two ways. Offset and limit control the range of - * entities that is iterated over. The range is applied to all entities matched - * with the system, thus may cover multiple archetypes. - * - * The type filter controls which entity types the system will evaluate. Only - * types that contain all components in the type filter will be iterated over. A - * type filter is only evaluated once per table, which makes filtering cheap if - * the number of entities is large and the number of tables is small, but not as - * cheap as filtering in the system signature. +/** System module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsSystem) + * @endcode * * @param world The world. - * @param system The system to invoke. - * @param delta_time The time passed since the last system invocation. - * @param param A user-defined parameter to pass to the system. - * @return handle to last evaluated entity if system was interrupted. - */ -FLECS_API -ecs_entity_t ecs_run_w_filter( - ecs_world_t *world, - ecs_entity_t system, - ecs_ftime_t delta_time, - int32_t offset, - int32_t limit, - void *param); - -/** Get the query object for a system. - * Systems use queries under the hood. This enables an application to get access - * to the underlying query object of a system. This can be useful when, for - * example, an application needs to enable sorting for a system. - * - * @param world The world. - * @param system The system from which to obtain the query. - * @return The query. - */ -FLECS_API -ecs_query_t* ecs_system_get_query( - const ecs_world_t *world, - ecs_entity_t system); - -/** Get system context. - * This operation returns the context pointer set for the system. If - * the provided entity is not a system, the function will return NULL. - * - * @param world The world. - * @param system The system from which to obtain the context. - * @return The context. - */ -FLECS_API -void* ecs_system_get_ctx( - const ecs_world_t *world, - ecs_entity_t system); - -/** Get system binding context. - * The binding context is a context typically used to attach any language - * binding specific data that is needed when invoking a callback that is - * implemented in another language. - * - * @param world The world. - * @param system The system from which to obtain the context. - * @return The context. */ FLECS_API -void* ecs_system_get_binding_ctx( - const ecs_world_t *world, - ecs_entity_t system); - -FLECS_API void FlecsSystemImport( ecs_world_t *world); @@ -11382,23 +12444,37 @@ void FlecsSystemImport( * @file addons/stats.h * @brief Statistics addon. * - * The statistics addon enables an application to obtain detailed metrics about - * the storage, systems and operations of a world. + * The stats addon tracks high resolution statistics for the world, systems and + * pipelines. The addon can be used as an API where an application calls + * functions to obtain statistics directly and as a module where statistics are + * automatically tracked. The latter is required for statistics tracking in the + * explorer. + * + * When the addon is imported as module, statistics are tracked for each frame, + * second, minute, hour, day and week with 60 datapoints per tier. */ #ifdef FLECS_STATS /** * @defgroup c_addons_stats Stats - * @brief Collection of statistics for world, queries, systems and pipelines. - * - * \ingroup c_addons + * @ingroup c_addons + * Collection of statistics for world, queries, systems and pipelines. + * * @{ */ #ifndef FLECS_STATS_H #define FLECS_STATS_H +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif + +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE +#endif + #ifdef __cplusplus extern "C" { #endif @@ -11465,7 +12541,7 @@ typedef struct ecs_world_stats_t { ecs_metric_t delete_count; ecs_metric_t clear_count; ecs_metric_t set_count; - ecs_metric_t get_mut_count; + ecs_metric_t ensure_count; ecs_metric_t modified_count; ecs_metric_t other_count; ecs_metric_t discard_count; @@ -11528,15 +12604,15 @@ typedef struct ecs_world_stats_t { int64_t last_; - /** Current position in ringbuffer */ + /** Current position in ring buffer */ int32_t t; } ecs_world_stats_t; -/** Statistics for a single query (use ecs_query_stats_get) */ +/** Statistics for a single query (use ecs_query_cache_stats_get) */ typedef struct ecs_query_stats_t { int64_t first_; - ecs_metric_t matched_table_count; /**< Matched non-empty tables */ - ecs_metric_t matched_empty_table_count; /**< Matched empty tables */ + ecs_metric_t result_count; /**< Number of query results */ + ecs_metric_t matched_table_count; /**< Number of matched tables */ ecs_metric_t matched_entity_count; /**< Number of matched entities */ int64_t last_; @@ -11544,11 +12620,10 @@ typedef struct ecs_query_stats_t { int32_t t; } ecs_query_stats_t; -/** Statistics for a single system (use ecs_system_stats_get) */ +/** Statistics for a single system (use ecs_system_stats_get()) */ typedef struct ecs_system_stats_t { int64_t first_; ecs_metric_t time_spent; /**< Time spent processing a system */ - ecs_metric_t invoke_count; /**< Number of times system is invoked */ int64_t last_; bool task; /**< Is system a task */ @@ -11565,7 +12640,7 @@ typedef struct ecs_sync_stats_t { int32_t system_count; bool multi_threaded; - bool no_readonly; + bool immediate; } ecs_sync_stats_t; /** Statistics for all systems in a pipeline. */ @@ -11576,15 +12651,11 @@ typedef struct ecs_pipeline_stats_t { /** Vector with system ids of all systems in the pipeline. The systems are * stored in the order they are executed. Merges are represented by a 0. */ ecs_vec_t systems; - + /** Vector with sync point stats */ ecs_vec_t sync_points; - /** Map with system statistics. For each system in the systems vector, an - * entry in the map exists of type ecs_system_stats_t. */ - ecs_map_t system_stats; - - /** Current position in ringbuffer */ + /** Current position in ring buffer */ int32_t t; int32_t system_count; /**< Number of systems in pipeline */ @@ -11597,13 +12668,13 @@ typedef struct ecs_pipeline_stats_t { * @param world The world. * @param stats Out parameter for statistics. */ -FLECS_API +FLECS_API void ecs_world_stats_get( const ecs_world_t *world, ecs_world_stats_t *stats); /** Reduce source measurement window into single destination measurement. */ -FLECS_API +FLECS_API void ecs_world_stats_reduce( ecs_world_stats_t *dst, const ecs_world_stats_t *src); @@ -11626,7 +12697,7 @@ void ecs_world_stats_copy_last( ecs_world_stats_t *dst, const ecs_world_stats_t *src); -FLECS_API +FLECS_API void ecs_world_stats_log( const ecs_world_t *world, const ecs_world_stats_t *stats); @@ -11638,7 +12709,7 @@ void ecs_world_stats_log( * @param query The query. * @param stats Out parameter for statistics. */ -FLECS_API +FLECS_API void ecs_query_stats_get( const ecs_world_t *world, const ecs_query_t *query, @@ -11646,29 +12717,28 @@ void ecs_query_stats_get( /** Reduce source measurement window into single destination measurement. */ FLECS_API -void ecs_query_stats_reduce( +void ecs_query_cache_stats_reduce( ecs_query_stats_t *dst, const ecs_query_stats_t *src); /** Reduce last measurement into previous measurement, restore old value. */ FLECS_API -void ecs_query_stats_reduce_last( +void ecs_query_cache_stats_reduce_last( ecs_query_stats_t *stats, const ecs_query_stats_t *old, int32_t count); /** Repeat last measurement. */ FLECS_API -void ecs_query_stats_repeat_last( +void ecs_query_cache_stats_repeat_last( ecs_query_stats_t *stats); /** Copy last measurement from source to destination. */ FLECS_API -void ecs_query_stats_copy_last( +void ecs_query_cache_stats_copy_last( ecs_query_stats_t *dst, const ecs_query_stats_t *src); -#ifdef FLECS_SYSTEM /** Get system statistics. * Obtain statistics for the provided system. * @@ -11677,14 +12747,14 @@ void ecs_query_stats_copy_last( * @param stats Out parameter for statistics. * @return true if success, false if not a system. */ -FLECS_API +FLECS_API bool ecs_system_stats_get( const ecs_world_t *world, ecs_entity_t system, ecs_system_stats_t *stats); /** Reduce source measurement window into single destination measurement */ -FLECS_API +FLECS_API void ecs_system_stats_reduce( ecs_system_stats_t *dst, const ecs_system_stats_t *src); @@ -11706,9 +12776,7 @@ FLECS_API void ecs_system_stats_copy_last( ecs_system_stats_t *dst, const ecs_system_stats_t *src); -#endif -#ifdef FLECS_PIPELINE /** Get pipeline statistics. * Obtain statistics for the provided pipeline. * @@ -11717,14 +12785,14 @@ void ecs_system_stats_copy_last( * @param stats Out parameter for statistics. * @return true if success, false if not a pipeline. */ -FLECS_API +FLECS_API bool ecs_pipeline_stats_get( ecs_world_t *world, ecs_entity_t pipeline, ecs_pipeline_stats_t *stats); /** Free pipeline stats. - * + * * @param stats The stats to free. */ FLECS_API @@ -11732,7 +12800,7 @@ void ecs_pipeline_stats_fini( ecs_pipeline_stats_t *stats); /** Reduce source measurement window into single destination measurement */ -FLECS_API +FLECS_API void ecs_pipeline_stats_reduce( ecs_pipeline_stats_t *dst, const ecs_pipeline_stats_t *src); @@ -11752,7 +12820,7 @@ void ecs_pipeline_stats_repeat_last( /** Copy last measurement to destination. * This operation copies the last measurement into the destination. It does not * modify the cursor. - * + * * @param dst The metrics. * @param src The metrics to copy. */ @@ -11761,10 +12829,8 @@ void ecs_pipeline_stats_copy_last( ecs_pipeline_stats_t *dst, const ecs_pipeline_stats_t *src); -#endif - /** Reduce all measurements from a window into a single measurement. */ -FLECS_API +FLECS_API void ecs_metric_reduce( ecs_metric_t *dst, const ecs_metric_t *src, @@ -11785,6 +12851,77 @@ void ecs_metric_copy( int32_t dst, int32_t src); +FLECS_API extern ECS_COMPONENT_DECLARE(FlecsStats); /**< Flecs stats module. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldStats); /**< Component id for EcsWorldStats. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldSummary); /**< Component id for EcsWorldSummary. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsSystemStats); /**< Component id for EcsSystemStats. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsPipelineStats); /**< Component id for EcsPipelineStats. */ + +FLECS_API extern ecs_entity_t EcsPeriod1s; /**< Tag used for metrics collected in last second. */ +FLECS_API extern ecs_entity_t EcsPeriod1m; /**< Tag used for metrics collected in last minute. */ +FLECS_API extern ecs_entity_t EcsPeriod1h; /**< Tag used for metrics collected in last hour. */ +FLECS_API extern ecs_entity_t EcsPeriod1d; /**< Tag used for metrics collected in last day. */ +FLECS_API extern ecs_entity_t EcsPeriod1w; /**< Tag used for metrics collected in last week. */ + +/** Common data for statistics. */ +typedef struct { + ecs_ftime_t elapsed; + int32_t reduce_count; +} EcsStatsHeader; + +/** Component that stores world statistics. */ +typedef struct { + EcsStatsHeader hdr; + ecs_world_stats_t stats; +} EcsWorldStats; + +/** Component that stores system statistics. */ +typedef struct { + EcsStatsHeader hdr; + ecs_map_t stats; +} EcsSystemStats; + +/** Component that stores pipeline statistics. */ +typedef struct { + EcsStatsHeader hdr; + ecs_map_t stats; +} EcsPipelineStats; + +/** Component that stores a summary of world statistics. */ +typedef struct { + /* Time */ + double target_fps; /**< Target FPS */ + double time_scale; /**< Simulation time scale */ + + /* Total time */ + double frame_time_total; /**< Total time spent processing a frame */ + double system_time_total; /**< Total time spent in systems */ + double merge_time_total; /**< Total time spent in merges */ + + /* Last frame time */ + double frame_time_last; /**< Time spent processing a frame */ + double system_time_last; /**< Time spent in systems */ + double merge_time_last; /**< Time spent in merges */ + + int64_t frame_count; /**< Number of frames processed */ + int64_t command_count; /**< Number of commands processed */ + + /* Build info */ + ecs_build_info_t build_info; /**< Build info */ +} EcsWorldSummary; + +/** Stats module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsStats) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsStatsImport( + ecs_world_t *world); + #ifdef __cplusplus } #endif @@ -11813,9 +12950,9 @@ void ecs_metric_copy( /** * @defgroup c_addons_metrics Metrics - * @brief Collect user-defined metrics from ECS data. - * - * \ingroup c_addons + * @ingroup c_addons + * Collect user-defined metrics from ECS data. + * * @{ */ @@ -11838,52 +12975,56 @@ void ecs_metric_copy( extern "C" { #endif +/** Flecs metrics module. */ FLECS_API extern ECS_COMPONENT_DECLARE(FlecsMetrics); -/** Tag added to metrics, and used as first element of metric kind pair */ +/** Tag added to metrics, and used as first element of metric kind pair. */ FLECS_API extern ECS_TAG_DECLARE(EcsMetric); -/** Metric that has monotonically increasing value */ +/** Metric that has monotonically increasing value. */ FLECS_API extern ECS_TAG_DECLARE(EcsCounter); -/** Counter metric that is auto-incremented by source value */ +/** Counter metric that is auto-incremented by source value. */ FLECS_API extern ECS_TAG_DECLARE(EcsCounterIncrement); -/** Counter metric that counts the number of entities with an id */ +/** Counter metric that counts the number of entities with an id. */ FLECS_API extern ECS_TAG_DECLARE(EcsCounterId); -/** Metric that represents current value */ +/** Metric that represents current value. */ FLECS_API extern ECS_TAG_DECLARE(EcsGauge); -/** Tag added to metric instances */ +/** Tag added to metric instances. */ FLECS_API extern ECS_TAG_DECLARE(EcsMetricInstance); -/** Component with metric instance value */ +/** Component with metric instance value. */ FLECS_API extern ECS_COMPONENT_DECLARE(EcsMetricValue); -/** Component with entity source of metric instance */ +/** Component with entity source of metric instance. */ FLECS_API extern ECS_COMPONENT_DECLARE(EcsMetricSource); +/** Component that stores metric value. */ typedef struct EcsMetricValue { double value; } EcsMetricValue; +/** Component that stores metric source. */ typedef struct EcsMetricSource { ecs_entity_t entity; } EcsMetricSource; +/** Used with ecs_metric_init to create metric. */ typedef struct ecs_metric_desc_t { int32_t _canary; /** Entity associated with metric */ ecs_entity_t entity; - + /** Entity associated with member that stores metric value. Must not be set * at the same time as id. Cannot be combined with EcsCounterId. */ ecs_entity_t member; /* Member dot expression. Can be used instead of member and supports nested - * members. Must be set together with id and should not be set at the same + * members. Must be set together with id and should not be set at the same * time as member. */ const char *dotmember; @@ -11892,7 +13033,7 @@ typedef struct ecs_metric_desc_t { ecs_id_t id; /** If id is a (R, *) wildcard and relationship R has the OneOf property, - * setting this value to true will track individual targets. + * setting this value to true will track individual targets. * If the kind is EcsCountId and the id is a (R, *) wildcard, this value * will create a metric per target. */ bool targets; @@ -11909,36 +13050,36 @@ typedef struct ecs_metric_desc_t { * properties in the ECS storage. Metrics provide a single unified interface to * discovering and reading these values, which can be useful for monitoring * utilities, or for debugging. - * + * * Examples of properties that can be measured by metrics are: * - Component member values * - How long an entity has had a specific component * - How long an entity has had a specific target for a relationship * - How many entities have a specific component - * + * * Metrics can either be created as a "gauge" or "counter". A gauge is a metric * that represents the value of something at a specific point in time, for * example "velocity". A counter metric represents a value that is monotonically * increasing, for example "miles driven". - * + * * There are three different kinds of counter metric kinds: * - EcsCounter * When combined with a member, this will store the actual value of the member * in the metric. This is useful for values that are already counters, such as * a MilesDriven component. * This kind creates a metric per entity that has the member/id. - * + * * - EcsCounterIncrement * When combined with a member, this will increment the value of the metric by * the value of the member * delta_time. This is useful for values that are * not counters, such as a Velocity component. * This kind creates a metric per entity that has the member. - * + * * - EcsCounterId - * This metric kind will count the number of entities with a specific + * This metric kind will count the number of entities with a specific * (component) id. This kind creates a single metric instance for regular ids, * and a metric instance per target for wildcard ids when targets is set. - * + * * @param world The world. * @param desc Metric description. * @return The metric entity. @@ -11948,18 +13089,28 @@ ecs_entity_t ecs_metric_init( ecs_world_t *world, const ecs_metric_desc_t *desc); -/** Shorthand for creating a metric with ecs_metric_init. +/** Shorthand for creating a metric with ecs_metric_init(). * * Example: - * ecs_metric(world, { - * .member = ecs_lookup_fullpath(world, "Position.x") - * .kind = EcsGauge - * }); + * + * @code + * ecs_metric(world, { + * .member = ecs_lookup(world, "Position.x") + * .kind = EcsGauge + * }); + * @endcode */ #define ecs_metric(world, ...)\ ecs_metric_init(world, &(ecs_metric_desc_t) __VA_ARGS__ ) -/* Module import */ +/** Metrics module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsMetrics) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsMetricsImport( ecs_world_t *world); @@ -11993,19 +13144,15 @@ void FlecsMetricsImport( /** * @defgroup c_addons_alerts Alerts - * @brief Create alerts from monitoring queries. - * - * \ingroup c_addons + * @ingroup c_addons + * Create alerts from monitoring queries. + * * @{ */ #ifndef FLECS_ALERTS_H #define FLECS_ALERTS_H -#ifndef FLECS_RULES -#define FLECS_RULES -#endif - #ifndef FLECS_PIPELINE #define FLECS_PIPELINE #endif @@ -12016,36 +13163,41 @@ extern "C" { #define ECS_ALERT_MAX_SEVERITY_FILTERS (4) -/* Module id */ +/** Module id. */ FLECS_API extern ECS_COMPONENT_DECLARE(FlecsAlerts); /* Module components */ -/** Tag added to alert, and used as first element of alert severity pair */ -FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlert); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertInstance); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertsActive); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertTimeout); +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlert); /**< Component added to alert, and used as first element of alert severity pair. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertInstance); /**< Component added to alert instance. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertsActive); /**< Component added to alert source which tracks how many active alerts there are. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertTimeout); /**< Component added to alert which tracks how long an alert has been inactive. */ /* Alert severity tags */ -FLECS_API extern ECS_TAG_DECLARE(EcsAlertInfo); -FLECS_API extern ECS_TAG_DECLARE(EcsAlertWarning); -FLECS_API extern ECS_TAG_DECLARE(EcsAlertError); -FLECS_API extern ECS_TAG_DECLARE(EcsAlertCritical); +FLECS_API extern ECS_TAG_DECLARE(EcsAlertInfo); /**< Info alert severity. */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertWarning); /**< Warning alert severity. */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertError); /**< Error alert severity. */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertCritical); /**< Critical alert severity. */ -/** Alert information. Added to each alert instance */ +/** Component added to alert instance. */ typedef struct EcsAlertInstance { - char *message; + char *message; /**< Generated alert message */ } EcsAlertInstance; /** Map with active alerts for entity. */ typedef struct EcsAlertsActive { - int32_t info_count; - int32_t warning_count; - int32_t error_count; + int32_t info_count; /**< Number of alerts for source with info severity */ + int32_t warning_count; /**< Number of alerts for source with warning severity */ + int32_t error_count; /**< Number of alerts for source with error severity */ ecs_map_t alerts; } EcsAlertsActive; +/** Alert severity filter. + * A severity filter can adjust the severity of an alert based on whether an + * entity in the alert query has a specific component. For example, a filter + * could check if an entity has the "Production" tag, and increase the default + * severity of an alert from Warning to Error. + */ typedef struct ecs_alert_severity_filter_t { ecs_entity_t severity; /* Severity kind */ ecs_id_t with; /* Component to match */ @@ -12054,7 +13206,8 @@ typedef struct ecs_alert_severity_filter_t { int32_t _var_index; /* Index of variable in filter (do not set) */ } ecs_alert_severity_filter_t; -typedef struct ecs_alert_desc_t { +/** Alert descriptor, used with ecs_alert_init(). */ +typedef struct ecs_alert_desc_t { int32_t _canary; /** Entity associated with alert */ @@ -12063,15 +13216,16 @@ typedef struct ecs_alert_desc_t { /** Alert query. An alert will be created for each entity that matches the * specified query. The query must have at least one term that uses the * $this variable (default). */ - ecs_filter_desc_t filter; + ecs_query_desc_t query; /** Template for alert message. This string is used to generate the alert * message and may refer to variables in the query result. The format for - * the template expressions is as specified by ecs_interpolate_string. - * + * the template expressions is as specified by ecs_script_string_interpolate(). + * * Examples: - * "$this has Position but not Velocity" - * "$this has a parent entity $parent without Position" + * + * "$this has Position but not Velocity" + * "$this has a parent entity $parent without Position" */ const char *message; @@ -12081,19 +13235,19 @@ typedef struct ecs_alert_desc_t { /** Description of alert. Will only be set if FLECS_DOC addon is enabled */ const char *brief; - /** Metric kind. Must be EcsAlertInfo, EcsAlertWarning, EcsAlertError or + /** Metric kind. Must be EcsAlertInfo, EcsAlertWarning, EcsAlertError or * EcsAlertCritical. Defaults to EcsAlertError. */ ecs_entity_t severity; /** Severity filters can be used to assign different severities to the same - * alert. This prevents having to create multiple alerts, and allows - * entities to transition between severities without resetting the + * alert. This prevents having to create multiple alerts, and allows + * entities to transition between severities without resetting the * alert duration (optional). */ ecs_alert_severity_filter_t severity_filters[ECS_ALERT_MAX_SEVERITY_FILTERS]; /** The retain period specifies how long an alert must be inactive before it * is cleared. This makes it easier to track noisy alerts. While an alert is - * inactive its duration won't increase. + * inactive its duration won't increase. * When the retain period is 0, the alert will clear immediately after it no * longer matches the alert query. */ ecs_ftime_t retain_period; @@ -12112,26 +13266,26 @@ typedef struct ecs_alert_desc_t { } ecs_alert_desc_t; /** Create a new alert. - * An alert is a query that is evaluated periodically and creates alert - * instances for each entity that matches the query. Alerts can be used to + * An alert is a query that is evaluated periodically and creates alert + * instances for each entity that matches the query. Alerts can be used to * automate detection of errors in an application. - * + * * Alerts are automatically cleared when a query is no longer true for an alert * instance. At most one alert instance will be created per matched entity. - * + * * Alert instances have three components: * - AlertInstance: contains the alert message for the instance * - MetricSource: contains the entity that triggered the alert * - MetricValue: contains how long the alert has been active - * + * * Alerts reuse components from the metrics addon so that alert instances can be * tracked and discovered as metrics. Just like metrics, alert instances are * created as children of the alert. - * + * * When an entity has active alerts, it will have the EcsAlertsActive component * which contains a map with active alerts for the entity. This component * will be automatically removed once all alerts are cleared for the entity. - * + * * @param world The world. * @param desc Alert description. * @return The alert entity. @@ -12141,6 +13295,9 @@ ecs_entity_t ecs_alert_init( ecs_world_t *world, const ecs_alert_desc_t *desc); +/** Create a new alert. + * @see ecs_alert_init() + */ #define ecs_alert(world, ...)\ ecs_alert_init(world, &(ecs_alert_desc_t)__VA_ARGS__) @@ -12149,7 +13306,7 @@ ecs_entity_t ecs_alert_init( * will return whether the specified alert is active for the entity. When no * alert is specified, the operation will return the total number of active * alerts for the entity. - * + * * @param world The world. * @param entity The entity. * @param alert The alert to test for (optional). @@ -12164,7 +13321,7 @@ int32_t ecs_get_alert_count( /** Return alert instance for specified alert. * This operation returns the alert instance for the specified alert. If the * alert is not active for the entity, the operation will return 0. - * + * * @param world The world. * @param entity The entity. * @param alert The alert to test for. @@ -12176,104 +13333,16 @@ ecs_entity_t ecs_get_alert( ecs_entity_t entity, ecs_entity_t alert); -/* Module import */ -FLECS_API -void FlecsAlertsImport( - ecs_world_t *world); - -#ifdef __cplusplus -} -#endif - -#endif - -/** @} */ - -#endif - -#endif - -#ifdef FLECS_MONITOR -#ifdef FLECS_NO_MONITOR -#error "FLECS_NO_MONITOR failed: MONITOR is required by other addons" -#endif -/** - * @file addons/monitor.h - * @brief Doc module. - * - * The monitor module automatically tracks statistics from the stats addon and - * stores them in components. - */ - -#ifdef FLECS_MONITOR - -/** - * @defgroup c_addons_monitor Monitor - * @brief The monitor addon periodically tracks statistics for the world and systems. +/** Alert module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsAlerts) + * @endcode * - * \ingroup c_addons - * @{ + * @param world The world. */ - -#ifndef FLECS_MONITOR_H -#define FLECS_MONITOR_H - -#ifndef FLECS_MODULE -#define FLECS_MODULE -#endif - -#ifndef FLECS_STATS -#define FLECS_STATS -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -FLECS_API extern ECS_COMPONENT_DECLARE(FlecsMonitor); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldStats); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldSummary); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsPipelineStats); - -FLECS_API extern ecs_entity_t EcsPeriod1s; -FLECS_API extern ecs_entity_t EcsPeriod1m; -FLECS_API extern ecs_entity_t EcsPeriod1h; -FLECS_API extern ecs_entity_t EcsPeriod1d; -FLECS_API extern ecs_entity_t EcsPeriod1w; - -typedef struct { - ecs_ftime_t elapsed; - int32_t reduce_count; -} EcsStatsHeader; - -typedef struct { - EcsStatsHeader hdr; - ecs_world_stats_t stats; -} EcsWorldStats; - -typedef struct { - EcsStatsHeader hdr; - ecs_pipeline_stats_t stats; -} EcsPipelineStats; - -typedef struct { - /* Target FPS */ - double target_fps; /**< Target FPS */ - - /* Total time */ - double frame_time_total; /**< Total time spent processing a frame */ - double system_time_total; /**< Total time spent in systems */ - double merge_time_total; /**< Total time spent in merges */ - - /* Last frame time */ - double frame_time_last; /**< Time spent processing a frame */ - double system_time_last; /**< Time spent in systems */ - double merge_time_last; /**< Time spent in merges */ -} EcsWorldSummary; - -/* Module import */ FLECS_API -void FlecsMonitorImport( +void FlecsAlertsImport( ecs_world_t *world); #ifdef __cplusplus @@ -12288,307 +13357,73 @@ void FlecsMonitorImport( #endif -#ifdef FLECS_COREDOC -#ifdef FLECS_NO_COREDOC -#error "FLECS_NO_COREDOC failed: COREDOC is required by other addons" +#ifdef FLECS_JSON +#ifdef FLECS_NO_JSON +#error "FLECS_NO_JSON failed: JSON is required by other addons" #endif /** - * @file addons/coredoc.h - * @brief Core doc module. + * @file addons/json.h + * @brief JSON parser addon. + * + * Parse expression strings into component values. Entity identifiers, + * enumerations and bitmasks are encoded as strings. * - * The core doc module imports documentation and reflection data for core - * components, tags and systems. + * See docs/FlecsRemoteApi.md for a description of the JSON format. */ -#ifdef FLECS_COREDOC - -#ifndef FLECS_DOC -#define FLECS_DOC -#endif +#ifdef FLECS_JSON #ifndef FLECS_META #define FLECS_META #endif -#ifndef FLECS_COREDOC_H -#define FLECS_COREDOC_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup c_addons_coredoc Coredoc - * @brief Module that adds documentation and reflection to core entities. - * - * \ingroup c_addons - * @{ - */ - -/* Module import */ - -FLECS_API -void FlecsCoreDocImport( - ecs_world_t *world); - -/* @} */ - -#ifdef __cplusplus -} -#endif - -#endif - +#ifndef FLECS_SCRIPT +#define FLECS_SCRIPT #endif -#endif +#ifndef FLECS_JSON_H +#define FLECS_JSON_H -#ifdef FLECS_DOC -#ifdef FLECS_NO_DOC -#error "FLECS_NO_DOC failed: DOC is required by other addons" -#endif /** - * @file addons/doc.h - * @brief Doc module. + * @defgroup c_addons_json Json + * @ingroup c_addons + * Functions for serializing to/from JSON. * - * The doc module allows for documenting entities (and thus components, systems) - * by adding brief and/or detailed descriptions as components. Documentation - * added with the doc module can be retrieved at runtime, and can be used by - * tooling such as UIs or documentation frameworks. + * @{ */ -#ifdef FLECS_DOC - -#ifndef FLECS_DOC_H -#define FLECS_DOC_H - -#ifndef FLECS_MODULE -#define FLECS_MODULE -#endif - #ifdef __cplusplus extern "C" { #endif -/** - * @defgroup c_addons_doc Doc - * @brief Utilities for documenting entities, components and systems. - * - * \ingroup c_addons - * @{ - */ +/** Used with ecs_ptr_from_json(), ecs_entity_from_json(). */ +typedef struct ecs_from_json_desc_t { + const char *name; /**< Name of expression (used for logging) */ + const char *expr; /**< Full expression (used for logging) */ -FLECS_API extern const ecs_entity_t ecs_id(EcsDocDescription); -FLECS_API extern const ecs_entity_t EcsDocBrief; -FLECS_API extern const ecs_entity_t EcsDocDetail; -FLECS_API extern const ecs_entity_t EcsDocLink; -FLECS_API extern const ecs_entity_t EcsDocColor; + /** Callback that allows for specifying a custom lookup function. The + * default behavior uses ecs_lookup() */ + ecs_entity_t (*lookup_action)( + const ecs_world_t*, + const char *value, + void *ctx); + void *lookup_ctx; -typedef struct EcsDocDescription { - char *value; -} EcsDocDescription; + /** Require components to be registered with reflection data. When not + * in strict mode, values for components without reflection are ignored. */ + bool strict; +} ecs_from_json_desc_t; -/** Add human-readable name to entity. - * Contrary to entity names, human readable names do not have to be unique and - * can contain special characters used in the query language like '*'. - * +/** Parse JSON string into value. + * This operation parses a JSON expression into the provided pointer. The + * memory pointed to must be large enough to contain a value of the used type. + * * @param world The world. - * @param entity The entity to which to add the name. - * @param name The name to add. - */ -FLECS_API -void ecs_doc_set_name( - ecs_world_t *world, - ecs_entity_t entity, - const char *name); - -/** Add brief description to entity. - * - * @param world The world. - * @param entity The entity to which to add the description. - * @param description The description to add. - */ -FLECS_API -void ecs_doc_set_brief( - ecs_world_t *world, - ecs_entity_t entity, - const char *description); - -/** Add detailed description to entity. - * - * @param world The world. - * @param entity The entity to which to add the description. - * @param description The description to add. - */ -FLECS_API -void ecs_doc_set_detail( - ecs_world_t *world, - ecs_entity_t entity, - const char *description); - -/** Add link to external documentation to entity. - * - * @param world The world. - * @param entity The entity to which to add the link. - * @param link The link to add. - */ -FLECS_API -void ecs_doc_set_link( - ecs_world_t *world, - ecs_entity_t entity, - const char *link); - -/** Add color to entity. - * UIs can use color as hint to improve visualizing entities. - * - * @param world The world. - * @param entity The entity to which to add the link. - * @param color The color to add. - */ -FLECS_API -void ecs_doc_set_color( - ecs_world_t *world, - ecs_entity_t entity, - const char *color); - -/** Get human readable name from entity. - * If entity does not have an explicit human readable name, this operation will - * return the entity name. - * - * To test if an entity has a human readable name, use: - * ecs_has_pair(world, e, ecs_id(EcsDescription), EcsName); - * Or in C++: - * e.has(flecs::Name); - * - * @param world The world. - * @param entity The entity from which to get the name. - * @return The name. - */ -FLECS_API -const char* ecs_doc_get_name( - const ecs_world_t *world, - ecs_entity_t entity); - -/** Get brief description from entity. - * - * @param world The world. - * @param entity The entity from which to get the description. - * @return The description. - */ -FLECS_API -const char* ecs_doc_get_brief( - const ecs_world_t *world, - ecs_entity_t entity); - -/** Get detailed description from entity. - * - * @param world The world. - * @param entity The entity from which to get the description. - * @return The description. - */ -FLECS_API -const char* ecs_doc_get_detail( - const ecs_world_t *world, - ecs_entity_t entity); - -/** Get link to external documentation from entity. - * - * @param world The world. - * @param entity The entity from which to get the link. - * @return The link. - */ -FLECS_API -const char* ecs_doc_get_link( - const ecs_world_t *world, - ecs_entity_t entity); - -/** Get color from entity. - * - * @param world The world. - * @param entity The entity from which to get the link. - * @return The color. - */ -FLECS_API -const char* ecs_doc_get_color( - const ecs_world_t *world, - ecs_entity_t entity); - -/* Module import */ -FLECS_API -void FlecsDocImport( - ecs_world_t *world); - -/* @} */ - -#ifdef __cplusplus -} -#endif - -#endif - -#endif - -#endif - -#ifdef FLECS_JSON -#ifdef FLECS_NO_JSON -#error "FLECS_NO_JSON failed: JSON is required by other addons" -#endif -/** - * @file addons/json.h - * @brief JSON parser addon. - * - * Parse expression strings into component values. Entity identifiers, - * enumerations and bitmasks are encoded as strings. - * - * See docs/JsonFormat.md for a description of the JSON format. - */ - -#ifdef FLECS_JSON - -#ifndef FLECS_EXPR -#define FLECS_EXPR -#endif - -#ifndef FLECS_JSON_H -#define FLECS_JSON_H - -/** - * @defgroup c_addons_json Json - * @brief Functions for serializing to/from JSON. - * - * \ingroup c_addons - * @{ - */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** Used with ecs_ptr_from_json, ecs_entity_from_json. */ -typedef struct ecs_from_json_desc_t { - const char *name; /**< Name of expression (used for logging) */ - const char *expr; /**< Full expression (used for logging) */ - - /** Callback that allows for specifying a custom lookup function. The - * default behavior uses ecs_lookup_fullpath */ - ecs_entity_t (*lookup_action)( - const ecs_world_t*, - const char *value, - void *ctx); - void *lookup_ctx; -} ecs_from_json_desc_t; - -/** Parse JSON string into value. - * This operation parses a JSON expression into the provided pointer. The - * memory pointed to must be large enough to contain a value of the used type. - * - * @param world The world. - * @param type The type of the expression to parse. - * @param ptr Pointer to the memory to write to. - * @param json The JSON expression to parse. - * @param desc Configuration parameters for deserializer. - * @return Pointer to the character after the last one read, or NULL if failed. + * @param type The type of the expression to parse. + * @param ptr Pointer to the memory to write to. + * @param json The JSON expression to parse. + * @param desc Configuration parameters for deserializer. + * @return Pointer to the character after the last one read, or NULL if failed. */ FLECS_API const char* ecs_ptr_from_json( @@ -12599,9 +13434,9 @@ const char* ecs_ptr_from_json( const ecs_from_json_desc_t *desc); /** Parse JSON object with multiple component values into entity. The format - * is the same as the one outputted by ecs_entity_to_json, but at the moment - * only supports the "ids" and "values" member. - * + * is the same as the one outputted by ecs_entity_to_json(), but at the moment + * only supports the "ids" and "values" member. + * * @param world The world. * @param entity The entity to serialize to. * @param json The JSON expression to parse (see entity in JSON format manual). @@ -12616,10 +13451,12 @@ const char* ecs_entity_from_json( const ecs_from_json_desc_t *desc); /** Parse JSON object with multiple entities into the world. The format is the - * same as the one outputted by ecs_world_to_json. - * + * same as the one outputted by ecs_world_to_json(). + * * @param world The world. * @param json The JSON expression to parse (see iterator in JSON format manual). + * @param desc Deserialization parameters. + * @return Last deserialized character, NULL if failed. */ FLECS_API const char* ecs_world_from_json( @@ -12627,14 +13464,27 @@ const char* ecs_world_from_json( const char *json, const ecs_from_json_desc_t *desc); +/** Same as ecs_world_from_json(), but loads JSON from file. + * + * @param world The world. + * @param filename The file from which to load the JSON. + * @param desc Deserialization parameters. + * @return Last deserialized character, NULL if failed. + */ +FLECS_API +const char* ecs_world_from_json_file( + ecs_world_t *world, + const char *filename, + const ecs_from_json_desc_t *desc); + /** Serialize array into JSON string. - * This operation serializes a value of the provided type to a JSON string. The + * This operation serializes a value of the provided type to a JSON string. The * memory pointed to must be large enough to contain a value of the used type. - * + * * If count is 0, the function will serialize a single value, not wrapped in * array brackets. If count is >= 1, the operation will serialize values to a * a comma-separated list inside of array brackets. - * + * * @param world The world. * @param type The type of the value to serialize. * @param data The value to serialize. @@ -12649,8 +13499,8 @@ char* ecs_array_to_json( int32_t count); /** Serialize array into JSON string buffer. - * Same as ecs_array_to_json_buf, but serializes to an ecs_strbuf_t instance. - * + * Same as ecs_array_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param world The world. * @param type The type of the value to serialize. * @param data The value to serialize. @@ -12667,8 +13517,8 @@ int ecs_array_to_json_buf( ecs_strbuf_t *buf_out); /** Serialize value into JSON string. - * Same as ecs_array_to_json, with count = 0. - * + * Same as ecs_array_to_json(), with count = 0. + * * @param world The world. * @param type The type of the value to serialize. * @param data The value to serialize. @@ -12681,8 +13531,8 @@ char* ecs_ptr_to_json( const void *data); /** Serialize value into JSON string buffer. - * Same as ecs_ptr_to_json, but serializes to an ecs_strbuf_t instance. - * + * Same as ecs_ptr_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param world The world. * @param type The type of the value to serialize. * @param data The value to serialize. @@ -12699,9 +13549,9 @@ int ecs_ptr_to_json_buf( /** Serialize type info to JSON. * This serializes type information to JSON, and can be used to store/transmit * the structure of a (component) value. - * + * * If the provided type does not have reflection data, "0" will be returned. - * + * * @param world The world. * @param type The type to serialize to JSON. * @return A JSON string with the serialized type info, or NULL if failed. @@ -12712,8 +13562,8 @@ char* ecs_type_info_to_json( ecs_entity_t type); /** Serialize type info into JSON string buffer. - * Same as ecs_type_info_to_json, but serializes to an ecs_strbuf_t instance. - * + * Same as ecs_type_info_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param world The world. * @param type The type to serialize. * @param buf_out The strbuf to append the string to. @@ -12725,35 +13575,39 @@ int ecs_type_info_to_json_buf( ecs_entity_t type, ecs_strbuf_t *buf_out); -/** Used with ecs_iter_to_json. */ +/** Used with ecs_iter_to_json(). */ typedef struct ecs_entity_to_json_desc_t { - bool serialize_path; /**< Serialize full pathname */ - bool serialize_label; /**< Serialize doc name */ - bool serialize_brief; /**< Serialize brief doc description */ - bool serialize_link; /**< Serialize doc link (URL) */ - bool serialize_color; /**< Serialize doc color */ - bool serialize_ids; /**< Serialize (component) ids */ - bool serialize_id_labels; /**< Serialize labels of (component) ids */ - bool serialize_base; /**< Serialize base components */ - bool serialize_private; /**< Serialize private components */ - bool serialize_hidden; /**< Serialize ids hidden by override */ + bool serialize_entity_id; /**< Serialize entity id */ + bool serialize_doc; /**< Serialize doc attributes */ + bool serialize_full_paths; /**< Serialize full paths for tags, components and pairs */ + bool serialize_inherited; /**< Serialize base components */ bool serialize_values; /**< Serialize component values */ + bool serialize_builtin; /**< Serialize builtin data as components (e.g. "name", "parent") */ bool serialize_type_info; /**< Serialize type info (requires serialize_values) */ bool serialize_alerts; /**< Serialize active alerts for entity */ ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */ bool serialize_matches; /**< Serialize which queries entity matches with */ } ecs_entity_to_json_desc_t; -#define ECS_ENTITY_TO_JSON_INIT (ecs_entity_to_json_desc_t){true, false,\ - false, false, false, true, false, true, false, false, false, false, false,\ - false, false } +/** Utility used to initialize JSON entity serializer. */ +#define ECS_ENTITY_TO_JSON_INIT (ecs_entity_to_json_desc_t){\ + .serialize_doc = false, \ + .serialize_full_paths = true, \ + .serialize_inherited = false, \ + .serialize_values = true, \ + .serialize_builtin = false, \ + .serialize_type_info = false, \ + .serialize_alerts = false, \ + .serialize_refs = 0, \ + .serialize_matches = false, \ +} /** Serialize entity into JSON string. * This creates a JSON object with the entity's (path) name, which components * and tags the entity has, and the component values. - * + * * The operation may fail if the entity contains components with invalid values. - * + * * @param world The world. * @param entity The entity to serialize to JSON. * @return A JSON string with the serialized entity data, or NULL if failed. @@ -12765,8 +13619,8 @@ char* ecs_entity_to_json( const ecs_entity_to_json_desc_t *desc); /** Serialize entity into JSON string buffer. - * Same as ecs_entity_to_json, but serializes to an ecs_strbuf_t instance. - * + * Same as ecs_entity_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param world The world. * @param entity The entity to serialize. * @param buf_out The strbuf to append the string to. @@ -12779,80 +13633,75 @@ int ecs_entity_to_json_buf( ecs_strbuf_t *buf_out, const ecs_entity_to_json_desc_t *desc); -/** Used with ecs_iter_to_json. */ +/** Used with ecs_iter_to_json(). */ typedef struct ecs_iter_to_json_desc_t { - bool serialize_term_ids; /**< Serialize query term component ids */ - bool serialize_term_labels; /**< Serialize query term component id labels */ - bool serialize_ids; /**< Serialize actual (matched) component ids */ - bool serialize_id_labels; /**< Serialize actual (matched) component id labels */ - bool serialize_sources; /**< Serialize sources */ - bool serialize_variables; /**< Serialize variables */ - bool serialize_is_set; /**< Serialize is_set (for optional terms) */ + bool serialize_entity_ids; /**< Serialize entity ids */ bool serialize_values; /**< Serialize component values */ - bool serialize_private; /**< Serialize component values */ - bool serialize_entities; /**< Serialize entities (for This terms) */ - bool serialize_entity_labels; /**< Serialize doc name for entities */ - bool serialize_entity_ids; /**< Serialize numerical ids for entities */ - bool serialize_entity_names; /**< Serialize names (not paths) for entities */ - bool serialize_variable_labels; /**< Serialize doc name for variables */ - bool serialize_variable_ids; /**< Serialize numerical ids for variables */ - bool serialize_colors; /**< Serialize doc color for entities */ - bool measure_eval_duration; /**< Serialize evaluation duration */ - bool serialize_type_info; /**< Serialize type information */ + bool serialize_builtin; /**< Serialize builtin data as components (e.g. "name", "parent") */ + bool serialize_doc; /**< Serialize doc attributes */ + bool serialize_full_paths; /**< Serialize full paths for tags, components and pairs */ + bool serialize_fields; /**< Serialize field data */ + bool serialize_inherited; /**< Serialize inherited components */ bool serialize_table; /**< Serialize entire table vs. matched components */ + bool serialize_type_info; /**< Serialize type information */ + bool serialize_field_info; /**< Serialize metadata for fields returned by query */ + bool serialize_query_info; /**< Serialize query terms */ + bool serialize_query_plan; /**< Serialize query plan */ + bool serialize_query_profile; /**< Profile query performance */ + bool dont_serialize_results; /**< If true, query won't be evaluated */ + bool serialize_alerts; /**< Serialize active alerts for entity */ + ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */ + bool serialize_matches; /**< Serialize which queries entity matches with */ + ecs_poly_t *query; /**< Query object (required for serialize_query_[plan|profile]). */ } ecs_iter_to_json_desc_t; +/** Utility used to initialize JSON iterator serializer. */ #define ECS_ITER_TO_JSON_INIT (ecs_iter_to_json_desc_t){\ - .serialize_term_ids = true, \ - .serialize_term_labels = false, \ - .serialize_ids = true, \ - .serialize_id_labels = false, \ - .serialize_sources = true, \ - .serialize_variables = true, \ - .serialize_is_set = true, \ - .serialize_values = true, \ - .serialize_entities = true, \ - .serialize_entity_labels = false, \ .serialize_entity_ids = false, \ - .serialize_entity_names = false, \ - .serialize_variable_labels = false, \ - .serialize_variable_ids = false, \ - .serialize_colors = false, \ - .measure_eval_duration = false, \ + .serialize_values = true, \ + .serialize_builtin = false, \ + .serialize_doc = false, \ + .serialize_full_paths = true, \ + .serialize_fields = true, \ + .serialize_inherited = false, \ + .serialize_table = false, \ .serialize_type_info = false, \ - .serialize_table = false \ + .serialize_field_info = false, \ + .serialize_query_info = false, \ + .serialize_query_plan = false, \ + .serialize_query_profile = false, \ + .dont_serialize_results = false, \ + .serialize_alerts = false, \ + .serialize_refs = false, \ + .serialize_matches = false, \ } /** Serialize iterator into JSON string. * This operation will iterate the contents of the iterator and serialize them * to JSON. The function accepts iterators from any source. - * - * @param world The world. + * * @param iter The iterator to serialize to JSON. * @return A JSON string with the serialized iterator data, or NULL if failed. */ FLECS_API char* ecs_iter_to_json( - const ecs_world_t *world, ecs_iter_t *iter, const ecs_iter_to_json_desc_t *desc); /** Serialize iterator into JSON string buffer. - * Same as ecs_iter_to_json, but serializes to an ecs_strbuf_t instance. - * - * @param world The world. + * Same as ecs_iter_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param iter The iterator to serialize. * @param buf_out The strbuf to append the string to. * @return Zero if success, non-zero if failed. */ FLECS_API int ecs_iter_to_json_buf( - const ecs_world_t *world, ecs_iter_t *iter, ecs_strbuf_t *buf_out, const ecs_iter_to_json_desc_t *desc); -/** Used with ecs_iter_to_json. */ +/** Used with ecs_iter_to_json(). */ typedef struct ecs_world_to_json_desc_t { bool serialize_builtin; /**< Exclude flecs modules & contents */ bool serialize_modules; /**< Exclude modules & contents */ @@ -12861,15 +13710,17 @@ typedef struct ecs_world_to_json_desc_t { /** 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, { + * + * @code + * ecs_query_t *f = ecs_query(world, { * .terms = {{ .id = EcsAny }} * }); - * - * ecs_iter_t it = ecs_filter_init(world, &f); + * + * ecs_iter_t it = ecs_query_init(world, &f); * ecs_iter_to_json_desc_t desc = { .serialize_table = true }; - * ecs_iter_to_json(world, iter, &desc); - * + * ecs_iter_to_json(iter, &desc); + * @endcode + * * @param world The world to serialize. * @return A JSON string with the serialized iterator data, or NULL if failed. */ @@ -12879,8 +13730,8 @@ char* ecs_world_to_json( const ecs_world_to_json_desc_t *desc); /** Serialize world into JSON string buffer. - * Same as ecs_world_to_json, but serializes to an ecs_strbuf_t instance. - * + * 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. @@ -12903,11 +13754,6 @@ int ecs_world_to_json_buf( #endif -#if defined(FLECS_EXPR) || defined(FLECS_META_C) -#ifndef FLECS_META -#define FLECS_META -#endif -#endif #ifdef FLECS_UNITS #ifdef FLECS_NO_UNITS #error "FLECS_NO_UNITS failed: UNITS is required by other addons" @@ -12920,12 +13766,18 @@ int ecs_world_to_json_buf( * the addon is included in the build. To import the module, do: * * In C: - * ECS_IMPORT(world, FlecsUnits); - * + * + * @code + * ECS_IMPORT(world, FlecsUnits); + * @endcode + * * In C++: - * world.import(); * - * As a result this module behaves just like an application-defined module, + * @code + * world.import(); + * @endcode + * + * As a result this module behaves just like an application-defined module, * which means that the ids generated for the entities inside the module are not * fixed, and depend on the order in which the module is imported. */ @@ -12934,9 +13786,9 @@ int ecs_world_to_json_buf( /** * @defgroup c_addons_units Units. - * @brief Common unit annotations for reflection framework. - * - * \ingroup c_addons + * @ingroup c_addons + * Common unit annotations for reflection framework. + * * @{ */ @@ -12957,292 +13809,298 @@ extern "C" { /** * @defgroup c_addons_units_prefixes Prefixes - * @brief Prefixes to indicate unit count (e.g. Kilo, Mega) - * - * \ingroup c_addons_units + * @ingroup c_addons_units + * Prefixes to indicate unit count (e.g. Kilo, Mega) + * * @{ */ -FLECS_API extern ECS_DECLARE(EcsUnitPrefixes); /* Parent scope for prefixes */ - -FLECS_API extern ECS_DECLARE(EcsYocto); -FLECS_API extern ECS_DECLARE(EcsZepto); -FLECS_API extern ECS_DECLARE(EcsAtto); -FLECS_API extern ECS_DECLARE(EcsFemto); -FLECS_API extern ECS_DECLARE(EcsPico); -FLECS_API extern ECS_DECLARE(EcsNano); -FLECS_API extern ECS_DECLARE(EcsMicro); -FLECS_API extern ECS_DECLARE(EcsMilli); -FLECS_API extern ECS_DECLARE(EcsCenti); -FLECS_API extern ECS_DECLARE(EcsDeci); -FLECS_API extern ECS_DECLARE(EcsDeca); -FLECS_API extern ECS_DECLARE(EcsHecto); -FLECS_API extern ECS_DECLARE(EcsKilo); -FLECS_API extern ECS_DECLARE(EcsMega); -FLECS_API extern ECS_DECLARE(EcsGiga); -FLECS_API extern ECS_DECLARE(EcsTera); -FLECS_API extern ECS_DECLARE(EcsPeta); -FLECS_API extern ECS_DECLARE(EcsExa); -FLECS_API extern ECS_DECLARE(EcsZetta); -FLECS_API extern ECS_DECLARE(EcsYotta); - -FLECS_API extern ECS_DECLARE(EcsKibi); -FLECS_API extern ECS_DECLARE(EcsMebi); -FLECS_API extern ECS_DECLARE(EcsGibi); -FLECS_API extern ECS_DECLARE(EcsTebi); -FLECS_API extern ECS_DECLARE(EcsPebi); -FLECS_API extern ECS_DECLARE(EcsExbi); -FLECS_API extern ECS_DECLARE(EcsZebi); -FLECS_API extern ECS_DECLARE(EcsYobi); +FLECS_API extern ecs_entity_t EcsUnitPrefixes; /**< Parent scope for prefixes. */ + +FLECS_API extern ecs_entity_t EcsYocto; /**< Yocto unit prefix. */ +FLECS_API extern ecs_entity_t EcsZepto; /**< Zepto unit prefix. */ +FLECS_API extern ecs_entity_t EcsAtto; /**< Atto unit prefix. */ +FLECS_API extern ecs_entity_t EcsFemto; /**< Femto unit prefix. */ +FLECS_API extern ecs_entity_t EcsPico; /**< Pico unit prefix. */ +FLECS_API extern ecs_entity_t EcsNano; /**< Nano unit prefix. */ +FLECS_API extern ecs_entity_t EcsMicro; /**< Micro unit prefix. */ +FLECS_API extern ecs_entity_t EcsMilli; /**< Milli unit prefix. */ +FLECS_API extern ecs_entity_t EcsCenti; /**< Centi unit prefix. */ +FLECS_API extern ecs_entity_t EcsDeci; /**< Deci unit prefix. */ +FLECS_API extern ecs_entity_t EcsDeca; /**< Deca unit prefix. */ +FLECS_API extern ecs_entity_t EcsHecto; /**< Hecto unit prefix. */ +FLECS_API extern ecs_entity_t EcsKilo; /**< Kilo unit prefix. */ +FLECS_API extern ecs_entity_t EcsMega; /**< Mega unit prefix. */ +FLECS_API extern ecs_entity_t EcsGiga; /**< Giga unit prefix. */ +FLECS_API extern ecs_entity_t EcsTera; /**< Tera unit prefix. */ +FLECS_API extern ecs_entity_t EcsPeta; /**< Peta unit prefix. */ +FLECS_API extern ecs_entity_t EcsExa; /**< Exa unit prefix. */ +FLECS_API extern ecs_entity_t EcsZetta; /**< Zetta unit prefix. */ +FLECS_API extern ecs_entity_t EcsYotta; /**< Yotta unit prefix. */ + +FLECS_API extern ecs_entity_t EcsKibi; /**< Kibi unit prefix. */ +FLECS_API extern ecs_entity_t EcsMebi; /**< Mebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsGibi; /**< Gibi unit prefix. */ +FLECS_API extern ecs_entity_t EcsTebi; /**< Tebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsPebi; /**< Pebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsExbi; /**< Exbi unit prefix. */ +FLECS_API extern ecs_entity_t EcsZebi; /**< Zebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsYobi; /**< Yobi unit prefix. */ /** @} */ /** * @defgroup c_addons_units_duration Duration - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsDuration); -FLECS_API extern ECS_DECLARE(EcsPicoSeconds); -FLECS_API extern ECS_DECLARE(EcsNanoSeconds); -FLECS_API extern ECS_DECLARE(EcsMicroSeconds); -FLECS_API extern ECS_DECLARE(EcsMilliSeconds); -FLECS_API extern ECS_DECLARE(EcsSeconds); -FLECS_API extern ECS_DECLARE(EcsMinutes); -FLECS_API extern ECS_DECLARE(EcsHours); -FLECS_API extern ECS_DECLARE(EcsDays); +FLECS_API extern ecs_entity_t EcsDuration; /**< Duration quantity. */ +FLECS_API extern ecs_entity_t EcsPicoSeconds; /**< PicoSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsNanoSeconds; /**< NanoSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsMicroSeconds; /**< MicroSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsMilliSeconds; /**< MilliSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsSeconds; /**< Seconds duration unit. */ +FLECS_API extern ecs_entity_t EcsMinutes; /**< Minutes duration unit. */ +FLECS_API extern ecs_entity_t EcsHours; /**< Hours duration unit. */ +FLECS_API extern ecs_entity_t EcsDays; /**< Days duration unit. */ /** @} */ /** * @defgroup c_addons_units_time Time - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsTime); -FLECS_API extern ECS_DECLARE(EcsDate); +FLECS_API extern ecs_entity_t EcsTime; /**< Time quantity. */ +FLECS_API extern ecs_entity_t EcsDate; /**< Date unit. */ /** @} */ /** * @defgroup c_addons_units_mass Mass - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsMass); -FLECS_API extern ECS_DECLARE(EcsGrams); -FLECS_API extern ECS_DECLARE(EcsKiloGrams); +FLECS_API extern ecs_entity_t EcsMass; /**< Mass quantity. */ +FLECS_API extern ecs_entity_t EcsGrams; /**< Grams unit. */ +FLECS_API extern ecs_entity_t EcsKiloGrams; /**< KiloGrams unit. */ /** @} */ /** * @defgroup c_addons_units_electric_Current Electric Current - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsElectricCurrent); -FLECS_API extern ECS_DECLARE(EcsAmpere); +FLECS_API extern ecs_entity_t EcsElectricCurrent; /**< ElectricCurrent quantity. */ +FLECS_API extern ecs_entity_t EcsAmpere; /**< Ampere unit. */ /** @} */ /** * @defgroup c_addons_units_amount Amount - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsAmount); -FLECS_API extern ECS_DECLARE(EcsMole); +FLECS_API extern ecs_entity_t EcsAmount; /**< Amount quantity. */ +FLECS_API extern ecs_entity_t EcsMole; /**< Mole unit. */ /** @} */ /** * @defgroup c_addons_units_luminous_intensity Luminous Intensity - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsLuminousIntensity); -FLECS_API extern ECS_DECLARE(EcsCandela); +FLECS_API extern ecs_entity_t EcsLuminousIntensity; /**< LuminousIntensity quantity. */ +FLECS_API extern ecs_entity_t EcsCandela; /**< Candela unit. */ /** @} */ /** * @defgroup c_addons_units_force Force - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsForce); -FLECS_API extern ECS_DECLARE(EcsNewton); +FLECS_API extern ecs_entity_t EcsForce; /**< Force quantity. */ +FLECS_API extern ecs_entity_t EcsNewton; /**< Newton unit. */ /** @} */ /** * @defgroup c_addons_units_length Length - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsLength); -FLECS_API extern ECS_DECLARE(EcsMeters); -FLECS_API extern ECS_DECLARE(EcsPicoMeters); -FLECS_API extern ECS_DECLARE(EcsNanoMeters); -FLECS_API extern ECS_DECLARE(EcsMicroMeters); -FLECS_API extern ECS_DECLARE(EcsMilliMeters); -FLECS_API extern ECS_DECLARE(EcsCentiMeters); -FLECS_API extern ECS_DECLARE(EcsKiloMeters); -FLECS_API extern ECS_DECLARE(EcsMiles); -FLECS_API extern ECS_DECLARE(EcsPixels); +FLECS_API extern ecs_entity_t EcsLength; /**< Length quantity. */ +FLECS_API extern ecs_entity_t EcsMeters; /**< Meters unit. */ +FLECS_API extern ecs_entity_t EcsPicoMeters; /**< PicoMeters unit. */ +FLECS_API extern ecs_entity_t EcsNanoMeters; /**< NanoMeters unit. */ +FLECS_API extern ecs_entity_t EcsMicroMeters; /**< MicroMeters unit. */ +FLECS_API extern ecs_entity_t EcsMilliMeters; /**< MilliMeters unit. */ +FLECS_API extern ecs_entity_t EcsCentiMeters; /**< CentiMeters unit. */ +FLECS_API extern ecs_entity_t EcsKiloMeters; /**< KiloMeters unit. */ +FLECS_API extern ecs_entity_t EcsMiles; /**< Miles unit. */ +FLECS_API extern ecs_entity_t EcsPixels; /**< Pixels unit. */ /** @} */ /** * @defgroup c_addons_units_pressure Pressure - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsPressure); -FLECS_API extern ECS_DECLARE(EcsPascal); -FLECS_API extern ECS_DECLARE(EcsBar); +FLECS_API extern ecs_entity_t EcsPressure; /**< Pressure quantity. */ +FLECS_API extern ecs_entity_t EcsPascal; /**< Pascal unit. */ +FLECS_API extern ecs_entity_t EcsBar; /**< Bar unit. */ /** @} */ /** * @defgroup c_addons_units_speed Speed - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsSpeed); -FLECS_API extern ECS_DECLARE(EcsMetersPerSecond); -FLECS_API extern ECS_DECLARE(EcsKiloMetersPerSecond); -FLECS_API extern ECS_DECLARE(EcsKiloMetersPerHour); -FLECS_API extern ECS_DECLARE(EcsMilesPerHour); +FLECS_API extern ecs_entity_t EcsSpeed; /**< Speed quantity. */ +FLECS_API extern ecs_entity_t EcsMetersPerSecond; /**< MetersPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloMetersPerSecond; /**< KiloMetersPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloMetersPerHour; /**< KiloMetersPerHour unit. */ +FLECS_API extern ecs_entity_t EcsMilesPerHour; /**< MilesPerHour unit. */ /** @} */ /** * @defgroup c_addons_units_temperature Temperature - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsTemperature); -FLECS_API extern ECS_DECLARE(EcsKelvin); -FLECS_API extern ECS_DECLARE(EcsCelsius); -FLECS_API extern ECS_DECLARE(EcsFahrenheit); +FLECS_API extern ecs_entity_t EcsTemperature; /**< Temperature quantity. */ +FLECS_API extern ecs_entity_t EcsKelvin; /**< Kelvin unit. */ +FLECS_API extern ecs_entity_t EcsCelsius; /**< Celsius unit. */ +FLECS_API extern ecs_entity_t EcsFahrenheit; /**< Fahrenheit unit. */ /** @} */ /** * @defgroup c_addons_units_data Data - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsData); -FLECS_API extern ECS_DECLARE(EcsBits); -FLECS_API extern ECS_DECLARE(EcsKiloBits); -FLECS_API extern ECS_DECLARE(EcsMegaBits); -FLECS_API extern ECS_DECLARE(EcsGigaBits); -FLECS_API extern ECS_DECLARE(EcsBytes); -FLECS_API extern ECS_DECLARE(EcsKiloBytes); -FLECS_API extern ECS_DECLARE(EcsMegaBytes); -FLECS_API extern ECS_DECLARE(EcsGigaBytes); -FLECS_API extern ECS_DECLARE(EcsKibiBytes); -FLECS_API extern ECS_DECLARE(EcsMebiBytes); -FLECS_API extern ECS_DECLARE(EcsGibiBytes); +FLECS_API extern ecs_entity_t EcsData; /**< Data quantity. */ +FLECS_API extern ecs_entity_t EcsBits; /**< Bits unit. */ +FLECS_API extern ecs_entity_t EcsKiloBits; /**< KiloBits unit. */ +FLECS_API extern ecs_entity_t EcsMegaBits; /**< MegaBits unit. */ +FLECS_API extern ecs_entity_t EcsGigaBits; /**< GigaBits unit. */ +FLECS_API extern ecs_entity_t EcsBytes; /**< Bytes unit. */ +FLECS_API extern ecs_entity_t EcsKiloBytes; /**< KiloBytes unit. */ +FLECS_API extern ecs_entity_t EcsMegaBytes; /**< MegaBytes unit. */ +FLECS_API extern ecs_entity_t EcsGigaBytes; /**< GigaBytes unit. */ +FLECS_API extern ecs_entity_t EcsKibiBytes; /**< KibiBytes unit. */ +FLECS_API extern ecs_entity_t EcsMebiBytes; /**< MebiBytes unit. */ +FLECS_API extern ecs_entity_t EcsGibiBytes; /**< GibiBytes unit. */ /** @} */ /** * @defgroup c_addons_units_datarate Data Rate - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsDataRate); -FLECS_API extern ECS_DECLARE(EcsBitsPerSecond); -FLECS_API extern ECS_DECLARE(EcsKiloBitsPerSecond); -FLECS_API extern ECS_DECLARE(EcsMegaBitsPerSecond); -FLECS_API extern ECS_DECLARE(EcsGigaBitsPerSecond); -FLECS_API extern ECS_DECLARE(EcsBytesPerSecond); -FLECS_API extern ECS_DECLARE(EcsKiloBytesPerSecond); -FLECS_API extern ECS_DECLARE(EcsMegaBytesPerSecond); -FLECS_API extern ECS_DECLARE(EcsGigaBytesPerSecond); +FLECS_API extern ecs_entity_t EcsDataRate; /**< DataRate quantity. */ +FLECS_API extern ecs_entity_t EcsBitsPerSecond; /**< BitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloBitsPerSecond; /**< KiloBitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsMegaBitsPerSecond; /**< MegaBitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsGigaBitsPerSecond; /**< GigaBitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsBytesPerSecond; /**< BytesPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloBytesPerSecond; /**< KiloBytesPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsMegaBytesPerSecond; /**< MegaBytesPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsGigaBytesPerSecond; /**< GigaBytesPerSecond unit. */ /** @} */ /** * @defgroup c_addons_units_duration Duration - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsAngle); -FLECS_API extern ECS_DECLARE(EcsRadians); -FLECS_API extern ECS_DECLARE(EcsDegrees); +FLECS_API extern ecs_entity_t EcsAngle; /**< Angle quantity. */ +FLECS_API extern ecs_entity_t EcsRadians; /**< Radians unit. */ +FLECS_API extern ecs_entity_t EcsDegrees; /**< Degrees unit. */ /** @} */ /** * @defgroup c_addons_units_angle Angle - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsFrequency); -FLECS_API extern ECS_DECLARE(EcsHertz); -FLECS_API extern ECS_DECLARE(EcsKiloHertz); -FLECS_API extern ECS_DECLARE(EcsMegaHertz); -FLECS_API extern ECS_DECLARE(EcsGigaHertz); +FLECS_API extern ecs_entity_t EcsFrequency; /**< Frequency quantity. */ +FLECS_API extern ecs_entity_t EcsHertz; /**< Hertz unit. */ +FLECS_API extern ecs_entity_t EcsKiloHertz; /**< KiloHertz unit. */ +FLECS_API extern ecs_entity_t EcsMegaHertz; /**< MegaHertz unit. */ +FLECS_API extern ecs_entity_t EcsGigaHertz; /**< GigaHertz unit. */ /** @} */ /** * @defgroup c_addons_units_uri Uri - * - * \ingroup c_addons_units + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsUri; /**< URI quantity. */ +FLECS_API extern ecs_entity_t EcsUriHyperlink; /**< UriHyperlink unit. */ +FLECS_API extern ecs_entity_t EcsUriImage; /**< UriImage unit. */ +FLECS_API extern ecs_entity_t EcsUriFile; /**< UriFile unit. */ + +/** @} */ + +/** + * @defgroup c_addons_units_color Color + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsUri); -FLECS_API extern ECS_DECLARE(EcsUriHyperlink); -FLECS_API extern ECS_DECLARE(EcsUriImage); -FLECS_API extern ECS_DECLARE(EcsUriFile); +FLECS_API extern ecs_entity_t EcsColor; /**< Color quantity. */ +FLECS_API extern ecs_entity_t EcsColorRgb; /**< ColorRgb unit. */ +FLECS_API extern ecs_entity_t EcsColorHsl; /**< ColorHsl unit. */ +FLECS_API extern ecs_entity_t EcsColorCss; /**< ColorCss unit. */ /** @} */ -FLECS_API extern ECS_DECLARE(EcsAcceleration); -FLECS_API extern ECS_DECLARE(EcsPercentage); -FLECS_API extern ECS_DECLARE(EcsBel); -FLECS_API extern ECS_DECLARE(EcsDeciBel); + +FLECS_API extern ecs_entity_t EcsAcceleration; /**< Acceleration unit. */ +FLECS_API extern ecs_entity_t EcsPercentage; /**< Percentage unit. */ +FLECS_API extern ecs_entity_t EcsBel; /**< Bel unit. */ +FLECS_API extern ecs_entity_t EcsDeciBel; /**< DeciBel unit. */ //////////////////////////////////////////////////////////////////////////////// //// Module //////////////////////////////////////////////////////////////////////////////// +/** Units module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsUnits) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsUnitsImport( ecs_world_t *world); @@ -13259,2153 +14117,2215 @@ void FlecsUnitsImport( #endif -#ifdef FLECS_META -#ifdef FLECS_NO_META -#error "FLECS_NO_META failed: META is required by other addons" +#ifdef FLECS_SCRIPT +#ifdef FLECS_NO_SCRIPT +#error "FLECS_NO_SCRIPT failed: SCRIPT is required by other addons" #endif /** - * @file addons/meta.h - * @brief Meta addon. - * - * The meta addon enables reflecting on component data. Types are stored as - * entities, with components that store the reflection data. A type has at least - * two components: - * - * - EcsComponent: core component, contains size & alignment - * - EcsMetaType: component that indicates what kind of type the entity is - * - * Additionally the type may have an additional component that contains the - * reflection data for the type. For example, structs have these components: - * - * - EcsComponent - * - EcsMetaType - * - EcsStruct - * - * Structs can be populated by adding child entities with the EcsMember - * component. Adding a child with a Member component to an entity will - * automatically add the EcsStruct component to the parent. - * - * Enums/bitmasks can be populated by adding child entities with the Constant - * tag. By default constants are automatically assigned values when they are - * added to the enum/bitmask. The parent entity must have the EcsEnum or - * EcsBitmask component before adding the constants. - * - * To create enum constants with a manual value, set (Constant, i32) to the - * desired value. To create bitmask constants with a manual value, set - * (Constant, u32) to the desired value. Constants with manual values should not - * conflict with other constants. - * - * The _init APIs are convenience wrappers around creating the entities and - * components for the types. - * - * When a type is created it automatically receives the EcsComponent and - * EcsMetaType components. The former means that the resulting type can be - * used as a regular component: - * - * // Create Position type - * ecs_entity_t pos = ecs_struct_init(world, &(ecs_struct_desc_t){ - * .entity.name = "Position", - * .members = { - * {"x", ecs_id(ecs_f32_t)}, - * {"y", ecs_id(ecs_f32_t)} - * } - * }); + * @file addons/script.h + * @brief Flecs script module. * - * // Create entity with Position component - * ecs_entity_t e = ecs_new_w_id(world, pos); - * - * Type entities do not have to be named. + * For script, see examples/script. */ -#ifdef FLECS_META +#ifdef FLECS_SCRIPT /** - * @defgroup c_addons_meta Meta - * @brief Flecs reflection framework. - * - * \ingroup c_addons + * @defgroup c_addons_script Flecs script + * @ingroup c_addons + * DSL for loading scenes, assets and configuration. + * * @{ */ -#include +#ifndef FLECS_META +#define FLECS_META +#endif -#ifndef FLECS_MODULE -#define FLECS_MODULE +#ifndef FLECS_DOC +#define FLECS_DOC #endif -#ifndef FLECS_META_H -#define FLECS_META_H + +#ifndef FLECS_SCRIPT_H +#define FLECS_SCRIPT_H #ifdef __cplusplus extern "C" { #endif -#define ECS_MEMBER_DESC_CACHE_SIZE (32) - -/** Primitive type definitions. - * These typedefs allow the builtin primitives to be used as regular components: - * ecs_set(world, e, ecs_i32_t, {10}); - * - * Or a more useful example (create an enum constant with a manual value): - * ecs_set_pair_object(world, e, EcsConstant, ecs_i32_t, {10}); - */ -typedef bool ecs_bool_t; -typedef char ecs_char_t; -typedef unsigned char ecs_byte_t; -typedef uint8_t ecs_u8_t; -typedef uint16_t ecs_u16_t; -typedef uint32_t ecs_u32_t; -typedef uint64_t ecs_u64_t; -typedef uintptr_t ecs_uptr_t; -typedef int8_t ecs_i8_t; -typedef int16_t ecs_i16_t; -typedef int32_t ecs_i32_t; -typedef int64_t ecs_i64_t; -typedef intptr_t ecs_iptr_t; -typedef float ecs_f32_t; -typedef double ecs_f64_t; -typedef char* ecs_string_t; +FLECS_API +extern ECS_COMPONENT_DECLARE(EcsScript); -/* Meta module component ids */ -FLECS_API extern const ecs_entity_t ecs_id(EcsMetaType); -FLECS_API extern const ecs_entity_t ecs_id(EcsMetaTypeSerialized); -FLECS_API extern const ecs_entity_t ecs_id(EcsPrimitive); -FLECS_API extern const ecs_entity_t ecs_id(EcsEnum); -FLECS_API extern const ecs_entity_t ecs_id(EcsBitmask); -FLECS_API extern const ecs_entity_t ecs_id(EcsMember); -FLECS_API extern const ecs_entity_t ecs_id(EcsMemberRanges); -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; -FLECS_API extern const ecs_entity_t EcsQuantity; +typedef struct ecs_script_template_t ecs_script_template_t; -/* Primitive type component ids */ -FLECS_API extern const ecs_entity_t ecs_id(ecs_bool_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_char_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_byte_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_u8_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_u16_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_u32_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_u64_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_uptr_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_i8_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_i16_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_i32_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_i64_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_iptr_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_f32_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_f64_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_string_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_entity_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_id_t); +/** Script variable. */ +typedef struct ecs_script_var_t { + const char *name; + ecs_value_t value; + const ecs_type_info_t *type_info; +} ecs_script_var_t; -/** Type kinds supported by meta addon */ -typedef enum ecs_type_kind_t { - EcsPrimitiveType, - EcsBitmaskType, - EcsEnumType, - EcsStructType, - EcsArrayType, - EcsVectorType, - EcsOpaqueType, - EcsTypeKindLast = EcsOpaqueType -} ecs_type_kind_t; +/** Script variable scope. */ +typedef struct ecs_script_vars_t { + struct ecs_script_vars_t *parent; + ecs_hashmap_t var_index; + ecs_vec_t vars; -/** Component that is automatically added to every type with the right kind. */ -typedef struct EcsMetaType { - ecs_type_kind_t kind; - bool existing; /**< Did the type exist or is it populated from reflection */ - bool partial; /**< Is the reflection data a partial type description */ -} EcsMetaType; + const ecs_world_t *world; + struct ecs_stack_t *stack; + ecs_stack_cursor_t *cursor; + ecs_allocator_t *allocator; +} ecs_script_vars_t; -/** Primitive type kinds supported by meta addon */ -typedef enum ecs_primitive_kind_t { - EcsBool = 1, - EcsChar, - EcsByte, - EcsU8, - EcsU16, - EcsU32, - EcsU64, - EcsI8, - EcsI16, - EcsI32, - EcsI64, - EcsF32, - EcsF64, - EcsUPtr, - EcsIPtr, - EcsString, - EcsEntity, - EcsId, - EcsPrimitiveKindLast = EcsId -} ecs_primitive_kind_t; +/** Script object. */ +typedef struct ecs_script_t { + ecs_world_t *world; + const char *name; + const char *code; +} ecs_script_t; -/** Component added to primitive types */ -typedef struct EcsPrimitive { - ecs_primitive_kind_t kind; -} EcsPrimitive; +/** Script component. + * This component is added to the entities of managed scripts and templates. + */ +typedef struct EcsScript { + ecs_script_t *script; + ecs_script_template_t *template_; /* Only set for template scripts */ +} EcsScript; -/** Component added to member entities */ -typedef struct EcsMember { - ecs_entity_t type; - int32_t count; - ecs_entity_t unit; - int32_t offset; -} EcsMember; -/** Type expressing a range for a member value */ -typedef struct ecs_member_value_range_t { - double min; - double max; -} ecs_member_value_range_t; +/* Parsing & running scripts */ -/** Component added to member entities to express valid value ranges */ -typedef struct EcsMemberRanges { - ecs_member_value_range_t value; - ecs_member_value_range_t warning; - ecs_member_value_range_t error; -} EcsMemberRanges; +/** Parse script. + * This operation parses a script and returns a script object upon success. To + * run the script, call ecs_script_eval(). + * + * @param world The world. + * @param name Name of the script (typically a file/module name). + * @param code The script code. + * @return Script object if success, NULL if failed. +*/ +FLECS_API +ecs_script_t* ecs_script_parse( + ecs_world_t *world, + const char *name, + const char *code); -/** Element type of members vector in EcsStruct */ -typedef struct ecs_member_t { - /** Must be set when used with ecs_struct_desc_t */ - const char *name; - ecs_entity_t type; +/** Evaluate script. + * This operation evaluates (runs) a parsed script. + * + * @param script The script. + * @return Zero if success, non-zero if failed. +*/ +FLECS_API +int ecs_script_eval( + ecs_script_t *script); - /** May be set when used with ecs_struct_desc_t */ - int32_t count; - int32_t offset; +/** Free script. + * This operation frees a script object. + * + * Templates created by the script rely upon resources in the script object, + * and for that reason keep the script alive until all templates created by the + * script are deleted. + * + * @param script The script. + */ +FLECS_API +void ecs_script_free( + ecs_script_t *script); - /** May be set when used with ecs_struct_desc_t, will be auto-populated if - * type entity is also a unit */ - ecs_entity_t unit; +/** Parse script. + * This parses a script and instantiates the entities in the world. + * This operation is the equivalent to doing: + * + * @code + * ecs_script_t *script = ecs_script_parse(world, name, code); + * ecs_script_eval(script); + * ecs_script_free(script); + * @endcode + * + * @param world The world. + * @param name The script name (typically the file). + * @param code The script. + * @return Zero if success, non-zero otherwise. + */ +FLECS_API +int ecs_script_run( + ecs_world_t *world, + const char *name, + const char *code); - /** Numerical range that specifies which values member can assume. This - * range may be used by UI elements such as a progress bar or slider. The - * value of a member should not exceed this range. */ - ecs_member_value_range_t range; +/** Parse script file. + * This parses a script file and instantiates the entities in the world. This + * operation is equivalent to loading the file contents and passing it to + * ecs_script_run(). + * + * @param world The world. + * @param filename The script file name. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_script_run_file( + ecs_world_t *world, + const char *filename); - /** Numerical range outside of which the value represents an error. This - * range may be used by UI elements to style a value. */ - ecs_member_value_range_t error_range; +/** Convert script AST to string. + * This operation converts the script abstract syntax tree to a string, which + * can be used to debug a script. + * + * @param script The script. + * @param buf The buffer to write to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_script_ast_to_buf( + ecs_script_t *script, + ecs_strbuf_t *buf); - /** Numerical range outside of which the value represents an warning. This - * range may be used by UI elements to style a value. */ - ecs_member_value_range_t warning_range; +/** Convert script AST to string. + * This operation converts the script abstract syntax tree to a string, which + * can be used to debug a script. + * + * @param script The script. + * @return The string if success, NULL if failed. + */ +FLECS_API +char* ecs_script_ast_to_str( + ecs_script_t *script); - /** Should not be set by ecs_struct_desc_t */ - ecs_size_t size; - ecs_entity_t member; -} ecs_member_t; -/** Component added to struct type entities */ -typedef struct EcsStruct { - /** Populated from child entities with Member component */ - ecs_vec_t members; /* vector */ -} EcsStruct; +/* Managed scripts (script associated with entity that outlives the function) */ -typedef struct ecs_enum_constant_t { - /** Must be set when used with ecs_enum_desc_t */ - const char *name; +/** Used with ecs_script_init() */ +typedef struct ecs_script_desc_t { + ecs_entity_t entity; /* Set to customize entity handle associated with script */ + const char *filename; /* Set to load script from file */ + const char *code; /* Set to parse script from string */ +} ecs_script_desc_t; - /** May be set when used with ecs_enum_desc_t */ - int32_t value; +/** Load managed script. + * A managed script tracks which entities it creates, and keeps those entities + * synchronized when the contents of the script are updated. When the script is + * updated, entities that are no longer in the new version will be deleted. + * + * This feature is experimental. + * + * @param world The world. + * @param desc Script descriptor. + */ +FLECS_API +ecs_entity_t ecs_script_init( + ecs_world_t *world, + const ecs_script_desc_t *desc); - /** Should not be set by ecs_enum_desc_t */ - ecs_entity_t constant; -} ecs_enum_constant_t; +#define ecs_script(world, ...)\ + ecs_script_init(world, &(ecs_script_desc_t) __VA_ARGS__) -/** Component added to enum type entities */ -typedef struct EcsEnum { - /** Populated from child entities with Constant component */ - ecs_map_t constants; /* map */ -} EcsEnum; +/** Update script with new code. + * + * @param world The world. + * @param script The script entity. + * @param instance An template instance (optional). + * @param code The script code. + */ +FLECS_API +int ecs_script_update( + ecs_world_t *world, + ecs_entity_t script, + ecs_entity_t instance, + const char *code); -typedef struct ecs_bitmask_constant_t { - /** Must be set when used with ecs_bitmask_desc_t */ - const char *name; +/** Clear all entities associated with script. + * + * @param world The world. + * @param script The script entity. + * @param instance The script instance. + */ +FLECS_API +void ecs_script_clear( + ecs_world_t *world, + ecs_entity_t script, + ecs_entity_t instance); - /** May be set when used with ecs_bitmask_desc_t */ - ecs_flags32_t value; - /** Should not be set by ecs_bitmask_desc_t */ - ecs_entity_t constant; -} ecs_bitmask_constant_t; +/* Script variables */ -/** Component added to bitmask type entities */ -typedef struct EcsBitmask { - /* Populated from child entities with Constant component */ - ecs_map_t constants; /* map */ -} EcsBitmask; +/** Create new variable scope. + * Create root variable scope. A variable scope contains one or more variables. + * Scopes can be nested, which allows variables in different scopes to have the + * same name. Variables from parent scopes will be shadowed by variables in + * child scopes with the same name. + * + * Use the `ecs_script_vars_push()` and `ecs_script_vars_pop()` functions to + * push and pop variable scopes. + * + * When a variable contains allocated resources (e.g. a string), its resources + * will be freed when `ecs_script_vars_pop()` is called on the scope, the + * ecs_script_vars_t::type_info field is initialized for the variable, and + * `ecs_type_info_t::hooks::dtor` is set. + * + * @param world The world. + */ +FLECS_API +ecs_script_vars_t* ecs_script_vars_init( + ecs_world_t *world); -/** Component added to array type entities */ -typedef struct EcsArray { - ecs_entity_t type; /**< Element type */ - int32_t count; /**< Number of elements */ -} EcsArray; +/** Free variable scope. + * Free root variable scope. The provided scope should not have a parent. This + * operation calls `ecs_script_vars_pop()` on the scope. + * + * @param vars The variable scope. + */ +FLECS_API +void ecs_script_vars_fini( + ecs_script_vars_t *vars); -/** Component added to vector type entities */ -typedef struct EcsVector { - ecs_entity_t type; /**< Element type */ -} EcsVector; +/** Push new variable scope. + * + * Scopes created with ecs_script_vars_push() must be cleaned up with + * ecs_script_vars_pop(). + * + * If the stack and allocator arguments are left to NULL, their values will be + * copied from the parent. + * + * @param parent The parent scope (provide NULL for root scope). + * @return The new variable scope. + */ +FLECS_API +ecs_script_vars_t* ecs_script_vars_push( + ecs_script_vars_t *parent); +/** Pop variable scope. + * This frees up the resources for a variable scope. The scope must be at the + * top of a vars stack. Calling ecs_script_vars_pop() on a scope that is not the + * last scope causes undefined behavior. + * + * @param vars The scope to free. + * @return The parent scope. + */ +FLECS_API +ecs_script_vars_t* ecs_script_vars_pop( + ecs_script_vars_t *vars); -/* Opaque type support */ +/** Declare a variable. + * This operation declares a new variable in the current scope. If a variable + * with the specified name already exists, the operation will fail. + * + * This operation does not allocate storage for the variable. This is done to + * allow for variables that point to existing storage, which prevents having + * to copy existing values to a variable scope. + * + * @param vars The variable scope. + * @param name The variable name. + * @return The new variable, or NULL if the operation failed. + */ +FLECS_API +ecs_script_var_t* ecs_script_vars_declare( + ecs_script_vars_t *vars, + const char *name); -#if !defined(__cplusplus) || !defined(FLECS_CPP) +/** Define a variable. + * This operation calls `ecs_script_vars_declare()` and allocates storage for + * the variable. If the type has a ctor, it will be called on the new storage. + * + * The scope's stack allocator will be used to allocate the storage. After + * `ecs_script_vars_pop()` is called on the scope, the variable storage will no + * longer be valid. + * + * The operation will fail if the type argument is not a type. + * + * @param vars The variable scope. + * @param name The variable name. + * @param type The variable type. + * @return The new variable, or NULL if the operation failed. + */ +FLECS_API +ecs_script_var_t* ecs_script_vars_define_id( + ecs_script_vars_t *vars, + const char *name, + ecs_entity_t type); -/** Serializer interface */ -typedef struct ecs_serializer_t { - /* Serialize value */ - int (*value)( - const struct ecs_serializer_t *ser, /**< Serializer */ - ecs_entity_t type, /**< Type of the value to serialize */ - const void *value); /**< Pointer to the value to serialize */ +#define ecs_script_vars_define(vars, name, type)\ + ecs_script_vars_define_id(vars, name, ecs_id(type)) - /* Serialize member */ - int (*member)( - const struct ecs_serializer_t *ser, /**< Serializer */ - const char *member); /**< Member name */ +/** Lookup a variable. + * This operation looks up a variable in the current scope. If the variable + * can't be found in the current scope, the operation will recursively search + * the parent scopes. + * + * @param vars The variable scope. + * @param name The variable name. + * @return The variable, or NULL if one with the provided name does not exist. + */ +FLECS_API +ecs_script_var_t* ecs_script_vars_lookup( + const ecs_script_vars_t *vars, + const char *name); - const ecs_world_t *world; - void *ctx; -} ecs_serializer_t; +/** Convert iterator to vars + * This operation converts an iterator to a variable array. This allows for + * using iterator results in expressions. The operation only converts a + * single result at a time, and does not progress the iterator. + * + * Iterator fields with data will be made available as variables with as name + * the field index (e.g. "$1"). The operation does not check if reflection data + * is registered for a field type. If no reflection data is registered for the + * type, using the field variable in expressions will fail. + * + * Field variables will only contain single elements, even if the iterator + * returns component arrays. The offset parameter can be used to specify which + * element in the component arrays to return. The offset parameter must be + * smaller than it->count. + * + * The operation will create a variable for query variables that contain a + * single entity. + * + * The operation will attempt to use existing variables. If a variable does not + * yet exist, the operation will create it. If an existing variable exists with + * a mismatching type, the operation will fail. + * + * Accessing variables after progressing the iterator or after the iterator is + * destroyed will result in undefined behavior. + * + * If vars contains a variable that is not present in the iterator, the variable + * will not be modified. + * + * @param it The iterator to convert to variables. + * @param vars The variables to write to. + * @param offset The offset to the current element. + */ +FLECS_API +void ecs_script_vars_from_iter( + const ecs_iter_t *it, + ecs_script_vars_t *vars, + int offset); -#elif defined(__cplusplus) -} /* extern "C" { */ +/* Standalone expression evaluation */ -/** Serializer interface (same layout as C, but with convenience methods) */ -typedef struct ecs_serializer_t { - /* Serialize value */ - int (*value_)( - const struct ecs_serializer_t *ser, - ecs_entity_t type, - const void *value); +/** Used with ecs_script_expr_run(). */ +typedef struct ecs_script_expr_run_desc_t { + const char *name; + const char *expr; + ecs_entity_t (*lookup_action)( + const ecs_world_t*, + const char *value, + void *ctx); + void *lookup_ctx; + ecs_script_vars_t *vars; +} ecs_script_expr_run_desc_t; - /* Serialize member */ - int (*member_)( - const struct ecs_serializer_t *ser, - const char *name); +/** Parse standalone expression into value. + * This operation parses a flecs expression into the provided pointer. The + * memory pointed to must be large enough to contain a value of the used type. + * + * If no type and pointer are provided for the value argument, the operation + * will discover the type from the expression and allocate storage for the + * value. The allocated value must be freed with ecs_value_free(). + * + * @param world The world. + * @param ptr The pointer to the expression to parse. + * @param value The value containing type & pointer to write to. + * @param desc Configuration parameters for deserializer. + * @return Pointer to the character after the last one read, or NULL if failed. + */ +FLECS_API +const char* ecs_script_expr_run( + ecs_world_t *world, + const char *ptr, + ecs_value_t *value, + const ecs_script_expr_run_desc_t *desc); - /* Serialize value */ - int value(ecs_entity_t type, const void *value) const; - - /* Serialize value */ - template - int value(const T& value) const; +/** Evaluate interpolated expressions in string. + * This operation evaluates expressions in a string, and replaces them with + * their evaluated result. Supported expression formats are: + * - $variable_name + * - {expression} + * + * The $, { and } characters can be escaped with a backslash (\). + * + * @param world The world. + * @param str The string to evaluate. + * @param vars The variables to use for evaluation. + */ +FLECS_API +char* ecs_script_string_interpolate( + ecs_world_t *world, + const char *str, + const ecs_script_vars_t *vars); - /* Serialize member */ - int member(const char *name) const; - const ecs_world_t *world; - void *ctx; -} ecs_serializer_t; +/* Value serialization */ -extern "C" { -#endif +/** Serialize value into expression string. + * This operation serializes a value of the provided type to a string. The + * memory pointed to must be large enough to contain a value of the used type. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @return String with expression, or NULL if failed. + */ +FLECS_API +char* ecs_ptr_to_expr( + const ecs_world_t *world, + ecs_entity_t type, + const void *data); -/** Callback invoked serializing an opaque type. */ -typedef int (*ecs_meta_serialize_t)( - const ecs_serializer_t *ser, - const void *src); /**< Pointer to value to serialize */ +/** Serialize value into expression buffer. + * Same as ecs_ptr_to_expr(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @param buf The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_ptr_to_expr_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *data, + ecs_strbuf_t *buf); -typedef struct EcsOpaque { - ecs_entity_t as_type; /**< Type that describes the serialized output */ - ecs_meta_serialize_t serialize; /**< Serialize action */ +/** Similar as ecs_ptr_to_expr(), but serializes values to string. + * Whereas the output of ecs_ptr_to_expr() is a valid expression, the output of + * ecs_ptr_to_str() is a string representation of the value. In most cases the + * output of the two operations is the same, but there are some differences: + * - Strings are not quoted + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @return String with result, or NULL if failed. + */ +FLECS_API +char* ecs_ptr_to_str( + const ecs_world_t *world, + ecs_entity_t type, + const void *data); - /* Deserializer interface - * Only override the callbacks that are valid for the opaque type. If a - * deserializer attempts to assign a value type that is not supported by the - * interface, a conversion error is thrown. - */ +/** Serialize value into string buffer. + * Same as ecs_ptr_to_str(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @param buf The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_ptr_to_str_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *data, + ecs_strbuf_t *buf); - /** Assign bool value */ - void (*assign_bool)( - void *dst, - bool value); +/** Script module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsScript) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsScriptImport( + ecs_world_t *world); - /** Assign char value */ - void (*assign_char)( - void *dst, - char value); +#ifdef __cplusplus +} +#endif - /** Assign int value */ - void (*assign_int)( - void *dst, - int64_t value); +#endif - /** Assign unsigned int value */ - void (*assign_uint)( - void *dst, - uint64_t value); +/** @} */ - /** Assign float value */ - void (*assign_float)( - void *dst, - double value); +#endif - /** Assign string value */ - void (*assign_string)( - void *dst, - const char *value); +#endif - /** Assign entity value */ - void (*assign_entity)( - void *dst, - ecs_world_t *world, - ecs_entity_t entity); +#ifdef FLECS_DOC +#ifdef FLECS_NO_DOC +#error "FLECS_NO_DOC failed: DOC is required by other addons" +#endif +/** + * @file addons/doc.h + * @brief Doc module. + * + * The doc module allows for documenting entities (and thus components, systems) + * by adding brief and/or detailed descriptions as components. Documentation + * added with the doc module can be retrieved at runtime, and can be used by + * tooling such as UIs or documentation frameworks. + */ - /** Assign (component) id value */ - void (*assign_id)( - void *dst, - ecs_world_t *world, - ecs_id_t id); +#ifdef FLECS_DOC - /** Assign null value */ - void (*assign_null)( - void *dst); +#ifndef FLECS_DOC_H +#define FLECS_DOC_H - /** Clear collection elements */ - void (*clear)( - void *dst); +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif - /** Ensure & get collection element */ - void* (*ensure_element)( - void *dst, - size_t elem); +#ifdef __cplusplus +extern "C" { +#endif - /** Ensure & get element */ - void* (*ensure_member)( - void *dst, - const char *member); +/** + * @defgroup c_addons_doc Doc + * @ingroup c_addons + * Utilities for documenting entities, components and systems. + * + * @{ + */ - /** Return number of elements */ - size_t (*count)( - const void *dst); - - /** Resize to number of elements */ - void (*resize)( - void *dst, - size_t count); -} EcsOpaque; +FLECS_API extern const ecs_entity_t ecs_id(EcsDocDescription); /**< Component id for EcsDocDescription. */ +/** Tag for adding a UUID to entities. + * Added to an entity as (EcsDocDescription, EcsUuid) by ecs_doc_set_uuid(). + */ +FLECS_API extern const ecs_entity_t EcsDocUuid; -/* Units */ +/** Tag for adding brief descriptions to entities. + * Added to an entity as (EcsDocDescription, EcsBrief) by ecs_doc_set_brief(). + */ +FLECS_API extern const ecs_entity_t EcsDocBrief; -/* Helper type to describe translation between two units. Note that this - * is not intended as a generic approach to unit conversions (e.g. from celsius - * to fahrenheit) but to translate between units that derive from the same base - * (e.g. meters to kilometers). - * - * Note that power is applied to the factor. When describing a translation of - * 1000, either use {factor = 1000, power = 1} or {factor = 1, power = 3}. */ -typedef struct ecs_unit_translation_t { - int32_t factor; /**< Factor to apply (e.g. "1000", "1000000", "1024") */ - int32_t power; /**< Power to apply to factor (e.g. "1", "3", "-9") */ -} ecs_unit_translation_t; +/** Tag for adding detailed descriptions to entities. + * Added to an entity as (EcsDocDescription, EcsDocDetail) by ecs_doc_set_detail(). + */ +FLECS_API extern const ecs_entity_t EcsDocDetail; -typedef struct EcsUnit { - char *symbol; - ecs_entity_t prefix; /**< Order of magnitude prefix relative to derived */ - ecs_entity_t base; /**< Base unit (e.g. "meters") */ - ecs_entity_t over; /**< Over unit (e.g. "per second") */ - ecs_unit_translation_t translation; /**< Translation for derived unit */ -} EcsUnit; +/** Tag for adding a link to entities. + * Added to an entity as (EcsDocDescription, EcsDocLink) by ecs_doc_set_link(). + */ +FLECS_API extern const ecs_entity_t EcsDocLink; -typedef struct EcsUnitPrefix { - char *symbol; /**< Symbol of prefix (e.g. "K", "M", "Ki") */ - ecs_unit_translation_t translation; /**< Translation of prefix */ -} EcsUnitPrefix; +/** Tag for adding a color to entities. + * Added to an entity as (EcsDocDescription, EcsDocColor) by ecs_doc_set_link(). + */ +FLECS_API extern const ecs_entity_t EcsDocColor; +/** Component that stores description. + * Used as pair together with the following tags to store entity documentation: + * - EcsName + * - EcsDocBrief + * - EcsDocDetail + * - EcsDocLink + * - EcsDocColor + */ +typedef struct EcsDocDescription { + char *value; +} EcsDocDescription; -/* Serializer utilities */ +/** Add UUID to entity. + * Associate entity with an (external) UUID. + * + * @param world The world. + * @param entity The entity to which to add the UUID. + * @param uuid The UUID to add. + * + * @see ecs_doc_get_uuid() + * @see flecs::doc::set_uuid() + * @see flecs::entity_builder::set_doc_uuid() + */ +FLECS_API +void ecs_doc_set_uuid( + ecs_world_t *world, + ecs_entity_t entity, + const char *uuid); -typedef enum ecs_meta_type_op_kind_t { - EcsOpArray, - EcsOpVector, - EcsOpOpaque, - EcsOpPush, - EcsOpPop, +/** Add human-readable name to entity. + * Contrary to entity names, human readable names do not have to be unique and + * can contain special characters used in the query language like '*'. + * + * @param world The world. + * @param entity The entity to which to add the name. + * @param name The name to add. + * + * @see ecs_doc_get_name() + * @see flecs::doc::set_name() + * @see flecs::entity_builder::set_doc_name() + */ +FLECS_API +void ecs_doc_set_name( + ecs_world_t *world, + ecs_entity_t entity, + const char *name); - EcsOpScope, /**< Marks last constant that can open/close a scope */ +/** Add brief description to entity. + * + * @param world The world. + * @param entity The entity to which to add the description. + * @param description The description to add. + * + * @see ecs_doc_get_brief() + * @see flecs::doc::set_brief() + * @see flecs::entity_builder::set_doc_brief() + */ +FLECS_API +void ecs_doc_set_brief( + ecs_world_t *world, + ecs_entity_t entity, + const char *description); - EcsOpEnum, - EcsOpBitmask, +/** Add detailed description to entity. + * + * @param world The world. + * @param entity The entity to which to add the description. + * @param description The description to add. + * + * @see ecs_doc_get_detail() + * @see flecs::doc::set_detail() + * @see flecs::entity_builder::set_doc_detail() + */ +FLECS_API +void ecs_doc_set_detail( + ecs_world_t *world, + ecs_entity_t entity, + const char *description); - EcsOpPrimitive, /**< Marks first constant that's a primitive */ +/** Add link to external documentation to entity. + * + * @param world The world. + * @param entity The entity to which to add the link. + * @param link The link to add. + * + * @see ecs_doc_get_link() + * @see flecs::doc::set_link() + * @see flecs::entity_builder::set_doc_link() + */ +FLECS_API +void ecs_doc_set_link( + ecs_world_t *world, + ecs_entity_t entity, + const char *link); - EcsOpBool, - EcsOpChar, - EcsOpByte, - EcsOpU8, - EcsOpU16, - EcsOpU32, - EcsOpU64, - EcsOpI8, - EcsOpI16, - EcsOpI32, - EcsOpI64, - EcsOpF32, - EcsOpF64, - EcsOpUPtr, - EcsOpIPtr, - EcsOpString, - EcsOpEntity, - EcsOpId, - EcsMetaTypeOpKindLast = EcsOpId -} ecs_meta_type_op_kind_t; +/** Add color to entity. + * UIs can use color as hint to improve visualizing entities. + * + * @param world The world. + * @param entity The entity to which to add the link. + * @param color The color to add. + * + * @see ecs_doc_get_color() + * @see flecs::doc::set_color() + * @see flecs::entity_builder::set_doc_color() + */ +FLECS_API +void ecs_doc_set_color( + ecs_world_t *world, + ecs_entity_t entity, + const char *color); -typedef struct ecs_meta_type_op_t { - ecs_meta_type_op_kind_t kind; - ecs_size_t offset; /**< Offset of current field */ - int32_t count; - const char *name; /**< Name of value (only used for struct members) */ - int32_t op_count; /**< Number of operations until next field or end */ - ecs_size_t size; /**< Size of type of operation */ - ecs_entity_t type; /**< Type entity */ - int32_t member_index; /**< Index of member in struct */ - ecs_hashmap_t *members; /**< string -> member index (structs only) */ -} ecs_meta_type_op_t; +/** Get UUID from entity. + * @param world The world. + * @param entity The entity from which to get the UUID. + * @return The UUID. + * + * @see ecs_doc_set_uuid() + * @see flecs::doc::get_uuid() + * @see flecs::entity_view::get_doc_uuid() + */ +FLECS_API +const char* ecs_doc_get_uuid( + const ecs_world_t *world, + ecs_entity_t entity); -typedef struct EcsMetaTypeSerialized { - ecs_vec_t ops; /**< vector */ -} EcsMetaTypeSerialized; +/** Get human readable name from entity. + * If entity does not have an explicit human readable name, this operation will + * return the entity name. + * + * To test if an entity has a human readable name, use: + * + * @code + * ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsName); + * @endcode + * + * Or in C++: + * + * @code + * e.has(flecs::Name); + * @endcode + * + * @param world The world. + * @param entity The entity from which to get the name. + * @return The name. + * + * @see ecs_doc_set_name() + * @see flecs::doc::get_name() + * @see flecs::entity_view::get_doc_name() + */ +FLECS_API +const char* ecs_doc_get_name( + const ecs_world_t *world, + ecs_entity_t entity); +/** Get brief description from entity. + * + * @param world The world. + * @param entity The entity from which to get the description. + * @return The description. + * + * @see ecs_doc_set_brief() + * @see flecs::doc::get_brief() + * @see flecs::entity_view::get_doc_brief() + */ +FLECS_API +const char* ecs_doc_get_brief( + const ecs_world_t *world, + ecs_entity_t entity); -/* Deserializer utilities */ +/** Get detailed description from entity. + * + * @param world The world. + * @param entity The entity from which to get the description. + * @return The description. + * + * @see ecs_doc_set_detail() + * @see flecs::doc::get_detail() + * @see flecs::entity_view::get_doc_detail() + */ +FLECS_API +const char* ecs_doc_get_detail( + const ecs_world_t *world, + ecs_entity_t entity); -#define ECS_META_MAX_SCOPE_DEPTH (32) /* >32 levels of nesting is not sane */ +/** Get link to external documentation from entity. + * + * @param world The world. + * @param entity The entity from which to get the link. + * @return The link. + * + * @see ecs_doc_set_link() + * @see flecs::doc::get_link() + * @see flecs::entity_view::get_doc_link() + */ +FLECS_API +const char* ecs_doc_get_link( + const ecs_world_t *world, + ecs_entity_t entity); -typedef struct ecs_meta_scope_t { - ecs_entity_t type; /**< The type being iterated */ - ecs_meta_type_op_t *ops; /**< The type operations (see ecs_meta_type_op_t) */ - int32_t op_count; /**< Number of operations in ops array to process */ - int32_t op_cur; /**< Current operation */ - int32_t elem_cur; /**< Current element (for collections) */ - int32_t prev_depth; /**< Depth to restore, in case dotmember was used */ - void *ptr; /**< Pointer to the value being iterated */ - - const EcsComponent *comp; /**< Pointer to component, in case size/alignment is needed */ - const EcsOpaque *opaque; /**< Opaque type interface */ - ecs_vec_t *vector; /**< Current vector, in case a vector is iterated */ - ecs_hashmap_t *members; /**< string -> member index */ - bool is_collection; /**< Is the scope iterating elements? */ - bool is_inline_array; /**< Is the scope iterating an inline array? */ - bool is_empty_scope; /**< Was scope populated (for collections) */ -} ecs_meta_scope_t; +/** Get color from entity. + * + * @param world The world. + * @param entity The entity from which to get the color. + * @return The color. + * + * @see ecs_doc_set_color() + * @see flecs::doc::get_color() + * @see flecs::entity_view::get_doc_color() + */ +FLECS_API +const char* ecs_doc_get_color( + const ecs_world_t *world, + ecs_entity_t entity); -/** Type that enables iterating/populating a value using reflection data */ -typedef struct ecs_meta_cursor_t { - const ecs_world_t *world; - ecs_meta_scope_t scope[ECS_META_MAX_SCOPE_DEPTH]; - int32_t depth; - bool valid; - bool is_primitive_scope; /**< If in root scope, this allows for a push for primitive types */ +/** Doc module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsDoc) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsDocImport( + ecs_world_t *world); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +#endif + +#ifdef FLECS_META +#ifdef FLECS_NO_META +#error "FLECS_NO_META failed: META is required by other addons" +#endif +/** + * @file addons/meta.h + * @brief Meta addon. + * + * The meta addon enables reflecting on component data. Types are stored as + * entities, with components that store the reflection data. A type has at least + * two components: + * + * - EcsComponent: core component, contains size & alignment + * - EcsType: component that indicates what kind of type the entity is + * + * Additionally the type may have an additional component that contains the + * reflection data for the type. For example, structs have these components: + * + * - EcsComponent + * - EcsType + * - EcsStruct + * + * Structs can be populated by adding child entities with the EcsMember + * component. Adding a child with a Member component to an entity will + * automatically add the EcsStruct component to the parent. + * + * Enums/bitmasks can be populated by adding child entities with the Constant + * tag. By default constants are automatically assigned values when they are + * added to the enum/bitmask. The parent entity must have the EcsEnum or + * EcsBitmask component before adding the constants. + * + * To create enum constants with a manual value, set (Constant, i32) to the + * desired value. To create bitmask constants with a manual value, set + * (Constant, u32) to the desired value. Constants with manual values should not + * conflict with other constants. + * + * The _init APIs are convenience wrappers around creating the entities and + * components for the types. + * + * When a type is created it automatically receives the EcsComponent and + * EcsType components. The former means that the resulting type can be + * used as a regular component: + * + * @code + * // Create Position type + * ecs_entity_t pos = ecs_struct_init(world, &(ecs_struct_desc_t){ + * .entity.name = "Position", + * .members = { + * {"x", ecs_id(ecs_f32_t)}, + * {"y", ecs_id(ecs_f32_t)} + * } + * }); + * + * // Create entity with Position component + * ecs_entity_t e = ecs_new_w_id(world, pos); + * @endcode + * + * Type entities do not have to be named. + */ - /* Custom entity lookup action for overriding default ecs_lookup_fullpath */ - ecs_entity_t (*lookup_action)(const ecs_world_t*, const char*, void*); - void *lookup_ctx; -} ecs_meta_cursor_t; +#ifdef FLECS_META -FLECS_API -ecs_meta_cursor_t ecs_meta_cursor( - const ecs_world_t *world, - ecs_entity_t type, - void *ptr); +/** + * @defgroup c_addons_meta Meta + * @ingroup c_addons + * Flecs reflection framework. + * + * @{ + */ -/** Get pointer to current field */ -FLECS_API -void* ecs_meta_get_ptr( - ecs_meta_cursor_t *cursor); +#include -/** Move cursor to next field */ -FLECS_API -int ecs_meta_next( - ecs_meta_cursor_t *cursor); +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif -/** Move cursor to a element */ -FLECS_API -int ecs_meta_elem( - ecs_meta_cursor_t *cursor, - int32_t elem); +#ifndef FLECS_META_H +#define FLECS_META_H -/** Move cursor to member */ -FLECS_API -int ecs_meta_member( - ecs_meta_cursor_t *cursor, - const char *name); +#ifdef __cplusplus +extern "C" { +#endif -/** Move cursor to member, supports dot-separated nested members */ -FLECS_API -int ecs_meta_dotmember( - ecs_meta_cursor_t *cursor, - const char *name); +/** Max number of constants/members that can be specified in desc structs. */ +#define ECS_MEMBER_DESC_CACHE_SIZE (32) -/** Push a scope (required/only valid for structs & collections) */ -FLECS_API -int ecs_meta_push( - ecs_meta_cursor_t *cursor); +/** Primitive type definitions. + * These typedefs allow the builtin primitives to be used as regular components: + * + * @code + * ecs_set(world, e, ecs_i32_t, {10}); + * @endcode + * + * Or a more useful example (create an enum constant with a manual value): + * + * @code + * ecs_set_pair_second(world, e, EcsConstant, ecs_i32_t, {10}); + * @endcode + */ + +typedef bool ecs_bool_t; /**< Builtin bool type */ +typedef char ecs_char_t; /**< Builtin char type */ +typedef unsigned char ecs_byte_t; /**< Builtin ecs_byte type */ +typedef uint8_t ecs_u8_t; /**< Builtin u8 type */ +typedef uint16_t ecs_u16_t; /**< Builtin u16 type */ +typedef uint32_t ecs_u32_t; /**< Builtin u32 type */ +typedef uint64_t ecs_u64_t; /**< Builtin u64 type */ +typedef uintptr_t ecs_uptr_t; /**< Builtin uptr type */ +typedef int8_t ecs_i8_t; /**< Builtin i8 type */ +typedef int16_t ecs_i16_t; /**< Builtin i16 type */ +typedef int32_t ecs_i32_t; /**< Builtin i32 type */ +typedef int64_t ecs_i64_t; /**< Builtin i64 type */ +typedef intptr_t ecs_iptr_t; /**< Builtin iptr type */ +typedef float ecs_f32_t; /**< Builtin f32 type */ +typedef double ecs_f64_t; /**< Builtin f64 type */ +typedef char* ecs_string_t; /**< Builtin string type */ -/** Pop a struct or collection scope (must follow a push) */ -FLECS_API -int ecs_meta_pop( - ecs_meta_cursor_t *cursor); +/* Meta module component ids */ +FLECS_API extern const ecs_entity_t ecs_id(EcsType); /**< Id for component added to all types with reflection data. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsTypeSerializer); /**< Id for component that stores a type specific serializer. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsPrimitive); /**< Id for component that stores reflection data for a primitive type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsEnum); /**< Id for component that stores reflection data for an enum type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsBitmask); /**< Id for component that stores reflection data for a bitmask type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsMember); /**< Id for component that stores reflection data for struct members. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsMemberRanges); /**< Id for component that stores min/max ranges for member values. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsStruct); /**< Id for component that stores reflection data for a struct type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsArray); /**< Id for component that stores reflection data for an array type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsVector); /**< Id for component that stores reflection data for a vector type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsOpaque); /**< Id for component that stores reflection data for an opaque type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsUnit); /**< Id for component that stores unit data. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsUnitPrefix); /**< Id for component that stores unit prefix data. */ +FLECS_API extern const ecs_entity_t EcsConstant; /**< Tag added to enum/bitmask constants. */ +FLECS_API extern const ecs_entity_t EcsQuantity; /**< Tag added to unit quantities. */ -/** Is the current scope a collection? */ -FLECS_API -bool ecs_meta_is_collection( - const ecs_meta_cursor_t *cursor); +/* Primitive type component ids */ -/** Get type of current element. */ -FLECS_API -ecs_entity_t ecs_meta_get_type( - const ecs_meta_cursor_t *cursor); +FLECS_API extern const ecs_entity_t ecs_id(ecs_bool_t); /**< Builtin boolean type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_char_t); /**< Builtin char type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_byte_t); /**< Builtin byte type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u8_t); /**< Builtin 8 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u16_t); /**< Builtin 16 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u32_t); /**< Builtin 32 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u64_t); /**< Builtin 64 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_uptr_t); /**< Builtin pointer sized unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i8_t); /**< Builtin 8 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i16_t); /**< Builtin 16 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i32_t); /**< Builtin 32 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i64_t); /**< Builtin 64 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_iptr_t); /**< Builtin pointer sized signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_f32_t); /**< Builtin 32 bit floating point type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_f64_t); /**< Builtin 64 bit floating point type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_string_t); /**< Builtin string type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_entity_t); /**< Builtin entity type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_id_t); /**< Builtin (component) id type. */ -/** Get unit of current element. */ -FLECS_API -ecs_entity_t ecs_meta_get_unit( - const ecs_meta_cursor_t *cursor); +/** Type kinds supported by meta addon */ +typedef enum ecs_type_kind_t { + EcsPrimitiveType, + EcsBitmaskType, + EcsEnumType, + EcsStructType, + EcsArrayType, + EcsVectorType, + EcsOpaqueType, + EcsTypeKindLast = EcsOpaqueType +} ecs_type_kind_t; -/** Get member name of current member */ -FLECS_API -const char* ecs_meta_get_member( - const ecs_meta_cursor_t *cursor); +/** Component that is automatically added to every type with the right kind. */ +typedef struct EcsType { + ecs_type_kind_t kind; /**< Type kind. */ + bool existing; /**< Did the type exist or is it populated from reflection */ + bool partial; /**< Is the reflection data a partial type description */ +} EcsType; -/** Get member entity of current member */ -FLECS_API -ecs_entity_t ecs_meta_get_member_id( - const ecs_meta_cursor_t *cursor); +/** Primitive type kinds supported by meta addon */ +typedef enum ecs_primitive_kind_t { + EcsBool = 1, + EcsChar, + EcsByte, + EcsU8, + EcsU16, + EcsU32, + EcsU64, + EcsI8, + EcsI16, + EcsI32, + EcsI64, + EcsF32, + EcsF64, + EcsUPtr, + EcsIPtr, + EcsString, + EcsEntity, + EcsId, + EcsPrimitiveKindLast = EcsId +} ecs_primitive_kind_t; -/* The set functions assign the field with the specified value. If the value - * does not have the same type as the field, it will be cased to the field type. - * If no valid conversion is available, the operation will fail. */ +/** Component added to primitive types */ +typedef struct EcsPrimitive { + ecs_primitive_kind_t kind; /**< Primitive type kind. */ +} EcsPrimitive; -/** Set field with boolean value */ -FLECS_API -int ecs_meta_set_bool( - ecs_meta_cursor_t *cursor, - bool value); +/** Component added to member entities */ +typedef struct EcsMember { + ecs_entity_t type; /**< Member type. */ + int32_t count; /**< Number of elements (for inline arrays). */ + ecs_entity_t unit; /**< Member unit. */ + int32_t offset; /**< Member offset. */ + bool use_offset; /**< If offset should be explicitly used. */ +} EcsMember; -/** Set field with char value */ -FLECS_API -int ecs_meta_set_char( - ecs_meta_cursor_t *cursor, - char value); +/** Type expressing a range for a member value */ +typedef struct ecs_member_value_range_t { + double min; /**< Min member value. */ + double max; /**< Max member value. */ +} ecs_member_value_range_t; -/** Set field with int value */ -FLECS_API -int ecs_meta_set_int( - ecs_meta_cursor_t *cursor, - int64_t value); +/** Component added to member entities to express valid value ranges */ +typedef struct EcsMemberRanges { + ecs_member_value_range_t value; /**< Member value range. */ + ecs_member_value_range_t warning; /**< Member value warning range. */ + ecs_member_value_range_t error; /**< Member value error range. */ +} EcsMemberRanges; -/** Set field with uint value */ -FLECS_API -int ecs_meta_set_uint( - ecs_meta_cursor_t *cursor, - uint64_t value); +/** Element type of members vector in EcsStruct */ +typedef struct ecs_member_t { + /** Must be set when used with ecs_struct_desc_t */ + const char *name; -/** Set field with float value */ -FLECS_API -int ecs_meta_set_float( - ecs_meta_cursor_t *cursor, - double value); + /** Member type. */ + ecs_entity_t type; -/** Set field with string value */ -FLECS_API -int ecs_meta_set_string( - ecs_meta_cursor_t *cursor, - const char *value); + /** Element count (for inline arrays). May be set when used with ecs_struct_desc_t */ + int32_t count; -/** Set field with string literal value (has enclosing "") */ -FLECS_API -int ecs_meta_set_string_literal( - ecs_meta_cursor_t *cursor, - const char *value); + /** May be set when used with ecs_struct_desc_t. Member offset. */ + int32_t offset; -/** Set field with entity value */ -FLECS_API -int ecs_meta_set_entity( - ecs_meta_cursor_t *cursor, - ecs_entity_t value); + /** May be set when used with ecs_struct_desc_t, will be auto-populated if + * type entity is also a unit */ + ecs_entity_t unit; -/** Set field with (component) id value */ -FLECS_API -int ecs_meta_set_id( - ecs_meta_cursor_t *cursor, - ecs_id_t value); + /** Set to true to prevent automatic offset computation. This option should + * be used when members are registered out of order or where calculation of + * member offsets doesn't match C type offsets. */ + bool use_offset; -/** Set field with (component) id value */ -FLECS_API -int ecs_meta_set_component( - ecs_meta_cursor_t *cursor, - ecs_id_t value); + /** Numerical range that specifies which values member can assume. This + * range may be used by UI elements such as a progress bar or slider. The + * value of a member should not exceed this range. */ + ecs_member_value_range_t range; -/** Set field with null value */ -FLECS_API -int ecs_meta_set_null( - ecs_meta_cursor_t *cursor); + /** Numerical range outside of which the value represents an error. This + * range may be used by UI elements to style a value. */ + ecs_member_value_range_t error_range; -/** Set field with dynamic value */ -FLECS_API -int ecs_meta_set_value( - ecs_meta_cursor_t *cursor, - const ecs_value_t *value); + /** Numerical range outside of which the value represents an warning. This + * range may be used by UI elements to style a value. */ + ecs_member_value_range_t warning_range; -/* Functions for getting members. */ + /** Should not be set by ecs_struct_desc_t */ + ecs_size_t size; -/** Get field value as boolean. */ -FLECS_API -bool ecs_meta_get_bool( - const ecs_meta_cursor_t *cursor); + /** Should not be set by ecs_struct_desc_t */ + ecs_entity_t member; +} ecs_member_t; -/** Get field value as char. */ -FLECS_API -char ecs_meta_get_char( - const ecs_meta_cursor_t *cursor); +/** Component added to struct type entities */ +typedef struct EcsStruct { + /** Populated from child entities with Member component */ + ecs_vec_t members; /* vector */ +} EcsStruct; -/** Get field value as signed integer. */ -FLECS_API -int64_t ecs_meta_get_int( - const ecs_meta_cursor_t *cursor); +/** Type that describes an enum constant */ +typedef struct ecs_enum_constant_t { + /** Must be set when used with ecs_enum_desc_t */ + const char *name; -/** Get field value as unsigned integer. */ -FLECS_API -uint64_t ecs_meta_get_uint( - const ecs_meta_cursor_t *cursor); + /** May be set when used with ecs_enum_desc_t */ + int32_t value; -/** Get field value as float. */ -FLECS_API -double ecs_meta_get_float( - const ecs_meta_cursor_t *cursor); + /** Should not be set by ecs_enum_desc_t */ + ecs_entity_t constant; +} ecs_enum_constant_t; -/** Get field value as string. - * This operation does not perform conversions. If the field is not a string, - * this operation will fail. - */ -FLECS_API -const char* ecs_meta_get_string( - const ecs_meta_cursor_t *cursor); +/** Component added to enum type entities */ +typedef struct EcsEnum { + /** Populated from child entities with Constant component */ + ecs_map_t constants; /**< map */ +} EcsEnum; -/** Get field value as entity. - * This operation does not perform conversions. */ -FLECS_API -ecs_entity_t ecs_meta_get_entity( - const ecs_meta_cursor_t *cursor); +/** Type that describes an bitmask constant */ +typedef struct ecs_bitmask_constant_t { + /** Must be set when used with ecs_bitmask_desc_t */ + const char *name; -/** Get field value as (component) id. - * This operation can convert from an entity. */ -ecs_id_t ecs_meta_get_id( - const ecs_meta_cursor_t *cursor); + /** May be set when used with ecs_bitmask_desc_t */ + ecs_flags32_t value; -/** Convert pointer of primitive kind to float. */ -FLECS_API -double ecs_meta_ptr_to_float( - ecs_primitive_kind_t type_kind, - const void *ptr); + /** Should not be set by ecs_bitmask_desc_t */ + ecs_entity_t constant; +} ecs_bitmask_constant_t; -/* API functions for creating meta types */ +/** Component added to bitmask type entities */ +typedef struct EcsBitmask { + /* Populated from child entities with Constant component */ + ecs_map_t constants; /**< map */ +} EcsBitmask; -/** Used with ecs_primitive_init. */ -typedef struct ecs_primitive_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_primitive_kind_t kind; -} ecs_primitive_desc_t; +/** Component added to array type entities */ +typedef struct EcsArray { + ecs_entity_t type; /**< Element type */ + int32_t count; /**< Number of elements */ +} EcsArray; -/** Create a new primitive type */ -FLECS_API -ecs_entity_t ecs_primitive_init( - ecs_world_t *world, - const ecs_primitive_desc_t *desc); +/** Component added to vector type entities */ +typedef struct EcsVector { + ecs_entity_t type; /**< Element type */ +} EcsVector; -/** Used with ecs_enum_init. */ -typedef struct ecs_enum_desc_t { - 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; -/** Create a new enum type */ -FLECS_API -ecs_entity_t ecs_enum_init( - ecs_world_t *world, - const ecs_enum_desc_t *desc); +/* Opaque type support */ +#if !defined(__cplusplus) || !defined(FLECS_CPP) -/** Used with ecs_bitmask_init. */ -typedef struct ecs_bitmask_desc_t { - 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; +/** Serializer interface */ +typedef struct ecs_serializer_t { + /* Serialize value */ + int (*value)( + const struct ecs_serializer_t *ser, /**< Serializer */ + ecs_entity_t type, /**< Type of the value to serialize */ + const void *value); /**< Pointer to the value to serialize */ -/** Create a new bitmask type */ -FLECS_API -ecs_entity_t ecs_bitmask_init( - ecs_world_t *world, - const ecs_bitmask_desc_t *desc); + /* Serialize member */ + int (*member)( + const struct ecs_serializer_t *ser, /**< Serializer */ + const char *member); /**< Member name */ + const ecs_world_t *world; /**< The world. */ + void *ctx; /**< Serializer context. */ +} ecs_serializer_t; -/** Used with ecs_array_init. */ -typedef struct ecs_array_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_entity_t type; - int32_t count; -} ecs_array_desc_t; +#elif defined(__cplusplus) -/** Create a new array type */ -FLECS_API -ecs_entity_t ecs_array_init( - ecs_world_t *world, - const ecs_array_desc_t *desc); +} /* extern "C" { */ +/** Serializer interface (same layout as C, but with convenience methods) */ +typedef struct ecs_serializer_t { + /* Serialize value */ + int (*value_)( + const struct ecs_serializer_t *ser, + ecs_entity_t type, + const void *value); -/** Used with ecs_vector_init. */ -typedef struct ecs_vector_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_entity_t type; -} ecs_vector_desc_t; + /* Serialize member */ + int (*member_)( + const struct ecs_serializer_t *ser, + const char *name); -/** Create a new vector type */ -FLECS_API -ecs_entity_t ecs_vector_init( - ecs_world_t *world, - const ecs_vector_desc_t *desc); + /* Serialize value */ + int value(ecs_entity_t type, const void *value) const; + /* Serialize value */ + template + int value(const T& value) const; -/** Used with ecs_struct_init. */ -typedef struct ecs_struct_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_member_t members[ECS_MEMBER_DESC_CACHE_SIZE]; -} ecs_struct_desc_t; + /* Serialize member */ + int member(const char *name) const; -/** Create a new struct type */ -FLECS_API -ecs_entity_t ecs_struct_init( - ecs_world_t *world, - const ecs_struct_desc_t *desc); + const ecs_world_t *world; + void *ctx; +} ecs_serializer_t; -/** Used with ecs_opaque_init. */ -typedef struct ecs_opaque_desc_t { - ecs_entity_t entity; - EcsOpaque type; -} ecs_opaque_desc_t; +extern "C" { +#endif -/** 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) +/** Callback invoked serializing an opaque type. */ +typedef int (*ecs_meta_serialize_t)( + const ecs_serializer_t *ser, + const void *src); /**< Pointer to value to serialize */ + +/** Opaque type reflection data. + * An opaque type is a type with an unknown layout that can be mapped to a type + * known to the reflection framework. See the opaque type reflection examples. */ -FLECS_API -ecs_entity_t ecs_opaque_init( - ecs_world_t *world, - const ecs_opaque_desc_t *desc); +typedef struct EcsOpaque { + ecs_entity_t as_type; /**< Type that describes the serialized output */ + ecs_meta_serialize_t serialize; /**< Serialize action */ -/** Used with ecs_unit_init. */ -typedef struct ecs_unit_desc_t { - /** Existing entity to associate with unit (optional) */ - ecs_entity_t entity; + /* Deserializer interface + * Only override the callbacks that are valid for the opaque type. If a + * deserializer attempts to assign a value type that is not supported by the + * interface, a conversion error is thrown. + */ - /** Unit symbol, e.g. "m", "%", "g". (optional) */ - const char *symbol; + /** Assign bool value */ + void (*assign_bool)( + void *dst, + bool value); - /** Unit quantity, e.g. distance, percentage, weight. (optional) */ - ecs_entity_t quantity; + /** Assign char value */ + void (*assign_char)( + void *dst, + char value); - /** Base unit, e.g. "meters" (optional) */ - ecs_entity_t base; + /** Assign int value */ + void (*assign_int)( + void *dst, + int64_t value); - /** Over unit, e.g. "per second" (optional) */ - ecs_entity_t over; + /** Assign unsigned int value */ + void (*assign_uint)( + void *dst, + uint64_t value); - /** Translation to apply to derived unit (optional) */ - ecs_unit_translation_t translation; + /** Assign float value */ + void (*assign_float)( + void *dst, + double value); - /** Prefix indicating order of magnitude relative to the derived unit. If set - * together with "translation", the values must match. If translation is not - * set, setting prefix will autopopulate it. - * Additionally, setting the prefix will enforce that the symbol (if set) - * is consistent with the prefix symbol + symbol of the derived unit. If the - * symbol is not set, it will be auto populated. */ - ecs_entity_t prefix; -} ecs_unit_desc_t; + /** Assign string value */ + void (*assign_string)( + void *dst, + const char *value); -/** Create a new unit */ -FLECS_API -ecs_entity_t ecs_unit_init( - ecs_world_t *world, - const ecs_unit_desc_t *desc); + /** Assign entity value */ + void (*assign_entity)( + void *dst, + ecs_world_t *world, + ecs_entity_t entity); -/** Used with ecs_unit_prefix_init. */ -typedef struct ecs_unit_prefix_desc_t { - /** Existing entity to associate with unit prefix (optional) */ - ecs_entity_t entity; + /** Assign (component) id value */ + void (*assign_id)( + void *dst, + ecs_world_t *world, + ecs_id_t id); - /** Unit symbol, e.g. "m", "%", "g". (optional) */ - const char *symbol; + /** Assign null value */ + void (*assign_null)( + void *dst); - /** Translation to apply to derived unit (optional) */ - ecs_unit_translation_t translation; -} ecs_unit_prefix_desc_t; + /** Clear collection elements */ + void (*clear)( + void *dst); -/** Create a new unit prefix */ -FLECS_API -ecs_entity_t ecs_unit_prefix_init( - ecs_world_t *world, - const ecs_unit_prefix_desc_t *desc); + /** Ensure & get collection element */ + void* (*ensure_element)( + void *dst, + size_t elem); -/** Create a new quantity */ -FLECS_API -ecs_entity_t ecs_quantity_init( - ecs_world_t *world, - const ecs_entity_desc_t *desc); + /** Ensure & get element */ + void* (*ensure_member)( + void *dst, + const char *member); -/* Convenience macros */ + /** Return number of elements */ + size_t (*count)( + const void *dst); -#define ecs_primitive(world, ...)\ - ecs_primitive_init(world, &(ecs_primitive_desc_t) __VA_ARGS__ ) + /** Resize to number of elements */ + void (*resize)( + void *dst, + size_t count); +} EcsOpaque; -#define ecs_enum(world, ...)\ - ecs_enum_init(world, &(ecs_enum_desc_t) __VA_ARGS__ ) -#define ecs_bitmask(world, ...)\ - ecs_bitmask_init(world, &(ecs_bitmask_desc_t) __VA_ARGS__ ) +/* Units */ + +/** Helper type to describe translation between two units. Note that this + * is not intended as a generic approach to unit conversions (e.g. from celsius + * to fahrenheit) but to translate between units that derive from the same base + * (e.g. meters to kilometers). + * + * Note that power is applied to the factor. When describing a translation of + * 1000, either use {factor = 1000, power = 1} or {factor = 1, power = 3}. */ +typedef struct ecs_unit_translation_t { + int32_t factor; /**< Factor to apply (e.g. "1000", "1000000", "1024") */ + int32_t power; /**< Power to apply to factor (e.g. "1", "3", "-9") */ +} ecs_unit_translation_t; -#define ecs_array(world, ...)\ - ecs_array_init(world, &(ecs_array_desc_t) __VA_ARGS__ ) +/** Component that stores unit data. */ +typedef struct EcsUnit { + char *symbol; /**< Unit symbol. */ + ecs_entity_t prefix; /**< Order of magnitude prefix relative to derived */ + ecs_entity_t base; /**< Base unit (e.g. "meters") */ + ecs_entity_t over; /**< Over unit (e.g. "per second") */ + ecs_unit_translation_t translation; /**< Translation for derived unit */ +} EcsUnit; -#define ecs_vector(world, ...)\ - ecs_vector_init(world, &(ecs_vector_desc_t) __VA_ARGS__ ) +/** Component that stores unit prefix data. */ +typedef struct EcsUnitPrefix { + char *symbol; /**< Symbol of prefix (e.g. "K", "M", "Ki") */ + ecs_unit_translation_t translation; /**< Translation of prefix */ +} EcsUnitPrefix; -#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__ ) +/* Serializer utilities */ -#define ecs_unit(world, ...)\ - ecs_unit_init(world, &(ecs_unit_desc_t) __VA_ARGS__ ) +/** Serializer instruction opcodes. + * The meta type serializer works by generating a flattened array with + * instructions that tells a serializer what kind of fields can be found in a + * type at which offsets. +*/ +typedef enum ecs_meta_type_op_kind_t { + EcsOpArray, + EcsOpVector, + EcsOpOpaque, + EcsOpPush, + EcsOpPop, -#define ecs_unit_prefix(world, ...)\ - ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t) __VA_ARGS__ ) + EcsOpScope, /**< Marks last constant that can open/close a scope */ -#define ecs_quantity(world, ...)\ - ecs_quantity_init(world, &(ecs_entity_desc_t) __VA_ARGS__ ) + EcsOpEnum, + EcsOpBitmask, -/* Module import */ -FLECS_API -void FlecsMetaImport( - ecs_world_t *world); + EcsOpPrimitive, /**< Marks first constant that's a primitive */ -#ifdef __cplusplus -} -#endif + EcsOpBool, + EcsOpChar, + EcsOpByte, + EcsOpU8, + EcsOpU16, + EcsOpU32, + EcsOpU64, + EcsOpI8, + EcsOpI16, + EcsOpI32, + EcsOpI64, + EcsOpF32, + EcsOpF64, + EcsOpUPtr, + EcsOpIPtr, + EcsOpString, + EcsOpEntity, + EcsOpId, + EcsMetaTypeOpKindLast = EcsOpId +} ecs_meta_type_op_kind_t; -#endif +/** Meta type serializer instruction data. */ +typedef struct ecs_meta_type_op_t { + ecs_meta_type_op_kind_t kind; /**< Instruction opcode. */ + ecs_size_t offset; /**< Offset of current field */ + int32_t count; /**< Number of elements (for inline arrays). */ + const char *name; /**< Name of value (only used for struct members) */ + int32_t op_count; /**< Number of operations until next field or end */ + ecs_size_t size; /**< Size of type of operation */ + ecs_entity_t type; /**< Type entity */ + int32_t member_index; /**< Index of member in struct */ + ecs_hashmap_t *members; /**< string -> member index (structs only) */ +} ecs_meta_type_op_t; -/** @} */ +/** Component that stores the type serializer. + * Added to all types with reflection data. + */ +typedef struct EcsTypeSerializer { + ecs_vec_t ops; /**< vector */ +} EcsTypeSerializer; -#endif -#endif +/* Deserializer utilities */ -#ifdef FLECS_EXPR -#ifdef FLECS_NO_EXPR -#error "FLECS_NO_EXPR failed: EXPR is required by other addons" -#endif -/** - * @file addons/expr.h - * @brief Flecs expression parser addon. - * - * Parse expression strings into component values. The notation is similar to - * JSON but with a smaller footprint, native support for (large) integer types, - * character types, enumerations, bitmasks and entity identifiers. - * - * Examples: - * - * Member names: - * {x: 10, y: 20} - * - * No member names (uses member ordering): - * {10, 20} - * - * Enum values: - * {color: Red} - * - * Bitmask values: - * {toppings: Lettuce|Tomato} - * - * Collections: - * {points: [10, 20, 30]} - * - * Nested objects: - * {start: {x: 10, y: 20}, stop: {x: 30, y: 40}} - * +/** Maximum level of type nesting. + * >32 levels of nesting is not sane. */ +#define ECS_META_MAX_SCOPE_DEPTH (32) -#ifdef FLECS_EXPR - -#ifndef FLECS_META -#define FLECS_META -#endif - -#ifndef FLECS_PARSER -#define FLECS_PARSER -#endif +/** Type with information about currently serialized scope. */ +typedef struct ecs_meta_scope_t { + ecs_entity_t type; /**< The type being iterated */ + ecs_meta_type_op_t *ops; /**< The type operations (see ecs_meta_type_op_t) */ + int32_t op_count; /**< Number of operations in ops array to process */ + int32_t op_cur; /**< Current operation */ + int32_t elem_cur; /**< Current element (for collections) */ + int32_t prev_depth; /**< Depth to restore, in case dotmember was used */ + void *ptr; /**< Pointer to the value being iterated */ + const EcsComponent *comp; /**< Pointer to component, in case size/alignment is needed */ + const EcsOpaque *opaque; /**< Opaque type interface */ + ecs_vec_t *vector; /**< Current vector, in case a vector is iterated */ + ecs_hashmap_t *members; /**< string -> member index */ + bool is_collection; /**< Is the scope iterating elements? */ + bool is_inline_array; /**< Is the scope iterating an inline array? */ + bool is_empty_scope; /**< Was scope populated (for collections) */ +} ecs_meta_scope_t; -#ifndef FLECS_EXPR_H -#define FLECS_EXPR_H +/** Type that enables iterating/populating a value using reflection data. */ +typedef struct ecs_meta_cursor_t { + const ecs_world_t *world; /**< The world. */ + ecs_meta_scope_t scope[ECS_META_MAX_SCOPE_DEPTH]; /**< Cursor scope stack. */ + int32_t depth; /**< Current scope depth. */ + bool valid; /**< Does the cursor point to a valid field. */ + bool is_primitive_scope; /**< If in root scope, this allows for a push for primitive types */ -#ifdef __cplusplus -extern "C" { -#endif + /** Custom entity lookup action for overriding default ecs_lookup */ + ecs_entity_t (*lookup_action)(const ecs_world_t*, const char*, void*); + void *lookup_ctx; /**< Context for lookup_action */ +} ecs_meta_cursor_t; -/** - * @defgroup c_addons_expr Expr - * @brief Serialize/deserialize values to string. +/** Create meta cursor. + * A meta cursor allows for walking over, reading and writing a value without + * having to know its type at compile time. * - * \ingroup c_addons - * @{ + * When a value is assigned through the cursor API, it will get converted to + * the actual value of the underlying type. This allows the underlying type to + * change without having to update the serialized data. For example, an integer + * field can be set by a string, a floating point can be set as integer etc. + * + * @param world The world. + * @param type The type of the value. + * @param ptr Pointer to the value. + * @return A meta cursor for the value. */ +FLECS_API +ecs_meta_cursor_t ecs_meta_cursor( + const ecs_world_t *world, + ecs_entity_t type, + void *ptr); -/** Write an escaped character. - * Write a character to an output string, insert escape character if necessary. +/** Get pointer to current field. * - * @param out The string to write the character to. - * @param in The input character. - * @param delimiter The delimiter used (for example '"') - * @return Pointer to the character after the last one written. + * @param cursor The cursor. + * @return A pointer to the current field. */ FLECS_API -char* ecs_chresc( - char *out, - char in, - char delimiter); +void* ecs_meta_get_ptr( + ecs_meta_cursor_t *cursor); -/** Parse an escaped character. - * Parse a character with a potential escape sequence. +/** Move cursor to next field. * - * @param in Pointer to character in input string. - * @param out Output string. - * @return Pointer to the character after the last one read. - */ -const char* ecs_chrparse( - const char *in, - char *out); + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_next( + ecs_meta_cursor_t *cursor); -/** Write an escaped string. - * Write an input string to an output string, escape characters where necessary. - * To determine the size of the output string, call the operation with a NULL - * argument for 'out', and use the returned size to allocate a string that is - * large enough. +/** Move cursor to a field. * - * @param out Pointer to output string (must be). - * @param size Maximum number of characters written to output. - * @param delimiter The delimiter used (for example '"'). - * @param in The input string. - * @return The number of characters that (would) have been written. + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. */ FLECS_API -ecs_size_t ecs_stresc( - char *out, - ecs_size_t size, - char delimiter, - const char *in); +int ecs_meta_elem( + ecs_meta_cursor_t *cursor, + int32_t elem); -/** Return escaped string. - * Return escaped version of input string. Same as ecs_stresc, but returns an - * allocated string of the right size. +/** Move cursor to member. * - * @param delimiter The delimiter used (for example '"'). - * @param in The input string. - * @return Escaped string. + * @param cursor The cursor. + * @param name The name of the member. + * @return Zero if success, non-zero if failed. */ FLECS_API -char* ecs_astresc( - char delimiter, - const char *in); - -/** Storage for parser variables. Variables make it possible to parameterize - * expression strings, and are referenced with the $ operator (e.g. $var). */ -typedef struct ecs_expr_var_t { - char *name; - ecs_value_t value; - bool owned; /* Set to false if ecs_vars_t should not take ownership of var */ -} ecs_expr_var_t; - -typedef struct ecs_expr_var_scope_t { - ecs_hashmap_t var_index; - ecs_vec_t vars; - struct ecs_expr_var_scope_t *parent; -} ecs_expr_var_scope_t; +int ecs_meta_member( + ecs_meta_cursor_t *cursor, + const char *name); -typedef struct ecs_vars_t { - ecs_world_t *world; - ecs_expr_var_scope_t root; - ecs_expr_var_scope_t *cur; -} ecs_vars_t; +/** Move cursor to member. + * Same as ecs_meta_member(), but with support for "foo.bar" syntax. + * + * @param cursor The cursor. + * @param name The name of the member. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_dotmember( + ecs_meta_cursor_t *cursor, + const char *name); -/** Init variable storage */ +/** Push a scope (required/only valid for structs & collections). + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ FLECS_API -void ecs_vars_init( - ecs_world_t *world, - ecs_vars_t *vars); +int ecs_meta_push( + ecs_meta_cursor_t *cursor); -/** Cleanup variable storage */ +/** Pop a struct or collection scope (must follow a push). + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ FLECS_API -void ecs_vars_fini( - ecs_vars_t *vars); +int ecs_meta_pop( + ecs_meta_cursor_t *cursor); -/** Push variable scope */ +/** Is the current scope a collection?. + * + * @param cursor The cursor. + * @return True if current scope is a collection, false if not. + */ FLECS_API -void ecs_vars_push( - ecs_vars_t *vars); +bool ecs_meta_is_collection( + const ecs_meta_cursor_t *cursor); -/** Pop variable scope */ +/** Get type of current field. + * + * @param cursor The cursor. + * @return The type of the current field. + */ FLECS_API -int ecs_vars_pop( - ecs_vars_t *vars); +ecs_entity_t ecs_meta_get_type( + const ecs_meta_cursor_t *cursor); -/** Declare variable in current scope */ +/** Get unit of current field. + * + * @param cursor The cursor. + * @return The unit of the current field. + */ FLECS_API -ecs_expr_var_t* ecs_vars_declare( - ecs_vars_t *vars, - const char *name, - ecs_entity_t type); +ecs_entity_t ecs_meta_get_unit( + const ecs_meta_cursor_t *cursor); -/** Declare variable in current scope from value. - * This operation takes ownership of the value. The value pointer must be - * allocated with ecs_value_new. +/** Get member name of current field. + * + * @param cursor The cursor. + * @return The member name of the current field. */ FLECS_API -ecs_expr_var_t* ecs_vars_declare_w_value( - ecs_vars_t *vars, - const char *name, - ecs_value_t *value); +const char* ecs_meta_get_member( + const ecs_meta_cursor_t *cursor); -/** Lookup variable in scope and parent scopes */ +/** Get member entity of current field. + * + * @param cursor The cursor. + * @return The member entity of the current field. + */ FLECS_API -ecs_expr_var_t* ecs_vars_lookup( - const ecs_vars_t *vars, - const char *name); +ecs_entity_t ecs_meta_get_member_id( + const ecs_meta_cursor_t *cursor); -/** Used with ecs_parse_expr. */ -typedef struct ecs_parse_expr_desc_t { - const char *name; - const char *expr; - ecs_entity_t (*lookup_action)( - const ecs_world_t*, - const char *value, - void *ctx); - void *lookup_ctx; - ecs_vars_t *vars; -} ecs_parse_expr_desc_t; +/* The set functions assign the field with the specified value. If the value + * does not have the same type as the field, it will be cased to the field type. + * If no valid conversion is available, the operation will fail. */ -/** Parse expression into value. - * This operation parses a flecs expression into the provided pointer. The - * memory pointed to must be large enough to contain a value of the used type. +/** Set field with boolean value. * - * If no type and pointer are provided for the value argument, the operation - * will discover the type from the expression and allocate storage for the - * value. The allocated value must be freed with ecs_value_free. + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_bool( + ecs_meta_cursor_t *cursor, + bool value); + +/** Set field with char value. * - * @param world The world. - * @param ptr The pointer to the expression to parse. - * @param value The value containing type & pointer to write to. - * @param desc Configuration parameters for deserializer. - * @return Pointer to the character after the last one read, or NULL if failed. + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. */ FLECS_API -const char* ecs_parse_expr( - ecs_world_t *world, - const char *ptr, - ecs_value_t *value, - const ecs_parse_expr_desc_t *desc); +int ecs_meta_set_char( + ecs_meta_cursor_t *cursor, + char value); -/** Serialize value into expression string. - * This operation serializes a value of the provided type to a string. The - * memory pointed to must be large enough to contain a value of the used type. +/** Set field with int value. * - * @param world The world. - * @param type The type of the value to serialize. - * @param data The value to serialize. - * @return String with expression, or NULL if failed. + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. */ FLECS_API -char* ecs_ptr_to_expr( - const ecs_world_t *world, - ecs_entity_t type, - const void *data); +int ecs_meta_set_int( + ecs_meta_cursor_t *cursor, + int64_t value); -/** Serialize value into expression buffer. - * Same as ecs_ptr_to_expr, but serializes to an ecs_strbuf_t instance. +/** Set field with uint value. * - * @param world The world. - * @param type The type of the value to serialize. - * @param data The value to serialize. - * @param buf The strbuf to append the string to. + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_uint( + ecs_meta_cursor_t *cursor, + uint64_t value); + +/** Set field with float value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_meta_set_float( + ecs_meta_cursor_t *cursor, + double value); + +/** Set field with string value. + * + * @param cursor The cursor. + * @param value The value to set. * @return Zero if success, non-zero if failed. */ FLECS_API -int ecs_ptr_to_expr_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *data, - ecs_strbuf_t *buf); +int ecs_meta_set_string( + ecs_meta_cursor_t *cursor, + const char *value); -/** Similar as ecs_ptr_to_expr, but serializes values to string. - * Whereas the output of ecs_ptr_to_expr is a valid expression, the output of - * ecs_ptr_to_str is a string representation of the value. In most cases the - * output of the two operations is the same, but there are some differences: - * - Strings are not quoted +/** Set field with string literal value (has enclosing ""). * - * @param world The world. - * @param type The type of the value to serialize. - * @param data The value to serialize. - * @return String with result, or NULL if failed. + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. */ FLECS_API -char* ecs_ptr_to_str( - const ecs_world_t *world, - ecs_entity_t type, - const void *data); +int ecs_meta_set_string_literal( + ecs_meta_cursor_t *cursor, + const char *value); -/** Serialize value into string buffer. - * Same as ecs_ptr_to_str, but serializes to an ecs_strbuf_t instance. +/** Set field with entity value. * - * @param world The world. - * @param type The type of the value to serialize. - * @param data The value to serialize. - * @param buf The strbuf to append the string to. + * @param cursor The cursor. + * @param value The value to set. * @return Zero if success, non-zero if failed. */ FLECS_API -int ecs_ptr_to_str_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *data, - ecs_strbuf_t *buf); +int ecs_meta_set_entity( + ecs_meta_cursor_t *cursor, + ecs_entity_t value); -/** Serialize primitive value into string buffer. - * Serializes a primitive value to an ecs_strbuf_t instance. This operation can - * be reused by other serializers to avoid having to write boilerplate code that - * serializes primitive values to a string. +/** Set field with (component) id value. * - * @param world The world. - * @param kind The kind of primitive value. - * @param data The value ot serialize - * @param buf The strbuf to append the string to. + * @param cursor The cursor. + * @param value The value to set. * @return Zero if success, non-zero if failed. */ FLECS_API -int ecs_primitive_to_expr_buf( - const ecs_world_t *world, - ecs_primitive_kind_t kind, - const void *data, - ecs_strbuf_t *buf); +int ecs_meta_set_id( + ecs_meta_cursor_t *cursor, + ecs_id_t value); -/** Parse expression token. - * Expression tokens can contain more characters (such as '|') than tokens - * parsed by the query (term) parser. +/** Set field with null value. * - * @param name The name of the expression (used for debug logs). - * @param expr The full expression (used for debug logs). - * @param ptr The pointer to the expression to parse. - * @param token The buffer to write to (must have size ECS_MAX_TOKEN_SIZE) - * @return Pointer to the character after the last one read, or NULL if failed. + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. */ FLECS_API -const char *ecs_parse_expr_token( - const char *name, - const char *expr, - const char *ptr, - char *token); +int ecs_meta_set_null( + ecs_meta_cursor_t *cursor); -/** Evaluate interpolated expressions in string. - * This operation evaluates expressions in a string, and replaces them with - * their evaluated result. Supported expression formats are: - * - $variable_name - * - {expression} - * - * The $, { and } characters can be escaped with a backslash (\). +/** Set field with dynamic value. * - * @param world The world. - * @param str The string to evaluate. - * @param vars The variables to use for evaluation. + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. */ FLECS_API -char* ecs_interpolate_string( - ecs_world_t *world, - const char *str, - const ecs_vars_t *vars); +int ecs_meta_set_value( + ecs_meta_cursor_t *cursor, + const ecs_value_t *value); -/** Convert iterator to vars - * This operation converts an iterator to a variable array. This allows for - * using iterator results in expressions. The operation only converts a - * single result at a time, and does not progress the iterator. - * - * Iterator fields with data will be made available as variables with as name - * the field index (e.g. "$1"). The operation does not check if reflection data - * is registered for a field type. If no reflection data is registered for the - * type, using the field variable in expressions will fail. - * - * Field variables will only contain single elements, even if the iterator - * returns component arrays. The offset parameter can be used to specify which - * element in the component arrays to return. The offset parameter must be - * smaller than it->count. - * - * The operation will create a variable for query variables that contain a - * single entity. - * - * The operation will attempt to use existing variables. If a variable does not - * yet exist, the operation will create it. If an existing variable exists with - * a mismatching type, the operation will fail. - * - * Accessing variables after progressing the iterator or after the iterator is - * destroyed will result in undefined behavior. - * - * If vars contains a variable that is not present in the iterator, the variable - * will not be modified. +/* Functions for getting members. */ + +/** Get field value as boolean. * - * @param it The iterator to convert to variables. - * @param vars The variables to write to. - * @param offset The offset to the current element. + * @param cursor The cursor. + * @return The value of the current field. */ FLECS_API -void ecs_iter_to_vars( - const ecs_iter_t *it, - ecs_vars_t *vars, - int offset); - -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif - -#endif - -#endif +bool ecs_meta_get_bool( + const ecs_meta_cursor_t *cursor); -#ifdef FLECS_META_C -#ifdef FLECS_NO_META_C -#error "FLECS_NO_META_C failed: META_C is required by other addons" -#endif -/** - * @file addons/meta_c.h - * @brief Utility macros for populating reflection data in C. +/** Get field value as char. + * + * @param cursor The cursor. + * @return The value of the current field. */ +FLECS_API +char ecs_meta_get_char( + const ecs_meta_cursor_t *cursor); -#ifdef FLECS_META_C - -/** - * @defgroup c_addons_meta_c Meta Utilities - * @brief Macro utilities to automatically insert reflection data. +/** Get field value as signed integer. * - * \ingroup c_addons - * @{ + * @param cursor The cursor. + * @return The value of the current field. */ - -#ifndef FLECS_META -#define FLECS_META -#endif - -#ifndef FLECS_PARSER -#define FLECS_PARSER -#endif - -#ifndef FLECS_META_C_H -#define FLECS_META_C_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Macro that controls behavior of API. Usually set in module header. When the - * macro is not defined, it defaults to IMPL. */ - -/* Define variables used by reflection utilities. This should only be defined - * by the module itself, not by the code importing the module */ -/* #define ECS_META_IMPL IMPL */ - -/* Don't define variables used by reflection utilities but still declare the - * variable for the component id. This enables the reflection utilities to be - * used for global component variables, even if no reflection is used. */ -/* #define ECS_META_IMPL DECLARE */ - -/* Don't define variables used by reflection utilities. This generates an extern - * variable for the component identifier. */ -/* #define ECS_META_IMPL EXTERN */ - -/** Declare component with descriptor */ -#define ECS_META_COMPONENT(world, name)\ - ECS_COMPONENT_DEFINE(world, name);\ - ecs_meta_from_desc(world, ecs_id(name),\ - FLECS__##name##_kind, FLECS__##name##_desc) - -/** ECS_STRUCT(name, body) */ -#define ECS_STRUCT(name, ...)\ - ECS_META_IMPL_CALL(ECS_STRUCT_, ECS_META_IMPL, name, #__VA_ARGS__);\ - ECS_STRUCT_TYPE(name, __VA_ARGS__) - -/** ECS_ENUM(name, body) */ -#define ECS_ENUM(name, ...)\ - ECS_META_IMPL_CALL(ECS_ENUM_, ECS_META_IMPL, name, #__VA_ARGS__);\ - ECS_ENUM_TYPE(name, __VA_ARGS__) - -/** ECS_BITMASK(name, body) */ -#define ECS_BITMASK(name, ...)\ - ECS_META_IMPL_CALL(ECS_BITMASK_, ECS_META_IMPL, name, #__VA_ARGS__);\ - ECS_ENUM_TYPE(name, __VA_ARGS__) - -/** Macro used to mark part of type for which no reflection data is created */ -#define ECS_PRIVATE - -/** Populate meta information from type descriptor. */ FLECS_API -int ecs_meta_from_desc( - ecs_world_t *world, - ecs_entity_t component, - ecs_type_kind_t kind, - const char *desc); - - -/* Private API */ - -/* Utilities to switch between IMPL, DECLARE and EXTERN variants */ -#define ECS_META_IMPL_CALL_INNER(base, impl, name, type_desc)\ - base ## impl(name, type_desc) - -#define ECS_META_IMPL_CALL(base, impl, name, type_desc)\ - ECS_META_IMPL_CALL_INNER(base, impl, name, type_desc) - -/* ECS_STRUCT implementation */ -#define ECS_STRUCT_TYPE(name, ...)\ - typedef struct __VA_ARGS__ name - -#define ECS_STRUCT_ECS_META_IMPL ECS_STRUCT_IMPL - -#define ECS_STRUCT_IMPL(name, type_desc)\ - extern ECS_COMPONENT_DECLARE(name);\ - static const char *FLECS__##name##_desc = type_desc;\ - static ecs_type_kind_t FLECS__##name##_kind = EcsStructType;\ - ECS_COMPONENT_DECLARE(name) = 0 - -#define ECS_STRUCT_DECLARE(name, type_desc)\ - extern ECS_COMPONENT_DECLARE(name);\ - ECS_COMPONENT_DECLARE(name) = 0 - -#define ECS_STRUCT_EXTERN(name, type_desc)\ - extern ECS_COMPONENT_DECLARE(name) - - -/* ECS_ENUM implementation */ -#define ECS_ENUM_TYPE(name, ...)\ - typedef enum __VA_ARGS__ name - -#define ECS_ENUM_ECS_META_IMPL ECS_ENUM_IMPL - -#define ECS_ENUM_IMPL(name, type_desc)\ - extern ECS_COMPONENT_DECLARE(name);\ - static const char *FLECS__##name##_desc = type_desc;\ - static ecs_type_kind_t FLECS__##name##_kind = EcsEnumType;\ - ECS_COMPONENT_DECLARE(name) = 0 - -#define ECS_ENUM_DECLARE(name, type_desc)\ - extern ECS_COMPONENT_DECLARE(name);\ - ECS_COMPONENT_DECLARE(name) = 0 - -#define ECS_ENUM_EXTERN(name, type_desc)\ - extern ECS_COMPONENT_DECLARE(name) - - -/* ECS_BITMASK implementation */ -#define ECS_BITMASK_TYPE(name, ...)\ - typedef enum __VA_ARGS__ name - -#define ECS_BITMASK_ECS_META_IMPL ECS_BITMASK_IMPL - -#define ECS_BITMASK_IMPL(name, type_desc)\ - extern ECS_COMPONENT_DECLARE(name);\ - static const char *FLECS__##name##_desc = type_desc;\ - static ecs_type_kind_t FLECS__##name##_kind = EcsBitmaskType;\ - ECS_COMPONENT_DECLARE(name) = 0 - -#define ECS_BITMASK_DECLARE(name, type_desc)\ - extern ECS_COMPONENT_DECLARE(name);\ - ECS_COMPONENT_DECLARE(name) = 0 - -#define ECS_BITMASK_EXTERN(name, type_desc)\ - extern ECS_COMPONENT_DECLARE(name) - -#ifdef __cplusplus -} -#endif - -#endif // FLECS_META_C_H +int64_t ecs_meta_get_int( + const ecs_meta_cursor_t *cursor); -/** @} */ +/** Get field value as unsigned integer. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +uint64_t ecs_meta_get_uint( + const ecs_meta_cursor_t *cursor); -#endif // FLECS_META_C +/** Get field value as float. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +double ecs_meta_get_float( + const ecs_meta_cursor_t *cursor); -#endif +/** Get field value as string. + * This operation does not perform conversions. If the field is not a string, + * this operation will fail. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +FLECS_API +const char* ecs_meta_get_string( + const ecs_meta_cursor_t *cursor); -#ifdef FLECS_PLECS -#ifdef FLECS_NO_PLECS -#error "FLECS_NO_PLECS failed: PLECS is required by other addons" -#endif -/** - * @file addons/plecs.h - * @brief Flecs script module. +/** Get field value as entity. + * This operation does not perform conversions. * - * For script, see examples/plecs. + * @param cursor The cursor. + * @return The value of the current field. */ +FLECS_API +ecs_entity_t ecs_meta_get_entity( + const ecs_meta_cursor_t *cursor); -#ifdef FLECS_PLECS +/** Get field value as (component) id. + * This operation can convert from an entity. + * + * @param cursor The cursor. + * @return The value of the current field. + */ +ecs_id_t ecs_meta_get_id( + const ecs_meta_cursor_t *cursor); -/** - * @defgroup c_addons_plecs Flecs script - * @brief Data definition format for loading entity data. +/** Convert pointer of primitive kind to float. * - * \ingroup c_addons - * @{ + * @param type_kind The primitive type kind of the value. + * @param ptr Pointer to a value of a primitive type. + * @return The value in floating point format. */ +FLECS_API +double ecs_meta_ptr_to_float( + ecs_primitive_kind_t type_kind, + const void *ptr); -#ifndef FLECS_MODULE -#define FLECS_MODULE -#endif +/* API functions for creating meta types */ -#ifndef FLECS_PARSER -#define FLECS_PARSER -#endif +/** Used with ecs_primitive_init(). */ +typedef struct ecs_primitive_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_primitive_kind_t kind; /**< Primitive type kind. */ +} ecs_primitive_desc_t; -#ifndef FLECS_EXPR -#define FLECS_EXPR -#endif +/** Create a new primitive type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ +FLECS_API +ecs_entity_t ecs_primitive_init( + ecs_world_t *world, + const ecs_primitive_desc_t *desc); -#ifndef FLECS_PLECS_H -#define FLECS_PLECS_H -#ifdef __cplusplus -extern "C" { -#endif +/** Used with ecs_enum_init(). */ +typedef struct ecs_enum_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_enum_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Enum constants. */ +} ecs_enum_desc_t; +/** Create a new enum type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ FLECS_API -extern ECS_COMPONENT_DECLARE(EcsScript); +ecs_entity_t ecs_enum_init( + ecs_world_t *world, + const ecs_enum_desc_t *desc); -/* Script component */ -typedef struct EcsScript { - ecs_vec_t using_; - char *script; - ecs_vec_t prop_defaults; - ecs_world_t *world; -} EcsScript; -/** Parse plecs string. - * This parses a plecs string and instantiates the entities in the world. - * +/** Used with ecs_bitmask_init(). */ +typedef struct ecs_bitmask_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_bitmask_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Bitmask constants. */ +} ecs_bitmask_desc_t; + +/** Create a new bitmask type. + * * @param world The world. - * @param name The script name (typically the file). - * @param str The plecs string. - * @return Zero if success, non-zero otherwise. + * @param desc The type descriptor. + * @return The new type, 0 if failed. */ FLECS_API -int ecs_plecs_from_str( +ecs_entity_t ecs_bitmask_init( ecs_world_t *world, - const char *name, - const char *str); + const ecs_bitmask_desc_t *desc); -/** Parse plecs file. - * This parses a plecs file and instantiates the entities in the world. This - * operation is equivalent to loading the file contents and passing it to - * ecs_plecs_from_str. - * + +/** Used with ecs_array_init(). */ +typedef struct ecs_array_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_entity_t type; /**< Element type. */ + int32_t count; /**< Number of elements. */ +} ecs_array_desc_t; + +/** Create a new array type. + * * @param world The world. - * @param filename The plecs file name. - * @return Zero if success, non-zero otherwise. + * @param desc The type descriptor. + * @return The new type, 0 if failed. */ FLECS_API -int ecs_plecs_from_file( +ecs_entity_t ecs_array_init( ecs_world_t *world, - const char *filename); + const ecs_array_desc_t *desc); -/** Used with ecs_script_init */ -typedef struct ecs_script_desc_t { - ecs_entity_t entity; /* Set to customize entity handle associated with script */ - const char *filename; /* Set to load script from file */ - const char *str; /* Set to parse script from string */ -} ecs_script_desc_t; -/** Load managed script. - * A managed script tracks which entities it creates, and keeps those entities - * synchronized when the contents of the script are updated. When the script is - * updated, entities that are no longer in the new version will be deleted. - * - * This feature is experimental. +/** Used with ecs_vector_init(). */ +typedef struct ecs_vector_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_entity_t type; /**< Element type. */ +} ecs_vector_desc_t; + +/** Create a new vector type. * * @param world The world. - * @param desc Script descriptor. + * @param desc The type descriptor. + * @return The new type, 0 if failed. */ FLECS_API -ecs_entity_t ecs_script_init( +ecs_entity_t ecs_vector_init( ecs_world_t *world, - const ecs_script_desc_t *desc); + const ecs_vector_desc_t *desc); -#define ecs_script(world, ...)\ - ecs_script_init(world, &(ecs_script_desc_t) __VA_ARGS__) -/** Update script with new code. +/** Used with ecs_struct_init(). */ +typedef struct ecs_struct_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_member_t members[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Struct members. */ +} ecs_struct_desc_t; + +/** Create a new struct type. * * @param world The world. - * @param script The script entity. - * @param instance An assembly instance (optional). - * @param str The script code. - * @param vars Optional preset variables for script parameterization. + * @param desc The type descriptor. + * @return The new type, 0 if failed. */ FLECS_API -int ecs_script_update( +ecs_entity_t ecs_struct_init( ecs_world_t *world, - ecs_entity_t script, - ecs_entity_t instance, - const char *str, - ecs_vars_t *vars); + const ecs_struct_desc_t *desc); -/** Clear all entities associated with script. + +/** Used with ecs_opaque_init(). */ +typedef struct ecs_opaque_desc_t { + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + EcsOpaque type; /**< Type that the opaque type maps to. */ +} 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) + * * @param world The world. - * @param script The script entity. - * @param instance The script instance. + * @param desc The type descriptor. + * @return The new type, 0 if failed. */ FLECS_API -void ecs_script_clear( +ecs_entity_t ecs_opaque_init( ecs_world_t *world, - ecs_entity_t script, - ecs_entity_t instance); + const ecs_opaque_desc_t *desc); -/* Module import */ -FLECS_API -void FlecsScriptImport( - ecs_world_t *world); -#ifdef __cplusplus -} -#endif +/** Used with ecs_unit_init(). */ +typedef struct ecs_unit_desc_t { + /** Existing entity to associate with unit (optional). */ + ecs_entity_t entity; -#endif + /** Unit symbol, e.g. "m", "%", "g". (optional). */ + const char *symbol; -/** @} */ + /** Unit quantity, e.g. distance, percentage, weight. (optional). */ + ecs_entity_t quantity; -#endif + /** Base unit, e.g. "meters" (optional). */ + ecs_entity_t base; -#endif + /** Over unit, e.g. "per second" (optional). */ + ecs_entity_t over; -#ifdef FLECS_RULES -#ifdef FLECS_NO_RULES -#error "FLECS_NO_RULES failed: RULES is required by other addons" -#endif -/** - * @file addons/rules.h - * @brief Rule query engine addon. - * - * Rules are advanced queries that in addition to the capabilities of regular - * queries and filters have the following features: - * - * - query for all components of an entity (vs. all entities for a component) - * - query for all relationship pairs of an entity - * - support for query variables that are resolved at evaluation time - * - automatic traversal of transitive relationships - */ + /** Translation to apply to derived unit (optional). */ + ecs_unit_translation_t translation; -#ifdef FLECS_RULES + /** Prefix indicating order of magnitude relative to the derived unit. If set + * together with "translation", the values must match. If translation is not + * set, setting prefix will auto-populate it. + * Additionally, setting the prefix will enforce that the symbol (if set) + * is consistent with the prefix symbol + symbol of the derived unit. If the + * symbol is not set, it will be auto populated. */ + ecs_entity_t prefix; +} ecs_unit_desc_t; -/** - * @defgroup c_addons_rules Rules - * @brief Rules are an advanced query engine for matching against entity graphs. +/** Create a new unit. * - * \ingroup c_addons - * @{ + * @param world The world. + * @param desc The unit descriptor. + * @return The new unit, 0 if failed. */ +FLECS_API +ecs_entity_t ecs_unit_init( + ecs_world_t *world, + const ecs_unit_desc_t *desc); -#ifndef FLECS_RULES_H -#define FLECS_RULES_H -#ifdef __cplusplus -extern "C" { -#endif +/** Used with ecs_unit_prefix_init(). */ +typedef struct ecs_unit_prefix_desc_t { + /** Existing entity to associate with unit prefix (optional). */ + ecs_entity_t entity; -/** Convenience macro for rule creation */ -#define ecs_rule(world, ...)\ - ecs_rule_init(world, &(ecs_filter_desc_t) __VA_ARGS__ ) + /** Unit symbol, e.g. "m", "%", "g". (optional). */ + const char *symbol; -/** Create a rule. - * A rule accepts the same descriptor as a filter, but has the additional - * ability to use query variables. - * - * Query variables can be used to constrain wildcards across multiple terms to - * the same entity. Regular ECS queries do this in a limited form, as querying - * for Position, Velocity only returns entities that have both components. - * - * Query variables expand this to constrain entities that are resolved while the - * query is being matched. Consider a query for all entities and the mission - * they are on: - * (Mission, *) - * - * If an entity is on multiple missions, the wildcard will match it multiple - * times. Now say we want to only list combat missions. Naively we could try: - * (Mission, *), CombatMission(*) - * - * But this doesn't work, as term 1 returns entities with missions, and term 2 - * returns all combat missions for all entities. Query variables make it - * possible to apply CombatMission to the found mission: - * (Mission, $M), CombatMission($M) - * - * By using the same variable ('M') we ensure that CombatMission is applied to - * the mission found in the current result. - * - * Variables can be used in each part of the term (predicate, subject, object). - * This is a valid query: - * Likes($X, $Y), Likes($Y, $X) - * - * This is also a valid query: - * _Component, Serializable(_Component) - * - * In the query expression syntax, variables are prefixed with a $. When using - * the descriptor, specify the variable kind: - * desc.terms[0].second = { .name = "X", .var = EcsVarIsVariable } - * - * Different terms with the same variable name are automatically correlated by - * the query engine. - * - * A rule needs to be explicitly deleted with ecs_rule_fini. + /** Translation to apply to derived unit (optional). */ + ecs_unit_translation_t translation; +} ecs_unit_prefix_desc_t; + +/** Create a new unit prefix. * * @param world The world. - * @param desc The descriptor (see ecs_filter_desc_t) - * @return The rule. + * @param desc The type descriptor. + * @return The new unit prefix, 0 if failed. */ FLECS_API -ecs_rule_t* ecs_rule_init( +ecs_entity_t ecs_unit_prefix_init( ecs_world_t *world, - const ecs_filter_desc_t *desc); + const ecs_unit_prefix_desc_t *desc); -/** Delete a rule. - * - * @param rule The rule. - */ -FLECS_API -void ecs_rule_fini( - ecs_rule_t *rule); -/** Obtain filter from rule. - * This operation returns the filter with which the rule was created. +/** Create a new quantity. * - * @param rule The rule. - * @return The filter. + * @param world The world. + * @param desc The quantity descriptor. + * @return The new quantity, 0 if failed. */ FLECS_API -const ecs_filter_t* ecs_rule_get_filter( - const ecs_rule_t *rule); +ecs_entity_t ecs_quantity_init( + ecs_world_t *world, + const ecs_entity_desc_t *desc); -/** Return number of variables in rule. - * - * @param rule The rule. - * @return The number of variables/ - */ -FLECS_API -int32_t ecs_rule_var_count( - const ecs_rule_t *rule); +/* Convenience macros */ -/** Find variable index. - * This operation looks up the index of a variable in the rule. This index can - * be used in operations like ecs_iter_set_var and ecs_iter_get_var. - * - * @param rule The rule. - * @param name The variable name. - * @return The variable index. - */ -FLECS_API -int32_t ecs_rule_find_var( - const ecs_rule_t *rule, - const char *name); +/** Create a primitive type. */ +#define ecs_primitive(world, ...)\ + ecs_primitive_init(world, &(ecs_primitive_desc_t) __VA_ARGS__ ) -/** Get variable name. - * This operation returns the variable name for an index. - * - * @param rule The rule. - * @param var_id The variable index. - */ -FLECS_API -const char* ecs_rule_var_name( - const ecs_rule_t *rule, - int32_t var_id); +/** Create an enum type. */ +#define ecs_enum(world, ...)\ + ecs_enum_init(world, &(ecs_enum_desc_t) __VA_ARGS__ ) -/** Test if variable is an entity. - * Internally the rule engine has entity variables and table variables. When - * iterating through rule variables (by using ecs_rule_variable_count) only - * the values for entity variables are accessible. This operation enables an - * application to check if a variable is an entity variable. - * - * @param rule The rule. - * @param var_id The variable id. - */ -FLECS_API -bool ecs_rule_var_is_entity( - const ecs_rule_t *rule, - int32_t var_id); +/** Create a bitmask type. */ +#define ecs_bitmask(world, ...)\ + ecs_bitmask_init(world, &(ecs_bitmask_desc_t) __VA_ARGS__ ) -/** Iterate a rule. - * Note that rule iterators may allocate memory, and that unless the iterator - * is iterated until completion, it may still hold resources. When stopping - * iteration before ecs_rule_next has returned false, use ecs_iter_fini to - * cleanup any remaining resources. - * - * @param world The world. - * @param rule The rule. - * @return An iterator. - */ -FLECS_API -ecs_iter_t ecs_rule_iter( - const ecs_world_t *world, - const ecs_rule_t *rule); +/** Create an array type. */ +#define ecs_array(world, ...)\ + ecs_array_init(world, &(ecs_array_desc_t) __VA_ARGS__ ) -/** Progress rule iterator. - * - * @param it The iterator. - */ -FLECS_API -bool ecs_rule_next( - ecs_iter_t *it); +/** Create a vector type. */ +#define ecs_vector(world, ...)\ + ecs_vector_init(world, &(ecs_vector_desc_t) __VA_ARGS__ ) + +/** Create an opaque type. */ +#define ecs_opaque(world, ...)\ + ecs_opaque_init(world, &(ecs_opaque_desc_t) __VA_ARGS__ ) + +/** Create a struct type. */ +#define ecs_struct(world, ...)\ + ecs_struct_init(world, &(ecs_struct_desc_t) __VA_ARGS__ ) -/** Progress instanced iterator. - * Should not be called unless you know what you're doing :-) - * - * @param it The iterator. - */ -FLECS_API -bool ecs_rule_next_instanced( - ecs_iter_t *it); +/** Create a unit. */ +#define ecs_unit(world, ...)\ + ecs_unit_init(world, &(ecs_unit_desc_t) __VA_ARGS__ ) -/** Convert rule to a string. - * This will convert the rule program to a string which can aid in debugging - * the behavior of a rule. - * - * The returned string must be freed with ecs_os_free. - * - * @param rule The rule. - * @return The string - */ -FLECS_API -char* ecs_rule_str( - const ecs_rule_t *rule); +/** Create a unit prefix. */ +#define ecs_unit_prefix(world, ...)\ + ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t) __VA_ARGS__ ) -/** Convert rule to string with profile. - * To use this you must set the EcsIterProfile flag on an iterator before - * starting iteration: - * it.flags |= EcsIterProfile - * - * @param rule The rule. - * @return The string - */ -FLECS_API -char* ecs_rule_str_w_profile( - const ecs_rule_t *rule, - const ecs_iter_t *it); +/** Create a unit quantity. */ +#define ecs_quantity(world, ...)\ + ecs_quantity_init(world, &(ecs_entity_desc_t) __VA_ARGS__ ) -/** Populate variables from key-value string. - * Convenience function to set rule variables from a key-value string separated - * by comma's. The string must have the following format: - * var_a: value, var_b: value - * - * The key-value list may optionally be enclosed in parenthesis. + +/** Meta module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsMeta) + * @endcode * - * @param rule The rule. - * @param it The iterator for which to set the variables. - * @param expr The key-value expression. + * @param world The world. */ FLECS_API -const char* ecs_rule_parse_vars( - ecs_rule_t *rule, - ecs_iter_t *it, - const char *expr); +void FlecsMetaImport( + ecs_world_t *world); #ifdef __cplusplus } #endif -#endif // FLECS_RULES_H - -/** @} */ - -#endif // FLECS_RULES - -#endif - -#ifdef FLECS_SNAPSHOT -#ifdef FLECS_NO_SNAPSHOT -#error "FLECS_NO_SNAPSHOT failed: SNAPSHOT is required by other addons" -#endif /** - * @file addons/snapshot.h - * @brief Snapshot addon. - * - * A snapshot records the state of a world in a way so that it can be restored - * later. Snapshots work with POD components and non-POD components, provided - * that the appropriate lifecycle actions are registered for non-POD components. - * - * A snapshot is tightly coupled to a world. It is not possible to restore a - * snapshot from world A into world B. + * @file addons/meta_c.h + * @brief Utility macros for populating reflection data in C. */ -#ifdef FLECS_SNAPSHOT +#ifdef FLECS_META /** - * @defgroup c_addons_snapshot Snapshot - * @brief Save & restore world. - * - * \ingroup c_addons + * @defgroup c_addons_meta_c Meta Utilities + * @ingroup c_addons + * Macro utilities to automatically insert reflection data. + * * @{ */ -#ifndef FLECS_SNAPSHOT_H -#define FLECS_SNAPSHOT_H +#ifndef FLECS_META_C_H +#define FLECS_META_C_H #ifdef __cplusplus extern "C" { #endif -/** A snapshot stores the state of a world in a particular point in time. */ -typedef struct ecs_snapshot_t ecs_snapshot_t; +/* Macro that controls behavior of API. Usually set in module header. When the + * macro is not defined, it defaults to IMPL. */ -/** Create a snapshot. - * This operation makes a copy of the current state of the world. - * - * @param world The world to snapshot. - * @return The snapshot. - */ -FLECS_API -ecs_snapshot_t* ecs_snapshot_take( - ecs_world_t *world); +/* Define variables used by reflection utilities. This should only be defined + * by the module itself, not by the code importing the module */ +/* #define ECS_META_IMPL IMPL */ -/** Create a filtered snapshot. - * This operation is the same as ecs_snapshot_take, but accepts an iterator so - * an application can control what is stored by the snapshot. - * - * @param iter An iterator to the data to be stored by the snapshot. - * @return The snapshot. - */ -FLECS_API -ecs_snapshot_t* ecs_snapshot_take_w_iter( - ecs_iter_t *iter); +/* Don't define variables used by reflection utilities but still declare the + * variable for the component id. This enables the reflection utilities to be + * used for global component variables, even if no reflection is used. */ +/* #define ECS_META_IMPL DECLARE */ -/** Restore a snapshot. - * This operation restores the world to the state it was in when the specified - * snapshot was taken. A snapshot can only be used once for restoring, as its - * data replaces the data that is currently in the world. - * This operation also resets the last issued entity handle, so any calls to - * ecs_new may return entity ids that have been issued before restoring the - * snapshot. - * - * The world in which the snapshot is restored must be the same as the world in - * which the snapshot is taken. - * - * @param world The world to restore the snapshot to. - * @param snapshot The snapshot to restore. - */ +/* Don't define variables used by reflection utilities. This generates an extern + * variable for the component identifier. */ +/* #define ECS_META_IMPL EXTERN */ + +/** Declare component with descriptor. */ +#define ECS_META_COMPONENT(world, name)\ + ECS_COMPONENT_DEFINE(world, name);\ + ecs_meta_from_desc(world, ecs_id(name),\ + FLECS__##name##_kind, FLECS__##name##_desc) + +/** ECS_STRUCT(name, body). */ +#define ECS_STRUCT(name, ...)\ + ECS_META_IMPL_CALL(ECS_STRUCT_, ECS_META_IMPL, name, #__VA_ARGS__);\ + ECS_STRUCT_TYPE(name, __VA_ARGS__) + +/** ECS_ENUM(name, body). */ +#define ECS_ENUM(name, ...)\ + ECS_META_IMPL_CALL(ECS_ENUM_, ECS_META_IMPL, name, #__VA_ARGS__);\ + ECS_ENUM_TYPE(name, __VA_ARGS__) + +/** ECS_BITMASK(name, body). */ +#define ECS_BITMASK(name, ...)\ + ECS_META_IMPL_CALL(ECS_BITMASK_, ECS_META_IMPL, name, #__VA_ARGS__);\ + ECS_ENUM_TYPE(name, __VA_ARGS__) + +/** Macro used to mark part of type for which no reflection data is created. */ +#define ECS_PRIVATE + +/** Populate meta information from type descriptor. */ FLECS_API -void ecs_snapshot_restore( +int ecs_meta_from_desc( ecs_world_t *world, - ecs_snapshot_t *snapshot); + ecs_entity_t component, + ecs_type_kind_t kind, + const char *desc); -/** Obtain iterator to snapshot data. - * - * @param snapshot The snapshot to iterate over. - * @return Iterator to snapshot data. */ -FLECS_API -ecs_iter_t ecs_snapshot_iter( - ecs_snapshot_t *snapshot); -/** Progress snapshot iterator. - * - * @param iter The snapshot iterator. - * @return True if more data is available, otherwise false. +/** \cond + * Private utilities to switch between meta IMPL, DECLARE and EXTERN variants. */ -FLECS_API -bool ecs_snapshot_next( - ecs_iter_t *iter); -/** Free snapshot resources. - * This frees resources associated with a snapshot without restoring it. - * - * @param snapshot The snapshot to free. - */ -FLECS_API -void ecs_snapshot_free( - ecs_snapshot_t *snapshot); - -#ifdef __cplusplus -} -#endif +#define ECS_META_IMPL_CALL_INNER(base, impl, name, type_desc)\ + base ## impl(name, type_desc) -#endif +#define ECS_META_IMPL_CALL(base, impl, name, type_desc)\ + ECS_META_IMPL_CALL_INNER(base, impl, name, type_desc) -/** @} */ +/* ECS_STRUCT implementation */ +#define ECS_STRUCT_TYPE(name, ...)\ + typedef struct __VA_ARGS__ name -#endif +#define ECS_STRUCT_ECS_META_IMPL ECS_STRUCT_IMPL -#endif +#define ECS_STRUCT_IMPL(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + static const char *FLECS__##name##_desc = type_desc;\ + static ecs_type_kind_t FLECS__##name##_kind = EcsStructType;\ + ECS_COMPONENT_DECLARE(name) = 0 -#ifdef FLECS_PARSER -#ifdef FLECS_NO_PARSER -#error "FLECS_NO_PARSER failed: PARSER is required by other addons" -#endif -/** - * @file addons/parser.h - * @brief Parser addon. - * - * The parser addon parses string expressions into lists of terms, and can be - * used to construct filters, queries and types. - */ +#define ECS_STRUCT_DECLARE(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + ECS_COMPONENT_DECLARE(name) = 0 -#ifdef FLECS_PARSER +#define ECS_STRUCT_EXTERN(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name) -/** - * @defgroup c_addons_parser Parser - * @brief Query DSL parser and parsing utilities. - * - * \ingroup c_addons - * @{ - */ -#ifndef FLECS_PARSER_H -#define FLECS_PARSER_H +/* ECS_ENUM implementation */ +#define ECS_ENUM_TYPE(name, ...)\ + typedef enum __VA_ARGS__ name -/** Maximum number of extra arguments in term expression */ -#define ECS_PARSER_MAX_ARGS (16) +#define ECS_ENUM_ECS_META_IMPL ECS_ENUM_IMPL -#ifdef __cplusplus -extern "C" { -#endif +#define ECS_ENUM_IMPL(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + static const char *FLECS__##name##_desc = type_desc;\ + static ecs_type_kind_t FLECS__##name##_kind = EcsEnumType;\ + ECS_COMPONENT_DECLARE(name) = 0 -/** Skip whitespace characters. - * This function skips whitespace characters. Does not skip newlines. - * - * @param ptr Pointer to (potential) whitespaces to skip. - * @return Pointer to the next non-whitespace character. - */ -FLECS_API -const char* ecs_parse_ws( - const char *ptr); +#define ECS_ENUM_DECLARE(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + ECS_COMPONENT_DECLARE(name) = 0 -/** Skip whitespace and newline characters. - * This function skips whitespace characters. - * - * @param ptr Pointer to (potential) whitespaces to skip. - * @return Pointer to the next non-whitespace character. - */ -FLECS_API -const char* ecs_parse_ws_eol( - const char *ptr); +#define ECS_ENUM_EXTERN(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name) -/** Utility function to parse an identifier */ -const char* ecs_parse_identifier( - const char *name, - const char *expr, - const char *ptr, - char *token_out); -/** Parse digit. - * This function will parse until the first non-digit character is found. The - * provided expression must contain at least one digit character. - * - * @param ptr The expression to parse. - * @param token The output buffer. - * @return Pointer to the first non-digit character. - */ -FLECS_API -const char* ecs_parse_digit( - const char *ptr, - char *token); +/* ECS_BITMASK implementation */ +#define ECS_BITMASK_TYPE(name, ...)\ + typedef enum __VA_ARGS__ name -/** Parse a single token. - * This function can be used as simple tokenizer by other parsers. - * - * @param name of program (used for logging). - * @param expr pointer to token to parse. - * @param ptr pointer to first character to parse. - * @param token_out Parsed token (buffer should be ECS_MAX_TOKEN_SIZE large) - * @return Pointer to the next token, or NULL if error occurred. - */ -FLECS_API -const char* ecs_parse_token( - const char *name, - const char *expr, - const char *ptr, - char *token_out, - char delim); +#define ECS_BITMASK_ECS_META_IMPL ECS_BITMASK_IMPL -/** Parse term in expression. - * This operation parses a single term in an expression and returns a pointer - * to the next term expression. - * - * If the returned pointer points to the 0-terminator, the expression is fully - * parsed. The function would typically be called in a while loop: - * - * const char *ptr = expr; - * while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term))) { } - * - * The operation does not attempt to find entity ids from the names in the - * expression. Use the ecs_term_resolve_ids function to resolve the identifiers - * in the parsed term. - * - * The returned term will in most cases contain allocated resources, which - * should freed (or used) by the application. To free the resources for a term, - * use the ecs_term_free function. - * - * The parser accepts expressions in the legacy string format. - * - * @param world The world. - * @param name The name of the expression (optional, improves error logs) - * @param expr The expression to parse (optional, improves error logs) - * @param ptr The pointer to the current term (must be in expr). - * @param term_out Out parameter for the term. - * @param extra_args Out array for extra args, must be of size ECS_PARSER_MAX_ARGS. - * @return pointer to next term if successful, NULL if failed. - */ -FLECS_API -char* ecs_parse_term( - const ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_term_t *term_out, - ecs_term_id_t *extra_args); +#define ECS_BITMASK_IMPL(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + static const char *FLECS__##name##_desc = type_desc;\ + static ecs_type_kind_t FLECS__##name##_kind = EcsBitmaskType;\ + ECS_COMPONENT_DECLARE(name) = 0 + +#define ECS_BITMASK_DECLARE(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name);\ + ECS_COMPONENT_DECLARE(name) = 0 + +#define ECS_BITMASK_EXTERN(name, type_desc)\ + extern ECS_COMPONENT_DECLARE(name) + +/** \endcond */ #ifdef __cplusplus } -#endif // __cplusplus +#endif -#endif // FLECS_PARSER_H +#endif // FLECS_META_H /** @} */ -#endif // FLECS_PARSER +#endif // FLECS_META + + +#endif + +/** @} */ + +#endif #endif @@ -15422,9 +16342,9 @@ char* ecs_parse_term( /** * @defgroup c_addons_os_api_impl OS API Implementation - * @brief Default implementation for OS API interface. - * - * \ingroup c_addons + * @ingroup c_addons + * Default implementation for OS API interface. + * * @{ */ @@ -15458,7 +16378,7 @@ void ecs_set_os_api_impl(void); * @file addons/module.h * @brief Module addon. * - * The module addon allows for creating and importing modules. Flecs modules + * The module addon allows for creating and importing modules. Flecs modules * enable applications to organize components and systems into reusable units of * code that can easily be across projects. */ @@ -15467,9 +16387,9 @@ void ecs_set_os_api_impl(void); /** * @defgroup c_addons_module Module - * @brief Modules organize components, systems and more in reusable units of code. - * - * \ingroup c_addons + * @ingroup c_addons + * Modules organize components, systems and more in reusable units of code. + * * @{ */ @@ -15487,9 +16407,9 @@ extern "C" { * will be translated from PascalCase to an entity path (pascal.case) before the * lookup occurs. * - * Module contents will be stored as children of the module entity. This + * Module contents will be stored as children of the module entity. This * prevents modules from accidentally defining conflicting identifiers. This is - * enforced by setting the scope before and after loading the module to the + * enforced by setting the scope before and after loading the module to the * module entity id. * * A more convenient way to import a module is by using the ECS_IMPORT macro. @@ -15505,7 +16425,7 @@ ecs_entity_t ecs_import( ecs_module_action_t module, const char *module_name); -/** Same as ecs_import, but with name to scope conversion. +/** Same as ecs_import(), but with name to scope conversion. * PascalCase names are automatically converted to scoped names. * * @param world The world. @@ -15520,7 +16440,7 @@ ecs_entity_t ecs_import_c( const char *module_name_c); /** Import a module from a library. - * Similar to ecs_import, except that this operation will attempt to load the + * Similar to ecs_import(), except that this operation will attempt to load the * module from a dynamic library. * * A library may contain multiple modules, which is why both a library name and @@ -15559,15 +16479,18 @@ ecs_entity_t ecs_module_init( ecs_set_scope(world, ecs_id(id));\ } +/** Create a module. */ #define ECS_MODULE(world, id)\ ecs_entity_t ecs_id(id) = 0; ECS_MODULE_DEFINE(world, id)\ (void)ecs_id(id) -/** Wrapper around ecs_import. +/** Wrapper around ecs_import(). * This macro provides a convenient way to load a module with the world. It can * be used like this: * + * @code * ECS_IMPORT(world, FlecsSystemsPhysics); + * @endcode */ #define ECS_IMPORT(world, id) ecs_import_c(world, id##Import, #id) @@ -15739,7 +16662,7 @@ const ecs_member_t* ecs_cpp_last_member( * @{ */ -namespace flecs +namespace flecs { struct world; @@ -15755,10 +16678,13 @@ struct untyped_component; template struct component; -namespace _ +template +struct ref; + +namespace _ { template -struct cpp_type; +struct type; template struct each_delegate; @@ -15778,25 +16704,24 @@ namespace flecs { /** * @defgroup cpp_globals API Types & Globals - * @brief Types & constants bridged from C API. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Types & constants bridged from C API. + * * @{ */ using world_t = ecs_world_t; using world_info_t = ecs_world_info_t; -using query_group_info_t = ecs_query_group_info_t; using id_t = ecs_id_t; using entity_t = ecs_entity_t; using type_t = ecs_type_t; using table_t = ecs_table_t; -using filter_t = ecs_filter_t; -using observer_t = ecs_observer_t; +using term_t = ecs_term_t; using query_t = ecs_query_t; -using rule_t = ecs_rule_t; -using ref_t = ecs_ref_t; +using query_group_info_t = ecs_query_group_info_t; +using observer_t = ecs_observer_t; using iter_t = ecs_iter_t; +using ref_t = ecs_ref_t; using type_info_t = ecs_type_info_t; using type_hooks_t = ecs_type_hooks_t; using flags32_t = ecs_flags32_t; @@ -15804,6 +16729,7 @@ using flags32_t = ecs_flags32_t; enum inout_kind_t { InOutDefault = EcsInOutDefault, InOutNone = EcsInOutNone, + InOutFilter = EcsInOutFilter, InOut = EcsInOut, In = EcsIn, Out = EcsOut @@ -15819,20 +16745,27 @@ enum oper_kind_t { NotFrom = EcsNotFrom }; -/** Id flags */ -static const flecs::entity_t Pair = ECS_PAIR; -static const flecs::entity_t Override = ECS_OVERRIDE; -static const flecs::entity_t Toggle = ECS_TOGGLE; +enum query_cache_kind_t { + QueryCacheDefault = EcsQueryCacheDefault, + QueryCacheAuto = EcsQueryCacheAuto, + QueryCacheAll = EcsQueryCacheAll, + QueryCacheNone = EcsQueryCacheNone +}; + +/** Id bit flags */ +static const flecs::entity_t PAIR = ECS_PAIR; +static const flecs::entity_t AUTO_OVERRIDE = ECS_AUTO_OVERRIDE; +static const flecs::entity_t TOGGLE = ECS_TOGGLE; //////////////////////////////////////////////////////////////////////////////// -//// Builtin components and tags +//// Builtin components and tags //////////////////////////////////////////////////////////////////////////////// /* Builtin components */ using Component = EcsComponent; using Identifier = EcsIdentifier; using Poly = EcsPoly; -using Target = EcsTarget; +using DefaultChildComponent = EcsDefaultChildComponent; /* Builtin tags */ static const flecs::entity_t Query = EcsQuery; @@ -15851,44 +16784,57 @@ static const flecs::entity_t Phase = EcsPhase; 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; -static const uint32_t Up = EcsUp; -static const uint32_t Down = EcsDown; -static const uint32_t Cascade = EcsCascade; -static const uint32_t Desc = EcsDesc; -static const uint32_t Parent = EcsParent; -static const uint32_t IsVariable = EcsIsVariable; -static const uint32_t IsEntity = EcsIsEntity; -static const uint32_t Filter = EcsFilter; -static const uint32_t TraverseFlags = EcsTraverseFlags; +static const uint64_t Self = EcsSelf; +static const uint64_t Up = EcsUp; +static const uint64_t Trav = EcsTrav; +static const uint64_t Cascade = EcsCascade; +static const uint64_t Desc = EcsDesc; +static const uint64_t IsVariable = EcsIsVariable; +static const uint64_t IsEntity = EcsIsEntity; +static const uint64_t IsName = EcsIsName; +static const uint64_t TraverseFlags = EcsTraverseFlags; +static const uint64_t TermRefFlags = EcsTermRefFlags; /* Builtin entity ids */ static const flecs::entity_t Flecs = EcsFlecs; static const flecs::entity_t FlecsCore = EcsFlecsCore; static const flecs::entity_t World = EcsWorld; -/* Relationship properties */ +/* Component traits */ static const flecs::entity_t Wildcard = EcsWildcard; static const flecs::entity_t Any = EcsAny; static const flecs::entity_t This = EcsThis; static const flecs::entity_t Transitive = EcsTransitive; static const flecs::entity_t Reflexive = EcsReflexive; static const flecs::entity_t Final = EcsFinal; -static const flecs::entity_t DontInherit = EcsDontInherit; -static const flecs::entity_t AlwaysOverride = EcsAlwaysOverride; -static const flecs::entity_t Tag = EcsTag; -static const flecs::entity_t Union = EcsUnion; +static const flecs::entity_t PairIsTag = EcsPairIsTag; static const flecs::entity_t Exclusive = EcsExclusive; static const flecs::entity_t Acyclic = EcsAcyclic; static const flecs::entity_t Traversable = EcsTraversable; static const flecs::entity_t Symmetric = EcsSymmetric; static const flecs::entity_t With = EcsWith; static const flecs::entity_t OneOf = EcsOneOf; +static const flecs::entity_t Trait = EcsTrait; +static const flecs::entity_t Relationship = EcsRelationship; +static const flecs::entity_t Target = EcsTarget; +static const flecs::entity_t CanToggle = EcsCanToggle; + +/* OnInstantiate trait */ +static const flecs::entity_t OnInstantiate = EcsOnInstantiate; +static const flecs::entity_t Override = EcsOverride; +static const flecs::entity_t Inherit = EcsInherit; +static const flecs::entity_t DontInherit = EcsDontInherit; + +/* OnDelete/OnDeleteTarget traits */ +static const flecs::entity_t OnDelete = EcsOnDelete; +static const flecs::entity_t OnDeleteTarget = EcsOnDeleteTarget; +static const flecs::entity_t Remove = EcsRemove; +static const flecs::entity_t Delete = EcsDelete; +static const flecs::entity_t Panic = EcsPanic; /* Builtin relationships */ static const flecs::entity_t IsA = EcsIsA; @@ -15900,18 +16846,11 @@ static const flecs::entity_t SlotOf = EcsSlotOf; static const flecs::entity_t Name = EcsName; static const flecs::entity_t Symbol = EcsSymbol; -/* Cleanup policies */ -static const flecs::entity_t OnDelete = EcsOnDelete; -static const flecs::entity_t OnDeleteTarget = EcsOnDeleteTarget; -static const flecs::entity_t Remove = EcsRemove; -static const flecs::entity_t Delete = EcsDelete; -static const flecs::entity_t Panic = EcsPanic; - -/* Misc */ -static const flecs::entity_t Flatten = EcsFlatten; -static const flecs::entity_t DefaultChildComponent = EcsDefaultChildComponent; +/* Storage */ +static const flecs::entity_t Sparse = EcsSparse; +static const flecs::entity_t Union = EcsUnion; -/* Builtin predicates for comparing entity ids in queries. Only supported by rules */ +/* Builtin predicates for comparing entity ids in queries. */ static const flecs::entity_t PredEq = EcsPredEq; static const flecs::entity_t PredMatch = EcsPredMatch; static const flecs::entity_t PredLookup = EcsPredLookup; @@ -16013,6 +16952,9 @@ using remove_pointer_t = typename std::remove_pointer::type; template using remove_reference_t = typename std::remove_reference::type; +template +using underlying_type_t = typename std::underlying_type::type; + using std::is_base_of; using std::is_empty; using std::is_const; @@ -16086,29 +17028,29 @@ template struct array_iterator { explicit array_iterator(T* value, int index) { - m_value = value; - m_index = index; + value_ = value; + index_ = index; } bool operator!=(array_iterator const& other) const { - return m_index != other.m_index; + return index_ != other.index_; } T & operator*() const { - return m_value[m_index]; + return value_[index_]; } array_iterator& operator++() { - ++m_index; + ++index_; return *this; } private: - T* m_value; - int m_index; + T* value_; + int index_; }; template @@ -16126,19 +17068,19 @@ struct array > final { } T& operator[](int index) { - return m_array[index]; + return array_[index]; } T& operator[](size_t index) { - return m_array[index]; + return array_[index]; } array_iterator begin() { - return array_iterator(m_array, 0); + return array_iterator(array_, 0); } array_iterator end() { - return array_iterator(m_array, Size); + return array_iterator(array_, Size); } size_t size() { @@ -16146,7 +17088,7 @@ struct array > final { } T* ptr() { - return m_array; + return array_; } template @@ -16157,7 +17099,7 @@ struct array > final { } private: - T m_array[Size]; + T array_[Size]; }; template @@ -16199,43 +17141,43 @@ struct string_view; // wrapping in an std::string. struct string { explicit string() - : m_str(nullptr) - , m_const_str("") - , m_length(0) { } + : str_(nullptr) + , const_str_("") + , length_(0) { } explicit string(char *str) - : m_str(str) - , m_const_str(str ? str : "") - , m_length(str ? ecs_os_strlen(str) : 0) { } + : str_(str) + , const_str_(str ? str : "") + , length_(str ? ecs_os_strlen(str) : 0) { } ~string() { // If flecs is included in a binary but is not used, it is possible that // the OS API is not initialized. Calling ecs_os_free in that case could // crash the application during exit. However, if a string has been set // flecs has been used, and OS API should have been initialized. - if (m_str) { - ecs_os_free(m_str); + if (str_) { + ecs_os_free(str_); } } - string(string&& str) { - ecs_os_free(m_str); - m_str = str.m_str; - m_const_str = str.m_const_str; - m_length = str.m_length; - str.m_str = nullptr; + string(string&& str) noexcept { + ecs_os_free(str_); + str_ = str.str_; + const_str_ = str.const_str_; + length_ = str.length_; + str.str_ = nullptr; } operator const char*() const { - return m_const_str; + return const_str_; } - string& operator=(string&& str) { - ecs_os_free(m_str); - m_str = str.m_str; - m_const_str = str.m_const_str; - m_length = str.m_length; - str.m_str = nullptr; + string& operator=(string&& str) noexcept { + ecs_os_free(str_); + str_ = str.str_; + const_str_ = str.const_str_; + length_ = str.length_; + str.str_ = nullptr; return *this; } @@ -16244,19 +17186,19 @@ struct string { string(const string& str) = delete; bool operator==(const flecs::string& str) const { - if (str.m_const_str == m_const_str) { + if (str.const_str_ == const_str_) { return true; } - if (!m_const_str || !str.m_const_str) { + if (!const_str_ || !str.const_str_) { return false; } - if (str.m_length != m_length) { + if (str.length_ != length_) { return false; } - return ecs_os_strcmp(str, m_const_str) == 0; + return ecs_os_strcmp(str, const_str_) == 0; } bool operator!=(const flecs::string& str) const { @@ -16264,15 +17206,15 @@ struct string { } bool operator==(const char *str) const { - if (m_const_str == str) { + if (const_str_ == str) { return true; } - if (!m_const_str || !str) { + if (!const_str_ || !str) { return false; } - return ecs_os_strcmp(str, m_const_str) == 0; + return ecs_os_strcmp(str, const_str_) == 0; } bool operator!=(const char *str) const { @@ -16280,11 +17222,11 @@ struct string { } const char* c_str() const { - return m_const_str; + return const_str_; } std::size_t length() const { - return static_cast(m_length); + return static_cast(length_); } template @@ -16297,9 +17239,17 @@ struct string { } void clear() { - ecs_os_free(m_str); - m_str = nullptr; - m_const_str = nullptr; + ecs_os_free(str_); + str_ = nullptr; + const_str_ = nullptr; + } + + bool contains(const char *substr) { + if (const_str_) { + return strstr(const_str_, substr) != nullptr; + } else { + return false; + } } protected: @@ -16309,13 +17259,13 @@ struct string { // Making this constructor private forces the code to explicitly create a // string_view which emphasizes that the string won't be freed by the class. string(const char *str) - : m_str(nullptr) - , m_const_str(str ? str : "") - , m_length(str ? ecs_os_strlen(str) : 0) { } + : str_(nullptr) + , const_str_(str ? str : "") + , length_(str ? ecs_os_strlen(str) : 0) { } - char *m_str = nullptr; - const char *m_const_str; - ecs_size_t m_length; + char *str_ = nullptr; + const char *const_str_; + ecs_size_t length_; }; // For consistency, the API returns a string_view where it could have returned @@ -16338,6 +17288,7 @@ struct string_view : string { */ #include +#include #define FLECS_ENUM_MAX(T) _::to_constant::value #define FLECS_ENUM_MAX_COUNT (FLECS_ENUM_MAX(int) + 1) @@ -16354,21 +17305,25 @@ struct string_view : string { #endif #endif +#if defined(__clang__) && __clang_major__ >= 16 +// https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 +#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v) +#elif defined(__GNUC__) && __GNUC__ > 10 +#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v) +#else +#define flecs_enum_cast(T, v) static_cast(v) +#endif + namespace flecs { /** Int to enum */ namespace _ { -template +template Value> struct to_constant { -#if defined(__clang__) && __clang_major__ >= 16 - // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 - static constexpr E value = __builtin_bit_cast(E, Value); -#else - static constexpr E value = static_cast(Value); -#endif + static constexpr E value = flecs_enum_cast(E, Value); }; -template +template Value> constexpr E to_constant::value; } @@ -16397,7 +17352,7 @@ namespace _ { #if INTPTR_MAX == INT64_MAX #ifdef ECS_TARGET_MSVC - #if _MSC_VER >= 1930 + #if _MSC_VER >= 1929 #define ECS_SIZE_T_STR "unsigned __int64" #else #define ECS_SIZE_T_STR "unsigned int" @@ -16413,7 +17368,7 @@ namespace _ { #endif #else #ifdef ECS_TARGET_MSVC - #if _MSC_VER >= 1930 + #if _MSC_VER >= 1929 #define ECS_SIZE_T_STR "unsigned __int32" #else #define ECS_SIZE_T_STR "unsigned int" @@ -16438,16 +17393,18 @@ constexpr size_t enum_type_len() { /** Test if value is valid for enumeration. * This function leverages that when a valid value is provided, * __PRETTY_FUNCTION__ contains the enumeration name, whereas if a value is - * invalid, the string contains a number. */ + * invalid, the string contains a number or a negative (-) symbol. */ #if defined(ECS_TARGET_CLANG) #if ECS_CLANG_VERSION < 13 template constexpr bool enum_constant_is_valid() { - return !( + return !(( (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + enum_type_len() + 6 /* ', C = ' */] >= '0') && (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + - enum_type_len() + 6 /* ', C = ' */] <= '9')); + enum_type_len() + 6 /* ', C = ' */] <= '9')) || + (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + + enum_type_len() + 6 /* ', C = ' */] == '-')); } #else template @@ -16459,7 +17416,7 @@ constexpr bool enum_constant_is_valid() { #elif defined(ECS_TARGET_GNU) template constexpr bool enum_constant_is_valid() { - return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constepxr bool, enum_constant_is_valid) + + return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constexpr bool, enum_constant_is_valid) + enum_type_len() + 8 /* ', E C = ' */] != '('); } #else @@ -16473,6 +17430,12 @@ constexpr bool enum_constant_is_valid() { } #endif +/* Without this wrapper __builtin_bit_cast doesn't work */ +template C> +constexpr bool enum_constant_is_valid_wrap() { + return enum_constant_is_valid(); +} + template struct enum_is_valid { static constexpr bool value = enum_constant_is_valid(); @@ -16489,23 +17452,179 @@ static const char* enum_constant_to_name() { } /** Enumeration constant data */ +template struct enum_constant_data { flecs::entity_t id; - int next; + T offset; +}; + +/** + * @brief Provides utilities for enum reflection. + * + * This struct provides static functions for enum reflection, including conversion + * between enum values and their underlying integral types, and iteration over enum + * values. + * + * @tparam E The enum type. + * @tparam Handler The handler for enum reflection operations. + */ +template +struct enum_reflection { + using U = underlying_type_t; + + /** + * @brief Iterates over the range [Low, High] of enum values between Low and High. + * + * Recursively divide and conquers the search space to reduce the template-depth. Once + * recursive division is complete, calls Handle::handle_constant in ascending order, + * passing the values computed up the chain. + * + * @tparam Low The lower bound of the search range, inclusive. + * @tparam High The upper bound of the search range, inclusive. + * @tparam Args Additional arguments to be passed through to Handler::handle_constant + * @param last_value The last value processed in the iteration. + * @param args Additional arguments to be passed through to Handler::handle_constant + * @return constexpr U The result of the iteration. + */ + template + static constexpr U each_enum_range(U last_value, Args... args) { + return High - Low <= 1 + ? High == Low + ? Handler::template handle_constant(last_value, args...) + : Handler::template handle_constant(Handler::template handle_constant(last_value, args...), args...) + : each_enum_range<(Low + High) / 2 + 1, High>( + each_enum_range(last_value, args...), + args... + ); + } + + /** + * @brief Iterates over the mask range (Low, High] of enum values between Low and High. + * + * Recursively iterates the search space, looking for enums defined as multiple-of-2 + * bitmasks. Each iteration, shifts bit to the right until it hits Low, then calls + * Handler::handle_constant for each bitmask in ascending order. + * + * @tparam Low The lower bound of the search range, not inclusive + * @tparam High The upper bound of the search range, inclusive. + * @tparam Args Additional arguments to be passed through to Handler::handle_constant + * @param last_value The last value processed in the iteration. + * @param args Additional arguments to be passed through to Handler::handle_constant + * @return constexpr U The result of the iteration. + */ + template + static constexpr U each_mask_range(U last_value, Args... args) { + // If Low shares any bits with Current Flag, or if High is less than/equal to Low (and High isn't negative because max-flag signed) + return (Low & High) || (High <= Low && High != high_bit) + ? last_value + : Handler::template handle_constant( + each_mask_range> 1) & ~high_bit)>(last_value, args...), + args... + ); + } + + /** + * @brief Handles enum iteration for gathering reflection data. + * + * Iterates over all enum values up to a specified maximum value + * (each_enum_range<0, Value>), then iterates the rest of the possible bitmasks + * (each_mask_range). + * + * @tparam Value The maximum enum value to iterate up to. + * @tparam Args Additional arguments to be passed through to Handler::handle_constant + * @param args Additional arguments to be passed through to Handler::handle_constant + * @return constexpr U The result of the iteration. + */ + template (FLECS_ENUM_MAX(E)), typename... Args> + static constexpr U each_enum(Args... args) { + return each_mask_range(each_enum_range<0, Value>(0, args...), args...); + } + + static const U high_bit = static_cast(1) << (sizeof(U) * 8 - 1); }; /** Enumeration type data */ +template struct enum_data_impl { +private: + using U = underlying_type_t; + + /** + * @brief Handler struct for generating compile-time count of enum constants. + */ + struct reflection_count { + template () > = 0> + static constexpr U handle_constant(U last_value) { + return last_value; + } + + template () > = 0> + static constexpr U handle_constant(U last_value) { + return 1 + last_value; + } + }; + +public: flecs::entity_t id; int min; int max; - enum_constant_data constants[FLECS_ENUM_MAX_COUNT]; + bool has_contiguous; + // If enum constants start not-sparse, contiguous_until will be the index of the first sparse value, or end of the constants array + U contiguous_until; + // Compile-time generated count of enum constants. + static constexpr unsigned int constants_size = enum_reflection::template each_enum< static_cast(enum_last::value) >(); + // Constants array is sized to the number of found-constants, or 1 (to avoid 0-sized array) + enum_constant_data constants[constants_size? constants_size: 1]; }; /** Class that scans an enum for constants, extracts names & creates entities */ template struct enum_type { - static enum_data_impl data; +private: + using U = underlying_type_t; + + /** + * @brief Helper struct for filling enum_type's static `enum_data_impl` member with reflection data. + * + * Because reflection occurs in-order, we can use current value/last value to determine continuity, and + * use that as a lookup heuristic later on. + */ + struct reflection_init { + template () > = 0> + static U handle_constant(U last_value, flecs::world_t*) { + // Search for constant failed. Pass last valid value through. + return last_value; + } + + template () > = 0> + static U handle_constant(U last_value, flecs::world_t *world) { + // Constant is valid, so fill reflection data. + auto v = Value; + const char *name = enum_constant_to_name(); + + ++enum_type::data.max; // Increment cursor as we build constants array. + + // If the enum was previously contiguous, and continues to be through the current value... + if (enum_type::data.has_contiguous && static_cast(enum_type::data.max) == v && enum_type::data.contiguous_until == v) { + ++enum_type::data.contiguous_until; + } + // else, if the enum was never contiguous and hasn't been set as not contiguous... + else if (!enum_type::data.contiguous_until && enum_type::data.has_contiguous) { + enum_type::data.has_contiguous = false; + } + + ecs_assert(!(last_value > 0 && v < std::numeric_limits::min() + last_value), ECS_UNSUPPORTED, + "Signed integer enums causes integer overflow when recording offset from high positive to" + " low negative. Consider using unsigned integers as underlying type."); + enum_type::data.constants[enum_type::data.max].offset = v - last_value; + enum_type::data.constants[enum_type::data.max].id = ecs_cpp_enum_constant_register( + world, enum_type::data.id, 0, name, static_cast(v)); + return v; + } + }; +public: + + static enum_data_impl data; static enum_type& get() { static _::enum_type instance; @@ -16513,66 +17632,35 @@ struct enum_type { } flecs::entity_t entity(E value) const { - return data.constants[static_cast(value)].id; + int index = index_by_value(value); + if (index >= 0) { + return data.constants[index].id; + } + return 0; } void init(flecs::world_t *world, flecs::entity_t id) { #if !FLECS_CPP_ENUM_REFLECTION_SUPPORT ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher") #endif + // Initialize/reset reflection data values to default state. + data.min = 0; + data.max = -1; + data.has_contiguous = true; + data.contiguous_until = 0; ecs_log_push(); ecs_cpp_enum_init(world, id); data.id = id; - data.min = FLECS_ENUM_MAX(int); - init< enum_last::value >(world); - ecs_log_pop(); - } - -private: - template - static constexpr int to_int() { - return static_cast(Value); - } - - template - static constexpr E from_int() { - return to_constant::value; - } - - template - static constexpr int is_not_0() { - return static_cast(Value != from_int<0>()); - } - template () > = 0> - static void init_constant(flecs::world_t*) { } - - template () > = 0> - static void init_constant(flecs::world_t *world) { - int v = to_int(); - const char *name = enum_constant_to_name(); - data.constants[v].next = data.min; - data.min = v; - if (!data.max) { - data.max = v; - } - - data.constants[v].id = ecs_cpp_enum_constant_register( - world, data.id, data.constants[v].id, name, v); - } - - template - static void init(flecs::world_t *world) { - init_constant(world); - if (is_not_0()) { - init() - is_not_0()>()>(world); - } + // Generate reflection data + enum_reflection::template each_enum< static_cast(enum_last::value) >(world); + ecs_log_pop(); } }; template -enum_data_impl enum_type::data; +enum_data_impl enum_type::data; template ::value > = 0> inline static void init_enum(flecs::world_t *world, flecs::entity_t id) { @@ -16587,12 +17675,70 @@ inline static void init_enum(flecs::world_t*, flecs::entity_t) { } /** Enumeration type data wrapper with world pointer */ template struct enum_data { - enum_data(flecs::world_t *world, _::enum_data_impl& impl) + using U = underlying_type_t; + + enum_data(flecs::world_t *world, _::enum_data_impl& impl) : world_(world) , impl_(impl) { } + + /** + * @brief Checks if a given integral value is a valid enum value. + * + * @param value The integral value. + * @return true If the value is a valid enum value. + * @return false If the value is not a valid enum value. + */ + bool is_valid(U value) { + int index = index_by_value(value); + if (index < 0) { + return false; + } + return impl_.constants[index].id != 0; + } + + /** + * @brief Checks if a given enum value is valid. + * + * @param value The enum value. + * @return true If the value is valid. + * @return false If the value is not valid. + */ + bool is_valid(E value) { + return is_valid(static_cast(value)); + } + + /** + * @brief Finds the index into the constants array for a value, if one exists + * + * @param value The enum value. + * @return int The index of the enum value. + */ + int index_by_value(U value) const { + if (!impl_.max) { + return -1; + } + // Check if value is in contiguous lookup section + if (impl_.has_contiguous && value < impl_.contiguous_until && value >= 0) { + return static_cast(value); + } + U accumulator = impl_.contiguous_until? impl_.contiguous_until - 1: 0; + for (int i = static_cast(impl_.contiguous_until); i <= impl_.max; ++i) { + accumulator += impl_.constants[i].offset; + if (accumulator == value) { + return i; + } + } + return -1; + } - bool is_valid(int value) { - return impl_.constants[value].id != 0; + /** + * @brief Finds the index into the constants array for an enum value, if one exists + * + * @param value The enum value. + * @return int The index of the enum value. + */ + int index_by_value(E value) const { + return index_by_value(static_cast(value)); } int first() const { @@ -16604,21 +17750,21 @@ struct enum_data { } int next(int cur) const { - return impl_.constants[cur].next; + return cur + 1; } flecs::entity entity() const; - flecs::entity entity(int value) const; + flecs::entity entity(U value) const; flecs::entity entity(E value) const; flecs::world_t *world_; - _::enum_data_impl& impl_; + _::enum_data_impl& impl_; }; /** Convenience function for getting enum reflection data */ template enum_data enum_type(flecs::world_t *world) { - _::cpp_type::id(world); // Ensure enum is registered + _::type::id(world); // Ensure enum is registered auto& ref = _::enum_type::get(); return enum_data(world, ref.data); } @@ -16634,22 +17780,22 @@ namespace flecs { struct stringstream { explicit stringstream() - : m_buf({}) { } + : buf_({}) { } ~stringstream() { - ecs_strbuf_reset(&m_buf); + ecs_strbuf_reset(&buf_); } - stringstream(stringstream&& str) { - ecs_strbuf_reset(&m_buf); - m_buf = str.m_buf; - str.m_buf = {}; + stringstream(stringstream&& str) noexcept { + ecs_strbuf_reset(&buf_); + buf_ = str.buf_; + str.buf_ = {}; } - stringstream& operator=(stringstream&& str) { - ecs_strbuf_reset(&m_buf); - m_buf = str.m_buf; - str.m_buf = {}; + stringstream& operator=(stringstream&& str) noexcept { + ecs_strbuf_reset(&buf_); + buf_ = str.buf_; + str.buf_ = {}; return *this; } @@ -16658,16 +17804,16 @@ struct stringstream { stringstream(const stringstream& str) = delete; stringstream& operator<<(const char* str) { - ecs_strbuf_appendstr(&m_buf, str); + ecs_strbuf_appendstr(&buf_, str); return *this; } flecs::string str() { - return flecs::string(ecs_strbuf_get(&m_buf)); + return flecs::string(ecs_strbuf_get(&buf_)); } private: - ecs_strbuf_t m_buf; + ecs_strbuf_t buf_; }; } @@ -16839,9 +17985,9 @@ struct entity; /** * @defgroup cpp_ids Ids - * @brief Class for working with entity, component, tag and pair ids. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Class for working with entity, component, tag and pair ids. + * * @{ */ @@ -16849,46 +17995,50 @@ struct entity; * A flecs id is an identifier that can be added to entities. Ids can be: * - entities (including components, tags) * - pair ids - * - entities with id flags set (like flecs::Override, flecs::Toggle) + * - entities with id flags set (like flecs::AUTO_OVERRIDE, flecs::TOGGLE) */ struct id { id() - : m_world(nullptr) - , m_id(0) { } + : world_(nullptr) + , id_(0) { } - explicit id(flecs::id_t value) - : m_world(nullptr) - , m_id(value) { } + explicit id(flecs::id_t value) + : world_(nullptr) + , id_(value) { } explicit id(flecs::world_t *world, flecs::id_t value = 0) - : m_world(world) - , m_id(value) { } + : world_(world) + , id_(value) { } explicit id(flecs::world_t *world, flecs::id_t first, flecs::id_t second) - : m_world(world) - , m_id(ecs_pair(first, second)) { } + : world_(world) + , id_(ecs_pair(first, second)) { } + + explicit id(flecs::world_t *world, const char *expr) + : world_(world) + , id_(ecs_id_from_str(world, expr)) { } explicit id(flecs::id_t first, flecs::id_t second) - : m_world(nullptr) - , m_id(ecs_pair(first, second)) { } + : world_(nullptr) + , id_(ecs_pair(first, second)) { } explicit id(const flecs::id& first, const flecs::id& second) - : m_world(first.m_world) - , m_id(ecs_pair(first.m_id, second.m_id)) { } + : world_(first.world_) + , id_(ecs_pair(first.id_, second.id_)) { } /** Test if id is pair (has first, second) */ bool is_pair() const { - return (m_id & ECS_ID_FLAGS_MASK) == flecs::Pair; + return (id_ & ECS_ID_FLAGS_MASK) == flecs::PAIR; } /** Test if id is a wildcard */ bool is_wildcard() const { - return ecs_id_is_wildcard(m_id); + return ecs_id_is_wildcard(id_); } /** Test if id is entity */ bool is_entity() const { - return !(m_id & ECS_ID_FLAGS_MASK); + return !(id_ & ECS_ID_FLAGS_MASK); } /** Return id as entity (only allowed when id is valid entity) */ @@ -16904,19 +18054,19 @@ struct id { flecs::entity remove_flags() const; /** Return id without role */ - flecs::entity remove_generation() const; + flecs::entity remove_generation() const; /** Return component type of id */ flecs::entity type_id() const; /** Test if id has specified role */ bool has_flags(flecs::id_t flags) const { - return ((m_id & flags) == flags); + return ((id_ & flags) == flags); } /** Test if id has any role */ bool has_flags() const { - return (m_id & ECS_ID_FLAGS_MASK) != 0; + return (id_ & ECS_ID_FLAGS_MASK) != 0; } /** Return id flags set on id */ @@ -16927,7 +18077,7 @@ struct id { if (!is_pair()) { return false; } - return ECS_PAIR_FIRST(m_id) == first; + return ECS_PAIR_FIRST(id_) == first; } /** Get first element from a pair. @@ -16944,30 +18094,30 @@ struct id { /* Convert id to string */ flecs::string str() const { - return flecs::string(ecs_id_str(m_world, m_id)); + return flecs::string(ecs_id_str(world_, id_)); } /** Convert role of id to string. */ flecs::string flags_str() const { - return flecs::string_view( ecs_id_flag_str(m_id & ECS_ID_FLAGS_MASK)); + return flecs::string_view( ecs_id_flag_str(id_ & ECS_ID_FLAGS_MASK)); } /** Return flecs::id_t value */ flecs::id_t raw_id() const { - return m_id; + return id_; } operator flecs::id_t() const { - return m_id; + return id_; } flecs::world world() const; - + protected: /* World is optional, but guarantees that entity identifiers extracted from * the id are valid */ - flecs::world_t *m_world; - flecs::id_t m_id; + flecs::world_t *world_; + flecs::id_t id_; }; /** @} */ @@ -16984,7 +18134,9 @@ struct id { namespace flecs { /** - * \ingroup cpp_core_filters + * @ingroup cpp_core_queries + * + * @{ */ struct term; @@ -16995,8 +18147,8 @@ struct term_builder; } /** - * @file addons/cpp/mixins/filter/decl.hpp - * @brief Filter declarations. + * @file addons/cpp/mixins/query/decl.hpp + * @brief Query declarations. */ #pragma once @@ -17004,20 +18156,19 @@ struct term_builder; namespace flecs { /** - * @defgroup cpp_core_filters Filters - * @brief Filters are cheaper to create, but slower to iterate than flecs::query. - * - * \ingroup cpp_core + * @defgroup cpp_core_queries Queries + * @ingroup cpp_core + * * @{ */ -struct filter_base; +struct query_base; template -struct filter; +struct query; template -struct filter_builder; +struct query_builder; /** @} */ @@ -17042,7 +18193,7 @@ struct filter_builder; namespace flecs { /** - * \ingroup cpp_addons_event + * @ingroup cpp_addons_event * @{ */ @@ -17050,20 +18201,20 @@ namespace flecs { template struct event_builder_base { event_builder_base(flecs::world_t *world, flecs::entity_t event) - : m_world(world) - , m_desc{} - , m_ids{} - , m_ids_array{} + : world_(world) + , desc_{} + , ids_{} + , ids_array_{} { - m_desc.event = event; + desc_.event = event; } /** Add component to emit for */ template Base& id() { - m_ids.array = m_ids_array; - m_ids.array[m_ids.count] = _::cpp_type().id(m_world); - m_ids.count ++; + ids_.array = ids_array_; + ids_.array[ids_.count] = _::type().id(world_); + ids_.count ++; return *this; } @@ -17075,8 +18226,8 @@ struct event_builder_base { template Base& id() { return id( - ecs_pair(_::cpp_type::id(this->m_world), - _::cpp_type::id(this->m_world))); + ecs_pair(_::type::id(this->world_), + _::type::id(this->world_))); } /** @@ -17086,7 +18237,7 @@ struct event_builder_base { */ template Base& id(entity_t second) { - return id(ecs_pair(_::cpp_type::id(this->m_world), second)); + return id(ecs_pair(_::type::id(this->world_), second)); } /** @@ -17098,59 +18249,66 @@ struct event_builder_base { return id(ecs_pair(first, second)); } + template ::value> = 0> + Base& id(Enum value) { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(value); + return id(et.entity(), target); + } + /** Add (component) id to emit for */ Base& id(flecs::id_t id) { - m_ids.array = m_ids_array; - m_ids.array[m_ids.count] = id; - m_ids.count ++; + ids_.array = ids_array_; + ids_.array[ids_.count] = id; + ids_.count ++; return *this; } /** Set entity for which to emit event */ Base& entity(flecs::entity_t e) { - m_desc.entity = e; + desc_.entity = e; return *this; } /* Set table for which to emit event */ Base& table(flecs::table_t *t, int32_t offset = 0, int32_t count = 0) { - m_desc.table = t; - m_desc.offset = offset; - m_desc.count = count; + desc_.table = t; + desc_.offset = offset; + desc_.count = count; return *this; } /* Set event data */ Base& ctx(const E* ptr) { - m_desc.const_param = ptr; + desc_.const_param = ptr; return *this; } /* Set event data */ Base& ctx(E* ptr) { - m_desc.param = ptr; + desc_.param = ptr; return *this; } void emit() { - m_ids.array = m_ids_array; - m_desc.ids = &m_ids; - m_desc.observable = const_cast(ecs_get_world(m_world)); - ecs_emit(m_world, &m_desc); + ids_.array = ids_array_; + desc_.ids = &ids_; + desc_.observable = const_cast(ecs_get_world(world_)); + ecs_emit(world_, &desc_); } void enqueue() { - m_ids.array = m_ids_array; - m_desc.ids = &m_ids; - m_desc.observable = const_cast(ecs_get_world(m_world)); - ecs_enqueue(m_world, &m_desc); + ids_.array = ids_array_; + desc_.ids = &ids_; + desc_.observable = const_cast(ecs_get_world(world_)); + ecs_enqueue(world_, &desc_); } protected: - flecs::world_t *m_world; - ecs_event_desc_t m_desc; - flecs::type_t m_ids; - flecs::id_t m_ids_array[ECS_EVENT_DESC_ID_COUNT_MAX]; + flecs::world_t *world_; + ecs_event_desc_t desc_; + flecs::type_t ids_; + flecs::id_t ids_array_[ECS_EVENT_DESC_ID_COUNT_MAX]; private: operator Base&() { @@ -17172,13 +18330,13 @@ struct event_builder_typed : event_builder_base, E> { /* Set event data */ Class& ctx(const E& ptr) { - this->m_desc.const_param = &ptr; + this->desc_.const_param = &ptr; return *this; } /* Set event data */ Class& ctx(E&& ptr) { - this->m_desc.param = &ptr; + this->desc_.param = &ptr; return *this; } }; @@ -17213,35 +18371,6 @@ using event_from_func_t = typename event_from_func::type; } } -/** - * @file addons/cpp/mixins/query/decl.hpp - * @brief Query declarations. - */ - -#pragma once - -namespace flecs { - -/** - * @defgroup cpp_core_queries Queries - * @brief Cached query implementation. Fast to iterate, but slower to create than flecs::filter. - * - * \ingroup cpp_core - * @{ - */ - -struct query_base; - -template -struct query; - -template -struct query_builder; - -/** @} */ - -} - /** * @file addons/cpp/mixins/observer/decl.hpp * @brief Observer declarations. @@ -17253,9 +18382,9 @@ namespace flecs { /** * @defgroup cpp_observers Observers - * @brief Observers let applications register callbacks for ECS events. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Observers let applications register callbacks for ECS events. + * * @{ */ @@ -17280,9 +18409,9 @@ namespace flecs { /** * @defgroup cpp_addons_systems Systems - * @brief Systems are a query + function that can be ran manually or by a pipeline. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Systems are a query + function that can be ran manually or by a pipeline. + * * @{ */ @@ -17315,9 +18444,9 @@ namespace flecs { /** * @defgroup cpp_pipelines Pipelines - * @brief Pipelines order and schedule systems for execution. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Pipelines order and schedule systems for execution. + * * @{ */ @@ -17357,9 +18486,9 @@ namespace flecs { /** * @defgroup cpp_addons_timer Timer - * @brief Run systems at a time interval. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Run systems at a time interval. + * * @{ */ @@ -17377,33 +18506,6 @@ void timer_init(flecs::world& world); } // namespace _ } // namespace flecs -#endif -#ifdef FLECS_SNAPSHOT -/** - * @file addons/cpp/mixins/snapshot/decl.hpp - * @brief Snapshot module declarations. - */ - -#pragma once - -namespace flecs { - -/** - * @defgroup cpp_addons_snapshots Snapshots - * @brief Save & restore world. - * - * \ingroup cpp_addons - * @{ - */ - -using snapshot_t = ecs_snapshot_t; - -struct snapshot; - -/** @} */ - -} - #endif #ifdef FLECS_DOC /** @@ -17418,15 +18520,18 @@ namespace doc { /** * @defgroup cpp_addons_doc Doc - * @brief Utilities for documenting entities, components and systems. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Utilities for documenting entities, components and systems. + * * @{ */ /** flecs.doc.Description component */ using Description = EcsDocDescription; +/** flecs.doc.Uuid component */ +static const flecs::entity_t Uuid = EcsDocUuid; + /** flecs.doc.Brief component */ static const flecs::entity_t Brief = EcsDocBrief; @@ -17439,7 +18544,9 @@ static const flecs::entity_t Link = EcsDocLink; /** flecs.doc.Color component */ static const flecs::entity_t Color = EcsDocColor; +/** @private */ namespace _ { +/** @private */ void init(flecs::world& world); } @@ -17461,9 +18568,9 @@ namespace flecs { /** * @defgroup cpp_addons_rest Rest - * @brief REST API for querying and mutating entities. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * REST API for querying and mutating entities. + * * @{ */ @@ -17482,37 +18589,6 @@ void init(flecs::world& world); } -#endif -#ifdef FLECS_RULES -/** - * @file addons/cpp/mixins/rule/decl.hpp - * @brief Rule declarations. - */ - -#pragma once - -namespace flecs { - -/** - * @defgroup cpp_addons_rules Rules - * @brief Rules are an advanced query engine for matching against entity graphs. - * - * \ingroup cpp_addons - * @{ - */ - -struct rule_base; - -template -struct rule; - -template -struct rule_builder; - -/** @} */ - -} - #endif #ifdef FLECS_META /** @@ -17526,9 +18602,9 @@ namespace flecs { /** * @defgroup cpp_addons_meta Meta - * @brief Flecs reflection framework. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Flecs reflection framework. + * * @{ */ @@ -17554,8 +18630,8 @@ using enum_constant_t = ecs_enum_constant_t; using bitmask_constant_t = ecs_bitmask_constant_t; /* Components */ -using MetaType = EcsMetaType; -using MetaTypeSerialized = EcsMetaTypeSerialized; +using Type = EcsType; +using TypeSerializer = EcsTypeSerializer; using Primitive = EcsPrimitive; using Enum = EcsEnum; using Bitmask = EcsBitmask; @@ -17647,54 +18723,54 @@ namespace flecs { /** * @defgroup cpp_addons_meta Meta - * @brief Flecs reflection framework. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Flecs reflection framework. + * * @{ */ /** Class for reading/writing dynamic values. - * - * \ingroup cpp_addons_meta + * + * @ingroup cpp_addons_meta */ struct cursor { cursor(flecs::world_t *world, flecs::entity_t type_id, void *ptr) { - m_cursor = ecs_meta_cursor(world, type_id, ptr); + cursor_ = ecs_meta_cursor(world, type_id, ptr); } /** Push value scope (such as a nested struct) */ int push() { - return ecs_meta_push(&m_cursor); + return ecs_meta_push(&cursor_); } /** Pop value scope */ int pop() { - return ecs_meta_pop(&m_cursor); + return ecs_meta_pop(&cursor_); } /** Move to next member/element */ int next() { - return ecs_meta_next(&m_cursor); + return ecs_meta_next(&cursor_); } /** Move to member by name */ int member(const char *name) { - return ecs_meta_member(&m_cursor, name); + return ecs_meta_member(&cursor_, name); } /** Move to element by index */ int elem(int32_t elem) { - return ecs_meta_elem(&m_cursor, elem); + return ecs_meta_elem(&cursor_, elem); } /** Test if current scope is a collection type */ bool is_collection() { - return ecs_meta_is_collection(&m_cursor); + return ecs_meta_is_collection(&cursor_); } /** Get member name */ flecs::string_view get_member() const { - return flecs::string_view(ecs_meta_get_member(&m_cursor)); + return flecs::string_view(ecs_meta_get_member(&cursor_)); } /** Get type of value */ @@ -17705,94 +18781,94 @@ struct cursor { /** Get untyped pointer to value */ void* get_ptr() { - return ecs_meta_get_ptr(&m_cursor); + return ecs_meta_get_ptr(&cursor_); } /** Set boolean value */ int set_bool(bool value) { - return ecs_meta_set_bool(&m_cursor, value); + return ecs_meta_set_bool(&cursor_, value); } /** Set char value */ int set_char(char value) { - return ecs_meta_set_char(&m_cursor, value); + return ecs_meta_set_char(&cursor_, value); } /** Set signed int value */ int set_int(int64_t value) { - return ecs_meta_set_int(&m_cursor, value); + return ecs_meta_set_int(&cursor_, value); } /** Set unsigned int value */ int set_uint(uint64_t value) { - return ecs_meta_set_uint(&m_cursor, value); + return ecs_meta_set_uint(&cursor_, value); } /** Set float value */ int set_float(double value) { - return ecs_meta_set_float(&m_cursor, value); + return ecs_meta_set_float(&cursor_, value); } /** Set string value */ int set_string(const char *value) { - return ecs_meta_set_string(&m_cursor, value); + return ecs_meta_set_string(&cursor_, value); } /** Set string literal value */ int set_string_literal(const char *value) { - return ecs_meta_set_string_literal(&m_cursor, value); + return ecs_meta_set_string_literal(&cursor_, value); } /** Set entity value */ int set_entity(flecs::entity_t value) { - return ecs_meta_set_entity(&m_cursor, value); + return ecs_meta_set_entity(&cursor_, value); } /** Set (component) id value */ int set_id(flecs::id_t value) { - return ecs_meta_set_id(&m_cursor, value); + return ecs_meta_set_id(&cursor_, value); } /** Set null value */ int set_null() { - return ecs_meta_set_null(&m_cursor); + return ecs_meta_set_null(&cursor_); } /** Get boolean value */ bool get_bool() const { - return ecs_meta_get_bool(&m_cursor); + return ecs_meta_get_bool(&cursor_); } /** Get char value */ char get_char() const { - return ecs_meta_get_char(&m_cursor); + return ecs_meta_get_char(&cursor_); } /** Get signed int value */ int64_t get_int() const { - return ecs_meta_get_int(&m_cursor); + return ecs_meta_get_int(&cursor_); } /** Get unsigned int value */ uint64_t get_uint() const { - return ecs_meta_get_uint(&m_cursor); + return ecs_meta_get_uint(&cursor_); } /** Get float value */ double get_float() const { - return ecs_meta_get_float(&m_cursor); + return ecs_meta_get_float(&cursor_); } /** Get string value */ const char *get_string() const { - return ecs_meta_get_string(&m_cursor); + return ecs_meta_get_string(&cursor_); } /** Get entity value */ flecs::entity get_entity() const; /** Cursor object */ - ecs_meta_cursor_t m_cursor; + ecs_meta_cursor_t cursor_; }; /** @} */ @@ -17812,9 +18888,9 @@ namespace flecs { /** * @defgroup cpp_addons_meta Meta - * @brief Flecs reflection framework. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Flecs reflection framework. + * * @{ */ @@ -17833,7 +18909,7 @@ template struct opaque { opaque(flecs::world_t *w = nullptr) : world(w) { if (world) { - desc.entity = _::cpp_type::id(world); + desc.entity = _::type::id(world); } } @@ -17845,7 +18921,7 @@ struct opaque { /** Serialize function */ opaque& serialize(flecs::serialize func) { - this->desc.type.serialize = + this->desc.type.serialize = reinterpret_castdesc.type.serialize)>(func); return *this; @@ -17853,7 +18929,7 @@ struct opaque { /** Assign bool value */ opaque& assign_bool(void (*func)(T *dst, bool value)) { - this->desc.type.assign_bool = + this->desc.type.assign_bool = reinterpret_castdesc.type.assign_bool)>(func); return *this; @@ -17861,7 +18937,7 @@ struct opaque { /** Assign char value */ opaque& assign_char(void (*func)(T *dst, char value)) { - this->desc.type.assign_char = + this->desc.type.assign_char = reinterpret_castdesc.type.assign_char)>(func); return *this; @@ -17869,7 +18945,7 @@ struct opaque { /** Assign int value */ opaque& assign_int(void (*func)(T *dst, int64_t value)) { - this->desc.type.assign_int = + this->desc.type.assign_int = reinterpret_castdesc.type.assign_int)>(func); return *this; @@ -17877,7 +18953,7 @@ struct opaque { /** Assign unsigned int value */ opaque& assign_uint(void (*func)(T *dst, uint64_t value)) { - this->desc.type.assign_uint = + this->desc.type.assign_uint = reinterpret_castdesc.type.assign_uint)>(func); return *this; @@ -17885,7 +18961,7 @@ struct opaque { /** Assign float value */ opaque& assign_float(void (*func)(T *dst, double value)) { - this->desc.type.assign_float = + this->desc.type.assign_float = reinterpret_castdesc.type.assign_float)>(func); return *this; @@ -17893,7 +18969,7 @@ struct opaque { /** Assign string value */ opaque& assign_string(void (*func)(T *dst, const char *value)) { - this->desc.type.assign_string = + this->desc.type.assign_string = reinterpret_castdesc.type.assign_string)>(func); return *this; @@ -17901,9 +18977,9 @@ struct opaque { /** Assign entity value */ opaque& assign_entity( - void (*func)(T *dst, ecs_world_t *world, ecs_entity_t entity)) + void (*func)(T *dst, ecs_world_t *world, ecs_entity_t entity)) { - this->desc.type.assign_entity = + this->desc.type.assign_entity = reinterpret_castdesc.type.assign_entity)>(func); return *this; @@ -17913,7 +18989,7 @@ struct opaque { opaque& assign_id( void (*func)(T *dst, ecs_world_t *world, ecs_id_t id)) { - this->desc.type.assign_id = + this->desc.type.assign_id = reinterpret_castdesc.type.assign_id)>(func); return *this; @@ -17921,7 +18997,7 @@ struct opaque { /** Assign null value */ opaque& assign_null(void (*func)(T *dst)) { - this->desc.type.assign_null = + this->desc.type.assign_null = reinterpret_castdesc.type.assign_null)>(func); return *this; @@ -17929,7 +19005,7 @@ struct opaque { /** Clear collection elements */ opaque& clear(void (*func)(T *dst)) { - this->desc.type.clear = + this->desc.type.clear = reinterpret_castdesc.type.clear)>(func); return *this; @@ -17937,7 +19013,7 @@ struct opaque { /** Ensure & get collection element */ opaque& ensure_element(ElemType* (*func)(T *dst, size_t elem)) { - this->desc.type.ensure_element = + this->desc.type.ensure_element = reinterpret_castdesc.type.ensure_element)>(func); return *this; @@ -17945,7 +19021,7 @@ struct opaque { /** Ensure & get element */ opaque& ensure_member(void* (*func)(T *dst, const char *member)) { - this->desc.type.ensure_member = + this->desc.type.ensure_member = reinterpret_castdesc.type.ensure_member)>(func); return *this; @@ -17953,15 +19029,15 @@ struct opaque { /** Return number of elements */ opaque& count(size_t (*func)(const T *dst)) { - this->desc.type.count = + this->desc.type.count = reinterpret_castdesc.type.count)>(func); return *this; } - + /** Resize to number of elements */ opaque& resize(void (*func)(T *dst, size_t count)) { - this->desc.type.resize = + this->desc.type.resize = reinterpret_castdesc.type.resize)>(func); return *this; @@ -17997,9 +19073,9 @@ struct units { /** * @defgroup cpp_addons_units Units - * @brief Common unit annotations for reflection framework. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Common unit annotations for reflection framework. + * * @{ */ @@ -18007,9 +19083,9 @@ struct Prefixes { }; /** * @defgroup cpp_addons_units_prefixes Prefixes - * @brief Prefixes to indicate unit count (e.g. Kilo, Mega) - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units + * Prefixes to indicate unit count (e.g. Kilo, Mega) + * * @{ */ @@ -18046,9 +19122,9 @@ struct Yobi { }; /** * @defgroup cpp_addons_units_quantities Quantities - * @brief Quantities that group units (e.g. Length) - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units + * Quantities that group units (e.g. Length) + * * @{ */ @@ -18068,14 +19144,14 @@ struct DataRate { }; struct Angle { }; struct Frequency { }; struct Uri { }; +struct Color { }; /** @} */ struct duration { /** * @defgroup cpp_addons_units_duration Duration - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18094,8 +19170,7 @@ struct Days { }; struct angle { /** * @defgroup cpp_addons_units_angle Angle - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18109,8 +19184,7 @@ struct Degrees { }; struct time { /** * @defgroup cpp_addons_units_time Time - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18123,8 +19197,7 @@ struct Date { }; struct mass { /** * @defgroup cpp_addons_units_mass Mass - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18138,8 +19211,7 @@ struct KiloGrams { }; struct electric_current { /** * @defgroup cpp_addons_units_electric_current Electric Current - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18152,8 +19224,7 @@ struct Ampere { }; struct amount { /** * @defgroup cpp_addons_units_amount Amount - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18166,8 +19237,7 @@ struct Mole { }; struct luminous_intensity { /** * @defgroup cpp_addons_units_luminous_intensity Luminous Intensity - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18180,8 +19250,7 @@ struct Candela { }; struct force { /** * @defgroup cpp_addons_units_force Force - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18194,8 +19263,7 @@ struct Newton { }; struct length { /** * @defgroup cpp_addons_units_length Length - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18216,8 +19284,7 @@ struct Pixels { }; struct pressure { /** * @defgroup cpp_addons_units_pressure Pressure - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18231,8 +19298,7 @@ struct Bar { }; struct speed { /** * @defgroup cpp_addons_units_speed Speed - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18248,8 +19314,7 @@ struct MilesPerHour { }; struct temperature { /** * @defgroup cpp_addons_units_temperature Temperature - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18264,8 +19329,7 @@ struct Fahrenheit { }; struct data { /** * @defgroup cpp_addons_units_data Data - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18287,8 +19351,7 @@ struct GibiBytes { }; struct datarate { /** * @defgroup cpp_addons_units_datarate Data Rate - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18308,8 +19371,7 @@ struct GigaBytesPerSecond { }; struct frequency { /** * @defgroup cpp_addons_units_frequency Frequency - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -18322,22 +19384,35 @@ struct GigaHertz { }; }; -struct uri { +struct uri { +/** + * @defgroup cpp_addons_units_uri Uri + * @ingroup cpp_addons_units + * @{ + */ + +struct Hyperlink { }; +struct Image { }; +struct File { }; + +/** @} */ +}; + + +struct color { /** - * @defgroup cpp_addons_units_uri Uri - * - * \ingroup cpp_addons_units + * @defgroup cpp_addons_units_color Color + * @ingroup cpp_addons_units * @{ */ -struct Hyperlink { }; -struct Image { }; -struct File { }; +struct Rgb { }; +struct Hsl { }; +struct Css { }; /** @} */ }; - struct Percentage { }; struct Bel { }; struct DeciBel { }; @@ -18350,10 +19425,10 @@ units(flecs::world& world); } #endif -#ifdef FLECS_MONITOR +#ifdef FLECS_STATS /** - * @file addons/cpp/mixins/monitor/decl.hpp - * @brief Monitor module declarations. + * @file addons/cpp/mixins/stats/decl.hpp + * @brief Stats module declarations. */ #pragma once @@ -18361,10 +19436,10 @@ units(flecs::world& world); namespace flecs { /** - * @defgroup cpp_addons_monitor Monitor - * @brief The monitor addon periodically tracks statistics for the world and systems. - * - * \ingroup cpp_addons + * @defgroup cpp_addons_stats Stats + * @ingroup cpp_addons + * The stats addon tracks statistics for the world and systems. + * * @{ */ @@ -18373,14 +19448,18 @@ using WorldStats = EcsWorldStats; /** Component that stores system/pipeline statistics */ using PipelineStats = EcsPipelineStats; - -struct monitor { - monitor(flecs::world& world); + +/** Component with world summary stats */ +using WorldSummary = EcsWorldSummary; + +struct stats { + stats(flecs::world& world); }; /** @} */ } + #endif #ifdef FLECS_METRICS /** @@ -18402,22 +19481,22 @@ struct monitor { namespace flecs { /** - * \ingroup cpp_addon_metrics + * @ingroup cpp_addons_metrics * @{ */ /** Event builder interface */ struct metric_builder { metric_builder(flecs::world_t *world, flecs::entity_t entity) - : m_world(world) + : world_(world) { - m_desc.entity = entity; + desc_.entity = entity; } ~metric_builder(); metric_builder& member(flecs::entity_t e) { - m_desc.member = e; + desc_.member = e; return *this; } @@ -18432,61 +19511,61 @@ struct metric_builder { metric_builder& dotmember(const char *name); metric_builder& id(flecs::id_t the_id) { - m_desc.id = the_id; + desc_.id = the_id; return *this; } metric_builder& id(flecs::entity_t first, flecs::entity_t second) { - m_desc.id = ecs_pair(first, second); + desc_.id = ecs_pair(first, second); return *this; } template metric_builder& id() { - return id(_::cpp_type::id(m_world)); + return id(_::type::id(world_)); } template metric_builder& id(flecs::entity_t second) { - return id(_::cpp_type::id(m_world), second); + return id(_::type::id(world_), second); } template metric_builder& id_second(flecs::entity_t first) { - return id(first, _::cpp_type::id(m_world)); + return id(first, _::type::id(world_)); } template metric_builder& id() { - return id(_::cpp_type::id(m_world)); + return id(_::type::id(world_)); } metric_builder& targets(bool value = true) { - m_desc.targets = value; + desc_.targets = value; return *this; } metric_builder& kind(flecs::entity_t the_kind) { - m_desc.kind = the_kind; + desc_.kind = the_kind; return *this; } template metric_builder& kind() { - return kind(_::cpp_type::id(m_world)); + return kind(_::type::id(world_)); } metric_builder& brief(const char *b) { - m_desc.brief = b; + desc_.brief = b; return *this; } operator flecs::entity(); protected: - flecs::world_t *m_world; - ecs_metric_desc_t m_desc = {}; - bool m_created = false; + flecs::world_t *world_; + ecs_metric_desc_t desc_ = {}; + bool created_ = false; }; /** @@ -18498,6 +19577,15 @@ struct metric_builder { namespace flecs { +/** + * @defgroup cpp_addons_metrics Metrics + * @ingroup cpp_addons + * The metrics module extracts metrics from components and makes them available + * through a unified component interface. + * + * @{ + */ + struct metrics { using Value = EcsMetricValue; using Source = EcsMetricSource; @@ -18512,6 +19600,8 @@ struct metrics { metrics(flecs::world& world); }; +/** @} */ + } #endif @@ -18527,9 +19617,9 @@ namespace flecs { /** * @defgroup cpp_addons_alerts Alerts - * @brief Alert implementation. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Alert implementation. + * * @{ */ @@ -18569,9 +19659,9 @@ namespace flecs { /** * @defgroup cpp_addons_json Json - * @brief Functions for serializing to/from JSON. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Functions for serializing to/from JSON. + * * @{ */ @@ -18603,80 +19693,83 @@ namespace flecs { /** * @defgroup cpp_addons_app App - * @brief Optional addon for running the main application loop. - * \ingroup cpp_addons + * @ingroup cpp_addons + * Optional addon for running the main application loop. + * * @{ */ /** App builder interface */ struct app_builder { app_builder(flecs::world_t *world) - : m_world(world) - , m_desc{} + : world_(world) + , desc_{} { const ecs_world_info_t *stats = ecs_get_world_info(world); - m_desc.target_fps = stats->target_fps; + desc_.target_fps = stats->target_fps; ecs_ftime_t t_zero = 0.0; - if (ECS_EQ(m_desc.target_fps, t_zero)) { - m_desc.target_fps = 60; + if (ECS_EQ(desc_.target_fps, t_zero)) { + desc_.target_fps = 60; } } app_builder& target_fps(ecs_ftime_t value) { - m_desc.target_fps = value; + desc_.target_fps = value; return *this; } app_builder& delta_time(ecs_ftime_t value) { - m_desc.delta_time = value; + desc_.delta_time = value; return *this; } app_builder& threads(int32_t value) { - m_desc.threads = value; + desc_.threads = value; return *this; } app_builder& frames(int32_t value) { - m_desc.frames = value; + desc_.frames = value; return *this; } app_builder& enable_rest(uint16_t port = 0) { - m_desc.enable_rest = true; - m_desc.port = port; + desc_.enable_rest = true; + desc_.port = port; return *this; } - app_builder& enable_monitor(bool value = true) { - m_desc.enable_monitor = value; + app_builder& enable_stats(bool value = true) { + desc_.enable_stats = value; return *this; } app_builder& init(ecs_app_init_action_t value) { - m_desc.init = value; + desc_.init = value; return *this; } app_builder& ctx(void *value) { - m_desc.ctx = value; + desc_.ctx = value; return *this; } int run() { - int result = ecs_app_run(m_world, &m_desc); - if (ecs_should_quit(m_world)) { + int result = ecs_app_run(world_, &desc_); + if (ecs_should_quit(world_)) { // Only free world if quit flag is set. This ensures that we won't - // try to cleanup the world if the app is used in an environment + // try to cleanup the world if the app is used in an environment // that takes over the main loop, like with emscripten. - ecs_fini(m_world); + if (!flecs_poly_release(world_)) { + ecs_fini(world_); + } } return result; } private: - flecs::world_t *m_world; - ecs_app_desc_t m_desc; + flecs::world_t *world_; + ecs_app_desc_t desc_; }; /** @} */ @@ -18684,6 +19777,79 @@ struct app_builder { } +#endif +#ifdef FLECS_SCRIPT +/** + * @file addons/cpp/mixins/script/decl.hpp + * @brief Script declarations. + */ + +#pragma once + +/** + * @file addons/cpp/mixins/script/builder.hpp + * @brief Script builder. + */ + +#pragma once + +namespace flecs { + +/** + * @ingroup cpp_addons_script + * @{ + */ + +/** Script builder interface */ +struct script_builder { + script_builder(flecs::world_t *world, const char *name = nullptr) + : world_(world) + , desc_{} + { + if (name != nullptr) { + ecs_entity_desc_t entity_desc = {}; + entity_desc.name = name; + entity_desc.sep = "::"; + entity_desc.root_sep = "::"; + this->desc_.entity = ecs_entity_init(world, &entity_desc); + } + } + + script_builder& code(const char *str) { + desc_.code = str; + return *this; + } + + script_builder& filename(const char *str) { + desc_.filename = str; + return *this; + } + + flecs::entity run() const; + +protected: + flecs::world_t *world_; + ecs_script_desc_t desc_; +}; + +} + + +namespace flecs { + +/** + * @defgroup cpp_addons_script Script + * @ingroup cpp_addons + * + * @{ + */ + +struct script_builder; + +/** @} */ + +} + #endif /** @@ -18698,9 +19864,9 @@ namespace log { /** * @defgroup cpp_log Logging - * @brief Logging functions. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Logging functions. + * * @{ */ @@ -18709,7 +19875,7 @@ inline void set_level(int level) { ecs_log_set_level(level); } -inline int get_level(void) { +inline int get_level() { return ecs_log_get_level(); } @@ -18794,15 +19960,15 @@ inline void pop() { namespace flecs { namespace _ { - struct pair_base { }; + struct pair_base { }; } // _ /** * @defgroup cpp_pair_type Pair type - * @brief Compile time utilities for working with relationship pairs. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Compile time utilities for working with relationship pairs. + * * @{ */ @@ -18810,16 +19976,16 @@ namespace _ { * The pair type can be used to represent a pair at compile time, and is able * to automatically derive the storage type associated with the pair, accessible * through pair::type. - * + * * The storage type is derived using the following rules: * - if pair::first is non-empty, the storage type is pair::first * - if pair::first is empty and pair::second is non-empty, the storage type is pair::second - * + * * The pair type can hold a temporary value so that it can be used in the * signatures of queries */ template -struct pair : _::pair_base { +struct pair : _::pair_base { using type = conditional_t::value || is_empty::value, First, Second>; using first = First; using second = Second; @@ -18829,13 +19995,13 @@ struct pair : _::pair_base { // This allows the class to be used as a temporary object pair(const type& v) : ref_(const_cast(v)) { } - operator type&() { + operator type&() { return ref_; } - operator const type&() const { + operator const type&() const { return ref_; - } + } type* operator->() { return &ref_; @@ -18852,7 +20018,7 @@ struct pair : _::pair_base { const type& operator*() const { return ref_; } - + private: type& ref_; }; @@ -18902,7 +20068,7 @@ using actual_type_t = typename actual_type::type; // Get type without const, *, & template struct base_type { - using type = decay_t< remove_pointer_t< actual_type_t > >; + using type = decay_t< actual_type_t >; }; template @@ -18922,7 +20088,7 @@ using base_arg_type_t = typename base_arg_type::type; // Test if type is the same as its actual type template struct is_actual { - static constexpr bool value = + static constexpr bool value = std::is_same >::value && !is_enum::value; }; @@ -19067,7 +20233,7 @@ void ctor_move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count, } } -// Move assign + dtor (non-trivial move assigmnment) +// Move assign + dtor (non-trivial move assignment) // Typically used when moving a component to a deleted component template ::value > = 0> @@ -19087,7 +20253,7 @@ void move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count, } } -// Move assign + dtor (trivial move assigmnment) +// Move assign + dtor (trivial move assignment) // Typically used when moving a component to a deleted component template ::value > = 0> @@ -19195,8 +20361,6 @@ ecs_move_t move() { // Component types must be move assignable template ::value > = 0> ecs_move_t move() { - flecs_static_assert(always_false::value, - "component type must be move assignable"); return ecs_move_illegal; } @@ -19239,8 +20403,6 @@ ecs_move_t move_ctor() { // Component types must be move constructible template ::value > = 0> ecs_move_t move_ctor() { - flecs_static_assert(always_false::value, - "component type must be move constructible"); return ecs_move_ctor_illegal; } @@ -19265,8 +20427,6 @@ template ::value || ! std::is_destructible::value > = 0> ecs_move_t ctor_move_dtor() { - flecs_static_assert(always_false::value, - "component type must be move constructible and destructible"); return ecs_move_ctor_illegal; } @@ -19293,8 +20453,6 @@ template ::value || ! std::is_destructible::value > = 0> ecs_move_t move_dtor() { - flecs_static_assert(always_false::value, - "component type must be move constructible and destructible"); return ecs_move_ctor_illegal; } @@ -19311,80 +20469,6 @@ ecs_move_t move_dtor() { } // _ } // flecs -/** - * @file addons/cpp/ref.hpp - * @brief Class that caches data to speedup get operations. - */ - -#pragma once - -namespace flecs -{ - -/** - * @defgroup cpp_ref Refs - * @brief Refs are a fast mechanism for referring to a specific entity/component. - * - * \ingroup cpp_core - * @{ - */ - -/** Component reference. - * Reference to a component from a specific entity. - */ -template -struct ref { - ref() : m_world(nullptr), m_ref{} { } - - ref(world_t *world, entity_t entity, flecs::id_t id = 0) - : m_ref() - { - // the world we were called with may be a stage; convert it to a world - // here if that is the case - m_world = world ? const_cast(ecs_get_world(world)) - : nullptr; - if (!id) { - id = _::cpp_type::id(world); - } - - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - - m_ref = ecs_ref_init_id(m_world, entity, id); - } - - T* operator->() { - T* result = static_cast(ecs_ref_get_id( - m_world, &m_ref, this->m_ref.id)); - - ecs_assert(result != NULL, ECS_INVALID_PARAMETER, NULL); - - return result; - } - - T* get() { - return static_cast(ecs_ref_get_id( - m_world, &m_ref, this->m_ref.id)); - } - - T* try_get() { - if (!m_world || !m_ref.entity) { - return nullptr; - } - - return get(); - } - - flecs::entity entity() const; - -private: - world_t *m_world; - flecs::ref_t m_ref; -}; - -/** @} */ - -} - /** * @file addons/cpp/world.hpp * @brief World class. @@ -19400,15 +20484,16 @@ namespace flecs // set(T&&), T = constructible 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); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); if (!ecs_is_deferred(world)) { - T& dst = *static_cast(ecs_get_mut_id(world, entity, id)); + T& dst = *static_cast(ecs_ensure_id(world, entity, id)); dst = FLECS_MOV(value); ecs_modified_id(world, entity, id); } else { - T& dst = *static_cast(ecs_get_mut_modified_id(world, entity, id)); + T& dst = *static_cast(ecs_ensure_modified_id(world, entity, id)); dst = FLECS_MOV(value); } } @@ -19416,15 +20501,16 @@ inline void set(world_t *world, flecs::entity_t entity, T&& value, flecs::id_t i // set(const T&), T = constructible template ::value > = 0> inline void set(world_t *world, flecs::entity_t entity, const T& value, flecs::id_t id) { - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); if (!ecs_is_deferred(world)) { - T& dst = *static_cast(ecs_get_mut_id(world, entity, id)); + T& dst = *static_cast(ecs_ensure_id(world, entity, id)); dst = FLECS_MOV(value); ecs_modified_id(world, entity, id); } else { - T& dst = *static_cast(ecs_get_mut_modified_id(world, entity, id)); + T& dst = *static_cast(ecs_ensure_modified_id(world, entity, id)); dst = FLECS_MOV(value); } } @@ -19432,15 +20518,16 @@ inline void set(world_t *world, flecs::entity_t entity, const T& value, flecs::i // set(T&&), T = not constructible 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); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); if (!ecs_is_deferred(world)) { - T& dst = *static_cast*>(ecs_get_mut_id(world, entity, id)); + T& dst = *static_cast*>(ecs_ensure_id(world, entity, id)); dst = FLECS_MOV(value); ecs_modified_id(world, entity, id); } else { - T& dst = *static_cast*>(ecs_get_mut_modified_id(world, entity, id)); + T& dst = *static_cast*>(ecs_ensure_modified_id(world, entity, id)); dst = FLECS_MOV(value); } } @@ -19448,49 +20535,51 @@ inline void set(world_t *world, flecs::entity_t entity, T&& value, flecs::id_t i // set(const T&), T = not constructible template ::value > = 0> inline void set(world_t *world, flecs::entity_t entity, const T& value, flecs::id_t id) { - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); if (!ecs_is_deferred(world)) { - T& dst = *static_cast*>(ecs_get_mut_id(world, entity, id)); + T& dst = *static_cast*>(ecs_ensure_id(world, entity, id)); dst = FLECS_MOV(value); ecs_modified_id(world, entity, id); } else { - T& dst = *static_cast*>(ecs_get_mut_modified_id(world, entity, id)); + T& dst = *static_cast*>(ecs_ensure_modified_id(world, entity, id)); dst = FLECS_MOV(value); } } // emplace for T(Args...) -template , Args...>::value || std::is_default_constructible>::value > = 0> inline void emplace(world_t *world, flecs::entity_t entity, flecs::id_t id, Args&&... args) { - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - T& dst = *static_cast(ecs_emplace_id(world, entity, id)); - + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + T& dst = *static_cast(ecs_emplace_id(world, entity, id, nullptr)); + FLECS_PLACEMENT_NEW(&dst, T{FLECS_FWD(args)...}); - ecs_modified_id(world, entity, id); + ecs_modified_id(world, entity, id); } // set(T&&) template inline void set(world_t *world, entity_t entity, A&& value) { - id_t id = _::cpp_type::id(world); + id_t id = _::type::id(world); flecs::set(world, entity, FLECS_FWD(value), id); } // set(const T&) template inline void set(world_t *world, entity_t entity, const A& value) { - id_t id = _::cpp_type::id(world); + id_t id = _::type::id(world); flecs::set(world, entity, value, id); } /** Return id without generation. - * - * @see ecs_strip_generation + * + * @see ecs_strip_generation() */ inline flecs::id_t strip_generation(flecs::entity_t e) { return ecs_strip_generation(e); @@ -19506,9 +20595,9 @@ struct scoped_world; /** * @defgroup cpp_world World - * @brief World operations. - * - * \ingroup cpp_core + * @ingroup cpp_core + * World operations. + * * @{ */ @@ -19519,182 +20608,238 @@ struct scoped_world; struct world { /** Create world. */ - explicit world() - : m_world( ecs_init() ) - , m_owned( true ) { init_builtin_components(); } + explicit world() + : world_( ecs_init() ) { + init_builtin_components(); + } /** Create world with command line arguments. * Currently command line arguments are not interpreted, but they may be * used in the future to configure Flecs parameters. */ explicit world(int argc, char *argv[]) - : m_world( ecs_init_w_args(argc, argv) ) - , m_owned( true ) { init_builtin_components(); } + : world_( ecs_init_w_args(argc, argv) ) { + init_builtin_components(); + } /** Create world from C world. */ explicit world(world_t *w) - : m_world( w ) - , m_owned( false ) { } + : world_( w ) { + if (w) { + flecs_poly_claim(w); + } + } - /** Not allowed to copy a world. May only take a reference. + /** Not allowed to copy a world. May only take a reference. */ - world(const world& obj) = delete; + world(const world& obj) { + this->world_ = obj.world_; + flecs_poly_claim(this->world_); + } - world(world&& obj) { - m_world = obj.m_world; - m_owned = obj.m_owned; - obj.m_world = nullptr; - obj.m_owned = false; + world& operator=(const world& obj) noexcept { + release(); + this->world_ = obj.world_; + flecs_poly_claim(this->world_); + return *this; } - /* Implicit conversion to world_t* */ - operator world_t*() const { return m_world; } + world(world&& obj) noexcept { + world_ = obj.world_; + obj.world_ = nullptr; + } - /** Not allowed to copy a world. May only take a reference. - */ - world& operator=(const world& obj) = delete; + world& operator=(world&& obj) noexcept { + release(); + world_ = obj.world_; + obj.world_ = nullptr; + return *this; + } - world& operator=(world&& obj) { - this->~world(); + /* Releases the underlying world object. If this is the last handle, the world + will be finalized. */ + void release() { + if (world_) { + if (!flecs_poly_release(world_)) { + if (ecs_stage_get_id(world_) == -1) { + ecs_stage_free(world_); + } else { + // before we call ecs_fini(), we increment the reference count back to 1 + // otherwise, copies of this object created during ecs_fini (e.g. a component on_remove hook) + // would call again this destructor and ecs_fini(). + flecs_poly_claim(world_); + ecs_fini(world_); + } + } + world_ = nullptr; + } + } - m_world = obj.m_world; - m_owned = obj.m_owned; - obj.m_world = nullptr; - obj.m_owned = false; - return *this; + ~world() { + release(); } - - ~world() { - if (m_owned && ecs_stage_is_async(m_world)) { - ecs_async_stage_free(m_world); - } else - if (m_owned && m_world) { - ecs_fini(m_world); - } + + /* Implicit conversion to world_t* */ + operator world_t*() const { return world_; } + + /** Make current world object owner of the world. This may only be called on + * one flecs::world object, an may only be called once. Failing to do so + * will result in undefined behavior. + * + * This operation allows a custom (C) world to be wrapped by a C++ object, + * and transfer ownership so that the world is automatically cleaned up. + */ + void make_owner() { + flecs_poly_release(world_); } /** Deletes and recreates the world. */ void reset() { - // Can only reset the world if we own the world object. - ecs_assert(this->m_owned, ECS_INVALID_OPERATION, NULL); - ecs_fini(m_world); - m_world = ecs_init(); + /* Make sure there's only one reference to the world */ + ecs_assert(flecs_poly_refcount(world_) == 1, ECS_INVALID_OPERATION, + "reset would invalidate other handles"); + ecs_fini(world_); + world_ = ecs_init(); } /** Obtain pointer to C world object. */ world_t* c_ptr() const { - return m_world; + return world_; } /** Signal application should quit. * After calling this operation, the next call to progress() returns false. */ void quit() const { - ecs_quit(m_world); + ecs_quit(world_); } /** Register action to be executed when world is destroyed. */ - void atfini(ecs_fini_action_t action, void *ctx) const { - ecs_atfini(m_world, action, ctx); + void atfini(ecs_fini_action_t action, void *ctx = nullptr) const { + ecs_atfini(world_, action, ctx); } /** Test if quit() has been called. */ bool should_quit() const { - return ecs_should_quit(m_world); + return ecs_should_quit(world_); } /** Begin frame. * When an application does not use progress() to control the main loop, it * can still use Flecs features such as FPS limiting and time measurements. - * This operation needs to be invoked whenever a new frame is about to get + * This operation needs to be invoked whenever a new frame is about to get * processed. * - * Calls to frame_begin must always be followed by frame_end. + * Calls to frame_begin() must always be followed by frame_end(). * - * The function accepts a delta_time parameter, which will get passed to - * systems. This value is also used to compute the amount of time the - * function needs to sleep to ensure it does not exceed the target_fps, when + * The function accepts a delta_time parameter, which will get passed to + * systems. This value is also used to compute the amount of time the + * function needs to sleep to ensure it does not exceed the target_fps, when * it is set. When 0 is provided for delta_time, the time will be measured. * * This function should only be ran from the main thread. * * @param delta_time Time elapsed since the last frame. * @return The provided delta_time, or measured time if 0 was provided. + * + * @see ecs_frame_begin() + * @see flecs::world::frame_end() */ ecs_ftime_t frame_begin(float delta_time = 0) const { - return ecs_frame_begin(m_world, delta_time); + return ecs_frame_begin(world_, delta_time); } - /** End frame. + /** End frame. * This operation must be called at the end of the frame, and always after - * ecs_frame_begin. + * frame_begin(). * * This function should only be ran from the main thread. + * + * @see ecs_frame_end() + * @see flecs::world::frame_begin() */ void frame_end() const { - ecs_frame_end(m_world); + ecs_frame_end(world_); } - /** Begin staging. - * When an application does not use ecs_progress to control the main loop, it - * can still use Flecs features such as the defer queue. When an application - * needs to stage changes, it needs to call this function after ecs_frame_begin. - * A call to ecs_readonly_begin must be followed by a call to ecs_readonly_end. - * - * When staging is enabled, modifications to entities are stored to a stage. - * This ensures that arrays are not modified while iterating. Modifications are - * merged back to the "main stage" when ecs_readonly_end is invoked. + /** Begin readonly mode. * - * While the world is in staging mode, no structural changes (add/remove/...) - * can be made to the world itself. Operations must be executed on a stage - * instead (see ecs_get_stage). - * - * This function should only be ran from the main thread. + * @param multi_threaded Whether to enable readonly/multi threaded mode. + * + * @return Whether world is currently readonly. * - * @return Whether world is currently staged. + * @see ecs_readonly_begin() + * @see flecs::world::is_readonly() + * @see flecs::world::readonly_end() */ - bool readonly_begin() const { - return ecs_readonly_begin(m_world); + bool readonly_begin(bool multi_threaded = false) const { + return ecs_readonly_begin(world_, multi_threaded); } - /** End staging. - * Leaves staging mode. After this operation the world may be directly mutated - * again. By default this operation also merges data back into the world, unless - * automerging was disabled explicitly. - * - * This function should only be ran from the main thread. + /** End readonly mode. + * + * @see ecs_readonly_end() + * @see flecs::world::is_readonly() + * @see flecs::world::readonly_begin() */ void readonly_end() const { - ecs_readonly_end(m_world); + ecs_readonly_end(world_); } - /** Defer operations until end of frame. + /** Defer operations until end of frame. * When this operation is invoked while iterating, operations inbetween the - * defer_begin and defer_end operations are executed at the end of the frame. + * defer_begin() and defer_end() operations are executed at the end of the frame. * * This operation is thread safe. + * + * @return true if world changed from non-deferred mode to deferred mode. + * + * @see ecs_defer_begin() + * @see flecs::world::defer() + * @see flecs::world::defer_end() + * @see flecs::world::is_deferred() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() */ bool defer_begin() const { - return ecs_defer_begin(m_world); + return ecs_defer_begin(world_); } - /** End block of operations to defer. - * See defer_begin. + /** End block of operations to defer. + * See defer_begin(). * * This operation is thread safe. + * + * @return true if world changed from deferred mode to non-deferred mode. + * + * @see ecs_defer_end() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::is_deferred() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() */ bool defer_end() const { - return ecs_defer_end(m_world); + return ecs_defer_end(world_); } /** Test whether deferring is enabled. + * + * @return True if deferred, false if not. + * + * @see ecs_is_deferred() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() */ bool is_deferred() const { - return ecs_is_deferred(m_world); + return ecs_is_deferred(world_); } /** Configure world to have N stages. @@ -19703,23 +20848,29 @@ struct world { * multiple threads, where each thread gets its own queue, and commands are * merged when threads are synchronized. * - * Note that set_threads() already creates the appropriate number of stages. - * The set_stage_count() operation is useful for applications that want to manage + * Note that set_threads() already creates the appropriate number of stages. + * The set_stage_count() operation is useful for applications that want to manage * their own stages and/or threads. - * + * * @param stages The number of stages. + * + * @see ecs_set_stage_count() + * @see flecs::world::get_stage_count() */ void set_stage_count(int32_t stages) const { - ecs_set_stage_count(m_world, stages); + ecs_set_stage_count(world_, stages); } /** Get number of configured stages. - * Return number of stages set by set_stage_count. + * Return number of stages set by set_stage_count(). * * @return The number of stages used for threading. + * + * @see ecs_get_stage_count() + * @see flecs::world::set_stage_count() */ int32_t get_stage_count() const { - return ecs_get_stage_count(m_world); + return ecs_get_stage_count(world_); } /** Get current stage id. @@ -19729,41 +20880,22 @@ struct world { * @return The stage id. */ int32_t get_stage_id() const { - return ecs_get_stage_id(m_world); + return ecs_stage_get_id(world_); } /** Test if is a stage. * If this function returns false, it is guaranteed that this is a valid * world object. - * + * * @return True if the world is a stage, false if not. */ bool is_stage() const { ecs_assert( - ecs_poly_is(m_world, ecs_world_t) || - ecs_poly_is(m_world, ecs_stage_t), - ECS_INVALID_PARAMETER, NULL); - return ecs_poly_is(m_world, ecs_stage_t); - } - - /** Enable/disable automerging for world or stage. - * When automerging is enabled, staged data will automatically be merged - * with the world when staging ends. This happens at the end of progress(), - * at a sync point or when readonly_end() is called. - * - * Applications can exercise more control over when data from a stage is - * merged by disabling automerging. This requires an application to - * explicitly call merge() on the stage. - * - * When this function is invoked on the world, it sets all current stages to - * the provided value and sets the default for new stages. When this - * function is invoked on a stage, automerging is only set for that specific - * stage. - * - * @param automerge Whether to enable or disable automerging. - */ - void set_automerge(bool automerge) const { - ecs_set_automerge(m_world, automerge); + flecs_poly_is(world_, ecs_world_t) || + flecs_poly_is(world_, ecs_stage_t), + ECS_INVALID_PARAMETER, + "flecs::world instance contains invalid reference to world or stage"); + return flecs_poly_is(world_, ecs_stage_t); } /** Merge world or stage. @@ -19773,27 +20905,29 @@ struct world { * (either after progress() or after readonly_end()). * * This operation may be called on an already merged stage or world. + * + * @see ecs_merge() */ void merge() const { - ecs_merge(m_world); + ecs_merge(world_); } /** Get stage-specific world pointer. - * Flecs threads can safely invoke the API as long as they have a private + * Flecs threads can safely invoke the API as long as they have a private * context to write to, also referred to as the stage. This function returns a * pointer to a stage, disguised as a world pointer. * * Note that this function does not(!) create a new world. It simply wraps the * existing world in a thread-specific context, which the API knows how to * unwrap. The reason the stage is returned as an ecs_world_t is so that it - * can be passed transparently to the existing API functions, vs. having to + * can be passed transparently to the existing API functions, vs. having to * create a dediated API for threading. * * @param stage_id The index of the stage to retrieve. - * @return A thread-specific pointer to the world. + * @return A thread-specific pointer to the world. */ flecs::world get_stage(int32_t stage_id) const { - return flecs::world(ecs_get_stage(m_world, stage_id)); + return flecs::world(ecs_get_stage(world_, stage_id)); } /** Create asynchronous stage. @@ -19803,20 +20937,18 @@ struct world { * * Asynchronous stages are never merged automatically, and must therefore be * manually merged with the ecs_merge function. It is not necessary to call - * defer_begin or defer_end before and after enqueuing commands, as an + * defer_begin or defer_end before and after enqueuing commands, as an * asynchronous stage unconditionally defers operations. * * The application must ensure that no commands are added to the stage while the * stage is being merged. * - * An asynchronous stage must be cleaned up by ecs_async_stage_free. - * * @return The stage. */ flecs::world async_stage() const { - auto result = flecs::world(ecs_async_stage_new(m_world)); - result.m_owned = true; - return result; + ecs_world_t *as = ecs_stage_new(world_); + flecs_poly_release(as); // world object will claim + return flecs::world(as); } /** Get actual world. @@ -19828,7 +20960,7 @@ struct world { flecs::world get_world() const { /* Safe cast, mutability is checked */ return flecs::world( - m_world ? const_cast(ecs_get_world(m_world)) : nullptr); + world_ ? const_cast(ecs_get_world(world_)) : nullptr); } /** Test whether the current world object is readonly. @@ -19836,54 +20968,80 @@ struct world { * object is readonly or whether it allows for writing. * * @return True if the world or stage is readonly. + * + * @see ecs_stage_is_readonly() + * @see flecs::world::readonly_begin() + * @see flecs::world::readonly_end() */ bool is_readonly() const { - return ecs_stage_is_readonly(m_world); + return ecs_stage_is_readonly(world_); } /** Set world context. * Set a context value that can be accessed by anyone that has a reference * to the world. * - * @param ctx The world context. + * @param ctx A pointer to a user defined structure. + * @param ctx_free A function that is invoked with ctx when the world is freed. + * + * + * @see ecs_set_ctx() + * @see flecs::world::get_ctx() */ void set_ctx(void* ctx, ecs_ctx_free_t ctx_free = nullptr) const { - ecs_set_ctx(m_world, ctx, ctx_free); + ecs_set_ctx(world_, ctx, ctx_free); } /** Get world context. + * This operation retrieves a previously set world context. * - * @return The configured world context. + * @return The context set with set_binding_ctx(). If no context was set, the + * function returns NULL. + * + * @see ecs_get_ctx() + * @see flecs::world::set_ctx() */ void* get_ctx() const { - return ecs_get_ctx(m_world); + return ecs_get_ctx(world_); } /** Set world binding context. - * Set a context value that can be accessed by anyone that has a reference - * to the world. * - * @param ctx The world context. + * Same as set_ctx() but for binding context. A binding context is intended + * specifically for language bindings to store binding specific data. + * + * @param ctx A pointer to a user defined structure. + * @param ctx_free A function that is invoked with ctx when the world is freed. + * + * @see ecs_set_binding_ctx() + * @see flecs::world::get_binding_ctx() */ void set_binding_ctx(void* ctx, ecs_ctx_free_t ctx_free = nullptr) const { - ecs_set_binding_ctx(m_world, ctx, ctx_free); + ecs_set_binding_ctx(world_, ctx, ctx_free); } /** Get world binding context. + * This operation retrieves a previously set world binding context. + * + * @return The context set with set_binding_ctx(). If no context was set, the + * function returns NULL. * - * @return The configured world context. + * @see ecs_get_binding_ctx() + * @see flecs::world::set_binding_ctx() */ void* get_binding_ctx() const { - return ecs_get_binding_ctx(m_world); + return ecs_get_binding_ctx(world_); } /** Preallocate memory for number of entities. * This function preallocates memory for the entity index. * * @param entity_count Number of entities to preallocate memory for. + * + * @see ecs_dim() */ void dim(int32_t entity_count) const { - ecs_dim(m_world, entity_count); + ecs_dim(world_, entity_count); } /** Set entity range. @@ -19891,9 +21049,11 @@ struct world { * * @param min Minimum entity id issued. * @param max Maximum entity id issued. + * + * @see ecs_set_entity_range() */ void set_entity_range(entity_t min, entity_t max) const { - ecs_set_entity_range(m_world, min, max); + ecs_set_entity_range(world_, min, max); } /** Enforce that operations cannot modify entities outside of range. @@ -19903,76 +21063,86 @@ struct world { * networked applications. * * @param enabled True if range check should be enabled, false if not. + * + * @see ecs_enable_range_check() */ - void enable_range_check(bool enabled) const { - ecs_enable_range_check(m_world, enabled); + void enable_range_check(bool enabled = true) const { + ecs_enable_range_check(world_, enabled); } /** Set current scope. * * @param scope The scope to set. * @return The current scope; - * @see ecs_set_scope + * + * @see ecs_set_scope() + * @see flecs::world::get_scope() */ flecs::entity set_scope(const flecs::entity_t scope) const; /** Get current scope. * * @return The current scope. - * * @see ecs_get_scope + * + * @see ecs_get_scope() + * @see flecs::world::set_scope() */ flecs::entity get_scope() const; /** Same as set_scope but with type. - * * @see ecs_set_scope + * + * @see ecs_set_scope() + * @see flecs::world::get_scope() */ template flecs::entity set_scope() const; /** Set search path. - * @see ecs_set_lookup_path + * + * @see ecs_set_lookup_path() + * @see flecs::world::lookup() */ flecs::entity_t* set_lookup_path(const flecs::entity_t *search_path) const { - return ecs_set_lookup_path(m_world, search_path); + return ecs_set_lookup_path(world_, search_path); } /** Lookup entity by name. - * + * * @param name Entity name. - * @param search_path When false, only the current scope is searched. + * @param recursive When false, only the current scope is searched. * @result The entity if found, or 0 if not found. */ - flecs::entity lookup(const char *name, bool search_path = true) const; + flecs::entity lookup(const char *name, const char *sep = "::", const char *root_sep = "::", bool recursive = true) const; /** Set singleton component. */ template ::value > = 0> void set(const T& value) const { - flecs::set(m_world, _::cpp_type::id(m_world), value); + flecs::set(world_, _::type::id(world_), value); } /** Set singleton component. */ template ::value > = 0> void set(T&& value) const { - flecs::set(m_world, _::cpp_type::id(m_world), + flecs::set(world_, _::type::id(world_), FLECS_FWD(value)); } /** Set singleton pair. */ - template , + template , typename A = actual_type_t

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

(m_world, _::cpp_type::id(m_world), value); + flecs::set

(world_, _::type::id(world_), value); } /** Set singleton pair. */ - template , + template , typename A = actual_type_t

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

(m_world, _::cpp_type::id(m_world), FLECS_FWD(value)); + flecs::set

(world_, _::type::id(world_), FLECS_FWD(value)); } /** Set singleton pair. @@ -19992,15 +21162,16 @@ struct world { template void emplace(Args&&... args) const { - flecs::id_t component_id = _::cpp_type::id(m_world); - flecs::emplace(m_world, component_id, component_id, - FLECS_FWD(args)...); - } + flecs::id_t component_id = _::type::id(world_); + flecs::emplace(world_, component_id, component_id, FLECS_FWD(args)...); + } - /** Get mut singleton component. + /** Ensure singleton component. */ + #ifndef ensure template - T* get_mut() const; + T& ensure() const; + #endif /** Mark singleton component as modified. */ @@ -20019,7 +21190,7 @@ struct world { /** Get singleton pair. */ - template , + template , typename A = actual_type_t

> const A* get() const; @@ -20027,11 +21198,27 @@ struct world { */ template const First* get(Second second) const; - + /** Get singleton component inside a callback. */ - template ::value > = 0 > - void get(const Func& func) const; + template ::value > = 0 > + void get(const Func& func) const; + + /** Get mutable singleton component. + */ + template + T* get_mut() const; + + /** Get mutable singleton pair. + */ + template , + typename A = actual_type_t

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

(); + } + + /** Get a mutable pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + */ + template::value> = 0> + First* get_mut(Second second) const { + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast( + ecs_get_mut_id(world_, id_, ecs_pair(first, second))); + } + + /** Get a mutable pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @param constant the enum constant. + */ + template::value> = 0> + First* get_mut(Second constant) const { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(constant); + return get_mut(target); + } + + /** Get mutable component value (untyped). + * + * @param comp The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + void* get_mut(flecs::id_t comp) const { + return ecs_get_mut_id(world_, id_, comp); + } + + /** Get a mutable pair (untyped). + * This operation gets the value for a pair from the entity. If neither the + * first nor the second part of the pair are components, the operation + * will fail. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + void* get_mut(flecs::entity_t first, flecs::entity_t second) const { + return ecs_get_mut_id(world_, id_, ecs_pair(first, second)); + } + + /** Get the second part for a pair. + * This operation gets the value for a pair from the entity. The first + * part of the pair should not be a component. + * + * @tparam Second the second element of a pair. + * @param first The first part of the pair. + */ + template + Second* get_mut_second(flecs::entity_t first) const { + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast( + ecs_get_mut_id(world_, id_, ecs_pair(first, second))); + } + + /** Get the second part for a pair. + * This operation gets the value for a pair from the entity. The first + * part of the pair should not be a component. + * + * @tparam First The first element of the pair. + * @tparam Second the second element of a pair. + */ + template + Second* get_mut_second() const { + return get_mut>(); + } + /** Get target for a given pair. * This operation returns the target for a given pair. The optional * index can be used to iterate through targets, in case the entity has @@ -22119,8 +23568,10 @@ struct entity_view : public id { * This operation can be used to lookup, for example, which prefab is providing * a component by specifying the IsA pair: * - * // Is Position provided by the entity or one of its base entities? - * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) + * @code + * // Is Position provided by the entity or one of its base entities? + * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) + * @endcode * * @param relationship The relationship to follow. * @param id The id to lookup. @@ -22140,7 +23591,7 @@ struct entity_view : public id { * @return The depth. */ int32_t depth(flecs::entity_t rel) const { - return ecs_get_depth(m_world, m_id, rel); + return ecs_get_depth(world_, id_, rel); } /** Get depth for given relationship. @@ -22150,7 +23601,7 @@ struct entity_view : public id { */ template int32_t depth() const { - return this->depth(_::cpp_type::id(m_world)); + return this->depth(_::type::id(world_)); } /** Get parent of entity. @@ -22176,7 +23627,7 @@ struct entity_view : public id { * @return True if the entity has the provided entity, false otherwise. */ bool has(flecs::id_t e) const { - return ecs_has_id(m_world, m_id, e); + return ecs_has_id(world_, id_, e); } /** Check if entity has the provided component. @@ -22186,14 +23637,14 @@ struct entity_view : public id { */ template bool has() const { - flecs::id_t cid = _::cpp_type::id(m_world); - bool result = ecs_has_id(m_world, m_id, cid); + flecs::id_t cid = _::type::id(world_); + bool result = ecs_has_id(world_, id_, cid); if (result) { return result; } if (is_enum::value) { - return ecs_has_pair(m_world, m_id, cid, flecs::Wildcard); + return ecs_has_pair(world_, id_, cid, flecs::Wildcard); } return false; @@ -22207,9 +23658,12 @@ struct entity_view : public id { */ template ::value > = 0> bool has(E value) const { - auto r = _::cpp_type::id(m_world); - auto o = enum_type(m_world).entity(value); - return ecs_has_pair(m_world, m_id, r, o); + auto r = _::type::id(world_); + auto o = enum_type(world_).entity(value); + ecs_assert(o, ECS_INVALID_PARAMETER, + "Constant was not found in Enum reflection data." + " Did you mean to use has() instead of has(E)?"); + return ecs_has_pair(world_, id_, r, o); } /** Check if entity has the provided pair. @@ -22220,7 +23674,7 @@ struct entity_view : public id { */ template bool has() const { - return this->has(_::cpp_type::id(m_world)); + return this->has(_::type::id(world_)); } /** Check if entity has the provided pair. @@ -22231,8 +23685,8 @@ struct entity_view : public id { */ template::value > = 0> bool has(Second second) const { - auto comp_id = _::cpp_type::id(m_world); - return ecs_has_id(m_world, m_id, ecs_pair(comp_id, second)); + auto comp_id = _::type::id(world_); + return ecs_has_id(world_, id_, ecs_pair(comp_id, second)); } /** Check if entity has the provided pair. @@ -22243,7 +23697,7 @@ struct entity_view : public id { */ template bool has_second(flecs::entity_t first) const { - return this->has(first, _::cpp_type::id(m_world)); + return this->has(first, _::type::id(world_)); } /** Check if entity has the provided pair. @@ -22254,7 +23708,7 @@ struct entity_view : public id { */ template::value > = 0> bool has(E value) const { - const auto& et = enum_type(this->m_world); + const auto& et = enum_type(this->world_); flecs::entity_t second = et.entity(value); return has(second); } @@ -22266,7 +23720,7 @@ struct entity_view : public id { * @return True if the entity has the provided component, false otherwise. */ bool has(flecs::id_t first, flecs::id_t second) const { - return ecs_has_id(m_world, m_id, ecs_pair(first, second)); + return ecs_has_id(world_, id_, ecs_pair(first, second)); } /** Check if entity owns the provided entity. @@ -22276,7 +23730,7 @@ struct entity_view : public id { * @return True if the entity owns the provided entity, false otherwise. */ bool owns(flecs::id_t e) const { - return ecs_owns_id(m_world, m_id, e); + return ecs_owns_id(world_, id_, e); } /** Check if entity owns the provided pair. @@ -22287,7 +23741,7 @@ struct entity_view : public id { */ template bool owns(flecs::id_t second) const { - auto comp_id = _::cpp_type::id(m_world); + auto comp_id = _::type::id(world_); return owns(ecs_pair(comp_id, second)); } @@ -22309,7 +23763,7 @@ struct entity_view : public id { */ template bool owns() const { - return owns(_::cpp_type::id(m_world)); + return owns(_::type::id(world_)); } /** Check if entity owns the provided pair. @@ -22322,8 +23776,8 @@ struct entity_view : public id { template bool owns() const { return owns( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world)); + _::type::id(world_), + _::type::id(world_)); } /** Test if id is enabled. @@ -22332,7 +23786,7 @@ struct entity_view : public id { * @return True if enabled, false if not. */ bool enabled(flecs::id_t id) const { - return ecs_is_enabled_id(m_world, m_id, id); + return ecs_is_enabled_id(world_, id_, id); } /** Test if component is enabled. @@ -22342,7 +23796,7 @@ struct entity_view : public id { */ template bool enabled() const { - return this->enabled(_::cpp_type::id(m_world)); + return this->enabled(_::type::id(world_)); } /** Test if pair is enabled. @@ -22363,7 +23817,7 @@ struct entity_view : public id { */ template bool enabled(flecs::id_t second) const { - return this->enabled(_::cpp_type::id(m_world), second); + return this->enabled(_::type::id(world_), second); } /** Test if pair is enabled. @@ -22374,7 +23828,7 @@ struct entity_view : public id { */ template bool enabled() const { - return this->enabled(_::cpp_type::id(m_world)); + return this->enabled(_::type::id(world_)); } flecs::entity clone(bool clone_value = true, flecs::entity_t dst_id = 0) const; @@ -22427,11 +23881,11 @@ struct entity_view : public id { /** Serialize entity to JSON. * - * \memberof flecs::entity_view - * \ingroup cpp_addons_json + * @memberof flecs::entity_view + * @ingroup cpp_addons_json */ -flecs::string to_json(const flecs::entity_to_json_desc_t *desc = nullptr) { - char *json = ecs_entity_to_json(m_world, m_id, desc); +flecs::string to_json(const flecs::entity_to_json_desc_t *desc = nullptr) const { + char *json = ecs_entity_to_json(world_, id_, desc); return flecs::string(json); } @@ -22442,24 +23896,82 @@ flecs::string to_json(const flecs::entity_to_json_desc_t *desc = nullptr) { * @brief Doc entity view mixin. */ -const char* doc_name() { - return ecs_doc_get_name(m_world, m_id); +/** Get human readable name. + * + * @see ecs_doc_get_name() + * @see flecs::doc::get_name() + * @see flecs::entity_builder::set_doc_name() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_name() const { + return ecs_doc_get_name(world_, id_); +} + +/** Get brief description. + * + * @see ecs_doc_get_brief() + * @see flecs::doc::get_brief() + * @see flecs::entity_builder::set_doc_brief() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_brief() const { + return ecs_doc_get_brief(world_, id_); } -const char* doc_brief() { - return ecs_doc_get_brief(m_world, m_id); +/** Get detailed description. + * + * @see ecs_doc_get_detail() + * @see flecs::doc::get_detail() + * @see flecs::entity_builder::set_doc_detail() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_detail() const { + return ecs_doc_get_detail(world_, id_); } -const char* doc_detail() { - return ecs_doc_get_detail(m_world, m_id); +/** Get link to external documentation. + * + * @see ecs_doc_get_link() + * @see flecs::doc::get_link() + * @see flecs::entity_builder::set_doc_link() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_link() const { + return ecs_doc_get_link(world_, id_); } -const char* doc_link() { - return ecs_doc_get_link(m_world, m_id); +/** Get color. + * + * @see ecs_doc_get_color() + * @see flecs::doc::get_color() + * @see flecs::entity_builder::set_doc_color() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_color() const { + return ecs_doc_get_color(world_, id_); } -const char* doc_color() { - return ecs_doc_get_color(m_world, m_id); +/** Get UUID. + * + * @see ecs_doc_get_uuid() + * @see flecs::doc::get_uuid() + * @see flecs::entity_builder::set_doc_uuid() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_uuid() const { + return ecs_doc_get_uuid(world_, id_); } # endif @@ -22471,11 +23983,11 @@ const char* doc_color() { /** Return number of alerts for entity. * - * \memberof flecs::entity_view - * \ingroup cpp_addons_alerts + * @memberof flecs::entity_view + * @ingroup cpp_addons_alerts */ int32_t alert_count(flecs::entity_t alert = 0) const { - return ecs_get_alert_count(m_world, m_id, alert); + return ecs_get_alert_count(world_, id_, alert); } # endif @@ -22487,61 +23999,61 @@ int32_t alert_count(flecs::entity_t alert = 0) const { /** Convert entity to enum constant. * - * \memberof flecs::entity_view - * \ingroup cpp_entities + * @memberof flecs::entity_view + * @ingroup cpp_entities */ template E to_constant() const; /** - * @file addons/cpp/mixins/event/entity_builder.hpp + * @file addons/cpp/mixins/event/entity_view.inl * @brief Event entity mixin. */ /** Emit event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @param evt The event to emit. */ -void emit(flecs::entity_t evt) { - flecs::world(m_world) +void emit(flecs::entity_t evt) const { + flecs::world(world_) .event(evt) - .entity(m_id) + .entity(id_) .emit(); } /** Emit event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @param evt The event to emit. */ -void emit(flecs::entity evt); +void emit(flecs::entity evt) const; /** Emit event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @tparam Evt The event to emit. */ template ::value> = 0> -void emit() { - this->emit(_::cpp_type::id(m_world)); +void emit() const { + this->emit(_::type::id(world_)); } /** Emit event with payload for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @tparam Evt The event to emit. */ template ::value> = 0> -void emit(const Evt& payload) { - flecs::world(m_world) - .event(_::cpp_type::id(m_world)) - .entity(m_id) +void emit(const Evt& payload) const { + flecs::world(world_) + .event(_::type::id(world_)) + .entity(id_) .ctx(&payload) .emit(); } @@ -22549,47 +24061,47 @@ void emit(const Evt& payload) { /** Enqueue event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @param evt The event to enqueue. */ -void enqueue(flecs::entity_t evt) { - flecs::world(m_world) +void enqueue(flecs::entity_t evt) const { + flecs::world(world_) .event(evt) - .entity(m_id) + .entity(id_) .enqueue(); } /** Enqueue event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @param evt The event to enqueue. */ -void enqueue(flecs::entity evt); +void enqueue(flecs::entity evt) const; /** Enqueue event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @tparam Evt The event to enqueue. */ template ::value> = 0> -void enqueue() { - this->enqueue(_::cpp_type::id(m_world)); +void enqueue() const { + this->enqueue(_::type::id(world_)); } /** Enqueue event with payload for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @tparam Evt The event to enqueue. */ template ::value> = 0> -void enqueue(const Evt& payload) { - flecs::world(m_world) - .event(_::cpp_type::id(m_world)) - .entity(m_id) +void enqueue(const Evt& payload) const { + flecs::world(world_) + .event(_::type::id(world_)) + .entity(id_) .ctx(&payload) .enqueue(); } @@ -22614,7 +24126,7 @@ namespace flecs { /** Entity builder. - * \ingroup cpp_entities + * @ingroup cpp_entities */ template struct entity_builder : entity_view { @@ -22627,10 +24139,10 @@ struct entity_builder : entity_view { * @tparam T the component type to add. */ template - Self& add() { + const Self& add() const { flecs_static_assert(is_flecs_constructible::value, "cannot default construct type: add T::T() or use emplace()"); - ecs_add_id(this->m_world, this->m_id, _::cpp_type::id(this->m_world)); + ecs_add_id(this->world_, this->id_, _::type::id(this->world_)); return to_base(); } @@ -22644,10 +24156,12 @@ struct entity_builder : entity_view { * @param value The enumeration value. */ template ::value > = 0> - Self& add(E value) { - flecs::entity_t first = _::cpp_type::id(this->m_world); - const auto& et = enum_type(this->m_world); + const Self& add(E value) const { + flecs::entity_t first = _::type::id(this->world_); + const auto& et = enum_type(this->world_); flecs::entity_t second = et.entity(value); + + ecs_assert(second, ECS_INVALID_PARAMETER, "Component was not found in reflection data."); return this->add(first, second); } @@ -22656,8 +24170,8 @@ struct entity_builder : entity_view { * * @param component The component to add. */ - Self& add(id_t component) { - ecs_add_id(this->m_world, this->m_id, component); + const Self& add(id_t component) const { + ecs_add_id(this->world_, this->id_, component); return to_base(); } @@ -22667,8 +24181,8 @@ struct entity_builder : entity_view { * @param first The first element of the pair. * @param second The second element of the pair. */ - Self& add(entity_t first, entity_t second) { - ecs_add_pair(this->m_world, this->m_id, first, second); + const Self& add(entity_t first, entity_t second) const { + ecs_add_pair(this->world_, this->id_, first, second); return to_base(); } @@ -22679,8 +24193,8 @@ struct entity_builder : entity_view { * @tparam Second The second element of the pair */ template - Self& add() { - return this->add(_::cpp_type::id(this->m_world)); + const Self& add() const { + return this->add(_::type::id(this->world_)); } /** Add a pair. @@ -22690,10 +24204,10 @@ struct entity_builder : entity_view { * @param second The second element of the pair. */ template::value > = 0> - Self& add(Second second) { + const Self& add(Second second) const { flecs_static_assert(is_flecs_constructible::value, "cannot default construct type: add T::T() or use emplace()"); - return this->add(_::cpp_type::id(this->m_world), second); + return this->add(_::type::id(this->world_), second); } /** Add a pair. @@ -22704,10 +24218,10 @@ struct entity_builder : entity_view { * @param constant the enum constant. */ template::value > = 0> - Self& add(Second constant) { + const Self& add(Second constant) const { flecs_static_assert(is_flecs_constructible::value, "cannot default construct type: add T::T() or use emplace()"); - const auto& et = enum_type(this->m_world); + const auto& et = enum_type(this->world_); return this->add(et.entity(constant)); } @@ -22718,8 +24232,8 @@ struct entity_builder : entity_view { * @tparam Second The second element of the pair */ template - Self& add_second(flecs::entity_t first) { - return this->add(first, _::cpp_type::id(this->m_world)); + const Self& add_second(flecs::entity_t first) const { + return this->add(first, _::type::id(this->world_)); } /** Conditional add. @@ -22728,7 +24242,7 @@ struct entity_builder : entity_view { * @param cond The condition to evaluate. * @param component The component to add. */ - Self& add_if(bool cond, flecs::id_t component) { + const Self& add_if(bool cond, flecs::id_t component) const { if (cond) { return this->add(component); } else { @@ -22743,7 +24257,7 @@ struct entity_builder : entity_view { * @param cond The condition to evaluate. */ template - Self& add_if(bool cond) { + const Self& add_if(bool cond) const { if (cond) { return this->add(); } else { @@ -22758,7 +24272,7 @@ struct entity_builder : entity_view { * @param first The first element of the pair. * @param second The second element of the pair. */ - Self& add_if(bool cond, flecs::entity_t first, flecs::entity_t second) { + const Self& add_if(bool cond, flecs::entity_t first, flecs::entity_t second) const { if (cond) { return this->add(first, second); } else { @@ -22766,7 +24280,7 @@ struct entity_builder : entity_view { * second which will remove all instances of the relationship. * Replacing 0 with Wildcard will make it possible to use the second * as the condition. */ - if (!second || ecs_has_id(this->m_world, first, flecs::Exclusive)) { + if (!second || ecs_has_id(this->world_, first, flecs::Exclusive)) { second = flecs::Wildcard; } return this->remove(first, second); @@ -22781,8 +24295,8 @@ struct entity_builder : entity_view { * @param second The second element of the pair. */ template - Self& add_if(bool cond, flecs::entity_t second) { - return this->add_if(cond, _::cpp_type::id(this->m_world), second); + const Self& add_if(bool cond, flecs::entity_t second) const { + return this->add_if(cond, _::type::id(this->world_), second); } /** Conditional add. @@ -22793,8 +24307,8 @@ struct entity_builder : entity_view { * @param cond The condition to evaluate. */ template - Self& add_if(bool cond) { - return this->add_if(cond, _::cpp_type::id(this->m_world)); + const Self& add_if(bool cond) const { + return this->add_if(cond, _::type::id(this->world_)); } /** Conditional add. @@ -22804,87 +24318,98 @@ struct entity_builder : entity_view { * @param constant The enumeration constant. */ template ::value > = 0> - Self& add_if(bool cond, E constant) { - const auto& et = enum_type(this->m_world); + const Self& add_if(bool cond, E constant) const { + const auto& et = enum_type(this->world_); return this->add_if(cond, et.entity(constant)); } - /** Shortcut for add(IsA, entity). + /** Shortcut for `add(IsA, entity)`. * * @param second The second element of the pair. */ - Self& is_a(entity_t second) { + const Self& is_a(entity_t second) const { return this->add(flecs::IsA, second); } - /** Shortcut for add(IsA, entity). + /** Shortcut for `add(IsA, entity)`. * * @tparam T the type associated with the entity. */ template - Self& is_a() { - return this->add(flecs::IsA, _::cpp_type::id(this->m_world)); + const Self& is_a() const { + return this->add(flecs::IsA, _::type::id(this->world_)); } - /** Shortcut for add(ChildOf, entity). + /** Shortcut for `add(ChildOf, entity)`. * * @param second The second element of the pair. */ - Self& child_of(entity_t second) { + const Self& child_of(entity_t second) const { return this->add(flecs::ChildOf, second); } - /** Shortcut for add(DependsOn, entity). + /** Shortcut for `add(DependsOn, entity)`. * * @param second The second element of the pair. */ - Self& depends_on(entity_t second) { + const Self& depends_on(entity_t second) const { return this->add(flecs::DependsOn, second); } - /** Shortcut for add(SlotOf, entity). + /** Shortcut for `add(DependsOn, entity)`. + * + * @param second The second element of the pair. + */ + template ::value> = 0> + const Self& depends_on(E second) const { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(second); + return depends_on(target); + } + + /** Shortcut for `add(SlotOf, entity)`. * * @param second The second element of the pair. */ - Self& slot_of(entity_t second) { + const Self& slot_of(entity_t second) const { return this->add(flecs::SlotOf, second); } - /** Shortcut for add(SlotOf, target(ChildOf)). + /** Shortcut for `add(SlotOf, target(ChildOf))`. */ - Self& slot() { - ecs_check(ecs_get_target(m_world, m_id, flecs::ChildOf, 0), + const Self& slot() const { + ecs_check(ecs_get_target(world_, id_, flecs::ChildOf, 0), ECS_INVALID_PARAMETER, "add ChildOf pair before using slot()"); return this->slot_of(this->target(flecs::ChildOf)); error: return to_base(); } - /** Shortcut for add(ChildOf, entity). + /** Shortcut for `add(ChildOf, entity)`. * * @tparam T the type associated with the entity. */ template - Self& child_of() { - return this->child_of(_::cpp_type::id(this->m_world)); + const Self& child_of() const { + return this->child_of(_::type::id(this->world_)); } - /** Shortcut for add(DependsOn, entity). + /** Shortcut for `add(DependsOn, entity)`. * * @tparam T the type associated with the entity. */ template - Self& depends_on() { - return this->depends_on(_::cpp_type::id(this->m_world)); + const Self& depends_on() const { + return this->depends_on(_::type::id(this->world_)); } - /** Shortcut for add(SlotOf, entity). + /** Shortcut for `add(SlotOf, entity)`. * * @tparam T the type associated with the entity. */ template - Self& slot_of() { - return this->slot_of(_::cpp_type::id(this->m_world)); + const Self& slot_of() const { + return this->slot_of(_::type::id(this->world_)); } /** Remove a component from an entity. @@ -22892,19 +24417,19 @@ struct entity_builder : entity_view { * @tparam T the type of the component to remove. */ template ::value > = 0> - Self& remove() { - ecs_remove_id(this->m_world, this->m_id, _::cpp_type::id(this->m_world)); + const Self& remove() const { + ecs_remove_id(this->world_, this->id_, _::type::id(this->world_)); return to_base(); } /** Remove pair for enum. - * This operation will remove any (Enum, *) pair from the entity. + * This operation will remove any `(Enum, *)` pair from the entity. * * @tparam E The enumeration type. */ template ::value > = 0> - Self& remove() { - flecs::entity_t first = _::cpp_type::id(this->m_world); + const Self& remove() const { + flecs::entity_t first = _::type::id(this->world_); return this->remove(first, flecs::Wildcard); } @@ -22912,8 +24437,8 @@ struct entity_builder : entity_view { * * @param entity The entity to remove. */ - Self& remove(entity_t entity) { - ecs_remove_id(this->m_world, this->m_id, entity); + const Self& remove(entity_t entity) const { + ecs_remove_id(this->world_, this->id_, entity); return to_base(); } @@ -22923,8 +24448,8 @@ struct entity_builder : entity_view { * @param first The first element of the pair. * @param second The second element of the pair. */ - Self& remove(entity_t first, entity_t second) { - ecs_remove_pair(this->m_world, this->m_id, first, second); + const Self& remove(entity_t first, entity_t second) const { + ecs_remove_pair(this->world_, this->id_, first, second); return to_base(); } @@ -22935,8 +24460,8 @@ struct entity_builder : entity_view { * @tparam Second The second element of the pair */ template - Self& remove() { - return this->remove(_::cpp_type::id(this->m_world)); + const Self& remove() const { + return this->remove(_::type::id(this->world_)); } /** Remove a pair. @@ -22946,8 +24471,8 @@ struct entity_builder : entity_view { * @param second The second element of the pair. */ template::value > = 0> - Self& remove(Second second) { - return this->remove(_::cpp_type::id(this->m_world), second); + const Self& remove(Second second) const { + return this->remove(_::type::id(this->world_), second); } /** Removes a pair. @@ -22957,8 +24482,8 @@ struct entity_builder : entity_view { * @param first The first element of the pair */ template - Self& remove_second(flecs::entity_t first) { - return this->remove(first, _::cpp_type::id(this->m_world)); + const Self& remove_second(flecs::entity_t first) const { + return this->remove(first, _::type::id(this->world_)); } /** Remove a pair. @@ -22968,168 +24493,176 @@ struct entity_builder : entity_view { * @param constant the enum constant. */ template::value > = 0> - Self& remove(Second constant) { - const auto& et = enum_type(this->m_world); + const Self& remove(Second constant) const { + const auto& et = enum_type(this->world_); flecs::entity_t second = et.entity(constant); return this->remove(second); } /** Mark id for auto-overriding. - * When an entity inherits from a base entity (using the IsA relationship) + * When an entity inherits from a base entity (using the `IsA` relationship) * any ids marked for auto-overriding on the base will be overridden * automatically by the entity. * * @param id The id to mark for overriding. - */ - Self& override(flecs::id_t id) { - return this->add(ECS_OVERRIDE | id); + */ + const Self& auto_override(flecs::id_t id) const { + return this->add(ECS_AUTO_OVERRIDE | id); } /** Mark pair for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @param first The first element of the pair. * @param second The second element of the pair. - */ - Self& override(flecs::entity_t first, flecs::entity_t second) { - return this->override(ecs_pair(first, second)); + */ + const Self& auto_override(flecs::entity_t first, flecs::entity_t second) const { + return this->auto_override(ecs_pair(first, second)); } /** Mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam T The component to mark for overriding. - */ + */ template - Self& override() { - return this->override(_::cpp_type::id(this->m_world)); + const Self& auto_override() const { + return this->auto_override(_::type::id(this->world_)); } /** Mark pair for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + */ template - Self& override(flecs::entity_t second) { - return this->override(_::cpp_type::id(this->m_world), second); + const Self& auto_override(flecs::entity_t second) const { + return this->auto_override(_::type::id(this->world_), second); } /** Mark pair for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + */ template - Self& override() { - return this->override(_::cpp_type::id(this->m_world)); + const Self& auto_override() const { + return this->auto_override(_::type::id(this->world_)); } /** Set component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam T The component to set and for which to add the OVERRIDE flag - */ + * @param val The value to set. + */ template - Self& set_override(const T& val) { - this->override(); + const Self& set_auto_override(const T& val) const { + this->auto_override(); return this->set(val); } /** Set component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam T The component to set and for which to add the OVERRIDE flag - */ + * @param val The value to set. + */ template - Self& set_override(T&& val) { - this->override(); + const Self& set_auto_override(T&& val) const { + this->auto_override(); return this->set(FLECS_FWD(val)); } /** Set pair, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + * @param val The value to set. + */ template - Self& set_override(flecs::entity_t second, const First& val) { - this->override(second); + const Self& set_auto_override(flecs::entity_t second, const First& val) const { + this->auto_override(second); return this->set(second, val); } /** Set pair, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + * @param val The value to set. + */ template - Self& set_override(flecs::entity_t second, First&& val) { - this->override(second); + const Self& set_auto_override(flecs::entity_t second, First&& val) const { + this->auto_override(second); return this->set(second, FLECS_FWD(val)); } /** Set component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + * @param val The value to set. + */ template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> - Self& set_override(const A& val) { - this->override(); + const Self& set_auto_override(const A& val) const { + this->auto_override(); return this->set(val); } /** Set component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + * @param val The value to set. + */ template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> - Self& set_override(A&& val) { - this->override(); + const Self& set_auto_override(A&& val) const { + this->auto_override(); return this->set(FLECS_FWD(val)); } /** Emplace component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam T The component to emplace and override. - */ + * @param args The arguments to pass to the constructor of `T`. + */ template - Self& emplace_override(Args&&... args) { - this->override(); + const Self& emplace_auto_override(Args&&... args) const { + this->auto_override(); - flecs::emplace(this->m_world, this->m_id, - _::cpp_type::id(this->m_world), FLECS_FWD(args)...); + flecs::emplace(this->world_, this->id_, + _::type::id(this->world_), FLECS_FWD(args)...); return to_base(); } /** Emplace pair, mark pair for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair to emplace and override. * @tparam Second The second element of the pair to emplace and override. - */ + * @param args The arguments to pass to the constructor of `Second`. + */ template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0, typename ... Args> - Self& emplace_override(Args&&... args) { - this->override(); + const Self& emplace_auto_override(Args&&... args) const { + this->auto_override(); - flecs::emplace(this->m_world, this->m_id, - ecs_pair(_::cpp_type::id(this->m_world), - _::cpp_type::id(this->m_world)), - FLECS_FWD(args)...); + flecs::emplace(this->world_, this->id_, + ecs_pair(_::type::id(this->world_), + _::type::id(this->world_)), + FLECS_FWD(args)...); return to_base(); } @@ -23138,8 +24671,8 @@ struct entity_builder : entity_view { * Enabled entities are matched with systems and can be searched with * queries. */ - Self& enable() { - ecs_enable(this->m_world, this->m_id, true); + const Self& enable() const { + ecs_enable(this->world_, this->id_, true); return to_base(); } @@ -23147,8 +24680,8 @@ struct entity_builder : entity_view { * Disabled entities are not matched with systems and cannot be searched * with queries, unless explicitly specified in the query expression. */ - Self& disable() { - ecs_enable(this->m_world, this->m_id, false); + const Self& disable() const { + ecs_enable(this->world_, this->id_, false); return to_base(); } @@ -23158,52 +24691,54 @@ struct entity_builder : entity_view { * * @param id The id to enable. * @param toggle True to enable, false to disable (default = true). - */ - Self& enable(flecs::id_t id, bool toggle = true) { - ecs_enable_id(this->m_world, this->m_id, id, toggle); + * + * @see ecs_enable_id() + */ + const Self& enable(flecs::id_t id, bool toggle = true) const { + ecs_enable_id(this->world_, this->id_, id, toggle); return to_base(); } /** Enable a component. - * @see enable(flecs::id_t id) + * @see enable(flecs::id_t) const * * @tparam T The component to enable. - */ + */ template - Self& enable() { - return this->enable(_::cpp_type::id(this->m_world)); + const Self& enable() const { + return this->enable(_::type::id(this->world_)); } /** Enable a pair. - * @see enable(flecs::id_t id) + * @see enable(flecs::id_t) const * * @param first The first element of the pair. * @param second The second element of the pair. - */ - Self& enable(flecs::id_t first, flecs::id_t second) { + */ + const Self& enable(flecs::id_t first, flecs::id_t second) const { return this->enable(ecs_pair(first, second)); } /** Enable a pair. - * @see enable(flecs::id_t id) + * @see enable(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + */ template - Self& enable(flecs::id_t second) { - return this->enable(_::cpp_type::id(), second); + const Self& enable(flecs::id_t second) const { + return this->enable(_::type::id(), second); } /** Enable a pair. - * @see enable(flecs::id_t id) + * @see enable(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + */ template - Self& enable() { - return this->enable(_::cpp_type::id()); + const Self& enable() const { + return this->enable(_::type::id()); } /** Disable an id. @@ -23211,61 +24746,64 @@ struct entity_builder : entity_view { * the id is enabled or disabled, the bitset is added. * * @param id The id to disable. - */ - Self& disable(flecs::id_t id) { + * + * @see ecs_enable_id() + * @see enable(flecs::id_t) const + */ + const Self& disable(flecs::id_t id) const { return this->enable(id, false); } /** Disable a component. - * @see disable(flecs::id_t id) + * @see disable(flecs::id_t) const * * @tparam T The component to enable. - */ + */ template - Self& disable() { - return this->disable(_::cpp_type::id()); + const Self& disable() const { + return this->disable(_::type::id()); } /** Disable a pair. - * @see disable(flecs::id_t id) + * @see disable(flecs::id_t) const * * @param first The first element of the pair. * @param second The second element of the pair. - */ - Self& disable(flecs::id_t first, flecs::id_t second) { + */ + const Self& disable(flecs::id_t first, flecs::id_t second) const { return this->disable(ecs_pair(first, second)); } /** Disable a pair. - * @see disable(flecs::id_t id) + * @see disable(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + */ template - Self& disable(flecs::id_t second) { - return this->disable(_::cpp_type::id(), second); + const Self& disable(flecs::id_t second) const { + return this->disable(_::type::id(), second); } /** Disable a pair. - * @see disable(flecs::id_t id) + * @see disable(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + */ template - Self& disable() { - return this->disable(_::cpp_type::id()); + const Self& disable() const { + return this->disable(_::type::id()); } - Self& set_ptr(entity_t comp, size_t size, const void *ptr) { - ecs_set_id(this->m_world, this->m_id, comp, size, ptr); + const Self& set_ptr(entity_t comp, size_t size, const void *ptr) const { + ecs_set_id(this->world_, this->id_, comp, size, ptr); return to_base(); } - Self& set_ptr(entity_t comp, const void *ptr) { + const Self& set_ptr(entity_t comp, const void *ptr) const { const flecs::Component *cptr = ecs_get( - this->m_world, comp, EcsComponent); + this->world_, comp, EcsComponent); /* Can't set if it's not a component */ ecs_assert(cptr != NULL, ECS_INVALID_PARAMETER, NULL); @@ -23273,31 +24811,29 @@ struct entity_builder : entity_view { return set_ptr(comp, cptr->size, ptr); } - template::value && is_actual::value> = 0 > - Self& set(T&& value) { - flecs::set(this->m_world, this->m_id, FLECS_FWD(value)); + template::value> = 0 > + const Self& set(T&& value) const { + flecs::set(this->world_, this->id_, FLECS_FWD(value)); return to_base(); } - template::value && is_actual::value > = 0> - Self& set(const T& value) { - flecs::set(this->m_world, this->m_id, value); + template::value > = 0> + const Self& set(const T& value) const { + flecs::set(this->world_, this->id_, value); return to_base(); } template, if_not_t< - is_callable::value || is_actual::value > = 0> - Self& set(A&& value) { - flecs::set(this->m_world, this->m_id, FLECS_FWD(value)); + is_actual::value > = 0> + const Self& set(A&& value) const { + flecs::set(this->world_, this->id_, FLECS_FWD(value)); return to_base(); } template, if_not_t< - is_callable::value || is_actual::value > = 0> - Self& set(const A& value) { - flecs::set(this->m_world, this->m_id, value); + is_actual::value > = 0> + const Self& set(const A& value) const { + flecs::set(this->world_, this->id_, value); return to_base(); } @@ -23311,8 +24847,8 @@ struct entity_builder : entity_view { */ template , typename A = actual_type_t

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

(this->m_world, this->m_id, FLECS_FWD(value)); + const Self& set(A&& value) const { + flecs::set

(this->world_, this->id_, FLECS_FWD(value)); return to_base(); } @@ -23326,8 +24862,8 @@ struct entity_builder : entity_view { */ template , typename A = actual_type_t

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

(this->m_world, this->m_id, value); + const Self& set(const A& value) const { + flecs::set

(this->world_, this->id_, value); return to_base(); } @@ -23340,9 +24876,9 @@ struct entity_builder : entity_view { * @param value The value to set. */ template ::value > = 0> - Self& set(Second second, const First& value) { - auto first = _::cpp_type::id(this->m_world); - flecs::set(this->m_world, this->m_id, value, + const Self& set(Second second, const First& value) const { + auto first = _::type::id(this->world_); + flecs::set(this->world_, this->id_, value, ecs_pair(first, second)); return to_base(); } @@ -23356,9 +24892,9 @@ struct entity_builder : entity_view { * @param value The value to set. */ template ::value > = 0> - Self& set(Second second, First&& value) { - auto first = _::cpp_type::id(this->m_world); - flecs::set(this->m_world, this->m_id, FLECS_FWD(value), + const Self& set(Second second, First&& value) const { + auto first = _::type::id(this->world_); + flecs::set(this->world_, this->id_, FLECS_FWD(value), ecs_pair(first, second)); return to_base(); } @@ -23372,8 +24908,8 @@ struct entity_builder : entity_view { * @param value The value to set. */ template ::value > = 0> - Self& set(Second constant, const First& value) { - const auto& et = enum_type(this->m_world); + const Self& set(Second constant, const First& value) const { + const auto& et = enum_type(this->world_); flecs::entity_t second = et.entity(constant); return set(second, value); } @@ -23387,9 +24923,13 @@ struct entity_builder : entity_view { * @param value The value to set. */ template - Self& set_second(entity_t first, const Second& value) { - auto second = _::cpp_type::id(this->m_world); - flecs::set(this->m_world, this->m_id, value, + const Self& set_second(entity_t first, const Second& value) const { + auto second = _::type::id(this->world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + flecs::set(this->world_, this->id_, value, ecs_pair(first, second)); return to_base(); } @@ -23403,16 +24943,20 @@ struct entity_builder : entity_view { * @param value The value to set. */ template - Self& set_second(entity_t first, Second&& value) { - auto second = _::cpp_type::id(this->m_world); - flecs::set(this->m_world, this->m_id, FLECS_FWD(value), + const Self& set_second(entity_t first, Second&& value) const { + auto second = _::type::id(this->world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + flecs::set(this->world_, this->id_, FLECS_FWD(value), ecs_pair(first, second)); return to_base(); } template - Self& set_second(const Second& value) { - flecs::set>(this->m_world, this->m_id, value); + const Self& set_second(const Second& value) const { + flecs::set>(this->world_, this->id_, value); return to_base(); } @@ -23423,7 +24967,7 @@ struct entity_builder : entity_view { * * This operation is faster than individually calling get for each component * as it only obtains entity metadata once. When this operation is called - * while deferred, its performance is equivalent to that of calling get_mut + * while deferred, its performance is equivalent to that of calling ensure * for each component separately. * * The operation will invoke modified for each component after the callback @@ -23431,16 +24975,19 @@ struct entity_builder : entity_view { * * @param func The callback to invoke. */ - template ::value > = 0> - Self& set(const Func& func); + template + const Self& insert(const Func& func) const; /** Emplace component. * Emplace constructs a component in the storage, which prevents calling the * destructor on the value passed into the function. * * Emplace attempts the following signatures to construct the component: - * T{Args...} - * T{flecs::entity, Args...} + * + * @code + * T{Args...} + * T{flecs::entity, Args...} + * @endcode * * If the second signature matches, emplace will pass in the current entity * as argument to the constructor, which is useful if the component needs @@ -23453,34 +25000,40 @@ struct entity_builder : entity_view { * @param args The arguments to pass to the constructor of T */ template> - Self& emplace(Args&&... args) { - flecs::emplace(this->m_world, this->m_id, - _::cpp_type::id(this->m_world), FLECS_FWD(args)...); + const Self& emplace(Args&&... args) const { + flecs::emplace(this->world_, this->id_, + _::type::id(this->world_), FLECS_FWD(args)...); return to_base(); } template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> - Self& emplace(Args&&... args) { - flecs::emplace(this->m_world, this->m_id, - ecs_pair(_::cpp_type::id(this->m_world), - _::cpp_type::id(this->m_world)), + const Self& emplace(Args&&... args) const { + flecs::emplace(this->world_, this->id_, + ecs_pair(_::type::id(this->world_), + _::type::id(this->world_)), FLECS_FWD(args)...); return to_base(); } template - Self& emplace_first(flecs::entity_t second, Args&&... args) { - flecs::emplace(this->m_world, this->m_id, - ecs_pair(_::cpp_type::id(this->m_world), second), + const Self& emplace_first(flecs::entity_t second, Args&&... args) const { + auto first = _::type::id(this->world_); + flecs::emplace(this->world_, this->id_, + ecs_pair(first, second), FLECS_FWD(args)...); return to_base(); } template - Self& emplace_second(flecs::entity_t first, Args&&... args) { - flecs::emplace(this->m_world, this->m_id, - ecs_pair(first, _::cpp_type::id(this->m_world)), + const Self& emplace_second(flecs::entity_t first, Args&&... args) const { + auto second = _::type::id(this->world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + flecs::emplace(this->world_, this->id_, + ecs_pair(first, second), FLECS_FWD(args)...); return to_base(); } @@ -23491,65 +25044,65 @@ struct entity_builder : entity_view { * @param func The function to call. */ template - Self& with(const Func& func) { - ecs_id_t prev = ecs_set_with(this->m_world, this->m_id); + const Self& with(const Func& func) const { + ecs_id_t prev = ecs_set_with(this->world_, this->id_); func(); - ecs_set_with(this->m_world, prev); + ecs_set_with(this->world_, prev); return to_base(); } - /** Entities created in function will have (First, this). + /** Entities created in function will have `(First, this)`. * This operation is thread safe. * * @tparam First The first element of the pair * @param func The function to call. */ template - Self& with(const Func& func) { - with(_::cpp_type::id(this->m_world), func); + const Self& with(const Func& func) const { + with(_::type::id(this->world_), func); return to_base(); } - /** Entities created in function will have (first, this). + /** Entities created in function will have `(first, this)`. * This operation is thread safe. * * @param first The first element of the pair. * @param func The function to call. */ template - Self& with(entity_t first, const Func& func) { - ecs_id_t prev = ecs_set_with(this->m_world, - ecs_pair(first, this->m_id)); + const Self& with(entity_t first, const Func& func) const { + ecs_id_t prev = ecs_set_with(this->world_, + ecs_pair(first, this->id_)); func(); - ecs_set_with(this->m_world, prev); + ecs_set_with(this->world_, prev); return to_base(); } /** The function will be ran with the scope set to the current entity. */ template - Self& scope(const Func& func) { - ecs_entity_t prev = ecs_set_scope(this->m_world, this->m_id); + const Self& scope(const Func& func) const { + ecs_entity_t prev = ecs_set_scope(this->world_, this->id_); func(); - ecs_set_scope(this->m_world, prev); + ecs_set_scope(this->world_, prev); return to_base(); } /** Return world scoped to entity */ scoped_world scope() const { - return scoped_world(m_world, m_id); + return scoped_world(world_, id_); } /* Set the entity name. */ - Self& set_name(const char *name) { - ecs_set_name(this->m_world, this->m_id, name); + const Self& set_name(const char *name) const { + ecs_set_name(this->world_, this->id_, name); return to_base(); } /* Set entity alias. */ - Self& set_alias(const char *name) { - ecs_set_alias(this->m_world, this->m_id, name); + const Self& set_alias(const char *name) const { + ecs_set_alias(this->world_, this->id_, name); return to_base(); } @@ -23559,58 +25112,93 @@ struct entity_builder : entity_view { * @brief Doc entity builder mixin. */ -/** Set doc name. - * This adds (flecs.doc.Description, flecs.Name) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc +/** Set human readable name. + * This adds `(flecs.doc.Description, flecs.Name)` to the entity. + * + * @see ecs_doc_set_name() + * @see flecs::doc::set_name() + * @see flecs::entity_view::doc_name() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_name(const char *name) { - ecs_doc_set_name(m_world, m_id, name); +const Self& set_doc_name(const char *name) const { + ecs_doc_set_name(world_, id_, name); return to_base(); } -/** Set doc brief. - * This adds (flecs.doc.Description, flecs.doc.Brief) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc +/** Set brief description. + * This adds `(flecs.doc.Description, flecs.doc.Brief)` to the entity. + * + * @see ecs_doc_set_brief() + * @see flecs::doc::set_brief() + * @see flecs::entity_view::doc_brief() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_brief(const char *brief) { - ecs_doc_set_brief(m_world, m_id, brief); +const Self& set_doc_brief(const char *brief) const { + ecs_doc_set_brief(world_, id_, brief); return to_base(); } -/** Set doc detailed description. - * This adds (flecs.doc.Description, flecs.doc.Detail) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc +/** Set detailed description. + * This adds `(flecs.doc.Description, flecs.doc.Detail)` to the entity. + * + * @see ecs_doc_set_detail() + * @see flecs::doc::set_detail() + * @see flecs::entity_view::doc_detail() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_detail(const char *detail) { - ecs_doc_set_detail(m_world, m_id, detail); +const Self& set_doc_detail(const char *detail) const { + ecs_doc_set_detail(world_, id_, detail); return to_base(); } -/** Set doc link. - * This adds (flecs.doc.Description, flecs.doc.Link) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc +/** Set link to external documentation. + * This adds `(flecs.doc.Description, flecs.doc.Link)` to the entity. + * + * @see ecs_doc_set_link() + * @see flecs::doc::set_link() + * @see flecs::entity_view::doc_link() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_link(const char *link) { - ecs_doc_set_link(m_world, m_id, link); +const Self& set_doc_link(const char *link) const { + ecs_doc_set_link(world_, id_, link); return to_base(); } /** Set doc color. - * This adds (flecs.doc.Description, flecs.doc.Color) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc + * This adds `(flecs.doc.Description, flecs.doc.Color)` to the entity. + * + * @see ecs_doc_set_color() + * @see flecs::doc::set_color() + * @see flecs::entity_view::doc_color() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc + */ +const Self& set_doc_color(const char *color) const { + ecs_doc_set_color(world_, id_, color); + return to_base(); +} + +/** Set doc UUID. + * This adds `(flecs.doc.Description, flecs.doc.Uuid)` to the entity. + * + * @see ecs_doc_set_uuid() + * @see flecs::doc::set_uuid() + * @see flecs::entity_view::doc_uuid() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_color(const char *link) { - ecs_doc_set_color(m_world, m_id, link); +const Self& set_doc_uuid(const char *uuid) const { + ecs_doc_set_uuid(world_, id_, uuid); return to_base(); } @@ -23623,23 +25211,23 @@ Self& set_doc_color(const char *link) { */ /** - * \memberof flecs::entity_view - * \ingroup cpp_addons_meta + * @memberof flecs::entity_view + * @ingroup cpp_addons_meta * * @{ */ /** Make entity a unit */ -Self& unit( +const Self& unit( const char *symbol, flecs::entity_t prefix = 0, flecs::entity_t base = 0, flecs::entity_t over = 0, int32_t factor = 0, - int32_t power = 0) + int32_t power = 0) const { ecs_unit_desc_t desc = {}; - desc.entity = this->m_id; + desc.entity = this->id_; desc.symbol = const_cast(symbol); /* safe, will be copied in */ desc.base = base; desc.over = over; @@ -23652,15 +25240,15 @@ Self& unit( } /** Make entity a derived unit */ -Self& unit( +const Self& unit( flecs::entity_t prefix = 0, flecs::entity_t base = 0, flecs::entity_t over = 0, int32_t factor = 0, - int32_t power = 0) + int32_t power = 0) const { ecs_unit_desc_t desc = {}; - desc.entity = this->m_id; + desc.entity = this->id_; desc.base = base; desc.over = over; desc.prefix = prefix; @@ -23672,13 +25260,13 @@ Self& unit( } /** Make entity a derived unit */ -Self& unit_prefix( +const Self& unit_prefix( const char *symbol, int32_t factor = 0, - int32_t power = 0) + int32_t power = 0) const { ecs_unit_prefix_desc_t desc = {}; - desc.entity = this->m_id; + desc.entity = this->id_; desc.symbol = const_cast(symbol); /* safe, will be copied in */ desc.translation.factor = factor; desc.translation.power = power; @@ -23688,19 +25276,19 @@ Self& unit_prefix( } /** Add quantity to unit */ -Self& quantity(flecs::entity_t quantity) { +const Self& quantity(flecs::entity_t quantity) const { ecs_add_pair(this->world(), this->id(), flecs::Quantity, quantity); return to_base(); } /** Make entity a unity prefix */ template -Self& quantity() { - return this->quantity(_::cpp_type::id(this->world())); +const Self& quantity() const { + return this->quantity(_::type::id(this->world())); } /** Make entity a quantity */ -Self& quantity() { +const Self& quantity() const { ecs_add_id(this->world(), this->id(), flecs::Quantity); return to_base(); } @@ -23717,148 +25305,148 @@ Self& quantity() { /** Set component from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ -Self& set_json( +const Self& set_json( flecs::id_t e, const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { - flecs::entity_t type = ecs_get_typeid(m_world, e); + flecs::entity_t type = ecs_get_typeid(world_, e); if (!type) { ecs_err("id is not a type"); return to_base(); } - void *ptr = ecs_get_mut_id(m_world, m_id, e); + void *ptr = ecs_ensure_id(world_, id_, e); ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_ptr_from_json(m_world, type, ptr, json, desc); - ecs_modified_id(m_world, m_id, e); + ecs_ptr_from_json(world_, type, ptr, json, desc); + ecs_modified_id(world_, id_, e); return to_base(); } /** Set pair from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ -Self& set_json( +const Self& set_json( flecs::entity_t r, flecs::entity_t t, const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { return set_json(ecs_pair(r, t), json, desc); } /** Set component from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ template -Self& set_json( +const Self& set_json( const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { - return set_json(_::cpp_type::id(m_world), json, desc); + return set_json(_::type::id(world_), json, desc); } /** Set pair from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ template -Self& set_json( +const Self& set_json( const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { return set_json( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world), + _::type::id(world_), + _::type::id(world_), json, desc); } /** Set pair from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ template -Self& set_json( +const Self& set_json( flecs::entity_t t, const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { return set_json( - _::cpp_type::id(m_world), t, + _::type::id(world_), t, json, desc); } /** Set pair from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ template -Self& set_json_second( +const Self& set_json_second( flecs::entity_t r, const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { return set_json( - r, _::cpp_type::id(m_world), + r, _::type::id(world_), json, desc); } # endif /** - * @file addons/cpp/mixins/event/entity_builder.hpp + * @file addons/cpp/mixins/event/entity_builder.inl * @brief Event entity mixin. */ /** Observe event on entity * - * \memberof flecs::entity_builder + * @memberof flecs::entity_builder * * @param evt The event id. * @param callback The observer callback. * @return Event builder. */ template -Self& observe(flecs::entity_t evt, Func&& callback); +const Self& observe(flecs::entity_t evt, Func&& callback) const; /** Observe event on entity * - * \memberof flecs::entity_builder + * @memberof flecs::entity_builder * * @tparam Evt The event type. * @param callback The observer callback. * @return Event builder. */ template -Self& observe(Func&& callback); +const Self& observe(Func&& callback) const; /** Observe event on entity * - * \memberof flecs::entity_builder + * @memberof flecs::entity_builder * * @param callback The observer callback. * @return Event builder. */ template -Self& observe(Func&& callback); +const Self& observe(Func&& callback) const; protected: - Self& to_base() { - return *static_cast(this); + const Self& to_base() const { + return *static_cast(this); } }; @@ -23867,9 +25455,9 @@ Self& observe(Func&& callback); /** * @defgroup cpp_entities Entities - * @brief Entity operations. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Entity operations. + * * @{ */ @@ -23878,8 +25466,8 @@ namespace flecs /** Entity. * Class with read/write operations for entities. - * - * \ingroup cpp_entities + * + * @ingroup cpp_entities */ struct entity : entity_builder { @@ -23889,11 +25477,16 @@ struct entity : entity_builder * * @param world The world in which to create the entity. */ - explicit entity(world_t *world) - : entity_builder() + explicit entity(world_t *world) + : entity_builder() { - m_world = world; - m_id = ecs_new(world, 0); + world_ = world; + if (!ecs_get_scope(world_) && !ecs_get_with(world_)) { + id_ = ecs_new(world); + } else { + ecs_entity_desc_t desc = {}; + id_ = ecs_entity_init(world_, &desc); + } } /** Wrap an existing entity id. @@ -23901,9 +25494,9 @@ struct entity : entity_builder * @param world The world in which the entity is created. * @param id The entity id. */ - explicit entity(const flecs::world_t *world, flecs::id_t id) { - m_world = const_cast(world); - m_id = id; + explicit entity(const flecs::world_t *world, flecs::entity_t id) { + world_ = const_cast(world); + id_ = id; } /** Create a named entity. @@ -23915,25 +25508,27 @@ struct entity : entity_builder * @param world The world in which to create the entity. * @param name The entity name. */ - explicit entity(world_t *world, const char *name) + explicit entity(world_t *world, const char *name) : entity_builder() - { - m_world = world; + { + world_ = world; ecs_entity_desc_t desc = {}; desc.name = name; desc.sep = "::"; desc.root_sep = "::"; - m_id = ecs_entity_init(world, &desc); + id_ = ecs_entity_init(world, &desc); } - /** Conversion from flecs::entity_t to flecs::entity. - * + /** Conversion from flecs::entity_t to flecs::entity. + * * @param id The entity_t value to convert. */ - explicit entity(entity_t id) + explicit entity(entity_t id) : entity_builder( nullptr, id ) { } + #ifndef ensure + /** Get mutable component value. * This operation returns a mutable pointer to the component. If the entity * did not yet have the component, it will be added. If a base entity had @@ -23944,10 +25539,11 @@ struct entity : entity_builder * @return Pointer to the component value. */ template - T* get_mut() const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast(ecs_get_mut_id(m_world, m_id, comp_id)); + T& ensure() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return *static_cast(ecs_ensure_id(world_, id_, comp_id)); } /** Get mutable component value (untyped). @@ -23959,8 +25555,8 @@ struct entity : entity_builder * @param comp The component to get. * @return Pointer to the component value. */ - void* get_mut(entity_t comp) const { - return ecs_get_mut_id(m_world, m_id, comp); + void* ensure(entity_t comp) const { + return ecs_ensure_id(world_, id_, comp); } /** Get mutable pointer for a pair. @@ -23969,12 +25565,12 @@ struct entity : entity_builder * @tparam First The first part of the pair. * @tparam Second the second part of the pair. */ - template , + template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> - A* get_mut() const { - return static_cast(ecs_get_mut_id(m_world, m_id, ecs_pair( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world)))); + A& ensure() const { + return *static_cast(ecs_ensure_id(world_, id_, ecs_pair( + _::type::id(world_), + _::type::id(world_)))); } /** Get mutable pointer for the first element of a pair. @@ -23984,23 +25580,24 @@ struct entity : entity_builder * @param second The second element of the pair. */ template - First* get_mut(entity_t second) const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast( - ecs_get_mut_id(m_world, m_id, ecs_pair(comp_id, second))); + First& ensure(entity_t second) const { + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return *static_cast( + ecs_ensure_id(world_, id_, ecs_pair(first, second))); } /** Get mutable pointer for a pair (untyped). * This operation gets the value for a pair from the entity. If neither the - * first nor second element of the pair is a component, the operation will + * first nor second element of the pair is a component, the operation will * fail. * * @param first The first element of the pair. * @param second The second element of the pair. */ - void* get_mut(entity_t first, entity_t second) const { - return ecs_get_mut_id(m_world, m_id, ecs_pair(first, second)); + void* ensure(entity_t first, entity_t second) const { + return ecs_ensure_id(world_, id_, ecs_pair(first, second)); } /** Get mutable pointer for the second element of a pair. @@ -24010,12 +25607,19 @@ struct entity : entity_builder * @param first The first element of the pair. */ template - Second* get_mut_second(entity_t first) const { - auto second = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast( - ecs_get_mut_id(m_world, m_id, ecs_pair(first, second))); - } + Second& ensure_second(entity_t first) const { + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return *static_cast( + ecs_ensure_id(world_, id_, ecs_pair(first, second))); + } + + #endif /** Signal that component was modified. * @@ -24023,19 +25627,24 @@ struct entity : entity_builder */ template void modified() const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); this->modified(comp_id); - } + } /** Signal that the first element of a pair was modified. * * @tparam First The first part of the pair. * @tparam Second the second part of the pair. */ - template + template >> void modified() const { - this->modified(_::cpp_type::id(m_world)); + auto first = _::type::id(world_); + auto second = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + this->modified(first, second); } /** Signal that the first part of a pair was modified. @@ -24045,8 +25654,9 @@ struct entity : entity_builder */ template void modified(entity_t second) const { - auto first = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); this->modified(first, second); } @@ -24066,7 +25676,7 @@ struct entity : entity_builder * @param comp component that was modified. */ void modified(entity_t comp) const { - ecs_modified_id(m_world, m_id, comp); + ecs_modified_id(world_, id_, comp); } /** Get reference to component. @@ -24076,65 +25686,82 @@ struct entity : entity_builder * @tparam T component for which to get a reference. * @return The reference. */ - template + template ::value > = 0> ref get_ref() const { - return ref(m_world, m_id, _::cpp_type::id(m_world)); + return ref(world_, id_, _::type::id(world_)); + } + + /** Get reference to component. + * Overload for when T is not the same as the actual type, which happens + * when using pair types. + * A reference allows for quick and safe access to a component value, and is + * a faster alternative to repeatedly calling 'get' for the same component. + * + * @tparam T component for which to get a reference. + * @return The reference. + */ + template , if_t< flecs::is_pair::value > = 0> + ref get_ref() const { + return ref(world_, id_, + ecs_pair(_::type::id(world_), + _::type::id(world_))); } - template , + + template , typename A = actual_type_t

> ref get_ref() const { - return ref(m_world, m_id, - ecs_pair(_::cpp_type::id(m_world), - _::cpp_type::id(m_world))); + return ref(world_, id_, + ecs_pair(_::type::id(world_), _::type::id(world_))); } template ref get_ref(flecs::entity_t second) const { - return ref(m_world, m_id, - ecs_pair(_::cpp_type::id(m_world), second)); + auto first = _::type::id(world_); + return ref(world_, id_, ecs_pair(first, second)); } template ref get_ref_second(flecs::entity_t first) const { - return ref(m_world, m_id, - ecs_pair(first, _::cpp_type::id(m_world))); - } - - /** Recursively flatten relationship. - * @see ecs_flatten - */ - void flatten(flecs::entity_t r, const ecs_flatten_desc_t *desc = nullptr) { - ecs_flatten(m_world, ecs_pair(r, m_id), desc); + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + return ref(world_, id_, ecs_pair(first, second)); } /** Clear an entity. * This operation removes all components from an entity without recycling * the entity id. + * + * @see ecs_clear() */ void clear() const { - ecs_clear(m_world, m_id); + ecs_clear(world_, id_); } /** Delete an entity. * Entities have to be deleted explicitly, and are not deleted when the * entity object goes out of scope. + * + * @see ecs_delete() */ void destruct() const { - ecs_delete(m_world, m_id); + ecs_delete(world_, id_); } /** Return entity as entity_view. * This returns an entity_view instance for the entity which is a readonly * version of the entity class. - * + * * This is similar to a regular upcast, except that this method ensures that * the entity_view instance is instantiated with a world vs. a stage, which * a regular upcast does not guarantee. */ flecs::entity_view view() const { return flecs::entity_view( - const_cast(ecs_get_world(m_world)), m_id); + const_cast(ecs_get_world(world_)), id_); } /** Entity id 0. @@ -24146,7 +25773,7 @@ struct entity : entity_builder static flecs::entity null(const flecs::world_t *world) { flecs::entity result; - result.m_world = const_cast(world); + result.world_ = const_cast(world); return result; } @@ -24159,11 +25786,11 @@ struct entity : entity_builder /** Deserialize entity to JSON. * - * \memberof flecs::entity - * \ingroup cpp_addons_json + * @memberof flecs::entity + * @ingroup cpp_addons_json */ const char* from_json(const char *json) { - return ecs_entity_from_json(m_world, m_id, json, nullptr); + return ecs_entity_from_json(world_, id_, json, nullptr); } # endif @@ -24180,6 +25807,8 @@ const char* from_json(const char *json) { #pragma once +#include // std::declval + namespace flecs { @@ -24209,66 +25838,111 @@ struct component_binding_ctx { }; // Utility to convert template argument pack to array of term ptrs -struct term_ptr { - void *ptr; - bool is_ref; +struct field_ptr { + void *ptr = nullptr; + int8_t index = 0; + bool is_ref = false; + bool is_row = false; }; template -struct term_ptrs { - using array = flecs::array<_::term_ptr, sizeof...(Components)>; +struct field_ptrs { + using array = flecs::array<_::field_ptr, sizeof...(Components)>; - bool populate(const ecs_iter_t *iter) { - return populate(iter, 0, static_cast< + void populate(const ecs_iter_t *iter) { + populate(iter, 0, static_cast< remove_reference_t< remove_pointer_t> *>(nullptr)...); } - array m_terms; + void populate_self(const ecs_iter_t *iter) { + populate_self(iter, 0, static_cast< + remove_reference_t< + remove_pointer_t> + *>(nullptr)...); + } + + array fields_; private: - /* Populate terms array without checking for references */ - bool populate(const ecs_iter_t*, size_t) { return false; } - - template - bool populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) { - m_terms[index].ptr = iter->ptrs[index]; - bool is_ref = iter->sources && iter->sources[index] != 0; - m_terms[index].is_ref = is_ref; - is_ref |= populate(iter, index + 1, comps ...); - return is_ref; - } -}; + void populate(const ecs_iter_t*, size_t) { } + + template >, + if_not_t< is_empty::value > = 0> + void populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + if (iter->row_fields & (1llu << index)) { + /* Need to fetch the value with ecs_field_at() */ + fields_[index].is_row = true; + fields_[index].is_ref = true; + fields_[index].index = static_cast(index); + } else { + fields_[index].ptr = ecs_field_w_size(iter, sizeof(A), + static_cast(index)); + fields_[index].is_ref = iter->sources[index] != 0; + } + + populate(iter, index + 1, comps ...); + } + + template >, + if_t< is_empty::value > = 0> + void populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + populate(iter, index + 1, comps ...); + } + + void populate_self(const ecs_iter_t*, size_t) { } + + template >, + if_not_t< is_empty::value > = 0> + void populate_self(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + fields_[index].ptr = ecs_field_w_size(iter, sizeof(A), + static_cast(index)); + fields_[index].is_ref = false; + ecs_assert(iter->sources[index] == 0, ECS_INTERNAL_ERROR, NULL); + populate_self(iter, index + 1, comps ...); + } + + template >, + if_t< is_empty::value > = 0> + void populate_self(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + populate(iter, index + 1, comps ...); + } +}; struct delegate { }; // Template that figures out from the template parameters of a query/system // how to pass the value to the each callback template -struct each_column { }; +struct each_field { }; // Base class struct each_column_base { - each_column_base(const _::term_ptr& term, size_t row) - : m_term(term), m_row(row) { } + each_column_base(const _::field_ptr& field, size_t row) + : field_(field), row_(row) { + } protected: - const _::term_ptr& m_term; - size_t m_row; + const _::field_ptr& field_; + size_t row_; }; // If type is not a pointer, return a reference to the type (default case) template -struct each_column::value && +struct each_field::value && !is_empty>::value && is_actual::value > > : each_column_base { - each_column(const _::term_ptr& term, size_t row) - : each_column_base(term, row) { } + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } T& get_row() { - return static_cast(this->m_term.ptr)[this->m_row]; + return static_cast(this->field_.ptr)[this->row_]; } }; @@ -24276,49 +25950,48 @@ struct each_column::value && // This requires that the actual type can be converted to the type. // A typical scenario where this happens is when using flecs::pair types. template -struct each_column::value && +struct each_field::value && !is_empty>::value && !is_actual::value> > : each_column_base { - each_column(const _::term_ptr& term, size_t row) - : each_column_base(term, row) { } + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } T get_row() { - return static_cast*>(this->m_term.ptr)[this->m_row]; + return static_cast*>(this->field_.ptr)[this->row_]; } }; - // If type is empty (indicating a tag) the query will pass a nullptr. To avoid // returning nullptr to reference arguments, return a temporary value. template -struct each_column>::value && +struct each_field>::value && !is_pointer::value > > : each_column_base { - each_column(const _::term_ptr& term, size_t row) - : each_column_base(term, row) { } + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } T get_row() { return actual_type_t(); } }; - -// If type is a pointer (indicating an optional value) return the type as is +// If type is a pointer (indicating an optional value) don't index with row if +// the field is not set. template -struct each_column::value && +struct each_field::value && !is_empty>::value > > : each_column_base { - each_column(const _::term_ptr& term, size_t row) - : each_column_base(term, row) { } + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } actual_type_t get_row() { - if (this->m_term.ptr) { - return &static_cast>(this->m_term.ptr)[this->m_row]; + if (this->field_.ptr) { + return &static_cast>(this->field_.ptr)[this->row_]; } else { - // optional argument doesn't hava a value + // optional argument doesn't have a value return nullptr; } } @@ -24327,11 +26000,11 @@ struct each_column::value && // If the query contains component references to other entities, check if the // current argument is one. template -struct each_ref_column : public each_column { - each_ref_column(const _::term_ptr& term, size_t row) - : each_column(term, row) { +struct each_ref_field : public each_field { + each_ref_field(const flecs::iter_t *iter, _::field_ptr& field, size_t row) + : each_field(iter, field, row) { - if (term.is_ref) { + if (field.is_ref) { // If this is a reference, set the row to 0 as a ref always is a // single value, not an array. This prevents the application from // having to do an if-check on whether the column is owned. @@ -24339,51 +26012,48 @@ struct each_ref_column : public each_column { // This check only happens when the current table being iterated // over caused the query to match a reference. The check is // performed once per iterated table. - this->m_row = 0; + this->row_ = 0; + } + + if (field.is_row) { + field.ptr = ecs_field_at_w_size(iter, sizeof(T), field.index, + static_cast(row)); } } }; +// Type that handles passing components to each callbacks template struct each_delegate : public delegate { - // If the number of arguments in the function signature is one more than the - // number of components in the query, an extra entity arg is required. - static constexpr bool PassEntity = - (sizeof...(Components) + 1) == (arity::value); - - // If the number of arguments in the function is two more than the number of - // components in the query, extra iter + index arguments are required. - static constexpr bool PassIter = - (sizeof...(Components) + 2) == (arity::value); - - static_assert(arity::value > 0, - "each() must have at least one argument"); - - using Terms = typename term_ptrs::array; + using Terms = typename field_ptrs::array; template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> explicit each_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + : func_(FLECS_MOV(func)) { } explicit each_delegate(const Func& func) noexcept - : m_func(func) { } + : func_(func) { } // Invoke object directly. This operation is useful when the calling // function has just constructed the delegate, such as what happens when // iterating a query. void invoke(ecs_iter_t *iter) const { - term_ptrs terms; + field_ptrs terms; - if (terms.populate(iter)) { - invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms); + iter->flags |= EcsIterCppEach; + + if (iter->ref_fields | iter->up_fields) { + terms.populate(iter); + invoke_unpack< each_ref_field >(iter, func_, 0, terms.fields_); } else { - invoke_callback< each_column >(iter, m_func, 0, terms.m_terms); - } + terms.populate_self(iter); + invoke_unpack< each_field >(iter, func_, 0, terms.fields_); + } } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); self->invoke(iter); } @@ -24394,113 +26064,99 @@ struct each_delegate : public delegate { } // Function that can be used as callback to free delegate - static void free(void *obj) { + static void destruct(void *obj) { _::free_obj(static_cast(obj)); } // Static function to call for component on_add hook static void run_add(ecs_iter_t *iter) { component_binding_ctx *ctx = reinterpret_cast( - iter->binding_ctx); - iter->binding_ctx = ctx->on_add; + iter->callback_ctx); + iter->callback_ctx = ctx->on_add; run(iter); } // Static function to call for component on_remove hook static void run_remove(ecs_iter_t *iter) { component_binding_ctx *ctx = reinterpret_cast( - iter->binding_ctx); - iter->binding_ctx = ctx->on_remove; + iter->callback_ctx); + iter->callback_ctx = ctx->on_remove; run(iter); } // Static function to call for component on_set hook static void run_set(ecs_iter_t *iter) { component_binding_ctx *ctx = reinterpret_cast( - iter->binding_ctx); - iter->binding_ctx = ctx->on_set; + iter->callback_ctx); + iter->callback_ctx = ctx->on_set; run(iter); } - // Each delegates always use instanced iterators - static bool instanced() { - return true; - } - private: - // Number of function arguments is one more than number of components, pass - // entity as argument. + // func(flecs::entity, Components...) template class ColumnType, - typename... Args, if_t< - sizeof...(Components) == sizeof...(Args) && PassEntity> = 0> + typename... Args, + typename Fn = Func, + decltype(std::declval()( + std::declval(), + std::declval > >().get_row()...), 0) = 0> static void invoke_callback( - ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) + ecs_iter_t *iter, const Func& func, size_t i, Args... comps) { - ECS_TABLE_LOCK(iter->world, iter->table); - - ecs_world_t *world = iter->world; - size_t count = static_cast(iter->count); - - ecs_assert(count > 0, ECS_INVALID_OPERATION, + ecs_assert(iter->count > 0, ECS_INVALID_OPERATION, "no entities returned, use each() without flecs::entity argument"); - for (size_t i = 0; i < count; i ++) { - func(flecs::entity(world, iter->entities[i]), - (ColumnType< remove_reference_t >(comps, i) - .get_row())...); - } - - ECS_TABLE_UNLOCK(iter->world, iter->table); + func(flecs::entity(iter->world, iter->entities[i]), + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...); } - // Number of function arguments is two more than number of components, pass - // iter + index as argument. + // func(flecs::iter&, size_t row, Components...) template class ColumnType, - typename... Args, int Enabled = PassIter, if_t< - sizeof...(Components) == sizeof...(Args) && Enabled> = 0> + typename... Args, + typename Fn = Func, + decltype(std::declval()( + std::declval(), + std::declval(), + std::declval > >().get_row()...), 0) = 0> static void invoke_callback( - ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) + ecs_iter_t *iter, const Func& func, size_t i, Args... comps) { - size_t count = static_cast(iter->count); - if (count == 0) { - // If query has no This terms, count can be 0. Since each does not - // have an entity parameter, just pass through components - count = 1; - } - flecs::iter it(iter); + func(it, i, (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...); + } - ECS_TABLE_LOCK(iter->world, iter->table); - - for (size_t i = 0; i < count; i ++) { - func(it, i, (ColumnType< remove_reference_t >(comps, i) - .get_row())...); - } - - ECS_TABLE_UNLOCK(iter->world, iter->table); + // func(Components...) + template class ColumnType, + typename... Args, + typename Fn = Func, + decltype(std::declval()( + std::declval > >().get_row()...), 0) = 0> + static void invoke_callback( + ecs_iter_t *iter, const Func& func, size_t i, Args... comps) + { + func((ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...); } - // Number of function arguments is equal to number of components, no entity template class ColumnType, typename... Args, if_t< - sizeof...(Components) == sizeof...(Args) && !PassEntity && !PassIter> = 0> - static void invoke_callback( + sizeof...(Components) == sizeof...(Args)> = 0> + static void invoke_unpack( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { + ECS_TABLE_LOCK(iter->world, iter->table); + size_t count = static_cast(iter->count); - if (count == 0) { + if (count == 0 && !iter->table) { // If query has no This terms, count can be 0. Since each does not // have an entity parameter, just pass through components count = 1; } - flecs::iter it(iter); - - ECS_TABLE_LOCK(iter->world, iter->table); - for (size_t i = 0; i < count; i ++) { - func( (ColumnType< remove_reference_t >(comps, i) - .get_row())...); + invoke_callback(iter, func, i, comps...); } ECS_TABLE_UNLOCK(iter->world, iter->table); @@ -24508,64 +26164,55 @@ struct each_delegate : public delegate { template class ColumnType, typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0> - static void invoke_callback(ecs_iter_t *iter, const Func& func, + static void invoke_unpack(ecs_iter_t *iter, const Func& func, size_t index, Terms& columns, Args... comps) { - invoke_callback( + invoke_unpack( iter, func, index + 1, columns, comps..., columns[index]); } - Func m_func; +public: + Func func_; }; template struct find_delegate : public delegate { - // If the number of arguments in the function signature is one more than the - // number of components in the query, an extra entity arg is required. - static constexpr bool PassEntity = - (sizeof...(Components) + 1) == (arity::value); - - // If the number of arguments in the function is two more than the number of - // components in the query, extra iter + index arguments are required. - static constexpr bool PassIter = - (sizeof...(Components) + 2) == (arity::value); - - static_assert(arity::value > 0, - "each() must have at least one argument"); - - using Terms = typename term_ptrs::array; + using Terms = typename field_ptrs::array; template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> explicit find_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + : func_(FLECS_MOV(func)) { } explicit find_delegate(const Func& func) noexcept - : m_func(func) { } + : func_(func) { } // Invoke object directly. This operation is useful when the calling // function has just constructed the delegate, such as what happens when // iterating a query. flecs::entity invoke(ecs_iter_t *iter) const { - term_ptrs terms; + field_ptrs terms; - if (terms.populate(iter)) { - return invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms); - } else { - return invoke_callback< each_column >(iter, m_func, 0, terms.m_terms); - } - } + iter->flags |= EcsIterCppEach; - // Find delegates always use instanced iterators - static bool instanced() { - return true; + if (iter->ref_fields | iter->up_fields) { + terms.populate(iter); + return invoke_callback< each_ref_field >(iter, func_, 0, terms.fields_); + } else { + terms.populate_self(iter); + return invoke_callback< each_field >(iter, func_, 0, terms.fields_); + } } private: // Number of function arguments is one more than number of components, pass // entity as argument. - template class ColumnType, - typename... Args, if_t< - sizeof...(Components) == sizeof...(Args) && PassEntity> = 0> + template class ColumnType, + typename... Args, + typename Fn = Func, + if_t = 0, + decltype(bool(std::declval()( + std::declval(), + std::declval > >().get_row()...))) = true> static flecs::entity invoke_callback( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { @@ -24580,7 +26227,7 @@ struct find_delegate : public delegate { for (size_t i = 0; i < count; i ++) { if (func(flecs::entity(world, iter->entities[i]), - (ColumnType< remove_reference_t >(comps, i) + (ColumnType< remove_reference_t >(iter, comps, i) .get_row())...)) { result = flecs::entity(world, iter->entities[i]); @@ -24595,9 +26242,14 @@ struct find_delegate : public delegate { // Number of function arguments is two more than number of components, pass // iter + index as argument. - template class ColumnType, - typename... Args, int Enabled = PassIter, if_t< - sizeof...(Components) == sizeof...(Args) && Enabled> = 0> + template class ColumnType, + typename... Args, + typename Fn = Func, + if_t = 0, + decltype(bool(std::declval()( + std::declval(), + std::declval(), + std::declval > >().get_row()...))) = true> static flecs::entity invoke_callback( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { @@ -24614,8 +26266,9 @@ struct find_delegate : public delegate { ECS_TABLE_LOCK(iter->world, iter->table); for (size_t i = 0; i < count; i ++) { - if (func(it, i, (ColumnType< remove_reference_t >(comps, i) - .get_row())...)) + if (func(it, i, + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...)) { result = flecs::entity(iter->world, iter->entities[i]); break; @@ -24628,9 +26281,12 @@ struct find_delegate : public delegate { } // Number of function arguments is equal to number of components, no entity - template class ColumnType, - typename... Args, if_t< - sizeof...(Components) == sizeof...(Args) && !PassEntity && !PassIter> = 0> + template class ColumnType, + typename... Args, + typename Fn = Func, + if_t = 0, + decltype(bool(std::declval()( + std::declval > >().get_row()...))) = true> static flecs::entity invoke_callback( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { @@ -24647,8 +26303,9 @@ struct find_delegate : public delegate { ECS_TABLE_LOCK(iter->world, iter->table); for (size_t i = 0; i < count; i ++) { - if (func( (ColumnType< remove_reference_t >(comps, i) - .get_row())...)) + if (func( + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...)) { result = flecs::entity(iter->world, iter->entities[i]); break; @@ -24669,91 +26326,39 @@ struct find_delegate : public delegate { iter, func, index + 1, columns, comps..., columns[index]); } - Func m_func; + Func func_; }; //////////////////////////////////////////////////////////////////////////////// //// Utility class to invoke a system iterate action //////////////////////////////////////////////////////////////////////////////// -template -struct iter_delegate : delegate { -private: - static constexpr bool IterOnly = arity::value == 1; - - using Terms = typename term_ptrs::array; - -public: +template +struct run_delegate : delegate { template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> - explicit iter_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + explicit run_delegate(Func&& func) noexcept + : func_(FLECS_MOV(func)) { } - explicit iter_delegate(const Func& func) noexcept - : m_func(func) { } + explicit run_delegate(const Func& func) noexcept + : func_(func) { } // Invoke object directly. This operation is useful when the calling // function has just constructed the delegate, such as what happens when // iterating a query. void invoke(ecs_iter_t *iter) const { - term_ptrs terms; - terms.populate(iter); - invoke_callback(iter, m_func, 0, terms.m_terms); + flecs::iter it(iter); + iter->flags &= ~EcsIterIsValid; + func_(it); } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->run_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); self->invoke(iter); } - // Instancing needs to be enabled explicitly for iter delegates - static bool instanced() { - return false; - } - -private: - template = 0> - static void invoke_callback(ecs_iter_t *iter, const Func& func, - size_t, Terms&, Args...) - { - flecs::iter it(iter); - - ECS_TABLE_LOCK(iter->world, iter->table); - - func(it); - - ECS_TABLE_UNLOCK(iter->world, iter->table); - } - - template = 0> - static void invoke_callback(ecs_iter_t *iter, const Func& func, size_t, - Terms&, Targs... comps) - { - flecs::iter it(iter); - - ECS_TABLE_LOCK(iter->world, iter->table); - - func(it, ( static_cast< - remove_reference_t< - remove_pointer_t< - actual_type_t > >* > - (comps.ptr))...); - - ECS_TABLE_UNLOCK(iter->world, iter->table); - } - - template = 0> - static void invoke_callback(ecs_iter_t *iter, const Func& func, - size_t index, Terms& columns, Targs... comps) - { - invoke_callback(iter, func, index + 1, columns, comps..., - columns[index]); - } - - Func m_func; + Func func_; }; @@ -24764,34 +26369,37 @@ struct iter_delegate : delegate { template struct entity_observer_delegate : delegate { explicit entity_observer_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + : func_(FLECS_MOV(func)) { } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { invoke(iter); } + private: - template ::value == 1> = 0> + template ()(std::declval()), 0) = 0> static void invoke(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); - self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1))); + self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0))); } - template ::value == 0> = 0> + template ()(), 0) = 0> static void invoke(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); - self->m_func(); + self->func_(); } - Func m_func; + Func func_; }; template struct entity_payload_observer_delegate : delegate { explicit entity_payload_observer_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + : func_(FLECS_MOV(func)) { } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { @@ -24799,31 +26407,36 @@ struct entity_payload_observer_delegate : delegate { } private: - template ::value == 1> = 0> + template ()( + std::declval()), 0) = 0> static void invoke(ecs_iter_t *iter) { auto self = static_cast( - iter->binding_ctx); + iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION, "entity observer invoked without payload"); Event *data = static_cast(iter->param); - self->m_func(*data); + self->func_(*data); } - template ::value == 2> = 0> + template ()( + std::declval(), + std::declval()), 0) = 0> static void invoke(ecs_iter_t *iter) { auto self = static_cast( - iter->binding_ctx); + iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION, "entity observer invoked without payload"); Event *data = static_cast(iter->param); - self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1)), *data); + self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0)), *data); } - Func m_func; + Func func_; }; @@ -24855,42 +26468,55 @@ struct entity_with_delegate_impl> { } static - bool get_ptrs(world_t *world, const ecs_record_t *r, ecs_table_t *table, + bool get_ptrs(world_t *world, flecs::entity_t e, const ecs_record_t *r, ecs_table_t *table, ArrayType& ptrs) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - if (!ecs_table_column_count(table)) { + if (!ecs_table_column_count(table) && + !ecs_table_has_flags(table, EcsTableHasSparse)) + { return false; } /* table_index_of needs real world */ const flecs::world_t *real_world = ecs_get_world(world); + IdArray ids ({ + _::type().id(world)... + }); + /* Get column indices for components */ ColumnArray columns ({ ecs_table_get_column_index(real_world, table, - _::cpp_type().id(world))... + _::type().id(world))... }); /* Get pointers for columns for entity */ size_t i = 0; for (int32_t column : columns) { if (column == -1) { - return false; + /* Component could be sparse */ + void *ptr = ecs_get_mut_id(world, e, ids[i]); + if (!ptr) { + return false; + } + + ptrs[i ++] = ptr; + continue; } - ptrs[i ++] = ecs_record_get_column(r, column, 0); + ptrs[i ++] = ecs_record_get_by_column(r, column, 0); } return true; } - static bool get_mut_ptrs(world_t *world, ecs_entity_t e, ArrayType& ptrs) { - /* Get pointers w/get_mut */ + static bool ensure_ptrs(world_t *world, ecs_entity_t e, ArrayType& ptrs) { + /* Get pointers w/ensure */ size_t i = 0; DummyArray dummy ({ - (ptrs[i ++] = ecs_get_mut_id(world, e, - _::cpp_type().id(world)), 0)... + (ptrs[i ++] = ecs_ensure_id(world, e, + _::type().id(world)), 0)... }); return true; @@ -24909,7 +26535,7 @@ struct entity_with_delegate_impl> { } ArrayType ptrs; - bool has_components = get_ptrs(world, r, table, ptrs); + bool has_components = get_ptrs(world, e, r, table, ptrs); if (has_components) { invoke_callback(func, 0, ptrs); } @@ -24932,7 +26558,7 @@ struct entity_with_delegate_impl> { } ArrayType ptrs; - bool has_components = get_ptrs(world, r, table, ptrs); + bool has_components = get_ptrs(world, e, r, table, ptrs); if (has_components) { invoke_callback(func, 0, ptrs); } @@ -24965,7 +26591,7 @@ struct entity_with_delegate_impl> { } template - static bool invoke_get_mut(world_t *world, entity_t id, const Func& func) { + static bool invoke_ensure(world_t *world, entity_t id, const Func& func) { flecs::world w(world); ArrayType ptrs; @@ -24998,6 +26624,7 @@ struct entity_with_delegate_impl> { elem = store_added(added, elem, prev, next, w.id()), prev = next, 0 )... }); + (void)dummy_before; // If table is different, move entity straight to it @@ -25009,15 +26636,15 @@ struct entity_with_delegate_impl> { table = next; } - if (!get_ptrs(w, r, table, ptrs)) { + if (!get_ptrs(w, id, r, table, ptrs)) { ecs_abort(ECS_INTERNAL_ERROR, NULL); } ECS_TABLE_LOCK(world, table); - // When deferred, obtain pointers with regular get_mut + // When deferred, obtain pointers with regular ensure } else { - get_mut_ptrs(world, id, ptrs); + ensure_ptrs(world, id, ptrs); } invoke_callback(func, 0, ptrs); @@ -25075,1595 +26702,1830 @@ using delegate = _::each_delegate::type, Args...>; } // namespace flecs /** - * @file addons/cpp/utils/iterable.hpp - * @brief Base class for iterable objects, like queries. + * @file addons/cpp/component.hpp + * @brief Registering/obtaining info from components. */ -namespace flecs { - -template -struct iter_iterable; - -template -struct page_iterable; - -template -struct worker_iterable; - -template -struct iterable { - - /** Each iterator. - * The "each" iterator accepts a function that is invoked for each matching - * entity. The following function signatures are valid: - * - func(flecs::entity e, Components& ...) - * - func(flecs::iter& it, size_t index, Components& ....) - * - func(Components& ...) - * - * Each iterators are automatically instanced. - */ - template - void each(Func&& func) const { - each(nullptr, FLECS_FWD(func)); - } +#pragma once - template - void each(flecs::world_t *world, Func&& func) const { - iterate<_::each_delegate>(world, FLECS_FWD(func), - this->next_each_action()); - } +#include +#include - template - void each(flecs::iter& it, Func&& func) const { - iterate<_::each_delegate>(it.world(), FLECS_FWD(func), - this->next_each_action()); - } +/** + * @defgroup cpp_components Components + * @ingroup cpp_core + * Registering and working with components. + * + * @{ + */ - template - void each(flecs::entity e, Func&& func) const { - iterate<_::each_delegate>(e.world(), FLECS_FWD(func), - this->next_each_action()); - } +namespace flecs { - template - flecs::entity find(Func&& func) const { - return iterate_find<_::find_delegate>(nullptr, FLECS_FWD(func), - this->next_each_action()); - } +namespace _ { - /** Iter iterator. - * The "iter" iterator accepts a function that is invoked for each matching - * table. The following function signatures are valid: - * - func(flecs::iter& it, Components* ...) - * - func(Components& ...) - * - * Iter iterators are not automatically instanced. When a result contains - * shared components, entities of the result will be iterated one by one. - * This ensures that applications can't accidentally read out of bounds by - * accessing a shared component as an array. - */ - template - void iter(Func&& func) const { - iterate<_::iter_delegate>(nullptr, FLECS_FWD(func), - this->next_action()); - } +// Trick to obtain typename from type, as described here +// https://blog.molecular-matters.com/2015/12/11/getting-the-type-of-a-template-argument-as-string-without-rtti/ +// +// The code from the link has been modified to work with more types, and across +// multiple compilers. The resulting string should be the same on all platforms +// for all compilers. +// - template - void iter(flecs::world_t *world, Func&& func) const { - iterate<_::iter_delegate>(world, FLECS_FWD(func), - this->next_action()); - } +#if defined(__GNUC__) || defined(_WIN32) +template +inline const char* type_name() { + static const size_t len = ECS_FUNC_TYPE_LEN(const char*, type_name, ECS_FUNC_NAME); + static char result[len + 1] = {}; + static const size_t front_len = ECS_FUNC_NAME_FRONT(const char*, type_name); + static const char* cppTypeName = ecs_cpp_get_type_name(result, ECS_FUNC_NAME, len, front_len); + return cppTypeName; +} +#else +#error "implicit component registration not supported" +#endif - template - void iter(flecs::iter& it, Func&& func) const { - iterate<_::iter_delegate>(it.world(), FLECS_FWD(func), - this->next_action()); - } +// Translate a typename into a language-agnostic identifier. This allows for +// registration of components/modules across language boundaries. +template +inline const char* symbol_name() { + static const size_t len = ECS_FUNC_TYPE_LEN(const char*, symbol_name, ECS_FUNC_NAME); + static char result[len + 1] = {}; + static const char* cppSymbolName = ecs_cpp_get_symbol_name(result, type_name(), len); + return cppSymbolName; +} - template - void iter(flecs::entity e, Func&& func) const { - iterate<_::iter_delegate>(e.world(), FLECS_FWD(func), - this->next_action()); - } +template <> inline const char* symbol_name() { + return "u8"; +} +template <> inline const char* symbol_name() { + return "u16"; +} +template <> inline const char* symbol_name() { + return "u32"; +} +template <> inline const char* symbol_name() { + return "u64"; +} +template <> inline const char* symbol_name() { + return "i8"; +} +template <> inline const char* symbol_name() { + return "i16"; +} +template <> inline const char* symbol_name() { + return "i32"; +} +template <> inline const char* symbol_name() { + return "i64"; +} +template <> inline const char* symbol_name() { + return "f32"; +} +template <> inline const char* symbol_name() { + return "f64"; +} - /** Create iterator. - * Create an iterator object that can be modified before iterating. - */ - iter_iterable iter(flecs::world_t *world = nullptr) const; +// If type is trivial, don't register lifecycle actions. While the functions +// that obtain the lifecycle callback do detect whether the callback is required +// adding a special case for trivial types eases the burden a bit on the +// compiler as it reduces the number of templates to evaluate. +template::value == true + >* = nullptr> +void register_lifecycle_actions(ecs_world_t*, ecs_entity_t) { } - /** Page iterator. - * Create an iterator that limits the returned entities with offset/limit. - * - * @param offset How many entities to skip. - * @param limit The maximum number of entities to return. - * @return Iterable that can be iterated with each/iter. - */ - page_iterable page(int32_t offset, int32_t limit); +// If the component is non-trivial, register component lifecycle actions. +// Depending on the type not all callbacks may be available. +template::value == false + >* = nullptr> +void register_lifecycle_actions( + ecs_world_t *world, + ecs_entity_t component) +{ + ecs_type_hooks_t cl{}; + cl.ctor = ctor(); + cl.dtor = dtor(); - /** Worker iterator. - * Create an iterator that divides the number of matched entities across - * a number of resources. - * - * @param index The index of the current resource. - * @param count The total number of resources to divide entities between. - * @return Iterable that can be iterated with each/iter. - */ - worker_iterable worker(int32_t index, int32_t count); + cl.copy = copy(); + cl.copy_ctor = copy_ctor(); + cl.move = move(); + cl.move_ctor = move_ctor(); - /** Return number of entities matched by iterable. */ - int32_t count() const { - return this->iter().count(); - } + cl.ctor_move_dtor = ctor_move_dtor(); + cl.move_dtor = move_dtor(); - /** Return whether iterable has any matches. */ - bool is_true() const { - return this->iter().is_true(); - } + ecs_set_hooks_id( world, component, &cl); - /** Return first entity matched by iterable. */ - flecs::entity first() const { - return this->iter().first(); + if (cl.move == ecs_move_illegal || cl.move_ctor == ecs_move_ctor_illegal) { + ecs_add_id(world, component, flecs::Sparse); } +} - virtual ~iterable() { } -protected: - friend iter_iterable; - friend page_iterable; - friend worker_iterable; - - virtual ecs_iter_t get_iter(flecs::world_t *stage) const = 0; - virtual ecs_iter_next_action_t next_action() const = 0; - virtual ecs_iter_next_action_t next_each_action() const = 0; +// Class that manages component ids across worlds & binaries. +// The type class stores the component id for a C++ type in a static global +// variable that is shared between worlds. Whenever a component is used this +// class will check if it already has been registered (has the global id been +// set), and if not, register the component with the world. +// +// If the id has been set, the class will ensure it is known by the world. If it +// is not known the component has been registered by another world and will be +// registered with the world using the same id. If the id does exist, the class +// will register it as a component, and verify whether the input is consistent. +template +struct type_impl { + static_assert(is_pointer::value == false, + "pointer types are not allowed for components"); - template < template class Delegate, typename Func, typename NextFunc, typename ... Args> - void iterate(flecs::world_t *stage, Func&& func, NextFunc next, Args &&... args) const { - ecs_iter_t it = this->get_iter(stage); - if (Delegate::instanced()) { - ECS_BIT_SET(it.flags, EcsIterIsInstanced); + // Initialize component identifier + static void init( + entity_t entity, + bool allow_tag = true) + { + if (s_reset_count != ecs_cpp_reset_count_get()) { + reset(); } - while (next(&it, FLECS_FWD(args)...)) { - Delegate(func).invoke(&it); - } - } + // If an identifier was already set, check for consistency + if (s_id) { + ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, + type_name()); + ecs_assert(allow_tag == s_allow_tag, ECS_INVALID_PARAMETER, NULL); - template < template class Delegate, typename Func, typename NextFunc, typename ... Args> - flecs::entity iterate_find(flecs::world_t *stage, Func&& func, NextFunc next, Args &&... args) const { - ecs_iter_t it = this->get_iter(stage); - if (Delegate::instanced()) { - ECS_BIT_SET(it.flags, EcsIterIsInstanced); + // Component was already registered and data is consistent with new + // identifier, so nothing else to be done. + return; } - flecs::entity result; - while (!result && next(&it, FLECS_FWD(args)...)) { - result = Delegate(func).invoke(&it); - } - if (result) { - ecs_iter_fini(&it); + // Component wasn't registered yet, set the values. Register component + // name as the fully qualified flecs path. + s_id = entity; + s_allow_tag = allow_tag; + s_size = sizeof(T); + s_alignment = alignof(T); + if (is_empty::value && allow_tag) { + s_size = 0; + s_alignment = 0; } - return result; - } -}; -template -struct iter_iterable final : iterable { - template - iter_iterable(Iterable *it, flecs::world_t *world) - { - m_it = it->get_iter(world); - m_next = it->next_action(); - m_next_each = it->next_action(); + s_reset_count = ecs_cpp_reset_count_get(); } - iter_iterable& set_var(int var_id, flecs::entity_t value) { - ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, 0); - ecs_iter_set_var(&m_it, var_id, value); - return *this; - } + // Obtain a component identifier for explicit component registration. + static entity_t id_explicit(world_t *world = nullptr, + const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0, + bool is_component = true, bool *existing = nullptr) + { + if (!s_id) { + // If no world was provided the component cannot be registered + ecs_assert(world != nullptr, ECS_COMPONENT_NOT_REGISTERED, name); + } else { + ecs_assert(!id || s_id == id, ECS_INCONSISTENT_COMPONENT_ID, NULL); + } -# ifdef FLECS_RULES -/** - * @file addons/cpp/mixins/rule/iterable.inl - * @brief Rule iterable mixin. - */ + // If no id has been registered yet for the component (indicating the + // component has not yet been registered, or the component is used + // across more than one binary), or if the id does not exists in the + // world (indicating a multi-world application), register it. + if (!s_id || (world && !ecs_exists(world, s_id))) { + init(s_id ? s_id : id, allow_tag); -/** - * \memberof flecs::iter - * \ingroup cpp_addons_rules - */ + ecs_assert(!id || s_id == id, ECS_INTERNAL_ERROR, NULL); -iter_iterable& set_var(const char *name, flecs::entity_t value) { - ecs_rule_iter_t *rit = &m_it.priv.iter.rule; - int var_id = ecs_rule_find_var(rit->rule, name); - ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); - ecs_iter_set_var(&m_it, var_id, value); - return *this; -} + const char *symbol = nullptr; + if (id) { + symbol = ecs_get_symbol(world, id); + } + if (!symbol) { + symbol = symbol_name(); + } -/** @} */ + entity_t entity = ecs_cpp_component_register_explicit( + world, s_id, id, name, type_name(), symbol, + s_size, s_alignment, is_component, existing); -# endif -# ifdef FLECS_JSON -/** - * @file addons/cpp/mixins/json/iterable.inl - * @brief JSON iterable mixin. - */ + s_id = entity; -/** Serialize iterator result to JSON. - * - * \memberof flecs::iter - * \ingroup cpp_addons_json - */ -flecs::string to_json(flecs::iter_to_json_desc_t *desc = nullptr) { - char *json = ecs_iter_to_json(m_it.real_world, &m_it, desc); - return flecs::string(json); -} + // If component is enum type, register constants + #if FLECS_CPP_ENUM_REFLECTION_SUPPORT + _::init_enum(world, entity); + #endif + } -# endif + // By now the identifier must be valid and known with the world. + ecs_assert(s_id != 0 && ecs_exists(world, s_id), + ECS_INTERNAL_ERROR, NULL); - // Return total number of entities in result. - int32_t count() { - int32_t result = 0; - while (m_next_each(&m_it)) { - result += m_it.count; - } - return result; + return s_id; } - // Returns true if iterator yields at least once result. - bool is_true() { - bool result = m_next_each(&m_it); - if (result) { - ecs_iter_fini(&m_it); - } - return result; - } + // Obtain a component identifier for implicit component registration. This + // is almost the same as id_explicit, except that this operation + // automatically registers lifecycle callbacks. + // Additionally, implicit registration temporarily resets the scope & with + // state of the world, so that the component is not implicitly created with + // the scope/with of the code it happens to be first used by. + static id_t id(world_t *world = nullptr, const char *name = nullptr, + bool allow_tag = true) + { + // If no id has been registered yet, do it now. +#ifndef FLECS_CPP_NO_AUTO_REGISTRATION + if (!registered(world)) { + ecs_entity_t prev_scope = 0; + ecs_id_t prev_with = 0; - // Return first matching entity. - flecs::entity first() { - flecs::entity result; - if (m_next_each(&m_it) && m_it.count) { - result = flecs::entity(m_it.world, m_it.entities[0]); - ecs_iter_fini(&m_it); - } - return result; - } + if (world) { + prev_scope = ecs_set_scope(world, 0); + prev_with = ecs_set_with(world, 0); + } - // Limit results to tables with specified group id (grouped queries only) - iter_iterable& set_group(uint64_t group_id) { - ecs_query_set_group(&m_it, group_id); - return *this; - } + // This will register a component id, but will not register + // lifecycle callbacks. + bool existing; + id_explicit(world, name, allow_tag, 0, true, &existing); - // Limit results to tables with specified group id (grouped queries only) - template - iter_iterable& set_group() { - ecs_query_set_group(&m_it, _::cpp_type().id(m_it.real_world)); - return *this; - } + // Register lifecycle callbacks, but only if the component has a + // size. Components that don't have a size are tags, and tags don't + // require construction/destruction/copy/move's. + if (size() && !existing) { + register_lifecycle_actions(world, s_id); + } -protected: - ecs_iter_t get_iter(flecs::world_t *world) const { - if (world) { - ecs_iter_t result = m_it; - result.world = world; - return result; + if (prev_with) { + ecs_set_with(world, prev_with); + } + if (prev_scope) { + ecs_set_scope(world, prev_scope); + } } - return m_it; - } - - ecs_iter_next_action_t next_action() const { - return m_next; - } +#else + (void)world; + (void)name; + (void)allow_tag; - ecs_iter_next_action_t next_each_action() const { - return m_next_each; - } + ecs_assert(registered(world), ECS_INVALID_OPERATION, + "component '%s' was not registered before use", + type_name()); +#endif -private: - ecs_iter_t m_it; - ecs_iter_next_action_t m_next; - ecs_iter_next_action_t m_next_each; -}; + // By now we should have a valid identifier + ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); -template -iter_iterable iterable::iter(flecs::world_t *world) const -{ - return iter_iterable(this, world); -} + return s_id; + } -template -struct page_iterable final : iterable { - template - page_iterable(int32_t offset, int32_t limit, Iterable *it) - : m_offset(offset) - , m_limit(limit) - { - m_chain_it = it->get_iter(nullptr); + // Return the size of a component. + static size_t size() { + ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + return s_size; } -protected: - ecs_iter_t get_iter(flecs::world_t*) const { - return ecs_page_iter(&m_chain_it, m_offset, m_limit); + // Return the alignment of a component. + static size_t alignment() { + ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + return s_alignment; } - ecs_iter_next_action_t next_action() const { - return ecs_page_next; + // Was the component already registered. + static bool registered(flecs::world_t *world) { + if (s_reset_count != ecs_cpp_reset_count_get()) { + reset(); + } + if (s_id == 0) { + return false; + } + if (world && !ecs_exists(world, s_id)) { + return false; + } + return true; } - ecs_iter_next_action_t next_each_action() const { - return ecs_page_next; + // This function is only used to test cross-translation unit features. No + // code other than test cases should invoke this function. + static void reset() { + s_id = 0; + s_size = 0; + s_alignment = 0; + s_allow_tag = true; } -private: - ecs_iter_t m_chain_it; - int32_t m_offset; - int32_t m_limit; + static entity_t s_id; + static size_t s_size; + static size_t s_alignment; + static bool s_allow_tag; + static int32_t s_reset_count; }; -template -page_iterable iterable::page( - int32_t offset, - int32_t limit) -{ - return page_iterable(offset, limit, this); -} - -template -struct worker_iterable final : iterable { - worker_iterable(int32_t offset, int32_t limit, iterable *it) - : m_offset(offset) - , m_limit(limit) - { - m_chain_it = it->get_iter(nullptr); - } +// Global templated variables that hold component identifier and other info +template entity_t type_impl::s_id; +template size_t type_impl::s_size; +template size_t type_impl::s_alignment; +template bool type_impl::s_allow_tag( true ); +template int32_t type_impl::s_reset_count; -protected: - ecs_iter_t get_iter(flecs::world_t*) const { - return ecs_worker_iter(&m_chain_it, m_offset, m_limit); - } +// Front facing class for implicitly registering a component & obtaining +// static component data - ecs_iter_next_action_t next_action() const { - return ecs_worker_next; - } +// Regular type +template +struct type::value >> + : type_impl> { }; - ecs_iter_next_action_t next_each_action() const { - return ecs_worker_next; +// Pair type +template +struct type::value >> +{ + // Override id method to return id of pair + static id_t id(world_t *world = nullptr) { + return ecs_pair( + type< pair_first_t >::id(world), + type< pair_second_t >::id(world)); } - -private: - ecs_iter_t m_chain_it; - int32_t m_offset; - int32_t m_limit; }; -template -worker_iterable iterable::worker( - int32_t index, - int32_t count) -{ - return worker_iterable(index, count, this); -} +} // namespace _ -} +/** Untyped component class. + * Generic base class for flecs::component. + * + * @ingroup cpp_components + */ +struct untyped_component : entity { + using entity::entity; +# ifdef FLECS_META /** - * @file addons/cpp/component.hpp - * @brief Registering/obtaining info from components. + * @file addons/cpp/mixins/meta/untyped_component.inl + * @brief Meta component mixin. */ -#pragma once - -#include -#include - /** - * @defgroup cpp_components Components - * @brief Registering and working with components. + * @memberof flecs::component + * @ingroup cpp_addons_meta * - * \ingroup cpp_core * @{ */ -namespace flecs { +private: -namespace _ { +/** Private method that adds member to component. */ +untyped_component& internal_member( + flecs::entity_t type_id, + flecs::entity_t unit, + const char *name, + int32_t count = 0, + size_t offset = 0, + bool use_offset = false) +{ + ecs_entity_desc_t desc = {}; + desc.name = name; + desc.parent = id_; + ecs_entity_t eid = ecs_entity_init(world_, &desc); + ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); -// Trick to obtain typename from type, as described here -// https://blog.molecular-matters.com/2015/12/11/getting-the-type-of-a-template-argument-as-string-without-rtti/ -// -// The code from the link has been modified to work with more types, and across -// multiple compilers. The resulting string should be the same on all platforms -// for all compilers. -// + flecs::entity e(world_, eid); -#if defined(__GNUC__) || defined(_WIN32) -template -inline static const char* type_name() { - static const size_t len = ECS_FUNC_TYPE_LEN(const char*, type_name, ECS_FUNC_NAME); - static char result[len + 1] = {}; - static const size_t front_len = ECS_FUNC_NAME_FRONT(const char*, type_name); - return ecs_cpp_get_type_name(result, ECS_FUNC_NAME, len, front_len); -} -#else -#error "implicit component registration not supported" -#endif + Member m = {}; + m.type = type_id; + m.unit = unit; + m.count = count; + m.offset = static_cast(offset); + m.use_offset = use_offset; + e.set(m); -// Translate a typename into a language-agnostic identifier. This allows for -// registration of components/modules across language boundaries. -template -inline static const char* symbol_name() { - static const size_t len = ECS_FUNC_TYPE_LEN(const char*, symbol_name, ECS_FUNC_NAME); - static char result[len + 1] = {}; - return ecs_cpp_get_symbol_name(result, type_name(), len); + return *this; } -template <> inline const char* symbol_name() { - return "u8"; +public: + +/** Add member with unit. */ +untyped_component& member( + flecs::entity_t type_id, + flecs::entity_t unit, + const char *name, + int32_t count = 0) +{ + return internal_member(type_id, unit, name, count, 0, false); } -template <> inline const char* symbol_name() { - return "u16"; + +/** Add member with unit, count and offset. */ +untyped_component& member( + flecs::entity_t type_id, + flecs::entity_t unit, + const char *name, + int32_t count, + size_t offset) +{ + return internal_member(type_id, unit, name, count, offset, true); } -template <> inline const char* symbol_name() { - return "u32"; + +/** Add member. */ +untyped_component& member( + flecs::entity_t type_id, + const char* name, + int32_t count = 0) +{ + return member(type_id, 0, name, count); } -template <> inline const char* symbol_name() { - return "u64"; + +/** Add member with count and offset. */ +untyped_component& member( + flecs::entity_t type_id, + const char* name, + int32_t count, + size_t offset) +{ + return member(type_id, 0, name, count, offset); } -template <> inline const char* symbol_name() { - return "i8"; + +/** Add member. */ +template +untyped_component& member( + const char *name, + int32_t count = 0) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, name, count); } -template <> inline const char* symbol_name() { - return "i16"; + +/** Add member. */ +template +untyped_component& member( + const char *name, + int32_t count, + size_t offset) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, name, count, offset); } -template <> inline const char* symbol_name() { - return "i32"; + +/** Add member with unit. */ +template +untyped_component& member( + flecs::entity_t unit, + const char *name, + int32_t count = 0) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, unit, name, count); } -template <> inline const char* symbol_name() { - return "i64"; + +/** Add member with unit. */ +template +untyped_component& member( + flecs::entity_t unit, + const char *name, + int32_t count, + size_t offset) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, unit, name, count, offset); } -template <> inline const char* symbol_name() { - return "f32"; + +/** Add member with unit. */ +template +untyped_component& member( + const char *name, + int32_t count = 0) +{ + flecs::entity_t type_id = _::type::id(world_); + flecs::entity_t unit_id = _::type::id(world_); + return member(type_id, unit_id, name, count); } -template <> inline const char* symbol_name() { - return "f64"; + +/** Add member with unit. */ +template +untyped_component& member( + const char *name, + int32_t count, + size_t offset) +{ + flecs::entity_t type_id = _::type::id(world_); + flecs::entity_t unit_id = _::type::id(world_); + return member(type_id, unit_id, name, count, offset); } -// If type is trivial, don't register lifecycle actions. While the functions -// that obtain the lifecycle callback do detect whether the callback is required -// adding a special case for trivial types eases the burden a bit on the -// compiler as it reduces the number of templates to evaluate. -template::value == true - >* = nullptr> -void register_lifecycle_actions(ecs_world_t*, ecs_entity_t) { } +/** Add member using pointer-to-member. */ +template ::type> +untyped_component& member( + const char* name, + const MemberType ComponentType::* ptr) +{ + flecs::entity_t type_id = _::type::id(world_); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, name, std::extent::value, offset); +} + +/** Add member with unit using pointer-to-member. */ +template ::type> +untyped_component& member( + flecs::entity_t unit, + const char* name, + const MemberType ComponentType::* ptr) +{ + flecs::entity_t type_id = _::type::id(world_); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, unit, name, std::extent::value, offset); +} + +/** Add member with unit using pointer-to-member. */ +template ::type> +untyped_component& member( + const char* name, + const MemberType ComponentType::* ptr) +{ + flecs::entity_t type_id = _::type::id(world_); + flecs::entity_t unit_id = _::type::id(world_); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, unit_id, name, std::extent::value, offset); +} + +/** Add constant. */ +untyped_component& constant( + const char *name, + int32_t value) +{ + ecs_add_id(world_, id_, _::type::id(world_)); + + ecs_entity_desc_t desc = {}; + desc.name = name; + desc.parent = id_; + ecs_entity_t eid = ecs_entity_init(world_, &desc); + ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); + + ecs_set_id(world_, eid, + ecs_pair(flecs::Constant, flecs::I32), sizeof(int32_t), + &value); -// If the component is non-trivial, register component lifecycle actions. -// Depending on the type not all callbacks may be available. -template::value == false - >* = nullptr> -void register_lifecycle_actions( - ecs_world_t *world, - ecs_entity_t component) + return *this; +} + +/** Add bitmask constant. */ +untyped_component& bit( + const char *name, + uint32_t value) { - ecs_type_hooks_t cl{}; - cl.ctor = ctor(); - cl.dtor = dtor(); + ecs_add_id(world_, id_, _::type::id(world_)); - cl.copy = copy(); - cl.copy_ctor = copy_ctor(); - cl.move = move(); - cl.move_ctor = move_ctor(); + ecs_entity_desc_t desc = {}; + desc.name = name; + desc.parent = id_; + ecs_entity_t eid = ecs_entity_init(world_, &desc); + ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); - cl.ctor_move_dtor = ctor_move_dtor(); - cl.move_dtor = move_dtor(); + ecs_set_id(world_, eid, + ecs_pair(flecs::Constant, flecs::U32), sizeof(uint32_t), + &value); - ecs_set_hooks_id( world, component, &cl); + return *this; } -// Class that manages component ids across worlds & binaries. -// The cpp_type class stores the component id for a C++ type in a static global -// variable that is shared between worlds. Whenever a component is used this -// class will check if it already has been registered (has the global id been -// set), and if not, register the component with the world. -// -// If the id has been set, the class will ensure it is known by the world. If it -// is not known the component has been registered by another world and will be -// registered with the world using the same id. If the id does exist, the class -// will register it as a component, and verify whether the input is consistent. -template -struct cpp_type_impl { - // Initialize component identifier - static void init( - entity_t entity, - bool allow_tag = true) - { - if (s_reset_count != ecs_cpp_reset_count_get()) { - reset(); - } +/** Register array metadata for component */ +template +untyped_component& array( + int32_t elem_count) +{ + ecs_array_desc_t desc = {}; + desc.entity = id_; + desc.type = _::type::id(world_); + desc.count = elem_count; + ecs_array_init(world_, &desc); + return *this; +} - // If an identifier was already set, check for consistency - if (s_id) { - ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, - type_name()); - ecs_assert(allow_tag == s_allow_tag, ECS_INVALID_PARAMETER, NULL); +/** Add member value range */ +untyped_component& range( + double min, + double max) +{ + const flecs::member_t *m = ecs_cpp_last_member(world_, id_); + if (!m) { + return *this; + } - // Component was already registered and data is consistent with new - // identifier, so nothing else to be done. - return; - } + flecs::world w(world_); + flecs::entity me = w.entity(m->member); - // Component wasn't registered yet, set the values. Register component - // name as the fully qualified flecs path. - s_id = entity; - s_allow_tag = allow_tag; - s_size = sizeof(T); - s_alignment = alignof(T); - if (is_empty::value && allow_tag) { - s_size = 0; - s_alignment = 0; - } + // Don't use C++ ensure because Unreal defines a macro called ensure + flecs::MemberRanges *mr = static_cast( + ecs_ensure_id(w, me, w.id())); + mr->value.min = min; + mr->value.max = max; + me.modified(); + return *this; +} - s_reset_count = ecs_cpp_reset_count_get(); +/** Add member warning range */ +untyped_component& warning_range( + double min, + double max) +{ + const flecs::member_t *m = ecs_cpp_last_member(world_, id_); + if (!m) { + return *this; } - // Obtain a component identifier for explicit component registration. - static entity_t id_explicit(world_t *world = nullptr, - const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0, - bool is_component = true, bool *existing = nullptr) - { - if (!s_id) { - // If no world was provided the component cannot be registered - ecs_assert(world != nullptr, ECS_COMPONENT_NOT_REGISTERED, name); - } else { - ecs_assert(!id || s_id == id, ECS_INCONSISTENT_COMPONENT_ID, NULL); - } + flecs::world w(world_); + flecs::entity me = w.entity(m->member); - // If no id has been registered yet for the component (indicating the - // component has not yet been registered, or the component is used - // across more than one binary), or if the id does not exists in the - // world (indicating a multi-world application), register it. - if (!s_id || (world && !ecs_exists(world, s_id))) { - init(s_id ? s_id : id, allow_tag); + // Don't use C++ ensure because Unreal defines a macro called ensure + flecs::MemberRanges *mr = static_cast( + ecs_ensure_id(w, me, w.id())); + mr->warning.min = min; + mr->warning.max = max; + me.modified(); + return *this; +} - ecs_assert(!id || s_id == id, ECS_INTERNAL_ERROR, NULL); +/** Add member error range */ +untyped_component& error_range( + double min, + double max) +{ + const flecs::member_t *m = ecs_cpp_last_member(world_, id_); + if (!m) { + return *this; + } - const char *symbol = nullptr; - if (id) { - symbol = ecs_get_symbol(world, id); - } - if (!symbol) { - symbol = symbol_name(); - } + flecs::world w(world_); + flecs::entity me = w.entity(m->member); - entity_t entity = ecs_cpp_component_register_explicit( - world, s_id, id, name, type_name(), symbol, - s_size, s_alignment, is_component, existing); + // Don't use C++ ensure because Unreal defines a macro called ensure + flecs::MemberRanges *mr = static_cast(ecs_ensure_id( + w, me, w.id())); + mr->error.min = min; + mr->error.max = max; + me.modified(); + return *this; +} - s_id = entity; +/** @} */ - // If component is enum type, register constants - #if FLECS_CPP_ENUM_REFLECTION_SUPPORT - _::init_enum(world, entity); - #endif - } +# endif +# ifdef FLECS_METRICS +/** + * @file addons/cpp/mixins/meta/untyped_component.inl + * @brief Metrics component mixin. + */ - // By now the identifier must be valid and known with the world. - ecs_assert(s_id != 0 && ecs_exists(world, s_id), - ECS_INTERNAL_ERROR, NULL); +/** + * @memberof flecs::component + * @ingroup cpp_addons_metrics + * + * @{ + */ - return s_id; - } +/** Register member as metric. + * When no explicit name is provided, this operation will derive the metric name + * from the member name. When the member name is "value", the operation will use + * the name of the component. + * + * When the brief parameter is provided, it is set on the metric as if + * set_doc_brief is used. The brief description can be obtained with + * get_doc_brief. + * + * @tparam Kind Metric kind (Counter, CounterIncrement or Gauge). + * @param parent Parent entity of the metric (optional). + * @param brief Description for metric (optional). + * @param name Name of metric (optional). + */ +template +untyped_component& metric( + flecs::entity_t parent = 0, + const char *brief = nullptr, + const char *name = nullptr); - // Obtain a component identifier for implicit component registration. This - // is almost the same as id_explicit, except that this operation - // automatically registers lifecycle callbacks. - // Additionally, implicit registration temporarily resets the scope & with - // state of the world, so that the component is not implicitly created with - // the scope/with of the code it happens to be first used by. - static id_t id(world_t *world = nullptr, const char *name = nullptr, - bool allow_tag = true) +/** @} */ + +# endif +}; + +/** Component class. + * Class used to register components and component metadata. + * + * @ingroup cpp_components + */ +template +struct component : untyped_component { + /** Register a component. + * If the component was already registered, this operation will return a handle + * to the existing component. + * + * @param world The world for which to register the component. + * @param name Optional name (overrides typename). + * @param allow_tag If true, empty types will be registered with size 0. + * @param id Optional id to register component with. + */ + component( + flecs::world_t *world, + const char *name = nullptr, + bool allow_tag = true, + flecs::id_t id = 0) { - // If no id has been registered yet, do it now. - if (!registered(world)) { - ecs_entity_t prev_scope = 0; - ecs_id_t prev_with = 0; + const char *n = name; + bool implicit_name = false; + if (!n) { + n = _::type_name(); - if (world) { - prev_scope = ecs_set_scope(world, 0); - prev_with = ecs_set_with(world, 0); - } + /* Keep track of whether name was explicitly set. If not, and the + * component was already registered, just use the registered name. + * + * The registered name may differ from the typename as the registered + * name includes the flecs scope. This can in theory be different from + * the C++ namespace though it is good practice to keep them the same */ + implicit_name = true; + } - // This will register a component id, but will not register - // lifecycle callbacks. - bool existing; - id_explicit(world, name, allow_tag, 0, true, &existing); + if (_::type::registered(world)) { + /* Obtain component id. Because the component is already registered, + * this operation does nothing besides returning the existing id */ + id = _::type::id_explicit(world, name, allow_tag, id); - // Register lifecycle callbacks, but only if the component has a - // size. Components that don't have a size are tags, and tags don't - // require construction/destruction/copy/move's. - if (size() && !existing) { - register_lifecycle_actions(world, s_id); - } - - if (prev_with) { - ecs_set_with(world, prev_with); - } - if (prev_scope) { - ecs_set_scope(world, prev_scope); + ecs_cpp_component_validate(world, id, n, _::symbol_name(), + _::type::size(), + _::type::alignment(), + implicit_name); + } else { + /* If component is registered from an existing scope, ignore the + * namespace in the name of the component. */ + if (implicit_name && (ecs_get_scope(world) != 0)) { + /* If the type is a template type, make sure to ignore ':' + * inside the template parameter list. */ + const char *start = strchr(n, '<'), *last_elem = NULL; + if (start) { + const char *ptr = start; + while (ptr[0] && (ptr[0] != ':') && (ptr > n)) { + ptr --; + } + if (ptr[0] == ':') { + last_elem = ptr; + } + } + + if (last_elem) { + name = last_elem + 1; + } } - } - // By now we should have a valid identifier - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + /* Find or register component */ + bool existing; + id = ecs_cpp_component_register(world, id, n, _::symbol_name(), + ECS_SIZEOF(T), ECS_ALIGNOF(T), implicit_name, &existing); + + /* Initialize static component data */ + id = _::type::id_explicit(world, name, allow_tag, id); - return s_id; - } + /* Initialize lifecycle actions (ctor, dtor, copy, move) */ + if (_::type::size() && !existing) { + _::register_lifecycle_actions(world, id); + } + } - // Return the size of a component. - static size_t size() { - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); - return s_size; + world_ = world; + id_ = id; } - // Return the alignment of a component. - static size_t alignment() { - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); - return s_alignment; + /** Register on_add hook. */ + template + component& on_add(Func&& func) { + using Delegate = typename _::each_delegate::type, T>; + flecs::type_hooks_t h = get_hooks(); + ecs_assert(h.on_add == nullptr, ECS_INVALID_OPERATION, + "on_add hook is already set"); + BindingCtx *ctx = get_binding_ctx(h); + h.on_add = Delegate::run_add; + ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func)); + ctx->free_on_add = reinterpret_cast( + _::free_obj); + ecs_set_hooks_id(world_, id_, &h); + return *this; } - // Was the component already registered. - static bool registered(flecs::world_t *world) { - if (s_reset_count != ecs_cpp_reset_count_get()) { - reset(); - } - if (s_id == 0) { - return false; - } - if (world && !ecs_exists(world, s_id)) { - return false; - } - return true; + /** Register on_remove hook. */ + template + component& on_remove(Func&& func) { + using Delegate = typename _::each_delegate< + typename std::decay::type, T>; + flecs::type_hooks_t h = get_hooks(); + ecs_assert(h.on_remove == nullptr, ECS_INVALID_OPERATION, + "on_remove hook is already set"); + BindingCtx *ctx = get_binding_ctx(h); + h.on_remove = Delegate::run_remove; + ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func)); + ctx->free_on_remove = reinterpret_cast( + _::free_obj); + ecs_set_hooks_id(world_, id_, &h); + return *this; } - // This function is only used to test cross-translation unit features. No - // code other than test cases should invoke this function. - static void reset() { - s_id = 0; - s_size = 0; - s_alignment = 0; - s_allow_tag = true; + /** Register on_set hook. */ + template + component& on_set(Func&& func) { + using Delegate = typename _::each_delegate< + typename std::decay::type, T>; + flecs::type_hooks_t h = get_hooks(); + ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION, + "on_set hook is already set"); + BindingCtx *ctx = get_binding_ctx(h); + h.on_set = Delegate::run_set; + ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func)); + ctx->free_on_set = reinterpret_cast( + _::free_obj); + ecs_set_hooks_id(world_, id_, &h); + return *this; } - static entity_t s_id; - static size_t s_size; - static size_t s_alignment; - static bool s_allow_tag; - static int32_t s_reset_count; -}; - -// Global templated variables that hold component identifier and other info -template entity_t cpp_type_impl::s_id; -template size_t cpp_type_impl::s_size; -template size_t cpp_type_impl::s_alignment; -template bool cpp_type_impl::s_allow_tag( true ); -template int32_t cpp_type_impl::s_reset_count; - -// Front facing class for implicitly registering a component & obtaining -// static component data +# ifdef FLECS_META -// Regular type -template -struct cpp_type::value >> - : cpp_type_impl> { }; +/** Register opaque type interface */ +template +component& opaque(const Func& type_support) { + flecs::world world(world_); + auto ts = type_support(world); + ts.desc.entity = _::type::id(world_); + ecs_opaque_init(world_, &ts.desc); + return *this; +} -// Pair type -template -struct cpp_type::value >> -{ - // Override id method to return id of pair - static id_t id(world_t *world = nullptr) { - return ecs_pair( - cpp_type< pair_first_t >::id(world), - cpp_type< pair_second_t >::id(world)); - } -}; +flecs::opaque opaque(flecs::entity_t as_type) { + return flecs::opaque(world_).as_type(as_type); +} -} // namespace _ +flecs::opaque opaque(flecs::entity as_type) { + return this->opaque(as_type.id()); +} -/** Untyped component class. - * Generic base class for flecs::component. - * - * \ingroup cpp_components - */ -struct untyped_component : entity { - using entity::entity; - -# ifdef FLECS_META -/** - * @file addons/cpp/mixins/meta/untyped_component.inl - * @brief Meta component mixin. - */ +flecs::opaque opaque(flecs::untyped_component as_type) { + return this->opaque(as_type.id()); +} -/** - * \memberof flecs::component - * \ingroup cpp_addons_meta - * - * @{ - */ +/** Return opaque type builder for collection type */ +template +flecs::opaque opaque(flecs::id_t as_type) { + return flecs::opaque(world_).as_type(as_type); +} -/** Add member with unit. */ -untyped_component& member(flecs::entity_t type_id, flecs::entity_t unit, const char *name, int32_t count = 0, size_t offset = 0) { - ecs_entity_desc_t desc = {}; - desc.name = name; - desc.add[0] = ecs_pair(flecs::ChildOf, m_id); - ecs_entity_t eid = ecs_entity_init(m_world, &desc); - ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); +/** Add constant. */ +component& constant(const char *name, T value) { + int32_t v = static_cast(value); + untyped_component::constant(name, v); + return *this; +} - flecs::entity e(m_world, eid); +# endif - Member m = {}; - m.type = type_id; - m.unit = unit; - m.count = count; - m.offset = static_cast(offset); - e.set(m); +private: + using BindingCtx = _::component_binding_ctx; - return *this; -} + BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){ + BindingCtx *result = static_cast(h.binding_ctx); + if (!result) { + result = FLECS_NEW(BindingCtx); + h.binding_ctx = result; + h.binding_ctx_free = reinterpret_cast( + _::free_obj); + } + return result; + } -/** Add member. */ -untyped_component& member(flecs::entity_t type_id, const char* name, int32_t count = 0, size_t offset = 0) { - return member(type_id, 0, name, count, offset); -} + flecs::type_hooks_t get_hooks() { + const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_); + if (h) { + return *h; + } else { + return {}; + } + } +}; -/** Add member. */ -template -untyped_component& member(const char *name, int32_t count = 0, size_t offset = 0) { - flecs::entity_t type_id = _::cpp_type::id(m_world); - return member(type_id, name, count, offset); +/** Get id currently assigned to component. If no world has registered the + * component yet, this operation will return 0. */ +template +flecs::entity_t type_id() { + if (_::type::s_reset_count == ecs_cpp_reset_count_get()) { + return _::type::s_id; + } else { + return 0; + } } -/** Add member with unit. */ -template -untyped_component& member(flecs::entity_t unit, const char *name, int32_t count = 0, size_t offset = 0) { - flecs::entity_t type_id = _::cpp_type::id(m_world); - return member(type_id, unit, name, count, offset); +/** Reset static component ids. + * When components are registered their component ids are stored in a static + * type specific variable. This stored id is passed into component registration + * functions to ensure consistent ids across worlds. + * + * In some cases this can be undesirable, like when a process repeatedly creates + * worlds with different components. A typical example where this can happen is + * when running multiple tests in a single process, where each test registers + * its own set of components. + * + * This operation can be used to prevent reusing of component ids and force + * generating a new ids upon registration. + * + * Note that this operation should *never* be called while there are still + * alive worlds in a process. Doing so results in undefined behavior. + * + * Also note that this operation does not actually change the static component + * variables. It only ensures that the next time a component id is requested, a + * new id will be generated. + * + * @ingroup cpp_components + */ +inline void reset() { + ecs_cpp_reset_count_inc(); } -/** Add member with unit. */ -template -untyped_component& member(const char *name, int32_t count = 0, size_t offset = 0) { - flecs::entity_t type_id = _::cpp_type::id(m_world); - flecs::entity_t unit_id = _::cpp_type::id(m_world); - return member(type_id, unit_id, name, count, offset); } -/** Add member using pointer-to-member. */ -template ::type> -untyped_component& member(const char* name, const MemberType ComponentType::* ptr) { - flecs::entity_t type_id = _::cpp_type::id(m_world); - size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); - return member(type_id, name, std::extent::value, offset); -} +/** @} */ -/** Add member with unit using pointer-to-member. */ -template ::type> -untyped_component& member(flecs::entity_t unit, const char* name, const MemberType ComponentType::* ptr) { - flecs::entity_t type_id = _::cpp_type::id(m_world); - size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); - return member(type_id, unit, name, std::extent::value, offset); -} +/** + * @file addons/cpp/ref.hpp + * @brief Class that caches data to speedup get operations. + */ -/** Add member with unit using pointer-to-member. */ -template ::type> -untyped_component& member(const char* name, const MemberType ComponentType::* ptr) { - flecs::entity_t type_id = _::cpp_type::id(m_world); - flecs::entity_t unit_id = _::cpp_type::id(m_world); - size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); - return member(type_id, unit_id, name, std::extent::value, offset); -} +#pragma once -/** Add constant. */ -untyped_component& constant(const char *name, int32_t value) { - ecs_add_id(m_world, m_id, _::cpp_type::id(m_world)); +namespace flecs +{ - ecs_entity_desc_t desc = {}; - desc.name = name; - desc.add[0] = ecs_pair(flecs::ChildOf, m_id); - ecs_entity_t eid = ecs_entity_init(m_world, &desc); - ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); +/** + * @defgroup cpp_ref Refs + * @ingroup cpp_core + * Refs are a fast mechanism for referring to a specific entity/component. + * + * @{ + */ - ecs_set_id(m_world, eid, - ecs_pair(flecs::Constant, flecs::I32), sizeof(int32_t), - &value); +/** Component reference. + * Reference to a component from a specific entity. + */ +template +struct ref { + ref() : world_(nullptr), ref_{} { } - return *this; -} + ref(world_t *world, entity_t entity, flecs::id_t id = 0) + : ref_() + { + // the world we were called with may be a stage; convert it to a world + // here if that is the case + world_ = world ? const_cast(ecs_get_world(world)) + : nullptr; + if (!id) { + id = _::type::id(world); + } -/** Add bitmask constant. */ -untyped_component& bit(const char *name, uint32_t value) { - ecs_add_id(m_world, m_id, _::cpp_type::id(m_world)); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); - ecs_entity_desc_t desc = {}; - desc.name = name; - desc.add[0] = ecs_pair(flecs::ChildOf, m_id); - ecs_entity_t eid = ecs_entity_init(m_world, &desc); - ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); + ref_ = ecs_ref_init_id(world_, entity, id); + } - ecs_set_id(m_world, eid, - ecs_pair(flecs::Constant, flecs::U32), sizeof(uint32_t), - &value); + ref(flecs::entity entity, flecs::id_t id = 0) + : ref(entity.world(), entity.id(), id) { } - return *this; -} + T* operator->() { + T* result = static_cast(ecs_ref_get_id( + world_, &ref_, this->ref_.id)); -/** Register array metadata for component */ -template -untyped_component& array(int32_t elem_count) { - ecs_array_desc_t desc = {}; - desc.entity = m_id; - desc.type = _::cpp_type::id(m_world); - desc.count = elem_count; - ecs_array_init(m_world, &desc); - return *this; -} + ecs_assert(result != NULL, ECS_INVALID_PARAMETER, + "nullptr dereference by flecs::ref"); -/** Add member value range */ -untyped_component& range(double min, double max) { - const flecs::member_t *m = ecs_cpp_last_member(m_world, m_id); - if (!m) { - return *this; + return result; } - flecs::world w(m_world); - flecs::entity me = w.entity(m->member); - flecs::MemberRanges *mr = me.get_mut(); - mr->value.min = min; - mr->value.max = max; - me.modified(); - return *this; -} + T* get() { + return static_cast(ecs_ref_get_id( + world_, &ref_, this->ref_.id)); + } -/** Add member warning range */ -untyped_component& warning_range(double min, double max) { - const flecs::member_t *m = ecs_cpp_last_member(m_world, m_id); - if (!m) { - return *this; + T* try_get() { + if (!world_ || !ref_.entity) { + return nullptr; + } + + return get(); } - flecs::world w(m_world); - flecs::entity me = w.entity(m->member); - flecs::MemberRanges *mr = me.get_mut(); - mr->warning.min = min; - mr->warning.max = max; - me.modified(); - return *this; -} + bool has() { + return !!try_get(); + } -/** Add member error range */ -untyped_component& error_range(double min, double max) { - const flecs::member_t *m = ecs_cpp_last_member(m_world, m_id); - if (!m) { - return *this; + /** implicit conversion to bool. return true if there is a valid T* being referred to **/ + operator bool() { + return has(); } - flecs::world w(m_world); - flecs::entity me = w.entity(m->member); - flecs::MemberRanges *mr = me.get_mut(); - mr->error.min = min; - mr->error.max = max; - me.modified(); - return *this; -} + flecs::entity entity() const; +private: + world_t *world_; + flecs::ref_t ref_; +}; /** @} */ -# endif -# ifdef FLECS_METRICS +} + /** - * @file addons/cpp/mixins/meta/untyped_component.inl - * @brief Metrics component mixin. + * @file addons/cpp/type.hpp + * @brief Utility functions for id vector. */ +#pragma once + +namespace flecs { + /** - * \memberof flecs::component - * \ingroup cpp_addons_metrics - * + * @defgroup cpp_types Types + * @ingroup cpp_core + * @brief Type operations. + * * @{ */ -/** Register member as metric. - * When no explicit name is provided, this operation will derive the metric name - * from the member name. When the member name is "value", the operation will use - * the name of the component. - * - * When the brief parameter is provided, it is set on the metric as if - * set_doc_brief is used. The brief description can be obtained with - * get_doc_brief. - * - * @tparam Kind Metric kind (Counter, CounterIncrement or Gauge). - * @param parent Parent entity of the metric (optional). - * @param brief Description for metric (optional). - * @param name Name of metric (optional). - * - * \ingroup cpp_addons_metrics - * \memberof flecs::world +/** Type class. + * A type is a vector of component ids which can be requested from entities or tables. */ -template -untyped_component& metric( - flecs::entity_t parent = 0, - const char *brief = nullptr, - const char *name = nullptr); +struct type { + type() : world_(nullptr), type_(nullptr) { } -/** @} */ + type(world_t *world, const type_t *t) + : world_(world) + , type_(t) { } -# endif + /** Convert type to comma-separated string */ + flecs::string str() const { + return flecs::string(ecs_type_str(world_, type_)); + } + + /** Return number of ids in type */ + int32_t count() const { + if (!type_) { + return 0; + } + return type_->count; + } + + /** Return pointer to array. */ + flecs::id_t* array() const { + if (!type_) { + return nullptr; + } + return type_->array; + } + + /** Get id at specified index in type */ + flecs::id get(int32_t index) const { + ecs_assert(type_ != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(type_->count > index, ECS_OUT_OF_RANGE, NULL); + if (!type_) { + return flecs::id(); + } + return flecs::id(world_, type_->array[index]); + } + + flecs::id_t* begin() const { + return type_->array; + } + + flecs::id_t* end() const { + return &type_->array[type_->count]; + } + + /** Implicit conversion to type_t */ + operator const type_t*() const { + return type_; + } +private: + world_t *world_; + const type_t *type_; }; -/** Component class. - * Class used to register components and component metadata. - * - * \ingroup cpp_components +/** #} */ + +} + +/** + * @file addons/cpp/table.hpp + * @brief Direct access to table data. */ -template -struct component : untyped_component { - /** Register a component. - * If the component was already registered, this operation will return a handle - * to the existing component. - * - * @param world The world for which to register the component. - * @param name Optional name (overrides typename). - * @param allow_tag If true, empty types will be registered with size 0. - * @param id Optional id to register component with. - */ - component( - flecs::world_t *world, - const char *name = nullptr, - bool allow_tag = true, - flecs::id_t id = 0) - { - const char *n = name; - bool implicit_name = false; - if (!n) { - n = _::type_name(); - /* Keep track of whether name was explicitly set. If not, and the - * component was already registered, just use the registered name. - * - * The registered name may differ from the typename as the registered - * name includes the flecs scope. This can in theory be different from - * the C++ namespace though it is good practice to keep them the same */ - implicit_name = true; - } +#pragma once - if (_::cpp_type::registered(world)) { - /* Obtain component id. Because the component is already registered, - * this operation does nothing besides returning the existing id */ - id = _::cpp_type::id_explicit(world, name, allow_tag, id); +namespace flecs { - ecs_cpp_component_validate(world, id, n, _::symbol_name(), - _::cpp_type::size(), - _::cpp_type::alignment(), - implicit_name); - } else { - /* If component is registered from an existing scope, ignore the - * namespace in the name of the component. */ - if (implicit_name && (ecs_get_scope(world) != 0)) { - /* If the type is a template type, make sure to ignore ':' - * inside the template parameter list. */ - const char *start = strchr(n, '<'), *last_elem = NULL; - if (start) { - const char *ptr = start; - while (ptr[0] && (ptr[0] != ':') && (ptr > n)) { - ptr --; - } - if (ptr[0] == ':') { - last_elem = ptr; - } - } else { - last_elem = strrchr(n, ':'); - } - if (last_elem) { - name = last_elem + 1; - } - } +/** + * @defgroup cpp_tables Tables + * @ingroup cpp_core + * Table operations. + * + * @{ + */ - /* Find or register component */ - bool existing; - id = ecs_cpp_component_register(world, id, n, _::symbol_name(), - ECS_SIZEOF(T), ECS_ALIGNOF(T), implicit_name, &existing); +struct table { + table() : world_(nullptr), table_(nullptr) { } - /* Initialize static component data */ - id = _::cpp_type::id_explicit(world, name, allow_tag, id); + table(world_t *world, table_t *t) + : world_(world) + , table_(t) { } - /* Initialize lifecycle actions (ctor, dtor, copy, move) */ - if (_::cpp_type::size() && !existing) { - _::register_lifecycle_actions(world, id); - } - } + virtual ~table() { } - m_world = world; - m_id = id; + /** Convert table type to string. */ + flecs::string str() const { + return flecs::string(ecs_table_str(world_, table_)); } - /** Register on_add hook. */ - template - component& on_add(Func&& func) { - using Delegate = typename _::each_delegate::type, T>; - flecs::type_hooks_t h = get_hooks(); - ecs_assert(h.on_add == nullptr, ECS_INVALID_OPERATION, - "on_add hook is already set"); - BindingCtx *ctx = get_binding_ctx(h); - h.on_add = Delegate::run_add; - ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func)); - ctx->free_on_add = reinterpret_cast( - _::free_obj); - ecs_set_hooks_id(m_world, m_id, &h); - return *this; + /** Get table type. */ + flecs::type type() const { + return flecs::type(world_, ecs_table_get_type(table_)); } - /** Register on_remove hook. */ - template - component& on_remove(Func&& func) { - using Delegate = typename _::each_delegate< - typename std::decay::type, T>; - flecs::type_hooks_t h = get_hooks(); - ecs_assert(h.on_remove == nullptr, ECS_INVALID_OPERATION, - "on_remove hook is already set"); - BindingCtx *ctx = get_binding_ctx(h); - h.on_remove = Delegate::run_remove; - ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func)); - ctx->free_on_remove = reinterpret_cast( - _::free_obj); - ecs_set_hooks_id(m_world, m_id, &h); - return *this; + /** Get table count. */ + int32_t count() const { + return ecs_table_count(table_); + } + + /** Find type index for (component) id. + * + * @param id The (component) id. + * @return The index of the id in the table type, -1 if not found/ + */ + int32_t type_index(flecs::id_t id) const { + return ecs_table_get_type_index(world_, table_, id); + } + + /** Find type index for type. + * + * @tparam T The type. + * @return True if the table has the type, false if not. + */ + template + int32_t type_index() const { + return type_index(_::type::id(world_)); } - /** Register on_set hook. */ - template - component& on_set(Func&& func) { - using Delegate = typename _::each_delegate< - typename std::decay::type, T>; - flecs::type_hooks_t h = get_hooks(); - ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION, - "on_set hook is already set"); - BindingCtx *ctx = get_binding_ctx(h); - h.on_set = Delegate::run_set; - ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func)); - ctx->free_on_set = reinterpret_cast( - _::free_obj); - ecs_set_hooks_id(m_world, m_id, &h); - return *this; + /** Find type index for pair. + * @param first First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + int32_t type_index(flecs::entity_t first, flecs::entity_t second) const { + return type_index(ecs_pair(first, second)); } -# ifdef FLECS_META + /** Find type index for pair. + * @tparam First First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + int32_t type_index(flecs::entity_t second) const { + return type_index(_::type::id(world_), second); + } -/** Register opaque type interface */ -template -component& opaque(const Func& type_support) { - flecs::world world(m_world); - auto ts = type_support(world); - ts.desc.entity = _::cpp_type::id(m_world); - ecs_opaque_init(m_world, &ts.desc); - return *this; -} + /** Find type index for pair. + * @tparam First First element of pair. + * @tparam Second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + int32_t type_index() const { + return type_index(_::type::id(world_)); + } -flecs::opaque opaque(flecs::entity_t as_type) { - return flecs::opaque(m_world).as_type(as_type); -} + /** Find column index for (component) id. + * + * @param id The (component) id. + * @return The index of the id in the table type, -1 if not found/ + */ + int32_t column_index(flecs::id_t id) const { + return ecs_table_get_column_index(world_, table_, id); + } -flecs::opaque opaque(flecs::entity as_type) { - return this->opaque(as_type.id()); -} + /** Find column index for type. + * + * @tparam T The type. + * @return True if the table has the type, false if not. + */ + template + int32_t column_index() const { + return column_index(_::type::id(world_)); + } -flecs::opaque opaque(flecs::untyped_component as_type) { - return this->opaque(as_type.id()); -} + /** Find column index for pair. + * @param first First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + int32_t column_index(flecs::entity_t first, flecs::entity_t second) const { + return column_index(ecs_pair(first, second)); + } -/** Return opaque type builder for collection type */ -template -flecs::opaque opaque(flecs::id_t as_type) { - return flecs::opaque(m_world).as_type(as_type); -} + /** Find column index for pair. + * @tparam First First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + int32_t column_index(flecs::entity_t second) const { + return column_index(_::type::id(world_), second); + } -/** Add constant. */ -component& constant(const char *name, T value) { - int32_t v = static_cast(value); - untyped_component::constant(name, v); - return *this; -} + /** Find column index for pair. + * @tparam First First element of pair. + * @tparam Second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + int32_t column_index() const { + return column_index(_::type::id(world_)); + } -# endif + /** Test if table has (component) id. + * + * @param id The (component) id. + * @return True if the table has the id, false if not. + */ + bool has(flecs::id_t id) const { + return type_index(id) != -1; + } -private: - using BindingCtx = _::component_binding_ctx; + /** Test if table has the type. + * + * @tparam T The type. + * @return True if the table has the type, false if not. + */ + template + bool has() const { + return type_index() != -1; + } - BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){ - BindingCtx *result = static_cast(h.binding_ctx); - if (!result) { - result = FLECS_NEW(BindingCtx); - h.binding_ctx = result; - h.binding_ctx_free = reinterpret_cast( - _::free_obj); - } - return result; + /** Test if table has the pair. + * + * @param first First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + bool has(flecs::entity_t first, flecs::entity_t second) const { + return type_index(first, second) != -1; } - flecs::type_hooks_t get_hooks() { - const flecs::type_hooks_t* h = ecs_get_hooks_id(m_world, m_id); - if (h) { - return *h; - } else { - return {}; - } + /** Test if table has the pair. + * + * @tparam First First element of pair. + * @param second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + bool has(flecs::entity_t second) const { + return type_index(second) != -1; } -}; -/** Get id currently assigned to component. If no world has registered the - * component yet, this operation will return 0. */ -template -flecs::entity_t type_id() { - if (_::cpp_type::s_reset_count == ecs_cpp_reset_count_get()) { - return _::cpp_type::s_id; - } else { - return 0; + /** Test if table has the pair. + * + * @tparam First First element of pair. + * @tparam Second Second element of pair. + * @return True if the table has the pair, false if not. + */ + template + bool has() const { + return type_index() != -1; } -} -/** Reset static component ids. - * When components are registered their component ids are stored in a static - * type specific variable. This stored id is passed into component registration - * functions to ensure consistent ids across worlds. - * - * In some cases this can be undesirable, like when a process repeatedly creates - * worlds with different components. A typical example where this can happen is - * when running multiple tests in a single process, where each test registers - * its own set of components. - * - * This operation can be used to prevent reusing of component ids and force - * generating a new ids upon registration. - * - * Note that this operation should *never* be called while there are still - * alive worlds in a process. Doing so results in undefined behavior. - * - * Also note that this operation does not actually change the static component - * variables. It only ensures that the next time a component id is requested, a - * new id will be generated. - * - * \ingroup cpp_components - */ -inline void reset() { - ecs_cpp_reset_count_inc(); -} + /** Get pointer to component array by column index. + * + * @param index The column index. + * @return Pointer to the column, NULL if not a component. + */ + virtual void* get_column(int32_t index) const { + return ecs_table_get_column(table_, index, 0); + } -} + /** Get pointer to component array by component. + * + * @param id The component id. + * @return Pointer to the column, NULL if not found. + */ + void* get(flecs::id_t id) const { + int32_t index = column_index(id); + if (index == -1) { + return NULL; + } + return get_column(index); + } -/** @} */ + /** Get pointer to component array by pair. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + * @return Pointer to the column, NULL if not found. + */ + void* get(flecs::entity_t first, flecs::entity_t second) const { + return get(ecs_pair(first, second)); + } -/** - * @file addons/cpp/type.hpp - * @brief Utility functions for id vector. - */ + /** Get pointer to component array by component. + * + * @tparam T The component. + * @return Pointer to the column, NULL if not found. + */ + template ::value > = 0> + T* get() const { + return static_cast(get(_::type::id(world_))); + } -#pragma once + /** Get pointer to component array by (enum) component. + * + * @tparam T The (enum) component. + * @return Pointer to the column, NULL if not found. + */ + template ::value > = 0> + T* get() const { + return static_cast(get(_::type::id(world_))); + } -namespace flecs { + /** Get pointer to component array by component. + * + * @tparam T The component. + * @return Pointer to the column, NULL if not found. + */ + template , + if_t< flecs::is_pair::value > = 0> + A* get() const { + return static_cast(get(_::type::id(world_))); + } -/** - * @defgroup cpp_types Types - * @brief Type operations. - * - * \ingroup cpp_core - * @{ - */ + /** Get pointer to component array by pair. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + * @return Pointer to the column, NULL if not found. + */ + template + First* get(flecs::entity_t second) const { + return static_cast(get(_::type::id(world_), second)); + } -/** Type class. - * A type is a vector of component ids which can be requested from entities or tables. - */ -struct type { - type() : m_world(nullptr), m_type(nullptr) { } + /** Get pointer to component array by pair. + * + * @tparam First The first element of the pair. + * @tparam Second The second element of the pair. + * @return Pointer to the column, NULL if not found. + */ + template , + typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> + A* get() const { + return static_cast(get(_::type::id(world_))); + } - type(world_t *world, const type_t *t) - : m_world(world) - , m_type(t) { } + /** Get column size */ + size_t column_size(int32_t index) { + return ecs_table_get_column_size(table_, index); + } - /** Convert type to comma-separated string */ - flecs::string str() const { - return flecs::string(ecs_type_str(m_world, m_type)); + /** Get depth for given relationship. + * + * @param rel The relationship. + * @return The depth. + */ + int32_t depth(flecs::entity_t rel) { + return ecs_table_get_depth(world_, table_, rel); } - /** Return number of ids in type */ - int32_t count() const { - if (!m_type) { - return 0; - } - return m_type->count; + /** Get depth for given relationship. + * + * @tparam Rel The relationship. + * @return The depth. + */ + template + int32_t depth() { + return depth(_::type::id(world_)); } - /** Return pointer to array. */ - flecs::id_t* array() const { - if (!m_type) { - return nullptr; - } - return m_type->array; + /** Get table. + * + * @return The table. + */ + table_t* get_table() const { + return table_; } - /** Get id at specified index in type */ - flecs::id get(int32_t index) const { - ecs_assert(m_type != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(m_type->count > index, ECS_OUT_OF_RANGE, NULL); - if (!m_type) { - return flecs::id(); - } - return flecs::id(m_world, m_type->array[index]); + /* Implicit conversion to table_t */ + operator table_t*() const { + return table_; } - - flecs::id_t* begin() const { - return m_type->array; + +protected: + world_t *world_; + table_t *table_; +}; + +struct table_range : table { + table_range() + : table() + , offset_(0) + , count_(0) { } + + table_range(world_t *world, table_t *t, int32_t offset, int32_t count) + : table(world, t) + , offset_(offset) + , count_(count) { } + + int32_t offset() const { + return offset_; } - flecs::id_t* end() const { - return &m_type->array[m_type->count]; + int32_t count() const { + return count_; } - /** Implicit conversion to type_t */ - operator const type_t*() const { - return m_type; + /** Get pointer to component array by column index. + * + * @param index The column index. + * @return Pointer to the column, NULL if not a component. + */ + void* get_column(int32_t index) const override { + return ecs_table_get_column(table_, index, offset_); } + private: - world_t *m_world; - const type_t *m_type; + int32_t offset_ = 0; + int32_t count_ = 0; }; -/** #} */ +/** @} */ } /** - * @file addons/cpp/table.hpp - * @brief Direct access to table data. + * @file addons/cpp/utils/iterable.hpp + * @brief Base class for iterable objects, like queries. */ -#pragma once - namespace flecs { -/** - * @defgroup cpp_tables Tables - * @brief Table operations. - * - * \ingroup cpp_core - * @{ - */ +template +struct iter_iterable; -struct table { - table() : m_world(nullptr), m_table(nullptr) { } +template +struct page_iterable; - table(world_t *world, table_t *t) - : m_world(world) - , m_table(t) { } +template +struct worker_iterable; - virtual ~table() { } +template +struct iterable { - /** Convert table type to string. */ - flecs::string str() const { - return flecs::string(ecs_table_str(m_world, m_table)); + /** Each iterator. + * The "each" iterator accepts a function that is invoked for each matching + * entity. The following function signatures are valid: + * - func(flecs::entity e, Components& ...) + * - func(flecs::iter& it, size_t index, Components& ....) + * - func(Components& ...) + */ + template + void each(Func&& func) const { + ecs_iter_t it = this->get_iter(nullptr); + ecs_iter_next_action_t next = this->next_action(); + while (next(&it)) { + _::each_delegate(func).invoke(&it); + } } - /** Get table type. */ - flecs::type type() const { - return flecs::type(m_world, ecs_table_get_type(m_table)); + /** Run iterator. + * The "each" iterator accepts a function that is invoked once for a query + * with a valid iterator. The following signature is valid: + * - func(flecs::iter&) + */ + template + void run(Func&& func) const { + ecs_iter_t it = this->get_iter(nullptr); + _::run_delegate(func).invoke(&it); } - /** Get table count. */ - int32_t count() const { - return ecs_table_count(m_table); + template + flecs::entity find(Func&& func) const { + ecs_iter_t it = this->get_iter(nullptr); + ecs_iter_next_action_t next = this->next_action(); + + flecs::entity result; + while (!result && next(&it)) { + result = _::find_delegate(func).invoke(&it); + } + + if (result) { + ecs_iter_fini(&it); + } + + return result; } - /** Find type index for (component) id. + /** Create iterator. + * Create an iterator object that can be modified before iterating. + */ + iter_iterable iter(flecs::world_t *world = nullptr) const; + + /** Create iterator. + * Create an iterator object that can be modified before iterating. + */ + iter_iterable iter(flecs::iter& iter) const; + + /** Create iterator. + * Create an iterator object that can be modified before iterating. + */ + iter_iterable iter(flecs::entity e) const; + + /** Page iterator. + * Create an iterator that limits the returned entities with offset/limit. * - * @param id The (component) id. - * @return The index of the id in the table type, -1 if not found/ + * @param offset How many entities to skip. + * @param limit The maximum number of entities to return. + * @return Iterable that can be iterated with each/iter. */ - int32_t type_index(flecs::id_t id) const { - return ecs_table_get_type_index(m_world, m_table, id); - } + page_iterable page(int32_t offset, int32_t limit); - /** Find type index for type. + /** Worker iterator. + * Create an iterator that divides the number of matched entities across + * a number of resources. * - * @tparam T The type. - * @return True if the table has the type, false if not. + * @param index The index of the current resource. + * @param count The total number of resources to divide entities between. + * @return Iterable that can be iterated with each/iter. */ - template - int32_t type_index() const { - return type_index(_::cpp_type::id(m_world)); + worker_iterable worker(int32_t index, int32_t count); + + /** Return number of entities matched by iterable. */ + int32_t count() const { + return this->iter().count(); } - /** Find type index for pair. - * @param first First element of pair. - * @param second Second element of pair. - * @return True if the table has the pair, false if not. - */ - int32_t type_index(flecs::entity_t first, flecs::entity_t second) const { - return type_index(ecs_pair(first, second)); + /** Return whether iterable has any matches. */ + bool is_true() const { + return this->iter().is_true(); } - /** Find type index for pair. - * @tparam First First element of pair. - * @param second Second element of pair. - * @return True if the table has the pair, false if not. - */ - template - int32_t type_index(flecs::entity_t second) const { - return type_index(_::cpp_type::id(m_world), second); + /** Return first entity matched by iterable. */ + flecs::entity first() const { + return this->iter().first(); } - /** Find type index for pair. - * @tparam First First element of pair. - * @tparam Second Second element of pair. - * @return True if the table has the pair, false if not. - */ - template - int32_t type_index() const { - return type_index(_::cpp_type::id(m_world)); + iter_iterable set_var(int var_id, flecs::entity_t value) { + return this->iter().set_var(var_id, value); } - /** Find column index for (component) id. - * - * @param id The (component) id. - * @return The index of the id in the table type, -1 if not found/ - */ - int32_t column_index(flecs::id_t id) const { - return ecs_table_get_column_index(m_world, m_table, id); + iter_iterable set_var(const char *name, flecs::entity_t value) { + return this->iter().set_var(name, value); } - /** Find column index for type. - * - * @tparam T The type. - * @return True if the table has the type, false if not. - */ - template - int32_t column_index() const { - return column_index(_::cpp_type::id(m_world)); + iter_iterable set_var(const char *name, flecs::table_t *value) { + return this->iter().set_var(name, value); } - /** Find column index for pair. - * @param first First element of pair. - * @param second Second element of pair. - * @return True if the table has the pair, false if not. - */ - int32_t column_index(flecs::entity_t first, flecs::entity_t second) const { - return column_index(ecs_pair(first, second)); + iter_iterable set_var(const char *name, ecs_table_range_t value) { + return this->iter().set_var(name, value); } - /** Find column index for pair. - * @tparam First First element of pair. - * @param second Second element of pair. - * @return True if the table has the pair, false if not. - */ - template - int32_t column_index(flecs::entity_t second) const { - return column_index(_::cpp_type::id(m_world), second); + iter_iterable set_var(const char *name, flecs::table_range value) { + return this->iter().set_var(name, value); } - /** Find column index for pair. - * @tparam First First element of pair. - * @tparam Second Second element of pair. - * @return True if the table has the pair, false if not. - */ - template - int32_t column_index() const { - return column_index(_::cpp_type::id(m_world)); + // Limit results to tables with specified group id (grouped queries only) + iter_iterable set_group(uint64_t group_id) { + return this->iter().set_group(group_id); } - /** Test if table has (component) id. - * - * @param id The (component) id. - * @return True if the table has the id, false if not. - */ - bool has(flecs::id_t id) const { - return type_index(id) != -1; + // Limit results to tables with specified group id (grouped queries only) + template + iter_iterable set_group() { + return this->iter().template set_group(); } - /** Test if table has the type. - * - * @tparam T The type. - * @return True if the table has the type, false if not. - */ - template - bool has() const { - return type_index() != -1; + virtual ~iterable() { } +protected: + friend iter_iterable; + friend page_iterable; + friend worker_iterable; + + virtual ecs_iter_t get_iter(flecs::world_t *stage) const = 0; + virtual ecs_iter_next_action_t next_action() const = 0; +}; + +template +struct iter_iterable final : iterable { + template + iter_iterable(Iterable *it, flecs::world_t *world) + { + it_ = it->get_iter(world); + next_ = it->next_action(); + next_each_ = it->next_action(); + ecs_assert(next_ != nullptr, ECS_INTERNAL_ERROR, NULL); + ecs_assert(next_each_ != nullptr, ECS_INTERNAL_ERROR, NULL); } - /** Test if table has the pair. - * - * @param first First element of pair. - * @param second Second element of pair. - * @return True if the table has the pair, false if not. - */ - bool has(flecs::entity_t first, flecs::entity_t second) const { - return type_index(first, second) != -1; + iter_iterable& set_var(int var_id, flecs::entity_t value) { + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, 0); + ecs_iter_set_var(&it_, var_id, value); + return *this; } - /** Test if table has the pair. - * - * @tparam First First element of pair. - * @param second Second element of pair. - * @return True if the table has the pair, false if not. - */ - template - bool has(flecs::entity_t second) const { - return type_index(second) != -1; + iter_iterable& set_var(const char *name, flecs::entity_t value) { + ecs_query_iter_t *qit = &it_.priv_.iter.query; + int var_id = ecs_query_find_var(qit->query, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + ecs_iter_set_var(&it_, var_id, value); + return *this; } - /** Test if table has the pair. - * - * @tparam First First element of pair. - * @tparam Second Second element of pair. - * @return True if the table has the pair, false if not. - */ - template - bool has() const { - return type_index() != -1; + iter_iterable& set_var(const char *name, flecs::table_t *value) { + ecs_query_iter_t *qit = &it_.priv_.iter.query; + int var_id = ecs_query_find_var(qit->query, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + ecs_iter_set_var_as_table(&it_, var_id, value); + return *this; } - /** Get pointer to component array by column index. - * - * @param index The column index. - * @return Pointer to the column, NULL if not a component. - */ - virtual void* get_column(int32_t index) const { - return ecs_table_get_column(m_table, index, 0); + iter_iterable& set_var(const char *name, ecs_table_range_t value) { + ecs_query_iter_t *qit = &it_.priv_.iter.query; + int var_id = ecs_query_find_var(qit->query, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + ecs_iter_set_var_as_range(&it_, var_id, &value); + return *this; } - /** Get pointer to component array by component. - * - * @param id The component id. - * @return Pointer to the column, NULL if not found. - */ - void* get(flecs::id_t id) const { - int32_t index = column_index(id); - if (index == -1) { - return NULL; - } - return get_column(index); + iter_iterable& set_var(const char *name, flecs::table_range value) { + ecs_table_range_t range; + range.table = value.get_table(); + range.offset = value.offset(); + range.count = value.count(); + return set_var(name, range); } - /** Get pointer to component array by pair. - * - * @param first The first element of the pair. - * @param second The second element of the pair. - * @return Pointer to the column, NULL if not found. - */ - void* get(flecs::entity_t first, flecs::entity_t second) const { - return get(ecs_pair(first, second)); +# ifdef FLECS_JSON +/** + * @file addons/cpp/mixins/json/iterable.inl + * @brief JSON iterable mixin. + */ + +/** Serialize iterator result to JSON. + * + * @memberof flecs::iter + * @ingroup cpp_addons_json + */ +flecs::string to_json(flecs::iter_to_json_desc_t *desc = nullptr) { + char *json = ecs_iter_to_json(&it_, desc); + return flecs::string(json); +} + +# endif + + // Return total number of entities in result. + int32_t count() { + int32_t result = 0; + while (next_each_(&it_)) { + result += it_.count; + } + return result; } - /** Get pointer to component array by component. - * - * @tparam T The component. - * @return Pointer to the column, NULL if not found. - */ - template ::value > = 0> - T* get() const { - return static_cast(get(_::cpp_type::id(m_world))); + // Returns true if iterator yields at least once result. + bool is_true() { + bool result = next_each_(&it_); + if (result) { + ecs_iter_fini(&it_); + } + return result; } - /** Get pointer to component array by (enum) component. - * - * @tparam T The (enum) component. - * @return Pointer to the column, NULL if not found. - */ - template ::value > = 0> - T* get() const { - return static_cast(get(_::cpp_type::id(m_world))); + // Return first matching entity. + flecs::entity first() { + flecs::entity result; + if (next_each_(&it_) && it_.count) { + result = flecs::entity(it_.world, it_.entities[0]); + ecs_iter_fini(&it_); + } + return result; } - /** Get pointer to component array by component. - * - * @tparam T The component. - * @return Pointer to the column, NULL if not found. - */ - template , - if_t< flecs::is_pair::value > = 0> - A* get() const { - return static_cast(get(_::cpp_type::id(m_world))); + // Limit results to tables with specified group id (grouped queries only) + iter_iterable& set_group(uint64_t group_id) { + ecs_iter_set_group(&it_, group_id); + return *this; } - /** Get pointer to component array by pair. - * - * @tparam First The first element of the pair. - * @param second The second element of the pair. - * @return Pointer to the column, NULL if not found. - */ - template - First* get(flecs::entity_t second) const { - return static_cast(get(_::cpp_type::id(m_world), second)); + // Limit results to tables with specified group id (grouped queries only) + template + iter_iterable& set_group() { + ecs_iter_set_group(&it_, _::type().id(it_.real_world)); + return *this; } - /** Get pointer to component array by pair. - * - * @tparam First The first element of the pair. - * @tparam Second The second element of the pair. - * @return Pointer to the column, NULL if not found. - */ - template , - typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> - A* get() const { - return static_cast(get(_::cpp_type::id(m_world))); +protected: + ecs_iter_t get_iter(flecs::world_t *world) const override { + if (world) { + ecs_iter_t result = it_; + result.world = world; + return result; + } + return it_; } - /** Get column size */ - size_t column_size(int32_t index) { - return ecs_table_get_column_size(m_table, index); + ecs_iter_next_action_t next_action() const override { + return next_; } - /** Get depth for given relationship. - * - * @param rel The relationship. - * @return The depth. - */ - int32_t depth(flecs::entity_t rel) { - return ecs_table_get_depth(m_world, m_table, rel); +private: + ecs_iter_t it_; + ecs_iter_next_action_t next_; + ecs_iter_next_action_t next_each_; +}; + +template +iter_iterable iterable::iter(flecs::world_t *world) const +{ + return iter_iterable(this, world); +} + +template +iter_iterable iterable::iter(flecs::iter& it) const +{ + return iter_iterable(this, it.world()); +} + +template +iter_iterable iterable::iter(flecs::entity e) const +{ + return iter_iterable(this, e.world()); +} + +template +struct page_iterable final : iterable { + template + page_iterable(int32_t offset, int32_t limit, Iterable *it) + : offset_(offset) + , limit_(limit) + { + chain_it_ = it->get_iter(nullptr); } - /** Get depth for given relationship. - * - * @tparam Rel The relationship. - * @return The depth. - */ - template - int32_t depth() { - return depth(_::cpp_type::id(m_world)); +protected: + ecs_iter_t get_iter(flecs::world_t*) const { + return ecs_page_iter(&chain_it_, offset_, limit_); } - /* Implicit conversion to table_t */ - operator table_t*() const { - return m_table; + ecs_iter_next_action_t next_action() const { + return ecs_page_next; } -protected: - world_t *m_world; - table_t *m_table; +private: + ecs_iter_t chain_it_; + int32_t offset_; + int32_t limit_; }; -struct table_range : table { - table_range() - : table() - , m_offset(0) - , m_count(0) { } - - table_range(world_t *world, table_t *t, int32_t offset, int32_t count) - : table(world, t) - , m_offset(offset) - , m_count(count) { } +template +page_iterable iterable::page( + int32_t offset, + int32_t limit) +{ + return page_iterable(offset, limit, this); +} - int32_t offset() const { - return m_offset; +template +struct worker_iterable final : iterable { + worker_iterable(int32_t offset, int32_t limit, iterable *it) + : offset_(offset) + , limit_(limit) + { + chain_it_ = it->get_iter(nullptr); } - int32_t count() const { - return m_count; +protected: + ecs_iter_t get_iter(flecs::world_t*) const { + return ecs_worker_iter(&chain_it_, offset_, limit_); } - /** Get pointer to component array by column index. - * - * @param index The column index. - * @return Pointer to the column, NULL if not a component. - */ - void* get_column(int32_t index) const override { - return ecs_table_get_column(m_table, index, m_offset); + ecs_iter_next_action_t next_action() const { + return ecs_worker_next; } private: - int32_t m_offset = 0; - int32_t m_count = 0; + ecs_iter_t chain_it_; + int32_t offset_; + int32_t limit_; }; -/** @} */ +template +worker_iterable iterable::worker( + int32_t index, + int32_t count) +{ + return worker_iterable(index, count, this); +} } @@ -26681,57 +28543,57 @@ namespace flecs { inline flecs::entity id::entity() const { ecs_assert(!is_pair(), ECS_INVALID_OPERATION, NULL); ecs_assert(!flags(), ECS_INVALID_OPERATION, NULL); - return flecs::entity(m_world, m_id); + return flecs::entity(world_, id_); } inline flecs::entity id::flags() const { - return flecs::entity(m_world, m_id & ECS_ID_FLAGS_MASK); + return flecs::entity(world_, id_ & ECS_ID_FLAGS_MASK); } inline flecs::entity id::first() const { ecs_assert(is_pair(), ECS_INVALID_OPERATION, NULL); - flecs::entity_t e = ECS_PAIR_FIRST(m_id); - if (m_world) { - return flecs::entity(m_world, ecs_get_alive(m_world, e)); + flecs::entity_t e = ECS_PAIR_FIRST(id_); + if (world_) { + return flecs::entity(world_, ecs_get_alive(world_, e)); } else { return flecs::entity(e); } } inline flecs::entity id::second() const { - flecs::entity_t e = ECS_PAIR_SECOND(m_id); - if (m_world) { - return flecs::entity(m_world, ecs_get_alive(m_world, e)); + flecs::entity_t e = ECS_PAIR_SECOND(id_); + if (world_) { + return flecs::entity(world_, ecs_get_alive(world_, e)); } else { return flecs::entity(e); } } inline flecs::entity id::add_flags(flecs::id_t flags) const { - return flecs::entity(m_world, m_id | flags); + return flecs::entity(world_, id_ | flags); } inline flecs::entity id::remove_flags(flecs::id_t flags) const { (void)flags; - ecs_assert((m_id & ECS_ID_FLAGS_MASK) == flags, ECS_INVALID_PARAMETER, NULL); - return flecs::entity(m_world, m_id & ECS_COMPONENT_MASK); + ecs_assert((id_ & ECS_ID_FLAGS_MASK) == flags, ECS_INVALID_PARAMETER, NULL); + return flecs::entity(world_, id_ & ECS_COMPONENT_MASK); } inline flecs::entity id::remove_flags() const { - return flecs::entity(m_world, m_id & ECS_COMPONENT_MASK); + return flecs::entity(world_, id_ & ECS_COMPONENT_MASK); } inline flecs::entity id::remove_generation() const { - return flecs::entity(m_world, static_cast(m_id)); + return flecs::entity(world_, static_cast(id_)); } inline flecs::world id::world() const { - return flecs::world(m_world); + return flecs::world(world_); } inline flecs::entity id::type_id() const { - return flecs::entity(m_world, ecs_get_typeid(m_world, m_id)); + return flecs::entity(world_, ecs_get_typeid(world_, id_)); } @@ -26739,21 +28601,21 @@ inline flecs::entity id::type_id() const { template inline flecs::id world::id() const { - return flecs::id(m_world, _::cpp_type::id(m_world)); + return flecs::id(world_, _::type::id(world_)); } template inline flecs::id world::id(Args&&... args) const { - return flecs::id(m_world, FLECS_FWD(args)...); + return flecs::id(world_, FLECS_FWD(args)...); } template inline flecs::id world::pair() const { return flecs::id( - m_world, + world_, ecs_pair( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world))); + _::type::id(world_), + _::type::id(world_))); } template @@ -26762,9 +28624,9 @@ inline flecs::id world::pair(entity_t o) const { "cannot create nested pairs"); return flecs::id( - m_world, + world_, ecs_pair( - _::cpp_type::id(m_world), + _::type::id(world_), o)); } @@ -26773,7 +28635,7 @@ inline flecs::id world::pair(entity_t r, entity_t o) const { "cannot create nested pairs"); return flecs::id( - m_world, + world_, ecs_pair(r, o)); } @@ -26790,65 +28652,65 @@ namespace flecs { template flecs::entity ref::entity() const { - return flecs::entity(m_world, m_ref.entity); + return flecs::entity(world_, ref_.entity); } template -template ::value > > -inline Self& entity_builder::set(const Func& func) { - _::entity_with_delegate::invoke_get_mut( - this->m_world, this->m_id, func); +template +inline const Self& entity_builder::insert(const Func& func) const { + _::entity_with_delegate::invoke_ensure( + this->world_, this->id_, func); return to_base(); } template ::value > > const T* entity_view::get() const { - entity_t r = _::cpp_type::id(m_world); - entity_t c = ecs_get_target(m_world, m_id, r, 0); + entity_t r = _::type::id(world_); + entity_t c = ecs_get_target(world_, id_, r, 0); if (c) { // Get constant value from constant entity - const T* v = static_cast(ecs_get_id(m_world, c, r)); + const T* v = static_cast(ecs_get_id(world_, c, r)); ecs_assert(v != NULL, ECS_INTERNAL_ERROR, "missing enum constant value"); return v; } else { // If there is no matching pair for (r, *), try just r - return static_cast(ecs_get_id(m_world, m_id, r)); + return static_cast(ecs_get_id(world_, id_, r)); } } template inline flecs::entity entity_view::target(int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, m_id, _::cpp_type::id(m_world), index)); + return flecs::entity(world_, + ecs_get_target(world_, id_, _::type::id(world_), index)); } inline flecs::entity entity_view::target( flecs::entity_t relationship, int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, m_id, relationship, index)); + return flecs::entity(world_, + ecs_get_target(world_, id_, relationship, index)); } inline flecs::entity entity_view::target_for( flecs::entity_t relationship, flecs::id_t id) const { - return flecs::entity(m_world, - ecs_get_target_for_id(m_world, m_id, relationship, id)); + return flecs::entity(world_, + ecs_get_target_for_id(world_, id_, relationship, id)); } template inline flecs::entity entity_view::target_for(flecs::entity_t relationship) const { - return target_for(relationship, _::cpp_type::id(m_world)); + return target_for(relationship, _::type::id(world_)); } template inline flecs::entity entity_view::target_for(flecs::entity_t relationship) const { - return target_for(relationship, _::cpp_type::id(m_world)); + return target_for(relationship, _::type::id(world_)); } inline flecs::entity entity_view::parent() const { @@ -26858,37 +28720,37 @@ inline flecs::entity entity_view::parent() const { inline flecs::entity entity_view::mut(const flecs::world& stage) const { ecs_assert(!stage.is_readonly(), ECS_INVALID_PARAMETER, "cannot use readonly world/stage to create mutable handle"); - return flecs::entity(m_id).set_stage(stage.c_ptr()); + return flecs::entity(id_).set_stage(stage.c_ptr()); } inline flecs::entity entity_view::mut(const flecs::iter& it) const { ecs_assert(!it.world().is_readonly(), ECS_INVALID_PARAMETER, "cannot use iterator created for readonly world/stage to create mutable handle"); - return flecs::entity(m_id).set_stage(it.world().c_ptr()); + return flecs::entity(id_).set_stage(it.world().c_ptr()); } inline flecs::entity entity_view::mut(const flecs::entity_view& e) const { ecs_assert(!e.world().is_readonly(), ECS_INVALID_PARAMETER, "cannot use entity created for readonly world/stage to create mutable handle"); - return flecs::entity(m_id).set_stage(e.m_world); + return flecs::entity(id_).set_stage(e.world_); } inline flecs::entity entity_view::set_stage(world_t *stage) { - return flecs::entity(stage, m_id); + return flecs::entity(stage, id_); } inline flecs::type entity_view::type() const { - return flecs::type(m_world, ecs_get_type(m_world, m_id)); + return flecs::type(world_, ecs_get_type(world_, id_)); } inline flecs::table entity_view::table() const { - return flecs::table(m_world, ecs_get_table(m_world, m_id)); + return flecs::table(world_, ecs_get_table(world_, id_)); } inline flecs::table_range entity_view::range() const { - ecs_record_t *r = ecs_record_find(m_world, m_id); + ecs_record_t *r = ecs_record_find(world_, id_); if (r) { - return flecs::table_range(m_world, r->table, + return flecs::table_range(world_, r->table, ECS_RECORD_TO_ROW(r->row), 1); } return flecs::table_range(); @@ -26896,7 +28758,7 @@ inline flecs::table_range entity_view::range() const { template inline void entity_view::each(const Func& func) const { - const ecs_type_t *type = ecs_get_type(m_world, m_id); + const ecs_type_t *type = ecs_get_type(world_, id_); if (!type) { return; } @@ -26906,24 +28768,17 @@ inline void entity_view::each(const Func& func) const { for (int i = 0; i < count; i ++) { ecs_id_t id = ids[i]; - flecs::id ent(m_world, id); + flecs::id ent(world_, id); func(ent); - - // Union object is not stored in type, so handle separately - if (ECS_PAIR_FIRST(id) == EcsUnion) { - ent = flecs::id(m_world, ECS_PAIR_SECOND(id), - ecs_get_target(m_world, m_id, ECS_PAIR_SECOND(id), 0)); - func(ent); - } } } template inline void entity_view::each(flecs::id_t pred, flecs::id_t obj, const Func& func) const { flecs::world_t *real_world = const_cast( - ecs_get_world(m_world)); + ecs_get_world(world_)); - const ecs_table_t *table = ecs_get_table(m_world, m_id); + const ecs_table_t *table = ecs_get_table(world_, id_); if (!table) { return; } @@ -26943,7 +28798,7 @@ inline void entity_view::each(flecs::id_t pred, flecs::id_t obj, const Func& fun while (-1 != (cur = ecs_search_offset(real_world, table, cur, pattern, 0))) { - flecs::id ent(m_world, ids[cur]); + flecs::id ent(world_, ids[cur]); func(ent); cur ++; } @@ -26959,59 +28814,59 @@ inline void entity_view::each(const flecs::entity_view& rel, const Func& func) c template ::value > > inline bool entity_view::get(const Func& func) const { - return _::entity_with_delegate::invoke_get(m_world, m_id, func); + return _::entity_with_delegate::invoke_get(world_, id_, func); } inline flecs::entity entity_view::lookup(const char *path, bool search_path) const { - ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, "invalid lookup from null handle"); - auto id = ecs_lookup_path_w_sep(m_world, m_id, path, "::", "::", search_path); - return flecs::entity(m_world, id); + ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, "invalid lookup from null handle"); + auto id = ecs_lookup_path_w_sep(world_, id_, path, "::", "::", search_path); + return flecs::entity(world_, id); } inline flecs::entity entity_view::clone(bool copy_value, flecs::entity_t dst_id) const { if (!dst_id) { - dst_id = ecs_new_id(m_world); + dst_id = ecs_new(world_); } - flecs::entity dst = flecs::entity(m_world, dst_id); - ecs_clone(m_world, dst_id, m_id, copy_value); + flecs::entity dst = flecs::entity(world_, dst_id); + ecs_clone(world_, dst_id, id_, copy_value); return dst; } // Entity mixin implementation template inline flecs::entity world::entity(Args &&... args) const { - return flecs::entity(m_world, FLECS_FWD(args)...); + return flecs::entity(world_, FLECS_FWD(args)...); } template ::value >> inline flecs::id world::id(E value) const { - flecs::entity_t constant = enum_type(m_world).entity(value); - return flecs::id(m_world, constant); + flecs::entity_t constant = enum_type(world_).entity(value); + return flecs::id(world_, constant); } template ::value >> inline flecs::entity world::entity(E value) const { - flecs::entity_t constant = enum_type(m_world).entity(value); - return flecs::entity(m_world, constant); + flecs::entity_t constant = enum_type(world_).entity(value); + return flecs::entity(world_, constant); } template inline flecs::entity world::entity(const char *name) const { - return flecs::entity(m_world, - _::cpp_type::id_explicit(m_world, name, true, 0, false) ); + return flecs::entity(world_, + _::type::id_explicit(world_, name, true, 0, false) ); } template inline flecs::entity world::prefab(Args &&... args) const { - flecs::entity result = flecs::entity(m_world, FLECS_FWD(args)...); + flecs::entity result = flecs::entity(world_, FLECS_FWD(args)...); result.add(flecs::Prefab); return result; } template inline flecs::entity world::prefab(const char *name) const { - flecs::entity result = flecs::component(m_world, name, true); + flecs::entity result = flecs::component(world_, name, true); result.add(flecs::Prefab); return result; } @@ -27029,12 +28884,12 @@ namespace flecs { template inline flecs::component world::component(Args &&... args) const { - return flecs::component(m_world, FLECS_FWD(args)...); + return flecs::component(world_, FLECS_FWD(args)...); } template inline flecs::untyped_component world::component(Args &&... args) const { - return flecs::untyped_component(m_world, FLECS_FWD(args)...); + return flecs::untyped_component(world_, FLECS_FWD(args)...); } } // namespace flecs @@ -27066,41 +28921,41 @@ namespace flecs { namespace _ { template ::value > = 0> - static constexpr flecs::inout_kind_t type_to_inout() { + constexpr flecs::inout_kind_t type_to_inout() { return flecs::In; } template ::value > = 0> - static constexpr flecs::inout_kind_t type_to_inout() { - return flecs::Out; + constexpr flecs::inout_kind_t type_to_inout() { + return flecs::InOut; } template ::value || is_reference::value > = 0> - static constexpr flecs::inout_kind_t type_to_inout() { + constexpr flecs::inout_kind_t type_to_inout() { return flecs::InOutDefault; } template ::value > = 0> - static constexpr flecs::oper_kind_t type_to_oper() { + constexpr flecs::oper_kind_t type_to_oper() { return flecs::Optional; } template ::value > = 0> - static constexpr flecs::oper_kind_t type_to_oper() { + constexpr flecs::oper_kind_t type_to_oper() { return flecs::And; } template struct sig { sig(flecs::world_t *world) - : m_world(world) - , ids({ (_::cpp_type::id(world))... }) + : world_(world) + , ids({ (_::type>::id(world))... }) , inout ({ (type_to_inout())... }) , oper ({ (type_to_oper())... }) { } - flecs::world_t *m_world; + flecs::world_t *world_; flecs::array ids; flecs::array inout; flecs::array oper; @@ -27109,8 +28964,8 @@ namespace _ { void populate(const Builder& b) { size_t i = 0; for (auto id : ids) { - if (!(id & ECS_ID_FLAGS_MASK)) { - const flecs::type_info_t *ti = ecs_get_type_info(m_world, id); + if (!(id & ECS_ID_FLAGS_MASK)) { + const flecs::type_info_t *ti = ecs_get_type_info(world_, id); if (ti) { // Union relationships always return a value of type // flecs::entity_t which holds the target id of the @@ -27120,12 +28975,14 @@ namespace _ { // functions would accept a parameter of the component // type instead of flecs::entity_t, which would cause // an assert. - ecs_assert(!ti->size || !ecs_has_id(m_world, id, flecs::Union), + ecs_assert( + !ti->size || !ecs_has_id(world_, id, flecs::Union), ECS_INVALID_PARAMETER, - "use term() method to add union relationship"); + "use with() method to add union relationship"); } } - b->term(id).inout(inout[i]).oper(oper[i]); + + b->with(id).inout(inout[i]).oper(oper[i]); i ++; } } @@ -27138,89 +28995,34 @@ namespace _ { namespace flecs { -/** Term identifier builder. - * A term identifier describes a single identifier in a term. Identifier - * descriptions can reference entities by id, name or by variable, which means - * the entity will be resolved when the term is evaluated. - * - * \ingroup cpp_core_filters - */ -template -struct term_id_builder_i { - term_id_builder_i() : m_term_id(nullptr) { } - - virtual ~term_id_builder_i() { } - - /* The self flag indicates the term identifier itself is used */ - Base& self() { - this->assert_term_id(); - m_term_id->flags |= flecs::Self; - return *this; - } - - /* The up flag indicates that the term identifier may be substituted by - * traversing a relationship upwards. For example: substitute the identifier - * with its parent by traversing the ChildOf relationship. */ - Base& up(flecs::entity_t trav = 0) { - this->assert_term_id(); - m_term_id->flags |= flecs::Up; - if (trav) { - m_term_id->trav = trav; - } - return *this; - } - - template - Base& up() { - return this->up(_::cpp_type::id(this->world_v())); - } - - /* The cascade flag is like up, but returns results in breadth-first order. - * Only supported for flecs::query */ - Base& cascade(flecs::entity_t trav = 0) { - this->assert_term_id(); - m_term_id->flags |= flecs::Cascade; - if (trav) { - m_term_id->trav = trav; - } - return *this; - } - - template - Base& cascade() { - return this->cascade(_::cpp_type::id(this->world_v())); - } - - /* Use with cascade to iterate results in descending (bottom -> top) order */ - Base& desc() { - this->assert_term_id(); - m_term_id->flags |= flecs::Desc; - return *this; - } +/** Term identifier builder. + * A term identifier describes a single identifier in a term. Identifier + * descriptions can reference entities by id, name or by variable, which means + * the entity will be resolved when the term is evaluated. + * + * @ingroup cpp_core_queries + */ +template +struct term_ref_builder_i { + term_ref_builder_i() : term_ref_(nullptr) { } - /* The parent flag is short for up(flecs::ChildOf) */ - Base& parent() { - this->assert_term_id(); - m_term_id->flags |= flecs::Parent; - return *this; - } + virtual ~term_ref_builder_i() { } - /* Specify relationship to traverse, and flags to indicate direction */ - Base& trav(flecs::entity_t trav, flecs::flags32_t flags = 0) { - this->assert_term_id(); - m_term_id->trav = trav; - m_term_id->flags |= flags; + /* The self flag indicates the term identifier itself is used */ + Base& self() { + this->assert_term_ref(); + term_ref_->id |= flecs::Self; return *this; } /* Specify value of identifier by id */ Base& id(flecs::entity_t id) { - this->assert_term_id(); - m_term_id->id = id; + this->assert_term_ref(); + term_ref_->id = id; return *this; } - /* Specify value of identifier by id. Amost the same as id(entity), but this + /* Specify value of identifier by id. Almost the same as id(entity), but this * operation explicitly sets the flecs::IsEntity flag. This forces the id to * be interpreted as entity, whereas not setting the flag would implicitly * convert ids for builtin variables such as flecs::This to a variable. @@ -27229,46 +29031,45 @@ struct term_id_builder_i { * both id(entity_t) and id(const char*). */ Base& entity(flecs::entity_t entity) { - this->assert_term_id(); - m_term_id->flags = flecs::IsEntity; - m_term_id->id = entity; + this->assert_term_ref(); + term_ref_->id = entity | flecs::IsEntity; return *this; } /* Specify value of identifier by name */ Base& name(const char *name) { - this->assert_term_id(); - m_term_id->flags |= flecs::IsEntity; - m_term_id->name = const_cast(name); + this->assert_term_ref(); + term_ref_->id |= flecs::IsEntity; + term_ref_->name = const_cast(name); return *this; } /* Specify identifier is a variable (resolved at query evaluation time) */ Base& var(const char *var_name) { - this->assert_term_id(); - m_term_id->flags |= flecs::IsVariable; - m_term_id->name = const_cast(var_name); + this->assert_term_ref(); + term_ref_->id |= flecs::IsVariable; + term_ref_->name = const_cast(var_name); return *this; } /* Override term id flags */ Base& flags(flecs::flags32_t flags) { - this->assert_term_id(); - m_term_id->flags = flags; + this->assert_term_ref(); + term_ref_->id = flags; return *this; } - ecs_term_id_t *m_term_id; - + ecs_term_ref_t *term_ref_; + protected: virtual flecs::world_t* world_v() = 0; -private: - void assert_term_id() { - ecs_assert(m_term_id != NULL, ECS_INVALID_PARAMETER, - "no active term (call .term() first)"); + void assert_term_ref() { + ecs_assert(term_ref_ != NULL, ECS_INVALID_PARAMETER, + "no active term (call .with() first)"); } +private: operator Base&() { return *static_cast(this); } @@ -27277,11 +29078,11 @@ struct term_id_builder_i { /** Term builder interface. * A term is a single element of a query expression. * - * \ingroup cpp_addons_filter + * @ingroup cpp_core_queries */ template -struct term_builder_i : term_id_builder_i { - term_builder_i() : m_term(nullptr) { } +struct term_builder_i : term_ref_builder_i { + term_builder_i() : term_(nullptr) { } term_builder_i(ecs_term_t *term_ptr) { set_term(term_ptr); @@ -27294,7 +29095,7 @@ struct term_builder_i : term_id_builder_i { /* Call prior to setting values for src identifier */ Base& src() { this->assert_term(); - this->m_term_id = &m_term->src; + this->term_ref_ = &term_->src; return *this; } @@ -27303,7 +29104,7 @@ struct term_builder_i : term_id_builder_i { * populated as well). */ Base& first() { this->assert_term(); - this->m_term_id = &m_term->first; + this->term_ref_ = &term_->first; return *this; } @@ -27311,7 +29112,7 @@ struct term_builder_i : term_id_builder_i { * element of a pair. Requires that first() is populated as well. */ Base& second() { this->assert_term(); - this->m_term_id = &m_term->second; + this->term_ref_ = &term_->second; return *this; } @@ -27325,7 +29126,7 @@ struct term_builder_i : term_id_builder_i { /* Select src identifier, initialize it with id associated with type */ template Base& src() { - this->src(_::cpp_type::id(this->world_v())); + this->src(_::type::id(this->world_v())); return *this; } @@ -27352,7 +29153,7 @@ struct term_builder_i : term_id_builder_i { /* Select first identifier, initialize it with id associated with type */ template Base& first() { - this->first(_::cpp_type::id(this->world_v())); + this->first(_::type::id(this->world_v())); return *this; } @@ -27379,7 +29180,7 @@ struct term_builder_i : term_id_builder_i { /* Select second identifier, initialize it with id associated with type */ template Base& second() { - this->second(_::cpp_type::id(this->world_v())); + this->second(_::type::id(this->world_v())); return *this; } @@ -27396,17 +29197,76 @@ struct term_builder_i : term_id_builder_i { return *this; } - /** Set role of term. */ - Base& role(id_t role) { + /* The up flag indicates that the term identifier may be substituted by + * traversing a relationship upwards. For example: substitute the identifier + * with its parent by traversing the ChildOf relationship. */ + Base& up(flecs::entity_t trav = 0) { + this->assert_term_ref(); + ecs_check(this->term_ref_ != &term_->first, ECS_INVALID_PARAMETER, + "up traversal can only be applied to term source"); + ecs_check(this->term_ref_ != &term_->second, ECS_INVALID_PARAMETER, + "up traversal can only be applied to term source"); + this->term_ref_->id |= flecs::Up; + if (trav) { + term_->trav = trav; + } + error: + return *this; + } + + template + Base& up() { + return this->up(_::type::id(this->world_v())); + } + + /* The cascade flag is like up, but returns results in breadth-first order. + * Only supported for flecs::query */ + Base& cascade(flecs::entity_t trav = 0) { + this->assert_term_ref(); + this->up(); + this->term_ref_->id |= flecs::Cascade; + if (trav) { + term_->trav = trav; + } + return *this; + } + + template + Base& cascade() { + return this->cascade(_::type::id(this->world_v())); + } + + /* Use with cascade to iterate results in descending (bottom -> top) order */ + Base& desc() { + this->assert_term_ref(); + this->term_ref_->id |= flecs::Desc; + return *this; + } + + /* Same as up(), exists for backwards compatibility */ + Base& parent() { + return this->up(); + } + + /* Specify relationship to traverse, and flags to indicate direction */ + Base& trav(flecs::entity_t trav, flecs::flags32_t flags = 0) { + this->assert_term_ref(); + term_->trav = trav; + this->term_ref_->id |= flags; + return *this; + } + + /** Set id flags for term. */ + Base& id_flags(id_t flags) { this->assert_term(); - m_term->id_flags = role; + term_->id |= flags; return *this; } /** Set read/write access of term. */ Base& inout(flecs::inout_kind_t inout) { this->assert_term(); - m_term->inout = static_cast(inout); + term_->inout = static_cast(inout); return *this; } @@ -27420,8 +29280,8 @@ struct term_builder_i : term_id_builder_i { */ Base& inout_stage(flecs::inout_kind_t inout) { this->assert_term(); - m_term->inout = static_cast(inout); - if (m_term->oper != EcsNot) { + term_->inout = static_cast(inout); + if (term_->oper != EcsNot) { this->src().entity(0); } return *this; @@ -27442,7 +29302,7 @@ struct term_builder_i : term_id_builder_i { } /** Short for inout_stage(flecs::InOut). - * Use when system uses get_mut. + * Use when system uses ensure. */ Base& read_write() { return this->inout_stage(flecs::InOut); @@ -27471,7 +29331,7 @@ struct term_builder_i : term_id_builder_i { /** Set operator of term. */ Base& oper(flecs::oper_kind_t oper) { this->assert_term(); - m_term->oper = static_cast(oper); + term_->oper = static_cast(oper); return *this; } @@ -27513,48 +29373,48 @@ struct term_builder_i : term_id_builder_i { /** Match singleton. */ Base& singleton() { this->assert_term(); - ecs_assert(m_term->id || m_term->first.id, ECS_INVALID_PARAMETER, + ecs_assert(term_->id || term_->first.id, ECS_INVALID_PARAMETER, "no component specified for singleton"); - flecs::id_t sid = m_term->id; + flecs::id_t sid = term_->id; if (!sid) { - sid = m_term->first.id; + sid = term_->first.id; } ecs_assert(sid != 0, ECS_INVALID_PARAMETER, NULL); if (!ECS_IS_PAIR(sid)) { - m_term->src.id = sid; + term_->src.id = sid; } else { - m_term->src.id = ecs_pair_first(world(), sid); + term_->src.id = ecs_pair_first(world(), sid); } return *this; } - /* Filter terms are not triggered on by observers */ + /* Query terms are not triggered on by observers */ Base& filter() { - m_term->src.flags |= flecs::Filter; + term_->inout = EcsInOutFilter; return *this; } - ecs_term_t *m_term; + ecs_term_t *term_; protected: - virtual flecs::world_t* world_v() = 0; + virtual flecs::world_t* world_v() override = 0; void set_term(ecs_term_t *term) { - m_term = term; + term_ = term; if (term) { - this->m_term_id = &m_term->src; // default to subject + this->term_ref_ = &term_->src; // default to subject } else { - this->m_term_id = nullptr; + this->term_ref_ = nullptr; } } private: void assert_term() { - ecs_assert(m_term != NULL, ECS_INVALID_PARAMETER, - "no active term (call .term() first)"); + ecs_assert(term_ != NULL, ECS_INVALID_PARAMETER, + "no active term (call .with() first)"); } operator Base&() { @@ -27569,119 +29429,76 @@ namespace flecs { /** Class that describes a term. * - * \ingroup cpp_core_filters + * @ingroup cpp_core_queries */ struct term final : term_builder_i { term() : term_builder_i(&value) , value({}) - , m_world(nullptr) { value.move = true; } + , world_(nullptr) { } term(flecs::world_t *world_ptr) : term_builder_i(&value) , value({}) - , m_world(world_ptr) { value.move = true; } + , world_(world_ptr) { } term(flecs::world_t *world_ptr, ecs_term_t t) : term_builder_i(&value) , value({}) - , m_world(world_ptr) { + , world_(world_ptr) { value = t; - value.move = false; this->set_term(&value); } term(flecs::world_t *world_ptr, id_t id) : term_builder_i(&value) , value({}) - , m_world(world_ptr) { + , world_(world_ptr) { if (id & ECS_ID_FLAGS_MASK) { value.id = id; } else { value.first.id = id; } - value.move = false; this->set_term(&value); } term(flecs::world_t *world_ptr, entity_t r, entity_t o) : term_builder_i(&value) , value({}) - , m_world(world_ptr) { + , world_(world_ptr) { value.id = ecs_pair(r, o); - value.move = false; this->set_term(&value); } term(id_t id) : term_builder_i(&value) , value({}) - , m_world(nullptr) { + , world_(nullptr) { if (id & ECS_ID_FLAGS_MASK) { value.id = id; } else { value.first.id = id; } - value.move = true; } term(id_t r, id_t o) : term_builder_i(&value) , value({}) - , m_world(nullptr) { + , world_(nullptr) { value.id = ecs_pair(r, o); - value.move = true; } - term(const term& t) : term_builder_i(&value) { - m_world = t.m_world; - value = ecs_term_copy(&t.value); - this->set_term(&value); - } - - term(term&& t) : term_builder_i(&value) { - m_world = t.m_world; - value = ecs_term_move(&t.value); - t.reset(); - this->set_term(&value); - } - - term& operator=(const term& t) { - ecs_assert(m_world == t.m_world, ECS_INVALID_PARAMETER, NULL); - ecs_term_fini(&value); - value = ecs_term_copy(&t.value); - this->set_term(&value); - return *this; - } - - term& operator=(term&& t) { - ecs_assert(m_world == t.m_world, ECS_INVALID_PARAMETER, NULL); - ecs_term_fini(&value); - value = t.value; - this->set_term(&value); - t.reset(); - return *this; - } - - ~term() { - ecs_term_fini(&value); - } - void reset() { value = {}; this->set_term(nullptr); } - int finalize() { - return ecs_term_finalize(m_world, &value); - } - bool is_set() { return ecs_term_is_initialized(&value); } flecs::id id() { - return flecs::id(m_world, value.id); + return flecs::id(world_, value.id); } flecs::inout_kind_t inout() { @@ -27693,60 +29510,60 @@ struct term final : term_builder_i { } flecs::entity get_src() { - return flecs::entity(m_world, value.src.id); + return flecs::entity(world_, ECS_TERM_REF_ID(&value.src)); } flecs::entity get_first() { - return flecs::entity(m_world, value.first.id); + return flecs::entity(world_, ECS_TERM_REF_ID(&value.first)); } flecs::entity get_second() { - return flecs::entity(m_world, value.second.id); + return flecs::entity(world_, ECS_TERM_REF_ID(&value.second)); } - ecs_term_t move() { /* explicit move to ecs_term_t */ - return ecs_term_move(&value); + operator flecs::term_t() const { + return value; } - ecs_term_t value; + flecs::term_t value; protected: - flecs::world_t* world_v() override { return m_world; } + flecs::world_t* world_v() override { return world_; } private: - flecs::world_t *m_world; + flecs::world_t *world_; }; // Term mixin implementation template inline flecs::term world::term(Args &&... args) const { - return flecs::term(m_world, FLECS_FWD(args)...); + return flecs::term(world_, FLECS_FWD(args)...); } template inline flecs::term world::term() const { - return flecs::term(m_world, _::cpp_type::id(m_world)); + return flecs::term(world_, _::type::id(world_)); } template inline flecs::term world::term() const { - return flecs::term(m_world, ecs_pair( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world))); + return flecs::term(world_, ecs_pair( + _::type::id(world_), + _::type::id(world_))); } } /** - * @file addons/cpp/mixins/filter/impl.hpp - * @brief Filter implementation. + * @file addons/cpp/mixins/query/impl.hpp + * @brief Query implementation. */ #pragma once /** - * @file addons/cpp/mixins/filter/builder.hpp - * @brief Filter builder. + * @file addons/cpp/mixins/query/builder.hpp + * @brief Query builder. */ #pragma once @@ -27774,645 +29591,286 @@ struct builder : IBuilder public: builder(flecs::world_t *world) - : IBase(&m_desc) - , m_desc{} - , m_world(world) { } + : IBase(&desc_) + , desc_{} + , world_(world) { } builder(const builder& f) - : IBase(&m_desc, f.m_term_index) + : IBase(&desc_, f.term_index_) { - m_world = f.m_world; - m_desc = f.m_desc; + world_ = f.world_; + desc_ = f.desc_; } - builder(builder&& f) + builder(builder&& f) noexcept : builder(f) { } operator TDesc*() { - return &m_desc; + return &desc_; } T build() { - return T(m_world, *static_cast(this)); + return T(world_, *static_cast(this)); } protected: - flecs::world_t* world_v() override { return m_world; } - TDesc m_desc; - flecs::world_t *m_world; + flecs::world_t* world_v() override { return world_; } + TDesc desc_; + flecs::world_t *world_; }; #undef FLECS_TBUILDER #undef FLECS_IBUILDER -} // namespace _ -} // namespace flecs - -/** - * @file addons/cpp/mixins/filter/builder_i.hpp - * @brief Filter builder interface. - */ - -#pragma once - - -namespace flecs -{ - -/** Filter builder interface. - * - * \ingroup cpp_filters - */ -template -struct filter_builder_i : term_builder_i { - filter_builder_i(ecs_filter_desc_t *desc, int32_t term_index = 0) - : m_term_index(term_index) - , m_expr_count(0) - , m_desc(desc) { } - - Base& instanced() { - m_desc->instanced = true; - return *this; - } - - Base& filter_flags(ecs_flags32_t flags) { - m_desc->flags |= flags; - return *this; - } - - Base& expr(const char *expr) { - ecs_check(m_expr_count == 0, ECS_INVALID_OPERATION, - "filter_builder::expr() called more than once"); - m_desc->expr = expr; - m_expr_count ++; - - error: - return *this; - } - - /* With/without shorthand notation. */ - - template - Base& with(Args&&... args) { - return this->term(FLECS_FWD(args)...).inout_none(); - } - - template - Base& with(Args&&... args) { - return this->term(FLECS_FWD(args)...).inout_none(); - } - - template - Base& with() { - return this->term().inout_none(); - } - - template - Base& without(Args&&... args) { - return this->term(FLECS_FWD(args)...).not_(); - } - - template - Base& without(Args&&... args) { - return this->term(FLECS_FWD(args)...).not_(); - } - - template - Base& without() { - return this->term().not_(); - } - - /* Write/read shorthand notation */ - - Base& write() { - term_builder_i::write(); - return *this; - } - - template - Base& write(Args&&... args) { - return this->term(FLECS_FWD(args)...).write(); - } - - template - Base& write(Args&&... args) { - return this->term(FLECS_FWD(args)...).write(); - } - - template - Base& write() { - return this->term().write(); - } - - Base& read() { - term_builder_i::read(); - return *this; - } - - template - Base& read(Args&&... args) { - return this->term(FLECS_FWD(args)...).read(); - } - - template - Base& read(Args&&... args) { - return this->term(FLECS_FWD(args)...).read(); - } - - template - Base& read() { - return this->term().read(); - } - - /* Scope_open/scope_close shorthand notation. */ - Base& scope_open() { - return this->with(flecs::ScopeOpen).entity(0); - } - - Base& scope_close() { - return this->with(flecs::ScopeClose).entity(0); - } - - /* Term notation for more complex query features */ - - Base& term() { - if (this->m_term) { - ecs_check(ecs_term_is_initialized(this->m_term), - ECS_INVALID_OPERATION, - "filter_builder::term() called without initializing term"); - } - - if (m_term_index >= FLECS_TERM_DESC_MAX) { - if (m_term_index == FLECS_TERM_DESC_MAX) { - m_desc->terms_buffer = ecs_os_calloc_n( - ecs_term_t, m_term_index + 1); - ecs_os_memcpy_n(m_desc->terms_buffer, m_desc->terms, - ecs_term_t, m_term_index); - ecs_os_memset_n(m_desc->terms, 0, - ecs_term_t, FLECS_TERM_DESC_MAX); - } else { - m_desc->terms_buffer = ecs_os_realloc_n(m_desc->terms_buffer, - ecs_term_t, m_term_index + 1); - } - - m_desc->terms_buffer_count = m_term_index + 1; - - this->set_term(&m_desc->terms_buffer[m_term_index]); - } else { - this->set_term(&m_desc->terms[m_term_index]); - } - - m_term_index ++; - - error: - return *this; - } - - Base& term_at(int32_t term_index) { - ecs_assert(term_index > 0, ECS_INVALID_PARAMETER, NULL); - int32_t prev_index = m_term_index; - m_term_index = term_index - 1; - this->term(); - m_term_index = prev_index; - ecs_assert(ecs_term_is_initialized(this->m_term), - ECS_INVALID_PARAMETER, NULL); - return *this; - } - - Base& arg(int32_t term_index) { - return this->term_at(term_index); - } - - template - Base& term() { - this->term(); - *this->m_term = flecs::term(_::cpp_type::id(this->world_v())).move(); - this->m_term->inout = static_cast( - _::type_to_inout()); - return *this; - } - - Base& term(id_t id) { - this->term(); - *this->m_term = flecs::term(id).move(); - return *this; - } - - Base& term(const char *name) { - this->term(); - *this->m_term = flecs::term().first(name).move(); - return *this; - } - - Base& term(const char *first, const char *second) { - this->term(); - *this->m_term = flecs::term().first(first).second(second).move(); - return *this; - } - - Base& term(entity_t r, entity_t o) { - this->term(); - *this->m_term = flecs::term(r, o).move(); - return *this; - } - - Base& term(entity_t r, const char *o) { - this->term(); - *this->m_term = flecs::term(r).second(o).move(); - return *this; - } - - template - Base& term(id_t o) { - return this->term(_::cpp_type::id(this->world_v()), o); - } - - template - Base& term(const char *second) { - return this->term(_::cpp_type::id(this->world_v())).second(second); - } - - template - Base& term() { - return this->term(_::cpp_type::id(this->world_v())); - } - - template ::value > = 0> - Base& term(E value) { - flecs::entity_t r = _::cpp_type::id(this->world_v()); - auto o = enum_type(this->world_v()).entity(value); - return this->term(r, o); - } - - Base& term(flecs::term& term) { - this->term(); - *this->m_term = term.move(); - return *this; - } - - Base& term(flecs::term&& term) { - this->term(); - *this->m_term = term.move(); - return *this; - } - -protected: - virtual flecs::world_t* world_v() = 0; - int32_t m_term_index; - int32_t m_expr_count; - -private: - operator Base&() { - return *static_cast(this); - } - - ecs_filter_desc_t *m_desc; -}; +} // namespace _ +} // namespace flecs -} +/** + * @file addons/cpp/mixins/query/builder_i.hpp + * @brief Query builder interface. + */ +#pragma once -namespace flecs { -namespace _ { - template - using filter_builder_base = builder< - filter, ecs_filter_desc_t, filter_builder, - filter_builder_i, Components ...>; -} -/** Filter builder. +namespace flecs +{ + +/** Query builder interface. * - * \ingroup cpp_filters + * @ingroup cpp_core_queries */ -template -struct filter_builder final : _::filter_builder_base { - filter_builder(flecs::world_t* world, const char *name = nullptr) - : _::filter_builder_base(world) - { - _::sig(world).populate(this); - if (name != nullptr) { - ecs_entity_desc_t entity_desc = {}; - entity_desc.name = name; - entity_desc.sep = "::"; - entity_desc.root_sep = "::"; - this->m_desc.entity = ecs_entity_init(world, &entity_desc); - } - } +template +struct query_builder_i : term_builder_i { + query_builder_i(ecs_query_desc_t *desc, int32_t term_index = 0) + : term_index_(term_index) + , expr_count_(0) + , desc_(desc) { } - template - void each(Func&& func) { - this->build().each(FLECS_FWD(func)); + Base& query_flags(ecs_flags32_t flags) { + desc_->flags |= flags; + return *this; } -}; -} + Base& cache_kind(query_cache_kind_t kind) { + desc_->cache_kind = static_cast(kind); + return *this; + } + Base& cached() { + return cache_kind(flecs::QueryCacheAuto); + } -namespace flecs -{ + Base& expr(const char *expr) { + ecs_check(expr_count_ == 0, ECS_INVALID_OPERATION, + "query_builder::expr() called more than once"); + desc_->expr = expr; + expr_count_ ++; -struct filter_base { - filter_base() - : m_world(nullptr) - , m_filter({}) - , m_filter_ptr(nullptr) { } - - filter_base(world_t *world, const ecs_filter_t *filter) - : m_world(world) - , m_filter({}) - , m_filter_ptr(filter) { } - - filter_base(world_t *world, ecs_filter_t *filter) - : m_world(world) - , m_filter_ptr(&m_filter) { - ecs_filter_move(&m_filter, filter); - } + error: + return *this; + } - filter_base(world_t *world, ecs_filter_desc_t *desc) - : m_world(world) - { - desc->storage = &m_filter; + /* With methods */ - if (ecs_filter_init(world, desc) == NULL) { - ecs_abort(ECS_INVALID_PARAMETER, NULL); + template + Base& with() { + this->term(); + *this->term_ = flecs::term(_::type::id(this->world_v())); + this->term_->inout = static_cast( + _::type_to_inout()); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); } + return *this; + } - if (desc->terms_buffer) { - ecs_os_free(desc->terms_buffer); + Base& with(id_t id) { + this->term(); + *this->term_ = flecs::term(id); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); } - - m_filter_ptr = &m_filter; + return *this; } - filter_base(const filter_base& obj) { - this->m_world = obj.m_world; - if (obj.m_filter_ptr) { - this->m_filter_ptr = &this->m_filter; - } else { - this->m_filter_ptr = nullptr; + Base& with(const char *name) { + this->term(); + *this->term_ = flecs::term().first(name); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); } - ecs_filter_copy(&m_filter, &obj.m_filter); + return *this; } - filter_base& operator=(const filter_base& obj) { - this->m_world = obj.m_world; - if (obj.m_filter_ptr) { - this->m_filter_ptr = &this->m_filter; - } else { - this->m_filter_ptr = nullptr; + Base& with(const char *first, const char *second) { + this->term(); + *this->term_ = flecs::term().first(first).second(second); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); } - ecs_filter_copy(&m_filter, &obj.m_filter); - return *this; + return *this; } - filter_base(filter_base&& obj) { - this->m_world = obj.m_world; - if (obj.m_filter_ptr) { - this->m_filter_ptr = &this->m_filter; - } else { - this->m_filter_ptr = nullptr; + Base& with(entity_t r, entity_t o) { + this->term(); + *this->term_ = flecs::term(r, o); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); } - ecs_filter_move(&m_filter, &obj.m_filter); + return *this; } - filter_base& operator=(filter_base&& obj) { - this->m_world = obj.m_world; - if (obj.m_filter_ptr) { - this->m_filter_ptr = &this->m_filter; - } else { - this->m_filter_ptr = nullptr; + Base& with(entity_t r, const char *o) { + this->term(); + *this->term_ = flecs::term(r).second(o); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); } - ecs_filter_move(&m_filter, &obj.m_filter); - return *this; + return *this; } - flecs::entity entity() { - return flecs::entity(m_world, ecs_get_entity(m_filter_ptr)); + Base& with(const char *r, entity_t o) { + this->term(); + *this->term_ = flecs::term().first(r).second(o); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; } - operator const flecs::filter_t*() const { - return m_filter_ptr; + template + Base& with(id_t o) { + return this->with(_::type::id(this->world_v()), o); } - /** Free the filter. - */ - ~filter_base() { - if ((&m_filter == m_filter_ptr) && m_filter_ptr) { - ecs_filter_fini(&m_filter); - } + template + Base& with(const char *second) { + return this->with(_::type::id(this->world_v())).second(second); } - template - void each_term(const Func& func) { - for (int i = 0; i < m_filter_ptr->term_count; i ++) { - flecs::term t(m_world, m_filter_ptr->terms[i]); - func(t); - t.reset(); // prevent freeing resources - } + template + Base& with() { + return this->with(_::type::id(this->world_v())); } - flecs::term term(int32_t index) { - return flecs::term(m_world, m_filter_ptr->terms[index]); + template ::value > = 0> + Base& with(E value) { + flecs::entity_t r = _::type::id(this->world_v()); + auto o = enum_type(this->world_v()).entity(value); + return this->with(r, o); } - int32_t field_count() { - return m_filter_ptr->term_count; + Base& with(flecs::term& term) { + this->term(); + *this->term_ = term; + return *this; } - flecs::string str() { - char *result = ecs_filter_str(m_world, m_filter_ptr); - return flecs::string(result); + Base& with(flecs::term&& term) { + this->term(); + *this->term_ = term; + return *this; } - operator filter<>() const; - -protected: - world_t *m_world = nullptr; - filter_t m_filter = ECS_FILTER_INIT; - const filter_t *m_filter_ptr; -}; - -template -struct filter : filter_base, iterable { -private: - using Terms = typename _::term_ptrs::array; - -public: - using filter_base::filter_base; + /* Without methods, shorthand for .with(...).not_(). */ - filter() : filter_base() { } // necessary not to confuse msvc + template + Base& without(Args&&... args) { + return this->with(FLECS_FWD(args)...).not_(); + } - filter(const filter& obj) : filter_base(obj) { } + template + Base& without(Args&&... args) { + return this->with(FLECS_FWD(args)...).not_(); + } - filter& operator=(const filter& obj) { - filter_base::operator=(obj); - return *this; + template + Base& without() { + return this->with().not_(); } - filter(filter&& obj) : filter_base(FLECS_MOV(obj)) { } + /* Write/read methods */ - filter& operator=(filter&& obj) { - filter_base::operator=(FLECS_FWD(obj)); + Base& write() { + term_builder_i::write(); return *this; } -private: - ecs_iter_t get_iter(flecs::world_t *world) const override { - if (!world) { - world = m_world; - } - return ecs_filter_iter(world, m_filter_ptr); + template + Base& write(Args&&... args) { + return this->with(FLECS_FWD(args)...).write(); } - ecs_iter_next_action_t next_action() const override { - return ecs_filter_next; + template + Base& write(Args&&... args) { + return this->with(FLECS_FWD(args)...).write(); } - ecs_iter_next_action_t next_each_action() const override { - return ecs_filter_next_instanced; + template + Base& write() { + return this->with().write(); } -}; - -// World mixin implementation -template -inline flecs::filter world::filter(Args &&... args) const { - return flecs::filter_builder(m_world, FLECS_FWD(args)...) - .build(); -} - -template -inline flecs::filter_builder world::filter_builder(Args &&... args) const { - return flecs::filter_builder(m_world, FLECS_FWD(args)...); -} - -// world::each -namespace _ { - -// Each with entity parameter -template -struct filter_delegate_w_ent; -template -struct filter_delegate_w_ent > -{ - filter_delegate_w_ent(const flecs::world& world, Func&& func) { - auto f = world.filter(); - f.each(FLECS_MOV(func)); + Base& read() { + term_builder_i::read(); + return *this; } -}; - -// Each without entity parameter -template -struct filter_delegate_no_ent; -template -struct filter_delegate_no_ent > -{ - filter_delegate_no_ent(const flecs::world& world, Func&& func) { - auto f = world.filter(); - f.each(FLECS_MOV(func)); + template + Base& read(Args&&... args) { + return this->with(FLECS_FWD(args)...).read(); } -}; -// Switch between function with & without entity parameter -template -struct filter_delegate; - -template -struct filter_delegate, flecs::entity>::value> > { - filter_delegate(const flecs::world& world, Func&& func) { - filter_delegate_w_ent>(world, FLECS_MOV(func)); + template + Base& read(Args&&... args) { + return this->with(FLECS_FWD(args)...).read(); } -}; -template -struct filter_delegate, flecs::entity>::value> > { - filter_delegate(const flecs::world& world, Func&& func) { - filter_delegate_no_ent>(world, FLECS_MOV(func)); + template + Base& read() { + return this->with().read(); } -}; - -} - -template -inline void world::each(Func&& func) const { - _::filter_delegate f_delegate(*this, FLECS_MOV(func)); -} - -template -inline void world::each(Func&& func) const { - ecs_term_t t = {}; - t.id = _::cpp_type::id(); - ecs_iter_t it = ecs_term_iter(m_world, &t); - while (ecs_term_next(&it)) { - _::each_delegate(func).invoke(&it); + /* Scope_open/scope_close shorthand notation. */ + Base& scope_open() { + return this->with(flecs::ScopeOpen).entity(0); } -} - -template -inline void world::each(flecs::id_t term_id, Func&& func) const { - ecs_term_t t = {}; - t.id = term_id; - ecs_iter_t it = ecs_term_iter(m_world, &t); - while (ecs_term_next(&it)) { - _::each_delegate(func).invoke(&it); + Base& scope_close() { + return this->with(flecs::ScopeClose).entity(0); } -} - -// filter_base implementation -inline filter_base::operator flecs::filter<> () const { - flecs::filter<> f; - ecs_filter_copy(&f.m_filter, &this->m_filter); - f.m_filter_ptr = &f.m_filter; - f.m_world = this->m_world; - return f; -} - -} - -/** - * @file addons/cpp/mixins/query/impl.hpp - * @brief Query implementation. - */ - -#pragma once - -/** - * @file addons/cpp/mixins/query/builder.hpp - * @brief Query builder. - */ - -#pragma once -/** - * @file addons/cpp/mixins/query/builder_i.hpp - * @brief Query builder interface. - */ + /* Term notation for more complex query features */ -#pragma once + Base& term() { + if (this->term_) { + ecs_check(ecs_term_is_initialized(this->term_), + ECS_INVALID_OPERATION, + "query_builder::term() called without initializing term"); + } + ecs_check(term_index_ < FLECS_TERM_COUNT_MAX, + ECS_INVALID_PARAMETER, "maximum number of terms exceeded"); -namespace flecs { + this->set_term(&desc_->terms[term_index_]); -/** Query builder interface. - * - * \ingroup cpp_core_queries - */ -template -struct query_builder_i : filter_builder_i { -private: - using BaseClass = filter_builder_i; + term_index_ ++; -public: - query_builder_i() - : BaseClass(nullptr) - , m_desc(nullptr) { } + error: + return *this; + } - query_builder_i(ecs_query_desc_t *desc, int32_t term_index = 0) - : BaseClass(&desc->filter, term_index) - , m_desc(desc) { } + Base& term_at(int32_t term_index) { + ecs_assert(term_index >= 0, ECS_INVALID_PARAMETER, NULL); + int32_t prev_index = term_index_; + term_index_ = term_index; + this->term(); + term_index_ = prev_index; + ecs_assert(ecs_term_is_initialized(this->term_), + ECS_INVALID_PARAMETER, NULL); + return *this; + } /** Sort the output of a query. * This enables sorting of entities across matched tables. As a result of this @@ -28435,7 +29893,7 @@ struct query_builder_i : filter_builder_i { template Base& order_by(int(*compare)(flecs::entity_t, const T*, flecs::entity_t, const T*)) { ecs_order_by_action_t cmp = reinterpret_cast(compare); - return this->order_by(_::cpp_type::id(this->world_v()), cmp); + return this->order_by(_::type::id(this->world_v()), cmp); } /** Sort the output of a query. @@ -28445,13 +29903,13 @@ struct query_builder_i : filter_builder_i { * @param compare The compare function used to sort the components. */ Base& order_by(flecs::entity_t component, int(*compare)(flecs::entity_t, const void*, flecs::entity_t, const void*)) { - m_desc->order_by = reinterpret_cast(compare); - m_desc->order_by_component = component; + desc_->order_by_callback = reinterpret_cast(compare); + desc_->order_by = component; return *this; } /** Group and sort matched tables. - * Similar yo ecs_query_order_by, but instead of sorting individual entities, this + * Similar to ecs_query_order_by(), but instead of sorting individual entities, this * operation only sorts matched tables. This can be useful of a query needs to * enforce a certain iteration order upon the tables it is iterating, for * example by giving a certain component or tag a higher priority. @@ -28470,7 +29928,7 @@ struct query_builder_i : filter_builder_i { template Base& group_by(uint64_t(*group_by_action)(flecs::world_t*, flecs::table_t *table, flecs::id_t id, void* ctx)) { ecs_group_by_action_t action = reinterpret_cast(group_by_action); - return this->group_by(_::cpp_type::id(this->world_v()), action); + return this->group_by(_::type::id(this->world_v()), action); } /** Group and sort matched tables. @@ -28480,8 +29938,8 @@ struct query_builder_i : filter_builder_i { * @param group_by_action Callback that determines group id for table. */ Base& group_by(flecs::entity_t component, uint64_t(*group_by_action)(flecs::world_t*, flecs::table_t *table, flecs::id_t id, void* ctx)) { - m_desc->group_by = reinterpret_cast(group_by_action); - m_desc->group_by_id = component; + desc_->group_by_callback = reinterpret_cast(group_by_action); + desc_->group_by = component; return *this; } @@ -28492,7 +29950,7 @@ struct query_builder_i : filter_builder_i { */ template Base& group_by() { - return this->group_by(_::cpp_type::id(this->world_v()), nullptr); + return this->group_by(_::type::id(this->world_v()), nullptr); } /** Group and sort matched tables. @@ -28510,37 +29968,36 @@ struct query_builder_i : filter_builder_i { * @param ctx_free Function to cleanup context (called when query is deleted). */ Base& group_by_ctx(void *ctx, ecs_ctx_free_t ctx_free = nullptr) { - m_desc->group_by_ctx = ctx; - m_desc->group_by_ctx_free = ctx_free; + desc_->group_by_ctx = ctx; + desc_->group_by_ctx_free = ctx_free; return *this; } /** Specify on_group_create action. */ Base& on_group_create(ecs_group_create_action_t action) { - m_desc->on_group_create = action; + desc_->on_group_create = action; return *this; } /** Specify on_group_delete action. */ Base& on_group_delete(ecs_group_delete_action_t action) { - m_desc->on_group_delete = action; + desc_->on_group_delete = action; return *this; } - /** Specify parent query (creates subquery) */ - Base& observable(const query_base& parent); - protected: - virtual flecs::world_t* world_v() = 0; + virtual flecs::world_t* world_v() override = 0; + int32_t term_index_; + int32_t expr_count_; private: operator Base&() { return *static_cast(this); } - ecs_query_desc_t *m_desc; + ecs_query_desc_t *desc_; }; } @@ -28556,10 +30013,17 @@ namespace _ { /** Query builder. * - * \ingroup cpp_core_queries + * @ingroup cpp_core_queries */ template struct query_builder final : _::query_builder_base { + query_builder(flecs::world_t* world, flecs::entity query_entity) + : _::query_builder_base(world) + { + _::sig(world).populate(this); + this->desc_.entity = query_entity.id(); + } + query_builder(flecs::world_t* world, const char *name = nullptr) : _::query_builder_base(world) { @@ -28569,45 +30033,110 @@ struct query_builder final : _::query_builder_base { entity_desc.name = name; entity_desc.sep = "::"; entity_desc.root_sep = "::"; - this->m_desc.filter.entity = ecs_entity_init(world, &entity_desc); + this->desc_.entity = ecs_entity_init(world, &entity_desc); + } + } + + template + void each(Func&& func) { + this->build().each(FLECS_FWD(func)); + } +}; + +} + + +namespace flecs +{ + +struct query_base { + query_base() { } + + query_base(query_t *q) + : query_(q) { + flecs_poly_claim(q); + } + + query_base(const query_t *q) + : query_(ECS_CONST_CAST(query_t*, q)) { + flecs_poly_claim(q); + } + + query_base(world_t *world, ecs_query_desc_t *desc) { + if (desc->entity && desc->terms[0].id == 0) { + const flecs::Poly *query_poly = ecs_get_pair( + world, desc->entity, EcsPoly, EcsQuery); + if (query_poly) { + query_ = static_cast(query_poly->poly); + flecs_poly_claim(query_); + return; + } } + + query_ = ecs_query_init(world, desc); } -}; -} + query_base(const query_base& obj) { + this->query_ = obj.query_; + flecs_poly_claim(this->query_); + } + query_base& operator=(const query_base& obj) { + this->query_ = obj.query_; + flecs_poly_claim(this->query_); + return *this; + } -namespace flecs { + query_base(query_base&& obj) noexcept { + this->query_ = obj.query_; + obj.query_ = nullptr; + } -//////////////////////////////////////////////////////////////////////////////// -//// Persistent queries -//////////////////////////////////////////////////////////////////////////////// + query_base& operator=(query_base&& obj) noexcept { + this->query_ = obj.query_; + obj.query_ = nullptr; + return *this; + } -struct query_base { - query_base() - : m_world(nullptr) - , m_query(nullptr) { } - - query_base(world_t *world, query_t *query = nullptr) - : m_world(world) - , m_query(query) { } + flecs::entity entity() { + return flecs::entity(query_->world, query_->entity); + } - query_base(world_t *world, ecs_query_desc_t *desc) - : m_world(world) - { - m_query = ecs_query_init(world, desc); + const flecs::query_t* c_ptr() const { + return query_; + } - if (!m_query) { - ecs_abort(ECS_INVALID_PARAMETER, NULL); - } + operator const flecs::query_t*() const { + return query_; + } - if (desc->filter.terms_buffer) { - ecs_os_free(desc->filter.terms_buffer); - } + operator bool() const { + return query_ != nullptr; } - operator query_t*() const { - return m_query; + /** Free persistent query. + * A persistent query is a query that is associated with an entity, such as + * system queries and named queries. Persistent queries must be deleted with + * destruct(), or will be deleted automatically at world cleanup. + */ + void destruct() { + ecs_assert(query_->entity != 0, ECS_INVALID_OPERATION, "destruct() " + "should only be called on queries associated with entities"); + ecs_query_fini(query_); + query_ = nullptr; + } + + ~query_base() { + /* Only free if query is not associated with entity, such as system + * queries and named queries. Named queries have to be either explicitly + * deleted with the .destruct() method, or will be deleted when the + * world is deleted. */ + if (query_ && !query_->entity) { + if (!flecs_poly_release(query_)) { + ecs_query_fini(query_); + query_ = nullptr; + } + } } /** Returns whether the query data changed since the last iteration. @@ -28620,18 +30149,7 @@ struct query_base { * @return true if entities changed, otherwise false. */ bool changed() const { - return ecs_query_changed(m_query, 0); - } - - /** Returns whether query is orphaned. - * When the parent query of a subquery is deleted, it is left in an orphaned - * state. The only valid operation on an orphaned query is deleting it. Only - * subqueries can be orphaned. - * - * @return true if query is orphaned, otherwise false. - */ - bool orphaned() const { - return ecs_query_orphaned(m_query); + return ecs_query_changed(query_); } /** Get info for group. @@ -28640,7 +30158,7 @@ struct query_base { * @return The group info. */ const flecs::query_group_info_t* group_info(uint64_t group_id) const { - return ecs_query_get_group_info(m_query, group_id); + return ecs_query_get_group_info(query_, group_id); } /** Get context for group. @@ -28657,105 +30175,186 @@ struct query_base { } } - /** Free the query. - */ - void destruct() { - ecs_query_fini(m_query); - m_world = nullptr; - m_query = nullptr; + template + void each_term(const Func& func) { + for (int i = 0; i < query_->term_count; i ++) { + flecs::term t(query_->world, query_->terms[i]); + func(t); + t.reset(); // prevent freeing resources + } } - template - void each_term(const Func& func) const { - this->filter().each_term(func); + flecs::term term(int32_t index) { + return flecs::term(query_->world, query_->terms[index]); } - filter_base filter() const { - return filter_base(m_world, ecs_query_get_filter(m_query)); + int32_t term_count() { + return query_->term_count; } - flecs::term term(int32_t index) const { - const ecs_filter_t *f = ecs_query_get_filter(m_query); - ecs_assert(f != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs::term(m_world, f->terms[index]); + int32_t field_count() { + return query_->field_count; } - int32_t field_count() const { - const ecs_filter_t *f = ecs_query_get_filter(m_query); - return f->term_count; + int32_t find_var(const char *name) { + return ecs_query_find_var(query_, name); } - flecs::string str() const { - const ecs_filter_t *f = ecs_query_get_filter(m_query); - char *result = ecs_filter_str(m_world, f); + flecs::string str() { + char *result = ecs_query_str(query_); return flecs::string(result); } - flecs::entity entity() const { - return flecs::entity(m_world, ecs_get_entity(m_query)); + /** Returns a string representing the query plan. + * This can be used to analyze the behavior & performance of the query. + * @see ecs_query_plan + */ + flecs::string plan() const { + char *result = ecs_query_plan(query_); + return flecs::string(result); } - + operator query<>() const; protected: - world_t *m_world; - query_t *m_query; + query_t *query_ = nullptr; }; template -struct query final : query_base, iterable { +struct query : query_base, iterable { +private: + using Fields = typename _::field_ptrs::array; + public: - flecs::world world() const { - return flecs::world(m_world); + using query_base::query_base; + + query() : query_base() { } // necessary not to confuse msvc + + query(const query& obj) : query_base(obj) { } + + query& operator=(const query& obj) { + query_base::operator=(obj); + return *this; + } + + query(query&& obj) noexcept : query_base(FLECS_MOV(obj)) { } + + query& operator=(query&& obj) noexcept { + query_base::operator=(FLECS_FWD(obj)); + return *this; } - -private: - using Terms = typename _::term_ptrs::array; +private: ecs_iter_t get_iter(flecs::world_t *world) const override { + ecs_assert(query_ != nullptr, ECS_INVALID_PARAMETER, + "cannot iterate invalid query"); if (!world) { - world = m_world; + world = query_->world; } - return ecs_query_iter(world, m_query); + return ecs_query_iter(world, query_); } ecs_iter_next_action_t next_action() const override { return ecs_query_next; } - - ecs_iter_next_action_t next_each_action() const override { - return ecs_query_next_instanced; - } - -public: - using query_base::query_base; }; -// Mixin implementation +// World mixin implementation template inline flecs::query world::query(Args &&... args) const { - return flecs::query_builder(m_world, FLECS_FWD(args)...) + return flecs::query_builder(world_, FLECS_FWD(args)...) .build(); } +inline flecs::query<> world::query(flecs::entity query_entity) const { + ecs_query_desc_t desc = {}; + desc.entity = query_entity; + return flecs::query<>(world_, &desc); +} + template inline flecs::query_builder world::query_builder(Args &&... args) const { - return flecs::query_builder(m_world, FLECS_FWD(args)...); + return flecs::query_builder(world_, FLECS_FWD(args)...); +} + +// world::each +namespace _ { + +// Each with entity parameter +template +struct query_delegate_w_ent; + +template +struct query_delegate_w_ent > +{ + query_delegate_w_ent(const flecs::world& world, Func&& func) { + auto f = world.query(); + f.each(FLECS_MOV(func)); + } +}; + +// Each without entity parameter +template +struct query_delegate_no_ent; + +template +struct query_delegate_no_ent > +{ + query_delegate_no_ent(const flecs::world& world, Func&& func) { + auto f = world.query(); + f.each(FLECS_MOV(func)); + } +}; + +// Switch between function with & without entity parameter +template +struct query_delegate; + +template +struct query_delegate, flecs::entity>::value> > { + query_delegate(const flecs::world& world, Func&& func) { + query_delegate_w_ent>(world, FLECS_MOV(func)); + } +}; + +template +struct query_delegate, flecs::entity>::value> > { + query_delegate(const flecs::world& world, Func&& func) { + query_delegate_no_ent>(world, FLECS_MOV(func)); + } +}; + +} + +template +inline void world::each(Func&& func) const { + _::query_delegate f_delegate(*this, FLECS_MOV(func)); +} + +template +inline void world::each(Func&& func) const { + ecs_iter_t it = ecs_each_id(world_, _::type::id()); + + while (ecs_each_next(&it)) { + _::each_delegate(func).invoke(&it); + } } -// Builder implementation -template -inline Base& query_builder_i::observable(const query_base& parent) { - m_desc->parent = parent; - return *static_cast(this); +template +inline void world::each(flecs::id_t each_id, Func&& func) const { + ecs_iter_t it = ecs_each_id(world_, each_id); + + while (ecs_each_next(&it)) { + _::each_delegate(func).invoke(&it); + } } // query_base implementation -inline query_base::operator query<>() const { - return flecs::query<>(m_world, m_query); +inline query_base::operator flecs::query<> () const { + return flecs::query<>(query_); } -} // namespace flecs +} /** * @file addons/cpp/mixins/observer/impl.hpp @@ -28791,55 +30390,59 @@ struct node_builder : IBuilder public: explicit node_builder(flecs::world_t* world, const char *name = nullptr) - : IBase(&m_desc) - , m_desc{} - , m_world(world) - , m_instanced(false) + : IBase(&desc_) + , desc_{} + , world_(world) { ecs_entity_desc_t entity_desc = {}; entity_desc.name = name; entity_desc.sep = "::"; entity_desc.root_sep = "::"; - m_desc.entity = ecs_entity_init(m_world, &entity_desc); + desc_.entity = ecs_entity_init(world_, &entity_desc); } - /* Iter (or each) is mandatory and always the last thing that - * is added in the fluent method chain. Create system signature from both - * template parameters and anything provided by the signature method. */ template - T iter(Func&& func) { - using Delegate = typename _::iter_delegate< - typename std::decay::type, Components...>; - return build(FLECS_FWD(func)); + T run(Func&& func) { + using Delegate = typename _::run_delegate< + typename std::decay::type>; + + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); + desc_.run = Delegate::run; + desc_.run_ctx = ctx; + desc_.run_ctx_free = reinterpret_cast< + ecs_ctx_free_t>(_::free_obj); + return T(world_, &desc_); + } + + template + T run(Func&& func, EachFunc&& each_func) { + using Delegate = typename _::run_delegate< + typename std::decay::type>; + + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); + desc_.run = Delegate::run; + desc_.run_ctx = ctx; + desc_.run_ctx_free = reinterpret_cast< + ecs_ctx_free_t>(_::free_obj); + return each(FLECS_FWD(each_func)); } - /* Each is similar to action, but accepts a function that operates on a - * single entity */ template T each(Func&& func) { using Delegate = typename _::each_delegate< typename std::decay::type, Components...>; - m_instanced = true; - return build(FLECS_FWD(func)); - } - -protected: - flecs::world_t* world_v() override { return m_world; } - TDesc m_desc; - flecs::world_t *m_world; - bool m_instanced; - -private: - template - T build(Func&& func) { auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); - m_desc.callback = Delegate::run; - m_desc.binding_ctx = ctx; - m_desc.binding_ctx_free = reinterpret_cast< + desc_.callback = Delegate::run; + desc_.callback_ctx = ctx; + desc_.callback_ctx_free = reinterpret_cast< ecs_ctx_free_t>(_::free_obj); - - return T(m_world, &m_desc, m_instanced); + return T(world_, &desc_); } + +protected: + flecs::world_t* world_v() override { return world_; } + TDesc desc_; + flecs::world_t *world_; }; #undef FLECS_IBUILDER @@ -28859,26 +30462,26 @@ namespace flecs { /** Observer builder interface. * - * \ingroup cpp_observers + * @ingroup cpp_observers */ template -struct observer_builder_i : filter_builder_i { - using BaseClass = filter_builder_i; +struct observer_builder_i : query_builder_i { + using BaseClass = query_builder_i; observer_builder_i() : BaseClass(nullptr) - , m_desc(nullptr) - , m_event_count(0) { } + , desc_(nullptr) + , event_count_(0) { } observer_builder_i(ecs_observer_desc_t *desc) - : BaseClass(&desc->filter) - , m_desc(desc) - , m_event_count(0) { } + : BaseClass(&desc->query) + , desc_(desc) + , event_count_(0) { } /** Specify the event(s) for when the observer should run. * @param evt The event. */ Base& event(entity_t evt) { - m_desc->events[m_event_count ++] = evt; + desc_->events[event_count_ ++] = evt; return *this; } @@ -28887,38 +30490,44 @@ struct observer_builder_i : filter_builder_i { */ template Base& event() { - m_desc->events[m_event_count ++] = _::cpp_type().id(world_v()); + desc_->events[event_count_ ++] = _::type().id(world_v()); return *this; } - /** Invoke observer for anything that matches its filter on creation */ + /** Invoke observer for anything that matches its query on creation */ Base& yield_existing(bool value = true) { - m_desc->yield_existing = value; + desc_->yield_existing = value; + return *this; + } + + /** Set observer flags */ + Base& observer_flags(ecs_flags32_t flags) { + desc_->flags_ |= flags; return *this; } /** Set observer context */ Base& ctx(void *ptr) { - m_desc->ctx = ptr; + desc_->ctx = ptr; return *this; } /** Set observer run callback */ Base& run(ecs_iter_action_t action) { - m_desc->run = action; + desc_->run = action; return *this; } protected: - virtual flecs::world_t* world_v() = 0; + virtual flecs::world_t* world_v() override = 0; private: operator Base&() { return *static_cast(this); } - ecs_observer_desc_t *m_desc; - int32_t m_event_count; + ecs_observer_desc_t *desc_; + int32_t event_count_; }; } @@ -28934,7 +30543,7 @@ namespace _ { /** Observer builder. * - * \ingroup cpp_observers + * @ingroup cpp_observers */ template struct observer_builder final : _::observer_builder_base { @@ -28957,46 +30566,35 @@ struct observer final : entity explicit observer() : entity() { } - observer(flecs::world_t *world, ecs_observer_desc_t *desc, bool instanced) - { - if (!desc->filter.instanced) { - desc->filter.instanced = instanced; - } - - m_world = world; - m_id = ecs_observer_init(world, desc); - - if (desc->filter.terms_buffer) { - ecs_os_free(desc->filter.terms_buffer); - } + observer(flecs::world_t *world, ecs_observer_desc_t *desc) { + world_ = world; + id_ = ecs_observer_init(world, desc); } void ctx(void *ctx) { ecs_observer_desc_t desc = {}; - desc.entity = m_id; + desc.entity = id_; desc.ctx = ctx; - ecs_observer_init(m_world, &desc); + ecs_observer_init(world_, &desc); } void* ctx() const { - return ecs_observer_get_ctx(m_world, m_id); + return ecs_observer_get(world_, id_)->ctx; } - flecs::filter<> query() const { - const flecs::Poly *poly = this->get(flecs::Observer); - const ecs_observer_t *ob = static_cast(poly->poly); - return flecs::filter<>(m_world, &ob->filter); + flecs::query<> query() const { + return flecs::query<>(ecs_observer_get(world_, id_)->query); } }; // Mixin implementation inline observer world::observer(flecs::entity e) const { - return flecs::observer(m_world, e); + return flecs::observer(world_, e); } template inline observer_builder world::observer(Args &&... args) const { - return flecs::observer_builder(m_world, FLECS_FWD(args)...); + return flecs::observer_builder(world_, FLECS_FWD(args)...); } } // namespace flecs @@ -29015,12 +30613,12 @@ namespace flecs // Mixin implementation inline flecs::event_builder world::event(flecs::entity_t evt) const { - return flecs::event_builder(m_world, evt); + return flecs::event_builder(world_, evt); } template inline flecs::event_builder_typed world::event() const { - return flecs::event_builder_typed(m_world, _::cpp_type().id(m_world)); + return flecs::event_builder_typed(world_, _::type().id(world_)); } namespace _ { @@ -29029,16 +30627,16 @@ namespace _ { flecs::entity_t event, flecs::entity_t entity, ecs_iter_action_t callback, - void *binding_ctx, - ecs_ctx_free_t binding_ctx_free) + void *callback_ctx, + ecs_ctx_free_t callback_ctx_free) { ecs_observer_desc_t desc = {}; desc.events[0] = event; - desc.filter.terms[0].id = EcsAny; - desc.filter.terms[0].src.id = entity; + desc.query.terms[0].id = EcsAny; + desc.query.terms[0].src.id = entity; desc.callback = callback; - desc.binding_ctx = binding_ctx; - desc.binding_ctx_free = binding_ctx_free; + desc.callback_ctx = callback_ctx; + desc.callback_ctx_free = callback_ctx_free; flecs::entity_t o = ecs_observer_init(world, &desc); ecs_add_pair(world, o, EcsChildOf, entity); @@ -29054,7 +30652,7 @@ namespace _ { { using Delegate = _::entity_observer_delegate; auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); - entity_observer_create(world, _::cpp_type::id(world), entity, Delegate::run, ctx, + entity_observer_create(world, _::type::id(world), entity, Delegate::run, ctx, reinterpret_cast(_::free_obj)); } @@ -29066,7 +30664,7 @@ namespace _ { { using Delegate = _::entity_payload_observer_delegate; auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); - entity_observer_create(world, _::cpp_type::id(world), entity, Delegate::run, ctx, + entity_observer_create(world, _::type::id(world), entity, Delegate::run, ctx, reinterpret_cast(_::free_obj)); } }; @@ -29074,11 +30672,11 @@ namespace _ { template template -inline Self& entity_builder::observe(flecs::entity_t evt, Func&& f) { +inline const Self& entity_builder::observe(flecs::entity_t evt, Func&& f) const { using Delegate = _::entity_observer_delegate; auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); - _::entity_observer_create(m_world, evt, m_id, Delegate::run, ctx, + _::entity_observer_create(world_, evt, id_, Delegate::run, ctx, reinterpret_cast(_::free_obj)); return to_base(); @@ -29086,23 +30684,23 @@ inline Self& entity_builder::observe(flecs::entity_t evt, Func&& f) { template template -inline Self& entity_builder::observe(Func&& f) { +inline const Self& entity_builder::observe(Func&& f) const { _::entity_observer_factory::template create( - m_world, m_id, FLECS_FWD(f)); + world_, id_, FLECS_FWD(f)); return to_base(); } template template -inline Self& entity_builder::observe(Func&& f) { +inline const Self& entity_builder::observe(Func&& f) const { return this->observe<_::event_from_func_t>(FLECS_FWD(f)); } -inline void entity_view::emit(flecs::entity evt) { +inline void entity_view::emit(flecs::entity evt) const { this->emit(evt.id()); } -inline void entity_view::enqueue(flecs::entity evt) { +inline void entity_view::enqueue(flecs::entity evt) const { this->enqueue(evt.id()); } @@ -29126,8 +30724,8 @@ inline E entity_view::to_constant() const { template ::value >> inline flecs::entity world::to_entity(E constant) const { - const auto& et = enum_type(m_world); - return flecs::entity(m_world, et.entity(constant)); + const auto& et = enum_type(world_); + return flecs::entity(world_, et.entity(constant)); } } @@ -29152,19 +30750,25 @@ ecs_entity_t do_import(world& world, const char *symbol) { // Initialize module component type & don't allow it to be registered as a // tag, as this would prevent calling emplace() - auto m_c = component(world, nullptr, false); - ecs_add_id(world, m_c, EcsModule); + auto c_ = component(world, nullptr, false); - ecs_set_scope(world, m_c); + // Make module component sparse so that it'll never move in memory. This + // guarantees that a module destructor can be reliably used to cleanup + // module resources. + c_.add(flecs::Sparse); + + ecs_set_scope(world, c_); world.emplace(world); ecs_set_scope(world, scope); + ecs_add_id(world, c_, EcsModule); + // It should now be possible to lookup the module - ecs_entity_t m = ecs_lookup_symbol(world, symbol, true, false); + ecs_entity_t m = ecs_lookup_symbol(world, symbol, false, false); ecs_assert(m != 0, ECS_MODULE_UNDEFINED, symbol); - ecs_assert(m == m_c, ECS_INTERNAL_ERROR, NULL); + ecs_assert(m == c_, ECS_INTERNAL_ERROR, NULL); - ecs_log_pop(); + ecs_log_pop(); return m; } @@ -29174,13 +30778,13 @@ flecs::entity import(world& world) { const char *symbol = _::symbol_name(); ecs_entity_t m = ecs_lookup_symbol(world, symbol, true, false); - - if (!_::cpp_type::registered(world)) { + + if (!_::type::registered(world)) { /* Module is registered with world, initialize static data */ if (m) { - _::cpp_type::init(m, false); - + _::type::init(m, false); + /* Module is not yet registered, register it now */ } else { m = _::do_import(world, symbol); @@ -29199,20 +30803,38 @@ flecs::entity import(world& world) { /** * @defgroup cpp_addons_modules Modules - * @brief Modules organize components, systems and more in reusable units of code. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Modules organize components, systems and more in reusable units of code. + * * @{ */ template inline flecs::entity world::module(const char *name) const { - flecs::id_t result = _::cpp_type::id(m_world, nullptr, false); + flecs::entity result = this->entity(_::type::id( + world_, nullptr, false)); + if (name) { - ecs_add_path_w_sep(m_world, result, 0, name, "::", "::"); + flecs::entity prev_parent = result.parent(); + ecs_add_path_w_sep(world_, result, 0, name, "::", "::"); + flecs::entity parent = result.parent(); + if (prev_parent != parent) { + // Module was reparented, cleanup old parent(s) + flecs::entity cur = prev_parent, next; + while (cur) { + next = cur.parent(); + + ecs_iter_t it = ecs_each_id(world_, ecs_pair(EcsChildOf, cur)); + if (!ecs_iter_is_true(&it)) { + cur.destruct(); + } + + cur = next; + } + } } - ecs_set_scope(m_world, result); - return flecs::entity(m_world, result); + + return result; } template @@ -29253,7 +30875,7 @@ namespace flecs /** System builder interface. * - * \ingroup cpp_addons_systems + * @ingroup cpp_addons_systems */ template struct system_builder_i : query_builder_i { @@ -29263,7 +30885,7 @@ struct system_builder_i : query_builder_i { public: system_builder_i(ecs_system_desc_t *desc) : BaseClass(&desc->query) - , m_desc(desc) { } + , desc_(desc) { } /** Specify in which phase the system should run. * @@ -29271,25 +30893,33 @@ struct system_builder_i : query_builder_i { */ Base& kind(entity_t phase) { flecs::entity_t cur_phase = ecs_get_target( - world_v(), m_desc->entity, EcsDependsOn, 0); + world_v(), desc_->entity, EcsDependsOn, 0); if (cur_phase) { - ecs_remove_id(world_v(), m_desc->entity, ecs_dependson(cur_phase)); - ecs_remove_id(world_v(), m_desc->entity, cur_phase); + ecs_remove_id(world_v(), desc_->entity, ecs_dependson(cur_phase)); + ecs_remove_id(world_v(), desc_->entity, cur_phase); } if (phase) { - ecs_add_id(world_v(), m_desc->entity, ecs_dependson(phase)); - ecs_add_id(world_v(), m_desc->entity, phase); + ecs_add_id(world_v(), desc_->entity, ecs_dependson(phase)); + ecs_add_id(world_v(), desc_->entity, phase); } return *this; } + template ::value> = 0> + Base& kind(E phase) + { + const auto& et = enum_type(this->world_v()); + flecs::entity_t target = et.entity(phase); + return this->kind(target); + } + /** Specify in which phase the system should run. * * @tparam Phase The phase. */ template Base& kind() { - return this->kind(_::cpp_type::id(world_v())); + return this->kind(_::type::id(world_v())); } /** Specify whether system can run on multiple threads. @@ -29297,7 +30927,7 @@ struct system_builder_i : query_builder_i { * @param value If false system will always run on a single thread. */ Base& multi_threaded(bool value = true) { - m_desc->multi_threaded = value; + desc_->multi_threaded = value; return *this; } @@ -29305,8 +30935,8 @@ struct system_builder_i : query_builder_i { * * @param value If false system will always run staged. */ - Base& no_readonly(bool value = true) { - m_desc->no_readonly = value; + Base& immediate(bool value = true) { + desc_->immediate = value; return *this; } @@ -29318,7 +30948,7 @@ struct system_builder_i : query_builder_i { * @param interval The interval value. */ Base& interval(ecs_ftime_t interval) { - m_desc->interval = interval; + desc_->interval = interval; return *this; } @@ -29331,8 +30961,8 @@ struct system_builder_i : query_builder_i { * @param rate The multiple at which to run the system. */ Base& rate(const entity_t tick_source, int32_t rate) { - m_desc->rate = rate; - m_desc->tick_source = tick_source; + desc_->rate = rate; + desc_->tick_source = tick_source; return *this; } @@ -29344,7 +30974,18 @@ struct system_builder_i : query_builder_i { * @param rate The multiple at which to run the system. */ Base& rate(int32_t rate) { - m_desc->rate = rate; + desc_->rate = rate; + return *this; + } + + /** Set tick source. + * This operation sets a shared tick source for the system. + * + * @tparam T The type associated with the singleton tick source to use for the system. + */ + template + Base& tick_source() { + desc_->tick_source = _::type::id(world_v()); return *this; } @@ -29354,35 +30995,33 @@ struct system_builder_i : query_builder_i { * @param tick_source The tick source to use for the system. */ Base& tick_source(flecs::entity_t tick_source) { - m_desc->tick_source = tick_source; + desc_->tick_source = tick_source; return *this; } /** Set system context */ Base& ctx(void *ptr) { - m_desc->ctx = ptr; + desc_->ctx = ptr; return *this; } /** Set system run callback */ Base& run(ecs_iter_action_t action) { - m_desc->run = action; + desc_->run = action; return *this; } protected: - virtual flecs::world_t* world_v() = 0; + virtual flecs::world_t* world_v() override = 0; private: operator Base&() { return *static_cast(this); } - ecs_system_desc_t *m_desc; + ecs_system_desc_t *desc_; }; -/** @} */ - } @@ -29396,7 +31035,7 @@ namespace _ { /** System builder. * - * \ingroup cpp_addons_systems + * @ingroup cpp_addons_systems */ template struct system_builder final : _::system_builder_base { @@ -29406,8 +31045,8 @@ struct system_builder final : _::system_builder_base { _::sig(world).populate(this); #ifdef FLECS_PIPELINE - ecs_add_id(world, this->m_desc.entity, ecs_dependson(flecs::OnUpdate)); - ecs_add_id(world, this->m_desc.entity, flecs::OnUpdate); + ecs_add_id(world, this->desc_.entity, ecs_dependson(flecs::OnUpdate)); + ecs_add_id(world, this->desc_.entity, flecs::OnUpdate); #endif } }; @@ -29426,50 +31065,47 @@ struct system_runner_fluent { int32_t stage_count, ecs_ftime_t delta_time, void *param) - : m_stage(world) - , m_id(id) - , m_delta_time(delta_time) - , m_param(param) - , m_offset(0) - , m_limit(0) - , m_stage_current(stage_current) - , m_stage_count(stage_count) { } + : stage_(world) + , id_(id) + , delta_time_(delta_time) + , param_(param) + , stage_current_(stage_current) + , stage_count_(stage_count) { } system_runner_fluent& offset(int32_t offset) { - m_offset = offset; + offset_ = offset; return *this; } system_runner_fluent& limit(int32_t limit) { - m_limit = limit; + limit_ = limit; return *this; } system_runner_fluent& stage(flecs::world& stage) { - m_stage = stage.c_ptr(); + stage_ = stage.c_ptr(); return *this; } ~system_runner_fluent() { - if (m_stage_count) { + if (stage_count_) { ecs_run_worker( - m_stage, m_id, m_stage_current, m_stage_count, m_delta_time, - m_param); + stage_, id_, stage_current_, stage_count_, delta_time_, + param_); } else { - ecs_run_w_filter( - m_stage, m_id, m_delta_time, m_offset, m_limit, m_param); + ecs_run(stage_, id_, delta_time_, param_); } } private: - world_t *m_stage; - entity_t m_id; - ecs_ftime_t m_delta_time; - void *m_param; - int32_t m_offset; - int32_t m_limit; - int32_t m_stage_current; - int32_t m_stage_count; + world_t *stage_; + entity_t id_; + ecs_ftime_t delta_time_; + void *param_; + int32_t offset_; + int32_t limit_; + int32_t stage_current_; + int32_t stage_count_; }; struct system final : entity @@ -29477,41 +31113,32 @@ struct system final : entity using entity::entity; explicit system() { - m_id = 0; - m_world = nullptr; + id_ = 0; + world_ = nullptr; } - explicit system(flecs::world_t *world, ecs_system_desc_t *desc, bool instanced) - { - if (!desc->query.filter.instanced) { - desc->query.filter.instanced = instanced; - } - - m_world = world; - m_id = ecs_system_init(world, desc); - - if (desc->query.filter.terms_buffer) { - ecs_os_free(desc->query.filter.terms_buffer); - } + explicit system(flecs::world_t *world, ecs_system_desc_t *desc) { + world_ = world; + id_ = ecs_system_init(world, desc); } void ctx(void *ctx) { ecs_system_desc_t desc = {}; - desc.entity = m_id; + desc.entity = id_; desc.ctx = ctx; - ecs_system_init(m_world, &desc); + ecs_system_init(world_, &desc); } void* ctx() const { - return ecs_system_get_ctx(m_world, m_id); + return ecs_system_get(world_, id_)->ctx; } flecs::query<> query() const { - return flecs::query<>(m_world, ecs_system_get_query(m_world, m_id)); + return flecs::query<>(ecs_system_get(world_, id_)->query); } system_runner_fluent run(ecs_ftime_t delta_time = 0.0f, void *param = nullptr) const { - return system_runner_fluent(m_world, m_id, 0, 0, delta_time, param); + return system_runner_fluent(world_, id_, 0, 0, delta_time, param); } system_runner_fluent run_worker( @@ -29521,7 +31148,7 @@ struct system final : entity void *param = nullptr) const { return system_runner_fluent( - m_world, m_id, stage_current, stage_count, delta_time, param); + world_, id_, stage_current, stage_count, delta_time, param); } # ifdef FLECS_TIMER @@ -29531,8 +31158,10 @@ struct system final : entity */ /** - * \memberof flecs::system - * \ingroup cpp_addons_timer + * @memberof flecs::system + * @ingroup cpp_addons_timer + * + * @{ */ /** Set interval. @@ -29570,6 +31199,12 @@ void start(); */ void stop(); +/** Set external tick source. + * @see ecs_set_tick_source + */ +template +void set_tick_source(); + /** Set external tick source. * @see ecs_set_tick_source */ @@ -29583,12 +31218,12 @@ void set_tick_source(flecs::entity e); // Mixin implementation inline system world::system(flecs::entity e) const { - return flecs::system(m_world, e); + return flecs::system(world_, e); } template inline system_builder world::system(Args &&... args) const { - return flecs::system_builder(m_world, FLECS_FWD(args)...); + return flecs::system_builder(world_, FLECS_FWD(args)...); } namespace _ { @@ -29628,16 +31263,16 @@ namespace flecs { /** Pipeline builder interface. * - * \ingroup cpp_pipelines + * @ingroup cpp_pipelines */ template struct pipeline_builder_i : query_builder_i { pipeline_builder_i(ecs_pipeline_desc_t *desc, int32_t term_index = 0) : query_builder_i(&desc->query, term_index) - , m_desc(desc) { } + , desc_(desc) { } private: - ecs_pipeline_desc_t *m_desc; + ecs_pipeline_desc_t *desc_; }; } @@ -29653,7 +31288,7 @@ namespace _ { /** Pipeline builder. * - * \ingroup cpp_pipelines + * @ingroup cpp_pipelines */ template struct pipeline_builder final : _::pipeline_builder_base { @@ -29661,7 +31296,7 @@ struct pipeline_builder final : _::pipeline_builder_base { : _::pipeline_builder_base(world) { _::sig(world).populate(this); - this->m_desc.entity = id; + this->desc_.entity = id; } }; @@ -29675,79 +31310,75 @@ struct pipeline : entity { pipeline(world_t *world, ecs_pipeline_desc_t *desc) : entity(world) { - m_id = ecs_pipeline_init(world, desc); + id_ = ecs_pipeline_init(world, desc); - if (!m_id) { + if (!id_) { ecs_abort(ECS_INVALID_PARAMETER, NULL); } - - if (desc->query.filter.terms_buffer) { - ecs_os_free(desc->query.filter.terms_buffer); - } } }; inline flecs::pipeline_builder<> world::pipeline() const { - return flecs::pipeline_builder<>(m_world); + return flecs::pipeline_builder<>(world_); } template ::value >> inline flecs::pipeline_builder<> world::pipeline() const { - return flecs::pipeline_builder<>(m_world, _::cpp_type::id(m_world)); + return flecs::pipeline_builder<>(world_, _::type::id(world_)); } inline void world::set_pipeline(const flecs::entity pip) const { - return ecs_set_pipeline(m_world, pip); + return ecs_set_pipeline(world_, pip); } template inline void world::set_pipeline() const { - return ecs_set_pipeline(m_world, _::cpp_type::id(m_world)); + return ecs_set_pipeline(world_, _::type::id(world_)); } inline flecs::entity world::get_pipeline() const { - return flecs::entity(m_world, ecs_get_pipeline(m_world)); + return flecs::entity(world_, ecs_get_pipeline(world_)); } inline bool world::progress(ecs_ftime_t delta_time) const { - return ecs_progress(m_world, delta_time); + return ecs_progress(world_, delta_time); } inline void world::run_pipeline(const flecs::entity_t pip, ecs_ftime_t delta_time) const { - return ecs_run_pipeline(m_world, pip, delta_time); + return ecs_run_pipeline(world_, pip, delta_time); } template ::value >> inline void world::run_pipeline(ecs_ftime_t delta_time) const { - return ecs_run_pipeline(m_world, _::cpp_type::id(m_world), delta_time); + return ecs_run_pipeline(world_, _::type::id(world_), delta_time); } inline void world::set_time_scale(ecs_ftime_t mul) const { - ecs_set_time_scale(m_world, mul); + ecs_set_time_scale(world_, mul); } inline void world::set_target_fps(ecs_ftime_t target_fps) const { - ecs_set_target_fps(m_world, target_fps); + ecs_set_target_fps(world_, target_fps); } inline void world::reset_clock() const { - ecs_reset_clock(m_world); + ecs_reset_clock(world_); } inline void world::set_threads(int32_t threads) const { - ecs_set_threads(m_world, threads); + ecs_set_threads(world_, threads); } inline int32_t world::get_threads() const { - return ecs_get_stage_count(m_world); + return ecs_get_stage_count(world_); } inline void world::set_task_threads(int32_t task_threads) const { - ecs_set_task_threads(m_world, task_threads); + ecs_set_task_threads(world_, task_threads); } inline bool world::using_task_threads() const { - return ecs_using_task_threads(m_world); + return ecs_using_task_threads(world_); } } @@ -29768,179 +31399,96 @@ struct timer final : entity { using entity::entity; timer& interval(ecs_ftime_t interval) { - ecs_set_interval(m_world, m_id, interval); + ecs_set_interval(world_, id_, interval); return *this; } ecs_ftime_t interval() { - return ecs_get_interval(m_world, m_id); + return ecs_get_interval(world_, id_); } timer& timeout(ecs_ftime_t timeout) { - ecs_set_timeout(m_world, m_id, timeout); + ecs_set_timeout(world_, id_, timeout); return *this; } ecs_ftime_t timeout() { - return ecs_get_timeout(m_world, m_id); + return ecs_get_timeout(world_, id_); } timer& rate(int32_t rate, flecs::entity_t tick_source = 0) { - ecs_set_rate(m_world, m_id, rate, tick_source); + ecs_set_rate(world_, id_, rate, tick_source); return *this; } void start() { - ecs_start_timer(m_world, m_id); + ecs_start_timer(world_, id_); } void stop() { - ecs_stop_timer(m_world, m_id); + ecs_stop_timer(world_, id_); } }; +template +inline flecs::timer world::timer() const { + return flecs::timer(world_, _::type::id(world_)); +} + template inline flecs::timer world::timer(Args &&... args) const { - return flecs::timer(m_world, FLECS_FWD(args)...); + return flecs::timer(world_, FLECS_FWD(args)...); } inline void world::randomize_timers() const { - ecs_randomize_timers(m_world); + ecs_randomize_timers(world_); } inline void system::interval(ecs_ftime_t interval) { - ecs_set_interval(m_world, m_id, interval); + ecs_set_interval(world_, id_, interval); } inline ecs_ftime_t system::interval() { - return ecs_get_interval(m_world, m_id); + return ecs_get_interval(world_, id_); } inline void system::timeout(ecs_ftime_t timeout) { - ecs_set_timeout(m_world, m_id, timeout); + ecs_set_timeout(world_, id_, timeout); } inline ecs_ftime_t system::timeout() { - return ecs_get_timeout(m_world, m_id); + return ecs_get_timeout(world_, id_); } inline void system::rate(int32_t rate) { - ecs_set_rate(m_world, m_id, rate, 0); + ecs_set_rate(world_, id_, rate, 0); } inline void system::start() { - ecs_start_timer(m_world, m_id); + ecs_start_timer(world_, id_); } inline void system::stop() { - ecs_stop_timer(m_world, m_id); + ecs_stop_timer(world_, id_); } -inline void system::set_tick_source(flecs::entity e) { - ecs_set_tick_source(m_world, m_id, e); -} - -namespace _ { - -inline void timer_init(flecs::world& world) { - world.component("flecs::timer::RateFilter"); - world.component("flecs::timer::Timer"); +template +inline void system::set_tick_source() { + ecs_set_tick_source(world_, id_, _::type::id(world_)); } +inline void system::set_tick_source(flecs::entity e) { + ecs_set_tick_source(world_, id_, e); } -} - -#endif -#ifdef FLECS_SNAPSHOT -/** - * @file addons/cpp/mixins/snapshot/impl.hpp - * @brief Snapshot module implementation. - */ - -#pragma once - -namespace flecs { - -struct snapshot final { - explicit snapshot(const world& world) - : m_world( world ) - , m_snapshot( nullptr ) { } - snapshot(const snapshot& obj) - : m_world( obj.m_world ) - { - ecs_iter_t it = ecs_snapshot_iter(obj.m_snapshot); - m_snapshot = ecs_snapshot_take_w_iter(&it); - } - - snapshot(snapshot&& obj) - : m_world(obj.m_world) - , m_snapshot(obj.m_snapshot) - { - obj.m_snapshot = nullptr; - } - - snapshot& operator=(const snapshot& obj) { - ecs_assert(m_world.c_ptr() == obj.m_world.c_ptr(), ECS_INVALID_PARAMETER, NULL); - ecs_iter_t it = ecs_snapshot_iter(obj.m_snapshot); - m_snapshot = ecs_snapshot_take_w_iter(&it); - return *this; - } - - snapshot& operator=(snapshot&& obj) { - ecs_assert(m_world.c_ptr() == obj.m_world.c_ptr(), ECS_INVALID_PARAMETER, NULL); - m_snapshot = obj.m_snapshot; - obj.m_snapshot = nullptr; - return *this; - } - - void take() { - if (m_snapshot) { - ecs_snapshot_free(m_snapshot); - } - - m_snapshot = ecs_snapshot_take(m_world.c_ptr()); - } - - template - void take(const F& f) { - if (m_snapshot) { - ecs_snapshot_free(m_snapshot); - } - - ecs_iter_t it = ecs_filter_iter(m_world, f.c_ptr()); - - m_snapshot = ecs_snapshot_take_w_iter(&it); - } - - void restore() { - if (m_snapshot) { - ecs_snapshot_restore(m_world.c_ptr(), m_snapshot); - m_snapshot = nullptr; - } - } - - ~snapshot() { - if (m_snapshot) { - ecs_snapshot_free(m_snapshot); - } - } - - snapshot_t* c_ptr() const { - return m_snapshot; - } - -private: - const world& m_world; - snapshot_t *m_snapshot; -}; +namespace _ { -// Snapshot mixin implementation -template -inline flecs::snapshot world::snapshot(Args &&... args) const { - return flecs::snapshot(*this, FLECS_FWD(args)...); +inline void timer_init(flecs::world& world) { + world.component("flecs::timer::RateFilter"); + world.component("flecs::timer::Timer"); } +} } #endif @@ -29955,40 +31503,154 @@ inline flecs::snapshot world::snapshot(Args &&... args) const { namespace flecs { namespace doc { +/** Get UUID for an entity. + * + * @see ecs_doc_get_uuid() + * @see flecs::doc::set_uuid() + * @see flecs::entity_view::doc_uuid() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_uuid(const flecs::entity_view& e) { + return ecs_doc_get_uuid(e.world(), e); +} + +/** Get human readable name for an entity. + * + * @see ecs_doc_get_name() + * @see flecs::doc::set_name() + * @see flecs::entity_view::doc_name() + * + * @ingroup cpp_addons_doc + */ inline const char* get_name(const flecs::entity_view& e) { return ecs_doc_get_name(e.world(), e); } +/** Get brief description for an entity. + * + * @see ecs_doc_get_brief() + * @see flecs::doc::set_brief() + * @see flecs::entity_view::doc_brief() + * + * @ingroup cpp_addons_doc + */ inline const char* get_brief(const flecs::entity_view& e) { return ecs_doc_get_brief(e.world(), e); } +/** Get detailed description for an entity. + * + * @see ecs_doc_get_detail() + * @see flecs::doc::set_detail() + * @see flecs::entity_view::doc_detail() + * + * @ingroup cpp_addons_doc + */ inline const char* get_detail(const flecs::entity_view& e) { return ecs_doc_get_detail(e.world(), e); } +/** Get link to external documentation for an entity. + * + * @see ecs_doc_get_link() + * @see flecs::doc::set_link() + * @see flecs::entity_view::doc_link() + * + * @ingroup cpp_addons_doc + */ inline const char* get_link(const flecs::entity_view& e) { return ecs_doc_get_link(e.world(), e); } +/** Get color for an entity. + * + * @see ecs_doc_get_color() + * @see flecs::doc::set_color() + * @see flecs::entity_view::doc_color() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_color(const flecs::entity_view& e) { + return ecs_doc_get_color(e.world(), e); +} + +/** Set UUID for an entity. + * + * @see ecs_doc_set_uuid() + * @see flecs::doc::get_uuid() + * @see flecs::entity_builder::set_doc_uuid() + * + * @ingroup cpp_addons_doc + */ +inline void set_uuid(flecs::entity& e, const char *uuid) { + ecs_doc_set_uuid(e.world(), e, uuid); +} + +/** Set human readable name for an entity. + * + * @see ecs_doc_set_name() + * @see flecs::doc::get_name() + * @see flecs::entity_builder::set_doc_name() + * + * @ingroup cpp_addons_doc + */ inline void set_name(flecs::entity& e, const char *name) { ecs_doc_set_name(e.world(), e, name); } +/** Set brief description for an entity. + * + * @see ecs_doc_set_brief() + * @see flecs::doc::get_brief() + * @see flecs::entity_builder::set_doc_brief() + * + * @ingroup cpp_addons_doc + */ inline void set_brief(flecs::entity& e, const char *description) { ecs_doc_set_brief(e.world(), e, description); } +/** Set detailed description for an entity. + * + * @see ecs_doc_set_detail() + * @see flecs::doc::get_detail() + * @see flecs::entity_builder::set_doc_detail() + * + * @ingroup cpp_addons_doc + */ inline void set_detail(flecs::entity& e, const char *description) { ecs_doc_set_detail(e.world(), e, description); } -inline void set_link(flecs::entity& e, const char *description) { - ecs_doc_set_link(e.world(), e, description); +/** Set link to external documentation for an entity. + * + * @see ecs_doc_set_link() + * @see flecs::doc::get_link() + * @see flecs::entity_builder::set_doc_link() + * + * @ingroup cpp_addons_doc + */ +inline void set_link(flecs::entity& e, const char *link) { + ecs_doc_set_link(e.world(), e, link); +} + +/** Set color for an entity. + * + * @see ecs_doc_set_color() + * @see flecs::doc::get_color() + * @see flecs::entity_builder::set_doc_color() + * + * @ingroup cpp_addons_doc + */ +inline void set_color(flecs::entity& e, const char *color) { + ecs_doc_set_color(e.world(), e, color); } +/** @private */ namespace _ { +/** @private */ inline void init(flecs::world& world) { world.component("flecs::doc::Description"); } @@ -30020,190 +31682,6 @@ inline void init(flecs::world& world) { } // namespace rest } // namespace flecs -#endif -#ifdef FLECS_RULES -/** - * @file addons/cpp/mixins/rule/impl.hpp - * @brief Rule implementation. - */ - -#pragma once - -/** - * @file addons/cpp/mixins/rule/builder.hpp - * @brief Rule builder. - */ - -#pragma once - - -namespace flecs { -namespace _ { - template - using rule_builder_base = builder< - rule, ecs_filter_desc_t, rule_builder, - filter_builder_i, Components ...>; -} - -/** Rule builder. - * - * \ingroup cpp_addons_rules - */ -template -struct rule_builder final : _::rule_builder_base { - rule_builder(flecs::world_t* world, const char *name = nullptr) - : _::rule_builder_base(world) - { - _::sig(world).populate(this); - if (name != nullptr) { - ecs_entity_desc_t entity_desc = {}; - entity_desc.name = name; - entity_desc.sep = "::"; - entity_desc.root_sep = "::"; - this->m_desc.entity = ecs_entity_init(world, &entity_desc); - } - } -}; - -} - - -namespace flecs { - -//////////////////////////////////////////////////////////////////////////////// -//// Persistent queries -//////////////////////////////////////////////////////////////////////////////// - -struct rule_base { - rule_base() - : m_world(nullptr) - , m_rule(nullptr) { } - - rule_base(world_t *world, rule_t *rule = nullptr) - : m_world(world) - , m_rule(rule) { } - - rule_base(world_t *world, ecs_filter_desc_t *desc) - : m_world(world) - { - m_rule = ecs_rule_init(world, desc); - if (desc->terms_buffer) { - ecs_os_free(desc->terms_buffer); - } - } - - bool is_valid() const { - return m_rule != nullptr; - } - - operator rule_t*() const { - return m_rule; - } - - flecs::entity entity() { - return flecs::entity(m_world, ecs_get_entity(m_rule)); - } - - /** Free the rule. */ - void destruct() { - if (m_rule) { - ecs_rule_fini(m_rule); - m_world = nullptr; - m_rule = nullptr; - } - } - - template - void each_term(const Func& func) const { - this->filter().each_term(func); - } - - /** Move the rule. */ - void move(flecs::rule_base&& obj) { - this->destruct(); - this->m_world = obj.m_world; - this->m_rule = obj.m_rule; - obj.m_world = nullptr; - obj.m_rule = nullptr; - } - - flecs::filter_base filter() const { - return filter_base(m_world, ecs_rule_get_filter(m_rule)); - } - - /** Converts this rule to a string expression - * @see ecs_filter_str - */ - flecs::string str() const { - const ecs_filter_t *f = ecs_rule_get_filter(m_rule); - char *result = ecs_filter_str(m_world, f); - return flecs::string(result); - } - - - /** Converts this rule to a string that can be used to aid debugging - * the behavior of the rule. - * @see ecs_rule_str - */ - flecs::string rule_str() const { - char *result = ecs_rule_str(m_rule); - return flecs::string(result); - } - - operator rule<>() const; - -protected: - world_t *m_world; - rule_t *m_rule; -}; - -template -struct rule final : rule_base, iterable { -private: - using Terms = typename _::term_ptrs::array; - - ecs_iter_t get_iter(flecs::world_t *world) const override { - if (!world) { - world = m_world; - } - return ecs_rule_iter(world, m_rule); - } - - ecs_iter_next_action_t next_action() const override { - return ecs_rule_next; - } - - ecs_iter_next_action_t next_each_action() const override { - return ecs_rule_next_instanced; - } - -public: - using rule_base::rule_base; - - int32_t find_var(const char *name) { - return ecs_rule_find_var(m_rule, name); - } -}; - -// Mixin implementation -template -inline flecs::rule world::rule(Args &&... args) const { - return flecs::rule_builder(m_world, FLECS_FWD(args)...) - .build(); -} - -template -inline flecs::rule_builder world::rule_builder(Args &&... args) const { - return flecs::rule_builder(m_world, FLECS_FWD(args)...); -} - -// rule_base implementation -inline rule_base::operator rule<>() const { - return flecs::rule<>(m_world, m_rule); -} - -} // namespace flecs - #endif #ifdef FLECS_META /** @@ -30251,28 +31729,29 @@ inline void init(flecs::world& world) { world.component("flecs::meta::type_kind"); world.component("flecs::meta::primitive_kind"); - world.component("flecs::meta::member"); + world.component("flecs::meta::member_t"); world.component("flecs::meta::enum_constant"); world.component("flecs::meta::bitmask_constant"); - world.component("flecs::meta::MetaType"); - world.component("flecs::meta::MetaTypeSerialized"); - world.component("flecs::meta::Primitive"); - world.component("flecs::meta::Enum"); - world.component("flecs::meta::Bitmask"); - world.component("flecs::meta::Member"); - world.component("flecs::meta::Struct"); - world.component("flecs::meta::Array"); - world.component("flecs::meta::Vector"); + world.component("flecs::meta::type"); + world.component("flecs::meta::TypeSerializer"); + world.component("flecs::meta::primitive"); + world.component("flecs::meta::enum"); + world.component("flecs::meta::bitmask"); + world.component("flecs::meta::member"); + world.component("flecs::meta::member_ranges"); + world.component("flecs::meta::struct"); + world.component("flecs::meta::array"); + world.component("flecs::meta::vector"); - world.component("flecs::meta::Unit"); + world.component("flecs::meta::unit"); // To support member and member register components // (that do not have conflicting symbols with builtin ones) for platform // specific types. if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::cpp_type::init(flecs::Iptr, true); + flecs::_::type::init(flecs::Iptr, true); ecs_assert(flecs::type_id() == flecs::Iptr, ECS_INTERNAL_ERROR, NULL); // Remove symbol to prevent validation errors, as it doesn't match with @@ -30281,7 +31760,7 @@ inline void init(flecs::world& world) { } if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::cpp_type::init(flecs::Uptr, true); + flecs::_::type::init(flecs::Uptr, true); ecs_assert(flecs::type_id() == flecs::Uptr, ECS_INTERNAL_ERROR, NULL); // Remove symbol to prevent validation errors, as it doesn't match with @@ -30290,11 +31769,12 @@ inline void init(flecs::world& world) { } // Register opaque type support for C++ entity wrappers - world.component() - .opaque(flecs_entity_support); - - world.component() - .opaque(flecs_entity_support); + world.entity("::flecs::cpp").add(flecs::Module).scope([&]{ + world.component() + .opaque(flecs_entity_support); + world.component() + .opaque(flecs_entity_support); + }); } } // namespace _ @@ -30303,24 +31783,24 @@ inline void init(flecs::world& world) { inline flecs::entity cursor::get_type() const { - return flecs::entity(m_cursor.world, ecs_meta_get_type(&m_cursor)); + return flecs::entity(cursor_.world, ecs_meta_get_type(&cursor_)); } inline flecs::entity cursor::get_unit() const { - return flecs::entity(m_cursor.world, ecs_meta_get_unit(&m_cursor)); + return flecs::entity(cursor_.world, ecs_meta_get_unit(&cursor_)); } inline flecs::entity cursor::get_entity() const { - return flecs::entity(m_cursor.world, ecs_meta_get_entity(&m_cursor)); + return flecs::entity(cursor_.world, ecs_meta_get_entity(&cursor_)); } /** 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); + flecs::entity_t eid = ecs_primitive_init(world_, &desc); ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); - return flecs::entity(m_world, eid); + return flecs::entity(world_, eid); } /** Create array type. */ @@ -30328,28 +31808,28 @@ 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); + flecs::entity_t eid = ecs_array_init(world_, &desc); ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); - return flecs::entity(m_world, eid); + return flecs::entity(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); + return this->array(_::type::id(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); + flecs::entity_t eid = ecs_vector_init(world_, &desc); ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); - return flecs::entity(m_world, eid); + return flecs::entity(world_, eid); } template inline flecs::entity world::vector() { - return this->vector(_::cpp_type::id(m_world)); + return this->vector(_::type::id(world_)); } } // namespace flecs @@ -30360,7 +31840,7 @@ inline int ecs_serializer_t::value(ecs_entity_t type, const void *v) const { template inline int ecs_serializer_t::value(const T& v) const { - return this->value(flecs::_::cpp_type::id( + return this->value(flecs::_::type::id( const_cast(this->world)), &v); } @@ -30436,6 +31916,7 @@ inline units::units(flecs::world& world) { world.entity("::flecs::units::Angle"); world.entity("::flecs::units::Frequency"); world.entity("::flecs::units::Uri"); + world.entity("::flecs::units::Color"); // Initialize duration units world.entity( @@ -30575,6 +32056,11 @@ inline units::units(flecs::world& world) { world.entity( "::flecs::units::Angle::Degrees"); + // Initialize color + world.entity("::flecs::units::Color::Rgb"); + world.entity("::flecs::units::Color::Hsl"); + world.entity("::flecs::units::Color::Css"); + // Initialize percentage world.entity("::flecs::units::Percentage"); @@ -30586,9 +32072,9 @@ inline units::units(flecs::world& world) { } #endif -#ifdef FLECS_MONITOR +#ifdef FLECS_STATS /** - * @file addons/cpp/mixins/monitor/impl.hpp + * @file addons/cpp/mixins/stats/impl.hpp * @brief Monitor module implementation. */ @@ -30596,10 +32082,17 @@ inline units::units(flecs::world& world) { namespace flecs { -inline monitor::monitor(flecs::world& world) { +inline stats::stats(flecs::world& world) { +#ifdef FLECS_UNITS + world.import(); +#endif + /* Import C module */ - FlecsMonitorImport(world); + FlecsStatsImport(world); + world.component(); + world.component(); + world.component(); } } @@ -30621,6 +32114,9 @@ inline metrics::metrics(flecs::world& world) { /* Import C module */ FlecsMetricsImport(world); + world.component(); + world.component(); + world.entity("::flecs::metrics::Instance"); world.entity("::flecs::metrics::Metric"); world.entity("::flecs::metrics::Metric::Counter"); @@ -30630,18 +32126,18 @@ inline metrics::metrics(flecs::world& world) { } inline metric_builder::~metric_builder() { - if (!m_created) { - ecs_metric_init(m_world, &m_desc); + if (!created_) { + ecs_metric_init(world_, &desc_); } } inline metric_builder& metric_builder::member(const char *name) { flecs::entity m; - if (m_desc.id) { - flecs::entity_t type = ecs_get_typeid(m_world, m_desc.id); - m = flecs::entity(m_world, type).lookup(name); + if (desc_.id) { + flecs::entity_t type = ecs_get_typeid(world_, desc_.id); + m = flecs::entity(world_, type).lookup(name); } else { - m = flecs::world(m_world).lookup(name); + m = flecs::world(world_).lookup(name); } if (!m) { flecs::log::err("member '%s' not found", name); @@ -30651,7 +32147,7 @@ inline metric_builder& metric_builder::member(const char *name) { template inline metric_builder& metric_builder::member(const char *name) { - flecs::entity e (m_world, _::cpp_type::id(m_world)); + flecs::entity e (world_, _::type::id(world_)); flecs::entity_t m = e.lookup(name); if (!m) { flecs::log::err("member '%s' not found in type '%s'", @@ -30662,32 +32158,32 @@ inline metric_builder& metric_builder::member(const char *name) { } inline metric_builder& metric_builder::dotmember(const char *expr) { - m_desc.dotmember = expr; + desc_.dotmember = expr; return *this; } template inline metric_builder& metric_builder::dotmember(const char *expr) { - m_desc.dotmember = expr; - m_desc.id = _::cpp_type::id(m_world); + desc_.dotmember = expr; + desc_.id = _::type::id(world_); return *this; } inline metric_builder::operator flecs::entity() { - if (!m_created) { - m_created = true; - flecs::entity result(m_world, ecs_metric_init(m_world, &m_desc)); - m_desc.entity = result; + if (!created_) { + created_ = true; + flecs::entity result(world_, ecs_metric_init(world_, &desc_)); + desc_.entity = result; return result; } else { - return flecs::entity(m_world, m_desc.entity); + return flecs::entity(world_, desc_.entity); } } template inline flecs::metric_builder world::metric(Args &&... args) const { - flecs::entity result(m_world, FLECS_FWD(args)...); - return flecs::metric_builder(m_world, result); + flecs::entity result(world_, FLECS_FWD(args)...); + return flecs::metric_builder(world_, result); } template @@ -30696,8 +32192,8 @@ inline untyped_component& untyped_component::metric( const char *brief, const char *metric_name) { - flecs::world w(m_world); - flecs::entity e(m_world, m_id); + flecs::world w(world_); + flecs::entity e(world_, id_); const flecs::member_t *m = ecs_cpp_last_member(w, e); if (!m) { @@ -30757,28 +32253,28 @@ namespace flecs { /** Alert builder interface. * - * \ingroup cpp_addons_alerts + * @ingroup cpp_addons_alerts */ template -struct alert_builder_i : filter_builder_i { +struct alert_builder_i : query_builder_i { private: - using BaseClass = filter_builder_i; - + using BaseClass = query_builder_i; + public: alert_builder_i() : BaseClass(nullptr) - , m_desc(nullptr) { } + , desc_(nullptr) { } alert_builder_i(ecs_alert_desc_t *desc, int32_t term_index = 0) - : BaseClass(&desc->filter, term_index) - , m_desc(desc) { } + : BaseClass(&desc->query, term_index) + , desc_(desc) { } /** Alert message. * * @see ecs_alert_desc_t::message */ Base& message(const char *message) { - m_desc->message = message; + desc_->message = message; return *this; } @@ -30787,7 +32283,7 @@ struct alert_builder_i : filter_builder_i { * @see ecs_alert_desc_t::brief */ Base& brief(const char *brief) { - m_desc->brief = brief; + desc_->brief = brief; return *this; } @@ -30796,7 +32292,7 @@ struct alert_builder_i : filter_builder_i { * @see ecs_alert_desc_t::doc_name */ Base& doc_name(const char *doc_name) { - m_desc->doc_name = doc_name; + desc_->doc_name = doc_name; return *this; } @@ -30805,7 +32301,7 @@ struct alert_builder_i : filter_builder_i { * @see ecs_alert_desc_t::severity */ Base& severity(flecs::entity_t kind) { - m_desc->severity = kind; + desc_->severity = kind; return *this; } @@ -30814,7 +32310,7 @@ struct alert_builder_i : filter_builder_i { * @see ecs_alert_desc_t::retain_period */ Base& retain_period(ecs_ftime_t period) { - m_desc->retain_period = period; + desc_->retain_period = period; return *this; } @@ -30824,16 +32320,16 @@ struct alert_builder_i : filter_builder_i { */ template Base& severity() { - return severity(_::cpp_type::id(world_v())); + return severity(_::type::id(world_v())); } /** Add severity filter */ Base& severity_filter(flecs::entity_t kind, flecs::id_t with, const char *var = nullptr) { ecs_assert(severity_filter_count < ECS_ALERT_MAX_SEVERITY_FILTERS, - ECS_INVALID_PARAMETER, "Maxium number of severity filters reached"); + ECS_INVALID_PARAMETER, "Maximum number of severity filters reached"); ecs_alert_severity_filter_t *filter = - &m_desc->severity_filters[severity_filter_count ++]; + &desc_->severity_filters[severity_filter_count ++]; filter->severity = kind; filter->with = with; @@ -30844,14 +32340,14 @@ struct alert_builder_i : filter_builder_i { /** Add severity filter */ template Base& severity_filter(flecs::id_t with, const char *var = nullptr) { - return severity_filter(_::cpp_type::id(world_v()), with, var); + return severity_filter(_::type::id(world_v()), with, var); } /** Add severity filter */ template ::value > = 0> Base& severity_filter(const char *var = nullptr) { - return severity_filter(_::cpp_type::id(world_v()), - _::cpp_type::id(world_v()), var); + return severity_filter(_::type::id(world_v()), + _::type::id(world_v()), var); } /** Add severity filter */ @@ -30859,37 +32355,37 @@ struct alert_builder_i : filter_builder_i { Base& severity_filter(T with, const char *var = nullptr) { flecs::world w(world_v()); flecs::entity constant = w.to_entity(with); - return severity_filter(_::cpp_type::id(world_v()), + return severity_filter(_::type::id(world_v()), w.pair(constant), var); } /** Set member to create an alert for out of range values */ Base& member(flecs::entity_t m) { - m_desc->member = m; + desc_->member = m; return *this; } /** Set (component) id for member (optional). If .member() is set and id * is not set, the id will default to the member parent. */ Base& id(flecs::id_t id) { - m_desc->id = id; + desc_->id = id; return *this; } /** Set member to create an alert for out of range values */ template Base& member(const char *m, const char *v = nullptr) { - flecs::entity_t id = _::cpp_type::id(world_v()); + flecs::entity_t id = _::type::id(world_v()); flecs::entity_t mid = ecs_lookup_path_w_sep( world_v(), id, m, "::", "::", false); ecs_assert(m != 0, ECS_INVALID_PARAMETER, NULL); - m_desc->var = v; + desc_->var = v; return this->member(mid); } /** Set source variable for member (optional, defaults to $this) */ Base& var(const char *v) { - m_desc->var = v; + desc_->var = v; return *this; } @@ -30901,7 +32397,7 @@ struct alert_builder_i : filter_builder_i { return *static_cast(this); } - ecs_alert_desc_t *m_desc; + ecs_alert_desc_t *desc_; int32_t severity_filter_count = 0; }; @@ -30918,7 +32414,7 @@ namespace _ { /** Alert builder. * - * \ingroup cpp_addons_alerts + * @ingroup cpp_addons_alerts */ template struct alert_builder final : _::alert_builder_base { @@ -30931,7 +32427,7 @@ struct alert_builder final : _::alert_builder_base { entity_desc.name = name; entity_desc.sep = "::"; entity_desc.root_sep = "::"; - this->m_desc.entity = ecs_entity_init(world, &entity_desc); + this->desc_.entity = ecs_entity_init(world, &entity_desc); } } }; @@ -30947,25 +32443,25 @@ struct alert final : entity using entity::entity; explicit alert() { - m_id = 0; - m_world = nullptr; + id_ = 0; + world_ = nullptr; } - explicit alert(flecs::world_t *world, ecs_alert_desc_t *desc) - { - m_world = world; - m_id = ecs_alert_init(world, desc); - - if (desc->filter.terms_buffer) { - ecs_os_free(desc->filter.terms_buffer); - } + explicit alert(flecs::world_t *world, ecs_alert_desc_t *desc) { + world_ = world; + id_ = ecs_alert_init(world, desc); } }; inline alerts::alerts(flecs::world& world) { + world.import(); + /* Import C module */ FlecsAlertsImport(world); + world.component(); + world.component(); + world.entity("::flecs::alerts::Alert"); world.entity("::flecs::alerts::Info"); world.entity("::flecs::alerts::Warning"); @@ -30974,13 +32470,94 @@ inline alerts::alerts(flecs::world& world) { template inline flecs::alert_builder world::alert(Args &&... args) const { - return flecs::alert_builder(m_world, FLECS_FWD(args)...); + return flecs::alert_builder(world_, FLECS_FWD(args)...); +} + +} + +#endif +#ifdef FLECS_SCRIPT +/** + * @file addons/cpp/mixins/script/impl.hpp + * @brief Script implementation. + */ + +#pragma once + + +namespace flecs +{ + +inline flecs::entity script_builder::run() const { + ecs_entity_t e = ecs_script_init(world_, &desc_); + return flecs::entity(world_, e); } } #endif +/** + * @file addons/cpp/impl/field.hpp + * @brief Field implementation. + */ + +#pragma once + +namespace flecs +{ + +template +inline field::field(iter &iter, int32_t index) { + *this = iter.field(index); +} + +template +T& field::operator[](size_t index) const { + ecs_assert(data_ != nullptr, ECS_INVALID_OPERATION, + "invalid nullptr dereference of component type %s", + _::type_name()); + ecs_assert(index < count_, ECS_COLUMN_INDEX_OUT_OF_RANGE, + "index %d out of range for array of component type %s", + index, _::type_name()); + ecs_assert(!index || !is_shared_, ECS_INVALID_PARAMETER, + "non-zero index invalid for shared field of component type %s", + _::type_name()); + return data_[index]; +} + +/** Return first element of component array. + * This operator is typically used when the field is shared. + * + * @return Reference to the first element. + */ +template +T& field::operator*() const { + ecs_assert(data_ != nullptr, ECS_INVALID_OPERATION, + "invalid nullptr dereference of component type %s", + _::type_name()); + return *data_; +} + +/** Return first element of component array. + * This operator is typically used when the field is shared. + * + * @return Pointer to the first element. + */ +template +T* field::operator->() const { + ecs_assert(data_ != nullptr, ECS_INVALID_OPERATION, + "invalid nullptr dereference of component type %s", + _::type_name()); + ecs_assert(data_ != nullptr, ECS_INVALID_OPERATION, + "-> operator invalid for array with >1 element of " + "component type %s, use [row] instead", + _::type_name()); + return data_; +} + +} + /** * @file addons/cpp/impl/iter.hpp * @brief Iterator implementation. @@ -30992,78 +32569,114 @@ namespace flecs { inline flecs::entity iter::system() const { - return flecs::entity(m_iter->world, m_iter->system); + return flecs::entity(iter_->world, iter_->system); } inline flecs::entity iter::event() const { - return flecs::entity(m_iter->world, m_iter->event); + return flecs::entity(iter_->world, iter_->event); } inline flecs::id iter::event_id() const { - return flecs::id(m_iter->world, m_iter->event_id); + return flecs::id(iter_->world, iter_->event_id); } inline flecs::world iter::world() const { - return flecs::world(m_iter->world); + return flecs::world(iter_->world); } inline flecs::entity iter::entity(size_t row) const { - ecs_assert(row < static_cast(m_iter->count), + ecs_assert(row < static_cast(iter_->count), ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - return flecs::entity(m_iter->world, m_iter->entities[row]); -} - -template -inline column::column(iter &iter, int32_t index) { - *this = iter.field(index); + return flecs::entity(iter_->world, iter_->entities[row]); } -inline flecs::entity iter::src(int32_t index) const { - return flecs::entity(m_iter->world, ecs_field_src(m_iter, index)); +inline flecs::entity iter::src(int8_t index) const { + return flecs::entity(iter_->world, ecs_field_src(iter_, index)); } -inline flecs::id iter::id(int32_t index) const { - return flecs::id(m_iter->world, ecs_field_id(m_iter, index)); +inline flecs::id iter::id(int8_t index) const { + return flecs::id(iter_->world, ecs_field_id(iter_, index)); } -inline flecs::id iter::pair(int32_t index) const { - flecs::id_t id = ecs_field_id(m_iter, index); +inline flecs::id iter::pair(int8_t index) const { + flecs::id_t id = ecs_field_id(iter_, index); ecs_check(ECS_HAS_ID_FLAG(id, PAIR), ECS_INVALID_PARAMETER, NULL); - return flecs::id(m_iter->world, id); + return flecs::id(iter_->world, id); error: return flecs::id(); } inline flecs::type iter::type() const { - return flecs::type(m_iter->world, ecs_table_get_type(m_iter->table)); + return flecs::type(iter_->world, ecs_table_get_type(iter_->table)); } inline flecs::table iter::table() const { - return flecs::table(m_iter->real_world, m_iter->table); + return flecs::table(iter_->real_world, iter_->table); +} + +inline flecs::table iter::other_table() const { + return flecs::table(iter_->real_world, iter_->other_table); } inline flecs::table_range iter::range() const { - return flecs::table_range(m_iter->real_world, m_iter->table, - m_iter->offset, m_iter->count); + return flecs::table_range(iter_->real_world, iter_->table, + iter_->offset, iter_->count); +} + +template ::value, void>::type*> +inline flecs::field iter::field(int8_t index) const { + ecs_assert(!(iter_->flags & EcsIterCppEach), ECS_INVALID_OPERATION, + "cannot .field from .each, use .field_at(%d, row) instead", + _::type_name(), index); + return get_field(index); +} + +template ::value == false, void>::type*> +inline flecs::field iter::field(int8_t index) const { + ecs_assert(!(iter_->flags & EcsIterCppEach), ECS_INVALID_OPERATION, + "cannot .field from .each, use .field_at<%s>(%d, row) instead", + _::type_name(), index); + ecs_assert(!ecs_field_is_readonly(iter_, index), + ECS_ACCESS_VIOLATION, NULL); + return get_field(index); } -#ifdef FLECS_RULES inline flecs::entity iter::get_var(int var_id) const { ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, 0); - return flecs::entity(m_iter->world, ecs_iter_get_var(m_iter, var_id)); + return flecs::entity(iter_->world, ecs_iter_get_var(iter_, var_id)); } /** Get value of variable by name. * Get value of a query variable for current result. */ inline flecs::entity iter::get_var(const char *name) const { - ecs_rule_iter_t *rit = &m_iter->priv.iter.rule; - const flecs::rule_t *r = rit->rule; - int var_id = ecs_rule_find_var(r, name); + ecs_query_iter_t *qit = &iter_->priv_.iter.query; + const flecs::query_t *q = qit->query; + int var_id = ecs_query_find_var(q, name); ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); - return flecs::entity(m_iter->world, ecs_iter_get_var(m_iter, var_id)); + return flecs::entity(iter_->world, ecs_iter_get_var(iter_, var_id)); +} + +template +void iter::targets(int8_t index, const Func& func) { + ecs_assert(iter_->table != nullptr, ECS_INVALID_OPERATION, NULL); + ecs_assert(index < iter_->field_count, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ecs_field_is_set(iter_, index), ECS_INVALID_PARAMETER, NULL); + const ecs_type_t *table_type = ecs_table_get_type(iter_->table); + const ecs_table_record_t *tr = iter_->trs[index]; + int32_t i = tr->index, end = i + tr->count; + for (; i < end; i ++) { + ecs_id_t id = table_type->array[i]; + ecs_assert(ECS_IS_PAIR(id), ECS_INVALID_PARAMETER, + "field does not match a pair"); + flecs::entity tgt(iter_->world, + ecs_pair_second(iter_->real_world, id)); + func(tgt); + } } -#endif } // namespace flecs @@ -31078,6 +32691,10 @@ namespace flecs { inline void world::init_builtin_components() { + this->component(); + this->component(); + this->component(); + # ifdef FLECS_SYSTEM _::system_init(*this); # endif @@ -31097,22 +32714,22 @@ inline void world::init_builtin_components() { template inline flecs::entity world::use(const char *alias) const { - entity_t e = _::cpp_type::id(m_world); + entity_t e = _::type::id(world_); const char *name = alias; if (!name) { // If no name is defined, use the entity name without the scope - name = ecs_get_name(m_world, e); + name = ecs_get_name(world_, e); } - ecs_set_alias(m_world, e, name); - return flecs::entity(m_world, e); + ecs_set_alias(world_, e, name); + return flecs::entity(world_, e); } inline flecs::entity world::use(const char *name, const char *alias) const { - entity_t e = ecs_lookup_path_w_sep(m_world, 0, name, "::", "::", true); + entity_t e = ecs_lookup_path_w_sep(world_, 0, name, "::", "::", true); ecs_assert(e != 0, ECS_INVALID_PARAMETER, NULL); - ecs_set_alias(m_world, e, alias); - return flecs::entity(m_world, e); + ecs_set_alias(world_, e, alias); + return flecs::entity(world_, e); } inline void world::use(flecs::entity e, const char *alias) const { @@ -31120,143 +32737,163 @@ inline void world::use(flecs::entity e, const char *alias) const { const char *name = alias; if (!name) { // If no name is defined, use the entity name without the scope - name = ecs_get_name(m_world, eid); + name = ecs_get_name(world_, eid); } - ecs_set_alias(m_world, eid, name); + ecs_set_alias(world_, eid, name); } inline flecs::entity world::set_scope(const flecs::entity_t s) const { - return flecs::entity(ecs_set_scope(m_world, s)); + return flecs::entity(ecs_set_scope(world_, s)); } inline flecs::entity world::get_scope() const { - return flecs::entity(m_world, ecs_get_scope(m_world)); + return flecs::entity(world_, ecs_get_scope(world_)); } template inline flecs::entity world::set_scope() const { - return set_scope( _::cpp_type::id(m_world) ); + return set_scope( _::type::id(world_) ); } -inline entity world::lookup(const char *name, bool search_path) const { - auto e = ecs_lookup_path_w_sep(m_world, 0, name, "::", "::", search_path); +inline entity world::lookup(const char *name, const char *sep, const char *root_sep, bool recursive) const { + auto e = ecs_lookup_path_w_sep(world_, 0, name, sep, root_sep, recursive); return flecs::entity(*this, e); } +#ifndef ensure template -inline T* world::get_mut() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); - return e.get_mut(); +inline T& world::ensure() const { + flecs::entity e(world_, _::type::id(world_)); + return e.ensure(); } +#endif template inline void world::modified() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.modified(); } template inline void world::set(Second second, const First& value) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.set(second, value); } template inline void world::set(Second second, First&& value) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.set(second, value); } template inline ref world::get_ref() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.get_ref(); } template inline const T* world::get() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.get(); } template const A* world::get() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.get(); } template const First* world::get(Second second) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.get(second); } +template +T* world::get_mut() const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_mut(); +} + +template +A* world::get_mut() const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_mut(); +} + +template +First* world::get_mut(Second second) const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_mut(second); +} + template inline bool world::has() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.has(); } template inline bool world::has() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.has(); } template inline bool world::has(flecs::id_t second) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.has(second); } inline bool world::has(flecs::id_t first, flecs::id_t second) const { - flecs::entity e(m_world, first); + flecs::entity e(world_, first); return e.has(first, second); } template inline void world::add() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.add(); } template inline void world::add() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.add(); } template inline void world::add(flecs::entity_t second) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.add(second); } inline void world::add(flecs::entity_t first, flecs::entity_t second) const { - flecs::entity e(m_world, first); + flecs::entity e(world_, first); e.add(first, second); } template inline void world::remove() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.remove(); } template inline void world::remove() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.remove(); } template inline void world::remove(flecs::entity_t second) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.remove(second); } inline void world::remove(flecs::entity_t first, flecs::entity_t second) const { - flecs::entity e(m_world, first); + flecs::entity e(world_, first); e.remove(first, second); } @@ -31267,14 +32904,14 @@ inline void world::children(Func&& f) const { template inline flecs::entity world::singleton() const { - return flecs::entity(m_world, _::cpp_type::id(m_world)); + return flecs::entity(world_, _::type::id(world_)); } template inline flecs::entity world::target(int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, _::cpp_type::id(m_world), _::cpp_type::id(m_world), index)); + return flecs::entity(world_, + ecs_get_target(world_, _::type::id(world_), _::type::id(world_), index)); } template @@ -31282,44 +32919,41 @@ inline flecs::entity world::target( flecs::entity_t relationship, int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, _::cpp_type::id(m_world), relationship, index)); + return flecs::entity(world_, + ecs_get_target(world_, _::type::id(world_), relationship, index)); } inline flecs::entity world::target( flecs::entity_t relationship, int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, relationship, relationship, index)); + return flecs::entity(world_, + ecs_get_target(world_, relationship, relationship, index)); } template ::value > > inline void world::get(const Func& func) const { static_assert(arity::value == 1, "singleton component must be the only argument"); _::entity_with_delegate::invoke_get( - this->m_world, this->singleton>(), func); + this->world_, this->singleton>(), func); } template ::value > > inline void world::set(const Func& func) const { static_assert(arity::value == 1, "singleton component must be the only argument"); - _::entity_with_delegate::invoke_get_mut( - this->m_world, this->singleton>(), func); + _::entity_with_delegate::invoke_ensure( + this->world_, this->singleton>(), func); } inline flecs::entity world::get_alive(flecs::entity_t e) const { - e = ecs_get_alive(m_world, e); - return flecs::entity(m_world, e); + e = ecs_get_alive(world_, e); + return flecs::entity(world_, e); } -/* Prevent clashing with Unreal define. Unreal applications will have to use - * ecs_ensure. */ -#ifndef ensure -inline flecs::entity world::ensure(flecs::entity_t e) const { - ecs_ensure(m_world, e); - return flecs::entity(m_world, e); + +inline flecs::entity world::make_alive(flecs::entity_t e) const { + ecs_make_alive(world_, e); + return flecs::entity(world_, e); } -#endif template inline flecs::entity enum_data::entity() const { @@ -31327,26 +32961,44 @@ inline flecs::entity enum_data::entity() const { } template -inline flecs::entity enum_data::entity(int value) const { - return flecs::entity(world_, impl_.constants[value].id); +inline flecs::entity enum_data::entity(underlying_type_t value) const { + int index = index_by_value(value); + if (index >= 0) { + return flecs::entity(world_, impl_.constants[index].id); + } +#ifdef FLECS_META + // Reflection data lookup failed. Try value lookup amongst flecs::Constant relationships + flecs::world world = flecs::world(world_); + return world.query_builder() + .with(flecs::ChildOf, world.id()) + .with(flecs::Constant, world.id()) + .build() + .find([value](flecs::entity constant) { + const int32_t *constant_value = constant.get_second(flecs::Constant); + ecs_assert(constant_value, ECS_INTERNAL_ERROR, NULL); + return value == static_cast>(*constant_value); + }); +#else + return flecs::entity::null(world_); +#endif } template inline flecs::entity enum_data::entity(E value) const { - return flecs::entity(world_, impl_.constants[static_cast(value)].id); + return entity(static_cast>(value)); } /** Use provided scope for operations ran on returned world. * Operations need to be ran in a single statement. */ inline flecs::scoped_world world::scope(id_t parent) const { - return scoped_world(m_world, parent); + return scoped_world(world_, parent); } template inline flecs::scoped_world world::scope() const { - flecs::id_t parent = _::cpp_type::id(m_world); - return scoped_world(m_world, parent); + flecs::id_t parent = _::type::id(world_); + return scoped_world(world_, parent); } inline flecs::scoped_world world::scope(const char* name) const { @@ -31358,16 +33010,16 @@ inline flecs::scoped_world world::scope(const char* name) const { /** * @defgroup cpp_core Core - * @brief Core ECS functionality (entities, storage, queries) - * + * Core ECS functionality (entities, storage, queries) + * * @{ * @} */ /** * @defgroup cpp_addons Addons - * @brief C++ APIs for addons. - * + * C++ APIs for addons. + * * @{ * @} */ diff --git a/vendors/flecs/include/flecs.h b/vendors/flecs/include/flecs.h index 1d4d39b03..6c76a7888 100644 --- a/vendors/flecs/include/flecs.h +++ b/vendors/flecs/include/flecs.h @@ -5,60 +5,82 @@ * This file contains the public API for Flecs. */ +#undef min +#undef max + #ifndef FLECS_H #define FLECS_H /** * @defgroup c C API - * + * * @{ * @} */ /** * @defgroup core Core - * @brief Core ECS functionality (entities, storage, queries). - * - * \ingroup c + * @ingroup c + * Core ECS functionality (entities, storage, queries). + * * @{ */ /** * @defgroup options API defines - * @brief Defines for customizing compile time features. + * Defines for customizing compile time features. + * * @{ */ -/** \def ecs_float_t +/* Flecs version macros */ +#define FLECS_VERSION_MAJOR 4 /**< Flecs major version. */ +#define FLECS_VERSION_MINOR 0 /**< Flecs minor version. */ +#define FLECS_VERSION_PATCH 3 /**< Flecs patch version. */ + +/** Flecs version. */ +#define FLECS_VERSION FLECS_VERSION_IMPL(\ + FLECS_VERSION_MAJOR, FLECS_VERSION_MINOR, FLECS_VERSION_PATCH) + +/** @def FLECS_CONFIG_HEADER + * Allows for including a user-customizable header that specifies compile-time + * features. */ +#ifdef FLECS_CONFIG_HEADER +#include "flecs_config.h" +#endif + +/** @def ecs_float_t * Customizable precision for floating point operations */ #ifndef ecs_float_t #define ecs_float_t float #endif -/** \def ecs_ftime_t - * Customizable precision for scalar time values. Change to double precision for +/** @def ecs_ftime_t + * Customizable precision for scalar time values. Change to double precision for * processes that can run for a long time (e.g. longer than a day). */ #ifndef ecs_ftime_t #define ecs_ftime_t ecs_float_t #endif -/** \def FLECS_LEGACY - * Define when building for C89 +/** @def FLECS_LEGACY + * Define when building for C89 */ // #define FLECS_LEGACY -/** \def FLECS_NO_DEPRECATED_WARNINGS - * disables deprecated warnings - */ -#define FLECS_NO_DEPRECATED_WARNINGS - -/** \def FLECS_ACCURATE_COUNTERS - * Define to ensure that global counters used for statistics (such as the +/** @def FLECS_ACCURATE_COUNTERS + * Define to ensure that global counters used for statistics (such as the * allocation counters in the OS API) are accurate in multithreaded - * applications, at the cost of increased overhead. + * applications, at the cost of increased overhead. */ // #define FLECS_ACCURATE_COUNTERS +/** @def FLECS_DISABLE_COUNTERS + * Disables counters used for statistics. Improves performance, but + * will prevent some features that rely on statistics from working, + * like the statistics pages in the explorer. + */ +// #define FLECS_DISABLE_COUNTERS + /* Make sure provided configuration is valid */ #if defined(FLECS_DEBUG) && defined(FLECS_NDEBUG) #error "invalid configuration: cannot both define FLECS_DEBUG and FLECS_NDEBUG" @@ -67,11 +89,11 @@ #error "invalid configuration: cannot both define FLECS_DEBUG and NDEBUG" #endif -/** \def FLECS_DEBUG - * Used for input parameter checking and cheap sanity checks. There are lots of - * asserts in every part of the code, so this will slow down applications. +/** @def FLECS_DEBUG + * Used for input parameter checking and cheap sanity checks. There are lots of + * asserts in every part of the code, so this will slow down applications. */ -#if !defined(FLECS_DEBUG) && !defined(FLECS_NDEBUG) +#if !defined(FLECS_DEBUG) && !defined(FLECS_NDEBUG) #if defined(NDEBUG) #define FLECS_NDEBUG #else @@ -79,7 +101,7 @@ #endif #endif -/** \def FLECS_SANITIZE +/** @def FLECS_SANITIZE * Enables expensive checks that can detect issues early. Recommended for * running tests or when debugging issues. This will severely slow down code. */ @@ -93,84 +115,102 @@ * test with the FLECS_DEBUG or FLECS_SANITIZE flags enabled. There's a good * chance that this gives you more information about the issue! */ -/** \def FLECS_SOFT_ASSERT +/** @def FLECS_SOFT_ASSERT * Define to not abort for recoverable errors, like invalid parameters. An error * is still thrown to the console. This is recommended for when running inside a * third party runtime, such as the Unreal editor. - * + * * Note that internal sanity checks (ECS_INTERNAL_ERROR) will still abort a * process, as this gives more information than a (likely) subsequent crash. - * - * When a soft assert occurs, the code will attempt to minimize the number of + * + * When a soft assert occurs, the code will attempt to minimize the number of * side effects of the failed operation, but this may not always be possible. - * Even though an application may still be able to continue running after a soft - * assert, it should be treated as if in an undefined state. + * Even though an application may still be able to continue running after a soft + * assert, it should be treated as if in an undefined state. */ // #define FLECS_SOFT_ASSERT -/** \def FLECS_KEEP_ASSERT +/** @def FLECS_KEEP_ASSERT * By default asserts are disabled in release mode, when either FLECS_NDEBUG or * NDEBUG is defined. Defining FLECS_KEEP_ASSERT ensures that asserts are not - * disabled. This define can be combined with FLECS_SOFT_ASSERT. + * disabled. This define can be combined with FLECS_SOFT_ASSERT. */ // #define FLECS_KEEP_ASSERT +/** \def FLECS_CPP_NO_AUTO_REGISTRATION + * When set, the C++ API will require that components are registered before they + * are used. This is useful in multithreaded applications, where components need + * to be registered beforehand, and to catch issues in projects where component + * registration is mandatory. Disabling automatic component registration also + * slightly improves performance. + * The C API is not affected by this feature. + */ +// #define FLECS_CPP_NO_AUTO_REGISTRATION + /** \def FLECS_CUSTOM_BUILD * This macro lets you customize which addons to build flecs with. - * Without any addons Flecs is just a minimal ECS storage, but addons add + * Without any addons Flecs is just a minimal ECS storage, but addons add * features such as systems, scheduling and reflection. If an addon is disabled, * it is excluded from the build, so that it consumes no resources. By default * all addons are enabled. - * + * * You can customize a build by either whitelisting or blacklisting addons. To * whitelist addons, first define the FLECS_CUSTOM_BUILD macro, which disables * all addons. You can then manually select the addons you need by defining * their macro, like "FLECS_SYSTEM". - * + * * To blacklist an addon, make sure to *not* define FLECS_CUSTOM_BUILD, and * instead define the addons you don't need by defining FLECS_NO_, for - * example "FLECS_NO_SYSTEM". If there are any addons that depend on the + * example "FLECS_NO_SYSTEM". If there are any addons that depend on the * blacklisted addon, an error will be thrown during the build. - * - * Note that addons can have dependencies on each other. Addons will + * + * Note that addons can have dependencies on each other. Addons will * automatically enable their dependencies. To see the list of addons that was * compiled in a build, enable tracing before creating the world by doing: - * ecs_log_set_level(0); + * + * @code + * ecs_log_set_level(0); + * @endcode + * * which outputs the full list of addons Flecs was compiled with. */ // #define FLECS_CUSTOM_BUILD +/** @def FLECS_CPP_NO_AUTO_REGISTRATION + * When set, the C++ API will require that components are registered before they + * are used. This is useful in multithreaded applications, where components need + * to be registered beforehand, and to catch issues in projects where component + * registration is mandatory. Disabling automatic component registration also + * slightly improves performance. + * The C API is not affected by this feature. + */ +// #define FLECS_CPP_NO_AUTO_REGISTRATION + #ifndef FLECS_CUSTOM_BUILD // #define FLECS_C /**< C API convenience macros, always enabled */ #define FLECS_CPP /**< C++ API */ #define FLECS_MODULE /**< Module support */ -#define FLECS_PARSER /**< String parser for queries */ -#define FLECS_PLECS /**< ECS data definition format */ -#define FLECS_RULES /**< Constraint solver for advanced queries */ -#define FLECS_SNAPSHOT /**< Snapshot & restore ECS data */ -#define FLECS_STATS /**< Access runtime statistics */ -#define FLECS_MONITOR /**< Track runtime statistics periodically */ +#define FLECS_SCRIPT /**< ECS data definition format */ +#define FLECS_STATS /**< Track runtime statistics */ #define FLECS_METRICS /**< Expose component data as statistics */ #define FLECS_ALERTS /**< Monitor conditions for errors */ #define FLECS_SYSTEM /**< System support */ #define FLECS_PIPELINE /**< Pipeline support */ #define FLECS_TIMER /**< Timer support */ #define FLECS_META /**< Reflection support */ -#define FLECS_META_C /**< Utilities for populating reflection data */ #define FLECS_UNITS /**< Builtin standard units */ -#define FLECS_EXPR /**< Parsing strings to/from component values */ #define FLECS_JSON /**< Parsing JSON to/from component values */ #define FLECS_DOC /**< Document entities & components */ -#define FLECS_COREDOC /**< Documentation for core entities & components */ #define FLECS_LOG /**< When enabled ECS provides more detailed logs */ #define FLECS_APP /**< Application addon */ #define FLECS_OS_API_IMPL /**< Default implementation for OS API */ #define FLECS_HTTP /**< Tiny HTTP server for connecting to remote UI */ #define FLECS_REST /**< REST API for querying application data */ // #define FLECS_JOURNAL /**< Journaling addon (disabled by default) */ +// #define FLECS_PERF_TRACE /**< Enable performance tracing (disabled by default) */ #endif // ifndef FLECS_CUSTOM_BUILD -/** \def FLECS_LOW_FOOTPRINT +/** @def FLECS_LOW_FOOTPRINT * Set a number of constants to values that decrease memory footprint, at the * cost of decreased performance. */ // #define FLECS_LOW_FOOTPRINT @@ -182,22 +222,22 @@ #define FLECS_USE_OS_ALLOC #endif -/** \def FLECS_HI_COMPONENT_ID - * This constant can be used to balance between performance and memory +/** @def FLECS_HI_COMPONENT_ID + * This constant can be used to balance between performance and memory * utilization. The constant is used in two ways: * - Entity ids 0..FLECS_HI_COMPONENT_ID are reserved for component ids. * - Used as lookup array size in table edges. - * + * * Increasing this value increases the size of the lookup array, which allows - * fast table traversal, which improves performance of ECS add/remove + * fast table traversal, which improves performance of ECS add/remove * operations. Component ids that fall outside of this range use a regular map * lookup, which is slower but more memory efficient. */ #ifndef FLECS_HI_COMPONENT_ID #define FLECS_HI_COMPONENT_ID (256) #endif -/** \def FLECS_HI_ID_RECORD_ID - * This constant can be used to balance between performance and memory +/** @def FLECS_HI_ID_RECORD_ID + * This constant can be used to balance between performance and memory * utilization. The constant is used to determine the size of the id record * lookup array. Id values that fall outside of this range use a regular map * lookup, which is slower but more memory efficient. @@ -206,7 +246,7 @@ #define FLECS_HI_ID_RECORD_ID (1024) #endif -/** \def FLECS_SPARSE_PAGE_BITS +/** @def FLECS_SPARSE_PAGE_BITS * This constant is used to determine the number of bits of an id that is used * to determine the page index when used with a sparse set. The number of bits * determines the page size, which is (1 << bits). @@ -215,49 +255,69 @@ #define FLECS_SPARSE_PAGE_BITS (12) #endif -/** \def FLECS_ENTITY_PAGE_BITS +/** @def FLECS_ENTITY_PAGE_BITS * Same as FLECS_SPARSE_PAGE_BITS, but for the entity index. */ #ifndef FLECS_ENTITY_PAGE_BITS #define FLECS_ENTITY_PAGE_BITS (12) #endif -/** \def FLECS_USE_OS_ALLOC +/** @def FLECS_USE_OS_ALLOC * When enabled, Flecs will use the OS allocator provided in the OS API directly * instead of the builtin block allocator. This can decrease memory utilization * as memory will be freed more often, at the cost of decreased performance. */ // #define FLECS_USE_OS_ALLOC -/** \def FLECS_ID_DESC_MAX +/** @def FLECS_ID_DESC_MAX * Maximum number of ids to add ecs_entity_desc_t / ecs_bulk_desc_t */ #ifndef FLECS_ID_DESC_MAX #define FLECS_ID_DESC_MAX (32) #endif -/** \def FLECS_TERM_DESC_MAX - * Maximum number of terms in ecs_filter_desc_t */ -#define FLECS_TERM_DESC_MAX (16) - /** \def FLECS_EVENT_DESC_MAX * Maximum number of events in ecs_observer_desc_t */ +#ifndef FLECS_EVENT_DESC_MAX #define FLECS_EVENT_DESC_MAX (8) +#endif -/** \def FLECS_VARIABLE_COUNT_MAX +/** @def FLECS_VARIABLE_COUNT_MAX * Maximum number of query variables per query */ #define FLECS_VARIABLE_COUNT_MAX (64) -/** \def FLECS_QUERY_SCOPE_NESTING_MAX +/** \def FLECS_TERM_COUNT_MAX + * Maximum number of terms in queries. Should not exceed 64. */ +#ifndef FLECS_TERM_COUNT_MAX +#define FLECS_TERM_COUNT_MAX 32 +#endif + +/** \def FLECS_TERM_ARG_COUNT_MAX + * Maximum number of arguments for a term. */ +#ifndef FLECS_TERM_ARG_COUNT_MAX +#define FLECS_TERM_ARG_COUNT_MAX (16) +#endif + +/** \def FLECS_QUERY_VARIABLE_COUNT_MAX + * Maximum number of query variables per query. Should not exceed 128. */ +#ifndef FLECS_QUERY_VARIABLE_COUNT_MAX +#define FLECS_QUERY_VARIABLE_COUNT_MAX (64) +#endif + +/** @def FLECS_QUERY_SCOPE_NESTING_MAX * Maximum nesting depth of query scopes */ +#ifndef FLECS_QUERY_SCOPE_NESTING_MAX #define FLECS_QUERY_SCOPE_NESTING_MAX (8) +#endif /** @} */ #include "flecs/private/api_defines.h" -#include "flecs/private/vec.h" /* Vector datatype */ -#include "flecs/private/sparse.h" /* Sparse set */ -#include "flecs/private/block_allocator.h" /* Block allocator */ -#include "flecs/private/map.h" /* Map */ -#include "flecs/private/allocator.h" /* Allocator */ -#include "flecs/private/strbuf.h" /* String builder */ +#include "flecs/datastructures/vec.h" /* Vector datatype */ +#include "flecs/datastructures/sparse.h" /* Sparse set */ +#include "flecs/datastructures/block_allocator.h" /* Block allocator */ +#include "flecs/datastructures/stack_allocator.h" /* Stack allocator */ +#include "flecs/datastructures/map.h" /* Map */ +#include "flecs/datastructures/switch_list.h" /* Switch list */ +#include "flecs/datastructures/allocator.h" /* Allocator */ +#include "flecs/datastructures/strbuf.h" /* String builder */ #include "flecs/os_api.h" /* Abstraction for operating system functions */ #ifdef __cplusplus @@ -266,140 +326,102 @@ extern "C" { /** * @defgroup api_types API types - * @brief Public API types. + * Public API types. + * * @{ */ /** * @defgroup core_types Core API Types - * @brief Types for core API objects. + * Types for core API objects. + * * @{ */ -/** Ids are the things that can be added to an entity. +/** Ids are the things that can be added to an entity. * An id can be an entity or pair, and can have optional id flags. */ typedef uint64_t ecs_id_t; /** An entity identifier. - * Entity ids consist out of a number unique to the entity in the lower 32 bits, - * and a counter used to track entity liveliness in the upper 32 bits. When an - * id is recycled, its generation count is increased. This causes recycled ids + * Entity ids consist out of a number unique to the entity in the lower 32 bits, + * and a counter used to track entity liveliness in the upper 32 bits. When an + * id is recycled, its generation count is increased. This causes recycled ids * to be very large (>4 billion), which is normal. */ typedef ecs_id_t ecs_entity_t; /** A type is a list of (component) ids. - * Types are used to communicate the "type" of an entity. In most type systems a - * typeof operation returns a single type. In ECS however, an entity can have + * Types are used to communicate the "type" of an entity. In most type systems a + * typeof operation returns a single type. In ECS however, an entity can have * multiple components, which is why an ECS type consists of a vector of ids. - * + * * The component ids of a type are sorted, which ensures that it doesn't matter * in which order components are added to an entity. For example, if adding - * Position then Velocity would result in type [Position, Velocity], first + * Position then Velocity would result in type [Position, Velocity], first * adding Velocity then Position would also result in type [Position, Velocity]. - * - * Entities are grouped together by type in the ECS storage in tables. The - * storage has exactly one table per unique type that is created by the + * + * Entities are grouped together by type in the ECS storage in tables. The + * storage has exactly one table per unique type that is created by the * application that stores all entities and components for that type. This is * also referred to as an archetype. */ typedef struct { - ecs_id_t *array; - int32_t count; + ecs_id_t *array; /**< Array with ids. */ + int32_t count; /**< Number of elements in array. */ } ecs_type_t; -/** A world is the container for all ECS data and supporting features. +/** A world is the container for all ECS data and supporting features. * Applications can have multiple worlds, though in most cases will only need * one. Worlds are isolated from each other, and can have separate sets of * systems, components, modules etc. - * - * If an application has multiple worlds with overlapping components, it is + * + * If an application has multiple worlds with overlapping components, it is * common (though not strictly required) to use the same component ids across * worlds, which can be achieved by declaring a global component id variable. * To do this in the C API, see the entities/fwd_component_decl example. The * C++ API automatically synchronizes component ids between worlds. - * + * * Component id conflicts between worlds can occur when a world has already used * an id for something else. There are a few ways to avoid this: - * + * * - Ensure to register the same components in each world, in the same order. - * - Create a dummy world in which all components are preregistered which + * - Create a dummy world in which all components are preregistered which * initializes the global id variables. - * - * In some use cases, typically when writing tests, multiple worlds are created - * and deleted with different components, registered in different order. To + * + * In some use cases, typically when writing tests, multiple worlds are created + * and deleted with different components, registered in different order. To * ensure isolation between tests, the C++ API has a `flecs::reset` function * that forces the API to ignore the old component ids. */ typedef struct ecs_world_t ecs_world_t; +/** A stage enables modification while iterating and from multiple threads */ +typedef struct ecs_stage_t ecs_stage_t; + /** A table stores entities and components for a specific type. */ typedef struct ecs_table_t ecs_table_t; /** A term is a single element in a query. */ typedef struct ecs_term_t ecs_term_t; -/** A filter is an iterable data structure that describes a query. - * Filters are used by the various query implementations in Flecs, like queries, - * observers and rules, to describe a query. Filters themselves can also be - * iterated. */ -typedef struct ecs_filter_t ecs_filter_t; - -/** A query that caches its results. - * Queries are the fastest mechanism for finding and iterating over entities. - * Queries cache results as a list of matching tables (vs. individual entities). - * - * This has several advantages: - * - Matching is only performed when new tables are created, which is infrequent - * - Iterating a query just walks over the cache, no actual searching is needed - * - Iteration is table-based, which allows for direct iteration of underlying - * component arrays, providing good cache locality. - * - * While queries are the fastest mechanism to iterate entiites, they are slower - * to create than other mechanisms, as a result of having to build the cache - * first. For this reason queries are best suited for use cases where a single - * query can be reused many times (like is the case for systems). - * - * For ad-hoc queries it is recommended to use filters or rules instead, which - * are slower to iterate, but much faster to create. Applications should at all - * times avoid frequent creation/deletion of queries. */ +/** A query returns entities matching a list of constraints. */ typedef struct ecs_query_t ecs_query_t; -/** A rule is a query with advanced graph traversal features. - * Rules are fast uncached queries with support for advanced graph features such - * as the usage of query variables. A simple example of a rule that matches all - * spaceship entities docked to a planet: - * SpaceShip, (DockedTo, $planet), Planet($planet) - * - * Here, the rule traverses the DockedTo relationship, and matches Planet on the - * target of this relationship. Through the usage of variables rules can match - * arbitrary patterns against entity graphs. Other features supported - * exclusively by rules are: - * - Component inheritance - * - Transitivity - * - * Rules have similar iteration performance to filters, but are slower than - * queries. Rules and filters will eventually be merged into a single query - * implementation. Features still lacking for rules are: - * - Up traversal - * - AndFrom, OrFrom, NotFrom operators - */ -typedef struct ecs_rule_t ecs_rule_t; - /** An observer is a system that is invoked when an event matches its query. * Observers allow applications to respond to specific events, such as adding or * removing a component. Observers are created by both specifying a query and * a list of event kinds that should be listened for. An example of an observer * that triggers when a Position component is added to an entity (in C++): - * - * world.observer() - * .event(flecs::OnAdd) - * .each([](Position& p) { - * // called when Position is added to an entity - * }); - * - * Observer queries can be as complex as filters. Observers only trigger when - * the source of the event matches the full observer query. For example, an - * OnAdd observer for Position, Velocity will only trigger after both components - * have been added to the entity. */ + * + * @code + * world.observer() + * .event(flecs::OnAdd) + * .each([](Position& p) { + * // called when Position is added to an entity + * }); + * @endcode + * + * Observers only trigger when the source of the event matches the full observer + * query. For example, an OnAdd observer for Position, Velocity will only + * trigger after both components have been added to the entity. */ typedef struct ecs_observer_t ecs_observer_t; /** An observable produces events that can be listened for by an observer. @@ -407,30 +429,29 @@ typedef struct ecs_observer_t ecs_observer_t; * observable objects as well. */ typedef struct ecs_observable_t ecs_observable_t; -/* Type used for iterating iterable objects. - * Iterators are a common interface across iterable objects (world, filters, - * rules, queries, systems, observers) to provide applications with information - * about the currently iterated result, and to store any state required for the +/** Type used for iterating iterable objects. + * Iterators are objects that provide applications with information + * about the currently iterated result, and store any state required for the * iteration. */ typedef struct ecs_iter_t ecs_iter_t; /** A ref is a fast way to fetch a component for a specific entity. - * Refs are a faster alternative to repeatedly calling ecs_get for the same + * Refs are a faster alternative to repeatedly calling ecs_get() for the same * entity/component combination. When comparing the performance of getting a ref - * to calling ecs_get, a ref is typically 3-5x faster. - * + * to calling ecs_get(), a ref is typically 3-5x faster. + * * Refs achieve this performance by caching internal data structures associated - * with the entity and component on the ecs_ref_t object that otherwise would + * with the entity and component on the ecs_ref_t object that otherwise would * have to be looked up. */ typedef struct ecs_ref_t ecs_ref_t; -/** Type hooks are callbacks associated with component lifecycle events. +/** Type hooks are callbacks associated with component lifecycle events. * Typical examples of lifecycle events are construction, destruction, copying * and moving of components. */ typedef struct ecs_type_hooks_t ecs_type_hooks_t; /** Type information. - * Contains information about a (component) type, such as its size and + * Contains information about a (component) type, such as its size and * alignment and type hooks. */ typedef struct ecs_type_info_t ecs_type_info_t; @@ -440,25 +461,19 @@ typedef struct ecs_record_t ecs_record_t; /** Information about a (component) id, such as type info and tables with the id */ typedef struct ecs_id_record_t ecs_id_record_t; -/** Information about where in a table a specific (component) id is stored. */ -typedef struct ecs_table_record_t ecs_table_record_t; - /** A poly object. * A poly (short for polymorph) object is an object that has a variable list of * capabilities, determined by a mixin table. This is the current list of types * in the flecs API that can be used as an ecs_poly_t: - * + * * - ecs_world_t * - ecs_stage_t * - ecs_query_t - * - ecs_filter_t - * - ecs_rule_t - * - (more to come) - * + * * Functions that accept an ecs_poly_t argument can accept objects of these * types. If the object does not have the requested mixin the API will throw an * assert. - * + * * The poly/mixin framework enables partially overlapping features to be * implemented once, and enables objects of different types to interact with * each other depending on what mixins they have, rather than their type @@ -472,26 +487,55 @@ typedef struct ecs_mixins_t ecs_mixins_t; /** Header for ecs_poly_t objects. */ typedef struct ecs_header_t { - int32_t magic; /* Magic number verifying it's a flecs object */ - int32_t type; /* Magic number indicating which type of flecs object */ - ecs_mixins_t *mixins; /* Table with offsets to (optional) mixins */ + int32_t magic; /**< Magic number verifying it's a flecs object */ + int32_t type; /**< Magic number indicating which type of flecs object */ + int32_t refcount; /**< Refcount, to enable RAII handles */ + ecs_mixins_t *mixins; /**< Table with offsets to (optional) mixins */ } ecs_header_t; +/** Record for entity index */ +struct ecs_record_t { + ecs_id_record_t *idr; /**< Id record to (*, entity) for target entities */ + ecs_table_t *table; /**< Identifies a type (and table) in world */ + uint32_t row; /**< Table row of the entity */ + int32_t dense; /**< Index in dense array of entity index */ +}; + +/** Header for table cache elements. */ +typedef struct ecs_table_cache_hdr_t { + struct ecs_table_cache_t *cache; /**< Table cache of element. Of type ecs_id_record_t* for component index elements. */ + ecs_table_t *table; /**< Table associated with element. */ + struct ecs_table_cache_hdr_t *prev, *next; /**< Next/previous elements for id in table cache. */ + bool empty; /**< Whether element is in empty list. */ +} ecs_table_cache_hdr_t; + +/** Metadata describing where a component id is stored in a table. + * This type is used as element type for the component index table cache. One + * record exists per table/component in the table. Only records for wildcard ids + * can have a count > 1. */ +typedef struct ecs_table_record_t { + ecs_table_cache_hdr_t hdr; /**< Table cache header */ + int16_t index; /**< First type index where id occurs in table */ + int16_t count; /**< Number of times id occurs in table */ + int16_t column; /**< First column index where id occurs */ +} ecs_table_record_t; + /** @} */ /** * @defgroup function_types Function types. - * @brief Function callback types. + * Function callback types. + * * @{ */ /** Function prototype for runnables (systems, observers). * The run callback overrides the default behavior for iterating through the * results of a runnable object. - * + * * The default runnable iterates the iterator, and calls an iter_action (see * below) for each returned result. - * + * * @param it The iterator to be iterated by the runnable. */ typedef void (*ecs_run_action_t)( @@ -500,46 +544,29 @@ typedef void (*ecs_run_action_t)( /** Function prototype for iterables. * A system may invoke a callback multiple times, typically once for each * matched table. - * + * * @param it The iterator containing the data for the current match. */ typedef void (*ecs_iter_action_t)( ecs_iter_t *it); -/** Function prototype for creating an iterator from a poly. - * Used to create iterators from poly objects with the iterable mixin. When a - * filter is provided, an array of two iterators must be passed to the function. - * This allows the mixin implementation to create a chained iterator when - * necessary, which requires two iterator objects. - * - * @param world The world or stage for which to create the iterator. - * @param iterable An iterable poly object. - * @param it The iterator to create (out parameter) - * @param filter Optional term to filter results. - */ -typedef void (*ecs_iter_init_action_t)( - const ecs_world_t *world, - const ecs_poly_t *iterable, - ecs_iter_t *it, - ecs_term_t *filter); - /** Function prototype for iterating an iterator. - * Stored inside initialized iterators. This allows an application to * iterate + * Stored inside initialized iterators. This allows an application to iterate * an iterator without needing to know what created it. - * + * * @param it The iterator to iterate. * @return True if iterator has no more results, false if it does. */ typedef bool (*ecs_iter_next_action_t)( - ecs_iter_t *it); + ecs_iter_t *it); /** Function prototype for freeing an iterator. * Free iterator resources. - * + * * @param it The iterator to free. */ typedef void (*ecs_iter_fini_action_t)( - ecs_iter_t *it); + ecs_iter_t *it); /** Callback used for comparing components */ typedef int (*ecs_order_by_action_t)( @@ -566,13 +593,13 @@ typedef uint64_t (*ecs_group_by_action_t)( ecs_id_t group_id, void *ctx); -/* Callback invoked when a query creates a new group. */ +/** Callback invoked when a query creates a new group. */ typedef void* (*ecs_group_create_action_t)( ecs_world_t *world, uint64_t group_id, void *group_by_ctx); /* from ecs_query_desc_t */ -/* Callback invoked when a query deletes an existing group. */ +/** Callback invoked when a query deletes an existing group. */ typedef void (*ecs_group_delete_action_t)( ecs_world_t *world, uint64_t group_id, @@ -581,7 +608,7 @@ typedef void (*ecs_group_delete_action_t)( /** Initialization action for modules */ typedef void (*ecs_module_action_t)( - ecs_world_t *world); + ecs_world_t *world); /** Action callback on world exit */ typedef void (*ecs_fini_action_t)( @@ -599,7 +626,7 @@ typedef int (*ecs_compare_action_t)( /** Callback used for hashing values */ typedef uint64_t (*ecs_hash_value_action_t)( - const void *ptr); + const void *ptr); /** Constructor/destructor callback */ typedef void (*ecs_xtor_t)( @@ -621,29 +648,16 @@ typedef void (*ecs_move_t)( int32_t count, const ecs_type_info_t *type_info); -/* Destructor function for poly objects */ -typedef void (*ecs_poly_dtor_t)( +/** Destructor function for poly objects. */ +typedef void (*flecs_poly_dtor_t)( ecs_poly_t *poly); /** @} */ -/** - * @defgroup mixins Poly mixin types. - * @brief Mixin types for poly mechanism. - * @{ - */ - -/** Iterable mixin. - * Allows its container to be iterated. */ -typedef struct ecs_iterable_t { - ecs_iter_init_action_t init; /**< Callback that creates iterator. */ -} ecs_iterable_t; - -/** @} */ - /** * @defgroup query_types Query descriptor types. - * @brief Types used to describe queries. + * Types used to describe queries. + * * @{ */ @@ -651,6 +665,7 @@ typedef struct ecs_iterable_t { typedef enum ecs_inout_kind_t { EcsInOutDefault, /**< InOut for regular terms, In for shared terms */ EcsInOutNone, /**< Term is neither read nor written */ + EcsInOutFilter, /**< Same as InOutNone + prevents term from triggering observers */ EcsInOut, /**< Term is both read and written */ EcsIn, /**< Term is only read */ EcsOut, /**< Term is only written */ @@ -667,147 +682,187 @@ typedef enum ecs_oper_kind_t { EcsNotFrom, /**< Term must match none of the components from term id */ } ecs_oper_kind_t; +/** Specify cache policy for query */ +typedef enum ecs_query_cache_kind_t { + EcsQueryCacheDefault, /**< Behavior determined by query creation context */ + EcsQueryCacheAuto, /**< Cache query terms that are cacheable */ + EcsQueryCacheAll, /**< Require that all query terms can be cached */ + EcsQueryCacheNone, /**< No caching */ +} ecs_query_cache_kind_t; + /* Term id flags */ -#define EcsSelf (1u << 1) /**< Match on self */ -#define EcsUp (1u << 2) /**< Match by traversing upwards */ -#define EcsDown (1u << 3) /**< Match by traversing downwards (derived, cannot be set) */ -#define EcsTraverseAll (1u << 4) /**< Match all entities encountered through traversal */ -#define EcsCascade (1u << 5) /**< Sort results breadth first */ -#define EcsDesc (1u << 6) /**< Iterate groups in descending order */ -#define EcsParent (1u << 7) /**< Short for up(ChildOf) */ -#define EcsIsVariable (1u << 8) /**< Term id is a variable */ -#define EcsIsEntity (1u << 9) /**< Term id is an entity */ -#define EcsIsName (1u << 10) /**< Term id is a name (don't attempt to lookup as entity) */ -#define EcsFilter (1u << 11) /**< Prevent observer from triggering on term */ -#define EcsTraverseFlags (EcsUp|EcsDown|EcsTraverseAll|EcsSelf|EcsCascade|EcsDesc|EcsParent) - -/* Term flags discovered & set during filter creation. Mostly used internally to - * store information relevant to queries. */ -#define EcsTermMatchAny (1u << 0) -#define EcsTermMatchAnySrc (1u << 1) -#define EcsTermSrcFirstEq (1u << 2) -#define EcsTermSrcSecondEq (1u << 3) -#define EcsTermTransitive (1u << 4) -#define EcsTermReflexive (1u << 5) -#define EcsTermIdInherited (1u << 6) -#define EcsTermIsTrivial (1u << 7) -#define EcsTermNoData (1u << 8) - -/* Term flags used for term iteration */ -#define EcsTermMatchDisabled (1u << 7) -#define EcsTermMatchPrefab (1u << 8) - -/** Type that describes a single identifier in a term */ -typedef struct ecs_term_id_t { + +/** Match on self. + * Can be combined with other term flags on the ecs_term_t::flags_ field. + * \ingroup queries + */ +#define EcsSelf (1llu << 63) + +/** Match by traversing upwards. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsUp (1llu << 62) + +/** Traverse relationship transitively. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsTrav (1llu << 61) + +/** Sort results breadth first. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsCascade (1llu << 60) + +/** Iterate groups in descending order. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsDesc (1llu << 59) + +/** Term id is a variable. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsIsVariable (1llu << 58) + +/** Term id is an entity. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsIsEntity (1llu << 57) + +/** Term id is a name (don't attempt to lookup as entity). + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsIsName (1llu << 56) + +/** All term traversal flags. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsTraverseFlags (EcsSelf|EcsUp|EcsTrav|EcsCascade|EcsDesc) + +/** All term reference kind flags. + * Can be combined with other term flags on the ecs_term_ref_t::id field. + * \ingroup queries + */ +#define EcsTermRefFlags (EcsTraverseFlags|EcsIsVariable|EcsIsEntity|EcsIsName) + +/** Type that describes a reference to an entity or variable in a term. */ +typedef struct ecs_term_ref_t { ecs_entity_t id; /**< Entity id. If left to 0 and flags does not * specify whether id is an entity or a variable - * the id will be initialized to EcsThis. + * the id will be initialized to #EcsThis. * To explicitly set the id to 0, leave the id - * member to 0 and set EcsIsEntity in flags. */ + * member to 0 and set #EcsIsEntity in flags. */ const char *name; /**< Name. This can be either the variable name - * (when the EcsIsVariable flag is set) or an + * (when the #EcsIsVariable flag is set) or an * entity name. When ecs_term_t::move is true, * the API assumes ownership over the string and * will free it when the term is destroyed. */ +} ecs_term_ref_t; - ecs_entity_t trav; /**< Relationship to traverse when looking for the - * component. The relationship must have - * the Traversable property. Default is IsA. */ - - ecs_flags32_t flags; /**< Term flags */ -} ecs_term_id_t; - -/** Type that describes a term (single element in a query) */ +/** Type that describes a term (single element in a query). */ struct ecs_term_t { ecs_id_t id; /**< Component id to be matched by term. Can be * set directly, or will be populated from the * first/second members, which provide more * flexibility. */ - ecs_term_id_t src; /**< Source of term */ - ecs_term_id_t first; /**< Component or first element of pair */ - ecs_term_id_t second; /**< Second element of pair */ - - ecs_inout_kind_t inout; /**< Access to contents matched by term */ - ecs_oper_kind_t oper; /**< Operator of term */ - - ecs_id_t id_flags; /**< Id flags of term id */ - char *name; /**< Name of term */ + ecs_term_ref_t src; /**< Source of term */ + ecs_term_ref_t first; /**< Component or first element of pair */ + ecs_term_ref_t second; /**< Second element of pair */ - int32_t field_index; /**< Index of field for term in iterator */ - ecs_id_record_t *idr; /**< Cached pointer to internal index */ + ecs_entity_t trav; /**< Relationship to traverse when looking for the + * component. The relationship must have + * the `Traversable` property. Default is `IsA`. */ - ecs_flags16_t flags; /**< Flags that help eval, set by ecs_filter_init */ + int16_t inout; /**< Access to contents matched by term */ + int16_t oper; /**< Operator of term */ - bool move; /**< Used by internals */ + int8_t field_index; /**< Index of field for term in iterator */ + ecs_flags16_t flags_; /**< Flags that help eval, set by ecs_query_init() */ }; -/** Use $this variable to initialize user-allocated filter object */ -FLECS_API extern ecs_filter_t ECS_FILTER_INIT; - -/** Filters alllow for ad-hoc quick filtering of entity tables. */ -struct ecs_filter_t { - ecs_header_t hdr; - - int8_t term_count; /**< Number of elements in terms array */ - int8_t field_count; /**< Number of fields in iterator for filter */ - ecs_flags32_t flags; /**< Filter flags */ - ecs_flags64_t data_fields; /**< Bitset with fields that have data */ +/** Queries are lists of constraints (terms) that match entities. + * Created with ecs_query_init(). + */ +struct ecs_query_t { + ecs_header_t hdr; /**< Object header */ + + ecs_term_t terms[FLECS_TERM_COUNT_MAX]; /**< Query terms */ + int32_t sizes[FLECS_TERM_COUNT_MAX]; /**< Component sizes. Indexed by field */ + ecs_id_t ids[FLECS_TERM_COUNT_MAX]; /**< Component ids. Indexed by field */ + + ecs_flags32_t flags; /**< Query flags */ + int8_t var_count; /**< Number of query variables */ + int8_t term_count; /**< Number of query terms */ + int8_t field_count; /**< Number of fields returned by query */ + + /* Bitmasks for quick field information lookups */ + ecs_termset_t fixed_fields; /**< Fields with a fixed source */ + ecs_termset_t var_fields; /**< Fields with non-$this variable source */ + ecs_termset_t static_id_fields; /**< Fields with a static (component) id */ + ecs_termset_t data_fields; /**< Fields that have data */ + ecs_termset_t write_fields; /**< Fields that write data */ + ecs_termset_t read_fields; /**< Fields that read data */ + ecs_termset_t row_fields; /**< Fields that must be acquired with field_at */ + ecs_termset_t shared_readonly_fields; /**< Fields that don't write shared data */ + ecs_termset_t set_fields; /**< Fields that will be set */ + + ecs_query_cache_kind_t cache_kind; /**< Caching policy of query */ - ecs_term_t *terms; /**< Array containing terms for filter */ - char *variable_names[1]; /**< Placeholder variable names array */ - int32_t *sizes; /**< Field size (same for each result) */ - - /* Mixins */ - ecs_entity_t entity; /**< Entity associated with filter (optional) */ - ecs_iterable_t iterable; /**< Iterable mixin */ - ecs_poly_dtor_t dtor; /**< Dtor mixin */ - ecs_world_t *world; /**< World mixin */ + char **vars; /**< Array with variable names for iterator */ + + void *ctx; /**< User context to pass to callback */ + void *binding_ctx; /**< Context to be used for language bindings */ + + ecs_entity_t entity; /**< Entity associated with query (optional) */ + ecs_world_t *real_world; /**< Actual world. */ + ecs_world_t *world; /**< World or stage query was created with. */ + + int32_t eval_count; /**< Number of times query is evaluated */ }; -/* An observer reacts to events matching a filter */ +/** An observer reacts to events matching a query. + * Created with ecs_observer_init(). + */ struct ecs_observer_t { - ecs_header_t hdr; + ecs_header_t hdr; /**< Object header */ - ecs_filter_t filter; /**< Query for observer */ + ecs_query_t *query; /**< Observer query */ - /* Observer events */ + /** Observer events */ ecs_entity_t events[FLECS_EVENT_DESC_MAX]; - int32_t event_count; - + int32_t event_count; /**< Number of events */ + ecs_iter_action_t callback; /**< See ecs_observer_desc_t::callback */ ecs_run_action_t run; /**< See ecs_observer_desc_t::run */ - void *ctx; /**< Callback context */ - void *binding_ctx; /**< Binding context (for language bindings) */ + void *ctx; /**< Observer context */ + void *callback_ctx; /**< Callback language binding context */ + void *run_ctx; /**< Run language binding context */ ecs_ctx_free_t ctx_free; /**< Callback to free ctx */ - ecs_ctx_free_t binding_ctx_free; /**< Callback to free binding_ctx */ + ecs_ctx_free_t callback_ctx_free; /**< Callback to free callback_ctx */ + ecs_ctx_free_t run_ctx_free; /**< Callback to free run_ctx */ 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) */ - - bool is_monitor; /**< If true, the observer only triggers when the - * filter did not match with the entity before - * the event happened. */ - - bool is_multi; /**< If true, the observer triggers on more than one term */ - - /* Mixins */ - ecs_poly_dtor_t dtor; + ecs_world_t *world; /**< The world */ + ecs_entity_t entity; /**< Entity associated with observer */ }; /** @} */ -/** Type that contains component lifecycle callbacks. - * - * \ingroup components +/** Type that contains component lifecycle callbacks. + * + * @ingroup components */ struct ecs_type_hooks_t { ecs_xtor_t ctor; /**< ctor */ @@ -842,21 +897,24 @@ struct ecs_type_hooks_t { * to respond to changes on itself before others can. */ ecs_iter_action_t on_set; - /** Callback that is invoked when an instance of the component is removed. + /** Callback that is invoked when an instance of the component is removed. * This callback is invoked after the triggers are invoked, and before the * destructor is invoked. */ ecs_iter_action_t on_remove; - void *ctx; /**< User defined context */ - void *binding_ctx; /**< Language binding context */ + void *ctx; /**< User defined context */ + void *binding_ctx; /**< Language binding context */ + void *lifecycle_ctx; /**< Component lifecycle context (see meta add-on)*/ + + ecs_ctx_free_t ctx_free; /**< Callback to free ctx */ + ecs_ctx_free_t binding_ctx_free; /**< Callback to free binding_ctx */ + ecs_ctx_free_t lifecycle_ctx_free; /**< Callback to free lifecycle_ctx */ - ecs_ctx_free_t ctx_free; /**< Callback to free ctx */ - ecs_ctx_free_t binding_ctx_free; /**< Callback to free binding_ctx */ }; -/** Type that contains component information (passed to ctors/dtors/...) - * - * \ingroup components +/** Type that contains component information (passed to ctors/dtors/...) + * + * @ingroup components */ struct ecs_type_info_t { ecs_size_t size; /**< Size of type */ @@ -868,18 +926,25 @@ struct ecs_type_info_t { #include "flecs/private/api_types.h" /* Supporting API types */ #include "flecs/private/api_support.h" /* Supporting API functions */ -#include "flecs/private/vec.h" /* Vector */ -#include "flecs/private/hashmap.h" /* Hashmap */ +#include "flecs/datastructures/hashmap.h" /* Hashmap */ -/** Used with ecs_entity_init - * - * \ingroup entities - */ +/** Utility to hold a value of a dynamic type. */ +typedef struct ecs_value_t { + ecs_entity_t type; /**< Type of value. */ + void *ptr; /**< Pointer to value. */ +} ecs_value_t; + +/** Used with ecs_entity_init(). + * + * @ingroup entities + */ typedef struct ecs_entity_desc_t { - int32_t _canary; + int32_t _canary; /**< Used for validity testing. Must be 0. */ ecs_entity_t id; /**< Set to modify existing entity (optional) */ + ecs_entity_t parent; /**< Parent entity. */ + const char *name; /**< Name of the entity. If no entity is provided, an * entity with this name will be looked up first. When * an entity is provided, the name will be verified @@ -905,19 +970,22 @@ typedef struct ecs_entity_desc_t { * components) will be used to create the entity, if * no id is specified. */ - /** Array of ids to add to the new or existing entity. */ - ecs_id_t add[FLECS_ID_DESC_MAX]; + /** 0-terminated array of ids to add to the entity. */ + const ecs_id_t *add; + + /** 0-terminated array of values to set on the entity. */ + const ecs_value_t *set; /** String expression with components to add */ const char *add_expr; } ecs_entity_desc_t; -/** Used with ecs_bulk_init - * - * \ingroup entities +/** Used with ecs_bulk_init(). + * + * @ingroup entities */ -typedef struct ecs_bulk_desc_t { - int32_t _canary; +typedef struct ecs_bulk_desc_t { + int32_t _canary; /**< Used for validity testing. Must be 0. */ ecs_entity_t *entities; /**< Entities to bulk insert. Entity ids provided by * the application must be empty (cannot @@ -928,7 +996,7 @@ typedef struct ecs_bulk_desc_t { ecs_id_t ids[FLECS_ID_DESC_MAX]; /**< Ids to create the entities with */ - void **data; /**< Array with component data to insert. Each element in + void **data; /**< Array with component data to insert. Each element in * the array must correspond with an element in the ids * array. If an element in the ids array is a tag, the * data array must contain a NULL. An element may be @@ -942,13 +1010,13 @@ typedef struct ecs_bulk_desc_t { } ecs_bulk_desc_t; -/** Used with ecs_component_init. - * - * \ingroup components +/** Used with ecs_component_init(). + * + * @ingroup components */ typedef struct ecs_component_desc_t { - int32_t _canary; - + int32_t _canary; /**< Used for validity testing. Must be 0. */ + /** Existing entity to associate with observer (optional) */ ecs_entity_t entity; @@ -956,82 +1024,189 @@ typedef struct ecs_component_desc_t { ecs_type_info_t type; } ecs_component_desc_t; -/** Used with ecs_filter_init. +/** Iterator. + * Used for iterating queries. The ecs_iter_t type contains all the information + * that is provided by a query, and contains all the state required for the + * iterator code. * - * \ingroup filters - */ -typedef struct ecs_filter_desc_t { - int32_t _canary; - - /** Terms of the filter. If a filter has more terms than - * FLECS_TERM_DESC_MAX use terms_buffer */ - ecs_term_t terms[FLECS_TERM_DESC_MAX]; + * Functions that create iterators accept as first argument the world, and as + * second argument the object they iterate. For example: + * + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * @endcode + * + * When this code is called from a system, it is important to use the world + * provided by its iterator object to ensure thread safety. For example: + * + * @code + * void Collide(ecs_iter_t *it) { + * ecs_iter_t qit = ecs_query_iter(it->world, Colliders); + * } + * @endcode + * + * An iterator contains resources that need to be released. By default this + * is handled by the last call to next() that returns false. When iteration is + * ended before iteration has completed, an application has to manually call + * ecs_iter_fini() to release the iterator resources: + * + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * while (ecs_query_next(&it)) { + * if (cond) { + * ecs_iter_fini(&it); + * break; + * } + * } + * @endcode + * + * @ingroup queries + */ +struct ecs_iter_t { + /* World */ + ecs_world_t *world; /**< The world. Can point to stage when in deferred/readonly mode. */ + ecs_world_t *real_world; /**< Actual world. Never points to a stage. */ + + /* Matched data */ + const ecs_entity_t *entities; /**< Entity identifiers */ + const ecs_size_t *sizes; /**< Component sizes */ + ecs_table_t *table; /**< Current table */ + ecs_table_t *other_table; /**< Prev or next table when adding/removing */ + ecs_id_t *ids; /**< (Component) ids */ + ecs_var_t *variables; /**< Values of variables (if any) */ + const ecs_table_record_t **trs; /**< Info on where to find field in table */ + ecs_entity_t *sources; /**< Entity on which the id was matched (0 if same as entities) */ + ecs_flags64_t constrained_vars; /**< Bitset that marks constrained variables */ + uint64_t group_id; /**< Group id for table, if group_by is used */ + ecs_termset_t set_fields; /**< Fields that are set */ + ecs_termset_t ref_fields; /**< Bitset with fields that aren't component arrays */ + ecs_termset_t row_fields; /**< Fields that must be obtained with field_at */ + ecs_termset_t up_fields; /**< Bitset with fields matched through up traversal */ + + /* Input information */ + ecs_entity_t system; /**< The system (if applicable) */ + ecs_entity_t event; /**< The event (if applicable) */ + ecs_id_t event_id; /**< The (component) id for the event */ + int32_t event_cur; /**< Unique event id. Used to dedup observer calls */ + + /* Query information */ + int8_t field_count; /**< Number of fields in iterator */ + int8_t term_index; /**< Index of term that emitted an event. + * This field will be set to the 'index' field + * of an observer term. */ + int8_t variable_count; /**< Number of variables for query */ + const ecs_query_t *query; /**< Query being evaluated */ + char **variable_names; /**< Names of variables (if any) */ + + /* Context */ + void *param; /**< Param passed to ecs_run */ + void *ctx; /**< System context */ + void *binding_ctx; /**< System binding context */ + void *callback_ctx; /**< Callback language binding context */ + void *run_ctx; /**< Run language binding context */ + + /* Time */ + ecs_ftime_t delta_time; /**< Time elapsed since last frame */ + ecs_ftime_t delta_system_time;/**< Time elapsed since last system invocation */ + + /* Iterator counters */ + int32_t frame_offset; /**< Offset relative to start of iteration */ + int32_t offset; /**< Offset relative to current table */ + int32_t count; /**< Number of entities to iterate */ + + /* Misc */ + ecs_flags32_t flags; /**< Iterator flags */ + ecs_entity_t interrupted_by; /**< When set, system execution is interrupted */ + ecs_iter_private_t priv_; /**< Private data */ + + /* Chained iterators */ + ecs_iter_next_action_t next; /**< Function to progress iterator */ + ecs_iter_action_t callback; /**< Callback of system or observer */ + ecs_iter_fini_action_t fini; /**< Function to cleanup iterator resources */ + ecs_iter_t *chain_it; /**< Optional, allows for creating iterator chains */ +}; - /** For filters with lots of terms an outside array can be provided. */ - ecs_term_t *terms_buffer; - /** Number of terms in array provided in terms_buffer. */ - int32_t terms_buffer_count; +/** Query must match prefabs. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryMatchPrefab (1u << 1u) - /** External storage to prevent allocation of the filter object */ - ecs_filter_t *storage; +/** Query must match disabled entities. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryMatchDisabled (1u << 2u) - /** When true, terms returned by an iterator may either contain 1 or N - * elements, where terms with N elements are owned, and terms with 1 element - * are shared, for example from a parent or base entity. When false, the - * iterator will at most return 1 element when the result contains both - * owned and shared terms. */ - bool instanced; +/** Query must match empty tables. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryMatchEmptyTables (1u << 3u) - /** Flags for advanced usage */ - ecs_flags32_t flags; +/** Query may have unresolved entity identifiers. + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryAllowUnresolvedByName (1u << 6u) - /** Filter expression. Should not be set at the same time as terms array */ - const char *expr; +/** Query only returns whole tables (ignores toggle/member fields). + * Can be combined with other query flags on the ecs_query_desc_t::flags field. + * \ingroup queries + */ +#define EcsQueryTableOnly (1u << 7u) - /** Entity associated with query (optional) */ - ecs_entity_t entity; -} ecs_filter_desc_t; -/** Used with ecs_query_init. +/** Used with ecs_query_init(). * * \ingroup queries */ typedef struct ecs_query_desc_t { + /** Used for validity testing. Must be 0. */ int32_t _canary; - /** Filter for the query */ - ecs_filter_desc_t filter; + /** Query terms */ + ecs_term_t terms[FLECS_TERM_COUNT_MAX]; + + /** Query DSL expression (optional) */ + const char *expr; + + /** Caching policy of query */ + ecs_query_cache_kind_t cache_kind; - /** Component to be used by order_by */ - ecs_entity_t order_by_component; + /** Flags for enabling query features */ + ecs_flags32_t flags; - /** Callback used for ordering query results. If order_by_id is 0, the + /** Callback used for ordering query results. If order_by_id is 0, the * pointer provided to the callback will be NULL. If the callback is not * set, results will not be ordered. */ - ecs_order_by_action_t order_by; + ecs_order_by_action_t order_by_callback; - /** Callback used for ordering query results. Same as order_by, + /** Callback used for ordering query results. Same as order_by_callback, * but more efficient. */ - ecs_sort_table_action_t sort_table; + ecs_sort_table_action_t order_by_table_callback; - /** Id to be used by group_by. This id is passed to the group_by function and - * can be used identify the part of an entity type that should be used for - * grouping. */ - ecs_id_t group_by_id; + /** Component to sort on, used together with order_by_callback or + * order_by_table_callback. */ + ecs_entity_t order_by; + + /** Component id to be used for grouping. Used together with the + * group_by_callback. */ + ecs_id_t group_by; /** Callback used for grouping results. If the callback is not set, results * will not be grouped. When set, this callback will be used to calculate a * "rank" for each entity (table) based on its components. This rank is then * used to sort entities (tables), so that entities (tables) of the same * rank are "grouped" together when iterated. */ - ecs_group_by_action_t group_by; + ecs_group_by_action_t group_by_callback; /** Callback that is invoked when a new group is created. The return value of * the callback is stored as context for a group. */ ecs_group_create_action_t on_group_create; - /** Callback that is invoked when an existing group is deleted. The return + /** Callback that is invoked when an existing group is deleted. The return * value of the on_group_create callback is passed as context parameter. */ ecs_group_delete_action_t on_group_delete; @@ -1041,54 +1216,49 @@ typedef struct ecs_query_desc_t { /** Function to free group_by_ctx */ ecs_ctx_free_t group_by_ctx_free; - /** If set, the query will be created as a subquery. A subquery matches at - * most a subset of its parent query. Subqueries do not directly receive - * (table) notifications from the world. Instead parent queries forward - * results to subqueries. This can improve matching performance, as fewer - * queries need to be matched with new tables. - * Subqueries can be nested. */ - ecs_query_t *parent; - /** User context to pass to callback */ void *ctx; /** Context to be used for language bindings */ void *binding_ctx; - + /** Callback to free ctx */ ecs_ctx_free_t ctx_free; - /** Callback to free binding_ctx */ + /** Callback to free binding_ctx */ ecs_ctx_free_t binding_ctx_free; + + /** Entity associated with query (optional) */ + ecs_entity_t entity; } ecs_query_desc_t; -/** Used with ecs_observer_init. - * - * \ingroup observers +/** Used with ecs_observer_init(). + * + * @ingroup observers */ typedef struct ecs_observer_desc_t { + /** Used for validity testing. Must be 0. */ int32_t _canary; /** Existing entity to associate with observer (optional) */ ecs_entity_t entity; - /** Filter for observer */ - ecs_filter_desc_t filter; + /** Query for observer */ + ecs_query_desc_t query; - /** Events to observe (OnAdd, OnRemove, OnSet, UnSet) */ + /** Events to observe (OnAdd, OnRemove, OnSet) */ ecs_entity_t events[FLECS_EVENT_DESC_MAX]; /** When observer is created, generate events from existing data. For example, - * EcsOnAdd Position would match all existing instances of Position. - * This is only supported for events that are iterable (see EcsIterable) */ + * #EcsOnAdd `Position` would match all existing instances of `Position`. */ bool yield_existing; /** Callback to invoke on an event, invoked when the observer matches. */ ecs_iter_action_t callback; /** Callback invoked on an event. When left to NULL the default runner - * is used which matches the event with the observer's filter, and calls - * 'callback' when it matches. + * is used which matches the event with the observer's query, and calls + * 'callback' when it matches. * A reason to override the run function is to improve performance, if there * are more efficient way to test whether an event matches the observer than * the general purpose query matcher. */ @@ -1097,14 +1267,20 @@ typedef struct ecs_observer_desc_t { /** User context to pass to callback */ void *ctx; - /** Context to be used for language bindings */ - void *binding_ctx; - /** Callback to free ctx */ ecs_ctx_free_t ctx_free; - /** Callback to free binding_ctx */ - ecs_ctx_free_t binding_ctx_free; + /** Context associated with callback (for language bindings). */ + void *callback_ctx; + + /** Callback to free callback ctx. */ + ecs_ctx_free_t callback_ctx_free; + + /** Context associated with run (for language bindings). */ + void *run_ctx; + + /** Callback to free run ctx. */ + ecs_ctx_free_t run_ctx_free; /** Observable with which to register the observer */ ecs_poly_t *observable; @@ -1114,12 +1290,13 @@ typedef struct ecs_observer_desc_t { int32_t *last_event_id; /** Used for internal purposes */ - int32_t term_index; + int8_t term_index_; + ecs_flags32_t flags_; } ecs_observer_desc_t; -/** Used with ecs_emit. - * - * \ingroup observers +/** Used with ecs_emit(). + * + * @ingroup observers */ typedef struct ecs_event_desc_t { /** The event id. Only observers for the specified event will be notified */ @@ -1142,7 +1319,7 @@ typedef struct ecs_event_desc_t { /** Limit number of notified entities to count. offset+count must be less * than the total number of entities in the table. If left to 0, it will be - * automatically determined by doing ecs_table_count(table) - offset. */ + * automatically determined by doing `ecs_table_count(table) - offset`. */ int32_t count; /** Single-entity alternative to setting table / offset / count */ @@ -1154,7 +1331,7 @@ typedef struct ecs_event_desc_t { * storage of the event type. */ void *param; - /* Same as param, but with the guarantee that the value won't be modified. + /** Same as param, but with the guarantee that the value won't be modified. * When an event with a const parameter is enqueued, the value of the param * is copied to a temporary storage of the event type. */ const void *const_param; @@ -1169,15 +1346,23 @@ typedef struct ecs_event_desc_t { /** * @defgroup misc_types Miscellaneous types - * @brief Types used to create entities, observers, queries and more. + * Types used to create entities, observers, queries and more. + * * @{ */ -/* Utility to hold a value of a dynamic type */ -typedef struct ecs_value_t { - ecs_entity_t type; - void *ptr; -} ecs_value_t; +/** Type with information about the current Flecs build */ +typedef struct ecs_build_info_t { + const char *compiler; /**< Compiler used to compile flecs */ + const char **addons; /**< Addons included in build */ + const char *version; /**< Stringified version */ + int16_t version_major; /**< Major flecs version */ + int16_t version_minor; /**< Minor flecs version */ + int16_t version_patch; /**< Patch flecs version */ + bool debug; /**< Is this a debug build */ + bool sanitize; /**< Is this a sanitize build */ + bool perf_trace; /**< Is this a perf tracing build */ +} ecs_build_info_t; /** Type that contains information about the world. */ typedef struct ecs_world_info_t { @@ -1186,19 +1371,20 @@ typedef struct ecs_world_info_t { ecs_entity_t max_id; /**< Last allowed entity id */ ecs_ftime_t delta_time_raw; /**< Raw delta time (no time scaling) */ - ecs_ftime_t delta_time; /**< Time passed to or computed by ecs_progress */ + ecs_ftime_t delta_time; /**< Time passed to or computed by ecs_progress() */ ecs_ftime_t time_scale; /**< Time scale applied to delta_time */ ecs_ftime_t target_fps; /**< Target fps */ ecs_ftime_t frame_time_total; /**< Total time spent processing a frame */ ecs_ftime_t system_time_total; /**< Total time spent in systems */ ecs_ftime_t emit_time_total; /**< Total time spent notifying observers */ ecs_ftime_t merge_time_total; /**< Total time spent in merges */ - ecs_ftime_t world_time_total; /**< Time elapsed in simulation */ - ecs_ftime_t world_time_total_raw; /**< Time elapsed in simulation (no scaling) */ ecs_ftime_t rematch_time_total; /**< Time spent on query rematching */ - + double world_time_total; /**< Time elapsed in simulation */ + double world_time_total_raw; /**< Time elapsed in simulation (no scaling) */ + int64_t frame_count_total; /**< Total number of frames */ int64_t merge_count_total; /**< Total number of merges */ + int64_t eval_comp_monitors_total; /**< Total number of monitor evaluations */ int64_t rematch_count_total; /**< Total number of rematches */ int64_t id_create_total; /**< Total number of times a new id was created */ @@ -1218,22 +1404,23 @@ typedef struct ecs_world_info_t { /* -- Command counts -- */ struct { - int64_t add_count; /**< add commands processed */ - int64_t remove_count; /**< remove commands processed */ - int64_t delete_count; /**< delete commands processed */ - int64_t clear_count; /**< clear commands processed */ - int64_t set_count; /**< set commands processed */ - int64_t get_mut_count; /**< get_mut/emplace commands processed */ - int64_t modified_count; /**< modified commands processed */ - int64_t other_count; /**< other commands processed */ - int64_t discard_count; /**< commands discarded, happens when entity is no longer alive when running the command */ - int64_t batched_entity_count; /**< entities for which commands were batched */ - int64_t batched_command_count; /**< commands batched */ - } cmd; - - const char *name_prefix; /**< Value set by ecs_set_name_prefix. Used + int64_t add_count; /**< Add commands processed */ + int64_t remove_count; /**< Remove commands processed */ + int64_t delete_count; /**< Selete commands processed */ + int64_t clear_count; /**< Clear commands processed */ + int64_t set_count; /**< Set commands processed */ + int64_t ensure_count; /**< Ensure/emplace commands processed */ + int64_t modified_count; /**< Modified commands processed */ + int64_t discard_count; /**< Commands discarded, happens when entity is no longer alive when running the command */ + int64_t event_count; /**< Enqueued custom events */ + int64_t other_count; /**< Other commands processed */ + int64_t batched_entity_count; /**< Entities for which commands were batched */ + int64_t batched_command_count; /**< Commands batched */ + } cmd; /**< Command statistics. */ + + const char *name_prefix; /**< Value set by ecs_set_name_prefix(). Used * to remove library prefixes of symbol - * names (such as Ecs, ecs_) when + * names (such as `Ecs`, `ecs_`) when * registering them as names. */ } ecs_world_info_t; @@ -1248,11 +1435,12 @@ typedef struct ecs_query_group_info_t { /** * @defgroup builtin_components Builtin component types. - * @brief Types that represent builtin components. + * Types that represent builtin components. + * * @{ */ -/** A (string) identifier. Used as pair with EcsName and EcsSymbol tags */ +/** A (string) identifier. Used as pair with #EcsName and #EcsSymbol tags */ typedef struct EcsIdentifier { char *value; /**< Identifier string */ ecs_size_t length; /**< Length of identifier */ @@ -1272,14 +1460,14 @@ typedef struct EcsPoly { ecs_poly_t *poly; /**< Pointer to poly object */ } EcsPoly; -/** Target data for flattened relationships. */ -typedef struct EcsTarget { - int32_t count; - ecs_record_t *target; -} EcsTarget; - -/** Component for iterable entities */ -typedef ecs_iterable_t EcsIterable; +/** When added to an entity this informs serialization formats which component + * to use when a value is assigned to an entity without specifying the + * component. This is intended as a hint, serialization formats are not required + * to use it. Adding this component does not change the behavior of core ECS + * operations. */ +typedef struct EcsDefaultChildComponent { + ecs_id_t component; /**< Default component id. */ +} EcsDefaultChildComponent; /** @} */ /** @} */ @@ -1291,13 +1479,15 @@ typedef ecs_iterable_t EcsIterable; /** * @defgroup api_constants API Constants - * @brief Public API constants. + * Public API constants. + * * @{ */ /** * @defgroup id_flags Component id flags. - * @brief Id flags are bits that can be set on an id (ecs_id_t). + * Id flags are bits that can be set on an id (ecs_id_t). + * * @{ */ @@ -1305,14 +1495,11 @@ typedef ecs_iterable_t EcsIterable; FLECS_API extern const ecs_id_t ECS_PAIR; /** Automatically override component when it is inherited */ -FLECS_API extern const ecs_id_t ECS_OVERRIDE; +FLECS_API extern const ecs_id_t ECS_AUTO_OVERRIDE; /** Adds bitset to storage which allows component to be enabled/disabled */ FLECS_API extern const ecs_id_t ECS_TOGGLE; -/** Include all components from entity to which AND is applied */ -FLECS_API extern const ecs_id_t ECS_AND; - /** @} */ /** @@ -1321,23 +1508,38 @@ FLECS_API extern const ecs_id_t ECS_AND; */ /* Builtin component ids */ + +/** Component component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsComponent); + +/** Identifier component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsIdentifier); -FLECS_API extern const ecs_entity_t ecs_id(EcsIterable); + +/** Poly component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsPoly); +/** DefaultChildComponent component id. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsDefaultChildComponent); + +/** Tag added to queries. */ FLECS_API extern const ecs_entity_t EcsQuery; + +/** Tag added to observers. */ FLECS_API extern const ecs_entity_t EcsObserver; -/* System module component ids */ +/** Tag added to systems. */ FLECS_API extern const ecs_entity_t EcsSystem; + +/** TickSource component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsTickSource); -/* Pipeline module component ids */ +/** Pipeline module component ids */ FLECS_API extern const ecs_entity_t ecs_id(EcsPipelineQuery); -/* Timer module component ids */ +/** Timer component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsTimer); + +/** RateFilter component id. */ FLECS_API extern const ecs_entity_t ecs_id(EcsRateFilter); /** Root scope for builtin flecs entities */ @@ -1361,86 +1563,140 @@ FLECS_API extern const ecs_entity_t EcsThis; /** Variable entity ("$"). Used in expressions to prefix variable names */ FLECS_API extern const ecs_entity_t EcsVariable; -/** Marks a relationship as transitive. - * Behavior: +/** Shortcut as EcsVariable is typically used as source for singleton terms */ +#define EcsSingleton EcsVariable + +/** Marks a relationship as transitive. + * Behavior: + * + * @code * if R(X, Y) and R(Y, Z) then R(X, Z) + * @endcode */ FLECS_API extern const ecs_entity_t EcsTransitive; -/** Marks a relatoinship as reflexive. - * Behavior: +/** Marks a relationship as reflexive. + * Behavior: + * + * @code * R(X, X) == true + * @endcode */ FLECS_API extern const ecs_entity_t EcsReflexive; -/** Ensures that entity/component cannot be used as target in IsA relationship. - * Final can improve the performance of rule-based queries, as they will not - * attempt to substitute a final component with its subsets. - * - * Behavior: +/** Ensures that entity/component cannot be used as target in `IsA` relationship. + * Final can improve the performance of queries as they will not attempt to + * substitute a final component with its subsets. + * + * Behavior: + * + * @code * if IsA(X, Y) and Final(Y) throw error + * @endcode */ FLECS_API extern const ecs_entity_t EcsFinal; -/** Ensures that component is never inherited from an IsA target. - * - * Behavior: - * if DontInherit(X) and X(B) and IsA(A, B) then X(A) is false. - */ -FLECS_API extern const ecs_entity_t EcsDontInherit; +/** Relationship that specifies component inheritance behavior. */ +FLECS_API extern const ecs_entity_t EcsOnInstantiate; -/** Ensures a component is always overridden. - * - * Behavior: - * As if the component is added together with OVERRIDE | T - */ -FLECS_API extern const ecs_entity_t EcsAlwaysOverride; +/** Override component on instantiate. + * This will copy the component from the base entity `(IsA target)` to the + * instance. The base component will never be inherited from the prefab. */ +FLECS_API extern const ecs_entity_t EcsOverride; + +/** Inherit component on instantiate. + * This will inherit (share) the component from the base entity `(IsA target)`. + * The component can be manually overridden by adding it to the instance. */ +FLECS_API extern const ecs_entity_t EcsInherit; + +/** Never inherit component on instantiate. + * This will not copy or share the component from the base entity `(IsA target)`. + * When the component is added to an instance, its value will never be copied + * from the base entity. */ +FLECS_API extern const ecs_entity_t EcsDontInherit; /** Marks relationship as commutative. * Behavior: + * + * @code * if R(X, Y) then R(Y, X) + * @endcode */ FLECS_API extern const ecs_entity_t EcsSymmetric; /** Can be added to relationship to indicate that the relationship can only occur - * once on an entity. Adding a 2nd instance will replace the 1st. + * once on an entity. Adding a 2nd instance will replace the 1st. * * Behavior: + * + * @code * R(X, Y) + R(X, Z) = R(X, Z) + * @endcode */ FLECS_API extern const ecs_entity_t EcsExclusive; /** Marks a relationship as acyclic. Acyclic relationships may not form cycles. */ FLECS_API extern const ecs_entity_t EcsAcyclic; -/** Marks a relationship as traversable. Traversable relationships may be +/** Marks a relationship as traversable. Traversable relationships may be * traversed with "up" queries. Traversable relationships are acyclic. */ FLECS_API extern const ecs_entity_t EcsTraversable; /** Ensure that a component always is added together with another component. - * + * * Behavior: + * + * @code * If With(R, O) and R(X) then O(X) * If With(R, O) and R(X, Y) then O(X, Y) + * @endcode */ FLECS_API extern const ecs_entity_t EcsWith; /** Ensure that relationship target is child of specified entity. - * + * * Behavior: + * + * @code * If OneOf(R, O) and R(X, Y), Y must be a child of O * If OneOf(R) and R(X, Y), Y must be a child of R + * @endcode */ FLECS_API extern const ecs_entity_t EcsOneOf; +/** Mark a component as toggleable with ecs_enable_id(). */ +FLECS_API extern const ecs_entity_t EcsCanToggle; + +/** Can be added to components to indicate it is a trait. Traits are components + * and/or tags that are added to other components to modify their behavior. + */ +FLECS_API extern const ecs_entity_t EcsTrait; + +/** Ensure that an entity is always used in pair as relationship. + * + * Behavior: + * + * @code + * e.add(R) panics + * e.add(X, R) panics, unless X has the "Trait" trait + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsRelationship; + +/** Ensure that an entity is always used in pair as target. + * + * Behavior: + * + * @code + * e.add(T) panics + * e.add(T, X) panics + * @endcode + */ +FLECS_API extern const ecs_entity_t EcsTarget; + /** Can be added to relationship to indicate that it should never hold data, * even when it or the relationship target is a component. */ -FLECS_API extern const ecs_entity_t EcsTag; - -/** Tag to indicate that relationship is stored as union. Union relationships - * enable changing the target of a union without switching tables. Union - * relationships are also marked as exclusive. */ -FLECS_API extern const ecs_entity_t EcsUnion; +FLECS_API extern const ecs_entity_t EcsPairIsTag; /** Tag to indicate name identifier */ FLECS_API extern const ecs_entity_t EcsName; @@ -1470,13 +1726,18 @@ FLECS_API extern const ecs_entity_t EcsModule; FLECS_API extern const ecs_entity_t EcsPrivate; /** Tag added to prefab entities. Any entity with this tag is automatically - * ignored by queries, unless EcsPrefab is explicitly queried for. */ + * ignored by queries, unless #EcsPrefab is explicitly queried for. */ FLECS_API extern const ecs_entity_t EcsPrefab; -/** When this tag is added to an entity it is skipped by queries, unless - * EcsDisabled is explicitly queried for. */ +/** When this tag is added to an entity it is skipped by queries, unless + * #EcsDisabled is explicitly queried for. */ FLECS_API extern const ecs_entity_t EcsDisabled; +/** Trait added to entities that should never be returned by queries. Reserved + * for internal entities that have special meaning to the query engine, such as + * #EcsThis, #EcsWildcard, #EcsAny. */ +FLECS_API extern const ecs_entity_t EcsNotQueryable; + /** Event that triggers when an id is added to an entity */ FLECS_API extern const ecs_entity_t EcsOnAdd; @@ -1486,9 +1747,6 @@ FLECS_API extern const ecs_entity_t EcsOnRemove; /** Event that triggers when a component is set for an entity */ FLECS_API extern const ecs_entity_t EcsOnSet; -/** Event that triggers when a component is unset for an entity */ -FLECS_API extern const ecs_entity_t EcsUnSet; - /** Event that triggers observer when an entity starts/stops matching a query */ FLECS_API extern const ecs_entity_t EcsMonitor; @@ -1511,61 +1769,61 @@ FLECS_API extern const ecs_entity_t EcsOnDelete; * element of a pair) is deleted. */ FLECS_API extern const ecs_entity_t EcsOnDeleteTarget; -/** Remove cleanup policy. Must be used as target in pair with EcsOnDelete or - * EcsOnDeleteTarget. */ +/** Remove cleanup policy. Must be used as target in pair with #EcsOnDelete or + * #EcsOnDeleteTarget. */ FLECS_API extern const ecs_entity_t EcsRemove; -/** Delete cleanup policy. Must be used as target in pair with EcsOnDelete or - * EcsOnDeleteTarget. */ +/** Delete cleanup policy. Must be used as target in pair with #EcsOnDelete or + * #EcsOnDeleteTarget. */ FLECS_API extern const ecs_entity_t EcsDelete; -/** Panic cleanup policy. Must be used as target in pair with EcsOnDelete or - * EcsOnDeleteTarget. */ +/** Panic cleanup policy. Must be used as target in pair with #EcsOnDelete or + * #EcsOnDeleteTarget. */ FLECS_API extern const ecs_entity_t EcsPanic; -/** Component that stores data for flattened relationships */ -FLECS_API extern const ecs_entity_t ecs_id(EcsTarget); +/** Mark component as sparse */ +FLECS_API extern const ecs_entity_t EcsSparse; -/** Tag added to root entity to indicate its subtree should be flattened. Used - * together with assemblies. */ -FLECS_API extern const ecs_entity_t EcsFlatten; - -/** Used like (EcsDefaultChildComponent, Component). When added to an entity, - * this informs serialization formats which component to use when a value is - * assigned to an entity without specifying the component. This is intended as - * a hint, serialization formats are not required to use it. Adding this - * component does not change the behavior of core ECS operations. */ -FLECS_API extern const ecs_entity_t EcsDefaultChildComponent; +/** Mark relationship as union */ +FLECS_API extern const ecs_entity_t EcsUnion; -/* Builtin predicates for comparing entity ids in queries. Only supported by rules */ +/** Marker used to indicate `$var == ...` matching in queries. */ FLECS_API extern const ecs_entity_t EcsPredEq; + +/** Marker used to indicate `$var == "name"` matching in queries. */ FLECS_API extern const ecs_entity_t EcsPredMatch; + +/** Marker used to indicate `$var ~= "pattern"` matching in queries. */ FLECS_API extern const ecs_entity_t EcsPredLookup; -/* Builtin marker entities for opening/closing query scopes */ +/** Marker used to indicate the start of a scope (`{`) in queries. */ FLECS_API extern const ecs_entity_t EcsScopeOpen; + +/** Marker used to indicate the end of a scope (`}`) in queries. */ FLECS_API extern const ecs_entity_t EcsScopeClose; -/** Tag used to indicate query is empty */ +/** Tag used to indicate query is empty. + * This tag is removed automatically when a query becomes non-empty, and is not + * automatically re-added when it becomes empty. + */ FLECS_API extern const ecs_entity_t EcsEmpty; -/* Pipeline module tags */ -FLECS_API extern const ecs_entity_t ecs_id(EcsPipeline); -FLECS_API extern const ecs_entity_t EcsOnStart; -FLECS_API extern const ecs_entity_t EcsPreFrame; -FLECS_API extern const ecs_entity_t EcsOnLoad; -FLECS_API extern const ecs_entity_t EcsPostLoad; -FLECS_API extern const ecs_entity_t EcsPreUpdate; -FLECS_API extern const ecs_entity_t EcsOnUpdate; -FLECS_API extern const ecs_entity_t EcsOnValidate; -FLECS_API extern const ecs_entity_t EcsPostUpdate; -FLECS_API extern const ecs_entity_t EcsPreStore; -FLECS_API extern const ecs_entity_t EcsOnStore; -FLECS_API extern const ecs_entity_t EcsPostFrame; -FLECS_API extern const ecs_entity_t EcsPhase; +FLECS_API extern const ecs_entity_t ecs_id(EcsPipeline); /**< Pipeline component id. */ +FLECS_API extern const ecs_entity_t EcsOnStart; /**< OnStart pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPreFrame; /**< PreFrame pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnLoad; /**< OnLoad pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPostLoad; /**< PostLoad pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPreUpdate; /**< PreUpdate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnUpdate; /**< OnUpdate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnValidate; /**< OnValidate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPostUpdate; /**< PostUpdate pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPreStore; /**< PreStore pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsOnStore; /**< OnStore pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPostFrame; /**< PostFrame pipeline phase. */ +FLECS_API extern const ecs_entity_t EcsPhase; /**< Phase pipeline phase. */ /** Value used to quickly check if component is builtin. This is used to quickly - * filter out tables with builtin components (for example for ecs_delete) */ + * filter out tables with builtin components (for example for ecs_delete()) */ #define EcsLastInternalComponentId (ecs_id(EcsPoly)) /** The first user-defined component starts from this id. Ids up to this number @@ -1577,9 +1835,9 @@ FLECS_API extern const ecs_entity_t EcsPhase; #define EcsFirstUserEntityId (FLECS_HI_COMPONENT_ID + 128) /* When visualized the reserved id ranges look like this: - * [1..8]: Builtin components - * [9..FLECS_HI_COMPONENT_ID]: Low ids reserved for application components - * [FLECS_HI_COMPONENT_ID + 1..EcsFirstUserEntityId]: Builtin entities + * - [1..8]: Builtin components + * - [9..FLECS_HI_COMPONENT_ID]: Low ids reserved for application components + * - [FLECS_HI_COMPONENT_ID + 1..EcsFirstUserEntityId]: Builtin entities */ /** @} */ @@ -1587,7 +1845,8 @@ FLECS_API extern const ecs_entity_t EcsPhase; /** * @defgroup world_api World - * @brief Functions for working with `ecs_world_t`. + * Functions for working with `ecs_world_t`. + * * @{ */ @@ -1606,8 +1865,8 @@ FLECS_API ecs_world_t* ecs_init(void); /** Create a new world with just the core module. - * Same as ecs_init, but doesn't import modules from addons. This operation is - * faster than ecs_init and results in less memory utilization. + * Same as ecs_init(), but doesn't import modules from addons. This operation is + * faster than ecs_init() and results in less memory utilization. * * @return A new tiny world */ @@ -1615,7 +1874,7 @@ FLECS_API ecs_world_t* ecs_mini(void); /** Create a new world with arguments. - * Same as ecs_init, but allows passing in command line arguments. Command line + * Same as ecs_init(), but allows passing in command line arguments. Command line * arguments are used to: * - automatically derive the name of the application from argv[0] * @@ -1637,7 +1896,7 @@ int ecs_fini( ecs_world_t *world); /** Returns whether the world is being deleted. - * This operation can be used in callbacks like type hooks or observers to + * This operation can be used in callbacks like type hooks or observers to * detect if they are invoked while the world is being deleted. * * @param world The world. @@ -1650,7 +1909,7 @@ bool ecs_is_fini( /** Register action to be executed when world is destroyed. * Fini actions are typically used when a module needs to clean up before a * world shuts down. - * + * * @param world The world. * @param action The function to execute. * @param ctx Userdata to pass to the function */ @@ -1660,6 +1919,54 @@ void ecs_atfini( ecs_fini_action_t action, void *ctx); +/** Type returned by ecs_get_entities(). */ +typedef struct ecs_entities_t { + const ecs_entity_t *ids; /**< Array with all entity ids in the world. */ + int32_t count; /**< Total number of entity ids. */ + int32_t alive_count; /**< Number of alive entity ids. */ +} ecs_entities_t; + +/** Return entity identifiers in world. + * This operation returns an array with all entity ids that exist in the world. + * Note that the returned array will change and may get invalidated as a result + * of entity creation & deletion. + * + * To iterate all alive entity ids, do: + * @code + * ecs_entities_t entities = ecs_get_entities(world); + * for (int i = 0; i < entities.alive_count; i ++) { + * ecs_entity_t id = entities.ids[i]; + * } + * @endcode + * + * To iterate not-alive ids, do: + * @code + * for (int i = entities.alive_count + 1; i < entities.count; i ++) { + * ecs_entity_t id = entities.ids[i]; + * } + * @endcode + * + * The returned array does not need to be freed. Mutating the returned array + * will return in undefined behavior (and likely crashes). + * + * @param world The world. + * @return Struct with entity id array. + */ +FLECS_API +ecs_entities_t ecs_get_entities( + const ecs_world_t *world); + +/** Get flags set on the world. + * This operation returns the internal flags (see api_flags.h) that are + * set on the world. + * + * @param world The world. + * @return Flags set on the world. + */ +FLECS_API +ecs_flags32_t ecs_world_get_flags( + const ecs_world_t *world); + /** @} */ /** @@ -1667,14 +1974,14 @@ void ecs_atfini( * @{ */ -/** Begin frame. - * When an application does not use ecs_progress to control the main loop, it +/** Begin frame. + * When an application does not use ecs_progress() to control the main loop, it * can still use Flecs features such as FPS limiting and time measurements. This * operation needs to be invoked whenever a new frame is about to get processed. * - * Calls to ecs_frame_begin must always be followed by ecs_frame_end. + * Calls to ecs_frame_begin() must always be followed by ecs_frame_end(). * - * The function accepts a delta_time parameter, which will get passed to + * The function accepts a delta_time parameter, which will get passed to * systems. This value is also used to compute the amount of time the function * needs to sleep to ensure it does not exceed the target_fps, when it is set. * When 0 is provided for delta_time, the time will be measured. @@ -1690,9 +1997,9 @@ ecs_ftime_t ecs_frame_begin( ecs_world_t *world, ecs_ftime_t delta_time); -/** End frame. +/** End frame. * This operation must be called at the end of the frame, and always after - * ecs_frame_begin. + * ecs_frame_begin(). * * @param world The world. */ @@ -1703,7 +2010,7 @@ void ecs_frame_end( /** Register action to be executed once after frame. * Post frame actions are typically used for calling operations that cannot be * invoked during iteration, such as changing the number of threads. - * + * * @param world The world. * @param action The function to execute. * @param ctx Userdata to pass to the function */ @@ -1711,11 +2018,11 @@ FLECS_API void ecs_run_post_frame( ecs_world_t *world, ecs_fini_action_t action, - void *ctx); + void *ctx); /** Signal exit * This operation signals that the application should quit. It will cause - * ecs_progress to return false. + * ecs_progress() to return false. * * @param world The world to quit. */ @@ -1723,16 +2030,18 @@ FLECS_API void ecs_quit( ecs_world_t *world); -/** Return whether a quit has been signaled. +/** Return whether a quit has been requested. * * @param world The world. + * @return Whether a quit has been requested. + * @see ecs_quit() */ -FLECS_API +FLECS_API bool ecs_should_quit( const ecs_world_t *world); -/** Measure frame time. - * Frame time measurements measure the total time passed in a single frame, and +/** Measure frame time. + * Frame time measurements measure the total time passed in a single frame, and * how much of that time was spent on systems and on merging. * * Frame time measurements add a small constant-time overhead to an application. @@ -1746,10 +2055,10 @@ FLECS_API void ecs_measure_frame_time( ecs_world_t *world, bool enable); -/** Measure system time. +/** Measure system time. * System time measurements measure the time spent in each system. * - * System time measurements add overhead to every system invocation and + * System time measurements add overhead to every system invocation and * therefore have a small but measurable impact on application performance. * System time measurements must be enabled before obtaining system statistics. * @@ -1758,17 +2067,17 @@ FLECS_API void ecs_measure_frame_time( */ FLECS_API void ecs_measure_system_time( ecs_world_t *world, - bool enable); + bool enable); /** Set target frames per second (FPS) for application. - * Setting the target FPS ensures that ecs_progress is not invoked faster than - * the specified FPS. When enabled, ecs_progress tracks the time passed since + * Setting the target FPS ensures that ecs_progress() is not invoked faster than + * the specified FPS. When enabled, ecs_progress() tracks the time passed since * the last invocation, and sleeps the remaining time of the frame (if any). * * This feature ensures systems are ran at a consistent interval, as well as * conserving CPU time by not running systems more often than required. * - * Note that ecs_progress only sleeps if there is time left in the frame. Both + * Note that ecs_progress() only sleeps if there is time left in the frame. Both * time spent in flecs as time spent outside of flecs are taken into * account. * @@ -1780,6 +2089,24 @@ void ecs_set_target_fps( ecs_world_t *world, ecs_ftime_t fps); +/** Set default query flags. + * Set a default value for the ecs_filter_desc_t::flags field. Default flags + * are applied in addition to the flags provided in the descriptor. For a + * list of available flags, see include/flecs/private/api_flags.h. Typical flags + * to use are: + * + * - `EcsQueryMatchEmptyTables` + * - `EcsQueryMatchDisabled` + * - `EcsQueryMatchPrefab` + * + * @param world The world. + * @param flags The query flags. + */ +FLECS_API +void ecs_set_default_query_flags( + ecs_world_t *world, + ecs_flags32_t flags); + /** @} */ /** @@ -1788,41 +2115,82 @@ void ecs_set_target_fps( */ /** Begin readonly mode. - * Readonly mode guarantees that no mutations will occur on the world, which - * makes the world safe to access from multiple threads. While the world is in - * readonly mode, operations are deferred. + * This operation puts the world in readonly mode, which disallows mutations on + * the world. Readonly mode exists so that internal mechanisms can implement + * optimizations that certain aspects of the world to not change, while also + * providing a mechanism for applications to prevent accidental mutations in, + * for example, multithreaded applications. + * + * Readonly mode is a stronger version of deferred mode. In deferred mode + * ECS operations such as add/remove/set/delete etc. are added to a command + * queue to be executed later. In readonly mode, operations that could break + * scheduler logic (such as creating systems, queries) are also disallowed. + * + * Readonly mode itself has a single threaded and a multi threaded mode. In + * single threaded mode certain mutations on the world are still allowed, for + * example: + * - Entity liveliness operations (such as new, make_alive), so that systems are + * able to create new entities. + * - Implicit component registration, so that this works from systems + * - Mutations to supporting data structures for the evaluation of uncached + * queries (filters), so that these can be created on the fly. + * + * These mutations are safe in a single threaded applications, but for + * multithreaded applications the world needs to be entirely immutable. For this + * purpose multi threaded readonly mode exists, which disallows all mutations on + * the world. This means that in multi threaded applications, entity liveliness + * operations, implicit component registration, and on-the-fly query creation + * are not guaranteed to work. + * + * While in readonly mode, applications can still enqueue ECS operations on a + * stage. Stages are managed automatically when using the pipeline addon and + * ecs_progress(), but they can also be configured manually as shown here: + * + * @code + * // Number of stages typically corresponds with number of threads + * ecs_set_stage_count(world, 2); + * ecs_stage_t *stage = ecs_get_stage(world, 1); + * + * ecs_readonly_begin(world); + * ecs_add(world, e, Tag); // readonly assert + * ecs_add(stage, e, Tag); // OK + * @endcode * - * Note that while similar to ecs_defer_begin, deferring only does not guarantee - * the world is not mutated. Operations that are not deferred (like creating a - * query) update data structures on the world and are allowed when deferring is - * enabled, but not when the world is in readonly mode. + * When an attempt is made to perform an operation on a world in readonly mode, + * the code will throw an assert saying that the world is in readonly mode. * - * A call to ecs_readonly_begin must be followed up with ecs_readonly_end. + * A call to ecs_readonly_begin() must be followed up with ecs_readonly_end(). + * When ecs_readonly_end() is called, all enqueued commands from configured + * stages are merged back into the world. Calls to ecs_readonly_begin() and + * ecs_readonly_end() should always happen from a context where the code has + * exclusive access to the world. The functions themselves are not thread safe. * - * The ecs_progress() function automatically enables readonly mode while systems - * are executed. + * In a typical application, a (non-exhaustive) call stack that uses + * ecs_readonly_begin() and ecs_readonly_end() will look like this: * - * When a world has more than one stage, the specific stage must be provided to - * mutating ECS operations. Failing to do so will throw a readonly assert. A - * world typically has more than one stage when using threads. An example: + * @code + * ecs_progress() + * ecs_readonly_begin() + * ecs_defer_begin() * - * ecs_set_stage_count(world, 2); - * ecs_stage_t *stage = ecs_get_stage(world, 1); + * // user code * - * ecs_readonly_begin(world); - * ecs_add(world, e, Tag); // readonly assert - * ecs_add(stage, e, Tag); // OK + * ecs_readonly_end() + * ecs_defer_end() + *@endcode * * @param world The world + * @param multi_threaded Whether to enable readonly/multi threaded mode. * @return Whether world is in readonly mode. */ FLECS_API bool ecs_readonly_begin( - ecs_world_t *world); + ecs_world_t *world, + bool multi_threaded); /** End readonly mode. - * This operation ends readonly mode, and must be called after - * ecs_readonly_begin. Operations that were deferred while the world was in + * This operation ends readonly mode, and must be called after + * ecs_readonly_begin(). Operations that were deferred while the world was in * readonly mode will be flushed. * * @param world The world @@ -1835,7 +2203,7 @@ void ecs_readonly_end( * When automatic merging is disabled, an application can call this * operation on either an individual stage, or on the world which will merge * all stages. This operation may only be called when staging is not enabled - * (either after progress() or after readonly_end()). + * (either after ecs_progress() or after ecs_readonly_end()). * * This operation may be called on an already merged stage or world. * @@ -1845,35 +2213,51 @@ FLECS_API void ecs_merge( ecs_world_t *world); -/** Defer operations until end of frame. +/** Defer operations until end of frame. * When this operation is invoked while iterating, operations inbetween the - * defer_begin and defer_end operations are executed at the end of the frame. + * ecs_defer_begin() and ecs_defer_end() operations are executed at the end + * of the frame. * * This operation is thread safe. - * + * * @param world The world. * @return true if world changed from non-deferred mode to deferred mode. + * + * @see ecs_defer_end() + * @see ecs_is_deferred() + * @see ecs_defer_resume() + * @see ecs_defer_suspend() */ FLECS_API bool ecs_defer_begin( ecs_world_t *world); /** Test if deferring is enabled for current stage. - * + * * @param world The world. * @return True if deferred, false if not. + * + * @see ecs_defer_begin() + * @see ecs_defer_end() + * @see ecs_defer_resume() + * @see ecs_defer_suspend() */ FLECS_API bool ecs_is_deferred( const ecs_world_t *world); -/** End block of operations to defer. - * See defer_begin. +/** End block of operations to defer. + * See ecs_defer_begin(). * * This operation is thread safe. * * @param world The world. * @return true if world changed from deferred mode to non-deferred mode. + * + * @see ecs_defer_begin() + * @see ecs_defer_is_deferred() + * @see ecs_defer_resume() + * @see ecs_defer_suspend() */ FLECS_API bool ecs_defer_end( @@ -1882,56 +2266,45 @@ bool ecs_defer_end( /** Suspend deferring but do not flush queue. * This operation can be used to do an undeferred operation while not flushing * the operations in the queue. - * - * An application should invoke ecs_defer_resume before ecs_defer_end is called. + * + * An application should invoke ecs_defer_resume() before ecs_defer_end() is called. * The operation may only be called when deferring is enabled. - * + * * @param world The world. + * + * @see ecs_defer_begin() + * @see ecs_defer_end() + * @see ecs_defer_is_deferred() + * @see ecs_defer_resume() */ FLECS_API void ecs_defer_suspend( ecs_world_t *world); /** Resume deferring. - * See ecs_defer_suspend. - * + * See ecs_defer_suspend(). + * * @param world The world. + * + * @see ecs_defer_begin() + * @see ecs_defer_end() + * @see ecs_defer_is_deferred() + * @see ecs_defer_suspend() */ FLECS_API void ecs_defer_resume( ecs_world_t *world); -/** Enable/disable automerging for world or stage. - * When automerging is enabled, staged data will automatically be merged with - * the world when staging ends. This happens at the end of progress(), at a - * sync point or when readonly_end() is called. - * - * Applications can exercise more control over when data from a stage is merged - * by disabling automerging. This requires an application to explicitly call - * merge() on the stage. - * - * When this function is invoked on the world, it sets all current stages to - * the provided value and sets the default for new stages. When this function is - * invoked on a stage, automerging is only set for that specific stage. - * - * @param world The world. - * @param automerge Whether to enable or disable automerging. - */ -FLECS_API -void ecs_set_automerge( - ecs_world_t *world, - bool automerge); - /** Configure world to have N stages. * This initializes N stages, which allows applications to defer operations to * multiple isolated defer queues. This is typically used for applications with * multiple threads, where each thread gets its own queue, and commands are * merged when threads are synchronized. * - * Note that the ecs_set_threads function already creates the appropriate - * number of stages. The set_stage_count() operation is useful for applications that - * want to manage their own stages and/or threads. - * + * Note that the ecs_set_threads() function already creates the appropriate + * number of stages. The ecs_set_stage_count() operation is useful for applications + * that want to manage their own stages and/or threads. + * * @param world The world. * @param stages The number of stages. */ @@ -1941,7 +2314,7 @@ void ecs_set_stage_count( int32_t stages); /** Get number of configured stages. - * Return number of stages set by ecs_set_stage_count. + * Return number of stages set by ecs_set_stage_count(). * * @param world The world. * @return The number of stages used for threading. @@ -1950,31 +2323,20 @@ FLECS_API int32_t ecs_get_stage_count( const ecs_world_t *world); -/** Get current stage id. - * The stage id can be used by an application to learn about which stage it is - * using, which typically corresponds with the worker thread id. - * - * @param world The world. - * @return The stage id. - */ -FLECS_API -int32_t ecs_get_stage_id( - const ecs_world_t *world); - /** Get stage-specific world pointer. - * Flecs threads can safely invoke the API as long as they have a private + * Flecs threads can safely invoke the API as long as they have a private * context to write to, also referred to as the stage. This function returns a * pointer to a stage, disguised as a world pointer. * * Note that this function does not(!) create a new world. It simply wraps the * existing world in a thread-specific context, which the API knows how to * unwrap. The reason the stage is returned as an ecs_world_t is so that it - * can be passed transparently to the existing API functions, vs. having to - * create a dediated API for threading. + * can be passed transparently to the existing API functions, vs. having to + * create a dedicated API for threading. * * @param world The world. * @param stage_id The index of the stage to retrieve. - * @return A thread-specific pointer to the world. + * @return A thread-specific pointer to the world. */ FLECS_API ecs_world_t* ecs_get_stage( @@ -1983,7 +2345,7 @@ ecs_world_t* ecs_get_stage( /** Test whether the current world is readonly. * This function allows the code to test whether the currently used world - * is readonly or whether it allows for writing. + * is readonly or whether it allows for writing. * * @param world A pointer to a stage or the world. * @return True if the world or stage is readonly. @@ -1992,47 +2354,35 @@ FLECS_API bool ecs_stage_is_readonly( const ecs_world_t *world); -/** Create asynchronous stage. - * An asynchronous stage can be used to asynchronously queue operations for - * later merging with the world. An asynchronous stage is similar to a regular - * stage, except that it does not allow reading from the world. - * - * Asynchronous stages are never merged automatically, and must therefore be - * manually merged with the ecs_merge function. It is not necessary to call - * defer_begin or defer_end before and after enqueuing commands, as an - * asynchronous stage unconditionally defers operations. - * - * The application must ensure that no commands are added to the stage while the - * stage is being merged. - * - * An asynchronous stage must be cleaned up by ecs_async_stage_free. +/** Create unmanaged stage. + * Create a stage whose lifecycle is not managed by the world. Must be freed + * with ecs_stage_free(). * * @param world The world. * @return The stage. */ FLECS_API -ecs_world_t* ecs_async_stage_new( +ecs_world_t* ecs_stage_new( ecs_world_t *world); -/** Free asynchronous stage. - * The provided stage must be an asynchronous stage. If a non-asynchronous stage - * is provided, the operation will fail. +/** Free unmanaged stage. * * @param stage The stage to free. */ FLECS_API -void ecs_async_stage_free( +void ecs_stage_free( ecs_world_t *stage); -/** Test whether provided stage is asynchronous. +/** Get stage id. + * The stage id can be used by an application to learn about which stage it is + * using, which typically corresponds with the worker thread id. * - * @param stage The stage. - * @return True when the stage is asynchronous, false for a regular stage or - * world. + * @param world The world. + * @return The stage id. */ FLECS_API -bool ecs_stage_is_async( - ecs_world_t *stage); +int32_t ecs_stage_get_id( + const ecs_world_t *world); /** @} */ @@ -2056,7 +2406,7 @@ void ecs_set_ctx( ecs_ctx_free_t ctx_free); /** Set a world binding context. - * Same as ecs_set_ctx but for binding context. A binding context is intended + * Same as ecs_set_ctx() but for binding context. A binding context is intended * specifically for language bindings to store binding specific data. * * @param world The world. @@ -2073,7 +2423,7 @@ void ecs_set_binding_ctx( * This operation retrieves a previously set world context. * * @param world The world. - * @return The context set with ecs_set_ctx. If no context was set, the + * @return The context set with ecs_set_ctx(). If no context was set, the * function returns NULL. */ FLECS_API @@ -2084,13 +2434,21 @@ void* ecs_get_ctx( * This operation retrieves a previously set world binding context. * * @param world The world. - * @return The context set with ecs_set_binding_ctx. If no context was set, the + * @return The context set with ecs_set_binding_ctx(). If no context was set, the * function returns NULL. */ FLECS_API void* ecs_get_binding_ctx( const ecs_world_t *world); +/** Get build info. + * Returns information about the current Flecs build. + * + * @return A struct with information about the current Flecs build. + */ +FLECS_API +const ecs_build_info_t* ecs_get_build_info(void); + /** Get world info. * * @param world The world. @@ -2113,17 +2471,17 @@ void ecs_dim( ecs_world_t *world, int32_t entity_count); -/** Set a range for issueing new entity ids. - * This function constrains the entity identifiers returned by ecs_new to the +/** Set a range for issuing new entity ids. + * This function constrains the entity identifiers returned by ecs_new_w() to the * specified range. This operation can be used to ensure that multiple processes * can run in the same simulation without requiring a central service that - * coordinates issueing identifiers. - * - * If id_end is set to 0, the range is infinite. If id_end is set to a non-zero - * value, it has to be larger than id_start. If id_end is set and ecs_new is - * invoked after an id is issued that is equal to id_end, the application will + * coordinates issuing identifiers. + * + * If `id_end` is set to 0, the range is infinite. If `id_end` is set to a non-zero + * value, it has to be larger than `id_start`. If `id_end` is set and ecs_new() is + * invoked after an id is issued that is equal to `id_end`, the application will * abort. - * + * * @param world The world. * @param id_start The start of the range. * @param id_end The end of the range. @@ -2137,7 +2495,7 @@ void ecs_set_entity_range( /** Enable/disable range limits. * When an application is both a receiver of range-limited entities and a * producer of range-limited entities, range checking needs to be temporarily - * disabled when inserting received entities. Range checking is disabled on a + * disabled when inserting received entities. Range checking is disabled on a * stage, so setting this value is thread safe. * * @param world The world. @@ -2150,8 +2508,9 @@ bool ecs_enable_range_check( bool enable); /** Get the largest issued entity id (not counting generation). - * + * * @param world The world. + * @return The largest issued entity id. */ FLECS_API ecs_entity_t ecs_get_max_id( @@ -2162,11 +2521,11 @@ ecs_entity_t ecs_get_max_id( * application to function correctly. This may cause observable side effects * such as delayed triggering of events, which can be inconvenient when for * example running a test suite. - * + * * The flags parameter specifies which aperiodic actions to run. Specify 0 to * run all actions. Supported flags start with 'EcsAperiodic'. Flags identify * internal mechanisms and may change unannounced. - * + * * @param world The world. * @param flags The flags specifying which actions to run. */ @@ -2178,28 +2537,28 @@ void ecs_run_aperiodic( /** Cleanup empty tables. * This operation cleans up empty tables that meet certain conditions. Having * large amounts of empty tables does not negatively impact performance of the - * ECS, but can take up considerable amounts of memory, especially in + * ECS, but can take up considerable amounts of memory, especially in * applications with many components, and many components per entity. - * + * * The generation specifies the minimum number of times this operation has * to be called before an empty table is cleaned up. If a table becomes non * empty, the generation is reset. - * + * * The operation allows for both a "clear" generation and a "delete" - * generation. When the clear generation is reached, the table's + * generation. When the clear generation is reached, the table's * resources are freed (like component arrays) but the table itself is not * deleted. When the delete generation is reached, the empty table is deleted. - * + * * By specifying a non-zero id the cleanup logic can be limited to tables with * a specific (component) id. The operation will only increase the generation * count of matching tables. - * + * * The min_id_count specifies a lower bound for the number of components a table - * should have. Often the more components a table has, the more specific it is + * should have. Often the more components a table has, the more specific it is * and therefore less likely to be reused. - * + * * The time budget specifies how long the operation should take at most. - * + * * @param world The world. * @param id Optional component filter for the tables to evaluate. * @param clear_generation Free table data when generation > clear_generation. @@ -2237,28 +2596,35 @@ ecs_entity_t ecs_get_entity( /** Test if pointer is of specified type. * Usage: - * ecs_poly_is(ptr, ecs_world_t) - * + * + * @code + * flecs_poly_is(ptr, ecs_world_t) + * @endcode + * * This operation only works for poly types. - * + * * @param object The object to test. * @param type The id of the type. * @return True if the pointer is of the specified type. */ FLECS_API -bool ecs_poly_is_( +bool flecs_poly_is_( const ecs_poly_t *object, int32_t type); -#define ecs_poly_is(object, type)\ - ecs_poly_is_(object, type##_magic) +/** Test if pointer is of specified type. + * @see flecs_poly_is_() + */ +#define flecs_poly_is(object, type)\ + flecs_poly_is_(object, type##_magic) /** Make a pair id. - * This function is equivalent to using the ecs_pair macro, and is added for + * This function is equivalent to using the ecs_pair() macro, and is added for * convenience to make it easier for non C/C++ bindings to work with pairs. * * @param first The first element of the pair of the pair. * @param second The target of the pair. + * @return A pair id. */ FLECS_API ecs_id_t ecs_make_pair( @@ -2271,40 +2637,42 @@ ecs_id_t ecs_make_pair( /** * @defgroup entities Entities - * @brief Functions for working with `ecs_entity_t`. + * Functions for working with `ecs_entity_t`. + * * @{ */ /** * @defgroup creating_entities Creating & Deleting - * @brief Functions for creating and deleting entities. + * Functions for creating and deleting entities. + * * @{ */ /** Create new entity id. * This operation returns an unused entity id. This operation is guaranteed to - * return an empty entity as it does not use values set by ecs_set_scope or - * ecs_set_with. + * return an empty entity as it does not use values set by ecs_set_scope() or + * ecs_set_with(). * * @param world The world. * @return The new entity id. */ FLECS_API -ecs_entity_t ecs_new_id( +ecs_entity_t ecs_new( ecs_world_t *world); /** Create new low id. * This operation returns a new low id. Entity ids start after the - * FLECS_HI_COMPONENT_ID constant. This reserves a range of low ids for things + * FLECS_HI_COMPONENT_ID constant. This reserves a range of low ids for things * like components, and allows parts of the code to optimize operations. * - * Note that FLECS_HI_COMPONENT_ID does not represent the maximum number of + * Note that FLECS_HI_COMPONENT_ID does not represent the maximum number of * components that can be created, only the maximum number of components that * can take advantage of these optimizations. - * - * This operation is guaranteed to return an empty entity as it does not use - * values set by ecs_set_scope or ecs_set_with. - * + * + * This operation is guaranteed to return an empty entity as it does not use + * values set by ecs_set_scope() or ecs_set_with(). + * * This operation does not recycle ids. * * @param world The world. @@ -2317,7 +2685,7 @@ ecs_entity_t ecs_new_low_id( /** Create new entity with (component) id. * This operation creates a new entity with an optional (component) id. When 0 * is passed to the id parameter, no component is added to the new entity. - * + * * @param world The world. * @param id The component id to initialize the new entity with. * @return The new entity. @@ -2329,7 +2697,7 @@ ecs_entity_t ecs_new_w_id( /** Create new entity in table. * This operation creates a new entity in the specified table. - * + * * @param world The world. * @param table The table to which to add the new entity. * @return The new entity. @@ -2339,7 +2707,7 @@ ecs_entity_t ecs_new_w_table( ecs_world_t *world, ecs_table_t *table); -/** Find or create an entity. +/** Find or create an entity. * This operation creates a new entity, or modifies an existing one. When a name * is set in the ecs_entity_desc_t::name field and ecs_entity_desc_t::entity is * not set, the operation will first attempt to find an existing entity by that @@ -2350,8 +2718,8 @@ ecs_entity_t ecs_new_w_table( * the function will fail and return 0. * * If an id to a non-existing entity is provided, that entity id become alive. - * - * See the documentation of ecs_entity_desc_t for more details. + * + * See the documentation of ecs_entity_desc_t for more details. * * @param world The world. * @param desc Entity init parameters. @@ -2365,25 +2733,25 @@ ecs_entity_t ecs_entity_init( /** Bulk create/populate new entities. * This operation bulk inserts a list of new or predefined entities into a * single table. - * + * * The operation does not take ownership of component arrays provided by the * application. Components that are non-trivially copyable will be moved into * the storage. - * + * * The operation will emit OnAdd events for each added id, and OnSet events for * each component that has been set. - * + * * If no entity ids are provided by the application, the returned array of ids - * points to an internal datastructure which changes when new entities are + * points to an internal data structure which changes when new entities are * created/deleted. - * + * * If as a result of the operation triggers are invoked that deletes * entities and no entity ids were provided by the application, the returned * array of identifiers may be incorrect. To avoid this problem, an application - * can first call ecs_bulk_init to create empty entities, copy the array to one + * can first call ecs_bulk_init() to create empty entities, copy the array to one * that is owned by the application, and then use this array to populate the * entities. - * + * * @param world The world. * @param desc Bulk creation parameters. * @return Array with the list of entity ids created/populated. @@ -2394,9 +2762,9 @@ const ecs_entity_t* ecs_bulk_init( const ecs_bulk_desc_t *desc); /** Create N new entities. - * This operation is the same as ecs_new_w_id, but creates N entities + * This operation is the same as ecs_new_w_id(), but creates N entities * instead of one. - * + * * @param world The world. * @param id The component id to create the entities with. * @param count The number of entities to create. @@ -2412,7 +2780,7 @@ const ecs_entity_t* ecs_bulk_new_w_id( * This operation clones the components of one entity into another entity. If * no destination entity is provided, a new entity will be created. Component * values are not copied unless copy_value is true. - * + * * If the source entity has a name, it will not be copied to the destination * entity. This is to prevent having two entities with the same name under the * same parent, which is not allowed. @@ -2432,7 +2800,7 @@ ecs_entity_t ecs_clone( /** Delete an entity. * This operation will delete an entity and all of its components. The entity id - * will be made available for recycling. If the entity passed to ecs_delete is + * will be made available for recycling. If the entity passed to ecs_delete() is * not alive, the operation will have no side effects. * * @param world The world. @@ -2444,9 +2812,9 @@ void ecs_delete( ecs_entity_t entity); /** Delete all entities with the specified id. - * This will delete all entities (tables) that have the specified id. The id + * This will delete all entities (tables) that have the specified id. The id * may be a wildcard and/or a pair. - * + * * @param world The world. * @param id The id. */ @@ -2459,12 +2827,13 @@ void ecs_delete_with( /** * @defgroup adding_removing Adding & Removing - * @brief Functions for adding and removing components. + * Functions for adding and removing components. + * * @{ */ /** Add a (component) id to an entity. - * This operation adds a single (component) id to an entity. If the entity + * This operation adds a single (component) id to an entity. If the entity * already has the id, this operation will have no side effects. * * @param world The world. @@ -2478,7 +2847,7 @@ void ecs_add_id( ecs_id_t id); /** Remove a (component) id from an entity. - * This operation removes a single (component) id to an entity. If the entity + * This operation removes a single (component) id to an entity. If the entity * does not have the id, this operation will have no side effects. * * @param world The world. @@ -2491,30 +2860,59 @@ void ecs_remove_id( ecs_entity_t entity, ecs_id_t id); -/** Add override for (component) id. - * Adding an override to an entity ensures that when the entity is instantiated - * (by adding an IsA relationship to it) the component with the override is - * copied to a component that is private to the instance. By default components - * reachable through an IsA relationship are shared. +/** Add auto override for (component) id. + * An auto override is a component that is automatically added to an entity when + * it is instantiated from a prefab. Auto overrides are added to the entity that + * is inherited from (usually a prefab). For example: + * + * @code + * ecs_entity_t prefab = ecs_insert(world, + * ecs_value(Position, {10, 20}), + * ecs_value(Mass, {100})); * - * Adding an override does not add the component. If an override is added to an - * entity that does not have the component, it will still be added to the - * instance, but with an uninitialized value (unless the component has a ctor). - * When the entity does have the entity, the component of the instance will be - * initialized with the value of the component on the entity. + * ecs_auto_override(world, prefab, Position); * - * This is the same as what happens when calling ecs_add_id for an id that is - * inherited (reachable through an IsA relationship). + * ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + * assert(ecs_owns(world, inst, Position)); // true + * assert(ecs_owns(world, inst, Mass)); // false + * @endcode * - * This operation is equivalent to doing: - * ecs_add_id(world, entity, ECS_OVERRIDE | id); + * An auto override is equivalent to a manual override: * + * @code + * ecs_entity_t prefab = ecs_insert(world, + * ecs_value(Position, {10, 20}), + * ecs_value(Mass, {100})); + * + * ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + * assert(ecs_owns(world, inst, Position)); // false + * ecs_add(world, inst, Position); // manual override + * assert(ecs_owns(world, inst, Position)); // true + * assert(ecs_owns(world, inst, Mass)); // false + * @endcode + * + * This operation is equivalent to manually adding the id with the AUTO_OVERRIDE + * bit applied: + * + * @code + * ecs_add_id(world, entity, ECS_AUTO_OVERRIDE | id); + * @endcode + * + * When a component is overridden and inherited from a prefab, the value from + * the prefab component is copied to the instance. When the component is not + * inherited from a prefab, it is added to the instance as if using ecs_add_id(). + * + * Overriding is the default behavior on prefab instantiation. Auto overriding + * is only useful for components with the `(OnInstantiate, Inherit)` trait. + * When a component has the `(OnInstantiate, DontInherit)` trait and is overridden + * the component is added, but the value from the prefab will not be copied. + * * @param world The world. * @param entity The entity. - * @param id The id to override. + * @param id The (component) id to auto override. */ FLECS_API -void ecs_override_id( +void ecs_auto_override_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); @@ -2533,7 +2931,7 @@ void ecs_clear( /** Remove all instances of the specified (component) id. * This will remove the specified id from all entities (tables). The id may be * a wildcard and/or a pair. - * + * * @param world The world. * @param id The id. */ @@ -2555,10 +2953,10 @@ ecs_entity_t ecs_set_with( ecs_id_t id); /** Get current with id. - * Get the id set with ecs_set_with. + * Get the id set with ecs_set_with(). * * @param world The world. - * @return The last id provided to ecs_set_with. + * @return The last id provided to ecs_set_with(). */ FLECS_API ecs_id_t ecs_get_with( @@ -2568,14 +2966,15 @@ ecs_id_t ecs_get_with( /** * @defgroup enabling_disabling Enabling & Disabling - * @brief Functions for enabling/disabling entities and components. + * Functions for enabling/disabling entities and components. + * * @{ */ /** Enable or disable entity. * This operation enables or disables an entity by adding or removing the - * EcsDisabled tag. A disabled entity will not be matched with any systems, - * unless the system explicitly specifies the EcsDisabled tag. + * #EcsDisabled tag. A disabled entity will not be matched with any systems, + * unless the system explicitly specifies the #EcsDisabled tag. * * @param world The world. * @param entity The entity to enable or disable. @@ -2600,7 +2999,7 @@ void ecs_enable( * @param id The component. * @param enable True to enable the component, false to disable. */ -FLECS_API +FLECS_API void ecs_enable_id( ecs_world_t *world, ecs_entity_t entity, @@ -2610,14 +3009,14 @@ void ecs_enable_id( /** Test if component is enabled. * Test whether a component is currently enabled or disabled. This operation * will return true when the entity has the component and if it has not been - * disabled by ecs_enable_component. + * disabled by ecs_enable_component(). * * @param world The world. * @param entity The entity. * @param id The component. * @return True if the component is enabled, otherwise false. */ -FLECS_API +FLECS_API bool ecs_is_enabled_id( const ecs_world_t *world, ecs_entity_t entity, @@ -2627,18 +3026,24 @@ bool ecs_is_enabled_id( /** * @defgroup getting Getting & Setting - * @brief Functions for getting/setting components. + * Functions for getting/setting components. + * * @{ */ /** Get an immutable pointer to a component. * This operation obtains a const pointer to the requested component. The * operation accepts the component entity id. + * + * This operation can return inherited components reachable through an `IsA` + * relationship. * * @param world The world. * @param entity The entity. * @param id The id of the component to get. * @return The component pointer, NULL if the entity does not have the component. + * + * @see ecs_get_mut_id() */ FLECS_API const void* ecs_get_id( @@ -2646,53 +3051,28 @@ const void* ecs_get_id( ecs_entity_t entity, ecs_id_t id); -/** Create a component ref. - * A ref is a handle to an entity + component which caches a small amount of - * data to reduce overhead of repeatedly accessing the component. Use - * ecs_ref_get to get the component data. +/** Get a mutable pointer to a component. + * This operation obtains a mutable pointer to the requested component. The + * operation accepts the component entity id. + * + * Unlike ecs_get_id(), this operation does not return inherited components. * * @param world The world. * @param entity The entity. - * @param id The id of the component. - * @return The reference. - */ -FLECS_API -ecs_ref_t ecs_ref_init_id( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_id_t id); - -/** Get component from ref. - * Get component pointer from ref. The ref must be created with ecs_ref_init. - * - * @param world The world. - * @param ref The ref. - * @param id The component id. + * @param id The id of the component to get. * @return The component pointer, NULL if the entity does not have the component. */ FLECS_API -void* ecs_ref_get_id( +void* ecs_get_mut_id( const ecs_world_t *world, - ecs_ref_t *ref, + ecs_entity_t entity, ecs_id_t id); -/** Update ref. - * Ensures contents of ref are up to date. Same as ecs_ref_get_id, but does not - * return pointer to component id. - * - * @param world The world. - * @param ref The ref. - */ -FLECS_API -void ecs_ref_update( - const ecs_world_t *world, - ecs_ref_t *ref); - /** Get a mutable pointer to a component. * 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 + * + * If ensure 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 @@ -2701,44 +3081,101 @@ void ecs_ref_update( * @param entity The entity. * @param id The entity id of the component to obtain. * @return The component pointer. + * + * @see ecs_ensure_modified_id() + * @see ecs_emplace_id() */ FLECS_API -void* ecs_get_mut_id( +void* ecs_ensure_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); -/** Combines get_mut + modifed in single operation. - * This operation is a more efficient alternative to calling ecs_get_mut_id and - * ecs_modified_id separately. This operation is only valid when the world is in +/** Combines ensure + modified in single operation. + * This operation is a more efficient alternative to calling ecs_ensure_id() and + * ecs_modified_id() separately. This operation is only valid when the world is in * deferred mode, which ensures that the Modified event is not emitted before * the modification takes place. - * + * * @param world The world. * @param entity The entity. * @param id The id of the component to obtain. * @return The component pointer. */ FLECS_API -void* ecs_get_mut_modified_id( +void* ecs_ensure_modified_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); -/** Begin exclusive write access to entity. - * This operation provides safe exclusive access to the components of an entity - * without the overhead of deferring operations. - * - * When this operation is called simultaneously for the same entity more than - * once it will throw an assert. Note that for this to happen, asserts must be - * enabled. It is up to the application to ensure that access is exclusive, for - * example by using a read-write mutex. +/** Create a component ref. + * A ref is a handle to an entity + component which caches a small amount of + * data to reduce overhead of repeatedly accessing the component. Use + * ecs_ref_get() to get the component data. + * + * @param world The world. + * @param entity The entity. + * @param id The id of the component. + * @return The reference. + */ +FLECS_API +ecs_ref_t ecs_ref_init_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id); + +/** Get component from ref. + * Get component pointer from ref. The ref must be created with ecs_ref_init(). + * + * @param world The world. + * @param ref The ref. + * @param id The component id. + * @return The component pointer, NULL if the entity does not have the component. + */ +FLECS_API +void* ecs_ref_get_id( + const ecs_world_t *world, + ecs_ref_t *ref, + ecs_id_t id); + +/** Update ref. + * Ensures contents of ref are up to date. Same as ecs_ref_get_id(), but does not + * return pointer to component id. + * + * @param world The world. + * @param ref The ref. + */ +FLECS_API +void ecs_ref_update( + const ecs_world_t *world, + ecs_ref_t *ref); + +/** Find record for entity. + * An entity record contains the table and row for the entity. * + * @param world The world. + * @param entity The entity. + * @return The record, NULL if the entity does not exist. + */ +FLECS_API +ecs_record_t* ecs_record_find( + const ecs_world_t *world, + ecs_entity_t entity); + +/** Begin exclusive write access to entity. + * This operation provides safe exclusive access to the components of an entity + * without the overhead of deferring operations. + * + * When this operation is called simultaneously for the same entity more than + * once it will throw an assert. Note that for this to happen, asserts must be + * enabled. It is up to the application to ensure that access is exclusive, for + * example by using a read-write mutex. + * * Exclusive access is enforced at the table level, so only one entity can be * exclusively accessed per table. The exclusive access check is thread safe. - * - * This operation must be followed up with ecs_write_end. - * + * + * This operation must be followed up with ecs_write_end(). + * * @param world The world. * @param entity The entity. * @return A record to the entity. @@ -2749,9 +3186,9 @@ ecs_record_t* ecs_write_begin( ecs_entity_t entity); /** End exclusive write access to entity. - * This operation ends exclusive access, and must be called after - * ecs_write_begin. - * + * This operation ends exclusive access, and must be called after + * ecs_write_begin(). + * * @param record Record to the entity. */ FLECS_API @@ -2761,17 +3198,17 @@ void ecs_write_end( /** Begin read access to entity. * This operation provides safe read access to the components of an entity. * Multiple simultaneous reads are allowed per entity. - * + * * This operation ensures that code attempting to mutate the entity's table will * throw an assert. Note that for this to happen, asserts must be enabled. It is * up to the application to ensure that this does not happen, for example by * using a read-write mutex. - * + * * This operation does *not* provide the same guarantees as a read-write mutex, - * as it is possible to call ecs_read_begin after calling ecs_write_begin. It is + * as it is possible to call ecs_read_begin() after calling ecs_write_begin(). It is * up to application has to ensure that this does not happen. - * - * This operation must be followed up with ecs_read_end. + * + * This operation must be followed up with ecs_read_end(). * * @param world The world. * @param entity The entity. @@ -2783,7 +3220,7 @@ const ecs_record_t* ecs_read_begin( ecs_entity_t entity); /** End read access to entity. - * This operation ends read access, and must be called after ecs_read_begin. + * This operation ends read access, and must be called after ecs_read_begin(). * * @param record Record to the entity. */ @@ -2793,51 +3230,55 @@ void ecs_read_end( /** Get entity corresponding with record. * This operation only works for entities that are not empty. - * + * * @param record The record for which to obtain the entity id. + * @return The entity id for the record. */ FLECS_API ecs_entity_t ecs_record_get_entity( const ecs_record_t *record); /** Get component from entity record. - * This operation returns a pointer to a component for the entity + * This operation returns a pointer to a component for the entity * associated with the provided record. For safe access to the component, obtain - * the record with ecs_read_begin or ecs_write_begin. - * + * the record with ecs_read_begin() or ecs_write_begin(). + * * Obtaining a component from a record is faster than obtaining it from the * entity handle, as it reduces the number of lookups required. - * + * * @param world The world. * @param record Record to the entity. * @param id The (component) id. * @return Pointer to component, or NULL if entity does not have the component. + * + * @see ecs_record_ensure_id() */ FLECS_API const void* ecs_record_get_id( - ecs_world_t *world, + const ecs_world_t *world, const ecs_record_t *record, ecs_id_t id); -/** Same as ecs_record_get_id, but returns a mutable pointer. - * For safe access to the component, obtain the record with ecs_write_begin. - * +/** Same as ecs_record_get_id(), but returns a mutable pointer. + * For safe access to the component, obtain the record with ecs_write_begin(). + * * @param world The world. * @param record Record to the entity. * @param id The (component) id. * @return Pointer to component, or NULL if entity does not have the component. */ FLECS_API -void* ecs_record_get_mut_id( +void* ecs_record_ensure_id( ecs_world_t *world, ecs_record_t *record, ecs_id_t id); -/** Test if entity for record has component. - * +/** Test if entity for record has a (component) id. + * * @param world The world. * @param record Record to the entity. * @param id The (component) id. + * @return Whether the entity has the component. */ FLECS_API bool ecs_record_has_id( @@ -2845,35 +3286,64 @@ bool ecs_record_has_id( const ecs_record_t *record, ecs_id_t id); +/** Get component pointer from column/record. + * This returns a pointer to the component using a table column index. The + * table's column index can be found with ecs_table_get_column_index(). + * + * Usage: + * @code + * ecs_record_t *r = ecs_record_find(world, entity); + * int32_t column = ecs_table_get_column_index(world, table, ecs_id(Position)); + * Position *ptr = ecs_record_get_by_column(r, column, sizeof(Position)); + * @endcode + * + * @param record The record. + * @param column The column index in the entity's table. + * @param size The component size. + * @return The component pointer. + */ +FLECS_API +void* ecs_record_get_by_column( + const ecs_record_t *record, + int32_t column, + size_t size); + /** Emplace a component. - * Emplace is similar to get_mut except that the component constructor is not - * invoked for the returned pointer, allowing the component to be "constructed" - * directly in the storage. - * - * Emplace can only be used if the entity does not yet have the component. If - * the entity has the component, the operation will fail. + * Emplace is similar to ecs_ensure_id() except that the component constructor + * is not invoked for the returned pointer, allowing the component to be + * constructed directly in the storage. + * + * When the `is_new` parameter is not provided, the operation will assert when the + * component already exists. When the `is_new` parameter is provided, it will + * indicate whether the returned storage has been constructed. + * + * When `is_new` indicates that the storage has not yet been constructed, it must + * be constructed by the code invoking this operation. Not constructing the + * component will result in undefined behavior. * * @param world The world. * @param entity The entity. * @param id The component to obtain. + * @param is_new Whether this is an existing or new component. * @return The (uninitialized) component pointer. */ FLECS_API void* ecs_emplace_id( ecs_world_t *world, ecs_entity_t entity, - ecs_id_t id); + ecs_id_t id, + bool *is_new); /** Signal that a component has been modified. * This operation is usually used after modifying a component value obtained by - * ecs_get_mut_id. The operation will mark the component as dirty, and invoke + * ecs_ensure_id(). The operation will mark the component as dirty, and invoke * OnSet observers and hooks. * * @param world The world. * @param entity The entity. * @param id The id of the component that was modified. */ -FLECS_API +FLECS_API void ecs_modified_id( ecs_world_t *world, ecs_entity_t entity, @@ -2881,8 +3351,8 @@ void ecs_modified_id( /** Set the value of a component. * This operation allows an application to set the value of a component. The - * operation is equivalent to calling ecs_get_mut_id followed by - * ecs_modified_id. The operation will not modify the value of the passed in + * operation is equivalent to calling ecs_ensure_id() followed by + * ecs_modified_id(). The operation will not modify the value of the passed in * component. If the component has a copy hook registered, it will be used to * copy in the component. * @@ -2893,10 +3363,9 @@ void ecs_modified_id( * @param id The id of the component to set. * @param size The size of the pointed-to value. * @param ptr The pointer to the value. - * @return The entity. A new entity if no entity was provided. */ FLECS_API -ecs_entity_t ecs_set_id( +void ecs_set_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, @@ -2907,19 +3376,20 @@ ecs_entity_t ecs_set_id( /** * @defgroup liveliness Entity Liveliness - * @brief Functions for testing and modifying entity liveliness. + * Functions for testing and modifying entity liveliness. + * * @{ */ /** Test whether an entity is valid. - * Entities that are valid can be used with API functions. Using invalid + * Entities that are valid can be used with API functions. Using invalid * entities with API operations will cause the function to panic. * * An entity is valid if it is not 0 and if it is alive. * - * is_valid will return true for ids that don't exist (alive or not alive). This - * allows for using ids that have never been created by ecs_new or similar. In - * this the function differs from ecs_is_alive, which will return false for + * ecs_is_valid() will return true for ids that don't exist (alive or not alive). This + * allows for using ids that have never been created by ecs_new_w() or similar. In + * this the function differs from ecs_is_alive(), which will return false for * entities that do not yet exist. * * The operation will return false for an id that exists and is not alive, as @@ -2936,22 +3406,24 @@ bool ecs_is_valid( /** Test whether an entity is alive. * Entities are alive after they are created, and become not alive when they are - * deleted. Operations that return alive ids are (amongst others) ecs_new_id, - * ecs_new_low_id and ecs_entity_init. Ids can be made alive with the ecs_ensure + * deleted. Operations that return alive ids are (amongst others) ecs_new(), + * ecs_new_low_id() and ecs_entity_init(). Ids can be made alive with the ecs_make_alive() * function. - * + * * After an id is deleted it can be recycled. Recycled ids are different from * the original id in that they have a different generation count. This makes it * possible for the API to distinguish between the two. An example: - * - * ecs_entity_t e1 = ecs_new_id(world); - * ecs_is_alive(world, e1); // true - * ecs_delete(world, e1); - * ecs_is_alive(world, e1); // false - * - * ecs_entity_t e2 = ecs_new_id(world); // recycles e1 - * ecs_is_alive(world, e2); // true - * ecs_is_alive(world, e1); // false + * + * @code + * ecs_entity_t e1 = ecs_new(world); + * ecs_is_alive(world, e1); // true + * ecs_delete(world, e1); + * ecs_is_alive(world, e1); // false + * + * ecs_entity_t e2 = ecs_new(world); // recycles e1 + * ecs_is_alive(world, e2); // true + * ecs_is_alive(world, e1); // false + * @endcode * * @param world The world. * @param e The entity. @@ -2971,29 +3443,13 @@ FLECS_API ecs_id_t ecs_strip_generation( ecs_entity_t e); -/** Override the generation of an entity. - * The generation count of an entity is increased each time an entity is deleted - * and is used to test whether an entity id is alive. - * - * This operation overrides the current generation of an entity with the - * specified generation, which can be useful if an entity is externally managed, - * like for external pools, savefiles or netcode. - * - * @param world The world. - * @param entity Entity for which to set the generation with the new generation. - */ -FLECS_API -void ecs_set_entity_generation( - ecs_world_t *world, - ecs_entity_t entity); - /** Get alive identifier. * In some cases an application may need to work with identifiers from which * the generation has been stripped. A typical scenario in which this happens is * when iterating relationships in an entity type. * - * For example, when obtaining the parent id from a ChildOf relationship, the parent - * (second element of the pair) will have been stored in a 32 bit value, which + * For example, when obtaining the parent id from a `ChildOf` relationship, the parent + * (second element of the pair) will have been stored in a 32 bit value, which * cannot store the entity generation. This function can retrieve the identifier * with the current generation for that id. * @@ -3011,9 +3467,9 @@ ecs_entity_t ecs_get_alive( /** Ensure id is alive. * This operation ensures that the provided id is alive. This is useful in * scenarios where an application has an existing id that has not been created - * with ecs_new (such as a global constant or an id from a remote application). - * - * When this operation is successful it guarantees that the provided id exists, + * with ecs_new_w() (such as a global constant or an id from a remote application). + * + * When this operation is successful it guarantees that the provided id exists, * is valid and is alive. * * Before this operation the id must either not be alive or have a generation @@ -3021,43 +3477,45 @@ ecs_entity_t ecs_get_alive( * * If the provided id has a non-zero generation count and the id does not exist * in the world, the id will be created with the specified generation. - * + * * If the provided id is alive and has a generation count that does not match * the provided id, the operation will fail. * * @param world The world. * @param entity The entity id to make alive. + * + * @see ecs_make_alive_id() */ FLECS_API -void ecs_ensure( +void ecs_make_alive( ecs_world_t *world, ecs_entity_t entity); -/** Same as ecs_ensure, but for (component) ids. +/** Same as ecs_make_alive(), but for (component) ids. * An id can be an entity or pair, and can contain id flags. This operation * ensures that the entity (or entities, for a pair) are alive. - * + * * When this operation is successful it guarantees that the provided id can be * used in operations that accept an id. - * + * * Since entities in a pair do not encode their generation ids, this operation * will not fail when an entity with non-zero generation count already exists in - * the world. - * - * This is different from ecs_ensure, which will fail if attempted with an id - * that has generation 0 and an entity with a non-zero generation is currently + * the world. + * + * This is different from ecs_make_alive(), which will fail if attempted with an id + * that has generation 0 and an entity with a non-zero generation is currently * alive. - * + * * @param world The world. * @param id The id to make alive. */ FLECS_API -void ecs_ensure_id( +void ecs_make_alive_id( ecs_world_t *world, ecs_id_t id); /** Test whether an entity exists. - * Similar as ecs_is_alive, but ignores entity generation count. + * Similar as ecs_is_alive(), but ignores entity generation count. * * @param world The world. * @param entity The entity. @@ -3068,11 +3526,31 @@ bool ecs_exists( const ecs_world_t *world, ecs_entity_t entity); +/** Override the generation of an entity. + * The generation count of an entity is increased each time an entity is deleted + * and is used to test whether an entity id is alive. + * + * This operation overrides the current generation of an entity with the + * specified generation, which can be useful if an entity is externally managed, + * like for external pools, savefiles or netcode. + * + * This operation is similar to ecs_make_alive(), except that it will also + * override the generation of an alive entity. + * + * @param world The world. + * @param entity Entity for which to set the generation with the new generation. + */ +FLECS_API +void ecs_set_version( + ecs_world_t *world, + ecs_entity_t entity); + /** @} */ /** * @defgroup entity_info Entity Information. - * @brief Get information from entity. + * Get information from entity. + * * @{ */ @@ -3099,8 +3577,8 @@ ecs_table_t* ecs_get_table( ecs_entity_t entity); /** Convert type to string. - * The result of this operation must be freed with ecs_os_free. - * + * The result of this operation must be freed with ecs_os_free(). + * * @param world The world. * @param type The type. * @return The stringified type. @@ -3111,12 +3589,15 @@ char* ecs_type_str( const ecs_type_t* type); /** Convert table to string. - * Same as ecs_type_str(world, ecs_table_get_type(table)). The result of this - * operation must be freed with ecs_os_free. + * Same as `ecs_type_str(world, ecs_table_get_type(table))`. The result of this + * operation must be freed with ecs_os_free(). * * @param world The world. * @param table The table. * @return The stringified table type. + * + * @see ecs_table_get_type() + * @see ecs_type_str() */ FLECS_API char* ecs_table_str( @@ -3125,14 +3606,17 @@ char* ecs_table_str( /** Convert entity to string. * Same as combining: - * - ecs_get_fullpath(world, entity) + * - ecs_get_path(world, entity) * - ecs_type_str(world, ecs_get_type(world, entity)) - * - * The result of this operation must be freed with ecs_os_free. + * + * The result of this operation must be freed with ecs_os_free(). * * @param world The world. * @param entity The entity. * @return The entity path with stringified type. + * + * @see ecs_get_path() + * @see ecs_type_str() */ FLECS_API char* ecs_entity_str( @@ -3146,6 +3630,8 @@ char* ecs_entity_str( * @param entity The entity. * @param id The id to test for. * @return True if the entity has the id, false if not. + * + * @see ecs_owns_id() */ FLECS_API bool ecs_has_id( @@ -3155,8 +3641,8 @@ bool ecs_has_id( /** Test if an entity owns an id. * This operation returns true if the entity has the specified id. The operation - * behaves the same as ecs_has_id, except that it will return false for - * components that are inherited through an IsA relationship. + * behaves the same as ecs_has_id(), except that it will return false for + * components that are inherited through an `IsA` relationship. * * @param world The world. * @param entity The entity. @@ -3170,8 +3656,8 @@ bool ecs_owns_id( ecs_id_t id); /** Get the target of a relationship. - * This will return a target (second element of a pair) of the entity for the - * specified relationship. The index allows for iterating through the targets, + * This will return a target (second element of a pair) of the entity for the + * specified relationship. The index allows for iterating through the targets, * if a single entity has multiple targets for the same relationship. * * If the index is larger than the total number of instances the entity has for @@ -3190,13 +3676,18 @@ ecs_entity_t ecs_get_target( ecs_entity_t rel, int32_t index); -/** Get parent (target of ChildOf relationship) for entity. +/** Get parent (target of `ChildOf` relationship) for entity. * This operation is the same as calling: - * ecs_get_target(world, entity, EcsChildOf, 0); - * + * + * @code + * ecs_get_target(world, entity, EcsChildOf, 0); + * @endcode + * * @param world The world. * @param entity The entity. * @return The parent of the entity, 0 if the entity has no parent. + * + * @see ecs_get_target() */ FLECS_API ecs_entity_t ecs_get_parent( @@ -3208,13 +3699,15 @@ ecs_entity_t ecs_get_parent( * the specified relationship. If the entity itself has the id then entity will * be returned. If the id cannot be found on the entity or by following the * relationship, the operation will return 0. - * + * * This operation can be used to lookup, for example, which prefab is providing - * a component by specifying the IsA relationship: - * - * // Is Position provided by the entity or one of its base entities? - * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) - * + * a component by specifying the `IsA` relationship: + * + * @code + * // Is Position provided by the entity or one of its base entities? + * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) + * @endcode + * * @param world The world. * @param entity The entity. * @param rel The relationship to follow. @@ -3229,10 +3722,10 @@ ecs_entity_t ecs_get_target_for_id( ecs_id_t id); /** Return depth for entity in tree for the specified relationship. - * Depth is determined by counting the number of targets encountered while + * Depth is determined by counting the number of targets encountered while * traversing up the relationship tree for rel. Only acyclic relationships are * supported. - * + * * @param world The world. * @param entity The entity. * @param rel The relationship. @@ -3244,48 +3737,6 @@ int32_t ecs_get_depth( ecs_entity_t entity, ecs_entity_t rel); -typedef struct ecs_flatten_desc_t { - /* When true, the flatten operation will not remove names from entities in - * the flattened tree. This may fail if entities from different subtrees - * have the same name. */ - bool keep_names; - - /* When true, the flattened tree won't contain information about the - * original depth of the entities. This can reduce fragmentation, but may - * cause existing code, such as cascade queries, to no longer work. */ - bool lose_depth; -} ecs_flatten_desc_t; - -/** Recursively flatten relationship for target entity (experimental). - * This operation combines entities in the subtree of the specified pair from - * different parents in the same table. This can reduce memory fragmentation - * and reduces the number of tables in the storage, which improves RAM - * utilization and various other operations, such as entity cleanup. - * - * The lifecycle of entities in a fixed subtree are bound to the specified - * parent. Entities in a fixed subtree cannot be deleted individually. Entities - * can also not change the target of the fixed relationship, which includes - * removing the relationship. - * - * Entities in a fixed subtree are still fragmented on subtree depth. This - * ensures that entities can still be iterated in breadth-first order with the - * cascade query modifier. - * - * The current implementation is limited to exclusive acyclic relationships, and - * does not allow for adding/removing to entities in flattened tables. An entity - * may only be flattened for a single relationship. Future iterations of the - * feature may remove these limitations. - * - * @param world The world. - * @param pair The relationship pair from which to start flattening. - * @param desc Options for flattening the tree. - */ -FLECS_API -void ecs_flatten( - ecs_world_t *world, - ecs_id_t pair, - const ecs_flatten_desc_t *desc); - /** Count entities that have the specified id. * Returns the number of entities that have the specified id. * @@ -3303,16 +3754,19 @@ int32_t ecs_count_id( /** * @defgroup paths Entity Names - * @brief Functions for working with entity names and paths. + * Functions for working with entity names and paths. + * * @{ */ /** Get the name of an entity. - * This will return the name stored in (EcsIdentifier, EcsName). + * This will return the name stored in `(EcsIdentifier, EcsName)`. * * @param world The world. * @param entity The entity. * @return The type of the entity, NULL if the entity has no name. + * + * @see ecs_set_name() */ FLECS_API const char* ecs_get_name( @@ -3320,11 +3774,13 @@ const char* ecs_get_name( ecs_entity_t entity); /** Get the symbol of an entity. - * This will return the symbol stored in (EcsIdentifier, EcsSymbol). + * This will return the symbol stored in `(EcsIdentifier, EcsSymbol)`. * * @param world The world. * @param entity The entity. * @return The type of the entity, NULL if the entity has no name. + * + * @see ecs_set_symbol() */ FLECS_API const char* ecs_get_symbol( @@ -3335,12 +3791,14 @@ const char* ecs_get_symbol( * This will set or overwrite the name of an entity. If no entity is provided, * a new entity will be created. * - * The name is stored in (EcsIdentifier, EcsName). + * The name is stored in `(EcsIdentifier, EcsName)`. * * @param world The world. * @param entity The entity. * @param name The name. * @return The provided entity, or a new entity if 0 was provided. + * + * @see ecs_get_name() */ FLECS_API ecs_entity_t ecs_set_name( @@ -3358,6 +3816,8 @@ ecs_entity_t ecs_set_name( * @param entity The entity. * @param symbol The symbol. * @return The provided entity, or a new entity if 0 was provided. + * + * @see ecs_get_symbol() */ FLECS_API ecs_entity_t ecs_set_symbol( @@ -3365,13 +3825,13 @@ ecs_entity_t ecs_set_symbol( ecs_entity_t entity, const char *symbol); -/** Set alias for entity. - * An entity can be looked up using its alias from the root scope without +/** Set alias for entity. + * An entity can be looked up using its alias from the root scope without * providing the fully qualified name if its parent. An entity can only have * a single alias. - * - * The symbol is stored in (EcsIdentifier, EcsAlias). - * + * + * The symbol is stored in `(EcsIdentifier, EcsAlias)`. + * * @param world The world. * @param entity The entity. * @param alias The alias. @@ -3382,18 +3842,25 @@ void ecs_set_alias( ecs_entity_t entity, const char *alias); -/** Lookup an entity by name. - * Returns an entity that matches the specified name. Only looks for entities in - * the current scope (root if no scope is provided). +/** Lookup an entity by it's path. + * This operation is equivalent to calling: + * + * @code + * ecs_lookup_path_w_sep(world, 0, path, ".", NULL, true); + * @endcode * * @param world The world. - * @param name The entity name. - * @return The entity with the specified name, or 0 if no entity was found. + * @param path The entity path. + * @return The entity with the specified path, or 0 if no entity was found. + * + * @see ecs_lookup_child() + * @see ecs_lookup_path_w_sep() + * @see ecs_lookup_symbol() */ FLECS_API ecs_entity_t ecs_lookup( const ecs_world_t *world, - const char *name); + const char *path); /** Lookup a child entity by name. * Returns an entity that matches the specified name. Only looks for entities in @@ -3401,8 +3868,13 @@ ecs_entity_t ecs_lookup( * root if no scope is provided). * * @param world The world. + * @param parent The parent for which to lookup the child. * @param name The entity name. * @return The entity with the specified name, or 0 if no entity was found. + * + * @see ecs_lookup() + * @see ecs_lookup_path_w_sep() + * @see ecs_lookup_symbol() */ FLECS_API ecs_entity_t ecs_lookup_child( @@ -3415,7 +3887,7 @@ ecs_entity_t ecs_lookup_child( * operation will use the provided separator to tokenize the path expression. If * the provided path contains the prefix, the search will start from the root. * - * If the entity is not found in the provided parent, the operation will + * If the entity is not found in the provided parent, the operation will * continue to search in the parent of the parent, until the root is reached. If * the entity is still not found, the lookup will search in the flecs.core * scope. If the entity is not found there either, the function returns 0. @@ -3427,6 +3899,10 @@ ecs_entity_t ecs_lookup_child( * @param prefix The path prefix. * @param recursive Recursively traverse up the tree until entity is found. * @return The entity if found, else 0. + * + * @see ecs_lookup() + * @see ecs_lookup_child() + * @see ecs_lookup_symbol() */ FLECS_API ecs_entity_t ecs_lookup_path_w_sep( @@ -3438,17 +3914,21 @@ ecs_entity_t ecs_lookup_path_w_sep( bool recursive); /** Lookup an entity by its symbol name. - * This looks up an entity by symbol stored in (EcsIdentifier, EcsSymbol). The + * This looks up an entity by symbol stored in `(EcsIdentifier, EcsSymbol)`. The * operation does not take into account hierarchies. * - * This operation can be useful to resolve, for example, a type by its C + * This operation can be useful to resolve, for example, a type by its C * identifier, which does not include the Flecs namespacing. - * + * * @param world The world. * @param symbol The symbol. * @param lookup_as_path If not found as a symbol, lookup as path. * @param recursive If looking up as path, recursively traverse up the tree. * @return The entity if found, else 0. + * + * @see ecs_lookup() + * @see ecs_lookup_child() + * @see ecs_lookup_path_w_sep() */ FLECS_API ecs_entity_t ecs_lookup_symbol( @@ -3459,12 +3939,12 @@ ecs_entity_t ecs_lookup_symbol( /** Get a path identifier for an entity. * This operation creates a path that contains the names of the entities from - * the specified parent to the provided entity, separated by the provided + * the specified parent to the provided entity, separated by the provided * separator. If no parent is provided the path will be relative to the root. If * a prefix is provided, the path will be prefixed by the prefix. * * If the parent is equal to the provided child, the operation will return an - * empty string. If a nonzero component is provided, the path will be created by + * empty string. If a nonzero component is provided, the path will be created by * looking for parents with that component. * * The returned path should be freed by the application. @@ -3475,6 +3955,8 @@ ecs_entity_t ecs_lookup_symbol( * @param sep The separator to use between path elements. * @param prefix The initial character to use for root elements. * @return The relative entity path. + * + * @see ecs_get_path_w_sep_buf() */ FLECS_API char* ecs_get_path_w_sep( @@ -3485,14 +3967,16 @@ char* ecs_get_path_w_sep( const char *prefix); /** Write path identifier to buffer. - * Same as ecs_get_path_w_sep, but writes result to an ecs_strbuf_t. - * + * Same as ecs_get_path_w_sep(), but writes result to an ecs_strbuf_t. + * * @param world The world. * @param parent The entity from which to create the path. * @param child The entity to which to create the path. * @param sep The separator to use between path elements. * @param prefix The initial character to use for root elements. * @param buf The buffer to write to. + * + * @see ecs_get_path_w_sep() */ void ecs_get_path_w_sep_buf( const ecs_world_t *world, @@ -3500,7 +3984,8 @@ void ecs_get_path_w_sep_buf( ecs_entity_t child, const char *sep, const char *prefix, - ecs_strbuf_t *buf); + ecs_strbuf_t *buf, + bool escape); /** Find or create entity from path. * This operation will find or create an entity from a path, and will create any @@ -3526,7 +4011,7 @@ ecs_entity_t ecs_new_from_path_w_sep( const char *prefix); /** Add specified path to entity. - * This operation is similar to ecs_new_from_path, but will instead add the path + * This operation is similar to ecs_new_from_path(), but will instead add the path * to an existing entity. * * If an entity already exists for the path, it will be returned instead. @@ -3538,7 +4023,7 @@ ecs_entity_t ecs_new_from_path_w_sep( * @param sep The separator used in the path. * @param prefix The prefix used in the path. * @return The entity. - */ + */ FLECS_API ecs_entity_t ecs_add_path_w_sep( ecs_world_t *world, @@ -3558,6 +4043,8 @@ ecs_entity_t ecs_add_path_w_sep( * @param world The world. * @param scope The entity to use as scope. * @return The previous scope. + * + * @see ecs_get_scope() */ FLECS_API ecs_entity_t ecs_set_scope( @@ -3565,7 +4052,7 @@ ecs_entity_t ecs_set_scope( ecs_entity_t scope); /** Get the current scope. - * Get the scope set by ecs_set_scope. If no scope is set, this operation will + * Get the scope set by ecs_set_scope(). If no scope is set, this operation will * return 0. * * @param world The world. @@ -3577,7 +4064,7 @@ ecs_entity_t ecs_get_scope( /** Set a name prefix for newly created entities. * This is a utility that lets C modules use prefixed names for C types and - * C functions, while using names for the entity names that do not have the + * C functions, while using names for the entity names that do not have the * prefix. The name prefix is currently only used by ECS_COMPONENT. * * @param world The world. @@ -3587,31 +4074,33 @@ ecs_entity_t ecs_get_scope( FLECS_API const char* ecs_set_name_prefix( ecs_world_t *world, - const char *prefix); + const char *prefix); /** Set search path for lookup operations. * This operation accepts an array of entity ids that will be used as search * scopes by lookup operations. The operation returns the current search path. * It is good practice to restore the old search path. - * + * * The search path will be evaluated starting from the last element. - * + * * The default search path includes flecs.core. When a custom search path is * provided it overwrites the existing search path. Operations that rely on * looking up names from flecs.core without providing the namespace may fail if * the custom search path does not include flecs.core (EcsFlecsCore). - * + * * The search path array is not copied into managed memory. The application must * ensure that the provided array is valid for as long as it is used as the * search path. - * + * * The provided array must be terminated with a 0 element. This enables an * application to push/pop elements to an existing array without invoking the - * ecs_set_lookup_path operation again. - * + * ecs_set_lookup_path() operation again. + * * @param world The world. * @param lookup_path 0-terminated array with entity ids for the lookup path. * @return Current lookup path array. + * + * @see ecs_get_lookup_path() */ FLECS_API ecs_entity_t* ecs_set_lookup_path( @@ -3619,8 +4108,8 @@ ecs_entity_t* ecs_set_lookup_path( const ecs_entity_t *lookup_path); /** Get current lookup path. - * Returns value set by ecs_set_lookup_path. - * + * Returns value set by ecs_set_lookup_path(). + * * @param world The world. * @return The current lookup path. */ @@ -3634,18 +4123,19 @@ ecs_entity_t* ecs_get_lookup_path( /** * @defgroup components Components - * @brief Functions for registering and working with components. + * Functions for registering and working with components. + * * @{ */ -/** Find or create a component. +/** Find or create a component. * This operation creates a new component, or finds an existing one. The find or - * create behavior is the same as ecs_entity_init. + * create behavior is the same as ecs_entity_init(). * * When an existing component is found, the size and alignment are verified with * the provided values. If the values do not match, the operation will fail. * - * See the documentation of ecs_component_desc_t for more details. + * See the documentation of ecs_component_desc_t for more details. * * @param world The world. * @param desc Component init parameters. @@ -3654,13 +4144,13 @@ ecs_entity_t* ecs_get_lookup_path( FLECS_API ecs_entity_t ecs_component_init( ecs_world_t *world, - const ecs_component_desc_t *desc); + const ecs_component_desc_t *desc); /** Get the type for an id. - * This function returnsthe type information for an id. The specified id can be + * This function returns the type information for an id. The specified id can be * any valid id. For the rules on how type information is determined based on - * id, see ecs_get_typeid. - * + * id, see ecs_get_typeid(). + * * @param world The world. * @param id The id. * @return The type information of the id. @@ -3674,8 +4164,8 @@ const ecs_type_info_t* ecs_get_type_info( * Hooks allow for the execution of user code when components are constructed, * copied, moved, destructed, added, removed or set. Hooks can be assigned as * as long as a component has not yet been used (added to an entity). - * - * The hooks that are currently set can be accessed with ecs_get_type_info. + * + * The hooks that are currently set can be accessed with ecs_get_type_info(). * * @param world The world. * @param id The component id for which to register the actions @@ -3688,34 +4178,35 @@ void ecs_set_hooks_id( const ecs_type_hooks_t *hooks); /** Get hooks for component. - * + * * @param world The world. * @param id The component id for which to retrieve the hooks. * @return The hooks for the component, or NULL if not registered. */ FLECS_API const ecs_type_hooks_t* ecs_get_hooks_id( - ecs_world_t *world, + const ecs_world_t *world, ecs_entity_t id); /** @} */ /** * @defgroup ids Ids - * @brief Functions for working with `ecs_id_t`. + * Functions for working with `ecs_id_t`. + * * @{ */ /** Returns whether specified id a tag. - * This operation returns whether the specified type is a tag (a component + * This operation returns whether the specified type is a tag (a component * without data/size). - * + * * An id is a tag when: * - it is an entity without the EcsComponent component * - it has an EcsComponent with size member set to 0 * - it is a pair where both elements are a tag - * - it is a pair where the first element has the EcsTag tag - * + * - it is a pair where the first element has the #EcsPairIsTag tag + * * @param world The world. * @param id The id. * @return Whether the provided id is a tag. @@ -3725,27 +4216,10 @@ bool ecs_id_is_tag( const ecs_world_t *world, ecs_id_t id); -/** Return whether represents a union. - * This operation returns whether the specified type represents a union. Only - * pair ids can be unions. - * - * An id represents a union when: - * - The first element of the pair is EcsUnion/flecs::Union - * - The first element of the pair has EcsUnion/flecs::Union - * - * @param world The world. - * @param id The id. - * @return Whether the provided id represents a union. - */ -FLECS_API -bool ecs_id_is_union( - const ecs_world_t *world, - ecs_id_t id); - /** Returns whether specified id is in use. * This operation returns whether an id is in use in the world. An id is in use * if it has been added to one or more tables. - * + * * @param world The world. * @param id The id. * @return Whether the id is in use. @@ -3759,12 +4233,12 @@ bool ecs_id_in_use( * This operation returns the component id for an id, if the id is associated * with a type. For a regular component with a non-zero size (an entity with the * EcsComponent component) the operation will return the entity itself. - * + * * For an entity that does not have the EcsComponent component, or with an * EcsComponent value with size 0, the operation will return 0. - * + * * For a pair id the operation will return the type associated with the pair, by - * applying the following rules in order: + * applying the following queries in order: * - The first pair element is returned if it is a component * - 0 is returned if the relationship entity has the Tag property * - The second pair element is returned if it is a component @@ -3785,6 +4259,7 @@ ecs_entity_t ecs_get_typeid( * * @param id The id. * @param pattern The pattern to compare with. + * @return Whether the id matches the pattern. */ FLECS_API bool ecs_id_match( @@ -3828,9 +4303,9 @@ bool ecs_id_is_valid( ecs_id_t id); /** Get flags associated with id. - * This operation returns the internal flags (see api_flags.h) that are + * This operation returns the internal flags (see api_flags.h) that are * associated with the provided id. - * + * * @param world The world. * @param id The id. * @return Flags associated with the id, or 0 if the id is not in use. @@ -3841,8 +4316,8 @@ ecs_flags32_t ecs_id_get_flags( ecs_id_t id); /** Convert id flag to string. - * This operation converts a id flag to a string. - * + * This operation converts an id flag to a string. + * * @param id_flags The id flag. * @return The id flag string, or NULL if no valid id is provided. */ @@ -3850,7 +4325,7 @@ FLECS_API const char* ecs_id_flag_str( ecs_id_t id_flags); -/** Convert id to string. +/** Convert (component) id to string. * This operation interprets the structure of an id and converts it to a string. * * @param world The world. @@ -3862,8 +4337,8 @@ char* ecs_id_str( const ecs_world_t *world, ecs_id_t id); -/** Write id string to buffer. - * Same as ecs_id_str but writes result to ecs_strbuf_t. +/** Write (component) id string to buffer. + * Same as ecs_id_str() but writes result to ecs_strbuf_t. * * @param world The world. * @param id The id to convert to a string. @@ -3875,85 +4350,34 @@ void ecs_id_str_buf( ecs_id_t id, ecs_strbuf_t *buf); +/** Convert string to a (component) id. + * This operation is the reverse of ecs_id_str(). The FLECS_SCRIPT addon + * is required for this operation to work. + * + * @param world The world. + * @param expr The string to convert to an id. + */ +FLECS_API +ecs_id_t ecs_id_from_str( + const ecs_world_t *world, + const char *expr); + /** @} */ /** - * @defgroup filters Filters - * @brief Functions for working with `ecs_term_t` and `ecs_filter_t`. + * @defgroup queries Queries + * @brief Functions for working with `ecs_term_t` and `ecs_query_t`. * @{ */ -/** Iterator for a single (component) id. - * A term iterator returns all entities (tables) that match a single (component) - * id. The search for the matching set of entities (tables) is performed in - * constant time. - * - * @param world The world. - * @param term The term. - * @return The iterator. - */ -FLECS_API -ecs_iter_t ecs_term_iter( - const ecs_world_t *world, - ecs_term_t *term); - -/** Return a chained term iterator. - * A chained iterator applies a filter to the results of the input iterator. The - * resulting iterator must be iterated with ecs_term_next. - * - * @param it The input iterator - * @param term The term filter to apply to the iterator. - * @return The chained iterator. - */ -FLECS_API -ecs_iter_t ecs_term_chain_iter( - const ecs_iter_t *it, - ecs_term_t *term); - -/** Progress a term iterator. - * This operation progresses the term iterator to the next table. The - * iterator must have been initialized with `ecs_term_iter`. This operation - * must be invoked at least once before interpreting the contents of the - * iterator. - * - * @param it The iterator. - * @returns True if more data is available, false if not. - */ -FLECS_API -bool ecs_term_next( - ecs_iter_t *it); - -/** Iterator for a parent's children. - * This operation is equivalent to a term iterator for (ChildOf, parent). - * Iterate the result with ecs_children_next. - * - * @param world The world. - * @param parent The parent for which to iterate the children. - * @return The iterator. - */ -FLECS_API -ecs_iter_t ecs_children( - const ecs_world_t *world, - ecs_entity_t parent); - -/** Progress a children iterator. - * Equivalent to ecs_term_next. - * - * @param it The iterator. - * @returns True if more data is available, false if not. - */ -FLECS_API -bool ecs_children_next( - ecs_iter_t *it); - -/** Test whether term id is set. +/** Test whether term id is set. * * @param id The term id. * @return True when set, false when not set. */ FLECS_API -bool ecs_term_id_is_set( - const ecs_term_id_t *id); +bool ecs_term_ref_is_set( + const ecs_term_ref_t *id); /** Test whether a term is set. * This operation can be used to test whether a term has been initialized with @@ -3973,14 +4397,14 @@ bool ecs_term_is_initialized( /** Is term matched on $this variable. * This operation checks whether a term is matched on the $this variable, which * is the default source for queries. - * + * * A term has a $this source when: * - ecs_term_t::src::id is EcsThis * - ecs_term_t::src::flags is EcsIsVariable - * + * * If ecs_term_t::src is not populated, it will be automatically initialized to * the $this source for the created query. - * + * * @param term The term. * @return True if term matches $this, false if not. */ @@ -3990,13 +4414,13 @@ bool ecs_term_match_this( /** Is term matched on 0 source. * This operation checks whether a term is matched on a 0 source. A 0 source is - * a term that isn't matched against anything, and can be used just to pass + * a term that isn't matched against anything, and can be used just to pass * (component) ids to a query iterator. - * + * * A term has a 0 source when: * - ecs_term_t::src::id is 0 * - ecs_term_t::src::flags has EcsIsEntity set - * + * * @param term The term. * @return True if term has 0 source, false if not. */ @@ -4004,139 +4428,10 @@ FLECS_API bool ecs_term_match_0( const ecs_term_t *term); -/** Finalize term. - * Ensure that all fields of a term are consistent and filled out. This - * operation should be invoked before using and after assigning members to, or - * parsing a term. When a term contains unresolved identifiers, this operation - * will resolve and assign the identifiers. If the term contains any identifiers - * that cannot be resolved, the operation will fail. - * - * An application generally does not need to invoke this operation as the APIs - * that use terms (such as filters, queries and triggers) will finalize terms - * when they are created. - * - * The name and expr parameters are optional, and only used for giving more - * descriptive error messages. - * - * @param world The world. - * @param term The term to finalize. - * @return Zero if success, nonzero if an error occurred. - */ -FLECS_API -int ecs_term_finalize( - const ecs_world_t *world, - ecs_term_t *term); - -/** Copy resources of a term to another term. - * This operation copies one term to another term. If the source term contains - * allocated resources (such as identifiers), they will be duplicated so that - * no memory is shared between the terms. - * - * @param src The term to copy from. - * @return The destination term. - */ -FLECS_API -ecs_term_t ecs_term_copy( - const ecs_term_t *src); - -/** Move resources of a term to another term. - * Same as copy, but moves resources from src, if src->move is set to true. If - * src->move is not set to true, this operation will do a copy. - * - * The conditional move reduces redundant allocations in scenarios where a list - * of terms is partially created with allocated resources. - * - * @param src The term to move from. - * @return The destination term. - */ -FLECS_API -ecs_term_t ecs_term_move( - ecs_term_t *src); - -/** Free resources of term. - * This operation frees all resources (such as identifiers) of a term. The term - * itself is not freed. - * - * @param term The term to free. - */ -FLECS_API -void ecs_term_fini( - ecs_term_t *term); - -/** Initialize filter - * A filter is a lightweight object that can be used to query for entities in - * a world. Filters, as opposed to queries, do not cache results. They are - * therefore slower to iterate, but are faster to create. - * - * When a filter is copied by value, make sure to use "ecs_filter_move" to - * ensure that the terms pointer still points to the inline array: - * - * ecs_filter_move(&dst_filter, &src_filter) - * - * Alternatively, the ecs_filter_move function can be called with both arguments - * set to the same filter, to ensure the pointer is valid: - * - * ecs_filter_move(&f, &f) - * - * It is possible to create a filter without allocating any memory, by setting - * the .storage member in ecs_filter_desc_t. See the documentation for the - * member for more details. - * - * @param world The world. - * @param desc Properties for the filter to create. - * @return The filter if successful, NULL if not successful. - */ -FLECS_API -ecs_filter_t * ecs_filter_init( - ecs_world_t *world, - const ecs_filter_desc_t *desc); - -/** Deinitialize filter. - * Free resources associated with filter. - * - * @param filter The filter to deinitialize. - */ -FLECS_API -void ecs_filter_fini( - ecs_filter_t *filter); - -/** Finalize filter. - * When manually assigning an array of terms to the filter struct (so not when - * using ecs_filter_init), this operation should be used to ensure that all - * terms are assigned properly and all (derived) fields have been set. - * - * When ecs_filter_init is used to create the filter, this function should not - * be called. The purpose of this operation is to support creation of filters - * without allocating memory. - * - * @param filter The filter to finalize. - * @return Zero if filter is valid, non-zero if it contains errors. - * @ - */ -FLECS_API -int ecs_filter_finalize( - const ecs_world_t *world, - ecs_filter_t *filter); - -/** Find index for $this variable. - * This operation looks up the index of the $this variable. This index can - * be used in operations like ecs_iter_set_var and ecs_iter_get_var. - * - * The operation will return -1 if the variable was not found. This happens when - * a filter only has terms that are not matched on the $this variable, like a - * filter that exclusively matches singleton components. - * - * @param filter The rule. - * @return The index of the $this variable. - */ -FLECS_API -int32_t ecs_filter_find_this_var( - const ecs_filter_t *filter); - /** Convert term to string expression. * Convert term to a string expression. The resulting expression is equivalent * to the same term, with the exception of And & Or operators. - * + * * @param world The world. * @param term The term. * @return The term converted to a string. @@ -4146,163 +4441,118 @@ char* ecs_term_str( const ecs_world_t *world, const ecs_term_t *term); -/** Convert filter to string expression. - * Convert filter terms to a string expression. The resulting expression can be - * parsed to create the same filter. +/** Convert query to string expression. + * Convert query to a string expression. The resulting expression can be + * parsed to create the same query. * - * @param world The world. - * @param filter The filter. - * @return The filter converted to a string. + * @param query The query. + * @return The query converted to a string. */ FLECS_API -char* ecs_filter_str( - const ecs_world_t *world, - const ecs_filter_t *filter); +char* ecs_query_str( + const ecs_query_t *query); -/** Return a filter iterator. - * A filter iterator lets an application iterate over entities that match the - * specified filter. - * - * @param world The world. - * @param filter The filter. - * @return An iterator that can be used with ecs_filter_next. - */ -FLECS_API -ecs_iter_t ecs_filter_iter( - const ecs_world_t *world, - const ecs_filter_t *filter); +/** @} */ -/** Return a chained filter iterator. - * A chained iterator applies a filter to the results of the input iterator. The - * resulting iterator must be iterated with ecs_filter_next. - * - * @param it The input iterator - * @param filter The filter to apply to the iterator. - * @return The chained iterator. +/** + * @defgroup each_iter Each iterator + * @brief Find all entities that have a single (component) id. + * @{ */ -FLECS_API -ecs_iter_t ecs_filter_chain_iter( - const ecs_iter_t *it, - const ecs_filter_t *filter); -/** Get pivot term for filter. - * The pivot term is the term that matches the smallest set of tables, and is - * a good default starting point for a search. +/** Iterate all entities with specified (component id). + * This returns an iterator that yields all entities with a single specified + * component. This is a much lighter weight operation than creating and + * iterating a query. * - * The following conditions must be met for a term to be considered as pivot: - * - It must have a This subject - * - It must have the And operator + * Usage: + * @code + * ecs_iter_t it = ecs_each(world, Player); + * while (ecs_each_next(&it)) { + * for (int i = 0; i < it.count; i ++) { + * // Iterate as usual. + * } + * } + * @endcode * - * When a filter does not have any terms that match those conditions, it will - * return -1. + * If the specified id is a component, it is possible to access the component + * pointer with ecs_field just like with regular queries: * - * If one or more terms in the filter have no matching tables the filter won't - * yield any results. In this case the operation will return -2 which gives a - * search function the option to early out. + * @code + * ecs_iter_t it = ecs_each(world, Position); + * while (ecs_each_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * for (int i = 0; i < it.count; i ++) { + * // Iterate as usual. + * } + * } + * @endcode * * @param world The world. - * @param filter The filter. - * @return Index of the pivot term (use with filter->terms) - */ + * @param id The (component) id to iterate. + * @return An iterator that iterates all entities with the (component) id. +*/ FLECS_API -int32_t ecs_filter_pivot_term( +ecs_iter_t ecs_each_id( const ecs_world_t *world, - const ecs_filter_t *filter); - -/** Iterate tables matched by filter. - * This operation progresses the filter iterator to the next table. The - * iterator must have been initialized with `ecs_filter_iter`. This operation - * must be invoked at least once before interpreting the contents of the - * iterator. - * - * @param it The iterator - * @return True if more data is available, false if not. - */ -FLECS_API -bool ecs_filter_next( - ecs_iter_t *it); + ecs_id_t id); -/** Same as ecs_filter_next, but always instanced. - * See instanced property of ecs_filter_desc_t. +/** Progress an iterator created with ecs_each_id(). * - * @param it The iterator - * @return True if more data is available, false if not. + * @param it The iterator. + * @return True if the iterator has more results, false if not. */ FLECS_API -bool ecs_filter_next_instanced( +bool ecs_each_next( ecs_iter_t *it); -/** Move resources of one filter to another. +/** Iterate children of parent. + * Equivalent to: + * @code + * ecs_iter_t it = ecs_each_id(world, ecs_pair(EcsChildOf, parent)); + * @endcode * - * @param dst The destination filter. - * @param src The source filter. - */ + * @param world The world. + * @param parent The parent. + * @return An iterator that iterates all children of the parent. + * + * @see ecs_each_id() +*/ FLECS_API -void ecs_filter_move( - ecs_filter_t *dst, - ecs_filter_t *src); +ecs_iter_t ecs_children( + const ecs_world_t *world, + ecs_entity_t parent); -/** Copy resources of one filter to another. +/** Progress an iterator created with ecs_children(). * - * @param dst The destination filter. - * @param src The source filter. + * @param it The iterator. + * @return True if the iterator has more results, false if not. */ FLECS_API -void ecs_filter_copy( - ecs_filter_t *dst, - const ecs_filter_t *src); +bool ecs_children_next( + ecs_iter_t *it); /** @} */ /** * @defgroup queries Queries - * @brief Functions for working with `ecs_query_t`. + * Functions for working with `ecs_query_t`. + * * @{ */ /** Create a query. - * This operation creates a query. Queries are used to iterate over entities - * that match a filter and are the fastest way to find and iterate over entities - * and their components. * - * Queries should be created once, and reused multiple times. While iterating a - * query is a cheap operation, creating and deleting a query is expensive. The - * reason for this is that queries are "prematched", which means that a query - * stores state about which entities (or rather, tables) match with the query. - * Building up this state happens during query creation. - * - * Once a query is created, matching only happens when new tables are created. - * In most applications this is an infrequent process, since it only occurs when - * a new combination of components is introduced. While matching is expensive, - * it is importent to note that matching does not happen on a per-entity basis, - * but on a per-table basis. This means that the average time spent on matching - * per frame should rapidly approach zero over the lifetime of an application. - * - * A query provides direct access to the component arrays. When an application - * creates/deletes entities or adds/removes components, these arrays can shift - * component values around, or may grow in size. This can cause unexpected or - * undefined behavior to occur if these operations are performed while - * iterating. To prevent this from happening an application should either not - * perform these operations while iterating, or use deferred operations (see - * ecs_defer_begin and ecs_defer_end). - * - * Queries can be created and deleted dynamically. If a query was not deleted - * (using ecs_query_fini) before the world is deleted, it will be deleted - * automatically. - * * @param world The world. - * @param desc A structure describing the query properties. - * @return The new query. + * @param desc The descriptor (see ecs_query_desc_t) + * @return The query. */ FLECS_API ecs_query_t* ecs_query_init( - ecs_world_t *world, + ecs_world_t *world, const ecs_query_desc_t *desc); -/** Destroy a query. - * This operation destroys a query and its resources. If the query is used as - * the parent of subqueries, those subqueries will be orphaned and must be - * deinitialized as well. +/** Delete a query. * * @param query The query. */ @@ -4310,111 +4560,275 @@ FLECS_API void ecs_query_fini( ecs_query_t *query); -/** Get filter from a query. - * This operation obtains a pointer to the internally constructed filter - * of the query and can be used to introspect the query terms. +/** Find variable index. + * This operation looks up the index of a variable in the query. This index can + * be used in operations like ecs_iter_set_var() and ecs_iter_get_var(). * * @param query The query. - * @return The filter. + * @param name The variable name. + * @return The variable index. */ FLECS_API -const ecs_filter_t* ecs_query_get_filter( - const ecs_query_t *query); +int32_t ecs_query_find_var( + const ecs_query_t *query, + const char *name); -/** Return a query iterator. - * A query iterator lets an application iterate over entities that match the - * specified query. If a sorting function is specified, the query will check - * whether a resort is required upon creating the iterator. - * - * Creating a query iterator is a cheap operation that does not allocate any - * resources. An application does not need to deinitialize or free a query - * iterator before it goes out of scope. +/** Get variable name. + * This operation returns the variable name for an index. * - * To iterate the iterator, an application should use ecs_query_next to progress - * the iterator and test if it has data. + * @param query The query. + * @param var_id The variable index. + * @return The variable name. + */ +FLECS_API +const char* ecs_query_var_name( + const ecs_query_t *query, + int32_t var_id); + +/** Test if variable is an entity. + * Internally the query engine has entity variables and table variables. When + * iterating through query variables (by using ecs_query_variable_count()) only + * the values for entity variables are accessible. This operation enables an + * application to check if a variable is an entity variable. * - * Query iteration requires an outer and an inner loop. The outer loop uses - * ecs_query_next to test if new tables are available. The inner loop iterates - * the entities in the table, and is usually a for loop that uses iter.count to - * loop through the entities and component arrays. + * @param query The query. + * @param var_id The variable id. + * @return Whether the variable is an entity variable. + */ +FLECS_API +bool ecs_query_var_is_entity( + const ecs_query_t *query, + int32_t var_id); + +/** Create a query iterator. + * Use an iterator to iterate through the entities that match an entity. Queries + * can return multiple results, and have to be iterated by repeatedly calling + * ecs_query_next() until the operation returns false. + * + * Depending on the query, a single result can contain an entire table, a range + * of entities in a table, or a single entity. Iteration code has an inner and + * an outer loop. The outer loop loops through the query results, and typically + * corresponds with a table. The inner loop loops entities in the result. + * + * Example: + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * + * while (ecs_query_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * Velocity *v = ecs_field(&it, Velocity, 1); + * + * for (int i = 0; i < it.count; i ++) { + * p[i].x += v[i].x; + * p[i].y += v[i].y; + * } + * } + * @endcode + * + * The world passed into the operation must be either the actual world or the + * current stage, when iterating from a system. The stage is accessible through + * the it.world member. + * + * Example: + * @code + * void MySystem(ecs_iter_t *it) { + * ecs_query_t *q = it->ctx; // Query passed as system context + * + * // Create query iterator from system stage + * ecs_iter_t qit = ecs_query_iter(it->world, q); + * while (ecs_query_next(&qit)) { + * // Iterate as usual + * } + * } + * @endcode + * + * If query iteration is stopped without the last call to ecs_query_next() + * returning false, iterator resources need to be cleaned up explicitly + * with ecs_iter_fini(). + * + * Example: + * @code + * ecs_iter_t it = ecs_query_iter(world, q); + * + * while (ecs_query_next(&it)) { + * if (!ecs_field_is_set(&it, 0)) { + * ecs_iter_fini(&it); // Free iterator resources + * break; + * } + * + * for (int i = 0; i < it.count; i ++) { + * // ... + * } + * } + * @endcode * - * The two loops are necessary because of how data is stored internally. - * Entities are grouped by the components they have, in tables. A single query - * can (and often does) match with multiple tables. Because each table has its - * own set of arrays, an application has to reobtain pointers to those arrays - * for each matching table. + * @param world The world. + * @param query The query. + * @return An iterator. * - * @param world The world or stage, when iterating in readonly mode. - * @param query The query to iterate. - * @return The query iterator. + * @see ecs_query_next() */ FLECS_API ecs_iter_t ecs_query_iter( const ecs_world_t *world, - ecs_query_t *query); + const ecs_query_t *query); -/** Progress the query iterator. - * This operation progresses the query iterator to the next table. The - * iterator must have been initialized with `ecs_query_iter`. This operation - * must be invoked at least once before interpreting the contents of the - * iterator. +/** Progress query iterator. * - * @param iter The iterator. - * @returns True if more data is available, false if not. + * @param it The iterator. + * @return True if the iterator has more results, false if not. + * + * @see ecs_query_iter() */ FLECS_API bool ecs_query_next( - ecs_iter_t *iter); + ecs_iter_t *it); -/** Same as ecs_query_next, but always instanced. - * See "instanced" property of ecs_filter_desc_t. +/** Match entity with query. + * This operation matches an entity with a query and returns the result of the + * match in the "it" out parameter. An application should free the iterator + * resources with ecs_iter_fini() if this function returns true. * - * @param iter The iterator. - * @returns True if more data is available, false if not. + * Usage: + * @code + * ecs_iter_t it; + * if (ecs_query_has(q, e, &it)) { + * ecs_iter_fini(&it); + * } + * @endcode + * + * @param query The query. + * @param entity The entity to match + * @param it The iterator with matched data. + * @return True if entity matches the query, false if not. */ FLECS_API -bool ecs_query_next_instanced( - ecs_iter_t *iter); +bool ecs_query_has( + ecs_query_t *query, + ecs_entity_t entity, + ecs_iter_t *it); -/** Fast alternative to ecs_query_next that only returns matched tables. - * This operation only populates the ecs_iter_t::table field. To access the - * matched components, call ecs_query_populate. +/** Match table with query. + * This operation matches a table with a query and returns the result of the + * match in the "it" out parameter. An application should free the iterator + * resources with ecs_iter_fini() if this function returns true. * - * If this operation is used with a query that has inout/out terms, those terms - * will not be marked dirty unless ecs_query_populate is called. + * Usage: + * @code + * ecs_iter_t it; + * if (ecs_query_has_table(q, t, &it)) { + * ecs_iter_fini(&it); + * } + * @endcode * - * @param iter The iterator. - * @returns True if more data is available, false if not. + * @param query The query. + * @param table The table to match + * @param it The iterator with matched data. + * @return True if table matches the query, false if not. */ FLECS_API -bool ecs_query_next_table( - ecs_iter_t *iter); +bool ecs_query_has_table( + ecs_query_t *query, + ecs_table_t *table, + ecs_iter_t *it); -/** Populate iterator fields. - * This operation can be combined with ecs_query_next_table to populate the - * iterator fields for the current table. +/** Match range with query. + * This operation matches a range with a query and returns the result of the + * match in the "it" out parameter. An application should free the iterator + * resources with ecs_iter_fini() if this function returns true. * - * Populating fields conditionally can save time when a query uses change - * detection, and only needs iterator data when the table has changed. When this - * operation is called, inout/out terms will be marked dirty. + * The entire range must match the query for the operation to return true. * - * In cases where inout/out terms are conditionally written and no changes - * were made after calling ecs_query_populate, the ecs_query_skip function can - * be called to prevent the matched table components from being marked dirty. + * Usage: + * @code + * ecs_table_range_t range = { + * .table = table, + * .offset = 1, + * .count = 2 + * }; + * + * ecs_iter_t it; + * if (ecs_query_has_range(q, &range, &it)) { + * ecs_iter_fini(&it); + * } + * @endcode * - * This operation does should not be used with queries that match disabled - * components, union relationships, or with queries that use order_by. + * @param query The query. + * @param range The range to match + * @param it The iterator with matched data. + * @return True if range matches the query, false if not. + */ +FLECS_API +bool ecs_query_has_range( + ecs_query_t *query, + ecs_table_range_t *range, + ecs_iter_t *it); + +/** Returns how often a match event happened for a cached query. + * This operation can be used to determine whether the query cache has been + * updated with new tables. * - * When the when_changed argument is set to true, the iterator data will only - * populate when the data has changed, using query change detection. + * @param query The query. + * @return The number of match events happened. + */ +FLECS_API +int32_t ecs_query_match_count( + const ecs_query_t *query); + +/** Convert query to a string. + * This will convert the query program to a string which can aid in debugging + * the behavior of a query. + * + * The returned string must be freed with ecs_os_free(). + * + * @param query The query. + * @return The query plan. + */ +FLECS_API +char* ecs_query_plan( + const ecs_query_t *query); + +/** Convert query to string with profile. + * To use this you must set the EcsIterProfile flag on an iterator before + * starting iteration: + * + * @code + * it.flags |= EcsIterProfile + * @endcode * - * @param iter The iterator. - * @param when_changed Only populate data when result has changed. + * The returned string must be freed with ecs_os_free(). + * + * @param query The query. + * @param it The iterator with profile data. + * @return The query plan with profile data. */ FLECS_API -int ecs_query_populate( - ecs_iter_t *iter, - bool when_changed); +char* ecs_query_plan_w_profile( + const ecs_query_t *query, + const ecs_iter_t *it); + +/** Populate variables from key-value string. + * Convenience function to set query variables from a key-value string separated + * by comma's. The string must have the following format: + * + * @code + * var_a: value, var_b: value + * @endcode + * + * The key-value list may optionally be enclosed in parenthesis. + * + * This function uses the script addon. + * + * @param query The query. + * @param it The iterator for which to set the variables. + * @param expr The key-value expression. + * @return Pointer to the next character after the last parsed one. + */ +FLECS_API +const char* ecs_query_args_parse( + ecs_query_t *query, + ecs_iter_t *it, + const char *expr); /** Returns whether the query data changed since the last iteration. * The operation will return true after: @@ -4422,79 +4836,76 @@ int ecs_query_populate( * - new tables have been matched/unmatched with * - matched entities were deleted * - matched components were changed - * + * * The operation will not return true after a write-only (EcsOut) or filter * (EcsInOutNone) term has changed, when a term is not matched with the * current table (This subject) or for tag terms. - * - * The changed state of a table is reset after it is iterated. If a iterator was + * + * The changed state of a table is reset after it is iterated. If an iterator was * not iterated until completion, tables may still be marked as changed. - * + * * If no iterator is provided the operation will return the changed state of the - * all matched tables of the query. - * - * If an iterator is provided, the operation will return the changed state of + * all matched tables of the query. + * + * If an iterator is provided, the operation will return the changed state of * the currently returned iterator result. The following preconditions must be * met before using an iterator with change detection: - * - * - The iterator is a query iterator (created with ecs_query_iter) - * - The iterator must be valid (ecs_query_next must have returned true) - * - The iterator must be instanced - * + * + * - The iterator is a query iterator (created with ecs_query_iter()) + * - The iterator must be valid (ecs_query_next() must have returned true) + * * @param query The query (optional if 'it' is provided). - * @param it The iterator result to test (optional if 'query' is provided). * @return true if entities changed, otherwise false. */ FLECS_API bool ecs_query_changed( - ecs_query_t *query, - const ecs_iter_t *it); + ecs_query_t *query); /** Skip a table while iterating. * This operation lets the query iterator know that a table was skipped while * iterating. A skipped table will not reset its changed state, and the query * will not update the dirty flags of the table for its out columns. - * + * * Only valid iterators must be provided (next has to be called at least once & * return true) and the iterator must be a query iterator. - * + * * @param it The iterator result to skip. */ FLECS_API -void ecs_query_skip( +void ecs_iter_skip( ecs_iter_t *it); /** Set group to iterate for query iterator. * This operation limits the results returned by the query to only the selected * group id. The query must have a group_by function, and the iterator must * be a query iterator. - * + * * Groups are sets of tables that are stored together in the query cache based - * on a group id, which is calculated per table by the group_by function. To + * on a group id, which is calculated per table by the group_by function. To * iterate a group, an iterator only needs to know the first and last cache node * for that group, which can both be found in a fast O(1) operation. - * - * As a result, group iteration is one of the most efficient mechanisms to + * + * As a result, group iteration is one of the most efficient mechanisms to * filter out large numbers of entities, even if those entities are distributed * across many tables. This makes it a good fit for things like dividing up * a world into cells, and only iterating cells close to a player. - * - * The group to iterate must be set before the first call to ecs_query_next. No - * operations that can add/remove components should be invoked between calling - * ecs_query_set_group and ecs_query_next. - * + * + * The group to iterate must be set before the first call to ecs_query_next(). No + * operations that can add/remove components should be invoked between calling + * ecs_iter_set_group() and ecs_query_next(). + * * @param it The query iterator. * @param group_id The group to iterate. */ FLECS_API -void ecs_query_set_group( +void ecs_iter_set_group( ecs_iter_t *it, uint64_t group_id); /** Get context of query group. - * This operation returns the context of a query group as returned by the + * This operation returns the context of a query group as returned by the * on_group_create callback. - * + * * @param query The query. * @param group_id The group for which to obtain the context. * @return The group context, NULL if the group doesn't exist. @@ -4507,7 +4918,7 @@ void* ecs_query_get_group_ctx( /** Get information about query group. * This operation returns information about a query group, including the group * context returned by the on_group_create callback. - * + * * @param query The query. * @param group_id The group for which to obtain the group info. * @return The group info, NULL if the group doesn't exist. @@ -4517,159 +4928,118 @@ const ecs_query_group_info_t* ecs_query_get_group_info( const ecs_query_t *query, uint64_t group_id); -/** Returns whether query is orphaned. - * When the parent query of a subquery is deleted, it is left in an orphaned - * state. The only valid operation on an orphaned query is deleting it. Only - * subqueries can be orphaned. - * - * @param query The query. - * @return true if query is orphaned, otherwise false. - */ -FLECS_API -bool ecs_query_orphaned( - const ecs_query_t *query); - -/** Convert query to string. - * - * @param query The query. - * @return The query string. - */ -FLECS_API -char* ecs_query_str( - const ecs_query_t *query); +/** Struct returned by ecs_query_count(). */ +typedef struct ecs_query_count_t { + int32_t results; /**< Number of results returned by query. */ + int32_t entities; /**< Number of entities returned by query. */ + int32_t tables; /**< Number of tables returned by query. */ + int32_t empty_tables; /**< Number of empty tables returned by query. */ +} ecs_query_count_t; -/** Returns number of tables query matched with. - * - * @param query The query. - * @return The number of matched tables. - */ -FLECS_API -int32_t ecs_query_table_count( - const ecs_query_t *query); - -/** Returns number of empty tables query matched with. - * - * @param query The query. - * @return The number of matched empty tables. - */ -FLECS_API -int32_t ecs_query_empty_table_count( - const ecs_query_t *query); - -/** Returns number of entities query matched with. - * This operation iterates all non-empty tables in the query cache to find the - * total number of entities. +/** Returns number of entities and results the query matches with. + * Only entities matching the $this variable as source are counted. * * @param query The query. * @return The number of matched entities. */ FLECS_API -int32_t ecs_query_entity_count( +ecs_query_count_t ecs_query_count( const ecs_query_t *query); -/** Get query ctx. - * Return the value set in ecs_query_desc_t::ctx. +/** Does query return one or more results. * * @param query The query. - * @return The context. + * @return True if query matches anything, false if not. */ FLECS_API -void* ecs_query_get_ctx( +bool ecs_query_is_true( const ecs_query_t *query); -/** Get query binding ctx. - * Return the value set in ecs_query_desc_t::binding_ctx. - * +/** Get query used to populate cache. + * This operation returns the query that is used to populate the query cache. + * For queries that are can be entirely cached, the returned query will be + * equivalent to the query passed to ecs_query_get_cache_query(). + * * @param query The query. - * @return The context. + * @return The query used to populate the cache, NULL if query is not cached. */ FLECS_API -void* ecs_query_get_binding_ctx( +const ecs_query_t* ecs_query_get_cache_query( const ecs_query_t *query); /** @} */ /** - * @defgroup observer Observers - * @brief Functions for working with events and observers. + * @defgroup observers Observers + * Functions for working with events and observers. + * * @{ */ /** Send event. * This sends an event to matching triggers & is the mechanism used by flecs - * itself to send OnAdd, OnRemove, etc events. - * + * itself to send `OnAdd`, `OnRemove`, etc events. + * * Applications can use this function to send custom events, where a custom * event can be any regular entity. - * + * * Applications should not send builtin flecs events, as this may violate * assumptions the code makes about the conditions under which those events are * sent. - * + * * Triggers are invoked synchronously. It is therefore safe to use stack-based * data as event context, which can be set in the "param" member. - * + * * @param world The world. * @param desc Event parameters. + * + * @see ecs_enqueue() */ FLECS_API -void ecs_emit( +void ecs_emit( ecs_world_t *world, ecs_event_desc_t *desc); +/** Enqueue event. + * Same as ecs_emit(), but enqueues an event in the command queue instead. The + * event will be emitted when ecs_defer_end() is called. + * + * If this operation is called when the provided world is not in deferred mode + * it behaves just like ecs_emit(). + * + * @param world The world. + * @param desc Event parameters. +*/ FLECS_API void ecs_enqueue( ecs_world_t *world, ecs_event_desc_t *desc); /** Create observer. - * Observers are like triggers, but can subscribe for multiple terms. An + * Observers are like triggers, but can subscribe for multiple terms. An * observer only triggers when the source of the event meets all terms. * * See the documentation for ecs_observer_desc_t for more details. * * @param world The world. * @param desc The observer creation parameters. + * @return The observer, or 0 if the operation failed. */ FLECS_API ecs_entity_t ecs_observer_init( ecs_world_t *world, const ecs_observer_desc_t *desc); -/** Default run action for observer. - * This function can be called from a custom observer run action (see - * ecs_observer_desc_t::run for more details). This function ensures that the - * observer's filter is applied to the iterator's table, filters out duplicate - * events and implements EcsMonitor logic. - * - * @param it The iterator. - * @return True if the observer was invoked. - */ -FLECS_API -bool ecs_observer_default_run_action( - ecs_iter_t *it); - -/** Get observer ctx. - * Return the value set in ecs_observer_desc_t::ctx. - * - * @param world The world. - * @param observer The observer. - * @return The context. - */ -FLECS_API -void* ecs_observer_get_ctx( - const ecs_world_t *world, - ecs_entity_t observer); - -/** Get observer binding ctx. - * Return the value set in ecs_observer_desc_t::binding_ctx. - * +/** Get observer object. + * Returns the observer object. Can be used to access various information about + * the observer, like the query and context. + * * @param world The world. * @param observer The observer. - * @return The context. + * @return The observer object. */ FLECS_API -void* ecs_observer_get_binding_ctx( +const ecs_observer_t* ecs_observer_get( const ecs_world_t *world, ecs_entity_t observer); @@ -4677,48 +5047,21 @@ void* ecs_observer_get_binding_ctx( /** * @defgroup iterator Iterators - * @brief Functions for working with `ecs_iter_t`. + * Functions for working with `ecs_iter_t`. + * * @{ */ -/** Create iterator from poly object. - * The provided poly object must have the iterable mixin. If an object is - * provided that does not have the mixin, the function will assert. - * - * When a filter is provided, an array of two iterators must be passed to the - * function. This allows the mixin implementation to create a chained iterator - * when necessary, which requires two iterator objects. - * - * If a filter is provided, the first element in the array of two iterators is - * the one that should be iterated. The mixin implementation may or may not set - * the second element, depending on whether an iterator chain is required. - * - * Additionally, when a filter is provided the returned iterator will be for a - * single term with the provided filter id. If the iterator is chained, the - * previous iterator in the chain can be accessed through it->chain_it. - * - * @param world The world or stage for which to create the iterator. - * @param poly The poly object from which to create the iterator. - * @param iter The iterator (out, ecs_iter_t[2] when filter is set). - * @param filter Optional term used for filtering the results. - */ -FLECS_API -void ecs_iter_poly( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter); - /** Progress any iterator. * This operation is useful in combination with iterators for which it is not * known what created them. Example use cases are functions that should accept * any kind of iterator (such as serializers) or iterators created from poly * objects. - * + * * This operation is slightly slower than using a type-specific iterator (e.g. - * ecs_filter_next, ecs_query_next) as it has to call a function pointer which + * ecs_query_next, ecs_query_next) as it has to call a function pointer which * introduces a level of indirection. - * + * * @param it The iterator. * @return True if iterator has more results, false if not. */ @@ -4728,11 +5071,11 @@ bool ecs_iter_next( /** Cleanup iterator resources. * This operation cleans up any resources associated with the iterator. - * + * * This operation should only be used when an iterator is not iterated until * completion (next has not yet returned false). When an iterator is iterated * until completion, resources are automatically freed. - * + * * @param it The iterator. */ FLECS_API @@ -4743,10 +5086,10 @@ void ecs_iter_fini( * This operation returns the number of matched entities. If a query contains no * matched entities but still yields results (e.g. it has no terms with This * sources) the operation will return 0. - * + * * To determine the number of matched entities, the operation iterates the * iterator until it yields no more results. - * + * * @param it The iterator. * @return True if iterator has more results, false if not. */ @@ -4756,13 +5099,13 @@ int32_t ecs_iter_count( /** Test if iterator is true. * This operation will return true if the iterator returns at least one result. - * This is especially useful in combination with fact-checking rules (see the - * rules addon). - * + * This is especially useful in combination with fact-checking queries (see the + * queries addon). + * * The operation requires a valid iterator. After the operation is invoked, the * application should no longer invoke next on the iterator and should treat it * as if the iterator is iterated until completion. - * + * * @param it The iterator. * @return true if the iterator returns at least one result. */ @@ -4773,7 +5116,7 @@ bool ecs_iter_is_true( /** Get first matching entity from iterator. * After this operation the application should treat the iterator as if it has * been iterated until completion. - * + * * @param it The iterator. * @return The first matching entity, or 0 if no entities were matched. */ @@ -4783,36 +5126,41 @@ ecs_entity_t ecs_iter_first( /** Set value for iterator variable. * This constrains the iterator to return only results for which the variable - * equals the specified value. The default value for all variables is + * equals the specified value. The default value for all variables is * EcsWildcard, which means the variable can assume any value. - * + * * Example: - * - * // Rule that matches (Eats, *) - * ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + * + * @code + * // Query that matches (Eats, *) + * ecs_query_t *q = ecs_query(world, { * .terms = { * { .first.id = Eats, .second.name = "$food" } * } * }); * - * int food_var = ecs_rule_find_var(r, "food"); + * int food_var = ecs_query_find_var(r, "food"); * * // Set Food to Apples, so we're only matching (Eats, Apples) - * ecs_iter_t it = ecs_rule_iter(world, r); + * ecs_iter_t it = ecs_query_iter(world, q); * ecs_iter_set_var(&it, food_var, Apples); * - * while (ecs_rule_next(&it)) { + * while (ecs_query_next(&it)) { * for (int i = 0; i < it.count; i ++) { * // iterate as usual * } * } - * + * @endcode + * * The variable must be initialized after creating the iterator and before the * first call to next. - * + * * @param it The iterator. * @param var_id The variable index. * @param entity The entity variable value. + * + * @see ecs_iter_set_var_as_range() + * @see ecs_iter_set_var_as_table() */ FLECS_API void ecs_iter_set_var( @@ -4820,12 +5168,15 @@ void ecs_iter_set_var( int32_t var_id, ecs_entity_t entity); -/** Same as ecs_iter_set_var, but for a table. +/** Same as ecs_iter_set_var(), but for a table. * This constrains the variable to all entities in a table. - * + * * @param it The iterator. * @param var_id The variable index. * @param table The table variable value. + * + * @see ecs_iter_set_var() + * @see ecs_iter_set_var_as_range() */ FLECS_API void ecs_iter_set_var_as_table( @@ -4833,12 +5184,15 @@ void ecs_iter_set_var_as_table( int32_t var_id, const ecs_table_t *table); -/** Same as ecs_iter_set_var, but for a range of entities +/** Same as ecs_iter_set_var(), but for a range of entities * This constrains the variable to a range of entities in a table. - * + * * @param it The iterator. * @param var_id The variable index. * @param range The range variable value. + * + * @see ecs_iter_set_var() + * @see ecs_iter_set_var_as_table() */ FLECS_API void ecs_iter_set_var_as_range( @@ -4849,11 +5203,11 @@ void ecs_iter_set_var_as_range( /** Get value of iterator variable as entity. * A variable can be interpreted as entity if it is set to an entity, or if it * is set to a table range with count 1. - * + * * This operation can only be invoked on valid iterators. The variable index * must be smaller than the total number of variables provided by the iterator * (as set in ecs_iter_t::variable_count). - * + * * @param it The iterator. * @param var_id The variable index. * @return The variable value. @@ -4867,11 +5221,11 @@ ecs_entity_t ecs_iter_get_var( * A variable can be interpreted as table if it is set as table range with * both offset and count set to 0, or if offset is 0 and count matches the * number of elements in the table. - * + * * This operation can only be invoked on valid iterators. The variable index * must be smaller than the total number of variables provided by the iterator * (as set in ecs_iter_t::variable_count). - * + * * @param it The iterator. * @param var_id The variable index. * @return The variable value. @@ -4885,11 +5239,11 @@ ecs_table_t* ecs_iter_get_var_as_table( * A value can be interpreted as table range if it is set as table range, or if * it is set to an entity with a non-empty type (the entity must have at least * one component, tag or relationship in its type). - * + * * This operation can only be invoked on valid iterators. The variable index * must be smaller than the total number of variables provided by the iterator * (as set in ecs_iter_t::variable_count). - * + * * @param it The iterator. * @param var_id The variable index. * @return The variable value. @@ -4902,10 +5256,10 @@ ecs_table_range_t ecs_iter_get_var_as_range( /** Returns whether variable is constrained. * This operation returns true for variables set by one of the ecs_iter_set_var* * operations. - * + * * A constrained variable is guaranteed not to change values while results are * being iterated. - * + * * @param it The iterator. * @param var_id The variable index. * @return Whether the variable is constrained to a specified value. @@ -4915,14 +5269,29 @@ bool ecs_iter_var_is_constrained( ecs_iter_t *it, int32_t var_id); +/** Returns whether current iterator result has changed. + * This operation must be used in combination with a query that supports change + * detection (e.g. is cached). The operation returns whether the currently + * iterated result has changed since the last time it was iterated by the query. + * + * Change detection works on a per-table basis. Changes to individual entities + * cannot be detected this way. + * + * @param it The iterator. + * @return True if the result changed, false if it didn't. +*/ +FLECS_API +bool ecs_iter_changed( + ecs_iter_t *it); + /** Convert iterator to string. * Prints the contents of an iterator to a string. Useful for debugging and/or * testing the output of an iterator. - * - * The function only converts the currently iterated data to a string. To + * + * The function only converts the currently iterated data to a string. To * convert all data, the application has to manually call the next function and - * call ecs_iter_str on each result. - * + * call ecs_iter_str() on each result. + * * @param it The iterator. * @return A string representing the contents of the iterator. */ @@ -4933,13 +5302,13 @@ char* ecs_iter_str( /** Create a paged iterator. * Paged iterators limit the results to those starting from 'offset', and will * return at most 'limit' results. - * - * The iterator must be iterated with ecs_page_next. - * + * + * The iterator must be iterated with ecs_page_next(). + * * A paged iterator acts as a passthrough for data exposed by the parent * iterator, so that any data provided by the parent will also be provided by * the paged iterator. - * + * * @param it The source iterator. * @param offset The number of entities to skip. * @param limit The maximum number of entities to iterate. @@ -4952,8 +5321,8 @@ ecs_iter_t ecs_page_iter( int32_t limit); /** Progress a paged iterator. - * Progresses an iterator created by ecs_page_iter. - * + * Progresses an iterator created by ecs_page_iter(). + * * @param it The iterator. * @return true if iterator has more results, false if not. */ @@ -4962,20 +5331,20 @@ bool ecs_page_next( ecs_iter_t *it); /** Create a worker iterator. - * Worker iterators can be used to equally divide the number of matched entities + * Worker iterators can be used to equally divide the number of matched entities * across N resources (usually threads). Each resource will process the total * number of matched entities divided by 'count'. - * + * * Entities are distributed across resources such that the distribution is * stable between queries. Two queries that match the same table are guaranteed * to match the same entities in that table. - * - * The iterator must be iterated with ecs_worker_next. - * + * + * The iterator must be iterated with ecs_worker_next(). + * * A worker iterator acts as a passthrough for data exposed by the parent * iterator, so that any data provided by the parent will also be provided by * the worker iterator. - * + * * @param it The source iterator. * @param index The index of the current resource. * @param count The total number of resources to divide entities between. @@ -4988,8 +5357,8 @@ ecs_iter_t ecs_worker_iter( int32_t count); /** Progress a worker iterator. - * Progresses an iterator created by ecs_worker_iter. - * + * Progresses an iterator created by ecs_worker_iter(). + * * @param it The iterator. * @return true if iterator has more results, false if not. */ @@ -4997,34 +5366,80 @@ FLECS_API bool ecs_worker_next( ecs_iter_t *it); -/** Obtain data for a query field. +/** Get data for field. * This operation retrieves a pointer to an array of data that belongs to the * term in the query. The index refers to the location of the term in the query, - * and starts counting from one. + * and starts counting from zero. * - * For example, the query "Position, Velocity" will return the Position array - * for index 1, and the Velocity array for index 2. + * For example, the query `"Position, Velocity"` will return the `Position` array + * for index 0, and the `Velocity` array for index 1. * * When the specified field is not owned by the entity this function returns a * pointer instead of an array. This happens when the source of a field is not * the entity being iterated, such as a shared component (from a prefab), a - * component from a parent, or another entity. The ecs_field_is_self operation + * component from a parent, or another entity. The ecs_field_is_self() operation * can be used to test dynamically if a field is owned. + * + * When a field contains a sparse component, use the ecs_field_at function. When + * a field is guaranteed to be set and owned, the ecs_field_self() function can be + * used. ecs_field_self() has slightly better performance, and provides stricter + * validity checking. * - * The provided size must be either 0 or must match the size of the datatype + * The provided size must be either 0 or must match the size of the type * of the returned array. If the size does not match, the operation may assert. - * The size can be dynamically obtained with ecs_field_size. + * The size can be dynamically obtained with ecs_field_size(). + * + * An example: + * + * @code + * while (ecs_query_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * Velocity *v = ecs_field(&it, Velocity, 1); + * for (int32_t i = 0; i < it->count; i ++) { + * p[i].x += v[i].x; + * p[i].y += v[i].y; + * } + * } + * @endcode * * @param it The iterator. - * @param size The type size of the requested data. - * @param index The index of the field in the iterator. + * @param size The size of the field type. + * @param index The index of the field. * @return A pointer to the data of the field. */ FLECS_API void* ecs_field_w_size( const ecs_iter_t *it, size_t size, - int32_t index); + int8_t index); + +/** Get data for field at specified row. + * This operation should be used instead of ecs_field_w_size for sparse + * component fields. This operation should be called for each returned row in a + * result. In the following example the Velocity component is sparse: + * + * @code + * while (ecs_query_next(&it)) { + * Position *p = ecs_field(&it, Position, 0); + * for (int32_t i = 0; i < it->count; i ++) { + * Velocity *v = ecs_field_at(&it, Velocity, 1); + * p[i].x += v->x; + * p[i].y += v->y; + * } + * } + * @endcode + * + * @param it the iterator. + * @param size The size of the field type. + * @param index The index of the field. + * @return A pointer to the data of the field. + */ +FLECS_API +void* ecs_field_at_w_size( + const ecs_iter_t *it, + size_t size, + int8_t index, + int32_t row); /** Test whether the field is readonly. * This operation returns whether the field is readonly. Readonly fields are @@ -5037,12 +5452,12 @@ void* ecs_field_w_size( FLECS_API bool ecs_field_is_readonly( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Test whether the field is writeonly. * This operation returns whether this is a writeonly field. Writeonly terms are * annotated with [out]. - * + * * Serializers are not required to serialize the values of a writeonly field. * * @param it The iterator. @@ -5052,10 +5467,10 @@ bool ecs_field_is_readonly( FLECS_API bool ecs_field_is_writeonly( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Test whether field is set. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return Whether the field is set. @@ -5063,10 +5478,10 @@ bool ecs_field_is_writeonly( FLECS_API bool ecs_field_is_set( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Return id matched for field. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return The id matched for the field. @@ -5074,24 +5489,24 @@ bool ecs_field_is_set( FLECS_API ecs_id_t ecs_field_id( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Return index of matched table column. * This function only returns column indices for fields that have been matched * on the $this variable. Fields matched on other tables will return -1. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return The index of the matched column, -1 if not matched. */ FLECS_API -int32_t ecs_field_column_index( +int32_t ecs_field_column( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Return field source. * The field source is the entity on which the field was matched. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return The source for the field. @@ -5099,11 +5514,11 @@ int32_t ecs_field_column_index( FLECS_API ecs_entity_t ecs_field_src( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Return field type size. * Return type size of the field. Returns 0 if the field has no data. - * + * * @param it The iterator. * @param index The index of the field in the iterator. * @return The type size for the field. @@ -5111,14 +5526,14 @@ ecs_entity_t ecs_field_src( FLECS_API size_t ecs_field_size( const ecs_iter_t *it, - int32_t index); + int8_t index); /** Test whether the field is matched on self. * This operation returns whether the field is matched on the currently iterated * entity. This function will return false when the field is owned by another * entity, such as a parent or a prefab. - * - * When this operation returns false, the field must be accessed as a single + * + * When this operation returns false, the field must be accessed as a single * value instead of an array. Fields for which this operation returns true * return arrays with it->count values. * @@ -5129,13 +5544,14 @@ size_t ecs_field_size( FLECS_API bool ecs_field_is_self( const ecs_iter_t *it, - int32_t index); + int8_t index); /** @} */ /** * @defgroup tables Tables - * @brief Functions for working with `ecs_table_t`. + * Functions for working with `ecs_table_t`. + * * @{ */ @@ -5151,11 +5567,13 @@ const ecs_type_t* ecs_table_get_type( /** Get type index for id. * This operation returns the index for an id in the table's type. - * + * * @param world The world. * @param table The table. * @param id The id. * @return The index of the id in the table type, or -1 if not found. + * + * @see ecs_table_has_id() */ FLECS_API int32_t ecs_table_get_type_index( @@ -5166,7 +5584,7 @@ int32_t ecs_table_get_type_index( /** Get column index for id. * This operation returns the column index for an id in the table's type. If the * id is not a component, the function will return -1. - * + * * @param world The world. * @param table The table. * @param id The component id. @@ -5178,10 +5596,10 @@ int32_t ecs_table_get_column_index( const ecs_table_t *table, ecs_id_t id); -/** Return number of columns in table. - * Similar to ecs_table_get_type(table)->count, except that the column count +/** Return number of columns in table. + * Similar to `ecs_table_get_type(table)->count`, except that the column count * only counts the number of components in a table. - * + * * @param table The table. * @return The number of columns in the table. */ @@ -5189,16 +5607,18 @@ FLECS_API int32_t ecs_table_column_count( const ecs_table_t *table); -/** Convert type index to column index. +/** Convert type index to column index. * Tables have an array of columns for each component in the table. This array - * does not include elements for tags, which means that the index for a + * does not include elements for tags, which means that the index for a * component in the table type is not necessarily the same as the index in the * column array. This operation converts from an index in the table type to an * index in the column array. - * + * * @param table The table. * @param index The index in the table type. * @return The index in the table column array. + * + * @see ecs_table_column_to_type_index() */ FLECS_API int32_t ecs_table_type_to_column_index( @@ -5206,9 +5626,9 @@ int32_t ecs_table_type_to_column_index( int32_t index); /** Convert column index to type index. - * Same as ecs_table_type_to_column_index, but converts from an index in the + * Same as ecs_table_type_to_column_index(), but converts from an index in the * column array to an index in the table type. - * + * * @param table The table. * @param index The column index. * @return The index in the table type. @@ -5220,7 +5640,7 @@ int32_t ecs_table_column_to_type_index( /** Get column from table by column index. * This operation returns the component array for the provided index. - * + * * @param table The table. * @param index The column index. * @param offset The index of the first row to return (0 for entire column). @@ -5234,7 +5654,8 @@ void* ecs_table_get_column( /** Get column from table by component id. * This operation returns the component array for the provided component id. - * + * + * @param world The world. * @param table The table. * @param id The component id for the column. * @param offset The index of the first row to return (0 for entire column). @@ -5249,7 +5670,7 @@ void* ecs_table_get_id( /** Get column size from table. * This operation returns the component size for the provided index. - * + * * @param table The table. * @param index The column index. * @return The component size, or 0 if the index is not a component. @@ -5259,25 +5680,46 @@ size_t ecs_table_get_column_size( const ecs_table_t *table, int32_t index); -/** Returns the number of records in the table. - * This operation returns the number of records that have been populated through - * the regular (entity) API as well as the number of records that have been - * inserted using the direct access API. +/** Returns the number of entities in the table. + * This operation returns the number of entities in the table. * * @param table The table. - * @return The number of records in a table. + * @return The number of entities in the table. */ FLECS_API int32_t ecs_table_count( const ecs_table_t *table); -/** Test if table has id. - * Same as ecs_table_get_type_index(world, table, id) != -1. +/** Returns allocated size of table. + * This operation returns the number of elements allocated in the table + * per column. * + * @param table The table. + * @return The number of allocated elements in the table. + */ +FLECS_API +int32_t ecs_table_size( + const ecs_table_t *table); + +/** Returns array with entity ids for table. + * The size of the returned array is the result of ecs_table_count(). + * + * @param table The table. + * @return Array with entity ids for table. + */ +FLECS_API +const ecs_entity_t* ecs_table_entities( + const ecs_table_t *table); + +/** Test if table has id. + * Same as `ecs_table_get_type_index(world, table, id) != -1`. + * * @param world The world. * @param table The table. * @param id The id. * @return True if the table has the id, false if the table doesn't. + * + * @see ecs_table_get_type_index() */ FLECS_API bool ecs_table_has_id( @@ -5286,10 +5728,10 @@ bool ecs_table_has_id( ecs_id_t id); /** Return depth for table in tree for relationship rel. - * Depth is determined by counting the number of targets encountered while + * Depth is determined by counting the number of targets encountered while * traversing up the relationship tree for rel. Only acyclic relationships are * supported. - * + * * @param world The world. * @param table The table. * @param rel The relationship. @@ -5316,11 +5758,11 @@ ecs_table_t* ecs_table_add_id( ecs_table_t *table, ecs_id_t id); -/** Find table from id array. - * This operation finds or creates a table with the specified array of +/** Find table from id array. + * This operation finds or creates a table with the specified array of * (component) ids. The ids in the array must be sorted, and it may not contain * duplicate elements. - * + * * @param world The world. * @param ids The id array. * @param id_count The number of elements in the id array. @@ -5347,15 +5789,15 @@ ecs_table_t* ecs_table_remove_id( ecs_table_t *table, ecs_id_t id); -/** Lock or unlock table. - * When a table is locked, modifications to it will throw an assert. When the +/** Lock a table. + * When a table is locked, modifications to it will throw an assert. When the * table is locked recursively, it will take an equal amount of unlock * operations to actually unlock the table. * * Table locks can be used to build safe iterators where it is guaranteed that * the contents of a table are not modified while it is being iterated. * - * The operation only works when called on the world, and has no side effects + * The operation only works when called on the world, and has no side effects * when called on a stage. The assumption is that when called on a stage, * operations are deferred already. * @@ -5368,7 +5810,7 @@ void ecs_table_lock( ecs_table_t *table); /** Unlock a table. - * Must be called after calling ecs_table_lock. + * Must be called after calling ecs_table_lock(). * * @param world The world. * @param table The table to unlock. @@ -5376,11 +5818,11 @@ void ecs_table_lock( FLECS_API void ecs_table_unlock( ecs_world_t *world, - ecs_table_t *table); + ecs_table_t *table); /** Test table for flags. - * Test if table has all of the provided flags. See - * include/flecs/private/api_flags.h for a list of table flags that can be used + * Test if table has all of the provided flags. See + * include/flecs/private/api_flags.h for a list of table flags that can be used * with this function. * * @param table The table. @@ -5412,15 +5854,15 @@ void ecs_table_swap_rows( * - Ctor for each component in the target table * - Move for each overlapping component * - Dtor for each component in the source table. - * - OnAdd triggers for non-overlapping components in the target table - * - OnRemove triggers for non-overlapping components in the source table. + * - `OnAdd` triggers for non-overlapping components in the target table + * - `OnRemove` triggers for non-overlapping components in the source table. * * This operation is a faster than adding/removing components individually. * * The application must explicitly provide the difference in components between * tables as the added/removed parameters. This can usually be derived directly - * from the result of ecs_table_add_id and esc_table_remove_id. These arrays are - * required to properly execute OnAdd/OnRemove triggers. + * from the result of ecs_table_add_id() and ecs_table_remove_id(). These arrays are + * required to properly execute `OnAdd`/`OnRemove` triggers. * * @param world The world. * @param entity The entity to commit. @@ -5437,33 +5879,24 @@ bool ecs_commit( const ecs_type_t *added, const ecs_type_t *removed); -/** Find record for entity. */ -FLECS_API -ecs_record_t* ecs_record_find( - const ecs_world_t *world, - ecs_entity_t entity); - -/** Get component pointer from column/record. */ -FLECS_API -void* ecs_record_get_column( - const ecs_record_t *r, - int32_t column, - size_t c_size); /** Search for component id in table type. - * This operation returns the index of first occurrance of the id in the table + * This operation returns the index of first occurrence of the id in the table * type. The id may be a wildcard. - * + * * When id_out is provided, the function will assign it with the found id. The * found id may be different from the provided id if it is a wildcard. - * + * * This is a constant time operation. - * + * * @param world The world. * @param table The table. * @param id The id to search for. * @param id_out If provided, it will be set to the found id (optional). * @return The index of the id in the table type. + * + * @see ecs_search_offset() + * @see ecs_search_relation() */ FLECS_API int32_t ecs_search( @@ -5473,34 +5906,39 @@ int32_t ecs_search( ecs_id_t *id_out); /** Search for component id in table type starting from an offset. - * This operation is the same as ecs_search, but starts searching from an offset + * This operation is the same as ecs_search(), but starts searching from an offset * in the table type. - * + * * This operation is typically called in a loop where the resulting index is * used in the next iteration as offset: - * + * + * @code * int32_t index = -1; * while ((index = ecs_search_offset(world, table, offset, id, NULL))) { * // do stuff * } - * + * @endcode + * * Depending on how the operation is used it is either linear or constant time. - * When the id has the form (id) or (rel, *) and the operation is invoked as + * When the id has the form `(id)` or `(rel, *)` and the operation is invoked as * in the above example, it is guaranteed to be constant time. - * - * If the provided id has the form (*, tgt) the operation takes linear time. The + * + * If the provided id has the form `(*, tgt)` the operation takes linear time. The * reason for this is that ids for an target are not packed together, as they * are sorted relationship first. - * + * * If the id at the offset does not match the provided id, the operation will do * a linear search to find a matching id. - * + * * @param world The world. * @param table The table. * @param offset Offset from where to start searching. * @param id The id to search for. * @param id_out If provided, it will be set to the found id (optional). * @return The index of the id in the table type. + * + * @see ecs_search() + * @see ecs_search_relation() */ FLECS_API int32_t ecs_search_offset( @@ -5511,12 +5949,13 @@ int32_t ecs_search_offset( ecs_id_t *id_out); /** Search for component/relationship id in table type starting from an offset. - * This operation is the same as ecs_search_offset, but has the additional + * This operation is the same as ecs_search_offset(), but has the additional * capability of traversing relationships to find a component. For example, if * an application wants to find a component for either the provided table or a - * prefab (using the IsA relationship) of that table, it could use the operation + * prefab (using the `IsA` relationship) of that table, it could use the operation * like this: - * + * + * @code * int32_t index = ecs_search_relation( * world, // the world * table, // the table @@ -5528,13 +5967,14 @@ int32_t ecs_search_offset( * NULL, // (optional) entity on which component was found * NULL, // see above * NULL); // internal type with information about matched id - * - * The operation searches depth first. If a table type has 2 IsA relationships, the - * operation will first search the IsA tree of the first relationship. - * - * When choosing betwen ecs_search, ecs_search_offset and ecs_search_relation, + * @endcode + * + * The operation searches depth first. If a table type has 2 `IsA` relationships, the + * operation will first search the `IsA` tree of the first relationship. + * + * When choosing between ecs_search(), ecs_search_offset() and ecs_search_relation(), * the simpler the function the better its performance. - * + * * @param world The world. * @param table The table. * @param offset Offset from where to start searching. @@ -5545,6 +5985,9 @@ int32_t ecs_search_offset( * @param id_out If provided, it will be set to the found id (optional). * @param tr_out Internal datatype. * @return The index of the id in the table type. + * + * @see ecs_search() + * @see ecs_search_offset() */ FLECS_API int32_t ecs_search_relation( @@ -5553,20 +5996,33 @@ int32_t ecs_search_relation( int32_t offset, ecs_id_t id, ecs_entity_t rel, - ecs_flags32_t flags, /* EcsSelf and/or EcsUp */ + ecs_flags64_t flags, /* EcsSelf and/or EcsUp */ ecs_entity_t *subject_out, ecs_id_t *id_out, struct ecs_table_record_t **tr_out); +/** Remove all entities in a table. Does not deallocate table memory. + * Retaining table memory can be efficient when planning + * to refill the table with operations like ecs_bulk_init + * + * @param world The world. + * @param table The table to clear. + */ +FLECS_API +void ecs_table_clear_entities( + ecs_world_t* world, + ecs_table_t* table); + /** @} */ /** * @defgroup values Values - * @brief Construct, destruct, copy and move dynamically created values. + * Construct, destruct, copy and move dynamically created values. + * * @{ */ -/** Construct a value in existing storage +/** Construct a value in existing storage * * @param world The world. * @param type The type of the value to create. @@ -5579,7 +6035,7 @@ int ecs_value_init( ecs_entity_t type, void *ptr); -/** Construct a value in existing storage +/** Construct a value in existing storage * * @param world The world. * @param ti The type info of the type to create. @@ -5592,8 +6048,8 @@ int ecs_value_init_w_type_info( const ecs_type_info_t *ti, void *ptr); -/** Construct a value in new storage - * +/** Construct a value in new storage + * * @param world The world. * @param type The type of the value to create. * @return Pointer to type if success, NULL if failed. @@ -5603,8 +6059,8 @@ void* ecs_value_new( ecs_world_t *world, ecs_entity_t type); -/** Construct a value in new storage - * +/** Construct a value in new storage + * * @param world The world. * @param ti The type info of the type to create. * @return Pointer to type if success, NULL if failed. @@ -5613,24 +6069,24 @@ void* ecs_value_new_w_type_info( ecs_world_t *world, const ecs_type_info_t *ti); -/** Destruct a value - * +/** Destruct a value + * * @param world The world. * @param ti Type info of the value to destruct. * @param ptr Pointer to constructed value of type 'type'. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_fini_w_type_info( const ecs_world_t *world, const ecs_type_info_t *ti, void *ptr); -/** Destruct a value - * +/** Destruct a value + * * @param world The world. * @param type The type of the value to destruct. * @param ptr Pointer to constructed value of type 'type'. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ FLECS_API int ecs_value_fini( @@ -5639,10 +6095,11 @@ int ecs_value_fini( void* ptr); /** Destruct a value, free storage - * + * * @param world The world. * @param type The type of the value to destruct. - * @return Zero if success, nonzero if failed. + * @param ptr A pointer to the value. + * @return Zero if success, nonzero if failed. */ FLECS_API int ecs_value_free( @@ -5651,12 +6108,12 @@ int ecs_value_free( void* ptr); /** Copy value. - * + * * @param world The world. * @param ti Type info of the value to copy. * @param dst Pointer to the storage to copy to. * @param src Pointer to the value to copy. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ FLECS_API int ecs_value_copy_w_type_info( @@ -5666,12 +6123,12 @@ int ecs_value_copy_w_type_info( const void *src); /** Copy value. - * + * * @param world The world. * @param type The type of the value to copy. * @param dst Pointer to the storage to copy to. * @param src Pointer to the value to copy. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ FLECS_API int ecs_value_copy( @@ -5681,12 +6138,12 @@ int ecs_value_copy( const void *src); /** Move value. - * + * * @param world The world. * @param ti Type info of the value to move. * @param dst Pointer to the storage to move to. * @param src Pointer to the value to move. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_move_w_type_info( const ecs_world_t *world, @@ -5695,12 +6152,12 @@ int ecs_value_move_w_type_info( void *src); /** Move value. - * + * * @param world The world. * @param type The type of the value to move. * @param dst Pointer to the storage to move to. * @param src Pointer to the value to move. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_move( const ecs_world_t *world, @@ -5709,12 +6166,12 @@ int ecs_value_move( void *src); /** Move construct value. - * + * * @param world The world. * @param ti Type info of the value to move. * @param dst Pointer to the storage to move to. * @param src Pointer to the value to move. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_move_ctor_w_type_info( const ecs_world_t *world, @@ -5723,12 +6180,12 @@ int ecs_value_move_ctor_w_type_info( void *src); /** Move construct value. - * + * * @param world The world. * @param type The type of the value to move. * @param dst Pointer to the storage to move to. * @param src Pointer to the value to move. - * @return Zero if success, nonzero if failed. + * @return Zero if success, nonzero if failed. */ int ecs_value_move_ctor( const ecs_world_t *world, @@ -5742,10 +6199,9 @@ int ecs_value_move_ctor( /** * @defgroup c_addons Addons - * @brief C APIs for addons. - * - * \ingroup c - * + * @ingroup c + * C APIs for addons. + * * @{ * @} */ diff --git a/vendors/flecs/include/flecs/addons/alerts.h b/vendors/flecs/include/flecs/addons/alerts.h index d6f060e72..100698696 100644 --- a/vendors/flecs/include/flecs/addons/alerts.h +++ b/vendors/flecs/include/flecs/addons/alerts.h @@ -11,19 +11,15 @@ /** * @defgroup c_addons_alerts Alerts - * @brief Create alerts from monitoring queries. - * - * \ingroup c_addons + * @ingroup c_addons + * Create alerts from monitoring queries. + * * @{ */ #ifndef FLECS_ALERTS_H #define FLECS_ALERTS_H -#ifndef FLECS_RULES -#define FLECS_RULES -#endif - #ifndef FLECS_PIPELINE #define FLECS_PIPELINE #endif @@ -34,36 +30,41 @@ extern "C" { #define ECS_ALERT_MAX_SEVERITY_FILTERS (4) -/* Module id */ +/** Module id. */ FLECS_API extern ECS_COMPONENT_DECLARE(FlecsAlerts); /* Module components */ -/** Tag added to alert, and used as first element of alert severity pair */ -FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlert); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertInstance); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertsActive); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertTimeout); +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlert); /**< Component added to alert, and used as first element of alert severity pair. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertInstance); /**< Component added to alert instance. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertsActive); /**< Component added to alert source which tracks how many active alerts there are. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertTimeout); /**< Component added to alert which tracks how long an alert has been inactive. */ /* Alert severity tags */ -FLECS_API extern ECS_TAG_DECLARE(EcsAlertInfo); -FLECS_API extern ECS_TAG_DECLARE(EcsAlertWarning); -FLECS_API extern ECS_TAG_DECLARE(EcsAlertError); -FLECS_API extern ECS_TAG_DECLARE(EcsAlertCritical); +FLECS_API extern ECS_TAG_DECLARE(EcsAlertInfo); /**< Info alert severity. */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertWarning); /**< Warning alert severity. */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertError); /**< Error alert severity. */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertCritical); /**< Critical alert severity. */ -/** Alert information. Added to each alert instance */ +/** Component added to alert instance. */ typedef struct EcsAlertInstance { - char *message; + char *message; /**< Generated alert message */ } EcsAlertInstance; /** Map with active alerts for entity. */ typedef struct EcsAlertsActive { - int32_t info_count; - int32_t warning_count; - int32_t error_count; + int32_t info_count; /**< Number of alerts for source with info severity */ + int32_t warning_count; /**< Number of alerts for source with warning severity */ + int32_t error_count; /**< Number of alerts for source with error severity */ ecs_map_t alerts; } EcsAlertsActive; +/** Alert severity filter. + * A severity filter can adjust the severity of an alert based on whether an + * entity in the alert query has a specific component. For example, a filter + * could check if an entity has the "Production" tag, and increase the default + * severity of an alert from Warning to Error. + */ typedef struct ecs_alert_severity_filter_t { ecs_entity_t severity; /* Severity kind */ ecs_id_t with; /* Component to match */ @@ -72,7 +73,8 @@ typedef struct ecs_alert_severity_filter_t { int32_t _var_index; /* Index of variable in filter (do not set) */ } ecs_alert_severity_filter_t; -typedef struct ecs_alert_desc_t { +/** Alert descriptor, used with ecs_alert_init(). */ +typedef struct ecs_alert_desc_t { int32_t _canary; /** Entity associated with alert */ @@ -81,15 +83,16 @@ typedef struct ecs_alert_desc_t { /** Alert query. An alert will be created for each entity that matches the * specified query. The query must have at least one term that uses the * $this variable (default). */ - ecs_filter_desc_t filter; + ecs_query_desc_t query; /** Template for alert message. This string is used to generate the alert * message and may refer to variables in the query result. The format for - * the template expressions is as specified by ecs_interpolate_string. - * + * the template expressions is as specified by ecs_script_string_interpolate(). + * * Examples: - * "$this has Position but not Velocity" - * "$this has a parent entity $parent without Position" + * + * "$this has Position but not Velocity" + * "$this has a parent entity $parent without Position" */ const char *message; @@ -99,19 +102,19 @@ typedef struct ecs_alert_desc_t { /** Description of alert. Will only be set if FLECS_DOC addon is enabled */ const char *brief; - /** Metric kind. Must be EcsAlertInfo, EcsAlertWarning, EcsAlertError or + /** Metric kind. Must be EcsAlertInfo, EcsAlertWarning, EcsAlertError or * EcsAlertCritical. Defaults to EcsAlertError. */ ecs_entity_t severity; /** Severity filters can be used to assign different severities to the same - * alert. This prevents having to create multiple alerts, and allows - * entities to transition between severities without resetting the + * alert. This prevents having to create multiple alerts, and allows + * entities to transition between severities without resetting the * alert duration (optional). */ ecs_alert_severity_filter_t severity_filters[ECS_ALERT_MAX_SEVERITY_FILTERS]; /** The retain period specifies how long an alert must be inactive before it * is cleared. This makes it easier to track noisy alerts. While an alert is - * inactive its duration won't increase. + * inactive its duration won't increase. * When the retain period is 0, the alert will clear immediately after it no * longer matches the alert query. */ ecs_ftime_t retain_period; @@ -130,26 +133,26 @@ typedef struct ecs_alert_desc_t { } ecs_alert_desc_t; /** Create a new alert. - * An alert is a query that is evaluated periodically and creates alert - * instances for each entity that matches the query. Alerts can be used to + * An alert is a query that is evaluated periodically and creates alert + * instances for each entity that matches the query. Alerts can be used to * automate detection of errors in an application. - * + * * Alerts are automatically cleared when a query is no longer true for an alert * instance. At most one alert instance will be created per matched entity. - * + * * Alert instances have three components: * - AlertInstance: contains the alert message for the instance * - MetricSource: contains the entity that triggered the alert * - MetricValue: contains how long the alert has been active - * + * * Alerts reuse components from the metrics addon so that alert instances can be * tracked and discovered as metrics. Just like metrics, alert instances are * created as children of the alert. - * + * * When an entity has active alerts, it will have the EcsAlertsActive component * which contains a map with active alerts for the entity. This component * will be automatically removed once all alerts are cleared for the entity. - * + * * @param world The world. * @param desc Alert description. * @return The alert entity. @@ -159,6 +162,9 @@ ecs_entity_t ecs_alert_init( ecs_world_t *world, const ecs_alert_desc_t *desc); +/** Create a new alert. + * @see ecs_alert_init() + */ #define ecs_alert(world, ...)\ ecs_alert_init(world, &(ecs_alert_desc_t)__VA_ARGS__) @@ -167,7 +173,7 @@ ecs_entity_t ecs_alert_init( * will return whether the specified alert is active for the entity. When no * alert is specified, the operation will return the total number of active * alerts for the entity. - * + * * @param world The world. * @param entity The entity. * @param alert The alert to test for (optional). @@ -182,7 +188,7 @@ int32_t ecs_get_alert_count( /** Return alert instance for specified alert. * This operation returns the alert instance for the specified alert. If the * alert is not active for the entity, the operation will return 0. - * + * * @param world The world. * @param entity The entity. * @param alert The alert to test for. @@ -194,7 +200,14 @@ ecs_entity_t ecs_get_alert( ecs_entity_t entity, ecs_entity_t alert); -/* Module import */ +/** Alert module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsAlerts) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsAlertsImport( ecs_world_t *world); diff --git a/vendors/flecs/include/flecs/addons/app.h b/vendors/flecs/include/flecs/addons/app.h index d71dee09f..454c314aa 100644 --- a/vendors/flecs/include/flecs/addons/app.h +++ b/vendors/flecs/include/flecs/addons/app.h @@ -23,9 +23,9 @@ extern "C" { /** * @defgroup c_addons_app App - * @brief Optional addon for running the main application loop. - * - * \ingroup c_addons + * @ingroup c_addons + * Optional addon for running the main application loop. + * * @{ */ @@ -33,14 +33,14 @@ extern "C" { typedef int(*ecs_app_init_action_t)( ecs_world_t *world); -/** Used with ecs_app_run. */ +/** Used with ecs_app_run(). */ typedef struct ecs_app_desc_t { ecs_ftime_t target_fps; /**< Target FPS. */ ecs_ftime_t delta_time; /**< Frame time increment (0 for measured values) */ int32_t threads; /**< Number of threads. */ int32_t frames; /**< Number of frames to run (0 for infinite) */ bool enable_rest; /**< Enables ECS access over HTTP, necessary for explorer */ - bool enable_monitor; /**< Periodically collect statistics */ + bool enable_stats; /**< Periodically collect statistics */ uint16_t port; /**< HTTP port used by REST API */ ecs_app_init_action_t init; /**< If set, function is ran before starting the @@ -51,22 +51,22 @@ typedef struct ecs_app_desc_t { /** Callback type for run action. */ typedef int(*ecs_app_run_action_t)( - ecs_world_t *world, + ecs_world_t *world, ecs_app_desc_t *desc); /** Callback type for frame action. */ typedef int(*ecs_app_frame_action_t)( - ecs_world_t *world, + ecs_world_t *world, const ecs_app_desc_t *desc); /** Run application. * This will run the application with the parameters specified in desc. After - * the application quits (ecs_quit is called) the world will be cleaned up. - * + * the application quits (ecs_quit() is called) the world will be cleaned up. + * * If a custom run action is set, it will be invoked by this operation. The * default run action calls the frame action in a loop until it returns a * non-zero value. - * + * * @param world The world. * @param desc Application parameters. */ @@ -77,11 +77,11 @@ int ecs_app_run( /** Default frame callback. * This operation will run a single frame. By default this operation will invoke - * ecs_progress directly, unless a custom frame action is set. - * + * ecs_progress() directly, unless a custom frame action is set. + * * @param world The world. - * @param desc The desc struct passed to ecs_app_run. - * @return value returned by ecs_progress + * @param desc The desc struct passed to ecs_app_run(). + * @return value returned by ecs_progress() */ FLECS_API int ecs_app_run_frame( @@ -89,8 +89,8 @@ int ecs_app_run_frame( const ecs_app_desc_t *desc); /** Set custom run action. - * See ecs_app_run. - * + * See ecs_app_run(). + * * @param callback The run action. */ FLECS_API @@ -98,8 +98,8 @@ int ecs_app_set_run_action( ecs_app_run_action_t callback); /** Set custom frame action. - * See ecs_app_run_frame. - * + * See ecs_app_run_frame(). + * * @param callback The frame action. */ FLECS_API diff --git a/vendors/flecs/include/flecs/addons/coredoc.h b/vendors/flecs/include/flecs/addons/coredoc.h deleted file mode 100644 index 14f77b2c7..000000000 --- a/vendors/flecs/include/flecs/addons/coredoc.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file addons/coredoc.h - * @brief Core doc module. - * - * The core doc module imports documentation and reflection data for core - * components, tags and systems. - */ - -#ifdef FLECS_COREDOC - -#ifndef FLECS_DOC -#define FLECS_DOC -#endif - -#ifndef FLECS_META -#define FLECS_META -#endif - -#ifndef FLECS_COREDOC_H -#define FLECS_COREDOC_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup c_addons_coredoc Coredoc - * @brief Module that adds documentation and reflection to core entities. - * - * \ingroup c_addons - * @{ - */ - -/* Module import */ - -FLECS_API -void FlecsCoreDocImport( - ecs_world_t *world); - -/* @} */ - -#ifdef __cplusplus -} -#endif - -#endif - -#endif diff --git a/vendors/flecs/include/flecs/addons/cpp/c_types.hpp b/vendors/flecs/include/flecs/addons/cpp/c_types.hpp index 1df1718ce..1016c1fb8 100644 --- a/vendors/flecs/include/flecs/addons/cpp/c_types.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/c_types.hpp @@ -9,25 +9,24 @@ namespace flecs { /** * @defgroup cpp_globals API Types & Globals - * @brief Types & constants bridged from C API. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Types & constants bridged from C API. + * * @{ */ using world_t = ecs_world_t; using world_info_t = ecs_world_info_t; -using query_group_info_t = ecs_query_group_info_t; using id_t = ecs_id_t; using entity_t = ecs_entity_t; using type_t = ecs_type_t; using table_t = ecs_table_t; -using filter_t = ecs_filter_t; -using observer_t = ecs_observer_t; +using term_t = ecs_term_t; using query_t = ecs_query_t; -using rule_t = ecs_rule_t; -using ref_t = ecs_ref_t; +using query_group_info_t = ecs_query_group_info_t; +using observer_t = ecs_observer_t; using iter_t = ecs_iter_t; +using ref_t = ecs_ref_t; using type_info_t = ecs_type_info_t; using type_hooks_t = ecs_type_hooks_t; using flags32_t = ecs_flags32_t; @@ -35,6 +34,7 @@ using flags32_t = ecs_flags32_t; enum inout_kind_t { InOutDefault = EcsInOutDefault, InOutNone = EcsInOutNone, + InOutFilter = EcsInOutFilter, InOut = EcsInOut, In = EcsIn, Out = EcsOut @@ -50,20 +50,27 @@ enum oper_kind_t { NotFrom = EcsNotFrom }; -/** Id flags */ -static const flecs::entity_t Pair = ECS_PAIR; -static const flecs::entity_t Override = ECS_OVERRIDE; -static const flecs::entity_t Toggle = ECS_TOGGLE; +enum query_cache_kind_t { + QueryCacheDefault = EcsQueryCacheDefault, + QueryCacheAuto = EcsQueryCacheAuto, + QueryCacheAll = EcsQueryCacheAll, + QueryCacheNone = EcsQueryCacheNone +}; + +/** Id bit flags */ +static const flecs::entity_t PAIR = ECS_PAIR; +static const flecs::entity_t AUTO_OVERRIDE = ECS_AUTO_OVERRIDE; +static const flecs::entity_t TOGGLE = ECS_TOGGLE; //////////////////////////////////////////////////////////////////////////////// -//// Builtin components and tags +//// Builtin components and tags //////////////////////////////////////////////////////////////////////////////// /* Builtin components */ using Component = EcsComponent; using Identifier = EcsIdentifier; using Poly = EcsPoly; -using Target = EcsTarget; +using DefaultChildComponent = EcsDefaultChildComponent; /* Builtin tags */ static const flecs::entity_t Query = EcsQuery; @@ -82,44 +89,57 @@ static const flecs::entity_t Phase = EcsPhase; 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; -static const uint32_t Up = EcsUp; -static const uint32_t Down = EcsDown; -static const uint32_t Cascade = EcsCascade; -static const uint32_t Desc = EcsDesc; -static const uint32_t Parent = EcsParent; -static const uint32_t IsVariable = EcsIsVariable; -static const uint32_t IsEntity = EcsIsEntity; -static const uint32_t Filter = EcsFilter; -static const uint32_t TraverseFlags = EcsTraverseFlags; +static const uint64_t Self = EcsSelf; +static const uint64_t Up = EcsUp; +static const uint64_t Trav = EcsTrav; +static const uint64_t Cascade = EcsCascade; +static const uint64_t Desc = EcsDesc; +static const uint64_t IsVariable = EcsIsVariable; +static const uint64_t IsEntity = EcsIsEntity; +static const uint64_t IsName = EcsIsName; +static const uint64_t TraverseFlags = EcsTraverseFlags; +static const uint64_t TermRefFlags = EcsTermRefFlags; /* Builtin entity ids */ static const flecs::entity_t Flecs = EcsFlecs; static const flecs::entity_t FlecsCore = EcsFlecsCore; static const flecs::entity_t World = EcsWorld; -/* Relationship properties */ +/* Component traits */ static const flecs::entity_t Wildcard = EcsWildcard; static const flecs::entity_t Any = EcsAny; static const flecs::entity_t This = EcsThis; static const flecs::entity_t Transitive = EcsTransitive; static const flecs::entity_t Reflexive = EcsReflexive; static const flecs::entity_t Final = EcsFinal; -static const flecs::entity_t DontInherit = EcsDontInherit; -static const flecs::entity_t AlwaysOverride = EcsAlwaysOverride; -static const flecs::entity_t Tag = EcsTag; -static const flecs::entity_t Union = EcsUnion; +static const flecs::entity_t PairIsTag = EcsPairIsTag; static const flecs::entity_t Exclusive = EcsExclusive; static const flecs::entity_t Acyclic = EcsAcyclic; static const flecs::entity_t Traversable = EcsTraversable; static const flecs::entity_t Symmetric = EcsSymmetric; static const flecs::entity_t With = EcsWith; static const flecs::entity_t OneOf = EcsOneOf; +static const flecs::entity_t Trait = EcsTrait; +static const flecs::entity_t Relationship = EcsRelationship; +static const flecs::entity_t Target = EcsTarget; +static const flecs::entity_t CanToggle = EcsCanToggle; + +/* OnInstantiate trait */ +static const flecs::entity_t OnInstantiate = EcsOnInstantiate; +static const flecs::entity_t Override = EcsOverride; +static const flecs::entity_t Inherit = EcsInherit; +static const flecs::entity_t DontInherit = EcsDontInherit; + +/* OnDelete/OnDeleteTarget traits */ +static const flecs::entity_t OnDelete = EcsOnDelete; +static const flecs::entity_t OnDeleteTarget = EcsOnDeleteTarget; +static const flecs::entity_t Remove = EcsRemove; +static const flecs::entity_t Delete = EcsDelete; +static const flecs::entity_t Panic = EcsPanic; /* Builtin relationships */ static const flecs::entity_t IsA = EcsIsA; @@ -131,18 +151,11 @@ static const flecs::entity_t SlotOf = EcsSlotOf; static const flecs::entity_t Name = EcsName; static const flecs::entity_t Symbol = EcsSymbol; -/* Cleanup policies */ -static const flecs::entity_t OnDelete = EcsOnDelete; -static const flecs::entity_t OnDeleteTarget = EcsOnDeleteTarget; -static const flecs::entity_t Remove = EcsRemove; -static const flecs::entity_t Delete = EcsDelete; -static const flecs::entity_t Panic = EcsPanic; - -/* Misc */ -static const flecs::entity_t Flatten = EcsFlatten; -static const flecs::entity_t DefaultChildComponent = EcsDefaultChildComponent; +/* Storage */ +static const flecs::entity_t Sparse = EcsSparse; +static const flecs::entity_t Union = EcsUnion; -/* Builtin predicates for comparing entity ids in queries. Only supported by rules */ +/* Builtin predicates for comparing entity ids in queries. */ static const flecs::entity_t PredEq = EcsPredEq; static const flecs::entity_t PredMatch = EcsPredMatch; static const flecs::entity_t PredLookup = EcsPredLookup; diff --git a/vendors/flecs/include/flecs/addons/cpp/component.hpp b/vendors/flecs/include/flecs/addons/cpp/component.hpp index cda9f44ad..58e82739f 100644 --- a/vendors/flecs/include/flecs/addons/cpp/component.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/component.hpp @@ -10,9 +10,9 @@ /** * @defgroup cpp_components Components - * @brief Registering and working with components. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Registering and working with components. + * * @{ */ @@ -30,12 +30,13 @@ namespace _ { #if defined(__GNUC__) || defined(_WIN32) template -inline static const char* type_name() { +inline const char* type_name() { static const size_t len = ECS_FUNC_TYPE_LEN(const char*, type_name, ECS_FUNC_NAME); static char result[len + 1] = {}; static const size_t front_len = ECS_FUNC_NAME_FRONT(const char*, type_name); - return ecs_cpp_get_type_name(result, ECS_FUNC_NAME, len, front_len); -} + static const char* cppTypeName = ecs_cpp_get_type_name(result, ECS_FUNC_NAME, len, front_len); + return cppTypeName; +} #else #error "implicit component registration not supported" #endif @@ -43,10 +44,11 @@ inline static const char* type_name() { // Translate a typename into a language-agnostic identifier. This allows for // registration of components/modules across language boundaries. template -inline static const char* symbol_name() { +inline const char* symbol_name() { static const size_t len = ECS_FUNC_TYPE_LEN(const char*, symbol_name, ECS_FUNC_NAME); - static char result[len + 1] = {}; - return ecs_cpp_get_symbol_name(result, type_name(), len); + static char result[len + 1] = {}; + static const char* cppSymbolName = ecs_cpp_get_symbol_name(result, type_name(), len); + return cppSymbolName; } template <> inline const char* symbol_name() { @@ -89,7 +91,7 @@ template* = nullptr> void register_lifecycle_actions(ecs_world_t*, ecs_entity_t) { } -// If the component is non-trivial, register component lifecycle actions. +// If the component is non-trivial, register component lifecycle actions. // Depending on the type not all callbacks may be available. template::value == false @@ -111,10 +113,14 @@ void register_lifecycle_actions( cl.move_dtor = move_dtor(); ecs_set_hooks_id( world, component, &cl); + + if (cl.move == ecs_move_illegal || cl.move_ctor == ecs_move_ctor_illegal) { + ecs_add_id(world, component, flecs::Sparse); + } } // Class that manages component ids across worlds & binaries. -// The cpp_type class stores the component id for a C++ type in a static global +// The type class stores the component id for a C++ type in a static global // variable that is shared between worlds. Whenever a component is used this // class will check if it already has been registered (has the global id been // set), and if not, register the component with the world. @@ -124,11 +130,14 @@ void register_lifecycle_actions( // registered with the world using the same id. If the id does exist, the class // will register it as a component, and verify whether the input is consistent. template -struct cpp_type_impl { +struct type_impl { + static_assert(is_pointer::value == false, + "pointer types are not allowed for components"); + // Initialize component identifier static void init( - entity_t entity, - bool allow_tag = true) + entity_t entity, + bool allow_tag = true) { if (s_reset_count != ecs_cpp_reset_count_get()) { reset(); @@ -136,7 +145,7 @@ struct cpp_type_impl { // If an identifier was already set, check for consistency if (s_id) { - ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, + ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, type_name()); ecs_assert(allow_tag == s_allow_tag, ECS_INVALID_PARAMETER, NULL); @@ -160,7 +169,7 @@ struct cpp_type_impl { } // Obtain a component identifier for explicit component registration. - static entity_t id_explicit(world_t *world = nullptr, + static entity_t id_explicit(world_t *world = nullptr, const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0, bool is_component = true, bool *existing = nullptr) { @@ -171,9 +180,9 @@ struct cpp_type_impl { ecs_assert(!id || s_id == id, ECS_INCONSISTENT_COMPONENT_ID, NULL); } - // If no id has been registered yet for the component (indicating the + // If no id has been registered yet for the component (indicating the // component has not yet been registered, or the component is used - // across more than one binary), or if the id does not exists in the + // across more than one binary), or if the id does not exists in the // world (indicating a multi-world application), register it. if (!s_id || (world && !ecs_exists(world, s_id))) { init(s_id ? s_id : id, allow_tag); @@ -189,34 +198,35 @@ struct cpp_type_impl { } entity_t entity = ecs_cpp_component_register_explicit( - world, s_id, id, name, type_name(), symbol, + world, s_id, id, name, type_name(), symbol, s_size, s_alignment, is_component, existing); s_id = entity; // If component is enum type, register constants - #if FLECS_CPP_ENUM_REFLECTION_SUPPORT + #if FLECS_CPP_ENUM_REFLECTION_SUPPORT _::init_enum(world, entity); #endif } // By now the identifier must be valid and known with the world. - ecs_assert(s_id != 0 && ecs_exists(world, s_id), + ecs_assert(s_id != 0 && ecs_exists(world, s_id), ECS_INTERNAL_ERROR, NULL); return s_id; } // Obtain a component identifier for implicit component registration. This - // is almost the same as id_explicit, except that this operation + // is almost the same as id_explicit, except that this operation // automatically registers lifecycle callbacks. // Additionally, implicit registration temporarily resets the scope & with // state of the world, so that the component is not implicitly created with // the scope/with of the code it happens to be first used by. - static id_t id(world_t *world = nullptr, const char *name = nullptr, + static id_t id(world_t *world = nullptr, const char *name = nullptr, bool allow_tag = true) { // If no id has been registered yet, do it now. +#ifndef FLECS_CPP_NO_AUTO_REGISTRATION if (!registered(world)) { ecs_entity_t prev_scope = 0; ecs_id_t prev_with = 0; @@ -226,7 +236,7 @@ struct cpp_type_impl { prev_with = ecs_set_with(world, 0); } - // This will register a component id, but will not register + // This will register a component id, but will not register // lifecycle callbacks. bool existing; id_explicit(world, name, allow_tag, 0, true, &existing); @@ -237,7 +247,7 @@ struct cpp_type_impl { if (size() && !existing) { register_lifecycle_actions(world, s_id); } - + if (prev_with) { ecs_set_with(world, prev_with); } @@ -245,6 +255,15 @@ struct cpp_type_impl { ecs_set_scope(world, prev_scope); } } +#else + (void)world; + (void)name; + (void)allow_tag; + + ecs_assert(registered(world), ECS_INVALID_OPERATION, + "component '%s' was not registered before use", + type_name()); +#endif // By now we should have a valid identifier ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); @@ -295,29 +314,29 @@ struct cpp_type_impl { }; // Global templated variables that hold component identifier and other info -template entity_t cpp_type_impl::s_id; -template size_t cpp_type_impl::s_size; -template size_t cpp_type_impl::s_alignment; -template bool cpp_type_impl::s_allow_tag( true ); -template int32_t cpp_type_impl::s_reset_count; +template entity_t type_impl::s_id; +template size_t type_impl::s_size; +template size_t type_impl::s_alignment; +template bool type_impl::s_allow_tag( true ); +template int32_t type_impl::s_reset_count; -// Front facing class for implicitly registering a component & obtaining +// Front facing class for implicitly registering a component & obtaining // static component data // Regular type template -struct cpp_type::value >> - : cpp_type_impl> { }; +struct type::value >> + : type_impl> { }; // Pair type template -struct cpp_type::value >> +struct type::value >> { // Override id method to return id of pair static id_t id(world_t *world = nullptr) { return ecs_pair( - cpp_type< pair_first_t >::id(world), - cpp_type< pair_second_t >::id(world)); + type< pair_first_t >::id(world), + type< pair_second_t >::id(world)); } }; @@ -325,12 +344,12 @@ struct cpp_type::value >> /** Untyped component class. * Generic base class for flecs::component. - * - * \ingroup cpp_components + * + * @ingroup cpp_components */ struct untyped_component : entity { using entity::entity; - + # ifdef FLECS_META # include "mixins/meta/untyped_component.inl" # endif @@ -341,32 +360,32 @@ struct untyped_component : entity { /** Component class. * Class used to register components and component metadata. - * - * \ingroup cpp_components + * + * @ingroup cpp_components */ template struct component : untyped_component { /** Register a component. * If the component was already registered, this operation will return a handle * to the existing component. - * + * * @param world The world for which to register the component. * @param name Optional name (overrides typename). * @param allow_tag If true, empty types will be registered with size 0. * @param id Optional id to register component with. */ component( - flecs::world_t *world, - const char *name = nullptr, - bool allow_tag = true, - flecs::id_t id = 0) + flecs::world_t *world, + const char *name = nullptr, + bool allow_tag = true, + flecs::id_t id = 0) { const char *n = name; bool implicit_name = false; if (!n) { n = _::type_name(); - /* Keep track of whether name was explicitly set. If not, and the + /* Keep track of whether name was explicitly set. If not, and the * component was already registered, just use the registered name. * * The registered name may differ from the typename as the registered @@ -375,14 +394,14 @@ struct component : untyped_component { implicit_name = true; } - if (_::cpp_type::registered(world)) { + if (_::type::registered(world)) { /* Obtain component id. Because the component is already registered, * this operation does nothing besides returning the existing id */ - id = _::cpp_type::id_explicit(world, name, allow_tag, id); + id = _::type::id_explicit(world, name, allow_tag, id); ecs_cpp_component_validate(world, id, n, _::symbol_name(), - _::cpp_type::size(), - _::cpp_type::alignment(), + _::type::size(), + _::type::alignment(), implicit_name); } else { /* If component is registered from an existing scope, ignore the @@ -399,9 +418,8 @@ struct component : untyped_component { if (ptr[0] == ':') { last_elem = ptr; } - } else { - last_elem = strrchr(n, ':'); } + if (last_elem) { name = last_elem + 1; } @@ -413,16 +431,16 @@ struct component : untyped_component { ECS_SIZEOF(T), ECS_ALIGNOF(T), implicit_name, &existing); /* Initialize static component data */ - id = _::cpp_type::id_explicit(world, name, allow_tag, id); + id = _::type::id_explicit(world, name, allow_tag, id); /* Initialize lifecycle actions (ctor, dtor, copy, move) */ - if (_::cpp_type::size() && !existing) { + if (_::type::size() && !existing) { _::register_lifecycle_actions(world, id); } } - m_world = world; - m_id = id; + world_ = world; + id_ = id; } /** Register on_add hook. */ @@ -430,14 +448,14 @@ struct component : untyped_component { component& on_add(Func&& func) { using Delegate = typename _::each_delegate::type, T>; flecs::type_hooks_t h = get_hooks(); - ecs_assert(h.on_add == nullptr, ECS_INVALID_OPERATION, + ecs_assert(h.on_add == nullptr, ECS_INVALID_OPERATION, "on_add hook is already set"); BindingCtx *ctx = get_binding_ctx(h); h.on_add = Delegate::run_add; ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_add = reinterpret_cast( _::free_obj); - ecs_set_hooks_id(m_world, m_id, &h); + ecs_set_hooks_id(world_, id_, &h); return *this; } @@ -447,14 +465,14 @@ struct component : untyped_component { using Delegate = typename _::each_delegate< typename std::decay::type, T>; flecs::type_hooks_t h = get_hooks(); - ecs_assert(h.on_remove == nullptr, ECS_INVALID_OPERATION, + ecs_assert(h.on_remove == nullptr, ECS_INVALID_OPERATION, "on_remove hook is already set"); BindingCtx *ctx = get_binding_ctx(h); h.on_remove = Delegate::run_remove; ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_remove = reinterpret_cast( _::free_obj); - ecs_set_hooks_id(m_world, m_id, &h); + ecs_set_hooks_id(world_, id_, &h); return *this; } @@ -464,14 +482,14 @@ struct component : untyped_component { using Delegate = typename _::each_delegate< typename std::decay::type, T>; flecs::type_hooks_t h = get_hooks(); - ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION, + ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION, "on_set hook is already set"); BindingCtx *ctx = get_binding_ctx(h); h.on_set = Delegate::run_set; ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_set = reinterpret_cast( _::free_obj); - ecs_set_hooks_id(m_world, m_id, &h); + ecs_set_hooks_id(world_, id_, &h); return *this; } @@ -482,7 +500,7 @@ struct component : untyped_component { private: using BindingCtx = _::component_binding_ctx; - BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){ + BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){ BindingCtx *result = static_cast(h.binding_ctx); if (!result) { result = FLECS_NEW(BindingCtx); @@ -494,7 +512,7 @@ struct component : untyped_component { } flecs::type_hooks_t get_hooks() { - const flecs::type_hooks_t* h = ecs_get_hooks_id(m_world, m_id); + const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_); if (h) { return *h; } else { @@ -507,8 +525,8 @@ struct component : untyped_component { * component yet, this operation will return 0. */ template flecs::entity_t type_id() { - if (_::cpp_type::s_reset_count == ecs_cpp_reset_count_get()) { - return _::cpp_type::s_id; + if (_::type::s_reset_count == ecs_cpp_reset_count_get()) { + return _::type::s_id; } else { return 0; } @@ -518,23 +536,23 @@ flecs::entity_t type_id() { * When components are registered their component ids are stored in a static * type specific variable. This stored id is passed into component registration * functions to ensure consistent ids across worlds. - * + * * In some cases this can be undesirable, like when a process repeatedly creates * worlds with different components. A typical example where this can happen is * when running multiple tests in a single process, where each test registers * its own set of components. - * - * This operation can be used to prevent reusing of component ids and force + * + * This operation can be used to prevent reusing of component ids and force * generating a new ids upon registration. - * + * * Note that this operation should *never* be called while there are still * alive worlds in a process. Doing so results in undefined behavior. - * + * * Also note that this operation does not actually change the static component * variables. It only ensures that the next time a component id is requested, a * new id will be generated. - * - * \ingroup cpp_components + * + * @ingroup cpp_components */ inline void reset() { ecs_cpp_reset_count_inc(); diff --git a/vendors/flecs/include/flecs/addons/cpp/delegate.hpp b/vendors/flecs/include/flecs/addons/cpp/delegate.hpp index 20129c2ff..42b22c9c7 100644 --- a/vendors/flecs/include/flecs/addons/cpp/delegate.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/delegate.hpp @@ -5,6 +5,8 @@ #pragma once +#include // std::declval + namespace flecs { @@ -34,66 +36,111 @@ struct component_binding_ctx { }; // Utility to convert template argument pack to array of term ptrs -struct term_ptr { - void *ptr; - bool is_ref; +struct field_ptr { + void *ptr = nullptr; + int8_t index = 0; + bool is_ref = false; + bool is_row = false; }; template -struct term_ptrs { - using array = flecs::array<_::term_ptr, sizeof...(Components)>; +struct field_ptrs { + using array = flecs::array<_::field_ptr, sizeof...(Components)>; - bool populate(const ecs_iter_t *iter) { - return populate(iter, 0, static_cast< + void populate(const ecs_iter_t *iter) { + populate(iter, 0, static_cast< remove_reference_t< remove_pointer_t> *>(nullptr)...); } - array m_terms; + void populate_self(const ecs_iter_t *iter) { + populate_self(iter, 0, static_cast< + remove_reference_t< + remove_pointer_t> + *>(nullptr)...); + } + + array fields_; private: - /* Populate terms array without checking for references */ - bool populate(const ecs_iter_t*, size_t) { return false; } - - template - bool populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) { - m_terms[index].ptr = iter->ptrs[index]; - bool is_ref = iter->sources && iter->sources[index] != 0; - m_terms[index].is_ref = is_ref; - is_ref |= populate(iter, index + 1, comps ...); - return is_ref; - } -}; + void populate(const ecs_iter_t*, size_t) { } + + template >, + if_not_t< is_empty::value > = 0> + void populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + if (iter->row_fields & (1llu << index)) { + /* Need to fetch the value with ecs_field_at() */ + fields_[index].is_row = true; + fields_[index].is_ref = true; + fields_[index].index = static_cast(index); + } else { + fields_[index].ptr = ecs_field_w_size(iter, sizeof(A), + static_cast(index)); + fields_[index].is_ref = iter->sources[index] != 0; + } + + populate(iter, index + 1, comps ...); + } + + template >, + if_t< is_empty::value > = 0> + void populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + populate(iter, index + 1, comps ...); + } + + void populate_self(const ecs_iter_t*, size_t) { } + + template >, + if_not_t< is_empty::value > = 0> + void populate_self(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + fields_[index].ptr = ecs_field_w_size(iter, sizeof(A), + static_cast(index)); + fields_[index].is_ref = false; + ecs_assert(iter->sources[index] == 0, ECS_INTERNAL_ERROR, NULL); + populate_self(iter, index + 1, comps ...); + } + + template >, + if_t< is_empty::value > = 0> + void populate_self(const ecs_iter_t *iter, size_t index, T, Targs... comps) { + populate(iter, index + 1, comps ...); + } +}; struct delegate { }; // Template that figures out from the template parameters of a query/system // how to pass the value to the each callback template -struct each_column { }; +struct each_field { }; // Base class struct each_column_base { - each_column_base(const _::term_ptr& term, size_t row) - : m_term(term), m_row(row) { } + each_column_base(const _::field_ptr& field, size_t row) + : field_(field), row_(row) { + } protected: - const _::term_ptr& m_term; - size_t m_row; + const _::field_ptr& field_; + size_t row_; }; // If type is not a pointer, return a reference to the type (default case) template -struct each_column::value && +struct each_field::value && !is_empty>::value && is_actual::value > > : each_column_base { - each_column(const _::term_ptr& term, size_t row) - : each_column_base(term, row) { } + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } T& get_row() { - return static_cast(this->m_term.ptr)[this->m_row]; + return static_cast(this->field_.ptr)[this->row_]; } }; @@ -101,49 +148,48 @@ struct each_column::value && // This requires that the actual type can be converted to the type. // A typical scenario where this happens is when using flecs::pair types. template -struct each_column::value && +struct each_field::value && !is_empty>::value && !is_actual::value> > : each_column_base { - each_column(const _::term_ptr& term, size_t row) - : each_column_base(term, row) { } + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } T get_row() { - return static_cast*>(this->m_term.ptr)[this->m_row]; + return static_cast*>(this->field_.ptr)[this->row_]; } }; - // If type is empty (indicating a tag) the query will pass a nullptr. To avoid // returning nullptr to reference arguments, return a temporary value. template -struct each_column>::value && +struct each_field>::value && !is_pointer::value > > : each_column_base { - each_column(const _::term_ptr& term, size_t row) - : each_column_base(term, row) { } + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } T get_row() { return actual_type_t(); } }; - -// If type is a pointer (indicating an optional value) return the type as is +// If type is a pointer (indicating an optional value) don't index with row if +// the field is not set. template -struct each_column::value && +struct each_field::value && !is_empty>::value > > : each_column_base { - each_column(const _::term_ptr& term, size_t row) - : each_column_base(term, row) { } + each_field(const flecs::iter_t*, _::field_ptr& field, size_t row) + : each_column_base(field, row) { } actual_type_t get_row() { - if (this->m_term.ptr) { - return &static_cast>(this->m_term.ptr)[this->m_row]; + if (this->field_.ptr) { + return &static_cast>(this->field_.ptr)[this->row_]; } else { - // optional argument doesn't hava a value + // optional argument doesn't have a value return nullptr; } } @@ -152,11 +198,11 @@ struct each_column::value && // If the query contains component references to other entities, check if the // current argument is one. template -struct each_ref_column : public each_column { - each_ref_column(const _::term_ptr& term, size_t row) - : each_column(term, row) { +struct each_ref_field : public each_field { + each_ref_field(const flecs::iter_t *iter, _::field_ptr& field, size_t row) + : each_field(iter, field, row) { - if (term.is_ref) { + if (field.is_ref) { // If this is a reference, set the row to 0 as a ref always is a // single value, not an array. This prevents the application from // having to do an if-check on whether the column is owned. @@ -164,51 +210,48 @@ struct each_ref_column : public each_column { // This check only happens when the current table being iterated // over caused the query to match a reference. The check is // performed once per iterated table. - this->m_row = 0; + this->row_ = 0; + } + + if (field.is_row) { + field.ptr = ecs_field_at_w_size(iter, sizeof(T), field.index, + static_cast(row)); } } }; +// Type that handles passing components to each callbacks template struct each_delegate : public delegate { - // If the number of arguments in the function signature is one more than the - // number of components in the query, an extra entity arg is required. - static constexpr bool PassEntity = - (sizeof...(Components) + 1) == (arity::value); - - // If the number of arguments in the function is two more than the number of - // components in the query, extra iter + index arguments are required. - static constexpr bool PassIter = - (sizeof...(Components) + 2) == (arity::value); - - static_assert(arity::value > 0, - "each() must have at least one argument"); - - using Terms = typename term_ptrs::array; + using Terms = typename field_ptrs::array; template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> explicit each_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + : func_(FLECS_MOV(func)) { } explicit each_delegate(const Func& func) noexcept - : m_func(func) { } + : func_(func) { } // Invoke object directly. This operation is useful when the calling // function has just constructed the delegate, such as what happens when // iterating a query. void invoke(ecs_iter_t *iter) const { - term_ptrs terms; + field_ptrs terms; - if (terms.populate(iter)) { - invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms); + iter->flags |= EcsIterCppEach; + + if (iter->ref_fields | iter->up_fields) { + terms.populate(iter); + invoke_unpack< each_ref_field >(iter, func_, 0, terms.fields_); } else { - invoke_callback< each_column >(iter, m_func, 0, terms.m_terms); - } + terms.populate_self(iter); + invoke_unpack< each_field >(iter, func_, 0, terms.fields_); + } } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); self->invoke(iter); } @@ -219,113 +262,99 @@ struct each_delegate : public delegate { } // Function that can be used as callback to free delegate - static void free(void *obj) { + static void destruct(void *obj) { _::free_obj(static_cast(obj)); } // Static function to call for component on_add hook static void run_add(ecs_iter_t *iter) { component_binding_ctx *ctx = reinterpret_cast( - iter->binding_ctx); - iter->binding_ctx = ctx->on_add; + iter->callback_ctx); + iter->callback_ctx = ctx->on_add; run(iter); } // Static function to call for component on_remove hook static void run_remove(ecs_iter_t *iter) { component_binding_ctx *ctx = reinterpret_cast( - iter->binding_ctx); - iter->binding_ctx = ctx->on_remove; + iter->callback_ctx); + iter->callback_ctx = ctx->on_remove; run(iter); } // Static function to call for component on_set hook static void run_set(ecs_iter_t *iter) { component_binding_ctx *ctx = reinterpret_cast( - iter->binding_ctx); - iter->binding_ctx = ctx->on_set; + iter->callback_ctx); + iter->callback_ctx = ctx->on_set; run(iter); } - // Each delegates always use instanced iterators - static bool instanced() { - return true; - } - private: - // Number of function arguments is one more than number of components, pass - // entity as argument. + // func(flecs::entity, Components...) template class ColumnType, - typename... Args, if_t< - sizeof...(Components) == sizeof...(Args) && PassEntity> = 0> + typename... Args, + typename Fn = Func, + decltype(std::declval()( + std::declval(), + std::declval > >().get_row()...), 0) = 0> static void invoke_callback( - ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) + ecs_iter_t *iter, const Func& func, size_t i, Args... comps) { - ECS_TABLE_LOCK(iter->world, iter->table); - - ecs_world_t *world = iter->world; - size_t count = static_cast(iter->count); - - ecs_assert(count > 0, ECS_INVALID_OPERATION, + ecs_assert(iter->count > 0, ECS_INVALID_OPERATION, "no entities returned, use each() without flecs::entity argument"); - for (size_t i = 0; i < count; i ++) { - func(flecs::entity(world, iter->entities[i]), - (ColumnType< remove_reference_t >(comps, i) - .get_row())...); - } - - ECS_TABLE_UNLOCK(iter->world, iter->table); + func(flecs::entity(iter->world, iter->entities[i]), + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...); } - // Number of function arguments is two more than number of components, pass - // iter + index as argument. + // func(flecs::iter&, size_t row, Components...) template class ColumnType, - typename... Args, int Enabled = PassIter, if_t< - sizeof...(Components) == sizeof...(Args) && Enabled> = 0> + typename... Args, + typename Fn = Func, + decltype(std::declval()( + std::declval(), + std::declval(), + std::declval > >().get_row()...), 0) = 0> static void invoke_callback( - ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) + ecs_iter_t *iter, const Func& func, size_t i, Args... comps) { - size_t count = static_cast(iter->count); - if (count == 0) { - // If query has no This terms, count can be 0. Since each does not - // have an entity parameter, just pass through components - count = 1; - } - flecs::iter it(iter); + func(it, i, (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...); + } - ECS_TABLE_LOCK(iter->world, iter->table); - - for (size_t i = 0; i < count; i ++) { - func(it, i, (ColumnType< remove_reference_t >(comps, i) - .get_row())...); - } - - ECS_TABLE_UNLOCK(iter->world, iter->table); + // func(Components...) + template class ColumnType, + typename... Args, + typename Fn = Func, + decltype(std::declval()( + std::declval > >().get_row()...), 0) = 0> + static void invoke_callback( + ecs_iter_t *iter, const Func& func, size_t i, Args... comps) + { + func((ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...); } - // Number of function arguments is equal to number of components, no entity template class ColumnType, typename... Args, if_t< - sizeof...(Components) == sizeof...(Args) && !PassEntity && !PassIter> = 0> - static void invoke_callback( + sizeof...(Components) == sizeof...(Args)> = 0> + static void invoke_unpack( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { + ECS_TABLE_LOCK(iter->world, iter->table); + size_t count = static_cast(iter->count); - if (count == 0) { + if (count == 0 && !iter->table) { // If query has no This terms, count can be 0. Since each does not // have an entity parameter, just pass through components count = 1; } - flecs::iter it(iter); - - ECS_TABLE_LOCK(iter->world, iter->table); - for (size_t i = 0; i < count; i ++) { - func( (ColumnType< remove_reference_t >(comps, i) - .get_row())...); + invoke_callback(iter, func, i, comps...); } ECS_TABLE_UNLOCK(iter->world, iter->table); @@ -333,64 +362,55 @@ struct each_delegate : public delegate { template class ColumnType, typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0> - static void invoke_callback(ecs_iter_t *iter, const Func& func, + static void invoke_unpack(ecs_iter_t *iter, const Func& func, size_t index, Terms& columns, Args... comps) { - invoke_callback( + invoke_unpack( iter, func, index + 1, columns, comps..., columns[index]); } - Func m_func; +public: + Func func_; }; template struct find_delegate : public delegate { - // If the number of arguments in the function signature is one more than the - // number of components in the query, an extra entity arg is required. - static constexpr bool PassEntity = - (sizeof...(Components) + 1) == (arity::value); - - // If the number of arguments in the function is two more than the number of - // components in the query, extra iter + index arguments are required. - static constexpr bool PassIter = - (sizeof...(Components) + 2) == (arity::value); - - static_assert(arity::value > 0, - "each() must have at least one argument"); - - using Terms = typename term_ptrs::array; + using Terms = typename field_ptrs::array; template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> explicit find_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + : func_(FLECS_MOV(func)) { } explicit find_delegate(const Func& func) noexcept - : m_func(func) { } + : func_(func) { } // Invoke object directly. This operation is useful when the calling // function has just constructed the delegate, such as what happens when // iterating a query. flecs::entity invoke(ecs_iter_t *iter) const { - term_ptrs terms; + field_ptrs terms; - if (terms.populate(iter)) { - return invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms); - } else { - return invoke_callback< each_column >(iter, m_func, 0, terms.m_terms); - } - } + iter->flags |= EcsIterCppEach; - // Find delegates always use instanced iterators - static bool instanced() { - return true; + if (iter->ref_fields | iter->up_fields) { + terms.populate(iter); + return invoke_callback< each_ref_field >(iter, func_, 0, terms.fields_); + } else { + terms.populate_self(iter); + return invoke_callback< each_field >(iter, func_, 0, terms.fields_); + } } private: // Number of function arguments is one more than number of components, pass // entity as argument. - template class ColumnType, - typename... Args, if_t< - sizeof...(Components) == sizeof...(Args) && PassEntity> = 0> + template class ColumnType, + typename... Args, + typename Fn = Func, + if_t = 0, + decltype(bool(std::declval()( + std::declval(), + std::declval > >().get_row()...))) = true> static flecs::entity invoke_callback( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { @@ -405,7 +425,7 @@ struct find_delegate : public delegate { for (size_t i = 0; i < count; i ++) { if (func(flecs::entity(world, iter->entities[i]), - (ColumnType< remove_reference_t >(comps, i) + (ColumnType< remove_reference_t >(iter, comps, i) .get_row())...)) { result = flecs::entity(world, iter->entities[i]); @@ -420,9 +440,14 @@ struct find_delegate : public delegate { // Number of function arguments is two more than number of components, pass // iter + index as argument. - template class ColumnType, - typename... Args, int Enabled = PassIter, if_t< - sizeof...(Components) == sizeof...(Args) && Enabled> = 0> + template class ColumnType, + typename... Args, + typename Fn = Func, + if_t = 0, + decltype(bool(std::declval()( + std::declval(), + std::declval(), + std::declval > >().get_row()...))) = true> static flecs::entity invoke_callback( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { @@ -439,8 +464,9 @@ struct find_delegate : public delegate { ECS_TABLE_LOCK(iter->world, iter->table); for (size_t i = 0; i < count; i ++) { - if (func(it, i, (ColumnType< remove_reference_t >(comps, i) - .get_row())...)) + if (func(it, i, + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...)) { result = flecs::entity(iter->world, iter->entities[i]); break; @@ -453,9 +479,12 @@ struct find_delegate : public delegate { } // Number of function arguments is equal to number of components, no entity - template class ColumnType, - typename... Args, if_t< - sizeof...(Components) == sizeof...(Args) && !PassEntity && !PassIter> = 0> + template class ColumnType, + typename... Args, + typename Fn = Func, + if_t = 0, + decltype(bool(std::declval()( + std::declval > >().get_row()...))) = true> static flecs::entity invoke_callback( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { @@ -472,8 +501,9 @@ struct find_delegate : public delegate { ECS_TABLE_LOCK(iter->world, iter->table); for (size_t i = 0; i < count; i ++) { - if (func( (ColumnType< remove_reference_t >(comps, i) - .get_row())...)) + if (func( + (ColumnType< remove_reference_t >(iter, comps, i) + .get_row())...)) { result = flecs::entity(iter->world, iter->entities[i]); break; @@ -494,91 +524,39 @@ struct find_delegate : public delegate { iter, func, index + 1, columns, comps..., columns[index]); } - Func m_func; + Func func_; }; //////////////////////////////////////////////////////////////////////////////// //// Utility class to invoke a system iterate action //////////////////////////////////////////////////////////////////////////////// -template -struct iter_delegate : delegate { -private: - static constexpr bool IterOnly = arity::value == 1; - - using Terms = typename term_ptrs::array; - -public: +template +struct run_delegate : delegate { template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> - explicit iter_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + explicit run_delegate(Func&& func) noexcept + : func_(FLECS_MOV(func)) { } - explicit iter_delegate(const Func& func) noexcept - : m_func(func) { } + explicit run_delegate(const Func& func) noexcept + : func_(func) { } // Invoke object directly. This operation is useful when the calling // function has just constructed the delegate, such as what happens when // iterating a query. void invoke(ecs_iter_t *iter) const { - term_ptrs terms; - terms.populate(iter); - invoke_callback(iter, m_func, 0, terms.m_terms); + flecs::iter it(iter); + iter->flags &= ~EcsIterIsValid; + func_(it); } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->run_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); self->invoke(iter); } - // Instancing needs to be enabled explicitly for iter delegates - static bool instanced() { - return false; - } - -private: - template = 0> - static void invoke_callback(ecs_iter_t *iter, const Func& func, - size_t, Terms&, Args...) - { - flecs::iter it(iter); - - ECS_TABLE_LOCK(iter->world, iter->table); - - func(it); - - ECS_TABLE_UNLOCK(iter->world, iter->table); - } - - template = 0> - static void invoke_callback(ecs_iter_t *iter, const Func& func, size_t, - Terms&, Targs... comps) - { - flecs::iter it(iter); - - ECS_TABLE_LOCK(iter->world, iter->table); - - func(it, ( static_cast< - remove_reference_t< - remove_pointer_t< - actual_type_t > >* > - (comps.ptr))...); - - ECS_TABLE_UNLOCK(iter->world, iter->table); - } - - template = 0> - static void invoke_callback(ecs_iter_t *iter, const Func& func, - size_t index, Terms& columns, Targs... comps) - { - invoke_callback(iter, func, index + 1, columns, comps..., - columns[index]); - } - - Func m_func; + Func func_; }; @@ -589,34 +567,37 @@ struct iter_delegate : delegate { template struct entity_observer_delegate : delegate { explicit entity_observer_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + : func_(FLECS_MOV(func)) { } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { invoke(iter); } + private: - template ::value == 1> = 0> + template ()(std::declval()), 0) = 0> static void invoke(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); - self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1))); + self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0))); } - template ::value == 0> = 0> + template ()(), 0) = 0> static void invoke(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); - self->m_func(); + self->func_(); } - Func m_func; + Func func_; }; template struct entity_payload_observer_delegate : delegate { explicit entity_payload_observer_delegate(Func&& func) noexcept - : m_func(FLECS_MOV(func)) { } + : func_(FLECS_MOV(func)) { } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { @@ -624,31 +605,36 @@ struct entity_payload_observer_delegate : delegate { } private: - template ::value == 1> = 0> + template ()( + std::declval()), 0) = 0> static void invoke(ecs_iter_t *iter) { auto self = static_cast( - iter->binding_ctx); + iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION, "entity observer invoked without payload"); Event *data = static_cast(iter->param); - self->m_func(*data); + self->func_(*data); } - template ::value == 2> = 0> + template ()( + std::declval(), + std::declval()), 0) = 0> static void invoke(ecs_iter_t *iter) { auto self = static_cast( - iter->binding_ctx); + iter->callback_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION, "entity observer invoked without payload"); Event *data = static_cast(iter->param); - self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1)), *data); + self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0)), *data); } - Func m_func; + Func func_; }; @@ -680,42 +666,55 @@ struct entity_with_delegate_impl> { } static - bool get_ptrs(world_t *world, const ecs_record_t *r, ecs_table_t *table, + bool get_ptrs(world_t *world, flecs::entity_t e, const ecs_record_t *r, ecs_table_t *table, ArrayType& ptrs) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - if (!ecs_table_column_count(table)) { + if (!ecs_table_column_count(table) && + !ecs_table_has_flags(table, EcsTableHasSparse)) + { return false; } /* table_index_of needs real world */ const flecs::world_t *real_world = ecs_get_world(world); + IdArray ids ({ + _::type().id(world)... + }); + /* Get column indices for components */ ColumnArray columns ({ ecs_table_get_column_index(real_world, table, - _::cpp_type().id(world))... + _::type().id(world))... }); /* Get pointers for columns for entity */ size_t i = 0; for (int32_t column : columns) { if (column == -1) { - return false; + /* Component could be sparse */ + void *ptr = ecs_get_mut_id(world, e, ids[i]); + if (!ptr) { + return false; + } + + ptrs[i ++] = ptr; + continue; } - ptrs[i ++] = ecs_record_get_column(r, column, 0); + ptrs[i ++] = ecs_record_get_by_column(r, column, 0); } return true; } - static bool get_mut_ptrs(world_t *world, ecs_entity_t e, ArrayType& ptrs) { - /* Get pointers w/get_mut */ + static bool ensure_ptrs(world_t *world, ecs_entity_t e, ArrayType& ptrs) { + /* Get pointers w/ensure */ size_t i = 0; DummyArray dummy ({ - (ptrs[i ++] = ecs_get_mut_id(world, e, - _::cpp_type().id(world)), 0)... + (ptrs[i ++] = ecs_ensure_id(world, e, + _::type().id(world)), 0)... }); return true; @@ -734,7 +733,7 @@ struct entity_with_delegate_impl> { } ArrayType ptrs; - bool has_components = get_ptrs(world, r, table, ptrs); + bool has_components = get_ptrs(world, e, r, table, ptrs); if (has_components) { invoke_callback(func, 0, ptrs); } @@ -757,7 +756,7 @@ struct entity_with_delegate_impl> { } ArrayType ptrs; - bool has_components = get_ptrs(world, r, table, ptrs); + bool has_components = get_ptrs(world, e, r, table, ptrs); if (has_components) { invoke_callback(func, 0, ptrs); } @@ -790,7 +789,7 @@ struct entity_with_delegate_impl> { } template - static bool invoke_get_mut(world_t *world, entity_t id, const Func& func) { + static bool invoke_ensure(world_t *world, entity_t id, const Func& func) { flecs::world w(world); ArrayType ptrs; @@ -823,6 +822,7 @@ struct entity_with_delegate_impl> { elem = store_added(added, elem, prev, next, w.id()), prev = next, 0 )... }); + (void)dummy_before; // If table is different, move entity straight to it @@ -834,15 +834,15 @@ struct entity_with_delegate_impl> { table = next; } - if (!get_ptrs(w, r, table, ptrs)) { + if (!get_ptrs(w, id, r, table, ptrs)) { ecs_abort(ECS_INTERNAL_ERROR, NULL); } ECS_TABLE_LOCK(world, table); - // When deferred, obtain pointers with regular get_mut + // When deferred, obtain pointers with regular ensure } else { - get_mut_ptrs(world, id, ptrs); + ensure_ptrs(world, id, ptrs); } invoke_callback(func, 0, ptrs); diff --git a/vendors/flecs/include/flecs/addons/cpp/entity.hpp b/vendors/flecs/include/flecs/addons/cpp/entity.hpp index fa429f2e4..258a82441 100644 --- a/vendors/flecs/include/flecs/addons/cpp/entity.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/entity.hpp @@ -1,7 +1,7 @@ /** * @file addons/cpp/entity.hpp * @brief Entity class. - * + * * This class provides read/write access to entities. */ @@ -12,9 +12,9 @@ /** * @defgroup cpp_entities Entities - * @brief Entity operations. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Entity operations. + * * @{ */ @@ -23,8 +23,8 @@ namespace flecs /** Entity. * Class with read/write operations for entities. - * - * \ingroup cpp_entities + * + * @ingroup cpp_entities */ struct entity : entity_builder { @@ -34,11 +34,16 @@ struct entity : entity_builder * * @param world The world in which to create the entity. */ - explicit entity(world_t *world) - : entity_builder() + explicit entity(world_t *world) + : entity_builder() { - m_world = world; - m_id = ecs_new(world, 0); + world_ = world; + if (!ecs_get_scope(world_) && !ecs_get_with(world_)) { + id_ = ecs_new(world); + } else { + ecs_entity_desc_t desc = {}; + id_ = ecs_entity_init(world_, &desc); + } } /** Wrap an existing entity id. @@ -46,9 +51,9 @@ struct entity : entity_builder * @param world The world in which the entity is created. * @param id The entity id. */ - explicit entity(const flecs::world_t *world, flecs::id_t id) { - m_world = const_cast(world); - m_id = id; + explicit entity(const flecs::world_t *world, flecs::entity_t id) { + world_ = const_cast(world); + id_ = id; } /** Create a named entity. @@ -60,25 +65,27 @@ struct entity : entity_builder * @param world The world in which to create the entity. * @param name The entity name. */ - explicit entity(world_t *world, const char *name) + explicit entity(world_t *world, const char *name) : entity_builder() - { - m_world = world; + { + world_ = world; ecs_entity_desc_t desc = {}; desc.name = name; desc.sep = "::"; desc.root_sep = "::"; - m_id = ecs_entity_init(world, &desc); + id_ = ecs_entity_init(world, &desc); } - /** Conversion from flecs::entity_t to flecs::entity. - * + /** Conversion from flecs::entity_t to flecs::entity. + * * @param id The entity_t value to convert. */ - explicit entity(entity_t id) + explicit entity(entity_t id) : entity_builder( nullptr, id ) { } + #ifndef ensure + /** Get mutable component value. * This operation returns a mutable pointer to the component. If the entity * did not yet have the component, it will be added. If a base entity had @@ -89,10 +96,11 @@ struct entity : entity_builder * @return Pointer to the component value. */ template - T* get_mut() const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast(ecs_get_mut_id(m_world, m_id, comp_id)); + T& ensure() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return *static_cast(ecs_ensure_id(world_, id_, comp_id)); } /** Get mutable component value (untyped). @@ -104,8 +112,8 @@ struct entity : entity_builder * @param comp The component to get. * @return Pointer to the component value. */ - void* get_mut(entity_t comp) const { - return ecs_get_mut_id(m_world, m_id, comp); + void* ensure(entity_t comp) const { + return ecs_ensure_id(world_, id_, comp); } /** Get mutable pointer for a pair. @@ -114,12 +122,12 @@ struct entity : entity_builder * @tparam First The first part of the pair. * @tparam Second the second part of the pair. */ - template , + template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> - A* get_mut() const { - return static_cast(ecs_get_mut_id(m_world, m_id, ecs_pair( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world)))); + A& ensure() const { + return *static_cast(ecs_ensure_id(world_, id_, ecs_pair( + _::type::id(world_), + _::type::id(world_)))); } /** Get mutable pointer for the first element of a pair. @@ -129,23 +137,24 @@ struct entity : entity_builder * @param second The second element of the pair. */ template - First* get_mut(entity_t second) const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast( - ecs_get_mut_id(m_world, m_id, ecs_pair(comp_id, second))); + First& ensure(entity_t second) const { + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return *static_cast( + ecs_ensure_id(world_, id_, ecs_pair(first, second))); } /** Get mutable pointer for a pair (untyped). * This operation gets the value for a pair from the entity. If neither the - * first nor second element of the pair is a component, the operation will + * first nor second element of the pair is a component, the operation will * fail. * * @param first The first element of the pair. * @param second The second element of the pair. */ - void* get_mut(entity_t first, entity_t second) const { - return ecs_get_mut_id(m_world, m_id, ecs_pair(first, second)); + void* ensure(entity_t first, entity_t second) const { + return ecs_ensure_id(world_, id_, ecs_pair(first, second)); } /** Get mutable pointer for the second element of a pair. @@ -155,12 +164,19 @@ struct entity : entity_builder * @param first The first element of the pair. */ template - Second* get_mut_second(entity_t first) const { - auto second = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast( - ecs_get_mut_id(m_world, m_id, ecs_pair(first, second))); - } + Second& ensure_second(entity_t first) const { + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return *static_cast( + ecs_ensure_id(world_, id_, ecs_pair(first, second))); + } + + #endif /** Signal that component was modified. * @@ -168,19 +184,24 @@ struct entity : entity_builder */ template void modified() const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); this->modified(comp_id); - } + } /** Signal that the first element of a pair was modified. * * @tparam First The first part of the pair. * @tparam Second the second part of the pair. */ - template + template >> void modified() const { - this->modified(_::cpp_type::id(m_world)); + auto first = _::type::id(world_); + auto second = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + this->modified(first, second); } /** Signal that the first part of a pair was modified. @@ -190,8 +211,9 @@ struct entity : entity_builder */ template void modified(entity_t second) const { - auto first = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); this->modified(first, second); } @@ -211,7 +233,7 @@ struct entity : entity_builder * @param comp component that was modified. */ void modified(entity_t comp) const { - ecs_modified_id(m_world, m_id, comp); + ecs_modified_id(world_, id_, comp); } /** Get reference to component. @@ -221,65 +243,82 @@ struct entity : entity_builder * @tparam T component for which to get a reference. * @return The reference. */ - template + template ::value > = 0> ref get_ref() const { - return ref(m_world, m_id, _::cpp_type::id(m_world)); + return ref(world_, id_, _::type::id(world_)); + } + + /** Get reference to component. + * Overload for when T is not the same as the actual type, which happens + * when using pair types. + * A reference allows for quick and safe access to a component value, and is + * a faster alternative to repeatedly calling 'get' for the same component. + * + * @tparam T component for which to get a reference. + * @return The reference. + */ + template , if_t< flecs::is_pair::value > = 0> + ref get_ref() const { + return ref(world_, id_, + ecs_pair(_::type::id(world_), + _::type::id(world_))); } - template , + + template , typename A = actual_type_t

> ref get_ref() const { - return ref(m_world, m_id, - ecs_pair(_::cpp_type::id(m_world), - _::cpp_type::id(m_world))); + return ref(world_, id_, + ecs_pair(_::type::id(world_), _::type::id(world_))); } template ref get_ref(flecs::entity_t second) const { - return ref(m_world, m_id, - ecs_pair(_::cpp_type::id(m_world), second)); + auto first = _::type::id(world_); + return ref(world_, id_, ecs_pair(first, second)); } template ref get_ref_second(flecs::entity_t first) const { - return ref(m_world, m_id, - ecs_pair(first, _::cpp_type::id(m_world))); - } - - /** Recursively flatten relationship. - * @see ecs_flatten - */ - void flatten(flecs::entity_t r, const ecs_flatten_desc_t *desc = nullptr) { - ecs_flatten(m_world, ecs_pair(r, m_id), desc); + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + return ref(world_, id_, ecs_pair(first, second)); } /** Clear an entity. * This operation removes all components from an entity without recycling * the entity id. + * + * @see ecs_clear() */ void clear() const { - ecs_clear(m_world, m_id); + ecs_clear(world_, id_); } /** Delete an entity. * Entities have to be deleted explicitly, and are not deleted when the * entity object goes out of scope. + * + * @see ecs_delete() */ void destruct() const { - ecs_delete(m_world, m_id); + ecs_delete(world_, id_); } /** Return entity as entity_view. * This returns an entity_view instance for the entity which is a readonly * version of the entity class. - * + * * This is similar to a regular upcast, except that this method ensures that * the entity_view instance is instantiated with a world vs. a stage, which * a regular upcast does not guarantee. */ flecs::entity_view view() const { return flecs::entity_view( - const_cast(ecs_get_world(m_world)), m_id); + const_cast(ecs_get_world(world_)), id_); } /** Entity id 0. @@ -291,7 +330,7 @@ struct entity : entity_builder static flecs::entity null(const flecs::world_t *world) { flecs::entity result; - result.m_world = const_cast(world); + result.world_ = const_cast(world); return result; } diff --git a/vendors/flecs/include/flecs/addons/cpp/entity_view.hpp b/vendors/flecs/include/flecs/addons/cpp/entity_view.hpp index 37f0e096a..1ff2492ff 100644 --- a/vendors/flecs/include/flecs/addons/cpp/entity_view.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/entity_view.hpp @@ -13,7 +13,7 @@ #pragma once /** - * \ingroup cpp_entities + * @ingroup cpp_entities * @{ */ @@ -23,7 +23,7 @@ namespace flecs /** Entity view. * Class with read operations for entities. Base for flecs::entity. * - * \ingroup cpp_entities + * @ingroup cpp_entities */ struct entity_view : public id { @@ -48,7 +48,7 @@ struct entity_view : public id { * @return The integer entity id. */ entity_t id() const { - return m_id; + return id_; } /** Check if entity is valid. @@ -56,7 +56,7 @@ struct entity_view : public id { * @return True if the entity is alive, false otherwise. */ bool is_valid() const { - return m_world && ecs_is_valid(m_world, m_id); + return world_ && ecs_is_valid(world_, id_); } explicit operator bool() const { @@ -68,7 +68,7 @@ struct entity_view : public id { * @return True if the entity is alive, false otherwise. */ bool is_alive() const { - return m_world && ecs_is_alive(m_world, m_id); + return world_ && ecs_is_alive(world_, id_); } /** Return the entity name. @@ -76,7 +76,7 @@ struct entity_view : public id { * @return The entity name. */ flecs::string_view name() const { - return flecs::string_view(ecs_get_name(m_world, m_id)); + return flecs::string_view(ecs_get_name(world_, id_)); } /** Return the entity symbol. @@ -84,7 +84,7 @@ struct entity_view : public id { * @return The entity symbol. */ flecs::string_view symbol() const { - return flecs::string_view(ecs_get_symbol(m_world, m_id)); + return flecs::string_view(ecs_get_symbol(world_, id_)); } /** Return the entity path. @@ -100,7 +100,7 @@ struct entity_view : public id { * @return The relative hierarchical entity path. */ flecs::string path_from(flecs::entity_t parent, const char *sep = "::", const char *init_sep = "::") const { - char *path = ecs_get_path_w_sep(m_world, parent, m_id, sep, init_sep); + char *path = ecs_get_path_w_sep(world_, parent, id_, sep, init_sep); return flecs::string(path); } @@ -110,11 +110,11 @@ struct entity_view : public id { */ template flecs::string path_from(const char *sep = "::", const char *init_sep = "::") const { - return path_from(_::cpp_type::id(m_world), sep, init_sep); + return path_from(_::type::id(world_), sep, init_sep); } bool enabled() const { - return !ecs_has_id(m_world, m_id, flecs::Disabled); + return !ecs_has_id(world_, id_, flecs::Disabled); } /** Get the entity's type. @@ -140,7 +140,10 @@ struct entity_view : public id { /** Iterate (component) ids of an entity. * The function parameter must match the following signature: - * void(*)(flecs::id id) + * + * @code + * void(*)(flecs::id id) + * @endcode * * @param func The function invoked for each id. */ @@ -149,7 +152,10 @@ struct entity_view : public id { /** Iterate matching pair ids of an entity. * The function parameter must match the following signature: - * void(*)(flecs::id id) + * + * @code + * void(*)(flecs::id id) + * @endcode * * @param func The function invoked for each id. */ @@ -158,7 +164,10 @@ struct entity_view : public id { /** Iterate targets for a given relationship. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * @param rel The relationship for which to iterate the targets. * @param func The function invoked for each target. @@ -168,19 +177,25 @@ struct entity_view : public id { /** Iterate targets for a given relationship. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * @tparam First The relationship for which to iterate the targets. * @param func The function invoked for each target. */ template void each(const Func& func) const { - return each(_::cpp_type::id(m_world), func); + return each(_::type::id(world_), func); } /** Iterate children for entity. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * @param rel The relationship to follow. * @param func The function invoked for each child. @@ -190,50 +205,40 @@ struct entity_view : public id { /* When the entity is a wildcard, this would attempt to query for all * entities with (ChildOf, *) or (ChildOf, _) instead of querying for * the children of the wildcard entity. */ - if (m_id == flecs::Wildcard || m_id == flecs::Any) { + if (id_ == flecs::Wildcard || id_ == flecs::Any) { /* This is correct, wildcard entities don't have children */ return; } - flecs::world world(m_world); - - ecs_term_t terms[2]; - ecs_filter_t f = ECS_FILTER_INIT; - f.terms = terms; - f.term_count = 2; - - ecs_filter_desc_t desc = {}; - desc.terms[0].first.id = rel; - desc.terms[0].second.id = m_id; - desc.terms[0].second.flags = EcsIsEntity; - desc.terms[1].id = flecs::Prefab; - desc.terms[1].oper = EcsOptional; - desc.storage = &f; - if (ecs_filter_init(m_world, &desc) != nullptr) { - ecs_iter_t it = ecs_filter_iter(m_world, &f); - while (ecs_filter_next(&it)) { - _::each_delegate(FLECS_MOV(func)).invoke(&it); - } - - ecs_filter_fini(&f); + flecs::world world(world_); + + ecs_iter_t it = ecs_each_id(world_, ecs_pair(rel, id_)); + while (ecs_each_next(&it)) { + _::each_delegate(FLECS_MOV(func)).invoke(&it); } } /** Iterate children for entity. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * @tparam Rel The relationship to follow. * @param func The function invoked for each child. */ template void children(Func&& func) const { - children(_::cpp_type::id(m_world), FLECS_MOV(func)); + children(_::type::id(world_), FLECS_MOV(func)); } /** Iterate children for entity. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * This operation follows the ChildOf relationship. * @@ -252,9 +257,10 @@ struct entity_view : public id { */ template ::value > = 0> const T* get() const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast(ecs_get_id(m_world, m_id, comp_id)); + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_id(world_, id_, comp_id)); } /** Get component value. @@ -268,9 +274,10 @@ struct entity_view : public id { template , if_t< flecs::is_pair::value > = 0> const A* get() const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast(ecs_get_id(m_world, m_id, comp_id)); + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_id(world_, id_, comp_id)); } /** Get a pair. @@ -293,10 +300,11 @@ struct entity_view : public id { */ template::value> = 0> const First* get(Second second) const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); return static_cast( - ecs_get_id(m_world, m_id, ecs_pair(comp_id, second))); + ecs_get_id(world_, id_, ecs_pair(first, second))); } /** Get a pair. @@ -307,7 +315,7 @@ struct entity_view : public id { */ template::value> = 0> const First* get(Second constant) const { - const auto& et = enum_type(this->m_world); + const auto& et = enum_type(this->world_); flecs::entity_t target = et.entity(constant); return get(target); } @@ -319,7 +327,7 @@ struct entity_view : public id { * have the component. */ const void* get(flecs::id_t comp) const { - return ecs_get_id(m_world, m_id, comp); + return ecs_get_id(world_, id_, comp); } /** Get a pair (untyped). @@ -331,7 +339,7 @@ struct entity_view : public id { * @param second The second element of the pair. */ const void* get(flecs::entity_t first, flecs::entity_t second) const { - return ecs_get_id(m_world, m_id, ecs_pair(first, second)); + return ecs_get_id(world_, id_, ecs_pair(first, second)); } /** Get 1..N components. @@ -354,13 +362,16 @@ struct entity_view : public id { * function will write-lock the table (see ecs_write_begin). * * Example: - * e.get([](Position& p, Velocity& v) { // write lock - * p.x += v.x; - * }); + * + * @code + * e.get([](Position& p, Velocity& v) { // write lock + * p.x += v.x; + * }); * - * e.get([](const Position& p) { // read lock - * std::cout << p.x << std::endl; - * }); + * e.get([](const Position& p) { // read lock + * std::cout << p.x << std::endl; + * }); + * @endcode * * @param func The callback to invoke. * @return True if the entity has all components, false if not. @@ -385,10 +396,15 @@ struct entity_view : public id { */ template const Second* get_second(flecs::entity_t first) const { - auto second = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); return static_cast( - ecs_get_id(m_world, m_id, ecs_pair(first, second))); + ecs_get_id(world_, id_, ecs_pair(first, second))); } /** Get the second part for a pair. @@ -403,6 +419,131 @@ struct entity_view : public id { return get>(); } + /** Get mutable component value. + * + * @tparam T The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + template ::value > = 0> + T* get_mut() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_mut_id(world_, id_, comp_id)); + } + + /** Get mutable component value. + * Overload for when T is not the same as the actual type, which happens + * when using pair types. + * + * @tparam T The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + template , + if_t< flecs::is_pair::value > = 0> + A* get_mut() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_mut_id(world_, id_, comp_id)); + } + + /** Get a mutable pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @tparam Second the second element of a pair. + */ + template , + typename A = actual_type_t

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

(); + } + + /** Get a mutable pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @param second The second element of the pair. + */ + template::value> = 0> + First* get_mut(Second second) const { + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast( + ecs_get_mut_id(world_, id_, ecs_pair(first, second))); + } + + /** Get a mutable pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @param constant the enum constant. + */ + template::value> = 0> + First* get_mut(Second constant) const { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(constant); + return get_mut(target); + } + + /** Get mutable component value (untyped). + * + * @param comp The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + void* get_mut(flecs::id_t comp) const { + return ecs_get_mut_id(world_, id_, comp); + } + + /** Get a mutable pair (untyped). + * This operation gets the value for a pair from the entity. If neither the + * first nor the second part of the pair are components, the operation + * will fail. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + void* get_mut(flecs::entity_t first, flecs::entity_t second) const { + return ecs_get_mut_id(world_, id_, ecs_pair(first, second)); + } + + /** Get the second part for a pair. + * This operation gets the value for a pair from the entity. The first + * part of the pair should not be a component. + * + * @tparam Second the second element of a pair. + * @param first The first part of the pair. + */ + template + Second* get_mut_second(flecs::entity_t first) const { + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast( + ecs_get_mut_id(world_, id_, ecs_pair(first, second))); + } + + /** Get the second part for a pair. + * This operation gets the value for a pair from the entity. The first + * part of the pair should not be a component. + * + * @tparam First The first element of the pair. + * @tparam Second the second element of a pair. + */ + template + Second* get_mut_second() const { + return get_mut>(); + } + /** Get target for a given pair. * This operation returns the target for a given pair. The optional * index can be used to iterate through targets, in case the entity has @@ -433,8 +574,10 @@ struct entity_view : public id { * This operation can be used to lookup, for example, which prefab is providing * a component by specifying the IsA pair: * - * // Is Position provided by the entity or one of its base entities? - * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) + * @code + * // Is Position provided by the entity or one of its base entities? + * ecs_get_target_for_id(world, entity, EcsIsA, ecs_id(Position)) + * @endcode * * @param relationship The relationship to follow. * @param id The id to lookup. @@ -454,7 +597,7 @@ struct entity_view : public id { * @return The depth. */ int32_t depth(flecs::entity_t rel) const { - return ecs_get_depth(m_world, m_id, rel); + return ecs_get_depth(world_, id_, rel); } /** Get depth for given relationship. @@ -464,7 +607,7 @@ struct entity_view : public id { */ template int32_t depth() const { - return this->depth(_::cpp_type::id(m_world)); + return this->depth(_::type::id(world_)); } /** Get parent of entity. @@ -490,7 +633,7 @@ struct entity_view : public id { * @return True if the entity has the provided entity, false otherwise. */ bool has(flecs::id_t e) const { - return ecs_has_id(m_world, m_id, e); + return ecs_has_id(world_, id_, e); } /** Check if entity has the provided component. @@ -500,14 +643,14 @@ struct entity_view : public id { */ template bool has() const { - flecs::id_t cid = _::cpp_type::id(m_world); - bool result = ecs_has_id(m_world, m_id, cid); + flecs::id_t cid = _::type::id(world_); + bool result = ecs_has_id(world_, id_, cid); if (result) { return result; } if (is_enum::value) { - return ecs_has_pair(m_world, m_id, cid, flecs::Wildcard); + return ecs_has_pair(world_, id_, cid, flecs::Wildcard); } return false; @@ -521,9 +664,12 @@ struct entity_view : public id { */ template ::value > = 0> bool has(E value) const { - auto r = _::cpp_type::id(m_world); - auto o = enum_type(m_world).entity(value); - return ecs_has_pair(m_world, m_id, r, o); + auto r = _::type::id(world_); + auto o = enum_type(world_).entity(value); + ecs_assert(o, ECS_INVALID_PARAMETER, + "Constant was not found in Enum reflection data." + " Did you mean to use has() instead of has(E)?"); + return ecs_has_pair(world_, id_, r, o); } /** Check if entity has the provided pair. @@ -534,7 +680,7 @@ struct entity_view : public id { */ template bool has() const { - return this->has(_::cpp_type::id(m_world)); + return this->has(_::type::id(world_)); } /** Check if entity has the provided pair. @@ -545,8 +691,8 @@ struct entity_view : public id { */ template::value > = 0> bool has(Second second) const { - auto comp_id = _::cpp_type::id(m_world); - return ecs_has_id(m_world, m_id, ecs_pair(comp_id, second)); + auto comp_id = _::type::id(world_); + return ecs_has_id(world_, id_, ecs_pair(comp_id, second)); } /** Check if entity has the provided pair. @@ -557,7 +703,7 @@ struct entity_view : public id { */ template bool has_second(flecs::entity_t first) const { - return this->has(first, _::cpp_type::id(m_world)); + return this->has(first, _::type::id(world_)); } /** Check if entity has the provided pair. @@ -568,7 +714,7 @@ struct entity_view : public id { */ template::value > = 0> bool has(E value) const { - const auto& et = enum_type(this->m_world); + const auto& et = enum_type(this->world_); flecs::entity_t second = et.entity(value); return has(second); } @@ -580,7 +726,7 @@ struct entity_view : public id { * @return True if the entity has the provided component, false otherwise. */ bool has(flecs::id_t first, flecs::id_t second) const { - return ecs_has_id(m_world, m_id, ecs_pair(first, second)); + return ecs_has_id(world_, id_, ecs_pair(first, second)); } /** Check if entity owns the provided entity. @@ -590,7 +736,7 @@ struct entity_view : public id { * @return True if the entity owns the provided entity, false otherwise. */ bool owns(flecs::id_t e) const { - return ecs_owns_id(m_world, m_id, e); + return ecs_owns_id(world_, id_, e); } /** Check if entity owns the provided pair. @@ -601,7 +747,7 @@ struct entity_view : public id { */ template bool owns(flecs::id_t second) const { - auto comp_id = _::cpp_type::id(m_world); + auto comp_id = _::type::id(world_); return owns(ecs_pair(comp_id, second)); } @@ -623,7 +769,7 @@ struct entity_view : public id { */ template bool owns() const { - return owns(_::cpp_type::id(m_world)); + return owns(_::type::id(world_)); } /** Check if entity owns the provided pair. @@ -636,8 +782,8 @@ struct entity_view : public id { template bool owns() const { return owns( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world)); + _::type::id(world_), + _::type::id(world_)); } /** Test if id is enabled. @@ -646,7 +792,7 @@ struct entity_view : public id { * @return True if enabled, false if not. */ bool enabled(flecs::id_t id) const { - return ecs_is_enabled_id(m_world, m_id, id); + return ecs_is_enabled_id(world_, id_, id); } /** Test if component is enabled. @@ -656,7 +802,7 @@ struct entity_view : public id { */ template bool enabled() const { - return this->enabled(_::cpp_type::id(m_world)); + return this->enabled(_::type::id(world_)); } /** Test if pair is enabled. @@ -677,7 +823,7 @@ struct entity_view : public id { */ template bool enabled(flecs::id_t second) const { - return this->enabled(_::cpp_type::id(m_world), second); + return this->enabled(_::type::id(world_), second); } /** Test if pair is enabled. @@ -688,7 +834,7 @@ struct entity_view : public id { */ template bool enabled() const { - return this->enabled(_::cpp_type::id(m_world)); + return this->enabled(_::type::id(world_)); } flecs::entity clone(bool clone_value = true, flecs::entity_t dst_id = 0) const; diff --git a/vendors/flecs/include/flecs/addons/cpp/field.hpp b/vendors/flecs/include/flecs/addons/cpp/field.hpp new file mode 100644 index 000000000..7248f1427 --- /dev/null +++ b/vendors/flecs/include/flecs/addons/cpp/field.hpp @@ -0,0 +1,113 @@ + +/** + * @file addons/cpp/field.hpp + * @brief Wrapper classes for fields returned by flecs::iter. + */ + +#pragma once + +/** + * @defgroup cpp_field Fields + * @ingroup cpp_core + * Field helper types. + * + * @{ + */ + +namespace flecs +{ + +/** Unsafe wrapper class around a field. + * This class can be used when a system does not know the type of a field at + * compile time. + * + * @ingroup cpp_iterator + */ +struct untyped_field { + untyped_field(void* array, size_t size, size_t count, bool is_shared = false) + : data_(array) + , size_(size) + , count_(count) + , is_shared_(is_shared) {} + + /** Return element in component array. + * This operator may only be used if the field is not shared. + * + * @param index Index of element. + * @return Reference to element. + */ + void* operator[](size_t index) const { + ecs_assert(!is_shared_, ECS_INVALID_PARAMETER, + "invalid usage of [] operator for shared component field"); + ecs_assert(index < count_, ECS_COLUMN_INDEX_OUT_OF_RANGE, + "index %d out of range for field", index); + return ECS_OFFSET(data_, size_ * index); + } + +protected: + void* data_; + size_t size_; + size_t count_; + bool is_shared_; +}; + +/** Wrapper class around a field. + * + * @tparam T component type of the field. + * + * @ingroup cpp_iterator + */ +template +struct field { + static_assert(std::is_empty::value == false, + "invalid type for field, cannot iterate empty type"); + + /** Create field from component array. + * + * @param array Pointer to the component array. + * @param count Number of elements in component array. + * @param is_shared Is the component shared or not. + */ + field(T* array, size_t count, bool is_shared = false) + : data_(array) + , count_(count) + , is_shared_(is_shared) {} + + /** Create field from iterator. + * + * @param iter Iterator object. + * @param field Index of the signature of the query being iterated over. + */ + field(iter &iter, int field); + + /** Return element in component array. + * This operator may only be used if the field is not shared. + * + * @param index Index of element. + * @return Reference to element. + */ + T& operator[](size_t index) const; + + /** Return first element of component array. + * This operator is typically used when the field is shared. + * + * @return Reference to the first element. + */ + T& operator*() const; + + /** Return first element of component array. + * This operator is typically used when the field is shared. + * + * @return Pointer to the first element. + */ + T* operator->() const; + +protected: + T* data_; + size_t count_; + bool is_shared_; +}; + +} // namespace flecs + +/** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/flecs.hpp b/vendors/flecs/include/flecs/addons/cpp/flecs.hpp index 129e80bdf..8d7c542e8 100644 --- a/vendors/flecs/include/flecs/addons/cpp/flecs.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/flecs.hpp @@ -13,7 +13,7 @@ * @{ */ -namespace flecs +namespace flecs { struct world; @@ -29,10 +29,13 @@ struct untyped_component; template struct component; -namespace _ +template +struct ref; + +namespace _ { template -struct cpp_type; +struct type; template struct each_delegate; @@ -49,9 +52,8 @@ struct each_delegate; // Mixin forward declarations #include "mixins/id/decl.hpp" #include "mixins/term/decl.hpp" -#include "mixins/filter/decl.hpp" -#include "mixins/event/decl.hpp" #include "mixins/query/decl.hpp" +#include "mixins/event/decl.hpp" #include "mixins/observer/decl.hpp" #ifdef FLECS_SYSTEM #include "mixins/system/decl.hpp" @@ -62,26 +64,20 @@ struct each_delegate; #ifdef FLECS_TIMER #include "mixins/timer/decl.hpp" #endif -#ifdef FLECS_SNAPSHOT -#include "mixins/snapshot/decl.hpp" -#endif #ifdef FLECS_DOC #include "mixins/doc/decl.hpp" #endif #ifdef FLECS_REST #include "mixins/rest/decl.hpp" #endif -#ifdef FLECS_RULES -#include "mixins/rule/decl.hpp" -#endif #ifdef FLECS_META #include "mixins/meta/decl.hpp" #endif #ifdef FLECS_UNITS #include "mixins/units/decl.hpp" #endif -#ifdef FLECS_MONITOR -#include "mixins/monitor/decl.hpp" +#ifdef FLECS_STATS +#include "mixins/stats/decl.hpp" #endif #ifdef FLECS_METRICS #include "mixins/metrics/decl.hpp" @@ -95,26 +91,29 @@ struct each_delegate; #ifdef FLECS_APP #include "mixins/app/decl.hpp" #endif +#ifdef FLECS_SCRIPT +#include "mixins/script/decl.hpp" +#endif #include "log.hpp" #include "pair.hpp" #include "lifecycle_traits.hpp" -#include "ref.hpp" #include "world.hpp" +#include "field.hpp" #include "iter.hpp" #include "entity.hpp" #include "delegate.hpp" -#include "utils/iterable.hpp" #include "component.hpp" +#include "ref.hpp" #include "type.hpp" #include "table.hpp" +#include "utils/iterable.hpp" // Mixin implementations #include "mixins/id/impl.hpp" #include "mixins/entity/impl.hpp" #include "mixins/component/impl.hpp" #include "mixins/term/impl.hpp" -#include "mixins/filter/impl.hpp" #include "mixins/query/impl.hpp" #include "mixins/observer/impl.hpp" #include "mixins/event/impl.hpp" @@ -131,9 +130,6 @@ struct each_delegate; #ifdef FLECS_TIMER #include "mixins/timer/impl.hpp" #endif -#ifdef FLECS_SNAPSHOT -#include "mixins/snapshot/impl.hpp" -#endif #ifdef FLECS_DOC #include "mixins/doc/impl.hpp" #endif @@ -143,17 +139,14 @@ struct each_delegate; #ifdef FLECS_REST #include "mixins/rest/impl.hpp" #endif -#ifdef FLECS_RULES -#include "mixins/rule/impl.hpp" -#endif #ifdef FLECS_META #include "mixins/meta/impl.hpp" #endif #ifdef FLECS_UNITS #include "mixins/units/impl.hpp" #endif -#ifdef FLECS_MONITOR -#include "mixins/monitor/impl.hpp" +#ifdef FLECS_STATS +#include "mixins/stats/impl.hpp" #endif #ifdef FLECS_METRICS #include "mixins/metrics/impl.hpp" @@ -161,22 +154,26 @@ struct each_delegate; #ifdef FLECS_ALERTS #include "mixins/alerts/impl.hpp" #endif +#ifdef FLECS_SCRIPT +#include "mixins/script/impl.hpp" +#endif +#include "impl/field.hpp" #include "impl/iter.hpp" #include "impl/world.hpp" /** * @defgroup cpp_core Core - * @brief Core ECS functionality (entities, storage, queries) - * + * Core ECS functionality (entities, storage, queries) + * * @{ * @} */ /** * @defgroup cpp_addons Addons - * @brief C++ APIs for addons. - * + * C++ APIs for addons. + * * @{ * @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/impl/field.hpp b/vendors/flecs/include/flecs/addons/cpp/impl/field.hpp new file mode 100644 index 000000000..3957a56a3 --- /dev/null +++ b/vendors/flecs/include/flecs/addons/cpp/impl/field.hpp @@ -0,0 +1,60 @@ +/** + * @file addons/cpp/impl/field.hpp + * @brief Field implementation. + */ + +#pragma once + +namespace flecs +{ + +template +inline field::field(iter &iter, int32_t index) { + *this = iter.field(index); +} + +template +T& field::operator[](size_t index) const { + ecs_assert(data_ != nullptr, ECS_INVALID_OPERATION, + "invalid nullptr dereference of component type %s", + _::type_name()); + ecs_assert(index < count_, ECS_COLUMN_INDEX_OUT_OF_RANGE, + "index %d out of range for array of component type %s", + index, _::type_name()); + ecs_assert(!index || !is_shared_, ECS_INVALID_PARAMETER, + "non-zero index invalid for shared field of component type %s", + _::type_name()); + return data_[index]; +} + +/** Return first element of component array. + * This operator is typically used when the field is shared. + * + * @return Reference to the first element. + */ +template +T& field::operator*() const { + ecs_assert(data_ != nullptr, ECS_INVALID_OPERATION, + "invalid nullptr dereference of component type %s", + _::type_name()); + return *data_; +} + +/** Return first element of component array. + * This operator is typically used when the field is shared. + * + * @return Pointer to the first element. + */ +template +T* field::operator->() const { + ecs_assert(data_ != nullptr, ECS_INVALID_OPERATION, + "invalid nullptr dereference of component type %s", + _::type_name()); + ecs_assert(data_ != nullptr, ECS_INVALID_OPERATION, + "-> operator invalid for array with >1 element of " + "component type %s, use [row] instead", + _::type_name()); + return data_; +} + +} diff --git a/vendors/flecs/include/flecs/addons/cpp/impl/iter.hpp b/vendors/flecs/include/flecs/addons/cpp/impl/iter.hpp index b901099ac..3e0dd50d1 100644 --- a/vendors/flecs/include/flecs/addons/cpp/impl/iter.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/impl/iter.hpp @@ -9,77 +9,113 @@ namespace flecs { inline flecs::entity iter::system() const { - return flecs::entity(m_iter->world, m_iter->system); + return flecs::entity(iter_->world, iter_->system); } inline flecs::entity iter::event() const { - return flecs::entity(m_iter->world, m_iter->event); + return flecs::entity(iter_->world, iter_->event); } inline flecs::id iter::event_id() const { - return flecs::id(m_iter->world, m_iter->event_id); + return flecs::id(iter_->world, iter_->event_id); } inline flecs::world iter::world() const { - return flecs::world(m_iter->world); + return flecs::world(iter_->world); } inline flecs::entity iter::entity(size_t row) const { - ecs_assert(row < static_cast(m_iter->count), + ecs_assert(row < static_cast(iter_->count), ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - return flecs::entity(m_iter->world, m_iter->entities[row]); + return flecs::entity(iter_->world, iter_->entities[row]); } -template -inline column::column(iter &iter, int32_t index) { - *this = iter.field(index); +inline flecs::entity iter::src(int8_t index) const { + return flecs::entity(iter_->world, ecs_field_src(iter_, index)); } -inline flecs::entity iter::src(int32_t index) const { - return flecs::entity(m_iter->world, ecs_field_src(m_iter, index)); +inline flecs::id iter::id(int8_t index) const { + return flecs::id(iter_->world, ecs_field_id(iter_, index)); } -inline flecs::id iter::id(int32_t index) const { - return flecs::id(m_iter->world, ecs_field_id(m_iter, index)); -} - -inline flecs::id iter::pair(int32_t index) const { - flecs::id_t id = ecs_field_id(m_iter, index); +inline flecs::id iter::pair(int8_t index) const { + flecs::id_t id = ecs_field_id(iter_, index); ecs_check(ECS_HAS_ID_FLAG(id, PAIR), ECS_INVALID_PARAMETER, NULL); - return flecs::id(m_iter->world, id); + return flecs::id(iter_->world, id); error: return flecs::id(); } inline flecs::type iter::type() const { - return flecs::type(m_iter->world, ecs_table_get_type(m_iter->table)); + return flecs::type(iter_->world, ecs_table_get_type(iter_->table)); } inline flecs::table iter::table() const { - return flecs::table(m_iter->real_world, m_iter->table); + return flecs::table(iter_->real_world, iter_->table); +} + +inline flecs::table iter::other_table() const { + return flecs::table(iter_->real_world, iter_->other_table); } inline flecs::table_range iter::range() const { - return flecs::table_range(m_iter->real_world, m_iter->table, - m_iter->offset, m_iter->count); + return flecs::table_range(iter_->real_world, iter_->table, + iter_->offset, iter_->count); +} + +template ::value, void>::type*> +inline flecs::field iter::field(int8_t index) const { + ecs_assert(!(iter_->flags & EcsIterCppEach), ECS_INVALID_OPERATION, + "cannot .field from .each, use .field_at(%d, row) instead", + _::type_name(), index); + return get_field(index); +} + +template ::value == false, void>::type*> +inline flecs::field iter::field(int8_t index) const { + ecs_assert(!(iter_->flags & EcsIterCppEach), ECS_INVALID_OPERATION, + "cannot .field from .each, use .field_at<%s>(%d, row) instead", + _::type_name(), index); + ecs_assert(!ecs_field_is_readonly(iter_, index), + ECS_ACCESS_VIOLATION, NULL); + return get_field(index); } -#ifdef FLECS_RULES inline flecs::entity iter::get_var(int var_id) const { ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, 0); - return flecs::entity(m_iter->world, ecs_iter_get_var(m_iter, var_id)); + return flecs::entity(iter_->world, ecs_iter_get_var(iter_, var_id)); } /** Get value of variable by name. * Get value of a query variable for current result. */ inline flecs::entity iter::get_var(const char *name) const { - ecs_rule_iter_t *rit = &m_iter->priv.iter.rule; - const flecs::rule_t *r = rit->rule; - int var_id = ecs_rule_find_var(r, name); + ecs_query_iter_t *qit = &iter_->priv_.iter.query; + const flecs::query_t *q = qit->query; + int var_id = ecs_query_find_var(q, name); ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); - return flecs::entity(m_iter->world, ecs_iter_get_var(m_iter, var_id)); + return flecs::entity(iter_->world, ecs_iter_get_var(iter_, var_id)); +} + +template +void iter::targets(int8_t index, const Func& func) { + ecs_assert(iter_->table != nullptr, ECS_INVALID_OPERATION, NULL); + ecs_assert(index < iter_->field_count, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ecs_field_is_set(iter_, index), ECS_INVALID_PARAMETER, NULL); + const ecs_type_t *table_type = ecs_table_get_type(iter_->table); + const ecs_table_record_t *tr = iter_->trs[index]; + int32_t i = tr->index, end = i + tr->count; + for (; i < end; i ++) { + ecs_id_t id = table_type->array[i]; + ecs_assert(ECS_IS_PAIR(id), ECS_INVALID_PARAMETER, + "field does not match a pair"); + flecs::entity tgt(iter_->world, + ecs_pair_second(iter_->real_world, id)); + func(tgt); + } } -#endif } // namespace flecs diff --git a/vendors/flecs/include/flecs/addons/cpp/impl/world.hpp b/vendors/flecs/include/flecs/addons/cpp/impl/world.hpp index fede4fa93..18a53751d 100644 --- a/vendors/flecs/include/flecs/addons/cpp/impl/world.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/impl/world.hpp @@ -9,6 +9,10 @@ namespace flecs { inline void world::init_builtin_components() { + this->component(); + this->component(); + this->component(); + # ifdef FLECS_SYSTEM _::system_init(*this); # endif @@ -28,22 +32,22 @@ inline void world::init_builtin_components() { template inline flecs::entity world::use(const char *alias) const { - entity_t e = _::cpp_type::id(m_world); + entity_t e = _::type::id(world_); const char *name = alias; if (!name) { // If no name is defined, use the entity name without the scope - name = ecs_get_name(m_world, e); + name = ecs_get_name(world_, e); } - ecs_set_alias(m_world, e, name); - return flecs::entity(m_world, e); + ecs_set_alias(world_, e, name); + return flecs::entity(world_, e); } inline flecs::entity world::use(const char *name, const char *alias) const { - entity_t e = ecs_lookup_path_w_sep(m_world, 0, name, "::", "::", true); + entity_t e = ecs_lookup_path_w_sep(world_, 0, name, "::", "::", true); ecs_assert(e != 0, ECS_INVALID_PARAMETER, NULL); - ecs_set_alias(m_world, e, alias); - return flecs::entity(m_world, e); + ecs_set_alias(world_, e, alias); + return flecs::entity(world_, e); } inline void world::use(flecs::entity e, const char *alias) const { @@ -51,143 +55,163 @@ inline void world::use(flecs::entity e, const char *alias) const { const char *name = alias; if (!name) { // If no name is defined, use the entity name without the scope - name = ecs_get_name(m_world, eid); + name = ecs_get_name(world_, eid); } - ecs_set_alias(m_world, eid, name); + ecs_set_alias(world_, eid, name); } inline flecs::entity world::set_scope(const flecs::entity_t s) const { - return flecs::entity(ecs_set_scope(m_world, s)); + return flecs::entity(ecs_set_scope(world_, s)); } inline flecs::entity world::get_scope() const { - return flecs::entity(m_world, ecs_get_scope(m_world)); + return flecs::entity(world_, ecs_get_scope(world_)); } template inline flecs::entity world::set_scope() const { - return set_scope( _::cpp_type::id(m_world) ); + return set_scope( _::type::id(world_) ); } -inline entity world::lookup(const char *name, bool search_path) const { - auto e = ecs_lookup_path_w_sep(m_world, 0, name, "::", "::", search_path); +inline entity world::lookup(const char *name, const char *sep, const char *root_sep, bool recursive) const { + auto e = ecs_lookup_path_w_sep(world_, 0, name, sep, root_sep, recursive); return flecs::entity(*this, e); } +#ifndef ensure template -inline T* world::get_mut() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); - return e.get_mut(); +inline T& world::ensure() const { + flecs::entity e(world_, _::type::id(world_)); + return e.ensure(); } +#endif template inline void world::modified() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.modified(); } template inline void world::set(Second second, const First& value) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.set(second, value); } template inline void world::set(Second second, First&& value) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.set(second, value); } template inline ref world::get_ref() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.get_ref(); } template inline const T* world::get() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.get(); } template const A* world::get() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.get(); } template const First* world::get(Second second) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.get(second); } +template +T* world::get_mut() const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_mut(); +} + +template +A* world::get_mut() const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_mut(); +} + +template +First* world::get_mut(Second second) const { + flecs::entity e(world_, _::type::id(world_)); + return e.get_mut(second); +} + template inline bool world::has() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.has(); } template inline bool world::has() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.has(); } template inline bool world::has(flecs::id_t second) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); return e.has(second); } inline bool world::has(flecs::id_t first, flecs::id_t second) const { - flecs::entity e(m_world, first); + flecs::entity e(world_, first); return e.has(first, second); } template inline void world::add() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.add(); } template inline void world::add() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.add(); } template inline void world::add(flecs::entity_t second) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.add(second); } inline void world::add(flecs::entity_t first, flecs::entity_t second) const { - flecs::entity e(m_world, first); + flecs::entity e(world_, first); e.add(first, second); } template inline void world::remove() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.remove(); } template inline void world::remove() const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.remove(); } template inline void world::remove(flecs::entity_t second) const { - flecs::entity e(m_world, _::cpp_type::id(m_world)); + flecs::entity e(world_, _::type::id(world_)); e.remove(second); } inline void world::remove(flecs::entity_t first, flecs::entity_t second) const { - flecs::entity e(m_world, first); + flecs::entity e(world_, first); e.remove(first, second); } @@ -198,14 +222,14 @@ inline void world::children(Func&& f) const { template inline flecs::entity world::singleton() const { - return flecs::entity(m_world, _::cpp_type::id(m_world)); + return flecs::entity(world_, _::type::id(world_)); } template inline flecs::entity world::target(int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, _::cpp_type::id(m_world), _::cpp_type::id(m_world), index)); + return flecs::entity(world_, + ecs_get_target(world_, _::type::id(world_), _::type::id(world_), index)); } template @@ -213,44 +237,41 @@ inline flecs::entity world::target( flecs::entity_t relationship, int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, _::cpp_type::id(m_world), relationship, index)); + return flecs::entity(world_, + ecs_get_target(world_, _::type::id(world_), relationship, index)); } inline flecs::entity world::target( flecs::entity_t relationship, int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, relationship, relationship, index)); + return flecs::entity(world_, + ecs_get_target(world_, relationship, relationship, index)); } template ::value > > inline void world::get(const Func& func) const { static_assert(arity::value == 1, "singleton component must be the only argument"); _::entity_with_delegate::invoke_get( - this->m_world, this->singleton>(), func); + this->world_, this->singleton>(), func); } template ::value > > inline void world::set(const Func& func) const { static_assert(arity::value == 1, "singleton component must be the only argument"); - _::entity_with_delegate::invoke_get_mut( - this->m_world, this->singleton>(), func); + _::entity_with_delegate::invoke_ensure( + this->world_, this->singleton>(), func); } inline flecs::entity world::get_alive(flecs::entity_t e) const { - e = ecs_get_alive(m_world, e); - return flecs::entity(m_world, e); + e = ecs_get_alive(world_, e); + return flecs::entity(world_, e); } -/* Prevent clashing with Unreal define. Unreal applications will have to use - * ecs_ensure. */ -#ifndef ensure -inline flecs::entity world::ensure(flecs::entity_t e) const { - ecs_ensure(m_world, e); - return flecs::entity(m_world, e); + +inline flecs::entity world::make_alive(flecs::entity_t e) const { + ecs_make_alive(world_, e); + return flecs::entity(world_, e); } -#endif template inline flecs::entity enum_data::entity() const { @@ -258,26 +279,44 @@ inline flecs::entity enum_data::entity() const { } template -inline flecs::entity enum_data::entity(int value) const { - return flecs::entity(world_, impl_.constants[value].id); +inline flecs::entity enum_data::entity(underlying_type_t value) const { + int index = index_by_value(value); + if (index >= 0) { + return flecs::entity(world_, impl_.constants[index].id); + } +#ifdef FLECS_META + // Reflection data lookup failed. Try value lookup amongst flecs::Constant relationships + flecs::world world = flecs::world(world_); + return world.query_builder() + .with(flecs::ChildOf, world.id()) + .with(flecs::Constant, world.id()) + .build() + .find([value](flecs::entity constant) { + const int32_t *constant_value = constant.get_second(flecs::Constant); + ecs_assert(constant_value, ECS_INTERNAL_ERROR, NULL); + return value == static_cast>(*constant_value); + }); +#else + return flecs::entity::null(world_); +#endif } template inline flecs::entity enum_data::entity(E value) const { - return flecs::entity(world_, impl_.constants[static_cast(value)].id); + return entity(static_cast>(value)); } /** Use provided scope for operations ran on returned world. * Operations need to be ran in a single statement. */ inline flecs::scoped_world world::scope(id_t parent) const { - return scoped_world(m_world, parent); + return scoped_world(world_, parent); } template inline flecs::scoped_world world::scope() const { - flecs::id_t parent = _::cpp_type::id(m_world); - return scoped_world(m_world, parent); + flecs::id_t parent = _::type::id(world_); + return scoped_world(world_, parent); } inline flecs::scoped_world world::scope(const char* name) const { diff --git a/vendors/flecs/include/flecs/addons/cpp/iter.hpp b/vendors/flecs/include/flecs/addons/cpp/iter.hpp index 4c1155f96..a9d45bd9b 100644 --- a/vendors/flecs/include/flecs/addons/cpp/iter.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/iter.hpp @@ -7,116 +7,15 @@ /** * @defgroup cpp_iterator Iterators - * @brief Iterator operations. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Iterator operations. + * * @{ */ -namespace flecs +namespace flecs { -/** Unsafe wrapper class around a column. - * This class can be used when a system does not know the type of a column at - * compile time. - * - * \ingroup cpp_iterator - */ -struct untyped_column { - untyped_column(void* array, size_t size, size_t count, bool is_shared = false) - : m_array(array) - , m_size(size) - , m_count(count) - , m_is_shared(is_shared) {} - - /** Return element in component array. - * This operator may only be used if the column is not shared. - * - * @param index Index of element. - * @return Reference to element. - */ - void* operator[](size_t index) const { - ecs_assert(index < m_count, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - ecs_assert(!m_is_shared, ECS_INVALID_PARAMETER, NULL); - return ECS_OFFSET(m_array, m_size * index); - } - -protected: - void* m_array; - size_t m_size; - size_t m_count; - bool m_is_shared; -}; - -/** Wrapper class around a column. - * - * @tparam T component type of the column. - * - * \ingroup cpp_iterator - */ -template -struct column { - static_assert(std::is_empty::value == false, - "invalid type for column, cannot iterate empty type"); - - /** Create column from component array. - * - * @param array Pointer to the component array. - * @param count Number of elements in component array. - * @param is_shared Is the component shared or not. - */ - column(T* array, size_t count, bool is_shared = false) - : m_array(array) - , m_count(count) - , m_is_shared(is_shared) {} - - /** Create column from iterator. - * - * @param iter Iterator object. - * @param column Index of the signature of the query being iterated over. - */ - column(iter &iter, int column); - - /** Return element in component array. - * This operator may only be used if the column is not shared. - * - * @param index Index of element. - * @return Reference to element. - */ - T& operator[](size_t index) const { - ecs_assert(index < m_count, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - ecs_assert(!index || !m_is_shared, ECS_INVALID_PARAMETER, NULL); - ecs_assert(m_array != nullptr, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - return m_array[index]; - } - - /** Return first element of component array. - * This operator is typically used when the column is shared. - * - * @return Reference to the first element. - */ - T& operator*() const { - ecs_assert(m_array != nullptr, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - return *m_array; - } - - /** Return first element of component array. - * This operator is typically used when the column is shared. - * - * @return Pointer to the first element. - */ - T* operator->() const { - ecs_assert(m_array != nullptr, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - return m_array; - } - -protected: - T* m_array; - size_t m_count; - bool m_is_shared; -}; - - //////////////////////////////////////////////////////////////////////////////// namespace _ { @@ -131,26 +30,26 @@ template struct range_iterator { explicit range_iterator(T value) - : m_value(value){} + : value_(value){} bool operator!=(range_iterator const& other) const { - return m_value != other.m_value; + return value_ != other.value_; } T const& operator*() const { - return m_value; + return value_; } range_iterator& operator++() { - ++m_value; + ++value_; return *this; } private: - T m_value; + T value_; }; } // namespace _ @@ -163,30 +62,27 @@ namespace flecs //////////////////////////////////////////////////////////////////////////////// /** Class for iterating over query results. - * - * \ingroup cpp_iterator + * + * @ingroup cpp_iterator */ struct iter { private: using row_iterator = _::range_iterator; - + public: /** Construct iterator from C iterator object. * This operation is typically not invoked directly by the user. * * @param it Pointer to C iterator. */ - iter(ecs_iter_t *it) : m_iter(it) { - m_begin = 0; - m_end = static_cast(it->count); - } + iter(ecs_iter_t *it) : iter_(it) { } row_iterator begin() const { - return row_iterator(m_begin); + return row_iterator(0); } row_iterator end() const { - return row_iterator(m_end); + return row_iterator(static_cast(iter_->count)); } flecs::entity system() const; @@ -198,56 +94,62 @@ struct iter { flecs::world world() const; const flecs::iter_t* c_ptr() const { - return m_iter; + return iter_; } size_t count() const { - return static_cast(m_iter->count); + ecs_check(iter_->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + return static_cast(iter_->count); + error: + return 0; } ecs_ftime_t delta_time() const { - return m_iter->delta_time; + return iter_->delta_time; } ecs_ftime_t delta_system_time() const { - return m_iter->delta_system_time; + return iter_->delta_system_time; } flecs::type type() const; flecs::table table() const; + flecs::table other_table() const; + flecs::table_range range() const; - /** Access ctx. + /** Access ctx. * ctx contains the context pointer assigned to a system. */ void* ctx() { - return m_iter->ctx; + return iter_->ctx; } - /** Access ctx. + /** Access ctx. * ctx contains the context pointer assigned to a system. */ template T* ctx() { - return static_cast(m_iter->ctx); + return static_cast(iter_->ctx); } - /** Access param. + /** Access param. * param contains the pointer passed to the param argument of system::run */ void* param() { - return m_iter->param; + return iter_->param; } - /** Access param. + /** Access param. * param contains the pointer passed to the param argument of system::run */ template T* param() { /* TODO: type check */ - return static_cast(m_iter->param); + return static_cast(iter_->param); } /** Obtain mutable handle to entity being iterated over. @@ -257,74 +159,80 @@ struct iter { flecs::entity entity(size_t row) const; /** Returns whether field is matched on self. - * + * * @param index The field index. */ - bool is_self(int32_t index) const { - return ecs_field_is_self(m_iter, index); + bool is_self(int8_t index) const { + return ecs_field_is_self(iter_, index); } /** Returns whether field is set. - * + * * @param index The field index. */ - bool is_set(int32_t index) const { - return ecs_field_is_set(m_iter, index); + bool is_set(int8_t index) const { + return ecs_field_is_set(iter_, index); } /** Returns whether field is readonly. * * @param index The field index. */ - bool is_readonly(int32_t index) const { - return ecs_field_is_readonly(m_iter, index); + bool is_readonly(int8_t index) const { + return ecs_field_is_readonly(iter_, index); } - /** Number of fields in iteator. + /** Number of fields in iterator. */ int32_t field_count() const { - return m_iter->field_count; + return iter_->field_count; } /** Size of field data type. * * @param index The field id. */ - size_t size(int32_t index) const { - return ecs_field_size(m_iter, index); + size_t size(int8_t index) const { + return ecs_field_size(iter_, index); } /** Obtain field source (0 if This). * * @param index The field index. - */ - flecs::entity src(int32_t index) const; + */ + flecs::entity src(int8_t index) const; /** Obtain id matched for field. * * @param index The field index. */ - flecs::id id(int32_t index) const; + flecs::id id(int8_t index) const; /** Obtain pair id matched for field. * This operation will fail if the id is not a pair. - * + * * @param index The field index. */ - flecs::id pair(int32_t index) const; + flecs::id pair(int8_t index) const; /** Obtain column index for field. * * @param index The field index. - */ - int32_t column_index(int32_t index) const { - return ecs_field_column_index(m_iter, index); + */ + int32_t column_index(int8_t index) const { + return ecs_field_column(iter_, index); + } + + /** Obtain term that triggered an observer + */ + int8_t term_index() const { + return iter_->term_index; } /** Convert current iterator result to string. */ flecs::string str() const { - char *s = ecs_iter_str(m_iter); + char *s = ecs_iter_str(iter_); return flecs::string(s); } @@ -338,12 +246,10 @@ struct iter { */ template , typename std::enable_if::value, void>::type* = nullptr> - flecs::column field(int32_t index) const { - return get_field(index); - } + flecs::field field(int8_t index) const; /** Get read/write access to field data. - * If the matched id for the specified field does not match with the provided + * If the matched id for the specified field does not match with the provided * type or if the field is readonly, the function will assert. * * @tparam T Type of the field. @@ -353,58 +259,85 @@ struct iter { template , typename std::enable_if< std::is_const::value == false, void>::type* = nullptr> - flecs::column field(int32_t index) const { - ecs_assert(!ecs_field_is_readonly(m_iter, index), - ECS_ACCESS_VIOLATION, NULL); - return get_field(index); - } + flecs::field field(int8_t index) const; /** Get unchecked access to field data. * Unchecked access is required when a system does not know the type of a * field at compile time. * - * @param index The field index. + * @param index The field index. */ - flecs::untyped_column field(int32_t index) const { + flecs::untyped_field field(int8_t index) const { + ecs_assert(!(iter_->flags & EcsIterCppEach), ECS_INVALID_OPERATION, + "cannot .field from .each, use .field_at(%d, row) instead", index); return get_unchecked_field(index); } + /** Get pointer to field at row. */ + void* field_at(int8_t index, size_t row) const { + if (iter_->row_fields & (1llu << index)) { + return get_unchecked_field_at(index, row)[0]; + } else { + return get_unchecked_field(index)[row]; + } + } + + /** Get reference to field at row. */ + template , + typename std::enable_if::value, void>::type* = nullptr> + const A& field_at(int8_t index, size_t row) const { + if (iter_->row_fields & (1llu << index)) { + return get_field_at(index, row)[0]; + } else { + return get_field(index)[row]; + } + } + + /** Get reference to field at row. */ + template , + typename std::enable_if< + std::is_const::value == false, void>::type* = nullptr> + A& field_at(int8_t index, size_t row) const { + ecs_assert(!ecs_field_is_readonly(iter_, index), + ECS_ACCESS_VIOLATION, NULL); + if (iter_->row_fields & (1llu << index)) { + return get_field_at(index, row)[0]; + } else { + return get_field(index)[row]; + } + } + /** Get readonly access to entity ids. * * @return The entity ids. */ - flecs::column entities() const { - return flecs::column(m_iter->entities, static_cast(m_iter->count), false); - } - - /** Obtain the total number of tables the iterator will iterate over. */ - int32_t table_count() const { - return m_iter->table_count; + flecs::field entities() const { + return flecs::field( + iter_->entities, static_cast(iter_->count), false); } /** Check if the current table has changed since the last iteration. * Can only be used when iterating queries and/or systems. */ bool changed() { - return ecs_query_changed(nullptr, m_iter); + return ecs_iter_changed(iter_); } /** Skip current table. * This indicates to the query that the data in the current table is not - * modified. By default, iterating a table with a query will mark the + * modified. By default, iterating a table with a query will mark the * iterated components as dirty if they are annotated with InOut or Out. - * + * * When this operation is invoked, the components of the current table will * not be marked dirty. */ void skip() { - ecs_query_skip(m_iter); + ecs_iter_skip(iter_); } /* Return group id for current table (grouped queries only) */ uint64_t group_id() const { - return m_iter->group_id; + return iter_->group_id; } -#ifdef FLECS_RULES /** Get value of variable by id. * Get value of a query variable for current result. */ @@ -414,22 +347,71 @@ struct iter { * Get value of a query variable for current result. */ flecs::entity get_var(const char *name) const; -#endif + + /** Progress iterator. + * This operation should only be called from a context where the iterator is + * not being progressed automatically. An example of a valid context is + * inside of a run() callback. An example of an invalid context is inside of + * an each() callback. + */ + bool next() { + if (iter_->flags & EcsIterIsValid && iter_->table) { + ECS_TABLE_UNLOCK(iter_->world, iter_->table); + } + bool result = iter_->next(iter_); + iter_->flags |= EcsIterIsValid; + if (result && iter_->table) { + ECS_TABLE_LOCK(iter_->world, iter_->table); + } + return result; + } + + /** Forward to each. + * If a system has an each callback registered, this operation will forward + * the current iterator to the each callback. + */ + void each() { + iter_->callback(iter_); + } + + /** Iterate targets for pair field. + * + * @param index The field index. + * @param func Callback invoked for each target + */ + template + void targets(int8_t index, const Func& func); + + /** Free iterator resources. + * This operation only needs to be called when the iterator is not iterated + * until completion (e.g. the last call to next() did not return false). + * + * Failing to call this operation on an unfinished iterator will throw a + * fatal LEAK_DETECTED error. + * + * @see ecs_iter_fini() + */ + void fini() { + if (iter_->flags & EcsIterIsValid && iter_->table) { + ECS_TABLE_UNLOCK(iter_->world, iter_->table); + } + ecs_iter_fini(iter_); + } private: /* Get field, check if correct type is used */ template > - flecs::column get_field(int32_t index) const { + flecs::field get_field(int8_t index) const { #ifndef FLECS_NDEBUG - ecs_entity_t term_id = ecs_field_id(m_iter, index); + ecs_entity_t term_id = ecs_field_id(iter_, index); ecs_assert(ECS_HAS_ID_FLAG(term_id, PAIR) || - term_id == _::cpp_type::id(m_iter->world), + term_id == _::type::id(iter_->world), ECS_COLUMN_TYPE_MISMATCH, NULL); #endif size_t count; - bool is_shared = !ecs_field_is_self(m_iter, index); + bool is_shared = !ecs_field_is_self(iter_, index); /* If a shared column is retrieved with 'column', there will only be a * single value. Ensure that the application does not accidentally read @@ -439,18 +421,34 @@ struct iter { } else { /* If column is owned, there will be as many values as there are * entities. */ - count = static_cast(m_iter->count); + count = static_cast(iter_->count); } - - return flecs::column( - static_cast(ecs_field_w_size(m_iter, sizeof(A), index)), + + return flecs::field( + static_cast(ecs_field_w_size(iter_, sizeof(A), index)), count, is_shared); } - flecs::untyped_column get_unchecked_field(int32_t index) const { + /* Get field, check if correct type is used */ + template > + flecs::field get_field_at(int8_t index, int32_t row) const { + +#ifndef FLECS_NDEBUG + ecs_entity_t term_id = ecs_field_id(iter_, index); + ecs_assert(ECS_HAS_ID_FLAG(term_id, PAIR) || + term_id == _::type::id(iter_->world), + ECS_COLUMN_TYPE_MISMATCH, NULL); +#endif + + return flecs::field( + static_cast(ecs_field_at_w_size(iter_, sizeof(A), index, row)), + 1, false); + } + + flecs::untyped_field get_unchecked_field(int8_t index) const { size_t count; - size_t size = ecs_field_size(m_iter, index); - bool is_shared = !ecs_field_is_self(m_iter, index); + size_t size = ecs_field_size(iter_, index); + bool is_shared = !ecs_field_is_self(iter_, index); /* If a shared column is retrieved with 'column', there will only be a * single value. Ensure that the application does not accidentally read @@ -460,16 +458,21 @@ struct iter { } else { /* If column is owned, there will be as many values as there are * entities. */ - count = static_cast(m_iter->count); + count = static_cast(iter_->count); } - return flecs::untyped_column( - ecs_field_w_size(m_iter, 0, index), size, count, is_shared); - } + return flecs::untyped_field( + ecs_field_w_size(iter_, 0, index), size, count, is_shared); + } + + flecs::untyped_field get_unchecked_field_at(int8_t index, size_t row) const { + size_t size = ecs_field_size(iter_, index); + return flecs::untyped_field( + ecs_field_at_w_size(iter_, 0, index, static_cast(row)), + size, 1, false); + } - flecs::iter_t *m_iter; - std::size_t m_begin; - std::size_t m_end; + flecs::iter_t *iter_; }; } // namespace flecs diff --git a/vendors/flecs/include/flecs/addons/cpp/lifecycle_traits.hpp b/vendors/flecs/include/flecs/addons/cpp/lifecycle_traits.hpp index e2aec9ea9..1ea94b1fc 100644 --- a/vendors/flecs/include/flecs/addons/cpp/lifecycle_traits.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/lifecycle_traits.hpp @@ -137,7 +137,7 @@ void ctor_move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count, } } -// Move assign + dtor (non-trivial move assigmnment) +// Move assign + dtor (non-trivial move assignment) // Typically used when moving a component to a deleted component template ::value > = 0> @@ -157,7 +157,7 @@ void move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count, } } -// Move assign + dtor (trivial move assigmnment) +// Move assign + dtor (trivial move assignment) // Typically used when moving a component to a deleted component template ::value > = 0> @@ -265,8 +265,6 @@ ecs_move_t move() { // Component types must be move assignable template ::value > = 0> ecs_move_t move() { - flecs_static_assert(always_false::value, - "component type must be move assignable"); return ecs_move_illegal; } @@ -309,8 +307,6 @@ ecs_move_t move_ctor() { // Component types must be move constructible template ::value > = 0> ecs_move_t move_ctor() { - flecs_static_assert(always_false::value, - "component type must be move constructible"); return ecs_move_ctor_illegal; } @@ -335,8 +331,6 @@ template ::value || ! std::is_destructible::value > = 0> ecs_move_t ctor_move_dtor() { - flecs_static_assert(always_false::value, - "component type must be move constructible and destructible"); return ecs_move_ctor_illegal; } @@ -363,8 +357,6 @@ template ::value || ! std::is_destructible::value > = 0> ecs_move_t move_dtor() { - flecs_static_assert(always_false::value, - "component type must be move constructible and destructible"); return ecs_move_ctor_illegal; } diff --git a/vendors/flecs/include/flecs/addons/cpp/log.hpp b/vendors/flecs/include/flecs/addons/cpp/log.hpp index 7d5f65cfe..b9cdf42ce 100644 --- a/vendors/flecs/include/flecs/addons/cpp/log.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/log.hpp @@ -10,9 +10,9 @@ namespace log { /** * @defgroup cpp_log Logging - * @brief Logging functions. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Logging functions. + * * @{ */ @@ -21,7 +21,7 @@ inline void set_level(int level) { ecs_log_set_level(level); } -inline int get_level(void) { +inline int get_level() { return ecs_log_get_level(); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/builder.hpp index 9ec3ce39c..9c525b496 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/builder.hpp @@ -18,7 +18,7 @@ namespace _ { /** Alert builder. * - * \ingroup cpp_addons_alerts + * @ingroup cpp_addons_alerts */ template struct alert_builder final : _::alert_builder_base { @@ -31,7 +31,7 @@ struct alert_builder final : _::alert_builder_base { entity_desc.name = name; entity_desc.sep = "::"; entity_desc.root_sep = "::"; - this->m_desc.entity = ecs_entity_init(world, &entity_desc); + this->desc_.entity = ecs_entity_init(world, &entity_desc); } } }; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/builder_i.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/builder_i.hpp index f7a292675..47d521edf 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/builder_i.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/builder_i.hpp @@ -5,34 +5,34 @@ #pragma once -#include "../filter/builder_i.hpp" +#include "../query/builder_i.hpp" namespace flecs { /** Alert builder interface. * - * \ingroup cpp_addons_alerts + * @ingroup cpp_addons_alerts */ template -struct alert_builder_i : filter_builder_i { +struct alert_builder_i : query_builder_i { private: - using BaseClass = filter_builder_i; - + using BaseClass = query_builder_i; + public: alert_builder_i() : BaseClass(nullptr) - , m_desc(nullptr) { } + , desc_(nullptr) { } alert_builder_i(ecs_alert_desc_t *desc, int32_t term_index = 0) - : BaseClass(&desc->filter, term_index) - , m_desc(desc) { } + : BaseClass(&desc->query, term_index) + , desc_(desc) { } /** Alert message. * * @see ecs_alert_desc_t::message */ Base& message(const char *message) { - m_desc->message = message; + desc_->message = message; return *this; } @@ -41,7 +41,7 @@ struct alert_builder_i : filter_builder_i { * @see ecs_alert_desc_t::brief */ Base& brief(const char *brief) { - m_desc->brief = brief; + desc_->brief = brief; return *this; } @@ -50,7 +50,7 @@ struct alert_builder_i : filter_builder_i { * @see ecs_alert_desc_t::doc_name */ Base& doc_name(const char *doc_name) { - m_desc->doc_name = doc_name; + desc_->doc_name = doc_name; return *this; } @@ -59,7 +59,7 @@ struct alert_builder_i : filter_builder_i { * @see ecs_alert_desc_t::severity */ Base& severity(flecs::entity_t kind) { - m_desc->severity = kind; + desc_->severity = kind; return *this; } @@ -68,7 +68,7 @@ struct alert_builder_i : filter_builder_i { * @see ecs_alert_desc_t::retain_period */ Base& retain_period(ecs_ftime_t period) { - m_desc->retain_period = period; + desc_->retain_period = period; return *this; } @@ -78,16 +78,16 @@ struct alert_builder_i : filter_builder_i { */ template Base& severity() { - return severity(_::cpp_type::id(world_v())); + return severity(_::type::id(world_v())); } /** Add severity filter */ Base& severity_filter(flecs::entity_t kind, flecs::id_t with, const char *var = nullptr) { ecs_assert(severity_filter_count < ECS_ALERT_MAX_SEVERITY_FILTERS, - ECS_INVALID_PARAMETER, "Maxium number of severity filters reached"); + ECS_INVALID_PARAMETER, "Maximum number of severity filters reached"); ecs_alert_severity_filter_t *filter = - &m_desc->severity_filters[severity_filter_count ++]; + &desc_->severity_filters[severity_filter_count ++]; filter->severity = kind; filter->with = with; @@ -98,14 +98,14 @@ struct alert_builder_i : filter_builder_i { /** Add severity filter */ template Base& severity_filter(flecs::id_t with, const char *var = nullptr) { - return severity_filter(_::cpp_type::id(world_v()), with, var); + return severity_filter(_::type::id(world_v()), with, var); } /** Add severity filter */ template ::value > = 0> Base& severity_filter(const char *var = nullptr) { - return severity_filter(_::cpp_type::id(world_v()), - _::cpp_type::id(world_v()), var); + return severity_filter(_::type::id(world_v()), + _::type::id(world_v()), var); } /** Add severity filter */ @@ -113,37 +113,37 @@ struct alert_builder_i : filter_builder_i { Base& severity_filter(T with, const char *var = nullptr) { flecs::world w(world_v()); flecs::entity constant = w.to_entity(with); - return severity_filter(_::cpp_type::id(world_v()), + return severity_filter(_::type::id(world_v()), w.pair(constant), var); } /** Set member to create an alert for out of range values */ Base& member(flecs::entity_t m) { - m_desc->member = m; + desc_->member = m; return *this; } /** Set (component) id for member (optional). If .member() is set and id * is not set, the id will default to the member parent. */ Base& id(flecs::id_t id) { - m_desc->id = id; + desc_->id = id; return *this; } /** Set member to create an alert for out of range values */ template Base& member(const char *m, const char *v = nullptr) { - flecs::entity_t id = _::cpp_type::id(world_v()); + flecs::entity_t id = _::type::id(world_v()); flecs::entity_t mid = ecs_lookup_path_w_sep( world_v(), id, m, "::", "::", false); ecs_assert(m != 0, ECS_INVALID_PARAMETER, NULL); - m_desc->var = v; + desc_->var = v; return this->member(mid); } /** Set source variable for member (optional, defaults to $this) */ Base& var(const char *v) { - m_desc->var = v; + desc_->var = v; return *this; } @@ -155,7 +155,7 @@ struct alert_builder_i : filter_builder_i { return *static_cast(this); } - ecs_alert_desc_t *m_desc; + ecs_alert_desc_t *desc_; int32_t severity_filter_count = 0; }; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/decl.hpp index 3dc82d608..d5b8b5f63 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/decl.hpp @@ -9,9 +9,9 @@ namespace flecs { /** * @defgroup cpp_addons_alerts Alerts - * @brief Alert implementation. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Alert implementation. + * * @{ */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/entity_view.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/entity_view.inl index 5ad20321e..d46832b5b 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/entity_view.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/entity_view.inl @@ -5,9 +5,9 @@ /** Return number of alerts for entity. * - * \memberof flecs::entity_view - * \ingroup cpp_addons_alerts + * @memberof flecs::entity_view + * @ingroup cpp_addons_alerts */ int32_t alert_count(flecs::entity_t alert = 0) const { - return ecs_get_alert_count(m_world, m_id, alert); + return ecs_get_alert_count(world_, id_, alert); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/impl.hpp index 4f1f2d6c7..b01c4dd6c 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/impl.hpp @@ -15,25 +15,25 @@ struct alert final : entity using entity::entity; explicit alert() { - m_id = 0; - m_world = nullptr; + id_ = 0; + world_ = nullptr; } - explicit alert(flecs::world_t *world, ecs_alert_desc_t *desc) - { - m_world = world; - m_id = ecs_alert_init(world, desc); - - if (desc->filter.terms_buffer) { - ecs_os_free(desc->filter.terms_buffer); - } + explicit alert(flecs::world_t *world, ecs_alert_desc_t *desc) { + world_ = world; + id_ = ecs_alert_init(world, desc); } }; inline alerts::alerts(flecs::world& world) { + world.import(); + /* Import C module */ FlecsAlertsImport(world); + world.component(); + world.component(); + world.entity("::flecs::alerts::Alert"); world.entity("::flecs::alerts::Info"); world.entity("::flecs::alerts::Warning"); @@ -42,7 +42,7 @@ inline alerts::alerts(flecs::world& world) { template inline flecs::alert_builder world::alert(Args &&... args) const { - return flecs::alert_builder(m_world, FLECS_FWD(args)...); + return flecs::alert_builder(world_, FLECS_FWD(args)...); } } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/mixin.inl index 72c4a1a4f..d0a0756b1 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/alerts/mixin.inl @@ -1,8 +1,8 @@ /** Create alert. * - * \ingroup cpp_addons_alerts - * \memberof flecs::world + * @ingroup cpp_addons_alerts + * @memberof flecs::world */ template flecs::alert_builder alert(Args &&... args) const; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/app/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/app/builder.hpp index 62951081a..9bcd1fae8 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/app/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/app/builder.hpp @@ -9,80 +9,83 @@ namespace flecs { /** * @defgroup cpp_addons_app App - * @brief Optional addon for running the main application loop. - * \ingroup cpp_addons + * @ingroup cpp_addons + * Optional addon for running the main application loop. + * * @{ */ /** App builder interface */ struct app_builder { app_builder(flecs::world_t *world) - : m_world(world) - , m_desc{} + : world_(world) + , desc_{} { const ecs_world_info_t *stats = ecs_get_world_info(world); - m_desc.target_fps = stats->target_fps; + desc_.target_fps = stats->target_fps; ecs_ftime_t t_zero = 0.0; - if (ECS_EQ(m_desc.target_fps, t_zero)) { - m_desc.target_fps = 60; + if (ECS_EQ(desc_.target_fps, t_zero)) { + desc_.target_fps = 60; } } app_builder& target_fps(ecs_ftime_t value) { - m_desc.target_fps = value; + desc_.target_fps = value; return *this; } app_builder& delta_time(ecs_ftime_t value) { - m_desc.delta_time = value; + desc_.delta_time = value; return *this; } app_builder& threads(int32_t value) { - m_desc.threads = value; + desc_.threads = value; return *this; } app_builder& frames(int32_t value) { - m_desc.frames = value; + desc_.frames = value; return *this; } app_builder& enable_rest(uint16_t port = 0) { - m_desc.enable_rest = true; - m_desc.port = port; + desc_.enable_rest = true; + desc_.port = port; return *this; } - app_builder& enable_monitor(bool value = true) { - m_desc.enable_monitor = value; + app_builder& enable_stats(bool value = true) { + desc_.enable_stats = value; return *this; } app_builder& init(ecs_app_init_action_t value) { - m_desc.init = value; + desc_.init = value; return *this; } app_builder& ctx(void *value) { - m_desc.ctx = value; + desc_.ctx = value; return *this; } int run() { - int result = ecs_app_run(m_world, &m_desc); - if (ecs_should_quit(m_world)) { + int result = ecs_app_run(world_, &desc_); + if (ecs_should_quit(world_)) { // Only free world if quit flag is set. This ensures that we won't - // try to cleanup the world if the app is used in an environment + // try to cleanup the world if the app is used in an environment // that takes over the main loop, like with emscripten. - ecs_fini(m_world); + if (!flecs_poly_release(world_)) { + ecs_fini(world_); + } } return result; } private: - flecs::world_t *m_world; - ecs_app_desc_t m_desc; + flecs::world_t *world_; + ecs_app_desc_t desc_; }; /** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/app/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/app/mixin.inl index c5847e760..dee812985 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/app/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/app/mixin.inl @@ -3,18 +3,23 @@ * @brief App world addon mixin. */ +/** + * @ingroup cpp_addons_app + * @memberof flecs::world + * + * @{ + */ + /** Return app builder. * The app builder is a convenience wrapper around a loop that runs * world::progress. An app allows for writing platform agnostic code, * as it provides hooks to modules for overtaking the main loop which is * required for frameworks like emscripten. - * - * \ingroup cpp_addons_app - * \memberof flecs::world */ flecs::app_builder app() { - m_owned = false; // App takes ownership of world - return flecs::app_builder(m_world); + flecs::world_t *w = world_; + world_ = nullptr; // Take ownership + return flecs::app_builder(w); } /** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/component/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/component/impl.hpp index 059e7ff84..fe77fcc57 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/component/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/component/impl.hpp @@ -9,12 +9,12 @@ namespace flecs { template inline flecs::component world::component(Args &&... args) const { - return flecs::component(m_world, FLECS_FWD(args)...); + return flecs::component(world_, FLECS_FWD(args)...); } template inline flecs::untyped_component world::component(Args &&... args) const { - return flecs::untyped_component(m_world, FLECS_FWD(args)...); + return flecs::untyped_component(world_, FLECS_FWD(args)...); } } // namespace flecs diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/component/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/component/mixin.inl index e3b13201c..dafed1c4a 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/component/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/component/mixin.inl @@ -5,8 +5,8 @@ /** Find or register component. * - * \ingroup cpp_components - * \memberof flecs::world + * @ingroup cpp_components + * @memberof flecs::world */ template flecs::component component(Args &&... args) const; @@ -14,8 +14,8 @@ flecs::component component(Args &&... args) const; /** Find or register untyped component. * Method available on flecs::world class. * - * \ingroup cpp_components - * \memberof flecs::world + * @ingroup cpp_components + * @memberof flecs::world */ template flecs::untyped_component component(Args &&... args) const; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/doc/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/doc/decl.hpp index 9b6a19be3..6a87ae436 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/doc/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/doc/decl.hpp @@ -10,15 +10,18 @@ namespace doc { /** * @defgroup cpp_addons_doc Doc - * @brief Utilities for documenting entities, components and systems. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Utilities for documenting entities, components and systems. + * * @{ */ /** flecs.doc.Description component */ using Description = EcsDocDescription; +/** flecs.doc.Uuid component */ +static const flecs::entity_t Uuid = EcsDocUuid; + /** flecs.doc.Brief component */ static const flecs::entity_t Brief = EcsDocBrief; @@ -31,7 +34,9 @@ static const flecs::entity_t Link = EcsDocLink; /** flecs.doc.Color component */ static const flecs::entity_t Color = EcsDocColor; +/** @private */ namespace _ { +/** @private */ void init(flecs::world& world); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/doc/entity_builder.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/doc/entity_builder.inl index 3e3c0330e..bfad3c7d6 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/doc/entity_builder.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/doc/entity_builder.inl @@ -3,57 +3,92 @@ * @brief Doc entity builder mixin. */ -/** Set doc name. - * This adds (flecs.doc.Description, flecs.Name) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc +/** Set human readable name. + * This adds `(flecs.doc.Description, flecs.Name)` to the entity. + * + * @see ecs_doc_set_name() + * @see flecs::doc::set_name() + * @see flecs::entity_view::doc_name() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_name(const char *name) { - ecs_doc_set_name(m_world, m_id, name); +const Self& set_doc_name(const char *name) const { + ecs_doc_set_name(world_, id_, name); return to_base(); } -/** Set doc brief. - * This adds (flecs.doc.Description, flecs.doc.Brief) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc +/** Set brief description. + * This adds `(flecs.doc.Description, flecs.doc.Brief)` to the entity. + * + * @see ecs_doc_set_brief() + * @see flecs::doc::set_brief() + * @see flecs::entity_view::doc_brief() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_brief(const char *brief) { - ecs_doc_set_brief(m_world, m_id, brief); +const Self& set_doc_brief(const char *brief) const { + ecs_doc_set_brief(world_, id_, brief); return to_base(); } -/** Set doc detailed description. - * This adds (flecs.doc.Description, flecs.doc.Detail) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc +/** Set detailed description. + * This adds `(flecs.doc.Description, flecs.doc.Detail)` to the entity. + * + * @see ecs_doc_set_detail() + * @see flecs::doc::set_detail() + * @see flecs::entity_view::doc_detail() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_detail(const char *detail) { - ecs_doc_set_detail(m_world, m_id, detail); +const Self& set_doc_detail(const char *detail) const { + ecs_doc_set_detail(world_, id_, detail); return to_base(); } -/** Set doc link. - * This adds (flecs.doc.Description, flecs.doc.Link) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc +/** Set link to external documentation. + * This adds `(flecs.doc.Description, flecs.doc.Link)` to the entity. + * + * @see ecs_doc_set_link() + * @see flecs::doc::set_link() + * @see flecs::entity_view::doc_link() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_link(const char *link) { - ecs_doc_set_link(m_world, m_id, link); +const Self& set_doc_link(const char *link) const { + ecs_doc_set_link(world_, id_, link); return to_base(); } /** Set doc color. - * This adds (flecs.doc.Description, flecs.doc.Color) to the entity. - * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_doc + * This adds `(flecs.doc.Description, flecs.doc.Color)` to the entity. + * + * @see ecs_doc_set_color() + * @see flecs::doc::set_color() + * @see flecs::entity_view::doc_color() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc */ -Self& set_doc_color(const char *link) { - ecs_doc_set_color(m_world, m_id, link); +const Self& set_doc_color(const char *color) const { + ecs_doc_set_color(world_, id_, color); + return to_base(); +} + +/** Set doc UUID. + * This adds `(flecs.doc.Description, flecs.doc.Uuid)` to the entity. + * + * @see ecs_doc_set_uuid() + * @see flecs::doc::set_uuid() + * @see flecs::entity_view::doc_uuid() + * + * @memberof flecs::entity_builder + * @ingroup cpp_addons_doc + */ +const Self& set_doc_uuid(const char *uuid) const { + ecs_doc_set_uuid(world_, id_, uuid); return to_base(); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/doc/entity_view.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/doc/entity_view.inl index 5871557b5..eee04f719 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/doc/entity_view.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/doc/entity_view.inl @@ -3,22 +3,80 @@ * @brief Doc entity view mixin. */ -const char* doc_name() { - return ecs_doc_get_name(m_world, m_id); +/** Get human readable name. + * + * @see ecs_doc_get_name() + * @see flecs::doc::get_name() + * @see flecs::entity_builder::set_doc_name() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_name() const { + return ecs_doc_get_name(world_, id_); +} + +/** Get brief description. + * + * @see ecs_doc_get_brief() + * @see flecs::doc::get_brief() + * @see flecs::entity_builder::set_doc_brief() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_brief() const { + return ecs_doc_get_brief(world_, id_); } -const char* doc_brief() { - return ecs_doc_get_brief(m_world, m_id); +/** Get detailed description. + * + * @see ecs_doc_get_detail() + * @see flecs::doc::get_detail() + * @see flecs::entity_builder::set_doc_detail() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_detail() const { + return ecs_doc_get_detail(world_, id_); } -const char* doc_detail() { - return ecs_doc_get_detail(m_world, m_id); +/** Get link to external documentation. + * + * @see ecs_doc_get_link() + * @see flecs::doc::get_link() + * @see flecs::entity_builder::set_doc_link() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_link() const { + return ecs_doc_get_link(world_, id_); } -const char* doc_link() { - return ecs_doc_get_link(m_world, m_id); +/** Get color. + * + * @see ecs_doc_get_color() + * @see flecs::doc::get_color() + * @see flecs::entity_builder::set_doc_color() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_color() const { + return ecs_doc_get_color(world_, id_); } -const char* doc_color() { - return ecs_doc_get_color(m_world, m_id); +/** Get UUID. + * + * @see ecs_doc_get_uuid() + * @see flecs::doc::get_uuid() + * @see flecs::entity_builder::set_doc_uuid() + * + * @memberof flecs::entity_view + * @ingroup cpp_addons_doc + */ +const char* doc_uuid() const { + return ecs_doc_get_uuid(world_, id_); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/doc/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/doc/impl.hpp index ee326c9e5..abcc77adf 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/doc/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/doc/impl.hpp @@ -8,40 +8,154 @@ namespace flecs { namespace doc { +/** Get UUID for an entity. + * + * @see ecs_doc_get_uuid() + * @see flecs::doc::set_uuid() + * @see flecs::entity_view::doc_uuid() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_uuid(const flecs::entity_view& e) { + return ecs_doc_get_uuid(e.world(), e); +} + +/** Get human readable name for an entity. + * + * @see ecs_doc_get_name() + * @see flecs::doc::set_name() + * @see flecs::entity_view::doc_name() + * + * @ingroup cpp_addons_doc + */ inline const char* get_name(const flecs::entity_view& e) { return ecs_doc_get_name(e.world(), e); } +/** Get brief description for an entity. + * + * @see ecs_doc_get_brief() + * @see flecs::doc::set_brief() + * @see flecs::entity_view::doc_brief() + * + * @ingroup cpp_addons_doc + */ inline const char* get_brief(const flecs::entity_view& e) { return ecs_doc_get_brief(e.world(), e); } +/** Get detailed description for an entity. + * + * @see ecs_doc_get_detail() + * @see flecs::doc::set_detail() + * @see flecs::entity_view::doc_detail() + * + * @ingroup cpp_addons_doc + */ inline const char* get_detail(const flecs::entity_view& e) { return ecs_doc_get_detail(e.world(), e); } +/** Get link to external documentation for an entity. + * + * @see ecs_doc_get_link() + * @see flecs::doc::set_link() + * @see flecs::entity_view::doc_link() + * + * @ingroup cpp_addons_doc + */ inline const char* get_link(const flecs::entity_view& e) { return ecs_doc_get_link(e.world(), e); } +/** Get color for an entity. + * + * @see ecs_doc_get_color() + * @see flecs::doc::set_color() + * @see flecs::entity_view::doc_color() + * + * @ingroup cpp_addons_doc + */ +inline const char* get_color(const flecs::entity_view& e) { + return ecs_doc_get_color(e.world(), e); +} + +/** Set UUID for an entity. + * + * @see ecs_doc_set_uuid() + * @see flecs::doc::get_uuid() + * @see flecs::entity_builder::set_doc_uuid() + * + * @ingroup cpp_addons_doc + */ +inline void set_uuid(flecs::entity& e, const char *uuid) { + ecs_doc_set_uuid(e.world(), e, uuid); +} + +/** Set human readable name for an entity. + * + * @see ecs_doc_set_name() + * @see flecs::doc::get_name() + * @see flecs::entity_builder::set_doc_name() + * + * @ingroup cpp_addons_doc + */ inline void set_name(flecs::entity& e, const char *name) { ecs_doc_set_name(e.world(), e, name); } +/** Set brief description for an entity. + * + * @see ecs_doc_set_brief() + * @see flecs::doc::get_brief() + * @see flecs::entity_builder::set_doc_brief() + * + * @ingroup cpp_addons_doc + */ inline void set_brief(flecs::entity& e, const char *description) { ecs_doc_set_brief(e.world(), e, description); } +/** Set detailed description for an entity. + * + * @see ecs_doc_set_detail() + * @see flecs::doc::get_detail() + * @see flecs::entity_builder::set_doc_detail() + * + * @ingroup cpp_addons_doc + */ inline void set_detail(flecs::entity& e, const char *description) { ecs_doc_set_detail(e.world(), e, description); } -inline void set_link(flecs::entity& e, const char *description) { - ecs_doc_set_link(e.world(), e, description); +/** Set link to external documentation for an entity. + * + * @see ecs_doc_set_link() + * @see flecs::doc::get_link() + * @see flecs::entity_builder::set_doc_link() + * + * @ingroup cpp_addons_doc + */ +inline void set_link(flecs::entity& e, const char *link) { + ecs_doc_set_link(e.world(), e, link); +} + +/** Set color for an entity. + * + * @see ecs_doc_set_color() + * @see flecs::doc::get_color() + * @see flecs::entity_builder::set_doc_color() + * + * @ingroup cpp_addons_doc + */ +inline void set_color(flecs::entity& e, const char *color) { + ecs_doc_set_color(e.world(), e, color); } +/** @private */ namespace _ { +/** @private */ inline void init(flecs::world& world) { world.component("flecs::doc::Description"); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/entity/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/entity/builder.hpp index 892ac1412..31ec886c3 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/entity/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/entity/builder.hpp @@ -9,7 +9,7 @@ namespace flecs { /** Entity builder. - * \ingroup cpp_entities + * @ingroup cpp_entities */ template struct entity_builder : entity_view { @@ -22,10 +22,10 @@ struct entity_builder : entity_view { * @tparam T the component type to add. */ template - Self& add() { + const Self& add() const { flecs_static_assert(is_flecs_constructible::value, "cannot default construct type: add T::T() or use emplace()"); - ecs_add_id(this->m_world, this->m_id, _::cpp_type::id(this->m_world)); + ecs_add_id(this->world_, this->id_, _::type::id(this->world_)); return to_base(); } @@ -39,10 +39,12 @@ struct entity_builder : entity_view { * @param value The enumeration value. */ template ::value > = 0> - Self& add(E value) { - flecs::entity_t first = _::cpp_type::id(this->m_world); - const auto& et = enum_type(this->m_world); + const Self& add(E value) const { + flecs::entity_t first = _::type::id(this->world_); + const auto& et = enum_type(this->world_); flecs::entity_t second = et.entity(value); + + ecs_assert(second, ECS_INVALID_PARAMETER, "Component was not found in reflection data."); return this->add(first, second); } @@ -51,8 +53,8 @@ struct entity_builder : entity_view { * * @param component The component to add. */ - Self& add(id_t component) { - ecs_add_id(this->m_world, this->m_id, component); + const Self& add(id_t component) const { + ecs_add_id(this->world_, this->id_, component); return to_base(); } @@ -62,8 +64,8 @@ struct entity_builder : entity_view { * @param first The first element of the pair. * @param second The second element of the pair. */ - Self& add(entity_t first, entity_t second) { - ecs_add_pair(this->m_world, this->m_id, first, second); + const Self& add(entity_t first, entity_t second) const { + ecs_add_pair(this->world_, this->id_, first, second); return to_base(); } @@ -74,8 +76,8 @@ struct entity_builder : entity_view { * @tparam Second The second element of the pair */ template - Self& add() { - return this->add(_::cpp_type::id(this->m_world)); + const Self& add() const { + return this->add(_::type::id(this->world_)); } /** Add a pair. @@ -85,10 +87,10 @@ struct entity_builder : entity_view { * @param second The second element of the pair. */ template::value > = 0> - Self& add(Second second) { + const Self& add(Second second) const { flecs_static_assert(is_flecs_constructible::value, "cannot default construct type: add T::T() or use emplace()"); - return this->add(_::cpp_type::id(this->m_world), second); + return this->add(_::type::id(this->world_), second); } /** Add a pair. @@ -99,10 +101,10 @@ struct entity_builder : entity_view { * @param constant the enum constant. */ template::value > = 0> - Self& add(Second constant) { + const Self& add(Second constant) const { flecs_static_assert(is_flecs_constructible::value, "cannot default construct type: add T::T() or use emplace()"); - const auto& et = enum_type(this->m_world); + const auto& et = enum_type(this->world_); return this->add(et.entity(constant)); } @@ -113,8 +115,8 @@ struct entity_builder : entity_view { * @tparam Second The second element of the pair */ template - Self& add_second(flecs::entity_t first) { - return this->add(first, _::cpp_type::id(this->m_world)); + const Self& add_second(flecs::entity_t first) const { + return this->add(first, _::type::id(this->world_)); } /** Conditional add. @@ -123,7 +125,7 @@ struct entity_builder : entity_view { * @param cond The condition to evaluate. * @param component The component to add. */ - Self& add_if(bool cond, flecs::id_t component) { + const Self& add_if(bool cond, flecs::id_t component) const { if (cond) { return this->add(component); } else { @@ -138,7 +140,7 @@ struct entity_builder : entity_view { * @param cond The condition to evaluate. */ template - Self& add_if(bool cond) { + const Self& add_if(bool cond) const { if (cond) { return this->add(); } else { @@ -153,7 +155,7 @@ struct entity_builder : entity_view { * @param first The first element of the pair. * @param second The second element of the pair. */ - Self& add_if(bool cond, flecs::entity_t first, flecs::entity_t second) { + const Self& add_if(bool cond, flecs::entity_t first, flecs::entity_t second) const { if (cond) { return this->add(first, second); } else { @@ -161,7 +163,7 @@ struct entity_builder : entity_view { * second which will remove all instances of the relationship. * Replacing 0 with Wildcard will make it possible to use the second * as the condition. */ - if (!second || ecs_has_id(this->m_world, first, flecs::Exclusive)) { + if (!second || ecs_has_id(this->world_, first, flecs::Exclusive)) { second = flecs::Wildcard; } return this->remove(first, second); @@ -176,8 +178,8 @@ struct entity_builder : entity_view { * @param second The second element of the pair. */ template - Self& add_if(bool cond, flecs::entity_t second) { - return this->add_if(cond, _::cpp_type::id(this->m_world), second); + const Self& add_if(bool cond, flecs::entity_t second) const { + return this->add_if(cond, _::type::id(this->world_), second); } /** Conditional add. @@ -188,8 +190,8 @@ struct entity_builder : entity_view { * @param cond The condition to evaluate. */ template - Self& add_if(bool cond) { - return this->add_if(cond, _::cpp_type::id(this->m_world)); + const Self& add_if(bool cond) const { + return this->add_if(cond, _::type::id(this->world_)); } /** Conditional add. @@ -199,87 +201,98 @@ struct entity_builder : entity_view { * @param constant The enumeration constant. */ template ::value > = 0> - Self& add_if(bool cond, E constant) { - const auto& et = enum_type(this->m_world); + const Self& add_if(bool cond, E constant) const { + const auto& et = enum_type(this->world_); return this->add_if(cond, et.entity(constant)); } - /** Shortcut for add(IsA, entity). + /** Shortcut for `add(IsA, entity)`. * * @param second The second element of the pair. */ - Self& is_a(entity_t second) { + const Self& is_a(entity_t second) const { return this->add(flecs::IsA, second); } - /** Shortcut for add(IsA, entity). + /** Shortcut for `add(IsA, entity)`. * * @tparam T the type associated with the entity. */ template - Self& is_a() { - return this->add(flecs::IsA, _::cpp_type::id(this->m_world)); + const Self& is_a() const { + return this->add(flecs::IsA, _::type::id(this->world_)); } - /** Shortcut for add(ChildOf, entity). + /** Shortcut for `add(ChildOf, entity)`. * * @param second The second element of the pair. */ - Self& child_of(entity_t second) { + const Self& child_of(entity_t second) const { return this->add(flecs::ChildOf, second); } - /** Shortcut for add(DependsOn, entity). + /** Shortcut for `add(DependsOn, entity)`. * * @param second The second element of the pair. */ - Self& depends_on(entity_t second) { + const Self& depends_on(entity_t second) const { return this->add(flecs::DependsOn, second); } - /** Shortcut for add(SlotOf, entity). + /** Shortcut for `add(DependsOn, entity)`. * * @param second The second element of the pair. */ - Self& slot_of(entity_t second) { + template ::value> = 0> + const Self& depends_on(E second) const { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(second); + return depends_on(target); + } + + /** Shortcut for `add(SlotOf, entity)`. + * + * @param second The second element of the pair. + */ + const Self& slot_of(entity_t second) const { return this->add(flecs::SlotOf, second); } - /** Shortcut for add(SlotOf, target(ChildOf)). + /** Shortcut for `add(SlotOf, target(ChildOf))`. */ - Self& slot() { - ecs_check(ecs_get_target(m_world, m_id, flecs::ChildOf, 0), + const Self& slot() const { + ecs_check(ecs_get_target(world_, id_, flecs::ChildOf, 0), ECS_INVALID_PARAMETER, "add ChildOf pair before using slot()"); return this->slot_of(this->target(flecs::ChildOf)); error: return to_base(); } - /** Shortcut for add(ChildOf, entity). + /** Shortcut for `add(ChildOf, entity)`. * * @tparam T the type associated with the entity. */ template - Self& child_of() { - return this->child_of(_::cpp_type::id(this->m_world)); + const Self& child_of() const { + return this->child_of(_::type::id(this->world_)); } - /** Shortcut for add(DependsOn, entity). + /** Shortcut for `add(DependsOn, entity)`. * * @tparam T the type associated with the entity. */ template - Self& depends_on() { - return this->depends_on(_::cpp_type::id(this->m_world)); + const Self& depends_on() const { + return this->depends_on(_::type::id(this->world_)); } - /** Shortcut for add(SlotOf, entity). + /** Shortcut for `add(SlotOf, entity)`. * * @tparam T the type associated with the entity. */ template - Self& slot_of() { - return this->slot_of(_::cpp_type::id(this->m_world)); + const Self& slot_of() const { + return this->slot_of(_::type::id(this->world_)); } /** Remove a component from an entity. @@ -287,19 +300,19 @@ struct entity_builder : entity_view { * @tparam T the type of the component to remove. */ template ::value > = 0> - Self& remove() { - ecs_remove_id(this->m_world, this->m_id, _::cpp_type::id(this->m_world)); + const Self& remove() const { + ecs_remove_id(this->world_, this->id_, _::type::id(this->world_)); return to_base(); } /** Remove pair for enum. - * This operation will remove any (Enum, *) pair from the entity. + * This operation will remove any `(Enum, *)` pair from the entity. * * @tparam E The enumeration type. */ template ::value > = 0> - Self& remove() { - flecs::entity_t first = _::cpp_type::id(this->m_world); + const Self& remove() const { + flecs::entity_t first = _::type::id(this->world_); return this->remove(first, flecs::Wildcard); } @@ -307,8 +320,8 @@ struct entity_builder : entity_view { * * @param entity The entity to remove. */ - Self& remove(entity_t entity) { - ecs_remove_id(this->m_world, this->m_id, entity); + const Self& remove(entity_t entity) const { + ecs_remove_id(this->world_, this->id_, entity); return to_base(); } @@ -318,8 +331,8 @@ struct entity_builder : entity_view { * @param first The first element of the pair. * @param second The second element of the pair. */ - Self& remove(entity_t first, entity_t second) { - ecs_remove_pair(this->m_world, this->m_id, first, second); + const Self& remove(entity_t first, entity_t second) const { + ecs_remove_pair(this->world_, this->id_, first, second); return to_base(); } @@ -330,8 +343,8 @@ struct entity_builder : entity_view { * @tparam Second The second element of the pair */ template - Self& remove() { - return this->remove(_::cpp_type::id(this->m_world)); + const Self& remove() const { + return this->remove(_::type::id(this->world_)); } /** Remove a pair. @@ -341,8 +354,8 @@ struct entity_builder : entity_view { * @param second The second element of the pair. */ template::value > = 0> - Self& remove(Second second) { - return this->remove(_::cpp_type::id(this->m_world), second); + const Self& remove(Second second) const { + return this->remove(_::type::id(this->world_), second); } /** Removes a pair. @@ -352,8 +365,8 @@ struct entity_builder : entity_view { * @param first The first element of the pair */ template - Self& remove_second(flecs::entity_t first) { - return this->remove(first, _::cpp_type::id(this->m_world)); + const Self& remove_second(flecs::entity_t first) const { + return this->remove(first, _::type::id(this->world_)); } /** Remove a pair. @@ -363,168 +376,176 @@ struct entity_builder : entity_view { * @param constant the enum constant. */ template::value > = 0> - Self& remove(Second constant) { - const auto& et = enum_type(this->m_world); + const Self& remove(Second constant) const { + const auto& et = enum_type(this->world_); flecs::entity_t second = et.entity(constant); return this->remove(second); } /** Mark id for auto-overriding. - * When an entity inherits from a base entity (using the IsA relationship) + * When an entity inherits from a base entity (using the `IsA` relationship) * any ids marked for auto-overriding on the base will be overridden * automatically by the entity. * * @param id The id to mark for overriding. - */ - Self& override(flecs::id_t id) { - return this->add(ECS_OVERRIDE | id); + */ + const Self& auto_override(flecs::id_t id) const { + return this->add(ECS_AUTO_OVERRIDE | id); } /** Mark pair for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @param first The first element of the pair. * @param second The second element of the pair. - */ - Self& override(flecs::entity_t first, flecs::entity_t second) { - return this->override(ecs_pair(first, second)); + */ + const Self& auto_override(flecs::entity_t first, flecs::entity_t second) const { + return this->auto_override(ecs_pair(first, second)); } /** Mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam T The component to mark for overriding. - */ + */ template - Self& override() { - return this->override(_::cpp_type::id(this->m_world)); + const Self& auto_override() const { + return this->auto_override(_::type::id(this->world_)); } /** Mark pair for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + */ template - Self& override(flecs::entity_t second) { - return this->override(_::cpp_type::id(this->m_world), second); + const Self& auto_override(flecs::entity_t second) const { + return this->auto_override(_::type::id(this->world_), second); } /** Mark pair for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + */ template - Self& override() { - return this->override(_::cpp_type::id(this->m_world)); + const Self& auto_override() const { + return this->auto_override(_::type::id(this->world_)); } /** Set component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam T The component to set and for which to add the OVERRIDE flag - */ + * @param val The value to set. + */ template - Self& set_override(const T& val) { - this->override(); + const Self& set_auto_override(const T& val) const { + this->auto_override(); return this->set(val); } /** Set component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam T The component to set and for which to add the OVERRIDE flag - */ + * @param val The value to set. + */ template - Self& set_override(T&& val) { - this->override(); + const Self& set_auto_override(T&& val) const { + this->auto_override(); return this->set(FLECS_FWD(val)); } /** Set pair, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + * @param val The value to set. + */ template - Self& set_override(flecs::entity_t second, const First& val) { - this->override(second); + const Self& set_auto_override(flecs::entity_t second, const First& val) const { + this->auto_override(second); return this->set(second, val); } /** Set pair, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + * @param val The value to set. + */ template - Self& set_override(flecs::entity_t second, First&& val) { - this->override(second); + const Self& set_auto_override(flecs::entity_t second, First&& val) const { + this->auto_override(second); return this->set(second, FLECS_FWD(val)); } /** Set component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + * @param val The value to set. + */ template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> - Self& set_override(const A& val) { - this->override(); + const Self& set_auto_override(const A& val) const { + this->auto_override(); return this->set(val); } /** Set component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + * @param val The value to set. + */ template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> - Self& set_override(A&& val) { - this->override(); + const Self& set_auto_override(A&& val) const { + this->auto_override(); return this->set(FLECS_FWD(val)); } /** Emplace component, mark component for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam T The component to emplace and override. - */ + * @param args The arguments to pass to the constructor of `T`. + */ template - Self& emplace_override(Args&&... args) { - this->override(); + const Self& emplace_auto_override(Args&&... args) const { + this->auto_override(); - flecs::emplace(this->m_world, this->m_id, - _::cpp_type::id(this->m_world), FLECS_FWD(args)...); + flecs::emplace(this->world_, this->id_, + _::type::id(this->world_), FLECS_FWD(args)...); return to_base(); } /** Emplace pair, mark pair for auto-overriding. - * @see override(flecs::id_t id) + * @see auto_override(flecs::id_t) const * * @tparam First The first element of the pair to emplace and override. * @tparam Second The second element of the pair to emplace and override. - */ + * @param args The arguments to pass to the constructor of `Second`. + */ template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0, typename ... Args> - Self& emplace_override(Args&&... args) { - this->override(); + const Self& emplace_auto_override(Args&&... args) const { + this->auto_override(); - flecs::emplace(this->m_world, this->m_id, - ecs_pair(_::cpp_type::id(this->m_world), - _::cpp_type::id(this->m_world)), - FLECS_FWD(args)...); + flecs::emplace(this->world_, this->id_, + ecs_pair(_::type::id(this->world_), + _::type::id(this->world_)), + FLECS_FWD(args)...); return to_base(); } @@ -533,8 +554,8 @@ struct entity_builder : entity_view { * Enabled entities are matched with systems and can be searched with * queries. */ - Self& enable() { - ecs_enable(this->m_world, this->m_id, true); + const Self& enable() const { + ecs_enable(this->world_, this->id_, true); return to_base(); } @@ -542,8 +563,8 @@ struct entity_builder : entity_view { * Disabled entities are not matched with systems and cannot be searched * with queries, unless explicitly specified in the query expression. */ - Self& disable() { - ecs_enable(this->m_world, this->m_id, false); + const Self& disable() const { + ecs_enable(this->world_, this->id_, false); return to_base(); } @@ -553,52 +574,54 @@ struct entity_builder : entity_view { * * @param id The id to enable. * @param toggle True to enable, false to disable (default = true). - */ - Self& enable(flecs::id_t id, bool toggle = true) { - ecs_enable_id(this->m_world, this->m_id, id, toggle); + * + * @see ecs_enable_id() + */ + const Self& enable(flecs::id_t id, bool toggle = true) const { + ecs_enable_id(this->world_, this->id_, id, toggle); return to_base(); } /** Enable a component. - * @see enable(flecs::id_t id) + * @see enable(flecs::id_t) const * * @tparam T The component to enable. - */ + */ template - Self& enable() { - return this->enable(_::cpp_type::id(this->m_world)); + const Self& enable() const { + return this->enable(_::type::id(this->world_)); } /** Enable a pair. - * @see enable(flecs::id_t id) + * @see enable(flecs::id_t) const * * @param first The first element of the pair. * @param second The second element of the pair. - */ - Self& enable(flecs::id_t first, flecs::id_t second) { + */ + const Self& enable(flecs::id_t first, flecs::id_t second) const { return this->enable(ecs_pair(first, second)); } /** Enable a pair. - * @see enable(flecs::id_t id) + * @see enable(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + */ template - Self& enable(flecs::id_t second) { - return this->enable(_::cpp_type::id(), second); + const Self& enable(flecs::id_t second) const { + return this->enable(_::type::id(), second); } /** Enable a pair. - * @see enable(flecs::id_t id) + * @see enable(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + */ template - Self& enable() { - return this->enable(_::cpp_type::id()); + const Self& enable() const { + return this->enable(_::type::id()); } /** Disable an id. @@ -606,61 +629,64 @@ struct entity_builder : entity_view { * the id is enabled or disabled, the bitset is added. * * @param id The id to disable. - */ - Self& disable(flecs::id_t id) { + * + * @see ecs_enable_id() + * @see enable(flecs::id_t) const + */ + const Self& disable(flecs::id_t id) const { return this->enable(id, false); } /** Disable a component. - * @see disable(flecs::id_t id) + * @see disable(flecs::id_t) const * * @tparam T The component to enable. - */ + */ template - Self& disable() { - return this->disable(_::cpp_type::id()); + const Self& disable() const { + return this->disable(_::type::id()); } /** Disable a pair. - * @see disable(flecs::id_t id) + * @see disable(flecs::id_t) const * * @param first The first element of the pair. * @param second The second element of the pair. - */ - Self& disable(flecs::id_t first, flecs::id_t second) { + */ + const Self& disable(flecs::id_t first, flecs::id_t second) const { return this->disable(ecs_pair(first, second)); } /** Disable a pair. - * @see disable(flecs::id_t id) + * @see disable(flecs::id_t) const * * @tparam First The first element of the pair. * @param second The second element of the pair. - */ + */ template - Self& disable(flecs::id_t second) { - return this->disable(_::cpp_type::id(), second); + const Self& disable(flecs::id_t second) const { + return this->disable(_::type::id(), second); } /** Disable a pair. - * @see disable(flecs::id_t id) + * @see disable(flecs::id_t) const * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. - */ + */ template - Self& disable() { - return this->disable(_::cpp_type::id()); + const Self& disable() const { + return this->disable(_::type::id()); } - Self& set_ptr(entity_t comp, size_t size, const void *ptr) { - ecs_set_id(this->m_world, this->m_id, comp, size, ptr); + const Self& set_ptr(entity_t comp, size_t size, const void *ptr) const { + ecs_set_id(this->world_, this->id_, comp, size, ptr); return to_base(); } - Self& set_ptr(entity_t comp, const void *ptr) { + const Self& set_ptr(entity_t comp, const void *ptr) const { const flecs::Component *cptr = ecs_get( - this->m_world, comp, EcsComponent); + this->world_, comp, EcsComponent); /* Can't set if it's not a component */ ecs_assert(cptr != NULL, ECS_INVALID_PARAMETER, NULL); @@ -668,31 +694,29 @@ struct entity_builder : entity_view { return set_ptr(comp, cptr->size, ptr); } - template::value && is_actual::value> = 0 > - Self& set(T&& value) { - flecs::set(this->m_world, this->m_id, FLECS_FWD(value)); + template::value> = 0 > + const Self& set(T&& value) const { + flecs::set(this->world_, this->id_, FLECS_FWD(value)); return to_base(); } - template::value && is_actual::value > = 0> - Self& set(const T& value) { - flecs::set(this->m_world, this->m_id, value); + template::value > = 0> + const Self& set(const T& value) const { + flecs::set(this->world_, this->id_, value); return to_base(); } template, if_not_t< - is_callable::value || is_actual::value > = 0> - Self& set(A&& value) { - flecs::set(this->m_world, this->m_id, FLECS_FWD(value)); + is_actual::value > = 0> + const Self& set(A&& value) const { + flecs::set(this->world_, this->id_, FLECS_FWD(value)); return to_base(); } template, if_not_t< - is_callable::value || is_actual::value > = 0> - Self& set(const A& value) { - flecs::set(this->m_world, this->m_id, value); + is_actual::value > = 0> + const Self& set(const A& value) const { + flecs::set(this->world_, this->id_, value); return to_base(); } @@ -706,8 +730,8 @@ struct entity_builder : entity_view { */ template , typename A = actual_type_t

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

(this->m_world, this->m_id, FLECS_FWD(value)); + const Self& set(A&& value) const { + flecs::set

(this->world_, this->id_, FLECS_FWD(value)); return to_base(); } @@ -721,8 +745,8 @@ struct entity_builder : entity_view { */ template , typename A = actual_type_t

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

(this->m_world, this->m_id, value); + const Self& set(const A& value) const { + flecs::set

(this->world_, this->id_, value); return to_base(); } @@ -735,9 +759,9 @@ struct entity_builder : entity_view { * @param value The value to set. */ template ::value > = 0> - Self& set(Second second, const First& value) { - auto first = _::cpp_type::id(this->m_world); - flecs::set(this->m_world, this->m_id, value, + const Self& set(Second second, const First& value) const { + auto first = _::type::id(this->world_); + flecs::set(this->world_, this->id_, value, ecs_pair(first, second)); return to_base(); } @@ -751,9 +775,9 @@ struct entity_builder : entity_view { * @param value The value to set. */ template ::value > = 0> - Self& set(Second second, First&& value) { - auto first = _::cpp_type::id(this->m_world); - flecs::set(this->m_world, this->m_id, FLECS_FWD(value), + const Self& set(Second second, First&& value) const { + auto first = _::type::id(this->world_); + flecs::set(this->world_, this->id_, FLECS_FWD(value), ecs_pair(first, second)); return to_base(); } @@ -767,8 +791,8 @@ struct entity_builder : entity_view { * @param value The value to set. */ template ::value > = 0> - Self& set(Second constant, const First& value) { - const auto& et = enum_type(this->m_world); + const Self& set(Second constant, const First& value) const { + const auto& et = enum_type(this->world_); flecs::entity_t second = et.entity(constant); return set(second, value); } @@ -782,9 +806,13 @@ struct entity_builder : entity_view { * @param value The value to set. */ template - Self& set_second(entity_t first, const Second& value) { - auto second = _::cpp_type::id(this->m_world); - flecs::set(this->m_world, this->m_id, value, + const Self& set_second(entity_t first, const Second& value) const { + auto second = _::type::id(this->world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + flecs::set(this->world_, this->id_, value, ecs_pair(first, second)); return to_base(); } @@ -798,16 +826,20 @@ struct entity_builder : entity_view { * @param value The value to set. */ template - Self& set_second(entity_t first, Second&& value) { - auto second = _::cpp_type::id(this->m_world); - flecs::set(this->m_world, this->m_id, FLECS_FWD(value), + const Self& set_second(entity_t first, Second&& value) const { + auto second = _::type::id(this->world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + flecs::set(this->world_, this->id_, FLECS_FWD(value), ecs_pair(first, second)); return to_base(); } template - Self& set_second(const Second& value) { - flecs::set>(this->m_world, this->m_id, value); + const Self& set_second(const Second& value) const { + flecs::set>(this->world_, this->id_, value); return to_base(); } @@ -818,7 +850,7 @@ struct entity_builder : entity_view { * * This operation is faster than individually calling get for each component * as it only obtains entity metadata once. When this operation is called - * while deferred, its performance is equivalent to that of calling get_mut + * while deferred, its performance is equivalent to that of calling ensure * for each component separately. * * The operation will invoke modified for each component after the callback @@ -826,16 +858,19 @@ struct entity_builder : entity_view { * * @param func The callback to invoke. */ - template ::value > = 0> - Self& set(const Func& func); + template + const Self& insert(const Func& func) const; /** Emplace component. * Emplace constructs a component in the storage, which prevents calling the * destructor on the value passed into the function. * * Emplace attempts the following signatures to construct the component: - * T{Args...} - * T{flecs::entity, Args...} + * + * @code + * T{Args...} + * T{flecs::entity, Args...} + * @endcode * * If the second signature matches, emplace will pass in the current entity * as argument to the constructor, which is useful if the component needs @@ -848,34 +883,40 @@ struct entity_builder : entity_view { * @param args The arguments to pass to the constructor of T */ template> - Self& emplace(Args&&... args) { - flecs::emplace(this->m_world, this->m_id, - _::cpp_type::id(this->m_world), FLECS_FWD(args)...); + const Self& emplace(Args&&... args) const { + flecs::emplace(this->world_, this->id_, + _::type::id(this->world_), FLECS_FWD(args)...); return to_base(); } template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> - Self& emplace(Args&&... args) { - flecs::emplace(this->m_world, this->m_id, - ecs_pair(_::cpp_type::id(this->m_world), - _::cpp_type::id(this->m_world)), + const Self& emplace(Args&&... args) const { + flecs::emplace(this->world_, this->id_, + ecs_pair(_::type::id(this->world_), + _::type::id(this->world_)), FLECS_FWD(args)...); return to_base(); } template - Self& emplace_first(flecs::entity_t second, Args&&... args) { - flecs::emplace(this->m_world, this->m_id, - ecs_pair(_::cpp_type::id(this->m_world), second), + const Self& emplace_first(flecs::entity_t second, Args&&... args) const { + auto first = _::type::id(this->world_); + flecs::emplace(this->world_, this->id_, + ecs_pair(first, second), FLECS_FWD(args)...); return to_base(); } template - Self& emplace_second(flecs::entity_t first, Args&&... args) { - flecs::emplace(this->m_world, this->m_id, - ecs_pair(first, _::cpp_type::id(this->m_world)), + const Self& emplace_second(flecs::entity_t first, Args&&... args) const { + auto second = _::type::id(this->world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + flecs::emplace(this->world_, this->id_, + ecs_pair(first, second), FLECS_FWD(args)...); return to_base(); } @@ -886,65 +927,65 @@ struct entity_builder : entity_view { * @param func The function to call. */ template - Self& with(const Func& func) { - ecs_id_t prev = ecs_set_with(this->m_world, this->m_id); + const Self& with(const Func& func) const { + ecs_id_t prev = ecs_set_with(this->world_, this->id_); func(); - ecs_set_with(this->m_world, prev); + ecs_set_with(this->world_, prev); return to_base(); } - /** Entities created in function will have (First, this). + /** Entities created in function will have `(First, this)`. * This operation is thread safe. * * @tparam First The first element of the pair * @param func The function to call. */ template - Self& with(const Func& func) { - with(_::cpp_type::id(this->m_world), func); + const Self& with(const Func& func) const { + with(_::type::id(this->world_), func); return to_base(); } - /** Entities created in function will have (first, this). + /** Entities created in function will have `(first, this)`. * This operation is thread safe. * * @param first The first element of the pair. * @param func The function to call. */ template - Self& with(entity_t first, const Func& func) { - ecs_id_t prev = ecs_set_with(this->m_world, - ecs_pair(first, this->m_id)); + const Self& with(entity_t first, const Func& func) const { + ecs_id_t prev = ecs_set_with(this->world_, + ecs_pair(first, this->id_)); func(); - ecs_set_with(this->m_world, prev); + ecs_set_with(this->world_, prev); return to_base(); } /** The function will be ran with the scope set to the current entity. */ template - Self& scope(const Func& func) { - ecs_entity_t prev = ecs_set_scope(this->m_world, this->m_id); + const Self& scope(const Func& func) const { + ecs_entity_t prev = ecs_set_scope(this->world_, this->id_); func(); - ecs_set_scope(this->m_world, prev); + ecs_set_scope(this->world_, prev); return to_base(); } /** Return world scoped to entity */ scoped_world scope() const { - return scoped_world(m_world, m_id); + return scoped_world(world_, id_); } /* Set the entity name. */ - Self& set_name(const char *name) { - ecs_set_name(this->m_world, this->m_id, name); + const Self& set_name(const char *name) const { + ecs_set_name(this->world_, this->id_, name); return to_base(); } /* Set entity alias. */ - Self& set_alias(const char *name) { - ecs_set_alias(this->m_world, this->m_id, name); + const Self& set_alias(const char *name) const { + ecs_set_alias(this->world_, this->id_, name); return to_base(); } @@ -963,8 +1004,8 @@ struct entity_builder : entity_view { # include "../event/entity_builder.inl" protected: - Self& to_base() { - return *static_cast(this); + const Self& to_base() const { + return *static_cast(this); } }; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/entity/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/entity/impl.hpp index 09102b5f9..47ec8c3fd 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/entity/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/entity/impl.hpp @@ -9,65 +9,65 @@ namespace flecs { template flecs::entity ref::entity() const { - return flecs::entity(m_world, m_ref.entity); + return flecs::entity(world_, ref_.entity); } template -template ::value > > -inline Self& entity_builder::set(const Func& func) { - _::entity_with_delegate::invoke_get_mut( - this->m_world, this->m_id, func); +template +inline const Self& entity_builder::insert(const Func& func) const { + _::entity_with_delegate::invoke_ensure( + this->world_, this->id_, func); return to_base(); } template ::value > > const T* entity_view::get() const { - entity_t r = _::cpp_type::id(m_world); - entity_t c = ecs_get_target(m_world, m_id, r, 0); + entity_t r = _::type::id(world_); + entity_t c = ecs_get_target(world_, id_, r, 0); if (c) { // Get constant value from constant entity - const T* v = static_cast(ecs_get_id(m_world, c, r)); + const T* v = static_cast(ecs_get_id(world_, c, r)); ecs_assert(v != NULL, ECS_INTERNAL_ERROR, "missing enum constant value"); return v; } else { // If there is no matching pair for (r, *), try just r - return static_cast(ecs_get_id(m_world, m_id, r)); + return static_cast(ecs_get_id(world_, id_, r)); } } template inline flecs::entity entity_view::target(int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, m_id, _::cpp_type::id(m_world), index)); + return flecs::entity(world_, + ecs_get_target(world_, id_, _::type::id(world_), index)); } inline flecs::entity entity_view::target( flecs::entity_t relationship, int32_t index) const { - return flecs::entity(m_world, - ecs_get_target(m_world, m_id, relationship, index)); + return flecs::entity(world_, + ecs_get_target(world_, id_, relationship, index)); } inline flecs::entity entity_view::target_for( flecs::entity_t relationship, flecs::id_t id) const { - return flecs::entity(m_world, - ecs_get_target_for_id(m_world, m_id, relationship, id)); + return flecs::entity(world_, + ecs_get_target_for_id(world_, id_, relationship, id)); } template inline flecs::entity entity_view::target_for(flecs::entity_t relationship) const { - return target_for(relationship, _::cpp_type::id(m_world)); + return target_for(relationship, _::type::id(world_)); } template inline flecs::entity entity_view::target_for(flecs::entity_t relationship) const { - return target_for(relationship, _::cpp_type::id(m_world)); + return target_for(relationship, _::type::id(world_)); } inline flecs::entity entity_view::parent() const { @@ -77,37 +77,37 @@ inline flecs::entity entity_view::parent() const { inline flecs::entity entity_view::mut(const flecs::world& stage) const { ecs_assert(!stage.is_readonly(), ECS_INVALID_PARAMETER, "cannot use readonly world/stage to create mutable handle"); - return flecs::entity(m_id).set_stage(stage.c_ptr()); + return flecs::entity(id_).set_stage(stage.c_ptr()); } inline flecs::entity entity_view::mut(const flecs::iter& it) const { ecs_assert(!it.world().is_readonly(), ECS_INVALID_PARAMETER, "cannot use iterator created for readonly world/stage to create mutable handle"); - return flecs::entity(m_id).set_stage(it.world().c_ptr()); + return flecs::entity(id_).set_stage(it.world().c_ptr()); } inline flecs::entity entity_view::mut(const flecs::entity_view& e) const { ecs_assert(!e.world().is_readonly(), ECS_INVALID_PARAMETER, "cannot use entity created for readonly world/stage to create mutable handle"); - return flecs::entity(m_id).set_stage(e.m_world); + return flecs::entity(id_).set_stage(e.world_); } inline flecs::entity entity_view::set_stage(world_t *stage) { - return flecs::entity(stage, m_id); + return flecs::entity(stage, id_); } inline flecs::type entity_view::type() const { - return flecs::type(m_world, ecs_get_type(m_world, m_id)); + return flecs::type(world_, ecs_get_type(world_, id_)); } inline flecs::table entity_view::table() const { - return flecs::table(m_world, ecs_get_table(m_world, m_id)); + return flecs::table(world_, ecs_get_table(world_, id_)); } inline flecs::table_range entity_view::range() const { - ecs_record_t *r = ecs_record_find(m_world, m_id); + ecs_record_t *r = ecs_record_find(world_, id_); if (r) { - return flecs::table_range(m_world, r->table, + return flecs::table_range(world_, r->table, ECS_RECORD_TO_ROW(r->row), 1); } return flecs::table_range(); @@ -115,7 +115,7 @@ inline flecs::table_range entity_view::range() const { template inline void entity_view::each(const Func& func) const { - const ecs_type_t *type = ecs_get_type(m_world, m_id); + const ecs_type_t *type = ecs_get_type(world_, id_); if (!type) { return; } @@ -125,24 +125,17 @@ inline void entity_view::each(const Func& func) const { for (int i = 0; i < count; i ++) { ecs_id_t id = ids[i]; - flecs::id ent(m_world, id); + flecs::id ent(world_, id); func(ent); - - // Union object is not stored in type, so handle separately - if (ECS_PAIR_FIRST(id) == EcsUnion) { - ent = flecs::id(m_world, ECS_PAIR_SECOND(id), - ecs_get_target(m_world, m_id, ECS_PAIR_SECOND(id), 0)); - func(ent); - } } } template inline void entity_view::each(flecs::id_t pred, flecs::id_t obj, const Func& func) const { flecs::world_t *real_world = const_cast( - ecs_get_world(m_world)); + ecs_get_world(world_)); - const ecs_table_t *table = ecs_get_table(m_world, m_id); + const ecs_table_t *table = ecs_get_table(world_, id_); if (!table) { return; } @@ -162,7 +155,7 @@ inline void entity_view::each(flecs::id_t pred, flecs::id_t obj, const Func& fun while (-1 != (cur = ecs_search_offset(real_world, table, cur, pattern, 0))) { - flecs::id ent(m_world, ids[cur]); + flecs::id ent(world_, ids[cur]); func(ent); cur ++; } @@ -178,59 +171,59 @@ inline void entity_view::each(const flecs::entity_view& rel, const Func& func) c template ::value > > inline bool entity_view::get(const Func& func) const { - return _::entity_with_delegate::invoke_get(m_world, m_id, func); + return _::entity_with_delegate::invoke_get(world_, id_, func); } inline flecs::entity entity_view::lookup(const char *path, bool search_path) const { - ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, "invalid lookup from null handle"); - auto id = ecs_lookup_path_w_sep(m_world, m_id, path, "::", "::", search_path); - return flecs::entity(m_world, id); + ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, "invalid lookup from null handle"); + auto id = ecs_lookup_path_w_sep(world_, id_, path, "::", "::", search_path); + return flecs::entity(world_, id); } inline flecs::entity entity_view::clone(bool copy_value, flecs::entity_t dst_id) const { if (!dst_id) { - dst_id = ecs_new_id(m_world); + dst_id = ecs_new(world_); } - flecs::entity dst = flecs::entity(m_world, dst_id); - ecs_clone(m_world, dst_id, m_id, copy_value); + flecs::entity dst = flecs::entity(world_, dst_id); + ecs_clone(world_, dst_id, id_, copy_value); return dst; } // Entity mixin implementation template inline flecs::entity world::entity(Args &&... args) const { - return flecs::entity(m_world, FLECS_FWD(args)...); + return flecs::entity(world_, FLECS_FWD(args)...); } template ::value >> inline flecs::id world::id(E value) const { - flecs::entity_t constant = enum_type(m_world).entity(value); - return flecs::id(m_world, constant); + flecs::entity_t constant = enum_type(world_).entity(value); + return flecs::id(world_, constant); } template ::value >> inline flecs::entity world::entity(E value) const { - flecs::entity_t constant = enum_type(m_world).entity(value); - return flecs::entity(m_world, constant); + flecs::entity_t constant = enum_type(world_).entity(value); + return flecs::entity(world_, constant); } template inline flecs::entity world::entity(const char *name) const { - return flecs::entity(m_world, - _::cpp_type::id_explicit(m_world, name, true, 0, false) ); + return flecs::entity(world_, + _::type::id_explicit(world_, name, true, 0, false) ); } template inline flecs::entity world::prefab(Args &&... args) const { - flecs::entity result = flecs::entity(m_world, FLECS_FWD(args)...); + flecs::entity result = flecs::entity(world_, FLECS_FWD(args)...); result.add(flecs::Prefab); return result; } template inline flecs::entity world::prefab(const char *name) const { - flecs::entity result = flecs::component(m_world, name, true); + flecs::entity result = flecs::component(world_, name, true); result.add(flecs::Prefab); return result; } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/entity/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/entity/mixin.inl index 6c7376e83..861f3a3f8 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/entity/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/entity/mixin.inl @@ -5,48 +5,48 @@ /** Create an entity. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template flecs::entity entity(Args &&... args) const; /** Convert enum constant to entity. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template ::value > = 0> flecs::id id(E value) const; /** Convert enum constant to entity. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template ::value > = 0> flecs::entity entity(E value) const; /** Create a prefab. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template flecs::entity prefab(Args &&... args) const; /** Create an entity that's associated with a type. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template flecs::entity entity(const char *name = nullptr) const; /** Create a prefab that's associated with a type. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template flecs::entity prefab(const char *name = nullptr) const; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/enum/entity_view.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/enum/entity_view.inl index 0ca814f44..ae5a448fe 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/enum/entity_view.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/enum/entity_view.inl @@ -5,8 +5,8 @@ /** Convert entity to enum constant. * - * \memberof flecs::entity_view - * \ingroup cpp_entities + * @memberof flecs::entity_view + * @ingroup cpp_entities */ template E to_constant() const; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/enum/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/enum/impl.hpp index 76e04039c..b3523487c 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/enum/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/enum/impl.hpp @@ -16,8 +16,8 @@ inline E entity_view::to_constant() const { template ::value >> inline flecs::entity world::to_entity(E constant) const { - const auto& et = enum_type(m_world); - return flecs::entity(m_world, et.entity(constant)); + const auto& et = enum_type(world_); + return flecs::entity(world_, et.entity(constant)); } } \ No newline at end of file diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/enum/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/enum/mixin.inl index 579bd7be6..7a1a65180 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/enum/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/enum/mixin.inl @@ -5,8 +5,8 @@ /** Convert enum constant to entity. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template ::value > = 0> flecs::entity to_entity(E constant) const; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/event/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/event/builder.hpp index 410fe758e..00ba00d35 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/event/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/event/builder.hpp @@ -10,7 +10,7 @@ namespace flecs { /** - * \ingroup cpp_addons_event + * @ingroup cpp_addons_event * @{ */ @@ -18,20 +18,20 @@ namespace flecs { template struct event_builder_base { event_builder_base(flecs::world_t *world, flecs::entity_t event) - : m_world(world) - , m_desc{} - , m_ids{} - , m_ids_array{} + : world_(world) + , desc_{} + , ids_{} + , ids_array_{} { - m_desc.event = event; + desc_.event = event; } /** Add component to emit for */ template Base& id() { - m_ids.array = m_ids_array; - m_ids.array[m_ids.count] = _::cpp_type().id(m_world); - m_ids.count ++; + ids_.array = ids_array_; + ids_.array[ids_.count] = _::type().id(world_); + ids_.count ++; return *this; } @@ -43,8 +43,8 @@ struct event_builder_base { template Base& id() { return id( - ecs_pair(_::cpp_type::id(this->m_world), - _::cpp_type::id(this->m_world))); + ecs_pair(_::type::id(this->world_), + _::type::id(this->world_))); } /** @@ -54,7 +54,7 @@ struct event_builder_base { */ template Base& id(entity_t second) { - return id(ecs_pair(_::cpp_type::id(this->m_world), second)); + return id(ecs_pair(_::type::id(this->world_), second)); } /** @@ -66,59 +66,66 @@ struct event_builder_base { return id(ecs_pair(first, second)); } + template ::value> = 0> + Base& id(Enum value) { + const auto& et = enum_type(this->world_); + flecs::entity_t target = et.entity(value); + return id(et.entity(), target); + } + /** Add (component) id to emit for */ Base& id(flecs::id_t id) { - m_ids.array = m_ids_array; - m_ids.array[m_ids.count] = id; - m_ids.count ++; + ids_.array = ids_array_; + ids_.array[ids_.count] = id; + ids_.count ++; return *this; } /** Set entity for which to emit event */ Base& entity(flecs::entity_t e) { - m_desc.entity = e; + desc_.entity = e; return *this; } /* Set table for which to emit event */ Base& table(flecs::table_t *t, int32_t offset = 0, int32_t count = 0) { - m_desc.table = t; - m_desc.offset = offset; - m_desc.count = count; + desc_.table = t; + desc_.offset = offset; + desc_.count = count; return *this; } /* Set event data */ Base& ctx(const E* ptr) { - m_desc.const_param = ptr; + desc_.const_param = ptr; return *this; } /* Set event data */ Base& ctx(E* ptr) { - m_desc.param = ptr; + desc_.param = ptr; return *this; } void emit() { - m_ids.array = m_ids_array; - m_desc.ids = &m_ids; - m_desc.observable = const_cast(ecs_get_world(m_world)); - ecs_emit(m_world, &m_desc); + ids_.array = ids_array_; + desc_.ids = &ids_; + desc_.observable = const_cast(ecs_get_world(world_)); + ecs_emit(world_, &desc_); } void enqueue() { - m_ids.array = m_ids_array; - m_desc.ids = &m_ids; - m_desc.observable = const_cast(ecs_get_world(m_world)); - ecs_enqueue(m_world, &m_desc); + ids_.array = ids_array_; + desc_.ids = &ids_; + desc_.observable = const_cast(ecs_get_world(world_)); + ecs_enqueue(world_, &desc_); } protected: - flecs::world_t *m_world; - ecs_event_desc_t m_desc; - flecs::type_t m_ids; - flecs::id_t m_ids_array[ECS_EVENT_DESC_ID_COUNT_MAX]; + flecs::world_t *world_; + ecs_event_desc_t desc_; + flecs::type_t ids_; + flecs::id_t ids_array_[ECS_EVENT_DESC_ID_COUNT_MAX]; private: operator Base&() { @@ -140,13 +147,13 @@ struct event_builder_typed : event_builder_base, E> { /* Set event data */ Class& ctx(const E& ptr) { - this->m_desc.const_param = &ptr; + this->desc_.const_param = &ptr; return *this; } /* Set event data */ Class& ctx(E&& ptr) { - this->m_desc.param = &ptr; + this->desc_.param = &ptr; return *this; } }; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/event/entity_builder.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/event/entity_builder.inl index cd4f3d581..68e01bc62 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/event/entity_builder.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/event/entity_builder.inl @@ -1,38 +1,38 @@ /** - * @file addons/cpp/mixins/event/entity_builder.hpp + * @file addons/cpp/mixins/event/entity_builder.inl * @brief Event entity mixin. */ /** Observe event on entity * - * \memberof flecs::entity_builder + * @memberof flecs::entity_builder * * @param evt The event id. * @param callback The observer callback. * @return Event builder. */ template -Self& observe(flecs::entity_t evt, Func&& callback); +const Self& observe(flecs::entity_t evt, Func&& callback) const; /** Observe event on entity * - * \memberof flecs::entity_builder + * @memberof flecs::entity_builder * * @tparam Evt The event type. * @param callback The observer callback. * @return Event builder. */ template -Self& observe(Func&& callback); +const Self& observe(Func&& callback) const; /** Observe event on entity * - * \memberof flecs::entity_builder + * @memberof flecs::entity_builder * * @param callback The observer callback. * @return Event builder. */ template -Self& observe(Func&& callback); +const Self& observe(Func&& callback) const; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/event/entity_view.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/event/entity_view.inl index eb2b44e35..7462a517c 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/event/entity_view.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/event/entity_view.inl @@ -1,51 +1,51 @@ /** - * @file addons/cpp/mixins/event/entity_builder.hpp + * @file addons/cpp/mixins/event/entity_view.inl * @brief Event entity mixin. */ /** Emit event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @param evt The event to emit. */ -void emit(flecs::entity_t evt) { - flecs::world(m_world) +void emit(flecs::entity_t evt) const { + flecs::world(world_) .event(evt) - .entity(m_id) + .entity(id_) .emit(); } /** Emit event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @param evt The event to emit. */ -void emit(flecs::entity evt); +void emit(flecs::entity evt) const; /** Emit event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @tparam Evt The event to emit. */ template ::value> = 0> -void emit() { - this->emit(_::cpp_type::id(m_world)); +void emit() const { + this->emit(_::type::id(world_)); } /** Emit event with payload for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @tparam Evt The event to emit. */ template ::value> = 0> -void emit(const Evt& payload) { - flecs::world(m_world) - .event(_::cpp_type::id(m_world)) - .entity(m_id) +void emit(const Evt& payload) const { + flecs::world(world_) + .event(_::type::id(world_)) + .entity(id_) .ctx(&payload) .emit(); } @@ -53,47 +53,47 @@ void emit(const Evt& payload) { /** Enqueue event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @param evt The event to enqueue. */ -void enqueue(flecs::entity_t evt) { - flecs::world(m_world) +void enqueue(flecs::entity_t evt) const { + flecs::world(world_) .event(evt) - .entity(m_id) + .entity(id_) .enqueue(); } /** Enqueue event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @param evt The event to enqueue. */ -void enqueue(flecs::entity evt); +void enqueue(flecs::entity evt) const; /** Enqueue event for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @tparam Evt The event to enqueue. */ template ::value> = 0> -void enqueue() { - this->enqueue(_::cpp_type::id(m_world)); +void enqueue() const { + this->enqueue(_::type::id(world_)); } /** Enqueue event with payload for entity. * - * \memberof flecs::entity_view + * @memberof flecs::entity_view * * @tparam Evt The event to enqueue. */ template ::value> = 0> -void enqueue(const Evt& payload) { - flecs::world(m_world) - .event(_::cpp_type::id(m_world)) - .entity(m_id) +void enqueue(const Evt& payload) const { + flecs::world(world_) + .event(_::type::id(world_)) + .entity(id_) .ctx(&payload) .enqueue(); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/event/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/event/impl.hpp index 299769ceb..785e0070b 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/event/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/event/impl.hpp @@ -13,12 +13,12 @@ namespace flecs // Mixin implementation inline flecs::event_builder world::event(flecs::entity_t evt) const { - return flecs::event_builder(m_world, evt); + return flecs::event_builder(world_, evt); } template inline flecs::event_builder_typed world::event() const { - return flecs::event_builder_typed(m_world, _::cpp_type().id(m_world)); + return flecs::event_builder_typed(world_, _::type().id(world_)); } namespace _ { @@ -27,16 +27,16 @@ namespace _ { flecs::entity_t event, flecs::entity_t entity, ecs_iter_action_t callback, - void *binding_ctx, - ecs_ctx_free_t binding_ctx_free) + void *callback_ctx, + ecs_ctx_free_t callback_ctx_free) { ecs_observer_desc_t desc = {}; desc.events[0] = event; - desc.filter.terms[0].id = EcsAny; - desc.filter.terms[0].src.id = entity; + desc.query.terms[0].id = EcsAny; + desc.query.terms[0].src.id = entity; desc.callback = callback; - desc.binding_ctx = binding_ctx; - desc.binding_ctx_free = binding_ctx_free; + desc.callback_ctx = callback_ctx; + desc.callback_ctx_free = callback_ctx_free; flecs::entity_t o = ecs_observer_init(world, &desc); ecs_add_pair(world, o, EcsChildOf, entity); @@ -52,7 +52,7 @@ namespace _ { { using Delegate = _::entity_observer_delegate; auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); - entity_observer_create(world, _::cpp_type::id(world), entity, Delegate::run, ctx, + entity_observer_create(world, _::type::id(world), entity, Delegate::run, ctx, reinterpret_cast(_::free_obj)); } @@ -64,7 +64,7 @@ namespace _ { { using Delegate = _::entity_payload_observer_delegate; auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); - entity_observer_create(world, _::cpp_type::id(world), entity, Delegate::run, ctx, + entity_observer_create(world, _::type::id(world), entity, Delegate::run, ctx, reinterpret_cast(_::free_obj)); } }; @@ -72,11 +72,11 @@ namespace _ { template template -inline Self& entity_builder::observe(flecs::entity_t evt, Func&& f) { +inline const Self& entity_builder::observe(flecs::entity_t evt, Func&& f) const { using Delegate = _::entity_observer_delegate; auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); - _::entity_observer_create(m_world, evt, m_id, Delegate::run, ctx, + _::entity_observer_create(world_, evt, id_, Delegate::run, ctx, reinterpret_cast(_::free_obj)); return to_base(); @@ -84,23 +84,23 @@ inline Self& entity_builder::observe(flecs::entity_t evt, Func&& f) { template template -inline Self& entity_builder::observe(Func&& f) { +inline const Self& entity_builder::observe(Func&& f) const { _::entity_observer_factory::template create( - m_world, m_id, FLECS_FWD(f)); + world_, id_, FLECS_FWD(f)); return to_base(); } template template -inline Self& entity_builder::observe(Func&& f) { +inline const Self& entity_builder::observe(Func&& f) const { return this->observe<_::event_from_func_t>(FLECS_FWD(f)); } -inline void entity_view::emit(flecs::entity evt) { +inline void entity_view::emit(flecs::entity evt) const { this->emit(evt.id()); } -inline void entity_view::enqueue(flecs::entity evt) { +inline void entity_view::enqueue(flecs::entity evt) const { this->enqueue(evt.id()); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/event/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/event/mixin.inl index ac0f85b56..32b31ee50 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/event/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/event/mixin.inl @@ -5,25 +5,25 @@ /** * @defgroup cpp_addons_event Events - * @brief API for emitting events. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * API for emitting events. + * * @{ */ /** Create a new event. - * - * \memberof flecs::world - * + * + * @memberof flecs::world + * * @param evt The event id. * @return Event builder. */ flecs::event_builder event(flecs::entity_t evt) const; /** Create a new event. - * - * \memberof flecs::world - * + * + * @memberof flecs::world + * * @tparam E The event type. * @return Event builder. */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/filter/builder.hpp deleted file mode 100644 index 9e3e720e7..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/builder.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @file addons/cpp/mixins/filter/builder.hpp - * @brief Filter builder. - */ - -#pragma once - -#include "../../utils/builder.hpp" -#include "builder_i.hpp" - -namespace flecs { -namespace _ { - template - using filter_builder_base = builder< - filter, ecs_filter_desc_t, filter_builder, - filter_builder_i, Components ...>; -} - -/** Filter builder. - * - * \ingroup cpp_filters - */ -template -struct filter_builder final : _::filter_builder_base { - filter_builder(flecs::world_t* world, const char *name = nullptr) - : _::filter_builder_base(world) - { - _::sig(world).populate(this); - if (name != nullptr) { - ecs_entity_desc_t entity_desc = {}; - entity_desc.name = name; - entity_desc.sep = "::"; - entity_desc.root_sep = "::"; - this->m_desc.entity = ecs_entity_init(world, &entity_desc); - } - } - - template - void each(Func&& func) { - this->build().each(FLECS_FWD(func)); - } -}; - -} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/builder_i.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/filter/builder_i.hpp deleted file mode 100644 index b70f4dd0e..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/builder_i.hpp +++ /dev/null @@ -1,263 +0,0 @@ -/** - * @file addons/cpp/mixins/filter/builder_i.hpp - * @brief Filter builder interface. - */ - -#pragma once - -#include "../term/builder_i.hpp" - -namespace flecs -{ - -/** Filter builder interface. - * - * \ingroup cpp_filters - */ -template -struct filter_builder_i : term_builder_i { - filter_builder_i(ecs_filter_desc_t *desc, int32_t term_index = 0) - : m_term_index(term_index) - , m_expr_count(0) - , m_desc(desc) { } - - Base& instanced() { - m_desc->instanced = true; - return *this; - } - - Base& filter_flags(ecs_flags32_t flags) { - m_desc->flags |= flags; - return *this; - } - - Base& expr(const char *expr) { - ecs_check(m_expr_count == 0, ECS_INVALID_OPERATION, - "filter_builder::expr() called more than once"); - m_desc->expr = expr; - m_expr_count ++; - - error: - return *this; - } - - /* With/without shorthand notation. */ - - template - Base& with(Args&&... args) { - return this->term(FLECS_FWD(args)...).inout_none(); - } - - template - Base& with(Args&&... args) { - return this->term(FLECS_FWD(args)...).inout_none(); - } - - template - Base& with() { - return this->term().inout_none(); - } - - template - Base& without(Args&&... args) { - return this->term(FLECS_FWD(args)...).not_(); - } - - template - Base& without(Args&&... args) { - return this->term(FLECS_FWD(args)...).not_(); - } - - template - Base& without() { - return this->term().not_(); - } - - /* Write/read shorthand notation */ - - Base& write() { - term_builder_i::write(); - return *this; - } - - template - Base& write(Args&&... args) { - return this->term(FLECS_FWD(args)...).write(); - } - - template - Base& write(Args&&... args) { - return this->term(FLECS_FWD(args)...).write(); - } - - template - Base& write() { - return this->term().write(); - } - - Base& read() { - term_builder_i::read(); - return *this; - } - - template - Base& read(Args&&... args) { - return this->term(FLECS_FWD(args)...).read(); - } - - template - Base& read(Args&&... args) { - return this->term(FLECS_FWD(args)...).read(); - } - - template - Base& read() { - return this->term().read(); - } - - /* Scope_open/scope_close shorthand notation. */ - Base& scope_open() { - return this->with(flecs::ScopeOpen).entity(0); - } - - Base& scope_close() { - return this->with(flecs::ScopeClose).entity(0); - } - - /* Term notation for more complex query features */ - - Base& term() { - if (this->m_term) { - ecs_check(ecs_term_is_initialized(this->m_term), - ECS_INVALID_OPERATION, - "filter_builder::term() called without initializing term"); - } - - if (m_term_index >= FLECS_TERM_DESC_MAX) { - if (m_term_index == FLECS_TERM_DESC_MAX) { - m_desc->terms_buffer = ecs_os_calloc_n( - ecs_term_t, m_term_index + 1); - ecs_os_memcpy_n(m_desc->terms_buffer, m_desc->terms, - ecs_term_t, m_term_index); - ecs_os_memset_n(m_desc->terms, 0, - ecs_term_t, FLECS_TERM_DESC_MAX); - } else { - m_desc->terms_buffer = ecs_os_realloc_n(m_desc->terms_buffer, - ecs_term_t, m_term_index + 1); - } - - m_desc->terms_buffer_count = m_term_index + 1; - - this->set_term(&m_desc->terms_buffer[m_term_index]); - } else { - this->set_term(&m_desc->terms[m_term_index]); - } - - m_term_index ++; - - error: - return *this; - } - - Base& term_at(int32_t term_index) { - ecs_assert(term_index > 0, ECS_INVALID_PARAMETER, NULL); - int32_t prev_index = m_term_index; - m_term_index = term_index - 1; - this->term(); - m_term_index = prev_index; - ecs_assert(ecs_term_is_initialized(this->m_term), - ECS_INVALID_PARAMETER, NULL); - return *this; - } - - Base& arg(int32_t term_index) { - return this->term_at(term_index); - } - - template - Base& term() { - this->term(); - *this->m_term = flecs::term(_::cpp_type::id(this->world_v())).move(); - this->m_term->inout = static_cast( - _::type_to_inout()); - return *this; - } - - Base& term(id_t id) { - this->term(); - *this->m_term = flecs::term(id).move(); - return *this; - } - - Base& term(const char *name) { - this->term(); - *this->m_term = flecs::term().first(name).move(); - return *this; - } - - Base& term(const char *first, const char *second) { - this->term(); - *this->m_term = flecs::term().first(first).second(second).move(); - return *this; - } - - Base& term(entity_t r, entity_t o) { - this->term(); - *this->m_term = flecs::term(r, o).move(); - return *this; - } - - Base& term(entity_t r, const char *o) { - this->term(); - *this->m_term = flecs::term(r).second(o).move(); - return *this; - } - - template - Base& term(id_t o) { - return this->term(_::cpp_type::id(this->world_v()), o); - } - - template - Base& term(const char *second) { - return this->term(_::cpp_type::id(this->world_v())).second(second); - } - - template - Base& term() { - return this->term(_::cpp_type::id(this->world_v())); - } - - template ::value > = 0> - Base& term(E value) { - flecs::entity_t r = _::cpp_type::id(this->world_v()); - auto o = enum_type(this->world_v()).entity(value); - return this->term(r, o); - } - - Base& term(flecs::term& term) { - this->term(); - *this->m_term = term.move(); - return *this; - } - - Base& term(flecs::term&& term) { - this->term(); - *this->m_term = term.move(); - return *this; - } - -protected: - virtual flecs::world_t* world_v() = 0; - int32_t m_term_index; - int32_t m_expr_count; - -private: - operator Base&() { - return *static_cast(this); - } - - ecs_filter_desc_t *m_desc; -}; - -} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/filter/decl.hpp deleted file mode 100644 index c71ac0436..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/decl.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file addons/cpp/mixins/filter/decl.hpp - * @brief Filter declarations. - */ - -#pragma once - -namespace flecs { - -/** - * @defgroup cpp_core_filters Filters - * @brief Filters are cheaper to create, but slower to iterate than flecs::query. - * - * \ingroup cpp_core - * @{ - */ - -struct filter_base; - -template -struct filter; - -template -struct filter_builder; - -/** @} */ - -} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/filter/impl.hpp deleted file mode 100644 index b91e24eb7..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/impl.hpp +++ /dev/null @@ -1,272 +0,0 @@ -/** - * @file addons/cpp/mixins/filter/impl.hpp - * @brief Filter implementation. - */ - -#pragma once - -#include "builder.hpp" - -namespace flecs -{ - -struct filter_base { - filter_base() - : m_world(nullptr) - , m_filter({}) - , m_filter_ptr(nullptr) { } - - filter_base(world_t *world, const ecs_filter_t *filter) - : m_world(world) - , m_filter({}) - , m_filter_ptr(filter) { } - - filter_base(world_t *world, ecs_filter_t *filter) - : m_world(world) - , m_filter_ptr(&m_filter) { - ecs_filter_move(&m_filter, filter); - } - - filter_base(world_t *world, ecs_filter_desc_t *desc) - : m_world(world) - { - desc->storage = &m_filter; - - if (ecs_filter_init(world, desc) == NULL) { - ecs_abort(ECS_INVALID_PARAMETER, NULL); - } - - if (desc->terms_buffer) { - ecs_os_free(desc->terms_buffer); - } - - m_filter_ptr = &m_filter; - } - - filter_base(const filter_base& obj) { - this->m_world = obj.m_world; - if (obj.m_filter_ptr) { - this->m_filter_ptr = &this->m_filter; - } else { - this->m_filter_ptr = nullptr; - } - ecs_filter_copy(&m_filter, &obj.m_filter); - } - - filter_base& operator=(const filter_base& obj) { - this->m_world = obj.m_world; - if (obj.m_filter_ptr) { - this->m_filter_ptr = &this->m_filter; - } else { - this->m_filter_ptr = nullptr; - } - ecs_filter_copy(&m_filter, &obj.m_filter); - return *this; - } - - filter_base(filter_base&& obj) { - this->m_world = obj.m_world; - if (obj.m_filter_ptr) { - this->m_filter_ptr = &this->m_filter; - } else { - this->m_filter_ptr = nullptr; - } - ecs_filter_move(&m_filter, &obj.m_filter); - } - - filter_base& operator=(filter_base&& obj) { - this->m_world = obj.m_world; - if (obj.m_filter_ptr) { - this->m_filter_ptr = &this->m_filter; - } else { - this->m_filter_ptr = nullptr; - } - ecs_filter_move(&m_filter, &obj.m_filter); - return *this; - } - - flecs::entity entity() { - return flecs::entity(m_world, ecs_get_entity(m_filter_ptr)); - } - - operator const flecs::filter_t*() const { - return m_filter_ptr; - } - - /** Free the filter. - */ - ~filter_base() { - if ((&m_filter == m_filter_ptr) && m_filter_ptr) { - ecs_filter_fini(&m_filter); - } - } - - template - void each_term(const Func& func) { - for (int i = 0; i < m_filter_ptr->term_count; i ++) { - flecs::term t(m_world, m_filter_ptr->terms[i]); - func(t); - t.reset(); // prevent freeing resources - } - } - - flecs::term term(int32_t index) { - return flecs::term(m_world, m_filter_ptr->terms[index]); - } - - int32_t field_count() { - return m_filter_ptr->term_count; - } - - flecs::string str() { - char *result = ecs_filter_str(m_world, m_filter_ptr); - return flecs::string(result); - } - - operator filter<>() const; - -protected: - world_t *m_world = nullptr; - filter_t m_filter = ECS_FILTER_INIT; - const filter_t *m_filter_ptr; -}; - -template -struct filter : filter_base, iterable { -private: - using Terms = typename _::term_ptrs::array; - -public: - using filter_base::filter_base; - - filter() : filter_base() { } // necessary not to confuse msvc - - filter(const filter& obj) : filter_base(obj) { } - - filter& operator=(const filter& obj) { - filter_base::operator=(obj); - return *this; - } - - filter(filter&& obj) : filter_base(FLECS_MOV(obj)) { } - - filter& operator=(filter&& obj) { - filter_base::operator=(FLECS_FWD(obj)); - return *this; - } - -private: - ecs_iter_t get_iter(flecs::world_t *world) const override { - if (!world) { - world = m_world; - } - return ecs_filter_iter(world, m_filter_ptr); - } - - ecs_iter_next_action_t next_action() const override { - return ecs_filter_next; - } - - ecs_iter_next_action_t next_each_action() const override { - return ecs_filter_next_instanced; - } -}; - -// World mixin implementation -template -inline flecs::filter world::filter(Args &&... args) const { - return flecs::filter_builder(m_world, FLECS_FWD(args)...) - .build(); -} - -template -inline flecs::filter_builder world::filter_builder(Args &&... args) const { - return flecs::filter_builder(m_world, FLECS_FWD(args)...); -} - -// world::each -namespace _ { - -// Each with entity parameter -template -struct filter_delegate_w_ent; - -template -struct filter_delegate_w_ent > -{ - filter_delegate_w_ent(const flecs::world& world, Func&& func) { - auto f = world.filter(); - f.each(FLECS_MOV(func)); - } -}; - -// Each without entity parameter -template -struct filter_delegate_no_ent; - -template -struct filter_delegate_no_ent > -{ - filter_delegate_no_ent(const flecs::world& world, Func&& func) { - auto f = world.filter(); - f.each(FLECS_MOV(func)); - } -}; - -// Switch between function with & without entity parameter -template -struct filter_delegate; - -template -struct filter_delegate, flecs::entity>::value> > { - filter_delegate(const flecs::world& world, Func&& func) { - filter_delegate_w_ent>(world, FLECS_MOV(func)); - } -}; - -template -struct filter_delegate, flecs::entity>::value> > { - filter_delegate(const flecs::world& world, Func&& func) { - filter_delegate_no_ent>(world, FLECS_MOV(func)); - } -}; - -} - -template -inline void world::each(Func&& func) const { - _::filter_delegate f_delegate(*this, FLECS_MOV(func)); -} - -template -inline void world::each(Func&& func) const { - ecs_term_t t = {}; - t.id = _::cpp_type::id(); - ecs_iter_t it = ecs_term_iter(m_world, &t); - - while (ecs_term_next(&it)) { - _::each_delegate(func).invoke(&it); - } -} - -template -inline void world::each(flecs::id_t term_id, Func&& func) const { - ecs_term_t t = {}; - t.id = term_id; - ecs_iter_t it = ecs_term_iter(m_world, &t); - - while (ecs_term_next(&it)) { - _::each_delegate(func).invoke(&it); - } -} - -// filter_base implementation -inline filter_base::operator flecs::filter<> () const { - flecs::filter<> f; - ecs_filter_copy(&f.m_filter, &this->m_filter); - f.m_filter_ptr = &f.m_filter; - f.m_world = this->m_world; - return f; -} - -} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/filter/mixin.inl deleted file mode 100644 index c6c5e4e71..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/filter/mixin.inl +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file addons/cpp/mixins/filter/mixin.inl - * @brief Filter world mixin. - */ - -/** - * \memberof flecs::world - * \ingroup cpp_core_filters - */ - -/** Create a filter. - * - * @see ecs_filter_init - */ -template -flecs::filter filter(Args &&... args) const; - -/** Create a filter builder. - * - * @see ecs_filter_init - */ -template -flecs::filter_builder filter_builder(Args &&... args) const; - -/** Iterate over all entities with components in argument list of function. - * The function parameter must match the following signature: - * void(*)(T&, U&, ...) or - * void(*)(flecs::entity, T&, U&, ...) - * - */ -template -void each(Func&& func) const; - -/** Iterate over all entities with provided component. - * The function parameter must match the following signature: - * void(*)(T&) or - * void(*)(flecs::entity, T&) - * - */ -template -void each(Func&& func) const; - -/** Iterate over all entities with provided (component) id. */ -template -void each(flecs::id_t term_id, Func&& func) const; - -/** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/id/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/id/decl.hpp index d47aad08f..f9850dea6 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/id/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/id/decl.hpp @@ -12,9 +12,9 @@ struct entity; /** * @defgroup cpp_ids Ids - * @brief Class for working with entity, component, tag and pair ids. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Class for working with entity, component, tag and pair ids. + * * @{ */ @@ -22,46 +22,50 @@ struct entity; * A flecs id is an identifier that can be added to entities. Ids can be: * - entities (including components, tags) * - pair ids - * - entities with id flags set (like flecs::Override, flecs::Toggle) + * - entities with id flags set (like flecs::AUTO_OVERRIDE, flecs::TOGGLE) */ struct id { id() - : m_world(nullptr) - , m_id(0) { } + : world_(nullptr) + , id_(0) { } - explicit id(flecs::id_t value) - : m_world(nullptr) - , m_id(value) { } + explicit id(flecs::id_t value) + : world_(nullptr) + , id_(value) { } explicit id(flecs::world_t *world, flecs::id_t value = 0) - : m_world(world) - , m_id(value) { } + : world_(world) + , id_(value) { } explicit id(flecs::world_t *world, flecs::id_t first, flecs::id_t second) - : m_world(world) - , m_id(ecs_pair(first, second)) { } + : world_(world) + , id_(ecs_pair(first, second)) { } + + explicit id(flecs::world_t *world, const char *expr) + : world_(world) + , id_(ecs_id_from_str(world, expr)) { } explicit id(flecs::id_t first, flecs::id_t second) - : m_world(nullptr) - , m_id(ecs_pair(first, second)) { } + : world_(nullptr) + , id_(ecs_pair(first, second)) { } explicit id(const flecs::id& first, const flecs::id& second) - : m_world(first.m_world) - , m_id(ecs_pair(first.m_id, second.m_id)) { } + : world_(first.world_) + , id_(ecs_pair(first.id_, second.id_)) { } /** Test if id is pair (has first, second) */ bool is_pair() const { - return (m_id & ECS_ID_FLAGS_MASK) == flecs::Pair; + return (id_ & ECS_ID_FLAGS_MASK) == flecs::PAIR; } /** Test if id is a wildcard */ bool is_wildcard() const { - return ecs_id_is_wildcard(m_id); + return ecs_id_is_wildcard(id_); } /** Test if id is entity */ bool is_entity() const { - return !(m_id & ECS_ID_FLAGS_MASK); + return !(id_ & ECS_ID_FLAGS_MASK); } /** Return id as entity (only allowed when id is valid entity) */ @@ -77,19 +81,19 @@ struct id { flecs::entity remove_flags() const; /** Return id without role */ - flecs::entity remove_generation() const; + flecs::entity remove_generation() const; /** Return component type of id */ flecs::entity type_id() const; /** Test if id has specified role */ bool has_flags(flecs::id_t flags) const { - return ((m_id & flags) == flags); + return ((id_ & flags) == flags); } /** Test if id has any role */ bool has_flags() const { - return (m_id & ECS_ID_FLAGS_MASK) != 0; + return (id_ & ECS_ID_FLAGS_MASK) != 0; } /** Return id flags set on id */ @@ -100,7 +104,7 @@ struct id { if (!is_pair()) { return false; } - return ECS_PAIR_FIRST(m_id) == first; + return ECS_PAIR_FIRST(id_) == first; } /** Get first element from a pair. @@ -117,30 +121,30 @@ struct id { /* Convert id to string */ flecs::string str() const { - return flecs::string(ecs_id_str(m_world, m_id)); + return flecs::string(ecs_id_str(world_, id_)); } /** Convert role of id to string. */ flecs::string flags_str() const { - return flecs::string_view( ecs_id_flag_str(m_id & ECS_ID_FLAGS_MASK)); + return flecs::string_view( ecs_id_flag_str(id_ & ECS_ID_FLAGS_MASK)); } /** Return flecs::id_t value */ flecs::id_t raw_id() const { - return m_id; + return id_; } operator flecs::id_t() const { - return m_id; + return id_; } flecs::world world() const; - + protected: /* World is optional, but guarantees that entity identifiers extracted from * the id are valid */ - flecs::world_t *m_world; - flecs::id_t m_id; + flecs::world_t *world_; + flecs::id_t id_; }; /** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/id/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/id/impl.hpp index b043ce676..3e3736661 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/id/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/id/impl.hpp @@ -10,57 +10,57 @@ namespace flecs { inline flecs::entity id::entity() const { ecs_assert(!is_pair(), ECS_INVALID_OPERATION, NULL); ecs_assert(!flags(), ECS_INVALID_OPERATION, NULL); - return flecs::entity(m_world, m_id); + return flecs::entity(world_, id_); } inline flecs::entity id::flags() const { - return flecs::entity(m_world, m_id & ECS_ID_FLAGS_MASK); + return flecs::entity(world_, id_ & ECS_ID_FLAGS_MASK); } inline flecs::entity id::first() const { ecs_assert(is_pair(), ECS_INVALID_OPERATION, NULL); - flecs::entity_t e = ECS_PAIR_FIRST(m_id); - if (m_world) { - return flecs::entity(m_world, ecs_get_alive(m_world, e)); + flecs::entity_t e = ECS_PAIR_FIRST(id_); + if (world_) { + return flecs::entity(world_, ecs_get_alive(world_, e)); } else { return flecs::entity(e); } } inline flecs::entity id::second() const { - flecs::entity_t e = ECS_PAIR_SECOND(m_id); - if (m_world) { - return flecs::entity(m_world, ecs_get_alive(m_world, e)); + flecs::entity_t e = ECS_PAIR_SECOND(id_); + if (world_) { + return flecs::entity(world_, ecs_get_alive(world_, e)); } else { return flecs::entity(e); } } inline flecs::entity id::add_flags(flecs::id_t flags) const { - return flecs::entity(m_world, m_id | flags); + return flecs::entity(world_, id_ | flags); } inline flecs::entity id::remove_flags(flecs::id_t flags) const { (void)flags; - ecs_assert((m_id & ECS_ID_FLAGS_MASK) == flags, ECS_INVALID_PARAMETER, NULL); - return flecs::entity(m_world, m_id & ECS_COMPONENT_MASK); + ecs_assert((id_ & ECS_ID_FLAGS_MASK) == flags, ECS_INVALID_PARAMETER, NULL); + return flecs::entity(world_, id_ & ECS_COMPONENT_MASK); } inline flecs::entity id::remove_flags() const { - return flecs::entity(m_world, m_id & ECS_COMPONENT_MASK); + return flecs::entity(world_, id_ & ECS_COMPONENT_MASK); } inline flecs::entity id::remove_generation() const { - return flecs::entity(m_world, static_cast(m_id)); + return flecs::entity(world_, static_cast(id_)); } inline flecs::world id::world() const { - return flecs::world(m_world); + return flecs::world(world_); } inline flecs::entity id::type_id() const { - return flecs::entity(m_world, ecs_get_typeid(m_world, m_id)); + return flecs::entity(world_, ecs_get_typeid(world_, id_)); } @@ -68,21 +68,21 @@ inline flecs::entity id::type_id() const { template inline flecs::id world::id() const { - return flecs::id(m_world, _::cpp_type::id(m_world)); + return flecs::id(world_, _::type::id(world_)); } template inline flecs::id world::id(Args&&... args) const { - return flecs::id(m_world, FLECS_FWD(args)...); + return flecs::id(world_, FLECS_FWD(args)...); } template inline flecs::id world::pair() const { return flecs::id( - m_world, + world_, ecs_pair( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world))); + _::type::id(world_), + _::type::id(world_))); } template @@ -91,9 +91,9 @@ inline flecs::id world::pair(entity_t o) const { "cannot create nested pairs"); return flecs::id( - m_world, + world_, ecs_pair( - _::cpp_type::id(m_world), + _::type::id(world_), o)); } @@ -102,7 +102,7 @@ inline flecs::id world::pair(entity_t r, entity_t o) const { "cannot create nested pairs"); return flecs::id( - m_world, + world_, ecs_pair(r, o)); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/id/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/id/mixin.inl index 344bad9c1..32aae0a14 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/id/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/id/mixin.inl @@ -5,34 +5,34 @@ /** Get id from a type. * - * \memberof flecs::world + * @memberof flecs::world */ template flecs::id id() const; /** Id factory. * - * \memberof flecs::world + * @memberof flecs::world */ template flecs::id id(Args&&... args) const; /** Get pair id from relationship, object. * - * \memberof flecs::world + * @memberof flecs::world */ template flecs::id pair() const; /** Get pair id from relationship, object. * - * \memberof flecs::world + * @memberof flecs::world */ template flecs::id pair(entity_t o) const; /** Get pair id from relationship, object. * - * \memberof flecs::world + * @memberof flecs::world */ flecs::id pair(entity_t r, entity_t o) const; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/json/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/json/decl.hpp index 2579b4f98..bfa18d361 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/json/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/json/decl.hpp @@ -9,9 +9,9 @@ namespace flecs { /** * @defgroup cpp_addons_json Json - * @brief Functions for serializing to/from JSON. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Functions for serializing to/from JSON. + * * @{ */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity.inl index 0db9a5afc..a893e0618 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity.inl @@ -1,9 +1,9 @@ /** Deserialize entity to JSON. * - * \memberof flecs::entity - * \ingroup cpp_addons_json + * @memberof flecs::entity + * @ingroup cpp_addons_json */ const char* from_json(const char *json) { - return ecs_entity_from_json(m_world, m_id, json, nullptr); + return ecs_entity_from_json(world_, id_, json, nullptr); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity_builder.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity_builder.inl index 28a02bd11..28848abdc 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity_builder.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity_builder.inl @@ -5,99 +5,99 @@ /** Set component from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ -Self& set_json( +const Self& set_json( flecs::id_t e, const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { - flecs::entity_t type = ecs_get_typeid(m_world, e); + flecs::entity_t type = ecs_get_typeid(world_, e); if (!type) { ecs_err("id is not a type"); return to_base(); } - void *ptr = ecs_get_mut_id(m_world, m_id, e); + void *ptr = ecs_ensure_id(world_, id_, e); ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_ptr_from_json(m_world, type, ptr, json, desc); - ecs_modified_id(m_world, m_id, e); + ecs_ptr_from_json(world_, type, ptr, json, desc); + ecs_modified_id(world_, id_, e); return to_base(); } /** Set pair from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ -Self& set_json( +const Self& set_json( flecs::entity_t r, flecs::entity_t t, const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { return set_json(ecs_pair(r, t), json, desc); } /** Set component from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ template -Self& set_json( +const Self& set_json( const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { - return set_json(_::cpp_type::id(m_world), json, desc); + return set_json(_::type::id(world_), json, desc); } /** Set pair from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ template -Self& set_json( +const Self& set_json( const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { return set_json( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world), + _::type::id(world_), + _::type::id(world_), json, desc); } /** Set pair from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ template -Self& set_json( +const Self& set_json( flecs::entity_t t, const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { return set_json( - _::cpp_type::id(m_world), t, + _::type::id(world_), t, json, desc); } /** Set pair from JSON. * - * \memberof flecs::entity_builder - * \ingroup cpp_addons_json + * @memberof flecs::entity_builder + * @ingroup cpp_addons_json */ template -Self& set_json_second( +const Self& set_json_second( flecs::entity_t r, const char *json, - flecs::from_json_desc_t *desc = nullptr) + flecs::from_json_desc_t *desc = nullptr) const { return set_json( - r, _::cpp_type::id(m_world), + r, _::type::id(world_), json, desc); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity_view.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity_view.inl index 7ac6737a3..5180bbf61 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity_view.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/json/entity_view.inl @@ -5,10 +5,10 @@ /** Serialize entity to JSON. * - * \memberof flecs::entity_view - * \ingroup cpp_addons_json + * @memberof flecs::entity_view + * @ingroup cpp_addons_json */ -flecs::string to_json(const flecs::entity_to_json_desc_t *desc = nullptr) { - char *json = ecs_entity_to_json(m_world, m_id, desc); +flecs::string to_json(const flecs::entity_to_json_desc_t *desc = nullptr) const { + char *json = ecs_entity_to_json(world_, id_, desc); return flecs::string(json); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/json/iterable.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/json/iterable.inl index af240842f..612c92a94 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/json/iterable.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/json/iterable.inl @@ -5,10 +5,10 @@ /** Serialize iterator result to JSON. * - * \memberof flecs::iter - * \ingroup cpp_addons_json + * @memberof flecs::iter + * @ingroup cpp_addons_json */ flecs::string to_json(flecs::iter_to_json_desc_t *desc = nullptr) { - char *json = ecs_iter_to_json(m_it.real_world, &m_it, desc); + char *json = ecs_iter_to_json(&it_, desc); return flecs::string(json); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/json/world.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/json/world.inl index a7bd2dadb..f441e8dd4 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/json/world.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/json/world.inl @@ -5,59 +5,68 @@ /** Serialize untyped value to JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ flecs::string to_json(flecs::entity_t tid, const void* value) { - char *json = ecs_ptr_to_json(m_world, tid, value); + char *json = ecs_ptr_to_json(world_, tid, value); return flecs::string(json); } /** Serialize value to JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ template flecs::string to_json(const T* value) { - flecs::entity_t tid = _::cpp_type::id(m_world); + flecs::entity_t tid = _::type::id(world_); return to_json(tid, value); } /** Serialize world to JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ flecs::string to_json() { - return flecs::string( ecs_world_to_json(m_world, nullptr) ); + return flecs::string( ecs_world_to_json(world_, nullptr) ); } /** Deserialize value from JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ const char* from_json(flecs::entity_t tid, void* value, const char *json, flecs::from_json_desc_t *desc = nullptr) { - return ecs_ptr_from_json(m_world, tid, value, json, desc); + return ecs_ptr_from_json(world_, tid, value, json, desc); } /** Deserialize value from JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ template const char* from_json(T* value, const char *json, flecs::from_json_desc_t *desc = nullptr) { - return ecs_ptr_from_json(m_world, _::cpp_type::id(m_world), + return ecs_ptr_from_json(world_, _::type::id(world_), value, json, desc); } /** Deserialize JSON into world. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ const char* from_json(const char *json, flecs::from_json_desc_t *desc = nullptr) { - return ecs_world_from_json(m_world, json, desc); + return ecs_world_from_json(world_, json, desc); +} + +/** Deserialize JSON file into world. + * + * @memberof flecs::world + * @ingroup cpp_addons_json + */ +const char* from_json_file(const char *json, flecs::from_json_desc_t *desc = nullptr) { + return ecs_world_from_json_file(world_, json, desc); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/component.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/component.inl index b88ed4e97..39e6ca2cd 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/component.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/component.inl @@ -2,15 +2,15 @@ /** Register opaque type interface */ template component& opaque(const Func& type_support) { - flecs::world world(m_world); + flecs::world world(world_); auto ts = type_support(world); - ts.desc.entity = _::cpp_type::id(m_world); - ecs_opaque_init(m_world, &ts.desc); + ts.desc.entity = _::type::id(world_); + ecs_opaque_init(world_, &ts.desc); return *this; } flecs::opaque opaque(flecs::entity_t as_type) { - return flecs::opaque(m_world).as_type(as_type); + return flecs::opaque(world_).as_type(as_type); } flecs::opaque opaque(flecs::entity as_type) { @@ -24,7 +24,7 @@ flecs::opaque opaque(flecs::untyped_component as_type) { /** Return opaque type builder for collection type */ template flecs::opaque opaque(flecs::id_t as_type) { - return flecs::opaque(m_world).as_type(as_type); + return flecs::opaque(world_).as_type(as_type); } /** Add constant. */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/cursor.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/cursor.hpp index 428356bcd..868621c2d 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/cursor.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/cursor.hpp @@ -9,54 +9,54 @@ namespace flecs { /** * @defgroup cpp_addons_meta Meta - * @brief Flecs reflection framework. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Flecs reflection framework. + * * @{ */ /** Class for reading/writing dynamic values. - * - * \ingroup cpp_addons_meta + * + * @ingroup cpp_addons_meta */ struct cursor { cursor(flecs::world_t *world, flecs::entity_t type_id, void *ptr) { - m_cursor = ecs_meta_cursor(world, type_id, ptr); + cursor_ = ecs_meta_cursor(world, type_id, ptr); } /** Push value scope (such as a nested struct) */ int push() { - return ecs_meta_push(&m_cursor); + return ecs_meta_push(&cursor_); } /** Pop value scope */ int pop() { - return ecs_meta_pop(&m_cursor); + return ecs_meta_pop(&cursor_); } /** Move to next member/element */ int next() { - return ecs_meta_next(&m_cursor); + return ecs_meta_next(&cursor_); } /** Move to member by name */ int member(const char *name) { - return ecs_meta_member(&m_cursor, name); + return ecs_meta_member(&cursor_, name); } /** Move to element by index */ int elem(int32_t elem) { - return ecs_meta_elem(&m_cursor, elem); + return ecs_meta_elem(&cursor_, elem); } /** Test if current scope is a collection type */ bool is_collection() { - return ecs_meta_is_collection(&m_cursor); + return ecs_meta_is_collection(&cursor_); } /** Get member name */ flecs::string_view get_member() const { - return flecs::string_view(ecs_meta_get_member(&m_cursor)); + return flecs::string_view(ecs_meta_get_member(&cursor_)); } /** Get type of value */ @@ -67,94 +67,94 @@ struct cursor { /** Get untyped pointer to value */ void* get_ptr() { - return ecs_meta_get_ptr(&m_cursor); + return ecs_meta_get_ptr(&cursor_); } /** Set boolean value */ int set_bool(bool value) { - return ecs_meta_set_bool(&m_cursor, value); + return ecs_meta_set_bool(&cursor_, value); } /** Set char value */ int set_char(char value) { - return ecs_meta_set_char(&m_cursor, value); + return ecs_meta_set_char(&cursor_, value); } /** Set signed int value */ int set_int(int64_t value) { - return ecs_meta_set_int(&m_cursor, value); + return ecs_meta_set_int(&cursor_, value); } /** Set unsigned int value */ int set_uint(uint64_t value) { - return ecs_meta_set_uint(&m_cursor, value); + return ecs_meta_set_uint(&cursor_, value); } /** Set float value */ int set_float(double value) { - return ecs_meta_set_float(&m_cursor, value); + return ecs_meta_set_float(&cursor_, value); } /** Set string value */ int set_string(const char *value) { - return ecs_meta_set_string(&m_cursor, value); + return ecs_meta_set_string(&cursor_, value); } /** Set string literal value */ int set_string_literal(const char *value) { - return ecs_meta_set_string_literal(&m_cursor, value); + return ecs_meta_set_string_literal(&cursor_, value); } /** Set entity value */ int set_entity(flecs::entity_t value) { - return ecs_meta_set_entity(&m_cursor, value); + return ecs_meta_set_entity(&cursor_, value); } /** Set (component) id value */ int set_id(flecs::id_t value) { - return ecs_meta_set_id(&m_cursor, value); + return ecs_meta_set_id(&cursor_, value); } /** Set null value */ int set_null() { - return ecs_meta_set_null(&m_cursor); + return ecs_meta_set_null(&cursor_); } /** Get boolean value */ bool get_bool() const { - return ecs_meta_get_bool(&m_cursor); + return ecs_meta_get_bool(&cursor_); } /** Get char value */ char get_char() const { - return ecs_meta_get_char(&m_cursor); + return ecs_meta_get_char(&cursor_); } /** Get signed int value */ int64_t get_int() const { - return ecs_meta_get_int(&m_cursor); + return ecs_meta_get_int(&cursor_); } /** Get unsigned int value */ uint64_t get_uint() const { - return ecs_meta_get_uint(&m_cursor); + return ecs_meta_get_uint(&cursor_); } /** Get float value */ double get_float() const { - return ecs_meta_get_float(&m_cursor); + return ecs_meta_get_float(&cursor_); } /** Get string value */ const char *get_string() const { - return ecs_meta_get_string(&m_cursor); + return ecs_meta_get_string(&cursor_); } /** Get entity value */ flecs::entity get_entity() const; /** Cursor object */ - ecs_meta_cursor_t m_cursor; + ecs_meta_cursor_t cursor_; }; /** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/decl.hpp index 95aa7d0d4..0e7c93deb 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/decl.hpp @@ -9,9 +9,9 @@ namespace flecs { /** * @defgroup cpp_addons_meta Meta - * @brief Flecs reflection framework. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Flecs reflection framework. + * * @{ */ @@ -37,8 +37,8 @@ using enum_constant_t = ecs_enum_constant_t; using bitmask_constant_t = ecs_bitmask_constant_t; /* Components */ -using MetaType = EcsMetaType; -using MetaTypeSerialized = EcsMetaTypeSerialized; +using Type = EcsType; +using TypeSerializer = EcsTypeSerializer; using Primitive = EcsPrimitive; using Enum = EcsEnum; using Bitmask = EcsBitmask; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/entity_builder.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/entity_builder.inl index a23fdcf4e..a5b4843e0 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/entity_builder.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/entity_builder.inl @@ -4,23 +4,23 @@ */ /** - * \memberof flecs::entity_view - * \ingroup cpp_addons_meta + * @memberof flecs::entity_view + * @ingroup cpp_addons_meta * * @{ */ /** Make entity a unit */ -Self& unit( +const Self& unit( const char *symbol, flecs::entity_t prefix = 0, flecs::entity_t base = 0, flecs::entity_t over = 0, int32_t factor = 0, - int32_t power = 0) + int32_t power = 0) const { ecs_unit_desc_t desc = {}; - desc.entity = this->m_id; + desc.entity = this->id_; desc.symbol = const_cast(symbol); /* safe, will be copied in */ desc.base = base; desc.over = over; @@ -33,15 +33,15 @@ Self& unit( } /** Make entity a derived unit */ -Self& unit( +const Self& unit( flecs::entity_t prefix = 0, flecs::entity_t base = 0, flecs::entity_t over = 0, int32_t factor = 0, - int32_t power = 0) + int32_t power = 0) const { ecs_unit_desc_t desc = {}; - desc.entity = this->m_id; + desc.entity = this->id_; desc.base = base; desc.over = over; desc.prefix = prefix; @@ -53,13 +53,13 @@ Self& unit( } /** Make entity a derived unit */ -Self& unit_prefix( +const Self& unit_prefix( const char *symbol, int32_t factor = 0, - int32_t power = 0) + int32_t power = 0) const { ecs_unit_prefix_desc_t desc = {}; - desc.entity = this->m_id; + desc.entity = this->id_; desc.symbol = const_cast(symbol); /* safe, will be copied in */ desc.translation.factor = factor; desc.translation.power = power; @@ -69,19 +69,19 @@ Self& unit_prefix( } /** Add quantity to unit */ -Self& quantity(flecs::entity_t quantity) { +const Self& quantity(flecs::entity_t quantity) const { ecs_add_pair(this->world(), this->id(), flecs::Quantity, quantity); return to_base(); } /** Make entity a unity prefix */ template -Self& quantity() { - return this->quantity(_::cpp_type::id(this->world())); +const Self& quantity() const { + return this->quantity(_::type::id(this->world())); } /** Make entity a quantity */ -Self& quantity() { +const Self& quantity() const { ecs_add_id(this->world(), this->id(), flecs::Quantity); return to_base(); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/impl.hpp index 31cb53395..e6f18a843 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/impl.hpp @@ -43,28 +43,29 @@ inline void init(flecs::world& world) { world.component("flecs::meta::type_kind"); world.component("flecs::meta::primitive_kind"); - world.component("flecs::meta::member"); + world.component("flecs::meta::member_t"); world.component("flecs::meta::enum_constant"); world.component("flecs::meta::bitmask_constant"); - world.component("flecs::meta::MetaType"); - world.component("flecs::meta::MetaTypeSerialized"); - world.component("flecs::meta::Primitive"); - world.component("flecs::meta::Enum"); - world.component("flecs::meta::Bitmask"); - world.component("flecs::meta::Member"); - world.component("flecs::meta::Struct"); - world.component("flecs::meta::Array"); - world.component("flecs::meta::Vector"); + world.component("flecs::meta::type"); + world.component("flecs::meta::TypeSerializer"); + world.component("flecs::meta::primitive"); + world.component("flecs::meta::enum"); + world.component("flecs::meta::bitmask"); + world.component("flecs::meta::member"); + world.component("flecs::meta::member_ranges"); + world.component("flecs::meta::struct"); + world.component("flecs::meta::array"); + world.component("flecs::meta::vector"); - world.component("flecs::meta::Unit"); + world.component("flecs::meta::unit"); // To support member and member register components // (that do not have conflicting symbols with builtin ones) for platform // specific types. if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::cpp_type::init(flecs::Iptr, true); + flecs::_::type::init(flecs::Iptr, true); ecs_assert(flecs::type_id() == flecs::Iptr, ECS_INTERNAL_ERROR, NULL); // Remove symbol to prevent validation errors, as it doesn't match with @@ -73,7 +74,7 @@ inline void init(flecs::world& world) { } if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::cpp_type::init(flecs::Uptr, true); + flecs::_::type::init(flecs::Uptr, true); ecs_assert(flecs::type_id() == flecs::Uptr, ECS_INTERNAL_ERROR, NULL); // Remove symbol to prevent validation errors, as it doesn't match with @@ -82,11 +83,12 @@ inline void init(flecs::world& world) { } // Register opaque type support for C++ entity wrappers - world.component() - .opaque(flecs_entity_support); - - world.component() - .opaque(flecs_entity_support); + world.entity("::flecs::cpp").add(flecs::Module).scope([&]{ + world.component() + .opaque(flecs_entity_support); + world.component() + .opaque(flecs_entity_support); + }); } } // namespace _ @@ -95,24 +97,24 @@ inline void init(flecs::world& world) { inline flecs::entity cursor::get_type() const { - return flecs::entity(m_cursor.world, ecs_meta_get_type(&m_cursor)); + return flecs::entity(cursor_.world, ecs_meta_get_type(&cursor_)); } inline flecs::entity cursor::get_unit() const { - return flecs::entity(m_cursor.world, ecs_meta_get_unit(&m_cursor)); + return flecs::entity(cursor_.world, ecs_meta_get_unit(&cursor_)); } inline flecs::entity cursor::get_entity() const { - return flecs::entity(m_cursor.world, ecs_meta_get_entity(&m_cursor)); + return flecs::entity(cursor_.world, ecs_meta_get_entity(&cursor_)); } /** 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); + flecs::entity_t eid = ecs_primitive_init(world_, &desc); ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); - return flecs::entity(m_world, eid); + return flecs::entity(world_, eid); } /** Create array type. */ @@ -120,28 +122,28 @@ 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); + flecs::entity_t eid = ecs_array_init(world_, &desc); ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); - return flecs::entity(m_world, eid); + return flecs::entity(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); + return this->array(_::type::id(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); + flecs::entity_t eid = ecs_vector_init(world_, &desc); ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); - return flecs::entity(m_world, eid); + return flecs::entity(world_, eid); } template inline flecs::entity world::vector() { - return this->vector(_::cpp_type::id(m_world)); + return this->vector(_::type::id(world_)); } } // namespace flecs @@ -152,7 +154,7 @@ inline int ecs_serializer_t::value(ecs_entity_t type, const void *v) const { template inline int ecs_serializer_t::value(const T& v) const { - return this->value(flecs::_::cpp_type::id( + return this->value(flecs::_::type::id( const_cast(this->world)), &v); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/opaque.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/opaque.hpp index da2160ba3..d1f23803c 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/opaque.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/opaque.hpp @@ -11,9 +11,9 @@ namespace flecs { /** * @defgroup cpp_addons_meta Meta - * @brief Flecs reflection framework. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Flecs reflection framework. + * * @{ */ @@ -32,7 +32,7 @@ template struct opaque { opaque(flecs::world_t *w = nullptr) : world(w) { if (world) { - desc.entity = _::cpp_type::id(world); + desc.entity = _::type::id(world); } } @@ -44,7 +44,7 @@ struct opaque { /** Serialize function */ opaque& serialize(flecs::serialize func) { - this->desc.type.serialize = + this->desc.type.serialize = reinterpret_castdesc.type.serialize)>(func); return *this; @@ -52,7 +52,7 @@ struct opaque { /** Assign bool value */ opaque& assign_bool(void (*func)(T *dst, bool value)) { - this->desc.type.assign_bool = + this->desc.type.assign_bool = reinterpret_castdesc.type.assign_bool)>(func); return *this; @@ -60,7 +60,7 @@ struct opaque { /** Assign char value */ opaque& assign_char(void (*func)(T *dst, char value)) { - this->desc.type.assign_char = + this->desc.type.assign_char = reinterpret_castdesc.type.assign_char)>(func); return *this; @@ -68,7 +68,7 @@ struct opaque { /** Assign int value */ opaque& assign_int(void (*func)(T *dst, int64_t value)) { - this->desc.type.assign_int = + this->desc.type.assign_int = reinterpret_castdesc.type.assign_int)>(func); return *this; @@ -76,7 +76,7 @@ struct opaque { /** Assign unsigned int value */ opaque& assign_uint(void (*func)(T *dst, uint64_t value)) { - this->desc.type.assign_uint = + this->desc.type.assign_uint = reinterpret_castdesc.type.assign_uint)>(func); return *this; @@ -84,7 +84,7 @@ struct opaque { /** Assign float value */ opaque& assign_float(void (*func)(T *dst, double value)) { - this->desc.type.assign_float = + this->desc.type.assign_float = reinterpret_castdesc.type.assign_float)>(func); return *this; @@ -92,7 +92,7 @@ struct opaque { /** Assign string value */ opaque& assign_string(void (*func)(T *dst, const char *value)) { - this->desc.type.assign_string = + this->desc.type.assign_string = reinterpret_castdesc.type.assign_string)>(func); return *this; @@ -100,9 +100,9 @@ struct opaque { /** Assign entity value */ opaque& assign_entity( - void (*func)(T *dst, ecs_world_t *world, ecs_entity_t entity)) + void (*func)(T *dst, ecs_world_t *world, ecs_entity_t entity)) { - this->desc.type.assign_entity = + this->desc.type.assign_entity = reinterpret_castdesc.type.assign_entity)>(func); return *this; @@ -112,7 +112,7 @@ struct opaque { opaque& assign_id( void (*func)(T *dst, ecs_world_t *world, ecs_id_t id)) { - this->desc.type.assign_id = + this->desc.type.assign_id = reinterpret_castdesc.type.assign_id)>(func); return *this; @@ -120,7 +120,7 @@ struct opaque { /** Assign null value */ opaque& assign_null(void (*func)(T *dst)) { - this->desc.type.assign_null = + this->desc.type.assign_null = reinterpret_castdesc.type.assign_null)>(func); return *this; @@ -128,7 +128,7 @@ struct opaque { /** Clear collection elements */ opaque& clear(void (*func)(T *dst)) { - this->desc.type.clear = + this->desc.type.clear = reinterpret_castdesc.type.clear)>(func); return *this; @@ -136,7 +136,7 @@ struct opaque { /** Ensure & get collection element */ opaque& ensure_element(ElemType* (*func)(T *dst, size_t elem)) { - this->desc.type.ensure_element = + this->desc.type.ensure_element = reinterpret_castdesc.type.ensure_element)>(func); return *this; @@ -144,7 +144,7 @@ struct opaque { /** Ensure & get element */ opaque& ensure_member(void* (*func)(T *dst, const char *member)) { - this->desc.type.ensure_member = + this->desc.type.ensure_member = reinterpret_castdesc.type.ensure_member)>(func); return *this; @@ -152,15 +152,15 @@ struct opaque { /** Return number of elements */ opaque& count(size_t (*func)(const T *dst)) { - this->desc.type.count = + this->desc.type.count = reinterpret_castdesc.type.count)>(func); return *this; } - + /** Resize to number of elements */ opaque& resize(void (*func)(T *dst, size_t count)) { - this->desc.type.resize = + this->desc.type.resize = reinterpret_castdesc.type.resize)>(func); return *this; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/untyped_component.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/untyped_component.inl index 7fb31a742..6c1df49db 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/untyped_component.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/untyped_component.inl @@ -4,95 +4,203 @@ */ /** - * \memberof flecs::component - * \ingroup cpp_addons_meta + * @memberof flecs::component + * @ingroup cpp_addons_meta * * @{ */ -/** Add member with unit. */ -untyped_component& member(flecs::entity_t type_id, flecs::entity_t unit, const char *name, int32_t count = 0, size_t offset = 0) { +private: + +/** Private method that adds member to component. */ +untyped_component& internal_member( + flecs::entity_t type_id, + flecs::entity_t unit, + const char *name, + int32_t count = 0, + size_t offset = 0, + bool use_offset = false) +{ ecs_entity_desc_t desc = {}; desc.name = name; - desc.add[0] = ecs_pair(flecs::ChildOf, m_id); - ecs_entity_t eid = ecs_entity_init(m_world, &desc); + desc.parent = id_; + ecs_entity_t eid = ecs_entity_init(world_, &desc); ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); - flecs::entity e(m_world, eid); + flecs::entity e(world_, eid); Member m = {}; m.type = type_id; m.unit = unit; m.count = count; m.offset = static_cast(offset); + m.use_offset = use_offset; e.set(m); return *this; } +public: + +/** Add member with unit. */ +untyped_component& member( + flecs::entity_t type_id, + flecs::entity_t unit, + const char *name, + int32_t count = 0) +{ + return internal_member(type_id, unit, name, count, 0, false); +} + +/** Add member with unit, count and offset. */ +untyped_component& member( + flecs::entity_t type_id, + flecs::entity_t unit, + const char *name, + int32_t count, + size_t offset) +{ + return internal_member(type_id, unit, name, count, offset, true); +} + /** Add member. */ -untyped_component& member(flecs::entity_t type_id, const char* name, int32_t count = 0, size_t offset = 0) { +untyped_component& member( + flecs::entity_t type_id, + const char* name, + int32_t count = 0) +{ + return member(type_id, 0, name, count); +} + +/** Add member with count and offset. */ +untyped_component& member( + flecs::entity_t type_id, + const char* name, + int32_t count, + size_t offset) +{ return member(type_id, 0, name, count, offset); } /** Add member. */ template -untyped_component& member(const char *name, int32_t count = 0, size_t offset = 0) { - flecs::entity_t type_id = _::cpp_type::id(m_world); +untyped_component& member( + const char *name, + int32_t count = 0) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, name, count); +} + +/** Add member. */ +template +untyped_component& member( + const char *name, + int32_t count, + size_t offset) +{ + flecs::entity_t type_id = _::type::id(world_); return member(type_id, name, count, offset); } /** Add member with unit. */ template -untyped_component& member(flecs::entity_t unit, const char *name, int32_t count = 0, size_t offset = 0) { - flecs::entity_t type_id = _::cpp_type::id(m_world); +untyped_component& member( + flecs::entity_t unit, + const char *name, + int32_t count = 0) +{ + flecs::entity_t type_id = _::type::id(world_); + return member(type_id, unit, name, count); +} + +/** Add member with unit. */ +template +untyped_component& member( + flecs::entity_t unit, + const char *name, + int32_t count, + size_t offset) +{ + flecs::entity_t type_id = _::type::id(world_); return member(type_id, unit, name, count, offset); } /** Add member with unit. */ template -untyped_component& member(const char *name, int32_t count = 0, size_t offset = 0) { - flecs::entity_t type_id = _::cpp_type::id(m_world); - flecs::entity_t unit_id = _::cpp_type::id(m_world); +untyped_component& member( + const char *name, + int32_t count = 0) +{ + flecs::entity_t type_id = _::type::id(world_); + flecs::entity_t unit_id = _::type::id(world_); + return member(type_id, unit_id, name, count); +} + +/** Add member with unit. */ +template +untyped_component& member( + const char *name, + int32_t count, + size_t offset) +{ + flecs::entity_t type_id = _::type::id(world_); + flecs::entity_t unit_id = _::type::id(world_); return member(type_id, unit_id, name, count, offset); } /** Add member using pointer-to-member. */ -template ::type> -untyped_component& member(const char* name, const MemberType ComponentType::* ptr) { - flecs::entity_t type_id = _::cpp_type::id(m_world); +template ::type> +untyped_component& member( + const char* name, + const MemberType ComponentType::* ptr) +{ + flecs::entity_t type_id = _::type::id(world_); size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); return member(type_id, name, std::extent::value, offset); } /** Add member with unit using pointer-to-member. */ -template ::type> -untyped_component& member(flecs::entity_t unit, const char* name, const MemberType ComponentType::* ptr) { - flecs::entity_t type_id = _::cpp_type::id(m_world); +template ::type> +untyped_component& member( + flecs::entity_t unit, + const char* name, + const MemberType ComponentType::* ptr) +{ + flecs::entity_t type_id = _::type::id(world_); size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); return member(type_id, unit, name, std::extent::value, offset); } /** Add member with unit using pointer-to-member. */ -template ::type> -untyped_component& member(const char* name, const MemberType ComponentType::* ptr) { - flecs::entity_t type_id = _::cpp_type::id(m_world); - flecs::entity_t unit_id = _::cpp_type::id(m_world); +template ::type> +untyped_component& member( + const char* name, + const MemberType ComponentType::* ptr) +{ + flecs::entity_t type_id = _::type::id(world_); + flecs::entity_t unit_id = _::type::id(world_); size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); return member(type_id, unit_id, name, std::extent::value, offset); } /** Add constant. */ -untyped_component& constant(const char *name, int32_t value) { - ecs_add_id(m_world, m_id, _::cpp_type::id(m_world)); +untyped_component& constant( + const char *name, + int32_t value) +{ + ecs_add_id(world_, id_, _::type::id(world_)); ecs_entity_desc_t desc = {}; desc.name = name; - desc.add[0] = ecs_pair(flecs::ChildOf, m_id); - ecs_entity_t eid = ecs_entity_init(m_world, &desc); + desc.parent = id_; + ecs_entity_t eid = ecs_entity_init(world_, &desc); ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); - ecs_set_id(m_world, eid, + ecs_set_id(world_, eid, ecs_pair(flecs::Constant, flecs::I32), sizeof(int32_t), &value); @@ -100,16 +208,19 @@ untyped_component& constant(const char *name, int32_t value) { } /** Add bitmask constant. */ -untyped_component& bit(const char *name, uint32_t value) { - ecs_add_id(m_world, m_id, _::cpp_type::id(m_world)); +untyped_component& bit( + const char *name, + uint32_t value) +{ + ecs_add_id(world_, id_, _::type::id(world_)); ecs_entity_desc_t desc = {}; desc.name = name; - desc.add[0] = ecs_pair(flecs::ChildOf, m_id); - ecs_entity_t eid = ecs_entity_init(m_world, &desc); + desc.parent = id_; + ecs_entity_t eid = ecs_entity_init(world_, &desc); ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); - ecs_set_id(m_world, eid, + ecs_set_id(world_, eid, ecs_pair(flecs::Constant, flecs::U32), sizeof(uint32_t), &value); @@ -118,25 +229,33 @@ untyped_component& bit(const char *name, uint32_t value) { /** Register array metadata for component */ template -untyped_component& array(int32_t elem_count) { +untyped_component& array( + int32_t elem_count) +{ ecs_array_desc_t desc = {}; - desc.entity = m_id; - desc.type = _::cpp_type::id(m_world); + desc.entity = id_; + desc.type = _::type::id(world_); desc.count = elem_count; - ecs_array_init(m_world, &desc); + ecs_array_init(world_, &desc); return *this; } /** Add member value range */ -untyped_component& range(double min, double max) { - const flecs::member_t *m = ecs_cpp_last_member(m_world, m_id); +untyped_component& range( + double min, + double max) +{ + const flecs::member_t *m = ecs_cpp_last_member(world_, id_); if (!m) { return *this; } - flecs::world w(m_world); + flecs::world w(world_); flecs::entity me = w.entity(m->member); - flecs::MemberRanges *mr = me.get_mut(); + + // Don't use C++ ensure because Unreal defines a macro called ensure + flecs::MemberRanges *mr = static_cast( + ecs_ensure_id(w, me, w.id())); mr->value.min = min; mr->value.max = max; me.modified(); @@ -144,15 +263,21 @@ untyped_component& range(double min, double max) { } /** Add member warning range */ -untyped_component& warning_range(double min, double max) { - const flecs::member_t *m = ecs_cpp_last_member(m_world, m_id); +untyped_component& warning_range( + double min, + double max) +{ + const flecs::member_t *m = ecs_cpp_last_member(world_, id_); if (!m) { return *this; } - flecs::world w(m_world); + flecs::world w(world_); flecs::entity me = w.entity(m->member); - flecs::MemberRanges *mr = me.get_mut(); + + // Don't use C++ ensure because Unreal defines a macro called ensure + flecs::MemberRanges *mr = static_cast( + ecs_ensure_id(w, me, w.id())); mr->warning.min = min; mr->warning.max = max; me.modified(); @@ -160,20 +285,25 @@ untyped_component& warning_range(double min, double max) { } /** Add member error range */ -untyped_component& error_range(double min, double max) { - const flecs::member_t *m = ecs_cpp_last_member(m_world, m_id); +untyped_component& error_range( + double min, + double max) +{ + const flecs::member_t *m = ecs_cpp_last_member(world_, id_); if (!m) { return *this; } - flecs::world w(m_world); + flecs::world w(world_); flecs::entity me = w.entity(m->member); - flecs::MemberRanges *mr = me.get_mut(); + + // Don't use C++ ensure because Unreal defines a macro called ensure + flecs::MemberRanges *mr = static_cast(ecs_ensure_id( + w, me, w.id())); mr->error.min = min; mr->error.max = max; me.modified(); return *this; } - /** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/world.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/world.inl index eee378bbf..707f94c30 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/meta/world.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/meta/world.inl @@ -4,34 +4,21 @@ */ /** - * \memberof flecs::world - * \ingroup cpp_addons_meta + * @memberof flecs::world + * @ingroup cpp_addons_meta * * @{ */ -/** 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::cursor cursor(flecs::entity_t tid, void *ptr) { - return flecs::cursor(m_world, tid, ptr); + return flecs::cursor(world_, tid, ptr); } /** Return meta cursor to value */ template flecs::cursor cursor(void *ptr) { - flecs::entity_t tid = _::cpp_type::id(m_world); + flecs::entity_t tid = _::type::id(world_); return cursor(tid, ptr); } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/builder.hpp index c21f53e60..b8cc961b1 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/builder.hpp @@ -10,22 +10,22 @@ namespace flecs { /** - * \ingroup cpp_addon_metrics + * @ingroup cpp_addons_metrics * @{ */ /** Event builder interface */ struct metric_builder { metric_builder(flecs::world_t *world, flecs::entity_t entity) - : m_world(world) + : world_(world) { - m_desc.entity = entity; + desc_.entity = entity; } ~metric_builder(); metric_builder& member(flecs::entity_t e) { - m_desc.member = e; + desc_.member = e; return *this; } @@ -40,61 +40,61 @@ struct metric_builder { metric_builder& dotmember(const char *name); metric_builder& id(flecs::id_t the_id) { - m_desc.id = the_id; + desc_.id = the_id; return *this; } metric_builder& id(flecs::entity_t first, flecs::entity_t second) { - m_desc.id = ecs_pair(first, second); + desc_.id = ecs_pair(first, second); return *this; } template metric_builder& id() { - return id(_::cpp_type::id(m_world)); + return id(_::type::id(world_)); } template metric_builder& id(flecs::entity_t second) { - return id(_::cpp_type::id(m_world), second); + return id(_::type::id(world_), second); } template metric_builder& id_second(flecs::entity_t first) { - return id(first, _::cpp_type::id(m_world)); + return id(first, _::type::id(world_)); } template metric_builder& id() { - return id(_::cpp_type::id(m_world)); + return id(_::type::id(world_)); } metric_builder& targets(bool value = true) { - m_desc.targets = value; + desc_.targets = value; return *this; } metric_builder& kind(flecs::entity_t the_kind) { - m_desc.kind = the_kind; + desc_.kind = the_kind; return *this; } template metric_builder& kind() { - return kind(_::cpp_type::id(m_world)); + return kind(_::type::id(world_)); } metric_builder& brief(const char *b) { - m_desc.brief = b; + desc_.brief = b; return *this; } operator flecs::entity(); protected: - flecs::world_t *m_world; - ecs_metric_desc_t m_desc = {}; - bool m_created = false; + flecs::world_t *world_; + ecs_metric_desc_t desc_ = {}; + bool created_ = false; }; /** diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/decl.hpp index bdfa1a747..0a394395c 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/decl.hpp @@ -9,6 +9,15 @@ namespace flecs { +/** + * @defgroup cpp_addons_metrics Metrics + * @ingroup cpp_addons + * The metrics module extracts metrics from components and makes them available + * through a unified component interface. + * + * @{ + */ + struct metrics { using Value = EcsMetricValue; using Source = EcsMetricSource; @@ -23,4 +32,6 @@ struct metrics { metrics(flecs::world& world); }; +/** @} */ + } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/impl.hpp index 623665861..e8b5b6132 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/impl.hpp @@ -13,6 +13,9 @@ inline metrics::metrics(flecs::world& world) { /* Import C module */ FlecsMetricsImport(world); + world.component(); + world.component(); + world.entity("::flecs::metrics::Instance"); world.entity("::flecs::metrics::Metric"); world.entity("::flecs::metrics::Metric::Counter"); @@ -22,18 +25,18 @@ inline metrics::metrics(flecs::world& world) { } inline metric_builder::~metric_builder() { - if (!m_created) { - ecs_metric_init(m_world, &m_desc); + if (!created_) { + ecs_metric_init(world_, &desc_); } } inline metric_builder& metric_builder::member(const char *name) { flecs::entity m; - if (m_desc.id) { - flecs::entity_t type = ecs_get_typeid(m_world, m_desc.id); - m = flecs::entity(m_world, type).lookup(name); + if (desc_.id) { + flecs::entity_t type = ecs_get_typeid(world_, desc_.id); + m = flecs::entity(world_, type).lookup(name); } else { - m = flecs::world(m_world).lookup(name); + m = flecs::world(world_).lookup(name); } if (!m) { flecs::log::err("member '%s' not found", name); @@ -43,7 +46,7 @@ inline metric_builder& metric_builder::member(const char *name) { template inline metric_builder& metric_builder::member(const char *name) { - flecs::entity e (m_world, _::cpp_type::id(m_world)); + flecs::entity e (world_, _::type::id(world_)); flecs::entity_t m = e.lookup(name); if (!m) { flecs::log::err("member '%s' not found in type '%s'", @@ -54,32 +57,32 @@ inline metric_builder& metric_builder::member(const char *name) { } inline metric_builder& metric_builder::dotmember(const char *expr) { - m_desc.dotmember = expr; + desc_.dotmember = expr; return *this; } template inline metric_builder& metric_builder::dotmember(const char *expr) { - m_desc.dotmember = expr; - m_desc.id = _::cpp_type::id(m_world); + desc_.dotmember = expr; + desc_.id = _::type::id(world_); return *this; } inline metric_builder::operator flecs::entity() { - if (!m_created) { - m_created = true; - flecs::entity result(m_world, ecs_metric_init(m_world, &m_desc)); - m_desc.entity = result; + if (!created_) { + created_ = true; + flecs::entity result(world_, ecs_metric_init(world_, &desc_)); + desc_.entity = result; return result; } else { - return flecs::entity(m_world, m_desc.entity); + return flecs::entity(world_, desc_.entity); } } template inline flecs::metric_builder world::metric(Args &&... args) const { - flecs::entity result(m_world, FLECS_FWD(args)...); - return flecs::metric_builder(m_world, result); + flecs::entity result(world_, FLECS_FWD(args)...); + return flecs::metric_builder(world_, result); } template @@ -88,8 +91,8 @@ inline untyped_component& untyped_component::metric( const char *brief, const char *metric_name) { - flecs::world w(m_world); - flecs::entity e(m_world, m_id); + flecs::world w(world_); + flecs::entity e(world_, id_); const flecs::member_t *m = ecs_cpp_last_member(w, e); if (!m) { diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/mixin.inl index 0124cec50..b81041659 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/mixin.inl @@ -1,8 +1,8 @@ /** Create metric. * - * \ingroup cpp_addons_metrics - * \memberof flecs::world + * @ingroup cpp_addons_metrics + * @memberof flecs::world */ template flecs::metric_builder metric(Args &&... args) const; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/untyped_component.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/untyped_component.inl index e1139307e..f0d458815 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/untyped_component.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/metrics/untyped_component.inl @@ -4,8 +4,8 @@ */ /** - * \memberof flecs::component - * \ingroup cpp_addons_metrics + * @memberof flecs::component + * @ingroup cpp_addons_metrics * * @{ */ @@ -23,9 +23,6 @@ * @param parent Parent entity of the metric (optional). * @param brief Description for metric (optional). * @param name Name of metric (optional). - * - * \ingroup cpp_addons_metrics - * \memberof flecs::world */ template untyped_component& metric( diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/module/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/module/impl.hpp index 5560cb8fb..de118b97b 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/module/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/module/impl.hpp @@ -18,19 +18,25 @@ ecs_entity_t do_import(world& world, const char *symbol) { // Initialize module component type & don't allow it to be registered as a // tag, as this would prevent calling emplace() - auto m_c = component(world, nullptr, false); - ecs_add_id(world, m_c, EcsModule); + auto c_ = component(world, nullptr, false); - ecs_set_scope(world, m_c); + // Make module component sparse so that it'll never move in memory. This + // guarantees that a module destructor can be reliably used to cleanup + // module resources. + c_.add(flecs::Sparse); + + ecs_set_scope(world, c_); world.emplace(world); ecs_set_scope(world, scope); + ecs_add_id(world, c_, EcsModule); + // It should now be possible to lookup the module - ecs_entity_t m = ecs_lookup_symbol(world, symbol, true, false); + ecs_entity_t m = ecs_lookup_symbol(world, symbol, false, false); ecs_assert(m != 0, ECS_MODULE_UNDEFINED, symbol); - ecs_assert(m == m_c, ECS_INTERNAL_ERROR, NULL); + ecs_assert(m == c_, ECS_INTERNAL_ERROR, NULL); - ecs_log_pop(); + ecs_log_pop(); return m; } @@ -40,13 +46,13 @@ flecs::entity import(world& world) { const char *symbol = _::symbol_name(); ecs_entity_t m = ecs_lookup_symbol(world, symbol, true, false); - - if (!_::cpp_type::registered(world)) { + + if (!_::type::registered(world)) { /* Module is registered with world, initialize static data */ if (m) { - _::cpp_type::init(m, false); - + _::type::init(m, false); + /* Module is not yet registered, register it now */ } else { m = _::do_import(world, symbol); @@ -65,20 +71,38 @@ flecs::entity import(world& world) { /** * @defgroup cpp_addons_modules Modules - * @brief Modules organize components, systems and more in reusable units of code. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Modules organize components, systems and more in reusable units of code. + * * @{ */ template inline flecs::entity world::module(const char *name) const { - flecs::id_t result = _::cpp_type::id(m_world, nullptr, false); + flecs::entity result = this->entity(_::type::id( + world_, nullptr, false)); + if (name) { - ecs_add_path_w_sep(m_world, result, 0, name, "::", "::"); + flecs::entity prev_parent = result.parent(); + ecs_add_path_w_sep(world_, result, 0, name, "::", "::"); + flecs::entity parent = result.parent(); + if (prev_parent != parent) { + // Module was reparented, cleanup old parent(s) + flecs::entity cur = prev_parent, next; + while (cur) { + next = cur.parent(); + + ecs_iter_t it = ecs_each_id(world_, ecs_pair(EcsChildOf, cur)); + if (!ecs_iter_is_true(&it)) { + cur.destruct(); + } + + cur = next; + } + } } - ecs_set_scope(m_world, result); - return flecs::entity(m_world, result); + + return result; } template diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/module/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/module/mixin.inl index 2c7420e18..f51519c4a 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/module/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/module/mixin.inl @@ -4,8 +4,8 @@ */ /** - * \memberof flecs::world - * \ingroup cpp_addons_modules + * @memberof flecs::world + * @ingroup cpp_addons_modules * * @{ */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/monitor/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/monitor/decl.hpp deleted file mode 100644 index d22edcce9..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/monitor/decl.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file addons/cpp/mixins/monitor/decl.hpp - * @brief Monitor module declarations. - */ - -#pragma once - -namespace flecs { - -/** - * @defgroup cpp_addons_monitor Monitor - * @brief The monitor addon periodically tracks statistics for the world and systems. - * - * \ingroup cpp_addons - * @{ - */ - -/** Component that stores world statistics */ -using WorldStats = EcsWorldStats; - -/** Component that stores system/pipeline statistics */ -using PipelineStats = EcsPipelineStats; - -struct monitor { - monitor(flecs::world& world); -}; - -/** @} */ - -} \ No newline at end of file diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/monitor/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/monitor/impl.hpp deleted file mode 100644 index 685d5175a..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/monitor/impl.hpp +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file addons/cpp/mixins/monitor/impl.hpp - * @brief Monitor module implementation. - */ - -#pragma once - -namespace flecs { - -inline monitor::monitor(flecs::world& world) { - /* Import C module */ - FlecsMonitorImport(world); - -} - -} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/builder.hpp index 2faa2ebe2..74d1440de 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/builder.hpp @@ -18,7 +18,7 @@ namespace _ { /** Observer builder. * - * \ingroup cpp_observers + * @ingroup cpp_observers */ template struct observer_builder final : _::observer_builder_base { diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/builder_i.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/builder_i.hpp index 53ffc310c..533d58ad8 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/builder_i.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/builder_i.hpp @@ -5,32 +5,32 @@ #pragma once -#include "../filter/builder_i.hpp" +#include "../query/builder_i.hpp" namespace flecs { /** Observer builder interface. * - * \ingroup cpp_observers + * @ingroup cpp_observers */ template -struct observer_builder_i : filter_builder_i { - using BaseClass = filter_builder_i; +struct observer_builder_i : query_builder_i { + using BaseClass = query_builder_i; observer_builder_i() : BaseClass(nullptr) - , m_desc(nullptr) - , m_event_count(0) { } + , desc_(nullptr) + , event_count_(0) { } observer_builder_i(ecs_observer_desc_t *desc) - : BaseClass(&desc->filter) - , m_desc(desc) - , m_event_count(0) { } + : BaseClass(&desc->query) + , desc_(desc) + , event_count_(0) { } /** Specify the event(s) for when the observer should run. * @param evt The event. */ Base& event(entity_t evt) { - m_desc->events[m_event_count ++] = evt; + desc_->events[event_count_ ++] = evt; return *this; } @@ -39,38 +39,44 @@ struct observer_builder_i : filter_builder_i { */ template Base& event() { - m_desc->events[m_event_count ++] = _::cpp_type().id(world_v()); + desc_->events[event_count_ ++] = _::type().id(world_v()); return *this; } - /** Invoke observer for anything that matches its filter on creation */ + /** Invoke observer for anything that matches its query on creation */ Base& yield_existing(bool value = true) { - m_desc->yield_existing = value; + desc_->yield_existing = value; + return *this; + } + + /** Set observer flags */ + Base& observer_flags(ecs_flags32_t flags) { + desc_->flags_ |= flags; return *this; } /** Set observer context */ Base& ctx(void *ptr) { - m_desc->ctx = ptr; + desc_->ctx = ptr; return *this; } /** Set observer run callback */ Base& run(ecs_iter_action_t action) { - m_desc->run = action; + desc_->run = action; return *this; } protected: - virtual flecs::world_t* world_v() = 0; + virtual flecs::world_t* world_v() override = 0; private: operator Base&() { return *static_cast(this); } - ecs_observer_desc_t *m_desc; - int32_t m_event_count; + ecs_observer_desc_t *desc_; + int32_t event_count_; }; } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/decl.hpp index 6213dd87e..ebf3f2061 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/decl.hpp @@ -9,9 +9,9 @@ namespace flecs { /** * @defgroup cpp_observers Observers - * @brief Observers let applications register callbacks for ECS events. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Observers let applications register callbacks for ECS events. + * * @{ */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/impl.hpp index 506305c6a..6b0e05d1b 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/impl.hpp @@ -16,46 +16,35 @@ struct observer final : entity explicit observer() : entity() { } - observer(flecs::world_t *world, ecs_observer_desc_t *desc, bool instanced) - { - if (!desc->filter.instanced) { - desc->filter.instanced = instanced; - } - - m_world = world; - m_id = ecs_observer_init(world, desc); - - if (desc->filter.terms_buffer) { - ecs_os_free(desc->filter.terms_buffer); - } + observer(flecs::world_t *world, ecs_observer_desc_t *desc) { + world_ = world; + id_ = ecs_observer_init(world, desc); } void ctx(void *ctx) { ecs_observer_desc_t desc = {}; - desc.entity = m_id; + desc.entity = id_; desc.ctx = ctx; - ecs_observer_init(m_world, &desc); + ecs_observer_init(world_, &desc); } void* ctx() const { - return ecs_observer_get_ctx(m_world, m_id); + return ecs_observer_get(world_, id_)->ctx; } - flecs::filter<> query() const { - const flecs::Poly *poly = this->get(flecs::Observer); - const ecs_observer_t *ob = static_cast(poly->poly); - return flecs::filter<>(m_world, &ob->filter); + flecs::query<> query() const { + return flecs::query<>(ecs_observer_get(world_, id_)->query); } }; // Mixin implementation inline observer world::observer(flecs::entity e) const { - return flecs::observer(m_world, e); + return flecs::observer(world_, e); } template inline observer_builder world::observer(Args &&... args) const { - return flecs::observer_builder(m_world, FLECS_FWD(args)...); + return flecs::observer_builder(world_, FLECS_FWD(args)...); } } // namespace flecs diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/mixin.inl index 40da3f3cb..3becc9496 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/observer/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/observer/mixin.inl @@ -5,8 +5,10 @@ /** Observer builder. * - * \memberof flecs::world - * \ingroup cpp_observers + * @memberof flecs::world + * @ingroup cpp_observers + * + * @{ */ /** Upcast entity to an observer. diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/builder.hpp index 0779910ec..4e99fc00d 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/builder.hpp @@ -18,7 +18,7 @@ namespace _ { /** Pipeline builder. * - * \ingroup cpp_pipelines + * @ingroup cpp_pipelines */ template struct pipeline_builder final : _::pipeline_builder_base { @@ -26,7 +26,7 @@ struct pipeline_builder final : _::pipeline_builder_base { : _::pipeline_builder_base(world) { _::sig(world).populate(this); - this->m_desc.entity = id; + this->desc_.entity = id; } }; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/builder_i.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/builder_i.hpp index cb70b855f..e3349da13 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/builder_i.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/builder_i.hpp @@ -11,16 +11,16 @@ namespace flecs { /** Pipeline builder interface. * - * \ingroup cpp_pipelines + * @ingroup cpp_pipelines */ template struct pipeline_builder_i : query_builder_i { pipeline_builder_i(ecs_pipeline_desc_t *desc, int32_t term_index = 0) : query_builder_i(&desc->query, term_index) - , m_desc(desc) { } + , desc_(desc) { } private: - ecs_pipeline_desc_t *m_desc; + ecs_pipeline_desc_t *desc_; }; } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/decl.hpp index 2d2f33c68..0ea1f6f24 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/decl.hpp @@ -9,9 +9,9 @@ namespace flecs { /** * @defgroup cpp_pipelines Pipelines - * @brief Pipelines order and schedule systems for execution. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Pipelines order and schedule systems for execution. + * * @{ */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/impl.hpp index f9595d7b6..e75b20880 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/impl.hpp @@ -14,79 +14,75 @@ struct pipeline : entity { pipeline(world_t *world, ecs_pipeline_desc_t *desc) : entity(world) { - m_id = ecs_pipeline_init(world, desc); + id_ = ecs_pipeline_init(world, desc); - if (!m_id) { + if (!id_) { ecs_abort(ECS_INVALID_PARAMETER, NULL); } - - if (desc->query.filter.terms_buffer) { - ecs_os_free(desc->query.filter.terms_buffer); - } } }; inline flecs::pipeline_builder<> world::pipeline() const { - return flecs::pipeline_builder<>(m_world); + return flecs::pipeline_builder<>(world_); } template ::value >> inline flecs::pipeline_builder<> world::pipeline() const { - return flecs::pipeline_builder<>(m_world, _::cpp_type::id(m_world)); + return flecs::pipeline_builder<>(world_, _::type::id(world_)); } inline void world::set_pipeline(const flecs::entity pip) const { - return ecs_set_pipeline(m_world, pip); + return ecs_set_pipeline(world_, pip); } template inline void world::set_pipeline() const { - return ecs_set_pipeline(m_world, _::cpp_type::id(m_world)); + return ecs_set_pipeline(world_, _::type::id(world_)); } inline flecs::entity world::get_pipeline() const { - return flecs::entity(m_world, ecs_get_pipeline(m_world)); + return flecs::entity(world_, ecs_get_pipeline(world_)); } inline bool world::progress(ecs_ftime_t delta_time) const { - return ecs_progress(m_world, delta_time); + return ecs_progress(world_, delta_time); } inline void world::run_pipeline(const flecs::entity_t pip, ecs_ftime_t delta_time) const { - return ecs_run_pipeline(m_world, pip, delta_time); + return ecs_run_pipeline(world_, pip, delta_time); } template ::value >> inline void world::run_pipeline(ecs_ftime_t delta_time) const { - return ecs_run_pipeline(m_world, _::cpp_type::id(m_world), delta_time); + return ecs_run_pipeline(world_, _::type::id(world_), delta_time); } inline void world::set_time_scale(ecs_ftime_t mul) const { - ecs_set_time_scale(m_world, mul); + ecs_set_time_scale(world_, mul); } inline void world::set_target_fps(ecs_ftime_t target_fps) const { - ecs_set_target_fps(m_world, target_fps); + ecs_set_target_fps(world_, target_fps); } inline void world::reset_clock() const { - ecs_reset_clock(m_world); + ecs_reset_clock(world_); } inline void world::set_threads(int32_t threads) const { - ecs_set_threads(m_world, threads); + ecs_set_threads(world_, threads); } inline int32_t world::get_threads() const { - return ecs_get_stage_count(m_world); + return ecs_get_stage_count(world_); } inline void world::set_task_threads(int32_t task_threads) const { - ecs_set_task_threads(m_world, task_threads); + ecs_set_task_threads(world_, task_threads); } inline bool world::using_task_threads() const { - return ecs_using_task_threads(m_world); + return ecs_using_task_threads(world_); } } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/mixin.inl index d41945922..0f34c7c0e 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/pipeline/mixin.inl @@ -4,8 +4,10 @@ */ /** - * \memberof flecs::world - * \ingroup cpp_pipelines + * @memberof flecs::world + * @ingroup cpp_pipelines + * + * @{ */ /** Create a new pipeline. diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/plecs/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/plecs/mixin.inl deleted file mode 100644 index 8ff595100..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/plecs/mixin.inl +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file addons/cpp/mixins/plecs/mixin.inl - * @brief Plecs world mixin. - */ - -/** - * @defgroup cpp_addons_plecs Plecs - * @brief Data definition format for loading entity data. - * - * \ingroup cpp_addons - * @{ - */ - -/** Load plecs string. - * @see ecs_plecs_from_str - */ -int plecs_from_str(const char *name, const char *str) const { - return ecs_plecs_from_str(m_world, name, str); -} - -/** Load plecs from file. - * @see ecs_plecs_from_file - */ -int plecs_from_file(const char *filename) const { - return ecs_plecs_from_file(m_world, filename); -} - -/** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/query/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/query/builder.hpp index 524e6f261..7cbd2cfa9 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/query/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/query/builder.hpp @@ -18,10 +18,17 @@ namespace _ { /** Query builder. * - * \ingroup cpp_core_queries + * @ingroup cpp_core_queries */ template struct query_builder final : _::query_builder_base { + query_builder(flecs::world_t* world, flecs::entity query_entity) + : _::query_builder_base(world) + { + _::sig(world).populate(this); + this->desc_.entity = query_entity.id(); + } + query_builder(flecs::world_t* world, const char *name = nullptr) : _::query_builder_base(world) { @@ -31,9 +38,14 @@ struct query_builder final : _::query_builder_base { entity_desc.name = name; entity_desc.sep = "::"; entity_desc.root_sep = "::"; - this->m_desc.filter.entity = ecs_entity_init(world, &entity_desc); + this->desc_.entity = ecs_entity_init(world, &entity_desc); } } + + template + void each(Func&& func) { + this->build().each(FLECS_FWD(func)); + } }; } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/query/builder_i.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/query/builder_i.hpp index 6a5f24865..c5e7258e2 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/query/builder_i.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/query/builder_i.hpp @@ -5,27 +5,246 @@ #pragma once -#include "../filter/builder_i.hpp" +#include "../term/builder_i.hpp" -namespace flecs { +namespace flecs +{ /** Query builder interface. * - * \ingroup cpp_core_queries + * @ingroup cpp_core_queries */ template -struct query_builder_i : filter_builder_i { -private: - using BaseClass = filter_builder_i; +struct query_builder_i : term_builder_i { + query_builder_i(ecs_query_desc_t *desc, int32_t term_index = 0) + : term_index_(term_index) + , expr_count_(0) + , desc_(desc) { } + + Base& query_flags(ecs_flags32_t flags) { + desc_->flags |= flags; + return *this; + } + + Base& cache_kind(query_cache_kind_t kind) { + desc_->cache_kind = static_cast(kind); + return *this; + } + + Base& cached() { + return cache_kind(flecs::QueryCacheAuto); + } + + Base& expr(const char *expr) { + ecs_check(expr_count_ == 0, ECS_INVALID_OPERATION, + "query_builder::expr() called more than once"); + desc_->expr = expr; + expr_count_ ++; + + error: + return *this; + } + + /* With methods */ + + template + Base& with() { + this->term(); + *this->term_ = flecs::term(_::type::id(this->world_v())); + this->term_->inout = static_cast( + _::type_to_inout()); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(id_t id) { + this->term(); + *this->term_ = flecs::term(id); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(const char *name) { + this->term(); + *this->term_ = flecs::term().first(name); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(const char *first, const char *second) { + this->term(); + *this->term_ = flecs::term().first(first).second(second); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(entity_t r, entity_t o) { + this->term(); + *this->term_ = flecs::term(r, o); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(entity_t r, const char *o) { + this->term(); + *this->term_ = flecs::term(r).second(o); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + Base& with(const char *r, entity_t o) { + this->term(); + *this->term_ = flecs::term().first(r).second(o); + if (this->term_->inout == EcsInOutDefault) { + this->inout_none(); + } + return *this; + } + + template + Base& with(id_t o) { + return this->with(_::type::id(this->world_v()), o); + } + + template + Base& with(const char *second) { + return this->with(_::type::id(this->world_v())).second(second); + } + + template + Base& with() { + return this->with(_::type::id(this->world_v())); + } + + template ::value > = 0> + Base& with(E value) { + flecs::entity_t r = _::type::id(this->world_v()); + auto o = enum_type(this->world_v()).entity(value); + return this->with(r, o); + } + + Base& with(flecs::term& term) { + this->term(); + *this->term_ = term; + return *this; + } + + Base& with(flecs::term&& term) { + this->term(); + *this->term_ = term; + return *this; + } + + /* Without methods, shorthand for .with(...).not_(). */ + + template + Base& without(Args&&... args) { + return this->with(FLECS_FWD(args)...).not_(); + } + + template + Base& without(Args&&... args) { + return this->with(FLECS_FWD(args)...).not_(); + } + + template + Base& without() { + return this->with().not_(); + } + + /* Write/read methods */ + + Base& write() { + term_builder_i::write(); + return *this; + } + + template + Base& write(Args&&... args) { + return this->with(FLECS_FWD(args)...).write(); + } + + template + Base& write(Args&&... args) { + return this->with(FLECS_FWD(args)...).write(); + } + + template + Base& write() { + return this->with().write(); + } + + Base& read() { + term_builder_i::read(); + return *this; + } + + template + Base& read(Args&&... args) { + return this->with(FLECS_FWD(args)...).read(); + } + + template + Base& read(Args&&... args) { + return this->with(FLECS_FWD(args)...).read(); + } + + template + Base& read() { + return this->with().read(); + } + + /* Scope_open/scope_close shorthand notation. */ + Base& scope_open() { + return this->with(flecs::ScopeOpen).entity(0); + } + + Base& scope_close() { + return this->with(flecs::ScopeClose).entity(0); + } + + /* Term notation for more complex query features */ + + Base& term() { + if (this->term_) { + ecs_check(ecs_term_is_initialized(this->term_), + ECS_INVALID_OPERATION, + "query_builder::term() called without initializing term"); + } + + ecs_check(term_index_ < FLECS_TERM_COUNT_MAX, + ECS_INVALID_PARAMETER, "maximum number of terms exceeded"); + + this->set_term(&desc_->terms[term_index_]); + + term_index_ ++; -public: - query_builder_i() - : BaseClass(nullptr) - , m_desc(nullptr) { } + error: + return *this; + } - query_builder_i(ecs_query_desc_t *desc, int32_t term_index = 0) - : BaseClass(&desc->filter, term_index) - , m_desc(desc) { } + Base& term_at(int32_t term_index) { + ecs_assert(term_index >= 0, ECS_INVALID_PARAMETER, NULL); + int32_t prev_index = term_index_; + term_index_ = term_index; + this->term(); + term_index_ = prev_index; + ecs_assert(ecs_term_is_initialized(this->term_), + ECS_INVALID_PARAMETER, NULL); + return *this; + } /** Sort the output of a query. * This enables sorting of entities across matched tables. As a result of this @@ -48,7 +267,7 @@ struct query_builder_i : filter_builder_i { template Base& order_by(int(*compare)(flecs::entity_t, const T*, flecs::entity_t, const T*)) { ecs_order_by_action_t cmp = reinterpret_cast(compare); - return this->order_by(_::cpp_type::id(this->world_v()), cmp); + return this->order_by(_::type::id(this->world_v()), cmp); } /** Sort the output of a query. @@ -58,13 +277,13 @@ struct query_builder_i : filter_builder_i { * @param compare The compare function used to sort the components. */ Base& order_by(flecs::entity_t component, int(*compare)(flecs::entity_t, const void*, flecs::entity_t, const void*)) { - m_desc->order_by = reinterpret_cast(compare); - m_desc->order_by_component = component; + desc_->order_by_callback = reinterpret_cast(compare); + desc_->order_by = component; return *this; } /** Group and sort matched tables. - * Similar yo ecs_query_order_by, but instead of sorting individual entities, this + * Similar to ecs_query_order_by(), but instead of sorting individual entities, this * operation only sorts matched tables. This can be useful of a query needs to * enforce a certain iteration order upon the tables it is iterating, for * example by giving a certain component or tag a higher priority. @@ -83,7 +302,7 @@ struct query_builder_i : filter_builder_i { template Base& group_by(uint64_t(*group_by_action)(flecs::world_t*, flecs::table_t *table, flecs::id_t id, void* ctx)) { ecs_group_by_action_t action = reinterpret_cast(group_by_action); - return this->group_by(_::cpp_type::id(this->world_v()), action); + return this->group_by(_::type::id(this->world_v()), action); } /** Group and sort matched tables. @@ -93,8 +312,8 @@ struct query_builder_i : filter_builder_i { * @param group_by_action Callback that determines group id for table. */ Base& group_by(flecs::entity_t component, uint64_t(*group_by_action)(flecs::world_t*, flecs::table_t *table, flecs::id_t id, void* ctx)) { - m_desc->group_by = reinterpret_cast(group_by_action); - m_desc->group_by_id = component; + desc_->group_by_callback = reinterpret_cast(group_by_action); + desc_->group_by = component; return *this; } @@ -105,7 +324,7 @@ struct query_builder_i : filter_builder_i { */ template Base& group_by() { - return this->group_by(_::cpp_type::id(this->world_v()), nullptr); + return this->group_by(_::type::id(this->world_v()), nullptr); } /** Group and sort matched tables. @@ -123,37 +342,36 @@ struct query_builder_i : filter_builder_i { * @param ctx_free Function to cleanup context (called when query is deleted). */ Base& group_by_ctx(void *ctx, ecs_ctx_free_t ctx_free = nullptr) { - m_desc->group_by_ctx = ctx; - m_desc->group_by_ctx_free = ctx_free; + desc_->group_by_ctx = ctx; + desc_->group_by_ctx_free = ctx_free; return *this; } /** Specify on_group_create action. */ Base& on_group_create(ecs_group_create_action_t action) { - m_desc->on_group_create = action; + desc_->on_group_create = action; return *this; } /** Specify on_group_delete action. */ Base& on_group_delete(ecs_group_delete_action_t action) { - m_desc->on_group_delete = action; + desc_->on_group_delete = action; return *this; } - /** Specify parent query (creates subquery) */ - Base& observable(const query_base& parent); - protected: - virtual flecs::world_t* world_v() = 0; + virtual flecs::world_t* world_v() override = 0; + int32_t term_index_; + int32_t expr_count_; private: operator Base&() { return *static_cast(this); } - ecs_query_desc_t *m_desc; + ecs_query_desc_t *desc_; }; } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/query/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/query/decl.hpp index c613ffc1e..a89e88a35 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/query/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/query/decl.hpp @@ -9,9 +9,8 @@ namespace flecs { /** * @defgroup cpp_core_queries Queries - * @brief Cached query implementation. Fast to iterate, but slower to create than flecs::filter. - * - * \ingroup cpp_core + * @ingroup cpp_core + * * @{ */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/query/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/query/impl.hpp index 452b0c8c1..c0c874bc3 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/query/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/query/impl.hpp @@ -7,37 +7,97 @@ #include "builder.hpp" -namespace flecs { - -//////////////////////////////////////////////////////////////////////////////// -//// Persistent queries -//////////////////////////////////////////////////////////////////////////////// +namespace flecs +{ struct query_base { - query_base() - : m_world(nullptr) - , m_query(nullptr) { } - - query_base(world_t *world, query_t *query = nullptr) - : m_world(world) - , m_query(query) { } - - query_base(world_t *world, ecs_query_desc_t *desc) - : m_world(world) - { - m_query = ecs_query_init(world, desc); - - if (!m_query) { - ecs_abort(ECS_INVALID_PARAMETER, NULL); + query_base() { } + + query_base(query_t *q) + : query_(q) { + flecs_poly_claim(q); + } + + query_base(const query_t *q) + : query_(ECS_CONST_CAST(query_t*, q)) { + flecs_poly_claim(q); } - if (desc->filter.terms_buffer) { - ecs_os_free(desc->filter.terms_buffer); + query_base(world_t *world, ecs_query_desc_t *desc) { + if (desc->entity && desc->terms[0].id == 0) { + const flecs::Poly *query_poly = ecs_get_pair( + world, desc->entity, EcsPoly, EcsQuery); + if (query_poly) { + query_ = static_cast(query_poly->poly); + flecs_poly_claim(query_); + return; + } } + + query_ = ecs_query_init(world, desc); + } + + query_base(const query_base& obj) { + this->query_ = obj.query_; + flecs_poly_claim(this->query_); + } + + query_base& operator=(const query_base& obj) { + this->query_ = obj.query_; + flecs_poly_claim(this->query_); + return *this; + } + + query_base(query_base&& obj) noexcept { + this->query_ = obj.query_; + obj.query_ = nullptr; } - operator query_t*() const { - return m_query; + query_base& operator=(query_base&& obj) noexcept { + this->query_ = obj.query_; + obj.query_ = nullptr; + return *this; + } + + flecs::entity entity() { + return flecs::entity(query_->world, query_->entity); + } + + const flecs::query_t* c_ptr() const { + return query_; + } + + operator const flecs::query_t*() const { + return query_; + } + + operator bool() const { + return query_ != nullptr; + } + + /** Free persistent query. + * A persistent query is a query that is associated with an entity, such as + * system queries and named queries. Persistent queries must be deleted with + * destruct(), or will be deleted automatically at world cleanup. + */ + void destruct() { + ecs_assert(query_->entity != 0, ECS_INVALID_OPERATION, "destruct() " + "should only be called on queries associated with entities"); + ecs_query_fini(query_); + query_ = nullptr; + } + + ~query_base() { + /* Only free if query is not associated with entity, such as system + * queries and named queries. Named queries have to be either explicitly + * deleted with the .destruct() method, or will be deleted when the + * world is deleted. */ + if (query_ && !query_->entity) { + if (!flecs_poly_release(query_)) { + ecs_query_fini(query_); + query_ = nullptr; + } + } } /** Returns whether the query data changed since the last iteration. @@ -50,18 +110,7 @@ struct query_base { * @return true if entities changed, otherwise false. */ bool changed() const { - return ecs_query_changed(m_query, 0); - } - - /** Returns whether query is orphaned. - * When the parent query of a subquery is deleted, it is left in an orphaned - * state. The only valid operation on an orphaned query is deleting it. Only - * subqueries can be orphaned. - * - * @return true if query is orphaned, otherwise false. - */ - bool orphaned() const { - return ecs_query_orphaned(m_query); + return ecs_query_changed(query_); } /** Get info for group. @@ -70,7 +119,7 @@ struct query_base { * @return The group info. */ const flecs::query_group_info_t* group_info(uint64_t group_id) const { - return ecs_query_get_group_info(m_query, group_id); + return ecs_query_get_group_info(query_, group_id); } /** Get context for group. @@ -87,102 +136,183 @@ struct query_base { } } - /** Free the query. - */ - void destruct() { - ecs_query_fini(m_query); - m_world = nullptr; - m_query = nullptr; + template + void each_term(const Func& func) { + for (int i = 0; i < query_->term_count; i ++) { + flecs::term t(query_->world, query_->terms[i]); + func(t); + t.reset(); // prevent freeing resources + } } - template - void each_term(const Func& func) const { - this->filter().each_term(func); + flecs::term term(int32_t index) { + return flecs::term(query_->world, query_->terms[index]); } - filter_base filter() const { - return filter_base(m_world, ecs_query_get_filter(m_query)); + int32_t term_count() { + return query_->term_count; } - flecs::term term(int32_t index) const { - const ecs_filter_t *f = ecs_query_get_filter(m_query); - ecs_assert(f != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs::term(m_world, f->terms[index]); + int32_t field_count() { + return query_->field_count; } - int32_t field_count() const { - const ecs_filter_t *f = ecs_query_get_filter(m_query); - return f->term_count; + int32_t find_var(const char *name) { + return ecs_query_find_var(query_, name); } - flecs::string str() const { - const ecs_filter_t *f = ecs_query_get_filter(m_query); - char *result = ecs_filter_str(m_world, f); + flecs::string str() { + char *result = ecs_query_str(query_); return flecs::string(result); } - flecs::entity entity() const { - return flecs::entity(m_world, ecs_get_entity(m_query)); + /** Returns a string representing the query plan. + * This can be used to analyze the behavior & performance of the query. + * @see ecs_query_plan + */ + flecs::string plan() const { + char *result = ecs_query_plan(query_); + return flecs::string(result); } - + operator query<>() const; protected: - world_t *m_world; - query_t *m_query; + query_t *query_ = nullptr; }; template -struct query final : query_base, iterable { +struct query : query_base, iterable { +private: + using Fields = typename _::field_ptrs::array; + public: - flecs::world world() const { - return flecs::world(m_world); + using query_base::query_base; + + query() : query_base() { } // necessary not to confuse msvc + + query(const query& obj) : query_base(obj) { } + + query& operator=(const query& obj) { + query_base::operator=(obj); + return *this; + } + + query(query&& obj) noexcept : query_base(FLECS_MOV(obj)) { } + + query& operator=(query&& obj) noexcept { + query_base::operator=(FLECS_FWD(obj)); + return *this; } - -private: - using Terms = typename _::term_ptrs::array; +private: ecs_iter_t get_iter(flecs::world_t *world) const override { + ecs_assert(query_ != nullptr, ECS_INVALID_PARAMETER, + "cannot iterate invalid query"); if (!world) { - world = m_world; + world = query_->world; } - return ecs_query_iter(world, m_query); + return ecs_query_iter(world, query_); } ecs_iter_next_action_t next_action() const override { return ecs_query_next; } - - ecs_iter_next_action_t next_each_action() const override { - return ecs_query_next_instanced; - } - -public: - using query_base::query_base; }; -// Mixin implementation +// World mixin implementation template inline flecs::query world::query(Args &&... args) const { - return flecs::query_builder(m_world, FLECS_FWD(args)...) + return flecs::query_builder(world_, FLECS_FWD(args)...) .build(); } +inline flecs::query<> world::query(flecs::entity query_entity) const { + ecs_query_desc_t desc = {}; + desc.entity = query_entity; + return flecs::query<>(world_, &desc); +} + template inline flecs::query_builder world::query_builder(Args &&... args) const { - return flecs::query_builder(m_world, FLECS_FWD(args)...); + return flecs::query_builder(world_, FLECS_FWD(args)...); } -// Builder implementation -template -inline Base& query_builder_i::observable(const query_base& parent) { - m_desc->parent = parent; - return *static_cast(this); +// world::each +namespace _ { + +// Each with entity parameter +template +struct query_delegate_w_ent; + +template +struct query_delegate_w_ent > +{ + query_delegate_w_ent(const flecs::world& world, Func&& func) { + auto f = world.query(); + f.each(FLECS_MOV(func)); + } +}; + +// Each without entity parameter +template +struct query_delegate_no_ent; + +template +struct query_delegate_no_ent > +{ + query_delegate_no_ent(const flecs::world& world, Func&& func) { + auto f = world.query(); + f.each(FLECS_MOV(func)); + } +}; + +// Switch between function with & without entity parameter +template +struct query_delegate; + +template +struct query_delegate, flecs::entity>::value> > { + query_delegate(const flecs::world& world, Func&& func) { + query_delegate_w_ent>(world, FLECS_MOV(func)); + } +}; + +template +struct query_delegate, flecs::entity>::value> > { + query_delegate(const flecs::world& world, Func&& func) { + query_delegate_no_ent>(world, FLECS_MOV(func)); + } +}; + +} + +template +inline void world::each(Func&& func) const { + _::query_delegate f_delegate(*this, FLECS_MOV(func)); +} + +template +inline void world::each(Func&& func) const { + ecs_iter_t it = ecs_each_id(world_, _::type::id()); + + while (ecs_each_next(&it)) { + _::each_delegate(func).invoke(&it); + } +} + +template +inline void world::each(flecs::id_t each_id, Func&& func) const { + ecs_iter_t it = ecs_each_id(world_, each_id); + + while (ecs_each_next(&it)) { + _::each_delegate(func).invoke(&it); + } } // query_base implementation -inline query_base::operator query<>() const { - return flecs::query<>(m_world, m_query); +inline query_base::operator flecs::query<> () const { + return flecs::query<>(query_); } -} // namespace flecs +} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/query/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/query/mixin.inl index 4e2d8d436..21d5e8e8c 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/query/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/query/mixin.inl @@ -4,26 +4,68 @@ */ /** - * \memberof flecs::world - * \ingroup cpp_core_queries + * @memberof flecs::world + * @ingroup cpp_core_queries + * + * @{ */ /** Create a query. + * * @see ecs_query_init */ template flecs::query query(Args &&... args) const; -/** Create a subquery. +/** Create a query from entity. + * * @see ecs_query_init */ -template -flecs::query query(flecs::query_base& parent, Args &&... args) const; +flecs::query<> query(flecs::entity query_entity) const; /** Create a query builder. + * * @see ecs_query_init */ template flecs::query_builder query_builder(Args &&... args) const; +/** Iterate over all entities with components in argument list of function. + * The function parameter must match the following signature: + * + * @code + * void(*)(T&, U&, ...) + * @endcode + * + * or: + * + * @code + * void(*)(flecs::entity, T&, U&, ...) + * @endcode + * + */ +template +void each(Func&& func) const; + +/** Iterate over all entities with provided component. + * The function parameter must match the following signature: + * + * @code + * void(*)(T&) + * @endcode + * + * or: + * + * @code + * void(*)(flecs::entity, T&) + * @endcode + * + */ +template +void each(Func&& func) const; + +/** Iterate over all entities with provided (component) id. */ +template +void each(flecs::id_t term_id, Func&& func) const; + /** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/rest/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/rest/decl.hpp index acc63cb6b..fac86b2de 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/rest/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/rest/decl.hpp @@ -9,9 +9,9 @@ namespace flecs { /** * @defgroup cpp_addons_rest Rest - * @brief REST API for querying and mutating entities. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * REST API for querying and mutating entities. + * * @{ */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/rule/builder.hpp deleted file mode 100644 index 2e98c5413..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/builder.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file addons/cpp/mixins/rule/builder.hpp - * @brief Rule builder. - */ - -#pragma once - -#include "../filter/builder_i.hpp" - -namespace flecs { -namespace _ { - template - using rule_builder_base = builder< - rule, ecs_filter_desc_t, rule_builder, - filter_builder_i, Components ...>; -} - -/** Rule builder. - * - * \ingroup cpp_addons_rules - */ -template -struct rule_builder final : _::rule_builder_base { - rule_builder(flecs::world_t* world, const char *name = nullptr) - : _::rule_builder_base(world) - { - _::sig(world).populate(this); - if (name != nullptr) { - ecs_entity_desc_t entity_desc = {}; - entity_desc.name = name; - entity_desc.sep = "::"; - entity_desc.root_sep = "::"; - this->m_desc.entity = ecs_entity_init(world, &entity_desc); - } - } -}; - -} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/rule/decl.hpp deleted file mode 100644 index 586b89997..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/decl.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file addons/cpp/mixins/rule/decl.hpp - * @brief Rule declarations. - */ - -#pragma once - -namespace flecs { - -/** - * @defgroup cpp_addons_rules Rules - * @brief Rules are an advanced query engine for matching against entity graphs. - * - * \ingroup cpp_addons - * @{ - */ - -struct rule_base; - -template -struct rule; - -template -struct rule_builder; - -/** @} */ - -} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/rule/impl.hpp deleted file mode 100644 index b9a8e9fe9..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/impl.hpp +++ /dev/null @@ -1,144 +0,0 @@ -/** - * @file addons/cpp/mixins/rule/impl.hpp - * @brief Rule implementation. - */ - -#pragma once - -#include "builder.hpp" - -namespace flecs { - -//////////////////////////////////////////////////////////////////////////////// -//// Persistent queries -//////////////////////////////////////////////////////////////////////////////// - -struct rule_base { - rule_base() - : m_world(nullptr) - , m_rule(nullptr) { } - - rule_base(world_t *world, rule_t *rule = nullptr) - : m_world(world) - , m_rule(rule) { } - - rule_base(world_t *world, ecs_filter_desc_t *desc) - : m_world(world) - { - m_rule = ecs_rule_init(world, desc); - if (desc->terms_buffer) { - ecs_os_free(desc->terms_buffer); - } - } - - bool is_valid() const { - return m_rule != nullptr; - } - - operator rule_t*() const { - return m_rule; - } - - flecs::entity entity() { - return flecs::entity(m_world, ecs_get_entity(m_rule)); - } - - /** Free the rule. */ - void destruct() { - if (m_rule) { - ecs_rule_fini(m_rule); - m_world = nullptr; - m_rule = nullptr; - } - } - - template - void each_term(const Func& func) const { - this->filter().each_term(func); - } - - /** Move the rule. */ - void move(flecs::rule_base&& obj) { - this->destruct(); - this->m_world = obj.m_world; - this->m_rule = obj.m_rule; - obj.m_world = nullptr; - obj.m_rule = nullptr; - } - - flecs::filter_base filter() const { - return filter_base(m_world, ecs_rule_get_filter(m_rule)); - } - - /** Converts this rule to a string expression - * @see ecs_filter_str - */ - flecs::string str() const { - const ecs_filter_t *f = ecs_rule_get_filter(m_rule); - char *result = ecs_filter_str(m_world, f); - return flecs::string(result); - } - - - /** Converts this rule to a string that can be used to aid debugging - * the behavior of the rule. - * @see ecs_rule_str - */ - flecs::string rule_str() const { - char *result = ecs_rule_str(m_rule); - return flecs::string(result); - } - - operator rule<>() const; - -protected: - world_t *m_world; - rule_t *m_rule; -}; - -template -struct rule final : rule_base, iterable { -private: - using Terms = typename _::term_ptrs::array; - - ecs_iter_t get_iter(flecs::world_t *world) const override { - if (!world) { - world = m_world; - } - return ecs_rule_iter(world, m_rule); - } - - ecs_iter_next_action_t next_action() const override { - return ecs_rule_next; - } - - ecs_iter_next_action_t next_each_action() const override { - return ecs_rule_next_instanced; - } - -public: - using rule_base::rule_base; - - int32_t find_var(const char *name) { - return ecs_rule_find_var(m_rule, name); - } -}; - -// Mixin implementation -template -inline flecs::rule world::rule(Args &&... args) const { - return flecs::rule_builder(m_world, FLECS_FWD(args)...) - .build(); -} - -template -inline flecs::rule_builder world::rule_builder(Args &&... args) const { - return flecs::rule_builder(m_world, FLECS_FWD(args)...); -} - -// rule_base implementation -inline rule_base::operator rule<>() const { - return flecs::rule<>(m_world, m_rule); -} - -} // namespace flecs diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/iterable.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/rule/iterable.inl deleted file mode 100644 index 6a56be369..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/iterable.inl +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @file addons/cpp/mixins/rule/iterable.inl - * @brief Rule iterable mixin. - */ - -/** - * \memberof flecs::iter - * \ingroup cpp_addons_rules - */ - -iter_iterable& set_var(const char *name, flecs::entity_t value) { - ecs_rule_iter_t *rit = &m_it.priv.iter.rule; - int var_id = ecs_rule_find_var(rit->rule, name); - ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); - ecs_iter_set_var(&m_it, var_id, value); - return *this; -} - -/** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/rule/mixin.inl deleted file mode 100644 index 571af95e8..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/rule/mixin.inl +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @file addons/cpp/mixins/rule/mixin.inl - * @brief Rule world mixin. - */ - -/** - * \memberof flecs::world - * \ingroup cpp_addons_rules - */ - -/** Create a rule. - * @see ecs_rule_init - */ -template -flecs::rule rule(Args &&... args) const; - -/** Create a subrule. - * @see ecs_rule_init - */ -template -flecs::rule rule(flecs::rule_base& parent, Args &&... args) const; - -/** Create a rule builder. - * @see ecs_rule_init - */ -template -flecs::rule_builder rule_builder(Args &&... args) const; - -/** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/script/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/script/builder.hpp new file mode 100644 index 000000000..1a244b754 --- /dev/null +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/script/builder.hpp @@ -0,0 +1,47 @@ +/** + * @file addons/cpp/mixins/script/builder.hpp + * @brief Script builder. + */ + +#pragma once + +namespace flecs { + +/** + * @ingroup cpp_addons_script + * @{ + */ + +/** Script builder interface */ +struct script_builder { + script_builder(flecs::world_t *world, const char *name = nullptr) + : world_(world) + , desc_{} + { + if (name != nullptr) { + ecs_entity_desc_t entity_desc = {}; + entity_desc.name = name; + entity_desc.sep = "::"; + entity_desc.root_sep = "::"; + this->desc_.entity = ecs_entity_init(world, &entity_desc); + } + } + + script_builder& code(const char *str) { + desc_.code = str; + return *this; + } + + script_builder& filename(const char *str) { + desc_.filename = str; + return *this; + } + + flecs::entity run() const; + +protected: + flecs::world_t *world_; + ecs_script_desc_t desc_; +}; + +} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/script/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/script/decl.hpp new file mode 100644 index 000000000..21d789acf --- /dev/null +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/script/decl.hpp @@ -0,0 +1,23 @@ +/** + * @file addons/cpp/mixins/script/decl.hpp + * @brief Script declarations. + */ + +#pragma once + +#include "builder.hpp" + +namespace flecs { + +/** + * @defgroup cpp_addons_script Script + * @ingroup cpp_addons + * + * @{ + */ + +struct script_builder; + +/** @} */ + +} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/script/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/script/impl.hpp new file mode 100644 index 000000000..6c98076b4 --- /dev/null +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/script/impl.hpp @@ -0,0 +1,18 @@ +/** + * @file addons/cpp/mixins/script/impl.hpp + * @brief Script implementation. + */ + +#pragma once + +#include "builder.hpp" + +namespace flecs +{ + +inline flecs::entity script_builder::run() const { + ecs_entity_t e = ecs_script_init(world_, &desc_); + return flecs::entity(world_, e); +} + +} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/script/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/script/mixin.inl new file mode 100644 index 000000000..b252ff6a6 --- /dev/null +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/script/mixin.inl @@ -0,0 +1,49 @@ +/** + * @file addons/cpp/mixins/script/mixin.inl + * @brief Script world mixin. + */ + +/** + * @defgroup cpp_addons_script Script + * @ingroup cpp_addons + * Data definition format for loading entity data. + * + * @{ + */ + +/** Run script. + * @see ecs_script_run + */ +int script_run(const char *name, const char *str) const { + return ecs_script_run(world_, name, str); +} + +/** Run script from file. + * @see ecs_script_run_file + */ +int script_run_file(const char *filename) const { + return ecs_script_run_file(world_, filename); +} + +/** Build script. + * @see ecs_script_init + */ +script_builder script(const char *name = nullptr) const { + return script_builder(world_, name); +} + +/** Convert value to string */ +flecs::string to_expr(flecs::entity_t tid, const void* value) { + char *expr = ecs_ptr_to_expr(world_, tid, value); + return flecs::string(expr); +} + +/** Convert value to string */ +template +flecs::string to_expr(const T* value) { + flecs::entity_t tid = _::type::id(world_); + return to_expr(tid, value); +} + + +/** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/snapshot/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/snapshot/decl.hpp deleted file mode 100644 index 559fb2214..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/snapshot/decl.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @file addons/cpp/mixins/snapshot/decl.hpp - * @brief Snapshot module declarations. - */ - -#pragma once - -namespace flecs { - -/** - * @defgroup cpp_addons_snapshots Snapshots - * @brief Save & restore world. - * - * \ingroup cpp_addons - * @{ - */ - -using snapshot_t = ecs_snapshot_t; - -struct snapshot; - -/** @} */ - -} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/snapshot/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/snapshot/impl.hpp deleted file mode 100644 index d914ffc2d..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/snapshot/impl.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file addons/cpp/mixins/snapshot/impl.hpp - * @brief Snapshot module implementation. - */ - -#pragma once - -namespace flecs { - -struct snapshot final { - explicit snapshot(const world& world) - : m_world( world ) - , m_snapshot( nullptr ) { } - - snapshot(const snapshot& obj) - : m_world( obj.m_world ) - { - ecs_iter_t it = ecs_snapshot_iter(obj.m_snapshot); - m_snapshot = ecs_snapshot_take_w_iter(&it); - } - - snapshot(snapshot&& obj) - : m_world(obj.m_world) - , m_snapshot(obj.m_snapshot) - { - obj.m_snapshot = nullptr; - } - - snapshot& operator=(const snapshot& obj) { - ecs_assert(m_world.c_ptr() == obj.m_world.c_ptr(), ECS_INVALID_PARAMETER, NULL); - ecs_iter_t it = ecs_snapshot_iter(obj.m_snapshot); - m_snapshot = ecs_snapshot_take_w_iter(&it); - return *this; - } - - snapshot& operator=(snapshot&& obj) { - ecs_assert(m_world.c_ptr() == obj.m_world.c_ptr(), ECS_INVALID_PARAMETER, NULL); - m_snapshot = obj.m_snapshot; - obj.m_snapshot = nullptr; - return *this; - } - - void take() { - if (m_snapshot) { - ecs_snapshot_free(m_snapshot); - } - - m_snapshot = ecs_snapshot_take(m_world.c_ptr()); - } - - template - void take(const F& f) { - if (m_snapshot) { - ecs_snapshot_free(m_snapshot); - } - - ecs_iter_t it = ecs_filter_iter(m_world, f.c_ptr()); - - m_snapshot = ecs_snapshot_take_w_iter(&it); - } - - void restore() { - if (m_snapshot) { - ecs_snapshot_restore(m_world.c_ptr(), m_snapshot); - m_snapshot = nullptr; - } - } - - ~snapshot() { - if (m_snapshot) { - ecs_snapshot_free(m_snapshot); - } - } - - snapshot_t* c_ptr() const { - return m_snapshot; - } - -private: - const world& m_world; - snapshot_t *m_snapshot; -}; - -// Snapshot mixin implementation -template -inline flecs::snapshot world::snapshot(Args &&... args) const { - return flecs::snapshot(*this, FLECS_FWD(args)...); -} - -} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/snapshot/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/snapshot/mixin.inl deleted file mode 100644 index 81d78c47c..000000000 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/snapshot/mixin.inl +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file addons/cpp/mixins/snapshot/mixin.inl - * @brief Snapshot world mixin. - */ - -/** - * \memberof flecs::world - * \ingroup cpp_addons_snapshot - */ - -/** Create a snapshot. - */ -template -flecs::snapshot snapshot(Args &&... args) const; - -/** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/stats/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/stats/decl.hpp new file mode 100644 index 000000000..2435aa3a6 --- /dev/null +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/stats/decl.hpp @@ -0,0 +1,33 @@ +/** + * @file addons/cpp/mixins/stats/decl.hpp + * @brief Stats module declarations. + */ + +#pragma once + +namespace flecs { + +/** + * @defgroup cpp_addons_stats Stats + * @ingroup cpp_addons + * The stats addon tracks statistics for the world and systems. + * + * @{ + */ + +/** Component that stores world statistics */ +using WorldStats = EcsWorldStats; + +/** Component that stores system/pipeline statistics */ +using PipelineStats = EcsPipelineStats; + +/** Component with world summary stats */ +using WorldSummary = EcsWorldSummary; + +struct stats { + stats(flecs::world& world); +}; + +/** @} */ + +} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/stats/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/stats/impl.hpp new file mode 100644 index 000000000..8b7da3a7d --- /dev/null +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/stats/impl.hpp @@ -0,0 +1,23 @@ +/** + * @file addons/cpp/mixins/stats/impl.hpp + * @brief Monitor module implementation. + */ + +#pragma once + +namespace flecs { + +inline stats::stats(flecs::world& world) { +#ifdef FLECS_UNITS + world.import(); +#endif + + /* Import C module */ + FlecsStatsImport(world); + + world.component(); + world.component(); + world.component(); +} + +} diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/system/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/system/builder.hpp index 981b4e50c..e819cadb7 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/system/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/system/builder.hpp @@ -18,7 +18,7 @@ namespace _ { /** System builder. * - * \ingroup cpp_addons_systems + * @ingroup cpp_addons_systems */ template struct system_builder final : _::system_builder_base { @@ -28,8 +28,8 @@ struct system_builder final : _::system_builder_base { _::sig(world).populate(this); #ifdef FLECS_PIPELINE - ecs_add_id(world, this->m_desc.entity, ecs_dependson(flecs::OnUpdate)); - ecs_add_id(world, this->m_desc.entity, flecs::OnUpdate); + ecs_add_id(world, this->desc_.entity, ecs_dependson(flecs::OnUpdate)); + ecs_add_id(world, this->desc_.entity, flecs::OnUpdate); #endif } }; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/system/builder_i.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/system/builder_i.hpp index 44cf48481..58634cc05 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/system/builder_i.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/system/builder_i.hpp @@ -12,7 +12,7 @@ namespace flecs /** System builder interface. * - * \ingroup cpp_addons_systems + * @ingroup cpp_addons_systems */ template struct system_builder_i : query_builder_i { @@ -22,7 +22,7 @@ struct system_builder_i : query_builder_i { public: system_builder_i(ecs_system_desc_t *desc) : BaseClass(&desc->query) - , m_desc(desc) { } + , desc_(desc) { } /** Specify in which phase the system should run. * @@ -30,25 +30,33 @@ struct system_builder_i : query_builder_i { */ Base& kind(entity_t phase) { flecs::entity_t cur_phase = ecs_get_target( - world_v(), m_desc->entity, EcsDependsOn, 0); + world_v(), desc_->entity, EcsDependsOn, 0); if (cur_phase) { - ecs_remove_id(world_v(), m_desc->entity, ecs_dependson(cur_phase)); - ecs_remove_id(world_v(), m_desc->entity, cur_phase); + ecs_remove_id(world_v(), desc_->entity, ecs_dependson(cur_phase)); + ecs_remove_id(world_v(), desc_->entity, cur_phase); } if (phase) { - ecs_add_id(world_v(), m_desc->entity, ecs_dependson(phase)); - ecs_add_id(world_v(), m_desc->entity, phase); + ecs_add_id(world_v(), desc_->entity, ecs_dependson(phase)); + ecs_add_id(world_v(), desc_->entity, phase); } return *this; } + template ::value> = 0> + Base& kind(E phase) + { + const auto& et = enum_type(this->world_v()); + flecs::entity_t target = et.entity(phase); + return this->kind(target); + } + /** Specify in which phase the system should run. * * @tparam Phase The phase. */ template Base& kind() { - return this->kind(_::cpp_type::id(world_v())); + return this->kind(_::type::id(world_v())); } /** Specify whether system can run on multiple threads. @@ -56,7 +64,7 @@ struct system_builder_i : query_builder_i { * @param value If false system will always run on a single thread. */ Base& multi_threaded(bool value = true) { - m_desc->multi_threaded = value; + desc_->multi_threaded = value; return *this; } @@ -64,8 +72,8 @@ struct system_builder_i : query_builder_i { * * @param value If false system will always run staged. */ - Base& no_readonly(bool value = true) { - m_desc->no_readonly = value; + Base& immediate(bool value = true) { + desc_->immediate = value; return *this; } @@ -77,7 +85,7 @@ struct system_builder_i : query_builder_i { * @param interval The interval value. */ Base& interval(ecs_ftime_t interval) { - m_desc->interval = interval; + desc_->interval = interval; return *this; } @@ -90,8 +98,8 @@ struct system_builder_i : query_builder_i { * @param rate The multiple at which to run the system. */ Base& rate(const entity_t tick_source, int32_t rate) { - m_desc->rate = rate; - m_desc->tick_source = tick_source; + desc_->rate = rate; + desc_->tick_source = tick_source; return *this; } @@ -103,7 +111,18 @@ struct system_builder_i : query_builder_i { * @param rate The multiple at which to run the system. */ Base& rate(int32_t rate) { - m_desc->rate = rate; + desc_->rate = rate; + return *this; + } + + /** Set tick source. + * This operation sets a shared tick source for the system. + * + * @tparam T The type associated with the singleton tick source to use for the system. + */ + template + Base& tick_source() { + desc_->tick_source = _::type::id(world_v()); return *this; } @@ -113,33 +132,31 @@ struct system_builder_i : query_builder_i { * @param tick_source The tick source to use for the system. */ Base& tick_source(flecs::entity_t tick_source) { - m_desc->tick_source = tick_source; + desc_->tick_source = tick_source; return *this; } /** Set system context */ Base& ctx(void *ptr) { - m_desc->ctx = ptr; + desc_->ctx = ptr; return *this; } /** Set system run callback */ Base& run(ecs_iter_action_t action) { - m_desc->run = action; + desc_->run = action; return *this; } protected: - virtual flecs::world_t* world_v() = 0; + virtual flecs::world_t* world_v() override = 0; private: operator Base&() { return *static_cast(this); } - ecs_system_desc_t *m_desc; + ecs_system_desc_t *desc_; }; -/** @} */ - } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/system/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/system/decl.hpp index d689c789e..d8203c687 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/system/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/system/decl.hpp @@ -9,9 +9,9 @@ namespace flecs { /** * @defgroup cpp_addons_systems Systems - * @brief Systems are a query + function that can be ran manually or by a pipeline. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Systems are a query + function that can be ran manually or by a pipeline. + * * @{ */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/system/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/system/impl.hpp index da13f5e3b..26d6c1f04 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/system/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/system/impl.hpp @@ -18,50 +18,47 @@ struct system_runner_fluent { int32_t stage_count, ecs_ftime_t delta_time, void *param) - : m_stage(world) - , m_id(id) - , m_delta_time(delta_time) - , m_param(param) - , m_offset(0) - , m_limit(0) - , m_stage_current(stage_current) - , m_stage_count(stage_count) { } + : stage_(world) + , id_(id) + , delta_time_(delta_time) + , param_(param) + , stage_current_(stage_current) + , stage_count_(stage_count) { } system_runner_fluent& offset(int32_t offset) { - m_offset = offset; + offset_ = offset; return *this; } system_runner_fluent& limit(int32_t limit) { - m_limit = limit; + limit_ = limit; return *this; } system_runner_fluent& stage(flecs::world& stage) { - m_stage = stage.c_ptr(); + stage_ = stage.c_ptr(); return *this; } ~system_runner_fluent() { - if (m_stage_count) { + if (stage_count_) { ecs_run_worker( - m_stage, m_id, m_stage_current, m_stage_count, m_delta_time, - m_param); + stage_, id_, stage_current_, stage_count_, delta_time_, + param_); } else { - ecs_run_w_filter( - m_stage, m_id, m_delta_time, m_offset, m_limit, m_param); + ecs_run(stage_, id_, delta_time_, param_); } } private: - world_t *m_stage; - entity_t m_id; - ecs_ftime_t m_delta_time; - void *m_param; - int32_t m_offset; - int32_t m_limit; - int32_t m_stage_current; - int32_t m_stage_count; + world_t *stage_; + entity_t id_; + ecs_ftime_t delta_time_; + void *param_; + int32_t offset_; + int32_t limit_; + int32_t stage_current_; + int32_t stage_count_; }; struct system final : entity @@ -69,41 +66,32 @@ struct system final : entity using entity::entity; explicit system() { - m_id = 0; - m_world = nullptr; + id_ = 0; + world_ = nullptr; } - explicit system(flecs::world_t *world, ecs_system_desc_t *desc, bool instanced) - { - if (!desc->query.filter.instanced) { - desc->query.filter.instanced = instanced; - } - - m_world = world; - m_id = ecs_system_init(world, desc); - - if (desc->query.filter.terms_buffer) { - ecs_os_free(desc->query.filter.terms_buffer); - } + explicit system(flecs::world_t *world, ecs_system_desc_t *desc) { + world_ = world; + id_ = ecs_system_init(world, desc); } void ctx(void *ctx) { ecs_system_desc_t desc = {}; - desc.entity = m_id; + desc.entity = id_; desc.ctx = ctx; - ecs_system_init(m_world, &desc); + ecs_system_init(world_, &desc); } void* ctx() const { - return ecs_system_get_ctx(m_world, m_id); + return ecs_system_get(world_, id_)->ctx; } flecs::query<> query() const { - return flecs::query<>(m_world, ecs_system_get_query(m_world, m_id)); + return flecs::query<>(ecs_system_get(world_, id_)->query); } system_runner_fluent run(ecs_ftime_t delta_time = 0.0f, void *param = nullptr) const { - return system_runner_fluent(m_world, m_id, 0, 0, delta_time, param); + return system_runner_fluent(world_, id_, 0, 0, delta_time, param); } system_runner_fluent run_worker( @@ -113,7 +101,7 @@ struct system final : entity void *param = nullptr) const { return system_runner_fluent( - m_world, m_id, stage_current, stage_count, delta_time, param); + world_, id_, stage_current, stage_count, delta_time, param); } # ifdef FLECS_TIMER @@ -124,12 +112,12 @@ struct system final : entity // Mixin implementation inline system world::system(flecs::entity e) const { - return flecs::system(m_world, e); + return flecs::system(world_, e); } template inline system_builder world::system(Args &&... args) const { - return flecs::system_builder(m_world, FLECS_FWD(args)...); + return flecs::system_builder(world_, FLECS_FWD(args)...); } namespace _ { diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/system/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/system/mixin.inl index b2b9c0287..1d6d7fcb2 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/system/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/system/mixin.inl @@ -4,8 +4,10 @@ */ /** - * \memberof flecs::world - * \ingroup cpp_addons_system + * @memberof flecs::world + * @ingroup cpp_addons_systems + * + * @{ */ /** Upcast entity to a system. diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/term/builder_i.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/term/builder_i.hpp index 6e1cef3cb..be8a333fd 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/term/builder_i.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/term/builder_i.hpp @@ -15,84 +15,29 @@ namespace flecs * descriptions can reference entities by id, name or by variable, which means * the entity will be resolved when the term is evaluated. * - * \ingroup cpp_core_filters + * @ingroup cpp_core_queries */ template -struct term_id_builder_i { - term_id_builder_i() : m_term_id(nullptr) { } +struct term_ref_builder_i { + term_ref_builder_i() : term_ref_(nullptr) { } - virtual ~term_id_builder_i() { } + virtual ~term_ref_builder_i() { } /* The self flag indicates the term identifier itself is used */ Base& self() { - this->assert_term_id(); - m_term_id->flags |= flecs::Self; - return *this; - } - - /* The up flag indicates that the term identifier may be substituted by - * traversing a relationship upwards. For example: substitute the identifier - * with its parent by traversing the ChildOf relationship. */ - Base& up(flecs::entity_t trav = 0) { - this->assert_term_id(); - m_term_id->flags |= flecs::Up; - if (trav) { - m_term_id->trav = trav; - } - return *this; - } - - template - Base& up() { - return this->up(_::cpp_type::id(this->world_v())); - } - - /* The cascade flag is like up, but returns results in breadth-first order. - * Only supported for flecs::query */ - Base& cascade(flecs::entity_t trav = 0) { - this->assert_term_id(); - m_term_id->flags |= flecs::Cascade; - if (trav) { - m_term_id->trav = trav; - } - return *this; - } - - template - Base& cascade() { - return this->cascade(_::cpp_type::id(this->world_v())); - } - - /* Use with cascade to iterate results in descending (bottom -> top) order */ - Base& desc() { - this->assert_term_id(); - m_term_id->flags |= flecs::Desc; - return *this; - } - - /* The parent flag is short for up(flecs::ChildOf) */ - Base& parent() { - this->assert_term_id(); - m_term_id->flags |= flecs::Parent; - return *this; - } - - /* Specify relationship to traverse, and flags to indicate direction */ - Base& trav(flecs::entity_t trav, flecs::flags32_t flags = 0) { - this->assert_term_id(); - m_term_id->trav = trav; - m_term_id->flags |= flags; + this->assert_term_ref(); + term_ref_->id |= flecs::Self; return *this; } /* Specify value of identifier by id */ Base& id(flecs::entity_t id) { - this->assert_term_id(); - m_term_id->id = id; + this->assert_term_ref(); + term_ref_->id = id; return *this; } - /* Specify value of identifier by id. Amost the same as id(entity), but this + /* Specify value of identifier by id. Almost the same as id(entity), but this * operation explicitly sets the flecs::IsEntity flag. This forces the id to * be interpreted as entity, whereas not setting the flag would implicitly * convert ids for builtin variables such as flecs::This to a variable. @@ -101,46 +46,45 @@ struct term_id_builder_i { * both id(entity_t) and id(const char*). */ Base& entity(flecs::entity_t entity) { - this->assert_term_id(); - m_term_id->flags = flecs::IsEntity; - m_term_id->id = entity; + this->assert_term_ref(); + term_ref_->id = entity | flecs::IsEntity; return *this; } /* Specify value of identifier by name */ Base& name(const char *name) { - this->assert_term_id(); - m_term_id->flags |= flecs::IsEntity; - m_term_id->name = const_cast(name); + this->assert_term_ref(); + term_ref_->id |= flecs::IsEntity; + term_ref_->name = const_cast(name); return *this; } /* Specify identifier is a variable (resolved at query evaluation time) */ Base& var(const char *var_name) { - this->assert_term_id(); - m_term_id->flags |= flecs::IsVariable; - m_term_id->name = const_cast(var_name); + this->assert_term_ref(); + term_ref_->id |= flecs::IsVariable; + term_ref_->name = const_cast(var_name); return *this; } /* Override term id flags */ Base& flags(flecs::flags32_t flags) { - this->assert_term_id(); - m_term_id->flags = flags; + this->assert_term_ref(); + term_ref_->id = flags; return *this; } - ecs_term_id_t *m_term_id; - + ecs_term_ref_t *term_ref_; + protected: virtual flecs::world_t* world_v() = 0; -private: - void assert_term_id() { - ecs_assert(m_term_id != NULL, ECS_INVALID_PARAMETER, - "no active term (call .term() first)"); + void assert_term_ref() { + ecs_assert(term_ref_ != NULL, ECS_INVALID_PARAMETER, + "no active term (call .with() first)"); } +private: operator Base&() { return *static_cast(this); } @@ -149,11 +93,11 @@ struct term_id_builder_i { /** Term builder interface. * A term is a single element of a query expression. * - * \ingroup cpp_addons_filter + * @ingroup cpp_core_queries */ template -struct term_builder_i : term_id_builder_i { - term_builder_i() : m_term(nullptr) { } +struct term_builder_i : term_ref_builder_i { + term_builder_i() : term_(nullptr) { } term_builder_i(ecs_term_t *term_ptr) { set_term(term_ptr); @@ -166,7 +110,7 @@ struct term_builder_i : term_id_builder_i { /* Call prior to setting values for src identifier */ Base& src() { this->assert_term(); - this->m_term_id = &m_term->src; + this->term_ref_ = &term_->src; return *this; } @@ -175,7 +119,7 @@ struct term_builder_i : term_id_builder_i { * populated as well). */ Base& first() { this->assert_term(); - this->m_term_id = &m_term->first; + this->term_ref_ = &term_->first; return *this; } @@ -183,7 +127,7 @@ struct term_builder_i : term_id_builder_i { * element of a pair. Requires that first() is populated as well. */ Base& second() { this->assert_term(); - this->m_term_id = &m_term->second; + this->term_ref_ = &term_->second; return *this; } @@ -197,7 +141,7 @@ struct term_builder_i : term_id_builder_i { /* Select src identifier, initialize it with id associated with type */ template Base& src() { - this->src(_::cpp_type::id(this->world_v())); + this->src(_::type::id(this->world_v())); return *this; } @@ -224,7 +168,7 @@ struct term_builder_i : term_id_builder_i { /* Select first identifier, initialize it with id associated with type */ template Base& first() { - this->first(_::cpp_type::id(this->world_v())); + this->first(_::type::id(this->world_v())); return *this; } @@ -251,7 +195,7 @@ struct term_builder_i : term_id_builder_i { /* Select second identifier, initialize it with id associated with type */ template Base& second() { - this->second(_::cpp_type::id(this->world_v())); + this->second(_::type::id(this->world_v())); return *this; } @@ -268,17 +212,76 @@ struct term_builder_i : term_id_builder_i { return *this; } - /** Set role of term. */ - Base& role(id_t role) { + /* The up flag indicates that the term identifier may be substituted by + * traversing a relationship upwards. For example: substitute the identifier + * with its parent by traversing the ChildOf relationship. */ + Base& up(flecs::entity_t trav = 0) { + this->assert_term_ref(); + ecs_check(this->term_ref_ != &term_->first, ECS_INVALID_PARAMETER, + "up traversal can only be applied to term source"); + ecs_check(this->term_ref_ != &term_->second, ECS_INVALID_PARAMETER, + "up traversal can only be applied to term source"); + this->term_ref_->id |= flecs::Up; + if (trav) { + term_->trav = trav; + } + error: + return *this; + } + + template + Base& up() { + return this->up(_::type::id(this->world_v())); + } + + /* The cascade flag is like up, but returns results in breadth-first order. + * Only supported for flecs::query */ + Base& cascade(flecs::entity_t trav = 0) { + this->assert_term_ref(); + this->up(); + this->term_ref_->id |= flecs::Cascade; + if (trav) { + term_->trav = trav; + } + return *this; + } + + template + Base& cascade() { + return this->cascade(_::type::id(this->world_v())); + } + + /* Use with cascade to iterate results in descending (bottom -> top) order */ + Base& desc() { + this->assert_term_ref(); + this->term_ref_->id |= flecs::Desc; + return *this; + } + + /* Same as up(), exists for backwards compatibility */ + Base& parent() { + return this->up(); + } + + /* Specify relationship to traverse, and flags to indicate direction */ + Base& trav(flecs::entity_t trav, flecs::flags32_t flags = 0) { + this->assert_term_ref(); + term_->trav = trav; + this->term_ref_->id |= flags; + return *this; + } + + /** Set id flags for term. */ + Base& id_flags(id_t flags) { this->assert_term(); - m_term->id_flags = role; + term_->id |= flags; return *this; } /** Set read/write access of term. */ Base& inout(flecs::inout_kind_t inout) { this->assert_term(); - m_term->inout = static_cast(inout); + term_->inout = static_cast(inout); return *this; } @@ -292,8 +295,8 @@ struct term_builder_i : term_id_builder_i { */ Base& inout_stage(flecs::inout_kind_t inout) { this->assert_term(); - m_term->inout = static_cast(inout); - if (m_term->oper != EcsNot) { + term_->inout = static_cast(inout); + if (term_->oper != EcsNot) { this->src().entity(0); } return *this; @@ -314,7 +317,7 @@ struct term_builder_i : term_id_builder_i { } /** Short for inout_stage(flecs::InOut). - * Use when system uses get_mut. + * Use when system uses ensure. */ Base& read_write() { return this->inout_stage(flecs::InOut); @@ -343,7 +346,7 @@ struct term_builder_i : term_id_builder_i { /** Set operator of term. */ Base& oper(flecs::oper_kind_t oper) { this->assert_term(); - m_term->oper = static_cast(oper); + term_->oper = static_cast(oper); return *this; } @@ -385,48 +388,48 @@ struct term_builder_i : term_id_builder_i { /** Match singleton. */ Base& singleton() { this->assert_term(); - ecs_assert(m_term->id || m_term->first.id, ECS_INVALID_PARAMETER, + ecs_assert(term_->id || term_->first.id, ECS_INVALID_PARAMETER, "no component specified for singleton"); - flecs::id_t sid = m_term->id; + flecs::id_t sid = term_->id; if (!sid) { - sid = m_term->first.id; + sid = term_->first.id; } ecs_assert(sid != 0, ECS_INVALID_PARAMETER, NULL); if (!ECS_IS_PAIR(sid)) { - m_term->src.id = sid; + term_->src.id = sid; } else { - m_term->src.id = ecs_pair_first(world(), sid); + term_->src.id = ecs_pair_first(world(), sid); } return *this; } - /* Filter terms are not triggered on by observers */ + /* Query terms are not triggered on by observers */ Base& filter() { - m_term->src.flags |= flecs::Filter; + term_->inout = EcsInOutFilter; return *this; } - ecs_term_t *m_term; + ecs_term_t *term_; protected: - virtual flecs::world_t* world_v() = 0; + virtual flecs::world_t* world_v() override = 0; void set_term(ecs_term_t *term) { - m_term = term; + term_ = term; if (term) { - this->m_term_id = &m_term->src; // default to subject + this->term_ref_ = &term_->src; // default to subject } else { - this->m_term_id = nullptr; + this->term_ref_ = nullptr; } } private: void assert_term() { - ecs_assert(m_term != NULL, ECS_INVALID_PARAMETER, - "no active term (call .term() first)"); + ecs_assert(term_ != NULL, ECS_INVALID_PARAMETER, + "no active term (call .with() first)"); } operator Base&() { diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/term/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/term/decl.hpp index 6c12de657..b5a1bdbd5 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/term/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/term/decl.hpp @@ -8,7 +8,9 @@ namespace flecs { /** - * \ingroup cpp_core_filters + * @ingroup cpp_core_queries + * + * @{ */ struct term; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/term/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/term/impl.hpp index 49fddf54d..347fb6f38 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/term/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/term/impl.hpp @@ -11,119 +11,76 @@ namespace flecs { /** Class that describes a term. * - * \ingroup cpp_core_filters + * @ingroup cpp_core_queries */ struct term final : term_builder_i { term() : term_builder_i(&value) , value({}) - , m_world(nullptr) { value.move = true; } + , world_(nullptr) { } term(flecs::world_t *world_ptr) : term_builder_i(&value) , value({}) - , m_world(world_ptr) { value.move = true; } + , world_(world_ptr) { } term(flecs::world_t *world_ptr, ecs_term_t t) : term_builder_i(&value) , value({}) - , m_world(world_ptr) { + , world_(world_ptr) { value = t; - value.move = false; this->set_term(&value); } term(flecs::world_t *world_ptr, id_t id) : term_builder_i(&value) , value({}) - , m_world(world_ptr) { + , world_(world_ptr) { if (id & ECS_ID_FLAGS_MASK) { value.id = id; } else { value.first.id = id; } - value.move = false; this->set_term(&value); } term(flecs::world_t *world_ptr, entity_t r, entity_t o) : term_builder_i(&value) , value({}) - , m_world(world_ptr) { + , world_(world_ptr) { value.id = ecs_pair(r, o); - value.move = false; this->set_term(&value); } term(id_t id) : term_builder_i(&value) , value({}) - , m_world(nullptr) { + , world_(nullptr) { if (id & ECS_ID_FLAGS_MASK) { value.id = id; } else { value.first.id = id; } - value.move = true; } term(id_t r, id_t o) : term_builder_i(&value) , value({}) - , m_world(nullptr) { + , world_(nullptr) { value.id = ecs_pair(r, o); - value.move = true; } - term(const term& t) : term_builder_i(&value) { - m_world = t.m_world; - value = ecs_term_copy(&t.value); - this->set_term(&value); - } - - term(term&& t) : term_builder_i(&value) { - m_world = t.m_world; - value = ecs_term_move(&t.value); - t.reset(); - this->set_term(&value); - } - - term& operator=(const term& t) { - ecs_assert(m_world == t.m_world, ECS_INVALID_PARAMETER, NULL); - ecs_term_fini(&value); - value = ecs_term_copy(&t.value); - this->set_term(&value); - return *this; - } - - term& operator=(term&& t) { - ecs_assert(m_world == t.m_world, ECS_INVALID_PARAMETER, NULL); - ecs_term_fini(&value); - value = t.value; - this->set_term(&value); - t.reset(); - return *this; - } - - ~term() { - ecs_term_fini(&value); - } - void reset() { value = {}; this->set_term(nullptr); } - int finalize() { - return ecs_term_finalize(m_world, &value); - } - bool is_set() { return ecs_term_is_initialized(&value); } flecs::id id() { - return flecs::id(m_world, value.id); + return flecs::id(world_, value.id); } flecs::inout_kind_t inout() { @@ -135,46 +92,46 @@ struct term final : term_builder_i { } flecs::entity get_src() { - return flecs::entity(m_world, value.src.id); + return flecs::entity(world_, ECS_TERM_REF_ID(&value.src)); } flecs::entity get_first() { - return flecs::entity(m_world, value.first.id); + return flecs::entity(world_, ECS_TERM_REF_ID(&value.first)); } flecs::entity get_second() { - return flecs::entity(m_world, value.second.id); + return flecs::entity(world_, ECS_TERM_REF_ID(&value.second)); } - ecs_term_t move() { /* explicit move to ecs_term_t */ - return ecs_term_move(&value); + operator flecs::term_t() const { + return value; } - ecs_term_t value; + flecs::term_t value; protected: - flecs::world_t* world_v() override { return m_world; } + flecs::world_t* world_v() override { return world_; } private: - flecs::world_t *m_world; + flecs::world_t *world_; }; // Term mixin implementation template inline flecs::term world::term(Args &&... args) const { - return flecs::term(m_world, FLECS_FWD(args)...); + return flecs::term(world_, FLECS_FWD(args)...); } template inline flecs::term world::term() const { - return flecs::term(m_world, _::cpp_type::id(m_world)); + return flecs::term(world_, _::type::id(world_)); } template inline flecs::term world::term() const { - return flecs::term(m_world, ecs_pair( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world))); + return flecs::term(world_, ecs_pair( + _::type::id(world_), + _::type::id(world_))); } } diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/term/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/term/mixin.inl index 4291b3899..2c34e9984 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/term/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/term/mixin.inl @@ -4,8 +4,10 @@ */ /** - * \memberof flecs::world - * \ingroup cpp_core_filters + * @memberof flecs::world + * @ingroup cpp_core_queries + * + * @{ */ /** Create a term. diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/timer/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/timer/decl.hpp index e7a0c9916..ccdf87990 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/timer/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/timer/decl.hpp @@ -9,9 +9,9 @@ namespace flecs { /** * @defgroup cpp_addons_timer Timer - * @brief Run systems at a time interval. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Run systems at a time interval. + * * @{ */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/timer/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/timer/impl.hpp index 057979719..e26c19db8 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/timer/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/timer/impl.hpp @@ -12,76 +12,86 @@ struct timer final : entity { using entity::entity; timer& interval(ecs_ftime_t interval) { - ecs_set_interval(m_world, m_id, interval); + ecs_set_interval(world_, id_, interval); return *this; } ecs_ftime_t interval() { - return ecs_get_interval(m_world, m_id); + return ecs_get_interval(world_, id_); } timer& timeout(ecs_ftime_t timeout) { - ecs_set_timeout(m_world, m_id, timeout); + ecs_set_timeout(world_, id_, timeout); return *this; } ecs_ftime_t timeout() { - return ecs_get_timeout(m_world, m_id); + return ecs_get_timeout(world_, id_); } timer& rate(int32_t rate, flecs::entity_t tick_source = 0) { - ecs_set_rate(m_world, m_id, rate, tick_source); + ecs_set_rate(world_, id_, rate, tick_source); return *this; } void start() { - ecs_start_timer(m_world, m_id); + ecs_start_timer(world_, id_); } void stop() { - ecs_stop_timer(m_world, m_id); + ecs_stop_timer(world_, id_); } }; +template +inline flecs::timer world::timer() const { + return flecs::timer(world_, _::type::id(world_)); +} + template inline flecs::timer world::timer(Args &&... args) const { - return flecs::timer(m_world, FLECS_FWD(args)...); + return flecs::timer(world_, FLECS_FWD(args)...); } inline void world::randomize_timers() const { - ecs_randomize_timers(m_world); + ecs_randomize_timers(world_); } inline void system::interval(ecs_ftime_t interval) { - ecs_set_interval(m_world, m_id, interval); + ecs_set_interval(world_, id_, interval); } inline ecs_ftime_t system::interval() { - return ecs_get_interval(m_world, m_id); + return ecs_get_interval(world_, id_); } inline void system::timeout(ecs_ftime_t timeout) { - ecs_set_timeout(m_world, m_id, timeout); + ecs_set_timeout(world_, id_, timeout); } inline ecs_ftime_t system::timeout() { - return ecs_get_timeout(m_world, m_id); + return ecs_get_timeout(world_, id_); } inline void system::rate(int32_t rate) { - ecs_set_rate(m_world, m_id, rate, 0); + ecs_set_rate(world_, id_, rate, 0); } inline void system::start() { - ecs_start_timer(m_world, m_id); + ecs_start_timer(world_, id_); } inline void system::stop() { - ecs_stop_timer(m_world, m_id); + ecs_stop_timer(world_, id_); +} + +template +inline void system::set_tick_source() { + ecs_set_tick_source(world_, id_, _::type::id(world_)); } inline void system::set_tick_source(flecs::entity e) { - ecs_set_tick_source(m_world, m_id, e); + ecs_set_tick_source(world_, id_, e); } namespace _ { diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/timer/mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/timer/mixin.inl index 46dd6091d..6c780e2aa 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/timer/mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/timer/mixin.inl @@ -4,10 +4,14 @@ */ /** - * \memberof flecs::world - * \ingroup cpp_addons_timer + * @memberof flecs::world + * @ingroup cpp_addons_timer */ +/** Find or register a singleton timer. */ +template +flecs::timer timer() const; + /** Find or register a timer. */ template flecs::timer timer(Args &&... args) const; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/timer/system_mixin.inl b/vendors/flecs/include/flecs/addons/cpp/mixins/timer/system_mixin.inl index d2ae996dc..2457bcec3 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/timer/system_mixin.inl +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/timer/system_mixin.inl @@ -4,8 +4,10 @@ */ /** - * \memberof flecs::system - * \ingroup cpp_addons_timer + * @memberof flecs::system + * @ingroup cpp_addons_timer + * + * @{ */ /** Set interval. @@ -43,6 +45,12 @@ void start(); */ void stop(); +/** Set external tick source. + * @see ecs_set_tick_source + */ +template +void set_tick_source(); + /** Set external tick source. * @see ecs_set_tick_source */ diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/units/decl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/units/decl.hpp index 6f62ab225..05592bcfd 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/units/decl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/units/decl.hpp @@ -10,9 +10,9 @@ struct units { /** * @defgroup cpp_addons_units Units - * @brief Common unit annotations for reflection framework. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * Common unit annotations for reflection framework. + * * @{ */ @@ -20,9 +20,9 @@ struct Prefixes { }; /** * @defgroup cpp_addons_units_prefixes Prefixes - * @brief Prefixes to indicate unit count (e.g. Kilo, Mega) - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units + * Prefixes to indicate unit count (e.g. Kilo, Mega) + * * @{ */ @@ -59,9 +59,9 @@ struct Yobi { }; /** * @defgroup cpp_addons_units_quantities Quantities - * @brief Quantities that group units (e.g. Length) - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units + * Quantities that group units (e.g. Length) + * * @{ */ @@ -81,14 +81,14 @@ struct DataRate { }; struct Angle { }; struct Frequency { }; struct Uri { }; +struct Color { }; /** @} */ struct duration { /** * @defgroup cpp_addons_units_duration Duration - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -107,8 +107,7 @@ struct Days { }; struct angle { /** * @defgroup cpp_addons_units_angle Angle - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -122,8 +121,7 @@ struct Degrees { }; struct time { /** * @defgroup cpp_addons_units_time Time - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -136,8 +134,7 @@ struct Date { }; struct mass { /** * @defgroup cpp_addons_units_mass Mass - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -151,8 +148,7 @@ struct KiloGrams { }; struct electric_current { /** * @defgroup cpp_addons_units_electric_current Electric Current - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -165,8 +161,7 @@ struct Ampere { }; struct amount { /** * @defgroup cpp_addons_units_amount Amount - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -179,8 +174,7 @@ struct Mole { }; struct luminous_intensity { /** * @defgroup cpp_addons_units_luminous_intensity Luminous Intensity - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -193,8 +187,7 @@ struct Candela { }; struct force { /** * @defgroup cpp_addons_units_force Force - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -207,8 +200,7 @@ struct Newton { }; struct length { /** * @defgroup cpp_addons_units_length Length - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -229,8 +221,7 @@ struct Pixels { }; struct pressure { /** * @defgroup cpp_addons_units_pressure Pressure - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -244,8 +235,7 @@ struct Bar { }; struct speed { /** * @defgroup cpp_addons_units_speed Speed - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -261,8 +251,7 @@ struct MilesPerHour { }; struct temperature { /** * @defgroup cpp_addons_units_temperature Temperature - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -277,8 +266,7 @@ struct Fahrenheit { }; struct data { /** * @defgroup cpp_addons_units_data Data - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -300,8 +288,7 @@ struct GibiBytes { }; struct datarate { /** * @defgroup cpp_addons_units_datarate Data Rate - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -321,8 +308,7 @@ struct GigaBytesPerSecond { }; struct frequency { /** * @defgroup cpp_addons_units_frequency Frequency - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -338,8 +324,7 @@ struct GigaHertz { }; struct uri { /** * @defgroup cpp_addons_units_uri Uri - * - * \ingroup cpp_addons_units + * @ingroup cpp_addons_units * @{ */ @@ -351,6 +336,20 @@ struct File { }; }; +struct color { +/** + * @defgroup cpp_addons_units_color Color + * @ingroup cpp_addons_units + * @{ + */ + +struct Rgb { }; +struct Hsl { }; +struct Css { }; + +/** @} */ +}; + struct Percentage { }; struct Bel { }; struct DeciBel { }; diff --git a/vendors/flecs/include/flecs/addons/cpp/mixins/units/impl.hpp b/vendors/flecs/include/flecs/addons/cpp/mixins/units/impl.hpp index 6eb3dc62d..596e8b781 100644 --- a/vendors/flecs/include/flecs/addons/cpp/mixins/units/impl.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/mixins/units/impl.hpp @@ -64,6 +64,7 @@ inline units::units(flecs::world& world) { world.entity("::flecs::units::Angle"); world.entity("::flecs::units::Frequency"); world.entity("::flecs::units::Uri"); + world.entity("::flecs::units::Color"); // Initialize duration units world.entity( @@ -203,6 +204,11 @@ inline units::units(flecs::world& world) { world.entity( "::flecs::units::Angle::Degrees"); + // Initialize color + world.entity("::flecs::units::Color::Rgb"); + world.entity("::flecs::units::Color::Hsl"); + world.entity("::flecs::units::Color::Css"); + // Initialize percentage world.entity("::flecs::units::Percentage"); diff --git a/vendors/flecs/include/flecs/addons/cpp/pair.hpp b/vendors/flecs/include/flecs/addons/cpp/pair.hpp index afd4d5b0d..67259c8e8 100644 --- a/vendors/flecs/include/flecs/addons/cpp/pair.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/pair.hpp @@ -8,15 +8,15 @@ namespace flecs { namespace _ { - struct pair_base { }; + struct pair_base { }; } // _ /** * @defgroup cpp_pair_type Pair type - * @brief Compile time utilities for working with relationship pairs. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Compile time utilities for working with relationship pairs. + * * @{ */ @@ -24,16 +24,16 @@ namespace _ { * The pair type can be used to represent a pair at compile time, and is able * to automatically derive the storage type associated with the pair, accessible * through pair::type. - * + * * The storage type is derived using the following rules: * - if pair::first is non-empty, the storage type is pair::first * - if pair::first is empty and pair::second is non-empty, the storage type is pair::second - * + * * The pair type can hold a temporary value so that it can be used in the * signatures of queries */ template -struct pair : _::pair_base { +struct pair : _::pair_base { using type = conditional_t::value || is_empty::value, First, Second>; using first = First; using second = Second; @@ -43,13 +43,13 @@ struct pair : _::pair_base { // This allows the class to be used as a temporary object pair(const type& v) : ref_(const_cast(v)) { } - operator type&() { + operator type&() { return ref_; } - operator const type&() const { + operator const type&() const { return ref_; - } + } type* operator->() { return &ref_; @@ -66,7 +66,7 @@ struct pair : _::pair_base { const type& operator*() const { return ref_; } - + private: type& ref_; }; @@ -116,7 +116,7 @@ using actual_type_t = typename actual_type::type; // Get type without const, *, & template struct base_type { - using type = decay_t< remove_pointer_t< actual_type_t > >; + using type = decay_t< actual_type_t >; }; template @@ -136,7 +136,7 @@ using base_arg_type_t = typename base_arg_type::type; // Test if type is the same as its actual type template struct is_actual { - static constexpr bool value = + static constexpr bool value = std::is_same >::value && !is_enum::value; }; diff --git a/vendors/flecs/include/flecs/addons/cpp/ref.hpp b/vendors/flecs/include/flecs/addons/cpp/ref.hpp index 76daf6735..00eddf7d1 100644 --- a/vendors/flecs/include/flecs/addons/cpp/ref.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/ref.hpp @@ -10,9 +10,9 @@ namespace flecs /** * @defgroup cpp_ref Refs - * @brief Refs are a fast mechanism for referring to a specific entity/component. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Refs are a fast mechanism for referring to a specific entity/component. + * * @{ */ @@ -21,51 +21,65 @@ namespace flecs */ template struct ref { - ref() : m_world(nullptr), m_ref{} { } + ref() : world_(nullptr), ref_{} { } ref(world_t *world, entity_t entity, flecs::id_t id = 0) - : m_ref() + : ref_() { // the world we were called with may be a stage; convert it to a world // here if that is the case - m_world = world ? const_cast(ecs_get_world(world)) + world_ = world ? const_cast(ecs_get_world(world)) : nullptr; if (!id) { - id = _::cpp_type::id(world); + id = _::type::id(world); } - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); - m_ref = ecs_ref_init_id(m_world, entity, id); + ref_ = ecs_ref_init_id(world_, entity, id); } + ref(flecs::entity entity, flecs::id_t id = 0) + : ref(entity.world(), entity.id(), id) { } + T* operator->() { T* result = static_cast(ecs_ref_get_id( - m_world, &m_ref, this->m_ref.id)); + world_, &ref_, this->ref_.id)); - ecs_assert(result != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(result != NULL, ECS_INVALID_PARAMETER, + "nullptr dereference by flecs::ref"); return result; } T* get() { return static_cast(ecs_ref_get_id( - m_world, &m_ref, this->m_ref.id)); + world_, &ref_, this->ref_.id)); } T* try_get() { - if (!m_world || !m_ref.entity) { + if (!world_ || !ref_.entity) { return nullptr; } return get(); } + bool has() { + return !!try_get(); + } + + /** implicit conversion to bool. return true if there is a valid T* being referred to **/ + operator bool() { + return has(); + } + flecs::entity entity() const; private: - world_t *m_world; - flecs::ref_t m_ref; + world_t *world_; + flecs::ref_t ref_; }; /** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/table.hpp b/vendors/flecs/include/flecs/addons/cpp/table.hpp index c967c0495..0b3d81c0a 100644 --- a/vendors/flecs/include/flecs/addons/cpp/table.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/table.hpp @@ -9,56 +9,56 @@ namespace flecs { /** * @defgroup cpp_tables Tables - * @brief Table operations. - * - * \ingroup cpp_core + * @ingroup cpp_core + * Table operations. + * * @{ */ struct table { - table() : m_world(nullptr), m_table(nullptr) { } + table() : world_(nullptr), table_(nullptr) { } table(world_t *world, table_t *t) - : m_world(world) - , m_table(t) { } + : world_(world) + , table_(t) { } virtual ~table() { } /** Convert table type to string. */ flecs::string str() const { - return flecs::string(ecs_table_str(m_world, m_table)); + return flecs::string(ecs_table_str(world_, table_)); } /** Get table type. */ flecs::type type() const { - return flecs::type(m_world, ecs_table_get_type(m_table)); + return flecs::type(world_, ecs_table_get_type(table_)); } /** Get table count. */ int32_t count() const { - return ecs_table_count(m_table); + return ecs_table_count(table_); } - /** Find type index for (component) id. - * + /** Find type index for (component) id. + * * @param id The (component) id. * @return The index of the id in the table type, -1 if not found/ */ int32_t type_index(flecs::id_t id) const { - return ecs_table_get_type_index(m_world, m_table, id); + return ecs_table_get_type_index(world_, table_, id); } - /** Find type index for type. - * + /** Find type index for type. + * * @tparam T The type. * @return True if the table has the type, false if not. */ template int32_t type_index() const { - return type_index(_::cpp_type::id(m_world)); + return type_index(_::type::id(world_)); } - /** Find type index for pair. + /** Find type index for pair. * @param first First element of pair. * @param second Second element of pair. * @return True if the table has the pair, false if not. @@ -67,46 +67,46 @@ struct table { return type_index(ecs_pair(first, second)); } - /** Find type index for pair. + /** Find type index for pair. * @tparam First First element of pair. * @param second Second element of pair. * @return True if the table has the pair, false if not. */ template int32_t type_index(flecs::entity_t second) const { - return type_index(_::cpp_type::id(m_world), second); + return type_index(_::type::id(world_), second); } - /** Find type index for pair. + /** Find type index for pair. * @tparam First First element of pair. * @tparam Second Second element of pair. * @return True if the table has the pair, false if not. */ template int32_t type_index() const { - return type_index(_::cpp_type::id(m_world)); + return type_index(_::type::id(world_)); } - /** Find column index for (component) id. - * + /** Find column index for (component) id. + * * @param id The (component) id. * @return The index of the id in the table type, -1 if not found/ */ int32_t column_index(flecs::id_t id) const { - return ecs_table_get_column_index(m_world, m_table, id); + return ecs_table_get_column_index(world_, table_, id); } - /** Find column index for type. - * + /** Find column index for type. + * * @tparam T The type. * @return True if the table has the type, false if not. */ template int32_t column_index() const { - return column_index(_::cpp_type::id(m_world)); + return column_index(_::type::id(world_)); } - /** Find column index for pair. + /** Find column index for pair. * @param first First element of pair. * @param second Second element of pair. * @return True if the table has the pair, false if not. @@ -115,28 +115,28 @@ struct table { return column_index(ecs_pair(first, second)); } - /** Find column index for pair. + /** Find column index for pair. * @tparam First First element of pair. * @param second Second element of pair. * @return True if the table has the pair, false if not. */ template int32_t column_index(flecs::entity_t second) const { - return column_index(_::cpp_type::id(m_world), second); + return column_index(_::type::id(world_), second); } - /** Find column index for pair. + /** Find column index for pair. * @tparam First First element of pair. * @tparam Second Second element of pair. * @return True if the table has the pair, false if not. */ template int32_t column_index() const { - return column_index(_::cpp_type::id(m_world)); + return column_index(_::type::id(world_)); } - /** Test if table has (component) id. - * + /** Test if table has (component) id. + * * @param id The (component) id. * @return True if the table has the id, false if not. */ @@ -144,8 +144,8 @@ struct table { return type_index(id) != -1; } - /** Test if table has the type. - * + /** Test if table has the type. + * * @tparam T The type. * @return True if the table has the type, false if not. */ @@ -155,7 +155,7 @@ struct table { } /** Test if table has the pair. - * + * * @param first First element of pair. * @param second Second element of pair. * @return True if the table has the pair, false if not. @@ -165,7 +165,7 @@ struct table { } /** Test if table has the pair. - * + * * @tparam First First element of pair. * @param second Second element of pair. * @return True if the table has the pair, false if not. @@ -176,7 +176,7 @@ struct table { } /** Test if table has the pair. - * + * * @tparam First First element of pair. * @tparam Second Second element of pair. * @return True if the table has the pair, false if not. @@ -186,17 +186,17 @@ struct table { return type_index() != -1; } - /** Get pointer to component array by column index. - * + /** Get pointer to component array by column index. + * * @param index The column index. * @return Pointer to the column, NULL if not a component. */ virtual void* get_column(int32_t index) const { - return ecs_table_get_column(m_table, index, 0); + return ecs_table_get_column(table_, index, 0); } /** Get pointer to component array by component. - * + * * @param id The component id. * @return Pointer to the column, NULL if not found. */ @@ -209,7 +209,7 @@ struct table { } /** Get pointer to component array by pair. - * + * * @param first The first element of the pair. * @param second The second element of the pair. * @return Pointer to the column, NULL if not found. @@ -219,71 +219,71 @@ struct table { } /** Get pointer to component array by component. - * + * * @tparam T The component. * @return Pointer to the column, NULL if not found. */ template ::value > = 0> T* get() const { - return static_cast(get(_::cpp_type::id(m_world))); + return static_cast(get(_::type::id(world_))); } /** Get pointer to component array by (enum) component. - * + * * @tparam T The (enum) component. * @return Pointer to the column, NULL if not found. */ template ::value > = 0> T* get() const { - return static_cast(get(_::cpp_type::id(m_world))); + return static_cast(get(_::type::id(world_))); } /** Get pointer to component array by component. - * + * * @tparam T The component. * @return Pointer to the column, NULL if not found. */ template , if_t< flecs::is_pair::value > = 0> A* get() const { - return static_cast(get(_::cpp_type::id(m_world))); + return static_cast(get(_::type::id(world_))); } /** Get pointer to component array by pair. - * + * * @tparam First The first element of the pair. * @param second The second element of the pair. * @return Pointer to the column, NULL if not found. */ template First* get(flecs::entity_t second) const { - return static_cast(get(_::cpp_type::id(m_world), second)); + return static_cast(get(_::type::id(world_), second)); } /** Get pointer to component array by pair. - * + * * @tparam First The first element of the pair. * @tparam Second The second element of the pair. * @return Pointer to the column, NULL if not found. */ - template , + template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value> = 0> A* get() const { - return static_cast(get(_::cpp_type::id(m_world))); + return static_cast(get(_::type::id(world_))); } /** Get column size */ size_t column_size(int32_t index) { - return ecs_table_get_column_size(m_table, index); + return ecs_table_get_column_size(table_, index); } /** Get depth for given relationship. * * @param rel The relationship. * @return The depth. - */ + */ int32_t depth(flecs::entity_t rel) { - return ecs_table_get_depth(m_world, m_table, rel); + return ecs_table_get_depth(world_, table_, rel); } /** Get depth for given relationship. @@ -293,50 +293,58 @@ struct table { */ template int32_t depth() { - return depth(_::cpp_type::id(m_world)); + return depth(_::type::id(world_)); + } + + /** Get table. + * + * @return The table. + */ + table_t* get_table() const { + return table_; } /* Implicit conversion to table_t */ operator table_t*() const { - return m_table; + return table_; } protected: - world_t *m_world; - table_t *m_table; + world_t *world_; + table_t *table_; }; struct table_range : table { - table_range() + table_range() : table() - , m_offset(0) - , m_count(0) { } + , offset_(0) + , count_(0) { } table_range(world_t *world, table_t *t, int32_t offset, int32_t count) : table(world, t) - , m_offset(offset) - , m_count(count) { } + , offset_(offset) + , count_(count) { } int32_t offset() const { - return m_offset; + return offset_; } int32_t count() const { - return m_count; + return count_; } - /** Get pointer to component array by column index. - * + /** Get pointer to component array by column index. + * * @param index The column index. * @return Pointer to the column, NULL if not a component. */ void* get_column(int32_t index) const override { - return ecs_table_get_column(m_table, index, m_offset); + return ecs_table_get_column(table_, index, offset_); } private: - int32_t m_offset = 0; - int32_t m_count = 0; + int32_t offset_ = 0; + int32_t count_ = 0; }; /** @} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/type.hpp b/vendors/flecs/include/flecs/addons/cpp/type.hpp index 07610ebb2..8a903fc4c 100644 --- a/vendors/flecs/include/flecs/addons/cpp/type.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/type.hpp @@ -9,9 +9,9 @@ namespace flecs { /** * @defgroup cpp_types Types + * @ingroup cpp_core * @brief Type operations. - * - * \ingroup cpp_core + * * @{ */ @@ -19,58 +19,58 @@ namespace flecs { * A type is a vector of component ids which can be requested from entities or tables. */ struct type { - type() : m_world(nullptr), m_type(nullptr) { } + type() : world_(nullptr), type_(nullptr) { } type(world_t *world, const type_t *t) - : m_world(world) - , m_type(t) { } + : world_(world) + , type_(t) { } /** Convert type to comma-separated string */ flecs::string str() const { - return flecs::string(ecs_type_str(m_world, m_type)); + return flecs::string(ecs_type_str(world_, type_)); } /** Return number of ids in type */ int32_t count() const { - if (!m_type) { + if (!type_) { return 0; } - return m_type->count; + return type_->count; } /** Return pointer to array. */ flecs::id_t* array() const { - if (!m_type) { + if (!type_) { return nullptr; } - return m_type->array; + return type_->array; } /** Get id at specified index in type */ flecs::id get(int32_t index) const { - ecs_assert(m_type != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(m_type->count > index, ECS_OUT_OF_RANGE, NULL); - if (!m_type) { + ecs_assert(type_ != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(type_->count > index, ECS_OUT_OF_RANGE, NULL); + if (!type_) { return flecs::id(); } - return flecs::id(m_world, m_type->array[index]); + return flecs::id(world_, type_->array[index]); } - + flecs::id_t* begin() const { - return m_type->array; + return type_->array; } flecs::id_t* end() const { - return &m_type->array[m_type->count]; + return &type_->array[type_->count]; } /** Implicit conversion to type_t */ operator const type_t*() const { - return m_type; + return type_; } private: - world_t *m_world; - const type_t *m_type; + world_t *world_; + const type_t *type_; }; /** #} */ diff --git a/vendors/flecs/include/flecs/addons/cpp/utils/array.hpp b/vendors/flecs/include/flecs/addons/cpp/utils/array.hpp index 57abd2c17..b90579788 100644 --- a/vendors/flecs/include/flecs/addons/cpp/utils/array.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/utils/array.hpp @@ -12,29 +12,29 @@ template struct array_iterator { explicit array_iterator(T* value, int index) { - m_value = value; - m_index = index; + value_ = value; + index_ = index; } bool operator!=(array_iterator const& other) const { - return m_index != other.m_index; + return index_ != other.index_; } T & operator*() const { - return m_value[m_index]; + return value_[index_]; } array_iterator& operator++() { - ++m_index; + ++index_; return *this; } private: - T* m_value; - int m_index; + T* value_; + int index_; }; template @@ -52,19 +52,19 @@ struct array > final { } T& operator[](int index) { - return m_array[index]; + return array_[index]; } T& operator[](size_t index) { - return m_array[index]; + return array_[index]; } array_iterator begin() { - return array_iterator(m_array, 0); + return array_iterator(array_, 0); } array_iterator end() { - return array_iterator(m_array, Size); + return array_iterator(array_, Size); } size_t size() { @@ -72,7 +72,7 @@ struct array > final { } T* ptr() { - return m_array; + return array_; } template @@ -83,7 +83,7 @@ struct array > final { } private: - T m_array[Size]; + T array_[Size]; }; template diff --git a/vendors/flecs/include/flecs/addons/cpp/utils/builder.hpp b/vendors/flecs/include/flecs/addons/cpp/utils/builder.hpp index e44298afc..c45d22724 100644 --- a/vendors/flecs/include/flecs/addons/cpp/utils/builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/utils/builder.hpp @@ -21,32 +21,32 @@ struct builder : IBuilder public: builder(flecs::world_t *world) - : IBase(&m_desc) - , m_desc{} - , m_world(world) { } + : IBase(&desc_) + , desc_{} + , world_(world) { } builder(const builder& f) - : IBase(&m_desc, f.m_term_index) + : IBase(&desc_, f.term_index_) { - m_world = f.m_world; - m_desc = f.m_desc; + world_ = f.world_; + desc_ = f.desc_; } - builder(builder&& f) + builder(builder&& f) noexcept : builder(f) { } operator TDesc*() { - return &m_desc; + return &desc_; } T build() { - return T(m_world, *static_cast(this)); + return T(world_, *static_cast(this)); } protected: - flecs::world_t* world_v() override { return m_world; } - TDesc m_desc; - flecs::world_t *m_world; + flecs::world_t* world_v() override { return world_; } + TDesc desc_; + flecs::world_t *world_; }; #undef FLECS_TBUILDER diff --git a/vendors/flecs/include/flecs/addons/cpp/utils/enum.hpp b/vendors/flecs/include/flecs/addons/cpp/utils/enum.hpp index d40f8f18e..739964651 100644 --- a/vendors/flecs/include/flecs/addons/cpp/utils/enum.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/utils/enum.hpp @@ -7,6 +7,7 @@ */ #include +#include #define FLECS_ENUM_MAX(T) _::to_constant::value #define FLECS_ENUM_MAX_COUNT (FLECS_ENUM_MAX(int) + 1) @@ -23,21 +24,25 @@ #endif #endif +#if defined(__clang__) && __clang_major__ >= 16 +// https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 +#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v) +#elif defined(__GNUC__) && __GNUC__ > 10 +#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v) +#else +#define flecs_enum_cast(T, v) static_cast(v) +#endif + namespace flecs { /** Int to enum */ namespace _ { -template +template Value> struct to_constant { -#if defined(__clang__) && __clang_major__ >= 16 - // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 - static constexpr E value = __builtin_bit_cast(E, Value); -#else - static constexpr E value = static_cast(Value); -#endif + static constexpr E value = flecs_enum_cast(E, Value); }; -template +template Value> constexpr E to_constant::value; } @@ -66,7 +71,7 @@ namespace _ { #if INTPTR_MAX == INT64_MAX #ifdef ECS_TARGET_MSVC - #if _MSC_VER >= 1930 + #if _MSC_VER >= 1929 #define ECS_SIZE_T_STR "unsigned __int64" #else #define ECS_SIZE_T_STR "unsigned int" @@ -82,7 +87,7 @@ namespace _ { #endif #else #ifdef ECS_TARGET_MSVC - #if _MSC_VER >= 1930 + #if _MSC_VER >= 1929 #define ECS_SIZE_T_STR "unsigned __int32" #else #define ECS_SIZE_T_STR "unsigned int" @@ -107,16 +112,18 @@ constexpr size_t enum_type_len() { /** Test if value is valid for enumeration. * This function leverages that when a valid value is provided, * __PRETTY_FUNCTION__ contains the enumeration name, whereas if a value is - * invalid, the string contains a number. */ + * invalid, the string contains a number or a negative (-) symbol. */ #if defined(ECS_TARGET_CLANG) #if ECS_CLANG_VERSION < 13 template constexpr bool enum_constant_is_valid() { - return !( + return !(( (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + enum_type_len() + 6 /* ', C = ' */] >= '0') && (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + - enum_type_len() + 6 /* ', C = ' */] <= '9')); + enum_type_len() + 6 /* ', C = ' */] <= '9')) || + (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) + + enum_type_len() + 6 /* ', C = ' */] == '-')); } #else template @@ -128,7 +135,7 @@ constexpr bool enum_constant_is_valid() { #elif defined(ECS_TARGET_GNU) template constexpr bool enum_constant_is_valid() { - return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constepxr bool, enum_constant_is_valid) + + return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constexpr bool, enum_constant_is_valid) + enum_type_len() + 8 /* ', E C = ' */] != '('); } #else @@ -142,6 +149,12 @@ constexpr bool enum_constant_is_valid() { } #endif +/* Without this wrapper __builtin_bit_cast doesn't work */ +template C> +constexpr bool enum_constant_is_valid_wrap() { + return enum_constant_is_valid(); +} + template struct enum_is_valid { static constexpr bool value = enum_constant_is_valid(); @@ -158,23 +171,179 @@ static const char* enum_constant_to_name() { } /** Enumeration constant data */ +template struct enum_constant_data { flecs::entity_t id; - int next; + T offset; +}; + +/** + * @brief Provides utilities for enum reflection. + * + * This struct provides static functions for enum reflection, including conversion + * between enum values and their underlying integral types, and iteration over enum + * values. + * + * @tparam E The enum type. + * @tparam Handler The handler for enum reflection operations. + */ +template +struct enum_reflection { + using U = underlying_type_t; + + /** + * @brief Iterates over the range [Low, High] of enum values between Low and High. + * + * Recursively divide and conquers the search space to reduce the template-depth. Once + * recursive division is complete, calls Handle::handle_constant in ascending order, + * passing the values computed up the chain. + * + * @tparam Low The lower bound of the search range, inclusive. + * @tparam High The upper bound of the search range, inclusive. + * @tparam Args Additional arguments to be passed through to Handler::handle_constant + * @param last_value The last value processed in the iteration. + * @param args Additional arguments to be passed through to Handler::handle_constant + * @return constexpr U The result of the iteration. + */ + template + static constexpr U each_enum_range(U last_value, Args... args) { + return High - Low <= 1 + ? High == Low + ? Handler::template handle_constant(last_value, args...) + : Handler::template handle_constant(Handler::template handle_constant(last_value, args...), args...) + : each_enum_range<(Low + High) / 2 + 1, High>( + each_enum_range(last_value, args...), + args... + ); + } + + /** + * @brief Iterates over the mask range (Low, High] of enum values between Low and High. + * + * Recursively iterates the search space, looking for enums defined as multiple-of-2 + * bitmasks. Each iteration, shifts bit to the right until it hits Low, then calls + * Handler::handle_constant for each bitmask in ascending order. + * + * @tparam Low The lower bound of the search range, not inclusive + * @tparam High The upper bound of the search range, inclusive. + * @tparam Args Additional arguments to be passed through to Handler::handle_constant + * @param last_value The last value processed in the iteration. + * @param args Additional arguments to be passed through to Handler::handle_constant + * @return constexpr U The result of the iteration. + */ + template + static constexpr U each_mask_range(U last_value, Args... args) { + // If Low shares any bits with Current Flag, or if High is less than/equal to Low (and High isn't negative because max-flag signed) + return (Low & High) || (High <= Low && High != high_bit) + ? last_value + : Handler::template handle_constant( + each_mask_range> 1) & ~high_bit)>(last_value, args...), + args... + ); + } + + /** + * @brief Handles enum iteration for gathering reflection data. + * + * Iterates over all enum values up to a specified maximum value + * (each_enum_range<0, Value>), then iterates the rest of the possible bitmasks + * (each_mask_range). + * + * @tparam Value The maximum enum value to iterate up to. + * @tparam Args Additional arguments to be passed through to Handler::handle_constant + * @param args Additional arguments to be passed through to Handler::handle_constant + * @return constexpr U The result of the iteration. + */ + template (FLECS_ENUM_MAX(E)), typename... Args> + static constexpr U each_enum(Args... args) { + return each_mask_range(each_enum_range<0, Value>(0, args...), args...); + } + + static const U high_bit = static_cast(1) << (sizeof(U) * 8 - 1); }; /** Enumeration type data */ +template struct enum_data_impl { +private: + using U = underlying_type_t; + + /** + * @brief Handler struct for generating compile-time count of enum constants. + */ + struct reflection_count { + template () > = 0> + static constexpr U handle_constant(U last_value) { + return last_value; + } + + template () > = 0> + static constexpr U handle_constant(U last_value) { + return 1 + last_value; + } + }; + +public: flecs::entity_t id; int min; int max; - enum_constant_data constants[FLECS_ENUM_MAX_COUNT]; + bool has_contiguous; + // If enum constants start not-sparse, contiguous_until will be the index of the first sparse value, or end of the constants array + U contiguous_until; + // Compile-time generated count of enum constants. + static constexpr unsigned int constants_size = enum_reflection::template each_enum< static_cast(enum_last::value) >(); + // Constants array is sized to the number of found-constants, or 1 (to avoid 0-sized array) + enum_constant_data constants[constants_size? constants_size: 1]; }; /** Class that scans an enum for constants, extracts names & creates entities */ template struct enum_type { - static enum_data_impl data; +private: + using U = underlying_type_t; + + /** + * @brief Helper struct for filling enum_type's static `enum_data_impl` member with reflection data. + * + * Because reflection occurs in-order, we can use current value/last value to determine continuity, and + * use that as a lookup heuristic later on. + */ + struct reflection_init { + template () > = 0> + static U handle_constant(U last_value, flecs::world_t*) { + // Search for constant failed. Pass last valid value through. + return last_value; + } + + template () > = 0> + static U handle_constant(U last_value, flecs::world_t *world) { + // Constant is valid, so fill reflection data. + auto v = Value; + const char *name = enum_constant_to_name(); + + ++enum_type::data.max; // Increment cursor as we build constants array. + + // If the enum was previously contiguous, and continues to be through the current value... + if (enum_type::data.has_contiguous && static_cast(enum_type::data.max) == v && enum_type::data.contiguous_until == v) { + ++enum_type::data.contiguous_until; + } + // else, if the enum was never contiguous and hasn't been set as not contiguous... + else if (!enum_type::data.contiguous_until && enum_type::data.has_contiguous) { + enum_type::data.has_contiguous = false; + } + + ecs_assert(!(last_value > 0 && v < std::numeric_limits::min() + last_value), ECS_UNSUPPORTED, + "Signed integer enums causes integer overflow when recording offset from high positive to" + " low negative. Consider using unsigned integers as underlying type."); + enum_type::data.constants[enum_type::data.max].offset = v - last_value; + enum_type::data.constants[enum_type::data.max].id = ecs_cpp_enum_constant_register( + world, enum_type::data.id, 0, name, static_cast(v)); + return v; + } + }; +public: + + static enum_data_impl data; static enum_type& get() { static _::enum_type instance; @@ -182,66 +351,35 @@ struct enum_type { } flecs::entity_t entity(E value) const { - return data.constants[static_cast(value)].id; + int index = index_by_value(value); + if (index >= 0) { + return data.constants[index].id; + } + return 0; } void init(flecs::world_t *world, flecs::entity_t id) { #if !FLECS_CPP_ENUM_REFLECTION_SUPPORT ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher") #endif + // Initialize/reset reflection data values to default state. + data.min = 0; + data.max = -1; + data.has_contiguous = true; + data.contiguous_until = 0; ecs_log_push(); ecs_cpp_enum_init(world, id); data.id = id; - data.min = FLECS_ENUM_MAX(int); - init< enum_last::value >(world); - ecs_log_pop(); - } - -private: - template - static constexpr int to_int() { - return static_cast(Value); - } - - template - static constexpr E from_int() { - return to_constant::value; - } - - template - static constexpr int is_not_0() { - return static_cast(Value != from_int<0>()); - } - - template () > = 0> - static void init_constant(flecs::world_t*) { } - - template () > = 0> - static void init_constant(flecs::world_t *world) { - int v = to_int(); - const char *name = enum_constant_to_name(); - data.constants[v].next = data.min; - data.min = v; - if (!data.max) { - data.max = v; - } - data.constants[v].id = ecs_cpp_enum_constant_register( - world, data.id, data.constants[v].id, name, v); - } - - template - static void init(flecs::world_t *world) { - init_constant(world); - if (is_not_0()) { - init() - is_not_0()>()>(world); - } + // Generate reflection data + enum_reflection::template each_enum< static_cast(enum_last::value) >(world); + ecs_log_pop(); } }; template -enum_data_impl enum_type::data; +enum_data_impl enum_type::data; template ::value > = 0> inline static void init_enum(flecs::world_t *world, flecs::entity_t id) { @@ -256,12 +394,70 @@ inline static void init_enum(flecs::world_t*, flecs::entity_t) { } /** Enumeration type data wrapper with world pointer */ template struct enum_data { - enum_data(flecs::world_t *world, _::enum_data_impl& impl) + using U = underlying_type_t; + + enum_data(flecs::world_t *world, _::enum_data_impl& impl) : world_(world) , impl_(impl) { } + + /** + * @brief Checks if a given integral value is a valid enum value. + * + * @param value The integral value. + * @return true If the value is a valid enum value. + * @return false If the value is not a valid enum value. + */ + bool is_valid(U value) { + int index = index_by_value(value); + if (index < 0) { + return false; + } + return impl_.constants[index].id != 0; + } + + /** + * @brief Checks if a given enum value is valid. + * + * @param value The enum value. + * @return true If the value is valid. + * @return false If the value is not valid. + */ + bool is_valid(E value) { + return is_valid(static_cast(value)); + } + + /** + * @brief Finds the index into the constants array for a value, if one exists + * + * @param value The enum value. + * @return int The index of the enum value. + */ + int index_by_value(U value) const { + if (!impl_.max) { + return -1; + } + // Check if value is in contiguous lookup section + if (impl_.has_contiguous && value < impl_.contiguous_until && value >= 0) { + return static_cast(value); + } + U accumulator = impl_.contiguous_until? impl_.contiguous_until - 1: 0; + for (int i = static_cast(impl_.contiguous_until); i <= impl_.max; ++i) { + accumulator += impl_.constants[i].offset; + if (accumulator == value) { + return i; + } + } + return -1; + } - bool is_valid(int value) { - return impl_.constants[value].id != 0; + /** + * @brief Finds the index into the constants array for an enum value, if one exists + * + * @param value The enum value. + * @return int The index of the enum value. + */ + int index_by_value(E value) const { + return index_by_value(static_cast(value)); } int first() const { @@ -273,21 +469,21 @@ struct enum_data { } int next(int cur) const { - return impl_.constants[cur].next; + return cur + 1; } flecs::entity entity() const; - flecs::entity entity(int value) const; + flecs::entity entity(U value) const; flecs::entity entity(E value) const; flecs::world_t *world_; - _::enum_data_impl& impl_; + _::enum_data_impl& impl_; }; /** Convenience function for getting enum reflection data */ template enum_data enum_type(flecs::world_t *world) { - _::cpp_type::id(world); // Ensure enum is registered + _::type::id(world); // Ensure enum is registered auto& ref = _::enum_type::get(); return enum_data(world, ref.data); } diff --git a/vendors/flecs/include/flecs/addons/cpp/utils/iterable.hpp b/vendors/flecs/include/flecs/addons/cpp/utils/iterable.hpp index 74ed23474..8f202875c 100644 --- a/vendors/flecs/include/flecs/addons/cpp/utils/iterable.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/utils/iterable.hpp @@ -23,71 +23,42 @@ struct iterable { * - func(flecs::entity e, Components& ...) * - func(flecs::iter& it, size_t index, Components& ....) * - func(Components& ...) - * - * Each iterators are automatically instanced. */ template void each(Func&& func) const { - each(nullptr, FLECS_FWD(func)); - } - - template - void each(flecs::world_t *world, Func&& func) const { - iterate<_::each_delegate>(world, FLECS_FWD(func), - this->next_each_action()); - } - - template - void each(flecs::iter& it, Func&& func) const { - iterate<_::each_delegate>(it.world(), FLECS_FWD(func), - this->next_each_action()); + ecs_iter_t it = this->get_iter(nullptr); + ecs_iter_next_action_t next = this->next_action(); + while (next(&it)) { + _::each_delegate(func).invoke(&it); + } } + /** Run iterator. + * The "each" iterator accepts a function that is invoked once for a query + * with a valid iterator. The following signature is valid: + * - func(flecs::iter&) + */ template - void each(flecs::entity e, Func&& func) const { - iterate<_::each_delegate>(e.world(), FLECS_FWD(func), - this->next_each_action()); + void run(Func&& func) const { + ecs_iter_t it = this->get_iter(nullptr); + _::run_delegate(func).invoke(&it); } template flecs::entity find(Func&& func) const { - return iterate_find<_::find_delegate>(nullptr, FLECS_FWD(func), - this->next_each_action()); - } - - /** Iter iterator. - * The "iter" iterator accepts a function that is invoked for each matching - * table. The following function signatures are valid: - * - func(flecs::iter& it, Components* ...) - * - func(Components& ...) - * - * Iter iterators are not automatically instanced. When a result contains - * shared components, entities of the result will be iterated one by one. - * This ensures that applications can't accidentally read out of bounds by - * accessing a shared component as an array. - */ - template - void iter(Func&& func) const { - iterate<_::iter_delegate>(nullptr, FLECS_FWD(func), - this->next_action()); - } + ecs_iter_t it = this->get_iter(nullptr); + ecs_iter_next_action_t next = this->next_action(); - template - void iter(flecs::world_t *world, Func&& func) const { - iterate<_::iter_delegate>(world, FLECS_FWD(func), - this->next_action()); - } + flecs::entity result; + while (!result && next(&it)) { + result = _::find_delegate(func).invoke(&it); + } - template - void iter(flecs::iter& it, Func&& func) const { - iterate<_::iter_delegate>(it.world(), FLECS_FWD(func), - this->next_action()); - } + if (result) { + ecs_iter_fini(&it); + } - template - void iter(flecs::entity e, Func&& func) const { - iterate<_::iter_delegate>(e.world(), FLECS_FWD(func), - this->next_action()); + return result; } /** Create iterator. @@ -95,6 +66,16 @@ struct iterable { */ iter_iterable iter(flecs::world_t *world = nullptr) const; + /** Create iterator. + * Create an iterator object that can be modified before iterating. + */ + iter_iterable iter(flecs::iter& iter) const; + + /** Create iterator. + * Create an iterator object that can be modified before iterating. + */ + iter_iterable iter(flecs::entity e) const; + /** Page iterator. * Create an iterator that limits the returned entities with offset/limit. * @@ -129,6 +110,37 @@ struct iterable { return this->iter().first(); } + iter_iterable set_var(int var_id, flecs::entity_t value) { + return this->iter().set_var(var_id, value); + } + + iter_iterable set_var(const char *name, flecs::entity_t value) { + return this->iter().set_var(name, value); + } + + iter_iterable set_var(const char *name, flecs::table_t *value) { + return this->iter().set_var(name, value); + } + + iter_iterable set_var(const char *name, ecs_table_range_t value) { + return this->iter().set_var(name, value); + } + + iter_iterable set_var(const char *name, flecs::table_range value) { + return this->iter().set_var(name, value); + } + + // Limit results to tables with specified group id (grouped queries only) + iter_iterable set_group(uint64_t group_id) { + return this->iter().set_group(group_id); + } + + // Limit results to tables with specified group id (grouped queries only) + template + iter_iterable set_group() { + return this->iter().template set_group(); + } + virtual ~iterable() { } protected: friend iter_iterable; @@ -137,36 +149,6 @@ struct iterable { virtual ecs_iter_t get_iter(flecs::world_t *stage) const = 0; virtual ecs_iter_next_action_t next_action() const = 0; - virtual ecs_iter_next_action_t next_each_action() const = 0; - - template < template class Delegate, typename Func, typename NextFunc, typename ... Args> - void iterate(flecs::world_t *stage, Func&& func, NextFunc next, Args &&... args) const { - ecs_iter_t it = this->get_iter(stage); - if (Delegate::instanced()) { - ECS_BIT_SET(it.flags, EcsIterIsInstanced); - } - - while (next(&it, FLECS_FWD(args)...)) { - Delegate(func).invoke(&it); - } - } - - template < template class Delegate, typename Func, typename NextFunc, typename ... Args> - flecs::entity iterate_find(flecs::world_t *stage, Func&& func, NextFunc next, Args &&... args) const { - ecs_iter_t it = this->get_iter(stage); - if (Delegate::instanced()) { - ECS_BIT_SET(it.flags, EcsIterIsInstanced); - } - - flecs::entity result; - while (!result && next(&it, FLECS_FWD(args)...)) { - result = Delegate(func).invoke(&it); - } - if (result) { - ecs_iter_fini(&it); - } - return result; - } }; template @@ -174,20 +156,51 @@ struct iter_iterable final : iterable { template iter_iterable(Iterable *it, flecs::world_t *world) { - m_it = it->get_iter(world); - m_next = it->next_action(); - m_next_each = it->next_action(); + it_ = it->get_iter(world); + next_ = it->next_action(); + next_each_ = it->next_action(); + ecs_assert(next_ != nullptr, ECS_INTERNAL_ERROR, NULL); + ecs_assert(next_each_ != nullptr, ECS_INTERNAL_ERROR, NULL); } iter_iterable& set_var(int var_id, flecs::entity_t value) { ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, 0); - ecs_iter_set_var(&m_it, var_id, value); + ecs_iter_set_var(&it_, var_id, value); return *this; } -# ifdef FLECS_RULES -# include "../mixins/rule/iterable.inl" -# endif + iter_iterable& set_var(const char *name, flecs::entity_t value) { + ecs_query_iter_t *qit = &it_.priv_.iter.query; + int var_id = ecs_query_find_var(qit->query, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + ecs_iter_set_var(&it_, var_id, value); + return *this; + } + + iter_iterable& set_var(const char *name, flecs::table_t *value) { + ecs_query_iter_t *qit = &it_.priv_.iter.query; + int var_id = ecs_query_find_var(qit->query, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + ecs_iter_set_var_as_table(&it_, var_id, value); + return *this; + } + + iter_iterable& set_var(const char *name, ecs_table_range_t value) { + ecs_query_iter_t *qit = &it_.priv_.iter.query; + int var_id = ecs_query_find_var(qit->query, name); + ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, name); + ecs_iter_set_var_as_range(&it_, var_id, &value); + return *this; + } + + iter_iterable& set_var(const char *name, flecs::table_range value) { + ecs_table_range_t range; + range.table = value.get_table(); + range.offset = value.offset(); + range.count = value.count(); + return set_var(name, range); + } + # ifdef FLECS_JSON # include "../mixins/json/iterable.inl" # endif @@ -195,17 +208,17 @@ struct iter_iterable final : iterable { // Return total number of entities in result. int32_t count() { int32_t result = 0; - while (m_next_each(&m_it)) { - result += m_it.count; + while (next_each_(&it_)) { + result += it_.count; } return result; } // Returns true if iterator yields at least once result. bool is_true() { - bool result = m_next_each(&m_it); + bool result = next_each_(&it_); if (result) { - ecs_iter_fini(&m_it); + ecs_iter_fini(&it_); } return result; } @@ -213,48 +226,44 @@ struct iter_iterable final : iterable { // Return first matching entity. flecs::entity first() { flecs::entity result; - if (m_next_each(&m_it) && m_it.count) { - result = flecs::entity(m_it.world, m_it.entities[0]); - ecs_iter_fini(&m_it); + if (next_each_(&it_) && it_.count) { + result = flecs::entity(it_.world, it_.entities[0]); + ecs_iter_fini(&it_); } return result; } // Limit results to tables with specified group id (grouped queries only) iter_iterable& set_group(uint64_t group_id) { - ecs_query_set_group(&m_it, group_id); + ecs_iter_set_group(&it_, group_id); return *this; } // Limit results to tables with specified group id (grouped queries only) template iter_iterable& set_group() { - ecs_query_set_group(&m_it, _::cpp_type().id(m_it.real_world)); + ecs_iter_set_group(&it_, _::type().id(it_.real_world)); return *this; } protected: - ecs_iter_t get_iter(flecs::world_t *world) const { + ecs_iter_t get_iter(flecs::world_t *world) const override { if (world) { - ecs_iter_t result = m_it; + ecs_iter_t result = it_; result.world = world; return result; } - return m_it; - } - - ecs_iter_next_action_t next_action() const { - return m_next; + return it_; } - ecs_iter_next_action_t next_each_action() const { - return m_next_each; + ecs_iter_next_action_t next_action() const override { + return next_; } private: - ecs_iter_t m_it; - ecs_iter_next_action_t m_next; - ecs_iter_next_action_t m_next_each; + ecs_iter_t it_; + ecs_iter_next_action_t next_; + ecs_iter_next_action_t next_each_; }; template @@ -263,33 +272,41 @@ iter_iterable iterable::iter(flecs::world_t *world return iter_iterable(this, world); } +template +iter_iterable iterable::iter(flecs::iter& it) const +{ + return iter_iterable(this, it.world()); +} + +template +iter_iterable iterable::iter(flecs::entity e) const +{ + return iter_iterable(this, e.world()); +} + template struct page_iterable final : iterable { template page_iterable(int32_t offset, int32_t limit, Iterable *it) - : m_offset(offset) - , m_limit(limit) + : offset_(offset) + , limit_(limit) { - m_chain_it = it->get_iter(nullptr); + chain_it_ = it->get_iter(nullptr); } protected: ecs_iter_t get_iter(flecs::world_t*) const { - return ecs_page_iter(&m_chain_it, m_offset, m_limit); + return ecs_page_iter(&chain_it_, offset_, limit_); } ecs_iter_next_action_t next_action() const { return ecs_page_next; } - ecs_iter_next_action_t next_each_action() const { - return ecs_page_next; - } - private: - ecs_iter_t m_chain_it; - int32_t m_offset; - int32_t m_limit; + ecs_iter_t chain_it_; + int32_t offset_; + int32_t limit_; }; template @@ -303,29 +320,25 @@ page_iterable iterable::page( template struct worker_iterable final : iterable { worker_iterable(int32_t offset, int32_t limit, iterable *it) - : m_offset(offset) - , m_limit(limit) + : offset_(offset) + , limit_(limit) { - m_chain_it = it->get_iter(nullptr); + chain_it_ = it->get_iter(nullptr); } protected: ecs_iter_t get_iter(flecs::world_t*) const { - return ecs_worker_iter(&m_chain_it, m_offset, m_limit); + return ecs_worker_iter(&chain_it_, offset_, limit_); } ecs_iter_next_action_t next_action() const { return ecs_worker_next; } - ecs_iter_next_action_t next_each_action() const { - return ecs_worker_next; - } - private: - ecs_iter_t m_chain_it; - int32_t m_offset; - int32_t m_limit; + ecs_iter_t chain_it_; + int32_t offset_; + int32_t limit_; }; template diff --git a/vendors/flecs/include/flecs/addons/cpp/utils/node_builder.hpp b/vendors/flecs/include/flecs/addons/cpp/utils/node_builder.hpp index 4c9fa9aee..b583378fa 100644 --- a/vendors/flecs/include/flecs/addons/cpp/utils/node_builder.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/utils/node_builder.hpp @@ -18,55 +18,59 @@ struct node_builder : IBuilder public: explicit node_builder(flecs::world_t* world, const char *name = nullptr) - : IBase(&m_desc) - , m_desc{} - , m_world(world) - , m_instanced(false) + : IBase(&desc_) + , desc_{} + , world_(world) { ecs_entity_desc_t entity_desc = {}; entity_desc.name = name; entity_desc.sep = "::"; entity_desc.root_sep = "::"; - m_desc.entity = ecs_entity_init(m_world, &entity_desc); + desc_.entity = ecs_entity_init(world_, &entity_desc); } - /* Iter (or each) is mandatory and always the last thing that - * is added in the fluent method chain. Create system signature from both - * template parameters and anything provided by the signature method. */ template - T iter(Func&& func) { - using Delegate = typename _::iter_delegate< - typename std::decay::type, Components...>; - return build(FLECS_FWD(func)); + T run(Func&& func) { + using Delegate = typename _::run_delegate< + typename std::decay::type>; + + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); + desc_.run = Delegate::run; + desc_.run_ctx = ctx; + desc_.run_ctx_free = reinterpret_cast< + ecs_ctx_free_t>(_::free_obj); + return T(world_, &desc_); + } + + template + T run(Func&& func, EachFunc&& each_func) { + using Delegate = typename _::run_delegate< + typename std::decay::type>; + + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); + desc_.run = Delegate::run; + desc_.run_ctx = ctx; + desc_.run_ctx_free = reinterpret_cast< + ecs_ctx_free_t>(_::free_obj); + return each(FLECS_FWD(each_func)); } - /* Each is similar to action, but accepts a function that operates on a - * single entity */ template T each(Func&& func) { using Delegate = typename _::each_delegate< typename std::decay::type, Components...>; - m_instanced = true; - return build(FLECS_FWD(func)); - } - -protected: - flecs::world_t* world_v() override { return m_world; } - TDesc m_desc; - flecs::world_t *m_world; - bool m_instanced; - -private: - template - T build(Func&& func) { auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); - m_desc.callback = Delegate::run; - m_desc.binding_ctx = ctx; - m_desc.binding_ctx_free = reinterpret_cast< + desc_.callback = Delegate::run; + desc_.callback_ctx = ctx; + desc_.callback_ctx_free = reinterpret_cast< ecs_ctx_free_t>(_::free_obj); - - return T(m_world, &m_desc, m_instanced); + return T(world_, &desc_); } + +protected: + flecs::world_t* world_v() override { return world_; } + TDesc desc_; + flecs::world_t *world_; }; #undef FLECS_IBUILDER diff --git a/vendors/flecs/include/flecs/addons/cpp/utils/signature.hpp b/vendors/flecs/include/flecs/addons/cpp/utils/signature.hpp index fd21d8691..0ebf59c1a 100644 --- a/vendors/flecs/include/flecs/addons/cpp/utils/signature.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/utils/signature.hpp @@ -11,41 +11,41 @@ namespace flecs { namespace _ { template ::value > = 0> - static constexpr flecs::inout_kind_t type_to_inout() { + constexpr flecs::inout_kind_t type_to_inout() { return flecs::In; } template ::value > = 0> - static constexpr flecs::inout_kind_t type_to_inout() { - return flecs::Out; + constexpr flecs::inout_kind_t type_to_inout() { + return flecs::InOut; } template ::value || is_reference::value > = 0> - static constexpr flecs::inout_kind_t type_to_inout() { + constexpr flecs::inout_kind_t type_to_inout() { return flecs::InOutDefault; } template ::value > = 0> - static constexpr flecs::oper_kind_t type_to_oper() { + constexpr flecs::oper_kind_t type_to_oper() { return flecs::Optional; } template ::value > = 0> - static constexpr flecs::oper_kind_t type_to_oper() { + constexpr flecs::oper_kind_t type_to_oper() { return flecs::And; } template struct sig { sig(flecs::world_t *world) - : m_world(world) - , ids({ (_::cpp_type::id(world))... }) + : world_(world) + , ids({ (_::type>::id(world))... }) , inout ({ (type_to_inout())... }) , oper ({ (type_to_oper())... }) { } - flecs::world_t *m_world; + flecs::world_t *world_; flecs::array ids; flecs::array inout; flecs::array oper; @@ -54,8 +54,8 @@ namespace _ { void populate(const Builder& b) { size_t i = 0; for (auto id : ids) { - if (!(id & ECS_ID_FLAGS_MASK)) { - const flecs::type_info_t *ti = ecs_get_type_info(m_world, id); + if (!(id & ECS_ID_FLAGS_MASK)) { + const flecs::type_info_t *ti = ecs_get_type_info(world_, id); if (ti) { // Union relationships always return a value of type // flecs::entity_t which holds the target id of the @@ -65,12 +65,14 @@ namespace _ { // functions would accept a parameter of the component // type instead of flecs::entity_t, which would cause // an assert. - ecs_assert(!ti->size || !ecs_has_id(m_world, id, flecs::Union), + ecs_assert( + !ti->size || !ecs_has_id(world_, id, flecs::Union), ECS_INVALID_PARAMETER, - "use term() method to add union relationship"); + "use with() method to add union relationship"); } } - b->term(id).inout(inout[i]).oper(oper[i]); + + b->with(id).inout(inout[i]).oper(oper[i]); i ++; } } diff --git a/vendors/flecs/include/flecs/addons/cpp/utils/string.hpp b/vendors/flecs/include/flecs/addons/cpp/utils/string.hpp index 1e19d0be9..41c8a5d9e 100644 --- a/vendors/flecs/include/flecs/addons/cpp/utils/string.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/utils/string.hpp @@ -12,43 +12,43 @@ struct string_view; // wrapping in an std::string. struct string { explicit string() - : m_str(nullptr) - , m_const_str("") - , m_length(0) { } + : str_(nullptr) + , const_str_("") + , length_(0) { } explicit string(char *str) - : m_str(str) - , m_const_str(str ? str : "") - , m_length(str ? ecs_os_strlen(str) : 0) { } + : str_(str) + , const_str_(str ? str : "") + , length_(str ? ecs_os_strlen(str) : 0) { } ~string() { // If flecs is included in a binary but is not used, it is possible that // the OS API is not initialized. Calling ecs_os_free in that case could // crash the application during exit. However, if a string has been set // flecs has been used, and OS API should have been initialized. - if (m_str) { - ecs_os_free(m_str); + if (str_) { + ecs_os_free(str_); } } - string(string&& str) { - ecs_os_free(m_str); - m_str = str.m_str; - m_const_str = str.m_const_str; - m_length = str.m_length; - str.m_str = nullptr; + string(string&& str) noexcept { + ecs_os_free(str_); + str_ = str.str_; + const_str_ = str.const_str_; + length_ = str.length_; + str.str_ = nullptr; } operator const char*() const { - return m_const_str; + return const_str_; } - string& operator=(string&& str) { - ecs_os_free(m_str); - m_str = str.m_str; - m_const_str = str.m_const_str; - m_length = str.m_length; - str.m_str = nullptr; + string& operator=(string&& str) noexcept { + ecs_os_free(str_); + str_ = str.str_; + const_str_ = str.const_str_; + length_ = str.length_; + str.str_ = nullptr; return *this; } @@ -57,19 +57,19 @@ struct string { string(const string& str) = delete; bool operator==(const flecs::string& str) const { - if (str.m_const_str == m_const_str) { + if (str.const_str_ == const_str_) { return true; } - if (!m_const_str || !str.m_const_str) { + if (!const_str_ || !str.const_str_) { return false; } - if (str.m_length != m_length) { + if (str.length_ != length_) { return false; } - return ecs_os_strcmp(str, m_const_str) == 0; + return ecs_os_strcmp(str, const_str_) == 0; } bool operator!=(const flecs::string& str) const { @@ -77,15 +77,15 @@ struct string { } bool operator==(const char *str) const { - if (m_const_str == str) { + if (const_str_ == str) { return true; } - if (!m_const_str || !str) { + if (!const_str_ || !str) { return false; } - return ecs_os_strcmp(str, m_const_str) == 0; + return ecs_os_strcmp(str, const_str_) == 0; } bool operator!=(const char *str) const { @@ -93,11 +93,11 @@ struct string { } const char* c_str() const { - return m_const_str; + return const_str_; } std::size_t length() const { - return static_cast(m_length); + return static_cast(length_); } template @@ -110,9 +110,17 @@ struct string { } void clear() { - ecs_os_free(m_str); - m_str = nullptr; - m_const_str = nullptr; + ecs_os_free(str_); + str_ = nullptr; + const_str_ = nullptr; + } + + bool contains(const char *substr) { + if (const_str_) { + return strstr(const_str_, substr) != nullptr; + } else { + return false; + } } protected: @@ -122,13 +130,13 @@ struct string { // Making this constructor private forces the code to explicitly create a // string_view which emphasizes that the string won't be freed by the class. string(const char *str) - : m_str(nullptr) - , m_const_str(str ? str : "") - , m_length(str ? ecs_os_strlen(str) : 0) { } + : str_(nullptr) + , const_str_(str ? str : "") + , length_(str ? ecs_os_strlen(str) : 0) { } - char *m_str = nullptr; - const char *m_const_str; - ecs_size_t m_length; + char *str_ = nullptr; + const char *const_str_; + ecs_size_t length_; }; // For consistency, the API returns a string_view where it could have returned diff --git a/vendors/flecs/include/flecs/addons/cpp/utils/stringstream.hpp b/vendors/flecs/include/flecs/addons/cpp/utils/stringstream.hpp index d70e1cd14..964d51698 100644 --- a/vendors/flecs/include/flecs/addons/cpp/utils/stringstream.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/utils/stringstream.hpp @@ -7,22 +7,22 @@ namespace flecs { struct stringstream { explicit stringstream() - : m_buf({}) { } + : buf_({}) { } ~stringstream() { - ecs_strbuf_reset(&m_buf); + ecs_strbuf_reset(&buf_); } - stringstream(stringstream&& str) { - ecs_strbuf_reset(&m_buf); - m_buf = str.m_buf; - str.m_buf = {}; + stringstream(stringstream&& str) noexcept { + ecs_strbuf_reset(&buf_); + buf_ = str.buf_; + str.buf_ = {}; } - stringstream& operator=(stringstream&& str) { - ecs_strbuf_reset(&m_buf); - m_buf = str.m_buf; - str.m_buf = {}; + stringstream& operator=(stringstream&& str) noexcept { + ecs_strbuf_reset(&buf_); + buf_ = str.buf_; + str.buf_ = {}; return *this; } @@ -31,16 +31,16 @@ struct stringstream { stringstream(const stringstream& str) = delete; stringstream& operator<<(const char* str) { - ecs_strbuf_appendstr(&m_buf, str); + ecs_strbuf_appendstr(&buf_, str); return *this; } flecs::string str() { - return flecs::string(ecs_strbuf_get(&m_buf)); + return flecs::string(ecs_strbuf_get(&buf_)); } private: - ecs_strbuf_t m_buf; + ecs_strbuf_t buf_; }; } diff --git a/vendors/flecs/include/flecs/addons/cpp/utils/utils.hpp b/vendors/flecs/include/flecs/addons/cpp/utils/utils.hpp index e5bc0e1f2..5c48534f5 100644 --- a/vendors/flecs/include/flecs/addons/cpp/utils/utils.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/utils/utils.hpp @@ -85,6 +85,9 @@ using remove_pointer_t = typename std::remove_pointer::type; template using remove_reference_t = typename std::remove_reference::type; +template +using underlying_type_t = typename std::underlying_type::type; + using std::is_base_of; using std::is_empty; using std::is_const; diff --git a/vendors/flecs/include/flecs/addons/cpp/world.hpp b/vendors/flecs/include/flecs/addons/cpp/world.hpp index c1c211eac..6f076d740 100644 --- a/vendors/flecs/include/flecs/addons/cpp/world.hpp +++ b/vendors/flecs/include/flecs/addons/cpp/world.hpp @@ -13,15 +13,16 @@ namespace flecs // set(T&&), T = constructible 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); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); if (!ecs_is_deferred(world)) { - T& dst = *static_cast(ecs_get_mut_id(world, entity, id)); + T& dst = *static_cast(ecs_ensure_id(world, entity, id)); dst = FLECS_MOV(value); ecs_modified_id(world, entity, id); } else { - T& dst = *static_cast(ecs_get_mut_modified_id(world, entity, id)); + T& dst = *static_cast(ecs_ensure_modified_id(world, entity, id)); dst = FLECS_MOV(value); } } @@ -29,15 +30,16 @@ inline void set(world_t *world, flecs::entity_t entity, T&& value, flecs::id_t i // set(const T&), T = constructible template ::value > = 0> inline void set(world_t *world, flecs::entity_t entity, const T& value, flecs::id_t id) { - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); if (!ecs_is_deferred(world)) { - T& dst = *static_cast(ecs_get_mut_id(world, entity, id)); + T& dst = *static_cast(ecs_ensure_id(world, entity, id)); dst = FLECS_MOV(value); ecs_modified_id(world, entity, id); } else { - T& dst = *static_cast(ecs_get_mut_modified_id(world, entity, id)); + T& dst = *static_cast(ecs_ensure_modified_id(world, entity, id)); dst = FLECS_MOV(value); } } @@ -45,15 +47,16 @@ inline void set(world_t *world, flecs::entity_t entity, const T& value, flecs::i // set(T&&), T = not constructible 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); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); if (!ecs_is_deferred(world)) { - T& dst = *static_cast*>(ecs_get_mut_id(world, entity, id)); + T& dst = *static_cast*>(ecs_ensure_id(world, entity, id)); dst = FLECS_MOV(value); ecs_modified_id(world, entity, id); } else { - T& dst = *static_cast*>(ecs_get_mut_modified_id(world, entity, id)); + T& dst = *static_cast*>(ecs_ensure_modified_id(world, entity, id)); dst = FLECS_MOV(value); } } @@ -61,49 +64,51 @@ inline void set(world_t *world, flecs::entity_t entity, T&& value, flecs::id_t i // set(const T&), T = not constructible template ::value > = 0> inline void set(world_t *world, flecs::entity_t entity, const T& value, flecs::id_t id) { - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); if (!ecs_is_deferred(world)) { - T& dst = *static_cast*>(ecs_get_mut_id(world, entity, id)); + T& dst = *static_cast*>(ecs_ensure_id(world, entity, id)); dst = FLECS_MOV(value); ecs_modified_id(world, entity, id); } else { - T& dst = *static_cast*>(ecs_get_mut_modified_id(world, entity, id)); + T& dst = *static_cast*>(ecs_ensure_modified_id(world, entity, id)); dst = FLECS_MOV(value); } } // emplace for T(Args...) -template , Args...>::value || std::is_default_constructible>::value > = 0> inline void emplace(world_t *world, flecs::entity_t entity, flecs::id_t id, Args&&... args) { - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - T& dst = *static_cast(ecs_emplace_id(world, entity, id)); - + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + T& dst = *static_cast(ecs_emplace_id(world, entity, id, nullptr)); + FLECS_PLACEMENT_NEW(&dst, T{FLECS_FWD(args)...}); - ecs_modified_id(world, entity, id); + ecs_modified_id(world, entity, id); } // set(T&&) template inline void set(world_t *world, entity_t entity, A&& value) { - id_t id = _::cpp_type::id(world); + id_t id = _::type::id(world); flecs::set(world, entity, FLECS_FWD(value), id); } // set(const T&) template inline void set(world_t *world, entity_t entity, const A& value) { - id_t id = _::cpp_type::id(world); + id_t id = _::type::id(world); flecs::set(world, entity, value, id); } /** Return id without generation. - * - * @see ecs_strip_generation + * + * @see ecs_strip_generation() */ inline flecs::id_t strip_generation(flecs::entity_t e) { return ecs_strip_generation(e); @@ -119,9 +124,9 @@ struct scoped_world; /** * @defgroup cpp_world World - * @brief World operations. - * - * \ingroup cpp_core + * @ingroup cpp_core + * World operations. + * * @{ */ @@ -132,182 +137,238 @@ struct scoped_world; struct world { /** Create world. */ - explicit world() - : m_world( ecs_init() ) - , m_owned( true ) { init_builtin_components(); } + explicit world() + : world_( ecs_init() ) { + init_builtin_components(); + } /** Create world with command line arguments. * Currently command line arguments are not interpreted, but they may be * used in the future to configure Flecs parameters. */ explicit world(int argc, char *argv[]) - : m_world( ecs_init_w_args(argc, argv) ) - , m_owned( true ) { init_builtin_components(); } + : world_( ecs_init_w_args(argc, argv) ) { + init_builtin_components(); + } /** Create world from C world. */ explicit world(world_t *w) - : m_world( w ) - , m_owned( false ) { } + : world_( w ) { + if (w) { + flecs_poly_claim(w); + } + } - /** Not allowed to copy a world. May only take a reference. + /** Not allowed to copy a world. May only take a reference. */ - world(const world& obj) = delete; + world(const world& obj) { + this->world_ = obj.world_; + flecs_poly_claim(this->world_); + } - world(world&& obj) { - m_world = obj.m_world; - m_owned = obj.m_owned; - obj.m_world = nullptr; - obj.m_owned = false; + world& operator=(const world& obj) noexcept { + release(); + this->world_ = obj.world_; + flecs_poly_claim(this->world_); + return *this; } - /* Implicit conversion to world_t* */ - operator world_t*() const { return m_world; } + world(world&& obj) noexcept { + world_ = obj.world_; + obj.world_ = nullptr; + } - /** Not allowed to copy a world. May only take a reference. - */ - world& operator=(const world& obj) = delete; + world& operator=(world&& obj) noexcept { + release(); + world_ = obj.world_; + obj.world_ = nullptr; + return *this; + } - world& operator=(world&& obj) { - this->~world(); + /* Releases the underlying world object. If this is the last handle, the world + will be finalized. */ + void release() { + if (world_) { + if (!flecs_poly_release(world_)) { + if (ecs_stage_get_id(world_) == -1) { + ecs_stage_free(world_); + } else { + // before we call ecs_fini(), we increment the reference count back to 1 + // otherwise, copies of this object created during ecs_fini (e.g. a component on_remove hook) + // would call again this destructor and ecs_fini(). + flecs_poly_claim(world_); + ecs_fini(world_); + } + } + world_ = nullptr; + } + } - m_world = obj.m_world; - m_owned = obj.m_owned; - obj.m_world = nullptr; - obj.m_owned = false; - return *this; + ~world() { + release(); } - - ~world() { - if (m_owned && ecs_stage_is_async(m_world)) { - ecs_async_stage_free(m_world); - } else - if (m_owned && m_world) { - ecs_fini(m_world); - } + + /* Implicit conversion to world_t* */ + operator world_t*() const { return world_; } + + /** Make current world object owner of the world. This may only be called on + * one flecs::world object, an may only be called once. Failing to do so + * will result in undefined behavior. + * + * This operation allows a custom (C) world to be wrapped by a C++ object, + * and transfer ownership so that the world is automatically cleaned up. + */ + void make_owner() { + flecs_poly_release(world_); } /** Deletes and recreates the world. */ void reset() { - // Can only reset the world if we own the world object. - ecs_assert(this->m_owned, ECS_INVALID_OPERATION, NULL); - ecs_fini(m_world); - m_world = ecs_init(); + /* Make sure there's only one reference to the world */ + ecs_assert(flecs_poly_refcount(world_) == 1, ECS_INVALID_OPERATION, + "reset would invalidate other handles"); + ecs_fini(world_); + world_ = ecs_init(); } /** Obtain pointer to C world object. */ world_t* c_ptr() const { - return m_world; + return world_; } /** Signal application should quit. * After calling this operation, the next call to progress() returns false. */ void quit() const { - ecs_quit(m_world); + ecs_quit(world_); } /** Register action to be executed when world is destroyed. */ - void atfini(ecs_fini_action_t action, void *ctx) const { - ecs_atfini(m_world, action, ctx); + void atfini(ecs_fini_action_t action, void *ctx = nullptr) const { + ecs_atfini(world_, action, ctx); } /** Test if quit() has been called. */ bool should_quit() const { - return ecs_should_quit(m_world); + return ecs_should_quit(world_); } /** Begin frame. * When an application does not use progress() to control the main loop, it * can still use Flecs features such as FPS limiting and time measurements. - * This operation needs to be invoked whenever a new frame is about to get + * This operation needs to be invoked whenever a new frame is about to get * processed. * - * Calls to frame_begin must always be followed by frame_end. + * Calls to frame_begin() must always be followed by frame_end(). * - * The function accepts a delta_time parameter, which will get passed to - * systems. This value is also used to compute the amount of time the - * function needs to sleep to ensure it does not exceed the target_fps, when + * The function accepts a delta_time parameter, which will get passed to + * systems. This value is also used to compute the amount of time the + * function needs to sleep to ensure it does not exceed the target_fps, when * it is set. When 0 is provided for delta_time, the time will be measured. * * This function should only be ran from the main thread. * * @param delta_time Time elapsed since the last frame. * @return The provided delta_time, or measured time if 0 was provided. + * + * @see ecs_frame_begin() + * @see flecs::world::frame_end() */ ecs_ftime_t frame_begin(float delta_time = 0) const { - return ecs_frame_begin(m_world, delta_time); + return ecs_frame_begin(world_, delta_time); } - /** End frame. + /** End frame. * This operation must be called at the end of the frame, and always after - * ecs_frame_begin. + * frame_begin(). * * This function should only be ran from the main thread. + * + * @see ecs_frame_end() + * @see flecs::world::frame_begin() */ void frame_end() const { - ecs_frame_end(m_world); + ecs_frame_end(world_); } - /** Begin staging. - * When an application does not use ecs_progress to control the main loop, it - * can still use Flecs features such as the defer queue. When an application - * needs to stage changes, it needs to call this function after ecs_frame_begin. - * A call to ecs_readonly_begin must be followed by a call to ecs_readonly_end. - * - * When staging is enabled, modifications to entities are stored to a stage. - * This ensures that arrays are not modified while iterating. Modifications are - * merged back to the "main stage" when ecs_readonly_end is invoked. + /** Begin readonly mode. * - * While the world is in staging mode, no structural changes (add/remove/...) - * can be made to the world itself. Operations must be executed on a stage - * instead (see ecs_get_stage). - * - * This function should only be ran from the main thread. + * @param multi_threaded Whether to enable readonly/multi threaded mode. + * + * @return Whether world is currently readonly. * - * @return Whether world is currently staged. + * @see ecs_readonly_begin() + * @see flecs::world::is_readonly() + * @see flecs::world::readonly_end() */ - bool readonly_begin() const { - return ecs_readonly_begin(m_world); + bool readonly_begin(bool multi_threaded = false) const { + return ecs_readonly_begin(world_, multi_threaded); } - /** End staging. - * Leaves staging mode. After this operation the world may be directly mutated - * again. By default this operation also merges data back into the world, unless - * automerging was disabled explicitly. - * - * This function should only be ran from the main thread. + /** End readonly mode. + * + * @see ecs_readonly_end() + * @see flecs::world::is_readonly() + * @see flecs::world::readonly_begin() */ void readonly_end() const { - ecs_readonly_end(m_world); + ecs_readonly_end(world_); } - /** Defer operations until end of frame. + /** Defer operations until end of frame. * When this operation is invoked while iterating, operations inbetween the - * defer_begin and defer_end operations are executed at the end of the frame. + * defer_begin() and defer_end() operations are executed at the end of the frame. * * This operation is thread safe. + * + * @return true if world changed from non-deferred mode to deferred mode. + * + * @see ecs_defer_begin() + * @see flecs::world::defer() + * @see flecs::world::defer_end() + * @see flecs::world::is_deferred() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() */ bool defer_begin() const { - return ecs_defer_begin(m_world); + return ecs_defer_begin(world_); } - /** End block of operations to defer. - * See defer_begin. + /** End block of operations to defer. + * See defer_begin(). * * This operation is thread safe. + * + * @return true if world changed from deferred mode to non-deferred mode. + * + * @see ecs_defer_end() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::is_deferred() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() */ bool defer_end() const { - return ecs_defer_end(m_world); + return ecs_defer_end(world_); } /** Test whether deferring is enabled. + * + * @return True if deferred, false if not. + * + * @see ecs_is_deferred() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() */ bool is_deferred() const { - return ecs_is_deferred(m_world); + return ecs_is_deferred(world_); } /** Configure world to have N stages. @@ -316,23 +377,29 @@ struct world { * multiple threads, where each thread gets its own queue, and commands are * merged when threads are synchronized. * - * Note that set_threads() already creates the appropriate number of stages. - * The set_stage_count() operation is useful for applications that want to manage + * Note that set_threads() already creates the appropriate number of stages. + * The set_stage_count() operation is useful for applications that want to manage * their own stages and/or threads. - * + * * @param stages The number of stages. + * + * @see ecs_set_stage_count() + * @see flecs::world::get_stage_count() */ void set_stage_count(int32_t stages) const { - ecs_set_stage_count(m_world, stages); + ecs_set_stage_count(world_, stages); } /** Get number of configured stages. - * Return number of stages set by set_stage_count. + * Return number of stages set by set_stage_count(). * * @return The number of stages used for threading. + * + * @see ecs_get_stage_count() + * @see flecs::world::set_stage_count() */ int32_t get_stage_count() const { - return ecs_get_stage_count(m_world); + return ecs_get_stage_count(world_); } /** Get current stage id. @@ -342,41 +409,22 @@ struct world { * @return The stage id. */ int32_t get_stage_id() const { - return ecs_get_stage_id(m_world); + return ecs_stage_get_id(world_); } /** Test if is a stage. * If this function returns false, it is guaranteed that this is a valid * world object. - * + * * @return True if the world is a stage, false if not. */ bool is_stage() const { ecs_assert( - ecs_poly_is(m_world, ecs_world_t) || - ecs_poly_is(m_world, ecs_stage_t), - ECS_INVALID_PARAMETER, NULL); - return ecs_poly_is(m_world, ecs_stage_t); - } - - /** Enable/disable automerging for world or stage. - * When automerging is enabled, staged data will automatically be merged - * with the world when staging ends. This happens at the end of progress(), - * at a sync point or when readonly_end() is called. - * - * Applications can exercise more control over when data from a stage is - * merged by disabling automerging. This requires an application to - * explicitly call merge() on the stage. - * - * When this function is invoked on the world, it sets all current stages to - * the provided value and sets the default for new stages. When this - * function is invoked on a stage, automerging is only set for that specific - * stage. - * - * @param automerge Whether to enable or disable automerging. - */ - void set_automerge(bool automerge) const { - ecs_set_automerge(m_world, automerge); + flecs_poly_is(world_, ecs_world_t) || + flecs_poly_is(world_, ecs_stage_t), + ECS_INVALID_PARAMETER, + "flecs::world instance contains invalid reference to world or stage"); + return flecs_poly_is(world_, ecs_stage_t); } /** Merge world or stage. @@ -386,27 +434,29 @@ struct world { * (either after progress() or after readonly_end()). * * This operation may be called on an already merged stage or world. + * + * @see ecs_merge() */ void merge() const { - ecs_merge(m_world); + ecs_merge(world_); } /** Get stage-specific world pointer. - * Flecs threads can safely invoke the API as long as they have a private + * Flecs threads can safely invoke the API as long as they have a private * context to write to, also referred to as the stage. This function returns a * pointer to a stage, disguised as a world pointer. * * Note that this function does not(!) create a new world. It simply wraps the * existing world in a thread-specific context, which the API knows how to * unwrap. The reason the stage is returned as an ecs_world_t is so that it - * can be passed transparently to the existing API functions, vs. having to + * can be passed transparently to the existing API functions, vs. having to * create a dediated API for threading. * * @param stage_id The index of the stage to retrieve. - * @return A thread-specific pointer to the world. + * @return A thread-specific pointer to the world. */ flecs::world get_stage(int32_t stage_id) const { - return flecs::world(ecs_get_stage(m_world, stage_id)); + return flecs::world(ecs_get_stage(world_, stage_id)); } /** Create asynchronous stage. @@ -416,20 +466,18 @@ struct world { * * Asynchronous stages are never merged automatically, and must therefore be * manually merged with the ecs_merge function. It is not necessary to call - * defer_begin or defer_end before and after enqueuing commands, as an + * defer_begin or defer_end before and after enqueuing commands, as an * asynchronous stage unconditionally defers operations. * * The application must ensure that no commands are added to the stage while the * stage is being merged. * - * An asynchronous stage must be cleaned up by ecs_async_stage_free. - * * @return The stage. */ flecs::world async_stage() const { - auto result = flecs::world(ecs_async_stage_new(m_world)); - result.m_owned = true; - return result; + ecs_world_t *as = ecs_stage_new(world_); + flecs_poly_release(as); // world object will claim + return flecs::world(as); } /** Get actual world. @@ -441,7 +489,7 @@ struct world { flecs::world get_world() const { /* Safe cast, mutability is checked */ return flecs::world( - m_world ? const_cast(ecs_get_world(m_world)) : nullptr); + world_ ? const_cast(ecs_get_world(world_)) : nullptr); } /** Test whether the current world object is readonly. @@ -449,54 +497,80 @@ struct world { * object is readonly or whether it allows for writing. * * @return True if the world or stage is readonly. + * + * @see ecs_stage_is_readonly() + * @see flecs::world::readonly_begin() + * @see flecs::world::readonly_end() */ bool is_readonly() const { - return ecs_stage_is_readonly(m_world); + return ecs_stage_is_readonly(world_); } /** Set world context. * Set a context value that can be accessed by anyone that has a reference * to the world. * - * @param ctx The world context. + * @param ctx A pointer to a user defined structure. + * @param ctx_free A function that is invoked with ctx when the world is freed. + * + * + * @see ecs_set_ctx() + * @see flecs::world::get_ctx() */ void set_ctx(void* ctx, ecs_ctx_free_t ctx_free = nullptr) const { - ecs_set_ctx(m_world, ctx, ctx_free); + ecs_set_ctx(world_, ctx, ctx_free); } /** Get world context. + * This operation retrieves a previously set world context. + * + * @return The context set with set_binding_ctx(). If no context was set, the + * function returns NULL. * - * @return The configured world context. + * @see ecs_get_ctx() + * @see flecs::world::set_ctx() */ void* get_ctx() const { - return ecs_get_ctx(m_world); + return ecs_get_ctx(world_); } /** Set world binding context. - * Set a context value that can be accessed by anyone that has a reference - * to the world. * - * @param ctx The world context. + * Same as set_ctx() but for binding context. A binding context is intended + * specifically for language bindings to store binding specific data. + * + * @param ctx A pointer to a user defined structure. + * @param ctx_free A function that is invoked with ctx when the world is freed. + * + * @see ecs_set_binding_ctx() + * @see flecs::world::get_binding_ctx() */ void set_binding_ctx(void* ctx, ecs_ctx_free_t ctx_free = nullptr) const { - ecs_set_binding_ctx(m_world, ctx, ctx_free); + ecs_set_binding_ctx(world_, ctx, ctx_free); } /** Get world binding context. + * This operation retrieves a previously set world binding context. * - * @return The configured world context. + * @return The context set with set_binding_ctx(). If no context was set, the + * function returns NULL. + * + * @see ecs_get_binding_ctx() + * @see flecs::world::set_binding_ctx() */ void* get_binding_ctx() const { - return ecs_get_binding_ctx(m_world); + return ecs_get_binding_ctx(world_); } /** Preallocate memory for number of entities. * This function preallocates memory for the entity index. * * @param entity_count Number of entities to preallocate memory for. + * + * @see ecs_dim() */ void dim(int32_t entity_count) const { - ecs_dim(m_world, entity_count); + ecs_dim(world_, entity_count); } /** Set entity range. @@ -504,9 +578,11 @@ struct world { * * @param min Minimum entity id issued. * @param max Maximum entity id issued. + * + * @see ecs_set_entity_range() */ void set_entity_range(entity_t min, entity_t max) const { - ecs_set_entity_range(m_world, min, max); + ecs_set_entity_range(world_, min, max); } /** Enforce that operations cannot modify entities outside of range. @@ -516,76 +592,86 @@ struct world { * networked applications. * * @param enabled True if range check should be enabled, false if not. + * + * @see ecs_enable_range_check() */ - void enable_range_check(bool enabled) const { - ecs_enable_range_check(m_world, enabled); + void enable_range_check(bool enabled = true) const { + ecs_enable_range_check(world_, enabled); } /** Set current scope. * * @param scope The scope to set. * @return The current scope; - * @see ecs_set_scope + * + * @see ecs_set_scope() + * @see flecs::world::get_scope() */ flecs::entity set_scope(const flecs::entity_t scope) const; /** Get current scope. * * @return The current scope. - * * @see ecs_get_scope + * + * @see ecs_get_scope() + * @see flecs::world::set_scope() */ flecs::entity get_scope() const; /** Same as set_scope but with type. - * * @see ecs_set_scope + * + * @see ecs_set_scope() + * @see flecs::world::get_scope() */ template flecs::entity set_scope() const; /** Set search path. - * @see ecs_set_lookup_path + * + * @see ecs_set_lookup_path() + * @see flecs::world::lookup() */ flecs::entity_t* set_lookup_path(const flecs::entity_t *search_path) const { - return ecs_set_lookup_path(m_world, search_path); + return ecs_set_lookup_path(world_, search_path); } /** Lookup entity by name. - * + * * @param name Entity name. - * @param search_path When false, only the current scope is searched. + * @param recursive When false, only the current scope is searched. * @result The entity if found, or 0 if not found. */ - flecs::entity lookup(const char *name, bool search_path = true) const; + flecs::entity lookup(const char *name, const char *sep = "::", const char *root_sep = "::", bool recursive = true) const; /** Set singleton component. */ template ::value > = 0> void set(const T& value) const { - flecs::set(m_world, _::cpp_type::id(m_world), value); + flecs::set(world_, _::type::id(world_), value); } /** Set singleton component. */ template ::value > = 0> void set(T&& value) const { - flecs::set(m_world, _::cpp_type::id(m_world), + flecs::set(world_, _::type::id(world_), FLECS_FWD(value)); } /** Set singleton pair. */ - template , + template , typename A = actual_type_t

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

(m_world, _::cpp_type::id(m_world), value); + flecs::set

(world_, _::type::id(world_), value); } /** Set singleton pair. */ - template , + template , typename A = actual_type_t

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

(m_world, _::cpp_type::id(m_world), FLECS_FWD(value)); + flecs::set

(world_, _::type::id(world_), FLECS_FWD(value)); } /** Set singleton pair. @@ -605,15 +691,16 @@ struct world { template void emplace(Args&&... args) const { - flecs::id_t component_id = _::cpp_type::id(m_world); - flecs::emplace(m_world, component_id, component_id, - FLECS_FWD(args)...); - } + flecs::id_t component_id = _::type::id(world_); + flecs::emplace(world_, component_id, component_id, FLECS_FWD(args)...); + } - /** Get mut singleton component. + /** Ensure singleton component. */ + #ifndef ensure template - T* get_mut() const; + T& ensure() const; + #endif /** Mark singleton component as modified. */ @@ -632,7 +719,7 @@ struct world { /** Get singleton pair. */ - template , + template , typename A = actual_type_t

> const A* get() const; @@ -640,19 +727,35 @@ struct world { */ template const First* get(Second second) const; - + /** Get singleton component inside a callback. */ template ::value > = 0 > void get(const Func& func) const; + /** Get mutable singleton component. + */ + template + T* get_mut() const; + + /** Get mutable singleton pair. + */ + template , + typename A = actual_type_t

> + A* get_mut() const; + + /** Get mutable singleton pair. + */ + template + First* get_mut(Second second) const; + /** Test if world has singleton component. */ template bool has() const; /** Test if world has the provided pair. - * + * * @tparam First The first element of the pair * @tparam Second The second element of the pair */ @@ -660,7 +763,7 @@ struct world { bool has() const; /** Test if world has the provided pair. - * + * * @tparam First The first element of the pair * @param second The second element of the pair. */ @@ -668,7 +771,7 @@ struct world { bool has(flecs::id_t second) const; /** Test if world has the provided pair. - * + * * @param first The first element of the pair * @param second The second element of the pair */ @@ -680,7 +783,7 @@ struct world { void add() const; /** Adds a pair to the singleton component. - * + * * @tparam First The first element of the pair * @tparam Second The second element of the pair */ @@ -688,7 +791,7 @@ struct world { void add() const; /** Adds a pair to the singleton component. - * + * * @tparam First The first element of the pair * @param second The second element of the pair. */ @@ -696,7 +799,7 @@ struct world { void add(flecs::entity_t second) const; /** Adds a pair to the singleton entity. - * + * * @param first The first element of the pair * @param second The second element of the pair */ @@ -708,7 +811,7 @@ struct world { void remove() const; /** Removes the pair singleton component. - * + * * @tparam First The first element of the pair * @tparam Second The second element of the pair */ @@ -716,7 +819,7 @@ struct world { void remove() const; /** Removes the pair singleton component. - * + * * @tparam First The first element of the pair * @param second The second element of the pair. */ @@ -724,15 +827,18 @@ struct world { void remove(flecs::entity_t second) const; /** Removes the pair singleton component. - * + * * @param first The first element of the pair * @param second The second element of the pair */ void remove(flecs::entity_t first, flecs::entity_t second) const; - /** Iterate entities in root of world + /** Iterate entities in root of world * Accepts a callback with the following signature: - * void(*)(flecs::entity e); + * + * @code + * void(*)(flecs::entity e); + * @endcode */ template void children(Func&& f) const; @@ -788,21 +894,21 @@ struct world { * @param name Name of the entity. * @param alias Alias for the entity. */ - flecs::entity use(const char *name, const char *alias = nullptr) const; + flecs::entity use(const char *name, const char *alias = nullptr) const; /** Create alias for entity. * * @param entity Entity for which to create the alias. * @param alias Alias for the entity. */ - void use(flecs::entity entity, const char *alias = nullptr) const; + void use(flecs::entity entity, const char *alias = nullptr) const; /** Count entities matching a component. * * @param component_id The component id. */ int count(flecs::id_t component_id) const { - return ecs_count_id(m_world, component_id); + return ecs_count_id(world_, component_id); } /** Count entities matching a pair. @@ -811,7 +917,7 @@ struct world { * @param second The second element of the pair. */ int count(flecs::entity_t first, flecs::entity_t second) const { - return ecs_count_id(m_world, ecs_pair(first, second)); + return ecs_count_id(world_, ecs_pair(first, second)); } /** Count entities matching a component. @@ -820,7 +926,7 @@ struct world { */ template int count() const { - return count(_::cpp_type::id(m_world)); + return count(_::type::id(world_)); } /** Count entities matching a pair. @@ -830,7 +936,7 @@ struct world { */ template int count(flecs::entity_t second) const { - return count(_::cpp_type::id(m_world), second); + return count(_::type::id(world_), second); } /** Count entities matching a pair. @@ -840,18 +946,18 @@ struct world { */ template int count() const { - return count( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world)); + return count( + _::type::id(world_), + _::type::id(world_)); } /** All entities created in function are created with id. */ template void with(id_t with_id, const Func& func) const { - ecs_id_t prev = ecs_set_with(m_world, with_id); + ecs_id_t prev = ecs_set_with(world_, with_id); func(); - ecs_set_with(m_world, prev); + ecs_set_with(world_, prev); } /** All entities created in function are created with type. @@ -873,7 +979,7 @@ struct world { template void with(id_t second, const Func& func) const { with(ecs_pair(this->id(), second), func); - } + } /** All entities created in function are created with pair. */ @@ -887,16 +993,16 @@ struct world { */ template void scope(id_t parent, const Func& func) const { - ecs_entity_t prev = ecs_set_scope(m_world, parent); + ecs_entity_t prev = ecs_set_scope(world_, parent); func(); - ecs_set_scope(m_world, prev); + ecs_set_scope(world_, prev); } - + /** Same as scope(parent, func), but with T as parent. */ template void scope(const Func& func) const { - flecs::id_t parent = _::cpp_type::id(m_world); + flecs::id_t parent = _::type::id(world_); scope(parent, func); } @@ -912,7 +1018,7 @@ struct world { /** Delete all entities with specified id. */ void delete_with(id_t the_id) const { - ecs_delete_with(m_world, the_id); + ecs_delete_with(world_, the_id); } /** Delete all entities with specified pair. */ @@ -923,18 +1029,24 @@ struct world { /** Delete all entities with specified component. */ template void delete_with() const { - delete_with(_::cpp_type::id(m_world)); + delete_with(_::type::id(world_)); } /** Delete all entities with specified pair. */ template void delete_with() const { - delete_with(_::cpp_type::id(m_world), _::cpp_type::id(m_world)); + delete_with(_::type::id(world_), _::type::id(world_)); + } + + /** Delete all entities with specified pair. */ + template + void delete_with(entity_t second) const { + delete_with(_::type::id(world_), second); } /** Remove all instances of specified id. */ void remove_all(id_t the_id) const { - ecs_remove_all(m_world, the_id); + ecs_remove_all(world_, the_id); } /** Remove all instances of specified pair. */ @@ -945,95 +1057,116 @@ struct world { /** Remove all instances of specified component. */ template void remove_all() const { - remove_all(_::cpp_type::id(m_world)); + remove_all(_::type::id(world_)); } /** Remove all instances of specified pair. */ template void remove_all() const { - remove_all(_::cpp_type::id(m_world), _::cpp_type::id(m_world)); + remove_all(_::type::id(world_), _::type::id(world_)); + } + + /** Remove all instances of specified pair. */ + template + void remove_all(entity_t second) const { + remove_all(_::type::id(world_), second); } - /** Defer all operations called in function. If the world is already in - * deferred mode, do nothing. + /** Defer all operations called in function. + * + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_is_deferred() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() */ template void defer(const Func& func) const { - ecs_defer_begin(m_world); + ecs_defer_begin(world_); func(); - ecs_defer_end(m_world); + ecs_defer_end(world_); } /** Suspend deferring operations. - * - * @see ecs_defer_suspend + * + * @see ecs_defer_suspend() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_is_deferred() + * @see flecs::world::defer_resume() */ void defer_suspend() const { - ecs_defer_suspend(m_world); + ecs_defer_suspend(world_); } /** Resume deferring operations. - * - * @see ecs_defer_suspend + * + * @see ecs_defer_resume() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_is_deferred() + * @see flecs::world::defer_suspend() */ void defer_resume() const { - ecs_defer_resume(m_world); + ecs_defer_resume(world_); } /** Check if entity id exists in the world. - * - * @see ecs_exists + * + * @see ecs_exists() + * @see flecs::world::is_alive() + * @see flecs::world::is_valid() */ bool exists(flecs::entity_t e) const { - return ecs_exists(m_world, e); + return ecs_exists(world_, e); } /** Check if entity id exists in the world. * - * @see ecs_is_alive + * @see ecs_is_alive() + * @see flecs::world::exists() + * @see flecs::world::is_valid() */ bool is_alive(flecs::entity_t e) const { - return ecs_is_alive(m_world, e); + return ecs_is_alive(world_, e); } /** Check if entity id is valid. * Invalid entities cannot be used with API functions. - * - * @see ecs_is_valid + * + * @see ecs_is_valid() + * @see flecs::world::exists() + * @see flecs::world::is_alive() */ bool is_valid(flecs::entity_t e) const { - return ecs_is_valid(m_world, e); + return ecs_is_valid(world_, e); } /** Get alive entity for id. * Returns the entity with the current generation. - * - * @see ecs_get_alive + * + * @see ecs_get_alive() */ flecs::entity get_alive(flecs::entity_t e) const; -/* Prevent clashing with Unreal define. Unreal applications will have to use - * ecs_ensure. */ -#ifndef ensure - /** Ensures that entity with provided generation is alive. - * Ths operation will fail if an entity exists with the same id and a - * different, non-zero generation. - * - * @see ecs_ensure + /** + * @see ecs_make_alive() */ - flecs::entity ensure(flecs::entity_t e) const; -#endif + flecs::entity make_alive(flecs::entity_t e) const; /* Run callback after completing frame */ void run_post_frame(ecs_fini_action_t action, void *ctx) const { - ecs_run_post_frame(m_world, action, ctx); + ecs_run_post_frame(world_, action, ctx); } /** Get the world info. - * @see ecs_get_world_info + * + * @see ecs_get_world_info() */ const flecs::world_info_t* get_info() const{ - return ecs_get_world_info(m_world); + return ecs_get_world_info(world_); } /** Get delta_time */ @@ -1046,7 +1179,6 @@ struct world { # include "mixins/entity/mixin.inl" # include "mixins/event/mixin.inl" # include "mixins/term/mixin.inl" -# include "mixins/filter/mixin.inl" # include "mixins/observer/mixin.inl" # include "mixins/query/mixin.inl" # include "mixins/enum/mixin.inl" @@ -1057,20 +1189,14 @@ struct world { # ifdef FLECS_PIPELINE # include "mixins/pipeline/mixin.inl" # endif -# ifdef FLECS_SNAPSHOT -# include "mixins/snapshot/mixin.inl" -# endif # ifdef FLECS_SYSTEM # include "mixins/system/mixin.inl" # endif # ifdef FLECS_TIMER # include "mixins/timer/mixin.inl" # endif -# ifdef FLECS_RULES -# include "mixins/rule/mixin.inl" -# endif -# ifdef FLECS_PLECS -# include "mixins/plecs/mixin.inl" +# ifdef FLECS_SCRIPT +# include "mixins/script/mixin.inl" # endif # ifdef FLECS_META # include "mixins/meta/world.inl" @@ -1091,8 +1217,7 @@ struct world { public: void init_builtin_components(); - world_t *m_world; - bool m_owned; + world_t *world_; }; /** Scoped world. @@ -1100,25 +1225,23 @@ struct world { */ struct scoped_world : world { scoped_world( - flecs::world_t *w, - flecs::entity_t s) : world(nullptr) + flecs::world_t *w, + flecs::entity_t s) : world(w) { - m_prev_scope = ecs_set_scope(w, s); - m_world = w; - m_owned = false; + prev_scope_ = ecs_set_scope(w, s); } ~scoped_world() { - ecs_set_scope(m_world, m_prev_scope); + ecs_set_scope(world_, prev_scope_); } scoped_world(const scoped_world& obj) : world(nullptr) { - m_prev_scope = obj.m_prev_scope; - m_world = obj.m_world; - m_owned = obj.m_owned; + prev_scope_ = obj.prev_scope_; + world_ = obj.world_; + flecs_poly_claim(world_); } - flecs::entity_t m_prev_scope; + flecs::entity_t prev_scope_; }; /** @} */ diff --git a/vendors/flecs/include/flecs/addons/doc.h b/vendors/flecs/include/flecs/addons/doc.h index ac56767f9..9bacf3bf1 100644 --- a/vendors/flecs/include/flecs/addons/doc.h +++ b/vendors/flecs/include/flecs/addons/doc.h @@ -23,29 +23,79 @@ extern "C" { /** * @defgroup c_addons_doc Doc - * @brief Utilities for documenting entities, components and systems. - * - * \ingroup c_addons + * @ingroup c_addons + * Utilities for documenting entities, components and systems. + * * @{ */ -FLECS_API extern const ecs_entity_t ecs_id(EcsDocDescription); +FLECS_API extern const ecs_entity_t ecs_id(EcsDocDescription); /**< Component id for EcsDocDescription. */ + +/** Tag for adding a UUID to entities. + * Added to an entity as (EcsDocDescription, EcsUuid) by ecs_doc_set_uuid(). + */ +FLECS_API extern const ecs_entity_t EcsDocUuid; + +/** Tag for adding brief descriptions to entities. + * Added to an entity as (EcsDocDescription, EcsBrief) by ecs_doc_set_brief(). + */ FLECS_API extern const ecs_entity_t EcsDocBrief; + +/** Tag for adding detailed descriptions to entities. + * Added to an entity as (EcsDocDescription, EcsDocDetail) by ecs_doc_set_detail(). + */ FLECS_API extern const ecs_entity_t EcsDocDetail; + +/** Tag for adding a link to entities. + * Added to an entity as (EcsDocDescription, EcsDocLink) by ecs_doc_set_link(). + */ FLECS_API extern const ecs_entity_t EcsDocLink; + +/** Tag for adding a color to entities. + * Added to an entity as (EcsDocDescription, EcsDocColor) by ecs_doc_set_link(). + */ FLECS_API extern const ecs_entity_t EcsDocColor; +/** Component that stores description. + * Used as pair together with the following tags to store entity documentation: + * - EcsName + * - EcsDocBrief + * - EcsDocDetail + * - EcsDocLink + * - EcsDocColor + */ typedef struct EcsDocDescription { char *value; } EcsDocDescription; +/** Add UUID to entity. + * Associate entity with an (external) UUID. + * + * @param world The world. + * @param entity The entity to which to add the UUID. + * @param uuid The UUID to add. + * + * @see ecs_doc_get_uuid() + * @see flecs::doc::set_uuid() + * @see flecs::entity_builder::set_doc_uuid() + */ +FLECS_API +void ecs_doc_set_uuid( + ecs_world_t *world, + ecs_entity_t entity, + const char *uuid); + /** Add human-readable name to entity. * Contrary to entity names, human readable names do not have to be unique and * can contain special characters used in the query language like '*'. - * + * * @param world The world. * @param entity The entity to which to add the name. * @param name The name to add. + * + * @see ecs_doc_get_name() + * @see flecs::doc::set_name() + * @see flecs::entity_builder::set_doc_name() */ FLECS_API void ecs_doc_set_name( @@ -54,10 +104,14 @@ void ecs_doc_set_name( const char *name); /** Add brief description to entity. - * + * * @param world The world. * @param entity The entity to which to add the description. * @param description The description to add. + * + * @see ecs_doc_get_brief() + * @see flecs::doc::set_brief() + * @see flecs::entity_builder::set_doc_brief() */ FLECS_API void ecs_doc_set_brief( @@ -66,10 +120,14 @@ void ecs_doc_set_brief( const char *description); /** Add detailed description to entity. - * + * * @param world The world. * @param entity The entity to which to add the description. * @param description The description to add. + * + * @see ecs_doc_get_detail() + * @see flecs::doc::set_detail() + * @see flecs::entity_builder::set_doc_detail() */ FLECS_API void ecs_doc_set_detail( @@ -78,10 +136,14 @@ void ecs_doc_set_detail( const char *description); /** Add link to external documentation to entity. - * + * * @param world The world. * @param entity The entity to which to add the link. * @param link The link to add. + * + * @see ecs_doc_get_link() + * @see flecs::doc::set_link() + * @see flecs::entity_builder::set_doc_link() */ FLECS_API void ecs_doc_set_link( @@ -91,10 +153,14 @@ void ecs_doc_set_link( /** Add color to entity. * UIs can use color as hint to improve visualizing entities. - * + * * @param world The world. * @param entity The entity to which to add the link. * @param color The color to add. + * + * @see ecs_doc_get_color() + * @see flecs::doc::set_color() + * @see flecs::entity_builder::set_doc_color() */ FLECS_API void ecs_doc_set_color( @@ -102,18 +168,43 @@ void ecs_doc_set_color( ecs_entity_t entity, const char *color); +/** Get UUID from entity. + * @param world The world. + * @param entity The entity from which to get the UUID. + * @return The UUID. + * + * @see ecs_doc_set_uuid() + * @see flecs::doc::get_uuid() + * @see flecs::entity_view::get_doc_uuid() + */ +FLECS_API +const char* ecs_doc_get_uuid( + const ecs_world_t *world, + ecs_entity_t entity); + /** Get human readable name from entity. * If entity does not have an explicit human readable name, this operation will * return the entity name. - * + * * To test if an entity has a human readable name, use: - * ecs_has_pair(world, e, ecs_id(EcsDescription), EcsName); + * + * @code + * ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsName); + * @endcode + * * Or in C++: - * e.has(flecs::Name); - * + * + * @code + * e.has(flecs::Name); + * @endcode + * * @param world The world. * @param entity The entity from which to get the name. * @return The name. + * + * @see ecs_doc_set_name() + * @see flecs::doc::get_name() + * @see flecs::entity_view::get_doc_name() */ FLECS_API const char* ecs_doc_get_name( @@ -121,10 +212,14 @@ const char* ecs_doc_get_name( ecs_entity_t entity); /** Get brief description from entity. - * + * * @param world The world. * @param entity The entity from which to get the description. * @return The description. + * + * @see ecs_doc_set_brief() + * @see flecs::doc::get_brief() + * @see flecs::entity_view::get_doc_brief() */ FLECS_API const char* ecs_doc_get_brief( @@ -132,10 +227,14 @@ const char* ecs_doc_get_brief( ecs_entity_t entity); /** Get detailed description from entity. - * + * * @param world The world. * @param entity The entity from which to get the description. * @return The description. + * + * @see ecs_doc_set_detail() + * @see flecs::doc::get_detail() + * @see flecs::entity_view::get_doc_detail() */ FLECS_API const char* ecs_doc_get_detail( @@ -143,10 +242,14 @@ const char* ecs_doc_get_detail( ecs_entity_t entity); /** Get link to external documentation from entity. - * + * * @param world The world. * @param entity The entity from which to get the link. * @return The link. + * + * @see ecs_doc_set_link() + * @see flecs::doc::get_link() + * @see flecs::entity_view::get_doc_link() */ FLECS_API const char* ecs_doc_get_link( @@ -154,22 +257,33 @@ const char* ecs_doc_get_link( ecs_entity_t entity); /** Get color from entity. - * + * * @param world The world. - * @param entity The entity from which to get the link. + * @param entity The entity from which to get the color. * @return The color. + * + * @see ecs_doc_set_color() + * @see flecs::doc::get_color() + * @see flecs::entity_view::get_doc_color() */ FLECS_API const char* ecs_doc_get_color( const ecs_world_t *world, ecs_entity_t entity); -/* Module import */ +/** Doc module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsDoc) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsDocImport( ecs_world_t *world); -/* @} */ +/** @} */ #ifdef __cplusplus } diff --git a/vendors/flecs/include/flecs/addons/expr.h b/vendors/flecs/include/flecs/addons/expr.h deleted file mode 100644 index e9cbb164d..000000000 --- a/vendors/flecs/include/flecs/addons/expr.h +++ /dev/null @@ -1,373 +0,0 @@ -/** - * @file addons/expr.h - * @brief Flecs expression parser addon. - * - * Parse expression strings into component values. The notation is similar to - * JSON but with a smaller footprint, native support for (large) integer types, - * character types, enumerations, bitmasks and entity identifiers. - * - * Examples: - * - * Member names: - * {x: 10, y: 20} - * - * No member names (uses member ordering): - * {10, 20} - * - * Enum values: - * {color: Red} - * - * Bitmask values: - * {toppings: Lettuce|Tomato} - * - * Collections: - * {points: [10, 20, 30]} - * - * Nested objects: - * {start: {x: 10, y: 20}, stop: {x: 30, y: 40}} - * - */ - -#ifdef FLECS_EXPR - -#ifndef FLECS_META -#define FLECS_META -#endif - -#ifndef FLECS_PARSER -#define FLECS_PARSER -#endif - -#ifndef FLECS_EXPR_H -#define FLECS_EXPR_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup c_addons_expr Expr - * @brief Serialize/deserialize values to string. - * - * \ingroup c_addons - * @{ - */ - -/** Write an escaped character. - * Write a character to an output string, insert escape character if necessary. - * - * @param out The string to write the character to. - * @param in The input character. - * @param delimiter The delimiter used (for example '"') - * @return Pointer to the character after the last one written. - */ -FLECS_API -char* ecs_chresc( - char *out, - char in, - char delimiter); - -/** Parse an escaped character. - * Parse a character with a potential escape sequence. - * - * @param in Pointer to character in input string. - * @param out Output string. - * @return Pointer to the character after the last one read. - */ -const char* ecs_chrparse( - const char *in, - char *out); - -/** Write an escaped string. - * Write an input string to an output string, escape characters where necessary. - * To determine the size of the output string, call the operation with a NULL - * argument for 'out', and use the returned size to allocate a string that is - * large enough. - * - * @param out Pointer to output string (must be). - * @param size Maximum number of characters written to output. - * @param delimiter The delimiter used (for example '"'). - * @param in The input string. - * @return The number of characters that (would) have been written. - */ -FLECS_API -ecs_size_t ecs_stresc( - char *out, - ecs_size_t size, - char delimiter, - const char *in); - -/** Return escaped string. - * Return escaped version of input string. Same as ecs_stresc, but returns an - * allocated string of the right size. - * - * @param delimiter The delimiter used (for example '"'). - * @param in The input string. - * @return Escaped string. - */ -FLECS_API -char* ecs_astresc( - char delimiter, - const char *in); - -/** Storage for parser variables. Variables make it possible to parameterize - * expression strings, and are referenced with the $ operator (e.g. $var). */ -typedef struct ecs_expr_var_t { - char *name; - ecs_value_t value; - bool owned; /* Set to false if ecs_vars_t should not take ownership of var */ -} ecs_expr_var_t; - -typedef struct ecs_expr_var_scope_t { - ecs_hashmap_t var_index; - ecs_vec_t vars; - struct ecs_expr_var_scope_t *parent; -} ecs_expr_var_scope_t; - -typedef struct ecs_vars_t { - ecs_world_t *world; - ecs_expr_var_scope_t root; - ecs_expr_var_scope_t *cur; -} ecs_vars_t; - -/** Init variable storage */ -FLECS_API -void ecs_vars_init( - ecs_world_t *world, - ecs_vars_t *vars); - -/** Cleanup variable storage */ -FLECS_API -void ecs_vars_fini( - ecs_vars_t *vars); - -/** Push variable scope */ -FLECS_API -void ecs_vars_push( - ecs_vars_t *vars); - -/** Pop variable scope */ -FLECS_API -int ecs_vars_pop( - ecs_vars_t *vars); - -/** Declare variable in current scope */ -FLECS_API -ecs_expr_var_t* ecs_vars_declare( - ecs_vars_t *vars, - const char *name, - ecs_entity_t type); - -/** Declare variable in current scope from value. - * This operation takes ownership of the value. The value pointer must be - * allocated with ecs_value_new. - */ -FLECS_API -ecs_expr_var_t* ecs_vars_declare_w_value( - ecs_vars_t *vars, - const char *name, - ecs_value_t *value); - -/** Lookup variable in scope and parent scopes */ -FLECS_API -ecs_expr_var_t* ecs_vars_lookup( - const ecs_vars_t *vars, - const char *name); - -/** Used with ecs_parse_expr. */ -typedef struct ecs_parse_expr_desc_t { - const char *name; - const char *expr; - ecs_entity_t (*lookup_action)( - const ecs_world_t*, - const char *value, - void *ctx); - void *lookup_ctx; - ecs_vars_t *vars; -} ecs_parse_expr_desc_t; - -/** Parse expression into value. - * This operation parses a flecs expression into the provided pointer. The - * memory pointed to must be large enough to contain a value of the used type. - * - * If no type and pointer are provided for the value argument, the operation - * will discover the type from the expression and allocate storage for the - * value. The allocated value must be freed with ecs_value_free. - * - * @param world The world. - * @param ptr The pointer to the expression to parse. - * @param value The value containing type & pointer to write to. - * @param desc Configuration parameters for deserializer. - * @return Pointer to the character after the last one read, or NULL if failed. - */ -FLECS_API -const char* ecs_parse_expr( - ecs_world_t *world, - const char *ptr, - ecs_value_t *value, - const ecs_parse_expr_desc_t *desc); - -/** Serialize value into expression string. - * This operation serializes a value of the provided type to a string. The - * memory pointed to must be large enough to contain a value of the used type. - * - * @param world The world. - * @param type The type of the value to serialize. - * @param data The value to serialize. - * @return String with expression, or NULL if failed. - */ -FLECS_API -char* ecs_ptr_to_expr( - const ecs_world_t *world, - ecs_entity_t type, - const void *data); - -/** Serialize value into expression buffer. - * Same as ecs_ptr_to_expr, but serializes to an ecs_strbuf_t instance. - * - * @param world The world. - * @param type The type of the value to serialize. - * @param data The value to serialize. - * @param buf The strbuf to append the string to. - * @return Zero if success, non-zero if failed. - */ -FLECS_API -int ecs_ptr_to_expr_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *data, - ecs_strbuf_t *buf); - -/** Similar as ecs_ptr_to_expr, but serializes values to string. - * Whereas the output of ecs_ptr_to_expr is a valid expression, the output of - * ecs_ptr_to_str is a string representation of the value. In most cases the - * output of the two operations is the same, but there are some differences: - * - Strings are not quoted - * - * @param world The world. - * @param type The type of the value to serialize. - * @param data The value to serialize. - * @return String with result, or NULL if failed. - */ -FLECS_API -char* ecs_ptr_to_str( - const ecs_world_t *world, - ecs_entity_t type, - const void *data); - -/** Serialize value into string buffer. - * Same as ecs_ptr_to_str, but serializes to an ecs_strbuf_t instance. - * - * @param world The world. - * @param type The type of the value to serialize. - * @param data The value to serialize. - * @param buf The strbuf to append the string to. - * @return Zero if success, non-zero if failed. - */ -FLECS_API -int ecs_ptr_to_str_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *data, - ecs_strbuf_t *buf); - -/** Serialize primitive value into string buffer. - * Serializes a primitive value to an ecs_strbuf_t instance. This operation can - * be reused by other serializers to avoid having to write boilerplate code that - * serializes primitive values to a string. - * - * @param world The world. - * @param kind The kind of primitive value. - * @param data The value ot serialize - * @param buf The strbuf to append the string to. - * @return Zero if success, non-zero if failed. - */ -FLECS_API -int ecs_primitive_to_expr_buf( - const ecs_world_t *world, - ecs_primitive_kind_t kind, - const void *data, - ecs_strbuf_t *buf); - -/** Parse expression token. - * Expression tokens can contain more characters (such as '|') than tokens - * parsed by the query (term) parser. - * - * @param name The name of the expression (used for debug logs). - * @param expr The full expression (used for debug logs). - * @param ptr The pointer to the expression to parse. - * @param token The buffer to write to (must have size ECS_MAX_TOKEN_SIZE) - * @return Pointer to the character after the last one read, or NULL if failed. - */ -FLECS_API -const char *ecs_parse_expr_token( - const char *name, - const char *expr, - const char *ptr, - char *token); - -/** Evaluate interpolated expressions in string. - * This operation evaluates expressions in a string, and replaces them with - * their evaluated result. Supported expression formats are: - * - $variable_name - * - {expression} - * - * The $, { and } characters can be escaped with a backslash (\). - * - * @param world The world. - * @param str The string to evaluate. - * @param vars The variables to use for evaluation. - */ -FLECS_API -char* ecs_interpolate_string( - ecs_world_t *world, - const char *str, - const ecs_vars_t *vars); - -/** Convert iterator to vars - * This operation converts an iterator to a variable array. This allows for - * using iterator results in expressions. The operation only converts a - * single result at a time, and does not progress the iterator. - * - * Iterator fields with data will be made available as variables with as name - * the field index (e.g. "$1"). The operation does not check if reflection data - * is registered for a field type. If no reflection data is registered for the - * type, using the field variable in expressions will fail. - * - * Field variables will only contain single elements, even if the iterator - * returns component arrays. The offset parameter can be used to specify which - * element in the component arrays to return. The offset parameter must be - * smaller than it->count. - * - * The operation will create a variable for query variables that contain a - * single entity. - * - * The operation will attempt to use existing variables. If a variable does not - * yet exist, the operation will create it. If an existing variable exists with - * a mismatching type, the operation will fail. - * - * Accessing variables after progressing the iterator or after the iterator is - * destroyed will result in undefined behavior. - * - * If vars contains a variable that is not present in the iterator, the variable - * will not be modified. - * - * @param it The iterator to convert to variables. - * @param vars The variables to write to. - * @param offset The offset to the current element. - */ -FLECS_API -void ecs_iter_to_vars( - const ecs_iter_t *it, - ecs_vars_t *vars, - int offset); - -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif - -#endif diff --git a/vendors/flecs/include/flecs/addons/flecs_c.h b/vendors/flecs/include/flecs/addons/flecs_c.h index 1b439c47d..dab550dc2 100644 --- a/vendors/flecs/include/flecs/addons/flecs_c.h +++ b/vendors/flecs/include/flecs/addons/flecs_c.h @@ -8,15 +8,16 @@ /** * @defgroup flecs_c Macro API - * @brief Convenience macro's for C API - * - * \ingroup c + * @ingroup c + * Convenience macro's for C API + * * @{ */ /** * @defgroup flecs_c_creation Creation macro's - * @brief Convenience macro's for creating entities, components and observers + * Convenience macro's for creating entities, components and observers + * * @{ */ @@ -28,9 +29,12 @@ #define ECS_ENTITY_DECLARE ECS_DECLARE /** Define a forward declared entity. - * + * * Example: - * ECS_ENTITY_DEFINE(world, MyEntity, Position, Velocity); + * + * @code + * ECS_ENTITY_DEFINE(world, MyEntity, Position, Velocity); + * @endcode */ #define ECS_ENTITY_DEFINE(world, id_, ...) \ { \ @@ -40,7 +44,7 @@ desc.add_expr = #__VA_ARGS__; \ id_ = ecs_entity_init(world, &desc); \ ecs_id(id_) = id_; \ - ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, NULL); \ + ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, "failed to create entity %s", #id_); \ } \ (void)id_; \ (void)ecs_id(id_) @@ -48,7 +52,10 @@ /** Declare & define an entity. * * Example: - * ECS_ENTITY(world, MyEntity, Position, Velocity); + * + * @code + * ECS_ENTITY(world, MyEntity, Position, Velocity); + * @endcode */ #define ECS_ENTITY(world, id, ...) \ ecs_entity_t ecs_id(id); \ @@ -59,16 +66,22 @@ #define ECS_TAG_DECLARE ECS_DECLARE /** Define a forward declared tag. - * + * * Example: - * ECS_TAG_DEFINE(world, MyTag); + * + * @code + * ECS_TAG_DEFINE(world, MyTag); + * @endcode */ #define ECS_TAG_DEFINE(world, id) ECS_ENTITY_DEFINE(world, id, 0) /** Declare & define a tag. * * Example: - * ECS_TAG(world, MyTag); + * + * @code + * ECS_TAG(world, MyTag); + * @endcode */ #define ECS_TAG(world, id) ECS_ENTITY(world, id, 0) @@ -76,16 +89,22 @@ #define ECS_PREFAB_DECLARE ECS_DECLARE /** Define a forward declared prefab. - * + * * Example: - * ECS_PREFAB_DEFINE(world, MyPrefab, Position, Velocity); + * + * @code + * ECS_PREFAB_DEFINE(world, MyPrefab, Position, Velocity); + * @endcode */ #define ECS_PREFAB_DEFINE(world, id, ...) ECS_ENTITY_DEFINE(world, id, Prefab, __VA_ARGS__) /** Declare & define a prefab. * * Example: - * ECS_PREFAB(world, MyPrefab, Position, Velocity); + * + * @code + * ECS_PREFAB(world, MyPrefab, Position, Velocity); + * @endcode */ #define ECS_PREFAB(world, id, ...) ECS_ENTITY(world, id, Prefab, __VA_ARGS__) @@ -93,9 +112,12 @@ #define ECS_COMPONENT_DECLARE(id) ecs_entity_t ecs_id(id) /** Define a forward declared component. - * + * * Example: - * ECS_COMPONENT_DEFINE(world, Position); + * + * @code + * ECS_COMPONENT_DEFINE(world, Position); + * @endcode */ #define ECS_COMPONENT_DEFINE(world, id_) \ {\ @@ -110,12 +132,15 @@ desc.type.alignment = ECS_ALIGNOF(id_); \ ecs_id(id_) = ecs_component_init(world, &desc);\ }\ - ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, NULL) + ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, "failed to create component %s", #id_) /** Declare & define a component. * * Example: - * ECS_COMPONENT(world, Position); + * + * @code + * ECS_COMPONENT(world, Position); + * @endcode */ #define ECS_COMPONENT(world, id)\ ecs_entity_t ecs_id(id) = 0;\ @@ -126,9 +151,12 @@ #define ECS_OBSERVER_DECLARE(id) ecs_entity_t ecs_id(id) /** Define a forward declared observer. - * + * * Example: - * ECS_OBSERVER_DEFINE(world, AddPosition, EcsOnAdd, Position); + * + * @code + * ECS_OBSERVER_DEFINE(world, AddPosition, EcsOnAdd, Position); + * @endcode */ #define ECS_OBSERVER_DEFINE(world, id_, kind, ...)\ {\ @@ -138,16 +166,19 @@ edesc.name = #id_; \ desc.entity = ecs_entity_init(world, &edesc); \ desc.callback = id_;\ - desc.filter.expr = #__VA_ARGS__;\ + desc.query.expr = #__VA_ARGS__;\ desc.events[0] = kind;\ ecs_id(id_) = ecs_observer_init(world, &desc);\ - ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, NULL);\ + ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, "failed to create observer %s", #id_);\ } /** Declare & define an observer. * * Example: - * ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); + * + * @code + * ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); + * @endcode */ #define ECS_OBSERVER(world, id, kind, ...)\ ecs_entity_t ecs_id(id) = 0; \ @@ -156,23 +187,64 @@ (void)ecs_id(id);\ (void)id -/** Shorthand for creating an entity with ecs_entity_init. +/* Forward declare a query. */ +#define ECS_QUERY_DECLARE(name) ecs_query_t* name + +/** Define a forward declared observer. * * Example: - * ecs_entity(world, { - * .name = "MyEntity" - * }); + * + * @code + * ECS_QUERY_DEFINE(world, AddPosition, Position); + * @endcode + */ +#define ECS_QUERY_DEFINE(world, name_, ...)\ + {\ + ecs_query_desc_t desc = {0};\ + ecs_entity_desc_t edesc = {0}; \ + edesc.name = #name_; \ + desc.entity = ecs_entity_init(world, &edesc); \ + desc.expr = #__VA_ARGS__;\ + name_ = ecs_query_init(world, &desc);\ + ecs_assert(name_ != NULL, ECS_INVALID_PARAMETER, "failed to create query %s", #name_);\ + } + +/** Declare & define an observer. + * + * Example: + * + * @code + * ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); + * @endcode + */ +#define ECS_QUERY(world, name, ...)\ + ecs_query_t* name = NULL; \ + ECS_QUERY_DEFINE(world, name, __VA_ARGS__);\ + (void)name + +/** Shorthand for creating an entity with ecs_entity_init(). + * + * Example: + * + * @code + * ecs_entity(world, { + * .name = "MyEntity" + * }); + * @endcode */ #define ecs_entity(world, ...)\ ecs_entity_init(world, &(ecs_entity_desc_t) __VA_ARGS__ ) -/** Shorthand for creating a component with ecs_component_init. +/** Shorthand for creating a component with ecs_component_init(). * * Example: - * ecs_component(world, { - * .type.size = 4, - * .type.alignment = 4 - * }); + * + * @code + * ecs_component(world, { + * .type.size = 4, + * .type.alignment = 4 + * }); + * @endcode */ #define ecs_component(world, ...)\ ecs_component_init(world, &(ecs_component_desc_t) __VA_ARGS__ ) @@ -180,7 +252,10 @@ /** Shorthand for creating a component from a type. * * Example: - * ecs_component_t(world, Position); + * + * @code + * ecs_component_t(world, Position); + * @endcode */ #define ecs_component_t(world, T)\ ecs_component_init(world, &(ecs_component_desc_t) { \ @@ -193,34 +268,27 @@ .type.alignment = ECS_ALIGNOF(T) \ }) -/** Shorthand for creating a filter with ecs_filter_init. - * - * Example: - * ecs_filter(world, { - * .terms = {{ ecs_id(Position) }} - * }); - */ -#define ecs_filter(world, ...)\ - ecs_filter_init(world, &(ecs_filter_desc_t) __VA_ARGS__ ) - -/** Shorthand for creating a query with ecs_query_init. +/** Shorthand for creating a query with ecs_query_cache_init. * * Example: * ecs_query(world, { - * .filter.terms = {{ ecs_id(Position) }} + * .terms = {{ ecs_id(Position) }} * }); */ #define ecs_query(world, ...)\ ecs_query_init(world, &(ecs_query_desc_t) __VA_ARGS__ ) -/** Shorthand for creating an observer with ecs_observer_init. +/** Shorthand for creating an observer with ecs_observer_init(). * * Example: - * ecs_observer(world, { - * .filter.terms = {{ ecs_id(Position) }}, - * .events = { EcsOnAdd }, - * .callback = AddPosition - * }); + * + * @code + * ecs_observer(world, { + * .terms = {{ ecs_id(Position) }}, + * .events = { EcsOnAdd }, + * .callback = AddPosition + * }); + * @endcode */ #define ecs_observer(world, ...)\ ecs_observer_init(world, &(ecs_observer_desc_t) __VA_ARGS__ ) @@ -229,7 +297,8 @@ /** * @defgroup flecs_c_type_safe Type Safe API - * @brief Macro's that wrap around core functions to provide a "type safe" API in C + * Macro's that wrap around core functions to provide a "type safe" API in C + * * @{ */ @@ -243,7 +312,7 @@ * @{ */ -#define ecs_new(world, T) ecs_new_w_id(world, ecs_id(T)) +#define ecs_new_w(world, T) ecs_new_w_id(world, ecs_id(T)) #define ecs_new_w_pair(world, first, second)\ ecs_new_w_id(world, ecs_pair(first, second)) @@ -251,20 +320,6 @@ #define ecs_bulk_new(world, component, count)\ ecs_bulk_new_w_id(world, ecs_id(component), count) -#define ecs_new_entity(world, n)\ - ecs_entity_init(world, &(ecs_entity_desc_t){\ - .name = n,\ - }) - -#define ecs_new_prefab(world, n)\ - ecs_entity_init(world, &(ecs_entity_desc_t){\ - .name = n,\ - .add = {EcsPrefab}\ - }) - -#define ecs_delete_children(world, parent)\ - ecs_delete_with(world, ecs_pair(EcsChildOf, parent)) - /** @} */ /** @@ -286,11 +341,11 @@ ecs_remove_id(world, subject, ecs_pair(first, second)) -#define ecs_override(world, entity, T)\ - ecs_override_id(world, entity, ecs_id(T)) +#define ecs_auto_override(world, entity, T)\ + ecs_auto_override_id(world, entity, ecs_id(T)) -#define ecs_override_pair(world, subject, first, second)\ - ecs_override_id(world, subject, ecs_pair(first, second)) +#define ecs_auto_override_pair(world, subject, first, second)\ + ecs_auto_override_id(world, subject, ecs_pair(first, second)) /** @} */ @@ -299,6 +354,12 @@ * @{ */ +/* insert */ +#define ecs_insert(world, ...)\ + ecs_entity(world, { .set = ecs_values(__VA_ARGS__)}) + +/* set */ + #define ecs_set_ptr(world, entity, component, ptr)\ ecs_set_id(world, entity, ecs_id(component), sizeof(component), ptr) @@ -315,17 +376,19 @@ ecs_pair(first, ecs_id(Second)),\ sizeof(Second), &(Second)__VA_ARGS__) -#define ecs_set_pair_object ecs_set_pair_second - #define ecs_set_override(world, entity, T, ...)\ - ecs_add_id(world, entity, ECS_OVERRIDE | ecs_id(T));\ + ecs_add_id(world, entity, ECS_AUTO_OVERRIDE | ecs_id(T));\ ecs_set(world, entity, T, __VA_ARGS__) -#define ecs_emplace(world, entity, T)\ - (ECS_CAST(T*, ecs_emplace_id(world, entity, ecs_id(T)))) +/* emplace */ + +#define ecs_emplace(world, entity, T, is_new)\ + (ECS_CAST(T*, ecs_emplace_id(world, entity, ecs_id(T), is_new))) -#define ecs_emplace_pair(world, entity, First, second)\ - (ECS_CAST(First*, ecs_emplace_id(world, entity, ecs_pair_t(First, second)))) +#define ecs_emplace_pair(world, entity, First, second, is_new)\ + (ECS_CAST(First*, ecs_emplace_id(world, entity, ecs_pair_t(First, second), is_new))) + +/* get */ #define ecs_get(world, entity, T)\ (ECS_CAST(const T*, ecs_get_id(world, entity, ecs_id(T)))) @@ -338,7 +401,55 @@ (ECS_CAST(const Second*, ecs_get_id(world, subject,\ ecs_pair(first, ecs_id(Second))))) -#define ecs_get_pair_object ecs_get_pair_second +/* get_mut */ + +#define ecs_get_mut(world, entity, T)\ + (ECS_CAST(T*, ecs_get_mut_id(world, entity, ecs_id(T)))) + +#define ecs_get_mut_pair(world, subject, First, second)\ + (ECS_CAST(First*, ecs_get_mut_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_get_mut_pair_second(world, subject, first, Second)\ + (ECS_CAST(Second*, ecs_get_mut_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +#define ecs_get_mut(world, entity, T)\ + (ECS_CAST(T*, ecs_get_mut_id(world, entity, ecs_id(T)))) + +/* ensure */ + +#define ecs_ensure(world, entity, T)\ + (ECS_CAST(T*, ecs_ensure_id(world, entity, ecs_id(T)))) + +#define ecs_ensure_pair(world, subject, First, second)\ + (ECS_CAST(First*, ecs_ensure_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_ensure_pair_second(world, subject, first, Second)\ + (ECS_CAST(Second*, ecs_ensure_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +#define ecs_ensure(world, entity, T)\ + (ECS_CAST(T*, ecs_ensure_id(world, entity, ecs_id(T)))) + +#define ecs_ensure_pair(world, subject, First, second)\ + (ECS_CAST(First*, ecs_ensure_id(world, subject,\ + ecs_pair(ecs_id(First), second)))) + +#define ecs_ensure_pair_second(world, subject, first, Second)\ + (ECS_CAST(Second*, ecs_ensure_id(world, subject,\ + ecs_pair(first, ecs_id(Second))))) + +/* modified */ + +#define ecs_modified(world, entity, component)\ + ecs_modified_id(world, entity, ecs_id(component)) + +#define ecs_modified_pair(world, subject, first, second)\ + ecs_modified_id(world, subject, ecs_pair(first, second)) + +/* record */ #define ecs_record_get(world, record, T)\ (ECS_CAST(const T*, ecs_record_get_id(world, record, ecs_id(T)))) @@ -354,43 +465,22 @@ (ECS_CAST(const Second*, ecs_record_get_id(world, record,\ ecs_pair(first, ecs_id(Second))))) -#define ecs_record_get_mut(world, record, T)\ - (ECS_CAST(T*, ecs_record_get_mut_id(world, record, ecs_id(T)))) +#define ecs_record_ensure(world, record, T)\ + (ECS_CAST(T*, ecs_record_ensure_id(world, record, ecs_id(T)))) -#define ecs_record_get_mut_pair(world, record, First, second)\ - (ECS_CAST(First*, ecs_record_get_mut_id(world, record, \ +#define ecs_record_ensure_pair(world, record, First, second)\ + (ECS_CAST(First*, ecs_record_ensure_id(world, record, \ ecs_pair(ecs_id(First), second)))) -#define ecs_record_get_mut_pair_second(world, record, first, Second)\ - (ECS_CAST(Second*, ecs_record_get_mut_id(world, record,\ +#define ecs_record_ensure_pair_second(world, record, first, Second)\ + (ECS_CAST(Second*, ecs_record_ensure_id(world, record,\ ecs_pair(first, ecs_id(Second))))) -#define ecs_record_get_mut_pair_object ecs_record_get_mut_pair_second - #define ecs_ref_init(world, entity, T)\ ecs_ref_init_id(world, entity, ecs_id(T)) #define ecs_ref_get(world, ref, T)\ - (ECS_CAST(const T*, ecs_ref_get_id(world, ref, ecs_id(T)))) - -#define ecs_get_mut(world, entity, T)\ - (ECS_CAST(T*, ecs_get_mut_id(world, entity, ecs_id(T)))) - -#define ecs_get_mut_pair(world, subject, First, second)\ - (ECS_CAST(First*, ecs_get_mut_id(world, subject,\ - ecs_pair(ecs_id(First), second)))) - -#define ecs_get_mut_pair_second(world, subject, first, Second)\ - (ECS_CAST(Second*, ecs_get_mut_id(world, subject,\ - ecs_pair(first, ecs_id(Second))))) - -#define ecs_get_mut_pair_object ecs_get_mut_pair_second - -#define ecs_modified(world, entity, component)\ - ecs_modified_id(world, entity, ecs_id(component)) - -#define ecs_modified_pair(world, subject, first, second)\ - ecs_modified_id(world, subject, ecs_pair(first, second)) + (ECS_CAST(T*, ecs_ref_get_id(world, ref, ecs_id(T)))) /** @} */ @@ -414,8 +504,8 @@ #define ecs_singleton_set(world, comp, ...)\ ecs_set(world, ecs_id(comp), comp, __VA_ARGS__) -#define ecs_singleton_get_mut(world, comp)\ - ecs_get_mut(world, ecs_id(comp), comp) +#define ecs_singleton_ensure(world, comp)\ + ecs_ensure(world, ecs_id(comp), comp) #define ecs_singleton_modified(world, comp)\ ecs_modified(world, ecs_id(comp), comp) @@ -449,6 +539,9 @@ #define ecs_shares(world, entity, T)\ (ecs_shares_id(world, entity, ecs_id(T))) +#define ecs_get_target_for(world, entity, rel, T)\ + ecs_get_target_for_id(world, entity, rel, ecs_id(T)) + /** @} */ /** @@ -459,7 +552,7 @@ #define ecs_enable_component(world, entity, T, enable)\ ecs_enable_id(world, entity, ecs_id(T), enable) -#define ecs_is_enabled_component(world, entity, T)\ +#define ecs_is_enabled(world, entity, T)\ ecs_is_enabled_id(world, entity, ecs_id(T)) #define ecs_enable_pair(world, entity, First, second, enable)\ @@ -475,27 +568,21 @@ * @{ */ -#define ecs_lookup_path(world, parent, path)\ +#define ecs_lookup_from(world, parent, path)\ ecs_lookup_path_w_sep(world, parent, path, ".", NULL, true) -#define ecs_lookup_fullpath(world, path)\ - ecs_lookup_path_w_sep(world, 0, path, ".", NULL, true) - -#define ecs_get_path(world, parent, child)\ +#define ecs_get_path_from(world, parent, child)\ ecs_get_path_w_sep(world, parent, child, ".", NULL) -#define ecs_get_fullpath(world, child)\ +#define ecs_get_path(world, child)\ ecs_get_path_w_sep(world, 0, child, ".", NULL) -#define ecs_get_fullpath_buf(world, child, buf)\ - ecs_get_path_w_sep_buf(world, 0, child, ".", NULL, buf) +#define ecs_get_path_buf(world, child, buf)\ + ecs_get_path_w_sep_buf(world, 0, child, ".", NULL, buf, false) #define ecs_new_from_path(world, parent, path)\ ecs_new_from_path_w_sep(world, parent, path, ".", NULL) -#define ecs_new_from_fullpath(world, path)\ - ecs_new_from_path_w_sep(world, 0, path, ".", NULL) - #define ecs_add_path(world, entity, parent, path)\ ecs_add_path_w_sep(world, entity, parent, path, ".", NULL) @@ -519,35 +606,50 @@ /** Declare a constructor. * Example: - * ECS_CTOR(MyType, ptr, { ptr->value = NULL; }); + * + * @code + * ECS_CTOR(MyType, ptr, { ptr->value = NULL; }); + * @endcode */ #define ECS_CTOR(type, var, ...)\ ECS_XTOR_IMPL(type, ctor, var, __VA_ARGS__) /** Declare a destructor. * Example: - * ECS_DTOR(MyType, ptr, { free(ptr->value); }); + * + * @code + * ECS_DTOR(MyType, ptr, { free(ptr->value); }); + * @endcode */ #define ECS_DTOR(type, var, ...)\ ECS_XTOR_IMPL(type, dtor, var, __VA_ARGS__) /** Declare a copy action. * Example: - * ECS_COPY(MyType, dst, src, { dst->value = strdup(src->value); }); + * + * @code + * ECS_COPY(MyType, dst, src, { dst->value = strdup(src->value); }); + * @endcode */ #define ECS_COPY(type, dst_var, src_var, ...)\ ECS_COPY_IMPL(type, dst_var, src_var, __VA_ARGS__) /** Declare a move action. * Example: - * ECS_MOVE(MyType, dst, src, { dst->value = src->value; src->value = 0; }); + * + * @code + * ECS_MOVE(MyType, dst, src, { dst->value = src->value; src->value = 0; }); + * @endcode */ #define ECS_MOVE(type, dst_var, src_var, ...)\ ECS_MOVE_IMPL(type, dst_var, src_var, __VA_ARGS__) /** Declare component hooks. * Example: - * ECS_ON_SET(MyType, ptr, { printf("%d\n", ptr->value); }); + * + * @code + * ECS_ON_SET(MyType, ptr, { printf("%d\n", ptr->value); }); + * @endcode */ #define ECS_ON_ADD(type, ptr, ...)\ ECS_HOOK_IMPL(type, ecs_on_add(type), ptr, __VA_ARGS__) @@ -585,6 +687,12 @@ #define ecs_field(it, T, index)\ (ECS_CAST(T*, ecs_field_w_size(it, sizeof(T), index))) +#define ecs_field_self(it, T, index)\ + (ECS_CAST(T*, ecs_field_self_w_size(it, sizeof(T), index))) + +#define ecs_field_at(it, T, index, row)\ + (ECS_CAST(T*, ecs_field_at_w_size(it, sizeof(T), index, row))) + /** @} */ /** @@ -608,17 +716,35 @@ * @{ */ -#define ecs_value(T, ptr) ((ecs_value_t){ecs_id(T), ptr}) +/** Convenience macro for creating compound literal id array */ +#define ecs_ids(...) (ecs_id_t[]){ __VA_ARGS__, 0 } + +/** Convenience macro for creating compound literal values array */ +#define ecs_values(...) (ecs_value_t[]){ __VA_ARGS__, {0, 0}} + +/** Convenience macro for creating compound literal value */ +#define ecs_value_ptr(T, ptr) ((ecs_value_t){ecs_id(T), ptr}) + +/** Convenience macro for creating compound literal pair value */ +#define ecs_value_pair(R, t, ...) ((ecs_value_t){ecs_pair_t(R, t), &(R)__VA_ARGS__}) + +/** Convenience macro for creating compound literal pair value */ +#define ecs_value_pair_2nd(r, T, ...) ((ecs_value_t){ecs_pair(r, ecs_id(T)), &(T)__VA_ARGS__}) + +/** Convenience macro for creating heap allocated value */ #define ecs_value_new_t(world, T) ecs_value_new(world, ecs_id(T)) +/** Convenience macro for creating compound literal value literal */ +#define ecs_value(T, ...) ((ecs_value_t){ecs_id(T), &(T)__VA_ARGS__}) + /** @} */ /** @} */ /** * @defgroup flecs_c_table_sorting Table sorting - * @brief Convenience macro's for sorting tables. - * + * Convenience macro's for sorting tables. + * * @{ */ #define ecs_sort_table(id) ecs_id(id##_sort_table) @@ -642,8 +768,11 @@ * int32_t hi * ecs_order_by_action_t order_by - Pointer to the original comparison function. You are not supposed to use it. * Example: - * int CompareMyType(ecs_entity_t e1, const void* ptr1, ecs_entity_t e2, const void* ptr2) { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; } - * ECS_SORT_TABLE_WITH_COMPARE(MyType, MyCustomCompare, CompareMyType) + * + * @code + * int CompareMyType(ecs_entity_t e1, const void* ptr1, ecs_entity_t e2, const void* ptr2) { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; } + * ECS_SORT_TABLE_WITH_COMPARE(MyType, MyCustomCompare, CompareMyType) + * @endcode */ #define ECS_SORT_TABLE_WITH_COMPARE(id, op_name, compare_fn, ...) \ static int32_t ECS_CONCAT(op_name, _partition)( \ @@ -709,8 +838,11 @@ * Variadic arguments are prepended before generated functions, use it to declare static * or exported functions. * Example: - * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); - * ECS_SORT_TABLE(MyType) + * + * @code + * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); + * ECS_SORT_TABLE(MyType) + * @endcode */ #define ECS_SORT_TABLE(id, ...) \ ECS_SORT_TABLE_WITH_COMPARE(id, ecs_sort_table(id), ecs_compare(id), __VA_ARGS__) @@ -720,7 +852,10 @@ * ecs_entity_t e1, const void* ptr1, * ecs_entity_t e2, const void* ptr2 * Example: - * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); + * + * @code + * ECS_COMPARE(MyType, { const MyType* p1 = ptr1; const MyType* p2 = ptr2; return p1->value - p2->value; }); + * @endcode */ #define ECS_COMPARE(id, ...) \ int ecs_compare(id)(ecs_entity_t e1, const void* ptr1, ecs_entity_t e2, const void* ptr2) { \ @@ -731,24 +866,19 @@ /** * @defgroup flecs_c_misc Misc - * @brief Misc convenience macro's. - * + * Misc convenience macro's. + * * @{ */ #define ecs_isa(e) ecs_pair(EcsIsA, e) #define ecs_childof(e) ecs_pair(EcsChildOf, e) #define ecs_dependson(e) ecs_pair(EcsDependsOn, e) +#define ecs_with(e) ecs_pair(EcsWith, e) -#define ecs_query_new(world, q_expr)\ - ecs_query_init(world, &(ecs_query_desc_t){\ - .filter.expr = q_expr\ - }) - -#define ecs_rule_new(world, q_expr)\ - ecs_rule_init(world, &(ecs_filter_desc_t){\ - .expr = q_expr\ - }) +#define ecs_each(world, id) ecs_each_id(world, ecs_id(id)) +#define ecs_each_pair(world, r, t) ecs_each_id(world, ecs_pair(r, t)) +#define ecs_each_pair_t(world, R, t) ecs_each_id(world, ecs_pair(ecs_id(R), t)) /** @} */ diff --git a/vendors/flecs/include/flecs/addons/http.h b/vendors/flecs/include/flecs/addons/http.h index 74706da8e..b45f3adfc 100644 --- a/vendors/flecs/include/flecs/addons/http.h +++ b/vendors/flecs/include/flecs/addons/http.h @@ -1,19 +1,19 @@ /** * @file addons/http.h * @brief HTTP addon. - * + * * Minimalistic HTTP server that can receive and reply to simple HTTP requests. * The main goal of this addon is to enable remotely connecting to a running * Flecs application (for example, with a web-based UI) and request/visualize * data from the ECS world. - * + * * Each server instance creates a single thread used for receiving requests. * Receiving requests are enqueued and handled when the application calls - * ecs_http_server_dequeue. This increases latency of request handling vs. - * responding directly in the receive thread, but is better suited for + * ecs_http_server_dequeue(). This increases latency of request handling vs. + * responding directly in the receive thread, but is better suited for * retrieving data from ECS applications, as requests can be processed by an ECS * system without having to lock the world. - * + * * This server is intended to be used in a development environment. */ @@ -21,9 +21,9 @@ /** * @defgroup c_addons_http Http - * @brief Simple HTTP server used for serving up REST API. - * - * \ingroup c_addons + * @ingroup c_addons + * Simple HTTP server used for serving up REST API. + * * @{ */ @@ -34,20 +34,20 @@ #ifndef FLECS_HTTP_H #define FLECS_HTTP_H -/* Maximum number of headers in request */ +/** Maximum number of headers in request. */ #define ECS_HTTP_HEADER_COUNT_MAX (32) -/* Maximum number of query parameters in request */ +/** Maximum number of query parameters in request. */ #define ECS_HTTP_QUERY_PARAM_COUNT_MAX (32) #ifdef __cplusplus extern "C" { #endif -/** HTTP server */ +/** HTTP server. */ typedef struct ecs_http_server_t ecs_http_server_t; -/** A connection manages communication with the remote host */ +/** A connection manages communication with the remote host. */ typedef struct { uint64_t id; ecs_http_server_t *server; @@ -56,13 +56,13 @@ typedef struct { char port[16]; } ecs_http_connection_t; -/** Helper type used for headers & URL query parameters */ +/** Helper type used for headers & URL query parameters. */ typedef struct { const char *key; const char *value; } ecs_http_key_value_t; -/** Supported request methods */ +/** Supported request methods. */ typedef enum { EcsHttpGet, EcsHttpPost, @@ -72,7 +72,7 @@ typedef enum { EcsHttpMethodUnsupported } ecs_http_method_t; -/** A request */ +/** An HTTP request. */ typedef struct { uint64_t id; @@ -87,7 +87,7 @@ typedef struct { ecs_http_connection_t *conn; } ecs_http_request_t; -/** A reply */ +/** An HTTP reply. */ typedef struct { int code; /**< default = 200 */ ecs_strbuf_t body; /**< default = "" */ @@ -99,38 +99,40 @@ typedef struct { #define ECS_HTTP_REPLY_INIT \ (ecs_http_reply_t){200, ECS_STRBUF_INIT, "OK", "application/json", ECS_STRBUF_INIT} -/* Global statistics. */ -extern int64_t ecs_http_request_received_count; -extern int64_t ecs_http_request_invalid_count; -extern int64_t ecs_http_request_handled_ok_count; -extern int64_t ecs_http_request_handled_error_count; -extern int64_t ecs_http_request_not_handled_count; -extern int64_t ecs_http_request_preflight_count; -extern int64_t ecs_http_send_ok_count; -extern int64_t ecs_http_send_error_count; -extern int64_t ecs_http_busy_count; +/* Global HTTP statistics. */ +extern int64_t ecs_http_request_received_count; /**< Total number of HTTP requests received. */ +extern int64_t ecs_http_request_invalid_count; /**< Total number of invalid HTTP requests. */ +extern int64_t ecs_http_request_handled_ok_count; /**< Total number of successful HTTP requests. */ +extern int64_t ecs_http_request_handled_error_count; /**< Total number of HTTP requests with errors. */ +extern int64_t ecs_http_request_not_handled_count; /**< Total number of HTTP requests with an unknown endpoint. */ +extern int64_t ecs_http_request_preflight_count; /**< Total number of preflight HTTP requests received. */ +extern int64_t ecs_http_send_ok_count; /**< Total number of HTTP replies successfully sent. */ +extern int64_t ecs_http_send_error_count; /**< Total number of HTTP replies that failed to send. */ +extern int64_t ecs_http_busy_count; /**< Total number of HTTP busy replies. */ /** Request callback. * Invoked for each valid request. The function should populate the reply and - * return true. When the function returns false, the server will reply with a + * return true. When the function returns false, the server will reply with a * 404 (Not found) code. */ typedef bool (*ecs_http_reply_action_t)( - const ecs_http_request_t* request, + const ecs_http_request_t* request, ecs_http_reply_t *reply, void *ctx); -/** Used with ecs_http_server_init. */ +/** Used with ecs_http_server_init(). */ typedef struct { ecs_http_reply_action_t callback; /**< Function called for each request */ void *ctx; /**< Passed to callback (optional) */ uint16_t port; /**< HTTP port */ const char *ipaddr; /**< Interface to listen on (optional) */ int32_t send_queue_wait_ms; /**< Send queue wait time when empty */ + double cache_timeout; /**< Cache invalidation timeout (0 disables caching) */ + double cache_purge_timeout; /**< Cache purge timeout (for purging cache entries) */ } ecs_http_server_desc_t; -/** Create server. - * Use ecs_http_server_start to start receiving requests. - * +/** Create server. + * Use ecs_http_server_start() to start receiving requests. + * * @param desc Server configuration parameters. * @return The new server, or NULL if creation failed. */ @@ -138,18 +140,18 @@ FLECS_API ecs_http_server_t* ecs_http_server_init( const ecs_http_server_desc_t *desc); -/** Destroy server. +/** Destroy server. * This operation will stop the server if it was still running. - * + * * @param server The server to destroy. */ FLECS_API void ecs_http_server_fini( ecs_http_server_t* server); -/** Start server. +/** Start server. * After this operation the server will be able to accept requests. - * + * * @param server The server to start. * @return Zero if successful, non-zero if failed. */ @@ -157,10 +159,10 @@ FLECS_API int ecs_http_server_start( ecs_http_server_t* server); -/** Process server requests. +/** Process server requests. * This operation invokes the reply callback for each received request. No new * requests will be enqueued while processing requests. - * + * * @param server The server for which to process requests. */ FLECS_API @@ -168,9 +170,9 @@ void ecs_http_server_dequeue( ecs_http_server_t* server, ecs_ftime_t delta_time); -/** Stop server. +/** Stop server. * After this operation no new requests can be received. - * + * * @param server The server. */ FLECS_API @@ -179,7 +181,8 @@ void ecs_http_server_stop( /** Emulate a request. * The request string must be a valid HTTP request. A minimal example: - * GET /entity/flecs/core/World?label=true HTTP/1.1 + * + * GET /entity/flecs/core/World?label=true HTTP/1.1 * * @param srv The server. * @param req The request. @@ -193,7 +196,7 @@ int ecs_http_server_http_request( ecs_size_t len, ecs_http_reply_t *reply_out); -/** Convenience wrapper around ecs_http_server_request. */ +/** Convenience wrapper around ecs_http_server_http_request(). */ FLECS_API int ecs_http_server_request( ecs_http_server_t* srv, @@ -206,8 +209,8 @@ FLECS_API void* ecs_http_server_ctx( ecs_http_server_t* srv); -/** Find header in request. - * +/** Find header in request. + * * @param req The request. * @param name name of the header to find * @return The header value, or NULL if not found. @@ -217,8 +220,8 @@ const char* ecs_http_get_header( const ecs_http_request_t* req, const char* name); -/** Find query parameter in request. - * +/** Find query parameter in request. + * * @param req The request. * @param name The parameter name. * @return The decoded parameter value, or NULL if not found. diff --git a/vendors/flecs/include/flecs/addons/json.h b/vendors/flecs/include/flecs/addons/json.h index c561c8480..7bc184f5f 100644 --- a/vendors/flecs/include/flecs/addons/json.h +++ b/vendors/flecs/include/flecs/addons/json.h @@ -2,16 +2,20 @@ * @file addons/json.h * @brief JSON parser addon. * - * Parse expression strings into component values. Entity identifiers, + * Parse expression strings into component values. Entity identifiers, * enumerations and bitmasks are encoded as strings. - * - * See docs/JsonFormat.md for a description of the JSON format. + * + * See docs/FlecsRemoteApi.md for a description of the JSON format. */ #ifdef FLECS_JSON -#ifndef FLECS_EXPR -#define FLECS_EXPR +#ifndef FLECS_META +#define FLECS_META +#endif + +#ifndef FLECS_SCRIPT +#define FLECS_SCRIPT #endif #ifndef FLECS_JSON_H @@ -19,9 +23,9 @@ /** * @defgroup c_addons_json Json - * @brief Functions for serializing to/from JSON. - * - * \ingroup c_addons + * @ingroup c_addons + * Functions for serializing to/from JSON. + * * @{ */ @@ -29,24 +33,28 @@ extern "C" { #endif -/** Used with ecs_ptr_from_json, ecs_entity_from_json. */ +/** Used with ecs_ptr_from_json(), ecs_entity_from_json(). */ typedef struct ecs_from_json_desc_t { const char *name; /**< Name of expression (used for logging) */ const char *expr; /**< Full expression (used for logging) */ - /** Callback that allows for specifying a custom lookup function. The - * default behavior uses ecs_lookup_fullpath */ + /** Callback that allows for specifying a custom lookup function. The + * default behavior uses ecs_lookup() */ ecs_entity_t (*lookup_action)( - const ecs_world_t*, - const char *value, + const ecs_world_t*, + const char *value, void *ctx); void *lookup_ctx; + + /** Require components to be registered with reflection data. When not + * in strict mode, values for components without reflection are ignored. */ + bool strict; } ecs_from_json_desc_t; /** Parse JSON string into value. * This operation parses a JSON expression into the provided pointer. The * memory pointed to must be large enough to contain a value of the used type. - * + * * @param world The world. * @param type The type of the expression to parse. * @param ptr Pointer to the memory to write to. @@ -63,9 +71,9 @@ const char* ecs_ptr_from_json( const ecs_from_json_desc_t *desc); /** Parse JSON object with multiple component values into entity. The format - * is the same as the one outputted by ecs_entity_to_json, but at the moment - * only supports the "ids" and "values" member. - * + * is the same as the one outputted by ecs_entity_to_json(), but at the moment + * only supports the "ids" and "values" member. + * * @param world The world. * @param entity The entity to serialize to. * @param json The JSON expression to parse (see entity in JSON format manual). @@ -80,10 +88,12 @@ const char* ecs_entity_from_json( const ecs_from_json_desc_t *desc); /** Parse JSON object with multiple entities into the world. The format is the - * same as the one outputted by ecs_world_to_json. - * + * same as the one outputted by ecs_world_to_json(). + * * @param world The world. * @param json The JSON expression to parse (see iterator in JSON format manual). + * @param desc Deserialization parameters. + * @return Last deserialized character, NULL if failed. */ FLECS_API const char* ecs_world_from_json( @@ -91,14 +101,27 @@ const char* ecs_world_from_json( const char *json, const ecs_from_json_desc_t *desc); +/** Same as ecs_world_from_json(), but loads JSON from file. + * + * @param world The world. + * @param filename The file from which to load the JSON. + * @param desc Deserialization parameters. + * @return Last deserialized character, NULL if failed. + */ +FLECS_API +const char* ecs_world_from_json_file( + ecs_world_t *world, + const char *filename, + const ecs_from_json_desc_t *desc); + /** Serialize array into JSON string. - * This operation serializes a value of the provided type to a JSON string. The + * This operation serializes a value of the provided type to a JSON string. The * memory pointed to must be large enough to contain a value of the used type. - * + * * If count is 0, the function will serialize a single value, not wrapped in * array brackets. If count is >= 1, the operation will serialize values to a * a comma-separated list inside of array brackets. - * + * * @param world The world. * @param type The type of the value to serialize. * @param data The value to serialize. @@ -113,8 +136,8 @@ char* ecs_array_to_json( int32_t count); /** Serialize array into JSON string buffer. - * Same as ecs_array_to_json_buf, but serializes to an ecs_strbuf_t instance. - * + * Same as ecs_array_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param world The world. * @param type The type of the value to serialize. * @param data The value to serialize. @@ -131,8 +154,8 @@ int ecs_array_to_json_buf( ecs_strbuf_t *buf_out); /** Serialize value into JSON string. - * Same as ecs_array_to_json, with count = 0. - * + * Same as ecs_array_to_json(), with count = 0. + * * @param world The world. * @param type The type of the value to serialize. * @param data The value to serialize. @@ -145,8 +168,8 @@ char* ecs_ptr_to_json( const void *data); /** Serialize value into JSON string buffer. - * Same as ecs_ptr_to_json, but serializes to an ecs_strbuf_t instance. - * + * Same as ecs_ptr_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param world The world. * @param type The type of the value to serialize. * @param data The value to serialize. @@ -163,9 +186,9 @@ int ecs_ptr_to_json_buf( /** Serialize type info to JSON. * This serializes type information to JSON, and can be used to store/transmit * the structure of a (component) value. - * + * * If the provided type does not have reflection data, "0" will be returned. - * + * * @param world The world. * @param type The type to serialize to JSON. * @return A JSON string with the serialized type info, or NULL if failed. @@ -176,8 +199,8 @@ char* ecs_type_info_to_json( ecs_entity_t type); /** Serialize type info into JSON string buffer. - * Same as ecs_type_info_to_json, but serializes to an ecs_strbuf_t instance. - * + * Same as ecs_type_info_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param world The world. * @param type The type to serialize. * @param buf_out The strbuf to append the string to. @@ -189,35 +212,39 @@ int ecs_type_info_to_json_buf( ecs_entity_t type, ecs_strbuf_t *buf_out); -/** Used with ecs_iter_to_json. */ +/** Used with ecs_iter_to_json(). */ typedef struct ecs_entity_to_json_desc_t { - bool serialize_path; /**< Serialize full pathname */ - bool serialize_label; /**< Serialize doc name */ - bool serialize_brief; /**< Serialize brief doc description */ - bool serialize_link; /**< Serialize doc link (URL) */ - bool serialize_color; /**< Serialize doc color */ - bool serialize_ids; /**< Serialize (component) ids */ - bool serialize_id_labels; /**< Serialize labels of (component) ids */ - bool serialize_base; /**< Serialize base components */ - bool serialize_private; /**< Serialize private components */ - bool serialize_hidden; /**< Serialize ids hidden by override */ + bool serialize_entity_id; /**< Serialize entity id */ + bool serialize_doc; /**< Serialize doc attributes */ + bool serialize_full_paths; /**< Serialize full paths for tags, components and pairs */ + bool serialize_inherited; /**< Serialize base components */ bool serialize_values; /**< Serialize component values */ + bool serialize_builtin; /**< Serialize builtin data as components (e.g. "name", "parent") */ bool serialize_type_info; /**< Serialize type info (requires serialize_values) */ bool serialize_alerts; /**< Serialize active alerts for entity */ ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */ bool serialize_matches; /**< Serialize which queries entity matches with */ } ecs_entity_to_json_desc_t; -#define ECS_ENTITY_TO_JSON_INIT (ecs_entity_to_json_desc_t){true, false,\ - false, false, false, true, false, true, false, false, false, false, false,\ - false, false } +/** Utility used to initialize JSON entity serializer. */ +#define ECS_ENTITY_TO_JSON_INIT (ecs_entity_to_json_desc_t){\ + .serialize_doc = false, \ + .serialize_full_paths = true, \ + .serialize_inherited = false, \ + .serialize_values = true, \ + .serialize_builtin = false, \ + .serialize_type_info = false, \ + .serialize_alerts = false, \ + .serialize_refs = 0, \ + .serialize_matches = false, \ +} /** Serialize entity into JSON string. * This creates a JSON object with the entity's (path) name, which components * and tags the entity has, and the component values. - * + * * The operation may fail if the entity contains components with invalid values. - * + * * @param world The world. * @param entity The entity to serialize to JSON. * @return A JSON string with the serialized entity data, or NULL if failed. @@ -229,8 +256,8 @@ char* ecs_entity_to_json( const ecs_entity_to_json_desc_t *desc); /** Serialize entity into JSON string buffer. - * Same as ecs_entity_to_json, but serializes to an ecs_strbuf_t instance. - * + * Same as ecs_entity_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param world The world. * @param entity The entity to serialize. * @param buf_out The strbuf to append the string to. @@ -243,80 +270,75 @@ int ecs_entity_to_json_buf( ecs_strbuf_t *buf_out, const ecs_entity_to_json_desc_t *desc); -/** Used with ecs_iter_to_json. */ +/** Used with ecs_iter_to_json(). */ typedef struct ecs_iter_to_json_desc_t { - bool serialize_term_ids; /**< Serialize query term component ids */ - bool serialize_term_labels; /**< Serialize query term component id labels */ - bool serialize_ids; /**< Serialize actual (matched) component ids */ - bool serialize_id_labels; /**< Serialize actual (matched) component id labels */ - bool serialize_sources; /**< Serialize sources */ - bool serialize_variables; /**< Serialize variables */ - bool serialize_is_set; /**< Serialize is_set (for optional terms) */ + bool serialize_entity_ids; /**< Serialize entity ids */ bool serialize_values; /**< Serialize component values */ - bool serialize_private; /**< Serialize component values */ - bool serialize_entities; /**< Serialize entities (for This terms) */ - bool serialize_entity_labels; /**< Serialize doc name for entities */ - bool serialize_entity_ids; /**< Serialize numerical ids for entities */ - bool serialize_entity_names; /**< Serialize names (not paths) for entities */ - bool serialize_variable_labels; /**< Serialize doc name for variables */ - bool serialize_variable_ids; /**< Serialize numerical ids for variables */ - bool serialize_colors; /**< Serialize doc color for entities */ - bool measure_eval_duration; /**< Serialize evaluation duration */ - bool serialize_type_info; /**< Serialize type information */ + bool serialize_builtin; /**< Serialize builtin data as components (e.g. "name", "parent") */ + bool serialize_doc; /**< Serialize doc attributes */ + bool serialize_full_paths; /**< Serialize full paths for tags, components and pairs */ + bool serialize_fields; /**< Serialize field data */ + bool serialize_inherited; /**< Serialize inherited components */ bool serialize_table; /**< Serialize entire table vs. matched components */ + bool serialize_type_info; /**< Serialize type information */ + bool serialize_field_info; /**< Serialize metadata for fields returned by query */ + bool serialize_query_info; /**< Serialize query terms */ + bool serialize_query_plan; /**< Serialize query plan */ + bool serialize_query_profile; /**< Profile query performance */ + bool dont_serialize_results; /**< If true, query won't be evaluated */ + bool serialize_alerts; /**< Serialize active alerts for entity */ + ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */ + bool serialize_matches; /**< Serialize which queries entity matches with */ + ecs_poly_t *query; /**< Query object (required for serialize_query_[plan|profile]). */ } ecs_iter_to_json_desc_t; +/** Utility used to initialize JSON iterator serializer. */ #define ECS_ITER_TO_JSON_INIT (ecs_iter_to_json_desc_t){\ - .serialize_term_ids = true, \ - .serialize_term_labels = false, \ - .serialize_ids = true, \ - .serialize_id_labels = false, \ - .serialize_sources = true, \ - .serialize_variables = true, \ - .serialize_is_set = true, \ - .serialize_values = true, \ - .serialize_entities = true, \ - .serialize_entity_labels = false, \ .serialize_entity_ids = false, \ - .serialize_entity_names = false, \ - .serialize_variable_labels = false, \ - .serialize_variable_ids = false, \ - .serialize_colors = false, \ - .measure_eval_duration = false, \ + .serialize_values = true, \ + .serialize_builtin = false, \ + .serialize_doc = false, \ + .serialize_full_paths = true, \ + .serialize_fields = true, \ + .serialize_inherited = false, \ + .serialize_table = false, \ .serialize_type_info = false, \ - .serialize_table = false \ + .serialize_field_info = false, \ + .serialize_query_info = false, \ + .serialize_query_plan = false, \ + .serialize_query_profile = false, \ + .dont_serialize_results = false, \ + .serialize_alerts = false, \ + .serialize_refs = false, \ + .serialize_matches = false, \ } /** Serialize iterator into JSON string. * This operation will iterate the contents of the iterator and serialize them * to JSON. The function accepts iterators from any source. - * - * @param world The world. + * * @param iter The iterator to serialize to JSON. * @return A JSON string with the serialized iterator data, or NULL if failed. */ FLECS_API char* ecs_iter_to_json( - const ecs_world_t *world, ecs_iter_t *iter, const ecs_iter_to_json_desc_t *desc); /** Serialize iterator into JSON string buffer. - * Same as ecs_iter_to_json, but serializes to an ecs_strbuf_t instance. - * - * @param world The world. + * Same as ecs_iter_to_json(), but serializes to an ecs_strbuf_t instance. + * * @param iter The iterator to serialize. * @param buf_out The strbuf to append the string to. * @return Zero if success, non-zero if failed. */ FLECS_API int ecs_iter_to_json_buf( - const ecs_world_t *world, ecs_iter_t *iter, ecs_strbuf_t *buf_out, const ecs_iter_to_json_desc_t *desc); -/** Used with ecs_iter_to_json. */ +/** Used with ecs_iter_to_json(). */ typedef struct ecs_world_to_json_desc_t { bool serialize_builtin; /**< Exclude flecs modules & contents */ bool serialize_modules; /**< Exclude modules & contents */ @@ -325,15 +347,17 @@ typedef struct ecs_world_to_json_desc_t { /** 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, { + * + * @code + * ecs_query_t *f = ecs_query(world, { * .terms = {{ .id = EcsAny }} * }); - * - * ecs_iter_t it = ecs_filter_init(world, &f); + * + * ecs_iter_t it = ecs_query_init(world, &f); * ecs_iter_to_json_desc_t desc = { .serialize_table = true }; - * ecs_iter_to_json(world, iter, &desc); - * + * ecs_iter_to_json(iter, &desc); + * @endcode + * * @param world The world to serialize. * @return A JSON string with the serialized iterator data, or NULL if failed. */ @@ -343,8 +367,8 @@ char* ecs_world_to_json( const ecs_world_to_json_desc_t *desc); /** Serialize world into JSON string buffer. - * Same as ecs_world_to_json, but serializes to an ecs_strbuf_t instance. - * + * 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. diff --git a/vendors/flecs/include/flecs/addons/log.h b/vendors/flecs/include/flecs/addons/log.h index fdca67ca5..523097556 100644 --- a/vendors/flecs/include/flecs/addons/log.h +++ b/vendors/flecs/include/flecs/addons/log.h @@ -1,25 +1,25 @@ /** * @file addons/log.h * @brief Logging addon. - * + * * The logging addon provides an API for (debug) tracing and reporting errors * at various levels. When enabled, the logging addon can provide more detailed * information about the state of the ECS and any errors that may occur. - * + * * The logging addon can be disabled to reduce footprint of the library, but * limits information logged to only file, line and error code. - * + * * When enabled the logging addon can be configured to exclude levels of tracing - * from the build to reduce the impact on performance. By default all debug + * from the build to reduce the impact on performance. By default all debug * tracing is enabled for debug builds, tracing is enabled at release builds. - * - * Applications can change the logging level at runtime with ecs_log_set_level, - * but what is actually logged depends on what is compiled (when compiled - * without debug tracing, setting the runtime level to debug won't have an + * + * Applications can change the logging level at runtime with ecs_log_set_level(), + * but what is actually logged depends on what is compiled (when compiled + * without debug tracing, setting the runtime level to debug won't have an * effect). - * + * * The logging addon uses the OS API log_ function for all tracing. - * + * * Note that even when the logging addon is not enabled, its header/source must * be included in a build. To prevent unused variable warnings in the code, some * API functions are included when the addon is disabled, but have empty bodies. @@ -36,9 +36,9 @@ extern "C" { /** * @defgroup c_addons_log Log - * @brief Logging functions. - * - * \ingroup c_addons + * @ingroup c_addons + * Logging functions. + * * @{ */ @@ -46,16 +46,17 @@ extern "C" { //// Tracing //////////////////////////////////////////////////////////////////////////////// +/** Log message indicating an operation is deprecated. */ FLECS_API void ecs_deprecated_( - const char *file, - int32_t line, + const char *file, + int32_t line, const char *msg); /** Increase log stack. * This operation increases the indent_ value of the OS API and can be useful to * make nested behavior more visible. - * + * * @param level The log level. */ FLECS_API @@ -64,14 +65,14 @@ void ecs_log_push_(int32_t level); /** Decrease log stack. * This operation decreases the indent_ value of the OS API and can be useful to * make nested behavior more visible. - * + * * @param level The log level. */ FLECS_API void ecs_log_pop_(int32_t level); /** Should current level be logged. - * This operation returns true when the specified log level should be logged + * This operation returns true when the specified log level should be logged * with the current log level. * * @param level The log level to check for. @@ -166,7 +167,7 @@ void ecs_assert_log_( FLECS_API void ecs_parser_error_( const char *name, - const char *expr, + const char *expr, int64_t column, const char *fmt, ...); @@ -174,7 +175,7 @@ void ecs_parser_error_( FLECS_API void ecs_parser_errorv_( const char *name, - const char *expr, + const char *expr, int64_t column, const char *fmt, va_list args); @@ -343,7 +344,7 @@ void ecs_parser_errorv_( ecs_abort_(error_code, __FILE__, __LINE__, __VA_ARGS__);\ ecs_os_abort(); abort(); /* satisfy compiler/static analyzers */ -/** Assert. +/** Assert. * Aborts if condition is false, disabled in debug mode. */ #if defined(FLECS_NDEBUG) && !defined(FLECS_KEEP_ASSERT) #define ecs_assert(condition, error_code, ...) @@ -453,8 +454,8 @@ FLECS_API int ecs_log_set_level( int level); -/** Get current log level. - * +/** Get current log level. + * * @return Previous log level. */ FLECS_API @@ -484,12 +485,12 @@ bool ecs_log_enable_timestamp( /** Enable/disable logging time since last log. * By default deltatime is disabled. Note that enabling timestamps introduces * overhead as the logging code will need to obtain the current time. - * + * * When enabled, this logs the amount of time in seconds passed since the last * log, when this amount is non-zero. The format is a '+' character followed by * the number of seconds: - * - * +1 trace: log message + * + * +1 trace: log message * * @param enabled Whether to enable tracing with timestamps. * @return Previous timestamp setting. diff --git a/vendors/flecs/include/flecs/addons/meta.h b/vendors/flecs/include/flecs/addons/meta.h index c32952441..498126921 100644 --- a/vendors/flecs/include/flecs/addons/meta.h +++ b/vendors/flecs/include/flecs/addons/meta.h @@ -5,38 +5,39 @@ * The meta addon enables reflecting on component data. Types are stored as * entities, with components that store the reflection data. A type has at least * two components: - * + * * - EcsComponent: core component, contains size & alignment - * - EcsMetaType: component that indicates what kind of type the entity is + * - EcsType: component that indicates what kind of type the entity is * * Additionally the type may have an additional component that contains the * reflection data for the type. For example, structs have these components: - * + * * - EcsComponent - * - EcsMetaType + * - EcsType * - EcsStruct - * - * Structs can be populated by adding child entities with the EcsMember - * component. Adding a child with a Member component to an entity will + * + * Structs can be populated by adding child entities with the EcsMember + * component. Adding a child with a Member component to an entity will * automatically add the EcsStruct component to the parent. - * + * * Enums/bitmasks can be populated by adding child entities with the Constant * tag. By default constants are automatically assigned values when they are * added to the enum/bitmask. The parent entity must have the EcsEnum or * EcsBitmask component before adding the constants. - * + * * To create enum constants with a manual value, set (Constant, i32) to the * desired value. To create bitmask constants with a manual value, set * (Constant, u32) to the desired value. Constants with manual values should not * conflict with other constants. - * + * * The _init APIs are convenience wrappers around creating the entities and * components for the types. - * - * When a type is created it automatically receives the EcsComponent and - * EcsMetaType components. The former means that the resulting type can be + * + * When a type is created it automatically receives the EcsComponent and + * EcsType components. The former means that the resulting type can be * used as a regular component: - * + * + * @code * // Create Position type * ecs_entity_t pos = ecs_struct_init(world, &(ecs_struct_desc_t){ * .entity.name = "Position", @@ -48,7 +49,8 @@ * * // Create entity with Position component * ecs_entity_t e = ecs_new_w_id(world, pos); - * + * @endcode + * * Type entities do not have to be named. */ @@ -56,9 +58,9 @@ /** * @defgroup c_addons_meta Meta - * @brief Flecs reflection framework. - * - * \ingroup c_addons + * @ingroup c_addons + * Flecs reflection framework. + * * @{ */ @@ -75,68 +77,77 @@ extern "C" { #endif +/** Max number of constants/members that can be specified in desc structs. */ #define ECS_MEMBER_DESC_CACHE_SIZE (32) /** Primitive type definitions. * These typedefs allow the builtin primitives to be used as regular components: - * ecs_set(world, e, ecs_i32_t, {10}); - * + * + * @code + * ecs_set(world, e, ecs_i32_t, {10}); + * @endcode + * * Or a more useful example (create an enum constant with a manual value): - * ecs_set_pair_object(world, e, EcsConstant, ecs_i32_t, {10}); - */ -typedef bool ecs_bool_t; -typedef char ecs_char_t; -typedef unsigned char ecs_byte_t; -typedef uint8_t ecs_u8_t; -typedef uint16_t ecs_u16_t; -typedef uint32_t ecs_u32_t; -typedef uint64_t ecs_u64_t; -typedef uintptr_t ecs_uptr_t; -typedef int8_t ecs_i8_t; -typedef int16_t ecs_i16_t; -typedef int32_t ecs_i32_t; -typedef int64_t ecs_i64_t; -typedef intptr_t ecs_iptr_t; -typedef float ecs_f32_t; -typedef double ecs_f64_t; -typedef char* ecs_string_t; + * + * @code + * ecs_set_pair_second(world, e, EcsConstant, ecs_i32_t, {10}); + * @endcode + */ + +typedef bool ecs_bool_t; /**< Builtin bool type */ +typedef char ecs_char_t; /**< Builtin char type */ +typedef unsigned char ecs_byte_t; /**< Builtin ecs_byte type */ +typedef uint8_t ecs_u8_t; /**< Builtin u8 type */ +typedef uint16_t ecs_u16_t; /**< Builtin u16 type */ +typedef uint32_t ecs_u32_t; /**< Builtin u32 type */ +typedef uint64_t ecs_u64_t; /**< Builtin u64 type */ +typedef uintptr_t ecs_uptr_t; /**< Builtin uptr type */ +typedef int8_t ecs_i8_t; /**< Builtin i8 type */ +typedef int16_t ecs_i16_t; /**< Builtin i16 type */ +typedef int32_t ecs_i32_t; /**< Builtin i32 type */ +typedef int64_t ecs_i64_t; /**< Builtin i64 type */ +typedef intptr_t ecs_iptr_t; /**< Builtin iptr type */ +typedef float ecs_f32_t; /**< Builtin f32 type */ +typedef double ecs_f64_t; /**< Builtin f64 type */ +typedef char* ecs_string_t; /**< Builtin string type */ /* Meta module component ids */ -FLECS_API extern const ecs_entity_t ecs_id(EcsMetaType); -FLECS_API extern const ecs_entity_t ecs_id(EcsMetaTypeSerialized); -FLECS_API extern const ecs_entity_t ecs_id(EcsPrimitive); -FLECS_API extern const ecs_entity_t ecs_id(EcsEnum); -FLECS_API extern const ecs_entity_t ecs_id(EcsBitmask); -FLECS_API extern const ecs_entity_t ecs_id(EcsMember); -FLECS_API extern const ecs_entity_t ecs_id(EcsMemberRanges); -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; -FLECS_API extern const ecs_entity_t EcsQuantity; +FLECS_API extern const ecs_entity_t ecs_id(EcsType); /**< Id for component added to all types with reflection data. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsTypeSerializer); /**< Id for component that stores a type specific serializer. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsPrimitive); /**< Id for component that stores reflection data for a primitive type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsEnum); /**< Id for component that stores reflection data for an enum type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsBitmask); /**< Id for component that stores reflection data for a bitmask type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsMember); /**< Id for component that stores reflection data for struct members. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsMemberRanges); /**< Id for component that stores min/max ranges for member values. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsStruct); /**< Id for component that stores reflection data for a struct type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsArray); /**< Id for component that stores reflection data for an array type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsVector); /**< Id for component that stores reflection data for a vector type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsOpaque); /**< Id for component that stores reflection data for an opaque type. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsUnit); /**< Id for component that stores unit data. */ +FLECS_API extern const ecs_entity_t ecs_id(EcsUnitPrefix); /**< Id for component that stores unit prefix data. */ +FLECS_API extern const ecs_entity_t EcsConstant; /**< Tag added to enum/bitmask constants. */ +FLECS_API extern const ecs_entity_t EcsQuantity; /**< Tag added to unit quantities. */ /* Primitive type component ids */ -FLECS_API extern const ecs_entity_t ecs_id(ecs_bool_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_char_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_byte_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_u8_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_u16_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_u32_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_u64_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_uptr_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_i8_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_i16_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_i32_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_i64_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_iptr_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_f32_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_f64_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_string_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_entity_t); -FLECS_API extern const ecs_entity_t ecs_id(ecs_id_t); + +FLECS_API extern const ecs_entity_t ecs_id(ecs_bool_t); /**< Builtin boolean type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_char_t); /**< Builtin char type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_byte_t); /**< Builtin byte type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u8_t); /**< Builtin 8 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u16_t); /**< Builtin 16 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u32_t); /**< Builtin 32 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_u64_t); /**< Builtin 64 bit unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_uptr_t); /**< Builtin pointer sized unsigned int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i8_t); /**< Builtin 8 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i16_t); /**< Builtin 16 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i32_t); /**< Builtin 32 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_i64_t); /**< Builtin 64 bit signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_iptr_t); /**< Builtin pointer sized signed int type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_f32_t); /**< Builtin 32 bit floating point type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_f64_t); /**< Builtin 64 bit floating point type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_string_t); /**< Builtin string type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_entity_t); /**< Builtin entity type. */ +FLECS_API extern const ecs_entity_t ecs_id(ecs_id_t); /**< Builtin (component) id type. */ /** Type kinds supported by meta addon */ typedef enum ecs_type_kind_t { @@ -151,11 +162,11 @@ typedef enum ecs_type_kind_t { } ecs_type_kind_t; /** Component that is automatically added to every type with the right kind. */ -typedef struct EcsMetaType { - ecs_type_kind_t kind; +typedef struct EcsType { + ecs_type_kind_t kind; /**< Type kind. */ bool existing; /**< Did the type exist or is it populated from reflection */ bool partial; /**< Is the reflection data a partial type description */ -} EcsMetaType; +} EcsType; /** Primitive type kinds supported by meta addon */ typedef enum ecs_primitive_kind_t { @@ -182,59 +193,71 @@ typedef enum ecs_primitive_kind_t { /** Component added to primitive types */ typedef struct EcsPrimitive { - ecs_primitive_kind_t kind; + ecs_primitive_kind_t kind; /**< Primitive type kind. */ } EcsPrimitive; /** Component added to member entities */ typedef struct EcsMember { - ecs_entity_t type; - int32_t count; - ecs_entity_t unit; - int32_t offset; + ecs_entity_t type; /**< Member type. */ + int32_t count; /**< Number of elements (for inline arrays). */ + ecs_entity_t unit; /**< Member unit. */ + int32_t offset; /**< Member offset. */ + bool use_offset; /**< If offset should be explicitly used. */ } EcsMember; /** Type expressing a range for a member value */ typedef struct ecs_member_value_range_t { - double min; - double max; + double min; /**< Min member value. */ + double max; /**< Max member value. */ } ecs_member_value_range_t; /** Component added to member entities to express valid value ranges */ typedef struct EcsMemberRanges { - ecs_member_value_range_t value; - ecs_member_value_range_t warning; - ecs_member_value_range_t error; + ecs_member_value_range_t value; /**< Member value range. */ + ecs_member_value_range_t warning; /**< Member value warning range. */ + ecs_member_value_range_t error; /**< Member value error range. */ } EcsMemberRanges; /** Element type of members vector in EcsStruct */ typedef struct ecs_member_t { /** Must be set when used with ecs_struct_desc_t */ const char *name; + + /** Member type. */ ecs_entity_t type; - /** May be set when used with ecs_struct_desc_t */ + /** Element count (for inline arrays). May be set when used with ecs_struct_desc_t */ int32_t count; + + /** May be set when used with ecs_struct_desc_t. Member offset. */ int32_t offset; /** May be set when used with ecs_struct_desc_t, will be auto-populated if * type entity is also a unit */ ecs_entity_t unit; - /** Numerical range that specifies which values member can assume. This + /** Set to true to prevent automatic offset computation. This option should + * be used when members are registered out of order or where calculation of + * member offsets doesn't match C type offsets. */ + bool use_offset; + + /** Numerical range that specifies which values member can assume. This * range may be used by UI elements such as a progress bar or slider. The * value of a member should not exceed this range. */ ecs_member_value_range_t range; - /** Numerical range outside of which the value represents an error. This + /** Numerical range outside of which the value represents an error. This * range may be used by UI elements to style a value. */ ecs_member_value_range_t error_range; - /** Numerical range outside of which the value represents an warning. This + /** Numerical range outside of which the value represents an warning. This * range may be used by UI elements to style a value. */ ecs_member_value_range_t warning_range; /** Should not be set by ecs_struct_desc_t */ ecs_size_t size; + + /** Should not be set by ecs_struct_desc_t */ ecs_entity_t member; } ecs_member_t; @@ -244,6 +267,7 @@ typedef struct EcsStruct { ecs_vec_t members; /* vector */ } EcsStruct; +/** Type that describes an enum constant */ typedef struct ecs_enum_constant_t { /** Must be set when used with ecs_enum_desc_t */ const char *name; @@ -258,9 +282,10 @@ typedef struct ecs_enum_constant_t { /** Component added to enum type entities */ typedef struct EcsEnum { /** Populated from child entities with Constant component */ - ecs_map_t constants; /* map */ + ecs_map_t constants; /**< map */ } EcsEnum; +/** Type that describes an bitmask constant */ typedef struct ecs_bitmask_constant_t { /** Must be set when used with ecs_bitmask_desc_t */ const char *name; @@ -275,7 +300,7 @@ typedef struct ecs_bitmask_constant_t { /** Component added to bitmask type entities */ typedef struct EcsBitmask { /* Populated from child entities with Constant component */ - ecs_map_t constants; /* map */ + ecs_map_t constants; /**< map */ } EcsBitmask; /** Component added to array type entities */ @@ -307,8 +332,8 @@ typedef struct ecs_serializer_t { const struct ecs_serializer_t *ser, /**< Serializer */ const char *member); /**< Member name */ - const ecs_world_t *world; - void *ctx; + const ecs_world_t *world; /**< The world. */ + void *ctx; /**< Serializer context. */ } ecs_serializer_t; #elif defined(__cplusplus) @@ -330,7 +355,7 @@ typedef struct ecs_serializer_t { /* Serialize value */ int value(ecs_entity_t type, const void *value) const; - + /* Serialize value */ template int value(const T& value) const; @@ -350,11 +375,15 @@ typedef int (*ecs_meta_serialize_t)( const ecs_serializer_t *ser, const void *src); /**< Pointer to value to serialize */ +/** Opaque type reflection data. + * An opaque type is a type with an unknown layout that can be mapped to a type + * known to the reflection framework. See the opaque type reflection examples. + */ typedef struct EcsOpaque { ecs_entity_t as_type; /**< Type that describes the serialized output */ ecs_meta_serialize_t serialize; /**< Serialize action */ - /* Deserializer interface + /* Deserializer interface * Only override the callbacks that are valid for the opaque type. If a * deserializer attempts to assign a value type that is not supported by the * interface, a conversion error is thrown. @@ -362,32 +391,32 @@ typedef struct EcsOpaque { /** Assign bool value */ void (*assign_bool)( - void *dst, + void *dst, bool value); /** Assign char value */ void (*assign_char)( - void *dst, + void *dst, char value); /** Assign int value */ void (*assign_int)( - void *dst, + void *dst, int64_t value); /** Assign unsigned int value */ void (*assign_uint)( - void *dst, + void *dst, uint64_t value); /** Assign float value */ void (*assign_float)( - void *dst, + void *dst, double value); /** Assign string value */ void (*assign_string)( - void *dst, + void *dst, const char *value); /** Assign entity value */ @@ -412,32 +441,32 @@ typedef struct EcsOpaque { /** Ensure & get collection element */ void* (*ensure_element)( - void *dst, + void *dst, size_t elem); /** Ensure & get element */ void* (*ensure_member)( - void *dst, + void *dst, const char *member); /** Return number of elements */ size_t (*count)( const void *dst); - + /** Resize to number of elements */ void (*resize)( - void *dst, + void *dst, size_t count); } EcsOpaque; /* Units */ -/* Helper type to describe translation between two units. Note that this +/** Helper type to describe translation between two units. Note that this * is not intended as a generic approach to unit conversions (e.g. from celsius - * to fahrenheit) but to translate between units that derive from the same base - * (e.g. meters to kilometers). - * + * to fahrenheit) but to translate between units that derive from the same base + * (e.g. meters to kilometers). + * * Note that power is applied to the factor. When describing a translation of * 1000, either use {factor = 1000, power = 1} or {factor = 1, power = 3}. */ typedef struct ecs_unit_translation_t { @@ -445,22 +474,29 @@ typedef struct ecs_unit_translation_t { int32_t power; /**< Power to apply to factor (e.g. "1", "3", "-9") */ } ecs_unit_translation_t; +/** Component that stores unit data. */ typedef struct EcsUnit { - char *symbol; - ecs_entity_t prefix; /**< Order of magnitude prefix relative to derived */ - ecs_entity_t base; /**< Base unit (e.g. "meters") */ - ecs_entity_t over; /**< Over unit (e.g. "per second") */ - ecs_unit_translation_t translation; /**< Translation for derived unit */ + char *symbol; /**< Unit symbol. */ + ecs_entity_t prefix; /**< Order of magnitude prefix relative to derived */ + ecs_entity_t base; /**< Base unit (e.g. "meters") */ + ecs_entity_t over; /**< Over unit (e.g. "per second") */ + ecs_unit_translation_t translation; /**< Translation for derived unit */ } EcsUnit; +/** Component that stores unit prefix data. */ typedef struct EcsUnitPrefix { - char *symbol; /**< Symbol of prefix (e.g. "K", "M", "Ki") */ - ecs_unit_translation_t translation; /**< Translation of prefix */ + char *symbol; /**< Symbol of prefix (e.g. "K", "M", "Ki") */ + ecs_unit_translation_t translation; /**< Translation of prefix */ } EcsUnitPrefix; /* Serializer utilities */ +/** Serializer instruction opcodes. + * The meta type serializer works by generating a flattened array with + * instructions that tells a serializer what kind of fields can be found in a + * type at which offsets. +*/ typedef enum ecs_meta_type_op_kind_t { EcsOpArray, EcsOpVector, @@ -496,123 +532,195 @@ typedef enum ecs_meta_type_op_kind_t { EcsMetaTypeOpKindLast = EcsOpId } ecs_meta_type_op_kind_t; +/** Meta type serializer instruction data. */ typedef struct ecs_meta_type_op_t { - ecs_meta_type_op_kind_t kind; - ecs_size_t offset; /**< Offset of current field */ - int32_t count; - const char *name; /**< Name of value (only used for struct members) */ - int32_t op_count; /**< Number of operations until next field or end */ - ecs_size_t size; /**< Size of type of operation */ - ecs_entity_t type; /**< Type entity */ - int32_t member_index; /**< Index of member in struct */ - ecs_hashmap_t *members; /**< string -> member index (structs only) */ + ecs_meta_type_op_kind_t kind; /**< Instruction opcode. */ + ecs_size_t offset; /**< Offset of current field */ + int32_t count; /**< Number of elements (for inline arrays). */ + const char *name; /**< Name of value (only used for struct members) */ + int32_t op_count; /**< Number of operations until next field or end */ + ecs_size_t size; /**< Size of type of operation */ + ecs_entity_t type; /**< Type entity */ + int32_t member_index; /**< Index of member in struct */ + ecs_hashmap_t *members; /**< string -> member index (structs only) */ } ecs_meta_type_op_t; -typedef struct EcsMetaTypeSerialized { +/** Component that stores the type serializer. + * Added to all types with reflection data. + */ +typedef struct EcsTypeSerializer { ecs_vec_t ops; /**< vector */ -} EcsMetaTypeSerialized; +} EcsTypeSerializer; /* Deserializer utilities */ -#define ECS_META_MAX_SCOPE_DEPTH (32) /* >32 levels of nesting is not sane */ +/** Maximum level of type nesting. + * >32 levels of nesting is not sane. + */ +#define ECS_META_MAX_SCOPE_DEPTH (32) +/** Type with information about currently serialized scope. */ typedef struct ecs_meta_scope_t { - ecs_entity_t type; /**< The type being iterated */ - ecs_meta_type_op_t *ops; /**< The type operations (see ecs_meta_type_op_t) */ - int32_t op_count; /**< Number of operations in ops array to process */ - int32_t op_cur; /**< Current operation */ - int32_t elem_cur; /**< Current element (for collections) */ - int32_t prev_depth; /**< Depth to restore, in case dotmember was used */ - void *ptr; /**< Pointer to the value being iterated */ - - const EcsComponent *comp; /**< Pointer to component, in case size/alignment is needed */ - const EcsOpaque *opaque; /**< Opaque type interface */ - ecs_vec_t *vector; /**< Current vector, in case a vector is iterated */ - ecs_hashmap_t *members; /**< string -> member index */ - bool is_collection; /**< Is the scope iterating elements? */ - bool is_inline_array; /**< Is the scope iterating an inline array? */ - bool is_empty_scope; /**< Was scope populated (for collections) */ + ecs_entity_t type; /**< The type being iterated */ + ecs_meta_type_op_t *ops; /**< The type operations (see ecs_meta_type_op_t) */ + int32_t op_count; /**< Number of operations in ops array to process */ + int32_t op_cur; /**< Current operation */ + int32_t elem_cur; /**< Current element (for collections) */ + int32_t prev_depth; /**< Depth to restore, in case dotmember was used */ + void *ptr; /**< Pointer to the value being iterated */ + const EcsComponent *comp; /**< Pointer to component, in case size/alignment is needed */ + const EcsOpaque *opaque; /**< Opaque type interface */ + ecs_vec_t *vector; /**< Current vector, in case a vector is iterated */ + ecs_hashmap_t *members; /**< string -> member index */ + bool is_collection; /**< Is the scope iterating elements? */ + bool is_inline_array; /**< Is the scope iterating an inline array? */ + bool is_empty_scope; /**< Was scope populated (for collections) */ } ecs_meta_scope_t; -/** Type that enables iterating/populating a value using reflection data */ +/** Type that enables iterating/populating a value using reflection data. */ typedef struct ecs_meta_cursor_t { - const ecs_world_t *world; - ecs_meta_scope_t scope[ECS_META_MAX_SCOPE_DEPTH]; - int32_t depth; - bool valid; - bool is_primitive_scope; /**< If in root scope, this allows for a push for primitive types */ + const ecs_world_t *world; /**< The world. */ + ecs_meta_scope_t scope[ECS_META_MAX_SCOPE_DEPTH]; /**< Cursor scope stack. */ + int32_t depth; /**< Current scope depth. */ + bool valid; /**< Does the cursor point to a valid field. */ + bool is_primitive_scope; /**< If in root scope, this allows for a push for primitive types */ - /* Custom entity lookup action for overriding default ecs_lookup_fullpath */ + /** Custom entity lookup action for overriding default ecs_lookup */ ecs_entity_t (*lookup_action)(const ecs_world_t*, const char*, void*); - void *lookup_ctx; + void *lookup_ctx; /**< Context for lookup_action */ } ecs_meta_cursor_t; +/** Create meta cursor. + * A meta cursor allows for walking over, reading and writing a value without + * having to know its type at compile time. + * + * When a value is assigned through the cursor API, it will get converted to + * the actual value of the underlying type. This allows the underlying type to + * change without having to update the serialized data. For example, an integer + * field can be set by a string, a floating point can be set as integer etc. + * + * @param world The world. + * @param type The type of the value. + * @param ptr Pointer to the value. + * @return A meta cursor for the value. + */ FLECS_API ecs_meta_cursor_t ecs_meta_cursor( const ecs_world_t *world, ecs_entity_t type, void *ptr); -/** Get pointer to current field */ +/** Get pointer to current field. + * + * @param cursor The cursor. + * @return A pointer to the current field. + */ FLECS_API void* ecs_meta_get_ptr( ecs_meta_cursor_t *cursor); -/** Move cursor to next field */ +/** Move cursor to next field. + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_next( ecs_meta_cursor_t *cursor); -/** Move cursor to a element */ +/** Move cursor to a field. + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_elem( ecs_meta_cursor_t *cursor, int32_t elem); -/** Move cursor to member */ +/** Move cursor to member. + * + * @param cursor The cursor. + * @param name The name of the member. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_member( ecs_meta_cursor_t *cursor, const char *name); -/** Move cursor to member, supports dot-separated nested members */ +/** Move cursor to member. + * Same as ecs_meta_member(), but with support for "foo.bar" syntax. + * + * @param cursor The cursor. + * @param name The name of the member. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_dotmember( ecs_meta_cursor_t *cursor, const char *name); -/** Push a scope (required/only valid for structs & collections) */ +/** Push a scope (required/only valid for structs & collections). + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_push( ecs_meta_cursor_t *cursor); -/** Pop a struct or collection scope (must follow a push) */ +/** Pop a struct or collection scope (must follow a push). + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_pop( ecs_meta_cursor_t *cursor); -/** Is the current scope a collection? */ +/** Is the current scope a collection?. + * + * @param cursor The cursor. + * @return True if current scope is a collection, false if not. + */ FLECS_API bool ecs_meta_is_collection( const ecs_meta_cursor_t *cursor); -/** Get type of current element. */ +/** Get type of current field. + * + * @param cursor The cursor. + * @return The type of the current field. + */ FLECS_API ecs_entity_t ecs_meta_get_type( const ecs_meta_cursor_t *cursor); -/** Get unit of current element. */ +/** Get unit of current field. + * + * @param cursor The cursor. + * @return The unit of the current field. + */ FLECS_API ecs_entity_t ecs_meta_get_unit( const ecs_meta_cursor_t *cursor); -/** Get member name of current member */ +/** Get member name of current field. + * + * @param cursor The cursor. + * @return The member name of the current field. + */ FLECS_API const char* ecs_meta_get_member( const ecs_meta_cursor_t *cursor); -/** Get member entity of current member */ +/** Get member entity of current field. + * + * @param cursor The cursor. + * @return The member entity of the current field. + */ FLECS_API ecs_entity_t ecs_meta_get_member_id( const ecs_meta_cursor_t *cursor); @@ -621,72 +729,120 @@ ecs_entity_t ecs_meta_get_member_id( * does not have the same type as the field, it will be cased to the field type. * If no valid conversion is available, the operation will fail. */ -/** Set field with boolean value */ +/** Set field with boolean value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_bool( ecs_meta_cursor_t *cursor, bool value); -/** Set field with char value */ +/** Set field with char value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_char( ecs_meta_cursor_t *cursor, char value); -/** Set field with int value */ +/** Set field with int value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_int( ecs_meta_cursor_t *cursor, int64_t value); -/** Set field with uint value */ +/** Set field with uint value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_uint( ecs_meta_cursor_t *cursor, uint64_t value); -/** Set field with float value */ +/** Set field with float value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_float( ecs_meta_cursor_t *cursor, double value); -/** Set field with string value */ +/** Set field with string value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_string( ecs_meta_cursor_t *cursor, const char *value); -/** Set field with string literal value (has enclosing "") */ +/** Set field with string literal value (has enclosing ""). + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_string_literal( ecs_meta_cursor_t *cursor, const char *value); -/** Set field with entity value */ +/** Set field with entity value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_entity( ecs_meta_cursor_t *cursor, ecs_entity_t value); -/** Set field with (component) id value */ +/** Set field with (component) id value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_id( ecs_meta_cursor_t *cursor, ecs_id_t value); -/** Set field with (component) id value */ -FLECS_API -int ecs_meta_set_component( - ecs_meta_cursor_t *cursor, - ecs_id_t value); - -/** Set field with null value */ +/** Set field with null value. + * + * @param cursor The cursor. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_null( ecs_meta_cursor_t *cursor); -/** Set field with dynamic value */ +/** Set field with dynamic value. + * + * @param cursor The cursor. + * @param value The value to set. + * @return Zero if success, non-zero if failed. + */ FLECS_API int ecs_meta_set_value( ecs_meta_cursor_t *cursor, @@ -694,51 +850,87 @@ int ecs_meta_set_value( /* Functions for getting members. */ -/** Get field value as boolean. */ +/** Get field value as boolean. + * + * @param cursor The cursor. + * @return The value of the current field. + */ FLECS_API bool ecs_meta_get_bool( const ecs_meta_cursor_t *cursor); -/** Get field value as char. */ +/** Get field value as char. + * + * @param cursor The cursor. + * @return The value of the current field. + */ FLECS_API char ecs_meta_get_char( const ecs_meta_cursor_t *cursor); -/** Get field value as signed integer. */ +/** Get field value as signed integer. + * + * @param cursor The cursor. + * @return The value of the current field. + */ FLECS_API int64_t ecs_meta_get_int( const ecs_meta_cursor_t *cursor); -/** Get field value as unsigned integer. */ +/** Get field value as unsigned integer. + * + * @param cursor The cursor. + * @return The value of the current field. + */ FLECS_API uint64_t ecs_meta_get_uint( const ecs_meta_cursor_t *cursor); -/** Get field value as float. */ +/** Get field value as float. + * + * @param cursor The cursor. + * @return The value of the current field. + */ FLECS_API double ecs_meta_get_float( const ecs_meta_cursor_t *cursor); -/** Get field value as string. +/** Get field value as string. * This operation does not perform conversions. If the field is not a string, * this operation will fail. + * + * @param cursor The cursor. + * @return The value of the current field. */ FLECS_API const char* ecs_meta_get_string( const ecs_meta_cursor_t *cursor); -/** Get field value as entity. - * This operation does not perform conversions. */ +/** Get field value as entity. + * This operation does not perform conversions. + * + * @param cursor The cursor. + * @return The value of the current field. + */ FLECS_API ecs_entity_t ecs_meta_get_entity( const ecs_meta_cursor_t *cursor); -/** Get field value as (component) id. - * This operation can convert from an entity. */ +/** Get field value as (component) id. + * This operation can convert from an entity. + * + * @param cursor The cursor. + * @return The value of the current field. + */ ecs_id_t ecs_meta_get_id( const ecs_meta_cursor_t *cursor); -/** Convert pointer of primitive kind to float. */ +/** Convert pointer of primitive kind to float. + * + * @param type_kind The primitive type kind of the value. + * @param ptr Pointer to a value of a primitive type. + * @return The value in floating point format. + */ FLECS_API double ecs_meta_ptr_to_float( ecs_primitive_kind_t type_kind, @@ -746,87 +938,119 @@ double ecs_meta_ptr_to_float( /* API functions for creating meta types */ -/** Used with ecs_primitive_init. */ +/** Used with ecs_primitive_init(). */ typedef struct ecs_primitive_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_primitive_kind_t kind; + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_primitive_kind_t kind; /**< Primitive type kind. */ } ecs_primitive_desc_t; -/** Create a new primitive type */ +/** Create a new primitive type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ FLECS_API ecs_entity_t ecs_primitive_init( ecs_world_t *world, const ecs_primitive_desc_t *desc); -/** Used with ecs_enum_init. */ + +/** Used with ecs_enum_init(). */ typedef struct ecs_enum_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_enum_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_enum_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Enum constants. */ } ecs_enum_desc_t; -/** Create a new enum type */ +/** Create a new enum type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ FLECS_API ecs_entity_t ecs_enum_init( ecs_world_t *world, const ecs_enum_desc_t *desc); -/** Used with ecs_bitmask_init. */ +/** Used with ecs_bitmask_init(). */ typedef struct ecs_bitmask_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_bitmask_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_bitmask_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Bitmask constants. */ } ecs_bitmask_desc_t; -/** Create a new bitmask type */ +/** Create a new bitmask type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ FLECS_API ecs_entity_t ecs_bitmask_init( ecs_world_t *world, const ecs_bitmask_desc_t *desc); -/** Used with ecs_array_init. */ +/** Used with ecs_array_init(). */ typedef struct ecs_array_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_entity_t type; - int32_t count; + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_entity_t type; /**< Element type. */ + int32_t count; /**< Number of elements. */ } ecs_array_desc_t; -/** Create a new array type */ +/** Create a new array type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ FLECS_API ecs_entity_t ecs_array_init( ecs_world_t *world, const ecs_array_desc_t *desc); -/** Used with ecs_vector_init. */ +/** Used with ecs_vector_init(). */ typedef struct ecs_vector_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_entity_t type; + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_entity_t type; /**< Element type. */ } ecs_vector_desc_t; -/** Create a new vector type */ +/** Create a new vector type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ FLECS_API ecs_entity_t ecs_vector_init( ecs_world_t *world, const ecs_vector_desc_t *desc); -/** Used with ecs_struct_init. */ +/** Used with ecs_struct_init(). */ typedef struct ecs_struct_desc_t { - ecs_entity_t entity; /**< Existing entity to use for type (optional) */ - ecs_member_t members[ECS_MEMBER_DESC_CACHE_SIZE]; + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + ecs_member_t members[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Struct members. */ } ecs_struct_desc_t; -/** Create a new struct type */ +/** Create a new struct type. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. + */ FLECS_API ecs_entity_t ecs_struct_init( ecs_world_t *world, const ecs_struct_desc_t *desc); -/** Used with ecs_opaque_init. */ + +/** Used with ecs_opaque_init(). */ typedef struct ecs_opaque_desc_t { - ecs_entity_t entity; - EcsOpaque type; + ecs_entity_t entity; /**< Existing entity to use for type (optional). */ + EcsOpaque type; /**< Type that the opaque type maps to. */ } ecs_opaque_desc_t; /** Create a new opaque type. @@ -835,77 +1059,99 @@ typedef struct ecs_opaque_desc_t { * 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 + * + * 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) + * + * @param world The world. + * @param desc The type descriptor. + * @return The new type, 0 if failed. */ FLECS_API ecs_entity_t ecs_opaque_init( ecs_world_t *world, const ecs_opaque_desc_t *desc); -/** Used with ecs_unit_init. */ + +/** Used with ecs_unit_init(). */ typedef struct ecs_unit_desc_t { - /** Existing entity to associate with unit (optional) */ + /** Existing entity to associate with unit (optional). */ ecs_entity_t entity; - /** Unit symbol, e.g. "m", "%", "g". (optional) */ + /** Unit symbol, e.g. "m", "%", "g". (optional). */ const char *symbol; - /** Unit quantity, e.g. distance, percentage, weight. (optional) */ + /** Unit quantity, e.g. distance, percentage, weight. (optional). */ ecs_entity_t quantity; - /** Base unit, e.g. "meters" (optional) */ + /** Base unit, e.g. "meters" (optional). */ ecs_entity_t base; - /** Over unit, e.g. "per second" (optional) */ + /** Over unit, e.g. "per second" (optional). */ ecs_entity_t over; - /** Translation to apply to derived unit (optional) */ + /** Translation to apply to derived unit (optional). */ ecs_unit_translation_t translation; /** Prefix indicating order of magnitude relative to the derived unit. If set * together with "translation", the values must match. If translation is not - * set, setting prefix will autopopulate it. + * set, setting prefix will auto-populate it. * Additionally, setting the prefix will enforce that the symbol (if set) * is consistent with the prefix symbol + symbol of the derived unit. If the * symbol is not set, it will be auto populated. */ ecs_entity_t prefix; } ecs_unit_desc_t; -/** Create a new unit */ +/** Create a new unit. + * + * @param world The world. + * @param desc The unit descriptor. + * @return The new unit, 0 if failed. + */ FLECS_API ecs_entity_t ecs_unit_init( ecs_world_t *world, const ecs_unit_desc_t *desc); -/** Used with ecs_unit_prefix_init. */ + +/** Used with ecs_unit_prefix_init(). */ typedef struct ecs_unit_prefix_desc_t { - /** Existing entity to associate with unit prefix (optional) */ + /** Existing entity to associate with unit prefix (optional). */ ecs_entity_t entity; - /** Unit symbol, e.g. "m", "%", "g". (optional) */ + /** Unit symbol, e.g. "m", "%", "g". (optional). */ const char *symbol; - /** Translation to apply to derived unit (optional) */ + /** Translation to apply to derived unit (optional). */ ecs_unit_translation_t translation; } ecs_unit_prefix_desc_t; -/** Create a new unit prefix */ +/** Create a new unit prefix. + * + * @param world The world. + * @param desc The type descriptor. + * @return The new unit prefix, 0 if failed. + */ FLECS_API ecs_entity_t ecs_unit_prefix_init( ecs_world_t *world, const ecs_unit_prefix_desc_t *desc); -/** Create a new quantity */ + +/** Create a new quantity. + * + * @param world The world. + * @param desc The quantity descriptor. + * @return The new quantity, 0 if failed. + */ FLECS_API ecs_entity_t ecs_quantity_init( ecs_world_t *world, @@ -913,37 +1159,55 @@ ecs_entity_t ecs_quantity_init( /* Convenience macros */ +/** Create a primitive type. */ #define ecs_primitive(world, ...)\ ecs_primitive_init(world, &(ecs_primitive_desc_t) __VA_ARGS__ ) +/** Create an enum type. */ #define ecs_enum(world, ...)\ ecs_enum_init(world, &(ecs_enum_desc_t) __VA_ARGS__ ) +/** Create a bitmask type. */ #define ecs_bitmask(world, ...)\ ecs_bitmask_init(world, &(ecs_bitmask_desc_t) __VA_ARGS__ ) +/** Create an array type. */ #define ecs_array(world, ...)\ ecs_array_init(world, &(ecs_array_desc_t) __VA_ARGS__ ) +/** Create a vector type. */ #define ecs_vector(world, ...)\ ecs_vector_init(world, &(ecs_vector_desc_t) __VA_ARGS__ ) +/** Create an opaque type. */ #define ecs_opaque(world, ...)\ ecs_opaque_init(world, &(ecs_opaque_desc_t) __VA_ARGS__ ) +/** Create a struct type. */ #define ecs_struct(world, ...)\ ecs_struct_init(world, &(ecs_struct_desc_t) __VA_ARGS__ ) +/** Create a unit. */ #define ecs_unit(world, ...)\ ecs_unit_init(world, &(ecs_unit_desc_t) __VA_ARGS__ ) +/** Create a unit prefix. */ #define ecs_unit_prefix(world, ...)\ ecs_unit_prefix_init(world, &(ecs_unit_prefix_desc_t) __VA_ARGS__ ) +/** Create a unit quantity. */ #define ecs_quantity(world, ...)\ ecs_quantity_init(world, &(ecs_entity_desc_t) __VA_ARGS__ ) -/* Module import */ + +/** Meta module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsMeta) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsMetaImport( ecs_world_t *world); @@ -952,6 +1216,8 @@ void FlecsMetaImport( } #endif +#include "meta_c.h" + #endif /** @} */ diff --git a/vendors/flecs/include/flecs/addons/meta_c.h b/vendors/flecs/include/flecs/addons/meta_c.h index 73191b75d..3a4bb9a29 100644 --- a/vendors/flecs/include/flecs/addons/meta_c.h +++ b/vendors/flecs/include/flecs/addons/meta_c.h @@ -3,24 +3,16 @@ * @brief Utility macros for populating reflection data in C. */ -#ifdef FLECS_META_C +#ifdef FLECS_META /** * @defgroup c_addons_meta_c Meta Utilities - * @brief Macro utilities to automatically insert reflection data. - * - * \ingroup c_addons + * @ingroup c_addons + * Macro utilities to automatically insert reflection data. + * * @{ */ -#ifndef FLECS_META -#define FLECS_META -#endif - -#ifndef FLECS_PARSER -#define FLECS_PARSER -#endif - #ifndef FLECS_META_C_H #define FLECS_META_C_H @@ -44,28 +36,28 @@ extern "C" { * variable for the component identifier. */ /* #define ECS_META_IMPL EXTERN */ -/** Declare component with descriptor */ +/** Declare component with descriptor. */ #define ECS_META_COMPONENT(world, name)\ ECS_COMPONENT_DEFINE(world, name);\ ecs_meta_from_desc(world, ecs_id(name),\ FLECS__##name##_kind, FLECS__##name##_desc) -/** ECS_STRUCT(name, body) */ +/** ECS_STRUCT(name, body). */ #define ECS_STRUCT(name, ...)\ ECS_META_IMPL_CALL(ECS_STRUCT_, ECS_META_IMPL, name, #__VA_ARGS__);\ ECS_STRUCT_TYPE(name, __VA_ARGS__) -/** ECS_ENUM(name, body) */ +/** ECS_ENUM(name, body). */ #define ECS_ENUM(name, ...)\ ECS_META_IMPL_CALL(ECS_ENUM_, ECS_META_IMPL, name, #__VA_ARGS__);\ ECS_ENUM_TYPE(name, __VA_ARGS__) -/** ECS_BITMASK(name, body) */ +/** ECS_BITMASK(name, body). */ #define ECS_BITMASK(name, ...)\ ECS_META_IMPL_CALL(ECS_BITMASK_, ECS_META_IMPL, name, #__VA_ARGS__);\ ECS_ENUM_TYPE(name, __VA_ARGS__) -/** Macro used to mark part of type for which no reflection data is created */ +/** Macro used to mark part of type for which no reflection data is created. */ #define ECS_PRIVATE /** Populate meta information from type descriptor. */ @@ -77,9 +69,10 @@ int ecs_meta_from_desc( const char *desc); -/* Private API */ +/** \cond + * Private utilities to switch between meta IMPL, DECLARE and EXTERN variants. + */ -/* Utilities to switch between IMPL, DECLARE and EXTERN variants */ #define ECS_META_IMPL_CALL_INNER(base, impl, name, type_desc)\ base ## impl(name, type_desc) @@ -145,12 +138,14 @@ int ecs_meta_from_desc( #define ECS_BITMASK_EXTERN(name, type_desc)\ extern ECS_COMPONENT_DECLARE(name) +/** \endcond */ + #ifdef __cplusplus } #endif -#endif // FLECS_META_C_H +#endif // FLECS_META_H /** @} */ -#endif // FLECS_META_C +#endif // FLECS_META diff --git a/vendors/flecs/include/flecs/addons/metrics.h b/vendors/flecs/include/flecs/addons/metrics.h index 1e19786fa..e943cd80e 100644 --- a/vendors/flecs/include/flecs/addons/metrics.h +++ b/vendors/flecs/include/flecs/addons/metrics.h @@ -10,9 +10,9 @@ /** * @defgroup c_addons_metrics Metrics - * @brief Collect user-defined metrics from ECS data. - * - * \ingroup c_addons + * @ingroup c_addons + * Collect user-defined metrics from ECS data. + * * @{ */ @@ -35,52 +35,56 @@ extern "C" { #endif +/** Flecs metrics module. */ FLECS_API extern ECS_COMPONENT_DECLARE(FlecsMetrics); -/** Tag added to metrics, and used as first element of metric kind pair */ +/** Tag added to metrics, and used as first element of metric kind pair. */ FLECS_API extern ECS_TAG_DECLARE(EcsMetric); -/** Metric that has monotonically increasing value */ +/** Metric that has monotonically increasing value. */ FLECS_API extern ECS_TAG_DECLARE(EcsCounter); -/** Counter metric that is auto-incremented by source value */ +/** Counter metric that is auto-incremented by source value. */ FLECS_API extern ECS_TAG_DECLARE(EcsCounterIncrement); -/** Counter metric that counts the number of entities with an id */ +/** Counter metric that counts the number of entities with an id. */ FLECS_API extern ECS_TAG_DECLARE(EcsCounterId); -/** Metric that represents current value */ +/** Metric that represents current value. */ FLECS_API extern ECS_TAG_DECLARE(EcsGauge); -/** Tag added to metric instances */ +/** Tag added to metric instances. */ FLECS_API extern ECS_TAG_DECLARE(EcsMetricInstance); -/** Component with metric instance value */ +/** Component with metric instance value. */ FLECS_API extern ECS_COMPONENT_DECLARE(EcsMetricValue); -/** Component with entity source of metric instance */ +/** Component with entity source of metric instance. */ FLECS_API extern ECS_COMPONENT_DECLARE(EcsMetricSource); +/** Component that stores metric value. */ typedef struct EcsMetricValue { double value; } EcsMetricValue; +/** Component that stores metric source. */ typedef struct EcsMetricSource { ecs_entity_t entity; } EcsMetricSource; +/** Used with ecs_metric_init to create metric. */ typedef struct ecs_metric_desc_t { int32_t _canary; /** Entity associated with metric */ ecs_entity_t entity; - + /** Entity associated with member that stores metric value. Must not be set * at the same time as id. Cannot be combined with EcsCounterId. */ ecs_entity_t member; /* Member dot expression. Can be used instead of member and supports nested - * members. Must be set together with id and should not be set at the same + * members. Must be set together with id and should not be set at the same * time as member. */ const char *dotmember; @@ -89,7 +93,7 @@ typedef struct ecs_metric_desc_t { ecs_id_t id; /** If id is a (R, *) wildcard and relationship R has the OneOf property, - * setting this value to true will track individual targets. + * setting this value to true will track individual targets. * If the kind is EcsCountId and the id is a (R, *) wildcard, this value * will create a metric per target. */ bool targets; @@ -106,36 +110,36 @@ typedef struct ecs_metric_desc_t { * properties in the ECS storage. Metrics provide a single unified interface to * discovering and reading these values, which can be useful for monitoring * utilities, or for debugging. - * + * * Examples of properties that can be measured by metrics are: * - Component member values * - How long an entity has had a specific component * - How long an entity has had a specific target for a relationship * - How many entities have a specific component - * + * * Metrics can either be created as a "gauge" or "counter". A gauge is a metric * that represents the value of something at a specific point in time, for * example "velocity". A counter metric represents a value that is monotonically * increasing, for example "miles driven". - * + * * There are three different kinds of counter metric kinds: * - EcsCounter * When combined with a member, this will store the actual value of the member * in the metric. This is useful for values that are already counters, such as * a MilesDriven component. * This kind creates a metric per entity that has the member/id. - * + * * - EcsCounterIncrement * When combined with a member, this will increment the value of the metric by * the value of the member * delta_time. This is useful for values that are * not counters, such as a Velocity component. * This kind creates a metric per entity that has the member. - * + * * - EcsCounterId - * This metric kind will count the number of entities with a specific + * This metric kind will count the number of entities with a specific * (component) id. This kind creates a single metric instance for regular ids, * and a metric instance per target for wildcard ids when targets is set. - * + * * @param world The world. * @param desc Metric description. * @return The metric entity. @@ -145,18 +149,28 @@ ecs_entity_t ecs_metric_init( ecs_world_t *world, const ecs_metric_desc_t *desc); -/** Shorthand for creating a metric with ecs_metric_init. +/** Shorthand for creating a metric with ecs_metric_init(). * * Example: - * ecs_metric(world, { - * .member = ecs_lookup_fullpath(world, "Position.x") - * .kind = EcsGauge - * }); + * + * @code + * ecs_metric(world, { + * .member = ecs_lookup(world, "Position.x") + * .kind = EcsGauge + * }); + * @endcode */ #define ecs_metric(world, ...)\ ecs_metric_init(world, &(ecs_metric_desc_t) __VA_ARGS__ ) -/* Module import */ +/** Metrics module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsMetrics) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsMetricsImport( ecs_world_t *world); diff --git a/vendors/flecs/include/flecs/addons/module.h b/vendors/flecs/include/flecs/addons/module.h index 0dad20ff7..c04971ee5 100644 --- a/vendors/flecs/include/flecs/addons/module.h +++ b/vendors/flecs/include/flecs/addons/module.h @@ -2,7 +2,7 @@ * @file addons/module.h * @brief Module addon. * - * The module addon allows for creating and importing modules. Flecs modules + * The module addon allows for creating and importing modules. Flecs modules * enable applications to organize components and systems into reusable units of * code that can easily be across projects. */ @@ -11,9 +11,9 @@ /** * @defgroup c_addons_module Module - * @brief Modules organize components, systems and more in reusable units of code. - * - * \ingroup c_addons + * @ingroup c_addons + * Modules organize components, systems and more in reusable units of code. + * * @{ */ @@ -31,9 +31,9 @@ extern "C" { * will be translated from PascalCase to an entity path (pascal.case) before the * lookup occurs. * - * Module contents will be stored as children of the module entity. This + * Module contents will be stored as children of the module entity. This * prevents modules from accidentally defining conflicting identifiers. This is - * enforced by setting the scope before and after loading the module to the + * enforced by setting the scope before and after loading the module to the * module entity id. * * A more convenient way to import a module is by using the ECS_IMPORT macro. @@ -49,7 +49,7 @@ ecs_entity_t ecs_import( ecs_module_action_t module, const char *module_name); -/** Same as ecs_import, but with name to scope conversion. +/** Same as ecs_import(), but with name to scope conversion. * PascalCase names are automatically converted to scoped names. * * @param world The world. @@ -64,7 +64,7 @@ ecs_entity_t ecs_import_c( const char *module_name_c); /** Import a module from a library. - * Similar to ecs_import, except that this operation will attempt to load the + * Similar to ecs_import(), except that this operation will attempt to load the * module from a dynamic library. * * A library may contain multiple modules, which is why both a library name and @@ -103,15 +103,18 @@ ecs_entity_t ecs_module_init( ecs_set_scope(world, ecs_id(id));\ } +/** Create a module. */ #define ECS_MODULE(world, id)\ ecs_entity_t ecs_id(id) = 0; ECS_MODULE_DEFINE(world, id)\ (void)ecs_id(id) -/** Wrapper around ecs_import. +/** Wrapper around ecs_import(). * This macro provides a convenient way to load a module with the world. It can * be used like this: * + * @code * ECS_IMPORT(world, FlecsSystemsPhysics); + * @endcode */ #define ECS_IMPORT(world, id) ecs_import_c(world, id##Import, #id) diff --git a/vendors/flecs/include/flecs/addons/monitor.h b/vendors/flecs/include/flecs/addons/monitor.h deleted file mode 100644 index 7a61f0683..000000000 --- a/vendors/flecs/include/flecs/addons/monitor.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @file addons/monitor.h - * @brief Doc module. - * - * The monitor module automatically tracks statistics from the stats addon and - * stores them in components. - */ - -#ifdef FLECS_MONITOR - -/** - * @defgroup c_addons_monitor Monitor - * @brief The monitor addon periodically tracks statistics for the world and systems. - * - * \ingroup c_addons - * @{ - */ - -#ifndef FLECS_MONITOR_H -#define FLECS_MONITOR_H - -#ifndef FLECS_MODULE -#define FLECS_MODULE -#endif - -#ifndef FLECS_STATS -#define FLECS_STATS -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -FLECS_API extern ECS_COMPONENT_DECLARE(FlecsMonitor); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldStats); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldSummary); -FLECS_API extern ECS_COMPONENT_DECLARE(EcsPipelineStats); - -FLECS_API extern ecs_entity_t EcsPeriod1s; -FLECS_API extern ecs_entity_t EcsPeriod1m; -FLECS_API extern ecs_entity_t EcsPeriod1h; -FLECS_API extern ecs_entity_t EcsPeriod1d; -FLECS_API extern ecs_entity_t EcsPeriod1w; - -typedef struct { - ecs_ftime_t elapsed; - int32_t reduce_count; -} EcsStatsHeader; - -typedef struct { - EcsStatsHeader hdr; - ecs_world_stats_t stats; -} EcsWorldStats; - -typedef struct { - EcsStatsHeader hdr; - ecs_pipeline_stats_t stats; -} EcsPipelineStats; - -typedef struct { - /* Target FPS */ - double target_fps; /**< Target FPS */ - - /* Total time */ - double frame_time_total; /**< Total time spent processing a frame */ - double system_time_total; /**< Total time spent in systems */ - double merge_time_total; /**< Total time spent in merges */ - - /* Last frame time */ - double frame_time_last; /**< Time spent processing a frame */ - double system_time_last; /**< Time spent in systems */ - double merge_time_last; /**< Time spent in merges */ -} EcsWorldSummary; - -/* Module import */ -FLECS_API -void FlecsMonitorImport( - ecs_world_t *world); - -#ifdef __cplusplus -} -#endif - -#endif - -/** @} */ - -#endif diff --git a/vendors/flecs/include/flecs/addons/parser.h b/vendors/flecs/include/flecs/addons/parser.h deleted file mode 100644 index b4a8d7c69..000000000 --- a/vendors/flecs/include/flecs/addons/parser.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @file addons/parser.h - * @brief Parser addon. - * - * The parser addon parses string expressions into lists of terms, and can be - * used to construct filters, queries and types. - */ - -#ifdef FLECS_PARSER - -/** - * @defgroup c_addons_parser Parser - * @brief Query DSL parser and parsing utilities. - * - * \ingroup c_addons - * @{ - */ - -#ifndef FLECS_PARSER_H -#define FLECS_PARSER_H - -/** Maximum number of extra arguments in term expression */ -#define ECS_PARSER_MAX_ARGS (16) - -#ifdef __cplusplus -extern "C" { -#endif - -/** Skip whitespace characters. - * This function skips whitespace characters. Does not skip newlines. - * - * @param ptr Pointer to (potential) whitespaces to skip. - * @return Pointer to the next non-whitespace character. - */ -FLECS_API -const char* ecs_parse_ws( - const char *ptr); - -/** Skip whitespace and newline characters. - * This function skips whitespace characters. - * - * @param ptr Pointer to (potential) whitespaces to skip. - * @return Pointer to the next non-whitespace character. - */ -FLECS_API -const char* ecs_parse_ws_eol( - const char *ptr); - -/** Utility function to parse an identifier */ -const char* ecs_parse_identifier( - const char *name, - const char *expr, - const char *ptr, - char *token_out); - -/** Parse digit. - * This function will parse until the first non-digit character is found. The - * provided expression must contain at least one digit character. - * - * @param ptr The expression to parse. - * @param token The output buffer. - * @return Pointer to the first non-digit character. - */ -FLECS_API -const char* ecs_parse_digit( - const char *ptr, - char *token); - -/** Parse a single token. - * This function can be used as simple tokenizer by other parsers. - * - * @param name of program (used for logging). - * @param expr pointer to token to parse. - * @param ptr pointer to first character to parse. - * @param token_out Parsed token (buffer should be ECS_MAX_TOKEN_SIZE large) - * @return Pointer to the next token, or NULL if error occurred. - */ -FLECS_API -const char* ecs_parse_token( - const char *name, - const char *expr, - const char *ptr, - char *token_out, - char delim); - -/** Parse term in expression. - * This operation parses a single term in an expression and returns a pointer - * to the next term expression. - * - * If the returned pointer points to the 0-terminator, the expression is fully - * parsed. The function would typically be called in a while loop: - * - * const char *ptr = expr; - * while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term))) { } - * - * The operation does not attempt to find entity ids from the names in the - * expression. Use the ecs_term_resolve_ids function to resolve the identifiers - * in the parsed term. - * - * The returned term will in most cases contain allocated resources, which - * should freed (or used) by the application. To free the resources for a term, - * use the ecs_term_free function. - * - * The parser accepts expressions in the legacy string format. - * - * @param world The world. - * @param name The name of the expression (optional, improves error logs) - * @param expr The expression to parse (optional, improves error logs) - * @param ptr The pointer to the current term (must be in expr). - * @param term_out Out parameter for the term. - * @param extra_args Out array for extra args, must be of size ECS_PARSER_MAX_ARGS. - * @return pointer to next term if successful, NULL if failed. - */ -FLECS_API -char* ecs_parse_term( - const ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_term_t *term_out, - ecs_term_id_t *extra_args); - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif // FLECS_PARSER_H - -/** @} */ - -#endif // FLECS_PARSER diff --git a/vendors/flecs/include/flecs/addons/pipeline.h b/vendors/flecs/include/flecs/addons/pipeline.h index aa8117577..02106b7dc 100644 --- a/vendors/flecs/include/flecs/addons/pipeline.h +++ b/vendors/flecs/include/flecs/addons/pipeline.h @@ -7,19 +7,19 @@ * systems. When ran, a pipeline will query for all systems that have the tags * that belong to a pipeline, and run them. * - * The module defines a number of builtin tags (EcsPreUpdate, EcsOnUpdate, - * EcsPostUpdate etc.) that are registered with the builtin pipeline. The - * builtin pipeline is ran by default when calling ecs_progress(). An - * application can set a custom pipeline with the ecs_set_pipeline function. + * The module defines a number of builtin tags (EcsPreUpdate, EcsOnUpdate, + * EcsPostUpdate etc.) that are registered with the builtin pipeline. The + * builtin pipeline is ran by default when calling ecs_progress(). An + * application can set a custom pipeline with the ecs_set_pipeline() function. */ #ifdef FLECS_PIPELINE /** * @defgroup c_addons_pipeline Pipeline - * @brief Pipelines order and schedule systems for execution. - * - * \ingroup c_addons + * @ingroup c_addons + * Pipelines order and schedule systems for execution. + * * @{ */ @@ -44,6 +44,13 @@ extern "C" { #ifndef FLECS_LEGACY +/** Convenience macro to create a predeclared pipeline. + * Usage: + * @code + * ECS_ENTITY_DECLARE(MyPipeline); + * ECS_PIPELINE_DEFINE(world, MyPipeline, Update || Physics || Render) + * @endcode + */ #define ECS_PIPELINE_DEFINE(world, id_, ...) \ { \ ecs_pipeline_desc_t desc = {0}; \ @@ -51,33 +58,68 @@ extern "C" { edesc.id = id_;\ edesc.name = #id_;\ desc.entity = ecs_entity_init(world, &edesc);\ - desc.query.filter.expr = #__VA_ARGS__; \ + desc.query.expr = #__VA_ARGS__; \ id_ = ecs_pipeline_init(world, &desc); \ ecs_id(id_) = id_;\ } \ - ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(id_ != 0, ECS_INVALID_PARAMETER, "failed to create pipeline"); +/** Convenience macro to create a pipeline. + * Usage: + * @code + * ECS_PIPELINE(world, MyPipeline, Update || Physics || Render) + * @endcode + * + */ #define ECS_PIPELINE(world, id, ...) \ ecs_entity_t id = 0, ecs_id(id) = 0; ECS_PIPELINE_DEFINE(world, id, __VA_ARGS__);\ (void)id;\ (void)ecs_id(id); +/** Convenience macro to create a pipeline. + * See ecs_pipeline_init(). + */ #define ecs_pipeline(world, ...)\ ecs_pipeline_init(world, &(ecs_pipeline_desc_t) __VA_ARGS__ ) #endif -/* Pipeline descriptor (used with ecs_pipeline_init) */ +/** Pipeline descriptor, used with ecs_pipeline_init(). */ typedef struct ecs_pipeline_desc_t { - /* Existing entity to associate with pipeline (optional) */ + /** Existing entity to associate with pipeline (optional). */ ecs_entity_t entity; - - /* Query descriptor. The first term of the query must match the EcsSystem - * component. */ + + /** The pipeline query. + * Pipelines are queries that are matched with system entities. Pipeline + * queries are the same as regular queries, which means the same query rules + * apply. A common mistake is to try a pipeline that matches systems in a + * list of phases by specifying all the phases, like: + * OnUpdate, OnPhysics, OnRender + * + * That however creates a query that matches entities with OnUpdate _and_ + * OnPhysics _and_ OnRender tags, which is likely undesired. Instead, a + * query could use the or operator match a system that has one of the + * specified phases: + * OnUpdate || OnPhysics || OnRender + * + * This will return the correct set of systems, but they likely won't be in + * the correct order. To make sure systems are returned in the correct order + * two query ordering features can be used: + * - group_by + * - order_by + * + * Take a look at the system manual for a more detailed explanation of + * how query features can be applied to pipelines, and how the builtin + * pipeline query works. + */ ecs_query_desc_t query; } ecs_pipeline_desc_t; /** Create a custom pipeline. + * + * @param world The world. + * @param desc The pipeline descriptor. + * @return The pipeline, 0 if failed. */ FLECS_API ecs_entity_t ecs_pipeline_init( @@ -85,7 +127,7 @@ ecs_entity_t ecs_pipeline_init( const ecs_pipeline_desc_t *desc); /** Set a custom pipeline. - * This operation sets the pipeline to run when ecs_progress is invoked. + * This operation sets the pipeline to run when ecs_progress() is invoked. * * @param world The world. * @param pipeline The pipeline to set. @@ -93,7 +135,7 @@ ecs_entity_t ecs_pipeline_init( FLECS_API void ecs_set_pipeline( ecs_world_t *world, - ecs_entity_t pipeline); + ecs_entity_t pipeline); /** Get the current pipeline. * This operation gets the current pipeline. @@ -103,7 +145,7 @@ void ecs_set_pipeline( */ FLECS_API ecs_entity_t ecs_get_pipeline( - const ecs_world_t *world); + const ecs_world_t *world); /** Progress a world. * This operation progresses the world by running all systems that are both @@ -114,19 +156,19 @@ ecs_entity_t ecs_get_pipeline( * update entity values proportional to the elapsed time since their last * invocation. * - * When an application passes 0 to delta_time, ecs_progress will automatically + * When an application passes 0 to delta_time, ecs_progress() will automatically * measure the time passed since the last frame. If an application does not uses * time management, it should pass a non-zero value for delta_time (1.0 is * recommended). That way, no time will be wasted measuring the time. * * @param world The world to progress. * @param delta_time The time passed since the last frame. - * @return false if ecs_quit has been called, true otherwise. + * @return false if ecs_quit() has been called, true otherwise. */ FLECS_API bool ecs_progress( ecs_world_t *world, - ecs_ftime_t delta_time); + ecs_ftime_t delta_time); /** Set time scale. * Increase or decrease simulation speed by the provided multiplier. @@ -134,7 +176,7 @@ bool ecs_progress( * @param world The world. * @param scale The scale to apply (default = 1). */ -FLECS_API +FLECS_API void ecs_set_time_scale( ecs_world_t *world, ecs_ftime_t scale); @@ -154,16 +196,17 @@ void ecs_reset_clock( * pipeline manages staging and, if necessary, synchronization between threads. * * If 0 is provided for the pipeline id, the default pipeline will be ran (this - * is either the builtin pipeline or the pipeline set with set_pipeline()). + * is either the builtin pipeline or the pipeline set with set_pipeline()). * * When using progress() this operation will be invoked automatically for the - * default pipeline (either the builtin pipeline or the pipeline set with + * default pipeline (either the builtin pipeline or the pipeline set with * set_pipeline()). An application may run additional pipelines. * * @param world The world. * @param pipeline The pipeline to run. + * @param delta_time The delta_time to pass to systems. */ -FLECS_API +FLECS_API void ecs_run_pipeline( ecs_world_t *world, ecs_entity_t pipeline, @@ -178,33 +221,44 @@ void ecs_run_pipeline( * Setting this value to a value higher than 1 will start as many threads and * will cause systems to evenly distribute matched entities across threads. The * operation may be called multiple times to reconfigure the number of threads - * used, but never while running a system / pipeline. - * Calling ecs_set_threads will also end the use of task threads setup with - * ecs_set_task_threads and vice-versa */ + * used, but never while running a system / pipeline. + * Calling ecs_set_threads() will also end the use of task threads setup with + * ecs_set_task_threads() and vice-versa. + * + * @param world The world. + * @param threads The number of threads to create. + */ FLECS_API void ecs_set_threads( ecs_world_t *world, int32_t threads); /** Set number of worker task threads. - * ecs_set_task_threads is similar to ecs_set_threads, except threads are treated - * as short-lived tasks and will be created and joined around each update of the world. + * ecs_set_task_threads() is similar to ecs_set_threads(), except threads are treated + * as short-lived tasks and will be created and joined around each update of the world. * Creation and joining of these tasks will use the os_api_t tasks APIs rather than the * the standard thread API functions, although they may be the same if desired. * This function is useful for multithreading world updates using an external * asynchronous job system rather than long running threads by providing the APIs - * to create tasks for your job system and then wait on their conclusion. + * to create tasks for your job system and then wait on their conclusion. * The operation may be called multiple times to reconfigure the number of task threads - * used, but never while running a system / pipeline. - * Calling ecs_set_task_threads will also end the use of threads setup with - * ecs_set_threads and vice-versa */ - + * used, but never while running a system / pipeline. + * Calling ecs_set_task_threads() will also end the use of threads setup with + * ecs_set_threads() and vice-versa + * + * @param world The world. + * @param task_threads The number of task threads to create. + */ FLECS_API void ecs_set_task_threads( ecs_world_t *world, int32_t task_threads); -/** Returns true if task thread use have been requested. */ +/** Returns true if task thread use have been requested. + * + * @param world The world. + * @result Whether the world is using task threads. + */ FLECS_API bool ecs_using_task_threads( ecs_world_t *world); @@ -213,6 +267,14 @@ bool ecs_using_task_threads( //// Module //////////////////////////////////////////////////////////////////////////////// +/** Pipeline module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsPipeline) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsPipelineImport( ecs_world_t *world); diff --git a/vendors/flecs/include/flecs/addons/plecs.h b/vendors/flecs/include/flecs/addons/plecs.h deleted file mode 100644 index 8b7554840..000000000 --- a/vendors/flecs/include/flecs/addons/plecs.h +++ /dev/null @@ -1,142 +0,0 @@ -/** - * @file addons/plecs.h - * @brief Flecs script module. - * - * For script, see examples/plecs. - */ - -#ifdef FLECS_PLECS - -/** - * @defgroup c_addons_plecs Flecs script - * @brief Data definition format for loading entity data. - * - * \ingroup c_addons - * @{ - */ - -#ifndef FLECS_MODULE -#define FLECS_MODULE -#endif - -#ifndef FLECS_PARSER -#define FLECS_PARSER -#endif - -#ifndef FLECS_EXPR -#define FLECS_EXPR -#endif - -#ifndef FLECS_PLECS_H -#define FLECS_PLECS_H - -#ifdef __cplusplus -extern "C" { -#endif - -FLECS_API -extern ECS_COMPONENT_DECLARE(EcsScript); - -/* Script component */ -typedef struct EcsScript { - ecs_vec_t using_; - char *script; - ecs_vec_t prop_defaults; - ecs_world_t *world; -} EcsScript; - -/** Parse plecs string. - * This parses a plecs string and instantiates the entities in the world. - * - * @param world The world. - * @param name The script name (typically the file). - * @param str The plecs string. - * @return Zero if success, non-zero otherwise. - */ -FLECS_API -int ecs_plecs_from_str( - ecs_world_t *world, - const char *name, - const char *str); - -/** Parse plecs file. - * This parses a plecs file and instantiates the entities in the world. This - * operation is equivalent to loading the file contents and passing it to - * ecs_plecs_from_str. - * - * @param world The world. - * @param filename The plecs file name. - * @return Zero if success, non-zero otherwise. - */ -FLECS_API -int ecs_plecs_from_file( - ecs_world_t *world, - const char *filename); - -/** Used with ecs_script_init */ -typedef struct ecs_script_desc_t { - ecs_entity_t entity; /* Set to customize entity handle associated with script */ - const char *filename; /* Set to load script from file */ - const char *str; /* Set to parse script from string */ -} ecs_script_desc_t; - -/** Load managed script. - * A managed script tracks which entities it creates, and keeps those entities - * synchronized when the contents of the script are updated. When the script is - * updated, entities that are no longer in the new version will be deleted. - * - * This feature is experimental. - * - * @param world The world. - * @param desc Script descriptor. - */ -FLECS_API -ecs_entity_t ecs_script_init( - ecs_world_t *world, - const ecs_script_desc_t *desc); - -#define ecs_script(world, ...)\ - ecs_script_init(world, &(ecs_script_desc_t) __VA_ARGS__) - -/** Update script with new code. - * - * @param world The world. - * @param script The script entity. - * @param instance An assembly instance (optional). - * @param str The script code. - * @param vars Optional preset variables for script parameterization. - */ -FLECS_API -int ecs_script_update( - ecs_world_t *world, - ecs_entity_t script, - ecs_entity_t instance, - const char *str, - ecs_vars_t *vars); - -/** Clear all entities associated with script. - * - * @param world The world. - * @param script The script entity. - * @param instance The script instance. - */ -FLECS_API -void ecs_script_clear( - ecs_world_t *world, - ecs_entity_t script, - ecs_entity_t instance); - -/* Module import */ -FLECS_API -void FlecsScriptImport( - ecs_world_t *world); - -#ifdef __cplusplus -} -#endif - -#endif - -/** @} */ - -#endif diff --git a/vendors/flecs/include/flecs/addons/rest.h b/vendors/flecs/include/flecs/addons/rest.h index f034a05d5..772d926aa 100644 --- a/vendors/flecs/include/flecs/addons/rest.h +++ b/vendors/flecs/include/flecs/addons/rest.h @@ -4,17 +4,17 @@ * * A small REST API that uses the HTTP server and JSON serializer to provide * access to application data for remote applications. - * - * A description of the API can be found in docs/RestApi.md + * + * A description of the API can be found in docs/FlecsRemoteApi.md */ #ifdef FLECS_REST /** * @defgroup c_addons_rest Rest - * @brief REST API for querying and mutating entities. - * - * \ingroup c_addons + * @ingroup c_addons + * REST API for querying and mutating entities. + * * @{ */ @@ -28,11 +28,6 @@ #define FLECS_JSON #endif -/* Query engine used */ -#ifndef FLECS_RULES -#define FLECS_RULES -#endif - /* For the REST system */ #ifndef FLECS_PIPELINE #define FLECS_PIPELINE @@ -47,19 +42,20 @@ extern "C" { #define ECS_REST_DEFAULT_PORT (27750) -/** Component that instantiates the REST API */ +/** Component that instantiates the REST API. */ FLECS_API extern const ecs_entity_t ecs_id(EcsRest); +/** Component that creates a REST API server when instantiated. */ typedef struct { uint16_t port; /**< Port of server (optional, default = 27750) */ char *ipaddr; /**< Interface address (optional, default = 0.0.0.0) */ void *impl; } EcsRest; -/** Create HTTP server for REST API. +/** Create HTTP server for REST API. * This allows for the creation of a REST server that can be managed by the * application without using Flecs systems. - * + * * @param world The world. * @param desc The HTTP server descriptor. * @return The HTTP server, or NULL if failed. @@ -69,14 +65,21 @@ ecs_http_server_t* ecs_rest_server_init( ecs_world_t *world, const ecs_http_server_desc_t *desc); -/** Cleanup REST HTTP server. - * The server must have been created with ecs_rest_server_init. +/** Cleanup REST HTTP server. + * The server must have been created with ecs_rest_server_init(). */ FLECS_API void ecs_rest_server_fini( ecs_http_server_t *srv); -/* Module import */ +/** Rest module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsRest) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsRestImport( ecs_world_t *world); diff --git a/vendors/flecs/include/flecs/addons/rules.h b/vendors/flecs/include/flecs/addons/rules.h deleted file mode 100644 index 49eabdfd5..000000000 --- a/vendors/flecs/include/flecs/addons/rules.h +++ /dev/null @@ -1,233 +0,0 @@ -/** - * @file addons/rules.h - * @brief Rule query engine addon. - * - * Rules are advanced queries that in addition to the capabilities of regular - * queries and filters have the following features: - * - * - query for all components of an entity (vs. all entities for a component) - * - query for all relationship pairs of an entity - * - support for query variables that are resolved at evaluation time - * - automatic traversal of transitive relationships - */ - -#ifdef FLECS_RULES - -/** - * @defgroup c_addons_rules Rules - * @brief Rules are an advanced query engine for matching against entity graphs. - * - * \ingroup c_addons - * @{ - */ - -#ifndef FLECS_RULES_H -#define FLECS_RULES_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** Convenience macro for rule creation */ -#define ecs_rule(world, ...)\ - ecs_rule_init(world, &(ecs_filter_desc_t) __VA_ARGS__ ) - -/** Create a rule. - * A rule accepts the same descriptor as a filter, but has the additional - * ability to use query variables. - * - * Query variables can be used to constrain wildcards across multiple terms to - * the same entity. Regular ECS queries do this in a limited form, as querying - * for Position, Velocity only returns entities that have both components. - * - * Query variables expand this to constrain entities that are resolved while the - * query is being matched. Consider a query for all entities and the mission - * they are on: - * (Mission, *) - * - * If an entity is on multiple missions, the wildcard will match it multiple - * times. Now say we want to only list combat missions. Naively we could try: - * (Mission, *), CombatMission(*) - * - * But this doesn't work, as term 1 returns entities with missions, and term 2 - * returns all combat missions for all entities. Query variables make it - * possible to apply CombatMission to the found mission: - * (Mission, $M), CombatMission($M) - * - * By using the same variable ('M') we ensure that CombatMission is applied to - * the mission found in the current result. - * - * Variables can be used in each part of the term (predicate, subject, object). - * This is a valid query: - * Likes($X, $Y), Likes($Y, $X) - * - * This is also a valid query: - * _Component, Serializable(_Component) - * - * In the query expression syntax, variables are prefixed with a $. When using - * the descriptor, specify the variable kind: - * desc.terms[0].second = { .name = "X", .var = EcsVarIsVariable } - * - * Different terms with the same variable name are automatically correlated by - * the query engine. - * - * A rule needs to be explicitly deleted with ecs_rule_fini. - * - * @param world The world. - * @param desc The descriptor (see ecs_filter_desc_t) - * @return The rule. - */ -FLECS_API -ecs_rule_t* ecs_rule_init( - ecs_world_t *world, - const ecs_filter_desc_t *desc); - -/** Delete a rule. - * - * @param rule The rule. - */ -FLECS_API -void ecs_rule_fini( - ecs_rule_t *rule); - -/** Obtain filter from rule. - * This operation returns the filter with which the rule was created. - * - * @param rule The rule. - * @return The filter. - */ -FLECS_API -const ecs_filter_t* ecs_rule_get_filter( - const ecs_rule_t *rule); - -/** Return number of variables in rule. - * - * @param rule The rule. - * @return The number of variables/ - */ -FLECS_API -int32_t ecs_rule_var_count( - const ecs_rule_t *rule); - -/** Find variable index. - * This operation looks up the index of a variable in the rule. This index can - * be used in operations like ecs_iter_set_var and ecs_iter_get_var. - * - * @param rule The rule. - * @param name The variable name. - * @return The variable index. - */ -FLECS_API -int32_t ecs_rule_find_var( - const ecs_rule_t *rule, - const char *name); - -/** Get variable name. - * This operation returns the variable name for an index. - * - * @param rule The rule. - * @param var_id The variable index. - */ -FLECS_API -const char* ecs_rule_var_name( - const ecs_rule_t *rule, - int32_t var_id); - -/** Test if variable is an entity. - * Internally the rule engine has entity variables and table variables. When - * iterating through rule variables (by using ecs_rule_variable_count) only - * the values for entity variables are accessible. This operation enables an - * application to check if a variable is an entity variable. - * - * @param rule The rule. - * @param var_id The variable id. - */ -FLECS_API -bool ecs_rule_var_is_entity( - const ecs_rule_t *rule, - int32_t var_id); - -/** Iterate a rule. - * Note that rule iterators may allocate memory, and that unless the iterator - * is iterated until completion, it may still hold resources. When stopping - * iteration before ecs_rule_next has returned false, use ecs_iter_fini to - * cleanup any remaining resources. - * - * @param world The world. - * @param rule The rule. - * @return An iterator. - */ -FLECS_API -ecs_iter_t ecs_rule_iter( - const ecs_world_t *world, - const ecs_rule_t *rule); - -/** Progress rule iterator. - * - * @param it The iterator. - */ -FLECS_API -bool ecs_rule_next( - ecs_iter_t *it); - -/** Progress instanced iterator. - * Should not be called unless you know what you're doing :-) - * - * @param it The iterator. - */ -FLECS_API -bool ecs_rule_next_instanced( - ecs_iter_t *it); - -/** Convert rule to a string. - * This will convert the rule program to a string which can aid in debugging - * the behavior of a rule. - * - * The returned string must be freed with ecs_os_free. - * - * @param rule The rule. - * @return The string - */ -FLECS_API -char* ecs_rule_str( - const ecs_rule_t *rule); - -/** Convert rule to string with profile. - * To use this you must set the EcsIterProfile flag on an iterator before - * starting iteration: - * it.flags |= EcsIterProfile - * - * @param rule The rule. - * @return The string - */ -FLECS_API -char* ecs_rule_str_w_profile( - const ecs_rule_t *rule, - const ecs_iter_t *it); - -/** Populate variables from key-value string. - * Convenience function to set rule variables from a key-value string separated - * by comma's. The string must have the following format: - * var_a: value, var_b: value - * - * The key-value list may optionally be enclosed in parenthesis. - * - * @param rule The rule. - * @param it The iterator for which to set the variables. - * @param expr The key-value expression. - */ -FLECS_API -const char* ecs_rule_parse_vars( - ecs_rule_t *rule, - ecs_iter_t *it, - const char *expr); - -#ifdef __cplusplus -} -#endif - -#endif // FLECS_RULES_H - -/** @} */ - -#endif // FLECS_RULES diff --git a/vendors/flecs/include/flecs/addons/script.h b/vendors/flecs/include/flecs/addons/script.h new file mode 100644 index 000000000..f123249b1 --- /dev/null +++ b/vendors/flecs/include/flecs/addons/script.h @@ -0,0 +1,521 @@ +/** + * @file addons/script.h + * @brief Flecs script module. + * + * For script, see examples/script. + */ + +#ifdef FLECS_SCRIPT + +/** + * @defgroup c_addons_script Flecs script + * @ingroup c_addons + * DSL for loading scenes, assets and configuration. + * + * @{ + */ + +#ifndef FLECS_META +#define FLECS_META +#endif + +#ifndef FLECS_DOC +#define FLECS_DOC +#endif + + +#ifndef FLECS_SCRIPT_H +#define FLECS_SCRIPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +FLECS_API +extern ECS_COMPONENT_DECLARE(EcsScript); + +typedef struct ecs_script_template_t ecs_script_template_t; + +/** Script variable. */ +typedef struct ecs_script_var_t { + const char *name; + ecs_value_t value; + const ecs_type_info_t *type_info; +} ecs_script_var_t; + +/** Script variable scope. */ +typedef struct ecs_script_vars_t { + struct ecs_script_vars_t *parent; + ecs_hashmap_t var_index; + ecs_vec_t vars; + + const ecs_world_t *world; + struct ecs_stack_t *stack; + ecs_stack_cursor_t *cursor; + ecs_allocator_t *allocator; +} ecs_script_vars_t; + +/** Script object. */ +typedef struct ecs_script_t { + ecs_world_t *world; + const char *name; + const char *code; +} ecs_script_t; + +/** Script component. + * This component is added to the entities of managed scripts and templates. + */ +typedef struct EcsScript { + ecs_script_t *script; + ecs_script_template_t *template_; /* Only set for template scripts */ +} EcsScript; + + +/* Parsing & running scripts */ + +/** Parse script. + * This operation parses a script and returns a script object upon success. To + * run the script, call ecs_script_eval(). + * + * @param world The world. + * @param name Name of the script (typically a file/module name). + * @param code The script code. + * @return Script object if success, NULL if failed. +*/ +FLECS_API +ecs_script_t* ecs_script_parse( + ecs_world_t *world, + const char *name, + const char *code); + +/** Evaluate script. + * This operation evaluates (runs) a parsed script. + * + * @param script The script. + * @return Zero if success, non-zero if failed. +*/ +FLECS_API +int ecs_script_eval( + ecs_script_t *script); + +/** Free script. + * This operation frees a script object. + * + * Templates created by the script rely upon resources in the script object, + * and for that reason keep the script alive until all templates created by the + * script are deleted. + * + * @param script The script. + */ +FLECS_API +void ecs_script_free( + ecs_script_t *script); + +/** Parse script. + * This parses a script and instantiates the entities in the world. + * This operation is the equivalent to doing: + * + * @code + * ecs_script_t *script = ecs_script_parse(world, name, code); + * ecs_script_eval(script); + * ecs_script_free(script); + * @endcode + * + * @param world The world. + * @param name The script name (typically the file). + * @param code The script. + * @return Zero if success, non-zero otherwise. + */ +FLECS_API +int ecs_script_run( + ecs_world_t *world, + const char *name, + const char *code); + +/** Parse script file. + * This parses a script file and instantiates the entities in the world. This + * operation is equivalent to loading the file contents and passing it to + * ecs_script_run(). + * + * @param world The world. + * @param filename The script file name. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_script_run_file( + ecs_world_t *world, + const char *filename); + +/** Convert script AST to string. + * This operation converts the script abstract syntax tree to a string, which + * can be used to debug a script. + * + * @param script The script. + * @param buf The buffer to write to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_script_ast_to_buf( + ecs_script_t *script, + ecs_strbuf_t *buf); + +/** Convert script AST to string. + * This operation converts the script abstract syntax tree to a string, which + * can be used to debug a script. + * + * @param script The script. + * @return The string if success, NULL if failed. + */ +FLECS_API +char* ecs_script_ast_to_str( + ecs_script_t *script); + + +/* Managed scripts (script associated with entity that outlives the function) */ + +/** Used with ecs_script_init() */ +typedef struct ecs_script_desc_t { + ecs_entity_t entity; /* Set to customize entity handle associated with script */ + const char *filename; /* Set to load script from file */ + const char *code; /* Set to parse script from string */ +} ecs_script_desc_t; + +/** Load managed script. + * A managed script tracks which entities it creates, and keeps those entities + * synchronized when the contents of the script are updated. When the script is + * updated, entities that are no longer in the new version will be deleted. + * + * This feature is experimental. + * + * @param world The world. + * @param desc Script descriptor. + */ +FLECS_API +ecs_entity_t ecs_script_init( + ecs_world_t *world, + const ecs_script_desc_t *desc); + +#define ecs_script(world, ...)\ + ecs_script_init(world, &(ecs_script_desc_t) __VA_ARGS__) + +/** Update script with new code. + * + * @param world The world. + * @param script The script entity. + * @param instance An template instance (optional). + * @param code The script code. + */ +FLECS_API +int ecs_script_update( + ecs_world_t *world, + ecs_entity_t script, + ecs_entity_t instance, + const char *code); + +/** Clear all entities associated with script. + * + * @param world The world. + * @param script The script entity. + * @param instance The script instance. + */ +FLECS_API +void ecs_script_clear( + ecs_world_t *world, + ecs_entity_t script, + ecs_entity_t instance); + + +/* Script variables */ + +/** Create new variable scope. + * Create root variable scope. A variable scope contains one or more variables. + * Scopes can be nested, which allows variables in different scopes to have the + * same name. Variables from parent scopes will be shadowed by variables in + * child scopes with the same name. + * + * Use the `ecs_script_vars_push()` and `ecs_script_vars_pop()` functions to + * push and pop variable scopes. + * + * When a variable contains allocated resources (e.g. a string), its resources + * will be freed when `ecs_script_vars_pop()` is called on the scope, the + * ecs_script_vars_t::type_info field is initialized for the variable, and + * `ecs_type_info_t::hooks::dtor` is set. + * + * @param world The world. + */ +FLECS_API +ecs_script_vars_t* ecs_script_vars_init( + ecs_world_t *world); + +/** Free variable scope. + * Free root variable scope. The provided scope should not have a parent. This + * operation calls `ecs_script_vars_pop()` on the scope. + * + * @param vars The variable scope. + */ +FLECS_API +void ecs_script_vars_fini( + ecs_script_vars_t *vars); + +/** Push new variable scope. + * + * Scopes created with ecs_script_vars_push() must be cleaned up with + * ecs_script_vars_pop(). + * + * If the stack and allocator arguments are left to NULL, their values will be + * copied from the parent. + * + * @param parent The parent scope (provide NULL for root scope). + * @return The new variable scope. + */ +FLECS_API +ecs_script_vars_t* ecs_script_vars_push( + ecs_script_vars_t *parent); + +/** Pop variable scope. + * This frees up the resources for a variable scope. The scope must be at the + * top of a vars stack. Calling ecs_script_vars_pop() on a scope that is not the + * last scope causes undefined behavior. + * + * @param vars The scope to free. + * @return The parent scope. + */ +FLECS_API +ecs_script_vars_t* ecs_script_vars_pop( + ecs_script_vars_t *vars); + +/** Declare a variable. + * This operation declares a new variable in the current scope. If a variable + * with the specified name already exists, the operation will fail. + * + * This operation does not allocate storage for the variable. This is done to + * allow for variables that point to existing storage, which prevents having + * to copy existing values to a variable scope. + * + * @param vars The variable scope. + * @param name The variable name. + * @return The new variable, or NULL if the operation failed. + */ +FLECS_API +ecs_script_var_t* ecs_script_vars_declare( + ecs_script_vars_t *vars, + const char *name); + +/** Define a variable. + * This operation calls `ecs_script_vars_declare()` and allocates storage for + * the variable. If the type has a ctor, it will be called on the new storage. + * + * The scope's stack allocator will be used to allocate the storage. After + * `ecs_script_vars_pop()` is called on the scope, the variable storage will no + * longer be valid. + * + * The operation will fail if the type argument is not a type. + * + * @param vars The variable scope. + * @param name The variable name. + * @param type The variable type. + * @return The new variable, or NULL if the operation failed. + */ +FLECS_API +ecs_script_var_t* ecs_script_vars_define_id( + ecs_script_vars_t *vars, + const char *name, + ecs_entity_t type); + +#define ecs_script_vars_define(vars, name, type)\ + ecs_script_vars_define_id(vars, name, ecs_id(type)) + +/** Lookup a variable. + * This operation looks up a variable in the current scope. If the variable + * can't be found in the current scope, the operation will recursively search + * the parent scopes. + * + * @param vars The variable scope. + * @param name The variable name. + * @return The variable, or NULL if one with the provided name does not exist. + */ +FLECS_API +ecs_script_var_t* ecs_script_vars_lookup( + const ecs_script_vars_t *vars, + const char *name); + +/** Convert iterator to vars + * This operation converts an iterator to a variable array. This allows for + * using iterator results in expressions. The operation only converts a + * single result at a time, and does not progress the iterator. + * + * Iterator fields with data will be made available as variables with as name + * the field index (e.g. "$1"). The operation does not check if reflection data + * is registered for a field type. If no reflection data is registered for the + * type, using the field variable in expressions will fail. + * + * Field variables will only contain single elements, even if the iterator + * returns component arrays. The offset parameter can be used to specify which + * element in the component arrays to return. The offset parameter must be + * smaller than it->count. + * + * The operation will create a variable for query variables that contain a + * single entity. + * + * The operation will attempt to use existing variables. If a variable does not + * yet exist, the operation will create it. If an existing variable exists with + * a mismatching type, the operation will fail. + * + * Accessing variables after progressing the iterator or after the iterator is + * destroyed will result in undefined behavior. + * + * If vars contains a variable that is not present in the iterator, the variable + * will not be modified. + * + * @param it The iterator to convert to variables. + * @param vars The variables to write to. + * @param offset The offset to the current element. + */ +FLECS_API +void ecs_script_vars_from_iter( + const ecs_iter_t *it, + ecs_script_vars_t *vars, + int offset); + + +/* Standalone expression evaluation */ + +/** Used with ecs_script_expr_run(). */ +typedef struct ecs_script_expr_run_desc_t { + const char *name; + const char *expr; + ecs_entity_t (*lookup_action)( + const ecs_world_t*, + const char *value, + void *ctx); + void *lookup_ctx; + ecs_script_vars_t *vars; +} ecs_script_expr_run_desc_t; + +/** Parse standalone expression into value. + * This operation parses a flecs expression into the provided pointer. The + * memory pointed to must be large enough to contain a value of the used type. + * + * If no type and pointer are provided for the value argument, the operation + * will discover the type from the expression and allocate storage for the + * value. The allocated value must be freed with ecs_value_free(). + * + * @param world The world. + * @param ptr The pointer to the expression to parse. + * @param value The value containing type & pointer to write to. + * @param desc Configuration parameters for deserializer. + * @return Pointer to the character after the last one read, or NULL if failed. + */ +FLECS_API +const char* ecs_script_expr_run( + ecs_world_t *world, + const char *ptr, + ecs_value_t *value, + const ecs_script_expr_run_desc_t *desc); + +/** Evaluate interpolated expressions in string. + * This operation evaluates expressions in a string, and replaces them with + * their evaluated result. Supported expression formats are: + * - $variable_name + * - {expression} + * + * The $, { and } characters can be escaped with a backslash (\). + * + * @param world The world. + * @param str The string to evaluate. + * @param vars The variables to use for evaluation. + */ +FLECS_API +char* ecs_script_string_interpolate( + ecs_world_t *world, + const char *str, + const ecs_script_vars_t *vars); + + +/* Value serialization */ + +/** Serialize value into expression string. + * This operation serializes a value of the provided type to a string. The + * memory pointed to must be large enough to contain a value of the used type. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @return String with expression, or NULL if failed. + */ +FLECS_API +char* ecs_ptr_to_expr( + const ecs_world_t *world, + ecs_entity_t type, + const void *data); + +/** Serialize value into expression buffer. + * Same as ecs_ptr_to_expr(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @param buf The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_ptr_to_expr_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *data, + ecs_strbuf_t *buf); + +/** Similar as ecs_ptr_to_expr(), but serializes values to string. + * Whereas the output of ecs_ptr_to_expr() is a valid expression, the output of + * ecs_ptr_to_str() is a string representation of the value. In most cases the + * output of the two operations is the same, but there are some differences: + * - Strings are not quoted + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @return String with result, or NULL if failed. + */ +FLECS_API +char* ecs_ptr_to_str( + const ecs_world_t *world, + ecs_entity_t type, + const void *data); + +/** Serialize value into string buffer. + * Same as ecs_ptr_to_str(), but serializes to an ecs_strbuf_t instance. + * + * @param world The world. + * @param type The type of the value to serialize. + * @param data The value to serialize. + * @param buf The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_ptr_to_str_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *data, + ecs_strbuf_t *buf); + +/** Script module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsScript) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsScriptImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif diff --git a/vendors/flecs/include/flecs/addons/snapshot.h b/vendors/flecs/include/flecs/addons/snapshot.h deleted file mode 100644 index c4a77aef5..000000000 --- a/vendors/flecs/include/flecs/addons/snapshot.h +++ /dev/null @@ -1,107 +0,0 @@ -/** - * @file addons/snapshot.h - * @brief Snapshot addon. - * - * A snapshot records the state of a world in a way so that it can be restored - * later. Snapshots work with POD components and non-POD components, provided - * that the appropriate lifecycle actions are registered for non-POD components. - * - * A snapshot is tightly coupled to a world. It is not possible to restore a - * snapshot from world A into world B. - */ - -#ifdef FLECS_SNAPSHOT - -/** - * @defgroup c_addons_snapshot Snapshot - * @brief Save & restore world. - * - * \ingroup c_addons - * @{ - */ - -#ifndef FLECS_SNAPSHOT_H -#define FLECS_SNAPSHOT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** A snapshot stores the state of a world in a particular point in time. */ -typedef struct ecs_snapshot_t ecs_snapshot_t; - -/** Create a snapshot. - * This operation makes a copy of the current state of the world. - * - * @param world The world to snapshot. - * @return The snapshot. - */ -FLECS_API -ecs_snapshot_t* ecs_snapshot_take( - ecs_world_t *world); - -/** Create a filtered snapshot. - * This operation is the same as ecs_snapshot_take, but accepts an iterator so - * an application can control what is stored by the snapshot. - * - * @param iter An iterator to the data to be stored by the snapshot. - * @return The snapshot. - */ -FLECS_API -ecs_snapshot_t* ecs_snapshot_take_w_iter( - ecs_iter_t *iter); - -/** Restore a snapshot. - * This operation restores the world to the state it was in when the specified - * snapshot was taken. A snapshot can only be used once for restoring, as its - * data replaces the data that is currently in the world. - * This operation also resets the last issued entity handle, so any calls to - * ecs_new may return entity ids that have been issued before restoring the - * snapshot. - * - * The world in which the snapshot is restored must be the same as the world in - * which the snapshot is taken. - * - * @param world The world to restore the snapshot to. - * @param snapshot The snapshot to restore. - */ -FLECS_API -void ecs_snapshot_restore( - ecs_world_t *world, - ecs_snapshot_t *snapshot); - -/** Obtain iterator to snapshot data. - * - * @param snapshot The snapshot to iterate over. - * @return Iterator to snapshot data. */ -FLECS_API -ecs_iter_t ecs_snapshot_iter( - ecs_snapshot_t *snapshot); - -/** Progress snapshot iterator. - * - * @param iter The snapshot iterator. - * @return True if more data is available, otherwise false. - */ -FLECS_API -bool ecs_snapshot_next( - ecs_iter_t *iter); - -/** Free snapshot resources. - * This frees resources associated with a snapshot without restoring it. - * - * @param snapshot The snapshot to free. - */ -FLECS_API -void ecs_snapshot_free( - ecs_snapshot_t *snapshot); - -#ifdef __cplusplus -} -#endif - -#endif - -/** @} */ - -#endif diff --git a/vendors/flecs/include/flecs/addons/stats.h b/vendors/flecs/include/flecs/addons/stats.h index dc53c44eb..ccfb90fb7 100644 --- a/vendors/flecs/include/flecs/addons/stats.h +++ b/vendors/flecs/include/flecs/addons/stats.h @@ -2,23 +2,37 @@ * @file addons/stats.h * @brief Statistics addon. * - * The statistics addon enables an application to obtain detailed metrics about - * the storage, systems and operations of a world. + * The stats addon tracks high resolution statistics for the world, systems and + * pipelines. The addon can be used as an API where an application calls + * functions to obtain statistics directly and as a module where statistics are + * automatically tracked. The latter is required for statistics tracking in the + * explorer. + * + * When the addon is imported as module, statistics are tracked for each frame, + * second, minute, hour, day and week with 60 datapoints per tier. */ #ifdef FLECS_STATS /** * @defgroup c_addons_stats Stats - * @brief Collection of statistics for world, queries, systems and pipelines. - * - * \ingroup c_addons + * @ingroup c_addons + * Collection of statistics for world, queries, systems and pipelines. + * * @{ */ #ifndef FLECS_STATS_H #define FLECS_STATS_H +#ifndef FLECS_MODULE +#define FLECS_MODULE +#endif + +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE +#endif + #ifdef __cplusplus extern "C" { #endif @@ -85,7 +99,7 @@ typedef struct ecs_world_stats_t { ecs_metric_t delete_count; ecs_metric_t clear_count; ecs_metric_t set_count; - ecs_metric_t get_mut_count; + ecs_metric_t ensure_count; ecs_metric_t modified_count; ecs_metric_t other_count; ecs_metric_t discard_count; @@ -148,15 +162,15 @@ typedef struct ecs_world_stats_t { int64_t last_; - /** Current position in ringbuffer */ + /** Current position in ring buffer */ int32_t t; } ecs_world_stats_t; -/** Statistics for a single query (use ecs_query_stats_get) */ +/** Statistics for a single query (use ecs_query_cache_stats_get) */ typedef struct ecs_query_stats_t { int64_t first_; - ecs_metric_t matched_table_count; /**< Matched non-empty tables */ - ecs_metric_t matched_empty_table_count; /**< Matched empty tables */ + ecs_metric_t result_count; /**< Number of query results */ + ecs_metric_t matched_table_count; /**< Number of matched tables */ ecs_metric_t matched_entity_count; /**< Number of matched entities */ int64_t last_; @@ -164,11 +178,10 @@ typedef struct ecs_query_stats_t { int32_t t; } ecs_query_stats_t; -/** Statistics for a single system (use ecs_system_stats_get) */ +/** Statistics for a single system (use ecs_system_stats_get()) */ typedef struct ecs_system_stats_t { int64_t first_; ecs_metric_t time_spent; /**< Time spent processing a system */ - ecs_metric_t invoke_count; /**< Number of times system is invoked */ int64_t last_; bool task; /**< Is system a task */ @@ -185,7 +198,7 @@ typedef struct ecs_sync_stats_t { int32_t system_count; bool multi_threaded; - bool no_readonly; + bool immediate; } ecs_sync_stats_t; /** Statistics for all systems in a pipeline. */ @@ -196,15 +209,11 @@ typedef struct ecs_pipeline_stats_t { /** Vector with system ids of all systems in the pipeline. The systems are * stored in the order they are executed. Merges are represented by a 0. */ ecs_vec_t systems; - + /** Vector with sync point stats */ ecs_vec_t sync_points; - /** Map with system statistics. For each system in the systems vector, an - * entry in the map exists of type ecs_system_stats_t. */ - ecs_map_t system_stats; - - /** Current position in ringbuffer */ + /** Current position in ring buffer */ int32_t t; int32_t system_count; /**< Number of systems in pipeline */ @@ -217,13 +226,13 @@ typedef struct ecs_pipeline_stats_t { * @param world The world. * @param stats Out parameter for statistics. */ -FLECS_API +FLECS_API void ecs_world_stats_get( const ecs_world_t *world, ecs_world_stats_t *stats); /** Reduce source measurement window into single destination measurement. */ -FLECS_API +FLECS_API void ecs_world_stats_reduce( ecs_world_stats_t *dst, const ecs_world_stats_t *src); @@ -246,7 +255,7 @@ void ecs_world_stats_copy_last( ecs_world_stats_t *dst, const ecs_world_stats_t *src); -FLECS_API +FLECS_API void ecs_world_stats_log( const ecs_world_t *world, const ecs_world_stats_t *stats); @@ -258,7 +267,7 @@ void ecs_world_stats_log( * @param query The query. * @param stats Out parameter for statistics. */ -FLECS_API +FLECS_API void ecs_query_stats_get( const ecs_world_t *world, const ecs_query_t *query, @@ -266,29 +275,28 @@ void ecs_query_stats_get( /** Reduce source measurement window into single destination measurement. */ FLECS_API -void ecs_query_stats_reduce( +void ecs_query_cache_stats_reduce( ecs_query_stats_t *dst, const ecs_query_stats_t *src); /** Reduce last measurement into previous measurement, restore old value. */ FLECS_API -void ecs_query_stats_reduce_last( +void ecs_query_cache_stats_reduce_last( ecs_query_stats_t *stats, const ecs_query_stats_t *old, int32_t count); /** Repeat last measurement. */ FLECS_API -void ecs_query_stats_repeat_last( +void ecs_query_cache_stats_repeat_last( ecs_query_stats_t *stats); /** Copy last measurement from source to destination. */ FLECS_API -void ecs_query_stats_copy_last( +void ecs_query_cache_stats_copy_last( ecs_query_stats_t *dst, const ecs_query_stats_t *src); -#ifdef FLECS_SYSTEM /** Get system statistics. * Obtain statistics for the provided system. * @@ -297,14 +305,14 @@ void ecs_query_stats_copy_last( * @param stats Out parameter for statistics. * @return true if success, false if not a system. */ -FLECS_API +FLECS_API bool ecs_system_stats_get( const ecs_world_t *world, ecs_entity_t system, ecs_system_stats_t *stats); /** Reduce source measurement window into single destination measurement */ -FLECS_API +FLECS_API void ecs_system_stats_reduce( ecs_system_stats_t *dst, const ecs_system_stats_t *src); @@ -326,9 +334,7 @@ FLECS_API void ecs_system_stats_copy_last( ecs_system_stats_t *dst, const ecs_system_stats_t *src); -#endif -#ifdef FLECS_PIPELINE /** Get pipeline statistics. * Obtain statistics for the provided pipeline. * @@ -337,14 +343,14 @@ void ecs_system_stats_copy_last( * @param stats Out parameter for statistics. * @return true if success, false if not a pipeline. */ -FLECS_API +FLECS_API bool ecs_pipeline_stats_get( ecs_world_t *world, ecs_entity_t pipeline, ecs_pipeline_stats_t *stats); /** Free pipeline stats. - * + * * @param stats The stats to free. */ FLECS_API @@ -352,7 +358,7 @@ void ecs_pipeline_stats_fini( ecs_pipeline_stats_t *stats); /** Reduce source measurement window into single destination measurement */ -FLECS_API +FLECS_API void ecs_pipeline_stats_reduce( ecs_pipeline_stats_t *dst, const ecs_pipeline_stats_t *src); @@ -372,7 +378,7 @@ void ecs_pipeline_stats_repeat_last( /** Copy last measurement to destination. * This operation copies the last measurement into the destination. It does not * modify the cursor. - * + * * @param dst The metrics. * @param src The metrics to copy. */ @@ -381,10 +387,8 @@ void ecs_pipeline_stats_copy_last( ecs_pipeline_stats_t *dst, const ecs_pipeline_stats_t *src); -#endif - /** Reduce all measurements from a window into a single measurement. */ -FLECS_API +FLECS_API void ecs_metric_reduce( ecs_metric_t *dst, const ecs_metric_t *src, @@ -405,6 +409,77 @@ void ecs_metric_copy( int32_t dst, int32_t src); +FLECS_API extern ECS_COMPONENT_DECLARE(FlecsStats); /**< Flecs stats module. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldStats); /**< Component id for EcsWorldStats. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsWorldSummary); /**< Component id for EcsWorldSummary. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsSystemStats); /**< Component id for EcsSystemStats. */ +FLECS_API extern ECS_COMPONENT_DECLARE(EcsPipelineStats); /**< Component id for EcsPipelineStats. */ + +FLECS_API extern ecs_entity_t EcsPeriod1s; /**< Tag used for metrics collected in last second. */ +FLECS_API extern ecs_entity_t EcsPeriod1m; /**< Tag used for metrics collected in last minute. */ +FLECS_API extern ecs_entity_t EcsPeriod1h; /**< Tag used for metrics collected in last hour. */ +FLECS_API extern ecs_entity_t EcsPeriod1d; /**< Tag used for metrics collected in last day. */ +FLECS_API extern ecs_entity_t EcsPeriod1w; /**< Tag used for metrics collected in last week. */ + +/** Common data for statistics. */ +typedef struct { + ecs_ftime_t elapsed; + int32_t reduce_count; +} EcsStatsHeader; + +/** Component that stores world statistics. */ +typedef struct { + EcsStatsHeader hdr; + ecs_world_stats_t stats; +} EcsWorldStats; + +/** Component that stores system statistics. */ +typedef struct { + EcsStatsHeader hdr; + ecs_map_t stats; +} EcsSystemStats; + +/** Component that stores pipeline statistics. */ +typedef struct { + EcsStatsHeader hdr; + ecs_map_t stats; +} EcsPipelineStats; + +/** Component that stores a summary of world statistics. */ +typedef struct { + /* Time */ + double target_fps; /**< Target FPS */ + double time_scale; /**< Simulation time scale */ + + /* Total time */ + double frame_time_total; /**< Total time spent processing a frame */ + double system_time_total; /**< Total time spent in systems */ + double merge_time_total; /**< Total time spent in merges */ + + /* Last frame time */ + double frame_time_last; /**< Time spent processing a frame */ + double system_time_last; /**< Time spent in systems */ + double merge_time_last; /**< Time spent in merges */ + + int64_t frame_count; /**< Number of frames processed */ + int64_t command_count; /**< Number of commands processed */ + + /* Build info */ + ecs_build_info_t build_info; /**< Build info */ +} EcsWorldSummary; + +/** Stats module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsStats) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsStatsImport( + ecs_world_t *world); + #ifdef __cplusplus } #endif diff --git a/vendors/flecs/include/flecs/addons/system.h b/vendors/flecs/include/flecs/addons/system.h index 6561b51cd..38efec489 100644 --- a/vendors/flecs/include/flecs/addons/system.h +++ b/vendors/flecs/include/flecs/addons/system.h @@ -11,9 +11,9 @@ /** * @defgroup c_addons_system System - * @brief Systems are a query + function that can be ran manually or by a pipeline. - * - * \ingroup c_addons + * @ingroup c_addons + * Systems are a query + function that can be ran manually or by a pipeline. + * * @{ */ @@ -34,7 +34,7 @@ typedef struct EcsTickSource { ecs_ftime_t time_elapsed; /**< Time elapsed since last tick */ } EcsTickSource; -/** Use with ecs_system_init */ +/** Use with ecs_system_init() to create or update a system. */ typedef struct ecs_system_desc_t { int32_t _canary; @@ -44,37 +44,42 @@ typedef struct ecs_system_desc_t { /** System query parameters */ ecs_query_desc_t query; - /** Callback that is invoked when a system is ran. - * When left to NULL, the default system runner is used, which calls the - * "callback" action for each result returned from the system's query. - * - * It should not be assumed that the input iterator can always be iterated - * with ecs_query_next. When a system is multithreaded and/or paged, the - * iterator can be either a worker or paged iterator. Future use cases may - * introduce additional inputs for a system, such as rules and filters. The - * correct function to use for iteration is ecs_iter_next. - * - * An implementation can test whether the iterator is a query iterator by - * testing whether the it->next value is equal to ecs_query_next. */ - ecs_run_action_t run; - /** Callback that is ran for each result returned by the system's query. This * means that this callback can be invoked multiple times per system per * frame, typically once for each matching table. */ ecs_iter_action_t callback; + /** Callback that is invoked when a system is ran. + * When left to NULL, the default system runner is used, which calls the + * "callback" action for each result returned from the system's query. + * + * It should not be assumed that the input iterator can always be iterated + * with ecs_query_next(). When a system is multithreaded and/or paged, the + * iterator can be either a worker or paged iterator. The correct function + * to use for iteration is ecs_iter_next(). + * + * An implementation can test whether the iterator is a query iterator by + * testing whether the it->next value is equal to ecs_query_next(). */ + ecs_run_action_t run; + /** Context to be passed to callback (as ecs_iter_t::param) */ void *ctx; - /** Binding context, for when system is implemented in other language */ - void *binding_ctx; - - /** Functions that are invoked during system cleanup to free context data. - * When set, functions are called unconditionally, even when the ctx - * pointers are NULL. */ + /** Callback to free ctx. */ ecs_ctx_free_t ctx_free; - ecs_ctx_free_t binding_ctx_free; + /** Context associated with callback (for language bindings). */ + void *callback_ctx; + + /** Callback to free callback ctx. */ + ecs_ctx_free_t callback_ctx_free; + + /** Context associated with run (for language bindings). */ + void *run_ctx; + + /** Callback to free run ctx. */ + ecs_ctx_free_t run_ctx_free; + /** Interval in seconds at which the system should run */ ecs_ftime_t interval; @@ -89,7 +94,7 @@ typedef struct ecs_system_desc_t { /** If true, system will have access to the actual world. Cannot be true at the * same time as multi_threaded. */ - bool no_readonly; + bool immediate; } ecs_system_desc_t; /** Create a system */ @@ -98,35 +103,119 @@ ecs_entity_t ecs_system_init( ecs_world_t *world, const ecs_system_desc_t *desc); +/** System type, get with ecs_system_get() */ +typedef struct ecs_system_t { + ecs_header_t hdr; + + /** See ecs_system_desc_t */ + ecs_run_action_t run; + + /** See ecs_system_desc_t */ + ecs_iter_action_t action; + + /** System query */ + ecs_query_t *query; + + /** Entity associated with query */ + ecs_entity_t query_entity; + + /** Tick source associated with system */ + ecs_entity_t tick_source; + + /** Is system multithreaded */ + bool multi_threaded; + + /** Is system ran in immediate mode */ + bool immediate; + + /** Cached system name (for perf tracing) */ + const char *name; + + /** Userdata for system */ + void *ctx; + + /** Callback language binding context */ + void *callback_ctx; + + /** Run language binding context */ + void *run_ctx; + + /** Callback to free ctx. */ + ecs_ctx_free_t ctx_free; + + /** Callback to free callback ctx. */ + ecs_ctx_free_t callback_ctx_free; + + /** Callback to free run ctx. */ + ecs_ctx_free_t run_ctx_free; + + /** Time spent on running system */ + ecs_ftime_t time_spent; + + /** Time passed since last invocation */ + ecs_ftime_t time_passed; + + /** Last frame for which the system was considered */ + int64_t last_frame; + + /* Mixins */ + ecs_world_t *world; + ecs_entity_t entity; + flecs_poly_dtor_t dtor; +} ecs_system_t; + +/** Get system object. + * Returns the system object. Can be used to access various information about + * the system, like the query and context. + * + * @param world The world. + * @param system The system. + * @return The system object. + */ +FLECS_API +const ecs_system_t* ecs_system_get( + const ecs_world_t *world, + ecs_entity_t system); + #ifndef FLECS_LEGACY /** Forward declare a system. */ #define ECS_SYSTEM_DECLARE(id) ecs_entity_t ecs_id(id) /** Define a forward declared system. - * + * * Example: - * ECS_SYSTEM_DEFINE(world, Move, EcsOnUpdate, Position, Velocity); + * + * @code + * ECS_SYSTEM_DEFINE(world, Move, EcsOnUpdate, Position, Velocity); + * @endcode */ #define ECS_SYSTEM_DEFINE(world, id_, phase, ...) \ { \ ecs_system_desc_t desc = {0}; \ ecs_entity_desc_t edesc = {0}; \ + ecs_id_t add_ids[3] = {\ + ((phase) ? ecs_pair(EcsDependsOn, (phase)) : 0), \ + (phase), \ + 0 \ + };\ edesc.id = ecs_id(id_);\ edesc.name = #id_;\ - edesc.add[0] = ((phase) ? ecs_pair(EcsDependsOn, (phase)) : 0); \ - edesc.add[1] = (phase); \ + edesc.add = add_ids;\ desc.entity = ecs_entity_init(world, &edesc);\ - desc.query.filter.expr = #__VA_ARGS__; \ + desc.query.expr = #__VA_ARGS__; \ desc.callback = id_; \ ecs_id(id_) = ecs_system_init(world, &desc); \ } \ - ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, NULL) + ecs_assert(ecs_id(id_) != 0, ECS_INVALID_PARAMETER, "failed to create system %s", #id_) /** Declare & define a system. - * + * * Example: - * ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); + * + * @code + * ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); + * @endcode */ #define ECS_SYSTEM(world, id, phase, ...) \ ecs_entity_t ecs_id(id) = 0; ECS_SYSTEM_DEFINE(world, id, phase, __VA_ARGS__);\ @@ -134,20 +223,23 @@ ecs_entity_t ecs_system_init( (void)ecs_id(id);\ (void)id -/** Shorthand for creating a system with ecs_system_init. +/** Shorthand for creating a system with ecs_system_init(). * * Example: - * ecs_system(world, { - * .entity = ecs_entity(world, { - * .name = "MyEntity", - * .add = { ecs_dependson(EcsOnUpdate) } - * }), - * .query.filter.terms = { - * { ecs_id(Position) }, - * { ecs_id(Velocity) } - * }, - * .callback = Move - * }); + * + * @code + * ecs_system(world, { + * .entity = ecs_entity(world, { + * .name = "MyEntity", + * .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) + * }), + * .query.terms = { + * { ecs_id(Position) }, + * { ecs_id(Velocity) } + * }, + * .callback = Move + * }); + * @endcode */ #define ecs_system(world, ...)\ ecs_system_init(world, &(ecs_system_desc_t) __VA_ARGS__ ) @@ -159,7 +251,7 @@ ecs_entity_t ecs_system_init( * invoke logic on a set of entities, as manual systems are only matched to * tables at creation time or after creation time, when a new table is created. * - * Manual systems are useful to evaluate lists of prematched entities at + * Manual systems are useful to evaluate lists of pre-matched entities at * application defined times. Because none of the matching logic is evaluated * before the system is invoked, manual systems are much more efficient than * manually obtaining a list of entities and retrieving their components. @@ -188,8 +280,8 @@ ecs_entity_t ecs_run( ecs_ftime_t delta_time, void *param); -/** Same as ecs_run, but subdivides entities across number of provided stages. - * +/** Same as ecs_run(), but subdivides entities across number of provided stages. + * * @param world The world. * @param system The system to run. * @param stage_current The id of the current stage. @@ -207,77 +299,15 @@ ecs_entity_t ecs_run_worker( ecs_ftime_t delta_time, void *param); -/** Run system with offset/limit and type filter. - * This operation is the same as ecs_run, but filters the entities that will be - * iterated by the system. - * - * Entities can be filtered in two ways. Offset and limit control the range of - * entities that is iterated over. The range is applied to all entities matched - * with the system, thus may cover multiple archetypes. - * - * The type filter controls which entity types the system will evaluate. Only - * types that contain all components in the type filter will be iterated over. A - * type filter is only evaluated once per table, which makes filtering cheap if - * the number of entities is large and the number of tables is small, but not as - * cheap as filtering in the system signature. +/** System module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsSystem) + * @endcode * * @param world The world. - * @param system The system to invoke. - * @param delta_time The time passed since the last system invocation. - * @param param A user-defined parameter to pass to the system. - * @return handle to last evaluated entity if system was interrupted. */ FLECS_API -ecs_entity_t ecs_run_w_filter( - ecs_world_t *world, - ecs_entity_t system, - ecs_ftime_t delta_time, - int32_t offset, - int32_t limit, - void *param); - -/** Get the query object for a system. - * Systems use queries under the hood. This enables an application to get access - * to the underlying query object of a system. This can be useful when, for - * example, an application needs to enable sorting for a system. - * - * @param world The world. - * @param system The system from which to obtain the query. - * @return The query. - */ -FLECS_API -ecs_query_t* ecs_system_get_query( - const ecs_world_t *world, - ecs_entity_t system); - -/** Get system context. - * This operation returns the context pointer set for the system. If - * the provided entity is not a system, the function will return NULL. - * - * @param world The world. - * @param system The system from which to obtain the context. - * @return The context. - */ -FLECS_API -void* ecs_system_get_ctx( - const ecs_world_t *world, - ecs_entity_t system); - -/** Get system binding context. - * The binding context is a context typically used to attach any language - * binding specific data that is needed when invoking a callback that is - * implemented in another language. - * - * @param world The world. - * @param system The system from which to obtain the context. - * @return The context. - */ -FLECS_API -void* ecs_system_get_binding_ctx( - const ecs_world_t *world, - ecs_entity_t system); - -FLECS_API void FlecsSystemImport( ecs_world_t *world); diff --git a/vendors/flecs/include/flecs/addons/timer.h b/vendors/flecs/include/flecs/addons/timer.h index 514254bb2..308236043 100644 --- a/vendors/flecs/include/flecs/addons/timer.h +++ b/vendors/flecs/include/flecs/addons/timer.h @@ -10,9 +10,9 @@ /** * @defgroup c_addons_timer Timer - * @brief Run systems at a time interval. - * - * \ingroup c_addons + * @ingroup c_addons + * Run systems at a time interval. + * * @{ */ @@ -52,15 +52,15 @@ typedef struct EcsRateFilter { /** Set timer timeout. * This operation executes any systems associated with the timer after the - * specified timeout value. If the entity contains an existing timer, the - * timeout value will be reset. The timer can be started and stopped with - * ecs_start_timer and ecs_stop_timer. + * specified timeout value. If the entity contains an existing timer, the + * timeout value will be reset. The timer can be started and stopped with + * ecs_start_timer() and ecs_stop_timer(). * * The timer is synchronous, and is incremented each frame by delta_time. * * The tick_source entity will be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick - * source ticked this frame, the 'tick' member will be true. When the tick + * source ticked this frame, the 'tick' member will be true. When the tick * source is a system, the system will tick when the timer ticks. * * @param world The world. @@ -75,18 +75,18 @@ ecs_entity_t ecs_set_timeout( ecs_ftime_t timeout); /** Get current timeout value for the specified timer. - * This operation returns the value set by ecs_set_timeout. If no timer is + * This operation returns the value set by ecs_set_timeout(). If no timer is * active for this entity, the operation returns 0. * * After the timeout expires the EcsTimer component is removed from the entity. - * This means that if ecs_get_timeout is invoked after the timer is expired, the + * This means that if ecs_get_timeout() is invoked after the timer is expired, the * operation will return 0. * * The timer is synchronous, and is incremented each frame by delta_time. * * The tick_source entity will be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick - * source ticked this frame, the 'tick' member will be true. When the tick + * source ticked this frame, the 'tick' member will be true. When the tick * source is a system, the system will tick when the timer ticks. * * @param world The world. @@ -107,8 +107,8 @@ ecs_ftime_t ecs_get_timeout( * * The tick_source entity will be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick - * source ticked this frame, the 'tick' member will be true. When the tick - * source is a system, the system will tick when the timer ticks. + * source ticked this frame, the 'tick' member will be true. When the tick + * source is a system, the system will tick when the timer ticks. * * @param world The world. * @param tick_source The timer for which to set the interval (0 to create one). @@ -119,10 +119,10 @@ FLECS_API ecs_entity_t ecs_set_interval( ecs_world_t *world, ecs_entity_t tick_source, - ecs_ftime_t interval); + ecs_ftime_t interval); /** Get current interval value for the specified timer. - * This operation returns the value set by ecs_set_interval. If the entity is + * This operation returns the value set by ecs_set_interval(). If the entity is * not a timer, the operation will return 0. * * @param world The world. @@ -158,7 +158,7 @@ void ecs_stop_timer( /** Reset time value of timer to 0. * This operation resets the timer value to 0. - * + * * @param world The world. * @param tick_source The timer to reset. */ @@ -167,10 +167,10 @@ void ecs_reset_timer( ecs_world_t *world, ecs_entity_t tick_source); -/** Enable randomizing initial time value of timers. +/** Enable randomizing initial time value of timers. * Initializes timers with a random time value, which can improve scheduling as * systems/timers for the same interval don't all happen on the same tick. - * + * * @param world The world. */ FLECS_API @@ -186,20 +186,20 @@ void ecs_randomize_timers( * with interval timers alone. For example, if timer A has interval 2.0 and * timer B has interval 4.0, it is not guaranteed that B will tick at exactly * twice the multiple of A. This is partly due to the indeterministic nature of - * timers, and partly due to floating point rounding errors. + * timers, and partly due to floating point rounding errors. * - * Rate filters can be combined with timers (or other rate filters) to ensure + * Rate filters can be combined with timers (or other rate filters) to ensure * that a system ticks at an exact multiple of a tick source (which can be * another system). If a rate filter is created with a rate of 1 it will tick * at the exact same time as its source. * * If no tick source is provided, the rate filter will use the frame tick as - * source, which corresponds with the number of times ecs_progress is called. + * source, which corresponds with the number of times ecs_progress() is called. * * The tick_source entity will be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick - * source ticked this frame, the 'tick' member will be true. When the tick - * source is a system, the system will tick when the timer ticks. + * source ticked this frame, the 'tick' member will be true. When the tick + * source is a system, the system will tick when the timer ticks. * * @param world The world. * @param tick_source The rate filter entity (0 to create one). @@ -218,7 +218,7 @@ ecs_entity_t ecs_set_rate( * Systems can be their own tick source, which can be any of the tick sources * (one shot timers, interval times and rate filters). However, in some cases it * is must be guaranteed that different systems tick on the exact same frame. - * + * * This cannot be guaranteed by giving two systems the same interval/rate filter * as it is possible that one system is (for example) disabled, which would * cause the systems to go out of sync. To provide these guarantees, systems @@ -229,13 +229,13 @@ ecs_entity_t ecs_set_rate( * source, including another system. If the provided entity is not a tick source * the system will not be ran. * - * To disassociate a tick source from a system, use 0 for the tick_source + * To disassociate a tick source from a system, use 0 for the tick_source * parameter. * * @param world The world. * @param system The system to associate with the timer. * @param tick_source The tick source to associate with the system. - */ + */ FLECS_API void ecs_set_tick_source( ecs_world_t *world, @@ -247,6 +247,14 @@ void ecs_set_tick_source( //// Module //////////////////////////////////////////////////////////////////////////////// +/** Timer module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsTimer) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsTimerImport( ecs_world_t *world); diff --git a/vendors/flecs/include/flecs/addons/units.h b/vendors/flecs/include/flecs/addons/units.h index e30f5afe3..ab1d47dab 100644 --- a/vendors/flecs/include/flecs/addons/units.h +++ b/vendors/flecs/include/flecs/addons/units.h @@ -6,12 +6,18 @@ * the addon is included in the build. To import the module, do: * * In C: - * ECS_IMPORT(world, FlecsUnits); - * + * + * @code + * ECS_IMPORT(world, FlecsUnits); + * @endcode + * * In C++: - * world.import(); * - * As a result this module behaves just like an application-defined module, + * @code + * world.import(); + * @endcode + * + * As a result this module behaves just like an application-defined module, * which means that the ids generated for the entities inside the module are not * fixed, and depend on the order in which the module is imported. */ @@ -20,9 +26,9 @@ /** * @defgroup c_addons_units Units. - * @brief Common unit annotations for reflection framework. - * - * \ingroup c_addons + * @ingroup c_addons + * Common unit annotations for reflection framework. + * * @{ */ @@ -43,292 +49,298 @@ extern "C" { /** * @defgroup c_addons_units_prefixes Prefixes - * @brief Prefixes to indicate unit count (e.g. Kilo, Mega) - * - * \ingroup c_addons_units + * @ingroup c_addons_units + * Prefixes to indicate unit count (e.g. Kilo, Mega) + * * @{ */ -FLECS_API extern ECS_DECLARE(EcsUnitPrefixes); /* Parent scope for prefixes */ - -FLECS_API extern ECS_DECLARE(EcsYocto); -FLECS_API extern ECS_DECLARE(EcsZepto); -FLECS_API extern ECS_DECLARE(EcsAtto); -FLECS_API extern ECS_DECLARE(EcsFemto); -FLECS_API extern ECS_DECLARE(EcsPico); -FLECS_API extern ECS_DECLARE(EcsNano); -FLECS_API extern ECS_DECLARE(EcsMicro); -FLECS_API extern ECS_DECLARE(EcsMilli); -FLECS_API extern ECS_DECLARE(EcsCenti); -FLECS_API extern ECS_DECLARE(EcsDeci); -FLECS_API extern ECS_DECLARE(EcsDeca); -FLECS_API extern ECS_DECLARE(EcsHecto); -FLECS_API extern ECS_DECLARE(EcsKilo); -FLECS_API extern ECS_DECLARE(EcsMega); -FLECS_API extern ECS_DECLARE(EcsGiga); -FLECS_API extern ECS_DECLARE(EcsTera); -FLECS_API extern ECS_DECLARE(EcsPeta); -FLECS_API extern ECS_DECLARE(EcsExa); -FLECS_API extern ECS_DECLARE(EcsZetta); -FLECS_API extern ECS_DECLARE(EcsYotta); - -FLECS_API extern ECS_DECLARE(EcsKibi); -FLECS_API extern ECS_DECLARE(EcsMebi); -FLECS_API extern ECS_DECLARE(EcsGibi); -FLECS_API extern ECS_DECLARE(EcsTebi); -FLECS_API extern ECS_DECLARE(EcsPebi); -FLECS_API extern ECS_DECLARE(EcsExbi); -FLECS_API extern ECS_DECLARE(EcsZebi); -FLECS_API extern ECS_DECLARE(EcsYobi); +FLECS_API extern ecs_entity_t EcsUnitPrefixes; /**< Parent scope for prefixes. */ + +FLECS_API extern ecs_entity_t EcsYocto; /**< Yocto unit prefix. */ +FLECS_API extern ecs_entity_t EcsZepto; /**< Zepto unit prefix. */ +FLECS_API extern ecs_entity_t EcsAtto; /**< Atto unit prefix. */ +FLECS_API extern ecs_entity_t EcsFemto; /**< Femto unit prefix. */ +FLECS_API extern ecs_entity_t EcsPico; /**< Pico unit prefix. */ +FLECS_API extern ecs_entity_t EcsNano; /**< Nano unit prefix. */ +FLECS_API extern ecs_entity_t EcsMicro; /**< Micro unit prefix. */ +FLECS_API extern ecs_entity_t EcsMilli; /**< Milli unit prefix. */ +FLECS_API extern ecs_entity_t EcsCenti; /**< Centi unit prefix. */ +FLECS_API extern ecs_entity_t EcsDeci; /**< Deci unit prefix. */ +FLECS_API extern ecs_entity_t EcsDeca; /**< Deca unit prefix. */ +FLECS_API extern ecs_entity_t EcsHecto; /**< Hecto unit prefix. */ +FLECS_API extern ecs_entity_t EcsKilo; /**< Kilo unit prefix. */ +FLECS_API extern ecs_entity_t EcsMega; /**< Mega unit prefix. */ +FLECS_API extern ecs_entity_t EcsGiga; /**< Giga unit prefix. */ +FLECS_API extern ecs_entity_t EcsTera; /**< Tera unit prefix. */ +FLECS_API extern ecs_entity_t EcsPeta; /**< Peta unit prefix. */ +FLECS_API extern ecs_entity_t EcsExa; /**< Exa unit prefix. */ +FLECS_API extern ecs_entity_t EcsZetta; /**< Zetta unit prefix. */ +FLECS_API extern ecs_entity_t EcsYotta; /**< Yotta unit prefix. */ + +FLECS_API extern ecs_entity_t EcsKibi; /**< Kibi unit prefix. */ +FLECS_API extern ecs_entity_t EcsMebi; /**< Mebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsGibi; /**< Gibi unit prefix. */ +FLECS_API extern ecs_entity_t EcsTebi; /**< Tebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsPebi; /**< Pebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsExbi; /**< Exbi unit prefix. */ +FLECS_API extern ecs_entity_t EcsZebi; /**< Zebi unit prefix. */ +FLECS_API extern ecs_entity_t EcsYobi; /**< Yobi unit prefix. */ /** @} */ /** * @defgroup c_addons_units_duration Duration - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsDuration); -FLECS_API extern ECS_DECLARE(EcsPicoSeconds); -FLECS_API extern ECS_DECLARE(EcsNanoSeconds); -FLECS_API extern ECS_DECLARE(EcsMicroSeconds); -FLECS_API extern ECS_DECLARE(EcsMilliSeconds); -FLECS_API extern ECS_DECLARE(EcsSeconds); -FLECS_API extern ECS_DECLARE(EcsMinutes); -FLECS_API extern ECS_DECLARE(EcsHours); -FLECS_API extern ECS_DECLARE(EcsDays); +FLECS_API extern ecs_entity_t EcsDuration; /**< Duration quantity. */ +FLECS_API extern ecs_entity_t EcsPicoSeconds; /**< PicoSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsNanoSeconds; /**< NanoSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsMicroSeconds; /**< MicroSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsMilliSeconds; /**< MilliSeconds duration unit. */ +FLECS_API extern ecs_entity_t EcsSeconds; /**< Seconds duration unit. */ +FLECS_API extern ecs_entity_t EcsMinutes; /**< Minutes duration unit. */ +FLECS_API extern ecs_entity_t EcsHours; /**< Hours duration unit. */ +FLECS_API extern ecs_entity_t EcsDays; /**< Days duration unit. */ /** @} */ /** * @defgroup c_addons_units_time Time - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsTime); -FLECS_API extern ECS_DECLARE(EcsDate); +FLECS_API extern ecs_entity_t EcsTime; /**< Time quantity. */ +FLECS_API extern ecs_entity_t EcsDate; /**< Date unit. */ /** @} */ /** * @defgroup c_addons_units_mass Mass - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsMass); -FLECS_API extern ECS_DECLARE(EcsGrams); -FLECS_API extern ECS_DECLARE(EcsKiloGrams); +FLECS_API extern ecs_entity_t EcsMass; /**< Mass quantity. */ +FLECS_API extern ecs_entity_t EcsGrams; /**< Grams unit. */ +FLECS_API extern ecs_entity_t EcsKiloGrams; /**< KiloGrams unit. */ /** @} */ /** * @defgroup c_addons_units_electric_Current Electric Current - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsElectricCurrent); -FLECS_API extern ECS_DECLARE(EcsAmpere); +FLECS_API extern ecs_entity_t EcsElectricCurrent; /**< ElectricCurrent quantity. */ +FLECS_API extern ecs_entity_t EcsAmpere; /**< Ampere unit. */ /** @} */ /** * @defgroup c_addons_units_amount Amount - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsAmount); -FLECS_API extern ECS_DECLARE(EcsMole); +FLECS_API extern ecs_entity_t EcsAmount; /**< Amount quantity. */ +FLECS_API extern ecs_entity_t EcsMole; /**< Mole unit. */ /** @} */ /** * @defgroup c_addons_units_luminous_intensity Luminous Intensity - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsLuminousIntensity); -FLECS_API extern ECS_DECLARE(EcsCandela); +FLECS_API extern ecs_entity_t EcsLuminousIntensity; /**< LuminousIntensity quantity. */ +FLECS_API extern ecs_entity_t EcsCandela; /**< Candela unit. */ /** @} */ /** * @defgroup c_addons_units_force Force - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsForce); -FLECS_API extern ECS_DECLARE(EcsNewton); +FLECS_API extern ecs_entity_t EcsForce; /**< Force quantity. */ +FLECS_API extern ecs_entity_t EcsNewton; /**< Newton unit. */ /** @} */ /** * @defgroup c_addons_units_length Length - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsLength); -FLECS_API extern ECS_DECLARE(EcsMeters); -FLECS_API extern ECS_DECLARE(EcsPicoMeters); -FLECS_API extern ECS_DECLARE(EcsNanoMeters); -FLECS_API extern ECS_DECLARE(EcsMicroMeters); -FLECS_API extern ECS_DECLARE(EcsMilliMeters); -FLECS_API extern ECS_DECLARE(EcsCentiMeters); -FLECS_API extern ECS_DECLARE(EcsKiloMeters); -FLECS_API extern ECS_DECLARE(EcsMiles); -FLECS_API extern ECS_DECLARE(EcsPixels); +FLECS_API extern ecs_entity_t EcsLength; /**< Length quantity. */ +FLECS_API extern ecs_entity_t EcsMeters; /**< Meters unit. */ +FLECS_API extern ecs_entity_t EcsPicoMeters; /**< PicoMeters unit. */ +FLECS_API extern ecs_entity_t EcsNanoMeters; /**< NanoMeters unit. */ +FLECS_API extern ecs_entity_t EcsMicroMeters; /**< MicroMeters unit. */ +FLECS_API extern ecs_entity_t EcsMilliMeters; /**< MilliMeters unit. */ +FLECS_API extern ecs_entity_t EcsCentiMeters; /**< CentiMeters unit. */ +FLECS_API extern ecs_entity_t EcsKiloMeters; /**< KiloMeters unit. */ +FLECS_API extern ecs_entity_t EcsMiles; /**< Miles unit. */ +FLECS_API extern ecs_entity_t EcsPixels; /**< Pixels unit. */ /** @} */ /** * @defgroup c_addons_units_pressure Pressure - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsPressure); -FLECS_API extern ECS_DECLARE(EcsPascal); -FLECS_API extern ECS_DECLARE(EcsBar); +FLECS_API extern ecs_entity_t EcsPressure; /**< Pressure quantity. */ +FLECS_API extern ecs_entity_t EcsPascal; /**< Pascal unit. */ +FLECS_API extern ecs_entity_t EcsBar; /**< Bar unit. */ /** @} */ /** * @defgroup c_addons_units_speed Speed - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsSpeed); -FLECS_API extern ECS_DECLARE(EcsMetersPerSecond); -FLECS_API extern ECS_DECLARE(EcsKiloMetersPerSecond); -FLECS_API extern ECS_DECLARE(EcsKiloMetersPerHour); -FLECS_API extern ECS_DECLARE(EcsMilesPerHour); +FLECS_API extern ecs_entity_t EcsSpeed; /**< Speed quantity. */ +FLECS_API extern ecs_entity_t EcsMetersPerSecond; /**< MetersPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloMetersPerSecond; /**< KiloMetersPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloMetersPerHour; /**< KiloMetersPerHour unit. */ +FLECS_API extern ecs_entity_t EcsMilesPerHour; /**< MilesPerHour unit. */ /** @} */ /** * @defgroup c_addons_units_temperature Temperature - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsTemperature); -FLECS_API extern ECS_DECLARE(EcsKelvin); -FLECS_API extern ECS_DECLARE(EcsCelsius); -FLECS_API extern ECS_DECLARE(EcsFahrenheit); +FLECS_API extern ecs_entity_t EcsTemperature; /**< Temperature quantity. */ +FLECS_API extern ecs_entity_t EcsKelvin; /**< Kelvin unit. */ +FLECS_API extern ecs_entity_t EcsCelsius; /**< Celsius unit. */ +FLECS_API extern ecs_entity_t EcsFahrenheit; /**< Fahrenheit unit. */ /** @} */ /** * @defgroup c_addons_units_data Data - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsData); -FLECS_API extern ECS_DECLARE(EcsBits); -FLECS_API extern ECS_DECLARE(EcsKiloBits); -FLECS_API extern ECS_DECLARE(EcsMegaBits); -FLECS_API extern ECS_DECLARE(EcsGigaBits); -FLECS_API extern ECS_DECLARE(EcsBytes); -FLECS_API extern ECS_DECLARE(EcsKiloBytes); -FLECS_API extern ECS_DECLARE(EcsMegaBytes); -FLECS_API extern ECS_DECLARE(EcsGigaBytes); -FLECS_API extern ECS_DECLARE(EcsKibiBytes); -FLECS_API extern ECS_DECLARE(EcsMebiBytes); -FLECS_API extern ECS_DECLARE(EcsGibiBytes); +FLECS_API extern ecs_entity_t EcsData; /**< Data quantity. */ +FLECS_API extern ecs_entity_t EcsBits; /**< Bits unit. */ +FLECS_API extern ecs_entity_t EcsKiloBits; /**< KiloBits unit. */ +FLECS_API extern ecs_entity_t EcsMegaBits; /**< MegaBits unit. */ +FLECS_API extern ecs_entity_t EcsGigaBits; /**< GigaBits unit. */ +FLECS_API extern ecs_entity_t EcsBytes; /**< Bytes unit. */ +FLECS_API extern ecs_entity_t EcsKiloBytes; /**< KiloBytes unit. */ +FLECS_API extern ecs_entity_t EcsMegaBytes; /**< MegaBytes unit. */ +FLECS_API extern ecs_entity_t EcsGigaBytes; /**< GigaBytes unit. */ +FLECS_API extern ecs_entity_t EcsKibiBytes; /**< KibiBytes unit. */ +FLECS_API extern ecs_entity_t EcsMebiBytes; /**< MebiBytes unit. */ +FLECS_API extern ecs_entity_t EcsGibiBytes; /**< GibiBytes unit. */ /** @} */ /** * @defgroup c_addons_units_datarate Data Rate - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsDataRate); -FLECS_API extern ECS_DECLARE(EcsBitsPerSecond); -FLECS_API extern ECS_DECLARE(EcsKiloBitsPerSecond); -FLECS_API extern ECS_DECLARE(EcsMegaBitsPerSecond); -FLECS_API extern ECS_DECLARE(EcsGigaBitsPerSecond); -FLECS_API extern ECS_DECLARE(EcsBytesPerSecond); -FLECS_API extern ECS_DECLARE(EcsKiloBytesPerSecond); -FLECS_API extern ECS_DECLARE(EcsMegaBytesPerSecond); -FLECS_API extern ECS_DECLARE(EcsGigaBytesPerSecond); +FLECS_API extern ecs_entity_t EcsDataRate; /**< DataRate quantity. */ +FLECS_API extern ecs_entity_t EcsBitsPerSecond; /**< BitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloBitsPerSecond; /**< KiloBitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsMegaBitsPerSecond; /**< MegaBitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsGigaBitsPerSecond; /**< GigaBitsPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsBytesPerSecond; /**< BytesPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsKiloBytesPerSecond; /**< KiloBytesPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsMegaBytesPerSecond; /**< MegaBytesPerSecond unit. */ +FLECS_API extern ecs_entity_t EcsGigaBytesPerSecond; /**< GigaBytesPerSecond unit. */ /** @} */ /** * @defgroup c_addons_units_duration Duration - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsAngle); -FLECS_API extern ECS_DECLARE(EcsRadians); -FLECS_API extern ECS_DECLARE(EcsDegrees); +FLECS_API extern ecs_entity_t EcsAngle; /**< Angle quantity. */ +FLECS_API extern ecs_entity_t EcsRadians; /**< Radians unit. */ +FLECS_API extern ecs_entity_t EcsDegrees; /**< Degrees unit. */ /** @} */ /** * @defgroup c_addons_units_angle Angle - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsFrequency); -FLECS_API extern ECS_DECLARE(EcsHertz); -FLECS_API extern ECS_DECLARE(EcsKiloHertz); -FLECS_API extern ECS_DECLARE(EcsMegaHertz); -FLECS_API extern ECS_DECLARE(EcsGigaHertz); +FLECS_API extern ecs_entity_t EcsFrequency; /**< Frequency quantity. */ +FLECS_API extern ecs_entity_t EcsHertz; /**< Hertz unit. */ +FLECS_API extern ecs_entity_t EcsKiloHertz; /**< KiloHertz unit. */ +FLECS_API extern ecs_entity_t EcsMegaHertz; /**< MegaHertz unit. */ +FLECS_API extern ecs_entity_t EcsGigaHertz; /**< GigaHertz unit. */ /** @} */ /** * @defgroup c_addons_units_uri Uri - * - * \ingroup c_addons_units + * @ingroup c_addons_units * @{ */ -FLECS_API extern ECS_DECLARE(EcsUri); -FLECS_API extern ECS_DECLARE(EcsUriHyperlink); -FLECS_API extern ECS_DECLARE(EcsUriImage); -FLECS_API extern ECS_DECLARE(EcsUriFile); +FLECS_API extern ecs_entity_t EcsUri; /**< URI quantity. */ +FLECS_API extern ecs_entity_t EcsUriHyperlink; /**< UriHyperlink unit. */ +FLECS_API extern ecs_entity_t EcsUriImage; /**< UriImage unit. */ +FLECS_API extern ecs_entity_t EcsUriFile; /**< UriFile unit. */ /** @} */ -FLECS_API extern ECS_DECLARE(EcsAcceleration); -FLECS_API extern ECS_DECLARE(EcsPercentage); -FLECS_API extern ECS_DECLARE(EcsBel); -FLECS_API extern ECS_DECLARE(EcsDeciBel); +/** + * @defgroup c_addons_units_color Color + * @ingroup c_addons_units + * @{ + */ + +FLECS_API extern ecs_entity_t EcsColor; /**< Color quantity. */ +FLECS_API extern ecs_entity_t EcsColorRgb; /**< ColorRgb unit. */ +FLECS_API extern ecs_entity_t EcsColorHsl; /**< ColorHsl unit. */ +FLECS_API extern ecs_entity_t EcsColorCss; /**< ColorCss unit. */ + +/** @} */ + + +FLECS_API extern ecs_entity_t EcsAcceleration; /**< Acceleration unit. */ +FLECS_API extern ecs_entity_t EcsPercentage; /**< Percentage unit. */ +FLECS_API extern ecs_entity_t EcsBel; /**< Bel unit. */ +FLECS_API extern ecs_entity_t EcsDeciBel; /**< DeciBel unit. */ //////////////////////////////////////////////////////////////////////////////// //// Module //////////////////////////////////////////////////////////////////////////////// +/** Units module import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsUnits) + * @endcode + * + * @param world The world. + */ FLECS_API void FlecsUnitsImport( ecs_world_t *world); diff --git a/vendors/flecs/include/flecs/private/allocator.h b/vendors/flecs/include/flecs/datastructures/allocator.h similarity index 81% rename from vendors/flecs/include/flecs/private/allocator.h rename to vendors/flecs/include/flecs/datastructures/allocator.h index bd4b85cb5..0e16df97a 100644 --- a/vendors/flecs/include/flecs/private/allocator.h +++ b/vendors/flecs/include/flecs/datastructures/allocator.h @@ -6,7 +6,7 @@ #ifndef FLECS_ALLOCATOR_H #define FLECS_ALLOCATOR_H -#include "api_defines.h" +#include "../private/api_defines.h" FLECS_DBG_API extern int64_t ecs_block_allocator_alloc_count; FLECS_DBG_API extern int64_t ecs_block_allocator_free_count; @@ -57,9 +57,13 @@ void* flecs_dup( #define flecs_calloc_t(a, T) flecs_calloc(a, ECS_SIZEOF(T)) #define flecs_calloc_n(a, T, count) flecs_calloc(a, ECS_SIZEOF(T) * (count)) -#define flecs_free(a, size, ptr) flecs_bfree(flecs_allocator_get(a, size), ptr) -#define flecs_free_t(a, T, ptr) flecs_free(a, ECS_SIZEOF(T), ptr) -#define flecs_free_n(a, T, count, ptr) flecs_free(a, ECS_SIZEOF(T) * (count), ptr) +#define flecs_free(a, size, ptr)\ + flecs_bfree((ptr) ? flecs_allocator_get(a, size) : NULL, ptr) +#define flecs_free_t(a, T, ptr)\ + flecs_bfree_w_dbg_info((ptr) ? flecs_allocator_get(a, ECS_SIZEOF(T)) : NULL, ptr, #T) +#define flecs_free_n(a, T, count, ptr)\ + flecs_bfree_w_dbg_info((ptr) ? flecs_allocator_get(a, ECS_SIZEOF(T) * (count)) : NULL\ + , ptr, #T) #define flecs_realloc(a, size_dst, size_src, ptr)\ flecs_brealloc(flecs_allocator_get(a, size_dst),\ diff --git a/vendors/flecs/include/flecs/private/bitset.h b/vendors/flecs/include/flecs/datastructures/bitset.h similarity index 97% rename from vendors/flecs/include/flecs/private/bitset.h rename to vendors/flecs/include/flecs/datastructures/bitset.h index 8c9e5c943..7e2707daa 100644 --- a/vendors/flecs/include/flecs/private/bitset.h +++ b/vendors/flecs/include/flecs/datastructures/bitset.h @@ -6,7 +6,7 @@ #ifndef FLECS_BITSET_H #define FLECS_BITSET_H -#include "flecs/private/api_defines.h" +#include "../private/api_defines.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/include/flecs/private/block_allocator.h b/vendors/flecs/include/flecs/datastructures/block_allocator.h similarity index 91% rename from vendors/flecs/include/flecs/private/block_allocator.h rename to vendors/flecs/include/flecs/datastructures/block_allocator.h index c6fec180d..ff3a7298e 100644 --- a/vendors/flecs/include/flecs/private/block_allocator.h +++ b/vendors/flecs/include/flecs/datastructures/block_allocator.h @@ -6,7 +6,7 @@ #ifndef FLECS_BLOCK_ALLOCATOR_H #define FLECS_BLOCK_ALLOCATOR_H -#include "api_defines.h" +#include "../private/api_defines.h" typedef struct ecs_block_allocator_block_t { void *memory; @@ -68,6 +68,12 @@ void flecs_bfree( ecs_block_allocator_t *allocator, void *memory); +FLECS_API +void flecs_bfree_w_dbg_info( + ecs_block_allocator_t *allocator, + void *memory, + const char *type_name); + FLECS_API void* flecs_brealloc( ecs_block_allocator_t *dst, diff --git a/vendors/flecs/include/flecs/private/hashmap.h b/vendors/flecs/include/flecs/datastructures/hashmap.h similarity index 98% rename from vendors/flecs/include/flecs/private/hashmap.h rename to vendors/flecs/include/flecs/datastructures/hashmap.h index ddb0c66c6..06e6eab2b 100644 --- a/vendors/flecs/include/flecs/private/hashmap.h +++ b/vendors/flecs/include/flecs/datastructures/hashmap.h @@ -6,7 +6,7 @@ #ifndef FLECS_HASHMAP_H #define FLECS_HASHMAP_H -#include "api_defines.h" +#include "../private/api_defines.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/include/flecs/private/map.h b/vendors/flecs/include/flecs/datastructures/map.h similarity index 99% rename from vendors/flecs/include/flecs/private/map.h rename to vendors/flecs/include/flecs/datastructures/map.h index 59a756266..969125f67 100644 --- a/vendors/flecs/include/flecs/private/map.h +++ b/vendors/flecs/include/flecs/datastructures/map.h @@ -6,7 +6,7 @@ #ifndef FLECS_MAP_H #define FLECS_MAP_H -#include "api_defines.h" +#include "../private/api_defines.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/include/flecs/private/sparse.h b/vendors/flecs/include/flecs/datastructures/sparse.h similarity index 87% rename from vendors/flecs/include/flecs/private/sparse.h rename to vendors/flecs/include/flecs/datastructures/sparse.h index f953042fb..6d7f34c27 100644 --- a/vendors/flecs/include/flecs/private/sparse.h +++ b/vendors/flecs/include/flecs/datastructures/sparse.h @@ -6,7 +6,7 @@ #ifndef FLECS_SPARSE_H #define FLECS_SPARSE_H -#include "flecs/private/api_defines.h" +#include "../private/api_defines.h" #ifdef __cplusplus extern "C" { @@ -15,6 +15,12 @@ extern "C" { /** The number of elements in a single page */ #define FLECS_SPARSE_PAGE_SIZE (1 << FLECS_SPARSE_PAGE_BITS) +/** Compute the page index from an id by stripping the first 12 bits */ +#define FLECS_SPARSE_PAGE(index) ((int32_t)((uint32_t)index >> FLECS_SPARSE_PAGE_BITS)) + +/** This computes the offset of an index inside a page */ +#define FLECS_SPARSE_OFFSET(index) ((int32_t)index & (FLECS_SPARSE_PAGE_SIZE - 1)) + typedef struct ecs_sparse_t { ecs_vec_t dense; /* Dense array with indices to sparse array. The * dense array stores both alive and not alive @@ -32,13 +38,13 @@ typedef struct ecs_sparse_t { /** Initialize sparse set */ FLECS_DBG_API void flecs_sparse_init( - ecs_sparse_t *sparse, + ecs_sparse_t *result, struct ecs_allocator_t *allocator, struct ecs_block_allocator_t *page_allocator, - ecs_size_t elem_size); + ecs_size_t size); -#define flecs_sparse_init_t(sparse, allocator, page_allocator, T)\ - flecs_sparse_init(sparse, allocator, page_allocator, ECS_SIZEOF(T)) +#define flecs_sparse_init_t(result, allocator, page_allocator, T)\ + flecs_sparse_init(result, allocator, page_allocator, ECS_SIZEOF(T)) FLECS_DBG_API void flecs_sparse_fini( @@ -78,6 +84,13 @@ void flecs_sparse_remove( #define flecs_sparse_remove_t(sparse, T, id)\ flecs_sparse_remove(sparse, ECS_SIZEOF(T), id) +/** Remove an element without liveliness checking */ +FLECS_DBG_API +void* flecs_sparse_remove_fast( + ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index); + /** Test if id is alive, which requires the generation count to match. */ FLECS_DBG_API bool flecs_sparse_is_alive( @@ -157,8 +170,8 @@ const uint64_t* flecs_sparse_ids( const ecs_sparse_t *sparse); /* Publicly exposed APIs - * The flecs_ functions aren't exposed directly as this can cause some - * optimizers to not consider them for link time optimization. */ + * These APIs are not part of the public API and as a result may change without + * notice (though they haven't changed in a long time). */ FLECS_API void ecs_sparse_init( @@ -184,12 +197,6 @@ FLECS_API int32_t ecs_sparse_count( const ecs_sparse_t *sparse); -/** Override the generation count for a specific id */ -FLECS_API -void flecs_sparse_set_generation( - ecs_sparse_t *sparse, - uint64_t id); - FLECS_API void* ecs_sparse_get_dense( const ecs_sparse_t *sparse, diff --git a/vendors/flecs/src/datastructures/stack_allocator.h b/vendors/flecs/include/flecs/datastructures/stack_allocator.h similarity index 89% rename from vendors/flecs/src/datastructures/stack_allocator.h rename to vendors/flecs/include/flecs/datastructures/stack_allocator.h index 740147bc7..9e2e5cd00 100644 --- a/vendors/flecs/src/datastructures/stack_allocator.h +++ b/vendors/flecs/include/flecs/datastructures/stack_allocator.h @@ -16,6 +16,16 @@ typedef struct ecs_stack_page_t { uint32_t id; } ecs_stack_page_t; +typedef struct ecs_stack_cursor_t { + struct ecs_stack_cursor_t *prev; + struct ecs_stack_page_t *page; + int16_t sp; + bool is_free; +#ifdef FLECS_DEBUG + struct ecs_stack_t *owner; +#endif +} ecs_stack_cursor_t; + typedef struct ecs_stack_t { ecs_stack_page_t first; ecs_stack_page_t *tail_page; diff --git a/vendors/flecs/include/flecs/private/strbuf.h b/vendors/flecs/include/flecs/datastructures/strbuf.h similarity index 57% rename from vendors/flecs/include/flecs/private/strbuf.h rename to vendors/flecs/include/flecs/datastructures/strbuf.h index 36341dfed..a16e73afc 100644 --- a/vendors/flecs/include/flecs/private/strbuf.h +++ b/vendors/flecs/include/flecs/datastructures/strbuf.h @@ -6,7 +6,7 @@ #ifndef FLECS_STRBUF_H_ #define FLECS_STRBUF_H_ -#include "api_defines.h" +#include "../private/api_defines.h" #ifdef __cplusplus extern "C" { @@ -18,25 +18,9 @@ extern "C" { #else #define ECS_STRBUF_INIT (ecs_strbuf_t){0} #endif -#define ECS_STRBUF_ELEMENT_SIZE (511) -#define ECS_STRBUF_MAX_LIST_DEPTH (32) - -typedef struct ecs_strbuf_element { - bool buffer_embedded; - int32_t pos; - char *buf; - struct ecs_strbuf_element *next; -} ecs_strbuf_element; -typedef struct ecs_strbuf_element_embedded { - ecs_strbuf_element super; - char buf[ECS_STRBUF_ELEMENT_SIZE + 1]; -} ecs_strbuf_element_embedded; - -typedef struct ecs_strbuf_element_str { - ecs_strbuf_element super; - char *alloc_str; -} ecs_strbuf_element_str; +#define ECS_STRBUF_SMALL_STRING_SIZE (512) +#define ECS_STRBUF_MAX_LIST_DEPTH (32) typedef struct ecs_strbuf_list_elem { int32_t count; @@ -44,40 +28,20 @@ typedef struct ecs_strbuf_list_elem { } ecs_strbuf_list_elem; typedef struct ecs_strbuf_t { - /* When set by an application, append will write to this buffer */ - char *buf; - - /* The maximum number of characters that may be printed */ - int32_t max; - - /* Size of elements minus current element */ - int32_t size; - - /* The number of elements in use */ - int32_t elementCount; - - /* Always allocate at least one element */ - ecs_strbuf_element_embedded firstElement; - - /* The current element being appended to */ - ecs_strbuf_element *current; + char *content; + ecs_size_t length; + ecs_size_t size; - /* Stack that keeps track of number of list elements, used for conditionally - * inserting a separator */ ecs_strbuf_list_elem list_stack[ECS_STRBUF_MAX_LIST_DEPTH]; int32_t list_sp; - /* This is set to the output string after calling ecs_strbuf_get */ - char *content; - - /* This is set to the output string length after calling ecs_strbuf_get */ - int32_t length; + char small_string[ECS_STRBUF_SMALL_STRING_SIZE]; } ecs_strbuf_t; /* Append format string to a buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_append( +void ecs_strbuf_append( ecs_strbuf_t *buffer, const char *fmt, ...); @@ -85,7 +49,7 @@ bool ecs_strbuf_append( /* Append format string with argument list to a buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_vappend( +void ecs_strbuf_vappend( ecs_strbuf_t *buffer, const char *fmt, va_list args); @@ -93,28 +57,28 @@ bool ecs_strbuf_vappend( /* Append string to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendstr( +void ecs_strbuf_appendstr( ecs_strbuf_t *buffer, const char *str); /* Append character to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendch( +void ecs_strbuf_appendch( ecs_strbuf_t *buffer, char ch); /* Append int to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendint( +void ecs_strbuf_appendint( ecs_strbuf_t *buffer, int64_t v); /* Append float to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendflt( +void ecs_strbuf_appendflt( ecs_strbuf_t *buffer, double v, char nan_delim); @@ -122,63 +86,33 @@ bool ecs_strbuf_appendflt( /* Append boolean to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendbool( +void ecs_strbuf_appendbool( ecs_strbuf_t *buffer, bool v); /* Append source buffer to destination buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_mergebuff( +void ecs_strbuf_mergebuff( ecs_strbuf_t *dst_buffer, ecs_strbuf_t *src_buffer); -/* Append string to buffer, transfer ownership to buffer. - * Returns false when max is reached, true when there is still space */ -FLECS_API -bool ecs_strbuf_appendstr_zerocpy( - ecs_strbuf_t *buffer, - char *str); - -/* Append string to buffer, transfer ownership to buffer. - * Returns false when max is reached, true when there is still space */ -FLECS_API -bool ecs_strbuf_appendstr_zerocpyn( - ecs_strbuf_t *buffer, - char *str, - int32_t n); - -/* Append string to buffer, do not free/modify string. - * Returns false when max is reached, true when there is still space */ -FLECS_API -bool ecs_strbuf_appendstr_zerocpy_const( - ecs_strbuf_t *buffer, - const char *str); - -/* Append string to buffer, transfer ownership to buffer. - * Returns false when max is reached, true when there is still space */ -FLECS_API -bool ecs_strbuf_appendstr_zerocpyn_const( - ecs_strbuf_t *buffer, - const char *str, - int32_t n); - /* Append n characters to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API -bool ecs_strbuf_appendstrn( +void ecs_strbuf_appendstrn( ecs_strbuf_t *buffer, const char *str, int32_t n); /* Return result string */ FLECS_API -char *ecs_strbuf_get( +char* ecs_strbuf_get( ecs_strbuf_t *buffer); /* Return small string from first element (appends \0) */ FLECS_API -char *ecs_strbuf_get_small( +char* ecs_strbuf_get_small( ecs_strbuf_t *buffer); /* Reset buffer without returning a string */ @@ -206,26 +140,26 @@ void ecs_strbuf_list_next( /* Append character to as new element in list. */ FLECS_API -bool ecs_strbuf_list_appendch( +void ecs_strbuf_list_appendch( ecs_strbuf_t *buffer, char ch); /* Append formatted string as a new element in list */ FLECS_API -bool ecs_strbuf_list_append( +void ecs_strbuf_list_append( ecs_strbuf_t *buffer, const char *fmt, ...); /* Append string as a new element in list */ FLECS_API -bool ecs_strbuf_list_appendstr( +void ecs_strbuf_list_appendstr( ecs_strbuf_t *buffer, const char *str); /* Append string as a new element in list */ FLECS_API -bool ecs_strbuf_list_appendstrn( +void ecs_strbuf_list_appendstrn( ecs_strbuf_t *buffer, const char *str, int32_t n); diff --git a/vendors/flecs/include/flecs/datastructures/switch_list.h b/vendors/flecs/include/flecs/datastructures/switch_list.h new file mode 100644 index 000000000..6a22a6a25 --- /dev/null +++ b/vendors/flecs/include/flecs/datastructures/switch_list.h @@ -0,0 +1,81 @@ +/** + * @file switch_list.h + * @brief Interleaved linked list for storing mutually exclusive values. + */ + +#ifndef FLECS_SWITCH_LIST_H +#define FLECS_SWITCH_LIST_H + +#include "flecs/private/api_defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ecs_switch_node_t { + uint32_t next; /* Next node in list */ + uint32_t prev; /* Prev node in list */ +} ecs_switch_node_t; + +typedef struct ecs_switch_page_t { + ecs_vec_t nodes; /* vec */ + ecs_vec_t values; /* vec */ +} ecs_switch_page_t; + +typedef struct ecs_switch_t { + ecs_map_t hdrs; /* map */ + ecs_vec_t pages; /* vec */ +} ecs_switch_t; + +/** Init new switch. */ +FLECS_DBG_API +void flecs_switch_init( + ecs_switch_t* sw, + ecs_allocator_t *allocator); + +/** Fini switch. */ +FLECS_DBG_API +void flecs_switch_fini( + ecs_switch_t *sw); + +/** Set value of element. */ +FLECS_DBG_API +bool flecs_switch_set( + ecs_switch_t *sw, + uint32_t element, + uint64_t value); + +/** Reset value of element. */ +FLECS_DBG_API +bool flecs_switch_reset( + ecs_switch_t *sw, + uint32_t element); + +/** Get value for element. */ +FLECS_DBG_API +uint64_t flecs_switch_get( + const ecs_switch_t *sw, + uint32_t element); + +/** Get first element for value. */ +FLECS_DBG_API +uint32_t flecs_switch_first( + const ecs_switch_t *sw, + uint64_t value); + +/** Get next element. */ +FLECS_DBG_API +uint32_t flecs_switch_next( + const ecs_switch_t *sw, + uint32_t previous); + +/** Get target iterator. */ +FLECS_DBG_API +ecs_map_iter_t flecs_switch_targets( + const ecs_switch_t *sw); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/vendors/flecs/include/flecs/private/vec.h b/vendors/flecs/include/flecs/datastructures/vec.h similarity index 93% rename from vendors/flecs/include/flecs/private/vec.h rename to vendors/flecs/include/flecs/datastructures/vec.h index 914769835..555d7ccb2 100644 --- a/vendors/flecs/include/flecs/private/vec.h +++ b/vendors/flecs/include/flecs/datastructures/vec.h @@ -6,7 +6,7 @@ #ifndef FLECS_VEC_H #define FLECS_VEC_H -#include "flecs/private/api_defines.h" +#include "../private/api_defines.h" #ifdef __cplusplus extern "C" { @@ -23,7 +23,7 @@ typedef struct ecs_vec_t { } ecs_vec_t; FLECS_API -ecs_vec_t* ecs_vec_init( +void ecs_vec_init( struct ecs_allocator_t *allocator, ecs_vec_t *vec, ecs_size_t size, @@ -93,6 +93,15 @@ ecs_vec_t ecs_vec_copy( #define ecs_vec_copy_t(allocator, vec, T) \ ecs_vec_copy(allocator, vec, ECS_SIZEOF(T)) +FLECS_API +ecs_vec_t ecs_vec_copy_shrink( + struct ecs_allocator_t *allocator, + const ecs_vec_t *vec, + ecs_size_t size); + +#define ecs_vec_copy_shrink_t(allocator, vec, T) \ + ecs_vec_copy_shrink(allocator, vec, ECS_SIZEOF(T)) + FLECS_API void ecs_vec_reclaim( struct ecs_allocator_t *allocator, @@ -198,4 +207,4 @@ void* ecs_vec_last( } #endif -#endif +#endif diff --git a/vendors/flecs/include/flecs/os_api.h b/vendors/flecs/include/flecs/os_api.h index edd5d8616..2d2cb42bd 100644 --- a/vendors/flecs/include/flecs/os_api.h +++ b/vendors/flecs/include/flecs/os_api.h @@ -2,11 +2,11 @@ * @file os_api.h * @brief Operating system abstraction API. * - * This file contains the operating system abstraction API. The flecs core + * This file contains the operating system abstraction API. The flecs core * library avoids OS/runtime specific API calls as much as possible. Instead it * provides an interface that can be implemented by applications. * - * Examples for how to implement this interface can be found in the + * Examples for how to implement this interface can be found in the * examples/os_api folder. */ @@ -15,14 +15,15 @@ /** * @defgroup c_os_api OS API - * @brief Interface for providing OS specific functionality. - * - * \ingroup c + * @ingroup c + * Interface for providing OS specific functionality. + * * @{ */ #include #include +#include #if defined(ECS_TARGET_WINDOWS) #include @@ -36,152 +37,176 @@ extern "C" { #endif +/** Time type. */ typedef struct ecs_time_t { - uint32_t sec; - uint32_t nanosec; + uint32_t sec; /**< Second part. */ + uint32_t nanosec; /**< Nanosecond part. */ } ecs_time_t; /* Allocation counters */ -extern int64_t ecs_os_api_malloc_count; -extern int64_t ecs_os_api_realloc_count; -extern int64_t ecs_os_api_calloc_count; -extern int64_t ecs_os_api_free_count; +extern int64_t ecs_os_api_malloc_count; /**< malloc count. */ +extern int64_t ecs_os_api_realloc_count; /**< realloc count. */ +extern int64_t ecs_os_api_calloc_count; /**< calloc count. */ +extern int64_t ecs_os_api_free_count; /**< free count. */ /* Use handle types that _at least_ can store pointers */ -typedef uintptr_t ecs_os_thread_t; -typedef uintptr_t ecs_os_cond_t; -typedef uintptr_t ecs_os_mutex_t; -typedef uintptr_t ecs_os_dl_t; -typedef uintptr_t ecs_os_sock_t; +typedef uintptr_t ecs_os_thread_t; /**< OS thread. */ +typedef uintptr_t ecs_os_cond_t; /**< OS cond. */ +typedef uintptr_t ecs_os_mutex_t; /**< OS mutex. */ +typedef uintptr_t ecs_os_dl_t; /**< OS dynamic library. */ +typedef uintptr_t ecs_os_sock_t; /**< OS socket. */ -/* 64 bit thread id */ +/** 64 bit thread id. */ typedef uint64_t ecs_os_thread_id_t; -/* Generic function pointer type */ +/** Generic function pointer type. */ typedef void (*ecs_os_proc_t)(void); -/* OS API init */ -typedef +/** OS API init. */ +typedef void (*ecs_os_api_init_t)(void); -/* OS API deinit */ -typedef +/** OS API deinit. */ +typedef void (*ecs_os_api_fini_t)(void); -/* Memory management */ -typedef +/** OS API malloc function type. */ +typedef void* (*ecs_os_api_malloc_t)( ecs_size_t size); -typedef +/** OS API free function type. */ +typedef void (*ecs_os_api_free_t)( void *ptr); +/** OS API realloc function type. */ typedef void* (*ecs_os_api_realloc_t)( - void *ptr, + void *ptr, ecs_size_t size); +/** OS API calloc function type. */ typedef void* (*ecs_os_api_calloc_t)( ecs_size_t size); +/** OS API strdup function type. */ typedef char* (*ecs_os_api_strdup_t)( const char *str); -/* Threads */ +/** OS API thread_callback function type. */ typedef void* (*ecs_os_thread_callback_t)( void*); +/** OS API thread_new function type. */ typedef ecs_os_thread_t (*ecs_os_api_thread_new_t)( ecs_os_thread_callback_t callback, void *param); +/** OS API thread_join function type. */ typedef void* (*ecs_os_api_thread_join_t)( ecs_os_thread_t thread); +/** OS API thread_self function type. */ typedef ecs_os_thread_id_t (*ecs_os_api_thread_self_t)(void); -/* Tasks */ +/** OS API task_new function type. */ typedef ecs_os_thread_t (*ecs_os_api_task_new_t)( ecs_os_thread_callback_t callback, void *param); +/** OS API task_join function type. */ typedef void* (*ecs_os_api_task_join_t)( ecs_os_thread_t thread); /* Atomic increment / decrement */ +/** OS API ainc function type. */ typedef int32_t (*ecs_os_api_ainc_t)( int32_t *value); +/** OS API lainc function type. */ typedef int64_t (*ecs_os_api_lainc_t)( int64_t *value); /* Mutex */ +/** OS API mutex_new function type. */ typedef ecs_os_mutex_t (*ecs_os_api_mutex_new_t)( void); +/** OS API mutex_lock function type. */ typedef void (*ecs_os_api_mutex_lock_t)( ecs_os_mutex_t mutex); +/** OS API mutex_unlock function type. */ typedef void (*ecs_os_api_mutex_unlock_t)( ecs_os_mutex_t mutex); +/** OS API mutex_free function type. */ typedef void (*ecs_os_api_mutex_free_t)( ecs_os_mutex_t mutex); /* Condition variable */ +/** OS API cond_new function type. */ typedef ecs_os_cond_t (*ecs_os_api_cond_new_t)( void); +/** OS API cond_free function type. */ typedef void (*ecs_os_api_cond_free_t)( ecs_os_cond_t cond); +/** OS API cond_signal function type. */ typedef void (*ecs_os_api_cond_signal_t)( ecs_os_cond_t cond); +/** OS API cond_broadcast function type. */ typedef void (*ecs_os_api_cond_broadcast_t)( ecs_os_cond_t cond); +/** OS API cond_wait function type. */ typedef void (*ecs_os_api_cond_wait_t)( ecs_os_cond_t cond, ecs_os_mutex_t mutex); -typedef +/** OS API sleep function type. */ +typedef void (*ecs_os_api_sleep_t)( int32_t sec, int32_t nanosec); -typedef +/** OS API enable_high_timer_resolution function type. */ +typedef void (*ecs_os_api_enable_high_timer_resolution_t)( bool enable); +/** OS API get_time function type. */ typedef void (*ecs_os_api_get_time_t)( ecs_time_t *time_out); +/** OS API now function type. */ typedef uint64_t (*ecs_os_api_now_t)(void); -/* Logging */ +/** OS API log function type. */ typedef void (*ecs_os_api_log_t)( int32_t level, /* Logging level */ @@ -189,138 +214,186 @@ void (*ecs_os_api_log_t)( int32_t line, /* Line it was logged */ const char *msg); -/* Application termination */ +/** OS API abort function type. */ typedef void (*ecs_os_api_abort_t)( void); -/* Dynamic libraries */ +/** OS API dlopen function type. */ typedef ecs_os_dl_t (*ecs_os_api_dlopen_t)( const char *libname); +/** OS API dlproc function type. */ typedef ecs_os_proc_t (*ecs_os_api_dlproc_t)( ecs_os_dl_t lib, const char *procname); +/** OS API dlclose function type. */ typedef void (*ecs_os_api_dlclose_t)( ecs_os_dl_t lib); +/** OS API module_to_path function type. */ typedef char* (*ecs_os_api_module_to_path_t)( const char *module_id); -/* Prefix members of struct with 'ecs_' as some system headers may define +/* Performance tracing */ +typedef void (*ecs_os_api_perf_trace_t)( + const char *filename, + size_t line, + const char *name); + +/* Prefix members of struct with 'ecs_' as some system headers may define * macros for functions like "strdup", "log" or "_free" */ +/** OS API interface. */ typedef struct ecs_os_api_t { /* API init / deinit */ - ecs_os_api_init_t init_; - ecs_os_api_fini_t fini_; + ecs_os_api_init_t init_; /**< init callback. */ + ecs_os_api_fini_t fini_; /**< fini callback. */ /* Memory management */ - ecs_os_api_malloc_t malloc_; - ecs_os_api_realloc_t realloc_; - ecs_os_api_calloc_t calloc_; - ecs_os_api_free_t free_; + ecs_os_api_malloc_t malloc_; /**< malloc callback. */ + ecs_os_api_realloc_t realloc_; /**< realloc callback. */ + ecs_os_api_calloc_t calloc_; /**< calloc callback. */ + ecs_os_api_free_t free_; /**< free callback. */ /* Strings */ - ecs_os_api_strdup_t strdup_; + ecs_os_api_strdup_t strdup_; /**< strdup callback. */ /* Threads */ - ecs_os_api_thread_new_t thread_new_; - ecs_os_api_thread_join_t thread_join_; - ecs_os_api_thread_self_t thread_self_; + ecs_os_api_thread_new_t thread_new_; /**< thread_new callback. */ + ecs_os_api_thread_join_t thread_join_; /**< thread_join callback. */ + ecs_os_api_thread_self_t thread_self_; /**< thread_self callback. */ /* Tasks */ - ecs_os_api_thread_new_t task_new_; - ecs_os_api_thread_join_t task_join_; + ecs_os_api_thread_new_t task_new_; /**< task_new callback. */ + ecs_os_api_thread_join_t task_join_; /**< task_join callback. */ /* Atomic increment / decrement */ - ecs_os_api_ainc_t ainc_; - ecs_os_api_ainc_t adec_; - ecs_os_api_lainc_t lainc_; - ecs_os_api_lainc_t ladec_; + ecs_os_api_ainc_t ainc_; /**< ainc callback. */ + ecs_os_api_ainc_t adec_; /**< adec callback. */ + ecs_os_api_lainc_t lainc_; /**< lainc callback. */ + ecs_os_api_lainc_t ladec_; /**< ladec callback. */ /* Mutex */ - ecs_os_api_mutex_new_t mutex_new_; - ecs_os_api_mutex_free_t mutex_free_; - ecs_os_api_mutex_lock_t mutex_lock_; - ecs_os_api_mutex_lock_t mutex_unlock_; + ecs_os_api_mutex_new_t mutex_new_; /**< mutex_new callback. */ + ecs_os_api_mutex_free_t mutex_free_; /**< mutex_free callback. */ + ecs_os_api_mutex_lock_t mutex_lock_; /**< mutex_lock callback. */ + ecs_os_api_mutex_lock_t mutex_unlock_; /**< mutex_unlock callback. */ /* Condition variable */ - ecs_os_api_cond_new_t cond_new_; - ecs_os_api_cond_free_t cond_free_; - ecs_os_api_cond_signal_t cond_signal_; - ecs_os_api_cond_broadcast_t cond_broadcast_; - ecs_os_api_cond_wait_t cond_wait_; + ecs_os_api_cond_new_t cond_new_; /**< cond_new callback. */ + ecs_os_api_cond_free_t cond_free_; /**< cond_free callback. */ + ecs_os_api_cond_signal_t cond_signal_; /**< cond_signal callback. */ + ecs_os_api_cond_broadcast_t cond_broadcast_; /**< cond_broadcast callback. */ + ecs_os_api_cond_wait_t cond_wait_; /**< cond_wait callback. */ /* Time */ - ecs_os_api_sleep_t sleep_; - ecs_os_api_now_t now_; - ecs_os_api_get_time_t get_time_; + ecs_os_api_sleep_t sleep_; /**< sleep callback. */ + ecs_os_api_now_t now_; /**< now callback. */ + ecs_os_api_get_time_t get_time_; /**< get_time callback. */ /* Logging */ - ecs_os_api_log_t log_; /* Logging function. The level should be interpreted as: */ - /* >0: Debug tracing. Only enabled in debug builds. */ - /* 0: Tracing. Enabled in debug/release builds. */ - /* -2: Warning. An issue occurred, but operation was successful. */ - /* -3: Error. An issue occurred, and operation was unsuccessful. */ - /* -4: Fatal. An issue occurred, and application must quit. */ + ecs_os_api_log_t log_; /**< log callback. + * The level should be interpreted as: + * >0: Debug tracing. Only enabled in debug builds. + * 0: Tracing. Enabled in debug/release builds. + * -2: Warning. An issue occurred, but operation was successful. + * -3: Error. An issue occurred, and operation was unsuccessful. + * -4: Fatal. An issue occurred, and application must quit. */ /* Application termination */ - ecs_os_api_abort_t abort_; + ecs_os_api_abort_t abort_; /**< abort callback. */ /* Dynamic library loading */ - ecs_os_api_dlopen_t dlopen_; - ecs_os_api_dlproc_t dlproc_; - ecs_os_api_dlclose_t dlclose_; + ecs_os_api_dlopen_t dlopen_; /**< dlopen callback. */ + ecs_os_api_dlproc_t dlproc_; /**< dlproc callback. */ + ecs_os_api_dlclose_t dlclose_; /**< dlclose callback. */ /* Overridable function that translates from a logical module id to a * shared library filename */ - ecs_os_api_module_to_path_t module_to_dl_; + ecs_os_api_module_to_path_t module_to_dl_; /**< module_to_dl callback. */ /* Overridable function that translates from a logical module id to a * path that contains module-specif resources or assets */ - ecs_os_api_module_to_path_t module_to_etc_; + ecs_os_api_module_to_path_t module_to_etc_; /**< module_to_etc callback. */ - /* Trace level */ - int32_t log_level_; + /* Performance tracing */ + ecs_os_api_perf_trace_t perf_trace_push_; - /* Trace indentation */ - int32_t log_indent_; + /* Performance tracing */ + ecs_os_api_perf_trace_t perf_trace_pop_; - /* Last error code */ - int32_t log_last_error_; + int32_t log_level_; /**< Tracing level. */ + int32_t log_indent_; /**< Tracing indentation level. */ + int32_t log_last_error_; /**< Last logged error code. */ + int64_t log_last_timestamp_; /**< Last logged timestamp. */ - /* Last recorded timestamp */ - int64_t log_last_timestamp_; + ecs_flags32_t flags_; /**< OS API flags */ - /* OS API flags */ - ecs_flags32_t flags_; + FILE *log_out_; /**< File used for logging output + * (hint, log_ decides where to write) */ } ecs_os_api_t; +/** Static OS API variable with configured callbacks. */ FLECS_API extern ecs_os_api_t ecs_os_api; +/** Initialize OS API. + * This operation is not usually called by an application. To override callbacks + * of the OS API, use the following pattern: + * + * @code + * ecs_os_set_api_defaults(); + * ecs_os_api_t os_api = ecs_os_get_api(); + * os_api.abort_ = my_abort; + * ecs_os_set_api(&os_api); + * @endcode + */ FLECS_API void ecs_os_init(void); +/** Deinitialize OS API. + * This operation is not usually called by an application. + */ FLECS_API void ecs_os_fini(void); +/** Override OS API. + * This overrides the OS API struct with new values for callbacks. See + * ecs_os_init() on how to use the function. + * + * @param os_api Pointer to struct with values to set. + */ FLECS_API void ecs_os_set_api( ecs_os_api_t *os_api); +/** Get OS API. + * + * @return A value with the current OS API callbacks + * @see ecs_os_init() + */ FLECS_API ecs_os_api_t ecs_os_get_api(void); +/** Set default values for OS API. + * This initializes the OS API struct with default values for callbacks like + * malloc and free. + * + * @see ecs_os_init() + */ FLECS_API void ecs_os_set_api_defaults(void); +/** Macro utilities + * \cond + */ + /* Memory management */ #ifndef ecs_os_malloc #define ecs_os_malloc(size) ecs_os_api.malloc_(size) @@ -372,16 +445,16 @@ void ecs_os_set_api_defaults(void); #endif #define ecs_os_memcpy_t(ptr1, ptr2, T) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T)) -#define ecs_os_memcpy_n(ptr1, ptr2, T, count) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T) * count) +#define ecs_os_memcpy_n(ptr1, ptr2, T, count) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T) * (size_t)count) #define ecs_os_memcmp_t(ptr1, ptr2, T) ecs_os_memcmp(ptr1, ptr2, ECS_SIZEOF(T)) #define ecs_os_memmove_t(ptr1, ptr2, T) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T)) -#define ecs_os_memmove_n(ptr1, ptr2, T, count) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T) * count) +#define ecs_os_memmove_n(ptr1, ptr2, T, count) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T) * (size_t)count) #define ecs_os_memmove_t(ptr1, ptr2, T) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T)) #define ecs_os_strcmp(str1, str2) strcmp(str1, str2) #define ecs_os_memset_t(ptr, value, T) ecs_os_memset(ptr, value, ECS_SIZEOF(T)) -#define ecs_os_memset_n(ptr, value, T, count) ecs_os_memset(ptr, value, ECS_SIZEOF(T) * count) +#define ecs_os_memset_n(ptr, value, T, count) ecs_os_memset(ptr, value, ECS_SIZEOF(T) * (size_t)count) #define ecs_os_zeromem(ptr) ecs_os_memset(ptr, 0, ECS_SIZEOF(*ptr)) #define ecs_os_memdup_t(ptr, T) ecs_os_memdup(ptr, ECS_SIZEOF(T)) @@ -392,24 +465,16 @@ void ecs_os_set_api_defaults(void); #if !defined(ECS_TARGET_POSIX) && !defined(ECS_TARGET_MINGW) #define ecs_os_strcat(str1, str2) strcat_s(str1, INT_MAX, str2) -#define ecs_os_sprintf(ptr, ...) sprintf_s(ptr, INT_MAX, __VA_ARGS__) -#define ecs_os_vsprintf(ptr, fmt, args) vsprintf_s(ptr, INT_MAX, fmt, args) +#define ecs_os_snprintf(ptr, len, ...) sprintf_s(ptr, ECS_CAST(size_t, len), __VA_ARGS__) +#define ecs_os_vsnprintf(ptr, len, fmt, args) vsnprintf(ptr, ECS_CAST(size_t, len), fmt, args) #define ecs_os_strcpy(str1, str2) strcpy_s(str1, INT_MAX, str2) -#ifdef __cplusplus -#define ecs_os_strncpy(str1, str2, num) strncpy_s(str1, INT_MAX, str2, static_cast(num)) -#else -#define ecs_os_strncpy(str1, str2, num) strncpy_s(str1, INT_MAX, str2, (size_t)(num)) -#endif +#define ecs_os_strncpy(str1, str2, len) strncpy_s(str1, INT_MAX, str2, ECS_CAST(size_t, len)) #else #define ecs_os_strcat(str1, str2) strcat(str1, str2) -#define ecs_os_sprintf(ptr, ...) sprintf(ptr, __VA_ARGS__) -#define ecs_os_vsprintf(ptr, fmt, args) vsprintf(ptr, fmt, args) +#define ecs_os_snprintf(ptr, len, ...) snprintf(ptr, ECS_CAST(size_t, len), __VA_ARGS__) +#define ecs_os_vsnprintf(ptr, len, fmt, args) vsnprintf(ptr, ECS_CAST(size_t, len), fmt, args) #define ecs_os_strcpy(str1, str2) strcpy(str1, str2) -#ifdef __cplusplus -#define ecs_os_strncpy(str1, str2, num) strncpy(str1, str2, static_cast(num)) -#else -#define ecs_os_strncpy(str1, str2, num) strncpy(str1, str2, (size_t)(num)) -#endif +#define ecs_os_strncpy(str1, str2, len) strncpy(str1, str2, ECS_CAST(size_t, len)) #endif /* Files */ @@ -452,28 +517,7 @@ void ecs_os_set_api_defaults(void); #define ecs_os_now() ecs_os_api.now_() #define ecs_os_get_time(time_out) ecs_os_api.get_time_(time_out) -/* Logging */ -FLECS_API -void ecs_os_dbg(const char *file, int32_t line, const char *msg); - -FLECS_API -void ecs_os_trace(const char *file, int32_t line, const char *msg); - -FLECS_API -void ecs_os_warn(const char *file, int32_t line, const char *msg); - -FLECS_API -void ecs_os_err(const char *file, int32_t line, const char *msg); - -FLECS_API -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); - +#ifndef FLECS_DISABLE_COUNTERS #ifdef FLECS_ACCURATE_COUNTERS #define ecs_os_inc(v) (ecs_os_ainc(v)) #define ecs_os_linc(v) (ecs_os_lainc(v)) @@ -485,6 +529,13 @@ void ecs_os_strset(char **str, const char *value); #define ecs_os_dec(v) (--(*v)) #define ecs_os_ldec(v) (--(*v)) #endif +#else +#define ecs_os_inc(v) +#define ecs_os_linc(v) +#define ecs_os_dec(v) +#define ecs_os_ldec(v) +#endif + #ifdef ECS_TARGET_MINGW /* mingw bug: without this a conversion error is thrown, but isnan/isinf should @@ -508,30 +559,168 @@ void ecs_os_strset(char **str, const char *value); #define ecs_os_module_to_dl(lib) ecs_os_api.module_to_dl_(lib) #define ecs_os_module_to_etc(lib) ecs_os_api.module_to_etc_(lib) -/* Sleep with floating point time */ +/** Macro utilities + * \endcond + */ + + +/* Logging */ + +/** Log at debug level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_dbg( + const char *file, + int32_t line, + const char *msg); + +/** Log at trace level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_trace( + const char *file, + int32_t line, + const char *msg); + +/** Log at warning level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_warn( + const char *file, + int32_t line, + const char *msg); + +/** Log at error level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_err( + const char *file, + int32_t line, + const char *msg); + +/** Log at fatal level. + * + * @param file The file to log. + * @param line The line to log. + * @param msg The message to log. +*/ +FLECS_API +void ecs_os_fatal( + const char *file, + int32_t line, + const char *msg); + +/** Convert errno to string. + * + * @param err The error number. + * @return A string describing the error. + */ +FLECS_API +const char* ecs_os_strerror( + int err); + +/** Utility for assigning strings. + * This operation frees an existing string and duplicates the input string. + * + * @param str Pointer to a string value. + * @param value The string value to assign. + */ +FLECS_API +void ecs_os_strset( + char **str, + const char *value); + +/* Profile tracing */ +#ifdef FLECS_PERF_TRACE +#define ecs_os_perf_trace_push(name) ecs_os_perf_trace_push_(__FILE__, __LINE__, name) +#define ecs_os_perf_trace_pop(name) ecs_os_perf_trace_pop_(__FILE__, __LINE__, name) +#else +#define ecs_os_perf_trace_push(name) +#define ecs_os_perf_trace_pop(name) +#endif + +void ecs_os_perf_trace_push_( + const char *file, + size_t line, + const char *name); + +void ecs_os_perf_trace_pop_( + const char *file, + size_t line, + const char *name); + +/** Sleep with floating point time. + * + * @param t The time in seconds. + */ FLECS_API void ecs_sleepf( double t); -/* Measure time since provided timestamp */ +/** Measure time since provided timestamp. + * Use with a time value initialized to 0 to obtain the number of seconds since + * the epoch. The operation will write the current timestamp in start. + * + * Usage: + * @code + * ecs_time_t t = {}; + * ecs_time_measure(&t); + * // code + * double elapsed = ecs_time_measure(&t); + * @endcode + * + * @param start The starting timestamp. + * @return The time elapsed since start. + */ FLECS_API double ecs_time_measure( ecs_time_t *start); -/* Calculate difference between two timestamps */ +/** Calculate difference between two timestamps. + * + * @param t1 The first timestamp. + * @param t2 The first timestamp. + * @return The difference between timestamps. + */ FLECS_API ecs_time_t ecs_time_sub( ecs_time_t t1, ecs_time_t t2); -/* Convert time value to a double */ +/** Convert time value to a double. + * + * @param t The timestamp. + * @return The timestamp converted to a double. + */ FLECS_API double ecs_time_to_double( ecs_time_t t); +/** Return newly allocated memory that contains a copy of src. + * + * @param src The source pointer. + * @param size The number of bytes to copy. + * @return The duplicated memory. + */ FLECS_API void* ecs_os_memdup( - const void *src, + const void *src, ecs_size_t size); /** Are heap functions available? */ diff --git a/vendors/flecs/include/flecs/private/addons.h b/vendors/flecs/include/flecs/private/addons.h index 83e594c92..6f2775bef 100644 --- a/vendors/flecs/include/flecs/private/addons.h +++ b/vendors/flecs/include/flecs/private/addons.h @@ -15,20 +15,8 @@ #ifdef FLECS_NO_MODULE #undef FLECS_MODULE #endif -#ifdef FLECS_NO_PARSER -#undef FLECS_PARSER -#endif -#ifdef FLECS_NO_PLECS -#undef FLECS_PLECS -#endif -#ifdef FLECS_NO_RULES -#undef FLECS_RULES -#endif -#ifdef FLECS_NO_SNAPSHOT -#undef FLECS_SNAPSHOT -#endif -#ifdef FLECS_NO_MONITOR -#undef FLECS_MONITOR +#ifdef FLECS_NO_SCRIPT +#undef FLECS_SCRIPT #endif #ifdef FLECS_NO_STATS #undef FLECS_STATS @@ -36,6 +24,9 @@ #ifdef FLECS_NO_SYSTEM #undef FLECS_SYSTEM #endif +#ifdef FLECS_NO_ALERTS +#undef FLECS_ALERTS +#endif #ifdef FLECS_NO_PIPELINE #undef FLECS_PIPELINE #endif @@ -45,24 +36,15 @@ #ifdef FLECS_NO_META #undef FLECS_META #endif -#ifdef FLECS_NO_META_C -#undef FLECS_META_C -#endif #ifdef FLECS_NO_UNITS #undef FLECS_UNITS #endif -#ifdef FLECS_NO_EXPR -#undef FLECS_EXPR -#endif #ifdef FLECS_NO_JSON #undef FLECS_JSON #endif #ifdef FLECS_NO_DOC #undef FLECS_DOC #endif -#ifdef FLECS_NO_COREDOC -#undef FLECS_COREDOC -#endif #ifdef FLECS_NO_LOG #undef FLECS_LOG #endif @@ -83,16 +65,13 @@ #endif /* Always included, if disabled functions are replaced with dummy macros */ -#include "flecs/addons/journal.h" +#include "flecs/private/journal.h" #include "flecs/addons/log.h" /* Handle addon dependencies that need declarations to be visible in header */ -#ifdef FLECS_MONITOR -#ifndef FLECS_STATS -#define FLECS_STATS -#endif -#ifndef FLECS_SYSTEM -#define FLECS_SYSTEM +#ifdef FLECS_STATS +#ifndef FLECS_PIPELINE +#define FLECS_PIPELINE #endif #ifndef FLECS_TIMER #define FLECS_TIMER @@ -100,11 +79,9 @@ #endif #ifdef FLECS_REST +#ifndef FLECS_HTTP #define FLECS_HTTP #endif - -#ifdef FLECS_PLECS -#define FLECS_EXPR #endif #ifdef FLECS_APP @@ -170,27 +147,6 @@ #include "../addons/alerts.h" #endif -#ifdef FLECS_MONITOR -#ifdef FLECS_NO_MONITOR -#error "FLECS_NO_MONITOR failed: MONITOR is required by other addons" -#endif -#include "../addons/monitor.h" -#endif - -#ifdef FLECS_COREDOC -#ifdef FLECS_NO_COREDOC -#error "FLECS_NO_COREDOC failed: COREDOC is required by other addons" -#endif -#include "../addons/coredoc.h" -#endif - -#ifdef FLECS_DOC -#ifdef FLECS_NO_DOC -#error "FLECS_NO_DOC failed: DOC is required by other addons" -#endif -#include "../addons/doc.h" -#endif - #ifdef FLECS_JSON #ifdef FLECS_NO_JSON #error "FLECS_NO_JSON failed: JSON is required by other addons" @@ -198,11 +154,6 @@ #include "../addons/json.h" #endif -#if defined(FLECS_EXPR) || defined(FLECS_META_C) -#ifndef FLECS_META -#define FLECS_META -#endif -#endif #ifdef FLECS_UNITS #ifdef FLECS_NO_UNITS #error "FLECS_NO_UNITS failed: UNITS is required by other addons" @@ -210,60 +161,32 @@ #include "../addons/units.h" #endif -#ifdef FLECS_META -#ifdef FLECS_NO_META -#error "FLECS_NO_META failed: META is required by other addons" +#ifdef FLECS_SCRIPT +#ifdef FLECS_NO_SCRIPT +#error "FLECS_NO_SCRIPT failed: SCRIPT is required by other addons" #endif -#include "../addons/meta.h" +#include "../addons/script.h" #endif -#ifdef FLECS_EXPR -#ifdef FLECS_NO_EXPR -#error "FLECS_NO_EXPR failed: EXPR is required by other addons" -#endif -#include "../addons/expr.h" -#endif - -#ifdef FLECS_META_C -#ifdef FLECS_NO_META_C -#error "FLECS_NO_META_C failed: META_C is required by other addons" -#endif -#include "../addons/meta_c.h" -#endif - -#ifdef FLECS_PLECS -#ifdef FLECS_NO_PLECS -#error "FLECS_NO_PLECS failed: PLECS is required by other addons" -#endif -#include "../addons/plecs.h" -#endif - -#ifdef FLECS_RULES -#ifdef FLECS_NO_RULES -#error "FLECS_NO_RULES failed: RULES is required by other addons" -#endif -#include "../addons/rules.h" -#endif - -#ifdef FLECS_SNAPSHOT -#ifdef FLECS_NO_SNAPSHOT -#error "FLECS_NO_SNAPSHOT failed: SNAPSHOT is required by other addons" +#ifdef FLECS_DOC +#ifdef FLECS_NO_DOC +#error "FLECS_NO_DOC failed: DOC is required by other addons" #endif -#include "../addons/snapshot.h" +#include "../addons/doc.h" #endif -#ifdef FLECS_PARSER -#ifdef FLECS_NO_PARSER -#error "FLECS_NO_PARSER failed: PARSER is required by other addons" +#ifdef FLECS_META +#ifdef FLECS_NO_META +#error "FLECS_NO_META failed: META is required by other addons" #endif -#include "../addons/parser.h" +#include "../addons/meta.h" #endif #ifdef FLECS_OS_API_IMPL #ifdef FLECS_NO_OS_API_IMPL #error "FLECS_NO_OS_API_IMPL failed: OS_API_IMPL is required by other addons" #endif -#include "../addons/os_api_impl.h" +#include "../private/os_api_impl.h" #endif #ifdef FLECS_MODULE diff --git a/vendors/flecs/include/flecs/private/api_defines.h b/vendors/flecs/include/flecs/private/api_defines.h index f96fbe58f..5eff6b0dc 100644 --- a/vendors/flecs/include/flecs/private/api_defines.h +++ b/vendors/flecs/include/flecs/private/api_defines.h @@ -104,9 +104,6 @@ /* Filenames aren't consistent across targets as they can use different casing * (e.g. WinSock2 vs winsock2). */ #pragma clang diagnostic ignored "-Wnonportable-system-include-path" -/* Enum reflection relies on testing constant values that may not be valid for - * the enumeration. */ -#pragma clang diagnostic ignored "-Wenum-constexpr-conversion" /* Very difficult to workaround this warning in C, especially for an ECS. */ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" /* This warning gets thrown when trying to cast pointer returned from dlproc */ @@ -114,6 +111,17 @@ /* This warning can get thrown for expressions that evaluate to constants * in debug/release mode. */ #pragma clang diagnostic ignored "-Wconstant-logical-operand" +/* With soft asserts enabled the code won't abort, which in some cases means + * code paths are reached where values are uninitialized. */ +#ifdef FLECS_SOFT_ASSERT +#pragma clang diagnostic ignored "-Wsometimes-uninitialized" +#endif + +/* Allows for enum reflection support on legacy compilers */ +#if __clang_major__ < 16 +#pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +#endif + #elif defined(ECS_TARGET_GNU) #ifndef __cplusplus #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" @@ -128,6 +136,21 @@ * initialized), and later versions of gcc (>=11) seem to no longer throw this * warning. */ #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +/* Produces false positives in addons/cpp/delegate.hpp. */ +#pragma GCC diagnostic ignored "-Warray-bounds" +/* Produces false positives in queries/src/cache.c */ +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#pragma GCC diagnostic ignored "-Wrestrict" + +#elif defined(ECS_TARGET_MSVC) +/* recursive on all control paths, function will cause runtime stack overflow + * This warning is incorrectly thrown on enum reflection code. */ +#pragma warning(disable: 4717) +#endif + +/* Allows for enum reflection support on legacy compilers */ +#if defined(__GNUC__) && __GNUC__ <= 10 +#pragma GCC diagnostic ignored "-Wconversion" #endif /* Standard library dependencies */ @@ -150,7 +173,7 @@ extern "C" { #endif /* Some symbols are only exported when building in debug build, to enable - * whitebox testing of internal datastructures */ + * white-box testing of internal data structures */ #ifndef FLECS_NDEBUG #define FLECS_DBG_API FLECS_API #else @@ -186,6 +209,20 @@ typedef uint16_t ecs_flags16_t; typedef uint32_t ecs_flags32_t; typedef uint64_t ecs_flags64_t; +/* Bitmask type with compile-time defined size */ +#define ecs_flagsn_t_(bits) ecs_flags##bits##_t +#define ecs_flagsn_t(bits) ecs_flagsn_t_(bits) + +/* Bitset type that can store exactly as many bits as there are terms */ +#define ecs_termset_t ecs_flagsn_t(FLECS_TERM_COUNT_MAX) + +/* Utility macro's for setting/clearing termset bits */ +#define ECS_TERMSET_SET(set, flag) ((set) |= (ecs_termset_t)(flag)) +#define ECS_TERMSET_CLEAR(set, flag) ((set) &= (ecs_termset_t)~(flag)) +#define ECS_TERMSET_COND(set, flag, cond) ((cond) \ + ? (ECS_TERMSET_SET(set, flag)) \ + : (ECS_TERMSET_CLEAR(set, flag))) + /* Keep unsigned integers out of the codebase as they do more harm than good */ typedef int32_t ecs_size_t; @@ -201,6 +238,8 @@ typedef struct ecs_allocator_t ecs_allocator_t; #define ECS_ALIGNOF(T) (int64_t)__alignof(T) #elif defined(ECS_TARGET_GNU) #define ECS_ALIGNOF(T) (int64_t)__alignof__(T) +#elif defined(ECS_TARGET_CLANG) +#define ECS_ALIGNOF(T) (int64_t)__alignof__(T) #else #define ECS_ALIGNOF(T) ((int64_t)&((struct { char c; T d; } *)0)->d) #endif @@ -251,6 +290,11 @@ typedef struct ecs_allocator_t ecs_allocator_t; #define ECS_EQZERO(a) ECS_EQ(a, (uint64_t){0}) #define ECS_NEQZERO(a) ECS_NEQ(a, (uint64_t){0}) +/* Utilities to convert flecs version to string */ +#define FLECS_VERSION_IMPLSTR(major, minor, patch) #major "." #minor "." #patch +#define FLECS_VERSION_IMPL(major, minor, patch) \ + FLECS_VERSION_IMPLSTR(major, minor, patch) + #define ECS_CONCAT(a, b) a ## b //////////////////////////////////////////////////////////////////////////////// @@ -260,11 +304,7 @@ typedef struct ecs_allocator_t ecs_allocator_t; /* Magic number to identify the type of the object */ #define ecs_world_t_magic (0x65637377) #define ecs_stage_t_magic (0x65637373) -#define ecs_query_t_magic (0x65637371) -#define ecs_rule_t_magic (0x65637375) -#define ecs_table_t_magic (0x65637374) -#define ecs_filter_t_magic (0x65637366) -#define ecs_trigger_t_magic (0x65637372) +#define ecs_query_t_magic (0x65637375) #define ecs_observer_t_magic (0x65637362) @@ -290,6 +330,8 @@ typedef struct ecs_allocator_t ecs_allocator_t; #define ECS_PAIR_SECOND(e) (ecs_entity_t_lo(e)) #define ECS_HAS_RELATION(e, rel) (ECS_HAS_ID_FLAG(e, PAIR) && (ECS_PAIR_FIRST(e) == rel)) +#define ECS_TERM_REF_FLAGS(ref) ((ref)->id & EcsTermRefFlags) +#define ECS_TERM_REF_ID(ref) ((ref)->id & ~EcsTermRefFlags) //////////////////////////////////////////////////////////////////////////////// //// Convert between C typenames and variables @@ -312,9 +354,9 @@ typedef struct ecs_allocator_t ecs_allocator_t; #define ecs_pair_first(world, pair) ecs_get_alive(world, ECS_PAIR_FIRST(pair)) #define ecs_pair_second(world, pair) ecs_get_alive(world, ECS_PAIR_SECOND(pair)) #define ecs_pair_relation ecs_pair_first -#define ecs_pair_object ecs_pair_second +#define ecs_pair_target ecs_pair_second -#define ecs_poly_id(tag) ecs_pair(ecs_id(EcsPoly), tag) +#define flecs_poly_id(tag) ecs_pair(ecs_id(EcsPoly), tag) //////////////////////////////////////////////////////////////////////////////// @@ -408,7 +450,7 @@ typedef struct ecs_allocator_t ecs_allocator_t; {\ for (int32_t i = 0; i < _it->count; i ++) {\ ecs_entity_t entity = _it->entities[i];\ - type *var = &((type*)_it->ptrs[0])[i];\ + type *var = ecs_field(_it, type, 0);\ (void)entity;\ (void)var;\ __VA_ARGS__\ diff --git a/vendors/flecs/include/flecs/private/api_flags.h b/vendors/flecs/include/flecs/private/api_flags.h index 3f465d345..be05493f8 100644 --- a/vendors/flecs/include/flecs/private/api_flags.h +++ b/vendors/flecs/include/flecs/private/api_flags.h @@ -23,7 +23,7 @@ extern "C" { #define EcsWorldMeasureFrameTime (1u << 5) #define EcsWorldMeasureSystemTime (1u << 6) #define EcsWorldMultiThreaded (1u << 7) - +#define EcsWorldFrameInProgress (1u << 8) //////////////////////////////////////////////////////////////////////////////// //// OS API flags @@ -61,26 +61,33 @@ extern "C" { (EcsIdOnDeleteObjectPanic|EcsIdOnDeleteObjectRemove|\ EcsIdOnDeleteObjectDelete) -#define EcsIdExclusive (1u << 6) -#define EcsIdDontInherit (1u << 7) -#define EcsIdTraversable (1u << 8) -#define EcsIdTag (1u << 9) -#define EcsIdWith (1u << 10) -#define EcsIdUnion (1u << 11) -#define EcsIdAlwaysOverride (1u << 12) +#define EcsIdOnInstantiateOverride (1u << 6) +#define EcsIdOnInstantiateInherit (1u << 7) +#define EcsIdOnInstantiateDontInherit (1u << 8) +#define EcsIdOnInstantiateMask\ + (EcsIdOnInstantiateOverride|EcsIdOnInstantiateInherit|\ + EcsIdOnInstantiateDontInherit) + +#define EcsIdExclusive (1u << 9) +#define EcsIdTraversable (1u << 10) +#define EcsIdTag (1u << 11) +#define EcsIdWith (1u << 12) +#define EcsIdCanToggle (1u << 13) +#define EcsIdIsTransitive (1u << 14) #define EcsIdHasOnAdd (1u << 16) /* Same values as table flags */ #define EcsIdHasOnRemove (1u << 17) #define EcsIdHasOnSet (1u << 18) -#define EcsIdHasUnSet (1u << 19) -#define EcsIdHasOnTableFill (1u << 20) -#define EcsIdHasOnTableEmpty (1u << 21) -#define EcsIdHasOnTableCreate (1u << 22) -#define EcsIdHasOnTableDelete (1u << 23) +#define EcsIdHasOnTableFill (1u << 19) +#define EcsIdHasOnTableEmpty (1u << 20) +#define EcsIdHasOnTableCreate (1u << 21) +#define EcsIdHasOnTableDelete (1u << 22) +#define EcsIdIsSparse (1u << 23) +#define EcsIdIsUnion (1u << 24) #define EcsIdEventMask\ - (EcsIdHasOnAdd|EcsIdHasOnRemove|EcsIdHasOnSet|EcsIdHasUnSet|\ + (EcsIdHasOnAdd|EcsIdHasOnRemove|EcsIdHasOnSet|\ EcsIdHasOnTableFill|EcsIdHasOnTableEmpty|EcsIdHasOnTableCreate|\ - EcsIdHasOnTableDelete) + EcsIdHasOnTableDelete|EcsIdIsSparse|EcsIdIsUnion) #define EcsIdMarkedForDelete (1u << 30) @@ -92,6 +99,12 @@ extern "C" { #define ECS_ID_ON_DELETE_FLAG(id) (1u << ((id) - EcsRemove)) #define ECS_ID_ON_DELETE_TARGET_FLAG(id) (1u << (3 + ((id) - EcsRemove))) +/* Utilities for converting from flags to instantiate policies and vice versa */ +#define ECS_ID_ON_INSTANTIATE(flags) \ + ((ecs_entity_t[]){EcsOverride, EcsOverride, EcsInherit, 0, EcsDontInherit}\ + [(((flags) & EcsIdOnInstantiateMask) >> 6)]) +#define ECS_ID_ON_INSTANTIATE_FLAG(id) (1u << (6 + ((id) - EcsOverride))) + //////////////////////////////////////////////////////////////////////////////// //// Iterator flags (used by ecs_iter_t::flags) @@ -99,49 +112,87 @@ extern "C" { #define EcsIterIsValid (1u << 0u) /* Does iterator contain valid result */ #define EcsIterNoData (1u << 1u) /* Does iterator provide (component) data */ -#define EcsIterIsInstanced (1u << 2u) /* Is iterator instanced */ -#define EcsIterHasShared (1u << 3u) /* Does result have shared terms */ -#define EcsIterTableOnly (1u << 4u) /* Result only populates table */ -#define EcsIterEntityOptional (1u << 5u) /* Treat terms with entity subject as optional */ -#define EcsIterNoResults (1u << 6u) /* Iterator has no results */ -#define EcsIterIgnoreThis (1u << 7u) /* Only evaluate non-this terms */ -#define EcsIterMatchVar (1u << 8u) -#define EcsIterHasCondSet (1u << 10u) /* Does iterator have conditionally set fields */ -#define EcsIterProfile (1u << 11u) /* Profile iterator performance */ -#define EcsIterTrivialSearch (1u << 12u) /* Trivial iterator mode */ -#define EcsIterTrivialSearchNoData (1u << 13u) /* Trivial iterator w/no data */ -#define EcsIterTrivialTest (1u << 14u) /* Trivial test mode (constrained $this) */ -#define EcsIterTrivialSearchWildcard (1u << 15u) /* Trivial search with wildcard ids */ +#define EcsIterNoResults (1u << 3u) /* Iterator has no results */ +#define EcsIterIgnoreThis (1u << 4u) /* Only evaluate non-this terms */ +#define EcsIterHasCondSet (1u << 6u) /* Does iterator have conditionally set fields */ +#define EcsIterProfile (1u << 7u) /* Profile iterator performance */ +#define EcsIterTrivialSearch (1u << 8u) /* Trivial iterator mode */ +#define EcsIterTrivialTest (1u << 11u) /* Trivial test mode (constrained $this) */ +#define EcsIterTrivialCached (1u << 14u) /* Trivial search for cached query */ +#define EcsIterCacheSearch (1u << 15u) /* Cache search */ +#define EcsIterFixedInChangeComputed (1u << 16u) /* Change detection for fixed in terms is done */ +#define EcsIterFixedInChanged (1u << 17u) /* Fixed in terms changed */ +#define EcsIterSkip (1u << 18u) /* Result was skipped for change detection */ +#define EcsIterCppEach (1u << 19u) /* Uses C++ 'each' iterator */ + +/* Same as event flags */ +#define EcsIterTableOnly (1u << 20u) /* Result only populates table */ + //////////////////////////////////////////////////////////////////////////////// //// Event flags (used by ecs_event_decs_t::flags) //////////////////////////////////////////////////////////////////////////////// -#define EcsEventTableOnly (1u << 4u) /* Table event (no data, same as iter flags) */ -#define EcsEventNoOnSet (1u << 16u) /* Don't emit OnSet/UnSet for inherited ids */ +#define EcsEventTableOnly (1u << 20u) /* Table event (no data, same as iter flags) */ +#define EcsEventNoOnSet (1u << 16u) /* Don't emit OnSet for inherited ids */ + + +//////////////////////////////////////////////////////////////////////////////// +//// Query flags (used by ecs_query_t::flags) +//////////////////////////////////////////////////////////////////////////////// + +/* Flags that can only be set by the query implementation */ +#define EcsQueryMatchThis (1u << 11u) /* Query has terms with $this source */ +#define EcsQueryMatchOnlyThis (1u << 12u) /* Query only has terms with $this source */ +#define EcsQueryMatchOnlySelf (1u << 13u) /* Query has no terms with up traversal */ +#define EcsQueryMatchWildcards (1u << 14u) /* Query matches wildcards */ +#define EcsQueryMatchNothing (1u << 15u) /* Query matches nothing */ +#define EcsQueryHasCondSet (1u << 16u) /* Query has conditionally set fields */ +#define EcsQueryHasPred (1u << 17u) /* Query has equality predicates */ +#define EcsQueryHasScopes (1u << 18u) /* Query has query scopes */ +#define EcsQueryHasRefs (1u << 19u) /* Query has terms with static source */ +#define EcsQueryHasOutTerms (1u << 20u) /* Query has [out] terms */ +#define EcsQueryHasNonThisOutTerms (1u << 21u) /* Query has [out] terms with no $this source */ +#define EcsQueryHasMonitor (1u << 22u) /* Query has monitor for change detection */ +#define EcsQueryIsTrivial (1u << 23u) /* Query can use trivial evaluation function */ +#define EcsQueryHasCacheable (1u << 24u) /* Query has cacheable terms */ +#define EcsQueryIsCacheable (1u << 25u) /* All terms of query are cacheable */ +#define EcsQueryHasTableThisVar (1u << 26u) /* Does query have $this table var */ +#define EcsQueryCacheYieldEmptyTables (1u << 27u) /* Does query cache empty tables */ +#define EcsQueryNested (1u << 28u) /* Query created by a query (for observer, cache) */ + +//////////////////////////////////////////////////////////////////////////////// +//// Term flags (used by ecs_term_t::flags_) +//////////////////////////////////////////////////////////////////////////////// + +#define EcsTermMatchAny (1u << 0) +#define EcsTermMatchAnySrc (1u << 1) +#define EcsTermTransitive (1u << 2) +#define EcsTermReflexive (1u << 3) +#define EcsTermIdInherited (1u << 4) +#define EcsTermIsTrivial (1u << 5) +#define EcsTermIsCacheable (1u << 7) +#define EcsTermIsScope (1u << 8) +#define EcsTermIsMember (1u << 9) +#define EcsTermIsToggle (1u << 10) +#define EcsTermKeepAlive (1u << 11) +#define EcsTermIsSparse (1u << 12) +#define EcsTermIsUnion (1u << 13) +#define EcsTermIsOr (1u << 14) + //////////////////////////////////////////////////////////////////////////////// -//// Filter flags (used by ecs_filter_t::flags) +//// Observer flags (used by ecs_observer_t::flags) //////////////////////////////////////////////////////////////////////////////// -#define EcsFilterMatchThis (1u << 1u) /* Has terms that match This */ -#define EcsFilterMatchOnlyThis (1u << 2u) /* Has only terms that match This */ -#define EcsFilterMatchPrefab (1u << 3u) /* Does filter match prefabs */ -#define EcsFilterMatchDisabled (1u << 4u) /* Does filter match disabled entities */ -#define EcsFilterMatchEmptyTables (1u << 5u) /* Does filter return empty tables */ -#define EcsFilterMatchAnything (1u << 6u) /* False if filter has no/only Not terms */ -#define EcsFilterNoData (1u << 7u) /* When true, data fields won't be populated */ -#define EcsFilterIsInstanced (1u << 8u) /* Is filter instanced (see ecs_filter_desc_t) */ -#define EcsFilterPopulate (1u << 9u) /* Populate data, ignore non-matching fields */ -#define EcsFilterHasCondSet (1u << 10u) /* Does filter have conditionally set fields */ -#define EcsFilterUnresolvedByName (1u << 11u) /* Use by-name matching for unresolved entity identifiers */ -#define EcsFilterHasPred (1u << 12u) /* Filter has equality predicates */ -#define EcsFilterHasScopes (1u << 13u) /* Filter has query scopes */ -#define EcsFilterIsTrivial (1u << 14u) /* Trivial filter */ -#define EcsFilterMatchOnlySelf (1u << 15u) /* Filter has no up traversal */ -#define EcsFilterHasWildcards (1u << 16u) /* Filter has no up traversal */ -#define EcsFilterOwnsStorage (1u << 17u) /* Is ecs_filter_t object owned by filter */ -#define EcsFilterOwnsTermsStorage (1u << 18u) /* Is terms array owned by filter */ +#define EcsObserverIsMulti (1u << 1u) /* Does observer have multiple terms */ +#define EcsObserverIsMonitor (1u << 2u) /* Is observer a monitor */ +#define EcsObserverIsDisabled (1u << 3u) /* Is observer entity disabled */ +#define EcsObserverIsParentDisabled (1u << 4u) /* Is module parent of observer disabled */ +#define EcsObserverBypassQuery (1u << 5u) /* Don't evaluate query for multi-component observer*/ +#define EcsObserverYieldOnCreate (1u << 6u) /* Yield matching entities when creating observer */ +#define EcsObserverYieldOnDelete (1u << 7u) /* Yield matching entities when deleting observer */ + //////////////////////////////////////////////////////////////////////////////// //// Table flags (used by ecs_table_t::flags) @@ -155,47 +206,35 @@ extern "C" { #define EcsTableHasPairs (1u << 6u) /* Does the table type have pairs */ #define EcsTableHasModule (1u << 7u) /* Does the table have module data */ #define EcsTableIsDisabled (1u << 8u) /* Does the table type has EcsDisabled */ -#define EcsTableHasCtors (1u << 9u) -#define EcsTableHasDtors (1u << 10u) -#define EcsTableHasCopy (1u << 11u) -#define EcsTableHasMove (1u << 12u) -#define EcsTableHasUnion (1u << 13u) +#define EcsTableNotQueryable (1u << 9u) /* Table should never be returned by queries */ +#define EcsTableHasCtors (1u << 10u) +#define EcsTableHasDtors (1u << 11u) +#define EcsTableHasCopy (1u << 12u) +#define EcsTableHasMove (1u << 13u) #define EcsTableHasToggle (1u << 14u) #define EcsTableHasOverrides (1u << 15u) #define EcsTableHasOnAdd (1u << 16u) /* Same values as id flags */ #define EcsTableHasOnRemove (1u << 17u) #define EcsTableHasOnSet (1u << 18u) -#define EcsTableHasUnSet (1u << 19u) -#define EcsTableHasOnTableFill (1u << 20u) -#define EcsTableHasOnTableEmpty (1u << 21u) -#define EcsTableHasOnTableCreate (1u << 22u) -#define EcsTableHasOnTableDelete (1u << 23u) - -#define EcsTableHasTraversable (1u << 25u) -#define EcsTableHasTarget (1u << 26u) - +#define EcsTableHasOnTableFill (1u << 19u) +#define EcsTableHasOnTableEmpty (1u << 20u) +#define EcsTableHasOnTableCreate (1u << 21u) +#define EcsTableHasOnTableDelete (1u << 22u) +#define EcsTableHasSparse (1u << 23u) +#define EcsTableHasUnion (1u << 24u) + +#define EcsTableHasTraversable (1u << 26u) #define EcsTableMarkedForDelete (1u << 30u) /* Composite table flags */ -#define EcsTableHasLifecycle (EcsTableHasCtors | EcsTableHasDtors) -#define EcsTableIsComplex (EcsTableHasLifecycle | EcsTableHasUnion | EcsTableHasToggle) -#define EcsTableHasAddActions (EcsTableHasIsA | EcsTableHasUnion | EcsTableHasCtors | EcsTableHasOnAdd | EcsTableHasOnSet) -#define EcsTableHasRemoveActions (EcsTableHasIsA | EcsTableHasDtors | EcsTableHasOnRemove | EcsTableHasUnSet) - - -//////////////////////////////////////////////////////////////////////////////// -//// Query flags (used by ecs_query_t::flags) -//////////////////////////////////////////////////////////////////////////////// - -#define EcsQueryHasRefs (1u << 1u) /* Does query have references */ -#define EcsQueryIsSubquery (1u << 2u) /* Is query a subquery */ -#define EcsQueryIsOrphaned (1u << 3u) /* Is subquery orphaned */ -#define EcsQueryHasOutTerms (1u << 4u) /* Does query have out terms */ -#define EcsQueryHasNonThisOutTerms (1u << 5u) /* Does query have non-this out terms */ -#define EcsQueryHasMonitor (1u << 6u) /* Does query track changes */ -#define EcsQueryTrivialIter (1u << 7u) /* Does the query require special features to iterate */ - +#define EcsTableHasLifecycle (EcsTableHasCtors | EcsTableHasDtors) +#define EcsTableIsComplex (EcsTableHasLifecycle | EcsTableHasToggle | EcsTableHasSparse) +#define EcsTableHasAddActions (EcsTableHasIsA | EcsTableHasCtors | EcsTableHasOnAdd | EcsTableHasOnSet) +#define EcsTableHasRemoveActions (EcsTableHasIsA | EcsTableHasDtors | EcsTableHasOnRemove) +#define EcsTableEdgeFlags (EcsTableHasOnAdd | EcsTableHasOnRemove | EcsTableHasSparse | EcsTableHasUnion) +#define EcsTableAddEdgeFlags (EcsTableHasOnAdd | EcsTableHasSparse | EcsTableHasUnion) +#define EcsTableRemoveEdgeFlags (EcsTableHasOnRemove | EcsTableHasSparse | EcsTableHasUnion) //////////////////////////////////////////////////////////////////////////////// //// Aperiodic action flags (used by ecs_run_aperiodic) diff --git a/vendors/flecs/include/flecs/private/api_support.h b/vendors/flecs/include/flecs/private/api_support.h index f99a3402d..927b025f2 100644 --- a/vendors/flecs/include/flecs/private/api_support.h +++ b/vendors/flecs/include/flecs/private/api_support.h @@ -28,39 +28,112 @@ extern "C" { /** Maximum length of a parser token (used by parser-related addons) */ #define ECS_MAX_TOKEN_SIZE (256) -//////////////////////////////////////////////////////////////////////////////// -//// Global type handles -//////////////////////////////////////////////////////////////////////////////// - -/** This allows passing 0 as type to functions that accept ids */ -#define FLECS_ID0ID_ 0 - FLECS_API -char* ecs_module_path_from_c( +char* flecs_module_path_from_c( const char *c_name); -bool ecs_identifier_is_0( +bool flecs_identifier_is_0( const char *id); /* Constructor that zeromem's a component value */ FLECS_API -void ecs_default_ctor( +void flecs_default_ctor( void *ptr, int32_t count, const ecs_type_info_t *ctx); /* Create allocated string from format */ FLECS_DBG_API -char* ecs_vasprintf( +char* flecs_vasprintf( const char *fmt, va_list args); /* Create allocated string from format */ FLECS_API -char* ecs_asprintf( +char* flecs_asprintf( const char *fmt, ...); +/** Write an escaped character. + * Write a character to an output string, insert escape character if necessary. + * + * @param out The string to write the character to. + * @param in The input character. + * @param delimiter The delimiter used (for example '"') + * @return Pointer to the character after the last one written. + */ +FLECS_API +char* flecs_chresc( + char *out, + char in, + char delimiter); + +/** Parse an escaped character. + * Parse a character with a potential escape sequence. + * + * @param in Pointer to character in input string. + * @param out Output string. + * @return Pointer to the character after the last one read. + */ +const char* flecs_chrparse( + const char *in, + char *out); + +/** Write an escaped string. + * Write an input string to an output string, escape characters where necessary. + * To determine the size of the output string, call the operation with a NULL + * argument for 'out', and use the returned size to allocate a string that is + * large enough. + * + * @param out Pointer to output string (must be). + * @param size Maximum number of characters written to output. + * @param delimiter The delimiter used (for example '"'). + * @param in The input string. + * @return The number of characters that (would) have been written. + */ +FLECS_API +ecs_size_t flecs_stresc( + char *out, + ecs_size_t size, + char delimiter, + const char *in); + +/** Return escaped string. + * Return escaped version of input string. Same as flecs_stresc(), but returns an + * allocated string of the right size. + * + * @param delimiter The delimiter used (for example '"'). + * @param in The input string. + * @return Escaped string. + */ +FLECS_API +char* flecs_astresc( + char delimiter, + const char *in); + +/** Skip whitespace and newline characters. + * This function skips whitespace characters. + * + * @param ptr Pointer to (potential) whitespaces to skip. + * @return Pointer to the next non-whitespace character. + */ +FLECS_API +const char* flecs_parse_ws_eol( + const char *ptr); + +/** Parse digit. + * This function will parse until the first non-digit character is found. The + * provided expression must contain at least one digit character. + * + * @param ptr The expression to parse. + * @param token The output buffer. + * @return Pointer to the first non-digit character. + */ +FLECS_API +const char* flecs_parse_digit( + const char *ptr, + char *token); + /* Convert identifier to snake case */ FLECS_API char* flecs_to_snake_case( @@ -74,6 +147,57 @@ FLECS_DBG_API void flecs_dump_backtrace( void *stream); +/* Suspend/resume readonly state. To fully support implicit registration of + * components, it should be possible to register components while the world is + * in readonly mode. It is not uncommon that a component is used first from + * within a system, which are often ran while in readonly mode. + * + * Suspending readonly mode is only allowed when the world is not multithreaded. + * When a world is multithreaded, it is not safe to (even temporarily) leave + * readonly mode, so a multithreaded application should always explicitly + * register components in advance. + * + * These operations also suspend deferred mode. + */ +typedef struct ecs_suspend_readonly_state_t { + bool is_readonly; + bool is_deferred; + int32_t defer_count; + ecs_entity_t scope; + ecs_entity_t with; + ecs_vec_t commands; + ecs_stack_t defer_stack; + ecs_stage_t *stage; +} ecs_suspend_readonly_state_t; + +FLECS_API +ecs_world_t* flecs_suspend_readonly( + const ecs_world_t *world, + ecs_suspend_readonly_state_t *state); + +FLECS_API +void flecs_resume_readonly( + ecs_world_t *world, + ecs_suspend_readonly_state_t *state); + +FLECS_API +int32_t flecs_poly_claim_( + ecs_poly_t *poly); + +FLECS_API +int32_t flecs_poly_release_( + ecs_poly_t *poly); + +FLECS_API +int32_t flecs_poly_refcount( + ecs_poly_t *poly); + +#define flecs_poly_claim(poly) \ + flecs_poly_claim_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) +#define flecs_poly_release(poly) \ + flecs_poly_release_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) + + /** Calculate offset from address */ #ifdef __cplusplus #define ECS_OFFSET(o, offset) reinterpret_cast((reinterpret_cast(o)) + (static_cast(offset))) @@ -91,8 +215,18 @@ void flecs_dump_backtrace( #define ECS_BIT_COND(flags, bit, cond) ((cond) \ ? (ECS_BIT_SET(flags, bit)) \ : (ECS_BIT_CLEAR(flags, bit))) + +#define ECS_BIT_CLEAR16(flags, bit) (flags) &= (ecs_flags16_t)~(bit) +#define ECS_BIT_COND16(flags, bit, cond) ((cond) \ + ? (ECS_BIT_SET(flags, bit)) \ + : (ECS_BIT_CLEAR16(flags, bit))) + #define ECS_BIT_IS_SET(flags, bit) ((flags) & (bit)) +#define ECS_BIT_SETN(flags, n) ECS_BIT_SET(flags, 1llu << n) +#define ECS_BIT_CLEARN(flags, n) ECS_BIT_CLEAR(flags, 1llu << n) +#define ECS_BIT_CONDN(flags, n, cond) ECS_BIT_COND(flags, 1llu << n, cond) + #ifdef __cplusplus } #endif diff --git a/vendors/flecs/include/flecs/private/api_types.h b/vendors/flecs/include/flecs/private/api_types.h index 0684cca96..83afc3873 100644 --- a/vendors/flecs/include/flecs/private/api_types.h +++ b/vendors/flecs/include/flecs/private/api_types.h @@ -20,23 +20,16 @@ extern "C" { //// Opaque types //////////////////////////////////////////////////////////////////////////////// -/** A stage enables modification while iterating and from multiple threads */ -typedef struct ecs_stage_t ecs_stage_t; - /** Table data */ typedef struct ecs_data_t ecs_data_t; -/* Switch list */ -typedef struct ecs_switch_t ecs_switch_t; - /* Cached query table data */ -typedef struct ecs_query_table_match_t ecs_query_table_match_t; +typedef struct ecs_query_cache_table_match_t ecs_query_cache_table_match_t; //////////////////////////////////////////////////////////////////////////////// //// Non-opaque types //////////////////////////////////////////////////////////////////////////////// -/** Mixin for emitting events to triggers/observers */ /** All observers for a specific event */ typedef struct ecs_event_record_t { struct ecs_event_id_record_t *any; @@ -50,19 +43,10 @@ struct ecs_observable_t { ecs_event_record_t on_add; ecs_event_record_t on_remove; ecs_event_record_t on_set; - ecs_event_record_t un_set; ecs_event_record_t on_wildcard; ecs_sparse_t events; /* sparse */ }; -/** Record for entity index */ -struct ecs_record_t { - ecs_id_record_t *idr; /* Id record to (*, entity) for target entities */ - ecs_table_t *table; /* Identifies a type (and table) in world */ - uint32_t row; /* Table row of the entity */ - int32_t dense; /* Index in dense array */ -}; - /** Range in table */ typedef struct ecs_table_range_t { ecs_table_t *table; @@ -85,22 +69,11 @@ typedef struct ecs_var_t { struct ecs_ref_t { ecs_entity_t entity; /* Entity */ ecs_entity_t id; /* Component id */ + uint64_t table_id; /* Table id for detecting ABA issues */ struct ecs_table_record_t *tr; /* Table record for component */ ecs_record_t *record; /* Entity index record */ }; -/* Cursor to stack allocator. Type is public to allow for white box testing. */ -struct ecs_stack_page_t; - -typedef struct ecs_stack_cursor_t { - struct ecs_stack_cursor_t *prev; - struct ecs_stack_page_t *page; - int16_t sp; - bool is_free; -#ifdef FLECS_DEBUG - struct ecs_stack_t *owner; -#endif -} ecs_stack_cursor_t; /* Page-iterator specific data */ typedef struct ecs_page_iter_t { @@ -121,96 +94,47 @@ typedef struct ecs_table_cache_iter_t { struct ecs_table_cache_hdr_t *next_list; } ecs_table_cache_iter_t; -/** Term-iterator specific data */ -typedef struct ecs_term_iter_t { - ecs_term_t term; - ecs_id_record_t *self_index; - ecs_id_record_t *set_index; - - ecs_id_record_t *cur; +/** Each iterator */ +typedef struct ecs_each_iter_t { ecs_table_cache_iter_t it; - int32_t index; - int32_t observed_table_count; - - ecs_table_t *table; - int32_t cur_match; - int32_t match_count; - int32_t last_column; - - bool empty_tables; - - /* Storage */ - ecs_id_t id; - int32_t column; - ecs_entity_t subject; - ecs_size_t size; - void *ptr; -} ecs_term_iter_t; -typedef enum ecs_iter_kind_t { - EcsIterEvalCondition, - EcsIterEvalTables, - EcsIterEvalChain, - EcsIterEvalNone -} ecs_iter_kind_t; + /* Storage for iterator fields */ + ecs_id_t ids; + ecs_entity_t sources; + ecs_size_t sizes; + int32_t columns; + const ecs_table_record_t* trs; +} ecs_each_iter_t; -/** Filter-iterator specific data */ -typedef struct ecs_filter_iter_t { - const ecs_filter_t *filter; - ecs_iter_kind_t kind; - ecs_term_iter_t term_iter; - int32_t matches_left; - int32_t pivot_term; -} ecs_filter_iter_t; - -/** Query-iterator specific data */ -typedef struct ecs_query_iter_t { - ecs_query_t *query; - ecs_query_table_match_t *node, *prev, *last; - int32_t sparse_smallest; - int32_t sparse_first; - int32_t bitset_first; - int32_t skip_count; -} ecs_query_iter_t; - -/** Snapshot-iterator specific data */ -typedef struct ecs_snapshot_iter_t { - ecs_filter_t filter; - ecs_vec_t tables; /* ecs_table_leaf_t */ - int32_t index; -} ecs_snapshot_iter_t; - -typedef struct ecs_rule_op_profile_t { +typedef struct ecs_query_op_profile_t { int32_t count[2]; /* 0 = enter, 1 = redo */ -} ecs_rule_op_profile_t; +} ecs_query_op_profile_t; -/** Rule-iterator specific data */ -typedef struct ecs_rule_iter_t { - const ecs_rule_t *rule; - struct ecs_var_t *vars; /* Variable storage */ - const struct ecs_rule_var_t *rule_vars; - const struct ecs_rule_op_t *ops; - struct ecs_rule_op_ctx_t *op_ctx; /* Operation-specific state */ +/** Query iterator */ +typedef struct ecs_query_iter_t { + const ecs_query_t *query; + struct ecs_var_t *vars; /* Variable storage */ + const struct ecs_query_var_t *query_vars; + const struct ecs_query_op_t *ops; + struct ecs_query_op_ctx_t *op_ctx; /* Operation-specific state */ + ecs_query_cache_table_match_t *node, *prev, *last; /* For cached iteration */ uint64_t *written; - ecs_flags32_t source_set; + int32_t skip_count; -#ifdef FLECS_DEBUG - ecs_rule_op_profile_t *profile; -#endif + ecs_query_op_profile_t *profile; int16_t op; int16_t sp; -} ecs_rule_iter_t; +} ecs_query_iter_t; /* Bits for tracking whether a cache was used/whether the array was allocated. * Used by flecs_iter_init, flecs_iter_validate and ecs_iter_fini. * Constants are named to enable easy macro substitution. */ #define flecs_iter_cache_ids (1u << 0u) -#define flecs_iter_cache_columns (1u << 1u) +#define flecs_iter_cache_trs (1u << 1u) #define flecs_iter_cache_sources (1u << 2u) #define flecs_iter_cache_ptrs (1u << 3u) -#define flecs_iter_cache_match_indices (1u << 4u) -#define flecs_iter_cache_variables (1u << 5u) +#define flecs_iter_cache_variables (1u << 4u) #define flecs_iter_cache_all (255) /* Inline iterator arrays to prevent allocations for small array sizes */ @@ -224,86 +148,16 @@ typedef struct ecs_iter_cache_t { * progress & to provide builtin storage. */ typedef struct ecs_iter_private_t { union { - ecs_term_iter_t term; - ecs_filter_iter_t filter; ecs_query_iter_t query; - ecs_rule_iter_t rule; - ecs_snapshot_iter_t snapshot; ecs_page_iter_t page; ecs_worker_iter_t worker; + ecs_each_iter_t each; } iter; /* Iterator specific data */ - void *entity_iter; /* Filter applied after matching a table */ + void *entity_iter; /* Query applied after matching a table */ ecs_iter_cache_t cache; /* Inline arrays to reduce allocations */ } ecs_iter_private_t; -/** Iterator */ -struct ecs_iter_t { - /* World */ - ecs_world_t *world; /* The world */ - ecs_world_t *real_world; /* Actual world. This differs from world when in readonly mode */ - - /* Matched data */ - ecs_entity_t *entities; /* Entity identifiers */ - void **ptrs; /* Pointers to components. Array if from this, pointer if not. */ - ecs_size_t *sizes; /* Component sizes */ - ecs_table_t *table; /* Current table */ - ecs_table_t *other_table; /* Prev or next table when adding/removing */ - ecs_id_t *ids; /* (Component) ids */ - ecs_var_t *variables; /* Values of variables (if any) */ - int32_t *columns; /* Query term to table column mapping */ - ecs_entity_t *sources; /* Entity on which the id was matched (0 if same as entities) */ - int32_t *match_indices; /* Indices of current match for term. Allows an iterator to iterate - * all permutations of wildcards in query. */ - ecs_ref_t *references; /* Cached refs to components (if iterating a cache) */ - ecs_flags64_t constrained_vars; /* Bitset that marks constrained variables */ - uint64_t group_id; /* Group id for table, if group_by is used */ - int32_t field_count; /* Number of fields in iterator */ - - /* Input information */ - ecs_entity_t system; /* The system (if applicable) */ - ecs_entity_t event; /* The event (if applicable) */ - ecs_id_t event_id; /* The (component) id for the event */ - - /* Query information */ - ecs_term_t *terms; /* Terms of query being evaluated */ - int32_t table_count; /* Active table count for query */ - int32_t term_index; /* Index of term that emitted an event. - * This field will be set to the 'index' field - * of an observer term. */ - int32_t variable_count; /* Number of variables for query */ - char **variable_names; /* Names of variables (if any) */ - - /* Context */ - void *param; /* Param passed to ecs_run */ - void *ctx; /* System context */ - void *binding_ctx; /* Binding context */ - - /* Time */ - ecs_ftime_t delta_time; /* Time elapsed since last frame */ - ecs_ftime_t delta_system_time;/* Time elapsed since last system invocation */ - - /* Iterator counters */ - int32_t frame_offset; /* Offset relative to start of iteration */ - int32_t offset; /* Offset relative to current table */ - int32_t count; /* Number of entities to iterate */ - int32_t instance_count; /* Number of entities to iterate before next table */ - - /* Iterator flags */ - ecs_flags32_t flags; - - ecs_entity_t interrupted_by; /* When set, system execution is interrupted */ - - ecs_iter_private_t priv; /* Private data */ - - /* Chained iterators */ - ecs_iter_next_action_t next; /* Function to progress iterator */ - ecs_iter_action_t callback; /* Callback of system or observer */ - ecs_iter_action_t set_var; /* Invoked after setting variable (optionally set) */ - ecs_iter_fini_action_t fini; /* Function to cleanup iterator resources */ - ecs_iter_t *chain_it; /* Optional, allows for creating iterator chains */ -}; - #ifdef __cplusplus } #endif diff --git a/vendors/flecs/include/flecs/addons/journal.h b/vendors/flecs/include/flecs/private/journal.h similarity index 94% rename from vendors/flecs/include/flecs/addons/journal.h rename to vendors/flecs/include/flecs/private/journal.h index 2fa5a0f86..1be6fe2d7 100644 --- a/vendors/flecs/include/flecs/addons/journal.h +++ b/vendors/flecs/include/flecs/private/journal.h @@ -5,8 +5,8 @@ * The journaling addon traces API calls. The trace is formatted as runnable * C code, which allows for (partially) reproducing the behavior of an app * with the journaling trace. - * - * The journaling addon is disabled by default. Enabling it can have a + * + * The journaling addon is disabled by default. Enabling it can have a * significant impact on performance. */ @@ -21,9 +21,10 @@ /** * @defgroup c_addons_journal Journal - * @brief Journaling addon (disabled by default). - * - * \ingroup c_addons + * @ingroup c_addons + * Journaling addon (disabled by default). + * + * * @{ */ @@ -64,12 +65,11 @@ void flecs_journal_end(void); #ifdef __cplusplus } #endif // __cplusplus +/** @} */ #endif // FLECS_JOURNAL_H #else #define flecs_journal_begin(...) #define flecs_journal_end(...) #define flecs_journal(...) -/** @} */ - #endif // FLECS_JOURNAL diff --git a/vendors/flecs/include/flecs/addons/os_api_impl.h b/vendors/flecs/include/flecs/private/os_api_impl.h similarity index 83% rename from vendors/flecs/include/flecs/addons/os_api_impl.h rename to vendors/flecs/include/flecs/private/os_api_impl.h index 8496c8006..4dff1e4e8 100644 --- a/vendors/flecs/include/flecs/addons/os_api_impl.h +++ b/vendors/flecs/include/flecs/private/os_api_impl.h @@ -7,9 +7,9 @@ /** * @defgroup c_addons_os_api_impl OS API Implementation - * @brief Default implementation for OS API interface. - * - * \ingroup c_addons + * @ingroup c_addons + * Default implementation for OS API interface. + * * @{ */ diff --git a/vendors/flecs/include/flecs/private/switch_list.h b/vendors/flecs/include/flecs/private/switch_list.h deleted file mode 100644 index 55fb1533a..000000000 --- a/vendors/flecs/include/flecs/private/switch_list.h +++ /dev/null @@ -1,129 +0,0 @@ -/** - * @file switch_list.h - * @brief Interleaved linked list for storing mutually exclusive values. - */ - -#ifndef FLECS_SWITCH_LIST_H -#define FLECS_SWITCH_LIST_H - -#include "flecs/private/api_defines.h" - -typedef struct ecs_switch_header_t { - int32_t element; /* First element for value */ - int32_t count; /* Number of elements for value */ -} ecs_switch_header_t; - -typedef struct ecs_switch_node_t { - int32_t next; /* Next node in list */ - int32_t prev; /* Prev node in list */ -} ecs_switch_node_t; - -struct ecs_switch_t { - ecs_map_t hdrs; /* map */ - ecs_vec_t nodes; /* vec */ - ecs_vec_t values; /* vec */ -}; - -/** Init new switch. */ -FLECS_DBG_API -void flecs_switch_init( - ecs_switch_t* sw, - ecs_allocator_t *allocator, - int32_t elements); - -/** Fini switch. */ -FLECS_DBG_API -void flecs_switch_fini( - ecs_switch_t *sw); - -/** Remove all values. */ -FLECS_DBG_API -void flecs_switch_clear( - ecs_switch_t *sw); - -/** Add element to switch, initialize value to 0 */ -FLECS_DBG_API -void flecs_switch_add( - ecs_switch_t *sw); - -/** Set number of elements in switch list */ -FLECS_DBG_API -void flecs_switch_set_count( - ecs_switch_t *sw, - int32_t count); - -/** Get number of elements */ -FLECS_DBG_API -int32_t flecs_switch_count( - ecs_switch_t *sw); - -/** Ensure that element exists. */ -FLECS_DBG_API -void flecs_switch_ensure( - ecs_switch_t *sw, - int32_t count); - -/** Add n elements. */ -FLECS_DBG_API -void flecs_switch_addn( - ecs_switch_t *sw, - int32_t count); - -/** Set value of element. */ -FLECS_DBG_API -void flecs_switch_set( - ecs_switch_t *sw, - int32_t element, - uint64_t value); - -/** Remove element. */ -FLECS_DBG_API -void flecs_switch_remove( - ecs_switch_t *sw, - int32_t element); - -/** Get value for element. */ -FLECS_DBG_API -uint64_t flecs_switch_get( - const ecs_switch_t *sw, - int32_t element); - -/** Swap element. */ -FLECS_DBG_API -void flecs_switch_swap( - ecs_switch_t *sw, - int32_t elem_1, - int32_t elem_2); - -/** Get vector with all values. Use together with count(). */ -FLECS_DBG_API -ecs_vec_t* flecs_switch_values( - const ecs_switch_t *sw); - -/** Return number of different values. */ -FLECS_DBG_API -int32_t flecs_switch_case_count( - const ecs_switch_t *sw, - uint64_t value); - -/** Return first element for value. */ -FLECS_DBG_API -int32_t flecs_switch_first( - const ecs_switch_t *sw, - uint64_t value); - -/** Return next element for value. Use with first(). */ -FLECS_DBG_API -int32_t flecs_switch_next( - const ecs_switch_t *sw, - int32_t elem); - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/vendors/flecs/meson.build b/vendors/flecs/meson.build index 990c90a83..de74cfd48 100644 --- a/vendors/flecs/meson.build +++ b/vendors/flecs/meson.build @@ -20,43 +20,57 @@ endif flecs_src = files( 'src/addons/alerts.c', - 'src/addons/coredoc.c', 'src/addons/doc.c', - 'src/addons/expr/deserialize.c', - 'src/addons/expr/serialize.c', - 'src/addons/expr/utils.c', - 'src/addons/expr/vars.c', 'src/addons/flecs_cpp.c', 'src/addons/http.c', 'src/addons/journal.c', + 'src/addons/json/deserialize_value.c', 'src/addons/json/deserialize.c', - 'src/addons/json/serialize.c', - 'src/addons/json/serialize_type_info.c', 'src/addons/json/json.c', + 'src/addons/json/serialize_entity.c', + 'src/addons/json/serialize_field_info.c', + 'src/addons/json/serialize_iter_result_query.c', + 'src/addons/json/serialize_iter_result_table.c', + 'src/addons/json/serialize_iter_result.c', + 'src/addons/json/serialize_iter.c', + 'src/addons/json/serialize_query_info.c', + 'src/addons/json/serialize_type_info.c', + 'src/addons/json/serialize_value.c', + 'src/addons/json/serialize_world.c', + 'src/addons/stats/monitor.c', + 'src/addons/stats/pipeline_monitor.c', + 'src/addons/stats/stats.c', + 'src/addons/stats/system_monitor.c', + 'src/addons/stats/world_monitor.c', + 'src/addons/stats/world_summary.c', 'src/addons/log.c', 'src/addons/meta/api.c', + 'src/addons/meta/definitions.c', 'src/addons/meta/meta.c', 'src/addons/meta/serialized.c', 'src/addons/meta/cursor.c', - 'src/addons/meta_c.c', + 'src/addons/meta/rtt_lifecycle.c', + 'src/addons/meta/c_utils.c', 'src/addons/metrics.c', 'src/addons/module.c', - 'src/addons/monitor.c', 'src/addons/os_api_impl/os_api_impl.c', - 'src/addons/parser.c', 'src/addons/pipeline/pipeline.c', 'src/addons/pipeline/worker.c', - 'src/addons/plecs.c', 'src/addons/rest.c', - 'src/addons/rules/api.c', - 'src/addons/rules/compile.c', - 'src/addons/rules/engine.c', - 'src/addons/rules/trav_cache.c', - 'src/addons/rules/trav_down_cache.c', - 'src/addons/rules/trav_up_cache.c', - 'src/addons/rules/trivial_iter.c', - 'src/addons/snapshot.c', - 'src/addons/stats.c', + 'src/addons/script/template.c', + 'src/addons/script/ast.c', + 'src/addons/script/expr.c', + 'src/addons/script/interpolate.c', + 'src/addons/script/parser.c', + 'src/addons/script/query_parser.c', + 'src/addons/script/script.c', + 'src/addons/script/serialize.c', + 'src/addons/script/tokenizer.c', + 'src/addons/script/vars.c', + 'src/addons/script/visit_eval.c', + 'src/addons/script/visit_free.c', + 'src/addons/script/visit_to_str.c', + 'src/addons/script/visit.c', 'src/addons/system/system.c', 'src/addons/timer.c', 'src/addons/units.c', @@ -77,18 +91,39 @@ flecs_src = files( 'src/storage/table.c', 'src/storage/table_cache.c', 'src/storage/table_graph.c', + 'src/query/compiler/compiler_term.c', + 'src/query/compiler/compiler.c', + 'src/query/engine/cache_iter.c', + 'src/query/engine/cache_order_by.c', + 'src/query/engine/cache.c', + 'src/query/engine/change_detection.c', + 'src/query/engine/eval_iter.c', + 'src/query/engine/eval_member.c', + 'src/query/engine/eval_pred.c', + 'src/query/engine/eval_toggle.c', + 'src/query/engine/eval_trav.c', + 'src/query/engine/eval_union.c', + 'src/query/engine/eval_up.c', + 'src/query/engine/eval_utils.c', + 'src/query/engine/eval.c', + 'src/query/engine/trav_cache.c', + 'src/query/engine/trav_down_cache.c', + 'src/query/engine/trav_up_cache.c', + 'src/query/engine/trivial_iter.c', + 'src/query/api.c', + 'src/query/util.c', + 'src/query/validator.c', 'src/bootstrap.c', - 'src/entity.c', - 'src/entity_filter.c', + 'src/each.c', 'src/entity_name.c', - 'src/filter.c', + 'src/entity.c', + 'src/id.c', 'src/iter.c', 'src/misc.c', 'src/observable.c', 'src/observer.c', 'src/os_api.c', 'src/poly.c', - 'src/query.c', 'src/stage.c', 'src/search.c', 'src/value.c', diff --git a/vendors/flecs/project.json b/vendors/flecs/project.json index a0553fa17..badfe5196 100644 --- a/vendors/flecs/project.json +++ b/vendors/flecs/project.json @@ -4,13 +4,19 @@ "value": { "author": "Sander Mertens", "description": "Entity Component System written in C99/C++11", - "amalgamate": true + "amalgamate": true, + "amalgamate-path": "distr" }, "dependee": { "lang.c": { "${cfg sanitize}": { "defines": ["FLECS_SANITIZE"] } + }, + "lang.cpp": { + "${cfg sanitize}": { + "defines": ["FLECS_SANITIZE"] + } } }, "lang.c": { diff --git a/vendors/flecs/src/addons/alerts.c b/vendors/flecs/src/addons/alerts.c index ae2a5a387..45bf8b0b5 100644 --- a/vendors/flecs/src/addons/alerts.c +++ b/vendors/flecs/src/addons/alerts.c @@ -118,7 +118,7 @@ void flecs_alerts_add_alert_to_src( ecs_entity_t alert, ecs_entity_t alert_instance) { - EcsAlertsActive *active = ecs_get_mut( + EcsAlertsActive *active = ecs_ensure( world, source, EcsAlertsActive); ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); @@ -143,7 +143,7 @@ void flecs_alerts_remove_alert_from_src( ecs_entity_t source, ecs_entity_t alert) { - EcsAlertsActive *active = ecs_get_mut( + EcsAlertsActive *active = ecs_ensure( world, source, EcsAlertsActive); ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); ecs_map_remove(&active->alerts, alert); @@ -239,16 +239,20 @@ ecs_entity_t flecs_alert_out_of_range_kind( static void MonitorAlerts(ecs_iter_t *it) { ecs_world_t *world = it->real_world; - EcsAlert *alert = ecs_field(it, EcsAlert, 1); - EcsPoly *poly = ecs_field(it, EcsPoly, 2); + EcsAlert *alert = ecs_field(it, EcsAlert, 0); + EcsPoly *poly = ecs_field(it, EcsPoly, 1); int32_t i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t a = it->entities[i]; /* Alert entity */ ecs_entity_t default_severity = ecs_get_target( world, a, ecs_id(EcsAlert), 0); - ecs_rule_t *rule = poly[i].poly; - ecs_poly_assert(rule, ecs_rule_t); + ecs_query_t *q = poly[i].poly; + if (!q) { + continue; + } + + flecs_poly_assert(q, ecs_query_t); ecs_id_t member_id = alert[i].id; const EcsMemberRanges *ranges = NULL; @@ -256,11 +260,10 @@ void MonitorAlerts(ecs_iter_t *it) { ranges = ecs_ref_get(world, &alert[i].ranges, EcsMemberRanges); } - ecs_iter_t rit = ecs_rule_iter(world, rule); + ecs_iter_t rit = ecs_query_iter(world, q); rit.flags |= EcsIterNoData; - rit.flags |= EcsIterIsInstanced; - while (ecs_rule_next(&rit)) { + while (ecs_query_next(&rit)) { ecs_entity_t severity = flecs_alert_get_severity( world, &rit, &alert[i]); if (!severity) { @@ -346,10 +349,10 @@ void MonitorAlerts(ecs_iter_t *it) { static void MonitorAlertInstances(ecs_iter_t *it) { ecs_world_t *world = it->real_world; - EcsAlertInstance *alert_instance = ecs_field(it, EcsAlertInstance, 1); - EcsMetricSource *source = ecs_field(it, EcsMetricSource, 2); - EcsMetricValue *value = ecs_field(it, EcsMetricValue, 3); - EcsAlertTimeout *timeout = ecs_field(it, EcsAlertTimeout, 4); + EcsAlertInstance *alert_instance = ecs_field(it, EcsAlertInstance, 0); + EcsMetricSource *source = ecs_field(it, EcsMetricSource, 1); + EcsMetricValue *value = ecs_field(it, EcsMetricValue, 2); + EcsAlertTimeout *timeout = ecs_field(it, EcsAlertTimeout, 3); /* Get alert component from alert instance parent (the alert) */ ecs_id_t childof_pair; @@ -357,16 +360,23 @@ void MonitorAlertInstances(ecs_iter_t *it) { ecs_err("alert instances must be a child of an alert"); return; } + ecs_entity_t parent = ecs_pair_second(world, childof_pair); ecs_assert(parent != 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(ecs_has(world, parent, EcsAlert), ECS_INVALID_OPERATION, "alert entity does not have Alert component"); - EcsAlert *alert = ecs_get_mut(world, parent, EcsAlert); + + EcsAlert *alert = ecs_ensure(world, parent, EcsAlert); const EcsPoly *poly = ecs_get_pair(world, parent, EcsPoly, EcsQuery); ecs_assert(poly != NULL, ECS_INVALID_OPERATION, "alert entity does not have (Poly, Query) component"); - ecs_rule_t *rule = poly->poly; - ecs_poly_assert(rule, ecs_rule_t); + + ecs_query_t *query = poly->poly; + if (!query) { + return; + } + + flecs_poly_assert(query, ecs_query_t); ecs_id_t member_id = alert->id; const EcsMemberRanges *ranges = NULL; @@ -374,9 +384,7 @@ void MonitorAlertInstances(ecs_iter_t *it) { ranges = ecs_ref_get(world, &alert->ranges, EcsMemberRanges); } - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - + ecs_script_vars_t *vars = ecs_script_vars_init(it->world); int32_t i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t ai = it->entities[i]; @@ -389,13 +397,12 @@ void MonitorAlertInstances(ecs_iter_t *it) { continue; } - /* Check if alert instance still matches rule */ - ecs_iter_t rit = ecs_rule_iter(world, rule); + /* Check if alert instance still matches query */ + ecs_iter_t rit = ecs_query_iter(world, query); rit.flags |= EcsIterNoData; - rit.flags |= EcsIterIsInstanced; ecs_iter_set_var(&rit, 0, e); - if (ecs_rule_next(&rit)) { + if (ecs_query_next(&rit)) { bool match = true; /* If alert is monitoring member range, test value against range */ @@ -427,7 +434,7 @@ void MonitorAlertInstances(ecs_iter_t *it) { if (generate_message) { if (alert_instance[i].message) { /* If a message was already generated, only regenerate if - * rule has multiple variables. Variable values could have + * query has multiple variables. Variable values could have * changed, this ensures the message remains up to date. */ generate_message = rit.variable_count > 1; } @@ -438,9 +445,9 @@ void MonitorAlertInstances(ecs_iter_t *it) { ecs_os_free(alert_instance[i].message); } - ecs_iter_to_vars(&rit, &vars, 0); - alert_instance[i].message = ecs_interpolate_string( - world, alert->message, &vars); + ecs_script_vars_from_iter(&rit, vars, 0); + alert_instance[i].message = ecs_script_string_interpolate( + world, alert->message, vars); } if (timeout) { @@ -452,7 +459,7 @@ void MonitorAlertInstances(ecs_iter_t *it) { timeout[i].inactive_time = 0; } - /* Alert instance still matches rule, keep it alive */ + /* Alert instance still matches query, keep it alive */ ecs_iter_fini(&rit); continue; } @@ -461,7 +468,6 @@ void MonitorAlertInstances(ecs_iter_t *it) { } /* Alert instance is no longer active */ - if (timeout) { if (ECS_EQZERO(timeout[i].inactive_time)) { /* The alert just became inactive. Add Disabled tag */ @@ -471,54 +477,54 @@ void MonitorAlertInstances(ecs_iter_t *it) { ecs_ftime_t t = timeout[i].inactive_time; timeout[i].inactive_time += it->delta_system_time; if (t < timeout[i].expire_time) { - /* Alert instance no longer matches rule, but is still + /* Alert instance no longer matches query, but is still * within the timeout period. Keep it alive. */ continue; } } - /* Alert instance no longer matches rule, remove it */ + /* Alert instance no longer matches query, remove it */ flecs_alerts_remove_alert_from_src(world, e, parent); ecs_map_remove(&alert->instances, e); ecs_delete(world, ai); } - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); } ecs_entity_t ecs_alert_init( ecs_world_t *world, const ecs_alert_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(!desc->filter.entity || desc->entity == desc->filter.entity, + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_alert_desc_t was not initialized to zero"); + ecs_check(!desc->query.entity || desc->entity == desc->query.entity, ECS_INVALID_PARAMETER, NULL); ecs_entity_t result = desc->entity; if (!result) { - result = ecs_new(world, 0); + result = ecs_new(world); } - ecs_filter_desc_t private_desc = desc->filter; + ecs_query_desc_t private_desc = desc->query; private_desc.entity = result; - ecs_rule_t *rule = ecs_rule_init(world, &private_desc); - if (!rule) { + ecs_query_t *q = ecs_query_init(world, &private_desc); + if (!q) { ecs_err("failed to create alert filter"); return 0; } - const ecs_filter_t *filter = ecs_rule_get_filter(rule); - if (!(filter->flags & EcsFilterMatchThis)) { + if (!(q->flags & EcsQueryMatchThis)) { ecs_err("alert filter must have at least one '$this' term"); - ecs_rule_fini(rule); + ecs_query_fini(q); return 0; } /* Initialize Alert component which identifiers entity as alert */ - EcsAlert *alert = ecs_get_mut(world, result, EcsAlert); + EcsAlert *alert = ecs_ensure(world, result, EcsAlert); ecs_assert(alert != NULL, ECS_INTERNAL_ERROR, NULL); alert->message = ecs_os_strdup(desc->message); alert->retain_period = desc->retain_period; @@ -535,7 +541,7 @@ ecs_entity_t ecs_alert_init( &alert->severity_filters, ecs_alert_severity_filter_t); *sf = desc->severity_filters[i]; if (sf->var) { - sf->_var_index = ecs_rule_find_var(rule, sf->var); + sf->_var_index = ecs_query_find_var(q, sf->var); if (sf->_var_index == -1) { ecs_err("unresolved variable '%s' in alert severity filter", sf->var); @@ -568,7 +574,7 @@ ecs_entity_t ecs_alert_init( ecs_entity_t type = idr->type_info->component; if (type != ecs_get_parent(world, desc->member)) { - char *type_name = ecs_get_fullpath(world, type); + char *type_name = ecs_get_path(world, type); ecs_err("member '%s' is not a member of '%s'", ecs_get_name(world, desc->member), type_name); ecs_os_free(type_name); @@ -598,7 +604,7 @@ ecs_entity_t ecs_alert_init( int32_t var_id = 0; if (desc->var) { - var_id = ecs_rule_find_var(rule, desc->var); + var_id = ecs_query_find_var(q, desc->var); if (var_id == -1) { ecs_err("unresolved variable '%s' in alert member", desc->var); goto error; @@ -657,7 +663,7 @@ int32_t ecs_get_alert_count( ecs_entity_t entity, ecs_entity_t alert) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); ecs_check(!alert || ecs_has(world, alert, EcsAlert), ECS_INVALID_PARAMETER, NULL); @@ -681,7 +687,7 @@ ecs_entity_t ecs_get_alert( ecs_entity_t entity, ecs_entity_t alert) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); ecs_check(alert != 0, ECS_INVALID_PARAMETER, NULL); @@ -723,7 +729,7 @@ void FlecsAlertsImport(ecs_world_t *world) { ECS_TAG_DEFINE(world, EcsAlertError); ECS_TAG_DEFINE(world, EcsAlertCritical); - ecs_add_id(world, ecs_id(EcsAlert), EcsTag); + ecs_add_id(world, ecs_id(EcsAlert), EcsPairIsTag); ecs_add_id(world, ecs_id(EcsAlert), EcsExclusive); ecs_add_id(world, ecs_id(EcsAlertsActive), EcsPrivate); @@ -747,7 +753,7 @@ void FlecsAlertsImport(ecs_world_t *world) { }); ecs_set_hooks(world, EcsAlertInstance, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(EcsAlertInstance), .move = ecs_move(EcsAlertInstance), .copy = ecs_copy(EcsAlertInstance) @@ -762,19 +768,25 @@ void FlecsAlertsImport(ecs_world_t *world) { } }); - ECS_SYSTEM(world, MonitorAlerts, EcsPreStore, Alert, (Poly, Query)); + ECS_SYSTEM(world, MonitorAlerts, EcsPreStore, + Alert, + (Poly, Query)); + ECS_SYSTEM(world, MonitorAlertInstances, EcsOnStore, Instance, - flecs.metrics.Source, flecs.metrics.Value, ?EcsAlertTimeout, ?Disabled); + flecs.metrics.Source, + flecs.metrics.Value, + ?Timeout, + ?Disabled); ecs_system(world, { .entity = ecs_id(MonitorAlerts), - .no_readonly = true, - .interval = 0.5 + .immediate = true, + .interval = (ecs_ftime_t)0.5 }); ecs_system(world, { .entity = ecs_id(MonitorAlertInstances), - .interval = 0.5 + .interval = (ecs_ftime_t)0.5 }); } diff --git a/vendors/flecs/src/addons/app.c b/vendors/flecs/src/addons/app.c index c038649d0..8cd0d6f17 100644 --- a/vendors/flecs/src/addons/app.c +++ b/vendors/flecs/src/addons/app.c @@ -70,8 +70,8 @@ char* flecs_explorer_request(const char *method, char *request) { if (body) { return body; } else { - return ecs_asprintf( - "{\"error\": \"bad request (code %d)\"}", reply.code); + return flecs_asprintf( + "{\"error\": \"bad request\", \"status\": %d}", reply.code); } } } @@ -83,16 +83,15 @@ int ecs_app_run( { ecs_app_desc = *desc; - /* Don't set FPS & threads if custom run action is set, as the platform on - * which the app is running may not support it. */ - if (run_action == flecs_default_run_action) { - if (ECS_NEQZERO(ecs_app_desc.target_fps)) { - ecs_set_target_fps(world, ecs_app_desc.target_fps); - } - if (ecs_app_desc.threads) { - ecs_set_threads(world, ecs_app_desc.threads); - } + /* Don't set FPS & threads if using emscripten */ +#ifndef ECS_TARGET_EM + if (ECS_NEQZERO(ecs_app_desc.target_fps)) { + ecs_set_target_fps(world, ecs_app_desc.target_fps); } + if (ecs_app_desc.threads) { + ecs_set_threads(world, ecs_app_desc.threads); + } +#endif /* REST server enables connecting to app with explorer */ if (desc->enable_rest) { @@ -100,6 +99,7 @@ int ecs_app_run( #ifdef ECS_TARGET_EM flecs_wasm_rest_server = ecs_rest_server_init(world, NULL); #else + ECS_IMPORT(world, FlecsRest); ecs_set(world, EcsWorld, EcsRest, {.port = desc->port }); #endif #else @@ -108,9 +108,9 @@ int ecs_app_run( } /* Monitoring periodically collects statistics */ - if (desc->enable_monitor) { -#ifdef FLECS_MONITOR - ECS_IMPORT(world, FlecsMonitor); + if (desc->enable_stats) { +#ifdef FLECS_STATS + ECS_IMPORT(world, FlecsStats); #else ecs_warn("cannot enable monitoring, MONITOR addon not available"); #endif diff --git a/vendors/flecs/src/addons/coredoc.c b/vendors/flecs/src/addons/coredoc.c deleted file mode 100644 index 07d6b48a1..000000000 --- a/vendors/flecs/src/addons/coredoc.c +++ /dev/null @@ -1,140 +0,0 @@ -/** - * @file addons/coredoc.c - * @brief Core doc addon. - */ - -#include "../private_api.h" - -#ifdef FLECS_COREDOC - -#define URL_ROOT "https://www.flecs.dev/flecs/md_docs_Relationships.html/" - -void FlecsCoreDocImport( - ecs_world_t *world) -{ - ECS_MODULE(world, FlecsCoreDoc); - - ECS_IMPORT(world, FlecsMeta); - ECS_IMPORT(world, FlecsDoc); - - ecs_set_name_prefix(world, "Ecs"); - - /* Initialize reflection data for core components */ - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsComponent), - .members = { - {.name = "size", .type = ecs_id(ecs_i32_t)}, - {.name = "alignment", .type = ecs_id(ecs_i32_t)} - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsDocDescription), - .members = { - {.name = "value", .type = ecs_id(ecs_string_t)} - } - }); - - /* Initialize documentation data for core components */ - ecs_doc_set_brief(world, EcsFlecs, "Flecs root module"); - ecs_doc_set_link(world, EcsFlecs, "https://github.com/SanderMertens/flecs"); - - ecs_doc_set_brief(world, EcsFlecsCore, "Flecs module with builtin components"); - - ecs_doc_set_brief(world, EcsWorld, "Entity associated with world"); - - ecs_doc_set_brief(world, ecs_id(EcsComponent), "Component that is added to all components"); - ecs_doc_set_brief(world, EcsModule, "Tag that is added to modules"); - ecs_doc_set_brief(world, EcsPrefab, "Tag that is added to prefabs"); - ecs_doc_set_brief(world, EcsDisabled, "Tag that is added to disabled entities"); - - ecs_doc_set_brief(world, ecs_id(EcsIdentifier), "Component used for entity names"); - ecs_doc_set_brief(world, EcsName, "Tag used with EcsIdentifier to signal entity name"); - ecs_doc_set_brief(world, EcsSymbol, "Tag used with EcsIdentifier to signal entity symbol"); - - ecs_doc_set_brief(world, EcsTransitive, "Transitive relationship property"); - ecs_doc_set_brief(world, EcsReflexive, "Reflexive relationship property"); - ecs_doc_set_brief(world, EcsFinal, "Final relationship property"); - ecs_doc_set_brief(world, EcsDontInherit, "DontInherit relationship property"); - ecs_doc_set_brief(world, EcsTag, "Tag relationship property"); - ecs_doc_set_brief(world, EcsAcyclic, "Acyclic relationship property"); - ecs_doc_set_brief(world, EcsTraversable, "Traversable relationship property"); - ecs_doc_set_brief(world, EcsExclusive, "Exclusive relationship property"); - ecs_doc_set_brief(world, EcsSymmetric, "Symmetric relationship property"); - ecs_doc_set_brief(world, EcsWith, "With relationship property"); - ecs_doc_set_brief(world, EcsOnDelete, "OnDelete relationship cleanup property"); - ecs_doc_set_brief(world, EcsOnDeleteTarget, "OnDeleteTarget relationship cleanup property"); - ecs_doc_set_brief(world, EcsDefaultChildComponent, "Sets default component hint for children of entity"); - ecs_doc_set_brief(world, EcsRemove, "Remove relationship cleanup property"); - ecs_doc_set_brief(world, EcsDelete, "Delete relationship cleanup property"); - ecs_doc_set_brief(world, EcsPanic, "Panic relationship cleanup property"); - ecs_doc_set_brief(world, EcsIsA, "Builtin IsA relationship"); - ecs_doc_set_brief(world, EcsChildOf, "Builtin ChildOf relationship"); - ecs_doc_set_brief(world, EcsDependsOn, "Builtin DependsOn relationship"); - ecs_doc_set_brief(world, EcsOnAdd, "Builtin OnAdd event"); - ecs_doc_set_brief(world, EcsOnRemove, "Builtin OnRemove event"); - ecs_doc_set_brief(world, EcsOnSet, "Builtin OnSet event"); - ecs_doc_set_brief(world, EcsUnSet, "Builtin UnSet event"); - - ecs_doc_set_link(world, EcsTransitive, URL_ROOT "#transitive-property"); - ecs_doc_set_link(world, EcsReflexive, URL_ROOT "#reflexive-property"); - ecs_doc_set_link(world, EcsFinal, URL_ROOT "#final-property"); - ecs_doc_set_link(world, EcsDontInherit, URL_ROOT "#dontinherit-property"); - ecs_doc_set_link(world, EcsTag, URL_ROOT "#tag-property"); - ecs_doc_set_link(world, EcsAcyclic, URL_ROOT "#acyclic-property"); - ecs_doc_set_link(world, EcsTraversable, URL_ROOT "#traversable-property"); - ecs_doc_set_link(world, EcsExclusive, URL_ROOT "#exclusive-property"); - ecs_doc_set_link(world, EcsSymmetric, URL_ROOT "#symmetric-property"); - ecs_doc_set_link(world, EcsWith, URL_ROOT "#with-property"); - ecs_doc_set_link(world, EcsOnDelete, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsOnDeleteTarget, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsRemove, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsDelete, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsPanic, URL_ROOT "#cleanup-properties"); - ecs_doc_set_link(world, EcsIsA, URL_ROOT "#the-isa-relationship"); - ecs_doc_set_link(world, EcsChildOf, URL_ROOT "#the-childof-relationship"); - - /* Initialize documentation for meta components */ - ecs_entity_t meta = ecs_lookup_fullpath(world, "flecs.meta"); - ecs_doc_set_brief(world, meta, "Flecs module with reflection components"); - - ecs_doc_set_brief(world, ecs_id(EcsMetaType), "Component added to types"); - ecs_doc_set_brief(world, ecs_id(EcsMetaTypeSerialized), "Component that stores reflection data in an optimized format"); - ecs_doc_set_brief(world, ecs_id(EcsPrimitive), "Component added to primitive types"); - ecs_doc_set_brief(world, ecs_id(EcsEnum), "Component added to enumeration types"); - ecs_doc_set_brief(world, ecs_id(EcsBitmask), "Component added to bitmask types"); - ecs_doc_set_brief(world, ecs_id(EcsMember), "Component added to struct members"); - ecs_doc_set_brief(world, ecs_id(EcsStruct), "Component added to struct types"); - ecs_doc_set_brief(world, ecs_id(EcsArray), "Component added to array types"); - ecs_doc_set_brief(world, ecs_id(EcsVector), "Component added to vector types"); - - ecs_doc_set_brief(world, ecs_id(ecs_bool_t), "bool component"); - ecs_doc_set_brief(world, ecs_id(ecs_char_t), "char component"); - ecs_doc_set_brief(world, ecs_id(ecs_byte_t), "byte component"); - ecs_doc_set_brief(world, ecs_id(ecs_u8_t), "8 bit unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_u16_t), "16 bit unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_u32_t), "32 bit unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_u64_t), "64 bit unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_uptr_t), "word sized unsigned int component"); - ecs_doc_set_brief(world, ecs_id(ecs_i8_t), "8 bit signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_i16_t), "16 bit signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_i32_t), "32 bit signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_i64_t), "64 bit signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_iptr_t), "word sized signed int component"); - ecs_doc_set_brief(world, ecs_id(ecs_f32_t), "32 bit floating point component"); - ecs_doc_set_brief(world, ecs_id(ecs_f64_t), "64 bit floating point component"); - ecs_doc_set_brief(world, ecs_id(ecs_string_t), "string component"); - ecs_doc_set_brief(world, ecs_id(ecs_entity_t), "entity component"); - - /* Initialize documentation for doc components */ - ecs_entity_t doc = ecs_lookup_fullpath(world, "flecs.doc"); - ecs_doc_set_brief(world, doc, "Flecs module with documentation components"); - - ecs_doc_set_brief(world, ecs_id(EcsDocDescription), "Component used to add documentation"); - ecs_doc_set_brief(world, EcsDocBrief, "Used as (Description, Brief) to add a brief description"); - ecs_doc_set_brief(world, EcsDocDetail, "Used as (Description, Detail) to add a detailed description"); - ecs_doc_set_brief(world, EcsDocLink, "Used as (Description, Link) to add a link"); -} - -#endif diff --git a/vendors/flecs/src/addons/doc.c b/vendors/flecs/src/addons/doc.c index 3a848143f..2f7bff277 100644 --- a/vendors/flecs/src/addons/doc.c +++ b/vendors/flecs/src/addons/doc.c @@ -39,6 +39,14 @@ void flecs_doc_set( } } +void ecs_doc_set_uuid( + ecs_world_t *world, + ecs_entity_t entity, + const char *name) +{ + flecs_doc_set(world, entity, EcsDocUuid, name); +} + void ecs_doc_set_name( ecs_world_t *world, ecs_entity_t entity, @@ -79,6 +87,19 @@ void ecs_doc_set_color( flecs_doc_set(world, entity, EcsDocColor, color); } +const char* ecs_doc_get_uuid( + const ecs_world_t *world, + ecs_entity_t entity) +{ + const EcsDocDescription *ptr = ecs_get_pair( + world, entity, EcsDocDescription, EcsDocUuid); + if (ptr) { + return ptr->value; + } else { + return NULL; + } +} + const char* ecs_doc_get_name( const ecs_world_t *world, ecs_entity_t entity) @@ -144,6 +165,95 @@ const char* ecs_doc_get_color( } } +/* Doc definitions for core components */ +static +void flecs_doc_import_core_definitions( + ecs_world_t *world) +{ + ecs_doc_set_brief(world, EcsFlecs, "Flecs root module"); + ecs_doc_set_link(world, EcsFlecs, "https://github.com/SanderMertens/flecs"); + ecs_doc_set_brief(world, EcsFlecsCore, "Module with builtin components"); + ecs_doc_set_brief(world, EcsFlecsInternals, "Module with internal entities"); + + ecs_doc_set_brief(world, EcsWorld, "Entity associated with world"); + + ecs_doc_set_brief(world, ecs_id(EcsComponent), "Component that is added to components"); + ecs_doc_set_brief(world, EcsModule, "Tag that is added to modules"); + ecs_doc_set_brief(world, EcsPrefab, "Tag that is added to prefabs"); + ecs_doc_set_brief(world, EcsDisabled, "Tag that is added to disabled entities"); + ecs_doc_set_brief(world, EcsPrivate, "Tag that is added to private components"); + ecs_doc_set_brief(world, EcsFlag, "Internal tag for tracking ids with special id flags"); + ecs_doc_set_brief(world, ecs_id(EcsPoly), "Internal component that stores pointer to poly objects"); + + ecs_doc_set_brief(world, ecs_id(EcsIdentifier), "Component used for entity names"); + ecs_doc_set_brief(world, EcsName, "Tag used with EcsIdentifier to store entity name"); + ecs_doc_set_brief(world, EcsSymbol, "Tag used with EcsIdentifier to store entity symbol"); + ecs_doc_set_brief(world, EcsAlias, "Tag used with EcsIdentifier to store entity alias"); + + ecs_doc_set_brief(world, EcsQuery, "Tag added to query entities"); + ecs_doc_set_brief(world, EcsObserver, "Tag added to observer entities"); + + ecs_doc_set_brief(world, EcsTransitive, "Trait that enables transitive evaluation of relationships"); + ecs_doc_set_brief(world, EcsReflexive, "Trait that enables reflexive evaluation of relationships"); + ecs_doc_set_brief(world, EcsFinal, "Trait that indicates an entity cannot be inherited from"); + ecs_doc_set_brief(world, EcsDontInherit, "Trait that indicates it should not be inherited"); + ecs_doc_set_brief(world, EcsPairIsTag, "Trait that ensures a pair cannot contain a value"); + ecs_doc_set_brief(world, EcsAcyclic, "Trait that indicates a relationship is acyclic"); + ecs_doc_set_brief(world, EcsTraversable, "Trait that indicates a relationship is traversable"); + ecs_doc_set_brief(world, EcsExclusive, "Trait that ensures a relationship can only have one target"); + ecs_doc_set_brief(world, EcsSymmetric, "Trait that causes a relationship to be two-way"); + ecs_doc_set_brief(world, EcsWith, "Trait for adding additional components when a component is added"); + ecs_doc_set_brief(world, EcsOneOf, "Trait that enforces target of relationship is a child of "); + ecs_doc_set_brief(world, EcsOnDelete, "Cleanup trait for specifying what happens when component is deleted"); + ecs_doc_set_brief(world, EcsOnDeleteTarget, "Cleanup trait for specifying what happens when pair target is deleted"); + ecs_doc_set_brief(world, EcsRemove, "Cleanup action used with OnDelete/OnDeleteTarget"); + ecs_doc_set_brief(world, EcsDelete, "Cleanup action used with OnDelete/OnDeleteTarget"); + ecs_doc_set_brief(world, EcsPanic, "Cleanup action used with OnDelete/OnDeleteTarget"); + ecs_doc_set_brief(world, ecs_id(EcsDefaultChildComponent), "Sets default component hint for children of entity"); + ecs_doc_set_brief(world, EcsIsA, "Relationship used for expressing inheritance"); + ecs_doc_set_brief(world, EcsChildOf, "Relationship used for expressing hierarchies"); + ecs_doc_set_brief(world, EcsDependsOn, "Relationship used for expressing dependencies"); + ecs_doc_set_brief(world, EcsSlotOf, "Relationship used for expressing prefab slots"); + ecs_doc_set_brief(world, EcsOnAdd, "Event emitted when component is added"); + ecs_doc_set_brief(world, EcsOnRemove, "Event emitted when component is removed"); + ecs_doc_set_brief(world, EcsOnSet, "Event emitted when component is set"); + ecs_doc_set_brief(world, EcsMonitor, "Marker used to create monitor observers"); + ecs_doc_set_brief(world, EcsOnTableFill, "Event emitted when table becomes non-empty"); + ecs_doc_set_brief(world, EcsOnTableEmpty, "Event emitted when table becomes empty"); + ecs_doc_set_brief(world, EcsOnTableCreate, "Event emitted when table is created"); + ecs_doc_set_brief(world, EcsOnTableDelete, "Event emitted when table is deleted"); + + ecs_doc_set_brief(world, EcsThis, "Query marker to express $this variable"); + ecs_doc_set_brief(world, EcsWildcard, "Query marker to express match all wildcard"); + ecs_doc_set_brief(world, EcsAny, "Query marker to express match at least one wildcard"); + + ecs_doc_set_brief(world, EcsPredEq, "Query marker to express == operator"); + ecs_doc_set_brief(world, EcsPredMatch, "Query marker to express ~= operator"); + ecs_doc_set_brief(world, EcsPredLookup, "Query marker to express by-name lookup"); + ecs_doc_set_brief(world, EcsScopeOpen, "Query marker to express scope open"); + ecs_doc_set_brief(world, EcsScopeClose, "Query marker to express scope close"); + ecs_doc_set_brief(world, EcsEmpty, "Tag used to indicate a query has no results"); +} + +/* Doc definitions for doc components */ +static +void flecs_doc_import_doc_definitions( + ecs_world_t *world) +{ + ecs_entity_t doc = ecs_lookup(world, "flecs.doc"); + ecs_doc_set_brief(world, doc, "Flecs module with documentation components"); + + ecs_doc_set_brief(world, EcsDocBrief, "Brief description"); + ecs_doc_set_brief(world, EcsDocDetail, "Detailed description"); + ecs_doc_set_brief(world, EcsDocLink, "Link to additional documentation"); + ecs_doc_set_brief(world, EcsDocColor, "Color hint for entity"); + + ecs_doc_set_brief(world, ecs_id(EcsDocDescription), "Component used to add documentation"); + ecs_doc_set_brief(world, EcsDocBrief, "Used as (Description, Brief) to add a brief description"); + ecs_doc_set_brief(world, EcsDocDetail, "Used as (Description, Detail) to add a detailed description"); + ecs_doc_set_brief(world, EcsDocLink, "Used as (Description, Link) to add a link"); +} + void FlecsDocImport( ecs_world_t *world) { @@ -152,21 +262,24 @@ void FlecsDocImport( ecs_set_name_prefix(world, "EcsDoc"); flecs_bootstrap_component(world, EcsDocDescription); + flecs_bootstrap_tag(world, EcsDocUuid); flecs_bootstrap_tag(world, EcsDocBrief); flecs_bootstrap_tag(world, EcsDocDetail); flecs_bootstrap_tag(world, EcsDocLink); flecs_bootstrap_tag(world, EcsDocColor); ecs_set_hooks(world, EcsDocDescription, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .move = ecs_move(EcsDocDescription), .copy = ecs_copy(EcsDocDescription), .dtor = ecs_dtor(EcsDocDescription) }); - ecs_add_id(world, ecs_id(EcsDocDescription), EcsDontInherit); + ecs_add_pair(world, ecs_id(EcsDocDescription), EcsOnInstantiate, EcsDontInherit); ecs_add_id(world, ecs_id(EcsDocDescription), EcsPrivate); - + + flecs_doc_import_core_definitions(world); + flecs_doc_import_doc_definitions(world); } #endif diff --git a/vendors/flecs/src/addons/expr/utils.c b/vendors/flecs/src/addons/expr/utils.c deleted file mode 100644 index bad2f321e..000000000 --- a/vendors/flecs/src/addons/expr/utils.c +++ /dev/null @@ -1,433 +0,0 @@ -/** - * @file expr/utils.c - * @brief String parsing utilities. - */ - -#include "../../private_api.h" - -#ifdef FLECS_EXPR - -#include - -char* ecs_chresc( - char *out, - char in, - char delimiter) -{ - char *bptr = out; - switch(in) { - case '\a': - *bptr++ = '\\'; - *bptr = 'a'; - break; - case '\b': - *bptr++ = '\\'; - *bptr = 'b'; - break; - case '\f': - *bptr++ = '\\'; - *bptr = 'f'; - break; - case '\n': - *bptr++ = '\\'; - *bptr = 'n'; - break; - case '\r': - *bptr++ = '\\'; - *bptr = 'r'; - break; - case '\t': - *bptr++ = '\\'; - *bptr = 't'; - break; - case '\v': - *bptr++ = '\\'; - *bptr = 'v'; - break; - case '\\': - *bptr++ = '\\'; - *bptr = '\\'; - break; - default: - if (in == delimiter) { - *bptr++ = '\\'; - *bptr = delimiter; - } else { - *bptr = in; - } - break; - } - - *(++bptr) = '\0'; - - return bptr; -} - -const char* ecs_chrparse( - const char *in, - char *out) -{ - const char *result = in + 1; - char ch; - - if (in[0] == '\\') { - result ++; - - switch(in[1]) { - case 'a': - ch = '\a'; - break; - case 'b': - ch = '\b'; - break; - case 'f': - ch = '\f'; - break; - case 'n': - ch = '\n'; - break; - case 'r': - ch = '\r'; - break; - case 't': - ch = '\t'; - break; - case 'v': - ch = '\v'; - break; - case '\\': - ch = '\\'; - break; - case '"': - ch = '"'; - break; - case '0': - ch = '\0'; - break; - case ' ': - ch = ' '; - break; - case '$': - ch = '$'; - break; - default: - goto error; - } - } else { - ch = in[0]; - } - - if (out) { - *out = ch; - } - - return result; -error: - return NULL; -} - -ecs_size_t ecs_stresc( - char *out, - ecs_size_t n, - char delimiter, - const char *in) -{ - const char *ptr = in; - char ch, *bptr = out, buff[3]; - ecs_size_t written = 0; - while ((ch = *ptr++)) { - if ((written += (ecs_size_t)(ecs_chresc( - buff, ch, delimiter) - buff)) <= n) - { - /* If size != 0, an out buffer must be provided. */ - ecs_check(out != NULL, ECS_INVALID_PARAMETER, NULL); - *bptr++ = buff[0]; - if ((ch = buff[1])) { - *bptr = ch; - bptr++; - } - } - } - - if (bptr) { - while (written < n) { - *bptr = '\0'; - bptr++; - written++; - } - } - return written; -error: - return 0; -} - -char* ecs_astresc( - char delimiter, - const char *in) -{ - if (!in) { - return NULL; - } - - ecs_size_t len = ecs_stresc(NULL, 0, delimiter, in); - char *out = ecs_os_malloc_n(char, len + 1); - ecs_stresc(out, len, delimiter, in); - out[len] = '\0'; - return out; -} - -static -const char* flecs_parse_var_name( - const char *ptr, - char *token_out) -{ - char ch, *bptr = token_out; - - while ((ch = *ptr)) { - if (bptr - token_out > ECS_MAX_TOKEN_SIZE) { - goto error; - } - - if (isalpha(ch) || isdigit(ch) || ch == '_') { - *bptr = ch; - bptr ++; - ptr ++; - } else { - break; - } - } - - if (bptr == token_out) { - goto error; - } - - *bptr = '\0'; - - return ptr; -error: - return NULL; -} - -static -const char* flecs_parse_interpolated_str( - const char *ptr, - char *token_out) -{ - char ch, *bptr = token_out; - - while ((ch = *ptr)) { - if (bptr - token_out > ECS_MAX_TOKEN_SIZE) { - goto error; - } - - if (ch == '\\') { - if (ptr[1] == '}') { - *bptr = '}'; - bptr ++; - ptr += 2; - continue; - } - } - - if (ch != '}') { - *bptr = ch; - bptr ++; - ptr ++; - } else { - ptr ++; - break; - } - } - - if (bptr == token_out) { - goto error; - } - - *bptr = '\0'; - - return ptr; -error: - return NULL; -} - -char* ecs_interpolate_string( - ecs_world_t *world, - const char *str, - const ecs_vars_t *vars) -{ - char token[ECS_MAX_TOKEN_SIZE]; - ecs_strbuf_t result = ECS_STRBUF_INIT; - const char *ptr; - char ch; - - for(ptr = str; (ch = *ptr); ptr++) { - if (ch == '\\') { - ptr ++; - if (ptr[0] == '$') { - ecs_strbuf_appendch(&result, '$'); - continue; - } - if (ptr[0] == '\\') { - ecs_strbuf_appendch(&result, '\\'); - continue; - } - if (ptr[0] == '{') { - ecs_strbuf_appendch(&result, '{'); - continue; - } - if (ptr[0] == '}') { - ecs_strbuf_appendch(&result, '}'); - continue; - } - ptr --; - } - - if (ch == '$') { - ptr = flecs_parse_var_name(ptr + 1, token); - if (!ptr) { - ecs_parser_error(NULL, str, ptr - str, - "invalid variable name '%s'", ptr); - goto error; - } - - ecs_expr_var_t *var = ecs_vars_lookup(vars, token); - if (!var) { - ecs_parser_error(NULL, str, ptr - str, - "unresolved variable '%s'", token); - goto error; - } - - if (ecs_ptr_to_str_buf( - world, var->value.type, var->value.ptr, &result)) - { - goto error; - } - - ptr --; - } else if (ch == '{') { - ptr = flecs_parse_interpolated_str(ptr + 1, token); - if (!ptr) { - ecs_parser_error(NULL, str, ptr - str, - "invalid interpolated expression"); - goto error; - } - - ecs_parse_expr_desc_t expr_desc = { - .vars = ECS_CONST_CAST(ecs_vars_t*, vars) - }; - ecs_value_t expr_result = {0}; - if (!ecs_parse_expr(world, token, &expr_result, &expr_desc)) { - goto error; - } - - if (ecs_ptr_to_str_buf( - world, expr_result.type, expr_result.ptr, &result)) - { - goto error; - } - - ecs_value_free(world, expr_result.type, expr_result.ptr); - - ptr --; - } else { - ecs_strbuf_appendch(&result, ch); - } - } - - return ecs_strbuf_get(&result); -error: - return NULL; -} - -void ecs_iter_to_vars( - const ecs_iter_t *it, - ecs_vars_t *vars, - int offset) -{ - ecs_check(vars != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!offset || offset < it->count, ECS_INVALID_PARAMETER, NULL); - - /* Set variable for $this */ - if (it->count) { - ecs_expr_var_t *var = ecs_vars_lookup(vars, "this"); - if (!var) { - ecs_value_t v = { - .ptr = &it->entities[offset], - .type = ecs_id(ecs_entity_t) - }; - var = ecs_vars_declare_w_value(vars, "this", &v); - var->owned = false; - } else { - var->value.ptr = &it->entities[offset]; - } - } - - /* Set variables for fields */ - { - int32_t i, field_count = it->field_count; - for (i = 0; i < field_count; i ++) { - ecs_size_t size = it->sizes[i]; - if (!size) { - continue; - } - - void *ptr = it->ptrs[i]; - if (!ptr) { - continue; - } - - ptr = ECS_OFFSET(ptr, offset * size); - - char name[16]; - ecs_os_sprintf(name, "%d", i + 1); - ecs_expr_var_t *var = ecs_vars_lookup(vars, name); - if (!var) { - ecs_value_t v = { .ptr = ptr, .type = it->ids[i] }; - var = ecs_vars_declare_w_value(vars, name, &v); - var->owned = false; - } else { - ecs_check(var->value.type == it->ids[i], - ECS_INVALID_PARAMETER, NULL); - var->value.ptr = ptr; - } - } - } - - /* Set variables for query variables */ - { - int32_t i, var_count = it->variable_count; - for (i = 1 /* skip this variable */ ; i < var_count; i ++) { - ecs_entity_t *e_ptr = NULL; - ecs_var_t *query_var = &it->variables[i]; - if (query_var->entity) { - e_ptr = &query_var->entity; - } else { - ecs_table_range_t *range = &query_var->range; - if (range->count == 1) { - ecs_entity_t *entities = range->table->data.entities.array; - e_ptr = &entities[range->offset]; - } - } - if (!e_ptr) { - continue; - } - - ecs_expr_var_t *var = ecs_vars_lookup(vars, it->variable_names[i]); - if (!var) { - ecs_value_t v = { .ptr = e_ptr, .type = ecs_id(ecs_entity_t) }; - var = ecs_vars_declare_w_value(vars, it->variable_names[i], &v); - var->owned = false; - } else { - ecs_check(var->value.type == ecs_id(ecs_entity_t), - ECS_INVALID_PARAMETER, NULL); - var->value.ptr = e_ptr; - } - } - } - -error: - return; -} - -#endif diff --git a/vendors/flecs/src/addons/expr/vars.c b/vendors/flecs/src/addons/expr/vars.c deleted file mode 100644 index 8e2b51613..000000000 --- a/vendors/flecs/src/addons/expr/vars.c +++ /dev/null @@ -1,175 +0,0 @@ -/** - * @file expr/vars.c - * @brief Utilities for variable substitution in flecs string expressions. - */ - -#include "../../private_api.h" - -#ifdef FLECS_EXPR - -static -void flecs_expr_var_scope_init( - ecs_world_t *world, - ecs_expr_var_scope_t *scope, - ecs_expr_var_scope_t *parent) -{ - flecs_name_index_init(&scope->var_index, &world->allocator); - ecs_vec_init_t(&world->allocator, &scope->vars, ecs_expr_var_t, 0); - scope->parent = parent; -} - -static -void flecs_expr_var_scope_fini( - ecs_world_t *world, - ecs_expr_var_scope_t *scope) -{ - ecs_vec_t *vars = &scope->vars; - int32_t i, count = vars->count; - for (i = 0; i < count; i++) { - ecs_expr_var_t *var = ecs_vec_get_t(vars, ecs_expr_var_t, i); - if (var->owned) { - ecs_value_free(world, var->value.type, var->value.ptr); - } - flecs_strfree(&world->allocator, var->name); - } - - ecs_vec_fini_t(&world->allocator, &scope->vars, ecs_expr_var_t); - flecs_name_index_fini(&scope->var_index); -} - -void ecs_vars_init( - ecs_world_t *world, - ecs_vars_t *vars) -{ - flecs_expr_var_scope_init(world, &vars->root, NULL); - vars->world = world; - vars->cur = &vars->root; -} - -void ecs_vars_fini( - ecs_vars_t *vars) -{ - ecs_expr_var_scope_t *cur = vars->cur, *next; - do { - next = cur->parent; - flecs_expr_var_scope_fini(vars->world, cur); - if (cur != &vars->root) { - flecs_free_t(&vars->world->allocator, ecs_expr_var_scope_t, cur); - } else { - break; - } - } while ((cur = next)); -} - -void ecs_vars_push( - ecs_vars_t *vars) -{ - ecs_expr_var_scope_t *scope = flecs_calloc_t(&vars->world->allocator, - ecs_expr_var_scope_t); - flecs_expr_var_scope_init(vars->world, scope, vars->cur); - vars->cur = scope; -} - -int ecs_vars_pop( - ecs_vars_t *vars) -{ - ecs_expr_var_scope_t *scope = vars->cur; - ecs_check(scope != &vars->root, ECS_INVALID_OPERATION, NULL); - vars->cur = scope->parent; - flecs_expr_var_scope_fini(vars->world, scope); - flecs_free_t(&vars->world->allocator, ecs_expr_var_scope_t, scope); - return 0; -error: - return 1; -} - -ecs_expr_var_t* ecs_vars_declare( - ecs_vars_t *vars, - const char *name, - ecs_entity_t type) -{ - ecs_assert(vars != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(type != 0, ECS_INVALID_PARAMETER, NULL); - ecs_expr_var_scope_t *scope = vars->cur; - ecs_hashmap_t *var_index = &scope->var_index; - - if (flecs_name_index_find(var_index, name, 0, 0) != 0) { - ecs_err("variable %s redeclared", name); - goto error; - } - - ecs_expr_var_t *var = ecs_vec_append_t(&vars->world->allocator, - &scope->vars, ecs_expr_var_t); - - var->value.ptr = ecs_value_new(vars->world, type); - if (!var->value.ptr) { - goto error; - } - var->value.type = type; - var->name = flecs_strdup(&vars->world->allocator, name); - var->owned = true; - - flecs_name_index_ensure(var_index, - flecs_ito(uint64_t, ecs_vec_count(&scope->vars)), var->name, 0, 0); - return var; -error: - return NULL; -} - -ecs_expr_var_t* ecs_vars_declare_w_value( - ecs_vars_t *vars, - const char *name, - ecs_value_t *value) -{ - ecs_assert(vars != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(value != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_expr_var_scope_t *scope = vars->cur; - ecs_hashmap_t *var_index = &scope->var_index; - - if (flecs_name_index_find(var_index, name, 0, 0) != 0) { - ecs_err("variable %s redeclared", name); - ecs_value_free(vars->world, value->type, value->ptr); - goto error; - } - - ecs_expr_var_t *var = ecs_vec_append_t(&vars->world->allocator, - &scope->vars, ecs_expr_var_t); - var->value = *value; - var->name = flecs_strdup(&vars->world->allocator, name); - var->owned = true; - value->ptr = NULL; /* Take ownership, prevent double free */ - - flecs_name_index_ensure(var_index, - flecs_ito(uint64_t, ecs_vec_count(&scope->vars)), var->name, 0, 0); - return var; -error: - return NULL; -} - -static -ecs_expr_var_t* flecs_vars_scope_lookup( - ecs_expr_var_scope_t *scope, - const char *name) -{ - uint64_t var_id = flecs_name_index_find(&scope->var_index, name, 0, 0); - if (var_id == 0) { - if (scope->parent) { - return flecs_vars_scope_lookup(scope->parent, name); - } - return NULL; - } - - return ecs_vec_get_t(&scope->vars, ecs_expr_var_t, - flecs_uto(int32_t, var_id - 1)); -} - -ecs_expr_var_t* ecs_vars_lookup( - const ecs_vars_t *vars, - const char *name) -{ - return flecs_vars_scope_lookup(vars->cur, name); -} - -#endif diff --git a/vendors/flecs/src/addons/flecs_cpp.c b/vendors/flecs/src/addons/flecs_cpp.c index c62b7bb5e..e8c2ddbbd 100644 --- a/vendors/flecs/src/addons/flecs_cpp.c +++ b/vendors/flecs/src/addons/flecs_cpp.c @@ -98,12 +98,9 @@ char* ecs_cpp_get_symbol_name( const char *type_name, size_t len) { - // Symbol is same as name, but with '::' replaced with '.' - ecs_os_strcpy(symbol_name, type_name); - - char *ptr; + const char *ptr; size_t i; - for (i = 0, ptr = symbol_name; i < len && *ptr; i ++, ptr ++) { + for (i = 0, ptr = type_name; i < len && *ptr; i ++, ptr ++) { if (*ptr == ':') { symbol_name[i] = '.'; ptr ++; @@ -182,17 +179,24 @@ const char* ecs_cpp_trim_module( char *path = ecs_get_path_w_sep(world, 0, scope, "::", NULL); if (path) { - const char *ptr = strrchr(type_name, ':'); - ecs_assert(ptr != type_name, ECS_INTERNAL_ERROR, NULL); - if (ptr) { - ptr --; - ecs_assert(ptr[0] == ':', ECS_INTERNAL_ERROR, NULL); - ecs_size_t name_path_len = (ecs_size_t)(ptr - type_name); - if (name_path_len <= ecs_os_strlen(path)) { - if (!ecs_os_strncmp(type_name, path, name_path_len)) { - type_name = &type_name[name_path_len + 2]; - } + ecs_size_t len = ecs_os_strlen(path); + if (!ecs_os_strncmp(path, type_name, len)) { + // Type is a child of current parent, trim name of parent + type_name += len; + ecs_assert(type_name[0], ECS_INVALID_PARAMETER, + "invalid C++ type name"); + ecs_assert(type_name[0] == ':', ECS_INVALID_PARAMETER, + "invalid C++ type name"); + ecs_assert(type_name[1] == ':', ECS_INVALID_PARAMETER, + "invalid C++ type name"); + type_name += 2; + } else { + // Type is not a child of current parent, trim entire path + char *ptr = strrchr(type_name, ':'); + if (ptr) { + type_name = ptr + 1; } + } } ecs_os_free(path); @@ -238,7 +242,7 @@ void ecs_cpp_component_validate( } else { /* Ensure that the entity id valid */ if (!ecs_is_alive(world, id)) { - ecs_ensure(world, id); + ecs_make_alive(world, id); } /* Register name with entity, so that when the entity is created the @@ -314,7 +318,7 @@ ecs_entity_t ecs_cpp_component_register( * The latter ensures that it was the intent of the application * to alias the type, vs. accidentally registering an unrelated * type with the same size/alignment. */ - char *type_path = ecs_get_fullpath(world, ent); + char *type_path = ecs_get_path(world, ent); if (ecs_os_strcmp(type_path, symbol) || component->size != size || component->alignment != alignment) @@ -375,7 +379,7 @@ ecs_entity_t ecs_cpp_component_register_explicit( } else { // If type is not yet known, derive from type name name = ecs_cpp_trim_module(world, type_name); - } + } } } else { // If an explicit id is provided but it has no name, inherit @@ -395,14 +399,16 @@ ecs_entity_t ecs_cpp_component_register_explicit( .symbol = symbol, .use_low_id = true }); - ecs_assert(entity != 0, ECS_INVALID_OPERATION, NULL); + ecs_assert(entity != 0, ECS_INVALID_OPERATION, + "registration failed for component %s", name); entity = ecs_component_init(world, &(ecs_component_desc_t){ .entity = entity, .type.size = flecs_uto(int32_t, size), .type.alignment = flecs_uto(int32_t, alignment) }); - ecs_assert(entity != 0, ECS_INVALID_OPERATION, NULL); + ecs_assert(entity != 0, ECS_INVALID_OPERATION, + "registration failed for component %s", name); } else { entity = ecs_entity(world, { .id = s_id, @@ -499,7 +505,7 @@ const ecs_member_t* ecs_cpp_last_member( { const EcsStruct *st = ecs_get(world, type, EcsStruct); if (!st) { - char *type_str = ecs_get_fullpath(world, type); + char *type_str = ecs_get_path(world, type); ecs_err("entity '%s' is not a struct", type_str); ecs_os_free(type_str); return 0; diff --git a/vendors/flecs/src/addons/http.c b/vendors/flecs/src/addons/http.c index e33a73189..5e8adf1c7 100644 --- a/vendors/flecs/src/addons/http.c +++ b/vendors/flecs/src/addons/http.c @@ -46,6 +46,9 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #include #include @@ -97,12 +100,6 @@ typedef int ecs_http_socket_t; /* Total number of outstanding send requests */ #define ECS_HTTP_SEND_QUEUE_MAX (256) -/* Cache invalidation timeout (s) */ -#define ECS_HTTP_CACHE_TIMEOUT ((ecs_ftime_t)1.0) - -/* Cache entry purge timeout (s) */ -#define ECS_HTTP_CACHE_PURGE_TIMEOUT ((ecs_ftime_t)10.0) - /* Global statistics */ int64_t ecs_http_request_received_count = 0; int64_t ecs_http_request_invalid_count = 0; @@ -139,7 +136,8 @@ typedef struct ecs_http_request_key_t { typedef struct ecs_http_request_entry_t { char *content; int32_t content_length; - ecs_ftime_t time; + int code; + double time; } ecs_http_request_entry_t; /* HTTP server struct */ @@ -154,6 +152,9 @@ struct ecs_http_server_t { ecs_http_reply_action_t callback; void *ctx; + double cache_timeout; + double cache_purge_timeout; + ecs_sparse_t connections; /* sparse */ ecs_sparse_t requests; /* sparse */ @@ -555,8 +556,8 @@ ecs_http_request_entry_t* http_find_request_entry( &srv->request_cache, &key, ecs_http_request_entry_t); if (entry) { - ecs_ftime_t tf = (ecs_ftime_t)ecs_time_measure(&t); - if ((tf - entry->time) < ECS_HTTP_CACHE_TIMEOUT) { + double tf = ecs_time_measure(&t); + if ((tf - entry->time) < srv->cache_timeout) { return entry; } } @@ -590,9 +591,10 @@ void http_insert_request_entry( } ecs_time_t t = {0, 0}; - entry->time = (ecs_ftime_t)ecs_time_measure(&t); + entry->time = ecs_time_measure(&t); entry->content_length = ecs_strbuf_written(&reply->body); entry->content = ecs_strbuf_get(&reply->body); + entry->code = reply->code; ecs_strbuf_appendstrn(&reply->body, entry->content, entry->content_length); } @@ -604,6 +606,7 @@ char* http_decode_request( { ecs_os_zeromem(req); + ecs_size_t req_len = frag->buf.length; char *res = ecs_strbuf_get(&frag->buf); if (!res) { return NULL; @@ -633,6 +636,9 @@ char* http_decode_request( req->pub.param_count = frag->param_count; req->res = res; req->req_len = frag->header_offsets[0]; + if (!req->req_len) { + req->req_len = req_len; + } return res; } @@ -694,16 +700,16 @@ bool http_parse_request( switch (frag->state) { case HttpFragStateBegin: ecs_os_memset_t(frag, 0, ecs_http_fragment_t); - frag->buf.max = ECS_HTTP_METHOD_LEN_MAX; frag->state = HttpFragStateMethod; frag->header_buf_ptr = frag->header_buf; - + /* fall through */ case HttpFragStateMethod: if (c == ' ') { http_parse_method(frag); + ecs_strbuf_reset(&frag->buf); frag->state = HttpFragStatePath; - frag->buf.max = ECS_HTTP_REQUEST_LEN_MAX; + frag->buf.content = NULL; } else { ecs_strbuf_appendch(&frag->buf, c); } @@ -980,7 +986,7 @@ void http_append_send_headers( ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Origin: *\r\n"); if (preflight) { ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Private-Network: true\r\n"); - ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Methods: GET, PUT, OPTIONS\r\n"); + ecs_strbuf_appendlit(hdrs, "Access-Control-Allow-Methods: GET, PUT, DELETE, OPTIONS\r\n"); ecs_strbuf_appendlit(hdrs, "Access-Control-Max-Age: 600\r\n"); } @@ -996,8 +1002,8 @@ void http_send_reply( bool preflight) { ecs_strbuf_t hdrs = ECS_STRBUF_INIT; + int32_t content_length = reply->body.length; char *content = ecs_strbuf_get(&reply->body); - int32_t content_length = reply->body.length - 1; /* Use asynchronous send queue for outgoing data so send operations won't * hold up main thread */ @@ -1013,8 +1019,8 @@ void http_send_reply( http_append_send_headers(&hdrs, reply->code, reply->status, reply->content_type, &reply->headers, content_length, preflight); - char *headers = ecs_strbuf_get(&hdrs); ecs_size_t headers_length = ecs_strbuf_written(&hdrs); + char *headers = ecs_strbuf_get(&hdrs); if (!req) { ecs_size_t written = http_send(conn->sock, headers, headers_length, 0); @@ -1079,7 +1085,7 @@ void http_recv_connection( if (entry) { ecs_http_reply_t reply; reply.body = ECS_STRBUF_INIT; - reply.code = 200; + reply.code = entry->code; reply.content_type = "application/json"; reply.headers = ECS_STRBUF_INIT; reply.status = "OK"; @@ -1153,7 +1159,7 @@ http_conn_res_t http_init_connection( } static -void http_accept_connections( +int http_accept_connections( ecs_http_server_t* srv, const struct sockaddr* addr, ecs_size_t addr_len) @@ -1167,7 +1173,7 @@ void http_accept_connections( if (result) { ecs_warn("http: WSAStartup failed with GetLastError = %d\n", GetLastError()); - return; + return 0; } } else { http_close(&testsocket); @@ -1178,6 +1184,8 @@ void http_accept_connections( char addr_host[256]; char addr_port[20]; + int ret = 0; /* 0 = ok, 1 = port occupied */ + ecs_http_socket_t sock = HTTP_SOCKET_INVALID; ecs_assert(srv->sock == HTTP_SOCKET_INVALID, ECS_INTERNAL_ERROR, NULL); @@ -1220,8 +1228,15 @@ void http_accept_connections( result = http_bind(sock, addr, addr_len); if (result) { - ecs_err("http: failed to bind to '%s:%s': %s", - addr_host, addr_port, ecs_os_strerror(errno)); + if (errno == EADDRINUSE) { + ret = 1; + ecs_warn("http: address '%s:%s' in use, retrying with port %u", + addr_host, addr_port, srv->port + 1); + } else { + ecs_err("http: failed to bind to '%s:%s': %s", + addr_host, addr_port, ecs_os_strerror(errno)); + } + ecs_os_mutex_unlock(srv->lock); goto done; } @@ -1273,6 +1288,8 @@ void http_accept_connections( ecs_trace("http: no longer accepting connections on '%s:%s'", addr_host, addr_port); + + return ret; } static @@ -1281,6 +1298,9 @@ void* http_server_thread(void* arg) { struct sockaddr_in addr; ecs_os_zeromem(&addr); addr.sin_family = AF_INET; + + int retries = 0; +retry: addr.sin_port = htons(srv->port); if (!srv->ipaddr) { @@ -1289,7 +1309,18 @@ void* http_server_thread(void* arg) { inet_pton(AF_INET, srv->ipaddr, &(addr.sin_addr)); } - http_accept_connections(srv, (struct sockaddr*)&addr, ECS_SIZEOF(addr)); + if (http_accept_connections( + srv, (struct sockaddr*)&addr, ECS_SIZEOF(addr)) == 1) + { + srv->port ++; + retries ++; + if (retries < 10) { + goto retry; + } else { + ecs_err("http: failed to connect (retried 10 times)"); + } + } + return NULL; } @@ -1299,6 +1330,10 @@ void http_do_request( ecs_http_reply_t *reply, const ecs_http_request_impl_t *req) { + ecs_check(srv != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(srv->callback != NULL, ECS_INVALID_OPERATION, + "missing request handler for server"); + if (srv->callback(ECS_CONST_CAST(ecs_http_request_t*, req), reply, srv->ctx) == false) { @@ -1312,6 +1347,8 @@ void http_do_request( ecs_os_linc(&ecs_http_request_handled_ok_count); } } +error: + return; } static @@ -1357,7 +1394,7 @@ void http_purge_request_cache( bool fini) { ecs_time_t t = {0, 0}; - ecs_ftime_t time = (ecs_ftime_t)ecs_time_measure(&t); + double time = ecs_time_measure(&t); ecs_map_iter_t it = ecs_map_iter(&srv->request_cache.impl); while (ecs_map_next(&it)) { ecs_hm_bucket_t *bucket = ecs_map_ptr(&it); @@ -1366,7 +1403,7 @@ void http_purge_request_cache( ecs_http_request_entry_t *entries = ecs_vec_first(&bucket->values); for (i = count - 1; i >= 0; i --) { ecs_http_request_entry_t *entry = &entries[i]; - if (fini || ((time - entry->time) > ECS_HTTP_CACHE_PURGE_TIMEOUT)) { + if (fini || ((time - entry->time) > srv->cache_purge_timeout)) { ecs_http_request_key_t *key = &keys[i]; /* Safe, code owns the value */ ecs_os_free(ECS_CONST_CAST(char*, key->array)); @@ -1457,6 +1494,15 @@ ecs_http_server_t* ecs_http_server_init( srv->should_run = false; srv->initialized = true; + srv->cache_timeout = desc->cache_timeout; + srv->cache_purge_timeout = desc->cache_purge_timeout; + + if (!ECS_EQZERO(srv->cache_timeout) && + ECS_EQZERO(srv->cache_purge_timeout)) + { + srv->cache_purge_timeout = srv->cache_timeout * 10; + } + srv->callback = desc->callback; srv->ctx = desc->ctx; srv->port = desc->port; @@ -1533,8 +1579,10 @@ void ecs_http_server_stop( ecs_http_server_t* srv) { ecs_check(srv != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(srv->initialized, ECS_INVALID_OPERATION, NULL); - ecs_check(srv->should_run, ECS_INVALID_PARAMETER, NULL); + ecs_check(srv->initialized, ECS_INVALID_OPERATION, + "cannot stop HTTP server: not initialized"); + ecs_check(srv->should_run, ECS_INVALID_PARAMETER, + "cannot stop HTTP server: already stopped/stopping"); /* Stop server thread */ ecs_dbg("http: shutting down server thread"); @@ -1637,9 +1685,28 @@ int ecs_http_server_http_request( return -1; } - http_do_request(srv, reply_out, &request); + ecs_http_request_entry_t *entry = + http_find_request_entry(srv, request.res, request.req_len); + if (entry) { + reply_out->body = ECS_STRBUF_INIT; + reply_out->code = entry->code; + reply_out->content_type = "application/json"; + reply_out->headers = ECS_STRBUF_INIT; + reply_out->status = "OK"; + ecs_strbuf_appendstrn(&reply_out->body, + entry->content, entry->content_length); + } else { + http_do_request(srv, reply_out, &request); + + if (request.pub.method == EcsHttpGet) { + http_insert_request_entry(srv, &request, reply_out); + } + } + ecs_os_free(res); + http_purge_request_cache(srv, false); + return (reply_out->code >= 400) ? -1 : 0; } @@ -1649,15 +1716,29 @@ int ecs_http_server_request( const char *req, ecs_http_reply_t *reply_out) { - ecs_strbuf_t reqbuf = ECS_STRBUF_INIT; - ecs_strbuf_appendstr_zerocpy_const(&reqbuf, method); - ecs_strbuf_appendlit(&reqbuf, " "); - ecs_strbuf_appendstr_zerocpy_const(&reqbuf, req); - ecs_strbuf_appendlit(&reqbuf, " HTTP/1.1\r\n\r\n"); - int32_t len = ecs_strbuf_written(&reqbuf); - char *reqstr = ecs_strbuf_get(&reqbuf); + const char *http_ver = " HTTP/1.1\r\n\r\n"; + int32_t method_len = ecs_os_strlen(method); + int32_t req_len = ecs_os_strlen(req); + int32_t http_ver_len = ecs_os_strlen(http_ver); + char reqbuf[1024], *reqstr = reqbuf; + + int32_t len = method_len + req_len + http_ver_len + 1; + if (method_len + req_len + http_ver_len >= 1024) { + reqstr = ecs_os_malloc(len + 1); + } + + char *ptr = reqstr; + ecs_os_memcpy(ptr, method, method_len); ptr += method_len; + ptr[0] = ' '; ptr ++; + ecs_os_memcpy(ptr, req, req_len); ptr += req_len; + ecs_os_memcpy(ptr, http_ver, http_ver_len); ptr += http_ver_len; + ptr[0] = '\n'; + int result = ecs_http_server_http_request(srv, reqstr, len, reply_out); - ecs_os_free(reqstr); + if (reqbuf != reqstr) { + ecs_os_free(reqstr); + } + return result; } diff --git a/vendors/flecs/src/addons/journal.c b/vendors/flecs/src/addons/journal.c index 69e219615..6733b918c 100644 --- a/vendors/flecs/src/addons/journal.c +++ b/vendors/flecs/src/addons/journal.c @@ -15,13 +15,13 @@ char* flecs_journal_entitystr( char *path; const char *_path = ecs_get_symbol(world, entity); if (_path && !strchr(_path, '.')) { - path = ecs_asprintf("#[blue]%s", _path); + path = flecs_asprintf("#[blue]%s", _path); } else { uint32_t gen = entity >> 32; if (gen) { - path = ecs_asprintf("#[normal]_%u_%u", (uint32_t)entity, gen); + path = flecs_asprintf("#[normal]_%u_%u", (uint32_t)entity, gen); } else { - path = ecs_asprintf("#[normal]_%u", (uint32_t)entity); + path = flecs_asprintf("#[normal]_%u", (uint32_t)entity); } } return path; @@ -37,7 +37,7 @@ char* flecs_journal_idstr( ecs_pair_first(world, id)); char *second_path = flecs_journal_entitystr(world, ecs_pair_second(world, id)); - char *result = ecs_asprintf("#[cyan]ecs_pair#[normal](%s, %s)", + char *result = flecs_asprintf("#[cyan]ecs_pair#[normal](%s, %s)", first_path, second_path); ecs_os_free(first_path); ecs_os_free(second_path); @@ -68,7 +68,7 @@ void flecs_journal_begin( char *var_id = NULL; if (entity) { if (kind != EcsJournalDeleteWith && kind != EcsJournalRemoveAll) { - path = ecs_get_fullpath(world, entity); + path = ecs_get_path(world, entity); var_id = flecs_journal_entitystr(world, entity); } else { path = ecs_id_str(world, entity); diff --git a/vendors/flecs/src/addons/json/deserialize.c b/vendors/flecs/src/addons/json/deserialize.c index 972247545..fe41948e5 100644 --- a/vendors/flecs/src/addons/json/deserialize.c +++ b/vendors/flecs/src/addons/json/deserialize.c @@ -1,401 +1,44 @@ /** - * @file json/deserialize.c + * @file addons/json/deserialize.c * @brief Deserialize JSON strings into (component) values. */ #include "../../private_api.h" #include "json.h" +#include "../script/script.h" #include #ifdef FLECS_JSON +typedef struct { + ecs_allocator_t *a; + ecs_vec_t table_type; + ecs_vec_t remove_ids; + ecs_map_t anonymous_ids; + ecs_map_t missing_reflection; + const char *expr; +} ecs_from_json_ctx_t; + static -const char* flecs_json_parse_path( - const ecs_world_t *world, - const char *json, - char *token, - ecs_entity_t *out, - const ecs_from_json_desc_t *desc) +void flecs_from_json_ctx_init( + ecs_allocator_t *a, + ecs_from_json_ctx_t *ctx) { - json = flecs_json_expect(json, JsonString, token, desc); - if (!json) { - goto error; - } - - ecs_entity_t result = ecs_lookup_fullpath(world, token); - if (!result) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "unresolved identifier '%s'", token); - goto error; - } - - *out = result; - - return json; -error: - return NULL; + ctx->a = a; + ecs_vec_init_t(a, &ctx->table_type, ecs_id_t, 0); + ecs_vec_init_t(a, &ctx->remove_ids, ecs_id_t, 0); + ecs_map_init(&ctx->anonymous_ids, a); + ecs_map_init(&ctx->missing_reflection, a); } -const char* ecs_ptr_from_json( - const ecs_world_t *world, - ecs_entity_t type, - void *ptr, - const char *json, - const ecs_from_json_desc_t *desc) +static +void flecs_from_json_ctx_fini( + ecs_from_json_ctx_t *ctx) { - ecs_json_token_t token_kind = 0; - char token_buffer[ECS_MAX_TOKEN_SIZE], t_lah[ECS_MAX_TOKEN_SIZE]; - char *token = token_buffer; - int depth = 0; - - const char *name = NULL; - const char *expr = NULL; - - ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, ptr); - if (cur.valid == false) { - return NULL; - } - - if (desc) { - name = desc->name; - expr = desc->expr; - cur.lookup_action = desc->lookup_action; - cur.lookup_ctx = desc->lookup_ctx; - } - - while ((json = flecs_json_parse(json, &token_kind, token))) { - if (token_kind == JsonLargeString) { - ecs_strbuf_t large_token = ECS_STRBUF_INIT; - json = flecs_json_parse_large_string(json, &large_token); - if (!json) { - break; - } - - token = ecs_strbuf_get(&large_token); - token_kind = JsonString; - } - - if (token_kind == JsonObjectOpen) { - depth ++; - if (ecs_meta_push(&cur) != 0) { - goto error; - } - - if (ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, json - expr, "expected '['"); - return NULL; - } - } else if (token_kind == JsonObjectClose) { - depth --; - - if (ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, json - expr, "expected ']'"); - return NULL; - } - - if (ecs_meta_pop(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonArrayOpen) { - depth ++; - if (ecs_meta_push(&cur) != 0) { - goto error; - } - - if (!ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, json - expr, "expected '{'"); - return NULL; - } - } else if (token_kind == JsonArrayClose) { - depth --; - - if (!ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, json - expr, "expected '}'"); - return NULL; - } - - if (ecs_meta_pop(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonComma) { - if (ecs_meta_next(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonNull) { - if (ecs_meta_set_null(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonString) { - const char *lah = flecs_json_parse( - json, &token_kind, t_lah); - if (token_kind == JsonColon) { - /* Member assignment */ - json = lah; - if (ecs_meta_dotmember(&cur, token) != 0) { - goto error; - } - } else { - if (ecs_meta_set_string(&cur, token) != 0) { - goto error; - } - } - } else if (token_kind == JsonNumber) { - double number = atof(token); - if (ecs_meta_set_float(&cur, number) != 0) { - goto error; - } - } else if (token_kind == JsonLargeInt) { - int64_t number = flecs_ito(int64_t, atoll(token)); - if (ecs_meta_set_int(&cur, number) != 0) { - goto error; - } - } else if (token_kind == JsonNull) { - if (ecs_meta_set_null(&cur) != 0) { - goto error; - } - } else if (token_kind == JsonTrue) { - if (ecs_meta_set_bool(&cur, true) != 0) { - goto error; - } - } else if (token_kind == JsonFalse) { - if (ecs_meta_set_bool(&cur, false) != 0) { - goto error; - } - } else { - goto error; - } - - if (token != token_buffer) { - ecs_os_free(token); - token = token_buffer; - } - - if (!depth) { - break; - } - } - - return json; -error: - return NULL; -} - -const char* ecs_entity_from_json( - ecs_world_t *world, - ecs_entity_t e, - const char *json, - const ecs_from_json_desc_t *desc_param) -{ - ecs_json_token_t token_kind = 0; - char token[ECS_MAX_TOKEN_SIZE]; - - ecs_from_json_desc_t desc = {0}; - - const char *name = NULL, *expr = json, *ids = NULL, *values = NULL, *lah; - if (desc_param) { - desc = *desc_param; - } - - json = flecs_json_expect(json, JsonObjectOpen, token, &desc); - if (!json) { - goto error; - } - - lah = flecs_json_parse(json, &token_kind, token); - if (!lah) { - goto error; - } - - if (token_kind == JsonObjectClose) { - return lah; - } - - json = flecs_json_expect_member(json, token, &desc); - if (!json) { - return NULL; - } - - if (!ecs_os_strcmp(token, "path")) { - json = flecs_json_expect(json, JsonString, token, &desc); - if (!json) { - goto error; - } - - ecs_add_fullpath(world, e, token); - - json = flecs_json_parse(json, &token_kind, token); - if (!json) { - goto error; - } - - if (token_kind == JsonObjectClose) { - return json; - } else if (token_kind != JsonComma) { - ecs_parser_error(name, expr, json - expr, "unexpected character"); - goto error; - } - - json = flecs_json_expect_member_name(json, token, "ids", &desc); - if (!json) { - goto error; - } - } else if (ecs_os_strcmp(token, "ids")) { - ecs_parser_error(name, expr, json - expr, "expected member 'ids'"); - goto error; - } - - json = flecs_json_expect(json, JsonArrayOpen, token, &desc); - if (!json) { - goto error; - } - - ids = json; - - json = flecs_json_skip_array(json, token, &desc); - if (!json) { - return NULL; - } - - json = flecs_json_parse(json, &token_kind, token); - if (token_kind != JsonObjectClose) { - if (token_kind != JsonComma) { - ecs_parser_error(name, expr, json - expr, "expected ','"); - goto error; - } - - json = flecs_json_expect_member_name(json, token, "values", &desc); - if (!json) { - goto error; - } - - json = flecs_json_expect(json, JsonArrayOpen, token, &desc); - if (!json) { - goto error; - } - - values = json; - } - - do { - ecs_entity_t first = 0, second = 0, type_id = 0; - ecs_id_t id; - - ids = flecs_json_parse(ids, &token_kind, token); - if (!ids) { - goto error; - } - - if (token_kind == JsonArrayClose) { - if (values) { - if (values[0] != ']') { - ecs_parser_error(name, expr, values - expr, "expected ']'"); - goto error; - } - json = ecs_parse_ws_eol(values + 1); - } else { - json = ids; - } - - break; - } else if (token_kind == JsonArrayOpen) { - ids = flecs_json_parse_path(world, ids, token, &first, &desc); - if (!ids) { - goto error; - } - - ids = flecs_json_parse(ids, &token_kind, token); - if (!ids) { - goto error; - } - - if (token_kind == JsonComma) { - /* Id is a pair*/ - ids = flecs_json_parse_path(world, ids, token, &second, &desc); - if (!ids) { - goto error; - } - - ids = flecs_json_expect(ids, JsonArrayClose, token, &desc); - if (!ids) { - goto error; - } - } else if (token_kind != JsonArrayClose) { - ecs_parser_error(name, expr, ids - expr, "expected ',' or ']'"); - goto error; - } - - lah = flecs_json_parse(ids, &token_kind, token); - if (!lah) { - goto error; - } - - if (token_kind == JsonComma) { - ids = lah; - } else if (token_kind != JsonArrayClose) { - ecs_parser_error(name, expr, lah - expr, "expected ',' or ']'"); - goto error; - } - } else { - ecs_parser_error(name, expr, lah - expr, "expected '[' or ']'"); - goto error; - } - - if (second) { - id = ecs_pair(first, second); - type_id = ecs_get_typeid(world, id); - if (!type_id) { - ecs_parser_error(name, expr, ids - expr, "id is not a type"); - goto error; - } - } else { - id = first; - type_id = first; - } - - /* Get mutable pointer */ - void *comp_ptr = ecs_get_mut_id(world, e, id); - if (!comp_ptr) { - char *idstr = ecs_id_str(world, id); - ecs_parser_error(name, expr, json - expr, - "id '%s' is not a valid component", idstr); - ecs_os_free(idstr); - goto error; - } - - if (values) { - ecs_from_json_desc_t parse_desc = { - .name = name, - .expr = expr, - }; - - values = ecs_ptr_from_json( - world, type_id, comp_ptr, values, &parse_desc); - if (!values) { - goto error; - } - - lah = flecs_json_parse(values, &token_kind, token); - if (!lah) { - goto error; - } - - if (token_kind == JsonComma) { - values = lah; - } else if (token_kind != JsonArrayClose) { - ecs_parser_error(name, expr, json - expr, - "expected ',' or ']'"); - goto error; - } else { - values = ecs_parse_ws_eol(values); - } - - ecs_modified_id(world, e, id); - } - } while(ids[0]); - - return flecs_json_expect(json, JsonObjectClose, token, &desc); -error: - return NULL; + ecs_vec_fini_t(ctx->a, &ctx->table_type, ecs_record_t*); + ecs_vec_fini_t(ctx->a, &ctx->remove_ids, ecs_record_t*); + ecs_map_fini(&ctx->anonymous_ids); + ecs_map_fini(&ctx->missing_reflection); } static @@ -407,10 +50,31 @@ ecs_entity_t flecs_json_new_id( if (ser_id < FLECS_HI_COMPONENT_ID) { return ecs_new_low_id(world); } else { - return ecs_new_id(world); + return ecs_new(world); } } +static +void flecs_json_missing_reflection( + ecs_world_t *world, + ecs_id_t id, + const char *json, + ecs_from_json_ctx_t *ctx, + const ecs_from_json_desc_t *desc) +{ + if (!desc->strict || ecs_map_get(&ctx->missing_reflection, id)) { + return; + } + + /* Don't spam log when multiple values of a type can't be deserialized */ + ecs_map_ensure(&ctx->missing_reflection, id); + + char *id_str = ecs_id_str(world, id); + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "missing reflection for '%s'", id_str); + ecs_os_free(id_str); +} + static ecs_entity_t flecs_json_lookup( ecs_world_t *world, @@ -422,10 +86,12 @@ ecs_entity_t flecs_json_lookup( if (parent) { scope = ecs_set_scope(world, parent); } + ecs_entity_t result = desc->lookup_action(world, name, desc->lookup_ctx); if (parent) { ecs_set_scope(world, scope); } + return result; } @@ -439,24 +105,6 @@ void flecs_json_mark_reserved( reserved[0] = 0; } -static -bool flecs_json_name_is_anonymous( - const char *name) -{ - if (isdigit(name[0])) { - const char *ptr; - for (ptr = name + 1; *ptr; ptr ++) { - if (!isdigit(*ptr)) { - break; - } - } - if (!(*ptr)) { - return true; - } - } - return false; -} - static ecs_entity_t flecs_json_ensure_entity( ecs_world_t *world, @@ -465,9 +113,9 @@ ecs_entity_t flecs_json_ensure_entity( { ecs_entity_t e = 0; - if (flecs_json_name_is_anonymous(name)) { + if (flecs_name_is_id(name)) { /* Anonymous entity, find or create mapping to new id */ - ecs_entity_t ser_id = flecs_ito(ecs_entity_t, atoll(name)); + ecs_entity_t ser_id = flecs_ito(ecs_entity_t, atoll(&name[1])); ecs_entity_t *deser_id = ecs_map_get(anonymous_ids, ser_id); if (deser_id) { if (!deser_id[0]) { @@ -484,11 +132,15 @@ ecs_entity_t flecs_json_ensure_entity( * to use. This allows the deserializer to bind to existing * anonymous ids, as they will never be reissued. */ deser_id = ecs_map_ensure(anonymous_ids, ser_id); - if (!ecs_exists(world, ser_id) || ecs_is_alive(world, ser_id)) { + if (!ecs_exists(world, ser_id) || + (ecs_is_alive(world, ser_id) && !ecs_get_name(world, ser_id))) + { /* Only use existing id if it's alive or doesn't exist yet. The - * id could have been recycled for another entity */ + * id could have been recycled for another entity + * Also don't use existing id if the existing entity is not + * anonymous. */ deser_id[0] = ser_id; - ecs_ensure(world, ser_id); + ecs_make_alive(world, ser_id); } else { /* If id exists and is not alive, create a new id */ deser_id[0] = flecs_json_new_id(world, ser_id); @@ -511,339 +163,331 @@ ecs_entity_t flecs_json_ensure_entity( } static -ecs_table_t* flecs_json_parse_table( - ecs_world_t *world, - const char *json, - char *token, - const ecs_from_json_desc_t *desc) +bool flecs_json_add_id_to_type( + ecs_id_t id) { - ecs_json_token_t token_kind = 0; - ecs_table_t *table = NULL; - - do { - ecs_id_t id = 0; - json = flecs_json_expect(json, JsonArrayOpen, token, desc); - if (!json) { - goto error; - } - - json = flecs_json_expect(json, JsonString, token, desc); - if (!json) { - goto error; - } - - ecs_entity_t first = flecs_json_lookup(world, 0, token, desc); - if (!first) { - goto error; - } - - json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonComma) { - json = flecs_json_expect(json, JsonString, token, desc); - if (!json) { - goto error; - } - - ecs_entity_t second = flecs_json_lookup(world, 0, token, desc); - if (!second) { - goto error; - } - - id = ecs_pair(first, second); - - json = flecs_json_expect(json, JsonArrayClose, token, desc); - if (!json) { - goto error; - } - } else if (token_kind == JsonArrayClose) { - id = first; - } else { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']"); - goto error; - } - - table = ecs_table_add_id(world, table, id); - if (!table) { - goto error; - } - - const char *lah = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonComma) { - json = lah; - } else if (token_kind == JsonArrayClose) { - break; - } else { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']'"); - goto error; - } - } while (json[0]); - - return table; -error: - return NULL; + if (id == ecs_pair_t(EcsIdentifier, EcsName)) { + return false; + } + if (ECS_IS_PAIR(id) && ECS_PAIR_FIRST(id) == EcsChildOf) { + return false; + } + return true; } static -int flecs_json_parse_entities( +const char* flecs_json_deser_tags( ecs_world_t *world, - ecs_allocator_t *a, - ecs_table_t *table, - ecs_entity_t parent, + ecs_entity_t e, const char *json, - char *token, - ecs_vec_t *records, - const ecs_from_json_desc_t *desc) + const ecs_from_json_desc_t *desc, + ecs_from_json_ctx_t *ctx) { - char name_token[ECS_MAX_TOKEN_SIZE]; - ecs_json_token_t token_kind = 0; - ecs_vec_clear(records); + ecs_json_token_t token_kind; + char token[ECS_MAX_TOKEN_SIZE]; + + const char *expr = ctx->expr, *lah; + + json = flecs_json_expect(json, JsonArrayOpen, token, desc); + if (!json) { + goto error; + } + + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonArrayClose) { + json = lah; + goto end; + } do { - json = flecs_json_parse(json, &token_kind, name_token); + char *str = NULL; + json = flecs_json_expect_string(json, token, &str, desc); if (!json) { goto error; } - if ((token_kind != JsonNumber) && (token_kind != JsonString)) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected number or string"); - goto error; + + ecs_entity_t tag = flecs_json_lookup(world, 0, str, desc); + if (flecs_json_add_id_to_type(tag)) { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = tag; } - ecs_entity_t e = flecs_json_lookup(world, parent, name_token, desc); - ecs_record_t *r = flecs_entities_try(world, e); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_add_id(world, e, tag); - if (r->table != table) { - bool cleared = false; - if (r->table) { - ecs_commit(world, e, r, r->table, NULL, &r->table->type); - cleared = true; - } - ecs_commit(world, e, r, table, &table->type, NULL); - if (cleared) { - char *entity_name = strrchr(name_token, '.'); - if (entity_name) { - entity_name ++; - } else { - entity_name = name_token; - } - if (!flecs_json_name_is_anonymous(entity_name)) { - ecs_set_name(world, e, entity_name); - } - } + if (str != token) { + ecs_os_free(str); } - ecs_assert(table == r->table, ECS_INTERNAL_ERROR, NULL); - ecs_record_t** elem = ecs_vec_append_t(a, records, ecs_record_t*); - *elem = r; - json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonArrayClose) { + if (token_kind != JsonComma) { break; - } else if (token_kind != JsonComma) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']'"); - goto error; } - } while(json[0]); + } while (true); + + if (token_kind != JsonArrayClose) { + ecs_parser_error(NULL, expr, json - expr, "expected }"); + goto error; + } - return 0; + +end: + return json; error: - return -1; + return NULL; } static -const char* flecs_json_parse_column( +const char* flecs_json_deser_pairs( ecs_world_t *world, - ecs_table_t *table, - int32_t index, + ecs_entity_t e, const char *json, - char *token, - ecs_vec_t *records, - const ecs_from_json_desc_t *desc) + const ecs_from_json_desc_t *desc, + ecs_from_json_ctx_t *ctx) { - if (!table->column_count) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "table has no components"); + ecs_json_token_t token_kind; + char token[ECS_MAX_TOKEN_SIZE]; + + const char *expr = ctx->expr, *lah; + + json = flecs_json_expect(json, JsonObjectOpen, token, desc); + if (!json) { goto error; } - if (index >= table->type.count) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "more value arrays than component columns in table"); - goto error; - } + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonObjectClose) { + json = lah; + goto end; + } + + do { + json = flecs_json_expect_member(json, token, desc); + if (!json) { + goto error; + } - int32_t data_column = table->column_map[index]; - if (data_column == -1) { - char *table_str = ecs_table_str(world, table); - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "values provided for tag at column %d of table [%s]", - index, table_str); + ecs_entity_t rel = flecs_json_lookup(world, 0, token, desc); - ecs_os_free(table_str); - goto error; - } + bool multiple_targets = false; - ecs_json_token_t token_kind = 0; - ecs_column_t *column = &table->data.columns[data_column]; - ecs_type_info_t *ti = column->ti; - ecs_size_t size = ti->size; - ecs_entity_t type = ti->component; - ecs_record_t **record_array = ecs_vec_first_t(records, ecs_record_t*); - int32_t entity = 0; + do { + json = flecs_json_parse(json, &token_kind, token); + + if (token_kind == JsonString) { + ecs_entity_t tgt = flecs_json_lookup(world, 0, token, desc); + ecs_id_t id = ecs_pair(rel, tgt); + ecs_add_id(world, e, id); + if (flecs_json_add_id_to_type(id)) { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = id; + } + } else if (token_kind == JsonLargeString) { + ecs_strbuf_t large_token = ECS_STRBUF_INIT; + json = flecs_json_parse_large_string(json, &large_token); + if (!json) { + break; + } - do { - ecs_record_t *r = record_array[entity]; - int32_t row = ECS_RECORD_TO_ROW(r->row); + char *str = ecs_strbuf_get(&large_token); + ecs_entity_t tgt = flecs_json_lookup(world, 0, str, desc); + ecs_os_free(str); + ecs_id_t id = ecs_pair(rel, tgt); + ecs_add_id(world, e, id); + if (flecs_json_add_id_to_type(id)) { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = id; + } + } else if (token_kind == JsonArrayOpen) { + if (multiple_targets) { + ecs_parser_error(NULL, expr, json - expr, + "expected string"); + goto error; + } - void *ptr = ecs_vec_get(&column->data, size, row); - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + multiple_targets = true; + } else if (token_kind == JsonArrayClose) { + if (!multiple_targets) { + ecs_parser_error(NULL, expr, json - expr, + "unexpected ]"); + goto error; + } - json = ecs_ptr_from_json(world, type, ptr, json, desc); - if (!json) { - break; - } + multiple_targets = false; + } else if (token_kind == JsonComma) { + if (!multiple_targets) { + ecs_parser_error(NULL, expr, json - expr, + "unexpected ,"); + goto error; + } + } else { + ecs_parser_error(NULL, expr, json - expr, + "expected array or string"); + goto error; + } + } while (multiple_targets); json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonArrayClose) { + if (token_kind != JsonComma) { break; - } else if (token_kind != JsonComma) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']'"); } + } while (true); - entity ++; - } while (json[0]); + if (token_kind != JsonObjectClose) { + ecs_parser_error(NULL, expr, json - expr, "expected }"); + goto error; + } +end: return json; error: return NULL; } static -const char* flecs_json_parse_values( +const char* flecs_json_deser_components( ecs_world_t *world, - ecs_table_t *table, + ecs_entity_t e, const char *json, - char *token, - ecs_vec_t *records, - ecs_vec_t *columns_set, - const ecs_from_json_desc_t *desc) + const ecs_from_json_desc_t *desc, + ecs_from_json_ctx_t *ctx) { - ecs_allocator_t *a = &world->allocator; - ecs_json_token_t token_kind = 0; - int32_t column = 0; + ecs_json_token_t token_kind; + char token[ECS_MAX_TOKEN_SIZE]; + + const char *expr = ctx->expr, *lah; - ecs_vec_clear(columns_set); + json = flecs_json_expect(json, JsonObjectOpen, token, desc); + if (!json) { + goto error; + } + + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonObjectClose) { + json = lah; + goto end; + } do { - json = flecs_json_parse(json, &token_kind, token); + json = flecs_json_expect_member(json, token, desc); if (!json) { goto error; } - if (token_kind == JsonArrayClose) { - break; - } else if (token_kind == JsonArrayOpen) { - json = flecs_json_parse_column(world, table, column, - json, token, records, desc); - if (!json) { + ecs_id_t id = 0; + + if (token[0] != '(') { + id = flecs_json_lookup(world, 0, token, desc); + } else { + char token_buffer[256]; + ecs_term_t term = {0}; + if (!flecs_term_parse(world, NULL, token, &term, token_buffer)) { goto error; } - ecs_id_t *id_set = ecs_vec_append_t(a, columns_set, ecs_id_t); - *id_set = table->type.array[column]; + ecs_assert(term.first.name != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(term.second.name != NULL, ECS_INTERNAL_ERROR, NULL); - column ++; - } else if (token_kind == JsonNumber) { - if (!ecs_os_strcmp(token, "0")) { - column ++; /* no data */ + ecs_entity_t rel = flecs_json_lookup( + world, 0, term.first.name, desc); + ecs_entity_t tgt = flecs_json_lookup( + world, 0, term.second.name, desc); + + id = ecs_pair(rel, tgt); + } + + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind != JsonNull) { + ecs_entity_t type = ecs_get_typeid(world, id); + if (!type) { + flecs_json_missing_reflection(world, id, json, ctx, desc); + if (desc->strict) { + goto error; + } + + json = flecs_json_skip_object(json + 1, token, desc); + if (!json) { + goto error; + } } else { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "unexpected number"); - goto error; + void *ptr = ecs_ensure_id(world, e, id); + + lah = flecs_json_parse(json, &token_kind, token); + if (token_kind != JsonNull) { + const char *next = ecs_ptr_from_json( + world, type, ptr, json, desc); + if (!next) { + flecs_json_missing_reflection( + world, id, json, ctx, desc); + if (desc->strict) { + goto error; + } + + json = flecs_json_skip_object(json + 1, token, desc); + if (!json) { + goto error; + } + } else { + json = next; + ecs_modified_id(world, e, id); + } + } else { + json = lah; + } } + } else { + ecs_add_id(world, e, id); + json = lah; + } + + /* Don't add ids that have their own fields in serialized data. */ + if (flecs_json_add_id_to_type(id)) { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = id; } json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonArrayClose) { + if (token_kind != JsonComma) { break; - } else if (token_kind != JsonComma) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or ']'"); - goto error; } - } while (json[0]); - - /* Send OnSet notifications */ - ecs_defer_begin(world); - ecs_type_t type = { - .array = columns_set->array, - .count = columns_set->count }; + } while (true); - int32_t table_count = ecs_table_count(table); - int32_t i, record_count = ecs_vec_count(records); - - /* If the entire table was inserted, send bulk notification */ - if (table_count == ecs_vec_count(records)) { - flecs_notify_on_set(world, table, 0, ecs_table_count(table), &type, true); - } else { - ecs_record_t **rvec = ecs_vec_first_t(records, ecs_record_t*); - for (i = 0; i < record_count; i ++) { - ecs_record_t *r = rvec[i]; - int32_t row = ECS_RECORD_TO_ROW(r->row); - flecs_notify_on_set(world, table, row, 1, &type, true); - } + if (token_kind != JsonObjectClose) { + ecs_parser_error(NULL, expr, json - expr, "expected }"); + goto error; } - ecs_defer_end(world); - +end: return json; error: return NULL; } static -const char* flecs_json_parse_result( +const char* flecs_entity_from_json( ecs_world_t *world, - ecs_allocator_t *a, + ecs_entity_t e, const char *json, - char *token, - ecs_vec_t *records, - ecs_vec_t *columns_set, - const ecs_from_json_desc_t *desc) + const ecs_from_json_desc_t *desc, + ecs_from_json_ctx_t *ctx) { - ecs_json_token_t token_kind = 0; - const char *ids = NULL, *values = NULL, *entities = NULL; + ecs_json_token_t token_kind; + char token[ECS_MAX_TOKEN_SIZE]; - json = flecs_json_expect(json, JsonObjectOpen, token, desc); - if (!json) { - goto error; - } + const char *expr = ctx->expr, *lah; - json = flecs_json_expect_member_name(json, token, "ids", desc); - if (!json) { - goto error; - } + ecs_vec_clear(&ctx->table_type); - json = flecs_json_expect(json, JsonArrayOpen, token, desc); + ecs_entity_t parent = 0; + + json = flecs_json_expect(json, JsonObjectOpen, token, desc); if (!json) { goto error; } - ids = json; /* store start of ids array */ - - json = flecs_json_skip_array(json, token, desc); - if (!json) { + lah = flecs_json_parse(json, &token_kind, token); + if (!lah) { goto error; } - json = flecs_json_expect(json, JsonComma, token, desc); - if (!json) { - goto error; + if (token_kind == JsonObjectClose) { + json = lah; + goto end; } json = flecs_json_expect_member(json, token, desc); @@ -851,94 +495,254 @@ const char* flecs_json_parse_result( goto error; } - ecs_entity_t parent = 0; if (!ecs_os_strcmp(token, "parent")) { - json = flecs_json_expect(json, JsonString, token, desc); + char *str = NULL; + json = flecs_json_expect_string(json, token, &str, desc); if (!json) { goto error; } - parent = ecs_lookup_fullpath(world, token); - json = flecs_json_expect(json, JsonComma, token, desc); + parent = flecs_json_lookup(world, 0, str, desc); + + if (e) { + ecs_add_pair(world, e, EcsChildOf, parent); + } + + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = + ecs_pair(EcsChildOf, parent); + + if (str != token) ecs_os_free(str); + + json = flecs_json_parse_next_member(json, token, &token_kind, desc); if (!json) { goto error; } + if (token_kind == JsonObjectClose) { + goto end; + } + } - json = flecs_json_expect_member(json, token, desc); + if (!ecs_os_strcmp(token, "name")) { + char *str = NULL; + json = flecs_json_expect_string(json, token, &str, desc); if (!json) { goto error; } - } - if (ecs_os_strcmp(token, "entities")) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected 'entities'"); - goto error; - } + if (!e) { + e = flecs_json_lookup(world, parent, str, desc); + } else { + ecs_set_name(world, e, str); + } - json = flecs_json_expect(json, JsonArrayOpen, token, desc); - if (!json) { - goto error; - } + if (str[0] != '#') { + ecs_vec_append_t(ctx->a, &ctx->table_type, ecs_id_t)[0] = + ecs_pair_t(EcsIdentifier, EcsName); + } - entities = json; /* store start of entity id array */ + if (str != token) ecs_os_free(str); - json = flecs_json_skip_array(json, token, desc); - if (!json) { - goto error; + json = flecs_json_parse_next_member(json, token, &token_kind, desc); + if (!json) { + goto error; + } + if (token_kind == JsonObjectClose) { + goto end; + } } - json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonComma) { - json = flecs_json_expect_member_name(json, token, "values", desc); + if (!ecs_os_strcmp(token, "id")) { + json = flecs_json_parse(json, &token_kind, token); if (!json) { goto error; } - json = flecs_json_expect(json, JsonArrayOpen, token, desc); + uint64_t id; + if (token_kind == JsonNumber || token_kind == JsonLargeInt) { + id = flecs_ito(uint64_t, atoll(token)); + } else { + ecs_parser_error(NULL, expr, json - expr, "expected entity id"); + goto error; + } + + if (!e) { + char name[32]; + ecs_os_snprintf(name, 32, "#%u", (uint32_t)id); + e = flecs_json_lookup(world, 0, name, desc); + } else { + /* If we already have an id, ignore explicit id */ + } + + json = flecs_json_parse_next_member(json, token, &token_kind, desc); if (!json) { goto error; } + if (token_kind == JsonObjectClose) { + goto end; + } + } - values = json; /* store start of entities array */ - } else if (token_kind != JsonObjectClose) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected ',' or '}'"); - goto error; + if (!e) { + ecs_parser_error(NULL, expr, json - expr, "failed to create entity"); + return NULL; } - /* Find table from ids */ - ecs_table_t *table = flecs_json_parse_table(world, ids, token, desc); - if (!table) { - goto error; + if (!ecs_os_strcmp(token, "has_alerts")) { + json = flecs_json_expect(json, JsonBoolean, token, desc); + if (!json) { + goto error; + } + + json = flecs_json_parse_next_member(json, token, &token_kind, desc); + if (!json) { + goto error; + } + if (token_kind == JsonObjectClose) { + goto end; + } } - /* Add entities to table */ - if (flecs_json_parse_entities(world, a, table, parent, - entities, token, records, desc)) - { - goto error; + if (!ecs_os_strcmp(token, "tags")) { + json = flecs_json_deser_tags(world, e, json, desc, ctx); + if (!json) { + goto error; + } + + json = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonObjectClose) { + goto end; + } else if (token_kind != JsonComma) { + ecs_parser_error(NULL, expr, json - expr, "expected ','"); + goto error; + } + + json = flecs_json_expect_member(json, token, desc); + if (!json) { + goto error; + } } - /* Parse values */ - if (values) { - json = flecs_json_parse_values(world, table, values, token, - records, columns_set, desc); + if (!ecs_os_strcmp(token, "pairs")) { + json = flecs_json_deser_pairs(world, e, json, desc, ctx); + if (!json) { + goto error; + } + + json = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonObjectClose) { + goto end; + } else if (token_kind != JsonComma) { + ecs_parser_error(NULL, expr, json - expr, "expected ','"); + goto error; + } + + json = flecs_json_expect_member(json, token, desc); if (!json) { goto error; } + } - json = flecs_json_expect(json, JsonObjectClose, token, desc); + if (!ecs_os_strcmp(token, "components")) { + json = flecs_json_deser_components(world, e, json, desc, ctx); if (!json) { goto error; } } + json = flecs_json_expect(json, JsonObjectClose, token, desc); + if (!json) { + goto error; + } + + ecs_record_t *r = flecs_entities_get(world, e); + ecs_table_t *table = r ? r->table : NULL; + if (table) { + ecs_id_t *ids = ecs_vec_first(&ctx->table_type); + int32_t ids_count = ecs_vec_count(&ctx->table_type); + qsort(ids, flecs_itosize(ids_count), sizeof(ecs_id_t), flecs_id_qsort_cmp); + + ecs_table_t *dst_table = ecs_table_find(world, + ecs_vec_first(&ctx->table_type), ecs_vec_count(&ctx->table_type)); + if (dst_table->type.count == 0) { + dst_table = NULL; + } + + /* Entity had existing components that weren't in the serialized data */ + if (table != dst_table) { + ecs_assert(ecs_get_target(world, e, EcsChildOf, 0) != EcsFlecsCore, + ECS_INVALID_OPERATION, "%s\n[%s] => \n[%s]", + ecs_get_path(world, e), + ecs_table_str(world, table), + ecs_table_str(world, dst_table)); + + if (!dst_table) { + ecs_clear(world, e); + } else { + ecs_vec_clear(&ctx->remove_ids); + + ecs_type_t *type = &table->type, *dst_type = &dst_table->type; + int32_t i = 0, i_dst = 0; + for (; (i_dst < dst_type->count) && (i < type->count); ) { + ecs_id_t id = type->array[i], dst_id = dst_type->array[i_dst]; + + if (dst_id > id) { + ecs_vec_append_t( + ctx->a, &ctx->remove_ids, ecs_id_t)[0] = id; + } + + i_dst += dst_id <= id; + i += dst_id >= id; + } + + ecs_type_t removed = { + .array = ecs_vec_first(&ctx->remove_ids), + .count = ecs_vec_count(&ctx->remove_ids) + }; + + ecs_commit(world, e, r, dst_table, NULL, &removed); + } + + ecs_assert(ecs_get_table(world, e) == dst_table, + ECS_INTERNAL_ERROR, NULL); + } + } + +end: return json; error: return NULL; } +const char* ecs_entity_from_json( + ecs_world_t *world, + ecs_entity_t e, + const char *json, + const ecs_from_json_desc_t *desc_arg) +{ + ecs_from_json_desc_t desc = {0}; + if (desc_arg) { + desc = *desc_arg; + } + + desc.expr = json; + + ecs_allocator_t *a = &world->allocator; + ecs_from_json_ctx_t ctx; + flecs_from_json_ctx_init(a, &ctx); + ctx.expr = json; + + if (!desc.lookup_action) { + desc.lookup_action = (ecs_entity_t(*)( + const ecs_world_t*, const char*, void*))flecs_json_ensure_entity; + desc.lookup_ctx = &ctx.anonymous_ids; + } + + json = flecs_entity_from_json(world, e, json, &desc, &ctx); + + flecs_from_json_ctx_fini(&ctx); + return json; +} + const char* ecs_world_from_json( ecs_world_t *world, const char *json, @@ -948,23 +752,23 @@ const char* ecs_world_from_json( char token[ECS_MAX_TOKEN_SIZE]; ecs_from_json_desc_t desc = {0}; - ecs_allocator_t *a = &world->allocator; - ecs_vec_t records; - ecs_vec_t columns_set; - ecs_map_t anonymous_ids; - ecs_vec_init_t(a, &records, ecs_record_t*, 0); - ecs_vec_init_t(a, &columns_set, ecs_id_t, 0); - ecs_map_init(&anonymous_ids, a); - - const char *name = NULL, *expr = json, *lah; if (desc_arg) { desc = *desc_arg; } + desc.expr = json; + + ecs_allocator_t *a = &world->allocator; + ecs_from_json_ctx_t ctx; + flecs_from_json_ctx_init(a, &ctx); + + const char *expr = json, *lah; + ctx.expr = expr; + if (!desc.lookup_action) { desc.lookup_action = (ecs_entity_t(*)( const ecs_world_t*, const char*, void*))flecs_json_ensure_entity; - desc.lookup_ctx = &anonymous_ids; + desc.lookup_ctx = &ctx.anonymous_ids; } json = flecs_json_expect(json, JsonObjectOpen, token, &desc); @@ -989,39 +793,45 @@ const char* ecs_world_from_json( } do { - json = flecs_json_parse_result(world, a, json, token, - &records, &columns_set, &desc); + json = flecs_entity_from_json(world, 0, json, &desc, &ctx); if (!json) { goto error; } json = flecs_json_parse(json, &token_kind, token); - if (token_kind == JsonArrayClose) { + if (token_kind != JsonComma) { + if (token_kind != JsonArrayClose) { + ecs_parser_error(NULL, expr, json - expr, "expected ']'"); + goto error; + } break; - } else if (token_kind != JsonComma) { - ecs_parser_error(name, expr, json - expr, - "expected ',' or ']'"); - goto error; } - } while(json && json[0]); + } while (true); end: - ecs_vec_fini_t(a, &records, ecs_record_t*); - ecs_vec_fini_t(a, &columns_set, ecs_id_t); - ecs_map_fini(&anonymous_ids); - json = flecs_json_expect(json, JsonObjectClose, token, &desc); - if (!json) { - goto error; - } + flecs_from_json_ctx_fini(&ctx); return json; error: - ecs_vec_fini_t(a, &records, ecs_record_t*); - ecs_vec_fini_t(a, &columns_set, ecs_id_t); - ecs_map_fini(&anonymous_ids); - + flecs_from_json_ctx_fini(&ctx); return NULL; } +const char* ecs_world_from_json_file( + ecs_world_t *world, + const char *filename, + const ecs_from_json_desc_t *desc) +{ + char *json = flecs_load_from_file(filename); + if (!json) { + ecs_err("file not found: %s", filename); + return NULL; + } + + const char *result = ecs_world_from_json(world, json, desc); + ecs_os_free(json); + return result; +} + #endif diff --git a/vendors/flecs/src/addons/json/deserialize_value.c b/vendors/flecs/src/addons/json/deserialize_value.c new file mode 100644 index 000000000..6de6fe3ba --- /dev/null +++ b/vendors/flecs/src/addons/json/deserialize_value.c @@ -0,0 +1,157 @@ +/** + * @file addons/json/deserialize_value.c + * @brief Deserialize JSON strings into (component) values. + */ + +#include "../../private_api.h" +#include "json.h" +#include "../script/script.h" +#include + +#ifdef FLECS_JSON + +const char* ecs_ptr_from_json( + const ecs_world_t *world, + ecs_entity_t type, + void *ptr, + const char *json, + const ecs_from_json_desc_t *desc) +{ + ecs_json_token_t token_kind = 0; + char token_buffer[ECS_MAX_TOKEN_SIZE], t_lah[ECS_MAX_TOKEN_SIZE]; + char *token = token_buffer; + int depth = 0; + + const char *name = NULL; + const char *expr = NULL; + + ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, ptr); + if (cur.valid == false) { + return NULL; + } + + if (desc) { + name = desc->name; + expr = desc->expr; + cur.lookup_action = desc->lookup_action; + cur.lookup_ctx = desc->lookup_ctx; + } + + while ((json = flecs_json_parse(json, &token_kind, token))) { + if (token_kind == JsonLargeString) { + ecs_strbuf_t large_token = ECS_STRBUF_INIT; + json = flecs_json_parse_large_string(json, &large_token); + if (!json) { + break; + } + + token = ecs_strbuf_get(&large_token); + token_kind = JsonString; + } + + if (token_kind == JsonObjectOpen) { + depth ++; + if (ecs_meta_push(&cur) != 0) { + goto error; + } + + if (ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, json - expr, "expected '['"); + return NULL; + } + } else if (token_kind == JsonObjectClose) { + depth --; + + if (ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, json - expr, "expected ']'"); + return NULL; + } + + if (ecs_meta_pop(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonArrayOpen) { + depth ++; + if (ecs_meta_push(&cur) != 0) { + goto error; + } + + if (!ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, json - expr, "expected '{'"); + return NULL; + } + } else if (token_kind == JsonArrayClose) { + depth --; + + if (!ecs_meta_is_collection(&cur)) { + ecs_parser_error(name, expr, json - expr, "expected '}'"); + return NULL; + } + + if (ecs_meta_pop(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonComma) { + if (ecs_meta_next(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonNull) { + if (ecs_meta_set_null(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonString) { + const char *lah = flecs_json_parse( + json, &token_kind, t_lah); + if (token_kind == JsonColon) { + /* Member assignment */ + json = lah; + if (ecs_meta_dotmember(&cur, token) != 0) { + goto error; + } + } else { + if (ecs_meta_set_string(&cur, token) != 0) { + goto error; + } + } + } else if (token_kind == JsonNumber) { + double number = atof(token); + if (ecs_meta_set_float(&cur, number) != 0) { + goto error; + } + } else if (token_kind == JsonLargeInt) { + int64_t number = flecs_ito(int64_t, atoll(token)); + if (ecs_meta_set_int(&cur, number) != 0) { + goto error; + } + } else if (token_kind == JsonNull) { + if (ecs_meta_set_null(&cur) != 0) { + goto error; + } + } else if (token_kind == JsonTrue) { + if (ecs_meta_set_bool(&cur, true) != 0) { + goto error; + } + } else if (token_kind == JsonFalse) { + if (ecs_meta_set_bool(&cur, false) != 0) { + goto error; + } + } else { + goto error; + } + + if (token != token_buffer) { + ecs_os_free(token); + token = token_buffer; + } + + if (!depth) { + break; + } + } + + return json; +error: + return NULL; +} + +#endif diff --git a/vendors/flecs/src/addons/json/json.c b/vendors/flecs/src/addons/json/json.c index af4b8b1fc..a905a16d6 100644 --- a/vendors/flecs/src/addons/json/json.c +++ b/vendors/flecs/src/addons/json/json.c @@ -1,5 +1,5 @@ /** - * @file json/json.c + * @file addons/json/json.c * @brief JSON serializer utilities. */ @@ -23,6 +23,7 @@ const char* flecs_json_token_str( case JsonLargeInt: return "large integer"; case JsonLargeString: case JsonString: return "string"; + case JsonBoolean: return "bool"; case JsonTrue: return "true"; case JsonFalse: return "false"; case JsonNull: return "null"; @@ -39,7 +40,8 @@ const char* flecs_json_parse( ecs_json_token_t *token_kind, char *token) { - json = ecs_parse_ws_eol(json); + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); + json = flecs_parse_ws_eol(json); char ch = json[0]; @@ -66,12 +68,6 @@ const char* flecs_json_parse( char *token_ptr = token; json ++; for (; (ch = json[0]); ) { - if (ch == '"') { - json ++; - token_ptr[0] = '\0'; - break; - } - if (token_ptr - token >= ECS_MAX_TOKEN_SIZE) { /* Token doesn't fit in buffer, signal to app to try again with * dynamic buffer. */ @@ -79,7 +75,13 @@ const char* flecs_json_parse( return start; } - json = ecs_chrparse(json, token_ptr ++); + if (ch == '"') { + json ++; + token_ptr[0] = '\0'; + break; + } + + json = flecs_chrparse(json, token_ptr ++); } if (!ch) { @@ -91,7 +93,7 @@ const char* flecs_json_parse( } } else if (isdigit(ch) || (ch == '-')) { token_kind[0] = JsonNumber; - const char *result = ecs_parse_digit(json, token); + const char *result = flecs_parse_digit(json, token); /* Cheap initial check if parsed token could represent large int */ if (result - json > 15) { @@ -144,7 +146,7 @@ const char* flecs_json_parse_large_string( break; } - json = ecs_chrparse(json, &ch_out); + json = flecs_chrparse(json, &ch_out); ecs_strbuf_appendch(buf, ch_out); } @@ -155,22 +157,109 @@ const char* flecs_json_parse_large_string( } } +const char* flecs_json_parse_next_member( + const char *json, + char *token, + ecs_json_token_t *token_kind, + const ecs_from_json_desc_t *desc) +{ + json = flecs_json_parse(json, token_kind, token); + if (*token_kind == JsonObjectClose) { + return json; + } + + if (*token_kind != JsonComma) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expecteded } or ,"); + return NULL; + } + + json = flecs_json_parse(json, token_kind, token); + if (*token_kind != JsonString) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expecteded member name"); + return NULL; + } + + char temp_token[ECS_MAX_TOKEN_SIZE]; + ecs_json_token_t temp_token_kind; + + json = flecs_json_parse(json, &temp_token_kind, temp_token); + if (temp_token_kind != JsonColon) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expecteded :"); + return NULL; + } + + return json; +} + const char* flecs_json_expect( const char *json, ecs_json_token_t token_kind, char *token, const ecs_from_json_desc_t *desc) { + /* Strings must be handled by flecs_json_expect_string for LargeString */ + ecs_assert(token_kind != JsonString, ECS_INTERNAL_ERROR, NULL); + ecs_json_token_t kind = 0; - json = flecs_json_parse(json, &kind, token); + const char *lah = flecs_json_parse(json, &kind, token); + if (kind == JsonInvalid) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, "invalid json"); + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "invalid json"); return NULL; } else if (kind != token_kind) { - ecs_parser_error(desc->name, desc->expr, json - desc->expr, "expected %s", - flecs_json_token_str(token_kind)); + if (token_kind == JsonBoolean && + (kind == JsonTrue || kind == JsonFalse)) + { + /* ok */ + } else { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expected %s, got %s", + flecs_json_token_str(token_kind), flecs_json_token_str(kind)); + return NULL; + } + } + + return lah; +} + +const char* flecs_json_expect_string( + const char *json, + char *token, + char **out, + const ecs_from_json_desc_t *desc) +{ + ecs_json_token_t token_kind = 0; + json = flecs_json_parse(json, &token_kind, token); + if (token_kind == JsonInvalid) { + ecs_parser_error( + desc->name, desc->expr, json - desc->expr, "invalid json"); + return NULL; + } else if (token_kind != JsonString && token_kind != JsonLargeString) { + ecs_parser_error(desc->name, desc->expr, json - desc->expr, + "expected string"); return NULL; } + + if (token_kind == JsonLargeString) { + ecs_strbuf_t large_token = ECS_STRBUF_INIT; + json = flecs_json_parse_large_string(json, &large_token); + if (!json) { + return NULL; + } + + if (out) { + *out = ecs_strbuf_get(&large_token); + } else { + ecs_strbuf_reset(&large_token); + } + } else if (out) { + *out = token; + } + return json; } @@ -179,10 +268,16 @@ const char* flecs_json_expect_member( char *token, const ecs_from_json_desc_t *desc) { - json = flecs_json_expect(json, JsonString, token, desc); + char *out = NULL; + json = flecs_json_expect_string(json, token, &out, desc); if (!json) { return NULL; } + + if (out != token) { + ecs_os_free(out); + } + json = flecs_json_expect(json, JsonColon, token, desc); if (!json) { return NULL; @@ -190,6 +285,19 @@ const char* flecs_json_expect_member( return json; } +const char* flecs_json_expect_next_member( + const char *json, + char *token, + const ecs_from_json_desc_t *desc) +{ + json = flecs_json_expect(json, JsonComma, token, desc); + if (!json) { + return NULL; + } + + return flecs_json_expect_member(json, token, desc); +} + const char* flecs_json_expect_member_name( const char *json, char *token, @@ -208,11 +316,38 @@ const char* flecs_json_expect_member_name( return json; } +static +const char* flecs_json_skip_string( + const char *json) +{ + if (json[0] != '"') { + return NULL; /* can only skip strings */ + } + + char ch, ch_out; + json ++; + for (; (ch = json[0]); ) { + if (ch == '"') { + json ++; + break; + } + + json = flecs_chrparse(json, &ch_out); + } + + if (!ch) { + return NULL; + } else { + return json; + } +} + const char* flecs_json_skip_object( const char *json, char *token, const ecs_from_json_desc_t *desc) { + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); ecs_json_token_t token_kind = 0; while ((json = flecs_json_parse(json, &token_kind, token))) { @@ -220,16 +355,21 @@ const char* flecs_json_skip_object( json = flecs_json_skip_object(json, token, desc); } else if (token_kind == JsonArrayOpen) { json = flecs_json_skip_array(json, token, desc); + } else if (token_kind == JsonLargeString) { + json = flecs_json_skip_string(json); } else if (token_kind == JsonObjectClose) { return json; } else if (token_kind == JsonArrayClose) { ecs_parser_error(desc->name, desc->expr, json - desc->expr, - "expected }"); + "expected }, got ]"); return NULL; } + + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); } - ecs_parser_error(desc->name, desc->expr, json - desc->expr, "expected }"); + ecs_parser_error(desc->name, json, 0, + "expected }, got end of string"); return NULL; } @@ -238,6 +378,7 @@ const char* flecs_json_skip_array( char *token, const ecs_from_json_desc_t *desc) { + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); ecs_json_token_t token_kind = 0; while ((json = flecs_json_parse(json, &token_kind, token))) { @@ -245,6 +386,8 @@ const char* flecs_json_skip_array( json = flecs_json_skip_object(json, token, desc); } else if (token_kind == JsonArrayOpen) { json = flecs_json_skip_array(json, token, desc); + } else if (token_kind == JsonLargeString) { + json = flecs_json_skip_string(json); } else if (token_kind == JsonObjectClose) { ecs_parser_error(desc->name, desc->expr, json - desc->expr, "expected ]"); @@ -252,6 +395,8 @@ const char* flecs_json_skip_array( } else if (token_kind == JsonArrayClose) { return json; } + + ecs_assert(json != NULL, ECS_INTERNAL_ERROR, NULL); } ecs_parser_error(desc->name, desc->expr, json - desc->expr, "expected ]"); @@ -271,6 +416,13 @@ void flecs_json_number( ecs_strbuf_appendflt(buf, value, '"'); } +void flecs_json_u32( + ecs_strbuf_t *buf, + uint32_t value) +{ + ecs_strbuf_appendint(buf, flecs_uto(int64_t, value)); +} + void flecs_json_true( ecs_strbuf_t *buf) { @@ -294,6 +446,12 @@ void flecs_json_bool( } } +void flecs_json_null( + ecs_strbuf_t *buf) +{ + ecs_strbuf_appendlit(buf, "null"); +} + void flecs_json_array_push( ecs_strbuf_t *buf) { @@ -331,18 +489,19 @@ void flecs_json_string_escape( ecs_strbuf_t *buf, const char *value) { - ecs_size_t length = ecs_stresc(NULL, 0, '"', value); + ecs_size_t length = flecs_stresc(NULL, 0, '"', value); if (length == ecs_os_strlen(value)) { ecs_strbuf_appendch(buf, '"'); ecs_strbuf_appendstrn(buf, value, length); ecs_strbuf_appendch(buf, '"'); } else { char *out = ecs_os_malloc(length + 3); - ecs_stresc(out + 1, length, '"', value); + flecs_stresc(out + 1, length, '"', value); out[0] = '"'; out[length + 1] = '"'; out[length + 2] = '\0'; - ecs_strbuf_appendstr_zerocpy(buf, out); + ecs_strbuf_appendstr(buf, out); + ecs_os_free(out); } } @@ -369,28 +528,53 @@ void flecs_json_path( ecs_entity_t e) { ecs_strbuf_appendch(buf, '"'); - ecs_get_path_w_sep_buf(world, 0, e, ".", "", buf); + ecs_get_path_w_sep_buf(world, 0, e, ".", "", buf, true); ecs_strbuf_appendch(buf, '"'); } -void flecs_json_label( - ecs_strbuf_t *buf, +static +const char* flecs_json_entity_label( const ecs_world_t *world, ecs_entity_t e) { const char *lbl = NULL; + if (!e) { + return "#0"; + } #ifdef FLECS_DOC lbl = ecs_doc_get_name(world, e); #else lbl = ecs_get_name(world, e); #endif + return lbl; +} +void flecs_json_label( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e) +{ + const char *lbl = flecs_json_entity_label(world, e); if (lbl) { + flecs_json_string_escape(buf, lbl); + } else { ecs_strbuf_appendch(buf, '"'); - ecs_strbuf_appendstr(buf, lbl); + ecs_strbuf_appendch(buf, '#'); + ecs_strbuf_appendint(buf, (uint32_t)e); ecs_strbuf_appendch(buf, '"'); + } +} + +void flecs_json_path_or_label( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e, + bool path) +{ + if (!path) { + flecs_json_label(buf, world, e); } else { - ecs_strbuf_appendch(buf, '0'); + flecs_json_path(buf, world, e); } } @@ -427,21 +611,89 @@ void flecs_json_id( ecs_entity_t first = ecs_pair_first(world, id); ecs_entity_t second = ecs_pair_second(world, id); ecs_strbuf_appendch(buf, '"'); - ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf); + ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf, true); ecs_strbuf_appendch(buf, '"'); ecs_strbuf_appendch(buf, ','); ecs_strbuf_appendch(buf, '"'); - ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf); + ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf, true); ecs_strbuf_appendch(buf, '"'); } else { ecs_strbuf_appendch(buf, '"'); - ecs_get_path_w_sep_buf(world, 0, id & ECS_COMPONENT_MASK, ".", "", buf); + ecs_get_path_w_sep_buf(world, 0, id & ECS_COMPONENT_MASK, ".", "", buf, true); ecs_strbuf_appendch(buf, '"'); } ecs_strbuf_appendch(buf, ']'); } +static +void flecs_json_id_member_fullpath( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_id_t id) +{ + if (ECS_IS_PAIR(id)) { + ecs_strbuf_appendch(buf, '('); + ecs_entity_t first = ecs_pair_first(world, id); + ecs_entity_t second = ecs_pair_second(world, id); + ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf, true); + ecs_strbuf_appendch(buf, ','); + ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf, true); + ecs_strbuf_appendch(buf, ')'); + } else { + ecs_get_path_w_sep_buf(world, 0, id & ECS_COMPONENT_MASK, ".", "", buf, true); + } +} + +void flecs_json_id_member( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_id_t id, + bool fullpath) +{ + ecs_id_t flags = id & ECS_ID_FLAGS_MASK; + + if (flags & ECS_AUTO_OVERRIDE) { + ecs_strbuf_appendlit(buf, "auto_override|"); + id &= ~ECS_AUTO_OVERRIDE; + } + + if (flags & ECS_TOGGLE) { + ecs_strbuf_appendlit(buf, "toggle|"); + id &= ~ECS_TOGGLE; + } + + if (fullpath) { + flecs_json_id_member_fullpath(buf, world, id); + return; + } + + if (ECS_IS_PAIR(id)) { + ecs_strbuf_appendch(buf, '('); + ecs_entity_t first = ecs_pair_first(world, id); + ecs_entity_t second = ecs_pair_second(world, id); + { + const char *lbl = flecs_json_entity_label(world, first); + if (lbl) { + ecs_strbuf_appendstr(buf, lbl); + } + } + ecs_strbuf_appendch(buf, ','); + { + const char *lbl = flecs_json_entity_label(world, second); + if (lbl) { + ecs_strbuf_appendstr(buf, lbl); + } + } + ecs_strbuf_appendch(buf, ')'); + } else { + const char *lbl = flecs_json_entity_label(world, id & ECS_COMPONENT_MASK); + if (lbl) { + ecs_strbuf_appendstr(buf, lbl); + } + } +} + ecs_primitive_kind_t flecs_json_op_to_primitive_kind( ecs_meta_type_op_kind_t kind) { diff --git a/vendors/flecs/src/addons/json/json.h b/vendors/flecs/src/addons/json/json.h index b223fcad9..ceb5383ac 100644 --- a/vendors/flecs/src/addons/json/json.h +++ b/vendors/flecs/src/addons/json/json.h @@ -1,8 +1,11 @@ /** - * @file json/json.h + * @file addons/json/json.h * @brief Internal functions for JSON addon. */ +#ifndef FLECS_JSON_PRIVATE_H +#define FLECS_JSON_PRIVATE_H + #include "../../private_api.h" #ifdef FLECS_JSON @@ -17,6 +20,7 @@ typedef enum ecs_json_token_t { JsonComma, JsonNumber, JsonString, + JsonBoolean, JsonTrue, JsonFalse, JsonNull, @@ -25,6 +29,31 @@ typedef enum ecs_json_token_t { JsonInvalid } ecs_json_token_t; +typedef struct ecs_json_value_ser_ctx_t { + ecs_entity_t type; + const EcsTypeSerializer *ser; + char *id_label; + bool initialized; +} ecs_json_value_ser_ctx_t; + +/* Cached data for serializer */ +typedef struct ecs_json_ser_ctx_t { + ecs_id_record_t *idr_doc_name; + ecs_id_record_t *idr_doc_color; + ecs_json_value_ser_ctx_t value_ctx[64]; +} ecs_json_ser_ctx_t; + +typedef struct ecs_json_this_data_t { + const ecs_entity_t *ids; + const EcsIdentifier *names; + const EcsDocDescription *label; + const EcsDocDescription *brief; + const EcsDocDescription *detail; + const EcsDocDescription *color; + const EcsDocDescription *link; + bool has_alerts; +} ecs_json_this_data_t; + const char* flecs_json_parse( const char *json, ecs_json_token_t *token_kind, @@ -34,17 +63,34 @@ const char* flecs_json_parse_large_string( const char *json, ecs_strbuf_t *buf); +const char* flecs_json_parse_next_member( + const char *json, + char *token, + ecs_json_token_t *token_kind, + const ecs_from_json_desc_t *desc); + const char* flecs_json_expect( const char *json, ecs_json_token_t token_kind, char *token, const ecs_from_json_desc_t *desc); +const char* flecs_json_expect_string( + const char *json, + char *token, + char **out, + const ecs_from_json_desc_t *desc); + const char* flecs_json_expect_member( const char *json, char *token, const ecs_from_json_desc_t *desc); +const char* flecs_json_expect_next_member( + const char *json, + char *token, + const ecs_from_json_desc_t *desc); + const char* flecs_json_expect_member_name( const char *json, char *token, @@ -69,6 +115,10 @@ void flecs_json_number( ecs_strbuf_t *buf, double value); +void flecs_json_u32( + ecs_strbuf_t *buf, + uint32_t value); + void flecs_json_true( ecs_strbuf_t *buf); @@ -79,6 +129,9 @@ void flecs_json_bool( ecs_strbuf_t *buf, bool value); +void flecs_json_null( + ecs_strbuf_t *buf); + void flecs_json_array_push( ecs_strbuf_t *buf); @@ -121,6 +174,12 @@ void flecs_json_label( const ecs_world_t *world, ecs_entity_t e); +void flecs_json_path_or_label( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_entity_t e, + bool path); + void flecs_json_color( ecs_strbuf_t *buf, const ecs_world_t *world, @@ -131,7 +190,109 @@ void flecs_json_id( const ecs_world_t *world, ecs_id_t id); +void flecs_json_id_member( + ecs_strbuf_t *buf, + const ecs_world_t *world, + ecs_id_t id, + bool fullpath); + ecs_primitive_kind_t flecs_json_op_to_primitive_kind( ecs_meta_type_op_kind_t kind); +int flecs_json_serialize_iter_result( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + ecs_json_ser_ctx_t *ser_ctx); + +void flecs_json_serialize_field( + const ecs_world_t *world, + const ecs_iter_t *it, + const ecs_query_t *q, + int field, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ctx); + +void flecs_json_serialize_query( + const ecs_world_t *world, + const ecs_query_t *q, + ecs_strbuf_t *buf); + +int flecs_json_ser_type( + const ecs_world_t *world, + const ecs_vec_t *ser, + const void *base, + ecs_strbuf_t *str); + +int flecs_json_serialize_iter_result_fields( + const ecs_world_t *world, + const ecs_iter_t *it, + int32_t i, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + ecs_json_ser_ctx_t *ser_ctx); + +bool flecs_json_serialize_get_value_ctx( + const ecs_world_t *world, + ecs_id_t id, + ecs_json_value_ser_ctx_t *ctx, + const ecs_iter_to_json_desc_t *desc); + +int flecs_json_serialize_iter_result_table( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + int32_t count, + bool has_this, + const char *parent_path, + const ecs_json_this_data_t *this_data); + +int flecs_json_serialize_iter_result_query( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ser_ctx, + const ecs_iter_to_json_desc_t *desc, + int32_t count, + bool has_this, + const char *parent_path, + const ecs_json_this_data_t *this_data); + +void flecs_json_serialize_iter_this( + const ecs_iter_t *it, + const char *parent_path, + const ecs_json_this_data_t *this_data, + int32_t row, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc); + +bool flecs_json_serialize_vars( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc); + +int flecs_json_serialize_matches( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity); + +int flecs_json_serialize_refs( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity, + ecs_entity_t relationship); + +int flecs_json_serialize_alerts( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity); + +bool flecs_json_is_builtin( + ecs_id_t id); + #endif + +#endif /* FLECS_JSON_PRIVATE_H */ diff --git a/vendors/flecs/src/addons/json/serialize.c b/vendors/flecs/src/addons/json/serialize.c deleted file mode 100644 index 33e45b7ae..000000000 --- a/vendors/flecs/src/addons/json/serialize.c +++ /dev/null @@ -1,2319 +0,0 @@ -/** - * @file json/serialize.c - * @brief Serialize (component) values to JSON strings. - */ - -#include "json.h" - -#ifdef FLECS_JSON - -/* Cached id records during serialization */ -typedef struct ecs_json_ser_idr_t { - ecs_id_record_t *idr_doc_name; - ecs_id_record_t *idr_doc_color; -} ecs_json_ser_idr_t; - -static -int json_ser_type( - const ecs_world_t *world, - const ecs_vec_t *ser, - const void *base, - ecs_strbuf_t *str); - -static -int json_ser_type_ops( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - const void *base, - ecs_strbuf_t *str, - int32_t in_array); - -static -int json_ser_type_op( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *base, - ecs_strbuf_t *str); - -/* Serialize enumeration */ -static -int json_ser_enum( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *base, - ecs_strbuf_t *str) -{ - const EcsEnum *enum_type = ecs_get(world, op->type, EcsEnum); - ecs_check(enum_type != NULL, ECS_INVALID_PARAMETER, NULL); - - int32_t value = *(const int32_t*)base; - - /* Enumeration constants are stored in a map that is keyed on the - * enumeration value. */ - ecs_enum_constant_t *constant = ecs_map_get_deref(&enum_type->constants, - ecs_enum_constant_t, (ecs_map_key_t)value); - if (!constant) { - /* If the value is not found, it is not a valid enumeration constant */ - char *name = ecs_get_fullpath(world, op->type); - ecs_err("enumeration value '%d' of type '%s' is not a valid constant", - value, name); - ecs_os_free(name); - goto error; - } - - ecs_strbuf_appendch(str, '"'); - ecs_strbuf_appendstr(str, ecs_get_name(world, constant->constant)); - ecs_strbuf_appendch(str, '"'); - - return 0; -error: - return -1; -} - -/* Serialize bitmask */ -static -int json_ser_bitmask( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *ptr, - ecs_strbuf_t *str) -{ - const EcsBitmask *bitmask_type = ecs_get(world, op->type, EcsBitmask); - ecs_check(bitmask_type != NULL, ECS_INVALID_PARAMETER, NULL); - - uint32_t value = *(const uint32_t*)ptr; - if (!value) { - ecs_strbuf_appendch(str, '0'); - return 0; - } - - ecs_strbuf_list_push(str, "\"", "|"); - - /* Multiple flags can be set at a given time. Iterate through all the flags - * and append the ones that are set. */ - ecs_map_iter_t it = ecs_map_iter(&bitmask_type->constants); - while (ecs_map_next(&it)) { - ecs_bitmask_constant_t *constant = ecs_map_ptr(&it); - ecs_map_key_t key = ecs_map_key(&it); - if ((value & key) == key) { - ecs_strbuf_list_appendstr(str, - ecs_get_name(world, constant->constant)); - value -= (uint32_t)key; - } - } - - if (value != 0) { - /* All bits must have been matched by a constant */ - char *name = ecs_get_fullpath(world, op->type); - ecs_err("bitmask value '%u' of type '%s' contains invalid/unknown bits", - value, name); - ecs_os_free(name); - goto error; - } - - ecs_strbuf_list_pop(str, "\""); - - return 0; -error: - return -1; -} - -/* Serialize elements of a contiguous array */ -static -int json_ser_elements( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - const void *base, - int32_t elem_count, - int32_t elem_size, - ecs_strbuf_t *str, - bool is_array) -{ - flecs_json_array_push(str); - - const void *ptr = base; - - int i; - for (i = 0; i < elem_count; i ++) { - ecs_strbuf_list_next(str); - if (json_ser_type_ops(world, ops, op_count, ptr, str, is_array)) { - return -1; - } - ptr = ECS_OFFSET(ptr, elem_size); - } - - flecs_json_array_pop(str); - - return 0; -} - -static -int json_ser_type_elements( - const ecs_world_t *world, - ecs_entity_t type, - const void *base, - int32_t elem_count, - ecs_strbuf_t *str, - bool is_array) -{ - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); - - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_meta_type_op_t *ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t); - int32_t op_count = ecs_vec_count(&ser->ops); - - return json_ser_elements( - world, ops, op_count, base, elem_count, comp->size, str, is_array); -} - -/* Serialize array */ -static -int json_ser_array( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *ptr, - ecs_strbuf_t *str) -{ - const EcsArray *a = ecs_get(world, op->type, EcsArray); - ecs_assert(a != NULL, ECS_INTERNAL_ERROR, NULL); - - return json_ser_type_elements( - world, a->type, ptr, a->count, str, true); -} - -/* Serialize vector */ -static -int json_ser_vector( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *base, - ecs_strbuf_t *str) -{ - const ecs_vec_t *value = base; - const EcsVector *v = ecs_get(world, op->type, EcsVector); - ecs_assert(v != NULL, ECS_INTERNAL_ERROR, NULL); - - int32_t count = ecs_vec_count(value); - void *array = ecs_vec_first(value); - - /* Serialize contiguous buffer of vector */ - return json_ser_type_elements(world, v->type, array, count, str, false); -} - -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_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_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); - ecs_assert(ct->serialize != NULL, ECS_INVALID_OPERATION, - ecs_get_name(world, op->type)); - - 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_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( - const ecs_world_t *world, - ecs_meta_type_op_t *op, - const void *ptr, - ecs_strbuf_t *str) -{ - void *vptr = ECS_OFFSET(ptr, op->offset); - bool large_int = false; - if (op->kind == EcsOpI64) { - if (*(int64_t*)vptr >= 2147483648) { - large_int = true; - } - } else if (op->kind == EcsOpU64) { - if (*(uint64_t*)vptr >= 2147483648) { - large_int = true; - } - } - - if (large_int) { - ecs_strbuf_appendch(str, '"'); - } - - switch(op->kind) { - case EcsOpPush: - case EcsOpPop: - /* Should not be parsed as single op */ - ecs_throw(ECS_INVALID_PARAMETER, NULL); - break; - case EcsOpF32: - ecs_strbuf_appendflt(str, - (ecs_f64_t)*(const ecs_f32_t*)vptr, '"'); - break; - case EcsOpF64: - ecs_strbuf_appendflt(str, - *(ecs_f64_t*)vptr, '"'); - break; - case EcsOpEnum: - if (json_ser_enum(world, op, vptr, str)) { - goto error; - } - break; - case EcsOpBitmask: - if (json_ser_bitmask(world, op, vptr, str)) { - goto error; - } - break; - case EcsOpArray: - if (json_ser_array(world, op, vptr, str)) { - goto error; - } - break; - case EcsOpVector: - if (json_ser_vector(world, op, vptr, str)) { - goto error; - } - break; - case EcsOpOpaque: - if (json_ser_custom_type(world, op, vptr, str)) { - goto error; - } - break; - case EcsOpEntity: { - ecs_entity_t e = *(const ecs_entity_t*)vptr; - if (!e) { - ecs_strbuf_appendch(str, '0'); - } else { - flecs_json_path(str, world, e); - } - break; - } - case EcsOpId: { - ecs_id_t id = *(const ecs_id_t*)vptr; - if (!id) { - ecs_strbuf_appendch(str, '0'); - } else { - flecs_json_id(str, world, id); - } - break; - } - - case EcsOpU64: - case EcsOpI64: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpString: - if (ecs_primitive_to_expr_buf(world, - flecs_json_op_to_primitive_kind(op->kind), - ECS_OFFSET(ptr, op->offset), str)) - { - ecs_throw(ECS_INTERNAL_ERROR, NULL); - } - break; - - case EcsOpPrimitive: - case EcsOpScope: - default: - ecs_throw(ECS_INTERNAL_ERROR, NULL); - } - - if (large_int) { - ecs_strbuf_appendch(str, '"'); - } - - return 0; -error: - return -1; -} - -/* Iterate over a slice of the type ops array */ -static -int json_ser_type_ops( - const ecs_world_t *world, - ecs_meta_type_op_t *ops, - int32_t op_count, - const void *base, - ecs_strbuf_t *str, - int32_t in_array) -{ - for (int i = 0; i < op_count; i ++) { - ecs_meta_type_op_t *op = &ops[i]; - - if (in_array <= 0) { - if (op->name) { - flecs_json_member(str, op->name); - } - - int32_t elem_count = op->count; - if (elem_count > 1) { - /* Serialize inline array */ - if (json_ser_elements(world, op, op->op_count, base, - elem_count, op->size, str, true)) - { - return -1; - } - - i += op->op_count - 1; - continue; - } - } - - switch(op->kind) { - case EcsOpPush: - flecs_json_object_push(str); - in_array --; - break; - case EcsOpPop: - flecs_json_object_pop(str); - in_array ++; - break; - case EcsOpArray: - case EcsOpVector: - case EcsOpScope: - case EcsOpEnum: - case EcsOpBitmask: - case EcsOpPrimitive: - case EcsOpBool: - case EcsOpChar: - case EcsOpByte: - case EcsOpU8: - case EcsOpU16: - case EcsOpU32: - case EcsOpU64: - case EcsOpI8: - case EcsOpI16: - case EcsOpI32: - case EcsOpI64: - case EcsOpF32: - case EcsOpF64: - case EcsOpUPtr: - case EcsOpIPtr: - case EcsOpEntity: - case EcsOpId: - case EcsOpString: - case EcsOpOpaque: - if (json_ser_type_op(world, op, base, str)) { - goto error; - } - break; - default: - ecs_throw(ECS_INTERNAL_ERROR, NULL); - } - } - - return 0; -error: - return -1; -} - -/* Iterate over the type ops of a type */ -static -int json_ser_type( - const ecs_world_t *world, - const ecs_vec_t *v_ops, - const void *base, - ecs_strbuf_t *str) -{ - ecs_meta_type_op_t *ops = ecs_vec_first_t(v_ops, ecs_meta_type_op_t); - int32_t count = ecs_vec_count(v_ops); - return json_ser_type_ops(world, ops, count, base, str, 0); -} - -static -int array_to_json_buf_w_type_data( - const ecs_world_t *world, - const void *ptr, - int32_t count, - ecs_strbuf_t *buf, - const EcsComponent *comp, - const EcsMetaTypeSerialized *ser) -{ - if (count) { - ecs_size_t size = comp->size; - - flecs_json_array_push(buf); - - do { - ecs_strbuf_list_next(buf); - if (json_ser_type(world, &ser->ops, ptr, buf)) { - return -1; - } - - ptr = ECS_OFFSET(ptr, size); - } while (-- count); - - flecs_json_array_pop(buf); - } else { - if (json_ser_type(world, &ser->ops, ptr, buf)) { - return -1; - } - } - - return 0; -} - -int ecs_array_to_json_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *ptr, - int32_t count, - ecs_strbuf_t *buf) -{ - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - if (!comp) { - char *path = ecs_get_fullpath(world, type); - ecs_err("cannot serialize to JSON, '%s' is not a component", path); - ecs_os_free(path); - return -1; - } - - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - if (!ser) { - char *path = ecs_get_fullpath(world, type); - ecs_err("cannot serialize to JSON, '%s' has no reflection data", path); - ecs_os_free(path); - return -1; - } - - return array_to_json_buf_w_type_data(world, ptr, count, buf, comp, ser); -} - -char* ecs_array_to_json( - const ecs_world_t *world, - ecs_entity_t type, - const void* ptr, - int32_t count) -{ - ecs_strbuf_t str = ECS_STRBUF_INIT; - - if (ecs_array_to_json_buf(world, type, ptr, count, &str) != 0) { - ecs_strbuf_reset(&str); - return NULL; - } - - return ecs_strbuf_get(&str); -} - -int ecs_ptr_to_json_buf( - const ecs_world_t *world, - ecs_entity_t type, - const void *ptr, - ecs_strbuf_t *buf) -{ - return ecs_array_to_json_buf(world, type, ptr, 0, buf); -} - -char* ecs_ptr_to_json( - const ecs_world_t *world, - ecs_entity_t type, - const void* ptr) -{ - return ecs_array_to_json(world, type, ptr, 0); -} - -static -bool flecs_json_skip_id( - const ecs_world_t *world, - ecs_id_t id, - const ecs_entity_to_json_desc_t *desc, - ecs_entity_t ent, - ecs_entity_t inst, - ecs_entity_t *pred_out, - ecs_entity_t *obj_out, - ecs_entity_t *role_out, - bool *hidden_out) -{ - bool is_base = ent != inst; - ecs_entity_t pred = 0, obj = 0, role = 0; - bool hidden = false; - - if (ECS_HAS_ID_FLAG(id, PAIR)) { - pred = ecs_pair_first(world, id); - obj = ecs_pair_second(world, id); - } else { - pred = id & ECS_COMPONENT_MASK; - if (id & ECS_ID_FLAGS_MASK) { - role = id & ECS_ID_FLAGS_MASK; - } - } - - if (is_base) { - if (ecs_has_id(world, pred, EcsDontInherit)) { - return true; - } - } - if (!desc || !desc->serialize_private) { - if (ecs_has_id(world, pred, EcsPrivate)) { - return true; - } - } - if (is_base) { - if (ecs_get_target_for_id(world, inst, EcsIsA, id) != ent) { - hidden = true; - } - } - if (hidden && (!desc || !desc->serialize_hidden)) { - return true; - } - - *pred_out = pred; - *obj_out = obj; - *role_out = role; - if (hidden_out) *hidden_out = hidden; - - return false; -} - -static -int flecs_json_append_type_labels( - const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_id_t *ids, - int32_t count, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - (void)world; (void)buf; (void)ids; (void)count; (void)ent; (void)inst; - (void)desc; - -#ifdef FLECS_DOC - if (!desc || !desc->serialize_id_labels) { - return 0; - } - - flecs_json_memberl(buf, "id_labels"); - flecs_json_array_push(buf); - - int32_t i; - for (i = 0; i < count; i ++) { - ecs_entity_t pred = 0, obj = 0, role = 0; - if (flecs_json_skip_id(world, ids[i], desc, ent, inst, &pred, &obj, &role, 0)) { - continue; - } - - if (obj && (pred == EcsUnion)) { - pred = obj; - obj = ecs_get_target(world, ent, pred, 0); - if (!ecs_is_alive(world, obj)) { - /* Union relationships aren't automatically cleaned up, so they - * can contain invalid entity ids. Don't serialize value until - * relationship is valid again. */ - continue; - } - } - - if (desc && desc->serialize_id_labels) { - flecs_json_next(buf); - - flecs_json_array_push(buf); - flecs_json_next(buf); - flecs_json_label(buf, world, pred); - if (obj) { - flecs_json_next(buf); - flecs_json_label(buf, world, obj); - } - - flecs_json_array_pop(buf); - } - } - - flecs_json_array_pop(buf); -#endif - return 0; -} - -static -int flecs_json_append_type_values( - const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_id_t *ids, - int32_t count, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - if (!desc || !desc->serialize_values) { - return 0; - } - - flecs_json_memberl(buf, "values"); - flecs_json_array_push(buf); - - int32_t i; - for (i = 0; i < count; i ++) { - bool hidden; - ecs_entity_t pred = 0, obj = 0, role = 0; - ecs_id_t id = ids[i]; - if (flecs_json_skip_id(world, id, desc, ent, inst, &pred, &obj, &role, - &hidden)) - { - continue; - } - - if (!hidden) { - bool serialized = false; - ecs_entity_t typeid = ecs_get_typeid(world, id); - if (typeid) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, typeid, EcsMetaTypeSerialized); - if (ser) { - const void *ptr = ecs_get_id(world, ent, id); - ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); - - flecs_json_next(buf); - if (json_ser_type(world, &ser->ops, ptr, buf) != 0) { - /* Entity contains invalid value */ - return -1; - } - serialized = true; - } - } - if (!serialized) { - flecs_json_next(buf); - flecs_json_number(buf, 0); - } - } else { - if (!desc || desc->serialize_hidden) { - flecs_json_next(buf); - flecs_json_number(buf, 0); - } - } - } - - flecs_json_array_pop(buf); - - return 0; -} - -static -int flecs_json_append_type_info( - const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_id_t *ids, - int32_t count, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - if (!desc || !desc->serialize_type_info) { - return 0; - } - - flecs_json_memberl(buf, "type_info"); - flecs_json_array_push(buf); - - int32_t i; - for (i = 0; i < count; i ++) { - bool hidden; - ecs_entity_t pred = 0, obj = 0, role = 0; - ecs_id_t id = ids[i]; - if (flecs_json_skip_id(world, id, desc, ent, inst, &pred, &obj, &role, - &hidden)) - { - continue; - } - - if (!hidden) { - ecs_entity_t typeid = ecs_get_typeid(world, id); - if (typeid) { - flecs_json_next(buf); - if (ecs_type_info_to_json_buf(world, typeid, buf) != 0) { - return -1; - } - } else { - flecs_json_next(buf); - flecs_json_number(buf, 0); - } - } else { - if (!desc || desc->serialize_hidden) { - flecs_json_next(buf); - flecs_json_number(buf, 0); - } - } - } - - flecs_json_array_pop(buf); - - return 0; -} - -static -int flecs_json_append_type_hidden( - const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_id_t *ids, - int32_t count, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - if (!desc || !desc->serialize_hidden) { - return 0; - } - - if (ent == inst) { - return 0; /* if this is not a base, components are never hidden */ - } - - flecs_json_memberl(buf, "hidden"); - flecs_json_array_push(buf); - - int32_t i; - for (i = 0; i < count; i ++) { - bool hidden; - ecs_entity_t pred = 0, obj = 0, role = 0; - ecs_id_t id = ids[i]; - if (flecs_json_skip_id(world, id, desc, ent, inst, &pred, &obj, &role, - &hidden)) - { - continue; - } - - flecs_json_next(buf); - flecs_json_bool(buf, hidden); - } - - flecs_json_array_pop(buf); - - return 0; -} - -static -int flecs_json_append_type( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - const ecs_id_t *ids = NULL; - int32_t i, count = 0; - - const ecs_type_t *type = ecs_get_type(world, ent); - if (type) { - ids = type->array; - count = type->count; - } - - if (!desc || desc->serialize_ids) { - flecs_json_memberl(buf, "ids"); - flecs_json_array_push(buf); - - for (i = 0; i < count; i ++) { - ecs_entity_t pred = 0, obj = 0, role = 0; - if (flecs_json_skip_id(world, ids[i], desc, ent, inst, &pred, &obj, &role, 0)) { - continue; - } - - if (obj && (pred == EcsUnion)) { - pred = obj; - obj = ecs_get_target(world, ent, pred, 0); - if (!ecs_is_alive(world, obj)) { - /* Union relationships aren't automatically cleaned up, so they - * can contain invalid entity ids. Don't serialize value until - * relationship is valid again. */ - continue; - } - } - - flecs_json_next(buf); - flecs_json_array_push(buf); - flecs_json_next(buf); - flecs_json_path(buf, world, pred); - if (obj || role) { - flecs_json_next(buf); - if (obj) { - flecs_json_path(buf, world, obj); - } else { - flecs_json_number(buf, 0); - } - if (role) { - flecs_json_next(buf); - flecs_json_string(buf, ecs_id_flag_str(role)); - } - } - flecs_json_array_pop(buf); - } - flecs_json_array_pop(buf); - } - - if (flecs_json_append_type_labels(world, buf, ids, count, ent, inst, desc)) { - return -1; - } - - if (flecs_json_append_type_values(world, buf, ids, count, ent, inst, desc)) { - return -1; - } - - if (flecs_json_append_type_info(world, buf, ids, count, ent, inst, desc)) { - return -1; - } - - if (flecs_json_append_type_hidden(world, buf, ids, count, ent, inst, desc)) { - return -1; - } - - return 0; -} - -static -int flecs_json_append_base( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t ent, - ecs_entity_t inst, - const ecs_entity_to_json_desc_t *desc) -{ - const ecs_type_t *type = ecs_get_type(world, ent); - ecs_id_t *ids = NULL; - int32_t i, count = 0; - if (type) { - ids = type->array; - count = type->count; - } - - for (i = 0; i < count; i ++) { - ecs_id_t id = ids[i]; - if (ECS_HAS_RELATION(id, EcsIsA)) { - if (flecs_json_append_base(world, buf, ecs_pair_second(world, id), inst, desc)) - { - return -1; - } - } - } - - ecs_strbuf_list_next(buf); - flecs_json_object_push(buf); - flecs_json_memberl(buf, "path"); - flecs_json_path(buf, world, ent); - - if (flecs_json_append_type(world, buf, ent, inst, desc)) { - return -1; - } - - flecs_json_object_pop(buf); - - return 0; -} - -#ifdef FLECS_ALERTS -static -int flecs_json_serialize_entity_alerts( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity, - const EcsAlertsActive *alerts, - bool self) -{ - ecs_assert(alerts != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_map_iter_t it = ecs_map_iter(&alerts->alerts); - while (ecs_map_next(&it)) { - flecs_json_next(buf); - flecs_json_object_push(buf); - ecs_entity_t ai = ecs_map_value(&it); - char *alert_name = ecs_get_fullpath(world, ai); - flecs_json_memberl(buf, "alert"); - flecs_json_string(buf, alert_name); - ecs_os_free(alert_name); - - ecs_entity_t severity_id = ecs_get_target( - world, ai, ecs_id(EcsAlert), 0); - const char *severity = ecs_get_name(world, severity_id); - - const EcsAlertInstance *alert = ecs_get( - world, ai, EcsAlertInstance); - if (alert) { - if (alert->message) { - flecs_json_memberl(buf, "message"); - flecs_json_string(buf, alert->message); - } - flecs_json_memberl(buf, "severity"); - flecs_json_string(buf, severity); - - if (!self) { - char *path = ecs_get_fullpath(world, entity); - flecs_json_memberl(buf, "path"); - flecs_json_string(buf, path); - ecs_os_free(path); - } - } - flecs_json_object_pop(buf); - } - - return 0; -} - -static -int flecs_json_serialize_children_alerts( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity) -{ - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter(ECS_CONST_CAST(ecs_world_t*, world), { - .storage = &f, - .terms = {{ .id = ecs_pair(EcsChildOf, entity) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - while (ecs_filter_next(&it)) { - EcsAlertsActive *alerts = ecs_table_get_id( - world, it.table, ecs_id(EcsAlertsActive), it.offset); - - int32_t i; - for (i = 0; i < it.count; i ++) { - ecs_entity_t child = it.entities[i]; - if (alerts) { - if (flecs_json_serialize_entity_alerts( - world, buf, child, &alerts[i], false)) - { - goto error; - } - } - - ecs_record_t *r = flecs_entities_get(world, it.entities[i]); - if (r->row & EcsEntityIsTraversable) { - if (flecs_json_serialize_children_alerts( - world, buf, child)) - { - goto error; - } - } - } - } - - ecs_filter_fini(&f); - - return 0; -error: - return -1; -} -#endif - -static -int flecs_json_serialize_alerts( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity) -{ - (void)world; - (void)buf; - (void)entity; - -#ifdef FLECS_ALERTS - if (!ecs_id(EcsAlertsActive)) { - return 0; /* Alert module not imported */ - } - - flecs_json_memberl(buf, "alerts"); - flecs_json_array_push(buf); - const EcsAlertsActive *alerts = ecs_get(world, entity, EcsAlertsActive); - if (alerts) { - flecs_json_serialize_entity_alerts(world, buf, entity, alerts, true); - } - flecs_json_serialize_children_alerts(world, buf, entity); - flecs_json_array_pop(buf); -#endif - return 0; -} - -static -int flecs_json_serialize_refs_idr( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_id_record_t *idr) -{ - char *id_str = ecs_id_str(world, ecs_pair_first(world, idr->id)); - - flecs_json_member(buf, id_str); - ecs_os_free(id_str); - - flecs_json_array_push(buf); - - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_all_iter((ecs_table_cache_t*)idr, &it)) { - const ecs_table_record_t *tr; - 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); - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); - for (i = 0; i < count; i ++) { - ecs_entity_t e = entities[i]; - flecs_json_next(buf); - flecs_json_path(buf, world, e); - } - } - } - - flecs_json_array_pop(buf); - - return 0; -} - -static -int flecs_json_serialize_refs( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity, - ecs_entity_t relationship) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_pair(relationship, entity)); - - if (idr) { - if (relationship == EcsWildcard) { - ecs_id_record_t *cur = idr; - while ((cur = cur->second.next)) { - flecs_json_serialize_refs_idr(world, buf, cur); - } - } else { - flecs_json_serialize_refs_idr(world, buf, idr); - } - } - - return 0; -} - -static -int flecs_json_serialize_matches( - const ecs_world_t *world, - ecs_strbuf_t *buf, - ecs_entity_t entity) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_pair_t(EcsPoly, EcsQuery)); - - if (idr) { - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) { - const ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; - EcsPoly *queries = ecs_table_get_column(table, tr->column, 0); - - int32_t i, count = ecs_table_count(table); - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); - for (i = 0; i < count; i ++) { - ecs_poly_t *q = queries[i].poly; - ecs_iter_t qit; - ecs_iter_poly(world, q, &qit, NULL); - if (!qit.variables) { - ecs_iter_fini(&qit); - continue; - } - ecs_iter_set_var(&qit, 0, entity); - if (ecs_iter_is_true(&qit)) { - flecs_json_next(buf); - flecs_json_path(buf, world, entities[i]); - } - } - } - } - } - - return 0; -} - -int ecs_entity_to_json_buf( - const ecs_world_t *world, - ecs_entity_t entity, - ecs_strbuf_t *buf, - const ecs_entity_to_json_desc_t *desc) -{ - if (!entity || !ecs_is_valid(world, entity)) { - return -1; - } - - flecs_json_object_push(buf); - - if (!desc || desc->serialize_path) { - flecs_json_memberl(buf, "path"); - flecs_json_path(buf, world, entity); - } - -#ifdef FLECS_DOC - if (desc && desc->serialize_label) { - flecs_json_memberl(buf, "label"); - const char *doc_name = ecs_doc_get_name(world, entity); - if (doc_name) { - flecs_json_string_escape(buf, doc_name); - } else { - char num_buf[20]; - ecs_os_sprintf(num_buf, "%u", (uint32_t)entity); - flecs_json_string(buf, num_buf); - } - } - - if (desc && desc->serialize_brief) { - const char *doc_brief = ecs_doc_get_brief(world, entity); - if (doc_brief) { - flecs_json_memberl(buf, "brief"); - flecs_json_string_escape(buf, doc_brief); - } - } - - if (desc && desc->serialize_link) { - const char *doc_link = ecs_doc_get_link(world, entity); - if (doc_link) { - flecs_json_memberl(buf, "link"); - flecs_json_string_escape(buf, doc_link); - } - } - - if (desc && desc->serialize_color) { - const char *doc_color = ecs_doc_get_color(world, entity); - if (doc_color) { - flecs_json_memberl(buf, "color"); - flecs_json_string_escape(buf, doc_color); - } - } -#endif - - const ecs_type_t *type = ecs_get_type(world, entity); - ecs_id_t *ids = NULL; - int32_t i, count = 0; - if (type) { - ids = type->array; - count = type->count; - } - - if (!desc || desc->serialize_base) { - if (ecs_has_pair(world, entity, EcsIsA, EcsWildcard)) { - flecs_json_memberl(buf, "is_a"); - flecs_json_array_push(buf); - - for (i = 0; i < count; i ++) { - ecs_id_t id = ids[i]; - if (ECS_HAS_RELATION(id, EcsIsA)) { - if (flecs_json_append_base( - world, buf, ecs_pair_second(world, id), entity, desc)) - { - return -1; - } - } - } - - flecs_json_array_pop(buf); - } - } - - if (flecs_json_append_type(world, buf, entity, entity, desc)) { - goto error; - } - - if (desc && desc->serialize_alerts) { - if (flecs_json_serialize_alerts(world, buf, entity)) { - goto error; - } - } - - if (desc && desc->serialize_refs) { - flecs_json_memberl(buf, "refs"); - flecs_json_object_push(buf); - if (flecs_json_serialize_refs(world, buf, entity, desc->serialize_refs)) { - goto error; - } - flecs_json_object_pop(buf); - } - - if (desc && desc->serialize_matches) { - flecs_json_memberl(buf, "matches"); - flecs_json_array_push(buf); - if (flecs_json_serialize_matches(world, buf, entity)) { - goto error; - } - flecs_json_array_pop(buf); - } - - flecs_json_object_pop(buf); - - return 0; -error: - return -1; -} - -char* ecs_entity_to_json( - const ecs_world_t *world, - ecs_entity_t entity, - const ecs_entity_to_json_desc_t *desc) -{ - ecs_strbuf_t buf = ECS_STRBUF_INIT; - - if (ecs_entity_to_json_buf(world, entity, &buf, desc) != 0) { - ecs_strbuf_reset(&buf); - return NULL; - } - - return ecs_strbuf_get(&buf); -} - -static -bool flecs_json_skip_variable( - const char *name) -{ - if (!name || name[0] == '_' || !ecs_os_strcmp(name, "this")) { - return true; - } else { - return false; - } -} - -static -void flecs_json_serialize_id( - const ecs_world_t *world, - ecs_id_t id, - ecs_strbuf_t *buf) -{ - flecs_json_id(buf, world, id); -} - -static -void flecs_json_serialize_id_label( - const ecs_world_t *world, - ecs_id_t id, - ecs_strbuf_t *buf) -{ - ecs_entity_t pred = id, obj = 0; - if (ECS_IS_PAIR(id)) { - pred = ecs_pair_first(world, id); - obj = ecs_pair_second(world, id); - } - - flecs_json_array_push(buf); - flecs_json_next(buf); - flecs_json_label(buf, world, pred); - if (obj) { - flecs_json_next(buf); - flecs_json_label(buf, world, obj); - } - flecs_json_array_pop(buf); -} - -static -void flecs_json_serialize_iter_ids( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - int32_t field_count = it->field_count; - if (!field_count) { - return; - } - - flecs_json_memberl(buf, "ids"); - flecs_json_array_push(buf); - - for (int i = 0; i < field_count; i ++) { - flecs_json_next(buf); - flecs_json_serialize_id(world, it->terms[i].id, buf); - } - - flecs_json_array_pop(buf); -} - -static -void flecs_json_serialize_iter_id_labels( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - int32_t field_count = it->field_count; - if (!field_count) { - return; - } - - flecs_json_memberl(buf, "id_labels"); - flecs_json_array_push(buf); - - for (int i = 0; i < field_count; i ++) { - flecs_json_next(buf); - flecs_json_serialize_id_label(world, it->terms[i].id, buf); - } - - flecs_json_array_pop(buf); -} - -static -void flecs_json_serialize_id_str( - const ecs_world_t *world, - ecs_id_t id, - ecs_strbuf_t *buf) -{ - ecs_strbuf_appendch(buf, '"'); - if (ECS_IS_PAIR(id)) { - ecs_entity_t first = ecs_pair_first(world, id); - ecs_entity_t second = ecs_pair_first(world, id); - ecs_strbuf_appendch(buf, '('); - ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf); - ecs_strbuf_appendch(buf, ','); - ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf); - ecs_strbuf_appendch(buf, ')'); - } else { - ecs_get_path_w_sep_buf( - world, 0, id & ECS_COMPONENT_MASK, ".", "", buf); - } - ecs_strbuf_appendch(buf, '"'); -} - -static -void flecs_json_serialize_type_info( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - int32_t field_count = it->field_count; - if (!field_count) { - return; - } - - if (it->flags & EcsIterNoData) { - return; - } - - flecs_json_memberl(buf, "type_info"); - flecs_json_object_push(buf); - - for (int i = 0; i < field_count; i ++) { - flecs_json_next(buf); - ecs_entity_t typeid = 0; - if (it->terms[i].inout != EcsInOutNone) { - typeid = ecs_get_typeid(world, it->terms[i].id); - } - if (typeid) { - flecs_json_serialize_id_str(world, typeid, buf); - ecs_strbuf_appendch(buf, ':'); - ecs_type_info_to_json_buf(world, typeid, buf); - } else { - flecs_json_serialize_id_str(world, it->terms[i].id, buf); - ecs_strbuf_appendlit(buf, ":0"); - } - } - - flecs_json_object_pop(buf); -} - -static -void flecs_json_serialize_iter_variables(ecs_iter_t *it, ecs_strbuf_t *buf) { - char **variable_names = it->variable_names; - int32_t var_count = it->variable_count; - int32_t actual_count = 0; - - for (int i = 1; i < var_count; i ++) { - const char *var_name = variable_names[i]; - if (flecs_json_skip_variable(var_name)) continue; - - if (!actual_count) { - flecs_json_memberl(buf, "vars"); - flecs_json_array_push(buf); - actual_count ++; - } - - ecs_strbuf_list_next(buf); - flecs_json_string(buf, var_name); - } - - if (actual_count) { - flecs_json_array_pop(buf); - } -} - -static -void flecs_json_serialize_iter_result_ids( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - flecs_json_memberl(buf, "ids"); - flecs_json_array_push(buf); - - 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_array_pop(buf); -} - -static -void flecs_json_serialize_iter_result_id_labels( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - flecs_json_memberl(buf, "id_labels"); - flecs_json_array_push(buf); - - for (int i = 0; i < it->field_count; i ++) { - flecs_json_next(buf); - flecs_json_serialize_id_label(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, - const ecs_iter_to_json_desc_t *desc) -{ - if (!it->table) { - return; - } - - if (desc->serialize_ids) { - 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 ++) { - ecs_id_t id = type->array[i]; - if (!desc->serialize_private) { - ecs_entity_t e = id; - if (ECS_IS_PAIR(id)) { - e = ecs_pair_first(world, id); - } - if (ecs_owns_id(world, e, EcsPrivate)) { - continue; - } - } - flecs_json_next(buf); - flecs_json_serialize_id(world, id, buf); - } - - flecs_json_array_pop(buf); - } - if (desc->serialize_id_labels) { - flecs_json_memberl(buf, "id_labels"); - flecs_json_array_push(buf); - - ecs_type_t *type = &it->table->type; - for (int i = 0; i < type->count; i ++) { - ecs_id_t id = type->array[i]; - if (!desc->serialize_private) { - ecs_entity_t e = id; - if (ECS_IS_PAIR(id)) { - e = ecs_pair_first(world, id); - } - if (ecs_owns_id(world, e, EcsPrivate)) { - continue; - } - } - flecs_json_next(buf); - flecs_json_serialize_id_label(world, id, buf); - } - - flecs_json_array_pop(buf); - } -} - -static -void flecs_json_serialize_iter_result_sources( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - flecs_json_memberl(buf, "sources"); - flecs_json_array_push(buf); - - for (int i = 0; i < it->field_count; i ++) { - flecs_json_next(buf); - ecs_entity_t subj = it->sources[i]; - if (subj) { - flecs_json_path(buf, world, subj); - } else { - ecs_strbuf_appendch(buf, '0'); - } - } - - flecs_json_array_pop(buf); -} - -static -void flecs_json_serialize_iter_result_is_set( - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - if (!(it->flags & EcsIterHasCondSet)) { - return; - } - - flecs_json_memberl(buf, "is_set"); - flecs_json_array_push(buf); - - for (int i = 0; i < it->field_count; i ++) { - ecs_strbuf_list_next(buf); - if (ecs_field_is_set(it, i + 1)) { - flecs_json_true(buf); - } else { - flecs_json_false(buf); - } - } - - flecs_json_array_pop(buf); -} - -static -void flecs_json_serialize_iter_result_variables( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - char **variable_names = it->variable_names; - ecs_var_t *variables = it->variables; - int32_t var_count = it->variable_count; - int32_t actual_count = 0; - - for (int i = 1; i < var_count; i ++) { - const char *var_name = variable_names[i]; - if (flecs_json_skip_variable(var_name)) continue; - - if (!actual_count) { - flecs_json_memberl(buf, "vars"); - flecs_json_array_push(buf); - actual_count ++; - } - - ecs_strbuf_list_next(buf); - flecs_json_path(buf, world, variables[i].entity); - } - - if (actual_count) { - flecs_json_array_pop(buf); - } -} - -static -void flecs_json_serialize_iter_result_variable_labels( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - char **variable_names = it->variable_names; - ecs_var_t *variables = it->variables; - int32_t var_count = it->variable_count; - int32_t actual_count = 0; - - for (int i = 1; i < var_count; i ++) { - const char *var_name = variable_names[i]; - if (flecs_json_skip_variable(var_name)) continue; - - if (!actual_count) { - flecs_json_memberl(buf, "var_labels"); - flecs_json_array_push(buf); - actual_count ++; - } - - ecs_strbuf_list_next(buf); - flecs_json_label(buf, world, variables[i].entity); - } - - if (actual_count) { - flecs_json_array_pop(buf); - } -} - -static -void flecs_json_serialize_iter_result_variable_ids( - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - char **variable_names = it->variable_names; - ecs_var_t *variables = it->variables; - int32_t var_count = it->variable_count; - int32_t actual_count = 0; - - for (int i = 1; i < var_count; i ++) { - const char *var_name = variable_names[i]; - if (flecs_json_skip_variable(var_name)) continue; - - if (!actual_count) { - flecs_json_memberl(buf, "var_ids"); - flecs_json_array_push(buf); - actual_count ++; - } - - ecs_strbuf_list_next(buf); - flecs_json_number(buf, (double)variables[i].entity); - } - - if (actual_count) { - flecs_json_array_pop(buf); - } -} - -static -bool flecs_json_serialize_iter_result_entity_names( - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - ecs_assert(it->count != 0, ECS_INTERNAL_ERROR, NULL); - - EcsIdentifier *names = ecs_table_get_id(it->world, it->table, - ecs_pair(ecs_id(EcsIdentifier), EcsName), it->offset); - if (!names) { - return false; - } - - int i; - for (i = 0; i < it->count; i ++) { - flecs_json_next(buf); - flecs_json_string(buf, names[i].value); - } - - return true; -} - -static -void flecs_json_serialize_iter_result_entity_ids( - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - if (!it->count) { - return; - } - - flecs_json_memberl(buf, "entity_ids"); - flecs_json_array_push(buf); - - ecs_entity_t *entities = it->entities; - - int i, count = it->count; - for (i = 0; i < count; i ++) { - flecs_json_next(buf); - flecs_json_number(buf, (double)(uint32_t)entities[i]); - } - - flecs_json_array_pop(buf); -} - -static -void flecs_json_serialize_iter_result_parent( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - ecs_table_t *table = it->table; - if (!(table->flags & EcsTableHasChildOf)) { - return; - } - - ecs_table_record_t *tr = flecs_id_record_get_table( - world->idr_childof_wildcard, it->table); - if (tr == NULL) { - return; - } - - ecs_id_t id = table->type.array[tr->index]; - ecs_entity_t parent = ecs_pair_second(world, id); - char *path = ecs_get_fullpath(world, parent); - flecs_json_memberl(buf, "parent"); - flecs_json_string(buf, path); - ecs_os_free(path); -} - -static -void flecs_json_serialize_iter_result_entities( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - if (!it->count) { - return; - } - - flecs_json_serialize_iter_result_parent(world, it, buf); - - flecs_json_memberl(buf, "entities"); - flecs_json_array_push(buf); - - if (!flecs_json_serialize_iter_result_entity_names(it, buf)) { - ecs_entity_t *entities = it->entities; - - int i, count = it->count; - for (i = 0; i < count; i ++) { - flecs_json_next(buf); - flecs_json_number(buf, (double)(uint32_t)entities[i]); - } - } - - flecs_json_array_pop(buf); -} - -static -void flecs_json_serialize_iter_result_entity_labels( - const ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_json_ser_idr_t *ser_idr) -{ - (void)buf; - (void)ser_idr; - if (!it->count) { - return; - } - - if (!ser_idr->idr_doc_name) { - return; - } - -#ifdef FLECS_DOC - ecs_table_t *table = it->table; - ecs_table_record_t *tr = flecs_id_record_get_table( - ser_idr->idr_doc_name, table); - if (tr == NULL) { - return; - } - - EcsDocDescription *labels = ecs_table_get_column( - table, tr->column, it->offset); - ecs_assert(labels != NULL, ECS_INTERNAL_ERROR, NULL); - - flecs_json_memberl(buf, "entity_labels"); - flecs_json_array_push(buf); - - int i; - for (i = 0; i < it->count; i ++) { - flecs_json_next(buf); - flecs_json_string(buf, labels[i].value); - } - - flecs_json_array_pop(buf); -#endif -} - -static -void flecs_json_serialize_iter_result_colors( - const ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_json_ser_idr_t *ser_idr) -{ - (void)buf; - (void)ser_idr; - - if (!it->count) { - return; - } - -#ifdef FLECS_DOC - if (!ser_idr->idr_doc_color) { - return; - } - - ecs_table_record_t *tr = flecs_id_record_get_table( - ser_idr->idr_doc_color, it->table); - if (tr == NULL) { - return; - } - - EcsDocDescription *colors = ecs_table_get_column( - it->table, tr->column, it->offset); - ecs_assert(colors != NULL, ECS_INTERNAL_ERROR, NULL); - - flecs_json_memberl(buf, "colors"); - flecs_json_array_push(buf); - - int i; - for (i = 0; i < it->count; i ++) { - flecs_json_next(buf); - flecs_json_string(buf, colors[i].value); - } - - flecs_json_array_pop(buf); -#endif -} - -static -int flecs_json_serialize_iter_result_values( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf) -{ - if (!it->ptrs || (it->flags & EcsIterNoData)) { - return 0; - } - - flecs_json_memberl(buf, "values"); - flecs_json_array_push(buf); - - int32_t i, term_count = it->field_count; - for (i = 0; i < term_count; i ++) { - ecs_strbuf_list_next(buf); - - const void *ptr = NULL; - if (it->ptrs) { - ptr = it->ptrs[i]; - } - - if (!ptr) { - /* No data in column. Append 0 if this is not an optional term */ - if (ecs_field_is_set(it, i + 1)) { - ecs_strbuf_appendch(buf, '0'); - continue; - } - } - - if (ecs_field_is_writeonly(it, i + 1)) { - ecs_strbuf_appendch(buf, '0'); - continue; - } - - /* Get component id (can be different in case of pairs) */ - ecs_entity_t type = ecs_get_typeid(world, it->ids[i]); - if (!type) { - /* Odd, we have a ptr but no Component? Not the place of the - * serializer to complain about that. */ - ecs_strbuf_appendch(buf, '0'); - continue; - } - - const EcsComponent *comp = ecs_get(world, type, EcsComponent); - if (!comp) { - /* Also odd, typeid but not a component? */ - ecs_strbuf_appendch(buf, '0'); - continue; - } - - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); - if (!ser) { - /* Not odd, component just has no reflection data */ - ecs_strbuf_appendch(buf, '0'); - continue; - } - - /* If term is not set, append empty array. This indicates that the term - * could have had data but doesn't */ - if (!ecs_field_is_set(it, i + 1)) { - flecs_json_array_push(buf); - flecs_json_array_pop(buf); - continue; - } - - if (ecs_field_is_self(it, i + 1)) { - int32_t count = it->count; - if (array_to_json_buf_w_type_data(world, ptr, count, buf, comp, ser)) { - return -1; - } - } else { - if (array_to_json_buf_w_type_data(world, ptr, 0, buf, comp, ser)) { - return -1; - } - } - } - - flecs_json_array_pop(buf); - - return 0; -} - -static -int flecs_json_serialize_iter_result_columns( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_iter_to_json_desc_t *desc) -{ - ecs_table_t *table = it->table; - if (!table || !table->column_count) { - return 0; - } - - flecs_json_memberl(buf, "values"); - flecs_json_array_push(buf); - - ecs_type_t *type = &table->type; - int32_t *column_map = table->column_map; - ecs_assert(column_map != NULL, ECS_INTERNAL_ERROR, NULL); - - for (int i = 0; i < type->count; i ++) { - int32_t storage_column = -1; - if (column_map) { - storage_column = column_map[i]; - } - - if (!desc->serialize_private) { - ecs_id_t id = type->array[i]; - ecs_entity_t e = id; - if (ECS_IS_PAIR(id)) { - e = ecs_pair_first(world, id); - } - if (ecs_owns_id(world, e, EcsPrivate)) { - continue; - } - } - - ecs_strbuf_list_next(buf); - - if (storage_column == -1) { - ecs_strbuf_appendch(buf, '0'); - continue; - } - - ecs_entity_t typeid = table->data.columns[storage_column].ti->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].data); - if (array_to_json_buf_w_type_data(world, ptr, it->count, buf, comp, ser)) { - return -1; - } - } - - flecs_json_array_pop(buf); - - return 0; -} - -static -int flecs_json_serialize_iter_result( - const ecs_world_t *world, - const ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_iter_to_json_desc_t *desc, - const ecs_json_ser_idr_t *ser_idr) -{ - flecs_json_next(buf); - flecs_json_object_push(buf); - - /* 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_table) { - flecs_json_serialize_iter_result_table_type(world, it, buf, desc); - } else { - if (!desc || desc->serialize_ids) { - flecs_json_serialize_iter_result_ids(world, it, buf); - } - if (desc && desc->serialize_id_labels) { - flecs_json_serialize_iter_result_id_labels(world, it, buf); - } - } - - /* Include information on which entity the term is matched with */ - if (!desc || (desc->serialize_sources && !desc->serialize_table)) { - flecs_json_serialize_iter_result_sources(world, it, buf); - } - - /* Write variable values for current result */ - if (!desc || desc->serialize_variables) { - flecs_json_serialize_iter_result_variables(world, it, buf); - } - - /* Write labels for variables */ - if (desc && desc->serialize_variable_labels) { - flecs_json_serialize_iter_result_variable_labels(world, it, buf); - } - - /* Write ids for variables */ - if (desc && desc->serialize_variable_ids) { - flecs_json_serialize_iter_result_variable_ids(it, buf); - } - - /* Include information on which terms are set, to support optional terms */ - 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) { - flecs_json_serialize_iter_result_entities(world, it, buf); - } - - /* Write ids for entities */ - if (desc && desc->serialize_entity_ids) { - flecs_json_serialize_iter_result_entity_ids(it, buf); - } - - /* Write labels for entities */ - if (desc && desc->serialize_entity_labels) { - flecs_json_serialize_iter_result_entity_labels(it, buf, ser_idr); - } - - /* Write colors for entities */ - if (desc && desc->serialize_colors) { - flecs_json_serialize_iter_result_colors(it, buf, ser_idr); - } - - /* Serialize component values */ - if (desc && desc->serialize_table) { - if (flecs_json_serialize_iter_result_columns(world, it, buf, desc)) { - return -1; - } - } else { - if (!desc || desc->serialize_values) { - if (flecs_json_serialize_iter_result_values(world, it, buf)) { - return -1; - } - } - } - - /* Add "alerts": true member if table has entities with active alerts */ -#ifdef FLECS_ALERTS - if (it->table && (ecs_id(EcsAlertsActive) != 0)) { - /* Only add field if alerts addon is imported */ - if (ecs_table_has_id(world, it->table, ecs_id(EcsAlertsActive))) { - flecs_json_memberl(buf, "alerts"); - flecs_json_true(buf); - } - } -#endif - - flecs_json_object_pop(buf); - - return 0; -} - -int ecs_iter_to_json_buf( - const ecs_world_t *world, - ecs_iter_t *it, - ecs_strbuf_t *buf, - const ecs_iter_to_json_desc_t *desc) -{ - ecs_time_t duration = {0}; - if (desc && desc->measure_eval_duration) { - ecs_time_measure(&duration); - } - - flecs_json_object_push(buf); - - /* Serialize component ids of the terms (usually provided by query) */ - if (!desc || desc->serialize_term_ids) { - flecs_json_serialize_iter_ids(world, it, buf); - } - - if (desc && desc->serialize_term_labels) { - flecs_json_serialize_iter_id_labels(world, it, buf); - } - - /* Serialize type info if enabled */ - if (desc && desc->serialize_type_info) { - flecs_json_serialize_type_info(world, it, buf); - } - - /* Serialize variable names, if iterator has any */ - flecs_json_serialize_iter_variables(it, buf); - - /* Serialize results */ - flecs_json_memberl(buf, "results"); - flecs_json_array_push(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, EcsIterNoData); - } - - /* Cache id record for flecs.doc ids */ - ecs_json_ser_idr_t ser_idr = {NULL, NULL}; -#ifdef FLECS_DOC - ser_idr.idr_doc_name = flecs_id_record_get(world, - ecs_pair_t(EcsDocDescription, EcsName)); - ser_idr.idr_doc_color = flecs_id_record_get(world, - ecs_pair_t(EcsDocDescription, EcsDocColor)); -#endif - - ecs_iter_next_action_t next = it->next; - while (next(it)) { - if (flecs_json_serialize_iter_result(world, it, buf, desc, &ser_idr)) { - ecs_strbuf_reset(buf); - ecs_iter_fini(it); - return -1; - } - } - - flecs_json_array_pop(buf); - - if (desc && desc->measure_eval_duration) { - double dt = ecs_time_measure(&duration); - flecs_json_memberl(buf, "eval_duration"); - flecs_json_number(buf, dt); - } - - flecs_json_object_pop(buf); - - return 0; -} - -char* ecs_iter_to_json( - const ecs_world_t *world, - ecs_iter_t *it, - const ecs_iter_to_json_desc_t *desc) -{ - ecs_strbuf_t buf = ECS_STRBUF_INIT; - - if (ecs_iter_to_json_buf(world, it, &buf, desc)) { - ecs_strbuf_reset(&buf); - return NULL; - } - - return ecs_strbuf_get(&buf); -} - -int ecs_world_to_json_buf( - ecs_world_t *world, - ecs_strbuf_t *buf_out, - const ecs_world_to_json_desc_t *desc) -{ - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_desc_t filter_desc = {0}; - filter_desc.storage = &f; - - if (desc && desc->serialize_builtin && desc->serialize_modules) { - filter_desc.terms[0].id = EcsAny; - } else { - bool serialize_builtin = desc && desc->serialize_builtin; - bool serialize_modules = desc && desc->serialize_modules; - int32_t term_id = 0; - - if (!serialize_builtin) { - filter_desc.terms[term_id].id = ecs_pair(EcsChildOf, EcsFlecs); - filter_desc.terms[term_id].oper = EcsNot; - filter_desc.terms[term_id].src.flags = EcsSelf | EcsParent; - term_id ++; - } - if (!serialize_modules) { - filter_desc.terms[term_id].id = EcsModule; - filter_desc.terms[term_id].oper = EcsNot; - filter_desc.terms[term_id].src.flags = EcsSelf | EcsParent; - } - } - - if (ecs_filter_init(world, &filter_desc) == NULL) { - return -1; - } - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_to_json_desc_t json_desc = { - .serialize_table = true, - .serialize_ids = true, - .serialize_entities = true, - .serialize_private = true - }; - - int ret = ecs_iter_to_json_buf(world, &it, buf_out, &json_desc); - ecs_filter_fini(&f); - return ret; -} - -char* ecs_world_to_json( - ecs_world_t *world, - const ecs_world_to_json_desc_t *desc) -{ - ecs_strbuf_t buf = ECS_STRBUF_INIT; - - if (ecs_world_to_json_buf(world, &buf, desc)) { - ecs_strbuf_reset(&buf); - return NULL; - } - - return ecs_strbuf_get(&buf); -} - -#endif diff --git a/vendors/flecs/src/addons/json/serialize_entity.c b/vendors/flecs/src/addons/json/serialize_entity.c new file mode 100644 index 000000000..1c5e09e71 --- /dev/null +++ b/vendors/flecs/src/addons/json/serialize_entity.c @@ -0,0 +1,96 @@ +/** + * @file addons/json/serialize_entity.c + * @brief Serialize single entity. + */ + +#include "json.h" +#include "../meta/meta.h" + +#ifdef FLECS_JSON + +int ecs_entity_to_json_buf( + const ecs_world_t *stage, + ecs_entity_t entity, + ecs_strbuf_t *buf, + const ecs_entity_to_json_desc_t *desc) +{ + const ecs_world_t *world = ecs_get_world(stage); + + if (!entity || !ecs_is_valid(world, entity)) { + return -1; + } + + /* Cache id record for flecs.doc ids */ + ecs_json_ser_ctx_t ser_ctx; + ecs_os_zeromem(&ser_ctx); +#ifdef FLECS_DOC + ser_ctx.idr_doc_name = flecs_id_record_get(world, + ecs_pair_t(EcsDocDescription, EcsName)); + ser_ctx.idr_doc_color = flecs_id_record_get(world, + ecs_pair_t(EcsDocDescription, EcsDocColor)); +#endif + + ecs_record_t *r = ecs_record_find(world, entity); + if (!r || !r->table) { + flecs_json_object_push(buf); + flecs_json_member(buf, "name"); + ecs_strbuf_appendch(buf, '"'); + ecs_strbuf_appendch(buf, '#'); + ecs_strbuf_appendint(buf, (uint32_t)entity); + ecs_strbuf_appendch(buf, '"'); + flecs_json_object_pop(buf); + return 0; + } + + /* Create iterator that's populated just with entity */ + int32_t row = ECS_RECORD_TO_ROW(r->row); + ecs_iter_t it = { + .world = ECS_CONST_CAST(ecs_world_t*, world), + .real_world = ECS_CONST_CAST(ecs_world_t*, world), + .table = r->table, + .offset = row, + .count = 1, + .entities = &ecs_table_entities(r->table)[row], + .field_count = 0 + }; + + /* Initialize iterator parameters */ + ecs_iter_to_json_desc_t iter_desc = { + .serialize_table = true, + .serialize_entity_ids = desc ? desc->serialize_entity_id : false, + .serialize_values = desc ? desc->serialize_values : true, + .serialize_builtin = desc ? desc->serialize_builtin : false, + .serialize_doc = desc ? desc->serialize_doc : false, + .serialize_matches = desc ? desc->serialize_matches : false, + .serialize_refs = desc ? desc->serialize_refs : 0, + .serialize_alerts = desc ? desc->serialize_alerts : false, + .serialize_full_paths = desc ? desc->serialize_full_paths : true, + .serialize_inherited = desc ? desc->serialize_inherited : false, + .serialize_type_info = desc ? desc->serialize_type_info : false + }; + + if (flecs_json_serialize_iter_result( + world, &it, buf, &iter_desc, &ser_ctx)) + { + return -1; + } + + return 0; +} + +char* ecs_entity_to_json( + const ecs_world_t *world, + ecs_entity_t entity, + const ecs_entity_to_json_desc_t *desc) +{ + ecs_strbuf_t buf = ECS_STRBUF_INIT; + + if (ecs_entity_to_json_buf(world, entity, &buf, desc) != 0) { + ecs_strbuf_reset(&buf); + return NULL; + } + + return ecs_strbuf_get(&buf); +} + +#endif diff --git a/vendors/flecs/src/addons/json/serialize_field_info.c b/vendors/flecs/src/addons/json/serialize_field_info.c new file mode 100644 index 000000000..d9286831d --- /dev/null +++ b/vendors/flecs/src/addons/json/serialize_field_info.c @@ -0,0 +1,98 @@ +/** + * @file addons/json/serialize_field_info.c + * @brief Serialize query field information to JSON. + */ + +#include "json.h" + +#ifdef FLECS_JSON + +static +bool flecs_json_serialize_get_field_ctx( + const ecs_world_t *world, + const ecs_iter_t *it, + int32_t f, + ecs_json_ser_ctx_t *ser_ctx, + const ecs_iter_to_json_desc_t *desc) +{ + ecs_json_value_ser_ctx_t *value_ctx = &ser_ctx->value_ctx[f]; + if (it->query) { + return flecs_json_serialize_get_value_ctx( + world, it->query->ids[f], value_ctx, desc); + } else if (it->ids[f]) { + return flecs_json_serialize_get_value_ctx( + world, it->ids[f], value_ctx, desc); + } else { + return false; + } +} + + +void flecs_json_serialize_field( + const ecs_world_t *world, + const ecs_iter_t *it, + const ecs_query_t *q, + int field, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ctx) +{ + flecs_json_object_push(buf); + flecs_json_memberl(buf, "id"); + + flecs_json_serialize_get_field_ctx(world, it, field, ctx, NULL); + ecs_json_value_ser_ctx_t *value_ctx = &ctx->value_ctx[field]; + + if (value_ctx->id_label) { + flecs_json_string(buf, value_ctx->id_label); + + const ecs_term_t *term = &q->terms[0]; + int t; + for (t = 0; t < q->term_count; t ++) { + if (q->terms[t].field_index == field) { + term = &q->terms[t]; + break; + } + } + + if (term->oper != EcsNot) { + if (term->oper == EcsOptional) { + flecs_json_memberl(buf, "optional"); + flecs_json_bool(buf, true); + } + + if (ECS_IS_PAIR(term->id)) { + if ((term->first.id & EcsIsEntity) && ECS_TERM_REF_ID(&term->first)) { + if (ecs_has_id(world, ECS_TERM_REF_ID(&term->first), EcsExclusive)) { + flecs_json_memberl(buf, "exclusive"); + flecs_json_bool(buf, true); + } + } + } + + if (value_ctx->type) { + flecs_json_memberl(buf, "type"); + flecs_json_label(buf, world, value_ctx->type); + + const char *symbol = ecs_get_symbol(world, value_ctx->type); + if (symbol) { + flecs_json_memberl(buf, "symbol"); + flecs_json_string(buf, symbol); + } + } + + if (value_ctx->ser) { + flecs_json_memberl(buf, "schema"); + ecs_type_info_to_json_buf(world, value_ctx->type, buf); + } + } else { + flecs_json_memberl(buf, "not"); + flecs_json_bool(buf, true); + } + } else { + ecs_strbuf_appendlit(buf, "0"); + } + + flecs_json_object_pop(buf); +} + +#endif diff --git a/vendors/flecs/src/addons/json/serialize_iter.c b/vendors/flecs/src/addons/json/serialize_iter.c new file mode 100644 index 000000000..bdcec008a --- /dev/null +++ b/vendors/flecs/src/addons/json/serialize_iter.c @@ -0,0 +1,368 @@ +/** + * @file addons/json/serialize_iter.c + * @brief Serialize iterator to JSON. + */ + +#include "json.h" +#include "../meta/meta.h" + +#ifdef FLECS_JSON + +static +void flecs_json_serialize_id_str( + const ecs_world_t *world, + ecs_id_t id, + ecs_strbuf_t *buf) +{ + ecs_strbuf_appendch(buf, '"'); + if (ECS_IS_PAIR(id)) { + ecs_entity_t first = ecs_pair_first(world, id); + ecs_entity_t second = ecs_pair_first(world, id); + ecs_strbuf_appendch(buf, '('); + ecs_get_path_w_sep_buf(world, 0, first, ".", "", buf, true); + ecs_strbuf_appendch(buf, ','); + ecs_get_path_w_sep_buf(world, 0, second, ".", "", buf, true); + ecs_strbuf_appendch(buf, ')'); + } else { + ecs_get_path_w_sep_buf( + world, 0, id & ECS_COMPONENT_MASK, ".", "", buf, true); + } + ecs_strbuf_appendch(buf, '"'); +} + +static +void flecs_json_serialize_type_info( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + flecs_json_memberl(buf, "type_info"); + flecs_json_object_push(buf); + + int32_t field_count = it->field_count; + if (!field_count) { + goto done; + } + + if (it->flags & EcsIterNoData) { + goto done; + } + + for (int i = 0; i < field_count; i ++) { + flecs_json_next(buf); + ecs_entity_t typeid = 0; + if (it->query->terms[i].inout != EcsInOutNone) { + typeid = ecs_get_typeid(world, it->query->terms[i].id); + } + if (typeid) { + flecs_json_serialize_id_str(world, typeid, buf); + ecs_strbuf_appendch(buf, ':'); + ecs_type_info_to_json_buf(world, typeid, buf); + } else { + flecs_json_serialize_id_str(world, it->query->terms[i].id, buf); + ecs_strbuf_appendlit(buf, ":0"); + } + } + +done: + flecs_json_object_pop(buf); +} + +static +void flecs_json_serialize_field_info( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ctx) +{ + int32_t field_count = it->field_count; + if (!field_count || !it->query) { + return; + } + + const ecs_query_t *q = it->query; + + flecs_json_memberl(buf, "field_info"); + flecs_json_array_push(buf); + + int f; + for (f = 0; f < field_count; f ++) { + flecs_json_next(buf); + flecs_json_serialize_field(world, it, q, f, buf, ctx); + } + + flecs_json_array_pop(buf); +} + +static +void flecs_json_serialize_query_info( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + if (!it->query) { + return; + } + + const ecs_query_t *q = it->query; + flecs_json_memberl(buf, "query_info"); + flecs_json_serialize_query(world, q, buf); +} + +static +void flecs_json_serialize_query_plan( + const ecs_world_t *world, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + (void)world; + (void)buf; + (void)desc; + + if (!desc->query) { + return; + } + + const ecs_query_t *q = desc->query; + flecs_poly_assert(q, ecs_query_t); + const ecs_query_t *cq = ecs_query_get_cache_query(q); + + flecs_json_memberl(buf, "query_plan"); + + bool prev_color = ecs_log_enable_colors(true); + char *plan = ecs_query_plan(q); + char *cache_plan = NULL; + if (cq) { + flecs_poly_assert(cq, ecs_query_t); + cache_plan = ecs_query_plan(cq); + } + + ecs_strbuf_t plan_buf = ECS_STRBUF_INIT; + if (plan) { + ecs_strbuf_appendstr(&plan_buf, plan); + } else { + if (q->term_count) { + ecs_strbuf_append(&plan_buf, " %sOptimized out (trivial query)\n", ECS_GREY); + } + } + + if (cq) { + ecs_strbuf_appendstr(&plan_buf, "\n\n"); + ecs_strbuf_appendstr(&plan_buf, " Cache plan\n"); + ecs_strbuf_appendstr(&plan_buf, " ---\n"); + + if (cache_plan) { + ecs_strbuf_appendstr(&plan_buf, cache_plan); + } else { + ecs_strbuf_append(&plan_buf, " %sOptimized out (trivial query)\n", ECS_GREY); + } + } + + char *plan_str = ecs_strbuf_get(&plan_buf); + if (plan_str) { + flecs_json_string_escape(buf, plan_str); + ecs_os_free(plan_str); + } else { + flecs_json_null(buf); + } + + ecs_os_free(plan); + ecs_os_free(cache_plan); + ecs_log_enable_colors(prev_color); +} + +static +void flecs_json_serialize_query_profile( + const ecs_world_t *world, + ecs_strbuf_t *buf, + const ecs_iter_t *it, + const ecs_iter_to_json_desc_t *desc) +{ + if (!desc->query) { + return; + } + + ecs_time_t t = {0}; + int32_t result_count = 0, entity_count = 0, i, sample_count = 100; + ecs_size_t component_bytes = 0, shared_component_bytes = 0; + double eval_time = 0, eval_min = 0, eval_max = 0; + ecs_time_measure(&t); + + for (i = 0; i < sample_count; i ++) { + result_count = 0; + entity_count = 0; + component_bytes = 0; + shared_component_bytes = 0; + + ecs_iter_t qit = ecs_query_iter(world, desc->query); + while (ecs_query_next(&qit)) { + result_count ++; + entity_count += qit.count; + + int8_t f, field_count = qit.field_count; + for (f = 0; f < field_count; f ++) { + size_t size = ecs_field_size(&qit, f); + if (ecs_field_is_set(&qit, f) && size) { + if (ecs_field_is_self(&qit, f)) { + component_bytes += + flecs_uto(ecs_size_t, size) * qit.count; + } else { + shared_component_bytes += flecs_uto(ecs_size_t, size); + } + } + } + } + + double time_measure = ecs_time_measure(&t); + if (!i) { + eval_min = time_measure; + } else if (time_measure < eval_min) { + eval_min = time_measure; + } + + if (time_measure > eval_max) { + eval_max = time_measure; + } + + eval_time += time_measure; + + /* Don't profile for too long */ + if (eval_time > 0.001) { + i ++; + break; + } + } + + eval_time /= i; + + flecs_json_memberl(buf, "query_profile"); + flecs_json_object_push(buf); + if (it->query) { + /* Correct for profiler */ + ECS_CONST_CAST(ecs_query_t*, it->query)->eval_count -= i; + flecs_json_memberl(buf, "eval_count"); + flecs_json_number(buf, it->query->eval_count); + } + flecs_json_memberl(buf, "result_count"); + flecs_json_number(buf, result_count); + flecs_json_memberl(buf, "entity_count"); + flecs_json_number(buf, entity_count); + + flecs_json_memberl(buf, "eval_time_avg_us"); + flecs_json_number(buf, eval_time * 1000.0 * 1000.0); + flecs_json_memberl(buf, "eval_time_min_us"); + flecs_json_number(buf, eval_min * 1000.0 * 1000.0); + flecs_json_memberl(buf, "eval_time_max_us"); + flecs_json_number(buf, eval_max * 1000.0 * 1000.0); + + flecs_json_memberl(buf, "component_bytes"); + flecs_json_number(buf, component_bytes); + flecs_json_memberl(buf, "shared_component_bytes"); + flecs_json_number(buf, shared_component_bytes); + + flecs_json_object_pop(buf); +} + +static +void flecs_iter_free_ser_ctx( + ecs_iter_t *it, + ecs_json_ser_ctx_t *ser_ctx) +{ + int32_t f, field_count = it->field_count; + for (f = 0; f < field_count; f ++) { + ecs_os_free(ser_ctx->value_ctx[f].id_label); + } +} + +int ecs_iter_to_json_buf( + ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + ecs_world_t *world = it->real_world; + + /* Cache id record for flecs.doc ids */ + ecs_json_ser_ctx_t ser_ctx; + ecs_os_zeromem(&ser_ctx); +#ifdef FLECS_DOC + ser_ctx.idr_doc_name = flecs_id_record_get(world, + ecs_pair_t(EcsDocDescription, EcsName)); + ser_ctx.idr_doc_color = flecs_id_record_get(world, + ecs_pair_t(EcsDocDescription, EcsDocColor)); +#endif + + flecs_json_object_push(buf); + + /* Serialize type info if enabled */ + if (desc && desc->serialize_type_info) { + flecs_json_serialize_type_info(world, it, buf); + } + + /* Serialize field info if enabled */ + if (desc && desc->serialize_field_info) { + flecs_json_serialize_field_info(world, it, buf, &ser_ctx); + } + + /* Serialize query info if enabled */ + if (desc && desc->serialize_query_info) { + flecs_json_serialize_query_info(world, it, buf); + } + + /* Serialize query plan if enabled */ + if (desc && desc->serialize_query_plan) { + flecs_json_serialize_query_plan(world, buf, desc); + } + + /* Profile query */ + if (desc && desc->serialize_query_profile) { + flecs_json_serialize_query_profile(world, buf, it, desc); + } + + /* Serialize results */ + if (!desc || !desc->dont_serialize_results) { + flecs_json_memberl(buf, "results"); + flecs_json_array_push(buf); + + /* 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, EcsIterNoData); + } + + ecs_iter_next_action_t next = it->next; + while (next(it)) { + if (flecs_json_serialize_iter_result(world, it, buf, desc, &ser_ctx)) { + ecs_strbuf_reset(buf); + flecs_iter_free_ser_ctx(it, &ser_ctx); + ecs_iter_fini(it); + return -1; + } + } + + flecs_json_array_pop(buf); + } else { + ecs_iter_fini(it); + } + + flecs_iter_free_ser_ctx(it, &ser_ctx); + + flecs_json_object_pop(buf); + + return 0; +} + +char* ecs_iter_to_json( + ecs_iter_t *it, + const ecs_iter_to_json_desc_t *desc) +{ + ecs_strbuf_t buf = ECS_STRBUF_INIT; + + if (ecs_iter_to_json_buf(it, &buf, desc)) { + ecs_strbuf_reset(&buf); + return NULL; + } + + return ecs_strbuf_get(&buf); +} + +#endif diff --git a/vendors/flecs/src/addons/json/serialize_iter_result.c b/vendors/flecs/src/addons/json/serialize_iter_result.c new file mode 100644 index 000000000..dc3b24c13 --- /dev/null +++ b/vendors/flecs/src/addons/json/serialize_iter_result.c @@ -0,0 +1,495 @@ +/** + * @file addons/json/serialize_iter_rows.c + * @brief Serialize (component) values to JSON strings. + */ + +#include "json.h" + +#ifdef FLECS_JSON + +static +bool flecs_json_skip_variable( + const char *name) +{ + if (!name || name[0] == '_' || !ecs_os_strcmp(name, "this")) { + return true; + } else { + return false; + } +} + +bool flecs_json_serialize_vars( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + char **variable_names = it->variable_names; + int32_t var_count = it->variable_count; + int32_t actual_count = 0; + + for (int i = 1; i < var_count; i ++) { + const char *var_name = variable_names[i]; + if (flecs_json_skip_variable(var_name)) continue; + + ecs_entity_t var = it->variables[i].entity; + if (!var) { + /* Can't happen, but not the place of the serializer to complain */ + continue; + } + + if (!actual_count) { + flecs_json_memberl(buf, "vars"); + flecs_json_object_push(buf); + actual_count ++; + } + + flecs_json_member(buf, var_name); + flecs_json_path_or_label(buf, world, var, + desc ? desc->serialize_full_paths : true); + } + + if (actual_count) { + flecs_json_object_pop(buf); + } + + return actual_count != 0; +} + +int flecs_json_serialize_matches( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity) +{ + flecs_json_memberl(buf, "matches"); + flecs_json_array_push(buf); + + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_pair_t(EcsPoly, EcsQuery)); + + if (idr) { + ecs_table_cache_iter_t it; + if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) { + const ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + EcsPoly *queries = ecs_table_get_column(table, tr->column, 0); + + const ecs_entity_t *entities = ecs_table_entities(table); + int32_t i, count = ecs_table_count(table); + for (i = 0; i < count; i ++) { + ecs_query_t *q = queries[i].poly; + if (!q) { + continue; + } + + ecs_assert(flecs_poly_is(q, ecs_query_t), + ECS_INTERNAL_ERROR, NULL); + + if (!(q->flags & EcsQueryMatchThis)) { + continue; + } + + ecs_iter_t qit = ecs_query_iter(world, q); + if (!qit.variables) { + ecs_iter_fini(&qit); + continue; + } + + ecs_iter_set_var(&qit, 0, entity); + if (ecs_iter_is_true(&qit)) { + flecs_json_next(buf); + flecs_json_path(buf, world, entities[i]); + } + } + } + } + } + + flecs_json_array_pop(buf); + + return 0; +} + +static +int flecs_json_serialize_refs_idr( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_id_record_t *idr) +{ + char *id_str = ecs_id_str(world, ecs_pair_first(world, idr->id)); + + flecs_json_member(buf, id_str); + ecs_os_free(id_str); + + flecs_json_array_push(buf); + + ecs_table_cache_iter_t it; + if (idr && flecs_table_cache_all_iter((ecs_table_cache_t*)idr, &it)) { + const ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + const ecs_entity_t *entities = ecs_table_entities(table); + int32_t i, count = ecs_table_count(table); + for (i = 0; i < count; i ++) { + ecs_entity_t e = entities[i]; + flecs_json_next(buf); + flecs_json_path(buf, world, e); + } + } + } + + flecs_json_array_pop(buf); + + return 0; +} + +int flecs_json_serialize_refs( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity, + ecs_entity_t relationship) +{ + flecs_json_memberl(buf, "refs"); + flecs_json_object_push(buf); + + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_pair(relationship, entity)); + + if (idr) { + if (relationship == EcsWildcard) { + ecs_id_record_t *cur = idr; + while ((cur = cur->second.next)) { + flecs_json_serialize_refs_idr(world, buf, cur); + } + } else { + flecs_json_serialize_refs_idr(world, buf, idr); + } + } + + flecs_json_object_pop(buf); + + return 0; +} + +#ifdef FLECS_ALERTS +static +int flecs_json_serialize_entity_alerts( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity, + const EcsAlertsActive *alerts, + bool self) +{ + ecs_assert(alerts != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_map_iter_t it = ecs_map_iter(&alerts->alerts); + while (ecs_map_next(&it)) { + flecs_json_next(buf); + flecs_json_object_push(buf); + ecs_entity_t ai = ecs_map_value(&it); + char *alert_name = ecs_get_path(world, ai); + flecs_json_memberl(buf, "alert"); + flecs_json_string(buf, alert_name); + ecs_os_free(alert_name); + + ecs_entity_t severity_id = ecs_get_target( + world, ai, ecs_id(EcsAlert), 0); + const char *severity = ecs_get_name(world, severity_id); + + const EcsAlertInstance *alert = ecs_get( + world, ai, EcsAlertInstance); + if (alert) { + if (alert->message) { + flecs_json_memberl(buf, "message"); + flecs_json_string(buf, alert->message); + } + flecs_json_memberl(buf, "severity"); + flecs_json_string(buf, severity); + + if (!self) { + char *path = ecs_get_path(world, entity); + flecs_json_memberl(buf, "path"); + flecs_json_string(buf, path); + ecs_os_free(path); + } + } + flecs_json_object_pop(buf); + } + + return 0; +} + +static +int flecs_json_serialize_children_alerts( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity) +{ + ecs_query_t *q = ecs_query(ECS_CONST_CAST(ecs_world_t*, world), { + .terms = {{ .id = ecs_pair(EcsChildOf, entity) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + EcsAlertsActive *alerts = ecs_table_get_id( + world, it.table, ecs_id(EcsAlertsActive), it.offset); + + int32_t i; + for (i = 0; i < it.count; i ++) { + ecs_entity_t child = it.entities[i]; + if (alerts) { + if (flecs_json_serialize_entity_alerts( + world, buf, child, &alerts[i], false)) + { + goto error; + } + } + + ecs_record_t *r = flecs_entities_get(world, it.entities[i]); + if (r->row & EcsEntityIsTraversable) { + if (flecs_json_serialize_children_alerts( + world, buf, child)) + { + goto error; + } + } + } + } + + ecs_query_fini(q); + + return 0; +error: + return -1; +} +#endif + +int flecs_json_serialize_alerts( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity) +{ + (void)world; + (void)buf; + (void)entity; + +#ifdef FLECS_ALERTS + if (!ecs_id(EcsAlertsActive)) { + return 0; /* Alert module not imported */ + } + + flecs_json_memberl(buf, "alerts"); + flecs_json_array_push(buf); + const EcsAlertsActive *alerts = ecs_get(world, entity, EcsAlertsActive); + if (alerts) { + flecs_json_serialize_entity_alerts(world, buf, entity, alerts, true); + } + flecs_json_serialize_children_alerts(world, buf, entity); + flecs_json_array_pop(buf); +#endif + return 0; +} + +bool flecs_json_serialize_get_value_ctx( + const ecs_world_t *world, + ecs_id_t id, + ecs_json_value_ser_ctx_t *ctx, + const ecs_iter_to_json_desc_t *desc) +{ + if (!id) { + return false; + } + + if (!ctx->initialized) { + ctx->initialized = true; + + ecs_strbuf_t idlbl = ECS_STRBUF_INIT; + flecs_json_id_member(&idlbl, world, id, + desc ? desc->serialize_full_paths : true); + ctx->id_label = ecs_strbuf_get(&idlbl); + + ecs_entity_t type = ecs_get_typeid(world, id); + if (!type) { + return false; + } + + ctx->type = type; + ctx->ser = ecs_get(world, type, EcsTypeSerializer); + if (!ctx->ser) { + return false; + } + + return true; + } else { + return ctx->ser != NULL; + } +} + +void flecs_json_serialize_iter_this( + const ecs_iter_t *it, + const char *parent_path, + const ecs_json_this_data_t *this_data, + int32_t row, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + ecs_assert(row < it->count, ECS_INTERNAL_ERROR, NULL); + + if (parent_path) { + flecs_json_memberl(buf, "parent"); + flecs_json_string(buf, parent_path); + } + + flecs_json_memberl(buf, "name"); + if (this_data->names) { + flecs_json_string(buf, this_data->names[row].value); + } else { + ecs_strbuf_appendlit(buf, "\"#"); + ecs_strbuf_appendint(buf, flecs_uto(int64_t, + (uint32_t)it->entities[row])); + ecs_strbuf_appendlit(buf, "\""); + } + + if (desc && desc->serialize_entity_ids) { + flecs_json_memberl(buf, "id"); + flecs_json_u32(buf, (uint32_t)this_data->ids[row]); + } + +#ifdef FLECS_DOC + if (desc && desc->serialize_doc) { + flecs_json_memberl(buf, "doc"); + flecs_json_object_push(buf); + if (this_data->label) { + flecs_json_memberl(buf, "label"); + flecs_json_string_escape(buf, this_data->label[row].value); + } else { + flecs_json_memberl(buf, "label"); + if (this_data->names) { + flecs_json_string(buf, this_data->names[row].value); + } else { + ecs_strbuf_appendlit(buf, "\"#"); + ecs_strbuf_appendint(buf, flecs_uto(int64_t, + (uint32_t)it->entities[row])); + ecs_strbuf_appendlit(buf, "\""); + } + } + + if (this_data->brief) { + flecs_json_memberl(buf, "brief"); + flecs_json_string_escape(buf, this_data->brief[row].value); + } + + if (this_data->detail) { + flecs_json_memberl(buf, "detail"); + flecs_json_string_escape(buf, this_data->detail[row].value); + } + + if (this_data->color) { + flecs_json_memberl(buf, "color"); + flecs_json_string_escape(buf, this_data->color[row].value); + } + + if (this_data->link) { + flecs_json_memberl(buf, "link"); + flecs_json_string_escape(buf, this_data->link[row].value); + } + + flecs_json_object_pop(buf); + } +#endif + + if (this_data->has_alerts) { + flecs_json_memberl(buf, "has_alerts"); + flecs_json_true(buf); + } +} + +int flecs_json_serialize_iter_result( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + ecs_json_ser_ctx_t *ser_ctx) +{ + char *parent_path = NULL; + ecs_json_this_data_t this_data = {0}; + + int32_t count = it->count; + bool has_this = true; + if (!count) { + count = 1; /* Query without this variable */ + has_this = false; + } else { + ecs_table_t *table = it->table; + if (table) { + this_data.ids = &ecs_table_entities(table)[it->offset]; + + /* Get path to parent once for entire table */ + if (table->flags & EcsTableHasChildOf) { + const ecs_table_record_t *tr = flecs_table_record_get( + world, table, ecs_pair(EcsChildOf, EcsWildcard)); + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t parent = ecs_pair_second( + world, table->type.array[tr->index]); + parent_path = ecs_get_path_w_sep(world, 0, parent, ".", ""); + } + + /* Fetch name column once vs. calling ecs_get_name for each row */ + if (table->flags & EcsTableHasName) { + this_data.names = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsIdentifier, EcsName), it->offset); + } + + /* Get entity labels */ +#ifdef FLECS_DOC + if (desc && desc->serialize_doc) { + this_data.label = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsName), it->offset); + this_data.brief = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsDocBrief), it->offset); + this_data.detail = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsDocDetail), it->offset); + this_data.color = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsDocColor), it->offset); + this_data.link = ecs_table_get_id(it->world, it->table, + ecs_pair_t(EcsDocDescription, EcsDocLink), it->offset); + } +#endif + +#ifdef FLECS_ALERTS + if (it->table && (ecs_id(EcsAlertsActive) != 0)) { + /* Only add field if alerts addon is imported */ + if (ecs_table_has_id(world, table, ecs_id(EcsAlertsActive))) { + this_data.has_alerts = true; + } + } +#endif + } else { + /* Very rare case, but could happen if someone's using an iterator + * to return empty entities. */ + } + } + + if (desc && desc->serialize_table) { + if (flecs_json_serialize_iter_result_table(world, it, buf, + desc, count, has_this, parent_path, &this_data)) + { + goto error; + } + } else { + if (flecs_json_serialize_iter_result_query(world, it, buf, ser_ctx, + desc, count, has_this, parent_path, &this_data)) + { + goto error; + } + } + + ecs_os_free(parent_path); + return 0; +error: + ecs_os_free(parent_path); + return -1; +} + +#endif diff --git a/vendors/flecs/src/addons/json/serialize_iter_result_query.c b/vendors/flecs/src/addons/json/serialize_iter_result_query.c new file mode 100644 index 000000000..c2c28e45c --- /dev/null +++ b/vendors/flecs/src/addons/json/serialize_iter_result_query.c @@ -0,0 +1,297 @@ +/** + * @file addons/json/serialize_iter_result_query.c + * @brief Serialize matched query data of result. + */ + +#include "json.h" + +#ifdef FLECS_JSON + +static +bool flecs_json_serialize_iter_result_is_set( + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + if (!(it->flags & EcsIterHasCondSet)) { + return false; + } + + flecs_json_memberl(buf, "is_set"); + flecs_json_array_push(buf); + + int8_t i, count = it->field_count; + for (i = 0; i < count; i ++) { + ecs_strbuf_list_next(buf); + if (ecs_field_is_set(it, i)) { + flecs_json_true(buf); + } else { + flecs_json_false(buf); + } + } + + flecs_json_array_pop(buf); + + return true; +} + +static +bool flecs_json_serialize_iter_result_ids( + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + const ecs_query_t *q = it->query; + if (!q) { + return false; + } + + ecs_world_t *world = it->world; + int16_t f, field_count = flecs_ito(int16_t, it->field_count); + uint32_t field_mask = (uint32_t)((1llu << field_count) - 1); + + if (q->static_id_fields == field_mask) { + /* All matched ids are static, nothing to serialize */ + return false; + } + + flecs_json_memberl(buf, "ids"); + flecs_json_array_push(buf); + + for (f = 0; f < field_count; f ++) { + ecs_termset_t field_bit = (ecs_termset_t)(1u << f); + + if (!(it->set_fields & field_bit)) { + /* Don't serialize ids for fields that aren't set */ + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } + + if (q->static_id_fields & field_bit) { + /* Only add non-static ids to save bandwidth/performance */ + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } + + flecs_json_next(buf); + flecs_json_id(buf, world, it->ids[f]); + } + + flecs_json_array_pop(buf); + + return true; +} + +static +bool flecs_json_serialize_iter_result_sources( + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + const ecs_query_t *q = it->query; + if (!q) { + return false; + } + + ecs_world_t *world = it->world; + int32_t f, field_count = it->field_count; + + for (f = 0; f < field_count; f ++) { + if (it->sources[f]) { + break; + } + } + + if (f == field_count) { + /* All fields are matched on $this */ + return false; + } + + flecs_json_memberl(buf, "sources"); + flecs_json_array_push(buf); + + for (f = 0; f < field_count; f ++) { + ecs_termset_t field_bit = (ecs_termset_t)(1u << f); + + if (!(it->set_fields & field_bit)) { + /* Don't serialize source for fields that aren't set */ + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } + + if (!it->sources[f]) { + /* Don't serialize source for fields that have $this source */ + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } + + flecs_json_next(buf); + flecs_json_path(buf, world, it->sources[f]); + } + + flecs_json_array_pop(buf); + + return true; +} + +static +bool flecs_json_serialize_common_for_table( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + ecs_strbuf_list_push(buf, "", ","); + flecs_json_serialize_vars(world, it, buf, desc); + + bool result = false; + if (!desc || desc->serialize_fields) { + ecs_strbuf_list_appendlit(buf, "\"fields\":"); + flecs_json_object_push(buf); + result |= flecs_json_serialize_iter_result_is_set(it, buf); + result |= flecs_json_serialize_iter_result_ids(it, buf); + result |= flecs_json_serialize_iter_result_sources(it, buf); + } + + ecs_strbuf_list_pop(buf, ""); + return result; +} + +static +int flecs_json_serialize_iter_result_field_values( + const ecs_world_t *world, + const ecs_iter_t *it, + int32_t i, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + ecs_json_ser_ctx_t *ser_ctx) +{ + int8_t f, field_count = it->field_count; + if (!field_count) { + return 0; + } + + ecs_strbuf_appendlit(buf, "\"values\":"); + flecs_json_array_push(buf); + + ecs_termset_t fields = it->set_fields; + if (it->query) { + fields &= it->query->data_fields; + } + + ecs_termset_t row_fields = it->query ? it->query->row_fields : 0; + + for (f = 0; f < field_count; f ++) { + ecs_termset_t field_bit = (ecs_termset_t)(1u << f); + if (!(fields & field_bit)) { + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } + + ecs_json_value_ser_ctx_t *value_ctx = &ser_ctx->value_ctx[f]; + if (!flecs_json_serialize_get_value_ctx( + world, it->ids[f], value_ctx, desc)) + { + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } + + void *ptr; + if (row_fields & field_bit) { + ptr = ecs_field_at_w_size(it, 0, f, i); + } else { + ecs_size_t size = it->sizes[f]; + ptr = ecs_field_w_size(it, flecs_itosize(size), f); + + if (!ptr) { + ecs_strbuf_list_appendlit(buf, "0"); + continue; + } + + if (!it->sources[f]) { + ptr = ECS_ELEM(ptr, size, i); + } + } + + flecs_json_next(buf); + if (flecs_json_ser_type(world, &value_ctx->ser->ops, ptr, buf) != 0) { + return -1; + } + } + + flecs_json_array_pop(buf); + + return 0; +} + +int flecs_json_serialize_iter_result_query( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + ecs_json_ser_ctx_t *ser_ctx, + const ecs_iter_to_json_desc_t *desc, + int32_t count, + bool has_this, + const char *parent_path, + const ecs_json_this_data_t *this_data) +{ + /* Serialize tags, pairs, vars once, since they're the same for each row */ + ecs_strbuf_t common_data_buf = ECS_STRBUF_INIT; + bool common_field_data = flecs_json_serialize_common_for_table( + world, it, &common_data_buf, desc); + int32_t common_data_len = ecs_strbuf_written(&common_data_buf); + char *common_data = NULL; + if (!common_data_len) { + ecs_strbuf_reset(&common_data_buf); + } else { + common_data = ecs_strbuf_get(&common_data_buf); + } + + int32_t i; + for (i = 0; i < count; i ++) { + flecs_json_next(buf); + flecs_json_object_push(buf); + + if (has_this) { + flecs_json_serialize_iter_this( + it, parent_path, this_data, i, buf, desc); + } + + if (common_data) { + ecs_strbuf_list_appendstrn(buf, + common_data, common_data_len); + } + + if (!desc || desc->serialize_fields) { + bool has_values = !desc || desc->serialize_values; + if (it->flags & EcsIterNoData || !it->field_count) { + has_values = false; + } + + const ecs_query_t *q = it->query; + if (q && !q->data_fields) { + has_values = false; + } + + if (has_values) { + if (common_field_data) { + flecs_json_next(buf); + } + + if (flecs_json_serialize_iter_result_field_values( + world, it, i, buf, desc, ser_ctx)) + { + ecs_os_free(common_data); + return -1; + } + } + + ecs_strbuf_appendstr(buf, "}"); // "fields": { + } + + flecs_json_object_pop(buf); + } + + ecs_os_free(common_data); + + return 0; +} + +#endif diff --git a/vendors/flecs/src/addons/json/serialize_iter_result_table.c b/vendors/flecs/src/addons/json/serialize_iter_result_table.c new file mode 100644 index 000000000..37ff03f27 --- /dev/null +++ b/vendors/flecs/src/addons/json/serialize_iter_result_table.c @@ -0,0 +1,548 @@ +/** + * @file addons/json/serialize_iter_result_table.c + * @brief Serialize all components of matched entity. + */ + +#include "json.h" + +#ifdef FLECS_JSON + +#define FLECS_JSON_MAX_TABLE_COMPONENTS (256) + +bool flecs_json_is_builtin( + ecs_id_t id) +{ + if (ECS_IS_PAIR(id)) { + if (ECS_PAIR_FIRST(id) == EcsChildOf) { + return true; + } + if (id == ecs_pair_t(EcsIdentifier, EcsName)) { + return true; + } + } + return false; +} + +static +bool flecs_json_serialize_table_type_info( + const ecs_world_t *world, + ecs_table_t *table, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + flecs_json_memberl(buf, "type_info"); + flecs_json_object_push(buf); + + int32_t i, type_count = table->type.count; + for (i = 0; i < type_count; i ++) { + const ecs_table_record_t *tr = &table->_->records[i]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_id_t id = table->type.array[i]; + if (!(idr->flags & EcsIdIsSparse) && + (!table->column_map || (table->column_map[i] == -1))) + { + continue; + } + + if (!desc || !desc->serialize_builtin) { + if (flecs_json_is_builtin(id)) { + continue; + } + } + + const ecs_type_info_t *ti = idr->type_info; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + + flecs_json_next(buf); + ecs_strbuf_appendlit(buf, "\""); + flecs_json_id_member(buf, world, id, desc->serialize_full_paths); + ecs_strbuf_appendlit(buf, "\":"); + + ecs_type_info_to_json_buf(world, ti->component, buf); + } + + flecs_json_object_pop(buf); + + return true; +} + +static +bool flecs_json_serialize_table_tags( + const ecs_world_t *world, + const ecs_table_t *table, + const ecs_table_t *src_table, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + int16_t f, type_count = flecs_ito(int16_t, table->type.count); + ecs_id_t *ids = table->type.array; + int16_t *column_map = table->column_map; + + int32_t tag_count = 0; + ecs_table_record_t *trs = table->_->records; + for (f = 0; f < type_count; f ++) { + ecs_id_t id = ids[f]; + if (ECS_IS_PAIR(id)) { + continue; + } + + if (!desc || !desc->serialize_builtin) { + if (flecs_json_is_builtin(id)) { + continue; + } + } + + if (column_map && column_map[f] != -1) { + continue; /* Ignore components */ + } + + const ecs_table_record_t *tr = &trs[f]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + + if (src_table) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + } + if (idr->flags & EcsIdIsSparse) { + continue; + } + + if (!tag_count) { + flecs_json_memberl(buf, "tags"); + flecs_json_array_push(buf); + } + + flecs_json_next(buf); + + ecs_strbuf_appendlit(buf, "\""); + flecs_json_id_member(buf, world, id, + desc ? desc->serialize_full_paths : true); + ecs_strbuf_appendlit(buf, "\""); + + tag_count ++; + } + + if (tag_count) { + flecs_json_array_pop(buf); + } + + return tag_count != 0; +} + +static +bool flecs_json_serialize_table_pairs( + const ecs_world_t *world, + const ecs_table_t *table, + const ecs_table_t *src_table, + int32_t row, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + int16_t f, type_count = flecs_ito(int16_t, table->type.count); + ecs_id_t *ids = table->type.array; + int16_t *column_map = table->column_map; + + int32_t pair_count = 0; + bool same_first = false; + + ecs_table_record_t *trs = table->_->records; + for (f = 0; f < type_count; f ++) { + ecs_id_t id = ids[f]; + if (!ECS_IS_PAIR(id)) { + continue; + } + + if (!desc || !desc->serialize_builtin) { + if (flecs_json_is_builtin(id)) { + continue; + } + } + + if (column_map && column_map[f] != -1) { + continue; /* Ignore components */ + } + + const ecs_table_record_t *tr = &trs[f]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + + if (src_table) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + } + if (idr->flags & EcsIdIsSparse) { + continue; + } + + ecs_entity_t first = flecs_entities_get_alive( + world, ECS_PAIR_FIRST(id)); + + if (!pair_count) { + flecs_json_memberl(buf, "pairs"); + flecs_json_object_push(buf); + } + + ecs_entity_t second = flecs_entities_get_alive( + world, ECS_PAIR_SECOND(id)); + + bool is_last = f == (type_count - 1); + bool is_same = !is_last && + (ECS_PAIR_FIRST(ids[f + 1]) == ECS_PAIR_FIRST(id)); + + if (same_first && f && ECS_PAIR_FIRST(ids[f - 1]) != ECS_PAIR_FIRST(id)) { + /* New pair has different first elem, so close array */ + flecs_json_array_pop(buf); + same_first = false; + } + + if (!same_first) { + /* Only append pair label if we're not appending to array */ + flecs_json_next(buf); + flecs_json_path_or_label(buf, world, first, + desc ? desc->serialize_full_paths : true); + ecs_strbuf_appendlit(buf, ":"); + + /* Open array scope if this is a pair with multiple targets */ + if (is_same) { + flecs_json_array_push(buf); + same_first = true; + } + } + if (same_first) { + flecs_json_next(buf); + } + + if (second == EcsUnion) { + ecs_assert(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); + ecs_entity_t e = ecs_table_entities(table)[row]; + second = ecs_get_target(world, e, first, 0); + } + + flecs_json_path_or_label(buf, world, second, + desc ? desc->serialize_full_paths : true); + + pair_count ++; + } + + if (same_first) { + flecs_json_array_pop(buf); + } + + if (pair_count) { + flecs_json_object_pop(buf); + } + + return pair_count != 0; +} + +static +int flecs_json_serialize_table_components( + const ecs_world_t *world, + ecs_table_t *table, + const ecs_table_t *src_table, + ecs_strbuf_t *buf, + ecs_json_value_ser_ctx_t *values_ctx, + const ecs_iter_to_json_desc_t *desc, + int32_t row, + int32_t *component_count) +{ + int32_t i, count = table->type.count; + for (i = 0; i < count; i ++) { + if (component_count[0] == FLECS_JSON_MAX_TABLE_COMPONENTS) { + break; + } + + ecs_id_t id = table->type.array[i]; + if (!desc || !desc->serialize_builtin) { + if (flecs_json_is_builtin(id)) { + continue; + } + } + + void *ptr; + const ecs_table_record_t *tr = &table->_->records[i]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + + if (src_table) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + } + + const ecs_type_info_t *ti; + int32_t column_index = table->column_map ? table->column_map[i] : -1; + if (column_index != -1) { + ecs_column_t *column = &table->data.columns[column_index]; + ti = column->ti; + ptr = ECS_ELEM(column->data, ti->size, row); + } else { + if (!(idr->flags & EcsIdIsSparse)) { + continue; + } + ecs_entity_t e = ecs_table_entities(table)[row]; + ptr = flecs_sparse_get_any(idr->sparse, 0, e); + ti = idr->type_info; + } + + if (!ptr) { + continue; + } + + if (!component_count[0]) { + flecs_json_memberl(buf, "components"); + flecs_json_object_push(buf); + } + + bool has_reflection; + const EcsTypeSerializer *type_ser; + if (values_ctx) { + ecs_json_value_ser_ctx_t *value_ctx = + &values_ctx[component_count[0]]; + has_reflection = flecs_json_serialize_get_value_ctx( + world, id, value_ctx, desc); + flecs_json_member(buf, value_ctx->id_label); + type_ser = value_ctx->ser; + } else { + ecs_strbuf_list_next(buf); + ecs_strbuf_appendlit(buf, "\""); + flecs_json_id_member(buf, world, id, + desc ? desc->serialize_full_paths : true); + ecs_strbuf_appendlit(buf, "\":"); + type_ser = NULL; + if (!desc || desc->serialize_values) { + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + type_ser = ecs_get(world, ti->component, EcsTypeSerializer); + } + has_reflection = type_ser != NULL; + } + + component_count[0] ++; + + if (has_reflection && (!desc || desc->serialize_values)) { + ecs_assert(type_ser != NULL, ECS_INTERNAL_ERROR, NULL); + if (flecs_json_ser_type( + world, &type_ser->ops, ptr, buf) != 0) + { + goto error; + } + } else { + ecs_strbuf_appendlit(buf, "null"); + } + } + + if (component_count[0]) { + flecs_json_object_pop(buf); + } + + return 0; +error: + return -1; +} + +static +bool flecs_json_serialize_table_inherited_type( + const ecs_world_t *world, + ecs_table_t *table, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + if (!(table->flags & EcsTableHasIsA)) { + return false; + } + + const ecs_table_record_t *tr = flecs_id_record_get_table( + world->idr_isa_wildcard, table); + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); /* Table has IsA flag */ + + int32_t i, start = tr->index, end = start + tr->count; + for (i = start; i < end; i ++) { + ecs_entity_t base = ecs_pair_second(world, table->type.array[i]); + ecs_record_t *base_record = ecs_record_find(world, base); + if (!base_record || !base_record->table) { + continue; + } + + ecs_table_t *base_table = base_record->table; + flecs_json_serialize_table_inherited_type(world, base_table, buf, desc); + + char *base_name = ecs_get_path(world, base); + flecs_json_member(buf, base_name); + flecs_json_object_push(buf); + ecs_os_free(base_name); + + flecs_json_serialize_table_tags( + world, base_table, table, buf, desc); + + flecs_json_serialize_table_pairs( + world, base_table, table, ECS_RECORD_TO_ROW(base_record->row), + buf, desc); + + int32_t component_count = 0; + flecs_json_serialize_table_components( + world, base_table, table, buf, NULL, desc, + ECS_RECORD_TO_ROW(base_record->row), &component_count); + + if (desc->serialize_type_info) { + flecs_json_serialize_table_type_info( + world, base_table, buf, desc); + } + + flecs_json_object_pop(buf); + } + + return true; +} + +static +bool flecs_json_serialize_table_inherited( + const ecs_world_t *world, + ecs_table_t *table, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + if (!(table->flags & EcsTableHasIsA)) { + return false; + } + + flecs_json_memberl(buf, "inherited"); + flecs_json_object_push(buf); + flecs_json_serialize_table_inherited_type(world, table, buf, desc); + flecs_json_object_pop(buf); + return true; +} + +static +bool flecs_json_serialize_table_tags_pairs_vars( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_table_t *table, + int32_t row, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc) +{ + bool result = false; + ecs_strbuf_list_push(buf, "", ","); + result |= flecs_json_serialize_table_tags(world, table, NULL, buf, desc); + result |= flecs_json_serialize_table_pairs(world, table, NULL, row, buf, desc); + result |= flecs_json_serialize_vars(world, it, buf, desc); + if (desc->serialize_inherited) { + result |= flecs_json_serialize_table_inherited(world, table, buf, desc); + } + + if (desc->serialize_type_info) { + /* If we're serializing tables and are requesting type info, it must be + * added to each result. */ + result |= flecs_json_serialize_table_type_info(world, table, buf, desc); + } + + ecs_strbuf_list_pop(buf, ""); + if (!result) { + ecs_strbuf_reset(buf); + } + return result; +} + +int flecs_json_serialize_iter_result_table( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf, + const ecs_iter_to_json_desc_t *desc, + int32_t count, + bool has_this, + const char *parent_path, + const ecs_json_this_data_t *this_data) +{ + ecs_table_t *table = it->table; + if (!table || !count) { + return 0; + } + + /* Serialize tags, pairs, vars once, since they're the same for each row, + * except when table has union pairs, which can be different for each + * entity. */ + ecs_strbuf_t tags_pairs_vars_buf = ECS_STRBUF_INIT; + int32_t tags_pairs_vars_len = 0; + char *tags_pairs_vars = NULL; + bool has_union = table->flags & EcsTableHasUnion; + + if (!has_union) { + if (flecs_json_serialize_table_tags_pairs_vars( + world, it, table, 0, &tags_pairs_vars_buf, desc)) + { + tags_pairs_vars_len = ecs_strbuf_written(&tags_pairs_vars_buf); + tags_pairs_vars = ecs_strbuf_get(&tags_pairs_vars_buf); + } + } + + /* If one entity has more than 256 components (oof), bad luck */ + ecs_json_value_ser_ctx_t values_ctx[FLECS_JSON_MAX_TABLE_COMPONENTS] = {{0}}; + int32_t component_count = 0; + + int32_t i, end = it->offset + count; + int result = 0; + for (i = it->offset; i < end; i ++) { + flecs_json_next(buf); + flecs_json_object_push(buf); + + if (has_this) { + ecs_json_this_data_t this_data_cpy = *this_data; + flecs_json_serialize_iter_this( + it, parent_path, &this_data_cpy, i - it->offset, buf, desc); + } + + if (has_union) { + if (flecs_json_serialize_table_tags_pairs_vars( + world, it, table, i, &tags_pairs_vars_buf, desc)) + { + tags_pairs_vars_len = ecs_strbuf_written(&tags_pairs_vars_buf); + tags_pairs_vars = ecs_strbuf_get(&tags_pairs_vars_buf); + } + } + + if (tags_pairs_vars) { + ecs_strbuf_list_appendstrn(buf, + tags_pairs_vars, tags_pairs_vars_len); + } + + if (has_union) { + ecs_os_free(tags_pairs_vars); + tags_pairs_vars = NULL; + } + + component_count = 0; /* Each row has the same number of components */ + if (flecs_json_serialize_table_components( + world, table, NULL, buf, values_ctx, desc, i, &component_count)) + { + result = -1; + break; + } + + if (desc->serialize_matches) { + flecs_json_serialize_matches( + world, buf, it->entities[i - it->offset]); + } + + if (desc->serialize_refs) { + flecs_json_serialize_refs(world, buf, it->entities[i - it->offset], + desc->serialize_refs); + } + + if (desc->serialize_alerts) { + flecs_json_serialize_alerts(world, buf, + it->entities[i - it->offset]); + } + + flecs_json_object_pop(buf); + } + + for (i = 0; i < component_count; i ++) { + ecs_os_free(values_ctx[i].id_label); + } + + ecs_os_free(tags_pairs_vars); + + return result; +} + +#endif diff --git a/vendors/flecs/src/addons/json/serialize_query_info.c b/vendors/flecs/src/addons/json/serialize_query_info.c new file mode 100644 index 000000000..5d02ee681 --- /dev/null +++ b/vendors/flecs/src/addons/json/serialize_query_info.c @@ -0,0 +1,204 @@ +/** + * @file addons/json/serialize_query_info.c + * @brief Serialize (component) values to JSON strings. + */ + +#include "json.h" + +#ifdef FLECS_JSON + +static +const char* flecs_json_inout_str( + int16_t kind) +{ + switch(kind) { + case EcsIn: return "in"; + case EcsOut: return "out"; + case EcsInOut: return "inout"; + case EcsInOutNone: return "none"; + case EcsInOutFilter: return "filter"; + case EcsInOutDefault: return "default"; + default: return "unknown"; + } +} + +static +const char* flecs_json_oper_str( + int16_t kind) +{ + switch(kind) { + case EcsAnd: return "and"; + case EcsNot: return "not"; + case EcsOr: return "or"; + case EcsOptional: return "optional"; + case EcsAndFrom: return "andfrom"; + case EcsNotFrom: return "notfrom"; + case EcsOrFrom: return "orfrom"; + default: return "unknown"; + } +} + +static +void flecs_json_serialize_term_entity( + const ecs_world_t *world, + ecs_entity_t e, + ecs_strbuf_t *buf) +{ + flecs_json_memberl(buf, "entity"); + flecs_json_path(buf, world, e); + + if (e) { + const char *symbol = ecs_get_symbol(world, e); + if (symbol) { + flecs_json_memberl(buf, "symbol"); + flecs_json_string(buf, symbol); + } + + if (ecs_has(world, e, EcsComponent)) { + flecs_json_memberl(buf, "type"); + flecs_json_true(buf); + } + } +} + +static +void flecs_json_serialize_term_ref( + const ecs_world_t *world, + const ecs_term_ref_t *ref, + ecs_strbuf_t *buf) +{ + flecs_json_object_push(buf); + if (ref->id & EcsIsEntity) { + flecs_json_serialize_term_entity(world, ECS_TERM_REF_ID(ref), buf); + } else if (ref->id & EcsIsVariable) { + flecs_json_memberl(buf, "var"); + if (ref->name) { + flecs_json_string(buf, ref->name); + } else if (ref->id) { + if (ECS_TERM_REF_ID(ref) == EcsThis) { + flecs_json_string(buf, "this"); + } else { + flecs_json_path(buf, world, ECS_TERM_REF_ID(ref)); + } + } + } else if (ref->id & EcsIsName) { + flecs_json_memberl(buf, "name"); + flecs_json_string(buf, ref->name); + } + flecs_json_object_pop(buf); +} + +static +void flecs_json_serialize_term_trav( + const ecs_world_t *world, + const ecs_term_t *term, + ecs_strbuf_t *buf) +{ + if (term->trav) { + flecs_json_memberl(buf, "trav"); + flecs_json_object_push(buf); + flecs_json_serialize_term_entity(world, term->trav, buf); + flecs_json_object_pop(buf); + } + + flecs_json_memberl(buf, "flags"); + flecs_json_array_push(buf); + if (term->src.id & EcsSelf) { + flecs_json_next(buf); + flecs_json_string(buf, "self"); + } + if (term->src.id & EcsCascade) { + flecs_json_next(buf); + flecs_json_string(buf, "cascade"); + } else + if (term->src.id & EcsUp) { + flecs_json_next(buf); + flecs_json_string(buf, "up"); + } + flecs_json_array_pop(buf); +} + +static +void flecs_json_serialize_term( + const ecs_world_t *world, + const ecs_query_t *q, + int t, + ecs_strbuf_t *buf) +{ + const ecs_term_t *term = &q->terms[t]; + + flecs_json_object_push(buf); + flecs_json_memberl(buf, "inout"); + flecs_json_string(buf, flecs_json_inout_str(term->inout)); + + flecs_json_memberl(buf, "has_value"); + flecs_json_bool(buf, !!((1llu << term->field_index) & q->data_fields)); + + ecs_entity_t first_id = ECS_TERM_REF_ID(&term->first); + if (term->first.id & EcsIsEntity && first_id) { + if (ecs_has_pair(world, first_id, EcsOnInstantiate, EcsInherit)) { + flecs_json_memberl(buf, "can_inherit"); + flecs_json_true(buf); + } + } + + flecs_json_memberl(buf, "oper"); + flecs_json_string(buf, flecs_json_oper_str(term->oper)); + + flecs_json_memberl(buf, "src"); + flecs_json_serialize_term_ref(world, &term->src, buf); + + flecs_json_memberl(buf, "first"); + flecs_json_serialize_term_ref(world, &term->first, buf); + + if (ECS_TERM_REF_ID(&term->second) || term->second.name || term->second.id & EcsIsEntity) { + flecs_json_memberl(buf, "second"); + flecs_json_serialize_term_ref(world, &term->second, buf); + } + + flecs_json_serialize_term_trav(world, term, buf); + + flecs_json_object_pop(buf); +} + +void flecs_json_serialize_query( + const ecs_world_t *world, + const ecs_query_t *q, + ecs_strbuf_t *buf) +{ + flecs_json_object_push(buf); + + if (q->var_count) { + flecs_json_memberl(buf, "vars"); + flecs_json_array_push(buf); + int32_t v, first = 0; + + if (!(q->flags & EcsQueryMatchThis)) { + first = 1; + } + + for (v = first; v < q->var_count; v ++) { + flecs_json_next(buf); + if (q->vars[v]) { + flecs_json_string_escape(buf, q->vars[v]); + } else { + flecs_json_string(buf, "this"); + } + } + flecs_json_array_pop(buf); + } + + flecs_json_memberl(buf, "terms"); + flecs_json_array_push(buf); + int t; + for (t = 0; t < q->term_count; t ++) { + flecs_json_next(buf); + flecs_json_serialize_term(world, q, t, buf); + } + flecs_json_array_pop(buf); + + + flecs_json_object_pop(buf); +} + +#endif diff --git a/vendors/flecs/src/addons/json/serialize_type_info.c b/vendors/flecs/src/addons/json/serialize_type_info.c index 2c9bda916..731f43dde 100644 --- a/vendors/flecs/src/addons/json/serialize_type_info.c +++ b/vendors/flecs/src/addons/json/serialize_type_info.c @@ -1,5 +1,5 @@ /** - * @file json/serialize_type_info.c + * @file addons/json/serialize_type_info.c * @brief Serialize type (reflection) information to JSON. */ @@ -64,11 +64,8 @@ void json_typeinfo_ser_constants( ecs_entity_t type, ecs_strbuf_t *str) { - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_pair(EcsChildOf, type) - }); - - while (ecs_term_next(&it)) { + ecs_iter_t it = ecs_each_id(world, ecs_pair(EcsChildOf, type)); + while (ecs_each_next(&it)) { int32_t i, count = it.count; for (i = 0; i < count; i ++) { flecs_json_next(str); @@ -217,7 +214,8 @@ int json_typeinfo_ser_type_op( case EcsOpPush: case EcsOpPop: /* Should not be parsed as single op */ - ecs_throw(ECS_INVALID_PARAMETER, NULL); + ecs_throw(ECS_INVALID_PARAMETER, + "unexpected push/pop serializer instruction"); break; case EcsOpEnum: json_typeinfo_ser_enum(world, op->type, str); @@ -340,10 +338,21 @@ int json_typeinfo_ser_type_ops( ecs_assert(sp < 63, ECS_INVALID_OPERATION, "type nesting too deep"); stack[sp ++] = ecs_get(world, op->type, EcsStruct); break; - case EcsOpPop: + case EcsOpPop: { + ecs_entity_t unit = ecs_get_target_for(world, op->type, EcsIsA, EcsUnit); + if (unit) { + flecs_json_member(str, "@self"); + flecs_json_array_push(str); + flecs_json_object_push(str); + json_typeinfo_ser_unit(world, str, unit); + flecs_json_object_pop(str); + flecs_json_array_pop(str); + } + flecs_json_object_pop(str); sp --; break; + } case EcsOpArray: case EcsOpVector: case EcsOpEnum: @@ -395,8 +404,8 @@ int json_typeinfo_ser_type( return 0; } - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); if (!ser) { ecs_strbuf_appendch(buf, '0'); return 0; @@ -406,7 +415,11 @@ int json_typeinfo_ser_type( ecs_meta_type_op_t *ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t); int32_t count = ecs_vec_count(&ser->ops); - return json_typeinfo_ser_type_ops(world, ops, count, buf, st); + if (json_typeinfo_ser_type_ops(world, ops, count, buf, st)) { + return -1; + } + + return 0; } int ecs_type_info_to_json_buf( diff --git a/vendors/flecs/src/addons/json/serialize_value.c b/vendors/flecs/src/addons/json/serialize_value.c new file mode 100644 index 000000000..f0662c059 --- /dev/null +++ b/vendors/flecs/src/addons/json/serialize_value.c @@ -0,0 +1,595 @@ +/** + * @file addons/json/serialize_value.c + * @brief Serialize value to JSON. + */ + +#include "json.h" +#include "../meta/meta.h" + +#ifdef FLECS_JSON + +static +int flecs_json_ser_type_ops( + const ecs_world_t *world, + ecs_meta_type_op_t *ops, + int32_t op_count, + const void *base, + ecs_strbuf_t *str, + int32_t in_array); + +static +int flecs_json_ser_type_op( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str); + +/* Serialize enumeration */ +static +int flecs_json_ser_enum( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + const EcsEnum *enum_type = ecs_get(world, op->type, EcsEnum); + ecs_check(enum_type != NULL, ECS_INVALID_PARAMETER, NULL); + + int32_t value = *(const int32_t*)base; + + /* Enumeration constants are stored in a map that is keyed on the + * enumeration value. */ + ecs_enum_constant_t *constant = ecs_map_get_deref(&enum_type->constants, + ecs_enum_constant_t, (ecs_map_key_t)value); + if (!constant) { + /* If the value is not found, it is not a valid enumeration constant */ + char *name = ecs_get_path(world, op->type); + ecs_err("enumeration value '%d' of type '%s' is not a valid constant", + value, name); + ecs_os_free(name); + goto error; + } + + ecs_strbuf_appendch(str, '"'); + ecs_strbuf_appendstr(str, ecs_get_name(world, constant->constant)); + ecs_strbuf_appendch(str, '"'); + + return 0; +error: + return -1; +} + +/* Serialize bitmask */ +static +int flecs_json_ser_bitmask( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *ptr, + ecs_strbuf_t *str) +{ + const EcsBitmask *bitmask_type = ecs_get(world, op->type, EcsBitmask); + ecs_check(bitmask_type != NULL, ECS_INVALID_PARAMETER, NULL); + + uint32_t value = *(const uint32_t*)ptr; + if (!value) { + ecs_strbuf_appendch(str, '0'); + return 0; + } + + ecs_strbuf_list_push(str, "\"", "|"); + + /* Multiple flags can be set at a given time. Iterate through all the flags + * and append the ones that are set. */ + ecs_map_iter_t it = ecs_map_iter(&bitmask_type->constants); + while (ecs_map_next(&it)) { + ecs_bitmask_constant_t *constant = ecs_map_ptr(&it); + ecs_map_key_t key = ecs_map_key(&it); + if ((value & key) == key) { + ecs_strbuf_list_appendstr(str, + ecs_get_name(world, constant->constant)); + value -= (uint32_t)key; + } + } + + if (value != 0) { + /* All bits must have been matched by a constant */ + char *name = ecs_get_path(world, op->type); + ecs_err("bitmask value '%u' of type '%s' contains invalid/unknown bits", + value, name); + ecs_os_free(name); + goto error; + } + + ecs_strbuf_list_pop(str, "\""); + + return 0; +error: + return -1; +} + +/* Serialize elements of a contiguous array */ +static +int flecs_json_ser_elements( + const ecs_world_t *world, + ecs_meta_type_op_t *ops, + int32_t op_count, + const void *base, + int32_t elem_count, + int32_t elem_size, + ecs_strbuf_t *str, + bool is_array) +{ + flecs_json_array_push(str); + + const void *ptr = base; + + int i; + for (i = 0; i < elem_count; i ++) { + ecs_strbuf_list_next(str); + if (flecs_json_ser_type_ops(world, ops, op_count, ptr, str, is_array)) { + return -1; + } + ptr = ECS_OFFSET(ptr, elem_size); + } + + flecs_json_array_pop(str); + + return 0; +} + +static +int flecs_json_ser_type_elements( + const ecs_world_t *world, + ecs_entity_t type, + const void *base, + int32_t elem_count, + ecs_strbuf_t *str, + bool is_array) +{ + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); + ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); + + const EcsComponent *comp = ecs_get(world, type, EcsComponent); + ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_meta_type_op_t *ops = ecs_vec_first_t(&ser->ops, ecs_meta_type_op_t); + int32_t op_count = ecs_vec_count(&ser->ops); + + return flecs_json_ser_elements( + world, ops, op_count, base, elem_count, comp->size, str, is_array); +} + +/* Serialize array */ +static +int json_ser_array( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *ptr, + ecs_strbuf_t *str) +{ + const EcsArray *a = ecs_get(world, op->type, EcsArray); + ecs_assert(a != NULL, ECS_INTERNAL_ERROR, NULL); + + return flecs_json_ser_type_elements( + world, a->type, ptr, a->count, str, true); +} + +/* Serialize vector */ +static +int json_ser_vector( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + const ecs_vec_t *value = base; + const EcsVector *v = ecs_get(world, op->type, EcsVector); + ecs_assert(v != NULL, ECS_INTERNAL_ERROR, NULL); + + int32_t count = ecs_vec_count(value); + void *array = ecs_vec_first(value); + + /* Serialize contiguous buffer of vector */ + return flecs_json_ser_type_elements(world, v->type, array, count, str, false); +} + +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_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_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, + "entity %s in opaque type serializer instruction is not an opaque type", + ecs_get_name(world, op->type)); + ecs_assert(ct->as_type != 0, ECS_INVALID_OPERATION, + "opaque type %s has not populated as_type field", + ecs_get_name(world, op->type)); + ecs_assert(ct->serialize != NULL, ECS_INVALID_OPERATION, + "opaque type %s does not have serialize interface", + ecs_get_name(world, op->type)); + + const EcsType *pt = ecs_get(world, ct->as_type, EcsType); + ecs_assert(pt != NULL, ECS_INVALID_OPERATION, + "opaque type %s is missing flecs.meta.Type component", + ecs_get_name(world, op->type)); + + 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_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 flecs_json_ser_type_op( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *ptr, + ecs_strbuf_t *str) +{ + void *vptr = ECS_OFFSET(ptr, op->offset); + bool large_int = false; + if (op->kind == EcsOpI64) { + if (*(int64_t*)vptr >= 2147483648) { + large_int = true; + } + } else if (op->kind == EcsOpU64) { + if (*(uint64_t*)vptr >= 2147483648) { + large_int = true; + } + } + + if (large_int) { + ecs_strbuf_appendch(str, '"'); + } + + switch(op->kind) { + case EcsOpPush: + case EcsOpPop: + /* Should not be parsed as single op */ + ecs_throw(ECS_INVALID_PARAMETER, NULL); + break; + case EcsOpF32: + ecs_strbuf_appendflt(str, + (ecs_f64_t)*(const ecs_f32_t*)vptr, '"'); + break; + case EcsOpF64: + ecs_strbuf_appendflt(str, + *(ecs_f64_t*)vptr, '"'); + break; + case EcsOpEnum: + if (flecs_json_ser_enum(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpBitmask: + if (flecs_json_ser_bitmask(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpArray: + if (json_ser_array(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpVector: + if (json_ser_vector(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpOpaque: + if (json_ser_custom_type(world, op, vptr, str)) { + goto error; + } + break; + case EcsOpEntity: { + ecs_entity_t e = *(const ecs_entity_t*)vptr; + if (!e) { + ecs_strbuf_appendlit(str, "\"#0\""); + } else { + flecs_json_path(str, world, e); + } + break; + } + case EcsOpId: { + ecs_id_t id = *(const ecs_id_t*)vptr; + if (!id) { + ecs_strbuf_appendlit(str, "\"#0\""); + } else { + flecs_json_id(str, world, id); + } + break; + } + + case EcsOpU64: + case EcsOpI64: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + if (flecs_expr_ser_primitive(world, + flecs_json_op_to_primitive_kind(op->kind), + ECS_OFFSET(ptr, op->offset), str, true)) + { + ecs_throw(ECS_INTERNAL_ERROR, NULL); + } + break; + + case EcsOpPrimitive: + case EcsOpScope: + default: + ecs_throw(ECS_INTERNAL_ERROR, NULL); + } + + if (large_int) { + ecs_strbuf_appendch(str, '"'); + } + + return 0; +error: + return -1; +} + +/* Iterate over a slice of the type ops array */ +static +int flecs_json_ser_type_ops( + const ecs_world_t *world, + ecs_meta_type_op_t *ops, + int32_t op_count, + const void *base, + ecs_strbuf_t *str, + int32_t in_array) +{ + for (int i = 0; i < op_count; i ++) { + ecs_meta_type_op_t *op = &ops[i]; + + if (in_array <= 0) { + if (op->name) { + flecs_json_member(str, op->name); + } + + int32_t elem_count = op->count; + if (elem_count > 1) { + /* Serialize inline array */ + if (flecs_json_ser_elements(world, op, op->op_count, base, + elem_count, op->size, str, true)) + { + return -1; + } + + i += op->op_count - 1; + continue; + } + } + + switch(op->kind) { + case EcsOpPush: + flecs_json_object_push(str); + in_array --; + break; + case EcsOpPop: + flecs_json_object_pop(str); + in_array ++; + break; + case EcsOpArray: + case EcsOpVector: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpEntity: + case EcsOpId: + case EcsOpString: + case EcsOpOpaque: + if (flecs_json_ser_type_op(world, op, base, str)) { + goto error; + } + break; + default: + ecs_throw(ECS_INTERNAL_ERROR, NULL); + } + } + + return 0; +error: + return -1; +} + +/* Iterate over the type ops of a type */ +int flecs_json_ser_type( + const ecs_world_t *world, + const ecs_vec_t *v_ops, + const void *base, + ecs_strbuf_t *str) +{ + ecs_meta_type_op_t *ops = ecs_vec_first_t(v_ops, ecs_meta_type_op_t); + int32_t count = ecs_vec_count(v_ops); + return flecs_json_ser_type_ops(world, ops, count, base, str, 0); +} + +static +int flecs_array_to_json_buf_w_type_data( + const ecs_world_t *world, + const void *ptr, + int32_t count, + ecs_strbuf_t *buf, + const EcsComponent *comp, + const EcsTypeSerializer *ser) +{ + if (count) { + ecs_size_t size = comp->size; + + flecs_json_array_push(buf); + + do { + ecs_strbuf_list_next(buf); + if (flecs_json_ser_type(world, &ser->ops, ptr, buf)) { + return -1; + } + + ptr = ECS_OFFSET(ptr, size); + } while (-- count); + + flecs_json_array_pop(buf); + } else { + if (flecs_json_ser_type(world, &ser->ops, ptr, buf)) { + return -1; + } + } + + return 0; +} + +int ecs_array_to_json_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *ptr, + int32_t count, + ecs_strbuf_t *buf) +{ + const EcsComponent *comp = ecs_get(world, type, EcsComponent); + if (!comp) { + char *path = ecs_get_path(world, type); + ecs_err("cannot serialize to JSON, '%s' is not a component", path); + ecs_os_free(path); + return -1; + } + + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); + if (!ser) { + char *path = ecs_get_path(world, type); + ecs_err("cannot serialize to JSON, '%s' has no reflection data", path); + ecs_os_free(path); + return -1; + } + + return flecs_array_to_json_buf_w_type_data(world, ptr, count, buf, comp, ser); +} + +char* ecs_array_to_json( + const ecs_world_t *world, + ecs_entity_t type, + const void* ptr, + int32_t count) +{ + ecs_strbuf_t str = ECS_STRBUF_INIT; + + if (ecs_array_to_json_buf(world, type, ptr, count, &str) != 0) { + ecs_strbuf_reset(&str); + return NULL; + } + + return ecs_strbuf_get(&str); +} + +int ecs_ptr_to_json_buf( + const ecs_world_t *world, + ecs_entity_t type, + const void *ptr, + ecs_strbuf_t *buf) +{ + return ecs_array_to_json_buf(world, type, ptr, 0, buf); +} + +char* ecs_ptr_to_json( + const ecs_world_t *world, + ecs_entity_t type, + const void* ptr) +{ + return ecs_array_to_json(world, type, ptr, 0); +} + +#endif diff --git a/vendors/flecs/src/addons/json/serialize_world.c b/vendors/flecs/src/addons/json/serialize_world.c new file mode 100644 index 000000000..936804283 --- /dev/null +++ b/vendors/flecs/src/addons/json/serialize_world.c @@ -0,0 +1,73 @@ +/** + * @file addons/json/serialize_world.c + * @brief Serialize world to JSON. + */ + +#include "json.h" +#include "../meta/meta.h" + +#ifdef FLECS_JSON + +int ecs_world_to_json_buf( + ecs_world_t *world, + ecs_strbuf_t *buf_out, + const ecs_world_to_json_desc_t *desc) +{ + ecs_query_desc_t query_desc = {0}; + + if (desc && desc->serialize_builtin && desc->serialize_modules) { + query_desc.terms[0].id = EcsAny; + } else { + bool serialize_builtin = desc && desc->serialize_builtin; + bool serialize_modules = desc && desc->serialize_modules; + int32_t term_id = 0; + + if (!serialize_builtin) { + query_desc.terms[term_id].id = ecs_pair(EcsChildOf, EcsFlecs); + query_desc.terms[term_id].oper = EcsNot; + query_desc.terms[term_id].src.id = EcsSelf | EcsUp; + term_id ++; + } + if (!serialize_modules) { + query_desc.terms[term_id].id = EcsModule; + query_desc.terms[term_id].oper = EcsNot; + query_desc.terms[term_id].src.id = EcsSelf | EcsUp; + } + } + + query_desc.flags = EcsQueryMatchDisabled|EcsQueryMatchPrefab; + + ecs_query_t *q = ecs_query_init(world, &query_desc); + if (!q) { + return -1; + } + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t json_desc = { + .serialize_table = true, + .serialize_full_paths = true, + .serialize_entity_ids = true, + .serialize_values = true + }; + + int ret = ecs_iter_to_json_buf(&it, buf_out, &json_desc); + ecs_query_fini(q); + return ret; +} + +char* ecs_world_to_json( + ecs_world_t *world, + const ecs_world_to_json_desc_t *desc) +{ + ecs_strbuf_t buf = ECS_STRBUF_INIT; + + if (ecs_world_to_json_buf(world, &buf, desc)) { + ecs_strbuf_reset(&buf); + return NULL; + } + + return ecs_strbuf_get(&buf); +} + +#endif + diff --git a/vendors/flecs/src/addons/log.c b/vendors/flecs/src/addons/log.c index 2de4a2f2c..713a3083e 100644 --- a/vendors/flecs/src/addons/log.c +++ b/vendors/flecs/src/addons/log.c @@ -73,7 +73,7 @@ void flecs_colorize_buf( if (enable_colors) ecs_strbuf_appendlit(buf, ECS_GREEN); } else if (!ecs_os_strncmp(&ptr[2], "red]", ecs_os_strlen("red]"))) { if (enable_colors) ecs_strbuf_appendlit(buf, ECS_RED); - } else if (!ecs_os_strncmp(&ptr[2], "blue]", ecs_os_strlen("red]"))) { + } else if (!ecs_os_strncmp(&ptr[2], "blue]", ecs_os_strlen("blue]"))) { if (enable_colors) ecs_strbuf_appendlit(buf, ECS_BLUE); } else if (!ecs_os_strncmp(&ptr[2], "magenta]", ecs_os_strlen("magenta]"))) { if (enable_colors) ecs_strbuf_appendlit(buf, ECS_MAGENTA); @@ -149,7 +149,7 @@ void ecs_printv_( /* Apply color. Even if we don't want color, we still need to call the * colorize function to get rid of the color tags (e.g. #[green]) */ - char *msg_nocolor = ecs_vasprintf(fmt, args); + char *msg_nocolor = flecs_vasprintf(fmt, args); flecs_colorize_buf(msg_nocolor, ecs_os_api.flags_ & EcsOsApiLogWithColors, &msg_buf); ecs_os_free(msg_nocolor); @@ -238,11 +238,24 @@ void ecs_parser_errorv_( * function is called with (expr - ptr), and expr is NULL. */ column_arg = 0; } + int32_t column = flecs_itoi32(column_arg); if (ecs_os_api.log_level_ >= -2) { ecs_strbuf_t msg_buf = ECS_STRBUF_INIT; + /* Count number of newlines up until column_arg */ + int32_t i, line = 1; + if (expr) { + for (i = 0; i < column; i ++) { + if (expr[i] == '\n') { + line ++; + } + } + + ecs_strbuf_append(&msg_buf, "%d: ", line); + } + ecs_strbuf_vappend(&msg_buf, fmt, args); if (expr) { @@ -252,15 +265,23 @@ void ecs_parser_errorv_( * last occurring newline */ if (column != -1) { const char *ptr = &expr[column]; + if (ptr[0] == '\n') { + ptr --; + } + while (ptr[0] != '\n' && ptr > expr) { ptr --; } + + if (ptr[0] == '\n') { + ptr ++; + } if (ptr == expr) { /* ptr is already at start of line */ } else { - column -= (int32_t)(ptr - expr + 1); - expr = ptr + 1; + column -= (int32_t)(ptr - expr); + expr = ptr; } } @@ -316,7 +337,7 @@ void ecs_abort_( if (fmt) { va_list args; va_start(args, fmt); - char *msg = ecs_vasprintf(fmt, args); + char *msg = flecs_vasprintf(fmt, args); va_end(args); ecs_fatal_(file, line, "%s (%s)", msg, ecs_strerror(err)); ecs_os_free(msg); @@ -337,7 +358,7 @@ void ecs_assert_log_( if (fmt) { va_list args; va_start(args, fmt); - char *msg = ecs_vasprintf(fmt, args); + char *msg = flecs_vasprintf(fmt, args); va_end(args); ecs_fatal_(file, line, "assert: %s %s (%s)", cond_str, msg, ecs_strerror(err)); @@ -503,7 +524,7 @@ int ecs_log_get_level(void) { int ecs_log_set_level( int level) { - int prev = level; + int prev = ecs_os_api.log_level_; ecs_os_api.log_level_ = level; return prev; } diff --git a/vendors/flecs/src/addons/meta/api.c b/vendors/flecs/src/addons/meta/api.c index 26be9809a..6c586212e 100644 --- a/vendors/flecs/src/addons/meta/api.c +++ b/vendors/flecs/src/addons/meta/api.c @@ -1,5 +1,5 @@ /** - * @file meta/api.c + * @file addons/meta/api.c * @brief API for creating entities with reflection data. */ @@ -44,6 +44,126 @@ bool flecs_type_is_number( } } +/* Serialize a primitive value */ +int flecs_expr_ser_primitive( + const ecs_world_t *world, + ecs_primitive_kind_t kind, + const void *base, + ecs_strbuf_t *str, + bool is_expr) +{ + switch(kind) { + case EcsBool: + if (*(const bool*)base) { + ecs_strbuf_appendlit(str, "true"); + } else { + ecs_strbuf_appendlit(str, "false"); + } + break; + case EcsChar: { + char chbuf[3]; + char ch = *(const char*)base; + if (ch) { + flecs_chresc(chbuf, *(const char*)base, '"'); + if (is_expr) ecs_strbuf_appendch(str, '"'); + ecs_strbuf_appendstr(str, chbuf); + if (is_expr) ecs_strbuf_appendch(str, '"'); + } else { + ecs_strbuf_appendch(str, '0'); + } + break; + } + case EcsByte: + ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint8_t*)base)); + break; + case EcsU8: + ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint8_t*)base)); + break; + case EcsU16: + ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint16_t*)base)); + break; + case EcsU32: + ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint32_t*)base)); + break; + case EcsU64: + ecs_strbuf_append(str, "%llu", *(const uint64_t*)base); + break; + case EcsI8: + ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int8_t*)base)); + break; + case EcsI16: + ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int16_t*)base)); + break; + case EcsI32: + ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int32_t*)base)); + break; + case EcsI64: + ecs_strbuf_appendint(str, *(const int64_t*)base); + break; + case EcsF32: + ecs_strbuf_appendflt(str, (double)*(const float*)base, 0); + break; + case EcsF64: + ecs_strbuf_appendflt(str, *(const double*)base, 0); + break; + case EcsIPtr: + ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const intptr_t*)base)); + break; + case EcsUPtr: + ecs_strbuf_append(str, "%u", *(const uintptr_t*)base); + break; + case EcsString: { + const char *value = *ECS_CONST_CAST(const char**, base); + if (value) { + if (!is_expr) { + ecs_strbuf_appendstr(str, value); + } else { + ecs_size_t length = flecs_stresc(NULL, 0, '"', value); + if (length == ecs_os_strlen(value)) { + ecs_strbuf_appendch(str, '"'); + ecs_strbuf_appendstrn(str, value, length); + ecs_strbuf_appendch(str, '"'); + } else { + char *out = ecs_os_malloc(length + 3); + flecs_stresc(out + 1, length, '"', value); + out[0] = '"'; + out[length + 1] = '"'; + out[length + 2] = '\0'; + ecs_strbuf_appendstr(str, out); + ecs_os_free(out); + } + } + } else { + ecs_strbuf_appendlit(str, "null"); + } + break; + } + case EcsEntity: { + ecs_entity_t e = *(const ecs_entity_t*)base; + if (!e) { + ecs_strbuf_appendlit(str, "#0"); + } else { + ecs_get_path_w_sep_buf(world, 0, e, ".", NULL, str, false); + } + break; + } + case EcsId: { + ecs_id_t id = *(const ecs_id_t*)base; + if (!id) { + ecs_strbuf_appendlit(str, "#0"); + } else { + ecs_id_str_buf(world, id, str); + } + break; + } + default: + ecs_err("invalid primitive kind"); + return -1; + } + + return 0; +} + ecs_entity_t ecs_primitive_init( ecs_world_t *world, const ecs_primitive_desc_t *desc) @@ -92,7 +212,7 @@ ecs_entity_t ecs_enum_init( if (!m_desc->value) { ecs_add_id(world, c, EcsConstant); } else { - ecs_set_pair_object(world, c, EcsConstant, ecs_i32_t, + ecs_set_pair_second(world, c, EcsConstant, ecs_i32_t, {m_desc->value}); } } @@ -139,7 +259,7 @@ ecs_entity_t ecs_bitmask_init( if (!m_desc->value) { ecs_add_id(world, c, EcsConstant); } else { - ecs_set_pair_object(world, c, EcsConstant, ecs_u32_t, + ecs_set_pair_second(world, c, EcsConstant, ecs_u32_t, {m_desc->value}); } } @@ -256,7 +376,8 @@ ecs_entity_t ecs_struct_init( .type = m_desc->type, .count = m_desc->count, .offset = m_desc->offset, - .unit = m_desc->unit + .unit = m_desc->unit, + .use_offset = m_desc->use_offset }); EcsMemberRanges *ranges = NULL; @@ -264,9 +385,9 @@ ecs_entity_t ecs_struct_init( const ecs_member_value_range_t *error = &m_desc->error_range; const ecs_member_value_range_t *warning = &m_desc->warning_range; if (ECS_NEQ(range->min, range->max)) { - ranges = ecs_get_mut(world, m, EcsMemberRanges); + ranges = ecs_ensure(world, m, EcsMemberRanges); if (range->min > range->max) { - char *member_name = ecs_get_fullpath(world, m); + char *member_name = ecs_get_path(world, m); ecs_err("member '%s' has an invalid value range [%f..%f]", member_name, range->min, range->max); ecs_os_free(member_name); @@ -277,21 +398,21 @@ ecs_entity_t ecs_struct_init( } if (ECS_NEQ(error->min, error->max)) { if (error->min > error->max) { - char *member_name = ecs_get_fullpath(world, m); + char *member_name = ecs_get_path(world, m); ecs_err("member '%s' has an invalid error range [%f..%f]", member_name, error->min, error->max); ecs_os_free(member_name); goto error; } if (flecs_member_range_overlaps(error, range)) { - char *member_name = ecs_get_fullpath(world, m); + char *member_name = ecs_get_path(world, m); ecs_err("error range of member '%s' overlaps with value range", member_name); ecs_os_free(member_name); goto error; } if (!ranges) { - ranges = ecs_get_mut(world, m, EcsMemberRanges); + ranges = ecs_ensure(world, m, EcsMemberRanges); } ranges->error.min = error->min; ranges->error.max = error->max; @@ -299,21 +420,21 @@ ecs_entity_t ecs_struct_init( if (ECS_NEQ(warning->min, warning->max)) { if (warning->min > warning->max) { - char *member_name = ecs_get_fullpath(world, m); + char *member_name = ecs_get_path(world, m); ecs_err("member '%s' has an invalid warning range [%f..%f]", member_name, warning->min, warning->max); ecs_os_free(member_name); goto error; } if (flecs_member_range_overlaps(warning, range)) { - char *member_name = ecs_get_fullpath(world, m); + char *member_name = ecs_get_path(world, m); ecs_err("warning range of member '%s' overlaps with value " "range", member_name); ecs_os_free(member_name); goto error; } if (flecs_member_range_overlaps(warning, error)) { - char *member_name = ecs_get_fullpath(world, m); + char *member_name = ecs_get_path(world, m); ecs_err("warning range of member '%s' overlaps with error " "range", member_name); ecs_os_free(member_name); @@ -321,14 +442,14 @@ ecs_entity_t ecs_struct_init( } if (!ranges) { - ranges = ecs_get_mut(world, m, EcsMemberRanges); + ranges = ecs_ensure(world, m, EcsMemberRanges); } ranges->warning.min = warning->min; ranges->warning.max = warning->max; } if (ranges && !flecs_type_is_number(world, m_desc->type)) { - char *member_name = ecs_get_fullpath(world, m); + char *member_name = ecs_get_path(world, m); ecs_err("member '%s' has an value/error/warning range, but is not a " "number", member_name); ecs_os_free(member_name); @@ -408,7 +529,7 @@ ecs_entity_t ecs_unit_init( ecs_remove_pair(world, t, EcsQuantity, EcsWildcard); } - EcsUnit *value = ecs_get_mut(world, t, EcsUnit); + EcsUnit *value = ecs_ensure(world, t, EcsUnit); value->base = desc->base; value->over = desc->over; value->translation = desc->translation; diff --git a/vendors/flecs/src/addons/meta_c.c b/vendors/flecs/src/addons/meta/c_utils.c similarity index 94% rename from vendors/flecs/src/addons/meta_c.c rename to vendors/flecs/src/addons/meta/c_utils.c index 104d56afa..1feee3e2c 100644 --- a/vendors/flecs/src/addons/meta_c.c +++ b/vendors/flecs/src/addons/meta/c_utils.c @@ -1,11 +1,11 @@ /** - * @file addons/meta_c.c + * @file addons/meta/c_utils.c * @brief C utilities for meta addon. */ -#include "../private_api.h" +#include "../../private_api.h" -#ifdef FLECS_META_C +#ifdef FLECS_META #include @@ -93,15 +93,15 @@ const char* parse_c_digit( int64_t *value_out) { char token[24]; - ptr = ecs_parse_ws_eol(ptr); - ptr = ecs_parse_digit(ptr, token); + ptr = flecs_parse_ws_eol(ptr); + ptr = flecs_parse_digit(ptr, token); if (!ptr) { goto error; } *value_out = strtol(token, NULL, 0); - return ecs_parse_ws_eol(ptr); + return flecs_parse_ws_eol(ptr); error: return NULL; } @@ -124,7 +124,7 @@ const char* parse_c_identifier( } /* Ignore whitespaces */ - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); ch = *ptr; if (!isalpha(ch) && (ch != '_')) { @@ -172,7 +172,7 @@ const char * meta_open_scope( meta_parse_ctx_t *ctx) { /* Skip initial whitespaces */ - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); /* Is this the start of the type definition? */ if (ctx->desc == ptr) { @@ -182,7 +182,7 @@ const char * meta_open_scope( } ptr ++; - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); } /* Is this the end of the type definition? */ @@ -193,7 +193,7 @@ const char * meta_open_scope( /* Is this the end of the type definition? */ if (*ptr == '}') { - ptr = ecs_parse_ws_eol(ptr + 1); + ptr = flecs_parse_ws_eol(ptr + 1); if (*ptr) { ecs_meta_error(ctx, ptr, "stray characters after struct definition"); @@ -226,7 +226,7 @@ const char* meta_parse_constant( return NULL; } - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); if (!ptr) { return NULL; } @@ -263,7 +263,7 @@ const char* meta_parse_type( token->is_ptr = false; token->is_const = false; - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); /* Parse token, expect type identifier or ECS_PROPERTY */ ptr = parse_c_identifier(ptr, token->type, token->params, ctx); @@ -286,7 +286,7 @@ const char* meta_parse_type( } /* Check if type is a pointer */ - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); if (*ptr == '*') { token->is_ptr = true; ptr ++; @@ -330,7 +330,7 @@ const char* meta_parse_member( } /* Skip whitespace between member and [ or ; */ - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); /* Check if this is an array */ char *array_start = strchr(token->name, '['); @@ -361,7 +361,7 @@ const char* meta_parse_member( /* If [ was found after name, continue parsing after ] */ ptr = array_end + 1; } else { - /* If [ was fonud in name, replace it with 0 terminator */ + /* If [ was found in name, replace it with 0 terminator */ array_start[0] = '\0'; } } @@ -386,7 +386,7 @@ int meta_parse_desc( token->is_key_value = false; token->is_fixed_size = false; - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); if (*ptr != '(' && *ptr != '<') { ecs_meta_error(ctx, ptr, "expected '(' at start of collection definition"); @@ -401,11 +401,11 @@ int meta_parse_desc( goto error; } - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); /* If next token is a ',' the first type was a key type */ if (*ptr == ',') { - ptr = ecs_parse_ws_eol(ptr + 1); + ptr = flecs_parse_ws_eol(ptr + 1); if (isdigit(*ptr)) { int64_t value; @@ -421,7 +421,7 @@ int meta_parse_desc( /* Parse element type */ ptr = meta_parse_type(ptr, &token->type, ctx); - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); token->is_key_value = true; } @@ -480,12 +480,14 @@ ecs_entity_t meta_lookup_array( } if (!e) { - e = ecs_new_id(world); + e = ecs_new(world); } ecs_check(params.count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL); - return ecs_set(world, e, EcsArray, { element_type, (int32_t)params.count }); + ecs_set(world, e, EcsArray, { element_type, (int32_t)params.count }); + + return e; error: return 0; } @@ -517,10 +519,12 @@ ecs_entity_t meta_lookup_vector( world, ¶ms.type, params_decl, 1, ¶m_ctx); if (!e) { - e = ecs_new_id(world); + e = ecs_new(world); } - return ecs_set(world, e, EcsVector, { element_type }); + ecs_set(world, e, EcsVector, { element_type }); + + return e; error: return 0; } @@ -562,7 +566,7 @@ ecs_entity_t meta_lookup_bitmask( #ifndef FLECS_NDEBUG /* Make sure this is a bitmask type */ - const EcsMetaType *type_ptr = ecs_get(world, bitmask_type, EcsMetaType); + const EcsType *type_ptr = ecs_get(world, bitmask_type, EcsType); ecs_check(type_ptr != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(type_ptr->kind == EcsBitmaskType, ECS_INVALID_PARAMETER, NULL); #endif @@ -665,7 +669,7 @@ ecs_entity_t meta_lookup( if (count != 1) { ecs_check(count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL); - type = ecs_set(world, 0, EcsArray, {type, (int32_t)count}); + type = ecs_insert(world, ecs_value(EcsArray, {type, (int32_t)count})); } if (!type) { @@ -773,10 +777,10 @@ int meta_parse_constants( }); if (!is_bitmask) { - ecs_set_pair_object(world, c, EcsConstant, ecs_i32_t, + ecs_set_pair_second(world, c, EcsConstant, ecs_i32_t, {(ecs_i32_t)last_value}); } else { - ecs_set_pair_object(world, c, EcsConstant, ecs_u32_t, + ecs_set_pair_second(world, c, EcsConstant, ecs_u32_t, {(ecs_u32_t)last_value}); } diff --git a/vendors/flecs/src/addons/meta/cursor.c b/vendors/flecs/src/addons/meta/cursor.c index 1f7002008..17d6439dc 100644 --- a/vendors/flecs/src/addons/meta/cursor.c +++ b/vendors/flecs/src/addons/meta/cursor.c @@ -1,5 +1,5 @@ /** - * @file meta/cursor.c + * @file addons/meta/cursor.c * @brief API for assigning values of runtime types with reflection. */ @@ -7,8 +7,8 @@ #include #ifdef FLECS_META -#ifdef FLECS_PARSER -#include "flecs/addons/parser.h" +#ifdef FLECS_SCRIPT +#include "../script/script.h" #endif static @@ -79,7 +79,8 @@ static ecs_meta_type_op_t* flecs_meta_cursor_get_op( ecs_meta_scope_t *scope) { - ecs_assert(scope->ops != NULL, ECS_INVALID_OPERATION, NULL); + ecs_assert(scope->ops != NULL, ECS_INVALID_OPERATION, + "type serializer is missing instructions"); return &scope->ops[scope->op_cur]; } @@ -138,7 +139,7 @@ ecs_meta_type_op_t* flecs_meta_cursor_get_ptr( } else if (opaque) { if (scope->is_collection) { if (!opaque->ensure_element) { - char *str = ecs_get_fullpath(world, scope->type); + char *str = ecs_get_path(world, scope->type); ecs_err("missing ensure_element for opaque type %s", str); ecs_os_free(str); return NULL; @@ -152,7 +153,7 @@ ecs_meta_type_op_t* flecs_meta_cursor_get_ptr( return opaque_ptr; } else if (op->name) { if (!opaque->ensure_member) { - char *str = ecs_get_fullpath(world, scope->type); + char *str = ecs_get_path(world, scope->type); ecs_err("missing ensure_member for opaque type %s", str); ecs_os_free(str); return NULL; @@ -175,11 +176,11 @@ int flecs_meta_cursor_push_type( ecs_entity_t type, void *ptr) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); if (ser == NULL) { char *str = ecs_id_str(world, type); - ecs_err("cannot open scope for entity '%s' which is not a type", str); + ecs_err("cannot open scope for '%s' (missing reflection data)", str); ecs_os_free(str); return -1; } @@ -298,7 +299,7 @@ int ecs_meta_member( const uint64_t *cur_ptr = flecs_name_index_find_ptr(members, name, 0, 0); if (!cur_ptr) { - char *path = ecs_get_fullpath(world, scope->type); + char *path = ecs_get_path(world, scope->type); ecs_err("unknown member '%s' for type '%s'", name, path); ecs_os_free(path); return -1; @@ -309,7 +310,7 @@ int ecs_meta_member( const EcsOpaque *opaque = scope->opaque; if (opaque) { if (!opaque->ensure_member) { - char *str = ecs_get_fullpath(world, scope->type); + char *str = ecs_get_path(world, scope->type); ecs_err("missing ensure_member for opaque type %s", str); ecs_os_free(str); } @@ -318,11 +319,33 @@ int ecs_meta_member( return 0; } +static +const char* flecs_meta_parse_member( + const char *start, + char *token_out) +{ + const char *ptr; + char ch; + for (ptr = start; (ch = *ptr); ptr ++) { + if (ch == '.') { + break; + } + } + + int32_t len = flecs_ito(int32_t, ptr - start); + ecs_os_memcpy(token_out, start, len); + token_out[len] = '\0'; + if (ch == '.') { + ptr ++; + } + + return ptr; +} + int ecs_meta_dotmember( ecs_meta_cursor_t *cursor, const char *name) { -#ifdef FLECS_PARSER ecs_meta_scope_t *cur_scope = flecs_meta_cursor_get_scope(cursor); flecs_meta_cursor_restore_scope(cursor, cur_scope); @@ -331,13 +354,7 @@ int ecs_meta_dotmember( char token[ECS_MAX_TOKEN_SIZE]; const char *ptr = name; - while ((ptr = ecs_parse_token(NULL, NULL, ptr, token, '.'))) { - if (ptr[0] != '.' && ptr[0]) { - ecs_parser_error(NULL, name, ptr - name, - "expected '.' or end of string"); - goto error; - } - + while ((ptr = flecs_meta_parse_member(ptr, token))) { if (dotcount) { ecs_meta_push(cursor); } @@ -350,8 +367,6 @@ int ecs_meta_dotmember( break; } - ptr ++; /* Skip . */ - dotcount ++; } @@ -363,12 +378,6 @@ int ecs_meta_dotmember( return 0; error: return -1; -#else - (void)cursor; - (void)name; - ecs_err("the FLECS_PARSER addon is required for ecs_meta_dotmember"); - return -1; -#endif } int ecs_meta_push( @@ -484,7 +493,7 @@ int ecs_meta_push( case EcsOpOpaque: { const EcsOpaque *type_ptr = ecs_get(world, op->type, EcsOpaque); ecs_entity_t as_type = type_ptr->as_type; - const EcsMetaType *mtype_ptr = ecs_get(world, as_type, EcsMetaType); + const EcsType *mtype_ptr = ecs_get(world, as_type, EcsType); /* Check what kind of type the opaque type represents */ switch(mtype_ptr->kind) { @@ -581,7 +590,7 @@ int ecs_meta_push( case EcsOpString: case EcsOpEntity: case EcsOpId: { - char *path = ecs_get_fullpath(world, scope->type); + char *path = ecs_get_path(world, scope->type); ecs_err("invalid push for type '%s'", path); ecs_os_free(path); goto error; @@ -631,18 +640,18 @@ int ecs_meta_pop( } else if (op->kind == EcsOpOpaque) { const EcsOpaque *opaque = scope->opaque; if (scope->is_collection) { - const EcsMetaType *mtype = ecs_get(cursor->world, - opaque->as_type, EcsMetaType); + const EcsType *mtype = ecs_get(cursor->world, + opaque->as_type, EcsType); ecs_assert(mtype != NULL, ECS_INTERNAL_ERROR, NULL); - /* When popping a opaque collection type, call resize to make + /* When popping an opaque collection type, call resize to make * sure the vector isn't larger than the number of elements we * deserialized. * If the opaque type represents an array, don't call resize. */ if (mtype->kind != EcsArrayType) { ecs_assert(opaque != NULL, ECS_INTERNAL_ERROR, NULL); if (!opaque->resize) { - char *str = ecs_get_fullpath(cursor->world, scope->type); + char *str = ecs_get_path(cursor->world, scope->type); ecs_err("missing resize for opaque type %s", str); ecs_os_free(str); return -1; @@ -858,7 +867,7 @@ void flecs_meta_conversion_error( if (op->kind == EcsOpPop) { ecs_err("cursor: out of bounds"); } else { - char *path = ecs_get_fullpath(cursor->world, op->type); + char *path = ecs_get_path(cursor->world, op->type); ecs_err("unsupported conversion from %s to '%s'", from, path); ecs_os_free(path); } @@ -917,7 +926,9 @@ int ecs_meta_set_char( cases_T_signed(ptr, value, ecs_meta_bounds_signed); case EcsOpOpaque: { const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); if (opaque->assign_char) { /* preferred operation */ opaque->assign_char(ptr, value); break; @@ -974,7 +985,9 @@ int ecs_meta_set_int( cases_T_float(ptr, value); case EcsOpOpaque: { const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); if (opaque->assign_int) { /* preferred operation */ opaque->assign_int(ptr, value); break; @@ -1025,7 +1038,9 @@ int ecs_meta_set_uint( cases_T_float(ptr, value); case EcsOpOpaque: { const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); if (opaque->assign_uint) { /* preferred operation */ opaque->assign_uint(ptr, value); break; @@ -1081,7 +1096,9 @@ int ecs_meta_set_float( cases_T_float(ptr, value); case EcsOpOpaque: { const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); if (opaque->assign_float) { /* preferred operation */ opaque->assign_float(ptr, value); break; @@ -1126,7 +1143,7 @@ int ecs_meta_set_value( ecs_check(value != NULL, ECS_INVALID_PARAMETER, NULL); ecs_entity_t type = value->type; ecs_check(type != 0, ECS_INVALID_PARAMETER, NULL); - const EcsMetaType *mt = ecs_get(cursor->world, type, EcsMetaType); + const EcsType *mt = ecs_get(cursor->world, type, EcsType); if (!mt) { ecs_err("type of value does not have reflection data"); return -1; @@ -1167,7 +1184,7 @@ int ecs_meta_set_value( ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); if (op->type != value->type) { - char *type_str = ecs_get_fullpath(cursor->world, value->type); + char *type_str = ecs_get_path(cursor->world, value->type); flecs_meta_conversion_error(cursor, op, type_str); ecs_os_free(type_str); goto error; @@ -1194,16 +1211,16 @@ int flecs_meta_add_bitmask_constant( ecs_entity_t c = ecs_lookup_child(cursor->world, op->type, value); if (!c) { - char *path = ecs_get_fullpath(cursor->world, op->type); + char *path = ecs_get_path(cursor->world, op->type); ecs_err("unresolved bitmask constant '%s' for type '%s'", value, path); ecs_os_free(path); return -1; } - const ecs_u32_t *v = ecs_get_pair_object( + const ecs_u32_t *v = ecs_get_pair_second( cursor->world, c, EcsConstant, ecs_u32_t); if (v == NULL) { - char *path = ecs_get_fullpath(cursor->world, op->type); + char *path = ecs_get_path(cursor->world, op->type); ecs_err("'%s' is not an bitmask constant for type '%s'", value, path); ecs_os_free(path); return -1; @@ -1251,13 +1268,13 @@ int flecs_meta_cursor_lookup( const char *value, ecs_entity_t *out) { - if (ecs_os_strcmp(value, "0")) { + if (ecs_os_strcmp(value, "#0")) { if (cursor->lookup_action) { *out = cursor->lookup_action( cursor->world, value, cursor->lookup_ctx); } else { - *out = ecs_lookup_path(cursor->world, 0, value); + *out = ecs_lookup_from(cursor->world, 0, value); } if (!*out) { ecs_err("unresolved entity identifier '%s'", value); @@ -1267,6 +1284,13 @@ int flecs_meta_cursor_lookup( return 0; } +static +bool flecs_meta_valid_digit( + const char *str) +{ + return str[0] == '-' || isdigit(str[0]); +} + int ecs_meta_set_string( ecs_meta_cursor_t *cursor, const char *value) @@ -1275,6 +1299,41 @@ int ecs_meta_set_string( ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch(op->kind) { + case EcsOpI8: + case EcsOpU8: + case EcsOpByte: + case EcsOpI16: + case EcsOpU16: + case EcsOpI32: + case EcsOpU32: + case EcsOpI64: + case EcsOpU64: + case EcsOpIPtr: + case EcsOpUPtr: + case EcsOpF32: + case EcsOpF64: + if (!flecs_meta_valid_digit(value)) { + ecs_err("expected number, got '%s'", value); + goto error; + } + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpString: + case EcsOpEntity: + case EcsOpId: + case EcsOpScope: + break; + } + switch(op->kind) { case EcsOpBool: if (!ecs_os_strcmp(value, "true")) { @@ -1333,16 +1392,16 @@ int ecs_meta_set_string( ecs_assert(op->type != 0, ECS_INTERNAL_ERROR, NULL); ecs_entity_t c = ecs_lookup_child(cursor->world, op->type, value); if (!c) { - char *path = ecs_get_fullpath(cursor->world, op->type); + char *path = ecs_get_path(cursor->world, op->type); ecs_err("unresolved enum constant '%s' for type '%s'", value, path); ecs_os_free(path); goto error; } - const ecs_i32_t *v = ecs_get_pair_object( + const ecs_i32_t *v = ecs_get_pair_second( cursor->world, c, EcsConstant, ecs_i32_t); if (v == NULL) { - char *path = ecs_get_fullpath(cursor->world, op->type); + char *path = ecs_get_path(cursor->world, op->type); ecs_err("'%s' is not an enum constant for type '%s'", value, path); ecs_os_free(path); goto error; @@ -1365,23 +1424,16 @@ int ecs_meta_set_string( break; } case EcsOpId: { + #ifdef FLECS_SCRIPT ecs_id_t id = 0; -#ifdef FLECS_PARSER - ecs_term_t term = {0}; - if (ecs_parse_term(cursor->world, NULL, value, value, &term, NULL)) { - if (ecs_term_finalize(cursor->world, &term)) { - ecs_term_fini(&term); - goto error; - } - id = term.id; - ecs_term_fini(&term); - } else { - ecs_term_fini(&term); + if (flecs_id_parse(cursor->world, NULL, value, &id) == NULL) { goto error; } -#endif - set_T(ecs_id_t, ptr, id); + set_T(ecs_id_t, ptr, id); + #else + ecs_err("cannot parse component expression: script addon required"); + #endif break; } case EcsOpPop: @@ -1389,7 +1441,9 @@ int ecs_meta_set_string( goto error; case EcsOpOpaque: { const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); - ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, NULL); + ecs_assert(opaque != NULL, ECS_INVALID_OPERATION, + "entity %s is not an opaque type but serializer thinks so", + ecs_get_name(cursor->world, op->type)); if (opaque->assign_string) { /* preferred */ opaque->assign_string(ptr, value); break; @@ -1907,6 +1961,29 @@ double ecs_meta_get_float( return flecs_meta_to_float(op->kind, ptr); } +/* Handler to get string from opaque (see ecs_meta_get_string below) */ +static int ecs_meta_get_string_value_from_opaque( + const struct ecs_serializer_t *ser, ecs_entity_t type, const void *value) +{ + if(type != ecs_id(ecs_string_t)) { + ecs_err("Expected value call for opaque type to be a string"); + return -1; + } + char*** ctx = (char ***) ser->ctx; + *ctx = ECS_CONST_CAST(char**, value); + return 0; +} + +/* Handler to get string from opaque (see ecs_meta_get_string below) */ +static int ecs_meta_get_string_member_from_opaque( + const struct ecs_serializer_t* ser, const char* name) +{ + (void)ser; // silence unused warning + (void)name; // silence unused warning + ecs_err("Unexpected member call when serializing string from opaque"); + return -1; +} + const char* ecs_meta_get_string( const ecs_meta_cursor_t *cursor) { @@ -1915,9 +1992,28 @@ const char* ecs_meta_get_string( void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); switch(op->kind) { case EcsOpString: return *(const char**)ptr; + case EcsOpOpaque: { + /* If opaque type happens to map to a string, retrieve it. + Otherwise, fallback to default case (error). */ + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + if(opaque && opaque->as_type == ecs_id(ecs_string_t) && opaque->serialize) { + char** str = NULL; + ecs_serializer_t ser = { + .world = cursor->world, + .value = ecs_meta_get_string_value_from_opaque, + .member = ecs_meta_get_string_member_from_opaque, + .ctx = &str + }; + opaque->serialize(&ser, ptr); + if(str && *str) + return *str; + /* invalid string, so fall through */ + } + /* Not a compatible opaque type, so fall through */ + } + /* fall through */ case EcsOpArray: case EcsOpVector: - case EcsOpOpaque: case EcsOpPush: case EcsOpPop: case EcsOpScope: diff --git a/vendors/flecs/src/addons/meta/definitions.c b/vendors/flecs/src/addons/meta/definitions.c new file mode 100644 index 000000000..f22a7ef98 --- /dev/null +++ b/vendors/flecs/src/addons/meta/definitions.c @@ -0,0 +1,320 @@ + +/** + * @file addons/meta/definitions.c + * @brief Reflection definitions for builtin types. + */ + +#include "meta.h" + +#ifdef FLECS_META + +/* Opaque type serializatior addon vector */ +static +int flecs_addon_vec_serialize(const ecs_serializer_t *ser, const void *ptr) { + char ***data = ECS_CONST_CAST(char***, ptr); + char **addons = data[0]; + do { + ser->value(ser, ecs_id(ecs_string_t), addons); + } while((++ addons)[0]); + return 0; +} + +static +size_t flecs_addon_vec_count(const void *ptr) { + int32_t count = 0; + char ***data = ECS_CONST_CAST(char***, ptr); + char **addons = data[0]; + do { + ++ count; + } while(addons[count]); + return flecs_ito(size_t, count); +} + +static +int flecs_const_str_serialize(const ecs_serializer_t *ser, const void *ptr) { + char **data = ECS_CONST_CAST(char**, ptr); + ser->value(ser, ecs_id(ecs_string_t), data); + return 0; +} + +/* Initialize reflection data for core components */ +static +void flecs_meta_import_core_definitions( + ecs_world_t *world) +{ + ecs_struct(world, { + .entity = ecs_id(EcsComponent), + .members = { + { .name = "size", .type = ecs_id(ecs_i32_t) }, + { .name = "alignment", .type = ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(EcsDefaultChildComponent), + .members = { + { .name = "component", .type = ecs_id(ecs_entity_t) } + } + }); + + /* Define const string as an opaque type that maps to string + This enables reflection for strings that are in .rodata, + (read-only) so that the meta add-on does not try to free them. + This opaque type defines how to serialize (read) the string, + but won't let users assign a new value. + */ + ecs_entity_t const_string = ecs_opaque(world, { + .entity = ecs_component(world, { + .entity = ecs_entity(world, { + .name = "flecs.core.const_string_t", + .root_sep = "" + }), + .type = { + .size = ECS_SIZEOF(const char*), + .alignment = ECS_ALIGNOF(const char*) + } + }), + .type = { + .as_type = ecs_id(ecs_string_t), + .serialize = flecs_const_str_serialize, + } + }); + + ecs_entity_t string_vec = ecs_vector(world, { + .entity = ecs_entity(world, { + .name = "flecs.core.string_vec_t", + .root_sep = "" + }), + .type = ecs_id(ecs_string_t) + }); + + ecs_entity_t addon_vec = ecs_opaque(world, { + .entity = ecs_component(world, { + .entity = ecs_entity(world, { + .name = "flecs.core.addon_vec_t", + .root_sep = "" + }), + .type = { + .size = ECS_SIZEOF(char**), + .alignment = ECS_ALIGNOF(char**) + } + }), + .type = { + .as_type = string_vec, + .serialize = flecs_addon_vec_serialize, + .count = flecs_addon_vec_count, + } + }); + + ecs_struct(world, { + .entity = ecs_entity(world, { + .name = "flecs.core.build_info_t", + .root_sep = "" + }), + .members = { + { .name = "compiler", .type = const_string }, + { .name = "addons", .type = addon_vec }, + { .name = "version", .type = const_string }, + { .name = "version_major", .type = ecs_id(ecs_i16_t) }, + { .name = "version_minor", .type = ecs_id(ecs_i16_t) }, + { .name = "version_patch", .type = ecs_id(ecs_i16_t) }, + { .name = "debug", .type = ecs_id(ecs_bool_t) }, + { .name = "sanitize", .type = ecs_id(ecs_bool_t) }, + { .name = "perf_trace", .type = ecs_id(ecs_bool_t) } + } + }); +} + +/* Initialize reflection data for doc components */ +static +void flecs_meta_import_doc_definitions( + ecs_world_t *world) +{ + (void)world; +#ifdef FLECS_DOC + ecs_struct(world, { + .entity = ecs_id(EcsDocDescription), + .members = { + { .name = "value", .type = ecs_id(ecs_string_t) } + } + }); +#endif +} + +/* Initialize reflection data for meta components */ +static +void flecs_meta_import_meta_definitions( + ecs_world_t *world) +{ + ecs_entity_t type_kind = ecs_enum_init(world, &(ecs_enum_desc_t){ + .entity = ecs_entity(world, { .name = "TypeKind" }), + .constants = { + { .name = "PrimitiveType" }, + { .name = "BitmaskType" }, + { .name = "EnumType" }, + { .name = "StructType" }, + { .name = "ArrayType" }, + { .name = "VectorType" }, + { .name = "OpaqueType" } + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsType), + .members = { + { .name = "kind", .type = type_kind } + } + }); + + ecs_entity_t primitive_kind = ecs_enum_init(world, &(ecs_enum_desc_t){ + .entity = ecs_entity(world, { .name = "PrimitiveKind" }), + .constants = { + { .name = "Bool", 1 }, + { .name = "Char" }, + { .name = "Byte" }, + { .name = "U8" }, + { .name = "U16" }, + { .name = "U32" }, + { .name = "U64 "}, + { .name = "I8" }, + { .name = "I16" }, + { .name = "I32" }, + { .name = "I64" }, + { .name = "F32" }, + { .name = "F64" }, + { .name = "UPtr "}, + { .name = "IPtr" }, + { .name = "String" }, + { .name = "Entity" }, + { .name = "Id" } + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsPrimitive), + .members = { + { .name = "kind", .type = primitive_kind } + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsMember), + .members = { + { .name = "type", .type = ecs_id(ecs_entity_t) }, + { .name = "count", .type = ecs_id(ecs_i32_t) }, + { .name = "unit", .type = ecs_id(ecs_entity_t) }, + { .name = "offset", .type = ecs_id(ecs_i32_t) }, + { .name = "use_offset", .type = ecs_id(ecs_bool_t) } + } + }); + + ecs_entity_t vr = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, { .name = "value_range" }), + .members = { + { .name = "min", .type = ecs_id(ecs_f64_t) }, + { .name = "max", .type = ecs_id(ecs_f64_t) } + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsMemberRanges), + .members = { + { .name = "value", .type = vr }, + { .name = "warning", .type = vr }, + { .name = "error", .type = vr } + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsArray), + .members = { + { .name = "type", .type = ecs_id(ecs_entity_t) }, + { .name = "count", .type = ecs_id(ecs_i32_t) }, + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsVector), + .members = { + { .name = "type", .type = ecs_id(ecs_entity_t) } + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsOpaque), + .members = { + { .name = "as_type", .type = ecs_id(ecs_entity_t) } + } + }); + + ecs_entity_t ut = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, { .name = "unit_translation" }), + .members = { + { .name = "factor", .type = ecs_id(ecs_i32_t) }, + { .name = "power", .type = ecs_id(ecs_i32_t) } + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsUnit), + .members = { + { .name = "symbol", .type = ecs_id(ecs_string_t) }, + { .name = "prefix", .type = ecs_id(ecs_entity_t) }, + { .name = "base", .type = ecs_id(ecs_entity_t) }, + { .name = "over", .type = ecs_id(ecs_entity_t) }, + { .name = "translation", .type = ut } + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsUnitPrefix), + .members = { + { .name = "symbol", .type = ecs_id(ecs_string_t) }, + { .name = "translation", .type = ut } + } + }); + + /* Meta doc definitions */ +#ifdef FLECS_DOC + ecs_entity_t meta = ecs_lookup(world, "flecs.meta"); + ecs_doc_set_brief(world, meta, "Flecs module with reflection components"); + + ecs_doc_set_brief(world, ecs_id(EcsType), "Component added to types"); + ecs_doc_set_brief(world, ecs_id(EcsTypeSerializer), "Component that stores reflection data in an optimized format"); + ecs_doc_set_brief(world, ecs_id(EcsPrimitive), "Component added to primitive types"); + ecs_doc_set_brief(world, ecs_id(EcsEnum), "Component added to enumeration types"); + ecs_doc_set_brief(world, ecs_id(EcsBitmask), "Component added to bitmask types"); + ecs_doc_set_brief(world, ecs_id(EcsMember), "Component added to struct members"); + ecs_doc_set_brief(world, ecs_id(EcsStruct), "Component added to struct types"); + ecs_doc_set_brief(world, ecs_id(EcsArray), "Component added to array types"); + ecs_doc_set_brief(world, ecs_id(EcsVector), "Component added to vector types"); + + ecs_doc_set_brief(world, ecs_id(ecs_bool_t), "bool component"); + ecs_doc_set_brief(world, ecs_id(ecs_char_t), "char component"); + ecs_doc_set_brief(world, ecs_id(ecs_byte_t), "byte component"); + ecs_doc_set_brief(world, ecs_id(ecs_u8_t), "8 bit unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_u16_t), "16 bit unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_u32_t), "32 bit unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_u64_t), "64 bit unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_uptr_t), "word sized unsigned int component"); + ecs_doc_set_brief(world, ecs_id(ecs_i8_t), "8 bit signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_i16_t), "16 bit signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_i32_t), "32 bit signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_i64_t), "64 bit signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_iptr_t), "word sized signed int component"); + ecs_doc_set_brief(world, ecs_id(ecs_f32_t), "32 bit floating point component"); + ecs_doc_set_brief(world, ecs_id(ecs_f64_t), "64 bit floating point component"); + ecs_doc_set_brief(world, ecs_id(ecs_string_t), "string component"); + ecs_doc_set_brief(world, ecs_id(ecs_entity_t), "entity component"); +#endif +} + +void flecs_meta_import_definitions( + ecs_world_t *world) +{ + flecs_meta_import_core_definitions(world); + flecs_meta_import_doc_definitions(world); + flecs_meta_import_meta_definitions(world); +} + +#endif diff --git a/vendors/flecs/src/addons/meta/meta.c b/vendors/flecs/src/addons/meta/meta.c index d5904c43d..eff54d968 100644 --- a/vendors/flecs/src/addons/meta/meta.c +++ b/vendors/flecs/src/addons/meta/meta.c @@ -1,5 +1,5 @@ /** - * @file meta/meta.c + * @file addons/meta/meta.c * @brief Meta addon. */ @@ -26,10 +26,10 @@ static ECS_DTOR(ecs_string_t, ptr, { }) -/* EcsMetaTypeSerialized lifecycle */ +/* EcsTypeSerializer lifecycle */ void ecs_meta_dtor_serialized( - EcsMetaTypeSerialized *ptr) + EcsTypeSerializer *ptr) { int32_t i, count = ecs_vec_count(&ptr->ops); ecs_meta_type_op_t *ops = ecs_vec_first(&ptr->ops); @@ -44,7 +44,7 @@ void ecs_meta_dtor_serialized( ecs_vec_fini_t(NULL, &ptr->ops, ecs_meta_type_op_t); } -static ECS_COPY(EcsMetaTypeSerialized, dst, src, { +static ECS_COPY(EcsTypeSerializer, dst, src, { ecs_meta_dtor_serialized(dst); dst->ops = ecs_vec_copy_t(NULL, &src->ops, ecs_meta_type_op_t); @@ -60,13 +60,13 @@ static ECS_COPY(EcsMetaTypeSerialized, dst, src, { } }) -static ECS_MOVE(EcsMetaTypeSerialized, dst, src, { +static ECS_MOVE(EcsTypeSerializer, dst, src, { ecs_meta_dtor_serialized(dst); dst->ops = src->ops; src->ops = (ecs_vec_t){0}; }) -static ECS_DTOR(EcsMetaTypeSerialized, ptr, { +static ECS_DTOR(EcsTypeSerializer, ptr, { ecs_meta_dtor_serialized(ptr); }) @@ -257,16 +257,18 @@ int flecs_init_type( ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(type != 0, ECS_INTERNAL_ERROR, NULL); - EcsMetaType *meta_type = ecs_get_mut(world, type, EcsMetaType); + EcsType *meta_type = ecs_ensure(world, type, EcsType); if (meta_type->kind == 0) { + /* Determine if this is an existing type or a reflection-defined type (runtime type) */ meta_type->existing = ecs_has(world, type, EcsComponent); - /* Ensure that component has a default constructor, to prevent crashing - * serializers on uninitialized values. */ + /* For existing types, ensure that component has a default constructor, to prevent crashing + * serializers on uninitialized values. For runtime types (rtt), the default hooks are set + by flecs_meta_rtt_init_default_hooks */ ecs_type_info_t *ti = flecs_type_info_ensure(world, type); - if (!ti->hooks.ctor) { - ti->hooks.ctor = ecs_default_ctor; - } + if (meta_type->existing && !ti->hooks.ctor) { + ti->hooks.ctor = flecs_default_ctor; + } } else { if (meta_type->kind != kind) { ecs_err("type '%s' reregistered as '%s' (was '%s')", @@ -278,7 +280,7 @@ int flecs_init_type( } if (!meta_type->existing) { - EcsComponent *comp = ecs_get_mut(world, type, EcsComponent); + EcsComponent *comp = ecs_ensure(world, type, EcsComponent); comp->size = size; comp->alignment = alignment; ecs_modified(world, type, EcsComponent); @@ -306,7 +308,7 @@ int flecs_init_type( } meta_type->kind = kind; - ecs_modified(world, type, EcsMetaType); + ecs_modified(world, type, EcsType); return 0; } @@ -363,22 +365,22 @@ int flecs_add_member_to_struct( const char *name = ecs_get_name(world, member); if (!name) { - char *path = ecs_get_fullpath(world, type); + char *path = ecs_get_path(world, type); ecs_err("member for struct '%s' does not have a name", path); ecs_os_free(path); return -1; } if (!m->type) { - char *path = ecs_get_fullpath(world, member); + char *path = ecs_get_path(world, member); ecs_err("member '%s' does not have a type", path); ecs_os_free(path); return -1; } if (ecs_get_typeid(world, m->type) == 0) { - char *path = ecs_get_fullpath(world, member); - char *ent_path = ecs_get_fullpath(world, m->type); + char *path = ecs_get_path(world, member); + char *ent_path = ecs_get_path(world, m->type); ecs_err("member '%s.type' is '%s' which is not a type", path, ent_path); ecs_os_free(path); ecs_os_free(ent_path); @@ -400,12 +402,17 @@ int flecs_add_member_to_struct( } } else { if (ecs_has(world, m->type, EcsUnit)) { - unit = m->type; - m->unit = unit; + ecs_entity_t unit_base = ecs_get_target_for( + world, m->type, EcsIsA, EcsUnit); + if (unit_base) { + unit = m->unit = unit_base; + } else { + unit = m->unit = m->type; + } } } - EcsStruct *s = ecs_get_mut(world, type, EcsStruct); + EcsStruct *s = ecs_ensure(world, type, EcsStruct); ecs_assert(s != NULL, ECS_INTERNAL_ERROR, NULL); /* First check if member is already added to struct */ @@ -432,10 +439,7 @@ int flecs_add_member_to_struct( count ++; } - bool explicit_offset = false; - if (m->offset) { - explicit_offset = true; - } + bool explicit_offset = m->offset || m->use_offset; /* Compute member offsets and size & alignment of struct */ ecs_size_t size = 0; @@ -451,7 +455,7 @@ int flecs_add_member_to_struct( /* Get component of member type to get its size & alignment */ const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); if (!mbr_comp) { - char *path = ecs_get_fullpath(world, elem->type); + char *path = ecs_get_path(world, elem->type); ecs_err("member '%s' is not a type", path); ecs_os_free(path); return -1; @@ -461,7 +465,7 @@ int flecs_add_member_to_struct( ecs_size_t member_alignment = mbr_comp->alignment; if (!member_size || !member_alignment) { - char *path = ecs_get_fullpath(world, elem->type); + char *path = ecs_get_path(world, elem->type); ecs_err("member '%s' has 0 size/alignment", path); ecs_os_free(path); return -1; @@ -476,7 +480,7 @@ int flecs_add_member_to_struct( if (elem->member == member) { m->offset = elem->offset; } else { - EcsMember *other = ecs_get_mut(world, elem->member, EcsMember); + EcsMember *other = ecs_ensure(world, elem->member, EcsMember); other->offset = elem->offset; } @@ -501,7 +505,7 @@ int flecs_add_member_to_struct( /* Get component of member type to get its size & alignment */ const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); if (!mbr_comp) { - char *path = ecs_get_fullpath(world, elem->type); + char *path = ecs_get_path(world, elem->type); ecs_err("member '%s' is not a type", path); ecs_os_free(path); return -1; @@ -511,7 +515,7 @@ int flecs_add_member_to_struct( ecs_size_t member_alignment = mbr_comp->alignment; if (!member_size || !member_alignment) { - char *path = ecs_get_fullpath(world, elem->type); + char *path = ecs_get_path(world, elem->type); ecs_err("member '%s' has 0 size/alignment", path); ecs_os_free(path); return -1; @@ -519,11 +523,14 @@ int flecs_add_member_to_struct( member_size *= elem->count; elem->size = member_size; - size = elem->offset + member_size; const EcsComponent* comp = ecs_get(world, type, EcsComponent); - alignment = comp->alignment; + if (comp) { + alignment = comp->alignment; + } else { + alignment = member_alignment; + } } if (size == 0) { @@ -541,14 +548,14 @@ int flecs_add_member_to_struct( ecs_modified(world, type, EcsStruct); - /* Do this last as it triggers the update of EcsMetaTypeSerialized */ + /* Do this last as it triggers the update of EcsTypeSerializer */ if (flecs_init_type(world, type, EcsStructType, size, alignment)) { return -1; } /* If current struct is also a member, assign to itself */ if (ecs_has(world, type, EcsMember)) { - EcsMember *type_mbr = ecs_get_mut(world, type, EcsMember); + EcsMember *type_mbr = ecs_ensure(world, type, EcsMember); ecs_assert(type_mbr != NULL, ECS_INTERNAL_ERROR, NULL); type_mbr->type = type; @@ -567,7 +574,7 @@ int flecs_add_constant_to_enum( ecs_entity_t e, ecs_id_t constant_id) { - EcsEnum *ptr = ecs_get_mut(world, type, EcsEnum); + EcsEnum *ptr = ecs_ensure(world, type, EcsEnum); /* Remove constant from map if it was already added */ ecs_map_iter_t it = ecs_map_iter(&ptr->constants); @@ -584,13 +591,13 @@ int flecs_add_constant_to_enum( bool value_set = false; if (ecs_id_is_pair(constant_id)) { if (ecs_pair_second(world, constant_id) != ecs_id(ecs_i32_t)) { - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); ecs_err("expected i32 type for enum constant '%s'", path); ecs_os_free(path); return -1; } - const int32_t *value_ptr = ecs_get_pair_object( + const int32_t *value_ptr = ecs_get_pair_second( world, e, EcsConstant, ecs_i32_t); ecs_assert(value_ptr != NULL, ECS_INTERNAL_ERROR, NULL); value = *value_ptr; @@ -603,7 +610,7 @@ int flecs_add_constant_to_enum( ecs_enum_constant_t *c = ecs_map_ptr(&it); if (value_set) { if (c->value == value) { - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); ecs_err("conflicting constant value %d for '%s' (other is '%s')", value, path, c->name); ecs_os_free(path); @@ -623,14 +630,15 @@ int flecs_add_constant_to_enum( c->value = value; c->constant = e; - ecs_i32_t *cptr = ecs_get_mut_pair_object( + ecs_i32_t *cptr = ecs_ensure_pair_second( world, e, EcsConstant, ecs_i32_t); ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL); cptr[0] = value; - cptr = ecs_get_mut_id(world, e, type); + cptr = ecs_ensure_id(world, e, type); cptr[0] = value; + ecs_modified(world, type, EcsEnum); return 0; } @@ -641,7 +649,7 @@ int flecs_add_constant_to_bitmask( ecs_entity_t e, ecs_id_t constant_id) { - EcsBitmask *ptr = ecs_get_mut(world, type, EcsBitmask); + EcsBitmask *ptr = ecs_ensure(world, type, EcsBitmask); /* Remove constant from map if it was already added */ ecs_map_iter_t it = ecs_map_iter(&ptr->constants); @@ -657,13 +665,13 @@ int flecs_add_constant_to_bitmask( uint32_t value = 1; if (ecs_id_is_pair(constant_id)) { if (ecs_pair_second(world, constant_id) != ecs_id(ecs_u32_t)) { - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); ecs_err("expected u32 type for bitmask constant '%s'", path); ecs_os_free(path); return -1; } - const uint32_t *value_ptr = ecs_get_pair_object( + const uint32_t *value_ptr = ecs_get_pair_second( world, e, EcsConstant, ecs_u32_t); ecs_assert(value_ptr != NULL, ECS_INTERNAL_ERROR, NULL); value = *value_ptr; @@ -676,7 +684,7 @@ int flecs_add_constant_to_bitmask( while (ecs_map_next(&it)) { ecs_bitmask_constant_t *c = ecs_map_ptr(&it); if (c->value == value) { - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); ecs_err("conflicting constant value for '%s' (other is '%s')", path, c->name); ecs_os_free(path); @@ -692,12 +700,12 @@ int flecs_add_constant_to_bitmask( c->value = value; c->constant = e; - ecs_u32_t *cptr = ecs_get_mut_pair_object( + ecs_u32_t *cptr = ecs_ensure_pair_second( world, e, EcsConstant, ecs_u32_t); ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL); cptr[0] = value; - cptr = ecs_get_mut_id(world, e, type); + cptr = ecs_ensure_id(world, e, type); cptr[0] = value; return 0; @@ -706,7 +714,7 @@ int flecs_add_constant_to_bitmask( static void flecs_set_primitive(ecs_iter_t *it) { ecs_world_t *world = it->world; - EcsPrimitive *type = ecs_field(it, EcsPrimitive, 1); + EcsPrimitive *type = ecs_field(it, EcsPrimitive, 0); int i, count = it->count; for (i = 0; i < count; i ++) { @@ -773,7 +781,7 @@ void flecs_set_primitive(ecs_iter_t *it) { static void flecs_set_member(ecs_iter_t *it) { ecs_world_t *world = it->world; - EcsMember *member = ecs_field(it, EcsMember, 1); + EcsMember *member = ecs_field(it, EcsMember, 0); EcsMemberRanges *ranges = ecs_table_get_id(world, it->table, ecs_id(EcsMemberRanges), it->offset); @@ -794,7 +802,7 @@ void flecs_set_member(ecs_iter_t *it) { static void flecs_set_member_ranges(ecs_iter_t *it) { ecs_world_t *world = it->world; - EcsMemberRanges *ranges = ecs_field(it, EcsMemberRanges, 1); + EcsMemberRanges *ranges = ecs_field(it, EcsMemberRanges, 0); EcsMember *member = ecs_table_get_id(world, it->table, ecs_id(EcsMember), it->offset); if (!member) { @@ -829,7 +837,7 @@ void flecs_add_enum(ecs_iter_t *it) { ecs_add_id(world, e, EcsExclusive); ecs_add_id(world, e, EcsOneOf); - ecs_add_id(world, e, EcsTag); + ecs_add_id(world, e, EcsPairIsTag); } } @@ -871,7 +879,7 @@ void flecs_add_constant(ecs_iter_t *it) { static void flecs_set_array(ecs_iter_t *it) { ecs_world_t *world = it->world; - EcsArray *array = ecs_field(it, EcsArray, 1); + EcsArray *array = ecs_field(it, EcsArray, 0); int i, count = it->count; for (i = 0; i < count; i ++) { @@ -901,7 +909,7 @@ void flecs_set_array(ecs_iter_t *it) { static void flecs_set_vector(ecs_iter_t *it) { ecs_world_t *world = it->world; - EcsVector *array = ecs_field(it, EcsVector, 1); + EcsVector *array = ecs_field(it, EcsVector, 0); int i, count = it->count; for (i = 0; i < count; i ++) { @@ -922,7 +930,7 @@ 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); + EcsOpaque *serialize = ecs_field(it, EcsOpaque, 0); int i, count = it->count; for (i = 0; i < count; i ++) { @@ -1071,7 +1079,7 @@ bool flecs_unit_validate( static void flecs_set_unit(ecs_iter_t *it) { - EcsUnit *u = ecs_field(it, EcsUnit, 1); + EcsUnit *u = ecs_field(it, EcsUnit, 0); ecs_world_t *world = it->world; @@ -1100,33 +1108,9 @@ void flecs_unit_quantity_monitor(ecs_iter_t *it) { } } -static -void ecs_meta_type_init_default_ctor(ecs_iter_t *it) { - ecs_world_t *world = it->world; - EcsMetaType *type = ecs_field(it, EcsMetaType, 1); - - int i; - for (i = 0; i < it->count; i ++) { - /* If a component is defined from reflection data, configure it with the - * default constructor. This ensures that a new component value does not - * contain uninitialized memory, which could cause serializers to crash - * when for example inspecting string fields. */ - if (!type->existing) { - ecs_entity_t e = it->entities[i]; - const ecs_type_info_t *ti = ecs_get_type_info(world, e); - if (!ti || !ti->hooks.ctor) { - ecs_set_hooks_id(world, e, - &(ecs_type_hooks_t){ - .ctor = ecs_default_ctor - }); - } - } - } -} - static void flecs_member_on_set(ecs_iter_t *it) { - EcsMember *mbr = it->ptrs[0]; + EcsMember *mbr = ecs_field(it, EcsMember, 0); if (!mbr->count) { mbr->count = 1; } @@ -1136,74 +1120,184 @@ void FlecsMetaImport( ecs_world_t *world) { ECS_MODULE(world, FlecsMeta); +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); +#endif ecs_set_name_prefix(world, "Ecs"); - flecs_bootstrap_component(world, EcsMetaType); - flecs_bootstrap_component(world, EcsMetaTypeSerialized); - flecs_bootstrap_component(world, EcsPrimitive); - flecs_bootstrap_component(world, EcsEnum); - flecs_bootstrap_component(world, EcsBitmask); - flecs_bootstrap_component(world, EcsMember); - flecs_bootstrap_component(world, EcsMemberRanges); - 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); - - flecs_bootstrap_tag(world, EcsConstant); - flecs_bootstrap_tag(world, EcsQuantity); - - ecs_set_hooks(world, EcsMetaType, { .ctor = ecs_default_ctor }); - - ecs_set_hooks(world, EcsMetaTypeSerialized, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsMetaTypeSerialized), - .copy = ecs_copy(EcsMetaTypeSerialized), - .dtor = ecs_dtor(EcsMetaTypeSerialized) + flecs_bootstrap_component(world, EcsTypeSerializer); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsType), + .name = "type", .symbol = "EcsType", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsType), + .type.alignment = ECS_ALIGNOF(EcsType), + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsPrimitive), + .name = "primitive", .symbol = "EcsPrimitive", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsPrimitive), + .type.alignment = ECS_ALIGNOF(EcsPrimitive) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = EcsConstant, + .name = "constant", .symbol = "EcsConstant", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsEnum), + .name = "enum", .symbol = "EcsEnum", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsEnum), + .type.alignment = ECS_ALIGNOF(EcsEnum) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsBitmask), + .name = "bitmask", .symbol = "EcsBitmask", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsBitmask), + .type.alignment = ECS_ALIGNOF(EcsBitmask) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsMember), + .name = "member", .symbol = "EcsMember", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsMember), + .type.alignment = ECS_ALIGNOF(EcsMember) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsMemberRanges), + .name = "member_ranges", .symbol = "EcsMemberRanges", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsMemberRanges), + .type.alignment = ECS_ALIGNOF(EcsMemberRanges) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsStruct), + .name = "struct", .symbol = "EcsStruct", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsStruct), + .type.alignment = ECS_ALIGNOF(EcsStruct) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsArray), + .name = "array", .symbol = "EcsArray", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsArray), + .type.alignment = ECS_ALIGNOF(EcsArray) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsVector), + .name = "vector", .symbol = "EcsVector", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsVector), + .type.alignment = ECS_ALIGNOF(EcsVector) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsOpaque), + .name = "opaque", .symbol = "EcsOpaque", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) + }), + .type.size = sizeof(EcsOpaque), + .type.alignment = ECS_ALIGNOF(EcsOpaque) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsUnit), + .name = "unit", .symbol = "EcsUnit", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsInherit)) + }), + .type.size = sizeof(EcsUnit), + .type.alignment = ECS_ALIGNOF(EcsUnit) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = ecs_id(EcsUnitPrefix), + .name = "unit_prefix", .symbol = "EcsUnitPrefix", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsInherit)) + }), + .type.size = sizeof(EcsUnitPrefix), + .type.alignment = ECS_ALIGNOF(EcsUnitPrefix) + }); + + ecs_component(world, { + .entity = ecs_entity(world, { .id = EcsQuantity, + .name = "quantity", .symbol = "EcsQuantity", + .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsInherit)) + }) + }); + + ecs_set_hooks(world, EcsType, { .ctor = flecs_default_ctor }); + + ecs_set_hooks(world, EcsTypeSerializer, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsTypeSerializer), + .copy = ecs_copy(EcsTypeSerializer), + .dtor = ecs_dtor(EcsTypeSerializer) }); ecs_set_hooks(world, EcsStruct, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .move = ecs_move(EcsStruct), .copy = ecs_copy(EcsStruct), .dtor = ecs_dtor(EcsStruct) }); ecs_set_hooks(world, EcsMember, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .on_set = flecs_member_on_set }); ecs_set_hooks(world, EcsMemberRanges, { - .ctor = ecs_default_ctor + .ctor = flecs_default_ctor }); ecs_set_hooks(world, EcsEnum, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .move = ecs_move(EcsEnum), .copy = ecs_copy(EcsEnum), .dtor = ecs_dtor(EcsEnum) }); ecs_set_hooks(world, EcsBitmask, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .move = ecs_move(EcsBitmask), .copy = ecs_copy(EcsBitmask), .dtor = ecs_dtor(EcsBitmask) }); ecs_set_hooks(world, EcsUnit, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .move = ecs_move(EcsUnit), .copy = ecs_copy(EcsUnit), .dtor = ecs_dtor(EcsUnit) }); ecs_set_hooks(world, EcsUnitPrefix, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .move = ecs_move(EcsUnitPrefix), .copy = ecs_copy(EcsUnitPrefix), .dtor = ecs_dtor(EcsUnitPrefix) @@ -1213,85 +1307,85 @@ void FlecsMetaImport( ecs_entity_t old_scope = ecs_set_scope( /* Keep meta scope clean */ world, EcsFlecsInternals); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsPrimitive), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsPrimitive) }, .events = {EcsOnSet}, .callback = flecs_set_primitive }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsMember), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsMember) }, .events = {EcsOnSet}, .callback = flecs_set_member }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsMemberRanges), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsMemberRanges) }, .events = {EcsOnSet}, .callback = flecs_set_member_ranges }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsEnum), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsEnum) }, .events = {EcsOnAdd}, .callback = flecs_add_enum }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsBitmask), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsBitmask) }, .events = {EcsOnAdd}, .callback = flecs_add_bitmask }); ecs_observer(world, { - .filter.terms[0] = { .id = EcsConstant, .src.flags = EcsSelf }, + .query.terms[0] = { .id = EcsConstant }, .events = {EcsOnAdd}, .callback = flecs_add_constant }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_pair(EcsConstant, EcsWildcard), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_pair(EcsConstant, EcsWildcard) }, .events = {EcsOnSet}, .callback = flecs_add_constant }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsArray), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsArray) }, .events = {EcsOnSet}, .callback = flecs_set_array }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsVector), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsVector) }, .events = {EcsOnSet}, .callback = flecs_set_vector }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsOpaque), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsOpaque) }, .events = {EcsOnSet}, .callback = flecs_set_custom_type }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsUnit), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsUnit) }, .events = {EcsOnSet}, .callback = flecs_set_unit }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsMetaType), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsType) }, .events = {EcsOnSet}, .callback = ecs_meta_type_serialized_init }); ecs_observer(world, { - .filter.terms[0] = { .id = ecs_id(EcsMetaType), .src.flags = EcsSelf }, + .query.terms[0] = { .id = ecs_id(EcsType) }, .events = {EcsOnSet}, - .callback = ecs_meta_type_init_default_ctor + .callback = flecs_rtt_init_default_hooks }); ecs_observer(world, { - .filter.terms = { + .query.terms = { { .id = ecs_id(EcsUnit) }, { .id = EcsQuantity } }, @@ -1332,156 +1426,24 @@ void FlecsMetaImport( #undef ECS_PRIMITIVE ecs_set_hooks(world, ecs_string_t, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .copy = ecs_copy(ecs_string_t), .move = ecs_move(ecs_string_t), .dtor = ecs_dtor(ecs_string_t) }); /* Set default child components */ - ecs_add_pair(world, ecs_id(EcsStruct), - EcsDefaultChildComponent, ecs_id(EcsMember)); - - ecs_add_pair(world, ecs_id(EcsMember), - EcsDefaultChildComponent, ecs_id(EcsMember)); - - ecs_add_pair(world, ecs_id(EcsEnum), - EcsDefaultChildComponent, EcsConstant); - - ecs_add_pair(world, ecs_id(EcsBitmask), - EcsDefaultChildComponent, EcsConstant); + ecs_set(world, ecs_id(EcsStruct), EcsDefaultChildComponent, {ecs_id(EcsMember)}); + ecs_set(world, ecs_id(EcsMember), EcsDefaultChildComponent, {ecs_id(EcsMember)}); + ecs_set(world, ecs_id(EcsEnum), EcsDefaultChildComponent, {EcsConstant}); + ecs_set(world, ecs_id(EcsBitmask), EcsDefaultChildComponent, {EcsConstant}); /* Relationship properties */ ecs_add_id(world, EcsQuantity, EcsExclusive); - ecs_add_id(world, EcsQuantity, EcsTag); - - /* Initialize reflection data for meta components */ - ecs_entity_t type_kind = ecs_enum_init(world, &(ecs_enum_desc_t){ - .entity = ecs_entity(world, { .name = "TypeKind" }), - .constants = { - { .name = "PrimitiveType" }, - { .name = "BitmaskType" }, - { .name = "EnumType" }, - { .name = "StructType" }, - { .name = "ArrayType" }, - { .name = "VectorType" }, - { .name = "OpaqueType" } - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsMetaType), - .members = { - { .name = "kind", .type = type_kind } - } - }); + ecs_add_id(world, EcsQuantity, EcsPairIsTag); - ecs_entity_t primitive_kind = ecs_enum_init(world, &(ecs_enum_desc_t){ - .entity = ecs_entity(world, { .name = "PrimitiveKind" }), - .constants = { - { .name = "Bool", 1 }, - { .name = "Char" }, - { .name = "Byte" }, - { .name = "U8" }, - { .name = "U16" }, - { .name = "U32" }, - { .name = "U64 "}, - { .name = "I8" }, - { .name = "I16" }, - { .name = "I32" }, - { .name = "I64" }, - { .name = "F32" }, - { .name = "F64" }, - { .name = "UPtr "}, - { .name = "IPtr" }, - { .name = "String" }, - { .name = "Entity" }, - { .name = "Id" } - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsPrimitive), - .members = { - { .name = "kind", .type = primitive_kind } - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsMember), - .members = { - { .name = "type", .type = ecs_id(ecs_entity_t) }, - { .name = "count", .type = ecs_id(ecs_i32_t) }, - { .name = "unit", .type = ecs_id(ecs_entity_t) }, - { .name = "offset", .type = ecs_id(ecs_i32_t) } - } - }); - - ecs_entity_t vr = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, { .name = "value_range" }), - .members = { - { .name = "min", .type = ecs_id(ecs_f64_t) }, - { .name = "max", .type = ecs_id(ecs_f64_t) } - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsMemberRanges), - .members = { - { .name = "value", .type = vr }, - { .name = "warning", .type = vr }, - { .name = "error", .type = vr } - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsArray), - .members = { - { .name = "type", .type = ecs_id(ecs_entity_t) }, - { .name = "count", .type = ecs_id(ecs_i32_t) }, - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsVector), - .members = { - { .name = "type", .type = ecs_id(ecs_entity_t) } - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsOpaque), - .members = { - { .name = "as_type", .type = ecs_id(ecs_entity_t) } - } - }); - - ecs_entity_t ut = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, { .name = "unit_translation" }), - .members = { - { .name = "factor", .type = ecs_id(ecs_i32_t) }, - { .name = "power", .type = ecs_id(ecs_i32_t) } - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsUnit), - .members = { - { .name = "symbol", .type = ecs_id(ecs_string_t) }, - { .name = "prefix", .type = ecs_id(ecs_entity_t) }, - { .name = "base", .type = ecs_id(ecs_entity_t) }, - { .name = "over", .type = ecs_id(ecs_entity_t) }, - { .name = "translation", .type = ut } - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(EcsUnitPrefix), - .members = { - { .name = "symbol", .type = ecs_id(ecs_string_t) }, - { .name = "translation", .type = ut } - } - }); + /* Import reflection definitions for builtin types */ + flecs_meta_import_definitions(world); } #endif diff --git a/vendors/flecs/src/addons/meta/meta.h b/vendors/flecs/src/addons/meta/meta.h index 9e06c8df3..0f2d401c8 100644 --- a/vendors/flecs/src/addons/meta/meta.h +++ b/vendors/flecs/src/addons/meta/meta.h @@ -1,5 +1,5 @@ /** - * @file meta/meta.h + * @file addons/meta/meta.h * @brief Private functions for meta addon. */ @@ -14,7 +14,7 @@ void ecs_meta_type_serialized_init( ecs_iter_t *it); void ecs_meta_dtor_serialized( - EcsMetaTypeSerialized *ptr); + EcsTypeSerializer *ptr); ecs_meta_type_op_kind_t flecs_meta_primitive_to_op_kind( ecs_primitive_kind_t kind); @@ -24,6 +24,19 @@ bool flecs_unit_validate( ecs_entity_t t, EcsUnit *data); +void flecs_meta_import_definitions( + ecs_world_t *world); + +int flecs_expr_ser_primitive( + const ecs_world_t *world, + ecs_primitive_kind_t kind, + const void *base, + ecs_strbuf_t *str, + bool is_expr); + +void flecs_rtt_init_default_hooks( + ecs_iter_t *it); + #endif - + #endif diff --git a/vendors/flecs/src/addons/meta/rtt_lifecycle.c b/vendors/flecs/src/addons/meta/rtt_lifecycle.c new file mode 100644 index 000000000..ee9e22194 --- /dev/null +++ b/vendors/flecs/src/addons/meta/rtt_lifecycle.c @@ -0,0 +1,682 @@ +/* + * @file addons/meta/rtt_lifecycle.c + * @brief Runtime components lifecycle management + */ + +#include "flecs.h" +#include "meta.h" + +#ifdef FLECS_META + +/* Stores all the information necessary to forward a hook call to a + * struct's member type */ +typedef struct ecs_rtt_call_data_t { + union { + ecs_xtor_t xtor; + ecs_move_t move; + ecs_copy_t copy; + } hook; + const ecs_type_info_t *type_info; + int32_t offset; + int32_t count; +} ecs_rtt_call_data_t; + +/* Lifecycle context for runtime structs */ +typedef struct ecs_rtt_struct_ctx_t { + ecs_vec_t vctor; /* vector */ + ecs_vec_t vdtor; /* vector */ + ecs_vec_t vmove; /* vector */ + ecs_vec_t vcopy; /* vector */ +} ecs_rtt_struct_ctx_t; + +/* Lifecycle context for runtime arrays */ +typedef struct ecs_rtt_array_ctx_t { + const ecs_type_info_t *type_info; + int32_t elem_count; +} ecs_rtt_array_ctx_t; + +/* Lifecycle context for runtime vectors */ +typedef struct ecs_rtt_vector_ctx_t { + const ecs_type_info_t *type_info; +} ecs_rtt_vector_ctx_t; + +/* Generic copy assign hook */ +static +void flecs_rtt_default_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_os_memcpy(dst_ptr, src_ptr, count * type_info->size); +} + +/* Generic move assign hook */ +static +void flecs_rtt_default_move( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + flecs_rtt_default_copy(dst_ptr, src_ptr, count, type_info); +} + +/* + * + * RTT struct support + * + */ + +/* Invokes struct member type's constructor/destructor using saved information + * in the lifecycle context */ +static +void flecs_rtt_struct_xtor( + ecs_vec_t *xtor_data_vec, + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + int cb_count = ecs_vec_count(xtor_data_vec); + int i, j; + for (j = 0; j < count; j++) { + void *elem_ptr = ECS_ELEM(ptr, type_info->size, j); + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *xtor_data = + ecs_vec_get_t(xtor_data_vec, ecs_rtt_call_data_t, i); + xtor_data->hook.xtor( + ECS_OFFSET(elem_ptr, xtor_data->offset), + xtor_data->count, + xtor_data->type_info); + } + } +} + +/* Generic struct constructor. It will read hook information call data from + * the structs's lifecycle context and call the constructors configured when + * the type was created. */ +static +void flecs_rtt_struct_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_rtt_struct_xtor(&rtt_ctx->vctor, ptr, count, type_info); +} + +/* Generic struct destructor. It will read hook information call data from + * the structs's lifecycle context and call the constructors configured when + * the type was created. */ +static +void flecs_rtt_struct_dtor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_rtt_struct_xtor(&rtt_ctx->vdtor, ptr, count, type_info); +} + +/* Generic move hook. It will read hook information call data from the + * structs's lifecycle context and call the move hooks configured when + * the type was created. */ +static +void flecs_rtt_struct_move( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + + int cb_count = ecs_vec_count(&rtt_ctx->vmove); + int i, j; + for (j = 0; j < count; j++) { + ecs_size_t elem_offset = type_info->size * j; + void *elem_dst_ptr = ECS_OFFSET(dst_ptr, elem_offset); + void *elem_src_ptr = ECS_OFFSET(src_ptr, elem_offset); + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *move_data = + ecs_vec_get_t(&rtt_ctx->vmove, ecs_rtt_call_data_t, i); + move_data->hook.move( + ECS_OFFSET(elem_dst_ptr, move_data->offset), + ECS_OFFSET(elem_src_ptr, move_data->offset), + move_data->count, + move_data->type_info); + } + } +} + +/* Generic copy hook. It will read hook information call data from the + * structs's lifecycle context and call the copy hooks configured when + * the type was created. */ +static +void flecs_rtt_struct_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + + int cb_count = ecs_vec_count(&rtt_ctx->vcopy); + int i, j; + for (j = 0; j < count; j++) { + ecs_size_t elem_offset = type_info->size * j; + void *elem_dst_ptr = ECS_OFFSET(dst_ptr, elem_offset); + const void *elem_src_ptr = ECS_OFFSET(src_ptr, elem_offset); + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *copy_data = + ecs_vec_get_t(&rtt_ctx->vcopy, ecs_rtt_call_data_t, i); + copy_data->hook.copy( + ECS_OFFSET(elem_dst_ptr, copy_data->offset), + ECS_OFFSET(elem_src_ptr, copy_data->offset), + copy_data->count, + copy_data->type_info); + } + } +} + +static +void flecs_rtt_free_lifecycle_struct_ctx( + void *ctx) +{ + if (!ctx) { + return; + } + + ecs_rtt_struct_ctx_t *lifecycle_ctx = ctx; + + ecs_vec_fini_t(NULL, &lifecycle_ctx->vctor, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vdtor, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vmove, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vcopy, ecs_rtt_call_data_t); + + ecs_os_free(ctx); +} + +static +ecs_rtt_struct_ctx_t * flecs_rtt_configure_struct_hooks( + ecs_world_t *world, + const ecs_type_info_t *ti, + bool ctor, + bool dtor, + bool move, + bool copy) +{ + ecs_type_hooks_t hooks = ti->hooks; + if (hooks.lifecycle_ctx_free) { + hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); + } + + ecs_rtt_struct_ctx_t *rtt_ctx = NULL; + if (ctor || dtor || move || copy) { + rtt_ctx = ecs_os_malloc_t(ecs_rtt_struct_ctx_t); + ecs_vec_init_t(NULL, &rtt_ctx->vctor, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vdtor, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vmove, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vcopy, ecs_rtt_call_data_t, 0); + hooks.lifecycle_ctx = rtt_ctx; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_struct_ctx; + + if (ctor) { + hooks.ctor = flecs_rtt_struct_ctor; + } + if (dtor) { + hooks.dtor = flecs_rtt_struct_dtor; + } + if (move) { + hooks.move = flecs_rtt_struct_move; + } + if (copy) { + hooks.copy = flecs_rtt_struct_copy; + } + } else { + hooks.lifecycle_ctx = NULL; + hooks.lifecycle_ctx_free = NULL; + } + ecs_set_hooks_id(world, ti->component, &hooks); + return rtt_ctx; +} + +/* Checks if a struct member's types have hooks installed. If so, it generates + * and installs required hooks for the struct type itself. These hooks will + * invoke the member hooks when necessary */ +static +void flecs_rtt_init_default_hooks_struct( + ecs_world_t *world, + ecs_entity_t component, + const ecs_type_info_t *ti) +{ + /* Obtain struct information to figure out what members it contains: */ + const EcsStruct *struct_info = ecs_get(world, component, EcsStruct); + ecs_assert(struct_info != NULL, ECS_INTERNAL_ERROR, NULL); + + /* These flags will be set to true if we determine we need to generate a + * hook of a particular type: */ + bool ctor_hook_required = false; + bool dtor_hook_required = false; + bool move_hook_required = false; + bool copy_hook_required = false; + + /* Iterate all struct members and see if any member type has hooks. If so, + * the struct itself will need to have that hook: */ + int i, member_count = ecs_vec_count(&struct_info->members); + ecs_member_t *members = ecs_vec_first(&struct_info->members); + for (i = 0; i < member_count; i++) { + ecs_member_t *m = &members[i]; + const ecs_type_info_t *member_ti = ecs_get_type_info(world, m->type); + ctor_hook_required |= member_ti->hooks.ctor && + member_ti->hooks.ctor != flecs_default_ctor; + dtor_hook_required |= member_ti->hooks.dtor != NULL; + move_hook_required |= member_ti->hooks.move != NULL; + copy_hook_required |= member_ti->hooks.copy != NULL; + } + + /* If any hook is required, then create a lifecycle context and configure a + * generic hook that will interpret that context: */ + ecs_rtt_struct_ctx_t *rtt_ctx = flecs_rtt_configure_struct_hooks( + world, + ti, + ctor_hook_required, + dtor_hook_required, + move_hook_required, + copy_hook_required); + + if (!rtt_ctx) { + return; /* no hooks required */ + } + + /* At least a hook was configured, therefore examine each struct member to + * build the vector of calls that will then be executed by the generic hook + * handler: */ + for (i = 0; i < member_count; i++) { + ecs_member_t *m = &members[i]; + const ecs_type_info_t *member_ti = ecs_get_type_info(world, m->type); + if (ctor_hook_required) { + ecs_rtt_call_data_t *ctor_data = + ecs_vec_append_t(NULL, &rtt_ctx->vctor, ecs_rtt_call_data_t); + ctor_data->count = m->count; + ctor_data->offset = m->offset; + ctor_data->type_info = member_ti; + if (member_ti->hooks.ctor) { + ctor_data->hook.xtor = member_ti->hooks.ctor; + } else { + ctor_data->hook.xtor = flecs_default_ctor; + } + } + if (dtor_hook_required && member_ti->hooks.dtor) { + ecs_rtt_call_data_t *dtor_data = + ecs_vec_append_t(NULL, &rtt_ctx->vdtor, ecs_rtt_call_data_t); + dtor_data->count = m->count; + dtor_data->offset = m->offset; + dtor_data->type_info = member_ti; + dtor_data->hook.xtor = member_ti->hooks.dtor; + } + if (move_hook_required) { + ecs_rtt_call_data_t *move_data = + ecs_vec_append_t(NULL, &rtt_ctx->vmove, ecs_rtt_call_data_t); + move_data->offset = m->offset; + move_data->type_info = member_ti; + move_data->count = m->count; + if (member_ti->hooks.move) { + move_data->hook.move = member_ti->hooks.move; + } else { + move_data->hook.move = flecs_rtt_default_move; + } + } + if (copy_hook_required) { + ecs_rtt_call_data_t *copy_data = + ecs_vec_append_t(NULL, &rtt_ctx->vcopy, ecs_rtt_call_data_t); + copy_data->offset = m->offset; + copy_data->type_info = member_ti; + copy_data->count = m->count; + if (member_ti->hooks.copy) { + copy_data->hook.copy = member_ti->hooks.copy; + } else { + copy_data->hook.copy = flecs_rtt_default_copy; + } + } + } +} + +/* + * + * RTT array support + * + */ + +static +void flecs_rtt_free_lifecycle_array_ctx( + void *ctx) +{ + if (!ctx) { + return; + } + + ecs_os_free(ctx); +} + +/* Generic array constructor. It will invoke the constructor of the underlying + * type for all the elements */ +static +void flecs_rtt_array_ctor( + void *ptr, + int32_t count, /* note: "count" is how many arrays to initialize, not how + many elements are in the array */ + const ecs_type_info_t *type_info) +{ + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_xtor_t ctor = rtt_ctx->type_info->hooks.ctor; + int i; + for (i = 0; i < count; i++) { + void *arr = ECS_ELEM(ptr, type_info->size, i); + ctor(arr, rtt_ctx->elem_count, rtt_ctx->type_info); + } +} + +/* Generic array constructor. It will invoke the destructor of the underlying + * type for all the elements */ +static +void flecs_rtt_array_dtor( + void *ptr, + int32_t count, /* note: "count" is how many arrays to destroy, not how + many elements are in the array */ + const ecs_type_info_t *type_info) +{ + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_xtor_t dtor = rtt_ctx->type_info->hooks.dtor; + int i; + for (i = 0; i < count; i++) { + void *arr = ECS_ELEM(ptr, type_info->size, i); + dtor(arr, rtt_ctx->elem_count, rtt_ctx->type_info); + } +} + +/* Generic array move hook. It will invoke the move hook of the underlying + * type for all the elements */ +static +void flecs_rtt_array_move( + void *dst_ptr, + void *src_ptr, + int32_t count, /* note: "count" is how many arrays to move, not how + many elements are in the array */ + const ecs_type_info_t *type_info) +{ + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_move_t move = rtt_ctx->type_info->hooks.move; + int i; + for (i = 0; i < count; i++) { + void *src_arr = ECS_ELEM(src_ptr, type_info->size, i); + void *dst_arr = ECS_ELEM(dst_ptr, type_info->size, i); + move(dst_arr, src_arr, rtt_ctx->elem_count, rtt_ctx->type_info); + } +} + +/* Generic array copy hook. It will invoke the copy hook of the underlying + * type for all the elements */ +static +void flecs_rtt_array_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, /* note: "count" is how many arrays to copy, not how + many elements are in the array */ + const ecs_type_info_t *type_info) +{ + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_copy_t copy = rtt_ctx->type_info->hooks.copy; + int i; + for (i = 0; i < count; i++) { + const void *src_arr = ECS_ELEM(src_ptr, type_info->size, i); + void *dst_arr = ECS_ELEM(dst_ptr, type_info->size, i); + copy(dst_arr, src_arr, rtt_ctx->elem_count, rtt_ctx->type_info); + } +} + +/* Checks if an array's underlying type has hooks installed. If so, it generates + * and installs required hooks for the array type itself. These hooks will + * invoke the underlying type's hook for each element in the array. */ +static +void flecs_rtt_init_default_hooks_array( + ecs_world_t *world, + ecs_entity_t component) +{ + const EcsArray *array_info = ecs_get(world, component, EcsArray); + ecs_assert(array_info != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *array_ti = + ecs_get_type_info(world, array_info->type); + bool ctor_hook_required = + array_ti->hooks.ctor && array_ti->hooks.ctor != flecs_default_ctor; + bool dtor_hook_required = array_ti->hooks.dtor != NULL; + bool move_hook_required = array_ti->hooks.move != NULL; + bool copy_hook_required = array_ti->hooks.copy != NULL; + + if (!ctor_hook_required && !dtor_hook_required && !move_hook_required && + !copy_hook_required) { + return; /* no hooks required */ + } + + ecs_rtt_array_ctx_t *rtt_ctx = ecs_os_malloc_t(ecs_rtt_array_ctx_t); + rtt_ctx->type_info = array_ti; + rtt_ctx->elem_count = array_info->count; + ecs_type_hooks_t hooks = *ecs_get_hooks_id(world, component); + if (hooks.lifecycle_ctx_free) { + hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); + } + + hooks.lifecycle_ctx = rtt_ctx; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_array_ctx; + + if (ctor_hook_required) { + hooks.ctor = flecs_rtt_array_ctor; + } + + if (dtor_hook_required) { + hooks.dtor = flecs_rtt_array_dtor; + } + + if (move_hook_required) { + hooks.move = flecs_rtt_array_move; + } + + if (copy_hook_required) { + hooks.copy = flecs_rtt_array_copy; + } + + ecs_set_hooks_id(world, component, &hooks); +} + +/* + * + * RTT vector support + * + */ + +static +void flecs_rtt_free_lifecycle_vector_ctx( + void *ctx) +{ + if (!ctx) { + return; + } + + ecs_os_free(ctx); +} + +/* Generic vector constructor. Makes sure the vector structure is initialized to + * 0 elements */ +static +void flecs_rtt_vector_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + int i; + for (i = 0; i < count; i++) { + ecs_vec_t *vec = ECS_ELEM(ptr, type_info->size, i); + ecs_vec_init(NULL, vec, rtt_ctx->type_info->size, 0); + } +} + +/* Generic vector destructor. It will invoke the destructor for each element of + * the vector and finalize resources associated to the vector itself. */ +static +void flecs_rtt_vector_dtor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_xtor_t dtor = rtt_ctx->type_info->hooks.dtor; + int i; + for (i = 0; i < count; i++) { + ecs_vec_t *vec = ECS_ELEM(ptr, type_info->size, i); + int32_t num_elements = ecs_vec_count(vec); + if (dtor && num_elements) { + dtor(ecs_vec_first(vec), num_elements, rtt_ctx->type_info); + } + ecs_vec_fini(NULL, vec, rtt_ctx->type_info->size); + } +} + +/* Generic vector move hook. */ +static +void flecs_rtt_vector_move( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + flecs_rtt_vector_dtor(dst_ptr, count, type_info); + int i; + for (i = 0; i < count; i++) { + ecs_vec_t *src_vec = ECS_ELEM(src_ptr, type_info->size, i); + ecs_vec_t *dst_vec = ECS_ELEM(dst_ptr, type_info->size, i); + *dst_vec = *src_vec; + src_vec->array = NULL; + src_vec->count = 0; + } +} + +/* Generic vector copy hook. It makes a deep copy of vector contents */ +static +void flecs_rtt_vector_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + flecs_rtt_vector_dtor(dst_ptr, count, type_info); + ecs_copy_t copy = rtt_ctx->type_info->hooks.copy + ? rtt_ctx->type_info->hooks.copy + : flecs_rtt_default_copy; + ecs_xtor_t ctor = rtt_ctx->type_info->hooks.ctor + ? rtt_ctx->type_info->hooks.ctor + : flecs_default_ctor; + ecs_xtor_t dtor = rtt_ctx->type_info->hooks.dtor; + int i; + for (i = 0; i < count; i++) { + const ecs_vec_t *src_vec = ECS_ELEM(src_ptr, type_info->size, i); + ecs_vec_t *dst_vec = ECS_ELEM(dst_ptr, type_info->size, i); + int32_t src_count = ecs_vec_count(src_vec); + int32_t dst_count = ecs_vec_count(dst_vec); + if (dtor && dst_count) { + dtor(ecs_vec_first(dst_vec), dst_count, rtt_ctx->type_info); + } + ecs_vec_set_count(NULL, dst_vec, rtt_ctx->type_info->size, src_count); + ctor(ecs_vec_first(dst_vec), src_count, rtt_ctx->type_info); + copy( + ecs_vec_first(dst_vec), + ecs_vec_first(src_vec), + src_count, + rtt_ctx->type_info); + } +} + +/* Generates and installs required hooks for managing the vector and underlying + * type lifecycle. Vectors always have hooks because at the very least the + * vector structure itself must be initialized/destroyed/copied/moved, even if + * empty. */ +static +void flecs_rtt_init_default_hooks_vector( + ecs_world_t *world, + ecs_entity_t component) +{ + const EcsVector *vector_info = ecs_get(world, component, EcsVector); + ecs_assert(vector_info != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *vector_ti = + ecs_get_type_info(world, vector_info->type); + ecs_rtt_vector_ctx_t *rtt_ctx = ecs_os_malloc_t(ecs_rtt_vector_ctx_t); + rtt_ctx->type_info = vector_ti; + ecs_type_hooks_t hooks = *ecs_get_hooks_id(world, component); + if (hooks.lifecycle_ctx_free) { + hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); + } + hooks.lifecycle_ctx = rtt_ctx; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_vector_ctx; + hooks.ctor = flecs_rtt_vector_ctor; + hooks.dtor = flecs_rtt_vector_dtor; + hooks.move = flecs_rtt_vector_move; + hooks.copy = flecs_rtt_vector_copy; + ecs_set_hooks_id(world, component, &hooks); +} + +void flecs_rtt_init_default_hooks( + ecs_iter_t *it) +{ + ecs_world_t *world = it->world; + EcsType *type_field = ecs_field(it, EcsType, 0); + + int i; + for (i = 0; i < it->count; i++) { + EcsType *type = &type_field[i]; + if (type->existing) { + continue; /* non-rtt type. Ignore. */ + } + + /* If a component is defined from reflection data, configure appropriate + * default hooks. + * - For trivial types, at least set a default constructor so memory is + * zero-initialized + * - For struct types, configure a hook that in turn calls hooks of + * member types, if those member types have hooks defined themselves. + * - For array types, configure a hook that in turn calls hooks for the + * underlying type, for each element in the array. + * - For vector types, configure hooks to manage the vector structure + * itself, move the vector and deep-copy vector elements + * */ + + ecs_entity_t component = it->entities[i]; + const ecs_type_info_t *ti = ecs_get_type_info(world, component); + + if (ti) { + if (type->kind == EcsStructType) { + flecs_rtt_init_default_hooks_struct(world, component, ti); + } else if (type->kind == EcsArrayType) { + flecs_rtt_init_default_hooks_array(world, component); + } else if (type->kind == EcsVectorType) { + flecs_rtt_init_default_hooks_vector(world, component); + } + } + + /* Make sure there is at least a default constructor. This ensures that + * a new component value does not contain uninitialized memory, which + * could cause serializers to crash when for example inspecting string + * fields. */ + if (!ti || !ti->hooks.ctor) { + ecs_set_hooks_id( + world, + component, + &(ecs_type_hooks_t){.ctor = flecs_default_ctor}); + } + } +} + +#endif diff --git a/vendors/flecs/src/addons/meta/serialized.c b/vendors/flecs/src/addons/meta/serialized.c index 4b57878ef..0a9260bc7 100644 --- a/vendors/flecs/src/addons/meta/serialized.c +++ b/vendors/flecs/src/addons/meta/serialized.c @@ -1,5 +1,5 @@ /** - * @file meta/serialized.c + * @file addons/meta/serialized.c * @brief Serialize type into flat operations array to speed up deserialization. */ @@ -56,7 +56,7 @@ int flecs_meta_serialize_primitive( { const EcsPrimitive *ptr = ecs_get(world, type, EcsPrimitive); if (!ptr) { - char *name = ecs_get_fullpath(world, type); + char *name = ecs_get_path(world, type); ecs_err("entity '%s' is not a primitive type", name); ecs_os_free(name); return -1; @@ -128,7 +128,9 @@ int flecs_meta_serialize_array_component( return -1; /* Should never happen, will trigger internal error */ } - flecs_meta_serialize_type(world, ptr->type, 0, ops); + if (flecs_meta_serialize_type(world, ptr->type, 0, ops) != 0) { + return -1; + } ecs_meta_type_op_t *first = ecs_vec_first(ops); first->count = ptr->count; @@ -194,8 +196,9 @@ int flecs_meta_serialize_struct( ecs_member_t *member = &members[i]; cur = ecs_vec_count(ops); - flecs_meta_serialize_type(world, - member->type, offset + member->offset, ops); + if (flecs_meta_serialize_type(world, member->type, offset + member->offset, ops) != 0) { + continue; + } op = flecs_meta_ops_get(ops, cur); if (!op->type) { @@ -216,7 +219,8 @@ int flecs_meta_serialize_struct( member_name, 0, 0); } - flecs_meta_ops_add(ops, EcsOpPop); + ecs_meta_type_op_t *pop = flecs_meta_ops_add(ops, EcsOpPop); + pop->type = type; flecs_meta_ops_get(ops, first)->op_count = ecs_vec_count(ops) - first; return 0; } @@ -228,10 +232,10 @@ int flecs_meta_serialize_type( ecs_size_t offset, ecs_vec_t *ops) { - const EcsMetaType *ptr = ecs_get(world, type, EcsMetaType); + const EcsType *ptr = ecs_get(world, type, EcsType); if (!ptr) { - char *path = ecs_get_fullpath(world, type); - ecs_err("missing EcsMetaType for type %s'", path); + char *path = ecs_get_path(world, type); + ecs_err("missing EcsType for type %s'", path); ecs_os_free(path); return -1; } @@ -255,10 +259,10 @@ int flecs_meta_serialize_component( ecs_entity_t type, ecs_vec_t *ops) { - const EcsMetaType *ptr = ecs_get(world, type, EcsMetaType); + const EcsType *ptr = ecs_get(world, type, EcsType); if (!ptr) { - char *path = ecs_get_fullpath(world, type); - ecs_err("missing EcsMetaType for type %s'", path); + char *path = ecs_get_path(world, type); + ecs_err("missing EcsType for type %s'", path); ecs_os_free(path); return -1; } @@ -282,8 +286,8 @@ void ecs_meta_type_serialized_init( ecs_vec_init_t(NULL, &ops, ecs_meta_type_op_t, 0); flecs_meta_serialize_component(world, e, &ops); - EcsMetaTypeSerialized *ptr = ecs_get_mut( - world, e, EcsMetaTypeSerialized); + EcsTypeSerializer *ptr = ecs_ensure( + world, e, EcsTypeSerializer); if (ptr->ops.array) { ecs_meta_dtor_serialized(ptr); } diff --git a/vendors/flecs/src/addons/metrics.c b/vendors/flecs/src/addons/metrics.c index 84a5c071c..1e31f5577 100644 --- a/vendors/flecs/src/addons/metrics.c +++ b/vendors/flecs/src/addons/metrics.c @@ -153,7 +153,7 @@ static ECS_MOVE(EcsMetricCountTargets, dst, src, { static void flecs_metrics_on_member_metric(ecs_iter_t *it) { ecs_world_t *world = it->world; ecs_member_metric_ctx_t *ctx = it->ctx; - ecs_id_t id = ecs_field_id(it, 1); + ecs_id_t id = ecs_field_id(it, 0); int32_t i, count = it->count; for (i = 0; i < count; i ++) { @@ -161,7 +161,7 @@ static void flecs_metrics_on_member_metric(ecs_iter_t *it) { ecs_entity_t m = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); EcsMetricMemberInstance *src = ecs_emplace( - world, m, EcsMetricMemberInstance); + world, m, EcsMetricMemberInstance, NULL); src->ref = ecs_ref_init_id(world, e, id); src->ctx = ctx; ecs_modified(world, m, EcsMetricMemberInstance); @@ -182,7 +182,8 @@ static void flecs_metrics_on_id_metric(ecs_iter_t *it) { ecs_entity_t e = it->entities[i]; ecs_entity_t m = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); - EcsMetricIdInstance *src = ecs_emplace(world, m, EcsMetricIdInstance); + EcsMetricIdInstance *src = ecs_emplace( + world, m, EcsMetricIdInstance, NULL); src->r = ecs_record_find(world, e); src->ctx = ctx; ecs_modified(world, m, EcsMetricIdInstance); @@ -207,7 +208,8 @@ static void flecs_metrics_on_oneof_metric(ecs_iter_t *it) { ecs_entity_t e = it->entities[i]; ecs_entity_t m = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); - EcsMetricOneOfInstance *src = ecs_emplace(world, m, EcsMetricOneOfInstance); + EcsMetricOneOfInstance *src = ecs_emplace( + world, m, EcsMetricOneOfInstance, NULL); src->r = ecs_record_find(world, e); src->ctx = ctx; ecs_modified(world, m, EcsMetricOneOfInstance); @@ -222,7 +224,7 @@ static void flecs_metrics_on_oneof_metric(ecs_iter_t *it) { #ifdef FLECS_DOC static void SetMetricDocName(ecs_iter_t *it) { ecs_world_t *world = it->world; - EcsMetricSource *src = ecs_field(it, EcsMetricSource, 1); + EcsMetricSource *src = ecs_field(it, EcsMetricSource, 0); int32_t i, count = it->count; for (i = 0; i < count; i ++) { @@ -238,7 +240,7 @@ static void SetMetricDocName(ecs_iter_t *it) { /** Delete metric instances for entities that are no longer alive */ static void ClearMetricInstance(ecs_iter_t *it) { ecs_world_t *world = it->world; - EcsMetricSource *src = ecs_field(it, EcsMetricSource, 1); + EcsMetricSource *src = ecs_field(it, EcsMetricSource, 0); int32_t i, count = it->count; for (i = 0; i < count; i ++) { @@ -252,14 +254,18 @@ static void ClearMetricInstance(ecs_iter_t *it) { /** Update member metric */ static void UpdateMemberInstance(ecs_iter_t *it, bool counter) { ecs_world_t *world = it->real_world; - EcsMetricValue *m = ecs_field(it, EcsMetricValue, 1); - EcsMetricMemberInstance *mi = ecs_field(it, EcsMetricMemberInstance, 2); + EcsMetricValue *m = ecs_field(it, EcsMetricValue, 0); + EcsMetricMemberInstance *mi = ecs_field(it, EcsMetricMemberInstance, 1); ecs_ftime_t dt = it->delta_time; int32_t i, count = it->count; for (i = 0; i < count; i ++) { ecs_member_metric_ctx_t *ctx = mi[i].ctx; ecs_ref_t *ref = &mi[i].ref; + if (!ref->entity) { + continue; + } + const void *ptr = ecs_ref_get_id(world, ref, ref->id); if (ptr) { ptr = ECS_OFFSET(ptr, ctx->offset); @@ -290,13 +296,18 @@ static void UpdateCounterIncrementMemberInstance(ecs_iter_t *it) { /** Update id metric */ static void UpdateIdInstance(ecs_iter_t *it, bool counter) { ecs_world_t *world = it->real_world; - EcsMetricValue *m = ecs_field(it, EcsMetricValue, 1); - EcsMetricIdInstance *mi = ecs_field(it, EcsMetricIdInstance, 2); + EcsMetricValue *m = ecs_field(it, EcsMetricValue, 0); + EcsMetricIdInstance *mi = ecs_field(it, EcsMetricIdInstance, 1); ecs_ftime_t dt = it->delta_time; int32_t i, count = it->count; for (i = 0; i < count; i ++) { - ecs_table_t *table = mi[i].r->table; + ecs_record_t *r = mi[i].r; + if (!r) { + continue; + } + + ecs_table_t *table = r->table; if (!table) { ecs_delete(it->world, it->entities[i]); continue; @@ -304,7 +315,7 @@ static void UpdateIdInstance(ecs_iter_t *it, bool counter) { ecs_id_metric_ctx_t *ctx = mi[i].ctx; ecs_id_record_t *idr = ctx->idr; - if (flecs_search_w_idr(world, table, idr->id, NULL, idr) != -1) { + if (flecs_search_w_idr(world, table, NULL, idr) != -1) { if (!counter) { m[i].value = 1.0; } else { @@ -328,15 +339,19 @@ static void UpdateCounterIdInstance(ecs_iter_t *it) { static void UpdateOneOfInstance(ecs_iter_t *it, bool counter) { ecs_world_t *world = it->real_world; ecs_table_t *table = it->table; - void *m = ecs_table_get_column(table, - ecs_table_type_to_column_index(table, it->columns[0] - 1), it->offset); - EcsMetricOneOfInstance *mi = ecs_field(it, EcsMetricOneOfInstance, 2); + void *m = ecs_table_get_column(table, it->trs[0]->column, it->offset); + EcsMetricOneOfInstance *mi = ecs_field(it, EcsMetricOneOfInstance, 1); ecs_ftime_t dt = it->delta_time; int32_t i, count = it->count; for (i = 0; i < count; i ++) { ecs_oneof_metric_ctx_t *ctx = mi[i].ctx; - ecs_table_t *mtable = mi[i].r->table; + ecs_record_t *r = mi[i].r; + if (!r) { + continue; + } + + ecs_table_t *mtable = r->table; double *value = ECS_ELEM(m, ctx->size, i); if (!counter) { @@ -350,7 +365,7 @@ static void UpdateOneOfInstance(ecs_iter_t *it, bool counter) { ecs_id_record_t *idr = ctx->idr; ecs_id_t id; - if (flecs_search_w_idr(world, mtable, idr->id, &id, idr) == -1) { + if (flecs_search_w_idr(world, mtable, &id, idr) == -1) { ecs_delete(it->world, it->entities[i]); continue; } @@ -382,7 +397,7 @@ static void UpdateCounterOneOfInstance(ecs_iter_t *it) { static void UpdateCountTargets(ecs_iter_t *it) { ecs_world_t *world = it->real_world; - EcsMetricCountTargets *m = ecs_field(it, EcsMetricCountTargets, 1); + EcsMetricCountTargets *m = ecs_field(it, EcsMetricCountTargets, 0); int32_t i, count = it->count; for (i = 0; i < count; i ++) { @@ -399,12 +414,12 @@ static void UpdateCountTargets(ecs_iter_t *it) { ecs_set_name(world, mi[0], name); } - EcsMetricSource *source = ecs_get_mut( + EcsMetricSource *source = ecs_ensure( world, mi[0], EcsMetricSource); source->entity = tgt; } - EcsMetricValue *value = ecs_get_mut(world, mi[0], EcsMetricValue); + EcsMetricValue *value = ecs_ensure(world, mi[0], EcsMetricValue); value->value += (double)ecs_count_id(world, cur->id) * (double)it->delta_system_time; } @@ -413,8 +428,8 @@ static void UpdateCountTargets(ecs_iter_t *it) { static void UpdateCountIds(ecs_iter_t *it) { ecs_world_t *world = it->real_world; - EcsMetricCountIds *m = ecs_field(it, EcsMetricCountIds, 1); - EcsMetricValue *v = ecs_field(it, EcsMetricValue, 2); + EcsMetricCountIds *m = ecs_field(it, EcsMetricCountIds, 0); + EcsMetricValue *v = ecs_field(it, EcsMetricValue, 1); int32_t i, count = it->count; for (i = 0; i < count; i ++) { @@ -435,7 +450,7 @@ int flecs_member_metric_init( if (desc->dotmember) { if (!desc->id) { - char *metric_name = ecs_get_fullpath(world, metric); + char *metric_name = ecs_get_path(world, metric); ecs_err("missing id for metric '%s' with member '%s", metric_name, desc->dotmember); ecs_os_free(metric_name); @@ -443,7 +458,7 @@ int flecs_member_metric_init( } if (desc->member) { - char *metric_name = ecs_get_fullpath(world, metric); + char *metric_name = ecs_get_path(world, metric); ecs_err("cannot set both member and dotmember for metric '%s'", metric_name); ecs_os_free(metric_name); @@ -454,13 +469,13 @@ int flecs_member_metric_init( ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, NULL); if (ecs_meta_push(&cur)) { - char *metric_name = ecs_get_fullpath(world, metric); + char *metric_name = ecs_get_path(world, metric); ecs_err("invalid type for metric '%s'", metric_name); ecs_os_free(metric_name); goto error; } if (ecs_meta_dotmember(&cur, desc->dotmember)) { - char *metric_name = ecs_get_fullpath(world, metric); + char *metric_name = ecs_get_path(world, metric); ecs_err("invalid dotmember '%s' for metric '%s'", desc->dotmember, metric_name); ecs_os_free(metric_name); @@ -474,8 +489,8 @@ int flecs_member_metric_init( } else { const EcsMember *m = ecs_get(world, desc->member, EcsMember); if (!m) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); ecs_err("entity '%s' provided for metric '%s' is not a member", member_name, metric_name); ecs_os_free(member_name); @@ -485,8 +500,8 @@ int flecs_member_metric_init( type = ecs_get_parent(world, desc->member); if (!type) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); ecs_err("member '%s' provided for metric '%s' is not part of a type", member_name, metric_name); ecs_os_free(member_name); @@ -497,9 +512,9 @@ int flecs_member_metric_init( id = type; if (desc->id) { if (type != ecs_get_typeid(world, desc->id)) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); - char *id_name = ecs_get_fullpath(world, desc->id); + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); + char *id_name = ecs_get_path(world, desc->id); ecs_err("member '%s' for metric '%s' is not of type '%s'", member_name, metric_name, id_name); ecs_os_free(id_name); @@ -517,8 +532,8 @@ int flecs_member_metric_init( const EcsPrimitive *p = ecs_get(world, member_type, EcsPrimitive); if (!p) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); ecs_err("member '%s' provided for metric '%s' must have primitive type", member_name, metric_name); ecs_os_free(member_name); @@ -526,10 +541,10 @@ int flecs_member_metric_init( goto error; } - const EcsMetaType *mt = ecs_get(world, type, EcsMetaType); + const EcsType *mt = ecs_get(world, type, EcsType); if (!mt) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); ecs_err("parent of member '%s' for metric '%s' is not a type", member_name, metric_name); ecs_os_free(member_name); @@ -538,8 +553,8 @@ int flecs_member_metric_init( } if (mt->kind != EcsStructType) { - char *metric_name = ecs_get_fullpath(world, metric); - char *member_name = ecs_get_fullpath(world, desc->member); + char *metric_name = ecs_get_path(world, metric); + char *member_name = ecs_get_path(world, desc->member); ecs_err("parent of member '%s' for metric '%s' is not a struct", member_name, metric_name); ecs_os_free(member_name); @@ -556,11 +571,7 @@ int flecs_member_metric_init( ecs_observer(world, { .entity = metric, .events = { EcsOnAdd }, - .filter.terms[0] = { - .id = id, - .src.flags = EcsSelf, - .inout = EcsInOutNone - }, + .query.terms[0] = { .id = id }, .callback = flecs_metrics_on_member_metric, .yield_existing = true, .ctx = ctx @@ -586,16 +597,12 @@ int flecs_id_metric_init( ctx->metric.metric = metric; ctx->metric.kind = desc->kind; ctx->idr = flecs_id_record_ensure(world, desc->id); - ecs_check(ctx->idr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ctx->idr != NULL, ECS_INTERNAL_ERROR, NULL); ecs_observer(world, { .entity = metric, .events = { EcsOnAdd }, - .filter.terms[0] = { - .id = desc->id, - .src.flags = EcsSelf, - .inout = EcsInOutNone - }, + .query.terms[0] = { .id = desc->id }, .callback = flecs_metrics_on_id_metric, .yield_existing = true, .ctx = ctx @@ -622,7 +629,7 @@ int flecs_oneof_metric_init( ctx->metric.metric = metric; ctx->metric.kind = desc->kind; ctx->idr = flecs_id_record_ensure(world, desc->id); - ecs_check(ctx->idr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ctx->idr != NULL, ECS_INTERNAL_ERROR, NULL); ecs_map_init(&ctx->target_offset, NULL); /* Add member for each child of oneof to metric, so it can be used as metric @@ -643,7 +650,7 @@ int flecs_oneof_metric_init( ecs_entity_t mbr = ecs_entity(world, { .name = to_snake_case, - .add = { ecs_childof(metric) } + .parent = ecs_childof(metric) }); ecs_os_free(to_snake_case); @@ -666,11 +673,7 @@ int flecs_oneof_metric_init( ecs_observer(world, { .entity = metric, .events = { EcsMonitor }, - .filter.terms[0] = { - .id = desc->id, - .src.flags = EcsSelf, - .inout = EcsInOutNone - }, + .query.terms[0] = { .id = desc->id }, .callback = flecs_metrics_on_oneof_metric, .yield_existing = true, .ctx = ctx @@ -695,7 +698,7 @@ int flecs_count_id_targets_metric_init( ctx->metric.metric = metric; ctx->metric.kind = desc->kind; ctx->idr = flecs_id_record_ensure(world, desc->id); - ecs_check(ctx->idr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ctx->idr != NULL, ECS_INTERNAL_ERROR, NULL); ecs_map_init(&ctx->targets, NULL); ecs_set(world, metric, EcsMetricCountTargets, { .ctx = ctx }); @@ -723,12 +726,13 @@ ecs_entity_t ecs_metric_init( const ecs_metric_desc_t *desc) { ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - ecs_poly_assert(world, ecs_world_t); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_metric_desc_t was not initialized to zero"); + flecs_poly_assert(world, ecs_world_t); ecs_entity_t result = desc->entity; if (!result) { - result = ecs_new_id(world); + result = ecs_new(world); } ecs_entity_t kind = desc->kind; @@ -742,7 +746,7 @@ ecs_entity_t ecs_metric_init( kind != EcsCounterId && kind != EcsCounterIncrement) { - ecs_err("invalid metric kind %s", ecs_get_fullpath(world, kind)); + ecs_err("invalid metric kind %s", ecs_get_path(world, kind)); goto error; } @@ -874,25 +878,25 @@ void FlecsMetricsImport(ecs_world_t *world) { }); ecs_set_hooks(world, EcsMetricMember, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(EcsMetricMember), .move = ecs_move(EcsMetricMember) }); ecs_set_hooks(world, EcsMetricId, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(EcsMetricId), .move = ecs_move(EcsMetricId) }); ecs_set_hooks(world, EcsMetricOneOf, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(EcsMetricOneOf), .move = ecs_move(EcsMetricOneOf) }); ecs_set_hooks(world, EcsMetricCountTargets, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(EcsMetricCountTargets), .move = ecs_move(EcsMetricCountTargets) }); @@ -900,7 +904,8 @@ void FlecsMetricsImport(ecs_world_t *world) { ecs_add_id(world, EcsMetric, EcsOneOf); #ifdef FLECS_DOC - ECS_OBSERVER(world, SetMetricDocName, EcsOnSet, EcsMetricSource); + ECS_OBSERVER(world, SetMetricDocName, EcsOnSet, + Source); #endif ECS_SYSTEM(world, ClearMetricInstance, EcsPreStore, diff --git a/vendors/flecs/src/addons/module.c b/vendors/flecs/src/addons/module.c index 2b67af385..6c1bc4965 100644 --- a/vendors/flecs/src/addons/module.c +++ b/vendors/flecs/src/addons/module.c @@ -10,7 +10,7 @@ #include "../private_api.h" #include -char* ecs_module_path_from_c( +char* flecs_module_path_from_c( const char *c_name) { ecs_strbuf_t str = ECS_STRBUF_INIT; @@ -42,8 +42,8 @@ ecs_entity_t ecs_import( ecs_entity_t old_scope = ecs_set_scope(world, 0); const char *old_name_prefix = world->info.name_prefix; - char *path = ecs_module_path_from_c(module_name); - ecs_entity_t e = ecs_lookup_fullpath(world, path); + char *path = flecs_module_path_from_c(module_name); + ecs_entity_t e = ecs_lookup(world, path); ecs_os_free(path); if (!e) { @@ -54,7 +54,7 @@ ecs_entity_t ecs_import( module(world); /* Lookup module entity (must be registered by module) */ - e = ecs_lookup_fullpath(world, module_name); + e = ecs_lookup(world, module_name); ecs_check(e != 0, ECS_MODULE_UNDEFINED, module_name); ecs_log_pop(); @@ -74,7 +74,7 @@ ecs_entity_t ecs_import_c( ecs_module_action_t module, const char *c_name) { - char *name = ecs_module_path_from_c(c_name); + char *name = flecs_module_path_from_c(c_name); ecs_entity_t e = ecs_import(world, module, name); ecs_os_free(name); return e; @@ -195,19 +195,19 @@ ecs_entity_t ecs_module_init( const ecs_component_desc_t *desc) { ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_entity_t old_scope = ecs_set_scope(world, 0); ecs_entity_t e = desc->entity; if (!e) { - char *module_path = ecs_module_path_from_c(c_name); - e = ecs_new_from_fullpath(world, module_path); + char *module_path = flecs_module_path_from_c(c_name); + e = ecs_entity(world, { .name = module_path }); ecs_set_symbol(world, e, module_path); ecs_os_free(module_path); } else if (!ecs_exists(world, e)) { - char *module_path = ecs_module_path_from_c(c_name); - ecs_ensure(world, e); + char *module_path = flecs_module_path_from_c(c_name); + ecs_make_alive(world, e); ecs_add_fullpath(world, e, module_path); ecs_set_symbol(world, e, module_path); ecs_os_free(module_path); diff --git a/vendors/flecs/src/addons/monitor.c b/vendors/flecs/src/addons/monitor.c deleted file mode 100644 index 8c9a4cb54..000000000 --- a/vendors/flecs/src/addons/monitor.c +++ /dev/null @@ -1,370 +0,0 @@ -/** - * @file addons/monitor.c - * @brief Monitor addon. - */ - -#include "flecs.h" -#include "../private_api.h" - -#ifdef FLECS_MONITOR - -ECS_COMPONENT_DECLARE(FlecsMonitor); -ECS_COMPONENT_DECLARE(EcsWorldStats); -ECS_COMPONENT_DECLARE(EcsWorldSummary); -ECS_COMPONENT_DECLARE(EcsPipelineStats); - -ecs_entity_t EcsPeriod1s = 0; -ecs_entity_t EcsPeriod1m = 0; -ecs_entity_t EcsPeriod1h = 0; -ecs_entity_t EcsPeriod1d = 0; -ecs_entity_t EcsPeriod1w = 0; - -static int32_t flecs_day_interval_count = 24; -static int32_t flecs_week_interval_count = 168; - -static -ECS_COPY(EcsPipelineStats, dst, src, { - (void)dst; - (void)src; - ecs_abort(ECS_INVALID_OPERATION, "cannot copy pipeline stats component"); -}) - -static -ECS_MOVE(EcsPipelineStats, dst, src, { - ecs_os_memcpy_t(dst, src, EcsPipelineStats); - ecs_os_zeromem(src); -}) - -static -ECS_DTOR(EcsPipelineStats, ptr, { - ecs_pipeline_stats_fini(&ptr->stats); -}) - -static -void UpdateWorldSummary(ecs_iter_t *it) { - EcsWorldSummary *summary = ecs_field(it, EcsWorldSummary, 1); - - const ecs_world_info_t *info = ecs_get_world_info(it->world); - - int32_t i, count = it->count; - for (i = 0; i < count; i ++) { - summary[i].target_fps = (double)info->target_fps; - - summary[i].frame_time_last = (double)info->frame_time_total - summary[i].frame_time_total; - summary[i].system_time_last = (double)info->system_time_total - summary[i].system_time_total; - summary[i].merge_time_last = (double)info->merge_time_total - summary[i].merge_time_total; - - summary[i].frame_time_total = (double)info->frame_time_total; - summary[i].system_time_total = (double)info->system_time_total; - summary[i].merge_time_total = (double)info->merge_time_total; - } -} - -static -void MonitorStats(ecs_iter_t *it) { - ecs_world_t *world = it->real_world; - - EcsStatsHeader *hdr = ecs_field_w_size(it, 0, 1); - ecs_id_t kind = ecs_pair_first(it->world, ecs_field_id(it, 1)); - void *stats = ECS_OFFSET_T(hdr, EcsStatsHeader); - - ecs_ftime_t elapsed = hdr->elapsed; - hdr->elapsed += it->delta_time; - - int32_t t_last = (int32_t)(elapsed * 60); - int32_t t_next = (int32_t)(hdr->elapsed * 60); - int32_t i, dif = t_last - t_next; - - ecs_world_stats_t last_world = {0}; - ecs_pipeline_stats_t last_pipeline = {0}; - void *last = NULL; - - if (!dif) { - /* Copy last value so we can pass it to reduce_last */ - if (kind == ecs_id(EcsWorldStats)) { - last = &last_world; - ecs_world_stats_copy_last(&last_world, stats); - } else if (kind == ecs_id(EcsPipelineStats)) { - last = &last_pipeline; - ecs_pipeline_stats_copy_last(&last_pipeline, stats); - } - } - - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_get(world, stats); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_get(world, ecs_get_pipeline(world), stats); - } - - if (!dif) { - /* Still in same interval, combine with last measurement */ - hdr->reduce_count ++; - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_reduce_last(stats, last, hdr->reduce_count); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_reduce_last(stats, last, hdr->reduce_count); - } - } else if (dif > 1) { - /* More than 16ms has passed, backfill */ - for (i = 1; i < dif; i ++) { - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_repeat_last(stats); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_world_stats_repeat_last(stats); - } - } - hdr->reduce_count = 0; - } - - if (last && kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_fini(last); - } -} - -static -void ReduceStats(ecs_iter_t *it) { - void *dst = ecs_field_w_size(it, 0, 1); - void *src = ecs_field_w_size(it, 0, 2); - - ecs_id_t kind = ecs_pair_first(it->world, ecs_field_id(it, 1)); - - dst = ECS_OFFSET_T(dst, EcsStatsHeader); - src = ECS_OFFSET_T(src, EcsStatsHeader); - - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_reduce(dst, src); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_reduce(dst, src); - } -} - -static -void AggregateStats(ecs_iter_t *it) { - int32_t interval = *(int32_t*)it->ctx; - - EcsStatsHeader *dst_hdr = ecs_field_w_size(it, 0, 1); - EcsStatsHeader *src_hdr = ecs_field_w_size(it, 0, 2); - - void *dst = ECS_OFFSET_T(dst_hdr, EcsStatsHeader); - void *src = ECS_OFFSET_T(src_hdr, EcsStatsHeader); - - ecs_id_t kind = ecs_pair_first(it->world, ecs_field_id(it, 1)); - - ecs_world_stats_t last_world = {0}; - ecs_pipeline_stats_t last_pipeline = {0}; - void *last = NULL; - - if (dst_hdr->reduce_count != 0) { - /* Copy last value so we can pass it to reduce_last */ - if (kind == ecs_id(EcsWorldStats)) { - last_world.t = 0; - ecs_world_stats_copy_last(&last_world, dst); - last = &last_world; - } else if (kind == ecs_id(EcsPipelineStats)) { - last_pipeline.t = 0; - ecs_pipeline_stats_copy_last(&last_pipeline, dst); - last = &last_pipeline; - } - } - - /* Reduce from minutes to the current day */ - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_reduce(dst, src); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_reduce(dst, src); - } - - if (dst_hdr->reduce_count != 0) { - if (kind == ecs_id(EcsWorldStats)) { - ecs_world_stats_reduce_last(dst, last, dst_hdr->reduce_count); - } else if (kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_reduce_last(dst, last, dst_hdr->reduce_count); - } - } - - /* A day has 60 24 minute intervals */ - dst_hdr->reduce_count ++; - if (dst_hdr->reduce_count >= interval) { - dst_hdr->reduce_count = 0; - } - - if (last && kind == ecs_id(EcsPipelineStats)) { - ecs_pipeline_stats_fini(last); - } -} - -static -void flecs_stats_monitor_import( - ecs_world_t *world, - ecs_id_t kind, - size_t size) -{ - ecs_entity_t prev = ecs_set_scope(world, kind); - - // Called each frame, collects 60 measurements per second - ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1s", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1s), - .src.id = EcsWorld - }}, - .callback = MonitorStats - }); - - // Called each second, reduces into 60 measurements per minute - ecs_entity_t mw1m = ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1m", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1m), - .src.id = EcsWorld - }, { - .id = ecs_pair(kind, EcsPeriod1s), - .src.id = EcsWorld - }}, - .callback = ReduceStats, - .interval = 1.0 - }); - - // Called each minute, reduces into 60 measurements per hour - ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1h", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1h), - .src.id = EcsWorld - }, { - .id = ecs_pair(kind, EcsPeriod1m), - .src.id = EcsWorld - }}, - .callback = ReduceStats, - .rate = 60, - .tick_source = mw1m - }); - - // Called each minute, reduces into 60 measurements per day - ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1d", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1d), - .src.id = EcsWorld - }, { - .id = ecs_pair(kind, EcsPeriod1m), - .src.id = EcsWorld - }}, - .callback = AggregateStats, - .rate = 60, - .tick_source = mw1m, - .ctx = &flecs_day_interval_count - }); - - // Called each hour, reduces into 60 measurements per week - ecs_system(world, { - .entity = ecs_entity(world, { .name = "Monitor1w", .add = {ecs_dependson(EcsPreFrame)} }), - .query.filter.terms = {{ - .id = ecs_pair(kind, EcsPeriod1w), - .src.id = EcsWorld - }, { - .id = ecs_pair(kind, EcsPeriod1h), - .src.id = EcsWorld - }}, - .callback = AggregateStats, - .rate = 60, - .tick_source = mw1m, - .ctx = &flecs_week_interval_count - }); - - ecs_set_scope(world, prev); - - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1s), size, NULL); - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1m), size, NULL); - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1h), size, NULL); - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1d), size, NULL); - ecs_set_id(world, EcsWorld, ecs_pair(kind, EcsPeriod1w), size, NULL); -} - -static -void flecs_world_monitor_import( - ecs_world_t *world) -{ - ECS_COMPONENT_DEFINE(world, EcsWorldStats); - - flecs_stats_monitor_import(world, ecs_id(EcsWorldStats), - sizeof(EcsWorldStats)); -} - -static -void flecs_pipeline_monitor_import( - ecs_world_t *world) -{ - ECS_COMPONENT_DEFINE(world, EcsPipelineStats); - - ecs_set_hooks(world, EcsPipelineStats, { - .ctor = ecs_default_ctor, - .copy = ecs_copy(EcsPipelineStats), - .move = ecs_move(EcsPipelineStats), - .dtor = ecs_dtor(EcsPipelineStats) - }); - - flecs_stats_monitor_import(world, ecs_id(EcsPipelineStats), - sizeof(EcsPipelineStats)); -} - -void FlecsMonitorImport( - ecs_world_t *world) -{ - ECS_MODULE_DEFINE(world, FlecsMonitor); - ECS_IMPORT(world, FlecsPipeline); - ECS_IMPORT(world, FlecsTimer); -#ifdef FLECS_META - ECS_IMPORT(world, FlecsMeta); -#endif -#ifdef FLECS_UNITS - ECS_IMPORT(world, FlecsUnits); -#endif - - ecs_set_name_prefix(world, "Ecs"); - - EcsPeriod1s = ecs_new_entity(world, "EcsPeriod1s"); - EcsPeriod1m = ecs_new_entity(world, "EcsPeriod1m"); - EcsPeriod1h = ecs_new_entity(world, "EcsPeriod1h"); - EcsPeriod1d = ecs_new_entity(world, "EcsPeriod1d"); - EcsPeriod1w = ecs_new_entity(world, "EcsPeriod1w"); - - ECS_COMPONENT_DEFINE(world, EcsWorldSummary); - -#if defined(FLECS_META) && defined(FLECS_UNITS) - ecs_struct(world, { - .entity = ecs_id(EcsWorldSummary), - .members = { - { .name = "target_fps", .type = ecs_id(ecs_f64_t), .unit = EcsHertz }, - { .name = "frame_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "system_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "merge_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "frame_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "system_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, - { .name = "merge_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds } - } - }); -#endif - - ecs_system(world, { - .entity = ecs_entity(world, { - .name = "UpdateWorldSummary", - .add = {ecs_dependson(EcsPreFrame)} - }), - .query.filter.terms[0] = { .id = ecs_id(EcsWorldSummary) }, - .callback = UpdateWorldSummary - }); - - ECS_SYSTEM(world, UpdateWorldSummary, EcsPreFrame, WorldSummary); - ecs_set(world, EcsWorld, EcsWorldSummary, {0}); - - flecs_world_monitor_import(world); - flecs_pipeline_monitor_import(world); - - if (ecs_os_has_time()) { - ecs_measure_frame_time(world, true); - ecs_measure_system_time(world, true); - } -} - -#endif diff --git a/vendors/flecs/src/addons/os_api_impl/windows_impl.inl b/vendors/flecs/src/addons/os_api_impl/windows_impl.inl index cdce3dbb2..d1bc142f9 100644 --- a/vendors/flecs/src/addons/os_api_impl/windows_impl.inl +++ b/vendors/flecs/src/addons/os_api_impl/windows_impl.inl @@ -6,6 +6,9 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #include diff --git a/vendors/flecs/src/addons/parser.c b/vendors/flecs/src/addons/parser.c deleted file mode 100644 index 829b837ca..000000000 --- a/vendors/flecs/src/addons/parser.c +++ /dev/null @@ -1,1126 +0,0 @@ -/** - * @file addons/parser.c - * @brief Parser addon. - */ - -#include "flecs.h" - -#ifdef FLECS_PARSER - -#include "../private_api.h" -#include - -#define TOK_COLON ':' -#define TOK_AND ',' -#define TOK_OR "||" -#define TOK_NOT '!' -#define TOK_OPTIONAL '?' -#define TOK_BITWISE_OR '|' -#define TOK_BRACKET_OPEN '[' -#define TOK_BRACKET_CLOSE ']' -#define TOK_SCOPE_OPEN '{' -#define TOK_SCOPE_CLOSE '}' -#define TOK_VARIABLE '$' -#define TOK_PAREN_OPEN '(' -#define TOK_PAREN_CLOSE ')' -#define TOK_EQ "==" -#define TOK_NEQ "!=" -#define TOK_MATCH "~=" -#define TOK_EXPR_STRING '"' - -#define TOK_SELF "self" -#define TOK_UP "up" -#define TOK_DOWN "down" -#define TOK_CASCADE "cascade" -#define TOK_PARENT "parent" -#define TOK_DESC "desc" - -#define TOK_OVERRIDE "OVERRIDE" -#define TOK_ROLE_AND "AND" -#define TOK_ROLE_OR "OR" -#define TOK_ROLE_NOT "NOT" -#define TOK_ROLE_TOGGLE "TOGGLE" - -#define TOK_IN "in" -#define TOK_OUT "out" -#define TOK_INOUT "inout" -#define TOK_INOUT_NONE "none" - -static -const ecs_id_t ECS_OR = (1ull << 59); - -static -const ecs_id_t ECS_NOT = (1ull << 58); - -#define ECS_MAX_TOKEN_SIZE (256) - -typedef char ecs_token_t[ECS_MAX_TOKEN_SIZE]; - -const char* ecs_parse_ws_eol( - const char *ptr) -{ - while (isspace(*ptr)) { - ptr ++; - } - - return ptr; -} - -const char* ecs_parse_ws( - const char *ptr) -{ - while ((*ptr != '\n') && isspace(*ptr)) { - ptr ++; - } - - return ptr; -} - -const char* ecs_parse_digit( - const char *ptr, - char *token) -{ - char *tptr = token; - char ch = ptr[0]; - - if (!isdigit(ch) && ch != '-') { - ecs_parser_error(NULL, NULL, 0, "invalid start of number '%s'", ptr); - return NULL; - } - - tptr[0] = ch; - tptr ++; - ptr ++; - - for (; (ch = *ptr); ptr ++) { - if (!isdigit(ch) && (ch != '.') && (ch != 'e')) { - break; - } - - tptr[0] = ch; - tptr ++; - } - - tptr[0] = '\0'; - - return ptr; -} - -/* -- Private functions -- */ - -bool flecs_isident( - char ch) -{ - return isalpha(ch) || (ch == '_'); -} - -static -bool flecs_valid_identifier_start_char( - char ch) -{ - if (ch && (flecs_isident(ch) || (ch == '*') || - (ch == '0') || (ch == TOK_VARIABLE) || isdigit(ch))) - { - return true; - } - - return false; -} - -static -bool flecs_valid_token_start_char( - char ch) -{ - if ((ch == '"') || (ch == '{') || (ch == '}') || (ch == ',') || (ch == '-') - || (ch == '[') || (ch == ']') || (ch == '`') || - flecs_valid_identifier_start_char(ch)) - { - return true; - } - - return false; -} - -static -bool flecs_valid_token_char( - char ch) -{ - if (ch && (flecs_isident(ch) || isdigit(ch) || ch == '.' || ch == '"')) { - return true; - } - - return false; -} - -static -bool flecs_valid_operator_char( - char ch) -{ - if (ch == TOK_OPTIONAL || ch == TOK_NOT) { - return true; - } - - return false; -} - -const char* ecs_parse_token( - const char *name, - const char *expr, - const char *ptr, - char *token_out, - char delim) -{ - int64_t column = ptr - expr; - - ptr = ecs_parse_ws(ptr); - char *tptr = token_out, ch = ptr[0]; - - if (!flecs_valid_token_start_char(ch)) { - if (ch == '\0' || ch == '\n') { - ecs_parser_error(name, expr, column, - "unexpected end of expression"); - } else { - ecs_parser_error(name, expr, column, - "invalid start of token '%s'", ptr); - } - return NULL; - } - - tptr[0] = ch; - tptr ++; - ptr ++; - - if (ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == ',' || ch == '`') { - tptr[0] = 0; - return ptr; - } - - int tmpl_nesting = 0; - bool in_str = ch == '"'; - - for (; (ch = *ptr); ptr ++) { - if (ch == '<') { - tmpl_nesting ++; - } else if (ch == '>') { - if (!tmpl_nesting) { - break; - } - tmpl_nesting --; - } else if (ch == '"') { - in_str = !in_str; - } else - if (!flecs_valid_token_char(ch) && !in_str) { - break; - } - if (delim && (ch == delim)) { - break; - } - - tptr[0] = ch; - tptr ++; - } - - tptr[0] = '\0'; - - if (tmpl_nesting != 0) { - ecs_parser_error(name, expr, column, - "identifier '%s' has mismatching < > pairs", ptr); - return NULL; - } - - const char *next_ptr = ecs_parse_ws(ptr); - if (next_ptr[0] == ':' && next_ptr != ptr) { - /* Whitespace between token and : is significant */ - ptr = next_ptr - 1; - } else { - ptr = next_ptr; - } - - return ptr; -} - -const char* ecs_parse_identifier( - const char *name, - const char *expr, - const char *ptr, - char *token_out) -{ - if (!flecs_valid_identifier_start_char(ptr[0]) && (ptr[0] != '"')) { - ecs_parser_error(name, expr, (ptr - expr), - "expected start of identifier"); - return NULL; - } - - ptr = ecs_parse_token(name, expr, ptr, token_out, 0); - - return ptr; -} - -static -int flecs_parse_identifier( - const char *token, - ecs_term_id_t *out) -{ - const char *tptr = token; - if (tptr[0] == TOK_VARIABLE && tptr[1]) { - out->flags |= EcsIsVariable; - tptr ++; - } - if (tptr[0] == TOK_EXPR_STRING && tptr[1]) { - out->flags |= EcsIsName; - tptr ++; - if (tptr[0] == TOK_NOT) { - /* Already parsed */ - tptr ++; - } - } - - char *name = ecs_os_strdup(tptr); - out->name = name; - - ecs_size_t len = ecs_os_strlen(name); - if (out->flags & EcsIsName) { - if (name[len - 1] != TOK_EXPR_STRING) { - ecs_parser_error(NULL, token, 0, "missing '\"' at end of string"); - return -1; - } else { - name[len - 1] = '\0'; - } - } - - return 0; -} - -static -ecs_entity_t flecs_parse_role( - const char *name, - const char *sig, - int64_t column, - const char *token) -{ - if (!ecs_os_strcmp(token, TOK_ROLE_AND)) { - return ECS_AND; - } else if (!ecs_os_strcmp(token, TOK_ROLE_OR)) { - return ECS_OR; - } else if (!ecs_os_strcmp(token, TOK_ROLE_NOT)) { - return ECS_NOT; - } else if (!ecs_os_strcmp(token, TOK_OVERRIDE)) { - return ECS_OVERRIDE; - } else if (!ecs_os_strcmp(token, TOK_ROLE_TOGGLE)) { - return ECS_TOGGLE; - } else { - ecs_parser_error(name, sig, column, "invalid role '%s'", token); - return 0; - } -} - -static -ecs_oper_kind_t flecs_parse_operator( - char ch) -{ - if (ch == TOK_OPTIONAL) { - return EcsOptional; - } else if (ch == TOK_NOT) { - return EcsNot; - } else { - ecs_throw(ECS_INTERNAL_ERROR, NULL); - } -error: - return 0; -} - -static -const char* flecs_parse_annotation( - const char *name, - const char *sig, - int64_t column, - const char *ptr, - ecs_inout_kind_t *inout_kind_out) -{ - char token[ECS_MAX_TOKEN_SIZE]; - - ptr = ecs_parse_identifier(name, sig, ptr, token); - if (!ptr) { - return NULL; - } - - if (!ecs_os_strcmp(token, TOK_IN)) { - *inout_kind_out = EcsIn; - } else - if (!ecs_os_strcmp(token, TOK_OUT)) { - *inout_kind_out = EcsOut; - } else - if (!ecs_os_strcmp(token, TOK_INOUT)) { - *inout_kind_out = EcsInOut; - } else if (!ecs_os_strcmp(token, TOK_INOUT_NONE)) { - *inout_kind_out = EcsInOutNone; - } - - ptr = ecs_parse_ws(ptr); - - if (ptr[0] != TOK_BRACKET_CLOSE) { - printf("errr\n"); - ecs_parser_error(name, sig, column, "expected ]"); - return NULL; - } - - return ptr + 1; -} - -static -uint8_t flecs_parse_set_token( - const char *token) -{ - if (!ecs_os_strcmp(token, TOK_SELF)) { - return EcsSelf; - } else if (!ecs_os_strcmp(token, TOK_UP)) { - return EcsUp; - } else if (!ecs_os_strcmp(token, TOK_DOWN)) { - return EcsDown; - } else if (!ecs_os_strcmp(token, TOK_CASCADE)) { - return EcsCascade; - } else if (!ecs_os_strcmp(token, TOK_DESC)) { - return EcsDesc; - } else if (!ecs_os_strcmp(token, TOK_PARENT)) { - return EcsParent; - } else { - return 0; - } -} - -static -const char* flecs_parse_term_flags( - const ecs_world_t *world, - const char *name, - const char *expr, - int64_t column, - const char *ptr, - char *token, - ecs_term_id_t *id, - char tok_end) -{ - char token_buf[ECS_MAX_TOKEN_SIZE] = {0}; - if (!token) { - token = token_buf; - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } - } - - do { - uint8_t tok = flecs_parse_set_token(token); - if (!tok) { - ecs_parser_error(name, expr, column, - "invalid set token '%s'", token); - return NULL; - } - - if (id->flags & tok) { - ecs_parser_error(name, expr, column, - "duplicate set token '%s'", token); - return NULL; - } - - id->flags |= tok; - - if (ptr[0] == TOK_PAREN_OPEN) { - ptr ++; - - /* Relationship (overrides IsA default) */ - if (!isdigit(ptr[0]) && flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } - - id->trav = ecs_lookup_fullpath(world, token); - if (!id->trav) { - ecs_parser_error(name, expr, column, - "unresolved identifier '%s'", token); - return NULL; - } - - if (ptr[0] == TOK_AND) { - ptr = ecs_parse_ws(ptr + 1); - } else if (ptr[0] != TOK_PAREN_CLOSE) { - ecs_parser_error(name, expr, column, - "expected ',' or ')'"); - return NULL; - } - } - - if (ptr[0] != TOK_PAREN_CLOSE) { - ecs_parser_error(name, expr, column, "expected ')', got '%c'", - ptr[0]); - return NULL; - } else { - ptr = ecs_parse_ws(ptr + 1); - if (ptr[0] != tok_end && ptr[0] != TOK_AND && ptr[0] != 0) { - ecs_parser_error(name, expr, column, - "expected end of set expr"); - return NULL; - } - } - } - - /* Next token in set expression */ - if (ptr[0] == TOK_BITWISE_OR) { - ptr ++; - if (flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } - } - - /* End of set expression */ - } else if (ptr[0] == tok_end || ptr[0] == TOK_AND || !ptr[0]) { - break; - } - } while (true); - - return ptr; -} - -static -const char* flecs_parse_arguments( - const ecs_world_t *world, - const char *name, - const char *expr, - int64_t column, - const char *ptr, - char *token, - ecs_term_t *term, - ecs_term_id_t *extra_args) -{ - (void)column; - - int32_t arg = 0; - - if (extra_args) { - ecs_os_memset_n(extra_args, 0, ecs_term_id_t, ECS_PARSER_MAX_ARGS); - } - - if (!term) { - arg = 2; - } - - do { - if (flecs_valid_token_start_char(ptr[0])) { - if ((arg == ECS_PARSER_MAX_ARGS) || (!extra_args && arg == 2)) { - ecs_parser_error(name, expr, (ptr - expr), - "too many arguments in term"); - return NULL; - } - - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } - - ecs_term_id_t *term_id = NULL; - - if (arg == 0) { - term_id = &term->src; - } else if (arg == 1) { - term_id = &term->second; - } else { - term_id = &extra_args[arg - 2]; - } - - /* If token is a colon, the token is an identifier followed by a - * set expression. */ - if (ptr[0] == TOK_COLON) { - if (flecs_parse_identifier(token, term_id)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - return NULL; - } - - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, - NULL, term_id, TOK_PAREN_CLOSE); - if (!ptr) { - return NULL; - } - - /* Check for term flags */ - } else if (!ecs_os_strcmp(token, TOK_CASCADE) || - !ecs_os_strcmp(token, TOK_DESC) || - !ecs_os_strcmp(token, TOK_SELF) || - !ecs_os_strcmp(token, TOK_UP) || - !ecs_os_strcmp(token, TOK_DOWN) || - !(ecs_os_strcmp(token, TOK_PARENT))) - { - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, - token, term_id, TOK_PAREN_CLOSE); - if (!ptr) { - return NULL; - } - - /* Regular identifier */ - } else if (flecs_parse_identifier(token, term_id)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - return NULL; - } - - if (ptr[0] == TOK_AND) { - ptr = ecs_parse_ws(ptr + 1); - - if (term) { - term->id_flags = ECS_PAIR; - } - - } else if (ptr[0] == TOK_PAREN_CLOSE) { - ptr = ecs_parse_ws(ptr + 1); - break; - - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected ',' or ')'"); - return NULL; - } - - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected identifier or set expression"); - return NULL; - } - - arg ++; - - } while (true); - - return ptr; -} - -static -void flecs_parser_unexpected_char( - const char *name, - const char *expr, - const char *ptr, - char ch) -{ - if (ch && (ch != '\n')) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected character '%c'", ch); - } else { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected end of term"); - } -} - -static -const char* flecs_parse_term( - const ecs_world_t *world, - const char *name, - const char *expr, - ecs_term_t *term_out, - ecs_term_id_t *extra_args) -{ - const char *ptr = expr; - char token[ECS_MAX_TOKEN_SIZE] = {0}; - ecs_term_t term = { .move = true /* parser never owns resources */ }; - - ptr = ecs_parse_ws(ptr); - - /* Inout specifiers always come first */ - if (ptr[0] == TOK_BRACKET_OPEN) { - ptr = flecs_parse_annotation(name, expr, (ptr - expr), ptr + 1, &term.inout); - if (!ptr) { - goto error; - } - ptr = ecs_parse_ws(ptr); - } - - if (flecs_valid_operator_char(ptr[0])) { - term.oper = flecs_parse_operator(ptr[0]); - ptr = ecs_parse_ws(ptr + 1); - } - - /* If next token is the start of an identifier, it could be either a type - * role, source or component identifier */ - if (flecs_valid_identifier_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - goto error; - } - - /* Is token a type role? */ - if (ptr[0] == TOK_BITWISE_OR && ptr[1] != TOK_BITWISE_OR) { - ptr ++; - goto flecs_parse_role; - } - - /* Is token a predicate? */ - if (ptr[0] == TOK_PAREN_OPEN) { - goto parse_predicate; - } - - /* Next token must be a predicate */ - goto parse_predicate; - - /* Pair with implicit subject */ - } else if (ptr[0] == TOK_PAREN_OPEN) { - goto parse_pair; - - /* Open query scope */ - } else if (ptr[0] == TOK_SCOPE_OPEN) { - term.first.id = EcsScopeOpen; - term.src.id = 0; - term.src.flags = EcsIsEntity; - term.inout = EcsInOutNone; - goto parse_done; - - /* Close query scope */ - } else if (ptr[0] == TOK_SCOPE_CLOSE) { - term.first.id = EcsScopeClose; - term.src.id = 0; - term.src.flags = EcsIsEntity; - term.inout = EcsInOutNone; - ptr = ecs_parse_ws(ptr + 1); - goto parse_done; - - /* Nothing else expected here */ - } else { - flecs_parser_unexpected_char(name, expr, ptr, ptr[0]); - goto error; - } - -flecs_parse_role: - term.id_flags = flecs_parse_role(name, expr, (ptr - expr), token); - if (!term.id_flags) { - goto error; - } - - ptr = ecs_parse_ws(ptr); - - /* If next token is the source token, this is an empty source */ - if (flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - goto error; - } - - /* If not, it's a predicate */ - goto parse_predicate; - - } else if (ptr[0] == TOK_PAREN_OPEN) { - goto parse_pair; - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected identifier after role"); - goto error; - } - -parse_predicate: - if (flecs_parse_identifier(token, &term.first)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - goto error; - } - - /* Set expression */ - if (ptr[0] == TOK_COLON) { - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, NULL, - &term.first, TOK_COLON); - if (!ptr) { - goto error; - } - - ptr = ecs_parse_ws(ptr); - - if (ptr[0] == TOK_AND || !ptr[0]) { - goto parse_done; - } - - if (ptr[0] != TOK_COLON) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected token '%c' after predicate set expression", ptr[0]); - goto error; - } - - ptr = ecs_parse_ws(ptr + 1); - } else if (!ecs_os_strncmp(ptr, TOK_EQ, 2)) { - ptr = ecs_parse_ws(ptr + 2); - goto parse_eq; - } else if (!ecs_os_strncmp(ptr, TOK_NEQ, 2)) { - ptr = ecs_parse_ws(ptr + 2); - goto parse_neq; - } else if (!ecs_os_strncmp(ptr, TOK_MATCH, 2)) { - ptr = ecs_parse_ws(ptr + 2); - goto parse_match; - } else { - ptr = ecs_parse_ws(ptr); - } - - if (ptr[0] == TOK_PAREN_OPEN) { - ptr ++; - if (ptr[0] == TOK_PAREN_CLOSE) { - term.src.flags = EcsIsEntity; - term.src.id = 0; - ptr ++; - ptr = ecs_parse_ws(ptr); - } else { - ptr = flecs_parse_arguments( - world, name, expr, (ptr - expr), ptr, token, &term, extra_args); - } - - goto parse_done; - } - - goto parse_done; - -parse_eq: - term.src = term.first; - term.first = (ecs_term_id_t){0}; - term.first.id = EcsPredEq; - goto parse_right_operand; - -parse_neq: - term.src = term.first; - term.first = (ecs_term_id_t){0}; - term.first.id = EcsPredEq; - if (term.oper != EcsAnd) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid operator combination"); - goto error; - } - term.oper = EcsNot; - goto parse_right_operand; - -parse_match: - term.src = term.first; - term.first = (ecs_term_id_t){0}; - term.first.id = EcsPredMatch; - goto parse_right_operand; - -parse_right_operand: - if (flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - goto error; - } - - if (term.first.id == EcsPredMatch) { - if (token[0] == '"' && token[1] == '!') { - term.oper = EcsNot; - } - } - - if (flecs_parse_identifier(token, &term.second)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - goto error; - } - - term.src.flags &= ~EcsTraverseFlags; - term.src.flags |= EcsSelf; - term.inout = EcsInOutNone; - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected identifier"); - goto error; - } - goto parse_done; -parse_pair: - ptr = ecs_parse_identifier(name, expr, ptr + 1, token); - if (!ptr) { - goto error; - } - - if (ptr[0] == TOK_COLON) { - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, - NULL, &term.first, TOK_PAREN_CLOSE); - if (!ptr) { - goto error; - } - } - - if (ptr[0] == TOK_AND) { - ptr = ecs_parse_ws(ptr + 1); - if (ptr[0] == TOK_PAREN_CLOSE) { - ecs_parser_error(name, expr, (ptr - expr), - "expected identifier for second element of pair"); - goto error; - } - - term.src.id = EcsThis; - term.src.flags |= EcsIsVariable; - goto parse_pair_predicate; - } else if (ptr[0] == TOK_PAREN_CLOSE) { - term.src.id = EcsThis; - term.src.flags |= EcsIsVariable; - goto parse_pair_predicate; - } else { - flecs_parser_unexpected_char(name, expr, ptr, ptr[0]); - goto error; - } - -parse_pair_predicate: - if (flecs_parse_identifier(token, &term.first)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - goto error; - } - - ptr = ecs_parse_ws(ptr); - if (flecs_valid_token_start_char(ptr[0])) { - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - goto error; - } - - if (ptr[0] == TOK_COLON) { - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_term_flags(world, name, expr, (ptr - expr), ptr, - NULL, &term.second, TOK_PAREN_CLOSE); - if (!ptr) { - goto error; - } - } - - if (ptr[0] == TOK_PAREN_CLOSE || ptr[0] == TOK_AND) { - goto parse_pair_object; - } else { - flecs_parser_unexpected_char(name, expr, ptr, ptr[0]); - goto error; - } - } else if (ptr[0] == TOK_PAREN_CLOSE) { - /* No object */ - ptr ++; - goto parse_done; - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected pair object or ')'"); - goto error; - } - -parse_pair_object: - if (flecs_parse_identifier(token, &term.second)) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid identifier '%s'", token); - goto error; - } - - if (term.id_flags == 0) { - term.id_flags = ECS_PAIR; - } - - if (ptr[0] == TOK_AND) { - ptr = ecs_parse_ws(ptr + 1); - ptr = flecs_parse_arguments( - world, name, expr, (ptr - expr), ptr, token, NULL, extra_args); - if (!ptr) { - goto error; - } - } else { - ptr ++; - } - - ptr = ecs_parse_ws(ptr); - goto parse_done; - -parse_done: - *term_out = term; - return ptr; - -error: - ecs_term_fini(&term); - *term_out = (ecs_term_t){0}; - return NULL; -} - -static -bool flecs_is_valid_end_of_term( - const char *ptr) -{ - if ((ptr[0] == TOK_AND) || /* another term with And operator */ - (ptr[0] == TOK_OR[0]) || /* another term with Or operator */ - (ptr[0] == '\n') || /* newlines are valid */ - (ptr[0] == '\0') || /* end of string */ - (ptr[0] == '/') || /* comment (in plecs) */ - (ptr[0] == '{') || /* scope (in plecs) */ - (ptr[0] == '}') || - (ptr[0] == '[') || /* collection scope (in plecs) */ - (ptr[0] == ']') || - (ptr[0] == ':') || /* inheritance (in plecs) */ - (ptr[0] == '=')) /* assignment (in plecs) */ - { - return true; - } - return false; -} - -char* ecs_parse_term( - const ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_term_t *term, - ecs_term_id_t *extra_args) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(term != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_term_id_t *src = &term->src; - - if (ptr != expr) { - if (ptr[0]) { - if (ptr[0] == ',') { - ptr ++; - } else if (ptr[0] == '|') { - ptr += 2; - } else if (ptr[0] == '{') { - ptr ++; - } else if (ptr[0] == '}') { - /* nothing to be done */ - } else { - ecs_parser_error(name, expr, (ptr - expr), - "invalid preceding token"); - } - } - } - - ptr = ecs_parse_ws_eol(ptr); - if (!ptr[0]) { - *term = (ecs_term_t){0}; - return ECS_CONST_CAST(char*, ptr); - } - - if (ptr == expr && !strcmp(expr, "0")) { - return ECS_CONST_CAST(char*, &ptr[1]); - } - - /* Parse next element */ - ptr = flecs_parse_term(world, name, ptr, term, extra_args); - if (!ptr) { - goto error; - } - - /* Check for $() notation */ - if (term->first.name && !ecs_os_strcmp(term->first.name, "$")) { - if (term->src.name) { - /* Safe, parser owns name */ - ecs_os_free(ECS_CONST_CAST(char*, term->first.name)); - - term->first = term->src; - - if (term->second.name) { - term->src = term->second; - } else { - term->src.id = EcsThis; - term->src.name = NULL; - term->src.flags |= EcsIsVariable; - } - - const char *var_name = strrchr(term->first.name, '.'); - if (var_name) { - var_name ++; - } else { - var_name = term->first.name; - } - - term->second.name = ecs_os_strdup(var_name); - term->second.flags |= EcsIsVariable; - } - } - - /* Post-parse consistency checks */ - - /* If next token is OR, term is part of an OR expression */ - if (!ecs_os_strncmp(ptr, TOK_OR, 2)) { - /* An OR operator must always follow an AND or another OR */ - if (term->oper != EcsAnd) { - ecs_parser_error(name, expr, (ptr - expr), - "cannot combine || with other operators"); - goto error; - } - - term->oper = EcsOr; - } - - /* Term must either end in end of expression, AND or OR token */ - if (!flecs_is_valid_end_of_term(ptr)) { - if (!flecs_isident(ptr[0]) || ((ptr != expr) && (ptr[-1] != ' '))) { - ecs_parser_error(name, expr, (ptr - expr), - "expected end of expression or next term"); - goto error; - } - } - - /* If the term just contained a 0, the expression has nothing. Ensure - * that after the 0 nothing else follows */ - if (term->first.name && !ecs_os_strcmp(term->first.name, "0")) { - if (ptr[0]) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected term after 0"); - goto error; - } - - if (src->flags != 0) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid combination of 0 with non-default subject"); - goto error; - } - - src->flags = EcsIsEntity; - src->id = 0; - /* Safe, parser owns string */ - ecs_os_free(ECS_CONST_CAST(char*, term->first.name)); - term->first.name = NULL; - } - - /* Cannot combine EcsIsEntity/0 with operators other than AND */ - if (term->oper != EcsAnd && ecs_term_match_0(term)) { - if (term->first.id != EcsScopeOpen && term->first.id != EcsScopeClose) { - ecs_parser_error(name, expr, (ptr - expr), - "invalid operator for empty source"); - goto error; - } - } - - /* Automatically assign This if entity is not assigned and the set is - * nothing */ - if (!(src->flags & EcsIsEntity)) { - if (!src->name) { - if (!src->id) { - src->id = EcsThis; - src->flags |= EcsIsVariable; - } - } - } - - if (src->name && !ecs_os_strcmp(src->name, "0")) { - src->id = 0; - src->flags = EcsIsEntity; - } - - /* Process role */ - if (term->id_flags == ECS_AND) { - term->oper = EcsAndFrom; - term->id_flags = 0; - } else if (term->id_flags == ECS_OR) { - term->oper = EcsOrFrom; - term->id_flags = 0; - } else if (term->id_flags == ECS_NOT) { - term->oper = EcsNotFrom; - term->id_flags = 0; - } - - ptr = ecs_parse_ws(ptr); - - return ECS_CONST_CAST(char*, ptr); -error: - if (term) { - ecs_term_fini(term); - } - return NULL; -} - -#endif diff --git a/vendors/flecs/src/addons/pipeline/pipeline.c b/vendors/flecs/src/addons/pipeline/pipeline.c index d90453c35..f28fafc19 100644 --- a/vendors/flecs/src/addons/pipeline/pipeline.c +++ b/vendors/flecs/src/addons/pipeline/pipeline.c @@ -1,5 +1,5 @@ /** - * @file addons/ipeline/pipeline.c + * @file addons/pipeline/pipeline.c * @brief Functions for building and running pipelines. */ @@ -13,7 +13,7 @@ static void flecs_pipeline_free( ecs_pipeline_state_t *p) { if (p) { - ecs_world_t *world = p->query->filter.world; + ecs_world_t *world = p->query->world; ecs_allocator_t *a = &world->allocator; ecs_vec_fini_t(a, &p->ops, ecs_pipeline_op_t); ecs_vec_fini_t(a, &p->systems, ecs_entity_t); @@ -130,17 +130,17 @@ bool flecs_pipeline_check_term( { (void)world; - ecs_term_id_t *src = &term->src; - if (src->flags & EcsInOutNone) { + ecs_term_ref_t *src = &term->src; + if (term->inout == EcsInOutNone || term->inout == EcsInOutFilter) { return false; } ecs_id_t id = term->id; - ecs_oper_kind_t oper = term->oper; - ecs_inout_kind_t inout = term->inout; + int16_t oper = term->oper; + int16_t inout = term->inout; bool from_any = ecs_term_match_0(term); bool from_this = ecs_term_match_this(term); - bool is_shared = !from_any && (!from_this || !(src->flags & EcsSelf)); + bool is_shared = !from_any && (!from_this || !(src->id & EcsSelf)); ecs_write_kind_t ws = flecs_pipeline_get_write_state(write_state, id); @@ -183,6 +183,7 @@ bool flecs_pipeline_check_term( break; case EcsInOutDefault: case EcsInOutNone: + case EcsInOutFilter: case EcsIn: break; } @@ -191,13 +192,14 @@ bool flecs_pipeline_check_term( case EcsIn: case EcsInOut: if (ws == WriteStateToStage) { - /* If a system does a get/get_mut, the component is fetched from + /* If a system does a get/ensure, the component is fetched from * the main store so it must be merged first */ return true; } /* fall through */ case EcsInOutDefault: case EcsInOutNone: + case EcsInOutFilter: case EcsOut: break; } @@ -209,13 +211,13 @@ bool flecs_pipeline_check_term( static bool flecs_pipeline_check_terms( ecs_world_t *world, - ecs_filter_t *filter, + ecs_query_t *query, bool is_active, ecs_write_state_t *ws) { bool needs_merge = false; - ecs_term_t *terms = filter->terms; - int32_t t, term_count = filter->term_count; + ecs_term_t *terms = query->terms; + int32_t t, term_count = query->term_count; /* Check This terms first. This way if a term indicating writing to a stage * was added before the term, it won't cause merging. */ @@ -242,7 +244,7 @@ EcsPoly* flecs_pipeline_term_system( ecs_iter_t *it) { int32_t index = ecs_table_get_column_index( - it->real_world, it->table, ecs_poly_id(EcsSystem)); + it->real_world, it->table, flecs_poly_id(EcsSystem)); ecs_assert(index != -1, ECS_INTERNAL_ERROR, NULL); EcsPoly *poly = ecs_table_get_column(it->table, index, it->offset); ecs_assert(poly != NULL, ECS_INTERNAL_ERROR, NULL); @@ -256,7 +258,8 @@ bool flecs_pipeline_build( { ecs_iter_t it = ecs_query_iter(world, pq->query); - if (pq->match_count == pq->query->match_count) { + int32_t new_match_count = ecs_query_match_count(pq->query); + if (pq->match_count == new_match_count) { /* No need to rebuild the pipeline */ ecs_iter_fini(&it); return false; @@ -275,28 +278,29 @@ bool flecs_pipeline_build( ecs_vec_reset_t(a, &pq->systems, ecs_entity_t); bool multi_threaded = false; - bool no_readonly = false; + bool immediate = false; bool first = true; /* Iterate systems in pipeline, add ops for running / merging */ while (ecs_query_next(&it)) { EcsPoly *poly = flecs_pipeline_term_system(&it); - bool is_active = ecs_table_get_type_index(world, it.table, EcsEmpty) == -1; + bool is_active = ecs_table_get_type_index( + world, it.table, EcsEmpty) == -1; int32_t i; for (i = 0; i < it.count; i ++) { - ecs_poly_assert(poly[i].poly, ecs_system_t); + flecs_poly_assert(poly[i].poly, ecs_system_t); ecs_system_t *sys = (ecs_system_t*)poly[i].poly; ecs_query_t *q = sys->query; bool needs_merge = false; needs_merge = flecs_pipeline_check_terms( - world, &q->filter, is_active, &ws); + world, q, is_active, &ws); if (is_active) { if (first) { multi_threaded = sys->multi_threaded; - no_readonly = sys->no_readonly; + immediate = sys->immediate; first = false; } @@ -304,13 +308,13 @@ bool flecs_pipeline_build( needs_merge = true; multi_threaded = sys->multi_threaded; } - if (sys->no_readonly != no_readonly) { + if (sys->immediate != immediate) { needs_merge = true; - no_readonly = sys->no_readonly; + immediate = sys->immediate; } } - if (no_readonly) { + if (immediate) { needs_merge = true; } @@ -333,7 +337,7 @@ bool flecs_pipeline_build( needs_merge = false; if (is_active) { needs_merge = flecs_pipeline_check_terms( - world, &q->filter, true, &ws); + world, q, true, &ws); } /* The component states were just reset, so if we conclude that @@ -346,7 +350,7 @@ bool flecs_pipeline_build( op->offset = ecs_vec_count(&pq->systems); op->count = 0; op->multi_threaded = false; - op->no_readonly = false; + op->immediate = false; op->time_spent = 0; op->commands_enqueued = 0; } @@ -358,7 +362,7 @@ bool flecs_pipeline_build( it.entities[i]; if (!op->count) { op->multi_threaded = multi_threaded; - op->no_readonly = no_readonly; + op->immediate = immediate; } op->count ++; } @@ -383,7 +387,7 @@ bool flecs_pipeline_build( ecs_log_push_1(); ecs_dbg("#[green]schedule#[reset]: threading: %d, staging: %d:", - op->multi_threaded, !op->no_readonly); + op->multi_threaded, !op->immediate); ecs_log_push_1(); int32_t i, count = ecs_vec_count(&pq->systems); @@ -392,11 +396,11 @@ bool flecs_pipeline_build( for (i = 0; i < count; i ++) { ecs_entity_t system = systems[i]; const EcsPoly *poly = ecs_get_pair(world, system, EcsPoly, EcsSystem); - ecs_poly_assert(poly->poly, ecs_system_t); + flecs_poly_assert(poly->poly, ecs_system_t); ecs_system_t *sys = (ecs_system_t*)poly->poly; #ifdef FLECS_LOG_1 - char *path = ecs_get_fullpath(world, system); + char *path = ecs_get_path(world, system); const char *doc_name = NULL; #ifdef FLECS_DOC const EcsDocDescription *doc_name_id = ecs_get_pair(world, system, @@ -427,7 +431,7 @@ bool flecs_pipeline_build( "#[green]schedule#[reset]: " "threading: %d, staging: %d:", op[op_index].multi_threaded, - !op[op_index].no_readonly); + !op[op_index].immediate); } ecs_log_push_1(); } @@ -447,7 +451,7 @@ bool flecs_pipeline_build( ecs_log_pop_1(); } - pq->match_count = pq->query->match_count; + pq->match_count = new_match_count; ecs_assert(pq->cur_op <= ecs_vec_last_t(&pq->ops, ecs_pipeline_op_t), ECS_INTERNAL_ERROR, NULL); @@ -477,8 +481,9 @@ bool flecs_pipeline_update( ecs_pipeline_state_t *pq, bool start_of_frame) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot update pipeline while world is in readonly mode"); /* If any entity mutations happened that could have affected query matching * notify appropriate queries so caches are up to date. This includes the @@ -551,25 +556,27 @@ int32_t flecs_run_pipeline_ops( for (; i < count; i++) { ecs_entity_t system = systems[i]; const EcsPoly* poly = ecs_get_pair(world, system, EcsPoly, EcsSystem); - ecs_poly_assert(poly->poly, ecs_system_t); + flecs_poly_assert(poly->poly, ecs_system_t); ecs_system_t* sys = (ecs_system_t*)poly->poly; /* Keep track of the last frame for which the system has ran, so we - * know from where to resume the schedule in case the schedule - * changes during a merge. */ - sys->last_frame = world->info.frame_count_total + 1; + * know from where to resume the schedule in case the schedule + * changes during a merge. */ + if (stage_index == 0) { + sys->last_frame = world->info.frame_count_total + 1; + } ecs_stage_t* s = NULL; - if (!op->no_readonly) { - /* If system is no_readonly it operates on the actual world, not + if (!op->immediate) { + /* If system is immediate it operates on the actual world, not * the stage. Only pass stage to system if it's readonly. */ s = stage; } - ecs_run_intern(world, s, system, sys, stage_index, - stage_count, delta_time, 0, 0, NULL); + flecs_run_intern(world, s, system, sys, stage_index, + stage_count, delta_time, NULL); - world->info.systems_ran_frame++; + ecs_os_linc(&world->info.systems_ran_frame); ran_since_merge++; if (ran_since_merge == op->count) { @@ -586,18 +593,18 @@ void flecs_run_pipeline( ecs_pipeline_state_t *pq, ecs_ftime_t delta_time) { - ecs_assert(world != NULL, ECS_INVALID_OPERATION, NULL); + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(pq != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(pq->query != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_poly_assert(world, ecs_stage_t); + flecs_poly_assert(world, ecs_stage_t); ecs_stage_t *stage = flecs_stage_from_world(&world); - int32_t stage_index = ecs_get_stage_id(stage->thread_ctx); + int32_t stage_index = ecs_stage_get_id(stage->thread_ctx); int32_t stage_count = ecs_get_stage_count(world); + bool multi_threaded = world->worker_cond != 0; - ecs_assert(!stage_index, ECS_INVALID_OPERATION, NULL); - - bool multi_threaded = ecs_get_stage_count(world) > 1; + ecs_assert(!stage_index, ECS_INVALID_OPERATION, + "cannot run pipeline on stage"); // Update the pipeline the workers will execute world->pq = pq; @@ -613,13 +620,13 @@ void flecs_run_pipeline( continue; } - bool no_readonly = pq->cur_op->no_readonly; + bool immediate = pq->cur_op->immediate; bool op_multi_threaded = multi_threaded && pq->cur_op->multi_threaded; - pq->no_readonly = no_readonly; + pq->immediate = immediate; - if (!no_readonly) { - ecs_readonly_begin(world); + if (!immediate) { + ecs_readonly_begin(world, multi_threaded); } ECS_BIT_COND(world->flags, EcsWorldMultiThreaded, op_multi_threaded); @@ -647,7 +654,7 @@ void flecs_run_pipeline( flecs_wait_for_sync(world); } - if (!no_readonly) { + if (!immediate) { ecs_time_t mt = { 0 }; if (measure_time) { ecs_time_measure(&mt); @@ -655,7 +662,7 @@ void flecs_run_pipeline( int32_t si; for (si = 0; si < stage_count; si ++) { - ecs_stage_t *s = &world->stages[si]; + ecs_stage_t *s = world->stages[si]; pq->cur_op->commands_enqueued += ecs_vec_count(&s->cmd->queue); } @@ -696,14 +703,14 @@ void flecs_run_startup_systems( ecs_log_push_2(); ecs_entity_t start_pip = ecs_pipeline_init(world, &(ecs_pipeline_desc_t){ .query = { - .filter.terms = { + .terms = { { .id = EcsSystem }, - { .id = EcsPhase, .src.flags = EcsCascade, .src.trav = EcsDependsOn }, - { .id = ecs_dependson(EcsOnStart), .src.trav = EcsDependsOn }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsDependsOn, .oper = EcsNot }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsChildOf, .oper = EcsNot } + { .id = EcsPhase, .src.id = EcsCascade, .trav = EcsDependsOn }, + { .id = ecs_dependson(EcsOnStart), .trav = EcsDependsOn }, + { .id = EcsDisabled, .src.id = EcsUp, .trav = EcsDependsOn, .oper = EcsNot }, + { .id = EcsDisabled, .src.id = EcsUp, .trav = EcsChildOf, .oper = EcsNot } }, - .order_by = flecs_entity_compare + .order_by_callback = flecs_entity_compare } }); ecs_log_pop_2(); @@ -713,7 +720,8 @@ void flecs_run_startup_systems( ecs_log_push_2(); ecs_assert(start_pip != 0, ECS_INTERNAL_ERROR, NULL); const EcsPipeline *p = ecs_get(world, start_pip, EcsPipeline); - ecs_check(p != NULL, ECS_INVALID_OPERATION, NULL); + ecs_check(p != NULL, ECS_INVALID_OPERATION, + "pipeline entity is missing flecs.pipeline.Pipeline component"); flecs_workers_progress(world, p->state, 0); ecs_log_pop_2(); @@ -748,7 +756,8 @@ bool ecs_progress( ecs_dbg_3("#[bold]progress#[reset](dt = %.2f)", (double)delta_time); ecs_log_push_3(); const EcsPipeline *p = ecs_get(world, world->pipeline, EcsPipeline); - ecs_check(p != NULL, ECS_INVALID_OPERATION, NULL); + ecs_check(p != NULL, ECS_INVALID_OPERATION, + "pipeline entity is missing flecs.pipeline.Pipeline component"); flecs_workers_progress(world, p->state, delta_time); ecs_log_pop_3(); @@ -782,7 +791,7 @@ void ecs_set_pipeline( ecs_world_t *world, ecs_entity_t pipeline) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check( ecs_get(world, pipeline, EcsPipeline) != NULL, ECS_INVALID_PARAMETER, "not a pipeline"); @@ -805,19 +814,19 @@ ecs_entity_t ecs_pipeline_init( ecs_world_t *world, const ecs_pipeline_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); ecs_entity_t result = desc->entity; if (!result) { - result = ecs_new(world, 0); + result = ecs_new(world); } ecs_query_desc_t qd = desc->query; - if (!qd.order_by) { - qd.order_by = flecs_entity_compare; + if (!qd.order_by_callback) { + qd.order_by_callback = flecs_entity_compare; } - qd.filter.entity = result; + qd.entity = result; ecs_query_t *query = ecs_query_init(world, &qd); if (!query) { @@ -825,9 +834,9 @@ ecs_entity_t ecs_pipeline_init( return 0; } - ecs_check(query->filter.terms != NULL, ECS_INVALID_PARAMETER, + ecs_check(query->terms != NULL, ECS_INVALID_PARAMETER, "pipeline query cannot be empty"); - ecs_check(query->filter.terms[0].id == EcsSystem, + ecs_check(query->terms[0].id == EcsSystem, ECS_INVALID_PARAMETER, "pipeline must start with System term"); ecs_pipeline_state_t *pq = ecs_os_calloc_t(ecs_pipeline_state_t); @@ -876,7 +885,11 @@ void FlecsPipelineImport( { ECS_MODULE(world, FlecsPipeline); ECS_IMPORT(world, FlecsSystem); - +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsPipeline), + "Module that schedules and runs systems"); +#endif ecs_set_name_prefix(world, "Ecs"); flecs_bootstrap_component(world, EcsPipeline); @@ -886,17 +899,17 @@ void FlecsPipelineImport( * relationships. This ensures that, for example, EcsOnUpdate doesn't have a * direct DependsOn relationship on EcsPreUpdate, which ensures that when * the EcsPreUpdate phase is disabled, EcsOnUpdate still runs. */ - ecs_entity_t phase_0 = ecs_new(world, 0); - ecs_entity_t phase_1 = ecs_new_w_pair(world, EcsDependsOn, phase_0); - ecs_entity_t phase_2 = ecs_new_w_pair(world, EcsDependsOn, phase_1); - ecs_entity_t phase_3 = ecs_new_w_pair(world, EcsDependsOn, phase_2); - ecs_entity_t phase_4 = ecs_new_w_pair(world, EcsDependsOn, phase_3); - ecs_entity_t phase_5 = ecs_new_w_pair(world, EcsDependsOn, phase_4); - ecs_entity_t phase_6 = ecs_new_w_pair(world, EcsDependsOn, phase_5); - ecs_entity_t phase_7 = ecs_new_w_pair(world, EcsDependsOn, phase_6); - ecs_entity_t phase_8 = ecs_new_w_pair(world, EcsDependsOn, phase_7); - - flecs_bootstrap_phase(world, EcsOnStart, 0); + ecs_entity_t phase_0 = ecs_entity(world, {0}); + ecs_entity_t phase_1 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_0)) }); + ecs_entity_t phase_2 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_1)) }); + ecs_entity_t phase_3 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_2)) }); + ecs_entity_t phase_4 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_3)) }); + ecs_entity_t phase_5 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_4)) }); + ecs_entity_t phase_6 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_5)) }); + ecs_entity_t phase_7 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_6)) }); + ecs_entity_t phase_8 = ecs_entity(world, { .add = ecs_ids(ecs_dependson(phase_7)) }); + + flecs_bootstrap_phase(world, EcsOnStart, 0); flecs_bootstrap_phase(world, EcsPreFrame, 0); flecs_bootstrap_phase(world, EcsOnLoad, phase_0); flecs_bootstrap_phase(world, EcsPostLoad, phase_1); @@ -909,7 +922,7 @@ void FlecsPipelineImport( flecs_bootstrap_phase(world, EcsPostFrame, phase_8); ecs_set_hooks(world, EcsPipeline, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(EcsPipeline), .move = ecs_move(EcsPipeline) }); @@ -917,14 +930,14 @@ void FlecsPipelineImport( world->pipeline = ecs_pipeline(world, { .entity = ecs_entity(world, { .name = "BuiltinPipeline" }), .query = { - .filter.terms = { + .terms = { { .id = EcsSystem }, - { .id = EcsPhase, .src.flags = EcsCascade, .src.trav = EcsDependsOn }, - { .id = ecs_dependson(EcsOnStart), .src.trav = EcsDependsOn, .oper = EcsNot }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsDependsOn, .oper = EcsNot }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsChildOf, .oper = EcsNot } + { .id = EcsPhase, .src.id = EcsCascade, .trav = EcsDependsOn }, + { .id = ecs_dependson(EcsOnStart), .trav = EcsDependsOn, .oper = EcsNot }, + { .id = EcsDisabled, .src.id = EcsUp, .trav = EcsDependsOn, .oper = EcsNot }, + { .id = EcsDisabled, .src.id = EcsUp, .trav = EcsChildOf, .oper = EcsNot } }, - .order_by = flecs_entity_compare + .order_by_callback = flecs_entity_compare } }); diff --git a/vendors/flecs/src/addons/pipeline/pipeline.h b/vendors/flecs/src/addons/pipeline/pipeline.h index 3319365ac..12aa8bb20 100644 --- a/vendors/flecs/src/addons/pipeline/pipeline.h +++ b/vendors/flecs/src/addons/pipeline/pipeline.h @@ -16,7 +16,7 @@ typedef struct ecs_pipeline_op_t { double time_spent; /* Time spent merging commands for sync point */ int64_t commands_enqueued; /* Number of commands enqueued for sync point */ bool multi_threaded; /* Whether systems can be ran multi threaded */ - bool no_readonly; /* Whether systems are staged or not */ + bool immediate; /* Whether systems are staged or not */ } ecs_pipeline_op_t; struct ecs_pipeline_state_t { @@ -35,7 +35,7 @@ struct ecs_pipeline_state_t { ecs_pipeline_op_t *cur_op; /* Current pipeline op */ int32_t cur_i; /* Index in current result */ int32_t ran_since_merge; /* Index in current op */ - bool no_readonly; /* Is pipeline in readonly mode */ + bool immediate; /* Is pipeline in readonly mode */ }; typedef struct EcsPipeline { diff --git a/vendors/flecs/src/addons/pipeline/worker.c b/vendors/flecs/src/addons/pipeline/worker.c index 95bd1e151..5f2c7fed8 100644 --- a/vendors/flecs/src/addons/pipeline/worker.c +++ b/vendors/flecs/src/addons/pipeline/worker.c @@ -37,8 +37,8 @@ void* flecs_worker(void *arg) { ecs_stage_t *stage = arg; ecs_world_t *world = stage->world; - ecs_poly_assert(world, ecs_world_t); - ecs_poly_assert(stage, ecs_stage_t); + flecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(stage, ecs_stage_t); ecs_dbg_2("worker %d: start", stage->id); @@ -80,13 +80,13 @@ void* flecs_worker(void *arg) { void flecs_create_worker_threads( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); int32_t stages = ecs_get_stage_count(world); for (int32_t i = 1; i < stages; i ++) { ecs_stage_t *stage = (ecs_stage_t*)ecs_get_stage(world, i); ecs_assert(stage != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_poly_assert(stage, ecs_stage_t); + flecs_poly_assert(stage, ecs_stage_t); ecs_assert(stage->thread == 0, ECS_INTERNAL_ERROR, NULL); if (ecs_using_task_threads(world)) { @@ -97,7 +97,8 @@ void flecs_create_worker_threads( /* workers are using long-running os threads */ stage->thread = ecs_os_thread_new(flecs_worker, stage); } - ecs_assert(stage->thread != 0, ECS_OPERATION_FAILED, NULL); + ecs_assert(stage->thread != 0, ECS_OPERATION_FAILED, + "failed to create thread"); } } @@ -120,7 +121,7 @@ static void flecs_wait_for_workers( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); int32_t stage_count = ecs_get_stage_count(world); if (stage_count <= 1) { @@ -181,15 +182,14 @@ void flecs_signal_workers( void flecs_join_worker_threads( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); bool threads_active = false; /* Test if threads are created. Cannot use workers_running, since this is * a potential race if threads haven't spun up yet. */ - ecs_stage_t *stages = world->stages; int i, count = world->stage_count; for (i = 1; i < count; i ++) { - ecs_stage_t *stage = &stages[i]; + ecs_stage_t *stage = world->stages[i]; if (stage->thread) { threads_active = true; break; @@ -210,12 +210,13 @@ void flecs_join_worker_threads( /* Join all threads with main */ for (i = 1; i < count; i ++) { + ecs_stage_t *stage = world->stages[i]; if (ecs_using_task_threads(world)) { - ecs_os_task_join(stages[i].thread); + ecs_os_task_join(stage->thread); } else { - ecs_os_thread_join(stages[i].thread); + ecs_os_thread_join(stage->thread); } - stages[i].thread = 0; + stage->thread = 0; } world->flags &= ~EcsWorldQuitWorkers; @@ -228,8 +229,9 @@ void flecs_workers_progress( ecs_pipeline_state_t *pq, ecs_ftime_t delta_time) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!ecs_is_deferred(world), ECS_INVALID_OPERATION, NULL); + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!ecs_is_deferred(world), ECS_INVALID_OPERATION, + "cannot call progress while world is deferred"); /* Make sure workers are running and ready */ flecs_wait_for_workers(world); diff --git a/vendors/flecs/src/addons/plecs.c b/vendors/flecs/src/addons/plecs.c deleted file mode 100644 index 94841564f..000000000 --- a/vendors/flecs/src/addons/plecs.c +++ /dev/null @@ -1,2309 +0,0 @@ -/** - * @file addons/plecs.c - * @brief Plecs addon. - */ - -#include "flecs.h" - -#ifdef FLECS_PLECS - -ECS_COMPONENT_DECLARE(EcsScript); - -#include "../private_api.h" -#include - -#define TOK_NEWLINE '\n' -#define TOK_USING "using" -#define TOK_MODULE "module" -#define TOK_WITH "with" -#define TOK_CONST "const" -#define TOK_PROP "prop" -#define TOK_ASSEMBLY "assembly" - -#define STACK_MAX_SIZE (64) - -typedef struct { - ecs_value_t value; - bool owned; -} plecs_with_value_t; - -typedef struct { - const char *name; - const char *code; - - ecs_entity_t last_predicate; - ecs_entity_t last_subject; - ecs_entity_t last_object; - - ecs_id_t last_assign_id; - ecs_entity_t assign_to; - - ecs_entity_t scope[STACK_MAX_SIZE]; - ecs_entity_t default_scope_type[STACK_MAX_SIZE]; - ecs_entity_t with[STACK_MAX_SIZE]; - ecs_entity_t using[STACK_MAX_SIZE]; - int32_t with_frames[STACK_MAX_SIZE]; - plecs_with_value_t with_value_frames[STACK_MAX_SIZE]; - int32_t using_frames[STACK_MAX_SIZE]; - int32_t sp; - int32_t with_frame; - int32_t using_frame; - ecs_entity_t global_with; - ecs_entity_t assembly; - const char *assembly_start, *assembly_stop; - - char *annot[STACK_MAX_SIZE]; - int32_t annot_count; - - ecs_vars_t vars; - char var_name[256]; - ecs_entity_t var_type; - - bool with_stmt; - bool scope_assign_stmt; - bool assign_stmt; - bool assembly_stmt; - bool assembly_instance; - bool isa_stmt; - bool decl_stmt; - bool decl_type; - bool var_stmt; - bool var_is_prop; - bool is_module; - - int32_t errors; -} plecs_state_t; - -static -int flecs_plecs_parse( - ecs_world_t *world, - const char *name, - const char *expr, - ecs_vars_t *vars, - ecs_entity_t script, - ecs_entity_t instance); - -static void flecs_dtor_script(EcsScript *ptr) { - ecs_os_free(ptr->script); - ecs_vec_fini_t(NULL, &ptr->using_, ecs_entity_t); - - int i, count = ptr->prop_defaults.count; - ecs_value_t *values = ptr->prop_defaults.array; - for (i = 0; i < count; i ++) { - ecs_value_free(ptr->world, values[i].type, values[i].ptr); - } - - ecs_vec_fini_t(NULL, &ptr->prop_defaults, ecs_value_t); -} - -static -ECS_MOVE(EcsScript, dst, src, { - flecs_dtor_script(dst); - dst->using_ = src->using_; - dst->prop_defaults = src->prop_defaults; - dst->script = src->script; - dst->world = src->world; - ecs_os_zeromem(&src->using_); - ecs_os_zeromem(&src->prop_defaults); - src->script = NULL; - src->world = NULL; -}) - -static -ECS_DTOR(EcsScript, ptr, { - flecs_dtor_script(ptr); -}) - -/* Assembly ctor to initialize with default property values */ -static -void flecs_assembly_ctor( - void *ptr, - int32_t count, - const ecs_type_info_t *ti) -{ - ecs_world_t *world = ti->hooks.ctx; - ecs_entity_t assembly = ti->component; - const EcsStruct *st = ecs_get(world, assembly, EcsStruct); - - if (!st) { - ecs_err("assembly '%s' is not a struct, cannot construct", ti->name); - return; - } - - const EcsScript *script = ecs_get(world, assembly, EcsScript); - if (!script) { - ecs_err("assembly '%s' is not a script, cannot construct", ti->name); - return; - } - - if (st->members.count != script->prop_defaults.count) { - ecs_err("number of props (%d) of assembly '%s' does not match members" - " (%d), cannot construct", script->prop_defaults.count, - ti->name, st->members.count); - return; - } - - const ecs_member_t *members = st->members.array; - int32_t i, m, member_count = st->members.count; - ecs_value_t *values = script->prop_defaults.array; - for (m = 0; m < member_count; m ++) { - const ecs_member_t *member = &members[m]; - ecs_value_t *value = &values[m]; - const ecs_type_info_t *mti = ecs_get_type_info(world, member->type); - if (!mti) { - ecs_err("failed to get type info for prop '%s' of assembly '%s'", - member->name, ti->name); - return; - } - - for (i = 0; i < count; i ++) { - void *el = ECS_ELEM(ptr, ti->size, i); - ecs_value_copy_w_type_info(world, mti, - ECS_OFFSET(el, member->offset), value->ptr); - } - } -} - -/* Assembly on_set handler to update contents for new property values */ -static -void flecs_assembly_on_set( - ecs_iter_t *it) -{ - if (it->table->flags & EcsTableIsPrefab) { - /* Don't instantiate assemblies for prefabs */ - return; - } - - ecs_world_t *world = it->world; - ecs_entity_t assembly = ecs_field_id(it, 1); - const char *name = ecs_get_name(world, assembly); - ecs_record_t *r = ecs_record_find(world, assembly); - - const EcsComponent *ct = ecs_record_get(world, r, EcsComponent); - ecs_get(world, assembly, EcsComponent); - if (!ct) { - ecs_err("assembly '%s' is not a component", name); - return; - } - - const EcsStruct *st = ecs_record_get(world, r, EcsStruct); - if (!st) { - ecs_err("assembly '%s' is not a struct", name); - return; - } - - const EcsScript *script = ecs_record_get(world, r, EcsScript); - if (!script) { - ecs_err("assembly '%s' is missing a script", name); - return; - } - - void *data = ecs_field_w_size(it, flecs_ito(size_t, ct->size), 1); - - int32_t i, m; - for (i = 0; i < it->count; i ++) { - /* Create variables to hold assembly properties */ - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - /* Populate properties from assembly members */ - const ecs_member_t *members = st->members.array; - for (m = 0; m < st->members.count; m ++) { - const ecs_member_t *member = &members[m]; - - ecs_value_t v = {0}; /* Prevent allocating value */ - ecs_expr_var_t *var = ecs_vars_declare_w_value( - &vars, member->name, &v); - if (var == NULL) { - ecs_err("could not create prop '%s' for assembly '%s'", - member->name, name); - break; - } - - /* Assign assembly property from assembly instance */ - var->value.type = member->type; - var->value.ptr = ECS_OFFSET(data, member->offset); - var->owned = false; - } - - /* Populate $this variable with instance entity */ - ecs_entity_t instance = it->entities[i]; - ecs_value_t v = {0}; - ecs_expr_var_t *var = ecs_vars_declare_w_value( - &vars, "this", &v); - var->value.type = ecs_id(ecs_entity_t); - var->value.ptr = &instance; - var->owned = false; - - /* Update script with new code/properties */ - ecs_script_update(world, assembly, instance, script->script, &vars); - ecs_vars_fini(&vars); - - if (ecs_record_has_id(world, r, EcsFlatten)) { - ecs_flatten(it->real_world, ecs_childof(instance), NULL); - } - - data = ECS_OFFSET(data, ct->size); - } -} - -/* Delete contents of assembly instance */ -static -void flecs_assembly_on_remove( - ecs_iter_t *it) -{ - int32_t i; - for (i = 0; i < it->count; i ++) { - ecs_entity_t instance = it->entities[i]; - ecs_script_clear(it->world, 0, instance); - } -} - -/* Set default property values on assembly Script component */ -static -int flecs_assembly_init_defaults( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_entity_t assembly, - EcsScript *script, - plecs_state_t *state) -{ - const EcsStruct *st = ecs_get(world, assembly, EcsStruct); - int32_t i, count = st->members.count; - const ecs_member_t *members = st->members.array; - - ecs_vec_init_t(NULL, &script->prop_defaults, ecs_value_t, count); - - for (i = 0; i < count; i ++) { - const ecs_member_t *member = &members[i]; - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, member->name); - if (!var) { - char *assembly_name = ecs_get_fullpath(world, assembly); - ecs_parser_error(name, expr, ptr - expr, - "missing property '%s' for assembly '%s'", - member->name, assembly_name); - ecs_os_free(assembly_name); - return -1; - } - - if (member->type != var->value.type) { - char *assembly_name = ecs_get_fullpath(world, assembly); - ecs_parser_error(name, expr, ptr - expr, - "property '%s' for assembly '%s' has mismatching type", - member->name, assembly_name); - ecs_os_free(assembly_name); - return -1; - } - - ecs_value_t *pv = ecs_vec_append_t(NULL, - &script->prop_defaults, ecs_value_t); - pv->type = member->type; - pv->ptr = var->value.ptr; - var->owned = false; /* Transfer ownership */ - } - - return 0; -} - -/* Create new assembly */ -static -int flecs_assembly_create( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - ecs_entity_t assembly, - char *script_code, - plecs_state_t *state) -{ - const EcsStruct *st = ecs_get(world, assembly, EcsStruct); - if (!st || !st->members.count) { - char *assembly_name = ecs_get_fullpath(world, assembly); - ecs_parser_error(name, expr, ptr - expr, - "assembly '%s' has no properties", assembly_name); - ecs_os_free(assembly_name); - ecs_os_free(script_code); - return -1; - } - - ecs_add_id(world, assembly, EcsAlwaysOverride); - - EcsScript *script = ecs_get_mut(world, assembly, EcsScript); - flecs_dtor_script(script); - script->world = world; - script->script = script_code; - ecs_vec_reset_t(NULL, &script->using_, ecs_entity_t); - - ecs_entity_t scope = ecs_get_scope(world); - if (scope && (scope = ecs_get_target(world, scope, EcsChildOf, 0))) { - ecs_vec_append_t(NULL, &script->using_, ecs_entity_t)[0] = scope; - } - - int i, count = state->using_frame; - for (i = 0; i < count; i ++) { - ecs_vec_append_t(NULL, &script->using_, ecs_entity_t)[0] = - state->using[i]; - } - - if (flecs_assembly_init_defaults( - world, name, expr, ptr, assembly, script, state)) - { - return -1; - } - - ecs_modified(world, assembly, EcsScript); - - ecs_set_hooks_id(world, assembly, &(ecs_type_hooks_t) { - .ctor = flecs_assembly_ctor, - .on_set = flecs_assembly_on_set, - .on_remove = flecs_assembly_on_remove, - .ctx = world - }); - - return 0; -} - -/* Parser */ - -static -bool plecs_is_newline_comment( - const char *ptr) -{ - if (ptr[0] == '/' && ptr[1] == '/') { - return true; - } - return false; -} - -static -const char* plecs_parse_fluff( - const char *ptr) -{ - do { - /* Skip whitespaces before checking for a comment */ - ptr = ecs_parse_ws(ptr); - - /* Newline comment, skip until newline character */ - if (plecs_is_newline_comment(ptr)) { - ptr += 2; - - while (ptr[0] && ptr[0] != TOK_NEWLINE) { - ptr ++; - } - } - - /* If a newline character is found, skip it */ - if (ptr[0] == TOK_NEWLINE) { - ptr ++; - } - - } while (isspace(ptr[0]) || plecs_is_newline_comment(ptr)); - - return ptr; -} - -static -ecs_entity_t plecs_lookup( - const ecs_world_t *world, - const char *path, - plecs_state_t *state, - ecs_entity_t rel, - bool is_subject) -{ - ecs_entity_t e = 0; - - if (!is_subject) { - ecs_entity_t oneof = 0; - if (rel) { - if (ecs_has_id(world, rel, EcsOneOf)) { - oneof = rel; - } else { - oneof = ecs_get_target(world, rel, EcsOneOf, 0); - } - if (oneof) { - return ecs_lookup_path_w_sep( - world, oneof, path, NULL, NULL, false); - } - } - int using_scope = state->using_frame - 1; - for (; using_scope >= 0; using_scope--) { - e = ecs_lookup_path_w_sep( - world, state->using[using_scope], path, NULL, NULL, false); - if (e) { - break; - } - } - } - - if (!e) { - e = ecs_lookup_path_w_sep(world, 0, path, NULL, NULL, !is_subject); - } - - return e; -} - -/* Lookup action used for deserializing entity refs in component values */ -static -ecs_entity_t plecs_lookup_action( - const ecs_world_t *world, - const char *path, - void *ctx) -{ - return plecs_lookup(world, path, ctx, 0, false); -} - -static -void plecs_apply_with_frame( - ecs_world_t *world, - plecs_state_t *state, - ecs_entity_t e) -{ - int32_t i, frame_count = state->with_frames[state->sp]; - for (i = 0; i < frame_count; i ++) { - ecs_id_t id = state->with[i]; - plecs_with_value_t *v = &state->with_value_frames[i]; - if (v->value.type) { - void *ptr = ecs_get_mut_id(world, e, id); - ecs_value_copy(world, v->value.type, ptr, v->value.ptr); - ecs_modified_id(world, e, id); - } else { - ecs_add_id(world, e, id); - } - } -} - -static -ecs_entity_t plecs_ensure_entity( - ecs_world_t *world, - plecs_state_t *state, - const char *path, - ecs_entity_t rel, - bool is_subject) -{ - if (!path) { - return 0; - } - - ecs_entity_t e = 0; - bool is_anonymous = !ecs_os_strcmp(path, "_"); - bool is_new = false; - if (is_anonymous) { - path = NULL; - e = ecs_new_id(world); - is_new = true; - } - - if (!e) { - e = plecs_lookup(world, path, state, rel, is_subject); - } - - if (!e) { - is_new = true; - if (rel && flecs_get_oneof(world, rel)) { - /* If relationship has oneof and entity was not found, don't proceed - * with creating an entity as this can cause asserts later on */ - char *relstr = ecs_get_fullpath(world, rel); - ecs_parser_error(state->name, 0, 0, - "invalid identifier '%s' for relationship '%s'", path, relstr); - ecs_os_free(relstr); - return 0; - } - - ecs_entity_t prev_scope = 0; - ecs_entity_t prev_with = 0; - if (!is_subject) { - /* Don't apply scope/with for non-subject entities */ - prev_scope = ecs_set_scope(world, 0); - prev_with = ecs_set_with(world, 0); - } - - e = ecs_add_path(world, e, 0, path); - ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); - - if (prev_scope) { - ecs_set_scope(world, prev_scope); - } - if (prev_with) { - ecs_set_with(world, prev_with); - } - } else { - /* If entity exists, make sure it gets the right scope and with */ - if (is_subject) { - ecs_entity_t scope = ecs_get_scope(world); - if (scope) { - ecs_add_pair(world, e, EcsChildOf, scope); - } - - ecs_entity_t with = ecs_get_with(world); - if (with) { - ecs_add_id(world, e, with); - } - } - } - - if (is_new) { - if (state->assembly && !state->assembly_instance) { - ecs_add_id(world, e, EcsPrefab); - } - - if (state->global_with) { - ecs_add_id(world, e, state->global_with); - } - } - - return e; -} - -static -ecs_entity_t plecs_ensure_term_id( - ecs_world_t *world, - plecs_state_t *state, - ecs_term_id_t *term_id, - const char *expr, - int64_t column, - ecs_entity_t pred, - bool is_subject) -{ - ecs_entity_t result = 0; - const char *name = term_id->name; - if (term_id->flags & EcsIsVariable) { - if (name != NULL) { - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, name); - if (!var) { - ecs_parser_error(name, expr, column, - "unresolved variable '%s'", name); - return 0; - } - if (var->value.type != ecs_id(ecs_entity_t)) { - ecs_parser_error(name, expr, column, - "variable '%s' is not an entity", name); - return 0; - } - result = *(ecs_entity_t*)var->value.ptr; - if (!result) { - ecs_parser_error(name, expr, column, - "variable '%s' is not initialized with valid entity", name); - return 0; - } - } else if (term_id->id) { - result = term_id->id; - } else { - ecs_parser_error(name, expr, column, "invalid variable in term"); - return 0; - } - } else { - result = plecs_ensure_entity(world, state, name, pred, is_subject); - } - return result; -} - -static -bool plecs_pred_is_subj( - ecs_term_t *term, - plecs_state_t *state) -{ - if (term->src.name != NULL) { - return false; - } - if (term->second.name != NULL) { - return false; - } - if (ecs_term_match_0(term)) { - return false; - } - if (state->with_stmt) { - return false; - } - if (state->assign_stmt) { - return false; - } - if (state->isa_stmt) { - return false; - } - if (state->decl_type) { - return false; - } - - return true; -} - -/* Set masks aren't useful in plecs, so translate them back to entity names */ -static -const char* plecs_set_mask_to_name( - ecs_flags32_t flags) -{ - flags &= EcsTraverseFlags; - if (flags == EcsSelf) { - return "self"; - } else if (flags == EcsUp) { - return "up"; - } else if (flags == EcsDown) { - return "down"; - } else if (flags == EcsCascade || flags == (EcsUp|EcsCascade)) { - return "cascade"; - } else if (flags == EcsParent) { - return "parent"; - } - return NULL; -} - -static -char* plecs_trim_annot( - char *annot) -{ - annot = ECS_CONST_CAST(char*, ecs_parse_ws(annot)); - int32_t len = ecs_os_strlen(annot) - 1; - while (isspace(annot[len]) && (len > 0)) { - annot[len] = '\0'; - len --; - } - return annot; -} - -static -void plecs_apply_annotations( - ecs_world_t *world, - ecs_entity_t subj, - plecs_state_t *state) -{ - (void)world; - (void)subj; - (void)state; -#ifdef FLECS_DOC - int32_t i = 0, count = state->annot_count; - for (i = 0; i < count; i ++) { - char *annot = state->annot[i]; - if (!ecs_os_strncmp(annot, "@brief ", 7)) { - annot = plecs_trim_annot(annot + 7); - ecs_doc_set_brief(world, subj, annot); - } else if (!ecs_os_strncmp(annot, "@link ", 6)) { - annot = plecs_trim_annot(annot + 6); - ecs_doc_set_link(world, subj, annot); - } else if (!ecs_os_strncmp(annot, "@name ", 6)) { - annot = plecs_trim_annot(annot + 6); - ecs_doc_set_name(world, subj, annot); - } else if (!ecs_os_strncmp(annot, "@color ", 7)) { - annot = plecs_trim_annot(annot + 7); - ecs_doc_set_color(world, subj, annot); - } - } -#else - ecs_warn("cannot apply annotations, doc addon is missing"); -#endif -} - -static -int plecs_create_term( - ecs_world_t *world, - ecs_term_t *term, - const char *name, - const char *expr, - int64_t column, - plecs_state_t *state) -{ - state->last_subject = 0; - state->last_predicate = 0; - state->last_object = 0; - state->last_assign_id = 0; - - const char *subj_name = term->src.name; - if (!subj_name) { - subj_name = plecs_set_mask_to_name(term->src.flags); - } - - if (!ecs_term_id_is_set(&term->first)) { - ecs_parser_error(name, expr, column, "missing term in expression"); - return -1; - } - - if (state->assign_stmt && !ecs_term_match_this(term)) { - ecs_parser_error(name, expr, column, - "invalid statement in assign statement"); - return -1; - } - - bool pred_as_subj = plecs_pred_is_subj(term, state); - ecs_entity_t subj = 0, obj = 0, pred = plecs_ensure_term_id( - world, state, &term->first, expr, column, 0, pred_as_subj); - if (!pred) { - return -1; - } - - subj = plecs_ensure_entity(world, state, subj_name, pred, true); - - if (ecs_term_id_is_set(&term->second)) { - obj = plecs_ensure_term_id(world, state, &term->second, expr, column, - pred, !state->assign_stmt && !state->with_stmt); - if (!obj) { - return -1; - } - } - - if (state->assign_stmt || state->isa_stmt) { - subj = state->assign_to; - } - - if (state->isa_stmt && obj) { - ecs_parser_error(name, expr, column, - "invalid object in inheritance statement"); - return -1; - } - - if (state->isa_stmt) { - pred = ecs_pair(EcsIsA, pred); - } - - if (subj == EcsVariable) { - subj = pred; - } - - if (subj) { - ecs_id_t id; - if (!obj) { - id = term->id_flags | pred; - } else { - id = term->id_flags | ecs_pair(pred, obj); - state->last_object = obj; - } - state->last_assign_id = id; - state->last_predicate = pred; - state->last_subject = subj; - ecs_add_id(world, subj, id); - - pred_as_subj = false; - } else { - if (!obj) { - /* If no subject or object were provided, use predicate as subj - * unless the expression explictly excluded the subject */ - if (pred_as_subj) { - state->last_subject = pred; - subj = pred; - } else { - state->last_predicate = pred; - pred_as_subj = false; - } - } else { - state->last_predicate = pred; - state->last_object = obj; - pred_as_subj = false; - } - } - - /* If this is a with clause (the list of entities between 'with' and scope - * open), add subject to the array of with frames */ - if (state->with_stmt) { - ecs_assert(pred != 0, ECS_INTERNAL_ERROR, NULL); - ecs_id_t id; - - if (obj) { - id = ecs_pair(pred, obj); - } else { - id = pred; - } - - state->with[state->with_frame ++] = id; - } else { - if (subj && !state->scope_assign_stmt) { - plecs_apply_with_frame(world, state, subj); - } - } - - /* If an id was provided by itself, add default scope type to it */ - ecs_entity_t default_scope_type = state->default_scope_type[state->sp]; - if (pred_as_subj && default_scope_type) { - ecs_add_id(world, subj, default_scope_type); - } - - /* If annotations preceded the statement, append */ - if (!state->decl_type && state->annot_count) { - if (!subj) { - ecs_parser_error(name, expr, column, - "missing subject for annotations"); - return -1; - } - - plecs_apply_annotations(world, subj, state); - } - - return 0; -} - -static -const char* plecs_parse_inherit_stmt( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - if (state->isa_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "cannot nest inheritance"); - return NULL; - } - - if (!state->last_subject) { - ecs_parser_error(name, expr, ptr - expr, - "missing entity to assign inheritance to"); - return NULL; - } - - state->isa_stmt = true; - state->assign_to = state->last_subject; - - return ptr; -} - -static -const char* plecs_parse_assign_var_expr( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state, - ecs_expr_var_t *var) -{ - ecs_value_t value = {0}; - - if (state->last_assign_id) { - value.type = state->last_assign_id; - value.ptr = ecs_value_new(world, state->last_assign_id); - if (!var && state->assembly_instance) { - var = ecs_vars_lookup(&state->vars, state->var_name); - } - } - - ptr = ecs_parse_expr(world, ptr, &value, - &(ecs_parse_expr_desc_t){ - .name = name, - .expr = expr, - .lookup_action = plecs_lookup_action, - .lookup_ctx = state, - .vars = &state->vars - }); - if (!ptr) { - if (state->last_assign_id) { - ecs_value_free(world, value.type, value.ptr); - } - goto error; - } - - if (var) { - bool ignore = state->var_is_prop && state->assembly_instance; - if (!ignore) { - if (var->value.ptr) { - ecs_value_free(world, var->value.type, var->value.ptr); - var->value.ptr = value.ptr; - var->value.type = value.type; - } - } else { - ecs_value_free(world, value.type, value.ptr); - } - } else { - var = ecs_vars_declare_w_value( - &state->vars, state->var_name, &value); - if (!var) { - goto error; - } - } - - state->var_is_prop = false; - return ptr; -error: - return NULL; -} - -static -const char* plecs_parse_assign_expr( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state, - ecs_expr_var_t *var) -{ - (void)world; - - if (state->var_stmt) { - return plecs_parse_assign_var_expr(world, name, expr, ptr, state, var); - } - - if (!state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "unexpected value outside of assignment statement"); - return NULL; - } - - ecs_id_t assign_id = state->last_assign_id; - if (!assign_id) { - ecs_parser_error(name, expr, ptr - expr, - "missing type for assignment statement"); - return NULL; - } - - ecs_entity_t assign_to = state->assign_to; - if (!assign_to) { - assign_to = state->last_subject; - } - - if (!assign_to) { - ecs_parser_error(name, expr, ptr - expr, - "missing entity to assign to"); - return NULL; - } - - ecs_entity_t type = ecs_get_typeid(world, assign_id); - if (!type) { - char *id_str = ecs_id_str(world, assign_id); - ecs_parser_error(name, expr, ptr - expr, - "invalid assignment, '%s' is not a type", id_str); - ecs_os_free(id_str); - return NULL; - } - - if (assign_to == EcsVariable) { - assign_to = type; - } - - void *value_ptr = ecs_get_mut_id(world, assign_to, assign_id); - - ptr = ecs_parse_expr(world, ptr, &(ecs_value_t){type, value_ptr}, - &(ecs_parse_expr_desc_t){ - .name = name, - .expr = expr, - .lookup_action = plecs_lookup_action, - .lookup_ctx = state, - .vars = &state->vars - }); - if (!ptr) { - return NULL; - } - - ecs_modified_id(world, assign_to, assign_id); - - return ptr; -} - -static -const char* plecs_parse_assign_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - (void)world; - - state->isa_stmt = false; - - /* Component scope (add components to entity) */ - if (!state->assign_to) { - if (!state->last_subject) { - ecs_parser_error(name, expr, ptr - expr, - "missing entity to assign to"); - return NULL; - } - state->assign_to = state->last_subject; - } - - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid assign statement in assign statement"); - return NULL; - } - - state->assign_stmt = true; - - /* Assignment without a preceding component */ - if (ptr[0] == '{') { - ecs_entity_t type = 0; - - /* If we're in a scope & last_subject is a type, assign to scope */ - if (ecs_get_scope(world) != 0) { - type = ecs_get_typeid(world, state->last_subject); - if (type != 0) { - type = state->last_subject; - } - } - - /* If type hasn't been set yet, check if scope has default type */ - if (!type && !state->scope_assign_stmt) { - type = state->default_scope_type[state->sp]; - } - - /* If no type has been found still, check if last with id is a type */ - if (!type && !state->scope_assign_stmt) { - int32_t with_frame_count = state->with_frames[state->sp]; - if (with_frame_count) { - type = state->with[with_frame_count - 1]; - } - } - - if (!type) { - ecs_parser_error(name, expr, ptr - expr, - "missing type for assignment"); - return NULL; - } - - state->last_assign_id = type; - } - - return ptr; -} - -static -const char* plecs_parse_assign_with_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - int32_t with_frame = state->with_frame - 1; - if (with_frame < 0) { - ecs_parser_error(name, expr, ptr - expr, - "missing type in with value"); - return NULL; - } - - ecs_id_t id = state->with[with_frame]; - ecs_id_record_t *idr = flecs_id_record_get(world, id); - const ecs_type_info_t *ti = idr ? idr->type_info : NULL; - if (!ti) { - char *typename = ecs_id_str(world, id); - ecs_parser_error(name, expr, ptr - expr, - "id '%s' in with value is not a type", typename); - ecs_os_free(typename); - return NULL; - } - - plecs_with_value_t *v = &state->with_value_frames[with_frame]; - v->value.type = ti->component; - v->value.ptr = ecs_value_new(world, ti->component); - v->owned = true; - if (!v->value.ptr) { - char *typename = ecs_id_str(world, id); - ecs_parser_error(name, expr, ptr - expr, - "failed to create value for '%s'", typename); - ecs_os_free(typename); - return NULL; - } - - ptr = ecs_parse_expr(world, ptr, &v->value, - &(ecs_parse_expr_desc_t){ - .name = name, - .expr = expr, - .lookup_action = plecs_lookup_action, - .lookup_ctx = state, - .vars = &state->vars - }); - if (!ptr) { - return NULL; - } - - return ptr; -} - -static -const char* plecs_parse_assign_with_var( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - ecs_assert(ptr[0] == '$', ECS_INTERNAL_ERROR, NULL); - ecs_assert(state->with_stmt, ECS_INTERNAL_ERROR, NULL); - - char var_name[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr; - ptr = ecs_parse_token(name, expr, ptr + 1, var_name, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "unresolved variable '%s'", var_name); - return NULL; - } - - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, var_name); - if (!var) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved variable '%s'", var_name); - return NULL; - } - - int32_t with_frame = state->with_frame; - state->with[with_frame] = var->value.type; - state->with_value_frames[with_frame].value = var->value; - state->with_value_frames[with_frame].owned = false; - state->with_frame ++; - - return ptr; -} - -static -const char* plecs_parse_var_as_component( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - ecs_assert(ptr[0] == '$', ECS_INTERNAL_ERROR, NULL); - ecs_assert(!state->var_stmt, ECS_INTERNAL_ERROR, NULL); - char var_name[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr; - ptr = ecs_parse_token(name, expr, ptr + 1, var_name, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "unresolved variable '%s'", var_name); - return NULL; - } - - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, var_name); - if (!var) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved variable '%s'", var_name); - return NULL; - } - - if (!state->assign_to) { - ecs_parser_error(name, expr, ptr - expr, - "missing lvalue for variable assignment '%s'", var_name); - return NULL; - } - - /* Use type of variable as component */ - ecs_entity_t type = var->value.type; - ecs_entity_t assign_to = state->assign_to; - if (!assign_to) { - assign_to = state->last_subject; - } - - void *dst = ecs_get_mut_id(world, assign_to, type); - if (!dst) { - char *type_name = ecs_get_fullpath(world, type); - ecs_parser_error(name, expr, ptr - expr, - "failed to obtain component for type '%s' of variable '%s'", - type_name, var_name); - ecs_os_free(type_name); - return NULL; - } - - if (ecs_value_copy(world, type, dst, var->value.ptr)) { - char *type_name = ecs_get_fullpath(world, type); - ecs_parser_error(name, expr, ptr - expr, - "failed to copy value for variable '%s' of type '%s'", - var_name, type_name); - ecs_os_free(type_name); - return NULL; - } - - ecs_modified_id(world, assign_to, type); - - return ptr; -} - -static -void plecs_push_using( - ecs_entity_t scope, - plecs_state_t *state) -{ - for (int i = 0; i < state->using_frame; i ++) { - if (state->using[i] == scope) { - return; - } - } - - state->using[state->using_frame ++] = scope; -} - -static -const char* plecs_parse_using_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - if (state->isa_stmt || state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid usage of using keyword"); - return NULL; - } - - char using_path[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr + 1; - ptr = ecs_parse_token(name, expr, ptr + 5, using_path, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "expected identifier for using statement"); - return NULL; - } - - ecs_size_t len = ecs_os_strlen(using_path); - if (!len) { - ecs_parser_error(name, expr, tmp - expr, - "missing identifier for using statement"); - return NULL; - } - - /* Lookahead as * is not matched by parse_token */ - if (ptr[0] == '*') { - using_path[len] = '*'; - using_path[len + 1] = '\0'; - len ++; - ptr ++; - } - - ecs_entity_t scope; - if (len > 2 && !ecs_os_strcmp(&using_path[len - 2], ".*")) { - using_path[len - 2] = '\0'; - scope = ecs_lookup_fullpath(world, using_path); - if (!scope) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved identifier '%s' in using statement", using_path); - return NULL; - } - - /* Add each child of the scope to using stack */ - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ - .id = ecs_childof(scope) }); - while (ecs_term_next(&it)) { - int32_t i, count = it.count; - for (i = 0; i < count; i ++) { - plecs_push_using(it.entities[i], state); - } - } - } else { - scope = plecs_ensure_entity(world, state, using_path, 0, false); - if (!scope) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved identifier '%s' in using statement", using_path); - return NULL; - } - - plecs_push_using(scope, state); - } - - state->using_frames[state->sp] = state->using_frame; - return ptr; -} - -static -const char* plecs_parse_module_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - const char *expr_start = ecs_parse_ws_eol(expr); - if (expr_start != ptr) { - ecs_parser_error(name, expr, ptr - expr, - "module must be first statement of script"); - return NULL; - } - - char module_path[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr + 1; - ptr = ecs_parse_token(name, expr, ptr + 6, module_path, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "expected identifier for module statement"); - return NULL; - } - - ecs_component_desc_t desc = {0}; - desc.entity = ecs_entity(world, { .name = module_path }); - ecs_entity_t module = ecs_module_init(world, NULL, &desc); - if (!module) { - return NULL; - } - - state->is_module = true; - state->sp ++; - state->scope[state->sp] = module; - ecs_set_scope(world, module); - return ptr; -} - -static -const char* plecs_parse_with_stmt( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - if (state->isa_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid with after inheritance"); - return NULL; - } - - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid with in assign_stmt"); - return NULL; - } - - /* Add following expressions to with list */ - state->with_stmt = true; - return ptr + 5; -} - -static -const char* plecs_parse_assembly_stmt( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - if (state->isa_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid with after inheritance"); - return NULL; - } - - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid with in assign_stmt"); - return NULL; - } - - state->assembly_stmt = true; - - return ptr + 9; -} - -static -const char* plecs_parse_var_type( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state, - ecs_entity_t *type_out) -{ - char prop_type_name[ECS_MAX_TOKEN_SIZE]; - const char *tmp = ptr + 1; - ptr = ecs_parse_token(name, expr, ptr + 1, prop_type_name, 0); - if (!ptr) { - ecs_parser_error(name, expr, tmp - expr, - "expected type for prop declaration"); - return NULL; - } - - ecs_entity_t prop_type = plecs_lookup(world, prop_type_name, state, 0, false); - if (!prop_type) { - ecs_parser_error(name, expr, ptr - expr, - "unresolved property type '%s'", prop_type_name); - return NULL; - } - - *type_out = prop_type; - - return ptr; -} - -static -const char* plecs_parse_const_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - ptr = ecs_parse_token(name, expr, ptr + 5, state->var_name, 0); - if (!ptr) { - return NULL; - } - - ptr = ecs_parse_ws(ptr); - - if (ptr[0] == ':') { - ptr = plecs_parse_var_type( - world, name, expr, ptr, state, &state->last_assign_id); - if (!ptr) { - return NULL; - } - - ptr = ecs_parse_ws(ptr); - } - - if (ptr[0] != '=') { - ecs_parser_error(name, expr, ptr - expr, - "expected '=' after const declaration"); - return NULL; - } - - state->var_stmt = true; - return ptr + 1; -} - -static -const char* plecs_parse_prop_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - char prop_name[ECS_MAX_TOKEN_SIZE]; - ptr = ecs_parse_token(name, expr, ptr + 5, prop_name, 0); - if (!ptr) { - return NULL; - } - - ptr = ecs_parse_ws(ptr); - - if (ptr[0] != ':') { - ecs_parser_error(name, expr, ptr - expr, - "expected ':' after prop declaration"); - return NULL; - } - - ecs_entity_t prop_type; - ptr = plecs_parse_var_type(world, name, expr, ptr, state, &prop_type); - if (!ptr) { - return NULL; - } - - ecs_entity_t assembly = state->assembly; - if (!assembly) { - ecs_parser_error(name, expr, ptr - expr, - "unexpected prop '%s' outside of assembly", prop_name); - return NULL; - } - - if (!state->assembly_instance) { - ecs_entity_t prop_member = ecs_entity(world, { - .name = prop_name, - .add = { ecs_childof(assembly) } - }); - - if (!prop_member) { - return NULL; - } - - ecs_set(world, prop_member, EcsMember, { - .type = prop_type - }); - } - - if (ptr[0] != '=') { - ecs_parser_error(name, expr, ptr - expr, - "expected '=' after prop type"); - return NULL; - } - - ecs_os_strcpy(state->var_name, prop_name); - state->last_assign_id = prop_type; - state->var_stmt = true; - state->var_is_prop = true; - - return plecs_parse_fluff(ptr + 1); -} - -static -const char* plecs_parse_scope_open( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - state->isa_stmt = false; - - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid scope in assign_stmt"); - return NULL; - } - - state->sp ++; - - ecs_entity_t scope = 0; - ecs_entity_t default_scope_type = 0; - bool assembly_stmt = false; - - if (!state->with_stmt) { - if (state->last_subject) { - scope = state->last_subject; - ecs_set_scope(world, state->last_subject); - - /* Check if scope has a default child component */ - ecs_entity_t def_type_src = ecs_get_target_for_id(world, scope, - 0, ecs_pair(EcsDefaultChildComponent, EcsWildcard)); - - if (def_type_src) { - default_scope_type = ecs_get_target( - world, def_type_src, EcsDefaultChildComponent, 0); - } - } else { - if (state->last_object) { - scope = ecs_pair( - state->last_predicate, state->last_object); - ecs_set_with(world, scope); - } else { - if (state->last_predicate) { - scope = ecs_pair(EcsChildOf, state->last_predicate); - } - ecs_set_scope(world, state->last_predicate); - } - } - - state->scope[state->sp] = scope; - state->default_scope_type[state->sp] = default_scope_type; - - if (state->assembly_stmt) { - assembly_stmt = true; - if (state->assembly) { - ecs_parser_error(name, expr, ptr - expr, - "invalid nested assembly"); - return NULL; - } - state->assembly = scope; - state->assembly_stmt = false; - state->assembly_start = ptr; - } - } else { - state->scope[state->sp] = state->scope[state->sp - 1]; - state->default_scope_type[state->sp] = - state->default_scope_type[state->sp - 1]; - state->assign_to = 0; - } - - state->using_frames[state->sp] = state->using_frame; - state->with_frames[state->sp] = state->with_frame; - state->with_stmt = false; - - ecs_vars_push(&state->vars); - - /* Declare variable to hold assembly instance during instantiation */ - if (assembly_stmt) { - ecs_value_t val = {0}; - ecs_expr_var_t *var = ecs_vars_declare_w_value( - &state->vars, "this", &val); - var->value.ptr = ECS_CONST_CAST(void*, &EcsThis); /* Dummy value */ - var->value.type = ecs_id(ecs_entity_t); - var->owned = false; - } - - return ptr; -} - -static -void plecs_free_with_frame( - ecs_world_t *world, - plecs_state_t *state) -{ - int32_t i, prev_with = state->with_frames[state->sp]; - for (i = prev_with; i < state->with_frame; i ++) { - plecs_with_value_t *v = &state->with_value_frames[i]; - if (!v->owned) { - continue; - } - if (v->value.type) { - ecs_value_free(world, v->value.type, v->value.ptr); - v->value.type = 0; - v->value.ptr = NULL; - v->owned = false; - } - } -} - -static -void plecs_free_all_with_frames( - ecs_world_t *world, - plecs_state_t *state) -{ - int32_t i; - for (i = state->sp - 1; i >= 0; i --) { - state->sp = i; - plecs_free_with_frame(world, state); - } -} - -static -const char* plecs_parse_scope_close( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - if (state->isa_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "invalid '}' after inheritance statement"); - return NULL; - } - - if (state->assign_stmt) { - ecs_parser_error(name, expr, ptr - expr, - "unfinished assignment before }"); - return NULL; - } - - ecs_entity_t cur = state->scope[state->sp], assembly = state->assembly; - if (state->sp && (cur == state->scope[state->sp - 1])) { - /* Previous scope is also from the assembly, not found the end yet */ - cur = 0; - } - if (cur && cur == assembly) { - ecs_size_t assembly_len = flecs_ito(ecs_size_t, ptr - state->assembly_start); - if (assembly_len) { - assembly_len --; - char *script = ecs_os_malloc_n(char, assembly_len + 1); - ecs_os_memcpy(script, state->assembly_start, assembly_len); - script[assembly_len] = '\0'; - state->assembly = 0; - state->assembly_start = NULL; - if (flecs_assembly_create(world, name, expr, ptr, assembly, script, state)) { - return NULL; - } - } else { - ecs_parser_error(name, expr, ptr - expr, "empty assembly"); - return NULL; - } - } - - state->scope[state->sp] = 0; - state->default_scope_type[state->sp] = 0; - state->sp --; - - if (state->sp < 0) { - ecs_parser_error(name, expr, ptr - expr, "invalid } without a {"); - return NULL; - } - - ecs_id_t id = state->scope[state->sp]; - if (!id || ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_set_with(world, id); - } - - if (!id || !ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_set_scope(world, id); - } - - plecs_free_with_frame(world, state); - - state->with_frame = state->with_frames[state->sp]; - state->using_frame = state->using_frames[state->sp]; - state->last_subject = 0; - state->assign_stmt = false; - - ecs_vars_pop(&state->vars); - - return plecs_parse_fluff(ptr + 1); -} - -static -const char *plecs_parse_plecs_term( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - ecs_term_t term = {0}; - ecs_entity_t decl_id = 0; - if (state->decl_stmt) { - decl_id = state->last_predicate; - } - - ptr = ecs_parse_term(world, name, expr, ptr, &term, NULL); - if (!ptr) { - return NULL; - } - - if (flecs_isident(ptr[0])) { - state->decl_type = true; - } - - if (!ecs_term_is_initialized(&term)) { - ecs_parser_error(name, expr, ptr - expr, "expected identifier"); - return NULL; /* No term found */ - } - - if (plecs_create_term(world, &term, name, expr, (ptr - expr), state)) { - ecs_term_fini(&term); - return NULL; /* Failed to create term */ - } - - if (decl_id && state->last_subject) { - ecs_add_id(world, state->last_subject, decl_id); - } - - state->decl_type = false; - - ecs_term_fini(&term); - - return ptr; -} - -static -const char* plecs_parse_annotation( - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - do { - if(state->annot_count >= STACK_MAX_SIZE) { - ecs_parser_error(name, expr, ptr - expr, - "max number of annotations reached"); - return NULL; - } - - char ch; - const char *start = ptr; - for (; (ch = *ptr) && ch != '\n'; ptr ++) { } - - int32_t len = (int32_t)(ptr - start); - char *annot = ecs_os_malloc_n(char, len + 1); - ecs_os_memcpy_n(annot, start, char, len); - annot[len] = '\0'; - - state->annot[state->annot_count] = annot; - state->annot_count ++; - - ptr = plecs_parse_fluff(ptr); - } while (ptr[0] == '@'); - - return ptr; -} - -static -void plecs_clear_annotations( - plecs_state_t *state) -{ - int32_t i, count = state->annot_count; - for (i = 0; i < count; i ++) { - ecs_os_free(state->annot[i]); - } - state->annot_count = 0; -} - -static -const char* plecs_parse_stmt( - ecs_world_t *world, - const char *name, - const char *expr, - const char *ptr, - plecs_state_t *state) -{ - state->assign_stmt = false; - state->scope_assign_stmt = false; - state->isa_stmt = false; - state->with_stmt = false; - state->decl_stmt = false; - state->var_stmt = false; - state->last_subject = 0; - state->last_predicate = 0; - state->last_object = 0; - state->assign_to = 0; - state->last_assign_id = 0; - - plecs_clear_annotations(state); - - ptr = plecs_parse_fluff(ptr); - - char ch = ptr[0]; - - if (!ch) { - goto done; - } else if (ch == '{') { - ptr = plecs_parse_fluff(ptr + 1); - goto scope_open; - } else if (ch == '}') { - goto scope_close; - } else if (ch == '-') { - ptr = plecs_parse_fluff(ptr + 1); - state->assign_to = ecs_get_scope(world); - state->scope_assign_stmt = true; - goto assign_stmt; - } else if (ch == '@') { - ptr = plecs_parse_annotation(name, expr, ptr, state); - if (!ptr) goto error; - goto term_expr; - } else if (!ecs_os_strncmp(ptr, TOK_USING " ", 5)) { - ptr = plecs_parse_using_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; - goto done; - } else if (!ecs_os_strncmp(ptr, TOK_MODULE " ", 6)) { - ptr = plecs_parse_module_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; - goto done; - } else if (!ecs_os_strncmp(ptr, TOK_WITH " ", 5)) { - ptr = plecs_parse_with_stmt(name, expr, ptr, state); - if (!ptr) goto error; - goto term_expr; - } else if (!ecs_os_strncmp(ptr, TOK_CONST " ", 6)) { - ptr = plecs_parse_const_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; - goto assign_expr; - } else if (!ecs_os_strncmp(ptr, TOK_ASSEMBLY " ", 9)) { - ptr = plecs_parse_assembly_stmt(name, expr, ptr, state); - if (!ptr) goto error; - goto decl_stmt; - } else if (!ecs_os_strncmp(ptr, TOK_PROP " ", 5)) { - ptr = plecs_parse_prop_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; - goto assign_expr; - } else { - goto term_expr; - } - -term_expr: - if (!ptr[0]) { - goto done; - } - - if (ptr[0] == '$' && !isspace(ptr[1])) { - if (state->with_stmt) { - ptr = plecs_parse_assign_with_var(name, expr, ptr, state); - if (!ptr) { - return NULL; - } - } else if (!state->var_stmt) { - goto assign_var_as_component; - } - } else if (!(ptr = plecs_parse_plecs_term(world, name, ptr, ptr, state))) { - goto error; - } - - - const char *tptr = ecs_parse_ws(ptr); - if (flecs_isident(tptr[0])) { - if (state->decl_stmt) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected ' ' in declaration statement"); - goto error; - } - ptr = tptr; - goto decl_stmt; - } - -next_term: - ptr = plecs_parse_fluff(ptr); - - if (ptr[0] == ':' && ptr[1] == '-') { - ptr = plecs_parse_fluff(ptr + 2); - goto assign_stmt; - } else if (ptr[0] == ':') { - ptr = plecs_parse_fluff(ptr + 1); - goto inherit_stmt; - } else if (ptr[0] == ',') { - ptr = plecs_parse_fluff(ptr + 1); - goto term_expr; - } else if (ptr[0] == '{' || ptr[0] == '[') { - if (state->assign_stmt) { - goto assign_expr; - } else if (state->with_stmt && !isspace(ptr[-1])) { - /* If this is a { in a with statement which directly follows a - * non-whitespace character, the with id has a value */ - ptr = plecs_parse_assign_with_stmt(world, name, expr, ptr, state); - if (!ptr) { - goto error; - } - - goto next_term; - } else { - ptr = plecs_parse_fluff(ptr + 1); - goto scope_open; - } - } - - state->assign_stmt = false; - goto done; - -decl_stmt: - state->decl_stmt = true; - goto term_expr; - -inherit_stmt: - ptr = plecs_parse_inherit_stmt(name, expr, ptr, state); - if (!ptr) goto error; - - /* Expect base identifier */ - goto term_expr; - -assign_stmt: - ptr = plecs_parse_assign_stmt(world, name, expr, ptr, state); - if (!ptr) goto error; - - ptr = plecs_parse_fluff(ptr); - - /* Assignment without a preceding component */ - if (ptr[0] == '{' || ptr[0] == '[') { - goto assign_expr; - } - - /* Expect component identifiers */ - goto term_expr; - -assign_expr: - ptr = plecs_parse_assign_expr(world, name, expr, ptr, state, NULL); - if (!ptr) goto error; - - ptr = plecs_parse_fluff(ptr); - if (ptr[0] == ',') { - ptr ++; - goto term_expr; - } else if (ptr[0] == '{') { - if (state->var_stmt) { - ecs_expr_var_t *var = ecs_vars_lookup(&state->vars, state->var_name); - if (var && var->value.type == ecs_id(ecs_entity_t)) { - ecs_assert(var->value.ptr != NULL, ECS_INTERNAL_ERROR, NULL); - /* The code contained an entity{...} variable assignment, use - * the assigned entity id as type for parsing the expression */ - state->last_assign_id = *(ecs_entity_t*)var->value.ptr; - ptr = plecs_parse_assign_expr(world, name, expr, ptr, state, var); - goto done; - } - } - ecs_parser_error(name, expr, (ptr - expr), - "unexpected '{' after assignment"); - goto error; - } - - state->assign_stmt = false; - state->assign_to = 0; - goto done; - -assign_var_as_component: { - ptr = plecs_parse_var_as_component(world, name, expr, ptr, state); - if (!ptr) { - goto error; - } - state->assign_stmt = false; - state->assign_to = 0; - goto done; -} - -scope_open: - ptr = plecs_parse_scope_open(world, name, expr, ptr, state); - if (!ptr) goto error; - goto done; - -scope_close: - ptr = plecs_parse_scope_close(world, name, expr, ptr, state); - if (!ptr) goto error; - goto done; - -done: - return ptr; -error: - return NULL; -} - -static -int flecs_plecs_parse( - ecs_world_t *world, - const char *name, - const char *expr, - ecs_vars_t *vars, - ecs_entity_t script, - ecs_entity_t instance) -{ - const char *ptr = expr; - ecs_term_t term = {0}; - plecs_state_t state = {0}; - - if (!expr) { - return 0; - } - - state.scope[0] = 0; - ecs_entity_t prev_scope = ecs_set_scope(world, 0); - ecs_entity_t prev_with = ecs_set_with(world, 0); - - if (ECS_IS_PAIR(prev_with) && ECS_PAIR_FIRST(prev_with) == EcsChildOf) { - ecs_set_scope(world, ECS_PAIR_SECOND(prev_with)); - state.scope[0] = ecs_pair_second(world, prev_with); - } else { - state.global_with = prev_with; - } - - ecs_vars_init(world, &state.vars); - - if (script) { - const EcsScript *s = ecs_get(world, script, EcsScript); - if (!s) { - ecs_err("%s: provided script entity is not a script", name); - goto error; - } - if (s && ecs_has(world, script, EcsStruct)) { - state.assembly = script; - state.assembly_instance = true; - - if (s->using_.count) { - ecs_os_memcpy_n(state.using, s->using_.array, - ecs_entity_t, s->using_.count); - state.using_frame = s->using_.count; - state.using_frames[0] = s->using_.count; - } - - if (instance) { - ecs_set_scope(world, instance); - } - } - } - - if (vars) { - state.vars.root.parent = vars->cur; - } - - do { - expr = ptr = plecs_parse_stmt(world, name, expr, ptr, &state); - if (!ptr) { - goto error; - } - - if (!ptr[0]) { - break; /* End of expression */ - } - } while (true); - - ecs_set_scope(world, prev_scope); - ecs_set_with(world, prev_with); - plecs_clear_annotations(&state); - - if (state.is_module) { - state.sp --; - } - - if (state.sp != 0) { - ecs_parser_error(name, expr, 0, "missing end of scope"); - goto error; - } - - if (state.assign_stmt) { - ecs_parser_error(name, expr, 0, "unfinished assignment"); - goto error; - } - - if (state.errors) { - goto error; - } - - ecs_vars_fini(&state.vars); - - return 0; -error: - plecs_free_all_with_frames(world, &state); - ecs_vars_fini(&state.vars); - ecs_set_scope(world, state.scope[0]); - ecs_set_with(world, prev_with); - ecs_term_fini(&term); - return -1; -} - -int ecs_plecs_from_str( - ecs_world_t *world, - const char *name, - const char *expr) -{ - return flecs_plecs_parse(world, name, expr, NULL, 0, 0); -} - -static -char* flecs_load_from_file( - const char *filename) -{ - FILE* file; - char* content = NULL; - int32_t bytes; - size_t size; - - /* Open file for reading */ - ecs_os_fopen(&file, filename, "r"); - if (!file) { - ecs_err("%s (%s)", ecs_os_strerror(errno), filename); - goto error; - } - - /* Determine file size */ - fseek(file, 0 , SEEK_END); - bytes = (int32_t)ftell(file); - if (bytes == -1) { - goto error; - } - rewind(file); - - /* Load contents in memory */ - content = ecs_os_malloc(bytes + 1); - size = (size_t)bytes; - if (!(size = fread(content, 1, size, file)) && bytes) { - ecs_err("%s: read zero bytes instead of %d", filename, size); - ecs_os_free(content); - content = NULL; - goto error; - } else { - content[size] = '\0'; - } - - fclose(file); - - return content; -error: - ecs_os_free(content); - return NULL; -} - -int ecs_plecs_from_file( - ecs_world_t *world, - const char *filename) -{ - char *script = flecs_load_from_file(filename); - if (!script) { - return -1; - } - - int result = ecs_plecs_from_str(world, filename, script); - ecs_os_free(script); - return result; -} - -static -ecs_id_t flecs_script_tag( - ecs_entity_t script, - ecs_entity_t instance) -{ - if (!instance) { - return ecs_pair_t(EcsScript, script); - } else { - return ecs_pair(EcsChildOf, instance); - } -} - -void ecs_script_clear( - ecs_world_t *world, - ecs_entity_t script, - ecs_entity_t instance) -{ - ecs_delete_with(world, flecs_script_tag(script, instance)); -} - -int ecs_script_update( - ecs_world_t *world, - ecs_entity_t e, - ecs_entity_t instance, - const char *script, - ecs_vars_t *vars) -{ - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(script != NULL, ECS_INTERNAL_ERROR, NULL); - - int result = 0; - bool is_defer = ecs_is_deferred(world); - ecs_suspend_readonly_state_t srs; - ecs_world_t *real_world = NULL; - if (is_defer) { - ecs_assert(ecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL); - real_world = flecs_suspend_readonly(world, &srs); - ecs_assert(real_world != NULL, ECS_INTERNAL_ERROR, NULL); - } - - ecs_script_clear(world, e, instance); - - EcsScript *s = ecs_get_mut(world, e, EcsScript); - if (!s->script || ecs_os_strcmp(s->script, script)) { - s->script = ecs_os_strdup(script); - ecs_modified(world, e, EcsScript); - } - - ecs_entity_t prev = ecs_set_with(world, flecs_script_tag(e, instance)); - if (flecs_plecs_parse(world, ecs_get_name(world, e), script, vars, e, instance)) { - ecs_delete_with(world, ecs_pair_t(EcsScript, e)); - result = -1; - } - ecs_set_with(world, prev); - - if (is_defer) { - flecs_resume_readonly(real_world, &srs); - } - - return result; -} - -ecs_entity_t ecs_script_init( - ecs_world_t *world, - const ecs_script_desc_t *desc) -{ - const char *script = NULL; - ecs_entity_t e = desc->entity; - - ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_check(desc != NULL, ECS_INTERNAL_ERROR, NULL); - - if (!e) { - if (desc->filename) { - e = ecs_new_from_path_w_sep(world, 0, desc->filename, "/", NULL); - } else { - e = ecs_new_id(world); - } - } - - script = desc->str; - if (!script && desc->filename) { - script = flecs_load_from_file(desc->filename); - if (!script) { - goto error; - } - } - - if (ecs_script_update(world, e, 0, script, NULL)) { - goto error; - } - - if (script != desc->str) { - /* Safe cast, only happens when script is loaded from file */ - ecs_os_free(ECS_CONST_CAST(char*, script)); - } - - return e; -error: - if (script != desc->str) { - /* Safe cast, only happens when script is loaded from file */ - ecs_os_free(ECS_CONST_CAST(char*, script)); - } - if (!desc->entity) { - ecs_delete(world, e); - } - return 0; -} - -void FlecsScriptImport( - ecs_world_t *world) -{ - ECS_MODULE(world, FlecsScript); - ECS_IMPORT(world, FlecsMeta); - - ecs_set_name_prefix(world, "Ecs"); - ECS_COMPONENT_DEFINE(world, EcsScript); - - ecs_set_hooks(world, EcsScript, { - .ctor = ecs_default_ctor, - .move = ecs_move(EcsScript), - .dtor = ecs_dtor(EcsScript) - }); - - ecs_add_id(world, ecs_id(EcsScript), EcsTag); - ecs_add_id(world, ecs_id(EcsScript), EcsPrivate); - - ecs_struct(world, { - .entity = ecs_id(EcsScript), - .members = { - { .name = "using", .type = ecs_vector(world, { - .entity = ecs_entity(world, { .name = "UsingVector" }), - .type = ecs_id(ecs_entity_t) - }), - .count = 0 - }, - { .name = "script", .type = ecs_id(ecs_string_t), .count = 0 } - } - }); -} - -#endif diff --git a/vendors/flecs/src/addons/rest.c b/vendors/flecs/src/addons/rest.c index e47648053..42c7183b2 100644 --- a/vendors/flecs/src/addons/rest.c +++ b/vendors/flecs/src/addons/rest.c @@ -5,16 +5,34 @@ #include "../private_api.h" +#include "script/script.h" +#include "pipeline/pipeline.h" + #ifdef FLECS_REST +/* Retain captured commands for one minute at 60 FPS */ +#define FLECS_REST_COMMAND_RETAIN_COUNT (60 * 60) + static ECS_TAG_DECLARE(EcsRestPlecs); typedef struct { ecs_world_t *world; ecs_http_server_t *srv; int32_t rc; + ecs_map_t cmd_captures; + double last_time; } ecs_rest_ctx_t; +typedef struct { + char *cmds; + ecs_time_t start_time; + ecs_strbuf_t buf; +} ecs_rest_cmd_sync_capture_t; + +typedef struct { + ecs_vec_t syncs; +} ecs_rest_cmd_capture_t; + static ECS_COPY(EcsRest, dst, src, { ecs_rest_ctx_t *impl = src->impl; if (impl) { @@ -37,8 +55,7 @@ static ECS_DTOR(EcsRest, ptr, { if (impl) { impl->rc --; if (!impl->rc) { - ecs_http_server_fini(impl->srv); - ecs_os_free(impl); + ecs_rest_server_fini(impl->srv); } } ecs_os_free(ptr->ipaddr); @@ -50,7 +67,7 @@ static ecs_os_api_log_t rest_prev_log; static void flecs_rest_capture_log( int32_t level, - const char *file, + const char *file, int32_t line, const char *msg) { @@ -148,16 +165,12 @@ void flecs_rest_parse_json_ser_entity_params( ecs_entity_to_json_desc_t *desc, const ecs_http_request_t *req) { - flecs_rest_bool_param(req, "path", &desc->serialize_path); - flecs_rest_bool_param(req, "label", &desc->serialize_label); - flecs_rest_bool_param(req, "brief", &desc->serialize_brief); - flecs_rest_bool_param(req, "link", &desc->serialize_link); - flecs_rest_bool_param(req, "color", &desc->serialize_color); - flecs_rest_bool_param(req, "ids", &desc->serialize_ids); - flecs_rest_bool_param(req, "id_labels", &desc->serialize_id_labels); - flecs_rest_bool_param(req, "base", &desc->serialize_base); + flecs_rest_bool_param(req, "entity_id", &desc->serialize_entity_id); + flecs_rest_bool_param(req, "doc", &desc->serialize_doc); + flecs_rest_bool_param(req, "full_paths", &desc->serialize_full_paths); + flecs_rest_bool_param(req, "inherited", &desc->serialize_inherited); flecs_rest_bool_param(req, "values", &desc->serialize_values); - flecs_rest_bool_param(req, "private", &desc->serialize_private); + flecs_rest_bool_param(req, "builtin", &desc->serialize_builtin); flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info); flecs_rest_bool_param(req, "matches", &desc->serialize_matches); flecs_rest_bool_param(req, "alerts", &desc->serialize_alerts); @@ -165,7 +178,7 @@ void flecs_rest_parse_json_ser_entity_params( char *rel = NULL; flecs_rest_string_param(req, "refs", &rel); if (rel) { - desc->serialize_refs = ecs_lookup_fullpath(world, rel); + desc->serialize_refs = ecs_lookup(world, rel); } } @@ -174,27 +187,27 @@ void flecs_rest_parse_json_ser_iter_params( ecs_iter_to_json_desc_t *desc, const ecs_http_request_t *req) { - flecs_rest_bool_param(req, "term_ids", &desc->serialize_term_ids); - flecs_rest_bool_param(req, "term_labels", &desc->serialize_term_labels); - flecs_rest_bool_param(req, "ids", &desc->serialize_ids); - flecs_rest_bool_param(req, "id_labels", &desc->serialize_id_labels); - flecs_rest_bool_param(req, "sources", &desc->serialize_sources); - flecs_rest_bool_param(req, "variables", &desc->serialize_variables); - flecs_rest_bool_param(req, "is_set", &desc->serialize_is_set); + flecs_rest_bool_param(req, "entity_ids", &desc->serialize_entity_ids); + flecs_rest_bool_param(req, "doc", &desc->serialize_doc); + flecs_rest_bool_param(req, "full_paths", &desc->serialize_full_paths); + flecs_rest_bool_param(req, "inherited", &desc->serialize_inherited); flecs_rest_bool_param(req, "values", &desc->serialize_values); - flecs_rest_bool_param(req, "private", &desc->serialize_private); - flecs_rest_bool_param(req, "entities", &desc->serialize_entities); - flecs_rest_bool_param(req, "entity_labels", &desc->serialize_entity_labels); - flecs_rest_bool_param(req, "variable_labels", &desc->serialize_variable_labels); - flecs_rest_bool_param(req, "variable_ids", &desc->serialize_variable_ids); - flecs_rest_bool_param(req, "colors", &desc->serialize_colors); - flecs_rest_bool_param(req, "duration", &desc->measure_eval_duration); + flecs_rest_bool_param(req, "builtin", &desc->serialize_builtin); flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info); + flecs_rest_bool_param(req, "field_info", &desc->serialize_field_info); + flecs_rest_bool_param(req, "query_info", &desc->serialize_query_info); + flecs_rest_bool_param(req, "query_plan", &desc->serialize_query_plan); + flecs_rest_bool_param(req, "query_profile", &desc->serialize_query_profile); flecs_rest_bool_param(req, "table", &desc->serialize_table); + flecs_rest_bool_param(req, "fields", &desc->serialize_fields); + + bool results = true; + flecs_rest_bool_param(req, "results", &results); + desc->dont_serialize_results = !results; } static -bool flecs_rest_reply_entity( +bool flecs_rest_get_entity( ecs_world_t *world, const ecs_http_request_t* req, ecs_http_reply_t *reply) @@ -223,7 +236,34 @@ bool flecs_rest_reply_entity( } static -bool flecs_rest_reply_world( +bool flecs_rest_put_entity( + ecs_world_t *world, + ecs_http_reply_t *reply, + const char *path) +{ + ecs_dbg_2("rest: create entity '%s'", path); + + ecs_entity_t result = ecs_entity(world, { + .name = path, + .sep = "/" + }); + + if (!result) { + ecs_dbg_2("rest: failed to create entity '%s'", path); + flecs_reply_error(reply, "failed to create entity '%s'", path); + reply->code = 500; + return true; + } + + ecs_strbuf_appendlit(&reply->body, "{\"id\":\""); + ecs_strbuf_appendint(&reply->body, (uint32_t)result); + ecs_strbuf_appendlit(&reply->body, "\"}"); + + return true; +} + +static +bool flecs_rest_get_world( ecs_world_t *world, const ecs_http_request_t* req, ecs_http_reply_t *reply) @@ -254,7 +294,7 @@ ecs_entity_t flecs_rest_entity_from_path( } static -bool flecs_rest_set( +bool flecs_rest_get_component( ecs_world_t *world, const ecs_http_request_t* req, ecs_http_reply_t *reply, @@ -265,21 +305,129 @@ bool flecs_rest_set( return true; } - const char *data = ecs_http_get_param(req, "data"); - ecs_from_json_desc_t desc = {0}; - desc.expr = data; - desc.name = path; - if (ecs_entity_from_json(world, e, data, &desc) == NULL) { - flecs_reply_error(reply, "invalid request"); + const char *component = ecs_http_get_param(req, "component"); + if (!component) { + flecs_reply_error(reply, "missing component for remove endpoint"); reply->code = 400; return true; } - + + ecs_entity_t id; + if (!flecs_id_parse(world, path, component, &id)) { + flecs_reply_error(reply, "unresolved component '%s'", component); + reply->code = 400; + return true; + } + + ecs_entity_t type = ecs_get_typeid(world, id); + if (!type) { + flecs_reply_error(reply, "component '%s' is not a type", component); + reply->code = 400; + return true; + } + + const void *ptr = ecs_get_id(world, e, id); + if (!ptr) { + flecs_reply_error(reply, "failed to get component '%s'", component); + reply->code = 500; + return true; + } + + ecs_ptr_to_json_buf(world, type, ptr, &reply->body); + + return true; +} + +static +bool flecs_rest_put_component( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + const char *path) +{ + ecs_entity_t e; + if (!(e = flecs_rest_entity_from_path(world, reply, path))) { + return true; + } + + const char *component = ecs_http_get_param(req, "component"); + if (!component) { + flecs_reply_error(reply, "missing component for remove endpoint"); + reply->code = 400; + return true; + } + + ecs_entity_t id; + if (!flecs_id_parse(world, path, component, &id)) { + flecs_reply_error(reply, "unresolved component '%s'", component); + reply->code = 400; + return true; + } + + const char *data = ecs_http_get_param(req, "value"); + if (!data) { + ecs_add_id(world, e, id); + return true; + } + + ecs_entity_t type = ecs_get_typeid(world, id); + if (!type) { + flecs_reply_error(reply, "component '%s' is not a type", component); + reply->code = 400; + return true; + } + + void *ptr = ecs_ensure_id(world, e, id); + if (!ptr) { + flecs_reply_error(reply, "failed to create component '%s'", component); + reply->code = 500; + return true; + } + + if (!ecs_ptr_from_json(world, type, ptr, data, NULL)) { + flecs_reply_error(reply, "invalid value for component '%s'", component); + reply->code = 400; + return true; + } + + ecs_modified_id(world, e, id); + + return true; +} + +static +bool flecs_rest_delete_component( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + const char *path) +{ + ecs_entity_t e; + if (!(e = flecs_rest_entity_from_path(world, reply, path))) { + return true; + } + + const char *component = ecs_http_get_param(req, "component"); + if (!component) { + flecs_reply_error(reply, "missing component for remove endpoint"); + reply->code = 400; + return true; + } + + ecs_entity_t id; + if (!flecs_id_parse(world, path, component, &id)) { + flecs_reply_error(reply, "unresolved component '%s'", component); + reply->code = 400; + return true; + } + + ecs_remove_id(world, e, id); + return true; } static -bool flecs_rest_delete( +bool flecs_rest_delete_entity( ecs_world_t *world, ecs_http_reply_t *reply, const char *path) @@ -295,18 +443,47 @@ bool flecs_rest_delete( } static -bool flecs_rest_enable( +bool flecs_rest_toggle( ecs_world_t *world, + const ecs_http_request_t* req, ecs_http_reply_t *reply, - const char *path, - bool enable) + const char *path) { ecs_entity_t e; if (!(e = flecs_rest_entity_from_path(world, reply, path))) { return true; } - ecs_enable(world, e, enable); + bool enable = true; + flecs_rest_bool_param(req, "enable", &enable); + + const char *component = ecs_http_get_param(req, "component"); + if (!component) { + ecs_enable(world, e, enable); + return true; + } + + ecs_entity_t id; + if (!flecs_id_parse(world, path, component, &id)) { + flecs_reply_error(reply, "unresolved component '%s'", component); + reply->code = 400; + return true; + } + + ecs_entity_t rel = 0; + if (ECS_IS_PAIR(id)) { + rel = ecs_pair_first(world, id); + } else { + rel = id & ECS_COMPONENT_MASK; + } + + if (!ecs_has_id(world, rel, EcsCanToggle)) { + flecs_reply_error(reply, "cannot toggle component '%s'", component); + reply->code = 400; + return true; + } + + ecs_enable_id(world, e, id, enable); return true; } @@ -315,37 +492,53 @@ static bool flecs_rest_script( ecs_world_t *world, const ecs_http_request_t* req, - ecs_http_reply_t *reply) + ecs_http_reply_t *reply, + const char *path) { (void)world; (void)req; (void)reply; -#ifdef FLECS_PLECS - const char *data = ecs_http_get_param(req, "data"); - if (!data) { +#ifdef FLECS_SCRIPT + ecs_entity_t script = flecs_rest_entity_from_path(world, reply, path); + if (!script) { + script = ecs_entity(world, { .name = path }); + } + + const char *code = ecs_http_get_param(req, "code"); + if (!code) { flecs_reply_error(reply, "missing data parameter"); return true; } + bool try = false; + flecs_rest_bool_param(req, "try", &try); + bool prev_color = ecs_log_enable_colors(false); - rest_prev_log = ecs_os_api.log_; + ecs_os_api_log_t prev_log = ecs_os_api.log_; + rest_prev_log = try ? NULL : prev_log; ecs_os_api.log_ = flecs_rest_capture_log; - ecs_entity_t script = ecs_script(world, { - .entity = ecs_entity(world, { .name = "scripts.main" }), - .str = data + script = ecs_script(world, { + .entity = script, + .code = code }); if (!script) { char *err = flecs_rest_get_captured_log(); - char *escaped_err = ecs_astresc('"', err); - flecs_reply_error(reply, escaped_err); - reply->code = 400; /* bad request */ + char *escaped_err = flecs_astresc('"', err); + if (escaped_err) { + flecs_reply_error(reply, escaped_err); + } else { + flecs_reply_error(reply, "error parsing script"); + } + if (!try) { + reply->code = 400; /* bad request */ + } ecs_os_free(escaped_err); ecs_os_free(err); } - ecs_os_api.log_ = rest_prev_log; + ecs_os_api.log_ = prev_log; ecs_log_enable_colors(prev_color); return true; @@ -360,25 +553,25 @@ void flecs_rest_reply_set_captured_log( { char *err = flecs_rest_get_captured_log(); if (err) { - char *escaped_err = ecs_astresc('"', err); + char *escaped_err = flecs_astresc('"', err); flecs_reply_error(reply, escaped_err); - reply->code = 400; ecs_os_free(escaped_err); ecs_os_free(err); } + + reply->code = 400; } static -int flecs_rest_iter_to_reply( - ecs_world_t *world, +void flecs_rest_iter_to_reply( const ecs_http_request_t* req, ecs_http_reply_t *reply, + ecs_poly_t *query, ecs_iter_t *it) { - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_entities = true; - desc.serialize_variables = true; + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; flecs_rest_parse_json_ser_iter_params(&desc, req); + desc.query = query; int32_t offset = 0; int32_t limit = 1000; @@ -388,17 +581,15 @@ int flecs_rest_iter_to_reply( if (offset < 0 || limit < 0) { flecs_reply_error(reply, "invalid offset/limit parameter"); - reply->code = 400; - return -1; + return; } ecs_iter_t pit = ecs_page_iter(it, offset, limit); - if (ecs_iter_to_json_buf(world, &pit, &reply->body, &desc)) { + if (ecs_iter_to_json_buf(&pit, &reply->body, &desc)) { flecs_rest_reply_set_captured_log(reply); - return -1; } - return 0; + flecs_rest_int_param(req, "offset", &offset); } static @@ -408,44 +599,58 @@ bool flecs_rest_reply_existing_query( ecs_http_reply_t *reply, const char *name) { - ecs_entity_t q = ecs_lookup_fullpath(world, name); - if (!q) { + ecs_entity_t qe = ecs_lookup(world, name); + if (!qe) { flecs_reply_error(reply, "unresolved identifier '%s'", name); reply->code = 404; return true; } - const EcsPoly *poly = ecs_get_pair(world, q, EcsPoly, EcsQuery); - if (!poly) { - flecs_reply_error(reply, - "resolved identifier '%s' is not a query", name); + ecs_query_t *q = NULL; + const EcsPoly *poly_comp = ecs_get_pair(world, qe, EcsPoly, EcsQuery); + if (!poly_comp) { + poly_comp = ecs_get_pair(world, qe, EcsPoly, EcsObserver); + if (poly_comp) { + q = ((ecs_observer_t*)poly_comp->poly)->query; + } else { + flecs_reply_error(reply, + "resolved identifier '%s' is not a query", name); + reply->code = 400; + return true; + } + } else { + q = poly_comp->poly; + } + + if (!q) { + flecs_reply_error(reply, "query '%s' is not initialized", name); reply->code = 400; return true; } - ecs_iter_t it; - ecs_iter_poly(world, poly->poly, &it, NULL); + ecs_iter_t it = ecs_query_iter(world, q); - ecs_dbg_2("rest: request query '%s'", q); + ecs_dbg_2("rest: request query '%s'", name); bool prev_color = ecs_log_enable_colors(false); rest_prev_log = ecs_os_api.log_; ecs_os_api.log_ = flecs_rest_capture_log; const char *vars = ecs_http_get_param(req, "vars"); if (vars) { - if (!ecs_poly_is(poly->poly, ecs_rule_t)) { - flecs_reply_error(reply, - "variables are only supported for rule queries"); - reply->code = 400; - return true; - } - if (ecs_rule_parse_vars(poly->poly, &it, vars) == NULL) { + #ifdef FLECS_SCRIPT + if (ecs_query_args_parse(q, &it, vars) == NULL) { flecs_rest_reply_set_captured_log(reply); return true; } + #else + flecs_reply_error(reply, + "cannot parse query arg expression: script addon required"); + reply->code = 400; + return true; + #endif } - flecs_rest_iter_to_reply(world, req, reply, &it); + flecs_rest_iter_to_reply(req, reply, q, &it); ecs_os_api.log_ = rest_prev_log; ecs_log_enable_colors(prev_color); @@ -454,7 +659,7 @@ bool flecs_rest_reply_existing_query( } static -bool flecs_rest_reply_query( +bool flecs_rest_get_query( ecs_world_t *world, const ecs_http_request_t* req, ecs_http_reply_t *reply) @@ -464,36 +669,42 @@ bool flecs_rest_reply_query( return flecs_rest_reply_existing_query(world, req, reply, q_name); } - const char *q = ecs_http_get_param(req, "q"); - if (!q) { - ecs_strbuf_appendlit(&reply->body, "Missing parameter 'q'"); + const char *expr = ecs_http_get_param(req, "expr"); + if (!expr) { + ecs_strbuf_appendlit(&reply->body, "Missing parameter 'expr'"); reply->code = 400; /* bad request */ return true; } - ecs_dbg_2("rest: request query '%s'", q); + bool try = false; + flecs_rest_bool_param(req, "try", &try); + + ecs_dbg_2("rest: request query '%s'", expr); bool prev_color = ecs_log_enable_colors(false); - rest_prev_log = ecs_os_api.log_; + ecs_os_api_log_t prev_log = ecs_os_api.log_; + rest_prev_log = try ? NULL : prev_log; ecs_os_api.log_ = flecs_rest_capture_log; - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ - .expr = q - }); - if (!r) { + ecs_query_t *q = ecs_query(world, { .expr = expr }); + if (!q) { flecs_rest_reply_set_captured_log(reply); + if (try) { + /* If client is trying queries, don't spam console with errors */ + reply->code = 200; + } } else { - ecs_iter_t it = ecs_rule_iter(world, r); - flecs_rest_iter_to_reply(world, req, reply, &it); - ecs_rule_fini(r); + ecs_iter_t it = ecs_query_iter(world, q); + flecs_rest_iter_to_reply(req, reply, q, &it); + ecs_query_fini(q); } - ecs_os_api.log_ = rest_prev_log; + ecs_os_api.log_ = prev_log; ecs_log_enable_colors(prev_color); return true; } -#ifdef FLECS_MONITOR +#ifdef FLECS_STATS static void flecs_rest_array_append_( @@ -597,7 +808,7 @@ void flecs_world_stats_to_json( ECS_COUNTER_APPEND(reply, stats, commands.delete_count, "Delete commands executed"); ECS_COUNTER_APPEND(reply, stats, commands.clear_count, "Clear commands executed"); ECS_COUNTER_APPEND(reply, stats, commands.set_count, "Set commands executed"); - ECS_COUNTER_APPEND(reply, stats, commands.get_mut_count, "Get_mut commands executed"); + ECS_COUNTER_APPEND(reply, stats, commands.ensure_count, "Get_mut commands executed"); ECS_COUNTER_APPEND(reply, stats, commands.modified_count, "Modified commands executed"); ECS_COUNTER_APPEND(reply, stats, commands.other_count, "Misc commands executed"); ECS_COUNTER_APPEND(reply, stats, commands.discard_count, "Commands for already deleted entities"); @@ -660,9 +871,13 @@ void flecs_system_stats_to_json( { ecs_strbuf_list_push(reply, "{", ","); ecs_strbuf_list_appendlit(reply, "\"name\":\""); - ecs_get_path_w_sep_buf(world, 0, system, ".", NULL, reply); + ecs_get_path_w_sep_buf(world, 0, system, ".", NULL, reply, true); ecs_strbuf_appendch(reply, '"'); + bool disabled = ecs_has_id(world, system, EcsDisabled); + ecs_strbuf_list_appendlit(reply, "\"disabled\":"); + ecs_strbuf_appendstr(reply, disabled ? "true" : "false"); + if (!stats->task) { ECS_GAUGE_APPEND(reply, &stats->query, matched_table_count, ""); ECS_GAUGE_APPEND(reply, &stats->query, matched_entity_count, ""); @@ -672,55 +887,128 @@ void flecs_system_stats_to_json( ecs_strbuf_list_pop(reply, "}"); } +static +void flecs_sync_stats_to_json( + ecs_http_reply_t *reply, + const ecs_pipeline_stats_t *pstats, + const ecs_sync_stats_t *stats) +{ + ecs_strbuf_list_push(&reply->body, "{", ","); + + ecs_strbuf_list_appendlit(&reply->body, "\"multi_threaded\":"); + ecs_strbuf_appendbool(&reply->body, stats->multi_threaded); + + ecs_strbuf_list_appendlit(&reply->body, "\"immediate\":"); + ecs_strbuf_appendbool(&reply->body, stats->immediate); + + ECS_GAUGE_APPEND_T(&reply->body, stats, time_spent, pstats->t, ""); + ECS_GAUGE_APPEND_T(&reply->body, stats, commands_enqueued, pstats->t, ""); + + ecs_strbuf_list_pop(&reply->body, "}"); +} + +static +void flecs_all_systems_stats_to_json( + ecs_world_t *world, + ecs_http_reply_t *reply, + ecs_entity_t period) +{ + const EcsSystemStats *stats = ecs_get_pair(world, EcsWorld, + EcsSystemStats, period); + + ecs_strbuf_list_push(&reply->body, "[", ","); + + if (stats) { + ecs_map_iter_t it = ecs_map_iter(&stats->stats); + while (ecs_map_next(&it)) { + ecs_entity_t id = ecs_map_key(&it); + ecs_system_stats_t *sys_stats = ecs_map_ptr(&it); + + if (!ecs_is_alive(world, id)) { + continue; + } + + ecs_strbuf_list_next(&reply->body); + flecs_system_stats_to_json(world, &reply->body, id, sys_stats); + } + } + + ecs_strbuf_list_pop(&reply->body, "]"); +} + static void flecs_pipeline_stats_to_json( ecs_world_t *world, - ecs_strbuf_t *reply, - const EcsPipelineStats *stats) + const ecs_http_request_t* req, + ecs_http_reply_t *reply, + ecs_entity_t period) { - ecs_strbuf_list_push(reply, "[", ","); + char *pipeline_name = NULL; + flecs_rest_string_param(req, "name", &pipeline_name); - int32_t i, count = ecs_vec_count(&stats->stats.systems), sync_cur = 0; - ecs_entity_t *ids = ecs_vec_first_t(&stats->stats.systems, ecs_entity_t); - for (i = 0; i < count; i ++) { - ecs_entity_t id = ids[i]; - - ecs_strbuf_list_next(reply); + if (!pipeline_name || !ecs_os_strcmp(pipeline_name, "all")) { + flecs_all_systems_stats_to_json(world, reply, period); + return; + } - if (id) { - ecs_system_stats_t *sys_stats = ecs_map_get_deref( - &stats->stats.system_stats, ecs_system_stats_t, id); - flecs_system_stats_to_json(world, reply, id, sys_stats); - } else { - /* Sync point */ - ecs_strbuf_list_push(reply, "{", ","); - ecs_sync_stats_t *sync_stats = ecs_vec_get_t( - &stats->stats.sync_points, ecs_sync_stats_t, sync_cur); + ecs_entity_t e = ecs_lookup(world, pipeline_name); + if (!e) { + flecs_reply_error(reply, "pipeline '%s' not found", pipeline_name); + reply->code = 404; + return; + } - ecs_strbuf_list_appendlit(reply, "\"system_count\":"); - ecs_strbuf_appendint(reply, sync_stats->system_count); + const EcsPipelineStats *stats = ecs_get_pair(world, EcsWorld, + EcsPipelineStats, period); + const EcsSystemStats *system_stats = ecs_get_pair(world, EcsWorld, + EcsSystemStats, period); + if (!stats || !system_stats) { + goto noresults; + } + + ecs_pipeline_stats_t *pstats = ecs_map_get_deref( + &stats->stats, ecs_pipeline_stats_t, e); + if (!pstats) { + goto noresults; + } + + const EcsPipeline *p = ecs_get(world, e, EcsPipeline); + + ecs_strbuf_list_push(&reply->body, "[", ","); - ecs_strbuf_list_appendlit(reply, "\"multi_threaded\":"); - ecs_strbuf_appendbool(reply, sync_stats->multi_threaded); + ecs_pipeline_op_t *ops = ecs_vec_first_t(&p->state->ops, ecs_pipeline_op_t); + ecs_entity_t *systems = ecs_vec_first_t(&p->state->systems, ecs_entity_t); + ecs_sync_stats_t *syncs = ecs_vec_first_t( + &pstats->sync_points, ecs_sync_stats_t); - ecs_strbuf_list_appendlit(reply, "\"no_readonly\":"); - ecs_strbuf_appendbool(reply, sync_stats->no_readonly); + int32_t s, o, op_count = ecs_vec_count(&p->state->ops); + for (o = 0; o < op_count; o ++) { + ecs_pipeline_op_t *op = &ops[o]; + for (s = op->offset; s < (op->offset + op->count); s ++) { + ecs_entity_t system = systems[s]; - ECS_GAUGE_APPEND_T(reply, sync_stats, - time_spent, stats->stats.t, ""); - ECS_GAUGE_APPEND_T(reply, sync_stats, - commands_enqueued, stats->stats.t, ""); + if (!ecs_is_alive(world, system)) { + continue; + } - ecs_strbuf_list_pop(reply, "}"); - sync_cur ++; + ecs_system_stats_t *sys_stats = ecs_map_get_deref( + &system_stats->stats, ecs_system_stats_t, system); + ecs_strbuf_list_next(&reply->body); + flecs_system_stats_to_json(world, &reply->body, system, sys_stats); } + + ecs_strbuf_list_next(&reply->body); + flecs_sync_stats_to_json(reply, pstats, &syncs[o]); } - ecs_strbuf_list_pop(reply, "]"); + ecs_strbuf_list_pop(&reply->body, "]"); + return; +noresults: + ecs_strbuf_appendlit(&reply->body, "[]"); } static -bool flecs_rest_reply_stats( +bool flecs_rest_get_stats( ecs_world_t *world, const ecs_http_request_t* req, ecs_http_reply_t *reply) @@ -731,8 +1019,8 @@ bool flecs_rest_reply_stats( ecs_entity_t period = EcsPeriod1s; if (period_str) { - char *period_name = ecs_asprintf("Period%s", period_str); - period = ecs_lookup_child(world, ecs_id(FlecsMonitor), period_name); + char *period_name = flecs_asprintf("Period%s", period_str); + period = ecs_lookup_child(world, ecs_id(FlecsStats), period_name); ecs_os_free(period_name); if (!period) { flecs_reply_error(reply, "bad request (invalid period string)"); @@ -748,9 +1036,7 @@ bool flecs_rest_reply_stats( return true; } else if (!ecs_os_strcmp(category, "pipeline")) { - const EcsPipelineStats *stats = ecs_get_pair(world, EcsWorld, - EcsPipelineStats, period); - flecs_pipeline_stats_to_json(world, &reply->body, stats); + flecs_pipeline_stats_to_json(world, req, reply, period); return true; } else { @@ -761,7 +1047,7 @@ bool flecs_rest_reply_stats( } #else static -bool flecs_rest_reply_stats( +bool flecs_rest_get_stats( ecs_world_t *world, const ecs_http_request_t* req, ecs_http_reply_t *reply) @@ -797,21 +1083,24 @@ void flecs_rest_reply_table_append_memory( const ecs_table_t *table) { int32_t used = 0, allocated = 0; + int32_t count = ecs_table_count(table), size = ecs_table_size(table); - used += table->data.entities.count * ECS_SIZEOF(ecs_entity_t); - allocated += table->data.entities.size * ECS_SIZEOF(ecs_entity_t); + used += count * ECS_SIZEOF(ecs_entity_t); + allocated += size * ECS_SIZEOF(ecs_entity_t); int32_t i, storage_count = table->column_count; ecs_column_t *columns = table->data.columns; for (i = 0; i < storage_count; i ++) { - used += columns[i].data.count * columns[i].ti->size; - allocated += columns[i].data.size * columns[i].ti->size; + used += count * columns[i].ti->size; + allocated += size * columns[i].ti->size; } ecs_strbuf_list_push(reply, "{", ","); - ecs_strbuf_list_append(reply, "\"used\":%d", used); - ecs_strbuf_list_append(reply, "\"allocated\":%d", allocated); + ecs_strbuf_list_appendlit(reply, "\"used\":"); + ecs_strbuf_appendint(reply, used); + ecs_strbuf_list_appendlit(reply, "\"allocated\":"); + ecs_strbuf_appendint(reply, allocated); ecs_strbuf_list_pop(reply, "}"); } @@ -823,17 +1112,19 @@ void flecs_rest_reply_table_append( { ecs_strbuf_list_next(reply); ecs_strbuf_list_push(reply, "{", ","); - ecs_strbuf_list_append(reply, "\"id\":%u", (uint32_t)table->id); - ecs_strbuf_list_appendstr(reply, "\"type\":"); + ecs_strbuf_list_appendlit(reply, "\"id\":"); + ecs_strbuf_appendint(reply, (uint32_t)table->id); + ecs_strbuf_list_appendlit(reply, "\"type\":"); flecs_rest_reply_table_append_type(world, reply, table); - ecs_strbuf_list_append(reply, "\"count\":%d", ecs_table_count(table)); - ecs_strbuf_list_append(reply, "\"memory\":"); + ecs_strbuf_list_appendlit(reply, "\"count\":"); + ecs_strbuf_appendint(reply, ecs_table_count(table)); + ecs_strbuf_list_appendlit(reply, "\"memory\":"); flecs_rest_reply_table_append_memory(reply, table); ecs_strbuf_list_pop(reply, "}"); } static -bool flecs_rest_reply_tables( +bool flecs_rest_get_tables( ecs_world_t *world, const ecs_http_request_t* req, ecs_http_reply_t *reply) @@ -852,6 +1143,294 @@ bool flecs_rest_reply_tables( return true; } +static +const char* flecs_rest_cmd_kind_to_str( + ecs_cmd_kind_t kind) +{ + switch(kind) { + case EcsCmdClone: return "Clone"; + case EcsCmdBulkNew: return "BulkNew"; + case EcsCmdAdd: return "Add"; + case EcsCmdRemove: return "Remove"; + case EcsCmdSet: return "Set"; + case EcsCmdEmplace: return "Emplace"; + case EcsCmdEnsure: return "Ensure"; + case EcsCmdModified: return "Modified"; + case EcsCmdModifiedNoHook: return "ModifiedNoHook"; + case EcsCmdAddModified: return "AddModified"; + case EcsCmdPath: return "Path"; + case EcsCmdDelete: return "Delete"; + case EcsCmdClear: return "Clear"; + case EcsCmdOnDeleteAction: return "OnDeleteAction"; + case EcsCmdEnable: return "Enable"; + case EcsCmdDisable: return "Disable"; + case EcsCmdEvent: return "Event"; + case EcsCmdSkip: return "Skip"; + default: return "Unknown"; + } +} + +static +bool flecs_rest_cmd_has_id( + const ecs_cmd_t *cmd) +{ + switch(cmd->kind) { + case EcsCmdClear: + case EcsCmdDelete: + case EcsCmdClone: + case EcsCmdDisable: + case EcsCmdPath: + return false; + case EcsCmdBulkNew: + case EcsCmdAdd: + case EcsCmdRemove: + case EcsCmdSet: + case EcsCmdEmplace: + case EcsCmdEnsure: + case EcsCmdModified: + case EcsCmdModifiedNoHook: + case EcsCmdAddModified: + case EcsCmdOnDeleteAction: + case EcsCmdEnable: + case EcsCmdEvent: + case EcsCmdSkip: + default: + return true; + } +} + +static +void flecs_rest_server_garbage_collect_all( + ecs_rest_ctx_t *impl) +{ + ecs_map_iter_t it = ecs_map_iter(&impl->cmd_captures); + + while (ecs_map_next(&it)) { + ecs_rest_cmd_capture_t *capture = ecs_map_ptr(&it); + int32_t i, count = ecs_vec_count(&capture->syncs); + ecs_rest_cmd_sync_capture_t *syncs = ecs_vec_first(&capture->syncs); + for (i = 0; i < count; i ++) { + ecs_rest_cmd_sync_capture_t *sync = &syncs[i]; + ecs_os_free(sync->cmds); + } + ecs_vec_fini_t(NULL, &capture->syncs, ecs_rest_cmd_sync_capture_t); + ecs_os_free(capture); + } + + ecs_map_fini(&impl->cmd_captures); +} + +static +void flecs_rest_server_garbage_collect( + ecs_world_t *world, + ecs_rest_ctx_t *impl) +{ + const ecs_world_info_t *wi = ecs_get_world_info(world); + ecs_map_iter_t it = ecs_map_iter(&impl->cmd_captures); + ecs_vec_t removed_frames = {0}; + + while (ecs_map_next(&it)) { + int64_t frame = flecs_uto(int64_t, ecs_map_key(&it)); + if ((wi->frame_count_total - frame) > FLECS_REST_COMMAND_RETAIN_COUNT) { + ecs_rest_cmd_capture_t *capture = ecs_map_ptr(&it); + int32_t i, count = ecs_vec_count(&capture->syncs); + ecs_rest_cmd_sync_capture_t *syncs = ecs_vec_first(&capture->syncs); + for (i = 0; i < count; i ++) { + ecs_rest_cmd_sync_capture_t *sync = &syncs[i]; + ecs_os_free(sync->cmds); + } + ecs_vec_fini_t(NULL, &capture->syncs, ecs_rest_cmd_sync_capture_t); + ecs_os_free(capture); + + ecs_vec_init_if_t(&removed_frames, int64_t); + ecs_vec_append_t(NULL, &removed_frames, int64_t)[0] = frame; + } + } + + int32_t i, count = ecs_vec_count(&removed_frames); + if (count) { + int64_t *frames = ecs_vec_first(&removed_frames); + if (count) { + for (i = 0; i < count; i ++) { + ecs_map_remove(&impl->cmd_captures, + flecs_ito(uint64_t, frames[i])); + } + } + ecs_vec_fini_t(NULL, &removed_frames, int64_t); + } +} + +static +void flecs_rest_cmd_to_json( + ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_cmd_t *cmd) +{ + ecs_strbuf_list_push(buf, "{", ","); + + ecs_strbuf_list_appendlit(buf, "\"kind\":\""); + ecs_strbuf_appendstr(buf, flecs_rest_cmd_kind_to_str(cmd->kind)); + ecs_strbuf_appendlit(buf, "\""); + + if (flecs_rest_cmd_has_id(cmd)) { + ecs_strbuf_list_appendlit(buf, "\"id\":\""); + char *idstr = ecs_id_str(world, cmd->id); + ecs_strbuf_appendstr(buf, idstr); + ecs_strbuf_appendlit(buf, "\""); + ecs_os_free(idstr); + } + + if (cmd->system) { + ecs_strbuf_list_appendlit(buf, "\"system\":\""); + char *sysstr = ecs_get_path(world, cmd->system); + ecs_strbuf_appendstr(buf, sysstr); + ecs_strbuf_appendlit(buf, "\""); + ecs_os_free(sysstr); + } + + if (cmd->kind == EcsCmdBulkNew) { + /* Todo */ + } else if (cmd->kind == EcsCmdEvent) { + /* Todo */ + } else { + if (cmd->entity) { + ecs_strbuf_list_appendlit(buf, "\"entity\":\""); + char *path = ecs_get_path_w_sep(world, 0, cmd->entity, ".", ""); + ecs_strbuf_appendstr(buf, path); + ecs_strbuf_appendlit(buf, "\""); + ecs_os_free(path); + + ecs_strbuf_list_appendlit(buf, "\"is_alive\":\""); + if (ecs_is_alive(world, cmd->entity)) { + ecs_strbuf_appendlit(buf, "true"); + } else { + ecs_strbuf_appendlit(buf, "false"); + } + ecs_strbuf_appendlit(buf, "\""); + + ecs_strbuf_list_appendlit(buf, "\"next_for_entity\":"); + ecs_strbuf_appendint(buf, cmd->next_for_entity); + } + } + + ecs_strbuf_list_pop(buf, "}"); +} + +static +void flecs_rest_on_commands( + const ecs_stage_t *stage, + const ecs_vec_t *commands, + void *ctx) +{ + ecs_world_t *world = stage->world; + ecs_rest_cmd_capture_t *capture = ctx; + ecs_assert(capture != NULL, ECS_INTERNAL_ERROR, NULL); + + if (commands) { + ecs_vec_init_if_t(&capture->syncs, ecs_rest_cmd_sync_capture_t); + ecs_rest_cmd_sync_capture_t *sync = ecs_vec_append_t( + NULL, &capture->syncs, ecs_rest_cmd_sync_capture_t); + + int32_t i, count = ecs_vec_count(commands); + ecs_cmd_t *cmds = ecs_vec_first(commands); + sync->buf = ECS_STRBUF_INIT; + ecs_strbuf_list_push(&sync->buf, "{", ","); + ecs_strbuf_list_appendlit(&sync->buf, "\"commands\":"); + ecs_strbuf_list_push(&sync->buf, "[", ","); + for (i = 0; i < count; i ++) { + ecs_strbuf_list_next(&sync->buf); + flecs_rest_cmd_to_json(world, &sync->buf, &cmds[i]); + } + ecs_strbuf_list_pop(&sync->buf, "]"); + + /* Measure how long it takes to process queue */ + sync->start_time = (ecs_time_t){0}; + ecs_time_measure(&sync->start_time); + } else { + /* Finished processing queue, measure duration */ + ecs_rest_cmd_sync_capture_t *sync = ecs_vec_last_t( + &capture->syncs, ecs_rest_cmd_sync_capture_t); + double duration = ecs_time_measure(&sync->start_time); + + ecs_strbuf_list_appendlit(&sync->buf, "\"duration\":"); + ecs_strbuf_appendflt(&sync->buf, duration, '"'); + ecs_strbuf_list_pop(&sync->buf, "}"); + + sync->cmds = ecs_strbuf_get(&sync->buf); + } +} + +static +bool flecs_rest_get_commands_capture( + ecs_world_t *world, + ecs_rest_ctx_t *impl, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) +{ + (void)req; + const ecs_world_info_t *wi = ecs_get_world_info(world); + ecs_strbuf_appendstr(&reply->body, "{"); + ecs_strbuf_appendlit(&reply->body, "\"frame\":"); + ecs_strbuf_appendint(&reply->body, wi->frame_count_total); + ecs_strbuf_appendstr(&reply->body, "}"); + + ecs_map_init_if(&impl->cmd_captures, &world->allocator); + ecs_rest_cmd_capture_t *capture = ecs_map_ensure_alloc_t( + &impl->cmd_captures, ecs_rest_cmd_capture_t, + flecs_ito(uint64_t, wi->frame_count_total)); + + world->on_commands = flecs_rest_on_commands; + world->on_commands_ctx = capture; + + /* Run garbage collection so that requests don't linger */ + flecs_rest_server_garbage_collect(world, impl); + + return true; +} + +static +bool flecs_rest_get_commands_request( + ecs_world_t *world, + ecs_rest_ctx_t *impl, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) +{ + (void)world; + char *frame_str = &req->path[15]; + int32_t frame = atoi(frame_str); + + ecs_map_init_if(&impl->cmd_captures, &world->allocator); + const ecs_rest_cmd_capture_t *capture = ecs_map_get_deref( + &impl->cmd_captures, ecs_rest_cmd_capture_t, + flecs_ito(uint64_t, frame)); + + if (!capture) { + ecs_strbuf_appendstr(&reply->body, "{"); + ecs_strbuf_append(&reply->body, + "\"error\": \"no capture for frame %u\"", frame); + ecs_strbuf_appendstr(&reply->body, "}"); + reply->code = 404; + return true; + } + + ecs_strbuf_appendstr(&reply->body, "{"); + ecs_strbuf_list_append(&reply->body, "\"syncs\":"); + ecs_strbuf_list_push(&reply->body, "[", ","); + + int32_t i, count = ecs_vec_count(&capture->syncs); + ecs_rest_cmd_sync_capture_t *syncs = ecs_vec_first(&capture->syncs); + + for (i = 0; i < count; i ++) { + ecs_rest_cmd_sync_capture_t *sync = &syncs[i]; + ecs_strbuf_list_appendstr(&reply->body, sync->cmds); + } + + ecs_strbuf_list_pop(&reply->body, "]"); + ecs_strbuf_appendstr(&reply->body, "}"); + + return true; +} + static bool flecs_rest_reply( const ecs_http_request_t* req, @@ -871,45 +1450,62 @@ bool flecs_rest_reply( if (req->method == EcsHttpGet) { /* Entity endpoint */ if (!ecs_os_strncmp(req->path, "entity/", 7)) { - return flecs_rest_reply_entity(world, req, reply); + return flecs_rest_get_entity(world, req, reply); + + /* Component GET endpoint */ + } else if (!ecs_os_strncmp(req->path, "component/", 10)) { + return flecs_rest_get_component(world, req, reply, &req->path[10]); /* Query endpoint */ } else if (!ecs_os_strcmp(req->path, "query")) { - return flecs_rest_reply_query(world, req, reply); + return flecs_rest_get_query(world, req, reply); /* World endpoint */ } else if (!ecs_os_strcmp(req->path, "world")) { - return flecs_rest_reply_world(world, req, reply); + return flecs_rest_get_world(world, req, reply); /* Stats endpoint */ } else if (!ecs_os_strncmp(req->path, "stats/", 6)) { - return flecs_rest_reply_stats(world, req, reply); + return flecs_rest_get_stats(world, req, reply); /* Tables endpoint */ } else if (!ecs_os_strncmp(req->path, "tables", 6)) { - return flecs_rest_reply_tables(world, req, reply); + return flecs_rest_get_tables(world, req, reply); + + /* Commands capture endpoint */ + } else if (!ecs_os_strncmp(req->path, "commands/capture", 16)) { + return flecs_rest_get_commands_capture(world, impl, req, reply); + + /* Commands request endpoint (request commands from specific frame) */ + } else if (!ecs_os_strncmp(req->path, "commands/frame/", 15)) { + return flecs_rest_get_commands_request(world, impl, req, reply); } } else if (req->method == EcsHttpPut) { - /* Set endpoint */ - if (!ecs_os_strncmp(req->path, "set/", 4)) { - return flecs_rest_set(world, req, reply, &req->path[4]); - - /* Delete endpoint */ - } else if (!ecs_os_strncmp(req->path, "delete/", 7)) { - return flecs_rest_delete(world, reply, &req->path[7]); + /* Component PUT endpoint */ + if (!ecs_os_strncmp(req->path, "entity/", 7)) { + return flecs_rest_put_entity(world, reply, &req->path[7]); - /* Enable endpoint */ - } else if (!ecs_os_strncmp(req->path, "enable/", 7)) { - return flecs_rest_enable(world, reply, &req->path[7], true); + /* Component PUT endpoint */ + } else if (!ecs_os_strncmp(req->path, "component/", 10)) { + return flecs_rest_put_component(world, req, reply, &req->path[10]); - /* Disable endpoint */ - } else if (!ecs_os_strncmp(req->path, "disable/", 8)) { - return flecs_rest_enable(world, reply, &req->path[8], false); + /* Enable endpoint */ + } else if (!ecs_os_strncmp(req->path, "toggle/", 7)) { + return flecs_rest_toggle(world, req, reply, &req->path[7]); /* Script endpoint */ - } else if (!ecs_os_strncmp(req->path, "script", 6)) { - return flecs_rest_script(world, req, reply); + } else if (!ecs_os_strncmp(req->path, "script/", 7)) { + return flecs_rest_script(world, req, reply, &req->path[7]); + } + } else if (req->method == EcsHttpDelete) { + /* Entity DELETE endpoint */ + if (!ecs_os_strncmp(req->path, "entity/", 7)) { + return flecs_rest_delete_entity(world, reply, &req->path[7]); + + /* Component DELETE endpoint */ + } else if (!ecs_os_strncmp(req->path, "component/", 10)) { + return flecs_rest_delete_component(world, req, reply, &req->path[10]); } } @@ -944,14 +1540,15 @@ ecs_http_server_t* ecs_rest_server_init( void ecs_rest_server_fini( ecs_http_server_t *srv) { - ecs_rest_ctx_t *srv_ctx = ecs_http_server_ctx(srv); - ecs_os_free(srv_ctx); + ecs_rest_ctx_t *impl = ecs_http_server_ctx(srv); + flecs_rest_server_garbage_collect_all(impl); + ecs_os_free(impl); ecs_http_server_fini(srv); } static void flecs_on_set_rest(ecs_iter_t *it) { - EcsRest *rest = it->ptrs[0]; + EcsRest *rest = ecs_field(it, EcsRest, 0); int i; for(i = 0; i < it->count; i ++) { @@ -962,7 +1559,8 @@ void flecs_on_set_rest(ecs_iter_t *it) { ecs_http_server_t *srv = ecs_rest_server_init(it->real_world, &(ecs_http_server_desc_t){ .ipaddr = rest[i].ipaddr, - .port = rest[i].port + .port = rest[i].port, + .cache_timeout = 0.2 }); if (!srv) { @@ -980,7 +1578,7 @@ void flecs_on_set_rest(ecs_iter_t *it) { static void DequeueRest(ecs_iter_t *it) { - EcsRest *rest = ecs_field(it, EcsRest, 1); + EcsRest *rest = ecs_field(it, EcsRest, 0); if (it->delta_system_time > (ecs_ftime_t)1.0) { ecs_warn( @@ -988,11 +1586,16 @@ void DequeueRest(ecs_iter_t *it) { (double)it->delta_system_time); } + const ecs_world_info_t *wi = ecs_get_world_info(it->world); + int32_t i; for(i = 0; i < it->count; i ++) { ecs_rest_ctx_t *ctx = rest[i].impl; if (ctx) { - ecs_http_server_dequeue(ctx->srv, it->delta_time); + float elapsed = (float)(wi->world_time_total_raw - ctx->last_time); + ecs_http_server_dequeue(ctx->srv, (ecs_ftime_t)elapsed); + flecs_rest_server_garbage_collect(it->world, ctx); + ctx->last_time = wi->world_time_total_raw; } } } @@ -1001,15 +1604,12 @@ static void DisableRest(ecs_iter_t *it) { ecs_world_t *world = it->world; - ecs_iter_t rit = ecs_term_iter(world, &(ecs_term_t){ - .id = ecs_id(EcsRest), - .src.flags = EcsSelf - }); + ecs_iter_t rit = ecs_each_id(world, ecs_id(EcsRest)); if (it->event == EcsOnAdd) { /* REST module was disabled */ - while (ecs_term_next(&rit)) { - EcsRest *rest = ecs_field(&rit, EcsRest, 1); + while (ecs_each_next(&rit)) { + EcsRest *rest = ecs_field(&rit, EcsRest, 0); int i; for (i = 0; i < rit.count; i ++) { ecs_rest_ctx_t *ctx = rest[i].impl; @@ -1018,8 +1618,8 @@ void DisableRest(ecs_iter_t *it) { } } else if (it->event == EcsOnRemove) { /* REST module was enabled */ - while (ecs_term_next(&rit)) { - EcsRest *rest = ecs_field(&rit, EcsRest, 1); + while (ecs_each_next(&rit)) { + EcsRest *rest = ecs_field(&rit, EcsRest, 0); int i; for (i = 0; i < rit.count; i ++) { ecs_rest_ctx_t *ctx = rest[i].impl; @@ -1035,31 +1635,38 @@ void FlecsRestImport( ECS_MODULE(world, FlecsRest); ECS_IMPORT(world, FlecsPipeline); -#ifdef FLECS_PLECS +#ifdef FLECS_SCRIPT ECS_IMPORT(world, FlecsScript); #endif +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsRest), + "Module that implements Flecs REST API"); +#endif ecs_set_name_prefix(world, "Ecs"); flecs_bootstrap_component(world, EcsRest); ecs_set_hooks(world, EcsRest, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .move = ecs_move(EcsRest), .copy = ecs_copy(EcsRest), .dtor = ecs_dtor(EcsRest), .on_set = flecs_on_set_rest }); - ECS_SYSTEM(world, DequeueRest, EcsPostFrame, EcsRest); - ecs_system(world, { - .entity = ecs_id(DequeueRest), - .no_readonly = true + .entity = ecs_entity(world, {.name = "DequeueRest", .add = ecs_ids( ecs_dependson(EcsPostFrame))}), + .query.terms = { + { .id = ecs_id(EcsRest) }, + }, + .callback = DequeueRest, + .immediate = true }); ecs_observer(world, { - .filter = { + .query = { .terms = {{ .id = EcsDisabled, .src.id = ecs_id(FlecsRest) }} }, .events = {EcsOnAdd, EcsOnRemove}, @@ -1068,6 +1675,12 @@ void FlecsRestImport( ecs_set_name_prefix(world, "EcsRest"); ECS_TAG_DEFINE(world, EcsRestPlecs); + + /* Enable frame time measurements so we're guaranteed to have a delta time + * value to pass into the HTTP server. */ + if (ecs_os_has_time()) { + ecs_measure_frame_time(world, true); + } } #endif diff --git a/vendors/flecs/src/addons/rules/api.c b/vendors/flecs/src/addons/rules/api.c deleted file mode 100644 index 0a63703f4..000000000 --- a/vendors/flecs/src/addons/rules/api.c +++ /dev/null @@ -1,420 +0,0 @@ - /** - * @file addons/rules/api.c - * @brief User facing API for rules. - */ - -#include "rules.h" -#include - -#ifdef FLECS_RULES - -static ecs_mixins_t ecs_rule_t_mixins = { - .type_name = "ecs_rule_t", - .elems = { - [EcsMixinWorld] = offsetof(ecs_rule_t, filter.world), - [EcsMixinEntity] = offsetof(ecs_rule_t, filter.entity), - [EcsMixinIterable] = offsetof(ecs_rule_t, iterable), - [EcsMixinDtor] = offsetof(ecs_rule_t, dtor) - } -}; - -const char* flecs_rule_op_str( - uint16_t kind) -{ - switch(kind) { - case EcsRuleAnd: return "and "; - case EcsRuleAndId: return "andid "; - case EcsRuleAndAny: return "andany "; - case EcsRuleTriv: return "triv "; - case EcsRuleTrivData: return "trivpop "; - case EcsRuleTrivWildcard: return "trivwc "; - case EcsRuleSelectAny: return "any "; - case EcsRuleUp: return "up "; - case EcsRuleUpId: return "upid "; - case EcsRuleSelfUp: return "selfup "; - case EcsRuleSelfUpId: return "selfupid"; - case EcsRuleWith: return "with "; - case EcsRuleTrav: return "trav "; - case EcsRuleIdsRight: return "idsr "; - case EcsRuleIdsLeft: return "idsl "; - case EcsRuleEach: return "each "; - case EcsRuleStore: return "store "; - case EcsRuleReset: return "reset "; - case EcsRuleOr: return "or "; - case EcsRuleOptional: return "option "; - case EcsRuleIf: return "if "; - case EcsRuleEnd: return "end "; - case EcsRuleNot: return "not "; - case EcsRulePredEq: return "eq "; - case EcsRulePredNeq: return "neq "; - case EcsRulePredEqName: return "eq_nm "; - case EcsRulePredNeqName: return "neq_nm "; - case EcsRulePredEqMatch: return "eq_m "; - case EcsRulePredNeqMatch: return "neq_m "; - case EcsRuleLookup: return "lookup "; - case EcsRuleSetVars: return "setvars "; - case EcsRuleSetThis: return "setthis "; - case EcsRuleSetFixed: return "setfix "; - case EcsRuleSetIds: return "setids "; - case EcsRuleSetId: return "setid "; - case EcsRuleContain: return "contain "; - case EcsRulePairEq: return "pair_eq "; - case EcsRulePopulate: return "pop "; - case EcsRulePopulateSelf: return "popself "; - case EcsRuleYield: return "yield "; - case EcsRuleNothing: return "nothing "; - default: return "!invalid"; - } -} - -/* Implementation for iterable mixin */ -static -void flecs_rule_iter_mixin_init( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter) -{ - ecs_poly_assert(poly, ecs_rule_t); - - if (filter) { - iter[1] = ecs_rule_iter(world, ECS_CONST_CAST(ecs_rule_t*, poly)); - iter[0] = ecs_term_chain_iter(&iter[1], filter); - } else { - iter[0] = ecs_rule_iter(world, ECS_CONST_CAST(ecs_rule_t*, poly)); - } -} - -static -void flecs_rule_fini( - ecs_rule_t *rule) -{ - if (rule->vars != &rule->vars_cache.var) { - ecs_os_free(rule->vars); - } - - ecs_os_free(rule->ops); - ecs_os_free(rule->src_vars); - flecs_name_index_fini(&rule->tvar_index); - flecs_name_index_fini(&rule->evar_index); - ecs_filter_fini(&rule->filter); - - ecs_poly_free(rule, ecs_rule_t); -} - -void ecs_rule_fini( - ecs_rule_t *rule) -{ - if (rule->filter.entity) { - /* If filter is associated with entity, use poly dtor path */ - ecs_delete(rule->filter.world, rule->filter.entity); - } else { - flecs_rule_fini(rule); - } -} - -ecs_rule_t* ecs_rule_init( - ecs_world_t *world, - const ecs_filter_desc_t *const_desc) -{ - ecs_rule_t *result = ecs_poly_new(ecs_rule_t); - ecs_stage_t *stage = flecs_stage_from_world(&world); - - /* Initialize the query */ - ecs_filter_desc_t desc = *const_desc; - desc.storage = &result->filter; /* Use storage of rule */ - result->filter = ECS_FILTER_INIT; - if (ecs_filter_init(world, &desc) == NULL) { - goto error; - } - - result->iterable.init = flecs_rule_iter_mixin_init; - - /* Compile filter to operations */ - if (flecs_rule_compile(world, stage, result)) { - goto error; - } - - ecs_entity_t entity = const_desc->entity; - result->dtor = (ecs_poly_dtor_t)flecs_rule_fini; - - if (entity) { - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_rule_t); - poly->poly = result; - ecs_poly_modified(world, entity, ecs_rule_t); - } - - return result; -error: - ecs_rule_fini(result); - return NULL; -} - -static -int32_t flecs_rule_op_ref_str( - const ecs_rule_t *rule, - ecs_rule_ref_t *ref, - ecs_flags16_t flags, - ecs_strbuf_t *buf) -{ - int32_t color_chars = 0; - if (flags & EcsRuleIsVar) { - ecs_assert(ref->var < rule->var_count, ECS_INTERNAL_ERROR, NULL); - ecs_rule_var_t *var = &rule->vars[ref->var]; - ecs_strbuf_appendlit(buf, "#[green]$#[reset]"); - if (var->kind == EcsVarTable) { - ecs_strbuf_appendch(buf, '['); - } - ecs_strbuf_appendlit(buf, "#[green]"); - if (var->name) { - ecs_strbuf_appendstr(buf, var->name); - } else { - if (var->id) { -#ifdef FLECS_DEBUG - if (var->label) { - ecs_strbuf_appendstr(buf, var->label); - ecs_strbuf_appendch(buf, '\''); - } -#endif - ecs_strbuf_append(buf, "%d", var->id); - } else { - ecs_strbuf_appendlit(buf, "this"); - } - } - ecs_strbuf_appendlit(buf, "#[reset]"); - if (var->kind == EcsVarTable) { - ecs_strbuf_appendch(buf, ']'); - } - color_chars = ecs_os_strlen("#[green]#[reset]#[green]#[reset]"); - } else if (flags & EcsRuleIsEntity) { - char *path = ecs_get_fullpath(rule->filter.world, ref->entity); - ecs_strbuf_appendlit(buf, "#[blue]"); - ecs_strbuf_appendstr(buf, path); - ecs_strbuf_appendlit(buf, "#[reset]"); - ecs_os_free(path); - color_chars = ecs_os_strlen("#[blue]#[reset]"); - } - return color_chars; -} - -char* ecs_rule_str_w_profile( - const ecs_rule_t *rule, - const ecs_iter_t *it) -{ - ecs_poly_assert(rule, ecs_rule_t); - - ecs_strbuf_t buf = ECS_STRBUF_INIT; - ecs_rule_op_t *ops = rule->ops; - int32_t i, count = rule->op_count, indent = 0; - for (i = 0; i < count; i ++) { - ecs_rule_op_t *op = &ops[i]; - ecs_flags16_t flags = op->flags; - ecs_flags16_t src_flags = flecs_rule_ref_flags(flags, EcsRuleSrc); - ecs_flags16_t first_flags = flecs_rule_ref_flags(flags, EcsRuleFirst); - ecs_flags16_t second_flags = flecs_rule_ref_flags(flags, EcsRuleSecond); - - if (it) { -#ifdef FLECS_DEBUG - const ecs_rule_iter_t *rit = &it->priv.iter.rule; - ecs_strbuf_append(&buf, - "#[green]%4d -> #[red]%4d <- #[grey] | ", - rit->profile[i].count[0], - rit->profile[i].count[1]); -#endif - } - - ecs_strbuf_append(&buf, - "#[normal]%2d. [#[grey]%2d#[reset], #[green]%2d#[reset]] ", - i, op->prev, op->next); - int32_t hidden_chars, start = ecs_strbuf_written(&buf); - if (op->kind == EcsRuleEnd) { - indent --; - } - - ecs_strbuf_append(&buf, "%*s", indent, ""); - ecs_strbuf_appendstr(&buf, flecs_rule_op_str(op->kind)); - ecs_strbuf_appendstr(&buf, " "); - - int32_t written = ecs_strbuf_written(&buf); - for (int32_t j = 0; j < (12 - (written - start)); j ++) { - ecs_strbuf_appendch(&buf, ' '); - } - - hidden_chars = flecs_rule_op_ref_str(rule, &op->src, src_flags, &buf); - - if (op->kind == EcsRuleNot || - op->kind == EcsRuleOr || - op->kind == EcsRuleOptional || - op->kind == EcsRuleIf) - { - indent ++; - } - - if (!first_flags && !second_flags) { - ecs_strbuf_appendstr(&buf, "\n"); - continue; - } - - written = ecs_strbuf_written(&buf) - hidden_chars; - for (int32_t j = 0; j < (30 - (written - start)); j ++) { - ecs_strbuf_appendch(&buf, ' '); - } - - if (!first_flags && !second_flags) { - ecs_strbuf_appendstr(&buf, "\n"); - continue; - } - - ecs_strbuf_appendstr(&buf, "("); - flecs_rule_op_ref_str(rule, &op->first, first_flags, &buf); - - if (second_flags) { - ecs_strbuf_appendstr(&buf, ", "); - flecs_rule_op_ref_str(rule, &op->second, second_flags, &buf); - } else { - switch (op->kind) { - case EcsRulePredEqName: - case EcsRulePredNeqName: - case EcsRulePredEqMatch: - case EcsRulePredNeqMatch: { - int8_t term_index = op->term_index; - ecs_strbuf_appendstr(&buf, ", #[yellow]\""); - ecs_strbuf_appendstr(&buf, rule->filter.terms[term_index].second.name); - ecs_strbuf_appendstr(&buf, "\"#[reset]"); - break; - } - case EcsRuleLookup: { - ecs_var_id_t src_id = op->src.var; - ecs_strbuf_appendstr(&buf, ", #[yellow]\""); - ecs_strbuf_appendstr(&buf, rule->vars[src_id].lookup); - ecs_strbuf_appendstr(&buf, "\"#[reset]"); - break; - } - default: - break; - } - } - - ecs_strbuf_appendch(&buf, ')'); - - ecs_strbuf_appendch(&buf, '\n'); - } - -#ifdef FLECS_LOG - char *str = ecs_strbuf_get(&buf); - flecs_colorize_buf(str, ecs_os_api.flags_ & EcsOsApiLogWithColors, &buf); - ecs_os_free(str); -#endif - return ecs_strbuf_get(&buf); -} - -char* ecs_rule_str( - const ecs_rule_t *rule) -{ - return ecs_rule_str_w_profile(rule, NULL); -} - -const ecs_filter_t* ecs_rule_get_filter( - const ecs_rule_t *rule) -{ - return &rule->filter; -} - -const char* ecs_rule_parse_vars( - ecs_rule_t *rule, - ecs_iter_t *it, - const char *expr) -{ - ecs_poly_assert(rule, ecs_rule_t); - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(expr != NULL, ECS_INVALID_PARAMETER, NULL) - char token[ECS_MAX_TOKEN_SIZE]; - const char *ptr = expr; - bool paren = false; - - const char *name = NULL; - if (rule->filter.entity) { - name = ecs_get_name(rule->filter.world, rule->filter.entity); - } - - ptr = ecs_parse_ws_eol(ptr); - if (!ptr[0]) { - return ptr; - } - - if (ptr[0] == '(') { - paren = true; - ptr = ecs_parse_ws_eol(ptr + 1); - if (ptr[0] == ')') { - return ptr + 1; - } - } - - do { - ptr = ecs_parse_ws_eol(ptr); - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } - - int var = ecs_rule_find_var(rule, token); - if (var == -1) { - ecs_parser_error(name, expr, (ptr - expr), - "unknown variable '%s'", token); - return NULL; - } - - ptr = ecs_parse_ws_eol(ptr); - if (ptr[0] != ':') { - ecs_parser_error(name, expr, (ptr - expr), - "missing ':'"); - return NULL; - } - - ptr = ecs_parse_ws_eol(ptr + 1); - ptr = ecs_parse_identifier(name, expr, ptr, token); - if (!ptr) { - return NULL; - } - - ecs_entity_t val = ecs_lookup_fullpath(rule->filter.world, token); - if (!val) { - ecs_parser_error(name, expr, (ptr - expr), - "unresolved entity '%s'", token); - return NULL; - } - - ecs_iter_set_var(it, var, val); - - ptr = ecs_parse_ws_eol(ptr); - if (ptr[0] == ')') { - if (!paren) { - ecs_parser_error(name, expr, (ptr - expr), - "unexpected closing parenthesis"); - return NULL; - } - - ptr ++; - break; - } else if (ptr[0] == ',') { - ptr ++; - } else if (!ptr[0]) { - if (paren) { - ecs_parser_error(name, expr, (ptr - expr), - "missing closing parenthesis"); - return NULL; - } - break; - } else { - ecs_parser_error(name, expr, (ptr - expr), - "expected , or end of string"); - return NULL; - } - } while (true); - - return ptr; -error: - return NULL; -} - -#endif diff --git a/vendors/flecs/src/addons/rules/compile.c b/vendors/flecs/src/addons/rules/compile.c deleted file mode 100644 index d78b7df42..000000000 --- a/vendors/flecs/src/addons/rules/compile.c +++ /dev/null @@ -1,2142 +0,0 @@ -/** - * @file addons/rules/compile.c - * @brief Compile rule program from filter. - */ - -#include "rules.h" - -#ifdef FLECS_RULES - -#define FlecsRuleOrMarker ((int16_t)-2) /* Marks instruction in OR chain */ - -ecs_rule_lbl_t flecs_itolbl(int64_t val) { - return flecs_ito(int16_t, val); -} - -static -ecs_var_id_t flecs_itovar(int64_t val) { - return flecs_ito(uint8_t, val); -} - -static -ecs_var_id_t flecs_utovar(uint64_t val) { - return flecs_uto(uint8_t, val); -} - -#ifdef FLECS_DEBUG -#define flecs_set_var_label(var, lbl) (var)->label = lbl -#else -#define flecs_set_var_label(var, lbl) -#endif - -static -bool flecs_rule_is_builtin_pred( - ecs_term_t *term) -{ - if (term->first.flags & EcsIsEntity) { - ecs_entity_t id = term->first.id; - if (id == EcsPredEq || id == EcsPredMatch || id == EcsPredLookup) { - return true; - } - } - return false; -} - -bool flecs_rule_is_written( - ecs_var_id_t var_id, - uint64_t written) -{ - if (var_id == EcsVarNone) { - return true; - } - - ecs_assert(var_id < EcsRuleMaxVarCount, ECS_INTERNAL_ERROR, NULL); - return (written & (1ull << var_id)) != 0; -} - -static -void flecs_rule_write( - ecs_var_id_t var_id, - uint64_t *written) -{ - ecs_assert(var_id < EcsRuleMaxVarCount, ECS_INTERNAL_ERROR, NULL); - *written |= (1ull << var_id); -} - -static -void flecs_rule_write_ctx( - ecs_var_id_t var_id, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) -{ - bool is_written = flecs_rule_is_written(var_id, ctx->written); - flecs_rule_write(var_id, &ctx->written); - if (!is_written) { - if (cond_write) { - flecs_rule_write(var_id, &ctx->cond_written); - } - } -} - -ecs_flags16_t flecs_rule_ref_flags( - ecs_flags16_t flags, - ecs_flags16_t kind) -{ - return (flags >> kind) & (EcsRuleIsVar | EcsRuleIsEntity); -} - -bool flecs_ref_is_written( - const ecs_rule_op_t *op, - const ecs_rule_ref_t *ref, - ecs_flags16_t kind, - uint64_t written) -{ - ecs_flags16_t flags = flecs_rule_ref_flags(op->flags, kind); - if (flags & EcsRuleIsEntity) { - ecs_assert(!(flags & EcsRuleIsVar), ECS_INTERNAL_ERROR, NULL); - if (ref->entity) { - return true; - } - } else if (flags & EcsRuleIsVar) { - return flecs_rule_is_written(ref->var, written); - } - return false; -} - -static -bool flecs_rule_var_is_anonymous( - const ecs_rule_t *rule, - ecs_var_id_t var_id) -{ - ecs_rule_var_t *var = &rule->vars[var_id]; - return var->anonymous; -} - -static -ecs_var_id_t flecs_rule_find_var_id( - const ecs_rule_t *rule, - const char *name, - ecs_var_kind_t kind) -{ - ecs_assert(name != NULL, ECS_INTERNAL_ERROR, NULL); - - /* Backwards compatibility */ - if (!ecs_os_strcmp(name, "This")) { - name = "this"; - } - - if (kind == EcsVarTable) { - if (!ecs_os_strcmp(name, EcsThisName)) { - if (rule->has_table_this) { - return 0; - } else { - return EcsVarNone; - } - } - - if (!flecs_name_index_is_init(&rule->tvar_index)) { - return EcsVarNone; - } - - uint64_t index = flecs_name_index_find( - &rule->tvar_index, name, 0, 0); - if (index == 0) { - return EcsVarNone; - } - return flecs_utovar(index); - } - - if (kind == EcsVarEntity) { - if (!flecs_name_index_is_init(&rule->evar_index)) { - return EcsVarNone; - } - - uint64_t index = flecs_name_index_find( - &rule->evar_index, name, 0, 0); - if (index == 0) { - return EcsVarNone; - } - return flecs_utovar(index); - } - - ecs_assert(kind == EcsVarAny, ECS_INTERNAL_ERROR, NULL); - - /* If searching for any kind of variable, start with most specific */ - ecs_var_id_t index = flecs_rule_find_var_id(rule, name, EcsVarEntity); - if (index != EcsVarNone) { - return index; - } - - return flecs_rule_find_var_id(rule, name, EcsVarTable); -} - -int32_t ecs_rule_var_count( - const ecs_rule_t *rule) -{ - return rule->var_pub_count; -} - -int32_t ecs_rule_find_var( - const ecs_rule_t *rule, - const char *name) -{ - ecs_var_id_t var_id = flecs_rule_find_var_id(rule, name, EcsVarEntity); - if (var_id == EcsVarNone) { - if (rule->filter.flags & EcsFilterMatchThis) { - if (!ecs_os_strcmp(name, "This")) { - name = "this"; - } - if (!ecs_os_strcmp(name, EcsThisName)) { - var_id = 0; - } - } - if (var_id == EcsVarNone) { - return -1; - } - } - return (int32_t)var_id; -} - -const char* ecs_rule_var_name( - const ecs_rule_t *rule, - int32_t var_id) -{ - if (var_id) { - return rule->vars[var_id].name; - } else { - return EcsThisName; - } -} - -bool ecs_rule_var_is_entity( - const ecs_rule_t *rule, - int32_t var_id) -{ - return rule->vars[var_id].kind == EcsVarEntity; -} - -static -const char* flecs_term_id_var_name( - ecs_term_id_t *term_id) -{ - if (!(term_id->flags & EcsIsVariable)) { - return NULL; - } - - if (term_id->id == EcsThis) { - return EcsThisName; - } - - return term_id->name; -} - -static -bool flecs_term_id_is_wildcard( - ecs_term_id_t *term_id) -{ - if ((term_id->flags & EcsIsVariable) && - ((term_id->id == EcsWildcard) || (term_id->id == EcsAny))) - { - return true; - } - return false; -} - -static -ecs_var_id_t flecs_rule_add_var( - ecs_rule_t *rule, - const char *name, - ecs_vec_t *vars, - ecs_var_kind_t kind) -{ - const char *dot = NULL; - if (name) { - dot = strchr(name, '.'); - if (dot) { - kind = EcsVarEntity; /* lookup variables are always entities */ - } - } - - ecs_hashmap_t *var_index = NULL; - ecs_var_id_t var_id = EcsVarNone; - if (name) { - if (kind == EcsVarAny) { - var_id = flecs_rule_find_var_id(rule, name, EcsVarEntity); - if (var_id != EcsVarNone) { - return var_id; - } - - var_id = flecs_rule_find_var_id(rule, name, EcsVarTable); - if (var_id != EcsVarNone) { - return var_id; - } - - kind = EcsVarTable; - } else { - var_id = flecs_rule_find_var_id(rule, name, kind); - if (var_id != EcsVarNone) { - return var_id; - } - } - - if (kind == EcsVarTable) { - var_index = &rule->tvar_index; - } else { - var_index = &rule->evar_index; - } - - /* If we're creating an entity var, check if it has a table variant */ - if (kind == EcsVarEntity && var_id == EcsVarNone) { - var_id = flecs_rule_find_var_id(rule, name, EcsVarTable); - } - } - - ecs_rule_var_t *var; - ecs_var_id_t result; - if (vars) { - var = ecs_vec_append_t(NULL, vars, ecs_rule_var_t); - result = var->id = flecs_itovar(ecs_vec_count(vars)); - } else { - ecs_dbg_assert(rule->var_count < rule->var_size, - ECS_INTERNAL_ERROR, NULL); - var = &rule->vars[rule->var_count]; - result = var->id = flecs_itovar(rule->var_count); - rule->var_count ++; - } - - var->kind = flecs_ito(int8_t, kind); - var->name = name; - var->table_id = var_id; - var->base_id = 0; - var->lookup = NULL; - flecs_set_var_label(var, NULL); - - if (name) { - flecs_name_index_init_if(var_index, NULL); - flecs_name_index_ensure(var_index, var->id, name, 0, 0); - var->anonymous = name[0] == '_'; - - /* Handle variables that require a by-name lookup, e.g. $this.wheel */ - if (dot != NULL) { - ecs_assert(var->table_id == EcsVarNone, ECS_INTERNAL_ERROR, NULL); - var->lookup = dot + 1; - } - } - - return result; -} - -static -ecs_var_id_t flecs_rule_add_var_for_term_id( - ecs_rule_t *rule, - ecs_term_id_t *term_id, - ecs_vec_t *vars, - ecs_var_kind_t kind) -{ - const char *name = flecs_term_id_var_name(term_id); - if (!name) { - return EcsVarNone; - } - - return flecs_rule_add_var(rule, name, vars, kind); -} - -/* This function walks over terms to discover which variables are used in the - * query. It needs to provide the following functionality: - * - create table vars for all variables used as source - * - create entity vars for all variables not used as source - * - create entity vars for all non-$this vars - * - create anonymous vars to store the content of wildcards - * - create anonymous vars to store result of lookups (for $var.child_name) - * - create anonymous vars for resolving component inheritance - * - create array that stores the source variable for each field - * - ensure table vars for non-$this variables are anonymous - * - ensure variables created inside scopes are anonymous - * - place anonymous variables after public variables in vars array - */ -static -int flecs_rule_discover_vars( - ecs_stage_t *stage, - ecs_rule_t *rule) -{ - ecs_vec_t *vars = &stage->variables; /* Buffer to reduce allocs */ - ecs_vec_reset_t(NULL, vars, ecs_rule_var_t); - - ecs_term_t *terms = rule->filter.terms; - int32_t a, i, anonymous_count = 0, count = rule->filter.term_count; - int32_t anonymous_table_count = 0, scope = 0, scoped_var_index = 0; - bool table_this = false, entity_before_table_this = false; - - /* For This table lookups during discovery. This will be overwritten after - * discovery with whether the rule actually has a This table variable. */ - rule->has_table_this = true; - - for (i = 0; i < count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_term_id_t *first = &term->first; - ecs_term_id_t *second = &term->second; - ecs_term_id_t *src = &term->src; - - if (first->id == EcsScopeOpen) { - /* Keep track of which variables are first used in scope, so that we - * can mark them as anonymous. Terms inside a scope are collapsed - * into a single result, which means that outside of the scope the - * value of those variables is undefined. */ - if (!scope) { - scoped_var_index = ecs_vec_count(vars); - } - scope ++; - continue; - } else if (first->id == EcsScopeClose) { - if (!--scope) { - /* Any new variables declared after entering a scope should be - * marked as anonymous. */ - int32_t v; - for (v = scoped_var_index; v < ecs_vec_count(vars); v ++) { - ecs_vec_get_t(vars, ecs_rule_var_t, v)->anonymous = true; - } - } - continue; - } - - ecs_var_id_t first_var_id = flecs_rule_add_var_for_term_id( - rule, first, vars, EcsVarEntity); - if (first_var_id == EcsVarNone) { - /* If first is not a variable, check if we need to insert anonymous - * variable for resolving component inheritance */ - if (term->flags & EcsTermIdInherited) { - anonymous_count += 2; /* table & entity variable */ - } - - /* If first is a wildcard, insert anonymous variable */ - if (flecs_term_id_is_wildcard(first)) { - anonymous_count ++; - } - } - - if ((src->flags & EcsIsVariable) && (src->id != EcsThis)) { - const char *var_name = flecs_term_id_var_name(src); - if (var_name) { - ecs_var_id_t var_id = flecs_rule_find_var_id( - rule, var_name, EcsVarEntity); - if (var_id == EcsVarNone || var_id == first_var_id) { - var_id = flecs_rule_add_var( - rule, var_name, vars, EcsVarEntity); - } - - if (var_id != EcsVarNone) { - /* Mark variable as one for which we need to create a table - * variable. Don't create table variable now, so that we can - * store it in the non-public part of the variable array. */ - ecs_rule_var_t *var = ecs_vec_get_t( - vars, ecs_rule_var_t, (int32_t)var_id - 1); - ecs_assert(var != NULL, ECS_INTERNAL_ERROR, NULL); - if (!var->lookup) { - var->kind = EcsVarAny; - anonymous_table_count ++; - } - - if (!(term->flags & EcsTermNoData)) { - /* Can't have an anonymous variable as source of a term - * that returns a component. We need to return each - * instance of the component, whereas anonymous - * variables are not guaranteed to be resolved to - * individual entities. */ - if (var->anonymous) { - ecs_err( - "can't use anonymous variable '%s' as source of " - "data term", var->name); - goto error; - } - } - - /* Track which variable ids are used as field source */ - if (!rule->src_vars) { - rule->src_vars = ecs_os_calloc_n(ecs_var_id_t, - rule->filter.field_count); - } - - rule->src_vars[term->field_index] = var_id; - } - } else { - if (flecs_term_id_is_wildcard(src)) { - anonymous_count ++; - } - } - } else if ((src->flags & EcsIsVariable) && (src->id == EcsThis)) { - if (flecs_rule_is_builtin_pred(term) && term->oper == EcsOr) { - flecs_rule_add_var(rule, EcsThisName, vars, EcsVarEntity); - } - } - - if (flecs_rule_add_var_for_term_id( - rule, second, vars, EcsVarEntity) == EcsVarNone) - { - /* If second is a wildcard, insert anonymous variable */ - if (flecs_term_id_is_wildcard(second)) { - anonymous_count ++; - } - } - - if (src->flags & EcsIsVariable && second->flags & EcsIsVariable) { - if (term->flags & EcsTermTransitive) { - /* Anonymous variable to store temporary id for finding - * targets for transitive relationship, see compile_term. */ - anonymous_count ++; - } - } - - /* Track if a This entity variable is used before a potential This table - * variable. If this happens, the rule has no This table variable */ - if (src->id == EcsThis) { - table_this = true; - } - if (first->id == EcsThis || second->id == EcsThis) { - if (!table_this) { - entity_before_table_this = true; - } - } - } - - int32_t var_count = ecs_vec_count(vars); - ecs_var_id_t placeholder = EcsVarNone - 1; - bool replace_placeholders = false; - - /* Ensure lookup variables have table and/or entity variables */ - for (i = 0; i < var_count; i ++) { - ecs_rule_var_t *var = ecs_vec_get_t(vars, ecs_rule_var_t, i); - if (var->lookup) { - char *var_name = ecs_os_strdup(var->name); - var_name[var->lookup - var->name - 1] = '\0'; - - ecs_var_id_t base_table_id = flecs_rule_find_var_id( - rule, var_name, EcsVarTable); - if (base_table_id != EcsVarNone) { - var->table_id = base_table_id; - } else if (anonymous_table_count) { - /* Scan for implicit anonymous table variables that haven't been - * inserted yet (happens after this step). Doing this here vs. - * ensures that anonymous variables are appended at the end of - * the variable array, while also ensuring that variable ids are - * stable (no swapping of table var ids that are in use). */ - for (a = 0; a < var_count; a ++) { - ecs_rule_var_t *avar = ecs_vec_get_t( - vars, ecs_rule_var_t, a); - if (avar->kind == EcsVarAny) { - if (!ecs_os_strcmp(avar->name, var_name)) { - base_table_id = (ecs_var_id_t)(a + 1); - break; - } - } - } - if (base_table_id != EcsVarNone) { - /* Set marker so we can set the new table id afterwards */ - var->table_id = placeholder; - replace_placeholders = true; - } - } - - ecs_var_id_t base_entity_id = flecs_rule_find_var_id( - rule, var_name, EcsVarEntity); - if (base_entity_id == EcsVarNone) { - /* Get name from table var (must exist). We can't use allocated - * name since variables don't own names. */ - const char *base_name = NULL; - if (base_table_id) { - ecs_rule_var_t *base_table_var = ecs_vec_get_t( - vars, ecs_rule_var_t, (int32_t)base_table_id - 1); - base_name = base_table_var->name; - } else { - base_name = EcsThisName; - } - - base_entity_id = flecs_rule_add_var( - rule, base_name, vars, EcsVarEntity); - var = ecs_vec_get_t(vars, ecs_rule_var_t, i); - } - - var->base_id = base_entity_id; - - ecs_os_free(var_name); - } - } - var_count = ecs_vec_count(vars); - - /* Add non-This table variables */ - if (anonymous_table_count) { - anonymous_table_count = 0; - for (i = 0; i < var_count; i ++) { - ecs_rule_var_t *var = ecs_vec_get_t(vars, ecs_rule_var_t, i); - if (var->kind == EcsVarAny) { - var->kind = EcsVarEntity; - - ecs_var_id_t var_id = flecs_rule_add_var( - rule, var->name, vars, EcsVarTable); - ecs_vec_get_t(vars, ecs_rule_var_t, i)->table_id = var_id; - anonymous_table_count ++; - } - } - - var_count = ecs_vec_count(vars); - } - - /* If any forward references to newly added anonymous tables exist, replace - * them with the actual table variable ids. */ - if (replace_placeholders) { - for (i = 0; i < var_count; i ++) { - ecs_rule_var_t *var = ecs_vec_get_t(vars, ecs_rule_var_t, i); - if (var->table_id == placeholder) { - char *var_name = ecs_os_strdup(var->name); - var_name[var->lookup - var->name - 1] = '\0'; - - var->table_id = flecs_rule_find_var_id( - rule, var_name, EcsVarTable); - ecs_assert(var->table_id != EcsVarNone, - ECS_INTERNAL_ERROR, NULL); - - ecs_os_free(var_name); - } - } - } - - /* Always include spot for This variable, even if rule doesn't use it */ - var_count ++; - - ecs_rule_var_t *rule_vars = &rule->vars_cache.var; - if ((var_count + anonymous_count) > 1) { - rule_vars = ecs_os_malloc( - (ECS_SIZEOF(ecs_rule_var_t) + ECS_SIZEOF(char*)) * - (var_count + anonymous_count)); - } - - rule->vars = rule_vars; - rule->var_count = var_count; - rule->var_pub_count = var_count; - rule->has_table_this = !entity_before_table_this; - -#ifdef FLECS_DEBUG - rule->var_size = var_count + anonymous_count; -#endif - - char **var_names = ECS_ELEM(rule_vars, ECS_SIZEOF(ecs_rule_var_t), - var_count + anonymous_count); - rule->var_names = (char**)var_names; - - rule_vars[0].kind = EcsVarTable; - rule_vars[0].name = NULL; - flecs_set_var_label(&rule_vars[0], NULL); - rule_vars[0].id = 0; - rule_vars[0].table_id = EcsVarNone; - rule_vars[0].lookup = NULL; - var_names[0] = ECS_CONST_CAST(char*, rule_vars[0].name); - rule_vars ++; - var_names ++; - var_count --; - - if (var_count) { - ecs_rule_var_t *user_vars = ecs_vec_first_t(vars, ecs_rule_var_t); - ecs_os_memcpy_n(rule_vars, user_vars, ecs_rule_var_t, var_count); - for (i = 0; i < var_count; i ++) { - var_names[i] = ECS_CONST_CAST(char*, rule_vars[i].name); - } - } - - /* Hide anonymous table variables from application */ - rule->var_pub_count -= anonymous_table_count; - - /* Sanity check to make sure that the public part of the variable array only - * contains entity variables. */ -#ifdef FLECS_DEBUG - for (i = 1 /* first element = $this */; i < rule->var_pub_count; i ++) { - ecs_assert(rule->vars[i].kind == EcsVarEntity, ECS_INTERNAL_ERROR, NULL); - } -#endif - - return 0; -error: - return -1; -} - -static -ecs_var_id_t flecs_rule_most_specific_var( - ecs_rule_t *rule, - const char *name, - ecs_var_kind_t kind, - ecs_rule_compile_ctx_t *ctx) -{ - if (kind == EcsVarTable || kind == EcsVarEntity) { - return flecs_rule_find_var_id(rule, name, kind); - } - - ecs_var_id_t evar = flecs_rule_find_var_id(rule, name, EcsVarEntity); - if ((evar != EcsVarNone) && flecs_rule_is_written(evar, ctx->written)) { - /* If entity variable is available and written to, it contains the most - * specific result and should be used. */ - return evar; - } - - ecs_var_id_t tvar = flecs_rule_find_var_id(rule, name, EcsVarTable); - if ((tvar != EcsVarNone) && !flecs_rule_is_written(tvar, ctx->written)) { - /* If variable of any kind is requested and variable hasn't been written - * yet, write to table variable */ - return tvar; - } - - /* If table var is written, and entity var doesn't exist or is not written, - * return table var */ - if (tvar != EcsVarNone) { - return tvar; - } else { - return evar; - } -} - -static -ecs_rule_lbl_t flecs_rule_op_insert( - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t *elem = ecs_vec_append_t(NULL, ctx->ops, ecs_rule_op_t); - int32_t count = ecs_vec_count(ctx->ops); - *elem = *op; - if (count > 1) { - if (ctx->cur->lbl_begin == -1) { - /* Variables written by previous instruction can't be written by - * this instruction, except when this is a union. */ - elem->written &= ~elem[-1].written; - } - } - - elem->next = flecs_itolbl(count); - elem->prev = flecs_itolbl(count - 2); - return flecs_itolbl(count - 1); -} - -static -ecs_rule_op_t* flecs_rule_begin_block( - ecs_rule_op_kind_t kind, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t op = {0}; - op.kind = flecs_ito(uint8_t, kind); - ctx->cur->lbl_begin = flecs_rule_op_insert(&op, ctx); - return ecs_vec_get_t(ctx->ops, ecs_rule_op_t, ctx->cur->lbl_begin); -} - -static -void flecs_rule_end_block( - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t new_op = {0}; - new_op.kind = EcsRuleEnd; - ecs_rule_lbl_t end = flecs_rule_op_insert(&new_op, ctx); - - ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - ops[ctx->cur->lbl_begin].next = end; - - ecs_rule_op_t *end_op = &ops[end]; - if (ctx->cur->lbl_query != -1) { - ecs_rule_op_t *query_op = &ops[ctx->cur->lbl_query]; - end_op->prev = ctx->cur->lbl_begin; - end_op->src = query_op->src; - end_op->first = query_op->first; - end_op->second = query_op->second; - end_op->flags = query_op->flags; - end_op->field_index = query_op->field_index; - } else { - end_op->prev = ctx->cur->lbl_begin; - end_op->field_index = -1; - } - - ctx->cur->lbl_begin = -1; -} - -static -void flecs_rule_begin_block_cond_eval( - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx, - ecs_write_flags_t cond_write_state) -{ - ecs_var_id_t first_var = EcsVarNone, second_var = EcsVarNone, src_var = EcsVarNone; - ecs_write_flags_t cond_mask = 0; - - if (flecs_rule_ref_flags(op->flags, EcsRuleFirst) == EcsRuleIsVar) { - first_var = op->first.var; - cond_mask |= (1ull << first_var); - } - if (flecs_rule_ref_flags(op->flags, EcsRuleSecond) == EcsRuleIsVar) { - second_var = op->second.var; - cond_mask |= (1ull << second_var); - } - if (flecs_rule_ref_flags(op->flags, EcsRuleSrc) == EcsRuleIsVar) { - src_var = op->src.var; - cond_mask |= (1ull << src_var); - } - - /* Variables set in an OR chain are marked as conditional writes. However, - * writes from previous terms in the current OR chain shouldn't be treated - * as variables that are conditionally set, so instead use the write mask - * from before the chain started. */ - if (ctx->ctrlflow->in_or) { - cond_write_state = ctx->ctrlflow->cond_written_or; - } - - /* If this term uses conditionally set variables, insert instruction that - * jumps over the term if the variables weren't set yet. */ - if (cond_mask & cond_write_state) { - ctx->cur->lbl_cond_eval = flecs_itolbl(ecs_vec_count(ctx->ops)); - - ecs_rule_op_t jmp_op = {0}; - jmp_op.kind = EcsRuleIf; - - if ((first_var != EcsVarNone) && cond_write_state & (1ull << first_var)) { - jmp_op.flags |= (EcsRuleIsVar << EcsRuleFirst); - jmp_op.first.var = first_var; - } - if ((second_var != EcsVarNone) && cond_write_state & (1ull << second_var)) { - jmp_op.flags |= (EcsRuleIsVar << EcsRuleSecond); - jmp_op.second.var = second_var; - } - if ((src_var != EcsVarNone) && cond_write_state & (1ull << src_var)) { - jmp_op.flags |= (EcsRuleIsVar << EcsRuleSrc); - jmp_op.src.var = src_var; - } - - flecs_rule_op_insert(&jmp_op, ctx); - } else { - ctx->cur->lbl_cond_eval = -1; - } -} - -static -void flecs_rule_end_block_cond_eval( - ecs_rule_compile_ctx_t *ctx) -{ - if (ctx->cur->lbl_cond_eval == -1) { - return; - } - - ecs_assert(ctx->cur->lbl_query >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_rule_op_t end_op = {0}; - end_op.kind = EcsRuleEnd; - ecs_rule_lbl_t end = flecs_rule_op_insert(&end_op, ctx); - - ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - ops[ctx->cur->lbl_cond_eval].next = end; - - ecs_rule_op_t *end_op_ptr = &ops[end]; - ecs_rule_op_t *query_op = &ops[ctx->cur->lbl_query]; - end_op_ptr->prev = ctx->cur->lbl_cond_eval; - end_op_ptr->src = query_op->src; - end_op_ptr->first = query_op->first; - end_op_ptr->second = query_op->second; - end_op_ptr->flags = query_op->flags; - end_op_ptr->field_index = query_op->field_index; -} - -static -void flecs_rule_begin_block_or( - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t *or_op = flecs_rule_begin_block(EcsRuleNot, ctx); - or_op->kind = EcsRuleOr; - if (op->flags & (EcsRuleIsVar << EcsRuleSrc)) { - or_op->flags = (EcsRuleIsVar << EcsRuleSrc); - or_op->src = op->src; - } -} - -static -void flecs_rule_end_block_or( - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t op = {0}; - op.kind = EcsRuleEnd; - ecs_rule_lbl_t end = flecs_rule_op_insert(&op, ctx); - - ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - int32_t i, prev_or = -2; - for (i = ctx->cur->lbl_begin + 1; i < end; i ++) { - if (ops[i].next == FlecsRuleOrMarker) { - if (prev_or != -2) { - ops[prev_or].prev = flecs_itolbl(i); - } - ops[i].next = flecs_itolbl(end); - prev_or = i; - } - } - - ops[ctx->cur->lbl_begin].next = flecs_itolbl(end); - ops[end].prev = ctx->cur->lbl_begin; - ops[end - 1].prev = ctx->cur->lbl_begin; - - /* Scan which variables were conditionally written in the OR chain and - * reset instructions after the OR chain. If a variable is set in part one - * of a chain but not part two, there would be nothing writing to the - * variable in part two, leaving it to the previous value. To address this - * a reset is inserted that resets the variable value on redo. */ - for (i = 1; i < (8 * ECS_SIZEOF(ecs_write_flags_t)); i ++) { - ecs_write_flags_t prev = 1 & (ctx->ctrlflow->cond_written_or >> i); - ecs_write_flags_t cur = 1 & (ctx->cond_written >> i); - - if (!prev && cur) { - ecs_rule_op_t reset_op = {0}; - reset_op.kind = EcsRuleReset; - reset_op.flags |= (EcsRuleIsVar << EcsRuleSrc); - reset_op.src.var = flecs_itovar(i); - flecs_rule_op_insert(&reset_op, ctx); - } - } - - ctx->ctrlflow->in_or = false; - ctx->cur->lbl_begin = -1; -} - -static -void flecs_rule_insert_each( - ecs_var_id_t tvar, - ecs_var_id_t evar, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) -{ - ecs_rule_op_t each = {0}; - each.kind = EcsRuleEach; - each.src.var = evar; - each.first.var = tvar; - each.flags = (EcsRuleIsVar << EcsRuleSrc) | - (EcsRuleIsVar << EcsRuleFirst); - flecs_rule_write_ctx(evar, ctx, cond_write); - flecs_rule_write(evar, &each.written); - flecs_rule_op_insert(&each, ctx); -} - -static -void flecs_rule_insert_lookup( - ecs_var_id_t base_var, - ecs_var_id_t evar, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) -{ - ecs_rule_op_t lookup = {0}; - lookup.kind = EcsRuleLookup; - lookup.src.var = evar; - lookup.first.var = base_var; - lookup.flags = (EcsRuleIsVar << EcsRuleSrc) | - (EcsRuleIsVar << EcsRuleFirst); - flecs_rule_write_ctx(evar, ctx, cond_write); - flecs_rule_write(evar, &lookup.written); - flecs_rule_op_insert(&lookup, ctx); -} - -static -void flecs_rule_insert_unconstrained_transitive( - ecs_rule_t *rule, - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) -{ - /* Create anonymous variable to store the target ids. This will return the - * list of targets without constraining the variable of the term, which - * needs to stay variable to find all transitive relationships for a src. */ - ecs_var_id_t tgt = flecs_rule_add_var(rule, NULL, NULL, EcsVarEntity); - flecs_set_var_label(&rule->vars[tgt], rule->vars[op->second.var].name); - - /* First, find ids to start traversal from. This fixes op.second. */ - ecs_rule_op_t find_ids = {0}; - find_ids.kind = EcsRuleIdsRight; - find_ids.field_index = -1; - find_ids.first = op->first; - find_ids.second = op->second; - find_ids.flags = op->flags; - find_ids.flags &= (ecs_flags8_t)~((EcsRuleIsVar|EcsRuleIsEntity) << EcsRuleSrc); - find_ids.second.var = tgt; - flecs_rule_write_ctx(tgt, ctx, cond_write); - flecs_rule_write(tgt, &find_ids.written); - flecs_rule_op_insert(&find_ids, ctx); - - /* Next, iterate all tables for the ids. This fixes op.src */ - ecs_rule_op_t and_op = {0}; - and_op.kind = EcsRuleAnd; - and_op.field_index = op->field_index; - and_op.first = op->first; - and_op.second = op->second; - and_op.src = op->src; - and_op.flags = op->flags | EcsRuleIsSelf; - and_op.second.var = tgt; - flecs_rule_write_ctx(and_op.src.var, ctx, cond_write); - flecs_rule_write(and_op.src.var, &and_op.written); - flecs_rule_op_insert(&and_op, ctx); -} - -static -void flecs_rule_insert_inheritance( - ecs_rule_t *rule, - ecs_term_t *term, - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) -{ - /* Anonymous variable to store the resolved component ids */ - ecs_var_id_t tvar = flecs_rule_add_var(rule, NULL, NULL, EcsVarTable); - ecs_var_id_t evar = flecs_rule_add_var(rule, NULL, NULL, EcsVarEntity); - flecs_set_var_label(&rule->vars[tvar], ecs_get_name(rule->filter.world, term->first.id)); - flecs_set_var_label(&rule->vars[evar], ecs_get_name(rule->filter.world, term->first.id)); - - ecs_rule_op_t trav_op = {0}; - trav_op.kind = EcsRuleTrav; - trav_op.field_index = -1; - trav_op.first.entity = term->first.trav; - trav_op.second.entity = term->first.id; - trav_op.src.var = tvar; - trav_op.flags = EcsRuleIsSelf; - trav_op.flags |= (EcsRuleIsEntity << EcsRuleFirst); - trav_op.flags |= (EcsRuleIsEntity << EcsRuleSecond); - trav_op.flags |= (EcsRuleIsVar << EcsRuleSrc); - trav_op.written |= (1ull << tvar); - if (term->first.flags & EcsSelf) { - trav_op.match_flags |= EcsTermReflexive; - } - flecs_rule_op_insert(&trav_op, ctx); - flecs_rule_insert_each(tvar, evar, ctx, cond_write); - - ecs_rule_ref_t r = { .var = evar }; - op->first = r; - op->flags &= (ecs_flags8_t)~(EcsRuleIsEntity << EcsRuleFirst); - op->flags |= (EcsRuleIsVar << EcsRuleFirst); -} - -static -void flecs_rule_compile_term_id( - ecs_world_t *world, - ecs_rule_t *rule, - ecs_rule_op_t *op, - ecs_term_id_t *term_id, - ecs_rule_ref_t *ref, - ecs_flags8_t ref_kind, - ecs_var_kind_t kind, - ecs_rule_compile_ctx_t *ctx, - bool create_wildcard_vars) -{ - (void)world; - - if (!ecs_term_id_is_set(term_id)) { - return; - } - - if (term_id->flags & EcsIsVariable) { - op->flags |= (ecs_flags8_t)(EcsRuleIsVar << ref_kind); - const char *name = flecs_term_id_var_name(term_id); - if (name) { - ref->var = flecs_rule_most_specific_var(rule, name, kind, ctx); - ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); - } else if (create_wildcard_vars) { - bool is_wildcard = flecs_term_id_is_wildcard(term_id); - if (is_wildcard && (kind == EcsVarAny)) { - ref->var = flecs_rule_add_var(rule, NULL, NULL, EcsVarTable); - } else { - ref->var = flecs_rule_add_var(rule, NULL, NULL, EcsVarEntity); - } - if (is_wildcard) { - flecs_set_var_label(&rule->vars[ref->var], - ecs_get_name(world, term_id->id)); - } - ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); - } - } - - if (term_id->flags & EcsIsEntity) { - op->flags |= (ecs_flags8_t)(EcsRuleIsEntity << ref_kind); - ref->entity = term_id->id; - } -} - -static -int flecs_rule_compile_ensure_vars( - ecs_rule_t *rule, - ecs_rule_op_t *op, - ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind, - ecs_rule_compile_ctx_t *ctx, - bool cond_write, - bool *written_out) -{ - ecs_flags16_t flags = flecs_rule_ref_flags(op->flags, ref_kind); - bool written = false; - - if (flags & EcsRuleIsVar) { - ecs_var_id_t var_id = ref->var; - ecs_rule_var_t *var = &rule->vars[var_id]; - - if (var->kind == EcsVarEntity && - !flecs_rule_is_written(var_id, ctx->written)) - { - /* If entity variable is not yet written but a table variant exists - * that has been written, insert each operation to translate from - * entity variable to table */ - ecs_var_id_t tvar = var->table_id; - if ((tvar != EcsVarNone) && - flecs_rule_is_written(tvar, ctx->written)) - { - if (var->lookup) { - if (!flecs_rule_is_written(tvar, ctx->written)) { - ecs_err("dependent variable of '$%s' is not written", - var->name); - return -1; - } - - if (!flecs_rule_is_written(var->base_id, ctx->written)) { - flecs_rule_insert_each( - tvar, var->base_id, ctx, cond_write); - } - } else { - flecs_rule_insert_each(tvar, var_id, ctx, cond_write); - } - - /* Variable was written, just not as entity */ - written = true; - } else if (var->lookup) { - if (!flecs_rule_is_written(var->base_id, ctx->written)) { - ecs_err("dependent variable of '$%s' is not written", - var->name); - return -1; - } - } - } - - written |= flecs_rule_is_written(var_id, ctx->written); - - /* After evaluating a term, a used variable is always written */ - flecs_rule_write(var_id, &op->written); - flecs_rule_write_ctx(var_id, ctx, cond_write); - } else { - /* If it's not a variable, it's always written */ - written = true; - } - - if (written_out) { - *written_out = written; - } - - return 0; -} - -static -bool flecs_rule_compile_lookup( - ecs_rule_t *rule, - ecs_var_id_t var_id, - ecs_rule_compile_ctx_t *ctx, - bool cond_write) -{ - ecs_rule_var_t *var = &rule->vars[var_id]; - if (var->lookup) { - flecs_rule_insert_lookup(var->base_id, var_id, ctx, cond_write); - return true; - } else { - return false; - } -} - -static -void flecs_rule_insert_contains( - ecs_rule_t *rule, - ecs_var_id_t src_var, - ecs_var_id_t other_var, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t contains = {0}; - if ((src_var != other_var) && (src_var == rule->vars[other_var].table_id)) { - contains.kind = EcsRuleContain; - contains.src.var = src_var; - contains.first.var = other_var; - contains.flags |= (EcsRuleIsVar << EcsRuleSrc) | - (EcsRuleIsVar << EcsRuleFirst); - flecs_rule_op_insert(&contains, ctx); - } -} - -static -void flecs_rule_insert_pair_eq( - int32_t field_index, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t contains = {0}; - contains.kind = EcsRulePairEq; - contains.field_index = flecs_ito(int8_t, field_index); - flecs_rule_op_insert(&contains, ctx); -} - -static -bool flecs_rule_term_fixed_id( - ecs_filter_t *filter, - ecs_term_t *term) -{ - /* Transitive/inherited terms have variable ids */ - if (term->flags & (EcsTermTransitive|EcsTermIdInherited)) { - return false; - } - - /* Or terms can match different ids */ - if (term->oper == EcsOr) { - return false; - } - if ((term != filter->terms) && term[-1].oper == EcsOr) { - return false; - } - - /* Wildcards can assume different ids */ - if (ecs_id_is_wildcard(term->id)) { - return false; - } - - /* Any terms can have fixed ids, but they require special handling */ - if (term->flags & (EcsTermMatchAny|EcsTermMatchAnySrc)) { - return false; - } - - /* First terms that are Not or Optional require special handling */ - if (term->oper == EcsNot || term->oper == EcsOptional) { - if (term == filter->terms) { - return false; - } - } - - return true; -} - -static -int flecs_rule_compile_builtin_pred( - ecs_term_t *term, - ecs_rule_op_t *op, - ecs_write_flags_t write_state) -{ - ecs_entity_t id = term->first.id; - - ecs_rule_op_kind_t eq[] = {EcsRulePredEq, EcsRulePredNeq}; - ecs_rule_op_kind_t eq_name[] = {EcsRulePredEqName, EcsRulePredNeqName}; - ecs_rule_op_kind_t eq_match[] = {EcsRulePredEqMatch, EcsRulePredNeqMatch}; - - ecs_flags16_t flags_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); - - if (id == EcsPredEq) { - if (term->second.flags & EcsIsName) { - op->kind = flecs_ito(uint8_t, eq_name[term->oper == EcsNot]); - } else { - op->kind = flecs_ito(uint8_t, eq[term->oper == EcsNot]); - } - } else if (id == EcsPredMatch) { - op->kind = flecs_ito(uint8_t, eq_match[term->oper == EcsNot]); - } - - op->first = op->src; - op->src = (ecs_rule_ref_t){0}; - op->flags &= (ecs_flags8_t)~((EcsRuleIsEntity|EcsRuleIsVar) << EcsRuleSrc); - op->flags &= (ecs_flags8_t)~((EcsRuleIsEntity|EcsRuleIsVar) << EcsRuleFirst); - op->flags |= EcsRuleIsVar << EcsRuleFirst; - - if (flags_2nd & EcsRuleIsVar) { - if (!(write_state & (1ull << op->second.var))) { - ecs_err("uninitialized variable '%s' on right-hand side of " - "equality operator", term->second.name); - return -1; - } - } - - return 0; -} - -static -int flecs_rule_ensure_scope_var( - ecs_rule_t *rule, - ecs_rule_op_t *op, - ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_var_id_t var = ref->var; - - if (rule->vars[var].kind == EcsVarEntity && - !flecs_rule_is_written(var, ctx->written)) - { - ecs_var_id_t table_var = rule->vars[var].table_id; - if (table_var != EcsVarNone && - flecs_rule_is_written(table_var, ctx->written)) - { - if (flecs_rule_compile_ensure_vars( - rule, op, ref, ref_kind, ctx, false, NULL)) - { - goto error; - } - - } - } - - return 0; -error: - return -1; -} - -static -int flecs_rule_ensure_scope_vars( - ecs_world_t *world, - ecs_rule_t *rule, - ecs_rule_compile_ctx_t *ctx, - ecs_term_t *term) -{ - /* If the scope uses variables as entity that have only been written as - * table, resolve them as entities before entering the scope. */ - ecs_term_t *cur = term; - while(cur->first.id != EcsScopeClose) { - /* Dummy operation to obtain variable information for term */ - ecs_rule_op_t op = {0}; - flecs_rule_compile_term_id(world, rule, &op, &cur->first, - &op.first, EcsRuleFirst, EcsVarEntity, ctx, false); - flecs_rule_compile_term_id(world, rule, &op, &cur->second, - &op.second, EcsRuleSecond, EcsVarEntity, ctx, false); - - if (op.flags & (EcsRuleIsVar << EcsRuleFirst)) { - if (flecs_rule_ensure_scope_var( - rule, &op, &op.first, EcsRuleFirst, ctx)) - { - goto error; - } - } - if (op.flags & (EcsRuleIsVar << EcsRuleSecond)) { - if (flecs_rule_ensure_scope_var( - rule, &op, &op.second, EcsRuleSecond, ctx)) - { - goto error; - } - } - - cur ++; - } - - return 0; -error: - return -1; -} - -static -void flecs_rule_compile_push( - ecs_rule_compile_ctx_t *ctx) -{ - ctx->cur = &ctx->ctrlflow[++ ctx->scope]; - ctx->cur->lbl_begin = -1; - ctx->cur->lbl_begin = -1; -} - -static -void flecs_rule_compile_pop( - ecs_rule_compile_ctx_t *ctx) -{ - ctx->cur = &ctx->ctrlflow[-- ctx->scope]; -} - -static -bool flecs_rule_term_is_or( - const ecs_filter_t *filter, - const ecs_term_t *term) -{ - bool first_term = term == filter->terms; - return (term->oper == EcsOr) || (!first_term && term[-1].oper == EcsOr); -} - -static -int flecs_rule_compile_term( - ecs_world_t *world, - ecs_rule_t *rule, - ecs_term_t *term, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_filter_t *filter = &rule->filter; - bool first_term = term == filter->terms; - bool first_is_var = term->first.flags & EcsIsVariable; - bool second_is_var = term->second.flags & EcsIsVariable; - bool src_is_var = term->src.flags & EcsIsVariable; - bool builtin_pred = flecs_rule_is_builtin_pred(term); - bool is_not = (term->oper == EcsNot) && !builtin_pred; - bool is_or = flecs_rule_term_is_or(filter, term); - bool first_or = false, last_or = false; - bool cond_write = term->oper == EcsOptional || is_or; - ecs_rule_op_t op = {0}; - - if (is_or) { - first_or = first_term || (term[-1].oper != EcsOr); - last_or = term->oper != EcsOr; - } - - /* !_ (don't match anything) terms always return nothing. */ - if (is_not && term->id == EcsAny) { - op.kind = EcsRuleNothing; - flecs_rule_op_insert(&op, ctx); - return 0; - } - - if (is_or && (first_term || term[-1].oper != EcsOr)) { - ctx->ctrlflow->cond_written_or = ctx->cond_written; - ctx->ctrlflow->in_or = true; - } - - if (!term->src.id && term->src.flags & EcsIsEntity) { - /* If the term has a 0 source, check if it's a scope open/close */ - if (term->first.id == EcsScopeOpen) { - if (flecs_rule_ensure_scope_vars(world, rule, ctx, term)) { - goto error; - } - if (term->oper == EcsNot) { - ctx->scope_is_not |= (ecs_flags32_t)(1ull << ctx->scope); - flecs_rule_begin_block(EcsRuleNot, ctx); - } else { - ctx->scope_is_not &= (ecs_flags32_t)~(1ull << ctx->scope); - } - flecs_rule_compile_push(ctx); - } else if (term->first.id == EcsScopeClose) { - flecs_rule_compile_pop(ctx); - if (ctx->scope_is_not & (ecs_flags32_t)(1ull << (ctx->scope))) { - ctx->cur->lbl_query = -1; - flecs_rule_end_block(ctx); - } - } else { - /* Noop */ - } - return 0; - } - - if (builtin_pred) { - if (term->second.id == EcsWildcard || term->second.id == EcsAny) { - /* Noop */ - return 0; - } - } - - /* Default instruction for And operators. If the source is fixed (like for - * singletons or terms with an entity source), use With, which like And but - * just matches against a source (vs. finding a source). */ - op.kind = src_is_var ? EcsRuleAnd : EcsRuleWith; - op.field_index = flecs_ito(int8_t, term->field_index); - op.term_index = flecs_ito(int8_t, term - filter->terms); - - /* If rule is transitive, use Trav(ersal) instruction */ - if (term->flags & EcsTermTransitive) { - ecs_assert(ecs_term_id_is_set(&term->second), ECS_INTERNAL_ERROR, NULL); - op.kind = EcsRuleTrav; - } else { - /* Ignore cascade & parent flags */ - ecs_flags32_t trav_flags = EcsTraverseFlags & ~(EcsCascade|EcsParent); - if (term->flags & (EcsTermMatchAny|EcsTermMatchAnySrc)) { - op.kind = EcsRuleAndAny; - } else if ((term->src.flags & trav_flags) == EcsUp) { - op.kind = EcsRuleUp; - } else if ((term->src.flags & trav_flags) == (EcsSelf|EcsUp)) { - op.kind = EcsRuleSelfUp; - } - } - - /* If term has fixed id, insert simpler instruction that skips dealing with - * wildcard terms and variables */ - if (flecs_rule_term_fixed_id(filter, term)) { - if (op.kind == EcsRuleAnd) { - op.kind = EcsRuleAndId; - } else if (op.kind == EcsRuleSelfUp) { - op.kind = EcsRuleSelfUpId; - } else if (op.kind == EcsRuleUp) { - op.kind = EcsRuleUpId; - } - } - - /* Save write state at start of term so we can use it to reliably track - * variables got written by this term. */ - ecs_write_flags_t cond_write_state = ctx->cond_written; - ecs_write_flags_t write_state = ctx->written; - - /* Resolve component inheritance if necessary */ - ecs_var_id_t first_var = EcsVarNone, second_var = EcsVarNone, src_var = EcsVarNone; - - /* Resolve variables and entities for operation arguments */ - flecs_rule_compile_term_id(world, rule, &op, &term->first, - &op.first, EcsRuleFirst, EcsVarEntity, ctx, true); - flecs_rule_compile_term_id(world, rule, &op, &term->second, - &op.second, EcsRuleSecond, EcsVarEntity, ctx, true); - - if (first_is_var) first_var = op.first.var; - if (second_is_var) second_var = op.second.var; - - flecs_rule_compile_term_id(world, rule, &op, &term->src, - &op.src, EcsRuleSrc, EcsVarAny, ctx, true); - if (src_is_var) src_var = op.src.var; - bool src_written = flecs_rule_is_written(src_var, ctx->written); - - /* Insert each instructions for table -> entity variable if needed */ - bool first_written, second_written; - if (flecs_rule_compile_ensure_vars( - rule, &op, &op.first, EcsRuleFirst, ctx, cond_write, &first_written)) - { - goto error; - } - if (flecs_rule_compile_ensure_vars( - rule, &op, &op.second, EcsRuleSecond, ctx, cond_write, &second_written)) - { - goto error; - } - - /* If the query starts with a Not or Optional term, insert an operation that - * matches all entities. */ - if (first_term && src_is_var && !src_written) { - bool pred_match = builtin_pred && term->first.id == EcsPredMatch; - if (term->oper == EcsNot || term->oper == EcsOptional || pred_match) { - ecs_rule_op_t match_any = {0}; - match_any.kind = EcsAnd; - match_any.flags = EcsRuleIsSelf | (EcsRuleIsEntity << EcsRuleFirst); - match_any.flags |= (EcsRuleIsVar << EcsRuleSrc); - match_any.src = op.src; - match_any.field_index = -1; - if (!pred_match) { - match_any.first.entity = EcsAny; - } else { - /* If matching by name, instead of finding all tables, just find - * the ones with a name. */ - match_any.first.entity = ecs_id(EcsIdentifier); - match_any.second.entity = EcsName; - match_any.flags |= (EcsRuleIsEntity << EcsRuleSecond); - } - match_any.written = (1ull << src_var); - flecs_rule_op_insert(&match_any, ctx); - flecs_rule_write_ctx(op.src.var, ctx, false); - - /* Update write administration */ - src_written = true; - } - } - - /* A bit of special logic for OR expressions and equality predicates. If the - * left-hand of an equality operator is a table, and there are multiple - * operators in an Or expression, the Or chain should match all entities in - * the table that match the right hand sides of the operator expressions. - * For this to work, the src variable needs to be resolved as entity, as an - * Or chain would otherwise only yield the first match from a table. */ - if (src_is_var && src_written && builtin_pred && term->oper == EcsOr) { - /* Or terms are required to have the same source, so we don't have to - * worry about the last term in the chain. */ - if (rule->vars[src_var].kind == EcsVarTable) { - flecs_rule_compile_term_id(world, rule, &op, &term->src, - &op.src, EcsRuleSrc, EcsVarEntity, ctx, true); - src_var = op.src.var; - } - } - - if (flecs_rule_compile_ensure_vars( - rule, &op, &op.src, EcsRuleSrc, ctx, cond_write, NULL)) - { - goto error; - } - - /* If source is Any (_) and first and/or second are unconstrained, insert an - * ids instruction instead of an And */ - if (term->flags & EcsTermMatchAnySrc) { - /* Use up-to-date written values after potentially inserting each */ - if (!first_written || !second_written) { - if (!first_written) { - /* If first is unknown, traverse left: <- (*, t) */ - op.kind = EcsRuleIdsLeft; - } else { - /* If second is wildcard, traverse right: (r, *) -> */ - op.kind = EcsRuleIdsRight; - } - op.src.entity = 0; - op.flags &= (ecs_flags8_t)~(EcsRuleIsVar << EcsRuleSrc); /* ids has no src */ - op.flags &= (ecs_flags8_t)~(EcsRuleIsEntity << EcsRuleSrc); - } - - /* If source variable is not written and we're querying just for Any, insert - * a dedicated instruction that uses the Any record in the id index. Any - * queries that are evaluated against written sources can use Wildcard - * records, which is what the AndAny instruction does. */ - } else if (!src_written && term->id == EcsAny && op.kind == EcsRuleAndAny) { - /* Lookup variables ($var.child_name) are always written */ - if (!rule->vars[src_var].lookup) { - op.kind = EcsRuleSelectAny; /* Uses Any (_) id record */ - } - } - - /* If this is a transitive term and both the target and source are unknown, - * find the targets for the relationship first. This clusters together - * tables for the same target, which allows for more efficient usage of the - * traversal caches. */ - if (term->flags & EcsTermTransitive && src_is_var && second_is_var) { - if (!src_written && !second_written) { - flecs_rule_insert_unconstrained_transitive( - rule, &op, ctx, cond_write); - } - } - - /* Check if this term has variables that have been conditionally written, - * like variables written by an optional term. */ - if (ctx->cond_written) { - if (!is_or || first_or) { - flecs_rule_begin_block_cond_eval(&op, ctx, cond_write_state); - } - } - - /* Handle Not, Optional, Or operators */ - if (is_not) { - flecs_rule_begin_block(EcsRuleNot, ctx); - } else if (term->oper == EcsOptional) { - flecs_rule_begin_block(EcsRuleOptional, ctx); - } else if (first_or) { - flecs_rule_begin_block_or(&op, ctx); - } - - /* If term has component inheritance enabled, insert instruction to walk - * down the relationship tree of the id. */ - if (term->flags & EcsTermIdInherited) { - flecs_rule_insert_inheritance(rule, term, &op, ctx, cond_write); - } - - op.match_flags = term->flags; - - if (first_is_var) { - op.first.var = first_var; - op.flags &= (ecs_flags8_t)~(EcsRuleIsEntity << EcsRuleFirst); - op.flags |= (EcsRuleIsVar << EcsRuleFirst); - } - - if (term->src.flags & EcsSelf) { - op.flags |= EcsRuleIsSelf; - } - - /* Insert instructions for lookup variables */ - if (first_is_var) { - if (flecs_rule_compile_lookup(rule, first_var, ctx, cond_write)) { - write_state |= (1ull << first_var); // lookups are resolved inline - } - } - if (src_is_var) { - if (flecs_rule_compile_lookup(rule, src_var, ctx, cond_write)) { - write_state |= (1ull << src_var); // lookups are resolved inline - } - } - if (second_is_var) { - if (flecs_rule_compile_lookup(rule, second_var, ctx, cond_write)) { - write_state |= (1ull << second_var); // lookups are resolved inline - } - } - - if (builtin_pred) { - if (flecs_rule_compile_builtin_pred(term, &op, write_state)) { - goto error; - } - } - - /* If we're writing the $this variable, filter out disabled/prefab entities - * unless the filter explicitly matches them. - * This could've been done with regular With instructions, but since - * filtering out disabled/prefab entities is the default and this check is - * cheap to perform on table flags, it's worth special casing. */ - if (!src_written && src_var == 0) { - ecs_flags32_t filter_flags = filter->flags; - if (!(filter_flags & EcsFilterMatchDisabled) || - !(filter_flags & EcsFilterMatchPrefab)) - { - ecs_flags32_t table_flags = 0; - if (!(filter_flags & EcsFilterMatchDisabled)) { - table_flags |= EcsTableIsDisabled; - } - if (!(filter_flags & EcsFilterMatchPrefab)) { - table_flags |= EcsTableIsPrefab; - } - - op.other = flecs_itolbl(table_flags); - } - } - - flecs_rule_op_insert(&op, ctx); - ctx->cur->lbl_query = flecs_itolbl(ecs_vec_count(ctx->ops) - 1); - if (is_or) { - ecs_rule_op_t *op_ptr = ecs_vec_get_t(ctx->ops, ecs_rule_op_t, - ctx->cur->lbl_query); - op_ptr->next = FlecsRuleOrMarker; - } - - /* Handle self-references between src and first/second variables */ - if (src_is_var) { - if (first_is_var) { - flecs_rule_insert_contains(rule, src_var, first_var, ctx); - } - if (second_is_var && first_var != second_var) { - flecs_rule_insert_contains(rule, src_var, second_var, ctx); - } - } - - /* Handle self references between first and second variables */ - if (first_is_var && !first_written && (first_var == second_var)) { - flecs_rule_insert_pair_eq(term->field_index, ctx); - } - - /* Handle closing of Not, Optional and Or operators */ - if (is_not) { - flecs_rule_end_block(ctx); - } else if (term->oper == EcsOptional) { - flecs_rule_end_block(ctx); - } else if (last_or) { - flecs_rule_end_block_or(ctx); - } - - /* Handle closing of conditional evaluation */ - if (ctx->cond_written && (first_is_var || second_is_var || src_is_var)) { - if (!is_or || last_or) { - flecs_rule_end_block_cond_eval(ctx); - } - } - - /* Ensure that term id is set after evaluating Not */ - if (term->flags & EcsTermIdInherited) { - if (is_not) { - ecs_rule_op_t set_id = {0}; - set_id.kind = EcsRuleSetId; - set_id.first.entity = term->id; - set_id.flags = (EcsRuleIsEntity << EcsRuleFirst); - set_id.field_index = flecs_ito(int8_t, term->field_index); - flecs_rule_op_insert(&set_id, ctx); - } - } - - return 0; -error: - return -1; -} - -static -bool flecs_rule_var_is_unknown( - ecs_rule_t *rule, - ecs_var_id_t var_id, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_var_t *vars = rule->vars; - if (ctx->written & (1ull << var_id)) { - return false; - } else { - ecs_var_id_t table_var = vars[var_id].table_id; - if (table_var != EcsVarNone) { - return flecs_rule_var_is_unknown(rule, table_var, ctx); - } - } - return true; -} - -/* Returns whether term is unkown. A term is unknown when it has variable - * elements (first, second, src) that are all unknown. */ -static -bool flecs_rule_term_is_unknown( - ecs_rule_t *rule, - ecs_term_t *term, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t dummy = {0}; - flecs_rule_compile_term_id(NULL, rule, &dummy, &term->first, - &dummy.first, EcsRuleFirst, EcsVarEntity, ctx, false); - flecs_rule_compile_term_id(NULL, rule, &dummy, &term->second, - &dummy.second, EcsRuleSecond, EcsVarEntity, ctx, false); - flecs_rule_compile_term_id(NULL, rule, &dummy, &term->src, - &dummy.src, EcsRuleSrc, EcsVarAny, ctx, false); - - bool has_vars = dummy.flags & - ((EcsRuleIsVar << EcsRuleFirst) | - (EcsRuleIsVar << EcsRuleSecond) | - (EcsRuleIsVar << EcsRuleSrc)); - if (!has_vars) { - /* If term has no variables (typically terms with a static src) there - * can't be anything that's unknown. */ - return false; - } - - if (dummy.flags & (EcsRuleIsVar << EcsRuleFirst)) { - if (!flecs_rule_var_is_unknown(rule, dummy.first.var, ctx)) { - return false; - } - } - if (dummy.flags & (EcsRuleIsVar << EcsRuleSecond)) { - if (!flecs_rule_var_is_unknown(rule, dummy.second.var, ctx)) { - return false; - } - } - if (dummy.flags & (EcsRuleIsVar << EcsRuleSrc)) { - if (!flecs_rule_var_is_unknown(rule, dummy.src.var, ctx)) { - return false; - } - } - - return true; -} - -/* Find the next known term from specified offset. This function is used to find - * a term that can be evaluated before a term that is unknown. Evaluating known - * before unknown terms can significantly decrease the search space. */ -static -int32_t flecs_rule_term_next_known( - ecs_rule_t *rule, - ecs_rule_compile_ctx_t *ctx, - int32_t offset, - ecs_flags64_t compiled) -{ - ecs_filter_t *filter = &rule->filter; - ecs_term_t *terms = filter->terms; - int32_t i, count = filter->term_count; - - for (i = offset; i < count; i ++) { - ecs_term_t *term = &terms[i]; - if (compiled & (1ull << i)) { - continue; - } - - /* Only evaluate And terms */ - if (term->oper != EcsAnd || flecs_rule_term_is_or(&rule->filter, term)){ - continue; - } - - /* Don't reorder terms before/after scopes */ - if (term->first.id == EcsScopeOpen || term->first.id == EcsScopeClose) { - return -1; - } - - if (flecs_rule_term_is_unknown(rule, term, ctx)) { - continue; - } - - return i; - } - - return -1; -} - -/* If the first part of a query contains more than one trivial term, insert a - * special instruction which batch-evaluates multiple terms. */ -static -int32_t flecs_rule_insert_trivial_search( - ecs_rule_t *rule, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_filter_t *filter = &rule->filter; - ecs_term_t *terms = filter->terms; - int32_t i, term_count = filter->term_count; - - /* Find trivial terms, which can be handled in single instruction */ - int32_t trivial_wildcard_terms = 0; - int32_t trivial_data_terms = 0; - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (!(term->flags & EcsTermIsTrivial)) { - break; - } - - /* We can only add trivial terms to plan if they no up traversal */ - if ((term->src.flags & EcsTraverseFlags) != EcsSelf) { - break; - } - - if (ecs_id_is_wildcard(term->id)) { - trivial_wildcard_terms ++; - } - - if (!(term->flags & EcsTermNoData)) { - trivial_data_terms ++; - } - } - - int32_t trivial_terms = i; - if (trivial_terms >= 2) { - /* If there's more than 1 trivial term, batch them in trivial search */ - ecs_rule_op_t trivial = {0}; - if (trivial_wildcard_terms) { - trivial.kind = EcsRuleTrivWildcard; - } else { - if (trivial_data_terms) { - /* Check to see if there are remaining data terms. If there are, - * we'll have to insert an instruction later that populates all - * fields, so don't do double work here. */ - for (i = trivial_terms; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (!(term->flags & EcsTermIsTrivial)) { - break; - } - } - if (trivial_terms == term_count || i != term_count) { - /* Nobody else is going to set the data fields, so we should - * do it here. */ - trivial.kind = EcsRuleTrivData; - } - } - if (!trivial.kind) { - trivial.kind = EcsRuleTriv; - } - } - - /* Store on the operation how many trivial terms should be evaluated */ - trivial.other = (ecs_rule_lbl_t)trivial_terms; - flecs_rule_op_insert(&trivial, ctx); - } else { - /* If fewer than 1 trivial term, there's no point in batching them */ - trivial_terms = 0; - } - - return trivial_terms; -} - -/* Insert instruction to populate data fields. */ -static -void flecs_rule_insert_populate( - ecs_rule_t *rule, - ecs_rule_compile_ctx_t *ctx, - int32_t trivial_terms) -{ - ecs_filter_t *filter = &rule->filter; - int32_t i, term_count = filter->term_count; - - /* Insert instruction that populates data. This instruction does not - * have to be inserted if the filter provides no data, or if all terms - * of the filter are trivial, in which case the trivial search operation - * also sets the data. */ - if (!(filter->flags & EcsFilterNoData) && (trivial_terms != term_count)) { - int32_t data_fields = 0; - bool only_self = true; - - /* There are two instructions for setting data fields, a fast one - * that only supports owned fields, and one that supports any kind - * of field. Loop through (remaining) terms to check which one we - * need to use. */ - for (i = trivial_terms; i < term_count; i ++) { - ecs_term_t *term = &filter->terms[i]; - if (term->flags & EcsTermNoData) { - /* Don't care about terms that have no data */ - continue; - } - - data_fields ++; - - if (!ecs_term_match_this(term)) { - break; - } - - if (term->src.flags & EcsUp) { - break; - } - } - - if (i != filter->term_count) { - only_self = false; /* Needs the more complex operation */ - } - - if (data_fields) { - if (only_self) { - ecs_rule_op_t nothing = {0}; - nothing.kind = EcsRulePopulateSelf; - flecs_rule_op_insert(¬hing, ctx); - } else { - ecs_rule_op_t nothing = {0}; - nothing.kind = EcsRulePopulate; - flecs_rule_op_insert(¬hing, ctx); - } - } - } -} - -int flecs_rule_compile( - ecs_world_t *world, - ecs_stage_t *stage, - ecs_rule_t *rule) -{ - ecs_filter_t *filter = &rule->filter; - ecs_term_t *terms = filter->terms; - ecs_rule_compile_ctx_t ctx = {0}; - ecs_vec_reset_t(NULL, &stage->operations, ecs_rule_op_t); - ctx.ops = &stage->operations; - ctx.cur = ctx.ctrlflow; - ctx.cur->lbl_begin = -1; - ctx.cur->lbl_begin = -1; - ecs_vec_clear(ctx.ops); - - /* Find all variables defined in query */ - if (flecs_rule_discover_vars(stage, rule)) { - return -1; - } - - /* If rule contains fixed source terms, insert operation to set sources */ - int32_t i, term_count = filter->term_count; - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (term->src.flags & EcsIsEntity) { - ecs_rule_op_t set_fixed = {0}; - set_fixed.kind = EcsRuleSetFixed; - flecs_rule_op_insert(&set_fixed, &ctx); - break; - } - } - - /* If the rule contains terms with fixed ids (no wildcards, variables), - * insert instruction that initializes ecs_iter_t::ids. This allows for the - * insertion of simpler instructions later on. */ - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (flecs_rule_term_fixed_id(filter, term) || - (term->src.flags & EcsIsEntity && !term->src.id)) - { - ecs_rule_op_t set_ids = {0}; - set_ids.kind = EcsRuleSetIds; - flecs_rule_op_insert(&set_ids, &ctx); - break; - } - } - - /* Insert trivial term search if query allows for it */ - int32_t trivial_terms = flecs_rule_insert_trivial_search(rule, &ctx); - - /* Compile remaining query terms to instructions */ - ecs_flags64_t compiled = 0; - for (i = trivial_terms; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - int32_t compile = i; - - if (compiled & (1ull << i)) { - continue; /* Already compiled */ - } - - bool can_reorder = true; - if (term->oper != EcsAnd || flecs_rule_term_is_or(&rule->filter, term)){ - can_reorder = false; - } - - /* If variables have been written, but this term has no known variables, - * first try to resolve terms that have known variables. This can - * significantly reduce the search space. - * Only perform this optimization after at least one variable has been - * written to, as all terms are unknown otherwise. */ - if (can_reorder && ctx.written && - flecs_rule_term_is_unknown(rule, term, &ctx)) - { - int32_t term_index = flecs_rule_term_next_known( - rule, &ctx, i + 1, compiled); - if (term_index != -1) { - term = &rule->filter.terms[term_index]; - compile = term_index; - i --; /* Repeat current term */ - } - } - - if (flecs_rule_compile_term(world, rule, term, &ctx)) { - return -1; - } - - compiled |= (1ull << compile); - } - - ecs_var_id_t this_id = flecs_rule_find_var_id(rule, "This", EcsVarEntity); - - /* If This variable has been written as entity, insert an operation to - * assign it to it.entities for consistency. */ - if (this_id != EcsVarNone && (ctx.written & (1ull << this_id))) { - ecs_rule_op_t set_this = {0}; - set_this.kind = EcsRuleSetThis; - set_this.flags |= (EcsRuleIsVar << EcsRuleFirst); - set_this.first.var = this_id; - flecs_rule_op_insert(&set_this, &ctx); - } - - /* Make sure non-This variables are written as entities */ - if (rule->vars) { - for (i = 0; i < rule->var_count; i ++) { - ecs_rule_var_t *var = &rule->vars[i]; - if (var->id && var->kind == EcsVarTable && var->name) { - ecs_var_id_t var_id = flecs_rule_find_var_id(rule, var->name, - EcsVarEntity); - if (!flecs_rule_is_written(var_id, ctx.written)) { - /* Skip anonymous variables */ - if (!flecs_rule_var_is_anonymous(rule, var_id)) { - flecs_rule_insert_each(var->id, var_id, &ctx, false); - } - } - } - } - } - - /* If rule contains non-This variables as term source, build lookup array */ - if (rule->src_vars) { - ecs_assert(rule->vars != NULL, ECS_INTERNAL_ERROR, NULL); - bool only_anonymous = true; - - for (i = 0; i < filter->field_count; i ++) { - ecs_var_id_t var_id = rule->src_vars[i]; - if (!var_id) { - continue; - } - - if (!flecs_rule_var_is_anonymous(rule, var_id)) { - only_anonymous = false; - break; - } else { - /* Don't fetch component data for anonymous variables. Because - * not all metadata (such as it.sources) is initialized for - * anonymous variables, and because they may only be available - * as table variables (each is not guaranteed to be inserted for - * anonymous variables) the iterator may not have sufficient - * information to resolve component data. */ - for (int32_t t = 0; t < filter->term_count; t ++) { - ecs_term_t *term = &filter->terms[t]; - if (term->field_index == i) { - term->inout = EcsInOutNone; - } - } - } - } - - /* Don't insert setvar instruction if all vars are anonymous */ - if (!only_anonymous) { - ecs_rule_op_t set_vars = {0}; - set_vars.kind = EcsRuleSetVars; - flecs_rule_op_insert(&set_vars, &ctx); - } - - for (i = 0; i < filter->field_count; i ++) { - ecs_var_id_t var_id = rule->src_vars[i]; - if (!var_id) { - continue; - } - - if (rule->vars[var_id].kind == EcsVarTable) { - var_id = flecs_rule_find_var_id(rule, rule->vars[var_id].name, - EcsVarEntity); - - /* Variables used as source that aren't This must be entities */ - ecs_assert(var_id != EcsVarNone, ECS_INTERNAL_ERROR, NULL); - } - - rule->src_vars[i] = var_id; - } - } - - /* If filter is empty, insert Nothing instruction */ - if (!term_count) { - ecs_rule_op_t nothing = {0}; - nothing.kind = EcsRuleNothing; - flecs_rule_op_insert(¬hing, &ctx); - } else { - /* Insert instruction to populate data fields */ - flecs_rule_insert_populate(rule, &ctx, trivial_terms); - - /* Insert yield. If program reaches this operation, a result was found */ - ecs_rule_op_t yield = {0}; - yield.kind = EcsRuleYield; - flecs_rule_op_insert(&yield, &ctx); - } - - int32_t op_count = ecs_vec_count(ctx.ops); - if (op_count) { - rule->op_count = op_count; - rule->ops = ecs_os_malloc_n(ecs_rule_op_t, op_count); - ecs_rule_op_t *rule_ops = ecs_vec_first_t(ctx.ops, ecs_rule_op_t); - ecs_os_memcpy_n(rule->ops, rule_ops, ecs_rule_op_t, op_count); - } - - return 0; -} - -#endif diff --git a/vendors/flecs/src/addons/rules/engine.c b/vendors/flecs/src/addons/rules/engine.c deleted file mode 100644 index ec1e0f8d4..000000000 --- a/vendors/flecs/src/addons/rules/engine.c +++ /dev/null @@ -1,2749 +0,0 @@ -/** - * @file addons/rules/engine.c - * @brief Rules engine implementation. - */ - -#include "rules.h" - -#ifdef FLECS_RULES - -static -bool flecs_rule_dispatch( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx); - -static -bool flecs_rule_run_until( - bool redo, - ecs_rule_run_ctx_t *ctx, - const ecs_rule_op_t *ops, - ecs_rule_lbl_t first, - ecs_rule_lbl_t cur, - ecs_rule_op_kind_t until); - -ecs_allocator_t* flecs_rule_get_allocator( - const ecs_iter_t *it) -{ - ecs_world_t *world = it->world; - if (ecs_poly_is(world, ecs_world_t)) { - return &world->allocator; - } else { - ecs_assert(ecs_poly_is(world, ecs_stage_t), ECS_INTERNAL_ERROR, NULL); - return &((ecs_stage_t*)world)->allocator; - } -} - -static -ecs_rule_op_ctx_t* flecs_op_ctx_( - const ecs_rule_run_ctx_t *ctx) -{ - return &ctx->op_ctx[ctx->op_index]; -} - -#define flecs_op_ctx(ctx, op_kind) (&flecs_op_ctx_(ctx)->is.op_kind) - -static -void flecs_reset_source_set_flag( - const ecs_rule_run_ctx_t *ctx, - int32_t field_index) -{ - ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); - (*ctx->source_set) &= ~(1u << field_index); -} - -static -void flecs_set_source_set_flag( - const ecs_rule_run_ctx_t *ctx, - int32_t field_index) -{ - ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); - (*ctx->source_set) |= (1u << field_index); -} - -static -ecs_table_range_t flecs_range_from_entity( - ecs_entity_t e, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_record_t *r = flecs_entities_get(ctx->world, e); - if (!r) { - return (ecs_table_range_t){ 0 }; - } - return (ecs_table_range_t){ - .table = r->table, - .offset = ECS_RECORD_TO_ROW(r->row), - .count = 1 - }; -} - -static -ecs_table_range_t flecs_rule_var_get_range( - int32_t var_id, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_assert(var_id < ctx->rule->var_count, ECS_INTERNAL_ERROR, NULL); - ecs_var_t *var = &ctx->vars[var_id]; - ecs_table_t *table = var->range.table; - if (table) { - return var->range; - } - - ecs_entity_t entity = var->entity; - if (entity && entity != EcsWildcard) { - var->range = flecs_range_from_entity(entity, ctx); - return var->range; - } - - return (ecs_table_range_t){ 0 }; -} - -static -ecs_table_t* flecs_rule_var_get_table( - int32_t var_id, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_var_t *var = &ctx->vars[var_id]; - ecs_table_t *table = var->range.table; - if (table) { - return table; - } - - ecs_entity_t entity = var->entity; - if (entity && entity != EcsWildcard) { - var->range = flecs_range_from_entity(entity, ctx); - return var->range.table; - } - - return NULL; -} - -static -ecs_table_t* flecs_rule_get_table( - const ecs_rule_op_t *op, - const ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_flags16_t flags = flecs_rule_ref_flags(op->flags, ref_kind); - if (flags & EcsRuleIsEntity) { - return ecs_get_table(ctx->world, ref->entity); - } else { - return flecs_rule_var_get_table(ref->var, ctx); - } -} - -static -ecs_table_range_t flecs_rule_get_range( - const ecs_rule_op_t *op, - const ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_flags16_t flags = flecs_rule_ref_flags(op->flags, ref_kind); - if (flags & EcsRuleIsEntity) { - ecs_assert(!(flags & EcsRuleIsVar), ECS_INTERNAL_ERROR, NULL); - return flecs_range_from_entity(ref->entity, ctx); - } else { - ecs_var_t *var = &ctx->vars[ref->var]; - if (var->range.table) { - return ctx->vars[ref->var].range; - } else if (var->entity) { - return flecs_range_from_entity(var->entity, ctx); - } - } - return (ecs_table_range_t){0}; -} - -static -ecs_entity_t flecs_rule_var_get_entity( - ecs_var_id_t var_id, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_assert(var_id < (ecs_var_id_t)ctx->rule->var_count, - ECS_INTERNAL_ERROR, NULL); - ecs_var_t *var = &ctx->vars[var_id]; - ecs_entity_t entity = var->entity; - if (entity) { - return entity; - } - - ecs_assert(var->range.count == 1, ECS_INTERNAL_ERROR, NULL); - ecs_table_t *table = var->range.table; - ecs_entity_t *entities = table->data.entities.array; - var->entity = entities[var->range.offset]; - return var->entity; -} - -static -void flecs_rule_var_reset( - ecs_var_id_t var_id, - const ecs_rule_run_ctx_t *ctx) -{ - ctx->vars[var_id].entity = EcsWildcard; - ctx->vars[var_id].range.table = NULL; -} - -static -void flecs_rule_var_set_table( - const ecs_rule_op_t *op, - ecs_var_id_t var_id, - ecs_table_t *table, - int32_t offset, - int32_t count, - const ecs_rule_run_ctx_t *ctx) -{ - (void)op; - ecs_assert(ctx->rule_vars[var_id].kind == EcsVarTable, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(flecs_rule_is_written(var_id, op->written), - ECS_INTERNAL_ERROR, NULL); - ecs_var_t *var = &ctx->vars[var_id]; - var->entity = 0; - var->range = (ecs_table_range_t){ - .table = table, - .offset = offset, - .count = count - }; -} - -static -void flecs_rule_var_set_entity( - const ecs_rule_op_t *op, - ecs_var_id_t var_id, - ecs_entity_t entity, - const ecs_rule_run_ctx_t *ctx) -{ - (void)op; - ecs_assert(var_id < (ecs_var_id_t)ctx->rule->var_count, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(flecs_rule_is_written(var_id, op->written), - ECS_INTERNAL_ERROR, NULL); - ecs_var_t *var = &ctx->vars[var_id]; - var->range.table = NULL; - var->entity = entity; -} - -static -void flecs_rule_set_vars( - const ecs_rule_op_t *op, - ecs_id_t id, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_flags16_t flags_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t flags_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); - - if (flags_1st & EcsRuleIsVar) { - ecs_var_id_t var = op->first.var; - if (op->written & (1ull << var)) { - if (ECS_IS_PAIR(id)) { - flecs_rule_var_set_entity( - op, var, ecs_get_alive(ctx->world, ECS_PAIR_FIRST(id)), ctx); - } else { - flecs_rule_var_set_entity(op, var, id, ctx); - } - } - } - if (flags_2nd & EcsRuleIsVar) { - ecs_var_id_t var = op->second.var; - if (op->written & (1ull << var)) { - flecs_rule_var_set_entity( - op, var, ecs_get_alive(ctx->world, ECS_PAIR_SECOND(id)), ctx); - } - } -} - -static -ecs_table_range_t flecs_get_ref_range( - const ecs_rule_ref_t *ref, - ecs_flags16_t flag, - const ecs_rule_run_ctx_t *ctx) -{ - if (flag & EcsRuleIsEntity) { - return flecs_range_from_entity(ref->entity, ctx); - } else if (flag & EcsRuleIsVar) { - return flecs_rule_var_get_range(ref->var, ctx); - } - return (ecs_table_range_t){0}; -} - -static -ecs_entity_t flecs_get_ref_entity( - const ecs_rule_ref_t *ref, - ecs_flags16_t flag, - const ecs_rule_run_ctx_t *ctx) -{ - if (flag & EcsRuleIsEntity) { - return ref->entity; - } else if (flag & EcsRuleIsVar) { - return flecs_rule_var_get_entity(ref->var, ctx); - } - return 0; -} - -static -ecs_id_t flecs_rule_op_get_id_w_written( - const ecs_rule_op_t *op, - uint64_t written, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_flags16_t flags_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t flags_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); - ecs_entity_t first = 0, second = 0; - - if (flags_1st) { - if (flecs_ref_is_written(op, &op->first, EcsRuleFirst, written)) { - first = flecs_get_ref_entity(&op->first, flags_1st, ctx); - } else if (flags_1st & EcsRuleIsVar) { - first = EcsWildcard; - } - } - if (flags_2nd) { - if (flecs_ref_is_written(op, &op->second, EcsRuleSecond, written)) { - second = flecs_get_ref_entity(&op->second, flags_2nd, ctx); - } else if (flags_2nd & EcsRuleIsVar) { - second = EcsWildcard; - } - } - - if (flags_2nd & (EcsRuleIsVar | EcsRuleIsEntity)) { - return ecs_pair(first, second); - } else { - return flecs_entities_get_generation(ctx->world, first); - } -} - -static -ecs_id_t flecs_rule_op_get_id( - const ecs_rule_op_t *op, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - return flecs_rule_op_get_id_w_written(op, written, ctx); -} - -static -int16_t flecs_rule_next_column( - ecs_table_t *table, - ecs_id_t id, - int32_t column) -{ - if (!ECS_IS_PAIR(id) || (ECS_PAIR_FIRST(id) != EcsWildcard)) { - column = column + 1; - } else { - ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); - column = ecs_search_offset(NULL, table, column + 1, id, NULL); - ecs_assert(column != -1, ECS_INTERNAL_ERROR, NULL); - } - return flecs_ito(int16_t, column); -} - -static -void flecs_rule_it_set_column( - ecs_iter_t *it, - int32_t field_index, - int32_t column) -{ - ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(field_index >= 0, ECS_INTERNAL_ERROR, NULL); - it->columns[field_index] = column + 1; -} - -static -ecs_id_t flecs_rule_it_set_id( - ecs_iter_t *it, - ecs_table_t *table, - int32_t field_index, - int32_t column) -{ - ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(field_index >= 0, ECS_INTERNAL_ERROR, NULL); - return it->ids[field_index] = table->type.array[column]; -} - -static -void flecs_rule_set_match( - const ecs_rule_op_t *op, - ecs_table_t *table, - int32_t column, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); - int32_t field_index = op->field_index; - if (field_index == -1) { - return; - } - - ecs_iter_t *it = ctx->it; - flecs_rule_it_set_column(it, field_index, column); - ecs_id_t matched = flecs_rule_it_set_id(it, table, field_index, column); - flecs_rule_set_vars(op, matched, ctx); -} - -static -void flecs_rule_set_trav_match( - const ecs_rule_op_t *op, - int32_t column, - ecs_entity_t trav, - ecs_entity_t second, - const ecs_rule_run_ctx_t *ctx) -{ - int32_t field_index = op->field_index; - if (field_index == -1) { - return; - } - - ecs_iter_t *it = ctx->it; - ecs_id_t matched = ecs_pair(trav, second); - it->ids[op->field_index] = matched; - if (column != -1) { - flecs_rule_it_set_column(it, op->field_index, column); - } - flecs_rule_set_vars(op, matched, ctx); -} - -static -bool flecs_rule_table_filter( - ecs_table_t *table, - ecs_rule_lbl_t other, - ecs_flags32_t filter_mask) -{ - uint32_t filter = flecs_ito(uint32_t, other); - return (table->flags & filter_mask & filter) != 0; -} - -static -bool flecs_rule_select_w_id( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx, - ecs_id_t id, - ecs_flags32_t filter_mask) -{ - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - ecs_id_record_t *idr = op_ctx->idr; - ecs_table_record_t *tr; - ecs_table_t *table; - - if (!redo) { - if (!idr || idr->id != id) { - idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); - if (!idr) { - return false; - } - } - - if (ctx->rule->filter.flags & EcsFilterMatchEmptyTables) { - if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { - return false; - } - } else { - if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { - return false; - } - } - } - -repeat: - if (!redo || !op_ctx->remaining) { - tr = flecs_table_cache_next(&op_ctx->it, ecs_table_record_t); - if (!tr) { - return false; - } - - op_ctx->column = flecs_ito(int16_t, tr->index); - op_ctx->remaining = flecs_ito(int16_t, tr->count - 1); - table = tr->hdr.table; - flecs_rule_var_set_table(op, op->src.var, table, 0, 0, ctx); - } else { - tr = (ecs_table_record_t*)op_ctx->it.cur; - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - table = tr->hdr.table; - op_ctx->column = flecs_rule_next_column(table, idr->id, op_ctx->column); - op_ctx->remaining --; - } - - if (flecs_rule_table_filter(table, op->other, filter_mask)) { - goto repeat; - } - - flecs_rule_set_match(op, table, op_ctx->column, ctx); - return true; -} - -static -bool flecs_rule_select( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_id_t id = 0; - if (!redo) { - id = flecs_rule_op_get_id(op, ctx); - } - return flecs_rule_select_w_id(op, redo, ctx, id, - (EcsTableIsPrefab|EcsTableIsDisabled)); -} - -static -bool flecs_rule_with( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - ecs_id_record_t *idr = op_ctx->idr; - const ecs_table_record_t *tr; - - ecs_table_t *table = flecs_rule_get_table(op, &op->src, EcsRuleSrc, ctx); - if (!table) { - return false; - } - - if (!redo) { - ecs_id_t id = flecs_rule_op_get_id(op, ctx); - if (!idr || idr->id != id) { - idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); - if (!idr) { - return false; - } - } - - tr = flecs_id_record_get_table(idr, table); - if (!tr) { - return false; - } - - op_ctx->column = flecs_ito(int16_t, tr->index); - op_ctx->remaining = flecs_ito(int16_t, tr->count); - } else { - if (--op_ctx->remaining <= 0) { - return false; - } - - op_ctx->column = flecs_rule_next_column(table, idr->id, op_ctx->column); - ecs_assert(op_ctx->column != -1, ECS_INTERNAL_ERROR, NULL); - } - - flecs_rule_set_match(op, table, op_ctx->column, ctx); - return true; -} - -static -bool flecs_rule_and( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (written & (1ull << op->src.var)) { - return flecs_rule_with(op, redo, ctx); - } else { - return flecs_rule_select(op, redo, ctx); - } -} - -static -bool flecs_rule_select_id( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx, - ecs_flags32_t table_filter) -{ - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - ecs_iter_t *it = ctx->it; - int8_t field = op->field_index; - ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL); - - if (!redo) { - ecs_id_t id = it->ids[field]; - ecs_id_record_t *idr = op_ctx->idr; - if (!idr || idr->id != id) { - idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); - if (!idr) { - return false; - } - } - - if (ctx->rule->filter.flags & EcsFilterMatchEmptyTables) { - if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { - return false; - } - } else { - if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { - return false; - } - } - } - -repeat: {} - const ecs_table_record_t *tr = flecs_table_cache_next( - &op_ctx->it, ecs_table_record_t); - if (!tr) { - return false; - } - - ecs_table_t *table = tr->hdr.table; - if (flecs_rule_table_filter(table, op->other, table_filter)) { - goto repeat; - } - - flecs_rule_var_set_table(op, op->src.var, table, 0, 0, ctx); - flecs_rule_it_set_column(it, field, tr->index); - return true; -} - -static -bool flecs_rule_with_id( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - if (redo) { - return false; - } - - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - ecs_iter_t *it = ctx->it; - int8_t field = op->field_index; - ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL); - - ecs_table_t *table = flecs_rule_get_table(op, &op->src, EcsRuleSrc, ctx); - if (!table) { - return false; - } - - ecs_id_t id = it->ids[field]; - ecs_id_record_t *idr = op_ctx->idr; - if (!idr || idr->id != id) { - idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); - if (!idr) { - return false; - } - } - - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (!tr) { - return false; - } - - flecs_rule_it_set_column(it, field, tr->index); - return true; -} - -static -bool flecs_rule_and_id( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (written & (1ull << op->src.var)) { - return flecs_rule_with_id(op, redo, ctx); - } else { - return flecs_rule_select_id(op, redo, ctx, - (EcsTableIsPrefab|EcsTableIsDisabled)); - } -} - -static -bool flecs_rule_up_select( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx, - bool self, - bool id_only) -{ - ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); - ecs_world_t *world = ctx->world; - ecs_iter_t *it = ctx->it; - bool redo_select = redo; - const ecs_filter_t *filter = &ctx->rule->filter; - - /* Early out if traversal relationship doesn't exist */ - op_ctx->trav = filter->terms[op->term_index].src.trav; - if (!op_ctx->idr_trav) { - op_ctx->idr_trav = flecs_id_record_get(ctx->world, - ecs_pair(op_ctx->trav, EcsWildcard)); - } - if (!op_ctx->idr_trav || !flecs_table_cache_count(&op_ctx->idr_trav->cache)){ - if (!self) { - return false; - } else if (id_only) { - return flecs_rule_select_id(op, redo, ctx, - (EcsTableIsPrefab|EcsTableIsDisabled)); - } else { - return flecs_rule_select(op, redo, ctx); - } - } - - if (!redo) { - op_ctx->with = flecs_rule_op_get_id(op, ctx); - op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); - if (!op_ctx->idr_with) { - return false; - } - - op_ctx->down = NULL; - op_ctx->cache_elem = 0; - } - - ecs_trav_down_t *down = op_ctx->down; - - do { - while (!down) { - ecs_table_t *table = op_ctx->table; - if (!table) { - ecs_table_range_t range; - it->sources[op->field_index] = 0; - do { - bool result; - if (id_only) { - result = flecs_rule_select_id(op, redo_select, ctx, 0); - } else { - result = flecs_rule_select_w_id(op, redo_select, ctx, - op_ctx->with, 0); - } - if (!result) { - return false; - } - - redo_select = true; - - range = flecs_rule_get_range( - op, &op->src, EcsRuleSrc, ctx); - ecs_assert(range.table != NULL, ECS_INTERNAL_ERROR, NULL); - } while (!self && range.table->_->traversable_count == 0); - - if (!range.count) { - range.count = ecs_table_count(range.table); - } - - table = op_ctx->table = range.table; - op_ctx->row = range.offset; - op_ctx->end = range.offset + range.count; - op_ctx->matched = it->ids[op->field_index]; - - if (self) { - if (!flecs_rule_table_filter(table, op->other, - (EcsTableIsPrefab|EcsTableIsDisabled))) - { - flecs_reset_source_set_flag(ctx, op->field_index); - op_ctx->row --; - return true; - } - } - - redo_select = true; - } else { - op_ctx->row ++; - } - - if (table->_->traversable_count == 0) { - op_ctx->table = NULL; - continue; - } else { - int32_t row; - ecs_entity_t entity = 0; - ecs_entity_t *entities = flecs_table_entities_array(table); - - for (row = op_ctx->row; row < op_ctx->end; row ++) { - entity = entities[row]; - ecs_record_t *record = flecs_entities_get(world, entity); - if (record->row & EcsEntityIsTraversable) { - it->sources[op->field_index] = entity; - break; - } - } - - if (row == op_ctx->end) { - op_ctx->table = NULL; - continue; - } - - down = op_ctx->down = flecs_rule_get_down_cache(ctx, &op_ctx->cache, - op_ctx->trav, entity, op_ctx->idr_with, self); - op_ctx->cache_elem = -1; - } - } - -next_elem: - if ((++ op_ctx->cache_elem) >= ecs_vec_count(&down->elems)) { - down = NULL; - continue; - } - - ecs_trav_down_elem_t *elem = ecs_vec_get_t( - &down->elems, ecs_trav_down_elem_t, op_ctx->cache_elem); - - flecs_rule_var_set_table(op, op->src.var, elem->table, 0, 0, ctx); - flecs_rule_set_vars(op, op_ctx->matched, ctx); - - if (flecs_rule_table_filter(elem->table, op->other, - (EcsTableIsPrefab|EcsTableIsDisabled))) - { - goto next_elem; - } - - break; - } while (true); - - flecs_set_source_set_flag(ctx, op->field_index); - - return true; -} - -static -bool flecs_rule_up_with( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - const ecs_filter_t *filter = &ctx->rule->filter; - ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); - ecs_iter_t *it = ctx->it; - - /* Early out if traversal relationship doesn't exist */ - op_ctx->trav = filter->terms[op->term_index].src.trav; - if (!op_ctx->idr_trav) { - op_ctx->idr_trav = flecs_id_record_get(ctx->world, - ecs_pair(op_ctx->trav, EcsWildcard)); - } - if (!op_ctx->idr_trav || !flecs_table_cache_count(&op_ctx->idr_trav->cache)){ - return false; - } - - if (!redo) { - op_ctx->trav = filter->terms[op->term_index].src.trav; - op_ctx->with = flecs_rule_op_get_id(op, ctx); - op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); - - if (!op_ctx->idr_with) { - return false; - } - - ecs_table_range_t range = flecs_rule_get_range( - op, &op->src, EcsRuleSrc, ctx); - if (!range.table) { - return false; - } - - ecs_trav_up_t *up = flecs_rule_get_up_cache(ctx, &op_ctx->cache, - range.table, op_ctx->with, op_ctx->trav, op_ctx->idr_with, - op_ctx->idr_trav); - - if (!up) { - return false; - } - - it->sources[op->field_index] = flecs_entities_get_generation( - ctx->world, up->src); - it->columns[op->field_index] = up->column + 1; - it->ids[op->field_index] = up->id; - flecs_rule_set_vars(op, up->id, ctx); - flecs_set_source_set_flag(ctx, op->field_index); - return true; - } else { - return false; - } -} - -static -bool flecs_rule_self_up_with( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx, - bool id_only) -{ - if (!redo) { - bool result; - if (id_only) { - result = flecs_rule_with_id(op, redo, ctx); - } else { - result = flecs_rule_with(op, redo, ctx); - } - if (result) { - ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); - op_ctx->trav = 0; - if (flecs_rule_ref_flags(op->flags, EcsRuleSrc) & EcsRuleIsVar) { - ecs_iter_t *it = ctx->it; - it->sources[op->field_index] = 0; - } - return true; - } - - flecs_reset_source_set_flag(ctx, op->field_index); - - return flecs_rule_up_with(op, redo, ctx); - } else { - ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); - if (op_ctx->trav == 0) { - return flecs_rule_with(op, redo, ctx); - } - } - - return false; -} - -static -bool flecs_rule_up( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - return flecs_rule_up_with(op, redo, ctx); - } else { - return flecs_rule_up_select(op, redo, ctx, false, false); - } -} - -static -bool flecs_rule_self_up( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - return flecs_rule_self_up_with(op, redo, ctx, false); - } else { - return flecs_rule_up_select(op, redo, ctx, true, false); - } -} - -static -bool flecs_rule_up_id( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - return flecs_rule_up_with(op, redo, ctx); - } else { - return flecs_rule_up_select(op, redo, ctx, false, true); - } -} - -static -bool flecs_rule_self_up_id( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - return flecs_rule_self_up_with(op, redo, ctx, true); - } else { - return flecs_rule_up_select(op, redo, ctx, true, true); - } -} - -static -bool flecs_rule_select_any( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - return flecs_rule_select_w_id(op, redo, ctx, EcsAny, - (EcsTableIsPrefab|EcsTableIsDisabled)); -} - -static -bool flecs_rule_and_any( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_flags16_t match_flags = op->match_flags; - if (redo) { - if (match_flags & EcsTermMatchAnySrc) { - return false; - } - } - - uint64_t written = ctx->written[ctx->op_index]; - int32_t remaining = 1; - bool result; - if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - result = flecs_rule_with(op, redo, ctx); - } else { - result = flecs_rule_select(op, redo, ctx); - remaining = 0; - } - - if (!redo) { - ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); - if (match_flags & EcsTermMatchAny && op_ctx->remaining) { - op_ctx->remaining = flecs_ito(int16_t, remaining); - } - } - - int32_t field = op->field_index; - if (field != -1) { - ctx->it->ids[field] = flecs_rule_op_get_id(op, ctx); - } - - return result; -} - -static -bool flecs_rule_triv( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); - int32_t until = flecs_ito(int32_t, op->other); - uint64_t written = ctx->written[ctx->op_index]; - ctx->written[ctx->op_index] |= 1ull; - if (written & 1ull) { - return flecs_rule_trivial_test(ctx->rule, ctx, !redo, until); - } else { - return flecs_rule_trivial_search_nodata(ctx->rule, ctx, op_ctx, !redo, until); - } -} - -static -bool flecs_rule_triv_data( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); - int32_t until = flecs_ito(int32_t, op->other); - uint64_t written = ctx->written[ctx->op_index]; - ctx->written[ctx->op_index] |= 1ull; - if (written & 1ull) { - return flecs_rule_trivial_test(ctx->rule, ctx, !redo, until); - } else { - return flecs_rule_trivial_search(ctx->rule, ctx, op_ctx, !redo, until); - } -} - -static -bool flecs_rule_triv_wildcard( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); - int32_t until = flecs_ito(int32_t, op->other); - uint64_t written = ctx->written[ctx->op_index]; - ctx->written[ctx->op_index] |= 1ull; - if (written & 1ull) { - return flecs_rule_trivial_test_w_wildcards(ctx->rule, ctx, !redo, until); - } else { - return flecs_rule_trivial_search_w_wildcards(ctx->rule, ctx, op_ctx, !redo, until); - } -} - -static -bool flecs_rule_trav_fixed_src_reflexive( - const ecs_rule_op_t *op, - const ecs_rule_run_ctx_t *ctx, - ecs_table_range_t *range, - ecs_entity_t trav, - ecs_entity_t second) -{ - ecs_table_t *table = range->table; - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t *entities = table->data.entities.array; - int32_t count = range->count; - if (!count) { - count = ecs_table_count(table); - } - - int32_t i = range->offset, end = i + count; - for (; i < end; i ++) { - if (entities[i] == second) { - /* Even though table doesn't have the specific relationship - * pair, the relationship is reflexive and the target entity - * is stored in the table. */ - break; - } - } - if (i == end) { - /* Table didn't contain target entity */ - return false; - } - if (count > 1) { - /* If the range contains more than one entity, set the range to - * return only the entity matched by the reflexive property. */ - ecs_assert(flecs_rule_ref_flags(op->flags, EcsRuleSrc) & EcsRuleIsVar, - ECS_INTERNAL_ERROR, NULL); - ecs_var_t *var = &ctx->vars[op->src.var]; - ecs_table_range_t *var_range = &var->range; - var_range->offset = i; - var_range->count = 1; - var->entity = entities[i]; - } - - flecs_rule_set_trav_match(op, -1, trav, second, ctx); - return true; -} - -static -bool flecs_rule_trav_unknown_src_reflexive( - const ecs_rule_op_t *op, - const ecs_rule_run_ctx_t *ctx, - ecs_entity_t trav, - ecs_entity_t second) -{ - ecs_assert(flecs_rule_ref_flags(op->flags, EcsRuleSrc) & EcsRuleIsVar, - ECS_INTERNAL_ERROR, NULL); - ecs_var_id_t src_var = op->src.var; - flecs_rule_var_set_entity(op, src_var, second, ctx); - flecs_rule_var_get_table(src_var, ctx); - flecs_rule_set_trav_match(op, -1, trav, second, ctx); - return true; -} - -static -bool flecs_rule_trav_fixed_src_up_fixed_second( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - if (redo) { - return false; /* If everything's fixed, can only have a single result */ - } - - ecs_flags16_t f_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t f_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); - ecs_flags16_t f_src = flecs_rule_ref_flags(op->flags, EcsRuleSrc); - ecs_entity_t trav = flecs_get_ref_entity(&op->first, f_1st, ctx); - ecs_entity_t second = flecs_get_ref_entity(&op->second, f_2nd, ctx); - ecs_table_range_t range = flecs_get_ref_range(&op->src, f_src, ctx); - ecs_table_t *table = range.table; - - /* Check if table has transitive relationship by traversing upwards */ - int32_t column = ecs_search_relation(ctx->world, table, 0, - ecs_pair(trav, second), trav, EcsSelf|EcsUp, NULL, NULL, NULL); - if (column == -1) { - if (op->match_flags & EcsTermReflexive) { - return flecs_rule_trav_fixed_src_reflexive(op, ctx, - &range, trav, second); - } else { - return false; - } - } - - flecs_rule_set_trav_match(op, column, trav, second, ctx); - return true; -} - -static -bool flecs_rule_trav_unknown_src_up_fixed_second( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_flags16_t f_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t f_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); - ecs_entity_t trav = flecs_get_ref_entity(&op->first, f_1st, ctx); - ecs_entity_t second = flecs_get_ref_entity(&op->second, f_2nd, ctx); - ecs_rule_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); - - if (!redo) { - ecs_record_t *r_second = flecs_entities_get(ctx->world, second); - bool traversable = r_second && r_second->row & EcsEntityIsTraversable; - bool reflexive = op->match_flags & EcsTermReflexive; - if (!traversable && !reflexive) { - trav_ctx->cache.id = 0; - - /* If there's no record for the entity, it can't have a subtree so - * forward operation to a regular select. */ - return flecs_rule_select(op, redo, ctx); - } - - /* Entity is traversable, which means it could have a subtree */ - flecs_rule_get_trav_down_cache(ctx, &trav_ctx->cache, trav, second); - trav_ctx->index = 0; - - if (op->match_flags & EcsTermReflexive) { - trav_ctx->index = -1; - return flecs_rule_trav_unknown_src_reflexive( - op, ctx, trav, second); - } - } else { - if (!trav_ctx->cache.id) { - /* No traversal cache, which means this is a regular select */ - return flecs_rule_select(op, redo, ctx); - } - } - - if (trav_ctx->index == -1) { - redo = false; /* First result after handling reflexive relationship */ - trav_ctx->index = 0; - } - - /* Forward to select */ - int32_t count = ecs_vec_count(&trav_ctx->cache.entities); - ecs_trav_elem_t *elems = ecs_vec_first(&trav_ctx->cache.entities); - for (; trav_ctx->index < count; trav_ctx->index ++) { - ecs_trav_elem_t *el = &elems[trav_ctx->index]; - trav_ctx->and.idr = el->idr; /* prevents lookup by select */ - if (flecs_rule_select_w_id(op, redo, ctx, ecs_pair(trav, el->entity), - (EcsTableIsPrefab|EcsTableIsDisabled))) - { - return true; - } - - redo = false; - } - - return false; -} - -static -bool flecs_rule_trav_yield_reflexive_src( - const ecs_rule_op_t *op, - const ecs_rule_run_ctx_t *ctx, - ecs_table_range_t *range, - ecs_entity_t trav) -{ - ecs_var_t *vars = ctx->vars; - ecs_rule_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); - int32_t offset = trav_ctx->offset, count = trav_ctx->count; - bool src_is_var = op->flags & (EcsRuleIsVar << EcsRuleSrc); - - if (trav_ctx->index >= (offset + count)) { - /* Restore previous offset, count */ - if (src_is_var) { - ecs_var_id_t src_var = op->src.var; - vars[src_var].range.offset = offset; - vars[src_var].range.count = count; - vars[src_var].entity = 0; - } - return false; - } - - ecs_entity_t entity = ecs_vec_get_t( - &range->table->data.entities, ecs_entity_t, trav_ctx->index)[0]; - flecs_rule_set_trav_match(op, -1, trav, entity, ctx); - - /* Hijack existing variable to return one result at a time */ - if (src_is_var) { - ecs_var_id_t src_var = op->src.var; - ecs_table_t *table = vars[src_var].range.table; - ecs_assert(!table || table == ecs_get_table(ctx->world, entity), - ECS_INTERNAL_ERROR, NULL); - (void)table; - vars[src_var].entity = entity; - vars[src_var].range = flecs_range_from_entity(entity, ctx); - } - - return true; -} - -static -bool flecs_rule_trav_fixed_src_up_unknown_second( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_flags16_t f_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t f_src = flecs_rule_ref_flags(op->flags, EcsRuleSrc); - ecs_entity_t trav = flecs_get_ref_entity(&op->first, f_1st, ctx); - ecs_table_range_t range = flecs_get_ref_range(&op->src, f_src, ctx); - ecs_table_t *table = range.table; - ecs_rule_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); - - if (!redo) { - flecs_rule_get_trav_up_cache(ctx, &trav_ctx->cache, trav, table); - trav_ctx->index = 0; - if (op->match_flags & EcsTermReflexive) { - trav_ctx->yield_reflexive = true; - trav_ctx->index = range.offset; - trav_ctx->offset = range.offset; - trav_ctx->count = range.count ? range.count : ecs_table_count(table); - } - } else { - trav_ctx->index ++; - } - - if (trav_ctx->yield_reflexive) { - if (flecs_rule_trav_yield_reflexive_src(op, ctx, &range, trav)) { - return true; - } - trav_ctx->yield_reflexive = false; - trav_ctx->index = 0; - } - - if (trav_ctx->index >= ecs_vec_count(&trav_ctx->cache.entities)) { - return false; - } - - ecs_trav_elem_t *el = ecs_vec_get_t( - &trav_ctx->cache.entities, ecs_trav_elem_t, trav_ctx->index); - flecs_rule_set_trav_match(op, el->column, trav, el->entity, ctx); - return true; -} - -static -bool flecs_rule_trav( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; - - if (!flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { - if (!flecs_ref_is_written(op, &op->second, EcsRuleSecond, written)) { - /* This can't happen, src or second should have been resolved */ - ecs_abort(ECS_INTERNAL_ERROR, - "invalid instruction sequence: unconstrained traversal"); - } else { - return flecs_rule_trav_unknown_src_up_fixed_second(op, redo, ctx); - } - } else { - if (!flecs_ref_is_written(op, &op->second, EcsRuleSecond, written)) { - return flecs_rule_trav_fixed_src_up_unknown_second(op, redo, ctx); - } else { - return flecs_rule_trav_fixed_src_up_fixed_second(op, redo, ctx); - } - } -} - -static -bool flecs_rule_idsright( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_ids_ctx_t *op_ctx = flecs_op_ctx(ctx, ids); - ecs_id_record_t *cur; - - if (!redo) { - ecs_id_t id = flecs_rule_op_get_id(op, ctx); - if (!ecs_id_is_wildcard(id)) { - /* If id is not a wildcard, we can directly return it. This can - * happen if a variable was constrained by an iterator. */ - op_ctx->cur = NULL; - flecs_rule_set_vars(op, id, ctx); - return true; - } - - cur = op_ctx->cur = flecs_id_record_get(ctx->world, id); - if (!cur) { - return false; - } - - cur = op_ctx->cur = cur->first.next; - } else { - if (!op_ctx->cur) { - return false; - } - - cur = op_ctx->cur = op_ctx->cur->first.next; - } - - if (!cur) { - return false; - } - - flecs_rule_set_vars(op, cur->id, ctx); - - if (op->field_index != -1) { - ecs_iter_t *it = ctx->it; - ecs_id_t id = flecs_rule_op_get_id_w_written(op, op->written, ctx); - it->ids[op->field_index] = id; - } - - return true; -} - -static -bool flecs_rule_idsleft( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_ids_ctx_t *op_ctx = flecs_op_ctx(ctx, ids); - ecs_id_record_t *cur; - - if (!redo) { - ecs_id_t id = flecs_rule_op_get_id(op, ctx); - if (!ecs_id_is_wildcard(id)) { - /* If id is not a wildcard, we can directly return it. This can - * happen if a variable was constrained by an iterator. */ - op_ctx->cur = NULL; - flecs_rule_set_vars(op, id, ctx); - return true; - } - - cur = op_ctx->cur = flecs_id_record_get(ctx->world, id); - if (!cur) { - return false; - } - - cur = op_ctx->cur = cur->second.next; - } else { - if (!op_ctx->cur) { - return false; - } - - cur = op_ctx->cur = op_ctx->cur->second.next; - } - - if (!cur) { - return false; - } - - flecs_rule_set_vars(op, cur->id, ctx); - - if (op->field_index != -1) { - ecs_iter_t *it = ctx->it; - ecs_id_t id = flecs_rule_op_get_id_w_written(op, op->written, ctx); - it->ids[op->field_index] = id; - } - - return true; -} - -static -bool flecs_rule_each( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_each_ctx_t *op_ctx = flecs_op_ctx(ctx, each); - int32_t row; - - ecs_table_range_t range = flecs_rule_var_get_range(op->first.var, ctx); - ecs_table_t *table = range.table; - if (!table) { - return false; - } - - if (!redo) { - row = op_ctx->row = range.offset; - } else { - int32_t end = range.count; - if (end) { - end += range.offset; - } else { - end = table->data.entities.count; - } - row = ++ op_ctx->row; - if (op_ctx->row >= end) { - return false; - } - } - - ecs_assert(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); - - ecs_entity_t *entities = table->data.entities.array; - ecs_entity_t e; - do { - e = entities[row ++]; - - /* Exclude entities that are used as markers by rule engine */ - } while ((e == EcsWildcard) || (e == EcsAny) || - (e == EcsThis) || (e == EcsVariable)); - - flecs_rule_var_set_entity(op, op->src.var, e, ctx); - - return true; -} - -static -bool flecs_rule_store( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - if (!redo) { - flecs_rule_var_set_entity(op, op->src.var, op->first.entity, ctx); - return true; - } else { - return false; - } -} - -static -bool flecs_rule_reset( - const ecs_rule_op_t *op, - bool redo, - const ecs_rule_run_ctx_t *ctx) -{ - if (!redo) { - return true; - } else { - flecs_rule_var_reset(op->src.var, ctx); - return false; - } -} - -static -void flecs_rule_reset_after_block( - const ecs_rule_op_t *start_op, - ecs_rule_run_ctx_t *ctx, - ecs_rule_ctrl_ctx_t *op_ctx) -{ - ecs_rule_lbl_t op_index = start_op->next; - const ecs_rule_op_t *op = &ctx->rit->ops[op_index]; - ctx->written[op_index] = ctx->written[ctx->op_index]; - ctx->op_index = op_index; - - int32_t field = op->field_index; - if (field == -1) { - goto done; - } - - ecs_iter_t *it = ctx->it; - - /* Not terms return no data */ - it->columns[field] = 0; - - /* Ignore variables written by Not operation */ - uint64_t *written = ctx->written; - uint64_t written_cur = written[op->prev + 1]; - ecs_flags16_t flags_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); - ecs_flags16_t flags_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); - - /* Overwrite id with cleared out variables */ - ecs_id_t id = flecs_rule_op_get_id(op, ctx); - if (id) { - it->ids[field] = id; - } - - /* Reset variables */ - if (flags_1st & EcsRuleIsVar) { - if (!flecs_ref_is_written(op, &op->first, EcsRuleFirst, written_cur)){ - flecs_rule_var_reset(op->first.var, ctx); - } - } - if (flags_2nd & EcsRuleIsVar) { - if (!flecs_ref_is_written(op, &op->second, EcsRuleSecond, written_cur)){ - flecs_rule_var_reset(op->second.var, ctx); - } - } - - /* If term has entity src, set it because no other instruction might */ - if (op->flags & (EcsRuleIsEntity << EcsRuleSrc)) { - it->sources[field] = op->src.entity; - } - -done: - op_ctx->op_index = op_index; -} - -static -const char* flecs_rule_name_arg( - const ecs_rule_op_t *op, - ecs_rule_run_ctx_t *ctx) -{ - int8_t term_index = op->term_index; - ecs_term_t *term = &ctx->rule->filter.terms[term_index]; - return term->second.name; -} - -static -bool flecs_rule_compare_range( - const ecs_table_range_t *l, - const ecs_table_range_t *r) -{ - if (l->table != r->table) { - return false; - } - - if (l->count) { - int32_t l_end = l->offset + l->count; - int32_t r_end = r->offset + r->count; - if (r->offset < l->offset) { - return false; - } - if (r_end > l_end) { - return false; - } - } else { - /* Entire table is matched */ - } - - return true; -} - -static -bool flecs_rule_pred_eq_w_range( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx, - ecs_table_range_t r) -{ - if (redo) { - return false; - } - - uint64_t written = ctx->written[ctx->op_index]; - ecs_var_id_t first_var = op->first.var; - if (!(written & (1ull << first_var))) { - /* left = unknown, right = known. Assign right-hand value to left */ - ecs_var_id_t l = first_var; - ctx->vars[l].range = r; - if (r.count == 1) { - ctx->vars[l].entity = ecs_vec_get_t(&r.table->data.entities, - ecs_entity_t, r.offset)[0]; - } - return true; - } else { - ecs_table_range_t l = flecs_rule_get_range( - op, &op->first, EcsRuleFirst, ctx); - - if (!flecs_rule_compare_range(&l, &r)) { - return false; - } - - ctx->vars[first_var].range.offset = r.offset; - ctx->vars[first_var].range.count = r.count; - return true; - } -} - -static -bool flecs_rule_pred_eq( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; (void)written; - ecs_assert(flecs_ref_is_written(op, &op->second, EcsRuleSecond, written), - ECS_INTERNAL_ERROR, - "invalid instruction sequence: uninitialized eq operand"); - - ecs_table_range_t r = flecs_rule_get_range( - op, &op->second, EcsRuleSecond, ctx); - return flecs_rule_pred_eq_w_range(op, redo, ctx, r); -} - -static -bool flecs_rule_pred_eq_name( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - const char *name = flecs_rule_name_arg(op, ctx); - ecs_entity_t e = ecs_lookup_fullpath(ctx->world, name); - if (!e) { - /* Entity doesn't exist */ - return false; - } - - ecs_table_range_t r = flecs_range_from_entity(e, ctx); - return flecs_rule_pred_eq_w_range(op, redo, ctx, r); -} - -static -bool flecs_rule_pred_neq_w_range( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx, - ecs_table_range_t r) -{ - ecs_rule_eq_ctx_t *op_ctx = flecs_op_ctx(ctx, eq); - ecs_var_id_t first_var = op->first.var; - ecs_table_range_t l = flecs_rule_get_range( - op, &op->first, EcsRuleFirst, ctx); - - /* If tables don't match, neq always returns once */ - if (l.table != r.table) { - return true && !redo; - } - - int32_t l_offset; - int32_t l_count; - if (!redo) { - /* Make sure we're working with the correct table count */ - if (!l.count && l.table) { - l.count = ecs_table_count(l.table); - } - - l_offset = l.offset; - l_count = l.count; - - /* Cache old value */ - op_ctx->range = l; - } else { - l_offset = op_ctx->range.offset; - l_count = op_ctx->range.count; - } - - /* If the table matches, a Neq returns twice: once for the slice before the - * excluded slice, once for the slice after the excluded slice. If the right - * hand range starts & overlaps with the left hand range, there is only - * one slice. */ - ecs_var_t *var = &ctx->vars[first_var]; - if (!redo && r.offset > l_offset) { - int32_t end = r.offset; - if (end > l_count) { - end = l_count; - } - - /* Return first slice */ - var->range.table = l.table; - var->range.offset = l_offset; - var->range.count = end - l_offset; - op_ctx->redo = false; - return true; - } else if (!op_ctx->redo) { - int32_t l_end = op_ctx->range.offset + l_count; - int32_t r_end = r.offset + r.count; - - if (l_end <= r_end) { - /* If end of existing range falls inside the excluded range, there's - * nothing more to return */ - var->range = l; - return false; - } - - /* Return second slice */ - var->range.table = l.table; - var->range.offset = r_end; - var->range.count = l_end - r_end; - - /* Flag so we know we're done the next redo */ - op_ctx->redo = true; - return true; - } else { - /* Restore previous value */ - var->range = l; - return false; - } -} - -static -bool flecs_rule_pred_match( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx, - bool is_neq) -{ - ecs_rule_eq_ctx_t *op_ctx = flecs_op_ctx(ctx, eq); - uint64_t written = ctx->written[ctx->op_index]; - ecs_assert(flecs_ref_is_written(op, &op->first, EcsRuleFirst, written), - ECS_INTERNAL_ERROR, - "invalid instruction sequence: uninitialized match operand"); - (void)written; - - ecs_var_id_t first_var = op->first.var; - const char *match = flecs_rule_name_arg(op, ctx); - ecs_table_range_t l; - if (!redo) { - l = flecs_rule_get_range(op, &op->first, EcsRuleFirst, ctx); - if (!l.table) { - return false; - } - - if (!l.count) { - l.count = ecs_table_count(l.table); - } - - op_ctx->range = l; - op_ctx->index = l.offset; - op_ctx->name_col = flecs_ito(int16_t, - ecs_table_get_type_index(ctx->world, l.table, - ecs_pair(ecs_id(EcsIdentifier), EcsName))); - if (op_ctx->name_col == -1) { - return is_neq; - } - op_ctx->name_col = flecs_ito(int16_t, - l.table->column_map[op_ctx->name_col]); - ecs_assert(op_ctx->name_col != -1, ECS_INTERNAL_ERROR, NULL); - } else { - if (op_ctx->name_col == -1) { - /* Table has no name */ - return false; - } - - l = op_ctx->range; - } - - const EcsIdentifier *names = l.table->data.columns[op_ctx->name_col].data.array; - int32_t count = l.offset + l.count, offset = -1; - for (; op_ctx->index < count; op_ctx->index ++) { - const char *name = names[op_ctx->index].value; - bool result = strstr(name, match); - if (is_neq) { - result = !result; - } - - if (!result) { - if (offset != -1) { - break; - } - } else { - if (offset == -1) { - offset = op_ctx->index; - } - } - } - - if (offset == -1) { - ctx->vars[first_var].range = op_ctx->range; - return false; - } - - ctx->vars[first_var].range.offset = offset; - ctx->vars[first_var].range.count = (op_ctx->index - offset); - return true; -} - -static -bool flecs_rule_pred_eq_match( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - return flecs_rule_pred_match(op, redo, ctx, false); -} - -static -bool flecs_rule_pred_neq_match( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - return flecs_rule_pred_match(op, redo, ctx, true); -} - -static -bool flecs_rule_pred_neq( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - uint64_t written = ctx->written[ctx->op_index]; (void)written; - ecs_assert(flecs_ref_is_written(op, &op->second, EcsRuleSecond, written), - ECS_INTERNAL_ERROR, - "invalid instruction sequence: uninitialized neq operand"); - - ecs_table_range_t r = flecs_rule_get_range( - op, &op->second, EcsRuleSecond, ctx); - return flecs_rule_pred_neq_w_range(op, redo, ctx, r); -} - -static -bool flecs_rule_pred_neq_name( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - const char *name = flecs_rule_name_arg(op, ctx); - ecs_entity_t e = ecs_lookup_fullpath(ctx->world, name); - if (!e) { - /* Entity doesn't exist */ - return true && !redo; - } - - ecs_table_range_t r = flecs_range_from_entity(e, ctx); - return flecs_rule_pred_neq_w_range(op, redo, ctx, r); -} - -static -bool flecs_rule_lookup( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (redo) { - return false; - } - - const ecs_rule_t *rule = ctx->rule; - ecs_entity_t first = flecs_rule_var_get_entity(op->first.var, ctx); - ecs_rule_var_t *var = &rule->vars[op->src.var]; - - ecs_entity_t result = ecs_lookup_path_w_sep(ctx->world, first, var->lookup, - NULL, NULL, false); - if (!result) { - flecs_rule_var_set_entity(op, op->src.var, EcsWildcard, ctx); - return false; - } - - flecs_rule_var_set_entity(op, op->src.var, result, ctx); - - return true; -} - -static -bool flecs_rule_setvars( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; - - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; - ecs_var_id_t *src_vars = rule->src_vars; - ecs_iter_t *it = ctx->it; - - if (redo) { - return false; - } - - int32_t i; - ecs_flags32_t source_set = *ctx->source_set; - for (i = 0; i < filter->field_count; i ++) { - ecs_var_id_t var_id = src_vars[i]; - if (!var_id) { - continue; - } - - if (source_set & (1u << i)) { - continue; - } - - it->sources[i] = flecs_rule_var_get_entity(var_id, ctx); - } - - return true; -} - -static -bool flecs_rule_setthis( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_setthis_ctx_t *op_ctx = flecs_op_ctx(ctx, setthis); - ecs_var_t *vars = ctx->vars; - ecs_var_t *this_var = &vars[op->first.var]; - - if (!redo) { - /* Save values so we can restore them later */ - op_ctx->range = vars[0].range; - - /* Constrain This table variable to a single entity from the table */ - vars[0].range = flecs_range_from_entity(this_var->entity, ctx); - vars[0].entity = this_var->entity; - return true; - } else { - /* Restore previous values, so that instructions that are operating on - * the table variable use all the entities in the table. */ - vars[0].range = op_ctx->range; - vars[0].entity = 0; - return false; - } -} - -static -bool flecs_rule_setfixed( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; - ecs_iter_t *it = ctx->it; - - if (redo) { - return false; - } - - int32_t i; - for (i = 0; i < filter->term_count; i ++) { - ecs_term_t *term = &filter->terms[i]; - ecs_term_id_t *src = &term->src; - if (src->flags & EcsIsEntity) { - it->sources[term->field_index] = src->id; - } - } - - return true; -} - -static -bool flecs_rule_setids( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; - ecs_iter_t *it = ctx->it; - - if (redo) { - return false; - } - - int32_t i; - for (i = 0; i < filter->term_count; i ++) { - ecs_term_t *term = &filter->terms[i]; - it->ids[term->field_index] = term->id; - } - - return true; -} - -static -bool flecs_rule_setid( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (redo) { - return false; - } - - ecs_assert(op->field_index != -1, ECS_INTERNAL_ERROR, NULL); - ctx->it->ids[op->field_index] = op->first.entity; - return true; -} - -/* Check if entity is stored in table */ -static -bool flecs_rule_contain( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (redo) { - return false; - } - - ecs_var_id_t src_id = op->src.var; - ecs_var_id_t first_id = op->first.var; - - ecs_table_t *table = flecs_rule_var_get_table(src_id, ctx); - - ecs_entity_t e = flecs_rule_var_get_entity(first_id, ctx); - return table == ecs_get_table(ctx->world, e); -} - -/* Check if first and second id of pair from last operation are the same */ -static -bool flecs_rule_pair_eq( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (redo) { - return false; - } - - ecs_iter_t *it = ctx->it; - ecs_id_t id = it->ids[op->field_index]; - return ECS_PAIR_FIRST(id) == ECS_PAIR_SECOND(id); -} - -static -bool flecs_rule_select_or( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - ecs_iter_t *it = ctx->it; - ecs_rule_iter_t *rit = &it->priv.iter.rule; - ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); - - ecs_rule_lbl_t first = flecs_itolbl(ctx->op_index + 1); - if (!redo) { - op_ctx->op_index = first; - } - - const ecs_rule_op_t *cur = &rit->ops[op_ctx->op_index]; - bool result = false; - - /* Evaluate operations in OR chain one by one. */ - - do { - ctx->op_index = op_ctx->op_index; - ctx->written[op_ctx->op_index] = op->written; - - result = flecs_rule_dispatch(cur, redo, ctx); - if (result) { - ctx->written[op_ctx->op_index] |= cur->written; - - /* If a previous operation in the OR chain returned a result for the - * same matched source, skip it so we don't yield for each matching - * element in the chain. */ - ecs_rule_lbl_t prev_index; - for (prev_index = first; prev_index < op_ctx->op_index; - prev_index ++) - { - const ecs_rule_op_t *prev = &rit->ops[prev_index]; - ctx->op_index = prev_index; - ctx->written[prev_index] = ctx->written[op_ctx->op_index]; - if (flecs_rule_dispatch(prev, false, ctx)) { - break; - } - } - - if (prev_index != op_ctx->op_index) { - /* Duplicate match was found, redo search */ - redo = true; - continue; - } - break; - } - - /* No result was found, go to next operation in chain */ - op_ctx->op_index ++; - cur = &rit->ops[op_ctx->op_index]; - redo = false; - } while (cur->kind != EcsRuleEnd); - - return result; -} - -static -bool flecs_rule_run_block( - bool redo, - ecs_rule_run_ctx_t *ctx, - ecs_rule_ctrl_ctx_t *op_ctx) -{ - ecs_iter_t *it = ctx->it; - ecs_rule_iter_t *rit = &it->priv.iter.rule; - - if (!redo) { - op_ctx->op_index = flecs_itolbl(ctx->op_index + 1); - } else if (ctx->rit->ops[op_ctx->op_index].kind == EcsRuleEnd) { - return false; - } - - ctx->written[ctx->op_index + 1] = ctx->written[ctx->op_index]; - - return flecs_rule_run_until( - redo, ctx, rit->ops, ctx->op_index, op_ctx->op_index, EcsRuleEnd); -} - -static -bool flecs_rule_with_or( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); - - bool result = flecs_rule_run_block(redo, ctx, op_ctx); - if (result) { - /* If a match was found, no need to keep searching for this source */ - op_ctx->op_index = op->next; - } - - return result; -} - -static -bool flecs_rule_or( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (op->flags & (EcsRuleIsVar << EcsRuleSrc)) { - uint64_t written = ctx->written[ctx->op_index]; - if (!(written & (1ull << op->src.var))) { - return flecs_rule_select_or(op, redo, ctx); - } - } - - return flecs_rule_with_or(op, redo, ctx); -} - -static -bool flecs_rule_run_block_w_reset( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); - bool result = flecs_rule_run_block(redo, ctx, op_ctx); - if (!result) { - flecs_rule_reset_after_block(op, ctx, op_ctx); - } - return result; -} - -static -bool flecs_rule_not( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (redo) { - return false; - } - - return !flecs_rule_run_block_w_reset(op, redo, ctx); -} - -static -bool flecs_rule_optional( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - bool result = flecs_rule_run_block_w_reset(op, redo, ctx); - if (!redo) { - return true; /* Return at least once */ - } else { - return result; - } -} - -static -bool flecs_rule_eval_if( - const ecs_rule_op_t *op, - ecs_rule_run_ctx_t *ctx, - const ecs_rule_ref_t *ref, - ecs_flags16_t ref_kind) -{ - if (flecs_rule_ref_flags(op->flags, ref_kind) == EcsRuleIsVar) { - if (ctx->vars[ref->var].entity == EcsWildcard) { - ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); - flecs_rule_reset_after_block(op, ctx, op_ctx); - return false; - } - } - return true; -} - -static -bool flecs_rule_if( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (!redo) { - if (!flecs_rule_eval_if(op, ctx, &op->src, EcsRuleSrc) || - !flecs_rule_eval_if(op, ctx, &op->first, EcsRuleFirst) || - !flecs_rule_eval_if(op, ctx, &op->second, EcsRuleSecond)) - { - return true; - } - } - - return flecs_rule_run_block_w_reset(op, redo, ctx); -} - -static -bool flecs_rule_end( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; (void)ctx; - return !redo; -} - -static -bool flecs_rule_populate( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; - if (!redo) { - ecs_iter_t *it = ctx->it; - if (it->flags & EcsIterNoData) { - return true; - } - - ECS_BIT_CLEAR(it->flags, EcsIterHasShared); - - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; - int32_t i, field_count = filter->field_count; - ecs_flags64_t data_fields = filter->data_fields; - ecs_table_range_t *range = &ctx->vars[0].range; - ecs_table_t *table = range->table; - if (table && !range->count) { - range->count = ecs_table_count(table); - } - - for (i = 0; i < field_count; i ++) { - if (!(data_fields & (1llu << i))) { - continue; - } - - int32_t index = it->columns[i]; - ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); - if (!index) { - continue; - } - - ecs_entity_t src = it->sources[i]; - if (!src) { - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - if (range->count && table->column_map) { - int32_t column = table->column_map[index - 1]; - if (column != -1) { - it->ptrs[i] = ECS_ELEM( - table->data.columns[column].data.array, - it->sizes[i], - range->offset); - continue; - } - } - } else { - ecs_record_t *r = flecs_entities_get(ctx->world, src); - ecs_table_t *src_table = r->table; - if (src_table->column_map) { - int32_t column = src_table->column_map[index - 1]; - if (column != -1) { - it->ptrs[i] = ecs_vec_get( - &src_table->data.columns[column].data, - it->sizes[i], - ECS_RECORD_TO_ROW(r->row)); - ECS_BIT_SET(it->flags, EcsIterHasShared); - continue; - } - } - } - } - - return true; - } else { - return false; - } -} - -static -bool flecs_rule_populate_self( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; - if (!redo) { - const ecs_rule_t *rule = ctx->rule; - const ecs_filter_t *filter = &rule->filter; - int32_t i, field_count = filter->field_count; - ecs_flags64_t data_fields = filter->data_fields; - ecs_iter_t *it = ctx->it; - - ecs_table_range_t *range = &ctx->vars[0].range; - ecs_table_t *table = range->table; - if (!table->column_map) { - return true; - } - - if (!ecs_table_count(table)) { - return true; - } - - for (i = 0; i < field_count; i ++) { - if (!(data_fields & (1llu << i))) { - continue; - } - - int32_t index = it->columns[i]; - ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); /* Only owned */ - if (!index) { - continue; - } - - int32_t column = table->column_map[index - 1]; - if (column != -1) { - it->ptrs[i] = ECS_ELEM( - table->data.columns[column].data.array, - it->sizes[i], - range->offset); - } - } - - return true; - } else { - return false; - } -} - -static -bool flecs_rule_dispatch( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - switch(op->kind) { - case EcsRuleAnd: return flecs_rule_and(op, redo, ctx); - case EcsRuleAndId: return flecs_rule_and_id(op, redo, ctx); - case EcsRuleAndAny: return flecs_rule_and_any(op, redo, ctx); - case EcsRuleTriv: return flecs_rule_triv(op, redo, ctx); - case EcsRuleTrivData: return flecs_rule_triv_data(op, redo, ctx); - case EcsRuleTrivWildcard: return flecs_rule_triv_wildcard(op, redo, ctx); - case EcsRuleSelectAny: return flecs_rule_select_any(op, redo, ctx); - case EcsRuleUp: return flecs_rule_up(op, redo, ctx); - case EcsRuleUpId: return flecs_rule_up_id(op, redo, ctx); - case EcsRuleSelfUp: return flecs_rule_self_up(op, redo, ctx); - case EcsRuleSelfUpId: return flecs_rule_self_up_id(op, redo, ctx); - case EcsRuleWith: return flecs_rule_with(op, redo, ctx); - case EcsRuleTrav: return flecs_rule_trav(op, redo, ctx); - case EcsRuleIdsRight: return flecs_rule_idsright(op, redo, ctx); - case EcsRuleIdsLeft: return flecs_rule_idsleft(op, redo, ctx); - case EcsRuleEach: return flecs_rule_each(op, redo, ctx); - case EcsRuleStore: return flecs_rule_store(op, redo, ctx); - case EcsRuleReset: return flecs_rule_reset(op, redo, ctx); - case EcsRuleOr: return flecs_rule_or(op, redo, ctx); - case EcsRuleOptional: return flecs_rule_optional(op, redo, ctx); - case EcsRuleIf: return flecs_rule_if(op, redo, ctx); - case EcsRuleEnd: return flecs_rule_end(op, redo, ctx); - case EcsRuleNot: return flecs_rule_not(op, redo, ctx); - case EcsRulePredEq: return flecs_rule_pred_eq(op, redo, ctx); - case EcsRulePredNeq: return flecs_rule_pred_neq(op, redo, ctx); - case EcsRulePredEqName: return flecs_rule_pred_eq_name(op, redo, ctx); - case EcsRulePredNeqName: return flecs_rule_pred_neq_name(op, redo, ctx); - case EcsRulePredEqMatch: return flecs_rule_pred_eq_match(op, redo, ctx); - case EcsRulePredNeqMatch: return flecs_rule_pred_neq_match(op, redo, ctx); - case EcsRuleLookup: return flecs_rule_lookup(op, redo, ctx); - case EcsRuleSetVars: return flecs_rule_setvars(op, redo, ctx); - case EcsRuleSetThis: return flecs_rule_setthis(op, redo, ctx); - case EcsRuleSetFixed: return flecs_rule_setfixed(op, redo, ctx); - case EcsRuleSetIds: return flecs_rule_setids(op, redo, ctx); - case EcsRuleSetId: return flecs_rule_setid(op, redo, ctx); - case EcsRuleContain: return flecs_rule_contain(op, redo, ctx); - case EcsRulePairEq: return flecs_rule_pair_eq(op, redo, ctx); - case EcsRulePopulate: return flecs_rule_populate(op, redo, ctx); - case EcsRulePopulateSelf: return flecs_rule_populate_self(op, redo, ctx); - case EcsRuleYield: return false; - case EcsRuleNothing: return false; - } - return false; -} - -static -bool flecs_rule_run_until( - bool redo, - ecs_rule_run_ctx_t *ctx, - const ecs_rule_op_t *ops, - ecs_rule_lbl_t first, - ecs_rule_lbl_t cur, - ecs_rule_op_kind_t until) -{ - ecs_assert(first >= -1, ECS_INTERNAL_ERROR, NULL); - ecs_assert(cur >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(cur > first, ECS_INTERNAL_ERROR, NULL); - - ctx->op_index = cur; - const ecs_rule_op_t *op = &ops[ctx->op_index]; - ecs_assert(op->kind != until, ECS_INTERNAL_ERROR, NULL); - - do { - #ifdef FLECS_DEBUG - ctx->rit->profile[ctx->op_index].count[redo] ++; - #endif - - bool result = flecs_rule_dispatch(op, redo, ctx); - cur = (&op->prev)[result]; - redo = cur < ctx->op_index; - - if (!redo) { - ctx->written[cur] |= ctx->written[ctx->op_index] | op->written; - } - - ctx->op_index = cur; - op = &ops[ctx->op_index]; - - if (cur <= first) { - return false; - } - if (op->kind == until) { - return true; - } - } while (true); - - return false; -} - -static -void flecs_rule_iter_init( - ecs_rule_run_ctx_t *ctx) -{ - ecs_assert(ctx->written != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_iter_t *it = ctx->it; - - const ecs_rule_t *rule = ctx->rule; - ecs_flags64_t it_written = it->constrained_vars; - ctx->written[0] = it_written; - if (it_written && ctx->rule->src_vars) { - /* If variables were constrained, check if there are any table - * variables that have a constrained entity variable. */ - ecs_var_t *vars = ctx->vars; - int32_t i, count = rule->filter.field_count; - for (i = 0; i < count; i ++) { - ecs_var_id_t var_id = rule->src_vars[i]; - ecs_rule_var_t *var = &rule->vars[var_id]; - - if (!(it_written & (1ull << var_id)) || - (var->kind == EcsVarTable) || (var->table_id == EcsVarNone)) - { - continue; - } - - /* Initialize table variable with constrained entity variable */ - ecs_var_t *tvar = &vars[var->table_id]; - tvar->range = flecs_range_from_entity(vars[var_id].entity, ctx); - ctx->written[0] |= (1ull << var->table_id); /* Mark as written */ - } - } - - ecs_flags32_t flags = rule->filter.flags; - if (flags & EcsFilterIsTrivial) { - if ((flags & EcsFilterMatchOnlySelf) || - !flecs_table_cache_count(&ctx->world->idr_isa_wildcard->cache)) - { - if (it_written) { - it->offset = ctx->vars[0].range.offset; - it->count = ctx->vars[0].range.count; - if (!it->count) { - ecs_assert(!it->offset, ECS_INVALID_PARAMETER, NULL); - it->count = ecs_table_count(ctx->vars[0].range.table); - } - - it->flags |= EcsIterTrivialTest; - flecs_rule_setids(&rule->ops[0], false, ctx); - } else { - if (flags & EcsFilterHasWildcards) { - it->flags |= EcsIterTrivialSearchWildcard; - flecs_rule_setids(&rule->ops[0], false, ctx); - } else if (flags & EcsFilterNoData) { - it->flags |= EcsIterTrivialSearchNoData; - flecs_rule_setids(&rule->ops[0], false, ctx); - } else { - it->flags |= EcsIterTrivialSearch; - flecs_rule_setids(&rule->ops[0], false, ctx); - } - } - } - } - - flecs_iter_validate(it); -} - -bool ecs_rule_next_instanced( - ecs_iter_t *it) -{ - ecs_assert(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(it->next == ecs_rule_next, ECS_INVALID_PARAMETER, NULL); - - ecs_rule_iter_t *rit = &it->priv.iter.rule; - ecs_rule_run_ctx_t ctx; - ctx.world = it->real_world; - ctx.rule = rit->rule; - ctx.it = it; - ctx.vars = rit->vars; - ctx.rule_vars = rit->rule_vars; - ctx.written = rit->written; - ctx.op_ctx = rit->op_ctx; - ctx.source_set = &rit->source_set; - ctx.rit = rit; - const ecs_rule_op_t *ops = rit->ops; - - bool redo = true; - if (!(it->flags & EcsIterIsValid)) { - ecs_assert(ctx.rule != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_rule_iter_init(&ctx); - redo = false; - } else { - it->frame_offset += it->count; - } - - /* Specialized iterator modes for trivial queries */ - if (it->flags & EcsIterTrivialSearch) { - ecs_rule_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; - int32_t fields = ctx.rule->filter.term_count; - if (!flecs_rule_trivial_search(ctx.rule, &ctx, op_ctx, !redo, fields)) { - goto done; - } - it->table = ctx.vars[0].range.table; - it->count = ecs_table_count(it->table); - it->entities = flecs_table_entities_array(it->table); - return true; - } else if (it->flags & EcsIterTrivialSearchNoData) { - ecs_rule_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; - int32_t fields = ctx.rule->filter.term_count; - if (!flecs_rule_trivial_search_nodata(ctx.rule, &ctx, op_ctx, !redo, fields)) { - goto done; - } - it->table = ctx.vars[0].range.table; - it->count = ecs_table_count(it->table); - it->entities = flecs_table_entities_array(it->table); - return true; - } else if (it->flags & EcsIterTrivialTest) { - int32_t fields = ctx.rule->filter.term_count; - if (!flecs_rule_trivial_test(ctx.rule, &ctx, !redo, fields)) { - goto done; - } - return true; - } else if (it->flags & EcsIterTrivialSearchWildcard) { - ecs_rule_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; - int32_t fields = ctx.rule->filter.term_count; - if (!flecs_rule_trivial_search_w_wildcards(ctx.rule, &ctx, op_ctx, !redo, fields)) { - goto done; - } - it->table = ctx.vars[0].range.table; - it->count = ecs_table_count(it->table); - it->entities = flecs_table_entities_array(it->table); - return true; - } - - /* Default iterator mode */ - if (flecs_rule_run_until(redo, &ctx, ops, -1, rit->op, EcsRuleYield)) { - ecs_assert(ops[ctx.op_index].kind == EcsRuleYield, - ECS_INTERNAL_ERROR, NULL); - ecs_table_range_t *range = &ctx.vars[0].range; - ecs_table_t *table = range->table; - int32_t count = range->count; - if (table) { - if (!count) { - count = ecs_table_count(table); - } - it->table = table; - it->offset = range->offset; - it->count = count; - it->entities = ECS_ELEM_T( - table->data.entities.array, ecs_entity_t, it->offset); - } else if (count == 1) { - it->count = 1; - it->entities = &ctx.vars[0].entity; - } - - rit->op = flecs_itolbl(ctx.op_index - 1); - return true; - } - -done: - ecs_iter_fini(it); - return false; -} - -bool ecs_rule_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_rule_next, ECS_INVALID_PARAMETER, NULL); - - if (flecs_iter_next_row(it)) { - return true; - } - - return flecs_iter_next_instanced(it, ecs_rule_next_instanced(it)); -error: - return false; -} - -static -void flecs_rule_iter_fini_ctx( - ecs_iter_t *it, - ecs_rule_iter_t *rit) -{ - const ecs_rule_t *rule = rit->rule; - int32_t i, count = rule->op_count; - ecs_rule_op_t *ops = rule->ops; - ecs_rule_op_ctx_t *ctx = rit->op_ctx; - ecs_allocator_t *a = flecs_rule_get_allocator(it); - - for (i = 0; i < count; i ++) { - ecs_rule_op_t *op = &ops[i]; - switch(op->kind) { - case EcsRuleTrav: - flecs_rule_trav_cache_fini(a, &ctx[i].is.trav.cache); - break; - case EcsRuleUp: - case EcsRuleSelfUp: - case EcsRuleUpId: - case EcsRuleSelfUpId: { - ecs_trav_up_cache_t *cache = &ctx[i].is.up.cache; - if (cache->dir == EcsDown) { - flecs_rule_down_cache_fini(a, cache); - } else { - flecs_rule_up_cache_fini(cache); - } - break; - } - default: - break; - } - } -} - -static -void flecs_rule_iter_fini( - ecs_iter_t *it) -{ - ecs_rule_iter_t *rit = &it->priv.iter.rule; - ecs_assert(rit->rule != NULL, ECS_INVALID_OPERATION, NULL); - ecs_poly_assert(rit->rule, ecs_rule_t); - int32_t op_count = rit->rule->op_count; - int32_t var_count = rit->rule->var_count; - -#ifdef FLECS_DEBUG - if (it->flags & EcsIterProfile) { - char *str = ecs_rule_str_w_profile(rit->rule, it); - printf("%s\n", str); - ecs_os_free(str); - } - - flecs_iter_free_n(rit->profile, ecs_rule_op_profile_t, op_count); -#endif - - flecs_rule_iter_fini_ctx(it, rit); - flecs_iter_free_n(rit->vars, ecs_var_t, var_count); - flecs_iter_free_n(rit->written, ecs_write_flags_t, op_count); - flecs_iter_free_n(rit->op_ctx, ecs_rule_op_ctx_t, op_count); - rit->vars = NULL; - rit->written = NULL; - rit->op_ctx = NULL; - rit->rule = NULL; -} - -ecs_iter_t ecs_rule_iter( - const ecs_world_t *world, - const ecs_rule_t *rule) -{ - ecs_iter_t it = {0}; - ecs_rule_iter_t *rit = &it.priv.iter.rule; - ecs_check(rule != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_run_aperiodic(rule->filter.world, EcsAperiodicEmptyTables); - - int32_t i, var_count = rule->var_count, op_count = rule->op_count; - it.world = ECS_CONST_CAST(ecs_world_t*, world); - it.real_world = rule->filter.world; - it.terms = rule->filter.terms; - it.next = ecs_rule_next; - it.fini = flecs_rule_iter_fini; - it.field_count = rule->filter.field_count; - it.sizes = rule->filter.sizes; - flecs_filter_apply_iter_flags(&it, &rule->filter); - - flecs_iter_init(world, &it, - flecs_iter_cache_ids | - flecs_iter_cache_columns | - flecs_iter_cache_sources | - flecs_iter_cache_ptrs); - - rit->rule = rule; - rit->rule_vars = rule->vars; - rit->ops = rule->ops; - rit->source_set = 0; - if (var_count) { - rit->vars = flecs_iter_calloc_n(&it, ecs_var_t, var_count); - } - if (op_count) { - rit->written = flecs_iter_calloc_n(&it, ecs_write_flags_t, op_count); - rit->op_ctx = flecs_iter_calloc_n(&it, ecs_rule_op_ctx_t, op_count); - } - -#ifdef FLECS_DEBUG - rit->profile = flecs_iter_calloc_n(&it, ecs_rule_op_profile_t, op_count); -#endif - - for (i = 1; i < var_count; i ++) { - rit->vars[i].entity = EcsWildcard; - } - - it.variables = rit->vars; - it.variable_count = rule->var_pub_count; - it.variable_names = rule->var_names; - -error: - return it; -} - -#endif diff --git a/vendors/flecs/src/addons/rules/rules.h b/vendors/flecs/src/addons/rules/rules.h deleted file mode 100644 index 6b0ab59c3..000000000 --- a/vendors/flecs/src/addons/rules/rules.h +++ /dev/null @@ -1,433 +0,0 @@ - /** - * @file addons/rules/rules.h - * @brief Internal types and functions for rules addon. - */ - -#include "../../private_api.h" - -#ifdef FLECS_RULES - -typedef uint8_t ecs_var_id_t; -typedef int16_t ecs_rule_lbl_t; -typedef ecs_flags64_t ecs_write_flags_t; - -#define EcsRuleMaxVarCount (64) -#define EcsVarNone ((ecs_var_id_t)-1) -#define EcsThisName "this" - -/* -- Variable types -- */ -typedef enum { - EcsVarEntity, /* Variable that stores an entity id */ - EcsVarTable, /* Variable that stores a table */ - EcsVarAny /* Used when requesting either entity or table var */ -} ecs_var_kind_t; - -typedef struct ecs_rule_var_t { - int8_t kind; /* variable kind (EcsVarEntity or EcsVarTable) */ - bool anonymous; /* variable is anonymous */ - ecs_var_id_t id; /* variable id */ - ecs_var_id_t table_id; /* id to table variable, if any */ - ecs_var_id_t base_id; /* id to base entity variable, for lookups */ - const char *name; /* variable name */ - const char *lookup; /* Lookup string for variable */ -#ifdef FLECS_DEBUG - const char *label; /* for debugging */ -#endif -} ecs_rule_var_t; - -/* -- Instruction kinds -- */ -typedef enum { - EcsRuleAnd, /* And operator: find or match id against variable source */ - EcsRuleAndId, /* And operator for fixed id (no wildcards/variables) */ - EcsRuleAndAny, /* And operator with support for matching Any src/id */ - EcsRuleTriv, /* Trivial search */ - EcsRuleTrivData, /* Trivial search with setting data fields */ - EcsRuleTrivWildcard, /* Trivial search with (exclusive) wildcard ids */ - EcsRuleSelectAny, /* Dedicated instruction for _ queries where the src is unknown */ - EcsRuleUp, /* Up traversal */ - EcsRuleUpId, /* Up traversal for fixed id (like AndId) */ - EcsRuleSelfUp, /* Self|up traversal */ - EcsRuleSelfUpId, /* Self|up traversal for fixed id (like AndId) */ - EcsRuleWith, /* Match id against fixed or variable source */ - EcsRuleTrav, /* Support for transitive/reflexive queries */ - EcsRuleIdsRight, /* Find ids in use that match (R, *) wildcard */ - EcsRuleIdsLeft, /* Find ids in use that match (*, T) wildcard */ - EcsRuleEach, /* Iterate entities in table, populate entity variable */ - EcsRuleStore, /* Store table or entity in variable */ - EcsRuleReset, /* Reset value of variable to wildcard (*) */ - EcsRuleOr, /* Or operator */ - EcsRuleOptional, /* Optional operator */ - EcsRuleIf, /* Conditional execution */ - EcsRuleNot, /* Sets iterator state after term was not matched */ - EcsRuleEnd, /* End of control flow block */ - EcsRulePredEq, /* Test if variable is equal to, or assign to if not set */ - EcsRulePredNeq, /* Test if variable is not equal to */ - EcsRulePredEqName, /* Same as EcsRulePredEq but with matching by name */ - EcsRulePredNeqName, /* Same as EcsRulePredNeq but with matching by name */ - EcsRulePredEqMatch, /* Same as EcsRulePredEq but with fuzzy matching by name */ - EcsRulePredNeqMatch, /* Same as EcsRulePredNeq but with fuzzy matching by name */ - EcsRuleLookup, /* Lookup relative to variable */ - EcsRuleSetVars, /* Populate it.sources from variables */ - EcsRuleSetThis, /* Populate This entity variable */ - EcsRuleSetFixed, /* Set fixed source entity ids */ - EcsRuleSetIds, /* Set fixed (component) ids */ - EcsRuleSetId, /* Set id if not set */ - EcsRuleContain, /* Test if table contains entity */ - EcsRulePairEq, /* Test if both elements of pair are the same */ - EcsRulePopulate, /* Populate any data fields */ - EcsRulePopulateSelf, /* Populate only self (owned) data fields */ - EcsRuleYield, /* Yield result back to application */ - EcsRuleNothing /* Must be last */ -} ecs_rule_op_kind_t; - -/* Op flags to indicate if ecs_rule_ref_t is entity or variable */ -#define EcsRuleIsEntity (1 << 0) -#define EcsRuleIsVar (1 << 1) -#define EcsRuleIsSelf (1 << 6) - -/* Op flags used to shift EcsRuleIsEntity and EcsRuleIsVar */ -#define EcsRuleSrc 0 -#define EcsRuleFirst 2 -#define EcsRuleSecond 4 - -/* References to variable or entity */ -typedef union { - ecs_var_id_t var; - ecs_entity_t entity; -} ecs_rule_ref_t; - -/* Query instruction */ -typedef struct ecs_rule_op_t { - uint8_t kind; /* Instruction kind */ - ecs_flags8_t flags; /* Flags storing whether 1st/2nd are variables */ - int8_t field_index; /* Query field corresponding with operation */ - int8_t term_index; /* Query term corresponding with operation */ - ecs_rule_lbl_t prev; /* Backtracking label (no data) */ - ecs_rule_lbl_t next; /* Forwarding label. Must come after prev */ - ecs_rule_lbl_t other; /* Misc register used for control flow */ - ecs_flags16_t match_flags; /* Flags that modify matching behavior */ - ecs_rule_ref_t src; - ecs_rule_ref_t first; - ecs_rule_ref_t second; - ecs_flags64_t written; /* Bitset with variables written by op */ -} ecs_rule_op_t; - - /* And context */ -typedef struct { - ecs_id_record_t *idr; - ecs_table_cache_iter_t it; - int16_t column; - int16_t remaining; -} ecs_rule_and_ctx_t; - -/* Down traversal cache (for resolving up queries w/unknown source) */ -typedef struct { - ecs_table_t *table; - bool leaf; /* Table owns and inherits id (for Up queries without Self) */ -} ecs_trav_down_elem_t; - -typedef struct { - ecs_vec_t elems; /* vector */ - bool ready; -} ecs_trav_down_t; - -typedef struct { - ecs_entity_t src; - ecs_id_t id; - int32_t column; - bool ready; -} ecs_trav_up_t; - -typedef struct { - ecs_map_t src; /* map or map */ - ecs_id_t with; - ecs_flags32_t dir; -} ecs_trav_up_cache_t; - -/* And up context */ -typedef struct { - ecs_rule_and_ctx_t and; - ecs_table_t *table; - int32_t row; - int32_t end; - ecs_entity_t trav; - ecs_id_t with; - ecs_id_t matched; - ecs_id_record_t *idr_with; - ecs_id_record_t *idr_trav; - ecs_trav_down_t *down; - int32_t cache_elem; - ecs_trav_up_cache_t cache; -} ecs_rule_up_ctx_t; - -/* Cache for storing results of upward/downward "all" traversal. This type of - * traversal iterates and caches the entire tree. */ -typedef struct { - ecs_entity_t entity; - ecs_id_record_t *idr; - int32_t column; -} ecs_trav_elem_t; - -typedef struct { - ecs_id_t id; - ecs_id_record_t *idr; - ecs_vec_t entities; - bool up; -} ecs_trav_cache_t; - -/* Trav context */ -typedef struct { - ecs_rule_and_ctx_t and; - int32_t index; - int32_t offset; - int32_t count; - ecs_trav_cache_t cache; - bool yield_reflexive; -} ecs_rule_trav_ctx_t; - - /* Eq context */ -typedef struct { - ecs_table_range_t range; - int32_t index; - int16_t name_col; - bool redo; -} ecs_rule_eq_ctx_t; - - /* Each context */ -typedef struct { - int32_t row; -} ecs_rule_each_ctx_t; - - /* Setthis context */ -typedef struct { - ecs_table_range_t range; -} ecs_rule_setthis_ctx_t; - -/* Ids context */ -typedef struct { - ecs_id_record_t *cur; -} ecs_rule_ids_ctx_t; - -/* Control flow context */ -typedef struct { - ecs_rule_lbl_t op_index; - ecs_id_t field_id; -} ecs_rule_ctrl_ctx_t; - -/* Trivial context */ -typedef struct { - ecs_table_cache_iter_t it; - const ecs_table_record_t *tr; -} ecs_rule_trivial_ctx_t; - -typedef struct ecs_rule_op_ctx_t { - union { - ecs_rule_and_ctx_t and; - ecs_rule_up_ctx_t up; - ecs_rule_trav_ctx_t trav; - ecs_rule_ids_ctx_t ids; - ecs_rule_eq_ctx_t eq; - ecs_rule_each_ctx_t each; - ecs_rule_setthis_ctx_t setthis; - ecs_rule_ctrl_ctx_t ctrl; - ecs_rule_trivial_ctx_t trivial; - } is; -} ecs_rule_op_ctx_t; - -typedef struct { - /* Labels used for control flow */ - ecs_rule_lbl_t lbl_query; /* Used to find the op that does the actual searching */ - ecs_rule_lbl_t lbl_begin; - ecs_rule_lbl_t lbl_cond_eval; - ecs_write_flags_t cond_written_or; /* Cond written flags at start of or chain */ - bool in_or; /* Whether we're in an or chain */ -} ecs_rule_compile_ctrlflow_t; - -/* Rule compiler state */ -typedef struct { - ecs_vec_t *ops; - ecs_write_flags_t written; /* Bitmask to check which variables have been written */ - ecs_write_flags_t cond_written; /* Track conditional writes (optional operators) */ - - /* Maintain control flow per scope */ - ecs_rule_compile_ctrlflow_t ctrlflow[FLECS_QUERY_SCOPE_NESTING_MAX]; - ecs_rule_compile_ctrlflow_t *cur; /* Current scope */ - - int32_t scope; /* Nesting level of query scopes */ - ecs_flags32_t scope_is_not; /* Whether scope is prefixed with not */ -} ecs_rule_compile_ctx_t; - -/* Rule run state */ -typedef struct { - uint64_t *written; /* Bitset to check which variables have been written */ - ecs_rule_lbl_t op_index; /* Currently evaluated operation */ - ecs_var_t *vars; /* Variable storage */ - ecs_iter_t *it; /* Iterator */ - ecs_rule_op_ctx_t *op_ctx; /* Operation context (stack) */ - ecs_world_t *world; /* Reference to world */ - const ecs_rule_t *rule; /* Reference to rule */ - const ecs_rule_var_t *rule_vars; /* Reference to rule variable array */ - ecs_flags32_t *source_set; /* Whether ecs_iter_t::sources is written by instruction */ - ecs_rule_iter_t *rit; -} ecs_rule_run_ctx_t; - -typedef struct { - ecs_rule_var_t var; - const char *name; -} ecs_rule_var_cache_t; - -struct ecs_rule_t { - ecs_header_t hdr; /* Poly header */ - ecs_filter_t filter; /* Filter */ - - /* Variables */ - ecs_rule_var_t *vars; /* Variables */ - int32_t var_count; /* Number of variables */ - int32_t var_pub_count; /* Number of public variables */ - bool has_table_this; /* Does rule have [$this] */ - ecs_hashmap_t tvar_index; /* Name index for table variables */ - ecs_hashmap_t evar_index; /* Name index for entity variables */ - ecs_rule_var_cache_t vars_cache; /* For trivial rules with only This variables */ - char **var_names; /* Array with variable names for iterator */ - - ecs_var_id_t *src_vars; /* Array with ids to source variables for fields */ - ecs_rule_op_t *ops; /* Operations */ - int32_t op_count; /* Number of operations */ - - /* Mixins */ - ecs_iterable_t iterable; - ecs_poly_dtor_t dtor; - -#ifdef FLECS_DEBUG - int32_t var_size; /* Used for out of bounds check during compilation */ -#endif -}; - -/* Convert integer to label */ -ecs_rule_lbl_t flecs_itolbl( - int64_t val); - -/* Get ref flags (IsEntity) or IsVar) for ref (Src, First, Second) */ -ecs_flags16_t flecs_rule_ref_flags( - ecs_flags16_t flags, - ecs_flags16_t kind); - -/* Check if variable is written */ -bool flecs_rule_is_written( - ecs_var_id_t var_id, - uint64_t written); - -/* Check if ref is written (calls flecs_rule_is_written)*/ -bool flecs_ref_is_written( - const ecs_rule_op_t *op, - const ecs_rule_ref_t *ref, - ecs_flags16_t kind, - uint64_t written); - -/* Compile filter to list of operations */ -int flecs_rule_compile( - ecs_world_t *world, - ecs_stage_t *stage, - ecs_rule_t *rule); - -/* Get allocator from iterator */ -ecs_allocator_t* flecs_rule_get_allocator( - const ecs_iter_t *it); - - -/* Traversal cache for transitive queries. Finds all reachable entities by - * following a relationship */ - -/* Find all entities when traversing downwards */ -void flecs_rule_get_trav_down_cache( - const ecs_rule_run_ctx_t *ctx, - ecs_trav_cache_t *cache, - ecs_entity_t trav, - ecs_entity_t entity); - -/* Find all entities when traversing upwards */ -void flecs_rule_get_trav_up_cache( - const ecs_rule_run_ctx_t *ctx, - ecs_trav_cache_t *cache, - ecs_entity_t trav, - ecs_table_t *table); - -/* Free traversal cache */ -void flecs_rule_trav_cache_fini( - ecs_allocator_t *a, - ecs_trav_cache_t *cache); - -/* Traversal caches for up traversal. Enables searching upwards until an entity - * with the queried for id has been found. */ - -/* Traverse downwards from starting entity to find all tables for which the - * specified entity is the source of the queried for id ('with'). */ -ecs_trav_down_t* flecs_rule_get_down_cache( - const ecs_rule_run_ctx_t *ctx, - ecs_trav_up_cache_t *cache, - ecs_entity_t trav, - ecs_entity_t entity, - ecs_id_record_t *idr_with, - bool self); - -/* Free down traversal cache */ -void flecs_rule_down_cache_fini( - ecs_allocator_t *a, - ecs_trav_up_cache_t *cache); - -ecs_trav_up_t* flecs_rule_get_up_cache( - const ecs_rule_run_ctx_t *ctx, - ecs_trav_up_cache_t *cache, - ecs_table_t *table, - ecs_id_t with, - ecs_entity_t trav, - ecs_id_record_t *idr_with, - ecs_id_record_t *idr_trav); - -/* Free up traversal cache */ -void flecs_rule_up_cache_fini( - ecs_trav_up_cache_t *cache); - -/* Convert instruction kind to string */ -const char* flecs_rule_op_str( - uint16_t kind); - -/* Iterator for trivial queries. */ -bool flecs_rule_trivial_search( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t until); - -/* Iterator for trivial queries. */ -bool flecs_rule_trivial_search_nodata( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t until); - -/* Iterator for trivial queries with wildcard matching. */ -bool flecs_rule_trivial_search_w_wildcards( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t until); - -/* Trivial test for constrained $this. */ -bool flecs_rule_trivial_test( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - bool first, - int32_t term_count); - -/* Trivial test for constrained $this with wildcard matching. */ -bool flecs_rule_trivial_test_w_wildcards( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - bool first, - int32_t term_count); - -#endif diff --git a/vendors/flecs/src/addons/rules/trivial_iter.c b/vendors/flecs/src/addons/rules/trivial_iter.c deleted file mode 100644 index 17ca2a15f..000000000 --- a/vendors/flecs/src/addons/rules/trivial_iter.c +++ /dev/null @@ -1,254 +0,0 @@ -/** - * @file addons/rules/engine.c - * @brief Iterator for trivial queries. - */ - -#include "rules.h" - -#ifdef FLECS_RULES - -static -bool flecs_rule_trivial_init( - ecs_world_t *world, - const ecs_filter_t *filter) -{ - int32_t t, count = filter->term_count; - ecs_term_t *terms = filter->terms; - - for (t = 0; t < count; t ++) { - ecs_term_t *term = &terms[t]; - if (!term->idr) { - term->idr = flecs_id_record_get(world, term->id); - if (!term->idr) { - /* Id doesn't exist, so query can't match */ - return false; - } - } - } - - return true; -} - -bool flecs_rule_trivial_test( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - bool first, - int32_t term_count) -{ - if (first) { - const ecs_filter_t *filter = &rule->filter; - int32_t t; - ecs_term_t *terms = filter->terms; - ecs_iter_t *it = ctx->it; - - if (!flecs_rule_trivial_init(ctx->world, filter)) { - return false; - } - - ecs_table_t *table = ctx->vars[0].range.table; - ecs_assert(table != NULL, ECS_INVALID_OPERATION, NULL); - - for (t = 0; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; - const ecs_table_record_t *tr = flecs_id_record_get_table( - term->idr, table); - if (!tr) { - return false; - } - - it->columns[t] = tr->index + 1; - if (it->count && tr->column != -1) { - it->ptrs[t] = ecs_vec_get( - &table->data.columns[tr->column].data, - it->sizes[t], - it->offset); - } - } - - it->table = table; - it->entities = &flecs_table_entities_array(table)[it->offset]; - return true; - } else { - return false; - } -} - -static -bool flecs_rule_trivial_search_init( - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - const ecs_filter_t *filter, - bool first) -{ - if (first) { - ecs_term_t *terms = filter->terms; - if (!flecs_rule_trivial_init(ctx->world, filter)) { - return false; - } - - if (filter->flags & EcsFilterMatchEmptyTables) { - if (!flecs_table_cache_all_iter(&terms[0].idr->cache, &op_ctx->it)){ - return false; - } - } else { - if (!flecs_table_cache_iter(&terms[0].idr->cache, &op_ctx->it)) { - return false; - } - } - } - - return true; -} - -bool flecs_rule_trivial_search( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t term_count) -{ - const ecs_filter_t *filter = &rule->filter; - int32_t t; - ecs_term_t *terms = filter->terms; - ecs_iter_t *it = ctx->it; - - if (!flecs_rule_trivial_search_init(ctx, op_ctx, filter, first)) { - return false; - } - - do { - const ecs_table_record_t *tr = flecs_table_cache_next( - &op_ctx->it, ecs_table_record_t); - if (!tr) { - return false; - } - - ecs_table_t *table = tr->hdr.table; - if (table->flags & (EcsTableIsPrefab|EcsTableIsDisabled)) { - continue; - } - - for (t = 1; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; - const ecs_table_record_t *tr_with = flecs_id_record_get_table( - term->idr, table); - if (!tr_with) { - break; - } - - it->columns[t] = tr_with->index + 1; - if (tr_with->column != -1) { - it->ptrs[t] = ecs_vec_first( - &table->data.columns[tr_with->column].data); - } - } - - if (t == term_count) { - ctx->vars[0].range.table = table; - ctx->vars[0].range.count = 0; - ctx->vars[0].range.offset = 0; - it->columns[0] = tr->index + 1; - if (tr->column != -1) { - it->ptrs[0] = ecs_vec_first( - &table->data.columns[tr->column].data); - } - break; - } - } while (true); - - return true; -} - -bool flecs_rule_trivial_search_w_wildcards( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t term_count) -{ - bool result = flecs_rule_trivial_search( - rule, ctx, op_ctx, first, term_count); - if (result) { - ecs_iter_t *it = ctx->it; - ecs_table_t *table = ctx->vars[0].range.table; - int32_t t; - for (t = 0; t < term_count; t ++) { - it->ids[t] = table->type.array[it->columns[t] - 1]; - } - } - - return result; -} - -bool flecs_rule_trivial_test_w_wildcards( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - bool first, - int32_t term_count) -{ - bool result = flecs_rule_trivial_test( - rule, ctx, first, term_count); - if (result) { - ecs_iter_t *it = ctx->it; - ecs_table_t *table = ctx->vars[0].range.table; - int32_t t; - for (t = 0; t < term_count; t ++) { - it->ids[t] = table->type.array[it->columns[t] - 1]; - } - } - - return result; -} - -bool flecs_rule_trivial_search_nodata( - const ecs_rule_t *rule, - const ecs_rule_run_ctx_t *ctx, - ecs_rule_trivial_ctx_t *op_ctx, - bool first, - int32_t term_count) -{ - const ecs_filter_t *filter = &rule->filter; - int32_t t; - ecs_term_t *terms = filter->terms; - ecs_iter_t *it = ctx->it; - - if (!flecs_rule_trivial_search_init(ctx, op_ctx, filter, first)) { - return false; - } - - do { - const ecs_table_record_t *tr = flecs_table_cache_next( - &op_ctx->it, ecs_table_record_t); - if (!tr) { - return false; - } - - ecs_table_t *table = tr->hdr.table; - if (table->flags & (EcsTableIsPrefab|EcsTableIsDisabled)) { - continue; - } - - for (t = 1; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; - const ecs_table_record_t *tr_with = flecs_id_record_get_table( - term->idr, table); - if (!tr_with) { - break; - } - - it->columns[t] = tr_with->index + 1; - } - - if (t == term_count) { - ctx->vars[0].range.table = table; - ctx->vars[0].range.count = 0; - ctx->vars[0].range.offset = 0; - it->columns[0] = tr->index + 1; - break; - } - } while (true); - - return true; -} - -#endif diff --git a/vendors/flecs/src/addons/script/ast.c b/vendors/flecs/src/addons/script/ast.c new file mode 100644 index 000000000..e45bbf0b3 --- /dev/null +++ b/vendors/flecs/src/addons/script/ast.c @@ -0,0 +1,301 @@ +/** + * @file addons/script/ast.c + * @brief Script AST implementation. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" + +#define flecs_ast_strdup(parser, str)\ + (str ? flecs_strdup(&parser->script->allocator, str) : NULL) +#define flecs_ast_new(parser, T, kind)\ + (T*)flecs_ast_new_(parser, ECS_SIZEOF(T), kind) +#define flecs_ast_vec(parser, vec, T) \ + ecs_vec_init_t(&parser->script->allocator, &vec, T*, 0) +#define flecs_ast_append(parser, vec, T, node) \ + ecs_vec_append_t(&parser->script->allocator, &vec, T*)[0] = node + +static +void* flecs_ast_new_( + ecs_script_parser_t *parser, + ecs_size_t size, + ecs_script_node_kind_t kind) +{ + ecs_assert(parser->script != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_allocator_t *a = &parser->script->allocator; + ecs_script_node_t *result = flecs_calloc(a, size); + result->kind = kind; + result->pos = parser->pos; + return result; +} + +ecs_script_scope_t* flecs_script_scope_new( + ecs_script_parser_t *parser) +{ + ecs_script_scope_t *result = flecs_ast_new( + parser, ecs_script_scope_t, EcsAstScope); + flecs_ast_vec(parser, result->stmts, ecs_script_node_t); + return result; +} + +bool flecs_scope_is_empty( + ecs_script_scope_t *scope) +{ + return ecs_vec_count(&scope->stmts) == 0; +} + +ecs_script_scope_t* flecs_script_insert_scope( + ecs_script_parser_t *parser) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_script_scope_t *result = flecs_script_scope_new(parser); + flecs_ast_append(parser, scope->stmts, ecs_script_scope_t, result); + return result; +} + +ecs_script_entity_t* flecs_script_insert_entity( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_entity_t *result = flecs_ast_new( + parser, ecs_script_entity_t, EcsAstEntity); + + if (name && !ecs_os_strcmp(name, "_")) { + name = NULL; + } + + result->name = name; + + ecs_script_scope_t *entity_scope = flecs_script_scope_new(parser); + ecs_assert(entity_scope != NULL, ECS_INTERNAL_ERROR, NULL); + result->scope = entity_scope; + + flecs_ast_append(parser, scope->stmts, ecs_script_entity_t, result); + return result; +} + +static +void flecs_script_set_id( + ecs_script_id_t *id, + const char *first, + const char *second) +{ + ecs_assert(first != NULL, ECS_INTERNAL_ERROR, NULL); + id->first = first; + id->second = second; +} + +ecs_script_pair_scope_t* flecs_script_insert_pair_scope( + ecs_script_parser_t *parser, + const char *first, + const char *second) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_script_pair_scope_t *result = flecs_ast_new( + parser, ecs_script_pair_scope_t, EcsAstPairScope); + flecs_script_set_id(&result->id, first, second); + result->scope = flecs_script_scope_new(parser); + + flecs_ast_append(parser, scope->stmts, ecs_script_pair_scope_t, result); + return result; +} + +ecs_script_tag_t* flecs_script_insert_pair_tag( + ecs_script_parser_t *parser, + const char *first, + const char *second) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_tag_t *result = flecs_ast_new( + parser, ecs_script_tag_t, EcsAstTag); + flecs_script_set_id(&result->id, first, second); + + flecs_ast_append(parser, scope->stmts, ecs_script_tag_t, result); + + return result; +} + +ecs_script_tag_t* flecs_script_insert_tag( + ecs_script_parser_t *parser, + const char *name) +{ + return flecs_script_insert_pair_tag(parser, name, NULL); +} + +ecs_script_component_t* flecs_script_insert_pair_component( + ecs_script_parser_t *parser, + const char *first, + const char *second) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_component_t *result = flecs_ast_new( + parser, ecs_script_component_t, EcsAstComponent); + flecs_script_set_id(&result->id, first, second); + + flecs_ast_append(parser, scope->stmts, ecs_script_component_t, result); + + return result; +} + +ecs_script_component_t* flecs_script_insert_component( + ecs_script_parser_t *parser, + const char *name) +{ + return flecs_script_insert_pair_component(parser, name, NULL); +} + +ecs_script_default_component_t* flecs_script_insert_default_component( + ecs_script_parser_t *parser) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_default_component_t *result = flecs_ast_new( + parser, ecs_script_default_component_t, EcsAstDefaultComponent); + + flecs_ast_append(parser, scope->stmts, + ecs_script_default_component_t, result); + + return result; +} + +ecs_script_var_component_t* flecs_script_insert_var_component( + ecs_script_parser_t *parser, + const char *var_name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(var_name != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_var_component_t *result = flecs_ast_new( + parser, ecs_script_var_component_t, EcsAstVarComponent); + result->name = var_name; + + flecs_ast_append(parser, scope->stmts, + ecs_script_var_component_t, result); + return result; +} + +ecs_script_with_t* flecs_script_insert_with( + ecs_script_parser_t *parser) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_with_t *result = flecs_ast_new( + parser, ecs_script_with_t, EcsAstWith); + + result->expressions = flecs_script_scope_new(parser); + result->scope = flecs_script_scope_new(parser); + + flecs_ast_append(parser, scope->stmts, ecs_script_with_t, result); + return result; +} + +ecs_script_using_t* flecs_script_insert_using( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_using_t *result = flecs_ast_new( + parser, ecs_script_using_t, EcsAstUsing); + + result->name = name; + + flecs_ast_append(parser, scope->stmts, ecs_script_using_t, result); + return result; +} + +ecs_script_module_t* flecs_script_insert_module( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_module_t *result = flecs_ast_new( + parser, ecs_script_module_t, EcsAstModule); + + result->name = name; + + flecs_ast_append(parser, scope->stmts, ecs_script_module_t, result); + return result; +} + +ecs_script_annot_t* flecs_script_insert_annot( + ecs_script_parser_t *parser, + const char *name, + const char *expr) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_annot_t *result = flecs_ast_new( + parser, ecs_script_annot_t, EcsAstAnnotation); + + result->name = name; + result->expr = expr; + + flecs_ast_append(parser, scope->stmts, ecs_script_annot_t, result); + return result; +} + +ecs_script_template_node_t* flecs_script_insert_template( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_template_node_t *result = flecs_ast_new( + parser, ecs_script_template_node_t, EcsAstTemplate); + result->name = name; + result->scope = flecs_script_scope_new(parser); + + flecs_ast_append(parser, scope->stmts, ecs_script_template_node_t, result); + return result; +} + +ecs_script_var_node_t* flecs_script_insert_var( + ecs_script_parser_t *parser, + const char *name) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_var_node_t *result = flecs_ast_new( + parser, ecs_script_var_node_t, EcsAstConst); + result->name = name; + + flecs_ast_append(parser, scope->stmts, ecs_script_var_node_t, result); + return result; +} + +ecs_script_if_t* flecs_script_insert_if( + ecs_script_parser_t *parser) +{ + ecs_script_scope_t *scope = parser->scope; + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_script_if_t *result = flecs_ast_new( + parser, ecs_script_if_t, EcsAstIf); + result->if_true = flecs_script_scope_new(parser); + result->if_false = flecs_script_scope_new(parser); + + flecs_ast_append(parser, scope->stmts, ecs_script_if_t, result); + return result; +} + +#endif diff --git a/vendors/flecs/src/addons/script/ast.h b/vendors/flecs/src/addons/script/ast.h new file mode 100644 index 000000000..42dd8a76a --- /dev/null +++ b/vendors/flecs/src/addons/script/ast.h @@ -0,0 +1,210 @@ +/** + * @file addons/script/ast.h + * @brief Script AST. + */ + +#ifndef FLECS_SCRIPT_AST_H +#define FLECS_SCRIPT_AST_H + +typedef enum ecs_script_node_kind_t { + EcsAstScope, + EcsAstTag, + EcsAstComponent, + EcsAstDefaultComponent, + EcsAstVarComponent, + EcsAstWithVar, + EcsAstWithTag, + EcsAstWithComponent, + EcsAstWith, + EcsAstUsing, + EcsAstModule, + EcsAstAnnotation, + EcsAstTemplate, + EcsAstProp, + EcsAstConst, + EcsAstEntity, + EcsAstPairScope, + EcsAstIf, +} ecs_script_node_kind_t; + +typedef struct ecs_script_node_t { + ecs_script_node_kind_t kind; + const char *pos; +} ecs_script_node_t; + +struct ecs_script_scope_t { + ecs_script_node_t node; + ecs_vec_t stmts; + ecs_script_scope_t *parent; + ecs_id_t default_component_eval; +}; + +typedef struct ecs_script_id_t { + const char *first; + const char *second; + ecs_id_t flag; + ecs_id_t eval; +} ecs_script_id_t; + +typedef struct ecs_script_tag_t { + ecs_script_node_t node; + ecs_script_id_t id; +} ecs_script_tag_t; + +typedef struct ecs_script_component_t { + ecs_script_node_t node; + ecs_script_id_t id; + const char *expr; + ecs_value_t eval; + bool is_collection; +} ecs_script_component_t; + +typedef struct ecs_script_default_component_t { + ecs_script_node_t node; + const char *expr; + ecs_value_t eval; +} ecs_script_default_component_t; + +typedef struct ecs_script_var_component_t { + ecs_script_node_t node; + const char *name; +} ecs_script_var_component_t; + +struct ecs_script_entity_t { + ecs_script_node_t node; + const char *kind; + const char *name; + bool name_is_var; + bool kind_w_expr; + ecs_script_scope_t *scope; + + // Populated during eval + ecs_script_entity_t *parent; + ecs_entity_t eval; + ecs_entity_t eval_kind; +}; + +typedef struct ecs_script_with_t { + ecs_script_node_t node; + ecs_script_scope_t *expressions; + ecs_script_scope_t *scope; +} ecs_script_with_t; + +typedef struct ecs_script_inherit_t { + ecs_script_node_t node; + ecs_script_scope_t *base_list; +} ecs_script_inherit_t; + +typedef struct ecs_script_pair_scope_t { + ecs_script_node_t node; + ecs_script_id_t id; + ecs_script_scope_t *scope; +} ecs_script_pair_scope_t; + +typedef struct ecs_script_using_t { + ecs_script_node_t node; + const char *name; +} ecs_script_using_t; + +typedef struct ecs_script_module_t { + ecs_script_node_t node; + const char *name; +} ecs_script_module_t; + +typedef struct ecs_script_annot_t { + ecs_script_node_t node; + const char *name; + const char *expr; +} ecs_script_annot_t; + +typedef struct ecs_script_template_node_t { + ecs_script_node_t node; + const char *name; + ecs_script_scope_t* scope; +} ecs_script_template_node_t; + +typedef struct ecs_script_var_node_t { + ecs_script_node_t node; + const char *name; + const char *type; + const char *expr; +} ecs_script_var_node_t; + +typedef struct ecs_script_if_t { + ecs_script_node_t node; + ecs_script_scope_t *if_true; + ecs_script_scope_t *if_false; + const char *expr; +} ecs_script_if_t; + +#define ecs_script_node(kind, node)\ + ((ecs_script_##kind##_t*)node) + +bool flecs_scope_is_empty( + ecs_script_scope_t *scope); + +ecs_script_scope_t* flecs_script_insert_scope( + ecs_script_parser_t *parser); + +ecs_script_entity_t* flecs_script_insert_entity( + ecs_script_parser_t *parser, + const char *name); + +ecs_script_pair_scope_t* flecs_script_insert_pair_scope( + ecs_script_parser_t *parser, + const char *first, + const char *second); + +ecs_script_with_t* flecs_script_insert_with( + ecs_script_parser_t *parser); + +ecs_script_using_t* flecs_script_insert_using( + ecs_script_parser_t *parser, + const char *name); + +ecs_script_module_t* flecs_script_insert_module( + ecs_script_parser_t *parser, + const char *name); + +ecs_script_template_node_t* flecs_script_insert_template( + ecs_script_parser_t *parser, + const char *name); + +ecs_script_annot_t* flecs_script_insert_annot( + ecs_script_parser_t *parser, + const char *name, + const char *expr); + +ecs_script_var_node_t* flecs_script_insert_var( + ecs_script_parser_t *parser, + const char *name); + +ecs_script_tag_t* flecs_script_insert_tag( + ecs_script_parser_t *parser, + const char *name); + +ecs_script_tag_t* flecs_script_insert_pair_tag( + ecs_script_parser_t *parser, + const char *first, + const char *second); + +ecs_script_component_t* flecs_script_insert_component( + ecs_script_parser_t *parser, + const char *name); + +ecs_script_component_t* flecs_script_insert_pair_component( + ecs_script_parser_t *parser, + const char *first, + const char *second); + +ecs_script_default_component_t* flecs_script_insert_default_component( + ecs_script_parser_t *parser); + +ecs_script_var_component_t* flecs_script_insert_var_component( + ecs_script_parser_t *parser, + const char *name); + +ecs_script_if_t* flecs_script_insert_if( + ecs_script_parser_t *parser); + +#endif diff --git a/vendors/flecs/src/addons/expr/deserialize.c b/vendors/flecs/src/addons/script/expr.c similarity index 76% rename from vendors/flecs/src/addons/expr/deserialize.c rename to vendors/flecs/src/addons/script/expr.c index 189fb0c8c..c0cafe888 100644 --- a/vendors/flecs/src/addons/expr/deserialize.c +++ b/vendors/flecs/src/addons/script/expr.c @@ -1,14 +1,16 @@ /** - * @file expr/deserialize.c - * @brief Deserialize flecs string format into (component) values. + * @file addons/script/expr.c + * @brief Evaluate script expressions. */ -#include "../../private_api.h" -#include +#include "flecs.h" -#ifdef FLECS_EXPR +#ifdef FLECS_SCRIPT +#include +#include +#include "script.h" -/* String deserializer for values & simple expressions */ +/* String deserializer for script expressions */ /* Order in enumeration is important, as it is used for precedence */ typedef enum ecs_expr_oper_t { @@ -32,7 +34,7 @@ typedef enum ecs_expr_oper_t { } ecs_expr_oper_t; /* Used to track temporary values */ -#define EXPR_MAX_STACK_SIZE (256) +#define FLECS_EXPR_MAX_STACK_SIZE (256) typedef struct ecs_expr_value_t { const ecs_type_info_t *ti; @@ -40,7 +42,7 @@ typedef struct ecs_expr_value_t { } ecs_expr_value_t; typedef struct ecs_value_stack_t { - ecs_expr_value_t values[EXPR_MAX_STACK_SIZE]; + ecs_expr_value_t values[FLECS_EXPR_MAX_STACK_SIZE]; ecs_stack_cursor_t *cursor; ecs_stack_t *stack; ecs_stage_t *stage; @@ -48,13 +50,192 @@ typedef struct ecs_value_stack_t { } ecs_value_stack_t; static -const char* flecs_parse_expr( +const char* flecs_script_expr_run( ecs_world_t *world, ecs_value_stack_t *stack, const char *ptr, ecs_value_t *value, ecs_expr_oper_t op, - const ecs_parse_expr_desc_t *desc); + const ecs_script_expr_run_desc_t *desc); + +#define TOK_VARIABLE '$' + +/* -- Private functions -- */ + +static +bool flecs_isident( + char ch) +{ + return isalpha(ch) || (ch == '_'); +} + +static +bool flecs_valid_identifier_start_char( + char ch) +{ + if (ch && (flecs_isident(ch) || (ch == '*') || + (ch == '0') || (ch == TOK_VARIABLE) || isdigit(ch))) + { + return true; + } + + return false; +} + +static +bool flecs_valid_token_start_char( + char ch) +{ + if ((ch == '"') || (ch == '{') || (ch == '}') || (ch == ',') || (ch == '-') + || (ch == '[') || (ch == ']') || (ch == '`') || + flecs_valid_identifier_start_char(ch)) + { + return true; + } + + return false; +} + +static +bool flecs_valid_token_char( + char ch) +{ + if (ch && (flecs_isident(ch) || isdigit(ch) || ch == '.' || ch == '"')) { + return true; + } + + return false; +} + +static +const char* flecs_parse_ws( + const char *ptr) +{ + while ((*ptr != '\n') && isspace(*ptr)) { + ptr ++; + } + + return ptr; +} + +static +const char* flecs_parse_expr_token( + const char *name, + const char *expr, + const char *ptr, + char *token_out, + char delim) +{ + int64_t column = ptr - expr; + + ptr = flecs_parse_ws(ptr); + char *tptr = token_out, ch = ptr[0]; + + if (!flecs_valid_token_start_char(ch)) { + if (ch == '\0' || ch == '\n') { + ecs_parser_error(name, expr, column, + "unexpected end of expression"); + } else { + ecs_parser_error(name, expr, column, + "invalid start of token '%s'", ptr); + } + return NULL; + } + + bool isDigit = isdigit(ch) || (ch == '-'); + + if (ch == '-') { + if (!isdigit(ptr[1]) && ptr[1] != '$' && ptr[1] != '(') { + ecs_parser_error(name, expr, column, + "invalid number token"); + return NULL; + } + if (ptr[1] == '$' || ptr[1] == '(') { + isDigit = false; /* -$var */ + } + } + + bool hasDot = false; + bool hasE = false; + + tptr[0] = ch; + tptr ++; + ptr ++; + + if (ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == ',' || ch == '`') { + tptr[0] = 0; + return ptr; + } + + int tmpl_nesting = 0; + bool in_str = ch == '"'; + + for (; (ch = *ptr); ptr ++) { + if (ch == '<') { + tmpl_nesting ++; + } else if (ch == '>') { + if (!tmpl_nesting) { + break; + } + tmpl_nesting --; + } else if (ch == '"') { + in_str = !in_str; + } else if (ch == '\\') { + ptr ++; + tptr[0] = ptr[0]; + tptr ++; + continue; + } else if (!flecs_valid_token_char(ch) && !in_str) { + break; + } + + if (isDigit) { + if (!isdigit(ch) && (ch != '.') && (ch != 'e')) { + ecs_parser_error(name, expr, column, "invalid token"); + return NULL; + } + if (ch == '.') { + if (hasDot) { + ecs_parser_error(name, expr, column, "invalid token"); + return NULL; + } + hasDot = true; + } + if (ch == 'e') { + if (hasE) { + ecs_parser_error(name, expr, column, "invalid token"); + return NULL; + } + hasE = true; + } + } + + if (delim && (ch == delim)) { + break; + } + + tptr[0] = ch; + tptr ++; + } + + tptr[0] = '\0'; + + if (tmpl_nesting != 0) { + ecs_parser_error(name, expr, column, + "identifier '%s' has mismatching < > pairs", ptr); + return NULL; + } + + const char *next_ptr = flecs_parse_ws(ptr); + if (next_ptr[0] == ':' && next_ptr != ptr) { + /* Whitespace between token and : is significant */ + ptr = next_ptr - 1; + } else { + ptr = next_ptr; + } + + return ptr; +} static void* flecs_expr_value_new( @@ -143,7 +324,8 @@ const char* flecs_str_to_expr_oper( return NULL; } -const char *ecs_parse_expr_token( +static +const char *flecs_script_expr_parse_token( const char *name, const char *expr, const char *ptr, @@ -157,13 +339,13 @@ const char *ecs_parse_expr_token( // Single line comment for (ptr = &ptr[2]; (ch = ptr[0]) && (ch != '\n'); ptr ++) {} token[0] = 0; - return ecs_parse_ws_eol(ptr); + return flecs_parse_ws_eol(ptr); } else if (ptr[1] == '*') { // Multi line comment for (ptr = &ptr[2]; (ch = ptr[0]); ptr ++) { if (ch == '*' && ptr[1] == '/') { token[0] = 0; - return ecs_parse_ws_eol(ptr + 2); + return flecs_parse_ws_eol(ptr + 2); } } @@ -186,7 +368,7 @@ const char *ecs_parse_expr_token( } } - while ((ptr = ecs_parse_token(name, expr, ptr, token_ptr, 0))) { + while ((ptr = flecs_parse_expr_token(name, expr, ptr, token_ptr, 0))) { if (ptr[0] == '|' && ptr[1] != '|') { token_ptr = &token_ptr[ecs_os_strlen(token_ptr)]; token_ptr[0] = '|'; @@ -225,8 +407,10 @@ const char* flecs_parse_multiline_string( "missing '`' to close multiline string"); goto error; } + char *strval = ecs_strbuf_get(&str); if (ecs_meta_set_string(cur, strval) != 0) { + ecs_os_free(strval); goto error; } ecs_os_free(strval); @@ -257,7 +441,7 @@ bool flecs_parse_is_float( static ecs_value_t flecs_dotresolve_var( ecs_world_t *world, - ecs_vars_t *vars, + ecs_script_vars_t *vars, char *token) { char *dot = strchr(token, '.'); @@ -267,7 +451,7 @@ ecs_value_t flecs_dotresolve_var( dot[0] = '\0'; - const ecs_expr_var_t *var = ecs_vars_lookup(vars, token); + const ecs_script_var_t *var = ecs_script_vars_lookup(vars, token); if (!var) { return (ecs_value_t){0}; } @@ -351,7 +535,7 @@ ecs_entity_t flecs_parse_discover_type( const char *expr, const char *ptr, ecs_entity_t input_type, - const ecs_parse_expr_desc_t *desc) + const ecs_script_expr_run_desc_t *desc) { /* String literal */ if (ptr[0] == '"' || ptr[0] == '`') { @@ -390,12 +574,13 @@ ecs_entity_t flecs_parse_discover_type( "unresolved variable (no variable scope)"); return 0; } + char token[ECS_MAX_TOKEN_SIZE]; - if (ecs_parse_expr_token(name, expr, &ptr[1], token) == NULL) { + if (flecs_script_expr_parse_token(name, expr, &ptr[1], token) == NULL) { return 0; } - const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, token); + const ecs_script_var_t *var = ecs_script_vars_lookup(desc->vars, token); if (!var) { ecs_size_t len = ecs_os_strlen(token); if (ptr[len + 1] == '(') { @@ -405,6 +590,8 @@ ecs_entity_t flecs_parse_discover_type( if (token[len] == '.') { token[len] = '\0'; } + + return ecs_id(ecs_entity_t); } ecs_value_t v = flecs_dotresolve_var(world, desc->vars, token); @@ -425,6 +612,7 @@ ecs_entity_t flecs_parse_discover_type( return ecs_id(ecs_bool_t); } } + if (ptr[0] == 'f' && !ecs_os_strncmp(ptr, "false", 5)) { if (!isalpha(ptr[5]) && ptr[5] != '_') { return ecs_id(ecs_bool_t); @@ -434,7 +622,59 @@ ecs_entity_t flecs_parse_discover_type( /* Entity identifier */ if (isalpha(ptr[0])) { if (!input_type) { /* Identifier could also be enum/bitmask constant */ - return ecs_id(ecs_entity_t); + char token[ECS_MAX_TOKEN_SIZE]; + const char *tptr = flecs_script_expr_parse_token( + name, expr, ptr, token); + if (!tptr) { + return 0; + } + + if (tptr[0] != '[') { + return ecs_id(ecs_entity_t); + } + + tptr = flecs_script_expr_parse_token( + name, expr, tptr + 1, token); + if (!tptr) { + return 0; + } + + ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(desc->lookup_action != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t type = desc->lookup_action( + world, token, desc->lookup_ctx); + if (!type) { + ecs_parser_error(name, expr, ptr - expr, + "unresolved type '%s'", token); + return 0; + } + + if (tptr[0] != ']') { + ecs_parser_error(name, expr, ptr - expr, + "missing ']' after '%s'", token); + return 0; + } + + if (tptr[1] != '.') { + return type; + } + + ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, NULL); + ecs_meta_push(&cur); + + tptr = flecs_script_expr_parse_token( + name, expr, tptr + 2, token); + if (!tptr) { + return 0; + } + + if (ecs_meta_dotmember(&cur, token) != 0) { + ecs_parser_error(name, expr, ptr - expr, + "failed to assign member '%s'", token); + return 0; + } + + return ecs_meta_get_type(&cur); } } @@ -676,8 +916,8 @@ ecs_entity_t flecs_binary_expr_type( const EcsPrimitive *ltype_ptr = ecs_get(world, lvalue->type, EcsPrimitive); const EcsPrimitive *rtype_ptr = ecs_get(world, rvalue->type, EcsPrimitive); if (!ltype_ptr || !rtype_ptr) { - char *lname = ecs_get_fullpath(world, lvalue->type); - char *rname = ecs_get_fullpath(world, rvalue->type); + char *lname = ecs_get_path(world, lvalue->type); + char *rname = ecs_get_path(world, rvalue->type); ecs_parser_error(name, expr, ptr - expr, "invalid non-primitive type in binary expression (%s, %s)", lname, rname); @@ -694,8 +934,13 @@ ecs_entity_t flecs_binary_expr_type( } if (flecs_expr_op_is_equality(op)) { + char *lstr = ecs_id_str(world, ltype); + char *rstr = ecs_id_str(world, rtype); ecs_parser_error(name, expr, ptr - expr, - "mismatching types in equality expression"); + "mismatching types in equality expression (%s vs %s)", + lstr, rstr); + ecs_os_free(rstr); + ecs_os_free(lstr); return 0; } @@ -902,21 +1147,22 @@ const char* flecs_binary_expr_parse( ecs_value_t *lvalue, ecs_value_t *result, ecs_expr_oper_t left_op, - const ecs_parse_expr_desc_t *desc) + const ecs_script_expr_run_desc_t *desc) { ecs_entity_t result_type = result->type; do { ecs_expr_oper_t op; + ptr = flecs_str_to_expr_oper(ptr, &op); if (!ptr) { ecs_parser_error(name, expr, ptr - expr, "invalid operator"); return NULL; } - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); ecs_value_t rvalue = {0}; - const char *rptr = flecs_parse_expr(world, stack, ptr, &rvalue, op, desc); + const char *rptr = flecs_script_expr_run(world, stack, ptr, &rvalue, op, desc); if (!rptr) { return NULL; } @@ -963,7 +1209,7 @@ const char* flecs_funccall_parse( ecs_meta_cursor_t *cur, ecs_value_t *value, bool isvar, - const ecs_parse_expr_desc_t *desc) + const ecs_script_expr_run_desc_t *desc) { char *sep = strrchr(token, '.'); if (!sep) { @@ -980,7 +1226,7 @@ const char* flecs_funccall_parse( goto error; } } else { - const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, token); + const ecs_script_var_t *var = ecs_script_vars_lookup(desc->vars, token); ecs_meta_set_value(cur, &var->value); } @@ -1032,13 +1278,23 @@ const char* flecs_funccall_parse( } static -const char* flecs_parse_expr( +ecs_entity_t flecs_script_default_lookup( + const ecs_world_t *world, + const char *name, + void *ctx) +{ + (void)ctx; + return ecs_lookup(world, name); +} + +static +const char* flecs_script_expr_run( ecs_world_t *world, ecs_value_stack_t *stack, const char *ptr, ecs_value_t *value, ecs_expr_oper_t left_op, - const ecs_parse_expr_desc_t *desc) + const ecs_script_expr_run_desc_t *desc) { ecs_assert(value != NULL, ECS_INTERNAL_ERROR, NULL); char token[ECS_MAX_TOKEN_SIZE]; @@ -1050,13 +1306,13 @@ const char* flecs_parse_expr( token[0] = '\0'; expr = expr ? expr : ptr; - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); /* Check for postfix operators */ ecs_expr_oper_t unary_op = EcsExprOperUnknown; if (ptr[0] == '-' && !isdigit(ptr[1])) { unary_op = EcsMin; - ptr = ecs_parse_ws_eol(ptr + 1); + ptr = flecs_parse_ws_eol(ptr + 1); } /* Initialize storage and cursor. If expression starts with a '(' storage @@ -1085,7 +1341,7 @@ const char* flecs_parse_expr( } /* Loop that parses all values in a value scope */ - while ((ptr = ecs_parse_expr_token(name, expr, ptr, token))) { + while ((ptr = flecs_script_expr_parse_token(name, expr, ptr, token))) { /* Used to track of the result of the parsed token can be used as the * lvalue for a binary expression */ bool is_lvalue = false; @@ -1107,13 +1363,14 @@ const char* flecs_parse_expr( } /* Parenthesis, parse nested expression */ - ptr = flecs_parse_expr(world, stack, ptr, out, EcsLeftParen, desc); + ptr = flecs_script_expr_run( + world, stack, ptr, out, EcsLeftParen, desc); if (ptr[0] != ')') { ecs_parser_error(name, expr, ptr - expr, "missing closing parenthesis"); return NULL; } - ptr = ecs_parse_ws(ptr + 1); + ptr = flecs_parse_ws(ptr + 1); is_lvalue = true; } else if (!ecs_os_strcmp(token, "{")) { @@ -1126,7 +1383,7 @@ const char* flecs_parse_expr( } if (ecs_meta_is_collection(&cur)) { - char *path = ecs_get_fullpath(world, scope_type); + char *path = ecs_get_path(world, scope_type); ecs_parser_error(name, expr, ptr - expr, "expected '[' for collection type '%s'", path); ecs_os_free(path); @@ -1233,7 +1490,7 @@ const char* flecs_parse_expr( ecs_os_strcpy(&token[1], member); } - const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, &token[1]); + const ecs_script_var_t *var = ecs_script_vars_lookup(desc->vars, &token[1]); if (!var) { if (ptr[0] == '(') { /* Function */ @@ -1255,10 +1512,10 @@ const char* flecs_parse_expr( } else { ecs_meta_set_value(&cur, &var->value); } - is_lvalue = true; + is_lvalue = true; } else { - const char *tptr = ecs_parse_ws(ptr); + const char *tptr = flecs_parse_ws(ptr); for (; ptr != tptr; ptr ++) { if (ptr[0] == '\n') { newline = true; @@ -1279,8 +1536,86 @@ const char* flecs_parse_expr( goto error; } } else { - if (ecs_meta_set_string(&cur, token) != 0) { - goto error; + if (ptr[0] != '[') { + /* Entity id expression */ + if (ecs_meta_set_string(&cur, token) != 0) { + goto error; + } + } else { + /* Component expression */ + ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(desc->lookup_action != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t e = desc->lookup_action( + world, token, desc->lookup_ctx); + if (!e) { + ecs_parser_error(name, expr, ptr - expr, + "entity '%s' not found", token); + goto error; + } + + ptr = flecs_script_expr_parse_token( + name, expr, ptr + 1, token); + if (!ptr) { + goto error; + } + + ecs_entity_t component = desc->lookup_action( + world, token, desc->lookup_ctx); + if (!component) { + ecs_parser_error(name, expr, ptr - expr, + "unresolved component '%s'", token); + goto error; + } + + ecs_entity_t type = ecs_get_typeid(world, component); + if (!type) { + ecs_parser_error(name, expr, ptr - expr, + "entity '%s' is not a component", token); + goto error; + } + + result.type = type; + result.ptr = ECS_CONST_CAST(void*, + ecs_get_id(world, e, component)); + if (!result.ptr) { + char *entitystr = ecs_id_str(world, e); + char *idstr = ecs_id_str(world, component); + ecs_parser_error(name, expr, ptr - expr, + "entity '%s' does not have component '%s'", + entitystr, idstr); + ecs_os_free(idstr); + ecs_os_free(entitystr); + goto error; + } + + if (ptr[0] != ']') { + ecs_parser_error(name, expr, ptr - expr, + "missing ] for component operator"); + goto error; + } + + ptr ++; + + if (ptr[0] == '.') { + ecs_meta_cursor_t member_cur = ecs_meta_cursor( + world, result.type, result.ptr); + + ptr = flecs_script_expr_parse_token( + name, expr, ptr + 1, token); + if (!ptr) { + goto error; + } + + ecs_meta_push(&member_cur); + if (ecs_meta_dotmember(&member_cur, token) != 0) { + ecs_parser_error(name, expr, ptr - expr, + "failed to assign member '%s'", token); + goto error; + } + + result.type = ecs_meta_get_type(&member_cur); + result.ptr = ecs_meta_get_ptr(&member_cur); + } } } @@ -1358,7 +1693,7 @@ const char* flecs_parse_expr( break; } - ptr = ecs_parse_ws_eol(ptr); + ptr = flecs_parse_ws_eol(ptr); } if (!value->ptr) { @@ -1379,12 +1714,21 @@ const char* flecs_parse_expr( return NULL; } -const char* ecs_parse_expr( +const char* ecs_script_expr_run( ecs_world_t *world, const char *ptr, ecs_value_t *value, - const ecs_parse_expr_desc_t *desc) + const ecs_script_expr_run_desc_t *desc) { + ecs_script_expr_run_desc_t priv_desc = {0}; + if (desc) { + priv_desc = *desc; + } + + if (!priv_desc.lookup_action) { + priv_desc.lookup_action = flecs_script_default_lookup; + } + /* Prepare storage for temporary values */ ecs_stage_t *stage = flecs_stage_from_world(&world); ecs_value_stack_t stack; @@ -1395,7 +1739,8 @@ const char* ecs_parse_expr( /* Parse expression */ bool storage_provided = value->ptr != NULL; - ptr = flecs_parse_expr(world, &stack, ptr, value, EcsExprOperUnknown, desc); + ptr = flecs_script_expr_run( + world, &stack, ptr, value, EcsExprOperUnknown, &priv_desc); /* If no result value was provided, allocate one as we can't return a * pointer to a temporary storage */ diff --git a/vendors/flecs/src/addons/script/interpolate.c b/vendors/flecs/src/addons/script/interpolate.c new file mode 100644 index 000000000..641c96901 --- /dev/null +++ b/vendors/flecs/src/addons/script/interpolate.c @@ -0,0 +1,176 @@ +/** + * @file addons/script/interpolate.c + * @brief String interpolation. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include +#include "script.h" + +static +const char* flecs_parse_var_name( + const char *ptr, + char *token_out) +{ + char ch, *bptr = token_out; + + while ((ch = *ptr)) { + if (bptr - token_out > ECS_MAX_TOKEN_SIZE) { + goto error; + } + + if (isalpha(ch) || isdigit(ch) || ch == '_') { + *bptr = ch; + bptr ++; + ptr ++; + } else { + break; + } + } + + if (bptr == token_out) { + goto error; + } + + *bptr = '\0'; + + return ptr; +error: + return NULL; +} + +static +const char* flecs_parse_interpolated_str( + const char *ptr, + char *token_out) +{ + char ch, *bptr = token_out; + + while ((ch = *ptr)) { + if (bptr - token_out > ECS_MAX_TOKEN_SIZE) { + goto error; + } + + if (ch == '\\') { + if (ptr[1] == '}') { + *bptr = '}'; + bptr ++; + ptr += 2; + continue; + } + } + + if (ch != '}') { + *bptr = ch; + bptr ++; + ptr ++; + } else { + ptr ++; + break; + } + } + + if (bptr == token_out) { + goto error; + } + + *bptr = '\0'; + + return ptr; +error: + return NULL; +} + +char* ecs_script_string_interpolate( + ecs_world_t *world, + const char *str, + const ecs_script_vars_t *vars) +{ + char token[ECS_MAX_TOKEN_SIZE]; + ecs_strbuf_t result = ECS_STRBUF_INIT; + const char *ptr; + char ch; + + for(ptr = str; (ch = *ptr); ptr++) { + if (ch == '\\') { + ptr ++; + if (ptr[0] == '$') { + ecs_strbuf_appendch(&result, '$'); + continue; + } + if (ptr[0] == '\\') { + ecs_strbuf_appendch(&result, '\\'); + continue; + } + if (ptr[0] == '{') { + ecs_strbuf_appendch(&result, '{'); + continue; + } + if (ptr[0] == '}') { + ecs_strbuf_appendch(&result, '}'); + continue; + } + ptr --; + } + + if (ch == '$') { + ptr = flecs_parse_var_name(ptr + 1, token); + if (!ptr) { + ecs_parser_error(NULL, str, ptr - str, + "invalid variable name '%s'", ptr); + goto error; + } + + ecs_script_var_t *var = ecs_script_vars_lookup(vars, token); + if (!var) { + ecs_parser_error(NULL, str, ptr - str, + "unresolved variable '%s'", token); + goto error; + } + + if (ecs_ptr_to_str_buf( + world, var->value.type, var->value.ptr, &result)) + { + goto error; + } + + ptr --; + } else if (ch == '{') { + ptr = flecs_parse_interpolated_str(ptr + 1, token); + if (!ptr) { + ecs_parser_error(NULL, str, ptr - str, + "invalid interpolated expression"); + goto error; + } + + ecs_script_expr_run_desc_t expr_desc = { + .vars = ECS_CONST_CAST(ecs_script_vars_t*, vars) + }; + + ecs_value_t expr_result = {0}; + if (!ecs_script_expr_run(world, token, &expr_result, &expr_desc)) { + goto error; + } + + if (ecs_ptr_to_str_buf( + world, expr_result.type, expr_result.ptr, &result)) + { + goto error; + } + + ecs_value_free(world, expr_result.type, expr_result.ptr); + + ptr --; + } else { + ecs_strbuf_appendch(&result, ch); + } + } + + return ecs_strbuf_get(&result); +error: + return NULL; +} + +#endif diff --git a/vendors/flecs/src/addons/script/parser.c b/vendors/flecs/src/addons/script/parser.c new file mode 100644 index 000000000..45fff5a43 --- /dev/null +++ b/vendors/flecs/src/addons/script/parser.c @@ -0,0 +1,853 @@ +/** + * @file addons/script/parser.c + * @brief Script grammar parser. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" +#include "parser.h" + +#define EcsTokEndOfStatement\ + case ';':\ + case '\n':\ + case '\0' + +static +const char* flecs_script_stmt( + ecs_script_parser_t *parser, + const char *pos); + +/* Parse scope (statements inside {}) */ +static +const char* flecs_script_scope( + ecs_script_parser_t *parser, + ecs_script_scope_t *scope, + const char *pos) +{ + ParserBegin; + + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(pos[-1] == '{', ECS_INTERNAL_ERROR, NULL); + + ecs_script_scope_t *prev = parser->scope; + parser->scope = scope; + + Loop( + LookAhead( + case EcsTokScopeClose: + pos = lookahead; + goto scope_close; + case EcsTokEnd: + Error("unexpected end of script"); + goto error; + ) + + pos = flecs_script_stmt(parser, pos); + if (!pos) { + goto error; + } + ) + +scope_close: + parser->scope = prev; + + ecs_assert(pos[-1] == '}', ECS_INTERNAL_ERROR, NULL); + return pos; + + ParserEnd; +} + +/* Parse comma expression (expressions separated by ',') */ +static +const char* flecs_script_comma_expr( + ecs_script_parser_t *parser, + const char *pos, + bool is_base_list) +{ + ParserBegin; + + Loop( + LookAhead( + case '\n': + pos = lookahead; + continue; + + case EcsTokIdentifier: + LookAhead_Keep(); + + if (is_base_list) { + flecs_script_insert_pair_tag(parser, "IsA", Token(0)); + } else { + flecs_script_insert_entity(parser, Token(0)); + } + + LookAhead_1(',', + pos = lookahead; + continue; + ) + ) + + break; + ) + + return pos; +} + +/* Parse with expression (expression after 'with' keyword) */ +static +const char* flecs_script_with_expr( + ecs_script_parser_t *parser, + const char *pos) +{ + ParserBegin; + + Parse( + // Position + case EcsTokIdentifier: { + // Position ( + LookAhead_1('(', + pos = lookahead; + + // Position ( expr ) + Expr(')', + ecs_script_component_t *component = + flecs_script_insert_component(parser, Token(0)); + component->node.kind = EcsAstWithComponent; + component->expr = Token(2); + EndOfRule; + ) + ) + + if (Token(0)[0] == '$') { + ecs_script_var_component_t *var = + flecs_script_insert_var_component(parser, &Token(0)[1]); + var->node.kind = EcsAstWithVar; + } else { + ecs_script_tag_t *tag = + flecs_script_insert_tag(parser, Token(0)); + tag->node.kind = EcsAstWithTag; + } + + EndOfRule; + } + + // ( + case '(': + // (Eats, Apples) + Parse_4(EcsTokIdentifier, ',', EcsTokIdentifier, ')', + // (Eats, Apples) ( expr + LookAhead_1('(', + pos = lookahead; + + // (Eats, Apples) ( expr ) + Expr(')', + ecs_script_component_t *component = + flecs_script_insert_pair_component(parser, + Token(1), Token(3)); + component->node.kind = EcsAstWithComponent; + component->expr = Token(6); + EndOfRule; + ) + ) + + ecs_script_tag_t *tag = + flecs_script_insert_pair_tag(parser, Token(1), Token(3)); + tag->node.kind = EcsAstWithTag; + EndOfRule; + ) + ) + + ParserEnd; +} + +/* Parse with expression list (expression list after 'with' keyword) */ +static +const char* flecs_script_with( + ecs_script_parser_t *parser, + ecs_script_with_t *with, + const char *pos) +{ + ParserBegin; + + bool has_next; + do { + Scope(with->expressions, + pos = flecs_script_with_expr(parser, pos); + ) + + if (!pos) { + goto error; + } + + Parse( + case ',': { + has_next = true; + break; + } + case '{': { + return flecs_script_scope(parser, with->scope, pos); + } + ) + } while (has_next); + + ParserEnd; +} + +/* Parenthesis expression */ +static +const char* flecs_script_paren_expr( + ecs_script_parser_t *parser, + const char *kind, + ecs_script_entity_t *entity, + const char *pos) +{ + ParserBegin; + + Expr(')', + entity->kind_w_expr = true; + + Scope(entity->scope, + ecs_script_component_t *component = + flecs_script_insert_component(parser, kind); + component->expr = Token(0); + ) + + Parse( + // Position spaceship (expr)\n + EcsTokEndOfStatement: { + EndOfRule; + } + + // Position spaceship (expr) { + case '{': { + return flecs_script_scope(parser, entity->scope, pos); + } + ) + ) + + ParserEnd; +} + +/* Parse a single statement */ +static +const char* flecs_script_stmt( + ecs_script_parser_t *parser, + const char *pos) +{ + ParserBegin; + + Parse( + case EcsTokIdentifier: goto identifier; + case '{': return flecs_script_scope(parser, + flecs_script_insert_scope(parser), pos); + case '(': goto paren; + case '@': goto annotation; + case EcsTokKeywordWith: goto with_stmt; + case EcsTokKeywordModule: goto module_stmt; + case EcsTokKeywordUsing: goto using_stmt; + case EcsTokKeywordTemplate: goto template_stmt; + case EcsTokKeywordProp: goto prop_var; + case EcsTokKeywordConst: goto const_var; + case EcsTokKeywordIf: goto if_stmt; + EcsTokEndOfStatement: EndOfRule; + ); + +identifier: { + // enterprise } (end of scope) + LookAhead_1('}', + goto insert_tag; + ) + + Parse( + // enterprise { + case '{': { + return flecs_script_scope(parser, + flecs_script_insert_entity( + parser, Token(0))->scope, pos); + } + + // Red, + case ',': { + flecs_script_insert_entity(parser, Token(0)); + pos = flecs_script_comma_expr(parser, pos, false); + EndOfRule; + } + + // Npc\n + EcsTokEndOfStatement: { + // Npc\n{ + LookAhead_1('{', + pos = lookahead; + return flecs_script_scope(parser, + flecs_script_insert_entity( + parser, Token(0))->scope, pos); + ) + + goto insert_tag; + } + + // auto_override | + case '|': { + goto identifier_flag; + } + + // Position: + case ':': { + goto identifier_colon; + } + + // x = + case '=': { + goto identifier_assign; + } + + // SpaceShip( + case '(': { + goto identifier_paren; + } + + // Spaceship enterprise + case EcsTokIdentifier: { + goto identifier_identifier; + } + ) +} + +insert_tag: { + if (Token(0)[0] == '$') { + if (!flecs_script_insert_var_component(parser, &Token(0)[1])) { + Error( + "invalid context for variable component '%s': must be " + "part of entity", tokens[0].value); + } + } else { + if (!flecs_script_insert_tag(parser, Token(0))) { + Error( + "invalid context for tag '%s': must be part of entity", + tokens[0].value); + } + } + + EndOfRule; +} + +// @ +annotation: { + // @brief + Parse_1(EcsTokIdentifier, + // $brief expr + Until('\n', + flecs_script_insert_annot(parser, Token(1), Token(2)); + EndOfRule; + ) + ) +} + +// with +with_stmt: { + ecs_script_with_t *with = flecs_script_insert_with(parser); + pos = flecs_script_with(parser, with, pos); + EndOfRule; +} + +// using +using_stmt: { + // using flecs.meta\n + Parse_1(EcsTokIdentifier, + flecs_script_insert_using(parser, Token(1)); + + Parse( + EcsTokEndOfStatement: + EndOfRule; + ) + ) +} + +// module +module_stmt: { + // using flecs.meta\n + Parse_2(EcsTokIdentifier, '\n', + flecs_script_insert_module(parser, Token(1)); + EndOfRule; + ) +} + +// template +template_stmt: { + // template SpaceShip + Parse_1(EcsTokIdentifier, + ecs_script_template_node_t *template = flecs_script_insert_template( + parser, Token(1)); + + Parse( + // template SpaceShip { + case '{': + return flecs_script_scope(parser, template->scope, pos); + + // template SpaceShip\n + EcsTokEndOfStatement: + EndOfRule; + ) + ) +} + +// prop +prop_var: { + // prop color = Color: + Parse_4(EcsTokIdentifier, '=', EcsTokIdentifier, ':', + ecs_script_var_node_t *var = flecs_script_insert_var( + parser, Token(1)); + var->node.kind = EcsAstProp; + var->type = Token(3); + + // prop color = Color : { + LookAhead_1('{', + // prop color = Color: {expr} + pos = lookahead; + Expr('}', + var->expr = Token(6); + EndOfRule; + ) + ) + + // prop color = Color : expr\n + Expr('\n', + var->expr = Token(5); + EndOfRule; + ) + ) +} + +// const +const_var: { + // const color + Parse_1(EcsTokIdentifier, + ecs_script_var_node_t *var = flecs_script_insert_var( + parser, Token(1)); + var->node.kind = EcsAstConst; + + Parse( + // const color = + case '=': { + // const color = Color : + LookAhead_2(EcsTokIdentifier, ':', + pos = lookahead; + + var->type = Token(3); + + // const color = Color: { + LookAhead_1('{', + // const color = Color: {expr} + pos = lookahead; + Expr('}', + var->expr = Token(6); + EndOfRule; + ) + ) + + // const color = Color: expr\n + Expr('\n', + var->expr = Token(5); + EndOfRule; + ) + ) + + // const PI = expr\n + Expr('\n', + var->expr = Token(3); + EndOfRule; + ) + } + ) + ) +} + +// if +if_stmt: { + // if expr { + Expr('{', + ecs_script_if_t *stmt = flecs_script_insert_if(parser); + stmt->expr = Token(1); + pos = flecs_script_scope(parser, stmt->if_true, pos); + if (!pos) { + goto error; + } + + // if expr { } else + LookAhead_1(EcsTokKeywordElse, + pos = lookahead; + + // if expr { } else { + Parse_1('{', + return flecs_script_scope(parser, stmt->if_false, pos); + ) + ) + + EndOfRule; + ) +} + +// ( +paren: { + // (Likes, Apples) + Parse_4(EcsTokIdentifier, ',', EcsTokIdentifier, ')', + goto pair; + ) +} + +// (Likes, Apples) +pair: { + // (Likes, Apples) } (end of scope) + LookAhead_1('}', + flecs_script_insert_pair_tag(parser, Token(1), Token(3)); + EndOfRule; + ) + + Parse( + // (Likes, Apples)\n + EcsTokEndOfStatement: { + flecs_script_insert_pair_tag(parser, Token(1), Token(3)); + EndOfRule; + } + + // (Eats, Apples): + case ':': { + // (Eats, Apples): { + Parse_1('{', + // (Eats, Apples): { expr } + Expr('}', + ecs_script_component_t *comp = + flecs_script_insert_pair_component( + parser, Token(1), Token(3)); + comp->expr = Token(7); + EndOfRule; + ) + ) + } + + // (IsA, Machine) { + case '{': { + ecs_script_pair_scope_t *ps = flecs_script_insert_pair_scope( + parser, Token(1), Token(3)); + return flecs_script_scope(parser, ps->scope, pos); + } + ) +} + +// auto_override | +identifier_flag: { + ecs_id_t flag; + if (!ecs_os_strcmp(Token(0), "auto_override")) { + flag = ECS_AUTO_OVERRIDE; + } else { + Error("invalid flag '%s'", Token(0)); + } + + Parse( + // auto_override | ( + case '(': + // auto_override | (Rel, Tgt) + Parse_4(EcsTokIdentifier, ',', EcsTokIdentifier, ')', + ecs_script_tag_t *tag = flecs_script_insert_pair_tag( + parser, Token(3), Token(5)); + tag->id.flag = flag; + + Parse( + // auto_override | (Rel, Tgt)\n + EcsTokEndOfStatement: { + EndOfRule; + } + + // auto_override | (Rel, Tgt): + case ':': { + Parse_1('{', + // auto_override | (Rel, Tgt): {expr} + Expr('}', { + ecs_script_component_t *comp = + flecs_script_insert_pair_component( + parser, Token(3), Token(5)); + comp->expr = Token(9); + EndOfRule; + }) + ) + } + ) + ) + + // auto_override | Position + case EcsTokIdentifier: { + ecs_script_tag_t *tag = flecs_script_insert_tag( + parser, Token(2)); + tag->id.flag = flag; + + Parse( + // auto_override | Position\n + EcsTokEndOfStatement: { + EndOfRule; + } + + // auto_override | Position: + case ':': { + Parse_1('{', + // auto_override | Position: {expr} + Expr('}', { + ecs_script_component_t *comp = flecs_script_insert_component( + parser, Token(2)); + comp->expr = Token(5); + EndOfRule; + }) + ) + } + ) + } + ) +} + +// Position: +identifier_colon: { + { + // Position: { + LookAhead_1('{', + pos = lookahead; + goto component_expr_scope; + ) + } + + { + // Position: [ + LookAhead_1('[', + pos = lookahead; + goto component_expr_collection; + ) + } + + // enterprise : SpaceShip + Parse_1(EcsTokIdentifier, { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, Token(0)); + + Scope(entity->scope, + flecs_script_insert_pair_tag(parser, "IsA", Token(2)); + + LookAhead_1(',', { + pos = lookahead; + pos = flecs_script_comma_expr(parser, pos, true); + }) + ) + + Parse( + // enterprise : SpaceShip\n + EcsTokEndOfStatement: + EndOfRule; + + // enterprise : SpaceShip { + case '{': + return flecs_script_scope(parser, entity->scope, pos); + ) + }) +} + +// x = +identifier_assign: { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, Token(0)); + + // x = Position: + LookAhead_2(EcsTokIdentifier, ':', + pos = lookahead; + + // x = Position: { + Parse_1('{', { + // x = Position: {expr} + Expr('}', + Scope(entity->scope, + ecs_script_component_t *comp = + flecs_script_insert_component(parser, Token(2)); + comp->expr = Token(5); + ) + + // x = Position: {expr}\n + Parse( + EcsTokEndOfStatement: + EndOfRule; + ) + ) + }) + ) + + // x = f32\n + Expr('\n', + Scope(entity->scope, + ecs_script_default_component_t *comp = + flecs_script_insert_default_component(parser); + comp->expr = Token(2); + ) + + EndOfRule; + ) +} + +// Spaceship enterprise +identifier_identifier: { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, Token(1)); + entity->kind = Token(0); + + // Spaceship enterprise : + LookAhead_1(':', + pos = lookahead; + + Parse_1(EcsTokIdentifier, { + Scope(entity->scope, + flecs_script_insert_pair_tag(parser, "IsA", Token(3)); + + LookAhead_1(',', { + pos = lookahead; + pos = flecs_script_comma_expr(parser, pos, true); + }) + ) + + goto identifier_identifier_x; + }) + ) + +identifier_identifier_x: + Parse( + // Spaceship enterprise\n + EcsTokEndOfStatement: { + EndOfRule; + } + + // Spaceship enterprise { + case '{': { + return flecs_script_scope(parser, entity->scope, pos); + } + + // Spaceship enterprise( + case '(': { + return flecs_script_paren_expr(parser, Token(0), entity, pos); + } + ) +} + +// SpaceShip( +identifier_paren: { + // SpaceShip() + Expr(')', + Parse( + // SpaceShip(expr)\n + EcsTokEndOfStatement: { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, NULL); + + Scope(entity->scope, + ecs_script_component_t *comp = + flecs_script_insert_component(parser, Token(0)); + comp->expr = Token(2); + ) + + EndOfRule; + } + + // SpaceShip(expr) { + case '{': { + ecs_script_entity_t *entity = flecs_script_insert_entity( + parser, NULL); + + Scope(entity->scope, + ecs_script_component_t *comp = + flecs_script_insert_component(parser, Token(0)); + comp->expr = Token(2); + ) + + return flecs_script_scope(parser, entity->scope, pos); + } + ) + ) +} + +// Position: { +component_expr_scope: { + + // Position: {expr} + Expr('}', { + ecs_script_component_t *comp = flecs_script_insert_component( + parser, Token(0)); + comp->expr = Token(3); + EndOfRule; + }) +} + +// Points: [ +component_expr_collection: { + // Position: [expr] + Expr(']', { + ecs_script_component_t *comp = flecs_script_insert_component( + parser, Token(0)); + comp->expr = Token(3); + comp->is_collection = true; + EndOfRule; + }) +} + + ParserEnd; +} + +/* Parse script */ +ecs_script_t* ecs_script_parse( + ecs_world_t *world, + const char *name, + const char *code) +{ + if (!code) { + code = ""; + } + + ecs_script_t *script = flecs_script_new(world); + script->name = ecs_os_strdup(name); + script->code = ecs_os_strdup(code); + + ecs_script_impl_t *impl = flecs_script_impl(script); + + ecs_script_parser_t parser = { + .script = impl, + .scope = impl->root, + .significant_newline = true + }; + + /* Allocate a buffer that is able to store all parsed tokens. Multiply the + * size of the script by two so that there is enough space to add \0 + * terminators and expression deliminators ('""') + * The token buffer will exist for as long as the script object exists, and + * ensures that AST nodes don't need to do separate allocations for the data + * they contain. */ + impl->token_buffer_size = ecs_os_strlen(code) * 2 + 1; + impl->token_buffer = flecs_alloc( + &impl->allocator, impl->token_buffer_size); + parser.token_cur = impl->token_buffer; + + /* Start parsing code */ + const char *pos = script->code; + + do { + pos = flecs_script_stmt(&parser, pos); + if (!pos) { + /* NULL means error */ + goto error; + } + + if (!pos[0]) { + /* \0 means end of input */ + break; + } + } while (true); + + return script; +error: + ecs_script_free(script); + return NULL; +} + +#endif diff --git a/vendors/flecs/src/addons/script/parser.h b/vendors/flecs/src/addons/script/parser.h new file mode 100644 index 000000000..de2c490df --- /dev/null +++ b/vendors/flecs/src/addons/script/parser.h @@ -0,0 +1,223 @@ +/** + * @file addons/script/parser.h + * @brief Script grammar parser. + * + * Macro utilities that facilitate a simple recursive descent parser. + */ + +#ifndef FLECS_SCRIPT_PARSER_H +#define FLECS_SCRIPT_PARSER_H + +#if defined(ECS_TARGET_CLANG) +/* Ignore unused enum constants in switch as it would blow up the parser code */ +#pragma clang diagnostic ignored "-Wswitch-enum" +/* To allow for nested Parse statements */ +#pragma clang diagnostic ignored "-Wshadow" +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#pragma GCC diagnostic ignored "-Wswitch-enum" +#pragma GCC diagnostic ignored "-Wshadow" +#elif defined(ECS_TARGET_MSVC) +/* Allow for variable shadowing */ +#pragma warning(disable : 4456) +#endif + +/* Create script & parser structs with static token buffer */ +#define EcsParserFixedBuffer(w, script_name, expr, tokens, tokens_len)\ + ecs_script_impl_t script = {\ + .pub.world = ECS_CONST_CAST(ecs_world_t*, w),\ + .pub.name = script_name,\ + .pub.code = expr\ + };\ + ecs_script_parser_t parser = {\ + .script = flecs_script_impl(&script),\ + .pos = expr,\ + .token_cur = tokens\ + } + +/* Definitions for parser functions */ +#define ParserBegin\ + ecs_script_tokens_t token_stack = {0};\ + ecs_script_token_t *tokens = token_stack.tokens;\ + (void)tokens + +#define ParserEnd\ + Error("unexpected end of rule (parser error)");\ + error:\ + return NULL + +/* Get token */ +#define Token(n) (tokens[n].value) + +/* Error */ +#define Error(...)\ + ecs_parser_error(parser->script->pub.name, parser->script->pub.code,\ + (pos - parser->script->pub.code) - 1, __VA_ARGS__);\ + goto error + +/* Parse expression */ +#define Expr(until, ...)\ + {\ + ecs_assert(token_stack.count < 256, ECS_INTERNAL_ERROR, NULL);\ + ecs_script_token_t *t = &token_stack.tokens[token_stack.count ++];\ + if (!(pos = flecs_script_expr(parser, pos, t, until))) {\ + goto error;\ + }\ + if (!t->value[0] && (until == '\n' || until == '{')) {\ + pos ++;\ + Error("empty expression");\ + }\ + }\ + Parse_1(until, __VA_ARGS__) + +/* Parse token until character */ +#define Until(until, ...)\ + {\ + ecs_assert(token_stack.count < 256, ECS_INTERNAL_ERROR, NULL);\ + ecs_script_token_t *t = &token_stack.tokens[token_stack.count ++];\ + if (!(pos = flecs_script_until(parser, pos, t, until))) {\ + goto error;\ + }\ + }\ + Parse_1(until, __VA_ARGS__) + +/* Parse next token */ +#define Parse(...)\ + {\ + ecs_assert(token_stack.count < 256, ECS_INTERNAL_ERROR, NULL);\ + ecs_script_token_t *t = &token_stack.tokens[token_stack.count ++];\ + if (!(pos = flecs_script_token(parser, pos, t, false))) {\ + goto error;\ + }\ + switch(t->kind) {\ + __VA_ARGS__\ + default:\ + if (t->value) {\ + Error("unexpected %s'%s'", \ + flecs_script_token_kind_str(t->kind), t->value);\ + } else {\ + Error("unexpected %s", \ + flecs_script_token_kind_str(t->kind));\ + }\ + }\ + } + +/* Parse N consecutive tokens */ +#define Parse_1(tok, ...)\ + Parse(\ + case tok: {\ + __VA_ARGS__\ + }\ + ) + +#define Parse_2(tok1, tok2, ...)\ + Parse_1(tok1, \ + Parse(\ + case tok2: {\ + __VA_ARGS__\ + }\ + )\ + ) + +#define Parse_3(tok1, tok2, tok3, ...)\ + Parse_2(tok1, tok2, \ + Parse(\ + case tok3: {\ + __VA_ARGS__\ + }\ + )\ + ) + +#define Parse_4(tok1, tok2, tok3, tok4, ...)\ + Parse_3(tok1, tok2, tok3, \ + Parse(\ + case tok4: {\ + __VA_ARGS__\ + }\ + )\ + ) + +#define Parse_5(tok1, tok2, tok3, tok4, tok5, ...)\ + Parse_4(tok1, tok2, tok3, tok4, \ + Parse(\ + case tok5: {\ + __VA_ARGS__\ + }\ + )\ + ) + +#define LookAhead_Keep() \ + pos = lookahead;\ + parser->token_keep = parser->token_cur + +/* Same as Parse, but doesn't error out if token is not in handled cases */ +#define LookAhead(...)\ + const char *lookahead;\ + ecs_script_token_t lookahead_token;\ + const char *old_lh_token_cur = parser->token_cur;\ + if ((lookahead = flecs_script_token(parser, pos, &lookahead_token, true))) {\ + token_stack.tokens[token_stack.count ++] = lookahead_token;\ + switch(lookahead_token.kind) {\ + __VA_ARGS__\ + default:\ + token_stack.count --;\ + break;\ + }\ + if (old_lh_token_cur > parser->token_keep) {\ + parser->token_cur = ECS_CONST_CAST(char*, old_lh_token_cur);\ + } else {\ + parser->token_cur = parser->token_keep;\ + }\ + } + +/* Lookahead N consecutive tokens */ +#define LookAhead_1(tok, ...)\ + LookAhead(\ + case tok: {\ + __VA_ARGS__\ + }\ + ) + +#define LookAhead_2(tok1, tok2, ...)\ + LookAhead_1(tok1, \ + const char *old_ptr = pos;\ + pos = lookahead;\ + LookAhead(\ + case tok2: {\ + __VA_ARGS__\ + }\ + )\ + pos = old_ptr;\ + ) + +#define LookAhead_3(tok1, tok2, tok3, ...)\ + LookAhead_2(tok1, tok2, \ + const char *old_ptr = pos;\ + pos = lookahead;\ + LookAhead(\ + case tok3: {\ + __VA_ARGS__\ + }\ + )\ + pos = old_ptr;\ + ) + +/* Open scope */ +#define Scope(s, ...) {\ + ecs_script_scope_t *old_scope = parser->scope;\ + parser->scope = s;\ + __VA_ARGS__\ + parser->scope = old_scope;\ + } + +/* Parser loop */ +#define Loop(...)\ + int32_t token_stack_count = token_stack.count;\ + do {\ + token_stack.count = token_stack_count;\ + __VA_ARGS__\ + } while (true); + +#define EndOfRule return pos + +#endif diff --git a/vendors/flecs/src/addons/script/query_parser.c b/vendors/flecs/src/addons/script/query_parser.c new file mode 100644 index 000000000..a250d0adf --- /dev/null +++ b/vendors/flecs/src/addons/script/query_parser.c @@ -0,0 +1,782 @@ +/** + * @file addons/script/query_parser.c + * @brief Script grammar parser. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" +#include "parser.h" + +#define EcsTokTermIdentifier\ + EcsTokIdentifier:\ + case EcsTokNumber:\ + case EcsTokMul + +#define EcsTokEndOfTerm\ + '}':\ + pos --; /* Give token back to parser */\ + case EcsTokOr:\ + if (t->kind == EcsTokOr) {\ + if (parser->term->oper != EcsAnd) {\ + Error("cannot mix operators in || expression");\ + }\ + parser->term->oper = EcsOr;\ + }\ + case ',':\ + case '\n':\ + case '\0' + +// $this == +static +const char* flecs_term_parse_equality_pred( + ecs_script_parser_t *parser, + const char *pos, + ecs_entity_t pred) +{ + ParserBegin; + + if (parser->term->oper != EcsAnd) { + Error("cannot mix operator with equality expression"); + } + + parser->term->src = parser->term->first; + parser->term->first = (ecs_term_ref_t){0}; + parser->term->first.id = pred; + + Parse( + // $this == foo + // ^ + case EcsTokTermIdentifier: { + parser->term->second.name = Token(0); + Parse( case EcsTokEndOfTerm: EndOfRule; ) + } + + // $this == "foo" + // ^ + case EcsTokString: { + parser->term->second.name = Token(0); + parser->term->second.id = EcsIsName; + + if (pred == EcsPredMatch) { + if (Token(0)[0] == '!') { + /* If match expression starts with !, set Not operator. The + * reason the ! is embedded in the expression is because + * there is only a single match (~=) operator. */ + parser->term->second.name ++; + parser->term->oper = EcsNot; + } + } + + Parse( + case EcsTokEndOfTerm: + EndOfRule; + ) + } + ) + + ParserEnd; +} + +static +ecs_entity_t flecs_query_parse_trav_flags( + const char *tok) +{ + if (!ecs_os_strcmp(tok, "self")) return EcsSelf; + else if (!ecs_os_strcmp(tok, "up")) return EcsUp; + else if (!ecs_os_strcmp(tok, "cascade")) return EcsCascade; + else if (!ecs_os_strcmp(tok, "desc")) return EcsDesc; + else return 0; +} + +static +const char* flecs_term_parse_trav( + ecs_script_parser_t *parser, + ecs_term_ref_t *ref, + const char *pos) +{ + ParserBegin; + + Loop( + // self + Parse_1(EcsTokIdentifier, + ref->id |= flecs_query_parse_trav_flags(Token(0)); + + LookAhead( + // self| + case '|': + pos = lookahead; + continue; + + // self IsA + case EcsTokIdentifier: + pos = lookahead; + parser->term->trav = ecs_lookup( + parser->script->pub.world, Token(1)); + if (!parser->term->trav) { + Error( + "unresolved traversal relationship '%s'", Token(1)); + goto error; + } + + EndOfRule; + ) + + EndOfRule; + ) + ) + + ParserEnd; +} + +// Position( +static +const char* flecs_term_parse_arg( + ecs_script_parser_t *parser, + const char *pos, + int32_t arg) +{ + ParserBegin; + + ecs_term_ref_t *ref = NULL; + + // Position(src + if (arg == 0) { + ref = &parser->term->src; + + // Position(src, tgt + } else if (arg == 1) { + ref = &parser->term->second; + } else { + if (arg > FLECS_TERM_ARG_COUNT_MAX) { + Error("too many arguments in term"); + } + ref = &parser->extra_args[arg - 2]; + } + + bool is_trav_flag = false; + + LookAhead_1(EcsTokIdentifier, + is_trav_flag = flecs_query_parse_trav_flags(Token(0)) != 0; + ) + + if (is_trav_flag) { + // Position(self|up + // ^ + pos = flecs_term_parse_trav(parser, ref, pos); + if (!pos) { + goto error; + } + } else { + // Position(src + // ^ + Parse( + case EcsTokTermIdentifier: { + ref->name = Token(0); + + // Position(src| + // ^ + LookAhead_1('|', + pos = lookahead; + pos = flecs_term_parse_trav(parser, ref, pos); + if (!pos) { + goto error; + } + + // Position(src|up IsA + // ^ + LookAhead_1(EcsTokIdentifier, + pos = lookahead; + parser->term->trav = ecs_lookup( + parser->script->pub.world, Token(1)); + if (!parser->term->trav) { + Error( + "unresolved trav identifier '%s'", Token(1)); + } + ) + ) + + break; + } + ) + } + + Parse( + // Position(src, + // ^ + case ',': + if ((arg > 1) && parser->extra_oper != EcsAnd) { + Error("cannot mix operators in extra term arguments"); + } + parser->extra_oper = EcsAnd; + return flecs_term_parse_arg(parser, pos, arg + 1); + + // Position(src, second || + // ^ + case EcsTokOr: + if ((arg > 1) && parser->extra_oper != EcsOr) { + Error("cannot mix operators in extra term arguments"); + } + parser->extra_oper = EcsOr; + return flecs_term_parse_arg(parser, pos, arg + 1); + + // Position(src) + // ^ + case ')': + Parse( + case EcsTokEndOfTerm: + EndOfRule; + ) + ) + + ParserEnd; +} + +// Position +static +const char* flecs_term_parse_id( + ecs_script_parser_t *parser, + const char *pos) +{ + ParserBegin; + + Parse( + case EcsTokEq: + return flecs_term_parse_equality_pred( + parser, pos, EcsPredEq); + case EcsTokNeq: { + const char *ret = flecs_term_parse_equality_pred( + parser, pos, EcsPredEq); + if (ret) { + parser->term->oper = EcsNot; + } + return ret; + } + case EcsTokMatch: + return flecs_term_parse_equality_pred( + parser, pos, EcsPredMatch); + + // Position| + case '|': { + pos = flecs_term_parse_trav(parser, &parser->term->first, pos); + if (!pos) { + goto error; + } + + // Position|self( + Parse( + case '(': + return flecs_term_parse_arg(parser, pos, 0); + case EcsTokEndOfTerm: + EndOfRule; + ) + } + + // Position( + case '(': { + // Position() + LookAhead_1(')', + pos = lookahead; + parser->term->src.id = EcsIsEntity; + + Parse( + case EcsTokEndOfTerm: + EndOfRule; + ) + ) + + return flecs_term_parse_arg(parser, pos, 0); + } + + case EcsTokEndOfTerm: + EndOfRule; + ) + + ParserEnd; +} + +// ( +static const char* flecs_term_parse_pair( + ecs_script_parser_t *parser, + const char *pos) +{ + ParserBegin; + + // (Position + // ^ + Parse( + case EcsTokTermIdentifier: { + parser->term->first.name = Token(0); + + LookAhead_1('|', + // (Position|self + pos = lookahead; + pos = flecs_term_parse_trav( + parser, &parser->term->first, pos); + if (!pos) { + goto error; + } + ) + + // (Position, + Parse_1(',', + return flecs_term_parse_arg(parser, pos, 1); + ) + } + ) + + ParserEnd; +} + +// AND +static +const char* flecs_term_parse_flags( + ecs_script_parser_t *parser, + const char *token_0, + const char *pos) +{ + ecs_assert(token_0 != NULL, ECS_INTERNAL_ERROR, NULL); + + ParserBegin; + + ecs_id_t flag = 0; + int16_t oper = 0; + ecs_term_t *term = parser->term; + + // AND + if (!ecs_os_strcmp(token_0, "and")) oper = EcsAndFrom; + else if (!ecs_os_strcmp(token_0, "or")) oper = EcsOrFrom; + else if (!ecs_os_strcmp(token_0, "not")) oper = EcsNotFrom; + else if (!ecs_os_strcmp(token_0, "auto_override")) flag = ECS_AUTO_OVERRIDE; + else if (!ecs_os_strcmp(token_0, "toggle")) flag = ECS_TOGGLE; + else { + // Position + term->first.name = token_0; + return flecs_term_parse_id(parser, pos); + } + + if (oper || flag) { + // and | + // ^ + Parse_1('|', + Parse( + // and | Position + // ^ + case EcsTokTermIdentifier: { + if (oper) { + term->oper = oper; + } else if (flag) { + term->id = flag; + } + + term->first.name = Token(1); + + return flecs_term_parse_id(parser, pos); + } + + // and | ( + // ^ + case '(': { + return flecs_term_parse_pair(parser, pos); + } + ) + ) + } + + ParserEnd; +} + +// ! +static +const char* flecs_term_parse_unary( + ecs_script_parser_t *parser, + const char *pos) +{ + ParserBegin; + + Parse( + // !( + case '(': { + return flecs_term_parse_pair(parser, pos); + } + + // !{ + case '{': { + parser->term->first.id = EcsScopeOpen; + parser->term->src.id = EcsIsEntity; + parser->term->inout = EcsInOutNone; + EndOfRule; + } + + // !Position + // ^ + case EcsTokTermIdentifier: { + parser->term->first.name = Token(0); + return flecs_term_parse_id(parser, pos); + } + ) + + ParserEnd; +} + +// [ +static +const char* flecs_term_parse_inout( + ecs_script_parser_t *parser, + const char *pos) +{ + ParserBegin; + + ecs_term_t *term = parser->term; + + // [inout] + // ^ + Parse_2(EcsTokIdentifier, ']', + if (!ecs_os_strcmp(Token(0), "default")) term->inout = EcsInOutDefault; + else if (!ecs_os_strcmp(Token(0), "none")) term->inout = EcsInOutNone; + else if (!ecs_os_strcmp(Token(0), "filter")) term->inout = EcsInOutFilter; + else if (!ecs_os_strcmp(Token(0), "inout")) term->inout = EcsInOut; + else if (!ecs_os_strcmp(Token(0), "in")) term->inout = EcsIn; + else if (!ecs_os_strcmp(Token(0), "out")) term->inout = EcsOut; + + Parse( + // [inout] Position + // ^ + case EcsTokTermIdentifier: { + return flecs_term_parse_flags(parser, Token(2), pos); + } + + // [inout] !Position + // ^ + case '!': + term->oper = EcsNot; + return flecs_term_parse_unary(parser, pos); + case '?': + term->oper = EcsOptional; + return flecs_term_parse_unary(parser, pos); + + // [inout] ( + // ^ + case '(': { + return flecs_term_parse_pair(parser, pos); + } + ) + ) + + ParserEnd; +} + +static +const char* flecs_query_term_parse( + ecs_script_parser_t *parser, + const char *pos) +{ + ParserBegin; + + Parse( + case '[': + return flecs_term_parse_inout(parser, pos); + case EcsTokTermIdentifier: + return flecs_term_parse_flags(parser, Token(0), pos); + case '(': + return flecs_term_parse_pair(parser, pos); + case '!': + parser->term->oper = EcsNot; + return flecs_term_parse_unary(parser, pos); + case '?': + parser->term->oper = EcsOptional; + return flecs_term_parse_unary(parser, pos); + case '{': + parser->term->first.id = EcsScopeOpen; + parser->term->src.id = EcsIsEntity; + parser->term->inout = EcsInOutNone; + EndOfRule; + case '}': + parser->term->first.id = EcsScopeClose; + parser->term->src.id = EcsIsEntity; + parser->term->inout = EcsInOutNone; + LookAhead_1(',', + pos = lookahead; + ) + EndOfRule; + case '\n':\ + case '\0': + EndOfRule; + ); + + ParserEnd; +} + +int flecs_terms_parse( + ecs_script_t *script, + ecs_term_t *terms, + int32_t *term_count_out) +{ + if (!ecs_os_strcmp(script->code, "0")) { + *term_count_out = 0; + return 0; + } + + ecs_script_parser_t parser = { + .script = flecs_script_impl(script), + .pos = script->code + }; + + parser.token_cur = flecs_script_impl(script)->token_buffer; + + int32_t term_count = 0; + const char *ptr = script->code; + ecs_term_ref_t extra_args[FLECS_TERM_ARG_COUNT_MAX]; + ecs_os_memset_n(extra_args, 0, ecs_term_ref_t, + FLECS_TERM_ARG_COUNT_MAX); + + parser.extra_args = extra_args; + parser.extra_oper = 0; + + do { + if (term_count == FLECS_TERM_COUNT_MAX) { + ecs_err("max number of terms (%d) reached, increase " + "FLECS_TERM_COUNT_MAX to support more", + FLECS_TERM_COUNT_MAX); + goto error; + } + + /* Parse next term */ + ecs_term_t *term = &terms[term_count]; + parser.term = term; + ecs_os_memset_t(term, 0, ecs_term_t); + ecs_os_memset_n(extra_args, 0, ecs_term_ref_t, FLECS_TERM_ARG_COUNT_MAX); + parser.extra_oper = 0; + + ptr = flecs_query_term_parse(&parser, ptr); + if (!ptr) { + /* Parser error */ + goto error; + } + + if (!ecs_term_is_initialized(term)) { + /* Last term parsed */ + break; + } + + term_count ++; + + /* Unpack terms with more than two args into multiple terms so that: + * Rel(X, Y, Z) + * becomes: + * Rel(X, Y), Rel(Y, Z) */ + int32_t arg = 0; + while (ecs_term_ref_is_set(&extra_args[arg ++])) { + ecs_assert(arg <= FLECS_TERM_ARG_COUNT_MAX, + ECS_INTERNAL_ERROR, NULL); + + if (term_count == FLECS_TERM_COUNT_MAX) { + ecs_err("max number of terms (%d) reached, increase " + "FLECS_TERM_COUNT_MAX to support more", + FLECS_TERM_COUNT_MAX); + goto error; + } + + term = &terms[term_count ++]; + *term = term[-1]; + + if (parser.extra_oper == EcsAnd) { + term->src = term[-1].second; + term->second = extra_args[arg - 1]; + } else if (parser.extra_oper == EcsOr) { + term->src = term[-1].src; + term->second = extra_args[arg - 1]; + term[-1].oper = EcsOr; + } + + if (term->first.name != NULL) { + term->first.name = term->first.name; + } + + if (term->src.name != NULL) { + term->src.name = term->src.name; + } + } + + if (arg) { + ecs_os_memset_n(extra_args, 0, ecs_term_ref_t, + FLECS_TERM_ARG_COUNT_MAX); + } + } while (ptr[0]); + + (*term_count_out) += term_count; + + return 0; +error: + return -1; +} + +const char* flecs_term_parse( + ecs_world_t *world, + const char *name, + const char *expr, + ecs_term_t *term, + char *token_buffer) +{ + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(expr != NULL, ECS_INVALID_PARAMETER, name); + ecs_assert(term != NULL, ECS_INVALID_PARAMETER, NULL); + + EcsParserFixedBuffer(world, name, expr, token_buffer, 256); + parser.term = term; + + const char *result = flecs_query_term_parse(&parser, expr); + if (!result) { + return NULL; + } + + ecs_os_memset_t(term, 0, ecs_term_t); + + return flecs_query_term_parse(&parser, expr); +} + +const char* flecs_id_parse( + const ecs_world_t *world, + const char *name, + const char *expr, + ecs_id_t *id) +{ + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(expr != NULL, ECS_INVALID_PARAMETER, name); + ecs_assert(id != NULL, ECS_INVALID_PARAMETER, NULL); + + char token_buffer[256]; + ecs_term_t term = {0}; + EcsParserFixedBuffer(world, name, expr, token_buffer, 256); + parser.term = &term; + + expr = flecs_scan_whitespace(&parser, expr); + if (!ecs_os_strcmp(expr, "#0")) { + *id = 0; + return &expr[1]; + } + + const char *result = flecs_query_term_parse(&parser, expr); + if (!result) { + return NULL; + } + + if (ecs_term_finalize(world, &term)) { + return NULL; + } + + if (term.oper != EcsAnd) { + ecs_parser_error(name, expr, (result - expr), + "invalid operator for add expression"); + return NULL; + } + + if ((term.src.id & ~EcsTraverseFlags) != (EcsThis|EcsIsVariable)) { + ecs_parser_error(name, expr, (result - expr), + "invalid source for add expression (must be $this)"); + return NULL; + } + + *id = term.id; + + return result; +} + +static +const char* flecs_query_arg_parse( + ecs_script_parser_t *parser, + ecs_query_t *q, + ecs_iter_t *it, + const char *pos) +{ + ParserBegin; + + Parse_3(EcsTokIdentifier, ':', EcsTokIdentifier, { + int var = ecs_query_find_var(q, Token(0)); + if (var == -1) { + Error("unknown variable '%s'", Token(0)); + } + + ecs_entity_t val = ecs_lookup(q->world, Token(2)); + if (!val) { + Error("unresolved entity '%s'", Token(2)); + } + + ecs_iter_set_var(it, var, val); + + EndOfRule; + }) + + ParserEnd; +} + +static +const char* flecs_query_args_parse( + ecs_script_parser_t *parser, + ecs_query_t *q, + ecs_iter_t *it, + const char *pos) +{ + ParserBegin; + + bool has_paren = false; + LookAhead( + case '\0': + pos = lookahead; + EndOfRule; + case '(': { + pos = lookahead; + has_paren = true; + LookAhead_1(')', + pos = lookahead; + EndOfRule; + ) + } + ) + + Loop( + pos = flecs_query_arg_parse(parser, q, it, pos); + if (!pos) { + goto error; + } + + Parse( + case ',': + continue; + case '\0': + EndOfRule; + case ')': + if (!has_paren) { + Error("unexpected ')' without opening '(')"); + } + EndOfRule; + ) + ) + + ParserEnd; +} + +const char* ecs_query_args_parse( + ecs_query_t *q, + ecs_iter_t *it, + const char *expr) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(expr != NULL, ECS_INVALID_PARAMETER, NULL); + + const char *q_name = q->entity ? ecs_get_name(q->world, q->entity) : NULL; + if (ecs_os_strlen(expr) > 512) { + ecs_parser_error(q_name, expr, 0, "query argument expression too long"); + return NULL; + } + + char token_buffer[1024]; + EcsParserFixedBuffer(q->world, q_name, expr, token_buffer, 256); + return flecs_query_args_parse(&parser, q, it, expr); +error: + return NULL; +} + +#endif diff --git a/vendors/flecs/src/addons/script/script.c b/vendors/flecs/src/addons/script/script.c new file mode 100644 index 000000000..2cfa85df7 --- /dev/null +++ b/vendors/flecs/src/addons/script/script.c @@ -0,0 +1,300 @@ +/** + * @file addons/script/script.c + * @brief Script API. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" + +ECS_COMPONENT_DECLARE(EcsScript); + +static +ECS_MOVE(EcsScript, dst, src, { + if (dst->script && (dst->script != src->script)) { + if (dst->template_ && (dst->template_ != src->template_)) { + flecs_script_template_fini( + flecs_script_impl(dst->script), dst->template_); + } + ecs_script_free(dst->script); + } + dst->script = src->script; + dst->template_ = src->template_; + src->script = NULL; + src->template_ = NULL; +}) + +static +ECS_DTOR(EcsScript, ptr, { + if (ptr->template_) { + flecs_script_template_fini( + flecs_script_impl(ptr->script), ptr->template_); + } + if (ptr->script) { + ecs_script_free(ptr->script); + } +}) + +static +ecs_id_t flecs_script_tag( + ecs_entity_t script, + ecs_entity_t instance) +{ + if (!instance) { + return ecs_pair_t(EcsScript, script); + } else { + return ecs_pair(EcsChildOf, instance); + } +} + +ecs_script_t* flecs_script_new( + ecs_world_t *world) +{ + ecs_script_impl_t *result = ecs_os_calloc_t(ecs_script_impl_t); + flecs_allocator_init(&result->allocator); + ecs_script_parser_t parser = { .script = result }; + result->root = flecs_script_scope_new(&parser); + result->pub.world = world; + result->refcount = 1; + return &result->pub; +} + +void ecs_script_clear( + ecs_world_t *world, + ecs_entity_t script, + ecs_entity_t instance) +{ + ecs_delete_with(world, flecs_script_tag(script, instance)); +} + +int ecs_script_run( + ecs_world_t *world, + const char *name, + const char *code) +{ + ecs_script_t *script = ecs_script_parse(world, name, code); + if (!script) { + goto error; + } + + ecs_entity_t prev_scope = ecs_set_scope(world, 0); + + if (ecs_script_eval(script)) { + goto error_free; + } + + ecs_set_scope(world, prev_scope); + + ecs_script_free(script); + return 0; +error_free: + ecs_script_free(script); +error: + return -1; +} + +int ecs_script_run_file( + ecs_world_t *world, + const char *filename) +{ + char *script = flecs_load_from_file(filename); + if (!script) { + return -1; + } + + int result = ecs_script_run(world, filename, script); + ecs_os_free(script); + return result; +} + +void ecs_script_free( + ecs_script_t *script) +{ + ecs_script_impl_t *impl = flecs_script_impl(script); + ecs_check(impl->refcount > 0, ECS_INVALID_OPERATION, NULL); + if (!--impl->refcount) { + flecs_script_visit_free(script); + flecs_free(&impl->allocator, + impl->token_buffer_size, impl->token_buffer); + flecs_allocator_fini(&impl->allocator); + ecs_os_free(ECS_CONST_CAST(char*, impl->pub.name)); /* safe, owned value */ + ecs_os_free(ECS_CONST_CAST(char*, impl->pub.code)); /* safe, owned value */ + ecs_os_free(impl); + } +error: + return; +} + +int ecs_script_update( + ecs_world_t *world, + ecs_entity_t e, + ecs_entity_t instance, + const char *code) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(code != NULL, ECS_INTERNAL_ERROR, NULL); + + const char *name = ecs_get_name(world, e); + EcsScript *s = ecs_ensure(world, e, EcsScript); + if (s->template_) { + char *template_name = ecs_get_path(world, s->template_->entity); + ecs_err("cannot update scripts for individual templates, " + "update parent script instead (tried to update '%s')", + template_name); + ecs_os_free(template_name); + return -1; + } + + if (s->script) { + ecs_script_free(s->script); + } + + s->script = ecs_script_parse(world, name, code); + if (!s->script) { + return -1; + } + + int result = 0; + bool is_defer = ecs_is_deferred(world); + ecs_suspend_readonly_state_t srs; + ecs_world_t *real_world = NULL; + if (is_defer) { + ecs_assert(flecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL); + real_world = flecs_suspend_readonly(world, &srs); + ecs_assert(real_world != NULL, ECS_INTERNAL_ERROR, NULL); + } + + ecs_script_clear(world, e, instance); + + ecs_entity_t prev = ecs_set_with(world, flecs_script_tag(e, instance)); + + if (ecs_script_eval(s->script)) { + ecs_delete_with(world, ecs_pair_t(EcsScript, e)); + result = -1; + } + + ecs_set_with(world, prev); + + if (is_defer) { + flecs_resume_readonly(real_world, &srs); + } + + return result; +} + +ecs_entity_t ecs_script_init( + ecs_world_t *world, + const ecs_script_desc_t *desc) +{ + const char *script = NULL; + ecs_entity_t e = desc->entity; + + ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_check(desc != NULL, ECS_INTERNAL_ERROR, NULL); + + if (!e) { + if (desc->filename) { + e = ecs_new_from_path_w_sep(world, 0, desc->filename, "/", NULL); + } else { + e = ecs_new(world); + } + } + + script = desc->code; + if (!script && desc->filename) { + script = flecs_load_from_file(desc->filename); + if (!script) { + goto error; + } + } + + if (ecs_script_update(world, e, 0, script)) { + goto error; + } + + if (script != desc->code) { + /* Safe cast, only happens when script is loaded from file */ + ecs_os_free(ECS_CONST_CAST(char*, script)); + } + + return e; +error: + if (script != desc->code) { + /* Safe cast, only happens when script is loaded from file */ + ecs_os_free(ECS_CONST_CAST(char*, script)); + } + if (!desc->entity) { + ecs_delete(world, e); + } + return 0; +} + +static +int EcsScript_serialize(const ecs_serializer_t *ser, const void *ptr) { + const EcsScript *data = ptr; + if (data->script) { + ser->member(ser, "name"); + ser->value(ser, ecs_id(ecs_string_t), &data->script->name); + ser->member(ser, "code"); + ser->value(ser, ecs_id(ecs_string_t), &data->script->code); + + char *ast = ecs_script_ast_to_str(data->script); + ser->member(ser, "ast"); + ser->value(ser, ecs_id(ecs_string_t), &ast); + ecs_os_free(ast); + } else { + char *nullString = NULL; + ser->member(ser, "name"); + ser->value(ser, ecs_id(ecs_string_t), &nullString); + ser->member(ser, "code"); + ser->value(ser, ecs_id(ecs_string_t), &nullString); + ser->member(ser, "ast"); + ser->value(ser, ecs_id(ecs_string_t), &nullString); + } + return 0; +} + +void FlecsScriptImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsScript); + ECS_IMPORT(world, FlecsMeta); +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsScript), + "Module with components for managing Flecs scripts"); +#endif + + ecs_set_name_prefix(world, "Ecs"); + ECS_COMPONENT_DEFINE(world, EcsScript); + + ecs_set_hooks(world, EcsScript, { + .ctor = flecs_default_ctor, + .move = ecs_move(EcsScript), + .dtor = ecs_dtor(EcsScript) + }); + + ECS_COMPONENT(world, ecs_script_t); + + ecs_struct(world, { + .entity = ecs_id(ecs_script_t), + .members = { + { .name = "name", .type = ecs_id(ecs_string_t) }, + { .name = "code", .type = ecs_id(ecs_string_t) }, + { .name = "ast", .type = ecs_id(ecs_string_t) } + } + }); + + ecs_opaque(world, { + .entity = ecs_id(EcsScript), + .type.as_type = ecs_id(ecs_script_t), + .type.serialize = EcsScript_serialize + }); + + ecs_add_id(world, ecs_id(EcsScript), EcsPairIsTag); + ecs_add_id(world, ecs_id(EcsScript), EcsPrivate); + ecs_add_pair(world, ecs_id(EcsScript), EcsOnInstantiate, EcsDontInherit); +} + +#endif diff --git a/vendors/flecs/src/addons/script/script.h b/vendors/flecs/src/addons/script/script.h new file mode 100644 index 000000000..90949b2a0 --- /dev/null +++ b/vendors/flecs/src/addons/script/script.h @@ -0,0 +1,104 @@ +/** + * @file addons/script/script.h + * @brief Flecs script implementation. + */ + +#ifndef FLECS_SCRIPT_PRIVATE_H +#define FLECS_SCRIPT_PRIVATE_H + +#include "../../private_api.h" + +#ifdef FLECS_SCRIPT + +#include + +typedef struct ecs_script_scope_t ecs_script_scope_t; +typedef struct ecs_script_entity_t ecs_script_entity_t; + +typedef struct ecs_script_impl_t { + ecs_script_t pub; + ecs_allocator_t allocator; + ecs_script_scope_t *root; + char *token_buffer; + int32_t token_buffer_size; + int32_t refcount; +} ecs_script_impl_t; + +typedef struct ecs_script_parser_t ecs_script_parser_t; + +#define flecs_script_impl(script) ((ecs_script_impl_t*)script) + +#include "tokenizer.h" + +struct ecs_script_parser_t { + ecs_script_impl_t *script; + ecs_script_scope_t *scope; + const char *pos; + char *token_cur; + char *token_keep; + bool significant_newline; + + /* For term parser */ + ecs_term_t *term; + ecs_oper_kind_t extra_oper; + ecs_term_ref_t *extra_args; +}; + +#include "ast.h" +#include "visit.h" +#include "visit_eval.h" + +struct ecs_script_template_t { + /* Template handle */ + ecs_entity_t entity; + + /* Template AST node */ + ecs_script_template_node_t *node; + + /* Hoisted using statements */ + ecs_vec_t using_; + + /* Hoisted variables */ + ecs_script_vars_t *vars; + + /* Default values for props */ + ecs_vec_t prop_defaults; + + /* Type info for template component */ + const ecs_type_info_t *type_info; +}; + +ecs_script_t* flecs_script_new( + ecs_world_t *world); + +ecs_script_scope_t* flecs_script_scope_new( + ecs_script_parser_t *parser); + +int flecs_script_visit_free( + ecs_script_t *script); + +ecs_script_vars_t* flecs_script_vars_push( + ecs_script_vars_t *parent, + ecs_stack_t *stack, + ecs_allocator_t *allocator); + +int flecs_terms_parse( + ecs_script_t *script, + ecs_term_t *terms, + int32_t *term_count_out); + +const char* flecs_id_parse( + const ecs_world_t *world, + const char *name, + const char *expr, + ecs_id_t *id); + +const char* flecs_term_parse( + ecs_world_t *world, + const char *name, + const char *expr, + ecs_term_t *term, + char *token_buffer); + +#endif // FLECS_SCRIPT +#endif // FLECS_SCRIPT_PRIVATE_H diff --git a/vendors/flecs/src/addons/expr/serialize.c b/vendors/flecs/src/addons/script/serialize.c similarity index 70% rename from vendors/flecs/src/addons/expr/serialize.c rename to vendors/flecs/src/addons/script/serialize.c index 106e50b5e..ca04c803c 100644 --- a/vendors/flecs/src/addons/expr/serialize.c +++ b/vendors/flecs/src/addons/script/serialize.c @@ -1,11 +1,13 @@ /** - * @file expr/serialize.c - * @brief Serialize (component) values to flecs string format. + * @file addons/script/serialize.c + * @brief Serialize values to string. */ -#include "../../private_api.h" +#include "flecs.h" +#include "../meta/meta.h" -#ifdef FLECS_EXPR +#ifdef FLECS_SCRIPT +#include "script.h" static int flecs_expr_ser_type( @@ -38,126 +40,6 @@ ecs_primitive_kind_t flecs_expr_op_to_primitive_kind(ecs_meta_type_op_kind_t kin return kind - EcsOpPrimitive; } -/* Serialize a primitive value */ -static -int flecs_expr_ser_primitive( - const ecs_world_t *world, - ecs_primitive_kind_t kind, - const void *base, - ecs_strbuf_t *str, - bool is_expr) -{ - switch(kind) { - case EcsBool: - if (*(const bool*)base) { - ecs_strbuf_appendlit(str, "true"); - } else { - ecs_strbuf_appendlit(str, "false"); - } - break; - case EcsChar: { - char chbuf[3]; - char ch = *(const char*)base; - if (ch) { - ecs_chresc(chbuf, *(const char*)base, '"'); - if (is_expr) ecs_strbuf_appendch(str, '"'); - ecs_strbuf_appendstr(str, chbuf); - if (is_expr) ecs_strbuf_appendch(str, '"'); - } else { - ecs_strbuf_appendch(str, '0'); - } - break; - } - case EcsByte: - ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint8_t*)base)); - break; - case EcsU8: - ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint8_t*)base)); - break; - case EcsU16: - ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint16_t*)base)); - break; - case EcsU32: - ecs_strbuf_appendint(str, flecs_uto(int64_t, *(const uint32_t*)base)); - break; - case EcsU64: - ecs_strbuf_append(str, "%llu", *(const uint64_t*)base); - break; - case EcsI8: - ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int8_t*)base)); - break; - case EcsI16: - ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int16_t*)base)); - break; - case EcsI32: - ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const int32_t*)base)); - break; - case EcsI64: - ecs_strbuf_appendint(str, *(const int64_t*)base); - break; - case EcsF32: - ecs_strbuf_appendflt(str, (double)*(const float*)base, 0); - break; - case EcsF64: - ecs_strbuf_appendflt(str, *(const double*)base, 0); - break; - case EcsIPtr: - ecs_strbuf_appendint(str, flecs_ito(int64_t, *(const intptr_t*)base)); - break; - case EcsUPtr: - ecs_strbuf_append(str, "%u", *(const uintptr_t*)base); - break; - case EcsString: { - const char *value = *ECS_CONST_CAST(const char**, base); - if (value) { - if (!is_expr) { - ecs_strbuf_appendstr(str, value); - } else { - ecs_size_t length = ecs_stresc(NULL, 0, '"', value); - if (length == ecs_os_strlen(value)) { - ecs_strbuf_appendch(str, '"'); - ecs_strbuf_appendstrn(str, value, length); - ecs_strbuf_appendch(str, '"'); - } else { - char *out = ecs_os_malloc(length + 3); - ecs_stresc(out + 1, length, '"', value); - out[0] = '"'; - out[length + 1] = '"'; - out[length + 2] = '\0'; - ecs_strbuf_appendstr_zerocpy(str, out); - } - } - } else { - ecs_strbuf_appendlit(str, "null"); - } - break; - } - case EcsEntity: { - ecs_entity_t e = *(const ecs_entity_t*)base; - if (!e) { - ecs_strbuf_appendch(str, '0'); - } else { - ecs_get_path_w_sep_buf(world, 0, e, ".", NULL, str); - } - break; - } - case EcsId: { - ecs_id_t id = *(const ecs_id_t*)base; - if (!id) { - ecs_strbuf_appendch(str, '0'); - } else { - ecs_id_str_buf(world, id, str); - } - break; - } - default: - ecs_err("invalid primitive kind"); - return -1; - } - - return 0; -} - /* Serialize enumeration */ static int flecs_expr_ser_enum( @@ -176,7 +58,7 @@ int flecs_expr_ser_enum( ecs_enum_constant_t *c = ecs_map_get_deref(&enum_type->constants, ecs_enum_constant_t, (ecs_map_key_t)val); if (!c) { - char *path = ecs_get_fullpath(world, op->type); + char *path = ecs_get_path(world, op->type); ecs_err("value %d is not valid for enum type '%s'", val, path); ecs_os_free(path); goto error; @@ -219,7 +101,7 @@ int flecs_expr_ser_bitmask( if (value != 0) { /* All bits must have been matched by a constant */ - char *path = ecs_get_fullpath(world, op->type); + char *path = ecs_get_path(world, op->type); ecs_err( "value for bitmask %s contains bits (%u) that cannot be mapped to constant", path, value); @@ -279,8 +161,8 @@ int expr_ser_type_elements( ecs_strbuf_t *str, bool is_array) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); const EcsComponent *comp = ecs_get(world, type, EcsComponent); @@ -501,10 +383,10 @@ int ecs_ptr_to_expr_buf( const void *ptr, ecs_strbuf_t *buf_out) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); if (ser == NULL) { - char *path = ecs_get_fullpath(world, type); + char *path = ecs_get_path(world, type); ecs_err("cannot serialize value for type '%s'", path); ecs_os_free(path); goto error; @@ -540,10 +422,10 @@ int ecs_ptr_to_str_buf( const void *ptr, ecs_strbuf_t *buf_out) { - const EcsMetaTypeSerialized *ser = ecs_get( - world, type, EcsMetaTypeSerialized); + const EcsTypeSerializer *ser = ecs_get( + world, type, EcsTypeSerializer); if (ser == NULL) { - char *path = ecs_get_fullpath(world, type); + char *path = ecs_get_path(world, type); ecs_err("cannot serialize value for type '%s'", path); ecs_os_free(path); goto error; @@ -573,13 +455,4 @@ char* ecs_ptr_to_str( return ecs_strbuf_get(&str); } -int ecs_primitive_to_expr_buf( - const ecs_world_t *world, - ecs_primitive_kind_t kind, - const void *base, - ecs_strbuf_t *str) -{ - return flecs_expr_ser_primitive(world, kind, base, str, true); -} - #endif diff --git a/vendors/flecs/src/addons/script/template.c b/vendors/flecs/src/addons/script/template.c new file mode 100644 index 000000000..db0f8903d --- /dev/null +++ b/vendors/flecs/src/addons/script/template.c @@ -0,0 +1,430 @@ +/** + * @file addons/script/template.c + * @brief Script template implementation. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" + +/* Template ctor to initialize with default property values */ +static +void flecs_script_template_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *ti) +{ + ecs_world_t *world = ti->hooks.ctx; + ecs_entity_t template_entity = ti->component; + + const EcsStruct *st = ecs_get(world, template_entity, EcsStruct); + if (!st) { + ecs_os_memset(ptr, 0, count * ti->size); + return; + } + + const EcsScript *script = ecs_get(world, template_entity, EcsScript); + if (!script) { + ecs_err("template '%s' is not a script, cannot construct", ti->name); + return; + } + + ecs_script_template_t *template = script->template_; + ecs_assert(template != NULL, ECS_INTERNAL_ERROR, NULL); + if (st->members.count != template->prop_defaults.count) { + ecs_err("number of props (%d) of template '%s' does not match members" + " (%d), cannot construct", template->prop_defaults.count, + ti->name, st->members.count); + return; + } + + const ecs_member_t *members = st->members.array; + int32_t i, m, member_count = st->members.count; + ecs_script_var_t *values = template->prop_defaults.array; + for (m = 0; m < member_count; m ++) { + const ecs_member_t *member = &members[m]; + ecs_script_var_t *value = &values[m]; + const ecs_type_info_t *mti = value->type_info; + ecs_assert(mti != NULL, ECS_INTERNAL_ERROR, NULL); + + for (i = 0; i < count; i ++) { + void *el = ECS_ELEM(ptr, ti->size, i); + ecs_value_copy_w_type_info(world, mti, + ECS_OFFSET(el, member->offset), value->value.ptr); + } + } +} + +/* Template on_set handler to update contents for new property values */ +static +void flecs_script_template_on_set( + ecs_iter_t *it) +{ + if (it->table->flags & EcsTableIsPrefab) { + /* Don't instantiate templates for prefabs */ + return; + } + + ecs_world_t *world = it->world; + ecs_entity_t template_entity = ecs_field_id(it, 0); + ecs_record_t *r = ecs_record_find(world, template_entity); + if (!r) { + ecs_err("template entity is empty (should never happen)"); + return; + } + + const EcsScript *script = ecs_record_get(world, r, EcsScript); + if (!script) { + ecs_err("template is missing script component"); + return; + } + + ecs_script_template_t *template = script->template_; + ecs_assert(template != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *ti = template->type_info; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + const EcsStruct *st = ecs_record_get(world, r, EcsStruct); + + void *data = ecs_field_w_size(it, flecs_ito(size_t, ti->size), 0); + + ecs_script_eval_visitor_t v; + flecs_script_eval_visit_init(flecs_script_impl(script->script), &v); + ecs_vec_t prev_using = v.using; + v.using = template->using_; + + ecs_script_scope_t *scope = template->node->scope; + + /* Dummy entity node for instance */ + ecs_script_entity_t instance_node = { + .node = { + .kind = EcsAstEntity, + .pos = template->node->node.pos + }, + .scope = scope + }; + + v.entity = &instance_node; + + int32_t i, m; + for (i = 0; i < it->count; i ++) { + v.parent = it->entities[i]; + instance_node.eval = it->entities[i]; + + /* Create variables to hold template properties */ + ecs_script_vars_t *vars = flecs_script_vars_push( + NULL, &v.stack, v.allocator); + vars->parent = template->vars; /* Include hoisted variables */ + + /* Populate properties from template members */ + if (st) { + const ecs_member_t *members = st->members.array; + for (m = 0; m < st->members.count; m ++) { + const ecs_member_t *member = &members[m]; + + /* Assign template property from template instance */ + ecs_script_var_t *var = ecs_script_vars_declare(vars, member->name); + var->value.type = member->type; + var->value.ptr = ECS_OFFSET(data, member->offset); + } + } + + /* Populate $this variable with instance entity */ + ecs_entity_t instance = it->entities[i]; + ecs_script_var_t *var = ecs_script_vars_declare(vars, "this"); + var->value.type = ecs_id(ecs_entity_t); + var->value.ptr = &instance; + + bool is_defer = ecs_is_deferred(world); + ecs_suspend_readonly_state_t srs; + ecs_world_t *real_world = NULL; + if (is_defer) { + ecs_assert(flecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL); + real_world = flecs_suspend_readonly(world, &srs); + ecs_assert(real_world != NULL, ECS_INTERNAL_ERROR, NULL); + } + + ecs_script_clear(world, template_entity, instance); + + /* Run template code */ + v.vars = vars; + ecs_script_visit_scope(&v, scope); + + if (is_defer) { + flecs_resume_readonly(real_world, &srs); + } + + /* Pop variable scope */ + ecs_script_vars_pop(vars); + + data = ECS_OFFSET(data, ti->size); + } + + v.using = prev_using; + flecs_script_eval_visit_fini(&v); +} + +static +int flecs_script_template_eval_prop( + ecs_script_eval_visitor_t *v, + ecs_script_var_node_t *node) +{ + ecs_script_template_t *template = v->template; + ecs_script_var_t *var = ecs_script_vars_declare(v->vars, node->name); + if (!var) { + flecs_script_eval_error(v, node, + "variable '%s' redeclared", node->name); + return -1; + } + + if (node->type) { + ecs_entity_t type = flecs_script_find_entity(v, 0, node->type); + if (!type) { + flecs_script_eval_error(v, node, + "unresolved type '%s' for const variable '%s'", + node->type, node->name); + return -1; + } + + const ecs_type_info_t *ti = flecs_script_get_type_info(v, node, type); + if (!ti) { + return -1; + } + + var->value.type = type; + var->value.ptr = flecs_stack_alloc(&v->stack, ti->size, ti->alignment); + var->type_info = ti; + + if (flecs_script_eval_expr(v, node->expr, &var->value)) { + return -1; + } + + ecs_script_var_t *value = ecs_vec_append_t(&v->base.script->allocator, + &template->prop_defaults, ecs_script_var_t); + value->value.ptr = flecs_calloc(&v->base.script->allocator, ti->size); + value->value.type = type; + value->type_info = ti; + ecs_value_copy_w_type_info( + v->world, ti, value->value.ptr, var->value.ptr); + + ecs_entity_t mbr = ecs_entity(v->world, { + .name = node->name, + .parent = template->entity + }); + + ecs_set(v->world, mbr, EcsMember, { .type = var->value.type }); + } + + return 0; +} + +static +int flecs_script_template_eval( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node) +{ + switch(node->kind) { + case EcsAstScope: + case EcsAstTag: + case EcsAstComponent: + case EcsAstVarComponent: + case EcsAstDefaultComponent: + case EcsAstWithVar: + case EcsAstWithTag: + case EcsAstWithComponent: + case EcsAstUsing: + case EcsAstModule: + case EcsAstAnnotation: + case EcsAstConst: + case EcsAstEntity: + case EcsAstPairScope: + case EcsAstTemplate: + break; + case EcsAstProp: + return flecs_script_template_eval_prop(v, (ecs_script_var_node_t*)node); + case EcsAstWith: + if (ecs_script_visit_scope(v, ((ecs_script_with_t*)node)->expressions)) { + return -1; + } + if (ecs_script_visit_scope(v, ((ecs_script_with_t*)node)->scope)) { + return -1; + } + return 0; + case EcsAstIf: + if (ecs_script_visit_scope(v, ((ecs_script_if_t*)node)->if_true)) { + return -1; + } + if (((ecs_script_if_t*)node)->if_false) { + if (ecs_script_visit_scope(v, ((ecs_script_if_t*)node)->if_false)) { + return -1; + } + } + return 0; + } + + return flecs_script_eval_node(v, node); +} + +static +int flecs_script_template_preprocess( + ecs_script_eval_visitor_t *v, + ecs_script_template_t *template) +{ + ecs_visit_action_t prev_visit = v->base.visit; + v->template = template; + v->base.visit = (ecs_visit_action_t)flecs_script_template_eval; + v->vars = flecs_script_vars_push(v->vars, &v->stack, v->allocator); + int result = ecs_script_visit_scope(v, template->node->scope); + v->vars = ecs_script_vars_pop(v->vars); + v->base.visit = prev_visit; + v->template = NULL; + return result; +} + +static +int flecs_script_template_hoist_using( + ecs_script_eval_visitor_t *v, + ecs_script_template_t *template) +{ + if (v->module) { + ecs_vec_append_t( + v->allocator, &template->using_, ecs_entity_t)[0] = v->module; + } + + int i, count = ecs_vec_count(&v->using); + for (i = 0; i < count; i ++) { + ecs_vec_append_t(v->allocator, &template->using_, ecs_entity_t)[0] = + ecs_vec_get_t(&v->using, ecs_entity_t, i)[0]; + } + + return 0; +} + +static +int flecs_script_template_hoist_vars( + ecs_script_eval_visitor_t *v, + ecs_script_template_t *template, + ecs_script_vars_t *vars) +{ + if (vars->parent) { + flecs_script_template_hoist_vars(v, template, vars); + } + + int32_t i, count = ecs_vec_count(&vars->vars); + ecs_script_var_t *src_vars = ecs_vec_first(&vars->vars); + for (i = 0; i < count; i ++) { + ecs_script_var_t *src = &src_vars[i]; + ecs_script_var_t *dst = ecs_script_vars_define_id( + template->vars, src->name, src->value.type); + ecs_value_copy(v->world, + src->value.type, dst->value.ptr, src->value.ptr); + } + + return 0; +} + +ecs_script_template_t* flecs_script_template_init( + ecs_script_impl_t *script) +{ + ecs_allocator_t *a = &script->allocator; + ecs_script_template_t *result = flecs_alloc_t(a, ecs_script_template_t); + ecs_vec_init_t(NULL, &result->prop_defaults, ecs_script_var_t, 0); + ecs_vec_init_t(NULL, &result->using_, ecs_entity_t, 0); + result->vars = ecs_script_vars_init(script->pub.world); + return result; +} + +void flecs_script_template_fini( + ecs_script_impl_t *script, + ecs_script_template_t *template) +{ + ecs_assert(script != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_allocator_t *a = &script->allocator; + + int32_t i, count = ecs_vec_count(&template->prop_defaults); + ecs_script_var_t *values = ecs_vec_first(&template->prop_defaults); + for (i = 0; i < count; i ++) { + ecs_script_var_t *value = &values[i]; + const ecs_type_info_t *ti = value->type_info; + if (ti->hooks.dtor) { + ti->hooks.dtor(value->value.ptr, 1, ti); + } + flecs_free(a, ti->size, value->value.ptr); + } + + ecs_vec_fini_t(a, &template->prop_defaults, ecs_script_var_t); + ecs_vec_fini_t(a, &template->using_, ecs_entity_t); + ecs_script_vars_fini(template->vars); + flecs_free_t(a, ecs_script_template_t, template); +} + +/* Create new template */ +int flecs_script_eval_template( + ecs_script_eval_visitor_t *v, + ecs_script_template_node_t *node) +{ + if (v->template) { + flecs_script_eval_error(v, node, "nested templates are not allowed"); + return -1; + } + + ecs_entity_t template_entity = flecs_script_create_entity(v, node->name); + if (!template_entity) { + return -1; + } + + ecs_script_template_t *template = flecs_script_template_init(v->base.script); + template->entity = template_entity; + template->node = node; + + if (flecs_script_template_preprocess(v, template)) { + goto error; + } + + if (flecs_script_template_hoist_using(v, template)) { + goto error; + } + + if (flecs_script_template_hoist_vars(v, template, v->vars)) { + goto error; + } + + /* If template has no props, give template dummy size so we can register + * hooks for it. */ + if (!ecs_has(v->world, template_entity, EcsComponent)) { + ecs_set(v->world, template_entity, EcsComponent, {1, 1}); + } + + template->type_info = ecs_get_type_info(v->world, template_entity); + + ecs_add_pair(v->world, template_entity, EcsOnInstantiate, EcsOverride); + + EcsScript *script = ecs_ensure(v->world, template_entity, EcsScript); + if (script->script) { + if (script->template_) { + flecs_script_template_fini( + flecs_script_impl(script->script), script->template_); + } + ecs_script_free(script->script); + } + + script->script = &v->base.script->pub; + script->template_ = template; + ecs_modified(v->world, template_entity, EcsScript); + + ecs_set_hooks_id(v->world, template_entity, &(ecs_type_hooks_t) { + .ctor = flecs_script_template_ctor, + .on_set = flecs_script_template_on_set, + .ctx = v->world + }); + + /* Keep script alive for as long as template is alive */ + v->base.script->refcount ++; + + return 0; +error: + flecs_script_template_fini(v->base.script, template); + return -1; +} + +#endif diff --git a/vendors/flecs/src/addons/script/tokenizer.c b/vendors/flecs/src/addons/script/tokenizer.c new file mode 100644 index 000000000..037ca2013 --- /dev/null +++ b/vendors/flecs/src/addons/script/tokenizer.c @@ -0,0 +1,531 @@ +/** + * @file addons/script/tokenizer.c + * @brief Script tokenizer. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" + +#define Keyword(keyword, _kind)\ + } else if (!ecs_os_strncmp(pos, keyword " ", ecs_os_strlen(keyword) + 1)) {\ + out->value = keyword;\ + out->kind = _kind;\ + return pos + ecs_os_strlen(keyword); + +#define OperatorMultiChar(oper, _kind)\ + } else if (!ecs_os_strncmp(pos, oper, ecs_os_strlen(oper))) {\ + out->value = oper;\ + out->kind = _kind;\ + return pos + ecs_os_strlen(oper); + +#define Operator(oper, _kind)\ + } else if (pos[0] == oper[0]) {\ + out->value = oper;\ + out->kind = _kind;\ + return pos + 1; + +const char* flecs_script_token_kind_str( + ecs_script_token_kind_t kind) +{ + switch(kind) { + case EcsTokUnknown: + return "unknown token "; + case EcsTokColon: + case EcsTokScopeOpen: + case EcsTokScopeClose: + case EcsTokParenOpen: + case EcsTokParenClose: + case EcsTokBracketOpen: + case EcsTokBracketClose: + case EcsTokAnnotation: + case EcsTokComma: + case EcsTokSemiColon: + case EcsTokMul: + case EcsTokAssign: + case EcsTokBitwiseOr: + case EcsTokNot: + case EcsTokOptional: + case EcsTokEq: + case EcsTokNeq: + case EcsTokMatch: + case EcsTokOr: + return ""; + case EcsTokKeywordWith: + case EcsTokKeywordUsing: + case EcsTokKeywordTemplate: + case EcsTokKeywordProp: + case EcsTokKeywordConst: + case EcsTokKeywordIf: + case EcsTokKeywordElse: + case EcsTokKeywordModule: + return "keyword "; + case EcsTokIdentifier: + return "identifier "; + case EcsTokString: + return "string "; + case EcsTokNumber: + return "number "; + case EcsTokNewline: + return "newline"; + case EcsTokEnd: + return "end of script"; + default: + return ""; + } +} + +const char* flecs_scan_whitespace( + ecs_script_parser_t *parser, + const char *pos) +{ + (void)parser; + + if (parser->significant_newline) { + while (pos[0] && isspace(pos[0]) && pos[0] != '\n') { + pos ++; + } + } else { + while (pos[0] && isspace(pos[0])) { + pos ++; + } + } + + return pos; +} + +static +const char* flecs_scan_whitespace_and_comment( + ecs_script_parser_t *parser, + const char *pos) +{ +repeat_skip_whitespace_comment: + pos = flecs_scan_whitespace(parser, pos); + if (pos[0] == '/') { + if (pos[1] == '/') { + for (pos = pos + 2; pos[0] && pos[0] != '\n'; pos ++) { } + if (pos[0] == '\n') { + pos ++; + goto repeat_skip_whitespace_comment; + } + } else if (pos[1] == '*') { + for (pos = &pos[2]; pos[0] != 0; pos ++) { + if (pos[0] == '*' && pos[1] == '/') { + pos += 2; + goto repeat_skip_whitespace_comment; + } + } + + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "missing */ for multiline comment"); + } + } + + return pos; +} + +// Identifier token +static +bool flecs_script_is_identifier( + char c) +{ + return isalpha(c) || (c == '_') || (c == '$') || (c == '#'); +} + +static +const char* flecs_script_identifier( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out) +{ + out->kind = EcsTokIdentifier; + out->value = parser->token_cur; + + ecs_assert(flecs_script_is_identifier(pos[0]), ECS_INTERNAL_ERROR, NULL); + char *outpos = parser->token_cur; + do { + char c = pos[0]; + bool is_ident = flecs_script_is_identifier(c) || + isdigit(c) || (c == '.') || (c == '*'); + + /* Retain \. for name lookup operation */ + if (!is_ident && c == '\\' && pos[1] == '.') { + is_ident = true; + } + + if (!is_ident) { + if (c == '\\') { + pos ++; + } else if (c == '<') { + int32_t indent = 0; + do { + c = *pos; + + if (c == '<') { + indent ++; + } else if (c == '>') { + indent --; + } else if (!c) { + ecs_parser_error(parser->script->pub.name, + parser->script->pub.code, pos - parser->script->pub.code, + "< without > in identifier"); + return NULL; + } + + *outpos = c; + outpos ++; + pos ++; + + if (!indent) { + break; + } + } while (true); + + *outpos = '\0'; + parser->token_cur = outpos + 1; + return pos; + } else if (c == '>') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "> without < in identifier"); + return NULL; + } else { + *outpos = '\0'; + parser->token_cur = outpos + 1; + return pos; + } + } + + *outpos = *pos; + outpos ++; + pos ++; + } while (true); +} + +// Number token static +static +bool flecs_script_is_number( + char c) +{ + return isdigit(c) || (c == '-'); +} + +static +const char* flecs_script_number( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out) +{ + out->kind = EcsTokNumber; + out->value = parser->token_cur; + + ecs_assert(flecs_script_is_number(pos[0]), ECS_INTERNAL_ERROR, NULL); + char *outpos = parser->token_cur; + do { + char c = pos[0]; + if (!isdigit(c)) { + *outpos = '\0'; + parser->token_cur = outpos + 1; + break; + } + + outpos[0] = pos[0]; + outpos ++; + pos ++; + } while (true); + + return pos; +} + +static +const char* flecs_script_skip_string( + ecs_script_parser_t *parser, + const char *pos, + char delim) +{ + char ch; + for (; (ch = pos[0]) && pos[0] != delim; pos ++) { + if (ch == '\\') { + pos ++; + } + } + + if (!pos[0]) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "unterminated string"); + return NULL; + } + + return pos; +} + +static +const char* flecs_script_string( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out) +{ + const char *end = flecs_script_skip_string(parser, pos + 1, '"'); + if (!end) { + return NULL; + } + + ecs_assert(end[0] == '"', ECS_INTERNAL_ERROR, NULL); + end --; + + int32_t len = flecs_ito(int32_t, end - pos); + ecs_os_memcpy(parser->token_cur, pos + 1, len); + parser->token_cur[len] = '\0'; + + out->kind = EcsTokString; + out->value = parser->token_cur; + parser->token_cur += len + 1; + return end + 2; +} + +const char* flecs_script_expr( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out, + char until) +{ + parser->pos = pos; + + int32_t scope_depth = until == '}' ? 1 : 0; + int32_t paren_depth = until == ')' ? 1 : 0; + + const char *start = pos = flecs_scan_whitespace(parser, pos); + char ch; + + for (; (ch = pos[0]); pos ++) { + if (ch == '{') { + if (ch == until) { + break; + } + scope_depth ++; + } else + if (ch == '}') { + scope_depth --; + if (!scope_depth && until == '}') { + break; + } + if (scope_depth < 0) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "mismatching { }"); + return NULL; + } + } else + if (ch == '(') { + paren_depth ++; + } else + if (ch == ')') { + paren_depth --; + if (!paren_depth && until == ')') { + break; + } + if (paren_depth < 0) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "mismatching ( )"); + return NULL; + } + } else + if (ch == '"') { + pos = flecs_script_skip_string(parser, pos + 1, '"'); + if (!pos) { + return NULL; + } + } else + if (ch == '`') { + pos = flecs_script_skip_string(parser, pos + 1, '`'); + if (!pos) { + return NULL; + } + } else + if (ch == until) { + break; + } + } + + if (!pos[0]) { + if (until == '\0') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected end of script"); + return NULL; + } else + if (until == '\n') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected newline"); + return NULL; + } else { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected '%c'", until); + return NULL; + } + } + + if (scope_depth) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "mismatching { }"); + return NULL; + } + if (paren_depth) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "mismatching ( )"); + return NULL; + } + + if (until != ']') { + parser->token_cur[0] = '{'; + } else { + parser->token_cur[0] = '['; + } + + int32_t len = flecs_ito(int32_t, pos - start); + ecs_os_memcpy(parser->token_cur + 1, start, len); + out->value = parser->token_cur; + parser->token_cur += len + 1; + + while (isspace(parser->token_cur[-1])) { + parser->token_cur --; + } + + if (until != ']') { + parser->token_cur[0] = '}'; + } else { + parser->token_cur[0] = ']'; + } + + parser->token_cur ++; + + parser->token_cur[0] = '\0'; + parser->token_cur ++; + + return pos; +} + +const char* flecs_script_until( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out, + char until) +{ + parser->pos = pos; + + const char *start = pos = flecs_scan_whitespace(parser, pos); + char ch; + + for (; (ch = pos[0]); pos ++) { + if (ch == until) { + break; + } + } + + if (!pos[0]) { + if (until == '\0') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected end of script"); + return NULL; + } else + if (until == '\n') { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected newline"); + return NULL; + } else { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "expected '%c'", until); + return NULL; + } + } + + int32_t len = flecs_ito(int32_t, pos - start); + ecs_os_memcpy(parser->token_cur, start, len); + out->value = parser->token_cur; + parser->token_cur += len; + + while (isspace(parser->token_cur[-1])) { + parser->token_cur --; + } + + parser->token_cur[0] = '\0'; + parser->token_cur ++; + + return pos; +} + +const char* flecs_script_token( + ecs_script_parser_t *parser, + const char *pos, + ecs_script_token_t *out, + bool is_lookahead) +{ + parser->pos = pos; + + // Skip whitespace and comments + pos = flecs_scan_whitespace_and_comment(parser, pos); + + out->kind = EcsTokUnknown; + out->value = NULL; + + if (pos[0] == '\0') { + out->kind = EcsTokEnd; + return pos; + } else if (pos[0] == '\n') { + out->kind = EcsTokNewline; + + // Parse multiple newlines/whitespaces as a single token + pos = flecs_scan_whitespace_and_comment(parser, pos + 1); + if (pos[0] == '\n') { + pos ++; + } + return pos; + + Operator (":", EcsTokColon) + Operator ("{", EcsTokScopeOpen) + Operator ("}", EcsTokScopeClose) + Operator ("(", EcsTokParenOpen) + Operator (")", EcsTokParenClose) + Operator ("[", EcsTokBracketOpen) + Operator ("]", EcsTokBracketClose) + Operator ("@", EcsTokAnnotation) + Operator (",", EcsTokComma) + Operator (";", EcsTokSemiColon) + Operator ("*", EcsTokMul) + Operator ("?", EcsTokOptional) + + OperatorMultiChar ("==", EcsTokEq) + OperatorMultiChar ("!=", EcsTokNeq) + OperatorMultiChar ("~=", EcsTokMatch) + OperatorMultiChar ("||", EcsTokOr) + + OperatorMultiChar ("!", EcsTokNot) + OperatorMultiChar ("=", EcsTokAssign) + OperatorMultiChar ("|", EcsTokBitwiseOr) + + Keyword ("with", EcsTokKeywordWith) + Keyword ("using", EcsTokKeywordUsing) + Keyword ("template", EcsTokKeywordTemplate) + Keyword ("prop", EcsTokKeywordProp) + Keyword ("const", EcsTokKeywordConst) + Keyword ("if", EcsTokKeywordIf) + Keyword ("else", EcsTokKeywordElse) + Keyword ("module", EcsTokKeywordModule) + + } else if (pos[0] == '"') { + return flecs_script_string(parser, pos, out); + + } else if (flecs_script_is_number(pos[0])) { + return flecs_script_number(parser, pos, out); + + } else if (flecs_script_is_identifier(pos[0])) { + return flecs_script_identifier(parser, pos, out); + } + + if (!is_lookahead) { + ecs_parser_error(parser->script->pub.name, parser->script->pub.code, + pos - parser->script->pub.code, "unknown token '%c'", pos[0]); + } + + return NULL; +} + +#endif diff --git a/vendors/flecs/src/addons/script/tokenizer.h b/vendors/flecs/src/addons/script/tokenizer.h new file mode 100644 index 000000000..8c21b3943 --- /dev/null +++ b/vendors/flecs/src/addons/script/tokenizer.h @@ -0,0 +1,81 @@ +/** + * @file addons/script/tokenizer.h + * @brief Script tokenizer. + */ + +#ifndef FLECS_SCRIPT_TOKENIZER_H +#define FLECS_SCRIPT_TOKENIZER_H + +/* Tokenizer */ +typedef enum ecs_script_token_kind_t { + EcsTokEnd = '\0', + EcsTokUnknown, + EcsTokScopeOpen = '{', + EcsTokScopeClose = '}', + EcsTokParenOpen = '(', + EcsTokParenClose = ')', + EcsTokBracketOpen = '[', + EcsTokBracketClose = ']', + EcsTokMul = '*', + EcsTokComma = ',', + EcsTokSemiColon = ';', + EcsTokColon = ':', + EcsTokAssign = '=', + EcsTokBitwiseOr = '|', + EcsTokNot = '!', + EcsTokOptional = '?', + EcsTokAnnotation = '@', + EcsTokNewline = '\n', + EcsTokEq, + EcsTokNeq, + EcsTokMatch, + EcsTokOr, + EcsTokIdentifier, + EcsTokString, + EcsTokNumber, + EcsTokKeywordModule, + EcsTokKeywordUsing, + EcsTokKeywordWith, + EcsTokKeywordIf, + EcsTokKeywordElse, + EcsTokKeywordTemplate, + EcsTokKeywordProp, + EcsTokKeywordConst, +} ecs_script_token_kind_t; + +typedef struct ecs_script_token_t { + const char *value; + ecs_script_token_kind_t kind; +} ecs_script_token_t; + +typedef struct ecs_script_tokens_t { + int32_t count; + ecs_script_token_t tokens[256]; +} ecs_script_tokens_t; + +const char* flecs_script_expr( + ecs_script_parser_t *parser, + const char *ptr, + ecs_script_token_t *out, + char until); + +const char* flecs_script_until( + ecs_script_parser_t *parser, + const char *ptr, + ecs_script_token_t *out, + char until); + +const char* flecs_script_token_kind_str( + ecs_script_token_kind_t kind); + +const char* flecs_script_token( + ecs_script_parser_t *parser, + const char *ptr, + ecs_script_token_t *out, + bool is_lookahead); + +const char* flecs_scan_whitespace( + ecs_script_parser_t *parser, + const char *pos); + +#endif diff --git a/vendors/flecs/src/addons/script/vars.c b/vendors/flecs/src/addons/script/vars.c new file mode 100644 index 000000000..5c4491a48 --- /dev/null +++ b/vendors/flecs/src/addons/script/vars.c @@ -0,0 +1,285 @@ +/** + * @file addons/script/vars.c + * @brief Script variables. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" + +ecs_script_vars_t* flecs_script_vars_push( + ecs_script_vars_t *parent, + ecs_stack_t *stack, + ecs_allocator_t *allocator) +{ + ecs_check(stack || parent, ECS_INVALID_PARAMETER, + "must provide either parent scope or stack allocator"); + ecs_check(allocator || parent, ECS_INVALID_PARAMETER, + "must provide either parent scope or allocator"); + + if (!stack) { + stack = parent->stack; + } else if (parent) { + ecs_check(stack == parent->stack, ECS_INVALID_PARAMETER, + "provided stack allocator is different from parent scope"); + } + if (!allocator) { + allocator = parent->allocator; + } else if (parent) { + ecs_check(allocator == parent->allocator, ECS_INVALID_PARAMETER, + "provided allocator is different from parent scope"); + } + + ecs_stack_cursor_t *cursor = flecs_stack_get_cursor(stack); + ecs_script_vars_t *result = flecs_stack_calloc_t(stack, ecs_script_vars_t); + ecs_vec_init_t(allocator, &result->vars, ecs_script_var_t, 0); + result->parent = parent; + if (parent) { + result->world = parent->world; + } + result->stack = stack; + result->allocator = allocator; + result->cursor = cursor; + return result; +error: + return NULL; +} + +ecs_script_vars_t* ecs_script_vars_init( + ecs_world_t *world) +{ + ecs_script_vars_t *result = flecs_script_vars_push(NULL, + flecs_stage_get_stack_allocator(world), + flecs_stage_get_allocator(world)); + result->world = ecs_get_world(world); /* Provided world can be stage */ + return result; +} + +void ecs_script_vars_fini( + ecs_script_vars_t *vars) +{ + ecs_check(vars->parent == NULL, ECS_INVALID_PARAMETER, + "ecs_script_vars_fini can only be called on the roots cope"); + ecs_script_vars_pop(vars); +error: + return; +} + +ecs_script_vars_t* ecs_script_vars_push( + ecs_script_vars_t *parent) +{ + ecs_check(parent != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stack_t *stack = parent->stack; + ecs_allocator_t *allocator = parent->allocator; + ecs_check(stack != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(allocator != NULL, ECS_INVALID_PARAMETER, NULL); + return flecs_script_vars_push(parent, stack, allocator); +error: + return NULL; +} + +ecs_script_vars_t* ecs_script_vars_pop( + ecs_script_vars_t *vars) +{ + ecs_script_vars_t *parent = vars->parent; + ecs_stack_cursor_t *cursor = vars->cursor; + int32_t i, count = ecs_vec_count(&vars->vars); + if (count) { + ecs_script_var_t *var_array = ecs_vec_first(&vars->vars); + for (i = 0; i < count; i ++) { + ecs_script_var_t *var = &var_array[i]; + if (!var->value.ptr) { + continue; + } + + if (!var->type_info || !var->type_info->hooks.dtor) { + continue; + } + + var->type_info->hooks.dtor(var->value.ptr, 1, var->type_info); + } + + flecs_name_index_fini(&vars->var_index); + } + + ecs_vec_fini_t(vars->allocator, &vars->vars, ecs_script_var_t); + flecs_stack_restore_cursor(vars->stack, cursor); + return parent; +} + +ecs_script_var_t* ecs_script_vars_declare( + ecs_script_vars_t *vars, + const char *name) +{ + if (!ecs_vec_count(&vars->vars)) { + flecs_name_index_init(&vars->var_index, vars->allocator); + } else { + if (flecs_name_index_find(&vars->var_index, name, 0, 0) != 0) { + goto error; + } + } + + ecs_script_var_t *var = ecs_vec_append_t( + vars->allocator, &vars->vars, ecs_script_var_t); + var->name = name; + var->value.ptr = NULL; + var->value.type = 0; + var->type_info = NULL; + + flecs_name_index_ensure(&vars->var_index, + flecs_ito(uint64_t, ecs_vec_count(&vars->vars)), name, 0, 0); + + return var; +error: + return NULL; +} + +ecs_script_var_t* ecs_script_vars_define_id( + ecs_script_vars_t *vars, + const char *name, + ecs_entity_t type) +{ + ecs_check(vars->world != NULL, ECS_INVALID_OPERATION, "variable scope is " + "not associated with world, create scope with ecs_script_vars_init"); + + const ecs_type_info_t *ti = ecs_get_type_info(vars->world, type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, + "the entity provided for the type parameter is not a type"); + + ecs_script_var_t *result = ecs_script_vars_declare(vars, name); + if (!result) { + return NULL; + } + + result->value.type = type; + result->value.ptr = flecs_stack_alloc(vars->stack, ti->size, ti->alignment); + result->type_info = ti; + + if (ti->hooks.ctor) { + ti->hooks.ctor(result->value.ptr, 1, ti); + } + + return result; +error: + return NULL; +} + +ecs_script_var_t* ecs_script_vars_lookup( + const ecs_script_vars_t *vars, + const char *name) +{ + uint64_t var_id = 0; + if (ecs_vec_count(&vars->vars)) { + var_id = flecs_name_index_find(&vars->var_index, name, 0, 0); + } + + if (!var_id) { + if (vars->parent) { + return ecs_script_vars_lookup(vars->parent, name); + } + return NULL; + } + + return ecs_vec_get_t(&vars->vars, ecs_script_var_t, + flecs_uto(int32_t, var_id - 1)); +} + +/* Static names for iterator fields */ +static const char* flecs_script_iter_field_names[] = { + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15" +}; + +void ecs_script_vars_from_iter( + const ecs_iter_t *it, + ecs_script_vars_t *vars, + int offset) +{ + ecs_check(vars != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(!offset || offset < it->count, ECS_INVALID_PARAMETER, NULL); + + /* Set variable for $this */ + if (it->count) { + ecs_script_var_t *var = ecs_script_vars_lookup(vars, "this"); + if (!var) { + var = ecs_script_vars_declare(vars, "this"); + var->value.type = ecs_id(ecs_entity_t); + } + + /* Safe, variable value will never be written */ + var->value.ptr = ECS_CONST_CAST(ecs_entity_t*, &it->entities[offset]); + } + + /* Set variables for fields */ + { + int8_t i, field_count = it->field_count; + for (i = 0; i < field_count; i ++) { + ecs_size_t size = it->sizes[i]; + if (!size) { + continue; + } + + void *ptr = ecs_field_w_size(it, flecs_itosize(size), i); + if (!ptr) { + continue; + } + + ptr = ECS_OFFSET(ptr, offset * size); + + const char *name = flecs_script_iter_field_names[i]; + ecs_script_var_t *var = ecs_script_vars_lookup(vars, name); + if (!var) { + var = ecs_script_vars_declare(vars, name); + ecs_assert(ecs_script_vars_lookup(vars, name) != NULL, + ECS_INTERNAL_ERROR, NULL); + var->value.type = it->ids[i]; + } else { + ecs_check(var->value.type == it->ids[i], + ECS_INVALID_PARAMETER, NULL); + } + var->value.ptr = ptr; + } + } + + /* Set variables for query variables */ + { + int32_t i, var_count = it->variable_count; + for (i = 1 /* skip this variable */ ; i < var_count; i ++) { + const ecs_entity_t *e_ptr = NULL; + ecs_var_t *query_var = &it->variables[i]; + if (query_var->entity) { + e_ptr = &query_var->entity; + } else { + ecs_table_range_t *range = &query_var->range; + if (range->count == 1) { + const ecs_entity_t *entities = + ecs_table_entities(range->table); + e_ptr = &entities[range->offset]; + } + } + if (!e_ptr) { + continue; + } + + ecs_script_var_t *var = ecs_script_vars_lookup( + vars, it->variable_names[i]); + if (!var) { + var = ecs_script_vars_declare(vars, it->variable_names[i]); + var->value.type = ecs_id(ecs_entity_t); + } else { + ecs_check(var->value.type == ecs_id(ecs_entity_t), + ECS_INVALID_PARAMETER, NULL); + } + + /* Safe, variable value will never be written */ + var->value.ptr = ECS_CONST_CAST(ecs_entity_t*, e_ptr); + } + } + +error: + return; +} + +#endif diff --git a/vendors/flecs/src/addons/script/visit.c b/vendors/flecs/src/addons/script/visit.c new file mode 100644 index 000000000..2d2a3c0da --- /dev/null +++ b/vendors/flecs/src/addons/script/visit.c @@ -0,0 +1,139 @@ +/** + * @file addons/script/visit.c + * @brief Script AST visitor utilities. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" + +ecs_script_node_t* ecs_script_parent_node_( + ecs_script_visit_t *v) +{ + if (v->depth > 1) { + return v->nodes[v->depth - 2]; /* Last node is current node */ + } else { + return NULL; + } +} + +ecs_script_scope_t* ecs_script_current_scope_( + ecs_script_visit_t *v) +{ + int32_t depth; + for(depth = v->depth - 1; depth >= 0; depth --) { + ecs_script_node_t *node = v->nodes[depth]; + if (node->kind == EcsAstScope) { + return (ecs_script_scope_t*)node; + } + } + + return NULL; +} + +ecs_script_node_t* ecs_script_parent_( + ecs_script_visit_t *v, + ecs_script_node_t *child) +{ + int32_t depth; + for(depth = v->depth - 1; depth >= 0; depth --) { + ecs_script_node_t *node = v->nodes[depth]; + if (node == child && depth) { + return v->nodes[depth - 1]; + } + } + + return NULL; +} + +int32_t ecs_script_node_line_number_( + ecs_script_impl_t *script, + ecs_script_node_t *node) +{ + const char *ptr; + int32_t line_count = 1; + for (ptr = script->pub.code; ptr < node->pos; ptr ++) { + ecs_assert(ptr[0] != 0, ECS_INTERNAL_ERROR, NULL); + if (ptr[0] == '\n') { + line_count ++; + } + } + + return line_count; +} + +int ecs_script_visit_scope_( + ecs_script_visit_t *v, + ecs_script_scope_t *scope) +{ + ecs_script_node_t **nodes = ecs_vec_first_t( + &scope->stmts, ecs_script_node_t*); + + v->nodes[v->depth ++] = (ecs_script_node_t*)scope; + + int32_t i, count = ecs_vec_count(&scope->stmts); + for (i = 0; i < count; i ++) { + if (!i) { + v->prev = NULL; + } else { + v->prev = nodes[i - 1]; + } + + if (i != (count - 1)) { + v->next = nodes[i + 1]; + } else { + v->next = NULL; + } + + v->nodes[v->depth ++] = nodes[i]; + + if (v->visit(v, nodes[i])) { + return -1; + } + + v->depth --; + } + + v->depth --; + + return 0; +} + +int ecs_script_visit_node_( + ecs_script_visit_t *v, + ecs_script_node_t *node) +{ + v->nodes[v->depth ++] = node; + + if (v->visit(v, node)) { + return -1; + } + + v->depth --; + + return 0; +} + +int ecs_script_visit_( + ecs_script_visit_t *visitor, + ecs_visit_action_t visit, + ecs_script_impl_t *script) +{ + visitor->script = script; + visitor->visit = visit; + visitor->depth = 0; + int result = ecs_script_visit_node(visitor, script->root); + if (result) { + return -1; + } + + if (visitor->depth) { + ecs_parser_error(script->pub.name, NULL, 0, "unexpected end of script"); + return -1; + } + + return 0; +} + +#endif diff --git a/vendors/flecs/src/addons/script/visit.h b/vendors/flecs/src/addons/script/visit.h new file mode 100644 index 000000000..275fedd8f --- /dev/null +++ b/vendors/flecs/src/addons/script/visit.h @@ -0,0 +1,80 @@ +/** + * @file addons/script/visit.h + * @brief Script AST visitor utilities. + */ + +#ifndef FLECS_SCRIPT_VISIT_H +#define FLECS_SCRIPT_VISIT_H + +typedef struct ecs_script_visit_t ecs_script_visit_t; + +typedef int (*ecs_visit_action_t)( + ecs_script_visit_t *visitor, + ecs_script_node_t *node); + +struct ecs_script_visit_t { + ecs_script_impl_t *script; + ecs_visit_action_t visit; + ecs_script_node_t* nodes[256]; + ecs_script_node_t *prev, *next; + int32_t depth; +}; + +int ecs_script_visit_( + ecs_script_visit_t *visitor, + ecs_visit_action_t visit, + ecs_script_impl_t *script); + +#define ecs_script_visit(script, visitor, visit) \ + ecs_script_visit_((ecs_script_visit_t*)visitor,\ + (ecs_visit_action_t)visit,\ + script) + +int ecs_script_visit_node_( + ecs_script_visit_t *v, + ecs_script_node_t *node); + +#define ecs_script_visit_node(visitor, node) \ + ecs_script_visit_node_((ecs_script_visit_t*)visitor, \ + (ecs_script_node_t*)node) + +int ecs_script_visit_scope_( + ecs_script_visit_t *v, + ecs_script_scope_t *node); + +#define ecs_script_visit_scope(visitor, node) \ + ecs_script_visit_scope_((ecs_script_visit_t*)visitor, node) + +ecs_script_node_t* ecs_script_parent_node_( + ecs_script_visit_t *v); + +#define ecs_script_parent_node(visitor) \ + ecs_script_parent_node_((ecs_script_visit_t*)visitor) + +ecs_script_scope_t* ecs_script_current_scope_( + ecs_script_visit_t *v); + +#define ecs_script_current_scope(visitor) \ + ecs_script_current_scope_((ecs_script_visit_t*)visitor) + +ecs_script_node_t* ecs_script_parent_( + ecs_script_visit_t *v, + ecs_script_node_t *node); + +#define ecs_script_parent(visitor, node) \ + ecs_script_parent_((ecs_script_visit_t*)visitor, (ecs_script_node_t*)node) + +ecs_script_node_t* ecs_script_next_node_( + ecs_script_visit_t *v); + +#define ecs_script_next_node(visitor) \ + ecs_script_next_node_((ecs_script_visit_t*)visitor) + +int32_t ecs_script_node_line_number_( + ecs_script_impl_t *script, + ecs_script_node_t *node); + +#define ecs_script_node_line_number(script, node) \ + ecs_script_node_line_number_(script, (ecs_script_node_t*)node) + +#endif diff --git a/vendors/flecs/src/addons/script/visit_eval.c b/vendors/flecs/src/addons/script/visit_eval.c new file mode 100644 index 000000000..c8bcd894f --- /dev/null +++ b/vendors/flecs/src/addons/script/visit_eval.c @@ -0,0 +1,1251 @@ +/** + * @file addons/script/visit_eval.c + * @brief Script evaluation visitor. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" + +void flecs_script_eval_error_( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + char *msg = flecs_vasprintf(fmt, args); + va_end(args); + + if (node) { + int32_t line = ecs_script_node_line_number(v->base.script, node); + ecs_parser_error(v->base.script->pub.name, NULL, 0, "%d: %s", line, msg); + } else { + ecs_parser_error(v->base.script->pub.name, NULL, 0, "%s", msg); + } + + ecs_os_free(msg); +} + +static +ecs_value_t* flecs_script_with_append( + ecs_allocator_t *a, + ecs_script_eval_visitor_t *v, + const ecs_type_info_t *ti) +{ + if (ecs_vec_count(&v->with)) { + ecs_assert(ecs_vec_last_t(&v->with, ecs_value_t)->type == 0, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_vec_last_t(&v->with, ecs_value_t)->ptr == NULL, + ECS_INTERNAL_ERROR, NULL); + ecs_vec_remove_last(&v->with); + } + + ecs_vec_append_t(a, &v->with_type_info, const ecs_type_info_t*)[0] = ti; + + ecs_vec_append_t(a, &v->with, ecs_value_t); + ecs_value_t *last = ecs_vec_append_t(a, &v->with, ecs_value_t); + ecs_os_memset_t(last, 0, ecs_value_t); + return ecs_vec_get_t(&v->with, ecs_value_t, ecs_vec_count(&v->with) - 2); +} + +static +void flecs_script_with_set_count( + ecs_allocator_t *a, + ecs_script_eval_visitor_t *v, + int32_t count) +{ + int32_t i = count, until = ecs_vec_count(&v->with) - 1; + for (; i < until; i ++) { + ecs_value_t *val = ecs_vec_get_t(&v->with, ecs_value_t, i); + ecs_type_info_t *ti = ecs_vec_get_t( + &v->with_type_info, ecs_type_info_t*, i)[0]; + if (ti && ti->hooks.dtor) { + ti->hooks.dtor(val->ptr, 1, ti); + } + } + + if (count) { + ecs_value_t *last = ecs_vec_get_t(&v->with, ecs_value_t, count); + ecs_os_memset_t(last, 0, ecs_value_t); + ecs_vec_set_count_t(a, &v->with, ecs_value_t, count + 1); + } else { + ecs_vec_set_count_t(a, &v->with, ecs_value_t, 0); + } + + ecs_vec_set_count_t(a, &v->with_type_info, ecs_type_info_t*, count); +} + +static +ecs_value_t* flecs_script_with_last( + ecs_script_eval_visitor_t *v) +{ + int32_t count = ecs_vec_count(&v->with); + if (count) { + return ecs_vec_get_t(&v->with, ecs_value_t, count - 2); + } + return NULL; +} + +static +int32_t flecs_script_with_count( + ecs_script_eval_visitor_t *v) +{ + if (ecs_vec_count(&v->with)) { + ecs_assert(ecs_vec_last_t(&v->with, ecs_value_t)->type == 0, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_vec_last_t(&v->with, ecs_value_t)->ptr == NULL, + ECS_INTERNAL_ERROR, NULL); + return ecs_vec_count(&v->with) - 1; + } + return 0; +} + +const ecs_type_info_t* flecs_script_get_type_info( + ecs_script_eval_visitor_t *v, + void *node, + ecs_id_t id) +{ + ecs_id_record_t *idr = flecs_id_record_ensure(v->world, id); + if (!idr) { + goto error; + } + + if (!idr->type_info) { + goto error; + } + + return idr->type_info; +error: + { + char *idstr = ecs_id_str(v->world, id); + flecs_script_eval_error(v, node, + "cannot set value of '%s': not a component", idstr); + ecs_os_free(idstr); + } + return NULL; +} + +ecs_entity_t flecs_script_find_entity( + ecs_script_eval_visitor_t *v, + ecs_entity_t from, + const char *path) +{ + if (!path) { + return 0; + } + + if (path[0] == '$') { + const ecs_script_var_t *var = ecs_script_vars_lookup(v->vars, &path[1]); + if (!var) { + return 0; + } + + if (var->value.type != ecs_id(ecs_entity_t)) { + char *type_str = ecs_id_str(v->world, var->value.type); + flecs_script_eval_error(v, NULL, + "variable '%s' must be of type entity, got '%s'", + path, type_str); + ecs_os_free(type_str); + return 0; + } + + if (var->value.ptr == NULL) { + flecs_script_eval_error(v, NULL, + "variable '%s' is not initialized", path); + return 0; + } + + ecs_entity_t result = *(ecs_entity_t*)var->value.ptr; + if (!result) { + flecs_script_eval_error(v, NULL, + "variable '%s' contains invalid entity id (0)", path); + return 0; + } + + return result; + } + + if (from) { + return ecs_lookup_path_w_sep(v->world, from, path, NULL, NULL, false); + } else { + int32_t i, using_count = ecs_vec_count(&v->using); + if (using_count) { + ecs_entity_t *using = ecs_vec_first(&v->using); + for (i = using_count - 1; i >= 0; i --) { + ecs_entity_t e = ecs_lookup_path_w_sep( + v->world, using[i], path, NULL, NULL, false); + if (e) { + return e; + } + } + } + + return ecs_lookup_path_w_sep( + v->world, v->parent, path, NULL, NULL, true); + } +} + +ecs_entity_t flecs_script_create_entity( + ecs_script_eval_visitor_t *v, + const char *name) +{ + ecs_value_t *with = NULL; + if (flecs_script_with_count(v)) { + with = ecs_vec_first_t(&v->with, ecs_value_t); + } + + ecs_entity_desc_t desc = {0}; + desc.name = name; + desc.parent = v->parent; + desc.set = with; + return ecs_entity_init(v->world, &desc); +} + +static +ecs_entity_t flecs_script_find_entity_action( + const ecs_world_t *world, + const char *path, + void *ctx) +{ + (void)world; + ecs_script_eval_visitor_t *v = ctx; + return flecs_script_find_entity(v, 0, path); +} + +static +int flecs_script_find_template_entity( + ecs_script_eval_visitor_t *v, + const char *name) +{ + /* Loop template scope to see if it declares an entity with requested name */ + ecs_script_template_t *t = v->template; + ecs_script_scope_t *scope = t->node->scope; + ecs_script_node_t **nodes = ecs_vec_first_t( + &scope->stmts, ecs_script_node_t*); + + int32_t i, count = ecs_vec_count(&scope->stmts); + for (i = 0; i < count; i ++) { + ecs_script_node_t *node = nodes[i]; + if (node->kind == EcsAstEntity) { + ecs_script_entity_t *entity_node = (ecs_script_entity_t*)node; + if (!ecs_os_strcmp(entity_node->name, name)) { + return 0; + } + } + } + + return -1; +} + +static +int flecs_script_eval_id( + ecs_script_eval_visitor_t *v, + void *node, + ecs_script_id_t *id) +{ + ecs_entity_t second_from = 0; + + if (!id->first) { + flecs_script_eval_error(v, node, + "invalid component/tag identifier"); + return -1; + } + + if (v->template) { + /* Can't resolve variables while preprocessing template scope */ + if (id->first[0] == '$') { + return 0; + } + if (id->second && id->second[0] == '$') { + return 0; + } + } + + ecs_entity_t first = flecs_script_find_entity(v, 0, id->first); + if (!first) { + if (id->first[0] == '$') { + flecs_script_eval_error(v, node, + "unresolved variable '%s'", id->first); + return -1; + } + + flecs_script_eval_error(v, node, + "unresolved identifier '%s'", id->first); + return -1; + } else if (id->second) { + second_from = flecs_get_oneof(v->world, first); + } + + if (id->second) { + ecs_entity_t second = flecs_script_find_entity( + v, second_from, id->second); + if (!second) { + if (id->second[0] == '$') { + flecs_script_eval_error(v, node, + "unresolved variable '%s'", id->second); + return -1; + } + + /* Targets may be defined by the template */ + if (v->template) { + if (!flecs_script_find_template_entity(v, id->second)) { + return 0; + } else { + return -1; + } + } + + if (second_from) { + char *parent_str = ecs_id_str(v->world, second_from); + flecs_script_eval_error(v, node, "target '%s' not found in " + "parent '%s'", id->second, parent_str); + ecs_os_free(parent_str); + return -1; + } + + flecs_script_eval_error(v, node, + "unresolved identifier '%s'", id->second); + return -1; + } + + if (first == EcsAny || second == EcsAny) { + flecs_script_eval_error(v, node, + "cannot use anonymous entity as element of pair"); + return -1; + } + + id->eval = id->flag | ecs_pair(first, second); + } else { + if (first == EcsAny) { + flecs_script_eval_error(v, node, + "cannot use anonymous entity as component or tag"); + return -1; + } + + id->eval = id->flag | first; + } + + return 0; +} + +int flecs_script_eval_expr( + ecs_script_eval_visitor_t *v, + const char *expr, + ecs_value_t *value) +{ + if (!value->type && expr[0] == '{') { + expr ++; + } + + ecs_script_expr_run_desc_t desc = { + .name = v->base.script->pub.name, + .expr = expr, + .lookup_action = flecs_script_find_entity_action, + .lookup_ctx = v, + .vars = v->vars + }; + + if (!ecs_script_expr_run(v->world, expr, value, &desc)) { + return -1; + } + + return 0; +} + +static +int flecs_script_eval_scope( + ecs_script_eval_visitor_t *v, + ecs_script_scope_t *node) +{ + ecs_script_node_t *scope_parent = ecs_script_parent_node(v); + ecs_entity_t prev_eval_parent = v->parent; + int32_t prev_using_count = ecs_vec_count(&v->using); + + for (int i = v->base.depth - 2; i >= 0; i --) { + if (v->base.nodes[i]->kind == EcsAstScope) { + node->parent = (ecs_script_scope_t*)v->base.nodes[i]; + break; + } + } + + ecs_allocator_t *a = v->allocator; + v->vars = flecs_script_vars_push(v->vars, &v->stack, a); + + if (scope_parent && (scope_parent->kind == EcsAstEntity)) { + if (!v->template) { + v->parent = ecs_script_node(entity, scope_parent)->eval; + } + } + + int result = ecs_script_visit_scope(v, node); + + ecs_vec_set_count_t(a, &v->using, ecs_entity_t, prev_using_count); + v->vars = ecs_script_vars_pop(v->vars); + v->parent = prev_eval_parent; + + return result; +} + +static +int flecs_script_apply_annot( + ecs_script_eval_visitor_t *v, + ecs_entity_t entity, + ecs_script_annot_t *node) +{ + if (!ecs_os_strcmp(node->name, "name")) { + ecs_doc_set_name(v->world, entity, node->expr); + } else + if (!ecs_os_strcmp(node->name, "brief")) { + ecs_doc_set_brief(v->world, entity, node->expr); + } else + if (!ecs_os_strcmp(node->name, "detail")) { + ecs_doc_set_detail(v->world, entity, node->expr); + } else + if (!ecs_os_strcmp(node->name, "link")) { + ecs_doc_set_link(v->world, entity, node->expr); + } else + if (!ecs_os_strcmp(node->name, "color")) { + ecs_doc_set_color(v->world, entity, node->expr); + } else { + flecs_script_eval_error(v, node, "unknown annotation '%s'", + node->name); + return -1; + } + + return 0; +} + +static +int flecs_script_eval_entity( + ecs_script_eval_visitor_t *v, + ecs_script_entity_t *node) +{ + bool is_slot = false; + if (node->kind) { + ecs_script_id_t id = { + .first = node->kind + }; + + if (!ecs_os_strcmp(node->kind, "prefab")) { + id.eval = EcsPrefab; + } else if (!ecs_os_strcmp(node->kind, "slot")) { + is_slot = true; + } else if (flecs_script_eval_id(v, node, &id)) { + return -1; + } + + node->eval_kind = id.eval; + } else { + /* Inherit kind from parent kind's DefaultChildComponent, if it existst */ + ecs_script_scope_t *scope = ecs_script_current_scope(v); + if (scope && scope->default_component_eval) { + node->eval_kind = scope->default_component_eval; + } + } + + if (v->template) { + if (ecs_script_visit_node(v, node->scope)) { + return -1; + } + return 0; + } + + node->eval = flecs_script_create_entity(v, node->name); + node->parent = v->entity; + + if (is_slot) { + ecs_entity_t parent = ecs_get_target( + v->world, node->eval, EcsChildOf, 0); + if (!parent) { + flecs_script_eval_error(v, node, + "slot entity must have a parent"); + return -1; + } + + ecs_add_pair(v->world, node->eval, EcsSlotOf, parent); + } + + const EcsDefaultChildComponent *default_comp = NULL; + ecs_script_entity_t *old_entity = v->entity; + v->entity = node; + + if (node->eval_kind) { + ecs_add_id(v->world, node->eval, node->eval_kind); + + default_comp = + ecs_get(v->world, node->eval_kind, EcsDefaultChildComponent); + if (default_comp) { + if (!default_comp->component) { + flecs_script_eval_error(v, node, "entity '%s' has kind '%s' " + "with uninitialized DefaultChildComponent", + node->name, node->kind); + return -1; + } + + node->scope->default_component_eval = default_comp->component; + } + } + + int32_t i, count = ecs_vec_count(&v->annot); + if (count) { + ecs_script_annot_t **annots = ecs_vec_first(&v->annot); + for (i = 0; i < count ; i ++) { + flecs_script_apply_annot(v, node->eval, annots[i]); + } + ecs_vec_clear(&v->annot); + } + + if (ecs_script_visit_node(v, node->scope)) { + return -1; + } + + if (node->eval_kind) { + if (!node->kind_w_expr) { + if (ecs_get_type_info(v->world, node->eval_kind) != NULL) { + ecs_modified_id(v->world, node->eval, node->eval_kind); + } + } + } + + v->entity = old_entity; + + return 0; +} + +static +ecs_entity_t flecs_script_get_src( + ecs_script_eval_visitor_t *v, + ecs_entity_t entity, + ecs_id_t id) +{ + if (entity == EcsVariable) { // Singleton ($) + if (ECS_IS_PAIR(id)) { + return ecs_pair_first(v->world, id); + } else { + return id & ECS_COMPONENT_MASK; + } + } + return entity; +} + +static +int flecs_script_eval_tag( + ecs_script_eval_visitor_t *v, + ecs_script_tag_t *node) +{ + if (flecs_script_eval_id(v, node, &node->id)) { + return -1; + } + + if (v->template) { + return 0; + } + + if (!v->entity) { + if (node->id.second) { + flecs_script_eval_error( + v, node, "missing entity for pair (%s, %s)", + node->id.first, node->id.second); + } else { + flecs_script_eval_error(v, node, "missing entity for tag %s", + node->id.first); + } + return -1; + } + + if (v->template) { + return 0; + } + + ecs_entity_t src = flecs_script_get_src( + v, v->entity->eval, node->id.eval); + ecs_add_id(v->world, src, node->id.eval); + + return 0; +} + +static +int flecs_script_eval_component( + ecs_script_eval_visitor_t *v, + ecs_script_component_t *node) +{ + if (flecs_script_eval_id(v, node, &node->id)) { + return -1; + } + + if (v->template) { + return 0; + } + + if (!v->entity) { + if (node->id.second) { + flecs_script_eval_error(v, node, "missing entity for pair (%s, %s)", + node->id.first, node->id.second); + } else { + flecs_script_eval_error(v, node, "missing entity for component %s", + node->id.first); + } + return -1; + } + + if (v->template) { + return 0; + } + + ecs_entity_t src = flecs_script_get_src(v, v->entity->eval, node->id.eval); + + if (node->expr && node->expr[0]) { + const ecs_type_info_t *ti = flecs_script_get_type_info( + v, node, node->id.eval); + if (!ti) { + return -1; + } + + const EcsType *type = ecs_get(v->world, ti->component, EcsType); + if (type) { + bool is_collection = false; + + switch(type->kind) { + case EcsPrimitiveType: + case EcsBitmaskType: + case EcsEnumType: + case EcsStructType: + case EcsOpaqueType: + break; + case EcsArrayType: + case EcsVectorType: + is_collection = true; + break; + } + + if (node->is_collection != is_collection) { + char *id_str = ecs_id_str(v->world, ti->component); + if (node->is_collection && !is_collection) { + flecs_script_eval_error(v, node, + "type %s is not a collection (use '%s: {...}')", + id_str, id_str); + } else { + flecs_script_eval_error(v, node, + "type %s is a collection (use '%s: [...]')", + id_str, id_str); + } + ecs_os_free(id_str); + return -1; + } + } + + ecs_value_t value = { + .ptr = ecs_ensure_id(v->world, src, node->id.eval), + .type = ti->component + }; + + /* Assign entire value, including members not set by expression. This + * prevents uninitialized or unexpected values. */ + if (!ti->hooks.ctor) { + ecs_os_memset(value.ptr, 0, ti->size); + } else if (ti->hooks.ctor) { + if (ti->hooks.dtor) { + ti->hooks.dtor(value.ptr, 1, ti); + } + ti->hooks.ctor(value.ptr, 1, ti); + } + + if (ecs_os_strcmp(node->expr, "{}")) { + if (flecs_script_eval_expr(v, node->expr, &value)) { + return -1; + } + } + + ecs_modified_id(v->world, src, node->id.eval); + } else { + ecs_add_id(v->world, src, node->id.eval); + } + + return 0; +} + +static +int flecs_script_eval_var_component( + ecs_script_eval_visitor_t *v, + ecs_script_var_component_t *node) +{ + ecs_script_var_t *var = ecs_script_vars_lookup(v->vars, node->name); + if (!var) { + flecs_script_eval_error(v, node, + "unresolved variable '%s'", node->name); + return -1; + } + + if (v->template) { + return 0; + } + + ecs_id_t var_id = var->value.type; + + if (var->value.ptr) { + const ecs_type_info_t *ti = flecs_script_get_type_info( + v, node, var_id); + if (!ti) { + return -1; + } + + ecs_value_t value = { + .ptr = ecs_ensure_id(v->world, v->entity->eval, var_id), + .type = var_id + }; + + ecs_value_copy_w_type_info(v->world, ti, value.ptr, var->value.ptr); + + ecs_modified_id(v->world, v->entity->eval, var_id); + } else { + ecs_add_id(v->world, v->entity->eval, var_id); + } + + return 0; +} + +static +int flecs_script_eval_default_component( + ecs_script_eval_visitor_t *v, + ecs_script_default_component_t *node) +{ + if (!v->entity) { + flecs_script_eval_error(v, node, + "missing entity for default component"); + return -1; + } + + if (v->template) { + return 0; + } + + ecs_script_scope_t *scope = ecs_script_current_scope(v); + ecs_assert(scope != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(scope->node.kind == EcsAstScope, ECS_INTERNAL_ERROR, NULL); + scope = scope->parent; + + if (!scope) { + flecs_script_eval_error(v, node, + "entity '%s' is in root scope which cannot have a default type", + v->entity->name); + return -1; + } + + ecs_id_t default_type = scope->default_component_eval; + if (!default_type) { + flecs_script_eval_error(v, node, + "scope for entity '%s' does not have a default type", + v->entity->name); + return -1; + } + + if (ecs_get_type_info(v->world, default_type) == NULL) { + char *id_str = ecs_id_str(v->world, default_type); + flecs_script_eval_error(v, node, + "cannot use tag '%s' as default type in assignment", + id_str); + ecs_os_free(id_str); + return -1; + } + + ecs_value_t value = { + .ptr = ecs_ensure_id(v->world, v->entity->eval, default_type), + .type = default_type + }; + + if (flecs_script_eval_expr(v, node->expr, &value)) { + return -1; + } + + ecs_modified_id(v->world, v->entity->eval, default_type); + + return 0; +} + +static +int flecs_script_eval_with_var( + ecs_script_eval_visitor_t *v, + ecs_script_var_node_t *node) +{ + ecs_script_var_t *var = ecs_script_vars_lookup(v->vars, node->name); + if (!var) { + flecs_script_eval_error(v, node, + "unresolved variable '%s'", node->name); + return -1; + } + + if (v->template) { + return 0; + } + + ecs_allocator_t *a = v->allocator; + ecs_value_t *value = flecs_script_with_append(a, v, NULL); // TODO: vars of non trivial types + *value = var->value; + + return 0; +} + +static +int flecs_script_eval_with_tag( + ecs_script_eval_visitor_t *v, + ecs_script_tag_t *node) +{ + if (flecs_script_eval_id(v, node, &node->id)) { + return -1; + } + + if (v->template) { + return 0; + } + + ecs_allocator_t *a = v->allocator; + ecs_value_t *value = flecs_script_with_append(a, v, NULL); + value->type = node->id.eval; + value->ptr = NULL; + + return 0; +} + +static +int flecs_script_eval_with_component( + ecs_script_eval_visitor_t *v, + ecs_script_component_t *node) +{ + if (flecs_script_eval_id(v, node, &node->id)) { + return -1; + } + + if (v->template) { + return 0; + } + + ecs_allocator_t *a = v->allocator; + const ecs_type_info_t *ti = flecs_script_get_type_info( + v, node, node->id.eval); + + ecs_value_t *value = flecs_script_with_append(a, v, ti); + value->type = node->id.eval; + value->ptr = NULL; + + if (node->expr && node->expr[0]) { + if (!ti) { + return -1; + } + + value->ptr = flecs_stack_alloc(&v->stack, ti->size, ti->alignment); + value->type = ti->component; // Expression parser needs actual type + + if (ti->hooks.ctor) { + ti->hooks.ctor(value->ptr, 1, ti); + } + + if (flecs_script_eval_expr(v, node->expr, value)) { + return -1; + } + + value->type = node->id.eval; // Restore so we're adding actual id + } + + return 0; +} + +static +int flecs_script_eval_with( + ecs_script_eval_visitor_t *v, + ecs_script_with_t *node) +{ + ecs_allocator_t *a = v->allocator; + int32_t prev_with_count = flecs_script_with_count(v); + ecs_stack_cursor_t *prev_stack_cursor = flecs_stack_get_cursor(&v->stack); + int result = 0; + + if (ecs_script_visit_scope(v, node->expressions)) { + result = -1; + goto error; + } + + ecs_value_t *value = flecs_script_with_last(v); + if (!value->ptr) { + if (ecs_is_valid(v->world, value->type)) { + node->scope->default_component_eval = value->type; + } + } + + if (ecs_script_visit_scope(v, node->scope)) { + result = -1; + goto error; + } + +error: + flecs_script_with_set_count(a, v, prev_with_count); + flecs_stack_restore_cursor(&v->stack, prev_stack_cursor); + return result; +} + +static +int flecs_script_eval_using( + ecs_script_eval_visitor_t *v, + ecs_script_using_t *node) +{ + ecs_allocator_t *a = v->allocator; + int32_t len = ecs_os_strlen(node->name); + + if (len > 2 && !ecs_os_strcmp(&node->name[len - 2], ".*")) { + char *path = flecs_strdup(a, node->name); + path[len - 2] = '\0'; + + ecs_entity_t from = ecs_lookup(v->world, path); + if (!from) { + flecs_script_eval_error(v, node, + "unresolved path '%s' in using statement", path); + flecs_strfree(a, path); + return -1; + } + + /* Add each child of the scope to using stack */ + ecs_iter_t it = ecs_children(v->world, from); + while (ecs_children_next(&it)) { + int32_t i, count = it.count; + for (i = 0; i < count; i ++) { + ecs_vec_append_t( + a, &v->using, ecs_entity_t)[0] = it.entities[i]; + } + } + + flecs_strfree(a, path); + } else { + ecs_entity_t from = ecs_lookup_path_w_sep( + v->world, 0, node->name, NULL, NULL, false); + if (!from) { + from = ecs_entity(v->world, { + .name = node->name, + .root_sep = "" + }); + + if (!from) { + return -1; + } + } + + ecs_vec_append_t(a, &v->using, ecs_entity_t)[0] = from; + } + + return 0; +} + +static +int flecs_script_eval_module( + ecs_script_eval_visitor_t *v, + ecs_script_module_t *node) +{ + ecs_entity_t m = flecs_script_create_entity(v, node->name); + if (!m) { + return -1; + } + + ecs_add_id(v->world, m, EcsModule); + + v->module = m; + v->parent = m; + + return 0; +} + +static +int flecs_script_eval_const( + ecs_script_eval_visitor_t *v, + ecs_script_var_node_t *node) +{ + ecs_script_var_t *var = ecs_script_vars_declare(v->vars, node->name); + if (!var) { + flecs_script_eval_error(v, node, + "variable '%s' redeclared", node->name); + return -1; + } + + if (node->type) { + ecs_entity_t type = flecs_script_find_entity(v, 0, node->type); + if (!type) { + flecs_script_eval_error(v, node, + "unresolved type '%s' for const variable '%s'", + node->type, node->name); + return -1; + } + + const ecs_type_info_t *ti = flecs_script_get_type_info(v, node, type); + if (!ti) { + flecs_script_eval_error(v, node, + "failed to retrieve type info for '%s' for const variable '%s'", + node->type, node->name); + return -1; + } + + var->value.ptr = flecs_stack_calloc(&v->stack, ti->size, ti->alignment); + var->value.type = type; + var->type_info = ti; + + if (ti->hooks.ctor) { + ti->hooks.ctor(var->value.ptr, 1, ti); + } + + if (flecs_script_eval_expr(v, node->expr, &var->value)) { + flecs_script_eval_error(v, node, + "failed to evaluate expression for const variable '%s'", + node->name); + return -1; + } + } else { + /* We don't know the type yet, so we can't create a storage for it yet. + * Run the expression first to deduce the type. */ + ecs_value_t value = {0}; + if (flecs_script_eval_expr(v, node->expr, &value)) { + flecs_script_eval_error(v, node, + "failed to evaluate expression for const variable '%s'", + node->name); + return -1; + } + + ecs_assert(value.type != 0, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *ti = ecs_get_type_info(v->world, value.type); + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); + + var->value.ptr = flecs_stack_calloc(&v->stack, ti->size, ti->alignment); + var->value.type = value.type; + var->type_info = ti; + + if (ti->hooks.ctor) { + ti->hooks.ctor(var->value.ptr, 1, ti); + } + + ecs_value_copy_w_type_info(v->world, ti, var->value.ptr, value.ptr); + ecs_value_fini_w_type_info(v->world, ti, value.ptr); + flecs_free(&v->world->allocator, ti->size, value.ptr); + } + + return 0; +} + +static +int flecs_script_eval_pair_scope( + ecs_script_eval_visitor_t *v, + ecs_script_pair_scope_t *node) +{ + ecs_entity_t first = flecs_script_find_entity(v, 0, node->id.first); + if (!first) { + first = flecs_script_create_entity(v, node->id.first); + if (!first) { + return -1; + } + } + + ecs_entity_t second = flecs_script_create_entity(v, node->id.second); + if (!second) { + return -1; + } + + ecs_allocator_t *a = v->allocator; + ecs_entity_t prev_first = v->with_relationship; + ecs_entity_t prev_second = 0; + int32_t prev_with_relationship_sp = v->with_relationship_sp; + + v->with_relationship = first; + + if (prev_first != first) { + /* Append new element to with stack */ + ecs_value_t *value = flecs_script_with_append(a, v, NULL); + value->type = ecs_pair(first, second); + value->ptr = NULL; + v->with_relationship_sp = flecs_script_with_count(v) - 1; + } else { + /* Get existing with element for current relationhip stack */ + ecs_value_t *value = ecs_vec_get_t( + &v->with, ecs_value_t, v->with_relationship_sp); + ecs_assert(ECS_PAIR_FIRST(value->type) == (uint32_t)first, + ECS_INTERNAL_ERROR, NULL); + prev_second = ECS_PAIR_SECOND(value->type); + value->type = ecs_pair(first, second); + value->ptr = NULL; + } + + if (ecs_script_visit_scope(v, node->scope)) { + return -1; + } + + if (prev_second) { + ecs_value_t *value = ecs_vec_get_t( + &v->with, ecs_value_t, v->with_relationship_sp); + value->type = ecs_pair(first, prev_second); + } else { + flecs_script_with_set_count(a, v, v->with_relationship_sp); + } + + v->with_relationship = prev_first; + v->with_relationship_sp = prev_with_relationship_sp; + + return 0; +} + +static +int flecs_script_eval_if( + ecs_script_eval_visitor_t *v, + ecs_script_if_t *node) +{ + ecs_value_t condval = { .type = 0, .ptr = NULL }; + if (flecs_script_eval_expr(v, node->expr, &condval)) { + return -1; + } + + bool cond; + if (condval.type == ecs_id(ecs_bool_t)) { + cond = *(bool*)(condval.ptr); + } else { + ecs_meta_cursor_t cur = ecs_meta_cursor( + v->world, condval.type, condval.ptr); + cond = ecs_meta_get_bool(&cur); + } + + ecs_value_free(v->world, condval.type, condval.ptr); + + if (flecs_script_eval_scope(v, cond ? node->if_true : node->if_false)) { + return -1; + } + + return 0; +} + +static +int flecs_script_eval_annot( + ecs_script_eval_visitor_t *v, + ecs_script_annot_t *node) +{ + if (!v->base.next) { + flecs_script_eval_error(v, node, + "annotation '%s' is not applied to anything", node->name); + return -1; + } + + ecs_script_node_kind_t kind = v->base.next->kind; + if (kind != EcsAstEntity && kind != EcsAstAnnotation) { + flecs_script_eval_error(v, node, + "annotation must be applied to an entity"); + return -1; + } + + ecs_allocator_t *a = v->allocator; + ecs_vec_append_t(a, &v->annot, ecs_script_annot_t*)[0] = node; + + return 0; +} + +int flecs_script_eval_node( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node) +{ + switch(node->kind) { + case EcsAstScope: + return flecs_script_eval_scope( + v, (ecs_script_scope_t*)node); + case EcsAstTag: + return flecs_script_eval_tag( + v, (ecs_script_tag_t*)node); + case EcsAstComponent: + return flecs_script_eval_component( + v, (ecs_script_component_t*)node); + case EcsAstVarComponent: + return flecs_script_eval_var_component( + v, (ecs_script_var_component_t*)node); + case EcsAstDefaultComponent: + return flecs_script_eval_default_component( + v, (ecs_script_default_component_t*)node); + case EcsAstWithVar: + return flecs_script_eval_with_var( + v, (ecs_script_var_node_t*)node); + case EcsAstWithTag: + return flecs_script_eval_with_tag( + v, (ecs_script_tag_t*)node); + case EcsAstWithComponent: + return flecs_script_eval_with_component( + v, (ecs_script_component_t*)node); + case EcsAstWith: + return flecs_script_eval_with( + v, (ecs_script_with_t*)node); + case EcsAstUsing: + return flecs_script_eval_using( + v, (ecs_script_using_t*)node); + case EcsAstModule: + return flecs_script_eval_module( + v, (ecs_script_module_t*)node); + case EcsAstAnnotation: + return flecs_script_eval_annot( + v, (ecs_script_annot_t*)node); + case EcsAstTemplate: + return flecs_script_eval_template( + v, (ecs_script_template_node_t*)node); + case EcsAstProp: + return 0; + case EcsAstConst: + return flecs_script_eval_const( + v, (ecs_script_var_node_t*)node); + case EcsAstEntity: + return flecs_script_eval_entity( + v, (ecs_script_entity_t*)node); + case EcsAstPairScope: + return flecs_script_eval_pair_scope( + v, (ecs_script_pair_scope_t*)node); + case EcsAstIf: + return flecs_script_eval_if( + v, (ecs_script_if_t*)node); + } + + ecs_abort(ECS_INTERNAL_ERROR, "corrupt AST node kind"); +} + +void flecs_script_eval_visit_init( + ecs_script_impl_t *script, + ecs_script_eval_visitor_t *v) +{ + *v = (ecs_script_eval_visitor_t){ + .base = { + .script = script, + .visit = (ecs_visit_action_t)flecs_script_eval_node + }, + .world = script->pub.world, + .allocator = &script->allocator + }; + + flecs_stack_init(&v->stack); + ecs_vec_init_t(v->allocator, &v->using, ecs_entity_t, 0); + ecs_vec_init_t(v->allocator, &v->with, ecs_value_t, 0); + ecs_vec_init_t(v->allocator, &v->with_type_info, ecs_type_info_t*, 0); + ecs_vec_init_t(v->allocator, &v->annot, ecs_script_annot_t*, 0); + + /* Always include flecs.meta */ + ecs_vec_append_t(v->allocator, &v->using, ecs_entity_t)[0] = + ecs_lookup(v->world, "flecs.meta"); +} + +void flecs_script_eval_visit_fini( + ecs_script_eval_visitor_t *v) +{ + ecs_vec_fini_t(v->allocator, &v->annot, ecs_script_annot_t*); + ecs_vec_fini_t(v->allocator, &v->with, ecs_value_t); + ecs_vec_fini_t(v->allocator, &v->with_type_info, ecs_type_info_t*); + ecs_vec_fini_t(v->allocator, &v->using, ecs_entity_t); + flecs_stack_fini(&v->stack); +} + +int ecs_script_eval( + ecs_script_t *script) +{ + ecs_script_eval_visitor_t v; + ecs_script_impl_t *impl = flecs_script_impl(script); + flecs_script_eval_visit_init(impl, &v); + int result = ecs_script_visit(impl, &v, flecs_script_eval_node); + flecs_script_eval_visit_fini(&v); + return result; +} + +#endif diff --git a/vendors/flecs/src/addons/script/visit_eval.h b/vendors/flecs/src/addons/script/visit_eval.h new file mode 100644 index 000000000..2e7e9336a --- /dev/null +++ b/vendors/flecs/src/addons/script/visit_eval.h @@ -0,0 +1,77 @@ +/** + * @file addons/script/visit_eval.h + * @brief Script evaluation visitor. + */ + +#ifndef FLECS_SCRIPT_VISIT_EVAL_H +#define FLECS_SCRIPT_VISIT_EVAL_H + +typedef struct ecs_script_eval_visitor_t { + ecs_script_visit_t base; + ecs_world_t *world; + ecs_allocator_t *allocator; + ecs_script_template_t *template; + ecs_entity_t module; + ecs_entity_t parent; + ecs_script_entity_t *entity; + ecs_vec_t using; + ecs_vec_t with; + ecs_vec_t with_type_info; + ecs_vec_t annot; + ecs_entity_t with_relationship; + int32_t with_relationship_sp; + ecs_script_vars_t *vars; + ecs_stack_t stack; +} ecs_script_eval_visitor_t; + +void flecs_script_eval_error_( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node, + const char *fmt, + ...); + +#define flecs_script_eval_error(v, node, ...)\ + flecs_script_eval_error_(v, (ecs_script_node_t*)node, __VA_ARGS__) + +ecs_entity_t flecs_script_find_entity( + ecs_script_eval_visitor_t *v, + ecs_entity_t from, + const char *path); + +ecs_entity_t flecs_script_create_entity( + ecs_script_eval_visitor_t *v, + const char *name); + +const ecs_type_info_t* flecs_script_get_type_info( + ecs_script_eval_visitor_t *v, + void *node, + ecs_id_t id); + +int flecs_script_eval_expr( + ecs_script_eval_visitor_t *v, + const char *expr, + ecs_value_t *value); + +int flecs_script_eval_template( + ecs_script_eval_visitor_t *v, + ecs_script_template_node_t *template); + +ecs_script_template_t* flecs_script_template_init( + ecs_script_impl_t *script); + +void flecs_script_template_fini( + ecs_script_impl_t *script, + ecs_script_template_t *template); + +void flecs_script_eval_visit_init( + ecs_script_impl_t *script, + ecs_script_eval_visitor_t *v); + +void flecs_script_eval_visit_fini( + ecs_script_eval_visitor_t *v); + +int flecs_script_eval_node( + ecs_script_eval_visitor_t *v, + ecs_script_node_t *node); + +#endif diff --git a/vendors/flecs/src/addons/script/visit_free.c b/vendors/flecs/src/addons/script/visit_free.c new file mode 100644 index 000000000..ec2f3c5f5 --- /dev/null +++ b/vendors/flecs/src/addons/script/visit_free.c @@ -0,0 +1,151 @@ +/** + * @file addons/script/visit_free.c + * @brief Script free visitor (frees AST resources). + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" + +static +void flecs_script_scope_free( + ecs_script_visit_t *v, + ecs_script_scope_t *node) +{ + ecs_script_visit_scope(v, node); + ecs_vec_fini_t(&v->script->allocator, &node->stmts, ecs_script_node_t*); + flecs_free_t(&v->script->allocator, ecs_script_scope_t, node); +} + +static +void flecs_script_with_free( + ecs_script_visit_t *v, + ecs_script_with_t *node) +{ + flecs_script_scope_free(v, node->expressions); + flecs_script_scope_free(v, node->scope); +} + +static +void flecs_script_template_free( + ecs_script_visit_t *v, + ecs_script_template_node_t *node) +{ + flecs_script_scope_free(v, node->scope); +} + +static +void flecs_script_entity_free( + ecs_script_visit_t *v, + ecs_script_entity_t *node) +{ + flecs_script_scope_free(v, node->scope); +} + +static +void flecs_script_pair_scope_free( + ecs_script_visit_t *v, + ecs_script_pair_scope_t *node) +{ + flecs_script_scope_free(v, node->scope); +} + +static +void flecs_script_if_free( + ecs_script_visit_t *v, + ecs_script_if_t *node) +{ + flecs_script_scope_free(v, node->if_true); + flecs_script_scope_free(v, node->if_false); +} + +static +int flecs_script_stmt_free( + ecs_script_visit_t *v, + ecs_script_node_t *node) +{ + ecs_allocator_t *a = &v->script->allocator; + switch(node->kind) { + case EcsAstScope: + flecs_script_scope_free(v, (ecs_script_scope_t*)node); + break; + case EcsAstWith: + flecs_script_with_free(v, (ecs_script_with_t*)node); + flecs_free_t(a, ecs_script_with_t, node); + break; + case EcsAstTemplate: + flecs_script_template_free(v, (ecs_script_template_node_t*)node); + flecs_free_t(a, ecs_script_template_node_t, node); + break; + case EcsAstEntity: + flecs_script_entity_free(v, (ecs_script_entity_t*)node); + flecs_free_t(a, ecs_script_entity_t, node); + break; + case EcsAstPairScope: + flecs_script_pair_scope_free(v, (ecs_script_pair_scope_t*)node); + flecs_free_t(a, ecs_script_pair_scope_t, node); + break; + case EcsAstIf: + flecs_script_if_free(v, (ecs_script_if_t*)node); + flecs_free_t(a, ecs_script_if_t, node); + break; + case EcsAstTag: + flecs_free_t(a, ecs_script_tag_t, node); + break; + case EcsAstComponent: + flecs_free_t(a, ecs_script_component_t, node); + break; + case EcsAstDefaultComponent: + flecs_free_t(a, ecs_script_default_component_t, node); + break; + case EcsAstVarComponent: + flecs_free_t(a, ecs_script_var_component_t, node); + break; + case EcsAstWithVar: + flecs_free_t(a, ecs_script_var_component_t, node); + break; + case EcsAstWithTag: + flecs_free_t(a, ecs_script_tag_t, node); + break; + case EcsAstWithComponent: + flecs_free_t(a, ecs_script_component_t, node); + break; + case EcsAstUsing: + flecs_free_t(a, ecs_script_using_t, node); + break; + case EcsAstModule: + flecs_free_t(a, ecs_script_module_t, node); + break; + case EcsAstAnnotation: + flecs_free_t(a, ecs_script_annot_t, node); + break; + case EcsAstProp: + case EcsAstConst: + flecs_free_t(a, ecs_script_var_node_t, node); + break; + } + + return 0; +} + +int flecs_script_visit_free( + ecs_script_t *script) +{ + ecs_check(script != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_script_visit_t v = { + .script = flecs_script_impl(script) + }; + + if (ecs_script_visit( + flecs_script_impl(script), &v, flecs_script_stmt_free)) + { + goto error; + } + + return 0; +error: + return - 1; +} + +#endif diff --git a/vendors/flecs/src/addons/script/visit_to_str.c b/vendors/flecs/src/addons/script/visit_to_str.c new file mode 100644 index 000000000..0c7d6b55d --- /dev/null +++ b/vendors/flecs/src/addons/script/visit_to_str.c @@ -0,0 +1,418 @@ +/** + * @file addons/script/visit_to_str.c + * @brief Script AST to string visitor. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT +#include "script.h" + +typedef struct ecs_script_str_visitor_t { + ecs_script_visit_t base; + ecs_strbuf_t *buf; + int32_t depth; + bool newline; +} ecs_script_str_visitor_t; + +static +int flecs_script_scope_to_str( + ecs_script_str_visitor_t *v, + ecs_script_scope_t *scope); + +static +void flecs_scriptbuf_append( + ecs_script_str_visitor_t *v, + const char *fmt, + ...) +{ + if (v->newline) { + ecs_strbuf_append(v->buf, "%*s", v->depth * 2, ""); + v->newline = false; + } + + va_list args; + va_start(args, fmt); + ecs_strbuf_vappend(v->buf, fmt, args); + va_end(args); + + if (fmt[strlen(fmt) - 1] == '\n') { + v->newline = true; + } +} + +static +void flecs_scriptbuf_appendstr( + ecs_script_str_visitor_t *v, + const char *str) +{ + if (v->newline) { + ecs_strbuf_append(v->buf, "%*s", v->depth * 2, ""); + v->newline = false; + } + + ecs_strbuf_appendstr(v->buf, str); + + if (str[strlen(str) - 1] == '\n') { + v->newline = true; + } +} + +static +void flecs_script_id_to_str( + ecs_script_str_visitor_t *v, + ecs_script_id_t *id) +{ + if (id->flag) { + if (id->flag == ECS_AUTO_OVERRIDE) { + flecs_scriptbuf_appendstr(v, "auto_override | "); + } else { + flecs_scriptbuf_appendstr(v, "??? | "); + } + } + + if (id->second) { + flecs_scriptbuf_append(v, "(%s, %s)", + id->first, id->second); + } else { + flecs_scriptbuf_appendstr(v, id->first); + } +} + +static +void flecs_script_expr_to_str( + ecs_script_str_visitor_t *v, + const char *expr) +{ + if (expr) { + flecs_scriptbuf_append(v, "%s%s%s", ECS_GREEN, expr, ECS_NORMAL); + } else { + flecs_scriptbuf_appendstr(v, "{}"); + } +} + +static +const char* flecs_script_node_to_str( + ecs_script_node_t *node) +{ + switch(node->kind) { + case EcsAstScope: return "scope"; + case EcsAstWithTag: + case EcsAstTag: return "tag"; + case EcsAstWithComponent: + case EcsAstComponent: return "component"; + case EcsAstWithVar: + case EcsAstVarComponent: return "var"; + case EcsAstDefaultComponent: return "default_component"; + case EcsAstWith: return "with"; + case EcsAstUsing: return "using"; + case EcsAstModule: return "module"; + case EcsAstAnnotation: return "annot"; + case EcsAstTemplate: return "template"; + case EcsAstProp: return "prop"; + case EcsAstConst: return "const"; + case EcsAstEntity: return "entity"; + case EcsAstPairScope: return "pair_scope"; + case EcsAstIf: return "if"; + } + return "???"; +} + +static +void flecs_scriptbuf_node( + ecs_script_str_visitor_t *v, + ecs_script_node_t *node) +{ + flecs_scriptbuf_append(v, "%s%s%s: ", + ECS_BLUE, flecs_script_node_to_str(node), ECS_NORMAL); +} + +static +void flecs_script_tag_to_str( + ecs_script_str_visitor_t *v, + ecs_script_tag_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_script_id_to_str(v, &node->id); + flecs_scriptbuf_appendstr(v, "\n"); +} + +static +void flecs_script_component_to_str( + ecs_script_str_visitor_t *v, + ecs_script_component_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_script_id_to_str(v, &node->id); + if (node->expr) { + flecs_scriptbuf_appendstr(v, ": "); + flecs_script_expr_to_str(v, node->expr); + } + flecs_scriptbuf_appendstr(v, "\n"); +} + +static +void flecs_script_default_component_to_str( + ecs_script_str_visitor_t *v, + ecs_script_default_component_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + if (node->expr) { + flecs_script_expr_to_str(v, node->expr); + } + flecs_scriptbuf_appendstr(v, "\n"); +} + +static +void flecs_script_with_var_to_str( + ecs_script_str_visitor_t *v, + ecs_script_var_component_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s ", node->name); + flecs_scriptbuf_appendstr(v, "\n"); +} + +static +void flecs_script_with_to_str( + ecs_script_str_visitor_t *v, + ecs_script_with_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + + flecs_scriptbuf_appendstr(v, "{\n"); + v->depth ++; + flecs_scriptbuf_append(v, "%sexpressions%s: ", ECS_CYAN, ECS_NORMAL); + flecs_script_scope_to_str(v, node->expressions); + flecs_scriptbuf_append(v, "%sscope%s: ", ECS_CYAN, ECS_NORMAL); + flecs_script_scope_to_str(v, node->scope); + v->depth --; + flecs_scriptbuf_appendstr(v, "}\n"); +} + +static +void flecs_script_using_to_str( + ecs_script_str_visitor_t *v, + ecs_script_using_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s\n", node->name); +} + +static +void flecs_script_module_to_str( + ecs_script_str_visitor_t *v, + ecs_script_module_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s\n", node->name); +} + +static +void flecs_script_annot_to_str( + ecs_script_str_visitor_t *v, + ecs_script_annot_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s = %s\"%s\"%s", node->name, + ECS_GREEN, node->expr, ECS_NORMAL); + flecs_scriptbuf_appendstr(v, "\n"); +} + +static +void flecs_script_template_to_str( + ecs_script_str_visitor_t *v, + ecs_script_template_node_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_scriptbuf_append(v, "%s ", node->name); + flecs_script_scope_to_str(v, node->scope); +} + +static +void flecs_script_var_node_to_str( + ecs_script_str_visitor_t *v, + ecs_script_var_node_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + if (node->type) { + flecs_scriptbuf_append(v, "%s : %s = ", + node->name, + node->type); + } else { + flecs_scriptbuf_append(v, "%s = ", + node->name); + } + flecs_script_expr_to_str(v, node->expr); + flecs_scriptbuf_appendstr(v, "\n"); +} + +static +void flecs_script_entity_to_str( + ecs_script_str_visitor_t *v, + ecs_script_entity_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + if (node->kind) { + flecs_scriptbuf_append(v, "%s ", node->kind); + } + if (node->name) { + flecs_scriptbuf_append(v, "%s ", node->name); + } else { + flecs_scriptbuf_appendstr(v, " "); + } + + if (!flecs_scope_is_empty(node->scope)) { + flecs_script_scope_to_str(v, node->scope); + } else { + flecs_scriptbuf_appendstr(v, "\n"); + } +} + +static +void flecs_script_pair_scope_to_str( + ecs_script_str_visitor_t *v, + ecs_script_pair_scope_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_script_id_to_str(v, &node->id); + flecs_scriptbuf_appendstr(v, " "); + flecs_script_scope_to_str(v, node->scope); +} + +static +void flecs_script_if_to_str( + ecs_script_str_visitor_t *v, + ecs_script_if_t *node) +{ + flecs_scriptbuf_node(v, &node->node); + flecs_script_expr_to_str(v, node->expr); + + flecs_scriptbuf_appendstr(v, " {\n"); + v->depth ++; + flecs_scriptbuf_append(v, "%strue%s: ", ECS_CYAN, ECS_NORMAL); + flecs_script_scope_to_str(v, node->if_true); + flecs_scriptbuf_append(v, "%sfalse%s: ", ECS_CYAN, ECS_NORMAL); + flecs_script_scope_to_str(v, node->if_false); + v->depth --; + flecs_scriptbuf_appendstr(v, "}\n"); +} + +static +int flecs_script_scope_to_str( + ecs_script_str_visitor_t *v, + ecs_script_scope_t *scope) +{ + if (!ecs_vec_count(&scope->stmts)) { + flecs_scriptbuf_appendstr(v, "{}\n"); + return 0; + } + + flecs_scriptbuf_appendstr(v, "{\n"); + + v->depth ++; + + if (ecs_script_visit_scope(v, scope)) { + return -1; + } + + v->depth --; + + flecs_scriptbuf_appendstr(v, "}\n"); + + return 0; +} + +static +int flecs_script_stmt_to_str( + ecs_script_str_visitor_t *v, + ecs_script_node_t *node) +{ + switch(node->kind) { + case EcsAstScope: + if (flecs_script_scope_to_str(v, (ecs_script_scope_t*)node)) { + return -1; + } + break; + case EcsAstTag: + case EcsAstWithTag: + flecs_script_tag_to_str(v, (ecs_script_tag_t*)node); + break; + case EcsAstComponent: + case EcsAstWithComponent: + flecs_script_component_to_str(v, (ecs_script_component_t*)node); + break; + case EcsAstVarComponent: + case EcsAstWithVar: + flecs_script_with_var_to_str(v, + (ecs_script_var_component_t*)node); + break; + case EcsAstDefaultComponent: + flecs_script_default_component_to_str(v, + (ecs_script_default_component_t*)node); + break; + case EcsAstWith: + flecs_script_with_to_str(v, (ecs_script_with_t*)node); + break; + case EcsAstUsing: + flecs_script_using_to_str(v, (ecs_script_using_t*)node); + break; + case EcsAstModule: + flecs_script_module_to_str(v, (ecs_script_module_t*)node); + break; + case EcsAstAnnotation: + flecs_script_annot_to_str(v, (ecs_script_annot_t*)node); + break; + case EcsAstTemplate: + flecs_script_template_to_str(v, (ecs_script_template_node_t*)node); + break; + case EcsAstConst: + case EcsAstProp: + flecs_script_var_node_to_str(v, (ecs_script_var_node_t*)node); + break; + case EcsAstEntity: + flecs_script_entity_to_str(v, (ecs_script_entity_t*)node); + break; + case EcsAstPairScope: + flecs_script_pair_scope_to_str(v, (ecs_script_pair_scope_t*)node); + break; + case EcsAstIf: + flecs_script_if_to_str(v, (ecs_script_if_t*)node); + break; + } + + return 0; +} + +int ecs_script_ast_to_buf( + ecs_script_t *script, + ecs_strbuf_t *buf) +{ + ecs_check(script != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(buf != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_script_str_visitor_t v = { .buf = buf }; + if (ecs_script_visit(flecs_script_impl(script), &v, flecs_script_stmt_to_str)) { + goto error; + } + + return 0; +error: + ecs_strbuf_reset(buf); + return - 1; +} + +char* ecs_script_ast_to_str( + ecs_script_t *script) +{ + ecs_check(script != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_strbuf_t buf = ECS_STRBUF_INIT; + if (ecs_script_ast_to_buf(script, &buf)) { + goto error; + } + + return ecs_strbuf_get(&buf); +error: + return NULL; +} + +#endif diff --git a/vendors/flecs/src/addons/snapshot.c b/vendors/flecs/src/addons/snapshot.c deleted file mode 100644 index e484e9678..000000000 --- a/vendors/flecs/src/addons/snapshot.c +++ /dev/null @@ -1,443 +0,0 @@ -/** - * @file addons/snapshot.c - * @brief Snapshot addon. - */ - -#include "flecs.h" - -#ifdef FLECS_SNAPSHOT - -#include "../private_api.h" - -/* World snapshot */ -struct ecs_snapshot_t { - ecs_world_t *world; - ecs_entity_index_t entity_index; - ecs_vec_t tables; - uint64_t last_id; -}; - -/** Small footprint data structure for storing data associated with a table. */ -typedef struct ecs_table_leaf_t { - ecs_table_t *table; - ecs_type_t type; - ecs_data_t *data; -} ecs_table_leaf_t; - -static -ecs_data_t* flecs_duplicate_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *main_data) -{ - if (!ecs_table_count(table)) { - return NULL; - } - - ecs_data_t *result = ecs_os_calloc_t(ecs_data_t); - int32_t i, column_count = table->column_count; - result->columns = flecs_wdup_n(world, ecs_column_t, column_count, - main_data->columns); - - /* Copy entities */ - ecs_allocator_t *a = &world->allocator; - result->entities = ecs_vec_copy_t(a, &main_data->entities, ecs_entity_t); - - /* Copy each column */ - for (i = 0; i < column_count; i ++) { - ecs_column_t *column = &result->columns[i]; - ecs_type_info_t *ti = column->ti; - ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t size = ti->size; - ecs_copy_t copy = ti->hooks.copy; - if (copy) { - ecs_vec_t dst = ecs_vec_copy(a, &column->data, size); - int32_t count = ecs_vec_count(&column->data); - void *dst_ptr = ecs_vec_first(&dst); - void *src_ptr = ecs_vec_first(&column->data); - - ecs_xtor_t ctor = ti->hooks.ctor; - if (ctor) { - ctor(dst_ptr, count, ti); - } - - copy(dst_ptr, src_ptr, count, ti); - column->data = dst; - } else { - column->data = ecs_vec_copy(a, &column->data, size); - } - } - - return result; -} - -static -void snapshot_table( - const ecs_world_t *world, - ecs_snapshot_t *snapshot, - ecs_table_t *table) -{ - if (table->flags & EcsTableHasBuiltins) { - return; - } - - ecs_table_leaf_t *l = ecs_vec_get_t( - &snapshot->tables, ecs_table_leaf_t, (int32_t)table->id); - ecs_assert(l != NULL, ECS_INTERNAL_ERROR, NULL); - - l->table = table; - l->type = flecs_type_copy( - ECS_CONST_CAST(ecs_world_t*, world), &table->type); - l->data = flecs_duplicate_data( - ECS_CONST_CAST(ecs_world_t*, world), table, &table->data); -} - -static -ecs_snapshot_t* snapshot_create( - const ecs_world_t *world, - const ecs_entity_index_t *entity_index, - ecs_iter_t *iter, - ecs_iter_next_action_t next) -{ - ecs_snapshot_t *result = ecs_os_calloc_t(ecs_snapshot_t); - ecs_assert(result != NULL, ECS_OUT_OF_MEMORY, NULL); - - ecs_run_aperiodic(ECS_CONST_CAST(ecs_world_t*, world), 0); - - result->world = ECS_CONST_CAST(ecs_world_t*, world); - - /* If no iterator is provided, the snapshot will be taken of the entire - * world, and we can simply copy the entity index as it will be restored - * entirely upon snapshote restore. */ - if (!iter && entity_index) { - flecs_entities_copy(&result->entity_index, entity_index); - } - - /* Create vector with as many elements as tables, so we can store the - * snapshot tables at their element ids. When restoring a snapshot, the code - * will run a diff between the tables in the world and the snapshot, to see - * which of the world tables still exist, no longer exist, or need to be - * deleted. */ - uint64_t t, table_count = flecs_sparse_last_id(&world->store.tables) + 1; - ecs_vec_init_t(NULL, &result->tables, ecs_table_leaf_t, (int32_t)table_count); - ecs_vec_set_count_t(NULL, &result->tables, ecs_table_leaf_t, (int32_t)table_count); - ecs_table_leaf_t *arr = ecs_vec_first_t(&result->tables, ecs_table_leaf_t); - - /* Array may have holes, so initialize with 0 */ - ecs_os_memset_n(arr, 0, ecs_table_leaf_t, table_count); - - /* Iterate tables in iterator */ - if (iter) { - while (next(iter)) { - ecs_table_t *table = iter->table; - snapshot_table(world, result, table); - } - } else { - for (t = 1; t < table_count; t ++) { - ecs_table_t *table = flecs_sparse_get_t( - &world->store.tables, ecs_table_t, t); - snapshot_table(world, result, table); - } - } - - return result; -} - -/** Create a snapshot */ -ecs_snapshot_t* ecs_snapshot_take( - ecs_world_t *stage) -{ - const ecs_world_t *world = ecs_get_world(stage); - - ecs_snapshot_t *result = snapshot_create( - world, ecs_eis(world), NULL, NULL); - - result->last_id = flecs_entities_max_id(world); - - return result; -} - -/** Create a filtered snapshot */ -ecs_snapshot_t* ecs_snapshot_take_w_iter( - ecs_iter_t *iter) -{ - ecs_world_t *world = iter->world; - ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_snapshot_t *result = snapshot_create( - world, ecs_eis(world), iter, iter ? iter->next : NULL); - - result->last_id = flecs_entities_max_id(world); - - return result; -} - -/* Restoring an unfiltered snapshot restores the world to the exact state it was - * when the snapshot was taken. */ -static -void restore_unfiltered( - ecs_world_t *world, - ecs_snapshot_t *snapshot) -{ - flecs_entity_index_restore(ecs_eis(world), &snapshot->entity_index); - flecs_entity_index_fini(&snapshot->entity_index); - - flecs_entities_max_id(world) = snapshot->last_id; - - ecs_table_leaf_t *leafs = ecs_vec_first_t(&snapshot->tables, ecs_table_leaf_t); - int32_t i, count = (int32_t)flecs_sparse_last_id(&world->store.tables); - int32_t snapshot_count = ecs_vec_count(&snapshot->tables); - - for (i = 1; i <= count; i ++) { - ecs_table_t *world_table = flecs_sparse_get_t( - &world->store.tables, ecs_table_t, (uint32_t)i); - - if (world_table && (world_table->flags & EcsTableHasBuiltins)) { - continue; - } - - ecs_table_leaf_t *snapshot_table = NULL; - if (i < snapshot_count) { - snapshot_table = &leafs[i]; - if (!snapshot_table->table) { - snapshot_table = NULL; - } - } - - /* If the world table no longer exists but the snapshot table does, - * reinsert it */ - if (!world_table && snapshot_table) { - ecs_table_t *table = flecs_table_find_or_create(world, - &snapshot_table->type); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - - if (snapshot_table->data) { - flecs_table_replace_data(world, table, snapshot_table->data); - } - - /* If the world table still exists, replace its data */ - } else if (world_table && snapshot_table) { - ecs_assert(snapshot_table->table == world_table, - ECS_INTERNAL_ERROR, NULL); - - if (snapshot_table->data) { - flecs_table_replace_data( - world, world_table, snapshot_table->data); - } else { - flecs_table_clear_data( - world, world_table, &world_table->data); - flecs_table_init_data(world, world_table); - } - - /* If the snapshot table doesn't exist, this table was created after the - * snapshot was taken and needs to be deleted */ - } else if (world_table && !snapshot_table) { - /* Deleting a table invokes OnRemove triggers & updates the entity - * index. That is not what we want, since entities may no longer be - * valid (if they don't exist in the snapshot) or may have been - * restored in a different table. Therefore first clear the data - * from the table (which doesn't invoke triggers), and then delete - * the table. */ - flecs_table_clear_data(world, world_table, &world_table->data); - flecs_delete_table(world, world_table); - - /* If there is no world & snapshot table, nothing needs to be done */ - } else { } - - if (snapshot_table) { - ecs_os_free(snapshot_table->data); - flecs_type_free(world, &snapshot_table->type); - } - } - - /* Now that all tables have been restored and world is in a consistent - * state, run OnSet systems */ - int32_t world_count = flecs_sparse_count(&world->store.tables); - for (i = 0; i < world_count; i ++) { - ecs_table_t *table = flecs_sparse_get_dense_t( - &world->store.tables, ecs_table_t, i); - if (table->flags & EcsTableHasBuiltins) { - continue; - } - - int32_t tcount = ecs_table_count(table); - if (tcount) { - int32_t j, storage_count = table->column_count; - for (j = 0; j < storage_count; j ++) { - ecs_type_t type = { - .array = &table->data.columns[j].id, - .count = 1 - }; - flecs_notify_on_set(world, table, 0, tcount, &type, true); - } - } - } -} - -/* Restoring a filtered snapshots only restores the entities in the snapshot - * to their previous state. */ -static -void restore_filtered( - ecs_world_t *world, - ecs_snapshot_t *snapshot) -{ - ecs_table_leaf_t *leafs = ecs_vec_first_t(&snapshot->tables, ecs_table_leaf_t); - int32_t l = 0, snapshot_count = ecs_vec_count(&snapshot->tables); - - for (l = 0; l < snapshot_count; l ++) { - ecs_table_leaf_t *snapshot_table = &leafs[l]; - ecs_table_t *table = snapshot_table->table; - - if (!table) { - continue; - } - - ecs_data_t *data = snapshot_table->data; - if (!data) { - flecs_type_free(world, &snapshot_table->type); - continue; - } - - /* Delete entity from storage first, so that when we restore it to the - * current table we can be sure that there won't be any duplicates */ - int32_t i, entity_count = ecs_vec_count(&data->entities); - ecs_entity_t *entities = ecs_vec_first( - &snapshot_table->data->entities); - for (i = 0; i < entity_count; i ++) { - ecs_entity_t e = entities[i]; - ecs_record_t *r = flecs_entities_try(world, e); - if (r && r->table) { - flecs_table_delete(world, r->table, - ECS_RECORD_TO_ROW(r->row), true); - } else { - /* Make sure that the entity has the same generation count */ - flecs_entities_set_generation(world, e); - } - } - - /* Merge data from snapshot table with world table */ - int32_t old_count = ecs_table_count(snapshot_table->table); - int32_t new_count = flecs_table_data_count(snapshot_table->data); - - flecs_table_merge(world, table, table, &table->data, snapshot_table->data); - - /* Run OnSet systems for merged entities */ - if (new_count) { - int32_t j, storage_count = table->column_count; - for (j = 0; j < storage_count; j ++) { - ecs_type_t type = { - .array = &table->data.columns[j].id, - .count = 1 - }; - flecs_notify_on_set( - world, table, old_count, new_count, &type, true); - } - } - - flecs_wfree_n(world, ecs_column_t, table->column_count, - snapshot_table->data->columns); - ecs_os_free(snapshot_table->data); - flecs_type_free(world, &snapshot_table->type); - } -} - -/** Restore a snapshot */ -void ecs_snapshot_restore( - ecs_world_t *world, - ecs_snapshot_t *snapshot) -{ - ecs_run_aperiodic(world, 0); - - if (flecs_entity_index_count(&snapshot->entity_index) > 0) { - /* Unfiltered snapshots have a copy of the entity index which is - * copied back entirely when the snapshot is restored */ - restore_unfiltered(world, snapshot); - } else { - restore_filtered(world, snapshot); - } - - ecs_vec_fini_t(NULL, &snapshot->tables, ecs_table_leaf_t); - - ecs_os_free(snapshot); -} - -ecs_iter_t ecs_snapshot_iter( - ecs_snapshot_t *snapshot) -{ - ecs_snapshot_iter_t iter = { - .tables = snapshot->tables, - .index = 0 - }; - - return (ecs_iter_t){ - .world = snapshot->world, - .table_count = ecs_vec_count(&snapshot->tables), - .priv.iter.snapshot = iter, - .next = ecs_snapshot_next - }; -} - -bool ecs_snapshot_next( - ecs_iter_t *it) -{ - ecs_snapshot_iter_t *iter = &it->priv.iter.snapshot; - ecs_table_leaf_t *tables = ecs_vec_first_t(&iter->tables, ecs_table_leaf_t); - int32_t count = ecs_vec_count(&iter->tables); - int32_t i; - - for (i = iter->index; i < count; i ++) { - ecs_table_t *table = tables[i].table; - if (!table) { - continue; - } - - ecs_data_t *data = tables[i].data; - - it->table = table; - it->count = ecs_table_count(table); - if (data) { - it->entities = ecs_vec_first(&data->entities); - } else { - it->entities = NULL; - } - - ECS_BIT_SET(it->flags, EcsIterIsValid); - iter->index = i + 1; - - goto yield; - } - - ECS_BIT_CLEAR(it->flags, EcsIterIsValid); - return false; - -yield: - ECS_BIT_CLEAR(it->flags, EcsIterIsValid); - return true; -} - -/** Cleanup snapshot */ -void ecs_snapshot_free( - ecs_snapshot_t *snapshot) -{ - flecs_entity_index_fini(&snapshot->entity_index); - - ecs_table_leaf_t *tables = ecs_vec_first_t(&snapshot->tables, ecs_table_leaf_t); - int32_t i, count = ecs_vec_count(&snapshot->tables); - for (i = 0; i < count; i ++) { - ecs_table_leaf_t *snapshot_table = &tables[i]; - ecs_table_t *table = snapshot_table->table; - if (table) { - ecs_data_t *data = snapshot_table->data; - if (data) { - flecs_table_clear_data(snapshot->world, table, data); - ecs_os_free(data); - } - flecs_type_free(snapshot->world, &snapshot_table->type); - } - } - - ecs_vec_fini_t(NULL, &snapshot->tables, ecs_table_leaf_t); - ecs_os_free(snapshot); -} - -#endif diff --git a/vendors/flecs/src/addons/stats/monitor.c b/vendors/flecs/src/addons/stats/monitor.c new file mode 100644 index 000000000..e25bbf2b9 --- /dev/null +++ b/vendors/flecs/src/addons/stats/monitor.c @@ -0,0 +1,413 @@ +/** + * @file addons/monitor.c + * @brief Stats addon module. + */ + +#include "flecs.h" +#include "stats.h" + +#ifdef FLECS_STATS + +ECS_COMPONENT_DECLARE(FlecsStats); + +ecs_entity_t EcsPeriod1s = 0; +ecs_entity_t EcsPeriod1m = 0; +ecs_entity_t EcsPeriod1h = 0; +ecs_entity_t EcsPeriod1d = 0; +ecs_entity_t EcsPeriod1w = 0; + +#define FlecsDayIntervalCount (24) +#define FlecsWeekIntervalCount (168) + +typedef struct { + ecs_stats_api_t api; + ecs_query_t *query; +} ecs_monitor_stats_ctx_t; + +typedef struct { + ecs_stats_api_t api; +} ecs_reduce_stats_ctx_t; + +typedef struct { + ecs_stats_api_t api; + int32_t interval; +} ecs_aggregate_stats_ctx_t; + +static +void MonitorStats(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + ecs_monitor_stats_ctx_t *ctx = it->ctx; + + EcsStatsHeader *hdr = ecs_field_w_size(it, 0, 0); + + ecs_ftime_t elapsed = hdr->elapsed; + hdr->elapsed += it->delta_time; + + int32_t t_last = (int32_t)(elapsed * 60); + int32_t t_next = (int32_t)(hdr->elapsed * 60); + int32_t i, dif = t_next - t_last; + void *stats_storage = ecs_os_alloca(ctx->api.stats_size); + void *last = NULL; + + if (!dif) { + hdr->reduce_count ++; + } + + ecs_iter_t qit; + int32_t cur = -1, count = 0; + void *stats = NULL; + ecs_map_t *stats_map = NULL; + + if (ctx->query) { + /* Query results are stored in a map */ + qit = ecs_query_iter(it->world, ctx->query); + stats_map = ECS_OFFSET_T(hdr, EcsStatsHeader); + } else { + /* No query, so tracking stats for single element */ + stats = ECS_OFFSET_T(hdr, EcsStatsHeader); + } + + do { + ecs_entity_t res = 0; + if (ctx->query) { + /* Query, fetch resource entity & stats pointer */ + if (cur == (count - 1)) { + if (!ecs_query_next(&qit)) { + break; + } + + cur = 0; + count = qit.count; + if (!count) { + cur = -1; + continue; + } + } else { + cur ++; + } + + res = qit.entities[cur]; + stats = ecs_map_ensure_alloc(stats_map, ctx->api.stats_size, res); + ctx->api.set_t(stats, t_last % ECS_STAT_WINDOW); + } + + if (!dif) { + /* Copy last value so we can pass it to reduce_last */ + last = stats_storage; + ecs_os_memset(last, 0, ctx->api.stats_size); + ctx->api.copy_last(last, stats); + } + + ctx->api.get(world, res, stats); + + if (!dif) { + /* Still in same interval, combine with last measurement */ + ctx->api.reduce_last(stats, last, hdr->reduce_count); + } else if (dif > 1) { + /* More than 16ms has passed, backfill */ + for (i = 1; i < dif; i ++) { + ctx->api.repeat_last(stats); + } + } + + if (last && ctx->api.fini) { + ctx->api.fini(last); + } + + if (!ctx->query) { + break; + } + } while (true); + + if (dif > 1) { + hdr->reduce_count = 0; + } +} + +static +void ReduceStats(ecs_iter_t *it) { + ecs_reduce_stats_ctx_t *ctx = it->ctx; + + void *dst = ecs_field_w_size(it, 0, 0); + void *src = ecs_field_w_size(it, 0, 1); + + dst = ECS_OFFSET_T(dst, EcsStatsHeader); + src = ECS_OFFSET_T(src, EcsStatsHeader); + + if (!ctx->api.query_component_id) { + ctx->api.reduce(dst, src); + } else { + ecs_map_iter_t mit = ecs_map_iter(src); + while (ecs_map_next(&mit)) { + void *src_el = ecs_map_ptr(&mit); + void *dst_el = ecs_map_ensure_alloc( + dst, ctx->api.stats_size, ecs_map_key(&mit)); + ctx->api.reduce(dst_el, src_el); + } + } +} + +static +void AggregateStats(ecs_iter_t *it) { + ecs_aggregate_stats_ctx_t *ctx = it->ctx; + int32_t interval = ctx->interval; + + EcsStatsHeader *dst_hdr = ecs_field_w_size(it, 0, 0); + EcsStatsHeader *src_hdr = ecs_field_w_size(it, 0, 1); + + void *dst = ECS_OFFSET_T(dst_hdr, EcsStatsHeader); + void *src = ECS_OFFSET_T(src_hdr, EcsStatsHeader); + void *dst_map = NULL; + void *src_map = NULL; + if (ctx->api.query_component_id) { + dst_map = dst; + src_map = src; + dst = NULL; + src = NULL; + } + + void *stats_storage = ecs_os_alloca(ctx->api.stats_size); + void *last = NULL; + + ecs_map_iter_t mit; + if (src_map) { + mit = ecs_map_iter(src_map); + } + + do { + if (src_map) { + if (!ecs_map_next(&mit)) { + break; + } + + src = ecs_map_ptr(&mit); + dst = ecs_map_ensure_alloc( + dst_map, ctx->api.stats_size, ecs_map_key(&mit)); + } + + if (dst_hdr->reduce_count != 0) { + /* Copy last value so we can pass it to reduce_last */ + last = stats_storage; + ecs_os_memset(last, 0, ctx->api.stats_size); + ctx->api.copy_last(last, dst); + } + + /* Reduce from minutes to the current day */ + ctx->api.reduce(dst, src); + + if (dst_hdr->reduce_count != 0) { + ctx->api.reduce_last(dst, last, dst_hdr->reduce_count); + } + + if (last && ctx->api.fini != NULL) { + ctx->api.fini(last); + } + + if (!src_map) { + break; + } + } while (true); + + /* A day has 60 24 minute intervals */ + dst_hdr->reduce_count ++; + if (dst_hdr->reduce_count >= interval) { + dst_hdr->reduce_count = 0; + } +} + +static +void flecs_monitor_ctx_free( + void *ptr) +{ + ecs_monitor_stats_ctx_t *ctx = ptr; + if (ctx->query) { + ecs_query_fini(ctx->query); + } + ecs_os_free(ctx); +} + +static +void flecs_reduce_ctx_free( + void *ptr) +{ + ecs_os_free(ptr); +} + +static +void flecs_aggregate_ctx_free( + void *ptr) +{ + ecs_os_free(ptr); +} + +void flecs_stats_api_import( + ecs_world_t *world, + ecs_stats_api_t *api) +{ + ecs_entity_t kind = api->monitor_component_id; + ecs_entity_t prev = ecs_set_scope(world, kind); + + ecs_query_t *q = NULL; + if (api->query_component_id) { + q = ecs_query(world, { + .terms = {{ .id = api->query_component_id }}, + .cache_kind = EcsQueryCacheNone, + .flags = EcsQueryMatchDisabled + }); + } + + // Called each frame, collects 60 measurements per second + { + ecs_monitor_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_monitor_stats_ctx_t); + ctx->api = *api; + ctx->query = q; + + ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1s", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1s), + .src.id = EcsWorld + }}, + .callback = MonitorStats, + .ctx = ctx, + .ctx_free = flecs_monitor_ctx_free + }); + } + + // Called each second, reduces into 60 measurements per minute + ecs_entity_t mw1m; + { + ecs_reduce_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_reduce_stats_ctx_t); + ctx->api = *api; + + mw1m = ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1m", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1m), + .src.id = EcsWorld + }, { + .id = ecs_pair(kind, EcsPeriod1s), + .src.id = EcsWorld + }}, + .callback = ReduceStats, + .interval = 1.0, + .ctx = ctx, + .ctx_free = flecs_reduce_ctx_free + }); + } + + // Called each minute, reduces into 60 measurements per hour + { + ecs_reduce_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_reduce_stats_ctx_t); + ctx->api = *api; + + ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1h", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1h), + .src.id = EcsWorld + }, { + .id = ecs_pair(kind, EcsPeriod1m), + .src.id = EcsWorld + }}, + .callback = ReduceStats, + .rate = 60, + .tick_source = mw1m, + .ctx = ctx, + .ctx_free = flecs_reduce_ctx_free + }); + } + + // Called each minute, reduces into 60 measurements per day + { + ecs_aggregate_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_aggregate_stats_ctx_t); + ctx->api = *api; + ctx->interval = FlecsDayIntervalCount; + + ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1d", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1d), + .src.id = EcsWorld + }, { + .id = ecs_pair(kind, EcsPeriod1m), + .src.id = EcsWorld + }}, + .callback = AggregateStats, + .rate = 60, + .tick_source = mw1m, + .ctx = ctx, + .ctx_free = flecs_aggregate_ctx_free + }); + } + + // Called each hour, reduces into 60 measurements per week + { + ecs_aggregate_stats_ctx_t *ctx = ecs_os_calloc_t(ecs_aggregate_stats_ctx_t); + ctx->api = *api; + ctx->interval = FlecsWeekIntervalCount; + + ecs_system(world, { + .entity = ecs_entity(world, { .name = "Monitor1w", .add = ecs_ids(ecs_dependson(EcsPreFrame)) }), + .query.terms = {{ + .id = ecs_pair(kind, EcsPeriod1w), + .src.id = EcsWorld + }, { + .id = ecs_pair(kind, EcsPeriod1h), + .src.id = EcsWorld + }}, + .callback = AggregateStats, + .rate = 60, + .tick_source = mw1m, + .ctx = ctx, + .ctx_free = flecs_aggregate_ctx_free + }); + } + + ecs_set_scope(world, prev); + + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1s); + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1m); + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1h); + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1d); + ecs_add_pair(world, EcsWorld, kind, EcsPeriod1w); +} + +void FlecsStatsImport( + ecs_world_t *world) +{ + ECS_MODULE_DEFINE(world, FlecsStats); + ECS_IMPORT(world, FlecsPipeline); + ECS_IMPORT(world, FlecsTimer); +#ifdef FLECS_META + ECS_IMPORT(world, FlecsMeta); +#endif +#ifdef FLECS_UNITS + ECS_IMPORT(world, FlecsUnits); +#endif +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsStats), + "Module that automatically monitors statistics for the world & systems"); +#endif + + ecs_set_name_prefix(world, "Ecs"); + + EcsPeriod1s = ecs_entity(world, { .name = "EcsPeriod1s" }); + EcsPeriod1m = ecs_entity(world, { .name = "EcsPeriod1m" }); + EcsPeriod1h = ecs_entity(world, { .name = "EcsPeriod1h" }); + EcsPeriod1d = ecs_entity(world, { .name = "EcsPeriod1d" }); + EcsPeriod1w = ecs_entity(world, { .name = "EcsPeriod1w" }); + + FlecsWorldSummaryImport(world); + FlecsWorldMonitorImport(world); + FlecsSystemMonitorImport(world); + FlecsPipelineMonitorImport(world); + + if (ecs_os_has_time()) { + ecs_measure_frame_time(world, true); + ecs_measure_system_time(world, true); + } +} + +#endif diff --git a/vendors/flecs/src/addons/stats/pipeline_monitor.c b/vendors/flecs/src/addons/stats/pipeline_monitor.c new file mode 100644 index 000000000..a4cddad0c --- /dev/null +++ b/vendors/flecs/src/addons/stats/pipeline_monitor.c @@ -0,0 +1,132 @@ +/** + * @file addons/stats/pipeline_monitor.c + * @brief Stats addon pipeline monitor + */ + +#include "flecs.h" +#include "stats.h" +#include "../pipeline/pipeline.h" + +#ifdef FLECS_STATS + +ECS_COMPONENT_DECLARE(EcsPipelineStats); + +static +void flecs_pipeline_monitor_dtor(EcsPipelineStats *ptr) { + ecs_map_iter_t it = ecs_map_iter(&ptr->stats); + while (ecs_map_next(&it)) { + ecs_pipeline_stats_t *stats = ecs_map_ptr(&it); + ecs_pipeline_stats_fini(stats); + ecs_os_free(stats); + } + ecs_map_fini(&ptr->stats); +} + +static ECS_CTOR(EcsPipelineStats, ptr, { + ecs_os_zeromem(ptr); + ecs_map_init(&ptr->stats, NULL); +}) + +static ECS_COPY(EcsPipelineStats, dst, src, { + (void)dst; + (void)src; + ecs_abort(ECS_INVALID_OPERATION, "cannot copy pipeline stats component"); +}) + +static ECS_MOVE(EcsPipelineStats, dst, src, { + flecs_pipeline_monitor_dtor(dst); + ecs_os_memcpy_t(dst, src, EcsPipelineStats); + ecs_os_zeromem(src); +}) + +static ECS_DTOR(EcsPipelineStats, ptr, { + flecs_pipeline_monitor_dtor(ptr); +}) + +static +void flecs_pipeline_stats_set_t( + void *stats, int32_t t) +{ + ecs_assert(t >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(t < ECS_STAT_WINDOW, ECS_INTERNAL_ERROR, NULL); + ((ecs_pipeline_stats_t*)stats)->t = t; +} + + +static +void flecs_pipeline_stats_copy_last( + void *stats, + void *src) +{ + ecs_pipeline_stats_copy_last(stats, src); +} + +static +void flecs_pipeline_stats_get( + ecs_world_t *world, + ecs_entity_t res, + void *stats) +{ + ecs_pipeline_stats_get(world, res, stats); +} + +static +void flecs_pipeline_stats_reduce( + void *stats, + void *src) +{ + ecs_pipeline_stats_reduce(stats, src); +} + +static +void flecs_pipeline_stats_reduce_last( + void *stats, + void *last, + int32_t reduce_count) +{ + ecs_pipeline_stats_reduce_last(stats, last, reduce_count); +} + +static +void flecs_pipeline_stats_repeat_last( + void* stats) +{ + ecs_pipeline_stats_repeat_last(stats); +} + +static +void flecs_pipeline_stats_fini( + void *stats) +{ + ecs_pipeline_stats_fini(stats); +} + +void FlecsPipelineMonitorImport( + ecs_world_t *world) +{ + ECS_COMPONENT_DEFINE(world, EcsPipelineStats); + + ecs_set_hooks(world, EcsPipelineStats, { + .ctor = ecs_ctor(EcsPipelineStats), + .copy = ecs_copy(EcsPipelineStats), + .move = ecs_move(EcsPipelineStats), + .dtor = ecs_dtor(EcsPipelineStats) + }); + + ecs_stats_api_t api = { + .copy_last = flecs_pipeline_stats_copy_last, + .get = flecs_pipeline_stats_get, + .reduce = flecs_pipeline_stats_reduce, + .reduce_last = flecs_pipeline_stats_reduce_last, + .repeat_last = flecs_pipeline_stats_repeat_last, + .set_t = flecs_pipeline_stats_set_t, + .fini = flecs_pipeline_stats_fini, + .stats_size = ECS_SIZEOF(ecs_pipeline_stats_t), + .monitor_component_id = ecs_id(EcsPipelineStats), + .query_component_id = ecs_id(EcsPipeline) + }; + + flecs_stats_api_import(world, &api); +} + +#endif diff --git a/vendors/flecs/src/addons/stats.c b/vendors/flecs/src/addons/stats/stats.c similarity index 85% rename from vendors/flecs/src/addons/stats.c rename to vendors/flecs/src/addons/stats/stats.c index 3484634e0..7da6f8270 100644 --- a/vendors/flecs/src/addons/stats.c +++ b/vendors/flecs/src/addons/stats/stats.c @@ -3,15 +3,10 @@ * @brief Stats addon. */ -#include "../private_api.h" +#include "stats.h" -#ifdef FLECS_SYSTEM -#include "../addons/system/system.h" -#endif - -#ifdef FLECS_PIPELINE -#include "../addons/pipeline/pipeline.h" -#endif +#include "../system/system.h" +#include "../pipeline/pipeline.h" #ifdef FLECS_STATS @@ -302,7 +297,7 @@ void ecs_world_stats_get( ECS_COUNTER_RECORD(&s->commands.delete_count, t, world->info.cmd.delete_count); ECS_COUNTER_RECORD(&s->commands.clear_count, t, world->info.cmd.clear_count); ECS_COUNTER_RECORD(&s->commands.set_count, t, world->info.cmd.set_count); - ECS_COUNTER_RECORD(&s->commands.get_mut_count, t, world->info.cmd.get_mut_count); + ECS_COUNTER_RECORD(&s->commands.ensure_count, t, world->info.cmd.ensure_count); ECS_COUNTER_RECORD(&s->commands.modified_count, t, world->info.cmd.modified_count); ECS_COUNTER_RECORD(&s->commands.other_count, t, world->info.cmd.other_count); ECS_COUNTER_RECORD(&s->commands.discard_count, t, world->info.cmd.discard_count); @@ -379,31 +374,21 @@ void ecs_query_stats_get( const ecs_query_t *query, ecs_query_stats_t *s) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL); (void)world; - int32_t t = s->t = t_next(s->t); - - if (query->filter.flags & EcsFilterMatchThis) { - ECS_GAUGE_RECORD(&s->matched_entity_count, t, - ecs_query_entity_count(query)); - ECS_GAUGE_RECORD(&s->matched_table_count, t, - ecs_query_table_count(query)); - ECS_GAUGE_RECORD(&s->matched_empty_table_count, t, - ecs_query_empty_table_count(query)); - } else { - ECS_GAUGE_RECORD(&s->matched_entity_count, t, 0); - ECS_GAUGE_RECORD(&s->matched_table_count, t, 0); - ECS_GAUGE_RECORD(&s->matched_empty_table_count, t, 0); - } + int32_t t = s->t = t_next(s->t); + ecs_query_count_t counts = ecs_query_count(query); + ECS_GAUGE_RECORD(&s->result_count, t, counts.results); + ECS_GAUGE_RECORD(&s->matched_table_count, t, counts.tables); + ECS_GAUGE_RECORD(&s->matched_entity_count, t, counts.entities); error: return; } -void ecs_query_stats_reduce( +void ecs_query_cache_stats_reduce( ecs_query_stats_t *dst, const ecs_query_stats_t *src) { @@ -411,7 +396,7 @@ void ecs_query_stats_reduce( ECS_METRIC_FIRST(src), (dst->t = t_next(dst->t)), src->t); } -void ecs_query_stats_reduce_last( +void ecs_query_cache_stats_reduce_last( ecs_query_stats_t *dst, const ecs_query_stats_t *src, int32_t count) @@ -420,14 +405,14 @@ void ecs_query_stats_reduce_last( ECS_METRIC_FIRST(src), (dst->t = t_prev(dst->t)), src->t, count); } -void ecs_query_stats_repeat_last( +void ecs_query_cache_stats_repeat_last( ecs_query_stats_t *stats) { flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats), (stats->t = t_next(stats->t))); } -void ecs_query_stats_copy_last( +void ecs_query_cache_stats_copy_last( ecs_query_stats_t *dst, const ecs_query_stats_t *src) { @@ -448,7 +433,7 @@ bool ecs_system_stats_get( world = ecs_get_world(world); - const ecs_system_t *ptr = ecs_poly_get(world, system, ecs_system_t); + const ecs_system_t *ptr = flecs_poly_get(world, system, ecs_system_t); if (!ptr) { return false; } @@ -457,9 +442,8 @@ bool ecs_system_stats_get( int32_t t = s->query.t; ECS_COUNTER_RECORD(&s->time_spent, t, ptr->time_spent); - ECS_COUNTER_RECORD(&s->invoke_count, t, ptr->invoke_count); - s->task = !(ptr->query->filter.flags & EcsFilterMatchThis); + s->task = !(ptr->query->flags & EcsQueryMatchThis); return true; error: @@ -470,7 +454,7 @@ void ecs_system_stats_reduce( ecs_system_stats_t *dst, const ecs_system_stats_t *src) { - ecs_query_stats_reduce(&dst->query, &src->query); + ecs_query_cache_stats_reduce(&dst->query, &src->query); dst->task = src->task; flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), ECS_METRIC_FIRST(src), dst->query.t, src->query.t); @@ -481,7 +465,7 @@ void ecs_system_stats_reduce_last( const ecs_system_stats_t *src, int32_t count) { - ecs_query_stats_reduce_last(&dst->query, &src->query, count); + ecs_query_cache_stats_reduce_last(&dst->query, &src->query, count); dst->task = src->task; flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), ECS_METRIC_FIRST(src), dst->query.t, src->query.t, count); @@ -490,7 +474,7 @@ void ecs_system_stats_reduce_last( void ecs_system_stats_repeat_last( ecs_system_stats_t *stats) { - ecs_query_stats_repeat_last(&stats->query); + ecs_query_cache_stats_repeat_last(&stats->query); flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats), (stats->query.t)); } @@ -499,7 +483,7 @@ void ecs_system_stats_copy_last( ecs_system_stats_t *dst, const ecs_system_stats_t *src) { - ecs_query_stats_copy_last(&dst->query, &src->query); + ecs_query_cache_stats_copy_last(&dst->query, &src->query); dst->task = src->task; flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst), ECS_METRIC_FIRST(src), dst->query.t, t_next(src->query.t)); @@ -553,11 +537,6 @@ bool ecs_pipeline_stats_get( return false; } - if (ecs_map_is_init(&s->system_stats) && !sys_count) { - ecs_map_fini(&s->system_stats); - } - ecs_map_init_if(&s->system_stats, NULL); - if (op) { ecs_entity_t *systems = NULL; if (pip_count) { @@ -609,24 +588,11 @@ bool ecs_pipeline_stats_get( el->system_count = cur->count; el->multi_threaded = cur->multi_threaded; - el->no_readonly = cur->no_readonly; + el->immediate = cur->immediate; } } } - /* Separately populate system stats map from build query, which includes - * systems that aren't currently active */ - it = ecs_query_iter(stage, pq->query); - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - ecs_system_stats_t *stats = ecs_map_ensure_alloc_t(&s->system_stats, - ecs_system_stats_t, it.entities[i]); - stats->query.t = s->t; - ecs_system_stats_get(world, it.entities[i], stats); - } - } - s->t = t_next(s->t); return true; @@ -637,12 +603,6 @@ bool ecs_pipeline_stats_get( void ecs_pipeline_stats_fini( ecs_pipeline_stats_t *stats) { - ecs_map_iter_t it = ecs_map_iter(&stats->system_stats); - while (ecs_map_next(&it)) { - ecs_system_stats_t *elem = ecs_map_ptr(&it); - ecs_os_free(elem); - } - ecs_map_fini(&stats->system_stats); ecs_vec_fini_t(NULL, &stats->systems, ecs_entity_t); ecs_vec_fini_t(NULL, &stats->sync_points, ecs_sync_stats_t); } @@ -670,19 +630,9 @@ void ecs_pipeline_stats_reduce( ECS_METRIC_FIRST(src_el), dst->t, src->t); dst_el->system_count = src_el->system_count; dst_el->multi_threaded = src_el->multi_threaded; - dst_el->no_readonly = src_el->no_readonly; + dst_el->immediate = src_el->immediate; } - ecs_map_init_if(&dst->system_stats, NULL); - ecs_map_iter_t it = ecs_map_iter(&src->system_stats); - - while (ecs_map_next(&it)) { - ecs_system_stats_t *sys_src = ecs_map_ptr(&it); - ecs_system_stats_t *sys_dst = ecs_map_ensure_alloc_t(&dst->system_stats, - ecs_system_stats_t, ecs_map_key(&it)); - sys_dst->query.t = dst->t; - ecs_system_stats_reduce(sys_dst, sys_src); - } dst->t = t_next(dst->t); } @@ -702,18 +652,9 @@ void ecs_pipeline_stats_reduce_last( ECS_METRIC_FIRST(src_el), dst->t, src->t, count); dst_el->system_count = src_el->system_count; dst_el->multi_threaded = src_el->multi_threaded; - dst_el->no_readonly = src_el->no_readonly; + dst_el->immediate = src_el->immediate; } - ecs_map_init_if(&dst->system_stats, NULL); - ecs_map_iter_t it = ecs_map_iter(&src->system_stats); - while (ecs_map_next(&it)) { - ecs_system_stats_t *sys_src = ecs_map_ptr(&it); - ecs_system_stats_t *sys_dst = ecs_map_ensure_alloc_t(&dst->system_stats, - ecs_system_stats_t, ecs_map_key(&it)); - sys_dst->query.t = dst->t; - ecs_system_stats_reduce_last(sys_dst, sys_src, count); - } dst->t = t_prev(dst->t); } @@ -729,12 +670,6 @@ void ecs_pipeline_stats_repeat_last( (stats->t)); } - ecs_map_iter_t it = ecs_map_iter(&stats->system_stats); - while (ecs_map_next(&it)) { - ecs_system_stats_t *sys = ecs_map_ptr(&it); - sys->query.t = stats->t; - ecs_system_stats_repeat_last(sys); - } stats->t = t_next(stats->t); } @@ -755,18 +690,7 @@ void ecs_pipeline_stats_copy_last( ECS_METRIC_FIRST(src_el), dst->t, t_next(src->t)); dst_el->system_count = src_el->system_count; dst_el->multi_threaded = src_el->multi_threaded; - dst_el->no_readonly = src_el->no_readonly; - } - - ecs_map_init_if(&dst->system_stats, NULL); - - ecs_map_iter_t it = ecs_map_iter(&src->system_stats); - while (ecs_map_next(&it)) { - ecs_system_stats_t *sys_src = ecs_map_ptr(&it); - ecs_system_stats_t *sys_dst = ecs_map_ensure_alloc_t(&dst->system_stats, - ecs_system_stats_t, ecs_map_key(&it)); - sys_dst->query.t = dst->t; - ecs_system_stats_copy_last(sys_dst, sys_src); + dst_el->immediate = src_el->immediate; } } @@ -788,8 +712,8 @@ void ecs_world_stats_log( flecs_counter_print("pipeline rebuilds", t, &s->frame.pipeline_build_count); flecs_counter_print("systems ran", t, &s->frame.systems_ran); ecs_trace(""); - flecs_metric_print("target FPS", world->info.target_fps); - flecs_metric_print("time scale", world->info.time_scale); + flecs_metric_print("target FPS", (ecs_float_t)world->info.target_fps); + flecs_metric_print("time scale", (ecs_float_t)world->info.time_scale); ecs_trace(""); flecs_gauge_print("actual FPS", t, &s->performance.fps); flecs_counter_print("frame time", t, &s->performance.frame_time); @@ -821,7 +745,7 @@ void ecs_world_stats_log( flecs_counter_print("delete commands", t, &s->commands.delete_count); flecs_counter_print("clear commands", t, &s->commands.clear_count); flecs_counter_print("set commands", t, &s->commands.set_count); - flecs_counter_print("get_mut commands", t, &s->commands.get_mut_count); + flecs_counter_print("ensure commands", t, &s->commands.ensure_count); flecs_counter_print("modified commands", t, &s->commands.modified_count); flecs_counter_print("other commands", t, &s->commands.other_count); flecs_counter_print("discarded commands", t, &s->commands.discard_count); diff --git a/vendors/flecs/src/addons/stats/stats.h b/vendors/flecs/src/addons/stats/stats.h new file mode 100644 index 000000000..80f0ec549 --- /dev/null +++ b/vendors/flecs/src/addons/stats/stats.h @@ -0,0 +1,47 @@ +/** + * @file addons/stats/stats.h + * @brief Internal functions/types for stats addon. + */ + +#ifndef FLECS_STATS_PRIVATE_H +#define FLECS_STATS_PRIVATE_H + +#include "../../private_api.h" + +typedef struct { + /* Statistics API interface */ + void (*copy_last)(void *stats, void *src); + void (*get)(ecs_world_t *world, ecs_entity_t res, void *stats); + void (*reduce)(void *stats, void *src); + void (*reduce_last)(void *stats, void *last, int32_t reduce_count); + void (*repeat_last)(void* stats); + void (*set_t)(void *stats, int32_t t); + void (*fini)(void *stats); + + /* Size of statistics type */ + ecs_size_t stats_size; + + /* Id of component that contains the statistics */ + ecs_entity_t monitor_component_id; + + /* Id of component used to query for monitored resources (optional) */ + ecs_id_t query_component_id; +} ecs_stats_api_t; + +void flecs_stats_api_import( + ecs_world_t *world, + ecs_stats_api_t *api); + +void FlecsWorldSummaryImport( + ecs_world_t *world); + +void FlecsWorldMonitorImport( + ecs_world_t *world); + +void FlecsSystemMonitorImport( + ecs_world_t *world); + +void FlecsPipelineMonitorImport( + ecs_world_t *world); + +#endif diff --git a/vendors/flecs/src/addons/stats/system_monitor.c b/vendors/flecs/src/addons/stats/system_monitor.c new file mode 100644 index 000000000..9bed52813 --- /dev/null +++ b/vendors/flecs/src/addons/stats/system_monitor.c @@ -0,0 +1,122 @@ +/** + * @file addons/stats/system_monitor.c + * @brief Stats addon system monitor + */ + +#include "flecs.h" +#include "stats.h" +#include "../pipeline/pipeline.h" + +#ifdef FLECS_STATS + +ECS_COMPONENT_DECLARE(EcsSystemStats); + +static +void flecs_system_monitor_dtor(EcsSystemStats *ptr) { + ecs_map_iter_t it = ecs_map_iter(&ptr->stats); + while (ecs_map_next(&it)) { + ecs_system_stats_t *stats = ecs_map_ptr(&it); + ecs_os_free(stats); + } + ecs_map_fini(&ptr->stats); +} + +static ECS_CTOR(EcsSystemStats, ptr, { + ecs_os_zeromem(ptr); + ecs_map_init(&ptr->stats, NULL); +}) + +static ECS_COPY(EcsSystemStats, dst, src, { + (void)dst; + (void)src; + ecs_abort(ECS_INVALID_OPERATION, "cannot copy system stats component"); +}) + +static ECS_MOVE(EcsSystemStats, dst, src, { + flecs_system_monitor_dtor(dst); + ecs_os_memcpy_t(dst, src, EcsSystemStats); + ecs_os_zeromem(src); +}) + +static ECS_DTOR(EcsSystemStats, ptr, { + flecs_system_monitor_dtor(ptr); +}) + +static +void flecs_system_stats_set_t( + void *stats, int32_t t) +{ + ecs_assert(t >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(t < ECS_STAT_WINDOW, ECS_INTERNAL_ERROR, NULL); + ((ecs_system_stats_t*)stats)->query.t = t; +} + +static +void flecs_system_stats_copy_last( + void *stats, + void *src) +{ + ecs_system_stats_copy_last(stats, src); +} + +static +void flecs_system_stats_get( + ecs_world_t *world, + ecs_entity_t res, + void *stats) +{ + ecs_system_stats_get(world, res, stats); +} + +static +void flecs_system_stats_reduce( + void *stats, + void *src) +{ + ecs_system_stats_reduce(stats, src); +} + +static +void flecs_system_stats_reduce_last( + void *stats, + void *last, + int32_t reduce_count) +{ + ecs_system_stats_reduce_last(stats, last, reduce_count); +} + +static +void flecs_system_stats_repeat_last( + void* stats) +{ + ecs_system_stats_repeat_last(stats); +} + +void FlecsSystemMonitorImport( + ecs_world_t *world) +{ + ECS_COMPONENT_DEFINE(world, EcsSystemStats); + + ecs_set_hooks(world, EcsSystemStats, { + .ctor = ecs_ctor(EcsSystemStats), + .copy = ecs_copy(EcsSystemStats), + .move = ecs_move(EcsSystemStats), + .dtor = ecs_dtor(EcsSystemStats) + }); + + ecs_stats_api_t api = { + .copy_last = flecs_system_stats_copy_last, + .get = flecs_system_stats_get, + .reduce = flecs_system_stats_reduce, + .reduce_last = flecs_system_stats_reduce_last, + .repeat_last = flecs_system_stats_repeat_last, + .set_t = flecs_system_stats_set_t, + .stats_size = ECS_SIZEOF(ecs_system_stats_t), + .monitor_component_id = ecs_id(EcsSystemStats), + .query_component_id = EcsSystem + }; + + flecs_stats_api_import(world, &api); +} + +#endif diff --git a/vendors/flecs/src/addons/stats/world_monitor.c b/vendors/flecs/src/addons/stats/world_monitor.c new file mode 100644 index 000000000..225a8965f --- /dev/null +++ b/vendors/flecs/src/addons/stats/world_monitor.c @@ -0,0 +1,86 @@ +/** + * @file addons/stats/world_monitor.c + * @brief Stats addon world monitor. + */ + +#include "flecs.h" +#include "stats.h" + +#ifdef FLECS_STATS + +ECS_COMPONENT_DECLARE(EcsWorldStats); + +static +void flecs_world_stats_get( + ecs_world_t *world, ecs_entity_t res, void *stats) +{ + (void)res; + ecs_world_stats_get(world, stats); +} + +static +void flecs_world_stats_set_t( + void *stats, int32_t t) +{ + ecs_assert(t >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(t < ECS_STAT_WINDOW, ECS_INTERNAL_ERROR, NULL); + ((ecs_world_stats_t*)stats)->t = t; +} + +static +void flecs_world_stats_copy_last( + void *stats, + void *src) +{ + ecs_world_stats_copy_last(stats, src); +} + +static +void flecs_world_stats_reduce( + void *stats, + void *src) +{ + ecs_world_stats_reduce(stats, src); +} + +static +void flecs_world_stats_reduce_last( + void *stats, + void *last, + int32_t reduce_count) +{ + ecs_world_stats_reduce_last(stats, last, reduce_count); +} + +static +void flecs_world_stats_repeat_last( + void* stats) +{ + ecs_world_stats_repeat_last(stats); +} + +void FlecsWorldMonitorImport( + ecs_world_t *world) +{ + ECS_COMPONENT_DEFINE(world, EcsWorldStats); + + ecs_set_hooks(world, EcsWorldStats, { + .ctor = flecs_default_ctor + }); + + ecs_stats_api_t api = { + .copy_last = flecs_world_stats_copy_last, + .get = flecs_world_stats_get, + .reduce = flecs_world_stats_reduce, + .reduce_last = flecs_world_stats_reduce_last, + .repeat_last = flecs_world_stats_repeat_last, + .set_t = flecs_world_stats_set_t, + .fini = NULL, + .stats_size = ECS_SIZEOF(ecs_world_stats_t), + .monitor_component_id = ecs_id(EcsWorldStats) + }; + + flecs_stats_api_import(world, &api); +} + +#endif diff --git a/vendors/flecs/src/addons/stats/world_summary.c b/vendors/flecs/src/addons/stats/world_summary.c new file mode 100644 index 000000000..cb856de2b --- /dev/null +++ b/vendors/flecs/src/addons/stats/world_summary.c @@ -0,0 +1,122 @@ +/** + * @file addons/world_summary.c + * @brief Monitor addon. + */ + +#include "flecs.h" +#include "stats.h" + +#ifdef FLECS_STATS + +ECS_COMPONENT_DECLARE(EcsWorldSummary); + +static +void flecs_copy_world_summary( + ecs_world_t *world, + EcsWorldSummary *dst) +{ + const ecs_world_info_t *info = ecs_get_world_info(world); + + dst->target_fps = (double)info->target_fps; + dst->time_scale = (double)info->time_scale; + + dst->frame_time_last = (double)info->frame_time_total - dst->frame_time_total; + dst->system_time_last = (double)info->system_time_total - dst->system_time_total; + dst->merge_time_last = (double)info->merge_time_total - dst->merge_time_total; + + dst->frame_time_total = (double)info->frame_time_total; + dst->system_time_total = (double)info->system_time_total; + dst->merge_time_total = (double)info->merge_time_total; + + dst->frame_count ++; + dst->command_count += + info->cmd.add_count + + info->cmd.remove_count + + info->cmd.delete_count + + info->cmd.clear_count + + info->cmd.set_count + + info->cmd.ensure_count + + info->cmd.modified_count + + info->cmd.discard_count + + info->cmd.event_count + + info->cmd.other_count; + + dst->build_info = *ecs_get_build_info(); +} + +static +void UpdateWorldSummary(ecs_iter_t *it) { + EcsWorldSummary *summary = ecs_field(it, EcsWorldSummary, 0); + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + flecs_copy_world_summary(it->world, &summary[i]); + } +} + +static +void OnSetWorldSummary(ecs_iter_t *it) { + EcsWorldSummary *summary = ecs_field(it, EcsWorldSummary, 0); + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_set_target_fps(it->world, (ecs_ftime_t)summary[i].target_fps); + ecs_set_time_scale(it->world, (ecs_ftime_t)summary[i].time_scale); + } +} + +void FlecsWorldSummaryImport( + ecs_world_t *world) +{ + ECS_COMPONENT_DEFINE(world, EcsWorldSummary); + +#if defined(FLECS_META) && defined(FLECS_UNITS) + ecs_entity_t build_info = ecs_lookup(world, "flecs.core.build_info_t"); + ecs_struct(world, { + .entity = ecs_id(EcsWorldSummary), + .members = { + { .name = "target_fps", .type = ecs_id(ecs_f64_t), .unit = EcsHertz }, + { .name = "time_scale", .type = ecs_id(ecs_f64_t) }, + { .name = "frame_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "system_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "merge_time_total", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "frame_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "system_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "merge_time_last", .type = ecs_id(ecs_f64_t), .unit = EcsSeconds }, + { .name = "frame_count", .type = ecs_id(ecs_u64_t) }, + { .name = "command_count", .type = ecs_id(ecs_u64_t) }, + { .name = "build_info", .type = build_info } + } + }); +#endif + const ecs_world_info_t *info = ecs_get_world_info(world); + + ecs_system(world, { + .entity = ecs_entity(world, { + .name = "UpdateWorldSummary", + .add = ecs_ids(ecs_pair(EcsDependsOn, EcsPreFrame)) + }), + .query.terms = {{ .id = ecs_id(EcsWorldSummary) }}, + .callback = UpdateWorldSummary + }); + + ecs_observer(world, { + .entity = ecs_entity(world, { + .name = "OnSetWorldSummary" + }), + .events = { EcsOnSet }, + .query.terms = {{ .id = ecs_id(EcsWorldSummary) }}, + .callback = OnSetWorldSummary + }); + + ecs_set(world, EcsWorld, EcsWorldSummary, { + .target_fps = (double)info->target_fps, + .time_scale = (double)info->time_scale + }); + + EcsWorldSummary *summary = ecs_ensure(world, EcsWorld, EcsWorldSummary); + flecs_copy_world_summary(world, summary); + ecs_modified(world, EcsWorld, EcsWorldSummary); +} + +#endif diff --git a/vendors/flecs/src/addons/system/system.c b/vendors/flecs/src/addons/system/system.c index 6deb9a0da..55216554d 100644 --- a/vendors/flecs/src/addons/system/system.c +++ b/vendors/flecs/src/addons/system/system.c @@ -21,7 +21,7 @@ ecs_mixins_t ecs_system_t_mixins = { /* -- Public API -- */ -ecs_entity_t ecs_run_intern( +ecs_entity_t flecs_run_intern( ecs_world_t *world, ecs_stage_t *stage, ecs_entity_t system, @@ -29,8 +29,6 @@ ecs_entity_t ecs_run_intern( int32_t stage_index, int32_t stage_count, ecs_ftime_t delta_time, - int32_t offset, - int32_t limit, void *param) { ecs_ftime_t time_elapsed = delta_time; @@ -61,8 +59,10 @@ ecs_entity_t ecs_run_intern( } } + ecs_os_perf_trace_push(system_data->name); + if (ecs_should_log_3()) { - char *path = ecs_get_fullpath(world, system); + char *path = ecs_get_path(world, system); ecs_dbg_3("worker %d: %s", stage_index, path); ecs_os_free(path); } @@ -77,82 +77,76 @@ ecs_entity_t ecs_run_intern( if (stage) { thread_ctx = stage->thread_ctx; } else { - stage = &world->stages[0]; + stage = world->stages[0]; } /* Prepare the query iterator */ - ecs_iter_t pit, wit, qit = ecs_query_iter(thread_ctx, system_data->query); + ecs_iter_t wit, qit = ecs_query_iter(thread_ctx, system_data->query); ecs_iter_t *it = &qit; qit.system = system; qit.delta_time = delta_time; qit.delta_system_time = time_elapsed; - qit.frame_offset = offset; qit.param = param; qit.ctx = system_data->ctx; - qit.binding_ctx = system_data->binding_ctx; + qit.callback_ctx = system_data->callback_ctx; + qit.run_ctx = system_data->run_ctx; flecs_defer_begin(world, stage); - if (offset || limit) { - pit = ecs_page_iter(it, offset, limit); - it = &pit; - } - if (stage_count > 1 && system_data->multi_threaded) { wit = ecs_worker_iter(it, stage_index, stage_count); it = &wit; } + ecs_entity_t old_system = flecs_stage_set_system(stage, system); ecs_iter_action_t action = system_data->action; it->callback = action; - + ecs_run_action_t run = system_data->run; if (run) { - run(it); - } else if (system_data->query->filter.term_count) { - if (it == &qit) { - while (ecs_query_next(&qit)) { - action(&qit); - } + /* If system query matches nothing, the system run callback doesn't have + * anything to iterate, so the iterator resources don't get cleaned up + * automatically, so clean it up here. */ + if (system_data->query->flags & EcsQueryMatchNothing) { + it->next = flecs_default_next_callback; /* Return once */ + run(it); + ecs_iter_fini(&qit); } else { - while (ecs_iter_next(it)) { - action(it); - } + run(it); } } else { - action(&qit); - ecs_iter_fini(&qit); + if (system_data->query->term_count) { + if (it == &qit) { + while (ecs_query_next(&qit)) { + action(&qit); + } + } else { + while (ecs_iter_next(it)) { + action(it); + } + } + } else { + action(&qit); + ecs_iter_fini(&qit); + } } + flecs_stage_set_system(stage, old_system); + if (measure_time) { system_data->time_spent += (ecs_ftime_t)ecs_time_measure(&time_start); } - system_data->invoke_count ++; - flecs_defer_end(world, stage); + ecs_os_perf_trace_pop(system_data->name); + return it->interrupted_by; } /* -- Public API -- */ -ecs_entity_t ecs_run_w_filter( - ecs_world_t *world, - ecs_entity_t system, - ecs_ftime_t delta_time, - int32_t offset, - int32_t limit, - void *param) -{ - ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_system_t *system_data = ecs_poly_get(world, system, ecs_system_t); - ecs_assert(system_data != NULL, ECS_INVALID_PARAMETER, NULL); - return ecs_run_intern(world, stage, system, system_data, 0, 0, delta_time, - offset, limit, param); -} - ecs_entity_t ecs_run_worker( ecs_world_t *world, ecs_entity_t system, @@ -162,12 +156,12 @@ ecs_entity_t ecs_run_worker( void *param) { ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_system_t *system_data = ecs_poly_get(world, system, ecs_system_t); + ecs_system_t *system_data = flecs_poly_get(world, system, ecs_system_t); ecs_assert(system_data != NULL, ECS_INVALID_PARAMETER, NULL); - return ecs_run_intern( + return flecs_run_intern( world, stage, system, system_data, stage_index, stage_count, - delta_time, 0, 0, param); + delta_time, param); } ecs_entity_t ecs_run( @@ -176,43 +170,11 @@ ecs_entity_t ecs_run( ecs_ftime_t delta_time, void *param) { - return ecs_run_w_filter(world, system, delta_time, 0, 0, param); -} - -ecs_query_t* ecs_system_get_query( - const ecs_world_t *world, - ecs_entity_t system) -{ - const ecs_system_t *s = ecs_poly_get(world, system, ecs_system_t); - if (s) { - return s->query; - } else { - return NULL; - } -} - -void* ecs_system_get_ctx( - const ecs_world_t *world, - ecs_entity_t system) -{ - const ecs_system_t *s = ecs_poly_get(world, system, ecs_system_t); - if (s) { - return s->ctx; - } else { - return NULL; - } -} - -void* ecs_system_get_binding_ctx( - const ecs_world_t *world, - ecs_entity_t system) -{ - const ecs_system_t *s = ecs_poly_get(world, system, ecs_system_t); - if (s) { - return s->binding_ctx; - } else { - return NULL; - } + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_system_t *system_data = flecs_poly_get(world, system, ecs_system_t); + ecs_assert(system_data != NULL, ECS_INVALID_PARAMETER, NULL); + return flecs_run_intern( + world, stage, system, system_data, 0, 0, delta_time, param); } /* System deinitialization */ @@ -222,19 +184,40 @@ void flecs_system_fini(ecs_system_t *sys) { sys->ctx_free(sys->ctx); } - if (sys->binding_ctx_free) { - sys->binding_ctx_free(sys->binding_ctx); + if (sys->callback_ctx_free) { + sys->callback_ctx_free(sys->callback_ctx); + } + + if (sys->run_ctx_free) { + sys->run_ctx_free(sys->run_ctx); } - ecs_poly_free(sys, ecs_system_t); + /* Safe cast, type owns name */ + ecs_os_free(ECS_CONST_CAST(char*, sys->name)); + + flecs_poly_free(sys, ecs_system_t); +} + +/* ecs_poly_dtor_t-compatible wrapper */ +static +void flecs_system_poly_fini(void *sys) +{ + flecs_system_fini(sys); } static -void flecs_system_init_timer( +int flecs_system_init_timer( ecs_world_t *world, ecs_entity_t entity, const ecs_system_desc_t *desc) { + if (ECS_NEQZERO(desc->interval) && ECS_NEQZERO(desc->rate)) { + char *name = ecs_get_path(world, entity); + ecs_err("system %s cannot have both interval and rate set", name); + ecs_os_free(name); + return -1; + } + if (ECS_NEQZERO(desc->interval) || ECS_NEQZERO(desc->rate) || ECS_NEQZERO(desc->tick_source)) { @@ -245,9 +228,6 @@ void flecs_system_init_timer( if (desc->rate) { ecs_entity_t tick_source = desc->tick_source; - if (!tick_source) { - tick_source = entity; - } ecs_set_rate(world, entity, desc->rate, tick_source); } else if (desc->tick_source) { ecs_set_tick_source(world, entity, desc->tick_source); @@ -258,34 +238,38 @@ void flecs_system_init_timer( ecs_abort(ECS_UNSUPPORTED, "timer module not available"); #endif } + + return 0; } ecs_entity_t ecs_system_init( ecs_world_t *world, const ecs_system_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_system_desc_t was not initialized to zero"); ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_WHILE_READONLY, NULL); ecs_entity_t entity = desc->entity; if (!entity) { - entity = ecs_new(world, 0); + entity = ecs_entity(world, {0}); } - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_system_t); + + EcsPoly *poly = flecs_poly_bind(world, entity, ecs_system_t); if (!poly->poly) { - ecs_system_t *system = ecs_poly_new(ecs_system_t); + ecs_system_t *system = flecs_poly_new(ecs_system_t); ecs_assert(system != NULL, ECS_INTERNAL_ERROR, NULL); poly->poly = system; system->world = world; - system->dtor = (ecs_poly_dtor_t)flecs_system_fini; + system->dtor = flecs_system_poly_fini; system->entity = entity; ecs_query_desc_t query_desc = desc->query; - query_desc.filter.entity = entity; + query_desc.entity = entity; ecs_query_t *query = ecs_query_init(world, &query_desc); if (!query) { @@ -294,91 +278,141 @@ ecs_entity_t ecs_system_init( } /* Prevent the system from moving while we're initializing */ - flecs_defer_begin(world, &world->stages[0]); + flecs_defer_begin(world, world->stages[0]); system->query = query; - system->query_entity = query->filter.entity; + system->query_entity = query->entity; system->run = desc->run; system->action = desc->callback; system->ctx = desc->ctx; - system->binding_ctx = desc->binding_ctx; + system->callback_ctx = desc->callback_ctx; + system->run_ctx = desc->run_ctx; system->ctx_free = desc->ctx_free; - system->binding_ctx_free = desc->binding_ctx_free; + system->callback_ctx_free = desc->callback_ctx_free; + system->run_ctx_free = desc->run_ctx_free; system->tick_source = desc->tick_source; system->multi_threaded = desc->multi_threaded; - system->no_readonly = desc->no_readonly; + system->immediate = desc->immediate; - flecs_system_init_timer(world, entity, desc); + system->name = ecs_get_path(world, entity); + + if (flecs_system_init_timer(world, entity, desc)) { + ecs_delete(world, entity); + ecs_defer_end(world); + goto error; + } if (ecs_get_name(world, entity)) { ecs_trace("#[green]system#[reset] %s created", ecs_get_name(world, entity)); } - ecs_defer_end(world); + ecs_defer_end(world); } else { - ecs_poly_assert(poly->poly, ecs_system_t); + flecs_poly_assert(poly->poly, ecs_system_t); ecs_system_t *system = (ecs_system_t*)poly->poly; - if (desc->run) { - system->run = desc->run; - } - if (desc->callback) { - system->action = desc->callback; - } - if (system->ctx_free) { if (system->ctx && system->ctx != desc->ctx) { system->ctx_free(system->ctx); } } - if (system->binding_ctx_free) { - if (system->binding_ctx && system->binding_ctx != desc->binding_ctx) { - system->binding_ctx_free(system->binding_ctx); + + if (system->callback_ctx_free) { + if (system->callback_ctx && system->callback_ctx != desc->callback_ctx) { + system->callback_ctx_free(system->callback_ctx); + system->callback_ctx_free = NULL; + system->callback_ctx = NULL; + } + } + + if (system->run_ctx_free) { + if (system->run_ctx && system->run_ctx != desc->run_ctx) { + system->run_ctx_free(system->run_ctx); + system->run_ctx_free = NULL; + system->run_ctx = NULL; + } + } + + if (desc->run) { + system->run = desc->run; + if (!desc->callback) { + system->action = NULL; + } + } + + if (desc->callback) { + system->action = desc->callback; + if (!desc->run) { + system->run = NULL; } } if (desc->ctx) { system->ctx = desc->ctx; } - if (desc->binding_ctx) { - system->binding_ctx = desc->binding_ctx; + + if (desc->callback_ctx) { + system->callback_ctx = desc->callback_ctx; } + + if (desc->run_ctx) { + system->run_ctx = desc->run_ctx; + } + if (desc->ctx_free) { system->ctx_free = desc->ctx_free; } - if (desc->binding_ctx_free) { - system->binding_ctx_free = desc->binding_ctx_free; + + if (desc->callback_ctx_free) { + system->callback_ctx_free = desc->callback_ctx_free; } - if (desc->query.filter.instanced) { - ECS_BIT_SET(system->query->filter.flags, EcsFilterIsInstanced); + + if (desc->run_ctx_free) { + system->run_ctx_free = desc->run_ctx_free; } + if (desc->multi_threaded) { system->multi_threaded = desc->multi_threaded; } - if (desc->no_readonly) { - system->no_readonly = desc->no_readonly; + + if (desc->immediate) { + system->immediate = desc->immediate; } - flecs_system_init_timer(world, entity, desc); + if (flecs_system_init_timer(world, entity, desc)) { + return 0; + } } - ecs_poly_modified(world, entity, ecs_system_t); + flecs_poly_modified(world, entity, ecs_system_t); return entity; error: return 0; } +const ecs_system_t* ecs_system_get( + const ecs_world_t *world, + ecs_entity_t entity) +{ + return flecs_poly_get(world, entity, ecs_system_t); +} + void FlecsSystemImport( ecs_world_t *world) { ECS_MODULE(world, FlecsSystem); +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsSystem), + "Module that implements Flecs systems"); +#endif ecs_set_name_prefix(world, "Ecs"); @@ -388,7 +422,7 @@ void FlecsSystemImport( /* Make sure to never inherit system component. This makes sure that any * term created for the System component will default to 'self' traversal, * which improves efficiency of the query. */ - ecs_add_id(world, EcsSystem, EcsDontInherit); + ecs_add_pair(world, EcsSystem, EcsOnInstantiate, EcsDontInherit); } #endif diff --git a/vendors/flecs/src/addons/system/system.h b/vendors/flecs/src/addons/system/system.h index 7dd91509c..77fbc3388 100644 --- a/vendors/flecs/src/addons/system/system.h +++ b/vendors/flecs/src/addons/system/system.h @@ -15,37 +15,6 @@ extern ecs_mixins_t ecs_system_t_mixins; -typedef struct ecs_system_t { - ecs_header_t hdr; - - ecs_run_action_t run; /* See ecs_system_desc_t */ - ecs_iter_action_t action; /* See ecs_system_desc_t */ - - ecs_query_t *query; /* System query */ - ecs_entity_t query_entity; /* Entity associated with query */ - ecs_entity_t tick_source; /* Tick source associated with system */ - - /* Schedule parameters */ - bool multi_threaded; - bool no_readonly; - - int64_t invoke_count; /* Number of times system is invoked */ - ecs_ftime_t time_spent; /* Time spent on running system */ - ecs_ftime_t time_passed; /* Time passed since last invocation */ - int64_t last_frame; /* Last frame for which the system was considered */ - - void *ctx; /* Userdata for system */ - void *binding_ctx; /* Optional language binding context */ - - ecs_ctx_free_t ctx_free; - ecs_ctx_free_t binding_ctx_free; - - /* Mixins */ - ecs_world_t *world; - ecs_entity_t entity; - ecs_poly_dtor_t dtor; -} ecs_system_t; - /* Invoked when system becomes active / inactive */ void ecs_system_activate( ecs_world_t *world, @@ -54,7 +23,7 @@ void ecs_system_activate( const ecs_system_t *system_data); /* Internal function to run a system */ -ecs_entity_t ecs_run_intern( +ecs_entity_t flecs_run_intern( ecs_world_t *world, ecs_stage_t *stage, ecs_entity_t system, @@ -62,8 +31,6 @@ ecs_entity_t ecs_run_intern( int32_t stage_current, int32_t stage_count, ecs_ftime_t delta_time, - int32_t offset, - int32_t limit, void *param); #endif diff --git a/vendors/flecs/src/addons/timer.c b/vendors/flecs/src/addons/timer.c index 33f71e7f4..af4968d18 100644 --- a/vendors/flecs/src/addons/timer.c +++ b/vendors/flecs/src/addons/timer.c @@ -19,8 +19,8 @@ void AddTickSource(ecs_iter_t *it) { static void ProgressTimers(ecs_iter_t *it) { - EcsTimer *timer = ecs_field(it, EcsTimer, 1); - EcsTickSource *tick_source = ecs_field(it, EcsTickSource, 2); + EcsTimer *timer = ecs_field(it, EcsTimer, 0); + EcsTickSource *tick_source = ecs_field(it, EcsTickSource, 1); ecs_assert(timer != NULL, ECS_INTERNAL_ERROR, NULL); @@ -58,8 +58,8 @@ void ProgressTimers(ecs_iter_t *it) { static void ProgressRateFilters(ecs_iter_t *it) { - EcsRateFilter *filter = ecs_field(it, EcsRateFilter, 1); - EcsTickSource *tick_dst = ecs_field(it, EcsTickSource, 2); + EcsRateFilter *filter = ecs_field(it, EcsRateFilter, 0); + EcsTickSource *tick_dst = ecs_field(it, EcsTickSource, 1); int i; for (i = 0; i < it->count; i ++) { @@ -97,7 +97,7 @@ void ProgressRateFilters(ecs_iter_t *it) { static void ProgressTickSource(ecs_iter_t *it) { - EcsTickSource *tick_src = ecs_field(it, EcsTickSource, 1); + EcsTickSource *tick_src = ecs_field(it, EcsTickSource, 0); /* If tick source has no filters, tick unconditionally */ int i; @@ -114,13 +114,17 @@ ecs_entity_t ecs_set_timeout( { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - timer = ecs_set(world, timer, EcsTimer, { + if (!timer) { + timer = ecs_entity(world, {0}); + } + + ecs_set(world, timer, EcsTimer, { .timeout = timeout, .single_shot = true, .active = true }); - ecs_system_t *system_data = ecs_poly_get(world, timer, ecs_system_t); + ecs_system_t *system_data = flecs_poly_get(world, timer, ecs_system_t); if (system_data) { system_data->tick_source = timer; } @@ -152,16 +156,16 @@ ecs_entity_t ecs_set_interval( ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); if (!timer) { - timer = ecs_new(world, EcsTimer); + timer = ecs_new_w(world, EcsTimer); } - EcsTimer *t = ecs_get_mut(world, timer, EcsTimer); - ecs_check(t != NULL, ECS_INVALID_PARAMETER, NULL); + EcsTimer *t = ecs_ensure(world, timer, EcsTimer); + ecs_check(t != NULL, ECS_INTERNAL_ERROR, NULL); t->timeout = interval; t->active = true; ecs_modified(world, timer, EcsTimer); - ecs_system_t *system_data = ecs_poly_get(world, timer, ecs_system_t); + ecs_system_t *system_data = flecs_poly_get(world, timer, ecs_system_t); if (system_data) { system_data->tick_source = timer; } @@ -191,8 +195,8 @@ void ecs_start_timer( ecs_world_t *world, ecs_entity_t timer) { - EcsTimer *ptr = ecs_get_mut(world, timer, EcsTimer); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); + EcsTimer *ptr = ecs_ensure(world, timer, EcsTimer); + ecs_check(ptr != NULL, ECS_INTERNAL_ERROR, NULL); ptr->active = true; ptr->time = 0; error: @@ -203,8 +207,8 @@ void ecs_stop_timer( ecs_world_t *world, ecs_entity_t timer) { - EcsTimer *ptr = ecs_get_mut(world, timer, EcsTimer); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); + EcsTimer *ptr = ecs_ensure(world, timer, EcsTimer); + ecs_check(ptr != NULL, ECS_INTERNAL_ERROR, NULL); ptr->active = false; error: return; @@ -214,8 +218,8 @@ void ecs_reset_timer( ecs_world_t *world, ecs_entity_t timer) { - EcsTimer *ptr = ecs_get_mut(world, timer, EcsTimer); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); + EcsTimer *ptr = ecs_ensure(world, timer, EcsTimer); + ecs_check(ptr != NULL, ECS_INTERNAL_ERROR, NULL); ptr->time = 0; error: return; @@ -229,12 +233,16 @@ ecs_entity_t ecs_set_rate( { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - filter = ecs_set(world, filter, EcsRateFilter, { + if (!filter) { + filter = ecs_entity(world, {0}); + } + + ecs_set(world, filter, EcsRateFilter, { .rate = rate, .src = source }); - ecs_system_t *system_data = ecs_poly_get(world, filter, ecs_system_t); + ecs_system_t *system_data = flecs_poly_get(world, filter, ecs_system_t); if (system_data) { system_data->tick_source = filter; } @@ -252,7 +260,7 @@ void ecs_set_tick_source( ecs_check(system != 0, ECS_INVALID_PARAMETER, NULL); ecs_check(tick_source != 0, ECS_INVALID_PARAMETER, NULL); - ecs_system_t *system_data = ecs_poly_get(world, system, ecs_system_t); + ecs_system_t *system_data = flecs_poly_get(world, system, ecs_system_t); ecs_check(system_data != NULL, ECS_INVALID_PARAMETER, NULL); system_data->tick_source = tick_source; @@ -262,7 +270,7 @@ void ecs_set_tick_source( static void RandomizeTimers(ecs_iter_t *it) { - EcsTimer *timer = ecs_field(it, EcsTimer, 1); + EcsTimer *timer = ecs_field(it, EcsTimer, 0); int32_t i; for (i = 0; i < it->count; i ++) { timer[i].time = @@ -275,7 +283,7 @@ void ecs_randomize_timers( { ecs_observer(world, { .entity = ecs_entity(world, { .name = "flecs.timer.RandomizeTimers" }), - .filter.terms = {{ + .query.terms = {{ .id = ecs_id(EcsTimer) }}, .events = {EcsOnSet}, @@ -288,8 +296,12 @@ void FlecsTimerImport( ecs_world_t *world) { ECS_MODULE(world, FlecsTimer); - ECS_IMPORT(world, FlecsPipeline); +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsTimer), + "Module that implements system timers (used by .interval)"); +#endif ecs_set_name_prefix(world, "Ecs"); @@ -297,15 +309,15 @@ void FlecsTimerImport( flecs_bootstrap_component(world, EcsRateFilter); ecs_set_hooks(world, EcsTimer, { - .ctor = ecs_default_ctor + .ctor = flecs_default_ctor }); /* Add EcsTickSource to timers and rate filters */ ecs_system(world, { - .entity = ecs_entity(world, {.name = "AddTickSource", .add = { ecs_dependson(EcsPreFrame) }}), - .query.filter.terms = { - { .id = ecs_id(EcsTimer), .oper = EcsOr, .inout = EcsIn }, - { .id = ecs_id(EcsRateFilter), .oper = EcsAnd, .inout = EcsIn }, + .entity = ecs_entity(world, {.name = "AddTickSource", .add = ecs_ids( ecs_dependson(EcsPreFrame) )}), + .query.terms = { + { .id = ecs_id(EcsTimer), .oper = EcsOr }, + { .id = ecs_id(EcsRateFilter), .oper = EcsAnd }, { .id = ecs_id(EcsTickSource), .oper = EcsNot, .inout = EcsOut} }, .callback = AddTickSource @@ -313,8 +325,8 @@ void FlecsTimerImport( /* Timer handling */ ecs_system(world, { - .entity = ecs_entity(world, {.name = "ProgressTimers", .add = { ecs_dependson(EcsPreFrame)}}), - .query.filter.terms = { + .entity = ecs_entity(world, {.name = "ProgressTimers", .add = ecs_ids( ecs_dependson(EcsPreFrame))}), + .query.terms = { { .id = ecs_id(EcsTimer) }, { .id = ecs_id(EcsTickSource) } }, @@ -323,8 +335,8 @@ void FlecsTimerImport( /* Rate filter handling */ ecs_system(world, { - .entity = ecs_entity(world, {.name = "ProgressRateFilters", .add = { ecs_dependson(EcsPreFrame)}}), - .query.filter.terms = { + .entity = ecs_entity(world, {.name = "ProgressRateFilters", .add = ecs_ids( ecs_dependson(EcsPreFrame))}), + .query.terms = { { .id = ecs_id(EcsRateFilter), .inout = EcsIn }, { .id = ecs_id(EcsTickSource), .inout = EcsOut } }, @@ -333,8 +345,8 @@ void FlecsTimerImport( /* TickSource without a timer or rate filter just increases each frame */ ecs_system(world, { - .entity = ecs_entity(world, { .name = "ProgressTickSource", .add = { ecs_dependson(EcsPreFrame)}}), - .query.filter.terms = { + .entity = ecs_entity(world, { .name = "ProgressTickSource", .add = ecs_ids( ecs_dependson(EcsPreFrame))}), + .query.terms = { { .id = ecs_id(EcsTickSource), .inout = EcsOut }, { .id = ecs_id(EcsRateFilter), .oper = EcsNot }, { .id = ecs_id(EcsTimer), .oper = EcsNot } diff --git a/vendors/flecs/src/addons/units.c b/vendors/flecs/src/addons/units.c index 680f82745..0c0ee549f 100644 --- a/vendors/flecs/src/addons/units.c +++ b/vendors/flecs/src/addons/units.c @@ -11,12 +11,19 @@ void FlecsUnitsImport( ecs_world_t *world) { ECS_MODULE(world, FlecsUnits); + ECS_IMPORT(world, FlecsMeta); + +#ifdef FLECS_DOC + ECS_IMPORT(world, FlecsDoc); + ecs_doc_set_brief(world, ecs_id(FlecsUnits), + "Module with (amongst others) SI units for annotating component members"); +#endif ecs_set_name_prefix(world, "Ecs"); EcsUnitPrefixes = ecs_entity(world, { .name = "prefixes", - .add = { EcsModule } + .add = ecs_ids( EcsModule ) }); /* Initialize unit prefixes */ @@ -810,6 +817,29 @@ void FlecsUnitsImport( }); ecs_set_scope(world, prev_scope); + /* Color */ + + EcsColor = ecs_quantity_init(world, &(ecs_entity_desc_t){ + .name = "Color" }); + prev_scope = ecs_set_scope(world, EcsColor); + EcsColorRgb = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Rgb" }), + .quantity = EcsColor }); + + EcsColorHsl = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Hsl" }), + .quantity = EcsColor }); + + EcsColorCss = ecs_unit_init(world, &(ecs_unit_desc_t){ + .entity = ecs_entity(world, { .name = "Css" }), + .quantity = EcsColor }); + ecs_primitive_init(world, &(ecs_primitive_desc_t){ + .entity = EcsColorCss, + .kind = EcsString + }); + + ecs_set_scope(world, prev_scope); + /* DeciBel */ EcsBel = ecs_unit_init(world, &(ecs_unit_desc_t){ @@ -828,6 +858,8 @@ void FlecsUnitsImport( .kind = EcsF32 }); + /* Frequency */ + EcsFrequency = ecs_quantity_init(world, &(ecs_entity_desc_t){ .name = "Frequency" }); prev_scope = ecs_set_scope(world, EcsFrequency); diff --git a/vendors/flecs/src/bootstrap.c b/vendors/flecs/src/bootstrap.c index 06a64ff87..99518947c 100644 --- a/vendors/flecs/src/bootstrap.c +++ b/vendors/flecs/src/bootstrap.c @@ -48,7 +48,7 @@ static ECS_MOVE(EcsIdentifier, dst, src, { static void ecs_on_set(EcsIdentifier)(ecs_iter_t *it) { - EcsIdentifier *ptr = ecs_field(it, EcsIdentifier, 1); + EcsIdentifier *ptr = ecs_field(it, EcsIdentifier, 0); ecs_world_t *world = it->real_world; ecs_entity_t evt = it->event; @@ -130,7 +130,7 @@ static ECS_COPY(EcsPoly, dst, src, { static ECS_MOVE(EcsPoly, dst, src, { if (dst->poly && (dst->poly != src->poly)) { - ecs_poly_dtor_t *dtor = ecs_get_dtor(dst->poly); + flecs_poly_dtor_t *dtor = ecs_get_dtor(dst->poly); ecs_assert(dtor != NULL, ECS_INTERNAL_ERROR, NULL); dtor[0](dst->poly); } @@ -141,7 +141,7 @@ static ECS_MOVE(EcsPoly, dst, src, { static ECS_DTOR(EcsPoly, ptr, { if (ptr->poly) { - ecs_poly_dtor_t *dtor = ecs_get_dtor(ptr->poly); + flecs_poly_dtor_t *dtor = ecs_get_dtor(ptr->poly); ecs_assert(dtor != NULL, ECS_INTERNAL_ERROR, NULL); dtor[0](ptr->poly); } @@ -172,12 +172,18 @@ void flecs_assert_relation_unused( } bool in_use = ecs_id_in_use(world, ecs_pair(rel, EcsWildcard)); + + /* Hack to make enum unions work. C++ enum reflection registers enum + * constants right after creating the enum entity. The enum constant + * entities have a component of the enum type with the constant value, which + * is why it shows up as in use. */ if (property != EcsUnion) { in_use |= ecs_id_in_use(world, rel); } + if (in_use) { - char *r_str = ecs_get_fullpath(world, rel); - char *p_str = ecs_get_fullpath(world, property); + char *r_str = ecs_get_path(world, rel); + char *p_str = ecs_get_path(world, property); ecs_throw(ECS_ID_IN_USE, "cannot change property '%s' for relationship '%s': already in use", @@ -193,11 +199,15 @@ void flecs_assert_relation_unused( static bool flecs_set_id_flag( + ecs_world_t *world, ecs_id_record_t *idr, ecs_flags32_t flag) { if (!(idr->flags & flag)) { idr->flags |= flag; + if (flag == EcsIdIsSparse) { + flecs_id_record_init_sparse(world, idr); + } return true; } return false; @@ -232,11 +242,17 @@ void flecs_register_id_flag_for_relation( bool changed = false; if (event == EcsOnAdd) { - ecs_id_record_t *idr = flecs_id_record_ensure(world, e); - changed |= flecs_set_id_flag(idr, flag); + ecs_id_record_t *idr; + if (!ecs_has_id(world, e, EcsRelationship) && + !ecs_has_id(world, e, EcsTarget)) + { + idr = flecs_id_record_ensure(world, e); + changed |= flecs_set_id_flag(world, idr, flag); + } + idr = flecs_id_record_ensure(world, ecs_pair(e, EcsWildcard)); do { - changed |= flecs_set_id_flag(idr, flag); + changed |= flecs_set_id_flag(world, idr, flag); } while ((idr = idr->first.next)); if (entity_flag) flecs_add_flag(world, e, entity_flag); } else if (event == EcsOnRemove) { @@ -264,7 +280,7 @@ void flecs_register_final(ecs_iter_t *it) { for (i = 0; i < count; i ++) { ecs_entity_t e = it->entities[i]; if (flecs_id_record_get(world, ecs_pair(EcsIsA, e)) != NULL) { - char *e_str = ecs_get_fullpath(world, e); + char *e_str = ecs_get_path(world, e); ecs_throw(ECS_ID_IN_USE, "cannot change property 'Final' for '%s': already inherited from", e_str); @@ -275,33 +291,9 @@ void flecs_register_final(ecs_iter_t *it) { } } -static -void flecs_register_on_delete(ecs_iter_t *it) { - ecs_id_t id = ecs_field_id(it, 1); - flecs_register_id_flag_for_relation(it, EcsOnDelete, - ECS_ID_ON_DELETE_FLAG(ECS_PAIR_SECOND(id)), - EcsIdOnDeleteMask, - EcsEntityIsId); -} - -static -void flecs_register_on_delete_object(ecs_iter_t *it) { - ecs_id_t id = ecs_field_id(it, 1); - flecs_register_id_flag_for_relation(it, EcsOnDeleteTarget, - ECS_ID_ON_DELETE_TARGET_FLAG(ECS_PAIR_SECOND(id)), - EcsIdOnDeleteObjectMask, - EcsEntityIsId); -} - -static -void flecs_register_traversable(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsAcyclic, EcsIdTraversable, - EcsIdTraversable, 0); -} - static void flecs_register_tag(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsTag, EcsIdTag, ~EcsIdTag, 0); + flecs_register_id_flag_for_relation(it, EcsPairIsTag, EcsIdTag, EcsIdTag, 0); /* Ensure that all id records for tag have type info set to NULL */ ecs_world_t *world = it->real_world; @@ -315,7 +307,7 @@ void flecs_register_tag(ecs_iter_t *it) { ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); do { if (idr->type_info != NULL) { - flecs_assert_relation_unused(world, e, EcsTag); + flecs_assert_relation_unused(world, e, EcsPairIsTag); } idr->type_info = NULL; } while ((idr = idr->first.next)); @@ -324,31 +316,47 @@ void flecs_register_tag(ecs_iter_t *it) { } static -void flecs_register_exclusive(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsExclusive, EcsIdExclusive, - EcsIdExclusive, 0); +void flecs_register_on_delete(ecs_iter_t *it) { + ecs_id_t id = ecs_field_id(it, 0); + flecs_register_id_flag_for_relation(it, EcsOnDelete, + ECS_ID_ON_DELETE_FLAG(ECS_PAIR_SECOND(id)), + EcsIdOnDeleteMask, + EcsEntityIsId); } static -void flecs_register_dont_inherit(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsDontInherit, - EcsIdDontInherit, EcsIdDontInherit, 0); +void flecs_register_on_delete_object(ecs_iter_t *it) { + ecs_id_t id = ecs_field_id(it, 0); + flecs_register_id_flag_for_relation(it, EcsOnDeleteTarget, + ECS_ID_ON_DELETE_TARGET_FLAG(ECS_PAIR_SECOND(id)), + EcsIdOnDeleteObjectMask, + EcsEntityIsId); } static -void flecs_register_always_override(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsAlwaysOverride, - EcsIdAlwaysOverride, EcsIdAlwaysOverride, 0); +void flecs_register_on_instantiate(ecs_iter_t *it) { + ecs_id_t id = ecs_field_id(it, 0); + flecs_register_id_flag_for_relation(it, EcsOnInstantiate, + ECS_ID_ON_INSTANTIATE_FLAG(ECS_PAIR_SECOND(id)), + 0, 0); } +typedef struct ecs_on_trait_ctx_t { + ecs_flags32_t flag, not_flag; +} ecs_on_trait_ctx_t; + static -void flecs_register_with(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsWith, EcsIdWith, 0, 0); +void flecs_register_trait(ecs_iter_t *it) { + ecs_on_trait_ctx_t *ctx = it->ctx; + flecs_register_id_flag_for_relation( + it, it->ids[0], ctx->flag, ctx->not_flag, 0); } static -void flecs_register_union(ecs_iter_t *it) { - flecs_register_id_flag_for_relation(it, EcsUnion, EcsIdUnion, 0, 0); +void flecs_register_trait_pair(ecs_iter_t *it) { + ecs_on_trait_ctx_t *ctx = it->ctx; + flecs_register_id_flag_for_relation( + it, ecs_pair_first(it->world, it->ids[0]), ctx->flag, ctx->not_flag, 0); } static @@ -361,7 +369,7 @@ void flecs_register_slot_of(ecs_iter_t *it) { static void flecs_on_symmetric_add_remove(ecs_iter_t *it) { - ecs_entity_t pair = ecs_field_id(it, 1); + ecs_entity_t pair = ecs_field_id(it, 0); if (!ECS_HAS_ID_FLAG(pair, PAIR)) { /* If relationship was not added as a pair, there's nothing to do */ @@ -372,17 +380,20 @@ void flecs_on_symmetric_add_remove(ecs_iter_t *it) { ecs_entity_t rel = ECS_PAIR_FIRST(pair); ecs_entity_t obj = ecs_pair_second(world, pair); ecs_entity_t event = it->event; - - int i, count = it->count; - for (i = 0; i < count; i ++) { - ecs_entity_t subj = it->entities[i]; - if (event == EcsOnAdd) { - if (!ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { - ecs_add_pair(it->world, obj, rel, subj); - } - } else { - if (ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { - ecs_remove_pair(it->world, obj, rel, subj); + + + if (obj) { + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t subj = it->entities[i]; + if (event == EcsOnAdd) { + if (!ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { + ecs_add_pair(it->world, obj, rel, subj); + } + } else { + if (ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { + ecs_remove_pair(it->world, obj, rel, subj); + } } } } @@ -400,8 +411,8 @@ 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(world, { - .entity = ecs_entity(world, {.add = {ecs_childof(r)}}), - .filter.terms[0] = { .id = ecs_pair(r, EcsWildcard) }, + .entity = ecs_entity(world, { .parent = r }), + .query.terms[0] = { .id = ecs_pair(r, EcsWildcard) }, .callback = flecs_on_symmetric_add_remove, .events = {EcsOnAdd, EcsOnRemove} }); @@ -411,7 +422,7 @@ void flecs_register_symmetric(ecs_iter_t *it) { static void flecs_on_component(ecs_iter_t *it) { ecs_world_t *world = it->world; - EcsComponent *c = ecs_field(it, EcsComponent, 1); + EcsComponent *c = ecs_field(it, EcsComponent, 0); int i, count = it->count; for (i = 0; i < count; i ++) { @@ -422,6 +433,13 @@ void flecs_on_component(ecs_iter_t *it) { "component id must be smaller than %u", ECS_MAX_COMPONENT_ID); (void)component_id; + if (it->event != EcsOnRemove) { + ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); + if (parent) { + ecs_add_id(world, parent, EcsModule); + } + } + if (it->event == EcsOnSet) { if (flecs_type_info_init_id( world, e, c[i].size, c[i].alignment, NULL)) @@ -429,7 +447,19 @@ void flecs_on_component(ecs_iter_t *it) { flecs_assert_relation_unused(world, e, ecs_id(EcsComponent)); } } else if (it->event == EcsOnRemove) { - flecs_type_info_free(world, e); + #ifdef FLECS_DEBUG + if (ecs_should_log(0)) { + char *path = ecs_get_path(world, e); + ecs_trace("unregistering component '%s'", path); + ecs_os_free(path); + } + #endif + if (!ecs_vec_count(&world->store.marked_ids)) { + flecs_type_info_free(world, e); + } else { + ecs_vec_append_t(&world->allocator, + &world->store.deleted_components, ecs_entity_t)[0] = e; + } } } } @@ -448,17 +478,60 @@ void flecs_ensure_module_tag(ecs_iter_t *it) { } } -/* -- Iterable mixins -- */ +static +void flecs_disable_observer( + ecs_iter_t *it) +{ + ecs_world_t *world = it->world; + ecs_entity_t evt = it->event; + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + flecs_observer_set_disable_bit(world, it->entities[i], + EcsObserverIsDisabled, evt == EcsOnAdd); + } +} static -void flecs_on_event_iterable_init( - const ecs_world_t *world, - const ecs_poly_t *poly, /* Observable */ - ecs_iter_t *it, - ecs_term_t *filter) +void flecs_disable_module_observers( + ecs_world_t *world, + ecs_entity_t module, + bool should_disable) { - ecs_iter_poly(world, poly, it, filter); - it->event_id = filter->id; + ecs_iter_t child_it = ecs_children(world, module); + while (ecs_children_next(&child_it)) { + ecs_table_t *table = child_it.table; + bool table_disabled = table->flags & EcsTableIsDisabled; + int32_t i; + + /* Recursively walk modules, don't propagate to disabled modules */ + if (ecs_table_has_id(world, table, EcsModule) && !table_disabled) { + for (i = 0; i < child_it.count; i ++) { + flecs_disable_module_observers( + world, child_it.entities[i], should_disable); + } + continue; + } + + /* Only disable observers */ + if (!ecs_table_has_id(world, table, EcsObserver)) { + continue; + } + + for (i = 0; i < child_it.count; i ++) { + flecs_observer_set_disable_bit(world, child_it.entities[i], + EcsObserverIsParentDisabled, should_disable); + } + } +} + +static +void flecs_disable_module(ecs_iter_t *it) { + int32_t i; + for (i = 0; i < it->count; i ++) { + flecs_disable_module_observers( + it->real_world, it->entities[i], it->event == EcsOnAdd); + } } /* -- Bootstrapping -- */ @@ -487,7 +560,7 @@ void flecs_bootstrap_builtin( int32_t index = flecs_table_append(world, table, entity, false, false); record->row = ECS_ROW_TO_RECORD(index, 0); - EcsComponent *component = ecs_vec_first(&columns[0].data); + EcsComponent *component = columns[0].data; component[index].size = size; component[index].alignment = alignment; @@ -495,7 +568,7 @@ void flecs_bootstrap_builtin( ecs_size_t symbol_length = ecs_os_strlen(symbol); ecs_size_t name_length = symbol_length - 3; - EcsIdentifier *name_col = ecs_vec_first(&columns[1].data); + EcsIdentifier *name_col = columns[1].data; uint64_t name_hash = flecs_hash(name, name_length); name_col[index].value = ecs_os_strdup(name); name_col[index].length = name_length; @@ -505,7 +578,7 @@ void flecs_bootstrap_builtin( flecs_name_index_ensure( table->_->name_index, entity, name, name_length, name_hash); - EcsIdentifier *symbol_col = ecs_vec_first(&columns[2].data); + EcsIdentifier *symbol_col = columns[2].data; symbol_col[index].value = ecs_os_strdup(symbol); symbol_col[index].length = symbol_length; symbol_col[index].hash = flecs_hash(symbol, symbol_length); @@ -526,16 +599,16 @@ ecs_table_t* flecs_bootstrap_component_table( /* Before creating table, manually set flags for ChildOf/Identifier, as this * can no longer be done after they are in use. */ ecs_id_record_t *idr = flecs_id_record_ensure(world, EcsChildOf); - idr->flags |= EcsIdOnDeleteObjectDelete | EcsIdDontInherit | + idr->flags |= EcsIdOnDeleteObjectDelete | EcsIdOnInstantiateDontInherit | EcsIdTraversable | EcsIdTag; /* Initialize id records cached on world */ world->idr_childof_wildcard = flecs_id_record_ensure(world, ecs_pair(EcsChildOf, EcsWildcard)); world->idr_childof_wildcard->flags |= EcsIdOnDeleteObjectDelete | - EcsIdDontInherit | EcsIdTraversable | EcsIdTag | EcsIdExclusive; + EcsIdOnInstantiateDontInherit | EcsIdTraversable | EcsIdTag | EcsIdExclusive; idr = flecs_id_record_ensure(world, ecs_pair_t(EcsIdentifier, EcsWildcard)); - idr->flags |= EcsIdDontInherit; + idr->flags |= EcsIdOnInstantiateDontInherit; world->idr_identifier_name = flecs_id_record_ensure(world, ecs_pair_t(EcsIdentifier, EcsName)); world->idr_childof_0 = flecs_id_record_ensure(world, @@ -556,14 +629,40 @@ ecs_table_t* flecs_bootstrap_component_table( }; ecs_table_t *result = flecs_table_find_or_create(world, &array); - ecs_data_t *data = &result->data; /* Preallocate enough memory for initial components */ ecs_allocator_t *a = &world->allocator; - ecs_vec_init_t(a, &data->entities, ecs_entity_t, EcsFirstUserComponentId); - ecs_vec_init_t(a, &data->columns[0].data, EcsComponent, EcsFirstUserComponentId); - ecs_vec_init_t(a, &data->columns[1].data, EcsIdentifier, EcsFirstUserComponentId); - ecs_vec_init_t(a, &data->columns[2].data, EcsIdentifier, EcsFirstUserComponentId); + ecs_vec_t v_entities = ecs_vec_from_entities(result); + ecs_vec_init_t(a, &v_entities, ecs_entity_t, EcsFirstUserComponentId); + + { + ecs_column_t *column = &result->data.columns[0]; + ecs_vec_t v = ecs_vec_from_column_t(column, result, EcsComponent); + ecs_vec_init_t(a, &v, EcsComponent, EcsFirstUserComponentId); + ecs_assert(v.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + column->data = v.array; + } + { + ecs_column_t *column = &result->data.columns[1]; + ecs_vec_t v = ecs_vec_from_column_t(column, result, EcsIdentifier); + ecs_vec_init_t(a, &v, EcsIdentifier, EcsFirstUserComponentId); + ecs_assert(v.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + column->data = v.array; + } + { + ecs_column_t *column = &result->data.columns[2]; + ecs_vec_t v = ecs_vec_from_column_t(column, result, EcsIdentifier); + ecs_vec_init_t(a, &v, EcsIdentifier, EcsFirstUserComponentId); + ecs_assert(v.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + column->data = v.array; + } + + result->data.entities = v_entities.array; + result->data.count = 0; + result->data.size = v_entities.size; return result; } @@ -579,7 +678,7 @@ void flecs_bootstrap_entity( ecs_os_strcpy(symbol, "flecs.core."); ecs_os_strcat(symbol, name); - ecs_ensure(world, id); + ecs_make_alive(world, id); ecs_add_pair(world, id, EcsChildOf, parent); ecs_set_name(world, id, name); ecs_set_symbol(world, id, symbol); @@ -587,7 +686,7 @@ void flecs_bootstrap_entity( ecs_assert(ecs_get_name(world, id) != NULL, ECS_INTERNAL_ERROR, NULL); if (!parent || parent == EcsFlecsCore) { - ecs_assert(ecs_lookup_fullpath(world, name) == id, + ecs_assert(ecs_lookup(world, name) == id, ECS_INTERNAL_ERROR, NULL); } } @@ -600,36 +699,41 @@ void flecs_bootstrap( ecs_set_name_prefix(world, "Ecs"); /* Ensure builtin ids are alive */ - ecs_ensure(world, ecs_id(EcsComponent)); - ecs_ensure(world, EcsFinal); - ecs_ensure(world, ecs_id(EcsIdentifier)); - ecs_ensure(world, EcsName); - ecs_ensure(world, EcsSymbol); - ecs_ensure(world, EcsAlias); - ecs_ensure(world, EcsChildOf); - ecs_ensure(world, EcsFlecs); - ecs_ensure(world, EcsFlecsCore); - ecs_ensure(world, EcsOnAdd); - ecs_ensure(world, EcsOnRemove); - ecs_ensure(world, EcsOnSet); - ecs_ensure(world, EcsUnSet); - ecs_ensure(world, EcsOnDelete); - ecs_ensure(world, EcsPanic); - ecs_ensure(world, EcsFlag); - ecs_ensure(world, EcsIsA); - ecs_ensure(world, EcsWildcard); - ecs_ensure(world, EcsAny); - ecs_ensure(world, EcsTag); + ecs_make_alive(world, ecs_id(EcsComponent)); + ecs_make_alive(world, EcsFinal); + ecs_make_alive(world, ecs_id(EcsIdentifier)); + ecs_make_alive(world, EcsName); + ecs_make_alive(world, EcsSymbol); + ecs_make_alive(world, EcsAlias); + ecs_make_alive(world, EcsChildOf); + ecs_make_alive(world, EcsFlecs); + ecs_make_alive(world, EcsFlecsCore); + ecs_make_alive(world, EcsOnAdd); + ecs_make_alive(world, EcsOnRemove); + ecs_make_alive(world, EcsOnSet); + ecs_make_alive(world, EcsOnDelete); + ecs_make_alive(world, EcsPanic); + ecs_make_alive(world, EcsFlag); + ecs_make_alive(world, EcsIsA); + ecs_make_alive(world, EcsWildcard); + ecs_make_alive(world, EcsAny); + ecs_make_alive(world, EcsPairIsTag); + ecs_make_alive(world, EcsCanToggle); + ecs_make_alive(world, EcsTrait); + ecs_make_alive(world, EcsRelationship); + ecs_make_alive(world, EcsTarget); + ecs_make_alive(world, EcsSparse); + ecs_make_alive(world, EcsUnion); /* Register type information for builtin components */ flecs_type_info_init(world, EcsComponent, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .on_set = flecs_on_component, .on_remove = flecs_on_component }); flecs_type_info_init(world, EcsIdentifier, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(EcsIdentifier), .copy = ecs_copy(EcsIdentifier), .move = ecs_move(EcsIdentifier), @@ -638,14 +742,15 @@ void flecs_bootstrap( }); flecs_type_info_init(world, EcsPoly, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .copy = ecs_copy(EcsPoly), .move = ecs_move(EcsPoly), .dtor = ecs_dtor(EcsPoly) }); - flecs_type_info_init(world, EcsIterable, { 0 }); - flecs_type_info_init(world, EcsTarget, { 0 }); + flecs_type_info_init(world, EcsDefaultChildComponent, { + .ctor = flecs_default_ctor, + }); /* Create and cache often used id records on world */ flecs_init_id_records(world); @@ -659,9 +764,8 @@ void flecs_bootstrap( /* Bootstrap builtin components */ flecs_bootstrap_builtin_t(world, table, EcsIdentifier); flecs_bootstrap_builtin_t(world, table, EcsComponent); - flecs_bootstrap_builtin_t(world, table, EcsIterable); flecs_bootstrap_builtin_t(world, table, EcsPoly); - flecs_bootstrap_builtin_t(world, table, EcsTarget); + flecs_bootstrap_builtin_t(world, table, EcsDefaultChildComponent); /* Initialize default entity id range */ world->info.last_component_id = EcsFirstUserComponentId; @@ -669,14 +773,10 @@ void flecs_bootstrap( world->info.min_id = 0; world->info.max_id = 0; - /* Make EcsOnAdd, EcsOnSet events iterable to enable .yield_existing */ - ecs_set(world, EcsOnAdd, EcsIterable, { .init = flecs_on_event_iterable_init }); - ecs_set(world, EcsOnSet, EcsIterable, { .init = flecs_on_event_iterable_init }); - - /* Register observer for tag property before adding EcsTag */ + /* Register observer for tag property before adding EcsPairIsTag */ ecs_observer(world, { - .entity = ecs_entity(world, {.add = { ecs_childof(EcsFlecsInternals)}}), - .filter.terms[0] = { .id = EcsTag, .src.flags = EcsSelf }, + .entity = ecs_entity(world, { .parent = EcsFlecsInternals }), + .query.terms[0] = { .id = EcsPairIsTag }, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_tag, .yield_existing = true @@ -697,6 +797,7 @@ void flecs_bootstrap( flecs_bootstrap_tag(world, EcsPrefab); flecs_bootstrap_tag(world, EcsSlotOf); flecs_bootstrap_tag(world, EcsDisabled); + flecs_bootstrap_tag(world, EcsNotQueryable); flecs_bootstrap_tag(world, EcsEmpty); /* Initialize builtin modules */ @@ -728,28 +829,33 @@ void flecs_bootstrap( flecs_bootstrap_entity(world, EcsFlag, "Flag", EcsFlecsCore); /* Component/relationship properties */ - flecs_bootstrap_tag(world, EcsTransitive); - flecs_bootstrap_tag(world, EcsReflexive); - flecs_bootstrap_tag(world, EcsSymmetric); - flecs_bootstrap_tag(world, EcsFinal); - flecs_bootstrap_tag(world, EcsDontInherit); - flecs_bootstrap_tag(world, EcsAlwaysOverride); - flecs_bootstrap_tag(world, EcsTag); - flecs_bootstrap_tag(world, EcsUnion); - flecs_bootstrap_tag(world, EcsExclusive); - flecs_bootstrap_tag(world, EcsAcyclic); - flecs_bootstrap_tag(world, EcsTraversable); - flecs_bootstrap_tag(world, EcsWith); - flecs_bootstrap_tag(world, EcsOneOf); - - flecs_bootstrap_tag(world, EcsOnDelete); - flecs_bootstrap_tag(world, EcsOnDeleteTarget); + flecs_bootstrap_trait(world, EcsTransitive); + flecs_bootstrap_trait(world, EcsReflexive); + flecs_bootstrap_trait(world, EcsSymmetric); + flecs_bootstrap_trait(world, EcsFinal); + flecs_bootstrap_trait(world, EcsPairIsTag); + flecs_bootstrap_trait(world, EcsExclusive); + flecs_bootstrap_trait(world, EcsAcyclic); + flecs_bootstrap_trait(world, EcsTraversable); + flecs_bootstrap_trait(world, EcsWith); + flecs_bootstrap_trait(world, EcsOneOf); + flecs_bootstrap_trait(world, EcsCanToggle); + flecs_bootstrap_trait(world, EcsTrait); + flecs_bootstrap_trait(world, EcsRelationship); + flecs_bootstrap_trait(world, EcsTarget); + flecs_bootstrap_trait(world, EcsOnDelete); + flecs_bootstrap_trait(world, EcsOnDeleteTarget); + flecs_bootstrap_trait(world, EcsOnInstantiate); + flecs_bootstrap_trait(world, EcsSparse); + flecs_bootstrap_trait(world, EcsUnion); + flecs_bootstrap_tag(world, EcsRemove); flecs_bootstrap_tag(world, EcsDelete); flecs_bootstrap_tag(world, EcsPanic); - flecs_bootstrap_tag(world, EcsFlatten); - flecs_bootstrap_tag(world, EcsDefaultChildComponent); + flecs_bootstrap_tag(world, EcsOverride); + flecs_bootstrap_tag(world, EcsInherit); + flecs_bootstrap_tag(world, EcsDontInherit); /* Builtin predicates */ flecs_bootstrap_tag(world, EcsPredEq); @@ -767,129 +873,166 @@ void flecs_bootstrap( flecs_bootstrap_entity(world, EcsOnAdd, "OnAdd", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnRemove, "OnRemove", EcsFlecsCore); 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); + /* Unqueryable entities */ + ecs_add_id(world, EcsThis, EcsNotQueryable); + ecs_add_id(world, EcsWildcard, EcsNotQueryable); + ecs_add_id(world, EcsAny, EcsNotQueryable); + ecs_add_id(world, EcsVariable, EcsNotQueryable); + /* Tag relationships (relationships that should never have data) */ - ecs_add_id(world, EcsIsA, EcsTag); - ecs_add_id(world, EcsChildOf, EcsTag); - ecs_add_id(world, EcsSlotOf, EcsTag); - ecs_add_id(world, EcsDependsOn, EcsTag); - ecs_add_id(world, EcsFlatten, EcsTag); - ecs_add_id(world, EcsDefaultChildComponent, EcsTag); - ecs_add_id(world, EcsUnion, EcsTag); - ecs_add_id(world, EcsFlag, EcsTag); - ecs_add_id(world, EcsWith, EcsTag); + ecs_add_id(world, EcsIsA, EcsPairIsTag); + ecs_add_id(world, EcsChildOf, EcsPairIsTag); + ecs_add_id(world, EcsSlotOf, EcsPairIsTag); + ecs_add_id(world, EcsDependsOn, EcsPairIsTag); + ecs_add_id(world, EcsFlag, EcsPairIsTag); + ecs_add_id(world, EcsWith, EcsPairIsTag); /* Exclusive properties */ ecs_add_id(world, EcsChildOf, EcsExclusive); ecs_add_id(world, EcsOnDelete, EcsExclusive); ecs_add_id(world, EcsOnDeleteTarget, EcsExclusive); - ecs_add_id(world, EcsDefaultChildComponent, EcsExclusive); + ecs_add_id(world, EcsOnInstantiate, EcsExclusive); + + /* Relationships */ + ecs_add_id(world, EcsChildOf, EcsRelationship); + ecs_add_id(world, EcsIsA, EcsRelationship); + ecs_add_id(world, EcsSlotOf, EcsRelationship); + ecs_add_id(world, EcsDependsOn, EcsRelationship); + ecs_add_id(world, EcsWith, EcsRelationship); + ecs_add_id(world, EcsOnDelete, EcsRelationship); + ecs_add_id(world, EcsOnDeleteTarget, EcsRelationship); + ecs_add_id(world, EcsOnInstantiate, EcsRelationship); + ecs_add_id(world, ecs_id(EcsIdentifier), EcsRelationship); + + /* Targets */ + ecs_add_id(world, EcsOverride, EcsTarget); + ecs_add_id(world, EcsInherit, EcsTarget); + ecs_add_id(world, EcsDontInherit, EcsTarget); /* Sync properties of ChildOf and Identifier with bootstrapped flags */ ecs_add_pair(world, EcsChildOf, EcsOnDeleteTarget, EcsDelete); + ecs_add_id(world, EcsChildOf, EcsTrait); ecs_add_id(world, EcsChildOf, EcsAcyclic); ecs_add_id(world, EcsChildOf, EcsTraversable); - ecs_add_id(world, EcsChildOf, EcsDontInherit); - ecs_add_id(world, ecs_id(EcsIdentifier), EcsDontInherit); + ecs_add_pair(world, EcsChildOf, EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, ecs_id(EcsIdentifier), EcsOnInstantiate, EcsDontInherit); /* Create triggers in internals scope */ ecs_set_scope(world, EcsFlecsInternals); - /* Term used to also match prefabs */ - ecs_term_t match_prefab = { - .id = EcsPrefab, - .oper = EcsOptional, - .src.flags = EcsSelf - }; - /* Register observers for components/relationship properties. Most observers * set flags on an id record when a property is added to a component, which * allows for quick property testing in various operations. */ ecs_observer(world, { - .filter.terms = {{ .id = EcsFinal, .src.flags = EcsSelf }, match_prefab }, + .query.terms = {{ .id = EcsFinal }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_final }); ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(EcsOnDelete, EcsWildcard), .src.flags = EcsSelf }, - match_prefab + .query.terms = { + { .id = ecs_pair(EcsOnDelete, EcsWildcard) } }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_on_delete }); ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(EcsOnDeleteTarget, EcsWildcard), .src.flags = EcsSelf }, - match_prefab + .query.terms = { + { .id = ecs_pair(EcsOnDeleteTarget, EcsWildcard) } }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_on_delete_object }); ecs_observer(world, { - .filter.terms = { - { .id = EcsTraversable, .src.flags = EcsSelf }, - match_prefab + .query.terms = { + { .id = ecs_pair(EcsOnInstantiate, EcsWildcard) } }, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = flecs_register_traversable + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_on_instantiate + }); + + ecs_observer(world, { + .query.terms = {{ .id = EcsSymmetric }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd}, + .callback = flecs_register_symmetric }); + static ecs_on_trait_ctx_t traversable_trait = { EcsIdTraversable, EcsIdTraversable }; ecs_observer(world, { - .filter.terms = {{ .id = EcsExclusive, .src.flags = EcsSelf }, match_prefab }, + .query.terms = {{ .id = EcsTraversable }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd, EcsOnRemove}, - .callback = flecs_register_exclusive + .callback = flecs_register_trait, + .ctx = &traversable_trait }); + static ecs_on_trait_ctx_t exclusive_trait = { EcsIdExclusive, EcsIdExclusive }; ecs_observer(world, { - .filter.terms = {{ .id = EcsSymmetric, .src.flags = EcsSelf }, match_prefab }, - .events = {EcsOnAdd}, - .callback = flecs_register_symmetric + .query.terms = {{ .id = EcsExclusive }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_register_trait, + .ctx = &exclusive_trait }); + static ecs_on_trait_ctx_t toggle_trait = { EcsIdCanToggle, 0 }; ecs_observer(world, { - .filter.terms = {{ .id = EcsDontInherit, .src.flags = EcsSelf }, match_prefab }, + .query.terms = {{ .id = EcsCanToggle }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, - .callback = flecs_register_dont_inherit + .callback = flecs_register_trait, + .ctx = &toggle_trait }); + static ecs_on_trait_ctx_t with_trait = { EcsIdWith, 0 }; ecs_observer(world, { - .filter.terms = {{ .id = EcsAlwaysOverride, .src.flags = EcsSelf } }, + .query.terms = { + { .id = ecs_pair(EcsWith, EcsWildcard) }, + }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, - .callback = flecs_register_always_override + .callback = flecs_register_trait_pair, + .ctx = &with_trait }); + static ecs_on_trait_ctx_t sparse_trait = { EcsIdIsSparse, 0 }; ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(EcsWith, EcsWildcard), .src.flags = EcsSelf }, - match_prefab - }, + .query.terms = {{ .id = EcsSparse }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, - .callback = flecs_register_with + .callback = flecs_register_trait, + .ctx = &sparse_trait }); + static ecs_on_trait_ctx_t union_trait = { EcsIdIsUnion, 0 }; ecs_observer(world, { - .filter.terms = {{ .id = EcsUnion, .src.flags = EcsSelf }, match_prefab }, + .query.terms = {{ .id = EcsUnion }}, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, - .callback = flecs_register_union + .callback = flecs_register_trait, + .ctx = &union_trait }); /* Entities used as slot are marked as exclusive to ensure a slot can always * only point to a single entity. */ ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(EcsSlotOf, EcsWildcard), .src.flags = EcsSelf }, - match_prefab + .query.terms = { + { .id = ecs_pair(EcsSlotOf, EcsWildcard) } }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_slot_of }); @@ -897,11 +1040,32 @@ void flecs_bootstrap( /* Define observer to make sure that adding a module to a child entity also * adds it to the parent. */ ecs_observer(world, { - .filter.terms = {{ .id = EcsModule, .src.flags = EcsSelf }, match_prefab}, + .query.terms = {{ .id = EcsModule } }, + .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_ensure_module_tag }); + /* Observer that tracks whether observers are disabled */ + ecs_observer(world, { + .query.terms = { + { .id = EcsObserver }, + { .id = EcsDisabled }, + }, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_disable_observer + }); + + /* Observer that tracks whether modules are disabled */ + ecs_observer(world, { + .query.terms = { + { .id = EcsModule }, + { .id = EcsDisabled }, + }, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = flecs_disable_module + }); + /* Set scope back to flecs core */ ecs_set_scope(world, EcsFlecsCore); @@ -912,9 +1076,10 @@ void flecs_bootstrap( ecs_add_pair(world, EcsTransitive, EcsWith, EcsTraversable); /* DontInherit components */ - ecs_add_id(world, EcsPrefab, EcsDontInherit); - ecs_add_id(world, ecs_id(EcsComponent), EcsDontInherit); - ecs_add_id(world, EcsOnDelete, EcsDontInherit); + ecs_add_pair(world, EcsPrefab, EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, ecs_id(EcsComponent), EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, EcsOnDelete, EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, EcsUnion, EcsOnInstantiate, EcsDontInherit); /* Acyclic/Traversable components */ ecs_add_id(world, EcsIsA, EcsTraversable); @@ -928,19 +1093,20 @@ void flecs_bootstrap( /* Exclusive properties */ ecs_add_id(world, EcsSlotOf, EcsExclusive); ecs_add_id(world, EcsOneOf, EcsExclusive); - ecs_add_id(world, EcsFlatten, EcsExclusive); /* Private properties */ ecs_add_id(world, ecs_id(EcsPoly), EcsPrivate); ecs_add_id(world, ecs_id(EcsIdentifier), EcsPrivate); - ecs_add_id(world, EcsChildOf, EcsPrivate); - ecs_add_id(world, EcsIsA, EcsPrivate); + + /* Inherited components */ + ecs_add_pair(world, EcsIsA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, EcsDependsOn, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, EcsDisabled, EcsOnInstantiate, EcsInherit); /* Run bootstrap functions for other parts of the code */ flecs_bootstrap_hierarchy(world); ecs_set_scope(world, 0); - ecs_set_name_prefix(world, NULL); ecs_assert(world->idr_childof_wildcard != NULL, ECS_INTERNAL_ERROR, NULL); diff --git a/vendors/flecs/src/datastructures/allocator.c b/vendors/flecs/src/datastructures/allocator.c index 484eb72f7..aeba8b33a 100644 --- a/vendors/flecs/src/datastructures/allocator.c +++ b/vendors/flecs/src/datastructures/allocator.c @@ -32,6 +32,8 @@ void flecs_allocator_init( void flecs_allocator_fini( ecs_allocator_t *a) { + ecs_assert(a != NULL, ECS_INVALID_PARAMETER, NULL); + int32_t i = 0, count = flecs_sparse_count(&a->sizes); for (i = 0; i < count; i ++) { ecs_block_allocator_t *ba = flecs_sparse_get_dense_t( @@ -39,6 +41,7 @@ void flecs_allocator_fini( flecs_ballocator_fini(ba); } flecs_sparse_fini(&a->sizes); + flecs_ballocator_fini(&a->chunks); } diff --git a/vendors/flecs/src/datastructures/block_allocator.c b/vendors/flecs/src/datastructures/block_allocator.c index a4c895c98..c71c06959 100644 --- a/vendors/flecs/src/datastructures/block_allocator.c +++ b/vendors/flecs/src/datastructures/block_allocator.c @@ -15,6 +15,8 @@ int64_t ecs_block_allocator_alloc_count = 0; int64_t ecs_block_allocator_free_count = 0; +#ifndef FLECS_USE_OS_ALLOC + static ecs_block_allocator_chunk_header_t* flecs_balloc_block( ecs_block_allocator_t *allocator) @@ -54,6 +56,8 @@ ecs_block_allocator_chunk_header_t* flecs_balloc_block( return first_chunk; } +#endif + void flecs_ballocator_init( ecs_block_allocator_t *ba, ecs_size_t size) @@ -62,6 +66,7 @@ void flecs_ballocator_init( ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); ba->data_size = size; #ifdef FLECS_SANITIZE + ba->alloc_count = 0; size += ECS_SIZEOF(int64_t); #endif ba->chunk_size = ECS_ALIGN(size, 16); @@ -97,6 +102,7 @@ void flecs_ballocator_fini( ecs_os_linc(&ecs_block_allocator_free_count); block = next; } + ba->block_head = NULL; } @@ -119,6 +125,7 @@ void* flecs_balloc( if (!ba->head) { ba->head = flecs_balloc_block(ba); + ecs_assert(ba->head != NULL, ECS_INTERNAL_ERROR, NULL); } result = ba->head; @@ -143,23 +150,35 @@ void* flecs_bcalloc( ecs_block_allocator_t *ba) { #ifdef FLECS_USE_OS_ALLOC + ecs_assert(ba != NULL, ECS_INTERNAL_ERROR, NULL); return ecs_os_calloc(ba->data_size); -#endif - +#else if (!ba) return NULL; void *result = flecs_balloc(ba); ecs_os_memset(result, 0, ba->data_size); return result; +#endif } void flecs_bfree( ecs_block_allocator_t *ba, - void *memory) + void *memory) +{ + flecs_bfree_w_dbg_info(ba, memory, NULL); +} + +void flecs_bfree_w_dbg_info( + ecs_block_allocator_t *ba, + void *memory, + const char *type_name) { + (void)type_name; + #ifdef FLECS_USE_OS_ALLOC + (void)ba; ecs_os_free(memory); return; -#endif +#else if (!ba) { ecs_assert(memory == NULL, ECS_INTERNAL_ERROR, NULL); @@ -172,9 +191,15 @@ void flecs_bfree( #ifdef FLECS_SANITIZE memory = ECS_OFFSET(memory, -ECS_SIZEOF(int64_t)); if (*(int64_t*)memory != ba->chunk_size) { - ecs_err("chunk %p returned to wrong allocator " - "(chunk = %ub, allocator = %ub)", - memory, *(int64_t*)memory, ba->chunk_size); + if (type_name) { + ecs_err("chunk %p returned to wrong allocator " + "(chunk = %ub, allocator = %ub, type = %s)", + memory, *(int64_t*)memory, ba->chunk_size, type_name); + } else { + ecs_err("chunk %p returned to wrong allocator " + "(chunk = %ub, allocator = %ub)", + memory, *(int64_t*)memory, ba->chunk_size); + } ecs_abort(ECS_INTERNAL_ERROR, NULL); } @@ -184,7 +209,9 @@ void flecs_bfree( ecs_block_allocator_chunk_header_t *chunk = memory; chunk->next = ba->head; ba->head = chunk; - ecs_assert(ba->alloc_count >= 0, ECS_INTERNAL_ERROR, "corrupted allocator"); + ecs_assert(ba->alloc_count >= 0, ECS_INTERNAL_ERROR, + "corrupted allocator (size = %d)", ba->chunk_size); +#endif } void* flecs_brealloc( @@ -194,6 +221,7 @@ void* flecs_brealloc( { void *result; #ifdef FLECS_USE_OS_ALLOC + (void)src; result = ecs_os_realloc(memory, dst->data_size); #else if (dst == src) { @@ -232,11 +260,11 @@ void* flecs_bdup( } else { return NULL; } -#endif - +#else void *result = flecs_balloc(ba); if (result) { ecs_os_memcpy(result, memory, ba->data_size); } return result; +#endif } diff --git a/vendors/flecs/src/datastructures/hashmap.c b/vendors/flecs/src/datastructures/hashmap.c index 0c138a90f..987e9e6a6 100644 --- a/vendors/flecs/src/datastructures/hashmap.c +++ b/vendors/flecs/src/datastructures/hashmap.c @@ -129,8 +129,10 @@ flecs_hashmap_result_t flecs_hashmap_ensure_( ecs_vec_t *keys = &bucket->keys; ecs_vec_t *values = &bucket->values; if (!keys->array) { - keys = ecs_vec_init(a, &bucket->keys, key_size, 1); - values = ecs_vec_init(a, &bucket->values, value_size, 1); + ecs_vec_init(a, &bucket->keys, key_size, 1); + ecs_vec_init(a, &bucket->values, value_size, 1); + keys = &bucket->keys; + values = &bucket->values; key_ptr = ecs_vec_append(a, keys, key_size); value_ptr = ecs_vec_append(a, values, value_size); ecs_os_memcpy(key_ptr, key, key_size); diff --git a/vendors/flecs/src/datastructures/sparse.c b/vendors/flecs/src/datastructures/sparse.c index 2c829f06b..04ef900db 100644 --- a/vendors/flecs/src/datastructures/sparse.c +++ b/vendors/flecs/src/datastructures/sparse.c @@ -5,12 +5,6 @@ #include "../private_api.h" -/** Compute the page index from an id by stripping the first 12 bits */ -#define PAGE(index) ((int32_t)((uint32_t)index >> FLECS_SPARSE_PAGE_BITS)) - -/** This computes the offset of an index inside a page */ -#define OFFSET(index) ((int32_t)index & (FLECS_SPARSE_PAGE_SIZE - 1)) - /* Utility to get a pointer to the payload */ #define DATA(array, size, offset) (ECS_OFFSET(array, size * offset)) @@ -137,7 +131,7 @@ void flecs_sparse_assign_index( { /* Initialize sparse-dense pair. This assigns the dense index to the sparse * array, and the sparse index to the dense array .*/ - page->sparse[OFFSET(index)] = dense; + page->sparse[FLECS_SPARSE_OFFSET(index)] = dense; dense_array[dense] = index; } @@ -188,8 +182,8 @@ uint64_t flecs_sparse_create_id( uint64_t index = flecs_sparse_inc_id(sparse); flecs_sparse_grow_dense(sparse); - ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, PAGE(index)); - ecs_assert(page->sparse[OFFSET(index)] == 0, ECS_INTERNAL_ERROR, NULL); + ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(index)); + ecs_assert(page->sparse[FLECS_SPARSE_OFFSET(index)] == 0, ECS_INTERNAL_ERROR, NULL); uint64_t *dense_array = ecs_vec_first_t(&sparse->dense, uint64_t); flecs_sparse_assign_index(page, dense_array, index, dense); @@ -224,12 +218,12 @@ void* flecs_sparse_get_sparse( uint64_t index) { flecs_sparse_strip_generation(&index); - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); if (!page || !page->sparse) { return NULL; } - int32_t offset = OFFSET(index); + int32_t offset = FLECS_SPARSE_OFFSET(index); ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(dense == page->sparse[offset], ECS_INTERNAL_ERROR, NULL); (void)dense; @@ -250,7 +244,7 @@ void flecs_sparse_swap_dense( uint64_t index_a = dense_array[a]; uint64_t index_b = dense_array[b]; - ecs_page_t *page_b = flecs_sparse_get_or_create_page(sparse, PAGE(index_b)); + ecs_page_t *page_b = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(index_b)); flecs_sparse_assign_index(page_a, dense_array, index_a, b); flecs_sparse_assign_index(page_b, dense_array, index_b, a); } @@ -327,9 +321,9 @@ void* flecs_sparse_add( ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); uint64_t index = flecs_sparse_new_index(sparse); - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL); - return DATA(page->data, size, OFFSET(index)); + return DATA(page->data, size, FLECS_SPARSE_OFFSET(index)); } uint64_t flecs_sparse_last_id( @@ -351,8 +345,8 @@ void* flecs_sparse_ensure( (void)size; uint64_t gen = flecs_sparse_strip_generation(&index); - ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, PAGE(index)); - int32_t offset = OFFSET(index); + ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(index)); + int32_t offset = FLECS_SPARSE_OFFSET(index); int32_t dense = page->sparse[offset]; if (dense) { @@ -366,6 +360,9 @@ void* flecs_sparse_ensure( /* First unused element is now last used element */ sparse->count ++; + + /* Set dense element to new generation */ + ecs_vec_first_t(&sparse->dense, uint64_t)[dense] = index | gen; } else { /* Dense is already alive, nothing to be done */ } @@ -395,7 +392,7 @@ void* flecs_sparse_ensure( /* If there are unused elements in the list, move the first unused * element to the end of the list */ uint64_t unused = dense_array[count]; - ecs_page_t *unused_page = flecs_sparse_get_or_create_page(sparse, PAGE(unused)); + ecs_page_t *unused_page = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(unused)); flecs_sparse_assign_index(unused_page, dense_array, unused, dense_count); } @@ -417,8 +414,8 @@ void* flecs_sparse_ensure_fast( (void)size; uint32_t index = (uint32_t)index_long; - ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, PAGE(index)); - int32_t offset = OFFSET(index); + ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, FLECS_SPARSE_PAGE(index)); + int32_t offset = FLECS_SPARSE_OFFSET(index); int32_t dense = page->sparse[offset]; int32_t count = sparse->count; @@ -436,6 +433,44 @@ void* flecs_sparse_ensure_fast( return DATA(page->data, sparse->size, offset); } +void* flecs_sparse_remove_fast( + ecs_sparse_t *sparse, + ecs_size_t size, + uint64_t index) +{ + ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); + (void)size; + + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); + if (!page || !page->sparse) { + return NULL; + } + + flecs_sparse_strip_generation(&index); + int32_t offset = FLECS_SPARSE_OFFSET(index); + int32_t dense = page->sparse[offset]; + + if (dense) { + int32_t count = sparse->count; + if (dense == (count - 1)) { + /* If dense is the last used element, simply decrease count */ + sparse->count --; + } else if (dense < count) { + /* If element is alive, move it to unused elements */ + flecs_sparse_swap_dense(sparse, page, dense, count - 1); + sparse->count --; + } + + /* Reset memory to zero on remove */ + return DATA(page->data, sparse->size, offset); + } else { + /* Element is not paired and thus not alive, nothing to be done */ + return NULL; + } +} + + void flecs_sparse_remove( ecs_sparse_t *sparse, ecs_size_t size, @@ -445,13 +480,13 @@ void flecs_sparse_remove( ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); (void)size; - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); if (!page || !page->sparse) { return; } uint64_t gen = flecs_sparse_strip_generation(&index); - int32_t offset = OFFSET(index); + int32_t offset = FLECS_SPARSE_OFFSET(index); int32_t dense = page->sparse[offset]; if (dense) { @@ -489,26 +524,6 @@ void flecs_sparse_remove( } } -void flecs_sparse_set_generation( - ecs_sparse_t *sparse, - uint64_t index) -{ - ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_page_t *page = flecs_sparse_get_or_create_page(sparse, PAGE(index)); - - uint64_t index_w_gen = index; - flecs_sparse_strip_generation(&index); - int32_t offset = OFFSET(index); - int32_t dense = page->sparse[offset]; - - if (dense) { - /* Increase generation */ - ecs_vec_get_t(&sparse->dense, uint64_t, dense)[0] = index_w_gen; - } else { - /* Element is not paired and thus not alive, nothing to be done */ - } -} - void* flecs_sparse_get_dense( const ecs_sparse_t *sparse, ecs_size_t size, @@ -529,12 +544,12 @@ bool flecs_sparse_is_alive( const ecs_sparse_t *sparse, uint64_t index) { - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); if (!page || !page->sparse) { return false; } - int32_t offset = OFFSET(index); + int32_t offset = FLECS_SPARSE_OFFSET(index); int32_t dense = page->sparse[offset]; if (!dense || (dense >= sparse->count)) { return false; @@ -560,12 +575,12 @@ void* flecs_sparse_try( ecs_assert(sparse != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); (void)size; - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); if (!page || !page->sparse) { return NULL; } - int32_t offset = OFFSET(index); + int32_t offset = FLECS_SPARSE_OFFSET(index); int32_t dense = page->sparse[offset]; if (!dense || (dense >= sparse->count)) { return NULL; @@ -591,8 +606,8 @@ void* flecs_sparse_get( ecs_assert(!size || size == sparse->size, ECS_INVALID_PARAMETER, NULL); (void)size; - ecs_page_t *page = ecs_vec_get_t(&sparse->pages, ecs_page_t, PAGE(index)); - int32_t offset = OFFSET(index); + ecs_page_t *page = ecs_vec_get_t(&sparse->pages, ecs_page_t, FLECS_SPARSE_PAGE(index)); + int32_t offset = FLECS_SPARSE_OFFSET(index); int32_t dense = page->sparse[offset]; ecs_assert(dense != 0, ECS_INTERNAL_ERROR, NULL); @@ -617,12 +632,12 @@ void* flecs_sparse_get_any( (void)size; flecs_sparse_strip_generation(&index); - ecs_page_t *page = flecs_sparse_get_page(sparse, PAGE(index)); + ecs_page_t *page = flecs_sparse_get_page(sparse, FLECS_SPARSE_PAGE(index)); if (!page || !page->sparse) { return NULL; } - int32_t offset = OFFSET(index); + int32_t offset = FLECS_SPARSE_OFFSET(index); int32_t dense = page->sparse[offset]; bool in_use = dense && (dense < sparse->count); if (!in_use) { diff --git a/vendors/flecs/src/datastructures/stack_allocator.c b/vendors/flecs/src/datastructures/stack_allocator.c index f5cba73cd..e41b16457 100644 --- a/vendors/flecs/src/datastructures/stack_allocator.c +++ b/vendors/flecs/src/datastructures/stack_allocator.c @@ -33,6 +33,8 @@ void* flecs_stack_alloc( ecs_size_t size, ecs_size_t align) { + ecs_assert(size > 0, ECS_INTERNAL_ERROR, NULL); + ecs_stack_page_t *page = stack->tail_page; if (page == &stack->first && !page->data) { page->data = ecs_os_malloc(ECS_STACK_PAGE_SIZE); @@ -91,6 +93,8 @@ void flecs_stack_free( ecs_stack_cursor_t* flecs_stack_get_cursor( ecs_stack_t *stack) { + ecs_assert(stack != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_stack_page_t *page = stack->tail_page; int16_t sp = stack->tail_page->sp; ecs_stack_cursor_t *result = flecs_stack_alloc_t(stack, ecs_stack_cursor_t); @@ -108,6 +112,10 @@ ecs_stack_cursor_t* flecs_stack_get_cursor( return result; } +#define FLECS_STACK_LEAK_MSG \ + "a stack allocator leak is most likely due to an unterminated " \ + "iteration: call ecs_iter_fini to fix" + void flecs_stack_restore_cursor( ecs_stack_t *stack, ecs_stack_cursor_t *cursor) @@ -116,9 +124,12 @@ void flecs_stack_restore_cursor( return; } - ecs_dbg_assert(stack == cursor->owner, ECS_INVALID_OPERATION, NULL); - ecs_dbg_assert(stack->cursor_count > 0, ECS_DOUBLE_FREE, NULL); - ecs_assert(cursor->is_free == false, ECS_DOUBLE_FREE, NULL); + ecs_dbg_assert(stack == cursor->owner, ECS_INVALID_OPERATION, + "attempting to restore a cursor for the wrong stack"); + ecs_dbg_assert(stack->cursor_count > 0, ECS_DOUBLE_FREE, + "double free detected in stack allocator"); + ecs_assert(cursor->is_free == false, ECS_DOUBLE_FREE, + "double free detected in stack allocator"); cursor->is_free = true; @@ -148,13 +159,14 @@ void flecs_stack_restore_cursor( * if the cursor count is non-zero, stack should not be empty */ ecs_dbg_assert((stack->cursor_count == 0) == (stack->tail_page == &stack->first && stack->tail_page->sp == 0), - ECS_LEAK_DETECTED, NULL); + ECS_LEAK_DETECTED, FLECS_STACK_LEAK_MSG); } void flecs_stack_reset( ecs_stack_t *stack) { - ecs_dbg_assert(stack->cursor_count == 0, ECS_LEAK_DETECTED, NULL); + ecs_dbg_assert(stack->cursor_count == 0, ECS_LEAK_DETECTED, + FLECS_STACK_LEAK_MSG); stack->tail_page = &stack->first; stack->first.sp = 0; stack->tail_cursor = NULL; @@ -172,9 +184,12 @@ void flecs_stack_fini( ecs_stack_t *stack) { ecs_stack_page_t *next, *cur = &stack->first; - ecs_dbg_assert(stack->cursor_count == 0, ECS_LEAK_DETECTED, NULL); - ecs_assert(stack->tail_page == &stack->first, ECS_LEAK_DETECTED, NULL); - ecs_assert(stack->tail_page->sp == 0, ECS_LEAK_DETECTED, NULL); + ecs_dbg_assert(stack->cursor_count == 0, ECS_LEAK_DETECTED, + FLECS_STACK_LEAK_MSG); + ecs_assert(stack->tail_page == &stack->first, ECS_LEAK_DETECTED, + FLECS_STACK_LEAK_MSG); + ecs_assert(stack->tail_page->sp == 0, ECS_LEAK_DETECTED, + FLECS_STACK_LEAK_MSG); do { next = cur->next; diff --git a/vendors/flecs/src/datastructures/strbuf.c b/vendors/flecs/src/datastructures/strbuf.c index bb2d6cd20..e36327726 100644 --- a/vendors/flecs/src/datastructures/strbuf.c +++ b/vendors/flecs/src/datastructures/strbuf.c @@ -85,7 +85,7 @@ char* flecs_strbuf_itoa( } static -int flecs_strbuf_ftoa( +void flecs_strbuf_ftoa( ecs_strbuf_t *out, double f, int precision, @@ -101,18 +101,22 @@ int flecs_strbuf_ftoa( if (nan_delim) { ecs_strbuf_appendch(out, nan_delim); ecs_strbuf_appendlit(out, "NaN"); - return ecs_strbuf_appendch(out, nan_delim); + ecs_strbuf_appendch(out, nan_delim); + return; } else { - return ecs_strbuf_appendlit(out, "NaN"); + ecs_strbuf_appendlit(out, "NaN"); + return; } } if (ecs_os_isinf(f)) { if (nan_delim) { ecs_strbuf_appendch(out, nan_delim); ecs_strbuf_appendlit(out, "Inf"); - return ecs_strbuf_appendch(out, nan_delim); + ecs_strbuf_appendch(out, nan_delim); + return; } else { - return ecs_strbuf_appendlit(out, "Inf"); + ecs_strbuf_appendlit(out, "Inf"); + return; } } @@ -213,7 +217,6 @@ int flecs_strbuf_ftoa( ptr = p1; } - ptr[0] = 'e'; ptr = flecs_strbuf_itoa(ptr + 1, exp); @@ -225,7 +228,7 @@ int flecs_strbuf_ftoa( ptr[0] = '\0'; } - return ecs_strbuf_appendstrn(out, buf, (int32_t)(ptr - buf)); + ecs_strbuf_appendstrn(out, buf, (int32_t)(ptr - buf)); } /* Add an extra element to the buffer */ @@ -233,262 +236,114 @@ static void flecs_strbuf_grow( ecs_strbuf_t *b) { - /* Allocate new element */ - ecs_strbuf_element_embedded *e = ecs_os_malloc_t(ecs_strbuf_element_embedded); - b->size += b->current->pos; - b->current->next = (ecs_strbuf_element*)e; - b->current = (ecs_strbuf_element*)e; - b->elementCount ++; - e->super.buffer_embedded = true; - e->super.buf = e->buf; - e->super.pos = 0; - e->super.next = NULL; -} - -/* Add an extra dynamic element */ -static -void flecs_strbuf_grow_str( - ecs_strbuf_t *b, - const char *str, - char *alloc_str, - int32_t size) -{ - /* Allocate new element */ - ecs_strbuf_element_str *e = ecs_os_malloc_t(ecs_strbuf_element_str); - b->size += b->current->pos; - b->current->next = (ecs_strbuf_element*)e; - b->current = (ecs_strbuf_element*)e; - b->elementCount ++; - e->super.buffer_embedded = false; - e->super.pos = size ? size : (int32_t)ecs_os_strlen(str); - e->super.next = NULL; - e->super.buf = ECS_CONST_CAST(char*, str); - e->alloc_str = alloc_str; -} - -static -char* flecs_strbuf_ptr( - ecs_strbuf_t *b) -{ - if (b->buf) { - return &b->buf[b->current->pos]; - } else { - return &b->current->buf[b->current->pos]; - } -} - -/* Compute the amount of space left in the current element */ -static -int32_t flecs_strbuf_memLeftInCurrentElement( - ecs_strbuf_t *b) -{ - if (b->current->buffer_embedded) { - return ECS_STRBUF_ELEMENT_SIZE - b->current->pos; + if (!b->content) { + b->content = b->small_string; + b->size = ECS_STRBUF_SMALL_STRING_SIZE; + } else if (b->content == b->small_string) { + b->size *= 2; + b->content = ecs_os_malloc_n(char, b->size); + ecs_os_memcpy(b->content, b->small_string, b->length); } else { - return 0; + b->size *= 2; + if (b->size < 16) b->size = 16; + b->content = ecs_os_realloc_n(b->content, char, b->size); } } -/* Compute the amount of space left */ static -int32_t flecs_strbuf_memLeft( - ecs_strbuf_t *b) -{ - if (b->max) { - return b->max - b->size - b->current->pos; - } else { - return INT_MAX; - } -} - -static -void flecs_strbuf_init( +char* flecs_strbuf_ptr( ecs_strbuf_t *b) { - /* Initialize buffer structure only once */ - if (!b->elementCount) { - b->size = 0; - b->firstElement.super.next = NULL; - b->firstElement.super.pos = 0; - b->firstElement.super.buffer_embedded = true; - b->firstElement.super.buf = b->firstElement.buf; - b->elementCount ++; - b->current = (ecs_strbuf_element*)&b->firstElement; - } + ecs_assert(b->content != NULL, ECS_INTERNAL_ERROR, NULL); + return &b->content[b->length]; } /* Append a format string to a buffer */ static -bool flecs_strbuf_vappend( +void flecs_strbuf_vappend( ecs_strbuf_t *b, const char* str, va_list args) { - bool result = true; va_list arg_cpy; if (!str) { - return result; - } - - flecs_strbuf_init(b); - - int32_t memLeftInElement = flecs_strbuf_memLeftInCurrentElement(b); - int32_t memLeft = flecs_strbuf_memLeft(b); - - if (!memLeft) { - return false; + return; } /* Compute the memory required to add the string to the buffer. If user * provided buffer, use space left in buffer, otherwise use space left in * current element. */ - int32_t max_copy = b->buf ? memLeft : memLeftInElement; - int32_t memRequired; + int32_t mem_left = b->size - b->length; + int32_t mem_required; va_copy(arg_cpy, args); - memRequired = vsnprintf( - flecs_strbuf_ptr(b), (size_t)(max_copy + 1), str, args); - - ecs_assert(memRequired != -1, ECS_INTERNAL_ERROR, NULL); - - if (memRequired <= memLeftInElement) { - /* Element was large enough to fit string */ - b->current->pos += memRequired; - } else if ((memRequired - memLeftInElement) < memLeft) { - /* If string is a format string, a new buffer of size memRequired is - * needed to re-evaluate the format string and only use the part that - * wasn't already copied to the previous element */ - if (memRequired <= ECS_STRBUF_ELEMENT_SIZE) { - /* Resulting string fits in standard-size buffer. Note that the - * entire string needs to fit, not just the remainder, as the - * format string cannot be partially evaluated */ - flecs_strbuf_grow(b); - /* Copy entire string to new buffer */ - ecs_os_vsprintf(flecs_strbuf_ptr(b), str, arg_cpy); + if (b->content) { + mem_required = ecs_os_vsnprintf( + flecs_strbuf_ptr(b), + flecs_itosize(mem_left), str, args); + } else { + mem_required = ecs_os_vsnprintf(NULL, 0, str, args); + mem_left = 0; + } - /* Ignore the part of the string that was copied into the - * previous buffer. The string copied into the new buffer could - * be memmoved so that only the remainder is left, but that is - * most likely more expensive than just keeping the entire - * string. */ + ecs_assert(mem_required != -1, ECS_INTERNAL_ERROR, NULL); - /* Update position in buffer */ - b->current->pos += memRequired; - } else { - /* Resulting string does not fit in standard-size buffer. - * Allocate a new buffer that can hold the entire string. */ - char *dst = ecs_os_malloc(memRequired + 1); - ecs_os_vsprintf(dst, str, arg_cpy); - flecs_strbuf_grow_str(b, dst, dst, memRequired); + if ((mem_required + 1) >= mem_left) { + while ((mem_required + 1) >= mem_left) { + flecs_strbuf_grow(b); + mem_left = b->size - b->length; } + ecs_os_vsnprintf(flecs_strbuf_ptr(b), + flecs_itosize(mem_required + 1), str, arg_cpy); } - va_end(arg_cpy); + b->length += mem_required; - return flecs_strbuf_memLeft(b) > 0; + va_end(arg_cpy); } static -bool flecs_strbuf_appendstr( +void flecs_strbuf_appendstr( ecs_strbuf_t *b, const char* str, int n) { - flecs_strbuf_init(b); - - int32_t memLeftInElement = flecs_strbuf_memLeftInCurrentElement(b); - int32_t memLeft = flecs_strbuf_memLeft(b); - if (memLeft <= 0) { - return false; - } - - /* Never write more than what the buffer can store */ - if (n > memLeft) { - n = memLeft; - } - - if (n <= memLeftInElement) { - /* Element was large enough to fit string */ - ecs_os_strncpy(flecs_strbuf_ptr(b), str, n); - b->current->pos += n; - } else if ((n - memLeftInElement) < memLeft) { - ecs_os_strncpy(flecs_strbuf_ptr(b), str, memLeftInElement); - - /* Element was not large enough, but buffer still has space */ - b->current->pos += memLeftInElement; - n -= memLeftInElement; - - /* Current element was too small, copy remainder into new element */ - if (n < ECS_STRBUF_ELEMENT_SIZE) { - /* A standard-size buffer is large enough for the new string */ - flecs_strbuf_grow(b); - - /* Copy the remainder to the new buffer */ - if (n) { - /* If a max number of characters to write is set, only a - * subset of the string should be copied to the buffer */ - ecs_os_strncpy( - flecs_strbuf_ptr(b), - str + memLeftInElement, - (size_t)n); - } else { - ecs_os_strcpy(flecs_strbuf_ptr(b), str + memLeftInElement); - } - - /* Update to number of characters copied to new buffer */ - b->current->pos += n; - } else { - /* String doesn't fit in a single element, strdup */ - char *remainder = ecs_os_strdup(str + memLeftInElement); - flecs_strbuf_grow_str(b, remainder, remainder, n); - } - } else { - /* Buffer max has been reached */ - return false; + int32_t mem_left = b->size - b->length; + while (n >= mem_left) { + flecs_strbuf_grow(b); + mem_left = b->size - b->length; } - return flecs_strbuf_memLeft(b) > 0; + ecs_os_memcpy(flecs_strbuf_ptr(b), str, n); + b->length += n; } static -bool flecs_strbuf_appendch( +void flecs_strbuf_appendch( ecs_strbuf_t *b, char ch) { - flecs_strbuf_init(b); - - int32_t memLeftInElement = flecs_strbuf_memLeftInCurrentElement(b); - int32_t memLeft = flecs_strbuf_memLeft(b); - if (memLeft <= 0) { - return false; - } - - if (memLeftInElement) { - /* Element was large enough to fit string */ - flecs_strbuf_ptr(b)[0] = ch; - b->current->pos ++; - } else { + if (b->size == b->length) { flecs_strbuf_grow(b); - flecs_strbuf_ptr(b)[0] = ch; - b->current->pos ++; } - return flecs_strbuf_memLeft(b) > 0; + flecs_strbuf_ptr(b)[0] = ch; + b->length ++; } -bool ecs_strbuf_vappend( +void ecs_strbuf_vappend( ecs_strbuf_t *b, const char* fmt, va_list args) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(fmt != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_vappend(b, fmt, args); + flecs_strbuf_vappend(b, fmt, args); } -bool ecs_strbuf_append( +void ecs_strbuf_append( ecs_strbuf_t *b, const char* fmt, ...) @@ -498,216 +353,120 @@ bool ecs_strbuf_append( va_list args; va_start(args, fmt); - bool result = flecs_strbuf_vappend(b, fmt, args); + flecs_strbuf_vappend(b, fmt, args); va_end(args); - - return result; } -bool ecs_strbuf_appendstrn( +void ecs_strbuf_appendstrn( ecs_strbuf_t *b, const char* str, int32_t len) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_appendstr(b, str, len); + flecs_strbuf_appendstr(b, str, len); } -bool ecs_strbuf_appendch( +void ecs_strbuf_appendch( ecs_strbuf_t *b, char ch) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_appendch(b, ch); + flecs_strbuf_appendch(b, ch); } -bool ecs_strbuf_appendint( +void ecs_strbuf_appendint( ecs_strbuf_t *b, int64_t v) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); char numbuf[32]; char *ptr = flecs_strbuf_itoa(numbuf, v); - return ecs_strbuf_appendstrn(b, numbuf, flecs_ito(int32_t, ptr - numbuf)); + ecs_strbuf_appendstrn(b, numbuf, flecs_ito(int32_t, ptr - numbuf)); } -bool ecs_strbuf_appendflt( +void ecs_strbuf_appendflt( ecs_strbuf_t *b, double flt, char nan_delim) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_ftoa(b, flt, 10, nan_delim); + flecs_strbuf_ftoa(b, flt, 10, nan_delim); } -bool ecs_strbuf_appendbool( +void ecs_strbuf_appendbool( ecs_strbuf_t *buffer, bool v) { ecs_assert(buffer != NULL, ECS_INVALID_PARAMETER, NULL); if (v) { - return ecs_strbuf_appendlit(buffer, "true"); + ecs_strbuf_appendlit(buffer, "true"); } else { - return ecs_strbuf_appendlit(buffer, "false"); + ecs_strbuf_appendlit(buffer, "false"); } } -bool ecs_strbuf_appendstr_zerocpy( - ecs_strbuf_t *b, - char* str) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_strbuf_init(b); - flecs_strbuf_grow_str(b, str, str, 0); - return true; -} - -bool ecs_strbuf_appendstr_zerocpyn( - ecs_strbuf_t *b, - char *str, - int32_t n) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - flecs_strbuf_init(b); - flecs_strbuf_grow_str(b, str, str, n); - return true; -} - -bool ecs_strbuf_appendstr_zerocpy_const( +void ecs_strbuf_appendstr( ecs_strbuf_t *b, const char* str) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - /* Removes const modifier, but logic prevents changing / delete string */ - flecs_strbuf_init(b); - flecs_strbuf_grow_str(b, str, NULL, 0); - return true; -} - -bool ecs_strbuf_appendstr_zerocpyn_const( - ecs_strbuf_t *b, - const char *str, - int32_t n) -{ - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - /* Removes const modifier, but logic prevents changing / delete string */ - flecs_strbuf_init(b); - flecs_strbuf_grow_str(b, str, NULL, n); - return true; + flecs_strbuf_appendstr(b, str, ecs_os_strlen(str)); } -bool ecs_strbuf_appendstr( +void ecs_strbuf_mergebuff( ecs_strbuf_t *b, - const char* str) + ecs_strbuf_t *src) { - ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_strbuf_appendstr(b, str, ecs_os_strlen(str)); -} - -bool ecs_strbuf_mergebuff( - ecs_strbuf_t *dst_buffer, - ecs_strbuf_t *src_buffer) -{ - if (src_buffer->elementCount) { - if (src_buffer->buf) { - return ecs_strbuf_appendstrn( - dst_buffer, src_buffer->buf, src_buffer->length); - } else { - ecs_strbuf_element *e = (ecs_strbuf_element*)&src_buffer->firstElement; - - /* Copy first element as it is inlined in the src buffer */ - ecs_strbuf_appendstrn(dst_buffer, e->buf, e->pos); - - while ((e = e->next)) { - dst_buffer->current->next = ecs_os_malloc(sizeof(ecs_strbuf_element)); - *dst_buffer->current->next = *e; - } - } - - *src_buffer = ECS_STRBUF_INIT; + if (src->content) { + ecs_strbuf_appendstr(b, src->content); } - - return true; + ecs_strbuf_reset(src); } char* ecs_strbuf_get( ecs_strbuf_t *b) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - - char* result = NULL; - if (b->elementCount) { - if (b->buf) { - b->buf[b->current->pos] = '\0'; - result = ecs_os_strdup(b->buf); - } else { - void *next = NULL; - int32_t len = b->size + b->current->pos + 1; - ecs_strbuf_element *e = (ecs_strbuf_element*)&b->firstElement; - - result = ecs_os_malloc(len); - char* ptr = result; - - do { - ecs_os_memcpy(ptr, e->buf, e->pos); - ptr += e->pos; - next = e->next; - if (e != &b->firstElement.super) { - if (!e->buffer_embedded) { - ecs_os_free(((ecs_strbuf_element_str*)e)->alloc_str); - } - ecs_os_free(e); - } - } while ((e = next)); - - result[len - 1] = '\0'; - b->length = len; - } - } else { - result = NULL; + char *result = b->content; + if (!result) { + return NULL; } - b->elementCount = 0; + ecs_strbuf_appendch(b, '\0'); + result = b->content; - b->content = result; + if (result == b->small_string) { + result = ecs_os_memdup_n(result, char, b->length + 1); + } + b->length = 0; + b->content = NULL; + b->size = 0; + b->list_sp = 0; return result; } -char *ecs_strbuf_get_small( +char* ecs_strbuf_get_small( ecs_strbuf_t *b) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - - int32_t written = ecs_strbuf_written(b); - ecs_assert(written <= ECS_STRBUF_ELEMENT_SIZE, ECS_INVALID_OPERATION, NULL); - char *buf = b->firstElement.buf; - buf[written] = '\0'; - return buf; + char *result = b->content; + result[b->length] = '\0'; + b->length = 0; + b->content = NULL; + b->size = 0; + return result; } void ecs_strbuf_reset( ecs_strbuf_t *b) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - - if (b->elementCount && !b->buf) { - void *next = NULL; - ecs_strbuf_element *e = (ecs_strbuf_element*)&b->firstElement; - do { - next = e->next; - if (e != (ecs_strbuf_element*)&b->firstElement) { - ecs_os_free(e); - } - } while ((e = next)); + if (b->content && b->content != b->small_string) { + ecs_os_free(b->content); } - *b = ECS_STRBUF_INIT; } @@ -719,10 +478,11 @@ void ecs_strbuf_list_push( ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(list_open != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(separator != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(b->list_sp >= 0, ECS_INVALID_OPERATION, NULL); + ecs_assert(b->list_sp >= 0, ECS_INVALID_OPERATION, + "strbuf list is corrupt"); b->list_sp ++; ecs_assert(b->list_sp < ECS_STRBUF_MAX_LIST_DEPTH, - ECS_INVALID_OPERATION, NULL); + ECS_INVALID_OPERATION, "max depth for strbuf list stack exceeded"); b->list_stack[b->list_sp].count = 0; b->list_stack[b->list_sp].separator = separator; @@ -743,7 +503,8 @@ void ecs_strbuf_list_pop( { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(list_close != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(b->list_sp > 0, ECS_INVALID_OPERATION, NULL); + ecs_assert(b->list_sp > 0, ECS_INVALID_OPERATION, + "pop called more often than push for strbuf list"); b->list_sp --; @@ -774,16 +535,16 @@ void ecs_strbuf_list_next( b->list_stack[list_sp].count ++; } -bool ecs_strbuf_list_appendch( +void ecs_strbuf_list_appendch( ecs_strbuf_t *b, char ch) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); ecs_strbuf_list_next(b); - return flecs_strbuf_appendch(b, ch); + flecs_strbuf_appendch(b, ch); } -bool ecs_strbuf_list_append( +void ecs_strbuf_list_append( ecs_strbuf_t *b, const char *fmt, ...) @@ -795,13 +556,11 @@ bool ecs_strbuf_list_append( va_list args; va_start(args, fmt); - bool result = flecs_strbuf_vappend(b, fmt, args); + flecs_strbuf_vappend(b, fmt, args); va_end(args); - - return result; } -bool ecs_strbuf_list_appendstr( +void ecs_strbuf_list_appendstr( ecs_strbuf_t *b, const char *str) { @@ -809,10 +568,10 @@ bool ecs_strbuf_list_appendstr( ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); ecs_strbuf_list_next(b); - return ecs_strbuf_appendstr(b, str); + ecs_strbuf_appendstr(b, str); } -bool ecs_strbuf_list_appendstrn( +void ecs_strbuf_list_appendstrn( ecs_strbuf_t *b, const char *str, int32_t n) @@ -821,16 +580,12 @@ bool ecs_strbuf_list_appendstrn( ecs_assert(str != NULL, ECS_INVALID_PARAMETER, NULL); ecs_strbuf_list_next(b); - return ecs_strbuf_appendstrn(b, str, n); + ecs_strbuf_appendstrn(b, str, n); } int32_t ecs_strbuf_written( const ecs_strbuf_t *b) { ecs_assert(b != NULL, ECS_INVALID_PARAMETER, NULL); - if (b->current) { - return b->size + b->current->pos; - } else { - return 0; - } + return b->length; } diff --git a/vendors/flecs/src/datastructures/switch_list.c b/vendors/flecs/src/datastructures/switch_list.c index a5012f8ed..6aa70ac47 100644 --- a/vendors/flecs/src/datastructures/switch_list.c +++ b/vendors/flecs/src/datastructures/switch_list.c @@ -1,383 +1,204 @@ -/** - * @file datastructures/switch_list.c - * @brief Interleaved linked list for storing mutually exclusive values. - * - * Datastructure that stores N interleaved linked lists in an array. - * This allows for efficient storage of elements with mutually exclusive values. - * Each linked list has a header element which points to the index in the array - * that stores the first node of the list. Each list node points to the next - * array element. - * - * The datastructure allows for efficient storage and retrieval for values with - * mutually exclusive values, such as enumeration values. The linked list allows - * an application to obtain all elements for a given (enumeration) value without - * having to search. - * - * While the list accepts 64 bit values, it only uses the lower 32bits of the - * value for selecting the correct linked list. - * - * The switch list is used to store union relationships. - */ - #include "../private_api.h" -#ifdef FLECS_SANITIZE -static -void flecs_switch_verify_nodes( - ecs_switch_header_t *hdr, - ecs_switch_node_t *nodes) +static +ecs_switch_page_t* flecs_switch_page_ensure( + ecs_switch_t* sw, + uint32_t elem) { - if (!hdr) { - return; - } + int32_t page_index = FLECS_SPARSE_PAGE(elem); + ecs_vec_set_min_count_zeromem_t( + sw->hdrs.allocator, &sw->pages, ecs_switch_page_t, page_index + 1); - int32_t prev = -1, elem = hdr->element, count = 0; - while (elem != -1) { - ecs_assert(prev == nodes[elem].prev, ECS_INTERNAL_ERROR, NULL); - prev = elem; - elem = nodes[elem].next; - count ++; + ecs_switch_page_t *page = ecs_vec_get_t( + &sw->pages, ecs_switch_page_t, page_index); + if (!ecs_vec_count(&page->nodes)) { + ecs_vec_init_t(sw->hdrs.allocator, &page->nodes, ecs_switch_node_t, + FLECS_SPARSE_PAGE_SIZE); + ecs_vec_init_t(sw->hdrs.allocator, &page->values, uint64_t, + FLECS_SPARSE_PAGE_SIZE); + ecs_vec_set_min_count_zeromem_t(sw->hdrs.allocator, &page->nodes, + ecs_switch_node_t, FLECS_SPARSE_PAGE_SIZE); + ecs_vec_set_min_count_zeromem_t(sw->hdrs.allocator, &page->values, + uint64_t, FLECS_SPARSE_PAGE_SIZE); } - ecs_assert(count == hdr->count, ECS_INTERNAL_ERROR, NULL); + return page; } -#else -#define flecs_switch_verify_nodes(hdr, nodes) -#endif static -ecs_switch_header_t* flecs_switch_get_header( - const ecs_switch_t *sw, - uint64_t value) +ecs_switch_page_t* flecs_switch_page_get( + const ecs_switch_t* sw, + uint32_t elem) { - if (value == 0) { + int32_t page_index = FLECS_SPARSE_PAGE(elem); + if (page_index >= ecs_vec_count(&sw->pages)) { return NULL; } - return (ecs_switch_header_t*)ecs_map_get(&sw->hdrs, value); -} -static -ecs_switch_header_t *flecs_switch_ensure_header( - ecs_switch_t *sw, - uint64_t value) -{ - ecs_switch_header_t *node = flecs_switch_get_header(sw, value); - if (!node && (value != 0)) { - node = (ecs_switch_header_t*)ecs_map_ensure(&sw->hdrs, value); - node->count = 0; - node->element = -1; + ecs_switch_page_t *page = ecs_vec_get_t( + &sw->pages, ecs_switch_page_t, page_index); + if (!ecs_vec_count(&page->nodes)) { + return NULL; } - return node; + return page; } static -void flecs_switch_remove_node( - ecs_switch_header_t *hdr, - ecs_switch_node_t *nodes, - ecs_switch_node_t *node, - int32_t element) +void flecs_switch_page_fini( + ecs_switch_t* sw, + ecs_switch_page_t *page) { - ecs_assert(&nodes[element] == node, ECS_INTERNAL_ERROR, NULL); - - /* Update previous node/header */ - if (hdr->element == element) { - ecs_assert(node->prev == -1, ECS_INVALID_PARAMETER, NULL); - /* If this is the first node, update the header */ - hdr->element = node->next; - } else { - /* If this is not the first node, update the previous node to the - * removed node's next ptr */ - ecs_assert(node->prev != -1, ECS_INVALID_PARAMETER, NULL); - ecs_switch_node_t *prev_node = &nodes[node->prev]; - prev_node->next = node->next; + if (ecs_vec_count(&page->nodes)) { + ecs_vec_fini_t(sw->hdrs.allocator, &page->nodes, ecs_switch_node_t); + ecs_vec_fini_t(sw->hdrs.allocator, &page->values, uint64_t); } +} - /* Update next node */ - int32_t next = node->next; - if (next != -1) { - ecs_assert(next >= 0, ECS_INVALID_PARAMETER, NULL); - /* If this is not the last node, update the next node to point to the - * removed node's prev ptr */ - ecs_switch_node_t *next_node = &nodes[next]; - next_node->prev = node->prev; +static +ecs_switch_node_t* flecs_switch_get_node( + ecs_switch_t* sw, + uint32_t element) +{ + if (!element) { + return NULL; } - /* Decrease count of current header */ - hdr->count --; - ecs_assert(hdr->count >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_switch_page_t *page = flecs_switch_page_ensure(sw, element); + int32_t page_offset = FLECS_SPARSE_OFFSET(element); + return ecs_vec_get_t(&page->nodes, ecs_switch_node_t, page_offset); } void flecs_switch_init( - ecs_switch_t *sw, - ecs_allocator_t *allocator, - int32_t elements) + ecs_switch_t* sw, + ecs_allocator_t *allocator) { ecs_map_init(&sw->hdrs, allocator); - ecs_vec_init_t(allocator, &sw->nodes, ecs_switch_node_t, elements); - ecs_vec_init_t(allocator, &sw->values, uint64_t, elements); - - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); - uint64_t *values = ecs_vec_first(&sw->values); - - int i; - for (i = 0; i < elements; i ++) { - nodes[i].prev = -1; - nodes[i].next = -1; - values[i] = 0; - } -} - -void flecs_switch_clear( - ecs_switch_t *sw) -{ - ecs_map_clear(&sw->hdrs); - ecs_vec_fini_t(sw->hdrs.allocator, &sw->nodes, ecs_switch_node_t); - ecs_vec_fini_t(sw->hdrs.allocator, &sw->values, uint64_t); + ecs_vec_init_t(allocator, &sw->pages, ecs_switch_page_t, 0); } void flecs_switch_fini( - ecs_switch_t *sw) -{ - ecs_map_fini(&sw->hdrs); - ecs_vec_fini_t(sw->hdrs.allocator, &sw->nodes, ecs_switch_node_t); - ecs_vec_fini_t(sw->hdrs.allocator, &sw->values, uint64_t); -} - -void flecs_switch_add( - ecs_switch_t *sw) -{ - ecs_switch_node_t *node = ecs_vec_append_t(sw->hdrs.allocator, - &sw->nodes, ecs_switch_node_t); - uint64_t *value = ecs_vec_append_t(sw->hdrs.allocator, - &sw->values, uint64_t); - node->prev = -1; - node->next = -1; - *value = 0; -} - -void flecs_switch_set_count( - ecs_switch_t *sw, - int32_t count) -{ - int32_t old_count = ecs_vec_count(&sw->nodes); - if (old_count == count) { - return; - } - - ecs_vec_set_count_t(sw->hdrs.allocator, &sw->nodes, ecs_switch_node_t, count); - ecs_vec_set_count_t(sw->hdrs.allocator, &sw->values, uint64_t, count); - - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); - uint64_t *values = ecs_vec_first(&sw->values); - - int32_t i; - for (i = old_count; i < count; i ++) { - ecs_switch_node_t *node = &nodes[i]; - node->prev = -1; - node->next = -1; - values[i] = 0; - } -} - -int32_t flecs_switch_count( - ecs_switch_t *sw) -{ - ecs_assert(ecs_vec_count(&sw->values) == ecs_vec_count(&sw->nodes), - ECS_INTERNAL_ERROR, NULL); - return ecs_vec_count(&sw->values); -} - -void flecs_switch_ensure( - ecs_switch_t *sw, - int32_t count) + ecs_switch_t* sw) { - int32_t old_count = ecs_vec_count(&sw->nodes); - if (old_count >= count) { - return; + int32_t i, count = ecs_vec_count(&sw->pages); + ecs_switch_page_t *pages = ecs_vec_first(&sw->pages); + for (i = 0; i < count; i ++) { + flecs_switch_page_fini(sw, &pages[i]); } - - flecs_switch_set_count(sw, count); -} - -void flecs_switch_addn( - ecs_switch_t *sw, - int32_t count) -{ - int32_t old_count = ecs_vec_count(&sw->nodes); - flecs_switch_set_count(sw, old_count + count); + ecs_vec_fini_t(sw->hdrs.allocator, &sw->pages, ecs_switch_page_t); + ecs_map_fini(&sw->hdrs); } -void flecs_switch_set( +bool flecs_switch_set( ecs_switch_t *sw, - int32_t element, + uint32_t element, uint64_t value) { - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->nodes), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->values), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element >= 0, ECS_INVALID_PARAMETER, NULL); + ecs_switch_page_t *page = flecs_switch_page_ensure(sw, element); + int32_t page_offset = FLECS_SPARSE_OFFSET(element); - uint64_t *values = ecs_vec_first(&sw->values); - uint64_t cur_value = values[element]; - - /* If the node is already assigned to the value, nothing to be done */ - if (cur_value == value) { - return; + uint64_t *elem = ecs_vec_get_t(&page->values, uint64_t, page_offset); + if (elem[0] == value) { + return false; } - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); - ecs_switch_node_t *node = &nodes[element]; - - ecs_switch_header_t *dst_hdr = flecs_switch_ensure_header(sw, value); - ecs_switch_header_t *cur_hdr = flecs_switch_get_header(sw, cur_value); + ecs_switch_node_t *node = ecs_vec_get_t( + &page->nodes, ecs_switch_node_t, page_offset); - flecs_switch_verify_nodes(cur_hdr, nodes); - flecs_switch_verify_nodes(dst_hdr, nodes); + uint64_t prev_value = elem[0]; + if (prev_value) { + ecs_switch_node_t *prev = flecs_switch_get_node(sw, node->prev); + if (prev) { + prev->next = node->next; + } - /* If value is not 0, and dst_hdr is NULL, then this is not a valid value - * for this switch */ - ecs_assert(dst_hdr != NULL || !value, ECS_INVALID_PARAMETER, NULL); + ecs_switch_node_t *next = flecs_switch_get_node(sw, node->next); + if (next) { + next->prev = node->prev; + } - if (cur_hdr) { - flecs_switch_remove_node(cur_hdr, nodes, node, element); + if (!prev) { + uint64_t *hdr = ecs_map_get(&sw->hdrs, prev_value); + ecs_assert(hdr[0] == (uint64_t)element, ECS_INTERNAL_ERROR, NULL); + hdr[0] = (uint64_t)node->next; + } } - /* Now update the node itself by adding it as the first node of dst */ - node->prev = -1; - values[element] = value; + elem[0] = value; - if (dst_hdr) { - node->next = dst_hdr->element; + if (value) { + uint64_t *hdr = ecs_map_ensure(&sw->hdrs, value); - /* Also update the dst header */ - int32_t first = dst_hdr->element; - if (first != -1) { - ecs_assert(first >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_switch_node_t *first_node = &nodes[first]; - first_node->prev = element; + if (!hdr[0]) { + hdr[0] = (uint64_t)element; + node->next = 0; + } else { + ecs_switch_node_t *head = flecs_switch_get_node(sw, (uint32_t)hdr[0]); + ecs_assert(head->prev == 0, ECS_INTERNAL_ERROR, NULL); + head->prev = element; + + node->next = (uint32_t)hdr[0]; + hdr[0] = (uint64_t)element; + ecs_assert(node->next != element, ECS_INTERNAL_ERROR, NULL); } - dst_hdr->element = element; - dst_hdr->count ++; + node->prev = 0; } + + return true; } -void flecs_switch_remove( +bool flecs_switch_reset( ecs_switch_t *sw, - int32_t elem) + uint32_t element) { - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(elem < ecs_vec_count(&sw->nodes), ECS_INVALID_PARAMETER, NULL); - ecs_assert(elem >= 0, ECS_INVALID_PARAMETER, NULL); - - uint64_t *values = ecs_vec_first(&sw->values); - uint64_t value = values[elem]; - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); - ecs_switch_node_t *node = &nodes[elem]; - - /* If node is currently assigned to a case, remove it from the list */ - if (value != 0) { - ecs_switch_header_t *hdr = flecs_switch_get_header(sw, value); - ecs_assert(hdr != NULL, ECS_INTERNAL_ERROR, NULL); - - flecs_switch_verify_nodes(hdr, nodes); - flecs_switch_remove_node(hdr, nodes, node, elem); - } - - int32_t last_elem = ecs_vec_count(&sw->nodes) - 1; - if (last_elem != elem) { - ecs_switch_node_t *last = ecs_vec_last_t(&sw->nodes, ecs_switch_node_t); - int32_t next = last->next, prev = last->prev; - if (next != -1) { - ecs_switch_node_t *n = &nodes[next]; - n->prev = elem; - } - - if (prev != -1) { - ecs_switch_node_t *n = &nodes[prev]; - n->next = elem; - } else { - ecs_switch_header_t *hdr = flecs_switch_get_header(sw, values[last_elem]); - if (hdr && hdr->element != -1) { - ecs_assert(hdr->element == last_elem, - ECS_INTERNAL_ERROR, NULL); - hdr->element = elem; - } - } - } - - /* Remove element from arrays */ - ecs_vec_remove_t(&sw->nodes, ecs_switch_node_t, elem); - ecs_vec_remove_t(&sw->values, uint64_t, elem); + return flecs_switch_set(sw, element, 0); } uint64_t flecs_switch_get( const ecs_switch_t *sw, - int32_t element) + uint32_t element) { - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->nodes), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->values), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element >= 0, ECS_INVALID_PARAMETER, NULL); - - uint64_t *values = ecs_vec_first(&sw->values); - return values[element]; -} + ecs_switch_page_t *page = flecs_switch_page_get(sw, element); + if (!page) { + return 0; + } -ecs_vec_t* flecs_switch_values( - const ecs_switch_t *sw) -{ - return ECS_CONST_CAST(ecs_vec_t*, &sw->values); + int32_t page_offset = FLECS_SPARSE_OFFSET(element); + uint64_t *elem = ecs_vec_get_t(&page->values, uint64_t, page_offset); + return elem[0]; } -int32_t flecs_switch_case_count( +uint32_t flecs_switch_first( const ecs_switch_t *sw, uint64_t value) { - ecs_switch_header_t *hdr = flecs_switch_get_header(sw, value); + uint64_t *hdr = ecs_map_get(&sw->hdrs, value); if (!hdr) { return 0; } - return hdr->count; + return (uint32_t)hdr[0]; } -void flecs_switch_swap( - ecs_switch_t *sw, - int32_t elem_1, - int32_t elem_2) -{ - uint64_t v1 = flecs_switch_get(sw, elem_1); - uint64_t v2 = flecs_switch_get(sw, elem_2); - - flecs_switch_set(sw, elem_2, v1); - flecs_switch_set(sw, elem_1, v2); -} - -int32_t flecs_switch_first( +FLECS_DBG_API +uint32_t flecs_switch_next( const ecs_switch_t *sw, - uint64_t value) + uint32_t previous) { - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_switch_header_t *hdr = flecs_switch_get_header(sw, value); - if (!hdr) { - return -1; + ecs_switch_page_t *page = flecs_switch_page_get(sw, previous); + if (!page) { + return 0; } - return hdr->element; + int32_t offset = FLECS_SPARSE_OFFSET(previous); + ecs_switch_node_t *elem = ecs_vec_get_t( + &page->nodes, ecs_switch_node_t, offset); + return elem->next; } -int32_t flecs_switch_next( - const ecs_switch_t *sw, - int32_t element) +ecs_map_iter_t flecs_switch_targets( + const ecs_switch_t *sw) { - ecs_assert(sw != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(element < ecs_vec_count(&sw->nodes), ECS_INVALID_PARAMETER, NULL); - ecs_assert(element >= 0, ECS_INVALID_PARAMETER, NULL); - - ecs_switch_node_t *nodes = ecs_vec_first(&sw->nodes); - - return nodes[element].next; + return ecs_map_iter(&sw->hdrs); } diff --git a/vendors/flecs/src/datastructures/vec.c b/vendors/flecs/src/datastructures/vec.c index 3ae75c774..9de0024e4 100644 --- a/vendors/flecs/src/datastructures/vec.c +++ b/vendors/flecs/src/datastructures/vec.c @@ -5,7 +5,7 @@ #include "../private_api.h" -ecs_vec_t* ecs_vec_init( +void ecs_vec_init( ecs_allocator_t *allocator, ecs_vec_t *v, ecs_size_t size, @@ -25,7 +25,6 @@ ecs_vec_t* ecs_vec_init( #ifdef FLECS_SANITIZE v->elem_size = size; #endif - return v; } void ecs_vec_init_if( @@ -105,6 +104,31 @@ ecs_vec_t ecs_vec_copy( }; } +ecs_vec_t ecs_vec_copy_shrink( + ecs_allocator_t *allocator, + const ecs_vec_t *v, + ecs_size_t size) +{ + ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + int32_t count = v->count; + void *array = NULL; + if (count) { + if (allocator) { + array = flecs_dup(allocator, size * count, v->array); + } else { + array = ecs_os_memdup(v->array, size * count); + } + } + return (ecs_vec_t) { + .count = count, + .size = count, + .array = array +#ifdef FLECS_SANITIZE + , .elem_size = size +#endif + }; +} + void ecs_vec_reclaim( ecs_allocator_t *allocator, ecs_vec_t *v, @@ -187,7 +211,7 @@ void ecs_vec_set_min_count_zeromem( int32_t count = vec->count; if (count < elem_count) { ecs_vec_set_min_count(allocator, vec, size, elem_count); - ecs_os_memset(ECS_ELEM(vec->array, size, count), 0, + ecs_os_memset(ECS_ELEM(vec->array, size, count), 0, size * (elem_count - count)); } } @@ -276,6 +300,7 @@ void* ecs_vec_get( int32_t index) { ecs_san_assert(size == v->elem_size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(index >= 0, ECS_OUT_OF_RANGE, NULL); ecs_assert(index < v->count, ECS_OUT_OF_RANGE, NULL); return ECS_ELEM(v->array, size, index); } @@ -284,7 +309,7 @@ void* ecs_vec_last( const ecs_vec_t *v, ecs_size_t size) { - ecs_san_assert(!v->elem_size || size == v->elem_size, + ecs_san_assert(!v->elem_size || size == v->elem_size, ECS_INVALID_PARAMETER, NULL); return ECS_ELEM(v->array, size, v->count - 1); } diff --git a/vendors/flecs/src/each.c b/vendors/flecs/src/each.c new file mode 100644 index 000000000..478764af9 --- /dev/null +++ b/vendors/flecs/src/each.c @@ -0,0 +1,83 @@ +/** + * @file query/each.c + * @brief Simple iterator for a single component id. + */ + +#include "private_api.h" + +ecs_iter_t ecs_each_id( + const ecs_world_t *stage, + ecs_id_t id) +{ + ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); + + const ecs_world_t *world = ecs_get_world(stage); + + flecs_process_pending_tables(world); + + ecs_iter_t it = { + .real_world = ECS_CONST_CAST(ecs_world_t*, world), + .world = ECS_CONST_CAST(ecs_world_t*, stage), + .field_count = 1, + .next = ecs_each_next + }; + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return it; + } + + ecs_each_iter_t *each_iter = &it.priv_.iter.each; + each_iter->ids = id; + each_iter->sizes = 0; + if (idr->type_info) { + each_iter->sizes = idr->type_info->size; + } + + each_iter->sources = 0; + each_iter->trs = NULL; + flecs_table_cache_iter((ecs_table_cache_t*)idr, &each_iter->it); + + return it; +error: + return (ecs_iter_t){0}; +} + +bool ecs_each_next( + ecs_iter_t *it) +{ + ecs_each_iter_t *each_iter = &it->priv_.iter.each; + const ecs_table_record_t *next = flecs_table_cache_next( + &each_iter->it, ecs_table_record_t); + it->flags |= EcsIterIsValid; + if (next) { + each_iter->trs = next; + ecs_table_t *table = next->hdr.table; + it->table = table; + it->count = ecs_table_count(table); + it->entities = ecs_table_entities(table); + it->ids = &table->type.array[next->index]; + it->trs = &each_iter->trs; + it->sources = &each_iter->sources; + it->sizes = &each_iter->sizes; + it->set_fields = 1; + + return true; + } else { + return false; + } +} + +ecs_iter_t ecs_children( + const ecs_world_t *stage, + ecs_entity_t parent) +{ + return ecs_each_id(stage, ecs_childof(parent)); +} + +bool ecs_children_next( + ecs_iter_t *it) +{ + return ecs_each_next(it); +} diff --git a/vendors/flecs/src/entity.c b/vendors/flecs/src/entity.c index cd532f37b..7ae216e61 100644 --- a/vendors/flecs/src/entity.c +++ b/vendors/flecs/src/entity.c @@ -13,6 +13,10 @@ #include "private_api.h" #include +#ifdef FLECS_SCRIPT +#include "addons/script/script.h" +#endif + static const ecs_entity_t* flecs_bulk_new( ecs_world_t *world, @@ -31,7 +35,7 @@ typedef struct { } flecs_component_ptr_t; static -flecs_component_ptr_t flecs_get_component_w_index( +flecs_component_ptr_t flecs_table_get_component( ecs_table_t *table, int32_t column_index, int32_t row) @@ -40,7 +44,7 @@ flecs_component_ptr_t flecs_get_component_w_index( ecs_column_t *column = &table->data.columns[column_index]; return (flecs_component_ptr_t){ .ti = column->ti, - .ptr = ecs_vec_get(&column->data, column->size, row) + .ptr = ECS_ELEM(column->data, column->ti->size, row) }; error: return (flecs_component_ptr_t){0}; @@ -48,50 +52,60 @@ flecs_component_ptr_t flecs_get_component_w_index( static flecs_component_ptr_t flecs_get_component_ptr( - const ecs_world_t *world, ecs_table_t *table, int32_t row, - ecs_id_t id) + ecs_id_record_t *idr) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - ecs_table_record_t *tr = flecs_table_record_get(world, table, id); + if (!idr) { + return (flecs_component_ptr_t){0}; + } + + if (idr->flags & EcsIdIsSparse) { + ecs_entity_t entity = ecs_table_entities(table)[row]; + return (flecs_component_ptr_t){ + .ti = idr->type_info, + .ptr = flecs_sparse_get_any(idr->sparse, 0, entity) + }; + } + + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); if (!tr || (tr->column == -1)) { - ecs_check(tr == NULL, ECS_NOT_A_COMPONENT, NULL); return (flecs_component_ptr_t){0}; } - return flecs_get_component_w_index(table, tr->column, row); -error: - return (flecs_component_ptr_t){0}; + return flecs_table_get_component(table, tr->column, row); } static void* flecs_get_component( - const ecs_world_t *world, ecs_table_t *table, int32_t row, - ecs_id_t id) + ecs_id_record_t *idr) { - return flecs_get_component_ptr(world, table, row, id).ptr; + return flecs_get_component_ptr(table, row, idr).ptr; } void* flecs_get_base_component( const ecs_world_t *world, ecs_table_t *table, ecs_id_t id, - ecs_id_record_t *table_index, + ecs_id_record_t *idr, int32_t recur_depth) { - /* Cycle detected in IsA relationship */ - ecs_check(recur_depth < ECS_MAX_RECURSION, ECS_INVALID_PARAMETER, NULL); + ecs_check(recur_depth < ECS_MAX_RECURSION, ECS_INVALID_PARAMETER, + "cycle detected in IsA relationship"); /* Table (and thus entity) does not have component, look for base */ if (!(table->flags & EcsTableHasIsA)) { return NULL; } + if (!(idr->flags & EcsIdOnInstantiateInherit)) { + return NULL; + } + /* Exclude Name */ if (id == ecs_pair(ecs_id(EcsIdentifier), EcsName)) { return NULL; @@ -120,14 +134,17 @@ void* flecs_get_base_component( continue; } - const ecs_table_record_t *tr = - flecs_id_record_get_table(table_index, table); - if (!tr || tr->column == -1) { - ptr = flecs_get_base_component(world, table, id, table_index, + const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + ptr = flecs_get_base_component(world, table, id, idr, recur_depth + 1); } else { - int32_t row = ECS_RECORD_TO_ROW(r->row); - ptr = flecs_get_component_w_index(table, tr->column, row).ptr; + if (idr->flags & EcsIdIsSparse) { + return flecs_sparse_get_any(idr->sparse, 0, base); + } else { + int32_t row = ECS_RECORD_TO_ROW(r->row); + return flecs_table_get_component(table, tr->column, row).ptr; + } } } while (!ptr && (i < end)); @@ -157,7 +174,7 @@ void flecs_instantiate_slot( if (ecs_has_pair(world, parent, EcsIsA, slot_of)) { const char *name = ecs_get_name(world, slot); if (name == NULL) { - char *slot_of_str = ecs_get_fullpath(world, slot_of); + char *slot_of_str = ecs_get_path(world, slot_of); ecs_throw(ECS_INVALID_OPERATION, "prefab '%s' has unnamed " "slot (slots must be named)", slot_of_str); ecs_os_free(slot_of_str); @@ -183,8 +200,8 @@ void flecs_instantiate_slot( } if (slot == 0) { - char *slot_of_str = ecs_get_fullpath(world, slot_of); - char *slot_str = ecs_get_fullpath(world, slot); + char *slot_of_str = ecs_get_path(world, slot_of); + char *slot_str = ecs_get_path(world, slot); ecs_throw(ECS_INVALID_OPERATION, "'%s' is not in hierarchy for slot '%s'", slot_of_str, slot_str); @@ -200,8 +217,8 @@ void flecs_instantiate_slot( } while ((parent = ecs_get_target(world, parent, EcsChildOf, 0))); if (parent == 0) { - char *slot_of_str = ecs_get_fullpath(world, slot_of); - char *slot_str = ecs_get_fullpath(world, slot); + char *slot_of_str = ecs_get_path(world, slot_of); + char *slot_str = ecs_get_path(world, slot); ecs_throw(ECS_INVALID_OPERATION, "'%s' is not in hierarchy for slot '%s'", slot_of_str, slot_str); @@ -246,6 +263,42 @@ ecs_table_t* flecs_find_table_remove( return NULL; } +static +int32_t flecs_child_type_insert( + ecs_type_t *type, + void **component_data, + ecs_id_t id) +{ + int32_t i, count = type->count; + for (i = 0; i < count; i ++) { + ecs_id_t cur = type->array[i]; + if (cur == id) { + /* Id is already part of type */ + return -1; + } + + if (cur > id) { + /* A larger id was found so id can't be part of the type. */ + break; + } + } + + /* Assumes that the array has enough memory to store the new element. */ + int32_t to_move = type->count - i; + if (to_move) { + ecs_os_memmove(&type->array[i + 1], + &type->array[i], to_move * ECS_SIZEOF(ecs_id_t)); + ecs_os_memmove(&component_data[i + 1], + &component_data[i], to_move * ECS_SIZEOF(void*)); + } + + component_data[i] = NULL; + type->array[i] = id; + type->count ++; + + return i; +} + static void flecs_instantiate_children( ecs_world_t *world, @@ -253,7 +306,8 @@ void flecs_instantiate_children( ecs_table_t *table, int32_t row, int32_t count, - ecs_table_t *child_table) + ecs_table_t *child_table, + const ecs_instantiate_ctx_t *ctx) { if (!ecs_table_count(child_table)) { return; @@ -269,15 +323,13 @@ void flecs_instantiate_children( /* Instantiate child table for each instance */ /* Create component array for creating the table */ - ecs_type_t components = { - .array = ecs_os_alloca_n(ecs_entity_t, type_count + 1) - }; - + ecs_table_diff_t diff = { .added = {0}}; + diff.added.array = ecs_os_alloca_n(ecs_entity_t, type_count + 1); void **component_data = ecs_os_alloca_n(void*, type_count + 1); /* Copy in component identifiers. Find the base index in the component * array, since we'll need this to replace the base with the instance id */ - int j, i, childof_base_index = -1, pos = 0; + int j, i, childof_base_index = -1; for (i = 0; i < type_count; i ++) { ecs_id_t id = ids[i]; @@ -288,19 +340,9 @@ void flecs_instantiate_children( if ((id != ecs_pair(ecs_id(EcsIdentifier), EcsName)) && ECS_PAIR_FIRST(id) != EcsChildOf) { - if (id == EcsUnion) { - /* This should eventually be handled by the DontInherit property - * but right now there is no way to selectively apply it to - * EcsUnion itself: it would also apply to (Union, *) pairs, - * which would make all union relationships uninheritable. - * - * The reason this is explicitly skipped is so that slot - * instances don't all end up with the Union property. */ - continue; - } ecs_table_record_t *tr = &child_table->_->records[i]; ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; - if (idr->flags & EcsIdDontInherit) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { continue; } } @@ -321,19 +363,30 @@ void flecs_instantiate_children( * created children point to the instance and not the prefab */ if (ECS_HAS_RELATION(id, EcsChildOf) && (ECS_PAIR_SECOND(id) == (uint32_t)base)) { - childof_base_index = pos; + childof_base_index = diff.added.count; + } + + /* If this is a pure override, make sure we have a concrete version of the + * component. This relies on the fact that overrides always come after + * concrete components in the table type so we can check the components + * that have already been added to the child table type. */ + if (ECS_HAS_ID_FLAG(id, AUTO_OVERRIDE)) { + ecs_id_t concreteId = id & ~ECS_AUTO_OVERRIDE; + flecs_child_type_insert(&diff.added, component_data, concreteId); + continue; } int32_t storage_index = ecs_table_type_to_column_index(child_table, i); if (storage_index != -1) { - ecs_vec_t *column = &child_data->columns[storage_index].data; - component_data[pos] = ecs_vec_first(column); + component_data[diff.added.count] = + child_data->columns[storage_index].data; } else { - component_data[pos] = NULL; + component_data[diff.added.count] = NULL; } - components.array[pos] = id; - pos ++; + diff.added.array[diff.added.count] = id; + diff.added.count ++; + diff.added_flags |= flecs_id_flags_get(world, id); } /* Table must contain children of base */ @@ -341,46 +394,42 @@ void flecs_instantiate_children( /* If children are added to a prefab, make sure they are prefabs too */ if (table->flags & EcsTableIsPrefab) { - components.array[pos] = EcsPrefab; - component_data[pos] = NULL; - pos ++; + if (flecs_child_type_insert( + &diff.added, component_data, EcsPrefab) != -1) + { + childof_base_index ++; + } } - components.count = pos; - /* Instantiate the prefab child table for each new instance */ - ecs_entity_t *instances = ecs_vec_first(&table->data.entities); - int32_t child_count = ecs_vec_count(&child_data->entities); - bool has_union = child_table->flags & EcsTableHasUnion; + const ecs_entity_t *instances = ecs_table_entities(table); + int32_t child_count = ecs_table_count(child_table); + ecs_entity_t *child_ids = flecs_walloc_n(world, ecs_entity_t, child_count); for (i = row; i < count + row; i ++) { ecs_entity_t instance = instances[i]; - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); ecs_table_t *i_table = NULL; /* Replace ChildOf element in the component array with instance id */ - components.array[childof_base_index] = ecs_pair(EcsChildOf, instance); + diff.added.array[childof_base_index] = ecs_pair(EcsChildOf, instance); /* Find or create table */ - for (j = 0; j < components.count; j ++) { - i_table = flecs_find_table_add( - world, i_table, components.array[j], &diff); - } + i_table = flecs_table_find_or_create(world, &diff.added); ecs_assert(i_table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(i_table->type.count == components.count, + ecs_assert(i_table->type.count == diff.added.count, ECS_INTERNAL_ERROR, NULL); /* The instance is trying to instantiate from a base that is also * its parent. This would cause the hierarchy to instantiate itself * which would cause infinite recursion. */ - ecs_entity_t *children = ecs_vec_first(&child_data->entities); + const ecs_entity_t *children = ecs_table_entities(child_table); #ifdef FLECS_DEBUG for (j = 0; j < child_count; j ++) { ecs_entity_t child = children[j]; - ecs_check(child != instance, ECS_INVALID_PARAMETER, NULL); + ecs_check(child != instance, ECS_INVALID_PARAMETER, + "cycle detected in IsA relationship"); } #else /* Bit of boilerplate to ensure that we don't get warnings about the @@ -388,34 +437,48 @@ void flecs_instantiate_children( ecs_check(true, ECS_INVALID_OPERATION, NULL); #endif - /* Create children */ - int32_t child_row; - ecs_table_diff_t table_diff; - flecs_table_diff_build_noalloc(&diff, &table_diff); - const ecs_entity_t *i_children = flecs_bulk_new(world, i_table, NULL, - &components, child_count, component_data, false, &child_row, - &table_diff); - flecs_table_diff_builder_fini(world, &diff); + /* Attempt to reserve ids for children that have the same offset from + * the instance as from the base prefab. This ensures stable ids for + * instance children, even across networked applications. */ + ecs_instantiate_ctx_t ctx_cur = {base, instance}; + if (ctx) { + ctx_cur = *ctx; + } - /* If children have union relationships, initialize */ - if (has_union) { - ecs_table__t *meta = child_table->_; - ecs_assert(meta != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(i_table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t u, u_count = meta->sw_count; - for (u = 0; u < u_count; u ++) { - ecs_switch_t *src_sw = &meta->sw_columns[i]; - ecs_switch_t *dst_sw = &i_table->_->sw_columns[i]; - ecs_vec_t *v_src_values = flecs_switch_values(src_sw); - ecs_vec_t *v_dst_values = flecs_switch_values(dst_sw); - uint64_t *src_values = ecs_vec_first(v_src_values); - uint64_t *dst_values = ecs_vec_first(v_dst_values); - for (j = 0; j < child_count; j ++) { - dst_values[j] = src_values[j]; - } + for (j = 0; j < child_count; j ++) { + if ((uint32_t)children[j] < (uint32_t)ctx_cur.root_prefab) { + /* Child id is smaller than root prefab id, can't use offset */ + child_ids[j] = ecs_new(world); + continue; + } + + /* Get prefab offset, ignore lifecycle generation count */ + ecs_entity_t prefab_offset = + (uint32_t)children[j] - (uint32_t)ctx_cur.root_prefab; + ecs_assert(prefab_offset != 0, ECS_INTERNAL_ERROR, NULL); + + /* First check if any entity with the desired id exists */ + ecs_entity_t instance_child = (uint32_t)ctx_cur.root_instance + prefab_offset; + ecs_entity_t alive_id = flecs_entities_get_alive(world, instance_child); + if (alive_id && flecs_entities_is_alive(world, alive_id)) { + /* Alive entity with requested id exists, can't use offset id */ + child_ids[j] = ecs_new(world); + continue; } + + /* Id is not in use. Make it alive & match the generation of the instance. */ + instance_child = ctx_cur.root_instance + prefab_offset; + flecs_entities_make_alive(world, instance_child); + flecs_entities_ensure(world, instance_child); + ecs_assert(ecs_is_alive(world, instance_child), ECS_INTERNAL_ERROR, NULL); + child_ids[j] = instance_child; } + /* Create children */ + int32_t child_row; + const ecs_entity_t *i_children = flecs_bulk_new(world, i_table, child_ids, + &diff.added, child_count, component_data, false, &child_row, &diff); + /* If children are slots, add slot relationships to parent */ if (slot_of) { for (j = 0; j < child_count; j ++) { @@ -429,9 +492,11 @@ void flecs_instantiate_children( /* If prefab child table has children itself, recursively instantiate */ for (j = 0; j < child_count; j ++) { ecs_entity_t child = children[j]; - flecs_instantiate(world, child, i_table, child_row + j, 1); + flecs_instantiate(world, child, i_table, child_row + j, 1, &ctx_cur); } - } + } + + flecs_wfree_n(world, ecs_entity_t, child_count, child_ids); error: return; } @@ -441,11 +506,44 @@ void flecs_instantiate( ecs_entity_t base, ecs_table_t *table, int32_t row, - int32_t count) + int32_t count, + const ecs_instantiate_ctx_t *ctx) { ecs_record_t *record = flecs_entities_get_any(world, base); ecs_table_t *base_table = record->table; - if (!base_table || !(base_table->flags & EcsTableIsPrefab)) { + if (!base_table) { + return; + } + + /* If prefab has union relationships, also set them on instance */ + if (base_table->flags & EcsTableHasUnion) { + const ecs_entity_t *entities = ecs_table_entities(table); + ecs_id_record_t *union_idr = flecs_id_record_get(world, + ecs_pair(EcsWildcard, EcsUnion)); + ecs_assert(union_idr != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_table_record_t *tr = flecs_id_record_get_table( + union_idr, base_table); + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t i = 0, j, union_count = 0; + do { + ecs_id_t id = base_table->type.array[i]; + if (ECS_PAIR_SECOND(id) == EcsUnion) { + ecs_entity_t rel = ECS_PAIR_FIRST(id); + ecs_entity_t tgt = ecs_get_target(world, base, rel, 0); + ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); + + for (j = row; j < (row + count); j ++) { + ecs_add_pair(world, entities[j], rel, tgt); + } + + union_count ++; + } + + i ++; + } while (union_count < tr->count); + } + + if (!(base_table->flags & EcsTableIsPrefab)) { /* Don't instantiate children from base entities that aren't prefabs */ return; } @@ -453,46 +551,134 @@ void flecs_instantiate( ecs_id_record_t *idr = flecs_id_record_get(world, ecs_childof(base)); ecs_table_cache_iter_t it; if (idr && flecs_table_cache_all_iter((ecs_table_cache_t*)idr, &it)) { + ecs_os_perf_trace_push("flecs.instantiate"); const ecs_table_record_t *tr; while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { flecs_instantiate_children( - world, base, table, row, count, tr->hdr.table); + world, base, table, row, count, tr->hdr.table, ctx); } + ecs_os_perf_trace_pop("flecs.instantiate"); } } static -void flecs_set_union( +void flecs_sparse_on_add( ecs_world_t *world, ecs_table_t *table, int32_t row, - int32_t count, - const ecs_type_t *ids) + int32_t count, + const ecs_type_t *added, + bool construct) { - ecs_id_t *array = ids->array; - int32_t i, id_count = ids->count; + int32_t i, j; + for (i = 0; i < added->count; i ++) { + ecs_id_t id = added->array[i]; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr && idr->flags & EcsIdIsSparse) { + const ecs_type_info_t *ti = idr->type_info; + ecs_xtor_t ctor = ti->hooks.ctor; + ecs_iter_action_t on_add = ti->hooks.on_add; + const ecs_entity_t *entities = ecs_table_entities(table); + for (j = 0; j < count; j ++) { + ecs_entity_t e = entities[row + j]; + void *ptr = flecs_sparse_ensure(idr->sparse, 0, e); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + if (construct && ctor) { + ctor(ptr, 1, ti); + } - for (i = 0; i < id_count; i ++) { - ecs_id_t id = array[i]; + if (on_add) { + const ecs_table_record_t *tr = + flecs_id_record_get_table(idr, table); + flecs_invoke_hook(world, table, tr, count, row, + &entities[row + j],id, ti, EcsOnAdd, on_add); + } + } + } + } +} - if (ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_pair(EcsUnion, ECS_PAIR_FIRST(id))); - if (!idr) { - continue; +static +void flecs_sparse_on_remove( + ecs_world_t *world, + ecs_table_t *table, + int32_t row, + int32_t count, + const ecs_type_t *removed) +{ + int32_t i, j; + for (i = 0; i < removed->count; i ++) { + ecs_id_t id = removed->array[i]; + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr && idr->flags & EcsIdIsSparse) { + const ecs_type_info_t *ti = idr->type_info; + const ecs_table_record_t *tr = + flecs_id_record_get_table(idr, table); + ecs_xtor_t dtor = ti->hooks.dtor; + ecs_iter_action_t on_remove = ti->hooks.on_remove; + const ecs_entity_t *entities = ecs_table_entities(table); + for (j = 0; j < count; j ++) { + ecs_entity_t e = entities[row + j]; + if (on_remove) { + flecs_invoke_hook(world, table, tr, count, row, + &entities[row + j], id, ti, EcsOnRemove, on_remove); + } + void *ptr = flecs_sparse_remove_fast(idr->sparse, 0, e); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + if (dtor) { + dtor(ptr, 1, ti); + } } + } + } +} - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t column = tr->index - table->_->sw_offset; - ecs_switch_t *sw = &table->_->sw_columns[column]; - ecs_entity_t union_case = 0; - union_case = ecs_pair_second(world, id); - - int32_t r; - for (r = 0; r < count; r ++) { - flecs_switch_set(sw, row + r, union_case); +static +void flecs_union_on_add( + ecs_world_t *world, + ecs_table_t *table, + int32_t row, + int32_t count, + const ecs_type_t *added) +{ + int32_t i, j; + for (i = 0; i < added->count; i ++) { + ecs_id_t id = added->array[i]; + if (ECS_IS_PAIR(id)) { + ecs_id_t wc = ecs_pair(ECS_PAIR_FIRST(id), EcsUnion); + ecs_id_record_t *idr = flecs_id_record_get(world, wc); + if (idr && idr->flags & EcsIdIsUnion) { + const ecs_entity_t *entities = ecs_table_entities(table); + for (j = 0; j < count; j ++) { + ecs_entity_t e = entities[row + j]; + flecs_switch_set( + idr->sparse, (uint32_t)e, ecs_pair_second(world, id)); + } + } + } + } +} + +static +void flecs_union_on_remove( + ecs_world_t *world, + ecs_table_t *table, + int32_t row, + int32_t count, + const ecs_type_t *removed) +{ + int32_t i, j; + for (i = 0; i < removed->count; i ++) { + ecs_id_t id = removed->array[i]; + if (ECS_IS_PAIR(id)) { + ecs_id_t wc = ecs_pair(ECS_PAIR_FIRST(id), EcsUnion); + ecs_id_record_t *idr = flecs_id_record_get(world, wc); + if (idr && idr->flags & EcsIdIsUnion) { + const ecs_entity_t *entities = ecs_table_entities(table); + for (j = 0; j < count; j ++) { + ecs_entity_t e = entities[row + j]; + flecs_switch_reset(idr->sparse, (uint32_t)e); + } } } } @@ -505,20 +691,32 @@ void flecs_notify_on_add( ecs_table_t *other_table, int32_t row, int32_t count, - const ecs_type_t *added, - ecs_flags32_t flags) + const ecs_table_diff_t *diff, + ecs_flags32_t flags, + ecs_flags64_t set_mask, + bool construct, + bool sparse) { - ecs_assert(added != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(diff != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_t *added = &diff->added; if (added->count) { - ecs_flags32_t table_flags = table->flags; + ecs_flags32_t diff_flags = + diff->added_flags|(table->flags & EcsTableHasTraversable); + if (!diff_flags) { + return; + } + + if (sparse && (diff_flags & EcsTableHasSparse)) { + flecs_sparse_on_add(world, table, row, count, added, construct); + } - if (table_flags & EcsTableHasUnion) { - flecs_set_union(world, table, row, count, added); + if (diff_flags & EcsTableHasUnion) { + flecs_union_on_add(world, table, row, count, added); } - if (table_flags & (EcsTableHasOnAdd|EcsTableHasIsA|EcsTableHasTraversable)) { - flecs_emit(world, world, &(ecs_event_desc_t){ + if (diff_flags & (EcsTableHasOnAdd|EcsTableHasTraversable)) { + flecs_emit(world, world, set_mask, &(ecs_event_desc_t){ .event = EcsOnAdd, .ids = added, .table = table, @@ -538,23 +736,38 @@ void flecs_notify_on_remove( ecs_table_t *other_table, int32_t row, int32_t count, - const ecs_type_t *removed) + const ecs_table_diff_t *diff) { - ecs_assert(removed != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(diff != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_t *removed = &diff->removed; ecs_assert(count != 0, ECS_INTERNAL_ERROR, NULL); - if (removed->count && (table->flags & - (EcsTableHasOnRemove|EcsTableHasUnSet|EcsTableHasIsA|EcsTableHasTraversable))) - { - flecs_emit(world, world, &(ecs_event_desc_t) { - .event = EcsOnRemove, - .ids = removed, - .table = table, - .other_table = other_table, - .offset = row, - .count = count, - .observable = world - }); + if (removed->count) { + ecs_flags32_t diff_flags = + diff->removed_flags|(table->flags & EcsTableHasTraversable); + if (!diff_flags) { + return; + } + + if (diff_flags & EcsTableHasUnion) { + flecs_union_on_remove(world, table, row, count, removed); + } + + if (diff_flags & (EcsTableHasOnRemove|EcsTableHasTraversable)) { + flecs_emit(world, world, 0, &(ecs_event_desc_t) { + .event = EcsOnRemove, + .ids = removed, + .table = table, + .other_table = other_table, + .offset = row, + .count = count, + .observable = world + }); + } + + if (diff_flags & EcsTableHasSparse) { + flecs_sparse_on_remove(world, table, row, count, removed); + } } } @@ -588,8 +801,7 @@ void flecs_update_name_index( ecs_assert(names != NULL, ECS_INTERNAL_ERROR, NULL); int32_t i; - ecs_entity_t *entities = ecs_vec_get_t( - &dst->data.entities, ecs_entity_t, offset); + const ecs_entity_t *entities = &ecs_table_entities(dst)[offset]; for (i = 0; i < count; i ++) { ecs_entity_t e = entities[i]; EcsIdentifier *name = &names[i]; @@ -624,9 +836,10 @@ ecs_record_t* flecs_new_entity( record->table = table; record->row = ECS_ROW_TO_RECORD(row, record->row & ECS_ROW_FLAGS_MASK); - ecs_assert(ecs_vec_count(&table->data.entities) > row, - ECS_INTERNAL_ERROR, NULL); - flecs_notify_on_add(world, table, NULL, row, 1, &diff->added, evt_flags); + ecs_assert(ecs_table_count(table) > row, ECS_INTERNAL_ERROR, NULL); + flecs_notify_on_add( + world, table, NULL, row, 1, diff, evt_flags, 0, ctor, true); + ecs_assert(table == record->table, ECS_INTERNAL_ERROR, NULL); return record; } @@ -648,35 +861,36 @@ void flecs_move_entity( ecs_assert(src_table != dst_table, ECS_INTERNAL_ERROR, NULL); ecs_assert(src_table->type.count > 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(src_row >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ecs_vec_count(&src_table->data.entities) > src_row, - ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_table_count(src_table) > src_row, ECS_INTERNAL_ERROR, NULL); ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(record == flecs_entities_get(world, entity), ECS_INTERNAL_ERROR, NULL); + ecs_assert(record->table == src_table, ECS_INTERNAL_ERROR, NULL); /* Append new row to destination table */ int32_t dst_row = flecs_table_append(world, dst_table, entity, false, false); /* Invoke remove actions for removed components */ - flecs_notify_on_remove( - world, src_table, dst_table, src_row, 1, &diff->removed); + flecs_notify_on_remove(world, src_table, dst_table, src_row, 1, diff); /* Copy entity & components from src_table to dst_table */ flecs_table_move(world, entity, entity, dst_table, dst_row, src_table, src_row, ctor); + ecs_assert(record->table == src_table, ECS_INTERNAL_ERROR, NULL); /* Update entity index & delete old data after running remove actions */ record->table = dst_table; record->row = ECS_ROW_TO_RECORD(dst_row, record->row & ECS_ROW_FLAGS_MASK); flecs_table_delete(world, src_table, src_row, false); - flecs_notify_on_add( - world, dst_table, src_table, dst_row, 1, &diff->added, evt_flags); + flecs_notify_on_add(world, dst_table, src_table, dst_row, 1, diff, + evt_flags, 0, ctor, true); flecs_update_name_index(world, src_table, dst_table, dst_row, 1); + ecs_assert(record->table == dst_table, ECS_INTERNAL_ERROR, NULL); error: return; } @@ -691,7 +905,7 @@ void flecs_delete_entity( int32_t row = ECS_RECORD_TO_ROW(record->row); /* Invoke remove actions before deleting */ - flecs_notify_on_remove(world, table, NULL, row, 1, &diff->removed); + flecs_notify_on_remove(world, table, NULL, row, 1, diff); flecs_table_delete(world, table, row, true); } @@ -757,14 +971,18 @@ void flecs_commit( /* If source and destination table are the same no action is needed * * However, if a component was added in the process of traversing a * table, this suggests that a union relationship could have changed. */ - if (src_table) { + if (src_table && src_table->flags & EcsTableHasUnion) { + diff->added_flags |= EcsIdIsUnion; flecs_notify_on_add(world, src_table, src_table, - ECS_RECORD_TO_ROW(record->row), 1, &diff->added, evt_flags); + ECS_RECORD_TO_ROW(record->row), 1, diff, evt_flags, 0, + construct, true); } flecs_journal_end(); return; } + ecs_os_perf_trace_push("flecs.commit"); + if (src_table) { ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); flecs_table_traversable_add(dst_table, is_trav); @@ -800,7 +1018,9 @@ void flecs_commit( ECS_OUT_OF_RANGE, 0); ecs_check(entity >= world->info.min_id, ECS_OUT_OF_RANGE, 0); - } + } + + ecs_os_perf_trace_pop("flecs.commit"); error: flecs_journal_end(); @@ -841,8 +1061,7 @@ const ecs_entity_t* flecs_bulk_new( component_array.count = type.count; } - ecs_data_t *data = &table->data; - int32_t row = flecs_table_appendn(world, table, data, count, entities); + int32_t row = flecs_table_appendn(world, table, count, entities); /* Update entity index. */ int i; @@ -852,9 +1071,10 @@ const ecs_entity_t* flecs_bulk_new( r->row = ECS_ROW_TO_RECORD(row + i, 0); } - flecs_defer_begin(world, &world->stages[0]); - flecs_notify_on_add(world, table, NULL, row, count, &diff->added, - (component_data == NULL) ? 0 : EcsEventNoOnSet); + flecs_defer_begin(world, world->stages[0]); + + flecs_notify_on_add(world, table, NULL, row, count, diff, + (component_data == NULL) ? 0 : EcsEventNoOnSet, 0, true, true); if (component_data) { int32_t c_i; @@ -878,9 +1098,9 @@ const ecs_entity_t* flecs_bulk_new( int32_t index = tr->column; ecs_column_t *column = &table->data.columns[index]; ecs_type_info_t *ti = column->ti; - int32_t size = column->size; + int32_t size = ti->size; ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); - void *ptr = ecs_vec_get(&column->data, size, row); + void *ptr = ECS_ELEM(column->data, size, row); ecs_copy_t copy; ecs_move_t move; @@ -895,15 +1115,17 @@ const ecs_entity_t* flecs_bulk_new( int32_t j, storage_count = table->column_count; for (j = 0; j < storage_count; j ++) { + ecs_id_t id = flecs_column_id(table, j); ecs_type_t set_type = { - .array = &table->data.columns[j].id, + .array = &id, .count = 1 }; + flecs_notify_on_set(world, table, row, count, &set_type, true); } } - flecs_defer_end(world, &world->stages[0]); + flecs_defer_end(world, world->stages[0]); if (row_out) { *row_out = row; @@ -983,7 +1205,7 @@ void flecs_remove_id( } static -flecs_component_ptr_t flecs_get_mut( +flecs_component_ptr_t flecs_ensure( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t id, @@ -991,17 +1213,41 @@ flecs_component_ptr_t flecs_get_mut( { flecs_component_ptr_t dst = {0}; - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); ecs_check(r != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check((id & ECS_COMPONENT_MASK) == id || - ECS_HAS_ID_FLAG(id, PAIR), ECS_INVALID_PARAMETER, NULL); + ECS_HAS_ID_FLAG(id, PAIR), ECS_INVALID_PARAMETER, + "invalid component id specified for ensure"); - if (r->table) { - dst = flecs_get_component_ptr( - world, r->table, ECS_RECORD_TO_ROW(r->row), id); - if (dst.ptr) { - return dst; + ecs_id_record_t *idr = NULL; + ecs_table_t *table; + if ((table = r->table)) { + if (id < FLECS_HI_COMPONENT_ID) { + int16_t column_index = table->component_map[id]; + if (column_index > 0) { + ecs_column_t *column = &table->data.columns[column_index - 1]; + ecs_type_info_t *ti = column->ti; + dst.ptr = ECS_ELEM(column->data, ti->size, + ECS_RECORD_TO_ROW(r->row)); + dst.ti = ti; + return dst; + } else if (column_index < 0) { + column_index = flecs_ito(int16_t, -column_index - 1); + const ecs_table_record_t *tr = &table->_->records[column_index]; + idr = (ecs_id_record_t*)tr->hdr.cache; + if (idr->flags & EcsIdIsSparse) { + dst.ptr = flecs_sparse_get_any(idr->sparse, 0, entity); + dst.ti = idr->type_info; + return dst; + } + } + } else { + idr = flecs_id_record_get(world, id); + dst = flecs_get_component_ptr(table, ECS_RECORD_TO_ROW(r->row), idr); + if (dst.ptr) { + return dst; + } } } @@ -1009,13 +1255,16 @@ flecs_component_ptr_t flecs_get_mut( 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]); + flecs_defer_end(world, world->stages[0]); + flecs_defer_begin(world, world->stages[0]); + + if (!idr) { + idr = flecs_id_record_get(world, id); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + } ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(r->table->column_count != 0, ECS_INTERNAL_ERROR, NULL); - dst = flecs_get_component_ptr( - world, r->table, ECS_RECORD_TO_ROW(r->row), id); + dst = flecs_get_component_ptr(r->table, ECS_RECORD_TO_ROW(r->row), idr); error: return dst; } @@ -1023,41 +1272,44 @@ flecs_component_ptr_t flecs_get_mut( void flecs_invoke_hook( ecs_world_t *world, ecs_table_t *table, + const ecs_table_record_t *tr, int32_t count, int32_t row, - ecs_entity_t *entities, - void *ptr, + const ecs_entity_t *entities, ecs_id_t id, const ecs_type_info_t *ti, ecs_entity_t event, ecs_iter_action_t hook) { - int32_t defer = world->stages[0].defer; + int32_t defer = world->stages[0]->defer; if (defer < 0) { - world->stages[0].defer *= -1; + world->stages[0]->defer *= -1; } 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.trs[0] = tr; + it.row_fields = !!(((ecs_id_record_t*)tr->hdr.cache)->flags & EcsIdIsSparse); + it.ref_fields = it.row_fields; it.sizes = ECS_CONST_CAST(ecs_size_t*, &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.callback_ctx = ti->hooks.binding_ctx; it.count = count; it.offset = row; - flecs_iter_validate(&it); + it.flags = EcsIterIsValid; + hook(&it); ecs_iter_fini(&it); - world->stages[0].defer = defer; + world->stages[0]->defer = defer; } void flecs_notify_on_set( @@ -1069,40 +1321,47 @@ void flecs_notify_on_set( bool owned) { ecs_assert(ids != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_data_t *data = &table->data; - - ecs_entity_t *entities = ecs_vec_get_t( - &data->entities, ecs_entity_t, row); + const ecs_entity_t *entities = &ecs_table_entities(table)[row]; ecs_assert(entities != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert((row + count) <= ecs_vec_count(&data->entities), + ecs_assert((row + count) <= ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); if (owned) { int i; for (i = 0; i < ids->count; i ++) { ecs_id_t id = ids->array[i]; - const ecs_table_record_t *tr = flecs_table_record_get(world, - table, id); + ecs_id_record_t *idr = flecs_id_record_get(world, id); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *ti = idr->type_info; + ecs_iter_action_t on_set = ti->hooks.on_set; + if (!on_set) { + continue; + } + + const ecs_table_record_t *tr = + flecs_id_record_get_table(idr, table); ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(tr->column != -1, ECS_INTERNAL_ERROR, NULL); - ecs_assert(tr->count == 1, ECS_INTERNAL_ERROR, NULL); - int32_t index = tr->column; - ecs_column_t *column = &table->data.columns[index]; - const ecs_type_info_t *ti = column->ti; - ecs_iter_action_t on_set = ti->hooks.on_set; - if (on_set) { - ecs_vec_t *c = &column->data; - void *ptr = ecs_vec_get(c, column->size, row); - flecs_invoke_hook(world, table, count, row, entities, ptr, id, - ti, EcsOnSet, on_set); + if (idr->flags & EcsIdIsSparse) { + int32_t j; + for (j = 0; j < count; j ++) { + flecs_invoke_hook(world, table, tr, 1, row, &entities[j], + id, ti, EcsOnSet, on_set); + } + } else { + ecs_assert(tr->column != -1, ECS_INTERNAL_ERROR, NULL); + ecs_assert(tr->count == 1, ECS_INTERNAL_ERROR, NULL); + if (on_set) { + flecs_invoke_hook(world, table, tr, count, row, entities, + id, ti, EcsOnSet, on_set); + } } } } /* Run OnSet notifications */ if (table->flags & EcsTableHasOnSet && ids->count) { - flecs_emit(world, world, &(ecs_event_desc_t) { + flecs_emit(world, world, 0, &(ecs_event_desc_t) { .event = EcsOnSet, .ids = ids, .table = table, @@ -1149,7 +1408,8 @@ bool ecs_commit( const ecs_type_t *removed) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!ecs_is_deferred(world), ECS_INVALID_OPERATION, NULL); + ecs_check(!ecs_is_deferred(world), ECS_INVALID_OPERATION, + "commit cannot be called on stage or while world is deferred"); ecs_table_t *src_table = NULL; if (!record) { @@ -1161,9 +1421,13 @@ bool ecs_commit( if (added) { diff.added = *added; + diff.added_flags = table->flags & EcsTableAddEdgeFlags; } if (removed) { diff.removed = *removed; + if (src_table) { + diff.removed_flags = src_table->flags & EcsTableRemoveEdgeFlags; + } } ecs_defer_begin(world); @@ -1198,13 +1462,11 @@ ecs_id_t ecs_get_with( return 0; } -ecs_entity_t ecs_new_id( +ecs_entity_t ecs_new( ecs_world_t *world) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - const ecs_stage_t *stage = flecs_stage_from_readonly_world(world); - /* It is possible that the world passed to this function is a stage, so * make sure we have the actual world. Cast away const since this is one of * the few functions that may modify the world while it is in readonly mode, @@ -1213,14 +1475,15 @@ ecs_entity_t ecs_new_id( ECS_CONST_CAST(ecs_world_t*, ecs_get_world(world)); ecs_entity_t entity; - if (stage->async || (unsafe_world->flags & EcsWorldMultiThreaded)) { - /* When using an async stage or world is in multithreading mode, make - * sure OS API has threading functions initialized */ - ecs_assert(ecs_os_has_threading(), ECS_INVALID_OPERATION, NULL); + if (unsafe_world->flags & EcsWorldMultiThreaded) { + /* When world is in multithreading mode, make sure OS API has threading + * functions initialized */ + ecs_assert(ecs_os_has_threading(), ECS_INVALID_OPERATION, + "thread safe id creation unavailable: threading API not available"); /* Can't atomically increase number above max int */ ecs_assert(flecs_entities_max_id(unsafe_world) < UINT_MAX, - ECS_INVALID_OPERATION, NULL); + ECS_INVALID_OPERATION, "thread safe ids exhausted"); entity = (ecs_entity_t)ecs_os_ainc( (int32_t*)&flecs_entities_max_id(unsafe_world)); } else { @@ -1264,7 +1527,7 @@ ecs_entity_t ecs_new_low_id( if (!id || id >= FLECS_HI_COMPONENT_ID) { /* If the low component ids are depleted, return a regular entity id */ - id = ecs_new_id(unsafe_world); + id = ecs_new(unsafe_world); } else { flecs_entities_ensure(world, id); } @@ -1281,60 +1544,27 @@ ecs_entity_t ecs_new_w_id( ecs_id_t id) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!id || ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_entity_t entity = ecs_new_id(world); - - ecs_id_t ids[3]; - ecs_type_t to_add = { .array = ids, .count = 0 }; + ecs_entity_t entity = ecs_new(world); - if (id) { - ids[to_add.count ++] = id; - } - - ecs_id_t with = stage->with; - if (with) { - ids[to_add.count ++] = with; - } - - ecs_entity_t scope = stage->scope; - if (scope) { - if (!id || !ECS_HAS_RELATION(id, EcsChildOf)) { - ids[to_add.count ++] = ecs_pair(EcsChildOf, scope); - } + if (flecs_defer_add(stage, entity, id)) { + return entity; } - if (to_add.count) { - if (flecs_defer_add(stage, entity, to_add.array[0])) { - int i; - for (i = 1; i < to_add.count; i ++) { - flecs_defer_add(stage, entity, to_add.array[i]); - } - return entity; - } - int32_t i, count = to_add.count; - ecs_table_t *table = &world->store.root; - - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); - for (i = 0; i < count; i ++) { - table = flecs_find_table_add( - world, table, to_add.array[i], &diff); - } + ecs_table_diff_builder_t diff_builder = ECS_TABLE_DIFF_INIT; + flecs_table_diff_builder_init(world, &diff_builder); + ecs_table_t *table = flecs_find_table_add( + world, &world->store.root, id, &diff_builder); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_diff_t table_diff; - flecs_table_diff_build_noalloc(&diff, &table_diff); - ecs_record_t *r = flecs_entities_get(world, entity); - flecs_new_entity(world, entity, r, table, &table_diff, true, true); - flecs_table_diff_builder_fini(world, &diff); - } else { - if (flecs_defer_cmd(stage)) { - return entity; - } + ecs_table_diff_t diff; + flecs_table_diff_build_noalloc(&diff_builder, &diff); + ecs_record_t *r = flecs_entities_get(world, entity); + flecs_new_entity(world, entity, r, table, &diff, true, 0); + flecs_table_diff_builder_fini(world, &diff_builder); - flecs_entities_ensure(world, entity); - } flecs_defer_end(world, stage); return entity; @@ -1347,82 +1577,104 @@ ecs_entity_t ecs_new_w_table( ecs_table_t *table) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); flecs_stage_from_world(&world); - ecs_entity_t entity = ecs_new_id(world); + ecs_entity_t entity = ecs_new(world); ecs_record_t *r = flecs_entities_get(world, entity); + ecs_flags32_t flags = table->flags & EcsTableAddEdgeFlags; + if (table->flags & EcsTableHasIsA) { + flags |= EcsTableHasOnAdd; + } + + ecs_table_diff_t table_diff = { + .added = table->type, + .added_flags = flags + }; + + flecs_new_entity(world, entity, r, table, &table_diff, true, 0); - ecs_table_diff_t table_diff = { .added = table->type }; - flecs_new_entity(world, entity, r, table, &table_diff, true, true); return entity; error: return 0; } -#ifdef FLECS_PARSER +static +void flecs_copy_id( + ecs_world_t *world, + ecs_record_t *r, + ecs_id_t id, + size_t size, + void *dst_ptr, + void *src_ptr, + const ecs_type_info_t *ti) +{ + ecs_check(dst_ptr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(src_ptr != NULL, ECS_INVALID_PARAMETER, NULL); + + ecs_copy_t copy = ti->hooks.copy; + if (copy) { + copy(dst_ptr, src_ptr, 1, ti); + } else { + ecs_os_memcpy(dst_ptr, src_ptr, flecs_utosize(size)); + } + + flecs_table_mark_dirty(world, r->table, id); + + ecs_table_t *table = r->table; + if (table->flags & EcsTableHasOnSet || ti->hooks.on_set) { + ecs_type_t ids = { .array = &id, .count = 1 }; + flecs_notify_on_set( + world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, true); + } +error: + return; +} /* Traverse table graph by either adding or removing identifiers parsed from the * passed in expression. */ static -ecs_table_t *flecs_traverse_from_expr( +int flecs_traverse_from_expr( ecs_world_t *world, - ecs_table_t *table, const char *name, const char *expr, - ecs_table_diff_builder_t *diff, - bool replace_and, - bool *error) + ecs_vec_t *ids) { +#ifdef FLECS_SCRIPT const char *ptr = expr; if (ptr) { - ecs_term_t term = {0}; - while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term, NULL))){ - if (!ecs_term_is_initialized(&term)) { + ecs_id_t id = 0; + while (ptr[0] && (ptr = flecs_id_parse(world, name, ptr, &id))) { + if (!id) { break; } - if (!(term.first.flags & (EcsSelf|EcsUp))) { - term.first.flags = EcsSelf; - } - if (!(term.second.flags & (EcsSelf|EcsUp))) { - term.second.flags = EcsSelf; - } - if (!(term.src.flags & (EcsSelf|EcsUp))) { - term.src.flags = EcsSelf; - } - - if (ecs_term_finalize(world, &term)) { - ecs_term_fini(&term); - if (error) { - *error = true; - } - return NULL; - } - - if (!ecs_id_is_valid(world, term.id)) { - ecs_term_fini(&term); + if (!ecs_id_is_valid(world, id)) { + char *idstr = ecs_id_str(world, id); ecs_parser_error(name, expr, (ptr - expr), - "invalid term for add expression"); - return NULL; + "id %s is invalid for add expression", idstr); + ecs_os_free(idstr); + goto error; } - if (term.oper == EcsAnd || !replace_and) { - /* Regular AND expression */ - table = flecs_find_table_add(world, table, term.id, diff); - } - - ecs_term_fini(&term); + ecs_vec_append_t(&world->allocator, ids, ecs_id_t)[0] = id; } if (!ptr) { - if (error) { - *error = true; - } - return NULL; + goto error; } } - - return table; + return 0; +#else + (void)world; + (void)name; + (void)expr; + (void)ids; + ecs_err("cannot parse component expression: script addon required"); + goto error; +#endif +error: + return -1; } /* Add/remove components based on the parsed expression. This operation is @@ -1432,43 +1684,27 @@ void flecs_defer_from_expr( ecs_world_t *world, ecs_entity_t entity, const char *name, - const char *expr, - bool is_add, - bool replace_and) + const char *expr) { +#ifdef FLECS_SCRIPT const char *ptr = expr; if (ptr) { - ecs_term_t term = {0}; - while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term, NULL))) { - if (!ecs_term_is_initialized(&term)) { + ecs_id_t id = 0; + while (ptr[0] && (ptr = flecs_id_parse(world, name, ptr, &id))) { + if (!id) { break; } - - if (ecs_term_finalize(world, &term)) { - return; - } - - if (!ecs_id_is_valid(world, term.id)) { - ecs_term_fini(&term); - ecs_parser_error(name, expr, (ptr - expr), - "invalid term for add expression"); - return; - } - - if (term.oper == EcsAnd || !replace_and) { - /* Regular AND expression */ - if (is_add) { - ecs_add_id(world, entity, term.id); - } else { - ecs_remove_id(world, entity, term.id); - } - } - - ecs_term_fini(&term); + ecs_add_id(world, entity, id); } } -} +#else + (void)world; + (void)entity; + (void)name; + (void)expr; + ecs_err("cannot parse component expression: script addon required"); #endif +} /* If operation is not deferred, add components by finding the target * table and moving the entity towards it. */ @@ -1480,51 +1716,81 @@ int flecs_traverse_add( const ecs_entity_desc_t *desc, ecs_entity_t scope, ecs_id_t with, - bool flecs_new_entity, + bool new_entity, bool name_assigned) { const char *sep = desc->sep; const char *root_sep = desc->root_sep; ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; flecs_table_diff_builder_init(world, &diff); + ecs_vec_t ids; + + /* Add components from the 'add_expr' expression. Look up before naming + * entity, so that expression can't resolve to self. */ + ecs_vec_init_t(&world->allocator, &ids, ecs_id_t, 0); + if (desc->add_expr && ecs_os_strcmp(desc->add_expr, "0")) { + if (flecs_traverse_from_expr(world, name, desc->add_expr, &ids)) { + goto error; + } + } + + /* Set symbol */ + if (desc->symbol && desc->symbol[0]) { + const char *sym = ecs_get_symbol(world, result); + if (sym) { + ecs_assert(!ecs_os_strcmp(desc->symbol, sym), + ECS_INCONSISTENT_NAME, desc->symbol); + } else { + ecs_set_symbol(world, result, desc->symbol); + } + } + + /* If a name is provided but not yet assigned, add the Name component */ + if (name && !name_assigned) { + ecs_add_path_w_sep(world, result, scope, name, sep, root_sep); + } else if (new_entity && scope) { + ecs_add_pair(world, result, EcsChildOf, scope); + } /* Find existing table */ ecs_table_t *src_table = NULL, *table = NULL; ecs_record_t *r = flecs_entities_get(world, result); table = r->table; - /* If a name is provided but not yet assigned, add the Name component */ - if (name && !name_assigned) { - table = flecs_find_table_add(world, table, - ecs_pair(ecs_id(EcsIdentifier), EcsName), &diff); - } + /* Add components from the 'add' array */ + if (desc->add) { + int32_t i = 0; + ecs_id_t id; - /* Add components from the 'add' id array */ - int32_t i = 0; - ecs_id_t id; - const ecs_id_t *ids = desc->add; - while ((i < FLECS_ID_DESC_MAX) && (id = ids[i ++])) { - bool should_add = true; - if (ECS_HAS_ID_FLAG(id, PAIR) && ECS_PAIR_FIRST(id) == EcsChildOf) { - scope = ECS_PAIR_SECOND(id); - if ((!desc->id && desc->name) || (name && !name_assigned)) { - /* If name is added to entity, pass scope to add_path instead - * of adding it to the table. The provided name may have nested - * elements, in which case the parent provided here is not the - * parent the entity will end up with. */ - should_add = false; - } + while ((id = desc->add[i ++])) { + table = flecs_find_table_add(world, table, id, &diff); } - if (should_add) { + } + + /* Add components from the 'set' array */ + if (desc->set) { + int32_t i = 0; + ecs_id_t id; + + while ((id = desc->set[i ++].type)) { table = flecs_find_table_add(world, table, id, &diff); } } + /* Add ids from .expr */ + { + int32_t i, count = ecs_vec_count(&ids); + ecs_id_t *expr_ids = ecs_vec_first(&ids); + for (i = 0; i < count; i ++) { + table = flecs_find_table_add(world, table, expr_ids[i], &diff); + } + } + /* Find destination table */ /* If this is a new entity without a name, add the scope. If a name is * provided, the scope will be added by the add_path_w_sep function */ - if (flecs_new_entity) { - if (flecs_new_entity && scope && !name && !name_assigned) { + if (new_entity) { + if (new_entity && scope && !name && !name_assigned) { table = flecs_find_table_add( world, table, ecs_pair(EcsChildOf, scope), &diff); } @@ -1533,50 +1799,47 @@ int flecs_traverse_add( } } - /* Add components from the 'add_expr' expression */ - if (desc->add_expr && ecs_os_strcmp(desc->add_expr, "0")) { -#ifdef FLECS_PARSER - bool error = false; - table = flecs_traverse_from_expr( - world, table, name, desc->add_expr, &diff, true, &error); - if (error) { - flecs_table_diff_builder_fini(world, &diff); - return -1; - } -#else - ecs_abort(ECS_UNSUPPORTED, "parser addon is not available"); -#endif - } - /* Commit entity to destination table */ if (src_table != table) { - flecs_defer_begin(world, &world->stages[0]); + flecs_defer_begin(world, world->stages[0]); ecs_table_diff_t table_diff; flecs_table_diff_build_noalloc(&diff, &table_diff); flecs_commit(world, result, r, table, &table_diff, true, 0); flecs_table_diff_builder_fini(world, &diff); - flecs_defer_end(world, &world->stages[0]); + flecs_defer_end(world, world->stages[0]); } - /* Set name */ - if (name && !name_assigned) { - ecs_add_path_w_sep(world, result, scope, name, sep, root_sep); - ecs_assert(ecs_get_name(world, result) != NULL, - ECS_INTERNAL_ERROR, NULL); - } + /* Set component values */ + if (desc->set) { + table = r->table; + ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t i = 0, row = ECS_RECORD_TO_ROW(r->row); + const ecs_value_t *v; + + flecs_defer_begin(world, world->stages[0]); - if (desc->symbol && desc->symbol[0]) { - const char *sym = ecs_get_symbol(world, result); - if (sym) { - ecs_assert(!ecs_os_strcmp(desc->symbol, sym), - ECS_INCONSISTENT_NAME, desc->symbol); - } else { - ecs_set_symbol(world, result, desc->symbol); + while ((void)(v = &desc->set[i ++]), v->type) { + if (!v->ptr) { + continue; + } + ecs_assert(ECS_RECORD_TO_ROW(r->row) == row, ECS_INTERNAL_ERROR, NULL); + ecs_id_record_t *idr = flecs_id_record_get(world, v->type); + flecs_component_ptr_t ptr = flecs_get_component_ptr(table, row, idr); + ecs_check(ptr.ptr != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_info_t *ti = idr->type_info; + flecs_copy_id(world, r, v->type, + flecs_itosize(ti->size), ptr.ptr, v->ptr, ti); } + + flecs_defer_end(world, world->stages[0]); } flecs_table_diff_builder_fini(world, &diff); + ecs_vec_fini_t(&world->allocator, &ids, ecs_id_t); return 0; +error: + ecs_vec_fini_t(&world->allocator, &ids, ecs_id_t); + return -1; } /* When in deferred mode, we need to add/remove components one by one using @@ -1608,40 +1871,46 @@ void flecs_deferred_add_remove( } /* Add components from the 'add' id array */ - int32_t i = 0; - ecs_id_t id; - const ecs_id_t *ids = desc->add; - while ((i < FLECS_ID_DESC_MAX) && (id = ids[i ++])) { - bool defer = true; - if (ECS_HAS_ID_FLAG(id, PAIR) && ECS_PAIR_FIRST(id) == EcsChildOf) { - scope = ECS_PAIR_SECOND(id); - if (name && (!desc->id || !name_assigned)) { - /* New named entities are created by temporarily going out of - * readonly mode to ensure no duplicates are created. */ - defer = false; + if (desc->add) { + int32_t i = 0; + ecs_id_t id; + + while ((id = desc->add[i ++])) { + bool defer = true; + if (ECS_HAS_ID_FLAG(id, PAIR) && ECS_PAIR_FIRST(id) == EcsChildOf) { + scope = ECS_PAIR_SECOND(id); + if (name && (!desc->id || !name_assigned)) { + /* New named entities are created by temporarily going out of + * readonly mode to ensure no duplicates are created. */ + defer = false; + } + } + if (defer) { + ecs_add_id(world, entity, id); } } - if (defer) { - ecs_add_id(world, entity, id); + } + + /* Set component values */ + if (desc->set) { + int32_t i = 0; + const ecs_value_t *v; + while ((void)(v = &desc->set[i ++]), v->type) { + if (v->ptr) { + ecs_set_id(world, entity, v->type, 0, v->ptr); + } else { + ecs_add_id(world, entity, v->type); + } } } /* Add components from the 'add_expr' expression */ if (desc->add_expr) { -#ifdef FLECS_PARSER - flecs_defer_from_expr(world, entity, name, desc->add_expr, true, true); -#else - ecs_abort(ECS_UNSUPPORTED, "parser addon is not available"); -#endif + flecs_defer_from_expr(world, entity, name, desc->add_expr); } int32_t thread_count = ecs_get_stage_count(world); - /* Set name */ - if (name && !name_assigned) { - ecs_add_path_w_sep(world, entity, scope, name, sep, root_sep); - } - /* Set symbol */ if (desc->symbol) { const char *sym = ecs_get_symbol(world, entity); @@ -1656,6 +1925,11 @@ void flecs_deferred_add_remove( } } } + + /* Set name */ + if (name && !name_assigned) { + ecs_add_path_w_sep(world, entity, scope, name, sep, root_sep); + } } ecs_entity_t ecs_entity_init( @@ -1664,13 +1938,35 @@ ecs_entity_t ecs_entity_init( { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_entity_desc_t was not initialized to zero"); ecs_stage_t *stage = flecs_stage_from_world(&world); ecs_entity_t scope = stage->scope; ecs_id_t with = ecs_get_with(world); ecs_entity_t result = desc->id; +#ifdef FLECS_DEBUG + if (desc->add) { + ecs_id_t id; + int32_t i = 0; + while ((id = desc->add[i ++])) { + if (ECS_HAS_ID_FLAG(id, PAIR) && + (ECS_PAIR_FIRST(id) == EcsChildOf)) + { + if (desc->name) { + ecs_check(false, ECS_INVALID_PARAMETER, "%s: cannot set parent in " + "ecs_entity_desc_t::add, use ecs_entity_desc_t::parent", + desc->name); + } else { + ecs_check(false, ECS_INVALID_PARAMETER, "cannot set parent in " + "ecs_entity_desc_t::add, use ecs_entity_desc_t::parent"); + } + } + } + } +#endif + const char *name = desc->name; const char *sep = desc->sep; if (!sep) { @@ -1681,7 +1977,7 @@ ecs_entity_t ecs_entity_init( if (!name[0]) { name = NULL; } else if (flecs_name_is_id(name)){ - ecs_entity_t id = flecs_name_to_id(world, name); + ecs_entity_t id = flecs_name_to_id(name); if (!id) { return 0; } @@ -1719,21 +2015,15 @@ ecs_entity_t ecs_entity_init( } } + /* Parent field takes precedence over scope */ + if (desc->parent) { + scope = desc->parent; + } + /* Find or create entity */ if (!result) { if (name) { /* If add array contains a ChildOf pair, use it as scope instead */ - const ecs_id_t *ids = desc->add; - ecs_id_t id; - int32_t i = 0; - while ((i < FLECS_ID_DESC_MAX) && (id = ids[i ++])) { - if (ECS_HAS_ID_FLAG(id, PAIR) && - (ECS_PAIR_FIRST(id) == EcsChildOf)) - { - scope = ECS_PAIR_SECOND(id); - } - } - result = ecs_lookup_path_w_sep( world, scope, name, sep, root_sep, false); if (result) { @@ -1745,7 +2035,7 @@ ecs_entity_t ecs_entity_init( if (desc->use_low_id) { result = ecs_new_low_id(world); } else { - result = ecs_new_id(world); + result = ecs_new(world); } flecs_new_entity = true; ecs_assert(ecs_get_type(world, result) == NULL, @@ -1753,7 +2043,7 @@ ecs_entity_t ecs_entity_init( } } else { /* Make sure provided id is either alive or revivable */ - ecs_ensure(world, result); + ecs_make_alive(world, result); name_assigned = ecs_has_pair( world, result, ecs_id(EcsIdentifier), EcsName); @@ -1818,10 +2108,11 @@ const ecs_entity_t* ecs_bulk_init( ecs_world_t *world, const ecs_bulk_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_bulk_desc_t was not initialized to zero"); const ecs_entity_t *entities = desc->entities; int32_t count = desc->count; @@ -1834,7 +2125,7 @@ const ecs_entity_t* ecs_bulk_init( } else { int i; for (i = 0; i < count; i ++) { - ecs_ensure(world, entities[i]); + ecs_make_alive(world, entities[i]); } } @@ -1889,12 +2180,12 @@ void flecs_check_component( ecs_size_t alignment) { if (ptr->size != size) { - char *path = ecs_get_fullpath(world, result); + char *path = ecs_get_path(world, result); ecs_abort(ECS_INVALID_COMPONENT_SIZE, path); ecs_os_free(path); } if (ptr->alignment != alignment) { - char *path = ecs_get_fullpath(world, result); + char *path = ecs_get_path(world, result); ecs_abort(ECS_INVALID_COMPONENT_ALIGNMENT, path); ecs_os_free(path); } @@ -1906,7 +2197,8 @@ ecs_entity_t ecs_component_init( { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_component_desc_t was not initialized to 0"); /* If existing entity is provided, check if it is already registered as a * component and matches the size/alignment. This can prevent having to @@ -1929,11 +2221,11 @@ ecs_entity_t ecs_component_init( if (!result) { result = ecs_new_low_id(world); } else { - ecs_ensure(world, result); + ecs_make_alive(world, result); new_component = ecs_has(world, result, EcsComponent); } - EcsComponent *ptr = ecs_get_mut(world, result, EcsComponent); + EcsComponent *ptr = ecs_ensure(world, result, EcsComponent); if (!ptr->size) { ecs_assert(ptr->alignment == 0, ECS_INTERNAL_ERROR, NULL); ptr->size = desc->type.size; @@ -1952,6 +2244,10 @@ ecs_entity_t ecs_component_init( desc->type.size, desc->type.alignment); } + if (desc->type.name && new_component) { + ecs_entity(world, { .id = result, .name = desc->type.name }); + } + ecs_modified(world, result, EcsComponent); if (desc->type.size && @@ -1965,9 +2261,6 @@ ecs_entity_t ecs_component_init( world->info.last_component_id = result + 1; } - /* Ensure components cannot be deleted */ - ecs_add_pair(world, result, EcsOnDelete, EcsPanic); - flecs_resume_readonly(world, &readonly_state); ecs_assert(result != 0, ECS_INTERNAL_ERROR, NULL); @@ -2028,7 +2321,8 @@ void ecs_clear( ecs_table_t *table = r->table; if (table) { ecs_table_diff_t diff = { - .removed = table->type + .removed = table->type, + .removed_flags = table->flags & EcsTableRemoveEdgeFlags }; flecs_delete_entity(world, r, &diff); @@ -2052,10 +2346,13 @@ void flecs_throw_invalid_delete( char *id_str = NULL; if (!(world->flags & EcsWorldQuit)) { id_str = ecs_id_str(world, id); - ecs_throw(ECS_CONSTRAINT_VIOLATED, id_str); - } -error: - ecs_os_free(id_str); + ecs_err("(OnDelete, Panic) constraint violated while deleting %s", + id_str); + ecs_os_free(id_str); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } } static @@ -2089,8 +2386,8 @@ void flecs_targets_mark_for_delete( ecs_table_t *table) { ecs_id_record_t *idr; - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); - int32_t i, count = ecs_vec_count(&table->data.entities); + const ecs_entity_t *entities = ecs_table_entities(table); + int32_t i, count = ecs_table_count(table); for (i = 0; i < count; i ++) { ecs_record_t *r = flecs_entities_get(world, entities[i]); if (!r) { @@ -2306,7 +2603,7 @@ void flecs_remove_from_table( ecs_world_t *world, ecs_table_t *table) { - ecs_table_diff_t temp_diff; + ecs_table_diff_t temp_diff = { .added = {0} }; ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; flecs_table_diff_builder_init(world, &diff); ecs_table_t *dst_table = table; @@ -2326,8 +2623,10 @@ void flecs_remove_from_table( do { ecs_id_t id = dst_table->type.array[t]; - dst_table = flecs_table_traverse_remove( + ecs_table_t *tgt_table = flecs_table_traverse_remove( world, dst_table, &id, &temp_diff); + ecs_assert(tgt_table != dst_table, ECS_INTERNAL_ERROR, NULL); + dst_table = tgt_table; flecs_table_diff_build_append_table(world, &diff, &temp_diff); } while (dst_table->type.count && (t = ecs_search_offset( world, dst_table, t, idr->id, NULL)) != -1); @@ -2346,13 +2645,11 @@ void flecs_remove_from_table( ecs_log_push_3(); ecs_table_diff_t td; flecs_table_diff_build_noalloc(&diff, &td); - flecs_notify_on_remove(world, table, NULL, 0, table_count, - &td.removed); + flecs_notify_on_remove(world, table, NULL, 0, table_count, &td); ecs_log_pop_3(); } - flecs_table_merge(world, dst_table, table, - &dst_table->data, &table->data); + flecs_table_merge(world, dst_table, table); } } @@ -2394,9 +2691,9 @@ bool flecs_on_delete_clear_tables( } /* Run commands so children get notified before parent is deleted */ - if (world->stages[0].defer) { - flecs_defer_end(world, &world->stages[0]); - flecs_defer_begin(world, &world->stages[0]); + if (world->stages[0]->defer) { + flecs_defer_end(world, world->stages[0]); + flecs_defer_begin(world, world->stages[0]); } /* User code (from triggers) could have enqueued more ids to delete, @@ -2481,7 +2778,7 @@ void flecs_on_delete( /* Cleanup can happen recursively. If a cleanup action is already in * progress, only append ids to the marked_ids. The topmost cleanup * frame will handle the actual cleanup. */ - int32_t count = ecs_vec_count(&world->store.marked_ids); + int32_t i, count = ecs_vec_count(&world->store.marked_ids); /* Make sure we're evaluating a consistent list of non-empty tables */ ecs_run_aperiodic(world, EcsAperiodicEmptyTables); @@ -2506,7 +2803,7 @@ void flecs_on_delete( /* Verify deleted ids are no longer in use */ #ifdef FLECS_DEBUG ecs_marked_id_t *ids = ecs_vec_first(&world->store.marked_ids); - int32_t i; count = ecs_vec_count(&world->store.marked_ids); + count = ecs_vec_count(&world->store.marked_ids); for (i = 0; i < count; i ++) { ecs_assert(!ecs_id_in_use(world, ids[i].id), ECS_INTERNAL_ERROR, NULL); @@ -2517,6 +2814,16 @@ void flecs_on_delete( /* Ids are deleted, clear stack */ ecs_vec_clear(&world->store.marked_ids); + /* If any components got deleted, cleanup type info. Delaying this + * ensures that type info remains available during cleanup. */ + count = ecs_vec_count(&world->store.deleted_components); + ecs_entity_t *comps = ecs_vec_first(&world->store.deleted_components); + for (i = 0; i < count; i ++) { + flecs_type_info_free(world, comps[i]); + } + + ecs_vec_clear(&world->store.deleted_components); + ecs_log_pop_2(); } } @@ -2567,6 +2874,8 @@ void ecs_delete( return; } + ecs_os_perf_trace_push("flecs.delete"); + ecs_record_t *r = flecs_entities_try(world, entity); if (r) { flecs_journal_begin(world, EcsJournalDelete, entity, NULL, NULL); @@ -2598,7 +2907,8 @@ void ecs_delete( if (table) { ecs_table_diff_t diff = { - .removed = table->type + .removed = table->type, + .removed_flags = table->flags & EcsTableRemoveEdgeFlags }; flecs_delete_entity(world, r, &diff); @@ -2614,6 +2924,7 @@ void ecs_delete( flecs_defer_end(world, stage); error: + ecs_os_perf_trace_pop("flecs.delete"); return; } @@ -2644,13 +2955,13 @@ void ecs_remove_id( return; } -void ecs_override_id( +void ecs_auto_override_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id) { ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - ecs_add_id(world, entity, ECS_OVERRIDE | id); + ecs_add_id(world, entity, ECS_AUTO_OVERRIDE | id); error: return; } @@ -2668,7 +2979,7 @@ ecs_entity_t ecs_clone( ecs_stage_t *stage = flecs_stage_from_world(&world); if (!dst) { - dst = ecs_new_id(world); + dst = ecs_new(world); } if (flecs_defer_clone(stage, dst, src, copy_value)) { @@ -2689,9 +3000,14 @@ ecs_entity_t ecs_clone( } ecs_type_t dst_type = dst_table->type; - ecs_table_diff_t diff = { .added = dst_type }; + ecs_table_diff_t diff = { + .added = dst_type, + .added_flags = dst_table->flags & EcsTableAddEdgeFlags + }; ecs_record_t *dst_r = flecs_entities_get(world, dst); - flecs_new_entity(world, dst, dst_r, dst_table, &diff, true, true); + /* Note 'ctor' parameter below is set to 'false' if the value will be copied, since flecs_table_move + will call a copy constructor */ + flecs_new_entity(world, dst, dst_r, dst_table, &diff, !copy_value, 0); int32_t row = ECS_RECORD_TO_ROW(dst_r->row); if (copy_value) { @@ -2699,8 +3015,9 @@ ecs_entity_t ecs_clone( row, src_table, ECS_RECORD_TO_ROW(src_r->row), true); int32_t i, count = dst_table->column_count; for (i = 0; i < count; i ++) { + ecs_id_t id = flecs_column_id(dst_table, i); ecs_type_t type = { - .array = &dst_table->data.columns[i].id, + .array = &id, .count = 1 }; flecs_notify_on_set(world, dst_table, row, 1, &type, true); @@ -2714,6 +3031,22 @@ ecs_entity_t ecs_clone( return 0; } +#define ecs_get_low_id(table, r, id)\ + ecs_assert(table->component_map != NULL, ECS_INTERNAL_ERROR, NULL);\ + int16_t column_index = table->component_map[id];\ + if (column_index > 0) {\ + ecs_column_t *column = &table->data.columns[column_index - 1];\ + return ECS_ELEM(column->data, column->ti->size, \ + ECS_RECORD_TO_ROW(r->row));\ + } else if (column_index < 0) {\ + column_index = flecs_ito(int16_t, -column_index - 1);\ + const ecs_table_record_t *tr = &table->_->records[column_index];\ + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache;\ + if (idr->flags & EcsIdIsSparse) {\ + return flecs_sparse_get_any(idr->sparse, 0, entity);\ + }\ + } + const void* ecs_get_id( const ecs_world_t *world, ecs_entity_t entity, @@ -2721,8 +3054,6 @@ const void* ecs_get_id( { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_check(flecs_stage_from_readonly_world(world)->async == false, - ECS_INVALID_PARAMETER, NULL); world = ecs_get_world(world); @@ -2734,6 +3065,13 @@ const void* ecs_get_id( return NULL; } + if (id < FLECS_HI_COMPONENT_ID) { + ecs_get_low_id(table, r, id); + if (!(table->flags & EcsTableHasIsA)) { + return NULL; + } + } + ecs_id_record_t *idr = flecs_id_record_get(world, id); if (!idr) { return NULL; @@ -2741,18 +3079,51 @@ const void* ecs_get_id( const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); if (!tr) { - return flecs_get_base_component(world, table, id, idr, 0); + return flecs_get_base_component(world, table, id, idr, 0); } else { + if (idr->flags & EcsIdIsSparse) { + return flecs_sparse_get_any(idr->sparse, 0, entity); + } ecs_check(tr->column != -1, ECS_NOT_A_COMPONENT, NULL); } int32_t row = ECS_RECORD_TO_ROW(r->row); - return flecs_get_component_w_index(table, tr->column, row).ptr; + return flecs_table_get_component(table, tr->column, row).ptr; error: return NULL; } void* ecs_get_mut_id( + const ecs_world_t *world, + ecs_entity_t entity, + ecs_id_t id) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + + world = ecs_get_world(world); + + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_assert(r != NULL, ECS_INVALID_PARAMETER, NULL); + + ecs_table_t *table = r->table; + if (!table) { + return NULL; + } + + if (id < FLECS_HI_COMPONENT_ID) { + ecs_get_low_id(table, r, id); + return NULL; + } + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + int32_t row = ECS_RECORD_TO_ROW(r->row); + return flecs_get_component_ptr(table, row, idr).ptr; +error: + return NULL; +} + +void* ecs_ensure_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id) @@ -2763,12 +3134,13 @@ void* ecs_get_mut_id( ecs_stage_t *stage = flecs_stage_from_world(&world); if (flecs_defer_cmd(stage)) { - return flecs_defer_set(world, stage, EcsCmdMut, entity, id, 0, NULL); + return flecs_defer_set( + world, stage, EcsCmdEnsure, entity, id, 0, NULL, NULL); } 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; + void *result = flecs_ensure(world, entity, id, r).ptr; ecs_check(result != NULL, ECS_INVALID_PARAMETER, NULL); flecs_defer_end(world, stage); @@ -2777,7 +3149,7 @@ void* ecs_get_mut_id( return NULL; } -void* ecs_get_mut_modified_id( +void* ecs_ensure_modified_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id) @@ -2789,7 +3161,7 @@ void* ecs_get_mut_modified_id( ecs_stage_t *stage = flecs_stage_from_world(&world); ecs_check(flecs_defer_cmd(stage), ECS_INVALID_PARAMETER, NULL); - return flecs_defer_set(world, stage, EcsCmdSet, entity, id, 0, NULL); + return flecs_defer_set(world, stage, EcsCmdSet, entity, id, 0, NULL, NULL); error: return NULL; } @@ -2876,19 +3248,19 @@ ecs_entity_t ecs_record_get_entity( return 0; } - return ecs_vec_get_t(&table->data.entities, ecs_entity_t, - ECS_RECORD_TO_ROW(record->row))[0]; + return table->data.entities[ECS_RECORD_TO_ROW(record->row)]; error: return 0; } const void* ecs_record_get_id( - ecs_world_t *stage, + const ecs_world_t *stage, const ecs_record_t *r, ecs_id_t id) { const ecs_world_t *world = ecs_get_world(stage); - return flecs_get_component(world, r->table, ECS_RECORD_TO_ROW(r->row), id); + ecs_id_record_t *idr = flecs_id_record_get(world, id); + return flecs_get_component(r->table, ECS_RECORD_TO_ROW(r->row), idr); } bool ecs_record_has_id( @@ -2903,13 +3275,14 @@ bool ecs_record_has_id( return false; } -void* ecs_record_get_mut_id( +void* ecs_record_ensure_id( ecs_world_t *stage, ecs_record_t *r, ecs_id_t id) { const ecs_world_t *world = ecs_get_world(stage); - return flecs_get_component(world, r->table, ECS_RECORD_TO_ROW(r->row), id); + ecs_id_record_t *idr = flecs_id_record_get(world, id); + return flecs_get_component(r->table, ECS_RECORD_TO_ROW(r->row), idr); } ecs_ref_t ecs_ref_init_id( @@ -2935,6 +3308,7 @@ ecs_ref_t ecs_ref_init_id( ecs_table_t *table = record->table; if (table) { result.tr = flecs_table_record_get(world, table, id); + result.table_id = table->id; } return result; @@ -2942,6 +3316,17 @@ ecs_ref_t ecs_ref_init_id( return (ecs_ref_t){0}; } +static +bool flecs_ref_needs_sync( + ecs_ref_t *ref, + ecs_table_record_t *tr, + const ecs_table_t *table) +{ + ecs_assert(ref != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + return !tr || ref->table_id != table->id || tr->hdr.table != table; +} + void ecs_ref_update( const ecs_world_t *world, ecs_ref_t *ref) @@ -2959,13 +3344,14 @@ void ecs_ref_update( } ecs_table_record_t *tr = ref->tr; - if (!tr || tr->hdr.table != table) { + if (flecs_ref_needs_sync(ref, tr, table)) { tr = ref->tr = flecs_table_record_get(world, table, ref->id); if (!tr) { return; } ecs_assert(tr->hdr.table == r->table, ECS_INTERNAL_ERROR, NULL); + ref->table_id = table->id; } error: return; @@ -2978,10 +3364,10 @@ void* ecs_ref_get_id( { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(ref != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref->entity != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref->id != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(ref->record != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(id == ref->id, ECS_INVALID_PARAMETER, NULL); + ecs_check(ref->entity != 0, ECS_INVALID_PARAMETER, "ref not initialized"); + ecs_check(ref->id != 0, ECS_INVALID_PARAMETER, "ref not initialized"); + ecs_check(ref->record != NULL, ECS_INVALID_PARAMETER, "ref not initialized"); + ecs_check(id == ref->id, ECS_INVALID_PARAMETER, "ref not initialized"); ecs_record_t *r = ref->record; ecs_table_t *table = r->table; @@ -2993,18 +3379,24 @@ void* ecs_ref_get_id( ecs_check(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); ecs_table_record_t *tr = ref->tr; - if (!tr || tr->hdr.table != table) { + if (flecs_ref_needs_sync(ref, tr, table)) { tr = ref->tr = flecs_table_record_get(world, table, id); if (!tr) { return NULL; } + ref->table_id = table->id; ecs_assert(tr->hdr.table == r->table, ECS_INTERNAL_ERROR, NULL); } int32_t column = tr->column; - ecs_assert(column != -1, ECS_INTERNAL_ERROR, NULL); - return flecs_get_component_w_index(table, column, row).ptr; + if (column == -1) { + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + if (idr->flags & EcsIdIsSparse) { + return flecs_sparse_get_any(idr->sparse, 0, ref->entity); + } + } + return flecs_table_get_component(table, column, row).ptr; error: return NULL; } @@ -3012,26 +3404,37 @@ void* ecs_ref_get_id( void* ecs_emplace_id( ecs_world_t *world, ecs_entity_t entity, - ecs_id_t id) + ecs_id_t id, + bool *is_new) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - ecs_check(!ecs_has_id(world, entity, id), ECS_INVALID_PARAMETER, - "cannot emplace a component the entity already has"); ecs_stage_t *stage = flecs_stage_from_world(&world); if (flecs_defer_cmd(stage)) { - return flecs_defer_set(world, stage, EcsCmdEmplace, entity, id, 0, NULL); + return flecs_defer_set( + world, stage, EcsCmdEmplace, entity, id, 0, NULL, is_new); } + ecs_check(is_new || !ecs_has_id(world, entity, id), ECS_INVALID_PARAMETER, + "cannot emplace a component the entity already has"); + ecs_record_t *r = flecs_entities_get(world, entity); + ecs_table_t *table = r->table; flecs_add_id_w_record(world, entity, r, id, false /* Add without ctor */); flecs_defer_end(world, stage); - void *ptr = flecs_get_component(world, r->table, ECS_RECORD_TO_ROW(r->row), id); - ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_id_record_t *idr = flecs_id_record_get(world, id); + void *ptr = flecs_get_component(r->table, ECS_RECORD_TO_ROW(r->row), idr); + ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, + "emplaced component was removed during operation, make sure to not " + "remove component T in on_add(T) hook/OnAdd(T) observer"); + + if (is_new) { + *is_new = table != r->table; + } return ptr; error: @@ -3104,7 +3507,7 @@ void ecs_modified_id( } static -void flecs_copy_ptr_w_id( +void flecs_set_id_copy( ecs_world_t *world, ecs_stage_t *stage, ecs_entity_t entity, @@ -3114,42 +3517,20 @@ void flecs_copy_ptr_w_id( { if (flecs_defer_cmd(stage)) { flecs_defer_set(world, stage, EcsCmdSet, entity, id, - flecs_utosize(size), ptr); + flecs_utosize(size), ptr, NULL); return; } ecs_record_t *r = flecs_entities_get(world, entity); - flecs_component_ptr_t dst = flecs_get_mut(world, entity, id, r); - const ecs_type_info_t *ti = dst.ti; - ecs_check(dst.ptr != NULL, ECS_INVALID_PARAMETER, NULL); - - if (ptr) { - ecs_copy_t copy = ti->hooks.copy; - if (copy) { - copy(dst.ptr, ptr, 1, ti); - } else { - ecs_os_memcpy(dst.ptr, ptr, flecs_utosize(size)); - } - } else { - ecs_os_memset(dst.ptr, 0, size); - } - - flecs_table_mark_dirty(world, r->table, id); + flecs_component_ptr_t dst = flecs_ensure(world, entity, id, r); - ecs_table_t *table = r->table; - if (table->flags & EcsTableHasOnSet || ti->hooks.on_set) { - ecs_type_t ids = { .array = &id, .count = 1 }; - flecs_notify_on_set( - world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, true); - } + flecs_copy_id(world, r, id, size, dst.ptr, ptr, dst.ti); flecs_defer_end(world, stage); -error: - return; } static -void flecs_move_ptr_w_id( +void flecs_set_id_move( ecs_world_t *world, ecs_stage_t *stage, ecs_entity_t entity, @@ -3160,19 +3541,19 @@ void flecs_move_ptr_w_id( { if (flecs_defer_cmd(stage)) { flecs_defer_set(world, stage, cmd_kind, entity, id, - flecs_utosize(size), ptr); + flecs_utosize(size), ptr, NULL); return; } ecs_record_t *r = flecs_entities_get(world, entity); - flecs_component_ptr_t dst = flecs_get_mut(world, entity, id, r); + flecs_component_ptr_t dst = flecs_ensure(world, entity, id, r); ecs_check(dst.ptr != NULL, ECS_INVALID_PARAMETER, NULL); const ecs_type_info_t *ti = dst.ti; ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); ecs_move_t move; if (cmd_kind != EcsCmdEmplace) { - /* ctor will have happened by get_mut */ + /* ctor will have happened by ensure */ move = ti->hooks.move_dtor; } else { move = ti->hooks.ctor_move_dtor; @@ -3199,7 +3580,7 @@ void flecs_move_ptr_w_id( return; } -ecs_entity_t ecs_set_id( +void ecs_set_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, @@ -3207,26 +3588,32 @@ ecs_entity_t ecs_set_id( const void *ptr) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!entity || ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); ecs_stage_t *stage = flecs_stage_from_world(&world); - if (!entity) { - entity = ecs_new_id(world); - ecs_entity_t scope = stage->scope; - if (scope) { - ecs_add_pair(world, entity, EcsChildOf, scope); - } - } - /* Safe to cast away const: function won't modify if move arg is false */ - flecs_copy_ptr_w_id(world, stage, entity, id, size, + flecs_set_id_copy(world, stage, entity, id, size, ECS_CONST_CAST(void*, ptr)); - return entity; error: - return 0; + return; +} + +#if defined(FLECS_DEBUG) || defined(FLECS_KEEP_ASSERT) +static +bool flecs_can_toggle( + ecs_world_t *world, + ecs_id_t id) +{ + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + return false; + } + + return (idr->flags & EcsIdCanToggle) != 0; } +#endif void ecs_enable_id( ecs_world_t *world, @@ -3235,16 +3622,15 @@ void ecs_enable_id( bool enable) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_check(ecs_is_valid(world, entity), ECS_INVALID_PARAMETER, NULL); ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); - - ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_check(flecs_can_toggle(world, id), ECS_INVALID_OPERATION, + "add CanToggle trait to component"); if (flecs_defer_enable(stage, entity, id, enable)) { return; - } else { - /* Operations invoked by enable/disable should not be deferred */ - stage->defer --; } ecs_record_t *r = flecs_entities_get(world, entity); @@ -3258,6 +3644,7 @@ void ecs_enable_id( if (index == -1) { ecs_add_id(world, entity, bs_id); + flecs_defer_end(world, stage); ecs_enable_id(world, entity, id, enable); return; } @@ -3266,11 +3653,13 @@ void ecs_enable_id( index -= table->_->bs_offset; ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); - /* Data cannot be NULl, since entity is stored in the table */ + /* Data cannot be NULL, since entity is stored in the table */ ecs_bitset_t *bs = &table->_->bs_columns[index]; ecs_assert(bs != NULL, ECS_INTERNAL_ERROR, NULL); flecs_bitset_set(bs, ECS_RECORD_TO_ROW(r->row), enable); + + flecs_defer_end(world, stage); error: return; } @@ -3331,40 +3720,40 @@ bool ecs_has_id( return false; } - ecs_id_record_t *idr = flecs_id_record_get(world, id); - int32_t column; - if (idr) { - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (tr) { + if (id < FLECS_HI_COMPONENT_ID) { + if (table->component_map[id] != 0) { return true; } + } else { + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (tr) { + return true; + } + } } if (!(table->flags & (EcsTableHasUnion|EcsTableHasIsA))) { return false; } + if (ECS_IS_PAIR(id) && (table->flags & EcsTableHasUnion)) { + ecs_id_record_t *u_idr = flecs_id_record_get(world, + ecs_pair(ECS_PAIR_FIRST(id), EcsUnion)); + if (u_idr && u_idr->flags & EcsIdIsUnion) { + uint64_t cur = flecs_switch_get(u_idr->sparse, (uint32_t)entity); + return (uint32_t)cur == ECS_PAIR_SECOND(id); + } + } + ecs_table_record_t *tr; - column = ecs_search_relation(world, table, 0, id, + int32_t column = ecs_search_relation(world, table, 0, id, EcsIsA, 0, 0, 0, &tr); if (column == -1) { return false; } - table = tr->hdr.table; - if ((table->flags & EcsTableHasUnion) && ECS_HAS_ID_FLAG(id, PAIR) && - ECS_PAIR_SECOND(id) != EcsWildcard) - { - if (ECS_PAIR_FIRST(table->type.array[column]) == EcsUnion) { - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_switch_t *sw = &table->_->sw_columns[ - column - table->_->sw_offset]; - int32_t row = ECS_RECORD_TO_ROW(r->row); - uint64_t value = flecs_switch_get(sw, row); - return value == ecs_pair_second(world, id); - } - } - return true; error: return false; @@ -3375,7 +3764,34 @@ bool ecs_owns_id( ecs_entity_t entity, ecs_id_t id) { - return (ecs_search(world, ecs_get_table(world, entity), id, 0) != -1); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); + ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); + + /* Make sure we're not working with a stage */ + world = ecs_get_world(world); + + ecs_record_t *r = flecs_entities_get_any(world, entity); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = r->table; + if (!table) { + return false; + } + + if (id < FLECS_HI_COMPONENT_ID) { + return table->component_map[id] != 0; + } else { + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (tr) { + return true; + } + } + } + +error: + return false; } ecs_entity_t ecs_get_target( @@ -3404,30 +3820,11 @@ ecs_entity_t ecs_get_target( tr = flecs_id_record_get_table(idr, table); } if (!tr) { - if (table->flags & EcsTableHasUnion) { - wc = ecs_pair(EcsUnion, rel); - tr = flecs_table_record_get(world, table, wc); - if (tr) { - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_switch_t *sw = &table->_->sw_columns[ - tr->index - table->_->sw_offset]; - int32_t row = ECS_RECORD_TO_ROW(r->row); - return flecs_switch_get(sw, row); - - } - } - - if (!idr || !(idr->flags & EcsIdDontInherit)) { + if (!idr || (idr->flags & EcsIdOnInstantiateInherit)) { goto look_in_base; } else { return 0; } - } else if (table->flags & EcsTableHasTarget) { - EcsTarget *tf = ecs_table_get_id(world, table, - ecs_pair_t(EcsTarget, rel), ECS_RECORD_TO_ROW(r->row)); - if (tf) { - return ecs_record_get_entity(tf->target); - } } if (index >= tr->count) { @@ -3435,7 +3832,17 @@ ecs_entity_t ecs_get_target( goto look_in_base; } - return ecs_pair_second(world, table->type.array[tr->index + index]); + ecs_entity_t result = + ecs_pair_second(world, table->type.array[tr->index + index]); + + if (result == EcsUnion) { + wc = ecs_pair(rel, EcsUnion); + ecs_id_record_t *wc_idr = flecs_id_record_get(world, wc); + ecs_assert(wc_idr != NULL, ECS_INTERNAL_ERROR, NULL); + result = flecs_switch_get(wc_idr->sparse, (uint32_t)entity); + } + + return result; look_in_base: if (table->flags & EcsTableHasIsA) { ecs_table_record_t *tr_isa = flecs_id_record_get_table( @@ -3528,7 +3935,9 @@ int32_t ecs_get_depth( ecs_entity_t rel) { ecs_check(ecs_is_valid(world, rel), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_has_id(world, rel, EcsAcyclic), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_has_id(world, rel, EcsAcyclic), ECS_INVALID_PARAMETER, + "cannot safely determine depth for relationship that is not acyclic " + "(add Acyclic property to relationship)"); ecs_table_t *table = ecs_get_table(world, entity); if (table) { @@ -3540,195 +3949,6 @@ int32_t ecs_get_depth( return -1; } -static -ecs_entity_t flecs_id_for_depth( - ecs_world_t *world, - int32_t depth) -{ - ecs_vec_t *depth_ids = &world->store.depth_ids; - int32_t i, count = ecs_vec_count(depth_ids); - for (i = count; i <= depth; i ++) { - ecs_entity_t *el = ecs_vec_append_t( - &world->allocator, depth_ids, ecs_entity_t); - el[0] = ecs_new_w_pair(world, EcsChildOf, EcsFlecsInternals); - ecs_map_val_t *v = ecs_map_ensure(&world->store.entity_to_depth, el[0]); - v[0] = flecs_ito(uint64_t, i); - } - - return ecs_vec_get_t(&world->store.depth_ids, ecs_entity_t, depth)[0]; -} - -static -int32_t flecs_depth_for_id( - ecs_world_t *world, - ecs_entity_t id) -{ - ecs_map_val_t *v = ecs_map_get(&world->store.entity_to_depth, id); - ecs_assert(v != NULL, ECS_INTERNAL_ERROR, NULL); - return flecs_uto(int32_t, v[0]); -} - -static -int32_t flecs_depth_for_flat_table( - ecs_world_t *world, - ecs_table_t *table) -{ - ecs_assert(table->flags & EcsTableHasTarget, ECS_INTERNAL_ERROR, NULL); - ecs_id_t id; - int32_t col = ecs_search(world, table, - ecs_pair(EcsFlatten, EcsWildcard), &id); - ecs_assert(col != -1, ECS_INTERNAL_ERROR, NULL); - (void)col; - return flecs_depth_for_id(world, ECS_PAIR_SECOND(id)); -} - -static -void flecs_flatten( - ecs_world_t *world, - ecs_entity_t root, - ecs_id_t pair, - int32_t depth, - const ecs_flatten_desc_t *desc) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, pair); - if (!idr) { - return; - } - - ecs_entity_t depth_id = flecs_id_for_depth(world, depth); - ecs_id_t root_pair = ecs_pair(EcsChildOf, root); - ecs_id_t tgt_pair = ecs_pair_t(EcsTarget, EcsChildOf); - ecs_id_t depth_pair = ecs_pair(EcsFlatten, depth_id); - ecs_id_t name_pair = ecs_pair_t(EcsIdentifier, EcsName); - - ecs_entity_t rel = ECS_PAIR_FIRST(pair); - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) { - const ecs_table_record_t *tr; - 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); - bool has_tgt = table->flags & EcsTableHasTarget; - flecs_emit_propagate_invalidate(world, table, 0, count); - - ecs_entity_t *entities = table->data.entities.array; - for (i = 0; i < count; i ++) { - ecs_record_t *record = flecs_entities_get(world, entities[i]); - ecs_flags32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); - if (flags & EcsEntityIsTarget) { - flecs_flatten(world, root, ecs_pair(rel, entities[i]), - depth + 1, desc); - } - } - - ecs_table_diff_t tmpdiff; - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); - ecs_table_t *dst; - - dst = flecs_table_traverse_add(world, table, &root_pair, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - - dst = flecs_table_traverse_add(world, dst, &tgt_pair, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - - if (!desc->lose_depth) { - if (!has_tgt) { - dst = flecs_table_traverse_add(world, dst, &depth_pair, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - } else { - int32_t cur_depth = flecs_depth_for_flat_table(world, table); - cur_depth += depth; - ecs_entity_t e_depth = flecs_id_for_depth(world, cur_depth); - ecs_id_t p_depth = ecs_pair(EcsFlatten, e_depth); - dst = flecs_table_traverse_add(world, dst, &p_depth, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - } - } - - if (!desc->keep_names) { - dst = flecs_table_traverse_remove(world, dst, &name_pair, &tmpdiff); - flecs_table_diff_build_append_table(world, &diff, &tmpdiff); - } - - int32_t dst_count = ecs_table_count(dst); - - ecs_table_diff_t td; - flecs_table_diff_build_noalloc(&diff, &td); - flecs_notify_on_remove(world, table, NULL, 0, count, &td.removed); - flecs_table_merge(world, dst, table, &dst->data, &table->data); - flecs_notify_on_add(world, dst, NULL, dst_count, count, - &td.added, 0); - flecs_table_diff_builder_fini(world, &diff); - - EcsTarget *fh = ecs_table_get_id(world, dst, tgt_pair, 0); - ecs_assert(fh != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(count != 0, ECS_INTERNAL_ERROR, NULL); - int32_t remain = count; - - for (i = dst_count; i < (dst_count + count); i ++) { - if (!has_tgt) { - fh[i].target = flecs_entities_get_any(world, - ECS_PAIR_SECOND(pair)); - fh[i].count = remain; - remain --; - } - ecs_assert(fh[i].target != NULL, ECS_INTERNAL_ERROR, NULL); - } - } - } - - ecs_delete_with(world, pair); - flecs_id_record_release(world, idr); -} - -void ecs_flatten( - ecs_world_t *world, - ecs_id_t pair, - const ecs_flatten_desc_t *desc) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_entity_t rel = ECS_PAIR_FIRST(pair); - ecs_entity_t root = ecs_pair_second(world, pair); - ecs_flatten_desc_t private_desc = {0}; - if (desc) { - private_desc = *desc; - } - - ecs_run_aperiodic(world, 0); - ecs_defer_begin(world); - - ecs_id_record_t *idr = flecs_id_record_get(world, pair); - - ecs_table_cache_iter_t it; - if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) { - const ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; - if (!table->_->traversable_count) { - continue; - } - - if (table->flags & EcsTableIsPrefab) { - continue; - } - - int32_t i, count = ecs_table_count(table); - ecs_entity_t *entities = table->data.entities.array; - for (i = 0; i < count; i ++) { - ecs_record_t *record = flecs_entities_get(world, entities[i]); - ecs_flags32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); - if (flags & EcsEntityIsTarget) { - flecs_flatten(world, root, ecs_pair(rel, entities[i]), 1, - &private_desc); - } - } - } - } - - ecs_defer_end(world); -} - static const char* flecs_get_identifier( const ecs_world_t *world, @@ -3781,7 +4001,7 @@ ecs_entity_t flecs_set_identifier( ecs_check(entity != 0 || name != NULL, ECS_INVALID_PARAMETER, NULL); if (!entity) { - entity = ecs_new_id(world); + entity = ecs_new(world); } if (!name) { @@ -3789,11 +4009,11 @@ ecs_entity_t flecs_set_identifier( return entity; } - EcsIdentifier *ptr = ecs_get_mut_pair(world, entity, EcsIdentifier, tag); + EcsIdentifier *ptr = ecs_ensure_pair(world, entity, EcsIdentifier, tag); ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); if (tag == EcsName) { - /* Insert command after get_mut, but before the name is potentially + /* Insert command after ensure, but before the name is potentially * freed. Even though the name is a const char*, it is possible that the * application passed in the existing name of the entity which could * still cause it to be freed. */ @@ -3871,13 +4091,6 @@ bool ecs_is_valid( return false; } - /* If entity doesn't exist in the world, the id is valid as long as the - * generation is 0. Using a non-existing id with a non-zero generation - * requires calling ecs_ensure first. */ - if (!ecs_exists(world, entity)) { - return ECS_GENERATION(entity) == 0; - } - /* If id exists, it must be alive (the generation count must match) */ return ecs_is_alive(world, entity); error: @@ -3927,12 +4140,12 @@ ecs_entity_t ecs_get_alive( } /* Make sure id does not have generation. This guards against accidentally - * "upcasting" a not alive identifier to a alive one. */ + * "upcasting" a not alive identifier to an alive one. */ if ((uint32_t)entity != entity) { return 0; } - ecs_entity_t current = flecs_entities_get_generation(world, entity); + ecs_entity_t current = flecs_entities_get_alive(world, entity); if (!current || !flecs_entities_is_alive(world, current)) { return 0; } @@ -3942,7 +4155,7 @@ ecs_entity_t ecs_get_alive( return 0; } -void ecs_ensure( +void ecs_make_alive( ecs_world_t *world, ecs_entity_t entity) { @@ -3955,7 +4168,8 @@ void ecs_ensure( /* 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); + ECS_INVALID_OPERATION, + "cannot make entity alive while world is in multithreaded mode"); /* Check if a version of the provided id is alive */ ecs_entity_t any = ecs_get_alive(world, (uint32_t)entity); @@ -3965,7 +4179,8 @@ void ecs_ensure( } /* If the id is currently alive but did not match the argument, fail */ - ecs_check(!any, ECS_INVALID_PARAMETER, NULL); + ecs_check(!any, ECS_INVALID_PARAMETER, + "entity is alive with different generation"); /* Set generation if not alive. The sparse set checks if the provided * id matches its own generation which is necessary for alive ids. This @@ -3975,20 +4190,20 @@ void ecs_ensure( * While this could've been addressed in the sparse set, this is a rare * scenario that can only be triggered by ecs_ensure. Implementing it here * allows the sparse set to not do this check, which is more efficient. */ - flecs_entities_set_generation(world, entity); + flecs_entities_make_alive(world, entity); - /* Ensure id exists. The underlying datastructure will verify that the + /* Ensure id exists. The underlying data structure will verify that the * generation count matches the provided one. */ flecs_entities_ensure(world, entity); error: return; } -void ecs_ensure_id( +void ecs_make_alive_id( ecs_world_t *world, ecs_id_t id) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); if (ECS_HAS_ID_FLAG(id, PAIR)) { ecs_entity_t r = ECS_PAIR_FIRST(id); ecs_entity_t o = ECS_PAIR_SECOND(id); @@ -3996,12 +4211,12 @@ void ecs_ensure_id( ecs_check(r != 0, ECS_INVALID_PARAMETER, NULL); ecs_check(o != 0, ECS_INVALID_PARAMETER, NULL); - if (flecs_entities_get_generation(world, r) == 0) { + if (flecs_entities_get_alive(world, r) == 0) { ecs_assert(!ecs_exists(world, r), ECS_INVALID_PARAMETER, "first element of pair is not alive"); flecs_entities_ensure(world, r); } - if (flecs_entities_get_generation(world, o) == 0) { + if (flecs_entities_get_alive(world, o) == 0) { ecs_assert(!ecs_exists(world, o), ECS_INVALID_PARAMETER, "second element of pair is not alive"); flecs_entities_ensure(world, o); @@ -4027,6 +4242,26 @@ bool ecs_exists( return false; } +void ecs_set_version( + ecs_world_t *world, + ecs_entity_t entity_with_generation) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot change entity generation when world is in readonly mode"); + ecs_assert(!(ecs_is_deferred(world)), ECS_INVALID_OPERATION, + "cannot change entity generation while world is deferred"); + + flecs_entities_make_alive(world, entity_with_generation); + + ecs_record_t *r = flecs_entities_get(world, entity_with_generation); + if (r && r->table) { + int32_t row = ECS_RECORD_TO_ROW(r->row); + ecs_entity_t *entities = r->table->data.entities; + entities[row] = entity_with_generation; + } +} + ecs_table_t* ecs_get_table( const ecs_world_t *world, ecs_entity_t entity) @@ -4073,7 +4308,7 @@ const ecs_type_info_t* ecs_get_type_info( } if (!idr) { ecs_entity_t first = ecs_pair_first(world, id); - if (!first || !ecs_has_id(world, first, EcsTag)) { + if (!first || !ecs_has_id(world, first, EcsPairIsTag)) { idr = flecs_id_record_get(world, ecs_pair(EcsWildcard, ECS_PAIR_SECOND(id))); if (!idr || !idr->type_info) { @@ -4117,7 +4352,7 @@ bool ecs_id_is_tag( if (ECS_PAIR_FIRST(id) != EcsWildcard) { ecs_entity_t rel = ecs_pair_first(world, id); if (ecs_is_valid(world, rel)) { - if (ecs_has_id(world, rel, EcsTag)) { + if (ecs_has_id(world, rel, EcsPairIsTag)) { return true; } } else { @@ -4143,24 +4378,6 @@ bool ecs_id_is_tag( return false; } -bool ecs_id_is_union( - const ecs_world_t *world, - ecs_id_t id) -{ - if (!ECS_IS_PAIR(id)) { - return false; - } else if (ECS_PAIR_FIRST(id) == EcsUnion) { - return true; - } else { - ecs_entity_t first = ecs_pair_first(world, id); - if (ecs_has_id(world, first, EcsUnion)) { - return true; - } - } - - return false; -} - int32_t ecs_count_id( const ecs_world_t *world, ecs_entity_t id) @@ -4172,16 +4389,8 @@ int32_t ecs_count_id( } int32_t count = 0; - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = id, - .src.flags = EcsSelf, - .flags = EcsTermMatchDisabled|EcsTermMatchPrefab - }); - - it.flags |= EcsIterNoData; - it.flags |= EcsIterEvalTables; - - while (ecs_term_next(&it)) { + ecs_iter_t it = ecs_each_id(world, id); + while (ecs_each_next(&it)) { count += it.count; } @@ -4203,10 +4412,9 @@ void ecs_enable( int32_t i, count = type->count; for (i = 0; i < count; i ++) { ecs_id_t id = ids[i]; - if (ecs_id_get_flags(world, id) & EcsIdDontInherit) { - continue; + if (!(ecs_id_get_flags(world, id) & EcsIdOnInstantiateDontInherit)){ + ecs_enable(world, id, enabled); } - ecs_enable(world, id, enabled); } } else { if (enabled) { @@ -4241,9 +4449,11 @@ void ecs_defer_suspend( ecs_world_t *world) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_is_deferred(world), ECS_INVALID_OPERATION, NULL); + ecs_check(ecs_is_deferred(world), ECS_INVALID_OPERATION, + "world/stage must be deferred before it can be suspended"); ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_check(stage->defer > 0, ECS_INVALID_OPERATION, NULL); + ecs_check(stage->defer > 0, ECS_INVALID_OPERATION, + "world/stage is already suspended"); stage->defer = -stage->defer; error: return; @@ -4254,7 +4464,8 @@ void ecs_defer_resume( { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_check(stage->defer < 0, ECS_INVALID_OPERATION, NULL); + ecs_check(stage->defer < 0, ECS_INVALID_OPERATION, + "world/stage must be suspended before it can be resumed"); stage->defer = -stage->defer; error: return; @@ -4269,11 +4480,8 @@ const char* ecs_id_flag_str( if (ECS_HAS_ID_FLAG(entity, TOGGLE)) { return "TOGGLE"; } else - if (ECS_HAS_ID_FLAG(entity, AND)) { - return "AND"; - } else - if (ECS_HAS_ID_FLAG(entity, OVERRIDE)) { - return "OVERRIDE"; + if (ECS_HAS_ID_FLAG(entity, AUTO_OVERRIDE)) { + return "AUTO_OVERRIDE"; } else { return "UNKNOWN"; } @@ -4293,13 +4501,8 @@ void ecs_id_str_buf( ecs_strbuf_appendch(buf, '|'); } - if (ECS_HAS_ID_FLAG(id, OVERRIDE)) { - ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_OVERRIDE)); - ecs_strbuf_appendch(buf, '|'); - } - - if (ECS_HAS_ID_FLAG(id, AND)) { - ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_AND)); + if (ECS_HAS_ID_FLAG(id, AUTO_OVERRIDE)) { + ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_AUTO_OVERRIDE)); ecs_strbuf_appendch(buf, '|'); } @@ -4316,13 +4519,13 @@ void ecs_id_str_buf( } ecs_strbuf_appendch(buf, '('); - ecs_get_path_w_sep_buf(world, 0, rel, NULL, NULL, buf); + ecs_get_path_w_sep_buf(world, 0, rel, NULL, NULL, buf, false); ecs_strbuf_appendch(buf, ','); - ecs_get_path_w_sep_buf(world, 0, obj, NULL, NULL, buf); + ecs_get_path_w_sep_buf(world, 0, obj, NULL, NULL, buf, false); ecs_strbuf_appendch(buf, ')'); } else { ecs_entity_t e = id & ECS_COMPONENT_MASK; - ecs_get_path_w_sep_buf(world, 0, e, NULL, NULL, buf); + ecs_get_path_w_sep_buf(world, 0, e, NULL, NULL, buf, false); } error: @@ -4394,7 +4597,7 @@ char* ecs_entity_str( ecs_strbuf_t buf = ECS_STRBUF_INIT; ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); - ecs_get_path_w_sep_buf(world, 0, entity, 0, "", &buf); + ecs_get_path_w_sep_buf(world, 0, entity, 0, "", &buf, false); ecs_strbuf_appendlit(&buf, " ["); const ecs_type_t *type = ecs_get_type(world, entity); @@ -4604,7 +4807,11 @@ void flecs_cmd_batch_for_entity( ecs_table_diff_t table_diff; /* Keep track of diff for observers/hooks */ int32_t cur = start; ecs_id_t id; - bool has_set = false; + + /* Mask to let observable implementation know which components were set. + * This prevents the code from overwriting components with an override if + * the batch also contains an IsA pair. */ + ecs_flags64_t set_mask = 0; do { cmd = &cmds[cur]; @@ -4655,16 +4862,17 @@ void flecs_cmd_batch_for_entity( * 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) { - const ecs_type_info_t *ti = ptr.ti; + ecs_id_record_t *idr = flecs_id_record_get(world, cmd->id); + const ecs_table_record_t *tr = + flecs_id_record_get_table(idr, start_table); + if (tr) { + const ecs_type_info_t *ti = idr->type_info; ecs_iter_action_t on_set; if ((on_set = ti->hooks.on_set)) { ecs_table_t *prev_table = r->table; ecs_defer_begin(world); - flecs_invoke_hook(world, start_table, 1, row, &entity, - ptr.ptr, cmd->id, ptr.ti, EcsOnSet, on_set); + flecs_invoke_hook(world, start_table, tr, 1, row, + &entity,cmd->id, ti, EcsOnSet, on_set); ecs_defer_end(world); /* Don't run on_set hook twice, but make sure to still @@ -4683,11 +4891,33 @@ void flecs_cmd_batch_for_entity( } break; case EcsCmdSet: - case EcsCmdMut: + case EcsCmdEnsure: { + ecs_id_t *ids = diff->added.array; + uint8_t added_index = flecs_ito(uint8_t, diff->added.count); + table = flecs_find_table_add(world, table, id, diff); + + if (diff->added.count == (added_index + 1)) { + /* Single id was added, must be at the end of the array */ + ecs_assert(ids[added_index] == id, ECS_INTERNAL_ERROR, NULL); + set_mask |= (1llu << added_index); + } else { + /* Id was already added or multiple ids got added. Do a linear + * search to find the index we need to set the set_mask. */ + int32_t i; + for (i = 0; i < diff->added.count; i ++) { + if (ids[i] == id) { + break; + } + } + + ecs_assert(i != diff->added.count, ECS_INTERNAL_ERROR, NULL); + set_mask |= (1llu << i); + } + world->info.cmd.batched_command_count ++; - has_set = true; break; + } case EcsCmdEmplace: /* Don't add for emplace, as this requires a special call to ensure * the constructor is not invoked for the component */ @@ -4702,6 +4932,7 @@ void flecs_cmd_batch_for_entity( &diff->removed, ecs_id_t, table->type.count); ecs_os_memcpy_n(ids, table->type.array, ecs_id_t, table->type.count); + diff->removed_flags |= table->flags & EcsTableRemoveEdgeFlags; } table = &world->store.root; world->info.cmd.batched_command_count ++; @@ -4726,12 +4957,25 @@ void flecs_cmd_batch_for_entity( } } while ((cur = next_for_entity)); + /* Invoke OnAdd handlers after commit. This ensures that observers with + * mixed OnAdd/OnSet events won't get called with uninitialized values for + * an OnSet field. */ + ecs_type_t added = { diff->added.array, diff->added.count }; + diff->added.array = NULL; + diff->added.count = 0; + /* Move entity to destination table in single operation */ flecs_table_diff_build_noalloc(diff, &table_diff); - flecs_defer_begin(world, &world->stages[0]); + flecs_defer_begin(world, world->stages[0]); flecs_commit(world, entity, r, table, &table_diff, true, 0); - flecs_defer_end(world, &world->stages[0]); - flecs_table_diff_builder_clear(diff); + flecs_defer_end(world, world->stages[0]); + + /* If destination table has new sparse components, make sure they're created + * for the entity. */ + if (table && (table->flags & EcsTableHasSparse) && added.count) { + flecs_sparse_on_add( + world, table, ECS_RECORD_TO_ROW(r->row), 1, &added, true); + } /* If the batch contains set commands, copy the component value from the * temporary command storage to the actual component storage before OnSet @@ -4741,7 +4985,7 @@ void flecs_cmd_batch_for_entity( * This only happens for entities that didn't have the assigned component * yet, as for entities that did have the component already the value will * have been assigned directly to the component storage. */ - if (has_set) { + if (set_mask) { cur = start; do { cmd = &cmds[cur]; @@ -4749,13 +4993,15 @@ void flecs_cmd_batch_for_entity( if (next_for_entity < 0) { next_for_entity *= -1; } + switch(cmd->kind) { case EcsCmdSet: - case EcsCmdMut: { + case EcsCmdEnsure: { flecs_component_ptr_t ptr = {0}; if (r->table) { - ptr = flecs_get_component_ptr(world, - r->table, ECS_RECORD_TO_ROW(r->row), cmd->id); + ecs_id_record_t *idr = flecs_id_record_get(world, cmd->id); + ptr = flecs_get_component_ptr( + r->table, ECS_RECORD_TO_ROW(r->row), idr); } /* It's possible that even though the component was set, the @@ -4774,6 +5020,7 @@ void flecs_cmd_batch_for_entity( } else { ecs_os_memcpy(ptr.ptr, cmd->is._1.value, ti->size); } + if (cmd->kind == EcsCmdSet) { /* A set operation is add + copy + modified. We just did * the add the copy, so the only thing that's left is a @@ -4781,7 +5028,7 @@ void flecs_cmd_batch_for_entity( * observers. */ cmd->kind = EcsCmdModified; } else { - /* If this was a get_mut, nothing's left to be done */ + /* If this was an ensure, nothing's left to be done */ cmd->kind = EcsCmdSkip; } } else { @@ -4812,6 +5059,26 @@ void flecs_cmd_batch_for_entity( } } while ((cur = next_for_entity)); } + + if (added.count && r->table) { + ecs_table_diff_t add_diff = ECS_TABLE_DIFF_INIT; + add_diff.added = added; + add_diff.added_flags = diff->added_flags; + + flecs_defer_begin(world, world->stages[0]); + flecs_notify_on_add(world, r->table, start_table, + ECS_RECORD_TO_ROW(r->row), 1, &add_diff, 0, set_mask, true, false); + flecs_defer_end(world, world->stages[0]); + if (r->row & EcsEntityIsTraversable) { + /* Update monitors since we didn't do this in flecs_commit. */ + flecs_update_component_monitors(world, &added, NULL); + } + } + + diff->added.array = added.array; + diff->added.count = added.count; + + flecs_table_diff_builder_clear(diff); } /* Leave safe section. Run all deferred commands. */ @@ -4819,8 +5086,8 @@ bool flecs_defer_end( ecs_world_t *world, ecs_stage_t *stage) { - ecs_poly_assert(world, ecs_world_t); - ecs_poly_assert(stage, ecs_stage_t); + flecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(stage, ecs_stage_t); if (stage->defer < 0) { /* Defer suspending makes it possible to do operations on the storage @@ -4831,11 +5098,13 @@ bool flecs_defer_end( ecs_assert(stage->defer > 0, ECS_INTERNAL_ERROR, NULL); if (!--stage->defer) { + ecs_os_perf_trace_push("flecs.commands.merge"); + /* Test whether we're flushing to another queue or whether we're * flushing to the storage */ bool merge_to_world = false; - if (ecs_poly_is(world, ecs_world_t)) { - merge_to_world = world->stages[0].defer == 0; + if (flecs_poly_is(world, ecs_world_t)) { + merge_to_world = world->stages[0]->defer == 0; } ecs_stage_t *dst_stage = flecs_stage_from_world(&world); @@ -4843,6 +5112,12 @@ bool flecs_defer_end( ecs_vec_t *queue = &commands->queue; if (ecs_vec_count(queue)) { + /* Internal callback for capturing commands */ + if (world->on_commands_active) { + world->on_commands_active(stage, queue, + world->on_commands_ctx_active); + } + ecs_cmd_t *cmds = ecs_vec_first(queue); int32_t i, count = ecs_vec_count(queue); @@ -4903,27 +5178,32 @@ bool flecs_defer_end( break; case EcsCmdClone: ecs_clone(world, e, id, cmd->is._1.clone_value); + world->info.cmd.other_count ++; break; case EcsCmdSet: - flecs_move_ptr_w_id(world, dst_stage, e, + flecs_set_id_move(world, dst_stage, e, cmd->id, flecs_itosize(cmd->is._1.size), cmd->is._1.value, kind); world->info.cmd.set_count ++; break; case EcsCmdEmplace: if (merge_to_world) { - ecs_emplace_id(world, e, id); + bool is_new; + ecs_emplace_id(world, e, id, &is_new); + if (!is_new) { + kind = EcsCmdEnsure; + } } - flecs_move_ptr_w_id(world, dst_stage, e, + flecs_set_id_move(world, dst_stage, e, cmd->id, flecs_itosize(cmd->is._1.size), cmd->is._1.value, kind); - world->info.cmd.get_mut_count ++; + world->info.cmd.ensure_count ++; break; - case EcsCmdMut: - flecs_move_ptr_w_id(world, dst_stage, e, + case EcsCmdEnsure: + flecs_set_id_move(world, dst_stage, e, cmd->id, flecs_itosize(cmd->is._1.size), cmd->is._1.value, kind); - world->info.cmd.get_mut_count ++; + world->info.cmd.ensure_count ++; break; case EcsCmdModified: flecs_modified_id_if(world, e, id, true); @@ -4936,8 +5216,7 @@ bool flecs_defer_end( case EcsCmdAddModified: flecs_add_id(world, e, id); flecs_modified_id_if(world, e, id, true); - world->info.cmd.add_count ++; - world->info.cmd.modified_count ++; + world->info.cmd.set_count ++; break; case EcsCmdDelete: { ecs_delete(world, e); @@ -4966,20 +5245,31 @@ bool flecs_defer_end( flecs_flush_bulk_new(world, cmd); world->info.cmd.other_count ++; continue; - case EcsCmdPath: - ecs_ensure(world, e); + case EcsCmdPath: { + bool keep_alive = true; + ecs_make_alive(world, e); if (cmd->id) { - ecs_add_pair(world, e, EcsChildOf, cmd->id); + if (ecs_is_alive(world, cmd->id)) { + ecs_add_pair(world, e, EcsChildOf, cmd->id); + } else { + ecs_delete(world, e); + keep_alive = false; + } + } + if (keep_alive) { + ecs_set_name(world, e, cmd->is._1.value); } - ecs_set_name(world, e, cmd->is._1.value); ecs_os_free(cmd->is._1.value); cmd->is._1.value = NULL; + world->info.cmd.other_count ++; break; + } case EcsCmdEvent: { ecs_event_desc_t *desc = cmd->is._1.value; ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); ecs_emit((ecs_world_t*)stage, desc); flecs_free_cmd_event(world, desc); + world->info.cmd.event_count ++; break; } case EcsCmdSkip: @@ -4996,8 +5286,16 @@ bool flecs_defer_end( flecs_commands_pop(stage); flecs_table_diff_builder_fini(world, &diff); + + /* Internal callback for capturing commands, signal queue is done */ + if (world->on_commands_active) { + world->on_commands_active(stage, NULL, + world->on_commands_ctx_active); + } } + ecs_os_perf_trace_pop("flecs.commands.merge"); + return true; } diff --git a/vendors/flecs/src/entity_filter.c b/vendors/flecs/src/entity_filter.c deleted file mode 100644 index 5e4729f62..000000000 --- a/vendors/flecs/src/entity_filter.c +++ /dev/null @@ -1,650 +0,0 @@ -/** - * @file entity_filter.c - * @brief Filters that are applied to entities in a table. - * - * After a table has been matched by a query, additional filters may have to - * be applied before returning entities to the application. The two scenarios - * under which this happens are queries for union relationship pairs (entities - * for multiple targets are stored in the same table) and toggles (components - * that are enabled/disabled with a bitset). - */ - -#include "private_api.h" - -static -int flecs_entity_filter_find_smallest_term( - ecs_table_t *table, - ecs_entity_filter_iter_t *iter) -{ - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - flecs_switch_term_t *sw_terms = ecs_vec_first(&iter->entity_filter->sw_terms); - int32_t i, count = ecs_vec_count(&iter->entity_filter->sw_terms); - int32_t min = INT_MAX, index = 0; - - for (i = 0; i < count; i ++) { - /* The array with sparse queries for the matched table */ - flecs_switch_term_t *sparse_column = &sw_terms[i]; - - /* Pointer to the switch column struct of the table */ - ecs_switch_t *sw = sparse_column->sw_column; - - /* If the sparse column pointer hadn't been retrieved yet, do it now */ - if (!sw) { - /* Get the table column index from the signature column index */ - int32_t table_column_index = iter->columns[ - sparse_column->signature_column_index]; - - /* Translate the table column index to switch column index */ - table_column_index -= table->_->sw_offset; - ecs_assert(table_column_index >= 1, ECS_INTERNAL_ERROR, NULL); - - /* Get the sparse column */ - sw = sparse_column->sw_column = - &table->_->sw_columns[table_column_index - 1]; - } - - /* Find the smallest column */ - int32_t case_count = flecs_switch_case_count(sw, sparse_column->sw_case); - if (case_count < min) { - min = case_count; - index = i + 1; - } - } - - return index; -} - -static -int flecs_entity_filter_switch_next( - ecs_table_t *table, - ecs_entity_filter_iter_t *iter, - bool filter) -{ - bool first_iteration = false; - int32_t switch_smallest; - - if (!(switch_smallest = iter->sw_smallest)) { - switch_smallest = iter->sw_smallest = - flecs_entity_filter_find_smallest_term(table, iter); - first_iteration = true; - } - - switch_smallest -= 1; - - flecs_switch_term_t *columns = ecs_vec_first(&iter->entity_filter->sw_terms); - flecs_switch_term_t *column = &columns[switch_smallest]; - ecs_switch_t *sw, *sw_smallest = column->sw_column; - ecs_entity_t case_smallest = column->sw_case; - - /* Find next entity to iterate in sparse column */ - int32_t first, sparse_first = iter->sw_offset; - - if (!filter) { - if (first_iteration) { - first = flecs_switch_first(sw_smallest, case_smallest); - } else { - first = flecs_switch_next(sw_smallest, sparse_first); - } - } else { - int32_t cur_first = iter->range.offset, cur_count = iter->range.count; - first = cur_first; - while (flecs_switch_get(sw_smallest, first) != case_smallest) { - first ++; - if (first >= (cur_first + cur_count)) { - first = -1; - break; - } - } - } - - if (first == -1) { - goto done; - } - - /* Check if entity matches with other sparse columns, if any */ - int32_t i, count = ecs_vec_count(&iter->entity_filter->sw_terms); - do { - for (i = 0; i < count; i ++) { - if (i == switch_smallest) { - /* Already validated this one */ - continue; - } - - column = &columns[i]; - sw = column->sw_column; - - if (flecs_switch_get(sw, first) != column->sw_case) { - first = flecs_switch_next(sw_smallest, first); - if (first == -1) { - goto done; - } - } - } - } while (i != count); - - iter->range.offset = iter->sw_offset = first; - iter->range.count = 1; - - return 0; -done: - /* Iterated all elements in the sparse list, we should move to the - * next matched table. */ - iter->sw_smallest = 0; - iter->sw_offset = 0; - - return -1; -} - -#define BS_MAX ((uint64_t)0xFFFFFFFFFFFFFFFF) - -static -int flecs_entity_filter_bitset_next( - ecs_table_t *table, - ecs_entity_filter_iter_t *iter) -{ - /* Precomputed single-bit test */ - static const uint64_t bitmask[64] = { - (uint64_t)1 << 0, (uint64_t)1 << 1, (uint64_t)1 << 2, (uint64_t)1 << 3, - (uint64_t)1 << 4, (uint64_t)1 << 5, (uint64_t)1 << 6, (uint64_t)1 << 7, - (uint64_t)1 << 8, (uint64_t)1 << 9, (uint64_t)1 << 10, (uint64_t)1 << 11, - (uint64_t)1 << 12, (uint64_t)1 << 13, (uint64_t)1 << 14, (uint64_t)1 << 15, - (uint64_t)1 << 16, (uint64_t)1 << 17, (uint64_t)1 << 18, (uint64_t)1 << 19, - (uint64_t)1 << 20, (uint64_t)1 << 21, (uint64_t)1 << 22, (uint64_t)1 << 23, - (uint64_t)1 << 24, (uint64_t)1 << 25, (uint64_t)1 << 26, (uint64_t)1 << 27, - (uint64_t)1 << 28, (uint64_t)1 << 29, (uint64_t)1 << 30, (uint64_t)1 << 31, - (uint64_t)1 << 32, (uint64_t)1 << 33, (uint64_t)1 << 34, (uint64_t)1 << 35, - (uint64_t)1 << 36, (uint64_t)1 << 37, (uint64_t)1 << 38, (uint64_t)1 << 39, - (uint64_t)1 << 40, (uint64_t)1 << 41, (uint64_t)1 << 42, (uint64_t)1 << 43, - (uint64_t)1 << 44, (uint64_t)1 << 45, (uint64_t)1 << 46, (uint64_t)1 << 47, - (uint64_t)1 << 48, (uint64_t)1 << 49, (uint64_t)1 << 50, (uint64_t)1 << 51, - (uint64_t)1 << 52, (uint64_t)1 << 53, (uint64_t)1 << 54, (uint64_t)1 << 55, - (uint64_t)1 << 56, (uint64_t)1 << 57, (uint64_t)1 << 58, (uint64_t)1 << 59, - (uint64_t)1 << 60, (uint64_t)1 << 61, (uint64_t)1 << 62, (uint64_t)1 << 63 - }; - - /* Precomputed test to verify if remainder of block is set (or not) */ - static const uint64_t bitmask_remain[64] = { - BS_MAX, BS_MAX - (BS_MAX >> 63), BS_MAX - (BS_MAX >> 62), - BS_MAX - (BS_MAX >> 61), BS_MAX - (BS_MAX >> 60), BS_MAX - (BS_MAX >> 59), - BS_MAX - (BS_MAX >> 58), BS_MAX - (BS_MAX >> 57), BS_MAX - (BS_MAX >> 56), - BS_MAX - (BS_MAX >> 55), BS_MAX - (BS_MAX >> 54), BS_MAX - (BS_MAX >> 53), - BS_MAX - (BS_MAX >> 52), BS_MAX - (BS_MAX >> 51), BS_MAX - (BS_MAX >> 50), - BS_MAX - (BS_MAX >> 49), BS_MAX - (BS_MAX >> 48), BS_MAX - (BS_MAX >> 47), - BS_MAX - (BS_MAX >> 46), BS_MAX - (BS_MAX >> 45), BS_MAX - (BS_MAX >> 44), - BS_MAX - (BS_MAX >> 43), BS_MAX - (BS_MAX >> 42), BS_MAX - (BS_MAX >> 41), - BS_MAX - (BS_MAX >> 40), BS_MAX - (BS_MAX >> 39), BS_MAX - (BS_MAX >> 38), - BS_MAX - (BS_MAX >> 37), BS_MAX - (BS_MAX >> 36), BS_MAX - (BS_MAX >> 35), - BS_MAX - (BS_MAX >> 34), BS_MAX - (BS_MAX >> 33), BS_MAX - (BS_MAX >> 32), - BS_MAX - (BS_MAX >> 31), BS_MAX - (BS_MAX >> 30), BS_MAX - (BS_MAX >> 29), - BS_MAX - (BS_MAX >> 28), BS_MAX - (BS_MAX >> 27), BS_MAX - (BS_MAX >> 26), - BS_MAX - (BS_MAX >> 25), BS_MAX - (BS_MAX >> 24), BS_MAX - (BS_MAX >> 23), - BS_MAX - (BS_MAX >> 22), BS_MAX - (BS_MAX >> 21), BS_MAX - (BS_MAX >> 20), - BS_MAX - (BS_MAX >> 19), BS_MAX - (BS_MAX >> 18), BS_MAX - (BS_MAX >> 17), - BS_MAX - (BS_MAX >> 16), BS_MAX - (BS_MAX >> 15), BS_MAX - (BS_MAX >> 14), - BS_MAX - (BS_MAX >> 13), BS_MAX - (BS_MAX >> 12), BS_MAX - (BS_MAX >> 11), - BS_MAX - (BS_MAX >> 10), BS_MAX - (BS_MAX >> 9), BS_MAX - (BS_MAX >> 8), - BS_MAX - (BS_MAX >> 7), BS_MAX - (BS_MAX >> 6), BS_MAX - (BS_MAX >> 5), - BS_MAX - (BS_MAX >> 4), BS_MAX - (BS_MAX >> 3), BS_MAX - (BS_MAX >> 2), - BS_MAX - (BS_MAX >> 1) - }; - - int32_t i, count = ecs_vec_count(&iter->entity_filter->bs_terms); - flecs_bitset_term_t *terms = ecs_vec_first(&iter->entity_filter->bs_terms); - int32_t bs_offset = table->_->bs_offset; - int32_t first = iter->bs_offset; - int32_t last = 0; - - for (i = 0; i < count; i ++) { - flecs_bitset_term_t *column = &terms[i]; - ecs_bitset_t *bs = terms[i].bs_column; - - if (!bs) { - int32_t index = column->column_index; - ecs_assert((index - bs_offset >= 0), ECS_INTERNAL_ERROR, NULL); - bs = &table->_->bs_columns[index - bs_offset]; - terms[i].bs_column = bs; - } - - int32_t bs_elem_count = bs->count; - int32_t bs_block = first >> 6; - int32_t bs_block_count = ((bs_elem_count - 1) >> 6) + 1; - - if (bs_block >= bs_block_count) { - goto done; - } - - uint64_t *data = bs->data; - int32_t bs_start = first & 0x3F; - - /* Step 1: find the first non-empty block */ - uint64_t v = data[bs_block]; - uint64_t remain = bitmask_remain[bs_start]; - while (!(v & remain)) { - /* If no elements are remaining, move to next block */ - if ((++bs_block) >= bs_block_count) { - /* No non-empty blocks left */ - goto done; - } - - bs_start = 0; - remain = BS_MAX; /* Test the full block */ - v = data[bs_block]; - } - - /* Step 2: find the first non-empty element in the block */ - while (!(v & bitmask[bs_start])) { - bs_start ++; - - /* Block was not empty, so bs_start must be smaller than 64 */ - ecs_assert(bs_start < 64, ECS_INTERNAL_ERROR, NULL); - } - - /* Step 3: Find number of contiguous enabled elements after start */ - int32_t bs_end = bs_start, bs_block_end = bs_block; - - remain = bitmask_remain[bs_end]; - while ((v & remain) == remain) { - bs_end = 0; - bs_block_end ++; - - if (bs_block_end == bs_block_count) { - break; - } - - v = data[bs_block_end]; - remain = BS_MAX; /* Test the full block */ - } - - /* Step 4: find remainder of enabled elements in current block */ - if (bs_block_end != bs_block_count) { - while ((v & bitmask[bs_end])) { - bs_end ++; - } - } - - /* Block was not 100% occupied, so bs_start must be smaller than 64 */ - ecs_assert(bs_end < 64, ECS_INTERNAL_ERROR, NULL); - - /* Step 5: translate to element start/end and make sure that each column - * range is a subset of the previous one. */ - first = bs_block * 64 + bs_start; - int32_t cur_last = bs_block_end * 64 + bs_end; - - /* No enabled elements found in table */ - if (first == cur_last) { - goto done; - } - - /* If multiple bitsets are evaluated, make sure each subsequent range - * is equal or a subset of the previous range */ - if (i) { - /* If the first element of a subsequent bitset is larger than the - * previous last value, start over. */ - if (first >= last) { - i = -1; - continue; - } - - /* Make sure the last element of the range doesn't exceed the last - * element of the previous range. */ - if (cur_last > last) { - cur_last = last; - } - } - - last = cur_last; - int32_t elem_count = last - first; - - /* Make sure last element doesn't exceed total number of elements in - * the table */ - if (elem_count > (bs_elem_count - first)) { - elem_count = (bs_elem_count - first); - if (!elem_count) { - iter->bs_offset = 0; - goto done; - } - } - - iter->range.offset = first; - iter->range.count = elem_count; - iter->bs_offset = first; - } - - /* Keep track of last processed element for iteration */ - iter->bs_offset = last; - - return 0; -done: - iter->sw_smallest = 0; - iter->sw_offset = 0; - return -1; -} - -#undef BS_MAX - -static -int32_t flecs_get_flattened_target( - ecs_world_t *world, - EcsTarget *cur, - ecs_entity_t rel, - ecs_id_t id, - ecs_entity_t *src_out, - ecs_table_record_t **tr_out) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - return -1; - } - - ecs_record_t *r = cur->target; - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_table_t *table = r->table; - if (!table) { - return -1; - } - - ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (tr) { - *src_out = ecs_record_get_entity(r); - *tr_out = tr; - return tr->index; - } - - if (table->flags & EcsTableHasTarget) { - int32_t col = table->column_map[table->_->ft_offset]; - ecs_assert(col != -1, ECS_INTERNAL_ERROR, NULL); - EcsTarget *next = table->data.columns[col].data.array; - next = ECS_ELEM_T(next, EcsTarget, ECS_RECORD_TO_ROW(r->row)); - return flecs_get_flattened_target( - world, next, rel, id, src_out, tr_out); - } - - return ecs_search_relation( - world, table, 0, id, rel, EcsSelf|EcsUp, src_out, NULL, tr_out); -} - -void flecs_entity_filter_init( - ecs_world_t *world, - ecs_entity_filter_t **entity_filter, - const ecs_filter_t *filter, - const ecs_table_t *table, - ecs_id_t *ids, - int32_t *columns) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_assert(entity_filter != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(filter != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(ids != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(columns != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_allocator_t *a = &world->allocator; - ecs_entity_filter_t ef; - ecs_os_zeromem(&ef); - ecs_vec_t *sw_terms = &ef.sw_terms; - ecs_vec_t *bs_terms = &ef.bs_terms; - ecs_vec_t *ft_terms = &ef.ft_terms; - if (*entity_filter) { - ef.sw_terms = (*entity_filter)->sw_terms; - ef.bs_terms = (*entity_filter)->bs_terms; - ef.ft_terms = (*entity_filter)->ft_terms; - } - ecs_vec_reset_t(a, sw_terms, flecs_switch_term_t); - ecs_vec_reset_t(a, bs_terms, flecs_bitset_term_t); - ecs_vec_reset_t(a, ft_terms, flecs_flat_table_term_t); - ecs_term_t *terms = filter->terms; - int32_t i, term_count = filter->term_count; - bool has_filter = false; - ef.flat_tree_column = -1; - - /* Look for union fields */ - if (table->flags & EcsTableHasUnion) { - for (i = 0; i < term_count; i ++) { - if (ecs_term_match_0(&terms[i])) { - continue; - } - - ecs_id_t id = terms[i].id; - if (ECS_HAS_ID_FLAG(id, PAIR) && ECS_PAIR_SECOND(id) == EcsWildcard) { - continue; - } - - int32_t field = terms[i].field_index; - int32_t column = columns[field]; - if (column <= 0) { - continue; - } - - ecs_id_t table_id = table->type.array[column - 1]; - if (ECS_PAIR_FIRST(table_id) != EcsUnion) { - continue; - } - - flecs_switch_term_t *el = ecs_vec_append_t(a, sw_terms, - flecs_switch_term_t); - el->signature_column_index = field; - el->sw_case = ecs_pair_second(world, id); - el->sw_column = NULL; - ids[field] = id; - has_filter = true; - } - } - - /* Look for disabled fields */ - if (table->flags & EcsTableHasToggle) { - for (i = 0; i < term_count; i ++) { - if (ecs_term_match_0(&terms[i])) { - continue; - } - - int32_t field = terms[i].field_index; - ecs_id_t id = ids[field]; - ecs_id_t bs_id = ECS_TOGGLE | id; - int32_t bs_index = ecs_table_get_type_index(world, table, bs_id); - - if (bs_index != -1) { - flecs_bitset_term_t *bc = ecs_vec_append_t(a, bs_terms, - flecs_bitset_term_t); - bc->column_index = bs_index; - bc->bs_column = NULL; - has_filter = true; - } - } - } - - /* Look for flattened fields */ - if (table->flags & EcsTableHasTarget) { - const ecs_table_record_t *tr = flecs_table_record_get(world, table, - ecs_pair_t(EcsTarget, EcsWildcard)); - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t column = tr->index; - ecs_assert(column != -1, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t rel = ecs_pair_second(world, table->type.array[column]); - - for (i = 0; i < term_count; i ++) { - if (ecs_term_match_0(&terms[i])) { - continue; - } - - if (terms[i].src.trav == rel) { - ef.flat_tree_column = table->column_map[column]; - ecs_assert(ef.flat_tree_column != -1, - ECS_INTERNAL_ERROR, NULL); - has_filter = true; - - flecs_flat_table_term_t *term = ecs_vec_append_t( - a, ft_terms, flecs_flat_table_term_t); - term->field_index = terms[i].field_index; - term->term = &terms[i]; - ecs_os_zeromem(&term->monitor); - } - } - } - - if (has_filter) { - if (!*entity_filter) { - *entity_filter = ecs_os_malloc_t(ecs_entity_filter_t); - } - ecs_assert(*entity_filter != NULL, ECS_OUT_OF_MEMORY, NULL); - **entity_filter = ef; - } -} - -void flecs_entity_filter_fini( - ecs_world_t *world, - ecs_entity_filter_t *ef) -{ - if (!ef) { - return; - } - - ecs_allocator_t *a = &world->allocator; - - flecs_flat_table_term_t *fields = ecs_vec_first(&ef->ft_terms); - int32_t i, term_count = ecs_vec_count(&ef->ft_terms); - for (i = 0; i < term_count; i ++) { - ecs_vec_fini_t(NULL, &fields[i].monitor, flecs_flat_monitor_t); - } - - ecs_vec_fini_t(a, &ef->sw_terms, flecs_switch_term_t); - ecs_vec_fini_t(a, &ef->bs_terms, flecs_bitset_term_t); - ecs_vec_fini_t(a, &ef->ft_terms, flecs_flat_table_term_t); - ecs_os_free(ef); -} - -int flecs_entity_filter_next( - ecs_entity_filter_iter_t *it) -{ - ecs_table_t *table = it->range.table; - flecs_switch_term_t *sw_terms = ecs_vec_first(&it->entity_filter->sw_terms); - flecs_bitset_term_t *bs_terms = ecs_vec_first(&it->entity_filter->bs_terms); - ecs_entity_filter_t *ef = it->entity_filter; - int32_t flat_tree_column = ef->flat_tree_column; - ecs_table_range_t *range = &it->range; - int32_t range_end = range->offset + range->count; - int result = EcsIterNext; - bool found = false; - - do { - found = false; - - if (bs_terms) { - if (flecs_entity_filter_bitset_next(table, it) == -1) { - /* No more enabled components for table */ - it->bs_offset = 0; - break; - } else { - result = EcsIterYield; - found = true; - } - } - - if (sw_terms) { - if (flecs_entity_filter_switch_next(table, it, found) == -1) { - /* No more elements in sparse column */ - if (found) { - /* Try again */ - result = EcsIterNext; - found = false; - } else { - /* Nothing found */ - it->bs_offset = 0; - break; - } - } else { - result = EcsIterYield; - found = true; - it->bs_offset = range->offset + range->count; - } - } - - if (flat_tree_column != -1) { - bool first_for_table = it->prev != table; - ecs_iter_t *iter = it->it; - ecs_world_t *world = iter->real_world; - EcsTarget *ft = table->data.columns[flat_tree_column].data.array; - int32_t ft_offset; - int32_t ft_count; - - if (first_for_table) { - ft_offset = it->flat_tree_offset = range->offset; - it->target_count = 1; - } else { - it->flat_tree_offset += ft[it->flat_tree_offset].count; - ft_offset = it->flat_tree_offset; - it->target_count ++; - } - - ecs_assert(ft_offset < ecs_table_count(table), - ECS_INTERNAL_ERROR, NULL); - - EcsTarget *cur = &ft[ft_offset]; - ft_count = cur->count; - bool is_last = (ft_offset + ft_count) >= range_end; - - int32_t i, field_count = ecs_vec_count(&ef->ft_terms); - flecs_flat_table_term_t *fields = ecs_vec_first(&ef->ft_terms); - for (i = 0; i < field_count; i ++) { - flecs_flat_table_term_t *field = &fields[i]; - ecs_vec_init_if_t(&field->monitor, flecs_flat_monitor_t); - int32_t field_index = field->field_index; - ecs_id_t id = it->it->ids[field_index]; - ecs_id_t flat_pair = table->type.array[flat_tree_column]; - ecs_entity_t rel = ECS_PAIR_FIRST(flat_pair); - ecs_entity_t tgt; - ecs_table_record_t *tr; - int32_t tgt_col = flecs_get_flattened_target( - world, cur, rel, id, &tgt, &tr); - if (tgt_col != -1) { - iter->sources[field_index] = tgt; - iter->columns[field_index] = /* encode flattened field */ - -(iter->field_count + tgt_col + 1); - ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); - - /* Keep track of maximum value encountered in target table - * dirty state so this doesn't have to be recomputed when - * synchronizing the query monitor. */ - ecs_vec_set_min_count_zeromem_t(NULL, &field->monitor, - flecs_flat_monitor_t, it->target_count); - ecs_table_t *tgt_table = tr->hdr.table; - int32_t *ds = flecs_table_get_dirty_state(world, tgt_table); - ecs_assert(ds != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_vec_get_t(&field->monitor, flecs_flat_monitor_t, - it->target_count - 1)->table_state = ds[tgt_col + 1]; - } else { - if (field->term->oper == EcsOptional) { - iter->columns[field_index] = 0; - iter->ptrs[field_index] = NULL; - } else { - it->prev = NULL; - break; - } - } - } - if (i != field_count) { - if (is_last) { - break; - } - } else { - found = true; - if ((ft_offset + ft_count) == range_end) { - result = EcsIterNextYield; - } else { - result = EcsIterYield; - } - } - - range->offset = ft_offset; - range->count = ft_count; - it->prev = table; - } - } while (!found); - - it->prev = table; - - if (!found) { - return EcsIterNext; - } else { - return result; - } -} diff --git a/vendors/flecs/src/entity_name.c b/vendors/flecs/src/entity_name.c index 69431b2b7..8b590986d 100644 --- a/vendors/flecs/src/entity_name.c +++ b/vendors/flecs/src/entity_name.c @@ -15,9 +15,10 @@ bool flecs_path_append( ecs_entity_t child, const char *sep, const char *prefix, - ecs_strbuf_t *buf) + ecs_strbuf_t *buf, + bool escape) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(sep[0] != 0, ECS_INVALID_PARAMETER, NULL); ecs_entity_t cur = 0; @@ -25,36 +26,82 @@ bool flecs_path_append( ecs_size_t name_len = 0; if (child && ecs_is_alive(world, child)) { - cur = ecs_get_target(world, child, EcsChildOf, 0); - if (cur) { - ecs_assert(cur != child, ECS_CYCLE_DETECTED, NULL); - if (cur != parent && (cur != EcsFlecsCore || prefix != NULL)) { - flecs_path_append(world, parent, cur, sep, prefix, buf); - if (!sep[1]) { - ecs_strbuf_appendch(buf, sep[0]); + ecs_record_t *r = flecs_entities_get(world, child); + bool hasName = false; + if (r && r->table) { + hasName = r->table->flags & EcsTableHasName; + } + + if (hasName) { + cur = ecs_get_target(world, child, EcsChildOf, 0); + if (cur) { + ecs_assert(cur != child, ECS_CYCLE_DETECTED, NULL); + if (cur != parent && (cur != EcsFlecsCore || prefix != NULL)) { + flecs_path_append(world, parent, cur, sep, prefix, buf, escape); + if (!sep[1]) { + ecs_strbuf_appendch(buf, sep[0]); + } else { + ecs_strbuf_appendstr(buf, sep); + } + } + } else if (prefix && prefix[0]) { + if (!prefix[1]) { + ecs_strbuf_appendch(buf, prefix[0]); } else { - ecs_strbuf_appendstr(buf, sep); + ecs_strbuf_appendstr(buf, prefix); } } - } else if (prefix && prefix[0]) { - if (!prefix[1]) { - ecs_strbuf_appendch(buf, prefix[0]); - } else { - ecs_strbuf_appendstr(buf, prefix); - } - } - const EcsIdentifier *id = ecs_get_pair( - world, child, EcsIdentifier, EcsName); - if (id) { - name = id->value; - name_len = id->length; - } + const EcsIdentifier *id = ecs_get_pair( + world, child, EcsIdentifier, EcsName); + if (id) { + name = id->value; + name_len = id->length; + } + } } if (name) { - ecs_strbuf_appendstrn(buf, name, name_len); + /* Check if we need to escape separator character */ + const char *sep_in_name = NULL; + if (!sep[1]) { + sep_in_name = strchr(name, sep[0]); + } + + if (sep_in_name || escape) { + const char *name_ptr; + char ch; + for (name_ptr = name; (ch = name_ptr[0]); name_ptr ++) { + char esc[3]; + if (ch != sep[0]) { + if (escape) { + flecs_chresc(esc, ch, '\"'); + ecs_strbuf_appendch(buf, esc[0]); + if (esc[1]) { + ecs_strbuf_appendch(buf, esc[1]); + } + } else { + ecs_strbuf_appendch(buf, ch); + } + } else { + if (!escape) { + ecs_strbuf_appendch(buf, '\\'); + ecs_strbuf_appendch(buf, sep[0]); + } else { + ecs_strbuf_appendlit(buf, "\\\\"); + flecs_chresc(esc, ch, '\"'); + ecs_strbuf_appendch(buf, esc[0]); + if (esc[1]) { + ecs_strbuf_appendch(buf, esc[1]); + } + } + } + } + } else { + ecs_strbuf_appendstrn(buf, name, name_len); + } } else { + ecs_strbuf_appendch(buf, '#'); ecs_strbuf_appendint(buf, flecs_uto(int64_t, (uint32_t)child)); } @@ -65,39 +112,26 @@ bool flecs_name_is_id( const char *name) { ecs_assert(name != NULL, ECS_INTERNAL_ERROR, NULL); - - if (!isdigit(name[0])) { - return false; - } - - ecs_size_t i, length = ecs_os_strlen(name); - for (i = 1; i < length; i ++) { - char ch = name[i]; - - if (!isdigit(ch)) { - break; + if (name[0] == '#') { + /* If name is not just digits it's not an id */ + const char *ptr; + char ch; + for (ptr = name + 1; (ch = ptr[0]); ptr ++) { + if (!isdigit(ch)) { + return false; + } } + return true; } - - return i >= length; + return false; } ecs_entity_t flecs_name_to_id( - const ecs_world_t *world, const char *name) { - int64_t result = atoll(name); - ecs_assert(result >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t alive = ecs_get_alive(world, (ecs_entity_t)result); - if (alive) { - return alive; - } else { - if ((uint32_t)result == (uint64_t)result) { - return (ecs_entity_t)result; - } else { - return 0; - } - } + ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(name[0] == '#', ECS_INVALID_PARAMETER, NULL); + return flecs_ito(uint64_t, atoll(name + 1)); } static @@ -136,18 +170,32 @@ static const char* flecs_path_elem( const char *path, const char *sep, - int32_t *len) + char **buffer_out, + ecs_size_t *size_out) { + char *buffer = NULL; + if (buffer_out) { + buffer = *buffer_out; + } + const char *ptr; char ch; int32_t template_nesting = 0; - int32_t count = 0; + int32_t pos = 0; + ecs_size_t size = size_out ? *size_out : 0; for (ptr = path; (ch = *ptr); ptr ++) { if (ch == '<') { template_nesting ++; } else if (ch == '>') { template_nesting --; + } else if (ch == '\\') { + ptr ++; + if (buffer) { + buffer[pos] = ptr[0]; + } + pos ++; + continue; } ecs_check(template_nesting >= 0, ECS_INVALID_PARAMETER, path); @@ -156,14 +204,31 @@ const char* flecs_path_elem( break; } - count ++; + if (buffer) { + if (pos == (size - 1)) { + if (size == ECS_NAME_BUFFER_LENGTH) { /* stack buffer */ + char *new_buffer = ecs_os_malloc(size * 2 + 1); + ecs_os_memcpy(new_buffer, buffer, size); + buffer = new_buffer; + } else { /* heap buffer */ + buffer = ecs_os_realloc(buffer, size * 2 + 1); + } + size *= 2; + } + + buffer[pos] = ch; + } + + pos ++; } - if (len) { - *len = count; + if (buffer) { + buffer[pos] = '\0'; + *buffer_out = buffer; + *size_out = size; } - if (count) { + if (pos) { return ptr; } else { return NULL; @@ -189,6 +254,7 @@ ecs_entity_t flecs_get_parent_from_path( const ecs_world_t *world, ecs_entity_t parent, const char **path_ptr, + const char *sep, const char *prefix, bool new_entity) { @@ -201,6 +267,24 @@ ecs_entity_t flecs_get_parent_from_path( start_from_root = true; } + if (path[0] == '#') { + parent = flecs_name_to_id(path); + + path ++; + while (path[0] && isdigit(path[0])) { + path ++; /* Skip id part of path */ + } + + /* Skip next separator so that the returned path points to the next + * name element. */ + ecs_size_t sep_len = ecs_os_strlen(sep); + if (!ecs_os_strncmp(path, sep, ecs_os_strlen(sep))) { + path += sep_len; + } + + start_from_root = true; + } + if (!start_from_root && !parent && new_entity) { parent = ecs_get_scope(world); } @@ -212,8 +296,8 @@ ecs_entity_t flecs_get_parent_from_path( static void flecs_on_set_symbol(ecs_iter_t *it) { - EcsIdentifier *n = ecs_field(it, EcsIdentifier, 1); - ecs_world_t *world = it->world; + EcsIdentifier *n = ecs_field(it, EcsIdentifier, 0); + ecs_world_t *world = it->real_world; int i; for (i = 0; i < it->count; i ++) { @@ -225,10 +309,9 @@ void flecs_on_set_symbol(ecs_iter_t *it) { void flecs_bootstrap_hierarchy(ecs_world_t *world) { ecs_observer(world, { - .entity = ecs_entity(world, {.add = {ecs_childof(EcsFlecsInternals)}}), - .filter.terms[0] = { - .id = ecs_pair(ecs_id(EcsIdentifier), EcsSymbol), - .src.flags = EcsSelf + .entity = ecs_entity(world, { .parent = EcsFlecsInternals }), + .query.terms[0] = { + .id = ecs_pair(ecs_id(EcsIdentifier), EcsSymbol) }, .callback = flecs_on_set_symbol, .events = {EcsOnSet}, @@ -245,7 +328,8 @@ void ecs_get_path_w_sep_buf( ecs_entity_t child, const char *sep, const char *prefix, - ecs_strbuf_t *buf) + ecs_strbuf_t *buf, + bool escape) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(buf != NULL, ECS_INVALID_PARAMETER, NULL); @@ -266,7 +350,7 @@ void ecs_get_path_w_sep_buf( } if (!child || parent != child) { - flecs_path_append(world, parent, child, sep, prefix, buf); + flecs_path_append(world, parent, child, sep, prefix, buf, escape); } else { ecs_strbuf_appendstrn(buf, "", 0); } @@ -283,7 +367,7 @@ char* ecs_get_path_w_sep( const char *prefix) { ecs_strbuf_t buf = ECS_STRBUF_INIT; - ecs_get_path_w_sep_buf(world, parent, child, sep, prefix, &buf); + ecs_get_path_w_sep_buf(world, parent, child, sep, prefix, &buf, false); return ecs_strbuf_get(&buf); } @@ -296,7 +380,7 @@ ecs_entity_t ecs_lookup_child( world = ecs_get_world(world); if (flecs_name_is_id(name)) { - ecs_entity_t result = flecs_name_to_id(world, name); + ecs_entity_t result = flecs_name_to_id(name); if (result && ecs_is_alive(world, result)) { if (parent && !ecs_has_pair(world, result, EcsChildOf, parent)) { return 0; @@ -318,32 +402,9 @@ ecs_entity_t ecs_lookup_child( ecs_entity_t ecs_lookup( const ecs_world_t *world, - const char *name) + const char *path) { - if (!name) { - return 0; - } - - ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); - world = ecs_get_world(world); - - ecs_entity_t e = flecs_get_builtin(name); - if (e) { - return e; - } - - if (flecs_name_is_id(name)) { - return flecs_name_to_id(world, name); - } - - e = flecs_name_index_find(&world->aliases, name, 0, 0); - if (e) { - return e; - } - - return ecs_lookup_child(world, 0, name); -error: - return 0; + return ecs_lookup_path_w_sep(world, 0, path, ".", NULL, true); } ecs_entity_t ecs_lookup_symbol( @@ -359,15 +420,16 @@ ecs_entity_t ecs_lookup_symbol( ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); world = ecs_get_world(world); - ecs_entity_t e = flecs_name_index_find(&world->symbols, name, 0, 0); - if (e) { - return e; + ecs_entity_t e = 0; + if (lookup_as_path) { + e = ecs_lookup_path_w_sep(world, 0, name, ".", NULL, recursive); } - if (lookup_as_path) { - return ecs_lookup_path_w_sep(world, 0, name, ".", NULL, recursive); + if (!e) { + e = flecs_name_index_find(&world->symbols, name, 0, 0); } + return e; error: return 0; } @@ -398,10 +460,9 @@ ecs_entity_t ecs_lookup_path_w_sep( return e; } - char buff[ECS_NAME_BUFFER_LENGTH]; - const char *ptr, *ptr_start; - char *elem = buff; - int32_t len, size = ECS_NAME_BUFFER_LENGTH; + char buff[ECS_NAME_BUFFER_LENGTH], *elem = buff; + const char *ptr; + int32_t size = ECS_NAME_BUFFER_LENGTH; ecs_entity_t cur; bool lookup_path_search = false; @@ -415,7 +476,16 @@ ecs_entity_t ecs_lookup_path_w_sep( sep = "."; } - parent = flecs_get_parent_from_path(stage, parent, &path, prefix, true); + parent = flecs_get_parent_from_path( + stage, parent, &path, sep, prefix, true); + + if (parent && !(parent = ecs_get_alive(world, parent))) { + return 0; + } + + if (!path[0]) { + return parent; + } if (!sep[0]) { return ecs_lookup_child(world, parent, path); @@ -423,24 +493,9 @@ ecs_entity_t ecs_lookup_path_w_sep( retry: cur = parent; - ptr_start = ptr = path; - - while ((ptr = flecs_path_elem(ptr, sep, &len))) { - if (len < size) { - ecs_os_memcpy(elem, ptr_start, len); - } else { - if (size == ECS_NAME_BUFFER_LENGTH) { - elem = NULL; - } - - elem = ecs_os_realloc(elem, len + 1); - ecs_os_memcpy(elem, ptr_start, len); - size = len + 1; - } - - elem[len] = '\0'; - ptr_start = ptr; + ptr = path; + while ((ptr = flecs_path_elem(ptr, sep, &elem, &size))) { cur = ecs_lookup_child(world, cur, elem); if (!cur) { goto tail; @@ -532,7 +587,7 @@ const char* ecs_set_name_prefix( ecs_world_t *world, const char *prefix) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); const char *old_prefix = world->info.name_prefix; world->info.name_prefix = prefix; return old_prefix; @@ -557,11 +612,15 @@ void flecs_add_path( ecs_add_pair(world, entity, EcsChildOf, parent); } + ecs_assert(name[0] != '#', ECS_INVALID_PARAMETER, + "path should not contain identifier with #"); + ecs_set_name(world, entity, name); if (defer_suspend) { + ecs_stage_t *stage = flecs_stage_from_world(&world); flecs_resume_readonly(real_world, &srs); - flecs_defer_path((ecs_stage_t*)world, parent, entity, name); + flecs_defer_path(stage, parent, entity, name); } } @@ -581,7 +640,7 @@ ecs_entity_t ecs_add_path_w_sep( if (!path) { if (!entity) { - entity = ecs_new_id(world); + entity = ecs_new(world); } if (parent) { @@ -592,39 +651,25 @@ ecs_entity_t ecs_add_path_w_sep( } bool root_path = flecs_is_root_path(path, prefix); - parent = flecs_get_parent_from_path(world, parent, &path, prefix, !entity); + parent = flecs_get_parent_from_path( + world, parent, &path, sep, prefix, !entity); char buff[ECS_NAME_BUFFER_LENGTH]; const char *ptr = path; - const char *ptr_start = path; char *elem = buff; - int32_t len, size = ECS_NAME_BUFFER_LENGTH; + int32_t size = ECS_NAME_BUFFER_LENGTH; /* If we're in deferred/readonly mode suspend it, so that the name index is * immediately updated. Without this, we could create multiple entities for * the same name in a single command queue. */ - bool suspend_defer = ecs_poly_is(world, ecs_stage_t) && - (ecs_get_stage_count(world) <= 1); + bool suspend_defer = ecs_is_deferred(world) && + !(world->flags & EcsWorldMultiThreaded); + ecs_entity_t cur = parent; char *name = NULL; if (sep[0]) { - while ((ptr = flecs_path_elem(ptr, sep, &len))) { - if (len < size) { - ecs_os_memcpy(elem, ptr_start, len); - } else { - if (size == ECS_NAME_BUFFER_LENGTH) { - elem = NULL; - } - - elem = ecs_os_realloc(elem, len + 1); - ecs_os_memcpy(elem, ptr_start, len); - size = len + 1; - } - - elem[len] = '\0'; - ptr_start = ptr; - + while ((ptr = flecs_path_elem(ptr, sep, &elem, &size))) { ecs_entity_t e = ecs_lookup_child(world, cur, elem); if (!e) { if (name) { @@ -635,7 +680,7 @@ ecs_entity_t ecs_add_path_w_sep( /* If this is the last entity in the path, use the provided id */ bool last_elem = false; - if (!flecs_path_elem(ptr, sep, NULL)) { + if (!flecs_path_elem(ptr, sep, NULL, NULL)) { e = entity; last_elem = true; } @@ -643,10 +688,10 @@ ecs_entity_t ecs_add_path_w_sep( if (!e) { if (last_elem) { ecs_entity_t prev = ecs_set_scope(world, 0); - e = ecs_new(world, 0); + e = ecs_entity(world, {0}); ecs_set_scope(world, prev); } else { - e = ecs_new_id(world); + e = ecs_new(world); } } diff --git a/vendors/flecs/src/filter.c b/vendors/flecs/src/filter.c deleted file mode 100644 index feced806d..000000000 --- a/vendors/flecs/src/filter.c +++ /dev/null @@ -1,3112 +0,0 @@ -/** - * @file filter.c - * @brief Uncached query implementation. - * - * Uncached queries (filters) are stateless objects that do not cache their - * results. This file contains the creation and validation of uncached queries - * and code for query iteration. - * - * There file contains the implementation for term queries and filters. Term - * queries are uncached queries that only apply to a single term. Filters are - * uncached queries that support multiple terms. Filters are built on top of - * term queries: before iteration a filter will first find a "pivot" term (the - * term with the smallest number of elements), and create a term iterator for - * it. The output of that term iterator is then evaluated against the rest of - * the terms of the filter. - * - * Cached queries and observers are built using filters. - */ - -#include "private_api.h" -#include - -ecs_filter_t ECS_FILTER_INIT = { .hdr = { .magic = ecs_filter_t_magic }}; - -/* Helper type for passing around context required for error messages */ -typedef struct { - const ecs_world_t *world; - ecs_filter_t *filter; - ecs_term_t *term; - int32_t term_index; -} ecs_filter_finalize_ctx_t; - -static -char* flecs_filter_str( - const ecs_world_t *world, - const ecs_filter_t *filter, - const ecs_filter_finalize_ctx_t *ctx, - int32_t *term_start_out); - -static -void flecs_filter_error( - const ecs_filter_finalize_ctx_t *ctx, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - - int32_t term_start = 0; - - char *expr = NULL; - if (ctx->filter) { - expr = flecs_filter_str(ctx->world, ctx->filter, ctx, &term_start); - } else { - expr = ecs_term_str(ctx->world, ctx->term); - } - const char *name = NULL; - if (ctx->filter && ctx->filter->entity) { - name = ecs_get_name(ctx->filter->world, ctx->filter->entity); - } - ecs_parser_errorv(name, expr, term_start, fmt, args); - ecs_os_free(expr); - - va_end(args); -} - -static -int flecs_term_id_finalize_flags( - ecs_term_id_t *term_id, - ecs_filter_finalize_ctx_t *ctx) -{ - if ((term_id->flags & EcsIsEntity) && (term_id->flags & EcsIsVariable)) { - flecs_filter_error(ctx, "cannot set both IsEntity and IsVariable"); - return -1; - } - - if (!(term_id->flags & (EcsIsEntity|EcsIsVariable|EcsIsName))) { - if (term_id->id || term_id->name) { - if (term_id->id == EcsThis || - term_id->id == EcsWildcard || - term_id->id == EcsAny || - term_id->id == EcsVariable) - { - /* Builtin variable ids default to variable */ - term_id->flags |= EcsIsVariable; - } else { - term_id->flags |= EcsIsEntity; - } - } - } - - if (term_id->flags & EcsParent) { - term_id->flags |= EcsUp; - term_id->trav = EcsChildOf; - } - - if ((term_id->flags & EcsCascade) && !(term_id->flags & (EcsUp|EcsDown))) { - term_id->flags |= EcsUp; - } - - if ((term_id->flags & (EcsUp|EcsDown)) && !term_id->trav) { - term_id->trav = EcsIsA; - } - - if (term_id->trav && !(term_id->flags & EcsTraverseFlags)) { - term_id->flags |= EcsUp; - } - - return 0; -} - -static -int flecs_term_id_lookup( - const ecs_world_t *world, - ecs_entity_t scope, - ecs_term_id_t *term_id, - bool free_name, - ecs_filter_finalize_ctx_t *ctx) -{ - const char *name = term_id->name; - if (!name) { - return 0; - } - - if (term_id->flags & EcsIsVariable) { - if (!ecs_os_strcmp(name, "This") || !ecs_os_strcmp(name, "this")) { - term_id->id = EcsThis; - if (free_name) { - /* Safe, if free_name is true the filter owns the name */ - ecs_os_free(ECS_CONST_CAST(char*, term_id->name)); - } - term_id->name = NULL; - } - return 0; - } else if (term_id->flags & EcsIsName) { - return 0; - } - - ecs_assert(term_id->flags & EcsIsEntity, ECS_INTERNAL_ERROR, NULL); - - if (ecs_identifier_is_0(name)) { - if (term_id->id) { - flecs_filter_error(ctx, "name '0' does not match entity id"); - return -1; - } - return 0; - } - - ecs_entity_t e = ecs_lookup_symbol(world, name, true, true); - if (scope && !e) { - e = ecs_lookup_child(world, scope, name); - } - - if (!e) { - if (ctx->filter && (ctx->filter->flags & EcsFilterUnresolvedByName)) { - term_id->flags |= EcsIsName; - term_id->flags &= ~EcsIsEntity; - } else { - flecs_filter_error(ctx, "unresolved identifier '%s'", name); - return -1; - } - } - - if (term_id->id && term_id->id != e) { - char *e_str = ecs_get_fullpath(world, term_id->id); - flecs_filter_error(ctx, "name '%s' does not match term.id '%s'", - name, e_str); - ecs_os_free(e_str); - return -1; - } - - term_id->id = e; - - if (!ecs_os_strcmp(name, "*") || !ecs_os_strcmp(name, "_") || - !ecs_os_strcmp(name, "$")) - { - term_id->flags &= ~EcsIsEntity; - term_id->flags |= EcsIsVariable; - } - - /* Check if looked up id is alive (relevant for numerical ids) */ - if (!(term_id->flags & EcsIsName)) { - if (!ecs_is_alive(world, term_id->id)) { - flecs_filter_error(ctx, "identifier '%s' is not alive", term_id->name); - return -1; - } - - if (free_name) { - /* Safe, if free_name is true, the filter owns the name */ - ecs_os_free(ECS_CONST_CAST(char*, name)); - } - - term_id->name = NULL; - } - - return 0; -} - -static -int flecs_term_ids_finalize( - const ecs_world_t *world, - ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) -{ - ecs_term_id_t *src = &term->src; - ecs_term_id_t *first = &term->first; - ecs_term_id_t *second = &term->second; - - /* Include inherited components (like from prefabs) by default for src */ - if (!(src->flags & EcsTraverseFlags)) { - src->flags |= EcsSelf | EcsUp; - } - - /* Include subsets for component by default, to support inheritance */ - if (!(first->flags & EcsTraverseFlags)) { - first->flags |= EcsSelf; - if (first->id && first->flags & EcsIsEntity) { - if (flecs_id_record_get(world, ecs_pair(EcsIsA, first->id))) { - first->flags |= EcsDown; - } - } - } - - /* Traverse Self by default for pair target */ - if (!(second->flags & EcsTraverseFlags)) { - second->flags |= EcsSelf; - } - - /* Source defaults to This */ - if ((src->id == 0) && (src->name == NULL) && !(src->flags & EcsIsEntity)) { - src->id = EcsThis; - src->flags |= EcsIsVariable; - } - - /* Initialize term identifier flags */ - if (flecs_term_id_finalize_flags(src, ctx)) { - return -1; - } - if (flecs_term_id_finalize_flags(first, ctx)) { - return -1; - } - - if (flecs_term_id_finalize_flags(second, ctx)) { - return -1; - } - - /* Lookup term identifiers by name */ - if (flecs_term_id_lookup(world, 0, src, term->move, ctx)) { - return -1; - } - if (flecs_term_id_lookup(world, 0, first, term->move, ctx)) { - return -1; - } - - ecs_entity_t first_id = 0; - ecs_entity_t oneof = 0; - if (first->flags & EcsIsEntity) { - first_id = first->id; - - /* If first element of pair has OneOf property, lookup second element of - * pair in the value of the OneOf property */ - oneof = flecs_get_oneof(world, first_id); - } - - if (flecs_term_id_lookup(world, oneof, &term->second, term->move, ctx)) { - return -1; - } - - /* If source is 0, reset traversal flags */ - if (src->id == 0 && src->flags & EcsIsEntity) { - src->flags &= ~EcsTraverseFlags; - src->trav = 0; - } - /* If second is 0, reset traversal flags */ - if (second->id == 0 && second->flags & EcsIsEntity) { - second->flags &= ~EcsTraverseFlags; - second->trav = 0; - } - - /* If source is wildcard, term won't return any data */ - if ((src->flags & EcsIsVariable) && ecs_id_is_wildcard(src->id)) { - term->inout |= EcsInOutNone; - } - - return 0; -} - -static -ecs_entity_t flecs_term_id_get_entity( - const ecs_term_id_t *term_id) -{ - if (term_id->flags & EcsIsEntity) { - return term_id->id; /* Id is known */ - } else if (term_id->flags & EcsIsVariable) { - /* Return wildcard for variables, as they aren't known yet */ - if (term_id->id != EcsAny) { - /* Any variable should not use wildcard, as this would return all - * ids matching a wildcard, whereas Any returns the first match */ - return EcsWildcard; - } else { - return EcsAny; - } - } else { - return 0; /* Term id is uninitialized */ - } -} - -static -int flecs_term_populate_id( - ecs_term_t *term) -{ - ecs_entity_t first = flecs_term_id_get_entity(&term->first); - ecs_entity_t second = flecs_term_id_get_entity(&term->second); - ecs_id_t role = term->id_flags; - - if (first & ECS_ID_FLAGS_MASK) { - return -1; - } - if (second & ECS_ID_FLAGS_MASK) { - return -1; - } - - if ((second || term->second.flags == EcsIsEntity)) { - role = term->id_flags |= ECS_PAIR; - } - - if (!second && !ECS_HAS_ID_FLAG(role, PAIR)) { - term->id = first | role; - } else { - term->id = ecs_pair(first, second) | role; - } - - return 0; -} - -static -int flecs_term_populate_from_id( - const ecs_world_t *world, - ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) -{ - ecs_entity_t first = 0; - ecs_entity_t second = 0; - ecs_id_t role = term->id & ECS_ID_FLAGS_MASK; - - if (!role && term->id_flags) { - role = term->id_flags; - term->id |= role; - } - - if (term->id_flags && term->id_flags != role) { - flecs_filter_error(ctx, "mismatch between term.id & term.id_flags"); - return -1; - } - - term->id_flags = role; - - if (ECS_HAS_ID_FLAG(term->id, PAIR)) { - first = ECS_PAIR_FIRST(term->id); - second = ECS_PAIR_SECOND(term->id); - - if (!first) { - flecs_filter_error(ctx, "missing first element in term.id"); - return -1; - } - if (!second) { - if (first != EcsChildOf) { - flecs_filter_error(ctx, "missing second element in term.id"); - return -1; - } else { - /* (ChildOf, 0) is allowed so filter can be used to efficiently - * query for root entities */ - } - } - } else { - first = term->id & ECS_COMPONENT_MASK; - if (!first) { - flecs_filter_error(ctx, "missing first element in term.id"); - return -1; - } - } - - ecs_entity_t term_first = flecs_term_id_get_entity(&term->first); - if (term_first) { - if ((uint32_t)term_first != (uint32_t)first) { - flecs_filter_error(ctx, "mismatch between term.id and term.first"); - return -1; - } - } else { - if (!(term->first.id = ecs_get_alive(world, first))) { - term->first.id = first; - } - } - - ecs_entity_t term_second = flecs_term_id_get_entity(&term->second); - if (term_second) { - if ((uint32_t)term_second != second) { - flecs_filter_error(ctx, "mismatch between term.id and term.second"); - return -1; - } - } else if (second) { - if (!(term->second.id = ecs_get_alive(world, second))) { - term->second.id = second; - } - } - - return 0; -} - -static -int flecs_term_verify_eq_pred( - const ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) -{ - ecs_entity_t first_id = term->first.id; - const ecs_term_id_t *second = &term->second; - const ecs_term_id_t *src = &term->src; - - if (term->oper != EcsAnd && term->oper != EcsNot && term->oper != EcsOr) { - flecs_filter_error(ctx, "invalid operator combination"); - goto error; - } - - if ((src->flags & EcsIsName) && (second->flags & EcsIsName)) { - flecs_filter_error(ctx, "both sides of operator cannot be a name"); - goto error; - } - - if ((src->flags & EcsIsEntity) && (second->flags & EcsIsEntity)) { - flecs_filter_error(ctx, "both sides of operator cannot be an entity"); - goto error; - } - - if (!(src->flags & EcsIsVariable)) { - flecs_filter_error(ctx, "left-hand of operator must be a variable"); - goto error; - } - - if (first_id == EcsPredMatch && !(second->flags & EcsIsName)) { - flecs_filter_error(ctx, "right-hand of match operator must be a string"); - goto error; - } - - if ((src->flags & EcsIsVariable) && (second->flags & EcsIsVariable)) { - if (src->id && src->id == second->id) { - flecs_filter_error(ctx, "both sides of operator are equal"); - goto error; - } - if (src->name && second->name && !ecs_os_strcmp(src->name, second->name)) { - flecs_filter_error(ctx, "both sides of operator are equal"); - goto error; - } - } - - return 0; -error: - return -1; -} - -static -int flecs_term_verify( - const ecs_world_t *world, - const ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) -{ - const ecs_term_id_t *first = &term->first; - const ecs_term_id_t *second = &term->second; - const ecs_term_id_t *src = &term->src; - ecs_entity_t first_id = 0, second_id = 0; - ecs_id_t role = term->id_flags; - ecs_id_t id = term->id; - - if ((src->flags & EcsIsName) && (second->flags & EcsIsName)) { - flecs_filter_error(ctx, "mismatch between term.id_flags & term.id"); - return -1; - } - - if (first->flags & EcsIsEntity) { - first_id = first->id; - } - if (second->flags & EcsIsEntity) { - second_id = second->id; - } - - if (first_id == EcsPredEq || first_id == EcsPredMatch || first_id == EcsPredLookup) { - return flecs_term_verify_eq_pred(term, ctx); - } - - if (role != (id & ECS_ID_FLAGS_MASK)) { - flecs_filter_error(ctx, "mismatch between term.id_flags & term.id"); - return -1; - } - - if (ecs_term_id_is_set(second) && !ECS_HAS_ID_FLAG(role, PAIR)) { - flecs_filter_error(ctx, "expected PAIR flag for term with pair"); - return -1; - } else if (!ecs_term_id_is_set(second) && ECS_HAS_ID_FLAG(role, PAIR)) { - if (first_id != EcsChildOf) { - flecs_filter_error(ctx, "unexpected PAIR flag for term without pair"); - return -1; - } else { - /* Exception is made for ChildOf so we can use (ChildOf, 0) to match - * all entities in the root */ - } - } - - if (!ecs_term_id_is_set(src)) { - flecs_filter_error(ctx, "term.src is not initialized"); - return -1; - } - - if (!ecs_term_id_is_set(first)) { - flecs_filter_error(ctx, "term.first is not initialized"); - return -1; - } - - if (ECS_HAS_ID_FLAG(role, PAIR)) { - if (!ECS_PAIR_FIRST(id)) { - flecs_filter_error(ctx, "invalid 0 for first element in pair id"); - return -1; - } - if ((ECS_PAIR_FIRST(id) != EcsChildOf) && !ECS_PAIR_SECOND(id)) { - flecs_filter_error(ctx, "invalid 0 for second element in pair id"); - return -1; - } - - if ((first->flags & EcsIsEntity) && - (ecs_entity_t_lo(first_id) != ECS_PAIR_FIRST(id))) - { - flecs_filter_error(ctx, "mismatch between term.id and term.first"); - return -1; - } - if ((first->flags & EcsIsVariable) && - !ecs_id_is_wildcard(ECS_PAIR_FIRST(id))) - { - char *id_str = ecs_id_str(world, id); - flecs_filter_error(ctx, - "expected wildcard for variable term.first (got %s)", id_str); - ecs_os_free(id_str); - return -1; - } - - if ((second->flags & EcsIsEntity) && - (ecs_entity_t_lo(second_id) != ECS_PAIR_SECOND(id))) - { - flecs_filter_error(ctx, "mismatch between term.id and term.second"); - return -1; - } - if ((second->flags & EcsIsVariable) && - !ecs_id_is_wildcard(ECS_PAIR_SECOND(id))) - { - char *id_str = ecs_id_str(world, id); - flecs_filter_error(ctx, - "expected wildcard for variable term.second (got %s)", id_str); - ecs_os_free(id_str); - return -1; - } - } else { - ecs_entity_t component = id & ECS_COMPONENT_MASK; - if (!component) { - flecs_filter_error(ctx, "missing component id"); - return -1; - } - if ((first->flags & EcsIsEntity) && - (ecs_entity_t_lo(first_id) != ecs_entity_t_lo(component))) - { - flecs_filter_error(ctx, "mismatch between term.id and term.first"); - return -1; - } - if ((first->flags & EcsIsVariable) && !ecs_id_is_wildcard(component)) { - char *id_str = ecs_id_str(world, id); - flecs_filter_error(ctx, - "expected wildcard for variable term.first (got %s)", id_str); - ecs_os_free(id_str); - return -1; - } - } - - if (first_id) { - if (ecs_term_id_is_set(second)) { - ecs_flags32_t mask = EcsIsEntity | EcsIsVariable; - if ((src->flags & mask) == (second->flags & mask)) { - bool is_same = false; - if (src->flags & EcsIsEntity) { - is_same = src->id == second->id; - } else if (src->name && second->name) { - is_same = !ecs_os_strcmp(src->name, second->name); - } - - if (is_same && ecs_has_id(world, first_id, EcsAcyclic) - && !(term->flags & EcsTermReflexive)) - { - char *pred_str = ecs_get_fullpath(world, term->first.id); - flecs_filter_error(ctx, "term with acyclic relationship" - " '%s' cannot have same subject and object", - pred_str); - ecs_os_free(pred_str); - return -1; - } - } - } - - if (second_id && !ecs_id_is_wildcard(second_id)) { - ecs_entity_t oneof = flecs_get_oneof(world, first_id); - if (oneof) { - if (!ecs_has_pair(world, second_id, EcsChildOf, oneof)) { - char *second_str = ecs_get_fullpath(world, second_id); - char *oneof_str = ecs_get_fullpath(world, oneof); - char *id_str = ecs_id_str(world, term->id); - flecs_filter_error(ctx, - "invalid target '%s' for %s: must be child of '%s'", - second_str, id_str, oneof_str); - ecs_os_free(second_str); - ecs_os_free(oneof_str); - ecs_os_free(id_str); - return -1; - } - } - } - } - - if (term->src.trav) { - if (!ecs_has_id(world, term->src.trav, EcsTraversable)) { - char *r_str = ecs_get_fullpath(world, term->src.trav); - flecs_filter_error(ctx, - "cannot traverse non-traversable relationship '%s'", r_str); - ecs_os_free(r_str); - return -1; - } - } - - return 0; -} - -static -int flecs_term_finalize( - const ecs_world_t *world, - ecs_term_t *term, - ecs_filter_finalize_ctx_t *ctx) -{ - ctx->term = term; - - ecs_term_id_t *src = &term->src; - ecs_term_id_t *first = &term->first; - ecs_term_id_t *second = &term->second; - ecs_flags32_t first_flags = first->flags; - ecs_flags32_t src_flags = src->flags; - ecs_flags32_t second_flags = second->flags; - - if (term->id) { - if (flecs_term_populate_from_id(world, term, ctx)) { - return -1; - } - } - - if (flecs_term_ids_finalize(world, term, ctx)) { - return -1; - } - - if ((first->flags & EcsIsVariable) && (term->first.id == EcsAny)) { - term->flags |= EcsTermMatchAny; - } - if ((second->flags & EcsIsVariable) && (term->second.id == EcsAny)) { - term->flags |= EcsTermMatchAny; - } - if ((src->flags & EcsIsVariable) && (term->src.id == EcsAny)) { - term->flags |= EcsTermMatchAnySrc; - } - - /* If EcsVariable is used by itself, assign to predicate (singleton) */ - if ((src->id == EcsVariable) && (src->flags & EcsIsVariable)) { - src->id = first->id; - src->flags &= ~(EcsIsVariable | EcsIsEntity); - src->flags |= first->flags & (EcsIsVariable | EcsIsEntity); - } - if ((second->id == EcsVariable) && (second->flags & EcsIsVariable)) { - second->id = first->id; - second->flags &= ~(EcsIsVariable | EcsIsEntity); - second->flags |= first->flags & (EcsIsVariable | EcsIsEntity); - } - - ecs_flags32_t mask = EcsIsEntity | EcsIsVariable; - if ((src->flags & mask) == (second->flags & mask)) { - bool is_same = false; - if (src->flags & EcsIsEntity) { - is_same = src->id == second->id; - } else if (src->name && second->name) { - is_same = !ecs_os_strcmp(src->name, second->name); - } - if (is_same) { - term->flags |= EcsTermSrcSecondEq; - } - } - if ((src->flags & mask) == (first->flags & mask)) { - bool is_same = false; - if (src->flags & EcsIsEntity) { - is_same = src->id == first->id; - } else if (src->name && first->name) { - is_same = !ecs_os_strcmp(src->name, first->name); - } - if (is_same) { - term->flags |= EcsTermSrcFirstEq; - } - } - - if (!term->id) { - if (flecs_term_populate_id(term)) { - return -1; - } - } - - /* If term queries for !(ChildOf, _), translate it to the builtin - * (ChildOf, 0) index which is a cheaper way to find root entities */ - if (term->oper == EcsNot && term->id == ecs_pair(EcsChildOf, EcsAny)) { - term->oper = EcsAnd; - term->id = ecs_pair(EcsChildOf, 0); - second->id = 0; - second->flags |= EcsIsEntity; - second->flags &= ~EcsIsVariable; - } - - ecs_entity_t first_id = 0; - if (first->flags & EcsIsEntity) { - first_id = first->id; - } - - term->idr = flecs_query_id_record_get(world, term->id); - ecs_flags32_t id_flags = term->idr ? term->idr->flags : 0; - - if (first_id) { - ecs_entity_t first_trav = first->trav; - - /* If component is inherited from, set correct traversal flags */ - ecs_flags32_t first_trav_flags = first_flags & EcsTraverseFlags; - if (!first_trav && first_trav_flags != EcsSelf) { - /* Inheritance uses IsA by default, but can use any relationship */ - first_trav = EcsIsA; - } - - ecs_record_t *trav_record = NULL; - ecs_table_t *trav_table = NULL; - if (first_trav) { - trav_record = flecs_entities_get(world, first_trav); - trav_table = trav_record ? trav_record->table : NULL; - if (first_trav != EcsIsA) { - if (!trav_table || !ecs_table_has_id(world, trav_table, EcsTraversable)) { - flecs_filter_error(ctx, "first.trav is not traversable"); - return -1; - } - } - } - - /* Only enable inheritance for ids which are inherited from at the time - * of filter creation. To force component inheritance to be evaluated, - * an application can explicitly set traversal flags. */ - if ((first_trav_flags & EcsDown) || - flecs_id_record_get(world, ecs_pair(first_trav, first->id))) - { - if (first_trav_flags == EcsSelf) { - flecs_filter_error(ctx, "first.trav specified with self"); - return -1; - } - - if (!first_trav_flags || (first_trav_flags & EcsDown)) { - term->flags |= EcsTermIdInherited; - first->trav = first_trav; - if (!first_trav_flags) { - first->flags &= ~EcsTraverseFlags; - first->flags |= EcsDown; - ecs_assert(trav_table != NULL, ECS_INTERNAL_ERROR, NULL); - if ((first_trav == EcsIsA) || ecs_table_has_id( - world, trav_table, EcsReflexive)) - { - first->flags |= EcsSelf; - } - } - } - } - - /* Don't traverse ids that cannot be inherited */ - if ((id_flags & EcsIdDontInherit) && (src->trav == EcsIsA)) { - if (src_flags & (EcsUp | EcsDown)) { - flecs_filter_error(ctx, - "traversing not allowed for id that can't be inherited"); - return -1; - } - src->flags &= ~(EcsUp | EcsDown); - src->trav = 0; - } - - /* If component id is final, don't attempt component inheritance */ - ecs_record_t *first_record = flecs_entities_get(world, first_id); - ecs_table_t *first_table = first_record ? first_record->table : NULL; - if (first_table) { - if (ecs_table_has_id(world, first_table, EcsFinal)) { - if (first_flags & EcsDown) { - flecs_filter_error(ctx, "final id cannot be traversed down"); - return -1; - } - } - - /* Add traversal flags for transitive relationships */ - if (!(second_flags & EcsTraverseFlags) && ecs_term_id_is_set(second)) { - if (!((src->flags & EcsIsVariable) && (src->id == EcsAny))) { - if (!((second->flags & EcsIsVariable) && (second->id == EcsAny))) { - if (ecs_table_has_id(world, first_table, EcsTransitive)) { - second->flags |= EcsSelf|EcsUp|EcsTraverseAll; - second->trav = first_id; - term->flags |= EcsTermTransitive; - } - } - } - } - - if (ecs_table_has_id(world, first_table, EcsReflexive)) { - term->flags |= EcsTermReflexive; - } - } - } - - if (first->id == EcsVariable) { - flecs_filter_error(ctx, "invalid $ for term.first"); - return -1; - } - - if (term->id_flags & ECS_AND) { - term->oper = EcsAndFrom; - term->id &= ECS_COMPONENT_MASK; - term->id_flags = 0; - } - - if (term->oper == EcsAndFrom || term->oper == EcsOrFrom || term->oper == EcsNotFrom) { - if (term->inout != EcsInOutDefault && term->inout != EcsInOutNone) { - flecs_filter_error(ctx, - "invalid inout value for AndFrom/OrFrom/NotFrom term"); - return -1; - } - } - - /* Is term trivial */ - bool trivial_term = true; - if (term->oper != EcsAnd) { - trivial_term = false; - } - if (ecs_id_is_wildcard(term->id)) { - if (!(term->idr && term->idr->flags & EcsIdExclusive)) { - trivial_term = false; - } - if (first->flags & EcsIsVariable) { - if (!ecs_id_is_wildcard(first->id) || first->id == EcsAny) { - trivial_term = false; - } - } - if (second->flags & EcsIsVariable) { - if (!ecs_id_is_wildcard(second->id) || second->id == EcsAny) { - trivial_term = false; - } - } - } - - if (term->flags & EcsTermIdInherited) { - trivial_term = false; - } - if (src->trav && src->trav != EcsIsA) { - trivial_term = false; - } - if (first->trav && first->trav != EcsIsA) { - trivial_term = false; - } - if (second->trav && second->trav != EcsIsA) { - trivial_term = false; - } - if (!(src->flags & EcsSelf)) { - trivial_term = false; - } - if (trivial_term) { - ECS_BIT_SET(term->flags, EcsTermIsTrivial); - } - - if (flecs_term_verify(world, term, ctx)) { - return -1; - } - - return 0; -} - -ecs_id_t flecs_to_public_id( - ecs_id_t id) -{ - if (ECS_PAIR_FIRST(id) == EcsUnion) { - return ecs_pair(ECS_PAIR_SECOND(id), EcsWildcard); - } else { - return id; - } -} - -ecs_id_t flecs_from_public_id( - ecs_world_t *world, - ecs_id_t id) -{ - if (ECS_HAS_ID_FLAG(id, PAIR)) { - ecs_entity_t first = ECS_PAIR_FIRST(id); - ecs_id_record_t *idr = flecs_id_record_ensure(world, - ecs_pair(first, EcsWildcard)); - if (idr->flags & EcsIdUnion) { - return ecs_pair(EcsUnion, first); - } - } - - return id; -} - -bool ecs_identifier_is_0( - const char *id) -{ - return id[0] == '0' && !id[1]; -} - -bool ecs_id_match( - ecs_id_t id, - ecs_id_t pattern) -{ - if (id == pattern) { - return true; - } - - if (ECS_HAS_ID_FLAG(pattern, PAIR)) { - if (!ECS_HAS_ID_FLAG(id, PAIR)) { - return false; - } - - ecs_entity_t id_rel = ECS_PAIR_FIRST(id); - ecs_entity_t id_obj = ECS_PAIR_SECOND(id); - ecs_entity_t pattern_rel = ECS_PAIR_FIRST(pattern); - ecs_entity_t pattern_obj = ECS_PAIR_SECOND(pattern); - - ecs_check(id_rel != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(id_obj != 0, ECS_INVALID_PARAMETER, NULL); - - ecs_check(pattern_rel != 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(pattern_obj != 0, ECS_INVALID_PARAMETER, NULL); - - if (pattern_rel == EcsWildcard) { - if (pattern_obj == EcsWildcard || pattern_obj == id_obj) { - return true; - } - } else if (pattern_rel == EcsFlag) { - /* Used for internals, helps to keep track of which ids are used in - * pairs that have additional flags (like OVERRIDE and TOGGLE) */ - if (ECS_HAS_ID_FLAG(id, PAIR) && !ECS_IS_PAIR(id)) { - if (ECS_PAIR_FIRST(id) == pattern_obj) { - return true; - } - if (ECS_PAIR_SECOND(id) == pattern_obj) { - return true; - } - } - } else if (pattern_obj == EcsWildcard) { - if (pattern_rel == id_rel) { - return true; - } - } - } else { - if ((id & ECS_ID_FLAGS_MASK) != (pattern & ECS_ID_FLAGS_MASK)) { - return false; - } - - if ((ECS_COMPONENT_MASK & pattern) == EcsWildcard) { - return true; - } - } - -error: - return false; -} - -bool ecs_id_is_pair( - ecs_id_t id) -{ - return ECS_HAS_ID_FLAG(id, PAIR); -} - -bool ecs_id_is_wildcard( - ecs_id_t id) -{ - if ((id == EcsWildcard) || (id == EcsAny)) { - return true; - } - - bool is_pair = ECS_IS_PAIR(id); - if (!is_pair) { - return false; - } - - ecs_entity_t first = ECS_PAIR_FIRST(id); - ecs_entity_t second = ECS_PAIR_SECOND(id); - - return (first == EcsWildcard) || (second == EcsWildcard) || - (first == EcsAny) || (second == EcsAny); -} - -bool ecs_id_is_valid( - const ecs_world_t *world, - ecs_id_t id) -{ - if (!id) { - return false; - } - if (ecs_id_is_wildcard(id)) { - return false; - } - - if (ECS_HAS_ID_FLAG(id, PAIR)) { - if (!ECS_PAIR_FIRST(id)) { - return false; - } - if (!ECS_PAIR_SECOND(id)) { - return false; - } - } else if (id & ECS_ID_FLAGS_MASK) { - if (!ecs_is_valid(world, id & ECS_COMPONENT_MASK)) { - return false; - } - } - - return true; -} - -ecs_flags32_t ecs_id_get_flags( - const ecs_world_t *world, - ecs_id_t id) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (idr) { - return idr->flags; - } else { - return 0; - } -} - -bool ecs_term_id_is_set( - const ecs_term_id_t *id) -{ - return id->id != 0 || id->name != NULL || id->flags & EcsIsEntity; -} - -bool ecs_term_is_initialized( - const ecs_term_t *term) -{ - return term->id != 0 || ecs_term_id_is_set(&term->first); -} - -bool ecs_term_match_this( - const ecs_term_t *term) -{ - return (term->src.flags & EcsIsVariable) && (term->src.id == EcsThis); -} - -bool ecs_term_match_0( - const ecs_term_t *term) -{ - return (term->src.id == 0) && (term->src.flags & EcsIsEntity); -} - -int ecs_term_finalize( - const ecs_world_t *world, - ecs_term_t *term) -{ - ecs_filter_finalize_ctx_t ctx = {0}; - ctx.world = world; - ctx.term = term; - return flecs_term_finalize(world, term, &ctx); -} - -ecs_term_t ecs_term_copy( - const ecs_term_t *src) -{ - ecs_term_t dst = *src; - dst.name = ecs_os_strdup(src->name); - dst.first.name = ecs_os_strdup(src->first.name); - dst.src.name = ecs_os_strdup(src->src.name); - dst.second.name = ecs_os_strdup(src->second.name); - return dst; -} - -ecs_term_t ecs_term_move( - ecs_term_t *src) -{ - if (src->move) { - ecs_term_t dst = *src; - src->name = NULL; - src->first.name = NULL; - src->src.name = NULL; - src->second.name = NULL; - dst.move = false; - return dst; - } else { - ecs_term_t dst = ecs_term_copy(src); - dst.move = false; - return dst; - } -} - -void ecs_term_fini( - ecs_term_t *term) -{ - /* Safe, values are owned by term */ - ecs_os_free(ECS_CONST_CAST(char*, term->first.name)); - ecs_os_free(ECS_CONST_CAST(char*, term->src.name)); - ecs_os_free(ECS_CONST_CAST(char*, term->second.name)); - ecs_os_free(term->name); - - term->first.name = NULL; - term->src.name = NULL; - term->second.name = NULL; - term->name = NULL; -} - -static -ecs_term_t* flecs_filter_or_other_type( - ecs_filter_t *f, - int32_t t) -{ - ecs_term_t *term = &f->terms[t]; - ecs_term_t *first = NULL; - while (t--) { - if (f->terms[t].oper != EcsOr) { - break; - } - first = &f->terms[t]; - } - - if (first) { - ecs_world_t *world = f->world; - const ecs_type_info_t *first_type; - if (first->idr) { - first_type = first->idr->type_info; - } else { - first_type = ecs_get_type_info(world, first->id); - } - const ecs_type_info_t *term_type; - if (term->idr) { - term_type = term->idr->type_info; - } else { - term_type = ecs_get_type_info(world, term->id); - } - - if (first_type == term_type) { - return NULL; - } - return first; - } else { - return NULL; - } -} - -int ecs_filter_finalize( - const ecs_world_t *world, - ecs_filter_t *f) -{ - int32_t i, term_count = f->term_count, field_count = 0; - ecs_term_t *terms = f->terms; - int32_t filter_terms = 0, scope_nesting = 0; - bool cond_set = false; - - ecs_filter_finalize_ctx_t ctx = {0}; - ctx.world = world; - ctx.filter = f; - - f->flags |= EcsFilterMatchOnlyThis; - - for (i = 0; i < term_count; i ++) { - bool filter_term = false; - ecs_term_t *term = &terms[i]; - ctx.term_index = i; - if (flecs_term_finalize(world, term, &ctx)) { - return -1; - } - - if (i && term[-1].oper == EcsOr) { - if (term[-1].src.id != term->src.id) { - flecs_filter_error(&ctx, "mismatching src.id for OR terms"); - return -1; - } - if (term->oper != EcsOr && term->oper != EcsAnd) { - flecs_filter_error(&ctx, - "term after OR operator must use AND operator"); - return -1; - } - } else { - field_count ++; - } - - term->field_index = field_count - 1; - - if (ecs_id_is_wildcard(term->id)) { - f->flags |= EcsFilterHasWildcards; - } - - if (ecs_term_match_this(term)) { - ECS_BIT_SET(f->flags, EcsFilterMatchThis); - } else { - ECS_BIT_CLEAR(f->flags, EcsFilterMatchOnlyThis); - } - - if (term->id == EcsPrefab) { - ECS_BIT_SET(f->flags, EcsFilterMatchPrefab); - } - if (term->id == EcsDisabled && (term->src.flags & EcsSelf)) { - ECS_BIT_SET(f->flags, EcsFilterMatchDisabled); - } - - if (ECS_BIT_IS_SET(f->flags, EcsFilterNoData)) { - term->inout = EcsInOutNone; - } - - if (term->oper == EcsNot && term->inout == EcsInOutDefault) { - term->inout = EcsInOutNone; - } - - if ((term->id == EcsWildcard) || (term->id == - ecs_pair(EcsWildcard, EcsWildcard))) - { - /* If term type is unknown beforehand, default the inout type to - * none. This prevents accidentally requesting lots of components, - * which can put stress on serializer code. */ - if (term->inout == EcsInOutDefault) { - term->inout = EcsInOutNone; - } - } - - if (term->inout == EcsInOutNone) { - filter_term = true; - } else if (term->idr) { - if (!term->idr->type_info && !(term->idr->flags & EcsIdUnion)) { - filter_term = true; - } - } else if (!ecs_id_is_union(world, term->id)) { - /* Union ids aren't filters because they return their target - * as component value with type ecs_entity_t */ - if (ecs_id_is_tag(world, term->id)) { - filter_term = true; - } else if (ECS_PAIR_SECOND(term->id) == EcsWildcard) { - /* If the second element of a pair is a wildcard and the first - * element is not a type, we can't know in advance what the - * type of the term is, so it can't provide data. */ - if (!ecs_get_type_info(world, ecs_pair_first(world, term->id))) { - filter_term = true; - } - } - } - - if (!filter_term) { - if (term->oper == EcsOr || (i && term[-1].oper == EcsOr)) { - ecs_term_t *first = flecs_filter_or_other_type(f, i); - if (first) { - if (first == &term[-1]) { - filter_terms ++; - } - filter_term = true; - } - } - } - - if (filter_term) { - filter_terms ++; - term->flags |= EcsTermNoData; - } else { - f->data_fields |= (1llu << term->field_index); - } - - if (term->oper != EcsNot || !ecs_term_match_this(term)) { - ECS_BIT_CLEAR(f->flags, EcsFilterMatchAnything); - } - - if (term->idr) { - if (ecs_os_has_threading()) { - ecs_os_ainc(&term->idr->keep_alive); - } else { - term->idr->keep_alive ++; - } - } - - if (term->oper == EcsOptional || term->oper == EcsNot) { - cond_set = true; - } - - if (term->first.id == EcsPredEq || term->first.id == EcsPredMatch || - term->first.id == EcsPredLookup) - { - f->flags |= EcsFilterHasPred; - } - - if (term->first.id == EcsScopeOpen) { - f->flags |= EcsFilterHasScopes; - scope_nesting ++; - } - if (term->first.id == EcsScopeClose) { - if (i && terms[i - 1].first.id == EcsScopeOpen) { - flecs_filter_error(&ctx, "invalid empty scope"); - return -1; - } - - f->flags |= EcsFilterHasScopes; - scope_nesting --; - } - if (scope_nesting < 0) { - flecs_filter_error(&ctx, "'}' without matching '{'"); - } - } - - if (scope_nesting != 0) { - flecs_filter_error(&ctx, "missing '}'"); - return -1; - } - - if (term_count && (terms[term_count - 1].oper == EcsOr)) { - flecs_filter_error(&ctx, "last term of filter can't have OR operator"); - return -1; - } - - f->field_count = flecs_ito(int8_t, field_count); - - if (field_count) { - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_id_record_t *idr = term->idr; - int32_t field = term->field_index; - - if (term->oper == EcsOr || (i && (term[-1].oper == EcsOr))) { - if (flecs_filter_or_other_type(f, i)) { - f->sizes[field] = 0; - continue; - } - } - - if (idr) { - if (!ECS_IS_PAIR(idr->id) || ECS_PAIR_FIRST(idr->id) != EcsWildcard) { - if (idr->flags & EcsIdUnion) { - f->sizes[field] = ECS_SIZEOF(ecs_entity_t); - } else if (idr->type_info) { - f->sizes[field] = idr->type_info->size; - } - } - } else { - bool is_union = false; - if (ECS_IS_PAIR(term->id)) { - ecs_entity_t first = ecs_pair_first(world, term->id); - if (ecs_has_id(world, first, EcsUnion)) { - is_union = true; - } - } - if (is_union) { - f->sizes[field] = ECS_SIZEOF(ecs_entity_t); - } else { - const ecs_type_info_t *ti = ecs_get_type_info( - world, term->id); - if (ti) { - f->sizes[field] = ti->size; - } - } - } - } - } else { - f->sizes = NULL; - } - - ecs_assert(filter_terms <= term_count, ECS_INTERNAL_ERROR, NULL); - if (filter_terms == term_count) { - ECS_BIT_SET(f->flags, EcsFilterNoData); - } - - ECS_BIT_COND(f->flags, EcsFilterHasCondSet, cond_set); - - /* Check if this is a trivial filter */ - if ((f->flags & EcsFilterMatchOnlyThis)) { - if (!(f->flags & - (EcsFilterHasPred|EcsFilterMatchDisabled|EcsFilterMatchPrefab))) - { - ECS_BIT_SET(f->flags, EcsFilterMatchOnlySelf); - - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_term_id_t *src = &term->src; - - if (src->flags & EcsUp) { - ECS_BIT_CLEAR(f->flags, EcsFilterMatchOnlySelf); - } - - if (!(term->flags & EcsTermIsTrivial)) { - break; - } - - if (!(f->flags & EcsFilterNoData)) { - if (term->inout == EcsInOutNone) { - break; - } - } - } - if (term_count && (i == term_count)) { - ECS_BIT_SET(f->flags, EcsFilterIsTrivial); - } - } - } - - return 0; -} - -/* Implementation for iterable mixin */ -static -void flecs_filter_iter_init( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter) -{ - ecs_poly_assert(poly, ecs_filter_t); - - if (filter) { - iter[1] = ecs_filter_iter(world, ECS_CONST_CAST(ecs_filter_t*, poly)); - iter[0] = ecs_term_chain_iter(&iter[1], filter); - } else { - iter[0] = ecs_filter_iter(world, ECS_CONST_CAST(ecs_filter_t*, poly)); - } -} - -/* Implementation for dtor mixin */ -static -void flecs_filter_fini( - ecs_filter_t *filter) -{ - if (filter->terms) { - int i, count = filter->term_count; - for (i = 0; i < count; i ++) { - ecs_term_t *term = &filter->terms[i]; - if (term->idr) { - if (!(filter->world->flags & EcsWorldQuit)) { - if (ecs_os_has_threading()) { - ecs_os_adec(&term->idr->keep_alive); - } else { - term->idr->keep_alive --; - } - } - } - ecs_term_fini(&filter->terms[i]); - } - - if (filter->flags & EcsFilterOwnsTermsStorage) { - /* Memory allocated for both terms & sizes */ - ecs_os_free(filter->terms); - } else { - ecs_os_free(filter->sizes); - } - } - - filter->terms = NULL; - - if (filter->flags & EcsFilterOwnsStorage) { - ecs_os_free(filter); - } -} - -void ecs_filter_fini( - ecs_filter_t *filter) -{ - if ((filter->flags & EcsFilterOwnsStorage) && filter->entity) { - /* If filter is associated with entity, use poly dtor path */ - ecs_delete(filter->world, filter->entity); - } else { - flecs_filter_fini(filter); - } -} - -ecs_filter_t* ecs_filter_init( - ecs_world_t *world, - const ecs_filter_desc_t *desc) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - flecs_stage_from_world(&world); - - ecs_filter_t *f = desc->storage; - int32_t i, term_count = desc->terms_buffer_count, storage_count = 0, expr_count = 0; - const ecs_term_t *terms = desc->terms_buffer; - ecs_term_t *storage_terms = NULL, *expr_terms = NULL; - - if (f) { - ecs_check(f->hdr.magic == ecs_filter_t_magic, - ECS_INVALID_PARAMETER, NULL); - storage_count = f->term_count; - storage_terms = f->terms; - ecs_poly_init(f, ecs_filter_t); - } else { - f = ecs_poly_new(ecs_filter_t); - f->flags |= EcsFilterOwnsStorage; - } - if (!storage_terms) { - f->flags |= EcsFilterOwnsTermsStorage; - } - - ECS_BIT_COND(f->flags, EcsFilterIsInstanced, desc->instanced); - ECS_BIT_SET(f->flags, EcsFilterMatchAnything); - f->flags |= desc->flags; - f->world = world; - - /* If terms_buffer was not set, count number of initialized terms in - * static desc::terms array */ - if (!terms) { - ecs_check(term_count == 0, ECS_INVALID_PARAMETER, NULL); - terms = desc->terms; - for (i = 0; i < FLECS_TERM_DESC_MAX; i ++) { - if (!ecs_term_is_initialized(&terms[i])) { - break; - } - term_count ++; - } - } else { - ecs_check(term_count != 0, ECS_INVALID_PARAMETER, NULL); - } - - /* If expr is set, parse query expression */ - const char *expr = desc->expr; - ecs_entity_t entity = desc->entity; - if (expr) { -#ifdef FLECS_PARSER - const char *name = NULL; - const char *ptr = desc->expr; - ecs_term_t term = {0}; - ecs_term_id_t extra_args[ECS_PARSER_MAX_ARGS]; - int32_t expr_size = 0; - - ecs_os_zeromem(extra_args); - - if (entity) { - name = ecs_get_name(world, entity); - } - - while (ptr[0] && - (ptr = ecs_parse_term(world, name, expr, ptr, &term, extra_args))) - { - if (!ecs_term_is_initialized(&term)) { - break; - } - - int32_t arg = 0; - - do { - ecs_assert(arg <= ECS_PARSER_MAX_ARGS, ECS_INTERNAL_ERROR, NULL); - - if (expr_count == expr_size) { - expr_size = expr_size ? expr_size * 2 : 8; - expr_terms = ecs_os_realloc_n(expr_terms, ecs_term_t, expr_size); - } - - ecs_term_t *expr_term = &expr_terms[expr_count ++]; - *expr_term = term; - - if (arg) { - expr_term->src = expr_term[-1].second; - expr_term->second = extra_args[arg - 1]; - - if (expr_term->first.name != NULL) { - expr_term->first.name = ecs_os_strdup( - expr_term->first.name); - } - if (expr_term->src.name != NULL) { - expr_term->src.name = ecs_os_strdup( - expr_term->src.name); - } - } - } while (ecs_term_id_is_set(&extra_args[arg ++])); - - if (ptr[0] == '\n') { - break; - } - - ecs_os_zeromem(extra_args); - } - - if (!ptr) { - /* Set terms in filter object to make sur they get cleaned up */ - f->terms = expr_terms; - f->term_count = flecs_ito(int8_t, expr_count); - f->flags |= EcsFilterOwnsTermsStorage; - goto error; - } -#else - (void)expr; - ecs_abort(ECS_UNSUPPORTED, "parser addon is not available"); -#endif - } - - /* If storage is provided, make sure it's large enough */ - ecs_check(!storage_terms || storage_count >= (term_count + expr_count), - ECS_INVALID_PARAMETER, NULL); - - if (term_count || expr_count) { - /* Allocate storage for terms and sizes array */ - if (!storage_terms) { - ecs_assert(f->flags & EcsFilterOwnsTermsStorage, - ECS_INTERNAL_ERROR, NULL); - f->term_count = flecs_ito(int8_t, term_count + expr_count); - ecs_size_t terms_size = ECS_SIZEOF(ecs_term_t) * f->term_count; - ecs_size_t sizes_size = ECS_SIZEOF(int32_t) * f->term_count; - f->terms = ecs_os_calloc(terms_size + sizes_size); - f->sizes = ECS_OFFSET(f->terms, terms_size); - } else { - f->terms = storage_terms; - f->term_count = flecs_ito(int8_t, storage_count); - f->sizes = ecs_os_calloc_n(ecs_size_t, term_count); - } - - /* Copy terms to filter storage */ - for (i = 0; i < term_count; i ++) { - f->terms[i] = ecs_term_copy(&terms[i]); - /* Allow freeing resources from expr parser during finalization */ - f->terms[i].move = true; - } - - /* Move expr terms to filter storage */ - for (i = 0; i < expr_count; i ++) { - f->terms[i + term_count] = ecs_term_move(&expr_terms[i]); - /* Allow freeing resources from expr parser during finalization */ - f->terms[i + term_count].move = true; - } - ecs_os_free(expr_terms); - } - - /* Ensure all fields are consistent and properly filled out */ - if (ecs_filter_finalize(world, f)) { - goto error; - } - - /* Any allocated resources remaining in terms are now owned by filter */ - for (i = 0; i < f->term_count; i ++) { - f->terms[i].move = false; - } - - f->variable_names[0] = NULL; - f->iterable.init = flecs_filter_iter_init; - f->dtor = (ecs_poly_dtor_t)flecs_filter_fini; - f->entity = entity; - - if (entity && (f->flags & EcsFilterOwnsStorage)) { - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_filter_t); - poly->poly = f; - ecs_poly_modified(world, entity, ecs_filter_t); - } - - return f; -error: - ecs_filter_fini(f); - return NULL; -} - -void ecs_filter_copy( - ecs_filter_t *dst, - const ecs_filter_t *src) -{ - if (src == dst) { - return; - } - - if (src) { - *dst = *src; - - int32_t i, term_count = src->term_count; - ecs_size_t terms_size = ECS_SIZEOF(ecs_term_t) * term_count; - ecs_size_t sizes_size = ECS_SIZEOF(int32_t) * term_count; - dst->terms = ecs_os_malloc(terms_size + sizes_size); - dst->sizes = ECS_OFFSET(dst->terms, terms_size); - dst->flags |= EcsFilterOwnsTermsStorage; - ecs_os_memcpy_n(dst->sizes, src->sizes, int32_t, term_count); - - for (i = 0; i < term_count; i ++) { - dst->terms[i] = ecs_term_copy(&src->terms[i]); - } - } else { - ecs_os_memset_t(dst, 0, ecs_filter_t); - } -} - -void ecs_filter_move( - ecs_filter_t *dst, - ecs_filter_t *src) -{ - if (src == dst) { - return; - } - - if (src) { - *dst = *src; - if (src->flags & EcsFilterOwnsTermsStorage) { - dst->terms = src->terms; - dst->sizes = src->sizes; - dst->flags |= EcsFilterOwnsTermsStorage; - } else { - ecs_filter_copy(dst, src); - } - src->terms = NULL; - src->sizes = NULL; - src->term_count = 0; - } else { - ecs_os_memset_t(dst, 0, ecs_filter_t); - } -} - -static -void flecs_filter_str_add_id( - const ecs_world_t *world, - ecs_strbuf_t *buf, - const ecs_term_id_t *id, - bool is_subject, - ecs_flags32_t default_traverse_flags) -{ - bool is_added = false; - if (!is_subject || id->id != EcsThis) { - if (id->flags & EcsIsVariable && !ecs_id_is_wildcard(id->id)) { - ecs_strbuf_appendlit(buf, "$"); - } - if (id->id) { - char *path = ecs_get_fullpath(world, id->id); - ecs_strbuf_appendstr(buf, path); - ecs_os_free(path); - } else if (id->name) { - ecs_strbuf_appendstr(buf, id->name); - } else { - ecs_strbuf_appendlit(buf, "0"); - } - is_added = true; - } - - ecs_flags32_t flags = id->flags; - if (!(flags & EcsTraverseFlags)) { - /* If flags haven't been set yet, initialize with defaults. This can - * happen if an error is thrown while the term is being finalized */ - flags |= default_traverse_flags; - } - - if ((flags & EcsTraverseFlags) != default_traverse_flags) { - if (is_added) { - ecs_strbuf_list_push(buf, ":", "|"); - } else { - ecs_strbuf_list_push(buf, "", "|"); - } - if (id->flags & EcsSelf) { - ecs_strbuf_list_appendstr(buf, "self"); - } - if (id->flags & EcsUp) { - ecs_strbuf_list_appendstr(buf, "up"); - } - if (id->flags & EcsDown) { - ecs_strbuf_list_appendstr(buf, "down"); - } - - if (id->trav && (id->trav != EcsIsA)) { - ecs_strbuf_list_push(buf, "(", ""); - - char *rel_path = ecs_get_fullpath(world, id->trav); - ecs_strbuf_appendstr(buf, rel_path); - ecs_os_free(rel_path); - - ecs_strbuf_list_pop(buf, ")"); - } - - ecs_strbuf_list_pop(buf, ""); - } -} - -static -void flecs_term_str_w_strbuf( - const ecs_world_t *world, - const ecs_term_t *term, - ecs_strbuf_t *buf, - int32_t t) -{ - const ecs_term_id_t *src = &term->src; - const ecs_term_id_t *second = &term->second; - - uint8_t def_src_mask = EcsSelf|EcsUp; - uint8_t def_first_mask = EcsSelf; - uint8_t def_second_mask = EcsSelf; - - bool pred_set = ecs_term_id_is_set(&term->first); - bool subj_set = !ecs_term_match_0(term); - bool obj_set = ecs_term_id_is_set(second); - - if (term->first.id == EcsScopeOpen) { - ecs_strbuf_appendlit(buf, "{"); - return; - } else if (term->first.id == EcsScopeClose) { - ecs_strbuf_appendlit(buf, "}"); - return; - } - - if (!t || !(term[-1].oper == EcsOr)) { - if (term->inout == EcsIn) { - ecs_strbuf_appendlit(buf, "[in] "); - } else if (term->inout == EcsInOut) { - ecs_strbuf_appendlit(buf, "[inout] "); - } else if (term->inout == EcsOut) { - ecs_strbuf_appendlit(buf, "[out] "); - } else if (term->inout == EcsInOutNone && term->oper != EcsNot) { - ecs_strbuf_appendlit(buf, "[none] "); - } - } - - if (term->first.flags & EcsIsEntity && term->first.id != 0) { - if (ecs_has_id(world, term->first.id, EcsDontInherit)) { - def_src_mask = EcsSelf; - } - } - - if (term->oper == EcsNot) { - ecs_strbuf_appendlit(buf, "!"); - } else if (term->oper == EcsOptional) { - ecs_strbuf_appendlit(buf, "?"); - } - - if (!subj_set) { - flecs_filter_str_add_id(world, buf, &term->first, false, - def_first_mask); - if (!obj_set) { - ecs_strbuf_appendlit(buf, "()"); - } else { - ecs_strbuf_appendlit(buf, "(0,"); - flecs_filter_str_add_id(world, buf, &term->second, false, - def_second_mask); - ecs_strbuf_appendlit(buf, ")"); - } - } else if (ecs_term_match_this(term) && - (src->flags & EcsTraverseFlags) == def_src_mask) - { - if (pred_set) { - if (obj_set) { - ecs_strbuf_appendlit(buf, "("); - } - flecs_filter_str_add_id(world, buf, &term->first, false, def_first_mask); - if (obj_set) { - ecs_strbuf_appendlit(buf, ","); - flecs_filter_str_add_id( - world, buf, &term->second, false, def_second_mask); - ecs_strbuf_appendlit(buf, ")"); - } - } else if (term->id) { - char *str = ecs_id_str(world, term->id); - ecs_strbuf_appendstr(buf, str); - ecs_os_free(str); - } - } else { - if (term->id_flags && !ECS_HAS_ID_FLAG(term->id_flags, PAIR)) { - ecs_strbuf_appendstr(buf, ecs_id_flag_str(term->id_flags)); - ecs_strbuf_appendch(buf, '|'); - } - - flecs_filter_str_add_id(world, buf, &term->first, false, def_first_mask); - ecs_strbuf_appendlit(buf, "("); - if (term->src.flags & EcsIsEntity && term->src.id == term->first.id) { - ecs_strbuf_appendlit(buf, "$"); - } else { - flecs_filter_str_add_id(world, buf, &term->src, true, def_src_mask); - } - if (obj_set) { - ecs_strbuf_appendlit(buf, ","); - flecs_filter_str_add_id(world, buf, &term->second, false, def_second_mask); - } - ecs_strbuf_appendlit(buf, ")"); - } -} - -char* ecs_term_str( - const ecs_world_t *world, - const ecs_term_t *term) -{ - ecs_strbuf_t buf = ECS_STRBUF_INIT; - flecs_term_str_w_strbuf(world, term, &buf, 0); - return ecs_strbuf_get(&buf); -} - -static -char* flecs_filter_str( - const ecs_world_t *world, - const ecs_filter_t *filter, - const ecs_filter_finalize_ctx_t *ctx, - int32_t *term_start_out) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(filter != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_strbuf_t buf = ECS_STRBUF_INIT; - ecs_term_t *terms = filter->terms; - int32_t i, count = filter->term_count; - - for (i = 0; i < count; i ++) { - ecs_term_t *term = &terms[i]; - - if (term_start_out && ctx) { - if (ctx->term_index == i) { - term_start_out[0] = ecs_strbuf_written(&buf); - if (i) { - term_start_out[0] += 2; /* whitespace + , */ - } - } - } - - flecs_term_str_w_strbuf(world, term, &buf, i); - - if (i != (count - 1)) { - if (term->oper == EcsOr) { - ecs_strbuf_appendlit(&buf, " || "); - } else { - if (term->first.id != EcsScopeOpen) { - if (term[1].first.id != EcsScopeClose) { - ecs_strbuf_appendlit(&buf, ", "); - } - } - } - } - } - - return ecs_strbuf_get(&buf); -error: - return NULL; -} - -char* ecs_filter_str( - const ecs_world_t *world, - const ecs_filter_t *filter) -{ - return flecs_filter_str(world, filter, NULL, NULL); -} - -int32_t ecs_filter_find_this_var( - const ecs_filter_t *filter) -{ - ecs_check(filter != NULL, ECS_INVALID_PARAMETER, NULL); - - if (ECS_BIT_IS_SET(filter->flags, EcsFilterMatchThis)) { - /* Filters currently only support the This variable at index 0. Only - * return 0 if filter actually has terms for the This variable. */ - return 0; - } - -error: - return -1; -} - -/* Check if the id is a pair that has Any as first or second element. Any - * pairs behave just like Wildcard pairs and reuses the same data structures, - * with as only difference that the number of results returned for an Any pair - * is never more than one. This function is used to tell the difference. */ -static -bool is_any_pair( - ecs_id_t id) -{ - if (!ECS_HAS_ID_FLAG(id, PAIR)) { - return false; - } - - if (ECS_PAIR_FIRST(id) == EcsAny) { - return true; - } - if (ECS_PAIR_SECOND(id) == EcsAny) { - return true; - } - - return false; -} - -static -bool flecs_n_term_match_table( - ecs_world_t *world, - const ecs_term_t *term, - const ecs_table_t *table, - ecs_entity_t type_id, - ecs_oper_kind_t oper, - ecs_id_t *id_out, - int32_t *column_out, - ecs_entity_t *subject_out, - int32_t *match_index_out, - bool first, - ecs_flags32_t iter_flags) -{ - (void)column_out; - - const ecs_type_t *type = ecs_get_type(world, type_id); - ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_id_t *ids = type->array; - int32_t i, count = type->count; - ecs_term_t temp = *term; - temp.oper = EcsAnd; - - for (i = 0; i < count; i ++) { - ecs_id_t id = ids[i]; - if (ecs_id_get_flags(world, id) & EcsIdDontInherit) { - continue; - } - bool result; - if (ECS_HAS_ID_FLAG(id, AND)) { - ecs_oper_kind_t id_oper = EcsAndFrom; - result = flecs_n_term_match_table(world, term, table, - id & ECS_COMPONENT_MASK, id_oper, id_out, column_out, - subject_out, match_index_out, first, iter_flags); - } else { - temp.id = id; - result = flecs_term_match_table(world, &temp, table, id_out, - 0, subject_out, match_index_out, first, iter_flags); - } - if (!result && oper == EcsAndFrom) { - return false; - } else - if (result && oper == EcsOrFrom) { - return true; - } - } - - if (oper == EcsAndFrom) { - if (id_out) { - id_out[0] = type_id; - } - return true; - } else - if (oper == EcsOrFrom) { - return false; - } - - return false; -} - -bool flecs_term_match_table( - ecs_world_t *world, - const ecs_term_t *term, - const ecs_table_t *table, - ecs_id_t *id_out, - int32_t *column_out, - ecs_entity_t *subject_out, - int32_t *match_index_out, - bool first, - ecs_flags32_t iter_flags) -{ - const ecs_term_id_t *src = &term->src; - ecs_oper_kind_t oper = term->oper; - const ecs_table_t *match_table = table; - ecs_id_t id = term->id; - - ecs_entity_t src_id = src->id; - if (ecs_term_match_0(term)) { - if (id_out) { - id_out[0] = id; /* If no entity is matched, just set id */ - } - return true; - } - - if (oper == EcsAndFrom || oper == EcsOrFrom) { - return flecs_n_term_match_table(world, term, table, term->id, - term->oper, id_out, column_out, subject_out, match_index_out, first, - iter_flags); - } - - /* If source is not This, search in table of source */ - if (!ecs_term_match_this(term)) { - if (iter_flags & EcsIterEntityOptional) { - /* Treat entity terms as optional */ - oper = EcsOptional; - } - - if (ecs_is_alive(world, src_id)) { - match_table = ecs_get_table(world, src_id); - } else { - match_table = NULL; - } - - if (match_table) { - } else if (oper != EcsOptional) { - return false; - } - } else { - /* If filter contains This terms, a table must be provided */ - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - } - - if (!match_table) { - return false; - } - - ecs_entity_t source = 0; - - /* If first = false, we're searching from an offset. This supports returning - * multiple results when using wildcard filters. */ - int32_t column = 0; - if (!first && column_out && column_out[0] != 0) { - column = column_out[0]; - if (column < 0) { - /* In case column is not from This, flip sign */ - column = -column; - } - - /* Remove base 1 offset */ - column --; - } - - /* Find location, source and id of match in table type */ - ecs_table_record_t *tr = 0; - bool is_any = is_any_pair(id); - - column = flecs_search_relation_w_idr(world, match_table, - column, id, src->trav, src->flags, &source, id_out, &tr, term->idr); - - if (tr && match_index_out) { - if (!is_any) { - match_index_out[0] = tr->count; - } else { - match_index_out[0] = 1; - } - } - - bool result = column != -1; - - if (oper == EcsNot) { - if (match_index_out) { - match_index_out[0] = 1; - } - result = !result; - } - - if (oper == EcsOptional) { - result = true; - } - - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - if ((column == -1) && (src->flags & EcsUp) && (table->flags & EcsTableHasTarget)) { - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_id_t rel = ECS_PAIR_SECOND(table->type.array[table->_->ft_offset]); - if (rel == (uint32_t)src->trav) { - result = true; - } - } - - if (!result) { - if (iter_flags & EcsFilterPopulate) { - column = 0; - } else { - return false; - } - } - - if (!ecs_term_match_this(term)) { - if (!source) { - source = src_id; - } - } - - if (id_out && column < 0) { - id_out[0] = id; - } - - if (column_out) { - if (column >= 0) { - column ++; - if (source != 0) { - column *= -1; - } - column_out[0] = column; - } else { - column_out[0] = 0; - } - } - - if (subject_out) { - subject_out[0] = source; - } - - return result; -} - -bool flecs_filter_match_table( - ecs_world_t *world, - const ecs_filter_t *filter, - const ecs_table_t *table, - ecs_id_t *ids, - int32_t *columns, - ecs_entity_t *sources, - int32_t *match_indices, - int32_t *matches_left, - bool first, - int32_t skip_term, - ecs_flags32_t iter_flags) -{ - ecs_term_t *terms = filter->terms; - int32_t i, count = filter->term_count; - int32_t match_count = 1; - bool result = true; - - if (matches_left) { - match_count = *matches_left; - } - - for (i = 0; i < count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_oper_kind_t oper = term->oper; - if (i == skip_term) { - if (oper != EcsAndFrom && oper != EcsOrFrom && oper != EcsNotFrom) { - continue; - } - } - - ecs_term_id_t *src = &term->src; - const ecs_table_t *match_table = table; - int32_t t_i = term->field_index; - - ecs_entity_t src_id = src->id; - if (!src_id) { - if (ids) { - ids[t_i] = term->id; - } - continue; - } - - if (!ecs_term_match_this(term)) { - if (ecs_is_alive(world, src_id)) { - match_table = ecs_get_table(world, src_id); - } else { - match_table = NULL; - } - } else { - if (ECS_BIT_IS_SET(iter_flags, EcsIterIgnoreThis)) { - continue; - } - - /* If filter contains This terms, table must be provided */ - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - } - - int32_t match_index = 0; - if (!i || term[-1].oper != EcsOr) { - result = false; - } else { - if (result) { - continue; /* Already found matching OR term */ - } - } - - bool term_result = flecs_term_match_table(world, term, match_table, - ids ? &ids[t_i] : NULL, - columns ? &columns[t_i] : NULL, - sources ? &sources[t_i] : NULL, - &match_index, - first, - iter_flags); - - if (i && term[-1].oper == EcsOr) { - result |= term_result; - } else { - result = term_result; - } - - if (oper != EcsOr && !result) { - return false; - } - - if (first && match_index) { - match_count *= match_index; - } - if (match_indices) { - match_indices[t_i] = match_index; - } - } - - if (matches_left) { - *matches_left = match_count; - } - - return true; -} - -static -void term_iter_init_no_data( - ecs_term_iter_t *iter) -{ - iter->term = (ecs_term_t){ .field_index = -1 }; - iter->self_index = NULL; - iter->index = 0; -} - -static -void term_iter_init_w_idr( - const ecs_term_t *term, - ecs_term_iter_t *iter, - ecs_id_record_t *idr, - bool empty_tables) -{ - if (idr) { - if (empty_tables) { - flecs_table_cache_all_iter(&idr->cache, &iter->it); - } else { - flecs_table_cache_iter(&idr->cache, &iter->it); - } - } else { - term_iter_init_no_data(iter); - } - - iter->index = 0; - iter->empty_tables = empty_tables; - iter->size = 0; - if (term && term->idr && term->idr->type_info) { - iter->size = term->idr->type_info->size; - } -} - -static -void term_iter_init_wildcard( - const ecs_world_t *world, - ecs_term_iter_t *iter, - bool empty_tables) -{ - iter->term = (ecs_term_t){ .field_index = -1 }; - iter->self_index = flecs_id_record_get(world, EcsAny); - ecs_id_record_t *idr = iter->cur = iter->self_index; - term_iter_init_w_idr(NULL, iter, idr, empty_tables); -} - -static -void term_iter_init( - const ecs_world_t *world, - ecs_term_t *term, - ecs_term_iter_t *iter, - bool empty_tables) -{ - const ecs_term_id_t *src = &term->src; - - iter->term = *term; - - if (src->flags & EcsSelf) { - iter->self_index = term->idr; - if (!iter->self_index) { - iter->self_index = flecs_query_id_record_get(world, term->id); - } - } - - if (src->flags & EcsUp) { - iter->set_index = flecs_id_record_get(world, - ecs_pair(src->trav, EcsWildcard)); - } - - ecs_id_record_t *idr; - if (iter->self_index) { - idr = iter->cur = iter->self_index; - } else { - idr = iter->cur = iter->set_index; - } - - term_iter_init_w_idr(term, iter, idr, empty_tables); -} - -ecs_iter_t ecs_term_iter( - const ecs_world_t *stage, - ecs_term_t *term) -{ - ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(term != NULL, ECS_INVALID_PARAMETER, NULL); - - const ecs_world_t *world = ecs_get_world(stage); - - flecs_process_pending_tables(world); - - if (ecs_term_finalize(world, term)) { - ecs_throw(ECS_INVALID_PARAMETER, NULL); - } - - ecs_iter_t it = { - .real_world = ECS_CONST_CAST(ecs_world_t*, world), - .world = ECS_CONST_CAST(ecs_world_t*, stage), - .field_count = 1, - .next = ecs_term_next - }; - - /* Term iter populates the iterator with arrays from its own cache, ensure - * they don't get overwritten by flecs_iter_validate. - * - * Note: the reason the term iterator doesn't use the iterator cache itself - * (which could easily accommodate a single term) is that the filter iterator - * is built on top of the term iterator. The private cache of the term - * iterator keeps the filter iterator code simple, as it doesn't need to - * worry about the term iter overwriting the iterator fields. */ - flecs_iter_init(stage, &it, 0); - term_iter_init(world, term, &it.priv.iter.term, false); - ECS_BIT_COND(it.flags, EcsIterNoData, it.priv.iter.term.size == 0); - - return it; -error: - return (ecs_iter_t){ 0 }; -} - -ecs_iter_t ecs_term_chain_iter( - const ecs_iter_t *chain_it, - ecs_term_t *term) -{ - ecs_check(chain_it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(term != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_world_t *world = chain_it->real_world; - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - - if (ecs_term_finalize(world, term)) { - ecs_throw(ECS_INVALID_PARAMETER, NULL); - } - - ecs_iter_t it = { - .real_world = world, - .world = chain_it->world, - .terms = term, - .field_count = 1, - .chain_it = ECS_CONST_CAST(ecs_iter_t*, chain_it), - .next = ecs_term_next - }; - - flecs_iter_init(chain_it->world, &it, flecs_iter_cache_all); - - term_iter_init(world, term, &it.priv.iter.term, false); - - return it; -error: - return (ecs_iter_t){ 0 }; -} - -ecs_iter_t ecs_children( - const ecs_world_t *world, - ecs_entity_t parent) -{ - return ecs_term_iter(world, &(ecs_term_t){ .id = ecs_childof(parent) }); -} - -bool ecs_children_next( - ecs_iter_t *it) -{ - return ecs_term_next(it); -} - -static -const ecs_table_record_t *flecs_term_iter_next_table( - ecs_term_iter_t *iter) -{ - ecs_id_record_t *idr = iter->cur; - if (!idr) { - return NULL; - } - - return flecs_table_cache_next(&iter->it, ecs_table_record_t); -} - -static -bool flecs_term_iter_find_superset( - ecs_world_t *world, - ecs_table_t *table, - ecs_term_t *term, - ecs_entity_t *source, - ecs_id_t *id, - int32_t *column) -{ - ecs_term_id_t *src = &term->src; - - /* Test if following the relationship finds the id */ - int32_t index = flecs_search_relation_w_idr(world, table, 0, - term->id, src->trav, src->flags, source, id, 0, term->idr); - - if (index == -1) { - *source = 0; - return false; - } - - ecs_assert(*source != 0, ECS_INTERNAL_ERROR, NULL); - - *column = (index + 1) * -1; - - return true; -} - -static -bool flecs_term_iter_next( - ecs_world_t *world, - ecs_term_iter_t *iter, - bool match_prefab, - bool match_disabled) -{ - ecs_table_t *table = iter->table; - ecs_entity_t source = 0; - const ecs_table_record_t *tr; - ecs_term_t *term = &iter->term; - - do { - if (table) { - iter->cur_match ++; - if (iter->cur_match >= iter->match_count) { - table = NULL; - } else { - iter->last_column = ecs_search_offset( - world, table, iter->last_column + 1, term->id, 0); - iter->column = iter->last_column + 1; - if (iter->last_column >= 0) { - iter->id = table->type.array[iter->last_column]; - } - } - } - - if (!table) { - if (!(tr = flecs_term_iter_next_table(iter))) { - if (iter->cur != iter->set_index && iter->set_index != NULL) { - if (iter->observed_table_count != 0) { - iter->cur = iter->set_index; - if (iter->empty_tables) { - flecs_table_cache_all_iter( - &iter->set_index->cache, &iter->it); - } else { - flecs_table_cache_iter( - &iter->set_index->cache, &iter->it); - } - iter->index = 0; - tr = flecs_term_iter_next_table(iter); - } - } - - if (!tr) { - return false; - } - } - - table = tr->hdr.table; - if (table->_->traversable_count) { - iter->observed_table_count ++; - } - - if (!match_prefab && (table->flags & EcsTableIsPrefab)) { - continue; - } - - if (!match_disabled && (table->flags & EcsTableIsDisabled)) { - continue; - } - - iter->table = table; - iter->match_count = tr->count; - if (is_any_pair(term->id)) { - iter->match_count = 1; - } - - iter->cur_match = 0; - iter->last_column = tr->index; - iter->column = tr->index + 1; - iter->id = flecs_to_public_id(table->type.array[tr->index]); - } - - if (iter->cur == iter->set_index) { - if (iter->self_index) { - if (flecs_id_record_get_table(iter->self_index, table) != NULL) { - /* If the table has the id itself and this term matched Self - * we already matched it */ - continue; - } - } - - if (!flecs_term_iter_find_superset( - world, table, term, &source, &iter->id, &iter->column)) - { - continue; - } - - /* The tr->count field refers to the number of relationship instances, - * not to the number of matches. Superset terms can only yield a - * single match. */ - iter->match_count = 1; - } - - break; - } while (true); - - iter->subject = source; - - return true; -} - -static -bool flecs_term_iter_set_table( - ecs_world_t *world, - ecs_term_iter_t *iter, - ecs_table_t *table) -{ - const ecs_table_record_t *tr = NULL; - const ecs_id_record_t *idr = iter->self_index; - if (idr) { - tr = ecs_table_cache_get(&idr->cache, table); - if (tr) { - iter->match_count = tr->count; - iter->last_column = tr->index; - iter->column = tr->index + 1; - iter->id = flecs_to_public_id(table->type.array[tr->index]); - } - } - - if (!tr) { - idr = iter->set_index; - if (idr) { - tr = ecs_table_cache_get(&idr->cache, table); - if (!flecs_term_iter_find_superset(world, table, &iter->term, - &iter->subject, &iter->id, &iter->column)) - { - return false; - } - iter->match_count = 1; - } - } - - if (!tr) { - return false; - } - - /* Populate fields as usual */ - iter->table = table; - iter->cur_match = 0; - - return true; -} - -bool ecs_term_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_term_next, ECS_INVALID_PARAMETER, NULL); - - flecs_iter_validate(it); - - ecs_term_iter_t *iter = &it->priv.iter.term; - ecs_term_t *term = &iter->term; - ecs_world_t *world = it->real_world; - ecs_table_t *table; - - it->ids = &iter->id; - it->sources = &iter->subject; - it->columns = &iter->column; - it->terms = &iter->term; - it->sizes = &iter->size; - it->ptrs = &iter->ptr; - - ecs_iter_t *chain_it = it->chain_it; - if (chain_it) { - ecs_iter_next_action_t next = chain_it->next; - bool match; - - do { - if (!next(chain_it)) { - goto done; - } - - table = chain_it->table; - match = flecs_term_match_table(world, term, table, - it->ids, it->columns, it->sources, it->match_indices, true, - it->flags); - } while (!match); - goto yield; - - } else { - if (!flecs_term_iter_next(world, iter, - (term->flags & EcsTermMatchPrefab) != 0, - (term->flags & EcsTermMatchDisabled) != 0)) - { - goto done; - } - - table = iter->table; - - /* Source must either be 0 (EcsThis) or nonzero in case of substitution */ - ecs_assert(iter->subject || iter->cur != iter->set_index, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(iter->table != NULL, ECS_INTERNAL_ERROR, NULL); - } - -yield: - flecs_iter_populate_data(world, it, table, 0, ecs_table_count(table), - it->ptrs); - ECS_BIT_SET(it->flags, EcsIterIsValid); - return true; -done: - ecs_iter_fini(it); -error: - return false; -} - -static -void flecs_init_filter_iter( - ecs_iter_t *it, - const ecs_filter_t *filter) -{ - ecs_assert(filter != NULL, ECS_INTERNAL_ERROR, NULL); - it->priv.iter.filter.filter = filter; - it->field_count = filter->field_count; -} - -int32_t ecs_filter_pivot_term( - const ecs_world_t *world, - const ecs_filter_t *filter) -{ - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(filter != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_term_t *terms = filter->terms; - int32_t i, term_count = filter->term_count; - int32_t pivot_term = -1, min_count = -1, self_pivot_term = -1; - - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_id_t id = term->id; - - if ((term->oper != EcsAnd) || (i && (term[-1].oper == EcsOr))) { - continue; - } - - if (!ecs_term_match_this(term)) { - continue; - } - - ecs_id_record_t *idr = flecs_query_id_record_get(world, id); - if (!idr) { - /* If one of the terms does not match with any data, iterator - * should not return anything */ - return -2; /* -2 indicates filter doesn't match anything */ - } - - int32_t table_count = flecs_table_cache_count(&idr->cache); - if (min_count == -1 || table_count < min_count) { - min_count = table_count; - pivot_term = i; - if ((term->src.flags & EcsTraverseFlags) == EcsSelf) { - self_pivot_term = i; - } - } - } - - if (self_pivot_term != -1) { - pivot_term = self_pivot_term; - } - - return pivot_term; -error: - return -2; -} - -void flecs_filter_apply_iter_flags( - ecs_iter_t *it, - const ecs_filter_t *filter) -{ - ECS_BIT_COND(it->flags, EcsIterIsInstanced, - ECS_BIT_IS_SET(filter->flags, EcsFilterIsInstanced)); - ECS_BIT_COND(it->flags, EcsIterNoData, - ECS_BIT_IS_SET(filter->flags, EcsFilterNoData)); - ECS_BIT_COND(it->flags, EcsIterHasCondSet, - ECS_BIT_IS_SET(filter->flags, EcsFilterHasCondSet)); -} - -ecs_iter_t flecs_filter_iter_w_flags( - const ecs_world_t *stage, - const ecs_filter_t *filter, - ecs_flags32_t flags) -{ - ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(filter != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(filter->flags & (EcsFilterHasPred|EcsFilterHasScopes)), - ECS_UNSUPPORTED, NULL); - const ecs_world_t *world = ecs_get_world(stage); - - if (!(flags & EcsIterMatchVar)) { - flecs_process_pending_tables(world); - } - - ecs_iter_t it = { - .real_world = ECS_CONST_CAST(ecs_world_t*, world), - .world = ECS_CONST_CAST(ecs_world_t*, stage), - .terms = filter ? filter->terms : NULL, - .next = ecs_filter_next, - .flags = flags, - .sizes = filter->sizes - }; - - ecs_filter_iter_t *iter = &it.priv.iter.filter; - iter->pivot_term = -1; - - flecs_init_filter_iter(&it, filter); - flecs_filter_apply_iter_flags(&it, filter); - - /* Find term that represents smallest superset */ - if (ECS_BIT_IS_SET(flags, EcsIterIgnoreThis)) { - term_iter_init_no_data(&iter->term_iter); - } else if (ECS_BIT_IS_SET(filter->flags, EcsFilterMatchThis)) { - ecs_term_t *terms = filter->terms; - int32_t pivot_term = -1; - ecs_check(terms != NULL, ECS_INVALID_PARAMETER, NULL); - - pivot_term = ecs_filter_pivot_term(world, filter); - iter->kind = EcsIterEvalTables; - iter->pivot_term = pivot_term; - - if (pivot_term == -2) { - /* One or more terms have no matching results */ - term_iter_init_no_data(&iter->term_iter); - } else if (pivot_term == -1) { - /* No terms meet the criteria to be a pivot term, evaluate filter - * against all tables */ - term_iter_init_wildcard(world, &iter->term_iter, - ECS_BIT_IS_SET(filter->flags, EcsFilterMatchEmptyTables)); - } else { - ecs_assert(pivot_term >= 0, ECS_INTERNAL_ERROR, NULL); - term_iter_init(world, &terms[pivot_term], &iter->term_iter, - ECS_BIT_IS_SET(filter->flags, EcsFilterMatchEmptyTables)); - } - } else { - if (!ECS_BIT_IS_SET(filter->flags, EcsFilterMatchAnything)) { - term_iter_init_no_data(&iter->term_iter); - } else { - iter->kind = EcsIterEvalNone; - } - } - - ECS_BIT_COND(it.flags, EcsIterNoData, - ECS_BIT_IS_SET(filter->flags, EcsFilterNoData)); - - if (ECS_BIT_IS_SET(filter->flags, EcsFilterMatchThis)) { - /* Make space for one variable if the filter has terms for This var */ - it.variable_count = 1; - - /* Set variable name array */ - it.variable_names = ECS_CONST_CAST(char**, filter->variable_names); - } - - flecs_iter_init(stage, &it, flecs_iter_cache_all); - - return it; -error: - return (ecs_iter_t){ 0 }; -} - -ecs_iter_t ecs_filter_iter( - const ecs_world_t *stage, - const ecs_filter_t *filter) -{ - return flecs_filter_iter_w_flags(stage, filter, 0); -} - -ecs_iter_t ecs_filter_chain_iter( - const ecs_iter_t *chain_it, - const ecs_filter_t *filter) -{ - ecs_iter_t it = { - .terms = filter->terms, - .field_count = filter->field_count, - .world = chain_it->world, - .real_world = chain_it->real_world, - .chain_it = ECS_CONST_CAST(ecs_iter_t*, chain_it), - .next = ecs_filter_next, - .sizes = filter->sizes - }; - - flecs_iter_init(chain_it->world, &it, flecs_iter_cache_all); - ecs_filter_iter_t *iter = &it.priv.iter.filter; - flecs_init_filter_iter(&it, filter); - - iter->kind = EcsIterEvalChain; - - return it; -} - -bool ecs_filter_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_filter_next, ECS_INVALID_PARAMETER, NULL); - - if (flecs_iter_next_row(it)) { - return true; - } - - return flecs_iter_next_instanced(it, ecs_filter_next_instanced(it)); -error: - return false; -} - -bool ecs_filter_next_instanced( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_filter_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->chain_it != it, ECS_INVALID_PARAMETER, NULL); - - ecs_filter_iter_t *iter = &it->priv.iter.filter; - const ecs_filter_t *filter = iter->filter; - ecs_world_t *world = it->real_world; - ecs_table_t *table = NULL; - bool match; - - flecs_iter_validate(it); - - ecs_iter_t *chain_it = it->chain_it; - ecs_iter_kind_t kind = iter->kind; - - if (chain_it) { - ecs_assert(kind == EcsIterEvalChain, ECS_INVALID_PARAMETER, NULL); - - ecs_iter_next_action_t next = chain_it->next; - do { - if (!next(chain_it)) { - goto done; - } - - table = chain_it->table; - match = flecs_filter_match_table(world, filter, table, - it->ids, it->columns, it->sources, it->match_indices, NULL, - true, -1, it->flags); - } while (!match); - - goto yield; - } else if (kind == EcsIterEvalTables || kind == EcsIterEvalCondition) { - ecs_term_iter_t *term_iter = &iter->term_iter; - ecs_term_t *term = &term_iter->term; - int32_t pivot_term = iter->pivot_term; - bool first; - - /* Check if the This variable has been set on the iterator. If set, - * the filter should only be applied to the variable value */ - ecs_var_t *this_var = NULL; - ecs_table_t *this_table = NULL; - if (it->variable_count) { - if (ecs_iter_var_is_constrained(it, 0)) { - this_var = it->variables; - this_table = this_var->range.table; - - /* If variable is constrained, make sure it's a value that's - * pointing to a table, as a filter can't iterate single - * entities (yet) */ - ecs_assert(this_table != NULL, ECS_INVALID_OPERATION, NULL); - - /* Can't set variable for filter that does not iterate tables */ - ecs_assert(kind == EcsIterEvalTables, - ECS_INVALID_OPERATION, NULL); - } - } - - do { - /* If there are no matches left for the previous table, this is the - * first match of the next table. */ - first = iter->matches_left == 0; - - if (first) { - if (kind != EcsIterEvalCondition) { - /* Check if this variable was constrained */ - if (this_table != NULL) { - /* If this is the first match of a new result and the - * previous result was equal to the value of a - * constrained var, there's nothing left to iterate */ - if (it->table == this_table) { - goto done; - } - - /* If table doesn't match term iterator, it doesn't - * match filter. */ - if (!flecs_term_iter_set_table( - world, term_iter, this_table)) - { - goto done; - } - - it->offset = this_var->range.offset; - it->count = this_var->range.count; - - /* But if it does, forward it to filter matching */ - ecs_assert(term_iter->table == this_table, - ECS_INTERNAL_ERROR, NULL); - - /* If This variable is not constrained, iterate as usual */ - } else { - it->offset = 0; - it->count = 0; - - /* Find new match, starting with the leading term */ - if (!flecs_term_iter_next(world, term_iter, - ECS_BIT_IS_SET(filter->flags, - EcsFilterMatchPrefab), - ECS_BIT_IS_SET(filter->flags, - EcsFilterMatchDisabled))) - { - goto done; - } - } - - ecs_assert(term_iter->match_count != 0, - ECS_INTERNAL_ERROR, NULL); - - if (pivot_term == -1) { - /* Without a pivot term, we're iterating all tables with - * a wildcard, so the match count is meaningless. */ - term_iter->match_count = 1; - } else { - it->match_indices[pivot_term] = term_iter->match_count; - } - - iter->matches_left = term_iter->match_count; - - /* Filter iterator takes control over iterating all the - * permutations that match the wildcard. */ - term_iter->match_count = 1; - - table = term_iter->table; - - if (pivot_term != -1) { - int32_t index = term->field_index; - it->ids[index] = term_iter->id; - it->sources[index] = term_iter->subject; - it->columns[index] = term_iter->column; - } - } else { - /* Progress iterator to next match for table, if any */ - table = it->table; - if (term_iter->index == 0) { - iter->matches_left = 1; - term_iter->index = 1; /* prevents looping again */ - } else { - goto done; - } - } - - /* Match the remainder of the terms */ - match = flecs_filter_match_table(world, filter, table, - it->ids, it->columns, it->sources, - it->match_indices, &iter->matches_left, first, - pivot_term, it->flags); - if (!match) { - it->table = table; - iter->matches_left = 0; - continue; - } - - /* Table got matched, set This variable */ - if (table) { - ecs_assert(it->variable_count == 1, ECS_INTERNAL_ERROR, NULL); - ecs_assert(it->variables != NULL, ECS_INTERNAL_ERROR, NULL); - it->variables[0].range.table = table; - } - - ecs_assert(iter->matches_left != 0, ECS_INTERNAL_ERROR, NULL); - } - - /* If this is not the first result for the table, and the table - * is matched more than once, iterate remaining matches */ - if (!first && (iter->matches_left > 0)) { - table = it->table; - - /* Find first term that still has matches left */ - int32_t i, j, count = it->field_count; - for (i = count - 1; i >= 0; i --) { - int32_t mi = -- it->match_indices[i]; - if (mi) { - if (mi < 0) { - continue; - } - break; - } - } - - /* If matches_left > 0 we should've found at least one match */ - ecs_assert(i >= 0, ECS_INTERNAL_ERROR, NULL); - - /* Progress first term to next match (must be at least one) */ - int32_t column = it->columns[i]; - if (column < 0) { - /* If this term was matched on a non-This entity, reconvert - * the column back to a positive value */ - column = -column; - } - - it->columns[i] = column + 1; - flecs_term_match_table(world, &filter->terms[i], table, - &it->ids[i], &it->columns[i], &it->sources[i], - &it->match_indices[i], false, it->flags); - - /* Reset remaining terms (if any) to first match */ - for (j = i + 1; j < count; j ++) { - flecs_term_match_table(world, &filter->terms[j], table, - &it->ids[j], &it->columns[j], &it->sources[j], - &it->match_indices[j], true, it->flags); - } - } - - match = iter->matches_left != 0; - iter->matches_left --; - - ecs_assert(iter->matches_left >= 0, ECS_INTERNAL_ERROR, NULL); - } while (!match); - - goto yield; - } - -done: -error: - ecs_iter_fini(it); - return false; - -yield: - if (!it->count && table) { - it->count = ecs_table_count(table); - } - flecs_iter_populate_data(world, it, table, it->offset, it->count, it->ptrs); - ECS_BIT_SET(it->flags, EcsIterIsValid); - return true; -} diff --git a/vendors/flecs/src/id.c b/vendors/flecs/src/id.c new file mode 100644 index 000000000..44d8f4631 --- /dev/null +++ b/vendors/flecs/src/id.c @@ -0,0 +1,159 @@ +/** + * @file id.c + * @brief Id utilities. + */ + +#include "private_api.h" + +#ifdef FLECS_SCRIPT +#include "addons/script/script.h" +#endif + +bool ecs_id_match( + ecs_id_t id, + ecs_id_t pattern) +{ + if (id == pattern) { + return true; + } + + if (ECS_HAS_ID_FLAG(pattern, PAIR)) { + if (!ECS_HAS_ID_FLAG(id, PAIR)) { + return false; + } + + ecs_entity_t id_first = ECS_PAIR_FIRST(id); + ecs_entity_t id_second = ECS_PAIR_SECOND(id); + ecs_entity_t pattern_first = ECS_PAIR_FIRST(pattern); + ecs_entity_t pattern_second = ECS_PAIR_SECOND(pattern); + + ecs_check(id_first != 0, ECS_INVALID_PARAMETER, + "first element of pair cannot be 0"); + ecs_check(id_second != 0, ECS_INVALID_PARAMETER, + "second element of pair cannot be 0"); + + ecs_check(pattern_first != 0, ECS_INVALID_PARAMETER, + "first element of pair cannot be 0"); + ecs_check(pattern_second != 0, ECS_INVALID_PARAMETER, + "second element of pair cannot be 0"); + + if (pattern_first == EcsWildcard) { + if (pattern_second == EcsWildcard || pattern_second == id_second) { + return true; + } + } else if (pattern_first == EcsFlag) { + /* Used for internals, helps to keep track of which ids are used in + * pairs that have additional flags (like OVERRIDE and TOGGLE) */ + if (ECS_HAS_ID_FLAG(id, PAIR) && !ECS_IS_PAIR(id)) { + if (ECS_PAIR_FIRST(id) == pattern_second) { + return true; + } + if (ECS_PAIR_SECOND(id) == pattern_second) { + return true; + } + } + } else if (pattern_second == EcsWildcard) { + if (pattern_first == id_first) { + return true; + } + } + } else { + if ((id & ECS_ID_FLAGS_MASK) != (pattern & ECS_ID_FLAGS_MASK)) { + return false; + } + + if ((ECS_COMPONENT_MASK & pattern) == EcsWildcard) { + return true; + } + } + +error: + return false; +} + +bool ecs_id_is_pair( + ecs_id_t id) +{ + return ECS_HAS_ID_FLAG(id, PAIR); +} + +bool ecs_id_is_wildcard( + ecs_id_t id) +{ + if ((id == EcsWildcard) || (id == EcsAny)) { + return true; + } + + bool is_pair = ECS_IS_PAIR(id); + if (!is_pair) { + return false; + } + + ecs_entity_t first = ECS_PAIR_FIRST(id); + ecs_entity_t second = ECS_PAIR_SECOND(id); + + return (first == EcsWildcard) || (second == EcsWildcard) || + (first == EcsAny) || (second == EcsAny); +} + +bool ecs_id_is_valid( + const ecs_world_t *world, + ecs_id_t id) +{ + if (!id) { + return false; + } + if (ecs_id_is_wildcard(id)) { + return false; + } + + if (ECS_HAS_ID_FLAG(id, PAIR)) { + if (!ECS_PAIR_FIRST(id)) { + return false; + } + if (!ECS_PAIR_SECOND(id)) { + return false; + } + } else if (id & ECS_ID_FLAGS_MASK) { + if (!ecs_is_valid(world, id & ECS_COMPONENT_MASK)) { + return false; + } + } + + return true; +} + +ecs_flags32_t ecs_id_get_flags( + const ecs_world_t *world, + ecs_id_t id) +{ + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + return idr->flags; + } else { + return 0; + } +} + +ecs_id_t ecs_id_from_str( + const ecs_world_t *world, + const char *expr) +{ +#ifdef FLECS_SCRIPT + ecs_id_t result; + + /* Temporarily disable parser logging */ + int prev_level = ecs_log_set_level(-3); + if (!flecs_id_parse(world, NULL, expr, &result)) { + /* Invalid expression */ + ecs_log_set_level(prev_level); + return 0; + } + ecs_log_set_level(prev_level); + return result; +#else + (void)world; + (void)expr; + ecs_abort(ECS_UNSUPPORTED, "ecs_id_from_str requires FLECS_SCRIPT addon"); +#endif +} diff --git a/vendors/flecs/src/iter.c b/vendors/flecs/src/iter.c index 69afe838d..4d9d8d348 100644 --- a/vendors/flecs/src/iter.c +++ b/vendors/flecs/src/iter.c @@ -18,12 +18,12 @@ #define INIT_CACHE(it, stack, fields, f, T, count)\ if (!it->f && (fields & flecs_iter_cache_##f) && count) {\ it->f = flecs_stack_calloc_n(stack, T, count);\ - it->priv.cache.used |= flecs_iter_cache_##f;\ + it->priv_.cache.used |= flecs_iter_cache_##f;\ } /* If array is allocated, free it when finalizing the iterator */ #define FINI_CACHE(it, f, T, count)\ - if (it->priv.cache.used & flecs_iter_cache_##f) {\ + if (it->priv_.cache.used & flecs_iter_cache_##f) {\ flecs_stack_free_n((void*)it->f, T, count);\ } @@ -57,34 +57,14 @@ void flecs_iter_init( ECS_CONST_CAST(ecs_world_t**, &world)); ecs_stack_t *stack = &stage->allocators.iter_stack; - it->priv.cache.used = 0; - it->priv.cache.allocated = 0; - it->priv.cache.stack_cursor = flecs_stack_get_cursor(stack); - it->priv.entity_iter = flecs_stack_calloc_t( - stack, ecs_entity_filter_iter_t); + it->priv_.cache.used = 0; + it->priv_.cache.allocated = 0; + it->priv_.cache.stack_cursor = flecs_stack_get_cursor(stack); INIT_CACHE(it, stack, fields, ids, ecs_id_t, it->field_count); INIT_CACHE(it, stack, fields, sources, ecs_entity_t, it->field_count); - INIT_CACHE(it, stack, fields, match_indices, int32_t, it->field_count); - INIT_CACHE(it, stack, fields, columns, int32_t, it->field_count); + INIT_CACHE(it, stack, fields, trs, ecs_table_record_t*, it->field_count); INIT_CACHE(it, stack, fields, variables, ecs_var_t, it->variable_count); - INIT_CACHE(it, stack, fields, ptrs, void*, it->field_count); -} - -void flecs_iter_validate( - ecs_iter_t *it) -{ - ECS_BIT_SET(it->flags, EcsIterIsValid); - - /* Make sure multithreaded iterator isn't created for real world */ - ecs_world_t *world = it->real_world; - ecs_poly_assert(world, ecs_world_t); - ecs_check(!(world->flags & EcsWorldMultiThreaded) || it->world != it->real_world, - ECS_INVALID_PARAMETER, - "create iterator for stage when world is in multithreaded mode"); - (void)world; -error: - return; } void ecs_iter_fini( @@ -103,273 +83,129 @@ void ecs_iter_fini( FINI_CACHE(it, ids, ecs_id_t, it->field_count); FINI_CACHE(it, sources, ecs_entity_t, it->field_count); - FINI_CACHE(it, match_indices, int32_t, it->field_count); - FINI_CACHE(it, columns, int32_t, it->field_count); + FINI_CACHE(it, trs, ecs_table_record_t*, it->field_count); FINI_CACHE(it, variables, ecs_var_t, it->variable_count); - FINI_CACHE(it, ptrs, void*, it->field_count); - flecs_stack_free_t(it->priv.entity_iter, ecs_entity_filter_iter_t); ecs_stage_t *stage = flecs_stage_from_world(&world); flecs_stack_restore_cursor(&stage->allocators.iter_stack, - it->priv.cache.stack_cursor); + it->priv_.cache.stack_cursor); } -static -bool flecs_iter_populate_term_data( - ecs_world_t *world, - ecs_iter_t *it, - int32_t t, - int32_t column, - void **ptr_out) -{ - bool is_shared = false; - ecs_table_t *table; - void *data; - int32_t row, u_index; - - if (!column) { - /* Term has no data. This includes terms that have Not operators. */ - goto no_data; - } +/* --- Public API --- */ - /* Filter terms may match with data but don't return it */ - if (it->terms[t].inout == EcsInOutNone) { - goto no_data; - } +void* ecs_field_w_size( + const ecs_iter_t *it, + size_t size, + int8_t index) +{ + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + ecs_check(!size || ecs_field_size(it, index) == size || + !ecs_field_size(it, index), + ECS_INVALID_PARAMETER, "mismatching size for field %d", index); + (void)size; - ecs_assert(it->sizes != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t size = it->sizes[t]; - if (!size) { - goto no_data; + const ecs_table_record_t *tr = it->trs[index]; + if (!tr) { + ecs_assert(!ecs_field_is_set(it, index), ECS_INTERNAL_ERROR, NULL); + return NULL; } - if (column < 0) { - table = it->table; - is_shared = true; - - /* Data is not from This */ - if (it->references && (!table || !(table->flags & EcsTableHasTarget))) { - /* The reference array is used only for components matched on a - * table (vs. individual entities). Remaining components should be - * assigned outside of this function */ - if (ecs_term_match_this(&it->terms[t])) { - - /* Iterator provides cached references for non-This terms */ - ecs_ref_t *ref = &it->references[-column - 1]; - if (ptr_out) { - if (ref->id) { - ptr_out[0] = (void*)ecs_ref_get_id(world, ref, ref->id); - } else { - ptr_out[0] = NULL; - } - } - - if (!ref->id) { - is_shared = false; - } - - return is_shared; - } - - return true; - } else { - ecs_entity_t subj = it->sources[t]; - ecs_assert(subj != 0, ECS_INTERNAL_ERROR, NULL); - - /* Don't use ecs_get_id directly. Instead, go directly to the - * storage so that we can get both the pointer and size */ - ecs_record_t *r = flecs_entities_get(world, subj); - ecs_assert(r != NULL && r->table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_assert(!(idr->flags & EcsIdIsSparse), ECS_INVALID_OPERATION, + "use ecs_field_at to access fields for sparse components"); + (void)idr; - row = ECS_RECORD_TO_ROW(r->row); - table = r->table; - - ecs_id_t id = it->ids[t]; - ecs_table_record_t *tr; - - if (!(tr = flecs_table_record_get(world, table, id)) || (tr->column == -1)) { - u_index = flecs_table_column_to_union_index(table, -column - 1); - if (u_index != -1) { - goto has_union; - } - goto no_data; - } - - /* We now have row and column, so we can get the storage for the id - * which gives us the pointer and size */ - column = tr->column; - ecs_vec_t *s = &table->data.columns[column].data; - data = ecs_vec_first(s); - /* Fallthrough to has_data */ - } - } else { - /* Data is from This, use table from iterator */ + ecs_entity_t src = it->sources[index]; + ecs_table_t *table; + int32_t row; + if (!src) { table = it->table; - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - row = it->offset; - - int32_t storage_column = ecs_table_type_to_column_index( - table, column - 1); - if (storage_column == -1) { - u_index = flecs_table_column_to_union_index(table, column - 1); - if (u_index != -1) { - goto has_union; - } - goto no_data; - } - - if (!it->count) { - goto no_data; - } - - ecs_vec_t *s = &table->data.columns[storage_column].data; - data = ecs_vec_first(s); - - /* Fallthrough to has_data */ - } - -has_data: - if (ptr_out) ptr_out[0] = ECS_ELEM(data, size, row); - return is_shared; - -has_union: { - /* Edge case: if column is a switch we should return the vector with case - * identifiers. Will be replaced in the future with pluggable storage */ - ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_switch_t *sw = &table->_->sw_columns[u_index]; - data = ecs_vec_first(flecs_switch_values(sw)); - goto has_data; + } else { + ecs_record_t *r = flecs_entities_get(it->real_world, src); + table = r->table; + row = ECS_RECORD_TO_ROW(r->row); } -no_data: - if (ptr_out) ptr_out[0] = NULL; - return false; -} + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(tr->hdr.table == table, ECS_INTERNAL_ERROR, NULL); -void flecs_iter_populate_data( - ecs_world_t *world, - ecs_iter_t *it, - ecs_table_t *table, - int32_t offset, - int32_t count, - void **ptrs) -{ - ecs_table_t *prev_table = it->table; - if (prev_table) { - it->frame_offset += ecs_table_count(prev_table); - } + int32_t column_index = tr->column; + ecs_assert(column_index != -1, ECS_NOT_A_COMPONENT, + "only components can be fetched with fields"); + ecs_assert(column_index >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column_index < table->column_count, ECS_INTERNAL_ERROR, NULL); - it->table = table; - it->offset = offset; - it->count = count; - if (table) { - ecs_assert(count != 0 || !ecs_table_count(table) || (it->flags & EcsIterTableOnly), + ecs_column_t *column = &table->data.columns[column_index]; + ecs_assert((row < table->data.count) || + (it->query && (it->query->flags & EcsQueryMatchEmptyTables)), ECS_INTERNAL_ERROR, NULL); - if (count) { - it->entities = ecs_vec_get_t( - &table->data.entities, ecs_entity_t, offset); - } else { - it->entities = NULL; - } - } - int t, field_count = it->field_count; - if (ECS_BIT_IS_SET(it->flags, EcsIterNoData)) { - ECS_BIT_CLEAR(it->flags, EcsIterHasShared); - return; - } - - bool has_shared = false; - if (ptrs) { - for (t = 0; t < field_count; t ++) { - int32_t column = it->columns[t]; - has_shared |= flecs_iter_populate_term_data(world, it, t, column, - &ptrs[t]); - } + if (!size) { + size = (size_t)column->ti->size; } - ECS_BIT_COND(it->flags, EcsIterHasShared, has_shared); + return ECS_ELEM(column->data, (ecs_size_t)size, row); +error: + return NULL; } -bool flecs_iter_next_row( - ecs_iter_t *it) -{ - ecs_assert(it != NULL, ECS_INTERNAL_ERROR, NULL); - - bool is_instanced = ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced); - if (!is_instanced) { - int32_t instance_count = it->instance_count; - int32_t count = it->count; - int32_t offset = it->offset; - - if (instance_count > count && offset < (instance_count - 1)) { - ecs_assert(count == 1, ECS_INTERNAL_ERROR, NULL); - int t, field_count = it->field_count; - - for (t = 0; t < field_count; t ++) { - ecs_entity_t src = it->sources[t]; - if (!src) { - void *ptr = it->ptrs[t]; - if (ptr) { - it->ptrs[t] = ECS_OFFSET(ptr, it->sizes[t]); - } - } - } - - if (it->entities) { - it->entities ++; - } - it->offset ++; - - return true; - } +void* ecs_field_at_w_size( + const ecs_iter_t *it, + size_t size, + int8_t index, + int32_t row) +{ + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + ecs_check(!size || ecs_field_size(it, index) == size || + !ecs_field_size(it, index), + ECS_INVALID_PARAMETER, "mismatching size for field %d", index); + + const ecs_table_record_t *tr = it->trs[index]; + if (!tr) { + ecs_assert(!ecs_field_is_set(it, index), ECS_INTERNAL_ERROR, NULL); + return NULL; } - return false; -} + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_assert((idr->flags & EcsIdIsSparse), ECS_INVALID_OPERATION, + "use ecs_field to access fields for non-sparse components"); + ecs_assert(it->row_fields & (1ull << index), ECS_INTERNAL_ERROR, NULL); -bool flecs_iter_next_instanced( - ecs_iter_t *it, - bool result) -{ - it->instance_count = it->count; - bool is_instanced = ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced); - bool has_shared = ECS_BIT_IS_SET(it->flags, EcsIterHasShared); - if (result && !is_instanced && it->count && has_shared) { - it->count = 1; + ecs_entity_t src = it->sources[index]; + if (!src) { + src = ecs_table_entities(it->table)[row + it->offset]; } - return result; -} - -/* --- Public API --- */ - -void* ecs_field_w_size( - const ecs_iter_t *it, - size_t size, - int32_t index) -{ - ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - - ecs_check(!size || ecs_field_size(it, index) == size || - (!ecs_field_size(it, index) && (!it->ptrs[index - 1])), - ECS_INVALID_PARAMETER, NULL); - (void)size; - - return it->ptrs[index - 1]; + return flecs_sparse_get_any(idr->sparse, flecs_uto(int32_t, size), src); error: return NULL; } bool ecs_field_is_readonly( const ecs_iter_t *it, - int32_t index) -{ - ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - ecs_term_t *term = &it->terms[index - 1]; + int8_t index) +{ + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(it->query != NULL, ECS_INVALID_PARAMETER, + "operation only valid for query iterators"); + ecs_check(it->query->terms != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + const ecs_term_t *term = &it->query->terms[index]; if (term->inout == EcsIn) { return true; @@ -378,8 +214,8 @@ bool ecs_field_is_readonly( return true; } - ecs_term_id_t *src = &term->src; - if (!(src->flags & EcsSelf)) { + const ecs_term_ref_t *src = &term->src; + if (!(src->id & EcsSelf)) { return true; } } @@ -389,11 +225,19 @@ bool ecs_field_is_readonly( bool ecs_field_is_writeonly( const ecs_iter_t *it, - int32_t index) -{ - ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - ecs_term_t *term = &it->terms[index - 1]; + int8_t index) +{ + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(it->query != NULL, ECS_INVALID_PARAMETER, + "operation only valid for query iterators"); + ecs_check(it->query->terms != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + const ecs_term_t *term = &it->query->terms[index]; return term->inout == EcsOut; error: return false; @@ -401,74 +245,97 @@ bool ecs_field_is_writeonly( bool ecs_field_is_set( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, NULL); - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - int32_t column = it->columns[index - 1]; - if (!column) { - return false; - } else if (column < 0) { - if (it->references) { - column = -column - 1; - ecs_ref_t *ref = &it->references[column]; - return ref->entity != 0; - } else { - return true; - } - } - return true; + ecs_check(it->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + return it->set_fields & (1llu << (index)); error: return false; } bool ecs_field_is_self( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - return it->sources == NULL || it->sources[index - 1] == 0; + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + return it->sources == NULL || it->sources[index] == 0; +error: + return false; } ecs_id_t ecs_field_id( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - return it->ids[index - 1]; + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + return it->ids[index]; +error: + return 0; } -int32_t ecs_field_column_index( +int32_t ecs_field_column( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - int32_t result = it->columns[index - 1]; - if (result <= 0) { - return -1; + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + const ecs_table_record_t *tr = it->trs[index]; + if (tr) { + return tr->index; } else { - return result - 1; + return -1; } +error: + return 0; } ecs_entity_t ecs_field_src( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + if (it->sources) { - return it->sources[index - 1]; + return it->sources[index]; } else { return 0; } +error: + return 0; } size_t ecs_field_size( const ecs_iter_t *it, - int32_t index) + int8_t index) { - ecs_assert(index >= 1, ECS_INVALID_PARAMETER, NULL); - return (size_t)it->sizes[index - 1]; + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); + ecs_check(index < it->field_count, ECS_INVALID_PARAMETER, + "field index %d out of bounds", index); + + return (size_t)it->sizes[index]; +error: + return 0; } char* ecs_iter_str( @@ -480,12 +347,12 @@ char* ecs_iter_str( ecs_world_t *world = it->world; ecs_strbuf_t buf = ECS_STRBUF_INIT; - int i; + int8_t i; if (it->field_count) { ecs_strbuf_list_push(&buf, "id: ", ","); for (i = 0; i < it->field_count; i ++) { - ecs_id_t id = ecs_field_id(it, i + 1); + ecs_id_t id = ecs_field_id(it, i); char *str = ecs_id_str(world, id); ecs_strbuf_list_appendstr(&buf, str); ecs_os_free(str); @@ -494,8 +361,8 @@ char* ecs_iter_str( ecs_strbuf_list_push(&buf, "src: ", ","); for (i = 0; i < it->field_count; i ++) { - ecs_entity_t subj = ecs_field_src(it, i + 1); - char *str = ecs_get_fullpath(world, subj); + ecs_entity_t subj = ecs_field_src(it, i); + char *str = ecs_get_path(world, subj); ecs_strbuf_list_appendstr(&buf, str); ecs_os_free(str); } @@ -503,7 +370,7 @@ char* ecs_iter_str( ecs_strbuf_list_push(&buf, "set: ", ","); for (i = 0; i < it->field_count; i ++) { - if (ecs_field_is_set(it, i + 1)) { + if (ecs_field_is_set(it, i)) { ecs_strbuf_list_appendlit(&buf, "true"); } else { ecs_strbuf_list_appendlit(&buf, "false"); @@ -516,7 +383,7 @@ char* ecs_iter_str( int32_t actual_count = 0; for (i = 0; i < it->variable_count; i ++) { const char *var_name = it->variable_names[i]; - if (!var_name || var_name[0] == '_' || !strcmp(var_name, "This")) { + if (!var_name || var_name[0] == '_' || !strcmp(var_name, "this")) { /* Skip anonymous variables */ continue; } @@ -531,7 +398,7 @@ char* ecs_iter_str( ecs_strbuf_list_push(&buf, "var: ", ","); } - char *str = ecs_get_fullpath(world, var.entity); + char *str = ecs_get_path(world, var.entity); ecs_strbuf_list_append(&buf, "%s=%s", var_name, str); ecs_os_free(str); @@ -546,7 +413,7 @@ char* ecs_iter_str( ecs_strbuf_appendlit(&buf, "this:\n"); for (i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; - char *str = ecs_get_fullpath(world, e); + char *str = ecs_get_path(world, e); ecs_strbuf_appendlit(&buf, " - "); ecs_strbuf_appendstr(&buf, str); ecs_strbuf_appendch(&buf, '\n'); @@ -557,16 +424,6 @@ char* ecs_iter_str( return ecs_strbuf_get(&buf); } -void ecs_iter_poly( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter_out, - ecs_term_t *filter) -{ - ecs_iterable_t *iterable = ecs_get_iterable(poly); - iterable->init(world, poly, iter_out, filter); -} - bool ecs_iter_next( ecs_iter_t *iter) { @@ -583,7 +440,6 @@ int32_t ecs_iter_count( ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ECS_BIT_SET(it->flags, EcsIterNoData); - ECS_BIT_SET(it->flags, EcsIterIsInstanced); int32_t count = 0; while (ecs_iter_next(it)) { @@ -600,7 +456,6 @@ ecs_entity_t ecs_iter_first( ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ECS_BIT_SET(it->flags, EcsIterNoData); - ECS_BIT_SET(it->flags, EcsIterIsInstanced); ecs_entity_t result = 0; if (ecs_iter_next(it)) { @@ -619,7 +474,6 @@ bool ecs_iter_is_true( ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ECS_BIT_SET(it->flags, EcsIterNoData); - ECS_BIT_SET(it->flags, EcsIterIsInstanced); bool result = ecs_iter_next(it); if (result) { @@ -634,8 +488,10 @@ ecs_entity_t ecs_iter_get_var( ecs_iter_t *it, int32_t var_id) { - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(it->variables != NULL, ECS_INVALID_PARAMETER, NULL); ecs_var_t *var = &it->variables[var_id]; @@ -649,8 +505,7 @@ ecs_entity_t ecs_iter_get_var( if ((var->range.count == 1) || (ecs_table_count(table) == 1)) { ecs_assert(ecs_table_count(table) > var->range.offset, ECS_INTERNAL_ERROR, NULL); - e = ecs_vec_get_t(&table->data.entities, ecs_entity_t, - var->range.offset)[0]; + e = ecs_table_entities(table)[var->range.offset]; } } } else { @@ -666,8 +521,10 @@ ecs_table_t* ecs_iter_get_var_as_table( ecs_iter_t *it, int32_t var_id) { - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(it->variables != NULL, ECS_INVALID_PARAMETER, NULL); ecs_var_t *var = &it->variables[var_id]; @@ -712,8 +569,10 @@ ecs_table_range_t ecs_iter_get_var_as_range( ecs_iter_t *it, int32_t var_id) { - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(it->variables != NULL, ECS_INVALID_PARAMETER, NULL); ecs_table_range_t result = { 0 }; @@ -754,12 +613,14 @@ void ecs_iter_set_var( ecs_entity_t entity) { ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < FLECS_VARIABLE_COUNT_MAX, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < FLECS_QUERY_VARIABLE_COUNT_MAX, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); - /* Can't set variable while iterating */ - ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_PARAMETER, NULL); + ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_PARAMETER, + "cannot constrain variable while iterating"); ecs_check(it->variables != NULL, ECS_INTERNAL_ERROR, NULL); ecs_var_t *var = &it->variables[var_id]; @@ -778,9 +639,8 @@ void ecs_iter_set_var( it->constrained_vars |= flecs_ito(uint64_t, 1 << var_id); - if (it->set_var) { - it->set_var(it); - } + /* Update iterator for constrained iterator */ + flecs_query_iter_constrain(it); error: return; @@ -801,9 +661,10 @@ void ecs_iter_set_var_as_range( const ecs_table_range_t *range) { ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < FLECS_VARIABLE_COUNT_MAX, ECS_INVALID_PARAMETER, NULL); - ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, NULL); + ecs_check(var_id >= 0, ECS_INVALID_PARAMETER, + "invalid variable index %d", var_id); + ecs_check(var_id < it->variable_count, ECS_INVALID_PARAMETER, + "variable index %d out of bounds", var_id); ecs_check(range != 0, ECS_INVALID_PARAMETER, NULL); ecs_check(range->table != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(!range->offset || range->offset < ecs_table_count(range->table), @@ -811,22 +672,24 @@ void ecs_iter_set_var_as_range( ecs_check((range->offset + range->count) <= ecs_table_count(range->table), ECS_INVALID_PARAMETER, NULL); - /* Can't set variable while iterating */ - ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_OPERATION, NULL); + ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_OPERATION, + "cannot set query variables while iterating"); ecs_var_t *var = &it->variables[var_id]; var->range = *range; if (range->count == 1) { ecs_table_t *table = range->table; - var->entity = ecs_vec_get_t( - &table->data.entities, ecs_entity_t, range->offset)[0]; + var->entity = ecs_table_entities(table)[range->offset]; } else { var->entity = 0; } it->constrained_vars |= flecs_uto(uint64_t, 1 << var_id); + /* Update iterator for constrained iterator */ + flecs_query_iter_constrain(it); + error: return; } @@ -859,9 +722,9 @@ ecs_iter_t ecs_page_iter( ecs_check(it->next != NULL, ECS_INVALID_PARAMETER, NULL); ecs_iter_t result = *it; - result.priv.cache.stack_cursor = NULL; /* Don't copy allocator cursor */ + result.priv_.cache.stack_cursor = NULL; /* Don't copy allocator cursor */ - result.priv.iter.page = (ecs_page_iter_t){ + result.priv_.iter.page = (ecs_page_iter_t){ .offset = offset, .limit = limit, .remaining = limit @@ -875,33 +738,7 @@ ecs_iter_t ecs_page_iter( return (ecs_iter_t){ 0 }; } -static -void flecs_offset_iter( - ecs_iter_t *it, - int32_t offset) -{ - it->entities = &it->entities[offset]; - - int32_t t, field_count = it->field_count; - void **it_ptrs = it->ptrs; - if (it_ptrs) { - for (t = 0; t < field_count; t ++) { - void *ptrs = it_ptrs[t]; - if (!ptrs) { - continue; - } - - if (it->sources[t]) { - continue; - } - - it->ptrs[t] = ECS_OFFSET(ptrs, offset * it->sizes[t]); - } - } -} - -static -bool ecs_page_next_instanced( +bool ecs_page_next( ecs_iter_t *it) { ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); @@ -909,20 +746,16 @@ bool ecs_page_next_instanced( ecs_check(it->next == ecs_page_next, ECS_INVALID_PARAMETER, NULL); ecs_iter_t *chain_it = it->chain_it; - bool instanced = ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced); do { if (!ecs_iter_next(chain_it)) { goto depleted; } - ecs_page_iter_t *iter = &it->priv.iter.page; + ecs_page_iter_t *iter = &it->priv_.iter.page; /* Copy everything up to the private iterator data */ - ecs_os_memcpy(it, chain_it, offsetof(ecs_iter_t, priv)); - - /* Keep instancing setting from original iterator */ - ECS_BIT_COND(it->flags, EcsIterIsInstanced, instanced); + ecs_os_memcpy(it, chain_it, offsetof(ecs_iter_t, priv_)); if (!chain_it->table) { goto yield; /* Task query */ @@ -948,10 +781,11 @@ bool ecs_page_next_instanced( it->count = 0; continue; } else { - it->offset += offset; - count = it->count -= offset; iter->offset = 0; - flecs_offset_iter(it, offset); + it->offset = offset; + count = it->count -= offset; + it->entities = + &(ecs_table_entities(it->table)[it->offset]); } } @@ -969,33 +803,13 @@ bool ecs_page_next_instanced( } while (it->count == 0); yield: - if (!ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced)) { - it->offset = 0; - } - return true; + done: /* Cleanup iterator resources if it wasn't yet depleted */ ecs_iter_fini(chain_it); -depleted: -error: - return false; -} - -bool ecs_page_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_page_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->chain_it != NULL, ECS_INVALID_PARAMETER, NULL); - - ECS_BIT_SET(it->chain_it->flags, EcsIterIsInstanced); - - if (flecs_iter_next_row(it)) { - return true; - } - return flecs_iter_next_instanced(it, ecs_page_next_instanced(it)); +depleted: error: return false; } @@ -1008,13 +822,14 @@ ecs_iter_t ecs_worker_iter( ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(it->next != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(count > 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(index >= 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(index >= 0, ECS_INVALID_PARAMETER, + "invalid field index %d", index); ecs_check(index < count, ECS_INVALID_PARAMETER, NULL); ecs_iter_t result = *it; - result.priv.cache.stack_cursor = NULL; /* Don't copy allocator cursor */ + result.priv_.cache.stack_cursor = NULL; /* Don't copy allocator cursor */ - result.priv.iter.worker = (ecs_worker_iter_t){ + result.priv_.iter.worker = (ecs_worker_iter_t){ .index = index, .count = count }; @@ -1027,20 +842,17 @@ ecs_iter_t ecs_worker_iter( return (ecs_iter_t){ 0 }; } -static -bool ecs_worker_next_instanced( +bool ecs_worker_next( ecs_iter_t *it) { ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(it->chain_it != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(it->next == ecs_worker_next, ECS_INVALID_PARAMETER, NULL); - bool instanced = ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced); - ecs_iter_t *chain_it = it->chain_it; - ecs_worker_iter_t *iter = &it->priv.iter.worker; + ecs_worker_iter_t *iter = &it->priv_.iter.worker; int32_t res_count = iter->count, res_index = iter->index; - int32_t per_worker, instances_per_worker, first; + int32_t per_worker, first; do { if (!ecs_iter_next(chain_it)) { @@ -1048,15 +860,10 @@ bool ecs_worker_next_instanced( } /* Copy everything up to the private iterator data */ - ecs_os_memcpy(it, chain_it, offsetof(ecs_iter_t, priv)); - - /* Keep instancing setting from original iterator */ - ECS_BIT_COND(it->flags, EcsIterIsInstanced, instanced); + ecs_os_memcpy(it, chain_it, offsetof(ecs_iter_t, priv_)); int32_t count = it->count; - int32_t instance_count = it->instance_count; per_worker = count / res_count; - instances_per_worker = instance_count / res_count; first = per_worker * res_index; count -= per_worker * res_count; @@ -1081,37 +888,13 @@ bool ecs_worker_next_instanced( } } while (!per_worker); - it->instance_count = instances_per_worker; it->frame_offset += first; - - flecs_offset_iter(it, it->offset + first); it->count = per_worker; + it->offset += first; - if (ECS_BIT_IS_SET(it->flags, EcsIterIsInstanced)) { - it->offset += first; - } else { - it->offset = 0; - } + it->entities = &(ecs_table_entities(it->table)[it->offset]); return true; error: return false; } - -bool ecs_worker_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_worker_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->chain_it != NULL, ECS_INVALID_PARAMETER, NULL); - - ECS_BIT_SET(it->chain_it->flags, EcsIterIsInstanced); - - if (flecs_iter_next_row(it)) { - return true; - } - - return flecs_iter_next_instanced(it, ecs_worker_next_instanced(it)); -error: - return false; -} diff --git a/vendors/flecs/src/iter.h b/vendors/flecs/src/iter.h index e07418c65..aa619f873 100644 --- a/vendors/flecs/src/iter.h +++ b/vendors/flecs/src/iter.h @@ -11,24 +11,6 @@ void flecs_iter_init( ecs_iter_t *it, ecs_flags8_t fields); -void flecs_iter_validate( - ecs_iter_t *it); - -void flecs_iter_populate_data( - ecs_world_t *world, - ecs_iter_t *it, - ecs_table_t *table, - int32_t offset, - int32_t count, - void **ptrs); - -bool flecs_iter_next_row( - ecs_iter_t *it); - -bool flecs_iter_next_instanced( - ecs_iter_t *it, - bool result); - void* flecs_iter_calloc( ecs_iter_t *it, ecs_size_t size, diff --git a/vendors/flecs/src/misc.c b/vendors/flecs/src/misc.c index 77932967d..5c4544540 100644 --- a/vendors/flecs/src/misc.c +++ b/vendors/flecs/src/misc.c @@ -127,6 +127,12 @@ int flecs_entity_compare( return (e1 > e2) - (e1 < e2); } +int flecs_id_qsort_cmp(const void *a, const void *b) { + ecs_id_t id_a = *(const ecs_id_t*)a; + ecs_id_t id_b = *(const ecs_id_t*)b; + return (id_a > id_b) - (id_a < id_b); +} + uint64_t flecs_string_hash( const void *ptr) { @@ -135,7 +141,7 @@ uint64_t flecs_string_hash( return str->hash; } -char* ecs_vasprintf( +char* flecs_vasprintf( const char *fmt, va_list args) { @@ -159,18 +165,18 @@ char* ecs_vasprintf( return NULL; } - ecs_os_vsprintf(result, fmt, args); + ecs_os_vsnprintf(result, size + 1, fmt, args); return result; } -char* ecs_asprintf( +char* flecs_asprintf( const char *fmt, ...) { va_list args; va_start(args, fmt); - char *result = ecs_vasprintf(fmt, args); + char *result = flecs_vasprintf(fmt, args); va_end(args); return result; } @@ -206,3 +212,256 @@ char* flecs_to_snake_case(const char *str) { return out; } + +char* flecs_load_from_file( + const char *filename) +{ + FILE* file; + char* content = NULL; + int32_t bytes; + size_t size; + + /* Open file for reading */ + ecs_os_fopen(&file, filename, "r"); + if (!file) { + ecs_err("%s (%s)", ecs_os_strerror(errno), filename); + goto error; + } + + /* Determine file size */ + fseek(file, 0, SEEK_END); + bytes = (int32_t)ftell(file); + if (bytes == -1) { + goto error; + } + fseek(file, 0, SEEK_SET); + + /* Load contents in memory */ + content = ecs_os_malloc(bytes + 1); + size = (size_t)bytes; + if (!(size = fread(content, 1, size, file)) && bytes) { + ecs_err("%s: read zero bytes instead of %d", filename, size); + ecs_os_free(content); + content = NULL; + goto error; + } else { + content[size] = '\0'; + } + + fclose(file); + + return content; +error: + ecs_os_free(content); + return NULL; +} + +char* flecs_chresc( + char *out, + char in, + char delimiter) +{ + char *bptr = out; + switch(in) { + case '\a': + *bptr++ = '\\'; + *bptr = 'a'; + break; + case '\b': + *bptr++ = '\\'; + *bptr = 'b'; + break; + case '\f': + *bptr++ = '\\'; + *bptr = 'f'; + break; + case '\n': + *bptr++ = '\\'; + *bptr = 'n'; + break; + case '\r': + *bptr++ = '\\'; + *bptr = 'r'; + break; + case '\t': + *bptr++ = '\\'; + *bptr = 't'; + break; + case '\v': + *bptr++ = '\\'; + *bptr = 'v'; + break; + case '\\': + *bptr++ = '\\'; + *bptr = '\\'; + break; + case '\033': + *bptr = '['; /* Used for terminal colors */ + break; + default: + if (in == delimiter) { + *bptr++ = '\\'; + *bptr = delimiter; + } else { + *bptr = in; + } + break; + } + + *(++bptr) = '\0'; + + return bptr; +} + +const char* flecs_chrparse( + const char *in, + char *out) +{ + const char *result = in + 1; + char ch; + + if (in[0] == '\\') { + result ++; + + switch(in[1]) { + case 'a': + ch = '\a'; + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case 'v': + ch = '\v'; + break; + case '\\': + ch = '\\'; + break; + case '"': + ch = '"'; + break; + case '0': + ch = '\0'; + break; + case ' ': + ch = ' '; + break; + case '$': + ch = '$'; + break; + default: + goto error; + } + } else { + ch = in[0]; + } + + if (out) { + *out = ch; + } + + return result; +error: + return NULL; +} + +ecs_size_t flecs_stresc( + char *out, + ecs_size_t n, + char delimiter, + const char *in) +{ + const char *ptr = in; + char ch, *bptr = out, buff[3]; + ecs_size_t written = 0; + while ((ch = *ptr++)) { + if ((written += (ecs_size_t)(flecs_chresc( + buff, ch, delimiter) - buff)) <= n) + { + /* If size != 0, an out buffer must be provided. */ + ecs_check(out != NULL, ECS_INVALID_PARAMETER, NULL); + *bptr++ = buff[0]; + if ((ch = buff[1])) { + *bptr = ch; + bptr++; + } + } + } + + if (bptr) { + while (written < n) { + *bptr = '\0'; + bptr++; + written++; + } + } + return written; +error: + return 0; +} + +char* flecs_astresc( + char delimiter, + const char *in) +{ + if (!in) { + return NULL; + } + + ecs_size_t len = flecs_stresc(NULL, 0, delimiter, in); + char *out = ecs_os_malloc_n(char, len + 1); + flecs_stresc(out, len, delimiter, in); + out[len] = '\0'; + return out; +} + +const char* flecs_parse_digit( + const char *ptr, + char *token) +{ + char *tptr = token; + char ch = ptr[0]; + + if (!isdigit(ch) && ch != '-') { + ecs_parser_error(NULL, NULL, 0, "invalid start of number '%s'", ptr); + return NULL; + } + + tptr[0] = ch; + tptr ++; + ptr ++; + + for (; (ch = *ptr); ptr ++) { + if (!isdigit(ch) && (ch != '.') && (ch != 'e')) { + break; + } + + tptr[0] = ch; + tptr ++; + } + + tptr[0] = '\0'; + + return ptr; +} + +const char* flecs_parse_ws_eol( + const char *ptr) +{ + while (isspace(*ptr)) { + ptr ++; + } + + return ptr; +} diff --git a/vendors/flecs/src/observable.c b/vendors/flecs/src/observable.c index 390b5b03e..d6bcdbe79 100644 --- a/vendors/flecs/src/observable.c +++ b/vendors/flecs/src/observable.c @@ -17,7 +17,6 @@ void flecs_observable_init( observable->on_add.event = EcsOnAdd; observable->on_remove.event = EcsOnRemove; observable->on_set.event = EcsOnSet; - observable->un_set.event = EcsUnSet; } void flecs_observable_fini( @@ -29,8 +28,6 @@ void flecs_observable_fini( ECS_INTERNAL_ERROR, NULL); ecs_assert(!ecs_map_is_init(&observable->on_set.event_ids), ECS_INTERNAL_ERROR, NULL); - ecs_assert(!ecs_map_is_init(&observable->un_set.event_ids), - ECS_INTERNAL_ERROR, NULL); ecs_sparse_t *events = &observable->events; int32_t i, count = flecs_sparse_count(events); @@ -40,7 +37,7 @@ void flecs_observable_fini( ecs_assert(er != NULL, ECS_INTERNAL_ERROR, NULL); (void)er; - /* All triggers should've unregistered by now */ + /* All observers should've unregistered by now */ ecs_assert(!ecs_map_is_init(&er->event_ids), ECS_INTERNAL_ERROR, NULL); } @@ -55,11 +52,10 @@ ecs_event_record_t* flecs_event_record_get( ecs_assert(o != NULL, ECS_INTERNAL_ERROR, NULL); /* Builtin events*/ - if (event == EcsOnAdd) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_add); - else if (event == EcsOnRemove) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_remove); - else if (event == EcsOnSet) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_set); - else if (event == EcsUnSet) return ECS_CONST_CAST(ecs_event_record_t*, &o->un_set); - else if (event == EcsWildcard) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_wildcard); + if (event == EcsOnAdd) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_add); + else if (event == EcsOnRemove) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_remove); + else if (event == EcsOnSet) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_set); + else if (event == EcsWildcard) return ECS_CONST_CAST(ecs_event_record_t*, &o->on_wildcard); /* User events */ return flecs_sparse_try_t(&o->events, ecs_event_record_t, event); @@ -211,13 +207,19 @@ int32_t flecs_event_observers_get( ecs_id_t id_fwc = ecs_pair(EcsWildcard, ECS_PAIR_SECOND(id)); ecs_id_t id_swc = ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard); ecs_id_t id_pwc = ecs_pair(EcsWildcard, EcsWildcard); - iders[count] = flecs_event_id_record_get_if(er, id_fwc); - count += iders[count] != 0; - iders[count] = flecs_event_id_record_get_if(er, id_swc); - count += iders[count] != 0; - iders[count] = flecs_event_id_record_get_if(er, id_pwc); - count += iders[count] != 0; - } else { + if (id_fwc != id) { + iders[count] = flecs_event_id_record_get_if(er, id_fwc); + count += iders[count] != 0; + } + if (id_swc != id) { + iders[count] = flecs_event_id_record_get_if(er, id_swc); + count += iders[count] != 0; + } + if (id_pwc != id) { + iders[count] = flecs_event_id_record_get_if(er, id_pwc); + count += iders[count] != 0; + } + } else if (id != EcsWildcard) { iders[count] = flecs_event_id_record_get_if(er, EcsWildcard); count += iders[count] != 0; } @@ -265,6 +267,7 @@ void flecs_emit_propagate_id( } const ecs_table_record_t *tr; + int32_t event_cur = it->event_cur; while ((tr = flecs_table_cache_next(&idt, ecs_table_record_t))) { ecs_table_t *table = tr->hdr.table; if (!ecs_table_count(table)) { @@ -278,23 +281,23 @@ void flecs_emit_propagate_id( it->other_table = NULL; it->offset = 0; it->count = entity_count; + it->up_fields = 1; if (entity_count) { - it->entities = ecs_vec_first(&table->data.entities); + it->entities = ecs_table_entities(table); } /* Treat as new event as this could invoke observers again for * different tables. */ - int32_t evtx = ++ world->event_id; + it->event_cur = ++ world->event_id; int32_t ider_i; for (ider_i = 0; ider_i < ider_count; ider_i ++) { ecs_event_id_record_t *ider = iders[ider_i]; - flecs_observers_invoke(world, &ider->up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->up, it, table, trav); if (!owned) { /* Owned takes precedence */ - flecs_observers_invoke( - world, &ider->self_up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->self_up, it, table, trav); } } @@ -302,7 +305,7 @@ void flecs_emit_propagate_id( continue; } - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + const ecs_entity_t *entities = ecs_table_entities(table); for (e = 0; e < entity_count; e ++) { ecs_record_t *r = flecs_entities_get(world, entities[e]); ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); @@ -315,6 +318,9 @@ void flecs_emit_propagate_id( } } } + + it->event_cur = event_cur; + it->up_fields = 0; } static @@ -334,6 +340,7 @@ void flecs_emit_propagate( ecs_dbg_3("propagate events/invalidate cache for %s", idstr); ecs_os_free(idstr); } + ecs_log_push_3(); /* Propagate to records of traversable relationships */ @@ -344,7 +351,9 @@ void flecs_emit_propagate( /* Get traversed relationship */ ecs_entity_t trav = ECS_PAIR_FIRST(cur->id); if (propagate_trav && propagate_trav != trav) { - continue; + if (propagate_trav != EcsIsA) { + continue; + } } flecs_emit_propagate_id( @@ -391,7 +400,7 @@ void flecs_emit_propagate_invalidate_tables( } int32_t e, entity_count = ecs_table_count(table); - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + const ecs_entity_t *entities = ecs_table_entities(table); for (e = 0; e < entity_count; e ++) { ecs_record_t *r = flecs_entities_get(world, entities[e]); @@ -412,8 +421,7 @@ void flecs_emit_propagate_invalidate( int32_t offset, int32_t count) { - ecs_entity_t *entities = ecs_vec_get_t(&table->data.entities, - ecs_entity_t, offset); + const ecs_entity_t *entities = &ecs_table_entities(table)[offset]; int32_t i; for (i = 0; i < count; i ++) { ecs_record_t *record = flecs_entities_get(world, entities[i]); @@ -431,10 +439,60 @@ void flecs_emit_propagate_invalidate( } } +static +void flecs_propagate_entities( + ecs_world_t *world, + ecs_iter_t *it, + ecs_id_record_t *idr, + const ecs_entity_t *entities, + int32_t count, + ecs_entity_t src, + ecs_event_id_record_t **iders, + int32_t ider_count) +{ + if (!count) { + return; + } + + ecs_entity_t old_src = it->sources[0]; + ecs_table_t *old_table = it->table; + ecs_table_t *old_other_table = it->other_table; + const ecs_entity_t *old_entities = it->entities; + int32_t old_count = it->count; + int32_t old_offset = it->offset; + + int32_t i; + for (i = 0; i < count; i ++) { + ecs_record_t *record = flecs_entities_get(world, entities[i]); + if (!record) { + /* If the event is emitted after a bulk operation, it's possible + * that it hasn't been populated with entities yet. */ + continue; + } + + ecs_id_record_t *idr_t = record->idr; + if (idr_t) { + /* Entity is used as target in traversable pairs, propagate */ + ecs_entity_t e = src ? src : entities[i]; + it->sources[0] = e; + flecs_emit_propagate( + world, it, idr, idr_t, 0, iders, ider_count); + } + } + + it->table = old_table; + it->other_table = old_other_table; + it->entities = old_entities; + it->count = old_count; + it->offset = old_offset; + it->sources[0] = old_src; +} + static void flecs_override_copy( ecs_world_t *world, ecs_table_t *table, + const ecs_table_record_t *tr, const ecs_type_info_t *ti, void *dst, const void *src, @@ -445,9 +503,10 @@ void flecs_override_copy( ecs_copy_t copy = ti->hooks.copy; ecs_size_t size = ti->size; int32_t i; + if (copy) { for (i = 0; i < count; i ++) { - copy(ptr, src, count, ti); + copy(ptr, src, 1, ti); ptr = ECS_OFFSET(ptr, size); } } else { @@ -459,10 +518,9 @@ void flecs_override_copy( ecs_iter_action_t on_set = ti->hooks.on_set; if (on_set) { - ecs_entity_t *entities = ecs_vec_get_t( - &table->data.entities, ecs_entity_t, offset); - flecs_invoke_hook(world, table, count, offset, entities, - dst, ti->component, ti, EcsOnSet, on_set); + const ecs_entity_t *entities = &ecs_table_entities(table)[offset]; + flecs_invoke_hook(world, table, tr, count, offset, entities, + ti->component, ti, EcsOnSet, on_set); } } @@ -486,7 +544,7 @@ void* flecs_override( * (like what happens during an auto override), we need to copy the * value of the inherited component to the new component. * Also flag to the callee that this component was overridden, so - * that an OnSet event can be emmitted for it. + * that an OnSet event can be emitted for it. * Note that this is different from a component that was overridden * after it was inherited, as this does not change the actual value * of the component for the entity (it is copied from the existing @@ -501,7 +559,7 @@ void* flecs_override( ecs_column_t *column = &table->data.columns[index]; ecs_size_t size = column->ti->size; - return ecs_vec_get(&column->data, size, it->offset); + return ECS_ELEM(column->data, size, it->offset); } } @@ -518,8 +576,7 @@ void flecs_emit_forward_up( ecs_table_t *table, ecs_id_record_t *idr, ecs_vec_t *stack, - ecs_vec_t *reachable_ids, - int32_t evtx); + ecs_vec_t *reachable_ids); static void flecs_emit_forward_id( @@ -533,9 +590,7 @@ void flecs_emit_forward_id( ecs_entity_t tgt, ecs_table_t *tgt_table, int32_t column, - int32_t offset, - ecs_entity_t trav, - int32_t evtx) + ecs_entity_t trav) { ecs_id_t id = idr->id; ecs_entity_t event = er ? er->event : 0; @@ -559,15 +614,15 @@ void flecs_emit_forward_id( it->ids[0] = id; it->sources[0] = tgt; it->event_id = id; - it->ptrs[0] = NULL; - it->sizes[0] = 0; + ECS_CONST_CAST(int32_t*, it->sizes)[0] = 0; /* safe, owned by observer */ + it->up_fields = 1; int32_t storage_i = ecs_table_type_to_column_index(tgt_table, column); if (storage_i != -1) { ecs_assert(idr->type_info != NULL, ECS_INTERNAL_ERROR, NULL); ecs_column_t *c = &tgt_table->data.columns[storage_i]; - it->ptrs[0] = ecs_vec_get(&c->data, c->ti->size, offset); - it->sizes[0] = c->ti->size; + it->trs[0] = &tgt_table->_->records[column]; + ECS_CONST_CAST(int32_t*, it->sizes)[0] = c->ti->size; /* safe, see above */ } ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); @@ -575,11 +630,11 @@ void flecs_emit_forward_id( for (ider_i = 0; ider_i < ider_count; ider_i ++) { ecs_event_id_record_t *ider = iders[ider_i]; - flecs_observers_invoke(world, &ider->up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->up, it, table, trav); /* Owned takes precedence */ if (!owned) { - flecs_observers_invoke(world, &ider->self_up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->self_up, it, table, trav); } } @@ -589,11 +644,11 @@ void flecs_emit_forward_id( /* If component was added together with IsA relationship, still emit * OnSet event, as it's a new value for the entity. */ - void *base_ptr = it->ptrs[0]; + ecs_table_record_t *base_tr = ECS_CONST_CAST( + ecs_table_record_t*, it->trs[0]); void *ptr = flecs_override(it, emit_ids, id, table, idr); if (ptr) { override = true; - it->ptrs[0] = ptr; } if (ider_onset_count) { @@ -601,25 +656,28 @@ void flecs_emit_forward_id( for (ider_onset_i = 0; ider_onset_i < ider_onset_count; ider_onset_i ++) { ecs_event_id_record_t *ider = iders_onset[ider_onset_i]; - flecs_observers_invoke(world, &ider->up, it, table, trav, evtx); + flecs_observers_invoke(world, &ider->up, it, table, trav); /* Owned takes precedence */ if (!owned) { flecs_observers_invoke( - world, &ider->self_up, it, table, trav, evtx); + world, &ider->self_up, it, table, trav); } else if (override) { ecs_entity_t src = it->sources[0]; it->sources[0] = 0; - flecs_observers_invoke(world, &ider->self, it, table, 0, evtx); - flecs_observers_invoke(world, &ider->self_up, it, table, 0, evtx); + it->trs[0] = tr; + flecs_observers_invoke(world, &ider->self, it, table, 0); + flecs_observers_invoke(world, &ider->self_up, it, table, 0); it->sources[0] = src; } } it->event = event; - it->ptrs[0] = base_ptr; + it->trs[0] = base_tr; } } + + it->up_fields = 0; } static @@ -636,10 +694,8 @@ void flecs_emit_forward_and_cache_id( ecs_table_t *tgt_table, const ecs_table_record_t *tgt_tr, int32_t column, - int32_t offset, ecs_vec_t *reachable_ids, - ecs_entity_t trav, - int32_t evtx) + ecs_entity_t trav) { /* Cache forwarded id for (rel, tgt) pair */ ecs_reachable_elem_t *elem = ecs_vec_append_t(&world->allocator, @@ -654,7 +710,7 @@ void flecs_emit_forward_and_cache_id( ecs_assert(tgt_table == tgt_record->table, ECS_INTERNAL_ERROR, NULL); flecs_emit_forward_id(world, er, er_onset, emit_ids, it, table, idr, - tgt, tgt_table, column, offset, trav, evtx); + tgt, tgt_table, column, trav); } static @@ -694,8 +750,7 @@ void flecs_emit_forward_cached_ids( ecs_reachable_cache_t *rc, ecs_vec_t *reachable_ids, ecs_vec_t *stack, - ecs_entity_t trav, - int32_t evtx) + ecs_entity_t trav) { ecs_reachable_elem_t *elems = ecs_vec_first_t(&rc->ids, ecs_reachable_elem_t); @@ -718,11 +773,10 @@ void flecs_emit_forward_cached_ids( continue; } - int32_t rc_offset = ECS_RECORD_TO_ROW(rc_record->row); flecs_emit_forward_and_cache_id(world, er, er_onset, emit_ids, it, table, rc_idr, rc_elem->src, rc_record, rc_record->table, rc_tr, rc_tr->index, - rc_offset, reachable_ids, trav, evtx); + reachable_ids, trav); } } @@ -761,13 +815,11 @@ void flecs_emit_forward_table_up( ecs_record_t *tgt_record, ecs_id_record_t *tgt_idr, ecs_vec_t *stack, - ecs_vec_t *reachable_ids, - int32_t evtx) + ecs_vec_t *reachable_ids) { ecs_allocator_t *a = &world->allocator; int32_t i, id_count = tgt_table->type.count; ecs_id_t *ids = tgt_table->type.array; - int32_t offset = ECS_RECORD_TO_ROW(tgt_record->row); int32_t rc_child_offset = ecs_vec_count(reachable_ids); int32_t stack_count = ecs_vec_count(stack); @@ -796,12 +848,12 @@ void flecs_emit_forward_table_up( ecs_id_t id = ids[i]; ecs_table_record_t *tgt_tr = &tgt_table->_->records[i]; ecs_id_record_t *idr = (ecs_id_record_t*)tgt_tr->hdr.cache; - if (inherit && (idr->flags & EcsIdDontInherit)) { + if (inherit && !(idr->flags & EcsIdOnInstantiateInherit)) { continue; } /* Id has the same relationship, traverse to find ids for forwarding */ - if (ECS_PAIR_FIRST(id) == trav) { + if (ECS_PAIR_FIRST(id) == trav || ECS_PAIR_FIRST(id) == EcsIsA) { ecs_table_t **t = ecs_vec_append_t(&world->allocator, stack, ecs_table_t*); t[0] = tgt_table; @@ -818,13 +870,13 @@ void flecs_emit_forward_table_up( } ecs_log_push_3(); flecs_emit_forward_cached_ids(world, er, er_onset, emit_ids, it, - table, idr_rc, reachable_ids, stack, trav, evtx); + table, idr_rc, reachable_ids, stack, trav); ecs_log_pop_3(); } else { /* Cache is dirty, traverse upwards */ do { flecs_emit_forward_up(world, er, er_onset, emit_ids, it, - table, idr, stack, reachable_ids, evtx); + table, idr, stack, reachable_ids); if (++i >= id_count) { break; } @@ -861,7 +913,7 @@ void flecs_emit_forward_table_up( flecs_emit_forward_and_cache_id(world, er, er_onset, emit_ids, it, table, idr, tgt, tgt_record, tgt_table, tgt_tr, i, - offset, reachable_ids, trav, evtx); + reachable_ids, trav); } if (parent_revalidate) { @@ -906,12 +958,11 @@ void flecs_emit_forward_up( ecs_table_t *table, ecs_id_record_t *idr, ecs_vec_t *stack, - ecs_vec_t *reachable_ids, - int32_t evtx) + ecs_vec_t *reachable_ids) { ecs_id_t id = idr->id; ecs_entity_t tgt = ECS_PAIR_SECOND(id); - tgt = flecs_entities_get_generation(world, tgt); + tgt = flecs_entities_get_alive(world, tgt); ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); ecs_record_t *tgt_record = flecs_entities_try(world, tgt); ecs_table_t *tgt_table; @@ -920,7 +971,7 @@ void flecs_emit_forward_up( } flecs_emit_forward_table_up(world, er, er_onset, emit_ids, it, table, - tgt, tgt_table, tgt_record, idr, stack, reachable_ids, evtx); + tgt, tgt_table, tgt_record, idr, stack, reachable_ids); } static @@ -931,8 +982,7 @@ void flecs_emit_forward( const ecs_type_t *emit_ids, ecs_iter_t *it, ecs_table_t *table, - ecs_id_record_t *idr, - int32_t evtx) + ecs_id_record_t *idr) { ecs_reachable_cache_t *rc = &idr->reachable; @@ -949,7 +999,7 @@ void flecs_emit_forward( ecs_vec_init_t(&world->allocator, &stack, ecs_table_t*, 0); ecs_vec_reset_t(&world->allocator, &rc->ids, ecs_reachable_elem_t); flecs_emit_forward_up(world, er, er_onset, emit_ids, it, table, - idr, &stack, &rc->ids, evtx); + idr, &stack, &rc->ids); it->sources[0] = 0; ecs_vec_fini_t(&world->allocator, &stack, ecs_table_t*); @@ -994,9 +1044,48 @@ void flecs_emit_forward( ECS_INTERNAL_ERROR, NULL); ecs_dbg_assert(r->table == elem->table, ECS_INTERNAL_ERROR, NULL); - int32_t offset = ECS_RECORD_TO_ROW(r->row); flecs_emit_forward_id(world, er, er_onset, emit_ids, it, table, - rc_idr, elem->src, r->table, tr->index, offset, trav, evtx); + rc_idr, elem->src, r->table, tr->index, trav); + } + } + + /* Propagate events for new reachable ids downwards */ + if (table->_->traversable_count) { + int32_t i; + const ecs_entity_t *entities = ecs_table_entities(table); + entities = ECS_ELEM_T(entities, ecs_entity_t, it->offset); + for (i = 0; i < it->count; i ++) { + ecs_record_t *r = flecs_entities_get(world, entities[i]); + if (r->idr) { + break; + } + } + + if (i != it->count) { + ecs_reachable_elem_t *elems = ecs_vec_first_t(&rc->ids, + ecs_reachable_elem_t); + int32_t count = ecs_vec_count(&rc->ids); + for (i = 0; i < count; i ++) { + ecs_reachable_elem_t *elem = &elems[i]; + const ecs_table_record_t *tr = elem->tr; + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_id_record_t *rc_idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_record_t *r = elem->record; + + ecs_assert(rc_idr->id == elem->id, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_entities_get(world, elem->src) == r, + ECS_INTERNAL_ERROR, NULL); + ecs_dbg_assert(r->table == elem->table, ECS_INTERNAL_ERROR, NULL); + (void)r; + + ecs_event_id_record_t *iders[5] = {0}; + int32_t ider_count = flecs_event_observers_get( + er, rc_idr->id, iders); + + flecs_propagate_entities(world, it, rc_idr, it->entities, + it->count, elem->src, iders, ider_count); + } } } } @@ -1009,9 +1098,10 @@ void flecs_emit_forward( void flecs_emit( ecs_world_t *world, ecs_world_t *stage, + ecs_flags64_t set_mask, ecs_event_desc_t *desc) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(desc->event != 0, ECS_INVALID_PARAMETER, NULL); ecs_check(desc->event != EcsWildcard, ECS_INVALID_PARAMETER, NULL); @@ -1020,6 +1110,8 @@ void flecs_emit( ecs_check(desc->table != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(desc->observable != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_os_perf_trace_push("flecs.emit"); + ecs_time_t t = {0}; bool measure_time = world->flags & EcsWorldMeasureSystemTime; if (measure_time) { @@ -1030,13 +1122,13 @@ void flecs_emit( ecs_entity_t event = desc->event; ecs_table_t *table = desc->table, *other_table = desc->other_table; int32_t offset = desc->offset; - int32_t i, r, count = desc->count; + int32_t i, count = desc->count; ecs_flags32_t table_flags = table->flags; /* Deferring cannot be suspended for observers */ - int32_t defer = world->stages[0].defer; + int32_t defer = world->stages[0]->defer; if (defer < 0) { - world->stages[0].defer *= -1; + world->stages[0]->defer *= -1; } /* Table events are emitted for internal table operations only, and do not @@ -1047,26 +1139,28 @@ void flecs_emit( count = ecs_table_count(table) - offset; } - /* When the NoOnSet flag is provided, no OnSet/UnSet events should be - * generated when new components are inherited. */ - bool no_on_set = desc->flags & EcsEventNoOnSet; + /* The world event id is used to determine if an observer has already been + * triggered for an event. Observers for multiple components are split up + * into multiple observers for a single component, and this counter is used + * to make sure a multi observer only triggers once, even if multiple of its + * single-component observers trigger. */ + int32_t evtx = ++world->event_id; ecs_id_t ids_cache = 0; - void *ptrs_cache = NULL; ecs_size_t sizes_cache = 0; - int32_t columns_cache = 0; + const ecs_table_record_t* trs_cache = 0; ecs_entity_t sources_cache = 0; ecs_iter_t it = { .world = stage, .real_world = world, .event = event, + .event_cur = evtx, .table = table, .field_count = 1, .ids = &ids_cache, - .ptrs = &ptrs_cache, .sizes = &sizes_cache, - .columns = &columns_cache, + .trs = (const ecs_table_record_t**)&trs_cache, .sources = &sources_cache, .other_table = other_table, .offset = offset, @@ -1075,34 +1169,26 @@ void flecs_emit( .flags = desc->flags | EcsIterIsValid }; - /* The world event id is used to determine if an observer has already been - * triggered for an event. Observers for multiple components are split up - * into multiple observers for a single component, and this counter is used - * to make sure a multi observer only triggers once, even if multiple of its - * single-component observers trigger. */ - int32_t evtx = ++world->event_id; - ecs_observable_t *observable = ecs_get_observable(desc->observable); ecs_check(observable != NULL, ECS_INVALID_PARAMETER, NULL); /* Event records contain all observers for a specific event. In addition to * the emitted event, also request data for the Wildcard event (for - * observers subscribing to the wildcard event), OnSet and UnSet events. The - * latter to are used for automatically emitting OnSet/UnSet events for + * observers subscribing to the wildcard event), OnSet events. The + * latter to are used for automatically emitting OnSet events for * inherited components, for example when an IsA relationship is added to an * entity. This doesn't add much overhead, as fetching records is cheap for * builtin event types. */ const ecs_event_record_t *er = flecs_event_record_get_if(observable, event); const ecs_event_record_t *wcer = flecs_event_record_get_if(observable, EcsWildcard); const ecs_event_record_t *er_onset = flecs_event_record_get_if(observable, EcsOnSet); - const ecs_event_record_t *er_unset = flecs_event_record_get_if(observable, EcsUnSet); ecs_data_t *storage = NULL; ecs_column_t *columns = NULL; if (count) { storage = &table->data; columns = storage->columns; - it.entities = ecs_vec_get_t(&storage->entities, ecs_entity_t, offset); + it.entities = &ecs_table_entities(table)[offset]; } int32_t id_count = ids->count; @@ -1123,13 +1209,7 @@ void flecs_emit( /* Does table has observed entities */ bool has_observed = table_flags & EcsTableHasTraversable; - /* When a relationship is removed, the events reachable through that - * relationship should emit UnSet events. This is part of the behavior that - * allows observers to be agnostic of whether a component is inherited. */ - bool can_unset = count && (event == EcsOnRemove) && !no_on_set; - ecs_event_id_record_t *iders[5] = {0}; - int32_t unset_count = 0; if (count && can_forward && has_observed) { flecs_emit_propagate_invalidate(world, table, offset, count); @@ -1151,22 +1231,37 @@ void flecs_emit( ecs_id_record_t *idr = NULL; const ecs_type_info_t *ti = NULL; ecs_id_t id = id_array[i]; + ecs_assert(id == EcsAny || !ecs_id_is_wildcard(id), + ECS_INVALID_PARAMETER, "cannot emit wildcard ids"); int32_t ider_i, ider_count = 0; bool is_pair = ECS_IS_PAIR(id); void *override_ptr = NULL; + bool override_base_added = false; + ecs_table_record_t *base_tr = NULL; ecs_entity_t base = 0; + bool id_can_override = can_override; + ecs_flags64_t id_bit = 1llu << i; + if (id_bit & set_mask) { + /* Component is already set, so don't override with prefab value */ + id_can_override = false; + } /* Check if this id is a pair of an traversable relationship. If so, we * may have to forward ids from the pair's target. */ - if ((can_forward && is_pair) || can_override) { - idr = flecs_query_id_record_get(world, id); + if ((can_forward && is_pair) || id_can_override) { + idr = flecs_id_record_get(world, id); + if (!idr) { + /* Possible for union ids */ + continue; + } + ecs_flags32_t idr_flags = idr->flags; if (is_pair && (idr_flags & EcsIdTraversable)) { const ecs_event_record_t *er_fwd = NULL; if (ECS_PAIR_FIRST(id) == EcsIsA) { if (event == EcsOnAdd) { - if (!world->stages[0].base) { + if (!world->stages[0]->base) { /* Adding an IsA relationship can trigger prefab * instantiation, which can instantiate prefab * hierarchies for the entity to which the @@ -1176,41 +1271,63 @@ void flecs_emit( /* Setting this value prevents flecs_instantiate * from being called recursively, in case prefab * children also have IsA relationships. */ - world->stages[0].base = tgt; - flecs_instantiate(world, tgt, table, offset, count); - world->stages[0].base = 0; + world->stages[0]->base = tgt; + flecs_instantiate(world, tgt, table, offset, count, NULL); + world->stages[0]->base = 0; } /* Adding an IsA relationship will emit OnSet events for * any new reachable components. */ er_fwd = er_onset; - } else if (event == EcsOnRemove) { - /* Vice versa for removing an IsA relationship. */ - er_fwd = er_unset; } } /* Forward events for components from pair target */ - flecs_emit_forward(world, er, er_fwd, ids, &it, table, idr, evtx); + flecs_emit_forward(world, er, er_fwd, ids, &it, table, idr); + ecs_assert(it.event_cur == evtx, ECS_INTERNAL_ERROR, NULL); } - if (can_override && (!(idr_flags & EcsIdDontInherit))) { + if (id_can_override && !(idr_flags & EcsIdOnInstantiateDontInherit)) { /* Initialize overridden components with value from base */ ti = idr->type_info; if (ti) { - ecs_table_record_t *base_tr = NULL; int32_t base_column = ecs_search_relation(world, table, 0, id, EcsIsA, EcsUp, &base, NULL, &base_tr); if (base_column != -1) { /* Base found with component */ ecs_table_t *base_table = base_tr->hdr.table; - base_column = base_tr->column; - ecs_assert(base_column != -1, ECS_INTERNAL_ERROR, NULL); - ecs_record_t *base_r = flecs_entities_get(world, base); - ecs_assert(base_r != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t base_row = ECS_RECORD_TO_ROW(base_r->row); - ecs_vec_t *base_v = &base_table->data.columns[base_column].data; - override_ptr = ecs_vec_get(base_v, ti->size, base_row); + if (idr->flags & EcsIdIsSparse) { + override_ptr = flecs_sparse_get_any( + idr->sparse, 0, base); + } else { + base_column = base_tr->column; + ecs_assert(base_column != -1, ECS_INTERNAL_ERROR, NULL); + ecs_record_t *base_r = flecs_entities_get(world, base); + ecs_assert(base_r != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t base_row = ECS_RECORD_TO_ROW(base_r->row); + override_ptr = base_table->data.columns[base_column].data; + override_ptr = ECS_ELEM(override_ptr, ti->size, base_row); + } + + /* For ids with override policy, check if base was added + * in same operation. This will determine later on + * whether we need to emit an OnSet event. */ + if (!(idr->flags & + (EcsIdOnInstantiateInherit|EcsIdOnInstantiateDontInherit))) { + int32_t base_i; + for (base_i = 0; base_i < id_count; base_i ++) { + ecs_id_t base_id = id_array[base_i]; + if (!ECS_IS_PAIR(base_id)) { + continue; + } + if (ECS_PAIR_FIRST(base_id) != EcsIsA) { + continue; + } + if (ECS_PAIR_SECOND(base_id) == (uint32_t)base) { + override_base_added = true; + } + } + } } } } @@ -1222,16 +1339,8 @@ void flecs_emit( * example, both observers for (ChildOf, p) and (ChildOf, *) would * match an event for (ChildOf, p). */ ider_count = flecs_event_observers_get(er, id, iders); - idr = idr ? idr : flecs_query_id_record_get(world, id); - ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - } - - if (can_unset) { - /* Increase UnSet count in case this is a component (has data). This - * will cause the event loop to be ran again as UnSet event. */ - idr = idr ? idr : flecs_query_id_record_get(world, id); + idr = idr ? idr : flecs_id_record_get(world, id); ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - unset_count += (idr->type_info != NULL); } if (!ider_count && !override_ptr) { @@ -1248,30 +1357,62 @@ void flecs_emit( continue; } - int32_t column = tr->index, storage_i; - it.columns[0] = column + 1; - it.ptrs[0] = NULL; - it.sizes[0] = 0; + int32_t storage_i; + it.trs[0] = tr; + ECS_CONST_CAST(int32_t*, it.sizes)[0] = 0; /* safe, owned by observer */ it.event_id = id; it.ids[0] = id; if (count) { storage_i = tr->column; - if (storage_i != -1) { - /* If this is a component, fetch pointer & size */ - ecs_assert(idr->type_info != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_column_t *c = &columns[storage_i]; - ecs_size_t size = c->ti->size; - void *ptr = ecs_vec_get(&c->data, size, offset); - it.sizes[0] = size; + bool is_sparse = idr->flags & EcsIdIsSparse; + + if (!ecs_id_is_wildcard(id) && (storage_i != -1 || is_sparse)) { + void *ptr; + ecs_size_t size = idr->type_info->size; + + if (is_sparse) { + ecs_assert(count == 1, ECS_UNSUPPORTED, + "events for multiple entities are currently unsupported" + " for sparse components"); + ecs_entity_t e = ecs_table_entities(table)[offset]; + ptr = flecs_sparse_get(idr->sparse, 0, e); + } else{ + ecs_assert(idr->type_info != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_column_t *c = &columns[storage_i]; + ptr = ECS_ELEM(c->data, size, offset); + } + + /* Safe, owned by observer */ + ECS_CONST_CAST(int32_t*, it.sizes)[0] = size; if (override_ptr) { if (event == EcsOnAdd) { /* If this is a new override, initialize the component * with the value of the overridden component. */ - flecs_override_copy( - world, table, ti, ptr, override_ptr, offset, count); - } else if (er_onset) { + flecs_override_copy(world, table, tr, ti, ptr, + override_ptr, offset, count); + + /* If the base for this component got added in the same + * operation, generate an OnSet event as this is the + * first time this value is observed for the entity. */ + if (override_base_added) { + ecs_event_id_record_t *iders_set[5] = {0}; + int32_t ider_set_i, ider_set_count = + flecs_event_observers_get(er_onset, id, iders_set); + for (ider_set_i = 0; ider_set_i < ider_set_count; ider_set_i ++) { + ecs_event_id_record_t *ider = iders_set[ider_set_i]; + flecs_observers_invoke( + world, &ider->self, &it, table, 0); + ecs_assert(it.event_cur == evtx, + ECS_INTERNAL_ERROR, NULL); + flecs_observers_invoke( + world, &ider->self_up, &it, table, 0); + ecs_assert(it.event_cur == evtx, + ECS_INTERNAL_ERROR, NULL); + } + } + } else if (er_onset && it.other_table) { /* If an override was removed, this re-exposes the * overridden component. Because this causes the actual * (now inherited) value of the component to change, an @@ -1284,31 +1425,36 @@ void flecs_emit( /* Set the source temporarily to the base and base * component pointer. */ it.sources[0] = base; - it.ptrs[0] = ptr; + it.trs[0] = base_tr; + it.up_fields = 1; + for (ider_set_i = 0; ider_set_i < ider_set_count; ider_set_i ++) { ecs_event_id_record_t *ider = iders_set[ider_set_i]; - flecs_observers_invoke(world, &ider->self_up, &it, table, EcsIsA, evtx); - flecs_observers_invoke(world, &ider->up, &it, table, EcsIsA, evtx); + flecs_observers_invoke( + world, &ider->self_up, &it, table, EcsIsA); + ecs_assert(it.event_cur == evtx, + ECS_INTERNAL_ERROR, NULL); + flecs_observers_invoke( + world, &ider->up, &it, table, EcsIsA); + ecs_assert(it.event_cur == evtx, + ECS_INTERNAL_ERROR, NULL); } + it.sources[0] = 0; + it.trs[0] = tr; } } } - - it.ptrs[0] = ptr; - } else { - if (it.event == EcsUnSet) { - /* Only valid for components, not tags */ - continue; - } } } /* Actually invoke observers for this event/id */ for (ider_i = 0; ider_i < ider_count; ider_i ++) { ecs_event_id_record_t *ider = iders[ider_i]; - flecs_observers_invoke(world, &ider->self, &it, table, 0, evtx); - flecs_observers_invoke(world, &ider->self_up, &it, table, 0, evtx); + flecs_observers_invoke(world, &ider->self, &it, table, 0); + ecs_assert(it.event_cur == evtx, ECS_INTERNAL_ERROR, NULL); + flecs_observers_invoke(world, &ider->self_up, &it, table, 0); + ecs_assert(it.event_cur == evtx, ECS_INTERNAL_ERROR, NULL); } if (!ider_count || !count || !has_observed) { @@ -1319,47 +1465,13 @@ void flecs_emit( * entities that are used as targets of traversable relationships. If the * entity/entities for which the event was generated is used as such a * target, events must be propagated downwards. */ - ecs_entity_t *entities = it.entities; - it.entities = NULL; - - for (r = 0; r < count; r ++) { - ecs_record_t *record = flecs_entities_get(world, entities[r]); - if (!record) { - /* If the event is emitted after a bulk operation, it's possible - * that it hasn't been populated with entities yet. */ - continue; - } - - ecs_id_record_t *idr_t = record->idr; - if (idr_t) { - /* Entity is used as target in traversable pairs, propagate */ - ecs_entity_t e = entities[r]; - it.sources[0] = e; - flecs_emit_propagate( - world, &it, idr, idr_t, 0, iders, ider_count); - } - } - - it.table = table; - it.other_table = other_table; - it.entities = entities; - it.count = count; - it.offset = offset; - it.sources[0] = 0; + flecs_propagate_entities( + world, &it, idr, it.entities, count, 0, iders, ider_count); } can_override = false; /* Don't override twice */ - can_unset = false; /* Don't unset twice */ can_forward = false; /* Don't forward twice */ - if (unset_count && er_unset && (er != er_unset)) { - /* Repeat event loop for UnSet event */ - unset_count = 0; - er = er_unset; - it.event = EcsUnSet; - goto repeat_event; - } - if (wcer && er != wcer) { /* Repeat event loop for Wildcard event */ er = wcer; @@ -1368,7 +1480,9 @@ void flecs_emit( } error: - world->stages[0].defer = defer; + world->stages[0]->defer = defer; + + ecs_os_perf_trace_pop("flecs.emit"); if (measure_time) { world->info.emit_time_total += (ecs_ftime_t)ecs_time_measure(&t); @@ -1418,7 +1532,9 @@ void ecs_emit( desc->const_param = NULL; } - flecs_emit(world, stage, desc); + ecs_defer_begin(world); + flecs_emit(world, stage, 0, desc); + ecs_defer_end(world); if (desc->ids == &default_ids) { desc->ids = NULL; @@ -1431,11 +1547,11 @@ void ecs_enqueue( ecs_world_t *world, ecs_event_desc_t *desc) { - ecs_check(ecs_is_deferred(world), ECS_INVALID_PARAMETER, - "can't enqueue if not in deferred mode"); - ecs_stage_t *stage = flecs_stage_from_world(&world); + if (!ecs_is_deferred(world)) { + ecs_emit(world, desc); + return; + } + ecs_stage_t *stage = flecs_stage_from_world(&world); flecs_enqueue(world, stage, desc); -error: - return; } diff --git a/vendors/flecs/src/observable.h b/vendors/flecs/src/observable.h index 40ad5240e..120553622 100644 --- a/vendors/flecs/src/observable.h +++ b/vendors/flecs/src/observable.h @@ -6,6 +6,47 @@ #ifndef FLECS_OBSERVABLE_H #define FLECS_OBSERVABLE_H +/** All observers for a specific (component) id */ +typedef struct ecs_event_id_record_t { + /* Triggers for Self */ + ecs_map_t self; /* map */ + ecs_map_t self_up; /* map */ + ecs_map_t up; /* map */ + + ecs_map_t observers; /* map */ + + /* Triggers for SuperSet, SubSet */ + ecs_map_t set_observers; /* map */ + + /* Triggers for Self with non-This subject */ + ecs_map_t entity_observers; /* map */ + + /* Number of active observers for (component) id */ + int32_t observer_count; +} ecs_event_id_record_t; + +typedef struct ecs_observer_impl_t { + ecs_observer_t pub; + + 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) */ + int8_t term_index; /**< Index of the term in parent observer (single term observers only) */ + + ecs_flags32_t flags; /**< Observer flags */ + uint64_t id; /**< Internal id (not entity id) */ + ecs_vec_t children; /**< If multi observer, vector stores child observers */ + + ecs_query_t *not_query; /**< Query used to populate observer data when a + term with a not operator triggers. */ + + /* Mixins */ + flecs_poly_dtor_t dtor; +} ecs_observer_impl_t; + +#define flecs_observer_impl(observer) (ECS_CONST_CAST(ecs_observer_impl_t*, observer)) + ecs_event_record_t* flecs_event_record_get( const ecs_observable_t *o, ecs_entity_t event); @@ -38,15 +79,21 @@ bool flecs_observers_exist( ecs_id_t id, ecs_entity_t event); +ecs_observer_t* flecs_observer_init( + ecs_world_t *world, + ecs_entity_t entity, + const ecs_observer_desc_t *desc); + void flecs_observer_fini( ecs_observer_t *observer); void flecs_emit( ecs_world_t *world, ecs_world_t *stage, + ecs_flags64_t set_mask, ecs_event_desc_t *desc); -bool flecs_default_observer_next_callback( +bool flecs_default_next_callback( ecs_iter_t *it); void flecs_observers_invoke( @@ -54,8 +101,7 @@ void flecs_observers_invoke( ecs_map_t *observers, ecs_iter_t *it, ecs_table_t *table, - ecs_entity_t trav, - int32_t evtx); + ecs_entity_t trav); void flecs_emit_propagate_invalidate( ecs_world_t *world, @@ -63,4 +109,10 @@ void flecs_emit_propagate_invalidate( int32_t offset, int32_t count); +void flecs_observer_set_disable_bit( + ecs_world_t *world, + ecs_entity_t e, + ecs_flags32_t bit, + bool cond); + #endif diff --git a/vendors/flecs/src/observer.c b/vendors/flecs/src/observer.c index b0d23a079..44f740fb3 100644 --- a/vendors/flecs/src/observer.c +++ b/vendors/flecs/src/observer.c @@ -18,7 +18,7 @@ ecs_entity_t flecs_get_observer_event( { /* If operator is Not, reverse the event */ if (term->oper == EcsNot) { - if (event == EcsOnAdd) { + if (event == EcsOnAdd || event == EcsOnSet) { event = EcsOnRemove; } else if (event == EcsOnRemove) { event = EcsOnAdd; @@ -41,9 +41,6 @@ ecs_flags32_t flecs_id_flag_for_event( if (e == EcsOnSet) { return EcsIdHasOnSet; } - if (e == EcsUnSet) { - return EcsIdHasUnSet; - } if (e == EcsOnTableFill) { return EcsIdHasOnTableFill; } @@ -56,6 +53,11 @@ ecs_flags32_t flecs_id_flag_for_event( if (e == EcsOnTableDelete) { return EcsIdHasOnTableDelete; } + if (e == EcsWildcard) { + return EcsIdHasOnAdd|EcsIdHasOnRemove|EcsIdHasOnSet| + EcsIdHasOnTableFill|EcsIdHasOnTableEmpty| + EcsIdHasOnTableCreate|EcsIdHasOnTableDelete; + } return 0; } @@ -106,21 +108,38 @@ void flecs_inc_observer_count( } } +static +ecs_id_t flecs_observer_id( + ecs_id_t id) +{ + if (ECS_IS_PAIR(id)) { + if (ECS_PAIR_FIRST(id) == EcsAny) { + id = ecs_pair(EcsWildcard, ECS_PAIR_SECOND(id)); + } + if (ECS_PAIR_SECOND(id) == EcsAny) { + id = ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard); + } + } + + return id; +} + static void flecs_register_observer_for_id( ecs_world_t *world, ecs_observable_t *observable, - ecs_observer_t *observer, + ecs_observer_t *o, size_t offset) { - ecs_id_t term_id = observer->register_id; - ecs_term_t *term = &observer->filter.terms[0]; - ecs_entity_t trav = term->src.trav; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_id_t term_id = flecs_observer_id(impl->register_id); + ecs_term_t *term = &o->query->terms[0]; + ecs_entity_t trav = term->trav; int i; - for (i = 0; i < observer->event_count; i ++) { + for (i = 0; i < o->event_count; i ++) { ecs_entity_t event = flecs_get_observer_event( - term, observer->events[i]); + term, o->events[i]); /* Get observers for event */ ecs_event_record_t *er = flecs_event_record_ensure(observable, event); @@ -133,7 +152,7 @@ void flecs_register_observer_for_id( ecs_map_t *observers = ECS_OFFSET(idt, offset); ecs_map_init_w_params_if(observers, &world->allocators.ptr); - ecs_map_insert_ptr(observers, observer->filter.entity, observer); + ecs_map_insert_ptr(observers, impl->id, o); flecs_inc_observer_count(world, event, er, term_id, 1); if (trav) { @@ -147,20 +166,20 @@ static void flecs_uni_observer_register( ecs_world_t *world, ecs_observable_t *observable, - ecs_observer_t *observer) + ecs_observer_t *o) { - ecs_term_t *term = &observer->filter.terms[0]; - ecs_flags32_t flags = term->src.flags; + ecs_term_t *term = &o->query->terms[0]; + ecs_flags64_t flags = ECS_TERM_REF_FLAGS(&term->src); if ((flags & (EcsSelf|EcsUp)) == (EcsSelf|EcsUp)) { - flecs_register_observer_for_id(world, observable, observer, + flecs_register_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, self_up)); } else if (flags & EcsSelf) { - flecs_register_observer_for_id(world, observable, observer, + flecs_register_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, self)); } else if (flags & EcsUp) { - ecs_assert(term->src.trav != 0, ECS_INTERNAL_ERROR, NULL); - flecs_register_observer_for_id(world, observable, observer, + ecs_assert(term->trav != 0, ECS_INTERNAL_ERROR, NULL); + flecs_register_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, up)); } } @@ -169,17 +188,18 @@ static void flecs_unregister_observer_for_id( ecs_world_t *world, ecs_observable_t *observable, - ecs_observer_t *observer, + ecs_observer_t *o, size_t offset) { - ecs_id_t term_id = observer->register_id; - ecs_term_t *term = &observer->filter.terms[0]; - ecs_entity_t trav = term->src.trav; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_id_t term_id = flecs_observer_id(impl->register_id); + ecs_term_t *term = &o->query->terms[0]; + ecs_entity_t trav = term->trav; int i; - for (i = 0; i < observer->event_count; i ++) { + for (i = 0; i < o->event_count; i ++) { ecs_entity_t event = flecs_get_observer_event( - term, observer->events[i]); + term, o->events[i]); /* Get observers for event */ ecs_event_record_t *er = flecs_event_record_get(observable, event); @@ -190,7 +210,7 @@ void flecs_unregister_observer_for_id( ecs_assert(idt != NULL, ECS_INTERNAL_ERROR, NULL); ecs_map_t *id_observers = ECS_OFFSET(idt, offset); - ecs_map_remove(id_observers, observer->filter.entity); + ecs_map_remove(id_observers, impl->id); if (!ecs_map_count(id_observers)) { ecs_map_fini(id_observers); } @@ -207,101 +227,101 @@ static void flecs_unregister_observer( ecs_world_t *world, ecs_observable_t *observable, - ecs_observer_t *observer) + ecs_observer_t *o) { - ecs_assert(observer != NULL, ECS_INTERNAL_ERROR, NULL); - if (!observer->filter.terms) { - ecs_assert(observer->filter.term_count == 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(o != NULL, ECS_INTERNAL_ERROR, NULL); + if (o->query->term_count == 0) { return; } - ecs_term_t *term = &observer->filter.terms[0]; - ecs_flags32_t flags = term->src.flags; + ecs_term_t *term = &o->query->terms[0]; + ecs_flags64_t flags = ECS_TERM_REF_FLAGS(&term->src); if ((flags & (EcsSelf|EcsUp)) == (EcsSelf|EcsUp)) { - flecs_unregister_observer_for_id(world, observable, observer, + flecs_unregister_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, self_up)); } else if (flags & EcsSelf) { - flecs_unregister_observer_for_id(world, observable, observer, + flecs_unregister_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, self)); } else if (flags & EcsUp) { - flecs_unregister_observer_for_id(world, observable, observer, + flecs_unregister_observer_for_id(world, observable, o, offsetof(ecs_event_id_record_t, up)); } } static bool flecs_ignore_observer( - ecs_observer_t *observer, + ecs_observer_t *o, ecs_table_t *table, - int32_t evtx) + ecs_iter_t *it) { - ecs_assert(observer != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(o != 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] == evtx) { + ecs_observer_impl_t *impl = flecs_observer_impl(o); + int32_t *last_event_id = impl->last_event_id; + if (last_event_id && last_event_id[0] == it->event_cur) { return true; } - ecs_flags32_t table_flags = table->flags, filter_flags = observer->filter.flags; + if (impl->flags & (EcsObserverIsDisabled|EcsObserverIsParentDisabled)) { + return true; + } + + ecs_flags32_t table_flags = table->flags, query_flags = o->query->flags; bool result = (table_flags & EcsTableIsPrefab) && - !(filter_flags & EcsFilterMatchPrefab); + !(query_flags & EcsQueryMatchPrefab); result = result || ((table_flags & EcsTableIsDisabled) && - !(filter_flags & EcsFilterMatchDisabled)); + !(query_flags & EcsQueryMatchDisabled)); 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 void flecs_observer_invoke( ecs_world_t *world, ecs_iter_t *it, - ecs_observer_t *observer, + ecs_observer_t *o, ecs_iter_action_t callback, - int32_t term_index, - bool simple_result) + int32_t term_index) { ecs_assert(it->callback != NULL, ECS_INVALID_PARAMETER, NULL); if (ecs_should_log_3()) { - char *path = ecs_get_fullpath(world, it->system); + char *path = ecs_get_path(world, it->system); ecs_dbg_3("observer: invoke %s", path); ecs_os_free(path); } ecs_log_push_3(); + ecs_entity_t old_system = flecs_stage_set_system( + world->stages[0], o->entity); world->info.observers_ran_frame ++; - 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]; - if (term->oper != EcsNot) { + ecs_query_t *query = o->query; + ecs_assert(term_index < query->term_count, ECS_INTERNAL_ERROR, NULL); + ecs_term_t *term = &query->terms[term_index]; + if (it->table && (term->oper != EcsNot)) { ecs_assert((it->offset + it->count) <= ecs_table_count(it->table), ECS_INTERNAL_ERROR, NULL); } - bool instanced = filter->flags & EcsFilterIsInstanced; - bool match_this = filter->flags & EcsFilterMatchThis; - bool table_only = it->flags & EcsIterTableOnly; - if (match_this && (simple_result || instanced || table_only)) { + ecs_termset_t row_fields = it->row_fields; + it->row_fields = query->row_fields; + + bool match_this = query->flags & EcsQueryMatchThis; + if (match_this) { callback(it); + ecs_os_inc(&query->eval_count); } else { - ecs_entity_t observer_src = term->src.id; - if (observer_src && !(term->src.flags & EcsIsEntity)) { + ecs_entity_t observer_src = ECS_TERM_REF_ID(&term->src); + if (observer_src && !(term->src.id & EcsIsEntity)) { observer_src = 0; } - ecs_entity_t *entities = it->entities; + const ecs_entity_t *entities = it->entities; int32_t i, count = it->count; ecs_entity_t src = it->sources[0]; it->count = 1; @@ -310,21 +330,29 @@ void flecs_observer_invoke( it->entities = &e; if (!observer_src) { callback(it); + ecs_os_inc(&query->eval_count); } else if (observer_src == e) { ecs_entity_t dummy = 0; it->entities = &dummy; if (!src) { it->sources[0] = e; } + callback(it); + ecs_os_inc(&query->eval_count); it->sources[0] = src; break; } } + it->entities = entities; it->count = count; } + it->row_fields = row_fields; + + flecs_stage_set_system(world->stages[0], old_system); + ecs_log_pop_3(); } @@ -335,61 +363,64 @@ void flecs_default_uni_observer_run_callback(ecs_iter_t *it) { it->callback = o->callback; if (ecs_should_log_3()) { - char *path = ecs_get_fullpath(it->world, it->system); + char *path = ecs_get_path(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)); + flecs_observer_invoke(it->real_world, it, o, o->callback, 0); ecs_log_pop_3(); } static void flecs_uni_observer_invoke( ecs_world_t *world, - ecs_observer_t *observer, + ecs_observer_t *o, ecs_iter_t *it, ecs_table_t *table, - ecs_entity_t trav, - int32_t evtx, - bool simple_result) + ecs_entity_t trav) { - ecs_filter_t *filter = &observer->filter; - ecs_term_t *term = &filter->terms[0]; - if (flecs_ignore_observer(observer, table, evtx)) { + ecs_query_t *query = o->query; + ecs_term_t *term = &query->terms[0]; + if (flecs_ignore_observer(o, table, it)) { return; } ecs_assert(trav == 0 || it->sources[0] != 0, ECS_INTERNAL_ERROR, NULL); - if (trav && term->src.trav != trav) { + if (trav && term->trav != trav) { return; } + ecs_observer_impl_t *impl = flecs_observer_impl(o); bool is_filter = term->inout == EcsInOutNone; ECS_BIT_COND(it->flags, EcsIterNoData, is_filter); - it->system = observer->filter.entity; - it->ctx = observer->ctx; - it->binding_ctx = observer->binding_ctx; - it->term_index = observer->term_index; - it->terms = term; - + it->system = o->entity; + it->ctx = o->ctx; + it->callback_ctx = o->callback_ctx; + it->run_ctx = o->run_ctx; + it->term_index = impl->term_index; + it->query = query; + it->ref_fields = query->fixed_fields | query->row_fields; + it->row_fields = query->row_fields; + ecs_entity_t event = it->event; + int32_t event_cur = it->event_cur; it->event = flecs_get_observer_event(term, event); - if (observer->run) { - it->next = flecs_default_observer_next_callback; + if (o->run) { + it->next = flecs_default_next_callback; it->callback = flecs_default_uni_observer_run_callback; - it->ctx = observer; - observer->run(it); + it->ctx = o; + o->run(it); } else { - ecs_iter_action_t callback = observer->callback; + ecs_iter_action_t callback = o->callback; it->callback = callback; - flecs_observer_invoke(world, it, observer, callback, 0, simple_result); + flecs_observer_invoke(world, it, o, callback, 0); } it->event = event; + it->event_cur = event_cur; } void flecs_observers_invoke( @@ -397,18 +428,16 @@ void flecs_observers_invoke( ecs_map_t *observers, ecs_iter_t *it, ecs_table_t *table, - ecs_entity_t trav, - int32_t evtx) + 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, evtx, simple_result); + flecs_uni_observer_invoke(world, o, it, table, trav); } ecs_table_unlock(it->world, table); @@ -416,135 +445,163 @@ void flecs_observers_invoke( } static -bool flecs_multi_observer_invoke(ecs_iter_t *it) { +void flecs_multi_observer_invoke( + ecs_iter_t *it) +{ ecs_observer_t *o = it->ctx; - ecs_world_t *world = it->real_world; + flecs_poly_assert(o, ecs_observer_t); - if (o->last_event_id[0] == world->event_id) { + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_world_t *world = it->real_world; + + if (impl->last_event_id[0] == it->event_cur) { /* Already handled this event */ - return false; + return; } - o->last_event_id[0] = world->event_id; - - ecs_iter_t user_it = *it; - user_it.field_count = o->filter.field_count; - user_it.terms = o->filter.terms; - user_it.flags = 0; - ECS_BIT_COND(user_it.flags, EcsIterNoData, - ECS_BIT_IS_SET(o->filter.flags, EcsFilterNoData)); - user_it.ids = NULL; - user_it.columns = NULL; - user_it.sources = NULL; - user_it.sizes = NULL; - user_it.ptrs = NULL; - - flecs_iter_init(it->world, &user_it, flecs_iter_cache_all); - user_it.flags |= (it->flags & EcsIterTableOnly); + impl->last_event_id[0] = world->event_id; ecs_table_t *table = it->table; ecs_table_t *prev_table = it->other_table; - int32_t pivot_term = it->term_index; - ecs_term_t *term = &o->filter.terms[pivot_term]; + int8_t pivot_term = it->term_index; + ecs_term_t *term = &o->query->terms[pivot_term]; - int32_t column = it->columns[0]; - if (term->oper == EcsNot) { + bool is_not = term->oper == EcsNot; + if (is_not) { table = it->other_table; prev_table = it->table; } - if (!table) { - table = &world->store.root; - } - if (!prev_table) { - prev_table = &world->store.root; - } + table = table ? table : &world->store.root; + prev_table = prev_table ? prev_table : &world->store.root; - if (column < 0) { - column = -column; - } + ecs_iter_t user_it; + + bool match; + if (is_not) { + match = ecs_query_has_table(o->query, table, &user_it); + if (match) { + /* The target table matches but the entity hasn't moved to it yet. + * Now match the not_query, which will populate the iterator with + * data from the table the entity is still stored in. */ + ecs_iter_fini(&user_it); + match = ecs_query_has_table(impl->not_query, prev_table, &user_it); + + /* A not query replaces Not terms with Optional terms, so if the + * regular query matches, the not_query should also match. */ + ecs_assert(match, ECS_INTERNAL_ERROR, NULL); + } + } else { + ecs_table_range_t range = { + .table = table, + .offset = it->offset, + .count = it->count + }; - user_it.columns[0] = 0; - user_it.columns[pivot_term] = column; - user_it.sources[pivot_term] = it->sources[0]; - user_it.sizes = o->filter.sizes; + match = ecs_query_has_range(o->query, &range, &user_it); + } - if (flecs_filter_match_table(world, &o->filter, table, user_it.ids, - user_it.columns, user_it.sources, NULL, NULL, false, pivot_term, - user_it.flags)) - { - /* Monitor observers only invoke when the filter matches for the first + if (match) { + /* Monitor observers only invoke when the query matches for the first * time with an entity */ - if (o->is_monitor) { - if (flecs_filter_match_table(world, &o->filter, prev_table, - NULL, NULL, NULL, NULL, NULL, true, -1, user_it.flags)) - { + if (impl->flags & EcsObserverIsMonitor) { + ecs_iter_t table_it; + if (ecs_query_has_table(o->query, prev_table, &table_it)) { + ecs_iter_fini(&table_it); + ecs_iter_fini(&user_it); goto done; } } - /* While filter matching needs to be reversed for a Not term, the - * component data must be fetched from the table we got notified for. - * Repeat the matching process for the non-matching table so we get the - * correct column ids and sources, which we need for populate_data */ - if (term->oper == EcsNot) { - flecs_filter_match_table(world, &o->filter, prev_table, user_it.ids, - user_it.columns, user_it.sources, NULL, NULL, false, -1, - user_it.flags | EcsFilterPopulate); - } - - flecs_iter_populate_data(world, &user_it, it->table, it->offset, - it->count, user_it.ptrs); - - user_it.ptrs[pivot_term] = it->ptrs[0]; - user_it.ids[pivot_term] = it->event_id; - user_it.system = o->filter.entity; + /* Patch data from original iterator. If the observer query has + * wildcards which triggered the original event, the component id that + * got matched by ecs_query_has_range may not be the same as the one + * that caused the event. We need to make sure to communicate the + * component id that actually triggered the observer. */ + int8_t pivot_field = term->field_index; + ecs_assert(pivot_field >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(pivot_field < user_it.field_count, ECS_INTERNAL_ERROR, NULL); + user_it.ids[pivot_field] = it->event_id; + user_it.trs[pivot_field] = it->trs[0]; user_it.term_index = pivot_term; + user_it.ctx = o->ctx; - user_it.binding_ctx = o->binding_ctx; - user_it.field_count = o->filter.field_count; + user_it.callback_ctx = o->callback_ctx; + user_it.run_ctx = o->run_ctx; + user_it.param = it->param; user_it.callback = o->callback; - - flecs_iter_validate(&user_it); + user_it.system = o->entity; + user_it.event = it->event; + user_it.event_id = it->event_id; + user_it.other_table = it->other_table; + + ecs_entity_t old_system = flecs_stage_set_system( + world->stages[0], o->entity); 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); + + if (o->run) { + user_it.next = flecs_default_next_callback; + o->run(&user_it); + } else { + user_it.callback(&user_it); + } + ecs_iter_fini(&user_it); - return true; + + ecs_table_unlock(it->world, table); + flecs_stage_set_system(world->stages[0], old_system); + } else { + /* While the observer query was strictly speaking evaluated, it's more + * useful to measure how often the observer was actually invoked. */ + o->query->eval_count --; } done: - ecs_iter_fini(&user_it); - return false; + return; } -bool ecs_observer_default_run_action(ecs_iter_t *it) { +static +void flecs_multi_observer_invoke_no_query( + ecs_iter_t *it) +{ ecs_observer_t *o = it->ctx; - if (o->is_multi) { - return flecs_multi_observer_invoke(it); + flecs_poly_assert(o, ecs_observer_t); + + ecs_world_t *world = it->real_world; + ecs_table_t *table = it->table; + ecs_iter_t user_it = *it; + + user_it.ctx = o->ctx; + user_it.callback_ctx = o->callback_ctx; + user_it.run_ctx = o->run_ctx; + user_it.param = it->param; + user_it.callback = o->callback; + user_it.system = o->entity; + user_it.event = it->event; + + ecs_entity_t old_system = flecs_stage_set_system( + world->stages[0], o->entity); + ecs_table_lock(it->world, table); + + if (o->run) { + user_it.next = flecs_default_next_callback; + o->run(&user_it); } else { - it->ctx = o->ctx; - 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; + user_it.callback(&user_it); } -} -static -void flecs_default_multi_observer_run_callback(ecs_iter_t *it) { - flecs_multi_observer_invoke(it); + ecs_table_unlock(it->world, table); + flecs_stage_set_system(world->stages[0], old_system); } /* 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) { +bool flecs_default_next_callback(ecs_iter_t *it) { if (it->interrupted_by) { return false; } else { /* Use interrupted_by to signal the next iteration must return false */ + ecs_assert(it->system != 0, ECS_INTERNAL_ERROR, NULL); it->interrupted_by = it->system; return true; } @@ -553,101 +610,71 @@ bool flecs_default_observer_next_callback(ecs_iter_t *it) { /* Run action for children of multi observer */ static void flecs_multi_observer_builtin_run(ecs_iter_t *it) { - ecs_observer_t *observer = it->ctx; - ecs_run_action_t run = observer->run; + ecs_observer_t *o = it->ctx; + ecs_run_action_t run = o->run; if (run) { - it->next = flecs_default_observer_next_callback; - it->callback = flecs_default_multi_observer_run_callback; - it->interrupted_by = 0; - run(it); - } else { - flecs_multi_observer_invoke(it); - } -} - -static -void flecs_uni_observer_yield_existing( - ecs_world_t *world, - ecs_observer_t *observer) -{ - ecs_iter_action_t callback = observer->callback; - - ecs_defer_begin(world); - - /* If yield existing is enabled, observer for each thing that matches - * the event, if the event is iterable. */ - int i, count = observer->event_count; - for (i = 0; i < count; i ++) { - ecs_entity_t evt = observer->events[i]; - const EcsIterable *iterable = ecs_get(world, evt, EcsIterable); - if (!iterable) { - continue; - } - - ecs_iter_t it; - iterable->init(world, world, &it, &observer->filter.terms[0]); - it.system = observer->filter.entity; - it.ctx = observer->ctx; - it.binding_ctx = observer->binding_ctx; - it.event = evt; - - ecs_iter_next_action_t next = it.next; - ecs_assert(next != NULL, ECS_INTERNAL_ERROR, NULL); - while (next(&it)) { - it.event_id = it.ids[0]; - callback(&it); + if (flecs_observer_impl(o)->flags & EcsObserverBypassQuery) { + it->next = flecs_default_next_callback; + it->callback = flecs_multi_observer_invoke; + it->interrupted_by = 0; + it->run_ctx = o->run_ctx; + run(it); + return; } } - ecs_defer_end(world); + flecs_multi_observer_invoke(it); } static -void flecs_multi_observer_yield_existing( +void flecs_observer_yield_existing( ecs_world_t *world, - ecs_observer_t *observer) + ecs_observer_t *o, + bool yield_on_remove) { - ecs_run_action_t run = observer->run; + ecs_run_action_t run = o->run; if (!run) { - run = flecs_default_multi_observer_run_callback; + run = flecs_multi_observer_invoke_no_query; } ecs_run_aperiodic(world, EcsAperiodicEmptyTables); ecs_defer_begin(world); - int32_t pivot_term = ecs_filter_pivot_term(world, &observer->filter); - if (pivot_term < 0) { - return; - } - /* If yield existing is enabled, invoke for each thing that matches * the event, if the event is iterable. */ - int i, count = observer->event_count; + int i, count = o->event_count; for (i = 0; i < count; i ++) { - ecs_entity_t evt = observer->events[i]; - const EcsIterable *iterable = ecs_get(world, evt, EcsIterable); - if (!iterable) { - continue; + ecs_entity_t event = o->events[i]; + + /* We only yield for OnRemove events if the observer is deleted. */ + if (event == EcsOnRemove) { + if (!yield_on_remove) { + continue; + } + } else { + if (yield_on_remove) { + continue; + } } - ecs_iter_t it; - iterable->init(world, world, &it, &observer->filter.terms[pivot_term]); - it.terms = observer->filter.terms; - it.field_count = 1; - it.term_index = pivot_term; - it.system = observer->filter.entity; - it.ctx = observer; - it.binding_ctx = observer->binding_ctx; - it.event = evt; - - ecs_iter_next_action_t next = it.next; - ecs_assert(next != NULL, ECS_INTERNAL_ERROR, NULL); - while (next(&it)) { + ecs_iter_t it = ecs_query_iter(world, o->query); + it.system = o->entity; + it.ctx = o; + it.callback = flecs_default_uni_observer_run_callback; + it.callback_ctx = o->callback_ctx; + it.run_ctx = o->run_ctx; + it.event = o->events[i]; + while (ecs_query_next(&it)) { it.event_id = it.ids[0]; + it.event_cur = ++ world->event_id; + + ecs_iter_next_action_t next = it.next; + it.next = flecs_default_next_callback; run(&it); - world->event_id ++; + it.next = next; + it.interrupted_by = 0; } } @@ -657,106 +684,183 @@ void flecs_multi_observer_yield_existing( static int flecs_uni_observer_init( ecs_world_t *world, - ecs_observer_t *observer, + ecs_observer_t *o, const ecs_observer_desc_t *desc) { - 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; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_term_t *term = &o->query->terms[0]; + impl->last_event_id = desc->last_event_id; + if (!impl->last_event_id) { + impl->last_event_id = &impl->last_event_id_storage; } - observer->register_id = flecs_from_public_id(world, term->id); - term->field_index = desc->term_index; + impl->register_id = term->id; + term->field_index = flecs_ito(int8_t, desc->term_index_); if (ecs_id_is_tag(world, term->id)) { - /* If id is a tag, downgrade OnSet/UnSet to OnAdd/OnRemove. */ - int32_t e, count = observer->event_count; + /* If id is a tag, downgrade OnSet to OnAdd. */ + int32_t e, count = o->event_count; + bool has_on_add = false; for (e = 0; e < count; e ++) { - if (observer->events[e] == EcsOnSet) { - observer->events[e] = EcsOnAdd; - } else - if (observer->events[e] == EcsUnSet) { - observer->events[e] = EcsOnRemove; + if (o->events[e] == EcsOnAdd) { + has_on_add = true; + } + } + + for (e = 0; e < count; e ++) { + if (o->events[e] == EcsOnSet) { + if (has_on_add) { + /* Already registered */ + o->events[e] = 0; + } else { + o->events[e] = EcsOnAdd; + } } } } - flecs_uni_observer_register(world, observer->observable, observer); + flecs_uni_observer_register(world, o->observable, o); + + return 0; +} + +static +int flecs_observer_add_child( + ecs_world_t *world, + ecs_observer_t *o, + const ecs_observer_desc_t *child_desc) +{ + ecs_assert(child_desc->query.flags & EcsQueryNested, + ECS_INTERNAL_ERROR, NULL); - if (desc->yield_existing) { - flecs_uni_observer_yield_existing(world, observer); + ecs_observer_t *child_observer = flecs_observer_init( + world, 0, child_desc); + if (!child_observer) { + return -1; } + ecs_observer_impl_t *impl = flecs_observer_impl(o); + ecs_vec_append_t(&world->allocator, &impl->children, + ecs_observer_t*)[0] = child_observer; + child_observer->entity = o->entity; return 0; } static int flecs_multi_observer_init( ecs_world_t *world, - ecs_observer_t *observer, + ecs_observer_t *o, const ecs_observer_desc_t *desc) { + ecs_observer_impl_t *impl = flecs_observer_impl(o); + /* Create last event id for filtering out the same event that arrives from * more than one term */ - observer->last_event_id = ecs_os_calloc_t(int32_t); + impl->last_event_id = ecs_os_calloc_t(int32_t); /* Mark observer as multi observer */ - observer->is_multi = true; + impl->flags |= EcsObserverIsMulti; + + /* Vector that stores a single-component observer for each query term */ + ecs_vec_init_t(&world->allocator, &impl->children, ecs_observer_t*, 2); - /* Create a child observer for each term in the filter */ - ecs_filter_t *filter = &observer->filter; + /* Create a child observer for each term in the query */ + ecs_query_t *query = o->query; ecs_observer_desc_t child_desc = *desc; - child_desc.last_event_id = observer->last_event_id; + child_desc.last_event_id = impl->last_event_id; child_desc.run = NULL; child_desc.callback = flecs_multi_observer_builtin_run; - child_desc.ctx = observer; + child_desc.ctx = o; child_desc.ctx_free = NULL; - child_desc.filter.expr = NULL; - child_desc.filter.terms_buffer = NULL; - child_desc.filter.terms_buffer_count = 0; - child_desc.binding_ctx = NULL; - child_desc.binding_ctx_free = NULL; + child_desc.query.expr = NULL; + child_desc.callback_ctx = NULL; + child_desc.callback_ctx_free = NULL; + child_desc.run_ctx = NULL; + child_desc.run_ctx_free = NULL; child_desc.yield_existing = false; + child_desc.flags_ &= ~(EcsObserverYieldOnCreate|EcsObserverYieldOnDelete); ecs_os_zeromem(&child_desc.entity); - ecs_os_zeromem(&child_desc.filter.terms); - ecs_os_memcpy_n(child_desc.events, observer->events, - ecs_entity_t, observer->event_count); + ecs_os_zeromem(&child_desc.query.terms); + ecs_os_zeromem(&child_desc.query); + ecs_os_memcpy_n(child_desc.events, o->events, ecs_entity_t, o->event_count); - int i, term_count = filter->term_count; - bool optional_only = filter->flags & EcsFilterMatchThis; + child_desc.query.flags |= EcsQueryNested; + + int i, term_count = query->term_count; + bool optional_only = query->flags & EcsQueryMatchThis; + bool has_not = false; for (i = 0; i < term_count; i ++) { - if (filter->terms[i].oper != EcsOptional) { - if (ecs_term_match_this(&filter->terms[i])) { + if (query->terms[i].oper != EcsOptional) { + if (ecs_term_match_this(&query->terms[i])) { optional_only = false; } } + + if ((query->terms[i].oper == EcsNot) && + (query->terms[i].inout != EcsInOutFilter)) + { + has_not = true; + } } - if (filter->flags & EcsFilterMatchPrefab) { - child_desc.filter.flags |= EcsFilterMatchPrefab; + /* If an observer is only interested in table events, we only need to + * observe a single component, as each table event will be emitted for all + * components of the source table. */ + bool only_table_events = true; + for (i = 0; i < o->event_count; i ++) { + ecs_entity_t e = o->events[i]; + if (e != EcsOnTableCreate && e != EcsOnTableDelete && + e != EcsOnTableEmpty && e != EcsOnTableFill) + { + only_table_events = false; + break; + } } - if (filter->flags & EcsFilterMatchDisabled) { - child_desc.filter.flags |= EcsFilterMatchDisabled; + + if (query->flags & EcsQueryMatchPrefab) { + child_desc.query.flags |= EcsQueryMatchPrefab; } - /* Create observers as children of observer */ - ecs_entity_t old_scope = ecs_set_scope(world, observer->filter.entity); + if (query->flags & EcsQueryMatchDisabled) { + child_desc.query.flags |= EcsQueryMatchDisabled; + } + bool self_term_handled = false; for (i = 0; i < term_count; i ++) { - if (filter->terms[i].src.flags & EcsFilter) { + if (query->terms[i].inout == EcsInOutFilter) { continue; } - ecs_term_t *term = &child_desc.filter.terms[0]; - child_desc.term_index = filter->terms[i].field_index; - *term = filter->terms[i]; + ecs_term_t *term = &child_desc.query.terms[0]; + child_desc.term_index_ = query->terms[i].field_index; + *term = query->terms[i]; - ecs_oper_kind_t oper = term->oper; + int16_t oper = term->oper; ecs_id_t id = term->id; + if (only_table_events) { + /* For table event observers, only observe a single $this|self + * term. Make sure to create observers for non-self terms, as those + * require event propagation. */ + if (ecs_term_match_this(term) && + (term->src.id & EcsTraverseFlags) == EcsSelf) + { + if (oper == EcsAnd) { + if (!self_term_handled) { + self_term_handled = true; + } else { + continue; + } + } + } + } + /* AndFrom & OrFrom terms insert multiple observers */ if (oper == EcsAndFrom || oper == EcsOrFrom) { const ecs_type_t *type = ecs_get_type(world, id); + if (!type) { + continue; + } + int32_t ti, ti_count = type->count; ecs_id_t *ti_ids = type->array; @@ -766,7 +870,7 @@ int flecs_multi_observer_init( for (ti = 0; ti < ti_count; ti ++) { ecs_id_t ti_id = ti_ids[ti]; ecs_id_record_t *idr = flecs_id_record_get(world, ti_id); - if (idr->flags & EcsIdDontInherit) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { continue; } @@ -774,7 +878,7 @@ int flecs_multi_observer_init( term->first.id = ti_ids[ti]; term->id = ti_ids[ti]; - if (ecs_observer_init(world, &child_desc) == 0) { + if (flecs_observer_add_child(world, o, &child_desc)) { goto error; } } @@ -790,13 +894,16 @@ int flecs_multi_observer_init( if (optional_only) { term->id = EcsAny; term->first.id = EcsAny; - term->src.id = EcsThis; - term->src.flags = EcsIsVariable; + term->src.id = EcsThis | EcsIsVariable | EcsSelf; term->second.id = 0; } else if (term->oper == EcsOptional) { - continue; + if (only_table_events) { + /* For table events optional terms aren't necessary */ + continue; + } } - if (ecs_observer_init(world, &child_desc) == 0) { + + if (flecs_observer_add_child(world, o, &child_desc)) { goto error; } @@ -805,10 +912,25 @@ int flecs_multi_observer_init( } } - ecs_set_scope(world, old_scope); + /* If observer has Not terms, we need to create a query that replaces Not + * with Optional which we can use to populate the observer data for the + * table that the entity moved away from (or to, if it's an OnRemove + * observer). */ + if (has_not) { + ecs_query_desc_t not_desc = desc->query; + not_desc.expr = NULL; + + ecs_os_memcpy_n(not_desc.terms, o->query->terms, + ecs_term_t, term_count); /* cast suppresses warning */ + + for (i = 0; i < term_count; i ++) { + if (not_desc.terms[i].oper == EcsNot) { + not_desc.terms[i].oper = EcsOptional; + } + } - if (desc->yield_existing) { - flecs_multi_observer_yield_existing(world, observer); + flecs_observer_impl(o)->not_query = + ecs_query_init(world, ¬_desc); } return 0; @@ -816,212 +938,333 @@ int flecs_multi_observer_init( return -1; } -ecs_entity_t ecs_observer_init( +static +void flecs_observer_poly_fini(void *ptr) { + flecs_observer_fini(ptr); +} + +ecs_observer_t* flecs_observer_init( ecs_world_t *world, + ecs_entity_t entity, const ecs_observer_desc_t *desc) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, NULL); - - ecs_entity_t entity = desc->entity; - if (!entity) { - entity = ecs_new(world, 0); + ecs_assert(flecs_poly_is(world, ecs_world_t), + ECS_INTERNAL_ERROR, NULL); + ecs_check(desc->callback != NULL || desc->run != NULL, + ECS_INVALID_OPERATION, + "cannot create observer: must at least specify callback or run"); + + ecs_observer_impl_t *impl = flecs_sparse_add_t( + &world->store.observers, ecs_observer_impl_t); + ecs_assert(impl != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_init(impl, ecs_observer_t); + ecs_observer_t *o = &impl->pub; + impl->id = flecs_sparse_last_id(&world->store.observers); + impl->dtor = flecs_observer_poly_fini; + + /* Make writeable copy of query desc so that we can set name. This will + * make debugging easier, as any error messages related to creating the + * query will have the name of the observer. */ + ecs_query_desc_t query_desc = desc->query; + query_desc.entity = 0; + query_desc.cache_kind = EcsQueryCacheNone; + + /* Create query */ + ecs_query_t *query = o->query = ecs_query_init( + world, &query_desc); + if (query == NULL) { + flecs_observer_fini(o); + return 0; } - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_observer_t); - if (!poly->poly) { - ecs_check(desc->callback != NULL || desc->run != NULL, - ECS_INVALID_OPERATION, NULL); - - ecs_observer_t *observer = ecs_poly_new(ecs_observer_t); - ecs_assert(observer != NULL, ECS_INTERNAL_ERROR, NULL); - observer->dtor = (ecs_poly_dtor_t)flecs_observer_fini; - - /* Make writeable copy of filter desc so that we can set name. This will - * make debugging easier, as any error messages related to creating the - * filter will have the name of the observer. */ - ecs_filter_desc_t filter_desc = desc->filter; - filter_desc.entity = entity; - ecs_filter_t *filter = filter_desc.storage = &observer->filter; - *filter = ECS_FILTER_INIT; - - /* Parse filter */ - if (ecs_filter_init(world, &filter_desc) == NULL) { - flecs_observer_fini(observer); - return 0; - } + flecs_poly_assert(query, ecs_query_t); - /* Observer must have at least one term */ - ecs_check(observer->filter.term_count > 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(o->query->term_count > 0, ECS_INVALID_PARAMETER, + "observer must have at least one term"); - poly->poly = observer; + ecs_observable_t *observable = desc->observable; + if (!observable) { + observable = ecs_get_observable(world); + } - ecs_observable_t *observable = desc->observable; - if (!observable) { - observable = ecs_get_observable(world); + o->run = desc->run; + o->callback = desc->callback; + o->ctx = desc->ctx; + o->callback_ctx = desc->callback_ctx; + o->run_ctx = desc->run_ctx; + o->ctx_free = desc->ctx_free; + o->callback_ctx_free = desc->callback_ctx_free; + o->run_ctx_free = desc->run_ctx_free; + o->observable = observable; + o->entity = entity; + impl->term_index = desc->term_index_; + impl->flags = desc->flags_; + + ecs_check(!(desc->yield_existing && + (desc->flags_ & (EcsObserverYieldOnCreate|EcsObserverYieldOnDelete))), + ECS_INVALID_PARAMETER, + "cannot set yield_existing and YieldOn* flags at the same time"); + + /* Check if observer is monitor. Monitors are created as multi observers + * since they require pre/post checking of the filter to test if the + * entity is entering/leaving the monitor. */ + int i; + for (i = 0; i < FLECS_EVENT_DESC_MAX; i ++) { + ecs_entity_t event = desc->events[i]; + if (!event) { + break; } - observer->run = desc->run; - observer->callback = desc->callback; - observer->ctx = desc->ctx; - observer->binding_ctx = desc->binding_ctx; - observer->ctx_free = desc->ctx_free; - observer->binding_ctx_free = desc->binding_ctx_free; - observer->term_index = desc->term_index; - observer->observable = observable; - - /* Check if observer is monitor. Monitors are created as multi observers - * since they require pre/post checking of the filter to test if the - * entity is entering/leaving the monitor. */ - int i; - for (i = 0; i < FLECS_EVENT_DESC_MAX; i ++) { - ecs_entity_t event = desc->events[i]; - if (!event) { - break; + if (event == EcsMonitor) { + ecs_check(i == 0, ECS_INVALID_PARAMETER, + "monitor observers can only have a single Monitor event"); + + o->events[0] = EcsOnAdd; + o->events[1] = EcsOnRemove; + o->event_count ++; + impl->flags |= EcsObserverIsMonitor; + if (desc->yield_existing) { + impl->flags |= EcsObserverYieldOnCreate; + impl->flags |= EcsObserverYieldOnDelete; + } + } else { + o->events[i] = event; + if (desc->yield_existing) { + if (event == EcsOnRemove) { + impl->flags |= EcsObserverYieldOnDelete; + } else { + impl->flags |= EcsObserverYieldOnCreate; + } } + } + + o->event_count ++; + } - if (event == EcsMonitor) { - /* Monitor event must be first and last event */ - ecs_check(i == 0, ECS_INVALID_PARAMETER, NULL); + /* Observer must have at least one event */ + ecs_check(o->event_count != 0, ECS_INVALID_PARAMETER, + "observer must have at least one event"); - observer->events[0] = EcsOnAdd; - observer->events[1] = EcsOnRemove; - observer->event_count ++; - observer->is_monitor = true; - } else { - observer->events[i] = event; - } + bool multi = false; + + if (query->term_count == 1 && !desc->last_event_id) { + ecs_term_t *term = &query->terms[0]; + /* If the query has a single term but it is a *From operator, we + * need to create a multi observer */ + multi |= (term->oper == EcsAndFrom) || (term->oper == EcsOrFrom); + + /* An observer with only optional terms is a special case that is + * only handled by multi observers */ + multi |= term->oper == EcsOptional; + } - observer->event_count ++; + bool is_monitor = impl->flags & EcsObserverIsMonitor; + if (query->term_count == 1 && !is_monitor && !multi) { + if (flecs_uni_observer_init(world, o, desc)) { + goto error; + } + } else { + if (flecs_multi_observer_init(world, o, desc)) { + goto error; } + } - /* Observer must have at least one event */ - ecs_check(observer->event_count != 0, ECS_INVALID_PARAMETER, NULL); + if (impl->flags & EcsObserverYieldOnCreate) { + flecs_observer_yield_existing(world, o, false); + } - bool multi = false; + return o; +error: + return NULL; +} - if (filter->term_count == 1 && !desc->last_event_id) { - ecs_term_t *term = &filter->terms[0]; - /* If the filter has a single term but it is a *From operator, we - * need to create a multi observer */ - multi |= (term->oper == EcsAndFrom) || (term->oper == EcsOrFrom); - - /* An observer with only optional terms is a special case that is - * only handled by multi observers */ - multi |= term->oper == EcsOptional; - } +ecs_entity_t ecs_observer_init( + ecs_world_t *world, + const ecs_observer_desc_t *desc) +{ + ecs_entity_t entity = 0; + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_observer_desc_t was not initialized to zero"); + ecs_check(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, + "cannot create observer while world is being deleted"); - if (filter->term_count == 1 && !observer->is_monitor && !multi) { - if (flecs_uni_observer_init(world, observer, desc)) { - goto error; - } - } else { - if (flecs_multi_observer_init(world, observer, desc)) { - goto error; - } - } + entity = desc->entity; + if (!entity) { + entity = ecs_entity(world, {0}); + } + + EcsPoly *poly = flecs_poly_bind(world, entity, ecs_observer_t); + if (!poly->poly) { + ecs_observer_t *o = flecs_observer_init(world, entity, desc); + ecs_assert(o->entity == entity, ECS_INTERNAL_ERROR, NULL); + poly->poly = o; if (ecs_get_name(world, entity)) { ecs_trace("#[green]observer#[reset] %s created", ecs_get_name(world, entity)); } } else { - ecs_poly_assert(poly->poly, ecs_observer_t); - ecs_observer_t *observer = (ecs_observer_t*)poly->poly; + flecs_poly_assert(poly->poly, ecs_observer_t); + ecs_observer_t *o = (ecs_observer_t*)poly->poly; - if (desc->run) { - observer->run = desc->run; + if (o->ctx_free) { + if (o->ctx && o->ctx != desc->ctx) { + o->ctx_free(o->ctx); + } } - if (desc->callback) { - observer->callback = desc->callback; + + if (o->callback_ctx_free) { + if (o->callback_ctx && o->callback_ctx != desc->callback_ctx) { + o->callback_ctx_free(o->callback_ctx); + o->callback_ctx_free = NULL; + o->callback_ctx = NULL; + } } - if (observer->ctx_free) { - if (observer->ctx && observer->ctx != desc->ctx) { - observer->ctx_free(observer->ctx); + if (o->run_ctx_free) { + if (o->run_ctx && o->run_ctx != desc->run_ctx) { + o->run_ctx_free(o->run_ctx); + o->run_ctx_free = NULL; + o->run_ctx = NULL; } } - if (observer->binding_ctx_free) { - if (observer->binding_ctx && observer->binding_ctx != desc->binding_ctx) { - observer->binding_ctx_free(observer->binding_ctx); + + if (desc->run) { + o->run = desc->run; + if (!desc->callback) { + o->callback = NULL; + } + } + + if (desc->callback) { + o->callback = desc->callback; + if (!desc->run) { + o->run = NULL; } } if (desc->ctx) { - observer->ctx = desc->ctx; + o->ctx = desc->ctx; + } + + if (desc->callback_ctx) { + o->callback_ctx = desc->callback_ctx; } - if (desc->binding_ctx) { - observer->binding_ctx = desc->binding_ctx; + + if (desc->run_ctx) { + o->run_ctx = desc->run_ctx; } + if (desc->ctx_free) { - observer->ctx_free = desc->ctx_free; + o->ctx_free = desc->ctx_free; } - if (desc->binding_ctx_free) { - observer->binding_ctx_free = desc->binding_ctx_free; + + if (desc->callback_ctx_free) { + o->callback_ctx_free = desc->callback_ctx_free; + } + + if (desc->run_ctx_free) { + o->run_ctx_free = desc->run_ctx_free; } } - ecs_poly_modified(world, entity, ecs_observer_t); + flecs_poly_modified(world, entity, ecs_observer_t); return entity; error: - ecs_delete(world, entity); + if (entity) { + ecs_delete(world, entity); + } return 0; } -void* ecs_observer_get_ctx( +const ecs_observer_t* ecs_observer_get( const ecs_world_t *world, ecs_entity_t observer) { - const EcsPoly *o = ecs_poly_bind_get(world, observer, ecs_observer_t); - if (o) { - ecs_poly_assert(o->poly, ecs_observer_t); - return ((ecs_observer_t*)o->poly)->ctx; - } else { - return NULL; - } -} - -void* ecs_observer_get_binding_ctx( - const ecs_world_t *world, - ecs_entity_t observer) -{ - const EcsPoly *o = ecs_poly_bind_get(world, observer, ecs_observer_t); - if (o) { - ecs_poly_assert(o->poly, ecs_observer_t); - return ((ecs_observer_t*)o->poly)->binding_ctx; - } else { - return NULL; - } + return flecs_poly_get(world, observer, ecs_observer_t); } void flecs_observer_fini( - ecs_observer_t *observer) + ecs_observer_t *o) { - if (observer->is_multi) { - ecs_os_free(observer->last_event_id); + ecs_assert(o != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(o->query != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_world_t *world = o->query->world; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + + if (impl->flags & EcsObserverYieldOnDelete) { + flecs_observer_yield_existing(world, o, true); + } + + if (impl->flags & EcsObserverIsMulti) { + ecs_observer_t **children = ecs_vec_first(&impl->children); + int32_t i, children_count = ecs_vec_count(&impl->children); + + for (i = 0; i < children_count; i ++) { + flecs_observer_fini(children[i]); + } + + ecs_os_free(impl->last_event_id); } else { - if (observer->filter.term_count) { - flecs_unregister_observer( - observer->filter.world, observer->observable, observer); + if (o->query->term_count) { + flecs_unregister_observer(world, o->observable, o); } else { - /* Observer creation failed while creating filter */ + /* Observer creation failed while creating query */ } } - /* Cleanup filters */ - ecs_filter_fini(&observer->filter); + ecs_vec_fini_t(&world->allocator, &impl->children, ecs_observer_t*); + + /* Cleanup queries */ + ecs_query_fini(o->query); + if (impl->not_query) { + ecs_query_fini(impl->not_query); + } /* Cleanup context */ - if (observer->ctx_free) { - observer->ctx_free(observer->ctx); + if (o->ctx_free) { + o->ctx_free(o->ctx); + } + + if (o->callback_ctx_free) { + o->callback_ctx_free(o->callback_ctx); } - if (observer->binding_ctx_free) { - observer->binding_ctx_free(observer->binding_ctx); + if (o->run_ctx_free) { + o->run_ctx_free(o->run_ctx); + } + + flecs_poly_fini(o, ecs_observer_t); + flecs_sparse_remove_t( + &world->store.observers, ecs_observer_impl_t, impl->id); +} + +void flecs_observer_set_disable_bit( + ecs_world_t *world, + ecs_entity_t e, + ecs_flags32_t bit, + bool cond) +{ + const EcsPoly *poly = ecs_get_pair(world, e, EcsPoly, EcsObserver); + if (!poly || !poly->poly) { + return; } - ecs_poly_free(observer, ecs_observer_t); + ecs_observer_t *o = poly->poly; + ecs_observer_impl_t *impl = flecs_observer_impl(o); + if (impl->flags & EcsObserverIsMulti) { + ecs_observer_t **children = ecs_vec_first(&impl->children); + int32_t i, children_count = ecs_vec_count(&impl->children); + if (children_count) { + for (i = 0; i < children_count; i ++) { + ECS_BIT_COND(flecs_observer_impl(children[i])->flags, bit, cond); + } + } + } else { + flecs_poly_assert(o, ecs_observer_t); + ECS_BIT_COND(impl->flags, bit, cond); + } } diff --git a/vendors/flecs/src/os_api.c b/vendors/flecs/src/os_api.c index cc9a82c2d..627021587 100644 --- a/vendors/flecs/src/os_api.c +++ b/vendors/flecs/src/os_api.c @@ -111,11 +111,9 @@ void flecs_log_msg( int32_t line, const char *msg) { - FILE *stream; - if (level >= 0) { + FILE *stream = ecs_os_api.log_out_; + if (!stream) { stream = stdout; - } else { - stream = stderr; } bool use_colors = ecs_os_api.flags_ & EcsOsApiLogWithColors; @@ -140,7 +138,7 @@ void flecs_log_msg( fputs(" ", stream); } char time_buf[20]; - ecs_os_sprintf(time_buf, "%u", (uint32_t)delta); + ecs_os_snprintf(time_buf, 20, "%u", (uint32_t)delta); fputs("+", stream); fputs(time_buf, stream); fputs(" ", stream); @@ -154,7 +152,7 @@ void flecs_log_msg( now = time(NULL); } char time_buf[20]; - ecs_os_sprintf(time_buf, "%u", (uint32_t)now); + ecs_os_snprintf(time_buf, 20, "%u", (uint32_t)now); fputs(time_buf, stream); fputs(" ", stream); } @@ -234,7 +232,7 @@ void flecs_log_msg( } void ecs_os_dbg( - const char *file, + const char *file, int32_t line, const char *msg) { @@ -352,6 +350,26 @@ void ecs_os_strset(char **str, const char *value) { ecs_os_free(old); } +void ecs_os_perf_trace_push_( + const char *file, + size_t line, + const char *name) +{ + if (ecs_os_api.perf_trace_push_) { + ecs_os_api.perf_trace_push_(file, line, name); + } +} + +void ecs_os_perf_trace_pop_( + const char *file, + size_t line, + const char *name) +{ + if (ecs_os_api.perf_trace_pop_) { + ecs_os_api.perf_trace_pop_(file, line, name); + } +} + /* Replace dots with underscores */ static char *module_file_base(const char *module, char sep) { diff --git a/vendors/flecs/src/poly.c b/vendors/flecs/src/poly.c index 0fac51fc8..e7cc0e8ac 100644 --- a/vendors/flecs/src/poly.c +++ b/vendors/flecs/src/poly.c @@ -10,10 +10,6 @@ * Mixins are like a vtable, but for members. Each type populates the table with * offsets to the members that correspond with the mixin. If an entry in the * mixin table is not set, the type does not support the mixin. - * - * An example is the Iterable mixin, which makes it possible to create an - * iterator for any poly object (like filters, queries, the world) that - * implements the Iterable mixin. */ #include "private_api.h" @@ -22,7 +18,6 @@ static const char* mixin_kind_str[] = { [EcsMixinWorld] = "world", [EcsMixinEntity] = "entity", [EcsMixinObservable] = "observable", - [EcsMixinIterable] = "iterable", [EcsMixinDtor] = "dtor", [EcsMixinMax] = "max (should never be requested by application)" }; @@ -32,7 +27,6 @@ ecs_mixins_t ecs_world_t_mixins = { .elems = { [EcsMixinWorld] = offsetof(ecs_world_t, self), [EcsMixinObservable] = offsetof(ecs_world_t, observable), - [EcsMixinIterable] = offsetof(ecs_world_t, iterable) } }; @@ -43,32 +37,12 @@ ecs_mixins_t ecs_stage_t_mixins = { } }; -ecs_mixins_t ecs_query_t_mixins = { - .type_name = "ecs_query_t", - .elems = { - [EcsMixinWorld] = offsetof(ecs_query_t, filter.world), - [EcsMixinEntity] = offsetof(ecs_query_t, filter.entity), - [EcsMixinIterable] = offsetof(ecs_query_t, iterable), - [EcsMixinDtor] = offsetof(ecs_query_t, dtor) - } -}; - ecs_mixins_t ecs_observer_t_mixins = { .type_name = "ecs_observer_t", .elems = { - [EcsMixinWorld] = offsetof(ecs_observer_t, filter.world), - [EcsMixinEntity] = offsetof(ecs_observer_t, filter.entity), - [EcsMixinDtor] = offsetof(ecs_observer_t, dtor) - } -}; - -ecs_mixins_t ecs_filter_t_mixins = { - .type_name = "ecs_filter_t", - .elems = { - [EcsMixinWorld] = offsetof(ecs_filter_t, world), - [EcsMixinEntity] = offsetof(ecs_filter_t, entity), - [EcsMixinIterable] = offsetof(ecs_filter_t, iterable), - [EcsMixinDtor] = offsetof(ecs_filter_t, dtor) + [EcsMixinWorld] = offsetof(ecs_observer_t, world), + [EcsMixinEntity] = offsetof(ecs_observer_t, entity), + [EcsMixinDtor] = offsetof(ecs_observer_impl_t, dtor) } }; @@ -82,7 +56,8 @@ void* assert_mixin( const ecs_header_t *hdr = poly; ecs_assert(hdr != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, NULL); + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); const ecs_mixins_t *mixins = hdr->mixins; ecs_assert(mixins != NULL, ECS_INVALID_PARAMETER, NULL); @@ -97,7 +72,7 @@ void* assert_mixin( return ECS_OFFSET(hdr, offset); } -void* ecs_poly_init_( +void* flecs_poly_init_( ecs_poly_t *poly, int32_t type, ecs_size_t size, @@ -110,12 +85,13 @@ void* ecs_poly_init_( hdr->magic = ECS_OBJECT_MAGIC; hdr->type = type; + hdr->refcount = 1; hdr->mixins = mixins; return poly; } -void ecs_poly_fini_( +void flecs_poly_fini_( ecs_poly_t *poly, int32_t type) { @@ -125,12 +101,52 @@ void ecs_poly_fini_( ecs_header_t *hdr = poly; /* Don't deinit poly that wasn't initialized */ - ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, NULL); - ecs_assert(hdr->type == type, ECS_INVALID_PARAMETER, NULL); + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); + ecs_assert(hdr->type == type, ECS_INVALID_PARAMETER, + "incorrect function called to free flecs object"); hdr->magic = 0; } -EcsPoly* ecs_poly_bind_( +int32_t flecs_poly_claim_( + ecs_poly_t *poly) +{ + ecs_assert(poly != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_header_t *hdr = poly; + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); + if (ecs_os_has_threading()) { + return ecs_os_ainc(&hdr->refcount); + } else { + return ++hdr->refcount; + } +} + +int32_t flecs_poly_release_( + ecs_poly_t *poly) +{ + ecs_assert(poly != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_header_t *hdr = poly; + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); + if (ecs_os_has_threading()) { + return ecs_os_adec(&hdr->refcount); + } else { + return --hdr->refcount; + } +} + +int32_t flecs_poly_refcount( + ecs_poly_t *poly) +{ + ecs_assert(poly != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_header_t *hdr = poly; + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); + return hdr->refcount; +} + +EcsPoly* flecs_poly_bind_( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag) @@ -150,7 +166,7 @@ EcsPoly* ecs_poly_bind_( /* If this is a new poly, leave the actual creation up to the caller so they * call tell the difference between a create or an update */ - EcsPoly *result = ecs_get_mut_pair(world, entity, EcsPoly, tag); + EcsPoly *result = ecs_ensure_pair(world, entity, EcsPoly, tag); if (deferred) { ecs_defer_resume(world); @@ -159,7 +175,7 @@ EcsPoly* ecs_poly_bind_( return result; } -void ecs_poly_modified_( +void flecs_poly_modified_( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag) @@ -167,7 +183,7 @@ void ecs_poly_modified_( ecs_modified_pair(world, entity, ecs_id(EcsPoly), tag); } -const EcsPoly* ecs_poly_bind_get_( +const EcsPoly* flecs_poly_bind_get_( const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag) @@ -175,35 +191,30 @@ const EcsPoly* ecs_poly_bind_get_( return ecs_get_pair(world, entity, EcsPoly, tag); } -ecs_poly_t* ecs_poly_get_( +ecs_poly_t* flecs_poly_get_( const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag) { - const EcsPoly *p = ecs_poly_bind_get_(world, entity, tag); + const EcsPoly *p = flecs_poly_bind_get_(world, entity, tag); if (p) { return p->poly; } return NULL; } -bool ecs_poly_is_( +bool flecs_poly_is_( const ecs_poly_t *poly, int32_t type) { ecs_assert(poly != NULL, ECS_INVALID_PARAMETER, NULL); const ecs_header_t *hdr = poly; - ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, NULL); + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, + "invalid/freed pointer to flecs object detected"); return hdr->type == type; } -ecs_iterable_t* ecs_get_iterable( - const ecs_poly_t *poly) -{ - return (ecs_iterable_t*)assert_mixin(poly, EcsMixinIterable); -} - ecs_observable_t* ecs_get_observable( const ecs_poly_t *poly) { @@ -225,8 +236,8 @@ ecs_entity_t ecs_get_entity( return *(ecs_entity_t*)assert_mixin(poly, EcsMixinEntity); } -ecs_poly_dtor_t* ecs_get_dtor( +flecs_poly_dtor_t* ecs_get_dtor( const ecs_poly_t *poly) { - return (ecs_poly_dtor_t*)assert_mixin(poly, EcsMixinDtor); + return (flecs_poly_dtor_t*)assert_mixin(poly, EcsMixinDtor); } diff --git a/vendors/flecs/src/poly.h b/vendors/flecs/src/poly.h index 5cc8af160..018ad5e02 100644 --- a/vendors/flecs/src/poly.h +++ b/vendors/flecs/src/poly.h @@ -9,68 +9,68 @@ #include /* Initialize poly */ -void* ecs_poly_init_( +void* flecs_poly_init_( ecs_poly_t *object, int32_t kind, ecs_size_t size, ecs_mixins_t *mixins); -#define ecs_poly_init(object, type)\ - ecs_poly_init_(object, type##_magic, sizeof(type), &type##_mixins) +#define flecs_poly_init(object, type)\ + flecs_poly_init_(object, type##_magic, sizeof(type), &type##_mixins) /* Deinitialize object for specified type */ -void ecs_poly_fini_( +void flecs_poly_fini_( ecs_poly_t *object, int32_t kind); -#define ecs_poly_fini(object, type)\ - ecs_poly_fini_(object, type##_magic) +#define flecs_poly_fini(object, type)\ + flecs_poly_fini_(object, type##_magic) /* Utility functions for creating an object on the heap */ -#define ecs_poly_new(type)\ - (type*)ecs_poly_init(ecs_os_calloc_t(type), type) +#define flecs_poly_new(type)\ + (type*)flecs_poly_init(ecs_os_calloc_t(type), type) -#define ecs_poly_free(obj, type)\ - ecs_poly_fini(obj, type);\ +#define flecs_poly_free(obj, type)\ + flecs_poly_fini(obj, type);\ ecs_os_free(obj) /* Get or create poly component for an entity */ -EcsPoly* ecs_poly_bind_( +EcsPoly* flecs_poly_bind_( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag); -#define ecs_poly_bind(world, entity, T) \ - ecs_poly_bind_(world, entity, T##_tag) +#define flecs_poly_bind(world, entity, T) \ + flecs_poly_bind_(world, entity, T##_tag) -void ecs_poly_modified_( +void flecs_poly_modified_( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag); -#define ecs_poly_modified(world, entity, T) \ - ecs_poly_modified_(world, entity, T##_tag) +#define flecs_poly_modified(world, entity, T) \ + flecs_poly_modified_(world, entity, T##_tag) /* Get poly component for an entity */ -const EcsPoly* ecs_poly_bind_get_( +const EcsPoly* flecs_poly_bind_get_( const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag); -#define ecs_poly_bind_get(world, entity, T) \ - ecs_poly_bind_get_(world, entity, T##_tag) +#define flecs_poly_bind_get(world, entity, T) \ + flecs_poly_bind_get_(world, entity, T##_tag) -ecs_poly_t* ecs_poly_get_( +ecs_poly_t* flecs_poly_get_( const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t tag); -#define ecs_poly_get(world, entity, T) \ - ((T*)ecs_poly_get_(world, entity, T##_tag)) +#define flecs_poly_get(world, entity, T) \ + ((T*)flecs_poly_get_(world, entity, T##_tag)) /* Utilities for testing/asserting an object type */ #ifndef FLECS_NDEBUG -#define ecs_poly_assert(object, ty)\ +#define flecs_poly_assert(object, ty)\ do {\ ecs_assert(object != NULL, ECS_INVALID_PARAMETER, NULL);\ const ecs_header_t *hdr = (const ecs_header_t *)object;\ @@ -79,17 +79,13 @@ ecs_poly_t* ecs_poly_get_( ecs_assert(hdr->type == ty##_magic, ECS_INVALID_PARAMETER, type_name);\ } while (0) #else -#define ecs_poly_assert(object, ty) +#define flecs_poly_assert(object, ty) #endif -/* Utility functions for getting a mixin from an object */ -ecs_iterable_t* ecs_get_iterable( - const ecs_poly_t *poly); - ecs_observable_t* ecs_get_observable( const ecs_poly_t *object); -ecs_poly_dtor_t* ecs_get_dtor( +flecs_poly_dtor_t* ecs_get_dtor( const ecs_poly_t *poly); #endif diff --git a/vendors/flecs/src/private_api.h b/vendors/flecs/src/private_api.h index 0009db6f4..e64f87702 100644 --- a/vendors/flecs/src/private_api.h +++ b/vendors/flecs/src/private_api.h @@ -9,6 +9,7 @@ #include "private_types.h" #include "storage/table_cache.h" #include "storage/id_index.h" +#include "query/query.h" #include "observable.h" #include "iter.h" #include "poly.h" @@ -33,12 +34,16 @@ void flecs_bootstrap( }); #define flecs_bootstrap_tag(world, name)\ - ecs_ensure(world, name);\ + ecs_make_alive(world, name);\ ecs_add_id(world, name, EcsFinal);\ ecs_add_pair(world, name, EcsChildOf, ecs_get_scope(world));\ - ecs_set(world, name, EcsComponent, {.size = 0});\ ecs_set_name(world, name, (const char*)&#name[ecs_os_strlen(world->info.name_prefix)]);\ - ecs_set_symbol(world, name, #name) + ecs_set_symbol(world, name, #name); + +#define flecs_bootstrap_trait(world, name)\ + flecs_bootstrap_tag(world, name)\ + ecs_add_id(world, name, EcsTrait) + /* Bootstrap functions for other parts in the code */ void flecs_bootstrap_hierarchy(ecs_world_t *world); @@ -69,7 +74,7 @@ void flecs_notify_on_remove( ecs_table_t *other_table, int32_t row, int32_t count, - const ecs_type_t *diff); + const ecs_table_diff_t *diff); void flecs_notify_on_set( ecs_world_t *world, @@ -84,12 +89,18 @@ int32_t flecs_relation_depth( ecs_entity_t r, const ecs_table_t *table); +typedef struct ecs_instantiate_ctx_t { + ecs_entity_t root_prefab; + ecs_entity_t root_instance; +} ecs_instantiate_ctx_t; + void flecs_instantiate( ecs_world_t *world, ecs_entity_t base, ecs_table_t *table, int32_t row, - int32_t count); + int32_t count, + const ecs_instantiate_ctx_t *ctx); void* flecs_get_base_component( const ecs_world_t *world, @@ -101,10 +112,10 @@ void* flecs_get_base_component( void flecs_invoke_hook( ecs_world_t *world, ecs_table_t *table, + const ecs_table_record_t *tr, int32_t count, int32_t row, - ecs_entity_t *entities, - void *ptr, + const ecs_entity_t *entities, ecs_id_t id, const ecs_type_info_t *ti, ecs_entity_t event, @@ -114,52 +125,9 @@ void flecs_invoke_hook( //// Query API //////////////////////////////////////////////////////////////////////////////// -/* Match table with term */ -bool flecs_term_match_table( - ecs_world_t *world, - const ecs_term_t *term, - const ecs_table_t *table, - ecs_id_t *id_out, - int32_t *column_out, - ecs_entity_t *subject_out, - int32_t *match_indices, - bool first, - ecs_flags32_t iter_flags); - -/* Match table with filter */ -bool flecs_filter_match_table( - ecs_world_t *world, - const ecs_filter_t *filter, - const ecs_table_t *table, - ecs_id_t *ids, - int32_t *columns, - ecs_entity_t *sources, - int32_t *match_indices, - int32_t *matches_left, - bool first, - int32_t skip_term, - ecs_flags32_t iter_flags); - -ecs_iter_t flecs_filter_iter_w_flags( - const ecs_world_t *stage, - const ecs_filter_t *filter, - ecs_flags32_t flags); - -void flecs_query_notify( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_event_t *event); - -ecs_id_t flecs_to_public_id( - ecs_id_t id); - -ecs_id_t flecs_from_public_id( - ecs_world_t *world, - ecs_id_t id); - -void flecs_filter_apply_iter_flags( +void flecs_query_apply_iter_flags( ecs_iter_t *it, - const ecs_filter_t *filter); + const ecs_query_t *query); //////////////////////////////////////////////////////////////////////////////// //// Safe(r) integer casting @@ -219,25 +187,6 @@ uint64_t flecs_ito_( #define flecs_itoi16(value) flecs_ito(int16_t, (value)) #define flecs_itoi32(value) flecs_ito(int32_t, (value)) -//////////////////////////////////////////////////////////////////////////////// -//// Entity filter -//////////////////////////////////////////////////////////////////////////////// - -void flecs_entity_filter_init( - ecs_world_t *world, - ecs_entity_filter_t **entity_filter, - const ecs_filter_t *filter, - const ecs_table_t *table, - ecs_id_t *ids, - int32_t *columns); - -void flecs_entity_filter_fini( - ecs_world_t *world, - ecs_entity_filter_t *entity_filter); - -int flecs_entity_filter_next( - ecs_entity_filter_iter_t *it); - //////////////////////////////////////////////////////////////////////////////// //// Utilities //////////////////////////////////////////////////////////////////////////////// @@ -268,18 +217,26 @@ const char* flecs_name_from_symbol( ecs_world_t *world, const char *type_name); -/* Compare function for entity ids */ +/* Compare function for entity ids used for order_by */ int flecs_entity_compare( ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2); +/* Compare function for component ids used for qsort */ +int flecs_id_qsort_cmp( + const void *a, + const void *b); + +/* Load file contents into string */ +char* flecs_load_from_file( + const char *filename); + bool flecs_name_is_id( const char *name); ecs_entity_t flecs_name_to_id( - const ecs_world_t *world, const char *name); /* Convert floating point to string */ @@ -300,13 +257,9 @@ void flecs_colorize_buf( bool enable_colors, ecs_strbuf_t *buf); -bool flecs_isident( - char ch); - int32_t flecs_search_w_idr( const ecs_world_t *world, const ecs_table_t *table, - ecs_id_t id, ecs_id_t *id_out, ecs_id_record_t *idr); @@ -316,7 +269,7 @@ int32_t flecs_search_relation_w_idr( int32_t offset, ecs_id_t id, ecs_entity_t rel, - ecs_flags32_t flags, + ecs_flags64_t flags, ecs_entity_t *subject_out, ecs_id_t *id_out, struct ecs_table_record_t **tr_out, @@ -328,4 +281,12 @@ bool flecs_type_can_inherit_id( const ecs_id_record_t *idr, ecs_id_t id); +int ecs_term_finalize( + const ecs_world_t *world, + ecs_term_t *term); + +int32_t flecs_query_pivot_term( + const ecs_world_t *world, + const ecs_query_t *query); + #endif diff --git a/vendors/flecs/src/private_types.h b/vendors/flecs/src/private_types.h index d8867afbb..dbb11a43c 100644 --- a/vendors/flecs/src/private_types.h +++ b/vendors/flecs/src/private_types.h @@ -18,9 +18,7 @@ #include "flecs.h" #include "storage/entity_index.h" -#include "datastructures/stack_allocator.h" -#include "flecs/private/bitset.h" -#include "flecs/private/switch_list.h" +#include "flecs/datastructures/bitset.h" #include "storage/table.h" /* Used in id records to keep track of entities used with id flags */ @@ -36,9 +34,6 @@ extern const ecs_entity_t EcsFlag; #define ecs_world_t_tag invalid #define ecs_stage_t_tag invalid #define ecs_query_t_tag EcsQuery -#define ecs_rule_t_tag EcsQuery -#define ecs_table_t_tag invalid -#define ecs_filter_t_tag EcsQuery #define ecs_observer_t_tag EcsObserver /* Mixin kinds */ @@ -46,7 +41,6 @@ typedef enum ecs_mixin_kind_t { EcsMixinWorld, EcsMixinEntity, EcsMixinObservable, - EcsMixinIterable, EcsMixinDtor, EcsMixinMax } ecs_mixin_kind_t; @@ -63,9 +57,7 @@ struct ecs_mixins_t { /* Mixin tables */ extern ecs_mixins_t ecs_world_t_mixins; extern ecs_mixins_t ecs_stage_t_mixins; -extern ecs_mixins_t ecs_filter_t_mixins; extern ecs_mixins_t ecs_query_t_mixins; -extern ecs_mixins_t ecs_trigger_t_mixins; extern ecs_mixins_t ecs_observer_t_mixins; /* Types that have no mixins */ @@ -81,14 +73,6 @@ typedef struct ecs_hashed_string_t { uint64_t hash; } ecs_hashed_string_t; -/** Must appear as first member in payload of table cache */ -typedef struct ecs_table_cache_hdr_t { - struct ecs_table_cache_t *cache; - ecs_table_t *table; - struct ecs_table_cache_hdr_t *prev, *next; - bool empty; -} ecs_table_cache_hdr_t; - /** Linked list of tables in table cache */ typedef struct ecs_table_cache_list_t { ecs_table_cache_hdr_t *first; @@ -103,194 +87,6 @@ typedef struct ecs_table_cache_t { ecs_table_cache_list_t empty_tables; } ecs_table_cache_t; -/* Sparse query term */ -typedef struct flecs_switch_term_t { - ecs_switch_t *sw_column; - ecs_entity_t sw_case; - int32_t signature_column_index; -} flecs_switch_term_t; - -/* Bitset query term */ -typedef struct flecs_bitset_term_t { - ecs_bitset_t *bs_column; - int32_t column_index; -} flecs_bitset_term_t; - -typedef struct flecs_flat_monitor_t { - int32_t table_state; - int32_t monitor; -} flecs_flat_monitor_t; - -/* Flat table term */ -typedef struct flecs_flat_table_term_t { - int32_t field_index; /* Iterator field index */ - ecs_term_t *term; - ecs_vec_t monitor; -} flecs_flat_table_term_t; - -/* Entity filter. This filters the entities of a matched table, for example when - * it has disabled components or union relationships (switch). */ -typedef struct ecs_entity_filter_t { - ecs_vec_t sw_terms; /* Terms with switch (union) entity filter */ - ecs_vec_t bs_terms; /* Terms with bitset (toggle) entity filter */ - ecs_vec_t ft_terms; /* Terms with components from flattened tree */ - int32_t flat_tree_column; -} ecs_entity_filter_t; - -typedef struct ecs_entity_filter_iter_t { - ecs_entity_filter_t *entity_filter; - ecs_iter_t *it; - int32_t *columns; - ecs_table_t *prev; - ecs_table_range_t range; - int32_t bs_offset; - int32_t sw_offset; - int32_t sw_smallest; - int32_t flat_tree_offset; - int32_t target_count; -} ecs_entity_filter_iter_t; - -/** Table match data. - * Each table matched by the query is represented by a ecs_query_table_match_t - * instance, which are linked together in a list. A table may match a query - * multiple times (due to wildcard queries) with different columns being matched - * by the query. */ -struct ecs_query_table_match_t { - ecs_query_table_match_t *next, *prev; - ecs_table_t *table; /* The current table. */ - int32_t offset; /* Starting point in table */ - int32_t count; /* Number of entities to iterate in table */ - int32_t *columns; /* Mapping from query fields to table columns */ - int32_t *storage_columns; /* Mapping from query fields to storage columns */ - ecs_id_t *ids; /* Resolved (component) ids for current table */ - ecs_entity_t *sources; /* Subjects (sources) of ids */ - ecs_vec_t refs; /* Cached components for non-this terms */ - uint64_t group_id; /* Value used to organize tables in groups */ - int32_t *monitor; /* Used to monitor table for changes */ - ecs_entity_filter_t *entity_filter; /* Entity specific filters */ - - /* Next match in cache for same table (includes empty tables) */ - ecs_query_table_match_t *next_match; -}; - -/** Table record type for query table cache. A query only has one per table. */ -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; - -/** Points to the beginning & ending of a query group */ -typedef struct ecs_query_table_list_t { - ecs_query_table_match_t *first; - ecs_query_table_match_t *last; - ecs_query_group_info_t info; -} ecs_query_table_list_t; - -/* Query event type for notifying queries of world events */ -typedef enum ecs_query_eventkind_t { - EcsQueryTableMatch, - EcsQueryTableRematch, - EcsQueryTableUnmatch, - EcsQueryOrphan -} ecs_query_eventkind_t; - -typedef struct ecs_query_event_t { - ecs_query_eventkind_t kind; - ecs_table_t *table; - ecs_query_t *parent_query; -} ecs_query_event_t; - -/* Query level block allocators have sizes that depend on query field count */ -typedef struct ecs_query_allocators_t { - ecs_block_allocator_t columns; - ecs_block_allocator_t ids; - ecs_block_allocator_t sources; - ecs_block_allocator_t monitors; -} ecs_query_allocators_t; - -/** Query that is automatically matched against tables */ -struct ecs_query_t { - ecs_header_t hdr; - - /* Query filter */ - ecs_filter_t filter; - - /* Tables matched with query */ - ecs_table_cache_t cache; - - /* Linked list with all matched non-empty tables, in iteration order */ - ecs_query_table_list_t list; - - /* Contains head/tail to nodes of query groups (if group_by is used) */ - ecs_map_t groups; - - /* Table sorting */ - ecs_entity_t order_by_component; - ecs_order_by_action_t order_by; - ecs_sort_table_action_t sort_table; - ecs_vec_t table_slices; - int32_t order_by_term; - - /* Table grouping */ - ecs_entity_t group_by_id; - ecs_group_by_action_t group_by; - ecs_group_create_action_t on_group_create; - ecs_group_delete_action_t on_group_delete; - void *group_by_ctx; - ecs_ctx_free_t group_by_ctx_free; - - /* Subqueries */ - ecs_query_t *parent; - ecs_vec_t subqueries; - - /* Flags for query properties */ - ecs_flags32_t flags; - - /* Monitor generation */ - int32_t monitor_generation; - - int32_t cascade_by; /* Identify cascade term */ - int32_t match_count; /* How often have tables been (un)matched */ - int32_t prev_match_count; /* Track if sorting is needed */ - int32_t rematch_count; /* Track which tables were added during rematch */ - - /* User context */ - void *ctx; /* User context to pass to callback */ - void *binding_ctx; /* Context to be used for language bindings */ - - ecs_ctx_free_t ctx_free; /** Callback to free ctx */ - ecs_ctx_free_t binding_ctx_free; /** Callback to free binding_ctx */ - - /* Mixins */ - ecs_iterable_t iterable; - ecs_poly_dtor_t dtor; - - /* Query-level allocators */ - ecs_query_allocators_t allocators; -}; - -/** All observers for a specific (component) id */ -typedef struct ecs_event_id_record_t { - /* Triggers for Self */ - ecs_map_t self; /* map */ - ecs_map_t self_up; /* map */ - ecs_map_t up; /* map */ - - ecs_map_t observers; /* map */ - - /* Triggers for SuperSet, SubSet */ - ecs_map_t set_observers; /* map */ - - /* Triggers for Self with non-This subject */ - ecs_map_t entity_observers; /* map */ - - /* Number of active observers for (component) id */ - int32_t observer_count; -} ecs_event_id_record_t; - /* World level allocators are for operations that are not multithreaded */ typedef struct ecs_world_allocators_t { ecs_map_params_t ptr; @@ -314,6 +110,8 @@ typedef struct ecs_stage_allocators_t { ecs_stack_t iter_stack; ecs_stack_t deser_stack; ecs_block_allocator_t cmd_entry_chunk; + ecs_block_allocator_t query_impl; + ecs_block_allocator_t query_cache; } ecs_stage_allocators_t; /** Types for deferred operations */ @@ -324,7 +122,7 @@ typedef enum ecs_cmd_kind_t { EcsCmdRemove, EcsCmdSet, EcsCmdEmplace, - EcsCmdMut, + EcsCmdEnsure, EcsCmdModified, EcsCmdModifiedNoHook, EcsCmdAddModified, @@ -345,7 +143,7 @@ typedef struct ecs_cmd_entry_t { } ecs_cmd_entry_t; typedef struct ecs_cmd_1_t { - void *value; /* Component value (used by set / get_mut) */ + void *value; /* Component value (used by set / ensure) */ ecs_size_t size; /* Size of value */ bool clone_value; /* Clone entity with value (used for clone) */ } ecs_cmd_1_t; @@ -367,6 +165,8 @@ typedef struct ecs_cmd_t { ecs_cmd_1_t _1; /* Data for single entity operation */ ecs_cmd_n_t _n; /* Data for multi entity operation */ } is; + + ecs_entity_t system; /* System that enqueued the command */ } ecs_cmd_t; /* Data structures that store the command queue */ @@ -376,10 +176,22 @@ typedef struct ecs_commands_t { ecs_sparse_t entries; /* - command batching */ } ecs_commands_t; +/** Callback used to capture commands of a frame */ +typedef void (*ecs_on_commands_action_t)( + const ecs_stage_t *stage, + const ecs_vec_t *commands, + void *ctx); + /** A stage is a context that allows for safely using the API from multiple * threads. Stage pointers can be passed to the world argument of API * operations, which causes the operation to be ran on the stage instead of the - * world. */ + * world. The features provided by a stage are: + * + * - A command queue for deferred ECS operations and events + * - Thread specific allocators + * - Thread specific world state (like current scope, with, current system) + * - Thread specific buffers for preventing allocations + */ struct ecs_stage_t { ecs_header_t hdr; @@ -408,22 +220,26 @@ struct ecs_stage_t { ecs_entity_t base; /* Currently instantiated top-level base */ const ecs_entity_t *lookup_path; /* Search path used by lookup operations */ - /* Properties */ - bool auto_merge; /* Should this stage automatically merge? */ - bool async; /* Is stage asynchronous? (write only) */ + /* Running system */ + ecs_entity_t system; /* Thread specific allocators */ ecs_stage_allocators_t allocators; ecs_allocator_t allocator; - /* Caches for rule creation */ + /* Caches for query creation */ ecs_vec_t variables; ecs_vec_t operations; + + /* Temporary token storage for DSL parser. This allows for parsing and + * interpreting a term without having to do allocations. */ + char parser_tokens[1024]; + char *parser_token; /* Pointer to next token */ }; /* Component monitor */ typedef struct ecs_monitor_t { - ecs_vec_t queries; /* vector */ + ecs_vec_t queries; /* vector */ bool is_dirty; /* Should queries be rematched? */ } ecs_monitor_t; @@ -445,7 +261,7 @@ typedef struct ecs_store_t { /* Entity lookup */ ecs_entity_index_t entity_index; - /* Table lookup by id */ + /* Tables */ ecs_sparse_t tables; /* sparse */ /* Table lookup by hash */ @@ -454,15 +270,19 @@ typedef struct ecs_store_t { /* Root table */ ecs_table_t root; + /* Observers */ + ecs_sparse_t observers; /* sparse */ + /* Records cache */ ecs_vec_t records; - /* Stack of ids being deleted. */ + /* Stack of ids being deleted during cleanup action. */ ecs_vec_t marked_ids; /* vector */ - - /* Entity ids associated with depth (for flat hierarchies) */ - ecs_vec_t depth_ids; - ecs_map_t entity_to_depth; /* What it says */ + + /* Components deleted during cleanup action. Used to delay cleaning up of + * type info so it's guaranteed that this data is available while the + * storage is cleaning up tables. */ + ecs_vec_t deleted_components; /* vector */ } ecs_store_t; /* fini actions */ @@ -495,7 +315,6 @@ struct ecs_world_t { /* -- Mixins -- */ ecs_world_t *self; ecs_observable_t observable; - ecs_iterable_t iterable; /* Unique id per generated event used to prevent duplicate notifications */ int32_t event_id; @@ -521,9 +340,18 @@ struct ecs_world_t { ecs_hashmap_t symbols; /* -- Staging -- */ - ecs_stage_t *stages; /* Stages */ + ecs_stage_t **stages; /* Stages */ int32_t stage_count; /* Number of stages */ + /* Internal callback for command inspection. Only one callback can be set at + * a time. After assignment the action will become active at the start of + * the next frame, set by ecs_frame_begin, and will be reset by + * ecs_frame_end. */ + ecs_on_commands_action_t on_commands; + ecs_on_commands_action_t on_commands_active; + void *on_commands_ctx; + void *on_commands_ctx_active; + /* -- Multithreading -- */ ecs_os_cond_t worker_cond; /* Signal that worker threads can start */ ecs_os_cond_t sync_cond; /* Signal that worker thread job is done */ @@ -544,6 +372,9 @@ struct ecs_world_t { /* -- World flags -- */ ecs_flags32_t flags; + /* -- Default query flags -- */ + ecs_flags32_t default_query_flags; + /* Count that increases when component monitors change */ int32_t monitor_generation; diff --git a/vendors/flecs/src/query.c b/vendors/flecs/src/query.c deleted file mode 100644 index 0a7b64f1e..000000000 --- a/vendors/flecs/src/query.c +++ /dev/null @@ -1,2816 +0,0 @@ -/** - * @file query.c - * @brief Cached query implementation. - * - * Cached queries store a list of matched tables. The inputs for a cached query - * are a filter and an observer. The filter is used to initially populate the - * cache, and an observer is used to keep the cacne up to date. - * - * Cached queries additionally support features like sorting and grouping. - * With sorting, an application can iterate over entities that can be sorted by - * a component. Grouping allows an application to group matched tables, which is - * used internally to implement the cascade feature, and can additionally be - * used to implement things like world cells. - */ - -#include "private_api.h" - -static -uint64_t flecs_query_get_group_id( - ecs_query_t *query, - ecs_table_t *table) -{ - if (query->group_by) { - return query->group_by(query->filter.world, table, - query->group_by_id, query->group_by_ctx); - } else { - return 0; - } -} - -static -void flecs_query_compute_group_id( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - - if (query->group_by) { - ecs_table_t *table = match->table; - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - - match->group_id = flecs_query_get_group_id(query, table); - } else { - match->group_id = 0; - } -} - -static -ecs_query_table_list_t* flecs_query_get_group( - const ecs_query_t *query, - uint64_t group_id) -{ - return ecs_map_get_deref(&query->groups, ecs_query_table_list_t, group_id); -} - -static -ecs_query_table_list_t* flecs_query_ensure_group( - ecs_query_t *query, - uint64_t id) -{ - ecs_query_table_list_t *group = ecs_map_get_deref(&query->groups, - ecs_query_table_list_t, id); - - if (!group) { - group = ecs_map_insert_alloc_t(&query->groups, - ecs_query_table_list_t, id); - ecs_os_zeromem(group); - if (query->on_group_create) { - group->info.ctx = query->on_group_create( - query->filter.world, id, query->group_by_ctx); - } - } - - return group; -} - -static -void flecs_query_remove_group( - ecs_query_t *query, - uint64_t id) -{ - if (query->on_group_delete) { - ecs_query_table_list_t *group = ecs_map_get_deref(&query->groups, - ecs_query_table_list_t, id); - if (group) { - query->on_group_delete(query->filter.world, id, - group->info.ctx, query->group_by_ctx); - } - } - - ecs_map_remove_free(&query->groups, id); -} - -static -uint64_t flecs_query_default_group_by( - ecs_world_t *world, - ecs_table_t *table, - ecs_id_t id, - void *ctx) -{ - (void)ctx; - - ecs_id_t match; - if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &match) != -1) { - return ecs_pair_second(world, match); - } - return 0; -} - -/* Find the last node of the group after which this group should be inserted */ -static -ecs_query_table_match_t* flecs_query_find_group_insertion_node( - ecs_query_t *query, - uint64_t group_id) -{ - /* Grouping must be enabled */ - ecs_assert(query->group_by != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_map_iter_t it = ecs_map_iter(&query->groups); - ecs_query_table_list_t *list, *closest_list = NULL; - uint64_t id, closest_id = 0; - - bool desc = false; - - if (query->cascade_by) { - desc = (query->filter.terms[ - query->cascade_by - 1].src.flags & EcsDesc) != 0; - } - - /* Find closest smaller group id */ - while (ecs_map_next(&it)) { - id = ecs_map_key(&it); - - if (!desc) { - if (id >= group_id) { - continue; - } - } else { - if (id <= group_id) { - continue; - } - } - - list = ecs_map_ptr(&it); - if (!list->last) { - ecs_assert(list->first == NULL, ECS_INTERNAL_ERROR, NULL); - continue; - } - - bool comp; - if (!desc) { - comp = ((group_id - id) < (group_id - closest_id)); - } else { - comp = ((group_id - id) > (group_id - closest_id)); - } - - if (!closest_list || comp) { - closest_id = id; - closest_list = list; - } - } - - if (closest_list) { - return closest_list->last; - } else { - return NULL; /* Group should be first in query */ - } -} - -/* Initialize group with first node */ -static -void flecs_query_create_group( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - uint64_t group_id = match->group_id; - - /* If query has grouping enabled & this is a new/empty group, find - * the insertion point for the group */ - ecs_query_table_match_t *insert_after = flecs_query_find_group_insertion_node( - query, group_id); - - if (!insert_after) { - /* This group should appear first in the query list */ - ecs_query_table_match_t *query_first = query->list.first; - if (query_first) { - /* If this is not the first match for the query, insert before it */ - match->next = query_first; - query_first->prev = match; - query->list.first = match; - } else { - /* If this is the first match of the query, initialize its list */ - ecs_assert(query->list.last == NULL, ECS_INTERNAL_ERROR, NULL); - query->list.first = match; - query->list.last = match; - } - } else { - ecs_assert(query->list.first != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.last != NULL, ECS_INTERNAL_ERROR, NULL); - - /* This group should appear after another group */ - ecs_query_table_match_t *insert_before = insert_after->next; - match->prev = insert_after; - insert_after->next = match; - match->next = insert_before; - if (insert_before) { - insert_before->prev = match; - } else { - ecs_assert(query->list.last == insert_after, - ECS_INTERNAL_ERROR, NULL); - - /* This group should appear last in the query list */ - query->list.last = match; - } - } -} - -/* Find the list the node should be part of */ -static -ecs_query_table_list_t* flecs_query_get_node_list( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - if (query->group_by) { - return flecs_query_get_group(query, match->group_id); - } else { - return &query->list; - } -} - -/* Find or create the list the node should be part of */ -static -ecs_query_table_list_t* flecs_query_ensure_node_list( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - if (query->group_by) { - return flecs_query_ensure_group(query, match->group_id); - } else { - return &query->list; - } -} - -/* Remove node from list */ -static -void flecs_query_remove_table_node( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - ecs_query_table_match_t *prev = match->prev; - ecs_query_table_match_t *next = match->next; - - ecs_assert(prev != match, ECS_INTERNAL_ERROR, NULL); - ecs_assert(next != match, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!prev || prev != next, ECS_INTERNAL_ERROR, NULL); - - ecs_query_table_list_t *list = flecs_query_get_node_list(query, match); - - if (!list || !list->first) { - /* If list contains no matches, the match must be empty */ - ecs_assert(!list || list->last == NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(prev == NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(next == NULL, ECS_INTERNAL_ERROR, NULL); - return; - } - - ecs_assert(prev != NULL || query->list.first == match, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(next != NULL || query->list.last == match, - ECS_INTERNAL_ERROR, NULL); - - if (prev) { - prev->next = next; - } - if (next) { - next->prev = prev; - } - - ecs_assert(list->info.table_count > 0, ECS_INTERNAL_ERROR, NULL); - list->info.table_count --; - - if (query->group_by) { - uint64_t group_id = match->group_id; - - /* Make sure query.list is updated if this is the first or last group */ - if (query->list.first == match) { - ecs_assert(prev == NULL, ECS_INTERNAL_ERROR, NULL); - query->list.first = next; - prev = next; - } - if (query->list.last == match) { - ecs_assert(next == NULL, ECS_INTERNAL_ERROR, NULL); - query->list.last = prev; - next = prev; - } - - ecs_assert(query->list.info.table_count > 0, ECS_INTERNAL_ERROR, NULL); - query->list.info.table_count --; - list->info.match_count ++; - - /* Make sure group list only contains nodes that belong to the group */ - if (prev && prev->group_id != group_id) { - /* The previous node belonged to another group */ - prev = next; - } - if (next && next->group_id != group_id) { - /* The next node belonged to another group */ - next = prev; - } - - /* Do check again, in case both prev & next belonged to another group */ - if ((!prev && !next) || (prev && prev->group_id != group_id)) { - /* There are no more matches left in this group */ - flecs_query_remove_group(query, group_id); - list = NULL; - } - } - - if (list) { - if (list->first == match) { - list->first = next; - } - if (list->last == match) { - list->last = prev; - } - } - - match->prev = NULL; - match->next = NULL; - - query->match_count ++; -} - -/* Add node to list */ -static -void flecs_query_insert_table_node( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - /* Node should not be part of an existing list */ - ecs_assert(match->prev == NULL && match->next == NULL, - ECS_INTERNAL_ERROR, NULL); - - /* If this is the first match, activate system */ - if (!query->list.first && query->filter.entity) { - ecs_remove_id(query->filter.world, query->filter.entity, EcsEmpty); - } - - flecs_query_compute_group_id(query, match); - - ecs_query_table_list_t *list = flecs_query_ensure_node_list(query, match); - if (list->last) { - ecs_assert(query->list.first != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.last != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(list->first != NULL, ECS_INTERNAL_ERROR, NULL); - - ecs_query_table_match_t *last = list->last; - ecs_query_table_match_t *last_next = last->next; - - match->prev = last; - match->next = last_next; - last->next = match; - - if (last_next) { - last_next->prev = match; - } - - list->last = match; - - if (query->group_by) { - /* Make sure to update query list if this is the last group */ - if (query->list.last == last) { - query->list.last = match; - } - } - } else { - ecs_assert(list->first == NULL, ECS_INTERNAL_ERROR, NULL); - - list->first = match; - list->last = match; - - if (query->group_by) { - /* Initialize group with its first node */ - flecs_query_create_group(query, match); - } - } - - if (query->group_by) { - list->info.table_count ++; - list->info.match_count ++; - } - - query->list.info.table_count ++; - query->match_count ++; - - ecs_assert(match->prev != match, ECS_INTERNAL_ERROR, NULL); - ecs_assert(match->next != match, ECS_INTERNAL_ERROR, NULL); - - ecs_assert(list->first != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(list->last != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(list->last == match, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.first != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.last != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.first->prev == NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(query->list.last->next == NULL, ECS_INTERNAL_ERROR, NULL); -} - -static -ecs_query_table_match_t* flecs_query_cache_add( - ecs_world_t *world, - ecs_query_table_t *elem) -{ - ecs_query_table_match_t *result = - flecs_bcalloc(&world->allocators.query_table_match); - - if (!elem->first) { - elem->first = result; - elem->last = result; - } else { - ecs_assert(elem->last != NULL, ECS_INTERNAL_ERROR, NULL); - elem->last->next_match = result; - elem->last = result; - } - - return result; -} - -typedef struct { - ecs_table_t *table; - int32_t column; -} flecs_table_column_t; - -static -void flecs_query_get_column_for_term( - ecs_query_t *query, - ecs_query_table_match_t *match, - int32_t t, - flecs_table_column_t *out) -{ - const ecs_filter_t *filter = &query->filter; - ecs_world_t *world = filter->world; - ecs_term_t *term = &filter->terms[t]; - int32_t field = term->field_index; - ecs_entity_t src = match->sources[field]; - ecs_table_t *table = NULL; - int32_t column = -1; - - if (term->oper != EcsNot) { - if (!src) { - if (term->src.flags != EcsIsEntity) { - table = match->table; - column = match->storage_columns[field]; - if (column == -2) { - /* Shared field */ - column = -1; - } - } - } else { - table = ecs_get_table(world, src); - if (ecs_term_match_this(term)) { - int32_t ref_index = -match->columns[field] - 1; - ecs_ref_t *ref = ecs_vec_get_t(&match->refs, ecs_ref_t, ref_index); - if (ref->id != 0) { - ecs_ref_update(world, ref); - column = ref->tr->column; - } - } else { - column = -(match->columns[field] + 1); - } - } - } - - out->table = table; - out->column = column; -} - -/* Get match monitor. Monitors are used to keep track of whether components - * matched by the query in a table have changed. */ -static -bool flecs_query_get_match_monitor( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - if (match->monitor) { - return false; - } - - int32_t *monitor = flecs_balloc(&query->allocators.monitors); - monitor[0] = 0; - - /* Mark terms that don't need to be monitored. This saves time when reading - * and/or updating the monitor. */ - const ecs_filter_t *f = &query->filter; - int32_t i, field = -1, term_count = f->term_count; - flecs_table_column_t tc; - - for (i = 0; i < term_count; i ++) { - if (field == f->terms[i].field_index) { - if (monitor[field + 1] != -1) { - continue; - } - } - - field = f->terms[i].field_index; - monitor[field + 1] = -1; - - if (f->terms[i].inout != EcsIn && - f->terms[i].inout != EcsInOut && - f->terms[i].inout != EcsInOutDefault) { - continue; /* If term isn't read, don't monitor */ - } - - int32_t column = match->columns[field]; - if (column == 0) { - continue; /* Don't track terms that aren't matched */ - } - - flecs_query_get_column_for_term(query, match, i, &tc); - if (tc.column == -1) { - continue; /* Don't track terms that aren't stored */ - } - - monitor[field + 1] = 0; - } - - /* If matched table needs entity filter, make sure to test fields that could - * be matched by flattened parents. */ - ecs_entity_filter_t *ef = match->entity_filter; - if (ef && ef->flat_tree_column != -1) { - int32_t *fields = ecs_vec_first(&ef->ft_terms); - int32_t field_count = ecs_vec_count(&ef->ft_terms); - for (i = 0; i < field_count; i ++) { - monitor[fields[i] + 1] = 0; - } - } - - match->monitor = monitor; - - query->flags |= EcsQueryHasMonitor; - - return true; -} - -/* Synchronize match monitor with table dirty state */ -static -void flecs_query_sync_match_monitor( - ecs_query_t *query, - ecs_query_table_match_t *match) -{ - ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - if (!match->monitor) { - if (query->flags & EcsQueryHasMonitor) { - flecs_query_get_match_monitor(query, match); - } else { - return; - } - } - - int32_t *monitor = match->monitor; - ecs_table_t *table = match->table; - if (table) { - int32_t *dirty_state = flecs_table_get_dirty_state( - query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - monitor[0] = dirty_state[0]; /* Did table gain/lose entities */ - } - - ecs_filter_t *filter = &query->filter; - { - flecs_table_column_t tc; - int32_t t, term_count = filter->term_count; - for (t = 0; t < term_count; t ++) { - int32_t field = filter->terms[t].field_index; - if (monitor[field + 1] == -1) { - continue; - } - - flecs_query_get_column_for_term(query, match, t, &tc); - - monitor[field + 1] = flecs_table_get_dirty_state( - filter->world, tc.table)[tc.column + 1]; - } - } - - ecs_entity_filter_t *ef = match->entity_filter; - if (ef && ef->flat_tree_column != -1) { - flecs_flat_table_term_t *fields = ecs_vec_first(&ef->ft_terms); - int32_t f, field_count = ecs_vec_count(&ef->ft_terms); - for (f = 0; f < field_count; f ++) { - flecs_flat_table_term_t *field = &fields[f]; - flecs_flat_monitor_t *tgt_mon = ecs_vec_first(&field->monitor); - int32_t tgt, tgt_count = ecs_vec_count(&field->monitor); - for (tgt = 0; tgt < tgt_count; tgt ++) { - tgt_mon[tgt].monitor = tgt_mon[tgt].table_state; - } - } - } - - query->prev_match_count = query->match_count; -} - -/* Check if single match term has changed */ -static -bool flecs_query_check_match_monitor_term( - ecs_query_t *query, - ecs_query_table_match_t *match, - int32_t term) -{ - ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - - if (flecs_query_get_match_monitor(query, match)) { - return true; - } - - int32_t *monitor = match->monitor; - int32_t state = monitor[term]; - if (state == -1) { - return false; - } - - ecs_table_t *table = match->table; - if (table) { - int32_t *dirty_state = flecs_table_get_dirty_state( - query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - if (!term) { - return monitor[0] != dirty_state[0]; - } - } else if (!term) { - return false; - } - - flecs_table_column_t cur; - flecs_query_get_column_for_term(query, match, term - 1, &cur); - ecs_assert(cur.column != -1, ECS_INTERNAL_ERROR, NULL); - - return monitor[term] != flecs_table_get_dirty_state( - query->filter.world, cur.table)[cur.column + 1]; -} - -/* Check if any term for match has changed */ -static -bool flecs_query_check_match_monitor( - ecs_query_t *query, - ecs_query_table_match_t *match, - const ecs_iter_t *it) -{ - ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); - - if (flecs_query_get_match_monitor(query, match)) { - return true; - } - - int32_t *monitor = match->monitor; - ecs_table_t *table = match->table; - int32_t *dirty_state = NULL; - if (table) { - dirty_state = flecs_table_get_dirty_state( - query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - if (monitor[0] != dirty_state[0]) { - return true; - } - } - - bool has_flat = false, is_this = false; - const ecs_filter_t *filter = &query->filter; - ecs_world_t *world = filter->world; - int32_t i, j, field_count = filter->field_count; - int32_t *storage_columns = match->storage_columns; - int32_t *columns = it ? it->columns : NULL; - if (!columns) { - columns = match->columns; - } - ecs_vec_t *refs = &match->refs; - for (i = 0; i < field_count; i ++) { - int32_t mon = monitor[i + 1]; - if (mon == -1) { - continue; - } - - int32_t column = storage_columns[i]; - if (columns[i] >= 0) { - /* owned component */ - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - if (mon != dirty_state[column + 1]) { - return true; - } - continue; - } else if (column == -1) { - continue; /* owned but not a component */ - } - - column = columns[i]; - if (!column) { - /* Not matched */ - continue; - } - - ecs_assert(column < 0, ECS_INTERNAL_ERROR, NULL); - column = -column; - - /* Find term index from field index, which differ when using || */ - int32_t term_index = i; - if (filter->terms[i].field_index != i) { - for (j = i; j < filter->term_count; j ++) { - if (filter->terms[j].field_index == i) { - term_index = j; - break; - } - } - } - - is_this = ecs_term_match_this(&filter->terms[term_index]); - - /* Flattened fields are encoded by adding field_count to the column - * index of the parent component. */ - if (is_this && it && (column > field_count)) { - has_flat = true; - } else { - if (is_this) { - /* Component reached through traversal from this */ - int32_t ref_index = column - 1; - ecs_ref_t *ref = ecs_vec_get_t(refs, ecs_ref_t, ref_index); - if (ref->id != 0) { - ecs_ref_update(world, ref); - ecs_table_record_t *tr = ref->tr; - ecs_table_t *src_table = tr->hdr.table; - column = tr->index; - column = ecs_table_type_to_column_index(src_table, column); - int32_t *src_dirty_state = flecs_table_get_dirty_state( - world, src_table); - if (mon != src_dirty_state[column + 1]) { - return true; - } - } - } else { - /* Component from static source */ - ecs_entity_t src = match->sources[i]; - ecs_table_t *src_table = ecs_get_table(world, src); - ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); - column = ecs_table_type_to_column_index(src_table, column - 1); - int32_t *src_dirty_state = flecs_table_get_dirty_state( - world, src_table); - if (mon != src_dirty_state[column + 1]) { - return true; - } - } - } - } - - if (has_flat) { - ecs_entity_filter_t *ef = match->entity_filter; - flecs_flat_table_term_t *fields = ecs_vec_first(&ef->ft_terms); - ecs_entity_filter_iter_t *ent_it = it->priv.entity_iter; - int32_t cur_tgt = ent_it->target_count - 1; - field_count = ecs_vec_count(&ef->ft_terms); - - for (i = 0; i < field_count; i ++) { - flecs_flat_table_term_t *field = &fields[i]; - flecs_flat_monitor_t *fmon = ecs_vec_get_t(&field->monitor, - flecs_flat_monitor_t, cur_tgt); - if (fmon->monitor != fmon->table_state) { - return true; - } - } - } - - return false; -} - -/* Check if any term for matched table has changed */ -static -bool flecs_query_check_table_monitor( - ecs_query_t *query, - ecs_query_table_t *table, - int32_t term) -{ - ecs_query_table_match_t *cur, *end = table->last->next; - - for (cur = table->first; cur != end; cur = cur->next) { - ecs_query_table_match_t *match = (ecs_query_table_match_t*)cur; - if (term == -1) { - if (flecs_query_check_match_monitor(query, match, NULL)) { - return true; - } - } else { - if (flecs_query_check_match_monitor_term(query, match, term)) { - return true; - } - } - } - - return false; -} - -static -bool flecs_query_check_query_monitor( - ecs_query_t *query) -{ - ecs_table_cache_iter_t it; - if (flecs_table_cache_iter(&query->cache, &it)) { - ecs_query_table_t *qt; - while ((qt = flecs_table_cache_next(&it, ecs_query_table_t))) { - if (flecs_query_check_table_monitor(query, qt, -1)) { - return true; - } - } - } - - return false; -} - -static -void flecs_query_init_query_monitors( - ecs_query_t *query) -{ - ecs_query_table_match_t *cur = query->list.first; - - /* Ensure each match has a monitor */ - for (; cur != NULL; cur = cur->next) { - ecs_query_table_match_t *match = (ecs_query_table_match_t*)cur; - flecs_query_get_match_monitor(query, match); - } -} - -/* The group by function for cascade computes the tree depth for the table type. - * This causes tables in the query cache to be ordered by depth, which ensures - * breadth-first iteration order. */ -static -uint64_t flecs_query_group_by_cascade( - ecs_world_t *world, - ecs_table_t *table, - ecs_id_t id, - void *ctx) -{ - (void)id; - ecs_term_t *term = ctx; - ecs_entity_t rel = term->src.trav; - int32_t depth = flecs_relation_depth(world, rel, table); - return flecs_ito(uint64_t, depth); -} - -static -void flecs_query_add_ref( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_table_match_t *qm, - ecs_entity_t component, - ecs_entity_t entity, - ecs_size_t size) -{ - ecs_assert(entity != 0, ECS_INTERNAL_ERROR, NULL); - ecs_ref_t *ref = ecs_vec_append_t(&world->allocator, &qm->refs, ecs_ref_t); - ecs_assert(entity != 0, ECS_INTERNAL_ERROR, NULL); - - if (size) { - *ref = ecs_ref_init_id(world, entity, component); - } else { - *ref = (ecs_ref_t){ - .entity = entity, - .id = 0 - }; - } - - query->flags |= EcsQueryHasRefs; -} - -static -ecs_query_table_match_t* flecs_query_add_table_match( - ecs_query_t *query, - ecs_query_table_t *qt, - ecs_table_t *table) -{ - /* Add match for table. One table can have more than one match, if - * the query contains wildcards. */ - ecs_query_table_match_t *qm = flecs_query_cache_add(query->filter.world, qt); - qm->table = table; - - qm->columns = flecs_balloc(&query->allocators.columns); - qm->storage_columns = flecs_balloc(&query->allocators.columns); - qm->ids = flecs_balloc(&query->allocators.ids); - qm->sources = flecs_balloc(&query->allocators.sources); - - /* Insert match to iteration list if table is not empty */ - if (!table || ecs_table_count(table) != 0) { - flecs_query_insert_table_node(query, qm); - } - - return qm; -} - -static -void flecs_query_set_table_match( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_table_match_t *qm, - ecs_table_t *table, - ecs_iter_t *it) -{ - ecs_allocator_t *a = &world->allocator; - ecs_filter_t *filter = &query->filter; - int32_t i, term_count = filter->term_count; - int32_t field_count = filter->field_count; - ecs_term_t *terms = filter->terms; - - /* Reset resources in case this is an existing record */ - ecs_vec_reset_t(a, &qm->refs, ecs_ref_t); - ecs_os_memcpy_n(qm->columns, it->columns, int32_t, field_count); - ecs_os_memcpy_n(qm->ids, it->ids, ecs_id_t, field_count); - ecs_os_memcpy_n(qm->sources, it->sources, ecs_entity_t, field_count); - - if (table) { - /* Initialize storage columns for faster access to component storage */ - for (i = 0; i < field_count; i ++) { - if (terms[i].inout == EcsInOutNone) { - qm->storage_columns[i] = -1; - continue; - } - - int32_t column = qm->columns[i]; - if (column > 0) { - qm->storage_columns[i] = ecs_table_type_to_column_index(table, - qm->columns[i] - 1); - } else { - /* Shared field (not from table) */ - qm->storage_columns[i] = -2; - } - } - - flecs_entity_filter_init(world, &qm->entity_filter, filter, - table, qm->ids, qm->columns); - - if (qm->entity_filter) { - query->flags &= ~EcsQueryTrivialIter; - } - if (table->flags & EcsTableHasUnion) { - query->flags &= ~EcsQueryTrivialIter; - } - } - - /* Add references for substituted terms */ - for (i = 0; i < term_count; i ++) { - ecs_term_t *term = &terms[i]; - if (!ecs_term_match_this(term)) { - /* non-This terms are set during iteration */ - continue; - } - - int32_t field = terms[i].field_index; - ecs_entity_t src = it->sources[field]; - ecs_size_t size = 0; - if (it->sizes) { - size = it->sizes[field]; - } - if (src) { - ecs_id_t id = it->ids[field]; - ecs_assert(ecs_is_valid(world, src), ECS_INTERNAL_ERROR, NULL); - - if (id) { - flecs_query_add_ref(world, query, qm, id, src, size); - - /* Use column index to bind term and ref */ - if (qm->columns[field] != 0) { - qm->columns[field] = -ecs_vec_count(&qm->refs); - } - } - } - } -} - -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( - ecs_world_t *world, - ecs_query_t *query) -{ - ecs_table_t *table = NULL; - ecs_query_table_t *qt = NULL; - - ecs_iter_t it = ecs_filter_iter(world, &query->filter); - ECS_BIT_SET(it.flags, EcsIterIsInstanced); - ECS_BIT_SET(it.flags, EcsIterNoData); - ECS_BIT_SET(it.flags, EcsIterTableOnly); - ECS_BIT_SET(it.flags, EcsIterEntityOptional); - - while (ecs_filter_next(&it)) { - if ((table != it.table) || (!it.table && !qt)) { - /* New table matched, add record to cache */ - 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); - flecs_query_set_table_match(world, query, qm, table, &it); - } -} - -static -bool flecs_query_match_table( - ecs_world_t *world, - ecs_query_t *query, - ecs_table_t *table) -{ - if (!ecs_map_is_init(&query->cache.index)) { - return false; - } - - ecs_query_table_t *qt = NULL; - ecs_filter_t *filter = &query->filter; - int var_id = ecs_filter_find_this_var(filter); - if (var_id == -1) { - /* If query doesn't match with This term, it can't match with tables */ - return false; - } - - ecs_iter_t it = flecs_filter_iter_w_flags(world, filter, EcsIterMatchVar| - EcsIterIsInstanced|EcsIterNoData|EcsIterEntityOptional); - ecs_iter_set_var_as_table(&it, var_id, table); - - while (ecs_filter_next(&it)) { - ecs_assert(it.table == table, ECS_INTERNAL_ERROR, NULL); - if (qt == NULL) { - 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); - flecs_query_set_table_match(world, query, qm, table, &it); - } - - return qt != NULL; -} - -ECS_SORT_TABLE_WITH_COMPARE(_, flecs_query_sort_table_generic, order_by, static) - -static -void flecs_query_sort_table( - ecs_world_t *world, - ecs_table_t *table, - int32_t column_index, - ecs_order_by_action_t compare, - ecs_sort_table_action_t sort) -{ - ecs_data_t *data = &table->data; - if (!ecs_vec_count(&data->entities)) { - /* Nothing to sort */ - return; - } - - int32_t count = flecs_table_data_count(data); - if (count < 2) { - return; - } - - ecs_entity_t *entities = ecs_vec_first(&data->entities); - - void *ptr = NULL; - int32_t size = 0; - if (column_index != -1) { - ecs_column_t *column = &data->columns[column_index]; - ecs_type_info_t *ti = column->ti; - size = ti->size; - ptr = ecs_vec_first(&column->data); - } - - if (sort) { - sort(world, table, entities, ptr, size, 0, count - 1, compare); - } else { - flecs_query_sort_table_generic(world, table, entities, ptr, size, 0, count - 1, compare); - } -} - -/* Helper struct for building sorted table ranges */ -typedef struct sort_helper_t { - ecs_query_table_match_t *match; - ecs_entity_t *entities; - const void *ptr; - int32_t row; - int32_t elem_size; - int32_t count; - bool shared; -} sort_helper_t; - -static -const void* ptr_from_helper( - sort_helper_t *helper) -{ - ecs_assert(helper->row < helper->count, ECS_INTERNAL_ERROR, NULL); - ecs_assert(helper->elem_size >= 0, ECS_INTERNAL_ERROR, NULL); - ecs_assert(helper->row >= 0, ECS_INTERNAL_ERROR, NULL); - if (helper->shared) { - return helper->ptr; - } else { - return ECS_ELEM(helper->ptr, helper->elem_size, helper->row); - } -} - -static -ecs_entity_t e_from_helper( - sort_helper_t *helper) -{ - if (helper->row < helper->count) { - return helper->entities[helper->row]; - } else { - return 0; - } -} - -static -void flecs_query_build_sorted_table_range( - ecs_query_t *query, - ecs_query_table_list_t *list) -{ - ecs_world_t *world = query->filter.world; - ecs_assert(!(world->flags & EcsWorldMultiThreaded), ECS_UNSUPPORTED, - "cannot sort query in multithreaded mode"); - - ecs_entity_t id = query->order_by_component; - ecs_order_by_action_t compare = query->order_by; - int32_t table_count = list->info.table_count; - if (!table_count) { - return; - } - - ecs_vec_init_if_t(&query->table_slices, ecs_query_table_match_t); - int32_t to_sort = 0; - int32_t order_by_term = query->order_by_term; - - sort_helper_t *helper = flecs_alloc_n( - &world->allocator, sort_helper_t, table_count); - ecs_query_table_match_t *cur, *end = list->last->next; - for (cur = list->first; cur != end; cur = cur->next) { - ecs_table_t *table = cur->table; - ecs_data_t *data = &table->data; - - ecs_assert(ecs_table_count(table) != 0, ECS_INTERNAL_ERROR, NULL); - - if (id) { - const ecs_term_t *term = &query->filter.terms[order_by_term]; - int32_t field = term->field_index; - int32_t column = cur->columns[field]; - ecs_size_t size = query->filter.sizes[field]; - ecs_assert(column != 0, ECS_INTERNAL_ERROR, NULL); - if (column >= 0) { - column = table->column_map[column - 1]; - ecs_vec_t *vec = &data->columns[column].data; - helper[to_sort].ptr = ecs_vec_first(vec); - helper[to_sort].elem_size = size; - helper[to_sort].shared = false; - } else { - ecs_entity_t src = cur->sources[field]; - ecs_assert(src != 0, ECS_INTERNAL_ERROR, NULL); - ecs_record_t *r = flecs_entities_get(world, src); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); - - if (term->src.flags & EcsUp) { - ecs_entity_t base = 0; - ecs_search_relation(world, r->table, 0, id, - EcsIsA, term->src.flags & EcsTraverseFlags, &base, 0, 0); - if (base && base != src) { /* Component could be inherited */ - r = flecs_entities_get(world, base); - } - } - - helper[to_sort].ptr = ecs_table_get_id( - world, r->table, id, ECS_RECORD_TO_ROW(r->row)); - helper[to_sort].elem_size = size; - helper[to_sort].shared = true; - } - ecs_assert(helper[to_sort].ptr != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(helper[to_sort].elem_size != 0, ECS_INTERNAL_ERROR, NULL); - } else { - helper[to_sort].ptr = NULL; - helper[to_sort].elem_size = 0; - helper[to_sort].shared = false; - } - - helper[to_sort].match = cur; - helper[to_sort].entities = ecs_vec_first(&data->entities); - helper[to_sort].row = 0; - helper[to_sort].count = ecs_table_count(table); - to_sort ++; - } - - ecs_assert(to_sort != 0, ECS_INTERNAL_ERROR, NULL); - - bool proceed; - do { - int32_t j, min = 0; - proceed = true; - - ecs_entity_t e1; - while (!(e1 = e_from_helper(&helper[min]))) { - min ++; - if (min == to_sort) { - proceed = false; - break; - } - } - - if (!proceed) { - break; - } - - for (j = min + 1; j < to_sort; j++) { - ecs_entity_t e2 = e_from_helper(&helper[j]); - if (!e2) { - continue; - } - - const void *ptr1 = ptr_from_helper(&helper[min]); - const void *ptr2 = ptr_from_helper(&helper[j]); - - if (compare(e1, ptr1, e2, ptr2) > 0) { - min = j; - e1 = e_from_helper(&helper[min]); - } - } - - sort_helper_t *cur_helper = &helper[min]; - if (!cur || cur->columns != cur_helper->match->columns) { - cur = ecs_vec_append_t(NULL, &query->table_slices, - ecs_query_table_match_t); - *cur = *(cur_helper->match); - cur->offset = cur_helper->row; - cur->count = 1; - } else { - cur->count ++; - } - - cur_helper->row ++; - } while (proceed); - - /* Iterate through the vector of slices to set the prev/next ptrs. This - * can't be done while building the vector, as reallocs may occur */ - int32_t i, count = ecs_vec_count(&query->table_slices); - ecs_query_table_match_t *nodes = ecs_vec_first(&query->table_slices); - for (i = 0; i < count; i ++) { - nodes[i].prev = &nodes[i - 1]; - nodes[i].next = &nodes[i + 1]; - } - - nodes[0].prev = NULL; - nodes[i - 1].next = NULL; - - flecs_free_n(&world->allocator, sort_helper_t, table_count, helper); -} - -static -void flecs_query_build_sorted_tables( - ecs_query_t *query) -{ - ecs_vec_clear(&query->table_slices); - - if (query->group_by) { - /* Populate sorted node list in grouping order */ - ecs_query_table_match_t *cur = query->list.first; - if (cur) { - do { - /* Find list for current group */ - uint64_t group_id = cur->group_id; - ecs_query_table_list_t *list = ecs_map_get_deref( - &query->groups, ecs_query_table_list_t, group_id); - ecs_assert(list != NULL, ECS_INTERNAL_ERROR, NULL); - - /* Sort tables in current group */ - flecs_query_build_sorted_table_range(query, list); - - /* Find next group to sort */ - cur = list->last->next; - } while (cur); - } - } else { - flecs_query_build_sorted_table_range(query, &query->list); - } -} - -static -void flecs_query_sort_tables( - ecs_world_t *world, - ecs_query_t *query) -{ - ecs_order_by_action_t compare = query->order_by; - if (!compare) { - return; - } - - ecs_sort_table_action_t sort = query->sort_table; - - ecs_entity_t order_by_component = query->order_by_component; - int32_t order_by_term = query->order_by_term; - - /* Iterate over non-empty tables. Don't bother with empty tables as they - * have nothing to sort */ - - bool tables_sorted = false; - - ecs_id_record_t *idr = flecs_id_record_get(world, order_by_component); - ecs_table_cache_iter_t it; - ecs_query_table_t *qt; - flecs_table_cache_iter(&query->cache, &it); - - while ((qt = flecs_table_cache_next(&it, ecs_query_table_t))) { - ecs_table_t *table = qt->hdr.table; - bool dirty = false; - - if (flecs_query_check_table_monitor(query, qt, 0)) { - dirty = true; - } - - int32_t column = -1; - if (order_by_component) { - if (flecs_query_check_table_monitor(query, qt, order_by_term + 1)) { - dirty = true; - } - - if (dirty) { - column = -1; - - const ecs_table_record_t *tr = flecs_id_record_get_table( - idr, table); - if (tr) { - column = tr->column; - } - - if (column == -1) { - /* Component is shared, no sorting is needed */ - dirty = false; - } - } - } - - if (!dirty) { - continue; - } - - /* Something has changed, sort the table. Prefers using - * flecs_query_sort_table when available */ - flecs_query_sort_table(world, table, column, compare, sort); - tables_sorted = true; - } - - if (tables_sorted || query->match_count != query->prev_match_count) { - flecs_query_build_sorted_tables(query); - query->match_count ++; /* Increase version if tables changed */ - } -} - -static -bool flecs_query_has_refs( - ecs_query_t *query) -{ - ecs_term_t *terms = query->filter.terms; - int32_t i, count = query->filter.term_count; - for (i = 0; i < count; i ++) { - if (terms[i].src.flags & (EcsUp | EcsIsEntity)) { - return true; - } - } - - return false; -} - -static -void flecs_query_for_each_component_monitor( - ecs_world_t *world, - ecs_query_t *query, - void(*callback)( - ecs_world_t* world, - ecs_id_t id, - ecs_query_t *query)) -{ - ecs_term_t *terms = query->filter.terms; - int32_t i, count = query->filter.term_count; - - for (i = 0; i < count; i++) { - ecs_term_t *term = &terms[i]; - ecs_term_id_t *src = &term->src; - - if (src->flags & EcsUp) { - callback(world, ecs_pair(src->trav, EcsWildcard), query); - if (src->trav != EcsIsA) { - callback(world, ecs_pair(EcsIsA, EcsWildcard), query); - } - callback(world, term->id, query); - - } else if (src->flags & EcsSelf && !ecs_term_match_this(term)) { - callback(world, term->id, query); - } - } -} - -static -bool flecs_query_is_term_id_supported( - ecs_term_id_t *term_id) -{ - if (!(term_id->flags & EcsIsVariable)) { - return true; - } - if (ecs_id_is_wildcard(term_id->id)) { - return true; - } - return false; -} - -static -int flecs_query_process_signature( - ecs_world_t *world, - ecs_query_t *query) -{ - ecs_term_t *terms = query->filter.terms; - int32_t i, count = query->filter.term_count; - - for (i = 0; i < count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_term_id_t *first = &term->first; - ecs_term_id_t *src = &term->src; - ecs_term_id_t *second = &term->second; - ecs_inout_kind_t inout = term->inout; - - bool is_src_ok = flecs_query_is_term_id_supported(src); - bool is_first_ok = flecs_query_is_term_id_supported(first); - bool is_second_ok = flecs_query_is_term_id_supported(second); - - (void)first; - (void)second; - (void)is_src_ok; - (void)is_first_ok; - (void)is_second_ok; - - /* Queries do not support named variables */ - ecs_check(is_src_ok || ecs_term_match_this(term), - ECS_UNSUPPORTED, NULL); - ecs_check(is_first_ok, ECS_UNSUPPORTED, NULL); - ecs_check(is_second_ok, ECS_UNSUPPORTED, NULL); - ecs_check(!(src->flags & EcsFilter), ECS_INVALID_PARAMETER, - "invalid usage of Filter for query"); - - if (inout != EcsIn && inout != EcsInOutNone) { - /* Non-this terms default to EcsIn */ - if (ecs_term_match_this(term) || inout != EcsInOutDefault) { - query->flags |= EcsQueryHasOutTerms; - } - - bool match_non_this = !ecs_term_match_this(term) || - (term->src.flags & EcsUp); - if (match_non_this && inout != EcsInOutDefault) { - query->flags |= EcsQueryHasNonThisOutTerms; - } - } - - if (src->flags & EcsCascade) { - /* Query can only have one cascade column */ - ecs_assert(query->cascade_by == 0, ECS_INVALID_PARAMETER, NULL); - query->cascade_by = i + 1; - } - } - - query->flags |= (ecs_flags32_t)(flecs_query_has_refs(query) * EcsQueryHasRefs); - - if (!(query->flags & EcsQueryIsSubquery)) { - flecs_query_for_each_component_monitor(world, query, flecs_monitor_register); - } - - return 0; -error: - return -1; -} - -/** When a table becomes empty remove it from the query list, or vice versa. */ -static -void flecs_query_update_table( - ecs_query_t *query, - ecs_table_t *table, - bool empty) -{ - int32_t prev_count = ecs_query_table_count(query); - ecs_table_cache_set_empty(&query->cache, table, empty); - int32_t cur_count = ecs_query_table_count(query); - - if (prev_count != cur_count) { - ecs_query_table_t *qt = ecs_table_cache_get(&query->cache, table); - ecs_assert(qt != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_query_table_match_t *cur, *next; - - for (cur = qt->first; cur != NULL; cur = next) { - next = cur->next_match; - - if (empty) { - ecs_assert(ecs_table_count(table) == 0, - ECS_INTERNAL_ERROR, NULL); - - flecs_query_remove_table_node(query, cur); - } else { - ecs_assert(ecs_table_count(table) != 0, - ECS_INTERNAL_ERROR, NULL); - flecs_query_insert_table_node(query, cur); - } - } - } - - ecs_assert(cur_count || query->list.first == NULL, - ECS_INTERNAL_ERROR, NULL); -} - -static -void flecs_query_add_subquery( - ecs_world_t *world, - ecs_query_t *parent, - ecs_query_t *subquery) -{ - ecs_vec_init_if_t(&parent->subqueries, ecs_query_t*); - ecs_query_t **elem = ecs_vec_append_t( - NULL, &parent->subqueries, ecs_query_t*); - *elem = subquery; - - ecs_table_cache_t *cache = &parent->cache; - ecs_table_cache_iter_t it; - ecs_query_table_t *qt; - flecs_table_cache_all_iter(cache, &it); - while ((qt = flecs_table_cache_next(&it, ecs_query_table_t))) { - flecs_query_match_table(world, subquery, qt->hdr.table); - } -} - -static -void flecs_query_notify_subqueries( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_event_t *event) -{ - if (query->subqueries.array) { - ecs_query_t **queries = ecs_vec_first(&query->subqueries); - int32_t i, count = ecs_vec_count(&query->subqueries); - - ecs_query_event_t sub_event = *event; - sub_event.parent_query = query; - - for (i = 0; i < count; i ++) { - ecs_query_t *sub = queries[i]; - flecs_query_notify(world, sub, &sub_event); - } - } -} - -/* Remove table */ -static -void flecs_query_table_match_free( - ecs_query_t *query, - ecs_query_table_t *elem, - ecs_query_table_match_t *first) -{ - ecs_query_table_match_t *cur, *next; - ecs_world_t *world = query->filter.world; - - for (cur = first; cur != NULL; cur = next) { - flecs_bfree(&query->allocators.columns, cur->columns); - flecs_bfree(&query->allocators.columns, cur->storage_columns); - flecs_bfree(&query->allocators.ids, cur->ids); - flecs_bfree(&query->allocators.sources, cur->sources); - - if (cur->monitor) { - flecs_bfree(&query->allocators.monitors, cur->monitor); - } - if (!elem->hdr.empty) { - flecs_query_remove_table_node(query, cur); - } - - ecs_vec_fini_t(&world->allocator, &cur->refs, ecs_ref_t); - flecs_entity_filter_fini(world, cur->entity_filter); - - next = cur->next_match; - - flecs_bfree(&world->allocators.query_table_match, cur); - } -} - -static -void flecs_query_table_free( - ecs_query_t *query, - ecs_query_table_t *elem) -{ - flecs_query_table_match_free(query, elem, elem->first); - flecs_bfree(&query->filter.world->allocators.query_table, elem); -} - -static -void flecs_query_unmatch_table( - ecs_query_t *query, - ecs_table_t *table, - ecs_query_table_t *elem) -{ - 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); - } -} - -/* Rematch system with tables after a change happened to a watched entity */ -static -void flecs_query_rematch_tables( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_t *parent_query) -{ - ecs_iter_t it, parent_it; - ecs_table_t *table = NULL; - ecs_query_table_t *qt = NULL; - ecs_query_table_match_t *qm = NULL; - - if (query->monitor_generation == world->monitor_generation) { - return; - } - - query->monitor_generation = world->monitor_generation; - - if (parent_query) { - parent_it = ecs_query_iter(world, parent_query); - it = ecs_filter_chain_iter(&parent_it, &query->filter); - } else { - it = ecs_filter_iter(world, &query->filter); - } - - ECS_BIT_SET(it.flags, EcsIterIsInstanced); - ECS_BIT_SET(it.flags, EcsIterNoData); - ECS_BIT_SET(it.flags, EcsIterEntityOptional); - - world->info.rematch_count_total ++; - int32_t rematch_count = ++ query->rematch_count; - - ecs_time_t t = {0}; - if (world->flags & EcsWorldMeasureFrameTime) { - ecs_time_measure(&t); - } - - while (ecs_filter_next(&it)) { - if ((table != it.table) || (!it.table && !qt)) { - if (qm && qm->next_match) { - flecs_query_table_match_free(query, qt, qm->next_match); - qm->next_match = NULL; - } - - table = it.table; - - qt = ecs_table_cache_get(&query->cache, table); - if (!qt) { - qt = flecs_query_table_insert(world, query, table); - } - - ecs_assert(qt->hdr.table == table, ECS_INTERNAL_ERROR, NULL); - qt->rematch_count = rematch_count; - qm = NULL; - } - if (!qm) { - qm = qt->first; - } else { - qm = qm->next_match; - } - if (!qm) { - qm = flecs_query_add_table_match(query, qt, table); - } - - flecs_query_set_table_match(world, query, qm, table, &it); - - if (table && ecs_table_count(table) && query->group_by) { - if (flecs_query_get_group_id(query, table) != qm->group_id) { - /* Update table group */ - flecs_query_remove_table_node(query, qm); - flecs_query_insert_table_node(query, qm); - } - } - } - - if (qm && qm->next_match) { - flecs_query_table_match_free(query, qt, qm->next_match); - qm->next_match = NULL; - } - - /* Iterate all tables in cache, remove ones that weren't just matched */ - ecs_table_cache_iter_t cache_it; - 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, qt); - } - } - } - - if (world->flags & EcsWorldMeasureFrameTime) { - world->info.rematch_time_total += (ecs_ftime_t)ecs_time_measure(&t); - } -} - -static -void flecs_query_remove_subquery( - ecs_query_t *parent, - ecs_query_t *sub) -{ - ecs_assert(parent != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(sub != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(parent->subqueries.array, ECS_INTERNAL_ERROR, NULL); - - int32_t i, count = ecs_vec_count(&parent->subqueries); - ecs_query_t **sq = ecs_vec_first(&parent->subqueries); - - for (i = 0; i < count; i ++) { - if (sq[i] == sub) { - break; - } - } - - ecs_vec_remove_t(&parent->subqueries, ecs_query_t*, i); -} - -/* -- Private API -- */ - -void flecs_query_notify( - ecs_world_t *world, - ecs_query_t *query, - ecs_query_event_t *event) -{ - bool notify = true; - - switch(event->kind) { - case EcsQueryTableMatch: - /* Creation of new table */ - if (flecs_query_match_table(world, query, event->table)) { - if (query->subqueries.array) { - flecs_query_notify_subqueries(world, query, event); - } - } - notify = false; - break; - case EcsQueryTableUnmatch: - /* Deletion of table */ - flecs_query_unmatch_table(query, event->table, NULL); - break; - case EcsQueryTableRematch: - /* Rematch tables of query */ - flecs_query_rematch_tables(world, query, event->parent_query); - break; - case EcsQueryOrphan: - ecs_assert(query->flags & EcsQueryIsSubquery, ECS_INTERNAL_ERROR, NULL); - query->flags |= EcsQueryIsOrphaned; - query->parent = NULL; - break; - } - - if (notify) { - flecs_query_notify_subqueries(world, query, event); - } -} - -static -void flecs_query_order_by( - ecs_world_t *world, - ecs_query_t *query, - ecs_entity_t order_by_component, - ecs_order_by_action_t order_by, - ecs_sort_table_action_t action) -{ - ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(query->flags & EcsQueryIsOrphaned), ECS_INVALID_PARAMETER, NULL); - ecs_check(!ecs_id_is_wildcard(order_by_component), - ECS_INVALID_PARAMETER, NULL); - - /* Find order_by_component term & make sure it is queried for */ - const ecs_filter_t *filter = &query->filter; - int32_t i, count = filter->term_count; - int32_t order_by_term = -1; - - if (order_by_component) { - for (i = 0; i < count; i ++) { - ecs_term_t *term = &filter->terms[i]; - - /* Only And terms are supported */ - if (term->id == order_by_component && term->oper == EcsAnd) { - order_by_term = i; - break; - } - } - - ecs_check(order_by_term != -1, ECS_INVALID_PARAMETER, - "sorted component not is queried for"); - } - - query->order_by_component = order_by_component; - query->order_by = order_by; - query->order_by_term = order_by_term; - query->sort_table = action; - - ecs_vec_fini_t(NULL, &query->table_slices, ecs_query_table_match_t); - flecs_query_sort_tables(world, query); - - if (!query->table_slices.array) { - flecs_query_build_sorted_tables(query); - } - - query->flags &= ~EcsQueryTrivialIter; -error: - return; -} - -static -void flecs_query_group_by( - ecs_query_t *query, - ecs_entity_t sort_component, - ecs_group_by_action_t group_by) -{ - /* Cannot change grouping once a query has been created */ - ecs_check(query->group_by_id == 0, ECS_INVALID_OPERATION, NULL); - ecs_check(query->group_by == 0, ECS_INVALID_OPERATION, NULL); - - if (!group_by) { - /* Builtin function that groups by relationship */ - group_by = flecs_query_default_group_by; - } - - query->group_by_id = sort_component; - query->group_by = group_by; - - ecs_map_init_w_params(&query->groups, - &query->filter.world->allocators.query_table_list); -error: - return; -} - -/* Implementation for iterable mixin */ -static -void flecs_query_iter_init( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter) -{ - ecs_poly_assert(poly, ecs_query_t); - - if (filter) { - iter[1] = ecs_query_iter(world, ECS_CONST_CAST(ecs_query_t*, poly)); - iter[0] = ecs_term_chain_iter(&iter[1], filter); - } else { - iter[0] = ecs_query_iter(world, ECS_CONST_CAST(ecs_query_t*, poly)); - } -} - -static -void flecs_query_on_event( - ecs_iter_t *it) -{ - /* Because this is the observer::run callback, checking if this is event is - * already handled is not done for us. */ - ecs_world_t *world = it->world; - ecs_observer_t *o = it->ctx; - if (o->last_event_id) { - if (o->last_event_id[0] == world->event_id) { - return; - } - o->last_event_id[0] = world->event_id; - } - - 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.array) { - 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 - * efficiently by checking the table with the query cache. */ - if (ecs_table_cache_get(&query->cache, table) == NULL) { - return; - } - - 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.array) { - ecs_query_event_t evt = { - .kind = EcsQueryTableUnmatch, - .table = table, - .parent_query = query - }; - flecs_query_notify_subqueries(world, query, &evt); - } - return; - } -} - -static -void flecs_query_table_cache_free( - ecs_query_t *query) -{ - ecs_table_cache_iter_t it; - ecs_query_table_t *qt; - - if (flecs_table_cache_all_iter(&query->cache, &it)) { - while ((qt = flecs_table_cache_next(&it, ecs_query_table_t))) { - flecs_query_table_free(query, qt); - } - } - - ecs_table_cache_fini(&query->cache); -} - -static -void flecs_query_allocators_init( - ecs_query_t *query) -{ - int32_t field_count = query->filter.field_count; - if (field_count) { - flecs_ballocator_init(&query->allocators.columns, - field_count * ECS_SIZEOF(int32_t)); - flecs_ballocator_init(&query->allocators.ids, - field_count * ECS_SIZEOF(ecs_id_t)); - flecs_ballocator_init(&query->allocators.sources, - field_count * ECS_SIZEOF(ecs_entity_t)); - flecs_ballocator_init(&query->allocators.monitors, - (1 + field_count) * ECS_SIZEOF(int32_t)); - } -} - -static -void flecs_query_allocators_fini( - ecs_query_t *query) -{ - int32_t field_count = query->filter.field_count; - if (field_count) { - flecs_ballocator_fini(&query->allocators.columns); - flecs_ballocator_fini(&query->allocators.ids); - flecs_ballocator_fini(&query->allocators.sources); - flecs_ballocator_fini(&query->allocators.monitors); - } -} - -static -void flecs_query_fini( - ecs_query_t *query) -{ - ecs_world_t *world = query->filter.world; - - ecs_group_delete_action_t on_delete = query->on_group_delete; - if (on_delete) { - ecs_map_iter_t it = ecs_map_iter(&query->groups); - while (ecs_map_next(&it)) { - ecs_query_table_list_t *group = ecs_map_ptr(&it); - uint64_t group_id = ecs_map_key(&it); - on_delete(world, group_id, group->info.ctx, query->group_by_ctx); - } - query->on_group_delete = NULL; - } - - if (query->group_by_ctx_free) { - if (query->group_by_ctx) { - query->group_by_ctx_free(query->group_by_ctx); - } - } - - if ((query->flags & EcsQueryIsSubquery) && - !(query->flags & EcsQueryIsOrphaned)) - { - flecs_query_remove_subquery(query->parent, query); - } - - flecs_query_notify_subqueries(world, query, &(ecs_query_event_t){ - .kind = EcsQueryOrphan - }); - - flecs_query_for_each_component_monitor(world, query, - flecs_monitor_unregister); - flecs_query_table_cache_free(query); - - ecs_map_fini(&query->groups); - - ecs_vec_fini_t(NULL, &query->subqueries, ecs_query_t*); - ecs_vec_fini_t(NULL, &query->table_slices, ecs_query_table_match_t); - ecs_filter_fini(&query->filter); - - flecs_query_allocators_fini(query); - - if (query->ctx_free) { - query->ctx_free(query->ctx); - } - if (query->binding_ctx_free) { - query->binding_ctx_free(query->binding_ctx); - } - - ecs_poly_free(query, ecs_query_t); -} - -/* -- Public API -- */ - -ecs_query_t* ecs_query_init( - ecs_world_t *world, - const ecs_query_desc_t *desc) -{ - ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, NULL); - - ecs_query_t *result = ecs_poly_new(ecs_query_t); - ecs_observer_desc_t observer_desc = { .filter = desc->filter }; - ecs_entity_t entity = desc->filter.entity; - - observer_desc.filter.flags = EcsFilterMatchEmptyTables; - observer_desc.filter.storage = &result->filter; - result->filter = ECS_FILTER_INIT; - - if (ecs_filter_init(world, &observer_desc.filter) == NULL) { - goto error; - } - - ECS_BIT_COND(result->flags, EcsQueryTrivialIter, - !!(result->filter.flags & EcsFilterMatchOnlyThis)); - - flecs_query_allocators_init(result); - - if (result->filter.term_count) { - observer_desc.entity = entity; - observer_desc.run = flecs_query_on_event; - 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; - - /* ecs_filter_init could have moved away resources from the terms array - * in the descriptor, so use the terms array from the filter. */ - observer_desc.filter.terms_buffer = result->filter.terms; - observer_desc.filter.terms_buffer_count = result->filter.term_count; - observer_desc.filter.expr = NULL; /* Already parsed */ - - entity = ecs_observer_init(world, &observer_desc); - if (!entity) { - goto error; - } - } - - result->iterable.init = flecs_query_iter_init; - result->dtor = (ecs_poly_dtor_t)flecs_query_fini; - result->prev_match_count = -1; - - result->ctx = desc->ctx; - result->binding_ctx = desc->binding_ctx; - result->ctx_free = desc->ctx_free; - result->binding_ctx_free = desc->binding_ctx_free; - - if (ecs_should_log_1()) { - char *filter_expr = ecs_filter_str(world, &result->filter); - ecs_dbg_1("#[green]query#[normal] [%s] created", - filter_expr ? filter_expr : ""); - ecs_os_free(filter_expr); - } - - ecs_log_push_1(); - - if (flecs_query_process_signature(world, result)) { - goto error; - } - - /* Group before matching so we won't have to move tables around later */ - int32_t cascade_by = result->cascade_by; - if (cascade_by) { - flecs_query_group_by(result, result->filter.terms[cascade_by - 1].id, - flecs_query_group_by_cascade); - result->group_by_ctx = &result->filter.terms[cascade_by - 1]; - } - - if (desc->group_by || desc->group_by_id) { - /* Can't have a cascade term and group by at the same time, as cascade - * uses the group_by mechanism */ - ecs_check(!result->cascade_by, ECS_INVALID_PARAMETER, NULL); - flecs_query_group_by(result, desc->group_by_id, desc->group_by); - result->group_by_ctx = desc->group_by_ctx; - result->on_group_create = desc->on_group_create; - result->on_group_delete = desc->on_group_delete; - result->group_by_ctx_free = desc->group_by_ctx_free; - } - - if (desc->parent != NULL) { - result->flags |= EcsQueryIsSubquery; - } - - /* If the query refers to itself, add the components that were queried for - * to the query itself. */ - if (entity) { - int32_t t, term_count = result->filter.term_count; - ecs_term_t *terms = result->filter.terms; - - for (t = 0; t < term_count; t ++) { - ecs_term_t *term = &terms[t]; - if (term->src.id == entity) { - ecs_add_id(world, entity, term->id); - } - } - } - - if (!entity) { - entity = ecs_new_id(world); - } - - EcsPoly *poly = ecs_poly_bind(world, entity, ecs_query_t); - if (poly->poly) { - /* If entity already had poly query, delete previous */ - flecs_query_fini(poly->poly); - } - poly->poly = result; - result->filter.entity = entity; - - /* Ensure that while initially populating the query with tables, they are - * in the right empty/non-empty list. This ensures the query won't miss - * empty/non-empty events for tables that are currently out of sync, but - * change back to being in sync before processing pending events. */ - ecs_run_aperiodic(world, EcsAperiodicEmptyTables); - - ecs_table_cache_init(world, &result->cache); - - if (!desc->parent) { - flecs_query_match_tables(world, result); - } else { - flecs_query_add_subquery(world, desc->parent, result); - result->parent = desc->parent; - } - - if (desc->order_by) { - flecs_query_order_by( - world, result, desc->order_by_component, desc->order_by, - desc->sort_table); - } - - if (!ecs_query_table_count(result) && result->filter.term_count) { - ecs_add_id(world, entity, EcsEmpty); - } - - ecs_poly_modified(world, entity, ecs_query_t); - - ecs_log_pop_1(); - - return result; -error: - if (result) { - ecs_filter_fini(&result->filter); - ecs_os_free(result); - } - return NULL; -} - -void ecs_query_fini( - ecs_query_t *query) -{ - ecs_poly_assert(query, ecs_query_t); - ecs_delete(query->filter.world, query->filter.entity); -} - -const ecs_filter_t* ecs_query_get_filter( - const ecs_query_t *query) -{ - ecs_poly_assert(query, ecs_query_t); - return &query->filter; -} - -static -void flecs_query_set_var( - ecs_iter_t *it) -{ - ecs_check(it->constrained_vars == 1, ECS_INVALID_OPERATION, - "can only set $this variable for queries"); - - ecs_var_t *var = &it->variables[0]; - ecs_table_t *table = var->range.table; - if (!table) { - goto nodata; - } - - ecs_query_iter_t *qit = &it->priv.iter.query; - ecs_query_t *query = qit->query; - ecs_query_table_t *qt = ecs_table_cache_get(&query->cache, table); - if (!qt) { - goto nodata; - } - - qit->node = qt->first; - qit->last = qt->last->next_match; - it->offset = var->range.offset; - it->count = var->range.count; - return; -error: -nodata: - it->priv.iter.query.node = NULL; - it->priv.iter.query.last = NULL; - return; -} - -ecs_iter_t ecs_query_iter( - const ecs_world_t *stage, - ecs_query_t *query) -{ - ecs_poly_assert(query, ecs_query_t); - ecs_check(!(query->flags & EcsQueryIsOrphaned), - ECS_INVALID_PARAMETER, NULL); - - ecs_world_t *world = query->filter.world; - ecs_poly_assert(world, ecs_world_t); - - /* Process table events to ensure that the list of iterated tables doesn't - * contain empty tables. */ - flecs_process_pending_tables(world); - - /* If query has order_by, apply sort */ - flecs_query_sort_tables(world, query); - - /* If monitors changed, do query rematching */ - if (!(world->flags & EcsWorldReadonly) && query->flags & EcsQueryHasRefs) { - flecs_eval_component_monitors(world); - } - - /* Prepare iterator */ - - int32_t table_count; - if (ecs_vec_count(&query->table_slices)) { - table_count = ecs_vec_count(&query->table_slices); - } else { - table_count = ecs_query_table_count(query); - } - - ecs_query_iter_t it = { - .query = query, - .node = query->list.first, - .last = NULL - }; - - if (query->order_by && query->list.info.table_count) { - it.node = ecs_vec_first(&query->table_slices); - } - - ecs_iter_t result = { - .real_world = world, - .world = ECS_CONST_CAST(ecs_world_t*, stage), - .terms = query->filter.terms, - .field_count = query->filter.field_count, - .table_count = table_count, - .variable_count = 1, - .priv.iter.query = it, - .next = ecs_query_next, - .set_var = flecs_query_set_var - }; - - flecs_filter_apply_iter_flags(&result, &query->filter); - - ecs_filter_t *filter = &query->filter; - ecs_iter_t fit; - if (!(query->flags & EcsQueryTrivialIter)) { - /* Check if non-This terms (like singleton terms) still match */ - if (!(filter->flags & EcsFilterMatchOnlyThis)) { - fit = flecs_filter_iter_w_flags(ECS_CONST_CAST(ecs_world_t*, stage), - &query->filter, EcsIterIgnoreThis); - if (!ecs_filter_next(&fit)) { - /* No match, so return nothing */ - goto noresults; - } - } - - flecs_iter_init(stage, &result, flecs_iter_cache_all); - - /* Copy the data */ - if (!(filter->flags & EcsFilterMatchOnlyThis)) { - int32_t field_count = filter->field_count; - if (field_count) { - if (result.ptrs) { - ecs_os_memcpy_n(result.ptrs, fit.ptrs, void*, field_count); - } - ecs_os_memcpy_n(result.ids, fit.ids, ecs_id_t, field_count); - ecs_os_memcpy_n(result.columns, fit.columns, int32_t, field_count); - ecs_os_memcpy_n(result.sources, fit.sources, int32_t, field_count); - } - ecs_iter_fini(&fit); - } - } else { - /* Trivial iteration, use arrays from query cache */ - flecs_iter_init(stage, &result, - flecs_iter_cache_ptrs|flecs_iter_cache_variables); - } - - result.sizes = query->filter.sizes; - - return result; -error: -noresults: - result.priv.iter.query.node = NULL; - return result; -} - -void ecs_query_set_group( - ecs_iter_t *it, - uint64_t group_id) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_PARAMETER, NULL); - - ecs_query_iter_t *qit = &it->priv.iter.query; - ecs_query_t *q = qit->query; - ecs_check(q != NULL, ECS_INVALID_PARAMETER, NULL); - - ecs_query_table_list_t *node = flecs_query_get_group(q, group_id); - if (!node) { - qit->node = NULL; - return; - } - - ecs_query_table_match_t *first = node->first; - if (first) { - qit->node = node->first; - qit->last = node->last->next; - } else { - qit->node = NULL; - qit->last = NULL; - } - -error: - return; -} - -const ecs_query_group_info_t* ecs_query_get_group_info( - const ecs_query_t *query, - uint64_t group_id) -{ - ecs_query_table_list_t *node = flecs_query_get_group(query, group_id); - if (!node) { - return NULL; - } - - return &node->info; -} - -void* ecs_query_get_group_ctx( - const ecs_query_t *query, - uint64_t group_id) -{ - const ecs_query_group_info_t *info = - ecs_query_get_group_info(query, group_id); - if (!info) { - return NULL; - } else { - return info->ctx; - } -} - -static -void flecs_query_mark_columns_dirty( - ecs_query_t *query, - ecs_query_table_match_t *qm) -{ - ecs_table_t *table = qm->table; - ecs_filter_t *filter = &query->filter; - if ((table && table->dirty_state) || (query->flags & EcsQueryHasNonThisOutTerms)) { - ecs_term_t *terms = filter->terms; - int32_t i, count = filter->term_count; - - for (i = 0; i < count; i ++) { - ecs_term_t *term = &terms[i]; - ecs_inout_kind_t inout = term->inout; - if (inout == EcsIn || inout == EcsInOutNone) { - /* Don't mark readonly terms dirty */ - continue; - } - - flecs_table_column_t tc; - flecs_query_get_column_for_term(query, qm, i, &tc); - - if (tc.column == -1) { - continue; - } - - ecs_assert(tc.table != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t *dirty_state = tc.table->dirty_state; - if (!dirty_state) { - continue; - } - - if (table != tc.table) { - if (inout == EcsInOutDefault) { - continue; - } - } - - ecs_assert(tc.column >= 0, ECS_INTERNAL_ERROR, NULL); - - dirty_state[tc.column + 1] ++; - } - } -} - -bool ecs_query_next_table( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - - flecs_iter_validate(it); - - ecs_query_iter_t *iter = &it->priv.iter.query; - ecs_query_table_match_t *node = iter->node; - ecs_query_t *query = iter->query; - - ecs_query_table_match_t *prev = iter->prev; - if (prev) { - if (query->flags & EcsQueryHasMonitor) { - flecs_query_sync_match_monitor(query, prev); - } - if (query->flags & EcsQueryHasOutTerms) { - if (it->count) { - flecs_query_mark_columns_dirty(query, prev); - } - } - } - - if (node != iter->last) { - it->table = node->table; - it->group_id = node->group_id; - it->count = 0; - iter->node = node->next; - iter->prev = node; - return true; - } - -error: - query->match_count = query->prev_match_count; - ecs_iter_fini(it); - return false; -} - -static -void flecs_query_populate_trivial( - ecs_iter_t *it, - ecs_query_table_match_t *match) -{; - ecs_table_t *table = match->table; - int32_t offset, count; - if (!it->constrained_vars) { - it->offset = offset = 0; - it->count = count = ecs_table_count(table); - } else { - offset = it->offset; - count = it->count; - } - - it->ids = match->ids; - it->sources = match->sources; - it->columns = match->columns; - it->group_id = match->group_id; - it->instance_count = 0; - it->references = ecs_vec_first(&match->refs); - - if (!it->references) { - ecs_data_t *data = &table->data; - if (!(it->flags & EcsIterNoData)) { - int32_t i; - for (i = 0; i < it->field_count; i ++) { - int32_t column = match->storage_columns[i]; - if (column < 0) { - it->ptrs[i] = NULL; - continue; - } - - ecs_size_t size = it->sizes[i]; - if (!size) { - it->ptrs[i] = NULL; - continue; - } - - it->ptrs[i] = ecs_vec_get(&data->columns[column].data, - it->sizes[i], offset); - } - } - - it->frame_offset += it->table ? ecs_table_count(it->table) : 0; - it->table = table; - it->entities = ecs_vec_get_t(&data->entities, ecs_entity_t, offset); - } else { - flecs_iter_populate_data( - it->real_world, it, table, offset, count, it->ptrs); - } -} - -int ecs_query_populate( - ecs_iter_t *it, - bool when_changed) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), - ECS_INVALID_PARAMETER, NULL); - - ecs_query_iter_t *iter = &it->priv.iter.query; - ecs_query_t *query = iter->query; - ecs_query_table_match_t *match = iter->prev; - ecs_assert(match != NULL, ECS_INVALID_OPERATION, NULL); - if (query->flags & EcsQueryTrivialIter) { - flecs_query_populate_trivial(it, match); - return EcsIterNextYield; - } - - ecs_table_t *table = match->table; - ecs_world_t *world = query->filter.world; - const ecs_filter_t *filter = &query->filter; - ecs_entity_filter_iter_t *ent_it = it->priv.entity_iter; - ecs_assert(ent_it != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_table_range_t *range = &ent_it->range; - int32_t t, term_count = filter->term_count; - int result; - -repeat: - result = EcsIterNextYield; - - ecs_os_memcpy_n(it->sources, match->sources, ecs_entity_t, - filter->field_count); - - for (t = 0; t < term_count; t ++) { - ecs_term_t *term = &filter->terms[t]; - int32_t field = term->field_index; - if (!ecs_term_match_this(term)) { - continue; - } - - it->ids[field] = match->ids[field]; - it->columns[field] = match->columns[field]; - } - - if (table) { - range->offset = match->offset; - range->count = match->count; - if (!range->count) { - range->count = ecs_table_count(table); - ecs_assert(range->count != 0, ECS_INTERNAL_ERROR, NULL); - } - - if (match->entity_filter) { - ent_it->entity_filter = match->entity_filter; - ent_it->columns = match->columns; - ent_it->range.table = table; - ent_it->it = it; - result = flecs_entity_filter_next(ent_it); - if (result == EcsIterNext) { - goto done; - } - } - - it->group_id = match->group_id; - } else { - range->offset = 0; - range->count = 0; - } - - if (when_changed) { - if (!ecs_query_changed(NULL, it)) { - if (result == EcsIterYield) { - goto repeat; - } else { - result = EcsIterNext; - goto done; - } - } - } - - it->references = ecs_vec_first(&match->refs); - it->instance_count = 0; - - flecs_iter_populate_data(world, it, table, range->offset, range->count, - it->ptrs); - -error: -done: - return result; -} - -bool ecs_query_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - - if (flecs_iter_next_row(it)) { - return true; - } - - return flecs_iter_next_instanced(it, ecs_query_next_instanced(it)); -error: - return false; -} - -bool ecs_query_next_instanced( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - - ecs_query_iter_t *iter = &it->priv.iter.query; - ecs_query_t *query = iter->query; - ecs_flags32_t flags = query->flags; - - ecs_query_table_match_t *prev, *next, *cur = iter->node, *last = iter->last; - if ((prev = iter->prev)) { - /* Match has been iterated, update monitor for change tracking */ - if (flags & EcsQueryHasMonitor) { - flecs_query_sync_match_monitor(query, prev); - } - if (flags & EcsQueryHasOutTerms) { - flecs_query_mark_columns_dirty(query, prev); - } - } - - flecs_iter_validate(it); - iter->skip_count = 0; - - /* Trivial iteration: each entry in the cache is a full match and ids are - * only matched on $this or through traversal starting from $this. */ - if (flags & EcsQueryTrivialIter) { - if (cur == last) { - goto done; - } - iter->node = cur->next; - iter->prev = cur; - flecs_query_populate_trivial(it, cur); - return true; - } - - /* Non-trivial iteration: query matches with static sources, or matches with - * tables that require per-entity filtering. */ - for (; cur != last; cur = next) { - next = cur->next; - iter->prev = cur; - switch(ecs_query_populate(it, false)) { - case EcsIterNext: iter->node = next; continue; - case EcsIterYield: next = cur; /* fall through */ - case EcsIterNextYield: goto yield; - default: ecs_abort(ECS_INTERNAL_ERROR, NULL); - } - } - -done: error: - query->match_count = query->prev_match_count; - ecs_iter_fini(it); - return false; - -yield: - iter->node = next; - iter->prev = cur; - return true; -} - -bool ecs_query_changed( - ecs_query_t *query, - const ecs_iter_t *it) -{ - if (it) { - ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - ecs_check(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), - ECS_INVALID_PARAMETER, NULL); - - ecs_query_table_match_t *qm = - (ecs_query_table_match_t*)it->priv.iter.query.prev; - ecs_assert(qm != NULL, ECS_INVALID_PARAMETER, NULL); - - if (!query) { - query = it->priv.iter.query.query; - } else { - ecs_check(query == it->priv.iter.query.query, - ECS_INVALID_PARAMETER, NULL); - } - - ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_poly_assert(query, ecs_query_t); - - return flecs_query_check_match_monitor(query, qm, it); - } - - ecs_poly_assert(query, ecs_query_t); - ecs_check(!(query->flags & EcsQueryIsOrphaned), - ECS_INVALID_PARAMETER, NULL); - - flecs_process_pending_tables(query->filter.world); - - if (!(query->flags & EcsQueryHasMonitor)) { - query->flags |= EcsQueryHasMonitor; - flecs_query_init_query_monitors(query); - return true; /* Monitors didn't exist yet */ - } - - if (query->match_count != query->prev_match_count) { - return true; - } - - return flecs_query_check_query_monitor(query); -error: - return false; -} - -void ecs_query_skip( - ecs_iter_t *it) -{ - ecs_assert(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); - ecs_assert(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), - ECS_INVALID_PARAMETER, NULL); - - if (it->instance_count > it->count) { - it->priv.iter.query.skip_count ++; - if (it->priv.iter.query.skip_count == it->instance_count) { - /* For non-instanced queries, make sure all entities are skipped */ - it->priv.iter.query.prev = NULL; - } - } else { - it->priv.iter.query.prev = NULL; - } -} - -bool ecs_query_orphaned( - const ecs_query_t *query) -{ - ecs_poly_assert(query, ecs_query_t); - return query->flags & EcsQueryIsOrphaned; -} - -char* ecs_query_str( - const ecs_query_t *query) -{ - return ecs_filter_str(query->filter.world, &query->filter); -} - -int32_t ecs_query_table_count( - const ecs_query_t *query) -{ - ecs_run_aperiodic(query->filter.world, EcsAperiodicEmptyTables); - return query->cache.tables.count; -} - -int32_t ecs_query_empty_table_count( - const ecs_query_t *query) -{ - ecs_run_aperiodic(query->filter.world, EcsAperiodicEmptyTables); - return query->cache.empty_tables.count; -} - -int32_t ecs_query_entity_count( - const ecs_query_t *query) -{ - ecs_run_aperiodic(query->filter.world, EcsAperiodicEmptyTables); - - int32_t result = 0; - ecs_table_cache_hdr_t *cur, *last = query->cache.tables.last; - if (!last) { - return 0; - } - - for (cur = query->cache.tables.first; cur != NULL; cur = cur->next) { - result += ecs_table_count(cur->table); - } - - return result; -} - -void* ecs_query_get_ctx( - const ecs_query_t *query) -{ - return query->ctx; -} - -void* ecs_query_get_binding_ctx( - const ecs_query_t *query) -{ - return query->binding_ctx; -} diff --git a/vendors/flecs/src/query/api.c b/vendors/flecs/src/query/api.c new file mode 100644 index 000000000..6924e959d --- /dev/null +++ b/vendors/flecs/src/query/api.c @@ -0,0 +1,543 @@ + /** + * @file queries/api.c + * @brief User facing API for rules. + */ + +#include "../private_api.h" +#include + +/* Placeholder arrays for queries that only have $this variable */ +ecs_query_var_t flecs_this_array = { + .kind = EcsVarTable, + .table_id = EcsVarNone +}; +char *flecs_this_name_array = NULL; + +ecs_mixins_t ecs_query_t_mixins = { + .type_name = "ecs_query_t", + .elems = { + [EcsMixinWorld] = offsetof(ecs_query_impl_t, pub.real_world), + [EcsMixinEntity] = offsetof(ecs_query_impl_t, pub.entity), + [EcsMixinDtor] = offsetof(ecs_query_impl_t, dtor) + } +}; + +int32_t ecs_query_find_var( + const ecs_query_t *q, + const char *name) +{ + flecs_poly_assert(q, ecs_query_t); + + ecs_query_impl_t *impl = flecs_query_impl(q); + ecs_var_id_t var_id = flecs_query_find_var_id(impl, name, EcsVarEntity); + if (var_id == EcsVarNone) { + if (q->flags & EcsQueryMatchThis) { + if (!ecs_os_strcmp(name, EcsThisName)) { + var_id = 0; + } + } + if (var_id == EcsVarNone) { + return -1; + } + } + return (int32_t)var_id; +} + +const char* ecs_query_var_name( + const ecs_query_t *q, + int32_t var_id) +{ + flecs_poly_assert(q, ecs_query_t); + + if (var_id) { + ecs_assert(var_id < flecs_query_impl(q)->var_count, + ECS_INVALID_PARAMETER, NULL); + return flecs_query_impl(q)->vars[var_id].name; + } else { + return EcsThisName; + } +} + +bool ecs_query_var_is_entity( + const ecs_query_t *q, + int32_t var_id) +{ + flecs_poly_assert(q, ecs_query_t); + + return flecs_query_impl(q)->vars[var_id].kind == EcsVarEntity; +} + +static +int flecs_query_set_caching_policy( + ecs_query_impl_t *impl, + const ecs_query_desc_t *desc) +{ + ecs_query_cache_kind_t kind = desc->cache_kind; + bool group_order_by = desc->group_by || desc->group_by_callback || + desc->order_by || desc->order_by_callback; + + /* If caching policy is default, try to pick a policy that does the right + * thing in most cases. */ + if (kind == EcsQueryCacheDefault) { + if (desc->entity || group_order_by) { + /* If the query is created with an entity handle (typically + * indicating that the query is named or belongs to a system) the + * chance is very high that the query will be reused, so enable + * caching. + * Additionally, if the query uses features that require a cache + * such as group_by/order_by, also enable caching. */ + kind = EcsQueryCacheAuto; + } else { + /* Be conservative in other scenario's, as caching adds significant + * overhead to the cost of query creation which doesn't offset the + * benefit of faster iteration if it's only used once. */ + kind = EcsQueryCacheNone; + } + } + + /* Don't cache query, even if it has cacheable terms */ + if (kind == EcsQueryCacheNone) { + impl->pub.cache_kind = EcsQueryCacheNone; + if (desc->group_by || desc->order_by) { + ecs_err("cannot create uncached query with group_by/order_by"); + return -1; + } + return 0; + } + + /* Entire query must be cached */ + if (desc->cache_kind == EcsQueryCacheAll) { + if (impl->pub.flags & EcsQueryIsCacheable) { + impl->pub.cache_kind = EcsQueryCacheAll; + return 0; + } else { + ecs_err("cannot enforce QueryCacheAll, " + "query contains uncacheable terms"); + return -1; + } + } + + /* Only cache terms that are cacheable */ + if (kind == EcsQueryCacheAuto) { + if (impl->pub.flags & EcsQueryIsCacheable) { + /* If all terms of the query are cacheable, just set the policy to + * All which simplifies work for the compiler. */ + impl->pub.cache_kind = EcsQueryCacheAll; + } else if (!(impl->pub.flags & EcsQueryHasCacheable)) { + /* Same for when the query has no cacheable terms */ + impl->pub.cache_kind = EcsQueryCacheNone; + } else { + /* Part of the query is cacheable. Make sure to only create a cache + * if the cacheable part of the query contains not just not/optional + * terms, as this would build a cache that contains all tables. */ + int32_t not_optional_terms = 0, cacheable_terms = 0; + if (!group_order_by) { + int32_t i, term_count = impl->pub.term_count; + const ecs_term_t *terms = impl->pub.terms; + for (i = 0; i < term_count; i ++) { + const ecs_term_t *term = &terms[i]; + if (term->flags_ & EcsTermIsCacheable) { + cacheable_terms ++; + if (term->oper == EcsNot || term->oper == EcsOptional) { + not_optional_terms ++; + } + } + } + } + + if (group_order_by || cacheable_terms != not_optional_terms) { + impl->pub.cache_kind = EcsQueryCacheAuto; + } else { + impl->pub.cache_kind = EcsQueryCacheNone; + } + } + } + + return 0; +} + +static +int flecs_query_create_cache( + ecs_query_impl_t *impl, + ecs_query_desc_t *desc) +{ + ecs_query_t *q = &impl->pub; + if (flecs_query_set_caching_policy(impl, desc)) { + return -1; + } + + if ((q->cache_kind != EcsQueryCacheNone) && !q->entity) { + /* Cached queries need an entity handle for observer components */ + q->entity = ecs_new(q->world); + desc->entity = q->entity; + } + + if (q->cache_kind == EcsQueryCacheAll) { + /* Create query cache for all terms */ + if (!flecs_query_cache_init(impl, desc)) { + goto error; + } + } else if (q->cache_kind == EcsQueryCacheAuto) { + /* Query is partially cached */ + ecs_query_desc_t cache_desc = *desc; + ecs_os_memset_n(&cache_desc.terms, 0, ecs_term_t, FLECS_TERM_COUNT_MAX); + cache_desc.expr = NULL; + + /* Maps field indices from cache to query */ + int8_t field_map[FLECS_TERM_COUNT_MAX]; + + int32_t i, count = q->term_count, dst_count = 0, dst_field = 0; + ecs_term_t *terms = q->terms; + for (i = 0; i < count; i ++) { + ecs_term_t *term = &terms[i]; + if (term->flags_ & EcsTermIsCacheable) { + cache_desc.terms[dst_count] = *term; + field_map[dst_field] = flecs_ito(int8_t, term->field_index); + dst_count ++; + if (i) { + dst_field += term->field_index != term[-1].field_index; + } else { + dst_field ++; + } + } + } + + if (dst_count) { + if (!flecs_query_cache_init(impl, &cache_desc)) { + goto error; + } + + impl->cache->field_map = flecs_alloc_n(&impl->stage->allocator, + int8_t, FLECS_TERM_COUNT_MAX); + + ecs_os_memcpy_n(impl->cache->field_map, field_map, int8_t, dst_count); + } + } else { + /* Check if query has features that are unsupported for uncached */ + ecs_assert(q->cache_kind == EcsQueryCacheNone, ECS_INTERNAL_ERROR, NULL); + + if (!(q->flags & EcsQueryNested)) { + /* If uncached query is not create to populate a cached query, it + * should not have cascade modifiers */ + int32_t i, count = q->term_count; + ecs_term_t *terms = q->terms; + for (i = 0; i < count; i ++) { + ecs_term_t *term = &terms[i]; + if (term->src.id & EcsCascade) { + char *query_str = ecs_query_str(q); + ecs_err( + "cascade is unsupported for uncached query\n %s", + query_str); + ecs_os_free(query_str); + goto error; + } + } + } + } + + return 0; +error: + return -1; +} + +static +void flecs_query_fini( + ecs_query_impl_t *impl) +{ + ecs_stage_t *stage = impl->stage; + ecs_assert(stage != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_allocator_t *a = &stage->allocator; + + if (impl->ctx_free) { + impl->ctx_free(impl->pub.ctx); + } + + if (impl->binding_ctx_free) { + impl->binding_ctx_free(impl->pub.binding_ctx); + } + + if (impl->vars != &flecs_this_array) { + flecs_free(a, (ECS_SIZEOF(ecs_query_var_t) + ECS_SIZEOF(char*)) * + impl->var_size, impl->vars); + flecs_name_index_fini(&impl->tvar_index); + flecs_name_index_fini(&impl->evar_index); + } + + flecs_free_n(a, ecs_query_op_t, impl->op_count, impl->ops); + flecs_free_n(a, ecs_var_id_t, impl->pub.field_count, impl->src_vars); + flecs_free_n(a, int32_t, impl->pub.field_count, impl->monitor); + + ecs_query_t *q = &impl->pub; + int i, count = q->term_count; + for (i = 0; i < count; i ++) { + ecs_term_t *term = &q->terms[i]; + if (!(term->flags_ & EcsTermKeepAlive)) { + continue; + } + + ecs_id_record_t *idr = flecs_id_record_get(q->real_world, term->id); + if (idr) { + if (!(q->world->flags & EcsWorldQuit)) { + if (ecs_os_has_threading()) { + int32_t idr_keep_alive = ecs_os_adec(&idr->keep_alive); + ecs_assert(idr_keep_alive >= 0, ECS_INTERNAL_ERROR, NULL); + (void)idr_keep_alive; + } else { + idr->keep_alive --; + ecs_assert(idr->keep_alive >= 0, ECS_INTERNAL_ERROR, NULL); + } + } + } + } + + if (impl->tokens) { + flecs_free(&impl->stage->allocator, impl->tokens_len, impl->tokens); + } + + if (impl->cache) { + flecs_free_n(a, int8_t, FLECS_TERM_COUNT_MAX, impl->cache->field_map); + flecs_query_cache_fini(impl); + } + + flecs_poly_fini(impl, ecs_query_t); + flecs_bfree(&stage->allocators.query_impl, impl); +} + +static +void flecs_query_poly_fini(void *ptr) { + flecs_query_fini(ptr); +} + +static +void flecs_query_add_self_ref( + ecs_query_t *q) +{ + if (q->entity) { + int32_t t, term_count = q->term_count; + ecs_term_t *terms = q->terms; + + for (t = 0; t < term_count; t ++) { + ecs_term_t *term = &terms[t]; + if (ECS_TERM_REF_ID(&term->src) == q->entity) { + ecs_add_id(q->world, q->entity, term->id); + } + } + } +} + +void ecs_query_fini( + ecs_query_t *q) +{ + flecs_poly_assert(q, ecs_query_t); + + if (q->entity) { + /* If query is associated with entity, use poly dtor path */ + ecs_delete(q->world, q->entity); + } else { + flecs_query_fini(flecs_query_impl(q)); + } +} + +ecs_query_t* ecs_query_init( + ecs_world_t *world, + const ecs_query_desc_t *const_desc) +{ + ecs_world_t *world_arg = world; + ecs_stage_t *stage = flecs_stage_from_world(&world); + ecs_query_impl_t *result = flecs_bcalloc(&stage->allocators.query_impl); + flecs_poly_init(result, ecs_query_t); + + ecs_query_desc_t desc = *const_desc; + ecs_entity_t entity = const_desc->entity; + + if (entity) { + /* Remove existing query if entity has one */ + bool deferred = false; + if (ecs_is_deferred(world)) { + deferred = true; + /* Ensures that remove operation doesn't get applied after bind */ + ecs_defer_suspend(world); + } + ecs_remove_pair(world, entity, ecs_id(EcsPoly), EcsQuery); + if (deferred) { + ecs_defer_resume(world); + } + } + + /* Initialize the query */ + result->pub.entity = entity; + result->pub.real_world = world; + result->pub.world = world_arg; + result->stage = stage; + + ecs_assert(flecs_poly_is(result->pub.real_world, ecs_world_t), + ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_poly_is(result->stage, ecs_stage_t), + ECS_INTERNAL_ERROR, NULL); + + /* Validate input, translate to canonical query representation */ + if (flecs_query_finalize_query(world, &result->pub, &desc)) { + goto error; + } + + /* If query terms have itself as source, add term ids to self. This makes it + * easy to attach components to queries, which is one of the ways + * applications can attach data to systems. */ + flecs_query_add_self_ref(&result->pub); + + /* Initialize static context */ + result->pub.ctx = const_desc->ctx; + result->pub.binding_ctx = const_desc->binding_ctx; + result->ctx_free = const_desc->ctx_free; + result->binding_ctx_free = const_desc->binding_ctx_free; + result->dtor = flecs_query_poly_fini; + result->cache = NULL; + + /* Initialize query cache if necessary */ + if (flecs_query_create_cache(result, &desc)) { + goto error; + } + + if (flecs_query_compile(world, stage, result)) { + goto error; + } + + /* Entity could've been set by finalize query if query is cached */ + entity = result->pub.entity; + if (entity) { + EcsPoly *poly = flecs_poly_bind(world, entity, ecs_query_t); + poly->poly = result; + flecs_poly_modified(world, entity, ecs_query_t); + } + + return &result->pub; +error: + result->pub.entity = 0; + ecs_query_fini(&result->pub); + return NULL; +} + +bool ecs_query_has( + ecs_query_t *q, + ecs_entity_t entity, + ecs_iter_t *it) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_check(q->flags & EcsQueryMatchThis, ECS_INVALID_PARAMETER, NULL); + + *it = ecs_query_iter(q->world, q); + ecs_iter_set_var(it, 0, entity); + return ecs_query_next(it); +error: + return false; +} + +bool ecs_query_has_table( + ecs_query_t *q, + ecs_table_t *table, + ecs_iter_t *it) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_check(q->flags & EcsQueryMatchThis, ECS_INVALID_PARAMETER, NULL); + + *it = ecs_query_iter(q->world, q); + ecs_iter_set_var_as_table(it, 0, table); + return ecs_query_next(it); +error: + return false; +} + +bool ecs_query_has_range( + ecs_query_t *q, + ecs_table_range_t *range, + ecs_iter_t *it) +{ + flecs_poly_assert(q, ecs_query_t); + + if (q->flags & EcsQueryMatchThis) { + if (range->table) { + if ((range->offset + range->count) > ecs_table_count(range->table)) { + return false; + } + } + } + + *it = ecs_query_iter(q->world, q); + if (q->flags & EcsQueryMatchThis) { + ecs_iter_set_var_as_range(it, 0, range); + } + + return ecs_query_next(it); +} + +ecs_query_count_t ecs_query_count( + const ecs_query_t *q) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_query_count_t result = {0}; + + if (!(q->flags & EcsQueryMatchThis)) { + return result; + } + + ecs_run_aperiodic(q->world, EcsAperiodicEmptyTables); + + ecs_query_impl_t *impl = flecs_query_impl(q); + if (impl->cache && q->flags & EcsQueryIsCacheable) { + result.results = flecs_query_cache_table_count(impl->cache); + result.entities = flecs_query_cache_entity_count(impl->cache); + result.tables = flecs_query_cache_table_count(impl->cache); + result.empty_tables = flecs_query_cache_empty_table_count(impl->cache); + } else { + ecs_iter_t it = flecs_query_iter(q->world, q); + it.flags |= EcsIterNoData; + + while (ecs_query_next(&it)) { + result.results ++; + result.entities += it.count; + ecs_iter_skip(&it); + } + } + + return result; +} + +bool ecs_query_is_true( + const ecs_query_t *q) +{ + flecs_poly_assert(q, ecs_query_t); + + ecs_run_aperiodic(q->world, EcsAperiodicEmptyTables); + + ecs_query_impl_t *impl = flecs_query_impl(q); + if (impl->cache && q->flags & EcsQueryIsCacheable) { + return flecs_query_cache_table_count(impl->cache) != 0; + } else { + ecs_iter_t it = flecs_query_iter(q->world, q); + return ecs_iter_is_true(&it); + } +} + +int32_t ecs_query_match_count( + const ecs_query_t *q) +{ + flecs_poly_assert(q, ecs_query_t); + + ecs_query_impl_t *impl = flecs_query_impl(q); + if (!impl->cache) { + return 0; + } else { + return impl->cache->match_count; + } +} + +const ecs_query_t* ecs_query_get_cache_query( + const ecs_query_t *q) +{ + ecs_query_impl_t *impl = flecs_query_impl(q); + if (!impl->cache) { + return NULL; + } else { + return impl->cache->query; + } +} diff --git a/vendors/flecs/src/query/compiler/compiler.c b/vendors/flecs/src/query/compiler/compiler.c new file mode 100644 index 000000000..271edbcff --- /dev/null +++ b/vendors/flecs/src/query/compiler/compiler.c @@ -0,0 +1,1111 @@ +/** + * @file query/compiler/compile.c + * @brief Compile query program from query. + */ + +#include "../../private_api.h" + +static +bool flecs_query_var_is_anonymous( + const ecs_query_impl_t *query, + ecs_var_id_t var_id) +{ + ecs_query_var_t *var = &query->vars[var_id]; + return var->anonymous; +} + +ecs_var_id_t flecs_query_add_var( + ecs_query_impl_t *query, + const char *name, + ecs_vec_t *vars, + ecs_var_kind_t kind) +{ + const char *dot = NULL; + if (name) { + dot = strchr(name, '.'); + if (dot) { + kind = EcsVarEntity; /* lookup variables are always entities */ + } + } + + ecs_hashmap_t *var_index = NULL; + ecs_var_id_t var_id = EcsVarNone; + if (name) { + if (kind == EcsVarAny) { + var_id = flecs_query_find_var_id(query, name, EcsVarEntity); + if (var_id != EcsVarNone) { + return var_id; + } + + var_id = flecs_query_find_var_id(query, name, EcsVarTable); + if (var_id != EcsVarNone) { + return var_id; + } + + kind = EcsVarTable; + } else { + var_id = flecs_query_find_var_id(query, name, kind); + if (var_id != EcsVarNone) { + return var_id; + } + } + + if (kind == EcsVarTable) { + var_index = &query->tvar_index; + } else { + var_index = &query->evar_index; + } + + /* If we're creating an entity var, check if it has a table variant */ + if (kind == EcsVarEntity && var_id == EcsVarNone) { + var_id = flecs_query_find_var_id(query, name, EcsVarTable); + } + } + + ecs_query_var_t *var; + ecs_var_id_t result; + if (vars) { + var = ecs_vec_append_t(NULL, vars, ecs_query_var_t); + result = var->id = flecs_itovar(ecs_vec_count(vars)); + } else { + ecs_dbg_assert(query->var_count < query->var_size, + ECS_INTERNAL_ERROR, NULL); + var = &query->vars[query->var_count]; + result = var->id = flecs_itovar(query->var_count); + query->var_count ++; + } + + var->kind = flecs_ito(int8_t, kind); + var->name = name; + var->table_id = var_id; + var->base_id = 0; + var->lookup = NULL; + flecs_set_var_label(var, NULL); + + if (name) { + flecs_name_index_init_if(var_index, NULL); + flecs_name_index_ensure(var_index, var->id, name, 0, 0); + var->anonymous = name[0] == '_'; + + /* Handle variables that require a by-name lookup, e.g. $this.wheel */ + if (dot != NULL) { + ecs_assert(var->table_id == EcsVarNone, ECS_INTERNAL_ERROR, NULL); + var->lookup = dot + 1; + } + } + + return result; +} + +static +ecs_var_id_t flecs_query_add_var_for_term_id( + ecs_query_impl_t *query, + ecs_term_ref_t *term_id, + ecs_vec_t *vars, + ecs_var_kind_t kind) +{ + const char *name = flecs_term_ref_var_name(term_id); + if (!name) { + return EcsVarNone; + } + + return flecs_query_add_var(query, name, vars, kind); +} + +/* This function walks over terms to discover which variables are used in the + * query. It needs to provide the following functionality: + * - create table vars for all variables used as source + * - create entity vars for all variables not used as source + * - create entity vars for all non-$this vars + * - create anonymous vars to store the content of wildcards + * - create anonymous vars to store result of lookups (for $var.child_name) + * - create anonymous vars for resolving component inheritance + * - create array that stores the source variable for each field + * - ensure table vars for non-$this variables are anonymous + * - ensure variables created inside scopes are anonymous + * - place anonymous variables after public variables in vars array + */ +static +int flecs_query_discover_vars( + ecs_stage_t *stage, + ecs_query_impl_t *query) +{ + ecs_vec_t *vars = &stage->variables; /* Buffer to reduce allocs */ + ecs_vec_reset_t(NULL, vars, ecs_query_var_t); + + ecs_term_t *terms = query->pub.terms; + int32_t a, i, anonymous_count = 0, count = query->pub.term_count; + int32_t anonymous_table_count = 0, scope = 0, scoped_var_index = 0; + bool table_this = false, entity_before_table_this = false; + + /* For This table lookups during discovery. This will be overwritten after + * discovery with whether the query actually has a This table variable. */ + query->pub.flags |= EcsQueryHasTableThisVar; + + for (i = 0; i < count; i ++) { + ecs_term_t *term = &terms[i]; + ecs_term_ref_t *first = &term->first; + ecs_term_ref_t *second = &term->second; + ecs_term_ref_t *src = &term->src; + + if (ECS_TERM_REF_ID(first) == EcsScopeOpen) { + /* Keep track of which variables are first used in scope, so that we + * can mark them as anonymous. Terms inside a scope are collapsed + * into a single result, which means that outside of the scope the + * value of those variables is undefined. */ + if (!scope) { + scoped_var_index = ecs_vec_count(vars); + } + scope ++; + continue; + } else if (ECS_TERM_REF_ID(first) == EcsScopeClose) { + if (!--scope) { + /* Any new variables declared after entering a scope should be + * marked as anonymous. */ + int32_t v; + for (v = scoped_var_index; v < ecs_vec_count(vars); v ++) { + ecs_vec_get_t(vars, ecs_query_var_t, v)->anonymous = true; + } + } + continue; + } + + ecs_var_id_t first_var_id = flecs_query_add_var_for_term_id( + query, first, vars, EcsVarEntity); + if (first_var_id == EcsVarNone) { + /* If first is not a variable, check if we need to insert anonymous + * variable for resolving component inheritance */ + if (term->flags_ & EcsTermIdInherited) { + anonymous_count += 2; /* table & entity variable */ + } + + /* If first is a wildcard, insert anonymous variable */ + if (flecs_term_ref_is_wildcard(first)) { + anonymous_count ++; + } + } + + if ((src->id & EcsIsVariable) && (ECS_TERM_REF_ID(src) != EcsThis)) { + const char *var_name = flecs_term_ref_var_name(src); + if (var_name) { + ecs_var_id_t var_id = flecs_query_find_var_id( + query, var_name, EcsVarEntity); + if (var_id == EcsVarNone || var_id == first_var_id) { + var_id = flecs_query_add_var( + query, var_name, vars, EcsVarEntity); + } + + if (var_id != EcsVarNone) { + /* Mark variable as one for which we need to create a table + * variable. Don't create table variable now, so that we can + * store it in the non-public part of the variable array. */ + ecs_query_var_t *var = ecs_vec_get_t( + vars, ecs_query_var_t, (int32_t)var_id - 1); + ecs_assert(var != NULL, ECS_INTERNAL_ERROR, NULL); + if (!var->lookup) { + var->kind = EcsVarAny; + anonymous_table_count ++; + } + + if (((1llu << term->field_index) & query->pub.data_fields)) { + /* Can't have an anonymous variable as source of a term + * that returns a component. We need to return each + * instance of the component, whereas anonymous + * variables are not guaranteed to be resolved to + * individual entities. */ + if (var->anonymous) { + ecs_err( + "can't use anonymous variable '%s' as source of " + "data term", var->name); + goto error; + } + } + + /* Track which variable ids are used as field source */ + if (!query->src_vars) { + query->src_vars = flecs_calloc_n(&stage->allocator, + ecs_var_id_t, query->pub.field_count); + } + + query->src_vars[term->field_index] = var_id; + } + } else { + if (flecs_term_ref_is_wildcard(src)) { + anonymous_count ++; + } + } + } else if ((src->id & EcsIsVariable) && (ECS_TERM_REF_ID(src) == EcsThis)) { + if (flecs_term_is_builtin_pred(term) && term->oper == EcsOr) { + flecs_query_add_var(query, EcsThisName, vars, EcsVarEntity); + } + } + + if (flecs_query_add_var_for_term_id( + query, second, vars, EcsVarEntity) == EcsVarNone) + { + /* If second is a wildcard, insert anonymous variable */ + if (flecs_term_ref_is_wildcard(second)) { + anonymous_count ++; + } + } + + if (src->id & EcsIsVariable && second->id & EcsIsVariable) { + if (term->flags_ & EcsTermTransitive) { + /* Anonymous variable to store temporary id for finding + * targets for transitive relationship, see compile_term. */ + anonymous_count ++; + } + } + + /* If member term, make sure source is available as entity */ + if (term->flags_ & EcsTermIsMember) { + flecs_query_add_var_for_term_id(query, src, vars, EcsVarEntity); + } + + /* Track if a This entity variable is used before a potential This table + * variable. If this happens, the query has no This table variable */ + if (ECS_TERM_REF_ID(src) == EcsThis) { + table_this = true; + } + + if (ECS_TERM_REF_ID(first) == EcsThis || ECS_TERM_REF_ID(second) == EcsThis) { + if (!table_this) { + entity_before_table_this = true; + } + } + } + + int32_t var_count = ecs_vec_count(vars); + ecs_var_id_t placeholder = EcsVarNone - 1; + bool replace_placeholders = false; + + /* Ensure lookup variables have table and/or entity variables */ + for (i = 0; i < var_count; i ++) { + ecs_query_var_t *var = ecs_vec_get_t(vars, ecs_query_var_t, i); + if (var->lookup) { + char *var_name = ecs_os_strdup(var->name); + var_name[var->lookup - var->name - 1] = '\0'; + + ecs_var_id_t base_table_id = flecs_query_find_var_id( + query, var_name, EcsVarTable); + if (base_table_id != EcsVarNone) { + var->table_id = base_table_id; + } else if (anonymous_table_count) { + /* Scan for implicit anonymous table variables that haven't been + * inserted yet (happens after this step). Doing this here vs. + * ensures that anonymous variables are appended at the end of + * the variable array, while also ensuring that variable ids are + * stable (no swapping of table var ids that are in use). */ + for (a = 0; a < var_count; a ++) { + ecs_query_var_t *avar = ecs_vec_get_t( + vars, ecs_query_var_t, a); + if (avar->kind == EcsVarAny) { + if (!ecs_os_strcmp(avar->name, var_name)) { + base_table_id = (ecs_var_id_t)(a + 1); + break; + } + } + } + if (base_table_id != EcsVarNone) { + /* Set marker so we can set the new table id afterwards */ + var->table_id = placeholder; + replace_placeholders = true; + } + } + + ecs_var_id_t base_entity_id = flecs_query_find_var_id( + query, var_name, EcsVarEntity); + if (base_entity_id == EcsVarNone) { + /* Get name from table var (must exist). We can't use allocated + * name since variables don't own names. */ + const char *base_name = NULL; + if (base_table_id != EcsVarNone && base_table_id) { + ecs_query_var_t *base_table_var = ecs_vec_get_t( + vars, ecs_query_var_t, (int32_t)base_table_id - 1); + base_name = base_table_var->name; + } else { + base_name = EcsThisName; + } + + base_entity_id = flecs_query_add_var( + query, base_name, vars, EcsVarEntity); + var = ecs_vec_get_t(vars, ecs_query_var_t, i); + } + + var->base_id = base_entity_id; + + ecs_os_free(var_name); + } + } + + var_count = ecs_vec_count(vars); + + /* Add non-This table variables */ + if (anonymous_table_count) { + anonymous_table_count = 0; + for (i = 0; i < var_count; i ++) { + ecs_query_var_t *var = ecs_vec_get_t(vars, ecs_query_var_t, i); + if (var->kind == EcsVarAny) { + var->kind = EcsVarEntity; + + ecs_var_id_t var_id = flecs_query_add_var( + query, var->name, vars, EcsVarTable); + ecs_vec_get_t(vars, ecs_query_var_t, i)->table_id = var_id; + anonymous_table_count ++; + } + } + + var_count = ecs_vec_count(vars); + } + + /* If any forward references to newly added anonymous tables exist, replace + * them with the actual table variable ids. */ + if (replace_placeholders) { + for (i = 0; i < var_count; i ++) { + ecs_query_var_t *var = ecs_vec_get_t(vars, ecs_query_var_t, i); + if (var->table_id == placeholder) { + char *var_name = ecs_os_strdup(var->name); + var_name[var->lookup - var->name - 1] = '\0'; + + var->table_id = flecs_query_find_var_id( + query, var_name, EcsVarTable); + ecs_assert(var->table_id != EcsVarNone, + ECS_INTERNAL_ERROR, NULL); + + ecs_os_free(var_name); + } + } + } + + /* Always include spot for This variable, even if query doesn't use it */ + var_count ++; + + ecs_query_var_t *query_vars = &flecs_this_array; + if ((var_count + anonymous_count) > 1) { + query_vars = flecs_alloc(&stage->allocator, + (ECS_SIZEOF(ecs_query_var_t) + ECS_SIZEOF(char*)) * + (var_count + anonymous_count)); + } + + query->vars = query_vars; + query->var_count = var_count; + query->pub.var_count = flecs_ito(int8_t, var_count); + ECS_BIT_COND(query->pub.flags, EcsQueryHasTableThisVar, + !entity_before_table_this); + query->var_size = var_count + anonymous_count; + + char **var_names; + if (query_vars != &flecs_this_array) { + query_vars[0].kind = EcsVarTable; + query_vars[0].name = NULL; + flecs_set_var_label(&query_vars[0], NULL); + query_vars[0].id = 0; + query_vars[0].table_id = EcsVarNone; + query_vars[0].lookup = NULL; + + var_names = ECS_ELEM(query_vars, ECS_SIZEOF(ecs_query_var_t), + var_count + anonymous_count); + var_names[0] = ECS_CONST_CAST(char*, query_vars[0].name); + } else { + var_names = &flecs_this_name_array; + } + + query->pub.vars = (char**)var_names; + + query_vars ++; + var_names ++; + var_count --; + + if (var_count) { + ecs_query_var_t *user_vars = ecs_vec_first_t(vars, ecs_query_var_t); + ecs_os_memcpy_n(query_vars, user_vars, ecs_query_var_t, var_count); + for (i = 0; i < var_count; i ++) { + ecs_assert(&var_names[i] != &(&flecs_this_name_array)[i], + ECS_INTERNAL_ERROR, NULL); + var_names[i] = ECS_CONST_CAST(char*, query_vars[i].name); + } + } + + /* Hide anonymous table variables from application */ + query->pub.var_count = + flecs_ito(int8_t, query->pub.var_count - anonymous_table_count); + + /* Sanity check to make sure that the public part of the variable array only + * contains entity variables. */ +#ifdef FLECS_DEBUG + for (i = 1 /* first element = $this */; i < query->pub.var_count; i ++) { + ecs_assert(query->vars[i].kind == EcsVarEntity, ECS_INTERNAL_ERROR, NULL); + } +#endif + + return 0; +error: + return -1; +} + +static +bool flecs_query_var_is_unknown( + ecs_query_impl_t *query, + ecs_var_id_t var_id, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_var_t *vars = query->vars; + if (ctx->written & (1ull << var_id)) { + return false; + } else { + ecs_var_id_t table_var = vars[var_id].table_id; + if (table_var != EcsVarNone) { + return flecs_query_var_is_unknown(query, table_var, ctx); + } + } + return true; +} + +/* Returns whether term is unknown. A term is unknown when it has variable + * elements (first, second, src) that are all unknown. */ +static +bool flecs_query_term_is_unknown( + ecs_query_impl_t *query, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t dummy = {0}; + flecs_query_compile_term_ref(NULL, query, &dummy, &term->first, + &dummy.first, EcsQueryFirst, EcsVarEntity, ctx, false); + flecs_query_compile_term_ref(NULL, query, &dummy, &term->second, + &dummy.second, EcsQuerySecond, EcsVarEntity, ctx, false); + flecs_query_compile_term_ref(NULL, query, &dummy, &term->src, + &dummy.src, EcsQuerySrc, EcsVarAny, ctx, false); + + bool has_vars = dummy.flags & + ((EcsQueryIsVar << EcsQueryFirst) | + (EcsQueryIsVar << EcsQuerySecond) | + (EcsQueryIsVar << EcsQuerySrc)); + if (!has_vars) { + /* If term has no variables (typically terms with a static src) there + * can't be anything that's unknown. */ + return false; + } + + if (dummy.flags & (EcsQueryIsVar << EcsQueryFirst)) { + if (!flecs_query_var_is_unknown(query, dummy.first.var, ctx)) { + return false; + } + } + if (dummy.flags & (EcsQueryIsVar << EcsQuerySecond)) { + if (!flecs_query_var_is_unknown(query, dummy.second.var, ctx)) { + return false; + } + } + if (dummy.flags & (EcsQueryIsVar << EcsQuerySrc)) { + if (!flecs_query_var_is_unknown(query, dummy.src.var, ctx)) { + return false; + } + } + + return true; +} + +/* Find the next known term from specified offset. This function is used to find + * a term that can be evaluated before a term that is unknown. Evaluating known + * before unknown terms can significantly decrease the search space. */ +static +int32_t flecs_query_term_next_known( + ecs_query_impl_t *query, + ecs_query_compile_ctx_t *ctx, + int32_t offset, + ecs_flags64_t compiled) +{ + ecs_query_t *q = &query->pub; + ecs_term_t *terms = q->terms; + int32_t i, count = q->term_count; + + for (i = offset; i < count; i ++) { + ecs_term_t *term = &terms[i]; + if (compiled & (1ull << i)) { + continue; + } + + /* Only evaluate And terms */ + if (term->oper != EcsAnd || flecs_term_is_or(q, term)){ + continue; + } + + /* Don't reorder terms in scopes */ + if (term->flags_ & EcsTermIsScope) { + continue; + } + + if (flecs_query_term_is_unknown(query, term, ctx)) { + continue; + } + + return i; + } + + return -1; +} + +/* If the first part of a query contains more than one trivial term, insert a + * special instruction which batch-evaluates multiple terms. */ +static +void flecs_query_insert_trivial_search( + ecs_query_impl_t *query, + ecs_flags64_t *compiled, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_t *q = &query->pub; + ecs_term_t *terms = q->terms; + int32_t i, term_count = q->term_count; + ecs_flags64_t trivial_set = 0; + + /* Trivial search always ignores prefabs and disabled entities */ + if (query->pub.flags & (EcsQueryMatchPrefab|EcsQueryMatchDisabled)) { + return; + } + + /* Find trivial terms, which can be handled in single instruction */ + int32_t trivial_wildcard_terms = 0; + int32_t trivial_terms = 0; + + for (i = 0; i < term_count; i ++) { + /* Term is already compiled */ + if (*compiled & (1ull << i)) { + continue; + } + + ecs_term_t *term = &terms[i]; + if (!(term->flags_ & EcsTermIsTrivial)) { + continue; + } + + /* We can only add trivial terms to plan if they no up traversal */ + if ((term->src.id & EcsTraverseFlags) != EcsSelf) { + continue; + } + + /* Wildcards are not supported for trivial queries */ + if (ecs_id_is_wildcard(term->id)) { + continue; + } + + trivial_set |= (1llu << i); + + trivial_terms ++; + } + + if (trivial_terms >= 2) { + /* Mark terms as compiled & populated */ + for (i = 0; i < q->term_count; i ++) { + if (trivial_set & (1llu << i)) { + *compiled |= (1ull << i); + } + } + + /* If there's more than 1 trivial term, batch them in trivial search */ + ecs_query_op_t trivial = {0}; + if (!trivial_wildcard_terms) { + trivial.kind = EcsQueryTriv; + } + + /* Store the bitset with trivial terms on the instruction */ + trivial.src.entity = trivial_set; + flecs_query_op_insert(&trivial, ctx); + + /* Mark $this as written */ + ctx->written |= (1llu << 0); + } +} + +static +void flecs_query_insert_cache_search( + ecs_query_impl_t *query, + ecs_flags64_t *compiled, + ecs_query_compile_ctx_t *ctx) +{ + if (!query->cache) { + return; + } + + ecs_query_t *q = &query->pub; + + if (q->cache_kind == EcsQueryCacheAll) { + /* If all terms are cacheable, make sure no other terms are compiled */ + *compiled = 0xFFFFFFFFFFFFFFFF; + } else if (q->cache_kind == EcsQueryCacheAuto) { + /* The query is partially cacheable */ + ecs_term_t *terms = q->terms; + int32_t i, count = q->term_count; + + for (i = 0; i < count; i ++) { + if ((*compiled) & (1ull << i)) { + continue; + } + + ecs_term_t *term = &terms[i]; + if (!(term->flags_ & EcsTermIsCacheable)) { + continue; + } + + *compiled |= (1ull << i); + } + } + + /* Insert the operation for cache traversal */ + ecs_query_op_t op = {0}; + + if (q->flags & EcsQueryIsCacheable) { + op.kind = EcsQueryIsCache; + } else { + op.kind = EcsQueryCache; + } + + flecs_query_write(0, &op.written); + flecs_query_write_ctx(0, ctx, false); + flecs_query_op_insert(&op, ctx); +} + +static +bool flecs_term_ref_match_multiple( + ecs_term_ref_t *ref) +{ + return (ref->id & EcsIsVariable) && (ECS_TERM_REF_ID(ref) != EcsAny); +} + +static +bool flecs_term_match_multiple( + ecs_term_t *term) +{ + return flecs_term_ref_match_multiple(&term->first) || + flecs_term_ref_match_multiple(&term->second); +} + +static +int flecs_query_insert_toggle( + ecs_query_impl_t *impl, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_t *q = &impl->pub; + int32_t i, j, term_count = q->term_count; + ecs_term_t *terms = q->terms; + ecs_flags64_t fields_done = 0; + + for (i = 0; i < term_count; i ++) { + if (fields_done & (1llu << i)) { + continue; + } + + ecs_term_t *term = &terms[i]; + if (term->flags_ & EcsTermIsToggle) { + ecs_query_op_t cur = {0}; + flecs_query_compile_term_ref(NULL, impl, &cur, &term->src, + &cur.src, EcsQuerySrc, EcsVarAny, ctx, false); + + ecs_flags64_t and_toggles = 0; + ecs_flags64_t not_toggles = 0; + ecs_flags64_t optional_toggles = 0; + + for (j = i; j < term_count; j ++) { + if (fields_done & (1llu << j)) { + continue; + } + + /* Also includes term[i], so flags get set correctly */ + term = &terms[j]; + + /* If term is not for the same src, skip */ + ecs_query_op_t next = {0}; + flecs_query_compile_term_ref(NULL, impl, &next, &term->src, + &next.src, EcsQuerySrc, EcsVarAny, ctx, false); + if (next.src.entity != cur.src.entity || + next.flags != cur.flags) + { + continue; + } + + /* Source matches, set flag */ + if (term->oper == EcsNot) { + not_toggles |= (1llu << j); + } else if (term->oper == EcsOptional) { + optional_toggles |= (1llu << j); + } else { + and_toggles |= (1llu << j); + } + + fields_done |= (1llu << j); + } + + if (and_toggles || not_toggles) { + ecs_query_op_t op = {0}; + op.kind = EcsQueryToggle; + op.src = cur.src; + op.flags = cur.flags; + + if (op.flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_write(op.src.var, &op.written); + } + + /* Encode fields: + * - first.entity is the fields that match enabled bits + * - second.entity is the fields that match disabled bits + */ + op.first.entity = and_toggles; + op.second.entity = not_toggles; + flecs_query_op_insert(&op, ctx); + } + + /* Insert separate instructions for optional terms. To make sure + * entities are returned in batches where fields are never partially + * set or unset, the result must be split up into batches that have + * the exact same toggle masks. Instead of complicating the toggle + * instruction with code to scan for blocks that have the same bits + * set, separate instructions let the query engine backtrack to get + * the right results. */ + if (optional_toggles) { + for (j = i; j < term_count; j ++) { + uint64_t field_bit = 1ull << j; + if (!(optional_toggles & field_bit)) { + continue; + } + + ecs_query_op_t op = {0}; + op.kind = EcsQueryToggleOption; + op.src = cur.src; + op.first.entity = field_bit; + op.flags = cur.flags; + flecs_query_op_insert(&op, ctx); + } + } + } + } + + return 0; +} + +static +int flecs_query_insert_fixed_src_terms( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_flags64_t *compiled, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_t *q = &impl->pub; + int32_t i, term_count = q->term_count; + ecs_term_t *terms = q->terms; + + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + + if (term->oper == EcsNot) { + /* If term has not operator and variables for first/second, we can't + * put the term first as this could prevent us from getting back + * valid results. For example: + * !$var(e), Tag($var) + * + * Here, the first term would evaluate to false (and cause the + * entire query not to match) if 'e' has any components. + * + * However, when reordering we get results: + * Tag($var), !$var(e) + * + * Now the query returns all entities with Tag, that 'e' does not + * have as component. For this reason, queries should never use + * unwritten variables in not terms- and we should also not reorder + * terms in a way that results in doing this. */ + if (flecs_term_match_multiple(term)) { + continue; + } + } + + /* Don't reorder terms in scopes */ + if (term->flags_ & EcsTermIsScope) { + continue; + } + + if (term->src.id & EcsIsEntity && ECS_TERM_REF_ID(&term->src)) { + if (flecs_query_compile_term(world, impl, term, ctx)) { + return -1; + } + + *compiled |= (1llu << i); + } + } + + return 0; +} + +int flecs_query_compile( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_query_impl_t *query) +{ + /* Compile query to operations. Only necessary for non-trivial queries, as + * trivial queries use trivial iterators that don't use query ops. */ + bool needs_plan = true; + ecs_flags32_t flags = query->pub.flags; + ecs_flags32_t trivial_flags = EcsQueryIsTrivial|EcsQueryMatchOnlySelf; + if ((flags & trivial_flags) == trivial_flags) { + if (query->cache) { + if (flags & EcsQueryIsCacheable) { + needs_plan = false; + } + } else { + if (!(flags & EcsQueryMatchWildcards)) { + needs_plan = false; + } + } + } + + if (!needs_plan) { + /* Initialize space for $this variable */ + query->pub.var_count = 1; + query->var_count = 1; + query->var_size = 1; + query->vars = &flecs_this_array; + query->pub.vars = &flecs_this_name_array; + query->pub.flags |= EcsQueryHasTableThisVar; + return 0; + } + + ecs_query_t *q = &query->pub; + ecs_term_t *terms = q->terms; + ecs_query_compile_ctx_t ctx = {0}; + ecs_vec_reset_t(NULL, &stage->operations, ecs_query_op_t); + ctx.ops = &stage->operations; + ctx.cur = ctx.ctrlflow; + ctx.cur->lbl_begin = -1; + ctx.cur->lbl_begin = -1; + ecs_vec_clear(ctx.ops); + + /* Find all variables defined in query */ + if (flecs_query_discover_vars(stage, query)) { + return -1; + } + + /* If query contains fixed source terms, insert operation to set sources */ + int32_t i, term_count = q->term_count; + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + if (term->src.id & EcsIsEntity) { + ecs_query_op_t set_fixed = {0}; + set_fixed.kind = EcsQuerySetFixed; + flecs_query_op_insert(&set_fixed, &ctx); + break; + } + } + + /* If the query contains terms with fixed ids (no wildcards, variables), + * insert instruction that initializes ecs_iter_t::ids. This allows for the + * insertion of simpler instructions later on. + * If the query is entirely cacheable, ids are populated by the cache. */ + if (q->cache_kind != EcsQueryCacheAll) { + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + if (flecs_term_is_fixed_id(q, term) || + (term->src.id & EcsIsEntity && + !(term->src.id & ~EcsTermRefFlags))) + { + ecs_query_op_t set_ids = {0}; + set_ids.kind = EcsQuerySetIds; + flecs_query_op_insert(&set_ids, &ctx); + break; + } + } + } + + ecs_flags64_t compiled = 0; + + /* Always evaluate terms with fixed source before other terms */ + flecs_query_insert_fixed_src_terms( + world, query, &compiled, &ctx); + + /* Compile cacheable terms */ + flecs_query_insert_cache_search(query, &compiled, &ctx); + + /* Insert trivial term search if query allows for it */ + flecs_query_insert_trivial_search(query, &compiled, &ctx); + + /* If a query starts with one or more optional terms, first compile the non + * optional terms. This prevents having to insert an instruction that + * matches the query against every entity in the storage. + * Only skip optional terms at the start of the query so that any + * short-circuiting behavior isn't affected (a non-optional term can become + * optional if it uses a variable set in an optional term). */ + int32_t start_term = 0; + for (; start_term < term_count; start_term ++) { + if (terms[start_term].oper != EcsOptional) { + break; + } + } + + do { + /* Compile remaining query terms to instructions */ + for (i = start_term; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + int32_t compile = i; + + if (compiled & (1ull << i)) { + continue; /* Already compiled */ + } + + if (term->oper == EcsOptional && start_term) { + /* Don't reorder past the first optional term that's not in the + * initial list of optional terms. This protects short + * circuiting branching in the query. + * A future algorithm could look at which variables are + * accessed by optional terms, and continue reordering terms + * that don't access those variables. */ + break; + } + + bool can_reorder = true; + if (term->oper != EcsAnd || flecs_term_is_or(q, term)){ + can_reorder = false; + } + + /* If variables have been written, but this term has no known variables, + * first try to resolve terms that have known variables. This can + * significantly reduce the search space. + * Only perform this optimization after at least one variable has been + * written to, as all terms are unknown otherwise. */ + if (can_reorder && ctx.written && + flecs_query_term_is_unknown(query, term, &ctx)) + { + int32_t term_index = flecs_query_term_next_known( + query, &ctx, i + 1, compiled); + if (term_index != -1) { + term = &q->terms[term_index]; + compile = term_index; + i --; /* Repeat current term */ + } + } + + if (flecs_query_compile_term(world, query, term, &ctx)) { + return -1; + } + + compiled |= (1ull << compile); + } + + if (start_term) { + start_term = 0; /* Repeat, now also insert optional terms */ + } else { + break; + } + } while (true); + + ecs_var_id_t this_id = flecs_query_find_var_id(query, "this", EcsVarEntity); + if (this_id != EcsVarNone) { + /* If This variable has been written as entity, insert an operation to + * assign it to it.entities for consistency. */ + if (ctx.written & (1ull << this_id)) { + ecs_query_op_t set_this = {0}; + set_this.kind = EcsQuerySetThis; + set_this.flags |= (EcsQueryIsVar << EcsQueryFirst); + set_this.first.var = this_id; + flecs_query_op_insert(&set_this, &ctx); + } + } + + /* Make sure non-This variables are written as entities */ + if (query->vars) { + for (i = 0; i < query->var_count; i ++) { + ecs_query_var_t *var = &query->vars[i]; + if (var->id && var->kind == EcsVarTable && var->name) { + ecs_var_id_t var_id = flecs_query_find_var_id(query, var->name, + EcsVarEntity); + if (!flecs_query_is_written(var_id, ctx.written)) { + /* Skip anonymous variables */ + if (!flecs_query_var_is_anonymous(query, var_id)) { + flecs_query_insert_each(var->id, var_id, &ctx, false); + } + } + } + } + } + + /* If query contains non-This variables as term source, build lookup array */ + if (query->src_vars) { + ecs_assert(query->vars != NULL, ECS_INTERNAL_ERROR, NULL); + bool only_anonymous = true; + + for (i = 0; i < q->field_count; i ++) { + ecs_var_id_t var_id = query->src_vars[i]; + if (!var_id) { + continue; + } + + if (!flecs_query_var_is_anonymous(query, var_id)) { + only_anonymous = false; + break; + } else { + /* Don't fetch component data for anonymous variables. Because + * not all metadata (such as it.sources) is initialized for + * anonymous variables, and because they may only be available + * as table variables (each is not guaranteed to be inserted for + * anonymous variables) the iterator may not have sufficient + * information to resolve component data. */ + for (int32_t t = 0; t < q->term_count; t ++) { + ecs_term_t *term = &q->terms[t]; + if (term->field_index == i) { + term->inout = EcsInOutNone; + } + } + } + } + + /* Don't insert setvar instruction if all vars are anonymous */ + if (!only_anonymous) { + ecs_query_op_t set_vars = {0}; + set_vars.kind = EcsQuerySetVars; + flecs_query_op_insert(&set_vars, &ctx); + } + + for (i = 0; i < q->field_count; i ++) { + ecs_var_id_t var_id = query->src_vars[i]; + if (!var_id) { + continue; + } + + if (query->vars[var_id].kind == EcsVarTable) { + var_id = flecs_query_find_var_id(query, query->vars[var_id].name, + EcsVarEntity); + + /* Variables used as source that aren't This must be entities */ + ecs_assert(var_id != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + } + + query->src_vars[i] = var_id; + } + } + + ecs_assert((term_count - ctx.skipped) >= 0, ECS_INTERNAL_ERROR, NULL); + + /* If query is empty, insert Nothing instruction */ + if (!(term_count - ctx.skipped)) { + ecs_vec_clear(ctx.ops); + ecs_query_op_t nothing = {0}; + nothing.kind = EcsQueryNothing; + flecs_query_op_insert(¬hing, &ctx); + } else { + /* If query contains terms for toggleable components, insert toggle */ + if (!(q->flags & EcsQueryTableOnly)) { + flecs_query_insert_toggle(query, &ctx); + } + + /* Insert yield. If program reaches this operation, a result was found */ + ecs_query_op_t yield = {0}; + yield.kind = EcsQueryYield; + flecs_query_op_insert(&yield, &ctx); + } + + int32_t op_count = ecs_vec_count(ctx.ops); + if (op_count) { + query->op_count = op_count; + query->ops = flecs_alloc_n(&stage->allocator, ecs_query_op_t, op_count); + ecs_query_op_t *query_ops = ecs_vec_first_t(ctx.ops, ecs_query_op_t); + ecs_os_memcpy_n(query->ops, query_ops, ecs_query_op_t, op_count); + } + + return 0; +} diff --git a/vendors/flecs/src/query/compiler/compiler.h b/vendors/flecs/src/query/compiler/compiler.h new file mode 100644 index 000000000..be7080273 --- /dev/null +++ b/vendors/flecs/src/query/compiler/compiler.h @@ -0,0 +1,82 @@ +/** + * @file query/compiler/compiler.h + * @brief Query compiler functions. + */ + +#include "../types.h" + +/* Compile query to list of operations */ +int flecs_query_compile( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_query_impl_t *query); + +/* Compile single term */ +int flecs_query_compile_term( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx); + +/* Compile term ref (first, second or src) */ +void flecs_query_compile_term_ref( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_term_ref_t *term_ref, + ecs_query_ref_t *ref, + ecs_flags8_t ref_kind, + ecs_var_kind_t kind, + ecs_query_compile_ctx_t *ctx, + bool create_wildcard_vars); + +/* Mark variable as written */ +void flecs_query_write( + ecs_var_id_t var_id, + uint64_t *written); + +/* Mark variable as written in compiler context */ +void flecs_query_write_ctx( + ecs_var_id_t var_id, + ecs_query_compile_ctx_t *ctx, + bool cond_write); + +/* Add operation to query plan */ +ecs_query_lbl_t flecs_query_op_insert( + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx); + +/* Insert each instruction */ +void flecs_query_insert_each( + ecs_var_id_t tvar, + ecs_var_id_t evar, + ecs_query_compile_ctx_t *ctx, + bool cond_write); + +/* Insert instruction that populates field */ +void flecs_query_insert_populate( + ecs_query_impl_t *query, + ecs_query_compile_ctx_t *ctx, + ecs_flags64_t populated); + +/* Add discovered variable */ +ecs_var_id_t flecs_query_add_var( + ecs_query_impl_t *query, + const char *name, + ecs_vec_t *vars, + ecs_var_kind_t kind); + +/* Find variable by name/kind */ +ecs_var_id_t flecs_query_find_var_id( + const ecs_query_impl_t *query, + const char *name, + ecs_var_kind_t kind); + +ecs_query_op_t* flecs_query_begin_block( + ecs_query_op_kind_t kind, + ecs_query_compile_ctx_t *ctx); + +void flecs_query_end_block( + ecs_query_compile_ctx_t *ctx, + bool reset); + diff --git a/vendors/flecs/src/query/compiler/compiler_term.c b/vendors/flecs/src/query/compiler/compiler_term.c new file mode 100644 index 000000000..1aceb9b44 --- /dev/null +++ b/vendors/flecs/src/query/compiler/compiler_term.c @@ -0,0 +1,1488 @@ +/** + * @file query/compiler/compiler_term.c + * @brief Compile query term. + */ + +#include "../../private_api.h" + +#define FlecsRuleOrMarker ((int16_t)-2) /* Marks instruction in OR chain */ + +ecs_var_id_t flecs_query_find_var_id( + const ecs_query_impl_t *query, + const char *name, + ecs_var_kind_t kind) +{ + ecs_assert(name != NULL, ECS_INTERNAL_ERROR, NULL); + + if (kind == EcsVarTable) { + if (!ecs_os_strcmp(name, EcsThisName)) { + if (query->pub.flags & EcsQueryHasTableThisVar) { + return 0; + } else { + return EcsVarNone; + } + } + + if (!flecs_name_index_is_init(&query->tvar_index)) { + return EcsVarNone; + } + + uint64_t index = flecs_name_index_find( + &query->tvar_index, name, 0, 0); + if (index == 0) { + return EcsVarNone; + } + return flecs_utovar(index); + } + + if (kind == EcsVarEntity) { + if (!flecs_name_index_is_init(&query->evar_index)) { + return EcsVarNone; + } + + uint64_t index = flecs_name_index_find( + &query->evar_index, name, 0, 0); + if (index == 0) { + return EcsVarNone; + } + return flecs_utovar(index); + } + + ecs_assert(kind == EcsVarAny, ECS_INTERNAL_ERROR, NULL); + + /* If searching for any kind of variable, start with most specific */ + ecs_var_id_t index = flecs_query_find_var_id(query, name, EcsVarEntity); + if (index != EcsVarNone) { + return index; + } + + return flecs_query_find_var_id(query, name, EcsVarTable); +} + +static +ecs_var_id_t flecs_query_most_specific_var( + ecs_query_impl_t *query, + const char *name, + ecs_var_kind_t kind, + ecs_query_compile_ctx_t *ctx) +{ + if (kind == EcsVarTable || kind == EcsVarEntity) { + return flecs_query_find_var_id(query, name, kind); + } + + ecs_var_id_t evar = flecs_query_find_var_id(query, name, EcsVarEntity); + if ((evar != EcsVarNone) && flecs_query_is_written(evar, ctx->written)) { + /* If entity variable is available and written to, it contains the most + * specific result and should be used. */ + return evar; + } + + ecs_var_id_t tvar = flecs_query_find_var_id(query, name, EcsVarTable); + if ((tvar != EcsVarNone) && !flecs_query_is_written(tvar, ctx->written)) { + /* If variable of any kind is requested and variable hasn't been written + * yet, write to table variable */ + return tvar; + } + + /* If table var is written, and entity var doesn't exist or is not written, + * return table var */ + if (tvar != EcsVarNone) { + return tvar; + } else { + return evar; + } +} + +ecs_query_lbl_t flecs_query_op_insert( + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t *elem = ecs_vec_append_t(NULL, ctx->ops, ecs_query_op_t); + int32_t count = ecs_vec_count(ctx->ops); + *elem = *op; + if (count > 1) { + if (ctx->cur->lbl_begin == -1) { + /* Variables written by previous instruction can't be written by + * this instruction, except when this is part of an OR chain. */ + elem->written &= ~elem[-1].written; + } + } + + elem->next = flecs_itolbl(count); + elem->prev = flecs_itolbl(count - 2); + return flecs_itolbl(count - 1); +} + +ecs_query_op_t* flecs_query_begin_block( + ecs_query_op_kind_t kind, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t op = {0}; + op.kind = flecs_ito(uint8_t, kind); + ctx->cur->lbl_begin = flecs_query_op_insert(&op, ctx); + return ecs_vec_get_t(ctx->ops, ecs_query_op_t, ctx->cur->lbl_begin); +} + +void flecs_query_end_block( + ecs_query_compile_ctx_t *ctx, + bool reset) +{ + ecs_query_op_t new_op = {0}; + new_op.kind = EcsQueryEnd; + ecs_query_lbl_t end = flecs_query_op_insert(&new_op, ctx); + + ecs_query_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_query_op_t); + ops[ctx->cur->lbl_begin].next = end; + + ecs_query_op_t *end_op = &ops[end]; + if (reset && ctx->cur->lbl_query != -1) { + ecs_query_op_t *query_op = &ops[ctx->cur->lbl_query]; + end_op->prev = ctx->cur->lbl_begin; + end_op->src = query_op->src; + end_op->first = query_op->first; + end_op->second = query_op->second; + end_op->flags = query_op->flags; + end_op->field_index = query_op->field_index; + } else { + end_op->prev = ctx->cur->lbl_begin; + end_op->field_index = -1; + } + + ctx->cur->lbl_begin = -1; +} + +static +void flecs_query_begin_block_cond_eval( + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx, + ecs_write_flags_t cond_write_state) +{ + ecs_var_id_t first_var = EcsVarNone, second_var = EcsVarNone, src_var = EcsVarNone; + ecs_write_flags_t cond_mask = 0; + + if (flecs_query_ref_flags(op->flags, EcsQueryFirst) == EcsQueryIsVar) { + first_var = op->first.var; + ecs_assert(first_var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + cond_mask |= (1ull << first_var); + } + if (flecs_query_ref_flags(op->flags, EcsQuerySecond) == EcsQueryIsVar) { + second_var = op->second.var; + ecs_assert(second_var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + cond_mask |= (1ull << second_var); + } + if (flecs_query_ref_flags(op->flags, EcsQuerySrc) == EcsQueryIsVar) { + src_var = op->src.var; + ecs_assert(src_var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + cond_mask |= (1ull << src_var); + } + + /* Variables set in an OR chain are marked as conditional writes. However, + * writes from previous terms in the current OR chain shouldn't be treated + * as variables that are conditionally set, so instead use the write mask + * from before the chain started. */ + if (ctx->ctrlflow->in_or) { + cond_write_state = ctx->ctrlflow->cond_written_or; + } + + /* If this term uses conditionally set variables, insert instruction that + * jumps over the term if the variables weren't set yet. */ + if (cond_mask & cond_write_state) { + ctx->cur->lbl_cond_eval = flecs_itolbl(ecs_vec_count(ctx->ops)); + + ecs_query_op_t jmp_op = {0}; + jmp_op.kind = EcsQueryIfVar; + + if ((first_var != EcsVarNone) && cond_write_state & (1ull << first_var)) { + jmp_op.flags |= (EcsQueryIsVar << EcsQueryFirst); + jmp_op.first.var = first_var; + } + if ((second_var != EcsVarNone) && cond_write_state & (1ull << second_var)) { + jmp_op.flags |= (EcsQueryIsVar << EcsQuerySecond); + jmp_op.second.var = second_var; + } + if ((src_var != EcsVarNone) && cond_write_state & (1ull << src_var)) { + jmp_op.flags |= (EcsQueryIsVar << EcsQuerySrc); + jmp_op.src.var = src_var; + } + + flecs_query_op_insert(&jmp_op, ctx); + } else { + ctx->cur->lbl_cond_eval = -1; + } +} + +static +void flecs_query_end_block_cond_eval( + ecs_query_compile_ctx_t *ctx) +{ + if (ctx->cur->lbl_cond_eval == -1) { + return; + } + + ecs_assert(ctx->cur->lbl_query >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_query_op_t end_op = {0}; + end_op.kind = EcsQueryEnd; + ecs_query_lbl_t end = flecs_query_op_insert(&end_op, ctx); + + ecs_query_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_query_op_t); + ops[ctx->cur->lbl_cond_eval].next = end; + + ecs_query_op_t *end_op_ptr = &ops[end]; + ecs_query_op_t *query_op = &ops[ctx->cur->lbl_query]; + end_op_ptr->prev = ctx->cur->lbl_cond_eval; + end_op_ptr->src = query_op->src; + end_op_ptr->first = query_op->first; + end_op_ptr->second = query_op->second; + end_op_ptr->flags = query_op->flags; + end_op_ptr->field_index = query_op->field_index; +} + +static +void flecs_query_begin_block_or( + ecs_query_op_t *op, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t *or_op = flecs_query_begin_block(EcsQueryNot, ctx); + or_op->kind = EcsQueryOr; + or_op->field_index = term->field_index; + + /* Set the source of the evaluate terms as source of the Or instruction. + * This lets the engine determine whether the variable has already been + * written. When the source is not yet written, an OR operation needs to + * take the union of all the terms in the OR chain. When the variable is + * known, it will return after the first matching term. + * + * In case a term in the OR expression is an equality predicate which + * compares the left hand side with a variable, the variable acts as an + * alias, so we can always assume that it's written. */ + bool add_src = true; + if (ECS_TERM_REF_ID(&term->first) == EcsPredEq && term->second.id & EcsIsVariable) { + if (!(flecs_query_is_written(op->src.var, ctx->written))) { + add_src = false; + } + } + + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + if (add_src) { + or_op->flags = (EcsQueryIsVar << EcsQuerySrc); + or_op->src = op->src; + ctx->cur->src_or = op->src; + } + + ctx->cur->src_written_or = flecs_query_is_written( + op->src.var, ctx->written); + } +} + +static +void flecs_query_end_block_or( + ecs_query_impl_t *impl, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t op = {0}; + op.kind = EcsQueryEnd; + ecs_query_lbl_t end = flecs_query_op_insert(&op, ctx); + + ecs_query_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_query_op_t); + int32_t i, prev_or = ctx->cur->lbl_begin + 1; + for (i = ctx->cur->lbl_begin + 1; i < end; i ++) { + if (ops[i].next == FlecsRuleOrMarker) { + if (i == (end - 1)) { + ops[prev_or].prev = ctx->cur->lbl_begin; + } else { + ops[prev_or].prev = flecs_itolbl(i + 1); + } + + ops[i].next = flecs_itolbl(end); + + prev_or = i + 1; + } + } + + ecs_query_op_t *first = &ops[ctx->cur->lbl_begin]; + bool src_is_var = first->flags & (EcsQueryIsVar << EcsQuerySrc); + first->next = flecs_itolbl(end); + ops[end].prev = ctx->cur->lbl_begin; + ops[end - 1].prev = ctx->cur->lbl_begin; + + ctx->ctrlflow->in_or = false; + ctx->cur->lbl_begin = -1; + if (src_is_var) { + ecs_var_id_t src_var = first->src.var; + ctx->written |= (1llu << src_var); + + /* If src is a table variable, it is possible that this was resolved to + * an entity variable in all of the OR terms. If this is the case, mark + * entity variable as written as well. */ + ecs_query_var_t *var = &impl->vars[src_var]; + if (var->kind == EcsVarTable) { + const char *name = var->name; + if (!name) { + name = "this"; + } + + ecs_var_id_t evar = flecs_query_find_var_id( + impl, name, EcsVarEntity); + if (evar != EcsVarNone && (ctx->cond_written & (1llu << evar))) { + ctx->written |= (1llu << evar); + ctx->cond_written &= ~(1llu << evar); + } + } + } + ctx->written |= ctx->cond_written; + + /* Scan which variables were conditionally written in the OR chain and + * reset instructions after the OR chain. If a variable is set in part one + * of a chain but not part two, there would be nothing writing to the + * variable in part two, leaving it to the previous value. To address this + * a reset is inserted that resets the variable value on redo. */ + for (i = 1; i < (8 * ECS_SIZEOF(ecs_write_flags_t)); i ++) { + ecs_write_flags_t prev = 1 & (ctx->ctrlflow->cond_written_or >> i); + ecs_write_flags_t cur = 1 & (ctx->cond_written >> i); + + /* Skip variable if it's the source for the OR chain */ + if (src_is_var && (i == first->src.var)) { + continue; + } + + if (!prev && cur) { + ecs_query_op_t reset_op = {0}; + reset_op.kind = EcsQueryReset; + reset_op.flags |= (EcsQueryIsVar << EcsQuerySrc); + reset_op.src.var = flecs_itovar(i); + flecs_query_op_insert(&reset_op, ctx); + } + } +} + +void flecs_query_insert_each( + ecs_var_id_t tvar, + ecs_var_id_t evar, + ecs_query_compile_ctx_t *ctx, + bool cond_write) +{ + ecs_query_op_t each = {0}; + each.kind = EcsQueryEach; + each.src.var = evar; + each.first.var = tvar; + each.flags = (EcsQueryIsVar << EcsQuerySrc) | + (EcsQueryIsVar << EcsQueryFirst); + flecs_query_write_ctx(evar, ctx, cond_write); + flecs_query_write(evar, &each.written); + flecs_query_op_insert(&each, ctx); +} + +static +void flecs_query_insert_lookup( + ecs_var_id_t base_var, + ecs_var_id_t evar, + ecs_query_compile_ctx_t *ctx, + bool cond_write) +{ + ecs_query_op_t lookup = {0}; + lookup.kind = EcsQueryLookup; + lookup.src.var = evar; + lookup.first.var = base_var; + lookup.flags = (EcsQueryIsVar << EcsQuerySrc) | + (EcsQueryIsVar << EcsQueryFirst); + flecs_query_write_ctx(evar, ctx, cond_write); + flecs_query_write(evar, &lookup.written); + flecs_query_op_insert(&lookup, ctx); +} + +static +void flecs_query_insert_unconstrained_transitive( + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx, + bool cond_write) +{ + /* Create anonymous variable to store the target ids. This will return the + * list of targets without constraining the variable of the term, which + * needs to stay variable to find all transitive relationships for a src. */ + ecs_var_id_t tgt = flecs_query_add_var(query, NULL, NULL, EcsVarEntity); + flecs_set_var_label(&query->vars[tgt], query->vars[op->second.var].name); + + /* First, find ids to start traversal from. This fixes op.second. */ + ecs_query_op_t find_ids = {0}; + find_ids.kind = EcsQueryIdsRight; + find_ids.field_index = -1; + find_ids.first = op->first; + find_ids.second = op->second; + find_ids.flags = op->flags; + find_ids.flags &= (ecs_flags8_t)~((EcsQueryIsVar|EcsQueryIsEntity) << EcsQuerySrc); + find_ids.second.var = tgt; + flecs_query_write_ctx(tgt, ctx, cond_write); + flecs_query_write(tgt, &find_ids.written); + flecs_query_op_insert(&find_ids, ctx); + + /* Next, iterate all tables for the ids. This fixes op.src */ + ecs_query_op_t and_op = {0}; + and_op.kind = EcsQueryAnd; + and_op.field_index = op->field_index; + and_op.first = op->first; + and_op.second = op->second; + and_op.src = op->src; + and_op.flags = op->flags | EcsQueryIsSelf; + and_op.second.var = tgt; + flecs_query_write_ctx(and_op.src.var, ctx, cond_write); + flecs_query_write(and_op.src.var, &and_op.written); + flecs_query_op_insert(&and_op, ctx); +} + +static +void flecs_query_insert_inheritance( + ecs_query_impl_t *query, + ecs_term_t *term, + ecs_query_op_t *op, + ecs_query_compile_ctx_t *ctx, + bool cond_write) +{ + /* Anonymous variable to store the resolved component ids */ + ecs_var_id_t tvar = flecs_query_add_var(query, NULL, NULL, EcsVarTable); + ecs_var_id_t evar = flecs_query_add_var(query, NULL, NULL, EcsVarEntity); + + flecs_set_var_label(&query->vars[tvar], ecs_get_name(query->pub.world, + ECS_TERM_REF_ID(&term->first))); + flecs_set_var_label(&query->vars[evar], ecs_get_name(query->pub.world, + ECS_TERM_REF_ID(&term->first))); + + ecs_query_op_t trav_op = {0}; + trav_op.kind = EcsQueryTrav; + trav_op.field_index = -1; + trav_op.first.entity = EcsIsA; + trav_op.second.entity = ECS_TERM_REF_ID(&term->first); + trav_op.src.var = tvar; + trav_op.flags = EcsQueryIsSelf; + trav_op.flags |= (EcsQueryIsEntity << EcsQueryFirst); + trav_op.flags |= (EcsQueryIsEntity << EcsQuerySecond); + trav_op.flags |= (EcsQueryIsVar << EcsQuerySrc); + trav_op.written |= (1ull << tvar); + if (term->first.id & EcsSelf) { + trav_op.match_flags |= EcsTermReflexive; + } + flecs_query_op_insert(&trav_op, ctx); + flecs_query_insert_each(tvar, evar, ctx, cond_write); + + ecs_query_ref_t r = { .var = evar }; + op->first = r; + op->flags &= (ecs_flags8_t)~(EcsQueryIsEntity << EcsQueryFirst); + op->flags |= (EcsQueryIsVar << EcsQueryFirst); +} + +void flecs_query_compile_term_ref( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_term_ref_t *term_ref, + ecs_query_ref_t *ref, + ecs_flags8_t ref_kind, + ecs_var_kind_t kind, + ecs_query_compile_ctx_t *ctx, + bool create_wildcard_vars) +{ + (void)world; + + if (!ecs_term_ref_is_set(term_ref)) { + return; + } + + if (term_ref->id & EcsIsVariable) { + op->flags |= (ecs_flags8_t)(EcsQueryIsVar << ref_kind); + const char *name = flecs_term_ref_var_name(term_ref); + if (name) { + ref->var = flecs_query_most_specific_var(query, name, kind, ctx); + ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + } else if (create_wildcard_vars) { + bool is_wildcard = flecs_term_ref_is_wildcard(term_ref); + if (is_wildcard && (kind == EcsVarAny)) { + ref->var = flecs_query_add_var(query, NULL, NULL, EcsVarTable); + } else { + ref->var = flecs_query_add_var(query, NULL, NULL, EcsVarEntity); + } + if (is_wildcard) { + flecs_set_var_label(&query->vars[ref->var], + ecs_get_name(world, ECS_TERM_REF_ID(term_ref))); + } + ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + } + } + + if (term_ref->id & EcsIsEntity) { + op->flags |= (ecs_flags8_t)(EcsQueryIsEntity << ref_kind); + ref->entity = ECS_TERM_REF_ID(term_ref); + } +} + +static +int flecs_query_compile_ensure_vars( + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + ecs_query_compile_ctx_t *ctx, + bool cond_write, + bool *written_out) +{ + ecs_flags16_t flags = flecs_query_ref_flags(op->flags, ref_kind); + bool written = false; + + if (flags & EcsQueryIsVar) { + ecs_var_id_t var_id = ref->var; + ecs_query_var_t *var = &query->vars[var_id]; + + if (var->kind == EcsVarEntity && + !flecs_query_is_written(var_id, ctx->written)) + { + /* If entity variable is not yet written but a table variant exists + * that has been written, insert each operation to translate from + * entity variable to table */ + ecs_var_id_t tvar = var->table_id; + if ((tvar != EcsVarNone) && + flecs_query_is_written(tvar, ctx->written)) + { + if (var->lookup) { + if (!flecs_query_is_written(tvar, ctx->written)) { + ecs_err("dependent variable of '$%s' is not written", + var->name); + return -1; + } + + if (!flecs_query_is_written(var->base_id, ctx->written)) { + flecs_query_insert_each( + tvar, var->base_id, ctx, cond_write); + } + } else { + flecs_query_insert_each(tvar, var_id, ctx, cond_write); + } + + /* Variable was written, just not as entity */ + written = true; + } else if (var->lookup) { + if (!flecs_query_is_written(var->base_id, ctx->written)) { + ecs_err("dependent variable of '$%s' is not written", + var->name); + return -1; + } + } + } + + written |= flecs_query_is_written(var_id, ctx->written); + } else { + /* If it's not a variable, it's always written */ + written = true; + } + + if (written_out) { + *written_out = written; + } + + return 0; +} + +static +bool flecs_query_compile_lookup( + ecs_query_impl_t *query, + ecs_var_id_t var_id, + ecs_query_compile_ctx_t *ctx, + bool cond_write) +{ + ecs_query_var_t *var = &query->vars[var_id]; + if (var->lookup) { + flecs_query_insert_lookup(var->base_id, var_id, ctx, cond_write); + return true; + } else { + return false; + } +} + +static +void flecs_query_insert_contains( + ecs_query_impl_t *query, + ecs_var_id_t src_var, + ecs_var_id_t other_var, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t contains = {0}; + if ((src_var != other_var) && (src_var == query->vars[other_var].table_id)) { + contains.kind = EcsQueryContain; + contains.src.var = src_var; + contains.first.var = other_var; + contains.flags |= (EcsQueryIsVar << EcsQuerySrc) | + (EcsQueryIsVar << EcsQueryFirst); + flecs_query_op_insert(&contains, ctx); + } +} + +static +void flecs_query_insert_pair_eq( + int32_t field_index, + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t contains = {0}; + contains.kind = EcsQueryPairEq; + contains.field_index = flecs_ito(int8_t, field_index); + flecs_query_op_insert(&contains, ctx); +} + +static +int flecs_query_compile_builtin_pred( + ecs_query_t *q, + ecs_term_t *term, + ecs_query_op_t *op, + ecs_write_flags_t write_state) +{ + ecs_entity_t id = ECS_TERM_REF_ID(&term->first); + + ecs_query_op_kind_t eq[] = {EcsQueryPredEq, EcsQueryPredNeq}; + ecs_query_op_kind_t eq_name[] = {EcsQueryPredEqName, EcsQueryPredNeqName}; + ecs_query_op_kind_t eq_match[] = {EcsQueryPredEqMatch, EcsQueryPredNeqMatch}; + + ecs_flags16_t flags_src = flecs_query_ref_flags(op->flags, EcsQuerySrc); + ecs_flags16_t flags_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); + + if (id == EcsPredEq) { + if (term->second.id & EcsIsName) { + op->kind = flecs_ito(uint8_t, eq_name[term->oper == EcsNot]); + } else { + op->kind = flecs_ito(uint8_t, eq[term->oper == EcsNot]); + } + } else if (id == EcsPredMatch) { + op->kind = flecs_ito(uint8_t, eq_match[term->oper == EcsNot]); + } + + if (flags_2nd & EcsQueryIsVar) { + if (!(write_state & (1ull << op->second.var))) { + ecs_err("uninitialized variable '%s' on right-hand side of " + "equality operator", ecs_query_var_name(q, op->second.var)); + return -1; + } + } + + ecs_assert(flags_src & EcsQueryIsVar, ECS_INTERNAL_ERROR, NULL); + (void)flags_src; + + if (!(write_state & (1ull << op->src.var))) { + /* If this is an == operator with a right-hand side that resolves to a + * single entity, the left-hand side is allowed to be undefined, as the + * instruction will be evaluated as an assignment. */ + if (op->kind != EcsQueryPredEq && op->kind != EcsQueryPredEqName) { + ecs_err("uninitialized variable '%s' on left-hand side of " + "equality operator", ecs_query_var_name(q, op->src.var)); + return -1; + } + } + + return 0; +} + +static +int flecs_query_ensure_scope_var( + ecs_query_impl_t *query, + ecs_query_op_t *op, + ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + ecs_query_compile_ctx_t *ctx) +{ + ecs_var_id_t var = ref->var; + + if (query->vars[var].kind == EcsVarEntity && + !flecs_query_is_written(var, ctx->written)) + { + ecs_var_id_t table_var = query->vars[var].table_id; + if (table_var != EcsVarNone && + flecs_query_is_written(table_var, ctx->written)) + { + if (flecs_query_compile_ensure_vars( + query, op, ref, ref_kind, ctx, false, NULL)) + { + goto error; + } + } + } + + return 0; +error: + return -1; +} + +static +int flecs_query_ensure_scope_vars( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_query_compile_ctx_t *ctx, + ecs_term_t *term) +{ + /* If the scope uses variables as entity that have only been written as + * table, resolve them as entities before entering the scope. */ + ecs_term_t *cur = term; + while(ECS_TERM_REF_ID(&cur->first) != EcsScopeClose) { + /* Dummy operation to obtain variable information for term */ + ecs_query_op_t op = {0}; + flecs_query_compile_term_ref(world, query, &op, &cur->first, + &op.first, EcsQueryFirst, EcsVarEntity, ctx, false); + flecs_query_compile_term_ref(world, query, &op, &cur->second, + &op.second, EcsQuerySecond, EcsVarEntity, ctx, false); + + if (op.flags & (EcsQueryIsVar << EcsQueryFirst)) { + if (flecs_query_ensure_scope_var( + query, &op, &op.first, EcsQueryFirst, ctx)) + { + goto error; + } + } + + if (op.flags & (EcsQueryIsVar << EcsQuerySecond)) { + if (flecs_query_ensure_scope_var( + query, &op, &op.second, EcsQuerySecond, ctx)) + { + goto error; + } + } + + cur ++; + } + + return 0; +error: + return -1; +} + +static +void flecs_query_compile_push( + ecs_query_compile_ctx_t *ctx) +{ + ctx->cur = &ctx->ctrlflow[++ ctx->scope]; + ctx->cur->lbl_begin = -1; + ctx->cur->lbl_begin = -1; +} + +static +void flecs_query_compile_pop( + ecs_query_compile_ctx_t *ctx) +{ + ctx->cur = &ctx->ctrlflow[-- ctx->scope]; +} + +static +int flecs_query_compile_0_src( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx) +{ + /* If the term has a 0 source, check if it's a scope open/close */ + if (ECS_TERM_REF_ID(&term->first) == EcsScopeOpen) { + if (flecs_query_ensure_scope_vars(world, impl, ctx, term)) { + goto error; + } + if (term->oper == EcsNot) { + ctx->scope_is_not |= (ecs_flags32_t)(1ull << ctx->scope); + flecs_query_begin_block(EcsQueryNot, ctx); + } else { + ctx->scope_is_not &= (ecs_flags32_t)~(1ull << ctx->scope); + } + flecs_query_compile_push(ctx); + } else if (ECS_TERM_REF_ID(&term->first) == EcsScopeClose) { + flecs_query_compile_pop(ctx); + if (ctx->scope_is_not & (ecs_flags32_t)(1ull << (ctx->scope))) { + flecs_query_end_block(ctx, false); + } + } else { + /* Noop */ + } + + return 0; +error: + return -1; +} + +static +ecs_flags32_t flecs_query_to_table_flags( + const ecs_query_t *q) +{ + ecs_flags32_t query_flags = q->flags; + if (!(query_flags & EcsQueryMatchDisabled) || + !(query_flags & EcsQueryMatchPrefab)) + { + ecs_flags32_t table_flags = EcsTableNotQueryable; + if (!(query_flags & EcsQueryMatchDisabled)) { + table_flags |= EcsTableIsDisabled; + } + if (!(query_flags & EcsQueryMatchPrefab)) { + table_flags |= EcsTableIsPrefab; + } + + return table_flags; + } + return 0; +} + +static +bool flecs_query_select_all( + const ecs_query_t *q, + ecs_term_t *term, + ecs_query_op_t *op, + ecs_var_id_t src_var, + ecs_query_compile_ctx_t *ctx) +{ + bool builtin_pred = flecs_term_is_builtin_pred(term); + bool pred_match = builtin_pred && ECS_TERM_REF_ID(&term->first) == EcsPredMatch; + + if (term->oper == EcsNot || term->oper == EcsOptional || + term->oper == EcsNotFrom || pred_match) + { + ecs_query_op_t match_any = {0}; + match_any.kind = EcsAnd; + match_any.flags = EcsQueryIsSelf | (EcsQueryIsEntity << EcsQueryFirst); + match_any.flags |= (EcsQueryIsVar << EcsQuerySrc); + match_any.src = op->src; + match_any.field_index = -1; + if (!pred_match) { + match_any.first.entity = EcsAny; + } else { + /* If matching by name, instead of finding all tables, just find + * the ones with a name. */ + match_any.first.entity = ecs_id(EcsIdentifier); + match_any.second.entity = EcsName; + match_any.flags |= (EcsQueryIsEntity << EcsQuerySecond); + } + match_any.written = (1ull << src_var); + match_any.other = flecs_itolbl(flecs_query_to_table_flags(q)); + flecs_query_op_insert(&match_any, ctx); + flecs_query_write_ctx(op->src.var, ctx, false); + + /* Update write administration */ + return true; + } + return false; +} + +#ifdef FLECS_META +static +int flecs_query_compile_begin_member_term( + ecs_world_t *world, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx, + ecs_entity_t first_id) +{ + ecs_assert(first_id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(first_id & EcsIsEntity, ECS_INTERNAL_ERROR, NULL); + + first_id = ECS_TERM_REF_ID(&term->first); + + /* First compile as if it's a regular term, to match the component */ + term->flags_ &= (uint16_t)~EcsTermIsMember; + + /* Replace term id with member parent (the component) */ + ecs_entity_t component = ecs_get_parent(world, first_id); + if (!component) { + ecs_err("member without parent in query"); + return -1; + } + + if (!ecs_has(world, component, EcsComponent)) { + ecs_err("parent of member is not a component"); + return -1; + } + + bool second_wildcard = + (ECS_TERM_REF_ID(&term->second) == EcsWildcard || + ECS_TERM_REF_ID(&term->second) == EcsAny) && + (term->second.id & EcsIsVariable) && !term->second.name; + + term->first.id = component | ECS_TERM_REF_FLAGS(&term->first); + term->second.id = 0; + term->id = component; + + ctx->oper = (ecs_oper_kind_t)term->oper; + if (term->oper == EcsNot && !second_wildcard) { + /* When matching a member term with not operator, we need to cover both + * the case where an entity doesn't have the component, and where it + * does have the component, but doesn't match the member. */ + term->oper = EcsOptional; + } + + return 0; +} + +static +int flecs_query_compile_end_member_term( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_query_op_t *op, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx, + ecs_id_t term_id, + ecs_entity_t first_id, + ecs_entity_t second_id, + bool cond_write) +{ + ecs_entity_t component = ECS_TERM_REF_ID(&term->first); + const EcsComponent *comp = ecs_get(world, component, EcsComponent); + ecs_assert(comp != NULL, ECS_INTERNAL_ERROR, NULL); + + /* Restore term values */ + term->id = term_id; + term->first.id = first_id; + term->second.id = second_id; + term->flags_ |= EcsTermIsMember; + term->oper = flecs_ito(int16_t, ctx->oper); + + first_id = ECS_TERM_REF_ID(&term->first); + const EcsMember *member = ecs_get(world, first_id, EcsMember); + ecs_assert(member != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_query_var_t *var = &impl->vars[op->src.var]; + const char *var_name = flecs_term_ref_var_name(&term->src); + ecs_var_id_t evar = flecs_query_find_var_id( + impl, var_name, EcsVarEntity); + + bool second_wildcard = + (ECS_TERM_REF_ID(&term->second) == EcsWildcard || + ECS_TERM_REF_ID(&term->second) == EcsAny) && + (term->second.id & EcsIsVariable) && !term->second.name; + + if (term->oper == EcsOptional) { + second_wildcard = true; + } + + ecs_query_op_t mbr_op = *op; + mbr_op.kind = EcsQueryMemberEq; + mbr_op.first.entity = /* Encode type size and member offset */ + flecs_ito(uint32_t, member->offset) | + (flecs_ito(uint64_t, comp->size) << 32); + + /* If this is a term with a Not operator, conditionally evaluate member on + * whether term was set by previous operation (see begin_member_term). */ + if (ctx->oper == EcsNot || ctx->oper == EcsOptional) { + if (second_wildcard && ctx->oper == EcsNot) { + /* A !(T.value, *) term doesn't need special operations */ + return 0; + } + + /* Resolve to entity variable before entering if block, so that we + * don't have different branches of the query working with different + * versions of the same variable. */ + if (var->kind == EcsVarTable) { + flecs_query_insert_each(op->src.var, evar, ctx, cond_write); + var = &impl->vars[evar]; + } + + ecs_query_op_t *if_op = flecs_query_begin_block(EcsQueryIfSet, ctx); + if_op->other = term->field_index; + + if (ctx->oper == EcsNot) { + mbr_op.kind = EcsQueryMemberNeq; + } + } + + if (var->kind == EcsVarTable) { + /* If MemberEq is called on table variable, store it on .other member. + * This causes MemberEq to do double duty as 'each' instruction, + * which is faster than having to go back & forth between instructions + * while finding matching values. */ + mbr_op.other = flecs_itolbl(op->src.var + 1); + + /* Mark entity variable as written */ + flecs_query_write_ctx(evar, ctx, cond_write); + flecs_query_write(evar, &mbr_op.written); + } + + flecs_query_compile_term_ref(world, impl, &mbr_op, &term->src, + &mbr_op.src, EcsQuerySrc, EcsVarEntity, ctx, true); + + if (second_wildcard) { + mbr_op.flags |= (EcsQueryIsEntity << EcsQuerySecond); + mbr_op.second.entity = EcsWildcard; + } else { + flecs_query_compile_term_ref(world, impl, &mbr_op, &term->second, + &mbr_op.second, EcsQuerySecond, EcsVarEntity, ctx, true); + + if (term->second.id & EcsIsVariable) { + if (flecs_query_compile_ensure_vars(impl, &mbr_op, &mbr_op.second, + EcsQuerySecond, ctx, cond_write, NULL)) + { + goto error; + } + + flecs_query_write_ctx(mbr_op.second.var, ctx, cond_write); + flecs_query_write(mbr_op.second.var, &mbr_op.written); + } + } + + flecs_query_op_insert(&mbr_op, ctx); + + if (ctx->oper == EcsNot || ctx->oper == EcsOptional) { + flecs_query_end_block(ctx, false); + } + + return 0; +error: + return -1; +} +#else +static +int flecs_query_compile_begin_member_term( + ecs_world_t *world, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx, + ecs_entity_t first_id) +{ + (void)world; (void)term; (void)ctx; (void)first_id; + return 0; +} + +static +int flecs_query_compile_end_member_term( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_query_op_t *op, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx, + ecs_id_t term_id, + ecs_entity_t first_id, + ecs_entity_t second_id, + bool cond_write) +{ + (void)world; (void)impl; (void)op; (void)term; (void)ctx; (void)term_id; + (void)first_id; (void)second_id; (void)cond_write; + return 0; +} +#endif + +static +void flecs_query_mark_last_or_op( + ecs_query_compile_ctx_t *ctx) +{ + ecs_query_op_t *op_ptr = ecs_vec_last_t(ctx->ops, ecs_query_op_t); + op_ptr->next = FlecsRuleOrMarker; +} + +static +void flecs_query_set_op_kind( + ecs_query_op_t *op, + ecs_term_t *term, + bool src_is_var) +{ + /* Default instruction for And operators. If the source is fixed (like for + * singletons or terms with an entity source), use With, which like And but + * just matches against a source (vs. finding a source). */ + op->kind = src_is_var ? EcsQueryAnd : EcsQueryWith; + + /* Ignore cascade flag */ + ecs_entity_t trav_flags = EcsTraverseFlags & ~(EcsCascade|EcsDesc); + + /* Handle *From operators */ + if (term->oper == EcsAndFrom) { + op->kind = EcsQueryAndFrom; + } else if (term->oper == EcsOrFrom) { + op->kind = EcsQueryOrFrom; + } else if (term->oper == EcsNotFrom) { + op->kind = EcsQueryNotFrom; + + /* If query is transitive, use Trav(ersal) instruction */ + } else if (term->flags_ & EcsTermTransitive) { + ecs_assert(ecs_term_ref_is_set(&term->second), ECS_INTERNAL_ERROR, NULL); + op->kind = EcsQueryTrav; + + /* If term queries for union pair, use union instruction */ + } else if (term->flags_ & EcsTermIsUnion) { + if (op->kind == EcsQueryAnd) { + op->kind = EcsQueryUnionEq; + if (term->oper == EcsNot) { + if (!ecs_id_is_wildcard(ECS_TERM_REF_ID(&term->second))) { + term->oper = EcsAnd; + op->kind = EcsQueryUnionNeq; + } + } + } else { + op->kind = EcsQueryUnionEqWith; + } + + if ((term->src.id & trav_flags) == EcsUp) { + if (op->kind == EcsQueryUnionEq) { + op->kind = EcsQueryUnionEqUp; + } + } else if ((term->src.id & trav_flags) == (EcsSelf|EcsUp)) { + if (op->kind == EcsQueryUnionEq) { + op->kind = EcsQueryUnionEqSelfUp; + } + } + } else { + if ((term->src.id & trav_flags) == EcsUp) { + op->kind = EcsQueryUp; + } else if ((term->src.id & trav_flags) == (EcsSelf|EcsUp)) { + op->kind = EcsQuerySelfUp; + } else if (term->flags_ & (EcsTermMatchAny|EcsTermMatchAnySrc)) { + op->kind = EcsQueryAndAny; + } + } +} + +int flecs_query_compile_term( + ecs_world_t *world, + ecs_query_impl_t *query, + ecs_term_t *term, + ecs_query_compile_ctx_t *ctx) +{ + ecs_id_t term_id = term->id; + ecs_entity_t first_id = term->first.id; + ecs_entity_t second_id = term->second.id; + bool toggle_term = (term->flags_ & EcsTermIsToggle) != 0; + bool member_term = (term->flags_ & EcsTermIsMember) != 0; + if (member_term) { + flecs_query_compile_begin_member_term(world, term, ctx, first_id); + } + + ecs_query_t *q = &query->pub; + bool first_term = term == q->terms; + bool first_is_var = term->first.id & EcsIsVariable; + bool second_is_var = term->second.id & EcsIsVariable; + bool src_is_var = term->src.id & EcsIsVariable; + bool src_is_wildcard = src_is_var && + (ECS_TERM_REF_ID(&term->src) == EcsWildcard || + ECS_TERM_REF_ID(&term->src) == EcsAny); + bool src_is_lookup = false; + bool builtin_pred = flecs_term_is_builtin_pred(term); + bool is_optional = (term->oper == EcsOptional); + bool is_or = flecs_term_is_or(q, term); + bool first_or = false, last_or = false; + bool cond_write = term->oper == EcsOptional || is_or; + ecs_query_op_t op = {0}; + + if (is_or) { + first_or = first_term || (term[-1].oper != EcsOr); + last_or = term->oper != EcsOr; + } + + if (term->oper == EcsAndFrom || term->oper == EcsOrFrom || + term->oper == EcsNotFrom) + { + const ecs_type_t *type = ecs_get_type(world, term->id); + if (!type) { + /* Empty type for id in *From operation is a noop */ + ctx->skipped ++; + return 0; + } + + int32_t i, count = type->count; + ecs_id_t *ti_ids = type->array; + + for (i = 0; i < count; i ++) { + ecs_id_t ti_id = ti_ids[i]; + ecs_id_record_t *idr = flecs_id_record_get(world, ti_id); + if (!(idr->flags & EcsIdOnInstantiateDontInherit)) { + break; + } + } + + if (i == count) { + /* Type did not contain any ids to perform operation on */ + ctx->skipped ++; + return 0; + } + } + + /* !_ (don't match anything) terms always return nothing. */ + if (term->oper == EcsNot && term->id == EcsAny) { + op.kind = EcsQueryNothing; + flecs_query_op_insert(&op, ctx); + return 0; + } + + if (first_or) { + ctx->ctrlflow->cond_written_or = ctx->cond_written; + ctx->ctrlflow->in_or = true; + } else if (is_or) { + ctx->written = ctx->ctrlflow->written_or; + } + + if (!ECS_TERM_REF_ID(&term->src) && term->src.id & EcsIsEntity) { + if (flecs_query_compile_0_src(world, query, term, ctx)) { + goto error; + } + return 0; + } + + if (builtin_pred) { + ecs_entity_t id_noflags = ECS_TERM_REF_ID(&term->second); + if (id_noflags == EcsWildcard || id_noflags == EcsAny) { + /* Noop */ + return 0; + } + } + + op.field_index = flecs_ito(int8_t, term->field_index); + op.term_index = flecs_ito(int8_t, term - q->terms); + + flecs_query_set_op_kind(&op, term, src_is_var); + + bool is_not = (term->oper == EcsNot) && !builtin_pred; + + /* Save write state at start of term so we can use it to reliably track + * variables got written by this term. */ + ecs_write_flags_t cond_write_state = ctx->cond_written; + + /* Resolve variables and entities for operation arguments */ + flecs_query_compile_term_ref(world, query, &op, &term->first, + &op.first, EcsQueryFirst, EcsVarEntity, ctx, true); + flecs_query_compile_term_ref(world, query, &op, &term->second, + &op.second, EcsQuerySecond, EcsVarEntity, ctx, true); + flecs_query_compile_term_ref(world, query, &op, &term->src, + &op.src, EcsQuerySrc, EcsVarAny, ctx, true); + + bool src_written = true; + if (src_is_var) { + src_is_lookup = query->vars[op.src.var].lookup != NULL; + src_written = flecs_query_is_written(op.src.var, ctx->written); + } + + /* Insert each instructions for table -> entity variable if needed */ + bool first_written, second_written; + if (flecs_query_compile_ensure_vars( + query, &op, &op.first, EcsQueryFirst, ctx, cond_write, &first_written)) + { + goto error; + } + + if (flecs_query_compile_ensure_vars( + query, &op, &op.second, EcsQuerySecond, ctx, cond_write, &second_written)) + { + goto error; + } + + /* Store write state of variables for first OR term in chain which will get + * restored for the other terms in the chain, so that all OR terms make the + * same assumptions about which variables were already written. */ + if (first_or) { + ctx->ctrlflow->written_or = ctx->written; + } + + /* If an optional or not term is inserted for a source that's not been + * written to yet, insert instruction that selects all entities so we have + * something to match the optional/not against. */ + if (src_is_var && !src_written && !src_is_wildcard && !src_is_lookup) { + src_written = flecs_query_select_all(q, term, &op, op.src.var, ctx); + } + + /* A bit of special logic for OR expressions and equality predicates. If the + * left-hand of an equality operator is a table, and there are multiple + * operators in an Or expression, the Or chain should match all entities in + * the table that match the right hand sides of the operator expressions. + * For this to work, the src variable needs to be resolved as entity, as an + * Or chain would otherwise only yield the first match from a table. */ + if (src_is_var && src_written && (builtin_pred || member_term) && term->oper == EcsOr) { + if (query->vars[op.src.var].kind == EcsVarTable) { + flecs_query_compile_term_ref(world, query, &op, &term->src, + &op.src, EcsQuerySrc, EcsVarEntity, ctx, true); + ctx->ctrlflow->written_or |= (1llu << op.src.var); + } + } + + if (flecs_query_compile_ensure_vars( + query, &op, &op.src, EcsQuerySrc, ctx, cond_write, NULL)) + { + goto error; + } + + /* If source is Any (_) and first and/or second are unconstrained, insert an + * ids instruction instead of an And */ + if (term->flags_ & EcsTermMatchAnySrc) { + op.kind = EcsQueryIds; + /* Use up-to-date written values after potentially inserting each */ + if (!first_written || !second_written) { + if (!first_written) { + /* If first is unknown, traverse left: <- (*, t) */ + if (ECS_TERM_REF_ID(&term->first) != EcsAny) { + op.kind = EcsQueryIdsLeft; + } + } else { + /* If second is wildcard, traverse right: (r, *) -> */ + if (ECS_TERM_REF_ID(&term->second) != EcsAny) { + op.kind = EcsQueryIdsRight; + } + } + op.src.entity = 0; + src_is_var = false; + op.flags &= (ecs_flags8_t)~(EcsQueryIsVar << EcsQuerySrc); /* ids has no src */ + op.flags &= (ecs_flags8_t)~(EcsQueryIsEntity << EcsQuerySrc); + } + + /* If source variable is not written and we're querying just for Any, insert + * a dedicated instruction that uses the Any record in the id index. Any + * queries that are evaluated against written sources can use Wildcard + * records, which is what the AndAny instruction does. */ + } else if (!src_written && term->id == EcsAny && op.kind == EcsQueryAndAny) { + /* Lookup variables ($var.child_name) are always written */ + if (!src_is_lookup) { + op.kind = EcsQueryOnlyAny; /* Uses Any (_) id record */ + } + } + + /* If this is a transitive term and both the target and source are unknown, + * find the targets for the relationship first. This clusters together + * tables for the same target, which allows for more efficient usage of the + * traversal caches. */ + if (term->flags_ & EcsTermTransitive && src_is_var && second_is_var) { + if (!src_written && !second_written) { + flecs_query_insert_unconstrained_transitive( + query, &op, ctx, cond_write); + } + } + + /* Check if this term has variables that have been conditionally written, + * like variables written by an optional term. */ + if (ctx->cond_written) { + if (!is_or || first_or) { + flecs_query_begin_block_cond_eval(&op, ctx, cond_write_state); + } + } + + /* If term can toggle and is Not, change operator to Optional as we + * have to match entities that have the component but disabled. */ + if (toggle_term && is_not) { + is_not = false; + is_optional = true; + } + + /* Handle Not, Optional, Or operators */ + if (is_not) { + flecs_query_begin_block(EcsQueryNot, ctx); + } else if (is_optional) { + flecs_query_begin_block(EcsQueryOptional, ctx); + } else if (first_or) { + flecs_query_begin_block_or(&op, term, ctx); + } + + /* If term has component inheritance enabled, insert instruction to walk + * down the relationship tree of the id. */ + if (term->flags_ & EcsTermIdInherited) { + flecs_query_insert_inheritance(query, term, &op, ctx, cond_write); + } + + op.match_flags = term->flags_; + + ecs_write_flags_t write_state = ctx->written; + if (first_is_var) { + op.flags &= (ecs_flags8_t)~(EcsQueryIsEntity << EcsQueryFirst); + op.flags |= (EcsQueryIsVar << EcsQueryFirst); + } + + if (term->src.id & EcsSelf) { + op.flags |= EcsQueryIsSelf; + } + + /* Insert instructions for lookup variables */ + if (first_is_var) { + if (flecs_query_compile_lookup(query, op.first.var, ctx, cond_write)) { + write_state |= (1ull << op.first.var); // lookups are resolved inline + } + } + if (src_is_var) { + if (flecs_query_compile_lookup(query, op.src.var, ctx, cond_write)) { + write_state |= (1ull << op.src.var); // lookups are resolved inline + } + } + if (second_is_var) { + if (flecs_query_compile_lookup(query, op.second.var, ctx, cond_write)) { + write_state |= (1ull << op.second.var); // lookups are resolved inline + } + } + + if (builtin_pred) { + if (flecs_query_compile_builtin_pred(q, term, &op, write_state)) { + goto error; + } + } + + /* If we're writing the $this variable, filter out disabled/prefab entities + * unless the query explicitly matches them. + * This could've been done with regular With instructions, but since + * filtering out disabled/prefab entities is the default and this check is + * cheap to perform on table flags, it's worth special casing. */ + if (!src_written && op.src.var == 0) { + op.other = flecs_itolbl(flecs_query_to_table_flags(q)); + } + + /* After evaluating a term, a used variable is always written */ + if (src_is_var) { + flecs_query_write(op.src.var, &op.written); + flecs_query_write_ctx(op.src.var, ctx, cond_write); + } + if (first_is_var) { + flecs_query_write(op.first.var, &op.written); + flecs_query_write_ctx(op.first.var, ctx, cond_write); + } + if (second_is_var) { + flecs_query_write(op.second.var, &op.written); + flecs_query_write_ctx(op.second.var, ctx, cond_write); + } + + flecs_query_op_insert(&op, ctx); + + ctx->cur->lbl_query = flecs_itolbl(ecs_vec_count(ctx->ops) - 1); + if (is_or && !member_term) { + flecs_query_mark_last_or_op(ctx); + } + + /* Handle self-references between src and first/second variables */ + if (src_is_var) { + if (first_is_var) { + flecs_query_insert_contains(query, op.src.var, op.first.var, ctx); + } + if (second_is_var && op.first.var != op.second.var) { + flecs_query_insert_contains(query, op.src.var, op.second.var, ctx); + } + } + + /* Handle self references between first and second variables */ + if (!ecs_id_is_wildcard(first_id)) { + if (first_is_var && !first_written && (op.first.var == op.second.var)) { + flecs_query_insert_pair_eq(term->field_index, ctx); + } + } + + /* Handle closing of Not, Optional and Or operators */ + if (is_not) { + flecs_query_end_block(ctx, true); + } else if (is_optional) { + flecs_query_end_block(ctx, true); + } + + /* Now that the term is resolved, evaluate member of component */ + if (member_term) { + flecs_query_compile_end_member_term(world, query, &op, term, ctx, + term_id, first_id, second_id, cond_write); + if (is_or) { + flecs_query_mark_last_or_op(ctx); + } + } + + if (last_or) { + flecs_query_end_block_or(query, ctx); + } + + /* Handle closing of conditional evaluation */ + if (ctx->cur->lbl_cond_eval && (first_is_var || second_is_var || src_is_var)) { + if (!is_or || last_or) { + flecs_query_end_block_cond_eval(ctx); + } + } + + /* Ensure that term id is set after evaluating Not */ + if (term->flags_ & EcsTermIdInherited) { + if (is_not) { + ecs_query_op_t set_id = {0}; + set_id.kind = EcsQuerySetId; + set_id.first.entity = term->id; + set_id.flags = (EcsQueryIsEntity << EcsQueryFirst); + set_id.field_index = flecs_ito(int8_t, term->field_index); + flecs_query_op_insert(&set_id, ctx); + } + } + + return 0; +error: + return -1; +} diff --git a/vendors/flecs/src/query/engine/cache.c b/vendors/flecs/src/query/engine/cache.c new file mode 100644 index 000000000..5529fe9bc --- /dev/null +++ b/vendors/flecs/src/query/engine/cache.c @@ -0,0 +1,1422 @@ +/** + * @file query/engine/cache.c + * @brief Cached query implementation. + */ + +#include "../../private_api.h" + +int32_t flecs_query_cache_table_count( + ecs_query_cache_t *cache) +{ + ecs_run_aperiodic(cache->query->world, EcsAperiodicEmptyTables); + return cache->cache.tables.count; +} + +int32_t flecs_query_cache_empty_table_count( + ecs_query_cache_t *cache) +{ + ecs_run_aperiodic(cache->query->world, EcsAperiodicEmptyTables); + return cache->cache.empty_tables.count; +} + +int32_t flecs_query_cache_entity_count( + const ecs_query_cache_t *cache) +{ + ecs_run_aperiodic(cache->query->world, EcsAperiodicEmptyTables); + + int32_t result = 0; + ecs_table_cache_hdr_t *cur, *last = cache->cache.tables.last; + if (!last) { + return 0; + } + + for (cur = cache->cache.tables.first; cur != NULL; cur = cur->next) { + result += ecs_table_count(cur->table); + } + + return result; +} + +static +uint64_t flecs_query_cache_get_group_id( + ecs_query_cache_t *cache, + ecs_table_t *table) +{ + if (cache->group_by_callback) { + return cache->group_by_callback(cache->query->world, table, + cache->group_by, cache->group_by_ctx); + } else { + return 0; + } +} + +static +void flecs_query_cache_compute_group_id( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) +{ + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); + + if (cache->group_by_callback) { + ecs_table_t *table = match->table; + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + + match->group_id = flecs_query_cache_get_group_id(cache, table); + } else { + match->group_id = 0; + } +} + +static +ecs_query_cache_table_list_t* flecs_query_cache_get_group( + const ecs_query_cache_t *cache, + uint64_t group_id) +{ + return ecs_map_get_deref( + &cache->groups, ecs_query_cache_table_list_t, group_id); +} + +static +ecs_query_cache_table_list_t* flecs_query_cache_ensure_group( + ecs_query_cache_t *cache, + uint64_t id) +{ + ecs_query_cache_table_list_t *group = ecs_map_get_deref(&cache->groups, + ecs_query_cache_table_list_t, id); + + if (!group) { + group = ecs_map_insert_alloc_t(&cache->groups, + ecs_query_cache_table_list_t, id); + ecs_os_zeromem(group); + if (cache->on_group_create) { + group->info.ctx = cache->on_group_create( + cache->query->world, id, cache->group_by_ctx); + } + } + + return group; +} + +static +void flecs_query_cache_remove_group( + ecs_query_cache_t *cache, + uint64_t id) +{ + if (cache->on_group_delete) { + ecs_query_cache_table_list_t *group = ecs_map_get_deref(&cache->groups, + ecs_query_cache_table_list_t, id); + if (group) { + cache->on_group_delete(cache->query->world, id, + group->info.ctx, cache->group_by_ctx); + } + } + + ecs_map_remove_free(&cache->groups, id); +} + +static +uint64_t flecs_query_cache_default_group_by( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + void *ctx) +{ + (void)ctx; + + ecs_id_t match; + if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &match) != -1) { + return ecs_pair_second(world, match); + } + return 0; +} + +/* Find the last node of the group after which this group should be inserted */ +static +ecs_query_cache_table_match_t* flecs_query_cache_find_group_insertion_node( + ecs_query_cache_t *cache, + uint64_t group_id) +{ + /* Grouping must be enabled */ + ecs_assert(cache->group_by_callback != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_map_iter_t it = ecs_map_iter(&cache->groups); + ecs_query_cache_table_list_t *list, *closest_list = NULL; + uint64_t id, closest_id = 0; + + bool desc = false; + + if (cache->cascade_by) { + desc = (cache->query->terms[ + cache->cascade_by - 1].src.id & EcsDesc) != 0; + } + + /* Find closest smaller group id */ + while (ecs_map_next(&it)) { + id = ecs_map_key(&it); + + if (!desc) { + if (id >= group_id) { + continue; + } + } else { + if (id <= group_id) { + continue; + } + } + + list = ecs_map_ptr(&it); + if (!list->last) { + ecs_assert(list->first == NULL, ECS_INTERNAL_ERROR, NULL); + continue; + } + + bool comp; + if (!desc) { + comp = ((group_id - id) < (group_id - closest_id)); + } else { + comp = ((group_id - id) > (group_id - closest_id)); + } + + if (!closest_list || comp) { + closest_id = id; + closest_list = list; + } + } + + if (closest_list) { + return closest_list->last; + } else { + return NULL; /* Group should be first in query */ + } +} + +/* Initialize group with first node */ +static +void flecs_query_cache_create_group( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) +{ + uint64_t group_id = match->group_id; + + /* If query has grouping enabled & this is a new/empty group, find + * the insertion point for the group */ + ecs_query_cache_table_match_t *insert_after = + flecs_query_cache_find_group_insertion_node(cache, group_id); + + if (!insert_after) { + /* This group should appear first in the query list */ + ecs_query_cache_table_match_t *query_first = cache->list.first; + if (query_first) { + /* If this is not the first match for the query, insert before it */ + match->next = query_first; + query_first->prev = match; + cache->list.first = match; + } else { + /* If this is the first match of the query, initialize its list */ + ecs_assert(cache->list.last == NULL, ECS_INTERNAL_ERROR, NULL); + cache->list.first = match; + cache->list.last = match; + } + } else { + ecs_assert(cache->list.first != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.last != NULL, ECS_INTERNAL_ERROR, NULL); + + /* This group should appear after another group */ + ecs_query_cache_table_match_t *insert_before = insert_after->next; + match->prev = insert_after; + insert_after->next = match; + match->next = insert_before; + if (insert_before) { + insert_before->prev = match; + } else { + ecs_assert(cache->list.last == insert_after, + ECS_INTERNAL_ERROR, NULL); + + /* This group should appear last in the query list */ + cache->list.last = match; + } + } +} + +/* Find the list the node should be part of */ +static +ecs_query_cache_table_list_t* flecs_query_cache_get_node_list( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) +{ + if (cache->group_by_callback) { + return flecs_query_cache_get_group(cache, match->group_id); + } else { + return &cache->list; + } +} + +/* Find or create the list the node should be part of */ +static +ecs_query_cache_table_list_t* flecs_query_cache_ensure_node_list( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) +{ + if (cache->group_by_callback) { + return flecs_query_cache_ensure_group(cache, match->group_id); + } else { + return &cache->list; + } +} + +/* Remove node from list */ +static +void flecs_query_cache_remove_table_node( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) +{ + ecs_query_cache_table_match_t *prev = match->prev; + ecs_query_cache_table_match_t *next = match->next; + + ecs_assert(prev != match, ECS_INTERNAL_ERROR, NULL); + ecs_assert(next != match, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!prev || prev != next, ECS_INTERNAL_ERROR, NULL); + + ecs_query_cache_table_list_t *list = + flecs_query_cache_get_node_list(cache, match); + + if (!list || !list->first) { + /* If list contains no matches, the match must be empty */ + ecs_assert(!list || list->last == NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(prev == NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(next == NULL, ECS_INTERNAL_ERROR, NULL); + return; + } + + ecs_assert(prev != NULL || cache->list.first == match, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(next != NULL || cache->list.last == match, + ECS_INTERNAL_ERROR, NULL); + + if (prev) { + prev->next = next; + } + if (next) { + next->prev = prev; + } + + ecs_assert(list->info.table_count > 0, ECS_INTERNAL_ERROR, NULL); + list->info.table_count --; + + if (cache->group_by_callback) { + uint64_t group_id = match->group_id; + + /* Make sure query.list is updated if this is the first or last group */ + if (cache->list.first == match) { + ecs_assert(prev == NULL, ECS_INTERNAL_ERROR, NULL); + cache->list.first = next; + prev = next; + } + if (cache->list.last == match) { + ecs_assert(next == NULL, ECS_INTERNAL_ERROR, NULL); + cache->list.last = prev; + next = prev; + } + + ecs_assert(cache->list.info.table_count > 0, ECS_INTERNAL_ERROR, NULL); + cache->list.info.table_count --; + list->info.match_count ++; + + /* Make sure group list only contains nodes that belong to the group */ + if (prev && prev->group_id != group_id) { + /* The previous node belonged to another group */ + prev = next; + } + if (next && next->group_id != group_id) { + /* The next node belonged to another group */ + next = prev; + } + + /* Do check again, in case both prev & next belonged to another group */ + if ((!prev && !next) || (prev && prev->group_id != group_id)) { + /* There are no more matches left in this group */ + flecs_query_cache_remove_group(cache, group_id); + list = NULL; + } + } + + if (list) { + if (list->first == match) { + list->first = next; + } + if (list->last == match) { + list->last = prev; + } + } + + match->prev = NULL; + match->next = NULL; + + cache->match_count ++; +} + +/* Add node to list */ +static +void flecs_query_cache_insert_table_node( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *match) +{ + /* Node should not be part of an existing list */ + ecs_assert(match->prev == NULL && match->next == NULL, + ECS_INTERNAL_ERROR, NULL); + + /* If this is the first match, activate system */ + if (!cache->list.first && cache->entity) { + ecs_remove_id(cache->query->world, cache->entity, EcsEmpty); + } + + flecs_query_cache_compute_group_id(cache, match); + + ecs_query_cache_table_list_t *list = + flecs_query_cache_ensure_node_list(cache, match); + if (list->last) { + ecs_assert(cache->list.first != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.last != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(list->first != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_query_cache_table_match_t *last = list->last; + ecs_query_cache_table_match_t *last_next = last->next; + + match->prev = last; + match->next = last_next; + last->next = match; + + if (last_next) { + last_next->prev = match; + } + + list->last = match; + + if (cache->group_by_callback) { + /* Make sure to update query list if this is the last group */ + if (cache->list.last == last) { + cache->list.last = match; + } + } + } else { + ecs_assert(list->first == NULL, ECS_INTERNAL_ERROR, NULL); + + list->first = match; + list->last = match; + + if (cache->group_by_callback) { + /* Initialize group with its first node */ + flecs_query_cache_create_group(cache, match); + } + } + + if (cache->group_by_callback) { + list->info.table_count ++; + list->info.match_count ++; + } + + cache->list.info.table_count ++; + cache->match_count ++; + + ecs_assert(match->prev != match, ECS_INTERNAL_ERROR, NULL); + ecs_assert(match->next != match, ECS_INTERNAL_ERROR, NULL); + + ecs_assert(list->first != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(list->last != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(list->last == match, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.first != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.last != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.first->prev == NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cache->list.last->next == NULL, ECS_INTERNAL_ERROR, NULL); +} + +static +ecs_query_cache_table_match_t* flecs_query_cache_cache_add( + ecs_world_t *world, + ecs_query_cache_table_t *elem) +{ + ecs_query_cache_table_match_t *result = + flecs_bcalloc(&world->allocators.query_table_match); + + if (!elem->first) { + elem->first = result; + elem->last = result; + } else { + ecs_assert(elem->last != NULL, ECS_INTERNAL_ERROR, NULL); + elem->last->next_match = result; + elem->last = result; + } + + return result; +} + +/* The group by function for cascade computes the tree depth for the table type. + * This causes tables in the query cache to be ordered by depth, which ensures + * breadth-first iteration order. */ +static +uint64_t flecs_query_cache_group_by_cascade( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + void *ctx) +{ + (void)id; + ecs_term_t *term = ctx; + ecs_entity_t rel = term->trav; + int32_t depth = flecs_relation_depth(world, rel, table); + return flecs_ito(uint64_t, depth); +} + +static +ecs_query_cache_table_match_t* flecs_query_cache_add_table_match( + ecs_query_cache_t *cache, + ecs_query_cache_table_t *qt, + ecs_table_t *table) +{ + /* Add match for table. One table can have more than one match, if + * the query contains wildcards. */ + ecs_query_cache_table_match_t *qm = flecs_query_cache_cache_add( + cache->query->world, qt); + + qm->table = table; + qm->trs = flecs_balloc(&cache->allocators.trs); + + /* Insert match to iteration list if table is not empty */ + if (!table || ecs_table_count(table) != 0 || + (cache->query->flags & EcsQueryCacheYieldEmptyTables)) + { + flecs_query_cache_insert_table_node(cache, qm); + } + + return qm; +} + +static +void flecs_query_cache_set_table_match( + ecs_query_cache_t *cache, + ecs_query_cache_table_match_t *qm, + ecs_iter_t *it) +{ + ecs_query_t *query = cache->query; + int8_t i, field_count = query->field_count; + + ecs_assert(field_count > 0, ECS_INTERNAL_ERROR, NULL); + + /* Reset resources in case this is an existing record */ + ecs_os_memcpy_n(ECS_CONST_CAST(ecs_table_record_t**, qm->trs), + it->trs, ecs_table_record_t*, field_count); + + /* Find out whether to store result-specific ids array or fixed array */ + ecs_id_t *ids = cache->query->ids; + for (i = 0; i < field_count; i ++) { + if (it->ids[i] != ids[i]) { + break; + } + } + + if (i != field_count) { + if (qm->ids == ids || !qm->ids) { + qm->ids = flecs_balloc(&cache->allocators.ids); + } + ecs_os_memcpy_n(qm->ids, it->ids, ecs_id_t, field_count); + } else { + if (qm->ids != ids) { + flecs_bfree(&cache->allocators.ids, qm->ids); + qm->ids = ids; + } + } + + /* Find out whether to store result-specific sources array or fixed array */ + for (i = 0; i < field_count; i ++) { + if (it->sources[i]) { + break; + } + } + + if (i != field_count) { + if (qm->sources == cache->sources || !qm->sources) { + qm->sources = flecs_balloc(&cache->allocators.sources); + } + ecs_os_memcpy_n(qm->sources, it->sources, ecs_entity_t, field_count); + } else { + if (qm->sources != cache->sources) { + flecs_bfree(&cache->allocators.sources, qm->sources); + qm->sources = cache->sources; + } + } + + qm->set_fields = it->set_fields; + qm->up_fields = it->up_fields; +} + +static +ecs_query_cache_table_t* flecs_query_cache_table_insert( + ecs_world_t *world, + ecs_query_cache_t *cache, + ecs_table_t *table) +{ + ecs_query_cache_table_t *qt = flecs_bcalloc(&world->allocators.query_table); + if (table) { + qt->table_id = table->id; + } else { + qt->table_id = 0; + } + + if (cache->query->flags & EcsQueryCacheYieldEmptyTables) { + ecs_table_cache_insert_w_empty(&cache->cache, table, &qt->hdr, false); + } else { + ecs_table_cache_insert(&cache->cache, table, &qt->hdr); + } + + return qt; +} + +/** Populate query cache with tables */ +static +void flecs_query_cache_match_tables( + ecs_world_t *world, + ecs_query_cache_t *cache) +{ + ecs_table_t *table = NULL; + ecs_query_cache_table_t *qt = NULL; + + ecs_iter_t it = ecs_query_iter(world, cache->query); + ECS_BIT_SET(it.flags, EcsIterNoData); + ECS_BIT_SET(it.flags, EcsIterTableOnly); + + while (ecs_query_next(&it)) { + if ((table != it.table) || (!it.table && !qt)) { + /* New table matched, add record to cache */ + table = it.table; + qt = flecs_query_cache_table_insert(world, cache, table); + } + + ecs_query_cache_table_match_t *qm = + flecs_query_cache_add_table_match(cache, qt, table); + flecs_query_cache_set_table_match(cache, qm, &it); + } +} + +static +bool flecs_query_cache_match_table( + ecs_world_t *world, + ecs_query_cache_t *cache, + ecs_table_t *table) +{ + if (!ecs_map_is_init(&cache->cache.index)) { + return false; + } + + ecs_query_cache_table_t *qt = NULL; + ecs_query_t *q = cache->query; + + /* Iterate uncached query for table to check if it matches. If this is a + * wildcard query, a table can match multiple times. */ + ecs_iter_t it = flecs_query_iter(world, q); + it.flags |= EcsIterNoData; + ecs_iter_set_var_as_table(&it, 0, table); + + while (ecs_query_next(&it)) { + ecs_assert(it.table == table, ECS_INTERNAL_ERROR, NULL); + if (qt == NULL) { + table = it.table; + qt = flecs_query_cache_table_insert(world, cache, table); + } + + ecs_query_cache_table_match_t *qm = flecs_query_cache_add_table_match( + cache, qt, table); + flecs_query_cache_set_table_match(cache, qm, &it); + } + + return qt != NULL; +} + +static +bool flecs_query_cache_has_refs( + ecs_query_cache_t *cache) +{ + ecs_term_t *terms = cache->query->terms; + int32_t i, count = cache->query->term_count; + for (i = 0; i < count; i ++) { + if (terms[i].src.id & (EcsUp | EcsIsEntity)) { + return true; + } + } + + return false; +} + +static +void flecs_query_cache_for_each_component_monitor( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_query_cache_t *cache, + void(*callback)( + ecs_world_t* world, + ecs_id_t id, + ecs_query_t *q)) +{ + ecs_query_t *q = &impl->pub; + ecs_term_t *terms = cache->query->terms; + int32_t i, count = cache->query->term_count; + + for (i = 0; i < count; i++) { + ecs_term_t *term = &terms[i]; + ecs_term_ref_t *src = &term->src; + + if (src->id & EcsUp) { + callback(world, ecs_pair(term->trav, EcsWildcard), q); + if (term->trav != EcsIsA) { + callback(world, ecs_pair(EcsIsA, EcsWildcard), q); + } + callback(world, term->id, q); + + } else if (src->id & EcsSelf && !ecs_term_match_this(term)) { + callback(world, term->id, q); + } + } +} + +static +bool flecs_query_cache_is_term_ref_supported( + ecs_term_ref_t *ref) +{ + if (!(ref->id & EcsIsVariable)) { + return true; + } + if (ecs_id_is_wildcard(ref->id)) { + return true; + } + return false; +} + +static +int flecs_query_cache_process_signature( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_query_cache_t *cache) +{ + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_term_t *terms = cache->query->terms; + int32_t i, count = cache->query->term_count; + + for (i = 0; i < count; i ++) { + ecs_term_t *term = &terms[i]; + ecs_term_ref_t *first = &term->first; + ecs_term_ref_t *src = &term->src; + ecs_term_ref_t *second = &term->second; + + bool is_src_ok = flecs_query_cache_is_term_ref_supported(src); + bool is_first_ok = flecs_query_cache_is_term_ref_supported(first); + bool is_second_ok = flecs_query_cache_is_term_ref_supported(second); + + (void)first; + (void)second; + (void)is_src_ok; + (void)is_first_ok; + (void)is_second_ok; + + /* Queries do not support named variables */ + ecs_check(is_src_ok || ecs_term_match_this(term), + ECS_UNSUPPORTED, NULL); + ecs_check(is_first_ok, ECS_UNSUPPORTED, NULL); + ecs_check(is_second_ok, ECS_UNSUPPORTED, NULL); + ecs_check(term->inout != EcsInOutFilter, ECS_INVALID_PARAMETER, + "invalid usage of InOutFilter for query"); + + if (src->id & EcsCascade) { + ecs_assert(cache->cascade_by == 0, ECS_INVALID_PARAMETER, + "query can only have one cascade term"); + cache->cascade_by = i + 1; + } + } + + impl->pub.flags |= + (ecs_flags32_t)(flecs_query_cache_has_refs(cache) * EcsQueryHasRefs); + + flecs_query_cache_for_each_component_monitor( + world, impl, cache, flecs_monitor_register); + + return 0; +error: + return -1; +} + +/** When a table becomes empty remove it from the query list, or vice versa. */ +static +void flecs_query_cache_update_table( + ecs_query_cache_t *cache, + ecs_table_t *table, + bool empty) +{ + int32_t prev_count = flecs_query_cache_table_count(cache); + ecs_table_cache_set_empty(&cache->cache, table, empty); + int32_t cur_count = flecs_query_cache_table_count(cache); + + if (prev_count != cur_count) { + ecs_query_cache_table_t *qt = ecs_table_cache_get(&cache->cache, table); + ecs_assert(qt != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_query_cache_table_match_t *cur, *next; + + for (cur = qt->first; cur != NULL; cur = next) { + next = cur->next_match; + + if (empty) { + ecs_assert(ecs_table_count(table) == 0, + ECS_INTERNAL_ERROR, NULL); + + flecs_query_cache_remove_table_node(cache, cur); + } else { + ecs_assert(ecs_table_count(table) != 0, + ECS_INTERNAL_ERROR, NULL); + + flecs_query_cache_insert_table_node(cache, cur); + } + } + } + + ecs_assert(cur_count || cache->list.first == NULL, + ECS_INTERNAL_ERROR, NULL); +} + +/* Remove table */ +static +void flecs_query_cache_table_match_free( + ecs_query_cache_t *cache, + ecs_query_cache_table_t *elem, + ecs_query_cache_table_match_t *first) +{ + ecs_query_cache_table_match_t *cur, *next; + ecs_world_t *world = cache->query->world; + + for (cur = first; cur != NULL; cur = next) { + flecs_bfree(&cache->allocators.trs, ECS_CONST_CAST(void*, cur->trs)); + + if (cur->ids != cache->query->ids) { + flecs_bfree(&cache->allocators.ids, cur->ids); + } + + if (cur->sources != cache->sources) { + flecs_bfree(&cache->allocators.sources, cur->sources); + } + + if (cur->monitor) { + flecs_bfree(&cache->allocators.monitors, cur->monitor); + } + + if (!elem->hdr.empty) { + flecs_query_cache_remove_table_node(cache, cur); + } + + next = cur->next_match; + + flecs_bfree(&world->allocators.query_table_match, cur); + } +} + +static +void flecs_query_cache_table_free( + ecs_query_cache_t *cache, + ecs_query_cache_table_t *elem) +{ + flecs_query_cache_table_match_free(cache, elem, elem->first); + flecs_bfree(&cache->query->world->allocators.query_table, elem); +} + +static +void flecs_query_cache_unmatch_table( + ecs_query_cache_t *cache, + ecs_table_t *table, + ecs_query_cache_table_t *elem) +{ + if (!elem) { + elem = ecs_table_cache_get(&cache->cache, table); + } + if (elem) { + ecs_table_cache_remove(&cache->cache, elem->table_id, &elem->hdr); + flecs_query_cache_table_free(cache, elem); + } +} + +/* Rematch system with tables after a change happened to a watched entity */ +static +void flecs_query_cache_rematch_tables( + ecs_world_t *world, + ecs_query_impl_t *impl) +{ + ecs_iter_t it; + ecs_table_t *table = NULL; + ecs_query_cache_table_t *qt = NULL; + ecs_query_cache_table_match_t *qm = NULL; + ecs_query_cache_t *cache = impl->cache; + + if (cache->monitor_generation == world->monitor_generation) { + return; + } + + ecs_os_perf_trace_push("flecs.query.rematch"); + + cache->monitor_generation = world->monitor_generation; + + it = ecs_query_iter(world, cache->query); + ECS_BIT_SET(it.flags, EcsIterNoData); + + world->info.rematch_count_total ++; + int32_t rematch_count = ++ cache->rematch_count; + + ecs_time_t t = {0}; + if (world->flags & EcsWorldMeasureFrameTime) { + ecs_time_measure(&t); + } + + while (ecs_query_next(&it)) { + if ((table != it.table) || (!it.table && !qt)) { + if (qm && qm->next_match) { + flecs_query_cache_table_match_free(cache, qt, qm->next_match); + qm->next_match = NULL; + } + + table = it.table; + + qt = ecs_table_cache_get(&cache->cache, table); + if (!qt) { + qt = flecs_query_cache_table_insert(world, cache, table); + } + + ecs_assert(qt->hdr.table == table, ECS_INTERNAL_ERROR, NULL); + qt->rematch_count = rematch_count; + qm = NULL; + } + if (!qm) { + qm = qt->first; + } else { + qm = qm->next_match; + } + if (!qm) { + qm = flecs_query_cache_add_table_match(cache, qt, table); + } + + flecs_query_cache_set_table_match(cache, qm, &it); + + if (table && ecs_table_count(table) && cache->group_by_callback) { + if (flecs_query_cache_get_group_id(cache, table) != qm->group_id) { + /* Update table group */ + flecs_query_cache_remove_table_node(cache, qm); + flecs_query_cache_insert_table_node(cache, qm); + } + } + } + + if (qm && qm->next_match) { + flecs_query_cache_table_match_free(cache, qt, qm->next_match); + qm->next_match = NULL; + } + + /* Iterate all tables in cache, remove ones that weren't just matched */ + ecs_table_cache_iter_t cache_it; + if (flecs_table_cache_all_iter(&cache->cache, &cache_it)) { + while ((qt = flecs_table_cache_next(&cache_it, ecs_query_cache_table_t))) { + if (qt->rematch_count != rematch_count) { + flecs_query_cache_unmatch_table(cache, qt->hdr.table, qt); + } + } + } + + if (world->flags & EcsWorldMeasureFrameTime) { + world->info.rematch_time_total += (ecs_ftime_t)ecs_time_measure(&t); + } + + ecs_os_perf_trace_pop("flecs.query.rematch"); +} + +/* -- Private API -- */ + +void flecs_query_cache_notify( + ecs_world_t *world, + ecs_query_t *q, + ecs_query_cache_event_t *event) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_query_impl_t *impl = flecs_query_impl(q); + ecs_query_cache_t *cache = impl->cache; + + switch(event->kind) { + case EcsQueryTableMatch: + /* Creation of new table */ + flecs_query_cache_match_table(world, cache, event->table); + break; + case EcsQueryTableUnmatch: + /* Deletion of table */ + flecs_query_cache_unmatch_table(cache, event->table, NULL); + break; + case EcsQueryTableRematch: + /* Rematch tables of query */ + flecs_query_cache_rematch_tables(world, impl); + break; + } +} + +static +int flecs_query_cache_order_by( + ecs_world_t *world, + ecs_query_impl_t *impl, + ecs_entity_t order_by, + ecs_order_by_action_t order_by_callback, + ecs_sort_table_action_t action) +{ + ecs_check(impl != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_query_cache_t *cache = impl->cache; + ecs_check(cache != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(!ecs_id_is_wildcard(order_by), + ECS_INVALID_PARAMETER, NULL); + + /* Find order_by term & make sure it is queried for */ + const ecs_query_t *query = cache->query; + int32_t i, count = query->term_count; + int32_t order_by_term = -1; + + if (order_by) { + for (i = 0; i < count; i ++) { + const ecs_term_t *term = &query->terms[i]; + + /* Only And terms are supported */ + if (term->id == order_by && term->oper == EcsAnd) { + order_by_term = i; + break; + } + } + + if (order_by_term == -1) { + char *id_str = ecs_id_str(world, order_by); + ecs_err("order_by component '%s' is not queried for", id_str); + ecs_os_free(id_str); + goto error; + } + } + + cache->order_by = order_by; + cache->order_by_callback = order_by_callback; + cache->order_by_term = order_by_term; + cache->order_by_table_callback = action; + + ecs_vec_fini_t(NULL, &cache->table_slices, ecs_query_cache_table_match_t); + flecs_query_cache_sort_tables(world, impl); + + if (!cache->table_slices.array) { + flecs_query_cache_build_sorted_tables(cache); + } + + return 0; +error: + return -1; +} + +static +void flecs_query_cache_group_by( + ecs_query_cache_t *cache, + ecs_entity_t sort_component, + ecs_group_by_action_t group_by) +{ + ecs_check(cache->group_by == 0, ECS_INVALID_OPERATION, + "query is already grouped"); + ecs_check(cache->group_by_callback == 0, ECS_INVALID_OPERATION, + "query is already grouped"); + + if (!group_by) { + /* Builtin function that groups by relationship */ + group_by = flecs_query_cache_default_group_by; + } + + cache->group_by = sort_component; + cache->group_by_callback = group_by; + + ecs_map_init_w_params(&cache->groups, + &cache->query->world->allocators.query_table_list); +error: + return; +} + +static +void flecs_query_cache_on_event( + ecs_iter_t *it) +{ + /* Because this is the observer::run callback, checking if this is event is + * already handled is not done for us. */ + ecs_world_t *world = it->world; + ecs_observer_t *o = it->ctx; + ecs_observer_impl_t *o_impl = flecs_observer_impl(o); + if (o_impl->last_event_id) { + if (o_impl->last_event_id[0] == world->event_id) { + return; + } + o_impl->last_event_id[0] = world->event_id; + } + + ecs_query_impl_t *impl = o->ctx; + flecs_poly_assert(impl, ecs_query_t); + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = it->table; + ecs_entity_t event = it->event; + + if (event == EcsOnTableCreate) { + /* Creation of new table */ + if (flecs_query_cache_match_table(world, cache, table)) { + if (ecs_should_log_3()) { + char *table_str = ecs_table_str(world, table); + ecs_dbg_3("query cache event: %s for [%s]", + ecs_get_name(world, event), + table_str); + ecs_os_free(table_str); + } + } + return; + } + + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + + /* The observer isn't doing the matching because the query can do it more + * efficiently by checking the table with the query cache. */ + if (ecs_table_cache_get(&cache->cache, table) == NULL) { + return; + } + + if (ecs_should_log_3()) { + char *table_str = ecs_table_str(world, table); + ecs_dbg_3("query cache event: %s for [%s]", + ecs_get_name(world, event), + table_str); + ecs_os_free(table_str); + } + + if (event == EcsOnTableEmpty) { + flecs_query_cache_update_table(cache, table, true); + } else + if (event == EcsOnTableFill) { + flecs_query_cache_update_table(cache, table, false); + } else if (event == EcsOnTableDelete) { + /* Deletion of table */ + flecs_query_cache_unmatch_table(cache, table, NULL); + return; + } +} + +static +void flecs_query_cache_table_cache_free( + ecs_query_cache_t *cache) +{ + ecs_table_cache_iter_t it; + ecs_query_cache_table_t *qt; + + if (flecs_table_cache_all_iter(&cache->cache, &it)) { + while ((qt = flecs_table_cache_next(&it, ecs_query_cache_table_t))) { + flecs_query_cache_table_free(cache, qt); + } + } + + ecs_table_cache_fini(&cache->cache); +} + +static +void flecs_query_cache_allocators_init( + ecs_query_cache_t *cache) +{ + int32_t field_count = cache->query->field_count; + if (field_count) { + flecs_ballocator_init(&cache->allocators.trs, + field_count * ECS_SIZEOF(ecs_table_record_t*)); + flecs_ballocator_init(&cache->allocators.ids, + field_count * ECS_SIZEOF(ecs_id_t)); + flecs_ballocator_init(&cache->allocators.sources, + field_count * ECS_SIZEOF(ecs_entity_t)); + flecs_ballocator_init(&cache->allocators.monitors, + (1 + field_count) * ECS_SIZEOF(int32_t)); + } +} + +static +void flecs_query_cache_allocators_fini( + ecs_query_cache_t *cache) +{ + int32_t field_count = cache->query->field_count; + if (field_count) { + flecs_ballocator_fini(&cache->allocators.trs); + flecs_ballocator_fini(&cache->allocators.ids); + flecs_ballocator_fini(&cache->allocators.sources); + flecs_ballocator_fini(&cache->allocators.monitors); + } +} + +void flecs_query_cache_fini( + ecs_query_impl_t *impl) +{ + ecs_world_t *world = impl->pub.world; + ecs_stage_t *stage = impl->stage; + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + + if (cache->observer) { + flecs_observer_fini(cache->observer); + } + + ecs_group_delete_action_t on_delete = cache->on_group_delete; + if (on_delete) { + ecs_map_iter_t it = ecs_map_iter(&cache->groups); + while (ecs_map_next(&it)) { + ecs_query_cache_table_list_t *group = ecs_map_ptr(&it); + uint64_t group_id = ecs_map_key(&it); + on_delete(world, group_id, group->info.ctx, cache->group_by_ctx); + } + cache->on_group_delete = NULL; + } + + if (cache->group_by_ctx_free) { + if (cache->group_by_ctx) { + cache->group_by_ctx_free(cache->group_by_ctx); + } + } + + flecs_query_cache_for_each_component_monitor(world, impl, cache, + flecs_monitor_unregister); + flecs_query_cache_table_cache_free(cache); + + ecs_map_fini(&cache->groups); + + ecs_vec_fini_t(NULL, &cache->table_slices, ecs_query_cache_table_match_t); + + if (cache->query->term_count) { + flecs_bfree(&cache->allocators.sources, cache->sources); + } + + flecs_query_cache_allocators_fini(cache); + ecs_query_fini(cache->query); + + flecs_bfree(&stage->allocators.query_cache, cache); +} + +/* -- Public API -- */ + +ecs_query_cache_t* flecs_query_cache_init( + ecs_query_impl_t *impl, + const ecs_query_desc_t *const_desc) +{ + ecs_world_t *world = impl->pub.real_world; + ecs_stage_t *stage = impl->stage; + ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_check(const_desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(const_desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_query_desc_t was not initialized to zero"); + ecs_check(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, + "cannot create query during world fini"); + + /* Create private version of desc to create the uncached query that will + * populate the query cache. */ + ecs_query_desc_t desc = *const_desc; + ecs_entity_t entity = desc.entity; + desc.cache_kind = EcsQueryCacheNone; /* Don't create caches recursively */ + desc.group_by_callback = NULL; + desc.group_by = 0; + desc.order_by_callback = NULL; + desc.order_by = 0; + desc.entity = 0; + + /* Don't pass ctx/binding_ctx to uncached query */ + desc.ctx = NULL; + desc.binding_ctx = NULL; + desc.ctx_free = NULL; + desc.binding_ctx_free = NULL; + + ecs_query_cache_t *result = flecs_bcalloc(&stage->allocators.query_cache); + result->entity = entity; + impl->cache = result; + + ecs_observer_desc_t observer_desc = { .query = desc }; + observer_desc.query.flags |= EcsQueryNested; + + ecs_flags32_t query_flags = const_desc->flags | world->default_query_flags; + desc.flags |= EcsQueryMatchEmptyTables | EcsQueryTableOnly | EcsQueryNested; + + ecs_query_t *q = result->query = ecs_query_init(world, &desc); + if (!q) { + goto error; + } + + /* The uncached query used to populate the cache always matches empty + * tables. This flag determines whether the empty tables are stored + * separately in the cache or are treated as regular tables. This is only + * enabled if the user requested that the query matches empty tables. */ + ECS_BIT_COND(q->flags, EcsQueryCacheYieldEmptyTables, + !!(query_flags & EcsQueryMatchEmptyTables)); + + flecs_query_cache_allocators_init(result); + + /* Zero'd out sources array that's used for results that only match $this. + * This reduces the amount of memory used by the cache, and improves CPU + * cache locality during iteration when doing source checks. */ + if (result->query->term_count) { + result->sources = flecs_bcalloc(&result->allocators.sources); + } + + if (q->term_count) { + observer_desc.run = flecs_query_cache_on_event; + observer_desc.ctx = impl; + + int32_t event_index = 0; + if (!(q->flags & EcsQueryCacheYieldEmptyTables)) { + observer_desc.events[event_index ++] = EcsOnTableEmpty; + observer_desc.events[event_index ++] = EcsOnTableFill; + } + + observer_desc.events[event_index ++] = EcsOnTableCreate; + observer_desc.events[event_index ++] = EcsOnTableDelete; + observer_desc.flags_ = EcsObserverBypassQuery; + + /* ecs_query_init could have moved away resources from the terms array + * in the descriptor, so use the terms array from the query. */ + ecs_os_memcpy_n(observer_desc.query.terms, q->terms, + ecs_term_t, FLECS_TERM_COUNT_MAX); + observer_desc.query.expr = NULL; /* Already parsed */ + + result->observer = flecs_observer_init(world, entity, &observer_desc); + if (!result->observer) { + goto error; + } + } + + result->prev_match_count = -1; + + if (ecs_should_log_1()) { + char *query_expr = ecs_query_str(result->query); + ecs_dbg_1("#[green]query#[normal] [%s] created", + query_expr ? query_expr : ""); + ecs_os_free(query_expr); + } + + ecs_log_push_1(); + + if (flecs_query_cache_process_signature(world, impl, result)) { + goto error; + } + + /* Group before matching so we won't have to move tables around later */ + int32_t cascade_by = result->cascade_by; + if (cascade_by) { + flecs_query_cache_group_by(result, result->query->terms[cascade_by - 1].id, + flecs_query_cache_group_by_cascade); + result->group_by_ctx = &result->query->terms[cascade_by - 1]; + } + + if (const_desc->group_by_callback || const_desc->group_by) { + ecs_check(!result->cascade_by, ECS_INVALID_PARAMETER, + "cannot mix cascade and group_by"); + flecs_query_cache_group_by(result, + const_desc->group_by, const_desc->group_by_callback); + result->group_by_ctx = const_desc->group_by_ctx; + result->on_group_create = const_desc->on_group_create; + result->on_group_delete = const_desc->on_group_delete; + result->group_by_ctx_free = const_desc->group_by_ctx_free; + } + + /* Ensure that while initially populating the query with tables, they are + * in the right empty/non-empty list. This ensures the query won't miss + * empty/non-empty events for tables that are currently out of sync, but + * change back to being in sync before processing pending events. */ + ecs_run_aperiodic(world, EcsAperiodicEmptyTables); + ecs_table_cache_init(world, &result->cache); + flecs_query_cache_match_tables(world, result); + + if (const_desc->order_by_callback) { + if (flecs_query_cache_order_by(world, impl, + const_desc->order_by, const_desc->order_by_callback, + const_desc->order_by_table_callback)) + { + goto error; + } + } + + if (entity) { + if (!flecs_query_cache_table_count(result) && result->query->term_count){ + ecs_add_id(world, entity, EcsEmpty); + } + } + + ecs_log_pop_1(); + + return result; +error: + return NULL; +} + +ecs_query_cache_table_t* flecs_query_cache_get_table( + ecs_query_cache_t *cache, + ecs_table_t *table) +{ + return ecs_table_cache_get(&cache->cache, table); +} + +void ecs_iter_set_group( + ecs_iter_t *it, + uint64_t group_id) +{ + ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); + ecs_check(!(it->flags & EcsIterIsValid), ECS_INVALID_PARAMETER, + "cannot set group during iteration"); + + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_impl_t *q = flecs_query_impl(qit->query); + ecs_check(q != NULL, ECS_INVALID_PARAMETER, NULL); + flecs_poly_assert(q, ecs_query_t); + ecs_query_cache_t *cache = q->cache; + ecs_check(cache != NULL, ECS_INVALID_PARAMETER, NULL); + + ecs_query_cache_table_list_t *node = flecs_query_cache_get_group( + cache, group_id); + if (!node) { + qit->node = NULL; + qit->last = NULL; + return; + } + + ecs_query_cache_table_match_t *first = node->first; + if (first) { + qit->node = node->first; + qit->last = node->last; + } else { + qit->node = NULL; + qit->last = NULL; + } + +error: + return; +} + +const ecs_query_group_info_t* ecs_query_get_group_info( + const ecs_query_t *query, + uint64_t group_id) +{ + flecs_poly_assert(query, ecs_query_t); + ecs_query_cache_table_list_t *node = flecs_query_cache_get_group( + flecs_query_impl(query)->cache, group_id); + if (!node) { + return NULL; + } + + return &node->info; +} + +void* ecs_query_get_group_ctx( + const ecs_query_t *query, + uint64_t group_id) +{ + flecs_poly_assert(query, ecs_query_t); + const ecs_query_group_info_t *info = ecs_query_get_group_info( + query, group_id); + if (!info) { + return NULL; + } else { + return info->ctx; + } +} diff --git a/vendors/flecs/src/query/engine/cache.h b/vendors/flecs/src/query/engine/cache.h new file mode 100644 index 000000000..4da4ec932 --- /dev/null +++ b/vendors/flecs/src/query/engine/cache.h @@ -0,0 +1,46 @@ + /** + * @file query/engine/cache.h + * @brief Query cache functions. + */ + +#include "../types.h" + +/* Create query cache */ +ecs_query_cache_t* flecs_query_cache_init( + ecs_query_impl_t *impl, + const ecs_query_desc_t *desc); + +/* Destroy query cache */ +void flecs_query_cache_fini( + ecs_query_impl_t *impl); + +/* Notify query cache of event (separate from query observer) */ +void flecs_query_cache_notify( + ecs_world_t *world, + ecs_query_t *q, + ecs_query_cache_event_t *event); + +/* Get cache entry for table */ +ecs_query_cache_table_t* flecs_query_cache_get_table( + ecs_query_cache_t *query, + ecs_table_t *table); + +/* Sort tables (order_by implementation) */ +void flecs_query_cache_sort_tables( + ecs_world_t *world, + ecs_query_impl_t *impl); + +void flecs_query_cache_build_sorted_tables( + ecs_query_cache_t *cache); + +/* Return number of tables in cache */ +int32_t flecs_query_cache_table_count( + ecs_query_cache_t *cache); + +/* Return number of empty tables in cache */ +int32_t flecs_query_cache_empty_table_count( + ecs_query_cache_t *cache); + +/* Return number of entities in cache (requires iterating tables) */ +int32_t flecs_query_cache_entity_count( + const ecs_query_cache_t *cache); diff --git a/vendors/flecs/src/query/engine/cache_iter.c b/vendors/flecs/src/query/engine/cache_iter.c new file mode 100644 index 000000000..18b479bc3 --- /dev/null +++ b/vendors/flecs/src/query/engine/cache_iter.c @@ -0,0 +1,190 @@ +/** + * @file query/engine/cache_iter.c + * @brief Compile query term. + */ + +#include "../../private_api.h" + +static +void flecs_query_update_node_up_trs( + const ecs_query_run_ctx_t *ctx, + ecs_query_cache_table_match_t *node) +{ + ecs_termset_t fields = node->up_fields & node->set_fields; + if (fields) { + const ecs_query_impl_t *impl = ctx->query; + const ecs_query_t *q = &impl->pub; + ecs_query_cache_t *cache = impl->cache; + int32_t i, field_count = q->field_count; + for (i = 0; i < field_count; i ++) { + if (!(fields & (1llu << i))) { + continue; + } + + ecs_entity_t src = node->sources[i]; + if (src) { + const ecs_table_record_t *tr = node->trs[i]; + ecs_record_t *r = flecs_entities_get(ctx->world, src); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + if (r->table != tr->hdr.table) { + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_assert(idr->id == q->ids[i], ECS_INTERNAL_ERROR, NULL); + tr = node->trs[i] = flecs_id_record_get_table(idr, r->table); + if (cache->field_map) { + ctx->it->trs[cache->field_map[i]] = tr; + } + } + } + } + } +} + +static +ecs_query_cache_table_match_t* flecs_query_cache_next( + const ecs_query_run_ctx_t *ctx) +{ + ecs_iter_t *it = ctx->it; + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_cache_table_match_t *node = qit->node; + ecs_query_cache_table_match_t *prev = qit->prev; + + if (prev != qit->last) { + ecs_assert(node != NULL, ECS_INTERNAL_ERROR, NULL); + ctx->vars[0].range.table = node->table; + it->group_id = node->group_id; + qit->node = node->next; + qit->prev = node; + return node; + } + + return NULL; +} + +static +ecs_query_cache_table_match_t* flecs_query_test( + const ecs_query_run_ctx_t *ctx, + bool redo) +{ + ecs_iter_t *it = ctx->it; + if (!redo) { + ecs_var_t *var = &ctx->vars[0]; + ecs_table_t *table = var->range.table; + ecs_assert(table != NULL, ECS_INVALID_OPERATION, + "the variable set on the iterator is missing a table"); + + ecs_query_cache_table_t *qt = flecs_query_cache_get_table( + ctx->query->cache, table); + if (!qt) { + return NULL; + } + + ecs_query_iter_t *qit = &it->priv_.iter.query; + qit->prev = NULL; + qit->node = qt->first; + qit->last = qt->last; + } + + return flecs_query_cache_next(ctx); +} + +static +void flecs_query_cache_init_mapped_fields( + const ecs_query_run_ctx_t *ctx, + ecs_query_cache_table_match_t *node) +{ + ecs_iter_t *it = ctx->it; + const ecs_query_impl_t *impl = ctx->query; + ecs_query_cache_t *cache = impl->cache; + int32_t i, field_count = cache->query->field_count; + int8_t *field_map = cache->field_map; + + for (i = 0; i < field_count; i ++) { + int8_t field_index = field_map[i]; + it->trs[field_index] = node->trs[i]; + + it->ids[field_index] = node->ids[i]; + it->sources[field_index] = node->sources[i]; + + ecs_termset_t bit = (ecs_termset_t)(1u << i); + ecs_termset_t field_bit = (ecs_termset_t)(1u << field_index); + + ECS_TERMSET_COND(it->set_fields, field_bit, node->set_fields & bit); + ECS_TERMSET_COND(it->up_fields, field_bit, node->up_fields & bit); + } +} + +/* Iterate cache for query that's partially cached */ +bool flecs_query_cache_search( + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_cache_table_match_t *node = flecs_query_cache_next(ctx); + if (!node) { + return false; + } + + flecs_query_cache_init_mapped_fields(ctx, node); + ctx->vars[0].range.count = node->count; + ctx->vars[0].range.offset = node->offset; + + flecs_query_update_node_up_trs(ctx, node); + + return true; +} + +/* Iterate cache for query that's entirely cached */ +bool flecs_query_is_cache_search( + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_cache_table_match_t *node = flecs_query_cache_next(ctx); + if (!node) { + return false; + } + + ecs_iter_t *it = ctx->it; + it->trs = node->trs; + it->ids = node->ids; + it->sources = node->sources; + it->set_fields = node->set_fields; + it->up_fields = node->up_fields; + + flecs_query_update_node_up_trs(ctx, node); + + return true; +} + +/* Test if query that is entirely cached matches constrained $this */ +bool flecs_query_cache_test( + const ecs_query_run_ctx_t *ctx, + bool redo) +{ + ecs_query_cache_table_match_t *node = flecs_query_test(ctx, redo); + if (!node) { + return false; + } + + flecs_query_cache_init_mapped_fields(ctx, node); + flecs_query_update_node_up_trs(ctx, node); + + return true; +} + +/* Test if query that is entirely cached matches constrained $this */ +bool flecs_query_is_cache_test( + const ecs_query_run_ctx_t *ctx, + bool redo) +{ + ecs_query_cache_table_match_t *node = flecs_query_test(ctx, redo); + if (!node) { + return false; + } + + ecs_iter_t *it = ctx->it; + it->trs = node->trs; + it->ids = node->ids; + it->sources = node->sources; + + flecs_query_update_node_up_trs(ctx, node); + + return true; +} diff --git a/vendors/flecs/src/query/engine/cache_iter.h b/vendors/flecs/src/query/engine/cache_iter.h new file mode 100644 index 000000000..0e2685cda --- /dev/null +++ b/vendors/flecs/src/query/engine/cache_iter.h @@ -0,0 +1,24 @@ +/** + * @file query/engine/cache_iter.h + * @brief Cache iterator functions. + */ + +#include "../types.h" + +/* Cache search */ +bool flecs_query_cache_search( + const ecs_query_run_ctx_t *ctx); + +/* Cache search where entire query is cached */ +bool flecs_query_is_cache_search( + const ecs_query_run_ctx_t *ctx); + +/* Cache test */ +bool flecs_query_cache_test( + const ecs_query_run_ctx_t *ctx, + bool redo); + +/* Cache test where entire query is cached */ +bool flecs_query_is_cache_test( + const ecs_query_run_ctx_t *ctx, + bool redo); diff --git a/vendors/flecs/src/query/engine/cache_order_by.c b/vendors/flecs/src/query/engine/cache_order_by.c new file mode 100644 index 000000000..fd5979840 --- /dev/null +++ b/vendors/flecs/src/query/engine/cache_order_by.c @@ -0,0 +1,319 @@ +/** + * @file query/engine/cache_order_by.c + * @brief Order by implementation + */ + +#include "../../private_api.h" + +ECS_SORT_TABLE_WITH_COMPARE(_, flecs_query_cache_sort_table_generic, order_by, static) + +static +void flecs_query_cache_sort_table( + ecs_world_t *world, + ecs_table_t *table, + int32_t column_index, + ecs_order_by_action_t compare, + ecs_sort_table_action_t sort) +{ + int32_t count = ecs_table_count(table); + if (!count) { + /* Nothing to sort */ + return; + } + + if (count < 2) { + return; + } + + ecs_entity_t *entities = table->data.entities; + void *ptr = NULL; + int32_t size = 0; + if (column_index != -1) { + ecs_column_t *column = &table->data.columns[column_index]; + ecs_type_info_t *ti = column->ti; + size = ti->size; + ptr = column->data; + } + + if (sort) { + sort(world, table, entities, ptr, size, 0, count - 1, compare); + } else { + flecs_query_cache_sort_table_generic( + world, table, entities, ptr, size, 0, count - 1, compare); + } +} + +/* Helper struct for building sorted table ranges */ +typedef struct sort_helper_t { + ecs_query_cache_table_match_t *match; + ecs_entity_t *entities; + const void *ptr; + int32_t row; + int32_t elem_size; + int32_t count; + bool shared; +} sort_helper_t; + +static +const void* ptr_from_helper( + sort_helper_t *helper) +{ + ecs_assert(helper->row < helper->count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(helper->elem_size >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(helper->row >= 0, ECS_INTERNAL_ERROR, NULL); + if (helper->shared) { + return helper->ptr; + } else { + return ECS_ELEM(helper->ptr, helper->elem_size, helper->row); + } +} + +static +ecs_entity_t e_from_helper( + sort_helper_t *helper) +{ + if (helper->row < helper->count) { + return helper->entities[helper->row]; + } else { + return 0; + } +} + +static +void flecs_query_cache_build_sorted_table_range( + ecs_query_cache_t *cache, + ecs_query_cache_table_list_t *list) +{ + ecs_world_t *world = cache->query->world; + ecs_assert(!(world->flags & EcsWorldMultiThreaded), ECS_UNSUPPORTED, + "cannot sort query in multithreaded mode"); + + ecs_entity_t id = cache->order_by; + ecs_order_by_action_t compare = cache->order_by_callback; + int32_t table_count = list->info.table_count; + if (!table_count) { + return; + } + + ecs_vec_init_if_t(&cache->table_slices, ecs_query_cache_table_match_t); + int32_t to_sort = 0; + int32_t order_by_term = cache->order_by_term; + + sort_helper_t *helper = flecs_alloc_n( + &world->allocator, sort_helper_t, table_count); + ecs_query_cache_table_match_t *cur, *end = list->last->next; + for (cur = list->first; cur != end; cur = cur->next) { + ecs_table_t *table = cur->table; + + if (ecs_table_count(table) == 0) { + continue; + } + + if (id) { + const ecs_term_t *term = &cache->query->terms[order_by_term]; + int32_t field = term->field_index; + ecs_size_t size = cache->query->sizes[field]; + ecs_entity_t src = cur->sources[field]; + if (src == 0) { + int32_t column_index = cur->trs[field]->column; + ecs_column_t *column = &table->data.columns[column_index]; + helper[to_sort].ptr = column->data; + helper[to_sort].elem_size = size; + helper[to_sort].shared = false; + } else { + ecs_record_t *r = flecs_entities_get(world, src); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + + if (term->src.id & EcsUp) { + ecs_entity_t base = 0; + ecs_search_relation(world, r->table, 0, id, + EcsIsA, term->src.id & EcsTraverseFlags, &base, 0, 0); + if (base && base != src) { /* Component could be inherited */ + r = flecs_entities_get(world, base); + } + } + + helper[to_sort].ptr = ecs_table_get_id( + world, r->table, id, ECS_RECORD_TO_ROW(r->row)); + helper[to_sort].elem_size = size; + helper[to_sort].shared = true; + } + ecs_assert(helper[to_sort].ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(helper[to_sort].elem_size != 0, ECS_INTERNAL_ERROR, NULL); + } else { + helper[to_sort].ptr = NULL; + helper[to_sort].elem_size = 0; + helper[to_sort].shared = false; + } + + helper[to_sort].match = cur; + helper[to_sort].entities = table->data.entities; + helper[to_sort].row = 0; + helper[to_sort].count = ecs_table_count(table); + to_sort ++; + } + + ecs_assert(to_sort != 0, ECS_INTERNAL_ERROR, NULL); + + bool proceed; + do { + int32_t j, min = 0; + proceed = true; + + ecs_entity_t e1; + while (!(e1 = e_from_helper(&helper[min]))) { + min ++; + if (min == to_sort) { + proceed = false; + break; + } + } + + if (!proceed) { + break; + } + + for (j = min + 1; j < to_sort; j++) { + ecs_entity_t e2 = e_from_helper(&helper[j]); + if (!e2) { + continue; + } + + const void *ptr1 = ptr_from_helper(&helper[min]); + const void *ptr2 = ptr_from_helper(&helper[j]); + + if (compare(e1, ptr1, e2, ptr2) > 0) { + min = j; + e1 = e_from_helper(&helper[min]); + } + } + + sort_helper_t *cur_helper = &helper[min]; + if (!cur || cur->trs != cur_helper->match->trs) { + cur = ecs_vec_append_t(NULL, &cache->table_slices, + ecs_query_cache_table_match_t); + *cur = *(cur_helper->match); + cur->offset = cur_helper->row; + cur->count = 1; + } else { + cur->count ++; + } + + cur_helper->row ++; + } while (proceed); + + /* Iterate through the vector of slices to set the prev/next ptrs. This + * can't be done while building the vector, as reallocs may occur */ + int32_t i, count = ecs_vec_count(&cache->table_slices); + ecs_query_cache_table_match_t *nodes = ecs_vec_first(&cache->table_slices); + for (i = 0; i < count; i ++) { + nodes[i].prev = &nodes[i - 1]; + nodes[i].next = &nodes[i + 1]; + } + + nodes[0].prev = NULL; + nodes[i - 1].next = NULL; + + flecs_free_n(&world->allocator, sort_helper_t, table_count, helper); +} + +void flecs_query_cache_build_sorted_tables( + ecs_query_cache_t *cache) +{ + ecs_vec_clear(&cache->table_slices); + + if (cache->group_by_callback) { + /* Populate sorted node list in grouping order */ + ecs_query_cache_table_match_t *cur = cache->list.first; + if (cur) { + do { + /* Find list for current group */ + uint64_t group_id = cur->group_id; + ecs_query_cache_table_list_t *list = ecs_map_get_deref( + &cache->groups, ecs_query_cache_table_list_t, group_id); + ecs_assert(list != NULL, ECS_INTERNAL_ERROR, NULL); + + /* Sort tables in current group */ + flecs_query_cache_build_sorted_table_range(cache, list); + + /* Find next group to sort */ + cur = list->last->next; + } while (cur); + } + } else { + flecs_query_cache_build_sorted_table_range(cache, &cache->list); + } +} + +void flecs_query_cache_sort_tables( + ecs_world_t *world, + ecs_query_impl_t *impl) +{ + ecs_query_cache_t *cache = impl->cache; + ecs_order_by_action_t compare = cache->order_by_callback; + if (!compare) { + return; + } + + ecs_sort_table_action_t sort = cache->order_by_table_callback; + + ecs_entity_t order_by = cache->order_by; + int32_t order_by_term = cache->order_by_term; + + /* Iterate over non-empty tables. Don't bother with empty tables as they + * have nothing to sort */ + + bool tables_sorted = false; + + ecs_id_record_t *idr = flecs_id_record_get(world, order_by); + ecs_table_cache_iter_t it; + ecs_query_cache_table_t *qt; + flecs_table_cache_iter(&cache->cache, &it); + + while ((qt = flecs_table_cache_next(&it, ecs_query_cache_table_t))) { + ecs_table_t *table = qt->hdr.table; + bool dirty = false; + + if (flecs_query_check_table_monitor(impl, qt, 0)) { + tables_sorted = true; + dirty = true; + } + + int32_t column = -1; + if (order_by) { + if (flecs_query_check_table_monitor(impl, qt, order_by_term + 1)) { + dirty = true; + } + + if (dirty) { + column = -1; + + const ecs_table_record_t *tr = flecs_id_record_get_table( + idr, table); + if (tr) { + column = tr->column; + } + + if (column == -1) { + /* Component is shared, no sorting is needed */ + dirty = false; + } + } + } + + if (!dirty) { + continue; + } + + /* Something has changed, sort the table. Prefers using + * flecs_query_cache_sort_table when available */ + flecs_query_cache_sort_table(world, table, column, compare, sort); + tables_sorted = true; + } + + if (tables_sorted || cache->match_count != cache->prev_match_count) { + flecs_query_cache_build_sorted_tables(cache); + cache->match_count ++; /* Increase version if tables changed */ + } +} diff --git a/vendors/flecs/src/query/engine/change_detection.c b/vendors/flecs/src/query/engine/change_detection.c new file mode 100644 index 000000000..6a4233e38 --- /dev/null +++ b/vendors/flecs/src/query/engine/change_detection.c @@ -0,0 +1,573 @@ +/** + * @file query/engine/change_detection.c + * @brief Compile query term. + */ + +#include "../../private_api.h" + +typedef struct { + ecs_table_t *table; + int32_t column; +} flecs_table_column_t; + +static +void flecs_query_get_column_for_field( + const ecs_query_t *q, + ecs_query_cache_table_match_t *match, + int32_t field, + flecs_table_column_t *out) +{ + ecs_assert(field >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(field < q->field_count, ECS_INTERNAL_ERROR, NULL); + (void)q; + + const ecs_table_record_t *tr = match->trs[field]; + ecs_table_t *table = tr->hdr.table; + int32_t column = tr->column; + + out->table = table; + out->column = column; +} + +/* Get match monitor. Monitors are used to keep track of whether components + * matched by the query in a table have changed. */ +static +bool flecs_query_get_match_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match) +{ + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); + if (match->monitor) { + return false; + } + + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *monitor = flecs_balloc(&cache->allocators.monitors); + monitor[0] = 0; + + /* Mark terms that don't need to be monitored. This saves time when reading + * and/or updating the monitor. */ + const ecs_query_t *q = cache->query; + int32_t i, field = -1, term_count = q->term_count; + flecs_table_column_t tc; + + for (i = 0; i < term_count; i ++) { + if (field == q->terms[i].field_index) { + if (monitor[field + 1] != -1) { + continue; + } + } + + field = q->terms[i].field_index; + monitor[field + 1] = -1; + + /* If term isn't read, don't monitor */ + if (q->terms[i].inout != EcsIn && + q->terms[i].inout != EcsInOut && + q->terms[i].inout != EcsInOutDefault) { + continue; + } + + /* Don't track fields that aren't set */ + if (!(match->set_fields & (1llu << field))) { + continue; + } + + flecs_query_get_column_for_field(q, match, field, &tc); + if (tc.column == -1) { + continue; /* Don't track terms that aren't stored */ + } + + monitor[field + 1] = 0; + } + + match->monitor = monitor; + + impl->pub.flags |= EcsQueryHasMonitor; + + return true; +} + +/* Get monitor for fixed query terms. Fixed terms are handled separately as they + * don't require a query cache, and fixed terms aren't stored in the cache. */ +static +bool flecs_query_get_fixed_monitor( + ecs_query_impl_t *impl, + bool check) +{ + ecs_query_t *q = &impl->pub; + ecs_world_t *world = q->world; + ecs_term_t *terms = q->terms; + int32_t i, term_count = q->term_count; + + if (!impl->monitor) { + impl->monitor = flecs_alloc_n(&impl->stage->allocator, + int32_t, q->field_count); + check = false; /* If the monitor is new, initialize it with dirty state */ + } + + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + int16_t field_index = term->field_index; + + if (!(q->read_fields & flecs_ito(uint32_t, 1 << field_index))) { + continue; /* If term doesn't read data there's nothing to track */ + } + + if (!(term->src.id & EcsIsEntity)) { + continue; /* Not a term with a fixed source */ + } + + ecs_entity_t src = ECS_TERM_REF_ID(&term->src); + ecs_assert(src != 0, ECS_INTERNAL_ERROR, NULL); + + ecs_record_t *r = flecs_entities_get(world, src); + if (!r || !r->table) { + continue; /* Entity is empty, nothing to track */ + } + + ecs_id_record_t *idr = flecs_id_record_get(world, term->id); + if (!idr) { + continue; /* If id doesn't exist, entity can't have it */ + } + + ecs_table_record_t *tr = flecs_id_record_get_table(idr, r->table); + if (!tr) { + continue; /* Entity doesn't have the component */ + } + + /* Copy/check column dirty state from table */ + int32_t *dirty_state = flecs_table_get_dirty_state(world, r->table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + + if (!check) { + impl->monitor[field_index] = dirty_state[tr->column + 1]; + } else { + if (impl->monitor[field_index] != dirty_state[tr->column + 1]) { + return true; + } + } + } + + return !check; +} + +bool flecs_query_update_fixed_monitor( + ecs_query_impl_t *impl) +{ + return flecs_query_get_fixed_monitor(impl, false); +} + +bool flecs_query_check_fixed_monitor( + ecs_query_impl_t *impl) +{ + return flecs_query_get_fixed_monitor(impl, true); +} + + +/* Check if single match term has changed */ +static +bool flecs_query_check_match_monitor_term( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match, + int32_t field) +{ + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); + + if (flecs_query_get_match_monitor(impl, match)) { + return true; + } + + int32_t *monitor = match->monitor; + int32_t state = monitor[field]; + if (state == -1) { + return false; + } + + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = match->table; + if (table) { + int32_t *dirty_state = flecs_table_get_dirty_state( + cache->query->world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (!field) { + return monitor[0] != dirty_state[0]; + } + } else if (!field) { + return false; + } + + flecs_table_column_t cur; + flecs_query_get_column_for_field( + &impl->pub, match, field - 1, &cur); + ecs_assert(cur.column != -1, ECS_INTERNAL_ERROR, NULL); + + return monitor[field] != flecs_table_get_dirty_state( + cache->query->world, cur.table)[cur.column + 1]; +} + +static +bool flecs_query_check_cache_monitor( + ecs_query_impl_t *impl) +{ + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + + /* If the match count changed, tables got matched/unmatched for the + * cache, so return that the query has changed. */ + if (cache->match_count != cache->prev_match_count) { + return true; + } + + ecs_table_cache_iter_t it; + if (flecs_table_cache_iter(&cache->cache, &it)) { + ecs_query_cache_table_t *qt; + while ((qt = flecs_table_cache_next(&it, ecs_query_cache_table_t))) { + if (flecs_query_check_table_monitor(impl, qt, -1)) { + return true; + } + } + } + + return false; +} + +static +void flecs_query_init_query_monitors( + ecs_query_impl_t *impl) +{ + /* Change monitor for cache */ + ecs_query_cache_t *cache = impl->cache; + if (cache) { + ecs_query_cache_table_match_t *cur = cache->list.first; + + /* Ensure each match has a monitor */ + for (; cur != NULL; cur = cur->next) { + ecs_query_cache_table_match_t *match = + (ecs_query_cache_table_match_t*)cur; + flecs_query_get_match_monitor(impl, match); + } + } +} + +static +bool flecs_query_check_match_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match, + const ecs_iter_t *it) +{ + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); + + if (flecs_query_get_match_monitor(impl, match)) { + return true; + } + + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *monitor = match->monitor; + ecs_table_t *table = match->table; + int32_t *dirty_state = NULL; + if (table) { + dirty_state = flecs_table_get_dirty_state( + cache->query->world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (monitor[0] != dirty_state[0]) { + return true; + } + } + + const ecs_query_t *query = cache->query; + ecs_world_t *world = query->world; + int32_t i, field_count = query->field_count; + ecs_entity_t *sources = match->sources; + const ecs_table_record_t **trs = it ? it->trs : match->trs; + ecs_flags64_t set_fields = it ? it->set_fields : match->set_fields; + + ecs_assert(trs != NULL, ECS_INTERNAL_ERROR, NULL); + + for (i = 0; i < field_count; i ++) { + int32_t mon = monitor[i + 1]; + if (mon == -1) { + continue; + } + + if (!(set_fields & (1llu << i))) { + continue; + } + + int32_t column = trs[i]->column; + ecs_entity_t src = sources[i]; + if (!src) { + if (column >= 0) { + /* owned component */ + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (mon != dirty_state[column + 1]) { + return true; + } + continue; + } else if (column == -1) { + continue; /* owned but not a component */ + } + } + + /* Component from non-this source */ + ecs_entity_t fixed_src = match->sources[i]; + ecs_table_t *src_table = ecs_get_table(world, fixed_src); + ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *src_dirty_state = flecs_table_get_dirty_state( + world, src_table); + if (mon != src_dirty_state[column + 1]) { + return true; + } + } + + return false; +} + +/* Check if any term for matched table has changed */ +bool flecs_query_check_table_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_t *table, + int32_t field) +{ + ecs_query_cache_table_match_t *cur, *end = table->last->next; + + for (cur = table->first; cur != end; cur = cur->next) { + ecs_query_cache_table_match_t *match = + (ecs_query_cache_table_match_t*)cur; + if (field == -1) { + if (flecs_query_check_match_monitor(impl, match, NULL)) { + return true; + } + } else { + if (flecs_query_check_match_monitor_term(impl, match, field)) { + return true; + } + } + } + + return false; +} + +void flecs_query_mark_fields_dirty( + ecs_query_impl_t *impl, + ecs_iter_t *it) +{ + ecs_query_t *q = &impl->pub; + + /* Evaluate all writeable non-fixed fields, set fields */ + ecs_termset_t write_fields = + (ecs_termset_t)(q->write_fields & ~q->fixed_fields & it->set_fields); + if (!write_fields || (it->flags & EcsIterNoData)) { + return; + } + + ecs_world_t *world = q->world; + int16_t i, field_count = q->field_count; + for (i = 0; i < field_count; i ++) { + ecs_termset_t field_bit = (ecs_termset_t)(1u << i); + if (!(write_fields & field_bit)) { + continue; /* If term doesn't write data there's nothing to track */ + } + + ecs_entity_t src = it->sources[i]; + ecs_table_t *table; + if (!src) { + table = it->table; + } else { + ecs_record_t *r = flecs_entities_get(world, src); + if (!r || !(table = r->table)) { + continue; + } + + if (q->shared_readonly_fields & flecs_ito(uint32_t, 1 << i)) { + /* Shared fields that aren't marked explicitly as out/inout + * default to readonly */ + continue; + } + } + + int32_t type_index = it->trs[i]->index; + ecs_assert(type_index >= 0, ECS_INTERNAL_ERROR, NULL); + + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *dirty_state = table->dirty_state; + if (!dirty_state) { + continue; + } + + ecs_assert(type_index < table->type.count, ECS_INTERNAL_ERROR, NULL); + int32_t column = table->column_map[type_index]; + dirty_state[column + 1] ++; + } +} + +void flecs_query_mark_fixed_fields_dirty( + ecs_query_impl_t *impl, + ecs_iter_t *it) +{ + /* This function marks fields dirty for terms with fixed sources. */ + ecs_query_t *q = &impl->pub; + ecs_termset_t fixed_write_fields = q->write_fields & q->fixed_fields; + if (!fixed_write_fields) { + return; + } + + ecs_world_t *world = q->world; + int32_t i, field_count = q->field_count; + for (i = 0; i < field_count; i ++) { + if (!(fixed_write_fields & flecs_ito(uint32_t, 1 << i))) { + continue; /* If term doesn't write data there's nothing to track */ + } + + ecs_entity_t src = it->sources[i]; + ecs_assert(src != 0, ECS_INTERNAL_ERROR, NULL); + ecs_record_t *r = flecs_entities_get(world, src); + ecs_table_t *table; + if (!r || !(table = r->table)) { + /* If the field is optional, it's possible that it didn't match */ + continue; + } + + int32_t *dirty_state = table->dirty_state; + if (!dirty_state) { + continue; + } + + ecs_assert(it->trs[i]->column >= 0, ECS_INTERNAL_ERROR, NULL); + int32_t column = table->column_map[it->trs[i]->column]; + dirty_state[column + 1] ++; + } +} + +/* Synchronize match monitor with table dirty state */ +void flecs_query_sync_match_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match) +{ + ecs_assert(match != NULL, ECS_INTERNAL_ERROR, NULL); + + if (!match->monitor) { + if (impl->pub.flags & EcsQueryHasMonitor) { + flecs_query_get_match_monitor(impl, match); + } else { + return; + } + } + + ecs_query_cache_t *cache = impl->cache; + ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *monitor = match->monitor; + ecs_table_t *table = match->table; + if (table) { + int32_t *dirty_state = flecs_table_get_dirty_state( + cache->query->world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + monitor[0] = dirty_state[0]; /* Did table gain/lose entities */ + } + + ecs_query_t *q = cache->query; + { + flecs_table_column_t tc; + int32_t t, term_count = q->term_count; + for (t = 0; t < term_count; t ++) { + int32_t field = q->terms[t].field_index; + if (monitor[field + 1] == -1) { + continue; + } + + flecs_query_get_column_for_field(q, match, field, &tc); + + monitor[field + 1] = flecs_table_get_dirty_state( + q->world, tc.table)[tc.column + 1]; + } + } + + cache->prev_match_count = cache->match_count; +} + +bool ecs_query_changed( + ecs_query_t *q) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_query_impl_t *impl = flecs_query_impl(q); + + ecs_assert(q->cache_kind != EcsQueryCacheNone, ECS_INVALID_OPERATION, + "change detection is only supported on cached queries"); + + /* If query reads terms with fixed sources, check those first as that's + * cheaper than checking entries in the cache. */ + if (impl->monitor) { + if (flecs_query_check_fixed_monitor(impl)) { + return true; + } + } + + /* Check cache for changes. We can't detect changes for terms that are not + * cached/cacheable and don't have a fixed source, since that requires + * storing state per result, which doesn't happen for uncached queries. */ + if (impl->cache) { + /* If we're checking the cache, make sure that tables are in the correct + * empty/non-empty lists. */ + flecs_process_pending_tables(q->world); + + if (!(impl->pub.flags & EcsQueryHasMonitor)) { + flecs_query_init_query_monitors(impl); + } + + /* Check cache entries for changes */ + return flecs_query_check_cache_monitor(impl); + } + + return false; +} + +bool ecs_iter_changed( + ecs_iter_t *it) +{ + ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(it->next == ecs_query_next, ECS_UNSUPPORTED, NULL); + ecs_check(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), + ECS_INVALID_PARAMETER, NULL); + + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_impl_t *impl = flecs_query_impl(qit->query); + ecs_query_t *q = &impl->pub; + + /* First check for changes for terms with fixed sources, if query has any */ + if (q->read_fields & q->fixed_fields) { + /* Detecting changes for uncached terms is costly, so only do it once + * per iteration. */ + if (!(it->flags & EcsIterFixedInChangeComputed)) { + it->flags |= EcsIterFixedInChangeComputed; + ECS_BIT_COND(it->flags, EcsIterFixedInChanged, + flecs_query_check_fixed_monitor(impl)); + } + + if (it->flags & EcsIterFixedInChanged) { + return true; + } + } + + /* If query has a cache, check for changes in current matched result */ + if (impl->cache) { + ecs_query_cache_table_match_t *qm = + (ecs_query_cache_table_match_t*)it->priv_.iter.query.prev; + ecs_check(qm != NULL, ECS_INVALID_PARAMETER, NULL); + return flecs_query_check_match_monitor(impl, qm, it); + } + +error: + return false; +} + +void ecs_iter_skip( + ecs_iter_t *it) +{ + ecs_assert(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ECS_BIT_IS_SET(it->flags, EcsIterIsValid), + ECS_INVALID_PARAMETER, NULL); + it->flags |= EcsIterSkip; +} diff --git a/vendors/flecs/src/query/engine/change_detection.h b/vendors/flecs/src/query/engine/change_detection.h new file mode 100644 index 000000000..176534d11 --- /dev/null +++ b/vendors/flecs/src/query/engine/change_detection.h @@ -0,0 +1,35 @@ +/** + * @file query/engine/change_detection.h + * @brief Query change detection functions. + */ + +#include "../types.h" + +/* Synchronize cache monitor with table dirty state */ +void flecs_query_sync_match_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_match_t *match); + +/* Mark iterated out fields dirty */ +void flecs_query_mark_fields_dirty( + ecs_query_impl_t *impl, + ecs_iter_t *it); + +/* Compare cache monitor with table dirty state to detect changes */ +bool flecs_query_check_table_monitor( + ecs_query_impl_t *impl, + ecs_query_cache_table_t *table, + int32_t term); + +/* Mark out fields with fixed source dirty */ +void flecs_query_mark_fixed_fields_dirty( + ecs_query_impl_t *impl, + ecs_iter_t *it); + +/* Synchronize fixed source monitor */ +bool flecs_query_update_fixed_monitor( + ecs_query_impl_t *impl); + +/* Compare fixed source monitor */ +bool flecs_query_check_fixed_monitor( + ecs_query_impl_t *impl); diff --git a/vendors/flecs/src/query/engine/engine.h b/vendors/flecs/src/query/engine/engine.h new file mode 100644 index 000000000..6a89c408f --- /dev/null +++ b/vendors/flecs/src/query/engine/engine.h @@ -0,0 +1,324 @@ +/** + * @file query/engine/engine.h + * @brief Query engine functions. + */ + +#include "cache.h" +#include "cache_iter.h" +#include "change_detection.h" +#include "trav_cache.h" +#include "trivial_iter.h" + +/* Query evaluation utilities */ + +void flecs_query_set_iter_this( + ecs_iter_t *it, + const ecs_query_run_ctx_t *ctx); + +ecs_query_op_ctx_t* flecs_op_ctx_( + const ecs_query_run_ctx_t *ctx); + +#define flecs_op_ctx(ctx, op_kind) (&flecs_op_ctx_(ctx)->is.op_kind) + +void flecs_reset_source_set_flag( + ecs_iter_t *it, + int32_t field_index); + +void flecs_set_source_set_flag( + ecs_iter_t *it, + int32_t field_index); + +ecs_table_range_t flecs_range_from_entity( + ecs_entity_t e, + const ecs_query_run_ctx_t *ctx); + +ecs_table_range_t flecs_query_var_get_range( + int32_t var_id, + const ecs_query_run_ctx_t *ctx); + +ecs_table_t* flecs_query_var_get_table( + int32_t var_id, + const ecs_query_run_ctx_t *ctx); + +ecs_table_t* flecs_query_get_table( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + const ecs_query_run_ctx_t *ctx); + +ecs_table_range_t flecs_query_get_range( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + const ecs_query_run_ctx_t *ctx); + +ecs_entity_t flecs_query_var_get_entity( + ecs_var_id_t var_id, + const ecs_query_run_ctx_t *ctx); + +void flecs_query_var_reset( + ecs_var_id_t var_id, + const ecs_query_run_ctx_t *ctx); + +void flecs_query_var_set_range( + const ecs_query_op_t *op, + ecs_var_id_t var_id, + ecs_table_t *table, + int32_t offset, + int32_t count, + const ecs_query_run_ctx_t *ctx); + +void flecs_query_var_narrow_range( + ecs_var_id_t var_id, + ecs_table_t *table, + int32_t offset, + int32_t count, + const ecs_query_run_ctx_t *ctx); + +void flecs_query_var_set_entity( + const ecs_query_op_t *op, + ecs_var_id_t var_id, + ecs_entity_t entity, + const ecs_query_run_ctx_t *ctx); + +void flecs_query_set_vars( + const ecs_query_op_t *op, + ecs_id_t id, + const ecs_query_run_ctx_t *ctx); + +ecs_table_range_t flecs_get_ref_range( + const ecs_query_ref_t *ref, + ecs_flags16_t flag, + const ecs_query_run_ctx_t *ctx); + +ecs_entity_t flecs_get_ref_entity( + const ecs_query_ref_t *ref, + ecs_flags16_t flag, + const ecs_query_run_ctx_t *ctx); + +ecs_id_t flecs_query_op_get_id_w_written( + const ecs_query_op_t *op, + uint64_t written, + const ecs_query_run_ctx_t *ctx); + +ecs_id_t flecs_query_op_get_id( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx); + +int16_t flecs_query_next_column( + ecs_table_t *table, + ecs_id_t id, + int32_t column); + +void flecs_query_it_set_tr( + ecs_iter_t *it, + int32_t field_index, + const ecs_table_record_t *tr); + +ecs_id_t flecs_query_it_set_id( + ecs_iter_t *it, + ecs_table_t *table, + int32_t field_index, + int32_t column); + +void flecs_query_set_match( + const ecs_query_op_t *op, + ecs_table_t *table, + int32_t column, + const ecs_query_run_ctx_t *ctx); + +void flecs_query_set_trav_match( + const ecs_query_op_t *op, + const ecs_table_record_t *tr, + ecs_entity_t trav, + ecs_entity_t second, + const ecs_query_run_ctx_t *ctx); + +bool flecs_query_table_filter( + ecs_table_t *table, + ecs_query_lbl_t other, + ecs_flags32_t filter_mask); + +bool flecs_query_setids( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_run_until( + bool redo, + ecs_query_run_ctx_t *ctx, + const ecs_query_op_t *ops, + ecs_query_lbl_t first, + ecs_query_lbl_t cur, + int32_t last); + + +/* Select evaluation */ + +bool flecs_query_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + +bool flecs_query_select_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_flags32_t table_filter); + +bool flecs_query_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + +bool flecs_query_with_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + +bool flecs_query_select_w_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_id_t id, + ecs_flags32_t filter_mask); + + +/* Union evaluation */ + +bool flecs_query_union_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + +bool flecs_query_union( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + +bool flecs_query_union_neq( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + +bool flecs_query_union_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + bool neq); + +bool flecs_query_union_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + +bool flecs_query_union_self_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + + +/* Toggle evaluation*/ + +bool flecs_query_toggle( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_toggle_option( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + + +/* Equality predicate evaluation */ + +bool flecs_query_pred_eq_match( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_pred_neq_match( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_pred_eq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_pred_eq_name( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_pred_neq_w_range( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx, + ecs_table_range_t r); + +bool flecs_query_pred_neq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_pred_neq_name( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + + +/* Component member evaluation */ + +bool flecs_query_member_eq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_member_neq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + + +/* Up traversal */ + +typedef enum ecs_query_up_select_trav_kind_t { + FlecsQueryUpSelectUp, + FlecsQueryUpSelectSelfUp +} ecs_query_up_select_trav_kind_t; + +typedef enum ecs_query_up_select_kind_t { + FlecsQueryUpSelectDefault, + FlecsQueryUpSelectId, + FlecsQueryUpSelectUnion +} ecs_query_up_select_kind_t; + +bool flecs_query_up_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_query_up_select_trav_kind_t trav_kind, + ecs_query_up_select_kind_t kind); + +bool flecs_query_up_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + +bool flecs_query_self_up_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + bool id_only); + + +/* Transitive relationship traversal */ + +bool flecs_query_trav( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx); + diff --git a/vendors/flecs/src/query/engine/eval.c b/vendors/flecs/src/query/engine/eval.c new file mode 100644 index 000000000..b600801d2 --- /dev/null +++ b/vendors/flecs/src/query/engine/eval.c @@ -0,0 +1,1450 @@ +/** + * @file query/engine/eval.c + * @brief Query engine implementation. + */ + +#include "../../private_api.h" + +// #define FLECS_QUERY_TRACE + +#ifdef FLECS_QUERY_TRACE +static int flecs_query_trace_indent = 0; +#endif + +static +bool flecs_query_dispatch( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx); + +bool flecs_query_select_w_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_id_t id, + ecs_flags32_t filter_mask) +{ + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + ecs_id_record_t *idr = op_ctx->idr; + ecs_table_record_t *tr; + ecs_table_t *table; + + if (!redo) { + if (!idr || idr->id != id) { + idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); + if (!idr) { + return false; + } + } + + if (ctx->query->pub.flags & EcsQueryMatchEmptyTables) { + if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { + return false; + } + } else { + if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { + return false; + } + } + } + +repeat: + if (!redo || !op_ctx->remaining) { + tr = flecs_table_cache_next(&op_ctx->it, ecs_table_record_t); + if (!tr) { + return false; + } + + op_ctx->column = flecs_ito(int16_t, tr->index); + op_ctx->remaining = flecs_ito(int16_t, tr->count - 1); + table = tr->hdr.table; + flecs_query_var_set_range(op, op->src.var, table, 0, 0, ctx); + } else { + tr = (ecs_table_record_t*)op_ctx->it.cur; + ecs_assert(tr != NULL, ECS_INTERNAL_ERROR, NULL); + table = tr->hdr.table; + op_ctx->column = flecs_query_next_column(table, idr->id, op_ctx->column); + op_ctx->remaining --; + } + + if (flecs_query_table_filter(table, op->other, filter_mask)) { + goto repeat; + } + + flecs_query_set_match(op, table, op_ctx->column, ctx); + return true; +} + +bool flecs_query_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_id_t id = 0; + if (!redo) { + id = flecs_query_op_get_id(op, ctx); + } + return flecs_query_select_w_id(op, redo, ctx, id, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)); +} + +bool flecs_query_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + ecs_id_record_t *idr = op_ctx->idr; + ecs_table_record_t *tr; + + ecs_table_t *table = flecs_query_get_table(op, &op->src, EcsQuerySrc, ctx); + if (!table) { + return false; + } + + if (!redo) { + ecs_id_t id = flecs_query_op_get_id(op, ctx); + if (!idr || idr->id != id) { + idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); + if (!idr) { + return false; + } + } + + tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return false; + } + + op_ctx->column = flecs_ito(int16_t, tr->index); + op_ctx->remaining = flecs_ito(int16_t, tr->count); + op_ctx->it.cur = &tr->hdr; + } else { + ecs_assert((op_ctx->remaining + op_ctx->column - 1) < table->type.count, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(op_ctx->remaining >= 0, ECS_INTERNAL_ERROR, NULL); + if (--op_ctx->remaining <= 0) { + return false; + } + + op_ctx->column = flecs_query_next_column(table, idr->id, op_ctx->column); + ecs_assert(op_ctx->column != -1, ECS_INTERNAL_ERROR, NULL); + } + + flecs_query_set_match(op, table, op_ctx->column, ctx); + return true; +} + +static +bool flecs_query_and( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (written & (1ull << op->src.var)) { + return flecs_query_with(op, redo, ctx); + } else { + return flecs_query_select(op, redo, ctx); + } +} + +bool flecs_query_select_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_flags32_t table_filter) +{ + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + ecs_iter_t *it = ctx->it; + int8_t field = op->field_index; + ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL); + + if (!redo) { + ecs_id_t id = it->ids[field]; + ecs_id_record_t *idr = op_ctx->idr; + if (!idr || idr->id != id) { + idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); + if (!idr) { + return false; + } + } + + if (ctx->query->pub.flags & EcsQueryMatchEmptyTables) { + if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { + return false; + } + } else { + if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { + return false; + } + } + } + +repeat: {} + const ecs_table_record_t *tr = flecs_table_cache_next( + &op_ctx->it, ecs_table_record_t); + if (!tr) { + return false; + } + + ecs_table_t *table = tr->hdr.table; + if (flecs_query_table_filter(table, op->other, table_filter)) { + goto repeat; + } + + flecs_query_var_set_range(op, op->src.var, table, 0, 0, ctx); + flecs_query_it_set_tr(it, field, tr); + return true; +} + +bool flecs_query_with_id( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; + } + + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + ecs_iter_t *it = ctx->it; + int8_t field = op->field_index; + ecs_assert(field != -1, ECS_INTERNAL_ERROR, NULL); + + ecs_table_t *table = flecs_query_get_table(op, &op->src, EcsQuerySrc, ctx); + if (!table) { + return false; + } + + ecs_id_t id = it->ids[field]; + ecs_id_record_t *idr = op_ctx->idr; + if (!idr || idr->id != id) { + idr = op_ctx->idr = flecs_id_record_get(ctx->world, id); + if (!idr) { + return false; + } + } + + ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return false; + } + + flecs_query_it_set_tr(it, field, tr); + return true; +} + +static +bool flecs_query_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + return flecs_query_up_with(op, redo, ctx); + } else { + return flecs_query_up_select(op, redo, ctx, + FlecsQueryUpSelectUp, FlecsQueryUpSelectDefault); + } +} + +static +bool flecs_query_self_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + return flecs_query_self_up_with(op, redo, ctx, false); + } else { + return flecs_query_up_select(op, redo, ctx, + FlecsQueryUpSelectSelfUp, FlecsQueryUpSelectDefault); + } +} + +static +bool flecs_query_and_any( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_flags16_t match_flags = op->match_flags; + if (redo) { + if (match_flags & EcsTermMatchAnySrc) { + return false; + } + } + + uint64_t written = ctx->written[ctx->op_index]; + int32_t remaining = 1; + bool result; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + result = flecs_query_with(op, redo, ctx); + } else { + result = flecs_query_select(op, redo, ctx); + remaining = 0; + } + + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + + if (match_flags & EcsTermMatchAny && op_ctx->remaining) { + op_ctx->remaining = flecs_ito(int16_t, remaining); + } + + int32_t field = op->field_index; + if (field != -1) { + ctx->it->ids[field] = flecs_query_op_get_id(op, ctx); + } + + ctx->it->trs[field] = (ecs_table_record_t*)op_ctx->it.cur; + + return result; +} + +static +bool flecs_query_only_any( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + return flecs_query_and_any(op, redo, ctx); + } else { + return flecs_query_select_w_id(op, redo, ctx, EcsAny, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)); + } +} + +static +bool flecs_query_triv( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); + ecs_flags64_t termset = op->src.entity; + uint64_t written = ctx->written[ctx->op_index]; + ctx->written[ctx->op_index + 1] |= 1ull; + if (written & 1ull) { + flecs_query_set_iter_this(ctx->it, ctx); + return flecs_query_trivial_test(ctx, redo, termset); + } else { + return flecs_query_trivial_search(ctx, op_ctx, redo, termset); + } +} + +static +bool flecs_query_cache( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + (void)op; + (void)redo; + + uint64_t written = ctx->written[ctx->op_index]; + ctx->written[ctx->op_index + 1] |= 1ull; + if (written & 1ull) { + return flecs_query_cache_test(ctx, redo); + } else { + return flecs_query_cache_search(ctx); + } +} + +static +bool flecs_query_is_cache( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + (void)op; + + uint64_t written = ctx->written[ctx->op_index]; + ctx->written[ctx->op_index + 1] |= 1ull; + if (written & 1ull) { + return flecs_query_is_cache_test(ctx, redo); + } else { + return flecs_query_is_cache_search(ctx); + } +} + +static +int32_t flecs_query_next_inheritable_id( + ecs_world_t *world, + ecs_type_t *type, + int32_t index) +{ + int32_t i; + for (i = index; i < type->count; i ++) { + ecs_id_record_t *idr = flecs_id_record_get(world, type->array[i]); + if (!(idr->flags & EcsIdOnInstantiateDontInherit)) { + return i; + } + } + return -1; +} + +static +bool flecs_query_x_from( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_oper_kind_t oper) +{ + ecs_query_xfrom_ctx_t *op_ctx = flecs_op_ctx(ctx, xfrom); + ecs_world_t *world = ctx->world; + ecs_type_t *type; + int32_t i; + + if (!redo) { + /* Find entity that acts as the template from which we match the ids */ + ecs_id_t id = flecs_query_op_get_id(op, ctx); + ecs_assert(ecs_is_alive(world, id), ECS_INTERNAL_ERROR, NULL); + ecs_record_t *r = flecs_entities_get(world, id); + ecs_table_t *table; + if (!r || !(table = r->table)) { + /* Nothing to match */ + return false; + } + + /* Find first id to test against. Skip ids with DontInherit flag. */ + type = op_ctx->type = &table->type; + op_ctx->first_id_index = flecs_query_next_inheritable_id( + world, type, 0); + op_ctx->cur_id_index = op_ctx->first_id_index; + + if (op_ctx->cur_id_index == -1) { + return false; /* No ids to filter on */ + } + } else { + type = op_ctx->type; + } + + ecs_id_t *ids = type->array; + + /* Check if source is variable, and if it's already written */ + bool src_written = true; + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + uint64_t written = ctx->written[ctx->op_index]; + src_written = written & (1ull << op->src.var); + } + + do { + int32_t id_index = op_ctx->cur_id_index; + + /* If source is not yet written, find tables with first id */ + if (!src_written) { + ecs_entity_t first_id = ids[id_index]; + + if (!flecs_query_select_w_id(op, redo, ctx, + first_id, (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) + { + if (oper == EcsOrFrom) { + id_index = flecs_query_next_inheritable_id( + world, type, id_index + 1); + if (id_index != -1) { + op_ctx->cur_id_index = id_index; + redo = false; + continue; + } + } + + return false; + } + + id_index ++; /* First id got matched */ + } else if (redo && src_written) { + return false; + } + + ecs_table_t *src_table = flecs_query_get_table( + op, &op->src, EcsQuerySrc, ctx); + if (!src_table) { + continue; + } + + redo = true; + + if (!src_written && oper == EcsOrFrom) { + /* Eliminate duplicate matches from tables that have multiple + * components from the type list */ + if (op_ctx->cur_id_index != op_ctx->first_id_index) { + for (i = op_ctx->first_id_index; i < op_ctx->cur_id_index; i ++) { + ecs_id_record_t *idr = flecs_id_record_get(world, ids[i]); + if (!idr) { + continue; + } + + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + + if (flecs_id_record_get_table(idr, src_table) != NULL) { + /* Already matched */ + break; + } + } + if (i != op_ctx->cur_id_index) { + continue; + } + } + return true; + } + + if (oper == EcsAndFrom || oper == EcsNotFrom || src_written) { + for (i = id_index; i < type->count; i ++) { + ecs_id_record_t *idr = flecs_id_record_get(world, ids[i]); + if (!idr) { + if (oper == EcsAndFrom) { + return false; + } else { + continue; + } + } + + if (idr->flags & EcsIdOnInstantiateDontInherit) { + continue; + } + + if (flecs_id_record_get_table(idr, src_table) == NULL) { + if (oper == EcsAndFrom) { + break; /* Must have all ids */ + } + } else { + if (oper == EcsNotFrom) { + break; /* Must have none of the ids */ + } else if (oper == EcsOrFrom) { + return true; /* Single match is enough */ + } + } + } + + if (i == type->count) { + if (oper == EcsAndFrom || oper == EcsNotFrom) { + break; /* All ids matched */ + } + } + } + } while (true); + + return true; +} + +static +bool flecs_query_and_from( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + return flecs_query_x_from(op, redo, ctx, EcsAndFrom); +} + +static +bool flecs_query_not_from( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + return flecs_query_x_from(op, redo, ctx, EcsNotFrom); +} + +static +bool flecs_query_or_from( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + return flecs_query_x_from(op, redo, ctx, EcsOrFrom); +} + +static +bool flecs_query_ids( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; + } + + ecs_id_record_t *cur; + ecs_id_t id = flecs_query_op_get_id(op, ctx); + + { + cur = flecs_id_record_get(ctx->world, id); + if (!cur || !cur->cache.tables.count) { + return false; + } + } + + flecs_query_set_vars(op, cur->id, ctx); + + if (op->field_index != -1) { + ecs_iter_t *it = ctx->it; + it->ids[op->field_index] = id; + it->sources[op->field_index] = EcsWildcard; + it->trs[op->field_index] = NULL; /* Mark field as set */ + } + + return true; +} + +static +bool flecs_query_idsright( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_ids_ctx_t *op_ctx = flecs_op_ctx(ctx, ids); + ecs_id_record_t *cur; + + if (!redo) { + ecs_id_t id = flecs_query_op_get_id(op, ctx); + if (!ecs_id_is_wildcard(id)) { + /* If id is not a wildcard, we can directly return it. This can + * happen if a variable was constrained by an iterator. */ + op_ctx->cur = NULL; + flecs_query_set_vars(op, id, ctx); + return true; + } + + cur = op_ctx->cur = flecs_id_record_get(ctx->world, id); + if (!cur) { + return false; + } + } else { + if (!op_ctx->cur) { + return false; + } + } + + do { + cur = op_ctx->cur = op_ctx->cur->first.next; + } while (cur && !cur->cache.tables.count); /* Skip empty ids */ + + if (!cur) { + return false; + } + + flecs_query_set_vars(op, cur->id, ctx); + + if (op->field_index != -1) { + ecs_iter_t *it = ctx->it; + ecs_id_t id = flecs_query_op_get_id_w_written(op, op->written, ctx); + it->ids[op->field_index] = id; + it->sources[op->field_index] = EcsWildcard; + ECS_TERMSET_SET(it->set_fields, 1u << op->field_index); + } + + return true; +} + +static +bool flecs_query_idsleft( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_ids_ctx_t *op_ctx = flecs_op_ctx(ctx, ids); + ecs_id_record_t *cur; + + if (!redo) { + ecs_id_t id = flecs_query_op_get_id(op, ctx); + if (!ecs_id_is_wildcard(id)) { + /* If id is not a wildcard, we can directly return it. This can + * happen if a variable was constrained by an iterator. */ + op_ctx->cur = NULL; + flecs_query_set_vars(op, id, ctx); + return true; + } + + cur = op_ctx->cur = flecs_id_record_get(ctx->world, id); + if (!cur) { + return false; + } + } else { + if (!op_ctx->cur) { + return false; + } + } + + do { + cur = op_ctx->cur = op_ctx->cur->second.next; + } while (cur && !cur->cache.tables.count); /* Skip empty ids */ + + if (!cur) { + return false; + } + + flecs_query_set_vars(op, cur->id, ctx); + + if (op->field_index != -1) { + ecs_iter_t *it = ctx->it; + ecs_id_t id = flecs_query_op_get_id_w_written(op, op->written, ctx); + it->ids[op->field_index] = id; + it->sources[op->field_index] = EcsWildcard; + ECS_TERMSET_SET(it->set_fields, 1u << op->field_index); + } + + return true; +} + +static +bool flecs_query_each( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_each_ctx_t *op_ctx = flecs_op_ctx(ctx, each); + int32_t row; + + ecs_table_range_t range = flecs_query_var_get_range(op->first.var, ctx); + ecs_table_t *table = range.table; + if (!table) { + return false; + } + + if (!redo) { + if (!ecs_table_count(table)) { + return false; + } + row = op_ctx->row = range.offset; + } else { + int32_t end = range.count; + if (end) { + end += range.offset; + } else { + end = ecs_table_count(table); + } + row = ++ op_ctx->row; + if (op_ctx->row >= end) { + return false; + } + } + + ecs_assert(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); + const ecs_entity_t *entities = ecs_table_entities(table); + flecs_query_var_set_entity(op, op->src.var, entities[row], ctx); + + return true; +} + +static +bool flecs_query_store( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + if (!redo) { + flecs_query_var_set_entity(op, op->src.var, op->first.entity, ctx); + return true; + } else { + return false; + } +} + +static +bool flecs_query_reset( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + if (!redo) { + return true; + } else { + flecs_query_var_reset(op->src.var, ctx); + return false; + } +} + +static +bool flecs_query_lookup( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; + } + + const ecs_query_impl_t *query = ctx->query; + ecs_entity_t first = flecs_query_var_get_entity(op->first.var, ctx); + ecs_query_var_t *var = &query->vars[op->src.var]; + + ecs_entity_t result = ecs_lookup_path_w_sep(ctx->world, first, var->lookup, + NULL, NULL, false); + if (!result) { + flecs_query_var_set_entity(op, op->src.var, EcsWildcard, ctx); + return false; + } + + flecs_query_var_set_entity(op, op->src.var, result, ctx); + + return true; +} + +static +bool flecs_query_setvars( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + (void)op; + + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + ecs_var_id_t *src_vars = query->src_vars; + ecs_iter_t *it = ctx->it; + + if (redo) { + return false; + } + + int32_t i; + ecs_flags32_t up_fields = it->up_fields; + for (i = 0; i < q->field_count; i ++) { + ecs_var_id_t var_id = src_vars[i]; + if (!var_id) { + continue; + } + + if (up_fields & (1u << i)) { + continue; + } + + it->sources[i] = flecs_query_var_get_entity(var_id, ctx); + } + + return true; +} + +static +bool flecs_query_setthis( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + ecs_query_setthis_ctx_t *op_ctx = flecs_op_ctx(ctx, setthis); + ecs_var_t *vars = ctx->vars; + ecs_var_t *this_var = &vars[op->first.var]; + + if (!redo) { + /* Save values so we can restore them later */ + op_ctx->range = vars[0].range; + + /* Constrain This table variable to a single entity from the table */ + vars[0].range = flecs_range_from_entity(this_var->entity, ctx); + vars[0].entity = this_var->entity; + return true; + } else { + /* Restore previous values, so that instructions that are operating on + * the table variable use all the entities in the table. */ + vars[0].range = op_ctx->range; + vars[0].entity = 0; + return false; + } +} + +static +bool flecs_query_setfixed( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + (void)op; + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + ecs_iter_t *it = ctx->it; + + if (redo) { + return false; + } + + int32_t i; + for (i = 0; i < q->term_count; i ++) { + const ecs_term_t *term = &q->terms[i]; + const ecs_term_ref_t *src = &term->src; + if (src->id & EcsIsEntity) { + it->sources[term->field_index] = ECS_TERM_REF_ID(src); + } + } + + return true; +} + +bool flecs_query_setids( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + (void)op; + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + ecs_iter_t *it = ctx->it; + + if (redo) { + return false; + } + + int32_t i; + for (i = 0; i < q->term_count; i ++) { + const ecs_term_t *term = &q->terms[i]; + it->ids[term->field_index] = term->id; + } + + return true; +} + +static +bool flecs_query_setid( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; + } + + ecs_assert(op->field_index != -1, ECS_INTERNAL_ERROR, NULL); + ctx->it->ids[op->field_index] = op->first.entity; + return true; +} + +/* Check if entity is stored in table */ +static +bool flecs_query_contain( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; + } + + ecs_var_id_t src_id = op->src.var; + ecs_var_id_t first_id = op->first.var; + + ecs_table_t *table = flecs_query_var_get_table(src_id, ctx); + + ecs_entity_t e = flecs_query_var_get_entity(first_id, ctx); + return table == ecs_get_table(ctx->world, e); +} + +/* Check if first and second id of pair from last operation are the same */ +static +bool flecs_query_pair_eq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; + } + + ecs_iter_t *it = ctx->it; + ecs_id_t id = it->ids[op->field_index]; + return ECS_PAIR_FIRST(id) == ECS_PAIR_SECOND(id); +} + +static +void flecs_query_reset_after_block( + const ecs_query_op_t *start_op, + ecs_query_run_ctx_t *ctx, + ecs_query_ctrl_ctx_t *op_ctx, + bool result) +{ + ecs_query_lbl_t op_index = start_op->next; + const ecs_query_op_t *op = &ctx->qit->ops[op_index]; + + int32_t field = op->field_index; + if (field == -1) { + goto done; + } + + /* Set/unset field */ + ecs_iter_t *it = ctx->it; + if (result) { + ECS_TERMSET_SET(it->set_fields, 1u << field); + return; + } + + /* Reset state after a field was not matched */ + ctx->written[op_index] = ctx->written[ctx->op_index]; + ctx->op_index = op_index; + ECS_TERMSET_CLEAR(it->set_fields, 1u << field); + + /* Ignore variables written by Not operation */ + uint64_t *written = ctx->written; + uint64_t written_cur = written[op->prev + 1]; + ecs_flags16_t flags_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t flags_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); + + /* Overwrite id with cleared out variables */ + ecs_id_t id = flecs_query_op_get_id(op, ctx); + if (id) { + it->ids[field] = id; + } + + it->trs[field] = NULL; + + /* Reset variables */ + if (flags_1st & EcsQueryIsVar) { + if (!flecs_ref_is_written(op, &op->first, EcsQueryFirst, written_cur)){ + flecs_query_var_reset(op->first.var, ctx); + } + } + if (flags_2nd & EcsQueryIsVar) { + if (!flecs_ref_is_written(op, &op->second, EcsQuerySecond, written_cur)){ + flecs_query_var_reset(op->second.var, ctx); + } + } + + /* If term has entity src, set it because no other instruction might */ + if (op->flags & (EcsQueryIsEntity << EcsQuerySrc)) { + it->sources[field] = op->src.entity; + } + +done: + op_ctx->op_index = op_index; +} + +static +bool flecs_query_run_block( + bool redo, + ecs_query_run_ctx_t *ctx, + ecs_query_ctrl_ctx_t *op_ctx) +{ + ecs_iter_t *it = ctx->it; + ecs_query_iter_t *qit = &it->priv_.iter.query; + + if (!redo) { + op_ctx->op_index = flecs_itolbl(ctx->op_index + 1); + } else if (ctx->qit->ops[op_ctx->op_index].kind == EcsQueryEnd) { + return false; + } + + ctx->written[ctx->op_index + 1] = ctx->written[ctx->op_index]; + + const ecs_query_op_t *op = &ctx->qit->ops[ctx->op_index]; + bool result = flecs_query_run_until( + redo, ctx, qit->ops, ctx->op_index, op_ctx->op_index, op->next); + + op_ctx->op_index = flecs_itolbl(ctx->op_index - 1); + return result; +} + +static +ecs_query_lbl_t flecs_query_last_op_for_or_cond( + const ecs_query_op_t *ops, + ecs_query_lbl_t cur, + ecs_query_lbl_t last) +{ + const ecs_query_op_t *cur_op, *last_op = &ops[last]; + + do { + cur_op = &ops[cur]; + cur ++; + } while (cur_op->next != last && cur_op != last_op); + + return cur; +} + +static +bool flecs_query_run_until_for_select_or( + bool redo, + ecs_query_run_ctx_t *ctx, + const ecs_query_op_t *ops, + ecs_query_lbl_t first, + ecs_query_lbl_t cur, + int32_t last) +{ + ecs_query_lbl_t last_for_cur = flecs_query_last_op_for_or_cond( + ops, cur, flecs_itolbl(last)); + if (redo) { + /* If redoing, start from the last instruction of the last executed + * sequence */ + cur = flecs_itolbl(last_for_cur - 1); + } + + flecs_query_run_until(redo, ctx, ops, first, cur, last_for_cur); +#ifdef FLECS_QUERY_TRACE + printf("%*s%s (or)\n", (flecs_query_trace_indent + 1)*2, "", + ctx->op_index == last ? "true" : "false"); +#endif + return ctx->op_index == last; +} + +static +bool flecs_query_select_or( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + ecs_iter_t *it = ctx->it; + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + + ecs_query_lbl_t first = flecs_itolbl(ctx->op_index + 1); + if (!redo) { + op_ctx->op_index = first; + } + + const ecs_query_op_t *ops = qit->ops; + const ecs_query_op_t *first_op = &ops[first - 1]; + ecs_query_lbl_t last = first_op->next; + const ecs_query_op_t *last_op = &ops[last]; + const ecs_query_op_t *cur_op = &ops[op_ctx->op_index]; + bool result = false; + + do { + ecs_query_lbl_t cur = op_ctx->op_index; + ctx->op_index = cur; + ctx->written[cur] = op->written; + + result = flecs_query_run_until_for_select_or( + redo, ctx, ops, flecs_itolbl(first - 1), cur, last); + + if (result) { + if (first == cur) { + break; + } + + /* If a previous operation in the OR chain returned a result for the + * same matched source, skip it so we don't yield for each matching + * element in the chain. */ + + /* Copy written status so that the variables we just wrote will show + * up as written for the test. This ensures that the instructions + * match against the result we already found, vs. starting a new + * search (the difference between select & with). */ + ecs_query_lbl_t prev = first; + bool dup_found = false; + + /* While terms of an OR chain always operate on the same source, it + * is possible that a table variable is resolved to an entity + * variable. When checking for duplicates, copy the entity variable + * to the table, to ensure we're only testing the found entity. */ + const ecs_query_op_t *prev_op = &ops[cur - 1]; + ecs_var_t old_table_var; + ecs_os_memset_t(&old_table_var, 0, ecs_var_t); + bool restore_table_var = false; + + if (prev_op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + if (first_op->src.var != prev_op->src.var) { + restore_table_var = true; + old_table_var = ctx->vars[first_op->src.var]; + ctx->vars[first_op->src.var] = + ctx->vars[prev_op->src.var]; + } + } + + int16_t field_index = op->field_index; + ecs_id_t prev_id = it->ids[field_index]; + const ecs_table_record_t *prev_tr = it->trs[field_index]; + + do { + ctx->written[prev] = ctx->written[last]; + + flecs_query_run_until(false, ctx, ops, flecs_itolbl(first - 1), + prev, cur); + + if (ctx->op_index == last) { + /* Duplicate match was found, find next result */ + redo = true; + dup_found = true; + break; + } + + break; + } while (true); + + /* Restore table variable to full range for next result */ + if (restore_table_var) { + ctx->vars[first_op->src.var] = old_table_var; + } + + if (dup_found) { + continue; + } + + /* Restore id in case op set it */ + it->ids[field_index] = prev_id; + it->trs[field_index] = prev_tr; + break; + } + + /* No result was found, go to next operation in chain */ + op_ctx->op_index = flecs_query_last_op_for_or_cond( + ops, op_ctx->op_index, last); + cur_op = &qit->ops[op_ctx->op_index]; + + redo = false; + } while (cur_op != last_op); + + return result; +} + +static +bool flecs_query_with_or( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + + bool result = flecs_query_run_block(redo, ctx, op_ctx); + if (result) { + /* If a match was found, no need to keep searching for this source */ + op_ctx->op_index = op->next; + } + + return result; +} + +static +bool flecs_query_or( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + uint64_t written = ctx->written[ctx->op_index]; + if (!(written & (1ull << op->src.var))) { + return flecs_query_select_or(op, redo, ctx); + } + } + + return flecs_query_with_or(op, redo, ctx); +} + +static +bool flecs_query_run_block_w_reset( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + + bool result = flecs_query_run_block(redo, ctx, op_ctx); + flecs_query_reset_after_block(op, ctx, op_ctx, result); + return result; +} + +static +bool flecs_query_not( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; + } + + return !flecs_query_run_block_w_reset(op, redo, ctx); +} + +static +bool flecs_query_optional( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + bool result = flecs_query_run_block_w_reset(op, redo, ctx); + if (!redo) { + return true; /* Return at least once */ + } else { + return result; + } +} + +static +bool flecs_query_eval_if( + const ecs_query_op_t *op, + ecs_query_run_ctx_t *ctx, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind) +{ + bool result = true; + if (flecs_query_ref_flags(op->flags, ref_kind) == EcsQueryIsVar) { + result = ctx->vars[ref->var].entity != EcsWildcard; + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + flecs_query_reset_after_block(op, ctx, op_ctx, result); + return result; + } + return true; +} + +static +bool flecs_query_if_var( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + if (!redo) { + if (!flecs_query_eval_if(op, ctx, &op->src, EcsQuerySrc) || + !flecs_query_eval_if(op, ctx, &op->first, EcsQueryFirst) || + !flecs_query_eval_if(op, ctx, &op->second, EcsQuerySecond)) + { + return true; + } + } + + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + return flecs_query_run_block(redo, ctx, op_ctx); +} + +static +bool flecs_query_if_set( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + ecs_iter_t *it = ctx->it; + int8_t field_index = flecs_ito(int8_t, op->other); + + ecs_query_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + if (!redo) { + op_ctx->is_set = ecs_field_is_set(it, field_index); + } + + if (!op_ctx->is_set) { + return !redo; + } + + return flecs_query_run_block(redo, ctx, op_ctx); +} + +static +bool flecs_query_end( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + (void)op; (void)ctx; + return !redo; +} + +static +bool flecs_query_dispatch( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + switch(op->kind) { + case EcsQueryAnd: return flecs_query_and(op, redo, ctx); + case EcsQueryAndAny: return flecs_query_and_any(op, redo, ctx); + case EcsQueryTriv: return flecs_query_triv(op, redo, ctx); + case EcsQueryCache: return flecs_query_cache(op, redo, ctx); + case EcsQueryIsCache: return flecs_query_is_cache(op, redo, ctx); + case EcsQueryOnlyAny: return flecs_query_only_any(op, redo, ctx); + case EcsQueryUp: return flecs_query_up(op, redo, ctx); + case EcsQuerySelfUp: return flecs_query_self_up(op, redo, ctx); + case EcsQueryWith: return flecs_query_with(op, redo, ctx); + case EcsQueryTrav: return flecs_query_trav(op, redo, ctx); + case EcsQueryAndFrom: return flecs_query_and_from(op, redo, ctx); + case EcsQueryNotFrom: return flecs_query_not_from(op, redo, ctx); + case EcsQueryOrFrom: return flecs_query_or_from(op, redo, ctx); + case EcsQueryIds: return flecs_query_ids(op, redo, ctx); + case EcsQueryIdsRight: return flecs_query_idsright(op, redo, ctx); + case EcsQueryIdsLeft: return flecs_query_idsleft(op, redo, ctx); + case EcsQueryEach: return flecs_query_each(op, redo, ctx); + case EcsQueryStore: return flecs_query_store(op, redo, ctx); + case EcsQueryReset: return flecs_query_reset(op, redo, ctx); + case EcsQueryOr: return flecs_query_or(op, redo, ctx); + case EcsQueryOptional: return flecs_query_optional(op, redo, ctx); + case EcsQueryIfVar: return flecs_query_if_var(op, redo, ctx); + case EcsQueryIfSet: return flecs_query_if_set(op, redo, ctx); + case EcsQueryEnd: return flecs_query_end(op, redo, ctx); + case EcsQueryNot: return flecs_query_not(op, redo, ctx); + case EcsQueryPredEq: return flecs_query_pred_eq(op, redo, ctx); + case EcsQueryPredNeq: return flecs_query_pred_neq(op, redo, ctx); + case EcsQueryPredEqName: return flecs_query_pred_eq_name(op, redo, ctx); + case EcsQueryPredNeqName: return flecs_query_pred_neq_name(op, redo, ctx); + case EcsQueryPredEqMatch: return flecs_query_pred_eq_match(op, redo, ctx); + case EcsQueryPredNeqMatch: return flecs_query_pred_neq_match(op, redo, ctx); + case EcsQueryMemberEq: return flecs_query_member_eq(op, redo, ctx); + case EcsQueryMemberNeq: return flecs_query_member_neq(op, redo, ctx); + case EcsQueryToggle: return flecs_query_toggle(op, redo, ctx); + case EcsQueryToggleOption: return flecs_query_toggle_option(op, redo, ctx); + case EcsQueryUnionEq: return flecs_query_union(op, redo, ctx); + case EcsQueryUnionEqWith: return flecs_query_union_with(op, redo, ctx, false); + case EcsQueryUnionNeq: return flecs_query_union_neq(op, redo, ctx); + case EcsQueryUnionEqUp: return flecs_query_union_up(op, redo, ctx); + case EcsQueryUnionEqSelfUp: return flecs_query_union_self_up(op, redo, ctx); + case EcsQueryLookup: return flecs_query_lookup(op, redo, ctx); + case EcsQuerySetVars: return flecs_query_setvars(op, redo, ctx); + case EcsQuerySetThis: return flecs_query_setthis(op, redo, ctx); + case EcsQuerySetFixed: return flecs_query_setfixed(op, redo, ctx); + case EcsQuerySetIds: return flecs_query_setids(op, redo, ctx); + case EcsQuerySetId: return flecs_query_setid(op, redo, ctx); + case EcsQueryContain: return flecs_query_contain(op, redo, ctx); + case EcsQueryPairEq: return flecs_query_pair_eq(op, redo, ctx); + case EcsQueryYield: return false; + case EcsQueryNothing: return false; + } + return false; +} + +bool flecs_query_run_until( + bool redo, + ecs_query_run_ctx_t *ctx, + const ecs_query_op_t *ops, + ecs_query_lbl_t first, + ecs_query_lbl_t cur, + int32_t last) +{ + ecs_assert(first >= -1, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur > first, ECS_INTERNAL_ERROR, NULL); + + ctx->op_index = cur; + const ecs_query_op_t *op = &ops[ctx->op_index]; + const ecs_query_op_t *last_op = &ops[last]; + ecs_assert(last > first, ECS_INTERNAL_ERROR, NULL); + +#ifdef FLECS_QUERY_TRACE + printf("%*sblock:\n", flecs_query_trace_indent*2, ""); + flecs_query_trace_indent ++; +#endif + + do { + #ifdef FLECS_DEBUG + ctx->qit->profile[ctx->op_index].count[redo] ++; + #endif + +#ifdef FLECS_QUERY_TRACE + printf("%*s%d: %s\n", flecs_query_trace_indent*2, "", + ctx->op_index, flecs_query_op_str(op->kind)); +#endif + + bool result = flecs_query_dispatch(op, redo, ctx); + cur = (&op->prev)[result]; + redo = cur < ctx->op_index; + + if (!redo) { + ctx->written[cur] |= ctx->written[ctx->op_index] | op->written; + } + + ctx->op_index = cur; + op = &ops[ctx->op_index]; + + if (cur <= first) { +#ifdef FLECS_QUERY_TRACE + printf("%*sfalse\n", flecs_query_trace_indent*2, ""); + flecs_query_trace_indent --; +#endif + return false; + } + } while (op < last_op); + +#ifdef FLECS_QUERY_TRACE + printf("%*strue\n", flecs_query_trace_indent*2, ""); + flecs_query_trace_indent --; +#endif + + return true; +} diff --git a/vendors/flecs/src/query/engine/eval_iter.c b/vendors/flecs/src/query/engine/eval_iter.c new file mode 100644 index 000000000..052955dda --- /dev/null +++ b/vendors/flecs/src/query/engine/eval_iter.c @@ -0,0 +1,388 @@ +/** + * @file query/engine/eval_iter.c + * @brief Query iterator. + */ + +#include "../../private_api.h" + +static +void flecs_query_iter_run_ctx_init( + ecs_iter_t *it, + ecs_query_run_ctx_t *ctx) +{ + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_impl_t *impl = ECS_CONST_CAST(ecs_query_impl_t*, qit->query); + ctx->world = it->real_world; + ctx->query = impl; + ctx->it = it; + ctx->vars = qit->vars; + ctx->query_vars = qit->query_vars; + ctx->written = qit->written; + ctx->op_ctx = qit->op_ctx; + ctx->qit = qit; +} + +void flecs_query_iter_constrain( + ecs_iter_t *it) +{ + ecs_query_run_ctx_t ctx; + flecs_query_iter_run_ctx_init(it, &ctx); + ecs_assert(ctx.written != NULL, ECS_INTERNAL_ERROR, NULL); + + const ecs_query_impl_t *query = ctx.query; + const ecs_query_t *q = &query->pub; + ecs_flags64_t it_written = it->constrained_vars; + ctx.written[0] = it_written; + if (it_written && ctx.query->src_vars) { + /* If variables were constrained, check if there are any table + * variables that have a constrained entity variable. */ + ecs_var_t *vars = ctx.vars; + int32_t i, count = q->field_count; + for (i = 0; i < count; i ++) { + ecs_var_id_t var_id = query->src_vars[i]; + ecs_query_var_t *var = &query->vars[var_id]; + + if (!(it_written & (1ull << var_id)) || + (var->kind == EcsVarTable) || (var->table_id == EcsVarNone)) + { + continue; + } + + /* Initialize table variable with constrained entity variable */ + ecs_var_t *tvar = &vars[var->table_id]; + tvar->range = flecs_range_from_entity(vars[var_id].entity, &ctx); + ctx.written[0] |= (1ull << var->table_id); /* Mark as written */ + } + } + + /* This function can be called multiple times when setting variables, so + * reset flags before setting them. */ + it->flags &= ~(EcsIterTrivialTest|EcsIterTrivialCached| + EcsIterTrivialSearch); + + /* Figure out whether this query can utilize specialized iterator modes for + * improved performance. */ + ecs_flags32_t flags = q->flags; + ecs_query_cache_t *cache = query->cache; + if (flags & EcsQueryIsTrivial) { + if ((flags & EcsQueryMatchOnlySelf)) { + if (it_written) { + /* When we're testing against an entity or table, set the $this + * variable in advance since it won't change later on. This + * initializes it.count, it.entities and it.table. */ + flecs_query_set_iter_this(it, &ctx); + + if (!cache) { + if (!(flags & EcsQueryMatchWildcards)) { + it->flags |= EcsIterTrivialTest; + } + } else if (flags & EcsQueryIsCacheable) { + it->flags |= EcsIterTrivialTest|EcsIterTrivialCached; + } + } else { + if (!cache) { + if (!(flags & EcsQueryMatchWildcards)) { + it->flags |= EcsIterTrivialSearch; + } + } else if (flags & EcsQueryIsCacheable) { + if (!cache->order_by_callback) { + it->flags |= EcsIterTrivialSearch|EcsIterTrivialCached; + } + } + } + + /* If we're using a specialized iterator mode, make sure to + * initialize static component ids. Usually this is the first + * instruction of a query plan, but because we're not running the + * query plan when using a specialized iterator mode, manually call + * the operation on iterator init. */ + flecs_query_setids(NULL, false, &ctx); + } + } +} + +bool ecs_query_next( + ecs_iter_t *it) +{ + ecs_assert(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(it->next == ecs_query_next, ECS_INVALID_PARAMETER, NULL); + + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_query_impl_t *impl = ECS_CONST_CAST(ecs_query_impl_t*, qit->query); + ecs_query_run_ctx_t ctx; + flecs_query_iter_run_ctx_init(it, &ctx); + const ecs_query_op_t *ops = qit->ops; + + bool redo = it->flags & EcsIterIsValid; + if (redo) { + /* Change detection */ + if (!(it->flags & EcsIterSkip)) { + /* Mark table columns that are written to dirty */ + flecs_query_mark_fields_dirty(impl, it); + if (qit->prev) { + if (ctx.query->pub.flags & EcsQueryHasMonitor) { + /* If this query uses change detection, synchronize the + * monitor for the iterated table with the query */ + flecs_query_sync_match_monitor(impl, qit->prev); + } + } + } + } + + it->flags &= ~(EcsIterSkip); + it->flags |= EcsIterIsValid; + it->frame_offset += it->count; + + /* Specialized iterator modes. When a query doesn't use any advanced + * features, it can call specialized iterator functions directly instead of + * going through the dispatcher of the query engine. + * The iterator mode is set during iterator initialization. Besides being + * determined by the query, there are different modes for searching and + * testing, where searching returns all matches for a query, whereas testing + * tests a single table or table range against the query. */ + + if (it->flags & EcsIterTrivialCached) { + /* Cached iterator modes */ + if (it->flags & EcsIterTrivialSearch) { + if (flecs_query_is_cache_search(&ctx)) { + goto trivial_search_yield; + } + } else if (it->flags & EcsIterTrivialTest) { + if (flecs_query_is_cache_test(&ctx, redo)) { + goto yield; + } + } + } else { + /* Uncached iterator modes */ + if (it->flags & EcsIterTrivialSearch) { + ecs_query_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; + if (flecs_query_is_trivial_search(&ctx, op_ctx, redo)) { + goto yield; + } + } else if (it->flags & EcsIterTrivialTest) { + int32_t fields = ctx.query->pub.term_count; + ecs_flags64_t mask = (2llu << (fields - 1)) - 1; + if (flecs_query_trivial_test(&ctx, redo, mask)) { + goto yield; + } + } else { + /* Default iterator mode. This enters the query VM dispatch loop. */ + if (flecs_query_run_until( + redo, &ctx, ops, -1, qit->op, impl->op_count - 1)) + { + ecs_assert(ops[ctx.op_index].kind == EcsQueryYield, + ECS_INTERNAL_ERROR, NULL); + flecs_query_set_iter_this(it, &ctx); + ecs_assert(it->count >= 0, ECS_INTERNAL_ERROR, NULL); + qit->op = flecs_itolbl(ctx.op_index - 1); + goto yield; + } + } + } + + /* Done iterating */ + flecs_query_mark_fixed_fields_dirty(impl, it); + if (ctx.query->monitor) { + flecs_query_update_fixed_monitor( + ECS_CONST_CAST(ecs_query_impl_t*, ctx.query)); + } + + ecs_iter_fini(it); + return false; + +trivial_search_yield: + it->table = ctx.vars[0].range.table; + it->count = ecs_table_count(it->table); + it->entities = ecs_table_entities(it->table); + +yield: + return true; +} + +static +void flecs_query_iter_fini_ctx( + ecs_iter_t *it, + ecs_query_iter_t *qit) +{ + const ecs_query_impl_t *query = flecs_query_impl(qit->query); + int32_t i, count = query->op_count; + ecs_query_op_t *ops = query->ops; + ecs_query_op_ctx_t *ctx = qit->op_ctx; + ecs_allocator_t *a = flecs_query_get_allocator(it); + + for (i = 0; i < count; i ++) { + ecs_query_op_t *op = &ops[i]; + switch(op->kind) { + case EcsQueryTrav: + flecs_query_trav_cache_fini(a, &ctx[i].is.trav.cache); + break; + case EcsQueryUp: + case EcsQuerySelfUp: + case EcsQueryUnionEqUp: + case EcsQueryUnionEqSelfUp: { + ecs_trav_up_cache_t *cache = &ctx[i].is.up.cache; + if (cache->dir == EcsTravDown) { + flecs_query_down_cache_fini(a, cache); + } else { + flecs_query_up_cache_fini(cache); + } + break; + } + default: + break; + } + } +} + +static +void flecs_query_iter_fini( + ecs_iter_t *it) +{ + ecs_query_iter_t *qit = &it->priv_.iter.query; + ecs_assert(qit->query != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(qit->query, ecs_query_t); + int32_t op_count = flecs_query_impl(qit->query)->op_count; + int32_t var_count = flecs_query_impl(qit->query)->var_count; + +#ifdef FLECS_DEBUG + if (it->flags & EcsIterProfile) { + char *str = ecs_query_plan_w_profile(qit->query, it); + printf("%s\n", str); + ecs_os_free(str); + } + + flecs_iter_free_n(qit->profile, ecs_query_op_profile_t, op_count); +#endif + + flecs_query_iter_fini_ctx(it, qit); + flecs_iter_free_n(qit->vars, ecs_var_t, var_count); + flecs_iter_free_n(qit->written, ecs_write_flags_t, op_count); + flecs_iter_free_n(qit->op_ctx, ecs_query_op_ctx_t, op_count); + + qit->vars = NULL; + qit->written = NULL; + qit->op_ctx = NULL; + qit->query = NULL; +} + +ecs_iter_t flecs_query_iter( + const ecs_world_t *world, + const ecs_query_t *q) +{ + ecs_iter_t it = {0}; + ecs_query_iter_t *qit = &it.priv_.iter.query; + ecs_check(q != NULL, ECS_INVALID_PARAMETER, NULL); + + flecs_poly_assert(q, ecs_query_t); + ecs_query_impl_t *impl = flecs_query_impl(q); + + int32_t i, var_count = impl->var_count; + int32_t op_count = impl->op_count ? impl->op_count : 1; + it.world = ECS_CONST_CAST(ecs_world_t*, world); + + /* If world passed to iterator is the real world, but query was created from + * a stage, stage takes precedence. */ + if (flecs_poly_is(it.world, ecs_world_t) && + flecs_poly_is(q->world, ecs_stage_t)) + { + it.world = ECS_CONST_CAST(ecs_world_t*, q->world); + } + + it.real_world = q->real_world; + ecs_assert(flecs_poly_is(it.real_world, ecs_world_t), + ECS_INTERNAL_ERROR, NULL); + ecs_check(!(it.real_world->flags & EcsWorldMultiThreaded) || + it.world != it.real_world, ECS_INVALID_PARAMETER, + "create iterator for stage when world is in multithreaded mode"); + + it.query = q; + it.system = q->entity; + it.next = ecs_query_next; + it.fini = flecs_query_iter_fini; + it.field_count = q->field_count; + it.sizes = q->sizes; + it.set_fields = q->set_fields; + it.ref_fields = q->fixed_fields | q->row_fields | q->var_fields; + it.row_fields = q->row_fields; + it.up_fields = 0; + flecs_query_apply_iter_flags(&it, q); + + flecs_iter_init(it.world, &it, + flecs_iter_cache_ids | + flecs_iter_cache_trs | + flecs_iter_cache_sources | + flecs_iter_cache_ptrs); + + qit->query = q; + qit->query_vars = impl->vars; + qit->ops = impl->ops; + + ecs_query_cache_t *cache = impl->cache; + if (cache) { + qit->node = cache->list.first; + qit->last = cache->list.last; + + if (cache->order_by_callback && cache->list.info.table_count) { + flecs_query_cache_sort_tables(it.real_world, impl); + qit->node = ecs_vec_first(&cache->table_slices); + qit->last = ecs_vec_last_t( + &cache->table_slices, ecs_query_cache_table_match_t); + } + + cache->prev_match_count = cache->match_count; + } + + if (var_count) { + qit->vars = flecs_iter_calloc_n(&it, ecs_var_t, var_count); + } + + if (op_count) { + qit->written = flecs_iter_calloc_n(&it, ecs_write_flags_t, op_count); + qit->op_ctx = flecs_iter_calloc_n(&it, ecs_query_op_ctx_t, op_count); + } + +#ifdef FLECS_DEBUG + qit->profile = flecs_iter_calloc_n(&it, ecs_query_op_profile_t, op_count); +#endif + + for (i = 1; i < var_count; i ++) { + qit->vars[i].entity = EcsWildcard; + } + + it.variables = qit->vars; + it.variable_count = impl->pub.var_count; + it.variable_names = impl->pub.vars; + + /* Set flags for unconstrained query iteration. Can be reinitialized when + * variables are constrained on iterator. */ + flecs_query_iter_constrain(&it); +error: + return it; +} + +ecs_iter_t ecs_query_iter( + const ecs_world_t *world, + const ecs_query_t *q) +{ + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(q != NULL, ECS_INVALID_PARAMETER, NULL); + + if (!(q->flags & EcsQueryCacheYieldEmptyTables)) { + ecs_run_aperiodic(q->real_world, EcsAperiodicEmptyTables); + } + + /* Ok, only for stats */ + ecs_os_linc(&ECS_CONST_CAST(ecs_query_t*, q)->eval_count); + + ecs_query_impl_t *impl = flecs_query_impl(q); + ecs_query_cache_t *cache = impl->cache; + if (cache) { + /* If monitors changed, do query rematching */ + ecs_flags32_t flags = q->flags; + if (!(world->flags & EcsWorldReadonly) && flags & EcsQueryHasRefs) { + flecs_eval_component_monitors(q->world); + } + } + + return flecs_query_iter(world, q); +} diff --git a/vendors/flecs/src/query/engine/eval_member.c b/vendors/flecs/src/query/engine/eval_member.c new file mode 100644 index 000000000..1eff2a312 --- /dev/null +++ b/vendors/flecs/src/query/engine/eval_member.c @@ -0,0 +1,139 @@ +/** + * @file query/engine/eval_member.c + * @brief Component member evaluation. + */ + +#include "../../private_api.h" + +static +bool flecs_query_member_cmp( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx, + bool neq) +{ + ecs_table_range_t range; + if (op->other) { + ecs_var_id_t table_var = flecs_itovar(op->other - 1); + range = flecs_query_var_get_range(table_var, ctx); + } else { + range = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + } + + ecs_table_t *table = range.table; + if (!table) { + return false; + } + + ecs_query_membereq_ctx_t *op_ctx = flecs_op_ctx(ctx, membereq); + ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; + + if (!range.count) { + range.count = ecs_table_count(range.table); + } + + int32_t row, end = range.count; + if (end) { + end += range.offset; + } else { + end = ecs_table_count(range.table); + } + + void *data; + if (!redo) { + row = op_ctx->each.row = range.offset; + + /* Get data ptr starting from offset 0 so we can use row to index */ + range.offset = 0; + + /* Populate data field so we have the array we can compare the member + * value against. */ + data = op_ctx->data = + ecs_table_get_column(range.table, it->trs[field_index]->column, 0); + + it->ids[field_index] = ctx->query->pub.terms[op->term_index].id; + } else { + row = ++ op_ctx->each.row; + if (op_ctx->each.row >= end) { + return false; + } + + data = op_ctx->data; + } + + int32_t offset = (int32_t)op->first.entity; + int32_t size = (int32_t)(op->first.entity >> 32); + const ecs_entity_t *entities = ecs_table_entities(table); + ecs_entity_t e = 0; + ecs_entity_t *val; + + ecs_assert(row < ecs_table_count(table), ECS_INTERNAL_ERROR, NULL); + ecs_assert(data != NULL, ECS_INTERNAL_ERROR, NULL); /* Must be written */ + ecs_assert(entities != NULL, ECS_INTERNAL_ERROR, NULL); + + bool second_written = true; + if (op->flags & (EcsQueryIsVar << EcsQuerySecond)) { + uint64_t written = ctx->written[ctx->op_index]; + second_written = written & (1ull << op->second.var); + } + + if (second_written) { + ecs_flags16_t second_flags = flecs_query_ref_flags( + op->flags, EcsQuerySecond); + ecs_entity_t second = flecs_get_ref_entity( + &op->second, second_flags, ctx); + + do { + e = entities[row]; + + val = ECS_OFFSET(ECS_ELEM(data, size, row), offset); + if (val[0] == second || second == EcsWildcard) { + if (!neq) { + goto match; + } + } else { + if (neq) { + goto match; + } + } + + row ++; + } while (row < end); + + return false; + } else { + e = entities[row]; + val = ECS_OFFSET(ECS_ELEM(data, size, row), offset); + flecs_query_var_set_entity(op, op->second.var, val[0], ctx); + } + +match: + if (op->other) { + ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); + flecs_query_var_set_entity(op, op->src.var, e, ctx); + } + + ecs_entity_t mbr = ECS_PAIR_FIRST(it->ids[field_index]); + it->ids[field_index] = ecs_pair(mbr, val[0]); + + op_ctx->each.row = row; + + return true; +} + +bool flecs_query_member_eq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + return flecs_query_member_cmp(op, redo, ctx, false); +} + +bool flecs_query_member_neq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + return flecs_query_member_cmp(op, redo, ctx, true); +} diff --git a/vendors/flecs/src/query/engine/eval_pred.c b/vendors/flecs/src/query/engine/eval_pred.c new file mode 100644 index 000000000..47309d09a --- /dev/null +++ b/vendors/flecs/src/query/engine/eval_pred.c @@ -0,0 +1,308 @@ +/** + * @file query/engine/eval_pred.c + * @brief Equality predicate evaluation. + */ + +#include "../../private_api.h" + +static +const char* flecs_query_name_arg( + const ecs_query_op_t *op, + ecs_query_run_ctx_t *ctx) +{ + int8_t term_index = op->term_index; + const ecs_term_t *term = &ctx->query->pub.terms[term_index]; + return term->second.name; +} + +static +bool flecs_query_compare_range( + const ecs_table_range_t *l, + const ecs_table_range_t *r) +{ + if (l->table != r->table) { + return false; + } + + if (l->count) { + int32_t l_end = l->offset + l->count; + int32_t r_end = r->offset + r->count; + if (r->offset < l->offset) { + return false; + } + if (r_end > l_end) { + return false; + } + } else { + /* Entire table is matched */ + } + + return true; +} + +static +bool flecs_query_pred_eq_w_range( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx, + ecs_table_range_t r) +{ + if (redo) { + return false; + } + + uint64_t written = ctx->written[ctx->op_index]; + ecs_var_id_t src_var = op->src.var; + if (!(written & (1ull << src_var))) { + /* left = unknown, right = known. Assign right-hand value to left */ + ecs_var_id_t l = src_var; + ctx->vars[l].range = r; + if (r.count == 1) { + ctx->vars[l].entity = ecs_table_entities(r.table)[r.offset]; + } + return true; + } else { + ecs_table_range_t l = flecs_query_get_range( + op, &op->src, EcsQuerySrc, ctx); + + if (!flecs_query_compare_range(&l, &r)) { + return false; + } + + ctx->vars[src_var].range.offset = r.offset; + ctx->vars[src_var].range.count = r.count; + return true; + } +} + +bool flecs_query_pred_eq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; (void)written; + ecs_assert(flecs_ref_is_written(op, &op->second, EcsQuerySecond, written), + ECS_INTERNAL_ERROR, + "invalid instruction sequence: uninitialized eq operand"); + + ecs_table_range_t r = flecs_query_get_range( + op, &op->second, EcsQuerySecond, ctx); + return flecs_query_pred_eq_w_range(op, redo, ctx, r); +} + +bool flecs_query_pred_eq_name( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + const char *name = flecs_query_name_arg(op, ctx); + ecs_entity_t e = ecs_lookup(ctx->world, name); + if (!e) { + /* Entity doesn't exist */ + return false; + } + + ecs_table_range_t r = flecs_range_from_entity(e, ctx); + return flecs_query_pred_eq_w_range(op, redo, ctx, r); +} + +bool flecs_query_pred_neq_w_range( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx, + ecs_table_range_t r) +{ + ecs_query_eq_ctx_t *op_ctx = flecs_op_ctx(ctx, eq); + ecs_var_id_t src_var = op->src.var; + ecs_table_range_t l = flecs_query_get_range( + op, &op->src, EcsQuerySrc, ctx); + + /* If tables don't match, neq always returns once */ + if (l.table != r.table) { + return true && !redo; + } + + int32_t l_offset; + int32_t l_count; + if (!redo) { + /* Make sure we're working with the correct table count */ + if (!l.count && l.table) { + l.count = ecs_table_count(l.table); + } + + l_offset = l.offset; + l_count = l.count; + + /* Cache old value */ + op_ctx->range = l; + } else { + l_offset = op_ctx->range.offset; + l_count = op_ctx->range.count; + } + + /* If the table matches, a Neq returns twice: once for the slice before the + * excluded slice, once for the slice after the excluded slice. If the right + * hand range starts & overlaps with the left hand range, there is only + * one slice. */ + ecs_var_t *var = &ctx->vars[src_var]; + if (!redo && r.offset > l_offset) { + int32_t end = r.offset; + if (end > l_count) { + end = l_count; + } + + /* Return first slice */ + var->range.table = l.table; + var->range.offset = l_offset; + var->range.count = end - l_offset; + op_ctx->redo = false; + return true; + } else if (!op_ctx->redo) { + int32_t l_end = op_ctx->range.offset + l_count; + int32_t r_end = r.offset + r.count; + + if (l_end <= r_end) { + /* If end of existing range falls inside the excluded range, there's + * nothing more to return */ + var->range = l; + return false; + } + + /* Return second slice */ + var->range.table = l.table; + var->range.offset = r_end; + var->range.count = l_end - r_end; + + /* Flag so we know we're done the next redo */ + op_ctx->redo = true; + return true; + } else { + /* Restore previous value */ + var->range = l; + return false; + } +} + +static +bool flecs_query_pred_match( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx, + bool is_neq) +{ + ecs_query_eq_ctx_t *op_ctx = flecs_op_ctx(ctx, eq); + uint64_t written = ctx->written[ctx->op_index]; + ecs_assert(flecs_ref_is_written(op, &op->src, EcsQuerySrc, written), + ECS_INTERNAL_ERROR, + "invalid instruction sequence: uninitialized match operand"); + (void)written; + + ecs_var_id_t src_var = op->src.var; + const char *match = flecs_query_name_arg(op, ctx); + ecs_table_range_t l; + if (!redo) { + l = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + if (!l.table) { + return false; + } + + if (!l.count) { + l.count = ecs_table_count(l.table); + } + + op_ctx->range = l; + op_ctx->index = l.offset; + op_ctx->name_col = flecs_ito(int16_t, + ecs_table_get_type_index(ctx->world, l.table, + ecs_pair(ecs_id(EcsIdentifier), EcsName))); + if (op_ctx->name_col == -1) { + return is_neq; + } + op_ctx->name_col = flecs_ito(int16_t, + l.table->column_map[op_ctx->name_col]); + ecs_assert(op_ctx->name_col != -1, ECS_INTERNAL_ERROR, NULL); + } else { + if (op_ctx->name_col == -1) { + /* Table has no name */ + return false; + } + + l = op_ctx->range; + } + + const EcsIdentifier *names = l.table->data.columns[op_ctx->name_col].data; + int32_t count = l.offset + l.count, offset = -1; + for (; op_ctx->index < count; op_ctx->index ++) { + const char *name = names[op_ctx->index].value; + bool result = strstr(name, match); + if (is_neq) { + result = !result; + } + + if (!result) { + if (offset != -1) { + break; + } + } else { + if (offset == -1) { + offset = op_ctx->index; + } + } + } + + if (offset == -1) { + ctx->vars[src_var].range = op_ctx->range; + return false; + } + + ctx->vars[src_var].range.offset = offset; + ctx->vars[src_var].range.count = (op_ctx->index - offset); + return true; +} + +bool flecs_query_pred_eq_match( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + return flecs_query_pred_match(op, redo, ctx, false); +} + +bool flecs_query_pred_neq_match( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + return flecs_query_pred_match(op, redo, ctx, true); +} + +bool flecs_query_pred_neq( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; (void)written; + ecs_assert(flecs_ref_is_written(op, &op->second, EcsQuerySecond, written), + ECS_INTERNAL_ERROR, + "invalid instruction sequence: uninitialized neq operand"); + + ecs_table_range_t r = flecs_query_get_range( + op, &op->second, EcsQuerySecond, ctx); + return flecs_query_pred_neq_w_range(op, redo, ctx, r); +} + +bool flecs_query_pred_neq_name( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + const char *name = flecs_query_name_arg(op, ctx); + ecs_entity_t e = ecs_lookup(ctx->world, name); + if (!e) { + /* Entity doesn't exist */ + return true && !redo; + } + + ecs_table_range_t r = flecs_range_from_entity(e, ctx); + return flecs_query_pred_neq_w_range(op, redo, ctx, r); +} diff --git a/vendors/flecs/src/query/engine/eval_toggle.c b/vendors/flecs/src/query/engine/eval_toggle.c new file mode 100644 index 000000000..7da9f72fe --- /dev/null +++ b/vendors/flecs/src/query/engine/eval_toggle.c @@ -0,0 +1,333 @@ +/** + * @file query/engine/eval_toggle.c + * @brief Bitset toggle evaluation. + */ + +#include "../../private_api.h" + +typedef struct { + ecs_flags64_t mask; + bool has_bitset; +} flecs_query_row_mask_t; + +static +flecs_query_row_mask_t flecs_query_get_row_mask( + ecs_iter_t *it, + ecs_table_t *table, + int32_t block_index, + ecs_flags64_t and_fields, + ecs_flags64_t not_fields, + ecs_query_toggle_ctx_t *op_ctx) +{ + ecs_flags64_t mask = UINT64_MAX; + int32_t i, field_count = it->field_count; + ecs_flags64_t fields = and_fields | not_fields; + bool has_bitset = false; + + for (i = 0; i < field_count; i ++) { + uint64_t field_bit = 1llu << i; + if (!(fields & field_bit)) { + continue; + } + + if (not_fields & field_bit) { + it->set_fields &= (ecs_termset_t)~field_bit; + } else if (and_fields & field_bit) { + ecs_assert(it->set_fields & field_bit, ECS_INTERNAL_ERROR, NULL); + } else { + ecs_abort(ECS_INTERNAL_ERROR, NULL); + } + + ecs_id_t id = it->ids[i]; + ecs_bitset_t *bs = flecs_table_get_toggle(table, id); + if (!bs) { + if (not_fields & field_bit) { + if (op_ctx->prev_set_fields & field_bit) { + has_bitset = false; + break; + } + } + continue; + } + + ecs_assert((64 * block_index) < bs->size, ECS_INTERNAL_ERROR, NULL); + ecs_flags64_t block = bs->data[block_index]; + + if (not_fields & field_bit) { + block = ~block; + } + mask &= block; + has_bitset = true; + } + + return (flecs_query_row_mask_t){ mask, has_bitset }; +} + +static +bool flecs_query_toggle_for_up( + ecs_iter_t *it, + ecs_flags64_t and_fields, + ecs_flags64_t not_fields) +{ + int32_t i, field_count = it->field_count; + ecs_flags64_t fields = (and_fields | not_fields) & it->up_fields; + + for (i = 0; i < field_count; i ++) { + uint64_t field_bit = 1llu << i; + if (!(fields & field_bit)) { + continue; + } + + bool match = false; + if ((it->set_fields & field_bit)) { + ecs_entity_t src = it->sources[i]; + ecs_assert(src != 0, ECS_INTERNAL_ERROR, NULL); + match = ecs_is_enabled_id(it->world, src, it->ids[i]); + } + + if (field_bit & not_fields) { + match = !match; + } + + if (!match) { + return false; + } + } + + return true; +} + +static +bool flecs_query_toggle_cmp( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx, + ecs_flags64_t and_fields, + ecs_flags64_t not_fields) +{ + ecs_iter_t *it = ctx->it; + ecs_query_toggle_ctx_t *op_ctx = flecs_op_ctx(ctx, toggle); + ecs_table_range_t range = flecs_query_get_range( + op, &op->src, EcsQuerySrc, ctx); + ecs_table_t *table = range.table; + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + + if ((and_fields & op_ctx->prev_set_fields) != and_fields) { + /* If not all fields matching and toggles are set, table can't match */ + return false; + } + + ecs_flags32_t up_fields = it->up_fields; + if (!redo) { + if (up_fields & (and_fields|not_fields)) { + /* If there are toggle fields that were matched with query + * traversal, evaluate those separately. */ + if (!flecs_query_toggle_for_up(it, and_fields, not_fields)) { + return false; + } + + it->set_fields &= (ecs_termset_t)~(not_fields & up_fields); + } + } + + /* Shared fields are evaluated, can be ignored from now on */ + // and_fields &= ~up_fields; + not_fields &= ~up_fields; + + if (!(table->flags & EcsTableHasToggle)) { + if (not_fields) { + /* If any of the toggle fields with a not operator are for fields + * that are set, without a bitset those fields can't match. */ + return false; + } else { + /* If table doesn't have toggles but query matched toggleable + * components, all entities match. */ + if (!redo) { + return true; + } else { + return false; + } + } + } + + if (table && !range.count) { + range.count = ecs_table_count(table); + if (!range.count) { + return false; + } + } + + int32_t i, j; + int32_t first, last, block_index, cur; + uint64_t block = 0; + if (!redo) { + op_ctx->range = range; + cur = op_ctx->cur = range.offset; + block_index = op_ctx->block_index = -1; + first = range.offset; + last = range.offset + range.count; + } else { + if (!op_ctx->has_bitset) { + goto done; + } + + last = op_ctx->range.offset + op_ctx->range.count; + cur = op_ctx->cur; + ecs_assert(cur <= last, ECS_INTERNAL_ERROR, NULL); + if (cur == last) { + goto done; + } + + first = cur; + block_index = op_ctx->block_index; + block = op_ctx->block; + } + + /* If end of last iteration is start of new block, compute new block */ + int32_t new_block_index = cur / 64, row = first; + if (new_block_index != block_index) { +compute_block: + block_index = op_ctx->block_index = new_block_index; + + flecs_query_row_mask_t row_mask = flecs_query_get_row_mask( + it, table, block_index, and_fields, not_fields, op_ctx); + + /* If table doesn't have bitset columns, all columns match */ + if (!(op_ctx->has_bitset = row_mask.has_bitset)) { + if (!not_fields) { + return true; + } else { + goto done; + } + } + + /* No enabled bits */ + block = row_mask.mask; + if (!block) { +next_block: + new_block_index ++; + cur = new_block_index * 64; + if (cur >= last) { + /* No more rows */ + goto done; + } + + op_ctx->cur = cur; + goto compute_block; + } + + op_ctx->block = block; + } + + /* Find first enabled bit (TODO: use faster bitmagic) */ + int32_t first_bit = cur - (block_index * 64); + int32_t last_bit = ECS_MIN(64, last - (block_index * 64)); + ecs_assert(first_bit >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(first_bit < 64, ECS_INTERNAL_ERROR, NULL); + ecs_assert(last_bit >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(last_bit <= 64, ECS_INTERNAL_ERROR, NULL); + ecs_assert(last_bit >= first_bit, ECS_INTERNAL_ERROR, NULL); + + for (i = first_bit; i < last_bit; i ++) { + uint64_t bit = (1ull << i); + bool cond = 0 != (block & bit); + if (cond) { + /* Find last enabled bit */ + for (j = i; j < last_bit; j ++) { + bit = (1ull << j); + cond = !(block & bit); + if (cond) { + break; + } + } + + row = i + (block_index * 64); + cur = j + (block_index * 64); + break; + } + } + + if (i == last_bit) { + goto next_block; + } + + ecs_assert(row >= first, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur <= last, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur >= first, ECS_INTERNAL_ERROR, NULL); + + if (!(cur - row)) { + goto done; + } + + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, row, cur - row, ctx); + } + op_ctx->cur = cur; + + return true; + +done: + /* Restore range & set fields */ + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, + table, op_ctx->range.offset, op_ctx->range.count, ctx); + } + + it->set_fields = op_ctx->prev_set_fields; + return false; +} + +bool flecs_query_toggle( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + ecs_iter_t *it = ctx->it; + ecs_query_toggle_ctx_t *op_ctx = flecs_op_ctx(ctx, toggle); + if (!redo) { + op_ctx->prev_set_fields = it->set_fields; + } + + ecs_flags64_t and_fields = op->first.entity; + ecs_flags64_t not_fields = op->second.entity & op_ctx->prev_set_fields; + + return flecs_query_toggle_cmp( + op, redo, ctx, and_fields, not_fields); +} + +bool flecs_query_toggle_option( + const ecs_query_op_t *op, + bool redo, + ecs_query_run_ctx_t *ctx) +{ + ecs_iter_t *it = ctx->it; + ecs_query_toggle_ctx_t *op_ctx = flecs_op_ctx(ctx, toggle); + if (!redo) { + op_ctx->prev_set_fields = it->set_fields; + op_ctx->optional_not = false; + op_ctx->has_bitset = false; + } + +repeat: {} + ecs_flags64_t and_fields = 0, not_fields = 0; + if (op_ctx->optional_not) { + not_fields = op->first.entity & op_ctx->prev_set_fields; + } else { + and_fields = op->first.entity; + } + + bool result = flecs_query_toggle_cmp( + op, redo, ctx, and_fields, not_fields); + if (!result) { + if (!op_ctx->optional_not) { + /* Run the not-branch of optional fields */ + op_ctx->optional_not = true; + it->set_fields = op_ctx->prev_set_fields; + redo = false; + goto repeat; + } + } + + return result; +} + diff --git a/vendors/flecs/src/query/engine/eval_trav.c b/vendors/flecs/src/query/engine/eval_trav.c new file mode 100644 index 000000000..342c50d18 --- /dev/null +++ b/vendors/flecs/src/query/engine/eval_trav.c @@ -0,0 +1,289 @@ +/** + * @file query/engine/eval_trav.c + * @brief Transitive/reflexive relationship traversal. + */ + +#include "../../private_api.h" + +static +bool flecs_query_trav_fixed_src_reflexive( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx, + ecs_table_range_t *range, + ecs_entity_t trav, + ecs_entity_t second) +{ + ecs_table_t *table = range->table; + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_entity_t *entities = ecs_table_entities(table); + int32_t count = range->count; + if (!count) { + count = ecs_table_count(table); + } + + int32_t i = range->offset, end = i + count; + for (; i < end; i ++) { + if (entities[i] == second) { + /* Even though table doesn't have the specific relationship + * pair, the relationship is reflexive and the target entity + * is stored in the table. */ + break; + } + } + if (i == end) { + /* Table didn't contain target entity */ + return false; + } + if (count > 1) { + /* If the range contains more than one entity, set the range to + * return only the entity matched by the reflexive property. */ + ecs_assert(flecs_query_ref_flags(op->flags, EcsQuerySrc) & EcsQueryIsVar, + ECS_INTERNAL_ERROR, NULL); + ecs_var_t *var = &ctx->vars[op->src.var]; + ecs_table_range_t *var_range = &var->range; + var_range->offset = i; + var_range->count = 1; + var->entity = entities[i]; + } + + flecs_query_set_trav_match(op, NULL, trav, second, ctx); + return true; +} + +static +bool flecs_query_trav_unknown_src_reflexive( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx, + ecs_entity_t trav, + ecs_entity_t second) +{ + ecs_assert(flecs_query_ref_flags(op->flags, EcsQuerySrc) & EcsQueryIsVar, + ECS_INTERNAL_ERROR, NULL); + ecs_var_id_t src_var = op->src.var; + flecs_query_var_set_entity(op, src_var, second, ctx); + flecs_query_var_get_table(src_var, ctx); + + ecs_table_t *table = ctx->vars[src_var].range.table; + if (table) { + if (flecs_query_table_filter(table, op->other, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) + { + return false; + } + } + + flecs_query_set_trav_match(op, NULL, trav, second, ctx); + return true; +} + +static +bool flecs_query_trav_fixed_src_up_fixed_second( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + if (redo) { + return false; /* If everything's fixed, can only have a single result */ + } + + ecs_flags16_t f_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t f_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); + ecs_flags16_t f_src = flecs_query_ref_flags(op->flags, EcsQuerySrc); + ecs_entity_t trav = flecs_get_ref_entity(&op->first, f_1st, ctx); + ecs_entity_t second = flecs_get_ref_entity(&op->second, f_2nd, ctx); + ecs_table_range_t range = flecs_get_ref_range(&op->src, f_src, ctx); + ecs_table_t *table = range.table; + + /* 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); + + if (!tr) { + if (op->match_flags & EcsTermReflexive) { + return flecs_query_trav_fixed_src_reflexive(op, ctx, + &range, trav, second); + } else { + return false; + } + } + + flecs_query_set_trav_match(op, tr, trav, second, ctx); + return true; +} + +static +bool flecs_query_trav_unknown_src_up_fixed_second( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_flags16_t f_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t f_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); + ecs_entity_t trav = flecs_get_ref_entity(&op->first, f_1st, ctx); + ecs_entity_t second = flecs_get_ref_entity(&op->second, f_2nd, ctx); + ecs_query_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); + + if (!redo) { + ecs_record_t *r_second = flecs_entities_get(ctx->world, second); + bool traversable = r_second && r_second->row & EcsEntityIsTraversable; + bool reflexive = op->match_flags & EcsTermReflexive; + if (!traversable && !reflexive) { + trav_ctx->cache.id = 0; + + /* If there's no record for the entity, it can't have a subtree so + * forward operation to a regular select. */ + return flecs_query_select(op, redo, ctx); + } + + /* Entity is traversable, which means it could have a subtree */ + flecs_query_get_trav_down_cache(ctx, &trav_ctx->cache, trav, second); + trav_ctx->index = 0; + + if (op->match_flags & EcsTermReflexive) { + trav_ctx->index = -1; + if(flecs_query_trav_unknown_src_reflexive( + op, ctx, trav, second)) + { + /* It's possible that we couldn't return the entity required for + * reflexive matching, like when it's a prefab or disabled. */ + return true; + } + } + } else { + if (!trav_ctx->cache.id) { + /* No traversal cache, which means this is a regular select */ + return flecs_query_select(op, redo, ctx); + } + } + + if (trav_ctx->index == -1) { + redo = false; /* First result after handling reflexive relationship */ + trav_ctx->index = 0; + } + + /* Forward to select */ + int32_t count = ecs_vec_count(&trav_ctx->cache.entities); + ecs_trav_elem_t *elems = ecs_vec_first(&trav_ctx->cache.entities); + for (; trav_ctx->index < count; trav_ctx->index ++) { + ecs_trav_elem_t *el = &elems[trav_ctx->index]; + trav_ctx->and.idr = el->idr; /* prevents lookup by select */ + if (flecs_query_select_w_id(op, redo, ctx, ecs_pair(trav, el->entity), + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) + { + return true; + } + + redo = false; + } + + return false; +} + +static +bool flecs_query_trav_yield_reflexive_src( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx, + ecs_table_range_t *range, + ecs_entity_t trav) +{ + ecs_var_t *vars = ctx->vars; + ecs_query_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); + int32_t offset = trav_ctx->offset, count = trav_ctx->count; + bool src_is_var = op->flags & (EcsQueryIsVar << EcsQuerySrc); + + if (trav_ctx->index >= (offset + count)) { + /* Restore previous offset, count */ + if (src_is_var) { + ecs_var_id_t src_var = op->src.var; + vars[src_var].range.offset = offset; + vars[src_var].range.count = count; + vars[src_var].entity = 0; + } + return false; + } + + ecs_entity_t entity = ecs_table_entities(range->table)[trav_ctx->index]; + flecs_query_set_trav_match(op, NULL, trav, entity, ctx); + + /* Hijack existing variable to return one result at a time */ + if (src_is_var) { + ecs_var_id_t src_var = op->src.var; + ecs_table_t *table = vars[src_var].range.table; + ecs_assert(!table || table == ecs_get_table(ctx->world, entity), + ECS_INTERNAL_ERROR, NULL); + (void)table; + vars[src_var].entity = entity; + vars[src_var].range = flecs_range_from_entity(entity, ctx); + } + + return true; +} + +static +bool flecs_query_trav_fixed_src_up_unknown_second( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_flags16_t f_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t f_src = flecs_query_ref_flags(op->flags, EcsQuerySrc); + ecs_entity_t trav = flecs_get_ref_entity(&op->first, f_1st, ctx); + ecs_table_range_t range = flecs_get_ref_range(&op->src, f_src, ctx); + ecs_table_t *table = range.table; + ecs_query_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); + + if (!redo) { + flecs_query_get_trav_up_cache(ctx, &trav_ctx->cache, trav, table); + trav_ctx->index = 0; + if (op->match_flags & EcsTermReflexive) { + trav_ctx->yield_reflexive = true; + trav_ctx->index = range.offset; + trav_ctx->offset = range.offset; + trav_ctx->count = range.count ? range.count : ecs_table_count(table); + } + } else { + trav_ctx->index ++; + } + + if (trav_ctx->yield_reflexive) { + if (flecs_query_trav_yield_reflexive_src(op, ctx, &range, trav)) { + return true; + } + trav_ctx->yield_reflexive = false; + trav_ctx->index = 0; + } + + if (trav_ctx->index >= ecs_vec_count(&trav_ctx->cache.entities)) { + return false; + } + + ecs_trav_elem_t *el = ecs_vec_get_t( + &trav_ctx->cache.entities, ecs_trav_elem_t, trav_ctx->index); + flecs_query_set_trav_match(op, el->tr, trav, el->entity, ctx); + return true; +} + +bool flecs_query_trav( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + + if (!flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + if (!flecs_ref_is_written(op, &op->second, EcsQuerySecond, written)) { + /* This can't happen, src or second should have been resolved */ + ecs_abort(ECS_INTERNAL_ERROR, + "invalid instruction sequence: unconstrained traversal"); + } else { + return flecs_query_trav_unknown_src_up_fixed_second(op, redo, ctx); + } + } else { + if (!flecs_ref_is_written(op, &op->second, EcsQuerySecond, written)) { + return flecs_query_trav_fixed_src_up_unknown_second(op, redo, ctx); + } else { + return flecs_query_trav_fixed_src_up_fixed_second(op, redo, ctx); + } + } +} diff --git a/vendors/flecs/src/query/engine/eval_union.c b/vendors/flecs/src/query/engine/eval_union.c new file mode 100644 index 000000000..2bc95f927 --- /dev/null +++ b/vendors/flecs/src/query/engine/eval_union.c @@ -0,0 +1,381 @@ +/** + * @file query/engine/eval_union.c + * @brief Union relationship evaluation. + */ + +#include "../../private_api.h" + +static +bool flecs_query_union_with_wildcard( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_entity_t rel, + bool neq) +{ + ecs_query_union_ctx_t *op_ctx = flecs_op_ctx(ctx, union_); + ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; + + ecs_table_range_t range; + ecs_table_t *table; + if (!redo) { + range = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + table = range.table; + if (!range.count) { + range.count = ecs_table_count(table); + } + + op_ctx->range = range; + op_ctx->idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + if (!op_ctx->idr) { + return neq; + } + + if (neq) { + if (flecs_id_record_get_table(op_ctx->idr, table) != NULL) { + /* If table has (R, Union) none match !(R, _) */ + return false; + } else { + /* If table doesn't have (R, Union) all match !(R, _) */ + return true; + } + } + + op_ctx->row = 0; + } else { + if (neq) { + /* !(R, _) terms only can have a single result */ + return false; + } + + range = op_ctx->range; + table = range.table; + op_ctx->row ++; + } + +next_row: + if (op_ctx->row >= range.count) { + /* Restore range */ + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, + op_ctx->range.offset, op_ctx->range.count, ctx); + } + return false; + } + + ecs_entity_t e = ecs_table_entities(range.table) + [range.offset + op_ctx->row]; + ecs_entity_t tgt = flecs_switch_get(op_ctx->idr->sparse, (uint32_t)e); + if (!tgt) { + op_ctx->row ++; + goto next_row; + } + + it->ids[field_index] = ecs_pair(rel, tgt); + + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, + range.offset + op_ctx->row, 1, ctx); + } + flecs_query_set_vars(op, it->ids[field_index], ctx); + + return true; +} + +static +bool flecs_query_union_with_tgt( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_entity_t rel, + ecs_entity_t tgt, + bool neq) +{ + ecs_query_union_ctx_t *op_ctx = flecs_op_ctx(ctx, union_); + ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; + + ecs_table_range_t range; + ecs_table_t *table; + if (!redo) { + range = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + table = range.table; + if (!range.count) { + range.count = ecs_table_count(table); + } + + op_ctx->range = range; + op_ctx->idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + if (!op_ctx->idr) { + return false; + } + + op_ctx->row = 0; + } else { + range = op_ctx->range; + table = range.table; + op_ctx->row ++; + } + +next_row: + if (op_ctx->row >= range.count) { + /* Restore range */ + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, + op_ctx->range.offset, op_ctx->range.count, ctx); + } + return false; + } + + ecs_entity_t e = ecs_table_entities(range.table) + [range.offset + op_ctx->row]; + ecs_entity_t e_tgt = flecs_switch_get(op_ctx->idr->sparse, (uint32_t)e); + bool match = e_tgt == tgt; + if (neq) { + match = !match; + } + + if (!match) { + op_ctx->row ++; + goto next_row; + } + + it->ids[field_index] = ecs_pair(rel, tgt); + + if (op->flags & (EcsQueryIsVar << EcsQuerySrc)) { + flecs_query_var_narrow_range(op->src.var, table, + range.offset + op_ctx->row, 1, ctx); + } + + flecs_query_set_vars(op, it->ids[field_index], ctx); + + return true; +} + +bool flecs_query_union_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + bool neq) +{ + ecs_id_t id = flecs_query_op_get_id(op, ctx); + ecs_entity_t rel = ECS_PAIR_FIRST(id); + ecs_entity_t tgt = ecs_pair_second(ctx->world, id); + + if (tgt == EcsWildcard) { + return flecs_query_union_with_wildcard(op, redo, ctx, rel, neq); + } else { + return flecs_query_union_with_tgt(op, redo, ctx, rel, tgt, neq); + } +} + +static +bool flecs_query_union_select_tgt( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_entity_t rel, + ecs_entity_t tgt) +{ + ecs_query_union_ctx_t *op_ctx = flecs_op_ctx(ctx, union_); + ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; + + if (!redo) { + op_ctx->idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + if (!op_ctx->idr) { + return false; + } + + op_ctx->cur = flecs_switch_first(op_ctx->idr->sparse, tgt); + } else { + op_ctx->cur = flecs_switch_next(op_ctx->idr->sparse, (uint32_t)op_ctx->cur); + } + + if (!op_ctx->cur) { + return false; + } + + ecs_table_range_t range = flecs_range_from_entity(op_ctx->cur, ctx); + flecs_query_var_set_range(op, op->src.var, + range.table, range.offset, range.count, ctx); + flecs_query_set_vars(op, it->ids[field_index], ctx); + + it->ids[field_index] = ecs_pair(rel, tgt); + + return true; +} + +static +bool flecs_query_union_select_wildcard( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_entity_t rel) +{ + ecs_query_union_ctx_t *op_ctx = flecs_op_ctx(ctx, union_); + ecs_iter_t *it = ctx->it; + int8_t field_index = op->field_index; + + if (!redo) { + op_ctx->idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + if (!op_ctx->idr) { + return false; + } + + op_ctx->tgt_iter = flecs_switch_targets(op_ctx->idr->sparse); + op_ctx->tgt = 0; + } + +next_tgt: + if (!op_ctx->tgt) { + if (!ecs_map_next(&op_ctx->tgt_iter)) { + return false; + } + + op_ctx->tgt = ecs_map_key(&op_ctx->tgt_iter); + op_ctx->cur = 0; + it->ids[field_index] = ecs_pair(rel, op_ctx->tgt); + } + + if (!op_ctx->cur) { + op_ctx->cur = flecs_switch_first(op_ctx->idr->sparse, op_ctx->tgt); + } else { + op_ctx->cur = flecs_switch_next(op_ctx->idr->sparse, (uint32_t)op_ctx->cur); + } + + if (!op_ctx->cur) { + op_ctx->tgt = 0; + goto next_tgt; + } + + ecs_table_range_t range = flecs_range_from_entity(op_ctx->cur, ctx); + flecs_query_var_set_range(op, op->src.var, + range.table, range.offset, range.count, ctx); + flecs_query_set_vars(op, it->ids[field_index], ctx); + + return true; +} + +bool flecs_query_union_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + ecs_id_t id = flecs_query_op_get_id(op, ctx); + ecs_entity_t rel = ECS_PAIR_FIRST(id); + ecs_entity_t tgt = ecs_pair_second(ctx->world, id); + + if (tgt == EcsWildcard) { + return flecs_query_union_select_wildcard(op, redo, ctx, rel); + } else { + return flecs_query_union_select_tgt(op, redo, ctx, rel, tgt); + } +} + +bool flecs_query_union( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (written & (1ull << op->src.var)) { + return flecs_query_union_with(op, redo, ctx, false); + } else { + return flecs_query_union_select(op, redo, ctx); + } +} + +bool flecs_query_union_neq( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (written & (1ull << op->src.var)) { + return flecs_query_union_with(op, redo, ctx, true); + } else { + return false; + } +} + +static +void flecs_query_union_set_shared( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx) +{ + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_id_record_t *idr = op_ctx->idr_with; + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_entity_t rel = ECS_PAIR_FIRST(idr->id); + idr = flecs_id_record_get(ctx->world, ecs_pair(rel, EcsUnion)); + ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(idr->sparse != NULL, ECS_INTERNAL_ERROR, NULL); + + int8_t field_index = op->field_index; + ecs_iter_t *it = ctx->it; + ecs_entity_t src = it->sources[field_index]; + ecs_entity_t tgt = flecs_switch_get(idr->sparse, (uint32_t)src); + + it->ids[field_index] = ecs_pair(rel, tgt); +} + +bool flecs_query_union_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + if (!redo) { + if (!flecs_query_up_with(op, redo, ctx)) { + return false; + } + + flecs_query_union_set_shared(op, ctx); + return true; + } else { + return false; + } + } else { + return flecs_query_up_select(op, redo, ctx, + FlecsQueryUpSelectUp, FlecsQueryUpSelectUnion); + } +} + +bool flecs_query_union_self_up( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsQuerySrc, written)) { + if (redo) { + goto next_for_union; + } + +next_for_self_up_with: + if (!flecs_query_self_up_with(op, redo, ctx, false)) { + return false; + } + + int8_t field_index = op->field_index; + ecs_iter_t *it = ctx->it; + if (it->sources[field_index]) { + flecs_query_union_set_shared(op, ctx); + return true; + } + +next_for_union: + if (!flecs_query_union_with(op, redo, ctx, false)) { + goto next_for_self_up_with; + } + + return true; + } else { + return flecs_query_up_select(op, redo, ctx, + FlecsQueryUpSelectSelfUp, FlecsQueryUpSelectUnion); + } +} diff --git a/vendors/flecs/src/query/engine/eval_up.c b/vendors/flecs/src/query/engine/eval_up.c new file mode 100644 index 000000000..571be94c0 --- /dev/null +++ b/vendors/flecs/src/query/engine/eval_up.c @@ -0,0 +1,367 @@ +/** + * @file query/engine/eval.c + * @brief Query engine implementation. + */ + +#include "../../private_api.h" + +/* Find tables with requested component that has traversable entities. */ +static +bool flecs_query_up_select_table( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_query_up_select_trav_kind_t trav_kind, + ecs_query_up_select_kind_t kind) +{ + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_iter_t *it = ctx->it; + bool self = trav_kind == FlecsQueryUpSelectSelfUp; + ecs_table_range_t range; + + do { + bool result; + if (kind == FlecsQueryUpSelectId) { + result = flecs_query_select_id(op, redo, ctx, 0); + } else if (kind == FlecsQueryUpSelectDefault) { + result = flecs_query_select_w_id(op, redo, ctx, + op_ctx->with, 0); + } else if (kind == FlecsQueryUpSelectUnion) { + result = flecs_query_union_select(op, redo, ctx); + } else { + ecs_abort(ECS_INTERNAL_ERROR, NULL); + } + + if (!result) { + /* No remaining tables with component found. */ + return false; + } + + redo = true; + + range = flecs_query_get_range(op, &op->src, EcsQuerySrc, ctx); + ecs_assert(range.table != NULL, ECS_INTERNAL_ERROR, NULL); + + /* Keep searching until we find a table that has the requested component, + * with traversable entities */ + } while (!self && range.table->_->traversable_count == 0); + + if (!range.count) { + range.count = ecs_table_count(range.table); + } + + op_ctx->table = range.table; + op_ctx->row = range.offset; + op_ctx->end = range.offset + range.count; + op_ctx->matched = it->ids[op->field_index]; + + return true; +} + +/* Find next traversable entity in table. */ +static +ecs_trav_down_t* flecs_query_up_find_next_traversable( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx, + ecs_query_up_select_trav_kind_t trav_kind) +{ + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_world_t *world = ctx->world; + ecs_iter_t *it = ctx->it; + const ecs_query_t *q = &ctx->query->pub; + ecs_table_t *table = op_ctx->table; + bool self = trav_kind == FlecsQueryUpSelectSelfUp; + + if (table->_->traversable_count == 0) { + /* No traversable entities in table */ + op_ctx->table = NULL; + return NULL; + } else { + int32_t row; + ecs_entity_t entity = 0; + const ecs_entity_t *entities = ecs_table_entities(table); + + for (row = op_ctx->row; row < op_ctx->end; row ++) { + entity = entities[row]; + ecs_record_t *record = flecs_entities_get(world, entity); + if (record->row & EcsEntityIsTraversable) { + /* Found traversable entity */ + it->sources[op->field_index] = entity; + break; + } + } + + if (row == op_ctx->end) { + /* No traversable entities remaining in table */ + op_ctx->table = NULL; + return NULL; + } + + op_ctx->row = row; + + /* Get down cache entry for traversable entity */ + bool match_empty = (q->flags & EcsQueryMatchEmptyTables) != 0; + op_ctx->down = flecs_query_get_down_cache(ctx, &op_ctx->cache, + op_ctx->trav, entity, op_ctx->idr_with, self, match_empty); + op_ctx->cache_elem = -1; + } + + return op_ctx->down; +} + +/* Select all tables that can reach the target component through the traversal + * relationship. */ +bool flecs_query_up_select( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + ecs_query_up_select_trav_kind_t trav_kind, + ecs_query_up_select_kind_t kind) +{ + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_iter_t *it = ctx->it; + bool redo_select = redo; + const ecs_query_t *q = &ctx->query->pub; + bool self = trav_kind == FlecsQueryUpSelectSelfUp; + + op_ctx->trav = q->terms[op->term_index].trav; + + /* Reuse id record from previous iteration if possible*/ + if (!op_ctx->idr_trav) { + op_ctx->idr_trav = flecs_id_record_get(ctx->world, + ecs_pair(op_ctx->trav, EcsWildcard)); + } + + /* If id record is not found, or if it doesn't have any tables, revert to + * iterating owned components (no traversal) */ + if (!op_ctx->idr_trav || + !flecs_table_cache_all_count(&op_ctx->idr_trav->cache)) + { + if (!self) { + /* If operation does not match owned components, return false */ + return false; + } else if (kind == FlecsQueryUpSelectId) { + return flecs_query_select_id(op, redo, ctx, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)); + } else if (kind == FlecsQueryUpSelectDefault) { + return flecs_query_select(op, redo, ctx); + } else if (kind == FlecsQueryUpSelectUnion) { + return flecs_query_union_select(op, redo, ctx); + } else { + /* Invalid select kind */ + ecs_abort(ECS_INTERNAL_ERROR, NULL); + } + } + + if (!redo) { + /* Get component id to match */ + op_ctx->with = flecs_query_op_get_id(op, ctx); + + /* Get id record for component to match */ + op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); + if (!op_ctx->idr_with) { + /* If id record does not exist, there can't be any results */ + return false; + } + + op_ctx->down = NULL; + op_ctx->cache_elem = 0; + } + + /* Get last used entry from down traversal cache. Cache entries in the down + * traversal cache contain a list of tables that can reach the requested + * component through the traversal relationship, for a traversable entity + * which acts as the key for the cache. */ + ecs_trav_down_t *down = op_ctx->down; + +next_down_entry: + /* Get (next) entry in down traversal cache */ + while (!down) { + ecs_table_t *table = op_ctx->table; + + /* Get (next) table with traversable entities that have the + * requested component. We'll traverse downwards from the + * traversable entities in the table to find all entities that can + * reach the component through the traversal relationship. */ + if (!table) { + /* Reset source, in case we have to return a component matched + * by the entity in the found table. */ + it->sources[op->field_index] = 0; + + if (!flecs_query_up_select_table( + op, redo_select, ctx, trav_kind, kind)) + { + return false; + } + + table = op_ctx->table; + + /* If 'self' is true, we're evaluating a term with self|up. This + * means that before traversing downwards, we should also return + * the current table as result. */ + if (self) { + if (!flecs_query_table_filter(table, op->other, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) + { + flecs_reset_source_set_flag(it, op->field_index); + op_ctx->row --; + return true; + } + } + + redo_select = true; + } else { + /* Evaluate next entity in table */ + op_ctx->row ++; + } + + /* Get down cache entry for next traversable entity in table */ + down = flecs_query_up_find_next_traversable(op, ctx, trav_kind); + if (!down) { + goto next_down_entry; + } + } + +next_down_elem: + /* Get next element (table) in cache entry */ + if ((++ op_ctx->cache_elem) >= ecs_vec_count(&down->elems)) { + /* No more elements in cache entry, find next.*/ + down = NULL; + goto next_down_entry; + } + + ecs_trav_down_elem_t *elem = ecs_vec_get_t( + &down->elems, ecs_trav_down_elem_t, op_ctx->cache_elem); + flecs_query_var_set_range(op, op->src.var, elem->table, 0, 0, ctx); + flecs_query_set_vars(op, op_ctx->matched, ctx); + + if (flecs_query_table_filter(elem->table, op->other, + (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))) + { + /* Go to next table if table contains prefabs, disabled entities or + * entities that are not queryable. */ + goto next_down_elem; + } + + flecs_set_source_set_flag(it, op->field_index); + + return true; +} + +/* Check if a table can reach the target component through the traversal + * relationship. */ +bool flecs_query_up_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx) +{ + const ecs_query_t *q = &ctx->query->pub; + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_iter_t *it = ctx->it; + + op_ctx->trav = q->terms[op->term_index].trav; + if (!op_ctx->idr_trav) { + op_ctx->idr_trav = flecs_id_record_get(ctx->world, + ecs_pair(op_ctx->trav, EcsWildcard)); + } + + if (!op_ctx->idr_trav || + !flecs_table_cache_all_count(&op_ctx->idr_trav->cache)) + { + /* If there are no tables with traversable relationship, there are no + * matches. */ + return false; + } + + if (!redo) { + op_ctx->trav = q->terms[op->term_index].trav; + op_ctx->with = flecs_query_op_get_id(op, ctx); + op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); + + /* If id record for component doesn't exist, there are no matches */ + if (!op_ctx->idr_with) { + return false; + } + + /* Get the range (table) that is currently being evaluated. In most + * cases the range will cover the entire table, but in some cases it + * can only cover a subset of the entities in the table. */ + ecs_table_range_t range = flecs_query_get_range( + op, &op->src, EcsQuerySrc, ctx); + if (!range.table) { + return false; + } + + /* Get entry from up traversal cache. The up traversal cache contains + * the entity on which the component was found, with additional metadata + * on where it is stored. */ + ecs_trav_up_t *up = flecs_query_get_up_cache(ctx, &op_ctx->cache, + range.table, op_ctx->with, op_ctx->trav, op_ctx->idr_with, + op_ctx->idr_trav); + + if (!up) { + /* Component is not reachable from table */ + return false; + } + + it->sources[op->field_index] = flecs_entities_get_alive( + ctx->world, up->src); + it->trs[op->field_index] = up->tr; + it->ids[op->field_index] = up->id; + flecs_query_set_vars(op, up->id, ctx); + flecs_set_source_set_flag(it, op->field_index); + return true; + } else { + /* The table either can or can't reach the component, nothing to do for + * a second evaluation of this operation.*/ + return false; + } +} + +/* Check if a table can reach the target component through the traversal + * relationship, or if the table has the target component itself. */ +bool flecs_query_self_up_with( + const ecs_query_op_t *op, + bool redo, + const ecs_query_run_ctx_t *ctx, + bool id_only) +{ + if (!redo) { + bool result; + + if (id_only) { + /* Simple id, no wildcards */ + result = flecs_query_with_id(op, redo, ctx); + ecs_query_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); + op_ctx->remaining = 1; + } else { + result = flecs_query_with(op, redo, ctx); + } + + flecs_reset_source_set_flag(ctx->it, op->field_index); + + if (result) { + /* Table has component, no need to traverse*/ + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + op_ctx->trav = 0; + if (flecs_query_ref_flags(op->flags, EcsQuerySrc) & EcsQueryIsVar) { + /* Matching self, so set sources to 0 */ + ecs_iter_t *it = ctx->it; + it->sources[op->field_index] = 0; + } + return true; + } + + /* Table doesn't have component, traverse relationship */ + return flecs_query_up_with(op, redo, ctx); + } else { + ecs_query_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + if (op_ctx->trav == 0) { + /* If matching components without traversing, make sure to still + * match remaining components that match the id (wildcard). */ + return flecs_query_with(op, redo, ctx); + } + } + + return false; +} diff --git a/vendors/flecs/src/query/engine/eval_utils.c b/vendors/flecs/src/query/engine/eval_utils.c new file mode 100644 index 000000000..2efe03326 --- /dev/null +++ b/vendors/flecs/src/query/engine/eval_utils.c @@ -0,0 +1,410 @@ +/** + * @file query/engine/eval_utils.c + * @brief Query engine evaluation utilities. + */ + +#include "../../private_api.h" + +void flecs_query_set_iter_this( + ecs_iter_t *it, + const ecs_query_run_ctx_t *ctx) +{ + const ecs_var_t *var = &ctx->vars[0]; + const ecs_table_range_t *range = &var->range; + ecs_table_t *table = range->table; + int32_t count = range->count; + if (table) { + if (!count) { + count = ecs_table_count(table); + } + it->table = table; + it->offset = range->offset; + it->count = count; + it->entities = ecs_table_entities(table); + if (it->entities) { + it->entities += it->offset; + } + } else if (count == 1) { + it->count = 1; + it->entities = &ctx->vars[0].entity; + } +} + +ecs_query_op_ctx_t* flecs_op_ctx_( + const ecs_query_run_ctx_t *ctx) +{ + return &ctx->op_ctx[ctx->op_index]; +} + +#define flecs_op_ctx(ctx, op_kind) (&flecs_op_ctx_(ctx)->is.op_kind) + +void flecs_reset_source_set_flag( + ecs_iter_t *it, + int32_t field_index) +{ + ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); + ECS_TERMSET_CLEAR(it->up_fields, 1u << field_index); +} + +void flecs_set_source_set_flag( + ecs_iter_t *it, + int32_t field_index) +{ + ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); + ECS_TERMSET_SET(it->up_fields, 1u << field_index); +} + +ecs_table_range_t flecs_range_from_entity( + ecs_entity_t e, + const ecs_query_run_ctx_t *ctx) +{ + ecs_record_t *r = flecs_entities_get(ctx->world, e); + if (!r) { + return (ecs_table_range_t){ 0 }; + } + return (ecs_table_range_t){ + .table = r->table, + .offset = ECS_RECORD_TO_ROW(r->row), + .count = 1 + }; +} + +ecs_table_range_t flecs_query_var_get_range( + int32_t var_id, + const ecs_query_run_ctx_t *ctx) +{ + ecs_assert(var_id < ctx->query->var_count, ECS_INTERNAL_ERROR, NULL); + ecs_var_t *var = &ctx->vars[var_id]; + ecs_table_t *table = var->range.table; + if (table) { + return var->range; + } + + ecs_entity_t entity = var->entity; + if (entity && entity != EcsWildcard) { + var->range = flecs_range_from_entity(entity, ctx); + return var->range; + } + + return (ecs_table_range_t){ 0 }; +} + +ecs_table_t* flecs_query_var_get_table( + int32_t var_id, + const ecs_query_run_ctx_t *ctx) +{ + ecs_var_t *var = &ctx->vars[var_id]; + ecs_table_t *table = var->range.table; + if (table) { + return table; + } + + ecs_entity_t entity = var->entity; + if (entity && entity != EcsWildcard) { + var->range = flecs_range_from_entity(entity, ctx); + return var->range.table; + } + + return NULL; +} + +ecs_table_t* flecs_query_get_table( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + const ecs_query_run_ctx_t *ctx) +{ + ecs_flags16_t flags = flecs_query_ref_flags(op->flags, ref_kind); + if (flags & EcsQueryIsEntity) { + return ecs_get_table(ctx->world, ref->entity); + } else { + return flecs_query_var_get_table(ref->var, ctx); + } +} + +ecs_table_range_t flecs_query_get_range( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t ref_kind, + const ecs_query_run_ctx_t *ctx) +{ + ecs_flags16_t flags = flecs_query_ref_flags(op->flags, ref_kind); + if (flags & EcsQueryIsEntity) { + ecs_assert(!(flags & EcsQueryIsVar), ECS_INTERNAL_ERROR, NULL); + return flecs_range_from_entity(ref->entity, ctx); + } else { + ecs_var_t *var = &ctx->vars[ref->var]; + if (var->range.table) { + return ctx->vars[ref->var].range; + } else if (var->entity) { + return flecs_range_from_entity(var->entity, ctx); + } + } + return (ecs_table_range_t){0}; +} + +ecs_entity_t flecs_query_var_get_entity( + ecs_var_id_t var_id, + const ecs_query_run_ctx_t *ctx) +{ + ecs_assert(var_id < (ecs_var_id_t)ctx->query->var_count, + ECS_INTERNAL_ERROR, NULL); + ecs_var_t *var = &ctx->vars[var_id]; + ecs_entity_t entity = var->entity; + if (entity) { + return entity; + } + + ecs_assert(var->range.count == 1, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = var->range.table; + const ecs_entity_t *entities = ecs_table_entities(table); + var->entity = entities[var->range.offset]; + return var->entity; +} + +void flecs_query_var_reset( + ecs_var_id_t var_id, + const ecs_query_run_ctx_t *ctx) +{ + ctx->vars[var_id].entity = EcsWildcard; + ctx->vars[var_id].range.table = NULL; +} + +void flecs_query_var_set_range( + const ecs_query_op_t *op, + ecs_var_id_t var_id, + ecs_table_t *table, + int32_t offset, + int32_t count, + const ecs_query_run_ctx_t *ctx) +{ + (void)op; + ecs_assert(ctx->query_vars[var_id].kind == EcsVarTable, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_query_is_written(var_id, op->written), + ECS_INTERNAL_ERROR, NULL); + ecs_var_t *var = &ctx->vars[var_id]; + var->entity = 0; + var->range = (ecs_table_range_t){ + .table = table, + .offset = offset, + .count = count + }; +} + +void flecs_query_var_narrow_range( + ecs_var_id_t var_id, + ecs_table_t *table, + int32_t offset, + int32_t count, + const ecs_query_run_ctx_t *ctx) +{ + ecs_var_t *var = &ctx->vars[var_id]; + + var->entity = 0; + var->range = (ecs_table_range_t){ + .table = table, + .offset = offset, + .count = count + }; + + ecs_assert(var_id < ctx->query->var_count, ECS_INTERNAL_ERROR, NULL); + if (ctx->query_vars[var_id].kind != EcsVarTable) { + ecs_assert(count == 1, ECS_INTERNAL_ERROR, NULL); + var->entity = ecs_table_entities(table)[offset]; + } +} + +void flecs_query_var_set_entity( + const ecs_query_op_t *op, + ecs_var_id_t var_id, + ecs_entity_t entity, + const ecs_query_run_ctx_t *ctx) +{ + (void)op; + ecs_assert(var_id < (ecs_var_id_t)ctx->query->var_count, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(flecs_query_is_written(var_id, op->written), + ECS_INTERNAL_ERROR, NULL); + ecs_var_t *var = &ctx->vars[var_id]; + var->range.table = NULL; + var->entity = entity; +} + +void flecs_query_set_vars( + const ecs_query_op_t *op, + ecs_id_t id, + const ecs_query_run_ctx_t *ctx) +{ + ecs_flags16_t flags_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t flags_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); + + if (flags_1st & EcsQueryIsVar) { + ecs_var_id_t var = op->first.var; + if (op->written & (1ull << var)) { + if (ECS_IS_PAIR(id)) { + flecs_query_var_set_entity( + op, var, ecs_get_alive(ctx->world, ECS_PAIR_FIRST(id)), ctx); + } else { + flecs_query_var_set_entity(op, var, id, ctx); + } + } + } + + if (flags_2nd & EcsQueryIsVar) { + ecs_var_id_t var = op->second.var; + if (op->written & (1ull << var)) { + flecs_query_var_set_entity( + op, var, ecs_get_alive(ctx->world, ECS_PAIR_SECOND(id)), ctx); + } + } +} + +ecs_table_range_t flecs_get_ref_range( + const ecs_query_ref_t *ref, + ecs_flags16_t flag, + const ecs_query_run_ctx_t *ctx) +{ + if (flag & EcsQueryIsEntity) { + return flecs_range_from_entity(ref->entity, ctx); + } else if (flag & EcsQueryIsVar) { + return flecs_query_var_get_range(ref->var, ctx); + } + return (ecs_table_range_t){0}; +} + +ecs_entity_t flecs_get_ref_entity( + const ecs_query_ref_t *ref, + ecs_flags16_t flag, + const ecs_query_run_ctx_t *ctx) +{ + if (flag & EcsQueryIsEntity) { + return ref->entity; + } else if (flag & EcsQueryIsVar) { + return flecs_query_var_get_entity(ref->var, ctx); + } + return 0; +} + +ecs_id_t flecs_query_op_get_id_w_written( + const ecs_query_op_t *op, + uint64_t written, + const ecs_query_run_ctx_t *ctx) +{ + ecs_flags16_t flags_1st = flecs_query_ref_flags(op->flags, EcsQueryFirst); + ecs_flags16_t flags_2nd = flecs_query_ref_flags(op->flags, EcsQuerySecond); + ecs_entity_t first = 0, second = 0; + + if (flags_1st) { + if (flecs_ref_is_written(op, &op->first, EcsQueryFirst, written)) { + first = flecs_get_ref_entity(&op->first, flags_1st, ctx); + } else if (flags_1st & EcsQueryIsVar) { + first = EcsWildcard; + } + } + if (flags_2nd) { + if (flecs_ref_is_written(op, &op->second, EcsQuerySecond, written)) { + second = flecs_get_ref_entity(&op->second, flags_2nd, ctx); + } else if (flags_2nd & EcsQueryIsVar) { + second = EcsWildcard; + } + } + + if (flags_2nd & (EcsQueryIsVar | EcsQueryIsEntity)) { + return ecs_pair(first, second); + } else { + return flecs_entities_get_alive(ctx->world, first); + } +} + +ecs_id_t flecs_query_op_get_id( + const ecs_query_op_t *op, + const ecs_query_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + return flecs_query_op_get_id_w_written(op, written, ctx); +} + +int16_t flecs_query_next_column( + ecs_table_t *table, + ecs_id_t id, + int32_t column) +{ + if (!ECS_IS_PAIR(id) || (ECS_PAIR_FIRST(id) != EcsWildcard)) { + column = column + 1; + } else { + ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); + column = ecs_search_offset(NULL, table, column + 1, id, NULL); + ecs_assert(column != -1, ECS_INTERNAL_ERROR, NULL); + } + return flecs_ito(int16_t, column); +} + +void flecs_query_it_set_tr( + ecs_iter_t *it, + int32_t field_index, + const ecs_table_record_t *tr) +{ + ecs_assert(field_index >= 0, ECS_INTERNAL_ERROR, NULL); + it->trs[field_index] = tr; +} + +ecs_id_t flecs_query_it_set_id( + ecs_iter_t *it, + ecs_table_t *table, + int32_t field_index, + int32_t column) +{ + ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(field_index >= 0, ECS_INTERNAL_ERROR, NULL); + return it->ids[field_index] = table->type.array[column]; +} + +void flecs_query_set_match( + const ecs_query_op_t *op, + ecs_table_t *table, + int32_t column, + const ecs_query_run_ctx_t *ctx) +{ + ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); + int32_t field_index = op->field_index; + if (field_index == -1) { + return; + } + + ecs_iter_t *it = ctx->it; + ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column < table->type.count, ECS_INTERNAL_ERROR, NULL); + const ecs_table_record_t *tr = &table->_->records[column]; + flecs_query_it_set_tr(it, field_index, tr); + ecs_id_t matched = flecs_query_it_set_id(it, table, field_index, tr->index); + flecs_query_set_vars(op, matched, ctx); +} + +void flecs_query_set_trav_match( + const ecs_query_op_t *op, + const ecs_table_record_t *tr, + ecs_entity_t trav, + ecs_entity_t second, + const ecs_query_run_ctx_t *ctx) +{ + int32_t field_index = op->field_index; + if (field_index == -1) { + return; + } + + ecs_iter_t *it = ctx->it; + ecs_id_t matched = ecs_pair(trav, second); + it->ids[op->field_index] = matched; + flecs_query_it_set_tr(it, op->field_index, tr); + flecs_query_set_vars(op, matched, ctx); +} + +bool flecs_query_table_filter( + ecs_table_t *table, + ecs_query_lbl_t other, + ecs_flags32_t filter_mask) +{ + uint32_t filter = flecs_ito(uint32_t, other); + return (table->flags & filter_mask & filter) != 0; +} diff --git a/vendors/flecs/src/addons/rules/trav_cache.c b/vendors/flecs/src/query/engine/trav_cache.c similarity index 79% rename from vendors/flecs/src/addons/rules/trav_cache.c rename to vendors/flecs/src/query/engine/trav_cache.c index 74855b865..72808755d 100644 --- a/vendors/flecs/src/addons/rules/trav_cache.c +++ b/vendors/flecs/src/query/engine/trav_cache.c @@ -1,17 +1,15 @@ /** - * @file addons/rules/trav_cache.c + * @file query/engine/trav_cache.c * @brief Cache that stores the result of graph traversal. */ -#include "rules.h" - -#ifdef FLECS_RULES +#include "../../private_api.h" static -void flecs_rule_build_down_cache( +void flecs_query_build_down_cache( ecs_world_t *world, ecs_allocator_t *a, - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, ecs_entity_t entity) @@ -37,11 +35,11 @@ void flecs_rule_build_down_cache( } int32_t i, count = ecs_table_count(table); - ecs_entity_t *entities = table->data.entities.array; + const ecs_entity_t *entities = ecs_table_entities(table); for (i = 0; i < count; i ++) { ecs_record_t *r = flecs_entities_get(world, entities[i]); if (r->row & EcsEntityIsTraversable) { - flecs_rule_build_down_cache( + flecs_query_build_down_cache( world, a, ctx, cache, trav, entities[i]); } } @@ -50,10 +48,10 @@ void flecs_rule_build_down_cache( } static -void flecs_rule_build_up_cache( +void flecs_query_build_up_cache( ecs_world_t *world, ecs_allocator_t *a, - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, ecs_table_t *table, @@ -72,8 +70,9 @@ void flecs_rule_build_up_cache( ecs_trav_elem_t *el = ecs_vec_append_t(a, &cache->entities, ecs_trav_elem_t); + el->entity = second; - el->column = root_column; + el->tr = &table->_->records[i]; el->idr = NULL; ecs_record_t *r = flecs_entities_get_any(world, second); @@ -83,44 +82,44 @@ void flecs_rule_build_up_cache( if (!r_tr) { return; } - flecs_rule_build_up_cache(world, a, ctx, cache, trav, r->table, + flecs_query_build_up_cache(world, a, ctx, cache, trav, r->table, r_tr, root_column); } } } -void flecs_rule_trav_cache_fini( +void flecs_query_trav_cache_fini( ecs_allocator_t *a, ecs_trav_cache_t *cache) { ecs_vec_fini_t(a, &cache->entities, ecs_trav_elem_t); } -void flecs_rule_get_trav_down_cache( - const ecs_rule_run_ctx_t *ctx, +void flecs_query_get_trav_down_cache( + const ecs_query_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, ecs_entity_t entity) { if (cache->id != ecs_pair(trav, entity) || cache->up) { ecs_world_t *world = ctx->it->real_world; - ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_allocator_t *a = flecs_query_get_allocator(ctx->it); ecs_vec_reset_t(a, &cache->entities, ecs_trav_elem_t); - flecs_rule_build_down_cache(world, a, ctx, cache, trav, entity); + flecs_query_build_down_cache(world, a, ctx, cache, trav, entity); cache->id = ecs_pair(trav, entity); cache->up = false; } } -void flecs_rule_get_trav_up_cache( - const ecs_rule_run_ctx_t *ctx, +void flecs_query_get_trav_up_cache( + const ecs_query_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, ecs_table_t *table) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_world_t *world = ctx->it->real_world; - ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_allocator_t *a = flecs_query_get_allocator(ctx->it); ecs_id_record_t *idr = cache->idr; if (!idr || idr->id != ecs_pair(trav, EcsWildcard)) { @@ -142,10 +141,8 @@ void flecs_rule_get_trav_up_cache( if (cache->id != id || !cache->up) { ecs_vec_reset_t(a, &cache->entities, ecs_trav_elem_t); - flecs_rule_build_up_cache(world, a, ctx, cache, trav, table, tr, -1); + flecs_query_build_up_cache(world, a, ctx, cache, trav, table, tr, -1); cache->id = id; cache->up = true; } } - -#endif diff --git a/vendors/flecs/src/query/engine/trav_cache.h b/vendors/flecs/src/query/engine/trav_cache.h new file mode 100644 index 000000000..05bfbc1db --- /dev/null +++ b/vendors/flecs/src/query/engine/trav_cache.h @@ -0,0 +1,60 @@ +/** + * @file query/engine/trav_cache.h + * @brief Traversal cache functions + */ + +#include "../types.h" + +/* Traversal cache for transitive queries. Finds all reachable entities by + * following a relationship */ + +/* Find all entities when traversing downwards */ +void flecs_query_get_trav_down_cache( + const ecs_query_run_ctx_t *ctx, + ecs_trav_cache_t *cache, + ecs_entity_t trav, + ecs_entity_t entity); + +/* Find all entities when traversing upwards */ +void flecs_query_get_trav_up_cache( + const ecs_query_run_ctx_t *ctx, + ecs_trav_cache_t *cache, + ecs_entity_t trav, + ecs_table_t *table); + +/* Free traversal cache */ +void flecs_query_trav_cache_fini( + ecs_allocator_t *a, + ecs_trav_cache_t *cache); + +/* Traversal caches for up traversal. Enables searching upwards until an entity + * with the queried for id has been found. */ + +/* Traverse downwards from starting entity to find all tables for which the + * specified entity is the source of the queried for id ('with'). */ +ecs_trav_down_t* flecs_query_get_down_cache( + const ecs_query_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + ecs_entity_t trav, + ecs_entity_t entity, + ecs_id_record_t *idr_with, + bool self, + bool empty); + +/* Free down traversal cache */ +void flecs_query_down_cache_fini( + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache); + +ecs_trav_up_t* flecs_query_get_up_cache( + const ecs_query_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + ecs_table_t *table, + ecs_id_t with, + ecs_entity_t trav, + ecs_id_record_t *idr_with, + ecs_id_record_t *idr_trav); + +/* Free up traversal cache */ +void flecs_query_up_cache_fini( + ecs_trav_up_cache_t *cache); diff --git a/vendors/flecs/src/addons/rules/trav_down_cache.c b/vendors/flecs/src/query/engine/trav_down_cache.c similarity index 79% rename from vendors/flecs/src/addons/rules/trav_down_cache.c rename to vendors/flecs/src/query/engine/trav_down_cache.c index ad1bb1ca7..32a7eec95 100644 --- a/vendors/flecs/src/addons/rules/trav_down_cache.c +++ b/vendors/flecs/src/query/engine/trav_down_cache.c @@ -1,6 +1,9 @@ -#include "rules.h" +/** + * @file query/engine/trav_down_cache.c + * @brief Compile query term. + */ -#ifdef FLECS_RULES +#include "../../private_api.h" static void flecs_trav_entity_down_isa( @@ -11,7 +14,8 @@ void flecs_trav_entity_down_isa( ecs_entity_t trav, ecs_entity_t entity, ecs_id_record_t *idr_with, - bool self); + bool self, + bool empty); static ecs_trav_down_t* flecs_trav_entity_down( @@ -23,11 +27,12 @@ ecs_trav_down_t* flecs_trav_entity_down( ecs_entity_t entity, ecs_id_record_t *idr_trav, ecs_id_record_t *idr_with, - bool self); + bool self, + bool empty); static ecs_trav_down_t* flecs_trav_down_ensure( - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_trav_up_cache_t *cache, ecs_entity_t entity) { @@ -50,7 +55,8 @@ ecs_trav_down_t* flecs_trav_table_down( ecs_entity_t trav, const ecs_table_t *table, ecs_id_record_t *idr_with, - bool self) + bool self, + bool empty) { ecs_assert(table->id != 0, ECS_INTERNAL_ERROR, NULL); @@ -60,7 +66,7 @@ ecs_trav_down_t* flecs_trav_table_down( ecs_assert(idr_with != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + const ecs_entity_t *entities = ecs_table_entities(table); int32_t i, count = ecs_table_count(table); for (i = 0; i < count; i ++) { ecs_entity_t entity = entities[i]; @@ -78,7 +84,7 @@ ecs_trav_down_t* flecs_trav_table_down( } flecs_trav_entity_down(world, a, cache, dst, - trav, entity, idr_trav, idr_with, self); + trav, entity, idr_trav, idr_with, self, empty); } } @@ -94,7 +100,8 @@ void flecs_trav_entity_down_isa( ecs_entity_t trav, ecs_entity_t entity, ecs_id_record_t *idr_with, - bool self) + bool self, + bool empty) { if (trav == EcsIsA || !world->idr_isa_wildcard) { return; @@ -107,7 +114,7 @@ void flecs_trav_entity_down_isa( } ecs_table_cache_iter_t it; - if (flecs_table_cache_all_iter(&idr_isa->cache, &it)) { + if (flecs_table_cache_iter(&idr_isa->cache, &it)) { ecs_table_record_t *tr; while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { ecs_table_t *table = tr->hdr.table; @@ -115,7 +122,12 @@ void flecs_trav_entity_down_isa( continue; } - ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + if (ecs_table_has_id(world, table, idr_with->id)) { + /* Table owns component */ + continue; + } + + const ecs_entity_t *entities = ecs_table_entities(table); int32_t i, count = ecs_table_count(table); for (i = 0; i < count; i ++) { ecs_entity_t e = entities[i]; @@ -128,8 +140,13 @@ void flecs_trav_entity_down_isa( if (flags & EcsEntityIsTraversable) { ecs_id_record_t *idr_trav = flecs_id_record_get(world, ecs_pair(trav, e)); - flecs_trav_entity_down(world, a, cache, dst, trav, e, - idr_trav, idr_with, self); + if (idr_trav) { + flecs_trav_entity_down(world, a, cache, dst, trav, e, + idr_trav, idr_with, self, empty); + } + + flecs_trav_entity_down_isa(world, a, cache, dst, trav, e, + idr_with, self, empty); } } } @@ -143,21 +160,30 @@ ecs_trav_down_t* flecs_trav_entity_down( ecs_trav_up_cache_t *cache, ecs_trav_down_t *dst, ecs_entity_t trav, - ecs_entity_t entity, + ecs_entity_t e, ecs_id_record_t *idr_trav, ecs_id_record_t *idr_with, - bool self) + bool self, + bool empty) { ecs_assert(dst != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(idr_with != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(idr_trav != NULL, ECS_INTERNAL_ERROR, NULL); flecs_trav_entity_down_isa( - world, a, cache, dst, trav, entity, idr_with, self); + world, a, cache, dst, trav, e, idr_with, self, empty); int32_t first = ecs_vec_count(&dst->elems); ecs_table_cache_iter_t it; - if (flecs_table_cache_iter(&idr_trav->cache, &it)) { + bool result; + if (empty) { + result = flecs_table_cache_all_iter(&idr_trav->cache, &it); + } else { + result = flecs_table_cache_iter(&idr_trav->cache, &it); + } + + if (result) { ecs_table_record_t *tr; while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { ecs_assert(tr->count == 1, ECS_INTERNAL_ERROR, NULL); @@ -203,26 +229,27 @@ ecs_trav_down_t* flecs_trav_entity_down( &dst->elems, ecs_trav_down_elem_t, t); if (!elem->leaf) { flecs_trav_table_down(world, a, cache, dst, trav, - elem->table, idr_with, self); + elem->table, idr_with, self, empty); } } return dst; } -ecs_trav_down_t* flecs_rule_get_down_cache( - const ecs_rule_run_ctx_t *ctx, +ecs_trav_down_t* flecs_query_get_down_cache( + const ecs_query_run_ctx_t *ctx, ecs_trav_up_cache_t *cache, ecs_entity_t trav, ecs_entity_t e, ecs_id_record_t *idr_with, - bool self) + bool self, + bool empty) { ecs_world_t *world = ctx->it->real_world; - ecs_assert(!(cache->dir & EcsUp), ECS_INTERNAL_ERROR, NULL); - cache->dir = EcsDown; + ecs_assert(cache->dir != EcsTravUp, ECS_INTERNAL_ERROR, NULL); + cache->dir = EcsTravDown; - ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_allocator_t *a = flecs_query_get_allocator(ctx->it); ecs_map_init_if(&cache->src, a); ecs_trav_down_t *result = flecs_trav_down_ensure(ctx, cache, e); @@ -234,7 +261,7 @@ ecs_trav_down_t* flecs_rule_get_down_cache( if (!idr_trav) { if (trav != EcsIsA) { flecs_trav_entity_down_isa( - world, a, cache, result, trav, e, idr_with, self); + world, a, cache, result, trav, e, idr_with, self, empty); } result->ready = true; return result; @@ -242,13 +269,13 @@ ecs_trav_down_t* flecs_rule_get_down_cache( ecs_vec_init_t(a, &result->elems, ecs_trav_down_elem_t, 0); flecs_trav_entity_down( - world, a, cache, result, trav, e, idr_trav, idr_with, self); + world, a, cache, result, trav, e, idr_trav, idr_with, self, empty); result->ready = true; return result; } -void flecs_rule_down_cache_fini( +void flecs_query_down_cache_fini( ecs_allocator_t *a, ecs_trav_up_cache_t *cache) { @@ -259,5 +286,3 @@ void flecs_rule_down_cache_fini( } ecs_map_fini(&cache->src); } - -#endif diff --git a/vendors/flecs/src/addons/rules/trav_up_cache.c b/vendors/flecs/src/query/engine/trav_up_cache.c similarity index 82% rename from vendors/flecs/src/addons/rules/trav_up_cache.c rename to vendors/flecs/src/query/engine/trav_up_cache.c index 0cebd5a93..2b35d5cf9 100644 --- a/vendors/flecs/src/addons/rules/trav_up_cache.c +++ b/vendors/flecs/src/query/engine/trav_up_cache.c @@ -1,10 +1,13 @@ -#include "rules.h" +/** + * @file query/engine/trav_up_cache.c + * @brief Compile query term. + */ -#ifdef FLECS_RULES +#include "../../private_api.h" static ecs_trav_up_t* flecs_trav_up_ensure( - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_trav_up_cache_t *cache, uint64_t table_id) { @@ -27,7 +30,8 @@ int32_t flecs_trav_type_search( ecs_table_record_t *tr = ecs_table_cache_get(&idr_with->cache, table); if (tr) { up->id = type->array[tr->index]; - return up->column = tr->index; + up->tr = tr; + return tr->index; } return -1; @@ -36,6 +40,7 @@ int32_t flecs_trav_type_search( static int32_t flecs_trav_type_offset_search( ecs_trav_up_t *up, + const ecs_table_t *table, int32_t offset, ecs_id_t with, ecs_type_t *type) @@ -46,8 +51,9 @@ int32_t flecs_trav_type_offset_search( while (offset < type->count) { ecs_id_t type_id = type->array[offset ++]; if (ecs_id_match(type_id, with)) { - up->id = flecs_to_public_id(type_id); - return up->column = offset - 1; + up->id = type_id; + up->tr = &table->_->records[offset - 1]; + return offset - 1; } } @@ -56,7 +62,7 @@ int32_t flecs_trav_type_offset_search( static ecs_trav_up_t* flecs_trav_table_up( - const ecs_rule_run_ctx_t *ctx, + const ecs_query_run_ctx_t *ctx, ecs_allocator_t *a, ecs_trav_up_cache_t *cache, const ecs_world_t *world, @@ -106,15 +112,15 @@ ecs_trav_up_t* flecs_trav_table_up( ecs_trav_up_t *up_parent = flecs_trav_table_up(ctx, a, cache, world, tgt, with, rel, idr_with, idr_trav); - if (up_parent->column != -1) { + if (up_parent->tr) { up->src = up_parent->src; - up->column = up_parent->column; + up->tr = up_parent->tr; up->id = up_parent->id; goto found; } r_column = flecs_trav_type_offset_search( - &up_pair, r_column + 1, rel, &type); + &up_pair, table, r_column + 1, rel, &type); } if (!is_a) { @@ -128,28 +134,28 @@ ecs_trav_up_t* flecs_trav_table_up( ecs_trav_up_t *up_parent = flecs_trav_table_up(ctx, a, cache, world, tgt, with, rel, idr_with, idr_trav); - if (up_parent->column != -1) { + if (up_parent->tr) { up->src = up_parent->src; - up->column = up_parent->column; + up->tr = up_parent->tr; up->id = up_parent->id; goto found; } r_column = flecs_trav_type_offset_search( - &up_pair, r_column + 1, rel, &type); + &up_pair, table, r_column + 1, rel, &type); } } } not_found: - up->column = -1; + up->tr = NULL; found: up->ready = true; return up; } -ecs_trav_up_t* flecs_rule_get_up_cache( - const ecs_rule_run_ctx_t *ctx, +ecs_trav_up_t* flecs_query_get_up_cache( + const ecs_query_run_ctx_t *ctx, ecs_trav_up_cache_t *cache, ecs_table_t *table, ecs_id_t with, @@ -158,15 +164,15 @@ ecs_trav_up_t* flecs_rule_get_up_cache( ecs_id_record_t *idr_trav) { if (cache->with && cache->with != with) { - flecs_rule_up_cache_fini(cache); + flecs_query_up_cache_fini(cache); } ecs_world_t *world = ctx->it->real_world; - ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_allocator_t *a = flecs_query_get_allocator(ctx->it); ecs_map_init_if(&cache->src, a); - ecs_assert(!(cache->dir & EcsDown), ECS_INTERNAL_ERROR, NULL); - cache->dir = EcsUp; + ecs_assert(cache->dir != EcsTravDown, ECS_INTERNAL_ERROR, NULL); + cache->dir = EcsTravUp; cache->with = with; ecs_assert(idr_with != NULL, ECS_INTERNAL_ERROR, NULL); @@ -191,10 +197,8 @@ ecs_trav_up_t* flecs_rule_get_up_cache( return NULL; } -void flecs_rule_up_cache_fini( +void flecs_query_up_cache_fini( ecs_trav_up_cache_t *cache) { ecs_map_fini(&cache->src); } - -#endif diff --git a/vendors/flecs/src/query/engine/trivial_iter.c b/vendors/flecs/src/query/engine/trivial_iter.c new file mode 100644 index 000000000..4f317c053 --- /dev/null +++ b/vendors/flecs/src/query/engine/trivial_iter.c @@ -0,0 +1,215 @@ +/** + * @file query/engine/trivial_iter.c + * @brief Iterator for trivial queries. + */ + +#include "../../private_api.h" + +static +bool flecs_query_trivial_search_init( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + const ecs_query_t *query, + bool redo, + ecs_flags64_t term_set) +{ + if (!redo) { + /* Find first trivial term*/ + int32_t t = 0; + if (term_set) { + for (; t < query->term_count; t ++) { + if (term_set & (1llu << t)) { + break; + } + } + } + + ecs_assert(t != query->term_count, ECS_INTERNAL_ERROR, NULL); + op_ctx->start_from = t; + + ecs_id_record_t *idr = flecs_id_record_get(ctx->world, query->ids[t]); + if (!idr) { + return false; + } + + if (query->flags & EcsQueryMatchEmptyTables) { + if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)){ + return false; + } + } else { + if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { + return false; + } + } + + /* Find next term to evaluate once */ + + for (t = t + 1; t < query->term_count; t ++) { + if (term_set & (1llu << t)) { + break; + } + } + + op_ctx->first_to_eval = t; + } + + return true; +} + +bool flecs_query_trivial_search( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + bool redo, + ecs_flags64_t term_set) +{ + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + const ecs_term_t *terms = q->terms; + ecs_iter_t *it = ctx->it; + int32_t t, term_count = query->pub.term_count; + + if (!flecs_query_trivial_search_init(ctx, op_ctx, q, redo, term_set)) { + return false; + } + + do { + const ecs_table_record_t *tr = flecs_table_cache_next( + &op_ctx->it, ecs_table_record_t); + if (!tr) { + return false; + } + + ecs_table_t *table = tr->hdr.table; + if (table->flags & (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)) { + continue; + } + + for (t = op_ctx->first_to_eval; t < term_count; t ++) { + if (!(term_set & (1llu << t))) { + continue; + } + + const ecs_term_t *term = &terms[t]; + ecs_id_record_t *idr = flecs_id_record_get(ctx->world, term->id); + if (!idr) { + break; + } + + const ecs_table_record_t *tr_with = flecs_id_record_get_table( + idr, table); + if (!tr_with) { + break; + } + + it->trs[term->field_index] = tr_with; + } + + if (t == term_count) { + ctx->vars[0].range.table = table; + ctx->vars[0].range.count = 0; + ctx->vars[0].range.offset = 0; + it->trs[op_ctx->start_from] = tr; + break; + } + } while (true); + + return true; +} + +bool flecs_query_is_trivial_search( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + bool redo) +{ + const ecs_query_impl_t *query = ctx->query; + const ecs_query_t *q = &query->pub; + const ecs_id_t *ids = q->ids; + ecs_iter_t *it = ctx->it; + int32_t t, term_count = query->pub.term_count; + + if (!flecs_query_trivial_search_init(ctx, op_ctx, q, redo, 0)) { + return false; + } + +next: + { + const ecs_table_record_t *tr = flecs_table_cache_next( + &op_ctx->it, ecs_table_record_t); + if (!tr) { + return false; + } + + ecs_table_t *table = tr->hdr.table; + if (table->flags & (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled)) { + goto next; + } + + for (t = 1; t < term_count; t ++) { + ecs_id_record_t *idr = flecs_id_record_get(ctx->world, ids[t]); + if (!idr) { + return false; + } + + const ecs_table_record_t *tr_with = flecs_id_record_get_table( + idr, table); + if (!tr_with) { + goto next; + } + + it->trs[t] = tr_with; + } + + it->table = table; + it->count = ecs_table_count(table); + it->entities = ecs_table_entities(table); + it->trs[0] = tr; + } + + return true; +} + +bool flecs_query_trivial_test( + const ecs_query_run_ctx_t *ctx, + bool redo, + ecs_flags64_t term_set) +{ + if (redo) { + return false; + } else { + const ecs_query_impl_t *impl = ctx->query; + const ecs_query_t *q = &impl->pub; + const ecs_term_t *terms = q->terms; + ecs_iter_t *it = ctx->it; + int32_t t, term_count = impl->pub.term_count; + + ecs_table_t *table = it->table; + ecs_assert(table != NULL, ECS_INVALID_OPERATION, + "the variable set on the iterator is missing a table"); + + for (t = 0; t < term_count; t ++) { + if (!(term_set & (1llu << t))) { + continue; + } + + const ecs_term_t *term = &terms[t]; + ecs_id_record_t *idr = flecs_id_record_get(q->world, term->id); + if (!idr) { + return false; + } + + const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr) { + return false; + } + + it->trs[term->field_index] = tr; + } + + it->entities = ecs_table_entities(table); + if (it->entities) { + it->entities = &it->entities[it->offset]; + } + + return true; + } +} diff --git a/vendors/flecs/src/query/engine/trivial_iter.h b/vendors/flecs/src/query/engine/trivial_iter.h new file mode 100644 index 000000000..f054f8026 --- /dev/null +++ b/vendors/flecs/src/query/engine/trivial_iter.h @@ -0,0 +1,25 @@ +/** + * @file query/engine/trivial_iter.h + * @brief Trivial iterator functions. + */ + +#include "../types.h" + +/* Iterator for queries with trivial terms. */ +bool flecs_query_trivial_search( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + bool redo, + ecs_flags64_t field_set); + +/* Iterator for queries with only trivial terms. */ +bool flecs_query_is_trivial_search( + const ecs_query_run_ctx_t *ctx, + ecs_query_trivial_ctx_t *op_ctx, + bool redo); + +/* Trivial test for constrained $this. */ +bool flecs_query_trivial_test( + const ecs_query_run_ctx_t *ctx, + bool first, + ecs_flags64_t field_set); diff --git a/vendors/flecs/src/query/query.h b/vendors/flecs/src/query/query.h new file mode 100644 index 000000000..24d414eef --- /dev/null +++ b/vendors/flecs/src/query/query.h @@ -0,0 +1,29 @@ + /** + * @file query/query.h + * @brief Query implementation. + */ + +#include "compiler/compiler.h" +#include "engine/engine.h" +#include "util.h" + +#ifdef FLECS_DEBUG +#define flecs_set_var_label(var, lbl) (var)->label = lbl +#else +#define flecs_set_var_label(var, lbl) +#endif + +/* Finalize query data & validate */ +int flecs_query_finalize_query( + ecs_world_t *world, + ecs_query_t *q, + const ecs_query_desc_t *desc); + +/* Internal function for creating iterator, doesn't run aperiodic tasks */ +ecs_iter_t flecs_query_iter( + const ecs_world_t *world, + const ecs_query_t *q); + +/* Internal function for initializing an iterator after vars are constrained */ +void flecs_query_iter_constrain( + ecs_iter_t *it); diff --git a/vendors/flecs/src/query/types.h b/vendors/flecs/src/query/types.h new file mode 100644 index 000000000..51b923dd7 --- /dev/null +++ b/vendors/flecs/src/query/types.h @@ -0,0 +1,488 @@ + /** + * @file query/types.h + * @brief Internal types and functions for queries. + */ + +#ifndef FLECS_QUERY_TYPES +#define FLECS_QUERY_TYPES + +typedef struct ecs_query_impl_t ecs_query_impl_t; +typedef uint8_t ecs_var_id_t; +typedef int16_t ecs_query_lbl_t; +typedef ecs_flags64_t ecs_write_flags_t; + +#define flecs_query_impl(query) (ECS_CONST_CAST(ecs_query_impl_t*, query)) +#define EcsQueryMaxVarCount (64) +#define EcsVarNone ((ecs_var_id_t)-1) +#define EcsThisName "this" + +/* -- Variable types -- */ +typedef enum { + EcsVarEntity, /* Variable that stores an entity id */ + EcsVarTable, /* Variable that stores a table */ + EcsVarAny /* Used when requesting either entity or table var */ +} ecs_var_kind_t; + +typedef struct ecs_query_var_t { + int8_t kind; /* variable kind (EcsVarEntity or EcsVarTable) */ + bool anonymous; /* variable is anonymous */ + ecs_var_id_t id; /* variable id */ + ecs_var_id_t table_id; /* id to table variable, if any */ + ecs_var_id_t base_id; /* id to base entity variable, for lookups */ + const char *name; /* variable name */ + const char *lookup; /* Lookup string for variable */ +#ifdef FLECS_DEBUG + const char *label; /* for debugging */ +#endif +} ecs_query_var_t; + +/* Placeholder values for queries with only $this variable */ +extern ecs_query_var_t flecs_this_array; +extern char *flecs_this_name_array; + +/* -- Instruction kinds -- */ +typedef enum { + EcsQueryAnd, /* And operator: find or match id against variable source */ + EcsQueryAndAny, /* And operator with support for matching Any src/id */ + EcsQueryOnlyAny, /* Dedicated instruction for _ queries where the src is unknown */ + EcsQueryTriv, /* Trivial search (batches multiple terms) */ + EcsQueryCache, /* Cached search */ + EcsQueryIsCache, /* Cached search for queries that are entirely cached */ + EcsQueryUp, /* Up traversal */ + EcsQuerySelfUp, /* Self|up traversal */ + EcsQueryWith, /* Match id against fixed or variable source */ + EcsQueryTrav, /* Support for transitive/reflexive queries */ + EcsQueryAndFrom, /* AndFrom operator */ + EcsQueryOrFrom, /* OrFrom operator */ + EcsQueryNotFrom, /* NotFrom operator */ + EcsQueryIds, /* Test for existence of ids matching wildcard */ + EcsQueryIdsRight, /* Find ids in use that match (R, *) wildcard */ + EcsQueryIdsLeft, /* Find ids in use that match (*, T) wildcard */ + EcsQueryEach, /* Iterate entities in table, populate entity variable */ + EcsQueryStore, /* Store table or entity in variable */ + EcsQueryReset, /* Reset value of variable to wildcard (*) */ + EcsQueryOr, /* Or operator */ + EcsQueryOptional, /* Optional operator */ + EcsQueryIfVar, /* Conditional execution on whether variable is set */ + EcsQueryIfSet, /* Conditional execution on whether term is set */ + EcsQueryNot, /* Sets iterator state after term was not matched */ + EcsQueryEnd, /* End of control flow block */ + EcsQueryPredEq, /* Test if variable is equal to, or assign to if not set */ + EcsQueryPredNeq, /* Test if variable is not equal to */ + EcsQueryPredEqName, /* Same as EcsQueryPredEq but with matching by name */ + EcsQueryPredNeqName, /* Same as EcsQueryPredNeq but with matching by name */ + EcsQueryPredEqMatch, /* Same as EcsQueryPredEq but with fuzzy matching by name */ + EcsQueryPredNeqMatch, /* Same as EcsQueryPredNeq but with fuzzy matching by name */ + EcsQueryMemberEq, /* Compare member value */ + EcsQueryMemberNeq, /* Compare member value */ + EcsQueryToggle, /* Evaluate toggle bitset, if present */ + EcsQueryToggleOption, /* Toggle for optional terms */ + EcsQueryUnionEq, /* Evaluate union relationship */ + EcsQueryUnionEqWith, /* Evaluate union relationship against fixed or variable source */ + EcsQueryUnionNeq, /* Evaluate union relationship */ + EcsQueryUnionEqUp, /* Evaluate union relationship w/up traversal */ + EcsQueryUnionEqSelfUp, /* Evaluate union relationship w/self|up traversal */ + EcsQueryLookup, /* Lookup relative to variable */ + EcsQuerySetVars, /* Populate it.sources from variables */ + EcsQuerySetThis, /* Populate This entity variable */ + EcsQuerySetFixed, /* Set fixed source entity ids */ + EcsQuerySetIds, /* Set fixed (component) ids */ + EcsQuerySetId, /* Set id if not set */ + EcsQueryContain, /* Test if table contains entity */ + EcsQueryPairEq, /* Test if both elements of pair are the same */ + EcsQueryYield, /* Yield result back to application */ + EcsQueryNothing /* Must be last */ +} ecs_query_op_kind_t; + +/* Op flags to indicate if ecs_query_ref_t is entity or variable */ +#define EcsQueryIsEntity (1 << 0) +#define EcsQueryIsVar (1 << 1) +#define EcsQueryIsSelf (1 << 6) + +/* Op flags used to shift EcsQueryIsEntity and EcsQueryIsVar */ +#define EcsQuerySrc 0 +#define EcsQueryFirst 2 +#define EcsQuerySecond 4 + +/* References to variable or entity */ +typedef union { + ecs_var_id_t var; + ecs_entity_t entity; +} ecs_query_ref_t; + +/* Query instruction */ +typedef struct ecs_query_op_t { + uint8_t kind; /* Instruction kind */ + ecs_flags8_t flags; /* Flags storing whether 1st/2nd are variables */ + int8_t field_index; /* Query field corresponding with operation */ + int8_t term_index; /* Query term corresponding with operation */ + ecs_query_lbl_t prev; /* Backtracking label (no data) */ + ecs_query_lbl_t next; /* Forwarding label. Must come after prev */ + ecs_query_lbl_t other; /* Misc register used for control flow */ + ecs_flags16_t match_flags; /* Flags that modify matching behavior */ + ecs_query_ref_t src; + ecs_query_ref_t first; + ecs_query_ref_t second; + ecs_flags64_t written; /* Bitset with variables written by op */ +} ecs_query_op_t; + + /* And context */ +typedef struct { + ecs_id_record_t *idr; + ecs_table_cache_iter_t it; + int16_t column; + int16_t remaining; +} ecs_query_and_ctx_t; + +/* Union context */ +typedef struct { + ecs_id_record_t *idr; + ecs_table_range_t range; + ecs_map_iter_t tgt_iter; + ecs_entity_t cur; + ecs_entity_t tgt; + int32_t row; +} ecs_query_union_ctx_t; + +/* Down traversal cache (for resolving up queries w/unknown source) */ +typedef struct { + ecs_table_t *table; + bool leaf; /* Table owns and inherits id (for Up queries without Self) */ +} ecs_trav_down_elem_t; + +typedef struct { + ecs_vec_t elems; /* vector */ + bool ready; +} ecs_trav_down_t; + +typedef struct { + ecs_entity_t src; + ecs_id_t id; + ecs_table_record_t *tr; + bool ready; +} ecs_trav_up_t; + +typedef enum { + EcsTravUp = 1, + EcsTravDown = 2 +} ecs_trav_direction_t; + +typedef struct { + ecs_map_t src; /* map or map */ + ecs_id_t with; + ecs_trav_direction_t dir; +} ecs_trav_up_cache_t; + +/* And up context */ +typedef struct { + union { + ecs_query_and_ctx_t and; + ecs_query_union_ctx_t union_; + } is; + ecs_table_t *table; + int32_t row; + int32_t end; + ecs_entity_t trav; + ecs_id_t with; + ecs_id_t matched; + ecs_id_record_t *idr_with; + ecs_id_record_t *idr_trav; + ecs_trav_down_t *down; + int32_t cache_elem; + ecs_trav_up_cache_t cache; +} ecs_query_up_ctx_t; + +/* Cache for storing results of upward/downward "all" traversal. This type of + * traversal iterates and caches the entire tree. */ +typedef struct { + ecs_entity_t entity; + ecs_id_record_t *idr; + const ecs_table_record_t *tr; +} ecs_trav_elem_t; + +typedef struct { + ecs_id_t id; + ecs_id_record_t *idr; + ecs_vec_t entities; + bool up; +} ecs_trav_cache_t; + +/* Trav context */ +typedef struct { + ecs_query_and_ctx_t and; + int32_t index; + int32_t offset; + int32_t count; + ecs_trav_cache_t cache; + bool yield_reflexive; +} ecs_query_trav_ctx_t; + + /* Eq context */ +typedef struct { + ecs_table_range_t range; + int32_t index; + int16_t name_col; + bool redo; +} ecs_query_eq_ctx_t; + + /* Each context */ +typedef struct { + int32_t row; +} ecs_query_each_ctx_t; + + /* Setthis context */ +typedef struct { + ecs_table_range_t range; +} ecs_query_setthis_ctx_t; + +/* Ids context */ +typedef struct { + ecs_id_record_t *cur; +} ecs_query_ids_ctx_t; + +/* Control flow context */ +typedef struct { + ecs_query_lbl_t op_index; + ecs_id_t field_id; + bool is_set; +} ecs_query_ctrl_ctx_t; + +/* Trivial iterator context */ +typedef struct { + ecs_table_cache_iter_t it; + const ecs_table_record_t *tr; + int32_t start_from; + int32_t first_to_eval; +} ecs_query_trivial_ctx_t; + +/* *From operator iterator context */ +typedef struct { + ecs_query_and_ctx_t and; + ecs_type_t *type; + int32_t first_id_index; + int32_t cur_id_index; +} ecs_query_xfrom_ctx_t; + +/* Member equality context */ +typedef struct { + ecs_query_each_ctx_t each; + void *data; +} ecs_query_membereq_ctx_t; + +/* Toggle context */ +typedef struct { + ecs_table_range_t range; + int32_t cur; + int32_t block_index; + ecs_flags64_t block; + ecs_termset_t prev_set_fields; + bool optional_not; + bool has_bitset; +} ecs_query_toggle_ctx_t; + +typedef struct ecs_query_op_ctx_t { + union { + ecs_query_and_ctx_t and; + ecs_query_xfrom_ctx_t xfrom; + ecs_query_up_ctx_t up; + ecs_query_trav_ctx_t trav; + ecs_query_ids_ctx_t ids; + ecs_query_eq_ctx_t eq; + ecs_query_each_ctx_t each; + ecs_query_setthis_ctx_t setthis; + ecs_query_ctrl_ctx_t ctrl; + ecs_query_trivial_ctx_t trivial; + ecs_query_membereq_ctx_t membereq; + ecs_query_toggle_ctx_t toggle; + ecs_query_union_ctx_t union_; + } is; +} ecs_query_op_ctx_t; + +typedef struct { + /* Labels used for control flow */ + ecs_query_lbl_t lbl_query; /* Used to find the op that does the actual searching */ + ecs_query_lbl_t lbl_begin; + ecs_query_lbl_t lbl_cond_eval; + ecs_write_flags_t written_or; /* Cond written flags at start of or chain */ + ecs_write_flags_t cond_written_or; /* Cond written flags at start of or chain */ + ecs_query_ref_t src_or; /* Source for terms in current or chain */ + bool src_written_or; /* Was src populated before OR chain */ + bool in_or; /* Whether we're in an or chain */ +} ecs_query_compile_ctrlflow_t; + +/* Query compiler state */ +typedef struct { + ecs_vec_t *ops; + ecs_write_flags_t written; /* Bitmask to check which variables have been written */ + ecs_write_flags_t cond_written; /* Track conditional writes (optional operators) */ + + /* Maintain control flow per scope */ + ecs_query_compile_ctrlflow_t ctrlflow[FLECS_QUERY_SCOPE_NESTING_MAX]; + ecs_query_compile_ctrlflow_t *cur; /* Current scope */ + + int32_t scope; /* Nesting level of query scopes */ + ecs_flags32_t scope_is_not; /* Whether scope is prefixed with not */ + ecs_oper_kind_t oper; /* Temp storage to track current operator for term */ + int32_t skipped; /* Term skipped during compilation */ +} ecs_query_compile_ctx_t; + +/* Query run state */ +typedef struct { + uint64_t *written; /* Bitset to check which variables have been written */ + ecs_query_lbl_t op_index; /* Currently evaluated operation */ + ecs_var_t *vars; /* Variable storage */ + ecs_iter_t *it; /* Iterator */ + ecs_query_op_ctx_t *op_ctx; /* Operation context (stack) */ + ecs_world_t *world; /* Reference to world */ + const ecs_query_impl_t *query; /* Reference to query */ + const ecs_query_var_t *query_vars; /* Reference to query variable array */ + ecs_query_iter_t *qit; +} ecs_query_run_ctx_t; + +struct ecs_query_impl_t { + ecs_query_t pub; /* Public query data */ + + ecs_stage_t *stage; /* Stage used for allocations */ + + /* Variables */ + ecs_query_var_t *vars; /* Variables */ + int32_t var_count; /* Number of variables */ + int32_t var_size; /* Size of variable array */ + ecs_hashmap_t tvar_index; /* Name index for table variables */ + ecs_hashmap_t evar_index; /* Name index for entity variables */ + ecs_var_id_t *src_vars; /* Array with ids to source variables for fields */ + + /* Query plan */ + ecs_query_op_t *ops; /* Operations */ + int32_t op_count; /* Number of operations */ + + /* Misc */ + int16_t tokens_len; /* Length of tokens buffer */ + char *tokens; /* Buffer with string tokens used by terms */ + int32_t *monitor; /* Change monitor for fields with fixed src */ + + /* Query cache */ + struct ecs_query_cache_t *cache; /* Cache, if query contains cached terms */ + + /* User context */ + ecs_ctx_free_t ctx_free; /* Callback to free ctx */ + ecs_ctx_free_t binding_ctx_free; /* Callback to free binding_ctx */ + + /* Mixins */ + flecs_poly_dtor_t dtor; +}; + + +/* Query cache types */ + +/** Table match data. + * Each table matched by the query is represented by an ecs_query_cache_table_match_t + * instance, which are linked together in a list. A table may match a query + * multiple times (due to wildcard queries) with different columns being matched + * by the query. */ +struct ecs_query_cache_table_match_t { + ecs_query_cache_table_match_t *next, *prev; + ecs_table_t *table; /* The current table. */ + int32_t offset; /* Starting point in table */ + int32_t count; /* Number of entities to iterate in table */ + const ecs_table_record_t **trs; /* Information about where to find field in table */ + ecs_id_t *ids; /* Resolved (component) ids for current table */ + ecs_entity_t *sources; /* Subjects (sources) of ids */ + ecs_termset_t set_fields; /* Fields that are set */ + ecs_termset_t up_fields; /* Fields that are matched through traversal */ + uint64_t group_id; /* Value used to organize tables in groups */ + int32_t *monitor; /* Used to monitor table for changes */ + + /* Next match in cache for same table (includes empty tables) */ + ecs_query_cache_table_match_t *next_match; +}; + +/** Table record type for query table cache. A query only has one per table. */ +typedef struct ecs_query_cache_table_t { + ecs_table_cache_hdr_t hdr; /* Header for ecs_table_cache_t */ + ecs_query_cache_table_match_t *first; /* List with matches for table */ + ecs_query_cache_table_match_t *last; /* Last discovered match for table */ + uint64_t table_id; + int32_t rematch_count; /* Track whether table was rematched */ +} ecs_query_cache_table_t; + +/** Points to the beginning & ending of a query group */ +typedef struct ecs_query_cache_table_list_t { + ecs_query_cache_table_match_t *first; + ecs_query_cache_table_match_t *last; + ecs_query_group_info_t info; +} ecs_query_cache_table_list_t; + +/* Query event type for notifying queries of world events */ +typedef enum ecs_query_cache_eventkind_t { + EcsQueryTableMatch, + EcsQueryTableRematch, + EcsQueryTableUnmatch, +} ecs_query_cache_eventkind_t; + +typedef struct ecs_query_cache_event_t { + ecs_query_cache_eventkind_t kind; + ecs_table_t *table; +} ecs_query_cache_event_t; + +/* Query level block allocators have sizes that depend on query field count */ +typedef struct ecs_query_cache_allocators_t { + ecs_block_allocator_t trs; + ecs_block_allocator_t ids; + ecs_block_allocator_t sources; + ecs_block_allocator_t monitors; +} ecs_query_cache_allocators_t; + +/** Query that is automatically matched against tables */ +typedef struct ecs_query_cache_t { + /* Uncached query used to populate the cache */ + ecs_query_t *query; + + /* Observer to keep the cache in sync */ + ecs_observer_t *observer; + + /* Tables matched with query */ + ecs_table_cache_t cache; + + /* Linked list with all matched non-empty tables, in iteration order */ + ecs_query_cache_table_list_t list; + + /* Contains head/tail to nodes of query groups (if group_by is used) */ + ecs_map_t groups; + + /* Table sorting */ + ecs_entity_t order_by; + ecs_order_by_action_t order_by_callback; + ecs_sort_table_action_t order_by_table_callback; + ecs_vec_t table_slices; + int32_t order_by_term; + + /* Table grouping */ + ecs_entity_t group_by; + ecs_group_by_action_t group_by_callback; + ecs_group_create_action_t on_group_create; + ecs_group_delete_action_t on_group_delete; + void *group_by_ctx; + ecs_ctx_free_t group_by_ctx_free; + + /* Monitor generation */ + int32_t monitor_generation; + + int32_t cascade_by; /* Identify cascade term */ + int32_t match_count; /* How often have tables been (un)matched */ + int32_t prev_match_count; /* Track if sorting is needed */ + int32_t rematch_count; /* Track which tables were added during rematch */ + + ecs_entity_t entity; + + /* Zero'd out sources array, used for results that only match on $this */ + ecs_entity_t *sources; + + /* Map field indices from cache to query */ + int8_t *field_map; + + /* Query-level allocators */ + ecs_query_cache_allocators_t allocators; +} ecs_query_cache_t; + +#endif diff --git a/vendors/flecs/src/query/util.c b/vendors/flecs/src/query/util.c new file mode 100644 index 000000000..d6da4b602 --- /dev/null +++ b/vendors/flecs/src/query/util.c @@ -0,0 +1,749 @@ + /** + * @file query/util.c + * @brief Query utilities. + */ + +#include "../private_api.h" + +ecs_query_lbl_t flecs_itolbl(int64_t val) { + return flecs_ito(int16_t, val); +} + +ecs_var_id_t flecs_itovar(int64_t val) { + return flecs_ito(uint8_t, val); +} + +ecs_var_id_t flecs_utovar(uint64_t val) { + return flecs_uto(uint8_t, val); +} + +bool flecs_term_is_builtin_pred( + ecs_term_t *term) +{ + if (term->first.id & EcsIsEntity) { + ecs_entity_t id = ECS_TERM_REF_ID(&term->first); + if (id == EcsPredEq || id == EcsPredMatch || id == EcsPredLookup) { + return true; + } + } + return false; +} + +const char* flecs_term_ref_var_name( + ecs_term_ref_t *ref) +{ + if (!(ref->id & EcsIsVariable)) { + return NULL; + } + + if (ECS_TERM_REF_ID(ref) == EcsThis) { + return EcsThisName; + } + + return ref->name; +} + +bool flecs_term_ref_is_wildcard( + ecs_term_ref_t *ref) +{ + if ((ref->id & EcsIsVariable) && + ((ECS_TERM_REF_ID(ref) == EcsWildcard) || (ECS_TERM_REF_ID(ref) == EcsAny))) + { + return true; + } + return false; +} + +bool flecs_term_is_fixed_id( + ecs_query_t *q, + ecs_term_t *term) +{ + /* Transitive/inherited terms have variable ids */ + if (term->flags_ & (EcsTermTransitive|EcsTermIdInherited)) { + return false; + } + + /* Or terms can match different ids */ + if (term->oper == EcsOr) { + return false; + } + if ((term != q->terms) && term[-1].oper == EcsOr) { + return false; + } + + /* Wildcards can assume different ids */ + if (ecs_id_is_wildcard(term->id)) { + return false; + } + + /* Any terms can have fixed ids, but they require special handling */ + if (term->flags_ & (EcsTermMatchAny|EcsTermMatchAnySrc)) { + return false; + } + + /* First terms that are Not or Optional require special handling */ + if (term->oper == EcsNot || term->oper == EcsOptional) { + if (term == q->terms) { + return false; + } + } + + return true; +} + +bool flecs_term_is_or( + const ecs_query_t *q, + const ecs_term_t *term) +{ + bool first_term = term == q->terms; + return (term->oper == EcsOr) || (!first_term && term[-1].oper == EcsOr); +} + +ecs_flags16_t flecs_query_ref_flags( + ecs_flags16_t flags, + ecs_flags16_t kind) +{ + return (flags >> kind) & (EcsQueryIsVar | EcsQueryIsEntity); +} + +bool flecs_query_is_written( + ecs_var_id_t var_id, + uint64_t written) +{ + if (var_id == EcsVarNone) { + return true; + } + + ecs_assert(var_id < EcsQueryMaxVarCount, ECS_INTERNAL_ERROR, NULL); + return (written & (1ull << var_id)) != 0; +} + +void flecs_query_write( + ecs_var_id_t var_id, + uint64_t *written) +{ + ecs_assert(var_id < EcsQueryMaxVarCount, ECS_INTERNAL_ERROR, NULL); + *written |= (1ull << var_id); +} + +void flecs_query_write_ctx( + ecs_var_id_t var_id, + ecs_query_compile_ctx_t *ctx, + bool cond_write) +{ + bool is_written = flecs_query_is_written(var_id, ctx->written); + flecs_query_write(var_id, &ctx->written); + if (!is_written) { + if (cond_write) { + flecs_query_write(var_id, &ctx->cond_written); + } + } +} + +bool flecs_ref_is_written( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t kind, + uint64_t written) +{ + ecs_flags16_t flags = flecs_query_ref_flags(op->flags, kind); + if (flags & EcsQueryIsEntity) { + ecs_assert(!(flags & EcsQueryIsVar), ECS_INTERNAL_ERROR, NULL); + if (ref->entity) { + return true; + } + } else if (flags & EcsQueryIsVar) { + return flecs_query_is_written(ref->var, written); + } + return false; +} + +ecs_allocator_t* flecs_query_get_allocator( + const ecs_iter_t *it) +{ + ecs_world_t *world = it->world; + if (flecs_poly_is(world, ecs_world_t)) { + return &world->allocator; + } else { + ecs_assert(flecs_poly_is(world, ecs_stage_t), ECS_INTERNAL_ERROR, NULL); + return &((ecs_stage_t*)world)->allocator; + } +} + +const char* flecs_query_op_str( + uint16_t kind) +{ + switch(kind) { + case EcsQueryAnd: return "and "; + case EcsQueryAndAny: return "andany "; + case EcsQueryTriv: return "triv "; + case EcsQueryCache: return "cache "; + case EcsQueryIsCache: return "xcache "; + case EcsQueryOnlyAny: return "any "; + case EcsQueryUp: return "up "; + case EcsQuerySelfUp: return "selfup "; + case EcsQueryWith: return "with "; + case EcsQueryTrav: return "trav "; + case EcsQueryAndFrom: return "andfrom "; + case EcsQueryOrFrom: return "orfrom "; + case EcsQueryNotFrom: return "notfrom "; + case EcsQueryIds: return "ids "; + case EcsQueryIdsRight: return "idsr "; + case EcsQueryIdsLeft: return "idsl "; + case EcsQueryEach: return "each "; + case EcsQueryStore: return "store "; + case EcsQueryReset: return "reset "; + case EcsQueryOr: return "or "; + case EcsQueryOptional: return "option "; + case EcsQueryIfVar: return "ifvar "; + case EcsQueryIfSet: return "ifset "; + case EcsQueryEnd: return "end "; + case EcsQueryNot: return "not "; + case EcsQueryPredEq: return "eq "; + case EcsQueryPredNeq: return "neq "; + case EcsQueryPredEqName: return "eq_nm "; + case EcsQueryPredNeqName: return "neq_nm "; + case EcsQueryPredEqMatch: return "eq_m "; + case EcsQueryPredNeqMatch: return "neq_m "; + case EcsQueryMemberEq: return "membereq "; + case EcsQueryMemberNeq: return "memberneq "; + case EcsQueryToggle: return "toggle "; + case EcsQueryToggleOption: return "togglopt "; + case EcsQueryUnionEq: return "union "; + case EcsQueryUnionEqWith: return "union_w "; + case EcsQueryUnionNeq: return "unionneq "; + case EcsQueryUnionEqUp: return "union_up "; + case EcsQueryUnionEqSelfUp: return "union_sup "; + case EcsQueryLookup: return "lookup "; + case EcsQuerySetVars: return "setvars "; + case EcsQuerySetThis: return "setthis "; + case EcsQuerySetFixed: return "setfix "; + case EcsQuerySetIds: return "setids "; + case EcsQuerySetId: return "setid "; + case EcsQueryContain: return "contain "; + case EcsQueryPairEq: return "pair_eq "; + case EcsQueryYield: return "yield "; + case EcsQueryNothing: return "nothing "; + default: return "!invalid "; + } +} + +static +int32_t flecs_query_op_ref_str( + const ecs_query_impl_t *query, + ecs_query_ref_t *ref, + ecs_flags16_t flags, + ecs_strbuf_t *buf) +{ + int32_t color_chars = 0; + if (flags & EcsQueryIsVar) { + ecs_assert(ref->var < query->var_count, ECS_INTERNAL_ERROR, NULL); + ecs_query_var_t *var = &query->vars[ref->var]; + ecs_strbuf_appendlit(buf, "#[green]$#[reset]"); + if (var->kind == EcsVarTable) { + ecs_strbuf_appendch(buf, '['); + } + ecs_strbuf_appendlit(buf, "#[green]"); + if (var->name) { + ecs_strbuf_appendstr(buf, var->name); + } else { + if (var->id) { +#ifdef FLECS_DEBUG + if (var->label) { + ecs_strbuf_appendstr(buf, var->label); + ecs_strbuf_appendch(buf, '\''); + } +#endif + ecs_strbuf_append(buf, "%d", var->id); + } else { + ecs_strbuf_appendlit(buf, "this"); + } + } + ecs_strbuf_appendlit(buf, "#[reset]"); + if (var->kind == EcsVarTable) { + ecs_strbuf_appendch(buf, ']'); + } + color_chars = ecs_os_strlen("#[green]#[reset]#[green]#[reset]"); + } else if (flags & EcsQueryIsEntity) { + char *path = ecs_get_path(query->pub.world, ref->entity); + ecs_strbuf_appendlit(buf, "#[blue]"); + ecs_strbuf_appendstr(buf, path); + ecs_strbuf_appendlit(buf, "#[reset]"); + ecs_os_free(path); + color_chars = ecs_os_strlen("#[blue]#[reset]"); + } + return color_chars; +} + +static +void flecs_query_str_append_bitset( + ecs_strbuf_t *buf, + ecs_flags64_t bitset) +{ + ecs_strbuf_list_push(buf, "{", ","); + int8_t b; + for (b = 0; b < 64; b ++) { + if (bitset & (1llu << b)) { + ecs_strbuf_list_append(buf, "%d", b); + } + } + ecs_strbuf_list_pop(buf, "}"); +} + +static +void flecs_query_plan_w_profile( + const ecs_query_t *q, + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + ecs_query_impl_t *impl = flecs_query_impl(q); + ecs_query_op_t *ops = impl->ops; + int32_t i, count = impl->op_count, indent = 0; + if (!count) { + ecs_strbuf_append(buf, ""); + return; /* No plan */ + } + + for (i = 0; i < count; i ++) { + ecs_query_op_t *op = &ops[i]; + ecs_flags16_t flags = op->flags; + ecs_flags16_t src_flags = flecs_query_ref_flags(flags, EcsQuerySrc); + ecs_flags16_t first_flags = flecs_query_ref_flags(flags, EcsQueryFirst); + ecs_flags16_t second_flags = flecs_query_ref_flags(flags, EcsQuerySecond); + + if (it) { +#ifdef FLECS_DEBUG + const ecs_query_iter_t *rit = &it->priv_.iter.query; + ecs_strbuf_append(buf, + "#[green]%4d -> #[red]%4d <- #[grey] | ", + rit->profile[i].count[0], + rit->profile[i].count[1]); +#endif + } + + ecs_strbuf_append(buf, + "#[normal]%2d. [#[grey]%2d#[reset], #[green]%2d#[reset]] ", + i, op->prev, op->next); + int32_t hidden_chars, start = ecs_strbuf_written(buf); + if (op->kind == EcsQueryEnd) { + indent --; + } + + ecs_strbuf_append(buf, "%*s", indent, ""); + ecs_strbuf_appendstr(buf, flecs_query_op_str(op->kind)); + ecs_strbuf_appendstr(buf, " "); + + int32_t written = ecs_strbuf_written(buf); + for (int32_t j = 0; j < (12 - (written - start)); j ++) { + ecs_strbuf_appendch(buf, ' '); + } + + hidden_chars = flecs_query_op_ref_str(impl, &op->src, src_flags, buf); + + if (op->kind == EcsQueryNot || + op->kind == EcsQueryOr || + op->kind == EcsQueryOptional || + op->kind == EcsQueryIfVar || + op->kind == EcsQueryIfSet) + { + indent ++; + } + + if (op->kind == EcsQueryTriv) { + flecs_query_str_append_bitset(buf, op->src.entity); + } + + if (op->kind == EcsQueryIfSet) { + ecs_strbuf_append(buf, "[%d]\n", op->other); + continue; + } + + bool is_toggle = op->kind == EcsQueryToggle || + op->kind == EcsQueryToggleOption; + + if (!first_flags && !second_flags && !is_toggle) { + ecs_strbuf_appendstr(buf, "\n"); + continue; + } + + written = ecs_strbuf_written(buf) - hidden_chars; + for (int32_t j = 0; j < (30 - (written - start)); j ++) { + ecs_strbuf_appendch(buf, ' '); + } + + if (is_toggle) { + if (op->first.entity) { + flecs_query_str_append_bitset(buf, op->first.entity); + } + if (op->second.entity) { + if (op->first.entity) { + ecs_strbuf_appendlit(buf, ", !"); + } + flecs_query_str_append_bitset(buf, op->second.entity); + } + ecs_strbuf_appendstr(buf, "\n"); + continue; + } + + ecs_strbuf_appendstr(buf, "("); + if (op->kind == EcsQueryMemberEq || op->kind == EcsQueryMemberNeq) { + uint32_t offset = (uint32_t)op->first.entity; + uint32_t size = (uint32_t)(op->first.entity >> 32); + ecs_strbuf_append(buf, "#[yellow]elem#[reset]([%d], 0x%x, 0x%x)", + op->field_index, size, offset); + } else { + flecs_query_op_ref_str(impl, &op->first, first_flags, buf); + } + + if (second_flags) { + ecs_strbuf_appendstr(buf, ", "); + flecs_query_op_ref_str(impl, &op->second, second_flags, buf); + } else { + switch (op->kind) { + case EcsQueryPredEqName: + case EcsQueryPredNeqName: + case EcsQueryPredEqMatch: + case EcsQueryPredNeqMatch: { + int8_t term_index = op->term_index; + ecs_strbuf_appendstr(buf, ", #[yellow]\""); + ecs_strbuf_appendstr(buf, q->terms[term_index].second.name); + ecs_strbuf_appendstr(buf, "\"#[reset]"); + break; + } + case EcsQueryLookup: { + ecs_var_id_t src_id = op->src.var; + ecs_strbuf_appendstr(buf, ", #[yellow]\""); + ecs_strbuf_appendstr(buf, impl->vars[src_id].lookup); + ecs_strbuf_appendstr(buf, "\"#[reset]"); + break; + } + default: + break; + } + } + + ecs_strbuf_appendch(buf, ')'); + + ecs_strbuf_appendch(buf, '\n'); + } +} + +char* ecs_query_plan_w_profile( + const ecs_query_t *q, + const ecs_iter_t *it) +{ + flecs_poly_assert(q, ecs_query_t); + ecs_strbuf_t buf = ECS_STRBUF_INIT; + + flecs_query_plan_w_profile(q, it, &buf); + // ecs_query_impl_t *impl = flecs_query_impl(q); + // if (impl->cache) { + // ecs_strbuf_appendch(&buf, '\n'); + // flecs_query_plan_w_profile(impl->cache->query, it, &buf); + // } + +#ifdef FLECS_LOG + char *str = ecs_strbuf_get(&buf); + flecs_colorize_buf(str, ecs_os_api.flags_ & EcsOsApiLogWithColors, &buf); + ecs_os_free(str); +#endif + + return ecs_strbuf_get(&buf); +} + +char* ecs_query_plan( + const ecs_query_t *q) +{ + return ecs_query_plan_w_profile(q, NULL); +} + +static +void flecs_query_str_add_id( + const ecs_world_t *world, + ecs_strbuf_t *buf, + const ecs_term_t *term, + const ecs_term_ref_t *ref, + bool is_src) +{ + bool is_added = false; + ecs_entity_t ref_id = ECS_TERM_REF_ID(ref); + if (ref->id & EcsIsVariable && !ecs_id_is_wildcard(ref_id)){ + ecs_strbuf_appendlit(buf, "$"); + } + + if (ref_id) { + char *path = ecs_get_path(world, ref_id); + ecs_strbuf_appendstr(buf, path); + ecs_os_free(path); + } else if (ref->name) { + ecs_strbuf_appendstr(buf, ref->name); + } else { + ecs_strbuf_appendlit(buf, "0"); + } + is_added = true; + + ecs_flags64_t flags = ECS_TERM_REF_FLAGS(ref); + if (!(flags & EcsTraverseFlags)) { + /* If flags haven't been set yet, initialize with defaults. This can + * happen if an error is thrown while the term is being finalized */ + flags |= EcsSelf; + } + + if ((flags & EcsTraverseFlags) != EcsSelf) { + if (is_added) { + ecs_strbuf_list_push(buf, "|", "|"); + } else { + ecs_strbuf_list_push(buf, "", "|"); + } + if (is_src) { + if (flags & EcsSelf) { + ecs_strbuf_list_appendstr(buf, "self"); + } + + if (flags & EcsCascade) { + ecs_strbuf_list_appendstr(buf, "cascade"); + } else if (flags & EcsUp) { + ecs_strbuf_list_appendstr(buf, "up"); + } + + if (flags & EcsDesc) { + ecs_strbuf_list_appendstr(buf, "desc"); + } + + if (term->trav) { + char *rel_path = ecs_get_path(world, term->trav); + ecs_strbuf_appendlit(buf, " "); + ecs_strbuf_appendstr(buf, rel_path); + ecs_os_free(rel_path); + } + } + + ecs_strbuf_list_pop(buf, ""); + } +} + +void flecs_term_to_buf( + const ecs_world_t *world, + const ecs_term_t *term, + ecs_strbuf_t *buf, + int32_t t) +{ + const ecs_term_ref_t *src = &term->src; + const ecs_term_ref_t *first = &term->first; + const ecs_term_ref_t *second = &term->second; + + ecs_entity_t src_id = ECS_TERM_REF_ID(src); + ecs_entity_t first_id = ECS_TERM_REF_ID(first); + + bool src_set = !ecs_term_match_0(term); + bool second_set = ecs_term_ref_is_set(second); + + if (first_id == EcsScopeOpen) { + ecs_strbuf_appendlit(buf, "{"); + return; + } else if (first_id == EcsScopeClose) { + ecs_strbuf_appendlit(buf, "}"); + return; + } + + if (((ECS_TERM_REF_ID(&term->first) == EcsPredEq) || + (ECS_TERM_REF_ID(&term->first) == EcsPredMatch)) && + (term->first.id & EcsIsEntity)) + { + ecs_strbuf_appendlit(buf, "$"); + if (ECS_TERM_REF_ID(&term->src) == EcsThis && + (term->src.id & EcsIsVariable)) + { + ecs_strbuf_appendlit(buf, "this"); + } else if (term->src.id & EcsIsVariable) { + ecs_strbuf_appendstr(buf, term->src.name); + } else { + /* Shouldn't happen */ + } + + if (ECS_TERM_REF_ID(&term->first) == EcsPredEq) { + if (term->oper == EcsNot) { + ecs_strbuf_appendlit(buf, " != "); + } else { + ecs_strbuf_appendlit(buf, " == "); + } + } else if (ECS_TERM_REF_ID(&term->first) == EcsPredMatch) { + ecs_strbuf_appendlit(buf, " ~= "); + } + + if (term->second.id & EcsIsEntity) { + if (term->second.id != 0) { + ecs_get_path_w_sep_buf(world, 0, ECS_TERM_REF_ID(&term->second), + ".", NULL, buf, false); + } + } else { + if (term->second.id & EcsIsVariable) { + ecs_strbuf_appendlit(buf, "$"); + if (term->second.name) { + ecs_strbuf_appendstr(buf, term->second.name); + } else if (ECS_TERM_REF_ID(&term->second) == EcsThis) { + ecs_strbuf_appendlit(buf, "this"); + } + } else if (term->second.id & EcsIsName) { + ecs_strbuf_appendlit(buf, "\""); + if ((ECS_TERM_REF_ID(&term->first) == EcsPredMatch) && + (term->oper == EcsNot)) + { + ecs_strbuf_appendlit(buf, "!"); + } + ecs_strbuf_appendstr(buf, term->second.name); + ecs_strbuf_appendlit(buf, "\""); + } + } + + return; + } + + if (!t || !(term[-1].oper == EcsOr)) { + if (term->inout == EcsIn) { + ecs_strbuf_appendlit(buf, "[in] "); + } else if (term->inout == EcsInOut) { + ecs_strbuf_appendlit(buf, "[inout] "); + } else if (term->inout == EcsOut) { + ecs_strbuf_appendlit(buf, "[out] "); + } else if (term->inout == EcsInOutNone && term->oper != EcsNot) { + ecs_strbuf_appendlit(buf, "[none] "); + } + } + + if (term->oper == EcsNot) { + ecs_strbuf_appendlit(buf, "!"); + } else if (term->oper == EcsOptional) { + ecs_strbuf_appendlit(buf, "?"); + } + + if (!src_set) { + flecs_query_str_add_id(world, buf, term, &term->first, false); + if (!second_set) { + ecs_strbuf_appendlit(buf, "()"); + } else { + ecs_strbuf_appendlit(buf, "(#0,"); + flecs_query_str_add_id(world, buf, term, &term->second, false); + ecs_strbuf_appendlit(buf, ")"); + } + } else { + ecs_id_t flags = term->id & ECS_ID_FLAGS_MASK; + if (flags && !ECS_HAS_ID_FLAG(flags, PAIR)) { + ecs_strbuf_appendstr(buf, ecs_id_flag_str(flags)); + ecs_strbuf_appendch(buf, '|'); + } + + flecs_query_str_add_id(world, buf, term, &term->first, false); + ecs_strbuf_appendlit(buf, "("); + if (term->src.id & EcsIsEntity && src_id == first_id) { + ecs_strbuf_appendlit(buf, "$"); + } else { + flecs_query_str_add_id(world, buf, term, &term->src, true); + } + if (second_set) { + ecs_strbuf_appendlit(buf, ","); + flecs_query_str_add_id(world, buf, term, &term->second, false); + } + ecs_strbuf_appendlit(buf, ")"); + } +} + +char* ecs_term_str( + const ecs_world_t *world, + const ecs_term_t *term) +{ + ecs_strbuf_t buf = ECS_STRBUF_INIT; + flecs_term_to_buf(world, term, &buf, 0); + return ecs_strbuf_get(&buf); +} + +char* ecs_query_str( + const ecs_query_t *q) +{ + ecs_check(q != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_world_t *world = q->world; + + ecs_strbuf_t buf = ECS_STRBUF_INIT; + const ecs_term_t *terms = q->terms; + int32_t i, count = q->term_count; + + for (i = 0; i < count; i ++) { + const ecs_term_t *term = &terms[i]; + + flecs_term_to_buf(world, term, &buf, i); + + if (i != (count - 1)) { + if (term->oper == EcsOr) { + ecs_strbuf_appendlit(&buf, " || "); + } else { + if (ECS_TERM_REF_ID(&term->first) != EcsScopeOpen) { + if (ECS_TERM_REF_ID(&term[1].first) != EcsScopeClose) { + ecs_strbuf_appendlit(&buf, ", "); + } + } + } + } + } + + return ecs_strbuf_get(&buf); +error: + return NULL; +} + +int32_t flecs_query_pivot_term( + const ecs_world_t *world, + const ecs_query_t *query) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL); + + const ecs_term_t *terms = query->terms; + int32_t i, term_count = query->term_count; + int32_t pivot_term = -1, min_count = -1, self_pivot_term = -1; + + for (i = 0; i < term_count; i ++) { + const ecs_term_t *term = &terms[i]; + ecs_id_t id = term->id; + + if ((term->oper != EcsAnd) || (i && (term[-1].oper == EcsOr))) { + continue; + } + + if (!ecs_term_match_this(term)) { + continue; + } + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (!idr) { + /* If one of the terms does not match with any data, iterator + * should not return anything */ + return -2; /* -2 indicates query doesn't match anything */ + } + + int32_t table_count = flecs_table_cache_count(&idr->cache); + if (min_count == -1 || table_count < min_count) { + min_count = table_count; + pivot_term = i; + if ((term->src.id & EcsTraverseFlags) == EcsSelf) { + self_pivot_term = i; + } + } + } + + if (self_pivot_term != -1) { + pivot_term = self_pivot_term; + } + + return pivot_term; +error: + return -2; +} + +void flecs_query_apply_iter_flags( + ecs_iter_t *it, + const ecs_query_t *query) +{ + ECS_BIT_COND(it->flags, EcsIterHasCondSet, + ECS_BIT_IS_SET(query->flags, EcsQueryHasCondSet)); + ECS_BIT_COND(it->flags, EcsIterNoData, query->data_fields == 0); +} diff --git a/vendors/flecs/src/query/util.h b/vendors/flecs/src/query/util.h new file mode 100644 index 000000000..cf1a09ee0 --- /dev/null +++ b/vendors/flecs/src/query/util.h @@ -0,0 +1,81 @@ +/** + * @file query/util.h + * @brief Utility functions + */ + +#include "types.h" + +/* Helper type for passing around context required for error messages */ +typedef struct { + const ecs_world_t *world; + const ecs_query_desc_t *desc; + ecs_query_t *query; + ecs_term_t *term; + int32_t term_index; +} ecs_query_validator_ctx_t; + +/* Convert integer to label */ +ecs_query_lbl_t flecs_itolbl( + int64_t val); + +/* Convert integer to variable id */ +ecs_var_id_t flecs_itovar( + int64_t val); + +/* Convert unsigned integer to variable id */ +ecs_var_id_t flecs_utovar( + uint64_t val); + +/* Get name for term ref */ +const char* flecs_term_ref_var_name( + ecs_term_ref_t *ref); + +/* Is term ref wildcard */ +bool flecs_term_ref_is_wildcard( + ecs_term_ref_t *ref); + +/* Does term use builtin predicates (eq, neq, ...)*/ +bool flecs_term_is_builtin_pred( + ecs_term_t *term); + +/* Does term have fixed id */ +bool flecs_term_is_fixed_id( + ecs_query_t *q, + ecs_term_t *term); + +/* Is term part of OR chain */ +bool flecs_term_is_or( + const ecs_query_t *q, + const ecs_term_t *term); + +/* Get ref flags (IsEntity) or IsVar) for ref (Src, First, Second) */ +ecs_flags16_t flecs_query_ref_flags( + ecs_flags16_t flags, + ecs_flags16_t kind); + +/* Check if variable is written */ +bool flecs_query_is_written( + ecs_var_id_t var_id, + uint64_t written); + +/* Check if ref is written (calls flecs_query_is_written)*/ +bool flecs_ref_is_written( + const ecs_query_op_t *op, + const ecs_query_ref_t *ref, + ecs_flags16_t kind, + uint64_t written); + +/* Get allocator from iterator */ +ecs_allocator_t* flecs_query_get_allocator( + const ecs_iter_t *it); + +/* Convert instruction kind to string */ +const char* flecs_query_op_str( + uint16_t kind); + +/* Convert term to string */ +void flecs_term_to_buf( + const ecs_world_t *world, + const ecs_term_t *term, + ecs_strbuf_t *buf, + int32_t t); diff --git a/vendors/flecs/src/query/validator.c b/vendors/flecs/src/query/validator.c new file mode 100644 index 000000000..3525cd8b0 --- /dev/null +++ b/vendors/flecs/src/query/validator.c @@ -0,0 +1,1831 @@ +/** + * @file query/validator.c + * @brief Validate and finalize queries. + */ + +#include "../private_api.h" +#include + +#ifdef FLECS_SCRIPT +#include "../addons/script/script.h" +#endif + +static +void flecs_query_validator_error( + const ecs_query_validator_ctx_t *ctx, + const char *fmt, + ...) +{ + ecs_strbuf_t buf = ECS_STRBUF_INIT; + + if (ctx->desc && ctx->desc->expr) { + ecs_strbuf_appendlit(&buf, "expr: "); + ecs_strbuf_appendstr(&buf, ctx->desc->expr); + ecs_strbuf_appendlit(&buf, "\n"); + } + + if (ctx->query) { + ecs_query_t *query = ctx->query; + const ecs_term_t *terms = query->terms; + int32_t i, count = query->term_count; + + for (i = 0; i < count; i ++) { + const ecs_term_t *term = &terms[i]; + if (ctx->term_index == i) { + ecs_strbuf_appendlit(&buf, " > "); + } else { + ecs_strbuf_appendlit(&buf, " "); + } + flecs_term_to_buf(ctx->world, term, &buf, i); + if (term->oper == EcsOr) { + ecs_strbuf_appendlit(&buf, " ||"); + } else if (i != (count - 1)) { + ecs_strbuf_appendlit(&buf, ","); + } + ecs_strbuf_appendlit(&buf, "\n"); + } + } else { + ecs_strbuf_appendlit(&buf, " > "); + flecs_term_to_buf(ctx->world, ctx->term, &buf, 0); + ecs_strbuf_appendlit(&buf, "\n"); + } + + char *expr = ecs_strbuf_get(&buf); + const char *name = NULL; + if (ctx->query && ctx->query->entity) { + name = ecs_get_name(ctx->query->world, ctx->query->entity); + } + + va_list args; + va_start(args, fmt); + char *msg = flecs_vasprintf(fmt, args); + ecs_parser_error(name, NULL, 0, "%s\n%s", msg, expr); + ecs_os_free(msg); + ecs_os_free(expr); + + va_end(args); +} + +static +int flecs_term_ref_finalize_flags( + ecs_term_ref_t *ref, + ecs_query_validator_ctx_t *ctx) +{ + if ((ref->id & EcsIsEntity) && (ref->id & EcsIsVariable)) { + flecs_query_validator_error(ctx, "cannot set both IsEntity and IsVariable"); + return -1; + } + + if (ref->name && ref->name[0] == '$') { + if (!ref->name[1]) { + if (!(ref->id & EcsIsName)) { + if (ref->id & ~EcsTermRefFlags) { + flecs_query_validator_error(ctx, + "conflicting values for .name and .id"); + return -1; + } + + ref->id |= EcsVariable; + ref->id |= EcsIsVariable; + } + } else { + ref->name = &ref->name[1]; + ref->id |= EcsIsVariable; + } + } + + if (!(ref->id & (EcsIsEntity|EcsIsVariable|EcsIsName))) { + if (ECS_TERM_REF_ID(ref) || ref->name) { + if (ECS_TERM_REF_ID(ref) == EcsThis || + ECS_TERM_REF_ID(ref) == EcsWildcard || + ECS_TERM_REF_ID(ref) == EcsAny || + ECS_TERM_REF_ID(ref) == EcsVariable) + { + /* Builtin variable ids default to variable */ + ref->id |= EcsIsVariable; + } else { + ref->id |= EcsIsEntity; + } + } + } + + return 0; +} + +static +int flecs_term_ref_lookup( + const ecs_world_t *world, + ecs_entity_t scope, + ecs_term_ref_t *ref, + ecs_query_validator_ctx_t *ctx) +{ + const char *name = ref->name; + if (!name) { + return 0; + } + + if (ref->id & EcsIsVariable) { + if (!ecs_os_strcmp(name, "this")) { + ref->id = EcsThis | ECS_TERM_REF_FLAGS(ref); + ref->name = NULL; + return 0; + } + return 0; + } else if (ref->id & EcsIsName) { + if (ref->name == NULL) { + flecs_query_validator_error(ctx, "IsName flag specified without name"); + return -1; + } + return 0; + } + + ecs_assert(ref->id & EcsIsEntity, ECS_INTERNAL_ERROR, NULL); + + if (flecs_identifier_is_0(name)) { + if (ECS_TERM_REF_ID(ref)) { + flecs_query_validator_error( + ctx, "name '0' does not match entity id"); + return -1; + } + ref->name = NULL; + return 0; + } + + ecs_entity_t e = 0; + if (scope) { + e = ecs_lookup_child(world, scope, name); + } + + if (!e) { + e = ecs_lookup(world, name); + } + + if (!e) { + if (ctx->query && (ctx->query->flags & EcsQueryAllowUnresolvedByName)) { + ref->id |= EcsIsName; + ref->id &= ~EcsIsEntity; + return 0; + } else { + flecs_query_validator_error(ctx, "unresolved identifier '%s'", name); + return -1; + } + } + + ecs_entity_t ref_id = ECS_TERM_REF_ID(ref); + if (ref_id && ref_id != e) { + char *e_str = ecs_get_path(world, ref_id); + flecs_query_validator_error(ctx, "name '%s' does not match term.id '%s'", + name, e_str); + ecs_os_free(e_str); + return -1; + } + + ref->id = e | ECS_TERM_REF_FLAGS(ref); + ref_id = ECS_TERM_REF_ID(ref); + + if (!ecs_os_strcmp(name, "*") || !ecs_os_strcmp(name, "_") || + !ecs_os_strcmp(name, "$")) + { + ref->id &= ~EcsIsEntity; + ref->id |= EcsIsVariable; + } + + /* Check if looked up id is alive (relevant for numerical ids) */ + if (!(ref->id & EcsIsName) && ref_id) { + if (!ecs_is_alive(world, ref_id)) { + flecs_query_validator_error(ctx, "identifier '%s' is not alive", ref->name); + return -1; + } + + ref->name = NULL; + return 0; + } + + return 0; +} + +static +ecs_id_t flecs_wildcard_to_any(ecs_id_t id) { + ecs_id_t flags = id & EcsTermRefFlags; + + if (ECS_IS_PAIR(id)) { + ecs_entity_t first = ECS_PAIR_FIRST(id); + ecs_entity_t second = ECS_PAIR_SECOND(id); + if (first == EcsWildcard) id = ecs_pair(EcsAny, second); + if (second == EcsWildcard) id = ecs_pair(ECS_PAIR_FIRST(id), EcsAny); + } else if ((id & ~EcsTermRefFlags) == EcsWildcard) { + id = EcsAny; + } + + return id | flags; +} + +static +int flecs_term_refs_finalize( + const ecs_world_t *world, + ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) +{ + ecs_term_ref_t *src = &term->src; + ecs_term_ref_t *first = &term->first; + ecs_term_ref_t *second = &term->second; + + /* Include subsets for component by default, to support inheritance */ + if (!(first->id & EcsTraverseFlags)) { + first->id |= EcsSelf; + } + + /* Traverse Self by default for pair target */ + if (!(second->id & EcsTraverseFlags)) { + if (ECS_TERM_REF_ID(second) || second->name || (second->id & EcsIsEntity)) { + second->id |= EcsSelf; + } + } + + /* Source defaults to This */ + if (!ECS_TERM_REF_ID(src) && (src->name == NULL) && !(src->id & EcsIsEntity)) { + src->id = EcsThis | ECS_TERM_REF_FLAGS(src); + src->id |= EcsIsVariable; + } + + /* Initialize term identifier flags */ + if (flecs_term_ref_finalize_flags(src, ctx)) { + return -1; + } + + if (flecs_term_ref_finalize_flags(first, ctx)) { + return -1; + } + + if (flecs_term_ref_finalize_flags(second, ctx)) { + return -1; + } + + /* Lookup term identifiers by name */ + if (flecs_term_ref_lookup(world, 0, src, ctx)) { + return -1; + } + if (flecs_term_ref_lookup(world, 0, first, ctx)) { + return -1; + } + + ecs_entity_t first_id = 0; + ecs_entity_t oneof = 0; + if (first->id & EcsIsEntity) { + first_id = ECS_TERM_REF_ID(first); + + if (!first_id) { + flecs_query_validator_error(ctx, "invalid \"0\" for first.name"); + return -1; + } + + /* If first element of pair has OneOf property, lookup second element of + * pair in the value of the OneOf property */ + oneof = flecs_get_oneof(world, first_id); + } + + if (flecs_term_ref_lookup(world, oneof, &term->second, ctx)) { + return -1; + } + + /* If source is 0, reset traversal flags */ + if (ECS_TERM_REF_ID(src) == 0 && src->id & EcsIsEntity) { + src->id &= ~EcsTraverseFlags; + term->trav = 0; + } + + /* If source is wildcard, term won't return any data */ + if ((src->id & EcsIsVariable) && ecs_id_is_wildcard(ECS_TERM_REF_ID(src))) { + term->inout = EcsInOutNone; + } + + /* If operator is Not, automatically convert wildcard queries to any */ + if (term->oper == EcsNot) { + if (ECS_TERM_REF_ID(first) == EcsWildcard) { + first->id = EcsAny | ECS_TERM_REF_FLAGS(first); + } + + if (ECS_TERM_REF_ID(second) == EcsWildcard) { + second->id = EcsAny | ECS_TERM_REF_FLAGS(second); + } + + term->id = flecs_wildcard_to_any(term->id); + } + + return 0; +} + +static +ecs_entity_t flecs_term_ref_get_entity( + const ecs_term_ref_t *ref) +{ + if (ref->id & EcsIsEntity) { + return ECS_TERM_REF_ID(ref); /* Id is known */ + } else if (ref->id & EcsIsVariable) { + /* Return wildcard for variables, as they aren't known yet */ + if (ECS_TERM_REF_ID(ref) != EcsAny) { + /* Any variable should not use wildcard, as this would return all + * ids matching a wildcard, whereas Any returns the first match */ + return EcsWildcard; + } else { + return EcsAny; + } + } else { + return 0; /* Term id is uninitialized */ + } +} + +static +int flecs_term_populate_id( + ecs_term_t *term) +{ + ecs_entity_t first = flecs_term_ref_get_entity(&term->first); + ecs_entity_t second = flecs_term_ref_get_entity(&term->second); + ecs_id_t flags = term->id & ECS_ID_FLAGS_MASK; + + if (first & ECS_ID_FLAGS_MASK) { + return -1; + } + if (second & ECS_ID_FLAGS_MASK) { + return -1; + } + + if ((second || (term->second.id & EcsIsEntity))) { + flags |= ECS_PAIR; + } + + if (!second && !ECS_HAS_ID_FLAG(flags, PAIR)) { + term->id = first | flags; + } else { + term->id = ecs_pair(first, second) | flags; + } + + return 0; +} + +static +int flecs_term_populate_from_id( + const ecs_world_t *world, + ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) +{ + ecs_entity_t first = 0; + ecs_entity_t second = 0; + + if (ECS_HAS_ID_FLAG(term->id, PAIR)) { + first = ECS_PAIR_FIRST(term->id); + second = ECS_PAIR_SECOND(term->id); + + if (!first) { + flecs_query_validator_error(ctx, "missing first element in term.id"); + return -1; + } + if (!second) { + if (first != EcsChildOf) { + flecs_query_validator_error(ctx, "missing second element in term.id"); + return -1; + } else { + /* (ChildOf, 0) is allowed so query can be used to efficiently + * query for root entities */ + } + } + } else { + first = term->id & ECS_COMPONENT_MASK; + if (!first) { + flecs_query_validator_error(ctx, "missing first element in term.id"); + return -1; + } + } + + ecs_entity_t term_first = flecs_term_ref_get_entity(&term->first); + if (term_first) { + if ((uint32_t)term_first != (uint32_t)first) { + flecs_query_validator_error(ctx, "mismatch between term.id and term.first"); + return -1; + } + } else { + ecs_entity_t first_id = ecs_get_alive(world, first); + if (!first_id) { + term->first.id = first | ECS_TERM_REF_FLAGS(&term->first); + } else { + term->first.id = first_id | ECS_TERM_REF_FLAGS(&term->first); + } + } + + ecs_entity_t term_second = flecs_term_ref_get_entity(&term->second); + if (term_second) { + if ((uint32_t)term_second != second) { + flecs_query_validator_error(ctx, "mismatch between term.id and term.second"); + return -1; + } + } else if (second) { + ecs_entity_t second_id = ecs_get_alive(world, second); + if (!second_id) { + term->second.id = second | ECS_TERM_REF_FLAGS(&term->second); + } else { + term->second.id = second_id | ECS_TERM_REF_FLAGS(&term->second); + } + } + + return 0; +} + +static +int flecs_term_verify_eq_pred( + const ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) +{ + const ecs_term_ref_t *second = &term->second; + const ecs_term_ref_t *src = &term->src; + ecs_entity_t first_id = ECS_TERM_REF_ID(&term->first); + ecs_entity_t second_id = ECS_TERM_REF_ID(&term->second); + ecs_entity_t src_id = ECS_TERM_REF_ID(&term->src); + + if (term->oper != EcsAnd && term->oper != EcsNot && term->oper != EcsOr) { + flecs_query_validator_error(ctx, "invalid operator combination"); + goto error; + } + + if ((src->id & EcsIsName) && (second->id & EcsIsName)) { + flecs_query_validator_error(ctx, "both sides of operator cannot be a name"); + goto error; + } + + if ((src->id & EcsIsEntity) && (second->id & EcsIsEntity)) { + flecs_query_validator_error(ctx, "both sides of operator cannot be an entity"); + goto error; + } + + if (!(src->id & EcsIsVariable)) { + flecs_query_validator_error(ctx, "left-hand of operator must be a variable"); + goto error; + } + + if (first_id == EcsPredMatch && !(second->id & EcsIsName)) { + flecs_query_validator_error(ctx, "right-hand of match operator must be a string"); + goto error; + } + + if ((src->id & EcsIsVariable) && (second->id & EcsIsVariable)) { + if (src_id && src_id == second_id) { + flecs_query_validator_error(ctx, "both sides of operator are equal"); + goto error; + } + if (src->name && second->name && !ecs_os_strcmp(src->name, second->name)) { + flecs_query_validator_error(ctx, "both sides of operator are equal"); + goto error; + } + } + + if (first_id == EcsPredEq) { + if (second_id == EcsPredEq || second_id == EcsPredMatch) { + flecs_query_validator_error(ctx, + "invalid right-hand side for equality operator"); + goto error; + } + } + + return 0; +error: + return -1; +} + +static +int flecs_term_verify( + const ecs_world_t *world, + const ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) +{ + const ecs_term_ref_t *first = &term->first; + const ecs_term_ref_t *second = &term->second; + const ecs_term_ref_t *src = &term->src; + ecs_entity_t first_id = 0, second_id = 0; + ecs_id_t flags = term->id & ECS_ID_FLAGS_MASK; + ecs_id_t id = term->id; + + if ((src->id & EcsIsName) && (second->id & EcsIsName)) { + flecs_query_validator_error(ctx, "mismatch between term.id_flags & term.id"); + return -1; + } + + ecs_entity_t src_id = ECS_TERM_REF_ID(src); + if (first->id & EcsIsEntity) { + first_id = ECS_TERM_REF_ID(first); + } + + if (second->id & EcsIsEntity) { + second_id = ECS_TERM_REF_ID(second); + } + + if (first_id == EcsPredEq || first_id == EcsPredMatch || first_id == EcsPredLookup) { + return flecs_term_verify_eq_pred(term, ctx); + } + + if (ecs_term_ref_is_set(second) && !ECS_HAS_ID_FLAG(flags, PAIR)) { + flecs_query_validator_error(ctx, "expected PAIR flag for term with pair"); + return -1; + } else if (!ecs_term_ref_is_set(second) && ECS_HAS_ID_FLAG(flags, PAIR)) { + if (first_id != EcsChildOf) { + flecs_query_validator_error(ctx, "unexpected PAIR flag for term without pair"); + return -1; + } else { + /* Exception is made for ChildOf so we can use (ChildOf, 0) to match + * all entities in the root */ + } + } + + if (!ecs_term_ref_is_set(src)) { + flecs_query_validator_error(ctx, "term.src is not initialized"); + return -1; + } + + if (!ecs_term_ref_is_set(first)) { + flecs_query_validator_error(ctx, "term.first is not initialized"); + return -1; + } + + if (ECS_HAS_ID_FLAG(flags, PAIR)) { + if (!ECS_PAIR_FIRST(id)) { + flecs_query_validator_error(ctx, "invalid 0 for first element in pair id"); + return -1; + } + if ((ECS_PAIR_FIRST(id) != EcsChildOf) && !ECS_PAIR_SECOND(id)) { + flecs_query_validator_error(ctx, "invalid 0 for second element in pair id"); + return -1; + } + + if ((first->id & EcsIsEntity) && + (ecs_entity_t_lo(first_id) != ECS_PAIR_FIRST(id))) + { + flecs_query_validator_error(ctx, "mismatch between term.id and term.first"); + return -1; + } + if ((first->id & EcsIsVariable) && + !ecs_id_is_wildcard(ECS_PAIR_FIRST(id))) + { + char *id_str = ecs_id_str(world, id); + flecs_query_validator_error(ctx, + "expected wildcard for variable term.first (got %s)", id_str); + ecs_os_free(id_str); + return -1; + } + + if ((second->id & EcsIsEntity) && + (ecs_entity_t_lo(second_id) != ECS_PAIR_SECOND(id))) + { + flecs_query_validator_error(ctx, "mismatch between term.id and term.second"); + return -1; + } + if ((second->id & EcsIsVariable) && + !ecs_id_is_wildcard(ECS_PAIR_SECOND(id))) + { + char *id_str = ecs_id_str(world, id); + flecs_query_validator_error(ctx, + "expected wildcard for variable term.second (got %s)", id_str); + ecs_os_free(id_str); + return -1; + } + } else { + ecs_entity_t component = id & ECS_COMPONENT_MASK; + if (!component) { + flecs_query_validator_error(ctx, "missing component id"); + return -1; + } + if ((first->id & EcsIsEntity) && + (ecs_entity_t_lo(first_id) != ecs_entity_t_lo(component))) + { + flecs_query_validator_error(ctx, "mismatch between term.id and term.first"); + return -1; + } + if ((first->id & EcsIsVariable) && !ecs_id_is_wildcard(component)) { + char *id_str = ecs_id_str(world, id); + flecs_query_validator_error(ctx, + "expected wildcard for variable term.first (got %s)", id_str); + ecs_os_free(id_str); + return -1; + } + } + + if (first_id) { + if (ecs_term_ref_is_set(second)) { + ecs_flags64_t mask = EcsIsEntity | EcsIsVariable; + if ((src->id & mask) == (second->id & mask)) { + bool is_same = false; + if (src->id & EcsIsEntity) { + is_same = src_id == second_id; + } else if (src->name && second->name) { + is_same = !ecs_os_strcmp(src->name, second->name); + } + + if (is_same && ecs_has_id(world, first_id, EcsAcyclic) + && !(term->flags_ & EcsTermReflexive)) + { + char *pred_str = ecs_get_path(world, term->first.id); + flecs_query_validator_error(ctx, "term with acyclic relationship" + " '%s' cannot have same subject and object", pred_str); + ecs_os_free(pred_str); + return -1; + } + } + } + + if (second_id && !ecs_id_is_wildcard(second_id)) { + ecs_entity_t oneof = flecs_get_oneof(world, first_id); + if (oneof) { + if (!ecs_has_pair(world, second_id, EcsChildOf, oneof)) { + char *second_str = ecs_get_path(world, second_id); + char *oneof_str = ecs_get_path(world, oneof); + char *id_str = ecs_id_str(world, term->id); + flecs_query_validator_error(ctx, + "invalid target '%s' for %s: must be child of '%s'", + second_str, id_str, oneof_str); + ecs_os_free(second_str); + ecs_os_free(oneof_str); + ecs_os_free(id_str); + return -1; + } + } + } + } + + if (term->trav) { + if (!ecs_has_id(world, term->trav, EcsTraversable)) { + char *r_str = ecs_get_path(world, term->trav); + flecs_query_validator_error(ctx, + "cannot traverse non-traversable relationship '%s'", r_str); + ecs_os_free(r_str); + return -1; + } + } + + return 0; +} + +static +int flecs_term_finalize( + const ecs_world_t *world, + ecs_term_t *term, + ecs_query_validator_ctx_t *ctx) +{ + ctx->term = term; + + ecs_term_ref_t *src = &term->src; + ecs_term_ref_t *first = &term->first; + ecs_term_ref_t *second = &term->second; + ecs_flags64_t first_flags = ECS_TERM_REF_FLAGS(first); + ecs_flags64_t second_flags = ECS_TERM_REF_FLAGS(second); + + if (first->name && (first->id & ~EcsTermRefFlags)) { + flecs_query_validator_error(ctx, + "first.name and first.id have competing values"); + return -1; + } + if (src->name && (src->id & ~EcsTermRefFlags)) { + flecs_query_validator_error(ctx, + "src.name and src.id have competing values"); + return -1; + } + if (second->name && (second->id & ~EcsTermRefFlags)) { + flecs_query_validator_error(ctx, + "second.name and second.id have competing values"); + return -1; + } + + if (term->id & ~ECS_ID_FLAGS_MASK) { + if (flecs_term_populate_from_id(world, term, ctx)) { + return -1; + } + } + + if (flecs_term_refs_finalize(world, term, ctx)) { + return -1; + } + + ecs_entity_t first_id = ECS_TERM_REF_ID(first); + ecs_entity_t second_id = ECS_TERM_REF_ID(second); + ecs_entity_t src_id = ECS_TERM_REF_ID(src); + + if ((first->id & EcsIsVariable) && (first_id == EcsAny)) { + term->flags_ |= EcsTermMatchAny; + } + + if ((second->id & EcsIsVariable) && (second_id == EcsAny)) { + term->flags_ |= EcsTermMatchAny; + } + + if ((src->id & EcsIsVariable) && (src_id == EcsAny)) { + term->flags_ |= EcsTermMatchAnySrc; + } + + ecs_flags64_t ent_var_mask = EcsIsEntity | EcsIsVariable; + + /* If EcsVariable is used by itself, assign to predicate (singleton) */ + if ((ECS_TERM_REF_ID(src) == EcsVariable) && (src->id & EcsIsVariable)) { + src->id = first->id | ECS_TERM_REF_FLAGS(src); + src->id &= ~ent_var_mask; + src->id |= first->id & ent_var_mask; + src->name = first->name; + } + + if ((ECS_TERM_REF_ID(second) == EcsVariable) && (second->id & EcsIsVariable)) { + second->id = first->id | ECS_TERM_REF_FLAGS(second); + second->id &= ~ent_var_mask; + second->id |= first->id & ent_var_mask; + second->name = first->name; + } + + if (!(term->id & ~ECS_ID_FLAGS_MASK)) { + if (flecs_term_populate_id(term)) { + return -1; + } + } + + /* If term queries for !(ChildOf, _), translate it to the builtin + * (ChildOf, 0) index which is a cheaper way to find root entities */ + if (term->oper == EcsNot && term->id == ecs_pair(EcsChildOf, EcsAny)) { + /* Only if the source is not EcsAny */ + if (!(ECS_TERM_REF_ID(&term->src) == EcsAny && (term->src.id & EcsIsVariable))) { + term->oper = EcsAnd; + term->id = ecs_pair(EcsChildOf, 0); + second->id = 0; + second->id |= EcsSelf|EcsIsEntity; + } + } + + ecs_entity_t first_entity = 0; + if ((first->id & EcsIsEntity)) { + first_entity = first_id; + } + + ecs_id_record_t *idr = flecs_id_record_get(world, term->id); + ecs_flags32_t id_flags = 0; + if (idr) { + id_flags = idr->flags; + } else if (ECS_IS_PAIR(term->id)) { + ecs_id_record_t *wc_idr = flecs_id_record_get( + world, ecs_pair(ECS_PAIR_FIRST(term->id), EcsWildcard)); + if (wc_idr) { + id_flags = wc_idr->flags; + } + } + + if (src_id || src->name) { + if (!(term->src.id & EcsTraverseFlags)) { + if (id_flags & EcsIdOnInstantiateInherit) { + term->src.id |= EcsSelf|EcsUp; + if (!term->trav) { + term->trav = EcsIsA; + } + } else { + term->src.id |= EcsSelf; + + if (term->trav) { + char *idstr = ecs_id_str(world, term->id); + flecs_query_validator_error(ctx, ".trav specified for " + "'%s' which can't be inherited", idstr); + ecs_os_free(idstr); + return -1; + } + } + } + + if (term->first.id & EcsCascade) { + flecs_query_validator_error(ctx, + "cascade modifier invalid for term.first"); + return -1; + } + + if (term->second.id & EcsCascade) { + flecs_query_validator_error(ctx, + "cascade modifier invalid for term.second"); + return -1; + } + + if (term->first.id & EcsDesc) { + flecs_query_validator_error(ctx, + "desc modifier invalid for term.first"); + return -1; + } + + if (term->second.id & EcsDesc) { + flecs_query_validator_error(ctx, + "desc modifier invalid for term.second"); + return -1; + } + + if (term->src.id & EcsDesc && !(term->src.id & EcsCascade)) { + flecs_query_validator_error(ctx, + "desc modifier invalid without cascade"); + return -1; + } + + if (term->src.id & EcsCascade) { + /* Cascade always implies up traversal */ + term->src.id |= EcsUp; + } + + if ((src->id & EcsUp) && !term->trav) { + /* When traversal flags are specified but no traversal relationship, + * default to ChildOf, which is the most common kind of traversal. */ + term->trav = EcsChildOf; + } + } + + if (!(id_flags & EcsIdOnInstantiateInherit) && (term->trav == EcsIsA)) { + if (src->id & EcsUp) { + char *idstr = ecs_id_str(world, term->id); + flecs_query_validator_error(ctx, "IsA traversal not allowed " + "for '%s', add the (OnInstantiate, Inherit) trait", idstr); + ecs_os_free(idstr); + return -1; + } + } + + if (first_entity) { + /* Only enable inheritance for ids which are inherited from at the time + * of query creation. To force component inheritance to be evaluated, + * an application can explicitly set traversal flags. */ + if (flecs_id_record_get(world, ecs_pair(EcsIsA, first->id))) { + if (!((first_flags & EcsTraverseFlags) == EcsSelf)) { + term->flags_ |= EcsTermIdInherited; + } + } + + /* If component id is final, don't attempt component inheritance */ + ecs_record_t *first_record = flecs_entities_get(world, first_entity); + ecs_table_t *first_table = first_record ? first_record->table : NULL; + if (first_table) { + if (ecs_term_ref_is_set(second)) { + /* Add traversal flags for transitive relationships */ + if (!((second_flags & EcsTraverseFlags) == EcsSelf)) { + if (!((src->id & EcsIsVariable) && (src_id == EcsAny))) { + if (!((second->id & EcsIsVariable) && (second_id == EcsAny))) { + if (ecs_table_has_id(world, first_table, EcsTransitive)) { + term->flags_ |= EcsTermTransitive; + } + + if (ecs_table_has_id(world, first_table, EcsReflexive)) { + term->flags_ |= EcsTermReflexive; + } + } + } + } + + /* Check if term is union */ + if (ecs_table_has_id(world, first_table, EcsUnion)) { + /* Any wildcards don't need special handling as they just return + * (Rel, *). */ + if (ECS_IS_PAIR(term->id) && ECS_PAIR_SECOND(term->id) != EcsAny) { + term->flags_ |= EcsTermIsUnion; + } + } + } + } + + /* Check if term has toggleable component */ + if (id_flags & EcsIdCanToggle) { + /* If the term isn't matched on a #0 source */ + if (term->src.id != EcsIsEntity) { + term->flags_ |= EcsTermIsToggle; + + } + } + + /* Check if this is a member query */ +#ifdef FLECS_META + if (ecs_id(EcsMember) != 0) { + if (first_entity) { + if (ecs_has(world, first_entity, EcsMember)) { + term->flags_ |= EcsTermIsMember; + } + } + } +#endif + } + + if (ECS_TERM_REF_ID(first) == EcsVariable) { + flecs_query_validator_error(ctx, "invalid $ for term.first"); + return -1; + } + + if (term->oper == EcsAndFrom || term->oper == EcsOrFrom || term->oper == EcsNotFrom) { + if (term->inout != EcsInOutDefault && term->inout != EcsInOutNone) { + flecs_query_validator_error(ctx, + "invalid inout value for AndFrom/OrFrom/NotFrom term"); + return -1; + } + } + + /* Is term trivial/cacheable */ + bool cacheable_term = true; + bool trivial_term = true; + if (term->oper != EcsAnd || term->flags_ & EcsTermIsOr) { + trivial_term = false; + } + + if (ecs_id_is_wildcard(term->id)) { + if (!(id_flags & EcsIdExclusive)) { + trivial_term = false; + } + + if (first->id & EcsIsVariable) { + if (!ecs_id_is_wildcard(first_id) || first_id == EcsAny) { + trivial_term = false; + cacheable_term = false; + } + } + + if (second->id & EcsIsVariable) { + if (!ecs_id_is_wildcard(second_id) || second_id == EcsAny) { + trivial_term = false; + cacheable_term = false; + } + } + } + + if (!ecs_term_match_this(term)) { + trivial_term = false; + } + + if (term->flags_ & EcsTermTransitive) { + trivial_term = false; + cacheable_term = false; + } + + if (term->flags_ & EcsTermIdInherited) { + trivial_term = false; + cacheable_term = false; + } + + if (term->flags_ & EcsTermReflexive) { + trivial_term = false; + cacheable_term = false; + } + + if (term->trav && term->trav != EcsIsA) { + trivial_term = false; + } + + if (!(src->id & EcsSelf)) { + trivial_term = false; + } + + if (((ECS_TERM_REF_ID(&term->first) == EcsPredEq) || + (ECS_TERM_REF_ID(&term->first) == EcsPredMatch) || + (ECS_TERM_REF_ID(&term->first) == EcsPredLookup)) && + (term->first.id & EcsIsEntity)) + { + trivial_term = false; + cacheable_term = false; + } + + if (ECS_TERM_REF_ID(src) != EcsThis) { + cacheable_term = false; + } + + if (term->id == ecs_childof(0)) { + cacheable_term = false; + } + + if (term->flags_ & EcsTermIsMember) { + trivial_term = false; + cacheable_term = false; + } + + if (term->flags_ & EcsTermIsToggle) { + trivial_term = false; + } + + if (term->flags_ & EcsTermIsUnion) { + trivial_term = false; + cacheable_term = false; + } + + ECS_BIT_COND16(term->flags_, EcsTermIsTrivial, trivial_term); + ECS_BIT_COND16(term->flags_, EcsTermIsCacheable, cacheable_term); + + if (flecs_term_verify(world, term, ctx)) { + return -1; + } + + return 0; +} + +bool flecs_identifier_is_0( + const char *id) +{ + return id[0] == '#' && id[1] == '0' && !id[2]; +} + +bool ecs_term_ref_is_set( + const ecs_term_ref_t *ref) +{ + return ECS_TERM_REF_ID(ref) != 0 || ref->name != NULL || ref->id & EcsIsEntity; +} + +bool ecs_term_is_initialized( + const ecs_term_t *term) +{ + return term->id != 0 || ecs_term_ref_is_set(&term->first); +} + +bool ecs_term_match_this( + const ecs_term_t *term) +{ + return (term->src.id & EcsIsVariable) && + (ECS_TERM_REF_ID(&term->src) == EcsThis); +} + +bool ecs_term_match_0( + const ecs_term_t *term) +{ + return (!ECS_TERM_REF_ID(&term->src) && (term->src.id & EcsIsEntity)); +} + +int ecs_term_finalize( + const ecs_world_t *world, + ecs_term_t *term) +{ + ecs_query_validator_ctx_t ctx = {0}; + ctx.world = world; + ctx.term = term; + return flecs_term_finalize(world, term, &ctx); +} + +static +ecs_term_t* flecs_query_or_other_type( + ecs_query_t *q, + int32_t t) +{ + ecs_term_t *term = &q->terms[t]; + ecs_term_t *first = NULL; + while (t--) { + if (q->terms[t].oper != EcsOr) { + break; + } + first = &q->terms[t]; + } + + if (first) { + ecs_world_t *world = q->world; + const ecs_type_info_t *first_type = ecs_get_type_info(world, first->id); + const ecs_type_info_t *term_type = ecs_get_type_info(world, term->id); + + if (first_type == term_type) { + return NULL; + } + return first; + } else { + return NULL; + } +} + +static +void flecs_normalize_term_name( + ecs_term_ref_t *ref) +{ + if (ref->name && ref->name[0] == '$' && ref->name[1]) { + ecs_assert(ref->id & EcsIsVariable, ECS_INTERNAL_ERROR, NULL); + const char *old = ref->name; + ref->name = &old[1]; + + if (!ecs_os_strcmp(ref->name, "this")) { + ref->name = NULL; + ref->id |= EcsThis; + } + } +} + +static +int flecs_query_finalize_terms( + const ecs_world_t *world, + ecs_query_t *q, + const ecs_query_desc_t *desc) +{ + int8_t i, term_count = q->term_count, field_count = 0; + ecs_term_t *terms = q->terms; + int32_t scope_nesting = 0, cacheable_terms = 0; + bool cond_set = false; + + ecs_query_validator_ctx_t ctx = {0}; + ctx.world = world; + ctx.query = q; + ctx.desc = desc; + + q->flags |= EcsQueryMatchOnlyThis; + + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + if (term->oper == EcsOr) { + term->flags_ |= EcsTermIsOr; + if (i != (term_count - 1)) { + term[1].flags_ |= EcsTermIsOr; + } + } + } + + bool cacheable = true; + bool match_nothing = true; + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + bool prev_is_or = i && term[-1].oper == EcsOr; + bool nodata_term = false; + ctx.term_index = i; + + if (flecs_term_finalize(world, term, &ctx)) { + return -1; + } + + if (term->src.id != EcsIsEntity) { + /* If term doesn't match 0 entity, query doesn't match nothing */ + match_nothing = false; + } + + if (scope_nesting) { + /* Terms inside a scope are not cacheable */ + ECS_BIT_CLEAR16(term->flags_, EcsTermIsCacheable); + } + + /* If one of the terms in an OR chain isn't cacheable, none are */ + if (term->flags_ & EcsTermIsCacheable) { + /* Current term is marked as cacheable. Check if it is part of an OR + * chain, and if so, the previous term was also cacheable. */ + if (prev_is_or) { + if (term[-1].flags_ & EcsTermIsCacheable) { + cacheable_terms ++; + } else { + ECS_BIT_CLEAR16(term->flags_, EcsTermIsCacheable); + } + } else { + cacheable_terms ++; + } + + /* Toggle terms may be cacheable for fetching the initial component, + * but require an additional toggle instruction for evaluation. */ + if (term->flags_ & EcsTermIsToggle) { + cacheable = false; + } + } else if (prev_is_or) { + /* Current term is not cacheable. If it is part of an OR chain, mark + * previous terms in the chain as also not cacheable. */ + int32_t j; + for (j = i - 1; j >= 0; j --) { + if (terms[j].oper != EcsOr) { + break; + } + if (terms[j].flags_ & EcsTermIsCacheable) { + cacheable_terms --; + ECS_BIT_CLEAR16(terms[j].flags_, EcsTermIsCacheable); + } + } + } + + if (prev_is_or) { + if (ECS_TERM_REF_ID(&term[-1].src) != ECS_TERM_REF_ID(&term->src)) { + flecs_query_validator_error(&ctx, "mismatching src.id for OR terms"); + return -1; + } + if (term->oper != EcsOr && term->oper != EcsAnd) { + flecs_query_validator_error(&ctx, + "term after OR operator must use AND operator"); + return -1; + } + } else { + field_count ++; + } + + term->field_index = flecs_ito(int8_t, field_count - 1); + + if (ecs_id_is_wildcard(term->id)) { + q->flags |= EcsQueryMatchWildcards; + } else if (!(term->flags_ & EcsTermIsOr)) { + ECS_TERMSET_SET(q->static_id_fields, 1u << term->field_index); + } + + if (ecs_term_match_this(term)) { + ECS_BIT_SET(q->flags, EcsQueryMatchThis); + } else { + ECS_BIT_CLEAR(q->flags, EcsQueryMatchOnlyThis); + } + + if (ECS_TERM_REF_ID(term) == EcsPrefab) { + ECS_BIT_SET(q->flags, EcsQueryMatchPrefab); + } + if (ECS_TERM_REF_ID(term) == EcsDisabled && (term->src.id & EcsSelf)) { + ECS_BIT_SET(q->flags, EcsQueryMatchDisabled); + } + + if (term->oper == EcsNot && term->inout == EcsInOutDefault) { + term->inout = EcsInOutNone; + } + + if ((term->id == EcsWildcard) || (term->id == + ecs_pair(EcsWildcard, EcsWildcard))) + { + /* If term type is unknown beforehand, default the inout type to + * none. This prevents accidentally requesting lots of components, + * which can put stress on serializer code. */ + if (term->inout == EcsInOutDefault) { + term->inout = EcsInOutNone; + } + } + + if (term->src.id == EcsIsEntity) { + nodata_term = true; + } else if (term->inout == EcsInOutNone) { + nodata_term = true; + } else if (!ecs_get_type_info(world, term->id)) { + nodata_term = true; + } else if (term->flags_ & EcsTermIsUnion) { + nodata_term = true; + } else if (term->flags_ & EcsTermIsMember) { + nodata_term = true; + } else if (scope_nesting) { + nodata_term = true; + } else { + if (ecs_id_is_tag(world, term->id)) { + nodata_term = true; + } else if ((ECS_PAIR_SECOND(term->id) == EcsWildcard) || + (ECS_PAIR_SECOND(term->id) == EcsAny)) + { + /* If the second element of a pair is a wildcard and the first + * element is not a type, we can't know in advance what the + * type of the term is, so it can't provide data. */ + if (!ecs_get_type_info(world, ecs_pair_first(world, term->id))) { + nodata_term = true; + } + } + } + + if (!nodata_term && term->inout != EcsIn && term->inout != EcsInOutNone) { + /* Non-this terms default to EcsIn */ + if (ecs_term_match_this(term) || term->inout != EcsInOutDefault) { + q->flags |= EcsQueryHasOutTerms; + } + + bool match_non_this = !ecs_term_match_this(term) || + (term->src.id & EcsUp); + if (match_non_this && term->inout != EcsInOutDefault) { + q->flags |= EcsQueryHasNonThisOutTerms; + } + } + + if (!nodata_term) { + /* If terms in an OR chain do not all return the same type, the + * field will not provide any data */ + if (term->flags_ & EcsTermIsOr) { + ecs_term_t *first = flecs_query_or_other_type(q, i); + if (first) { + nodata_term = true; + } + q->data_fields &= (ecs_termset_t)~(1llu << term->field_index); + } + } + + if (term->flags_ & EcsTermIsMember) { + nodata_term = false; + } + + if (!nodata_term && term->oper != EcsNot) { + ECS_TERMSET_SET(q->data_fields, 1u << term->field_index); + + if (term->inout != EcsIn) { + ECS_TERMSET_SET(q->write_fields, 1u << term->field_index); + } + if (term->inout != EcsOut) { + ECS_TERMSET_SET(q->read_fields, 1u << term->field_index); + } + if (term->inout == EcsInOutDefault) { + ECS_TERMSET_SET(q->shared_readonly_fields, + 1u << term->field_index); + } + } + + if (ECS_TERM_REF_ID(&term->src) && (term->src.id & EcsIsEntity)) { + ECS_TERMSET_SET(q->fixed_fields, 1u << term->field_index); + } + + if ((term->src.id & EcsIsVariable) && + (ECS_TERM_REF_ID(&term->src) != EcsThis)) + { + ECS_TERMSET_SET(q->var_fields, 1u << term->field_index); + } + + ecs_id_record_t *idr = flecs_id_record_get(world, term->id); + if (idr) { + if (ecs_os_has_threading()) { + ecs_os_ainc(&idr->keep_alive); + } else { + idr->keep_alive ++; + } + + term->flags_ |= EcsTermKeepAlive; + + if (idr->flags & EcsIdIsSparse) { + term->flags_ |= EcsTermIsSparse; + ECS_BIT_CLEAR16(term->flags_, EcsTermIsTrivial); + if (term->flags_ & EcsTermIsCacheable) { + cacheable_terms --; + ECS_BIT_CLEAR16(term->flags_, EcsTermIsCacheable); + } + + /* Sparse component fields must be accessed with ecs_field_at */ + if (!nodata_term) { + q->row_fields |= flecs_uto(uint32_t, 1llu << i); + } + } + } + + if (term->oper == EcsOptional || term->oper == EcsNot) { + cond_set = true; + } + + ecs_entity_t first_id = ECS_TERM_REF_ID(&term->first); + if (first_id == EcsPredEq || first_id == EcsPredMatch || + first_id == EcsPredLookup) + { + q->flags |= EcsQueryHasPred; + term->src.id = (term->src.id & ~EcsTraverseFlags) | EcsSelf; + term->inout = EcsInOutNone; + } else { + if (!ecs_term_match_0(term) && term->oper != EcsNot && + term->oper != EcsNotFrom) + { + ECS_TERMSET_SET(q->set_fields, 1u << term->field_index); + } + } + + if (first_id == EcsScopeOpen) { + q->flags |= EcsQueryHasScopes; + scope_nesting ++; + } + + if (scope_nesting) { + term->flags_ |= EcsTermIsScope; + ECS_BIT_CLEAR16(term->flags_, EcsTermIsTrivial); + ECS_BIT_CLEAR16(term->flags_, EcsTermIsCacheable); + cacheable_terms --; + } + + if (first_id == EcsScopeClose) { + if (i && ECS_TERM_REF_ID(&terms[i - 1].first) == EcsScopeOpen) { + flecs_query_validator_error(&ctx, "invalid empty scope"); + return -1; + } + + q->flags |= EcsQueryHasScopes; + scope_nesting --; + } + + if (scope_nesting < 0) { + flecs_query_validator_error(&ctx, "'}' without matching '{'"); + } + } + + if (scope_nesting != 0) { + flecs_query_validator_error(&ctx, "missing '}'"); + return -1; + } + + if (term_count && (terms[term_count - 1].oper == EcsOr)) { + flecs_query_validator_error(&ctx, + "last term of query can't have OR operator"); + return -1; + } + + q->field_count = flecs_ito(int8_t, field_count); + + if (field_count) { + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + int32_t field = term->field_index; + q->ids[field] = term->id; + + if (term->flags_ & EcsTermIsOr) { + if (flecs_query_or_other_type(q, i)) { + q->sizes[field] = 0; + q->ids[field] = 0; + continue; + } + } + + ecs_id_record_t *idr = flecs_id_record_get(world, term->id); + if (idr) { + if (!ECS_IS_PAIR(idr->id) || ECS_PAIR_FIRST(idr->id) != EcsWildcard) { + if (idr->type_info) { + q->sizes[field] = idr->type_info->size; + q->ids[field] = idr->id; + } + } + } else { + const ecs_type_info_t *ti = ecs_get_type_info( + world, term->id); + if (ti) { + q->sizes[field] = ti->size; + q->ids[field] = term->id; + } + } + } + } + + ECS_BIT_COND(q->flags, EcsQueryHasCondSet, cond_set); + + /* Check if this is a trivial query */ + if ((q->flags & EcsQueryMatchOnlyThis)) { + if (!(q->flags & + (EcsQueryHasPred|EcsQueryMatchDisabled|EcsQueryMatchPrefab))) + { + ECS_BIT_SET(q->flags, EcsQueryMatchOnlySelf); + + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + ecs_term_ref_t *src = &term->src; + + if (src->id & EcsUp) { + ECS_BIT_CLEAR(q->flags, EcsQueryMatchOnlySelf); + } + + if (!(term->flags_ & EcsTermIsTrivial)) { + break; + } + } + + if (term_count && (i == term_count)) { + ECS_BIT_SET(q->flags, EcsQueryIsTrivial); + } + } + } + + /* Set cacheable flags */ + ECS_BIT_COND(q->flags, EcsQueryHasCacheable, + cacheable_terms != 0); + + /* Exclude queries with order_by from setting the IsCacheable flag. This + * allows the routine that evaluates entirely cached queries to use more + * optimized logic as it doesn't have to deal with order_by edge cases */ + ECS_BIT_COND(q->flags, EcsQueryIsCacheable, + cacheable && (cacheable_terms == term_count) && + !desc->order_by_callback); + + /* If none of the terms match a source, the query matches nothing */ + ECS_BIT_COND(q->flags, EcsQueryMatchNothing, match_nothing); + + for (i = 0; i < q->term_count; i ++) { + ecs_term_t *term = &q->terms[i]; + /* Post process term names in case they were used to create variables */ + flecs_normalize_term_name(&term->first); + flecs_normalize_term_name(&term->second); + flecs_normalize_term_name(&term->src); + } + + return 0; +} + +static +int flecs_query_query_populate_terms( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_query_t *q, + const ecs_query_desc_t *desc) +{ + /* Count number of initialized terms in desc->terms */ + int32_t i, term_count = 0; + for (i = 0; i < FLECS_TERM_COUNT_MAX; i ++) { + if (!ecs_term_is_initialized(&desc->terms[i])) { + break; + } + term_count ++; + } + + /* Copy terms from array to query */ + if (term_count) { + ecs_os_memcpy_n(&q->terms, desc->terms, ecs_term_t, term_count); + } + + /* Parse query expression if set */ + const char *expr = desc->expr; + if (expr && expr[0]) { + #ifdef FLECS_SCRIPT + ecs_script_impl_t script = { + .pub.world = world, + .pub.name = desc->entity ? ecs_get_name(world, desc->entity) : NULL, + .pub.code = expr + }; + + /* Allocate buffer that's large enough to tokenize the query string */ + script.token_buffer_size = ecs_os_strlen(expr) * 2 + 1; + script.token_buffer = flecs_alloc( + &flecs_query_impl(q)->stage->allocator, script.token_buffer_size); + + if (flecs_terms_parse(&script.pub, &q->terms[term_count], + &term_count)) + { + flecs_free(&stage->allocator, + script.token_buffer_size, script.token_buffer); + goto error; + } + + /* Store on query object so we can free later */ + flecs_query_impl(q)->tokens = script.token_buffer; + flecs_query_impl(q)->tokens_len = + flecs_ito(int16_t, script.token_buffer_size); + #else + (void)world; + (void)stage; + ecs_err("cannot parse query expression: script addon required"); + goto error; + #endif + } + + q->term_count = flecs_ito(int8_t, term_count); + + return 0; +error: + return -1; +} + +#ifndef FLECS_SANITIZE +static +bool flecs_query_finalize_simple( + ecs_world_t *world, + ecs_query_t *q, + const ecs_query_desc_t *desc) +{ + /* Filter out queries that aren't simple enough */ + if (desc->expr) { + return false; + } + + if (desc->order_by_callback || desc->group_by_callback) { + return false; + } + + int8_t i, term_count; + for (i = 0; i < FLECS_TERM_COUNT_MAX; i ++) { + if (!ecs_term_is_initialized(&desc->terms[i])) { + break; + } + + ecs_id_t id = desc->terms[i].id; + if (ecs_id_is_wildcard(id)) { + return false; + } + + if (id == EcsThis || ECS_PAIR_FIRST(id) == EcsThis || + ECS_PAIR_SECOND(id) == EcsThis) + { + return false; + } + + if (id == EcsVariable || ECS_PAIR_FIRST(id) == EcsVariable || + ECS_PAIR_SECOND(id) == EcsVariable) + { + return false; + } + + if (id == EcsPrefab || id == EcsDisabled) { + return false; + } + + ecs_term_t term = { .id = desc->terms[i].id }; + if (ecs_os_memcmp_t(&term, &desc->terms[i], ecs_term_t)) { + return false; + } + } + + if (!i) { + return false; /* No terms */ + } + + term_count = i; + ecs_os_memcpy_n(&q->terms, desc->terms, ecs_term_t, term_count); + + /* Simple query that only queries for component ids */ + + /* Populate terms */ + int8_t cacheable_count = 0, trivial_count = 0, up_count = 0; + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &q->terms[i]; + ecs_id_t id = term->id; + + ecs_entity_t first = id; + if (ECS_IS_PAIR(id)) { + ecs_entity_t second = flecs_entities_get_alive(world, + ECS_PAIR_SECOND(id)); + first = flecs_entities_get_alive(world, ECS_PAIR_FIRST(id)); + term->second.id = second | EcsIsEntity | EcsSelf; + } + + term->field_index = i; + term->first.id = first | EcsIsEntity | EcsSelf; + term->src.id = EcsThis | EcsIsVariable | EcsSelf; + + q->ids[i] = id; + + ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + idr->keep_alive ++; + term->flags_ |= EcsTermKeepAlive; + } + + if (!idr && ECS_IS_PAIR(id)) { + idr = flecs_id_record_get(world, + ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard)); + } + + bool cacheable = true, trivial = true; + if (idr) { + if (idr->type_info) { + q->sizes[i] = idr->type_info->size; + q->flags |= EcsQueryHasOutTerms; + q->data_fields |= (ecs_termset_t)(1llu << i); + } + + if (idr->flags & EcsIdOnInstantiateInherit) { + term->src.id |= EcsUp; + term->trav = EcsIsA; + up_count ++; + } + + if (idr->flags & EcsIdCanToggle) { + term->flags_ |= EcsTermIsToggle; + trivial = false; + } + + if (ECS_IS_PAIR(id)) { + if (idr->flags & EcsIdIsUnion) { + term->flags_ |= EcsTermIsUnion; + trivial = false; + cacheable = false; + } + } + + if (idr->flags & EcsIdIsSparse) { + term->flags_ |= EcsTermIsSparse; + cacheable = false; trivial = false; + q->row_fields |= flecs_uto(uint32_t, 1llu << i); + } + } + + if (ECS_IS_PAIR(id)) { + if (ecs_has_id(world, first, EcsTransitive)) { + term->flags_ |= EcsTermTransitive; + trivial = false; + cacheable = false; + } + if (ecs_has_id(world, first, EcsReflexive)) { + term->flags_ |= EcsTermReflexive; + trivial = false; + cacheable = false; + } + } + + if (flecs_id_record_get(world, ecs_pair(EcsIsA, first)) != NULL) { + term->flags_ |= EcsTermIdInherited; + cacheable = false; trivial = false; + } + + if (cacheable) { + term->flags_ |= EcsTermIsCacheable; + cacheable_count ++; + } + + if (trivial) { + term->flags_ |= EcsTermIsTrivial; + trivial_count ++; + } + } + + /* Initialize static data */ + q->term_count = term_count; + q->field_count = term_count; + q->set_fields = (ecs_termset_t)((1llu << i) - 1); + q->static_id_fields = (ecs_termset_t)((1llu << i) - 1); + q->flags |= EcsQueryMatchThis|EcsQueryMatchOnlyThis|EcsQueryHasTableThisVar; + + if (cacheable_count) { + q->flags |= EcsQueryHasCacheable; + } + + if (cacheable_count == term_count && trivial_count == term_count) { + q->flags |= EcsQueryIsCacheable|EcsQueryIsTrivial; + } + + if (!up_count) { + q->flags |= EcsQueryMatchOnlySelf; + } + + return true; +} +#endif + +static +char* flecs_query_append_token( + char *dst, + const char *src) +{ + int32_t len = ecs_os_strlen(src); + ecs_os_memcpy(dst, src, len + 1); + return dst + len + 1; +} + +static +void flecs_query_populate_tokens( + ecs_query_impl_t *impl) +{ + ecs_query_t *q = &impl->pub; + int32_t i, term_count = q->term_count; + + char *old_tokens = impl->tokens; + int32_t old_tokens_len = impl->tokens_len; + impl->tokens = NULL; + impl->tokens_len = 0; + + /* Step 1: determine size of token buffer */ + int32_t len = 0; + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &q->terms[i]; + + if (term->first.name) { + len += ecs_os_strlen(term->first.name) + 1; + } + if (term->second.name) { + len += ecs_os_strlen(term->second.name) + 1; + } + if (term->src.name) { + len += ecs_os_strlen(term->src.name) + 1; + } + } + + /* Step 2: reassign term tokens to buffer */ + if (len) { + impl->tokens = flecs_alloc(&impl->stage->allocator, len); + impl->tokens_len = flecs_ito(int16_t, len); + char *token = impl->tokens, *next; + + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &q->terms[i]; + if (term->first.name) { + next = flecs_query_append_token(token, term->first.name); + term->first.name = token; + token = next; + } + if (term->second.name) { + next = flecs_query_append_token(token, term->second.name); + term->second.name = token; + token = next; + } + if (term->src.name) { + next = flecs_query_append_token(token, term->src.name); + term->src.name = token; + token = next; + } + } + } + + if (old_tokens) { + flecs_free(&impl->stage->allocator, old_tokens_len, old_tokens); + } +} + +int flecs_query_finalize_query( + ecs_world_t *world, + ecs_query_t *q, + const ecs_query_desc_t *desc) +{ + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->_canary == 0, ECS_INVALID_PARAMETER, + "ecs_query_desc_t was not initialized to zero"); + ecs_stage_t *stage = flecs_stage_from_world(&world); + + q->flags |= desc->flags | world->default_query_flags; + + /* Fast routine that initializes simple queries and skips complex validation + * logic if it's not needed. When running in sanitized mode, always take the + * slow path. This in combination with the test suite ensures that the + * result of the fast & slow code is the same. */ + #ifndef FLECS_SANITIZE + if (flecs_query_finalize_simple(world, q, desc)) { + return 0; + } + #endif + + /* Populate term array from desc terms & DSL expression */ + if (flecs_query_query_populate_terms(world, stage, q, desc)) { + goto error; + } + + /* Ensure all fields are consistent and properly filled out */ + if (flecs_query_finalize_terms(world, q, desc)) { + goto error; + } + + /* Store remaining string tokens in terms (after entity lookups) in single + * token buffer which simplifies memory management & reduces allocations. */ + flecs_query_populate_tokens(flecs_query_impl(q)); + + return 0; +error: + return -1; +} diff --git a/vendors/flecs/src/search.c b/vendors/flecs/src/search.c index 7d57b1580..62324bc99 100644 --- a/vendors/flecs/src/search.c +++ b/vendors/flecs/src/search.c @@ -12,22 +12,17 @@ static int32_t flecs_type_search( const ecs_table_t *table, - ecs_id_t search_id, ecs_id_record_t *idr, ecs_id_t *ids, ecs_id_t *id_out, ecs_table_record_t **tr_out) -{ +{ ecs_table_record_t *tr = ecs_table_cache_get(&idr->cache, table); if (tr) { int32_t r = tr->index; if (tr_out) tr_out[0] = tr; if (id_out) { - if (ECS_PAIR_FIRST(search_id) == EcsUnion) { - id_out[0] = ids[r]; - } else { - id_out[0] = flecs_to_public_id(ids[r]); - } + id_out[0] = ids[r]; } return r; } @@ -52,7 +47,7 @@ int32_t flecs_type_offset_search( ecs_id_t type_id = ids[offset ++]; if (ecs_id_match(type_id, id)) { if (id_out) { - id_out[0] = flecs_to_public_id(type_id); + id_out[0] = type_id; } return offset - 1; } @@ -68,9 +63,10 @@ bool flecs_type_can_inherit_id( ecs_id_t id) { ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); - if (idr->flags & EcsIdDontInherit) { + if (idr->flags & EcsIdOnInstantiateDontInherit) { return false; } + if (idr->flags & EcsIdExclusive) { if (ECS_HAS_ID_FLAG(id, PAIR)) { ecs_entity_t er = ECS_PAIR_FIRST(id); @@ -109,7 +105,7 @@ int32_t flecs_type_search_relation( return r; } } else { - int32_t r = flecs_type_search(table, id, idr, ids, id_out, tr_out); + int32_t r = flecs_type_search(table, idr, ids, id_out, tr_out); if (r != -1) { return r; } @@ -142,7 +138,7 @@ int32_t flecs_type_search_relation( if (offset) { r_column = flecs_type_offset_search(offset, rel, ids, count, &id_r); } else { - r_column = flecs_type_search(table, id, idr_r, ids, &id_r, 0); + r_column = flecs_type_search(table, idr_r, ids, &id_r, 0); } while (r_column != -1) { ecs_entity_t obj = ECS_PAIR_SECOND(id_r); @@ -191,7 +187,7 @@ int32_t flecs_search_relation_w_idr( int32_t offset, ecs_id_t id, ecs_entity_t rel, - ecs_flags32_t flags, + ecs_flags64_t flags, ecs_entity_t *subject_out, ecs_id_t *id_out, struct ecs_table_record_t **tr_out, @@ -199,13 +195,13 @@ int32_t flecs_search_relation_w_idr( { if (!table) return -1; - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); flags = flags ? flags : (EcsSelf|EcsUp); if (!idr) { - idr = flecs_query_id_record_get(world, id); + idr = flecs_id_record_get(world, id); if (!idr) { return -1; } @@ -217,7 +213,7 @@ int32_t flecs_search_relation_w_idr( return ecs_search_offset(world, table, offset, id, id_out); } else { return flecs_type_search( - table, id, idr, table->type.array, id_out, tr_out); + table, idr, table->type.array, id_out, tr_out); } } @@ -234,14 +230,14 @@ int32_t ecs_search_relation( int32_t offset, ecs_id_t id, ecs_entity_t rel, - ecs_flags32_t flags, + ecs_flags64_t flags, ecs_entity_t *subject_out, ecs_id_t *id_out, struct ecs_table_record_t **tr_out) { if (!table) return -1; - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); flags = flags ? flags : (EcsSelf|EcsUp); @@ -251,7 +247,7 @@ int32_t ecs_search_relation( return ecs_search_offset(world, table, offset, id, id_out); } - ecs_id_record_t *idr = flecs_query_id_record_get(world, id); + ecs_id_record_t *idr = flecs_id_record_get(world, id); if (!idr) { return -1; } @@ -266,19 +262,17 @@ int32_t ecs_search_relation( int32_t flecs_search_w_idr( const ecs_world_t *world, const ecs_table_t *table, - ecs_id_t id, ecs_id_t *id_out, ecs_id_record_t *idr) { if (!table) return -1; - ecs_poly_assert(world, ecs_world_t); - ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); + flecs_poly_assert(world, ecs_world_t); (void)world; ecs_type_t type = table->type; ecs_id_t *ids = type.array; - return flecs_type_search(table, id, idr, ids, id_out, 0); + return flecs_type_search(table, idr, ids, id_out, 0); } int32_t ecs_search( @@ -289,17 +283,17 @@ int32_t ecs_search( { if (!table) return -1; - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); - ecs_id_record_t *idr = flecs_query_id_record_get(world, id); + ecs_id_record_t *idr = flecs_id_record_get(world, id); if (!idr) { return -1; } ecs_type_t type = table->type; ecs_id_t *ids = type.array; - return flecs_type_search(table, id, idr, ids, id_out, 0); + return flecs_type_search(table, idr, ids, id_out, 0); } int32_t ecs_search_offset( @@ -310,7 +304,7 @@ int32_t ecs_search_offset( ecs_id_t *id_out) { if (!offset) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); return ecs_search(world, table, id, id_out); } @@ -366,25 +360,5 @@ int32_t flecs_relation_depth( return 0; } - int32_t depth_offset = 0; - if (table->flags & EcsTableHasTarget) { - if (ecs_table_get_type_index(world, table, - ecs_pair_t(EcsTarget, r)) != -1) - { - ecs_id_t id; - int32_t col = ecs_search(world, table, - ecs_pair(EcsFlatten, EcsWildcard), &id); - if (col == -1) { - return 0; - } - - ecs_entity_t did = ecs_pair_second(world, id); - ecs_assert(did != 0, ECS_INTERNAL_ERROR, NULL); - uint64_t *val = ecs_map_get(&world->store.entity_to_depth, did); - ecs_assert(val != NULL, ECS_INTERNAL_ERROR, NULL); - depth_offset = flecs_uto(int32_t, val[0]); - } - } - - return flecs_relation_depth_walk(world, idr, table, table) + depth_offset; + return flecs_relation_depth_walk(world, idr, table, table); } diff --git a/vendors/flecs/src/stage.c b/vendors/flecs/src/stage.c index aab1dcb83..7b29d85ad 100644 --- a/vendors/flecs/src/stage.c +++ b/vendors/flecs/src/stage.c @@ -26,6 +26,7 @@ ecs_cmd_t* flecs_cmd_new( cmd->is._1.value = NULL; cmd->next_for_entity = 0; cmd->entry = NULL; + cmd->system = stage->system; return cmd; } @@ -69,11 +70,10 @@ ecs_cmd_t* flecs_cmd_new_batched( } static -void flecs_stages_merge( - ecs_world_t *world, - bool force_merge) +void flecs_stage_merge( + ecs_world_t *world) { - bool is_stage = ecs_poly_is(world, ecs_stage_t); + bool is_stage = flecs_poly_is(world, ecs_stage_t); ecs_stage_t *stage = flecs_stage_from_world(&world); bool measure_frame_time = ECS_BIT_IS_SET(world->flags, @@ -91,21 +91,17 @@ void flecs_stages_merge( /* Check for consistency if force_merge is enabled. In practice this * function will never get called with force_merge disabled for just * a single stage. */ - if (force_merge || stage->auto_merge) { - ecs_assert(stage->defer == 1, ECS_INVALID_OPERATION, - "mismatching defer_begin/defer_end detected"); - flecs_defer_end(world, stage); - } + ecs_assert(stage->defer == 1, ECS_INVALID_OPERATION, + "mismatching defer_begin/defer_end detected"); + flecs_defer_end(world, stage); } else { /* Merge stages. Only merge if the stage has auto_merging turned on, or * if this is a forced merge (like when ecs_merge is called) */ int32_t i, count = ecs_get_stage_count(world); for (i = 0; i < count; i ++) { ecs_stage_t *s = (ecs_stage_t*)ecs_get_stage(world, i); - ecs_poly_assert(s, ecs_stage_t); - if (force_merge || s->auto_merge) { - flecs_defer_end(world, s); - } + flecs_poly_assert(s, ecs_stage_t); + flecs_defer_end(world, s); } } @@ -117,34 +113,20 @@ void flecs_stages_merge( world->info.merge_count_total ++; - /* If stage is asynchronous, deferring is always enabled */ - if (stage->async) { + /* If stage is unmanaged, deferring is always enabled */ + if (stage->id == -1) { flecs_defer_begin(world, stage); } ecs_log_pop_3(); } -static -void flecs_stage_auto_merge( - ecs_world_t *world) -{ - flecs_stages_merge(world, false); -} - -static -void flecs_stage_manual_merge( - ecs_world_t *world) -{ - flecs_stages_merge(world, true); -} - bool flecs_defer_begin( ecs_world_t *world, ecs_stage_t *stage) { - ecs_poly_assert(world, ecs_world_t); - ecs_poly_assert(stage, ecs_stage_t); + flecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(stage, ecs_stage_t); (void)world; if (stage->defer < 0) return false; return (++ stage->defer) == 1; @@ -282,7 +264,7 @@ bool flecs_defer_bulk_new( /* Use ecs_new_id as this is thread safe */ int i; for (i = 0; i < count; i ++) { - ids[i] = ecs_new_id(world); + ids[i] = ecs_new(world); } *ids_out = ids; @@ -338,7 +320,8 @@ void* flecs_defer_set( ecs_entity_t entity, ecs_id_t id, ecs_size_t size, - void *value) + void *value, + bool *is_new) { ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); @@ -348,9 +331,8 @@ void* flecs_defer_set( if (!idr) { /* If idr doesn't exist yet, create it but only if the * application is not multithreaded. */ - if (stage->async || (world->flags & EcsWorldMultiThreaded)) { + if (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. */ @@ -368,10 +350,12 @@ void* flecs_defer_set( } /* If the id isn't associated with a type, we can't set anything */ - ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, + "provided component is not a type"); /* Make sure the size of the value equals the type size */ - ecs_assert(!size || size == ti->size, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!size || size == ti->size, ECS_INVALID_PARAMETER, + "mismatching size specified for component in ensure/emplace/set"); size = ti->size; /* Find existing component. Make sure it's owned, so that we won't use the @@ -386,10 +370,16 @@ void* flecs_defer_set( const ecs_table_record_t *tr = flecs_id_record_get_table( idr, table); if (tr) { - ecs_assert(tr->column != -1, ECS_NOT_A_COMPONENT, NULL); - /* Entity has the component */ - ecs_vec_t *column = &table->data.columns[tr->column].data; - existing = ecs_vec_get(column, size, ECS_RECORD_TO_ROW(r->row)); + if (tr->column != -1) { + /* Entity has the component */ + existing = table->data.columns[tr->column].data; + existing = ECS_ELEM(existing, size, + ECS_RECORD_TO_ROW(r->row)); + } else { + ecs_assert(idr->flags & EcsIdIsSparse, + ECS_NOT_A_COMPONENT, NULL); + existing = flecs_sparse_get_any(idr->sparse, 0, entity); + } } } } @@ -482,6 +472,10 @@ void* flecs_defer_set( cmd->entity = entity; cmd->is._1.size = size; cmd->is._1.value = cmd_value; + + if (is_new) { + *is_new = true; + } } else { /* If component already exists, still insert an Add command to ensure * that any preceding remove commands won't remove the component. If the @@ -493,6 +487,10 @@ void* flecs_defer_set( } cmd->id = id; cmd->entity = entity; + + if (is_new) { + *is_new = false; + } } return cmd_value; @@ -610,23 +608,33 @@ void flecs_commands_pop( stage->cmd = &stage->cmd_stack[sp]; } -void flecs_stage_init( - ecs_world_t *world, - ecs_stage_t *stage) +ecs_entity_t flecs_stage_set_system( + ecs_stage_t *stage, + ecs_entity_t system) { - ecs_poly_assert(world, ecs_world_t); - ecs_poly_init(stage, ecs_stage_t); + ecs_entity_t old = stage->system; + stage->system = system; + return old; +} + +static +ecs_stage_t* flecs_stage_new( + ecs_world_t *world) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_stage_t *stage = ecs_os_calloc(sizeof(ecs_stage_t)); + flecs_poly_init(stage, ecs_stage_t); stage->world = world; stage->thread_ctx = world; - stage->auto_merge = true; - stage->async = false; flecs_stack_init(&stage->allocators.iter_stack); flecs_stack_init(&stage->allocators.deser_stack); flecs_allocator_init(&stage->allocator); flecs_ballocator_init_n(&stage->allocators.cmd_entry_chunk, ecs_cmd_entry_t, FLECS_SPARSE_PAGE_SIZE); + flecs_ballocator_init_t(&stage->allocators.query_impl, ecs_query_impl_t); + flecs_ballocator_init_t(&stage->allocators.query_cache, ecs_query_cache_t); ecs_allocator_t *a = &stage->allocator; ecs_vec_init_t(a, &stage->post_frame_actions, ecs_action_elem_t, 0); @@ -637,17 +645,19 @@ void flecs_stage_init( } stage->cmd = &stage->cmd_stack[0]; + return stage; } -void flecs_stage_fini( +static +void flecs_stage_free( ecs_world_t *world, ecs_stage_t *stage) { (void)world; - ecs_poly_assert(world, ecs_world_t); - ecs_poly_assert(stage, ecs_stage_t); + flecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(stage, ecs_stage_t); - ecs_poly_fini(stage, ecs_stage_t); + flecs_poly_fini(stage, ecs_stage_t); ecs_allocator_t *a = &stage->allocator; @@ -663,52 +673,87 @@ void flecs_stage_fini( flecs_stack_fini(&stage->allocators.iter_stack); flecs_stack_fini(&stage->allocators.deser_stack); flecs_ballocator_fini(&stage->allocators.cmd_entry_chunk); + flecs_ballocator_fini(&stage->allocators.query_impl); + flecs_ballocator_fini(&stage->allocators.query_cache); flecs_allocator_fini(&stage->allocator); + + ecs_os_free(stage); +} + +ecs_allocator_t* flecs_stage_get_allocator( + ecs_world_t *world) +{ + ecs_stage_t *stage = flecs_stage_from_world( + ECS_CONST_CAST(ecs_world_t**, &world)); + return &stage->allocator; +} + +ecs_stack_t* flecs_stage_get_stack_allocator( + ecs_world_t *world) +{ + ecs_stage_t *stage = flecs_stage_from_world( + ECS_CONST_CAST(ecs_world_t**, &world)); + return &stage->allocators.iter_stack; +} + +ecs_world_t* ecs_stage_new( + ecs_world_t *world) +{ + ecs_stage_t *stage = flecs_stage_new(world); + stage->id = -1; + + flecs_defer_begin(world, stage); + + return (ecs_world_t*)stage; +} + +void ecs_stage_free( + ecs_world_t *world) +{ + flecs_poly_assert(world, ecs_stage_t); + ecs_stage_t *stage = (ecs_stage_t*)world; + ecs_check(stage->id == -1, ECS_INVALID_PARAMETER, + "cannot free stage that's owned by world"); + flecs_stage_free(stage->world, stage); +error: + return; } void ecs_set_stage_count( ecs_world_t *world, int32_t stage_count) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); /* World must have at least one default stage */ ecs_assert(stage_count >= 1 || (world->flags & EcsWorldFini), ECS_INTERNAL_ERROR, NULL); - bool auto_merge = true; const ecs_entity_t *lookup_path = NULL; - ecs_entity_t scope = 0; - ecs_entity_t with = 0; if (world->stage_count >= 1) { - auto_merge = world->stages[0].auto_merge; - lookup_path = world->stages[0].lookup_path; - scope = world->stages[0].scope; - with = world->stages[0].with; + lookup_path = world->stages[0]->lookup_path; } int32_t i, count = world->stage_count; - if (count && count != stage_count) { - ecs_stage_t *stages = world->stages; - - for (i = 0; i < count; i ++) { + if (stage_count < count) { + for (i = stage_count; i < count; i ++) { /* If stage contains a thread handle, ecs_set_threads was used to - * create the stages. ecs_set_threads and ecs_set_stage_count should not - * be mixed. */ - ecs_poly_assert(&stages[i], ecs_stage_t); - ecs_check(stages[i].thread == 0, ECS_INVALID_OPERATION, NULL); - flecs_stage_fini(world, &stages[i]); + * create the stages. ecs_set_threads and ecs_set_stage_count should + * not be mixed. */ + ecs_stage_t *stage = world->stages[i]; + flecs_poly_assert(stage, ecs_stage_t); + ecs_check(stage->thread == 0, ECS_INVALID_OPERATION, + "cannot mix using set_stage_count and set_threads"); + flecs_stage_free(world, stage); } - - ecs_os_free(world->stages); } if (stage_count) { - world->stages = ecs_os_malloc_n(ecs_stage_t, stage_count); + world->stages = ecs_os_realloc_n( + world->stages, ecs_stage_t*, stage_count); - for (i = 0; i < stage_count; i ++) { - ecs_stage_t *stage = &world->stages[i]; - flecs_stage_init(world, stage); + for (i = count; i < stage_count; i ++) { + ecs_stage_t *stage = world->stages[i] = flecs_stage_new(world); stage->id = i; /* Set thread_ctx to stage, as this stage might be used in a @@ -718,6 +763,7 @@ void ecs_set_stage_count( } } else { /* Set to NULL to prevent double frees */ + ecs_os_free(world->stages); world->stages = NULL; } @@ -725,10 +771,7 @@ void ecs_set_stage_count( * ecs_set_stage_count function is called, all stages inherit the auto_merge * property from the world */ for (i = 0; i < stage_count; i ++) { - world->stages[i].auto_merge = auto_merge; - world->stages[i].lookup_path = lookup_path; - world->stages[0].scope = scope; - world->stages[0].with = with; + world->stages[i]->lookup_path = lookup_path; } world->stage_count = stage_count; @@ -743,17 +786,17 @@ int32_t ecs_get_stage_count( return world->stage_count; } -int32_t ecs_get_stage_id( +int32_t ecs_stage_get_id( const ecs_world_t *world) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - if (ecs_poly_is(world, ecs_stage_t)) { + if (flecs_poly_is(world, ecs_stage_t)) { ecs_stage_t *stage = ECS_CONST_CAST(ecs_stage_t*, world); /* Index 0 is reserved for main stage */ return stage->id; - } else if (ecs_poly_is(world, ecs_world_t)) { + } else if (flecs_poly_is(world, ecs_world_t)) { return 0; } else { ecs_throw(ECS_INTERNAL_ERROR, NULL); @@ -766,17 +809,18 @@ ecs_world_t* ecs_get_stage( const ecs_world_t *world, int32_t stage_id) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(world->stage_count > stage_id, ECS_INVALID_PARAMETER, NULL); - return (ecs_world_t*)&world->stages[stage_id]; + return (ecs_world_t*)world->stages[stage_id]; error: return NULL; } bool ecs_readonly_begin( - ecs_world_t *world) + ecs_world_t *world, + bool multi_threaded) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); flecs_process_pending_tables(world); @@ -785,8 +829,8 @@ bool ecs_readonly_begin( int32_t i, count = ecs_get_stage_count(world); for (i = 0; i < count; i ++) { - ecs_stage_t *stage = &world->stages[i]; - stage->lookup_path = world->stages[0].lookup_path; + ecs_stage_t *stage = world->stages[i]; + stage->lookup_path = world->stages[0]->lookup_path; ecs_assert(stage->defer == 0, ECS_INVALID_OPERATION, "deferred mode cannot be enabled when entering readonly mode"); flecs_defer_begin(world, stage); @@ -797,14 +841,7 @@ bool ecs_readonly_begin( /* From this point on, the world is "locked" for mutations, and it is only * allowed to enqueue commands from stages */ ECS_BIT_SET(world->flags, EcsWorldReadonly); - - /* If world has more than one stage, signal we might be running on multiple - * threads. This is a stricter version of readonly mode: while some - * mutations like implicit component registration are still allowed in plain - * readonly mode, no mutations are allowed when multithreaded. */ - if (world->worker_cond) { - ECS_BIT_SET(world->flags, EcsWorldMultiThreaded); - } + ECS_BIT_COND(world->flags, EcsWorldMultiThreaded, multi_threaded); return is_readonly; } @@ -812,8 +849,9 @@ bool ecs_readonly_begin( void ecs_readonly_end( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(world->flags & EcsWorldReadonly, ECS_INVALID_OPERATION, NULL); + flecs_poly_assert(world, ecs_world_t); + ecs_check(world->flags & EcsWorldReadonly, ECS_INVALID_OPERATION, + "world is not in readonly mode"); /* After this it is safe again to mutate the world directly */ ECS_BIT_CLEAR(world->flags, EcsWorldReadonly); @@ -821,7 +859,7 @@ void ecs_readonly_end( ecs_log_pop_3(); - flecs_stage_auto_merge(world); + flecs_stage_merge(world); error: return; } @@ -830,57 +868,31 @@ void ecs_merge( ecs_world_t *world) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_poly_is(world, ecs_world_t) || - ecs_poly_is(world, ecs_stage_t), ECS_INVALID_PARAMETER, NULL); - flecs_stage_manual_merge(world); + ecs_check(flecs_poly_is(world, ecs_world_t) || + flecs_poly_is(world, ecs_stage_t), ECS_INVALID_PARAMETER, NULL); + flecs_stage_merge(world); error: return; } -void ecs_set_automerge( - ecs_world_t *world, - bool auto_merge) -{ - /* If a world is provided, set auto_merge globally for the world. This - * doesn't actually do anything (the main stage never merges) but it serves - * as the default for when stages are created. */ - if (ecs_poly_is(world, ecs_world_t)) { - world->stages[0].auto_merge = auto_merge; - - /* Propagate change to all stages */ - int i, stage_count = ecs_get_stage_count(world); - for (i = 0; i < stage_count; i ++) { - ecs_stage_t *stage = (ecs_stage_t*)ecs_get_stage(world, i); - stage->auto_merge = auto_merge; - } - - /* If a stage is provided, override the auto_merge value for the individual - * stage. This allows an application to control per-stage which stage should - * be automatically merged and which one shouldn't */ - } else { - ecs_poly_assert(world, ecs_stage_t); - ecs_stage_t *stage = (ecs_stage_t*)world; - stage->auto_merge = auto_merge; - } -} - bool ecs_stage_is_readonly( const ecs_world_t *stage) { const ecs_world_t *world = ecs_get_world(stage); - if (ecs_poly_is(stage, ecs_stage_t)) { - if (((const ecs_stage_t*)stage)->async) { + if (flecs_poly_is(stage, ecs_stage_t)) { + if (((const ecs_stage_t*)stage)->id == -1) { + /* Stage is not owned by world, so never readonly */ return false; } } if (world->flags & EcsWorldReadonly) { - if (ecs_poly_is(stage, ecs_world_t)) { + if (flecs_poly_is(stage, ecs_world_t)) { return true; } } else { - if (ecs_poly_is(stage, ecs_stage_t)) { + if (flecs_poly_is(stage, ecs_stage_t)) { return true; } } @@ -888,47 +900,6 @@ bool ecs_stage_is_readonly( return false; } -ecs_world_t* ecs_async_stage_new( - ecs_world_t *world) -{ - ecs_stage_t *stage = ecs_os_calloc(sizeof(ecs_stage_t)); - flecs_stage_init(world, stage); - - stage->id = -1; - stage->auto_merge = false; - stage->async = true; - - flecs_defer_begin(world, stage); - - return (ecs_world_t*)stage; -} - -void ecs_async_stage_free( - ecs_world_t *world) -{ - ecs_poly_assert(world, ecs_stage_t); - ecs_stage_t *stage = (ecs_stage_t*)world; - ecs_check(stage->async == true, ECS_INVALID_PARAMETER, NULL); - flecs_stage_fini(stage->world, stage); - ecs_os_free(stage); -error: - return; -} - -bool ecs_stage_is_async( - ecs_world_t *stage) -{ - if (!stage) { - return false; - } - - if (!ecs_poly_is(stage, ecs_stage_t)) { - return false; - } - - return ((ecs_stage_t*)stage)->async; -} - bool ecs_is_deferred( const ecs_world_t *world) { diff --git a/vendors/flecs/src/stage.h b/vendors/flecs/src/stage.h index f0afd91fc..7311eb3c3 100644 --- a/vendors/flecs/src/stage.h +++ b/vendors/flecs/src/stage.h @@ -6,16 +6,6 @@ #ifndef FLECS_STAGE_H #define FLECS_STAGE_H -/* Initialize stage data structures */ -void flecs_stage_init( - ecs_world_t *world, - ecs_stage_t *stage); - -/* Deinitialize stage */ -void flecs_stage_fini( - ecs_world_t *world, - ecs_stage_t *stage); - /* Post-frame merge actions */ void flecs_stage_merge_post_frame( ecs_world_t *world, @@ -88,7 +78,8 @@ void* flecs_defer_set( ecs_entity_t entity, ecs_entity_t component, ecs_size_t size, - void *value); + void *value, + bool *is_new); bool flecs_defer_end( ecs_world_t *world, @@ -109,4 +100,14 @@ void flecs_commands_push( void flecs_commands_pop( ecs_stage_t *stage); +ecs_entity_t flecs_stage_set_system( + ecs_stage_t *stage, + ecs_entity_t system); + +ecs_allocator_t* flecs_stage_get_allocator( + ecs_world_t *world); + +ecs_stack_t* flecs_stage_get_stack_allocator( + ecs_world_t *world); + #endif diff --git a/vendors/flecs/src/storage/entity_index.c b/vendors/flecs/src/storage/entity_index.c index aac9f1ee9..4dbc0c836 100644 --- a/vendors/flecs/src/storage/entity_index.c +++ b/vendors/flecs/src/storage/entity_index.c @@ -59,7 +59,8 @@ ecs_record_t* flecs_entity_index_get_any( ecs_entity_index_page_t *page = ecs_vec_get_t(&index->pages, ecs_entity_index_page_t*, page_index)[0]; ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK]; - ecs_assert(r->dense != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(r->dense != 0, ECS_INVALID_PARAMETER, + "entity %u does not exist", (uint32_t)entity); return r; } @@ -70,7 +71,7 @@ ecs_record_t* flecs_entity_index_get( ecs_record_t *r = flecs_entity_index_get_any(index, entity); ecs_assert(r->dense < index->alive_count, ECS_INVALID_PARAMETER, NULL); ecs_assert(ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] == entity, - ECS_INVALID_PARAMETER, NULL); + ECS_INVALID_PARAMETER, "mismatching liveliness generation for entity"); return r; } @@ -187,7 +188,7 @@ void flecs_entity_index_remove( ECS_INTERNAL_ERROR, NULL); } -void flecs_entity_index_set_generation( +void flecs_entity_index_make_alive( ecs_entity_index_t *index, uint64_t entity) { @@ -197,7 +198,7 @@ void flecs_entity_index_set_generation( } } -uint64_t flecs_entity_index_get_generation( +uint64_t flecs_entity_index_get_alive( const ecs_entity_index_t *index, uint64_t entity) { @@ -248,6 +249,11 @@ uint64_t flecs_entity_index_new_id( /* Create new id */ uint32_t id = (uint32_t)++ index->max_id; + + /* Make sure id hasn't been issued before */ + ecs_assert(!flecs_entity_index_exists(index, id), ECS_INVALID_OPERATION, + "new entity %u id already in use (likely due to overlapping ranges)", (uint32_t)id); + ecs_vec_append_t(index->allocator, &index->dense, uint64_t)[0] = id; ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id); @@ -279,6 +285,11 @@ uint64_t* flecs_entity_index_new_ids( int32_t i, to_add = new_count - dense_count; for (i = 0; i < to_add; i ++) { uint32_t id = (uint32_t)++ index->max_id; + + /* Make sure id hasn't been issued before */ + ecs_assert(!flecs_entity_index_exists(index, id), ECS_INVALID_OPERATION, + "new entity %u id already in use (likely due to overlapping ranges)", (uint32_t)id); + int32_t dense = dense_count + i; ecs_vec_get_t(&index->dense, uint64_t, dense)[0] = id; ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id); @@ -340,49 +351,3 @@ const uint64_t* flecs_entity_index_ids( { return ecs_vec_get_t(&index->dense, uint64_t, 1); } - -static -void flecs_entity_index_copy_intern( - ecs_entity_index_t * dst, - const ecs_entity_index_t * src) -{ - flecs_entity_index_set_size(dst, flecs_entity_index_size(src)); - const uint64_t *ids = flecs_entity_index_ids(src); - - int32_t i, count = src->alive_count; - for (i = 0; i < count - 1; i ++) { - uint64_t id = ids[i]; - ecs_record_t *src_ptr = flecs_entity_index_get(src, id); - ecs_record_t *dst_ptr = flecs_entity_index_ensure(dst, id); - flecs_entity_index_set_generation(dst, id); - ecs_os_memcpy_t(dst_ptr, src_ptr, ecs_record_t); - } - - dst->max_id = src->max_id; - - ecs_assert(src->alive_count == dst->alive_count, ECS_INTERNAL_ERROR, NULL); -} - -void flecs_entity_index_copy( - ecs_entity_index_t *dst, - const ecs_entity_index_t *src) -{ - if (!src) { - return; - } - - flecs_entity_index_init(src->allocator, dst); - flecs_entity_index_copy_intern(dst, src); -} - -void flecs_entity_index_restore( - ecs_entity_index_t *dst, - const ecs_entity_index_t *src) -{ - if (!src) { - return; - } - - flecs_entity_index_clear(dst); - flecs_entity_index_copy_intern(dst, src); -} diff --git a/vendors/flecs/src/storage/entity_index.h b/vendors/flecs/src/storage/entity_index.h index 05dc37322..a81db5dbd 100644 --- a/vendors/flecs/src/storage/entity_index.h +++ b/vendors/flecs/src/storage/entity_index.h @@ -64,12 +64,12 @@ void flecs_entity_index_remove( uint64_t entity); /* Set generation of entity */ -void flecs_entity_index_set_generation( +void flecs_entity_index_make_alive( ecs_entity_index_t *index, uint64_t entity); /* Get current generation of entity */ -uint64_t flecs_entity_index_get_generation( +uint64_t flecs_entity_index_get_alive( const ecs_entity_index_t *index, uint64_t entity); @@ -122,14 +122,6 @@ void flecs_entity_index_clear( const uint64_t* flecs_entity_index_ids( const ecs_entity_index_t *index); -void flecs_entity_index_copy( - ecs_entity_index_t *dst, - const ecs_entity_index_t *src); - -void flecs_entity_index_restore( - ecs_entity_index_t *dst, - const ecs_entity_index_t *src); - #define ecs_eis(world) (&((world)->store.entity_index)) #define flecs_entities_init(world) flecs_entity_index_init(&world->allocator, ecs_eis(world)) #define flecs_entities_fini(world) flecs_entity_index_fini(ecs_eis(world)) @@ -138,8 +130,8 @@ void flecs_entity_index_restore( #define flecs_entities_get_any(world, entity) flecs_entity_index_get_any(ecs_eis(world), entity) #define flecs_entities_ensure(world, entity) flecs_entity_index_ensure(ecs_eis(world), entity) #define flecs_entities_remove(world, entity) flecs_entity_index_remove(ecs_eis(world), entity) -#define flecs_entities_set_generation(world, entity) flecs_entity_index_set_generation(ecs_eis(world), entity) -#define flecs_entities_get_generation(world, entity) flecs_entity_index_get_generation(ecs_eis(world), entity) +#define flecs_entities_make_alive(world, entity) flecs_entity_index_make_alive(ecs_eis(world), entity) +#define flecs_entities_get_alive(world, entity) flecs_entity_index_get_alive(ecs_eis(world), entity) #define flecs_entities_is_alive(world, entity) flecs_entity_index_is_alive(ecs_eis(world), entity) #define flecs_entities_is_valid(world, entity) flecs_entity_index_is_valid(ecs_eis(world), entity) #define flecs_entities_exists(world, entity) flecs_entity_index_exists(ecs_eis(world), entity) @@ -152,7 +144,5 @@ void flecs_entity_index_restore( #define flecs_entities_not_alive_count(world) flecs_entity_index_not_alive_count(ecs_eis(world)) #define flecs_entities_clear(world) flecs_entity_index_clear(ecs_eis(world)) #define flecs_entities_ids(world) flecs_entity_index_ids(ecs_eis(world)) -#define flecs_entities_copy(dst, src) flecs_entity_index_copy(dst, src) -#define flecs_entities_restore(dst, src) flecs_entity_index_restore(dst, src) #endif diff --git a/vendors/flecs/src/storage/id_index.c b/vendors/flecs/src/storage/id_index.c index 9df5ad118..d546f11f6 100644 --- a/vendors/flecs/src/storage/id_index.c +++ b/vendors/flecs/src/storage/id_index.c @@ -1,5 +1,5 @@ /** - * @file id_index.c + * @file storage/id_index.c * @brief Index for looking up tables by (component) id. * * An id record stores the administration for an in use (component) id, that is @@ -128,6 +128,64 @@ ecs_id_t flecs_id_record_hash( return id; } +void flecs_id_record_init_sparse( + ecs_world_t *world, + ecs_id_record_t *idr) +{ + if (!idr->sparse) { + if (idr->flags & EcsIdIsSparse) { + ecs_assert(!(idr->flags & EcsIdIsUnion), ECS_CONSTRAINT_VIOLATED, + "cannot mix union and sparse traits"); + ecs_assert(idr->type_info != NULL, ECS_INVALID_OPERATION, + "only components can be marked as sparse"); + idr->sparse = flecs_walloc_t(world, ecs_sparse_t); + flecs_sparse_init(idr->sparse, NULL, NULL, idr->type_info->size); + } else + if (idr->flags & EcsIdIsUnion) { + idr->sparse = flecs_walloc_t(world, ecs_switch_t); + flecs_switch_init(idr->sparse, &world->allocator); + } + } +} + +static +void flecs_id_record_fini_sparse( + ecs_world_t *world, + ecs_id_record_t *idr) +{ + if (idr->sparse) { + if (idr->flags & EcsIdIsSparse) { + ecs_assert(flecs_sparse_count(idr->sparse) == 0, + ECS_INTERNAL_ERROR, NULL); + flecs_sparse_fini(idr->sparse); + flecs_wfree_t(world, ecs_sparse_t, idr->sparse); + } else + if (idr->flags & EcsIdIsUnion) { + flecs_switch_fini(idr->sparse); + flecs_wfree_t(world, ecs_switch_t, idr->sparse); + } else { + ecs_abort(ECS_INTERNAL_ERROR, "unknown sparse storage"); + } + } +} + +static +ecs_flags32_t flecs_id_record_event_flags( + ecs_world_t *world, + ecs_id_t id) +{ + ecs_observable_t *o = &world->observable; + ecs_flags32_t result = 0; + result |= flecs_observers_exist(o, id, EcsOnAdd) * EcsIdHasOnAdd; + result |= flecs_observers_exist(o, id, EcsOnRemove) * EcsIdHasOnRemove; + result |= flecs_observers_exist(o, id, EcsOnSet) * EcsIdHasOnSet; + result |= flecs_observers_exist(o, id, EcsOnTableFill) * EcsIdHasOnTableFill; + result |= flecs_observers_exist(o, id, EcsOnTableEmpty) * EcsIdHasOnTableEmpty; + result |= flecs_observers_exist(o, id, EcsOnTableCreate) * EcsIdHasOnTableCreate; + result |= flecs_observers_exist(o, id, EcsOnTableDelete) * EcsIdHasOnTableDelete; + return result; +} + static ecs_id_record_t* flecs_id_record_new( ecs_world_t *world, @@ -156,7 +214,7 @@ ecs_id_record_t* flecs_id_record_new( if (is_pair) { // rel = ecs_pair_first(world, id); rel = ECS_PAIR_FIRST(id); - rel = flecs_entities_get_generation(world, rel); + rel = flecs_entities_get_alive(world, rel); ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL); /* Relationship object can be 0, as tables without a ChildOf @@ -166,22 +224,74 @@ ecs_id_record_t* flecs_id_record_new( #ifdef FLECS_DEBUG /* Check constraints */ if (tgt) { - tgt = flecs_entities_get_generation(world, tgt); + tgt = flecs_entities_get_alive(world, tgt); ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); + + /* Can't use relationship as target */ + if (ecs_has_id(world, tgt, EcsRelationship)) { + if (!ecs_id_is_wildcard(rel) && + !ecs_has_id(world, rel, EcsTrait)) + { + char *idstr = ecs_id_str(world, id); + char *tgtstr = ecs_id_str(world, tgt); + ecs_err("constraint violated: relationship '%s' cannot be used" + " as target in pair '%s'", tgtstr, idstr); + ecs_os_free(tgtstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } + } + } + + if (ecs_has_id(world, rel, EcsTarget)) { + char *idstr = ecs_id_str(world, id); + char *relstr = ecs_id_str(world, rel); + ecs_err("constraint violated: " + "%s: target '%s' cannot be used as relationship", + idstr, relstr); + ecs_os_free(relstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif } - if (tgt && !ecs_id_is_wildcard(tgt)) { + + if (tgt && !ecs_id_is_wildcard(tgt) && tgt != EcsUnion) { /* Check if target of relationship satisfies OneOf property */ ecs_entity_t oneof = flecs_get_oneof(world, rel); - ecs_check( !oneof || ecs_has_pair(world, tgt, EcsChildOf, oneof), - ECS_CONSTRAINT_VIOLATED, NULL); - (void)oneof; + if (oneof) { + if (!ecs_has_pair(world, tgt, EcsChildOf, oneof)) { + char *idstr = ecs_id_str(world, id); + char *tgtstr = ecs_get_path(world, tgt); + char *oneofstr = ecs_get_path(world, oneof); + ecs_err("OneOf constraint violated: " + "%s: '%s' is not a child of '%s'", + idstr, tgtstr, oneofstr); + ecs_os_free(oneofstr); + ecs_os_free(tgtstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } + } /* Check if we're not trying to inherit from a final target */ if (rel == EcsIsA) { - bool is_final = ecs_has_id(world, tgt, EcsFinal); - ecs_check(!is_final, ECS_CONSTRAINT_VIOLATED, - "cannot inherit from final entity"); - (void)is_final; + if (ecs_has_id(world, tgt, EcsFinal)) { + char *idstr = ecs_id_str(world, id); + char *tgtstr = ecs_get_path(world, tgt); + ecs_err("Final constraint violated: " + "%s: cannot inherit from final entity '%s'", + idstr, tgtstr); + ecs_os_free(tgtstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } } } #endif @@ -195,19 +305,35 @@ ecs_id_record_t* flecs_id_record_new( /* If pair is not a wildcard, append it to wildcard lists. These * allow for quickly enumerating all relationships for an object, - * or all objecs for a relationship. */ + * or all objects for a relationship. */ flecs_insert_id_elem(world, idr, ecs_pair(rel, EcsWildcard), idr_r); idr_t = flecs_id_record_ensure(world, ecs_pair(EcsWildcard, tgt)); flecs_insert_id_elem(world, idr, ecs_pair(EcsWildcard, tgt), idr_t); - - if (rel == EcsUnion) { - idr->flags |= EcsIdUnion; - } } } else { rel = id & ECS_COMPONENT_MASK; ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL); + + /* Can't use relationship outside of a pair */ +#ifdef FLECS_DEBUG + rel = flecs_entities_get_alive(world, rel); + bool is_tgt = false; + if (ecs_has_id(world, rel, EcsRelationship) || + (is_tgt = ecs_has_id(world, rel, EcsTarget))) + { + char *idstr = ecs_id_str(world, id); + char *relstr = ecs_id_str(world, rel); + ecs_err("constraint violated: " + "%s: relationship%s '%s' cannot be used as component", + idstr, is_tgt ? " target" : "", relstr); + ecs_os_free(relstr); + ecs_os_free(idstr); + #ifndef FLECS_SOFT_ASSERT + ecs_abort(ECS_CONSTRAINT_VIOLATED, NULL); + #endif + } +#endif } /* Initialize type info if id is not a tag */ @@ -242,15 +368,15 @@ ecs_id_record_t* flecs_id_record_new( } } - ecs_observable_t *o = &world->observable; - idr->flags |= flecs_observers_exist(o, id, EcsOnAdd) * EcsIdHasOnAdd; - idr->flags |= flecs_observers_exist(o, id, EcsOnRemove) * EcsIdHasOnRemove; - idr->flags |= flecs_observers_exist(o, id, EcsOnSet) * EcsIdHasOnSet; - idr->flags |= flecs_observers_exist(o, id, EcsUnSet) * EcsIdHasUnSet; - idr->flags |= flecs_observers_exist(o, id, EcsOnTableFill) * EcsIdHasOnTableFill; - idr->flags |= flecs_observers_exist(o, id, EcsOnTableEmpty) * EcsIdHasOnTableEmpty; - idr->flags |= flecs_observers_exist(o, id, EcsOnTableCreate) * EcsIdHasOnTableCreate; - idr->flags |= flecs_observers_exist(o, id, EcsOnTableDelete) * EcsIdHasOnTableDelete; + idr->flags |= flecs_id_record_event_flags(world, id); + + if (idr->flags & EcsIdIsSparse) { + flecs_id_record_init_sparse(world, idr); + } else if (idr->flags & EcsIdIsUnion) { + if (ECS_IS_PAIR(id) && ECS_PAIR_SECOND(id) == EcsUnion) { + flecs_id_record_init_sparse(world, idr); + } + } if (ecs_should_log_1()) { char *id_str = ecs_id_str(world, id); @@ -265,10 +391,6 @@ ecs_id_record_t* flecs_id_record_new( world->info.pair_id_count += is_pair; return idr; -#ifdef FLECS_DEBUG -error: - return NULL; -#endif } static @@ -287,13 +409,13 @@ void flecs_id_record_free( ecs_world_t *world, ecs_id_record_t *idr) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); ecs_id_t id = idr->id; flecs_id_record_assert_empty(idr); - /* Id is still in use by a filter, query, rule or observer */ + /* Id is still in use by a query */ ecs_assert((world->flags & EcsWorldQuit) || (idr->keep_alive == 0), ECS_ID_IN_USE, "cannot delete id that is queried for"); @@ -335,6 +457,9 @@ void flecs_id_record_free( } } + /* Cleanup sparse storage */ + flecs_id_record_fini_sparse(world, idr); + /* Update counters */ world->info.id_delete_total ++; world->info.pair_id_count -= ECS_IS_PAIR(id); @@ -376,7 +501,7 @@ ecs_id_record_t* flecs_id_record_get( const ecs_world_t *world, ecs_id_t id) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); if (id == ecs_pair(EcsIsA, EcsWildcard)) { return world->idr_isa_wildcard; } else if (id == ecs_pair(EcsChildOf, EcsWildcard)) { @@ -399,29 +524,6 @@ ecs_id_record_t* flecs_id_record_get( return idr; } -ecs_id_record_t* flecs_query_id_record_get( - const ecs_world_t *world, - ecs_id_t id) -{ - ecs_id_record_t *idr = flecs_id_record_get(world, id); - if (!idr) { - ecs_entity_t first = ECS_PAIR_FIRST(id); - if (ECS_IS_PAIR(id) && (first != EcsWildcard)) { - idr = flecs_id_record_get(world, ecs_pair(EcsUnion, first)); - } - return idr; - } - if (ECS_IS_PAIR(id) && - ECS_PAIR_SECOND(id) == EcsWildcard && - (idr->flags & EcsIdUnion)) - { - idr = flecs_id_record_get(world, - ecs_pair(EcsUnion, ECS_PAIR_FIRST(id))); - } - - return idr; -} - void flecs_id_record_claim( ecs_world_t *world, ecs_id_record_t *idr) @@ -448,16 +550,12 @@ void flecs_id_record_release_tables( ecs_world_t *world, ecs_id_record_t *idr) { - /* Cache should not contain tables that aren't empty */ - ecs_assert(flecs_table_cache_count(&idr->cache) == 0, - ECS_INTERNAL_ERROR, NULL); - ecs_table_cache_iter_t it; - if (flecs_table_cache_empty_iter(&idr->cache, &it)) { + if (flecs_table_cache_all_iter(&idr->cache, &it)) { ecs_table_record_t *tr; while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { /* Release current table */ - flecs_table_free(world, tr->hdr.table); + flecs_table_fini(world, tr->hdr.table); } } } @@ -504,7 +602,7 @@ ecs_hashmap_t* flecs_id_name_index_ensure( ecs_world_t *world, ecs_id_t id) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_id_record_t *idr = flecs_id_record_get(world, id); ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); @@ -516,7 +614,7 @@ ecs_hashmap_t* flecs_id_name_index_get( const ecs_world_t *world, ecs_id_t id) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_id_record_t *idr = flecs_id_record_get(world, id); if (!idr) { @@ -531,7 +629,7 @@ ecs_table_record_t* flecs_table_record_get( const ecs_table_t *table, ecs_id_t id) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_id_record_t* idr = flecs_id_record_get(world, id); if (!idr) { @@ -587,3 +685,53 @@ void flecs_fini_id_records( ecs_map_fini(&world->id_index_hi); ecs_os_free(world->id_index_lo); } + +static +ecs_flags32_t flecs_id_flags( + ecs_world_t *world, + ecs_id_t id) +{ + const ecs_id_record_t *idr = flecs_id_record_get(world, id); + if (idr) { + ecs_flags32_t extra_flags = 0; + if (idr->flags & EcsIdOnInstantiateInherit) { + extra_flags |= EcsIdHasOnAdd|EcsIdHasOnRemove; + } + return idr->flags|extra_flags; + } + return flecs_id_record_event_flags(world, id); +} + +ecs_flags32_t flecs_id_flags_get( + ecs_world_t *world, + ecs_id_t id) +{ + ecs_flags32_t result = flecs_id_flags(world, id); + + if (id != EcsAny) { + result |= flecs_id_flags(world, EcsAny); + } + + if (ECS_IS_PAIR(id)) { + ecs_entity_t first = ECS_PAIR_FIRST(id); + ecs_entity_t second = ECS_PAIR_SECOND(id); + + if (id != ecs_pair(first, EcsWildcard)) { + result |= flecs_id_flags(world, ecs_pair(first, EcsWildcard)); + } + if (id != ecs_pair(EcsWildcard, second)) { + result |= flecs_id_flags(world, ecs_pair(EcsWildcard, second)); + } + if (id != ecs_pair(EcsWildcard, EcsWildcard)) { + result |= flecs_id_flags(world, ecs_pair(EcsWildcard, EcsWildcard)); + } + + if (first == EcsIsA) { + result |= EcsIdHasOnAdd|EcsIdHasOnRemove; + } + } else if (id != EcsWildcard) { + result |= flecs_id_flags(world, EcsWildcard); + } + + return result; +} diff --git a/vendors/flecs/src/storage/id_index.h b/vendors/flecs/src/storage/id_index.h index 83f1609c8..27072fc0b 100644 --- a/vendors/flecs/src/storage/id_index.h +++ b/vendors/flecs/src/storage/id_index.h @@ -1,19 +1,11 @@ /** - * @file id_index.h + * @file storage/id_index.h * @brief Index for looking up tables by (component) id. */ #ifndef FLECS_ID_INDEX_H #define FLECS_ID_INDEX_H -/* Payload for id cache */ -struct ecs_table_record_t { - ecs_table_cache_hdr_t hdr; /* Table cache header */ - int16_t index; /* First type index where id occurs in table */ - int16_t count; /* Number of times id occurs in table */ - int16_t column; /* First column index where id occurs */ -}; - /* Linked list of id records */ typedef struct ecs_id_record_elem_t { struct ecs_id_record_t *prev, *next; @@ -35,7 +27,7 @@ typedef struct ecs_reachable_cache_t { ecs_vec_t ids; /* vec */ } ecs_reachable_cache_t; -/* Payload for id index which contains all datastructures for an id. */ +/* Payload for id index which contains all data structures for an id. */ struct ecs_id_record_t { /* Cache with all tables that contain the id. Must be first member. */ ecs_table_cache_t cache; /* table_cache */ @@ -52,6 +44,9 @@ struct ecs_id_record_t { /* Name lookup index (currently only used for ChildOf pairs) */ ecs_hashmap_t *name_index; + /* Storage for sparse components or union relationships */ + void *sparse; + /* Lists for all id records that match a pair wildcard. The wildcard id * record is at the head of the list. */ ecs_id_record_elem_t first; /* (R, *) */ @@ -69,7 +64,7 @@ struct ecs_id_record_t { * queried for. */ int32_t keep_alive; - /* Cache invalidation counter */ + /* Cache for finding components that are reachable through a relationship */ ecs_reachable_cache_t reachable; }; @@ -78,13 +73,6 @@ ecs_id_record_t* flecs_id_record_get( const ecs_world_t *world, ecs_id_t id); -/* Get id record for id for searching. - * Same as flecs_id_record_get, but replaces (R, *) with (Union, R) if R is a - * union relationship. */ -ecs_id_record_t* flecs_query_id_record_get( - const ecs_world_t *world, - ecs_id_t id); - /* Ensure id record for id */ ecs_id_record_t* flecs_id_record_ensure( ecs_world_t *world, @@ -136,6 +124,11 @@ ecs_table_record_t* flecs_id_record_get_table( const ecs_id_record_t *idr, const ecs_table_t *table); +/* Init sparse storage */ +void flecs_id_record_init_sparse( + ecs_world_t *world, + ecs_id_record_t *idr); + /* Bootstrap cached id records */ void flecs_init_id_records( ecs_world_t *world); @@ -144,4 +137,9 @@ void flecs_init_id_records( void flecs_fini_id_records( ecs_world_t *world); +/* Return flags for matching id records */ +ecs_flags32_t flecs_id_flags_get( + ecs_world_t *world, + ecs_id_t id); + #endif diff --git a/vendors/flecs/src/storage/table.c b/vendors/flecs/src/storage/table.c index 7cda65a14..c6e88734f 100644 --- a/vendors/flecs/src/storage/table.c +++ b/vendors/flecs/src/storage/table.c @@ -1,5 +1,5 @@ /** - * @file table.c + * @file storage/table.c * @brief Table storage implementation. * * Tables are the data structure that store the component data. Tables have @@ -27,59 +27,57 @@ #include "../private_api.h" /* Table sanity check to detect storage issues. Only enabled in SANITIZE mode as - * this can severly slow down many ECS operations. */ + * this can severely slow down many ECS operations. */ #ifdef FLECS_SANITIZE static -void flecs_table_check_sanity(ecs_table_t *table) { - int32_t size = ecs_vec_size(&table->data.entities); - int32_t count = ecs_vec_count(&table->data.entities); +void flecs_table_check_sanity( + ecs_world_t *world, + ecs_table_t *table) +{ + int32_t i, count = ecs_table_count(table); + int32_t size = ecs_table_size(table); + ecs_assert(count <= size, ECS_INTERNAL_ERROR, NULL); - int32_t i; - int32_t sw_offset = table->_ ? table->_->sw_offset : 0; - int32_t sw_count = table->_ ? table->_->sw_count : 0; int32_t bs_offset = table->_ ? table->_->bs_offset : 0; int32_t bs_count = table->_ ? table->_->bs_count : 0; int32_t type_count = table->type.count; ecs_id_t *ids = table->type.array; - ecs_assert((sw_count + sw_offset) <= type_count, ECS_INTERNAL_ERROR, NULL); ecs_assert((bs_count + bs_offset) <= type_count, ECS_INTERNAL_ERROR, NULL); + if (size) { + ecs_assert(table->data.entities != NULL, ECS_INTERNAL_ERROR, NULL); + } else { + ecs_assert(table->data.entities == NULL, ECS_INTERNAL_ERROR, NULL); + } + if (table->column_count) { int32_t column_count = table->column_count; ecs_assert(type_count >= column_count, ECS_INTERNAL_ERROR, NULL); - int32_t *column_map = table->column_map; + int16_t *column_map = table->column_map; ecs_assert(column_map != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(table->data.columns != NULL, ECS_INTERNAL_ERROR, NULL); for (i = 0; i < column_count; i ++) { - ecs_vec_t *column = &table->data.columns[i].data; - ecs_assert(size == column->size, ECS_INTERNAL_ERROR, NULL); - ecs_assert(count == column->count, ECS_INTERNAL_ERROR, NULL); int32_t column_map_id = column_map[i + type_count]; ecs_assert(column_map_id >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(table->data.columns[i].ti != NULL, + ECS_INTERNAL_ERROR, NULL); + if (size) { + ecs_assert(table->data.columns[i].data != NULL, + ECS_INTERNAL_ERROR, NULL); + } else { + ecs_assert(table->data.columns[i].data == NULL, + ECS_INTERNAL_ERROR, NULL); + } } } else { ecs_assert(table->column_map == NULL, ECS_INTERNAL_ERROR, NULL); } - if (sw_count) { - ecs_assert(table->_->sw_columns != NULL, - ECS_INTERNAL_ERROR, NULL); - for (i = 0; i < sw_count; i ++) { - ecs_switch_t *sw = &table->_->sw_columns[i]; - ecs_assert(ecs_vec_count(&sw->values) == count, - ECS_INTERNAL_ERROR, NULL); - ecs_assert(ECS_PAIR_FIRST(ids[i + sw_offset]) == EcsUnion, - ECS_INTERNAL_ERROR, NULL); - } - } - if (bs_count) { - ecs_assert(table->_->bs_columns != NULL, - ECS_INTERNAL_ERROR, NULL); + ecs_assert(table->_->bs_columns != NULL, ECS_INTERNAL_ERROR, NULL); for (i = 0; i < bs_count; i ++) { ecs_bitset_t *bs = &table->_->bs_columns[i]; ecs_assert(flecs_bitset_count(bs) == count, @@ -93,7 +91,7 @@ void flecs_table_check_sanity(ecs_table_t *table) { (table->flags & EcsTableHasTraversable), ECS_INTERNAL_ERROR, NULL); } #else -#define flecs_table_check_sanity(table) +#define flecs_table_check_sanity(world, table) #endif /* Set flags for type hooks so table operations can quickly check whether a @@ -132,24 +130,34 @@ void flecs_table_init_columns( ecs_table_t *table, int32_t column_count) { + int16_t i, cur = 0, ids_count = flecs_ito(int16_t, table->type.count); + + for (i = 0; i < ids_count; i ++) { + ecs_id_t id = table->type.array[i]; + if (id < FLECS_HI_COMPONENT_ID) { + table->component_map[id] = flecs_ito(int16_t, -(i + 1)); + } + } + if (!column_count) { return; } - int32_t i, cur = 0, ids_count = table->type.count; ecs_column_t *columns = flecs_wcalloc_n(world, ecs_column_t, column_count); table->data.columns = columns; ecs_id_t *ids = table->type.array; ecs_table_record_t *records = table->_->records; - int32_t *t2s = table->column_map; - int32_t *s2t = &table->column_map[ids_count]; + int16_t *t2s = table->column_map; + int16_t *s2t = &table->column_map[ids_count]; for (i = 0; i < ids_count; i ++) { + ecs_id_t id = ids[i]; ecs_table_record_t *tr = &records[i]; ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; const ecs_type_info_t *ti = idr->type_info; - if (!ti) { + + if (!ti || (idr->flags & EcsIdIsSparse)) { t2s[i] = -1; continue; } @@ -159,24 +167,26 @@ void flecs_table_init_columns( tr->column = flecs_ito(int16_t, cur); columns[cur].ti = ECS_CONST_CAST(ecs_type_info_t*, ti); - columns[cur].id = ids[i]; - columns[cur].size = ti->size; - - if (ECS_IS_PAIR(ids[i])) { - ecs_table_record_t *wc_tr = flecs_id_record_get_table( - idr->parent, table); - if (wc_tr->index == tr->index) { - wc_tr->column = tr->column; - } + + if (id < FLECS_HI_COMPONENT_ID) { + table->component_map[id] = flecs_ito(int16_t, cur + 1); } -#ifdef FLECS_DEBUG - ecs_vec_init(NULL, &columns[cur].data, ti->size, 0); -#endif - table->flags |= flecs_type_info_flags(ti); cur ++; } + + int32_t record_count = table->_->record_count; + for (; i < record_count; i ++) { + ecs_table_record_t *tr = &records[i]; + ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; + ecs_id_t id = idr->id; + + if (ecs_id_is_wildcard(id)) { + ecs_table_record_t *first_tr = &records[tr->index]; + tr->column = first_tr->column; + } + } } /* Initialize table storage */ @@ -184,22 +194,10 @@ void flecs_table_init_data( ecs_world_t *world, ecs_table_t *table) { - ecs_data_t *storage = &table->data; - ecs_vec_init_t(NULL, &storage->entities, ecs_entity_t, 0); - flecs_table_init_columns(world, table, table->column_count); ecs_table__t *meta = table->_; - int32_t i, sw_count = meta->sw_count; - int32_t bs_count = meta->bs_count; - - if (sw_count) { - meta->sw_columns = flecs_wcalloc_n(world, ecs_switch_t, sw_count); - for (i = 0; i < sw_count; i ++) { - flecs_switch_init(&meta->sw_columns[i], - &world->allocator, 0); - } - } + int32_t i, bs_count = meta->bs_count; if (bs_count) { meta->bs_columns = flecs_wcalloc_n(world, ecs_bitset_t, bs_count); @@ -235,6 +233,8 @@ void flecs_table_init_flags( table->flags |= EcsTableIsPrefab; } else if (id == EcsDisabled) { table->flags |= EcsTableIsDisabled; + } else if (id == EcsNotQueryable) { + table->flags |= EcsTableNotQueryable; } else { if (ECS_IS_PAIR(id)) { ecs_entity_t r = ECS_PAIR_FIRST(id); @@ -258,18 +258,6 @@ void flecs_table_init_flags( } } else if (id == ecs_pair_t(EcsIdentifier, EcsName)) { table->flags |= EcsTableHasName; - } else if (r == EcsUnion) { - ecs_table__t *meta = table->_; - table->flags |= EcsTableHasUnion; - - if (!meta->sw_count) { - meta->sw_offset = flecs_ito(int16_t, i); - } - meta->sw_count ++; - } else if (r == ecs_id(EcsTarget)) { - ecs_table__t *meta = table->_; - table->flags |= EcsTableHasTarget; - meta->ft_offset = flecs_ito(int16_t, i); } else if (r == ecs_id(EcsPoly)) { table->flags |= EcsTableHasBuiltins; } @@ -283,7 +271,7 @@ void flecs_table_init_flags( } meta->bs_count ++; } - if (ECS_HAS_ID_FLAG(id, OVERRIDE)) { + if (ECS_HAS_ID_FLAG(id, AUTO_OVERRIDE)) { table->flags |= EcsTableHasOverrides; } } @@ -318,6 +306,20 @@ void flecs_table_append_to_records( ecs_assert(tr->hdr.cache != NULL, ECS_INTERNAL_ERROR, NULL); } +void flecs_table_emit( + ecs_world_t *world, + ecs_table_t *table, + ecs_entity_t event) +{ + flecs_emit(world, world, 0, &(ecs_event_desc_t) { + .ids = &table->type, + .event = event, + .table = table, + .flags = EcsEventTableOnly, + .observable = world + }); +} + /* Main table initialization function */ void flecs_table_init( ecs_world_t *world, @@ -488,8 +490,8 @@ void flecs_table_init( last_pair = dst_i; /* Add a (*, Target) record for each relationship target. Type - * ids are sorted relationship-first, so we can't simply do a single linear - * scan to find all occurrences for a target. */ + * ids are sorted relationship-first, so we can't simply do a single + * linear scan to find all occurrences for a target. */ for (dst_i = first_pair; dst_i < last_pair; dst_i ++) { ecs_id_t dst_id = dst_ids[dst_i]; ecs_id_t tgt_id = ecs_pair(EcsWildcard, ECS_PAIR_SECOND(dst_id)); @@ -560,19 +562,25 @@ void flecs_table_init( /* Initialize column index (will be overwritten by init_columns) */ tr->column = -1; - if (idr->flags & EcsIdAlwaysOverride) { + if (ECS_ID_ON_INSTANTIATE(idr->flags) == EcsOverride) { table->flags |= EcsTableHasOverrides; } if ((i < table->type.count) && (idr->type_info != NULL)) { - column_count ++; + if (!(idr->flags & EcsIdIsSparse)) { + column_count ++; + } } } + table->component_map = flecs_wcalloc_n( + world, int16_t, FLECS_HI_COMPONENT_ID); + if (column_count) { - table->column_map = flecs_walloc_n(world, int32_t, + table->column_map = flecs_walloc_n(world, int16_t, dst_count + column_count); } + table->column_count = flecs_ito(int16_t, column_count); flecs_table_init_data(world, table); @@ -584,13 +592,7 @@ void flecs_table_init( } if (table->flags & EcsTableHasOnTableCreate) { - flecs_emit(world, world, &(ecs_event_desc_t) { - .ids = &table->type, - .event = EcsOnTableCreate, - .table = table, - .flags = EcsEventTableOnly, - .observable = world - }); + flecs_table_emit(world, table, EcsOnTableCreate); } } @@ -627,22 +629,34 @@ static void flecs_table_add_trigger_flags( ecs_world_t *world, ecs_table_t *table, + ecs_id_t id, ecs_entity_t event) { (void)world; + ecs_flags32_t flags = 0; + if (event == EcsOnAdd) { - table->flags |= EcsTableHasOnAdd; + flags = EcsTableHasOnAdd; } else if (event == EcsOnRemove) { - table->flags |= EcsTableHasOnRemove; + flags = EcsTableHasOnRemove; } else if (event == EcsOnSet) { - table->flags |= EcsTableHasOnSet; - } else if (event == EcsUnSet) { - table->flags |= EcsTableHasUnSet; + flags = EcsTableHasOnSet; } else if (event == EcsOnTableFill) { - table->flags |= EcsTableHasOnTableFill; + flags = EcsTableHasOnTableFill; } else if (event == EcsOnTableEmpty) { - table->flags |= EcsTableHasOnTableEmpty; + flags = EcsTableHasOnTableEmpty; + } else if (event == EcsWildcard) { + flags = EcsTableHasOnAdd|EcsTableHasOnRemove|EcsTableHasOnSet| + EcsTableHasOnTableFill|EcsTableHasOnTableEmpty| + EcsTableHasOnTableCreate|EcsTableHasOnTableDelete; + } + + table->flags |= flags; + + /* Add observer flags to incoming edges for id */ + if (id && ((flags == EcsTableHasOnAdd) || (flags == EcsTableHasOnRemove))) { + flecs_table_edges_add_flags(world, table, id, flags); } } @@ -651,12 +665,14 @@ void flecs_table_add_trigger_flags( static void flecs_table_notify_on_remove( ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data) + ecs_table_t *table) { - int32_t count = data->entities.count; + int32_t count = ecs_table_count(table); if (count) { - flecs_notify_on_remove(world, table, NULL, 0, count, &table->type); + ecs_table_diff_t diff = ECS_TABLE_DIFF_INIT; + diff.removed = table->type; + diff.removed_flags = table->flags & EcsTableRemoveEdgeFlags; + flecs_notify_on_remove(world, table, NULL, 0, count, &diff); } } @@ -668,13 +684,19 @@ void flecs_table_invoke_hook( ecs_iter_action_t callback, ecs_entity_t event, ecs_column_t *column, - ecs_entity_t *entities, + const ecs_entity_t *entities, int32_t row, int32_t count) { - void *ptr = ecs_vec_get(&column->data, column->size, row); - flecs_invoke_hook(world, table, count, row, entities, ptr, column->id, - column->ti, event, callback); + int32_t column_index = flecs_ito(int32_t, column - table->data.columns); + ecs_assert(column_index >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column_index < table->column_count, ECS_INTERNAL_ERROR, NULL); + int32_t type_index = table->column_map[table->type.count + column_index]; + ecs_assert(type_index >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type_index < table->type.count, ECS_INTERNAL_ERROR, NULL); + const ecs_table_record_t *tr = &table->_->records[type_index]; + flecs_invoke_hook(world, table, tr, count, row, entities, + table->type.array[type_index], column->ti, event, callback); } /* Construct components */ @@ -684,11 +706,13 @@ void flecs_table_invoke_ctor( int32_t row, int32_t count) { + ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column->data != NULL, ECS_INTERNAL_ERROR, NULL); ecs_type_info_t *ti = column->ti; ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); ecs_xtor_t ctor = ti->hooks.ctor; if (ctor) { - void *ptr = ecs_vec_get(&column->data, column->size, row); + void *ptr = ECS_ELEM(column->data, ti->size, row); ctor(ptr, count, ti); } } @@ -700,11 +724,13 @@ void flecs_table_invoke_dtor( int32_t row, int32_t count) { + ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column->data != NULL, ECS_INTERNAL_ERROR, NULL); ecs_type_info_t *ti = column->ti; ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); ecs_xtor_t dtor = ti->hooks.dtor; if (dtor) { - void *ptr = ecs_vec_get(&column->data, column->size, row); + void *ptr = ECS_ELEM(column->data, ti->size, row); dtor(ptr, count, ti); } } @@ -720,6 +746,8 @@ void flecs_table_invoke_add_hooks( int32_t count, bool construct) { + ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column->data != NULL, ECS_INTERNAL_ERROR, NULL); ecs_type_info_t *ti = column->ti; ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); @@ -745,6 +773,8 @@ void flecs_table_invoke_remove_hooks( int32_t count, bool dtor) { + ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column->data != NULL, ECS_INTERNAL_ERROR, NULL); ecs_type_info_t *ti = column->ti; ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); @@ -764,19 +794,17 @@ static void flecs_table_dtor_all( ecs_world_t *world, ecs_table_t *table, - ecs_data_t *data, int32_t row, int32_t count, - bool update_entity_index, bool is_delete) { - /* Can't delete and not update the entity index */ - ecs_assert(!is_delete || update_entity_index, ECS_INTERNAL_ERROR, NULL); - - int32_t ids_count = table->column_count; - ecs_entity_t *entities = data->entities.array; + const ecs_entity_t *entities = ecs_table_entities(table); + int32_t column_count = table->column_count; int32_t i, c, end = row + count; + ecs_assert(!column_count || table->data.columns != NULL, + ECS_INTERNAL_ERROR, NULL); + if (is_delete && table->_->traversable_count) { /* If table contains monitored entities with traversable relationships, * make sure to invalidate observer cache */ @@ -789,8 +817,8 @@ void flecs_table_dtor_all( table->_->lock = true; /* Run on_remove callbacks first before destructing components */ - for (c = 0; c < ids_count; c++) { - ecs_column_t *column = &data->columns[c]; + for (c = 0; c < column_count; c++) { + ecs_column_t *column = &table->data.columns[c]; ecs_iter_action_t on_remove = column->ti->hooks.on_remove; if (on_remove) { flecs_table_invoke_hook(world, table, on_remove, EcsOnRemove, @@ -799,8 +827,8 @@ void flecs_table_dtor_all( } /* Destruct components */ - for (c = 0; c < ids_count; c++) { - flecs_table_invoke_dtor(&data->columns[c], row, count); + for (c = 0; c < column_count; c++) { + flecs_table_invoke_dtor(&table->data.columns[c], row, count); } /* Iterate entities first, then components. This ensures that only one @@ -809,31 +837,26 @@ void flecs_table_dtor_all( for (i = row; i < end; i ++) { /* Update entity index after invoking destructors so that entity can * be safely used in destructor callbacks. */ - if (update_entity_index) { - ecs_entity_t e = entities[i]; - ecs_assert(!e || ecs_is_valid(world, e), - ECS_INTERNAL_ERROR, NULL); + ecs_entity_t e = entities[i]; + ecs_assert(!e || ecs_is_valid(world, e), + ECS_INTERNAL_ERROR, NULL); - if (is_delete) { - flecs_entities_remove(world, e); - ecs_assert(ecs_is_valid(world, e) == false, - ECS_INTERNAL_ERROR, NULL); - } else { - // If this is not a delete, clear the entity index record - ecs_record_t *record = flecs_entities_get(world, e); - record->table = NULL; - record->row = 0; - } + if (is_delete) { + flecs_entities_remove(world, e); + ecs_assert(ecs_is_valid(world, e) == false, + ECS_INTERNAL_ERROR, NULL); } else { - /* This should only happen in rare cases, such as when the data - * cleaned up is not part of the world (like with snapshots) */ + // If this is not a delete, clear the entity index record + ecs_record_t *record = flecs_entities_get(world, e); + record->table = NULL; + record->row = 0; } } table->_->lock = false; /* If table does not have destructors, just update entity index */ - } else if (update_entity_index) { + } else { if (is_delete) { for (i = row; i < end; i ++) { ecs_entity_t e = entities[i]; @@ -854,69 +877,75 @@ void flecs_table_dtor_all( } } +#define FLECS_LOCKED_STORAGE_MSG \ + "to fix, defer operations with defer_begin/defer_end" + /* Cleanup table storage */ static void flecs_table_fini_data( ecs_world_t *world, ecs_table_t *table, - ecs_data_t *data, bool do_on_remove, - bool update_entity_index, bool is_delete, - bool deactivate) + bool deactivate, + bool deallocate) { - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - - if (!data) { - return; - } + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); if (do_on_remove) { - flecs_table_notify_on_remove(world, table, data); + flecs_table_notify_on_remove(world, table); } - int32_t count = flecs_table_data_count(data); + int32_t count = ecs_table_count(table); if (count) { - flecs_table_dtor_all(world, table, data, 0, count, - update_entity_index, is_delete); - } + flecs_table_dtor_all(world, table, 0, count, is_delete); + } + + if (deallocate) { + ecs_column_t *columns = table->data.columns; + if (columns) { + int32_t c, column_count = table->column_count; + for (c = 0; c < column_count; c ++) { + ecs_column_t *column = &columns[c]; + ecs_vec_t v = ecs_vec_from_column(column, table, column->ti->size); + ecs_vec_fini(&world->allocator, &v, column->ti->size); + column->data = NULL; + } - ecs_column_t *columns = data->columns; - if (columns) { - int32_t c, column_count = table->column_count; - for (c = 0; c < column_count; c ++) { - /* Sanity check */ - ecs_assert(columns[c].data.count == data->entities.count, - ECS_INTERNAL_ERROR, NULL); - ecs_vec_fini(&world->allocator, - &columns[c].data, columns[c].size); + flecs_wfree_n(world, ecs_column_t, table->column_count, columns); + table->data.columns = NULL; } - flecs_wfree_n(world, ecs_column_t, column_count, columns); - data->columns = NULL; } ecs_table__t *meta = table->_; - ecs_switch_t *sw_columns = meta->sw_columns; - if (sw_columns) { - int32_t c, column_count = meta->sw_count; - for (c = 0; c < column_count; c ++) { - flecs_switch_fini(&sw_columns[c]); - } - flecs_wfree_n(world, ecs_switch_t, column_count, sw_columns); - meta->sw_columns = NULL; - } - ecs_bitset_t *bs_columns = meta->bs_columns; if (bs_columns) { int32_t c, column_count = meta->bs_count; - for (c = 0; c < column_count; c ++) { - flecs_bitset_fini(&bs_columns[c]); + if (deallocate) { + for (c = 0; c < column_count; c ++) { + flecs_bitset_fini(&bs_columns[c]); + } + } + else { + for (c = 0; c < column_count; c++) { + bs_columns[c].count = 0; + } + } + + if (deallocate) { + flecs_wfree_n(world, ecs_bitset_t, column_count, bs_columns); + meta->bs_columns = NULL; } - flecs_wfree_n(world, ecs_bitset_t, column_count, bs_columns); - meta->bs_columns = NULL; } - ecs_vec_fini_t(&world->allocator, &data->entities, ecs_entity_t); + if (deallocate) { + ecs_vec_t v = ecs_vec_from_entities(table); + ecs_vec_fini_t(&world->allocator, &v, ecs_entity_t); + table->data.entities = NULL; + table->data.size = 0; + } + + table->data.count = 0; if (deactivate && count) { flecs_table_set_empty(world, table); @@ -926,82 +955,71 @@ void flecs_table_fini_data( table->flags &= ~EcsTableHasTraversable; } -ecs_vec_t* flecs_table_entities( - ecs_table_t *table) -{ - return &table->data.entities; -} - -ecs_entity_t* flecs_table_entities_array( - ecs_table_t *table) +const ecs_entity_t* ecs_table_entities( + const ecs_table_t *table) { - return ecs_vec_first(flecs_table_entities(table)); + return table->data.entities; } -/* Cleanup, no OnRemove, don't update entity index, don't deactivate table */ -void flecs_table_clear_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data) +/* Cleanup, no OnRemove, delete from entity index, deactivate table, retain allocations */ +void ecs_table_clear_entities( + ecs_world_t* world, + ecs_table_t* table) { - flecs_table_fini_data(world, table, data, false, false, false, false); + flecs_table_fini_data(world, table, true, true, true, false); } -/* Cleanup, no OnRemove, clear entity index, deactivate table */ +/* Cleanup, no OnRemove, clear entity index, deactivate table, free allocations */ void flecs_table_clear_entities_silent( ecs_world_t *world, ecs_table_t *table) { - flecs_table_fini_data(world, table, &table->data, false, true, false, true); + flecs_table_fini_data(world, table, false, false, true, true); } -/* Cleanup, run OnRemove, clear entity index, deactivate table */ +/* Cleanup, run OnRemove, clear entity index, deactivate table, free allocations */ void flecs_table_clear_entities( ecs_world_t *world, ecs_table_t *table) { - flecs_table_fini_data(world, table, &table->data, true, true, false, true); + flecs_table_fini_data(world, table, true, false, true, true); } -/* Cleanup, run OnRemove, delete from entity index, deactivate table */ +/* Cleanup, run OnRemove, delete from entity index, deactivate table, free allocations */ void flecs_table_delete_entities( ecs_world_t *world, ecs_table_t *table) { - flecs_table_fini_data(world, table, &table->data, true, true, true, true); + flecs_table_fini_data(world, table, true, true, true, true); } /* Unset all components in table. This function is called before a table is - * deleted, and invokes all UnSet handlers, if any */ + * deleted, and invokes all OnRemove handlers, if any */ void flecs_table_remove_actions( ecs_world_t *world, ecs_table_t *table) { (void)world; - flecs_table_notify_on_remove(world, table, &table->data); + flecs_table_notify_on_remove(world, table); } /* Free table resources. */ -void flecs_table_free( +void flecs_table_fini( ecs_world_t *world, ecs_table_t *table) { bool is_root = table == &world->store.root; - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); ecs_assert(is_root || table->id != 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(is_root || flecs_sparse_is_alive(&world->store.tables, table->id), ECS_INTERNAL_ERROR, NULL); (void)world; + ecs_os_perf_trace_push("flecs.table.free"); + if (!is_root && !(world->flags & EcsWorldQuit)) { if (table->flags & EcsTableHasOnTableDelete) { - flecs_emit(world, world, &(ecs_event_desc_t) { - .ids = &table->type, - .event = EcsOnTableDelete, - .table = table, - .flags = EcsEventTableOnly, - .observable = world - }); + flecs_table_emit(world, table, EcsOnTableDelete); } } @@ -1017,7 +1035,7 @@ void flecs_table_free( world->info.empty_table_count -= (ecs_table_count(table) == 0); /* Cleanup data, no OnRemove, delete from entity index, don't deactivate */ - flecs_table_fini_data(world, table, &table->data, false, true, true, false); + flecs_table_fini_data(world, table, false, true, false, true); flecs_table_clear_edges(world, table); if (!is_root) { @@ -1031,8 +1049,9 @@ void flecs_table_free( } flecs_wfree_n(world, int32_t, table->column_count + 1, table->dirty_state); - flecs_wfree_n(world, int32_t, table->column_count + table->type.count, + flecs_wfree_n(world, int16_t, table->column_count + table->type.count, table->column_map); + flecs_wfree_n(world, int16_t, FLECS_HI_COMPONENT_ID, table->component_map); flecs_table_records_unregister(world, table); /* Update counters */ @@ -1048,6 +1067,8 @@ void flecs_table_free( } ecs_log_pop_2(); + + ecs_os_perf_trace_pop("flecs.table.free"); } /* Free table type. Do this separately from freeing the table as types can be @@ -1064,7 +1085,7 @@ void flecs_table_reset( ecs_world_t *world, ecs_table_t *table) { - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); flecs_table_clear_edges(world, table); } @@ -1105,21 +1126,33 @@ void flecs_table_mark_dirty( ecs_table_t *table, ecs_entity_t component) { - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); if (table->dirty_state) { - ecs_id_record_t *idr = flecs_id_record_get(world, component); - if (!idr) { - return; - } + int32_t column; + if (component < FLECS_HI_COMPONENT_ID) { + column = table->component_map[component]; + if (column <= 0) { + return; + } + } else { + ecs_id_record_t *idr = flecs_id_record_get(world, component); + if (!idr) { + return; + } - const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); - if (!tr || tr->column == -1) { - return; + const ecs_table_record_t *tr = flecs_id_record_get_table(idr, table); + if (!tr || tr->column == -1) { + return; + } + + column = tr->column + 1; } - table->dirty_state[tr->column + 1] ++; + /* Column is offset by 1, 0 is reserved for entity column. */ + + table->dirty_state[column] ++; } } @@ -1128,7 +1161,7 @@ int32_t* flecs_table_get_dirty_state( ecs_world_t *world, ecs_table_t *table) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); if (!table->dirty_state) { int32_t column_count = table->column_count; @@ -1142,81 +1175,6 @@ int32_t* flecs_table_get_dirty_state( return table->dirty_state; } -/* Table move logic for switch (union relationship) column */ -static -void flecs_table_move_switch_columns( - ecs_table_t *dst_table, - int32_t dst_index, - ecs_table_t *src_table, - int32_t src_index, - int32_t count, - bool clear) -{ - ecs_table__t *dst_meta = dst_table->_; - ecs_table__t *src_meta = src_table->_; - if (!dst_meta && !src_meta) { - return; - } - - int32_t i_old = 0, src_column_count = src_meta ? src_meta->sw_count : 0; - int32_t i_new = 0, dst_column_count = dst_meta ? dst_meta->sw_count : 0; - if (!src_column_count && !dst_column_count) { - return; - } - - ecs_switch_t *src_columns = src_meta ? src_meta->sw_columns : NULL; - ecs_switch_t *dst_columns = dst_meta ? dst_meta->sw_columns : NULL; - - ecs_type_t dst_type = dst_table->type; - ecs_type_t src_type = src_table->type; - - int32_t offset_new = dst_meta ? dst_meta->sw_offset : 0; - int32_t offset_old = src_meta ? src_meta->sw_offset : 0; - - ecs_id_t *dst_ids = dst_type.array; - ecs_id_t *src_ids = src_type.array; - - for (; (i_new < dst_column_count) && (i_old < src_column_count);) { - ecs_entity_t dst_id = dst_ids[i_new + offset_new]; - ecs_entity_t src_id = src_ids[i_old + offset_old]; - - if (dst_id == src_id) { - ecs_switch_t *src_switch = &src_columns[i_old]; - ecs_switch_t *dst_switch = &dst_columns[i_new]; - - flecs_switch_ensure(dst_switch, dst_index + count); - - int i; - for (i = 0; i < count; i ++) { - uint64_t value = flecs_switch_get(src_switch, src_index + i); - flecs_switch_set(dst_switch, dst_index + i, value); - } - - if (clear) { - ecs_assert(count == flecs_switch_count(src_switch), - ECS_INTERNAL_ERROR, NULL); - flecs_switch_clear(src_switch); - } - } else if (dst_id > src_id) { - ecs_switch_t *src_switch = &src_columns[i_old]; - flecs_switch_clear(src_switch); - } - - i_new += dst_id <= src_id; - i_old += dst_id >= src_id; - } - - /* Clear remaining columns */ - if (clear) { - for (; (i_old < src_column_count); i_old ++) { - ecs_switch_t *src_switch = &src_columns[i_old]; - ecs_assert(count == flecs_switch_count(src_switch), - ECS_INTERNAL_ERROR, NULL); - flecs_switch_clear(src_switch); - } - } -} - /* Table move logic for bitset (toggle component) column */ static void flecs_table_move_bitset_columns( @@ -1298,19 +1256,19 @@ void flecs_table_move_bitset_columns( static void flecs_table_grow_column( ecs_world_t *world, - ecs_column_t *column, + ecs_vec_t *column, + const ecs_type_info_t *ti, int32_t to_add, int32_t dst_size, bool construct) { ecs_assert(column != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_type_info_t *ti = column->ti; - int32_t size = column->size; - int32_t count = column->data.count; - int32_t src_size = column->data.size; + int32_t count = ecs_vec_count(column); + int32_t size = ecs_vec_size(column); + int32_t elem_size = ti->size; int32_t dst_count = count + to_add; - bool can_realloc = dst_size != src_size; + bool can_realloc = dst_size != size; void *result = NULL; ecs_assert(dst_size >= dst_count, ECS_INTERNAL_ERROR, NULL); @@ -1325,10 +1283,10 @@ void flecs_table_grow_column( /* Create vector */ ecs_vec_t dst; - ecs_vec_init(&world->allocator, &dst, size, dst_size); + ecs_vec_init(&world->allocator, &dst, elem_size, dst_size); dst.count = dst_count; - void *src_buffer = column->data.array; + void *src_buffer = column->array; void *dst_buffer = dst.array; /* Move (and construct) existing elements to new vector */ @@ -1336,21 +1294,21 @@ void flecs_table_grow_column( if (construct) { /* Construct new element(s) */ - result = ECS_ELEM(dst_buffer, size, count); + result = ECS_ELEM(dst_buffer, elem_size, count); ctor(result, to_add, ti); } /* Free old vector */ - ecs_vec_fini(&world->allocator, &column->data, size); + ecs_vec_fini(&world->allocator, column, elem_size); - column->data = dst; + *column = dst; } else { /* If array won't realloc or has no move, simply add new elements */ if (can_realloc) { - ecs_vec_set_size(&world->allocator, &column->data, size, dst_size); + ecs_vec_set_size(&world->allocator, column, elem_size, dst_size); } - result = ecs_vec_grow(&world->allocator, &column->data, size, to_add); + result = ecs_vec_grow(&world->allocator, column, elem_size, to_add); ecs_xtor_t ctor; if (construct && (ctor = ti->hooks.ctor)) { @@ -1360,7 +1318,7 @@ void flecs_table_grow_column( } } - ecs_assert(column->data.size == dst_size, ECS_INTERNAL_ERROR, NULL); + ecs_assert(column->size == dst_size, ECS_INTERNAL_ERROR, NULL); } /* Grow all data structures in a table */ @@ -1368,25 +1326,31 @@ static int32_t flecs_table_grow_data( ecs_world_t *world, ecs_table_t *table, - ecs_data_t *data, int32_t to_add, int32_t size, const ecs_entity_t *ids) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(data != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t cur_count = flecs_table_data_count(data); + int32_t count = ecs_table_count(table); int32_t column_count = table->column_count; /* Add entity to column with entity ids */ - ecs_vec_set_size_t(&world->allocator, &data->entities, ecs_entity_t, size); - ecs_entity_t *e = ecs_vec_last_t(&data->entities, ecs_entity_t) + 1; - data->entities.count += to_add; - if (data->entities.size > size) { - size = data->entities.size; + ecs_vec_t v_entities = ecs_vec_from_entities(table); + ecs_vec_set_size_t(&world->allocator, &v_entities, ecs_entity_t, size); + + ecs_entity_t *e = ecs_vec_last_t(&v_entities, ecs_entity_t) + 1; + v_entities.count += to_add; + if (v_entities.size > size) { + size = v_entities.size; } + /* Update table entities/count/size */ + int32_t prev_count = table->data.count, prev_size = table->data.size; + table->data.entities = v_entities.array; + table->data.count = v_entities.count; + table->data.size = v_entities.size; + /* Initialize entity ids and record ptrs */ int32_t i; if (ids) { @@ -1396,26 +1360,24 @@ int32_t flecs_table_grow_data( } /* Add elements to each column array */ - ecs_column_t *columns = data->columns; + ecs_column_t *columns = table->data.columns; for (i = 0; i < column_count; i ++) { - flecs_table_grow_column(world, &columns[i], to_add, size, true); - ecs_assert(columns[i].data.size == size, ECS_INTERNAL_ERROR, NULL); - flecs_table_invoke_add_hooks(world, table, &columns[i], e, - cur_count, to_add, false); + ecs_column_t *column = &columns[i]; + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v_column = ecs_vec_from_column_ext(column, prev_count, prev_size, ti->size); + flecs_table_grow_column(world, &v_column, ti, to_add, size, true); + ecs_assert(v_column.size == size, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v_column.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v_column.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); + column->data = v_column.array; + flecs_table_invoke_add_hooks(world, table, column, e, + count, to_add, false); } ecs_table__t *meta = table->_; - int32_t sw_count = meta->sw_count; int32_t bs_count = meta->bs_count; - ecs_switch_t *sw_columns = meta->sw_columns; ecs_bitset_t *bs_columns = meta->bs_columns; - /* Add elements to each switch column */ - for (i = 0; i < sw_count; i ++) { - ecs_switch_t *sw = &sw_columns[i]; - flecs_switch_addn(sw, to_add); - } - /* Add elements to each bitset column */ for (i = 0; i < bs_count; i ++) { ecs_bitset_t *bs = &bs_columns[i]; @@ -1425,26 +1387,29 @@ int32_t flecs_table_grow_data( /* If the table is monitored indicate that there has been a change */ flecs_table_mark_table_dirty(world, table, 0); - if (!(world->flags & EcsWorldReadonly) && !cur_count) { + if (!(world->flags & EcsWorldReadonly) && !count) { flecs_table_set_empty(world, table); } /* Return index of first added entity */ - return cur_count; + return count; } /* Append operation for tables that don't have any complex logic */ static void flecs_table_fast_append( ecs_world_t *world, - ecs_column_t *columns, - int32_t count) + ecs_table_t *table) { /* Add elements to each column array */ - int32_t i; + ecs_column_t *columns = table->data.columns; + int32_t i, count = table->column_count; for (i = 0; i < count; i ++) { ecs_column_t *column = &columns[i]; - ecs_vec_append(&world->allocator, &column->data, column->size); + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v = ecs_vec_from_column(column, table, ti->size); + ecs_vec_append(&world->allocator, &v, ti->size); + column->data = v.array; } } @@ -1457,23 +1422,23 @@ int32_t flecs_table_append( bool on_add) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!(table->flags & EcsTableHasTarget), - ECS_INVALID_OPERATION, NULL); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); - flecs_table_check_sanity(table); + flecs_table_check_sanity(world, table); /* Get count & size before growing entities array. This tells us whether the * arrays will realloc */ - ecs_data_t *data = &table->data; - int32_t count = data->entities.count; + int32_t count = ecs_table_count(table); int32_t column_count = table->column_count; ecs_column_t *columns = table->data.columns; /* Grow buffer with entity ids, set new element to new entity */ + ecs_vec_t v_entities = ecs_vec_from_entities(table); ecs_entity_t *e = ecs_vec_append_t(&world->allocator, - &data->entities, ecs_entity_t); + &v_entities, ecs_entity_t); + ecs_assert(e != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t *entities = table->data.entities = v_entities.array; *e = entity; /* If the table is monitored indicate that there has been a change */ @@ -1482,25 +1447,42 @@ int32_t flecs_table_append( /* Fast path: no switch columns, no lifecycle actions */ if (!(table->flags & EcsTableIsComplex)) { - flecs_table_fast_append(world, columns, column_count); + flecs_table_fast_append(world, table); + table->data.count = v_entities.count; + table->data.size = v_entities.size; if (!count) { flecs_table_set_empty(world, table); /* See below */ } return count; } - ecs_entity_t *entities = data->entities.array; + int32_t prev_count = table->data.count; + int32_t prev_size = table->data.size; + + ecs_assert(table->data.count == v_entities.count - 1, + ECS_INTERNAL_ERROR, NULL); + table->data.count = v_entities.count; + table->data.size = v_entities.size; + + /* If this is the first entity in this table, signal queries so that the + * table moves from an inactive table to an active table. */ + if (!count) { + flecs_table_set_empty(world, table); + } /* Reobtain size to ensure that the columns have the same size as the * entities and record vectors. This keeps reasoning about when allocations * occur easier. */ - int32_t size = data->entities.size; + int32_t size = v_entities.size; /* Grow component arrays with 1 element */ int32_t i; for (i = 0; i < column_count; i ++) { ecs_column_t *column = &columns[i]; - flecs_table_grow_column(world, column, 1, size, construct); + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v_column = ecs_vec_from_column_ext(column, prev_count, prev_size, ti->size); + flecs_table_grow_column(world, &v_column, ti, 1, size, construct); + column->data = v_column.array; ecs_iter_action_t on_add_hook; if (on_add && (on_add_hook = column->ti->hooks.on_add)) { @@ -1508,25 +1490,15 @@ int32_t flecs_table_append( &entities[count], count, 1); } - ecs_assert(columns[i].data.size == - data->entities.size, ECS_INTERNAL_ERROR, NULL); - ecs_assert(columns[i].data.count == - data->entities.count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v_column.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); + ecs_assert(v_column.count == v_entities.count, + ECS_INTERNAL_ERROR, NULL); } ecs_table__t *meta = table->_; - int32_t sw_count = meta->sw_count; int32_t bs_count = meta->bs_count; - ecs_switch_t *sw_columns = meta->sw_columns; ecs_bitset_t *bs_columns = meta->bs_columns; - /* Add element to each switch column */ - for (i = 0; i < sw_count; i ++) { - ecs_assert(sw_columns != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_switch_t *sw = &sw_columns[i]; - flecs_switch_add(sw); - } - /* Add element to each bitset column */ for (i = 0; i < bs_count; i ++) { ecs_assert(bs_columns != NULL, ECS_INTERNAL_ERROR, NULL); @@ -1534,40 +1506,25 @@ int32_t flecs_table_append( flecs_bitset_addn(bs, 1); } - /* If this is the first entity in this table, signal queries so that the - * table moves from an inactive table to an active table. */ - if (!count) { - flecs_table_set_empty(world, table); - } - - flecs_table_check_sanity(table); + flecs_table_check_sanity(world, table); return count; } -/* Delete last operation for tables that don't have any complex logic */ -static -void flecs_table_fast_delete_last( - ecs_column_t *columns, - int32_t column_count) -{ - int i; - for (i = 0; i < column_count; i ++) { - ecs_vec_remove_last(&columns[i].data); - } -} - /* Delete operation for tables that don't have any complex logic */ static void flecs_table_fast_delete( - ecs_column_t *columns, - int32_t column_count, - int32_t index) + ecs_table_t *table, + int32_t row) { - int i; - for (i = 0; i < column_count; i ++) { + ecs_column_t *columns = table->data.columns; + int32_t i, count = table->column_count; + for (i = 0; i < count; i ++) { ecs_column_t *column = &columns[i]; - ecs_vec_remove(&column->data, column->size, index); + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v = ecs_vec_from_column(column, table, ti->size); + ecs_vec_remove(&v, ti->size, row); + column->data = v.array; } } @@ -1575,37 +1532,32 @@ void flecs_table_fast_delete( void flecs_table_delete( ecs_world_t *world, ecs_table_t *table, - int32_t index, + int32_t row, bool destruct) { ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!(table->flags & EcsTableHasTarget), - ECS_INVALID_OPERATION, NULL); - - flecs_table_check_sanity(table); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); - ecs_data_t *data = &table->data; - int32_t count = data->entities.count; + flecs_table_check_sanity(world, table); + int32_t count = ecs_table_count(table); ecs_assert(count > 0, ECS_INTERNAL_ERROR, NULL); count --; - ecs_assert(index <= count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(row <= count, ECS_INTERNAL_ERROR, NULL); - /* Move last entity id to index */ - ecs_entity_t *entities = data->entities.array; + /* Move last entity id to row */ + ecs_entity_t *entities = table->data.entities; ecs_entity_t entity_to_move = entities[count]; - ecs_entity_t entity_to_delete = entities[index]; - entities[index] = entity_to_move; - ecs_vec_remove_last(&data->entities); + ecs_entity_t entity_to_delete = entities[row]; + entities[row] = entity_to_move; /* Update record of moved entity in entity index */ - if (index != count) { + if (row != count) { ecs_record_t *record_to_move = flecs_entities_get(world, entity_to_move); if (record_to_move) { uint32_t row_flags = record_to_move->row & ECS_ROW_FLAGS_MASK; - record_to_move->row = ECS_ROW_TO_RECORD(index, row_flags); + record_to_move->row = ECS_ROW_TO_RECORD(row, row_flags); ecs_assert(record_to_move->table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(record_to_move->table == table, ECS_INTERNAL_ERROR, NULL); } @@ -1614,41 +1566,37 @@ void flecs_table_delete( /* If the table is monitored indicate that there has been a change */ flecs_table_mark_table_dirty(world, table, 0); - /* If table is empty, deactivate it */ - if (!count) { - flecs_table_set_empty(world, table); - } - /* Destruct component data */ - ecs_column_t *columns = data->columns; + ecs_column_t *columns = table->data.columns; int32_t column_count = table->column_count; int32_t i; /* If this is a table without lifecycle callbacks or special columns, take * fast path that just remove an element from the array(s) */ if (!(table->flags & EcsTableIsComplex)) { - if (index == count) { - flecs_table_fast_delete_last(columns, column_count); - } else { - flecs_table_fast_delete(columns, column_count, index); + if (row != count) { + flecs_table_fast_delete(table, row); } - flecs_table_check_sanity(table); + table->data.count --; + if (!count) { + flecs_table_set_empty(world, table); + } + + flecs_table_check_sanity(world, table); return; } /* Last element, destruct & remove */ - if (index == count) { + if (row == count) { /* If table has component destructors, invoke */ if (destruct && (table->flags & EcsTableHasDtors)) { for (i = 0; i < column_count; i ++) { flecs_table_invoke_remove_hooks(world, table, &columns[i], - &entity_to_delete, index, 1, true); + &entity_to_delete, row, 1, true); } } - flecs_table_fast_delete_last(columns, column_count); - /* Not last element, move last element to deleted element & destruct */ } else { /* If table has component destructors, invoke */ @@ -1656,46 +1604,52 @@ void flecs_table_delete( for (i = 0; i < column_count; i ++) { ecs_column_t *column = &columns[i]; ecs_type_info_t *ti = column->ti; - ecs_size_t size = column->size; - void *dst = ecs_vec_get(&column->data, size, index); - void *src = ecs_vec_last(&column->data, size); + ecs_size_t size = ti->size; + void *dst = ECS_ELEM(column->data, size, row); + void *src = ECS_ELEM(column->data, size, count); ecs_iter_action_t on_remove = ti->hooks.on_remove; if (destruct && on_remove) { - flecs_table_invoke_hook(world, table, on_remove, EcsOnRemove, - column, &entity_to_delete, index, 1); + flecs_table_invoke_hook(world, table, on_remove, + EcsOnRemove, column, &entity_to_delete, row, 1); } ecs_move_t move_dtor = ti->hooks.move_dtor; + + /* If neither move nor move_ctor are set, this indicates that + * non-destructive move semantics are not supported for this + * type. In such cases, we set the move_dtor as ctor_move_dtor, + * which indicates a destructive move operation. This adjustment + * ensures compatibility with different language bindings. */ + if (!ti->hooks.move_ctor && ti->hooks.ctor_move_dtor) { + move_dtor = ti->hooks.ctor_move_dtor; + } + if (move_dtor) { move_dtor(dst, src, 1, ti); } else { ecs_os_memcpy(dst, src, size); } - - ecs_vec_remove_last(&column->data); } } else { - flecs_table_fast_delete(columns, column_count, index); + flecs_table_fast_delete(table, row); } } - /* Remove elements from switch columns */ - ecs_table__t *meta = table->_; - ecs_switch_t *sw_columns = meta->sw_columns; - int32_t sw_count = meta->sw_count; - for (i = 0; i < sw_count; i ++) { - flecs_switch_remove(&sw_columns[i], index); - } - /* Remove elements from bitset columns */ + ecs_table__t *meta = table->_; ecs_bitset_t *bs_columns = meta->bs_columns; int32_t bs_count = meta->bs_count; for (i = 0; i < bs_count; i ++) { - flecs_bitset_remove(&bs_columns[i], index); + flecs_bitset_remove(&bs_columns[i], row); + } + + table->data.count --; + if (!count) { + flecs_table_set_empty(world, table); } - flecs_table_check_sanity(table); + flecs_table_check_sanity(world, table); } /* Move operation for tables that don't have any complex logic */ @@ -1715,13 +1669,13 @@ void flecs_table_fast_move( for (; (i_new < dst_column_count) && (i_old < src_column_count);) { ecs_column_t *dst_column = &dst_columns[i_new]; ecs_column_t *src_column = &src_columns[i_old]; - ecs_id_t dst_id = dst_column->id; - ecs_id_t src_id = src_column->id; + ecs_id_t dst_id = flecs_column_id(dst_table, i_new); + ecs_id_t src_id = flecs_column_id(src_table, i_old); if (dst_id == src_id) { - int32_t size = dst_column->size; - void *dst = ecs_vec_get(&dst_column->data, size, dst_index); - void *src = ecs_vec_get(&src_column->data, size, src_index); + int32_t size = dst_column->ti->size; + void *dst = ECS_ELEM(dst_column->data, size, dst_index); + void *src = ECS_ELEM(src_column->data, size, src_index); ecs_os_memcpy(dst, src, size); } @@ -1743,24 +1697,24 @@ void flecs_table_move( { ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!dst_table->_->lock, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!src_table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_assert(!dst_table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + ecs_assert(!src_table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); ecs_assert(src_index >= 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(dst_index >= 0, ECS_INTERNAL_ERROR, NULL); - flecs_table_check_sanity(dst_table); - flecs_table_check_sanity(src_table); + flecs_table_check_sanity(world, dst_table); + flecs_table_check_sanity(world, src_table); if (!((dst_table->flags | src_table->flags) & EcsTableIsComplex)) { flecs_table_fast_move(dst_table, dst_index, src_table, src_index); - flecs_table_check_sanity(dst_table); - flecs_table_check_sanity(src_table); + flecs_table_check_sanity(world, dst_table); + flecs_table_check_sanity(world, src_table); return; } - flecs_table_move_switch_columns(dst_table, dst_index, src_table, src_index, 1, false); - flecs_table_move_bitset_columns(dst_table, dst_index, src_table, src_index, 1, false); + flecs_table_move_bitset_columns( + dst_table, dst_index, src_table, src_index, 1, false); /* If the source and destination entities are the same, move component * between tables. If the entities are not the same (like when cloning) use @@ -1782,16 +1736,16 @@ void flecs_table_move( for (; (i_new < dst_column_count) && (i_old < src_column_count); ) { ecs_column_t *dst_column = &dst_columns[i_new]; ecs_column_t *src_column = &src_columns[i_old]; - ecs_id_t dst_id = dst_column->id; - ecs_id_t src_id = src_column->id; + ecs_id_t dst_id = flecs_column_id(dst_table, i_new); + ecs_id_t src_id = flecs_column_id(src_table, i_old); if (dst_id == src_id) { - int32_t size = dst_column->size; + ecs_type_info_t *ti = dst_column->ti; + int32_t size = ti->size; ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); - void *dst = ecs_vec_get(&dst_column->data, size, dst_index); - void *src = ecs_vec_get(&src_column->data, size, src_index); - ecs_type_info_t *ti = dst_column->ti; + void *dst = ECS_ELEM(dst_column->data, size, dst_index); + void *src = ECS_ELEM(src_column->data, size, src_index); if (same_entity) { ecs_move_t move = ti->hooks.move_ctor; @@ -1819,7 +1773,7 @@ void flecs_table_move( if (dst_id < src_id) { flecs_table_invoke_add_hooks(world, dst_table, dst_column, &dst_entity, dst_index, 1, construct); - } else { + } else if (same_entity) { flecs_table_invoke_remove_hooks(world, src_table, src_column, &src_entity, src_index, 1, use_move_dtor); } @@ -1834,103 +1788,66 @@ void flecs_table_move( &dst_entity, dst_index, 1, construct); } - for (; (i_old < src_column_count); i_old ++) { - flecs_table_invoke_remove_hooks(world, src_table, &src_columns[i_old], - &src_entity, src_index, 1, use_move_dtor); + if (same_entity) { + for (; (i_old < src_column_count); i_old ++) { + flecs_table_invoke_remove_hooks(world, src_table, &src_columns[i_old], + &src_entity, src_index, 1, use_move_dtor); + } } - flecs_table_check_sanity(dst_table); - flecs_table_check_sanity(src_table); + flecs_table_check_sanity(world, dst_table); + flecs_table_check_sanity(world, src_table); } /* Append n entities to table */ int32_t flecs_table_appendn( ecs_world_t *world, ecs_table_t *table, - ecs_data_t *data, int32_t to_add, const ecs_entity_t *ids) { - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); - flecs_table_check_sanity(table); - int32_t cur_count = flecs_table_data_count(data); + flecs_table_check_sanity(world, table); + int32_t cur_count = ecs_table_count(table); int32_t result = flecs_table_grow_data( - world, table, data, to_add, cur_count + to_add, ids); - flecs_table_check_sanity(table); + world, table, to_add, cur_count + to_add, ids); + flecs_table_check_sanity(world, table); return result; } -/* Set allocated table size */ -void flecs_table_set_size( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data, - int32_t size) -{ - ecs_assert(table != NULL, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - - flecs_table_check_sanity(table); - - int32_t cur_count = flecs_table_data_count(data); - - if (cur_count < size) { - flecs_table_grow_data(world, table, data, 0, size, NULL); - flecs_table_check_sanity(table); - } -} - /* Shrink table storage to fit number of entities */ bool flecs_table_shrink( ecs_world_t *world, ecs_table_t *table) { - ecs_assert(table != NULL, ECS_LOCKED_STORAGE, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_assert(table != NULL, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); (void)world; - flecs_table_check_sanity(table); + flecs_table_check_sanity(world, table); - ecs_data_t *data = &table->data; - bool has_payload = data->entities.array != NULL; - ecs_vec_reclaim_t(&world->allocator, &data->entities, ecs_entity_t); + bool has_payload = table->data.entities != NULL; + ecs_vec_t v_entities = ecs_vec_from_entities(table); + ecs_vec_reclaim_t(&world->allocator, &v_entities, ecs_entity_t); int32_t i, count = table->column_count; for (i = 0; i < count; i ++) { - ecs_column_t *column = &data->columns[i]; - ecs_vec_reclaim(&world->allocator, &column->data, column->size); + ecs_column_t *column = &table->data.columns[i]; + const ecs_type_info_t *ti = column->ti; + ecs_vec_t v_column = ecs_vec_from_column(column, table, ti->size); + ecs_vec_reclaim(&world->allocator, &v_column, ti->size); + column->data = v_column.array; } - return has_payload; -} - -/* Return number of entities in table */ -int32_t flecs_table_data_count( - const ecs_data_t *data) -{ - return data ? data->entities.count : 0; -} - -/* Swap operation for switch (union relationship) columns */ -static -void flecs_table_swap_switch_columns( - ecs_table_t *table, - int32_t row_1, - int32_t row_2) -{ - int32_t i = 0, column_count = table->_->sw_count; - if (!column_count) { - return; - } + table->data.count = v_entities.count; + table->data.size = v_entities.size; + table->data.entities = v_entities.array; - ecs_switch_t *columns = table->_->sw_columns; + flecs_table_check_sanity(world, table); - for (i = 0; i < column_count; i ++) { - ecs_switch_t *sw = &columns[i]; - flecs_switch_swap(sw, row_1, row_2); - } + return has_payload; } /* Swap operation for bitset (toggle component) columns */ @@ -1962,11 +1879,11 @@ void flecs_table_swap( { (void)world; - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); + ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); ecs_assert(row_1 >= 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(row_2 >= 0, ECS_INTERNAL_ERROR, NULL); - flecs_table_check_sanity(table); + flecs_table_check_sanity(world, table); if (row_1 == row_2) { return; @@ -1975,7 +1892,7 @@ void flecs_table_swap( /* If the table is monitored indicate that there has been a change */ flecs_table_mark_table_dirty(world, table, 0); - ecs_entity_t *entities = table->data.entities.array; + ecs_entity_t *entities = table->data.entities; ecs_entity_t e1 = entities[row_1]; ecs_entity_t e2 = entities[row_2]; @@ -1995,12 +1912,11 @@ void flecs_table_swap( record_ptr_1->row = ECS_ROW_TO_RECORD(row_2, flags_1); record_ptr_2->row = ECS_ROW_TO_RECORD(row_1, flags_2); - flecs_table_swap_switch_columns(table, row_1, row_2); flecs_table_swap_bitset_columns(table, row_1, row_2); ecs_column_t *columns = table->data.columns; if (!columns) { - flecs_table_check_sanity(table); + flecs_table_check_sanity(world, table); return; } @@ -2008,25 +1924,39 @@ void flecs_table_swap( * and allocate a temporary buffer for swapping */ int32_t i, temp_buffer_size = ECS_SIZEOF(uint64_t), column_count = table->column_count; for (i = 0; i < column_count; i++) { - temp_buffer_size = ECS_MAX(temp_buffer_size, columns[i].size); + temp_buffer_size = ECS_MAX(temp_buffer_size, columns[i].ti->size); } void* tmp = ecs_os_alloca(temp_buffer_size); /* Swap columns */ for (i = 0; i < column_count; i ++) { - int32_t size = columns[i].size; - void *ptr = columns[i].data.array; + int32_t size = columns[i].ti->size; + ecs_column_t *column = &columns[i]; + void *ptr = column->data; + const ecs_type_info_t *ti = column->ti; + ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); void *el_1 = ECS_ELEM(ptr, size, row_1); void *el_2 = ECS_ELEM(ptr, size, row_2); - ecs_os_memcpy(tmp, el_1, size); - ecs_os_memcpy(el_1, el_2, size); - ecs_os_memcpy(el_2, tmp, size); + ecs_move_t move = ti->hooks.move; + if (!move) { + ecs_os_memcpy(tmp, el_1, size); + ecs_os_memcpy(el_1, el_2, size); + ecs_os_memcpy(el_2, tmp, size); + } else { + ecs_move_t move_ctor = ti->hooks.move_ctor; + ecs_move_t move_dtor = ti->hooks.move_dtor; + ecs_assert(move_ctor != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(move_dtor != NULL, ECS_INTERNAL_ERROR, NULL); + move_ctor(tmp, el_1, 1, ti); + move(el_1, el_2, 1, ti); + move_dtor(el_2, tmp, 1, ti); + } } - flecs_table_check_sanity(table); + flecs_table_check_sanity(world, table); } static @@ -2067,41 +1997,44 @@ void flecs_table_merge_vec( static void flecs_table_merge_column( ecs_world_t *world, + ecs_vec_t *dst_vec, + ecs_vec_t *src_vec, ecs_column_t *dst, ecs_column_t *src, int32_t column_size) { - ecs_size_t size = dst->size; - int32_t dst_count = dst->data.count; + const ecs_type_info_t *ti = dst->ti; + ecs_assert(ti == src->ti, ECS_INTERNAL_ERROR, NULL); + ecs_size_t elem_size = ti->size; + int32_t dst_count = ecs_vec_count(dst_vec); if (!dst_count) { - ecs_vec_fini(&world->allocator, &dst->data, size); - *dst = *src; - src->data.array = NULL; - src->data.count = 0; - src->data.size = 0; + ecs_vec_fini(&world->allocator, dst_vec, elem_size); + *dst_vec = *src_vec; /* If the new table is not empty, copy the contents from the * src into the dst. */ } else { - int32_t src_count = src->data.count; + int32_t src_count = src_vec->count; - flecs_table_grow_column(world, dst, src_count, column_size, true); - void *dst_ptr = ECS_ELEM(dst->data.array, size, dst_count); - void *src_ptr = src->data.array; + flecs_table_grow_column(world, dst_vec, ti, src_count, column_size, false); + void *dst_ptr = ECS_ELEM(dst_vec->array, elem_size, dst_count); + void *src_ptr = src_vec->array; /* Move values into column */ - ecs_type_info_t *ti = dst->ti; ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_move_t move = ti->hooks.move_dtor; + ecs_move_t move = ti->hooks.ctor_move_dtor; if (move) { move(dst_ptr, src_ptr, src_count, ti); } else { - ecs_os_memcpy(dst_ptr, src_ptr, size * src_count); + ecs_os_memcpy(dst_ptr, src_ptr, elem_size * src_count); } - ecs_vec_fini(&world->allocator, &src->data, size); + ecs_vec_fini(&world->allocator, src_vec, elem_size); } + + dst->data = dst_vec->array; + src->data = NULL; } /* Merge storage of two tables. */ @@ -2110,15 +2043,13 @@ void flecs_table_merge_data( ecs_world_t *world, ecs_table_t *dst_table, ecs_table_t *src_table, - int32_t src_count, int32_t dst_count, - ecs_data_t *src_data, - ecs_data_t *dst_data) + int32_t src_count) { int32_t i_new = 0, dst_column_count = dst_table->column_count; int32_t i_old = 0, src_column_count = src_table->column_count; - ecs_column_t *src_columns = src_data->columns; - ecs_column_t *dst_columns = dst_data->columns; + ecs_column_t *src_columns = src_table->data.columns; + ecs_column_t *dst_columns = dst_table->data.columns; ecs_assert(!dst_column_count || dst_columns, ECS_INTERNAL_ERROR, NULL); @@ -2127,61 +2058,84 @@ void flecs_table_merge_data( } /* Merge entities */ - flecs_table_merge_vec(world, &dst_data->entities, &src_data->entities, + ecs_vec_t dst_entities = ecs_vec_from_entities(dst_table); + ecs_vec_t src_entities = ecs_vec_from_entities(src_table); + flecs_table_merge_vec(world, &dst_entities, &src_entities, ECS_SIZEOF(ecs_entity_t), 0); - ecs_assert(dst_data->entities.count == src_count + dst_count, + ecs_assert(dst_entities.count == src_count + dst_count, ECS_INTERNAL_ERROR, NULL); - int32_t column_size = dst_data->entities.size; + int32_t column_size = dst_entities.size; ecs_allocator_t *a = &world->allocator; for (; (i_new < dst_column_count) && (i_old < src_column_count); ) { ecs_column_t *dst_column = &dst_columns[i_new]; ecs_column_t *src_column = &src_columns[i_old]; - ecs_id_t dst_id = dst_column->id; - ecs_id_t src_id = src_column->id; + ecs_id_t dst_id = flecs_column_id(dst_table, i_new); + ecs_id_t src_id = flecs_column_id(src_table, i_old); + ecs_size_t dst_elem_size = dst_column->ti->size; + ecs_size_t src_elem_size = src_column->ti->size; + ecs_vec_t dst_vec = ecs_vec_from_column( + dst_column, dst_table, dst_elem_size); + ecs_vec_t src_vec = ecs_vec_from_column( + src_column, src_table, src_elem_size); + if (dst_id == src_id) { - flecs_table_merge_column(world, dst_column, src_column, column_size); + flecs_table_merge_column(world, &dst_vec, &src_vec, dst_column, + src_column, column_size); flecs_table_mark_table_dirty(world, dst_table, i_new + 1); i_new ++; i_old ++; } else if (dst_id < src_id) { /* New column, make sure vector is large enough. */ - ecs_size_t size = dst_column->size; - ecs_vec_set_size(a, &dst_column->data, size, column_size); - ecs_vec_set_count(a, &dst_column->data, size, src_count + dst_count); + ecs_vec_set_size(a, &dst_vec, dst_elem_size, column_size); + dst_column->data = dst_vec.array; flecs_table_invoke_ctor(dst_column, dst_count, src_count); i_new ++; } else if (dst_id > src_id) { /* Old column does not occur in new table, destruct */ flecs_table_invoke_dtor(src_column, 0, src_count); - ecs_vec_fini(a, &src_column->data, src_column->size); + ecs_vec_fini(a, &src_vec, src_elem_size); + src_column->data = NULL; i_old ++; } } - flecs_table_move_switch_columns(dst_table, dst_count, src_table, 0, src_count, true); - flecs_table_move_bitset_columns(dst_table, dst_count, src_table, 0, src_count, true); + flecs_table_move_bitset_columns( + dst_table, dst_count, src_table, 0, src_count, true); /* Initialize remaining columns */ for (; i_new < dst_column_count; i_new ++) { ecs_column_t *column = &dst_columns[i_new]; - int32_t size = column->size; - ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); - ecs_vec_set_size(a, &column->data, size, column_size); - ecs_vec_set_count(a, &column->data, size, src_count + dst_count); + int32_t elem_size = column->ti->size; + ecs_assert(elem_size != 0, ECS_INTERNAL_ERROR, NULL); + ecs_vec_t vec = ecs_vec_from_column(column, dst_table, elem_size); + ecs_vec_set_size(a, &vec, elem_size, column_size); + column->data = vec.array; flecs_table_invoke_ctor(column, dst_count, src_count); } /* Destruct remaining columns */ for (; i_old < src_column_count; i_old ++) { ecs_column_t *column = &src_columns[i_old]; + int32_t elem_size = column->ti->size; + ecs_assert(elem_size != 0, ECS_INTERNAL_ERROR, NULL); flecs_table_invoke_dtor(column, 0, src_count); - ecs_vec_fini(a, &column->data, column->size); + ecs_vec_t vec = ecs_vec_from_column(column, src_table, elem_size); + ecs_vec_fini(a, &vec, elem_size); + column->data = vec.array; } /* Mark entity column as dirty */ - flecs_table_mark_table_dirty(world, dst_table, 0); + flecs_table_mark_table_dirty(world, dst_table, 0); + + dst_table->data.entities = dst_entities.array; + dst_table->data.count = dst_entities.count; + dst_table->data.size = dst_entities.size; + + src_table->data.entities = src_entities.array; + src_table->data.count = src_entities.count; + src_table->data.size = src_entities.size; } /* Merge source table into destination table. This typically happens as result @@ -2190,42 +2144,19 @@ void flecs_table_merge_data( void flecs_table_merge( ecs_world_t *world, ecs_table_t *dst_table, - ecs_table_t *src_table, - ecs_data_t *dst_data, - ecs_data_t *src_data) + ecs_table_t *src_table) { ecs_assert(src_table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!src_table->_->lock, ECS_LOCKED_STORAGE, NULL); - - flecs_table_check_sanity(src_table); - flecs_table_check_sanity(dst_table); - - bool move_data = false; - - /* If there is nothing to merge to, just clear the old table */ - if (!dst_table) { - flecs_table_clear_data(world, src_table, src_data); - flecs_table_check_sanity(src_table); - return; - } else { - ecs_assert(!dst_table->_->lock, ECS_LOCKED_STORAGE, NULL); - } - - /* If there is no data to merge, drop out */ - if (!src_data) { - return; - } + ecs_assert(dst_table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!src_table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); + ecs_assert(!dst_table->_->lock, ECS_LOCKED_STORAGE, FLECS_LOCKED_STORAGE_MSG); - if (!dst_data) { - dst_data = &dst_table->data; - if (dst_table == src_table) { - move_data = true; - } - } + flecs_table_check_sanity(world, src_table); + flecs_table_check_sanity(world, dst_table); - ecs_entity_t *src_entities = src_data->entities.array; - int32_t src_count = src_data->entities.count; - int32_t dst_count = dst_data->entities.count; + const ecs_entity_t *src_entities = ecs_table_entities(src_table); + int32_t src_count = ecs_table_count(src_table); + int32_t dst_count = ecs_table_count(dst_table); /* First, update entity index so old entities point to new type */ int32_t i; @@ -2237,12 +2168,7 @@ void flecs_table_merge( } /* Merge table columns */ - if (move_data) { - *dst_data = *src_data; - } else { - flecs_table_merge_data(world, dst_table, src_table, src_count, dst_count, - src_data, dst_data); - } + flecs_table_merge_data(world, dst_table, src_table, dst_count, src_count); if (src_count) { if (!dst_count) { @@ -2255,48 +2181,15 @@ void flecs_table_merge( ecs_assert(src_table->_->traversable_count == 0, ECS_INTERNAL_ERROR, NULL); } - flecs_table_check_sanity(src_table); - flecs_table_check_sanity(dst_table); -} - -/* Replace data with other data. Used by snapshots to restore previous state. */ -void flecs_table_replace_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data) -{ - int32_t prev_count = 0; - ecs_data_t *table_data = &table->data; - ecs_assert(!data || data != table_data, ECS_INTERNAL_ERROR, NULL); - ecs_assert(!table->_->lock, ECS_LOCKED_STORAGE, NULL); - - flecs_table_check_sanity(table); - - prev_count = table_data->entities.count; - flecs_table_notify_on_remove(world, table, table_data); - flecs_table_clear_data(world, table, table_data); - - if (data) { - table->data = *data; - } else { - flecs_table_init_data(world, table); - } - - int32_t count = ecs_table_count(table); - - if (!prev_count && count) { - flecs_table_set_empty(world, table); - } else if (prev_count && !count) { - flecs_table_set_empty(world, table); - } - - flecs_table_check_sanity(table); + flecs_table_check_sanity(world, src_table); + flecs_table_check_sanity(world, dst_table); } /* Internal mechanism for propagating information to tables */ void flecs_table_notify( ecs_world_t *world, ecs_table_t *table, + ecs_id_t id, ecs_table_event_t *event) { if (world->flags & EcsWorldFini) { @@ -2305,11 +2198,50 @@ void flecs_table_notify( switch(event->kind) { case EcsTableTriggersForId: - flecs_table_add_trigger_flags(world, table, event->event); + flecs_table_add_trigger_flags(world, table, id, event->event); break; case EcsTableNoTriggersForId: - break; + break; /* TODO */ + } +} + +int32_t flecs_table_get_toggle_column( + ecs_table_t *table, + ecs_id_t id) +{ + ecs_id_t *ids = table->type.array; + int32_t i = table->_->bs_offset, end = i + table->_->bs_count; + + for (; i < end; i ++) { + if (ids[i] == (ECS_TOGGLE | id)) { + return i; + } + } + + return -1; +} + +ecs_bitset_t* flecs_table_get_toggle( + ecs_table_t *table, + ecs_id_t id) +{ + int32_t toggle_column = flecs_table_get_toggle_column(table, id); + if (toggle_column == -1) { + return NULL; } + + toggle_column -= table->_->bs_offset; + ecs_assert(toggle_column >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(toggle_column < table->_->bs_count, ECS_INTERNAL_ERROR, NULL); + return &table->_->bs_columns[toggle_column]; +} + +ecs_id_t flecs_column_id( + ecs_table_t *table, + int32_t column_index) +{ + int32_t type_index = table->column_map[table->type.count + column_index]; + return table->type.array[type_index]; } /* -- Public API -- */ @@ -2319,7 +2251,7 @@ void ecs_table_lock( ecs_table_t *table) { if (table) { - if (ecs_poly_is(world, ecs_world_t) && !(world->flags & EcsWorldReadonly)) { + if (flecs_poly_is(world, ecs_world_t) && !(world->flags & EcsWorldReadonly)) { table->_->lock ++; } } @@ -2330,9 +2262,10 @@ void ecs_table_unlock( ecs_table_t *table) { if (table) { - if (ecs_poly_is(world, ecs_world_t) && !(world->flags & EcsWorldReadonly)) { + if (flecs_poly_is(world, ecs_world_t) && !(world->flags & EcsWorldReadonly)) { table->_->lock --; - ecs_assert(table->_->lock >= 0, ECS_INVALID_OPERATION, NULL); + ecs_assert(table->_->lock >= 0, ECS_INVALID_OPERATION, + "table_unlock called more often than table_lock"); } } } @@ -2352,9 +2285,17 @@ int32_t ecs_table_get_type_index( const ecs_table_t *table, ecs_id_t id) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + + if (id < FLECS_HI_COMPONENT_ID) { + int16_t res = table->component_map[id]; + if (res > 0) { + return table->column_map[table->type.count + (res - 1)]; + } + + return -res - 1; + } ecs_id_record_t *idr = flecs_id_record_get(world, id); if (!idr) { @@ -2376,10 +2317,18 @@ int32_t ecs_table_get_column_index( const ecs_table_t *table, ecs_id_t id) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(ecs_id_is_valid(world, id), ECS_INVALID_PARAMETER, NULL); + if (id < FLECS_HI_COMPONENT_ID) { + int16_t res = table->component_map[id]; + if (res > 0) { + return res - 1; + } + return -1; + } + ecs_id_record_t *idr = flecs_id_record_get(world, id); if (!idr) { return -1; @@ -2407,7 +2356,7 @@ int32_t ecs_table_type_to_column_index( { ecs_assert(index >= 0, ECS_INVALID_PARAMETER, NULL); ecs_check(index < table->type.count, ECS_INVALID_PARAMETER, NULL); - int32_t *column_map = table->column_map; + int16_t *column_map = table->column_map; if (column_map) { return column_map[index]; } @@ -2436,9 +2385,9 @@ void* ecs_table_get_column( ecs_check(index < table->column_count, ECS_INVALID_PARAMETER, NULL); ecs_column_t *column = &table->data.columns[index]; - void *result = column->data.array; + void *result = column->data; if (offset) { - result = ECS_ELEM(result, column->size, offset); + result = ECS_ELEM(result, column->ti->size, offset); } return result; @@ -2476,7 +2425,7 @@ size_t ecs_table_get_column_size( ecs_check(column < table->column_count, ECS_INVALID_PARAMETER, NULL); ecs_check(table->column_map != NULL, ECS_INVALID_PARAMETER, NULL); - return flecs_ito(size_t, table->data.columns[column].size); + return flecs_ito(size_t, table->data.columns[column].ti->size); error: return 0; } @@ -2485,7 +2434,14 @@ int32_t ecs_table_count( const ecs_table_t *table) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - return flecs_table_data_count(&table->data); + return table->data.count; +} + +int32_t ecs_table_size( + const ecs_table_t *table) +{ + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + return table->data.size; } bool ecs_table_has_id( @@ -2504,7 +2460,9 @@ int32_t ecs_table_get_depth( ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(table != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(ecs_id_is_valid(world, rel), ECS_INVALID_PARAMETER, NULL); - ecs_check(ecs_has_id(world, rel, EcsAcyclic), ECS_INVALID_PARAMETER, NULL); + ecs_check(ecs_has_id(world, rel, EcsAcyclic), ECS_INVALID_PARAMETER, + "cannot safely determine depth for relationship that is not acyclic " + "(add Acyclic property to relationship)"); world = ecs_get_world(world); @@ -2520,20 +2478,6 @@ bool ecs_table_has_flags( return (table->flags & flags) == flags; } -int32_t flecs_table_column_to_union_index( - const ecs_table_t *table, - int32_t column) -{ - int32_t sw_count = table->_->sw_count; - if (sw_count) { - int32_t sw_offset = table->_->sw_offset; - if (column >= sw_offset && column < (sw_offset + sw_count)){ - return column - sw_offset; - } - } - return -1; -} - void ecs_table_swap_rows( ecs_world_t* world, ecs_table_t* table, @@ -2549,7 +2493,7 @@ int32_t flecs_table_observed_count( return table->_->traversable_count; } -void* ecs_record_get_column( +void* ecs_record_get_by_column( const ecs_record_t *r, int32_t index, size_t c_size) @@ -2559,12 +2503,12 @@ void* ecs_record_get_column( ecs_check(index < table->column_count, ECS_INVALID_PARAMETER, NULL); ecs_column_t *column = &table->data.columns[index]; - ecs_size_t size = column->size; + ecs_size_t size = column->ti->size; ecs_check(!flecs_utosize(c_size) || flecs_utosize(c_size) == size, ECS_INVALID_PARAMETER, NULL); - return ecs_vec_get(&column->data, size, ECS_RECORD_TO_ROW(r->row)); + return ECS_ELEM(column->data, size, ECS_RECORD_TO_ROW(r->row)); error: return NULL; } diff --git a/vendors/flecs/src/storage/table.h b/vendors/flecs/src/storage/table.h index fba284c67..ec6c5a89c 100644 --- a/vendors/flecs/src/storage/table.h +++ b/vendors/flecs/src/storage/table.h @@ -1,5 +1,5 @@ /** - * @file table.h + * @file storage/table.h * @brief Table storage implementation. */ @@ -8,6 +8,50 @@ #include "table_graph.h" +#ifdef FLECS_SANITIZE +#define ecs_vec_from_column(arg_column, table, arg_elem_size) {\ + .array = (arg_column)->data,\ + .count = table->data.count,\ + .size = table->data.size,\ + .elem_size = arg_elem_size\ +} + +#define ecs_vec_from_column_ext(arg_column, arg_count, arg_size, arg_elem_size) {\ + .array = (arg_column)->data,\ + .count = arg_count,\ + .size = arg_size,\ + .elem_size = arg_elem_size\ +} + +#define ecs_vec_from_entities(table) {\ + .array = table->data.entities,\ + .count = table->data.count,\ + .size = table->data.size,\ + .elem_size = ECS_SIZEOF(ecs_entity_t)\ +} +#else +#define ecs_vec_from_column(arg_column, table, arg_elem_size) {\ + .array = (arg_column)->data,\ + .count = table->data.count,\ + .size = table->data.size,\ +} + +#define ecs_vec_from_column_ext(arg_column, arg_count, arg_size, arg_elem_size) {\ + .array = (arg_column)->data,\ + .count = arg_count,\ + .size = arg_size,\ +} + +#define ecs_vec_from_entities(table) {\ + .array = table->data.entities,\ + .count = table->data.count,\ + .size = table->data.size,\ +} +#endif + +#define ecs_vec_from_column_t(arg_column, table, T)\ + ecs_vec_from_column(arg_column, table, ECS_SIZEOF(T)) + /* Table event type for notifying tables of world events */ typedef enum ecs_table_eventkind_t { EcsTableTriggersForId, @@ -17,9 +61,6 @@ typedef enum ecs_table_eventkind_t { typedef struct ecs_table_event_t { ecs_table_eventkind_t kind; - /* Query event */ - ecs_query_t *query; - /* Component info event */ ecs_entity_t component; @@ -42,10 +83,7 @@ typedef struct ecs_table__t { struct ecs_table_record_t *records; /* Array with table records */ ecs_hashmap_t *name_index; /* Cached pointer to name index */ - ecs_switch_t *sw_columns; /* Switch columns */ ecs_bitset_t *bs_columns; /* Bitset columns */ - int16_t sw_count; - int16_t sw_offset; int16_t bs_count; int16_t bs_offset; int16_t ft_offset; @@ -53,16 +91,16 @@ typedef struct ecs_table__t { /** Table column */ typedef struct ecs_column_t { - ecs_vec_t data; /* Vector with component data */ - ecs_id_t id; /* Component id */ + void *data; /* Array with component data */ ecs_type_info_t *ti; /* Component type info */ - ecs_size_t size; /* Component size */ } ecs_column_t; /** Table data */ struct ecs_data_t { - ecs_vec_t entities; /* Entity ids */ + ecs_entity_t *entities; /* Entity ids */ ecs_column_t *columns; /* Component data */ + int32_t count; + int32_t size; }; /** A table is the Flecs equivalent of an archetype. Tables store all entities @@ -79,7 +117,8 @@ struct ecs_table_t { ecs_graph_node_t node; /* Graph node */ int32_t *dirty_state; /* Keep track of changes in columns */ - int32_t *column_map; /* Map type index <-> column + int16_t *component_map; /* Get column for component id */ + int16_t *column_map; /* Map type index <-> column * - 0..count(T): type index -> column * - count(T)..count(C): column -> type index */ @@ -128,16 +167,6 @@ void flecs_table_clear_entities_silent( ecs_world_t *world, ecs_table_t *table); -/* Clear table data. Don't call OnRemove handlers. */ -void flecs_table_clear_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data); - -/* Return number of entities in data */ -int32_t flecs_table_data_count( - const ecs_data_t *data); - /* Add a new entry to the table for the specified entity */ int32_t flecs_table_append( ecs_world_t *world, @@ -173,17 +202,9 @@ void flecs_table_move( int32_t flecs_table_appendn( ecs_world_t *world, ecs_table_t *table, - ecs_data_t *data, int32_t count, const ecs_entity_t *ids); -/* Set table to a fixed size. Useful for preallocating memory in advance. */ -void flecs_table_set_size( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data, - int32_t count); - /* Shrink table to contents */ bool flecs_table_shrink( ecs_world_t *world, @@ -204,7 +225,7 @@ void flecs_table_remove_actions( ecs_table_t *table); /* Free table */ -void flecs_table_free( +void flecs_table_fini( ecs_world_t *world, ecs_table_t *table); @@ -212,20 +233,12 @@ void flecs_table_free( void flecs_table_free_type( ecs_world_t *world, ecs_table_t *table); - -/* Replace data */ -void flecs_table_replace_data( - ecs_world_t *world, - ecs_table_t *table, - ecs_data_t *data); /* Merge data of one table into another table */ void flecs_table_merge( ecs_world_t *world, ecs_table_t *new_table, - ecs_table_t *old_table, - ecs_data_t *new_data, - ecs_data_t *old_data); + ecs_table_t *old_table); void flecs_table_swap( ecs_world_t *world, @@ -241,25 +254,33 @@ void flecs_table_mark_dirty( void flecs_table_notify( ecs_world_t *world, ecs_table_t *table, + ecs_id_t id, ecs_table_event_t *event); void flecs_table_delete_entities( ecs_world_t *world, ecs_table_t *table); -int32_t flecs_table_column_to_union_index( - const ecs_table_t *table, - int32_t column); - /* Increase observer count of table */ void flecs_table_traversable_add( ecs_table_t *table, int32_t value); -ecs_vec_t* flecs_table_entities( - ecs_table_t *table); +void flecs_table_emit( + ecs_world_t *world, + ecs_table_t *table, + ecs_entity_t event); -ecs_entity_t* flecs_table_entities_array( - ecs_table_t *table); +int32_t flecs_table_get_toggle_column( + ecs_table_t *table, + ecs_id_t id); + +ecs_bitset_t* flecs_table_get_toggle( + ecs_table_t *table, + ecs_id_t id); + +ecs_id_t flecs_column_id( + ecs_table_t *table, + int32_t column_index); #endif diff --git a/vendors/flecs/src/storage/table_cache.c b/vendors/flecs/src/storage/table_cache.c index d932fc0d0..ec4a82cbc 100644 --- a/vendors/flecs/src/storage/table_cache.c +++ b/vendors/flecs/src/storage/table_cache.c @@ -1,5 +1,5 @@ /** - * @file table_cache.c + * @file storage/table_cache.c * @brief Data structure for fast table iteration/lookups. * * A table cache is a data structure that provides constant time operations for @@ -96,23 +96,17 @@ bool ecs_table_cache_is_empty( return ecs_map_count(&cache->index) == 0; } -void ecs_table_cache_insert( +void ecs_table_cache_insert_w_empty( ecs_table_cache_t *cache, const ecs_table_t *table, - ecs_table_cache_hdr_t *result) + ecs_table_cache_hdr_t *result, + bool empty) { ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(ecs_table_cache_get(cache, table) == NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(result != NULL, ECS_INTERNAL_ERROR, NULL); - bool empty; - if (!table) { - empty = false; - } else { - empty = ecs_table_count(table) == 0; - } - result->cache = cache; result->table = ECS_CONST_CAST(ecs_table_t*, table); result->empty = empty; @@ -129,6 +123,21 @@ void ecs_table_cache_insert( ECS_INTERNAL_ERROR, NULL); } +void ecs_table_cache_insert( + ecs_table_cache_t *cache, + const ecs_table_t *table, + ecs_table_cache_hdr_t *result) +{ + bool empty; + if (!table) { + empty = false; + } else { + empty = ecs_table_count(table) == 0; + } + + ecs_table_cache_insert_w_empty(cache, table, result, empty); +} + void ecs_table_cache_replace( ecs_table_cache_t *cache, const ecs_table_t *table, diff --git a/vendors/flecs/src/storage/table_cache.h b/vendors/flecs/src/storage/table_cache.h index 18e1767e5..1020d8a6b 100644 --- a/vendors/flecs/src/storage/table_cache.h +++ b/vendors/flecs/src/storage/table_cache.h @@ -1,5 +1,5 @@ /** - * @file table_cache.h + * @file storage/table_cache.h * @brief Data structure for fast table iteration/lookups. */ @@ -18,6 +18,12 @@ void ecs_table_cache_insert( const ecs_table_t *table, ecs_table_cache_hdr_t *result); +void ecs_table_cache_insert_w_empty( + ecs_table_cache_t *cache, + const ecs_table_t *table, + ecs_table_cache_hdr_t *result, + bool is_empty); + void ecs_table_cache_replace( ecs_table_cache_t *cache, const ecs_table_t *table, @@ -42,6 +48,7 @@ bool ecs_table_cache_is_empty( #define flecs_table_cache_count(cache) (cache)->tables.count #define flecs_table_cache_empty_count(cache) (cache)->empty_tables.count +#define flecs_table_cache_all_count(cache) ((cache)->tables.count + (cache)->empty_tables.count) bool flecs_table_cache_iter( ecs_table_cache_t *cache, diff --git a/vendors/flecs/src/storage/table_graph.c b/vendors/flecs/src/storage/table_graph.c index 4d420b9d5..a55602451 100644 --- a/vendors/flecs/src/storage/table_graph.c +++ b/vendors/flecs/src/storage/table_graph.c @@ -1,5 +1,5 @@ /** - * @file table_graph.c + * @file storage/table_graph.c * @brief Data structure to speed up table transitions. * * The table graph is used to speed up finding tables in add/remove operations. @@ -294,6 +294,8 @@ void flecs_table_diff_builder_init( ecs_allocator_t *a = &world->allocator; ecs_vec_init_t(a, &builder->added, ecs_id_t, 256); ecs_vec_init_t(a, &builder->removed, ecs_id_t, 256); + builder->added_flags = 0; + builder->removed_flags = 0; } void flecs_table_diff_builder_fini( @@ -340,6 +342,8 @@ void flecs_table_diff_build( added_offset); flecs_table_diff_build_type(world, &builder->removed, &diff->removed, removed_offset); + diff->added_flags = builder->added_flags; + diff->removed_flags = builder->removed_flags; } void flecs_table_diff_build_noalloc( @@ -350,6 +354,8 @@ void flecs_table_diff_build_noalloc( .array = builder->added.array, .count = builder->added.count }; diff->removed = (ecs_type_t){ .array = builder->removed.array, .count = builder->removed.count }; + diff->added_flags = builder->added_flags; + diff->removed_flags = builder->removed_flags; } static @@ -375,6 +381,8 @@ void flecs_table_diff_build_append_table( { flecs_table_diff_build_add_type_to_vec(world, &dst->added, &src->added); flecs_table_diff_build_add_type_to_vec(world, &dst->removed, &src->removed); + dst->added_flags |= src->added_flags; + dst->removed_flags |= src->removed_flags; } static @@ -531,17 +539,37 @@ void flecs_init_table( } static -ecs_table_t *flecs_create_table( +ecs_table_t *flecs_table_new( ecs_world_t *world, ecs_type_t *type, flecs_hashmap_result_t table_elem, ecs_table_t *prev) { + ecs_os_perf_trace_push("flecs.table.create"); + ecs_table_t *result = flecs_sparse_add_t(&world->store.tables, ecs_table_t); ecs_assert(result != NULL, ECS_INTERNAL_ERROR, NULL); result->_ = flecs_calloc_t(&world->allocator, ecs_table__t); ecs_assert(result->_ != NULL, ECS_INTERNAL_ERROR, NULL); - + +#ifdef FLECS_SANITIZE + int32_t i, j, count = type->count; + for (i = 0; i < count - 1; i ++) { + if (type->array[i] >= type->array[i + 1]) { + for (j = 0; j < count; j ++) { + char *str = ecs_id_str(world, type->array[j]); + if (i == j) { + ecs_err(" > %d: %s", j, str); + } else { + ecs_err(" %d: %s", j, str); + } + ecs_os_free(str); + } + ecs_abort(ECS_CONSTRAINT_VIOLATED, "table type is not ordered"); + } + } +#endif + result->id = flecs_sparse_last_id(&world->store.tables); result->type = *type; @@ -571,6 +599,8 @@ ecs_table_t *flecs_create_table( ecs_log_pop_2(); + ecs_os_perf_trace_pop("flecs.table.create"); + return result; } @@ -581,7 +611,7 @@ ecs_table_t* flecs_table_ensure( bool own_type, ecs_table_t *prev) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); int32_t id_count = type->count; if (!id_count) { @@ -604,11 +634,11 @@ ecs_table_t* flecs_table_ensure( /* If we get here, the table has not been found, so create it. */ if (own_type) { - return flecs_create_table(world, type, elem, prev); + return flecs_table_new(world, type, elem, prev); } ecs_type_t copy = flecs_type_copy(world, type); - return flecs_create_table(world, ©, elem, prev); + return flecs_table_new(world, ©, elem, prev); } static @@ -638,32 +668,34 @@ void flecs_compute_table_diff( ecs_graph_edge_t *edge, ecs_id_t id) { + ecs_type_t node_type = node->type; + ecs_type_t next_type = next->type; + if (ECS_IS_PAIR(id)) { ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair( ECS_PAIR_FIRST(id), EcsWildcard)); - if (idr->flags & EcsIdUnion) { + if (idr->flags & EcsIdIsUnion) { if (node != next) { - id = ecs_pair(EcsUnion, ECS_PAIR_FIRST(id)); + id = ecs_pair(ECS_PAIR_FIRST(id), EcsUnion); } else { ecs_table_diff_t *diff = flecs_bcalloc( &world->allocators.table_diff); diff->added.count = 1; diff->added.array = flecs_wdup_n(world, ecs_id_t, 1, &id); + diff->added_flags = EcsTableHasUnion; edge->diff = diff; return; } } } - ecs_type_t node_type = node->type; - ecs_type_t next_type = next->type; - ecs_id_t *ids_node = node_type.array; ecs_id_t *ids_next = next_type.array; int32_t i_node = 0, node_count = node_type.count; int32_t i_next = 0, next_count = next_type.count; int32_t added_count = 0; int32_t removed_count = 0; + ecs_flags32_t added_flags = 0, removed_flags = 0; bool trivial_edge = !ECS_HAS_RELATION(id, EcsIsA); /* First do a scan to see how big the diff is, so we don't have to realloc @@ -678,18 +710,36 @@ void flecs_compute_table_diff( trivial_edge &= !added || id_next == id; trivial_edge &= !removed || id_node == id; - added_count += added; - removed_count += removed; + if (added) { + added_flags |= flecs_id_flags_get(world, id_next) & + EcsTableAddEdgeFlags; + added_count ++; + } + + if (removed) { + removed_flags |= flecs_id_flags_get(world, id_node) & + EcsTableRemoveEdgeFlags; + removed_count ++; + } i_node += id_node <= id_next; i_next += id_next <= id_node; } - added_count += next_count - i_next; - removed_count += node_count - i_node; + for (; i_next < next_count; i_next ++) { + added_flags |= flecs_id_flags_get(world, ids_next[i_next]) & + EcsTableAddEdgeFlags; + added_count ++; + } + + for (; i_node < node_count; i_node ++) { + removed_flags |= flecs_id_flags_get(world, ids_node[i_node]) & + EcsTableRemoveEdgeFlags; + removed_count ++; + } trivial_edge &= (added_count + removed_count) <= 1 && - !ecs_id_is_wildcard(id); + !ecs_id_is_wildcard(id) && !(added_flags|removed_flags); if (trivial_edge) { /* If edge is trivial there's no need to create a diff element for it */ @@ -724,6 +774,8 @@ void flecs_compute_table_diff( ecs_table_diff_t *diff = flecs_bcalloc(&world->allocators.table_diff); edge->diff = diff; flecs_table_diff_build(world, builder, diff, added_offset, removed_offset); + diff->added_flags = added_flags; + diff->removed_flags = removed_flags; ecs_assert(diff->added.count == added_count, ECS_INTERNAL_ERROR, NULL); ecs_assert(diff->removed.count == removed_count, ECS_INTERNAL_ERROR, NULL); @@ -743,22 +795,22 @@ void flecs_add_overrides_for_base( } ecs_id_t *ids = base_table->type.array; - ecs_flags32_t flags = base_table->flags; if (flags & EcsTableHasOverrides) { int32_t i, count = base_table->type.count; for (i = 0; i < count; i ++) { ecs_id_t id = ids[i]; ecs_id_t to_add = 0; - if (ECS_HAS_ID_FLAG(id, OVERRIDE)) { - to_add = id & ~ECS_OVERRIDE; + if (ECS_HAS_ID_FLAG(id, AUTO_OVERRIDE)) { + to_add = id & ~ECS_AUTO_OVERRIDE; } else { ecs_table_record_t *tr = &base_table->_->records[i]; ecs_id_record_t *idr = (ecs_id_record_t*)tr->hdr.cache; - if (idr->flags & EcsIdAlwaysOverride) { + if (ECS_ID_ON_INSTANTIATE(idr->flags) == EcsOverride) { to_add = id; } } + if (to_add) { ecs_id_t wc = ecs_pair(ECS_PAIR_FIRST(to_add), EcsWildcard); bool exclusive = false; @@ -837,8 +889,8 @@ ecs_table_t* flecs_find_table_with( ecs_world_t *world, ecs_table_t *node, ecs_id_t with) -{ - ecs_ensure_id(world, with); +{ + ecs_make_alive_id(world, with); ecs_id_record_t *idr = NULL; ecs_entity_t r = 0, o = 0; @@ -847,16 +899,8 @@ ecs_table_t* flecs_find_table_with( r = ECS_PAIR_FIRST(with); o = ECS_PAIR_SECOND(with); idr = flecs_id_record_ensure(world, ecs_pair(r, EcsWildcard)); - if (idr->flags & EcsIdUnion) { - ecs_type_t dst_type; - ecs_id_t union_id = ecs_pair(EcsUnion, r); - int res = flecs_type_new_with( - world, &dst_type, &node->type, union_id); - if (res == -1) { - return node; - } - - return flecs_table_ensure(world, &dst_type, true, node); + if (idr->flags & EcsIdIsUnion) { + with = ecs_pair(r, EcsUnion); } else if (idr->flags & EcsIdExclusive) { /* Relationship is exclusive, check if table already has it */ ecs_table_record_t *tr = flecs_id_record_get_table(idr, node); @@ -912,8 +956,8 @@ ecs_table_t* flecs_find_table_without( ecs_id_record_t *idr = NULL; r = ECS_PAIR_FIRST(without); idr = flecs_id_record_get(world, ecs_pair(r, EcsWildcard)); - if (idr && idr->flags & EcsIdUnion) { - without = ecs_pair(EcsUnion, r); + if (idr && idr->flags & EcsIdIsUnion) { + without = ecs_pair(r, EcsUnion); } } @@ -957,7 +1001,7 @@ void flecs_init_edge_for_add( flecs_table_ensure_hi_edge(world, &table->node.add, id); - if (table != to || table->flags & EcsTableHasUnion) { + if ((table != to) || (table->flags & EcsTableHasUnion)) { /* Add edges are appended to refs.next */ ecs_graph_edge_hdr_t *to_refs = &to->node.refs; ecs_graph_edge_hdr_t *next = to_refs->next; @@ -1033,7 +1077,7 @@ ecs_table_t* flecs_table_traverse_remove( ecs_id_t *id_ptr, ecs_table_diff_t *diff) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); node = node ? node : &world->store.root; @@ -1072,7 +1116,7 @@ ecs_table_t* flecs_table_traverse_add( ecs_id_t *id_ptr, ecs_table_diff_t *diff) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(diff != NULL, ECS_INTERNAL_ERROR, NULL); node = node ? node : &world->store.root; @@ -1094,6 +1138,13 @@ ecs_table_t* flecs_table_traverse_add( if (node != to || edge->diff) { if (edge->diff) { *diff = *edge->diff; + if (diff->added_flags & EcsIdIsUnion) { + if (diff->added.count == 1) { + diff->added.array = id_ptr; + diff->added.count = 1; + diff->removed.count = 0; + } + } } else { diff->added.array = id_ptr; diff->added.count = 1; @@ -1110,14 +1161,14 @@ ecs_table_t* flecs_table_find_or_create( ecs_world_t *world, ecs_type_t *type) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); return flecs_table_ensure(world, type, false, NULL); } void flecs_init_root_table( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); world->store.root.type = (ecs_type_t){0}; world->store.root._ = flecs_calloc_t(&world->allocator, ecs_table__t); @@ -1129,12 +1180,60 @@ void flecs_init_root_table( (void)new_id; } +void flecs_table_edges_add_flags( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + ecs_flags32_t flags) +{ + ecs_graph_node_t *table_node = &table->node; + ecs_graph_edge_hdr_t *node_refs = &table_node->refs; + + /* Add flags to incoming matching add edges */ + if (flags == EcsTableHasOnAdd) { + ecs_graph_edge_hdr_t *next, *cur = node_refs->next; + if (cur) { + do { + ecs_graph_edge_t *edge = (ecs_graph_edge_t*)cur; + if ((id == EcsAny) || ecs_id_match(edge->id, id)) { + if (!edge->diff) { + edge->diff = flecs_bcalloc(&world->allocators.table_diff); + edge->diff->added.array = flecs_walloc_t(world, ecs_id_t); + edge->diff->added.count = 1; + edge->diff->added.array[0] = edge->id; + } + edge->diff->added_flags |= EcsTableHasOnAdd; + } + next = cur->next; + } while ((cur = next)); + } + } + + /* Add flags to outgoing matching remove edges */ + if (flags == EcsTableHasOnRemove) { + ecs_map_iter_t it = ecs_map_iter(table->node.remove.hi); + while (ecs_map_next(&it)) { + ecs_id_t edge_id = ecs_map_key(&it); + if ((id == EcsAny) || ecs_id_match(edge_id, id)) { + ecs_graph_edge_t *edge = ecs_map_ptr(&it); + if (!edge->diff) { + edge->diff = flecs_bcalloc(&world->allocators.table_diff); + edge->diff->removed.array = flecs_walloc_t(world, ecs_id_t); + edge->diff->removed.count = 1; + edge->diff->removed.array[0] = edge->id; + } + edge->diff->removed_flags |= EcsTableHasOnRemove; + } + } + } +} + void flecs_table_clear_edges( ecs_world_t *world, ecs_table_t *table) { (void)world; - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_log_push_1(); diff --git a/vendors/flecs/src/storage/table_graph.h b/vendors/flecs/src/storage/table_graph.h index 18fcf0a34..c6b617665 100644 --- a/vendors/flecs/src/storage/table_graph.h +++ b/vendors/flecs/src/storage/table_graph.h @@ -1,5 +1,5 @@ /** - * @file table_graph.h + * @file storage/table_graph.h * @brief Table graph types and functions. */ @@ -15,11 +15,15 @@ typedef struct ecs_table_diff_builder_t { ecs_vec_t added; ecs_vec_t removed; + ecs_flags32_t added_flags; + ecs_flags32_t removed_flags; } ecs_table_diff_builder_t; typedef struct ecs_table_diff_t { ecs_type_t added; /* Components added between tables */ ecs_type_t removed; /* Components removed between tables */ + ecs_flags32_t added_flags; + ecs_flags32_t removed_flags; } ecs_table_diff_t; /** Edge linked list (used to keep track of incoming edges) */ @@ -33,7 +37,7 @@ typedef struct ecs_graph_edge_t { ecs_graph_edge_hdr_t hdr; ecs_table_t *from; /* Edge source table */ ecs_table_t *to; /* Edge destination table */ - ecs_table_diff_t *diff; /* Index into diff vector, if non trivial edge */ + ecs_table_diff_t *diff; /* Added/removed components for edge */ ecs_id_t id; /* Id associated with edge */ } ecs_graph_edge_t; @@ -101,4 +105,10 @@ void flecs_table_diff_build_noalloc( ecs_table_diff_builder_t *builder, ecs_table_diff_t *diff); +void flecs_table_edges_add_flags( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + ecs_flags32_t flags); + #endif diff --git a/vendors/flecs/src/value.c b/vendors/flecs/src/value.c index 70bd05cf4..324b6d9cf 100644 --- a/vendors/flecs/src/value.c +++ b/vendors/flecs/src/value.c @@ -10,7 +10,7 @@ int ecs_value_init_w_type_info( const ecs_type_info_t *ti, void *ptr) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); (void)world; @@ -31,7 +31,7 @@ int ecs_value_init( ecs_entity_t type, void *ptr) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); const ecs_type_info_t *ti = ecs_get_type_info(world, type); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); return ecs_value_init_w_type_info(world, ti, ptr); @@ -43,7 +43,7 @@ void* ecs_value_new_w_type_info( ecs_world_t *world, const ecs_type_info_t *ti) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); (void)world; @@ -62,7 +62,7 @@ void* ecs_value_new( ecs_world_t *world, ecs_entity_t type) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); const ecs_type_info_t *ti = ecs_get_type_info(world, type); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); @@ -76,7 +76,7 @@ int ecs_value_fini_w_type_info( const ecs_type_info_t *ti, void *ptr) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); (void)world; @@ -95,7 +95,7 @@ int ecs_value_fini( ecs_entity_t type, void* ptr) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); (void)world; const ecs_type_info_t *ti = ecs_get_type_info(world, type); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); @@ -109,7 +109,7 @@ int ecs_value_free( ecs_entity_t type, void* ptr) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); const ecs_type_info_t *ti = ecs_get_type_info(world, type); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); if (ecs_value_fini_w_type_info(world, ti, ptr) != 0) { @@ -129,7 +129,7 @@ int ecs_value_copy_w_type_info( void* dst, const void *src) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); (void)world; @@ -151,7 +151,7 @@ int ecs_value_copy( void* dst, const void *src) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); const ecs_type_info_t *ti = ecs_get_type_info(world, type); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); return ecs_value_copy_w_type_info(world, ti, dst, src); @@ -165,7 +165,7 @@ int ecs_value_move_w_type_info( void* dst, void *src) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); (void)world; @@ -187,7 +187,7 @@ int ecs_value_move( void* dst, void *src) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); const ecs_type_info_t *ti = ecs_get_type_info(world, type); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); return ecs_value_move_w_type_info(world, ti, dst, src); @@ -201,7 +201,7 @@ int ecs_value_move_ctor_w_type_info( void* dst, void *src) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, NULL); (void)world; @@ -223,7 +223,7 @@ int ecs_value_move_ctor( void* dst, void *src) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); const ecs_type_info_t *ti = ecs_get_type_info(world, type); ecs_check(ti != NULL, ECS_INVALID_PARAMETER, "entity is not a type"); return ecs_value_move_w_type_info(world, ti, dst, src); diff --git a/vendors/flecs/src/world.c b/vendors/flecs/src/world.c index a4397f4bd..1408c3518 100644 --- a/vendors/flecs/src/world.c +++ b/vendors/flecs/src/world.c @@ -7,15 +7,13 @@ /* Id flags */ const ecs_id_t ECS_PAIR = (1ull << 63); -const ecs_id_t ECS_OVERRIDE = (1ull << 62); +const ecs_id_t ECS_AUTO_OVERRIDE = (1ull << 62); 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(EcsPoly) = 3; /* Poly target components */ const ecs_entity_t EcsQuery = 5; @@ -31,91 +29,102 @@ const ecs_entity_t EcsModule = FLECS_HI_COMPONENT_ID + 4; const ecs_entity_t EcsPrivate = FLECS_HI_COMPONENT_ID + 5; const ecs_entity_t EcsPrefab = FLECS_HI_COMPONENT_ID + 6; const ecs_entity_t EcsDisabled = FLECS_HI_COMPONENT_ID + 7; +const ecs_entity_t EcsNotQueryable = FLECS_HI_COMPONENT_ID + 8; + +const ecs_entity_t EcsSlotOf = FLECS_HI_COMPONENT_ID + 9; +const ecs_entity_t EcsFlag = FLECS_HI_COMPONENT_ID + 10; + +/* Marker entities for query encoding */ +const ecs_entity_t EcsWildcard = FLECS_HI_COMPONENT_ID + 11; +const ecs_entity_t EcsAny = FLECS_HI_COMPONENT_ID + 12; +const ecs_entity_t EcsThis = FLECS_HI_COMPONENT_ID + 13; +const ecs_entity_t EcsVariable = FLECS_HI_COMPONENT_ID + 14; + +/* Traits */ +const ecs_entity_t EcsTransitive = FLECS_HI_COMPONENT_ID + 15; +const ecs_entity_t EcsReflexive = FLECS_HI_COMPONENT_ID + 16; +const ecs_entity_t EcsSymmetric = FLECS_HI_COMPONENT_ID + 17; +const ecs_entity_t EcsFinal = FLECS_HI_COMPONENT_ID + 18; +const ecs_entity_t EcsOnInstantiate = FLECS_HI_COMPONENT_ID + 19; +const ecs_entity_t EcsOverride = FLECS_HI_COMPONENT_ID + 20; +const ecs_entity_t EcsInherit = FLECS_HI_COMPONENT_ID + 21; +const ecs_entity_t EcsDontInherit = FLECS_HI_COMPONENT_ID + 22; +const ecs_entity_t EcsPairIsTag = FLECS_HI_COMPONENT_ID + 23; +const ecs_entity_t EcsExclusive = FLECS_HI_COMPONENT_ID + 24; +const ecs_entity_t EcsAcyclic = FLECS_HI_COMPONENT_ID + 25; +const ecs_entity_t EcsTraversable = FLECS_HI_COMPONENT_ID + 26; +const ecs_entity_t EcsWith = FLECS_HI_COMPONENT_ID + 27; +const ecs_entity_t EcsOneOf = FLECS_HI_COMPONENT_ID + 28; +const ecs_entity_t EcsCanToggle = FLECS_HI_COMPONENT_ID + 29; +const ecs_entity_t EcsTrait = FLECS_HI_COMPONENT_ID + 30; +const ecs_entity_t EcsRelationship = FLECS_HI_COMPONENT_ID + 31; +const ecs_entity_t EcsTarget = FLECS_HI_COMPONENT_ID + 32; -const ecs_entity_t EcsSlotOf = FLECS_HI_COMPONENT_ID + 8; -const ecs_entity_t EcsFlag = FLECS_HI_COMPONENT_ID + 9; - -/* Relationship properties */ -const ecs_entity_t EcsWildcard = FLECS_HI_COMPONENT_ID + 10; -const ecs_entity_t EcsAny = FLECS_HI_COMPONENT_ID + 11; -const ecs_entity_t EcsThis = FLECS_HI_COMPONENT_ID + 12; -const ecs_entity_t EcsVariable = FLECS_HI_COMPONENT_ID + 13; -const ecs_entity_t EcsTransitive = FLECS_HI_COMPONENT_ID + 14; -const ecs_entity_t EcsReflexive = FLECS_HI_COMPONENT_ID + 15; -const ecs_entity_t EcsSymmetric = FLECS_HI_COMPONENT_ID + 16; -const ecs_entity_t EcsFinal = FLECS_HI_COMPONENT_ID + 17; -const ecs_entity_t EcsDontInherit = FLECS_HI_COMPONENT_ID + 18; -const ecs_entity_t EcsAlwaysOverride = FLECS_HI_COMPONENT_ID + 19; -const ecs_entity_t EcsTag = FLECS_HI_COMPONENT_ID + 20; -const ecs_entity_t EcsUnion = FLECS_HI_COMPONENT_ID + 21; -const ecs_entity_t EcsExclusive = FLECS_HI_COMPONENT_ID + 22; -const ecs_entity_t EcsAcyclic = FLECS_HI_COMPONENT_ID + 23; -const ecs_entity_t EcsTraversable = FLECS_HI_COMPONENT_ID + 24; -const ecs_entity_t EcsWith = FLECS_HI_COMPONENT_ID + 25; -const ecs_entity_t EcsOneOf = FLECS_HI_COMPONENT_ID + 26; /* Builtin relationships */ -const ecs_entity_t EcsChildOf = FLECS_HI_COMPONENT_ID + 27; -const ecs_entity_t EcsIsA = FLECS_HI_COMPONENT_ID + 28; -const ecs_entity_t EcsDependsOn = FLECS_HI_COMPONENT_ID + 29; +const ecs_entity_t EcsChildOf = FLECS_HI_COMPONENT_ID + 33; +const ecs_entity_t EcsIsA = FLECS_HI_COMPONENT_ID + 34; +const ecs_entity_t EcsDependsOn = FLECS_HI_COMPONENT_ID + 35; /* Identifier tags */ -const ecs_entity_t EcsName = FLECS_HI_COMPONENT_ID + 30; -const ecs_entity_t EcsSymbol = FLECS_HI_COMPONENT_ID + 31; -const ecs_entity_t EcsAlias = FLECS_HI_COMPONENT_ID + 32; +const ecs_entity_t EcsName = FLECS_HI_COMPONENT_ID + 36; +const ecs_entity_t EcsSymbol = FLECS_HI_COMPONENT_ID + 37; +const ecs_entity_t EcsAlias = FLECS_HI_COMPONENT_ID + 38; /* Events */ -const ecs_entity_t EcsOnAdd = FLECS_HI_COMPONENT_ID + 33; -const ecs_entity_t EcsOnRemove = FLECS_HI_COMPONENT_ID + 34; -const ecs_entity_t EcsOnSet = FLECS_HI_COMPONENT_ID + 35; -const ecs_entity_t EcsUnSet = FLECS_HI_COMPONENT_ID + 36; -const ecs_entity_t EcsOnDelete = FLECS_HI_COMPONENT_ID + 37; -const ecs_entity_t EcsOnTableCreate = FLECS_HI_COMPONENT_ID + 38; -const ecs_entity_t EcsOnTableDelete = FLECS_HI_COMPONENT_ID + 39; -const ecs_entity_t EcsOnTableEmpty = FLECS_HI_COMPONENT_ID + 40; -const ecs_entity_t EcsOnTableFill = FLECS_HI_COMPONENT_ID + 41; -const ecs_entity_t EcsOnDeleteTarget = FLECS_HI_COMPONENT_ID + 46; +const ecs_entity_t EcsOnAdd = FLECS_HI_COMPONENT_ID + 39; +const ecs_entity_t EcsOnRemove = FLECS_HI_COMPONENT_ID + 40; +const ecs_entity_t EcsOnSet = FLECS_HI_COMPONENT_ID + 41; +const ecs_entity_t EcsOnDelete = FLECS_HI_COMPONENT_ID + 43; +const ecs_entity_t EcsOnDeleteTarget = FLECS_HI_COMPONENT_ID + 44; +const ecs_entity_t EcsOnTableCreate = FLECS_HI_COMPONENT_ID + 45; +const ecs_entity_t EcsOnTableDelete = FLECS_HI_COMPONENT_ID + 46; +const ecs_entity_t EcsOnTableEmpty = FLECS_HI_COMPONENT_ID + 47; +const ecs_entity_t EcsOnTableFill = FLECS_HI_COMPONENT_ID + 48; /* Timers */ -const ecs_entity_t ecs_id(EcsTickSource) = FLECS_HI_COMPONENT_ID + 47; -const ecs_entity_t ecs_id(EcsTimer) = FLECS_HI_COMPONENT_ID + 48; -const ecs_entity_t ecs_id(EcsRateFilter) = FLECS_HI_COMPONENT_ID + 49; +const ecs_entity_t ecs_id(EcsTickSource) = FLECS_HI_COMPONENT_ID + 49; +const ecs_entity_t ecs_id(EcsTimer) = FLECS_HI_COMPONENT_ID + 50; +const ecs_entity_t ecs_id(EcsRateFilter) = FLECS_HI_COMPONENT_ID + 51; /* Actions */ -const ecs_entity_t EcsRemove = FLECS_HI_COMPONENT_ID + 50; -const ecs_entity_t EcsDelete = FLECS_HI_COMPONENT_ID + 51; -const ecs_entity_t EcsPanic = FLECS_HI_COMPONENT_ID + 52; +const ecs_entity_t EcsRemove = FLECS_HI_COMPONENT_ID + 52; +const ecs_entity_t EcsDelete = FLECS_HI_COMPONENT_ID + 53; +const ecs_entity_t EcsPanic = FLECS_HI_COMPONENT_ID + 54; + +/* Storage */ +const ecs_entity_t EcsSparse = FLECS_HI_COMPONENT_ID + 55; +const ecs_entity_t EcsUnion = FLECS_HI_COMPONENT_ID + 56; /* Misc */ -const ecs_entity_t ecs_id(EcsTarget) = FLECS_HI_COMPONENT_ID + 53; -const ecs_entity_t EcsFlatten = FLECS_HI_COMPONENT_ID + 54; -const ecs_entity_t EcsDefaultChildComponent = FLECS_HI_COMPONENT_ID + 55; +const ecs_entity_t ecs_id(EcsDefaultChildComponent) = FLECS_HI_COMPONENT_ID + 57; -/* Builtin predicate ids (used by rule engine) */ -const ecs_entity_t EcsPredEq = FLECS_HI_COMPONENT_ID + 56; -const ecs_entity_t EcsPredMatch = FLECS_HI_COMPONENT_ID + 57; -const ecs_entity_t EcsPredLookup = FLECS_HI_COMPONENT_ID + 58; -const ecs_entity_t EcsScopeOpen = FLECS_HI_COMPONENT_ID + 59; -const ecs_entity_t EcsScopeClose = FLECS_HI_COMPONENT_ID + 60; +/* Builtin predicate ids (used by query engine) */ +const ecs_entity_t EcsPredEq = FLECS_HI_COMPONENT_ID + 58; +const ecs_entity_t EcsPredMatch = FLECS_HI_COMPONENT_ID + 59; +const ecs_entity_t EcsPredLookup = FLECS_HI_COMPONENT_ID + 60; +const ecs_entity_t EcsScopeOpen = FLECS_HI_COMPONENT_ID + 61; +const ecs_entity_t EcsScopeClose = FLECS_HI_COMPONENT_ID + 62; /* Systems */ -const ecs_entity_t EcsMonitor = FLECS_HI_COMPONENT_ID + 61; -const ecs_entity_t EcsEmpty = FLECS_HI_COMPONENT_ID + 62; -const ecs_entity_t ecs_id(EcsPipeline) = FLECS_HI_COMPONENT_ID + 63; -const ecs_entity_t EcsOnStart = FLECS_HI_COMPONENT_ID + 64; -const ecs_entity_t EcsPreFrame = FLECS_HI_COMPONENT_ID + 65; -const ecs_entity_t EcsOnLoad = FLECS_HI_COMPONENT_ID + 66; -const ecs_entity_t EcsPostLoad = FLECS_HI_COMPONENT_ID + 67; -const ecs_entity_t EcsPreUpdate = FLECS_HI_COMPONENT_ID + 68; -const ecs_entity_t EcsOnUpdate = FLECS_HI_COMPONENT_ID + 69; -const ecs_entity_t EcsOnValidate = FLECS_HI_COMPONENT_ID + 70; -const ecs_entity_t EcsPostUpdate = FLECS_HI_COMPONENT_ID + 71; -const ecs_entity_t EcsPreStore = FLECS_HI_COMPONENT_ID + 72; -const ecs_entity_t EcsOnStore = FLECS_HI_COMPONENT_ID + 73; -const ecs_entity_t EcsPostFrame = FLECS_HI_COMPONENT_ID + 74; -const ecs_entity_t EcsPhase = FLECS_HI_COMPONENT_ID + 75; +const ecs_entity_t EcsMonitor = FLECS_HI_COMPONENT_ID + 63; +const ecs_entity_t EcsEmpty = FLECS_HI_COMPONENT_ID + 64; +const ecs_entity_t ecs_id(EcsPipeline) = FLECS_HI_COMPONENT_ID + 65; +const ecs_entity_t EcsOnStart = FLECS_HI_COMPONENT_ID + 66; +const ecs_entity_t EcsPreFrame = FLECS_HI_COMPONENT_ID + 67; +const ecs_entity_t EcsOnLoad = FLECS_HI_COMPONENT_ID + 68; +const ecs_entity_t EcsPostLoad = FLECS_HI_COMPONENT_ID + 69; +const ecs_entity_t EcsPreUpdate = FLECS_HI_COMPONENT_ID + 70; +const ecs_entity_t EcsOnUpdate = FLECS_HI_COMPONENT_ID + 71; +const ecs_entity_t EcsOnValidate = FLECS_HI_COMPONENT_ID + 72; +const ecs_entity_t EcsPostUpdate = FLECS_HI_COMPONENT_ID + 73; +const ecs_entity_t EcsPreStore = FLECS_HI_COMPONENT_ID + 74; +const ecs_entity_t EcsOnStore = FLECS_HI_COMPONENT_ID + 75; +const ecs_entity_t EcsPostFrame = FLECS_HI_COMPONENT_ID + 76; +const ecs_entity_t EcsPhase = FLECS_HI_COMPONENT_ID + 77; /* Meta primitive components (don't use low ids to save id space) */ +#ifdef FLECS_META const ecs_entity_t ecs_id(ecs_bool_t) = FLECS_HI_COMPONENT_ID + 80; const ecs_entity_t ecs_id(ecs_char_t) = FLECS_HI_COMPONENT_ID + 81; const ecs_entity_t ecs_id(ecs_byte_t) = FLECS_HI_COMPONENT_ID + 82; @@ -136,8 +145,8 @@ const ecs_entity_t ecs_id(ecs_entity_t) = FLECS_HI_COMPONENT_ID + 96; const ecs_entity_t ecs_id(ecs_id_t) = FLECS_HI_COMPONENT_ID + 97; /** Meta module component ids */ -const ecs_entity_t ecs_id(EcsMetaType) = FLECS_HI_COMPONENT_ID + 98; -const ecs_entity_t ecs_id(EcsMetaTypeSerialized) = FLECS_HI_COMPONENT_ID + 99; +const ecs_entity_t ecs_id(EcsType) = FLECS_HI_COMPONENT_ID + 98; +const ecs_entity_t ecs_id(EcsTypeSerializer) = FLECS_HI_COMPONENT_ID + 99; const ecs_entity_t ecs_id(EcsPrimitive) = FLECS_HI_COMPONENT_ID + 100; const ecs_entity_t ecs_id(EcsEnum) = FLECS_HI_COMPONENT_ID + 101; const ecs_entity_t ecs_id(EcsBitmask) = FLECS_HI_COMPONENT_ID + 102; @@ -151,22 +160,32 @@ const ecs_entity_t ecs_id(EcsUnit) = FLECS_HI_COMPONENT_ID + 109; const ecs_entity_t ecs_id(EcsUnitPrefix) = FLECS_HI_COMPONENT_ID + 110; const ecs_entity_t EcsConstant = FLECS_HI_COMPONENT_ID + 111; const ecs_entity_t EcsQuantity = FLECS_HI_COMPONENT_ID + 112; +#endif /* Doc module components */ +#ifdef FLECS_DOC const ecs_entity_t ecs_id(EcsDocDescription) = FLECS_HI_COMPONENT_ID + 113; const ecs_entity_t EcsDocBrief = FLECS_HI_COMPONENT_ID + 114; const ecs_entity_t EcsDocDetail = FLECS_HI_COMPONENT_ID + 115; const ecs_entity_t EcsDocLink = FLECS_HI_COMPONENT_ID + 116; const ecs_entity_t EcsDocColor = FLECS_HI_COMPONENT_ID + 117; +const ecs_entity_t EcsDocUuid = FLECS_HI_COMPONENT_ID + 118; +#endif /* REST module components */ -const ecs_entity_t ecs_id(EcsRest) = FLECS_HI_COMPONENT_ID + 118; +#ifdef FLECS_REST +const ecs_entity_t ecs_id(EcsRest) = FLECS_HI_COMPONENT_ID + 119; +#endif + +/* Max static id: + * #define EcsFirstUserEntityId (FLECS_HI_COMPONENT_ID + 128) */ /* Default lookup path */ static ecs_entity_t ecs_default_lookup_path[2] = { 0, 0 }; -/* Declarations for addons. Located in world.c to avoid issues during linking of +/* Declarations for addons. Located in world.c to avoid issues during linking of * static library */ + #ifdef FLECS_ALERTS ECS_COMPONENT_DECLARE(EcsAlert); ECS_COMPONENT_DECLARE(EcsAlertInstance); @@ -177,157 +196,161 @@ ECS_TAG_DECLARE(EcsAlertError); ECS_TAG_DECLARE(EcsAlertCritical); #endif #ifdef FLECS_UNITS -ECS_DECLARE(EcsUnitPrefixes); - -ECS_DECLARE(EcsYocto); -ECS_DECLARE(EcsZepto); -ECS_DECLARE(EcsAtto); -ECS_DECLARE(EcsFemto); -ECS_DECLARE(EcsPico); -ECS_DECLARE(EcsNano); -ECS_DECLARE(EcsMicro); -ECS_DECLARE(EcsMilli); -ECS_DECLARE(EcsCenti); -ECS_DECLARE(EcsDeci); -ECS_DECLARE(EcsDeca); -ECS_DECLARE(EcsHecto); -ECS_DECLARE(EcsKilo); -ECS_DECLARE(EcsMega); -ECS_DECLARE(EcsGiga); -ECS_DECLARE(EcsTera); -ECS_DECLARE(EcsPeta); -ECS_DECLARE(EcsExa); -ECS_DECLARE(EcsZetta); -ECS_DECLARE(EcsYotta); - -ECS_DECLARE(EcsKibi); -ECS_DECLARE(EcsMebi); -ECS_DECLARE(EcsGibi); -ECS_DECLARE(EcsTebi); -ECS_DECLARE(EcsPebi); -ECS_DECLARE(EcsExbi); -ECS_DECLARE(EcsZebi); -ECS_DECLARE(EcsYobi); - -ECS_DECLARE(EcsDuration); - ECS_DECLARE(EcsPicoSeconds); - ECS_DECLARE(EcsNanoSeconds); - ECS_DECLARE(EcsMicroSeconds); - ECS_DECLARE(EcsMilliSeconds); - ECS_DECLARE(EcsSeconds); - ECS_DECLARE(EcsMinutes); - ECS_DECLARE(EcsHours); - ECS_DECLARE(EcsDays); - -ECS_DECLARE(EcsTime); - ECS_DECLARE(EcsDate); - -ECS_DECLARE(EcsMass); - ECS_DECLARE(EcsGrams); - ECS_DECLARE(EcsKiloGrams); - -ECS_DECLARE(EcsElectricCurrent); - ECS_DECLARE(EcsAmpere); - -ECS_DECLARE(EcsAmount); - ECS_DECLARE(EcsMole); - -ECS_DECLARE(EcsLuminousIntensity); - ECS_DECLARE(EcsCandela); - -ECS_DECLARE(EcsForce); - ECS_DECLARE(EcsNewton); - -ECS_DECLARE(EcsLength); - ECS_DECLARE(EcsMeters); - ECS_DECLARE(EcsPicoMeters); - ECS_DECLARE(EcsNanoMeters); - ECS_DECLARE(EcsMicroMeters); - ECS_DECLARE(EcsMilliMeters); - ECS_DECLARE(EcsCentiMeters); - ECS_DECLARE(EcsKiloMeters); - ECS_DECLARE(EcsMiles); - ECS_DECLARE(EcsPixels); - -ECS_DECLARE(EcsPressure); - ECS_DECLARE(EcsPascal); - ECS_DECLARE(EcsBar); - -ECS_DECLARE(EcsSpeed); - ECS_DECLARE(EcsMetersPerSecond); - ECS_DECLARE(EcsKiloMetersPerSecond); - ECS_DECLARE(EcsKiloMetersPerHour); - ECS_DECLARE(EcsMilesPerHour); - -ECS_DECLARE(EcsAcceleration); - -ECS_DECLARE(EcsTemperature); - ECS_DECLARE(EcsKelvin); - ECS_DECLARE(EcsCelsius); - ECS_DECLARE(EcsFahrenheit); - -ECS_DECLARE(EcsData); - ECS_DECLARE(EcsBits); - ECS_DECLARE(EcsKiloBits); - ECS_DECLARE(EcsMegaBits); - ECS_DECLARE(EcsGigaBits); - ECS_DECLARE(EcsBytes); - ECS_DECLARE(EcsKiloBytes); - ECS_DECLARE(EcsMegaBytes); - ECS_DECLARE(EcsGigaBytes); - ECS_DECLARE(EcsKibiBytes); - ECS_DECLARE(EcsGibiBytes); - ECS_DECLARE(EcsMebiBytes); - -ECS_DECLARE(EcsDataRate); - ECS_DECLARE(EcsBitsPerSecond); - ECS_DECLARE(EcsKiloBitsPerSecond); - ECS_DECLARE(EcsMegaBitsPerSecond); - ECS_DECLARE(EcsGigaBitsPerSecond); - ECS_DECLARE(EcsBytesPerSecond); - ECS_DECLARE(EcsKiloBytesPerSecond); - ECS_DECLARE(EcsMegaBytesPerSecond); - ECS_DECLARE(EcsGigaBytesPerSecond); - -ECS_DECLARE(EcsPercentage); - -ECS_DECLARE(EcsAngle); - ECS_DECLARE(EcsRadians); - ECS_DECLARE(EcsDegrees); - -ECS_DECLARE(EcsBel); -ECS_DECLARE(EcsDeciBel); - -ECS_DECLARE(EcsFrequency); - ECS_DECLARE(EcsHertz); - ECS_DECLARE(EcsKiloHertz); - ECS_DECLARE(EcsMegaHertz); - ECS_DECLARE(EcsGigaHertz); - -ECS_DECLARE(EcsUri); - ECS_DECLARE(EcsUriHyperlink); - ECS_DECLARE(EcsUriImage); - ECS_DECLARE(EcsUriFile); +ecs_entity_t EcsUnitPrefixes; + +ecs_entity_t EcsYocto; +ecs_entity_t EcsZepto; +ecs_entity_t EcsAtto; +ecs_entity_t EcsFemto; +ecs_entity_t EcsPico; +ecs_entity_t EcsNano; +ecs_entity_t EcsMicro; +ecs_entity_t EcsMilli; +ecs_entity_t EcsCenti; +ecs_entity_t EcsDeci; +ecs_entity_t EcsDeca; +ecs_entity_t EcsHecto; +ecs_entity_t EcsKilo; +ecs_entity_t EcsMega; +ecs_entity_t EcsGiga; +ecs_entity_t EcsTera; +ecs_entity_t EcsPeta; +ecs_entity_t EcsExa; +ecs_entity_t EcsZetta; +ecs_entity_t EcsYotta; + +ecs_entity_t EcsKibi; +ecs_entity_t EcsMebi; +ecs_entity_t EcsGibi; +ecs_entity_t EcsTebi; +ecs_entity_t EcsPebi; +ecs_entity_t EcsExbi; +ecs_entity_t EcsZebi; +ecs_entity_t EcsYobi; + +ecs_entity_t EcsDuration; + ecs_entity_t EcsPicoSeconds; + ecs_entity_t EcsNanoSeconds; + ecs_entity_t EcsMicroSeconds; + ecs_entity_t EcsMilliSeconds; + ecs_entity_t EcsSeconds; + ecs_entity_t EcsMinutes; + ecs_entity_t EcsHours; + ecs_entity_t EcsDays; + +ecs_entity_t EcsTime; + ecs_entity_t EcsDate; + +ecs_entity_t EcsMass; + ecs_entity_t EcsGrams; + ecs_entity_t EcsKiloGrams; + +ecs_entity_t EcsElectricCurrent; + ecs_entity_t EcsAmpere; + +ecs_entity_t EcsAmount; + ecs_entity_t EcsMole; + +ecs_entity_t EcsLuminousIntensity; + ecs_entity_t EcsCandela; + +ecs_entity_t EcsForce; + ecs_entity_t EcsNewton; + +ecs_entity_t EcsLength; + ecs_entity_t EcsMeters; + ecs_entity_t EcsPicoMeters; + ecs_entity_t EcsNanoMeters; + ecs_entity_t EcsMicroMeters; + ecs_entity_t EcsMilliMeters; + ecs_entity_t EcsCentiMeters; + ecs_entity_t EcsKiloMeters; + ecs_entity_t EcsMiles; + ecs_entity_t EcsPixels; + +ecs_entity_t EcsPressure; + ecs_entity_t EcsPascal; + ecs_entity_t EcsBar; + +ecs_entity_t EcsSpeed; + ecs_entity_t EcsMetersPerSecond; + ecs_entity_t EcsKiloMetersPerSecond; + ecs_entity_t EcsKiloMetersPerHour; + ecs_entity_t EcsMilesPerHour; + +ecs_entity_t EcsAcceleration; + +ecs_entity_t EcsTemperature; + ecs_entity_t EcsKelvin; + ecs_entity_t EcsCelsius; + ecs_entity_t EcsFahrenheit; + +ecs_entity_t EcsData; + ecs_entity_t EcsBits; + ecs_entity_t EcsKiloBits; + ecs_entity_t EcsMegaBits; + ecs_entity_t EcsGigaBits; + ecs_entity_t EcsBytes; + ecs_entity_t EcsKiloBytes; + ecs_entity_t EcsMegaBytes; + ecs_entity_t EcsGigaBytes; + ecs_entity_t EcsKibiBytes; + ecs_entity_t EcsGibiBytes; + ecs_entity_t EcsMebiBytes; + +ecs_entity_t EcsDataRate; + ecs_entity_t EcsBitsPerSecond; + ecs_entity_t EcsKiloBitsPerSecond; + ecs_entity_t EcsMegaBitsPerSecond; + ecs_entity_t EcsGigaBitsPerSecond; + ecs_entity_t EcsBytesPerSecond; + ecs_entity_t EcsKiloBytesPerSecond; + ecs_entity_t EcsMegaBytesPerSecond; + ecs_entity_t EcsGigaBytesPerSecond; + +ecs_entity_t EcsPercentage; + +ecs_entity_t EcsAngle; + ecs_entity_t EcsRadians; + ecs_entity_t EcsDegrees; + +ecs_entity_t EcsColor; + ecs_entity_t EcsColorRgb; + ecs_entity_t EcsColorHsl; + ecs_entity_t EcsColorCss; + +ecs_entity_t EcsBel; +ecs_entity_t EcsDeciBel; + +ecs_entity_t EcsFrequency; + ecs_entity_t EcsHertz; + ecs_entity_t EcsKiloHertz; + ecs_entity_t EcsMegaHertz; + ecs_entity_t EcsGigaHertz; + +ecs_entity_t EcsUri; + ecs_entity_t EcsUriHyperlink; + ecs_entity_t EcsUriImage; + ecs_entity_t EcsUriFile; #endif /* -- Private functions -- */ -const ecs_stage_t* flecs_stage_from_readonly_world( +ecs_stage_t* flecs_stage_from_readonly_world( const ecs_world_t *world) { - ecs_assert(ecs_poly_is(world, ecs_world_t) || - ecs_poly_is(world, ecs_stage_t), + ecs_assert(flecs_poly_is(world, ecs_world_t) || + flecs_poly_is(world, ecs_stage_t), ECS_INTERNAL_ERROR, NULL); - if (ecs_poly_is(world, ecs_world_t)) { - return &world->stages[0]; - - } else if (ecs_poly_is(world, ecs_stage_t)) { + if (flecs_poly_is(world, ecs_world_t)) { + return ECS_CONST_CAST(ecs_stage_t*, world->stages[0]); + } else if (flecs_poly_is(world, ecs_stage_t)) { return ECS_CONST_CAST(ecs_stage_t*, world); } - - return NULL; + + return NULL; } ecs_stage_t* flecs_stage_from_world( @@ -335,13 +358,13 @@ ecs_stage_t* flecs_stage_from_world( { ecs_world_t *world = *world_ptr; - ecs_assert(ecs_poly_is(world, ecs_world_t) || - ecs_poly_is(world, ecs_stage_t), + ecs_assert(flecs_poly_is(world, ecs_world_t) || + flecs_poly_is(world, ecs_stage_t), ECS_INTERNAL_ERROR, NULL); - if (ecs_poly_is(world, ecs_world_t)) { - return &world->stages[0]; + if (flecs_poly_is(world, ecs_world_t)) { + return world->stages[0]; } *world_ptr = ((ecs_stage_t*)world)->world; @@ -355,9 +378,9 @@ ecs_world_t* flecs_suspend_readonly( ecs_assert(stage_world != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(state != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_world_t *world = + ecs_world_t *world = ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage_world)); - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); bool is_readonly = ECS_BIT_IS_SET(world->flags, EcsWorldReadonly); ecs_world_t *temp_world = world; @@ -373,7 +396,7 @@ ecs_world_t* flecs_suspend_readonly( /* Cannot suspend when running with multiple threads */ ecs_assert(!(world->flags & EcsWorldReadonly) || - (ecs_get_stage_count(world) <= 1), ECS_INVALID_WHILE_READONLY, NULL); + !(world->flags & EcsWorldMultiThreaded), ECS_INVALID_WHILE_READONLY, NULL); state->is_readonly = is_readonly; state->is_deferred = stage->defer != 0; @@ -390,7 +413,7 @@ ecs_world_t* flecs_suspend_readonly( state->with = stage->with; stage->defer = 0; ecs_vec_init_t(NULL, &stage->cmd->queue, ecs_cmd_t, 0); - + return world; } @@ -398,15 +421,15 @@ void flecs_resume_readonly( ecs_world_t *world, ecs_suspend_readonly_state_t *state) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(state != NULL, ECS_INTERNAL_ERROR, NULL); - + ecs_world_t *temp_world = world; ecs_stage_t *stage = flecs_stage_from_world(&temp_world); if (state->is_readonly || state->is_deferred) { ecs_dbg_3("resuming readonly mode"); - + ecs_run_aperiodic(world, 0); /* Restore readonly state / defer count */ @@ -422,19 +445,23 @@ void flecs_resume_readonly( } /* Evaluate component monitor. If a monitored entity changed it will have set a - * flag in one of the world's component monitors. Queries can register + * flag in one of the world's component monitors. Queries can register * themselves with component monitors to determine whether they need to rematch * with tables. */ static void flecs_eval_component_monitor( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); if (!world->monitors.is_dirty) { return; } + world->info.eval_comp_monitors_total ++; + + ecs_os_perf_trace_push("flecs.component_monitor.eval"); + world->monitors.is_dirty = false; ecs_map_iter_t it = ecs_map_iter(&world->monitors.monitors); @@ -450,11 +477,14 @@ void flecs_eval_component_monitor( ecs_query_t **elems = ecs_vec_first(&m->queries); for (i = 0; i < count; i ++) { ecs_query_t *q = elems[i]; - flecs_query_notify(world, q, &(ecs_query_event_t) { + flecs_poly_assert(q, ecs_query_t); + flecs_query_cache_notify(world, q, &(ecs_query_cache_event_t) { .kind = EcsQueryTableRematch }); } } + + ecs_os_perf_trace_pop("flecs.component_monitor.eval"); } void flecs_monitor_mark_dirty( @@ -485,6 +515,7 @@ void flecs_monitor_register( ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(query != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(query, ecs_query_t); ecs_map_t *monitors = &world->monitors.monitors; ecs_map_init_if(monitors, &world->allocator); @@ -503,6 +534,7 @@ void flecs_monitor_unregister( ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(query != NULL, ECS_INTERNAL_ERROR, NULL); + flecs_poly_assert(query, ecs_query_t); ecs_map_t *monitors = &world->monitors.monitors; if (!ecs_map_is_init(monitors)) { @@ -536,28 +568,31 @@ void flecs_monitor_unregister( static void flecs_init_store( - ecs_world_t *world) + ecs_world_t *world) { ecs_os_memset(&world->store, 0, ECS_SIZEOF(ecs_store_t)); - + ecs_allocator_t *a = &world->allocator; ecs_vec_init_t(a, &world->store.records, ecs_table_record_t, 0); ecs_vec_init_t(a, &world->store.marked_ids, ecs_marked_id_t, 0); - ecs_vec_init_t(a, &world->store.depth_ids, ecs_entity_t, 0); - ecs_map_init(&world->store.entity_to_depth, &world->allocator); + ecs_vec_init_t(a, &world->store.deleted_components, ecs_entity_t, 0); /* Initialize entity index */ flecs_entities_init(world); - /* Initialize root table */ + /* Initialize table sparse set */ flecs_sparse_init_t(&world->store.tables, a, &world->allocators.sparse_chunk, ecs_table_t); /* Initialize table map */ flecs_table_hashmap_init(world, &world->store.table_map); - /* Initialize one root table per stage */ + /* Initialize root table */ flecs_init_root_table(world); + + /* Initialize observer sparse set */ + flecs_sparse_init_t(&world->store.observers, + a, &world->allocators.sparse_chunk, ecs_observer_impl_t); } static @@ -568,20 +603,20 @@ void flecs_clean_tables( /* Ensure that first table in sparse set has id 0. This is a dummy table * that only exists so that there is no table with id 0 */ - ecs_table_t *first = flecs_sparse_get_dense_t(&world->store.tables, + ecs_table_t *first = flecs_sparse_get_dense_t(&world->store.tables, ecs_table_t, 0); (void)first; for (i = 1; i < count; i ++) { - ecs_table_t *t = flecs_sparse_get_dense_t(&world->store.tables, + ecs_table_t *t = flecs_sparse_get_dense_t(&world->store.tables, ecs_table_t, i); - flecs_table_free(world, t); + flecs_table_fini(world, t); } /* Free table types separately so that if application destructors rely on * a type it's still valid. */ for (i = 1; i < count; i ++) { - ecs_table_t *t = flecs_sparse_get_dense_t(&world->store.tables, + ecs_table_t *t = flecs_sparse_get_dense_t(&world->store.tables, ecs_table_t, i); flecs_table_free_type(world, t); } @@ -598,121 +633,116 @@ void flecs_fini_root_tables( ecs_id_record_t *idr, bool fini_targets) { - ecs_table_cache_iter_t it; + ecs_stage_t *stage0 = world->stages[0]; + bool finished = false; + const ecs_size_t MAX_DEFERRED_DELETE_QUEUE_SIZE = 4096; + while (!finished) { + ecs_table_cache_iter_t it; + ecs_size_t queue_size = 0; + finished = true; - bool has_roots = flecs_table_cache_iter(&idr->cache, &it); - ecs_assert(has_roots == true, ECS_INTERNAL_ERROR, NULL); - (void)has_roots; + bool has_roots = flecs_table_cache_iter(&idr->cache, &it); + ecs_assert(has_roots == true, ECS_INTERNAL_ERROR, NULL); + (void)has_roots; - const ecs_table_record_t *tr; - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; - if (table->flags & EcsTableHasBuiltins) { - continue; /* Filter out modules */ - } + const ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + if (table->flags & EcsTableHasBuiltins) { + continue; /* Query out modules */ + } - int32_t i, count = table->data.entities.count; - ecs_entity_t *entities = table->data.entities.array; - - if (fini_targets) { - /* Only delete entities that are used as pair target. Iterate - * backwards to minimize moving entities around in table. */ - for (i = count - 1; i >= 0; i --) { - ecs_record_t *r = flecs_entities_get(world, entities[i]); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - if (ECS_RECORD_TO_ROW_FLAGS(r->row) & EcsEntityIsTarget) { - ecs_delete(world, entities[i]); + int32_t i, count = ecs_table_count(table); + const ecs_entity_t *entities = ecs_table_entities(table); + + if (fini_targets) { + /* Only delete entities that are used as pair target. Iterate + * backwards to minimize moving entities around in table. */ + for (i = count - 1; i >= 0; i --) { + ecs_record_t *r = flecs_entities_get(world, entities[i]); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table == table, ECS_INTERNAL_ERROR, NULL); + if (ECS_RECORD_TO_ROW_FLAGS(r->row) & EcsEntityIsTarget) { + ecs_delete(world, entities[i]); + queue_size++; + /* Flush the queue before it grows too big: */ + if(queue_size >= MAX_DEFERRED_DELETE_QUEUE_SIZE) { + finished = false; + break; /* restart iteration */ + } + } } - } - } else { - /* Delete remaining entities that are not in use (added to another - * entity). This limits table moves during cleanup and delays - * cleanup of tags. */ - for (i = count - 1; i >= 0; i --) { - ecs_record_t *r = flecs_entities_get(world, entities[i]); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - if (!ECS_RECORD_TO_ROW_FLAGS(r->row)) { - ecs_delete(world, entities[i]); + } else { + /* Delete remaining entities that are not in use (added to another + * entity). This limits table moves during cleanup and delays + * cleanup of tags. */ + for (i = count - 1; i >= 0; i --) { + ecs_record_t *r = flecs_entities_get(world, entities[i]); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table == table, ECS_INTERNAL_ERROR, NULL); + if (!ECS_RECORD_TO_ROW_FLAGS(r->row)) { + ecs_delete(world, entities[i]); + queue_size++; + /* Flush the queue before it grows too big: */ + if(queue_size >= MAX_DEFERRED_DELETE_QUEUE_SIZE) { + finished = false; + break; /* restart iteration */ + } + } } } + if(!finished) { + /* flush queue and restart iteration */ + flecs_defer_end(world, stage0); + flecs_defer_begin(world, stage0); + break; + } } } } static void flecs_fini_roots( - ecs_world_t *world) + ecs_world_t *world) { ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair(EcsChildOf, 0)); ecs_run_aperiodic(world, EcsAperiodicEmptyTables); - /* Delete root entities that are not modules. This prioritizes deleting + /* Delete root entities that are not modules. This prioritizes deleting * regular entities first, which reduces the chance of components getting * destructed in random order because it got deleted before entities, * thereby bypassing the OnDeleteTarget policy. */ - flecs_defer_begin(world, &world->stages[0]); + flecs_defer_begin(world, world->stages[0]); flecs_fini_root_tables(world, idr, true); - flecs_defer_end(world, &world->stages[0]); + flecs_defer_end(world, world->stages[0]); - flecs_defer_begin(world, &world->stages[0]); + flecs_defer_begin(world, world->stages[0]); flecs_fini_root_tables(world, idr, false); - flecs_defer_end(world, &world->stages[0]); + flecs_defer_end(world, world->stages[0]); } static void flecs_fini_store(ecs_world_t *world) { flecs_clean_tables(world); flecs_sparse_fini(&world->store.tables); - flecs_table_free(world, &world->store.root); + flecs_table_fini(world, &world->store.root); flecs_entities_clear(world); flecs_hashmap_fini(&world->store.table_map); + ecs_assert(flecs_sparse_count(&world->store.observers) == 0, + ECS_INTERNAL_ERROR, NULL); + flecs_sparse_fini(&world->store.observers); + + ecs_assert(ecs_vec_count(&world->store.marked_ids) == 0, + ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_vec_count(&world->store.deleted_components) == 0, + ECS_INTERNAL_ERROR, NULL); + ecs_allocator_t *a = &world->allocator; ecs_vec_fini_t(a, &world->store.records, ecs_table_record_t); ecs_vec_fini_t(a, &world->store.marked_ids, ecs_marked_id_t); - ecs_vec_fini_t(a, &world->store.depth_ids, ecs_entity_t); - ecs_map_fini(&world->store.entity_to_depth); -} - -/* Implementation for iterable mixin */ -static -bool flecs_world_iter_next( - ecs_iter_t *it) -{ - if (ECS_BIT_IS_SET(it->flags, EcsIterIsValid)) { - ECS_BIT_CLEAR(it->flags, EcsIterIsValid); - ecs_iter_fini(it); - return false; - } - - ecs_world_t *world = it->real_world; - it->entities = ECS_CONST_CAST(ecs_entity_t*, flecs_entities_ids(world)); - it->count = flecs_entities_count(world); - flecs_iter_validate(it); - - return true; -} - -static -void flecs_world_iter_init( - const ecs_world_t *world, - const ecs_poly_t *poly, - ecs_iter_t *iter, - ecs_term_t *filter) -{ - ecs_poly_assert(poly, ecs_world_t); - (void)poly; - - if (filter) { - iter[0] = ecs_term_iter(world, filter); - } else { - iter[0] = (ecs_iter_t){ - .world = ECS_CONST_CAST(ecs_world_t*, world), - .real_world = ECS_CONST_CAST(ecs_world_t*, ecs_get_world(world)), - .next = flecs_world_iter_next - }; - } + ecs_vec_fini_t(a, &world->store.deleted_components, ecs_entity_t); } static @@ -726,8 +756,8 @@ void flecs_world_allocators_init( ecs_map_params_init(&a->ptr, &world->allocator); ecs_map_params_init(&a->query_table_list, &world->allocator); - flecs_ballocator_init_t(&a->query_table, ecs_query_table_t); - flecs_ballocator_init_t(&a->query_table_match, ecs_query_table_match_t); + flecs_ballocator_init_t(&a->query_table, ecs_query_cache_table_t); + flecs_ballocator_init_t(&a->query_table_match, ecs_query_cache_table_match_t); flecs_ballocator_init_n(&a->graph_edge_lo, ecs_graph_edge_t, FLECS_HI_COMPONENT_ID); flecs_ballocator_init_t(&a->graph_edge, ecs_graph_edge_t); flecs_ballocator_init_t(&a->id_record, ecs_id_record_t); @@ -738,7 +768,7 @@ void flecs_world_allocators_init( flecs_table_diff_builder_init(world, &world->allocators.diff_builder); } -static +static void flecs_world_allocators_fini( ecs_world_t *world) { @@ -746,7 +776,6 @@ void flecs_world_allocators_fini( ecs_map_params_fini(&a->ptr); ecs_map_params_fini(&a->query_table_list); - flecs_ballocator_fini(&a->query_table); flecs_ballocator_fini(&a->query_table_match); flecs_ballocator_fini(&a->graph_edge_lo); @@ -761,93 +790,146 @@ void flecs_world_allocators_fini( flecs_allocator_fini(&world->allocator); } +#define ECS_STRINGIFY_INNER(x) #x +#define ECS_STRINGIFY(x) ECS_STRINGIFY_INNER(x) + +static const char flecs_compiler_info[] +#if defined(__clang__) + = "clang " __clang_version__; +#elif defined(__GNUC__) + = "gcc " ECS_STRINGIFY(__GNUC__) "." ECS_STRINGIFY(__GNUC_MINOR__); +#elif defined(_MSC_VER) + = "msvc " ECS_STRINGIFY(_MSC_VER); +#elif defined(__TINYC__) + = "tcc " ECS_STRINGIFY(__TINYC__); +#else + = "unknown compiler"; +#endif + +static const char *flecs_addons_info[] = { +#ifdef FLECS_CPP + "FLECS_CPP", +#endif +#ifdef FLECS_MODULE + "FLECS_MODULE", +#endif +#ifdef FLECS_SCRIPT + "FLECS_SCRIPT", +#endif +#ifdef FLECS_STATS + "FLECS_STATS", +#endif +#ifdef FLECS_METRICS + "FLECS_METRICS", +#endif +#ifdef FLECS_ALERTS + "FLECS_ALERTS", +#endif +#ifdef FLECS_SYSTEM + "FLECS_SYSTEM", +#endif +#ifdef FLECS_PIPELINE + "FLECS_PIPELINE", +#endif +#ifdef FLECS_TIMER + "FLECS_TIMER", +#endif +#ifdef FLECS_META + "FLECS_META", +#endif +#ifdef FLECS_UNITS + "FLECS_UNITS", +#endif +#ifdef FLECS_JSON + "FLECS_JSON", +#endif +#ifdef FLECS_DOC + "FLECS_DOC", +#endif +#ifdef FLECS_LOG + "FLECS_LOG", +#endif +#ifdef FLECS_JOURNAL + "FLECS_JOURNAL", +#endif +#ifdef FLECS_APP + "FLECS_APP", +#endif +#ifdef FLECS_OS_API_IMPL + "FLECS_OS_API_IMPL", +#endif +#ifdef FLECS_SCRIPT + "FLECS_SCRIPT", +#endif +#ifdef FLECS_HTTP + "FLECS_HTTP", +#endif +#ifdef FLECS_REST + "FLECS_REST", +#endif +NULL +}; + +static const ecs_build_info_t flecs_build_info = { + .compiler = flecs_compiler_info, + .addons = flecs_addons_info, +#ifdef FLECS_DEBUG + .debug = true, +#endif +#ifdef FLECS_SANITIZE + .sanitize = true, +#endif +#ifdef FLECS_PERF_TRACE + .perf_trace = true, +#endif + .version = FLECS_VERSION, + .version_major = FLECS_VERSION_MAJOR, + .version_minor = FLECS_VERSION_MINOR, + .version_patch = FLECS_VERSION_PATCH +}; + static -void flecs_log_addons(void) { +void flecs_log_build_info(void) { + const ecs_build_info_t *bi = ecs_get_build_info(); + ecs_assert(bi != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_trace("flecs version %s", bi->version); + ecs_trace("addons included in build:"); ecs_log_push(); - #ifdef FLECS_CPP - ecs_trace("FLECS_CPP"); - #endif - #ifdef FLECS_MODULE - ecs_trace("FLECS_MODULE"); - #endif - #ifdef FLECS_PARSER - ecs_trace("FLECS_PARSER"); - #endif - #ifdef FLECS_PLECS - ecs_trace("FLECS_PLECS"); - #endif - #ifdef FLECS_RULES - ecs_trace("FLECS_RULES"); - #endif - #ifdef FLECS_SNAPSHOT - ecs_trace("FLECS_SNAPSHOT"); - #endif - #ifdef FLECS_STATS - ecs_trace("FLECS_STATS"); - #endif - #ifdef FLECS_MONITOR - ecs_trace("FLECS_MONITOR"); - #endif - #ifdef FLECS_METRICS - ecs_trace("FLECS_METRICS"); - #endif - #ifdef FLECS_SYSTEM - ecs_trace("FLECS_SYSTEM"); - #endif - #ifdef FLECS_PIPELINE - ecs_trace("FLECS_PIPELINE"); - #endif - #ifdef FLECS_TIMER - ecs_trace("FLECS_TIMER"); - #endif - #ifdef FLECS_META - ecs_trace("FLECS_META"); - #endif - #ifdef FLECS_META_C - ecs_trace("FLECS_META_C"); - #endif - #ifdef FLECS_UNITS - ecs_trace("FLECS_UNITS"); - #endif - #ifdef FLECS_EXPR - ecs_trace("FLECS_EXPR"); - #endif - #ifdef FLECS_JSON - ecs_trace("FLECS_JSON"); - #endif - #ifdef FLECS_DOC - ecs_trace("FLECS_DOC"); - #endif - #ifdef FLECS_COREDOC - ecs_trace("FLECS_COREDOC"); - #endif - #ifdef FLECS_LOG - ecs_trace("FLECS_LOG"); - #endif - #ifdef FLECS_JOURNAL - ecs_trace("FLECS_JOURNAL"); - #endif - #ifdef FLECS_APP - ecs_trace("FLECS_APP"); - #endif - #ifdef FLECS_OS_API_IMPL - ecs_trace("FLECS_OS_API_IMPL"); - #endif - #ifdef FLECS_SCRIPT - ecs_trace("FLECS_SCRIPT"); - #endif - #ifdef FLECS_HTTP - ecs_trace("FLECS_HTTP"); - #endif - #ifdef FLECS_REST - ecs_trace("FLECS_REST"); - #endif + + const char **addon = bi->addons; + do { + ecs_trace(addon[0]); + } while ((++ addon)[0]); ecs_log_pop(); + + if (bi->sanitize) { + ecs_trace("sanitize build, rebuild without FLECS_SANITIZE for (much) " + "improved performance"); + } else if (bi->debug) { + ecs_trace("debug build, rebuild with NDEBUG or FLECS_NDEBUG for " + "improved performance"); + } else { + ecs_trace("#[green]release#[reset] build"); + } + + ecs_trace("compiled with %s", bi->compiler); } /* -- Public functions -- */ +const ecs_build_info_t* ecs_get_build_info(void) { + return &flecs_build_info; +} + +const ecs_world_info_t* ecs_get_world_info( + const ecs_world_t *world) +{ + world = ecs_get_world(world); + return &world->info; +} + ecs_world_t *ecs_mini(void) { #ifdef FLECS_OS_API_IMPL ecs_set_os_api_impl(); @@ -871,31 +953,11 @@ ecs_world_t *ecs_mini(void) { ecs_trace("time management not available"); } - flecs_log_addons(); - -#ifdef FLECS_SANITIZE - ecs_trace("sanitize build, rebuild without FLECS_SANITIZE for (much) " - "improved performance"); -#elif defined(FLECS_DEBUG) - ecs_trace("debug build, rebuild with NDEBUG or FLECS_NDEBUG for improved " - "performance"); -#else - ecs_trace("#[green]release#[reset] build"); -#endif - -#ifdef __clang__ - ecs_trace("compiled with clang %s", __clang_version__); -#elif defined(__GNUC__) - ecs_trace("compiled with gcc %d.%d", __GNUC__, __GNUC_MINOR__); -#elif defined (_MSC_VER) - ecs_trace("compiled with msvc %d", _MSC_VER); -#elif defined (__TINYC__) - ecs_trace("compiled with tcc %d", __TINYC__); -#endif + flecs_log_build_info(); ecs_world_t *world = ecs_os_calloc_t(ecs_world_t); ecs_assert(world != NULL, ECS_OUT_OF_MEMORY, NULL); - ecs_poly_init(world, ecs_world_t); + flecs_poly_init(world, ecs_world_t); world->flags |= EcsWorldInit; @@ -908,10 +970,9 @@ ecs_world_t *ecs_mini(void) { ecs_map_init_w_params(&world->id_index_hi, &world->allocators.ptr); world->id_index_lo = ecs_os_calloc_n(ecs_id_record_t, FLECS_HI_ID_RECORD_ID); flecs_observable_init(&world->observable); - world->iterable.init = flecs_world_iter_init; world->pending_tables = ecs_os_calloc_t(ecs_sparse_t); - flecs_sparse_init_t(world->pending_tables, a, + flecs_sparse_init_t(world->pending_tables, a, &world->allocators.sparse_chunk, ecs_table_t*); world->pending_buffer = ecs_os_calloc_t(ecs_sparse_t); flecs_sparse_init_t(world->pending_buffer, a, @@ -963,9 +1024,6 @@ ecs_world_t *ecs_init(void) { #ifdef FLECS_DOC ECS_IMPORT(world, FlecsDoc); #endif -#ifdef FLECS_COREDOC - ECS_IMPORT(world, FlecsCoreDoc); -#endif #ifdef FLECS_SCRIPT ECS_IMPORT(world, FlecsScript); #endif @@ -1032,7 +1090,7 @@ void flecs_notify_tables( ecs_id_t id, ecs_table_event_t *event) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); /* If no id is specified, broadcast to all tables */ if (!id) { @@ -1040,7 +1098,7 @@ void flecs_notify_tables( int32_t i, count = flecs_sparse_count(tables); for (i = 0; i < count; i ++) { ecs_table_t *table = flecs_sparse_get_dense_t(tables, ecs_table_t, i); - flecs_table_notify(world, table, event); + flecs_table_notify(world, table, id, event); } /* If id is specified, only broadcast to tables with id */ @@ -1055,12 +1113,12 @@ void flecs_notify_tables( flecs_table_cache_all_iter(&idr->cache, &it); while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - flecs_table_notify(world, tr->hdr.table, event); + flecs_table_notify(world, tr->hdr.table, id, event); } } } -void ecs_default_ctor( +void flecs_default_ctor( void *ptr, int32_t count, const ecs_type_info_t *ti) @@ -1147,15 +1205,15 @@ void ecs_set_hooks_id( flecs_stage_from_world(&world); /* Ensure that no tables have yet been created for the component */ - ecs_assert( ecs_id_in_use(world, component) == false, + ecs_assert( ecs_id_in_use(world, component) == false, ECS_ALREADY_IN_USE, ecs_get_name(world, component)); - ecs_assert( ecs_id_in_use(world, ecs_pair(component, EcsWildcard)) == false, + ecs_assert( ecs_id_in_use(world, ecs_pair(component, EcsWildcard)) == false, ECS_ALREADY_IN_USE, ecs_get_name(world, component)); ecs_type_info_t *ti = flecs_type_info_ensure(world, component); ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_check(!ti->component || ti->component == component, + ecs_check(!ti->component || ti->component == component, ECS_INCONSISTENT_COMPONENT_ACTION, NULL); if (!ti->size) { @@ -1163,9 +1221,10 @@ void ecs_set_hooks_id( world, component, EcsComponent); /* Cannot register lifecycle actions for things that aren't a component */ - ecs_check(component_ptr != NULL, ECS_INVALID_PARAMETER, NULL); - /* Cannot register lifecycle actions for components with size 0 */ - ecs_check(component_ptr->size != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(component_ptr != NULL, ECS_INVALID_PARAMETER, + "provided entity is not a component"); + ecs_check(component_ptr->size != 0, ECS_INVALID_PARAMETER, + "cannot register type hooks for type with size 0"); ti->size = component_ptr->size; ti->alignment = component_ptr->alignment; @@ -1186,15 +1245,17 @@ void ecs_set_hooks_id( if (h->ctx) ti->hooks.ctx = h->ctx; if (h->binding_ctx) ti->hooks.binding_ctx = h->binding_ctx; + if (h->lifecycle_ctx) ti->hooks.lifecycle_ctx = h->lifecycle_ctx; if (h->ctx_free) ti->hooks.ctx_free = h->ctx_free; if (h->binding_ctx_free) ti->hooks.binding_ctx_free = h->binding_ctx_free; + if (h->lifecycle_ctx_free) ti->hooks.lifecycle_ctx_free = h->lifecycle_ctx_free; - /* If no constructor is set, invoking any of the other lifecycle actions - * is not safe as they will potentially access uninitialized memory. For - * ease of use, if no constructor is specified, set a default one that + /* If no constructor is set, invoking any of the other lifecycle actions + * is not safe as they will potentially access uninitialized memory. For + * ease of use, if no constructor is specified, set a default one that * initializes the component to 0. */ if (!h->ctor && (h->dtor || h->copy || h->move)) { - ti->hooks.ctor = ecs_default_ctor; + ti->hooks.ctor = flecs_default_ctor; } /* Set default copy ctor, move ctor and merge */ @@ -1210,7 +1271,7 @@ void ecs_set_hooks_id( if (h->move) { if (h->dtor) { if (h->move_ctor) { - /* If an explicit move ctor has been set, use callback + /* If an explicit move ctor has been set, use callback * that uses the move ctor vs. using a ctor+move */ ti->hooks.ctor_move_dtor = flecs_default_move_ctor_w_dtor; } else { @@ -1221,7 +1282,7 @@ void ecs_set_hooks_id( } else { /* If no dtor has been set, this is just a move ctor */ ti->hooks.ctor_move_dtor = ti->hooks.move_ctor; - } + } } else { /* If move is not set but move_ctor and dtor is, we can still set * ctor_move_dtor. */ @@ -1254,7 +1315,7 @@ void ecs_set_hooks_id( } const ecs_type_hooks_t* ecs_get_hooks_id( - ecs_world_t *world, + const ecs_world_t *world, ecs_entity_t id) { const ecs_type_info_t *ti = ecs_get_type_info(world, id); @@ -1269,7 +1330,7 @@ void ecs_atfini( ecs_fini_action_t action, void *ctx) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(action != NULL, ECS_INVALID_PARAMETER, NULL); ecs_action_elem_t *elem = ecs_vec_append_t(NULL, &world->fini_actions, @@ -1289,14 +1350,17 @@ void ecs_run_post_frame( { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(action != NULL, ECS_INVALID_PARAMETER, NULL); - + ecs_stage_t *stage = flecs_stage_from_world(&world); - ecs_action_elem_t *elem = ecs_vec_append_t(&stage->allocator, + ecs_check((world->flags & EcsWorldFrameInProgress), ECS_INVALID_OPERATION, + "cannot register post frame action while frame is not in progress"); + + ecs_action_elem_t *elem = ecs_vec_append_t(&stage->allocator, &stage->post_frame_actions, ecs_action_elem_t); ecs_assert(elem != NULL, ECS_INTERNAL_ERROR, NULL); elem->action = action; - elem->ctx = ctx; + elem->ctx = ctx; error: return; } @@ -1337,7 +1401,7 @@ void flecs_fini_type_info( int32_t i, count = flecs_sparse_count(&world->type_info); ecs_sparse_t *type_info = &world->type_info; for (i = 0; i < count; i ++) { - ecs_type_info_t *ti = flecs_sparse_get_dense_t(type_info, + ecs_type_info_t *ti = flecs_sparse_get_dense_t(type_info, ecs_type_info_t, i); flecs_type_info_fini(ti); } @@ -1363,10 +1427,12 @@ ecs_entity_t flecs_get_oneof( int ecs_fini( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); - ecs_assert(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, NULL); - ecs_assert(world->stages[0].defer == 0, ECS_INVALID_OPERATION, + flecs_poly_assert(world, ecs_world_t); + ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot fini world while it is in readonly mode"); + ecs_assert(!(world->flags & EcsWorldFini), ECS_INVALID_OPERATION, + "cannot fini world when it is already being deleted"); + ecs_assert(world->stages[0]->defer == 0, ECS_INVALID_OPERATION, "call defer_end before destroying world"); ecs_trace("#[bold]shutting down world"); @@ -1393,11 +1459,11 @@ int ecs_fini( ecs_dbg_1("#[bold]cleanup remaining entities"); ecs_log_push_1(); - /* Operations invoked during UnSet/OnRemove/destructors are deferred and + /* Operations invoked during OnRemove/destructors are deferred and * will be discarded after world cleanup */ - flecs_defer_begin(world, &world->stages[0]); + flecs_defer_begin(world, world->stages[0]); - /* Run UnSet/OnRemove actions for components while the store is still + /* Run OnRemove actions for components while the store is still * unmodified by cleanup. */ flecs_fini_unset_tables(world); @@ -1406,11 +1472,11 @@ int ecs_fini( /* Purge deferred operations from the queue. This discards operations but * makes sure that any resources in the queue are freed */ - flecs_defer_purge(world, &world->stages[0]); + flecs_defer_purge(world, world->stages[0]); ecs_log_pop_1(); /* All queries are cleaned up, so monitors should've been cleaned up too */ - ecs_assert(!ecs_map_is_init(&world->monitors.monitors), + ecs_assert(!ecs_map_is_init(&world->monitors.monitors), ECS_INTERNAL_ERROR, NULL); /* Cleanup world ctx and binding_ctx */ @@ -1423,7 +1489,7 @@ int ecs_fini( /* After this point no more user code is invoked */ - ecs_dbg_1("#[bold]cleanup world datastructures"); + ecs_dbg_1("#[bold]cleanup world data structures"); ecs_log_push_1(); flecs_entities_fini(world); flecs_sparse_fini(world->pending_tables); @@ -1441,7 +1507,7 @@ int ecs_fini( flecs_world_allocators_fini(world); /* End of the world */ - ecs_poly_free(world, ecs_world_t); + flecs_poly_free(world, ecs_world_t); ecs_os_fini(); ecs_trace("world destroyed, bye!"); @@ -1462,14 +1528,14 @@ void ecs_dim( ecs_world_t *world, int32_t entity_count) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); flecs_entities_set_size(world, entity_count + FLECS_HI_COMPONENT_ID); } void flecs_eval_component_monitors( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); flecs_process_pending_tables(world); flecs_eval_component_monitor(world); } @@ -1478,7 +1544,7 @@ void ecs_measure_frame_time( ecs_world_t *world, bool enable) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(ecs_os_has_time(), ECS_MISSING_OS_API, NULL); if (ECS_EQZERO(world->info.target_fps) || enable) { @@ -1492,7 +1558,7 @@ void ecs_measure_system_time( ecs_world_t *world, bool enable) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(ecs_os_has_time(), ECS_MISSING_OS_API, NULL); ECS_BIT_COND(world->flags, EcsWorldMeasureSystemTime, enable); error: @@ -1503,7 +1569,7 @@ void ecs_set_target_fps( ecs_world_t *world, ecs_ftime_t fps) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(ecs_os_has_time(), ECS_MISSING_OS_API, NULL); ecs_measure_frame_time(world, true); @@ -1512,10 +1578,19 @@ void ecs_set_target_fps( return; } +void ecs_set_default_query_flags( + ecs_world_t *world, + ecs_flags32_t flags) +{ + flecs_poly_assert(world, ecs_world_t); + flecs_process_pending_tables(world); + world->default_query_flags = flags; +} + void* ecs_get_ctx( const ecs_world_t *world) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); world = ecs_get_world(world); return world->ctx; error: @@ -1525,7 +1600,7 @@ void* ecs_get_ctx( void* ecs_get_binding_ctx( const ecs_world_t *world) { - ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); world = ecs_get_world(world); return world->binding_ctx; error: @@ -1537,7 +1612,7 @@ void ecs_set_ctx( void *ctx, ecs_ctx_free_t ctx_free) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); world->ctx = ctx; world->ctx_free = ctx_free; } @@ -1547,7 +1622,7 @@ void ecs_set_binding_ctx( void *ctx, ecs_ctx_free_t ctx_free) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); world->binding_ctx = ctx; world->binding_ctx_free = ctx_free; } @@ -1557,17 +1632,17 @@ void ecs_set_entity_range( ecs_entity_t id_start, ecs_entity_t id_end) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_check(!id_end || id_end > id_start, ECS_INVALID_PARAMETER, NULL); - ecs_check(!id_end || id_end > flecs_entities_max_id(world), - ECS_INVALID_PARAMETER, NULL); + + if (id_start == 0) { + id_start = flecs_entities_max_id(world) + 1; + } uint32_t start = (uint32_t)id_start; uint32_t end = (uint32_t)id_end; - if (flecs_entities_max_id(world) < start) { - flecs_entities_max_id(world) = start - 1; - } + flecs_entities_max_id(world) = start - 1; world->info.min_id = start; world->info.max_id = end; @@ -1579,7 +1654,7 @@ bool ecs_enable_range_check( ecs_world_t *world, bool enable) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); bool old_value = world->range_check_enabled; world->range_check_enabled = enable; return old_value; @@ -1595,29 +1670,11 @@ ecs_entity_t ecs_get_max_id( return 0; } -void ecs_set_entity_generation( - ecs_world_t *world, - ecs_entity_t entity_with_generation) -{ - ecs_poly_assert(world, ecs_world_t); - ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); - ecs_assert(!(ecs_is_deferred(world)), ECS_INVALID_OPERATION, NULL); - - flecs_entities_set_generation(world, entity_with_generation); - - ecs_record_t *r = flecs_entities_get(world, entity_with_generation); - if (r && r->table) { - int32_t row = ECS_RECORD_TO_ROW(r->row); - ecs_entity_t *entities = r->table->data.entities.array; - entities[row] = entity_with_generation; - } -} - const ecs_type_info_t* flecs_type_info_get( const ecs_world_t *world, ecs_entity_t component) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(component != 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(!(component & ECS_ID_FLAGS_MASK), ECS_INTERNAL_ERROR, NULL); @@ -1629,7 +1686,7 @@ ecs_type_info_t* flecs_type_info_ensure( ecs_world_t *world, ecs_entity_t component) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(component != 0, ECS_INTERNAL_ERROR, NULL); const ecs_type_info_t *ti = flecs_type_info_get(world, component); @@ -1671,7 +1728,7 @@ bool flecs_type_info_init_id( ecs_type_info_t *ti = NULL; if (!size || !alignment) { - ecs_assert(size == 0 && alignment == 0, + ecs_assert(size == 0 && alignment == 0, ECS_INVALID_COMPONENT_SIZE, NULL); ecs_assert(li == NULL, ECS_INCONSISTENT_COMPONENT_ACTION, NULL); flecs_sparse_remove_t(&world->type_info, ecs_type_info_t, component); @@ -1699,7 +1756,7 @@ bool flecs_type_info_init_id( changed |= flecs_id_record_set_type_info(world, idr, NULL); } else if (ti) { changed |= flecs_id_record_set_type_info(world, idr, ti); - } else if ((idr->type_info != NULL) && + } else if ((idr->type_info != NULL) && (idr->type_info->component == component)) { changed |= flecs_id_record_set_type_info(world, idr, NULL); @@ -1731,11 +1788,17 @@ void flecs_type_info_fini( if (ti->hooks.binding_ctx_free) { ti->hooks.binding_ctx_free(ti->hooks.binding_ctx); } + if (ti->hooks.lifecycle_ctx_free) { + ti->hooks.lifecycle_ctx_free(ti->hooks.lifecycle_ctx); + } if (ti->name) { /* Safe to cast away const, world has ownership over string */ ecs_os_free(ECS_CONST_CAST(char*, ti->name)); ti->name = NULL; } + + ti->size = 0; + ti->alignment = 0; } void flecs_type_info_free( @@ -1750,7 +1813,7 @@ void flecs_type_info_free( return; } - ecs_type_info_t *ti = flecs_sparse_try_t(&world->type_info, + ecs_type_info_t *ti = flecs_sparse_try_t(&world->type_info, ecs_type_info_t, component); if (ti) { flecs_type_info_fini(ti); @@ -1763,7 +1826,7 @@ ecs_ftime_t flecs_insert_sleep( ecs_world_t *world, ecs_time_t *stop) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_time_t start = *stop, now = start; ecs_ftime_t delta_time = (ecs_ftime_t)ecs_time_measure(stop); @@ -1772,7 +1835,9 @@ ecs_ftime_t flecs_insert_sleep( return delta_time; } - ecs_ftime_t target_delta_time = + ecs_os_perf_trace_push("flecs.insert_sleep"); + + ecs_ftime_t target_delta_time = ((ecs_ftime_t)1.0 / (ecs_ftime_t)world->info.target_fps); /* Calculate the time we need to sleep by taking the measured delta from the @@ -1792,9 +1857,11 @@ ecs_ftime_t flecs_insert_sleep( now = start; delta_time = (ecs_ftime_t)ecs_time_measure(&now); - } while ((target_delta_time - delta_time) > + } while ((target_delta_time - delta_time) > (sleep_time / (ecs_ftime_t)2.0)); + ecs_os_perf_trace_pop("flecs.insert_sleep"); + *stop = now; return delta_time; } @@ -1804,16 +1871,16 @@ ecs_ftime_t flecs_start_measure_frame( ecs_world_t *world, ecs_ftime_t user_delta_time) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_ftime_t delta_time = 0; - if ((world->flags & EcsWorldMeasureFrameTime) || - (ECS_EQZERO(user_delta_time))) + if ((world->flags & EcsWorldMeasureFrameTime) || + (ECS_EQZERO(user_delta_time))) { ecs_time_t t = world->frame_start_time; do { - if (world->frame_start_time.nanosec || world->frame_start_time.sec){ + if (world->frame_start_time.nanosec || world->frame_start_time.sec){ delta_time = flecs_insert_sleep(world, &t); } else { ecs_time_measure(&t); @@ -1821,17 +1888,22 @@ ecs_ftime_t flecs_start_measure_frame( delta_time = (ecs_ftime_t)1.0 / world->info.target_fps; } else { /* Best guess */ - delta_time = (ecs_ftime_t)1.0 / (ecs_ftime_t)60.0; + delta_time = (ecs_ftime_t)1.0 / (ecs_ftime_t)60.0; + + if (ECS_EQZERO(delta_time)) { + delta_time = user_delta_time; + break; + } } } - + /* Keep trying while delta_time is zero */ } while (ECS_EQZERO(delta_time)); - world->frame_start_time = t; + world->frame_start_time = t; /* Keep track of total time passed in world */ - world->info.world_time_total_raw += (ecs_ftime_t)delta_time; + world->info.world_time_total_raw += (double)delta_time; } return (ecs_ftime_t)delta_time; @@ -1841,7 +1913,7 @@ static void flecs_stop_measure_frame( ecs_world_t* world) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); if (world->flags & EcsWorldMeasureFrameTime) { ecs_time_t t = world->frame_start_time; @@ -1853,8 +1925,11 @@ ecs_ftime_t ecs_frame_begin( ecs_world_t *world, ecs_ftime_t user_delta_time) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); + flecs_poly_assert(world, ecs_world_t); + ecs_check(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot begin frame while world is in readonly mode"); + ecs_check(!(world->flags & EcsWorldFrameInProgress), ECS_INVALID_OPERATION, + "cannot begin frame while frame is already in progress"); ecs_check(ECS_NEQZERO(user_delta_time) || ecs_os_has_time(), ECS_MISSING_OS_API, "get_time"); @@ -1862,16 +1937,25 @@ ecs_ftime_t ecs_frame_begin( ecs_ftime_t delta_time = flecs_start_measure_frame(world, user_delta_time); if (ECS_EQZERO(user_delta_time)) { user_delta_time = delta_time; - } + } world->info.delta_time_raw = user_delta_time; world->info.delta_time = user_delta_time * world->info.time_scale; /* Keep track of total scaled time passed in world */ - world->info.world_time_total += world->info.delta_time; + world->info.world_time_total += (double)world->info.delta_time; + + /* Command buffer capturing */ + world->on_commands_active = world->on_commands; + world->on_commands = NULL; + + world->on_commands_ctx_active = world->on_commands_ctx; + world->on_commands_ctx = NULL; ecs_run_aperiodic(world, 0); + world->flags |= EcsWorldFrameInProgress; + return world->info.delta_time; error: return (ecs_ftime_t)0; @@ -1880,44 +1964,46 @@ ecs_ftime_t ecs_frame_begin( void ecs_frame_end( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); - ecs_check(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, NULL); + flecs_poly_assert(world, ecs_world_t); + ecs_check(!(world->flags & EcsWorldReadonly), ECS_INVALID_OPERATION, + "cannot end frame while world is in readonly mode"); + ecs_check((world->flags & EcsWorldFrameInProgress), ECS_INVALID_OPERATION, + "cannot end frame while frame is not in progress"); world->info.frame_count_total ++; - ecs_stage_t *stages = world->stages; int32_t i, count = world->stage_count; for (i = 0; i < count; i ++) { - flecs_stage_merge_post_frame(world, &stages[i]); + flecs_stage_merge_post_frame(world, world->stages[i]); } flecs_stop_measure_frame(world); + + /* Reset command handler each frame */ + world->on_commands_active = NULL; + world->on_commands_ctx_active = NULL; + + world->flags &= ~EcsWorldFrameInProgress; + error: return; } -const ecs_world_info_t* ecs_get_world_info( - const ecs_world_t *world) -{ - world = ecs_get_world(world); - return &world->info; -} - void flecs_delete_table( ecs_world_t *world, ecs_table_t *table) { - ecs_poly_assert(world, ecs_world_t); - flecs_table_free(world, table); + flecs_poly_assert(world, ecs_world_t); + flecs_table_fini(world, table); } static void flecs_process_empty_queries( ecs_world_t *world) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); - ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair(ecs_id(EcsPoly), EcsQuery)); if (!idr) { return; @@ -1927,7 +2013,7 @@ void flecs_process_empty_queries( /* Make sure that we defer adding the inactive tags until after iterating * the query */ - flecs_defer_begin(world, &world->stages[0]); + flecs_defer_begin(world, world->stages[0]); ecs_table_cache_iter_t it; const ecs_table_record_t *tr; @@ -1939,22 +2025,22 @@ void flecs_process_empty_queries( for (i = 0; i < count; i ++) { ecs_query_t *query = queries[i].poly; - ecs_entity_t *entities = table->data.entities.array; - if (!ecs_query_table_count(query)) { + const ecs_entity_t *entities = ecs_table_entities(table); + if (!ecs_query_is_true(query)) { ecs_add_id(world, entities[i], EcsEmpty); } } } } - flecs_defer_end(world, &world->stages[0]); + flecs_defer_end(world, world->stages[0]); } /** Walk over tables that had a state change which requires bookkeeping */ void flecs_process_pending_tables( const ecs_world_t *world_r) { - ecs_poly_assert(world_r, ecs_world_t); + flecs_poly_assert(world_r, ecs_world_t); /* We can't update the administration while in readonly mode, but we can * ensure that when this function is called there are no pending events. */ @@ -1966,10 +2052,10 @@ void flecs_process_pending_tables( /* Safe to cast, world is not readonly */ ecs_world_t *world = ECS_CONST_CAST(ecs_world_t*, world_r); - + /* If pending buffer is NULL there already is a stackframe that's iterating * the table list. This can happen when an observer for a table event results - * in a mutation that causes another table to change state. A typical + * in a mutation that causes another table to change state. A typical * example of this is a system that becomes active/inactive as the result of * a query (and as a result, its matched tables) becoming empty/non empty */ if (!world->pending_buffer) { @@ -1980,13 +2066,15 @@ void flecs_process_pending_tables( * single sparse set, but that would've complicated (and slowed down) the * iteration. Additionally, by using a double buffer approach we can still * keep most of the original ordering of events intact, which is desirable - * as it means that the ordering of tables in the internal datastructures is + * as it means that the ordering of tables in the internal data structures is * more predictable. */ int32_t i, count = flecs_sparse_count(world->pending_tables); if (!count) { return; } + ecs_os_perf_trace_push("flecs.process_pending_tables"); + flecs_journal_begin(world, EcsJournalTableEvents, 0, 0, 0); do { @@ -1995,9 +2083,9 @@ void flecs_process_pending_tables( world->pending_buffer = NULL; /* Make sure that any ECS operations that occur while delivering the - * events does not cause inconsistencies, like sending an Empty + * events does not cause inconsistencies, like sending an Empty * notification for a table that just became non-empty. */ - flecs_defer_begin(world, &world->stages[0]); + flecs_defer_begin(world, world->stages[0]); for (i = 0; i < count; i ++) { ecs_table_t *table = flecs_sparse_get_dense_t( @@ -2012,29 +2100,23 @@ void flecs_process_pending_tables( if (flecs_table_records_update_empty(table)) { int32_t table_count = ecs_table_count(table); if (table->flags & (EcsTableHasOnTableFill|EcsTableHasOnTableEmpty)) { - /* Only emit an event when there was a change in the + /* Only emit an event when there was a change in the * administration. It is possible that a table ended up in the * pending_tables list by going from empty->non-empty, but then * became empty again. By the time we run this code, no changes * in the administration would actually be made. */ ecs_entity_t evt = table_count ? EcsOnTableFill : EcsOnTableEmpty; if (ecs_should_log_3()) { - ecs_dbg_3("table %u state change (%s)", + ecs_dbg_3("table %u state change (%s)", (uint32_t)table->id, table_count ? "non-empty" : "empty"); } ecs_log_push_3(); - flecs_emit(world, world, &(ecs_event_desc_t){ - .event = evt, - .table = table, - .ids = &table->type, - .observable = world, - .flags = EcsEventTableOnly - }); + flecs_table_emit(world, table, evt); - ecs_log_pop_3(); + ecs_log_pop_3(); } world->info.empty_table_count += (table_count == 0) * 2 - 1; } @@ -2047,13 +2129,15 @@ void flecs_process_pending_tables( } while ((count = flecs_sparse_count(world->pending_tables))); flecs_journal_end(); + + ecs_os_perf_trace_pop("flecs.process_pending_tables"); } void flecs_table_set_empty( ecs_world_t *world, ecs_table_t *table) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); ecs_assert(!(world->flags & EcsWorldReadonly), ECS_INTERNAL_ERROR, NULL); ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); @@ -2061,7 +2145,7 @@ void flecs_table_set_empty( table->_->generation = 0; } - flecs_sparse_ensure_fast_t(world->pending_tables, ecs_table_t*, + flecs_sparse_ensure_fast_t(world->pending_tables, ecs_table_t*, (uint32_t)table->id)[0] = table; } @@ -2081,14 +2165,16 @@ void ecs_run_aperiodic( ecs_world_t *world, ecs_flags32_t flags) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); if (!flags || (flags & EcsAperiodicEmptyTables)) { flecs_process_pending_tables(world); } + if ((flags & EcsAperiodicEmptyQueries)) { flecs_process_empty_queries(world); } + if (!flags || (flags & EcsAperiodicComponentMonitors)) { flecs_eval_component_monitors(world); } @@ -2102,13 +2188,15 @@ int32_t ecs_delete_empty_tables( int32_t min_id_count, double time_budget_seconds) { - ecs_poly_assert(world, ecs_world_t); + flecs_poly_assert(world, ecs_world_t); + + ecs_os_perf_trace_push("flecs.delete_empty_tables"); /* Make sure empty tables are in the empty table lists */ ecs_run_aperiodic(world, EcsAperiodicEmptyTables); ecs_time_t start = {0}, cur = {0}; - int32_t delete_count = 0, clear_count = 0; + int32_t delete_count = 0; bool time_budget = false; if (ECS_NEQZERO(time_budget_seconds) || (ecs_should_log_1() && ecs_os_has_time())) { @@ -2144,27 +2232,33 @@ int32_t ecs_delete_empty_tables( uint16_t gen = ++ table->_->generation; if (delete_generation && (gen > delete_generation)) { - flecs_table_free(world, table); + flecs_table_fini(world, table); delete_count ++; } else if (clear_generation && (gen > clear_generation)) { - if (flecs_table_shrink(world, table)) { - clear_count ++; - } + flecs_table_shrink(world, table); } } } done: - if (ecs_should_log_1() && ecs_os_has_time()) { - if (delete_count) { - ecs_dbg_1("#[red]deleted#[normal] %d empty tables in %.2fs", - delete_count, ecs_time_measure(&start)); - } - if (clear_count) { - ecs_dbg_1("#[red]cleared#[normal] %d empty tables in %.2fs", - clear_count, ecs_time_measure(&start)); - } - } + ecs_os_perf_trace_pop("flecs.delete_empty_tables"); return delete_count; } + +ecs_entities_t ecs_get_entities( + const ecs_world_t *world) +{ + ecs_entities_t result; + result.ids = flecs_entities_ids(world); + result.count = flecs_entities_size(world); + result.alive_count = flecs_entities_count(world); + return result; +} + +ecs_flags32_t ecs_world_get_flags( + const ecs_world_t *world) +{ + flecs_poly_assert(world, ecs_world_t); + return world->flags; +} diff --git a/vendors/flecs/src/world.h b/vendors/flecs/src/world.h index def797307..2cfa3b262 100644 --- a/vendors/flecs/src/world.h +++ b/vendors/flecs/src/world.h @@ -11,7 +11,7 @@ ecs_stage_t* flecs_stage_from_world( ecs_world_t **world_ptr); /* Get current thread-specific stage from readonly world */ -const ecs_stage_t* flecs_stage_from_readonly_world( +ecs_stage_t* flecs_stage_from_readonly_world( const ecs_world_t *world); /* Get component callbacks */ @@ -83,44 +83,17 @@ void flecs_delete_table( void flecs_process_pending_tables( const ecs_world_t *world); -/* Suspend/resume readonly state. To fully support implicit registration of - * components, it should be possible to register components while the world is - * in readonly mode. It is not uncommon that a component is used first from - * within a system, which are often ran while in readonly mode. - * - * Suspending readonly mode is only allowed when the world is not multithreaded. - * When a world is multithreaded, it is not safe to (even temporarily) leave - * readonly mode, so a multithreaded application should always explicitly - * register components in advance. - * - * These operations also suspend deferred mode. - */ -typedef struct ecs_suspend_readonly_state_t { - bool is_readonly; - bool is_deferred; - int32_t defer_count; - ecs_entity_t scope; - ecs_entity_t with; - ecs_vec_t commands; - ecs_stack_t defer_stack; - ecs_stage_t *stage; -} ecs_suspend_readonly_state_t; - -ecs_world_t* flecs_suspend_readonly( - const ecs_world_t *world, - ecs_suspend_readonly_state_t *state); - -void flecs_resume_readonly( - ecs_world_t *world, - ecs_suspend_readonly_state_t *state); - /* Convenience macro's for world allocator */ #define flecs_walloc(world, size)\ flecs_alloc(&world->allocator, size) +#define flecs_walloc_t(world, T)\ + flecs_alloc_t(&world->allocator, T) #define flecs_walloc_n(world, T, count)\ flecs_alloc_n(&world->allocator, T, count) #define flecs_wcalloc(world, size)\ flecs_calloc(&world->allocator, size) +#define flecs_wfree_t(world, T, ptr)\ + flecs_free_t(&world->allocator, T, ptr) #define flecs_wcalloc_n(world, T, count)\ flecs_calloc_n(&world->allocator, T, count) #define flecs_wfree(world, size, ptr)\ diff --git a/vendors/flecs/test/.gitignore b/vendors/flecs/test/.gitignore deleted file mode 100644 index 4cfc12ad8..000000000 --- a/vendors/flecs/test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -bake/ diff --git a/vendors/flecs/test/BUILD.bazel b/vendors/flecs/test/BUILD similarity index 84% rename from vendors/flecs/test/BUILD.bazel rename to vendors/flecs/test/BUILD index dadce385b..8cafe0818 100644 --- a/vendors/flecs/test/BUILD.bazel +++ b/vendors/flecs/test/BUILD @@ -21,11 +21,11 @@ cc_test( ) cc_test( - name = "cpp_api", + name = "cpp", deps = ["//:flecs", "@bake//:driver-test"], - srcs = glob(["cpp_api/src/*.cpp", "cpp_api/**/*.h"]), - includes = ["cpp_api/include"], + srcs = glob(["cpp/src/*.cpp", "cpp/**/*.h"]), + includes = ["cpp/include"], timeout = "short", ) diff --git a/vendors/flecs/test/CMakeLists.txt b/vendors/flecs/test/CMakeLists.txt index 9e1534e7b..b487801fc 100644 --- a/vendors/flecs/test/CMakeLists.txt +++ b/vendors/flecs/test/CMakeLists.txt @@ -83,7 +83,9 @@ if(UNIX AND NOT APPLE) target_link_libraries(bake_base PUBLIC m) endif() +add_flecs_test("${CMAKE_CURRENT_LIST_DIR}/core") add_flecs_test("${CMAKE_CURRENT_LIST_DIR}/addons") -add_flecs_test("${CMAKE_CURRENT_LIST_DIR}/api") +add_flecs_test("${CMAKE_CURRENT_LIST_DIR}/query") +add_flecs_test("${CMAKE_CURRENT_LIST_DIR}/meta") add_flecs_test("${CMAKE_CURRENT_LIST_DIR}/collections") -add_flecs_test("${CMAKE_CURRENT_LIST_DIR}/cpp_api") +add_flecs_test("${CMAKE_CURRENT_LIST_DIR}/cpp") diff --git a/vendors/flecs/test/addons/include/addons.h b/vendors/flecs/test/addons/include/addons.h index e6fbcb805..7a4754ed9 100644 --- a/vendors/flecs/test/addons/include/addons.h +++ b/vendors/flecs/test/addons/include/addons.h @@ -75,35 +75,6 @@ void probe_has_entity(Probe *probe, ecs_entity_t e); void install_test_abort(void); -#define ITER_MAX_ENTITIES (64) -#define ITER_MAX_TERMS (16) -#define ITER_MAX_VARIABLES (16) - -typedef struct test_iter_result_t { - ecs_entity_t entities[ITER_MAX_ENTITIES]; - ecs_id_t term_ids[ITER_MAX_ENTITIES][ITER_MAX_TERMS]; - void *term_columns[ITER_MAX_TERMS]; - - int32_t table_count_expect; - int32_t table_count_actual; - - char *entity_names[ITER_MAX_ENTITIES]; - char *term_ids_expr[ITER_MAX_ENTITIES][ITER_MAX_TERMS]; - int32_t matched[ITER_MAX_ENTITIES]; - - struct { - int32_t id; - ecs_entity_t entities[ITER_MAX_ENTITIES]; - char *entity_names[ITER_MAX_ENTITIES]; - } variables[ITER_MAX_VARIABLES]; -} test_iter_result_t; - -// Utility for doing order-independent validation of iterator output -bool test_iter( - ecs_iter_t *it, - ecs_iter_next_action_t next, - test_iter_result_t *expect); - const ecs_entity_t* bulk_new_w_type( ecs_world_t *world, ecs_entity_t type_ent, int32_t count); diff --git a/vendors/flecs/test/addons/project.json b/vendors/flecs/test/addons/project.json index c4860aeb7..3fa3b471e 100644 --- a/vendors/flecs/test/addons/project.json +++ b/vendors/flecs/test/addons/project.json @@ -5,493 +5,12 @@ "author": "Sander Mertens", "description": "Test project for flecs addons", "public": false, - "coverage": false, "use": [ "flecs" ] }, "test": { "testsuites": [{ - "id": "Parser", - "testcases": [ - "resolve_this", - "resolve_wildcard", - "resolve_any", - "resolve_is_a", - "0", - "component_implicit_subject", - "component_explicit_subject", - "component_explicit_subject_this", - "component_explicit_subject_this_by_name", - "component_explicit_subject_this_by_var_name", - "component_explicit_subject_wildcard", - "component_explicit_subject_any", - "component_explicit_subject_0", - "this_as_predicate", - "this_var_as_predicate", - "this_lowercase_var_as_predicate", - "this_as_object", - "this_var_as_object", - "pair_implicit_subject", - "pair_implicit_subject_wildcard_pred", - "pair_implicit_subject_wildcard_obj", - "pair_implicit_subject_any_pred", - "pair_implicit_subject_any_obj", - "pair_implicit_subject_this_pred", - "pair_implicit_subject_this_obj", - "pair_implicit_subject_pred_w_self", - "pair_implicit_subject_obj_w_self", - "pair_implicit_subject_pred_w_up", - "pair_implicit_subject_obj_w_up", - "pair_implicit_subject_pred_w_self_up", - "pair_implicit_subject_obj_w_self_up", - "pair_implicit_subject_pred_w_up_trav", - "pair_implicit_subject_obj_w_up_trav", - "pair_implicit_subject_pred_w_invalid_flags", - "pair_implicit_subject_obj_w_invalid_flags", - "pair_explicit_subject", - "pair_explicit_subject_this", - "pair_explicit_subject_this_by_name", - "pair_explicit_subject_this_by_var_name", - "pair_explicit_subject_wildcard_pred", - "pair_explicit_subject_wildcard_subj", - "pair_explicit_subject_wildcard_obj", - "pair_implicit_subject_0_object", - "pair_explicit_subject_0_object", - "pair_explicit_subject_0", - "in_component_implicit_subject", - "in_component_explicit_subject", - "in_pair_implicit_subject", - "in_pair_explicit_subject", - "inout_component_implicit_subject", - "inout_component_explicit_subject", - "inout_pair_implicit_subject", - "inout_pair_explicit_subject", - "out_component_implicit_subject", - "out_component_explicit_subject", - "out_pair_implicit_subject", - "out_pair_explicit_subject", - "inout_filter_component", - "component_singleton", - "this_singleton", - "component_implicit_no_subject", - "component_explicit_no_subject", - "pair_no_subject", - "variable_single_char", - "variable_multi_char", - "variable_multi_char_w_underscore", - "variable_multi_char_w_number", - "variable_multi_char_not_allcaps", - "pred_var", - "obj_var", - "component_not", - "pair_implicit_subject_not", - "pair_explicit_subject_not", - "2_component_not", - "2_component_not_no_space", - "component_optional", - "2_component_optional", - "2_component_optional_no_space", - "from_and", - "from_or", - "from_not", - "pair_implicit_subject_optional", - "pair_explicit_subject_optional", - "pred_implicit_subject_w_role", - "pred_explicit_subject_w_role", - "pred_no_subject_w_role", - "pair_implicit_subject_w_role", - "pair_explicit_subject_w_role", - "inout_role_pred_implicit_subject", - "inout_role_pred_no_subject", - "inout_role_pred_explicit_subject", - "inout_role_pair_implicit_subject", - "inout_role_pair_explicit_subject", - "2_pred_implicit_subject", - "2_pred_no_subject", - "2_pred_explicit_subject", - "2_pair_implicit_subject", - "2_pair_explicit_subject", - "2_pred_role", - "2_pair_implicit_subj_role", - "2_pair_explicit_subj_role", - "2_or_pred_implicit_subj", - "2_or_pred_explicit_subj", - "2_or_pair_implicit_subj", - "2_or_pair_explicit_subj", - "2_or_pred_inout", - "1_digit_pred_implicit_subj", - "1_digit_pred_no_subj", - "1_digit_pred_explicit_subj", - "1_digit_pair_implicit_subj", - "1_digit_pair_explicit_subj", - "pred_implicit_subject_self", - "pred_implicit_subject_superset", - "pred_implicit_subject_subset", - "pred_implicit_subject_superset_inclusive", - "pred_implicit_subject_subset_inclusive", - "pred_implicit_subject_superset_cascade", - "pred_implicit_subject_subset_cascade", - "pred_implicit_subject_superset_inclusive_cascade", - "pred_implicit_subject_subset_inclusive_cascade", - "pred_implicit_subject_implicit_superset_cascade", - "pred_implicit_subject_implicit_superset_inclusive_cascade", - "pred_implicit_subject_implicit_superset_cascade_w_rel", - "pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel", - "pred_implicit_subject_superset_childof", - "pred_implicit_subject_cascade_superset_childof", - "pred_implicit_subject_superset_cascade_childof", - "pred_implicit_subject_superset_cascade_childof_optional", - "expr_w_symbol", - "expr_w_newline", - "subj_entity_w_explicit_self", - "subj_entity_w_explicit_self_superset", - "subj_entity_w_explicit_superset_relation", - "subj_entity_w_explicit_self_superset_relation", - "obj_entity_w_explicit_self", - "obj_entity_w_explicit_self_superset", - "obj_entity_w_explicit_superset_relation", - "obj_entity_w_explicit_self_superset_relation", - "pred_entity_w_explicit_self", - "pred_entity_w_explicit_self_superset", - "pred_entity_w_explicit_superset_relation", - "pred_entity_w_explicit_self_superset_relation", - "pred_entity_no_args_w_explicit_self", - "pred_entity_no_args_w_explicit_self_superset", - "pred_entity_no_args_w_explicit_superset_relation", - "pred_entity_no_args_w_explicit_self_superset_relation", - "pred_entity_no_args_2_terms_w_explicit_self", - "pred_entity_no_args_2_terms_w_explicit_self_superset", - "pred_entity_no_args_2_terms_w_explicit_superset_relation", - "pred_entity_no_args_2_terms_w_explicit_self_superset_relation", - "newline", - "2_newlines", - "3_newlines", - "space", - "2_spaces", - "trailing_newline", - "2_trailing_newlines", - "trailing_space", - "2_trailing_spaces", - "template_type", - "predicate_w_parens", - "not_alive_pred", - "not_alive_subj", - "not_alive_obj", - "this_subj_var_kind", - "this_obj_var_kind", - "this_subj_obj_var_kind", - "var_w_name", - "entity_pred_no_name", - "entity_subj_no_name", - "entity_obj_no_name", - "this_pred_no_name", - "this_subj_no_name", - "this_obj_no_name", - "auto_object_variable", - "auto_object_variable_w_subj", - "auto_scoped_variable", - "invalid_variable_only", - "oneof_self_pred_w_relative_obj", - "oneof_other_pred_w_relative_obj", - "oneof_self_pred_w_invalid_obj", - "oneof_other_pred_w_invalid_obj", - "pair_implicit_src_missing_rel", - "pair_implicit_src_missing_obj", - "pair_explicit_src_missing_src", - "pair_explicit_src_missing_obj", - "eq_id", - "eq_id_var", - "eq_var_id", - "eq_var", - "neq_id", - "neq_id_var", - "neq_var_id", - "neq_var", - "eq_name", - "eq_name_var", - "eq_var_name", - "eq_var", - "neq_name", - "neq_name_var", - "neq_var_name", - "neq_var", - "match_name", - "match_name_var", - "match_var_name", - "match_var", - "nmatch_name", - "nmatch_name_var", - "nmatch_var_name", - "eq_same_var", - "neq_same_var", - "eq_same_var_this", - "neq_same_var_this", - "eq_w_optional", - "neq_w_optional", - "match_w_optional", - "query_scope_1_term", - "query_scope_1_term_spaces", - "query_scope_2_terms", - "query_nested_scope", - "query_nested_scope_spaces", - "query_scope_unbalanced", - "query_not_scope", - "query_empty_scope", - "override_tag", - "override_pair", - "pair_3_args", - "pair_3_args_implicit_this", - "pair_4_args", - "pair_4_args_implicit_this", - "pair_3_args_2_terms", - "pair_3_args_this_tgt", - "pair_3_args_2_terms_this_tgt", - "pair_3_args_2_terms_this_tgt_implicit_this", - "cascade_desc" - ] - }, { - "id": "Plecs", - "testcases": [ - "null", - "empty", - "space", - "space_newline", - "two_empty_newlines", - "three_empty_newlines", - "newline_trailing_space", - "newline_trailing_spaces", - "multiple_trailing_newlines", - "entity", - "entity_w_entity", - "entity_w_pair", - "2_entities", - "2_entities_w_entities", - "3_entities_w_pairs", - "line_comment", - "line_comment_before_stmt", - "line_comment_after_stmt", - "line_comment_between_stmt", - "multiple_line_comment", - "line_comment_after_stmt_same_line", - "comma_separated_pred", - "comma_separated_pred_w_subj", - "comma_separated_pred_w_subj_obj", - "comma_separated_pred_trailing_comma", - "comma_separated_pred_trailing_comma_newline", - "comma_separated_pred_trailing_comma_newline_multiline", - "hierarchy_1_child", - "hierarchy_2_children", - "hierarchy_1_child_same_line", - "hierarchy_2_children_same_line", - "entity_after_hierarchy", - "newline_before_scope_open", - "comment_before_scope_open", - "comment_after_newline_before_scope_open", - "hierarchy_2_levels", - "hierarchy_2_levels_2_subtrees", - "missing_end_of_scope", - "missing_end_of_predicate_scope", - "create_in_scope", - "hierarchy_w_pred_subj", - "hierarchy_custom_relation", - "hierarchy_custom_relation_2_levels", - "hierarchy_custom_relation_apply_to_object", - "hierarchy_custom_relation_apply_to_object_2_levels", - "entity_after_hierarchy_custom_relation", - "entity_after_hierarchy_custom_relation_2_levels", - "pred_scope", - "pred_scope_2_levels", - "pred_scope_inside_with", - "pred_scope_nested_w_subj_scope", - "with_tag", - "with_tag_2_entities", - "with_tag_same_line", - "with_tag_2_entities_same_line", - "with_tag_2_levels", - "with_tag_2_levels_2_subtrees", - "with_n_tags", - "with_n_tags_2_levels", - "with_after_scope", - "with_after_with", - "scope_inside_with_inside_scope", - "with_inside_scope", - "assignment_w_1", - "assignment_w_2", - "assignment_w_pair", - "assignment_w_invalid_subject", - "assignment_w_invalid_with", - "inherit_w_colon", - "inherit_w_colon_w_scope", - "inherit_w_colon_w_assign", - "assign_component_value", - "assign_2_component_values", - "assign_component_value_in_assign_scope", - "assign_2_component_values_in_assign_scope", - "type_and_assign_in_plecs", - "type_and_assign_in_plecs_w_2_members", - "type_and_assign_in_plecs_w_3_members", - "type_and_assign_in_plecs_w_enum", - "type_and_assign_in_plecs_w_enum_using_meta", - "type_and_assign_in_plecs_w_enum_primitive_using_meta", - "type_and_assign_in_plecs_w_enum_primitive_and_struct", - "type_and_assign_in_plecs_nested_member", - "dot_assign_nested_member", - "dot_assign_binary_expr", - "open_scope_no_parent", - "create_subject_in_root_scope_w_resolvable_id", - "create_subject_in_scope_w_resolvable_id", - "create_subject_in_scope_w_resolvable_id_using", - "using_scope", - "using_nested_scope", - "using_nested_in_scope", - "using_with_scope", - "using_w_entity_ref_in_value_2_members", - "using_w_entity_ref_in_value_3_members", - "2_using_scope", - "2_using_in_different_scope", - "empty_scope_after_using", - "assignment_to_non_component", - "struct_w_member_w_assignment_to_nothing", - "struct_w_member_w_assignment_to_empty_scope", - "scope_after_assign", - "assign_after_inherit", - "multiple_assignments_single_line", - "2_stmts_in_scope_w_no_parent", - "scope_after_assign_1_tag", - "scope_after_assign_2_tags", - "invalid_nested_assignment", - "invalid_partial_pair_assignment", - "empty_assignment", - "assign_tag_to_parent", - "assign_component_to_parent", - "empty_assignment_before_end_of_scope", - "assign_to_parent_pair_w_new_entities_in_scope", - "assign_to_parent_pair_w_existing_entities_in_scope", - "default_child_component", - "default_child_component_w_assign", - "struct_type_w_default_child_component", - "struct_type_w_default_child_component_nested_member", - "enum_type_w_default_child_component", - "default_type_from_with", - "scope_w_1_subj_and_2_pairs", - "inherit_from_multiple", - "assign_pair_component", - "assign_pair_component_in_scope", - "assign_pair_component_in_script", - "assign_pair_component_in_script_update", - "set_entity_names", - "oneof", - "invalid_oneof", - "brief_annotation", - "name_annotation", - "link_annotation", - "color_annotation", - "multiple_annotations", - "annotation_w_trailing_space", - "multiline_string", - "unterminated_multiline_string", - "declaration_w_underscore_name", - "annotate_declaration", - "anonymous_entity", - "anonymous_entity_in_scope", - "anonymous_declaration", - "const_var_int", - "const_var_float", - "const_var_bool", - "const_var_string", - "const_var_struct", - "const_var_redeclare", - "const_var_scoped", - "assign_component_from_var", - "assign_component_from_var_in_scope", - "scope_w_component_after_const_var", - "component_after_const_paren_expr", - "component_after_const_add_expr", - "component_after_const_sub_expr", - "component_after_const_mul_expr", - "component_after_const_div_expr", - "parse_with", - "parse_with_w_with", - "parse_with_w_tag", - "parse_with_value", - "parse_with_2_values", - "parse_with_2_nested_values", - "parse_with_var", - "parse_with_2_vars", - "parse_with_2_nested_vars", - "parse_with_var_in_scope", - "assign_const_w_expr", - "const_w_type", - "assembly_no_scope", - "assembly_empty", - "assembly_no_props", - "assembly_prop_no_type", - "assembly_prop_no_default", - "assembly_prop", - "assembly_prop_space_colon", - "assembly_2_props", - "assembly_instance_w_default_values", - "assembly_instance_w_assign_default_values", - "assembly_instance_w_overridden_values", - "assembly_w_child", - "assembly_w_child_parse_script", - "assembly_w_child_parse_script_twice", - "assembly_w_child_update_after_parse", - "assembly_w_nested_child", - "assembly_w_prefab", - "assembly_w_prefab_tree", - "assembly_w_nested_assembly", - "instantiate_prefab_w_assembly", - "assembly_w_prefab_w_assembly", - "3_assemblies", - "assembly_nested_w_default_var", - "assembly_w_anonymous", - "assembly_w_anonymous_parse_again", - "typed_const_w_composite_type_invalid_assignment", - "typed_const_w_composite_type", - "assign_var_to_typed_const_w_composite_type", - "typed_const_w_composite_type_invalid_assignment", - "assembly_w_composite_prop_invalid_assignment", - "assembly_w_composite_prop", - "assembly_with_with", - "using_wildcard", - "single_line_comment_in_value", - "single_line_comment_in_value_after_scope", - "multi_line_comment_in_value", - "multi_line_comment_in_value_after_scope", - "unterminated_multi_line_comment_in_value", - "module_stmt", - "nested_module_stmt", - "module_stmt_w_scope", - "module_stmt_w_nested_scope", - "module_w_assembly", - "module_w_nested_assembly", - "assign_singleton_tag", - "assign_singleton_component", - "assign_singleton_tag_w_scope", - "assign_singleton_2_tags_w_scope", - "assign_singleton_component_w_scope", - "assign_singleton_2_components_w_scope", - "with_pair_in_scope", - "assembly_redeclare_prop_as_const", - "assembly_redeclare_prop_as_prop", - "assembly_redeclare_const_as_const", - "add_auto_override", - "add_auto_override_pair", - "scope_w_auto_override", - "scope_w_auto_override_pair", - "pair_w_rel_var", - "pair_w_tgt_var", - "assembly_w_pair_w_this_var", - "with_value_not_a_component", - "component_in_with_scope", - "component_in_with_scope_nested", - "component_in_with_scope_in_scope", - "assign_after_with_in_scope", - "array_component" - ] - }, { "id": "Doc", "testcases": [ "get_set_name", @@ -499,11 +18,13 @@ "get_set_brief", "get_set_detail", "get_set_link", + "get_set_uuid", "set_name_nullptr", "set_brief_nullptr", "set_detail_nullptr", "set_link_nullptr", - "set_color_nullptr" + "set_color_nullptr", + "set_uuid_nullptr" ] }, { "id": "Pipeline", @@ -589,7 +110,10 @@ "run_pipeline_multithreaded", "run_pipeline_multithreaded_tasks", "pipeline_init_no_terms", - "pipeline_init_no_system_term" + "pipeline_init_no_system_term", + "disable_component_from_immediate_system", + "run_w_empty_query", + "run_w_0_src_query" ] }, { "id": "SystemMisc", @@ -597,7 +121,6 @@ "invalid_not_without_id", "invalid_optional_without_id", "invalid_entity_without_id", - "invalid_empty_without_id", "invalid_empty_element", "invalid_empty_element_w_space", "invalid_empty_or", @@ -610,7 +133,7 @@ "invalid_entity_id", "invalid_null_string", "invalid_empty_string", - "invalid_empty_string_w_space", + "invalid_empty_string_w_space", "redefine_row_system", "system_w_or_prefab", "system_w_or_disabled", @@ -618,7 +141,6 @@ "table_columns_access", "dont_enable_after_rematch", "ensure_single_merge", - "table_count", "match_system", "system_initial_state", "add_own_component", @@ -628,8 +150,6 @@ "add_to_system_in_progress", "redefine_null_signature", "redefine_0_signature", - "one_named_column_of_two", - "two_named_columns_of_two", "redeclare_system_explicit_id", "redeclare_system_explicit_id_null_expr", "redeclare_system_explicit_id_no_name", @@ -653,7 +173,6 @@ "delete_system_w_ctx", "update_ctx", "run_custom_run_action", - "run_w_offset_limit_custom_run_action", "pipeline_custom_run_action", "change_custom_run_action", "custom_run_action_call_next", @@ -661,720 +180,13 @@ "update_interval_w_system_init", "update_rate_w_system_init", "system_w_interval_rate_stop_timer", - "system_same_interval_same_tick" - ] - }, { - "id": "RulesBasic", - "testcases": [ - "1_fact_w_tag", - "1_fact_w_component", - "1_fact_w_tag_pair", - "1_fact_w_component_pair", - "2_facts_same_src_w_tag", - "2_facts_same_src_w_component", - "2_facts_same_src_w_tag_pair", - "2_facts_same_src_w_component_pair", - "2_facts_other_src_w_tag", - "2_facts_other_src_w_component", - "2_facts_other_src_w_tag_pair", - "2_facts_other_src_w_component_pair", - "1_fact_w_any", - "1_fact_w_pair_any_tgt", - "1_fact_w_pair_any_rel", - "1_fact_w_pair_any_rel_tgt", - "2_facts_same_src_w_any", - "2_facts_same_src_w_pair_any_tgt", - "2_facts_same_src_w_pair_any_rel", - "2_facts_same_src_w_pair_any_rel_tgt", - "2_facts_other_src_w_any", - "2_facts_other_src_w_pair_any_tgt", - "2_facts_other_src_w_pair_any_rel", - "2_facts_other_src_w_pair_any_rel_tgt", - "1_this_src_w_tag", - "1_this_src_w_component", - "1_this_src_w_tag_pair", - "1_this_src_w_component_pair", - "1_this_src_w_tag_2_tables", - "1_this_src_w_component_2_tables", - "1_this_src_w_tag_pair_2_tables", - "1_this_src_w_component_pair_2_tables", - "2_this_src_w_tag", - "2_this_src_w_component", - "2_this_src_ent_src_w_tag", - "2_this_src_ent_src_w_component", - "2_ent_src_this_src_w_tag", - "2_ent_src_this_src_w_component", - "recycled_tag", - "recycled_src", - "recycled_pair_rel", - "recycled_pair_tgt", - "this_src_w_wildcard", - "this_src_w_pair_rel_wildcard", - "this_src_w_pair_tgt_wildcard", - "this_src_w_pair_rel_tgt_wildcard", - "this_src_w_any", - "this_src_w_any_written", - "this_src_w_pair_rel_any", - "this_src_w_pair_tgt_any", - "this_src_w_pair_rel_tgt_any", - "ent_src_w_wildcard", - "ent_src_w_pair_rel_wildcard", - "ent_src_w_pair_tgt_wildcard", - "ent_src_w_pair_rel_tgt_wildcard", - "1_wildcard_src", - "1_wildcard_src_w_pair", - "2_wildcard_src", - "2_wildcard_src_w_pair", - "1_wildcard_src_w_pair_tgt_var", - "1_wildcard_src_w_pair_rel_var", - "1_wildcard_src_w_pair_tgt_this", - "1_wildcard_src_w_pair_rel_this", - "1_any_src", - "1_any_src_w_pair", - "2_any_src", - "2_any_src_w_pair", - "1_any_src_w_pair_tgt_var", - "1_any_src_w_pair_rel_var", - "1_any_src_w_pair_tgt_this", - "1_any_src_w_pair_rel_this", - "not_any", - "rule_w_iter_next", - "empty_rule", - "invalid_rule", - "not_instanced_table_src", - "not_instanced_entity_src", - "not_instanced_mixed_src", - "instanced_table_src", - "instanced_entity_src", - "instanced_mixed_src", - "in_term", - "out_term", - "inout_term", - "nodata_term", - "find_this_lowercase", - "find_this_uppercase", - "find_this_tgt_lowercase", - "find_this_tgt_uppercase", - "get_filter", - "iter_empty_source", - "iter_empty_source_2_terms", - "iter_empty_source_wildcard", - "iter_empty_source_pair", - "iter_empty_source_pair_wildcard", - "iter_empty_source_2_terms_pair", - "iter_empty_source_2_terms_mixed", - "iter_empty_source_2_terms_mixed_pair", - "iter_empty_source_2_terms_mixed_pair_wildcard", - "this_var_w_empty_entity", - "match_disabled", - "match_prefab", - "match_disabled_prefab", - "match_disabled_this_tgt", - "match_prefab_this_tgt", - "match_disabled_prefab_this_tgt", - "match_self_disabled", - "match_self_prefab", - "match_self_disabled_prefab", - "inout_none_first_term", - "inout_none_second_term", - "no_data_rule", - "frame_offset", - "frame_offset_no_data", - "match_empty_tables", - "match_empty_tables_no_data", - "match_empty_tables_w_not", - "match_empty_tables_w_wildcard", - "match_empty_tables_w_no_empty_tables", - "match_empty_tables_trivial", - "oneof_wildcard", - "oneof_any", - "instanced_w_singleton", - "instanced_w_base", - "not_instanced_w_singleton", - "not_instanced_w_base", - "unknown_before_known", - "unknown_before_known_after_or", - "unknown_before_known_after_not", - "unknown_before_known_after_optional", - "unknown_before_known_after_scope", - "reordered_plan_1", - "reordered_plan_2", - "1_trivial_plan", - "2_trivial_plan", - "1_trivial_plan_component", - "2_trivial_plan_component", - "3_trivial_plan_w_pair", - "3_trivial_plan_w_wildcard", - "3_trivial_plan_w_any", - "3_trivial_plan_w_pair_component", - "3_trivial_plan_w_wildcard_component", - "3_trivial_plan_w_any_component", - "1_trivial_component_w_none", - "2_trivial_component_w_none" - ] - }, { - "id": "RulesVariables", - "testcases": [ - "1_ent_src_w_var", - "1_ent_src_w_pair_rel_var", - "1_ent_src_w_pair_tgt_var", - "1_ent_src_w_pair_rel_tgt_var", - "1_ent_src_w_pair_rel_tgt_same_var", - "1_ent_src_w_pair_rel_tgt_same_var_after_write", - "1_this_src_w_var", - "1_this_src_w_pair_rel_var", - "1_this_src_w_pair_tgt_var", - "1_this_src_w_pair_rel_tgt_var", - "1_this_src_w_pair_rel_tgt_same_var", - "1_this_src_w_pair_rel_tgt_same_var_after_write", - "1_src_id_same_var", - "1_src_pair_first_same_var", - "1_src_pair_second_same_var", - "1_src_pair_first_and_second_same_var", - "1_src_id_same_var_after_write", - "1_src_pair_first_same_var_after_write", - "1_src_pair_second_same_var_after_write", - "1_src_pair_first_and_second_same_var_after_write", - "1_src_pair_first_same_var_this", - "1_src_pair_second_same_var_this", - "1_src_pair_first_and_second_same_var_this", - "1_src_id_same_var_this_after_write", - "1_src_pair_first_same_var_this_after_write", - "1_src_pair_second_same_var_this_after_write", - "1_src_pair_first_and_second_same_var_this_after_write", - "1_ent_src_w_this_var", - "1_ent_src_w_pair_this_rel", - "1_ent_src_w_pair_this_tgt", - "1_ent_src_w_pair_this_rel_tgt", - "1_this_src_w_this", - "1_this_src_w_pair_this_rel_tgt", - "1_this_src_w_this_after_write", - "1_this_src_w_pair_this_rel_tgt_after_write", - "2_constrain_src_from_src", - "2_constrain_rel_from_src_w_ent", - "2_constrain_rel_from_src_w_var", - "2_constrain_rel_from_src_w_this", - "2_constrain_pair_rel_from_src_w_ent", - "2_constrain_pair_rel_from_src_w_var", - "2_constrain_pair_rel_from_src_w_this", - "2_constrain_pair_tgt_from_src_w_ent", - "2_constrain_pair_tgt_from_src_w_var", - "2_constrain_pair_tgt_from_src_w_this", - "2_constrain_pair_rel_tgt_from_src_w_ent", - "2_constrain_pair_rel_tgt_from_src_w_var", - "2_constrain_pair_rel_tgt_from_src_w_this", - "1_ent_src_set_rel_var", - "1_ent_src_set_pair_rel_var", - "1_ent_src_set_pair_tgt_var", - "1_set_src_var", - "1_set_src_var_w_pair", - "1_set_src_var_w_pair_set_rel", - "1_set_src_var_w_pair_set_tgt", - "1_set_src_var_w_pair_set_rel_tgt", - "1_set_src_this", - "1_set_src_this_w_pair", - "1_set_src_this_w_pair_set_rel", - "1_set_src_this_w_pair_set_tgt", - "1_set_src_this_w_pair_set_rel_tgt", - "1_set_src_this_to_empty_table", - "1_set_src_this_to_empty_table_w_component", - "1_set_src_this_to_empty_table_w_component_self", - "1_set_src_this_to_entiy_in_table", - "1_set_src_this_to_entiy_in_table_self", - "2_set_src_this", - "2_set_src_this_self", - "2_set_src_this_component", - "2_set_src_this_self_component", - "2_set_src_this_w_up", - "2_set_src_this_self_w_up", - "2_set_src_this_component_w_up", - "2_set_src_this_self_component_w_up", - "2_set_src_this_w_exclusive_wildcard", - "2_set_src_this_self_w_exclusive_wildcard", - "1_src_this_var_as_entity", - "1_src_this_var_as_table", - "1_src_this_var_as_table_range", - "2_join_by_rel_var", - "2_join_by_pair_rel_var", - "2_join_by_pair_tgt_var", - "2_cycle_w_var", - "2_cycle_w_this_var", - "2_cycle_w_var_this", - "2_cycle_pair_w_var", - "2_cycle_pair_w_this_var_var", - "2_cycle_pair_w_var_this_var", - "2_cycle_pair_w_var_var_this", - "2_cycle_pair_ent_var_var", - "2_cycle_pair_ent_this_var", - "2_cycle_pair_ent_var_this", - "parse_0_var", - "parse_1_var", - "parse_2_vars", - "parse_0_var_paren", - "parse_1_var_paren", - "parse_2_vars_paren", - "parse_1_vars_w_path", - "parse_missing_close_paren", - "parse_missing_open_paren", - "parse_missing_value", - "parse_0_var_w_spaces", - "parse_1_var_w_spaces", - "parse_2_vars_w_spaces", - "parse_0_var_paren_w_spaces", - "parse_1_var_paren_w_spaces", - "parse_2_vars_paren_w_spaces", - "var_count", - "var_name", - "var_is_entity", - "no_this_anonymous_src", - "no_this_anonymous_src_w_pair", - "no_this_anonymous_component_src", - "no_this_anonymous_component_src_w_pair", - "lookup_from_table_this", - "lookup_from_entity_this", - "lookup_from_table", - "lookup_from_entity", - "lookup_from_not_written", - "lookup_from_table_this_component", - "lookup_from_entity_this_component", - "lookup_from_table_component", - "lookup_from_entity_component", - "lookup_from_table_two_children", - "lookup_from_entity_two_children", - "lookup_from_table_same_child_twice", - "lookup_from_entity_same_child_twice", - "lookup_from_table_not", - "lookup_from_entity_not", - "lookup_from_table_w_any_component", - "lookup_from_entity_w_any_component", - "lookup_as_tag", - "lookup_as_relationship", - "lookup_as_target", - "lookup_assign_var", - "lookup_eq_var", - "lookup_neq_var", - "check_vars_this", - "check_vars_var", - "check_vars_wildcard", - "check_vars_any", - "check_vars_var_as_tgt", - "check_vars_this_as_tgt", - "check_vars_anonymous_var_as_tgt", - "check_vars_wildcard_as_tgt", - "check_vars_any_as_tgt", - "check_vars_this_w_lookup_var", - "check_vars_var_w_lookup_var", - "1_trivial_1_var", - "2_trivial_1_var", - "1_trivial_1_var_component", - "2_trivial_1_var_component", - "1_trivial_1_wildcard", - "2_trivial_1_wildcard", - "1_trivial_1_wildcard_component", - "2_trivial_1_wildcard_component", - "1_trivial_1_any", - "2_trivial_1_any", - "1_trivial_1_any_component", - "2_trivial_1_any_component" - ] - }, { - "id": "RulesOperators", - "testcases": [ - "2_and_not", - "3_and_not_not", - "2_and_not_pair_rel_wildcard", - "2_and_not_pair_tgt_wildcard", - "2_and_not_pair_rel_tgt_wildcard", - "2_and_not_pair_rel_var", - "2_and_not_pair_tgt_var", - "2_and_not_pair_rel_tgt_var", - "2_and_not_pair_rel_tgt_same_var", - "2_and_not_pair_rel_var_written", - "2_and_not_pair_tgt_var_written", - "2_and_not_pair_rel_tgt_var_written", - "2_and_not_pair_rel_tgt_same_var_written", - "2_and_not_pair_rel_src_tgt_same_var_written", - "2_and_not_pair_any_rel", - "2_and_not_pair_any_tgt", - "2_and_not_pair_any_src", - "2_and_optional", - "3_and_optional_optional", - "2_and_optional_pair_rel_wildcard", - "2_and_optional_pair_tgt_wildcard", - "2_and_optional_pair_rel_var", - "2_and_optional_pair_tgt_var", - "2_and_optional_pair_rel_tgt_var", - "2_and_optional_pair_rel_tgt_same_var", - "2_and_optional_pair_rel_var_written", - "2_and_optional_pair_tgt_var_written", - "2_and_optional_pair_rel_tgt_var_written", - "2_and_optional_pair_rel_tgt_same_var_written", - "2_and_optional_pair_rel_src_tgt_same_var_written", - "3_and_optional_optional_pair_w_var", - "2_and_optional_pair_any_rel", - "2_and_optional_pair_any_tgt", - "2_and_optional_pair_any_src", - "3_and_optional_dependent_and_pair_rel", - "3_and_optional_dependent_and_pair_tgt", - "3_and_optional_dependent_and_pair_rel_tgt", - "3_and_optional_dependent_and_pair_rel_tgt_same_var", - "3_and_optional_dependent_and_pair_rel_tgt_same_other_var", - "3_and_optional_dependent_and_pair_src", - "3_and_optional_dependent_optional_pair_rel", - "3_and_optional_dependent_optional_pair_tgt", - "3_and_optional_dependent_optional_pair_src", - "3_and_optional_dependent_not_pair_rel", - "3_and_optional_dependent_not_pair_tgt", - "3_and_optional_dependent_not_pair_src", - "2_or", - "3_or", - "2_or_written", - "3_or_written", - "2_or_written_w_rel_var", - "3_or_written_w_rel_var", - "2_or_written_w_tgt_var", - "2_or_written_w_rel_tgt_var", - "2_or_written_w_rel_tgt_same_var", - "3_or_written_w_tgt_var", - "2_or_chains", - "2_or_chains_written", - "2_or_dependent", - "2_or_dependent_reverse", - "2_or_dependent_2_vars", - "2_or_written_dependent", - "2_or_written_dependent_2_vars", - "2_or_w_dependent", - "2_or_w_both", - "3_or_w_both", - "2_not_first", - "2_optional_first", - "root_entities_empty", - "root_entities", - "root_entities_w_children", - "root_entities_w_optional_children", - "core_entities_w_optional_children", - "root_entities_w_not_children", - "core_entities_w_not_children", - "1_ent_src_not", - "1_ent_src_not_pair", - "1_ent_src_not_pair_rel_wildcard", - "1_ent_src_not_pair_tgt_wildcard", - "1_ent_src_not_pair_rel_tgt_wildcard", - "1_ent_src_not_pair_rel_any", - "1_ent_src_not_pair_tgt_any", - "1_ent_src_not_pair_rel_tgt_any", - "1_ent_src_not_pair_rel_var", - "1_ent_src_not_pair_tgt_var", - "1_ent_src_not_pair_rel_tgt_var", - "1_ent_src_not_pair_rel_tgt_same_var", - "1_this_src_not_pair_rel_var", - "1_this_src_not_pair_tgt_var", - "1_this_src_not_pair_rel_tgt_var", - "1_this_src_not_pair_rel_tgt_same_var", - "1_ent_src_not_pair_rel_var_written", - "1_ent_src_not_pair_tgt_var_written", - "1_ent_src_not_pair_rel_tgt_var_written", - "1_ent_src_not_pair_rel_tgt_same_var_written" - ] - }, { - "id": "RulesTransitive", - "testcases": [ - "1_fact_0_lvl_true", - "1_fact_1_lvl_true", - "1_fact_2_lvl_true", - "1_fact_0_lvl_false", - "1_fact_1_lvl_false", - "1_fact_2_lvl_false", - "1_fact_reflexive", - "1_this_src_written_0_lvl", - "1_this_src_written_1_lvl", - "1_this_src_written_2_lvl", - "1_this_src_written_reflexive", - "1_this_src_0_lvl", - "1_this_src_1_lvl", - "1_this_src_2_lvl", - "1_this_src_reflexive", - "1_ent_src_tgt_var_0_lvl", - "1_ent_src_tgt_var_1_lvl", - "1_ent_src_tgt_var_2_lvl", - "1_ent_src_tgt_var_reflexive", - "1_this_src_tgt_var", - "1_this_src_tgt_var_reflexive", - "1_var_src_written_0_lvl", - "1_var_src_written_1_lvl", - "1_var_src_written_2_lvl", - "1_var_src_written_reflexive", - "1_var_src_0_lvl", - "1_var_src_1_lvl", - "1_var_src_2_lvl", - "1_var_src_reflexive", - "1_var_src_tgt_var", - "1_var_src_tgt_var_reflexive", - "1_ent_src_tgt_this_0_lvl", - "1_ent_src_tgt_this_1_lvl", - "1_ent_src_tgt_this_2_lvl", - "1_ent_src_tgt_this_reflexive", - "1_var_src_tgt_this", - "1_var_src_tgt_this_reflexive", - "2_ent_src_constrain_tgt_var_before_0_lvl", - "2_ent_src_constrain_tgt_var_before_1_lvl", - "2_ent_src_constrain_tgt_var_before_2_lvl", - "2_ent_src_constrain_tgt_var_after_0_lvl", - "2_ent_src_constrain_tgt_var_after_1_lvl", - "2_ent_src_constrain_tgt_var_after_2_lvl", - "2_this_src_constrain_tgt_var_before_0_lvl", - "2_this_src_constrain_tgt_var_before_1_lvl", - "2_this_src_constrain_tgt_var_before_2_lvl", - "2_this_src_constrain_tgt_var_after_0_lvl", - "2_this_src_constrain_tgt_var_after_1_lvl", - "2_this_src_constrain_tgt_var_after_2_lvl", - "1_src_tgt_same_var", - "1_src_tgt_same_var_reflexive", - "1_src_tgt_same_this_var_reflexive", - "1_any_src_tgt_var", - "not_transitive_ent_tgt", - "not_transitive_var_tgt", - "not_transitive_ent_tgt_written", - "not_transitive_var_tgt_written", - "optional_transitive_ent_tgt", - "optional_transitive_var_tgt", - "optional_transitive_ent_tgt_written", - "optional_transitive_var_tgt_written", - "2_var_src_w_same_tgt_ent", - "self_target", - "any_target" - ] - }, { - "id": "RulesComponentInheritance", - "testcases": [ - "1_ent_0_lvl", - "1_ent_1_lvl", - "1_ent_2_lvl", - "1_ent_3_lvl", - "1_this_0_lvl", - "1_this_1_lvl", - "1_this_2_lvl", - "1_this_3_lvl", - "1_this_0_lvl_written", - "1_this_1_lvl_written", - "1_this_2_lvl_written", - "1_this_3_lvl_written", - "1_var_0_lvl", - "1_var_1_lvl", - "1_var_2_lvl", - "1_var_3_lvl", - "1_var_0_lvl_written", - "1_var_1_lvl_written", - "1_var_2_lvl_written", - "1_var_3_lvl_written", - "1_ent_1_lvl_self", - "1_this_1_lvl_self", - "1_this_1_lvl_written_self", - "1_var_1_lvl_self", - "1_var_1_lvl_written_self", - "1_ent_src_not", - "1_this_src_not", - "1_var_src_not", - "1_this_src_not_written", - "1_var_src_not_written", - "first_self", - "first_down", - "first_self_down", - "first_rel_self", - "first_rel_down", - "first_rel_self_down" - ] - }, { - "id": "RulesRecycled", - "testcases": [ - "recycled_vars", - "recycled_pair_vars", - "recycled_this_ent_var", - "has_recycled_id_from_pair" - ] - }, { - "id": "RulesBuiltinPredicates", - "testcases": [ - "this_eq_id", - "this_eq_name", - "this_eq_var", - "this_eq_id_written", - "this_eq_id_written_no_match", - "this_eq_name_written", - "this_eq_name_written_no_match", - "this_eq_var_written", - "var_eq_id", - "var_eq_name", - "var_eq_var", - "var_eq_id_written", - "var_eq_id_written_no_match", - "var_eq_name_written", - "var_eq_name_written_no_match", - "var_eq_var_written", - "this_neq_id", - "this_neq_name", - "this_neq_var", - "this_neq_id_written", - "this_neq_id_written_no_match", - "this_neq_name_written", - "this_neq_name_written_no_match", - "this_neq_var_written", - "var_neq_id", - "var_neq_name", - "var_neq_var", - "var_neq_id_written", - "var_neq_id_written_no_match", - "var_neq_name_written", - "var_neq_name_written_no_match", - "var_neq_var_written", - "this_2_neq_id", - "this_2_neq_name", - "var_2_neq_id", - "var_2_neq_name", - "this_2_neq_id_written", - "this_2_neq_name_written", - "var_2_neq_id_written", - "var_2_neq_name_written", - "this_2_or_id", - "this_2_or_name", - "var_2_or_id", - "var_2_or_name", - "this_2_or_id_written", - "this_2_or_name_written", - "var_2_or_id_written", - "var_2_or_name_written", - "this_match_eq", - "var_match_eq", - "this_match_eq_written", - "var_match_eq_written", - "this_match_neq", - "var_match_neq", - "this_match_neq_written", - "var_match_neq_written", - "this_match_2_neq", - "var_match_2_neq", - "this_match_2_neq_written", - "var_match_2_neq_written", - "this_match_2_or", - "this_match_2_or_written", - "this_match_3_or", - "this_match_3_or_written", - "unresolved_by_name", - "var_eq_wildcard", - "var_eq_any", - "var_eq_wildcard_after_write", - "var_eq_any_after_write", - "var_eq_after_var_0_src" - ] - }, { - "id": "RulesScopes", - "testcases": [ - "term_w_not_scope_1_term", - "term_w_not_scope_2_terms", - "term_w_not_scope_1_term_w_not", - "term_w_not_scope_2_terms_w_not", - "term_w_not_scope_1_term_w_var", - "term_w_not_scope_2_terms_w_var", - "term_w_not_scope_1_term_w_not_w_var", - "term_w_not_scope_2_terms_w_not_w_var", - "term_w_not_scope_2_terms_w_or", - "term_w_not_scope_3_terms_w_or" - ] - }, { - "id": "RulesTraversal", - "testcases": [ - "this_self_up_childof", - "this_up_childof", - "this_written_self_up_childof", - "this_written_up_childof", - "var_self_up_childof", - "var_up_childof", - "var_written_self_up_childof", - "var_written_up_childof", - "set_var_self_up_childof", - "set_var_up_childof", - "set_var_written_self_up_childof", - "set_var_written_up_childof", - "ent_self_up_childof", - "ent_up_childof", - "implicit_this_self_up_isa", - "implicit_this_up_isa", - "implicit_var_self_up_isa", - "implicit_var_up_isa", - "implicit_ent_self_up_isa", - "implicit_ent_up_isa", - "self_up_2_targets", - "up_2_targets", - "self_up_2_targets_diamond", - "up_2_targets_diamond", - "written_self_up_2_targets", - "written_up_2_targets", - "written_self_up_2_targets_diamond", - "written_up_2_targets_diamond", - "2_self_up_terms", - "2_self_up_terms_2_targets", - "self_up_empty_table", - "up_empty_table", - "self_up_all_owned", - "up_all_owned", - "this_self_up_childof_inherited", - "this_up_childof_inherited", - "this_written_self_up_childof_inherited", - "this_written_up_childof_inherited", - "var_self_up_childof_inherited", - "var_up_childof_inherited", - "var_written_self_up_childof_inherited", - "var_written_up_childof_inherited", - "ent_self_up_childof_inherited", - "ent_up_childof_inherited", - "ent_written_self_up_childof_inherited", - "ent_written_up_childof_inherited", - "this_self_up_childof_component", - "this_up_childof_component", - "this_written_self_up_childof_component", - "this_written_up_childof_component", - "var_self_up_childof_component", - "var_up_childof_component", - "var_written_self_up_childof_component", - "var_written_up_childof_component", - "this_self_up_childof_recycled_parent", - "this_up_childof_recycled_parent", - "this_written_self_up_childof_recycled_parent", - "this_written_up_childof_recycled_parent", - "this_self_up_childof_recycled_parent_component", - "this_up_childof_recycled_parent_component", - "this_written_self_up_childof_recycled_parent_component", - "this_written_up_childof_recycled_parent_component", - "this_self_up_childof_pair", - "this_up_childof_pair", - "this_written_self_up_childof_pair", - "this_written_up_childof_pair", - "this_self_up_childof_pair_wildcard", - "this_up_childof_pair_wildcard", - "this_written_self_up_childof_pair_wildcard", - "this_written_up_childof_pair_wildcard", - "this_self_up_childof_pair_tgt_var", - "this_written_self_up_childof_pair_tgt_var", - "this_self_up_childof_pair_rel_var", - "this_written_self_up_childof_pair_rel_var", - "this_self_up_childof_pair_for_var_written", - "this_up_childof_pair_for_var_written", - "this_written_self_up_childof_pair_for_var_written", - "this_self_up_childof_pair_for_var_written_n_targets", - "this_written_self_up_childof_pair_for_var_written_n_targets", - "self_up_2_levels", - "not_up_disabled", - "up_2_rel_instances", - "up_2_rel_instances_match_2nd", - "up_only_w_owned", - "this_self_cascade_childof", - "this_cascade_childof", - "this_written_self_cascade_childof", - "this_written_cascade_childof", - "this_self_cascade_childof_w_parent_flag", - "this_cascade_childof_w_parent_flag", - "this_written_self_cascade_childof_w_parent_flag", - "this_written_cascade_childof_w_parent_flag" + "system_w_rate_filter_self", + "system_same_interval_same_tick", + "system_no_id_in_scope", + "register_callback_after_run", + "register_run_after_callback", + "register_callback_after_run_ctx", + "register_run_after_callback_ctx" ] }, { "id": "SystemPeriodic", @@ -1468,7 +280,6 @@ "setup": true, "testcases": [ "1_type_1_component", - "no_automerge", "dont_run_w_unmatching_entity_query" ] }, { @@ -1488,7 +299,7 @@ "3_column_2_from_container", "2_column_1_from_container_w_not", "2_column_1_from_container_w_not_prefab", - "3_column_1_from_comtainer_1_from_container_w_not", + "3_column_1_from_container_1_from_container_w_not", "2_column_1_from_container_w_or", "select_same_from_container", "add_component_after_match", @@ -1537,7 +348,9 @@ "get_pipeline_stats_after_progress_2_systems_one_merge", "get_entity_count", "get_pipeline_stats_w_task_system", - "get_not_alive_entity_count" + "get_not_alive_entity_count", + "progress_stats_systems", + "progress_stats_systems_w_empty_table_flag" ] }, { "id": "Run", @@ -1546,28 +359,15 @@ "run", "run_w_param", "run_no_match", - "run_w_offset", - "run_w_offset_skip_1_archetype", - "run_w_offset_skip_1_archetype_plus_one", - "run_w_offset_skip_2_archetypes", - "run_w_limit_skip_1_archetype", - "run_w_limit_skip_1_archetype_minus_one", - "run_w_limit_skip_2_archetypes", - "run_w_offset_1_limit_max", - "run_w_offset_1_limit_minus_1", - "run_w_offset_2_type_limit_max", - "run_w_offset_2_type_limit_minus_1", - "run_w_limit_1_all_offsets", - "run_w_offset_out_of_bounds", - "run_w_limit_out_of_bounds", - "run_comb_10_entities_1_type", - "run_comb_10_entities_2_types", "run_w_interrupt", "run_staging" ] }, { "id": "MultiThread", "setup": true, + "params": { + "worker_kind": ["thread", "task"] + }, "testcases": [ "2_thread_1_entity", "2_thread_2_entity", @@ -1623,6 +423,9 @@ }, { "id": "MultiThreadStaging", "setup": true, + "params": { + "worker_kind": ["thread", "task"] + }, "testcases": [ "2_threads_add_to_current", "3_threads_add_to_current", @@ -1632,117 +435,11 @@ "2_threads_on_add", "new_w_count", "custom_thread_auto_merge", - "custom_thread_manual_merge", - "custom_thread_partial_manual_merge", - "set_pair_w_new_target_readonly", - "set_pair_w_new_target_tgt_component_readonly", - "set_pair_w_new_target_defer", - "set_pair_w_new_target_tgt_component_defer" - ] - }, { - "id": "MultiTaskThread", - "setup": true, - "testcases": [ - "2_thread_1_entity", - "2_thread_2_entity", - "2_thread_5_entity", - "2_thread_10_entity", - "3_thread_1_entity", - "3_thread_2_entity", - "3_thread_5_entity", - "3_thread_10_entity", - "4_thread_1_entity", - "4_thread_2_entity", - "4_thread_5_entity", - "4_thread_10_entity", - "5_thread_1_entity", - "5_thread_2_entity", - "5_thread_5_entity", - "5_thread_10_entity", - "6_thread_1_entity", - "6_thread_2_entity", - "6_thread_5_entity", - "6_thread_10_entity", - "2_thread_1_entity_instanced", - "2_thread_5_entity_instanced", - "2_thread_10_entity_instanced", - "2_thread_test_combs_100_entity_w_next_worker", - "2_thread_test_combs_100_entity", - "3_thread_test_combs_100_entity", - "4_thread_test_combs_100_entity", - "5_thread_test_combs_100_entity", - "6_thread_test_combs_100_entity", - "2_thread_test_combs_100_entity_2_types", - "3_thread_test_combs_100_entity_2_types", - "4_thread_test_combs_100_entity_2_types", - "5_thread_test_combs_100_entity_2_types", - "6_thread_test_combs_100_entity_2_types", - "change_thread_count", - "multithread_quit", - "schedule_w_tasks", - "reactive_system", - "fini_after_set_threads", - "2_threads_single_threaded_system", - "no_staging_w_multithread", - "multithread_w_monitor_addon", - "get_ctx", - "get_binding_ctx", - "get_ctx_w_run", - "get_binding_ctx_w_run", - "bulk_new_in_no_readonly_w_multithread", - "bulk_new_in_no_readonly_w_multithread_2", - "run_first_worker_on_main", - "run_single_thread_on_main" - ] - }, { - "id": "MultiTaskThreadStaging", - "setup": true, - "testcases": [ - "2_threads_add_to_current", - "3_threads_add_to_current", - "4_threads_add_to_current", - "5_threads_add_to_current", - "6_threads_add_to_current", - "2_threads_on_add", - "new_w_count", - "custom_thread_auto_merge", - "custom_thread_manual_merge", - "custom_thread_partial_manual_merge", "set_pair_w_new_target_readonly", "set_pair_w_new_target_tgt_component_readonly", "set_pair_w_new_target_defer", "set_pair_w_new_target_tgt_component_defer" ] - }, { - "id": "Snapshot", - "testcases": [ - "simple_snapshot", - "snapshot_after_new", - "snapshot_after_delete", - "snapshot_after_new_type", - "snapshot_after_add", - "snapshot_after_remove", - "snapshot_w_include_filter", - "snapshot_w_exclude_filter", - "snapshot_w_filter_after_new", - "snapshot_w_filter_after_delete", - "snapshot_free_empty", - "snapshot_free", - "snapshot_free_filtered", - "snapshot_free_filtered_w_dtor", - "snapshot_activate_table_w_filter", - "snapshot_copy", - "snapshot_get_ref_after_restore", - "new_after_snapshot", - "new_empty_after_snapshot", - "add_after_snapshot", - "delete_after_snapshot", - "set_after_snapshot", - "restore_recycled", - "snapshot_w_new_in_onset", - "snapshot_w_new_in_onset_in_snapshot_table", - "snapshot_from_stage" - ] }, { "id": "Modules", "setup": true, @@ -1769,7 +466,8 @@ "module_tag_on_namespace_on_add_2_levels", "import_monitor_2_worlds", "import_monitor_after_mini", - "import_2_worlds" + "import_2_worlds", + "component_parent_becomes_module" ] }, { "id": "App", @@ -1793,7 +491,23 @@ "id": "Rest", "testcases": [ "teardown", - "get" + "get", + "get_cached", + "get_cached_invalid", + "try_query", + "query", + "named_query", + "tables", + "request_commands", + "request_commands_2_syncs", + "request_commands_no_frames", + "request_commands_no_commands", + "request_commands_garbage_collect", + "script_error", + "import_rest_after_mini", + "get_pipeline_stats_after_delete_system", + "request_world_summary_before_monitor_sys_run", + "escape_backslash" ] }, { "id": "Metrics", diff --git a/vendors/flecs/test/addons/src/Alerts.c b/vendors/flecs/test/addons/src/Alerts.c index 74359213f..82d0014ab 100644 --- a/vendors/flecs/test/addons/src/Alerts.c +++ b/vendors/flecs/test/addons/src/Alerts.c @@ -8,16 +8,16 @@ void Alerts_one_active_alert(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity" + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity" }); test_assert(alert != 0); @@ -29,9 +29,9 @@ void Alerts_one_active_alert(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -44,8 +44,8 @@ void Alerts_one_active_alert(void) { test_assert(source != NULL); test_int(source->entity, e2); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_progress(world, 1.0); @@ -57,9 +57,9 @@ void Alerts_one_active_alert(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -72,8 +72,8 @@ void Alerts_one_active_alert(void) { test_assert(source != NULL); test_int(source->entity, e2); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add(world, e2, Velocity); @@ -97,8 +97,8 @@ void Alerts_two_active_alerts(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); @@ -106,13 +106,13 @@ void Alerts_two_active_alerts(void) { ecs_add(world, e2, Position); ecs_entity_t alert_1 = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity" + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity" }); ecs_entity_t alert_2 = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_mass"), - .filter.expr = "Position, !Mass" + .entity = ecs_entity(world, { .name = "position_without_mass" }), + .query.expr = "Position, !Mass" }); ecs_progress(world, 1.0); @@ -125,9 +125,9 @@ void Alerts_two_active_alerts(void) { test_assert(ecs_get_alert_count(world, e2, alert_2) == 1); test_assert(ecs_get_alert_count(world, e2, 0) == 2); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert_1); @@ -140,7 +140,7 @@ void Alerts_two_active_alerts(void) { test_int(source->entity, e2); } - test_bool(ecs_filter_next(&it), true); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert_2); @@ -153,8 +153,8 @@ void Alerts_two_active_alerts(void) { test_int(source->entity, e2); } - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_progress(world, 1.0); @@ -168,9 +168,9 @@ void Alerts_two_active_alerts(void) { test_assert(ecs_get_alert_count(world, e2, alert_2) == 1); test_assert(ecs_get_alert_count(world, e2, 0) == 2); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert_1); @@ -183,7 +183,7 @@ void Alerts_two_active_alerts(void) { test_int(source->entity, e2); } - test_bool(ecs_filter_next(&it), true); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert_2); @@ -196,8 +196,8 @@ void Alerts_two_active_alerts(void) { test_int(source->entity, e2); } - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add(world, e2, Mass); @@ -213,9 +213,9 @@ void Alerts_two_active_alerts(void) { test_assert(ecs_get_alert_count(world, e2, alert_2) == 0); test_assert(ecs_get_alert_count(world, e2, 0) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -228,8 +228,8 @@ void Alerts_two_active_alerts(void) { test_assert(source != NULL); test_int(source->entity, e2); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add(world, e2, Velocity); @@ -252,16 +252,16 @@ void Alerts_alert_message(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .message = "missing velocity" }); test_assert(alert != 0); @@ -274,9 +274,9 @@ void Alerts_alert_message(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -290,8 +290,8 @@ void Alerts_alert_message(void) { test_assert(source != NULL); test_int(source->entity, e2); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -305,16 +305,16 @@ void Alerts_alert_message_w_this_var(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .message = "$this: missing velocity" }); test_assert(alert != 0); @@ -327,9 +327,9 @@ void Alerts_alert_message_w_this_var(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -343,8 +343,8 @@ void Alerts_alert_message_w_this_var(void) { test_assert(source != NULL); test_int(source->entity, e2); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -357,11 +357,11 @@ void Alerts_alert_message_w_var(void) { ECS_COMPONENT(world, Position); - ecs_entity_t parent_1 = ecs_new_entity(world, "p1"); + ecs_entity_t parent_1 = ecs_entity(world, { .name = "p1" }); ecs_add(world, parent_1, Position); - ecs_entity_t parent_2 = ecs_new_entity(world, "p2"); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t parent_2 = ecs_entity(world, { .name = "p2" }); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_pair(world, e1, EcsChildOf, parent_1); ecs_add_pair(world, e2, EcsChildOf, parent_2); @@ -369,8 +369,8 @@ void Alerts_alert_message_w_var(void) { ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position($this), ChildOf($this, $parent), !Position($parent)", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position($this), ChildOf($this, $parent), !Position($parent)", .message = "$this: parent $parent does not have Position" }); test_assert(alert != 0); @@ -383,9 +383,9 @@ void Alerts_alert_message_w_var(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -399,8 +399,8 @@ void Alerts_alert_message_w_var(void) { test_assert(source != NULL); test_int(source->entity, e2); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -413,12 +413,12 @@ void Alerts_alert_message_w_changed_var(void) { ECS_COMPONENT(world, Position); - ecs_entity_t parent_1 = ecs_new_entity(world, "p1"); + ecs_entity_t parent_1 = ecs_entity(world, { .name = "p1" }); ecs_add(world, parent_1, Position); - ecs_entity_t parent_2 = ecs_new_entity(world, "p2"); - ecs_entity_t parent_3 = ecs_new_entity(world, "p3"); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t parent_2 = ecs_entity(world, { .name = "p2" }); + ecs_entity_t parent_3 = ecs_entity(world, { .name = "p3" }); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_pair(world, e1, EcsChildOf, parent_1); ecs_add_pair(world, e2, EcsChildOf, parent_2); @@ -426,8 +426,8 @@ void Alerts_alert_message_w_changed_var(void) { ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position($this), ChildOf($this, $parent), !Position($parent)", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position($this), ChildOf($this, $parent), !Position($parent)", .message = "$this: parent $parent does not have Position" }); test_assert(alert != 0); @@ -440,9 +440,9 @@ void Alerts_alert_message_w_changed_var(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -456,8 +456,8 @@ void Alerts_alert_message_w_changed_var(void) { test_assert(source != NULL); test_int(source->entity, e2); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add_pair(world, e2, EcsChildOf, parent_3); @@ -470,9 +470,9 @@ void Alerts_alert_message_w_changed_var(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -485,8 +485,8 @@ void Alerts_alert_message_w_changed_var(void) { test_assert(source != NULL); test_int(source->entity, e2); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -500,8 +500,8 @@ void Alerts_set_brief(void) { ECS_COMPONENT(world, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "has_position"), - .filter.expr = "Position", + .entity = ecs_entity(world, { .name = "has_position" }), + .query.expr = "Position", .brief = "Entity has Position" }); test_assert(alert != 0); @@ -518,8 +518,8 @@ void Alerts_set_doc_name(void) { ECS_COMPONENT(world, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "has_position"), - .filter.expr = "Position", + .entity = ecs_entity(world, { .name = "has_position" }), + .query.expr = "Position", .doc_name = "Has Position" }); test_assert(alert != 0); @@ -536,12 +536,12 @@ void Alerts_alert_instance_has_doc_name(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity" + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity" }); test_assert(alert != 0); @@ -552,9 +552,9 @@ void Alerts_alert_instance_has_doc_name(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -569,8 +569,8 @@ void Alerts_alert_instance_has_doc_name(void) { test_str(ecs_doc_get_name(world, it.entities[0]), "e2"); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -584,13 +584,13 @@ void Alerts_reraise_alert(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity" + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity" }); test_assert(alert != 0); @@ -601,9 +601,9 @@ void Alerts_reraise_alert(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -616,8 +616,8 @@ void Alerts_reraise_alert(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } /* Clear the alert */ @@ -639,9 +639,9 @@ void Alerts_reraise_alert(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -654,8 +654,8 @@ void Alerts_reraise_alert(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -669,12 +669,12 @@ void Alerts_info_severity(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .severity = EcsAlertInfo }); test_assert(alert != 0); @@ -687,9 +687,9 @@ void Alerts_info_severity(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -702,8 +702,8 @@ void Alerts_info_severity(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -717,12 +717,12 @@ void Alerts_warning_severity(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .severity = EcsAlertWarning }); test_assert(alert != 0); @@ -735,9 +735,9 @@ void Alerts_warning_severity(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -750,8 +750,8 @@ void Alerts_warning_severity(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -765,12 +765,12 @@ void Alerts_error_severity(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .severity = EcsAlertError }); test_assert(alert != 0); @@ -783,9 +783,9 @@ void Alerts_error_severity(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -798,8 +798,8 @@ void Alerts_error_severity(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -813,16 +813,16 @@ void Alerts_expire_after_retain(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .retain_period = 1.0 }); test_assert(alert != 0); @@ -836,9 +836,9 @@ void Alerts_expire_after_retain(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -855,8 +855,8 @@ void Alerts_expire_after_retain(void) { test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_progress(world, 1.0); @@ -905,16 +905,16 @@ void Alerts_revive_w_retain(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .retain_period = 1.0 }); test_assert(alert != 0); @@ -928,9 +928,9 @@ void Alerts_revive_w_retain(void) { { test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -947,8 +947,8 @@ void Alerts_revive_w_retain(void) { test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_progress(world, 1.0); @@ -1015,12 +1015,12 @@ void Alerts_severity_filter(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .severity_filters[0] = { .severity = EcsAlertWarning, .with = ecs_id(Mass) @@ -1036,9 +1036,9 @@ void Alerts_severity_filter(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1051,8 +1051,8 @@ void Alerts_severity_filter(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add(world, e1, Mass); @@ -1063,9 +1063,9 @@ void Alerts_severity_filter(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1078,8 +1078,8 @@ void Alerts_severity_filter(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_remove(world, e1, Mass); @@ -1090,9 +1090,9 @@ void Alerts_severity_filter(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1105,8 +1105,8 @@ void Alerts_severity_filter(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -1122,12 +1122,12 @@ void Alerts_two_severity_filters(void) { ECS_COMPONENT(world, Mass); ECS_COMPONENT(world, Rotation); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .severity_filters[0] = { .severity = EcsAlertWarning, .with = ecs_id(Mass) @@ -1147,9 +1147,9 @@ void Alerts_two_severity_filters(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1162,8 +1162,8 @@ void Alerts_two_severity_filters(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add(world, e1, Mass); @@ -1174,9 +1174,9 @@ void Alerts_two_severity_filters(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1189,8 +1189,8 @@ void Alerts_two_severity_filters(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_remove(world, e1, Mass); @@ -1202,9 +1202,9 @@ void Alerts_two_severity_filters(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1217,8 +1217,8 @@ void Alerts_two_severity_filters(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_remove(world, e1, Rotation); @@ -1230,9 +1230,9 @@ void Alerts_two_severity_filters(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1245,8 +1245,8 @@ void Alerts_two_severity_filters(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -1261,16 +1261,16 @@ void Alerts_severity_filter_w_var(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add(world, e1, Position); ecs_add(world, e1, Mass); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity, (ChildOf, $parent)", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity, (ChildOf, $parent)", .severity_filters[0] = { .severity = EcsAlertWarning, .with = ecs_id(Mass), @@ -1287,9 +1287,9 @@ void Alerts_severity_filter_w_var(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1302,8 +1302,8 @@ void Alerts_severity_filter_w_var(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add(world, parent, Mass); @@ -1314,9 +1314,9 @@ void Alerts_severity_filter_w_var(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1329,8 +1329,8 @@ void Alerts_severity_filter_w_var(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_remove(world, parent, Mass); @@ -1341,9 +1341,9 @@ void Alerts_severity_filter_w_var(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1356,8 +1356,8 @@ void Alerts_severity_filter_w_var(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -1372,18 +1372,18 @@ void Alerts_severity_filter_w_var_change_var(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t parent_1 = ecs_new_id(world); - ecs_entity_t parent_2 = ecs_new_id(world); + ecs_entity_t parent_1 = ecs_new(world); + ecs_entity_t parent_2 = ecs_new(world); ecs_add(world, parent_2, Mass); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add_pair(world, e1, EcsChildOf, parent_1); ecs_add(world, e1, Position); ecs_add(world, e1, Mass); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity, (ChildOf, $parent)", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity, (ChildOf, $parent)", .severity_filters[0] = { .severity = EcsAlertWarning, .with = ecs_id(Mass), @@ -1400,9 +1400,9 @@ void Alerts_severity_filter_w_var_change_var(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1415,8 +1415,8 @@ void Alerts_severity_filter_w_var_change_var(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add_pair(world, e1, EcsChildOf, parent_2); @@ -1427,9 +1427,9 @@ void Alerts_severity_filter_w_var_change_var(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1442,8 +1442,8 @@ void Alerts_severity_filter_w_var_change_var(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add_pair(world, e1, EcsChildOf, parent_1); @@ -1454,9 +1454,9 @@ void Alerts_severity_filter_w_var_change_var(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1469,8 +1469,8 @@ void Alerts_severity_filter_w_var_change_var(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -1488,15 +1488,15 @@ void Alerts_member_range_warning(void) { .members = {{ "value", ecs_id(ecs_f32_t), .warning_range = { 0, 100 }}} }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Mass, {50}); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = member }); test_assert(alert != 0); @@ -1515,9 +1515,9 @@ void Alerts_member_range_warning(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1530,8 +1530,8 @@ void Alerts_member_range_warning(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_set(world, e1, Mass, {25}); @@ -1556,15 +1556,15 @@ void Alerts_member_range_error(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Mass, {50}); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = member }); test_assert(alert != 0); @@ -1583,9 +1583,9 @@ void Alerts_member_range_error(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1598,8 +1598,8 @@ void Alerts_member_range_error(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_set(world, e1, Mass, {25}); @@ -1624,15 +1624,15 @@ void Alerts_member_range_warning_error(void) { .members = {{ "value", ecs_id(ecs_f32_t), .warning_range = {0, 50}, .error_range = { 0, 100 }}} }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Mass, {25}); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = member }); test_assert(alert != 0); @@ -1651,9 +1651,9 @@ void Alerts_member_range_warning_error(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1666,8 +1666,8 @@ void Alerts_member_range_warning_error(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_set(world, e1, Mass, {125}); @@ -1679,9 +1679,9 @@ void Alerts_member_range_warning_error(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1694,8 +1694,8 @@ void Alerts_member_range_warning_error(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_set(world, e1, Mass, {25}); @@ -1720,15 +1720,15 @@ void Alerts_member_range_error_w_warning_severity(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Mass, {50}); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .severity = EcsAlertWarning, .member = member }); @@ -1748,9 +1748,9 @@ void Alerts_member_range_error_w_warning_severity(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1763,8 +1763,8 @@ void Alerts_member_range_error_w_warning_severity(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_set(world, e1, Mass, {25}); @@ -1790,15 +1790,15 @@ void Alerts_member_range_error_w_severity_filter(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Mass, {50}); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .severity = EcsAlertWarning, .severity_filters[0] = { .with = Tag, @@ -1822,9 +1822,9 @@ void Alerts_member_range_error_w_severity_filter(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1837,8 +1837,8 @@ void Alerts_member_range_error_w_severity_filter(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add(world, e1, Tag); @@ -1850,9 +1850,9 @@ void Alerts_member_range_error_w_severity_filter(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1865,8 +1865,8 @@ void Alerts_member_range_error_w_severity_filter(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -1885,15 +1885,15 @@ void Alerts_member_range_warning_w_severity_filter(void) { .members = {{ "value", ecs_id(ecs_f32_t), .warning_range = { 0, 100 }}} }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Mass, {50}); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .severity = EcsAlertWarning, .severity_filters[0] = { .with = Tag, @@ -1917,9 +1917,9 @@ void Alerts_member_range_warning_w_severity_filter(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1932,8 +1932,8 @@ void Alerts_member_range_warning_w_severity_filter(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add(world, e1, Tag); @@ -1945,9 +1945,9 @@ void Alerts_member_range_warning_w_severity_filter(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -1960,8 +1960,8 @@ void Alerts_member_range_warning_w_severity_filter(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_fini(world); @@ -1980,15 +1980,15 @@ void Alerts_member_range_pair_id(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Mass, {50}); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = member, .id = ecs_pair_t(Mass, Tag) }); @@ -2008,9 +2008,9 @@ void Alerts_member_range_pair_id(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -2023,8 +2023,8 @@ void Alerts_member_range_pair_id(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_set_pair(world, e1, Mass, Tag, {25}); @@ -2050,13 +2050,13 @@ void Alerts_member_range_invalid_member(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = Tag, }); test_assert(alert == 0); @@ -2076,15 +2076,15 @@ void Alerts_member_range_invalid_member_child(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, ecs_id(Mass)); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = child, }); test_assert(alert == 0); @@ -2105,13 +2105,13 @@ void Alerts_member_range_invalid_type(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = member, .id = Tag }); @@ -2133,17 +2133,17 @@ void Alerts_member_range_invalid_member_type(void) { }); ecs_struct(world, { - .entity = ecs_new_entity(world, "Foo"), + .entity = ecs_entity(world, { .name = "Foo" }), .members = {{ "value", ecs_id(Mass) }} }); - ecs_entity_t member = ecs_lookup_fullpath(world, "Foo.value"); + ecs_entity_t member = ecs_lookup(world, "Foo.value"); test_assert(member != 0); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = member, }); test_assert(alert == 0); @@ -2163,13 +2163,13 @@ void Alerts_member_range_no_range(void) { .members = {{ "value", ecs_id(ecs_f32_t) }} }); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = member, }); test_assert(alert == 0); @@ -2189,17 +2189,17 @@ void Alerts_member_range_alert_two_instances(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Mass, {50}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_set(world, e2, Mass, {50}); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_mass"), - .filter.expr = "Mass", + .entity = ecs_entity(world, { .name = "high_mass" }), + .query.expr = "Mass", .member = member, }); test_assert(alert != 0); @@ -2222,9 +2222,9 @@ void Alerts_member_range_alert_two_instances(void) { test_assert(ecs_get_alert_count(world, e1, alert) == 1); test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 2); test_assert(it.entities[0] != 0); @@ -2244,8 +2244,8 @@ void Alerts_member_range_alert_two_instances(void) { test_int(source->entity, e2); } - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_progress(world, 1.0); @@ -2257,9 +2257,9 @@ void Alerts_member_range_alert_two_instances(void) { test_assert(ecs_get_alert_count(world, e1, alert) == 1); test_assert(ecs_get_alert_count(world, e2, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 2); test_assert(it.entities[0] != 0); @@ -2279,8 +2279,8 @@ void Alerts_member_range_alert_two_instances(void) { test_int(source->entity, e2); } - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_set(world, e1, Mass, {25}); @@ -2306,16 +2306,16 @@ void Alerts_member_range_from_var(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t p = ecs_new_entity(world, "p"); + ecs_entity_t p = ecs_entity(world, { .name = "p" }); ecs_set(world, p, Mass, {50}); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_parent_mass"), - .filter.expr = "(ChildOf, $parent), Mass($parent)", + .entity = ecs_entity(world, { .name = "high_parent_mass" }), + .query.expr = "(ChildOf, $parent), Mass($parent)", .member = member, .var = "parent" }); @@ -2335,9 +2335,9 @@ void Alerts_member_range_from_var(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -2350,8 +2350,8 @@ void Alerts_member_range_from_var(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_set(world, p, Mass, {25}); @@ -2376,16 +2376,16 @@ void Alerts_member_range_from_var_after_remove(void) { .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); - ecs_entity_t p = ecs_new_entity(world, "p"); + ecs_entity_t p = ecs_entity(world, { .name = "p" }); ecs_set(world, p, Mass, {50}); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); + ecs_entity_t member = ecs_lookup(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "high_parent_mass"), - .filter.expr = "(ChildOf, $parent), Mass($parent)", + .entity = ecs_entity(world, { .name = "high_parent_mass" }), + .query.expr = "(ChildOf, $parent), Mass($parent)", .member = member, .var = "parent" }); @@ -2405,9 +2405,9 @@ void Alerts_member_range_from_var_after_remove(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); @@ -2420,8 +2420,8 @@ void Alerts_member_range_from_var_after_remove(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_remove(world, p, Mass); @@ -2442,12 +2442,12 @@ void Alerts_retained_alert_w_dead_source(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .severity = EcsAlertError, .retain_period = 10 }); @@ -2462,9 +2462,9 @@ void Alerts_retained_alert_w_dead_source(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 1); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); ai = it.entities[0]; @@ -2479,8 +2479,8 @@ void Alerts_retained_alert_w_dead_source(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_add(world, e1, Velocity); @@ -2492,9 +2492,9 @@ void Alerts_retained_alert_w_dead_source(void) { { test_assert(ecs_get_alert_count(world, e1, alert) == 0); - ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance, ?Disabled" }); - ecs_iter_t it = ecs_filter_iter(world, alerts); - test_bool(ecs_filter_next(&it), true); + ecs_query_t *alerts = ecs_query(world, { .expr = "flecs.alerts.Instance, ?Disabled" }); + ecs_iter_t it = ecs_query_iter(world, alerts); + test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] == ai); @@ -2507,8 +2507,8 @@ void Alerts_retained_alert_w_dead_source(void) { test_assert(source != NULL); test_int(source->entity, e1); - test_bool(ecs_filter_next(&it), false); - ecs_filter_fini(alerts); + test_bool(ecs_query_next(&it), false); + ecs_query_fini(alerts); } ecs_delete(world, e1); @@ -2529,18 +2529,18 @@ void Alerts_alert_counts(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Position); ecs_entity_t alert_1 = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity" + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity" }); test_assert(alert_1 != 0); ecs_entity_t alert_2 = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_mass"), - .filter.expr = "Position, !Mass", + .entity = ecs_entity(world, { .name = "position_without_mass" }), + .query.expr = "Position, !Mass", .severity = EcsAlertWarning }); test_assert(alert_2 != 0); diff --git a/vendors/flecs/test/addons/src/Doc.c b/vendors/flecs/test/addons/src/Doc.c index 7e3f1fa8a..620c217c0 100644 --- a/vendors/flecs/test/addons/src/Doc.c +++ b/vendors/flecs/test/addons/src/Doc.c @@ -3,7 +3,7 @@ void Doc_get_set_name(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_doc_set_name(world, e, "Human readable name"); @@ -63,10 +63,23 @@ void Doc_get_set_link(void) { ecs_fini(world); } +void Doc_get_set_uuid(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, MyTag); + + ecs_doc_set_uuid(world, MyTag, "81f50b40-09ff-4ce0-a388-4a52a14052c7"); + + test_assert( ecs_has_pair(world, MyTag, ecs_id(EcsDocDescription), EcsDocUuid)); + test_str( ecs_doc_get_uuid(world, MyTag), "81f50b40-09ff-4ce0-a388-4a52a14052c7"); + + ecs_fini(world); +} + void Doc_set_name_nullptr(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_doc_set_name(world, e, "foo"); test_assert( ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsName)); @@ -80,7 +93,7 @@ void Doc_set_name_nullptr(void) { void Doc_set_brief_nullptr(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_doc_set_brief(world, e, "foo"); test_assert( ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsDocBrief)); @@ -94,7 +107,7 @@ void Doc_set_brief_nullptr(void) { void Doc_set_detail_nullptr(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_doc_set_detail(world, e, "foo"); test_assert( ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsDocDetail)); @@ -108,7 +121,7 @@ void Doc_set_detail_nullptr(void) { void Doc_set_link_nullptr(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_doc_set_link(world, e, "foo"); test_assert( ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsDocLink)); @@ -122,7 +135,7 @@ void Doc_set_link_nullptr(void) { void Doc_set_color_nullptr(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_doc_set_color(world, e, "foo"); test_assert( ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsDocColor)); @@ -132,3 +145,17 @@ void Doc_set_color_nullptr(void) { ecs_fini(world); } + +void Doc_set_uuid_nullptr(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t e = ecs_new(world); + + ecs_doc_set_uuid(world, e, "foo"); + test_assert( ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsDocUuid)); + + ecs_doc_set_uuid(world, e, NULL); + test_assert( !ecs_has_pair(world, e, ecs_id(EcsDocDescription), EcsDocUuid)); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/addons/src/Metrics.c b/vendors/flecs/test/addons/src/Metrics.c index 2efeda1be..9ad801fc7 100644 --- a/vendors/flecs/test/addons/src/Metrics.c +++ b/vendors/flecs/test/addons/src/Metrics.c @@ -16,12 +16,12 @@ void Metrics_member_gauge_1_entity(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_progress(world, 0); @@ -60,13 +60,13 @@ void Metrics_member_gauge_2_entities(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); ecs_progress(world, 0); @@ -113,16 +113,16 @@ void Metrics_member_gauge_2_entities_1_existing(void) { } }); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); ecs_progress(world, 0); @@ -171,13 +171,13 @@ void Metrics_member_gauge_2_entities_update(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); ecs_progress(world, 0); @@ -260,13 +260,13 @@ void Metrics_member_gauge_w_remove(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); ecs_add(world, e1, Foo); ecs_add(world, e2, Foo); @@ -339,13 +339,13 @@ void Metrics_member_gauge_w_clear(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); ecs_add(world, e1, Foo); ecs_add(world, e2, Foo); @@ -418,13 +418,13 @@ void Metrics_member_gauge_w_delete(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); ecs_add(world, e1, Foo); ecs_add(world, e2, Foo); @@ -493,7 +493,7 @@ void Metrics_id_gauge_1_entity(void) { }); test_assert(m != 0); - ecs_entity_t e1 = ecs_new(world, Foo); + ecs_entity_t e1 = ecs_new_w(world, Foo); ecs_progress(world, 0); @@ -529,8 +529,8 @@ void Metrics_id_gauge_2_entities(void) { }); test_assert(m != 0); - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); ecs_progress(world, 0); @@ -569,7 +569,7 @@ void Metrics_id_gauge_2_entities_1_existing(void) { ECS_TAG(world, Foo); - ecs_entity_t e1 = ecs_new(world, Foo); + ecs_entity_t e1 = ecs_new_w(world, Foo); ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.has_foo" }), @@ -578,7 +578,7 @@ void Metrics_id_gauge_2_entities_1_existing(void) { }); test_assert(m != 0); - ecs_entity_t e2 = ecs_new(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); ecs_progress(world, 0); @@ -625,8 +625,8 @@ void Metrics_id_gauge_w_remove(void) { }); test_assert(m != 0); - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); ecs_add(world, e1, Bar); ecs_add(world, e2, Bar); @@ -696,8 +696,8 @@ void Metrics_id_gauge_w_clear(void) { }); test_assert(m != 0); - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); ecs_add(world, e1, Bar); ecs_add(world, e2, Bar); @@ -767,8 +767,8 @@ void Metrics_id_gauge_w_delete(void) { }); test_assert(m != 0); - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); ecs_add(world, e1, Bar); ecs_add(world, e2, Bar); @@ -847,7 +847,7 @@ void Metrics_oneof_gauge_3_entities(void) { ecs_progress(world, 0); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_childof(m) }, { .id = ecs_pair(EcsMetric, EcsGauge) } @@ -855,8 +855,8 @@ void Metrics_oneof_gauge_3_entities(void) { }); test_assert(f != NULL); - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 3); { ecs_entity_t e = it.entities[0]; @@ -891,9 +891,9 @@ void Metrics_oneof_gauge_3_entities(void) { test_int(((double*)inst)[1], 0); test_int(((double*)inst)[2], 1); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -922,7 +922,7 @@ void Metrics_oneof_gauge_3_entities_1_existing(void) { ecs_progress(world, 0); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_childof(m) }, { .id = ecs_pair(EcsMetric, EcsGauge) } @@ -930,8 +930,8 @@ void Metrics_oneof_gauge_3_entities_1_existing(void) { }); test_assert(f != NULL); - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 3); { ecs_entity_t e = it.entities[0]; @@ -966,9 +966,9 @@ void Metrics_oneof_gauge_3_entities_1_existing(void) { test_int(((double*)inst)[1], 0); test_int(((double*)inst)[2], 1); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -1000,7 +1000,7 @@ void Metrics_oneof_gauge_w_remove(void) { ecs_progress(world, 0); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_childof(m) }, { .id = ecs_pair(EcsMetric, EcsGauge) } @@ -1009,8 +1009,8 @@ void Metrics_oneof_gauge_w_remove(void) { test_assert(f != NULL); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 3); { ecs_entity_t e = it.entities[0]; @@ -1045,15 +1045,15 @@ void Metrics_oneof_gauge_w_remove(void) { test_int(((double*)inst)[1], 0); test_int(((double*)inst)[2], 1); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } ecs_remove_pair(world, e2, Color, EcsWildcard); ecs_progress(world, 0); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 2); { ecs_entity_t e = it.entities[0]; @@ -1077,10 +1077,10 @@ void Metrics_oneof_gauge_w_remove(void) { test_int(((double*)inst)[1], 0); test_int(((double*)inst)[2], 1); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -1112,7 +1112,7 @@ void Metrics_oneof_gauge_w_clear(void) { ecs_progress(world, 0); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_childof(m) }, { .id = ecs_pair(EcsMetric, EcsGauge) } @@ -1121,8 +1121,8 @@ void Metrics_oneof_gauge_w_clear(void) { test_assert(f != NULL); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 3); { ecs_entity_t e = it.entities[0]; @@ -1157,15 +1157,15 @@ void Metrics_oneof_gauge_w_clear(void) { test_int(((double*)inst)[1], 0); test_int(((double*)inst)[2], 1); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } ecs_clear(world, e2); ecs_progress(world, 0); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 2); { ecs_entity_t e = it.entities[0]; @@ -1189,10 +1189,10 @@ void Metrics_oneof_gauge_w_clear(void) { test_int(((double*)inst)[1], 0); test_int(((double*)inst)[2], 1); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -1224,7 +1224,7 @@ void Metrics_oneof_gauge_w_delete(void) { ecs_progress(world, 0); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_childof(m) }, { .id = ecs_pair(EcsMetric, EcsGauge) } @@ -1233,8 +1233,8 @@ void Metrics_oneof_gauge_w_delete(void) { test_assert(f != NULL); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 3); { ecs_entity_t e = it.entities[0]; @@ -1269,15 +1269,15 @@ void Metrics_oneof_gauge_w_delete(void) { test_int(((double*)inst)[1], 0); test_int(((double*)inst)[2], 1); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } ecs_delete(world, e2); ecs_progress(world, 0); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 2); { ecs_entity_t e = it.entities[0]; @@ -1301,10 +1301,10 @@ void Metrics_oneof_gauge_w_delete(void) { test_int(((double*)inst)[1], 0); test_int(((double*)inst)[2], 1); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -1355,12 +1355,12 @@ void Metrics_member_counter(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsCounter }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_progress(world, 1.0); { @@ -1418,12 +1418,12 @@ void Metrics_member_auto_counter(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsCounterIncrement }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_progress(world, 1.0); { @@ -1478,7 +1478,7 @@ void Metrics_id_counter(void) { }); test_assert(m != 0); - ecs_entity_t e1 = ecs_new(world, Foo); + ecs_entity_t e1 = ecs_new_w(world, Foo); ecs_progress(world, 1.0); @@ -1542,7 +1542,7 @@ void Metrics_oneof_counter(void) { ecs_progress(world, 1.0); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_childof(m) }, { .id = ecs_pair(EcsMetric, EcsCounter) } @@ -1551,8 +1551,8 @@ void Metrics_oneof_counter(void) { test_assert(f != NULL); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 1); { ecs_entity_t e = it.entities[0]; @@ -1565,15 +1565,15 @@ void Metrics_oneof_counter(void) { test_int(((double*)inst)[1], 0); test_int(((double*)inst)[2], 0); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, Color, Green); ecs_progress(world, 1.0); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 1); { ecs_entity_t e = it.entities[0]; @@ -1586,15 +1586,15 @@ void Metrics_oneof_counter(void) { test_int(((double*)inst)[1], 1); test_int(((double*)inst)[2], 0); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, Color, Blue); ecs_progress(world, 1.0); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 1); { ecs_entity_t e = it.entities[0]; @@ -1607,14 +1607,14 @@ void Metrics_oneof_counter(void) { test_int(((double*)inst)[1], 1); test_int(((double*)inst)[2], 1); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } ecs_progress(world, 1.0); { - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); test_uint(it.count, 1); { ecs_entity_t e = it.entities[0]; @@ -1627,10 +1627,10 @@ void Metrics_oneof_counter(void) { test_int(((double*)inst)[1], 1); test_int(((double*)inst)[2], 2); } - test_bool(false, ecs_filter_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -1652,7 +1652,7 @@ void Metrics_metric_description(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge, .brief = "Position y" }); @@ -1660,7 +1660,7 @@ void Metrics_metric_description(void) { test_str(ecs_doc_get_brief(world, m), "Position y"); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_progress(world, 0); @@ -1697,7 +1697,7 @@ void Metrics_id_count(void) { }); test_assert(m != 0); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); ecs_progress(world, 1); @@ -1707,8 +1707,8 @@ void Metrics_id_count(void) { test_int(v->value, 1); } - ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_insert(world, ecs_value(Position, {10, 20})); ecs_progress(world, 1); @@ -1739,18 +1739,18 @@ void Metrics_id_target_count(void) { }); test_assert(m != 0); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add_pair(world, e1, Color, Red); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_add_pair(world, e2, Color, Green); ecs_add_pair(world, e2, Color, Blue); - ecs_entity_t e3 = ecs_new_id(world); + ecs_entity_t e3 = ecs_new(world); ecs_add_pair(world, e3, Color, Blue); ecs_progress(world, 1); { - ecs_entity_t red = ecs_lookup_fullpath(world, "metrics.color.Red"); + ecs_entity_t red = ecs_lookup(world, "metrics.color.Red"); test_assert(red != 0); const EcsMetricValue *v = ecs_get(world, red, EcsMetricValue); test_assert(v != NULL); @@ -1758,7 +1758,7 @@ void Metrics_id_target_count(void) { } { - ecs_entity_t green = ecs_lookup_fullpath(world, "metrics.color.Green"); + ecs_entity_t green = ecs_lookup(world, "metrics.color.Green"); test_assert(green != 0); const EcsMetricValue *v = ecs_get(world, green, EcsMetricValue); test_assert(v != NULL); @@ -1766,7 +1766,7 @@ void Metrics_id_target_count(void) { } { - ecs_entity_t blue = ecs_lookup_fullpath(world, "metrics.color.Blue"); + ecs_entity_t blue = ecs_lookup(world, "metrics.color.Blue"); test_assert(blue != 0); const EcsMetricValue *v = ecs_get(world, blue, EcsMetricValue); test_assert(v != NULL); @@ -1792,12 +1792,12 @@ void Metrics_metric_instance_has_doc_name(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); ecs_progress(world, 0); @@ -1864,7 +1864,7 @@ void Metrics_metric_nested_member(void) { test_assert(m != 0); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, {20, 30}}); ecs_progress(world, 0); @@ -1931,7 +1931,7 @@ void Metrics_metric_nested_member_counter(void) { test_assert(m != 0); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, {20, 30}}); ecs_progress(world, 0); @@ -1998,7 +1998,7 @@ void Metrics_metric_nested_member_counter_increment(void) { test_assert(m != 0); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, {20, 30}}); ecs_progress(world, 1); @@ -2065,12 +2065,12 @@ void Metrics_id_w_member_same_type(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), .id = ecs_id(Position), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_progress(world, 0); @@ -2112,7 +2112,7 @@ void Metrics_id_w_member_mismatching_type(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), .id = ecs_id(Velocity), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m == 0); @@ -2139,12 +2139,12 @@ void Metrics_pair_member_rel_type(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), .id = ecs_pair_t(Position, Foo), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set_pair(world, 0, Position, Foo, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value_pair(Position, Foo, {10, 20})); ecs_set_pair(world, e1, Position, Bar, {1, 2}); ecs_progress(world, 0); @@ -2187,13 +2187,14 @@ void Metrics_pair_member_tgt_type(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), .id = ecs_pair(Foo, ecs_id(Position)), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsGauge }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set_pair_second(world, 0, Foo, Position, {10, 20}); - ecs_set_pair_second(world, e1, Bar, Position, {1, 2}); + ecs_entity_t e1 = ecs_insert(world, + ecs_value_pair_2nd(Foo, Position, {10, 20}), + ecs_value_pair_2nd(Bar, Position, {1, 2})); ecs_progress(world, 0); @@ -2258,7 +2259,7 @@ void Metrics_pair_dotmember_rel_type(void) { }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set_pair(world, 0, Position, Foo, {0, {10, 20}}); + ecs_entity_t e1 = ecs_insert(world, ecs_value_pair(Position, Foo, {0, {10, 20}})); ecs_set_pair(world, e1, Position, Bar, {0, {1, 2}}); ecs_progress(world, 0); @@ -2324,8 +2325,9 @@ void Metrics_pair_dotmember_tgt_type(void) { }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set_pair_second(world, 0, Foo, Position, {0, {10, 20}}); - ecs_set_pair_second(world, e1, Bar, Position, {0, {1, 2}}); + ecs_entity_t e1 = ecs_insert(world, + ecs_value_pair_2nd(Foo, Position, {0, {10, 20}}), + ecs_value_pair_2nd(Bar, Position, {0, {1, 2}})); ecs_progress(world, 0); @@ -2367,12 +2369,12 @@ void Metrics_pair_member_counter_increment(void) { ecs_entity_t m = ecs_metric(world, { .entity = ecs_entity(world, { .name = "metrics.position_y" }), .id = ecs_pair_t(Position, Foo), - .member = ecs_lookup_fullpath(world, "Position.y"), + .member = ecs_lookup(world, "Position.y"), .kind = EcsCounterIncrement }); test_assert(m != 0); - ecs_entity_t e1 = ecs_set_pair(world, 0, Position, Foo, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value_pair(Position, Foo, {10, 20})); ecs_set_pair(world, e1, Position, Bar, {1, 2}); ecs_progress(world, 1); diff --git a/vendors/flecs/test/addons/src/Modules.c b/vendors/flecs/test/addons/src/Modules.c index a8d250493..3222ff74d 100644 --- a/vendors/flecs/test/addons/src/Modules.c +++ b/vendors/flecs/test/addons/src/Modules.c @@ -63,15 +63,15 @@ void SimpleModuleImport( ECS_COMPONENT_DEFINE(world, SimpleFooComponent); ECS_TAG_DEFINE(world, Tag); - ECS_ENTITY_DEFINE(world, Entity, 0); + ECS_ENTITY_DEFINE(world, Entity, #0); ECS_SYSTEM_DEFINE(world, Move, EcsOnUpdate, Position, Velocity); ECS_SYSTEM_DEFINE(world, SimpleFooSystem, EcsOnUpdate, Position); ECS_OBSERVER_DEFINE(world, SimpleFooTrigger, EcsOnAdd, Position); ECS_TAG_DEFINE(world, SimpleFooTag); - ECS_ENTITY_DEFINE(world, SimpleFooEntity, 0); - ECS_PREFAB_DEFINE(world, SimpleFooPrefab, 0); + ECS_ENTITY_DEFINE(world, SimpleFooEntity, #0); + ECS_PREFAB_DEFINE(world, SimpleFooPrefab, #0); ECS_PIPELINE_DEFINE(world, SimpleFooPipeline, flecs.system.System, Tag); ECS_TAG_DEFINE(world, Simple_underscore); } @@ -83,7 +83,7 @@ void Modules_simple_module(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert( ecs_has(world, e, Position)); @@ -107,7 +107,7 @@ void Modules_import_module_from_system(void) { ECS_IMPORT(world, SimpleModule); ECS_SYSTEM(world, AddVtoP, EcsOnUpdate, simple.module.Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert( ecs_has(world, e, Position)); @@ -136,7 +136,7 @@ void Modules_scoped_component(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.Position"); + ecs_entity_t e = ecs_lookup(world, "simple.module.Position"); test_assert(e != 0); test_assert(e == ecs_id(Position)); @@ -148,7 +148,7 @@ void Modules_scoped_tag(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.Tag"); + ecs_entity_t e = ecs_lookup(world, "simple.module.Tag"); test_assert(e != 0); test_assert(e == Tag); @@ -160,7 +160,7 @@ void Modules_scoped_system(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.Move"); + ecs_entity_t e = ecs_lookup(world, "simple.module.Move"); test_assert(e != 0); test_assert(e == ecs_id(Move)); @@ -172,7 +172,7 @@ void Modules_scoped_entity(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.Entity"); + ecs_entity_t e = ecs_lookup(world, "simple.module.Entity"); test_assert(e != 0); test_assert(e == Entity); @@ -184,7 +184,7 @@ void Modules_name_prefix_component(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.FooComponent"); + ecs_entity_t e = ecs_lookup(world, "simple.module.FooComponent"); test_assert(e != 0); test_assert(e == ecs_id(SimpleFooComponent)); @@ -196,7 +196,7 @@ void Modules_name_prefix_tag(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.FooTag"); + ecs_entity_t e = ecs_lookup(world, "simple.module.FooTag"); test_assert(e != 0); test_assert(e == SimpleFooTag); @@ -208,7 +208,7 @@ void Modules_name_prefix_system(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.FooSystem"); + ecs_entity_t e = ecs_lookup(world, "simple.module.FooSystem"); test_assert(e != 0); test_assert(e == ecs_id(SimpleFooSystem)); @@ -220,7 +220,7 @@ void Modules_name_prefix_entity(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.FooEntity"); + ecs_entity_t e = ecs_lookup(world, "simple.module.FooEntity"); test_assert(e != 0); test_assert(e == SimpleFooEntity); @@ -232,7 +232,7 @@ void Modules_name_prefix_prefab(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.FooPrefab"); + ecs_entity_t e = ecs_lookup(world, "simple.module.FooPrefab"); test_assert(e != 0); test_assert(e == SimpleFooPrefab); @@ -244,7 +244,7 @@ void Modules_name_prefix_pipeline(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.FooPipeline"); + ecs_entity_t e = ecs_lookup(world, "simple.module.FooPipeline"); test_assert(e != 0); test_assert(e == SimpleFooPipeline); @@ -256,7 +256,7 @@ void Modules_name_prefix_trigger(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.FooTrigger"); + ecs_entity_t e = ecs_lookup(world, "simple.module.FooTrigger"); test_assert(e != 0); test_assert(e == ecs_id(SimpleFooTrigger)); @@ -268,7 +268,7 @@ void Modules_name_prefix_underscore(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "simple.module.underscore"); + ecs_entity_t e = ecs_lookup(world, "simple.module.underscore"); test_assert(e != 0); test_assert(e == Simple_underscore); @@ -296,10 +296,10 @@ void Modules_nested_module(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t e = ecs_lookup_fullpath(world, "nested.module.Component"); + ecs_entity_t e = ecs_lookup(world, "nested.module.Component"); test_assert(e != 0); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, "nested.module.Component"); ecs_os_free(path); @@ -311,11 +311,11 @@ void Modules_module_tag_on_namespace(void) { ECS_IMPORT(world, SimpleModule); - ecs_entity_t mid = ecs_lookup_fullpath(world, "simple.module"); + ecs_entity_t mid = ecs_lookup(world, "simple.module"); test_assert(mid != 0); test_assert(ecs_has_id(world, mid, EcsModule)); - ecs_entity_t nid = ecs_lookup_fullpath(world, "simple"); + ecs_entity_t nid = ecs_lookup(world, "simple"); test_assert(nid != 0); test_assert(ecs_has_id(world, nid, EcsModule)); @@ -325,7 +325,7 @@ void Modules_module_tag_on_namespace(void) { void Modules_module_tag_on_namespace_on_add(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -341,7 +341,7 @@ void Modules_module_tag_on_namespace_on_add(void) { void Modules_module_tag_on_namespace_on_add_2_levels(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); test_assert(root != 0); ecs_entity_t parent = ecs_new_w_pair(world, EcsChildOf, root); @@ -365,18 +365,18 @@ void Modules_import_2_worlds(void) { ECS_IMPORT(world_1, SimpleModule); ECS_IMPORT(world_2, SimpleModule); - test_assert(ecs_lookup_fullpath(world_1, "simple.module") != 0); - test_assert(ecs_lookup_fullpath(world_2, "simple.module") != 0); + test_assert(ecs_lookup(world_1, "simple.module") != 0); + test_assert(ecs_lookup(world_2, "simple.module") != 0); { - ecs_entity_t e = ecs_new(world_1, Position); + ecs_entity_t e = ecs_new_w(world_1, Position); test_assert(e != 0); test_assert( ecs_has(world_1, e, Position)); ecs_add(world_1, e, Velocity); test_assert( ecs_has(world_1, e, Velocity)); } { - ecs_entity_t e = ecs_new(world_2, Position); + ecs_entity_t e = ecs_new_w(world_2, Position); test_assert(e != 0); test_assert( ecs_has(world_2, e, Position)); ecs_add(world_2, e, Velocity); @@ -391,11 +391,11 @@ void Modules_import_monitor_2_worlds(void) { ecs_world_t *world_1 = ecs_init(); ecs_world_t *world_2 = ecs_init(); - ECS_IMPORT(world_1, FlecsMonitor); - ECS_IMPORT(world_2, FlecsMonitor); + ECS_IMPORT(world_1, FlecsStats); + ECS_IMPORT(world_2, FlecsStats); - test_assert(ecs_exists(world_1, ecs_id(FlecsMonitor))); - test_assert(ecs_exists(world_2, ecs_id(FlecsMonitor))); + test_assert(ecs_exists(world_1, ecs_id(FlecsStats))); + test_assert(ecs_exists(world_2, ecs_id(FlecsStats))); ecs_fini(world_1); ecs_fini(world_2); @@ -404,9 +404,26 @@ void Modules_import_monitor_2_worlds(void) { void Modules_import_monitor_after_mini(void) { ecs_world_t *world = ecs_mini(); - ECS_IMPORT(world, FlecsMonitor); + ECS_IMPORT(world, FlecsStats); - test_assert(ecs_exists(world, ecs_id(FlecsMonitor))); + test_assert(ecs_exists(world, ecs_id(FlecsStats))); + + ecs_fini(world); +} + +void Modules_component_parent_becomes_module(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t parent = ecs_new(world); + ecs_entity_t comp = ecs_new_w_pair(world, EcsChildOf, parent); + + test_assert(!ecs_has_id(world, parent, EcsModule)); + test_assert(!ecs_has_id(world, comp, ecs_id(EcsComponent))); + + ecs_set(world, comp, EcsComponent, {4, 4}); + + test_assert(ecs_has_id(world, parent, EcsModule)); + test_assert(ecs_has_id(world, comp, ecs_id(EcsComponent))); ecs_fini(world); } diff --git a/vendors/flecs/test/addons/src/MultiTaskThread.c b/vendors/flecs/test/addons/src/MultiTaskThread.c deleted file mode 100644 index e23f331d2..000000000 --- a/vendors/flecs/test/addons/src/MultiTaskThread.c +++ /dev/null @@ -1,1384 +0,0 @@ -#include - -static ECS_COMPONENT_DECLARE(Position); -static ECS_DECLARE(Tag); - -void MultiTaskThread_setup(void) { - ecs_log_set_level(-3); -} - -void MultiTaskThread_Progress(ecs_iter_t *it) { - Position *pos = ecs_field(it, Position, 1); - int row; - for (row = 0; row < it->count; row ++) { - Position *p = &pos[row]; - p->x ++; - } -} - -static -ecs_world_t* init_world(void) { - ecs_world_t *world = ecs_init(); - ECS_COMPONENT_DEFINE(world, Position); - - ECS_SYSTEM(world, MultiTaskThread_Progress, EcsOnUpdate, Position); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = MultiTaskThread_Progress, - .multi_threaded = true - }); - - return world; -} - -void MultiTaskThread_2_thread_10_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 10, THREADS = 2; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_2_thread_1_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 1, THREADS = 2; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - -void MultiTaskThread_2_thread_2_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 2, THREADS = 2; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - -void MultiTaskThread_2_thread_5_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 5, THREADS = 2; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_3_thread_10_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 10, THREADS = 3; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_3_thread_1_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 1, THREADS = 3; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_3_thread_2_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 2, THREADS = 3; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_3_thread_5_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 5, THREADS = 3; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_4_thread_10_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 10, THREADS = 4; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_4_thread_1_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 1, THREADS = 4; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_4_thread_2_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 2, THREADS = 4; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_4_thread_5_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 5, THREADS = 4; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_5_thread_10_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 10, THREADS = 5; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_5_thread_1_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 1, THREADS = 5; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_5_thread_2_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 2, THREADS = 5; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_5_thread_5_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 5, THREADS = 5; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_6_thread_10_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 10, THREADS = 6; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_6_thread_1_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 1, THREADS = 6; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - - -void MultiTaskThread_6_thread_2_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 2, THREADS = 6; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - -void MultiTaskThread_6_thread_5_entity(void) { - ecs_world_t *world = init_world(); - - int i, ENTITIES = 5, THREADS = 6; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - -void MultiTaskThread_2_thread_1_entity_instanced(void) { - ecs_world_t *world = ecs_init(); - ECS_COMPONENT_DEFINE(world, Position); - - ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), - .callback = MultiTaskThread_Progress, - .query.filter = { - .expr = "Position", - .instanced = true - }, - .multi_threaded = true - }); - test_assert(s != 0); - - int i, ENTITIES = 1, THREADS = 2; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - -void MultiTaskThread_2_thread_5_entity_instanced(void) { - ecs_world_t *world = ecs_init(); - ECS_COMPONENT_DEFINE(world, Position); - - ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), - .callback = MultiTaskThread_Progress, - .query.filter = { - .expr = "Position", - .instanced = true - }, - .multi_threaded = true - }); - test_assert(s != 0); - - int i, ENTITIES = 5, THREADS = 2; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - -void MultiTaskThread_2_thread_10_entity_instanced(void) { - ecs_world_t *world = ecs_init(); - ECS_COMPONENT_DEFINE(world, Position); - - ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), - .callback = MultiTaskThread_Progress, - .query.filter = { - .expr = "Position", - .instanced = true - }, - .multi_threaded = true - }); - test_assert(s != 0); - - int i, ENTITIES = 10, THREADS = 2; - ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); - ecs_set(world, handles[i], Position, {0}); - } - - ecs_set_task_threads(world, THREADS); - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 1); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - test_int(ecs_get(world, handles[i], Position)->x, 2); - } - - ecs_fini(world); -} - -typedef struct Param { - ecs_entity_t entity; - int count; -} Param; - -static -void TestSubset(ecs_iter_t *it) { - Param *param = it->param; - - int i; - for (i = 0; i < it->count; i ++) { - test_assert(param->entity != it->entities[i]); - param->count ++; - } -} - -static -void TestAll(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - - ecs_entity_t TestSubset = ecs_field_id(it, 2); - - int i; - for (i = 0; i < it->count; i ++) { - Param param = {.entity = it->entities[i], 0}; - ecs_run_w_filter(it->world, TestSubset, 1, it->frame_offset + i + 1, 0, ¶m); - p[i].x += param.count; - } -} - -static -void test_combs_100_entity(int THREADS) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT_DEFINE(world, Position); - - ECS_SYSTEM(world, TestSubset, 0, Position); - ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position, TestSubset()); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = TestAll, - .multi_threaded = true - }); - - int i, ENTITIES = 100; - - const ecs_entity_t *ids = ecs_bulk_new(world, Position, ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - ecs_set(world, ids[i], Position, {1, 2}); - } - - ecs_set_task_threads(world, THREADS); - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - const Position *p = ecs_get(world, ids[i], Position); - test_int(p->x, ENTITIES - i); - } - - ecs_fini(world); -} - -void MultiTaskThread_2_thread_test_combs_100_entity_w_next_worker(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT_DEFINE(world, Position); - - ECS_SYSTEM(world, TestSubset, 0, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, TestSubset()"); - - int i, ENTITIES = 100; - - const ecs_entity_t *ids = ecs_bulk_new(world, Position, ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - ecs_set(world, ids[i], Position, {1, 2}); - } - - ecs_iter_t it = ecs_query_iter(world, q); - ecs_iter_t wit = ecs_worker_iter(&it, 0, 2); - while (ecs_worker_next(&wit)) { - TestAll(&wit); - } - - it = ecs_query_iter(world, q); - wit = ecs_worker_iter(&it, 1, 2); - while (ecs_worker_next(&wit)) { - TestAll(&wit); - } - - for (i = 0; i < ENTITIES; i ++) { - const Position *p = ecs_get(world, ids[i], Position); - test_int(p->x, ENTITIES - i); - } - - ecs_fini(world); -} - -void MultiTaskThread_2_thread_test_combs_100_entity(void) { - test_combs_100_entity(2); -} - -void MultiTaskThread_3_thread_test_combs_100_entity(void) { - test_combs_100_entity(3); -} - -void MultiTaskThread_4_thread_test_combs_100_entity(void) { - test_combs_100_entity(4); -} - -void MultiTaskThread_5_thread_test_combs_100_entity(void) { - test_combs_100_entity(5); -} - -void MultiTaskThread_6_thread_test_combs_100_entity(void) { - test_combs_100_entity(6); -} - -static -void test_combs_100_entity_2_types(int THREADS) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_PREFAB(world, Type, Position, Velocity); - - ECS_SYSTEM(world, TestSubset, 0, Position); - ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position, TestSubset()); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = TestAll, - .multi_threaded = true - }); - - int i, ENTITIES = 100; - - const ecs_entity_t *temp_ids_1 = ecs_bulk_new(world, Position, ENTITIES / 2); - ecs_entity_t ids_1[50]; - memcpy(ids_1, temp_ids_1, sizeof(ecs_entity_t) * ENTITIES / 2); - const ecs_entity_t *ids_2 = bulk_new_w_type(world, Type, ENTITIES / 2); - - for (i = 0; i < ENTITIES / 2; i ++) { - ecs_set(world, ids_1[i], Position, {1, 2}); - ecs_set(world, ids_2[i], Position, {1, 2}); - } - - ecs_set_task_threads(world, THREADS); - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES / 2; i ++) { - const Position *p = ecs_get(world, ids_1[i], Position); - test_int(p->x, ENTITIES - i); - - p = ecs_get(world, ids_2[i], Position); - test_int(p->x, ENTITIES - (i + ENTITIES / 2)); - } - - ecs_fini(world); -} - -void MultiTaskThread_2_thread_test_combs_100_entity_2_types(void) { - test_combs_100_entity_2_types(2); -} - -void MultiTaskThread_3_thread_test_combs_100_entity_2_types(void) { - test_combs_100_entity_2_types(3); -} - -void MultiTaskThread_4_thread_test_combs_100_entity_2_types(void) { - test_combs_100_entity_2_types(4); -} - -void MultiTaskThread_5_thread_test_combs_100_entity_2_types(void) { - test_combs_100_entity_2_types(5); -} - -void MultiTaskThread_6_thread_test_combs_100_entity_2_types(void) { - test_combs_100_entity_2_types(6); -} - -void MultiTaskThread_change_thread_count(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_PREFAB(world, Type, Position, Velocity); - - ECS_SYSTEM(world, TestSubset, 0, Position); - ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position, TestSubset()); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = TestAll, - .multi_threaded = true - }); - - int i, ENTITIES = 100; - - const ecs_entity_t *temp_ids_1 = ecs_bulk_new(world, Position, ENTITIES / 2); - ecs_entity_t ids_1[50]; - memcpy(ids_1, temp_ids_1, sizeof(ecs_entity_t) * ENTITIES / 2); - const ecs_entity_t *ids_2 = bulk_new_w_type(world, Type, ENTITIES / 2); - - for (i = 0; i < ENTITIES / 2; i ++) { - ecs_set(world, ids_1[i], Position, {1, 2}); - ecs_set(world, ids_2[i], Position, {1, 2}); - } - - ecs_set_task_threads(world, 2); - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES / 2; i ++) { - Position *p = ecs_get_mut(world, ids_1[i], Position); - test_int(p->x, ENTITIES - i); - p->x = 1; - - p = ecs_get_mut(world, ids_2[i], Position); - test_int(p->x, ENTITIES - (i + ENTITIES / 2)); - p->x = 1; - } - - ecs_set_task_threads(world, 3); - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES / 2; i ++) { - const Position *p = ecs_get(world, ids_1[i], Position); - test_int(p->x, ENTITIES - i); - - p = ecs_get(world, ids_2[i], Position); - test_int(p->x, ENTITIES - (i + ENTITIES / 2)); - } - - ecs_fini(world); -} - -static -void QuitSystem(ecs_iter_t *it) { - ecs_quit(it->world); -} - -void MultiTaskThread_multithread_quit(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT_DEFINE(world, Position); - - ECS_SYSTEM(world, QuitSystem, EcsOnUpdate, Position); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = QuitSystem, - .multi_threaded = true - }); - - ecs_bulk_new(world, Position, 100); - - ecs_set_task_threads(world, 2); - - test_assert( ecs_progress(world, 0) == 0); - - ecs_fini(world); -} - -static bool has_ran = false; - -static -void MtTask(ecs_iter_t *it) { - has_ran = true; -} - -void MultiTaskThread_schedule_w_tasks(void) { - ecs_world_t *world = ecs_init(); - - ECS_SYSTEM(world, MtTask, EcsOnUpdate, 0); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = MtTask, - .multi_threaded = true - }); - - ecs_set_task_threads(world, 2); - - test_assert( ecs_progress(world, 0) != 0); - - test_assert( has_ran == true); - - ecs_fini(world); -} - -static -void ReactiveDummySystem(ecs_iter_t * it) { - has_ran = true; -} - -static -void PeriodicDummySystem(ecs_iter_t * it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); - - int i; - for (i = 0; i < it->count; i++ ) { - ecs_set(it->world, it->entities[i], Position, {0}); - } -} - -void MultiTaskThread_reactive_system(void) { - ecs_world_t * world = ecs_init(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_SYSTEM(world, PeriodicDummySystem, EcsOnUpdate, Position); - ECS_OBSERVER(world, ReactiveDummySystem, EcsOnSet, Position); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = PeriodicDummySystem, - .multi_threaded = true - }); - - ecs_bulk_new(world, Position, 2); - ecs_set_task_threads(world, 2); - - test_assert(has_ran == false); - - ecs_progress(world, 0); - - test_assert(has_ran == true); - - ecs_fini(world); -} - -void MultiTaskThread_fini_after_set_threads(void) { - ecs_world_t * world = ecs_init(); - - ecs_set_task_threads(world, 2); - - ecs_fini(world); - - // Make sure code doesn't crash - test_assert(true); -} - -static -void SingleThreadedSystem(ecs_iter_t * it) { - Position *p = ecs_field(it, Position, 1); - - int i; - for (i = 0; i < it->count; i++ ) { - p[i].x ++; - p[i].y ++; - } -} - -void MultiTaskThread_2_threads_single_threaded_system(void) { - ecs_world_t * world = ecs_init(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_SYSTEM(world, SingleThreadedSystem, EcsOnUpdate, Position); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = SingleThreadedSystem, - .multi_threaded = false - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - - ecs_set_task_threads(world, 2); - - ecs_progress(world, 0); - - const Position *p = ecs_get(world, e1, Position); - test_assert(p != NULL); - test_int(p->x, 11); - test_int(p->y, 21); - - p = ecs_get(world, e2, Position); - test_assert(p != NULL); - test_int(p->x, 21); - test_int(p->y, 31); - - ecs_fini(world); -} - -static int create_query_invoked = 0; - -static -void CreateQuery(ecs_iter_t *it) { - ecs_query_new(it->world, "0"); - create_query_invoked ++; -} - -void MultiTaskThread_no_staging_w_multithread(void) { - for (int i = 0; i < 10; i ++) { - ecs_world_t *world = ecs_init(); - - ecs_set_task_threads(world, 32); - - ecs_system_init(world, &(ecs_system_desc_t){ - .callback = CreateQuery, - .no_readonly = true, - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}) - }); - - create_query_invoked = 0; - ecs_progress(world, 0); - test_int(create_query_invoked, 1); - - ecs_fini(world); - } - - test_assert(true); -} - -void MultiTaskThread_multithread_w_monitor_addon(void) { - ecs_world_t *world = ecs_init(); - ECS_IMPORT(world, FlecsMonitor); - ecs_set_task_threads(world, 4); - ecs_progress(world, 0); - ecs_fini(world); - - /* Make sure monitor could be run in multithreaded mode */ - test_assert(true); -} - -static int system_ctx = 0; - -static -void System_w_ctx(ecs_iter_t *it) { - test_assert(it->ctx == &system_ctx); - test_assert(it->system != 0); - test_assert(it->delta_time != 0); - system_ctx ++; -} - -static -void System_run_w_ctx(ecs_iter_t *it) { - System_w_ctx(it); - ecs_iter_fini(it); -} - -static -void System_w_binding_ctx(ecs_iter_t *it) { - test_assert(it->binding_ctx == &system_ctx); - test_assert(it->system != 0); - test_assert(it->delta_time != 0); - system_ctx ++; -} - -static -void System_run_w_binding_ctx(ecs_iter_t *it) { - System_w_binding_ctx(it); - ecs_iter_fini(it); -} - -void MultiTaskThread_get_ctx(void) { - ecs_world_t *world = ecs_init(); - - ecs_set_task_threads(world, 2); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) } }), - .callback = System_w_ctx, - .multi_threaded = true, - .ctx = &system_ctx - }); - - ecs_progress(world, 0); - - test_assert(system_ctx != 0); - - ecs_fini(world); -} - -void MultiTaskThread_get_binding_ctx(void) { - ecs_world_t *world = ecs_init(); - - ecs_set_task_threads(world, 2); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) } }), - .callback = System_w_binding_ctx, - .multi_threaded = true, - .binding_ctx = &system_ctx - }); - - ecs_progress(world, 0); - - test_assert(system_ctx != 0); - - ecs_fini(world); -} - -void MultiTaskThread_get_ctx_w_run(void) { - ecs_world_t *world = ecs_init(); - - ecs_set_task_threads(world, 2); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) } }), - .run = System_run_w_ctx, - .multi_threaded = true, - .ctx = &system_ctx - }); - - ecs_progress(world, 0); - - test_assert(system_ctx != 0); - - ecs_fini(world); -} - -void MultiTaskThread_get_binding_ctx_w_run(void) { - ecs_world_t *world = ecs_init(); - - ecs_set_task_threads(world, 2); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) } }), - .run = System_run_w_binding_ctx, - .multi_threaded = true, - .binding_ctx = &system_ctx - }); - - ecs_progress(world, 0); - - test_assert(system_ctx != 0); - - ecs_fini(world); -} - -void MultiTaskThread_sys(ecs_iter_t *it) { } - -void MultiTaskThread_sys_bulk_init(ecs_iter_t *it) { - ecs_bulk_init(it->world, &(ecs_bulk_desc_t){ - .count = 10, - .ids = {Tag} - }); -} - -void MultiTaskThread_bulk_new_in_no_readonly_w_multithread(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG_DEFINE(world, Tag); - - ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), - .no_readonly = true, - .callback = MultiTaskThread_sys_bulk_init - }); - - ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), - .multi_threaded = true, - .callback = MultiTaskThread_sys - }); - - ecs_set_task_threads(world, 80); - - for (int i = 0; i < 100; i ++) { - ecs_progress(world, 0); - } - - test_int(ecs_count(world, Tag), 100 * 10); - - ecs_fini(world); -} - -void MultiTaskThread_sys_bulk_init_2(ecs_iter_t *it) { - ecs_bulk_init(it->world, &(ecs_bulk_desc_t){ - .count = 1, - .ids = {ecs_id(Position)} - }); - - ecs_iter_t qit = ecs_query_iter(it->world, it->ctx); - ecs_iter_fini(&qit); -} - -void MultiTaskThread_bulk_new_in_no_readonly_w_multithread_2(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT_DEFINE(world, Position); - - ecs_set_task_threads(world, 64); - - ecs_system(world, { - .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } - }), - .query.filter.terms = {{ ecs_id(Position) }}, - .callback = MultiTaskThread_sys - }); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ ecs_id(Position) }} - }); - - ecs_system(world, { - .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } - }), - .callback = MultiTaskThread_sys_bulk_init_2, - .no_readonly = true, - .ctx = q - }); - - ecs_system(world, { - .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } - }), - .callback = MultiTaskThread_sys - }); - - ecs_progress(world, 0); - - test_int(ecs_count(world, Position), 1); - - ecs_fini(world); -} - -static ecs_os_thread_id_t main_thread; -static int invoked_count = 0; -static int invoked_main_count = 0; - -static void dummy(ecs_iter_t *it) { - int stage_id = ecs_get_stage_id(it->world); - - if (stage_id == 0) { - test_assert(main_thread == ecs_os_thread_self()); - ecs_os_ainc(&invoked_main_count); - } else { - test_assert(main_thread != ecs_os_thread_self()); - } - - ecs_os_ainc(&invoked_count); -} - -void MultiTaskThread_run_first_worker_on_main(void) { - ecs_world_t *world = ecs_init(); - - ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), - .multi_threaded = true, - .callback = dummy - }); - - ecs_set_task_threads(world, 3); - - main_thread = ecs_os_thread_self(); - - ecs_progress(world, 0); - - test_int(invoked_count, 3); - test_int(invoked_main_count, 1); - - ecs_fini(world); -} - -void MultiTaskThread_run_single_thread_on_main(void) { - ecs_world_t *world = ecs_init(); - - ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), - .multi_threaded = false, - .callback = dummy - }); - - ecs_set_task_threads(world, 3); - - main_thread = ecs_os_thread_self(); - - ecs_progress(world, 0); - - test_int(invoked_count, 1); - test_int(invoked_main_count, 1); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/MultiTaskThreadStaging.c b/vendors/flecs/test/addons/src/MultiTaskThreadStaging.c deleted file mode 100644 index 0e6512911..000000000 --- a/vendors/flecs/test/addons/src/MultiTaskThreadStaging.c +++ /dev/null @@ -1,613 +0,0 @@ -#include - -void MultiTaskThreadStaging_setup(void) { - ecs_log_set_level(-3); -} - -static -void MultiTaskThread_Add_to_current(ecs_iter_t *it) { - IterData *ctx = ecs_get_ctx(it->world); - - int i; - for (i = 0; i < it->count; i ++) { - if (ctx->component) { - ecs_add_id(it->world, it->entities[i], ctx->component); - } - - if (ctx->component_2) { - ecs_add_id(it->world, it->entities[i], ctx->component_2); - } - - ctx->entity_count ++; - } -} - -void MultiTaskThreadStaging_2_threads_add_to_current(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Rotation); - ECS_PREFAB(world, Type, Position, Velocity); - - ECS_SYSTEM(world, MultiTaskThread_Add_to_current, EcsOnUpdate, Position); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = MultiTaskThread_Add_to_current, - .multi_threaded = true - }); - - IterData ctx = {.component = ecs_id(Rotation)}; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t ids_1[100]; - const ecs_entity_t *temp_ids_1 = ecs_bulk_new(world, Position, 100); - memcpy(ids_1, temp_ids_1, sizeof(ecs_entity_t) * 100); - - const ecs_entity_t *ids_2 = bulk_new_w_type(world, Type, 100); - - ecs_set_task_threads(world, 2); - - ecs_progress(world, 1); - - int i; - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_1[i], Position)); - test_assert( ecs_has(world, ids_1[i], Rotation)); - test_assert( !ecs_has(world, ids_1[i], Velocity)); - } - - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_2[i], Position)); - test_assert( ecs_has(world, ids_2[i], Rotation)); - test_assert( ecs_has(world, ids_2[i], Velocity)); - } - - ecs_fini(world); -} - -void MultiTaskThreadStaging_3_threads_add_to_current(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Rotation); - ECS_PREFAB(world, Type, Position, Velocity); - - ECS_SYSTEM(world, MultiTaskThread_Add_to_current, EcsOnUpdate, Position); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = MultiTaskThread_Add_to_current, - .multi_threaded = true - }); - - IterData ctx = {.component = ecs_id(Rotation)}; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t ids_1[100]; - const ecs_entity_t *temp_ids_1 = ecs_bulk_new(world, Position, 100); - memcpy(ids_1, temp_ids_1, sizeof(ecs_entity_t) * 100); - - const ecs_entity_t *ids_2 = bulk_new_w_type(world, Type, 100); - - ecs_set_task_threads(world, 3); - - ecs_progress(world, 1); - - int i; - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_1[i], Position)); - test_assert( ecs_has(world, ids_1[i], Rotation)); - test_assert( !ecs_has(world, ids_1[i], Velocity)); - } - - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_2[i], Position)); - test_assert( ecs_has(world, ids_2[i], Rotation)); - test_assert( ecs_has(world, ids_2[i], Velocity)); - } - - ecs_fini(world); -} - -void MultiTaskThreadStaging_4_threads_add_to_current(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Rotation); - ECS_PREFAB(world, Type, Position, Velocity); - - ECS_SYSTEM(world, MultiTaskThread_Add_to_current, EcsOnUpdate, Position); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = MultiTaskThread_Add_to_current, - .multi_threaded = true - }); - - IterData ctx = {.component = ecs_id(Rotation)}; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t ids_1[100]; - const ecs_entity_t *temp_ids_1 = ecs_bulk_new(world, Position, 100); - memcpy(ids_1, temp_ids_1, sizeof(ecs_entity_t) * 100); - - const ecs_entity_t *ids_2 = bulk_new_w_type(world, Type, 100); - - ecs_set_task_threads(world, 4); - - ecs_progress(world, 1); - - int i; - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_1[i], Position)); - test_assert( ecs_has(world, ids_1[i], Rotation)); - test_assert( !ecs_has(world, ids_1[i], Velocity)); - } - - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_2[i], Position)); - test_assert( ecs_has(world, ids_2[i], Rotation)); - test_assert( ecs_has(world, ids_2[i], Velocity)); - } - - ecs_fini(world); -} - -void MultiTaskThreadStaging_5_threads_add_to_current(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Rotation); - ECS_PREFAB(world, Type, Position, Velocity); - - ECS_SYSTEM(world, MultiTaskThread_Add_to_current, EcsOnUpdate, Position); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = MultiTaskThread_Add_to_current, - .multi_threaded = true - }); - - IterData ctx = {.component = ecs_id(Rotation)}; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t ids_1[100]; - const ecs_entity_t *temp_ids_1 = ecs_bulk_new(world, Position, 100); - memcpy(ids_1, temp_ids_1, sizeof(ecs_entity_t) * 100); - - const ecs_entity_t *ids_2 = bulk_new_w_type(world, Type, 100); - - ecs_set_task_threads(world, 5); - - ecs_progress(world, 1); - - int i; - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_1[i], Position)); - test_assert( ecs_has(world, ids_1[i], Rotation)); - test_assert( !ecs_has(world, ids_1[i], Velocity)); - } - - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_2[i], Position)); - test_assert( ecs_has(world, ids_2[i], Rotation)); - test_assert( ecs_has(world, ids_2[i], Velocity)); - } - - ecs_fini(world); -} - -void MultiTaskThreadStaging_6_threads_add_to_current(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Rotation); - ECS_PREFAB(world, Type, Position, Velocity); - - ECS_SYSTEM(world, MultiTaskThread_Add_to_current, EcsOnUpdate, Position); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = MultiTaskThread_Add_to_current, - .multi_threaded = true - }); - - IterData ctx = {.component = ecs_id(Rotation)}; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t ids_1[100]; - const ecs_entity_t *temp_ids_1 = ecs_bulk_new(world, Position, 100); - memcpy(ids_1, temp_ids_1, sizeof(ecs_entity_t) * 100); - - const ecs_entity_t *ids_2 = bulk_new_w_type(world, Type, 100); - - ecs_set_task_threads(world, 6); - - ecs_progress(world, 1); - - int i; - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_1[i], Position)); - test_assert( ecs_has(world, ids_1[i], Rotation)); - test_assert( !ecs_has(world, ids_1[i], Velocity)); - } - - for (i = 0; i < 100; i ++) { - test_assert( ecs_has(world, ids_2[i], Position)); - test_assert( ecs_has(world, ids_2[i], Rotation)); - test_assert( ecs_has(world, ids_2[i], Velocity)); - } - - ecs_fini(world); -} - -static -void InitVelocity(ecs_iter_t *it) { - Velocity *v = ecs_field(it, Velocity, 1); - - int i; - for (i = 0; i < it->count; i ++) { - v[i].x = 10; - v[i].y = 20; - } -} - -static -void AddVelocity(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); - - int i; - for (i = 0; i < it->count; i ++) { - ecs_add(it->world, it->entities[i], Velocity); - } -} - -void MultiTaskThreadStaging_2_threads_on_add(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ECS_OBSERVER(world, InitVelocity, EcsOnAdd, Velocity); - ECS_SYSTEM(world, AddVelocity, EcsOnUpdate, Position, Velocity()); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = AddVelocity, - .multi_threaded = true - }); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - const ecs_entity_t *ids = ecs_bulk_new(world, Position, 10); - test_assert(ids != NULL); - - ecs_set_task_threads(world, 2); - - ecs_progress(world, 0); - - int i; - for (i = 0; i < 10; i ++) { - ecs_entity_t e = ids[i]; - test_assert( ecs_has(world, e, Velocity)); - const Velocity *v = ecs_get(world, e, Velocity); - test_assert(v != NULL); - test_int(v->x, 10); - test_int(v->y, 20); - } - - ecs_fini(world); -} - -static -void New_w_count(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); - - ecs_bulk_new(it->world, Position, 10); -} - -void MultiTaskThreadStaging_new_w_count(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_SYSTEM(world, New_w_count, EcsOnUpdate, Position()); - - ecs_system_init(world, &(ecs_system_desc_t){ - .entity = New_w_count, - .multi_threaded = true - }); - - ecs_set_task_threads(world, 2); - - ecs_progress(world, 0); - - test_int( ecs_count(world, Position), 10); - - ecs_fini(world); -} - -void MultiTaskThreadStaging_custom_thread_auto_merge(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_set_stage_count(world, 2); - - ecs_world_t *ctx_1 = ecs_get_stage(world, 0); - ecs_world_t *ctx_2 = ecs_get_stage(world, 1); - - ecs_frame_begin(world, 0); - ecs_readonly_begin(world); - - /* thread 1 */ - ecs_defer_begin(ctx_1); - ecs_set(ctx_1, e1, Position, {10, 20}); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - ecs_defer_end(ctx_1); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - - /* thread 2 */ - ecs_defer_begin(ctx_2); - ecs_set(ctx_2, e2, Position, {20, 30}); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - ecs_defer_end(ctx_2); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - - ecs_readonly_end(world); - ecs_frame_end(world); - - test_assert(ecs_has(world, e1, Position)); - test_assert(ecs_has(world, e2, Position)); - - const Position *p1 = ecs_get(world, e1, Position); - test_int(p1->x, 10); - test_int(p1->y, 20); - - const Position *p2 = ecs_get(world, e2, Position); - test_int(p2->x, 20); - test_int(p2->y, 30); - - ecs_fini(world); -} - -void MultiTaskThreadStaging_custom_thread_manual_merge(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_set_automerge(world, false); - - ecs_set_stage_count(world, 2); - - ecs_world_t *ctx_1 = ecs_get_stage(world, 0); - ecs_world_t *ctx_2 = ecs_get_stage(world, 1); - - ecs_frame_begin(world, 0); - ecs_readonly_begin(world); - - /* thread 1 */ - ecs_defer_begin(ctx_1); - ecs_set(ctx_1, e1, Position, {10, 20}); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - ecs_defer_end(ctx_1); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - - /* thread 2 */ - ecs_defer_begin(ctx_2); - ecs_set(ctx_2, e2, Position, {20, 30}); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - ecs_defer_end(ctx_2); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - - ecs_readonly_end(world); - ecs_frame_end(world); - - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(world, e2, Position)); - - ecs_merge(world); - - test_assert(ecs_has(world, e1, Position)); - test_assert(ecs_has(world, e2, Position)); - - const Position *p1 = ecs_get(world, e1, Position); - test_int(p1->x, 10); - test_int(p1->y, 20); - - const Position *p2 = ecs_get(world, e2, Position); - test_int(p2->x, 20); - test_int(p2->y, 30); - - ecs_fini(world); -} - -void MultiTaskThreadStaging_custom_thread_partial_manual_merge(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_set_stage_count(world, 2); - - ecs_world_t *ctx_1 = ecs_get_stage(world, 0); - ecs_world_t *ctx_2 = ecs_get_stage(world, 1); - - /* Only disable automerging for ctx_2 */ - ecs_set_automerge(ctx_2, false); - - ecs_frame_begin(world, 0); - ecs_readonly_begin(world); - - /* thread 1 */ - ecs_defer_begin(ctx_1); - ecs_set(ctx_1, e1, Position, {10, 20}); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - ecs_defer_end(ctx_1); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - - /* thread 2 */ - ecs_defer_begin(ctx_2); - ecs_set(ctx_2, e2, Position, {20, 30}); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - ecs_defer_end(ctx_2); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - - ecs_readonly_end(world); - ecs_frame_end(world); - - test_assert(ecs_has(world, e1, Position)); - test_assert(!ecs_has(world, e2, Position)); - - const Position *p1 = ecs_get(world, e1, Position); - test_int(p1->x, 10); - test_int(p1->y, 20); - - ecs_merge(ctx_2); - - test_assert(ecs_has(world, e1, Position)); - test_assert(ecs_has(world, e2, Position)); - - p1 = ecs_get(world, e1, Position); - test_int(p1->x, 10); - test_int(p1->y, 20); - - const Position *p2 = ecs_get(world, e2, Position); - test_int(p2->x, 20); - test_int(p2->y, 30); - - ecs_fini(world); -} - -void MultiTaskThreadStaging_set_pair_w_new_target_readonly(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_set_task_threads(world, 2); - - ecs_entity_t e = ecs_new_id(world); - - ecs_world_t *thr_1 = ecs_get_stage(world, 0); - - ecs_frame_begin(world, 0); - ecs_readonly_begin(world); - - ecs_entity_t tgt = ecs_new_id(thr_1); - ecs_set_pair(thr_1, e, Position, tgt, {10, 20}); - - ecs_readonly_end(world); - ecs_frame_end(world); - - test_assert(ecs_has_pair(world, e, ecs_id(Position), tgt)); - - const Position *p = ecs_get_pair(world, e, Position, tgt); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void MultiTaskThreadStaging_set_pair_w_new_target_tgt_component_readonly(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_set_task_threads(world, 2); - - ecs_entity_t e = ecs_new_id(world); - - ecs_world_t *thr_1 = ecs_get_stage(world, 0); - - ecs_frame_begin(world, 0); - ecs_readonly_begin(world); - - ecs_entity_t tgt = ecs_new_id(thr_1); - ecs_set_pair_second(thr_1, e, tgt, Position, {10, 20}); - - ecs_readonly_end(world); - ecs_frame_end(world); - - test_assert(ecs_has_pair(world, e, tgt, ecs_id(Position))); - - const Position *p = ecs_get_pair_second(world, e, tgt, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void MultiTaskThreadStaging_set_pair_w_new_target_defer(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_set_task_threads(world, 2); - - ecs_entity_t e = ecs_new_id(world); - - ecs_defer_begin(world); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_set_pair(world, e, Position, tgt, {10, 20}); - - ecs_defer_end(world); - - test_assert(ecs_has_pair(world, e, ecs_id(Position), tgt)); - - const Position *p = ecs_get_pair(world, e, Position, tgt); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void MultiTaskThreadStaging_set_pair_w_new_target_tgt_component_defer(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_set_task_threads(world, 2); - - ecs_entity_t e = ecs_new_id(world); - - ecs_defer_begin(world); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_set_pair_second(world, e, tgt, Position, {10, 20}); - - ecs_defer_end(world); - - test_assert(ecs_has_pair(world, e, tgt, ecs_id(Position))); - - const Position *p = ecs_get_pair_second(world, e, tgt, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/MultiThread.c b/vendors/flecs/test/addons/src/MultiThread.c index 8c94403f5..cfd523bd7 100644 --- a/vendors/flecs/test/addons/src/MultiThread.c +++ b/vendors/flecs/test/addons/src/MultiThread.c @@ -7,8 +7,18 @@ void MultiThread_setup(void) { ecs_log_set_level(-3); } +static +void set_worker_kind(ecs_world_t *world, int32_t thread_count) { + const char *worker_kind = test_param("worker_kind"); + if (!worker_kind || !strcmp(worker_kind, "thread")) { + ecs_set_threads(world, thread_count); + } else if (!strcmp(worker_kind, "task")) { + ecs_set_task_threads(world, thread_count); + } +} + void Progress(ecs_iter_t *it) { - Position *pos = ecs_field(it, Position, 1); + Position *pos = ecs_field(it, Position, 0); int row; for (row = 0; row < it->count; row ++) { Position *p = &pos[row]; @@ -38,11 +48,11 @@ void MultiThread_2_thread_10_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -66,11 +76,11 @@ void MultiThread_2_thread_1_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -93,11 +103,11 @@ void MultiThread_2_thread_2_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -120,11 +130,11 @@ void MultiThread_2_thread_5_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -148,11 +158,11 @@ void MultiThread_3_thread_10_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -176,11 +186,11 @@ void MultiThread_3_thread_1_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -204,11 +214,11 @@ void MultiThread_3_thread_2_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -232,11 +242,11 @@ void MultiThread_3_thread_5_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -260,11 +270,11 @@ void MultiThread_4_thread_10_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -288,11 +298,11 @@ void MultiThread_4_thread_1_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -316,11 +326,11 @@ void MultiThread_4_thread_2_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -344,11 +354,11 @@ void MultiThread_4_thread_5_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -372,11 +382,11 @@ void MultiThread_5_thread_10_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -400,11 +410,11 @@ void MultiThread_5_thread_1_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -428,11 +438,11 @@ void MultiThread_5_thread_2_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -456,11 +466,11 @@ void MultiThread_5_thread_5_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -484,11 +494,11 @@ void MultiThread_6_thread_10_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -512,11 +522,11 @@ void MultiThread_6_thread_1_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -540,11 +550,11 @@ void MultiThread_6_thread_2_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -567,11 +577,11 @@ void MultiThread_6_thread_5_entity(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -592,11 +602,10 @@ void MultiThread_2_thread_1_entity_instanced(void) { ECS_COMPONENT_DEFINE(world, Position); ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}), .callback = Progress, - .query.filter = { + .query = { .expr = "Position", - .instanced = true }, .multi_threaded = true }); @@ -606,11 +615,11 @@ void MultiThread_2_thread_1_entity_instanced(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -631,11 +640,10 @@ void MultiThread_2_thread_5_entity_instanced(void) { ECS_COMPONENT_DEFINE(world, Position); ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}), .callback = Progress, - .query.filter = { + .query = { .expr = "Position", - .instanced = true }, .multi_threaded = true }); @@ -645,11 +653,11 @@ void MultiThread_2_thread_5_entity_instanced(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -670,11 +678,10 @@ void MultiThread_2_thread_10_entity_instanced(void) { ECS_COMPONENT_DEFINE(world, Position); ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}), .callback = Progress, - .query.filter = { + .query = { .expr = "Position", - .instanced = true }, .multi_threaded = true }); @@ -684,11 +691,11 @@ void MultiThread_2_thread_10_entity_instanced(void) { ecs_entity_t *handles = ecs_os_alloca(sizeof(ecs_entity_t) * ENTITIES); for (i = 0; i < ENTITIES; i ++) { - handles[i] = ecs_new(world, Position); + handles[i] = ecs_new_w(world, Position); ecs_set(world, handles[i], Position, {0}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); for (i = 0; i < ENTITIES; i ++) { @@ -709,27 +716,29 @@ typedef struct Param { int count; } Param; -static -void TestSubset(ecs_iter_t *it) { - Param *param = it->param; +static ECS_QUERY_DECLARE(qTestSubset); - int i; - for (i = 0; i < it->count; i ++) { - test_assert(param->entity != it->entities[i]); - param->count ++; - } +static +void TestSubset(ecs_world_t *world, ecs_query_t *q, int32_t offset, int32_t count, Param *param) { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, offset, count); + while (ecs_page_next(&pit)) { + int i; + for (i = 0; i < pit.count; i ++) { + test_assert(param->entity != pit.entities[i]); + param->count ++; + } + } } static void TestAll(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - - ecs_entity_t TestSubset = ecs_field_id(it, 2); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { Param param = {.entity = it->entities[i], 0}; - ecs_run_w_filter(it->world, TestSubset, 1, it->frame_offset + i + 1, 0, ¶m); + TestSubset(it->world, qTestSubset, it->frame_offset + i + 1, 0, ¶m); p[i].x += param.count; } } @@ -740,8 +749,8 @@ void test_combs_100_entity(int THREADS) { ECS_COMPONENT_DEFINE(world, Position); - ECS_SYSTEM(world, TestSubset, 0, Position); - ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position, TestSubset()); + ECS_QUERY_DEFINE(world, qTestSubset, Position); + ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position); ecs_system_init(world, &(ecs_system_desc_t){ .entity = TestAll, @@ -756,7 +765,7 @@ void test_combs_100_entity(int THREADS) { ecs_set(world, ids[i], Position, {1, 2}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); @@ -773,9 +782,9 @@ void MultiThread_2_thread_test_combs_100_entity_w_next_worker(void) { ECS_COMPONENT_DEFINE(world, Position); - ECS_SYSTEM(world, TestSubset, 0, Position); + ECS_QUERY_DEFINE(world, qTestSubset, Position); - ecs_query_t *q = ecs_query_new(world, "Position, TestSubset()"); + ecs_query_t *q = ecs_query(world, { .expr = "Position" }); int i, ENTITIES = 100; @@ -802,6 +811,8 @@ void MultiThread_2_thread_test_combs_100_entity_w_next_worker(void) { test_int(p->x, ENTITIES - i); } + ecs_query_fini(q); + ecs_fini(world); } @@ -833,8 +844,8 @@ void test_combs_100_entity_2_types(int THREADS) { ECS_COMPONENT(world, Velocity); ECS_PREFAB(world, Type, Position, Velocity); - ECS_SYSTEM(world, TestSubset, 0, Position); - ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position, TestSubset()); + ECS_QUERY_DEFINE(world, qTestSubset, Position); + ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position); ecs_system_init(world, &(ecs_system_desc_t){ .entity = TestAll, @@ -853,7 +864,7 @@ void test_combs_100_entity_2_types(int THREADS) { ecs_set(world, ids_2[i], Position, {1, 2}); } - ecs_set_threads(world, THREADS); + set_worker_kind(world, THREADS); ecs_progress(world, 0); @@ -895,8 +906,8 @@ void MultiThread_change_thread_count(void) { ECS_COMPONENT(world, Velocity); ECS_PREFAB(world, Type, Position, Velocity); - ECS_SYSTEM(world, TestSubset, 0, Position); - ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position, TestSubset()); + ECS_QUERY_DEFINE(world, qTestSubset, Position); + ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position); ecs_system_init(world, &(ecs_system_desc_t){ .entity = TestAll, @@ -915,21 +926,21 @@ void MultiThread_change_thread_count(void) { ecs_set(world, ids_2[i], Position, {1, 2}); } - ecs_set_threads(world, 2); + set_worker_kind(world, 2); ecs_progress(world, 0); for (i = 0; i < ENTITIES / 2; i ++) { - Position *p = ecs_get_mut(world, ids_1[i], Position); + Position *p = ecs_ensure(world, ids_1[i], Position); test_int(p->x, ENTITIES - i); p->x = 1; - p = ecs_get_mut(world, ids_2[i], Position); + p = ecs_ensure(world, ids_2[i], Position); test_int(p->x, ENTITIES - (i + ENTITIES / 2)); p->x = 1; } - ecs_set_threads(world, 3); + set_worker_kind(world, 3); ecs_progress(world, 0); @@ -963,7 +974,7 @@ void MultiThread_multithread_quit(void) { ecs_bulk_new(world, Position, 100); - ecs_set_threads(world, 2); + set_worker_kind(world, 2); test_assert( ecs_progress(world, 0) == 0); @@ -987,7 +998,7 @@ void MultiThread_schedule_w_tasks(void) { .multi_threaded = true }); - ecs_set_threads(world, 2); + set_worker_kind(world, 2); test_assert( ecs_progress(world, 0) != 0); @@ -1003,7 +1014,7 @@ void ReactiveDummySystem(ecs_iter_t * it) { static void PeriodicDummySystem(ecs_iter_t * it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i++ ) { @@ -1024,7 +1035,7 @@ void MultiThread_reactive_system(void) { }); ecs_bulk_new(world, Position, 2); - ecs_set_threads(world, 2); + set_worker_kind(world, 2); test_assert(has_ran == false); @@ -1038,7 +1049,7 @@ void MultiThread_reactive_system(void) { void MultiThread_fini_after_set_threads(void) { ecs_world_t * world = ecs_init(); - ecs_set_threads(world, 2); + set_worker_kind(world, 2); ecs_fini(world); @@ -1048,7 +1059,7 @@ void MultiThread_fini_after_set_threads(void) { static void SingleThreadedSystem(ecs_iter_t * it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i++ ) { @@ -1068,10 +1079,10 @@ void MultiThread_2_threads_single_threaded_system(void) { .multi_threaded = false }); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); - ecs_set_threads(world, 2); + set_worker_kind(world, 2); ecs_progress(world, 0); @@ -1092,7 +1103,8 @@ static int create_query_invoked = 0; static void CreateQuery(ecs_iter_t *it) { - ecs_query_new(it->world, "0"); + ecs_query_t *q = ecs_query(it->world, { .expr = "0" }); + ecs_query_fini(q); create_query_invoked ++; } @@ -1100,12 +1112,12 @@ void MultiThread_no_staging_w_multithread(void) { for (int i = 0; i < 10; i ++) { ecs_world_t *world = ecs_init(); - ecs_set_threads(world, 32); + set_worker_kind(world, 32); ecs_system_init(world, &(ecs_system_desc_t){ .callback = CreateQuery, - .no_readonly = true, - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}) + .immediate = true, + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}) }); create_query_invoked = 0; @@ -1120,8 +1132,8 @@ void MultiThread_no_staging_w_multithread(void) { void MultiThread_multithread_w_monitor_addon(void) { ecs_world_t *world = ecs_init(); - ECS_IMPORT(world, FlecsMonitor); - ecs_set_threads(world, 4); + ECS_IMPORT(world, FlecsStats); + set_worker_kind(world, 4); ecs_progress(world, 0); ecs_fini(world); @@ -1141,7 +1153,7 @@ void System_w_ctx(ecs_iter_t *it) { static void System_w_binding_ctx(ecs_iter_t *it) { - test_assert(it->binding_ctx == &system_ctx); + test_assert(it->callback_ctx == &system_ctx); test_assert(it->system != 0); test_assert(it->delta_time != 0); system_ctx ++; @@ -1150,22 +1162,20 @@ void System_w_binding_ctx(ecs_iter_t *it) { static void System_run_w_ctx(ecs_iter_t *it) { System_w_ctx(it); - ecs_iter_fini(it); } static void System_run_w_binding_ctx(ecs_iter_t *it) { System_w_binding_ctx(it); - ecs_iter_fini(it); } void MultiThread_get_ctx(void) { ecs_world_t *world = ecs_init(); - ecs_set_threads(world, 2); + set_worker_kind(world, 2); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) } }), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = System_w_ctx, .multi_threaded = true, .ctx = &system_ctx @@ -1181,13 +1191,13 @@ void MultiThread_get_ctx(void) { void MultiThread_get_binding_ctx(void) { ecs_world_t *world = ecs_init(); - ecs_set_threads(world, 2); + set_worker_kind(world, 2); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) } }), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = System_w_binding_ctx, .multi_threaded = true, - .binding_ctx = &system_ctx + .callback_ctx = &system_ctx }); ecs_progress(world, 0); @@ -1200,10 +1210,10 @@ void MultiThread_get_binding_ctx(void) { void MultiThread_get_ctx_w_run(void) { ecs_world_t *world = ecs_init(); - ecs_set_threads(world, 2); + set_worker_kind(world, 2); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) } }), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .run = System_run_w_ctx, .multi_threaded = true, .ctx = &system_ctx @@ -1219,13 +1229,13 @@ void MultiThread_get_ctx_w_run(void) { void MultiThread_get_binding_ctx_w_run(void) { ecs_world_t *world = ecs_init(); - ecs_set_threads(world, 2); + set_worker_kind(world, 2); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) } }), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate)) }), .run = System_run_w_binding_ctx, .multi_threaded = true, - .binding_ctx = &system_ctx + .callback_ctx = &system_ctx }); ecs_progress(world, 0); @@ -1250,18 +1260,18 @@ void MultiThread_bulk_new_in_no_readonly_w_multithread(void) { ECS_TAG_DEFINE(world, Tag); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), - .no_readonly = true, + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), + .immediate = true, .callback = sys_bulk_init }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .multi_threaded = true, .callback = sys }); - ecs_set_threads(world, 80); + set_worker_kind(world, 80); for (int i = 0; i < 100; i ++) { ecs_progress(world, 0); @@ -1287,32 +1297,32 @@ void MultiThread_bulk_new_in_no_readonly_w_multithread_2(void) { ECS_COMPONENT_DEFINE(world, Position); - ecs_set_threads(world, 64); + set_worker_kind(world, 64); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = {{ ecs_id(Position) }}, + .query.terms = {{ ecs_id(Position) }}, .callback = sys }); ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ ecs_id(Position) }} + .terms = {{ ecs_id(Position) }} }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = sys_bulk_init_2, - .no_readonly = true, + .immediate = true, .ctx = q }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), .callback = sys }); @@ -1321,6 +1331,8 @@ void MultiThread_bulk_new_in_no_readonly_w_multithread_2(void) { test_int(ecs_count(world, Position), 1); + ecs_query_fini(q); + ecs_fini(world); } @@ -1329,7 +1341,7 @@ static int invoked_count = 0; static int invoked_main_count = 0; static void dummy(ecs_iter_t *it) { - int stage_id = ecs_get_stage_id(it->world); + int stage_id = ecs_stage_get_id(it->world); if (stage_id == 0) { test_assert(main_thread == ecs_os_thread_self()); @@ -1345,12 +1357,12 @@ void MultiThread_run_first_worker_on_main(void) { ecs_world_t *world = ecs_init(); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .multi_threaded = true, .callback = dummy }); - ecs_set_threads(world, 3); + set_worker_kind(world, 3); main_thread = ecs_os_thread_self(); @@ -1366,12 +1378,12 @@ void MultiThread_run_single_thread_on_main(void) { ecs_world_t *world = ecs_init(); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .multi_threaded = false, .callback = dummy }); - ecs_set_threads(world, 3); + set_worker_kind(world, 3); main_thread = ecs_os_thread_self(); diff --git a/vendors/flecs/test/addons/src/MultiThreadStaging.c b/vendors/flecs/test/addons/src/MultiThreadStaging.c index 9b5f0b62f..a35a57d55 100644 --- a/vendors/flecs/test/addons/src/MultiThreadStaging.c +++ b/vendors/flecs/test/addons/src/MultiThreadStaging.c @@ -244,7 +244,7 @@ void MultiThreadStaging_6_threads_add_to_current(void) { static void InitVelocity(ecs_iter_t *it) { - Velocity *v = ecs_field(it, Velocity, 1); + Velocity *v = ecs_field(it, Velocity, 0); int i; for (i = 0; i < it->count; i ++) { @@ -255,7 +255,7 @@ void InitVelocity(ecs_iter_t *it) { static void AddVelocity(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -302,7 +302,7 @@ void MultiThreadStaging_2_threads_on_add(void) { static void New_w_count(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); ecs_bulk_new(it->world, Position, 10); } @@ -333,8 +333,8 @@ void MultiThreadStaging_custom_thread_auto_merge(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); ecs_set_stage_count(world, 2); @@ -342,7 +342,7 @@ void MultiThreadStaging_custom_thread_auto_merge(void) { ecs_world_t *ctx_2 = ecs_get_stage(world, 1); ecs_frame_begin(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, true); /* thread 1 */ ecs_defer_begin(ctx_1); @@ -379,127 +379,6 @@ void MultiThreadStaging_custom_thread_auto_merge(void) { ecs_fini(world); } -void MultiThreadStaging_custom_thread_manual_merge(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_set_automerge(world, false); - - ecs_set_stage_count(world, 2); - - ecs_world_t *ctx_1 = ecs_get_stage(world, 0); - ecs_world_t *ctx_2 = ecs_get_stage(world, 1); - - ecs_frame_begin(world, 0); - ecs_readonly_begin(world); - - /* thread 1 */ - ecs_defer_begin(ctx_1); - ecs_set(ctx_1, e1, Position, {10, 20}); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - ecs_defer_end(ctx_1); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - - /* thread 2 */ - ecs_defer_begin(ctx_2); - ecs_set(ctx_2, e2, Position, {20, 30}); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - ecs_defer_end(ctx_2); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - - ecs_readonly_end(world); - ecs_frame_end(world); - - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(world, e2, Position)); - - ecs_merge(world); - - test_assert(ecs_has(world, e1, Position)); - test_assert(ecs_has(world, e2, Position)); - - const Position *p1 = ecs_get(world, e1, Position); - test_int(p1->x, 10); - test_int(p1->y, 20); - - const Position *p2 = ecs_get(world, e2, Position); - test_int(p2->x, 20); - test_int(p2->y, 30); - - ecs_fini(world); -} - -void MultiThreadStaging_custom_thread_partial_manual_merge(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_set_stage_count(world, 2); - - ecs_world_t *ctx_1 = ecs_get_stage(world, 0); - ecs_world_t *ctx_2 = ecs_get_stage(world, 1); - - /* Only disable automerging for ctx_2 */ - ecs_set_automerge(ctx_2, false); - - ecs_frame_begin(world, 0); - ecs_readonly_begin(world); - - /* thread 1 */ - ecs_defer_begin(ctx_1); - ecs_set(ctx_1, e1, Position, {10, 20}); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - ecs_defer_end(ctx_1); - test_assert(!ecs_has(world, e1, Position)); - test_assert(!ecs_has(ctx_1, e1, Position)); - - /* thread 2 */ - ecs_defer_begin(ctx_2); - ecs_set(ctx_2, e2, Position, {20, 30}); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - ecs_defer_end(ctx_2); - test_assert(!ecs_has(world, e2, Position)); - test_assert(!ecs_has(ctx_2, e2, Position)); - - ecs_readonly_end(world); - ecs_frame_end(world); - - test_assert(ecs_has(world, e1, Position)); - test_assert(!ecs_has(world, e2, Position)); - - const Position *p1 = ecs_get(world, e1, Position); - test_int(p1->x, 10); - test_int(p1->y, 20); - - ecs_merge(ctx_2); - - test_assert(ecs_has(world, e1, Position)); - test_assert(ecs_has(world, e2, Position)); - - p1 = ecs_get(world, e1, Position); - test_int(p1->x, 10); - test_int(p1->y, 20); - - const Position *p2 = ecs_get(world, e2, Position); - test_int(p2->x, 20); - test_int(p2->y, 30); - - ecs_fini(world); -} - void MultiThreadStaging_set_pair_w_new_target_readonly(void) { ecs_world_t *world = ecs_init(); @@ -507,14 +386,14 @@ void MultiThreadStaging_set_pair_w_new_target_readonly(void) { ecs_set_threads(world, 2); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_world_t *thr_1 = ecs_get_stage(world, 0); ecs_frame_begin(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, true); - ecs_entity_t tgt = ecs_new_id(thr_1); + ecs_entity_t tgt = ecs_new(thr_1); ecs_set_pair(thr_1, e, Position, tgt, {10, 20}); ecs_readonly_end(world); @@ -537,14 +416,14 @@ void MultiThreadStaging_set_pair_w_new_target_tgt_component_readonly(void) { ecs_set_threads(world, 2); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_world_t *thr_1 = ecs_get_stage(world, 0); ecs_frame_begin(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, true); - ecs_entity_t tgt = ecs_new_id(thr_1); + ecs_entity_t tgt = ecs_new(thr_1); ecs_set_pair_second(thr_1, e, tgt, Position, {10, 20}); ecs_readonly_end(world); @@ -567,11 +446,11 @@ void MultiThreadStaging_set_pair_w_new_target_defer(void) { ecs_set_threads(world, 2); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); - ecs_entity_t tgt = ecs_new_id(world); + ecs_entity_t tgt = ecs_new(world); ecs_set_pair(world, e, Position, tgt, {10, 20}); ecs_defer_end(world); @@ -593,11 +472,11 @@ void MultiThreadStaging_set_pair_w_new_target_tgt_component_defer(void) { ecs_set_threads(world, 2); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); - ecs_entity_t tgt = ecs_new_id(world); + ecs_entity_t tgt = ecs_new(world); ecs_set_pair_second(world, e, tgt, Position, {10, 20}); ecs_defer_end(world); diff --git a/vendors/flecs/test/addons/src/Parser.c b/vendors/flecs/test/addons/src/Parser.c deleted file mode 100644 index b01377fa2..000000000 --- a/vendors/flecs/test/addons/src/Parser.c +++ /dev/null @@ -1,5661 +0,0 @@ -#include - -static -int filter_count(ecs_filter_t *f) { - test_assert(f != NULL); - return f->term_count; -} - -static -ecs_term_t* filter_terms(ecs_filter_t *f) { - test_assert(f != NULL); - return f->terms; -} - -#define test_first(column, e, flgs)\ - test_int(column.first.id, e);\ - test_int(column.first.flags, flgs); - -#define test_src(column, e, flgs)\ - test_int(column.src.id, e);\ - test_int(column.src.flags, flgs); - -#define test_second(column, e, flgs)\ - test_int(column.second.id, e);\ - test_int(column.second.flags, flgs); - -#define test_first_var(column, e, flgs, str)\ - test_first(column, e, flgs|EcsIsVariable);\ - test_str(column.first.name, str); - -#define test_src_var(column, e, flgs, str)\ - test_src(column, e, flgs|EcsIsVariable);\ - test_str(column.src.name, str); - -#define test_second_var(column, e, flgs, str)\ - test_second(column, e, flgs|EcsIsVariable);\ - test_str(column.second.name, str); - -void Parser_resolve_this(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t e = ecs_lookup_fullpath(world, "."); - test_assert(e != 0); - test_assert(e == EcsThis); - - ecs_fini(world); -} - -void Parser_resolve_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t e = ecs_lookup_fullpath(world, "*"); - test_assert(e != 0); - test_assert(e == EcsWildcard); - - ecs_fini(world); -} - -void Parser_resolve_any(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t e = ecs_lookup_fullpath(world, "_"); - test_assert(e != 0); - test_assert(e == EcsAny); - - ecs_fini(world); -} - -void Parser_resolve_is_a(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t e = ecs_lookup_fullpath(world, "IsA"); - test_assert(e != 0); - test_assert(e == EcsIsA); - - ecs_fini(world); -} - -void Parser_0(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "0" - })); - - test_int(filter_count(&f), 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_explicit_subject_this(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($This)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_explicit_subject_this_by_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(this)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_explicit_subject_this_by_var_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($This)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_explicit_subject_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(*)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsWildcard, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_explicit_subject_any(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(_)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsAny, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_explicit_subject_0(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(0)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_as_predicate(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "this(Subj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsThis, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_var_as_predicate(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$This(Subj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_lowercase_var_as_predicate(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$this(Subj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_as_object(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj, this)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], EcsThis, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_var_as_object(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj, $This)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_wildcard_pred(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(*, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_wildcard_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, *)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_any_pred(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(_, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsAny, EcsSelf|EcsIsVariable); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_any_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, _)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], EcsAny, EcsSelf|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_this_pred(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "($This, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_this_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, $This)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_pred_w_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred:self, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_obj_w_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, Obj:self)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_pred_w_up(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred:up, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsUp|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_obj_w_up(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, Obj:up)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_pred_w_self_up(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred:self|up, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_obj_w_self_up(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, Obj:self|up)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_pred_w_up_trav(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred:up(ChildOf), Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsUp|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_assert(terms[0].first.trav == EcsChildOf); - test_assert(terms[0].second.trav == 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_obj_w_up_trav(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, Obj:up(ChildOf))" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_assert(terms[0].first.trav == 0); - test_assert(terms[0].second.trav == EcsChildOf); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_pred_w_invalid_flags(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred:, Obj)" - })); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_obj_w_invalid_flags(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, Obj:)" - })); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_this(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($This, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_this_by_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(this, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_this_by_var_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($This, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_wildcard_pred(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "*($This, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_wildcard_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(*, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsWildcard, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_wildcard_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($This, *)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_0_object(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(ChildOf, 0)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_second(terms[0], 0, EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id, ecs_pair(EcsChildOf, 0)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_0_object(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "ChildOf($This, 0)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_second(terms[0], 0, EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id, ecs_pair(EcsChildOf, 0)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_0(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(0, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_in_component_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[in] Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsIn); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_in_component_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[in] Pred(Subj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsIn); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_in_pair_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[in] (Pred, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsIn); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_in_pair_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[in] Pred(Subj, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsIn); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_component_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOut); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_component_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] Pred(Subj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOut); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_pair_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] (Pred, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOut); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_pair_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] Pred(Subj, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOut); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_out_component_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[out] Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsOut); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_out_component_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[out] Pred(Subj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsOut); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_out_pair_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[out] (Pred, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsOut); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_out_pair_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[out] Pred(Subj, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsOut); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_filter_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[none] Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$This($)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_implicit_no_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred()" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_explicit_no_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(0)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_no_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(0, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_variable_single_char(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, X); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($X)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf|EcsUp, "X"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_variable_multi_char(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, XYZ); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($XYZ)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf|EcsUp, "XYZ"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_variable_multi_char_w_underscore(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, XY_Z); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($XY_Z)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf|EcsUp, "XY_Z"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_variable_multi_char_w_number(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, XY_1); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($XY_1)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf|EcsUp, "XY_1"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_variable_multi_char_not_allcaps(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, XYZ); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($xyZ)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf|EcsUp, "xyZ"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "($Pred, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first_var(terms[0], 0, EcsSelf, "Pred"); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_obj_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, $Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second_var(terms[0], 0, EcsSelf, "Obj"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "!Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsNot); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "!(Pred, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsNot); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "!Pred(Subj, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsNot); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_component_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "!Pred_1, !Pred_2" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsNot); - test_int(terms[0].inout, EcsInOutNone); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsNot); - test_int(terms[1].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_component_not_no_space(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "!Pred_1,!Pred_2" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsNot); - test_int(terms[0].inout, EcsInOutNone); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsNot); - test_int(terms[1].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_component_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "?Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsOptional); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_component_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "?Pred_1, ?Pred_2" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsOptional); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsOptional); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_component_optional_no_space(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "?Pred_1,?Pred_2" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsOptional); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsOptional); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_from_and(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "AND | Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAndFrom); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_from_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "OR | Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsOrFrom); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_from_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "NOT | Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsNotFrom); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "?(Pred, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsOptional); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "?Pred(Subj, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsOptional); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_w_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TOGGLE | Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_TOGGLE); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_explicit_subject_w_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TOGGLE | Pred($This)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_TOGGLE); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_no_subject_w_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TOGGLE | Pred()" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_TOGGLE); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_implicit_subject_w_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_PAIR); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_explicit_subject_w_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred($This, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_PAIR); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_role_pred_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] TOGGLE | Pred" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOut); - test_int(terms[0].id_flags, ECS_TOGGLE); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_role_pred_no_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] TOGGLE | Pred()" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOut); - test_int(terms[0].id_flags, ECS_TOGGLE); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_role_pred_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] TOGGLE | Pred($This)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOut); - test_int(terms[0].id_flags, ECS_TOGGLE); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_role_pair_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] (Pred, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOut); - test_int(terms[0].id_flags, ECS_PAIR); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_inout_role_pair_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] Pred($This, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOut); - test_int(terms[0].id_flags, ECS_PAIR); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_pred_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred_1, Pred_2" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_pred_no_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred_1(), Pred_2()" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], 0, EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_pred_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred_1($This), Pred_2($This)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_pair_implicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred_1, Obj_1), (Pred_2, Obj_2)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_pair_explicit_subject(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred_1($This, Obj_1), Pred_2($This, Obj_2)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_pred_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TOGGLE | Pred_1, TOGGLE | Pred_2" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_TOGGLE); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - test_int(terms[1].id_flags, ECS_TOGGLE); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_pair_implicit_subj_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred_1, Obj_1), (Pred_2, Obj_2)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_PAIR); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - test_int(terms[1].id_flags, ECS_PAIR); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_pair_explicit_subj_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred_1($This, Obj_1), Pred_2($This, Obj_2)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_PAIR); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - test_int(terms[1].id_flags, ECS_PAIR); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_or_pred_implicit_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred_1 || Pred_2" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsOr); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_or_pred_explicit_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred_1($This) || Pred_2($This)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsOr); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_or_pair_implicit_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred_1, Obj_1) || (Pred_2, Obj_2)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsOr); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_or_pair_explicit_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred_1($This, Obj_1) || Pred_2($This, Obj_2)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsOr); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_or_pred_inout(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "[inout] Pred_1 || Pred_2" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsOr); - test_int(terms[0].inout, EcsInOut); - - test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_1_digit_pred_implicit_subj(void) { - ecs_world_t *world = ecs_mini(); - - ecs_ensure(world, 100); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "100" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], 100, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_1_digit_pred_no_subj(void) { - ecs_world_t *world = ecs_mini(); - - ecs_ensure(world, 100); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "100()" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], 100, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_1_digit_pred_explicit_subj(void) { - ecs_world_t *world = ecs_mini(); - - ecs_ensure(world, 100); - ecs_ensure(world, 200); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "100(200)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], 100, EcsSelf|EcsIsEntity); - test_src(terms[0], 200, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_1_digit_pair_implicit_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_ensure(world, 100); - ecs_ensure(world, 300); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(100, 300)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], 100, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], 300, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_1_digit_pair_explicit_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred_1); - ECS_TAG(world, Pred_2); - - ecs_ensure(world, 100); - ecs_ensure(world, 200); - ecs_ensure(world, 300); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "100(200, 300)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], 100, EcsSelf|EcsIsEntity); - test_src(terms[0], 200, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], 300, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(self)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(up)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_subset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(down)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsDown|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_superset_inclusive(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(self|up)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_subset_inclusive(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(self|down)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsDown|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_superset_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(up|cascade)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_subset_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(down|cascade)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsDown|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_superset_inclusive_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(up|cascade|self)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_subset_inclusive_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(down|cascade|self)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsDown|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_implicit_superset_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(cascade)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_implicit_superset_inclusive_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(self|cascade)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsSelf|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_implicit_superset_cascade_w_rel(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_ENTITY(world, Rel, Traversable); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(cascade(Rel))" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, Rel); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_ENTITY(world, Rel, Traversable); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(self|cascade(Rel))" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsSelf|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, Rel); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_superset_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(up(ChildOf))" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsChildOf); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_cascade_superset_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(cascade|up(ChildOf))" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsChildOf); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_superset_cascade_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(up|cascade(ChildOf))" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsChildOf); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_implicit_subject_superset_cascade_childof_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "?Pred(up|cascade(ChildOf))" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); - test_int(terms[0].oper, EcsOptional); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsChildOf); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_expr_w_symbol(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_entity_t comp = ecs_component_init(world, &(ecs_component_desc_t){ - .entity = ecs_entity(world, { - .name = "Foo", - .symbol = "FooBar" - }) - }); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "FooBar" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], comp, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_expr_w_newline(void) { - ecs_world_t *world = ecs_mini(); - - ecs_new_entity(world, "Foo"); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Foo\n" - })); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_subj_entity_w_explicit_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj:self)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_subj_entity_w_explicit_self_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj:self|up)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_subj_entity_w_explicit_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj:up(ChildOf))" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsUp|EcsIsEntity); - test_int(terms[0].src.trav, EcsChildOf); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_subj_entity_w_explicit_self_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj:self|up(ChildOf))" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].src.trav, EcsChildOf); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_obj_entity_w_explicit_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj, Obj:self)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_obj_entity_w_explicit_self_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj, Obj:self|up)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_obj_entity_w_explicit_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj, Obj:up(ChildOf))" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_obj_entity_w_explicit_self_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj, Obj:self|up(ChildOf))" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_w_explicit_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:self:(Subj)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_w_explicit_self_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:self|up:(Subj)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_w_explicit_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:up(ChildOf):(Subj)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsUp|EcsIsEntity); - test_int(terms[0].first.trav, EcsChildOf); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_w_explicit_self_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:self|up(ChildOf):(Subj)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].first.trav, EcsChildOf); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_no_args_w_explicit_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:self" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_no_args_w_explicit_self_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:self|up" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_no_args_w_explicit_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:up(ChildOf)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsUp|EcsIsEntity); - test_int(terms[0].first.trav, EcsChildOf); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_no_args_w_explicit_self_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:self|up(ChildOf)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].first.trav, EcsChildOf); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_no_args_2_terms_w_explicit_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Pred2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:self, Pred2" - })); - - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_no_args_2_terms_w_explicit_self_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Pred2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:self|up, Pred2" - })); - - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_no_args_2_terms_w_explicit_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Pred2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:up(ChildOf), Pred2" - })); - - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsUp|EcsIsEntity); - test_int(terms[0].first.trav, EcsChildOf); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pred_entity_no_args_2_terms_w_explicit_self_superset_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Pred2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred:self|up(ChildOf), Pred2" - })); - - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].first.trav, EcsChildOf); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], Pred2, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_newline(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "\n" - })); - - test_int(filter_count(&f), 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_newlines(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "\n\n" - })); - - test_int(filter_count(&f), 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_3_newlines(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "\n\n" - })); - - test_int(filter_count(&f), 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_space(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = " " - })); - - test_int(filter_count(&f), 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_spaces(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = " " - })); - - test_int(filter_count(&f), 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_trailing_newline(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred\n" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_trailing_newlines(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred\n\n" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_trailing_space(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred " - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_2_trailing_spaces(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred " - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_template_type(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t Pred = ecs_entity_init(world, &(ecs_entity_desc_t){ - .name = "Template" - }); - test_assert(Pred != 0); - test_str(ecs_get_name(world, Pred), "Template"); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Template" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_predicate_w_parens(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Pred)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_not_alive_pred(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - test_assert(!ecs_is_alive(world, 5000)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "5000" - })); - - ecs_fini(world); -} - -void Parser_not_alive_subj(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - test_assert(!ecs_is_alive(world, 5000)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(5000)" - })); - - ecs_fini(world); -} - -void Parser_not_alive_obj(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - test_assert(!ecs_is_alive(world, 5000)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(Subj, 5000)" - })); - - ecs_fini(world); -} - -void Parser_this_subj_var_kind(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, TagB($This)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], TagB, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_obj_var_kind(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, TagB(Subj, $This)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], TagB, EcsSelf|EcsIsEntity); - test_src(terms[1], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[1], EcsThis, EcsSelf|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_subj_obj_var_kind(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, TagB($This, $This)" - })); - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], TagB, EcsSelf|EcsIsEntity); - test_src(terms[1], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[1], EcsThis, EcsSelf|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_var_w_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA($Var)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], 0, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_str(terms[0].src.name, "Var"); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_entity_pred_no_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_str(terms[0].first.name, NULL); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_entity_subj_no_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA(Subj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_str(terms[0].src.name, NULL); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_entity_obj_no_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Subj); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA(Subj, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_str(terms[0].second.name, NULL); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_pred_no_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$This" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - test_str(terms[0].first.name, NULL); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_subj_no_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA($This)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_str(terms[0].src.name, NULL); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_this_obj_no_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA(Subj, $This)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_str(terms[0].second.name, NULL); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_auto_object_variable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$(Pred)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second_var(terms[0], 0, EcsSelf, "Pred"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_auto_object_variable_w_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$(Pred, Subj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); - test_second_var(terms[0], 0, EcsSelf, "Pred"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_auto_scoped_variable(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t Pred = ecs_entity(world, { - .name = "foo.Pred" - }); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$(foo.Pred)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second_var(terms[0], 0, EcsSelf, "Pred"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_invalid_variable_only(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - ECS_TAG(world, Subj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$" - })); - - ecs_fini(world); -} - -void Parser_oneof_self_pred_w_relative_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, OneOf); - ECS_ENTITY(world, Obj, (ChildOf, Rel)); - - test_assert( ecs_lookup_child(world, 0, "Obj") == 0 ); // sanity check - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Rel, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Rel, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_oneof_other_pred_w_relative_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_ENTITY(world, Obj, (ChildOf, Parent)); - - test_assert( ecs_lookup_child(world, 0, "Obj") == 0 ); // sanity check - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Rel, Obj)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Rel, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Obj, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_oneof_self_pred_w_invalid_obj(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, OneOf); - ECS_TAG(world, Obj); - - test_assert( ecs_lookup_child(world, 0, "Obj") != 0 ); // sanity check - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Rel, Obj)" - })); - - ecs_fini(world); -} - -void Parser_oneof_other_pred_w_invalid_obj(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_TAG(world, Obj); - - test_assert( ecs_lookup_child(world, 0, "Obj") != 0 ); // sanity check - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Rel, Obj)" - })); - - ecs_fini(world); -} - -void Parser_pair_implicit_src_missing_rel(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(, Obj)" - })); - - ecs_fini(world); -} - -void Parser_pair_implicit_src_missing_obj(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Rel, )" - })); - - ecs_fini(world); -} - -void Parser_pair_explicit_src_missing_src(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Rel(, Obj)" - })); - - ecs_fini(world); -} - -void Parser_pair_explicit_src_missing_obj(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Rel($this, )" - })); - - ecs_fini(world); -} - -void Parser_eq_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, First); - ECS_TAG(world, Second); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "First == Second" - })); - - ecs_fini(world); -} - -void Parser_eq_id_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Second); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$this == Second" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsPredEq, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_second(terms[0], Second, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_eq_var_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, First); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "First == $this" - })); - - ecs_fini(world); -} - -void Parser_eq_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, First); - ECS_TAG(world, Second); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$x == $y" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsPredEq, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf, "x"); - test_second_var(terms[0], 0, EcsSelf, "y"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_neq_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, First); - ECS_TAG(world, Second); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "First != Second" - })); - - ecs_fini(world); -} - -void Parser_neq_id_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Second); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$this != Second" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsPredEq, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_second(terms[0], Second, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsNot); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_neq_var_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, First); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "First != $this" - })); - - ecs_fini(world); -} - -void Parser_neq_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$x != $y" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsPredEq, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf, "x"); - test_second_var(terms[0], 0, EcsSelf, "y"); - test_int(terms[0].oper, EcsNot); - test_int(terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_eq_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "\"First\" == \"Second\"" - })); - - ecs_fini(world); -} - -void Parser_eq_name_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "\"First\" == $y" - })); - - ecs_fini(world); -} - -void Parser_eq_var_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$x == \"Second\"" - })); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_neq_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "\"First\" != \"Second\"" - })); - - ecs_fini(world); -} - -void Parser_neq_name_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "\"First\" != $y" - })); - - ecs_fini(world); -} - -void Parser_neq_var_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$x != \"Second\"" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsPredEq, EcsSelf|EcsIsEntity); - test_uint(terms[0].src.flags, (EcsSelf|EcsIsVariable)); - test_str(terms[0].src.name, "x"); - test_uint(terms[0].src.id, 0); - test_uint(terms[0].second.flags, (EcsSelf|EcsIsName)); - test_str(terms[0].second.name, "Second"); - test_uint(terms[0].second.id, 0); - test_int(terms[0].oper, EcsNot); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_match_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "\"First\" ~= \"Second\"" - })); - - ecs_fini(world); -} - -void Parser_match_name_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "\"First\" ~= $y" - })); - - ecs_fini(world); -} - -void Parser_match_var_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$x ~= \"Second\"" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsPredMatch, EcsSelf|EcsIsEntity); - test_uint(terms[0].src.flags, (EcsSelf|EcsIsVariable)); - test_str(terms[0].src.name, "x"); - test_uint(terms[0].src.id, 0); - test_uint(terms[0].second.flags, (EcsSelf|EcsIsName)); - test_str(terms[0].second.name, "Second"); - test_uint(terms[0].second.id, 0); - test_int(terms[0].oper, EcsAnd); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_match_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "$x ~= $y" - })); - - ecs_fini(world); -} - -void Parser_nmatch_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "\"First\" ~= \"!Second\"" - })); - - ecs_fini(world); -} - -void Parser_nmatch_name_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "\"!First\" ~= $y" - })); - - ecs_fini(world); -} - -void Parser_nmatch_var_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "$x ~= \"!Second\"" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsPredMatch, EcsSelf|EcsIsEntity); - test_uint(terms[0].src.flags, (EcsSelf|EcsIsVariable)); - test_str(terms[0].src.name, "x"); - test_uint(terms[0].src.id, 0); - test_uint(terms[0].second.flags, (EcsSelf|EcsIsName)); - test_str(terms[0].second.name, "Second"); - test_uint(terms[0].second.id, 0); - test_int(terms[0].oper, EcsNot); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_eq_same_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "$x == $x" - })); - - ecs_fini(world); -} - -void Parser_neq_same_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "$x != $x" - })); - - ecs_fini(world); -} - -void Parser_eq_same_var_this(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "$this == $this" - })); - - ecs_fini(world); -} - -void Parser_neq_same_var_this(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "$this != $this" - })); - - ecs_fini(world); -} - -void Parser_eq_w_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "?$this == Foo" - })); - - ecs_fini(world); -} - -void Parser_neq_w_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "?$this != Foo" - })); - - ecs_fini(world); -} - -void Parser_match_w_optional(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "?$this ~= \"Foo\"" - })); - - ecs_fini(world); -} - -void Parser_query_scope_1_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, {TagB}" - })); - test_int(filter_count(&f), 4); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsScopeOpen, EcsSelf|EcsIsEntity); - test_src(terms[1], 0, EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutNone); - - test_first(terms[2], TagB, EcsSelf|EcsIsEntity); - test_src(terms[2], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - test_first(terms[3], EcsScopeClose, EcsSelf|EcsIsEntity); - test_src(terms[3], 0, EcsIsEntity); - test_int(terms[3].oper, EcsAnd); - test_int(terms[3].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_query_scope_1_term_spaces(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, { TagB }" - })); - test_int(filter_count(&f), 4); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsScopeOpen, EcsSelf|EcsIsEntity); - test_src(terms[1], 0, EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutNone); - - test_first(terms[2], TagB, EcsSelf|EcsIsEntity); - test_src(terms[2], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - test_first(terms[3], EcsScopeClose, EcsSelf|EcsIsEntity); - test_src(terms[3], 0, EcsIsEntity); - test_int(terms[3].oper, EcsAnd); - test_int(terms[3].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_query_scope_2_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, { TagB, TagC }" - })); - test_int(filter_count(&f), 5); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsScopeOpen, EcsSelf|EcsIsEntity); - test_src(terms[1], 0, EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutNone); - - test_first(terms[2], TagB, EcsSelf|EcsIsEntity); - test_src(terms[2], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - test_first(terms[3], TagC, EcsSelf|EcsIsEntity); - test_src(terms[3], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[3].oper, EcsAnd); - test_int(terms[3].inout, EcsInOutDefault); - - test_first(terms[4], EcsScopeClose, EcsSelf|EcsIsEntity); - test_src(terms[4], 0, EcsIsEntity); - test_int(terms[4].oper, EcsAnd); - test_int(terms[4].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_query_nested_scope(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, {TagB, {TagC}}" - })); - test_int(filter_count(&f), 7); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsScopeOpen, EcsSelf|EcsIsEntity); - test_src(terms[1], 0, EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutNone); - - test_first(terms[2], TagB, EcsSelf|EcsIsEntity); - test_src(terms[2], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - test_first(terms[3], EcsScopeOpen, EcsSelf|EcsIsEntity); - test_src(terms[3], 0, EcsIsEntity); - test_int(terms[3].oper, EcsAnd); - test_int(terms[3].inout, EcsInOutNone); - - test_first(terms[4], TagC, EcsSelf|EcsIsEntity); - test_src(terms[4], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[4].oper, EcsAnd); - test_int(terms[4].inout, EcsInOutDefault); - - test_first(terms[5], EcsScopeClose, EcsSelf|EcsIsEntity); - test_src(terms[5], 0, EcsIsEntity); - test_int(terms[5].oper, EcsAnd); - test_int(terms[5].inout, EcsInOutNone); - - test_first(terms[6], EcsScopeClose, EcsSelf|EcsIsEntity); - test_src(terms[6], 0, EcsIsEntity); - test_int(terms[6].oper, EcsAnd); - test_int(terms[6].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_query_nested_scope_spaces(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, { TagB, { TagC } }" - })); - test_int(filter_count(&f), 7); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsScopeOpen, EcsSelf|EcsIsEntity); - test_src(terms[1], 0, EcsIsEntity); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutNone); - - test_first(terms[2], TagB, EcsSelf|EcsIsEntity); - test_src(terms[2], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - test_first(terms[3], EcsScopeOpen, EcsSelf|EcsIsEntity); - test_src(terms[3], 0, EcsIsEntity); - test_int(terms[3].oper, EcsAnd); - test_int(terms[3].inout, EcsInOutNone); - - test_first(terms[4], TagC, EcsSelf|EcsIsEntity); - test_src(terms[4], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[4].oper, EcsAnd); - test_int(terms[4].inout, EcsInOutDefault); - - test_first(terms[5], EcsScopeClose, EcsSelf|EcsIsEntity); - test_src(terms[5], 0, EcsIsEntity); - test_int(terms[5].oper, EcsAnd); - test_int(terms[5].inout, EcsInOutNone); - - test_first(terms[6], EcsScopeClose, EcsSelf|EcsIsEntity); - test_src(terms[6], 0, EcsIsEntity); - test_int(terms[6].oper, EcsAnd); - test_int(terms[6].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_query_scope_unbalanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_log_set_level(-4); - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, { TagB" - })); - - ecs_fini(world); -} - -void Parser_query_not_scope(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, !{TagB}" - })); - test_int(filter_count(&f), 4); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], TagA, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsScopeOpen, EcsSelf|EcsIsEntity); - test_src(terms[1], 0, EcsIsEntity); - test_int(terms[1].oper, EcsNot); - test_int(terms[1].inout, EcsInOutNone); - - test_first(terms[2], TagB, EcsSelf|EcsIsEntity); - test_src(terms[2], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - test_first(terms[3], EcsScopeClose, EcsSelf|EcsIsEntity); - test_src(terms[3], 0, EcsIsEntity); - test_int(terms[3].oper, EcsAnd); - test_int(terms[3].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_query_empty_scope(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_log_set_level(-4); - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, !{}" - })); - - ecs_fini(world); -} - -void Parser_override_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "OVERRIDE|Tag" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Tag, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_OVERRIDE); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_override_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "OVERRIDE|(Rel, Tgt)" - })); - - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Rel, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second(terms[0], Tgt, EcsSelf|EcsIsEntity); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].id_flags, ECS_PAIR|ECS_OVERRIDE); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_3_args(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "ChildOf($this, $parent, $grandparent)" - })); - - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); - test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_3_args_implicit_this(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(ChildOf, $parent, $grandparent)" - })); - - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); - test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_4_args(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "ChildOf($this, $parent, $grandparent, $greatgrandparent)" - })); - - test_int(filter_count(&f), 3); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); - test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - test_first(terms[2], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[2], 0, EcsSelf|EcsIsVariable, "grandparent"); - test_second_var(terms[2], 0, EcsSelf|EcsIsVariable, "greatgrandparent"); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_4_args_implicit_this(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(ChildOf, $parent, $grandparent, $greatgrandparent)" - })); - - test_int(filter_count(&f), 3); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); - test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - test_first(terms[2], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[2], 0, EcsSelf|EcsIsVariable, "grandparent"); - test_second_var(terms[2], 0, EcsSelf|EcsIsVariable, "greatgrandparent"); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_3_args_2_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "ChildOf($this, $parent, $grandparent), Rel($this, $parent)" - })); - - test_int(filter_count(&f), 3); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); - test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); - test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - test_first(terms[2], Rel, EcsSelf|EcsIsEntity); - test_src(terms[2], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_second_var(terms[2], 0, EcsSelf|EcsIsVariable, "parent"); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_cascade_desc(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Pred); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Pred(cascade|desc)" - })); - test_int(filter_count(&f), 1); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], Pred, EcsSelf|EcsIsEntity); - test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsDesc|EcsIsVariable); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - test_int(terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_3_args_this_tgt(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "ChildOf($grandchild, $child, $this)" - })); - - test_int(filter_count(&f), 2); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "grandchild"); - test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "child"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "child"); - test_second(terms[1], EcsThis, EcsSelf|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_3_args_2_terms_this_tgt(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "ChildOf($grandchild, $child, $this), Rel($this)" - })); - - test_int(filter_count(&f), 3); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "grandchild"); - test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "child"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "child"); - test_second(terms[1], EcsThis, EcsSelf|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - test_first(terms[2], Rel, EcsSelf|EcsIsEntity); - test_src(terms[2], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Parser_pair_3_args_2_terms_this_tgt_implicit_this(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "ChildOf($grandchild, $child, $this), Rel" - })); - - test_int(filter_count(&f), 3); - - ecs_term_t *terms = filter_terms(&f); - test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "grandchild"); - test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "child"); - test_int(terms[0].oper, EcsAnd); - test_int(terms[0].inout, EcsInOutDefault); - - test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); - test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "child"); - test_second(terms[1], EcsThis, EcsSelf|EcsIsVariable); - test_int(terms[1].oper, EcsAnd); - test_int(terms[1].inout, EcsInOutDefault); - - test_first(terms[2], Rel, EcsSelf|EcsIsEntity); - test_src(terms[2], EcsThis, EcsSelf|EcsUp|EcsIsVariable); - test_int(terms[2].oper, EcsAnd); - test_int(terms[2].inout, EcsInOutDefault); - - ecs_filter_fini(&f); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/Pipeline.c b/vendors/flecs/test/addons/src/Pipeline.c index c27944ba8..6165b6287 100644 --- a/vendors/flecs/test/addons/src/Pipeline.c +++ b/vendors/flecs/test/addons/src/Pipeline.c @@ -78,6 +78,8 @@ void Pipeline_system_order_same_phase(void) { ECS_SYSTEM(world, SysC, EcsOnUpdate, Position); const ecs_world_info_t *stats = ecs_get_world_info(world); + + test_int(stats->pipeline_build_count_total, 0); ecs_progress(world, 1); @@ -267,7 +269,7 @@ void Pipeline_system_order_after_new_system_lower_id(void) { ECS_ENTITY(world, E, Position); - ecs_entity_t Sys = ecs_new(world, 0); + ecs_entity_t Sys = ecs_new(world); ECS_SYSTEM(world, SysB, EcsOnUpdate, Position); ECS_SYSTEM(world, SysC, EcsOnUpdate, Position); @@ -275,8 +277,8 @@ void Pipeline_system_order_after_new_system_lower_id(void) { /* Create new system with Sys id */ ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = Sys, .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, {.id = Sys, .name = "SysA", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysA }); @@ -305,15 +307,15 @@ void Pipeline_system_order_after_new_system_inbetween_id(void) { ECS_ENTITY(world, E, Position); ECS_SYSTEM(world, SysA, EcsOnUpdate, Position); - ecs_entity_t Sys = ecs_new(world, 0); + ecs_entity_t Sys = ecs_new(world); ECS_SYSTEM(world, SysC, EcsOnUpdate, Position); const ecs_world_info_t *stats = ecs_get_world_info(world); /* Create new system with Sys id */ ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = Sys, .name = "SysB", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, {.id = Sys, .name = "SysB", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysB }); @@ -343,14 +345,14 @@ void Pipeline_system_order_after_new_system_higher_id(void) { ECS_SYSTEM(world, SysA, EcsOnUpdate, Position); ECS_SYSTEM(world, SysB, EcsOnUpdate, Position); - ecs_entity_t Sys = ecs_new(world, 0); + ecs_entity_t Sys = ecs_new(world); const ecs_world_info_t *stats = ecs_get_world_info(world); /* Create new system with Sys id */ ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = Sys, .name = "SysC", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, {.id = Sys, .name = "SysC", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysC }); @@ -374,7 +376,7 @@ static int sys_out_invoked; static int sys_in_invoked; static void SysOut(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); sys_out_invoked ++; @@ -385,7 +387,7 @@ static void SysOut(ecs_iter_t *it) { } static void SysOutMain(ecs_iter_t *it) { - Velocity *v = ecs_field(it, Velocity, 2); + Velocity *v = ecs_field(it, Velocity, 1); sys_out_invoked ++; @@ -397,7 +399,7 @@ static void SysOutMain(ecs_iter_t *it) { } static void SysIn(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 0); test_assert(sys_out_invoked != 0); sys_in_invoked ++; @@ -414,8 +416,8 @@ static void SysIn(ecs_iter_t *it) { } static void SysInMain(ecs_iter_t *it) { - Velocity *v = ecs_field(it, Velocity, 1); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); + Velocity *v = ecs_field(it, Velocity, 0); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 0); test_assert(sys_out_invoked != 0); sys_in_invoked ++; @@ -635,7 +637,7 @@ void Pipeline_switch_pipeline(void) { ecs_add(world, ecs_id(SysC), Tag); - ECS_PIPELINE(world, P1, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), !Tag); + ECS_PIPELINE(world, P1, flecs.system.System, flecs.pipeline.Phase(cascade DependsOn), !Tag); ecs_progress(world, 1); @@ -667,7 +669,7 @@ void Pipeline_run_pipeline(void) { ecs_add(world, ecs_id(SysC), Tag); - ECS_PIPELINE(world, P1, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), !Tag); + ECS_PIPELINE(world, P1, flecs.system.System, flecs.pipeline.Phase(cascade DependsOn), !Tag); const ecs_world_info_t *stats = ecs_get_world_info(world); @@ -714,18 +716,18 @@ void Pipeline_3_systems_3_types(void) { ECS_TAG(world, Tag); ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysA", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysA }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = NULL, .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = NULL, .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysB }); ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysC", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position()", + .entity = ecs_entity(world, { .name = "SysC", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position()", .callback = SysC }); @@ -735,7 +737,7 @@ void Pipeline_3_systems_3_types(void) { ecs_add_id(world, s3, Tag); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_progress(world, 1); @@ -748,7 +750,7 @@ void Pipeline_3_systems_3_types(void) { static void RandomWrite(ecs_iter_t *it) { - ecs_entity_t ecs_id(Position) = ecs_field_id(it, 2); + ecs_entity_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -799,7 +801,7 @@ void RandomReadAfterRW(ecs_iter_t *it) { static void RandomRead_Not(ecs_iter_t *it) { - ecs_entity_t ecs_id(Position) = ecs_field_id(it, 2); + ecs_entity_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -818,7 +820,7 @@ void Pipeline_random_read_after_random_write_out_in(void) { ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] Position()); ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [in] Position()); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 1); @@ -839,7 +841,7 @@ void Pipeline_random_read_after_random_write_inout_in(void) { ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [inout] Position()); ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [in] Position()); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 1); @@ -860,7 +862,7 @@ void Pipeline_random_read_after_random_write_out_inout(void) { ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] Position()); ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [inout] Position()); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 1); @@ -881,7 +883,7 @@ void Pipeline_random_read_after_random_write_inout_inout(void) { ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [inout] Position()); ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [inout] Position()); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 1); @@ -902,7 +904,7 @@ void Pipeline_random_read_after_random_write_w_not_write(void) { ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] !Position); ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [in] Position()); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 1); @@ -923,7 +925,7 @@ void Pipeline_random_read_after_random_write_w_not_read(void) { ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] Position()); ECS_SYSTEM(world, RandomRead_Not, EcsOnUpdate, Tag, [in] !Position); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 1); @@ -944,7 +946,7 @@ void Pipeline_random_read_after_random_write_w_wildcard(void) { ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] Position()); ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [in] *()); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 1); @@ -966,7 +968,7 @@ void Pipeline_random_in_after_random_inout_after_random_out(void) { ECS_SYSTEM(world, RandomReadWrite, EcsOnUpdate, Tag, [inout] Position()); ECS_SYSTEM(world, RandomReadAfterRW, EcsOnUpdate, Tag, [in] Position()); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 1); @@ -1010,26 +1012,26 @@ void Pipeline_system_reverse_order_by_phase_custom_pipeline(void) { ecs_add_pair(world, OnFrame, EcsDependsOn, PreFrame); ecs_add_pair(world, PostFrame, EcsDependsOn, OnFrame); - ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag); + ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade DependsOn), Tag); int count = 0; ecs_system_init(world, &(ecs_system_desc_t){ .callback = cb_third, .ctx = &count, - .entity = ecs_entity(world, {.add = {Tag, ecs_pair(EcsDependsOn, PostFrame)}}) + .entity = ecs_entity(world, {.add = ecs_ids(Tag, ecs_pair(EcsDependsOn, PostFrame))}) }); ecs_system_init(world, &(ecs_system_desc_t){ .callback = cb_second, .ctx = &count, - .entity = ecs_entity(world, {.add = {Tag, ecs_pair(EcsDependsOn, OnFrame)}}) + .entity = ecs_entity(world, {.add = ecs_ids(Tag, ecs_pair(EcsDependsOn, OnFrame))}) }); ecs_system_init(world, &(ecs_system_desc_t){ .callback = cb_first, .ctx = &count, - .entity = ecs_entity(world, {.add = {Tag, ecs_pair(EcsDependsOn, PreFrame)}}) + .entity = ecs_entity(world, {.add = ecs_ids(Tag, ecs_pair(EcsDependsOn, PreFrame))}) }); test_int(count, 0); @@ -1050,29 +1052,29 @@ void Pipeline_stage_write_before_read(void) { ECS_COMPONENT(world, Position); ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysA", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysA", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysA }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysB", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "[out] Position(), Position", + .entity = ecs_entity(world, { .name = "SysB", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "[out] Position(), Position", .callback = SysB }); ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysC", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysC", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysC }); - ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag); + ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade DependsOn), Tag); ecs_set_pipeline(world, P); test_assert(s1 != 0); test_assert(s2 != 0); test_assert(s3 != 0); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_progress(world, 1); @@ -1104,40 +1106,40 @@ void Pipeline_mixed_multithreaded_internal(bool task_threads) { ECS_COMPONENT(world, Position); ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysA", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysA", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysA, .multi_threaded = true }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysB", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysB", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysB }); ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysC", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysC", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysC }); ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysD", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysD", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysD, .multi_threaded = true }); ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysE", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysE", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysE, .multi_threaded = true }); ecs_entity_t s6 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysF", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysF", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysF }); - ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag); + ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade DependsOn), Tag); ecs_set_pipeline(world, P); test_assert(s1 != 0); @@ -1147,8 +1149,8 @@ void Pipeline_mixed_multithreaded_internal(bool task_threads) { test_assert(s5 != 0); test_assert(s6 != 0); - ecs_new(world, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); + ecs_new_w(world, Position); if (task_threads) { @@ -1204,40 +1206,40 @@ void Pipeline_mixed_staging(void) { ECS_COMPONENT(world, Position); ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysA", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysA", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysA, - .no_readonly = true + .immediate = true }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysB", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysB", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysB }); ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysC", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysC", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysC }); ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysD", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysD", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysD, - .no_readonly = true + .immediate = true }); ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysE", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysE", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysE, - .no_readonly = true + .immediate = true }); ecs_entity_t s6 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysF", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysF", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysF }); - ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag); + ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade DependsOn), Tag); ecs_set_pipeline(world, P); test_assert(s1 != 0); @@ -1247,8 +1249,8 @@ void Pipeline_mixed_staging(void) { test_assert(s5 != 0); test_assert(s6 != 0); - ecs_new(world, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); + ecs_new_w(world, Position); ecs_progress(world, 1); test_int(sys_a_invoked, 1); @@ -1315,7 +1317,7 @@ void Pipeline_mixed_staging(void) { static void WritePosition(ecs_iter_t *it) { if (*(bool*)it->ctx) { - ecs_entity_t ecs_id(Position) = ecs_field_id(it, 2); + ecs_entity_t ecs_id(Position) = ecs_field_id(it, 1); for (int i = 0; i < it->count; i ++) { ecs_add(it->world, it->entities[i], Position); } @@ -1331,33 +1333,33 @@ void Pipeline_single_threaded_pipeline_change(void) { bool write_position = false; ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, !Position", + .entity = ecs_entity(world, { .name = "SysA", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, !Position", .callback = SysA }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysB", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, Position", + .entity = ecs_entity(world, { .name = "SysB", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, Position", .callback = SysB }); ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "WritePosition", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, [out] Position()", + .entity = ecs_entity(world, { .name = "WritePosition", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, [out] Position()", .callback = WritePosition, .ctx = &write_position }); ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysC", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, Position", + .entity = ecs_entity(world, { .name = "SysC", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, Position", .callback = SysC }); ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysD", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, !Position", + .entity = ecs_entity(world, { .name = "SysD", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, !Position", .callback = SysD }); @@ -1367,8 +1369,8 @@ void Pipeline_single_threaded_pipeline_change(void) { sys_c_invoked = 1; sys_d_invoked = 1; - ecs_new(world, Tag); - ecs_new(world, Tag); + ecs_new_w(world, Tag); + ecs_new_w(world, Tag); test_assert(s1 != 0); test_assert(s2 != 0); @@ -1414,37 +1416,37 @@ void Pipeline_multi_threaded_pipeline_change_internal(bool task_threads) { bool write_position = false; ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, !Position", + .entity = ecs_entity(world, { .name = "SysA", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, !Position", .callback = SysA, .multi_threaded = true }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysB", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, Position, [out] Position()", + .entity = ecs_entity(world, { .name = "SysB", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, Position, [out] Position()", .callback = SysB, .multi_threaded = true }); ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "WritePosition", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, [out] Position(), [in] ?Position", + .entity = ecs_entity(world, { .name = "WritePosition", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, [out] Position(), [in] ?Position", .callback = WritePosition, .ctx = &write_position, .multi_threaded = true }); ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysC", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, Position", + .entity = ecs_entity(world, { .name = "SysC", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, Position", .callback = SysC, .multi_threaded = true }); ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysD", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, !Position", + .entity = ecs_entity(world, { .name = "SysD", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, !Position", .callback = SysD, .multi_threaded = true }); @@ -1461,8 +1463,8 @@ void Pipeline_multi_threaded_pipeline_change_internal(bool task_threads) { sys_c_invoked = 1; sys_d_invoked = 1; - ecs_new(world, Tag); - ecs_new(world, Tag); + ecs_new_w(world, Tag); + ecs_new_w(world, Tag); test_assert(s1 != 0); test_assert(s2 != 0); @@ -1515,19 +1517,19 @@ void Pipeline_activate_after_add(void) { bool write_position = false; ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "WritePosition", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Tag, [out] Position()", + .entity = ecs_entity(world, { .name = "WritePosition", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Tag, [out] Position()", .callback = WritePosition, .ctx = &write_position }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysA", .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysA }); - ecs_new(world, Tag); + ecs_new_w(world, Tag); test_assert(s1 != 0); test_assert(s2 != 0); @@ -1552,7 +1554,8 @@ static ecs_query_t *q_result; static void CreateQuery(ecs_iter_t *it) { test_assert(it->real_world == it->world); - q_result = ecs_query_new(it->world, "Position"); + q_result = ecs_query(it->world, { .expr = "Position" }); + ecs_query_fini(q_result); } void Pipeline_no_staging_system_create_query(void) { @@ -1562,12 +1565,12 @@ void Pipeline_no_staging_system_create_query(void) { ECS_COMPONENT(world, Position); ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "CreateQuery", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), + .entity = ecs_entity(world, { .name = "CreateQuery", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), .callback = CreateQuery, - .no_readonly = true + .immediate = true }); - ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag); + ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade DependsOn), Tag); ecs_set_pipeline(world, P); test_assert(s != 0); @@ -1613,20 +1616,20 @@ void Pipeline_match_all_after_pipeline_rebuild(void) { ECS_TAG_DEFINE(world, TagB); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "[out] TagB()", + .entity = ecs_entity(world, { .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "[out] TagB()", .callback = set_singleton }); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "TagB", + .entity = ecs_entity(world, { .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "TagB", .callback = match_singleton }); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "?TagA", + .entity = ecs_entity(world, { .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "?TagA", .callback = match_all }); @@ -1683,7 +1686,7 @@ void Pipeline_pipeline_w_short_notation(void) { ECS_TAG(world, Tag); ecs_entity_t p = ecs_pipeline(world, { - .query.filter.expr = "flecs.system.System, Tag" + .query.expr = "flecs.system.System, Tag" }); test_assert(p != 0); @@ -1699,23 +1702,23 @@ void Pipeline_stack_allocator_after_progress(void) { ECS_SYSTEM(world, SysA, EcsOnUpdate, Position); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = {{ ecs_id(Position) }} }); - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_stack_cursor_t cursor = *it.priv.cache.stack_cursor; + ecs_iter_t it = ecs_query_iter(world, f); + ecs_stack_cursor_t cursor = *it.priv_.cache.stack_cursor; ecs_iter_fini(&it); ecs_progress(world, 1); test_int(sys_a_invoked, 1); - it = ecs_filter_iter(world, f); - test_assert(it.priv.cache.stack_cursor->page == cursor.page); - test_assert(it.priv.cache.stack_cursor->sp == cursor.sp); + it = ecs_query_iter(world, f); + test_assert(it.priv_.cache.stack_cursor->page == cursor.page); + test_assert(it.priv_.cache.stack_cursor->sp == cursor.sp); ecs_iter_fini(&it); - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -1729,21 +1732,21 @@ void Pipeline_stack_allocator_after_progress_w_pipeline_change(void) { ECS_SYSTEM(world, SysA, EcsOnUpdate, Position); ECS_SYSTEM(world, SysB, EcsOnUpdate, Position); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = {{ ecs_id(Position) }} }); - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_stack_cursor_t cursor = *it.priv.cache.stack_cursor; + ecs_iter_t it = ecs_query_iter(world, f); + ecs_stack_cursor_t cursor = *it.priv_.cache.stack_cursor; ecs_iter_fini(&it); ecs_progress(world, 1); test_int(sys_a_invoked, 1); test_int(sys_b_invoked, 1); - it = ecs_filter_iter(world, f); - test_assert(it.priv.cache.stack_cursor->page == cursor.page); - test_assert(it.priv.cache.stack_cursor->sp == cursor.sp); + it = ecs_query_iter(world, f); + test_assert(it.priv_.cache.stack_cursor->page == cursor.page); + test_assert(it.priv_.cache.stack_cursor->sp == cursor.sp); ecs_iter_fini(&it); ecs_enable(world, SysB, false); @@ -1752,28 +1755,28 @@ void Pipeline_stack_allocator_after_progress_w_pipeline_change(void) { test_int(sys_a_invoked, 2); test_int(sys_b_invoked, 1); - it = ecs_filter_iter(world, f); - test_assert(it.priv.cache.stack_cursor->page == cursor.page); - test_assert(it.priv.cache.stack_cursor->sp == cursor.sp); + it = ecs_query_iter(world, f); + test_assert(it.priv_.cache.stack_cursor->page == cursor.page); + test_assert(it.priv_.cache.stack_cursor->sp == cursor.sp); ecs_iter_fini(&it); - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } static void Sys_w_MainWorldIter(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); - ecs_filter_t *f = ecs_filter(it->real_world, { + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); + ecs_query_t *f = ecs_query(it->real_world, { .terms = {{ ecs_id(Position) }} }); - ecs_iter_t fit = ecs_filter_iter(it->real_world, f); - test_bool(true, ecs_filter_next(&fit)); + ecs_iter_t fit = ecs_query_iter(it->real_world, f); + test_bool(true, ecs_query_next(&fit)); test_int(1, fit.count); - test_bool(false, ecs_filter_next(&fit)); - ecs_filter_fini(f); + test_bool(false, ecs_query_next(&fit)); + ecs_query_fini(f); } static @@ -1782,7 +1785,7 @@ void Pipeline_iter_from_world_in_singlethread_system_multitead_app_internal(bool ECS_COMPONENT(world, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ECS_SYSTEM(world, Sys_w_MainWorldIter, EcsOnUpdate, Position()); @@ -1828,26 +1831,26 @@ void Pipeline_no_staging_after_inactive_system(void) { ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), - .query.filter.terms = {{ Tag, .oper = EcsNot }}, + .query.terms = {{ Tag, .oper = EcsNot }}, .callback = StagingSystem }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), - .query.filter.terms = {{ ecs_id(Position) }, { Tag, .oper = EcsNot }}, + .query.terms = {{ ecs_id(Position) }, { Tag, .oper = EcsNot }}, .callback = StagingSystem }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), .callback = NoStagingSystem, - .no_readonly = true + .immediate = true }); ecs_progress(world, 0); @@ -1873,20 +1876,20 @@ static int no_staging_create_velocity_invoked = 0; static void NoStagingSystemCreatePosition(ecs_iter_t *it) { ecs_defer_end(it->world); - create_position_e = ecs_new_id(it->world); + create_position_e = ecs_new(it->world); ecs_set(it->world, create_position_e, Position, {0, 0}); - ecs_filter_t *f = ecs_filter(it->world, { + ecs_query_t *f = ecs_query(it->world, { .terms = {{ ecs_id(Position) }} }); - ecs_iter_t fit = ecs_filter_iter(it->world, f); - test_bool(true, ecs_filter_next(&fit)); + ecs_iter_t fit = ecs_query_iter(it->world, f); + test_bool(true, ecs_query_next(&fit)); test_int(fit.count, 1); test_uint(fit.entities[0], create_position_e); - test_bool(false, ecs_filter_next(&fit)); + test_bool(false, ecs_query_next(&fit)); - ecs_filter_fini(f); + ecs_query_fini(f); ecs_defer_begin(it->world); no_staging_create_position_invoked ++; @@ -1895,20 +1898,20 @@ static void NoStagingSystemCreatePosition(ecs_iter_t *it) { static void NoStagingSystemCreateVelocity(ecs_iter_t *it) { ecs_defer_end(it->world); - create_velocity_e = ecs_new_id(it->world); + create_velocity_e = ecs_new(it->world); ecs_set(it->world, create_velocity_e, Velocity, {0, 0}); - ecs_filter_t *f = ecs_filter(it->world, { + ecs_query_t *f = ecs_query(it->world, { .terms = {{ ecs_id(Velocity) }} }); - ecs_iter_t fit = ecs_filter_iter(it->world, f); - test_bool(true, ecs_filter_next(&fit)); + ecs_iter_t fit = ecs_query_iter(it->world, f); + test_bool(true, ecs_query_next(&fit)); test_int(fit.count, 1); test_uint(fit.entities[0], create_velocity_e); - test_bool(false, ecs_filter_next(&fit)); + test_bool(false, ecs_query_next(&fit)); - ecs_filter_fini(f); + ecs_query_fini(f); ecs_defer_begin(it->world); no_staging_create_velocity_invoked ++; @@ -1931,17 +1934,17 @@ void Pipeline_inactive_system_after_no_staging_system_no_defer_w_filter(void) { ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), .callback = NoStagingSystemCreatePosition, - .no_readonly = true + .immediate = true }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), - .query.filter.terms = {{ ecs_id(Position) }}, + .query.terms = {{ ecs_id(Position) }}, .callback = ReadPosition }); @@ -1967,26 +1970,26 @@ void Pipeline_inactive_system_after_no_staging_system_no_defer_w_filter_w_no_sta ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), .callback = NoStagingSystemCreatePosition, - .no_readonly = true + .immediate = true }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), - .query.filter.terms = {{ ecs_id(Position) }}, + .query.terms = {{ ecs_id(Position) }}, .callback = ReadPosition }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), .callback = SysA, - .no_readonly = true + .immediate = true }); ecs_progress(world, 0); @@ -2012,33 +2015,33 @@ void Pipeline_inactive_system_after_2_no_staging_system_no_defer_w_filter(void) ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), .callback = NoStagingSystemCreatePosition, - .no_readonly = true + .immediate = true }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), .callback = NoStagingSystemCreateVelocity, - .no_readonly = true + .immediate = true }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), - .query.filter.terms = {{ ecs_id(Position) }}, + .query.terms = {{ ecs_id(Position) }}, .callback = ReadPosition }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), - .query.filter.terms = {{ ecs_id(Velocity) }}, + .query.terms = {{ ecs_id(Velocity) }}, .callback = ReadVelocity }); @@ -2074,17 +2077,17 @@ void Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_inter ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), .callback = NoStagingSystemCreatePosition, - .no_readonly = true + .immediate = true }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), - .query.filter.terms = {{ ecs_id(Position) }}, + .query.terms = {{ ecs_id(Position) }}, .callback = ReadPosition, .multi_threaded = true }); @@ -2130,27 +2133,27 @@ void Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_w_no_ ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), .callback = NoStagingSystemCreatePosition, - .no_readonly = true + .immediate = true }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), - .query.filter.terms = {{ ecs_id(Position) }}, + .query.terms = {{ ecs_id(Position) }}, .callback = ReadPosition, .multi_threaded = true }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate )} + .add = ecs_ids( ecs_dependson(EcsOnUpdate )) }), .callback = ReadPosition, - .no_readonly = true + .immediate = true }); if (task_threads) @@ -2190,7 +2193,7 @@ static int foo_system_invoked = 0; static void AddId(ecs_iter_t *it) { ecs_world_t *world = it->world; - ecs_id_t id = ecs_field_id(it, 1); + ecs_id_t id = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i++) { @@ -2223,7 +2226,7 @@ void Pipeline_multi_threaded_pipeline_change_w_only_singlethreaded_internal(bool ecs_set_threads(world, 2); } - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 0); @@ -2243,7 +2246,7 @@ void Pipeline_multi_threaded_tasks_pipeline_change_w_only_singlethreaded(void) { } static void SetPosition(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i ++) { p[i].x = 10; @@ -2260,7 +2263,7 @@ void Pipeline_sync_after_not_out_for_out(void) { ECS_SYSTEM(world, AddId, EcsOnUpdate, [out] !Position, Tag); ECS_SYSTEM(world, SetPosition, EcsOnUpdate, [out] Position); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); const ecs_world_info_t *wi = ecs_get_world_info(world); @@ -2286,7 +2289,7 @@ void Pipeline_pair_wildcard_read_after_staged_write(void) { ECS_SYSTEM(world, AddId, EcsOnUpdate, [out] !(Rel, Position), Tag); ECS_SYSTEM(world, SysA, EcsOnUpdate, [in] (Rel, *)); - ecs_new(world, Tag); + ecs_new_w(world, Tag); const ecs_world_info_t *wi = ecs_get_world_info(world); @@ -2304,7 +2307,7 @@ static int add_pair_invoked = 0; static void AddPair(ecs_iter_t *it) { ecs_world_t *world = it->world; - ecs_id_t id = ecs_field_id(it, 1); + ecs_id_t id = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i++) { @@ -2325,7 +2328,7 @@ void Pipeline_pair_read_after_staged_wildcard_write(void) { ECS_SYSTEM(world, AddPair, EcsOnUpdate, [out] !(Rel, *), Tag); ECS_SYSTEM(world, SysA, EcsOnUpdate, [in] (Rel, Position)); - ecs_new(world, Tag); + ecs_new_w(world, Tag); const ecs_world_info_t *wi = ecs_get_world_info(world); @@ -2350,7 +2353,7 @@ void Pipeline_no_sync_after_pair_wildcard_read_after_unmatching_staged_write(voi ECS_SYSTEM(world, AddPair, EcsOnUpdate, [out] !(Rel2, Position), Tag); ECS_SYSTEM(world, SysA, EcsOnUpdate, [in] (Rel, *)); - ecs_new(world, Tag); + ecs_new_w(world, Tag); const ecs_world_info_t *wi = ecs_get_world_info(world); @@ -2375,7 +2378,7 @@ void Pipeline_no_merge_after_from_nothing_w_default_inout(void) { ECS_SYSTEM(world, SysA, EcsOnUpdate, Tag, Position()); ECS_SYSTEM(world, SysB, EcsOnUpdate, Tag, Position); - ecs_new(world, Tag); + ecs_new_w(world, Tag); const ecs_world_info_t *wi = ecs_get_world_info(world); @@ -2393,8 +2396,8 @@ static int sys_add_tag_invoked = 0; static int sys_no_readonly_invoked = 0; static void sys_add_tag(ecs_iter_t *it) { - ecs_new(it->world, TagA); - ecs_new(it->world, TagB); + ecs_new_w(it->world, TagA); + ecs_new_w(it->world, TagB); sys_add_tag_invoked ++; test_assert(sys_a_invoked == 0); } @@ -2414,23 +2417,23 @@ void Pipeline_on_merge_activate_system_before_merge(void) { // system is annotated with TagA but writes both TagA, TagB ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), - .query.filter.terms = {{ TagA, .inout = EcsOut, .src.flags = EcsIsEntity }}, + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), + .query.terms = {{ TagA, .inout = EcsOut, .src.id = EcsIsEntity }}, .callback = sys_add_tag }); // no merge inserted between systems, but system activates after merge ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), - .query.filter.terms = {{ TagB, .inout = EcsIn }}, + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), + .query.terms = {{ TagB, .inout = EcsIn }}, .callback = SysA }); // read TagA, causes insertion of merge ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), - .query.filter.terms = {{ TagA, .inout = EcsIn }}, - .no_readonly = true, + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), + .query.terms = {{ TagA, .inout = EcsIn }}, + .immediate = true, .callback = sys_no_readonly }); @@ -2450,12 +2453,12 @@ void Pipeline_disable_phase(void) { ecs_world_t *world = ecs_init(); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .callback = SysA }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsPostUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsPostUpdate) )}), .callback = SysB }); @@ -2492,16 +2495,22 @@ void Pipeline_disable_phase(void) { void Pipeline_disable_parent(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t p1 = ecs_new_id(world); - ecs_entity_t p2 = ecs_new_id(world); + ecs_entity_t p1 = ecs_new(world); + ecs_entity_t p2 = ecs_new(world); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_childof(p1), ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { + .parent = p1, + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) + }), .callback = SysA }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_childof(p2), ecs_dependson(EcsPostUpdate) }}), + .entity = ecs_entity(world, { + .parent = p2, + .add = ecs_ids( ecs_dependson(EcsPostUpdate) ) + }), .callback = SysB }); @@ -2541,7 +2550,7 @@ static void NoReadonlyAddPosition(ecs_iter_t *it) { test_assert(it->world == it->real_world); no_staging_add_position_invoked ++; - ecs_entity_t e = ecs_new_id(it->world); + ecs_entity_t e = ecs_new(it->world); ecs_add(it->world, e, Position); } @@ -2561,15 +2570,15 @@ void Pipeline_multi_threaded_no_staging_w_add_after_read_internal(bool task_thre } ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), - .query.filter = { .terms = {{ ecs_id(Position) }}}, + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), + .query = { .terms = {{ ecs_id(Position) }}}, .callback = SysA }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .callback = NoReadonlyAddPosition, - .no_readonly = true + .immediate = true }); ecs_progress(world, 0); @@ -2601,12 +2610,12 @@ void Pipeline_1_startup_system(void) { const ecs_world_info_t *stats = ecs_get_world_info(world); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnStart) )}), .callback = SysA }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .callback = SysB }); @@ -2634,17 +2643,17 @@ void Pipeline_2_startup_systems(void) { const ecs_world_info_t *stats = ecs_get_world_info(world); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnStart) )}), .callback = SysA }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnStart) )}), .callback = SysB }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .callback = SysC }); @@ -2678,17 +2687,17 @@ void Pipeline_2_startup_phases(void) { ecs_add_pair(world, AfterStart, EcsDependsOn, EcsOnStart); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(AfterStart) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(AfterStart) )}), .callback = SysB }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnStart) )}), .callback = SysA }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .callback = SysC }); @@ -2718,24 +2727,24 @@ void Pipeline_2_startup_systems_w_merge(void) { ECS_COMPONENT(world, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); const ecs_world_info_t *stats = ecs_get_world_info(world); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}), - .query.filter = { .terms = {{ ecs_id(Position), .src.flags = EcsIsEntity, .inout = EcsOut }}}, + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnStart) )}), + .query = { .terms = {{ ecs_id(Position), .src.id = EcsIsEntity, .inout = EcsOut }}}, .callback = SysA }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}), - .query.filter = { .terms = {{ ecs_id(Position) }}}, + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnStart) )}), + .query = { .terms = {{ ecs_id(Position) }}}, .callback = SysB }); ecs_system(world, { - .entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, { .add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .callback = SysC }); @@ -2769,29 +2778,29 @@ void Pipeline_inactive_last_system_merge_count(void) { ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { TagA }, - { TagB, .src.flags = EcsIsEntity, .inout = EcsOut } + { TagB, .src.id = EcsIsEntity, .inout = EcsOut } }, .callback = SysA }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { TagB }, - { TagC, .src.flags = EcsIsEntity, .inout = EcsOut } + { TagC, .src.id = EcsIsEntity, .inout = EcsOut } }, .callback = SysB }); test_int(stats->merge_count_total, 0); - ecs_new(world, TagA); + ecs_new_w(world, TagA); ecs_progress(world, 0); @@ -2815,42 +2824,42 @@ void Pipeline_inactive_middle_system_merge_count(void) { ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { TagA }, - { TagB, .src.flags = EcsIsEntity, .inout = EcsOut } + { TagB, .src.id = EcsIsEntity, .inout = EcsOut } }, .callback = SysA }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { TagB }, - { TagC, .src.flags = EcsIsEntity, .inout = EcsOut } + { TagC, .src.id = EcsIsEntity, .inout = EcsOut } }, .callback = SysC }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { TagC }, - { TagD, .src.flags = EcsIsEntity, .inout = EcsOut } + { TagD, .src.id = EcsIsEntity, .inout = EcsOut } }, .callback = SysD }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { TagD } }, .callback = SysB @@ -2858,8 +2867,8 @@ void Pipeline_inactive_middle_system_merge_count(void) { test_int(stats->merge_count_total, 0); - ecs_new(world, TagA); - ecs_new(world, TagD); + ecs_new_w(world, TagA); + ecs_new_w(world, TagD); ecs_progress(world, 0); @@ -2875,7 +2884,7 @@ void Pipeline_inactive_middle_system_merge_count(void) { static void CreateEntity(ecs_iter_t *it) { - ecs_id_t tag = ecs_field_id(it, 1); + ecs_id_t tag = ecs_field_id(it, 0); ecs_new_w_id(it->world, tag); } @@ -2891,30 +2900,30 @@ void Pipeline_last_no_readonly_system_merge_count(void) { ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { TagA }, - { TagB, .src.flags = EcsIsEntity, .inout = EcsOut } + { TagB, .src.id = EcsIsEntity, .inout = EcsOut } }, .callback = SysA }); ecs_system(world, { .entity = ecs_entity(world, { - .add = { ecs_dependson(EcsOnUpdate) } + .add = ecs_ids( ecs_dependson(EcsOnUpdate) ) }), - .query.filter.terms = { + .query.terms = { { TagD } }, .callback = CreateEntity, - .no_readonly = true + .immediate = true }); test_int(stats->merge_count_total, 0); - ecs_new(world, TagA); - ecs_new(world, TagD); + ecs_new_w(world, TagA); + ecs_new_w(world, TagD); test_int(1, ecs_count(world, TagD)); @@ -2935,7 +2944,7 @@ void Pipeline_2_pipelines_1_system(void) { ecs_entity_t p1 = ecs_pipeline(world, { .query = { - .filter.terms = { + .terms = { { .id = EcsSystem }, { .id = Phase1 } } @@ -2944,7 +2953,7 @@ void Pipeline_2_pipelines_1_system(void) { ecs_entity_t p2 = ecs_pipeline(world, { .query = { - .filter.terms = { + .terms = { { .id = EcsSystem }, { .id = Phase2 } } @@ -2953,7 +2962,7 @@ void Pipeline_2_pipelines_1_system(void) { ecs_system(world, { .entity = ecs_entity(world, { - .add = { Phase1 } + .add = ecs_ids( Phase1 ) }), .callback = SysA }); @@ -2980,12 +2989,11 @@ void Pipeline_builtin_pipeline_w_self_system_term(void) { const EcsPoly *p = ecs_get_pair(world, pipeline, EcsPoly, EcsQuery); test_assert(p != NULL); - test_assert(ecs_poly_is(p->poly, ecs_query_t)); + test_assert(flecs_poly_is(p->poly, ecs_query_t)); ecs_query_t *q = p->poly; - const ecs_filter_t *f = ecs_query_get_filter(q); - test_assert(f != NULL); - test_assert((f->terms[0].src.flags & EcsTraverseFlags) == EcsSelf); + test_assert(q != NULL); + test_assert((q->terms[0].src.id & EcsTraverseFlags) == EcsSelf); ecs_fini(world); } @@ -2996,7 +3004,7 @@ void Pipeline_custom_pipeline_w_self_system_term(void) { ECS_TAG(world, Tag); ecs_entity_t pipeline = ecs_pipeline(world, { - .query.filter.terms = { + .query.terms = { { EcsSystem }, { Tag } } @@ -3005,12 +3013,11 @@ void Pipeline_custom_pipeline_w_self_system_term(void) { const EcsPoly *p = ecs_get_pair(world, pipeline, EcsPoly, EcsQuery); test_assert(p != NULL); - test_assert(ecs_poly_is(p->poly, ecs_query_t)); + test_assert(flecs_poly_is(p->poly, ecs_query_t)); ecs_query_t *q = p->poly; - const ecs_filter_t *f = ecs_query_get_filter(q); - test_assert(f != NULL); - test_assert((f->terms[0].src.flags & EcsTraverseFlags) == EcsSelf); + test_assert(q != NULL); + test_assert((q->terms[0].src.id & EcsTraverseFlags) == EcsSelf); ecs_fini(world); } @@ -3021,16 +3028,16 @@ void Pipeline_switch_from_threads_to_tasks(void) { ECS_COMPONENT_DEFINE(world, Position); ecs_system(world, { - .entity = ecs_entity(world, {.add = { ecs_dependson(EcsOnUpdate) }}), - .query.filter = {.terms = {{ ecs_id(Position) }}}, + .entity = ecs_entity(world, {.add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), + .query = {.terms = {{ ecs_id(Position) }}}, .callback = SysA - }); + }); ecs_system(world, { - .entity = ecs_entity(world, {.add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, {.add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .callback = NoReadonlyAddPosition, - .no_readonly = true - }); + .immediate = true + }); ecs_set_threads(world, 2); @@ -3057,15 +3064,15 @@ void Pipeline_switch_from_tasks_to_threads(void) { ECS_COMPONENT_DEFINE(world, Position); ecs_system(world, { - .entity = ecs_entity(world, {.add = { ecs_dependson(EcsOnUpdate) }}), - .query.filter = {.terms = {{ ecs_id(Position) }}}, + .entity = ecs_entity(world, {.add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), + .query = {.terms = {{ ecs_id(Position) }}}, .callback = SysA }); ecs_system(world, { - .entity = ecs_entity(world, {.add = { ecs_dependson(EcsOnUpdate) }}), + .entity = ecs_entity(world, {.add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), .callback = NoReadonlyAddPosition, - .no_readonly = true + .immediate = true }); ecs_set_threads(world, 2); @@ -3094,40 +3101,40 @@ void Pipeline_run_pipeline_multithreaded_internal(bool task_threads) { ECS_COMPONENT(world, Position); ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysA", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysA", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysA, .multi_threaded = true }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysB", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysB", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysB }); ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysC", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysC", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysC }); ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysD", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysD", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysD, .multi_threaded = true }); ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysE", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysE", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysE, .multi_threaded = true }); ecs_entity_t s6 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "SysF", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "SysF", .add = ecs_ids(Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)) }), + .query.expr = "Position", .callback = SysF }); - ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag); + ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade DependsOn), Tag); test_assert(s1 != 0); test_assert(s2 != 0); @@ -3136,8 +3143,8 @@ void Pipeline_run_pipeline_multithreaded_internal(bool task_threads) { test_assert(s5 != 0); test_assert(s6 != 0); - ecs_new(world, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); + ecs_new_w(world, Position); if (task_threads) { @@ -3203,6 +3210,78 @@ void Pipeline_pipeline_init_no_system_term(void) { test_expect_abort(); ecs_pipeline(world, { - .query.filter.terms = {{ ecs_id(Position) }} + .query.terms = {{ ecs_id(Position) }} }); } + +static ECS_DECLARE(ToggleTag); +static ecs_entity_t toggle_entity = 0; +static int toggle_immediate_system_invoked = 0; + +static void ToggleImmediateSystem(ecs_iter_t *it) { + ecs_defer_suspend(it->world); + ecs_enable_id(it->world, toggle_entity, ToggleTag, false); + ecs_defer_resume(it->world); + toggle_immediate_system_invoked ++; +} + +void Pipeline_disable_component_from_immediate_system(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY_DEFINE(world, ToggleTag, CanToggle); + + toggle_entity = ecs_new_w(world, ToggleTag); + + ecs_system(world, { + .entity = ecs_entity(world, { + .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) + }), + .callback = ToggleImmediateSystem, + .immediate = true + }); + + ecs_progress(world, 0); + test_int(toggle_immediate_system_invoked, 1); + test_bool(ecs_is_deferred(world), false); + + ecs_fini(world); +} + +void Pipeline_run_w_empty_query(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_system(world, { + .entity = ecs_entity(world, { + .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) + }), + .run = SysA + }); + + ecs_progress(world, 0); + test_int(sys_a_invoked, 1); + + ecs_fini(world); +} + +void Pipeline_run_w_0_src_query(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_system(world, { + .entity = ecs_entity(world, { + .add = ecs_ids(ecs_pair(EcsDependsOn, EcsOnUpdate)) + }), + .query.terms = { + { ecs_id(Position), .src.id = EcsIsEntity } + }, + .run = SysA + }); + + ecs_progress(world, 0); + test_int(sys_a_invoked, 1); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/addons/src/Plecs.c b/vendors/flecs/test/addons/src/Plecs.c deleted file mode 100644 index efb863451..000000000 --- a/vendors/flecs/test/addons/src/Plecs.c +++ /dev/null @@ -1,7887 +0,0 @@ -#include - -#define HEAD -#define LINE "\n" - -void Plecs_null(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, NULL) == 0); - - ecs_fini(world); -} - -void Plecs_empty(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "") == 0); - - ecs_fini(world); -} - -void Plecs_space(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, " ") == 0); - - ecs_fini(world); -} - -void Plecs_space_newline(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, " \n \n") == 0); - - ecs_fini(world); -} - -void Plecs_two_empty_newlines(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "\n\n") == 0); - - ecs_fini(world); -} - -void Plecs_three_empty_newlines(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "\n\n\n") == 0); - - ecs_fini(world); -} - -void Plecs_newline_trailing_space(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "\n ") == 0); - - ecs_fini(world); -} - -void Plecs_newline_trailing_spaces(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "\n ") == 0); - - ecs_fini(world); -} - -void Plecs_multiple_trailing_newlines(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo\n\n\n") == 0); - - ecs_entity_t foo = ecs_lookup(world, "Foo"); - test_assert(foo != 0); - test_str(ecs_get_name(world, foo), "Foo"); - test_int(ecs_get_type(world, foo)->count, 1); - - ecs_fini(world); -} - -void Plecs_entity(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo") == 0); - - ecs_entity_t foo = ecs_lookup(world, "Foo"); - test_assert(foo != 0); - test_str(ecs_get_name(world, foo), "Foo"); - test_int(ecs_get_type(world, foo)->count, 1); - - ecs_fini(world); -} - -void Plecs_entity_w_entity(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo(Subj)") == 0); - - ecs_entity_t foo = ecs_lookup(world, "Foo"); - test_assert(foo != 0); - test_str(ecs_get_name(world, foo), "Foo"); - - ecs_entity_t subj = ecs_lookup(world, "Subj"); - test_assert(subj != 0); - test_str(ecs_get_name(world, subj), "Subj"); - - test_assert(ecs_has_id(world, subj, foo)); - - ecs_fini(world); -} - -void Plecs_entity_w_pair(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Rel(Subj, Obj)") == 0); - - ecs_entity_t rel = ecs_lookup(world, "Rel"); - test_assert(rel != 0); - test_str(ecs_get_name(world, rel), "Rel"); - - ecs_entity_t obj = ecs_lookup(world, "Obj"); - test_assert(obj != 0); - test_str(ecs_get_name(world, obj), "Obj"); - - ecs_entity_t subj = ecs_lookup(world, "Subj"); - test_assert(subj != 0); - test_str(ecs_get_name(world, subj), "Subj"); - - test_assert(ecs_has_pair(world, subj, rel, obj)); - - ecs_fini(world); -} - -void Plecs_2_entities(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo\nBar\n") == 0); - - ecs_entity_t e = ecs_lookup(world, "Foo"); - test_assert(e != 0); - test_str(ecs_get_name(world, e), "Foo"); - - ecs_fini(world); -} - -void Plecs_2_entities_w_entities(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo(Subj_1)\nBar(Subj_2)\n") == 0); - - ecs_entity_t foo = ecs_lookup(world, "Foo"); - test_assert(foo != 0); - test_str(ecs_get_name(world, foo), "Foo"); - - ecs_entity_t bar = ecs_lookup(world, "Bar"); - test_assert(bar != 0); - test_str(ecs_get_name(world, bar), "Bar"); - - ecs_entity_t subj_1 = ecs_lookup(world, "Subj_1"); - test_assert(subj_1 != 0); - test_str(ecs_get_name(world, subj_1), "Subj_1"); - - ecs_entity_t subj_2 = ecs_lookup(world, "Subj_2"); - test_assert(subj_2 != 0); - test_str(ecs_get_name(world, subj_2), "Subj_2"); - - test_assert(ecs_has_id(world, subj_1, foo)); - test_assert(ecs_has_id(world, subj_2, bar)); - - ecs_fini(world); -} - -void Plecs_3_entities_w_pairs(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, - "Rel_1(Subj_1, Obj_1)\n" - "Rel_1(Subj_2, Obj_2)\n" - "Rel_2(Subj_3, Obj_2)\n") == 0); - - ecs_entity_t rel_1 = ecs_lookup(world, "Rel_1"); - test_assert(rel_1 != 0); - test_str(ecs_get_name(world, rel_1), "Rel_1"); - - ecs_entity_t rel_2 = ecs_lookup(world, "Rel_2"); - test_assert(rel_2 != 0); - test_str(ecs_get_name(world, rel_2), "Rel_2"); - - ecs_entity_t obj_1 = ecs_lookup(world, "Obj_1"); - test_assert(obj_1 != 0); - test_str(ecs_get_name(world, obj_1), "Obj_1"); - - ecs_entity_t obj_2 = ecs_lookup(world, "Obj_2"); - test_assert(obj_2 != 0); - test_str(ecs_get_name(world, obj_2), "Obj_2"); - - ecs_entity_t subj_1 = ecs_lookup(world, "Subj_1"); - test_assert(subj_1 != 0); - test_str(ecs_get_name(world, subj_1), "Subj_1"); - - ecs_entity_t subj_2 = ecs_lookup(world, "Subj_2"); - test_assert(subj_2 != 0); - test_str(ecs_get_name(world, subj_2), "Subj_2"); - - ecs_entity_t subj_3 = ecs_lookup(world, "Subj_3"); - test_assert(subj_3 != 0); - test_str(ecs_get_name(world, subj_3), "Subj_3"); - - test_assert(ecs_has_pair(world, subj_1, rel_1, obj_1)); - test_assert(ecs_has_pair(world, subj_2, rel_1, obj_2)); - test_assert(ecs_has_pair(world, subj_3, rel_2, obj_2)); - - ecs_fini(world); -} - -void Plecs_line_comment(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "// Foo(Bar)\n") == 0); - - test_assert(ecs_lookup(world, "Foo") == 0); - test_assert(ecs_lookup(world, "Bar") == 0); - - ecs_fini(world); -} - -void Plecs_line_comment_before_stmt(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "// Hello(World)\nFoo\n") == 0); - - test_assert(ecs_lookup(world, "Hello") == 0); - test_assert(ecs_lookup(world, "World") == 0); - test_assert(ecs_lookup(world, "Foo") != 0); - - ecs_fini(world); -} - -void Plecs_line_comment_after_stmt(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo\n// Hello(World)\n") == 0); - - test_assert(ecs_lookup(world, "Hello") == 0); - test_assert(ecs_lookup(world, "World") == 0); - test_assert(ecs_lookup(world, "Foo") != 0); - - ecs_fini(world); -} - -void Plecs_line_comment_between_stmt(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo\n// Hello(World)\nBar\n") == 0); - - test_assert(ecs_lookup(world, "Hello") == 0); - test_assert(ecs_lookup(world, "World") == 0); - test_assert(ecs_lookup(world, "Foo") != 0); - test_assert(ecs_lookup(world, "Bar") != 0); - - ecs_fini(world); -} - -void Plecs_multiple_line_comment(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "// Hello(World)\n// Boo(Baz)\nFoo") == 0); - - test_assert(ecs_lookup(world, "Hello") == 0); - test_assert(ecs_lookup(world, "World") == 0); - test_assert(ecs_lookup(world, "Boo") == 0); - test_assert(ecs_lookup(world, "Baz") == 0); - test_assert(ecs_lookup(world, "Foo") != 0); - - ecs_fini(world); -} - -void Plecs_line_comment_after_stmt_same_line(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo // Hello(World)\nBar\n") == 0); - - test_assert(ecs_lookup(world, "Hello") == 0); - test_assert(ecs_lookup(world, "World") == 0); - test_assert(ecs_lookup(world, "Foo") != 0); - test_assert(ecs_lookup(world, "Bar") != 0); - - ecs_fini(world); -} - -void Plecs_comma_separated_pred(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo,Bar,Hello,Worlds") == 0); - - test_assert(ecs_lookup(world, "Hello") != 0); - test_assert(ecs_lookup(world, "Worlds") != 0); - test_assert(ecs_lookup(world, "Foo") != 0); - test_assert(ecs_lookup(world, "Bar") != 0); - - ecs_fini(world); -} - -void Plecs_comma_separated_pred_w_subj(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo(Bar),Hello(Worlds)") == 0); - - ecs_entity_t foo = ecs_lookup(world, "Foo"); - ecs_entity_t bar = ecs_lookup(world, "Bar"); - ecs_entity_t hello = ecs_lookup(world, "Hello"); - ecs_entity_t _world = ecs_lookup(world, "Worlds"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello != 0); - test_assert(_world != 0); - - test_assert(ecs_has_id(world, bar, foo)); - test_assert(ecs_has_id(world, _world, hello)); - - ecs_fini(world); -} - -void Plecs_comma_separated_pred_w_subj_obj(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo(Bar, Obj1),Hello(Worlds, Obj2)") == 0); - - ecs_entity_t foo = ecs_lookup(world, "Foo"); - ecs_entity_t bar = ecs_lookup(world, "Bar"); - ecs_entity_t hello = ecs_lookup(world, "Hello"); - ecs_entity_t _world = ecs_lookup(world, "Worlds"); - ecs_entity_t obj1 = ecs_lookup(world, "Obj1"); - ecs_entity_t obj2 = ecs_lookup(world, "Obj2"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello != 0); - test_assert(_world != 0); - test_assert(obj1 != 0); - test_assert(obj2 != 0); - - test_assert(ecs_has_pair(world, bar, foo, obj1)); - test_assert(ecs_has_pair(world, _world, hello, obj2)); - - ecs_fini(world); -} - -void Plecs_comma_separated_pred_trailing_comma(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo,Bar,Hello,Worlds,") == 0); - - test_assert(ecs_lookup(world, "Hello") != 0); - test_assert(ecs_lookup(world, "Worlds") != 0); - test_assert(ecs_lookup(world, "Foo") != 0); - test_assert(ecs_lookup(world, "Bar") != 0); - - ecs_fini(world); -} - -void Plecs_comma_separated_pred_trailing_comma_newline(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo,Bar,Hello,Worlds,\n") == 0); - - test_assert(ecs_lookup(world, "Hello") != 0); - test_assert(ecs_lookup(world, "Worlds") != 0); - test_assert(ecs_lookup(world, "Foo") != 0); - test_assert(ecs_lookup(world, "Bar") != 0); - - ecs_fini(world); -} - -void Plecs_comma_separated_pred_trailing_comma_newline_multiline(void) { - ecs_world_t *world = ecs_init(); - - test_assert(ecs_plecs_from_str(world, NULL, "Foo,Bar,\nHello,Worlds,") == 0); - - test_assert(ecs_lookup(world, "Hello") != 0); - test_assert(ecs_lookup(world, "Worlds") != 0); - test_assert(ecs_lookup(world, "Foo") != 0); - test_assert(ecs_lookup(world, "Bar") != 0); - - ecs_fini(world); -} - -void Plecs_hierarchy_1_child(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent {" - LINE " Child" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child"); - - test_assert(parent != 0); - test_assert(child != 0); - - test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - - ecs_fini(world); -} - -void Plecs_hierarchy_2_children(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent {" - LINE " ChildA" - LINE " ChildB" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "ChildA") == 0); - test_assert(ecs_lookup_fullpath(world, "ChildB") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA"); - ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB"); - - test_assert(parent != 0); - test_assert(child_a != 0); - test_assert(child_b != 0); - - test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent)); - test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent)); - - ecs_fini(world); -} - -void Plecs_hierarchy_1_child_same_line(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent { Child }"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child"); - - test_assert(parent != 0); - test_assert(child != 0); - - test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - - ecs_fini(world); -} - -void Plecs_hierarchy_2_children_same_line(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent { ChildA, ChildB }"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "ChildA") == 0); - test_assert(ecs_lookup_fullpath(world, "ChildB") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA"); - ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB"); - - test_assert(parent != 0); - test_assert(child_a != 0); - test_assert(child_b != 0); - - test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent)); - test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent)); - - ecs_fini(world); -} - -void Plecs_entity_after_hierarchy(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent {" - LINE " Child" - LINE "}" - LINE "Foo"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(parent != 0); - test_assert(child != 0); - test_assert(foo != 0); - - test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - test_assert(!ecs_has_pair(world, foo, EcsChildOf, parent)); - test_assert(!ecs_has_pair(world, foo, EcsChildOf, EcsWildcard)); - - ecs_fini(world); -} - -void Plecs_newline_before_scope_open(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent" - LINE "{" - LINE " Child" - LINE "}" - LINE "Foo"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child"); - - test_assert(parent != 0); - test_assert(child != 0); - - test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - - ecs_fini(world); -} - -void Plecs_comment_before_scope_open(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent // Some(Comment)" - LINE "{" - LINE " Child" - LINE "}" - LINE "Foo"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - - test_assert(ecs_lookup_fullpath(world, "Some") == 0); - test_assert(ecs_lookup_fullpath(world, "Comment") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child"); - - test_assert(parent != 0); - test_assert(child != 0); - - test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - - ecs_fini(world); -} - -void Plecs_comment_after_newline_before_scope_open(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent" - LINE "// Some(Comment)" - LINE "{" - LINE " Child" - LINE "}" - LINE "Foo"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - - test_assert(ecs_lookup_fullpath(world, "Some") == 0); - test_assert(ecs_lookup_fullpath(world, "Comment") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child"); - - test_assert(parent != 0); - test_assert(child != 0); - - test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - - ecs_fini(world); -} - -void Plecs_hierarchy_2_levels(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent {" - LINE " Child {" - LINE " GrandChild" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - test_assert(ecs_lookup_fullpath(world, "GrandChild") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child"); - ecs_entity_t grandchild = ecs_lookup_fullpath(world, "Parent.Child.GrandChild"); - - test_assert(parent != 0); - test_assert(child != 0); - test_assert(grandchild != 0); - - test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - test_assert(ecs_has_pair(world, grandchild, EcsChildOf, child)); - - ecs_fini(world); -} - -void Plecs_hierarchy_2_levels_2_subtrees(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent {" - LINE " ChildA {" - LINE " GrandChild" - LINE " }" - LINE " ChildB {" - LINE " GrandChild" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "ChildA") == 0); - test_assert(ecs_lookup_fullpath(world, "ChildB") == 0); - test_assert(ecs_lookup_fullpath(world, "GrandChild") == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA"); - ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB"); - ecs_entity_t grandchild_a = ecs_lookup_fullpath(world, "Parent.ChildA.GrandChild"); - ecs_entity_t grandchild_b = ecs_lookup_fullpath(world, "Parent.ChildB.GrandChild"); - - test_assert(parent != 0); - test_assert(child_a != 0); - test_assert(child_b != 0); - test_assert(child_a != child_b); - test_assert(grandchild_a != 0); - test_assert(grandchild_b != 0); - test_assert(grandchild_a != grandchild_b); - - test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent)); - test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent)); - test_assert(ecs_has_pair(world, grandchild_a, EcsChildOf, child_a)); - test_assert(ecs_has_pair(world, grandchild_b, EcsChildOf, child_b)); - - ecs_fini(world); -} - -void Plecs_missing_end_of_scope(void) { - ecs_log_set_level(-4); - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent {" - LINE " Child"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - test_assert(ecs_get_scope(world) == 0); - test_assert(ecs_get_with(world) == 0); - - ecs_fini(world); -} - -void Plecs_missing_end_of_predicate_scope(void) { - ecs_log_set_level(-4); - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent() {" - LINE " Child"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - test_assert(ecs_get_scope(world) == 0); - test_assert(ecs_get_with(world) == 0); - - ecs_fini(world); -} - -void Plecs_create_in_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - - ecs_set_scope(world, Root); - - const char *expr = - HEAD "Parent { Child }"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_get_scope(world) == Root); - - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - - ecs_set_scope(world, 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child"); - - test_assert(parent != 0); - test_assert(child != 0); - - test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - test_assert(!ecs_has_pair(world, parent, EcsChildOf, Root)); - - ecs_fini(world); -} - -void Plecs_hierarchy_w_pred_subj(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo(Bar) {" - LINE " Child" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Bar.Child"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(child != 0); - - test_assert(ecs_has_id(world, bar, foo)); - test_assert(ecs_has_pair(world, child, EcsChildOf, bar)); - - ecs_fini(world); -} - -void Plecs_hierarchy_custom_relation(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "(Foo, Bar) {" - LINE " Child" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Child"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(child != 0); - - test_assert(!ecs_has_id(world, bar, foo)); - test_assert(!ecs_has_id(world, foo, bar)); - test_assert(!ecs_has_pair(world, child, EcsChildOf, foo)); - test_assert(!ecs_has_pair(world, child, EcsChildOf, bar)); - test_assert(ecs_has_pair(world, child, foo, bar)); - - ecs_fini(world); -} - -void Plecs_hierarchy_custom_relation_2_levels(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "(Foo, Bar) {" - LINE " (Rel, ObjA) {" - LINE " Child" - LINE " ChildA" - LINE " }" - LINE " (Rel, ObjB) {" - LINE " Child" - LINE " ChildB" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel"); - ecs_entity_t obj_a = ecs_lookup_fullpath(world, "ObjA"); - ecs_entity_t obj_b = ecs_lookup_fullpath(world, "ObjB"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Child"); - ecs_entity_t child_a = ecs_lookup_fullpath(world, "ChildA"); - ecs_entity_t child_b = ecs_lookup_fullpath(world, "ChildB"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(rel != 0); - test_assert(obj_a != 0); - test_assert(obj_b != 0); - test_assert(child != 0); - test_assert(child_a != 0); - test_assert(child_b != 0); - - test_assert(!ecs_has_id(world, bar, foo)); - test_assert(!ecs_has_id(world, foo, bar)); - - test_assert(!ecs_has_id(world, rel, obj_a)); - test_assert(!ecs_has_id(world, obj_a, rel)); - - test_assert(!ecs_has_id(world, rel, obj_b)); - test_assert(!ecs_has_id(world, obj_b, rel)); - - test_assert(!ecs_has_pair(world, child, EcsChildOf, foo)); - test_assert(!ecs_has_pair(world, child, EcsChildOf, bar)); - - test_assert(ecs_has_pair(world, child, rel, obj_a)); - test_assert(ecs_has_pair(world, child, rel, obj_b)); - - test_assert(ecs_has_pair(world, child_a, rel, obj_a)); - test_assert(ecs_has_pair(world, child_b, rel, obj_b)); - - ecs_fini(world); -} - -void Plecs_entity_after_hierarchy_custom_relation(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "(Foo, Bar) {" - LINE " Child" - LINE "}" - LINE "Hello"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Child"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(child != 0); - test_assert(hello != 0); - - test_assert(!ecs_has_id(world, bar, foo)); - test_assert(!ecs_has_id(world, foo, bar)); - test_assert(!ecs_has_pair(world, child, EcsChildOf, foo)); - test_assert(!ecs_has_pair(world, child, EcsChildOf, bar)); - test_assert(ecs_has_pair(world, child, foo, bar)); - test_assert(!ecs_has_pair(world, hello, foo, bar)); - - ecs_fini(world); -} - -void Plecs_entity_after_hierarchy_custom_relation_2_levels(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "(Foo, Bar) {" - LINE " (Rel, ObjA) {" - LINE " Child" - LINE " ChildA" - LINE " }" - LINE " TestA" - LINE " (Rel, ObjB) {" - LINE " Child" - LINE " ChildB" - LINE " }" - LINE "}" - LINE "TestB"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel"); - ecs_entity_t obj_a = ecs_lookup_fullpath(world, "ObjA"); - ecs_entity_t obj_b = ecs_lookup_fullpath(world, "ObjB"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Child"); - ecs_entity_t child_a = ecs_lookup_fullpath(world, "ChildA"); - ecs_entity_t child_b = ecs_lookup_fullpath(world, "ChildB"); - - ecs_entity_t test_a = ecs_lookup_fullpath(world, "TestA"); - ecs_entity_t test_b = ecs_lookup_fullpath(world, "TestB"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(rel != 0); - test_assert(obj_a != 0); - test_assert(obj_b != 0); - test_assert(child != 0); - test_assert(child_a != 0); - test_assert(child_b != 0); - - test_assert(test_a != 0); - test_assert(test_b != 0); - - test_assert(!ecs_has_id(world, bar, foo)); - test_assert(!ecs_has_id(world, foo, bar)); - - test_assert(!ecs_has_id(world, rel, obj_a)); - test_assert(!ecs_has_id(world, obj_a, rel)); - - test_assert(!ecs_has_id(world, rel, obj_b)); - test_assert(!ecs_has_id(world, obj_b, rel)); - - test_assert(!ecs_has_pair(world, child, EcsChildOf, foo)); - test_assert(!ecs_has_pair(world, child, EcsChildOf, bar)); - - test_assert(ecs_has_pair(world, child, rel, obj_a)); - test_assert(ecs_has_pair(world, child, rel, obj_b)); - - test_assert(ecs_has_pair(world, child_a, rel, obj_a)); - test_assert(ecs_has_pair(world, child_b, rel, obj_b)); - - test_assert(ecs_has_pair(world, test_a, foo, bar)); - test_assert(!ecs_has_pair(world, test_a, rel, EcsWildcard)); - - test_assert(!ecs_has_pair(world, test_b, foo, bar)); - test_assert(!ecs_has_pair(world, test_b, rel, EcsWildcard)); - - ecs_fini(world); -} - -void Plecs_hierarchy_custom_relation_apply_to_object(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "(Rel, ObjA) {" - LINE " (Rel, ObjB) {" - LINE " ObjC" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel"); - ecs_entity_t obj_a = ecs_lookup_fullpath(world, "ObjA"); - ecs_entity_t obj_b = ecs_lookup_fullpath(world, "ObjB"); - ecs_entity_t obj_c = ecs_lookup_fullpath(world, "ObjC"); - - test_assert(rel != 0); - test_assert(obj_a != 0); - test_assert(obj_b != 0); - test_assert(obj_c != 0); - - test_assert(ecs_has_pair(world, obj_b, rel, obj_a)); - test_assert(ecs_has_pair(world, obj_c, rel, obj_b)); - test_assert(!ecs_has_pair(world, obj_c, rel, obj_a)); - - ecs_fini(world); -} - -void Plecs_hierarchy_custom_relation_apply_to_object_2_levels(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "(Rel, ObjA) {" - LINE " (Rel, ObjB) {" - LINE " (Rel, ObjC) {" - LINE " ObjD" - LINE " }" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel"); - ecs_entity_t obj_a = ecs_lookup_fullpath(world, "ObjA"); - ecs_entity_t obj_b = ecs_lookup_fullpath(world, "ObjB"); - ecs_entity_t obj_c = ecs_lookup_fullpath(world, "ObjC"); - ecs_entity_t obj_d = ecs_lookup_fullpath(world, "ObjD"); - - test_assert(rel != 0); - test_assert(obj_a != 0); - test_assert(obj_b != 0); - test_assert(obj_c != 0); - test_assert(obj_d != 0); - - test_assert(ecs_has_pair(world, obj_b, rel, obj_a)); - test_assert(ecs_has_pair(world, obj_c, rel, obj_b)); - test_assert(!ecs_has_pair(world, obj_c, rel, obj_a)); - test_assert(ecs_has_pair(world, obj_d, rel, obj_c)); - test_assert(!ecs_has_pair(world, obj_d, rel, obj_b)); - test_assert(!ecs_has_pair(world, obj_d, rel, obj_a)); - - ecs_fini(world); -} - -void Plecs_pred_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo() {" - LINE " Hello" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello"); - - test_assert(foo != 0); - test_assert(hello != 0); - - test_assert(ecs_has_pair(world, hello, EcsChildOf, foo)); - test_assert(!ecs_has_id(world, hello, foo)); - - ecs_fini(world); -} - -void Plecs_pred_scope_2_levels(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo() {" - LINE " Bar() {" - LINE " Hello" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Bar.Hello"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello != 0); - - test_assert(!ecs_has_pair(world, hello, EcsChildOf, foo)); - test_assert(ecs_has_pair(world, hello, EcsChildOf, bar)); - test_assert(!ecs_has_id(world, hello, foo)); - test_assert(!ecs_has_id(world, hello, bar)); - - test_assert(!ecs_has_pair(world, bar, EcsChildOf, foo)); - test_assert(!ecs_has_id(world, bar, foo)); - - ecs_fini(world); -} - -void Plecs_pred_scope_inside_with(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with Tag {" - HEAD " Foo() {" - LINE " Hello" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello"); - - test_assert(foo != 0); - test_assert(hello != 0); - test_assert(tag != 0); - - test_assert(ecs_has_pair(world, hello, EcsChildOf, foo)); - test_assert(!ecs_has_id(world, hello, foo)); - test_assert(ecs_has_id(world, hello, tag)); - - test_assert(!ecs_has_id(world, foo, tag)); - - ecs_fini(world); -} - -void Plecs_pred_scope_nested_w_subj_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent {" - HEAD " Foo() {" - LINE " Hello" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello"); - - test_assert(parent != 0); - test_assert(foo != 0); - test_assert(hello != 0); - - test_assert(!ecs_has_pair(world, hello, EcsChildOf, parent)); - test_assert(ecs_has_pair(world, hello, EcsChildOf, foo)); - test_assert(!ecs_has_id(world, hello, foo)); - - test_assert(!ecs_has_pair(world, foo, EcsChildOf, parent)); - - ecs_fini(world); -} - - -void Plecs_with_tag(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with Tag {" - LINE " Foo" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag"); - - test_assert(foo != 0); - test_assert(tag != 0); - - test_assert(ecs_has_id(world, foo, tag)); - - ecs_fini(world); -} - -void Plecs_with_tag_2_entities(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with Tag {" - LINE " Foo" - LINE " Bar" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(tag != 0); - - test_assert(ecs_has_id(world, foo, tag)); - test_assert(ecs_has_id(world, bar, tag)); - - ecs_fini(world); -} - -void Plecs_with_tag_same_line(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with Tag { Foo }"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag"); - - test_assert(foo != 0); - test_assert(tag != 0); - - test_assert(ecs_has_id(world, foo, tag)); - - ecs_fini(world); -} - -void Plecs_with_tag_2_entities_same_line(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with Tag { Foo, Bar }"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(tag != 0); - - test_assert(ecs_has_id(world, foo, tag)); - test_assert(ecs_has_id(world, bar, tag)); - - ecs_fini(world); -} - -void Plecs_with_tag_2_levels(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with TagA {" - HEAD " with TagB {" - LINE " Foo" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t tag_a = ecs_lookup_fullpath(world, "TagA"); - ecs_entity_t tag_b = ecs_lookup_fullpath(world, "TagB"); - - test_assert(foo != 0); - test_assert(tag_a != 0); - test_assert(tag_b != 0); - - test_assert(ecs_has_id(world, foo, tag_a)); - test_assert(ecs_has_id(world, foo, tag_b)); - - ecs_fini(world); -} - -void Plecs_with_tag_2_levels_2_subtrees(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with TagA {" - HEAD " with TagB {" - LINE " Foo" - LINE " BarA" - LINE " }" - HEAD " with TagC {" - LINE " Foo" - LINE " BarB" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t tag_a = ecs_lookup_fullpath(world, "TagA"); - ecs_entity_t tag_b = ecs_lookup_fullpath(world, "TagB"); - ecs_entity_t tag_c = ecs_lookup_fullpath(world, "TagC"); - ecs_entity_t bar_a = ecs_lookup_fullpath(world, "BarA"); - ecs_entity_t bar_b = ecs_lookup_fullpath(world, "BarB"); - - test_assert(foo != 0); - test_assert(tag_a != 0); - test_assert(tag_b != 0); - test_assert(bar_a != 0); - test_assert(bar_b != 0); - - test_assert(ecs_has_id(world, foo, tag_a)); - test_assert(ecs_has_id(world, foo, tag_b)); - test_assert(ecs_has_id(world, foo, tag_c)); - - test_assert(ecs_has_id(world, bar_a, tag_a)); - test_assert(ecs_has_id(world, bar_b, tag_a)); - - test_assert(ecs_has_id(world, bar_a, tag_b)); - test_assert(ecs_has_id(world, bar_b, tag_c)); - - test_assert(!ecs_has_id(world, bar_b, tag_b)); - test_assert(!ecs_has_id(world, bar_a, tag_c)); - - ecs_fini(world); -} - -void Plecs_with_n_tags(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with TagA, TagB {" - LINE " Foo" - LINE " Bar" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t tag_a = ecs_lookup_fullpath(world, "TagA"); - ecs_entity_t tag_b = ecs_lookup_fullpath(world, "TagB"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(tag_a != 0); - test_assert(tag_b != 0); - - test_assert(ecs_has_id(world, foo, tag_a)); - test_assert(ecs_has_id(world, foo, tag_b)); - - test_assert(ecs_has_id(world, bar, tag_a)); - test_assert(ecs_has_id(world, bar, tag_b)); - - ecs_fini(world); -} - -void Plecs_with_n_tags_2_levels(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with TagA, TagB {" - LINE " with TagC, TagD, TagE {" - LINE " Foo" - LINE " }" - LINE " HelloA" - LINE " with TagF, TagG, TagH {" - LINE " Bar" - LINE " }" - LINE " HelloB" - LINE "}" - LINE "HelloC"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tag_a = ecs_lookup_fullpath(world, "TagA"); - ecs_entity_t tag_b = ecs_lookup_fullpath(world, "TagB"); - ecs_entity_t tag_c = ecs_lookup_fullpath(world, "TagC"); - ecs_entity_t tag_d = ecs_lookup_fullpath(world, "TagD"); - ecs_entity_t tag_e = ecs_lookup_fullpath(world, "TagE"); - ecs_entity_t tag_f = ecs_lookup_fullpath(world, "TagF"); - ecs_entity_t tag_g = ecs_lookup_fullpath(world, "TagG"); - ecs_entity_t tag_h = ecs_lookup_fullpath(world, "TagH"); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - ecs_entity_t hello_a = ecs_lookup_fullpath(world, "HelloA"); - ecs_entity_t hello_b = ecs_lookup_fullpath(world, "HelloB"); - ecs_entity_t hello_c = ecs_lookup_fullpath(world, "HelloC"); - - test_assert(tag_a != 0); - test_assert(tag_b != 0); - test_assert(tag_a != 0); - test_assert(tag_b != 0); - test_assert(tag_a != 0); - test_assert(tag_b != 0); - test_assert(tag_b != 0); - test_assert(tag_b != 0); - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello_a != 0); - test_assert(hello_b != 0); - test_assert(hello_c != 0); - - test_assert(ecs_has_id(world, foo, tag_a)); - test_assert(ecs_has_id(world, foo, tag_b)); - test_assert(ecs_has_id(world, foo, tag_c)); - test_assert(ecs_has_id(world, foo, tag_d)); - test_assert(ecs_has_id(world, foo, tag_e)); - test_assert(!ecs_has_id(world, foo, tag_f)); - test_assert(!ecs_has_id(world, foo, tag_g)); - test_assert(!ecs_has_id(world, foo, tag_h)); - - test_assert(ecs_has_id(world, bar, tag_a)); - test_assert(ecs_has_id(world, bar, tag_b)); - test_assert(!ecs_has_id(world, bar, tag_c)); - test_assert(!ecs_has_id(world, bar, tag_d)); - test_assert(!ecs_has_id(world, bar, tag_e)); - test_assert(ecs_has_id(world, bar, tag_f)); - test_assert(ecs_has_id(world, bar, tag_g)); - test_assert(ecs_has_id(world, bar, tag_h)); - - test_assert(ecs_has_id(world, hello_a, tag_a)); - test_assert(ecs_has_id(world, hello_a, tag_b)); - test_assert(!ecs_has_id(world, hello_a, tag_c)); - test_assert(!ecs_has_id(world, hello_a, tag_d)); - test_assert(!ecs_has_id(world, hello_a, tag_e)); - test_assert(!ecs_has_id(world, hello_a, tag_f)); - test_assert(!ecs_has_id(world, hello_a, tag_g)); - test_assert(!ecs_has_id(world, hello_a, tag_h)); - - test_assert(ecs_has_id(world, hello_b, tag_a)); - test_assert(ecs_has_id(world, hello_b, tag_b)); - test_assert(!ecs_has_id(world, hello_b, tag_c)); - test_assert(!ecs_has_id(world, hello_b, tag_d)); - test_assert(!ecs_has_id(world, hello_b, tag_e)); - test_assert(!ecs_has_id(world, hello_b, tag_f)); - test_assert(!ecs_has_id(world, hello_b, tag_g)); - test_assert(!ecs_has_id(world, hello_b, tag_h)); - - test_assert(!ecs_has_id(world, hello_c, tag_a)); - test_assert(!ecs_has_id(world, hello_c, tag_b)); - test_assert(!ecs_has_id(world, hello_c, tag_c)); - test_assert(!ecs_has_id(world, hello_c, tag_d)); - test_assert(!ecs_has_id(world, hello_c, tag_e)); - test_assert(!ecs_has_id(world, hello_c, tag_f)); - test_assert(!ecs_has_id(world, hello_c, tag_g)); - test_assert(!ecs_has_id(world, hello_c, tag_h)); - - ecs_fini(world); -} - -void Plecs_with_after_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "E1 { }" - HEAD "with E2 {" - LINE " E3() { }" - LINE " E4 { }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e1 = ecs_lookup_fullpath(world, "E1"); - ecs_entity_t e2 = ecs_lookup_fullpath(world, "E2"); - ecs_entity_t e3 = ecs_lookup_fullpath(world, "E3"); - ecs_entity_t e4 = ecs_lookup_fullpath(world, "E4"); - - test_assert(e1 != 0); - test_assert(e2 != 0); - test_assert(e3 != 0); - test_assert(e4 != 0); - - test_assert(!ecs_has_pair(world, e2, EcsChildOf, e1)); - test_assert(!ecs_has_pair(world, e3, EcsChildOf, e1)); - test_assert(!ecs_has_pair(world, e4, EcsChildOf, e1)); - - test_assert(!ecs_has_id(world, e3, e2)); - test_assert(ecs_has_id(world, e4, e2)); - - ecs_fini(world); -} - -void Plecs_with_after_with(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "with E1 { }" - HEAD "with E2 {" - LINE " E3() { }" - LINE " E4 { }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e1 = ecs_lookup_fullpath(world, "E1"); - ecs_entity_t e2 = ecs_lookup_fullpath(world, "E2"); - ecs_entity_t e3 = ecs_lookup_fullpath(world, "E3"); - ecs_entity_t e4 = ecs_lookup_fullpath(world, "E4"); - - test_assert(e1 != 0); - test_assert(e2 != 0); - test_assert(e3 != 0); - test_assert(e4 != 0); - - test_assert(!ecs_has_id(world, e2, e1)); - test_assert(!ecs_has_id(world, e3, e1)); - test_assert(!ecs_has_id(world, e4, e1)); - - test_assert(!ecs_has_id(world, e3, e2)); - test_assert(ecs_has_id(world, e4, e2)); - - ecs_fini(world); -} - -void Plecs_scope_inside_with_inside_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Sun {" - LINE " with Planet {" - LINE " Earth {" - LINE " Moon" - LINE " }" - LINE " Mars" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t sun = ecs_lookup_fullpath(world, "Sun"); - ecs_entity_t planet = ecs_lookup_fullpath(world, "Planet"); - ecs_entity_t earth = ecs_lookup_fullpath(world, "Sun.Earth"); - ecs_entity_t moon = ecs_lookup_fullpath(world, "Sun.Earth.Moon"); - ecs_entity_t mars = ecs_lookup_fullpath(world, "Sun.Mars"); - - test_assert(sun != 0); - test_assert(planet != 0); - test_assert(earth != 0); - test_assert(moon != 0); - test_assert(mars != 0); - - test_assert(ecs_has_id(world, earth, planet)); - test_assert(ecs_has_id(world, moon, planet)); - test_assert(ecs_has_id(world, mars, planet)); - - test_assert(ecs_has_pair(world, earth, EcsChildOf, sun)); - test_assert(ecs_has_pair(world, moon, EcsChildOf, earth)); - test_assert(ecs_has_pair(world, mars, EcsChildOf, sun)); - - test_assert(!ecs_has_id(world, earth, sun)); - test_assert(!ecs_has_id(world, moon, earth)); - test_assert(!ecs_has_id(world, mars, sun)); - - test_assert(!ecs_has_pair(world, planet, EcsChildOf, sun)); - - ecs_fini(world); -} - -void Plecs_with_inside_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Earth {" - LINE " with Continent {" - LINE " Europe" - LINE " }" - LINE " Europe" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t earth = ecs_lookup_fullpath(world, "Earth"); - ecs_entity_t continent = ecs_lookup_fullpath(world, "Continent"); - ecs_entity_t europe = ecs_lookup_fullpath(world, "Earth.Europe"); - - test_assert(earth != 0); - test_assert(continent != 0); - test_assert(europe != 0); - - test_assert( ecs_has_pair(world, europe, EcsChildOf, earth)); - test_assert( !ecs_has_pair(world, continent, EcsChildOf, earth)); - test_assert( !ecs_has_id(world, europe, earth)); - test_assert( !ecs_has_id(world, continent, earth)); - - ecs_fini(world); -} - -void Plecs_assignment_w_1(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Earth :- Planet"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t earth = ecs_lookup_fullpath(world, "Earth"); - ecs_entity_t planet = ecs_lookup_fullpath(world, "Planet"); - - test_assert(earth != 0); - test_assert(planet != 0); - - test_assert( ecs_has_id(world, earth, planet)); - - ecs_fini(world); -} - -void Plecs_assignment_w_2(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Earth :- Planet, Habitable"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t earth = ecs_lookup_fullpath(world, "Earth"); - ecs_entity_t planet = ecs_lookup_fullpath(world, "Planet"); - ecs_entity_t habitable = ecs_lookup_fullpath(world, "Habitable"); - - test_assert(earth != 0); - test_assert(planet != 0); - test_assert(habitable != 0); - - test_assert( ecs_has_id(world, earth, planet)); - test_assert( ecs_has_id(world, earth, habitable)); - - ecs_fini(world); -} - -void Plecs_assignment_w_pair(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Earth :- Planet, (ChildOf, Sun)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t earth = ecs_lookup_fullpath(world, "Sun.Earth"); - ecs_entity_t planet = ecs_lookup_fullpath(world, "Planet"); - ecs_entity_t sun = ecs_lookup_fullpath(world, "Sun"); - - test_assert(earth != 0); - test_assert(planet != 0); - test_assert(sun != 0); - - test_assert( ecs_has_id(world, earth, planet)); - test_assert( ecs_has_pair(world, earth, EcsChildOf, sun)); - - ecs_fini(world); -} - -void Plecs_assignment_w_invalid_subject(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Earth :- Planet(Mars)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assignment_w_invalid_with(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Earth :- with Planet { Mars }"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_inherit_w_colon(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo : Bar"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(foo != 0); - test_assert(bar != 0); - - test_assert(ecs_has_pair(world, foo, EcsIsA, bar)); - - ecs_fini(world); -} - -void Plecs_inherit_w_colon_w_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo : Bar { Child }"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Foo.Child"); - - test_assert(foo != 0); - test_assert(bar != 0); - - test_assert(ecs_has_pair(world, foo, EcsIsA, bar)); - test_assert(ecs_has_pair(world, child, EcsChildOf, foo)); - - ecs_fini(world); -} - -void Plecs_inherit_w_colon_w_assign(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo : Bar :- Comp"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t comp = ecs_lookup_fullpath(world, "Comp"); - - test_assert(foo != 0); - test_assert(bar != 0); - - test_assert(ecs_has_pair(world, foo, EcsIsA, bar)); - test_assert(ecs_has_id(world, foo, comp)); - - ecs_fini(world); -} - -void Plecs_assign_component_value(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo :- Position{10, 20}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_2_component_values(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Velocity"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo :- Position{10, 20}" - LINE "Foo :- Velocity {1, 2}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - test_assert(ecs_has(world, foo, Velocity)); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - const Velocity *ptr = ecs_get(world, foo, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - - ecs_fini(world); -} - -void Plecs_assign_component_value_in_assign_scope(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo {" - LINE " - Position {10, 20}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_2_component_values_in_assign_scope(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Velocity"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo {" - LINE " - Position{10, 20}" - LINE " -Velocity {1, 2}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - test_assert(ecs_has(world, foo, Velocity)); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - const Velocity *ptr = ecs_get(world, foo, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - - ecs_fini(world); -} - -void Plecs_type_and_assign_in_plecs(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "flecs.meta.Struct Position {" - LINE " x :- flecs.meta.Member{flecs.meta.f32}" - LINE " y :- flecs.meta.Member{flecs.meta.f32}" - LINE "}" - LINE - LINE "Foo :- Position{10, 20}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(ecs_id(Position) != 0); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - -void Plecs_type_and_assign_in_plecs_w_2_members(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "flecs.meta.Struct Position {" - LINE " x :- flecs.meta.Member{flecs.meta.f32}" - LINE " y :- flecs.meta.Member{flecs.meta.f32}" - LINE "}" - LINE "" - LINE "Foo :- Position{x: 10, y: 20}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(ecs_id(Position) != 0); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - -void Plecs_type_and_assign_in_plecs_w_3_members(void) { - ecs_world_t *world = ecs_init(); - - typedef struct { - float x; - float y; - float z; - } Position3D; - - const char *expr = - HEAD "flecs.meta.Struct Position {" - LINE " x :- flecs.meta.Member{flecs.meta.f32}" - LINE " y :- flecs.meta.Member{flecs.meta.f32}" - LINE " z :- flecs.meta.Member{flecs.meta.f32}" - LINE "}" - LINE "" - LINE "Foo :- Position{x: 10, y: 20, z: 30}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Position3D) = ecs_lookup_fullpath(world, "Position"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(ecs_id(Position3D) != 0); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position3D)); - - { - const Position3D *ptr = ecs_get(world, foo, Position3D); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - test_int(ptr->z, 30); - } - - ecs_fini(world); -} - -void Plecs_type_and_assign_in_plecs_w_enum(void) { - ecs_world_t *world = ecs_init(); - - typedef enum { - Red, Green, Blue - } Color; - - typedef struct { - Color value; - } SomeType; - - const char *expr = - HEAD "flecs.meta.Enum Color {" - LINE " flecs.meta.Constant Red" - LINE " flecs.meta.Constant Green" - LINE " flecs.meta.Constant Blue" - LINE "}" - LINE "" - LINE "flecs.meta.Struct SomeType {" - LINE " value :- flecs.meta.Member{Color}" - LINE "}" - LINE "" - LINE "Foo :- SomeType{value: Blue}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(SomeType) = ecs_lookup_fullpath(world, "SomeType"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(ecs_id(SomeType) != 0); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, SomeType)); - - { - const SomeType *ptr = ecs_get(world, foo, SomeType); - test_assert(ptr != NULL); - test_int(ptr->value, Blue); - } - - ecs_fini(world); -} - -void Plecs_type_and_assign_in_plecs_w_enum_using_meta(void) { - ecs_world_t *world = ecs_init(); - - typedef enum { - Red, Green, Blue - } Color; - - typedef struct { - Color value; - } SomeType; - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Enum Color {" - LINE " Constant Red" - LINE " Constant Green" - LINE " Constant Blue" - LINE "}" - LINE - LINE "Struct SomeType {" - LINE " value :- Member{Color}" - LINE "}" - LINE - LINE "Foo :- SomeType{value: Blue}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(SomeType) = ecs_lookup_fullpath(world, "SomeType"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - /* Make sure no meta entities were created in the root */ - test_assert(ecs_lookup_fullpath(world, "Enum") == 0); - test_assert(ecs_lookup_fullpath(world, "Struct") == 0); - test_assert(ecs_lookup_fullpath(world, "Constant") == 0); - test_assert(ecs_lookup_fullpath(world, "Red") == 0); - test_assert(ecs_lookup_fullpath(world, "Green") == 0); - test_assert(ecs_lookup_fullpath(world, "Blue") == 0); - - test_assert(ecs_id(SomeType) != 0); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, SomeType)); - - { - const SomeType *ptr = ecs_get(world, foo, SomeType); - test_assert(ptr != NULL); - test_int(ptr->value, Blue); - } - - ecs_fini(world); -} - -void Plecs_type_and_assign_in_plecs_w_enum_primitive_using_meta(void) { - ecs_world_t *world = ecs_init(); - - typedef enum { - Red, Green, Blue - } Color; - - const char *expr = - HEAD "using flecs.meta" - LINE "Enum Color {" - LINE " Constant Red" - LINE " Constant Green" - LINE " Constant Blue" - LINE "}" - LINE "" - LINE "Foo :- Color{Blue}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Color) = ecs_lookup_fullpath(world, "Color"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - /* Make sure no meta entities were created in the root */ - test_assert(ecs_lookup_fullpath(world, "Enum") == 0); - test_assert(ecs_lookup_fullpath(world, "Struct") == 0); - test_assert(ecs_lookup_fullpath(world, "Constant") == 0); - test_assert(ecs_lookup_fullpath(world, "Red") == 0); - test_assert(ecs_lookup_fullpath(world, "Green") == 0); - test_assert(ecs_lookup_fullpath(world, "Blue") == 0); - - test_assert(ecs_id(Color) != 0); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Color)); - - { - const Color *ptr = ecs_get(world, foo, Color); - test_assert(ptr != NULL); - test_int(*ptr, Blue); - } - - ecs_fini(world); -} - - -void Plecs_type_and_assign_in_plecs_w_enum_primitive_and_struct(void) { - ecs_world_t *world = ecs_init(); - - typedef enum { - Red, Green, Blue - } Color; - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct Position {" - LINE " x :- Member{f32}" - LINE " y :- Member{f32}" - LINE "}" - LINE - LINE "Enum Color {" - LINE " Constant Red" - LINE " Constant Green" - LINE " Constant Blue" - LINE "}" - LINE - LINE "Foo {" - LINE " - Position {x: 10, y: 20}" - LINE " - Color {Green}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position"); - ecs_entity_t ecs_id(Color) = ecs_lookup_fullpath(world, "Color"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - /* Make sure no meta entities were created in the root */ - test_assert(ecs_lookup_fullpath(world, "Enum") == 0); - test_assert(ecs_lookup_fullpath(world, "Struct") == 0); - test_assert(ecs_lookup_fullpath(world, "Constant") == 0); - test_assert(ecs_lookup_fullpath(world, "Red") == 0); - test_assert(ecs_lookup_fullpath(world, "Green") == 0); - test_assert(ecs_lookup_fullpath(world, "Blue") == 0); - - test_assert(ecs_id(Position) != 0); - test_assert(ecs_id(Color) != 0); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - test_assert(ecs_has(world, foo, Color)); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - const Color *ptr = ecs_get(world, foo, Color); - test_assert(ptr != NULL); - test_int(*ptr, Green); - } - - ecs_fini(world); -} - -void Plecs_type_and_assign_in_plecs_nested_member(void) { - ecs_world_t *world = ecs_init(); - - typedef struct { - struct { - float x; - float y; - } start; - - struct { - float x; - float y; - } stop; - } Line; - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct Line {" - LINE " Member start {" - LINE " x :- Member{f32}" - LINE " y :- Member{f32}" - LINE " }" - LINE " Member stop {" - LINE " x :- Member{f32}" - LINE " y :- Member{f32}" - LINE " }" - LINE "}" - LINE - LINE "l :- Line{start: {x: 10, y: 20}, stop: {x: 30, y: 40}}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Line) = ecs_lookup_fullpath(world, "Line"); - ecs_entity_t l = ecs_lookup_fullpath(world, "l"); - - test_assert(ecs_id(Line) != 0); - test_assert(l != 0); - - test_assert( ecs_has(world, ecs_id(Line), EcsComponent)); - test_assert( ecs_has(world, ecs_id(Line), EcsStruct)); - test_assert( ecs_has(world, l, Line)); - - const Line *ptr = ecs_get(world, l, Line); - test_assert(ptr != NULL); - test_int(ptr->start.x, 10); - test_int(ptr->start.y, 20); - test_int(ptr->stop.x, 30); - test_int(ptr->stop.y, 40); - - - ecs_fini(world); -} - -void Plecs_dot_assign_nested_member(void) { - ecs_world_t *world = ecs_init(); - - typedef struct { - struct { - float x; - float y; - } start; - - struct { - float x; - float y; - } stop; - } Line; - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct Line {" - LINE " Member start {" - LINE " x :- Member{f32}" - LINE " y :- Member{f32}" - LINE " }" - LINE " Member stop {" - LINE " x :- Member{f32}" - LINE " y :- Member{f32}" - LINE " }" - LINE "}" - LINE - LINE "l :- Line{start.x: 10, start.y: 20, stop.x: 30, stop.y: 40}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Line) = ecs_lookup_fullpath(world, "Line"); - ecs_entity_t l = ecs_lookup_fullpath(world, "l"); - - test_assert(ecs_id(Line) != 0); - test_assert(l != 0); - - test_assert( ecs_has(world, ecs_id(Line), EcsComponent)); - test_assert( ecs_has(world, ecs_id(Line), EcsStruct)); - test_assert( ecs_has(world, l, Line)); - - const Line *ptr = ecs_get(world, l, Line); - test_assert(ptr != NULL); - test_int(ptr->start.x, 10); - test_int(ptr->start.y, 20); - test_int(ptr->stop.x, 30); - test_int(ptr->stop.y, 40); - - - ecs_fini(world); -} - -void Plecs_dot_assign_binary_expr(void) { - ecs_world_t *world = ecs_init(); - - typedef struct { - struct { - float x; - float y; - } start; - - struct { - float x; - float y; - } stop; - } Line; - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct Line {" - LINE " Member start {" - LINE " x :- Member{f32}" - LINE " y :- Member{f32}" - LINE " }" - LINE " Member stop {" - LINE " x :- Member{f32}" - LINE " y :- Member{f32}" - LINE " }" - LINE "}" - LINE - LINE "l :- Line{start.x: 2 + 8, start.y: 5 + 15, stop.x: 10 + 20, stop.y: 15 + 25}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Line) = ecs_lookup_fullpath(world, "Line"); - ecs_entity_t l = ecs_lookup_fullpath(world, "l"); - - test_assert(ecs_id(Line) != 0); - test_assert(l != 0); - - test_assert( ecs_has(world, ecs_id(Line), EcsComponent)); - test_assert( ecs_has(world, ecs_id(Line), EcsStruct)); - test_assert( ecs_has(world, l, Line)); - - const Line *ptr = ecs_get(world, l, Line); - test_assert(ptr != NULL); - test_int(ptr->start.x, 10); - test_int(ptr->start.y, 20); - test_int(ptr->stop.x, 30); - test_int(ptr->stop.y, 40); - - - ecs_fini(world); -} - -void Plecs_open_scope_no_parent(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo {" - LINE " Bar" - LINE "}" - LINE "{" - LINE " Zoo" - LINE "}" - LINE "Hello"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar"); - ecs_entity_t zoo = ecs_lookup_fullpath(world, "Zoo"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(zoo != 0); - test_assert(hello != 0); - - test_assert(ecs_has_pair(world, bar, EcsChildOf, foo)); - test_assert(!ecs_has_pair(world, zoo, EcsChildOf, EcsWildcard)); - test_assert(!ecs_has_pair(world, hello, EcsChildOf, EcsWildcard)); - - ecs_fini(world); -} - -void Plecs_create_subject_in_root_scope_w_resolvable_id(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Tag"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t tag = ecs_lookup_child(world, 0, "Tag"); - test_assert(tag != 0); - test_assert(tag != EcsTag); - - ecs_fini(world); -} - -void Plecs_create_subject_in_scope_w_resolvable_id(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo {" - LINE " Tag" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - ecs_entity_t tag = ecs_lookup_fullpath(world, "Foo.Tag"); - test_assert(tag != 0); - test_assert(tag != EcsTag); - - test_assert(ecs_has_pair(world, tag, EcsChildOf, foo)); - - ecs_fini(world); -} - -void Plecs_create_subject_in_scope_w_resolvable_id_using(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo {" - LINE " Bar" - LINE "}" - LINE - LINE "using Foo" - LINE - LINE "Hello(Bar)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar"); - ecs_entity_t root_bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(root_bar != 0); - test_assert(hello != 0); - - test_assert(ecs_has_pair(world, bar, EcsChildOf, foo)); - - test_assert(ecs_has_id(world, root_bar, hello)); - test_assert(!ecs_has_id(world, bar, hello)); - - ecs_fini(world); -} - -void Plecs_using_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo {" - LINE " Bar" - LINE "}" - LINE "" - LINE "using Foo" - LINE "Bar(Hello)" - LINE "Foo.Bar(World)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar"); - ecs_entity_t not_bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello"); - ecs_entity_t _world = ecs_lookup_fullpath(world, "World"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello != 0); - test_assert(_world != 0); - test_assert(not_bar == 0); - - test_assert(_world != EcsWorld); /* sanity check, verified by other tests */ - - test_assert(ecs_has_pair(world, bar, EcsChildOf, foo)); - test_assert(ecs_has_id(world, hello, bar)); - test_assert(ecs_has_id(world, _world, bar)); - - ecs_fini(world); -} - -void Plecs_using_nested_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo {" - LINE " Bar {" - LINE " Zoo" - LINE " }" - LINE "}" - LINE "" - LINE "using Foo.Bar" - LINE "Zoo(Hello)" - LINE "Foo.Bar.Zoo(World)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar"); - ecs_entity_t zoo = ecs_lookup_fullpath(world, "Foo.Bar.Zoo"); - ecs_entity_t not_bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t not_zoo = ecs_lookup_fullpath(world, "Zoo"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello"); - ecs_entity_t _world = ecs_lookup_fullpath(world, "World"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(zoo != 0); - test_assert(hello != 0); - test_assert(_world != 0); - test_assert(not_bar == 0); - test_assert(not_zoo == 0); - - test_assert(_world != EcsWorld); /* sanity check, verified by other tests */ - - test_assert(ecs_has_id(world, hello, zoo)); - test_assert(ecs_has_id(world, _world, zoo)); - - ecs_fini(world); -} - -void Plecs_using_nested_in_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo {" - LINE " Bar {" - LINE " Zoo" - LINE " }" - LINE "}" - LINE "{" - LINE " using Foo.Bar" - LINE " Zoo(Hello)" - LINE "}" - LINE "Zoo(World)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar"); - ecs_entity_t zoo = ecs_lookup_fullpath(world, "Foo.Bar.Zoo"); - ecs_entity_t not_bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t zoo_root = ecs_lookup_fullpath(world, "Zoo"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello"); - ecs_entity_t _world = ecs_lookup_fullpath(world, "World"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(zoo != 0); - test_assert(hello != 0); - test_assert(_world != 0); - test_assert(not_bar == 0); - test_assert(zoo_root != 0); - - test_assert(_world != EcsWorld); /* sanity check, verified by other tests */ - - test_assert(ecs_has_id(world, hello, zoo)); - test_assert(ecs_has_id(world, _world, zoo_root)); - - ecs_fini(world); -} - -void Plecs_using_with_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "using flecs.meta" - LINE "" - LINE "Scope {" - LINE "" - LINE "Foo {}" - LINE "" - LINE "Struct Bar {" - LINE " x :- Member{f32}" - LINE "}" - LINE "" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t scope = ecs_lookup_fullpath(world, "Scope"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Scope.Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Scope.Bar"); - ecs_entity_t x = ecs_lookup_fullpath(world, "Scope.Bar.x"); - - test_assert(ecs_lookup_fullpath(world, "Foo") == 0); - test_assert(ecs_lookup_fullpath(world, "Bar") == 0); - test_assert(ecs_lookup_fullpath(world, "x") == 0); - test_assert(ecs_lookup_fullpath(world, "Struct") == 0); - test_assert(ecs_lookup_fullpath(world, "Member") == 0); - - test_assert(scope != 0); - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(x != 0); - - test_assert(ecs_has_pair(world, foo, EcsChildOf, scope)); - test_assert(ecs_has_pair(world, bar, EcsChildOf, scope)); - test_assert(ecs_has_pair(world, x, EcsChildOf, bar)); - - ecs_fini(world); -} - -void Plecs_using_w_entity_ref_in_value_2_members(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct Position {" - LINE " x :- Member{f32}" // member type is looked up in flecs.meta - LINE " y :- Member{f32}" - LINE "}" - LINE "" - LINE "Foo :- Position{x: 10, y: 20}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(ecs_id(Position) != 0); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - -void Plecs_using_w_entity_ref_in_value_3_members(void) { - ecs_world_t *world = ecs_init(); - - typedef struct { - float x; - float y; - float z; - } Position3D; - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct Position {" - LINE " x :- Member{f32}" // member type is looked up in flecs.meta - LINE " y :- Member{f32}" - LINE " z :- Member{f32}" - LINE "}" - LINE - LINE "Foo :- Position{x: 10, y: 20, z: 30}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t ecs_id(Position3D) = ecs_lookup_fullpath(world, "Position"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - test_assert(ecs_id(Position3D) != 0); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position3D)); - - { - const Position3D *ptr = ecs_get(world, foo, Position3D); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - test_int(ptr->z, 30); - } - - ecs_fini(world); -} - -void Plecs_2_using_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "using Foo" - LINE "using Bar" - LINE - LINE "Foo {" - LINE " Hello" - LINE "}" - LINE - LINE "Bar {" - LINE " TheWorld" - LINE "}" - LINE - LINE "Hello(E1)" - LINE "TheWorld(E2)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t _world = ecs_lookup_fullpath(world, "Bar.TheWorld"); - - ecs_entity_t e1 = ecs_lookup_fullpath(world, "E1"); - ecs_entity_t e2 = ecs_lookup_fullpath(world, "E2"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello != 0); - test_assert(_world != 0); - test_assert(e1 != 0); - test_assert(e2 != 0); - - test_assert(ecs_lookup_fullpath(world, "Hello") == 0); - test_assert(ecs_lookup_fullpath(world, "TheWorld") == 0); - - test_assert(ecs_has_pair(world, hello, EcsChildOf, foo)); - test_assert(ecs_has_pair(world, _world, EcsChildOf, bar)); - test_assert(ecs_has_id(world, e1, hello)); - test_assert(ecs_has_id(world, e2, _world)); - - ecs_fini(world); -} - -void Plecs_2_using_in_different_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "Foo {" - LINE " Hello" - LINE "}" - LINE - LINE "Bar {" - LINE " TheWorld" - LINE "}" - LINE - LINE "E1 {" - LINE " using Foo" - LINE - LINE " Hello(Child)" - LINE " TheWorld(Child)" - LINE "}" - LINE - LINE "E2 {" - LINE " using Bar" - LINE - LINE " Hello(Child)" - LINE " TheWorld(Child)" - LINE "}" - LINE - LINE "Hello(RootChild)" - LINE "TheWorld(RootChild)" - LINE; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t _world = ecs_lookup_fullpath(world, "Bar.TheWorld"); - - ecs_entity_t e1 = ecs_lookup_fullpath(world, "E1"); - ecs_entity_t e1_child = ecs_lookup_fullpath(world, "E1.Child"); - ecs_entity_t e2 = ecs_lookup_fullpath(world, "E2"); - ecs_entity_t e2_child = ecs_lookup_fullpath(world, "E2.Child"); - - ecs_entity_t root_hello = ecs_lookup_fullpath(world, "Hello"); - ecs_entity_t root_world = ecs_lookup_fullpath(world, "TheWorld"); - - ecs_entity_t root_child = ecs_lookup_fullpath(world, "RootChild"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello != 0); - test_assert(_world != 0); - test_assert(e1 != 0); - test_assert(e2 != 0); - test_assert(e1_child != 0); - test_assert(e2_child != 0); - test_assert(root_hello != 0); - test_assert(root_world != 0); - test_assert(root_world != 0); - - test_assert(ecs_has_pair(world, hello, EcsChildOf, foo)); - test_assert(ecs_has_pair(world, _world, EcsChildOf, bar)); - - test_assert(ecs_has_pair(world, e1_child, EcsChildOf, e1)); - test_assert(ecs_has_pair(world, e2_child, EcsChildOf, e2)); - - test_assert(ecs_has_id(world, e1_child, hello)); - test_assert(ecs_has_id(world, e1_child, root_world)); - - test_assert(ecs_has_id(world, e2_child, root_hello)); - test_assert(ecs_has_id(world, e2_child, _world)); - - test_assert(ecs_has_id(world, root_child, root_hello)); - test_assert(ecs_has_id(world, root_child, root_world)); - - ecs_fini(world); -} - -void Plecs_assignment_to_non_component(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo :- Position{x: 10, y: 20}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_struct_w_member_w_assignment_to_nothing(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "flecs.meta.Struct Position {" - LINE " flecs.meta.Member(x) = " - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_struct_w_member_w_assignment_to_empty_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "flecs.meta.Struct Position {" - LINE " x :- flecs.meta.Member{" - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_scope_after_assign(void) { - ecs_world_t *world = ecs_init(); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo :- Position{10, 20} {" - LINE " Bar" - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assign_after_inherit(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo : Position" - LINE "Bar :- Position{10, 20}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(foo != 0); - test_assert(bar != 0); - - test_assert( ecs_has_pair(world, foo, EcsIsA, ecs_id(Position))); - test_assert( !ecs_has(world, foo, Position)); - - test_assert( !ecs_has_pair(world, bar, EcsIsA, ecs_id(Position))); - test_assert( ecs_has(world, bar, Position)); - - const Position *ptr = ecs_get(world, bar, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_multiple_assignments_single_line(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Velocity"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo :- Position{10, 20}, Velocity{1, 2}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - const Velocity *ptr = ecs_get(world, foo, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - - ecs_fini(world); -} - -void Plecs_2_stmts_in_scope_w_no_parent(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "{" - LINE "Bar { }" - LINE "Foo" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(foo != 0); - test_assert(bar != 0); - - test_assert( !ecs_has_id(world, foo, bar)); - test_assert( !ecs_has_pair(world, foo, EcsChildOf, bar)); - - test_assert( !ecs_has_id(world, bar, foo)); - test_assert( !ecs_has_pair(world, bar, EcsChildOf, foo)); - - ecs_fini(world); -} - -void Plecs_scope_after_assign_1_tag(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo :- TagA {" - LINE " Bar" - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_scope_after_assign_2_tags(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo :- TagA, TagB {" - LINE " Bar" - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_empty_scope_after_using(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "using flecs.meta" - LINE "{" - LINE " Foo" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert( foo != 0); - test_assert( !ecs_has_pair(world, foo, EcsChildOf, EcsWildcard)); - - ecs_fini(world); -} - -void Plecs_invalid_nested_assignment(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo :-" - LINE "Bar :- Hello"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_invalid_partial_pair_assignment(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo :- (Hello, "; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_empty_assignment(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo :-"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_empty_assignment_before_end_of_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "{Foo :-}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assign_tag_to_parent(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo {" - LINE " - Bar" - LINE " Child" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Foo.Child"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(child != 0); - - test_assert( ecs_has_id(world, foo, bar)); - test_assert( !ecs_has_id(world, child, bar)); - test_assert( !ecs_has_pair(world, bar, EcsChildOf, foo)); - test_assert( ecs_has_pair(world, child, EcsChildOf, foo)); - - ecs_fini(world); -} - -void Plecs_assign_component_to_parent(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo {" - LINE " - Position {10, 20}" - LINE " Child" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Foo.Child"); - - test_assert(foo != 0); - test_assert(child != 0); - - test_assert( ecs_has(world, foo, Position)); - test_assert( !ecs_has(world, child, Position)); - test_assert( !ecs_has_pair(world, ecs_id(Position), EcsChildOf, foo)); - test_assert( ecs_has_pair(world, child, EcsChildOf, foo)); - - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_to_parent_pair_w_new_entities_in_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo {" - LINE " - (Rel, Obj)" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel"); - ecs_entity_t obj = ecs_lookup_fullpath(world, "Obj"); - - test_assert(foo != 0); - test_assert(rel != 0); - test_assert(obj != 0); - - test_assert( ecs_has_pair(world, foo, rel, obj)); - test_assert( !ecs_has_pair(world, rel, EcsChildOf, EcsWildcard)); - test_assert( !ecs_has_pair(world, obj, EcsChildOf, EcsWildcard)); - - ecs_fini(world); -} - -void Plecs_assign_to_parent_pair_w_existing_entities_in_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Rel, Obj" - LINE "Foo {" - LINE " - (Rel, Obj)" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel"); - ecs_entity_t obj = ecs_lookup_fullpath(world, "Obj"); - - test_assert(foo != 0); - test_assert(rel != 0); - test_assert(obj != 0); - - test_assert( ecs_has_pair(world, foo, rel, obj)); - test_assert( !ecs_has_pair(world, rel, EcsChildOf, EcsWildcard)); - test_assert( !ecs_has_pair(world, obj, EcsChildOf, EcsWildcard)); - - ecs_fini(world); -} - -void Plecs_default_child_component(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "DefaultChildComponent(Foo, Bar)" - LINE "Foo(Parent) {" - LINE " ChildA" - LINE " ChildB" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA"); - ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(parent != 0); - test_assert(child_a != 0); - test_assert(child_b != 0); - - test_assert( ecs_has_pair(world, foo, EcsDefaultChildComponent, bar)); - test_assert( ecs_has_id(world, parent, foo)); - test_assert( ecs_has_pair(world, child_a, EcsChildOf, parent)); - test_assert( ecs_has_pair(world, child_b, EcsChildOf, parent)); - - test_assert(ecs_has_id(world, child_a, bar)); - test_assert(ecs_has_id(world, child_b, bar)); - - ecs_fini(world); -} - -void Plecs_default_child_component_w_assign(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "DefaultChildComponent(Foo, Position)" - LINE "Foo(Parent) {" - LINE " ChildA :- {10, 20}" - LINE " ChildB :- {10, 20}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA"); - ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB"); - - test_assert(foo != 0); - test_assert(parent != 0); - test_assert(child_a != 0); - test_assert(child_b != 0); - - test_assert( ecs_has_pair(world, foo, EcsDefaultChildComponent, ecs_id(Position))); - test_assert( ecs_has_id(world, parent, foo)); - test_assert( ecs_has_pair(world, child_a, EcsChildOf, parent)); - test_assert( ecs_has_pair(world, child_b, EcsChildOf, parent)); - - test_assert(ecs_has(world, child_a, Position)); - test_assert(ecs_has(world, child_b, Position)); - - ecs_fini(world); -} - -void Plecs_struct_type_w_default_child_component(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct(Position) {" - LINE " x :- {f32}" - LINE " y :- {f32}" - LINE "}" - LINE - LINE "Foo :- Position{x: 10, y: 20}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position"); - - test_assert(foo != 0); - test_assert(ecs_id(Position) != 0); - - test_assert( ecs_has(world, foo, Position)); - - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_struct_type_w_default_child_component_nested_member(void) { - ecs_world_t *world = ecs_init(); - - typedef struct { - struct { - float x; - float y; - } start; - - struct { - float x; - float y; - } stop; - } Line; - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct(Line) {" - LINE " start {" - LINE " x :- {f32}" - LINE " y :- {f32}" - LINE " }" - LINE " stop {" - LINE " x :- {f32}" - LINE " y :- {f32}" - LINE " }" - LINE "}" - LINE - LINE "Foo :- Line{start: {10, 20}, stop: {30, 40}}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t ecs_id(Line) = ecs_lookup_fullpath(world, "Line"); - - test_assert(foo != 0); - test_assert(ecs_id(Line) != 0); - - test_assert( ecs_has(world, foo, Line)); - - const Line *ptr = ecs_get(world, foo, Line); - test_assert(ptr != NULL); - test_int(ptr->start.x, 10); - test_int(ptr->start.y, 20); - test_int(ptr->stop.x, 30); - test_int(ptr->stop.y, 40); - - ecs_fini(world); -} - -void Plecs_enum_type_w_default_child_component(void) { - ecs_world_t *world = ecs_init(); - - typedef enum { - Red, Green, Blue - } Color; - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Enum Color {" - LINE " Red, Green, Blue" - LINE "}" - LINE - LINE "Foo :- Color{Green}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t ecs_id(Color) = ecs_lookup_fullpath(world, "Color"); - - test_assert(foo != 0); - test_assert(ecs_id(Color) != 0); - - test_assert( ecs_has(world, foo, Color)); - - const Color *ptr = ecs_get(world, foo, Color); - test_assert(ptr != NULL); - test_int(*ptr, Green); - - ecs_fini(world); -} - -void Plecs_default_type_from_with(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "with Position {" - LINE " e1 :- {10, 20}" - LINE " e2 :- {30, 40}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e1 = ecs_lookup_fullpath(world, "e1"); - ecs_entity_t e2 = ecs_lookup_fullpath(world, "e2"); - - test_assert(e1 != 0); - test_assert(e2 != 0); - - const Position *p = ecs_get(world, e1, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - p = ecs_get(world, e2, Position); - test_assert(p != NULL); - test_int(p->x, 30); - test_int(p->y, 40); - - ecs_fini(world); -} - -void Plecs_scope_w_1_subj_and_2_pairs(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent {" - LINE " RelA(Foo, Bar)" - LINE " RelB(Foo, Bar)" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t rel_a = ecs_lookup_fullpath(world, "RelA"); - ecs_entity_t rel_b = ecs_lookup_fullpath(world, "RelB"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Parent.Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Parent.Bar"); - - test_assert(parent != 0); - test_assert(rel_a != 0); - test_assert(rel_b != 0); - test_assert(foo != 0); - test_assert(bar != 0); - - test_assert( ecs_has_pair(world, foo, rel_a, bar)); - test_assert( ecs_has_pair(world, foo, rel_b, bar)); - - ecs_fini(world); -} - -void Plecs_inherit_from_multiple(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Inst : Foo, Bar"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t inst = ecs_lookup_fullpath(world, "Inst"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(inst != 0); - test_assert(foo != 0); - test_assert(bar != 0); - - test_assert(ecs_has_pair(world, inst, EcsIsA, foo)); - test_assert(ecs_has_pair(world, inst, EcsIsA, bar)); - - ecs_fini(world); -} - -// using flecs.meta -// Struct(Position) { -// x :- {f32} -// y :- {f32} -// } -// Foo :- (Position, Bar){x: 10, y: 20} - -void Plecs_assign_pair_component(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct(Position) {" - LINE " x :- {f32}" - LINE " y :- {f32}" - LINE "}" - LINE - LINE "Foo :- (Position, Bar){x: 10, y: 20}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(ecs_id(Position) != 0); - - test_assert( ecs_has_pair(world, foo, ecs_id(Position), bar)); - - const Position *ptr = ecs_get_pair(world, foo, Position, bar); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_pair_component_in_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct(Position) {" - LINE " x :- {f32}" - LINE " y :- {f32}" - LINE "}" - LINE - LINE "Parent {" - LINE " - (Position, Foo) {x: 10, y: 20}" - LINE " - (Position, Bar) {x: 20, y: 30}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position"); - - test_assert(parent != 0); - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(ecs_id(Position) != 0); - - test_assert( ecs_has_pair(world, parent, ecs_id(Position), foo)); - test_assert( ecs_has_pair(world, parent, ecs_id(Position), bar)); - - const Position * - ptr = ecs_get_pair(world, parent, Position, foo); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ptr = ecs_get_pair(world, parent, Position, bar); - test_assert(ptr != NULL); - test_int(ptr->x, 20); - test_int(ptr->y, 30); - - ecs_fini(world); -} - -void Plecs_assign_pair_component_in_script(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct(Position) {" - LINE " x :- {f32}" - LINE " y :- {f32}" - LINE "}" - LINE - LINE "Foo :- (Position, Bar){x: 10, y: 20}"; - - ecs_entity_t s = ecs_script(world, { - .entity = ecs_entity(world, { .name = "main" }), - .str = expr - }); - test_assert(s != 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(ecs_id(Position) != 0); - - test_assert( ecs_has_pair(world, foo, ecs_id(Position), bar)); - - const Position *ptr = ecs_get_pair(world, foo, Position, bar); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_pair_component_in_script_update(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "using flecs.meta" - LINE - LINE "Struct(Position) {" - LINE " x :- {f32}" - LINE " y :- {f32}" - LINE "}" - LINE - LINE "Foo :- (Position, Bar){x: 10, y: 20}"; - - ecs_entity_t s = ecs_script(world, { - .entity = ecs_entity(world, { .name = "main" }), - .str = expr - }); - test_assert(s != 0); - - test_assert(ecs_script_update(world, s, 0, expr, NULL) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(ecs_id(Position) != 0); - - test_assert( ecs_has_pair(world, foo, ecs_id(Position), bar)); - - const Position *ptr = ecs_get_pair(world, foo, Position, bar); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_set_entity_names(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "foo(self)" - LINE "foo(parent)" - LINE "foo(cascade)" - LINE "foo(down)" - LINE "foo(up)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "foo"); - ecs_entity_t self = ecs_lookup_fullpath(world, "self"); - ecs_entity_t down = ecs_lookup_fullpath(world, "down"); - ecs_entity_t up = ecs_lookup_fullpath(world, "up"); - ecs_entity_t parent = ecs_lookup_fullpath(world, "parent"); - ecs_entity_t cascade = ecs_lookup_fullpath(world, "cascade"); - - test_assert(foo != 0); - test_assert(self != 0); - test_assert(down != 0); - test_assert(up != 0); - test_assert(parent != 0); - test_assert(cascade != 0); - - test_assert( ecs_has_id(world, self, foo)); - test_assert( ecs_has_id(world, down, foo)); - test_assert( ecs_has_id(world, up, foo)); - test_assert( ecs_has_id(world, parent, foo)); - test_assert( ecs_has_id(world, cascade, foo)); - - ecs_fini(world); -} - -void Plecs_oneof(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "flecs.meta.Enum(Color) {" - LINE " flecs.meta.Constant(Red)" - LINE " flecs.meta.Constant(Green)" - LINE " flecs.meta.Constant(Blue)" - LINE "}" - LINE "e :- (Color, Green)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t color = ecs_lookup_fullpath(world, "Color"); - ecs_entity_t green = ecs_lookup_fullpath(world, "Color.Green"); - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - - test_assert(color != 0); - test_assert(green != 0); - test_assert(e != 0); - test_assert(ecs_lookup_fullpath(world, "Green") == 0); - - test_assert( ecs_has_id(world, color, EcsOneOf)); - test_assert( ecs_has_id(world, green, EcsConstant)); - test_assert( ecs_has_id(world, e, ecs_pair(color, green))); - - ecs_fini(world); -} - -void Plecs_invalid_oneof(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "flecs.meta.Enum(Color) {" - LINE " flecs.meta.Constant(Red)" - LINE " flecs.meta.Constant(Green)" - LINE " flecs.meta.Constant(Blue)" - LINE "}" - LINE "e :- (Color, Foo)"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_entity_t color = ecs_lookup_fullpath(world, "Color"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - - test_assert(color != 0); - test_assert(foo == 0); - test_assert(e != 0); - - test_assert( !ecs_has_pair(world, e, color, EcsWildcard)); - - ecs_fini(world); -} - -void Plecs_brief_annotation(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "@brief A description" - LINE "Foo" - LINE "Bar" - LINE "" - LINE "@brief Another description" - LINE "Baz"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(baz != 0); - - test_str(ecs_doc_get_brief(world, foo), "A description"); - test_str(ecs_doc_get_brief(world, bar), NULL); - test_str(ecs_doc_get_brief(world, baz), "Another description"); - - ecs_fini(world); -} - -void Plecs_name_annotation(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "@name A name" - LINE "Foo" - LINE "Bar" - LINE "" - LINE "@name Another name" - LINE "Baz"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(baz != 0); - - test_str(ecs_doc_get_name(world, foo), "A name"); - test_str(ecs_doc_get_name(world, bar), "Bar"); - test_str(ecs_doc_get_name(world, baz), "Another name"); - - ecs_fini(world); -} - -void Plecs_link_annotation(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "@link A link" - LINE "Foo" - LINE "Bar" - LINE "" - LINE "@link Another link" - LINE "Baz"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(baz != 0); - - test_str(ecs_doc_get_link(world, foo), "A link"); - test_str(ecs_doc_get_link(world, bar), NULL); - test_str(ecs_doc_get_link(world, baz), "Another link"); - - ecs_fini(world); -} - -void Plecs_color_annotation(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "@color #554433" - LINE "Foo" - LINE "Bar" - LINE "" - LINE "@color rgb(10, 20, 30, 1.0)" - LINE "Baz"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(baz != 0); - - test_str(ecs_doc_get_color(world, foo), "#554433"); - test_str(ecs_doc_get_color(world, bar), NULL); - test_str(ecs_doc_get_color(world, baz), "rgb(10, 20, 30, 1.0)"); - - ecs_fini(world); -} - -void Plecs_multiple_annotations(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "@brief A description" - LINE "@name A name" - LINE "@link A link" - LINE "@color #554433" - LINE "Foo" - LINE "Bar" - LINE "" - LINE "@brief Another description" - LINE "@name Another name" - LINE "Baz"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(baz != 0); - - test_str(ecs_doc_get_brief(world, foo), "A description"); - test_str(ecs_doc_get_name(world, foo), "A name"); - test_str(ecs_doc_get_link(world, foo), "A link"); - test_str(ecs_doc_get_color(world, foo), "#554433"); - test_str(ecs_doc_get_brief(world, bar), NULL); - test_str(ecs_doc_get_brief(world, baz), "Another description"); - test_str(ecs_doc_get_name(world, baz), "Another name"); - - ecs_fini(world); -} - -void Plecs_annotation_w_trailing_space(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "@brief A description " - LINE "Foo"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - test_str(ecs_doc_get_brief(world, foo), "A description"); - - ecs_fini(world); -} - -typedef struct String { - char *value; -} String; - -void Plecs_multiline_string(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(String) = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "String"}), - .members = { - {"value", ecs_id(ecs_string_t)} - } - }); - - const char *expr = - HEAD "Foo :- String{value: `start" - LINE "Hello World" - LINE "Foo Bar" - LINE "Special characters }{\"\"''," - LINE "`}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - test_assert(ecs_has(world, foo, String)); - - const String *ptr = ecs_get(world, foo, String); - test_assert(ptr != NULL); - test_assert(ptr->value != NULL); - test_str(ptr->value, - "start\n" - "Hello World\n" - "Foo Bar\n" - "Special characters }{\"\"'',\n" - ); - - ecs_os_free(ptr->value); - - ecs_fini(world); -} - -void Plecs_unterminated_multiline_string(void) { - ecs_world_t *world = ecs_init(); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_entity(world, {.name = "String"}), - .members = { - {"value", ecs_id(ecs_string_t)} - } - }); - - const char *expr = - HEAD "Foo :- String{value: `start" - LINE "Hello World" - LINE "Foo Bar" - LINE "Special characters }{\"\"''," - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_annotate_declaration(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "@brief A brief description" - LINE "Foo Bar"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - test_assert(bar != 0); - - test_assert(ecs_has_id(world, bar, foo)); - - test_assert(ecs_doc_get_brief(world, foo) == NULL); - test_assert(ecs_doc_get_brief(world, bar) != NULL); - test_str(ecs_doc_get_brief(world, bar), "A brief description"); - - ecs_fini(world); -} - -void Plecs_declaration_w_underscore_name(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo _Bar"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - ecs_entity_t bar = ecs_lookup_fullpath(world, "_Bar"); - test_assert(bar != 0); - - test_assert(!ecs_has_id(world, foo, bar)); - test_assert(ecs_has_id(world, bar, foo)); - - ecs_fini(world); -} - -void Plecs_anonymous_entity(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "_ :- Foo"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ foo }); - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - ecs_entity_t e = it.entities[0]; - test_assert(e != 0); - test_str(ecs_get_name(world, e), NULL); - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Plecs_anonymous_entity_in_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Parent {" - LINE " _ :- Foo" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - test_assert(parent != 0); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ foo }); - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - ecs_entity_t e = it.entities[0]; - test_assert(e != 0); - test_str(ecs_get_name(world, e), NULL); - test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Plecs_anonymous_declaration(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo _"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ foo }); - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - ecs_entity_t e = it.entities[0]; - test_assert(e != 0); - test_str(ecs_get_name(world, e), NULL); - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Plecs_const_var_int(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "const var_x = 10" - LINE "const var_y = 20" - LINE "" - LINE "e :- Position{$var_x, $var_y}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - const Position *p = ecs_get(world, e, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_const_var_float(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "const var_x = 10.5" - LINE "const var_y = 20.5" - LINE "" - LINE "e :- Position{$var_x, $var_y}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - const Position *p = ecs_get(world, e, Position); - test_assert(p != NULL); - test_flt(p->x, 10.5); - test_flt(p->y, 20.5); - - ecs_fini(world); -} - -void Plecs_const_var_bool(void) { - ecs_world_t *world = ecs_init(); - - typedef struct Bools { - bool x; - bool y; - } Bools; - - ecs_entity_t ecs_id(Bools) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Bools"}), - .members = { - {"x", ecs_id(ecs_bool_t)}, - {"y", ecs_id(ecs_bool_t)} - } - }); - - const char *expr = - HEAD "const var_x = true" - LINE "const var_y = false" - LINE "" - LINE "e :- Bools{$var_x, $var_y}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - test_assert(ecs_has(world, e, Bools)); - - const Bools *p = ecs_get(world, e, Bools); - test_assert(p != NULL); - test_bool(p->x, true); - test_bool(p->y, false); - - ecs_fini(world); -} - -void Plecs_const_var_string(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "const var_x = \"10.5\"" - LINE "const var_y = \"20.5\"" - LINE "" - LINE "e :- Position{$var_x, $var_y}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - const Position *p = ecs_get(world, e, Position); - test_assert(p != NULL); - test_flt(p->x, 10.5); - test_flt(p->y, 20.5); - - ecs_fini(world); -} - -typedef struct Line { - Position start; - Position stop; -} Line; - -void Plecs_const_var_struct(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_entity_t ecs_id(Line) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Line"}), - .members = { - {"start", ecs_id(Position)}, - {"stop", ecs_id(Position)} - } - }); - - const char *expr = - HEAD "const var_start = Position{10.5, 20.5}" - LINE "const var_stop = Position{30.5, 40.5}" - LINE "" - LINE "e :- Line{start: $var_start, stop: $var_stop}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - test_assert(ecs_has(world, e, Line)); - - const Line *l = ecs_get(world, e, Line); - test_assert(l != NULL); - test_flt(l->start.x, 10.5); - test_flt(l->start.y, 20.5); - test_flt(l->stop.x, 30.5); - test_flt(l->stop.y, 40.5); - - ecs_fini(world); -} - -void Plecs_const_var_redeclare(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "const var_x = 10" - LINE "const var_x = 20"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_const_var_scoped(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "const var_x = 10" - LINE "a :- Position{$var_x, $var_x}" - LINE "a {" - LINE " const var_x = 20" - LINE " const var_y = 30" - LINE " b :- Position{$var_x, $var_y}" - LINE "}" - LINE "a {" - LINE " const var_y = 20" - LINE " c :- Position{$var_x, $var_y}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t a = ecs_lookup_fullpath(world, "a"); - test_assert(a != 0); - ecs_entity_t b = ecs_lookup_fullpath(world, "a.b"); - test_assert(b != 0); - ecs_entity_t c = ecs_lookup_fullpath(world, "a.c"); - test_assert(c != 0); - - test_assert(ecs_has(world, a, Position)); - test_assert(ecs_has(world, b, Position)); - test_assert(ecs_has(world, c, Position)); - const Position *p; - - p = ecs_get(world, a, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 10); - - p = ecs_get(world, b, Position); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - - p = ecs_get(world, c, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_component_from_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "const var_pos = Position{10, 20}" - LINE "a :- $var_pos" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t var = ecs_lookup_fullpath(world, "var_pos"); - test_assert(var == 0); - - ecs_entity_t a = ecs_lookup_fullpath(world, "a"); - test_assert(a != 0); - - const Position *p = ecs_get(world, a, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_component_from_var_in_scope(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "const var_pos = Position{10, 20}" - LINE "a {" - LINE " - $var_pos" - LINE "}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t var = ecs_lookup_fullpath(world, "var_pos"); - test_assert(var == 0); - - ecs_entity_t a = ecs_lookup_fullpath(world, "a"); - test_assert(a != 0); - - const Position *p = ecs_get(world, a, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_scope_w_component_after_const_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo {" - LINE " const var = 5" - LINE " - Position{x: 10, y: $var}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - test_assert(ecs_has(world, foo, Position)); - - const Position *p = ecs_get(world, foo, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 5); - - ecs_fini(world); -} - -void Plecs_component_after_const_add_expr(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo {" - LINE " const var = 5 + 15" - LINE " - Position{x: 10, y: $var}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - test_assert(ecs_has(world, foo, Position)); - - const Position *p = ecs_get(world, foo, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_component_after_const_sub_expr(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo {" - LINE " const var = 25 - 5" - LINE " - Position{x: 10, y: $var}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - test_assert(ecs_has(world, foo, Position)); - - const Position *p = ecs_get(world, foo, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_component_after_const_mul_expr(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo {" - LINE " const var = 2 * 10" - LINE " - Position{x: 10, y: $var}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - test_assert(ecs_has(world, foo, Position)); - - const Position *p = ecs_get(world, foo, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_component_after_const_div_expr(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "Foo {" - LINE " const var = 40 / 2" - LINE " - Position{x: 10, y: $var}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - test_assert(ecs_has(world, foo, Position)); - - const Position *p = ecs_get(world, foo, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_component_after_const_paren_expr(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "e {" - LINE " const val = (10 + 20)" - LINE " - Position{$val, $val * 2}" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - const Position *p = ecs_get(world, e, Position); - test_assert(p != NULL); - test_int(p->x, 30); - test_int(p->y, 60); - - ecs_fini(world); -} - -void Plecs_parse_with(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - HEAD "Foo" - LINE "Bar {" - LINE " Hello" - LINE "}"; - - ECS_TAG(world, Tag); - - ecs_set_with(world, Tag); - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_set_with(world, 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Bar.Hello"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello != 0); - - test_assert(ecs_has(world, foo, Tag)); - test_assert(ecs_has(world, bar, Tag)); - test_assert(ecs_has(world, hello, Tag)); - - ecs_fini(world); -} - -void Plecs_parse_with_w_with(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - const char *expr = - HEAD "Foo" - LINE "with TagB {" - LINE " Bar {" - LINE " Hello" - LINE " }" - LINE "}"; - - ecs_set_with(world, TagA); - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_set_with(world, 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Bar.Hello"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello != 0); - - test_assert(ecs_has(world, foo, TagA)); - test_assert(ecs_has(world, bar, TagA)); - test_assert(ecs_has(world, hello, TagA)); - - test_assert(!ecs_has(world, TagB, TagA)); - test_assert(!ecs_has(world, foo, TagB)); - test_assert(ecs_has(world, bar, TagB)); - test_assert(ecs_has(world, hello, TagB)); - - ecs_fini(world); -} - -void Plecs_parse_with_w_tag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - const char *expr = - HEAD "Foo" - LINE "with TagB {" - LINE " Bar {" - LINE " - Hello" - LINE " }" - LINE "}"; - - ecs_set_with(world, TagA); - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - ecs_set_with(world, 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello"); - - test_assert(foo != 0); - test_assert(bar != 0); - test_assert(hello != 0); - - test_assert(ecs_has(world, foo, TagA)); - test_assert(ecs_has(world, bar, TagA)); - test_assert(ecs_has(world, hello, TagA)); - test_assert(ecs_has_id(world, bar, hello)); - - test_assert(!ecs_has(world, TagB, TagA)); - test_assert(!ecs_has(world, foo, TagB)); - test_assert(ecs_has(world, bar, TagB)); - test_assert(!ecs_has(world, hello, TagB)); - - ecs_fini(world); -} - -void Plecs_parse_with_value(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "with Position{10, 20} {" - LINE " Foo" - LINE " Bar" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(foo != 0); - test_assert(bar != 0); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - const Position *ptr = ecs_get(world, bar, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - -void Plecs_parse_with_2_values(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "with Position{10, 20}, Velocity{1, 2} {" - LINE " Foo" - LINE " Bar" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(foo != 0); - test_assert(bar != 0); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - { - const Position *ptr = ecs_get(world, bar, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - const Velocity *ptr = ecs_get(world, foo, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - { - const Velocity *ptr = ecs_get(world, bar, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - - ecs_fini(world); -} - -void Plecs_parse_with_2_nested_values(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "with Position{10, 20} {" - LINE " with Velocity{1, 2} {" - LINE " Foo" - LINE " Bar" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(foo != 0); - test_assert(bar != 0); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - { - const Position *ptr = ecs_get(world, bar, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - const Velocity *ptr = ecs_get(world, foo, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - { - const Velocity *ptr = ecs_get(world, bar, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - - ecs_fini(world); -} - -void Plecs_parse_with_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "const pos : Position = {10, 20}" - LINE "with $pos {" - LINE " Foo" - LINE " Bar" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(foo != 0); - test_assert(bar != 0); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - { - const Position *ptr = ecs_get(world, bar, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - -void Plecs_parse_with_2_vars(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "const pos : Position = {10, 20}" - LINE "const vel : Velocity = {1, 2}" - LINE "with $pos, $vel {" - LINE " Foo" - LINE " Bar" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(foo != 0); - test_assert(bar != 0); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - { - const Position *ptr = ecs_get(world, bar, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - const Velocity *ptr = ecs_get(world, foo, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - { - const Velocity *ptr = ecs_get(world, bar, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - - ecs_fini(world); -} - -void Plecs_parse_with_2_nested_vars(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "const pos : Position = {10, 20}" - LINE "const vel : Velocity = {1, 2}" - LINE "with $pos, $vel {" - LINE " with $vel {" - LINE " Foo" - LINE " Bar" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - - test_assert(foo != 0); - test_assert(bar != 0); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - { - const Position *ptr = ecs_get(world, bar, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - const Velocity *ptr = ecs_get(world, foo, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - { - const Velocity *ptr = ecs_get(world, bar, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - - ecs_fini(world); -} - -void Plecs_parse_with_var_in_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "Parent {" - LINE " const pos : Position = {10, 20}" - LINE " with $pos { " - LINE " Foo" - LINE " Bar" - LINE " }" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Parent.Foo"); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Parent.Bar"); - - test_assert(parent != 0); - test_assert(foo != 0); - test_assert(bar != 0); - - { - const Position *ptr = ecs_get(world, foo, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - { - const Position *ptr = ecs_get(world, bar, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - - -void Plecs_assign_const_w_expr(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "const var = 5 + 1" - LINE "e :- Position{x: $var, y: $var * 2}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - const Position *p = ecs_get(world, e, Position); - test_assert(p != NULL); - test_int(p->x, 6); - test_int(p->y, 12); - - ecs_fini(world); -} - -void Plecs_const_w_type(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "const var : flecs.meta.i32 = 5 / 2" - LINE "e :- Position{x: $var, y: $var * 3}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - const Position *p = ecs_get(world, e, Position); - test_assert(p != NULL); - test_int(p->x, 2); - test_int(p->y, 6); - - ecs_fini(world); -} - -void Plecs_assembly_no_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_fini(world); -} - -void Plecs_assembly_empty(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assembly_no_props(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " - Foo" - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assembly_prop_no_type(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop height" - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assembly_prop_no_default(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop height: flecs.meta.f32" - LINE "}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assembly_prop(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop height: flecs.meta.f32 = 0" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 1); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - - ecs_fini(world); -} - -void Plecs_assembly_prop_space_colon(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop height : flecs.meta.f32 = 0" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 1); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - - ecs_fini(world); -} - -void Plecs_assembly_2_props(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.i32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE "}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_i32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - ecs_fini(world); -} - -void Plecs_assembly_instance_w_default_values(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 10" - LINE " prop height : flecs.meta.f32 = 20" - LINE "}" - LINE "" - LINE "e :- Tree" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 10, height: 20}"); - ecs_os_free(str); - - ecs_fini(world); -} - -void Plecs_assembly_instance_w_assign_default_values(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 10" - LINE " prop height : flecs.meta.f32 = 20" - LINE "}" - LINE "" - LINE "e :- Tree{}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 10, height: 20}"); - ecs_os_free(str); - - ecs_fini(world); -} - -void Plecs_assembly_instance_w_overridden_values(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 10" - LINE " prop height : flecs.meta.f32 = 20" - LINE "}" - LINE "" - LINE "e :- Tree{width: 30, height: 40}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 30, height: 40}"); - ecs_os_free(str); - - ecs_fini(world); -} - -void Plecs_assembly_w_child(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " child :- Position{$width * 10 + 1, $height * 20 + 2}" - LINE "}" - LINE "" - LINE "e :- Tree{width: 1, height: 2}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - ecs_entity_t child = ecs_lookup_fullpath(world, "e.child"); - test_assert(child != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 1, height: 2}"); - ecs_os_free(str); - - const Position *p = ecs_get(world, child, Position); - test_assert(p != NULL); - test_int(p->x, 11); - test_int(p->y, 42); - - ecs_fini(world); -} - -void Plecs_assembly_w_child_parse_script(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " child :- Position{$width * 10 + 1, $height * 20 + 2}" - LINE "}" - LINE "" - LINE "e :- Tree{width: 1, height: 2}" - LINE ""; - - test_assert(ecs_script(world, { - .entity = ecs_entity(world, { .name = "main" }), - .str = expr - }) != 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - ecs_entity_t child = ecs_lookup_fullpath(world, "e.child"); - test_assert(child != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 1, height: 2}"); - ecs_os_free(str); - - const Position *p = ecs_get(world, child, Position); - test_assert(p != NULL); - test_int(p->x, 11); - test_int(p->y, 42); - - ecs_fini(world); -} - -void Plecs_assembly_w_child_parse_script_twice(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " child :- Position{$width * 10 + 1, $height * 20 + 2}" - LINE "}" - LINE "" - LINE "e :- Tree{width: 1, height: 2}" - LINE ""; - - test_assert(ecs_script(world, { - .entity = ecs_entity(world, { .name = "main" }), - .str = expr - }) != 0); - - test_assert(ecs_script(world, { - .entity = ecs_entity(world, { .name = "main" }), - .str = expr - }) != 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - ecs_entity_t child = ecs_lookup_fullpath(world, "e.child"); - test_assert(child != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 1, height: 2}"); - ecs_os_free(str); - - const Position *p = ecs_get(world, child, Position); - test_assert(p != NULL); - test_int(p->x, 11); - test_int(p->y, 42); - - ecs_fini(world); -} - -void Plecs_assembly_w_child_update_after_parse(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " child :- Position{$width * 10 + 1, $height * 20 + 2}" - LINE "}" - LINE "" - LINE "e :- Tree{width: 1, height: 2}" - LINE ""; - - test_assert(ecs_script(world, { - .entity = ecs_entity(world, { .name = "main" }), - .str = expr - }) != 0); - - { - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - ecs_entity_t child = ecs_lookup_fullpath(world, "e.child"); - test_assert(child != 0); - } - - test_assert(!ecs_is_deferred(world)); - - const char *expr_update = - LINE "e :- Tree{width: 3, height: 4}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr_update) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - ecs_entity_t child = ecs_lookup_fullpath(world, "e.child"); - test_assert(child != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 3, height: 4}"); - ecs_os_free(str); - - const Position *p = ecs_get(world, child, Position); - test_assert(p != NULL); - test_int(p->x, 31); - test_int(p->y, 82); - - ecs_fini(world); -} - -void Plecs_assembly_w_nested_child(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " child {" - LINE " - Position{$width, $height}" - LINE " grand_child :- Position{$height, $width}" - LINE " }" - LINE "}" - LINE "" - LINE "e :- Tree{width: 1, height: 2}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - ecs_entity_t child = ecs_lookup_fullpath(world, "e.child"); - test_assert(child != 0); - - ecs_entity_t grand_child = ecs_lookup_fullpath(world, "e.child.grand_child"); - test_assert(grand_child != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 1, height: 2}"); - ecs_os_free(str); - - { - const Position *p = ecs_get(world, child, Position); - test_assert(p != NULL); - test_int(p->x, 1); - test_int(p->y, 2); - } - - { - const Position *p = ecs_get(world, grand_child, Position); - test_assert(p != NULL); - test_int(p->x, 2); - test_int(p->y, 1); - } - - ecs_fini(world); -} - -void Plecs_assembly_w_prefab(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " Prefab base {" - LINE " - Velocity{$width * 2, $height * 3}" - LINE " }" - LINE " child : base {" - LINE " - Position{$width, $height}" - LINE " }" - LINE "}" - LINE "" - LINE "e :- Tree{width: 1, height: 2}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - ecs_entity_t child = ecs_lookup_fullpath(world, "e.child"); - test_assert(child != 0); - - ecs_entity_t base = ecs_lookup_fullpath(world, "e.base"); - test_assert(base != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 1, height: 2}"); - ecs_os_free(str); - - test_assert(ecs_has_pair(world, child, EcsIsA, base)); - - { - const Position *p = ecs_get(world, child, Position); - test_assert(p != NULL); - test_int(p->x, 1); - test_int(p->y, 2); - } - - { - const Velocity *v = ecs_get(world, child, Velocity); - test_assert(v != NULL); - test_int(v->x, 2); - test_int(v->y, 6); - } - - { - const Velocity *v = ecs_get(world, base, Velocity); - test_assert(v != NULL); - test_int(v->x, 2); - test_int(v->y, 6); - } - - ecs_fini(world); -} - -void Plecs_assembly_w_prefab_tree(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " Prefab base {" - LINE " - Velocity{$width * 2, $height * 3}" - LINE " Prefab child {" - LINE " - Velocity{$height * 3, $width * 2}" - LINE " }" - LINE " }" - LINE " child : base {" - LINE " - Position{$width, $height}" - LINE " }" - LINE "}" - LINE "" - LINE "e :- Tree{width: 1, height: 2}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - ecs_entity_t child = ecs_lookup_fullpath(world, "e.child"); - test_assert(child != 0); - - ecs_entity_t base = ecs_lookup_fullpath(world, "e.base"); - test_assert(base != 0); - - ecs_entity_t base_child = ecs_lookup_fullpath(world, "e.base.child"); - test_assert(base_child != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - test_assert(ecs_has_id(world, e, tree)); - const void *ptr = ecs_get_id(world, e, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_assert(str != NULL); - test_str(str, "{width: 1, height: 2}"); - ecs_os_free(str); - - test_assert(ecs_has_pair(world, child, EcsIsA, base)); - - { - const Position *p = ecs_get(world, child, Position); - test_assert(p != NULL); - test_int(p->x, 1); - test_int(p->y, 2); - } - - { - const Velocity *v = ecs_get(world, child, Velocity); - test_assert(v != NULL); - test_int(v->x, 2); - test_int(v->y, 6); - } - - { - const Velocity *v = ecs_get(world, base, Velocity); - test_assert(v != NULL); - test_int(v->x, 2); - test_int(v->y, 6); - } - - { - const Velocity *v = ecs_get(world, base_child, Velocity); - test_assert(v != NULL); - test_int(v->x, 6); - test_int(v->y, 2); - } - - ecs_fini(world); -} - -void Plecs_assembly_w_nested_assembly(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " child {" - LINE " - Position{$width, $height}" - LINE " }" - LINE "}" - LINE "" - LINE "assembly Forest {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " tree_1 :- Tree{-$width, -$height}" - LINE " tree_2 :- Tree{$width + 1, $height + 1}" - LINE "}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - ecs_entity_t forest = ecs_lookup_fullpath(world, "Forest"); - test_assert(forest != 0); - test_assert(ecs_lookup_fullpath(world, "Forest.tree_1") != 0); - test_assert(ecs_lookup_fullpath(world, "Forest.tree_2") != 0); - test_assert(ecs_lookup_fullpath(world, "Forest.tree_1.child") == 0); - test_assert(ecs_lookup_fullpath(world, "Forest.tree_2.child") == 0); - test_assert(ecs_lookup_fullpath(world, "tree_1") == 0); - test_assert(ecs_lookup_fullpath(world, "tree_2") == 0); - - { - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - } - - { - const EcsStruct *st = ecs_get(world, forest, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - } - - test_assert(!ecs_is_deferred(world)); - - const char *expr_instance = - LINE "f :- Forest{10, 20}" - LINE ""; - test_assert(ecs_plecs_from_str(world, NULL, expr_instance) == 0); - - ecs_entity_t f = ecs_lookup_fullpath(world, "f"); - test_assert(f != 0); - ecs_entity_t f_tree_1 = ecs_lookup_fullpath(world, "f.tree_1"); - test_assert(f_tree_1 != 0); - ecs_entity_t f_tree_2 = ecs_lookup_fullpath(world, "f.tree_2"); - test_assert(f_tree_2 != 0); - ecs_entity_t f_tree_1_child = ecs_lookup_fullpath(world, "f.tree_1.child"); - test_assert(f_tree_1_child != 0); - ecs_entity_t f_tree_2_child = ecs_lookup_fullpath(world, "f.tree_2.child"); - test_assert(f_tree_2_child != 0); - - { - const void *ptr = ecs_get_id(world, f, forest); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, forest, ptr); - test_str(str, "{width: 10, height: 20}"); - ecs_os_free(str); - } - { - const void *ptr = ecs_get_id(world, f_tree_1, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{width: -10, height: -20}"); - ecs_os_free(str); - } - { - const void *ptr = ecs_get_id(world, f_tree_2, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{width: 11, height: 21}"); - ecs_os_free(str); - } - - { - const Position *p = ecs_get(world, f_tree_1_child, Position); - test_assert(p != NULL); - test_int(p->x, -10); - test_int(p->y, -20); - } - { - const Position *p = ecs_get(world, f_tree_2_child, Position); - test_assert(p != NULL); - test_int(p->x, 11); - test_int(p->y, 21); - } - - ecs_fini(world); -} - -void Plecs_instantiate_prefab_w_assembly(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop width : flecs.meta.f32 = 0" - LINE " prop height : flecs.meta.f32 = 0" - LINE " child :- Position{$width, $height}" - LINE "}" - LINE "" - LINE "Prefab p :- Tree{width: 10, height: 20}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - ecs_entity_t p = ecs_lookup_fullpath(world, "p"); - test_assert(p != 0); - test_assert(ecs_lookup_fullpath(world, "p.child") == 0); - test_assert(ecs_lookup_fullpath(world, "child") == 0); - - const char *expr_instance = - LINE "e : p" - LINE ""; - test_assert(ecs_plecs_from_str(world, NULL, expr_instance) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - ecs_entity_t child = ecs_lookup_fullpath(world, "e.child"); - test_assert(child != 0); - - test_assert(ecs_lookup_fullpath(world, "e.child") != 0); - - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 2); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); - - { - const void *ptr = ecs_get_id(world, p, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{width: 10, height: 20}"); - ecs_os_free(str); - } - - test_assert(ecs_has_pair(world, e, EcsIsA, p)); - - { - const Position *p = ecs_get(world, child, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - - ecs_fini(world); -} - -void Plecs_assembly_w_prefab_w_assembly(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "assembly Tree {" - LINE " prop count: flecs.meta.f32 = 0" - LINE "}" - LINE "" - LINE "assembly Forest {" - LINE " prop count: flecs.meta.f32 = 0" - LINE "" - LINE " Prefab TreePrefab {" - LINE " - Tree{count: $count}" - LINE " }" - LINE "" - LINE " child : TreePrefab" - LINE "}" - LINE "f :- Forest{10}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - ecs_entity_t forest = ecs_lookup_fullpath(world, "Forest"); - test_assert(forest != 0); - ecs_entity_t tree_prefab = ecs_lookup_fullpath(world, "Forest.TreePrefab"); - test_assert(tree_prefab != 0); - - ecs_entity_t f = ecs_lookup_fullpath(world, "f"); - test_assert(f != 0); - ecs_entity_t child = ecs_lookup_fullpath(world, "f.child"); - test_assert(child != 0); - - { - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 1); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - } - - { - const EcsStruct *st = ecs_get(world, forest, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 1); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - } - - { - const void *ptr = ecs_get_id(world, f, forest); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, forest, ptr); - test_str(str, "{count: 10}"); - ecs_os_free(str); - } - - { - const void *ptr = ecs_get_id(world, tree_prefab, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{count: 0}"); - ecs_os_free(str); - } - - ecs_fini(world); -} - -void Plecs_3_assemblies(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "assembly Tree {" - LINE " prop t: flecs.meta.f32 = 10" - LINE "}" - LINE "" - LINE "assembly Forest {" - LINE " prop f: flecs.meta.f32 = 20" - LINE "}" - LINE "" - LINE "assembly Park {" - LINE " prop p: flecs.meta.f32 = 30" - LINE "}" - LINE "" - LINE "a :- Tree{}" - LINE "b :- Forest{}" - LINE "c :- Park{}" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - ecs_entity_t forest = ecs_lookup_fullpath(world, "Forest"); - test_assert(forest != 0); - ecs_entity_t park = ecs_lookup_fullpath(world, "Park"); - test_assert(park != 0); - - ecs_entity_t a = ecs_lookup_fullpath(world, "a"); - test_assert(a != 0); - ecs_entity_t b = ecs_lookup_fullpath(world, "b"); - test_assert(b != 0); - ecs_entity_t c = ecs_lookup_fullpath(world, "c"); - test_assert(c != 0); - - { - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 1); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "t"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - } - { - const EcsStruct *st = ecs_get(world, forest, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 1); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "f"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - } - { - const EcsStruct *st = ecs_get(world, park, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 1); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "p"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - } - - { - const void *ptr = ecs_get_id(world, a, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{t: 10}"); - ecs_os_free(str); - } - { - const void *ptr = ecs_get_id(world, b, forest); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, forest, ptr); - test_str(str, "{f: 20}"); - ecs_os_free(str); - } - { - const void *ptr = ecs_get_id(world, c, park); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, park, ptr); - test_str(str, "{p: 30}"); - ecs_os_free(str); - } - - ecs_fini(world); -} - -void Plecs_assembly_nested_w_default_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop count: flecs.meta.f32 = 0" - LINE " trunk :- Position{$count, $count * 2}" - LINE "}" - LINE "" - LINE "assembly Forest {" - LINE " prop count: flecs.meta.f32 = 0" - LINE " child :- Tree{count:$}" - LINE "}" - LINE "f :- Forest{10}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - ecs_entity_t forest = ecs_lookup_fullpath(world, "Forest"); - test_assert(forest != 0); - - ecs_entity_t f = ecs_lookup_fullpath(world, "f"); - test_assert(f != 0); - ecs_entity_t child = ecs_lookup_fullpath(world, "f.child"); - test_assert(child != 0); - ecs_entity_t trunk = ecs_lookup_fullpath(world, "f.child.trunk"); - test_assert(trunk != 0); - - { - const EcsStruct *st = ecs_get(world, tree, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 1); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - } - - { - const EcsStruct *st = ecs_get(world, forest, EcsStruct); - test_assert(st != NULL); - test_int(st->members.count, 1); - test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count"); - test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); - } - - { - const void *ptr = ecs_get_id(world, f, forest); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, forest, ptr); - test_str(str, "{count: 10}"); - ecs_os_free(str); - } - { - const void *ptr = ecs_get_id(world, child, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{count: 10}"); - ecs_os_free(str); - } - - { - const Position *p = ecs_get(world, trunk, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - - ecs_fini(world); -} - -void Plecs_assembly_w_anonymous(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop count: flecs.meta.i32 = 0" - LINE " _ :- Position{$count, $count * 2}" - LINE " _ :- Position{$count, $count * 2}" - LINE "}" - LINE "" - LINE "t :- Tree{10}"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - ecs_entity_t t = ecs_lookup_fullpath(world, "t"); - test_assert(t != 0); - - { - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { .id = ecs_childof(tree) }, - { .id = ecs_id(Position) }, - { .id = EcsPrefab }, - } - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_int(2, ecs_iter_count(&it)); - ecs_filter_fini(f); - } - - { - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { .id = ecs_childof(t) }, - { .id = ecs_id(Position) }, - } - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_int(2, ecs_iter_count(&it)); - ecs_filter_fini(f); - } - - ecs_fini(world); -} - -void Plecs_assembly_w_anonymous_parse_again(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop count: flecs.meta.i32 = 0" - LINE " _ :- Position{$count, $count * 2}" - LINE " _ :- Position{$count, $count * 2}" - LINE "}" - LINE "" - LINE "t :- Tree{10}"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - const char *expr_again = - LINE "t :- Tree{10}"; - test_assert(ecs_plecs_from_str(world, NULL, expr_again) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - ecs_entity_t t = ecs_lookup_fullpath(world, "t"); - test_assert(t != 0); - - { - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { .id = ecs_childof(tree) }, - { .id = ecs_id(Position) }, - { .id = EcsPrefab }, - } - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_int(2, ecs_iter_count(&it)); - ecs_filter_fini(f); - } - - { - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { .id = ecs_childof(t) }, - { .id = ecs_id(Position) }, - } - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_int(2, ecs_iter_count(&it)); - ecs_filter_fini(f); - } - - ecs_fini(world); -} - -void Plecs_typed_const_w_composite_type_invalid_assignment(void) { - ecs_world_t *world = ecs_init(); - - ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "const var_pos : Position = Position{10, 20}" - LINE "a :- $var_pos" - LINE ""; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_typed_const_w_composite_type(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "const var_pos : Position = {10, 20}" - LINE "a :- $var_pos" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t var = ecs_lookup_fullpath(world, "var_pos"); - test_assert(var == 0); - - ecs_entity_t a = ecs_lookup_fullpath(world, "a"); - test_assert(a != 0); - - const Position *p = ecs_get(world, a, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_var_to_typed_const_w_composite_type(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ecs_id(Position) = ecs_struct(world, { - .entity = ecs_entity(world, {.name = "Position"}), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - HEAD "const var_pos_a : Position = {10, 20}" - HEAD "const var_pos_b = $var_pos_a" - LINE "a :- $var_pos_b" - LINE ""; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t var = ecs_lookup_fullpath(world, "var_pos"); - test_assert(var == 0); - - ecs_entity_t a = ecs_lookup_fullpath(world, "a"); - test_assert(a != 0); - - const Position *p = ecs_get(world, a, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_assembly_w_composite_prop_invalid_assignment(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop pos: Position = Position{10, 20}" - LINE " child :- $pos" - LINE "}" - LINE "" - LINE "t :- Tree{pos: {20, 30}}"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assembly_w_composite_prop(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop pos: Position = {10, 20}" - LINE " child :- $pos" - LINE "}" - LINE "t :- Tree{pos: {20, 30}}" - LINE ""; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - ecs_entity_t t = ecs_lookup_fullpath(world, "t"); - test_assert(t != 0); - ecs_entity_t t_child = ecs_lookup_fullpath(world, "t.child"); - test_assert(t_child != 0); - - { - const void *ptr = ecs_get_id(world, t, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{pos: {x: 20, y: 30}}"); - ecs_os_free(str); - } - - { - const Position *p = ecs_get(world, t_child, Position); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - } - - ecs_fini(world); -} - -void Plecs_assembly_with_with(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "assembly Tree {" - LINE " prop count: flecs.meta.i32 = 0" - LINE " with Foo {" - LINE " child :- Position{$count, $count * 2}" - LINE " }" - LINE "}" - LINE "t :- Tree{count: 10}" - LINE ""; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree"); - test_assert(tree != 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - ecs_entity_t t = ecs_lookup_fullpath(world, "t"); - test_assert(t != 0); - ecs_entity_t t_child = ecs_lookup_fullpath(world, "t.child"); - test_assert(t_child != 0); - - { - const void *ptr = ecs_get_id(world, t, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{count: 10}"); - ecs_os_free(str); - } - - { - const Position *p = ecs_get(world, t_child, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - - ecs_fini(world); -} - -void Plecs_using_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t p1 = ecs_new_entity(world, "foo.p1"); - ecs_entity_t p2 = ecs_new_entity(world, "foo.p2"); - - ecs_set_scope(world, p1); - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_set_scope(world, p2); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_set_scope(world, 0); - - const char *expr = - LINE "using foo.*\n" - LINE "e :- Position{10, 20}\n" - LINE "e :- Velocity{1, 2}\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t pos = ecs_lookup_fullpath(world, "foo.p1.Position"); - test_assert(pos == ecs_id(Position)); - ecs_entity_t vel = ecs_lookup_fullpath(world, "foo.p2.Velocity"); - test_assert(vel == ecs_id(Velocity)); - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - test_assert(ecs_has(world, e, Position)); - test_assert(ecs_has(world, e, Velocity)); - - { - const Position *ptr = ecs_get(world, e, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - { - const Velocity *ptr = ecs_get(world, e, Velocity); - test_assert(ptr != NULL); - test_int(ptr->x, 1); - test_int(ptr->y, 2); - } - - ecs_fini(world); -} - -void Plecs_single_line_comment_in_value(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "e :- Position{\n" - LINE " x: 10\n" - LINE " //foo\n" - LINE " y: 20\n" - LINE "}"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - test_assert(ecs_has(world, e, Position)); - - const Position *ptr = ecs_get(world, e, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_single_line_comment_in_value_after_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "e :- Position{\n" - LINE " // foo\n" - LINE " x: 10\n" - LINE " y: 20\n" - LINE "}"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - test_assert(ecs_has(world, e, Position)); - - const Position *ptr = ecs_get(world, e, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_multi_line_comment_in_value(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "e :- Position{\n" - LINE " x: 10\n" - LINE " /*\n" - LINE " * foo\n" - LINE " */\n" - LINE " y: 20\n" - LINE "}"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - test_assert(ecs_has(world, e, Position)); - - const Position *ptr = ecs_get(world, e, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_multi_line_comment_in_value_after_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "e :- Position{\n" - LINE " /*\n" - LINE " * foo\n" - LINE " */\n" - LINE " x: 10\n" - LINE " y: 20\n" - LINE "}"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t e = ecs_lookup_fullpath(world, "e"); - test_assert(e != 0); - - test_assert(ecs_has(world, e, Position)); - - const Position *ptr = ecs_get(world, e, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - ecs_fini(world); -} - -void Plecs_unterminated_multi_line_comment_in_value(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "e :- Position{\n" - LINE " x: 10\n" - LINE " /*\n" - LINE " * foo\n" - LINE " y: 20\n" - LINE "}"; - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_module_stmt(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "module hello\n" - LINE "e :- Position{10, 20}\n" - LINE "e :- Foo\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t hello = ecs_lookup_fullpath(world, "hello"); - test_assert(hello != 0); - test_assert(ecs_has_id(world, hello, EcsModule)); - - ecs_entity_t e = ecs_lookup_fullpath(world, "hello.e"); - test_assert(e != 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - test_assert(ecs_has(world, e, Position)); - test_assert(ecs_has_id(world, e, foo)); - - { - const Position *ptr = ecs_get(world, e, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - -void Plecs_nested_module_stmt(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "module hello.world\n" - LINE "e :- Position{10, 20}\n" - LINE "e :- Foo\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t hello = ecs_lookup_fullpath(world, "hello"); - test_assert(hello != 0); - test_assert(ecs_has_id(world, hello, EcsModule)); - ecs_entity_t hello_world = ecs_lookup_fullpath(world, "hello.world"); - test_assert(hello_world != 0); - test_assert(ecs_has_id(world, hello_world, EcsModule)); - - ecs_entity_t e = ecs_lookup_fullpath(world, "hello.world.e"); - test_assert(e != 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - test_assert(ecs_has(world, e, Position)); - test_assert(ecs_has_id(world, e, foo)); - - { - const Position *ptr = ecs_get(world, e, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - -void Plecs_module_stmt_w_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "module hello.world\n" - LINE "parent {\n" - LINE " e :- Position{10, 20}\n" - LINE " e :- Foo\n" - LINE "}\n" - LINE "f :- Position{30, 40}\n" - LINE "f :- Foo\n" - LINE ""; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t hello = ecs_lookup_fullpath(world, "hello"); - test_assert(hello != 0); - test_assert(ecs_has_id(world, hello, EcsModule)); - ecs_entity_t hello_world = ecs_lookup_fullpath(world, "hello.world"); - test_assert(hello_world != 0); - test_assert(ecs_has_id(world, hello_world, EcsModule)); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "hello.world.parent"); - test_assert(parent != 0); - ecs_entity_t e = ecs_lookup_fullpath(world, "hello.world.parent.e"); - test_assert(e != 0); - ecs_entity_t f = ecs_lookup_fullpath(world, "hello.world.f"); - test_assert(f != 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - test_assert(ecs_has(world, e, Position)); - test_assert(ecs_has_id(world, e, foo)); - test_assert(ecs_has(world, f, Position)); - test_assert(ecs_has_id(world, f, foo)); - - { - const Position *ptr = ecs_get(world, e, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - { - const Position *ptr = ecs_get(world, f, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 30); - test_int(ptr->y, 40); - } - - ecs_fini(world); -} - -void Plecs_module_stmt_w_nested_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "module hello.world\n" - LINE "parent {\n" - LINE " child {\n" - LINE " e :- Position{10, 20}\n" - LINE " e :- Foo\n" - LINE " }\n" - LINE "}\n" - LINE "f :- Position{30, 40}\n" - LINE "f :- Foo\n" - LINE ""; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t hello = ecs_lookup_fullpath(world, "hello"); - test_assert(hello != 0); - test_assert(ecs_has_id(world, hello, EcsModule)); - ecs_entity_t hello_world = ecs_lookup_fullpath(world, "hello.world"); - test_assert(hello_world != 0); - test_assert(ecs_has_id(world, hello_world, EcsModule)); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "hello.world.parent"); - test_assert(parent != 0); - ecs_entity_t child = ecs_lookup_fullpath(world, "hello.world.parent.child"); - test_assert(child != 0); - ecs_entity_t e = ecs_lookup_fullpath(world, "hello.world.parent.child.e"); - test_assert(e != 0); - ecs_entity_t f = ecs_lookup_fullpath(world, "hello.world.f"); - test_assert(f != 0); - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - test_assert(ecs_has(world, e, Position)); - test_assert(ecs_has_id(world, e, foo)); - test_assert(ecs_has(world, f, Position)); - test_assert(ecs_has_id(world, f, foo)); - - { - const Position *ptr = ecs_get(world, e, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - { - const Position *ptr = ecs_get(world, f, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 30); - test_int(ptr->y, 40); - } - - ecs_fini(world); -} - -void Plecs_module_w_assembly(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "module hello.world\n" - LINE "assembly Tree {\n" - LINE " prop count: flecs.meta.i32 = 0\n" - LINE " child :- Position{$count, $count * 2}\n" - LINE "}\n" - LINE ""; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - const char *expr_inst = - LINE "t :- hello.world.Tree{10}\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr_inst) == 0); - - ecs_entity_t tree = ecs_lookup_fullpath(world, "hello.world.Tree"); - test_assert(tree != 0); - ecs_entity_t t = ecs_lookup_fullpath(world, "t"); - test_assert(t != 0); - ecs_entity_t child = ecs_lookup_fullpath(world, "t.child"); - test_assert(child != 0); - - { - const void *ptr = ecs_get_id(world, t, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{count: 10}"); - ecs_os_free(str); - } - - test_assert(ecs_has(world, child, Position)); - - { - const Position *ptr = ecs_get(world, child, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - -void Plecs_module_w_nested_assembly(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "module hello.world\n" - LINE "assembly Tree {\n" - LINE " prop count: flecs.meta.i32 = 0\n" - LINE " child :- Position{$count, $count * 2}\n" - LINE "}\n" - LINE "assembly Forest {\n" - LINE " prop count: flecs.meta.i32 = 0\n" - LINE " t :- Tree{count:$}\n" - LINE "}\n" - LINE ""; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - const char *expr_inst = - LINE "f :- hello.world.Forest{10}\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr_inst) == 0); - - ecs_entity_t forest = ecs_lookup_fullpath(world, "hello.world.Forest"); - test_assert(forest != 0); - ecs_entity_t tree = ecs_lookup_fullpath(world, "hello.world.Tree"); - test_assert(tree != 0); - ecs_entity_t f = ecs_lookup_fullpath(world, "f"); - test_assert(f != 0); - ecs_entity_t t = ecs_lookup_fullpath(world, "f.t"); - test_assert(t != 0); - ecs_entity_t child = ecs_lookup_fullpath(world, "f.t.child"); - test_assert(child != 0); - - { - const void *ptr = ecs_get_id(world, f, forest); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, forest, ptr); - test_str(str, "{count: 10}"); - ecs_os_free(str); - } - { - const void *ptr = ecs_get_id(world, t, tree); - test_assert(ptr != NULL); - char *str = ecs_ptr_to_expr(world, tree, ptr); - test_str(str, "{count: 10}"); - ecs_os_free(str); - } - - test_assert(ecs_has(world, child, Position)); - - { - const Position *ptr = ecs_get(world, child, Position); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - ecs_fini(world); -} - -void Plecs_assign_singleton_tag(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "$ :- Foo\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - test_assert(ecs_has_id(world, foo, foo)); - - ecs_fini(world); -} - -void Plecs_assign_singleton_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "$ :- Position{10, 20}\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_has(world, ecs_id(Position), Position)); - - const Position *p = ecs_get(world, ecs_id(Position), Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_singleton_tag_w_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "$ {\n" - LINE " - Foo\n" - LINE "}\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - - test_assert(ecs_has_id(world, foo, foo)); - - ecs_fini(world); -} - -void Plecs_assign_singleton_2_tags_w_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "$ {\n" - LINE " - Foo\n" - LINE " - Bar\n" - LINE "}\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - test_assert(foo != 0); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - test_assert(bar != 0); - - test_assert(ecs_has_id(world, foo, foo)); - test_assert(ecs_has_id(world, bar, bar)); - - ecs_fini(world); -} - -void Plecs_assign_singleton_component_w_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "$ {\n" - LINE " - Position{10, 20}\n" - LINE "}\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - test_assert(ecs_has(world, ecs_id(Position), Position)); - - const Position *p = ecs_get(world, ecs_id(Position), Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_singleton_2_components_w_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "$ {\n" - LINE " - Position{10, 20}\n" - LINE " - Velocity{1, 2}\n" - LINE "}\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - const Position *p = ecs_get(world, ecs_id(Position), Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - const Velocity *v = ecs_get(world, ecs_id(Velocity), Velocity); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - - ecs_fini(world); -} - -void Plecs_with_pair_in_scope(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "Tgt\n" - LINE "Rel\n" - LINE "\n" - LINE "Parent {\n" - LINE " with (Rel, Tgt) {\n" - LINE " Child\n" - LINE " }\n" - LINE "}\n"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent"); - ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child"); - ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel"); - ecs_entity_t tgt = ecs_lookup_fullpath(world, "Tgt"); - - test_assert(!ecs_lookup_fullpath(world, "Parent.Rel")); - test_assert(!ecs_lookup_fullpath(world, "Parent.Tgt")); - - test_assert(parent != 0); - test_assert(child != 0); - test_assert(rel != 0); - test_assert(tgt != 0); - - test_assert(ecs_has_pair(world, child, rel, tgt)); - - ecs_fini(world); -} - -void Plecs_assembly_redeclare_prop_as_const(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "assembly Foo {\n" - LINE " prop x : flecs.meta.f32 = 10\n" - LINE " prop y : flecs.meta.f32 = 10\n" - LINE " const y : flecs.meta.f32 = 20\n" - LINE "}" - LINE "ent :- Foo{}\n" - LINE "\n"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assembly_redeclare_prop_as_prop(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "assembly Foo {\n" - LINE " prop x : flecs.meta.f32 = 10\n" - LINE " prop y : flecs.meta.f32 = 10\n" - LINE " prop y : flecs.meta.f32 = 20\n" - LINE "}" - LINE "ent :- Foo{}\n" - LINE "\n"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_assembly_redeclare_const_as_const(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "assembly Foo {\n" - LINE " prop x : flecs.meta.f32 = 10\n" - LINE " const y : flecs.meta.f32 = 10\n" - LINE " const y : flecs.meta.f32 = 20\n" - LINE "}" - LINE "ent :- Foo{}\n" - LINE "\n"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_add_auto_override(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - const char *expr = - LINE "p :- OVERRIDE|Tag" - LINE "\n"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t p = ecs_lookup_fullpath(world, "p"); - test_assert(p != 0); - test_assert(ecs_has_id(world, p, ECS_OVERRIDE | Tag)); - - ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, p); - test_assert(e != 0); - test_assert(ecs_has(world, e, Tag)); - - ecs_fini(world); -} - -void Plecs_add_auto_override_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - const char *expr = - LINE "p :- OVERRIDE|(Rel, Tgt)" - LINE "\n"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t p = ecs_lookup_fullpath(world, "p"); - test_assert(p != 0); - test_assert(ecs_has_id(world, p, ECS_OVERRIDE | ecs_pair(Rel, Tgt))); - - ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, p); - test_assert(e != 0); - test_assert(ecs_has_pair(world, e, Rel, Tgt)); - - ecs_fini(world); -} - -void Plecs_scope_w_auto_override(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - const char *expr = - LINE "p {" - LINE " - OVERRIDE|Tag" - LINE "}" - LINE "\n"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t p = ecs_lookup_fullpath(world, "p"); - test_assert(p != 0); - test_assert(ecs_has_id(world, p, ECS_OVERRIDE | Tag)); - - ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, p); - test_assert(e != 0); - test_assert(ecs_has(world, e, Tag)); - - ecs_fini(world); -} - -void Plecs_scope_w_auto_override_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - const char *expr = - LINE "p {" - LINE " - OVERRIDE|(Rel, Tgt)" - LINE "}" - LINE "\n"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t p = ecs_lookup_fullpath(world, "p"); - test_assert(p != 0); - test_assert(ecs_has_id(world, p, ECS_OVERRIDE | ecs_pair(Rel, Tgt))); - - ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, p); - test_assert(e != 0); - test_assert(ecs_has_pair(world, e, Rel, Tgt)); - - ecs_fini(world); -} - -void Plecs_pair_w_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - const char *expr = - LINE "const rel = Rel\n" - LINE "ent {\n" - LINE " - ($rel, Tgt)\n" - LINE "}\n" - LINE "\n"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t ent = ecs_lookup_fullpath(world, "ent"); - - test_assert(ecs_has_pair(world, ent, Rel, Tgt)); - - ecs_fini(world); -} - -void Plecs_pair_w_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - const char *expr = - LINE "const tgt = Tgt\n" - LINE "ent {\n" - LINE " - (Rel, $tgt)\n" - LINE "}\n" - LINE "\n"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t ent = ecs_lookup_fullpath(world, "ent"); - - test_assert(ecs_has_pair(world, ent, Rel, Tgt)); - - ecs_fini(world); -} - -void Plecs_assembly_w_pair_w_this_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - const char *expr = - LINE "assembly Foo {\n" - LINE " prop x : flecs.meta.f32 = 10\n" // dummy prop - LINE " - (Rel, $this)\n" - LINE "}\n" - LINE "ent :- Foo{}\n" - LINE "\n"; - - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo"); - ecs_entity_t ent = ecs_lookup_fullpath(world, "ent"); - - test_assert(ecs_has_id(world, ent, foo)); - test_assert(ecs_has_pair(world, ent, Rel, ent)); - - ecs_fini(world); -} - -void Plecs_with_value_not_a_component(void) { - ecs_world_t *world = ecs_init(); - - const char *expr = - LINE "with Foo{10} {\n" - LINE " Bar\n" - LINE "}\n" - LINE "\n"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_component_in_with_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "with Position{10, 20} {\n" - LINE " - Bar\n" - LINE "}\n" - LINE "\n"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_component_in_with_scope_nested(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "with Position{10, 20} {\n" - LINE " with Velocity{10, 20} {\n" - LINE " - Bar\n" - LINE " }\n" - LINE "}\n" - LINE "\n"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) != 0); - - ecs_fini(world); -} - -void Plecs_component_in_with_scope_in_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "foo {\n" - LINE " - Position{10, 20}\n" - LINE " with Position{30, 40} {\n" - LINE " - Bar\n" - LINE " }\n" - LINE "}\n"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "foo"); - test_assert(foo != 0); - ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar"); - test_assert(bar != 0); - - test_assert(ecs_has(world, foo, Position)); - test_assert(ecs_has_id(world, foo, bar)); - - const Position *p = ecs_get(world, foo, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Plecs_assign_after_with_in_scope(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_struct(world, { - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - ecs_struct(world, { - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } - }); - - const char *expr = - LINE "foo {\n" - LINE " - Position{10, 20}\n" - LINE " with Position{30, 40} {\n" - LINE " }\n" - LINE " - Velocity{1, 2}\n" - LINE "}\n"; - - ecs_log_set_level(-4); - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "foo"); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - test_assert(ecs_has(world, foo, Velocity)); - - const Position *p = ecs_get(world, foo, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - const Velocity *v = ecs_get(world, foo, Velocity); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - - ecs_fini(world); -} - -void Plecs_array_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_array(world, { - .entity = ecs_id(Position), - .type = ecs_id(ecs_f32_t), - .count = 2 - }); - - const char *expr = - LINE "foo :- Position[10, 20]\n"; - test_assert(ecs_plecs_from_str(world, NULL, expr) == 0); - - ecs_entity_t foo = ecs_lookup_fullpath(world, "foo"); - test_assert(foo != 0); - - test_assert(ecs_has(world, foo, Position)); - - const Position *p = ecs_get(world, foo, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/Rest.c b/vendors/flecs/test/addons/src/Rest.c index 14412942f..b527316b6 100644 --- a/vendors/flecs/test/addons/src/Rest.c +++ b/vendors/flecs/test/addons/src/Rest.c @@ -24,7 +24,565 @@ void Rest_get(void) { char *reply_str = ecs_strbuf_get(&reply.body); test_assert(reply_str != NULL); test_str(reply_str, - "{\"path\":\"flecs.core.World\", \"label\":\"World\", \"ids\":[]}"); + "{\"parent\":\"flecs.core\", \"name\":\"World\", " + "\"components\":{\"(flecs.core.Identifier,flecs.core.Symbol)\":null, \"(flecs.doc.Description,flecs.doc.Brief)\":{\"value\":\"Entity associated with world\"}}}"); + ecs_os_free(reply_str); + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_get_cached(void) { + ecs_world_t *world = ecs_init(); + + ecs_http_server_t *srv = ecs_rest_server_init(world, + &(ecs_http_server_desc_t){ + .cache_timeout = 1.0 + }); + test_assert(srv != NULL); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/entity/flecs/core/World", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, + "{\"parent\":\"flecs.core\", \"name\":\"World\", " + "\"components\":{\"(flecs.core.Identifier,flecs.core.Symbol)\":null, \"(flecs.doc.Description,flecs.doc.Brief)\":{\"value\":\"Entity associated with world\"}}}"); + ecs_os_free(reply_str); + } + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/entity/flecs/core/World", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, + "{\"parent\":\"flecs.core\", \"name\":\"World\", " + "\"components\":{\"(flecs.core.Identifier,flecs.core.Symbol)\":null, \"(flecs.doc.Description,flecs.doc.Brief)\":{\"value\":\"Entity associated with world\"}}}"); + ecs_os_free(reply_str); + } + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_get_cached_invalid(void) { + ecs_world_t *world = ecs_init(); + + ecs_http_server_t *srv = ecs_rest_server_init(world, + &(ecs_http_server_desc_t){ + .cache_timeout = 1.0 + }); + test_assert(srv != NULL); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(-1, ecs_http_server_request(srv, "GET", + "/entity/flecs/core/Wor", &reply)); + test_int(reply.code, 404); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, + "{\"error\":\"entity \'flecs/core/Wor\' not found\"}"); + ecs_os_free(reply_str); + } + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(-1, ecs_http_server_request(srv, "GET", + "/entity/flecs/core/Wor", &reply)); + test_int(reply.code, 404); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, + "{\"error\":\"entity \'flecs/core/Wor\' not found\"}"); + ecs_os_free(reply_str); + } + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_try_query(void) { + ecs_world_t *world = ecs_init(); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + ecs_log_set_level(-4); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(-1, ecs_http_server_request(srv, "GET", + "/query?expr=Foo", &reply)); + test_int(reply.code, 400); // No try, should error + ecs_strbuf_reset(&reply.body); + } + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/query?expr=Foo&try=true", &reply)); + test_int(reply.code, 200); // With try, should not error + ecs_strbuf_reset(&reply.body); + } + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_query(void) { + ecs_world_t *world = ecs_init(); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/query?expr=Position", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, + "{\"results\":[{\"name\":\"e\", \"fields\":{\"values\":[0]}}]}"); + ecs_os_free(reply_str); + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_named_query(void) { + ecs_world_t *world = ecs_init(); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + ECS_COMPONENT(world, Position); + + ecs_query(world, { + .entity = ecs_entity(world, { .name = "position_query" }), + .terms = { + { .id = ecs_id(Position) } + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/query?name=position_query", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, + "{\"results\":[{\"name\":\"e\", \"fields\":{\"values\":[0]}}]}"); + ecs_os_free(reply_str); + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_tables(void) { + ecs_world_t *world = ecs_init(); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/tables", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_request_commands(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/capture", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, "{\"frame\":0}"); + ecs_os_free(reply_str); + } + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_frame_begin(world, 0); + ecs_defer_begin(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_defer_end(world); + ecs_frame_end(world); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/frame/0", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + } + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_request_commands_2_syncs(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/capture", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, "{\"frame\":0}"); + ecs_os_free(reply_str); + } + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_frame_begin(world, 0); + ecs_defer_begin(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_defer_end(world); + ecs_defer_begin(world); + ecs_set(world, e2, Position, {20, 30}); + ecs_defer_end(world); + ecs_frame_end(world); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/frame/0", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + } + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_request_commands_no_frames(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(-1, ecs_http_server_request(srv, "GET", + "/commands/frame/0", &reply)); + test_int(reply.code, 404); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + } + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_request_commands_no_commands(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/capture", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, "{\"frame\":0}"); + ecs_os_free(reply_str); + } + + ecs_frame_begin(world, 0); + ecs_defer_begin(world); + ecs_defer_end(world); + ecs_frame_end(world); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/frame/0", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + } + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_request_commands_garbage_collect(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/capture", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, "{\"frame\":0}"); + ecs_os_free(reply_str); + } + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_frame_begin(world, 0); + ecs_defer_begin(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_defer_end(world); + ecs_frame_end(world); + + /* Retained for a minute at 60 FPS */ + for (int i = 0; i < 60 * 60; i ++) { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/frame/0", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + + ecs_progress(world, 0.016); + } + + /* New request hits garbage collection */ + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/capture", &reply)); + test_int(reply.code, 200); + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, "{\"frame\":3601}"); + ecs_os_free(reply_str); + } + + /* Garbage collected */ + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(-1, ecs_http_server_request(srv, "GET", + "/commands/frame/0", &reply)); + test_int(reply.code, 404); + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + } + + /* Garbage collected */ + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/commands/frame/3601", &reply)); + test_int(reply.code, 200); + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + } + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_script_error(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_script(world, { + .entity = ecs_entity(world, { .name = "main.flecs" }), + .code = "" + }); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + { + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + ecs_log_set_level(-4); + test_int(-1, ecs_http_server_request(srv, "PUT", + "/script/main.flecs?code=struct%20Position%20%7B%0A%20%20x%20%3A%0A%7D", + &reply)); + test_int(reply.code, 400); + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + } + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_import_rest_after_mini(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsRest); + + test_assert(ecs_id(EcsRest) != 0); + + ecs_fini(world); +} + +static +void Move(ecs_iter_t *it) { } + +void Rest_get_pipeline_stats_after_delete_system(void) { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, FlecsStats); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + ecs_progress(world, 1.0); + + ecs_delete(world, ecs_id(Move)); + + ecs_progress(world, 1.0); + + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/stats/pipeline?name=all&period=1m", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_request_world_summary_before_monitor_sys_run(void) { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, FlecsStats); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/entity/flecs/core/World?values=true", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + ecs_os_free(reply_str); + + ecs_rest_server_fini(srv); + + ecs_fini(world); +} + +void Rest_escape_backslash(void) { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, FlecsStats); + + ecs_http_server_t *srv = ecs_rest_server_init(world, NULL); + test_assert(srv != NULL); + + ecs_entity(world, { .name = "foo/bar" }); + + ecs_http_reply_t reply = ECS_HTTP_REPLY_INIT; + test_int(0, ecs_http_server_request(srv, "GET", + "/entity/foo%5C%2Fbar", &reply)); + test_int(reply.code, 200); + + char *reply_str = ecs_strbuf_get(&reply.body); + test_assert(reply_str != NULL); + test_str(reply_str, "{\"name\":\"foo/bar\"}"); ecs_os_free(reply_str); ecs_rest_server_fini(srv); diff --git a/vendors/flecs/test/addons/src/RulesBasic.c b/vendors/flecs/test/addons/src/RulesBasic.c deleted file mode 100644 index 226ae3d2c..000000000 --- a/vendors/flecs/test/addons/src/RulesBasic.c +++ /dev/null @@ -1,6698 +0,0 @@ -#include - -void RulesBasic_1_fact_w_tag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_fact_w_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, ent, Position, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_fact_w_tag_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_fact_w_component_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(ent, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set_pair(world, ent, Position, TgtA, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_same_src_w_tag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent), RelB(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_same_src_w_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(ent), Velocity(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, ent, Position, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, ent, Velocity, {1, 2}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 2)); - const Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_same_src_w_tag_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, TgtA), RelA(ent, TgtB)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_same_src_w_component_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(ent, TgtA), Position(ent, TgtB)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set_pair(world, ent, Position, TgtA, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set_pair(world, ent, Position, TgtB, {30, 40}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ecs_pair(ecs_id(Position), TgtB), ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_other_src_w_tag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent1 = ecs_new_entity(world, "ent1"); - ecs_entity_t ent2 = ecs_new_entity(world, "ent2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent1), RelB(ent2)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent1, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent2, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent1, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(ent2, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_other_src_w_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t ent1 = ecs_new_entity(world, "ent1"); - ecs_entity_t ent2 = ecs_new_entity(world, "ent2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(ent1), Velocity(ent2)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, ent1, Position, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, ent2, Velocity, {1, 2}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ent1, ecs_field_src(&it, 1)); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ent2, ecs_field_src(&it, 2)); - const Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_other_src_w_tag_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent1 = ecs_new_entity(world, "ent1"); - ecs_entity_t ent2 = ecs_new_entity(world, "ent2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent1, TgtA), RelA(ent2, TgtB)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent1, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent2, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(ent2, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_other_src_w_component_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent1 = ecs_new_entity(world, "ent1"); - ecs_entity_t ent2 = ecs_new_entity(world, "ent2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(ent1, TgtA), Position(ent2, TgtB)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set_pair(world, ent1, Position, TgtA, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set_pair(world, ent2, Position, TgtB, {30, 40}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 1)); - test_uint(ent1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(ecs_id(Position), TgtB), ecs_field_id(&it, 2)); - test_uint(ent2, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_fact_w_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelA); - ecs_add(world, ent, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(EcsWildcard, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_fact_w_pair_any_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_fact_w_pair_any_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_(ent, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_fact_w_pair_any_rel_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelB, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_(ent, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_same_src_w_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_(ent), _(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelA); - ecs_add(world, ent, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(EcsWildcard, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(EcsWildcard, ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_same_src_w_pair_any_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, _), RelA(ent, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_same_src_w_pair_any_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_(ent, TgtA), _(ent, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_same_src_w_pair_any_rel_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelB, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_(ent, _), _(ent, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_other_src_w_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent1 = ecs_new_entity(world, "ent1"); - ecs_entity_t ent2 = ecs_new_entity(world, "ent2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_(ent1), _(ent2)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent1, RelA); - ecs_add(world, ent1, RelB); - ecs_add(world, ent2, RelA); - ecs_add(world, ent2, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(EcsWildcard, ecs_field_id(&it, 1)); - test_uint(ent1, ecs_field_src(&it, 1)); - test_uint(EcsWildcard, ecs_field_id(&it, 2)); - test_uint(ent2, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_other_src_w_pair_any_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent1 = ecs_new_entity(world, "ent1"); - ecs_entity_t ent2 = ecs_new_entity(world, "ent2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent1, _), RelA(ent2, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent1, RelA, TgtA); - ecs_add_pair(world, ent1, RelA, TgtB); - ecs_add_pair(world, ent2, RelA, TgtA); - ecs_add_pair(world, ent2, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(ent1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ent2, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_other_src_w_pair_any_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent1 = ecs_new_entity(world, "ent1"); - ecs_entity_t ent2 = ecs_new_entity(world, "ent2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent1, _), RelA(ent2, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent1, RelA, TgtA); - ecs_add_pair(world, ent1, RelA, TgtB); - ecs_add_pair(world, ent2, RelA, TgtA); - ecs_add_pair(world, ent2, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(ent1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ent2, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_facts_other_src_w_pair_any_rel_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_entity_t ent1 = ecs_new_entity(world, "ent1"); - ecs_add_pair(world, ent1, RelA, TgtA); - ecs_add_pair(world, ent1, RelB, TgtA); - ecs_entity_t ent2 = ecs_new_entity(world, "ent2"); - ecs_add_pair(world, ent2, RelA, TgtA); - ecs_add_pair(world, ent2, RelB, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_(ent1, _), _(ent2, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(ent1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ent2, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_this_src_w_tag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelA); - ecs_add(world, e2, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_this_src_w_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e2, Position, {30, 40}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); test_int(p[0].y, 20); - test_int(p[1].x, 30); test_int(p[1].y, 40); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_this_src_w_tag_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_this_src_w_component_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set_pair(world, e1, Position, TgtA, {10, 20}); - ecs_set_pair(world, e2, Position, TgtA, {30, 40}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); test_int(p[0].y, 20); - test_int(p[1].x, 30); test_int(p[1].y, 40); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_this_src_w_tag_2_tables(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, FragmentA); - ECS_TAG(world, FragmentB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_add(world, e1, FragmentA); - ecs_add(world, e2, FragmentB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelA); - ecs_add(world, e2, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_this_src_w_component_2_tables(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, FragmentA); - ECS_TAG(world, FragmentB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_add(world, e1, FragmentA); - ecs_add(world, e2, FragmentB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e2, Position, {30, 40}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - { - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - { - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_this_src_w_tag_pair_2_tables(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, FragmentA); - ECS_TAG(world, FragmentB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_add(world, e1, FragmentA); - ecs_add(world, e2, FragmentB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_this_src_w_component_pair_2_tables(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TgtA); - ECS_TAG(world, FragmentA); - ECS_TAG(world, FragmentB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_add(world, e1, FragmentA); - ecs_add(world, e2, FragmentB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set_pair(world, e1, Position, TgtA, {10, 20}); - ecs_set_pair(world, e2, Position, TgtA, {30, 40}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - { - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - { - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_this_src_w_tag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), RelB($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelA); - ecs_add(world, e2, RelA); - ecs_add(world, e1, RelB); - ecs_add(world, e2, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_this_src_w_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this), Velocity($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e2, Position, {30, 40}); - - ecs_set(world, e1, Velocity, {1, 2}); - ecs_set(world, e2, Velocity, {3, 4}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); test_int(p[0].y, 20); - test_int(p[1].x, 30); test_int(p[1].y, 40); - const Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v[0].x, 1); test_int(v[0].y, 2); - test_int(v[1].x, 3); test_int(v[1].y, 4); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_this_src_ent_src_w_tag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), RelB(e3)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelA); - ecs_add(world, e2, RelA); - ecs_add(world, e3, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_this_src_ent_src_w_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this), Velocity(e3)", - .instanced = true - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e2, Position, {30, 40}); - ecs_set(world, e3, Velocity, {1, 2}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); test_int(p[0].y, 20); - test_int(p[1].x, 30); test_int(p[1].y, 40); - const Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v[0].x, 1); test_int(v[0].y, 2); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_ent_src_this_src_w_tag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB(e3), RelA($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelA); - ecs_add(world, e2, RelA); - ecs_add(world, e3, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_ent_src_this_src_w_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Velocity(e3), Position($this)", - .instanced = true - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e2, Position, {30, 40}); - ecs_set(world, e3, Velocity, {1, 2}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - const Velocity *v = ecs_field(&it, Velocity, 1); - test_assert(v != NULL); - test_int(v[0].x, 1); test_int(v[0].y, 2); - const Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); test_int(p[0].y, 20); - test_int(p[1].x, 30); test_int(p[1].y, 40); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_recycled_tag(void) { - ecs_world_t *world = ecs_init(); - - ecs_delete(world, ecs_new_id(world)); - ECS_TAG(world, RelA); - test_assert((uint32_t)RelA != RelA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_recycled_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t ent = ecs_new_entity(world, "ent"); - test_assert((uint32_t)ent != ent); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_recycled_pair_rel(void) { - ecs_world_t *world = ecs_init(); - - ecs_delete(world, ecs_new_id(world)); - ECS_TAG(world, RelA); - test_assert((uint32_t)RelA != RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_recycled_pair_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ecs_delete(world, ecs_new_id(world)); - ECS_TAG(world, TgtA); - test_assert((uint32_t)TgtA != TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_src_w_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), *($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelA); - ecs_add(world, e1, RelB); - - ecs_add(world, e2, RelA); - ecs_add(world, e2, RelB); - ecs_add(world, e2, RelC); - - ecs_add(world, e3, RelA); - ecs_add(world, e3, RelB); - ecs_add(world, e3, RelC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelC, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_src_w_pair_rel_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, TgtA), *($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e1, RelB, TgtB); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - ecs_add_pair(world, e2, RelC, TgtA); - ecs_add_pair(world, e2, RelC, TgtB); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - ecs_add_pair(world, e3, RelC, TgtA); - ecs_add_pair(world, e3, RelC, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_src_w_pair_tgt_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, TgtA), RelA($this, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add_pair(world, e1, RelB, TgtA); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtB); - ecs_add_pair(world, e2, RelA, TgtC); - ecs_add_pair(world, e2, RelB, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - ecs_add_pair(world, e3, RelA, TgtC); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_src_w_pair_rel_tgt_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, TgtA), *($this, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtB); - ecs_add_pair(world, e2, RelB, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_src_w_any(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_" - }); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); - - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_entity_t prev = 0; - int32_t count = 0, e1_matched = 0; - while (ecs_rule_next(&it)) { - test_assert(it.count > 0); - test_assert(!prev || prev != it.entities[0]); - prev = it.entities[0]; - if (it.entities[0] == e1) { - e1_matched ++; - } - count ++; - } - - test_assert(count > 0); - test_int(e1_matched, 1); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_not_any(void) { - ecs_world_t *world = ecs_mini(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!_" - }); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_src_w_any_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), _($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelA); - ecs_add(world, e1, RelB); - - ecs_add(world, e2, RelA); - ecs_add(world, e2, RelB); - ecs_add(world, e2, RelC); - - ecs_add(world, e3, RelA); - ecs_add(world, e3, RelB); - ecs_add(world, e3, RelC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(EcsWildcard, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(EcsWildcard, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_src_w_pair_rel_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, TgtA), _($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e1, RelB, TgtB); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - ecs_add_pair(world, e2, RelC, TgtA); - ecs_add_pair(world, e2, RelC, TgtB); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - ecs_add_pair(world, e3, RelC, TgtA); - ecs_add_pair(world, e3, RelC, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_src_w_pair_tgt_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, TgtA), RelA($this, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add_pair(world, e1, RelB, TgtA); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtB); - ecs_add_pair(world, e2, RelA, TgtC); - ecs_add_pair(world, e2, RelB, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - ecs_add_pair(world, e3, RelA, TgtC); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_src_w_pair_rel_tgt_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, TgtA), _($this, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtB); - ecs_add_pair(world, e2, RelB, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_ent_src_w_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_entity_t e1 = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "*(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelA); - ecs_add(world, e1, RelB); - ecs_add(world, e1, RelC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelC, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_ent_src_w_pair_rel_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "*(ent, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e1, RelC, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_ent_src_w_pair_tgt_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_entity_t e1 = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add_pair(world, e1, RelA, TgtC); - ecs_add_pair(world, e1, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_ent_src_w_pair_rel_tgt_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "ent"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add_pair(world, e1, RelB, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "*(ent, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_wildcard_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(*)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_add_id(world, e1, Tag); - ecs_new(world, RelA); - ecs_new(world, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_wildcard_src_w_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, Tag); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(*, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_add(world, e1, Tag); - ecs_new_w_pair(world, RelA, TgtA); - ecs_new_w_pair(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_wildcard_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(*), RelB(*)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_add_id(world, e1, Tag); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelB); - ecs_add(world, e2, RelB); - ecs_add(world, e3, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_wildcard_src_w_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, Tag); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(*, TgtA), RelB(*, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_add(world, e1, Tag); - ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_rule_w_iter_next(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_iter_next(&it)); - } - - ecs_add(world, ent, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_iter_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_iter_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_wildcard_src_w_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(*, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_new_w_pair(world, Rel, TgtA); - ecs_new_w_pair(world, Rel, TgtB); - ecs_new_w_pair(world, Rel, TgtC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_wildcard_src_w_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(*, Tgt)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_new_w_pair(world, RelA, Tgt); - ecs_new_w_pair(world, RelB, Tgt); - ecs_new_w_pair(world, RelC, Tgt); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 1)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_wildcard_src_w_pair_tgt_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(*, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_new_w_pair(world, Rel, TgtA); - ecs_new_w_pair(world, Rel, TgtB); - ecs_new_w_pair(world, Rel, TgtC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, this_var)); - test_uint(TgtA, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, this_var)); - test_uint(TgtB, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - test_uint(TgtC, ecs_iter_get_var(&it, this_var)); - test_uint(TgtC, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_wildcard_src_w_pair_rel_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this(*, Tgt)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_new_w_pair(world, RelA, Tgt); - ecs_new_w_pair(world, RelB, Tgt); - ecs_new_w_pair(world, RelC, Tgt); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, this_var)); - test_uint(RelA, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, this_var)); - test_uint(RelB, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 1)); - test_uint(RelC, ecs_iter_get_var(&it, this_var)); - test_uint(RelC, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_any_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(_)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_add_id(world, e1, Tag); - ecs_new(world, RelA); - ecs_new(world, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_any_src_w_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, Tag); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(_, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_add(world, e1, Tag); - ecs_new_w_pair(world, RelA, TgtA); - ecs_new_w_pair(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_any_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(_), RelB(_)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_add_id(world, e1, Tag); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelB); - ecs_add(world, e2, RelB); - ecs_add(world, e3, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_any_src_w_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, Tag); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(_, TgtA), RelB(_, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_add(world, e1, Tag); - ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_any_src_w_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(_, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_new_w_pair(world, Rel, TgtA); - ecs_new_w_pair(world, Rel, TgtB); - ecs_new_w_pair(world, Rel, TgtC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_any_src_w_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(_, Tgt)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_new_w_pair(world, RelA, Tgt); - ecs_new_w_pair(world, RelB, Tgt); - ecs_new_w_pair(world, RelC, Tgt); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 1)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_any_src_w_pair_tgt_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(_, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_new_w_pair(world, Rel, TgtA); - ecs_new_w_pair(world, Rel, TgtB); - ecs_new_w_pair(world, Rel, TgtC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - test_uint(TgtC, ecs_iter_get_var(&it, this_var)); - test_uint(TgtC, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, this_var)); - test_uint(TgtB, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, this_var)); - test_uint(TgtA, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_any_src_w_pair_rel_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this(_, Tgt)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_new_w_pair(world, RelA, Tgt); - ecs_new_w_pair(world, RelB, Tgt); - ecs_new_w_pair(world, RelC, Tgt); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 1)); - test_uint(RelC, ecs_iter_get_var(&it, this_var)); - test_uint(RelC, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, this_var)); - test_uint(RelB, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, this_var)); - test_uint(RelA, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_empty_rule(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_iter_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_invalid_rule(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo" - }); - - test_assert(r == NULL); - - ecs_fini(world); -} - -void RulesBasic_not_instanced_table_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this)" - }); - - test_assert(r != NULL); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_iter_t it = ecs_rule_iter(world, r); - { - test_bool(true, ecs_iter_next(&it)); - test_int(3, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - - const Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); test_int(p[0].y, 20); - test_int(p[1].x, 20); test_int(p[1].y, 30); - test_int(p[2].x, 30); test_int(p[2].y, 40); - } - - test_bool(false, ecs_iter_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_not_instanced_entity_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(e1)" - }); - - test_assert(r != NULL); - - ecs_set(world, e1, Position, {10, 20}); - - ecs_iter_t it = ecs_rule_iter(world, r); - { - test_bool(true, ecs_iter_next(&it)); - test_int(0, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - - const Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 10); - test_int(p->y, 20); - } - - test_bool(false, ecs_iter_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_not_instanced_mixed_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e0 = ecs_new_entity(world, "e0"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this), Velocity(e0)" - }); - - test_assert(r != NULL); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_set(world, e0, Velocity, {1, 2}); - - ecs_iter_t it = ecs_rule_iter(world, r); - { - test_bool(true, ecs_iter_next(&it)); - test_int(1, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - const Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 10); - test_int(p->y, 20); - - const Velocity *v = ecs_field(&it, Velocity, 2); - test_int(v->x, 1); - test_int(v->y, 2); - } - - { - test_bool(true, ecs_iter_next(&it)); - test_int(1, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, it.entities[0]); - - const Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 20); - test_int(p->y, 30); - - const Velocity *v = ecs_field(&it, Velocity, 2); - test_int(v->x, 1); - test_int(v->y, 2); - } - - { - test_bool(true, ecs_iter_next(&it)); - test_int(1, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, it.entities[0]); - - const Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 30); - test_int(p->y, 40); - - const Velocity *v = ecs_field(&it, Velocity, 2); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_bool(false, ecs_iter_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_instanced_table_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this)", - .instanced = true - }); - - test_assert(r != NULL); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_iter_t it = ecs_rule_iter(world, r); - { - test_bool(true, ecs_iter_next(&it)); - test_int(3, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - - const Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); test_int(p[0].y, 20); - test_int(p[1].x, 20); test_int(p[1].y, 30); - test_int(p[2].x, 30); test_int(p[2].y, 40); - } - - test_bool(false, ecs_iter_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_instanced_entity_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(e1)", - .instanced = true - }); - - test_assert(r != NULL); - - ecs_set(world, e1, Position, {10, 20}); - - ecs_iter_t it = ecs_rule_iter(world, r); - { - test_bool(true, ecs_iter_next(&it)); - test_int(0, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - - const Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 10); - test_int(p->y, 20); - } - - test_bool(false, ecs_iter_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_instanced_mixed_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e0 = ecs_new_entity(world, "e0"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($this), Velocity(e0)", - .instanced = true - }); - - test_assert(r != NULL); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_set(world, e0, Velocity, {1, 2}); - - ecs_iter_t it = ecs_rule_iter(world, r); - { - test_bool(true, ecs_iter_next(&it)); - test_int(3, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - - const Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); test_int(p[0].y, 20); - test_int(p[1].x, 20); test_int(p[1].y, 30); - test_int(p[2].x, 30); test_int(p[2].y, 40); - - const Velocity *v = ecs_field(&it, Velocity, 2); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_bool(false, ecs_iter_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_in_term(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "[in] Position(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, ent, Position, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_readonly(&it, 1)); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_out_term(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "[out] Position(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, ent, Position, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_readonly(&it, 1)); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_inout_term(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "[inout] Position(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, ent, Position, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_readonly(&it, 1)); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_nodata_term(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "[none] Position(ent)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_set(world, ent, Position, {10, 20}); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_readonly(&it, 1)); - const Position *p = ecs_field(&it, Position, 1); - test_assert(p == NULL); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_find_this_lowercase(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel($this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "this"); - test_assert(this_var != -1); - test_assert(this_var == 0); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_find_this_uppercase(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel($this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var == 0); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_find_this_tgt_lowercase(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(*, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "this"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_find_this_tgt_uppercase(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(*, $This)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "this"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_get_filter(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel" - }); - - test_assert(r != NULL); - - const ecs_filter_t *f = ecs_rule_get_filter(r); - test_assert(f != NULL); - test_int(f->term_count, 1); - test_uint(f->terms[0].id, Rel); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_iter_empty_source(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo()" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_id(Foo), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_iter_empty_source_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "*()" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(EcsWildcard, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_iter_empty_source_2_terms(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(), Bar()" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_id(Foo), ecs_field_id(&it, 1)); - test_uint(ecs_id(Bar), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_iter_empty_source_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(0, Tgt)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Foo, Tgt), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_iter_empty_source_pair_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "*(0, Tgt)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, Tgt), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_iter_empty_source_2_terms_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(0, Tgt), Bar(0, Tgt)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Foo, Tgt), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Bar, Tgt), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_iter_empty_source_2_terms_mixed(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, Bar()" - }); - - test_assert(r != NULL); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_iter_empty_source_2_terms_mixed_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Foo, Tgt), Bar(0, Tgt)" - }); - - test_assert(r != NULL); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Foo, Tgt); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_pair(Foo, Tgt), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Bar, Tgt), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_iter_empty_source_2_terms_mixed_pair_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Foo, Tgt), *(0, Tgt)" - }); - - test_assert(r != NULL); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Foo, Tgt); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_pair(Foo, Tgt), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, Tgt), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_this_var_w_empty_entity(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_entity_t t = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, Rel, t); - - ecs_rule_t *r = ecs_rule(world, { .expr = "Rel($x, $this)"}); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(t, it.entities[0]); - test_uint(ecs_pair(Rel, t), ecs_field_id(&it, 1)); - test_uint(e, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_match_disabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - - ecs_rule_t *r_1 = ecs_rule(world, { - .expr = "TagA" - }); - - ecs_rule_t *r_2 = ecs_rule(world, { - .expr = "TagA, ?Disabled" - }); - - test_assert(r_1 != NULL); - test_assert(r_2 != NULL); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchDisabled, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchDisabled, true); - - ecs_iter_t it = ecs_rule_iter(world, r_1); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - it = ecs_rule_iter(world, r_2); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r_1); - ecs_rule_fini(r_2); - - ecs_fini(world); -} - -void RulesBasic_match_prefab(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - - ecs_rule_t *r_1 = ecs_rule(world, { - .expr = "TagA" - }); - - ecs_rule_t *r_2 = ecs_rule(world, { - .expr = "TagA, ?Prefab" - }); - - test_assert(r_1 != NULL); - test_assert(r_2 != NULL); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchPrefab, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchPrefab, true); - - ecs_iter_t it = ecs_rule_iter(world, r_1); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - it = ecs_rule_iter(world, r_2); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r_1); - ecs_rule_fini(r_2); - - ecs_fini(world); -} - -void RulesBasic_match_disabled_prefab(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - ecs_entity_t e_4 = ecs_new(world, TagA); - ecs_add_id(world, e_4, EcsPrefab); - ecs_add_id(world, e_4, EcsDisabled); - - ecs_rule_t *r_1 = ecs_rule(world, { - .expr = "TagA" - }); - - ecs_rule_t *r_2 = ecs_rule(world, { - .expr = "TagA, ?Disabled" - }); - - ecs_rule_t *r_3 = ecs_rule(world, { - .expr = "TagA, ?Prefab" - }); - - ecs_rule_t *r_4 = ecs_rule(world, { - .expr = "TagA, ?Prefab, ?Disabled" - }); - - test_assert(r_1 != NULL); - test_assert(r_2 != NULL); - test_assert(r_3 != NULL); - test_assert(r_4 != NULL); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchDisabled, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchDisabled, true); - test_bool(ecs_rule_get_filter(r_3)->flags & EcsFilterMatchDisabled, false); - test_bool(ecs_rule_get_filter(r_4)->flags & EcsFilterMatchDisabled, true); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchPrefab, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchPrefab, false); - test_bool(ecs_rule_get_filter(r_3)->flags & EcsFilterMatchPrefab, true); - test_bool(ecs_rule_get_filter(r_4)->flags & EcsFilterMatchPrefab, true); - - ecs_iter_t it = ecs_rule_iter(world, r_1); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - { - it = ecs_rule_iter(world, r_2); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - } - - { - it = ecs_rule_iter(world, r_3); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - } - - { - it = ecs_rule_iter(world, r_4); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_rule_next(&it)); - } - - ecs_rule_fini(r_1); - ecs_rule_fini(r_2); - ecs_rule_fini(r_3); - ecs_rule_fini(r_4); - - ecs_fini(world); -} - -void RulesBasic_match_disabled_this_tgt(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - - ecs_new_w_pair(world, EcsChildOf, e_1); - ecs_new_w_pair(world, EcsChildOf, e_2); - ecs_new_w_pair(world, EcsChildOf, e_3); - - ecs_rule_t *r_1 = ecs_rule(world, { - .expr = "TagA, ChildOf($child, $this)" - }); - - ecs_rule_t *r_2 = ecs_rule(world, { - .expr = "TagA, ChildOf($child, $this), ?Disabled" - }); - - test_assert(r_1 != NULL); - test_assert(r_2 != NULL); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchDisabled, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchDisabled, true); - - ecs_iter_t it = ecs_rule_iter(world, r_1); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - it = ecs_rule_iter(world, r_2); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r_1); - ecs_rule_fini(r_2); - - ecs_fini(world); -} - -void RulesBasic_match_prefab_this_tgt(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - - ecs_new_w_pair(world, EcsChildOf, e_1); - ecs_new_w_pair(world, EcsChildOf, e_2); - ecs_new_w_pair(world, EcsChildOf, e_3); - - ecs_rule_t *r_1 = ecs_rule(world, { - .expr = "TagA, ChildOf($child, $this)" - }); - - ecs_rule_t *r_2 = ecs_rule(world, { - .expr = "TagA, ChildOf($child, $this), ?Prefab" - }); - - test_assert(r_1 != NULL); - test_assert(r_2 != NULL); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchPrefab, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchPrefab, true); - - ecs_iter_t it = ecs_rule_iter(world, r_1); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - it = ecs_rule_iter(world, r_2); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r_1); - ecs_rule_fini(r_2); - - ecs_fini(world); -} - -void RulesBasic_match_disabled_prefab_this_tgt(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - ecs_entity_t e_4 = ecs_new(world, TagA); - ecs_add_id(world, e_4, EcsPrefab); - ecs_add_id(world, e_4, EcsDisabled); - - ecs_new_w_pair(world, EcsChildOf, e_1); - ecs_new_w_pair(world, EcsChildOf, e_2); - ecs_new_w_pair(world, EcsChildOf, e_3); - ecs_new_w_pair(world, EcsChildOf, e_4); - - ecs_rule_t *r_1 = ecs_rule(world, { - .expr = "TagA, ChildOf($child, $this)" - }); - - ecs_rule_t *r_2 = ecs_rule(world, { - .expr = "TagA, ChildOf($child, $this), ?Disabled" - }); - - ecs_rule_t *r_3 = ecs_rule(world, { - .expr = "TagA, ChildOf($child, $this), ?Prefab" - }); - - ecs_rule_t *r_4 = ecs_rule(world, { - .expr = "TagA, ChildOf($child, $this), ?Prefab, ?Disabled" - }); - - test_assert(r_1 != NULL); - test_assert(r_2 != NULL); - test_assert(r_3 != NULL); - test_assert(r_4 != NULL); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchDisabled, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchDisabled, true); - test_bool(ecs_rule_get_filter(r_3)->flags & EcsFilterMatchDisabled, false); - test_bool(ecs_rule_get_filter(r_4)->flags & EcsFilterMatchDisabled, true); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchPrefab, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchPrefab, false); - test_bool(ecs_rule_get_filter(r_3)->flags & EcsFilterMatchPrefab, true); - test_bool(ecs_rule_get_filter(r_4)->flags & EcsFilterMatchPrefab, true); - - ecs_iter_t it = ecs_rule_iter(world, r_1); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - { - it = ecs_rule_iter(world, r_2); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - } - - { - it = ecs_rule_iter(world, r_3); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - } - - { - it = ecs_rule_iter(world, r_4); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_rule_next(&it)); - } - - ecs_rule_fini(r_1); - ecs_rule_fini(r_2); - ecs_rule_fini(r_3); - ecs_rule_fini(r_4); - - ecs_fini(world); -} - -void RulesBasic_match_self_disabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - - ecs_rule_t *r_1 = ecs_rule(world, { - .expr = "TagA(self)" - }); - - ecs_rule_t *r_2 = ecs_rule(world, { - .expr = "TagA(self), ?Disabled(self)" - }); - - test_assert(r_1 != NULL); - test_assert(r_2 != NULL); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchDisabled, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchDisabled, true); - - ecs_iter_t it = ecs_rule_iter(world, r_1); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - it = ecs_rule_iter(world, r_2); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r_1); - ecs_rule_fini(r_2); - - ecs_fini(world); -} - -void RulesBasic_match_self_prefab(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - - ecs_rule_t *r_1 = ecs_rule(world, { - .expr = "TagA(self)" - }); - - ecs_rule_t *r_2 = ecs_rule(world, { - .expr = "TagA(self), ?Prefab(self)" - }); - - test_assert(r_1 != NULL); - test_assert(r_2 != NULL); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchPrefab, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchPrefab, true); - - ecs_iter_t it = ecs_rule_iter(world, r_1); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - it = ecs_rule_iter(world, r_2); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r_1); - ecs_rule_fini(r_2); - - ecs_fini(world); -} - -void RulesBasic_match_self_disabled_prefab(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - ecs_entity_t e_4 = ecs_new(world, TagA); - ecs_add_id(world, e_4, EcsPrefab); - ecs_add_id(world, e_4, EcsDisabled); - - ecs_rule_t *r_1 = ecs_rule(world, { - .expr = "TagA(self)" - }); - - ecs_rule_t *r_2 = ecs_rule(world, { - .expr = "TagA(self), ?Disabled(self)" - }); - - ecs_rule_t *r_3 = ecs_rule(world, { - .expr = "TagA(self), ?Prefab(self)" - }); - - ecs_rule_t *r_4 = ecs_rule(world, { - .expr = "TagA(self), ?Prefab(self), ?Disabled(self)" - }); - - test_assert(r_1 != NULL); - test_assert(r_2 != NULL); - test_assert(r_3 != NULL); - test_assert(r_4 != NULL); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchDisabled, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchDisabled, true); - test_bool(ecs_rule_get_filter(r_3)->flags & EcsFilterMatchDisabled, false); - test_bool(ecs_rule_get_filter(r_4)->flags & EcsFilterMatchDisabled, true); - - test_bool(ecs_rule_get_filter(r_1)->flags & EcsFilterMatchPrefab, false); - test_bool(ecs_rule_get_filter(r_2)->flags & EcsFilterMatchPrefab, false); - test_bool(ecs_rule_get_filter(r_3)->flags & EcsFilterMatchPrefab, true); - test_bool(ecs_rule_get_filter(r_4)->flags & EcsFilterMatchPrefab, true); - - ecs_iter_t it = ecs_rule_iter(world, r_1); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - - { - it = ecs_rule_iter(world, r_2); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - } - - { - it = ecs_rule_iter(world, r_3); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_rule_next(&it)); - } - - { - it = ecs_rule_iter(world, r_4); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_rule_next(&it)); - } - - ecs_rule_fini(r_1); - ecs_rule_fini(r_2); - ecs_rule_fini(r_3); - ecs_rule_fini(r_4); - - ecs_fini(world); -} - -void RulesBasic_inout_none_first_term(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_rule_t *r = ecs_rule(world, { - .terms = { - { .id = ecs_id(Position), .inout = EcsInOutNone }, - { .id = ecs_id(Velocity) } - } - }); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p == NULL); - } - { - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_inout_none_second_term(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_rule_t *r = ecs_rule(world, { - .terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Velocity), .inout = EcsInOutNone } - } - }); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - { - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v == NULL); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_no_data_rule(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_rule_t *r = ecs_rule(world, { - .terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Velocity) } - }, - .flags = EcsFilterNoData - }); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p == NULL); - } - { - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v == NULL); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_frame_offset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_rule_t *f = ecs_rule(world, { - .terms = { - { .id = ecs_id(Position), } - }, - }); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); - ecs_entity_t e4 = ecs_new(world, Position); - ecs_entity_t e5 = ecs_new(world, Position); - - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagB); - ecs_add(world, e5, TagC); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_bool(ecs_rule_next(&it), true); - test_int(it.count, 2); - test_int(it.frame_offset, 0); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e1); - test_assert(it.entities[1] == e2); - test_assert(it.ids[0] == ecs_id(Position)); - - test_bool(ecs_rule_next(&it), true); - test_int(it.count, 2); - test_int(it.frame_offset, 2); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e3); - test_assert(it.entities[1] == e4); - test_assert(it.ids[0] == ecs_id(Position)); - - test_bool(ecs_rule_next(&it), true); - test_int(it.count, 1); - test_int(it.frame_offset, 4); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e5); - test_assert(it.ids[0] == ecs_id(Position)); - - test_bool(ecs_rule_next(&it), false); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_frame_offset_no_data(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_rule_t *f = ecs_rule(world, { - .terms = { - { .id = TagA, } - }, - }); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_entity_t e5 = ecs_new(world, TagA); - - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagB); - ecs_add(world, e5, TagC); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_bool(ecs_rule_next(&it), true); - test_int(it.count, 2); - test_int(it.frame_offset, 0); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e1); - test_assert(it.entities[1] == e2); - test_assert(it.ids[0] == TagA); - - test_bool(ecs_rule_next(&it), true); - test_int(it.count, 2); - test_int(it.frame_offset, 2); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e3); - test_assert(it.entities[1] == e4); - test_assert(it.ids[0] == TagA); - - test_bool(ecs_rule_next(&it), true); - test_int(it.count, 1); - test_int(it.frame_offset, 4); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e5); - test_assert(it.ids[0] == TagA); - - test_bool(ecs_rule_next(&it), false); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_match_empty_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); - ecs_entity_t e4 = ecs_new(world, Position); - - ecs_add(world, e2, TagA); - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_table_t *t2 = ecs_get_table(world, e2); - ecs_table_t *t3 = ecs_get_table(world, e3); - ecs_table_t *t4 = ecs_get_table(world, e4); - - ecs_delete(world, e1); - ecs_delete(world, e2); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ ecs_id(Position) }}, - .flags = EcsFilterMatchEmptyTables - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t1); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t2); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t3); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t4); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_rule_next(&it), false); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_match_empty_tables_no_data(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_entity_t e4 = ecs_new(world, Foo); - - ecs_add(world, e2, TagA); - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_table_t *t2 = ecs_get_table(world, e2); - ecs_table_t *t3 = ecs_get_table(world, e3); - ecs_table_t *t4 = ecs_get_table(world, e4); - - ecs_delete(world, e1); - ecs_delete(world, e2); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ Foo }}, - .flags = EcsFilterMatchEmptyTables - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t1); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), Foo); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t2); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), Foo); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t3); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_uint(ecs_field_id(&it, 1), Foo); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t4); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_uint(ecs_field_id(&it, 1), Foo); - - test_bool( ecs_rule_next(&it), false); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_match_empty_tables_w_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_entity_t e4 = ecs_new(world, Foo); - - ecs_add(world, e2, TagA); - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_get_table(world, e2); - ecs_table_t *t3 = ecs_get_table(world, e3); - ecs_table_t *t4 = ecs_get_table(world, e4); - - ecs_delete(world, e1); - ecs_delete(world, e2); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ Foo }, { TagA, .oper = EcsNot }}, - .flags = EcsFilterMatchEmptyTables - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t1); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), Foo); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t3); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_uint(ecs_field_id(&it, 1), Foo); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t4); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_uint(ecs_field_id(&it, 1), Foo); - - test_bool( ecs_rule_next(&it), false); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_match_empty_tables_w_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, TgtD); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtC); - ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtD); - - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_table_t *t2 = ecs_get_table(world, e2); - ecs_table_t *t3 = ecs_get_table(world, e3); - ecs_table_t *t4 = ecs_get_table(world, e4); - - ecs_delete(world, e1); - ecs_delete(world, e2); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ ecs_pair(Rel, EcsWildcard) }}, - .flags = EcsFilterMatchEmptyTables - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t1); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), ecs_pair(Rel, TgtA)); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t2); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), ecs_pair(Rel, TgtB)); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t3); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_uint(ecs_field_id(&it, 1), ecs_pair(Rel, TgtC)); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t4); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_uint(ecs_field_id(&it, 1), ecs_pair(Rel, TgtD)); - - test_bool( ecs_rule_next(&it), false); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_match_empty_tables_w_no_empty_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *f = ecs_rule(world, { - .terms = { - { Foo } - }, - .flags = EcsFilterMatchEmptyTables - }); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_add(world, e2, Bar); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_match_empty_tables_trivial(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); - ecs_entity_t e4 = ecs_new(world, Position); - - ecs_add(world, e2, TagA); - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_table_t *t2 = ecs_get_table(world, e2); - ecs_table_t *t3 = ecs_get_table(world, e3); - ecs_table_t *t4 = ecs_get_table(world, e4); - - ecs_delete(world, e1); - ecs_delete(world, e2); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ ecs_id(Position), .src.flags = EcsSelf }}, - .flags = EcsFilterMatchEmptyTables - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t1); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t2); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t3); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_rule_next(&it), true); - test_assert(it.table == t4); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_rule_next(&it), false); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_oneof_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_ENTITY(world, ObjA, (ChildOf, Parent)); - ECS_ENTITY(world, ObjB, (ChildOf, Parent)); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - test_assert( ecs_has_pair(world, e1, Rel, ObjA)); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - test_assert( ecs_has_pair(world, e2, Rel, ObjB)); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ - .id = ecs_pair(Rel, EcsWildcard) - }} - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_oneof_any(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_ENTITY(world, ObjA, (ChildOf, Parent)); - ECS_ENTITY(world, ObjB, (ChildOf, Parent)); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - test_assert( ecs_has_pair(world, e1, Rel, ObjA)); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - test_assert( ecs_has_pair(world, e2, Rel, ObjB)); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ - .id = ecs_pair(Rel, EcsAny) - }} - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, EcsWildcard), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, EcsWildcard), ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_instanced_w_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_rule_t *f = ecs_rule(world, { - .expr = "Position, Velocity($)", - .instanced = true - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_assert(ecs_rule_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 3); - test_int(it.entities[0], e1); - test_int(it.entities[1], e2); - test_int(it.entities[2], e3); - test_int(v->x, 1); - test_int(v->y, 2); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_int(p[2].x, 30); - test_int(p[2].y, 40); - } - - test_assert(ecs_rule_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e4); - test_int(it.entities[1], e5); - test_int(p[0].x, 40); - test_int(p[0].y, 50); - test_int(p[1].x, 50); - test_int(p[1].y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_instanced_w_base(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base_1 = ecs_set(world, 0, Velocity, {1, 2}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {80, 90}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_add_pair(world, e1, EcsIsA, base_1); - ecs_add_pair(world, e2, EcsIsA, base_1); - ecs_add_pair(world, e3, EcsIsA, base_1); - ecs_add_pair(world, e4, EcsIsA, base_1); - ecs_add_pair(world, e5, EcsIsA, base_1); - - ecs_entity_t e6 = ecs_set(world, 0, Position, {60, 70}); - ecs_entity_t e7 = ecs_set(world, 0, Position, {70, 80}); - ecs_set(world, e6, Velocity, {2, 3}); - ecs_set(world, e7, Velocity, {4, 5}); - - ecs_entity_t e8 = ecs_set(world, 0, Velocity, {6, 7}); - ecs_entity_t e9 = ecs_set(world, 0, Velocity, {8, 9}); - ecs_add_pair(world, e8, EcsIsA, base_2); - ecs_add_pair(world, e9, EcsIsA, base_2); - - ecs_rule_t *f = ecs_rule(world, { - .expr = "Position, Velocity", - .instanced = true - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), false); - test_bool(ecs_field_is_self(&it, 2), true); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e8); - test_int(it.entities[1], e9); - test_int(p->x, 80); - test_int(p->y, 90); - test_int(v[0].x, 6); - test_int(v[0].y, 7); - test_int(v[1].x, 8); - test_int(v[1].y, 9); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), false); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 3); - test_int(it.entities[0], e1); - test_int(it.entities[1], e2); - test_int(it.entities[2], e3); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_int(p[2].x, 30); - test_int(p[2].y, 40); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), false); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e4); - test_int(it.entities[1], e5); - test_int(p[0].x, 40); - test_int(p[0].y, 50); - test_int(p[1].x, 50); - test_int(p[1].y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), true); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e6); - test_int(p[0].x, 60); - test_int(p[0].y, 70); - test_int(v[0].x, 2); - test_int(v[0].y, 3); - - test_int(it.entities[1], e7); - test_int(p[1].x, 70); - test_int(p[1].y, 80); - test_int(v[1].x, 4); - test_int(v[1].y, 5); - } - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_not_instanced_w_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_rule_t *f = ecs_rule(world, { - .expr = "Position, Velocity($)" - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_assert(ecs_rule_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e1); - test_int(v->x, 1); - test_int(v->y, 2); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); - } - - test_assert(ecs_rule_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e2); - test_int(v->x, 1); - test_int(v->y, 2); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); - } - - test_assert(ecs_rule_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_int(v->x, 1); - test_int(v->y, 2); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); - } - - test_assert(ecs_rule_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_int(p[0].x, 40); - test_int(p[0].y, 50); - test_int(v->x, 1); - test_int(v->y, 2); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); - } - - test_assert(ecs_rule_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e5); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); - } - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_not_instanced_w_base(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base_1 = ecs_set(world, 0, Velocity, {1, 2}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {80, 90}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_add_pair(world, e1, EcsIsA, base_1); - ecs_add_pair(world, e2, EcsIsA, base_1); - ecs_add_pair(world, e3, EcsIsA, base_1); - ecs_add_pair(world, e4, EcsIsA, base_1); - ecs_add_pair(world, e5, EcsIsA, base_1); - - ecs_entity_t e6 = ecs_set(world, 0, Position, {60, 70}); - ecs_entity_t e7 = ecs_set(world, 0, Position, {70, 80}); - ecs_set(world, e6, Velocity, {2, 3}); - ecs_set(world, e7, Velocity, {4, 5}); - - ecs_entity_t e8 = ecs_set(world, 0, Velocity, {6, 7}); - ecs_entity_t e9 = ecs_set(world, 0, Velocity, {8, 9}); - ecs_add_pair(world, e8, EcsIsA, base_2); - ecs_add_pair(world, e9, EcsIsA, base_2); - - ecs_rule_t *f = ecs_rule(world, { - .expr = "Position, Velocity" - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), false); - test_bool(ecs_field_is_self(&it, 2), true); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e8); - test_int(p->x, 80); - test_int(p->y, 90); - test_int(v[0].x, 6); - test_int(v[0].y, 7); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), false); - test_bool(ecs_field_is_self(&it, 2), true); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e9); - test_int(p->x, 80); - test_int(p->y, 90); - test_int(v[0].x, 8); - test_int(v[0].y, 9); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), false); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_int(it.entities[0], e1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), false); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_int(it.entities[0], e2); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), false); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), false); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_int(p[0].x, 40); - test_int(p[0].y, 50); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), false); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e5); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_rule_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), true); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e6); - test_int(p[0].x, 60); - test_int(p[0].y, 70); - test_int(v[0].x, 2); - test_int(v[0].y, 3); - - test_int(it.entities[1], e7); - test_int(p[1].x, 70); - test_int(p[1].y, 80); - test_int(v[1].x, 4); - test_int(v[1].y, 5); - } - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesBasic_unknown_before_known(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, ChildOf($gc, $c), ChildOf($c, $this)" - }); - - test_assert(r != NULL); - - int c_var = ecs_rule_find_var(r, "c"); - test_assert(c_var != -1); - int gc_var = ecs_rule_find_var(r, "gc"); - test_assert(gc_var != -1); - - ecs_entity_t p = ecs_new(world, Foo); - ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); - /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t gc = ecs_new_w_pair(world, EcsChildOf, c); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); - test_uint(c, ecs_iter_get_var(&it, c_var)); - test_uint(gc, ecs_iter_get_var(&it, gc_var)); - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_unknown_before_known_after_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, ChildOf($gc, $c), Foo($gc) || Bar($gc), ChildOf($c, $this)" - }); - - test_assert(r != NULL); - - int c_var = ecs_rule_find_var(r, "c"); - test_assert(c_var != -1); - int gc_var = ecs_rule_find_var(r, "gc"); - test_assert(gc_var != -1); - - ecs_entity_t p = ecs_new(world, Foo); - ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); - /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t gc1 = ecs_new_w_pair(world, EcsChildOf, c); - ecs_add(world, gc1, Foo); - ecs_entity_t gc2 = ecs_new_w_pair(world, EcsChildOf, c); - ecs_add(world, gc2, Bar); - /* ecs_entity_t gc3 = */ ecs_new_w_pair(world, EcsChildOf, c); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 4)); - test_uint(c, ecs_iter_get_var(&it, c_var)); - test_uint(gc1, ecs_iter_get_var(&it, gc_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 4)); - test_uint(c, ecs_iter_get_var(&it, c_var)); - test_uint(gc2, ecs_iter_get_var(&it, gc_var)); - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_unknown_before_known_after_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, ChildOf($gc, $c), !Foo($gc), ChildOf($c, $this)" - }); - - test_assert(r != NULL); - - int c_var = ecs_rule_find_var(r, "c"); - test_assert(c_var != -1); - int gc_var = ecs_rule_find_var(r, "gc"); - test_assert(gc_var != -1); - - ecs_entity_t p = ecs_new(world, Foo); - ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); - /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t gc1 = ecs_new_w_pair(world, EcsChildOf, c); - ecs_entity_t gc2 = ecs_new_w_pair(world, EcsChildOf, c); - ecs_add(world, gc2, Foo); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 2)); - test_uint(Foo, ecs_field_id(&it, 3)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 4)); - test_uint(c, ecs_iter_get_var(&it, c_var)); - test_uint(gc1, ecs_iter_get_var(&it, gc_var)); - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_unknown_before_known_after_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, ChildOf($gc, $c), ?Foo($gc), ChildOf($c, $this)" - }); - - test_assert(r != NULL); - - int c_var = ecs_rule_find_var(r, "c"); - test_assert(c_var != -1); - int gc_var = ecs_rule_find_var(r, "gc"); - test_assert(gc_var != -1); - - ecs_entity_t p = ecs_new(world, Foo); - ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); - /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t gc1 = ecs_new_w_pair(world, EcsChildOf, c); - ecs_entity_t gc2 = ecs_new_w_pair(world, EcsChildOf, c); - ecs_add(world, gc2, Foo); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 2)); - test_uint(Foo, ecs_field_id(&it, 3)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 4)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(c, ecs_iter_get_var(&it, c_var)); - test_uint(gc1, ecs_iter_get_var(&it, gc_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 2)); - test_uint(Foo, ecs_field_id(&it, 3)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 4)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(c, ecs_iter_get_var(&it, c_var)); - test_uint(gc2, ecs_iter_get_var(&it, gc_var)); - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_unknown_before_known_after_scope(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, ChildOf($gc, $c), !{Foo($gc) || Bar($gc)}, ChildOf($c, $this)" - }); - - test_assert(r != NULL); - - int c_var = ecs_rule_find_var(r, "c"); - test_assert(c_var != -1); - int gc_var = ecs_rule_find_var(r, "gc"); - test_assert(gc_var != -1); - - ecs_entity_t p = ecs_new(world, Foo); - ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); - /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t gc1 = ecs_new_w_pair(world, EcsChildOf, c); - ecs_entity_t gc2 = ecs_new_w_pair(world, EcsChildOf, c); - ecs_add(world, gc2, Foo); - ecs_entity_t gc3 = ecs_new_w_pair(world, EcsChildOf, c); - ecs_add(world, gc3, Bar); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(c, ecs_iter_get_var(&it, c_var)); - test_uint(gc1, ecs_iter_get_var(&it, gc_var)); - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_reordered_plan_1(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, ChildOf($this, $p, $gp, $ggp), Bar($ggp)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] selfupid $[this] (Foo)" - LINE " 2. [ 1, 3] and $[this] (ChildOf, $p)" - LINE " 3. [ 2, 4] and $p (ChildOf, $gp)" - LINE " 4. [ 3, 5] and $gp (ChildOf, $ggp)" - LINE " 5. [ 4, 6] selfupid $ggp (Bar)" - LINE " 6. [ 5, 7] setvars " - LINE " 7. [ 6, 8] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_reordered_plan_2(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($ggp), ChildOf($this, $p, $gp, $ggp), Bar($this)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] selfupid $[ggp] (Foo)" - LINE " 2. [ 1, 3] each $ggp ($[ggp])" - LINE " 3. [ 2, 4] and $[gp] (ChildOf, $ggp)" - LINE " 4. [ 3, 5] each $gp ($[gp])" - LINE " 5. [ 4, 6] and $[p] (ChildOf, $gp)" - LINE " 6. [ 5, 7] each $p ($[p])" - LINE " 7. [ 6, 8] and $[this] (ChildOf, $p)" - LINE " 8. [ 7, 9] selfupid $[this] (Bar)" - LINE " 9. [ 8, 10] setvars " - LINE "10. [ 9, 11] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_trivial_plan(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] andid $[this] (Foo)" - LINE " 2. [ 1, 3] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_trivial_plan(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), Bar(self)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] triv " - LINE " 2. [ 1, 3] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_trivial_plan_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] andid $[this] (Position)" - LINE " 2. [ 1, 3] popself " - LINE " 3. [ 2, 4] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_trivial_plan_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), Velocity(self)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] trivpop " - LINE " 2. [ 1, 3] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_3_trivial_plan_w_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_new_entity(world, "p"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), Bar(self), ChildOf(self, p)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] triv " - LINE " 2. [ 1, 3] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_3_trivial_plan_w_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_new_entity(world, "p"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), Bar(self), ChildOf(self, *)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] trivwc " - LINE " 2. [ 1, 3] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_3_trivial_plan_w_any(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_new_entity(world, "p"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), Bar(self), ChildOf(self, _)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] triv " - LINE " 2. [ 1, 3] andany $[this] (ChildOf, $_'1)" - LINE " 3. [ 2, 4] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_3_trivial_plan_w_pair_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_new_entity(world, "p"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), Velocity(self), ChildOf(self, p)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] trivpop " - LINE " 2. [ 1, 3] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_3_trivial_plan_w_wildcard_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_new_entity(world, "p"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), Velocity(self), ChildOf(self, *)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] trivwc " - LINE " 2. [ 1, 3] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_3_trivial_plan_w_any_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_new_entity(world, "p"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), Velocity(self), ChildOf(self, _)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] trivpop " - LINE " 2. [ 1, 3] andany $[this] (ChildOf, $_'1)" - LINE " 3. [ 2, 4] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_1_trivial_component_w_none(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_new_entity(world, "p"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "[none] Position(self)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] andid $[this] (Position)" - LINE " 2. [ 1, 3] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBasic_2_trivial_component_w_none(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_new_entity(world, "p"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "[none] Position(self), [none] Velocity(self)" - }); - - ecs_log_enable_colors(false); - - const char *expect = - HEAD " 0. [-1, 1] setids " - LINE " 1. [ 0, 2] triv " - LINE " 2. [ 1, 3] yield " - LINE ""; - char *plan = ecs_rule_str(r); - - test_str(expect, plan); - ecs_os_free(plan); - - ecs_rule_fini(r); - - ecs_fini(world); -} - diff --git a/vendors/flecs/test/addons/src/RulesBuiltinPredicates.c b/vendors/flecs/test/addons/src/RulesBuiltinPredicates.c deleted file mode 100644 index 0eae7b1cc..000000000 --- a/vendors/flecs/test/addons/src/RulesBuiltinPredicates.c +++ /dev/null @@ -1,2865 +0,0 @@ -#include - -void RulesBuiltinPredicates_this_eq_id(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this == ent" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_eq_name(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this == \"ent\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_eq_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_log_set_level(-4); - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this == $x" - }); - - test_assert(r == NULL); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_eq_id_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this == ent_2" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_eq_id_written_no_match(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB($this), $this == ent_2" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_eq_name_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this == \"ent_2\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_eq_name_written_no_match(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB($this), $this == \"ent_2\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_eq_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add_pair(world, ent_1, RelA, ent_2); - ecs_add_pair(world, ent_2, RelA, ent_2); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add_pair(world, ent_3, RelA, ent_2); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $x), $this == $x" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - test_uint(ent_2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_id(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x == ent" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_name(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x == \"ent\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_log_set_level(-4); - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x == $y" - }); - - test_assert(r == NULL); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_id_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x == ent_2" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_id_written_no_match(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB($x), $x == ent_2" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_name_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x == \"ent_2\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_name_written_no_match(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB($x), $x == \"ent_2\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add_pair(world, ent_1, RelA, ent_2); - ecs_add_pair(world, ent_2, RelA, ent_2); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add_pair(world, ent_3, RelA, ent_2); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x, $y), $x == $y" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - test_uint(ent_2, ecs_iter_get_var(&it, y_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_neq_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this != ent_2" - }); - - test_assert(r != NULL); - - bool ent_1_found = false; - bool ent_3_found = false; - bool ent_4_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != ent_2); - if (it.entities[i] == ent_1) { - ent_1_found = true; - } - if (it.entities[i] == ent_3) { - ent_3_found = true; - } - if (it.entities[i] == ent_4) { - ent_4_found = true; - } - } - } - - test_bool(ent_1_found, true); - test_bool(ent_3_found, true); - test_bool(ent_4_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_neq_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this != \"ent_2\"" - }); - - test_assert(r != NULL); - - bool ent_1_found = false; - bool ent_3_found = false; - bool ent_4_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != ent_2); - if (it.entities[i] == ent_1) { - ent_1_found = true; - } - if (it.entities[i] == ent_3) { - ent_3_found = true; - } - if (it.entities[i] == ent_4) { - ent_4_found = true; - } - } - } - - test_bool(ent_1_found, true); - test_bool(ent_3_found, true); - test_bool(ent_4_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_neq_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_log_set_level(-4); - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this != $x" - }); - - test_assert(r == NULL); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_neq_id_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this != ent_2" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_neq_id_written_no_match(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB($this), $this != ent_2" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_neq_name_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this != \"ent_2\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_neq_name_written_no_match(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB($this), $this != \"ent_2\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_neq_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add_pair(world, ent_1, RelA, ent_2); - ecs_add_pair(world, ent_2, RelA, ent_2); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add_pair(world, ent_3, RelA, ent_2); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $x), $this != $x" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - test_uint(ent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_neq_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x != ent_2" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - bool ent_1_found = false; - bool ent_3_found = false; - bool ent_4_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_uint(it.count, 0); - ecs_entity_t e = ecs_iter_get_var(&it, x_var); - test_assert(e != ent_2); - if (e == ent_1) { - ent_1_found = true; - } - if (e == ent_3) { - ent_3_found = true; - } - if (e == ent_4) { - ent_4_found = true; - } - } - - test_bool(ent_1_found, true); - test_bool(ent_3_found, true); - test_bool(ent_4_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_neq_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x != \"ent_2\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - bool ent_1_found = false; - bool ent_3_found = false; - bool ent_4_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_uint(it.count, 0); - ecs_entity_t e = ecs_iter_get_var(&it, x_var); - test_assert(e != ent_2); - if (e == ent_1) { - ent_1_found = true; - } - if (e == ent_3) { - ent_3_found = true; - } - if (e == ent_4) { - ent_4_found = true; - } - } - - test_bool(ent_1_found, true); - test_bool(ent_3_found, true); - test_bool(ent_4_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_neq_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_log_set_level(-4); - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x != $y" - }); - - test_assert(r == NULL); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_neq_id_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x != ent_2" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_neq_id_written_no_match(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB($x), $x != ent_2" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_4, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_neq_name_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x != \"ent_2\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_neq_name_written_no_match(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB($x), $x != \"ent_2\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_4, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_neq_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add_pair(world, ent_1, RelA, ent_2); - ecs_add_pair(world, ent_2, RelA, ent_2); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add_pair(world, ent_3, RelA, ent_2); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x, $y), $x != $y" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, ecs_iter_get_var(&it, x_var)); - test_uint(ent_2, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, ecs_iter_get_var(&it, x_var)); - test_uint(ent_2, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_2_neq_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this != ent_2, $this != ent_3" - }); - - test_assert(r != NULL); - - bool ent_1_found = false; - bool ent_4_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != ent_2); - test_assert(it.entities[i] != ent_3); - if (it.entities[i] == ent_1) { - ent_1_found = true; - } - if (it.entities[i] == ent_4) { - ent_4_found = true; - } - if (it.entities[i] == ent_5) { - ent_5_found = true; - } - } - } - - test_bool(ent_1_found, true); - test_bool(ent_4_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_2_neq_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this != \"ent_2\", $this != \"ent_3\"" - }); - - test_assert(r != NULL); - - bool ent_1_found = false; - bool ent_4_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != ent_2); - test_assert(it.entities[i] != ent_3); - if (it.entities[i] == ent_1) { - ent_1_found = true; - } - if (it.entities[i] == ent_4) { - ent_4_found = true; - } - if (it.entities[i] == ent_5) { - ent_5_found = true; - } - } - } - - test_bool(ent_1_found, true); - test_bool(ent_4_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_2_neq_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x != ent_2, $x != ent_3" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - bool ent_1_found = false; - bool ent_4_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_uint(it.count, 0); - ecs_entity_t e = ecs_iter_get_var(&it, x_var); - test_assert(e != ent_2); - test_assert(e != ent_3); - if (e == ent_1) { - ent_1_found = true; - } - if (e == ent_4) { - ent_4_found = true; - } - if (e == ent_5) { - ent_5_found = true; - } - } - - test_bool(ent_1_found, true); - test_bool(ent_4_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_2_neq_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x != \"ent_2\", $x != \"ent_3\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - bool ent_1_found = false; - bool ent_4_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_uint(it.count, 0); - ecs_entity_t e = ecs_iter_get_var(&it, x_var); - test_assert(e != ent_2); - test_assert(e != ent_3); - if (e == ent_1) { - ent_1_found = true; - } - if (e == ent_4) { - ent_4_found = true; - } - if (e == ent_5) { - ent_5_found = true; - } - } - - test_bool(ent_1_found, true); - test_bool(ent_4_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_2_neq_id_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this != ent_2, $this != ent_3" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_4, it.entities[0]); - test_uint(ent_5, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_2_neq_name_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this != \"ent_2\", $this != \"ent_3\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_4, it.entities[0]); - test_uint(ent_5, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_2_neq_id_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x != ent_2, $x != ent_3" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_2_neq_name_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x != \"ent_2\", $x != \"ent_3\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_2_or_id(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - /* ecs_entity_t ent_1 = */ ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - /* ecs_entity_t ent_4 = */ ecs_new_entity(world, "ent_4"); - /* ecs_entity_t ent_5 = */ ecs_new_entity(world, "ent_5"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this == ent_2 || $this == ent_3" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_2_or_name(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - /* ecs_entity_t ent_1 = */ ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - /* ecs_entity_t ent_4 = */ ecs_new_entity(world, "ent_4"); - /* ecs_entity_t ent_5 = */ ecs_new_entity(world, "ent_5"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this == \"ent_2\" || $this == \"ent_3\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_2_or_id(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - /* ecs_entity_t ent_1 = */ ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - /* ecs_entity_t ent_4 = */ ecs_new_entity(world, "ent_4"); - /* ecs_entity_t ent_5 = */ ecs_new_entity(world, "ent_5"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x == ent_2 || $x == ent_3" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_2_or_name(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - /* ecs_entity_t ent_1 = */ ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - /* ecs_entity_t ent_4 = */ ecs_new_entity(world, "ent_4"); - /* ecs_entity_t ent_5 = */ ecs_new_entity(world, "ent_5"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x == \"ent_2\" || $x == \"ent_3\"" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_2_or_id_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this == ent_2 || $this == ent_3" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_2_or_name_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this == \"ent_2\" || $this == \"ent_3\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_2_or_id_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x == ent_2 || $x == ent_3" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_2_or_name_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent_2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "ent_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent_5"); - ecs_add(world, ent_5, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x == \"ent_2\" || $x == \"ent_3\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_eq(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - /* ecs_entity_t ent_2 = */ ecs_new_entity(world, "ent2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, Tag); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this ~= \"nt_\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_3, it.entities[0]); - test_uint(ent_4, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_match_eq(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - /* ecs_entity_t ent_2 = */ ecs_new_entity(world, "ent2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, Tag); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x ~= \"nt_\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_6, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_eq_written(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, RelA); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, RelA); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this ~= \"nt_\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, it.entities[0]); - test_uint(ent_4, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_match_eq_written(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, RelA); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, RelA); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x ~= \"nt_\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_6, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_neq(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, Tag); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this ~= \"!nt_\"" - }); - - test_assert(r != NULL); - - bool ent_2_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != ent_1); - test_assert(it.entities[i] != ent_3); - test_assert(it.entities[i] != ent_4); - test_assert(it.entities[i] != ent_6); - if (it.entities[i] == ent_2) { - ent_2_found = true; - } - if (it.entities[i] == ent_5) { - ent_5_found = true; - } - } - } - - test_bool(ent_2_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_match_neq(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, Tag); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x ~= \"!nt_\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - bool ent_2_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_uint(it.count, 0); - ecs_entity_t e = ecs_iter_get_var(&it, x_var); - test_assert(e != ent_1); - test_assert(e != ent_3); - test_assert(e != ent_4); - test_assert(e != ent_6); - if (e == ent_2) { - ent_2_found = true; - } - if (e == ent_5) { - ent_5_found = true; - } - } - - test_bool(ent_2_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_neq_written(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, RelA); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, RelA); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this ~= \"!nt_\"" - }); - - test_assert(r != NULL); - - bool ent_2_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != ent_1); - test_assert(it.entities[i] != ent_3); - test_assert(it.entities[i] != ent_4); - test_assert(it.entities[i] != ent_6); - if (it.entities[i] == ent_2) { - ent_2_found = true; - } - if (it.entities[i] == ent_5) { - ent_5_found = true; - } - } - } - - test_bool(ent_2_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_match_neq_written(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, RelA); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, RelA); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x ~= \"!nt_\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - bool ent_2_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_uint(it.count, 0); - ecs_entity_t e = ecs_iter_get_var(&it, x_var); - test_assert(e != ent_1); - test_assert(e != ent_3); - test_assert(e != ent_4); - test_assert(e != ent_6); - if (e == ent_2) { - ent_2_found = true; - } - if (e == ent_5) { - ent_5_found = true; - } - } - - test_bool(ent_2_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_2_neq(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, Tag); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this ~= \"!nt_\", $this ~= \"!_3\"" - }); - - test_assert(r != NULL); - - bool ent_2_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != ent_1); - test_assert(it.entities[i] != ent_3); - test_assert(it.entities[i] != ent_4); - test_assert(it.entities[i] != ent_6); - if (it.entities[i] == ent_2) { - ent_2_found = true; - } - if (it.entities[i] == ent_5) { - ent_5_found = true; - } - } - } - - test_bool(ent_2_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_match_2_neq(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, Tag); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x ~= \"!nt_\", $x ~= \"!_3\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - bool ent_2_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - ecs_entity_t e = ecs_iter_get_var(&it, x_var); - test_assert(e != ent_1); - test_assert(e != ent_3); - test_assert(e != ent_4); - test_assert(e != ent_6); - if (e == ent_2) { - ent_2_found = true; - } - if (e == ent_5) { - ent_5_found = true; - } - } - - test_bool(ent_2_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_2_neq_written(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, RelA); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, RelA); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this ~= \"!nt_\", $this ~= \"!_3\"" - }); - - test_assert(r != NULL); - - bool ent_2_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != ent_1); - test_assert(it.entities[i] != ent_3); - test_assert(it.entities[i] != ent_4); - test_assert(it.entities[i] != ent_6); - if (it.entities[i] == ent_2) { - ent_2_found = true; - } - if (it.entities[i] == ent_5) { - ent_5_found = true; - } - } - } - - test_bool(ent_2_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_match_2_neq_written(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, RelA); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, RelA); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x ~= \"!nt_\", $x ~= \"!_3\"" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - bool ent_2_found = false; - bool ent_5_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - ecs_entity_t e = ecs_iter_get_var(&it, x_var); - test_assert(e != ent_1); - test_assert(e != ent_3); - test_assert(e != ent_4); - test_assert(e != ent_6); - if (e == ent_2) { - ent_2_found = true; - } - if (e == ent_5) { - ent_5_found = true; - } - } - - test_bool(ent_2_found, true); - test_bool(ent_5_found, true); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_2_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - /* ecs_entity_t ent_2 = */ ecs_new_entity(world, "ent2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - /* ecs_entity_t ent_4 = */ ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, Tag); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this ~= \"1\" || $this ~= \"_3\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_2_or_written(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, RelA); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, RelA); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this ~= \"1\" || $this ~= \"_3\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_3_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - /* ecs_entity_t ent_2 = */ ecs_new_entity(world, "ent2"); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - /* ecs_entity_t ent_4 = */ ecs_new_entity(world, "ent_4"); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, Tag); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this ~= \"1\" || $this ~= \"_3\" || $this ~= \"nt_6\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent_6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_this_match_3_or_written(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - - ecs_entity_t ent_1 = ecs_new_entity(world, "ent_1"); - ecs_add(world, ent_1, RelA); - ecs_entity_t ent_2 = ecs_new_entity(world, "ent2"); - ecs_add(world, ent_2, RelA); - ecs_entity_t ent_3 = ecs_new_entity(world, "nt_3"); - ecs_add(world, ent_3, RelA); - ecs_entity_t ent_4 = ecs_new_entity(world, "ent_4"); - ecs_add(world, ent_4, RelA); - ecs_entity_t ent_5 = ecs_new_entity(world, "ent5"); - ecs_add(world, ent_5, RelA); - ecs_entity_t ent_6 = ecs_new_entity(world, "ent_6"); - ecs_add(world, ent_6, RelA); - ecs_add(world, ent_6, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $this ~= \"1\" || $this ~= \"_3\" || $this ~= \"nt_6\"" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(true, ecs_field_is_set(&it, 1)); - test_uint(false, ecs_field_is_set(&it, 2)); - test_uint(ent_6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_unresolved_by_name(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this == ent", - .flags = EcsFilterUnresolvedByName - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(false, ecs_field_is_set(&it, 1)); - test_uint(ent, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x == *, (Rel, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e, it.entities[0]); - test_uint(tgt, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x == _, (Rel, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e, it.entities[0]); - test_uint(tgt, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_wildcard_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Rel, $x), $x == *" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e, it.entities[0]); - test_uint(tgt, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_any_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Rel, $x), $x == _" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e, it.entities[0]); - test_uint(tgt, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesBuiltinPredicates_var_eq_after_var_0_src(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(), $x == flecs" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(EcsFlecs, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/RulesComponentInheritance.c b/vendors/flecs/test/addons/src/RulesComponentInheritance.c deleted file mode 100644 index 8869e0ffc..000000000 --- a/vendors/flecs/test/addons/src/RulesComponentInheritance.c +++ /dev/null @@ -1,3656 +0,0 @@ -#include - -ECS_TAG_DECLARE(Unit); -ECS_TAG_DECLARE(MeleeUnit); -ECS_TAG_DECLARE(RangedUnit); -ECS_TAG_DECLARE(Warrior); -ECS_TAG_DECLARE(Archer); -ECS_TAG_DECLARE(Wizard); -ECS_TAG_DECLARE(Warlock); - -static -void populate_facts(ecs_world_t *world) { - ECS_TAG_DEFINE(world, Unit); - ECS_ENTITY_DEFINE(world, MeleeUnit, (IsA, Unit)); - ECS_ENTITY_DEFINE(world, RangedUnit, (IsA, Unit)); - ECS_ENTITY_DEFINE(world, Warrior, (IsA, MeleeUnit)); - ECS_ENTITY_DEFINE(world, Archer, (IsA, RangedUnit)); - ECS_ENTITY_DEFINE(world, Wizard, (IsA, RangedUnit)); - ECS_ENTITY_DEFINE(world, Warlock, (IsA, Wizard), (IsA, Warrior)); -} - -void RulesComponentInheritance_1_ent_0_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warlock(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_ent_1_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_ent_2_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "MeleeUnit(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_ent_3_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Unit(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); /* not ideal, diamond problem */ - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - - -void RulesComponentInheritance_1_this_0_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - ecs_entity_t e2 = ecs_new(world, Wizard); - ecs_entity_t e3 = ecs_new(world, Warrior); - ecs_entity_t e4 = ecs_new(world, Archer); - ecs_entity_t e5 = ecs_new(world, MeleeUnit); - ecs_entity_t e6 = ecs_new(world, RangedUnit); - ecs_entity_t e7 = ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Unit($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Unit, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e7, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RangedUnit, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e6, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_1_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - /* ecs_entity_t e2 = */ ecs_new(world, Wizard); - ecs_entity_t e3 = ecs_new(world, Warrior); - /* ecs_entity_t e4 = */ ecs_new(world, Archer); - ecs_entity_t e5 = ecs_new(world, MeleeUnit); - /* ecs_entity_t e6 = */ ecs_new(world, RangedUnit); - /* ecs_entity_t e7 = */ ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "MeleeUnit($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_2_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - /* ecs_entity_t e2 = */ ecs_new(world, Wizard); - ecs_entity_t e3 = ecs_new(world, Warrior); - /* ecs_entity_t e4 = */ ecs_new(world, Archer); - /* ecs_entity_t e5 = */ ecs_new(world, MeleeUnit); - /* ecs_entity_t e6 = */ ecs_new(world, RangedUnit); - /* ecs_entity_t e7 = */ ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_3_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - /* ecs_entity_t e2 = */ ecs_new(world, Wizard); - /* ecs_entity_t e3 = */ ecs_new(world, Warrior); - /* ecs_entity_t e4 = */ ecs_new(world, Archer); - /* ecs_entity_t e5 = */ ecs_new(world, MeleeUnit); - /* ecs_entity_t e6 = */ ecs_new(world, RangedUnit); - /* ecs_entity_t e7 = */ ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warlock($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_0_lvl_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this), Unit($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RangedUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Unit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e7, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_1_lvl_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this), MeleeUnit($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_2_lvl_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this), Warrior($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_3_lvl_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this), Warlock($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_0_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - ecs_entity_t e2 = ecs_new(world, Wizard); - ecs_entity_t e3 = ecs_new(world, Warrior); - ecs_entity_t e4 = ecs_new(world, Archer); - ecs_entity_t e5 = ecs_new(world, MeleeUnit); - ecs_entity_t e6 = ecs_new(world, RangedUnit); - ecs_entity_t e7 = ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Unit($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Unit, ecs_field_id(&it, 1)); - test_uint(e7, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(e5, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RangedUnit, ecs_field_id(&it, 1)); - test_uint(e6, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(e3, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(e4, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_1_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - /* ecs_entity_t e2 = */ ecs_new(world, Wizard); - ecs_entity_t e3 = ecs_new(world, Warrior); - /* ecs_entity_t e4 = */ ecs_new(world, Archer); - ecs_entity_t e5 = ecs_new(world, MeleeUnit); - /* ecs_entity_t e6 = */ ecs_new(world, RangedUnit); - /* ecs_entity_t e7 = */ ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "MeleeUnit($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(e5, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(e3, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_2_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - /* ecs_entity_t e2 = */ ecs_new(world, Wizard); - ecs_entity_t e3 = ecs_new(world, Warrior); - /* ecs_entity_t e4 = */ ecs_new(world, Archer); - /* ecs_entity_t e5 = */ ecs_new(world, MeleeUnit); - /* ecs_entity_t e6 = */ ecs_new(world, RangedUnit); - /* ecs_entity_t e7 = */ ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_3_lvl(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - /* ecs_entity_t e2 = */ ecs_new(world, Wizard); - /* ecs_entity_t e3 = */ ecs_new(world, Warrior); - /* ecs_entity_t e4 = */ ecs_new(world, Archer); - /* ecs_entity_t e5 = */ ecs_new(world, MeleeUnit); - /* ecs_entity_t e6 = */ ecs_new(world, RangedUnit); - /* ecs_entity_t e7 = */ ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warlock($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_0_lvl_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x), Unit($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(e4, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RangedUnit, ecs_field_id(&it, 2)); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(e6, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Unit, ecs_field_id(&it, 2)); - test_uint(e7, ecs_field_src(&it, 1)); - test_uint(e7, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_1_lvl_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x), MeleeUnit($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_2_lvl_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x), Warrior($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_3_lvl_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x), Warlock($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_ent_1_lvl_self(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior(e1:self)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_1_lvl_self(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - /* ecs_entity_t e2 = */ ecs_new(world, Wizard); - ecs_entity_t e3 = ecs_new(world, Warrior); - /* ecs_entity_t e4 = */ ecs_new(world, Archer); - ecs_entity_t e5 = ecs_new(world, MeleeUnit); - /* ecs_entity_t e6 = */ ecs_new(world, RangedUnit); - /* ecs_entity_t e7 = */ ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "MeleeUnit(self)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_1_lvl_written_self(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), MeleeUnit(self)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_1_lvl_self(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Warlock); - /* ecs_entity_t e2 = */ ecs_new(world, Wizard); - ecs_entity_t e3 = ecs_new(world, Warrior); - /* ecs_entity_t e4 = */ ecs_new(world, Archer); - ecs_entity_t e5 = ecs_new(world, MeleeUnit); - /* ecs_entity_t e6 = */ ecs_new(world, RangedUnit); - /* ecs_entity_t e7 = */ ecs_new(world, Unit); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "MeleeUnit($x:self)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(e5, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(e3, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_1_lvl_written_self(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Warlock); - ecs_add(world, e2, Wizard); - ecs_add(world, e3, Warrior); - ecs_add(world, e4, Archer); - ecs_add(world, e5, MeleeUnit); - ecs_add(world, e6, RangedUnit); - ecs_add(world, e7, Unit); - - /* entities that don't match query */ - ecs_new(world, Unit); - ecs_new(world, MeleeUnit); - ecs_new(world, Warrior); - ecs_new(world, Warlock); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x), MeleeUnit($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesComponentInheritance_1_ent_src_not(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warrior); - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Unit(e1)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!MeleeUnit(e1)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Warrior(e1)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Warlock(e1)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!RangedUnit(e1)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RangedUnit, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Archer(e1)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Wizard(e1)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_src_not(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Unit); - ecs_add(world, e2, MeleeUnit); - ecs_add(world, e3, RangedUnit); - ecs_add(world, e4, Warrior); - ecs_add(world, e5, Archer); - ecs_add(world, e6, Wizard); - ecs_add(world, e7, Warlock); - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Unit($this), Tag($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!MeleeUnit($this), Tag($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Warrior($this), Tag($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Warlock($this), Tag($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!RangedUnit($this), Tag($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RangedUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RangedUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RangedUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Archer($this), Tag($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e7, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Wizard($this), Tag($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_src_not(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Unit); - ecs_add(world, e2, MeleeUnit); - ecs_add(world, e3, RangedUnit); - ecs_add(world, e4, Warrior); - ecs_add(world, e5, Archer); - ecs_add(world, e6, Wizard); - ecs_add(world, e7, Warlock); - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Unit($x), Tag($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!MeleeUnit($x), Tag($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(MeleeUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(e6, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Warrior($x), Tag($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(e6, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Warlock($x), Tag($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(e4, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(e6, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!RangedUnit($x), Tag($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RangedUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RangedUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RangedUnit, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(e4, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Archer($x), Tag($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(e4, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(e6, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Archer, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e7, ecs_field_src(&it, 1)); - test_uint(e7, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "!Wizard($x), Tag($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(e4, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Wizard, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesComponentInheritance_1_this_src_not_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Unit); - ecs_add(world, e2, MeleeUnit); - ecs_add(world, e3, RangedUnit); - ecs_add(world, e4, Warrior); - ecs_add(world, e5, Archer); - ecs_add(world, e6, Wizard); - ecs_add(world, e7, Warlock); - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($this), !Unit($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($this), !MeleeUnit($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($this), !Warrior($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($this), !Warlock($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($this), !RangedUnit($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RangedUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RangedUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RangedUnit, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($this), !Archer($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e6, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e7, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($this), !Wizard($this)" }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e5, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesComponentInheritance_1_var_src_not_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - - ecs_add(world, e1, Unit); - ecs_add(world, e2, MeleeUnit); - ecs_add(world, e3, RangedUnit); - ecs_add(world, e4, Warrior); - ecs_add(world, e5, Archer); - ecs_add(world, e6, Wizard); - ecs_add(world, e7, Warlock); - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($x), !Unit($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($x), !MeleeUnit($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(MeleeUnit, ecs_field_id(&it, 2)); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(e6, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($x), !Warrior($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warrior, ecs_field_id(&it, 2)); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(e6, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($x), !Warlock($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(e4, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Warlock, ecs_field_id(&it, 2)); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(e6, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($x), !RangedUnit($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RangedUnit, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RangedUnit, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RangedUnit, ecs_field_id(&it, 2)); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(e4, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($x), !Archer($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(e4, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(e6, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Archer, ecs_field_id(&it, 2)); - test_uint(e7, ecs_field_src(&it, 1)); - test_uint(e7, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { .expr = "Tag($x), !Wizard($x)" }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(e4, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Wizard, ecs_field_id(&it, 2)); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesComponentInheritance_first_self(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_add(world, e2, Warrior); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:self:(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warlock:self:(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:self:(e2)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesComponentInheritance_first_down(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_add(world, e2, Warrior); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:down:(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warlock:down:(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:down:(e2)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesComponentInheritance_first_self_down(void) { - ecs_world_t *world = ecs_init(); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_add(world, e2, Warrior); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:self|down:(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warlock:self|down:(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:self|down:(e2)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesComponentInheritance_first_rel_self(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Rel, Traversable); - ECS_TAG(world, Warrior); - ECS_ENTITY(world, Warlock, (Rel, Warrior)); - - populate_facts(world); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - - ecs_log_set_level(-4); - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:self(Rel):(e1)" - }); - - test_assert(r == NULL); - - ecs_fini(world); -} - -void RulesComponentInheritance_first_rel_down(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Rel, Traversable); - ECS_TAG(world, Warrior); - ECS_ENTITY(world, Warlock, (Rel, Warrior)); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_add(world, e2, Warrior); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:down(Rel):(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warlock:down(Rel):(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:down(Rel):(e2)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesComponentInheritance_first_rel_self_down(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Rel, Traversable); - ECS_TAG(world, Warrior); - ECS_ENTITY(world, Warlock, (Rel, Warrior)); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, Warlock); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_add(world, e2, Warrior); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:self|down(Rel):(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warlock:self|down(Rel):(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warlock, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "Warrior:self|down(Rel):(e2)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Warrior, ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/RulesOperators.c b/vendors/flecs/test/addons/src/RulesOperators.c deleted file mode 100644 index 93bd8ddbe..000000000 --- a/vendors/flecs/test/addons/src/RulesOperators.c +++ /dev/null @@ -1,6543 +0,0 @@ -#include - -void RulesOperators_2_and_not(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !RelB($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add(world, e1, RelB); - ecs_add(world, e2, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, e1, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_not_not(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !RelB($this), !RelC($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add(world, e1, RelB); - ecs_add(world, e2, RelB); - ecs_add(world, e1, RelC); - ecs_add(world, e2, RelC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, e1, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, e2, RelC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, e1, RelC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_rel_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !*($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_tgt_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !RelA($this, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add_pair(world, e2, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_rel_tgt_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !*($this, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add_pair(world, e2, RelA, TgtB); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !$x($this, TgtA)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !RelA($this, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add_pair(world, e2, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_rel_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !$x($this, $y)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int32_t y_var = ecs_rule_find_var(r, "x"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_rel_tgt_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !$x($this, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add_pair(world, e1, RelA, RelA); - ecs_add_pair(world, e2, RelA, RelA); - ecs_add_pair(world, e1, RelB, RelB); - ecs_add_pair(world, e2, RelB, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelB, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_rel_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), !$x($this, TgtA)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelB); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e2, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_tgt_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), !RelA($this, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e2, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_rel_tgt_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), !$x($this, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); - ecs_add_pair(world, e1, TgtA, TgtA); - ecs_add_pair(world, e1, TgtB, TgtB); - ecs_add_pair(world, e2, TgtA, TgtA); - ecs_add_pair(world, e2, TgtB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, TgtA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e2, TgtB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_rel_tgt_same_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), !$x($this, $this)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); - ecs_add_pair(world, e1, TgtA, e1); - ecs_add_pair(world, e1, TgtB, e1); - ecs_add_pair(world, e2, TgtA, e2); - ecs_add_pair(world, e2, TgtB, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, TgtA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, e1), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e2, TgtB, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, e1), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtB, e2), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - - -void RulesOperators_2_and_not_pair_rel_src_tgt_same_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), !$x($x, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add_pair(world, e1, e1, e1); - ecs_add_pair(world, e2, e2, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e2, e2, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_any_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !_($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_any_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), !RelA($this, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_not_pair_any_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), !RelA(_, $x)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); - ecs_new_w_pair(world, Tag, TgtB); - ecs_new_w_pair(world, Tag, TgtC); - - ecs_entity_t e4 = ecs_new_w_pair(world, RelA, TgtB); - ecs_add_pair(world, e4, RelA, TgtC); - ecs_new_w_pair(world, RelA, TgtC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelB($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add(world, e2, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_optional(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelB($this), ?RelC($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_add(world, e2, RelB); - ecs_add(world, e3, RelC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_rel_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?*($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_tgt_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?$x($this, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_rel_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?$x($this, $y)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtB, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_rel_tgt_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?$x($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - - ecs_add_pair(world, e2, TgtA, TgtA); - ecs_add_pair(world, e3, TgtA, TgtA); - ecs_add_pair(world, e3, RelA, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, RelA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_rel_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), ?$x($this, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelA); - ecs_entity_t e3 = ecs_new_w_pair(world, Tag, RelA); - ecs_add_pair(world, e3, Tag, RelB); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_tgt_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), ?RelA($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtA); - ecs_add_pair(world, e3, Tag, TgtB); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtB); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_rel_tgt_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), ?$x($this, $y)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelA); - ecs_entity_t e3 = ecs_new_w_pair(world, Tag, RelA); - ecs_add_pair(world, e3, Tag, RelB); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtB, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_rel_tgt_same_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), ?$x($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtA); - ecs_add_pair(world, e3, Tag, RelA); - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - - ecs_add_pair(world, e2, TgtA, TgtA); - ecs_add_pair(world, e3, TgtA, TgtA); - ecs_add_pair(world, e3, RelA, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, RelA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_rel_src_tgt_same_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), ?$x($x, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_add_pair(world, e2, e2, e2); - ecs_add_pair(world, e3, e3, e3); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e3, e3), ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_optional_pair_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelB($this, $x), ?RelC($this, $y)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int32_t y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_add_pair(world, e2, RelB, TgtA); - ecs_add_pair(world, e3, RelC, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelC, EcsWildcard), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelC, EcsWildcard), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelC, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(TgtB, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_any_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?_($this, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_any_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_and_optional_pair_any_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), ?RelA(_, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtC); - - ecs_entity_t e4 = ecs_new_w_pair(world, RelA, TgtB); - ecs_add_pair(world, e4, RelA, TgtC); - ecs_new_w_pair(world, RelA, TgtC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtC), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_and_pair_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?$x($this, TgtA), $x($this, TgtB)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_entity_t e4 = ecs_new(world, RelA); - - ecs_add_pair(world, e1, RelA, TgtA); - - ecs_add_pair(world, e2, RelA, TgtB); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - ecs_add_pair(world, e3, RelB, TgtB); - - ecs_add_pair(world, e4, RelA, TgtA); - ecs_add_pair(world, e4, RelA, TgtB); - ecs_add_pair(world, e4, RelB, TgtA); - ecs_add_pair(world, e4, RelB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_and_pair_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, $x), RelB($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_entity_t e4 = ecs_new(world, RelA); - - ecs_add_pair(world, e1, RelA, TgtA); - - ecs_add_pair(world, e2, RelB, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - ecs_add_pair(world, e3, RelB, TgtB); - - ecs_add_pair(world, e4, RelA, TgtA); - ecs_add_pair(world, e4, RelA, TgtB); - ecs_add_pair(world, e4, RelB, TgtA); - ecs_add_pair(world, e4, RelB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_and_pair_rel_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?$x($this, $y), $y($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_add_pair(world, e2, RelA, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, TgtA, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(TgtA, RelA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, RelA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(RelA, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_and_pair_rel_tgt_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, $x), $x($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_add_pair(world, e2, RelA, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, TgtA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_and_pair_rel_tgt_same_other_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, $x), $y($x, $y)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_w_pair(world, TgtA, TgtA); - ecs_entity_t t3 = ecs_new_w_pair(world, TgtB, TgtB); - ecs_add_pair(world, t3, TgtC, TgtC); - - ecs_add_pair(world, e2, RelA, t1); - ecs_add_pair(world, e3, RelA, t2); - ecs_add_pair(world, e3, RelA, t3); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(EcsWildcard, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t2), ecs_field_id(&it, 2)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t2, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(t2, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t3), ecs_field_id(&it, 2)); - test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t3, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(t3, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t3), ecs_field_id(&it, 2)); - test_uint(ecs_pair(TgtC, TgtC), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t3, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(t3, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_and_pair_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, $x), Tag($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new(world, Tag); - ecs_entity_t t3 = ecs_new(world, Tag); - - ecs_add_pair(world, e2, RelA, t1); - ecs_add_pair(world, e3, RelA, t2); - ecs_add_pair(world, e3, RelA, t3); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(EcsWildcard, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t2), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t2, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(t2, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t3), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t3, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(t3, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_optional_pair_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?$x($this, TgtA), ?$x($this, TgtB)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_entity_t e4 = ecs_new(world, RelA); - - ecs_add_pair(world, e1, RelA, TgtA); - - ecs_add_pair(world, e2, RelA, TgtB); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - ecs_add_pair(world, e3, RelB, TgtB); - - ecs_add_pair(world, e4, RelA, TgtA); - ecs_add_pair(world, e4, RelA, TgtB); - ecs_add_pair(world, e4, RelB, TgtA); - ecs_add_pair(world, e4, RelB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_optional_pair_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, $x), ?RelB($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_entity_t e4 = ecs_new(world, RelA); - - ecs_add_pair(world, e1, RelA, TgtA); - - ecs_add_pair(world, e2, RelB, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - ecs_add_pair(world, e3, RelB, TgtB); - - ecs_add_pair(world, e4, RelA, TgtA); - ecs_add_pair(world, e4, RelA, TgtB); - ecs_add_pair(world, e4, RelB, TgtA); - ecs_add_pair(world, e4, RelB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_optional_pair_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, $x), ?Tag($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new(world, Tag); - ecs_entity_t t3 = ecs_new(world, Tag); - - ecs_add_pair(world, e2, RelA, t1); - ecs_add_pair(world, e3, RelA, t2); - ecs_add_pair(world, e3, RelA, t3); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(EcsWildcard, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t1), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t1, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(t1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t2), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t2, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(t2, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t3), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t3, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(t3, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_not_pair_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?$x($this, TgtA), !$x($this, TgtB)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtB); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - ecs_add_pair(world, e3, RelB, TgtA); - ecs_add_pair(world, e3, RelB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e3, RelB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_not_pair_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, $x), !RelB($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtA); - - ecs_add_pair(world, e3, RelA, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - ecs_add_pair(world, e3, RelA, TgtB); - ecs_add_pair(world, e3, RelB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e3, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e3, RelB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e1, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_and_optional_dependent_not_pair_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), ?RelA($this, $x), !Tag($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_entity_t t1 = ecs_new(world, Tag); - ecs_entity_t t2 = ecs_new(world, Tag); - ecs_entity_t t3 = ecs_new(world, Tag); - - ecs_add_pair(world, e2, RelA, t1); - ecs_add_pair(world, e3, RelA, t2); - ecs_add_pair(world, e3, RelA, t3); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(EcsWildcard, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, t1, Tag); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(EcsWildcard, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t1), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t1, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(t1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e3, RelA, t2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(EcsWildcard, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t1), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t1, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(t1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove_pair(world, e3, RelA, t3); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(e1, it.entities[0]); - test_uint(e3, it.entities[1]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(EcsWildcard, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, t1), ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(t1, ecs_field_src(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(t1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this) || RelB($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelB); - ecs_new(world, RelC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_or(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, RelD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this) || RelB($this) || RelC($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelB); - ecs_entity_t e3 = ecs_new(world, RelC); - ecs_new(world, RelD); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelC, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this), RelA($this) || RelB($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_add(world, e1, RelA); - ecs_add(world, e2, RelA); - ecs_add(world, e2, RelB); - ecs_add(world, e3, RelB); - ecs_add(world, e4, RelC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_or_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, RelD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this), RelA($this) || RelB($this) || RelC($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_add(world, e1, RelA); - ecs_add(world, e2, RelB); - ecs_add(world, e3, RelB); - ecs_add(world, e3, RelC); - ecs_add(world, e4, RelC); - ecs_add(world, e5, RelD); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelC, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_written_w_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), $x($this, TgtA) || $x($this, TgtB)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelB); - ecs_entity_t e3 = ecs_new_w_pair(world, Tag, RelC); - ecs_entity_t e4 = ecs_new_w_pair(world, Tag, RelA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtB); - ecs_add_pair(world, e3, RelC, TgtA); - ecs_add_pair(world, e3, RelC, TgtB); - ecs_add_pair(world, e4, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelC), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_or_written_w_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, RelD); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), $x($this, TgtA) || $x($this, TgtB) || $x($this, TgtC)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelB); - ecs_entity_t e3 = ecs_new_w_pair(world, Tag, RelC); - ecs_entity_t e4 = ecs_new_w_pair(world, Tag, RelD); - ecs_entity_t e5 = ecs_new_w_pair(world, Tag, RelA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtB); - ecs_add_pair(world, e3, RelC, TgtA); - ecs_add_pair(world, e3, RelC, TgtB); - ecs_add_pair(world, e4, RelD, TgtC); - ecs_add_pair(world, e5, RelD, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelC), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, RelD), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelD, TgtC), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(RelD, ecs_iter_get_var(&it, x_var)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_written_w_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), RelA($this, $x) || RelB($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtC); - ecs_entity_t e4 = ecs_new_w_pair(world, Tag, TgtA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtB); - ecs_add_pair(world, e3, RelA, TgtC); - ecs_add_pair(world, e3, RelB, TgtC); - ecs_add_pair(world, e4, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtC), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_written_w_rel_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, TgtD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $x), RelB($this, $y), $x($this, $y) || $y($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtC); - ecs_entity_t e4 = ecs_new_w_pair(world, RelA, TgtD); - - ecs_add_pair(world, e1, RelB, TgtD); - ecs_add_pair(world, e2, RelB, TgtC); - ecs_add_pair(world, e3, RelB, TgtB); - ecs_add_pair(world, e4, RelB, TgtA); - - ecs_add_pair(world, e2, TgtB, TgtC); - ecs_add_pair(world, e3, TgtB, TgtC); - ecs_add_pair(world, e4, TgtD, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtC), ecs_field_id(&it, 2)); - test_uint(ecs_pair(TgtB, TgtC), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(TgtC, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); - test_uint(ecs_pair(TgtB, TgtC), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - test_uint(TgtB, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtD), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(ecs_pair(TgtD, TgtA), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtD, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_written_w_rel_tgt_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, TgtD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $x), $x($this, $x) || RelB($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtC); - ecs_new_w_pair(world, RelA, TgtC); - - ecs_add_pair(world, e1, TgtA, TgtA); - ecs_add_pair(world, e2, RelB, TgtB); - ecs_add_pair(world, e3, TgtC, TgtC); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtC, TgtC), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_or_written_w_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, TgtD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this, $x), RelA($this, $x) || RelB($this, $x) || RelC($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtC); - ecs_entity_t e4 = ecs_new_w_pair(world, Tag, TgtD); - ecs_entity_t e5 = ecs_new_w_pair(world, Tag, TgtA); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelB, TgtB); - ecs_add_pair(world, e3, RelA, TgtC); - ecs_add_pair(world, e3, RelB, TgtC); - ecs_add_pair(world, e4, RelC, TgtD); - ecs_add_pair(world, e5, RelA, TgtD); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtC), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Tag, TgtD), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelC, TgtD), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(TgtD, ecs_iter_get_var(&it, x_var)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_chains_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, RelD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this), RelA($this) || RelB($this), RelC($this) || RelD($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - - ecs_add(world, e1, RelA); - ecs_add(world, e2, RelC); - - ecs_add(world, e3, RelA); - ecs_add(world, e3, RelC); - - ecs_add(world, e4, RelA); - ecs_add(world, e4, RelD); - - ecs_add(world, e5, RelB); - ecs_add(world, e5, RelC); - - ecs_add(world, e6, RelB); - ecs_add(world, e6, RelD); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(RelD, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e4, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e5, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(RelD, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e6, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_chains(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, RelD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this) || RelB($this), RelC($this) || RelD($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - ecs_entity_t e5 = ecs_new_id(world); - ecs_entity_t e6 = ecs_new_id(world); - - ecs_add(world, e1, RelA); - ecs_add(world, e2, RelC); - - ecs_add(world, e3, RelA); - ecs_add(world, e3, RelC); - - ecs_add(world, e4, RelA); - ecs_add(world, e4, RelD); - - ecs_add(world, e5, RelB); - ecs_add(world, e5, RelC); - - ecs_add(world, e6, RelB); - ecs_add(world, e6, RelD); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelC, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelD, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(RelC, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(RelD, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_dependent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelB($this) || RelA($this, $tgt), RelC($tgt)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - int tgt_var = ecs_rule_find_var(r, "tgt"); - test_assert(tgt_var != -1); - - ecs_entity_t tgt = ecs_new(world, RelC); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add(world, e1, RelB); - ecs_add_pair(world, e2, RelA, tgt); - ecs_add(world, e3, RelB); - ecs_add(world, e3, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, tgt), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - test_uint(tgt, ecs_iter_get_var(&it, tgt_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_dependent_reverse(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $tgt) || RelB($this), RelC($tgt)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - int tgt_var = ecs_rule_find_var(r, "tgt"); - test_assert(tgt_var != -1); - - ecs_entity_t tgt = ecs_new(world, RelC); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add(world, e1, RelB); - ecs_add_pair(world, e2, RelA, tgt); - ecs_add(world, e3, RelB); - ecs_add(world, e3, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, tgt), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, it.entities[0]); - test_uint(tgt, ecs_iter_get_var(&it, tgt_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_dependent_2_vars(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, RelD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $x) || RelB($this, $y), RelC($x), RelD($y)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_entity_t tgt_a = ecs_new(world, RelC); - ecs_entity_t tgt_b = ecs_new(world, RelD); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - - ecs_add_pair(world, e1, RelA, tgt_a); - ecs_add_pair(world, e2, RelB, tgt_b); - - ecs_add_pair(world, e3, RelA, tgt_b); - ecs_add_pair(world, e4, RelB, tgt_a); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, tgt_a), ecs_field_id(&it, 1)); - test_uint(RelC, ecs_field_id(&it, 2)); - test_uint(RelD, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(e1, it.entities[0]); - test_uint(tgt_a, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelB, tgt_b), ecs_field_id(&it, 1)); - test_uint(RelC, ecs_field_id(&it, 2)); - test_uint(RelD, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(e2, it.entities[0]); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(tgt_b, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_written_dependent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag, RelA($this, $tgt) || RelB($this), RelC($tgt)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - int tgt_var = ecs_rule_find_var(r, "tgt"); - test_assert(tgt_var != -1); - - ecs_entity_t tgt = ecs_new(world, RelC); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - - ecs_add(world, e1, RelB); - ecs_add_pair(world, e2, RelA, tgt); - ecs_add(world, e3, RelB); - ecs_add(world, e3, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(e1, it.entities[0]); - test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, tgt), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(e2, it.entities[0]); - test_uint(tgt, ecs_iter_get_var(&it, tgt_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(e3, it.entities[0]); - test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_written_dependent_2_vars(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, RelD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag, RelA($this, $x) || RelB($this, $y), RelC($x), RelD($y)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_entity_t tgt_a = ecs_new(world, RelC); - ecs_entity_t tgt_b = ecs_new(world, RelD); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - - ecs_add_pair(world, e1, RelA, tgt_a); - ecs_add_pair(world, e2, RelB, tgt_b); - - ecs_add_pair(world, e3, RelA, tgt_b); - ecs_add_pair(world, e4, RelB, tgt_a); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, tgt_a), ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_id(&it, 3)); - test_uint(RelD, ecs_field_id(&it, 4)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_bool(false, ecs_field_is_set(&it, 4)); - test_uint(e1, it.entities[0]); - test_uint(tgt_a, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, tgt_b), ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_id(&it, 3)); - test_uint(RelD, ecs_field_id(&it, 4)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 4)); - test_uint(e2, it.entities[0]); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(tgt_b, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_w_dependent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, RelD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag, ?RelA($this, $x), RelB($x) || RelC($x)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t tgt_a = ecs_new(world, RelB); - ecs_entity_t tgt_b = ecs_new(world, RelC); - ecs_entity_t tgt_c = ecs_new(world, RelD); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - - ecs_add_pair(world, e2, RelA, tgt_a); - ecs_add_pair(world, e3, RelA, tgt_b); - ecs_add_pair(world, e4, RelA, tgt_c); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 2)); - // test_uint(RelC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(e1, it.entities[0]); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, tgt_a), ecs_field_id(&it, 2)); - test_uint(RelB, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(e2, it.entities[0]); - test_uint(tgt_a, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, tgt_b), ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(e3, it.entities[0]); - test_uint(tgt_b, ecs_iter_get_var(&it, x_var)); - - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_or_w_both(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA || RelB" - }); - - test_assert(r != NULL); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add(world, e2, RelB); - ecs_entity_t e3 = ecs_new(world, RelB); - ecs_new(world, RelC); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(RelB, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_3_or_w_both(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, RelD); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA || RelB || RelC" - }); - - test_assert(r != NULL); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_add(world, e2, RelB); - ecs_entity_t e3 = ecs_new(world, RelA); - ecs_add(world, e3, RelD); - - ecs_entity_t e4 = ecs_new(world, RelB); - ecs_entity_t e5 = ecs_new(world, RelB); - ecs_add(world, e5, RelC); - ecs_entity_t e6 = ecs_new(world, RelB); - ecs_add(world, e6, RelD); - - ecs_entity_t e7 = ecs_new(world, RelC); - ecs_entity_t e8 = ecs_new(world, RelC); - ecs_add(world, e8, RelD); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(RelA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(RelB, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(RelB, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(RelB, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(RelC, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e8, it.entities[0]); - test_uint(RelC, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_not_first(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - - ecs_add(world, e1, TagB); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!TagB($this), TagA($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, e1, TagB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagB, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, e2, TagB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(TagB, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_2_optional_first(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - - ecs_add(world, e1, TagB); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "?TagB($this), TagA($this)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(3, it.count); - test_uint(TagB, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, e3, TagB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(TagB, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagB, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_root_entities_empty(void) { - ecs_world_t *world = ecs_mini(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!ChildOf($this, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(EcsFlecs, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_root_entities(void) { - ecs_world_t *world = ecs_mini(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!ChildOf($this, _)" - }); - - test_assert(r != NULL); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_new_entity(world, "e1.e2"); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(EcsFlecs, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_root_entities_w_children(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!ChildOf($this, _), ChildOf(_, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_add_pair(world, e3, EcsChildOf, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, EcsFlecs), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(EcsFlecs, ecs_iter_get_var(&it, this_var)); - test_uint(EcsFlecs, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, e2), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_root_entities_w_optional_children(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!ChildOf($this, _), ?ChildOf(_, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_add_pair(world, e3, EcsChildOf, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, EcsFlecs), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(EcsFlecs, ecs_iter_get_var(&it, this_var)); - test_uint(EcsFlecs, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, Tag), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(Tag, ecs_iter_get_var(&it, this_var)); - test_uint(Tag, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, e1), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, e2), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, e4), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, this_var)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_core_entities_w_optional_children(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "ChildOf($this, flecs.core), ?ChildOf(_, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_assert(ecs_field_is_set(&it, 1)); - for (int i = 0; i < it.count; i ++) { - test_assert(ecs_has_pair( - world, it.entities[i], EcsChildOf, EcsFlecsCore)); - if (ecs_field_is_set(&it, 2)) { - test_assert(ecs_count_id( - world, ecs_pair(EcsChildOf, it.entities[i])) != 0); - } else { - test_assert(ecs_count_id( - world, ecs_pair(EcsChildOf, it.entities[i])) == 0); - } - } - } - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_root_entities_w_not_children(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!ChildOf($this, _), !ChildOf(_, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_add_pair(world, e3, EcsChildOf, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, Tag), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(Tag, ecs_iter_get_var(&it, this_var)); - test_uint(Tag, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, e1), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, e4), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, this_var)); - test_uint(e4, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_core_entities_w_not_children(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "ChildOf($this, flecs.core), !ChildOf(_, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_assert(ecs_field_is_set(&it, 1)); - test_assert(!ecs_field_is_set(&it, 2)); - for (int i = 0; i < it.count; i ++) { - test_assert(ecs_has_pair( - world, it.entities[i], EcsChildOf, EcsFlecsCore)); - test_assert(ecs_count_id( - world, ecs_pair(EcsChildOf, it.entities[i])) == 0); - } - } - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add(world, e1, RelA); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelA(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelB(e1)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, Tgt); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelA(e1, Tgt)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelB(e1, Tgt)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!*(e1, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!*(e1, TgtB)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_tgt_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelA(e1, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelB(e1, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_tgt_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!*(e1, *)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!_(e1, TgtA)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!_(e1, TgtB)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_tgt_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelA(e1, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelB(e1, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_tgt_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!_(e1, _)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!$x(e1, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!$x(e1, TgtB)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelA(e1, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelB(e1, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!$x(e1, $y)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_tgt_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - { - ecs_rule_t *r = ecs_rule(world, { - .expr = "!$x(e1, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - } - - ecs_fini(world); -} - -void RulesOperators_1_this_src_not_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!$x($this, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, RelA, TgtB); - - bool e2_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_assert(it.count != 0); - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != e1); - if (it.entities[i] == e2) { - e2_found = true; - } - } - - test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - } - - test_assert(e2_found); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_1_this_src_not_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!RelA($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, RelB, TgtA); - - bool e2_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_assert(it.count != 0); - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != e1); - if (it.entities[i] == e2) { - e2_found = true; - } - } - - test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - } - - test_assert(e2_found); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_1_this_src_not_pair_rel_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!$x($this, $y)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add(world, e2, TgtA); - - bool e2_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_assert(it.count != 0); - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != e1); - if (it.entities[i] == e2) { - e2_found = true; - } - } - - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); - } - - test_assert(e2_found); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_1_this_src_not_pair_rel_tgt_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e2, RelA, RelA); - - ecs_entity_t e3 = ecs_new_id(world); - ecs_add(world, e3, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "!$x($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - bool e1_found = false; - - ecs_iter_t it = ecs_rule_iter(world, r); - while (ecs_rule_next(&it)) { - test_assert(it.count != 0); - for (int i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != e2); - if (it.entities[i] == e1) { - e1_found = true; - } - } - - test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); - } - - test_assert(e1_found); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_ENTITY(world, RelA, Tag); - ECS_ENTITY(world, RelB, Tag); - ECS_ENTITY(world, RelC, Tag); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e1, RelB, TgtB); - ecs_add_pair(world, e1, RelC, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x), !$x(e1, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_tgt_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_ENTITY(world, TgtA, Tag); - ECS_ENTITY(world, TgtB, Tag); - ECS_ENTITY(world, TgtC, Tag); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add_pair(world, e1, RelB, TgtB); - ecs_add_pair(world, e1, RelB, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x), !RelA(e1, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 2)); - test_uint(TgtC, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_tgt_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_ENTITY(world, RelA, (Tag, TgtA)); - ECS_ENTITY(world, RelB, (Tag, TgtA)); - ECS_ENTITY(world, RelC, (Tag, TgtB)); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e1, RelC, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x, $y), !$x(e1, $y)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelC, TgtB), ecs_field_id(&it, 2)); - test_uint(RelC, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - test_uint(TgtB, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesOperators_1_ent_src_not_pair_rel_tgt_same_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelA); - ECS_ENTITY(world, RelB, Tag); - ECS_ENTITY(world, TgtA, Tag); - ECS_ENTITY(world, TgtB, Tag); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e1, TgtA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add_pair(world, e1, RelB, TgtB); - ecs_add_pair(world, e1, TgtB, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x), !$x(e1, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, RelB), ecs_field_id(&it, 2)); - test_uint(RelB, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/RulesRecycled.c b/vendors/flecs/test/addons/src/RulesRecycled.c deleted file mode 100644 index d8864108e..000000000 --- a/vendors/flecs/test/addons/src/RulesRecycled.c +++ /dev/null @@ -1,208 +0,0 @@ -#include - -static ecs_entity_t recycled_id(ecs_world_t *world, const char *name) { - ecs_entity_t result = ecs_new_id(world); - ecs_delete(world, result); - ecs_entity_t result_2 = ecs_new_id(world); - test_assert(result_2 != (uint32_t)result); - ecs_set_name(world, result_2, name); - return result_2; -} - -void RulesRecycled_recycled_vars(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_entity_t src = recycled_id(world, "src"); - ecs_entity_t rel = recycled_id(world, "rel"); - ecs_add(world, src, Tag); - ecs_add_id(world, src, rel); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($y), Tag($y)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(src, ecs_field_src(&it, 1)); - test_uint(src, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_iter_get_var(&it, x_var)); - test_uint(src, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(rel, ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(src, ecs_field_src(&it, 1)); - test_uint(src, ecs_field_src(&it, 2)); - test_uint(rel, ecs_iter_get_var(&it, x_var)); - test_uint(src, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesRecycled_recycled_pair_vars(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_entity_t src = recycled_id(world, "src"); - ecs_entity_t rel = recycled_id(world, "rel"); - ecs_entity_t tgt = recycled_id(world, "tgt"); - ecs_add(world, src, Tag); - ecs_add_pair(world, src, rel, tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($y, $z), Tag($y)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - int32_t z_var = ecs_rule_find_var(r, "z"); - test_assert(z_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(src, ecs_field_src(&it, 1)); - test_uint(src, ecs_field_src(&it, 2)); - test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); - test_uint(src, ecs_iter_get_var(&it, y_var)); - test_uint(EcsName, ecs_iter_get_var(&it, z_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(rel, tgt), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(src, ecs_field_src(&it, 1)); - test_uint(src, ecs_field_src(&it, 2)); - test_uint(rel, ecs_iter_get_var(&it, x_var)); - test_uint(src, ecs_iter_get_var(&it, y_var)); - test_uint(tgt, ecs_iter_get_var(&it, z_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesRecycled_recycled_this_ent_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_entity_t src = recycled_id(world, "src"); - ecs_entity_t rel = recycled_id(world, "rel"); - ecs_entity_t tgt = recycled_id(world, "tgt"); - ecs_add(world, src, Tag); - ecs_add_pair(world, src, rel, tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($y, $this), Tag($y)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - int32_t this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(src, ecs_field_src(&it, 1)); - test_uint(src, ecs_field_src(&it, 2)); - test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); - test_uint(src, ecs_iter_get_var(&it, y_var)); - test_uint(EcsName, ecs_iter_get_var(&it, this_var)); - test_uint(EcsName, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(rel, tgt), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(src, ecs_field_src(&it, 1)); - test_uint(src, ecs_field_src(&it, 2)); - test_uint(rel, ecs_iter_get_var(&it, x_var)); - test_uint(src, ecs_iter_get_var(&it, y_var)); - test_uint(tgt, ecs_iter_get_var(&it, this_var)); - test_uint(tgt, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesRecycled_has_recycled_id_from_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_entity_t src = recycled_id(world, "src"); - ecs_entity_t rel = recycled_id(world, "rel"); - ecs_entity_t tgt = recycled_id(world, "tgt"); - ecs_add_pair(world, src, rel, tgt); - ecs_add_id(world, src, tgt); - ecs_add(world, src, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($y, $z), $z($y), Tag($y)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - int32_t z_var = ecs_rule_find_var(r, "z"); - test_assert(z_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(rel, tgt), ecs_field_id(&it, 1)); - test_uint(tgt, ecs_field_id(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(src, ecs_field_src(&it, 1)); - test_uint(src, ecs_field_src(&it, 2)); - test_uint(src, ecs_field_src(&it, 3)); - test_uint(rel, ecs_iter_get_var(&it, x_var)); - test_uint(src, ecs_iter_get_var(&it, y_var)); - test_uint(tgt, ecs_iter_get_var(&it, z_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/RulesScopes.c b/vendors/flecs/test/addons/src/RulesScopes.c deleted file mode 100644 index a410ba049..000000000 --- a/vendors/flecs/test/addons/src/RulesScopes.c +++ /dev/null @@ -1,405 +0,0 @@ -#include - -void RulesScopes_term_w_not_scope_1_term(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - ECS_TAG(world, TagA); - - ecs_entity_t parent_1 = ecs_new(world, Root); - ecs_add(world, parent_1, TagA); - - ecs_entity_t parent_2 = ecs_new(world, Root); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ TagA }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(parent_2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesScopes_term_w_not_scope_2_terms(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t parent_1 = ecs_new(world, Root); - ecs_add(world, parent_1, TagA); - - ecs_entity_t parent_2 = ecs_new(world, Root); - ecs_add(world, parent_2, TagB); - - ecs_entity_t parent_3 = ecs_new(world, Root); - ecs_add(world, parent_3, TagA); - ecs_add(world, parent_3, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ TagA, TagB }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(parent_1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(parent_2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesScopes_term_w_not_scope_1_term_w_not(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - ECS_TAG(world, TagA); - - ecs_entity_t parent_1 = ecs_new(world, Root); - ecs_add(world, parent_1, TagA); - - ecs_new(world, Root); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ !TagA }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(parent_1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesScopes_term_w_not_scope_2_terms_w_not(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t parent_1 = ecs_new(world, Root); - ecs_add(world, parent_1, TagA); - - ecs_entity_t parent_2 = ecs_new(world, Root); - ecs_add(world, parent_2, TagB); - - ecs_entity_t parent_3 = ecs_new(world, Root); - ecs_add(world, parent_3, TagA); - ecs_add(world, parent_3, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ TagA, !TagB }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(parent_2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(parent_3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesScopes_term_w_not_scope_1_term_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - - ecs_entity_t parent_1 = ecs_new(world, Root); - ecs_new_w_pair(world, EcsChildOf, parent_1); - ecs_new_w_pair(world, EcsChildOf, parent_1); - - ecs_entity_t parent_2 = ecs_new(world, Root); - ecs_new_w_pair(world, EcsChildOf, parent_2); - - ecs_entity_t parent_3 = ecs_new(world, Root); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ ChildOf($child, $this) }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(parent_3, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesScopes_term_w_not_scope_2_terms_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - ECS_COMPONENT(world, Position); - - ecs_entity_t parent_0 = ecs_new(world, Root); - { - ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_0); - ecs_set(world, child_1, Position, {10, 20}); - ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent_0); - ecs_set(world, child_2, Position, {10, 20}); - } - - ecs_entity_t parent_1 = ecs_new(world, Root); - { - ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_1); - ecs_set(world, child_1, Position, {10, 20}); - ecs_new_w_pair(world, EcsChildOf, parent_1); - } - - ecs_entity_t parent_2 = ecs_new(world, Root); - { - ecs_new_w_pair(world, EcsChildOf, parent_2); - ecs_new_w_pair(world, EcsChildOf, parent_2); - } - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ ChildOf($child, $this), Position($child) }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(parent_2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesScopes_term_w_not_scope_1_term_w_not_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - - ecs_entity_t parent_0 = ecs_new(world, Root); - { - ecs_new_w_pair(world, EcsChildOf, parent_0); - ecs_new_w_pair(world, EcsChildOf, parent_0); - } - - ecs_new(world, Root); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ !ChildOf($child, $this) }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(parent_0, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesScopes_term_w_not_scope_2_terms_w_not_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - ECS_COMPONENT(world, Position); - ECS_TAG(world, Foo); - - ecs_entity_t parent_0 = ecs_new(world, Root); - { - ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_0); - ecs_set(world, child_1, Position, {10, 20}); - ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent_0); - ecs_set(world, child_2, Position, {10, 20}); - ecs_entity_t child_3 = ecs_new_w_pair(world, EcsChildOf, parent_0); - ecs_set(world, child_3, Position, {10, 20}); - ecs_add(world, child_3, Foo); - } - - ecs_entity_t parent_1 = ecs_new(world, Root); - { - ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_1); - ecs_set(world, child_1, Position, {10, 20}); - ecs_new_w_pair(world, EcsChildOf, parent_1); - } - - ecs_entity_t parent_2 = ecs_new(world, Root); - { - ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_2); - ecs_add(world, child_1, Foo); - ecs_new_w_pair(world, EcsChildOf, parent_2); - } - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ ChildOf($child, $this), !Position($child) }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(parent_0, it.entities[0]); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesScopes_term_w_not_scope_2_terms_w_or(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, Root); - ecs_add(world, e1, TagA); - ecs_entity_t e2 = ecs_new(world, Root); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, Root); - ecs_add(world, e3, TagA); - ecs_add(world, e3, TagB); - ecs_entity_t e4 = ecs_new(world, Root); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ TagA || TagB }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e4, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesScopes_term_w_not_scope_3_terms_w_or(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Root); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, Root); - ecs_add(world, e1, TagA); - ecs_entity_t e2 = ecs_new(world, Root); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, Root); - ecs_add(world, e3, TagA); - ecs_add(world, e3, TagB); - ecs_entity_t e4 = ecs_new(world, Root); - ecs_add(world, e4, TagC); - ecs_entity_t e5 = ecs_new(world, Root); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Root, !{ TagA || TagB || TagC }" - }); - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Root, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e5, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/RulesTraversal.c b/vendors/flecs/test/addons/src/RulesTraversal.c deleted file mode 100644 index 0c93b65b1..000000000 --- a/vendors/flecs/test/addons/src/RulesTraversal.c +++ /dev/null @@ -1,8202 +0,0 @@ -#include - -void RulesTraversal_this_self_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_self_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($x:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(p1, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(p2, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(p3, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($x:up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_written_self_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x:self), Foo($x:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(p1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(p2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(p3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e7, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_written_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x:self), Foo($x:up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e7, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_set_var_self_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($x:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_entity_t e8 = ecs_new_w_pair(world, EcsChildOf, p0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, p2); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(p2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e3); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e5); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e8); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_set_var_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($x:up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_entity_t e8 = ecs_new_w_pair(world, EcsChildOf, p0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, p2); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e3); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e5); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e8); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_set_var_written_self_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x:self), Foo($x:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_entity_t e8 = ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, p2); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(p2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e3); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e5); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e8); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_set_var_written_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x:self), Foo($x:up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_entity_t e8 = ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, p2); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e3); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e5); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e8); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_ent_self_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(ent:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, e, Foo); - ecs_add_pair(world, e, EcsChildOf, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, parent, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(parent, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_ent_up_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(ent:up(ChildOf))" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e, EcsChildOf, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, parent, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(parent, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_implicit_this_self_up_isa(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsIsA, p3); - - ecs_new_w_pair(world, EcsIsA, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_implicit_this_up_isa(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(up)" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsIsA, p3); - - ecs_new_w_pair(world, EcsIsA, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_implicit_var_self_up_isa(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsIsA, p3); - - ecs_new_w_pair(world, EcsIsA, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(p1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(p2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(p3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_implicit_var_up_isa(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($x:up)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsIsA, p3); - - ecs_new_w_pair(world, EcsIsA, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_implicit_ent_self_up_isa(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(ent:self|up)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_remove(world, e, Foo); - ecs_add_pair(world, e, EcsIsA, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, parent, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(parent, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_implicit_ent_up_isa(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(ent:up)" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e, EcsIsA, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, parent, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(parent, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_self_up_2_targets(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self|up)" - }); - - test_assert(r != NULL); - - ecs_entity_t parent_a = ecs_new_id(world); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_entity_t parent_c = ecs_new(world, Foo); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, e_1, EcsIsA, parent_b); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_4 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_add_pair(world, e_4, EcsIsA, parent_c); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(parent_b, it.entities[0]); - test_uint(parent_c, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(parent_b, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(parent_b, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_4, it.entities[0]); - test_uint(parent_b, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_up_2_targets(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(up)" - }); - - test_assert(r != NULL); - - ecs_entity_t parent_a = ecs_new_id(world); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_entity_t parent_c = ecs_new(world, Foo); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, e_1, EcsIsA, parent_b); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_4 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_add_pair(world, e_4, EcsIsA, parent_c); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(parent_b, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(parent_b, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_4, it.entities[0]); - test_uint(parent_b, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_self_up_2_targets_diamond(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self|up)" - }); - - test_assert(r != NULL); - - ecs_entity_t parent_root = ecs_new(world, Foo); - ecs_entity_t parent_a = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_b = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_c = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, parent_c, EcsIsA, parent_b); - - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, parent_c); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_root, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(parent_b, it.entities[1]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_c, it.entities[0]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_3, it.entities[0]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_up_2_targets_diamond(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(up)" - }); - - test_assert(r != NULL); - - ecs_entity_t parent_root = ecs_new(world, Foo); - ecs_entity_t parent_a = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_b = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_c = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, parent_c, EcsIsA, parent_b); - - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, parent_c); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(parent_b, it.entities[1]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_c, it.entities[0]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_3, it.entities[0]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_written_self_up_2_targets(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(self|up)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t parent_a = ecs_new_id(world); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_entity_t parent_c = ecs_new(world, Foo); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, e_1, EcsIsA, parent_b); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_4 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_add_pair(world, e_4, EcsIsA, parent_c); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(parent_b, it.entities[0]); - test_uint(parent_c, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_written_up_2_targets(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(up)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t parent_a = ecs_new_id(world); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_entity_t parent_c = ecs_new(world, Foo); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, e_1, EcsIsA, parent_b); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_4 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_add_pair(world, e_4, EcsIsA, parent_c); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_written_self_up_2_targets_diamond(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(self|up)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t parent_root = ecs_new(world, Foo); - ecs_entity_t parent_a = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_b = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_c = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, parent_c, EcsIsA, parent_b); - - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, parent_c); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_root, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(parent_b, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_c, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_written_up_2_targets_diamond(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(up)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t parent_root = ecs_new(world, Foo); - ecs_entity_t parent_a = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_b = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_c = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, parent_c, EcsIsA, parent_b); - - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, parent_c); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(parent_b, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_c, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_root, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_2_self_up_terms(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag, Foo(self|up)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t parent_a = ecs_new_id(world); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, e_2); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_b, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_2_self_up_terms_2_targets(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag, Foo(self|up)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t parent_a = ecs_new_id(world); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_entity_t parent_c = ecs_new(world, Foo); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, e_1, EcsIsA, parent_b); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_4 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_add_pair(world, e_4, EcsIsA, parent_c); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(parent_b, it.entities[0]); - test_uint(parent_c, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(parent_b, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_self_up_empty_table(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self|up)" - }); - - test_assert(r != NULL); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - ecs_delete(world, e2); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_up_empty_table(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(up)" - }); - - test_assert(r != NULL); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - ecs_delete(world, e2); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_self_up_all_owned(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self|up)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t parent_root = ecs_new(world, 0); - ecs_entity_t parent_a = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_b = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_c = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, parent_c, EcsIsA, parent_b); - - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, parent_c); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_root, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(parent_b, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_c, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_up_all_owned(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(up)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t parent_root = ecs_new(world, 0); - ecs_entity_t parent_a = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_b = ecs_new_w_pair(world, EcsIsA, parent_root); - ecs_entity_t parent_c = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_add_pair(world, parent_c, EcsIsA, parent_b); - - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent_a); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent_b); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, parent_c); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(parent_b, it.entities[1]); - test_uint(parent_root, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_1, it.entities[0]); - test_uint(parent_a, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_c, it.entities[0]); - test_uint(parent_a, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_2, it.entities[0]); - test_uint(parent_b, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e_3, it.entities[0]); - test_uint(parent_c, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(base, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(base, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(base, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(base, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_self_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($x:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(base, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(child, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($x:up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(child, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_written_self_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x:self), Foo($x:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(base, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(base, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(child, ecs_field_src(&it, 1)); - test_uint(base, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(child, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_written_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x:self), Foo($x:up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(child, ecs_field_src(&it, 1)); - test_uint(base, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_uint(child, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_ent_self_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_set_name(world, parent, "parent"); - ecs_set_name(world, child, "child"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(parent.child:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, child, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(child, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_ent_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_set_name(world, parent, "parent"); - ecs_set_name(world, child, "child"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(parent.child:up(ChildOf))" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, child, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_ent_written_self_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_set_with(world, Tag); - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_set_name(world, parent, "parent"); - ecs_set_name(world, child, "child"); - ecs_set_with(world, 0); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(parent.child:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, child, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(child, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_ent_written_up_childof_inherited(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tag); - - ecs_set_with(world, Tag); - ecs_entity_t base = ecs_new(world, Foo); - ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_set_name(world, parent, "parent"); - ecs_set_name(world, child, "child"); - ecs_set_with(world, 0); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(parent.child:up(ChildOf))" - }); - - test_assert(r != NULL); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, child, Foo); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(base, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t p2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t p3 = ecs_set(world, 0, Position, {30, 40}); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_up_childof_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t p2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t p3 = ecs_set(world, 0, Position, {30, 40}); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Position(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_set(world, ecs_new(world, Tag), Position, {10, 20}); - ecs_entity_t p2 = ecs_set(world, ecs_new(world, Tag), Position, {20, 30}); - ecs_entity_t p3 = ecs_set(world, ecs_new(world, Tag), Position, {30, 40}); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_up_childof_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Position(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_set(world, ecs_new(world, Tag), Position, {10, 20}); - ecs_entity_t p2 = ecs_set(world, ecs_new(world, Tag), Position, {20, 30}); - ecs_entity_t p3 = ecs_set(world, ecs_new(world, Tag), Position, {30, 40}); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_self_up_childof_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($x:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t p2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t p3 = ecs_set(world, 0, Position, {30, 40}); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(p3, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_up_childof_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($x:up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t p2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t p3 = ecs_set(world, 0, Position, {30, 40}); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_written_self_up_childof_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x:self), Position($x:self|up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_set(world, ecs_new(world, Tag), Position, {10, 20}); - ecs_entity_t p2 = ecs_set(world, ecs_new(world, Tag), Position, {20, 30}); - ecs_entity_t p3 = ecs_set(world, ecs_new(world, Tag), Position, {30, 40}); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(p1, ecs_iter_get_var(&it, 1)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(p2, ecs_iter_get_var(&it, 1)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(p3, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e7, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_var_written_up_childof_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($x:self), Position($x:up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_set(world, ecs_new(world, Tag), Position, {10, 20}); - ecs_entity_t p2 = ecs_set(world, ecs_new(world, Tag), Position, {20, 30}); - ecs_entity_t p3 = ecs_set(world, ecs_new(world, Tag), Position, {30, 40}); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e4, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e4, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e5, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e5, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e6, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e6, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(0, it.count); - test_uint(e7, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(e7, ecs_iter_get_var(&it, x_var)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_recycled_parent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p0 = ecs_new_id(world); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - test_assert((uint32_t)p0 != p0); - test_assert((uint32_t)p1 != p1); - test_assert((uint32_t)p2 != p2); - test_assert((uint32_t)p3 != p3); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_up_childof_recycled_parent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p0 = ecs_new_id(world); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - test_assert((uint32_t)p0 != p0); - test_assert((uint32_t)p1 != p1); - test_assert((uint32_t)p2 != p2); - test_assert((uint32_t)p3 != p3); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_recycled_parent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p0 = ecs_new_id(world); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - test_assert((uint32_t)p0 != p0); - test_assert((uint32_t)p1 != p1); - test_assert((uint32_t)p2 != p2); - test_assert((uint32_t)p3 != p3); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_up_childof_recycled_parent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p0 = ecs_new_id(world); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - test_assert((uint32_t)p0 != p0); - test_assert((uint32_t)p1 != p1); - test_assert((uint32_t)p2 != p2); - test_assert((uint32_t)p3 != p3); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_recycled_parent_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p0 = ecs_new_id(world); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p1 = ecs_set(world, 0, Position, {10, 20}); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p2 = ecs_set(world, 0, Position, {20, 30}); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p3 = ecs_set(world, 0, Position, {30, 40}); - ecs_add(world, p3, Bar); - - test_assert((uint32_t)p0 != p0); - test_assert((uint32_t)p1 != p1); - test_assert((uint32_t)p2 != p2); - test_assert((uint32_t)p3 != p3); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_up_childof_recycled_parent_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p0 = ecs_new_id(world); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p1 = ecs_set(world, 0, Position, {10, 20}); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p2 = ecs_set(world, 0, Position, {20, 30}); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p3 = ecs_set(world, 0, Position, {30, 40}); - ecs_add(world, p3, Bar); - - test_assert((uint32_t)p0 != p0); - test_assert((uint32_t)p1 != p1); - test_assert((uint32_t)p2 != p2); - test_assert((uint32_t)p3 != p3); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_recycled_parent_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Position(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p0 = ecs_new_id(world); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p1 = ecs_set(world, ecs_new(world, Tag), Position, {10, 20}); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p2 = ecs_set(world, ecs_new(world, Tag), Position, {20, 30}); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p3 = ecs_set(world, ecs_new(world, Tag), Position, {30, 40}); - ecs_add(world, p3, Bar); - - test_assert((uint32_t)p0 != p0); - test_assert((uint32_t)p1 != p1); - test_assert((uint32_t)p2 != p2); - test_assert((uint32_t)p3 != p3); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_up_childof_recycled_parent_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Position(up(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p0 = ecs_new_id(world); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p1 = ecs_set(world, ecs_new(world, Tag), Position, {10, 20}); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p2 = ecs_set(world, ecs_new(world, Tag), Position, {20, 30}); - ecs_delete(world, ecs_new_id(world)); - ecs_entity_t p3 = ecs_set(world, ecs_new(world, Tag), Position, {30, 40}); - ecs_add(world, p3, Bar); - - test_assert((uint32_t)p0 != p0); - test_assert((uint32_t)p1 != p1); - test_assert((uint32_t)p2 != p2); - test_assert((uint32_t)p3 != p3); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - } - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Bar); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(self|up(ChildOf), Tgt)" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, Tgt); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_up_childof_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Bar); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(up(ChildOf), Tgt)" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, Tgt); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Rel(self|up(ChildOf), Tgt)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, Tgt); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_up_childof_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Rel(up(ChildOf), Tgt)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, Tgt); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_pair_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Bar); - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(self|up(ChildOf), *)" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add_pair(world, p3, EcsChildOf, p2); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_up_childof_pair_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Bar); - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(up(ChildOf), *)" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add_pair(world, p3, EcsChildOf, p2); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_pair_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Rel(self|up(ChildOf), *)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add(world, p3, Bar); - ecs_add_pair(world, p3, EcsChildOf, p2); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_up_childof_pair_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Rel(up(ChildOf), *)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add(world, p3, Bar); - ecs_add_pair(world, p3, EcsChildOf, p2); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Bar); - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(self|up(ChildOf), $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add_pair(world, p3, EcsChildOf, p2); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Rel(self|up(ChildOf), $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add(world, p3, Bar); - ecs_add_pair(world, p3, EcsChildOf, p2); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 2)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 2)); - test_uint(TgtC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Bar); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(self|up(ChildOf), Tgt)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, RelA, Tgt); - ecs_entity_t p2 = ecs_new_w_pair(world, RelB, Tgt); - ecs_entity_t p3 = ecs_new_w_pair(world, RelC, Tgt); - ecs_add_pair(world, p3, EcsChildOf, p2); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 1)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 1)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, Tgt); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), $x(self|up(ChildOf), Tgt)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new_w_pair(world, RelA, Tgt); - ecs_entity_t p2 = ecs_new_w_pair(world, RelB, Tgt); - ecs_entity_t p3 = ecs_new_w_pair(world, RelC, Tgt); - ecs_add(world, p3, Bar); - ecs_add_pair(world, p3, EcsChildOf, p2); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 2)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 2)); - test_uint(RelC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_pair_for_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ECS_ENTITY(world, ent, (Rel, TagA), (Rel, TagB), (Rel, TagC)); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(ent, $x), $x(self|up)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p1 = ecs_new(world, TagA); - ecs_entity_t p2 = ecs_new(world, TagB); - ecs_entity_t p3 = ecs_new(world, TagC); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_add_pair(world, e1, EcsIsA, p2); - ecs_add_pair(world, e1, EcsIsA, p3); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p2, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 1)); - test_uint(TagC, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 1)); - test_uint(TagC, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_up_childof_pair_for_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ECS_ENTITY(world, ent, (Rel, TagA), (Rel, TagB), (Rel, TagC)); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(ent, $x), $x(up)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p1 = ecs_new(world, TagA); - ecs_entity_t p2 = ecs_new(world, TagB); - ecs_entity_t p3 = ecs_new(world, TagC); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_add_pair(world, e1, EcsIsA, p2); - ecs_add_pair(world, e1, EcsIsA, p3); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 1)); - test_uint(TagC, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_pair_for_var_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(self, $x), $x(self|up(ChildOf))" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new(world, 0); - ecs_entity_t p1 = ecs_new(world, TagA); - ecs_entity_t p2 = ecs_new(world, TagB); - ecs_entity_t p3 = ecs_new(world, TagC); - - ecs_entity_t e0 = ecs_new_w_pair(world, EcsChildOf, p0); - ecs_add_pair(world, e0, Rel, TagA); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add_pair(world, e1, Rel, TagA); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add_pair(world, e2, Rel, TagB); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p3); - ecs_add_pair(world, e3, Rel, TagC); - - ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TagA); - ecs_add(world, e4, TagA); - ecs_new_w_pair(world, Rel, TagB); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 1)); - test_uint(TagC, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_up_childof_pair_for_var_written_n_targets(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ECS_ENTITY(world, ent, (Rel, TagA), (Rel, TagB), (Rel, TagC)); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(ent, $x), $x(self|up)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p0 = ecs_new(world, 0); - ecs_entity_t p0_a = ecs_new(world, TagA); - ecs_entity_t p0_b = ecs_new(world, TagB); - ecs_add(world, p0_b, Foo); - ecs_entity_t p0_c = ecs_new(world, TagC); - ecs_add(world, p0_c, Foo); - ecs_add(world, p0_c, Bar); - ecs_entity_t p1 = ecs_new_id(world); - ecs_add_pair(world, p1, EcsIsA, p0_a); - ecs_add_pair(world, p1, EcsIsA, p0_b); - ecs_add_pair(world, p1, EcsIsA, p0_c); - - ecs_new_w_pair(world, EcsIsA, p0); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TagA); - ecs_add(world, e2, TagA); - ecs_new_w_pair(world, Rel, TagB); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p0_a, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p0_a, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p0_a, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p0_b, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p0_b, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p0_b, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p0_c, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 1)); - test_uint(TagC, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p0_c, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 1)); - test_uint(TagC, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(p0_c, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 1)); - test_uint(TagC, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_up_childof_pair_for_var_written_n_targets(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel(self, $x), $x(self|up)" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new(world, 0); - ecs_entity_t p1 = ecs_new(world, TagA); - ecs_entity_t p2 = ecs_new(world, TagB); - ecs_entity_t p3 = ecs_new(world, TagC); - - ecs_entity_t e0 = ecs_new_w_pair(world, EcsIsA, p0); - ecs_add_pair(world, e0, Rel, TagA); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_add_pair(world, e1, EcsIsA, p2); - ecs_add_pair(world, e1, EcsIsA, p3); - ecs_add_pair(world, e1, Rel, TagA); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_add_pair(world, e2, EcsIsA, p2); - ecs_add_pair(world, e2, EcsIsA, p3); - ecs_add_pair(world, e2, Rel, TagB); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, p1); - ecs_add_pair(world, e3, EcsIsA, p2); - ecs_add_pair(world, e3, EcsIsA, p3); - ecs_add_pair(world, e3, Rel, TagC); - - ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TagA); - ecs_add(world, e4, TagA); - ecs_new_w_pair(world, Rel, TagB); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagB, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 1)); - test_uint(TagC, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_self_up_2_levels(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_set(world, base, Position, {10, 20}); - - ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_2, EcsIsA, base); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_2); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = ecs_id(Position) } - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesTraversal_not_up_disabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_rule_t *f = ecs_rule(world, { - .terms = { - { TagA }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsChildOf, .oper = EcsNot } - } - }); - test_assert(f != NULL); - - ecs_entity_t parent = ecs_new_w_id(world, EcsDisabled); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add_id(world, e2, EcsDisabled); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, EcsChildOf, parent); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, it.ids[0]); - test_uint(0, it.sources[0]); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesTraversal_up_2_rel_instances(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsTraversable); - - ecs_entity_t b1 = ecs_new(world, TagA); - ecs_entity_t b2 = ecs_new(world, TagA); - ecs_add(world, b1, TagB); - ecs_add(world, b2, TagB); - ecs_add(world, b1, TagC); - ecs_add(world, b2, TagC); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Rel, b1); - ecs_add_pair(world, e, Rel, b2); - - ecs_rule_t *f = ecs_rule(world, { - .terms = { - {TagC, .src.flags = EcsUp, .src.trav = Rel } - } - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(it.sources[0], b1); - test_uint(it.ids[0], TagC); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesTraversal_up_2_rel_instances_match_2nd(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsTraversable); - - ecs_entity_t b1 = ecs_new(world, TagA); - ecs_entity_t b2 = ecs_new(world, TagA); - ecs_add(world, b1, TagB); - ecs_add(world, b2, TagB); - ecs_add(world, b2, TagC); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Rel, b1); - ecs_add_pair(world, e, Rel, b2); - - ecs_rule_t *f = ecs_rule(world, { - .terms = { - { TagC, .src.flags = EcsUp, .src.trav = Rel } - } - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(it.sources[0], b2); - test_uint(it.ids[0], TagC); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesTraversal_up_only_w_owned(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, R, EcsTraversable); - ECS_COMPONENT(world, Position); - - ecs_entity_t e_0 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_1 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {50, 60}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {70, 80}); - - ecs_add_pair(world, e_3, R, e_2); - ecs_add_pair(world, e_2, R, e_1); - ecs_add_pair(world, e_1, R, e_0); - - ecs_rule_t *f = ecs_rule(world, { - .expr = "Position(up(R))" - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_1); - test_uint(it.sources[0], e_0); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_3); - test_uint(it.sources[0], e_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_2); - test_uint(it.sources[0], e_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesTraversal_this_self_cascade_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self|cascade(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_cascade_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(cascade(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_cascade_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(self|cascade(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_cascade_childof(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(cascade(ChildOf))" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_self_cascade_childof_w_parent_flag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self|cascade|parent)" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_cascade_childof_w_parent_flag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(cascade|parent)" - }); - - test_assert(r != NULL); - - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(p2, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(p3, ecs_field_src(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 1)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_self_cascade_childof_w_parent_flag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(self|cascade|parent)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(2, it.count); - test_uint(p1, it.entities[0]); - test_uint(p2, it.entities[1]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesTraversal_this_written_cascade_childof_w_parent_flag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag(self), Foo(cascade|parent)" - }); - - test_assert(r != NULL); - - ecs_set_with(world, Tag); - ecs_entity_t p0 = ecs_new_id(world); - ecs_entity_t p1 = ecs_new(world, Foo); - ecs_entity_t p2 = ecs_new(world, Foo); - ecs_entity_t p3 = ecs_new(world, Foo); - ecs_add(world, p3, Bar); - - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); - ecs_add(world, e4, Bar); - - ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_add(world, e6, Bar); - - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); - - ecs_new_w_pair(world, EcsChildOf, p0); - ecs_set_with(world, 0); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p2, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p1, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e7, it.entities[0]); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(p3, ecs_field_src(&it, 2)); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/RulesVariables.c b/vendors/flecs/test/addons/src/RulesVariables.c deleted file mode 100644 index 0737fa600..000000000 --- a/vendors/flecs/test/addons/src/RulesVariables.c +++ /dev/null @@ -1,9175 +0,0 @@ -#include - -void RulesVariables_1_ent_src_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(ent)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_w_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(ent, TgtA)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_w_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_w_pair_rel_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(ent, $y)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); - test_uint(EcsName, ecs_iter_get_var(&it, y_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); - test_uint(EcsName, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); - test_uint(EcsName, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); - test_uint(EcsName, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtB, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_w_pair_rel_tgt_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(ent, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, TgtA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, TgtB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_w_pair_rel_tgt_same_var_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(ent), $x(ent, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, TgtA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TgtA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ent, ecs_field_src(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, TgtB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TgtA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ent, ecs_field_src(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TgtA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ent, ecs_field_src(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TgtB, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 2)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(ent, ecs_field_src(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this), $x($this)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(3, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e2, RelB); - ecs_add(world, e3, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtB); - ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, TgtA)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(3, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelB, TgtA); - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_w_pair(world, RelB, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, RelB, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(3, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, TgtB); - ecs_add_pair(world, e3, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_pair_rel_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Tag($this), $x($this, $y)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int32_t y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(3, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, TgtB); - ecs_add_pair(world, e3, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtB, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtB, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(TgtB, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(TgtA, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_pair_rel_tgt_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_w_pair(world, RelB, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, RelB, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, TgtA, TgtA); - ecs_add_pair(world, e2, TgtA, TgtA); - ecs_add_pair(world, e3, TgtA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(3, it.count); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, TgtB, TgtB); - ecs_add_pair(world, e3, TgtB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_pair_rel_tgt_same_var_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_w_pair(world, RelB, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, RelB, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this), $x($this, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, TgtA); - ecs_add_pair(world, e2, RelA, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, TgtA, TgtA); - ecs_add_pair(world, e2, TgtA, TgtA); - ecs_add_pair(world, e3, TgtA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, TgtA); - ecs_add(world, e2, TgtA); - ecs_add(world, e3, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(3, it.count); - test_uint(TgtA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, TgtB, TgtB); - ecs_add_pair(world, e3, TgtB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TgtA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(TgtA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e2, TgtB); - ecs_add(world, e3, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TgtA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(TgtA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(TgtB, ecs_field_id(&it, 1)); - test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_id_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_id(world, e3, e1); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($x), TagA($x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(e1, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(e1, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(e2, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_first_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, e1, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($x, TgtA), TagA($x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e2, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_second_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, RelA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, RelA, e1); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x, $x), TagA($x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_first_and_second_same_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, RelA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, e1, e1); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add_pair(world, e4, e4, e1); - ecs_entity_t e5 = ecs_new(world, TagA); - ecs_add_pair(world, e5, e1, e5); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($x, $x), TagA($x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e2, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_id_same_var_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_id(world, e3, e1); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($x), $x($x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_first_same_var_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TagA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, e1, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($x), $x($x, TgtA)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e2, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_second_same_var_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TagA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, RelA, e1); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($x), RelA($x, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_first_and_second_same_var_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TagA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, e1, e1); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add_pair(world, e4, e4, e1); - ecs_entity_t e5 = ecs_new(world, TagA); - ecs_add_pair(world, e5, e1, e5); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($x), $x($x, $x)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e2, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_first_same_var_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_id(world, e3, e1); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this($this), TagA($this)" - }); - - test_assert(r != NULL); - - int32_t this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e2, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_second_same_var_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, e1, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this($this, TgtA), TagA($this)" - }); - - test_assert(r != NULL); - - int32_t this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e2, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); - -} - -void RulesVariables_1_src_pair_first_and_second_same_var_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, RelA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, RelA, e1); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $this), TagA($this)" - }); - - test_assert(r != NULL); - - int32_t this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_id_same_var_this_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_id(world, e3, e1); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($this), $this($this)" - }); - - test_assert(r != NULL); - - int32_t this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_first_same_var_this_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TagA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, e1, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($this), $this($this, TgtA)" - }); - - test_assert(r != NULL); - - int32_t this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e2, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_second_same_var_this_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TagA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, RelA, e1); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($this), RelA($this, $this)" - }); - - test_assert(r != NULL); - - int32_t this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_src_pair_first_and_second_same_var_this_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TagA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, e1, e1); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add_pair(world, e4, e4, e1); - ecs_entity_t e5 = ecs_new(world, TagA); - ecs_add_pair(world, e5, e1, e5); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($this), $this($this, $this)" - }); - - test_assert(r != NULL); - - int32_t this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, RelA, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e2, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_w_this_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this(ent)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, RelA); - ecs_add(world, ent, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, this_var)); - test_uint(RelA, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, this_var)); - test_uint(RelB, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_w_pair_this_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this(ent, TgtA)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, TgtA); - ecs_add(world, ent, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelB, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, this_var)); - test_uint(RelA, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, this_var)); - test_uint(RelB, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_w_pair_this_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, TgtA); - ecs_add(world, ent, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, this_var)); - test_uint(TgtA, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, this_var)); - test_uint(TgtB, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_w_pair_this_rel_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this(ent, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, TgtA); - ecs_add(world, ent, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelA, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, TgtA, TgtA); - ecs_add_pair(world, ent, TgtB, TgtB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, this_var)); - test_uint(TgtA, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, this_var)); - test_uint(TgtB, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t ent = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this($this), TagA($this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, TagB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, ent, ent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ent, ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ent, ecs_iter_get_var(&it, this_var)); - test_uint(ent, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_pair_this_rel_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t ent = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this($this, $this), TagA($this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, TagB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, ent, ent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, ent, ent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(ent, ent), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ent, ecs_iter_get_var(&it, this_var)); - test_uint(ent, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_this_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t ent = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($this), $this($this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, TagB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, ent, ent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ent, ecs_iter_get_var(&it, this_var)); - test_uint(ent, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_this_src_w_pair_this_rel_tgt_after_write(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t ent = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($this), $this($this, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, ent, TagB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, ent, ent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, ent, ent, ent); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(ent, ent), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ent, ecs_iter_get_var(&it, this_var)); - test_uint(ent, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_src_from_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), RelB($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e1, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add(world, e2, RelB); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_rel_from_src_w_ent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x(e2)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_rel_from_src_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x($y)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_rel_from_src_w_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x($this)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_pair_rel_from_src_w_ent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x(e2, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_pair_rel_from_src_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x($y, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_pair_rel_from_src_w_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x($this, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, e1, TgtA); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_pair_tgt_from_src_w_ent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), RelA(e2, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_pair_tgt_from_src_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), RelA($y, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_pair_tgt_from_src_w_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), RelA($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_pair_rel_tgt_from_src_w_ent(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x(e2, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_pair_rel_tgt_from_src_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x($y, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, RelA, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_constrain_pair_rel_tgt_from_src_w_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x), $x($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e3, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e3, e1, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(2, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, it.entities[0]); - test_uint(e3, it.entities[1]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_set_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - ecs_add(world, ent, RelA); - ecs_add(world, ent, RelB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(ent)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, RelA); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_set_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelB, TgtA); - ecs_add_pair(world, ent, RelC, TgtB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x(ent, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, RelA); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, RelB); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, RelC); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_ent_src_set_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_entity_t ent = ecs_new_entity(world, "ent"); - ecs_add_pair(world, ent, RelA, TgtA); - ecs_add_pair(world, ent, RelA, TgtB); - ecs_add_pair(world, ent, RelB, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA(ent, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, TgtA); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, TgtB); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(ent, ecs_field_src(&it, 1)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, TgtC); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelB); - ecs_entity_t e3 = ecs_new(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e2); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_var_w_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, RelC, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e2); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_var_w_pair_set_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, e1, TgtA); - ecs_add_pair(world, e2, e2, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($x, TgtA)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e2); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_var_w_pair_set_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, RelA, e1); - ecs_add_pair(world, e2, RelA, e2); - ecs_add_pair(world, e3, RelA, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($x, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e2); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_var_w_pair_set_rel_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, e1, e1); - ecs_add_pair(world, e2, e2, e2); - ecs_add_pair(world, e3, RelA, e3); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($x, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e2); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, x_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_entity_t e2 = ecs_new(world, RelB); - ecs_entity_t e3 = ecs_new(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var == 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e2); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this_w_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, RelC, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, TgtA)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e2); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this_w_pair_set_rel(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, e1, TgtA); - ecs_add_pair(world, e2, e2, TgtA); - ecs_add_pair(world, e3, RelA, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this($this, TgtA)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e2); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this_w_pair_set_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, RelA, e1); - ecs_add_pair(world, e2, RelA, e2); - ecs_add_pair(world, e3, RelA, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e2); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this_w_pair_set_rel_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, e1, e1); - ecs_add_pair(world, e2, e2, e2); - ecs_add_pair(world, e3, RelA, e3); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this($this, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e1); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e2); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_set_var(&it, this_var, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this_to_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add(world, e1, TagB); - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_remove(world, e1, TagB); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ TagA }} - }); - - int this_var_id = ecs_rule_find_var(f, "this"); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 0); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this_to_empty_table_w_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add(world, e1, TagA); - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_remove(world, e1, TagA); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ ecs_id(Position) }} - }); - - int this_var_id = ecs_rule_find_var(f, "this"); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 0); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - test_assert(ecs_field(&it, Position, 1) == NULL); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this_to_empty_table_w_component_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add(world, e1, TagA); - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_remove(world, e1, TagA); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ ecs_id(Position), .src.flags = EcsSelf }} - }); - - int this_var_id = ecs_rule_find_var(f, "this"); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 0); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - test_assert(ecs_field(&it, Position, 1) == NULL); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this_to_entiy_in_table(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0].id = ecs_id(Position) - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_1_set_src_this_to_entiy_in_table_self(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = ecs_id(Position), .src.flags = EcsSelf } - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = Foo }, - .terms[1] = { .id = Bar }, - }); - - ecs_entity_t prefab = ecs_new(world, Bar); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_add(world, e1, Bar); - - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_add_pair(world, e3, EcsIsA, prefab); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(prefab, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this_self(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = Foo, .src.flags = EcsSelf }, - .terms[1] = { .id = Bar, .src.flags = EcsSelf }, - }); - - ecs_entity_t prefab = ecs_new(world, Bar); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_add(world, e1, Bar); - - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_add(world, e2, Bar); - - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_add_pair(world, e3, EcsIsA, prefab); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = ecs_id(Position) }, - .terms[1] = { .id = ecs_id(Velocity) }, - }); - - ecs_entity_t prefab = ecs_set(world, 0, Velocity, {3, 4}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_add_pair(world, e3, EcsIsA, prefab); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 2); - test_int(v->y, 3); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 30); - test_int(p->y, 40); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 3); - test_int(v->y, 4); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(prefab, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this_self_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = ecs_id(Position), .src.flags = EcsSelf }, - .terms[1] = { .id = ecs_id(Velocity), .src.flags = EcsSelf }, - }); - - ecs_entity_t prefab = ecs_set(world, 0, Velocity, {4, 5}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - - ecs_entity_t e3 = ecs_set(world, 0, Position, {40, 50}); - ecs_add_pair(world, e3, EcsIsA, prefab); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 2); - test_int(v->y, 3); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this_w_up(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Hello); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = Foo }, - .terms[1] = { .id = Bar }, - .terms[2] = { .id = Hello, .src.flags = EcsUp, .src.trav = EcsChildOf }, - }); - - ecs_entity_t prefab = ecs_new(world, Bar); - ecs_entity_t parent = ecs_new(world, Hello); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_add(world, e1, Bar); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_add(world, e2, Bar); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_add_pair(world, e3, EcsIsA, prefab); - ecs_add_pair(world, e3, EcsChildOf, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(Hello, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(Hello, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(Hello, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(prefab, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this_self_w_up(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Hello); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = Foo, .src.flags = EcsSelf }, - .terms[1] = { .id = Bar, .src.flags = EcsSelf }, - .terms[2] = { .id = Hello, .src.flags = EcsUp, .src.trav = EcsChildOf }, - }); - - ecs_entity_t prefab = ecs_new(world, Bar); - ecs_entity_t parent = ecs_new(world, Hello); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_add(world, e1, Bar); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_add(world, e2, Bar); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_add_pair(world, e3, EcsIsA, prefab); - ecs_add_pair(world, e3, EcsChildOf, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(Hello, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(Hello, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this_component_w_up(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = ecs_id(Position) }, - .terms[1] = { .id = ecs_id(Velocity) }, - .terms[2] = { .id = ecs_id(Mass), .src.flags = EcsUp, .src.trav = EcsChildOf }, - }); - - ecs_entity_t prefab = ecs_set(world, 0, Velocity, {3, 4}); - ecs_entity_t parent = ecs_set(world, 0, Mass, {60}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_add_pair(world, e3, EcsIsA, prefab); - ecs_add_pair(world, e3, EcsChildOf, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - Mass *m = ecs_field(&it, Mass, 3); - test_assert(m != NULL); - test_int(m[0], 60); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 2); - test_int(v->y, 3); - Mass *m = ecs_field(&it, Mass, 3); - test_assert(m != NULL); - test_int(m[0], 60); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 30); - test_int(p->y, 40); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 3); - test_int(v->y, 4); - Mass *m = ecs_field(&it, Mass, 3); - test_assert(m != NULL); - test_int(m[0], 60); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(prefab, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this_self_component_w_up(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = ecs_id(Position), .src.flags = EcsSelf }, - .terms[1] = { .id = ecs_id(Velocity), .src.flags = EcsSelf }, - .terms[2] = { .id = ecs_id(Mass), .src.flags = EcsUp, .src.trav = EcsChildOf }, - }); - - ecs_entity_t prefab = ecs_set(world, 0, Velocity, {3, 4}); - ecs_entity_t parent = ecs_set(world, 0, Mass, {60}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_add_pair(world, e3, EcsIsA, prefab); - ecs_add_pair(world, e3, EcsChildOf, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - Mass *m = ecs_field(&it, Mass, 3); - test_assert(m != NULL); - test_int(m[0], 60); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 2); - test_int(v->y, 3); - Mass *m = ecs_field(&it, Mass, 3); - test_assert(m != NULL); - test_int(m[0], 60); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(parent, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this_w_exclusive_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Hello); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = Foo }, - .terms[1] = { .id = Bar }, - .terms[2] = { .id = ecs_pair(EcsChildOf, EcsWildcard) }, - }); - - ecs_entity_t prefab = ecs_new(world, Bar); - ecs_entity_t parent = ecs_new(world, Hello); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_add(world, e1, Bar); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_add(world, e2, Bar); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_add_pair(world, e3, EcsIsA, prefab); - ecs_add_pair(world, e3, EcsChildOf, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(prefab, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_set_src_this_self_w_exclusive_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Hello); - - ecs_rule_t *f = ecs_rule(world, { - .terms[0] = { .id = Foo, .src.flags = EcsSelf }, - .terms[1] = { .id = ecs_pair(EcsChildOf, EcsWildcard), .src.flags = EcsSelf }, - .terms[2] = { .id = Bar, .src.flags = EcsUp }, - }); - - ecs_entity_t prefab = ecs_new(world, Bar); - ecs_entity_t parent = ecs_new(world, Hello); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_add_pair(world, e1, EcsIsA, prefab); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_add_pair(world, e2, EcsIsA, prefab); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_add(world, e3, Bar); - ecs_add_pair(world, e3, EcsChildOf, parent); - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 2)); - test_uint(Bar, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(prefab, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 2)); - test_uint(Bar, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(prefab, ecs_field_src(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - } - - { - ecs_iter_t it = ecs_rule_iter(world, f); - ecs_iter_set_var(&it, 0, e3); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_1_src_this_var_as_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - ecs_add(world, e4, TagC); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ TagA }} - }); - - int this_var_id = ecs_rule_find_var(f, "this"); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - ecs_entity_t this_var = ecs_iter_get_var(&it, this_var_id); - test_assert(this_var != 0); - test_assert(this_var == e1); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - this_var = ecs_iter_get_var(&it, this_var_id); - test_assert(this_var != 0); - test_assert(this_var == e2); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - this_var = ecs_iter_get_var(&it, this_var_id); - test_assert(this_var == 0); /* more than one entity matches */ - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_1_src_this_var_as_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_table_t *t2 = ecs_get_table(world, e2); - test_assert(t2 != NULL); - test_assert(t2 != t1); - - ecs_table_t *t3 = ecs_get_table(world, e3); - test_assert(t3 != NULL); - test_assert(t3 != t1); - test_assert(t3 != t2); - test_assert(t3 == ecs_get_table(world, e4)); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ TagA }} - }); - - int this_var_id = ecs_rule_find_var(f, "this"); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_rule_iter(world, f); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - ecs_table_t *this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t2); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t3); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_1_src_this_var_as_table_range(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_table_t *t2 = ecs_get_table(world, e2); - test_assert(t2 != NULL); - test_assert(t2 != t1); - - ecs_table_t *t3 = ecs_get_table(world, e3); - test_assert(t3 != NULL); - test_assert(t3 != t1); - test_assert(t3 != t2); - test_assert(t3 == ecs_get_table(world, e4)); - - ecs_rule_t *f = ecs_rule(world, { - .terms = {{ TagA }} - }); - - int this_var_id = ecs_rule_find_var(f, "this"); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_rule_iter(world, f); - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - ecs_table_range_t this_var = ecs_iter_get_var_as_range(&it, this_var_id); - test_assert(this_var.table != NULL); - test_assert(this_var.table == t1); - test_assert(this_var.offset == 0); - test_assert(this_var.count == 1); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - this_var = ecs_iter_get_var_as_range(&it, this_var_id); - test_assert(this_var.table != NULL); - test_assert(this_var.table == t2); - test_assert(this_var.offset == 0); - test_assert(this_var.count == 1); - - test_assert(ecs_rule_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - this_var = ecs_iter_get_var_as_range(&it, this_var_id); - test_assert(this_var.table != NULL); - test_assert(this_var.table == t3); - test_assert(this_var.offset == 0); - test_assert(this_var.count == 2); - - test_assert(!ecs_rule_next(&it)); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void RulesVariables_2_join_by_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - - ecs_entity_t e1 = ecs_new(world, RelA); - ecs_add(world, e1, RelB); - ecs_add_pair(world, e1, TagA, TagB); - - ecs_entity_t e2 = ecs_new(world, RelA); - ecs_entity_t e3 = ecs_new(world, RelB); - ecs_new(world, RelC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this), $x($y), TagA($this, TagB)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(ecs_pair(TagA, TagB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelA, ecs_field_id(&it, 1)); - test_uint(RelA, ecs_field_id(&it, 2)); - test_uint(ecs_pair(TagA, TagB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(ecs_pair(TagA, TagB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(RelB, ecs_field_id(&it, 1)); - test_uint(RelB, ecs_field_id(&it, 2)); - test_uint(ecs_pair(TagA, TagB), ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_join_by_pair_rel_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, RelC); - ECS_TAG(world, TgtA); - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_add_pair(world, e1, RelB, TgtA); - ecs_add(world, e1, TagA); - - ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, RelB, TgtA); - ecs_new_w_pair(world, RelC, TgtA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, TgtA), $x($y, TgtA), TagA" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(RelA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(RelB, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_join_by_pair_tgt_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, RelA); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); - ecs_add_pair(world, e1, RelA, TgtB); - ecs_add(world, e1, TagA); - - ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtA); - ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtB); - ecs_new_w_pair(world, RelA, TgtC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "RelA($this, $x), RelA($y, $x), TagA" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(TgtA, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); - test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(TgtB, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($y), $y($x), TagA($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e2); - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(e2, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e2, ecs_field_src(&it, 3)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(e1, ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_w_this_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this($y), $y($this), TagA($this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e2); - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e2, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_w_var_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this), $this($x), TagA($this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_id(world, e1, e2); - ecs_add_id(world, e2, e1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e2, ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(e1, ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_pair_w_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($y, $z), $x($z, $y), TagA($y)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - int z_var = ecs_rule_find_var(r, "z"); - test_assert(z_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, e3); - ecs_add_pair(world, e3, e1, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e2, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_uint(e3, ecs_iter_get_var(&it, z_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e3, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - test_uint(e2, ecs_iter_get_var(&it, z_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_pair_w_this_var_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$this($y, $z), $this($z, $y), TagA($y)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - int z_var = ecs_rule_find_var(r, "z"); - test_assert(z_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, e3); - ecs_add_pair(world, e3, e1, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e2, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_uint(e3, ecs_iter_get_var(&it, z_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e3, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - test_uint(e2, ecs_iter_get_var(&it, z_var)); - test_uint(e1, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_pair_w_var_this_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $z), $x($z, $this), TagA($this)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - int z_var = ecs_rule_find_var(r, "z"); - test_assert(z_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, e3); - ecs_add_pair(world, e3, e1, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e3, ecs_iter_get_var(&it, z_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(0, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, this_var)); - test_uint(e2, ecs_iter_get_var(&it, z_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_pair_w_var_var_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($y, $this), $x($this, $y), TagA($y)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e2, e1, e3); - ecs_add_pair(world, e3, e1, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_uint(e3, ecs_iter_get_var(&it, this_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 2)); - test_uint(TagA, ecs_field_id(&it, 3)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e3, ecs_field_src(&it, 3)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_pair_ent_var_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Likes); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Likes($x, $y), Likes($y, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, Likes, e2); - ecs_add_pair(world, e1, Likes, e1); - ecs_add_pair(world, e2, Likes, e1); - ecs_add_pair(world, e2, Likes, e3); - ecs_add_pair(world, e3, Likes, e1); - ecs_add_pair(world, e3, Likes, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_pair_ent_this_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Likes); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Likes($this, $y), Likes($y, $this)" - }); - - test_assert(r != NULL); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, Likes, e2); - ecs_add_pair(world, e1, Likes, e1); - ecs_add_pair(world, e2, Likes, e1); - ecs_add_pair(world, e2, Likes, e3); - ecs_add_pair(world, e3, Likes, e1); - ecs_add_pair(world, e3, Likes, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, this_var)); - test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_uint(e3, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e3, ecs_iter_get_var(&it, y_var)); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_cycle_pair_ent_var_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Likes); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Likes($x, $this), Likes($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - int this_var = ecs_rule_find_var(r, "This"); - test_assert(this_var != -1); - test_assert(this_var != 0); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); - } - - ecs_add_pair(world, e1, Likes, e2); - ecs_add_pair(world, e1, Likes, e1); - ecs_add_pair(world, e2, Likes, e1); - ecs_add_pair(world, e2, Likes, e3); - ecs_add_pair(world, e3, Likes, e1); - ecs_add_pair(world, e3, Likes, e2); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 2)); - test_uint(e3, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e3, ecs_iter_get_var(&it, x_var)); - test_uint(e2, ecs_iter_get_var(&it, this_var)); - test_uint(e2, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - test_uint(e1, ecs_iter_get_var(&it, this_var)); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 2)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_uint(e2, ecs_iter_get_var(&it, x_var)); - test_uint(e3, ecs_iter_get_var(&it, this_var)); - test_uint(e3, it.entities[0]); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_0_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, "")); - test_uint(it.variables[x_var].entity, EcsWildcard); - test_uint(it.variables[y_var].entity, EcsWildcard); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_1_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, "x:e1")); - test_uint(it.variables[x_var].entity, e1); - test_uint(it.variables[y_var].entity, EcsWildcard); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_2_vars(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, "x:e1,y:e2")); - test_uint(it.variables[x_var].entity, e1); - test_uint(it.variables[y_var].entity, e2); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_0_var_paren(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, "()")); - test_uint(it.variables[x_var].entity, EcsWildcard); - test_uint(it.variables[y_var].entity, EcsWildcard); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_1_var_paren(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, "(x:e1)")); - test_uint(it.variables[x_var].entity, e1); - test_uint(it.variables[y_var].entity, EcsWildcard); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_2_vars_paren(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, "(x:e1,y:e2)")); - test_uint(it.variables[x_var].entity, e1); - test_uint(it.variables[y_var].entity, e2); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_1_vars_w_path(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t e1 = ecs_new_entity(world, "parent.e1"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, "x:parent.e1")); - test_uint(it.variables[x_var].entity, e1); - test_uint(it.variables[y_var].entity, EcsWildcard); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_missing_close_paren(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_log_set_level(-4); - test_str(NULL, ecs_rule_parse_vars(r, &it, "(x:e1")); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_missing_open_paren(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_log_set_level(-4); - test_str(NULL, ecs_rule_parse_vars(r, &it, "x:e1)")); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_missing_value(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_log_set_level(-4); - test_str(NULL, ecs_rule_parse_vars(r, &it, "x:")); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_0_var_w_spaces(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, " ")); - test_uint(it.variables[x_var].entity, EcsWildcard); - test_uint(it.variables[y_var].entity, EcsWildcard); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_1_var_w_spaces(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, " x : e1 ")); - test_uint(it.variables[x_var].entity, e1); - test_uint(it.variables[y_var].entity, EcsWildcard); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_2_vars_w_spaces(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str("", ecs_rule_parse_vars(r, &it, " x : e1 , y : e2 ")); - test_uint(it.variables[x_var].entity, e1); - test_uint(it.variables[y_var].entity, e2); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_0_var_paren_w_spaces(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str(" ", ecs_rule_parse_vars(r, &it, " ( ) ")); - test_uint(it.variables[x_var].entity, EcsWildcard); - test_uint(it.variables[y_var].entity, EcsWildcard); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_1_var_paren_w_spaces(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str(" ", ecs_rule_parse_vars(r, &it, " ( x : e1 ) ")); - test_uint(it.variables[x_var].entity, e1); - test_uint(it.variables[y_var].entity, EcsWildcard); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_parse_2_vars_paren_w_spaces(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y)" - }); - - test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_str(" ", ecs_rule_parse_vars(r, &it, " ( x : e1 , y : e2 ) ")); - test_uint(it.variables[x_var].entity, e1); - test_uint(it.variables[y_var].entity, e2); - ecs_iter_fini(&it); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_var_count(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y), $x($this), $y($this)" - }); - test_assert(r != NULL); - - test_int(ecs_rule_var_count(r), 3); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_var_name(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y), $x($this), $y($this)" - }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - test_str(ecs_rule_var_name(r, x_var), "x"); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - test_str(ecs_rule_var_name(r, y_var), "y"); - - int this_var = ecs_rule_find_var(r, "this"); - test_assert(this_var != -1); - test_str(ecs_rule_var_name(r, this_var), "this"); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_var_is_entity(void) { - ecs_world_t *world = ecs_init(); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "$x($this, $y), $x($this), $y($this)" - }); - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - test_bool(ecs_rule_var_is_entity(r, x_var), true); - - int y_var = ecs_rule_find_var(r, "y"); - test_assert(y_var != -1); - test_bool(ecs_rule_var_is_entity(r, y_var), true); - - int this_var = ecs_rule_find_var(r, "this"); - test_assert(this_var != -1); - test_bool(ecs_rule_var_is_entity(r, this_var), false); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_no_this_anonymous_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t e = ecs_new_w_id(world, TagA); - test_assert(e != 0); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($_x)" - }); - test_assert(r != NULL); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(ecs_rule_next(&it), true); - test_uint(TagA, ecs_field_id(&it, 1)); - test_bool(ecs_rule_next(&it), false); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_no_this_anonymous_src_w_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_id(world, TagA); - test_assert(e != 0); - ecs_add_pair(world, e, EcsChildOf, parent); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($_x), ChildOf($_x, $Parent)" - }); - test_assert(r != NULL); - - int parent_var = ecs_rule_find_var(r, "Parent"); - test_assert(parent_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(ecs_rule_next(&it), true); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 2)); - test_uint(parent, ecs_iter_get_var(&it, parent_var)); - test_bool(ecs_rule_next(&it), false); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_no_this_anonymous_component_src(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - test_assert(e != 0); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($x)" - }); - test_assert(r != NULL); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(ecs_rule_next(&it), true); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_bool(ecs_rule_next(&it), false); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_no_this_anonymous_component_src_w_pair(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e = ecs_new(world, Position); - test_assert(e != 0); - ecs_add_pair(world, e, EcsChildOf, parent); - - ecs_log_set_level(-4); - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position($_x), ChildOf($_x, $Parent)" - }); - test_assert(r == NULL); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_table_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t p1 = ecs_new(world, TagA); - ecs_new(world, TagA); - ecs_entity_t p3 = ecs_new(world, TagA); - ecs_entity_t p4 = ecs_new(world, TagA); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, p1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, p3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, p4); - ecs_add(world, child_1, TagB); - ecs_add(world, child_4, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA, TagB($this.child)" - }); - - test_assert(r != NULL); - - int32_t child_var = ecs_rule_find_var(r, "this.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p4, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(child_4, ecs_field_src(&it, 2)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_entity_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_id(world); - ecs_entity_t t3 = ecs_new_id(world); - ecs_entity_t t4 = ecs_new_id(world); - - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); - ecs_new_w_pair(world, Rel, t2); - ecs_new_w_pair(world, Rel, t3); - ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, t1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, t3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, t4); - ecs_add(world, child_1, TagB); - ecs_add(world, child_4, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel($x, $this), TagB($this.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "this.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(t1, it.entities[0]); - test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 1)); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(p1, ecs_iter_get_var(&it, x_var)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(t4, it.entities[0]); - test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 1)); - test_uint(p4, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(child_4, ecs_field_src(&it, 2)); - test_uint(p4, ecs_iter_get_var(&it, x_var)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_table(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t p1 = ecs_new(world, TagA); - ecs_new(world, TagA); - ecs_entity_t p3 = ecs_new(world, TagA); - ecs_entity_t p4 = ecs_new(world, TagA); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, p1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, p3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, p4); - ecs_add(world, child_1, TagB); - ecs_add(world, child_4, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($x), TagB($x.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "x.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(p1, ecs_iter_get_var(&it, x_var)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(p4, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(p4, ecs_iter_get_var(&it, x_var)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_entity(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_id(world); - ecs_entity_t t3 = ecs_new_id(world); - ecs_entity_t t4 = ecs_new_id(world); - - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); - ecs_new_w_pair(world, Rel, t2); - ecs_new_w_pair(world, Rel, t3); - ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, t1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, t3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, t4); - ecs_add(world, child_1, TagB); - ecs_add(world, child_4, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Rel, $x), TagB($x.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "x.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(t1, ecs_iter_get_var(&it, x_var)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p4, it.entities[0]); - test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(child_4, ecs_field_src(&it, 2)); - test_uint(t4, ecs_iter_get_var(&it, x_var)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_not_written(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - - ecs_log_set_level(-4); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($this.foo)" - }); - - test_assert(r == NULL); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_table_this_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_COMPONENT(world, Position); - - ecs_entity_t p1 = ecs_new(world, TagA); - ecs_new(world, TagA); - ecs_entity_t p3 = ecs_new(world, TagA); - ecs_entity_t p4 = ecs_new(world, TagA); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, p1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, p3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, p4); - ecs_set(world, child_1, Position, {10, 20}); - ecs_set(world, child_4, Position, {11, 22}); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA, Position($this.child)" - }); - - test_assert(r != NULL); - - int32_t child_var = ecs_rule_find_var(r, "this.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - { - Position *ptr = ecs_field(&it, Position, 2); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p4, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(child_4, ecs_field_src(&it, 2)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - { - Position *ptr = ecs_field(&it, Position, 2); - test_assert(ptr != NULL); - test_int(ptr->x, 11); - test_int(ptr->y, 22); - } - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_entity_this_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_COMPONENT(world, Position); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_id(world); - ecs_entity_t t3 = ecs_new_id(world); - ecs_entity_t t4 = ecs_new_id(world); - - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); - ecs_new_w_pair(world, Rel, t2); - ecs_new_w_pair(world, Rel, t3); - ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, t1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, t3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, t4); - ecs_set(world, child_1, Position, {10, 20}); - ecs_set(world, child_4, Position, {11, 22}); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Rel($x, $this), Position($this.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "this.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(t1, it.entities[0]); - test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 1)); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(p1, ecs_iter_get_var(&it, x_var)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - { - Position *ptr = ecs_field(&it, Position, 2); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(t4, it.entities[0]); - test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 1)); - test_uint(p4, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(child_4, ecs_field_src(&it, 2)); - test_uint(p4, ecs_iter_get_var(&it, x_var)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - { - Position *ptr = ecs_field(&it, Position, 2); - test_assert(ptr != NULL); - test_int(ptr->x, 11); - test_int(ptr->y, 22); - } - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_table_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_COMPONENT(world, Position); - - ecs_entity_t p1 = ecs_new(world, TagA); - ecs_new(world, TagA); - ecs_entity_t p3 = ecs_new(world, TagA); - ecs_entity_t p4 = ecs_new(world, TagA); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, p1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, p3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, p4); - ecs_set(world, child_1, Position, {10, 20}); - ecs_set(world, child_4, Position, {11, 22}); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($x), Position($x.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "x.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(p1, ecs_iter_get_var(&it, x_var)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - { - Position *ptr = ecs_field(&it, Position, 2); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(p4, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(p4, ecs_iter_get_var(&it, x_var)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - { - Position *ptr = ecs_field(&it, Position, 2); - test_assert(ptr != NULL); - test_int(ptr->x, 11); - test_int(ptr->y, 22); - } - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_entity_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_COMPONENT(world, Position); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_id(world); - ecs_entity_t t3 = ecs_new_id(world); - ecs_entity_t t4 = ecs_new_id(world); - - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); - ecs_new_w_pair(world, Rel, t2); - ecs_new_w_pair(world, Rel, t3); - ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, t1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, t3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, t4); - ecs_set(world, child_1, Position, {10, 20}); - ecs_set(world, child_4, Position, {11, 22}); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Rel, $x), Position($x.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "x.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(t1, ecs_iter_get_var(&it, x_var)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - { - Position *ptr = ecs_field(&it, Position, 2); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p4, it.entities[0]); - test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_uint(child_4, ecs_field_src(&it, 2)); - test_uint(t4, ecs_iter_get_var(&it, x_var)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - { - Position *ptr = ecs_field(&it, Position, 2); - test_assert(ptr != NULL); - test_int(ptr->x, 11); - test_int(ptr->y, 22); - } - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_table_two_children(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t p1 = ecs_new_entity(world, "p1"); ecs_add(world, p1, TagA); - ecs_new(world, TagA); - ecs_entity_t p3 = ecs_new_entity(world, "p3"); ecs_add(world, p3, TagA); - ecs_entity_t p4 = ecs_new_entity(world, "p4"); ecs_add(world, p4, TagA); - - ecs_entity_t child_a_1 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_1, EcsChildOf, p1); - ecs_entity_t child_a_3 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_3, EcsChildOf, p3); - ecs_entity_t child_a_4 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_4, EcsChildOf, p4); - ecs_add(world, child_a_1, TagB); - ecs_add(world, child_a_4, TagB); - - ecs_entity_t child_b_1 = ecs_new_entity(world, "child_b"); - ecs_add_pair(world, child_b_1, EcsChildOf, p1); - ecs_entity_t child_b_3 = ecs_new_entity(world, "child_b"); - ecs_add_pair(world, child_b_3, EcsChildOf, p3); - ecs_entity_t child_b_4 = ecs_new_entity(world, "child_b"); - ecs_add_pair(world, child_b_4, EcsChildOf, p4); - ecs_add(world, child_b_1, TagC); - ecs_add(world, child_b_3, TagC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA, TagB($this.child_a), TagC($this.child_b)" - }); - - test_assert(r != NULL); - - int32_t child_a_var = ecs_rule_find_var(r, "this.child_a"); - test_assert(child_a_var != -1); - int32_t child_b_var = ecs_rule_find_var(r, "this.child_b"); - test_assert(child_b_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(child_a_1, ecs_field_src(&it, 2)); - test_uint(child_b_1, ecs_field_src(&it, 3)); - test_uint(child_a_1, ecs_iter_get_var(&it, child_a_var)); - test_uint(child_b_1, ecs_iter_get_var(&it, child_b_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_entity_two_children(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_id(world); - ecs_entity_t t3 = ecs_new_id(world); - ecs_entity_t t4 = ecs_new_id(world); - - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); - ecs_new_w_pair(world, Rel, t2); - ecs_new_w_pair(world, Rel, t3); - ecs_new_w_pair(world, Rel, t4); - - ecs_entity_t child_a_1 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_1, EcsChildOf, t1); - ecs_entity_t child_a_3 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_3, EcsChildOf, t3); - ecs_entity_t child_a_4 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_4, EcsChildOf, t4); - ecs_add(world, child_a_1, TagB); - ecs_add(world, child_a_4, TagB); - - ecs_entity_t child_b_1 = ecs_new_entity(world, "child_b"); - ecs_add_pair(world, child_b_1, EcsChildOf, t1); - ecs_entity_t child_b_3 = ecs_new_entity(world, "child_b"); - ecs_add_pair(world, child_b_3, EcsChildOf, t3); - ecs_entity_t child_b_4 = ecs_new_entity(world, "child_b"); - ecs_add_pair(world, child_b_4, EcsChildOf, t4); - ecs_add(world, child_b_1, TagC); - ecs_add(world, child_b_3, TagC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Rel, $x), TagB($x.child_a), TagC($x.child_b)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_a_var = ecs_rule_find_var(r, "x.child_a"); - test_assert(child_a_var != -1); - int32_t child_b_var = ecs_rule_find_var(r, "x.child_b"); - test_assert(child_b_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(child_a_1, ecs_field_src(&it, 2)); - test_uint(child_b_1, ecs_field_src(&it, 3)); - test_uint(t1, ecs_iter_get_var(&it, x_var)); - test_uint(child_a_1, ecs_iter_get_var(&it, child_a_var)); - test_uint(child_b_1, ecs_iter_get_var(&it, child_b_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_table_same_child_twice(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t p1 = ecs_new_entity(world, "p1"); ecs_add(world, p1, TagA); - ecs_new(world, TagA); - ecs_entity_t p3 = ecs_new_entity(world, "p3"); ecs_add(world, p3, TagA); - ecs_entity_t p4 = ecs_new_entity(world, "p4"); ecs_add(world, p4, TagA); - - ecs_entity_t child_a_1 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_1, EcsChildOf, p1); - ecs_entity_t child_a_3 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_3, EcsChildOf, p3); - ecs_entity_t child_a_4 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_4, EcsChildOf, p4); - ecs_add(world, child_a_1, TagB); - ecs_add(world, child_a_4, TagB); - ecs_add(world, child_a_1, TagC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA, TagB($this.child_a), TagC($this.child_a)" - }); - - test_assert(r != NULL); - - int32_t child_a_var = ecs_rule_find_var(r, "this.child_a"); - test_assert(child_a_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(child_a_1, ecs_field_src(&it, 2)); - test_uint(child_a_1, ecs_field_src(&it, 3)); - test_uint(child_a_1, ecs_iter_get_var(&it, child_a_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_entity_same_child_twice(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_id(world); - ecs_entity_t t3 = ecs_new_id(world); - ecs_entity_t t4 = ecs_new_id(world); - - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); - ecs_new_w_pair(world, Rel, t2); - ecs_new_w_pair(world, Rel, t3); - ecs_new_w_pair(world, Rel, t4); - - ecs_entity_t child_a_1 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_1, EcsChildOf, t1); - ecs_entity_t child_a_3 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_3, EcsChildOf, t3); - ecs_entity_t child_a_4 = ecs_new_entity(world, "child_a"); - ecs_add_pair(world, child_a_4, EcsChildOf, t4); - ecs_add(world, child_a_1, TagB); - ecs_add(world, child_a_4, TagB); - ecs_add(world, child_a_1, TagC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Rel, $x), TagB($x.child_a), TagC($x.child_a)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_a_var = ecs_rule_find_var(r, "x.child_a"); - test_assert(child_a_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(TagC, ecs_field_id(&it, 3)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(child_a_1, ecs_field_src(&it, 2)); - test_uint(child_a_1, ecs_field_src(&it, 3)); - test_uint(t1, ecs_iter_get_var(&it, x_var)); - test_uint(child_a_1, ecs_iter_get_var(&it, child_a_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_table_not(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t p1 = ecs_new(world, TagA); ecs_set_name(world, p1, "p1"); - ecs_entity_t p2 = ecs_new(world, TagA); ecs_set_name(world, p2, "p2"); - ecs_entity_t p3 = ecs_new(world, TagA); ecs_set_name(world, p3, "p3"); - ecs_entity_t p4 = ecs_new(world, TagA); ecs_set_name(world, p4, "p4"); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, p1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, p3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, p4); - ecs_add(world, child_1, TagB); - ecs_add(world, child_4, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($x), !TagB($x.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "x.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(p2, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(p2, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, child_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_uint(p3, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(p3, ecs_iter_get_var(&it, x_var)); - test_uint(child_3, ecs_iter_get_var(&it, child_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_entity_not(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_id(world); - ecs_entity_t t3 = ecs_new_id(world); - ecs_entity_t t4 = ecs_new_id(world); - - ecs_new_w_pair(world, Rel, t1); - ecs_entity_t p2 = ecs_new_w_pair(world, Rel, t2); - ecs_entity_t p3 = ecs_new_w_pair(world, Rel, t3); - ecs_new_w_pair(world, Rel, t4); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, t1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, t3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, t4); - ecs_add(world, child_1, TagB); - ecs_add(world, child_4, TagB); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Rel, $x), !TagB($x.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "x.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p2, it.entities[0]); - test_uint(ecs_pair(Rel, t2), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(t2, ecs_iter_get_var(&it, x_var)); - test_uint(EcsWildcard, ecs_iter_get_var(&it, child_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p3, it.entities[0]); - test_uint(ecs_pair(Rel, t3), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(TagB, ecs_field_id(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(t3, ecs_iter_get_var(&it, x_var)); - test_uint(child_3, ecs_iter_get_var(&it, child_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_table_w_any_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t p1 = ecs_new(world, TagA); - ecs_new(world, TagA); - ecs_entity_t p3 = ecs_new(world, TagA); - ecs_entity_t p4 = ecs_new(world, TagA); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, p1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, p3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, p4); - ecs_add(world, child_1, TagB); - ecs_add(world, child_4, TagB); - ecs_add(world, child_4, TagC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "TagA($x), _($x.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "x.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(p1, ecs_field_src(&it, 1)); - test_uint(EcsWildcard, ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(p1, ecs_iter_get_var(&it, x_var)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(0, it.count); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(p4, ecs_field_src(&it, 1)); - test_uint(EcsWildcard, ecs_field_id(&it, 2)); - test_uint(p4, ecs_iter_get_var(&it, x_var)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_from_entity_w_any_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_id(world); - ecs_entity_t t3 = ecs_new_id(world); - ecs_entity_t t4 = ecs_new_id(world); - - ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); - ecs_new_w_pair(world, Rel, t2); - ecs_new_w_pair(world, Rel, t3); - ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); - - ecs_entity_t child_1 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_1, EcsChildOf, t1); - ecs_entity_t child_3 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_3, EcsChildOf, t3); - ecs_entity_t child_4 = ecs_new_entity(world, "child"); - ecs_add_pair(world, child_4, EcsChildOf, t4); - ecs_add(world, child_1, TagB); - ecs_add(world, child_4, TagB); - ecs_add(world, child_4, TagC); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "(Rel, $x), _($x.child)" - }); - - test_assert(r != NULL); - - int32_t x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int32_t child_var = ecs_rule_find_var(r, "x.child"); - test_assert(child_var != -1); - - { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p1, it.entities[0]); - test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(EcsWildcard, ecs_field_id(&it, 2)); - test_uint(child_1, ecs_field_src(&it, 2)); - test_uint(t1, ecs_iter_get_var(&it, x_var)); - test_uint(child_1, ecs_iter_get_var(&it, child_var)); - - test_bool(true, ecs_rule_next(&it)); - test_uint(1, it.count); - test_uint(p4, it.entities[0]); - test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(EcsWildcard, ecs_field_id(&it, 2)); - test_uint(child_4, ecs_field_src(&it, 2)); - test_uint(t4, ecs_iter_get_var(&it, x_var)); - test_uint(child_4, ecs_iter_get_var(&it, child_var)); - - test_bool(false, ecs_rule_next(&it)); - } - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_as_tag(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_entity_t parent_a = ecs_new(world, Foo); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_entity_t parent_c = ecs_new(world, Foo); - ecs_new(world, Foo); - - ecs_entity_t tag_a = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_a, EcsChildOf, parent_a); - - ecs_entity_t tag_b = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_b, EcsChildOf, parent_b); - - ecs_entity_t tag_c = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_c, EcsChildOf, parent_c); - - ecs_entity_t a = ecs_new_w_id(world, tag_a); - ecs_entity_t c = ecs_new_w_id(world, tag_c); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, $this.Tag($x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int tag_var = ecs_rule_find_var(r, "this.Tag"); - test_assert(tag_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(tag_a, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(a, ecs_field_src(&it, 2)); - test_uint(a, ecs_iter_get_var(&it, x_var)); - test_uint(tag_a, ecs_iter_get_var(&it, tag_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_c, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(tag_c, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(c, ecs_field_src(&it, 2)); - test_uint(c, ecs_iter_get_var(&it, x_var)); - test_uint(tag_c, ecs_iter_get_var(&it, tag_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_as_relationship(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Tgt); - - ecs_entity_t parent_a = ecs_new(world, Foo); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_entity_t parent_c = ecs_new(world, Foo); - ecs_new(world, Foo); - - ecs_entity_t tag_a = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_a, EcsChildOf, parent_a); - - ecs_entity_t tag_b = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_b, EcsChildOf, parent_b); - - ecs_entity_t tag_c = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_c, EcsChildOf, parent_c); - - ecs_entity_t a = ecs_new_w_pair(world, tag_a, Tgt); - ecs_entity_t c = ecs_new_w_pair(world, tag_c, Tgt); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, $this.Tag($x, Tgt)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int tag_var = ecs_rule_find_var(r, "this.Tag"); - test_assert(tag_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(tag_a, Tgt), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(a, ecs_field_src(&it, 2)); - test_uint(a, ecs_iter_get_var(&it, x_var)); - test_uint(tag_a, ecs_iter_get_var(&it, tag_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_c, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(tag_c, Tgt), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(c, ecs_field_src(&it, 2)); - test_uint(c, ecs_iter_get_var(&it, x_var)); - test_uint(tag_c, ecs_iter_get_var(&it, tag_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_as_target(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Foo); - - ecs_entity_t parent_a = ecs_new(world, Foo); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_entity_t parent_c = ecs_new(world, Foo); - ecs_new(world, Foo); - - ecs_entity_t tag_a = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_a, EcsChildOf, parent_a); - - ecs_entity_t tag_b = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_b, EcsChildOf, parent_b); - - ecs_entity_t tag_c = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_c, EcsChildOf, parent_c); - - ecs_entity_t a = ecs_new_w_pair(world, Rel, tag_a); - ecs_entity_t c = ecs_new_w_pair(world, Rel, tag_c); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, Rel($x, $this.Tag)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int tag_var = ecs_rule_find_var(r, "this.Tag"); - test_assert(tag_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, tag_a), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(a, ecs_field_src(&it, 2)); - test_uint(a, ecs_iter_get_var(&it, x_var)); - test_uint(tag_a, ecs_iter_get_var(&it, tag_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_c, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(Rel, tag_c), ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(c, ecs_field_src(&it, 2)); - test_uint(c, ecs_iter_get_var(&it, x_var)); - test_uint(tag_c, ecs_iter_get_var(&it, tag_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_assign_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Foo); - - ecs_entity_t parent_a = ecs_new(world, Foo); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_new(world, Foo); - - ecs_entity_t tag_a = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_a, EcsChildOf, parent_a); - - ecs_entity_t tag_b = ecs_new_entity(world, "Tag"); - ecs_add_pair(world, tag_b, EcsChildOf, parent_b); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, $x == $this.Tag" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int tag_var = ecs_rule_find_var(r, "this.Tag"); - test_assert(tag_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(tag_a, ecs_iter_get_var(&it, x_var)); - test_uint(tag_a, ecs_iter_get_var(&it, tag_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_b, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(tag_b, ecs_iter_get_var(&it, x_var)); - test_uint(tag_b, ecs_iter_get_var(&it, tag_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_eq_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Foo); - - ecs_entity_t parent_a = ecs_new(world, Foo); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_new(world, Foo); - - ecs_entity_t tag_ax = ecs_new_entity(world, "TagX"); - ecs_add_pair(world, tag_ax, EcsChildOf, parent_a); - ecs_entity_t tag_ay = ecs_new_entity(world, "TagY"); - ecs_add_pair(world, tag_ay, EcsChildOf, parent_a); - ecs_entity_t tag_az = ecs_new_entity(world, "TagZ"); - ecs_add_pair(world, tag_az, EcsChildOf, parent_a); - - ecs_entity_t tag_bx = ecs_new_entity(world, "TagX"); - ecs_add_pair(world, tag_bx, EcsChildOf, parent_b); - ecs_entity_t tag_by = ecs_new_entity(world, "TagY"); - ecs_add_pair(world, tag_by, EcsChildOf, parent_b); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, ChildOf($x, $this), $x == $this.TagY" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int tag_var = ecs_rule_find_var(r, "this.TagY"); - test_assert(tag_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_childof(parent_a), ecs_field_id(&it, 2)); - test_uint(tag_ay, ecs_iter_get_var(&it, x_var)); - test_uint(tag_ay, ecs_iter_get_var(&it, tag_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_b, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_childof(parent_b), ecs_field_id(&it, 2)); - test_uint(tag_by, ecs_iter_get_var(&it, x_var)); - test_uint(tag_by, ecs_iter_get_var(&it, tag_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_lookup_neq_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Foo); - - ecs_entity_t parent_a = ecs_new(world, Foo); - ecs_entity_t parent_b = ecs_new(world, Foo); - ecs_new(world, Foo); - - ecs_entity_t tag_ax = ecs_new_entity(world, "TagX"); - ecs_add_pair(world, tag_ax, EcsChildOf, parent_a); - ecs_entity_t tag_ay = ecs_new_entity(world, "TagY"); - ecs_add_pair(world, tag_ay, EcsChildOf, parent_a); - ecs_entity_t tag_az = ecs_new_entity(world, "TagZ"); - ecs_add_pair(world, tag_az, EcsChildOf, parent_a); - - ecs_entity_t tag_bx = ecs_new_entity(world, "TagX"); - ecs_add_pair(world, tag_bx, EcsChildOf, parent_b); - ecs_entity_t tag_by = ecs_new_entity(world, "TagY"); - ecs_add_pair(world, tag_by, EcsChildOf, parent_b); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo, ChildOf($x, $this), $x != $this.TagY" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - int tag_var = ecs_rule_find_var(r, "this.TagY"); - test_assert(tag_var != -1); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_childof(parent_a), ecs_field_id(&it, 2)); - test_uint(tag_ax, ecs_iter_get_var(&it, x_var)); - test_uint(tag_ay, ecs_iter_get_var(&it, tag_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_a, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_childof(parent_a), ecs_field_id(&it, 2)); - test_uint(tag_az, ecs_iter_get_var(&it, x_var)); - test_uint(tag_ay, ecs_iter_get_var(&it, tag_var)); - - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(parent_b, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_childof(parent_b), ecs_field_id(&it, 2)); - test_uint(tag_bx, ecs_iter_get_var(&it, x_var)); - test_uint(tag_by, ecs_iter_get_var(&it, tag_var)); - - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_this(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo" - }); - - test_assert(r != NULL); - test_int(1, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($x)" - }); - - test_assert(r != NULL); - test_int(2, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - test_str("x", ecs_rule_var_name(r, 1)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "*" - }); - - test_assert(r != NULL); - test_int(1, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "_" - }); - - test_assert(r != NULL); - test_int(1, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_var_as_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($this), ChildOf($this, $x)" - }); - - test_assert(r != NULL); - test_int(2, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - test_str("x", ecs_rule_var_name(r, 1)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_this_as_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($this), ChildOf($x, $this)" - }); - - test_assert(r != NULL); - test_int(3, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); // table - test_str("x", ecs_rule_var_name(r, 1)); - test_str("this", ecs_rule_var_name(r, 2)); // entity - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_this_w_lookup_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($this), ChildOf($x, $this.var)" - }); - - test_assert(r != NULL); - test_int(4, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - test_str("x", ecs_rule_var_name(r, 1)); - test_str("this.var", ecs_rule_var_name(r, 2)); - test_str("this", ecs_rule_var_name(r, 3)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_var_w_lookup_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($y), ChildOf($x, $y.var)" - }); - - test_assert(r != NULL); - test_int(4, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - test_str("y", ecs_rule_var_name(r, 1)); - test_str("x", ecs_rule_var_name(r, 2)); - test_str("y.var", ecs_rule_var_name(r, 3)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_anonymous_var_as_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($this), ChildOf($this, $_x)" - }); - - test_assert(r != NULL); - test_int(2, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - test_str("_x", ecs_rule_var_name(r, 1)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_wildcard_as_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($this), ChildOf($this, *)" - }); - - test_assert(r != NULL); - test_int(1, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_check_vars_any_as_tgt(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo($this), ChildOf($this, _)" - }); - - test_assert(r != NULL); - test_int(1, ecs_rule_var_count(r)); - test_str("this", ecs_rule_var_name(r, 0)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_trivial_1_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), ChildOf($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_add(world, e, Foo); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 2)); - test_uint(p, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_trivial_1_var(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), Bar(self), ChildOf($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_add(world, e, Foo); - ecs_add(world, e, Bar); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); - test_uint(p, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_trivial_1_var_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), ChildOf($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set(world, e, Position, {10, 20}); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - test_uint(p, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_trivial_1_var_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), Velocity(self), ChildOf($this, $x)" - }); - - test_assert(r != NULL); - - int x_var = ecs_rule_find_var(r, "x"); - test_assert(x_var != -1); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set(world, e, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 2}); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - test_assert(ecs_field(&it, Velocity, 2) != NULL); - { - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - } - test_uint(p, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_trivial_1_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), ChildOf($this, *)" - }); - - test_assert(r != NULL); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_add(world, e, Foo); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_trivial_1_wildcard(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), Bar(self), ChildOf($this, *)" - }); - - test_assert(r != NULL); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_add(world, e, Foo); - ecs_add(world, e, Bar); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_trivial_1_wildcard_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), ChildOf($this, *)" - }); - - test_assert(r != NULL); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set(world, e, Position, {10, 20}); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_trivial_1_wildcard_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), Velocity(self), ChildOf($this, *)" - }); - - test_assert(r != NULL); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set(world, e, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 2}); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - test_assert(ecs_field(&it, Velocity, 2) != NULL); - { - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - } - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_trivial_1_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), ChildOf($this, _)" - }); - - test_assert(r != NULL); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_add(world, e, Foo); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 2)); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_trivial_1_any(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Foo(self), Bar(self), ChildOf($this, _)" - }); - - test_assert(r != NULL); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_add(world, e, Foo); - ecs_add(world, e, Bar); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(Foo, ecs_field_id(&it, 1)); - test_uint(Bar, ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 3)); - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_1_trivial_1_any_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), ChildOf($this, _)" - }); - - test_assert(r != NULL); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set(world, e, Position, {10, 20}); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 2)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void RulesVariables_2_trivial_1_any_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_rule_t *r = ecs_rule(world, { - .expr = "Position(self), Velocity(self), ChildOf($this, _)" - }); - - test_assert(r != NULL); - - ecs_entity_t p = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set(world, e, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 2}); - - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 3)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - test_assert(ecs_field(&it, Velocity, 2) != NULL); - { - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - } - test_bool(false, ecs_rule_next(&it)); - - ecs_rule_fini(r); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/Run.c b/vendors/flecs/test/addons/src/Run.c index 78afb8b25..600dfe1f4 100644 --- a/vendors/flecs/test/addons/src/Run.c +++ b/vendors/flecs/test/addons/src/Run.c @@ -6,16 +6,16 @@ void Run_setup(void) { static void Iter(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); Velocity *v = NULL; Mass *m = NULL; if (it->field_count >= 2) { - v = ecs_field(it, Velocity, 2); + v = ecs_field(it, Velocity, 1); } if (it->field_count >= 3) { - m = ecs_field(it, Mass, 3); + m = ecs_field(it, Mass, 2); } int *param = it->param; @@ -139,899 +139,6 @@ void Run_run_w_param(void) { ecs_fini(world); } -void Run_run_w_offset(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 2, 0, NULL), 0); - - test_int(ctx.count, 4); - test_int(ctx.invoked, 3); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e3); - test_int(ctx.e[1], e4); - test_int(ctx.e[2], e5); - test_int(ctx.e[3], e6); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_offset_skip_1_archetype(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 3, 0, NULL), 0); - - test_int(ctx.count, 3); - test_int(ctx.invoked, 2); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e4); - test_int(ctx.e[1], e5); - test_int(ctx.e[2], e6); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_offset_skip_1_archetype_plus_one(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 4, 0, NULL), 0); - - test_int(ctx.count, 2); - test_int(ctx.invoked, 2); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e5); - test_int(ctx.e[1], e6); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_offset_skip_2_archetypes(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 5, 0, NULL), 0); - - test_int(ctx.count, 1); - test_int(ctx.invoked, 1); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e6); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.s[0][1], 0); - - const Position *p = ecs_get(world, e6, Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, e6, Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - - ecs_fini(world); -} - -void Run_run_w_limit_skip_1_archetype(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 0, 5, NULL), 0); - - test_int(ctx.count, 5); - test_int(ctx.invoked, 2); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e3); - test_int(ctx.e[3], e4); - test_int(ctx.e[4], e5); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_limit_skip_1_archetype_minus_one(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 0, 4, NULL), 0); - - test_int(ctx.count, 4); - test_int(ctx.invoked, 2); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e3); - test_int(ctx.e[3], e4); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_limit_skip_2_archetypes(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 0, 3, NULL), 0); - - test_int(ctx.count, 3); - test_int(ctx.invoked, 1); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e3); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_offset_1_limit_max(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 1, 5, NULL), 0); - - test_int(ctx.count, 5); - test_int(ctx.invoked, 3); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e2); - test_int(ctx.e[1], e3); - test_int(ctx.e[2], e4); - test_int(ctx.e[3], e5); - test_int(ctx.e[4], e6); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_offset_1_limit_minus_1(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 1, 4, NULL), 0); - - test_int(ctx.count, 4); - test_int(ctx.invoked, 2); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e2); - test_int(ctx.e[1], e3); - test_int(ctx.e[2], e4); - test_int(ctx.e[3], e5); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_offset_2_type_limit_max(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 3, 3, NULL), 0); - - test_int(ctx.count, 3); - test_int(ctx.invoked, 2); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e4); - test_int(ctx.e[1], e5); - test_int(ctx.e[2], e6); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_offset_2_type_limit_minus_1(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 3, 2, NULL), 0); - - test_int(ctx.count, 2); - test_int(ctx.invoked, 1); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e4); - test_int(ctx.e[1], e5); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_limit_1_all_offsets(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - /* Process entities out of order so we can validate whether it is correct */ - test_int( ecs_run_w_filter(world, Iter, 1.0, 0, 1, NULL), 0); - test_int( ecs_run_w_filter(world, Iter, 1.0, 2, 1, NULL), 0); - test_int( ecs_run_w_filter(world, Iter, 1.0, 1, 1, NULL), 0); - test_int( ecs_run_w_filter(world, Iter, 1.0, 3, 1, NULL), 0); - test_int( ecs_run_w_filter(world, Iter, 1.0, 5, 1, NULL), 0); - test_int( ecs_run_w_filter(world, Iter, 1.0, 4, 1, NULL), 0); - - test_int(ctx.count, 6); - test_int(ctx.invoked, 6); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e3); - test_int(ctx.e[2], e2); - test_int(ctx.e[3], e4); - test_int(ctx.e[4], e6); - test_int(ctx.e[5], e5); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_w_offset_out_of_bounds(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 6, 1, NULL), 0); - - test_int(ctx.count, 0); - test_int(ctx.invoked, 0); - - ecs_fini(world); -} - -void Run_run_w_limit_out_of_bounds(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - ECS_ENTITY(world, e1, Position, Velocity); - ECS_ENTITY(world, e2, Position, Velocity); - ECS_ENTITY(world, e3, Position, Velocity); - ECS_ENTITY(world, e4, Position, Velocity, Mass); - ECS_ENTITY(world, e5, Position, Velocity, Mass); - ECS_ENTITY(world, e6, Position, Velocity, Rotation); - ECS_ENTITY(world, e7, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run_w_filter(world, Iter, 1.0, 5, 2, NULL), 0); - - test_int(ctx.count, 1); - test_int(ctx.invoked, 1); - test_int(ctx.system, Iter); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e6); - - int i; - for (i = 0; i < ctx.invoked; i ++) { - test_int(ctx.c[i][0], ecs_id(Position)); - test_int(ctx.s[i][0], 0); - test_int(ctx.c[i][1], ecs_id(Velocity)); - test_int(ctx.s[i][1], 0); - } - - for (i = 0; i < ctx.count; i ++) { - const Position *p = ecs_get(world, ctx.e[i], Position); - test_int(p->x, 10); - test_int(p->y, 20); - const Velocity *v = ecs_get(world, ctx.e[i], Velocity); - test_int(v->x, 30); - test_int(v->y, 40); - } - - ecs_fini(world); -} - -void Run_run_no_match(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ECS_ENTITY(world, e1, Position); - - ECS_SYSTEM(world, Iter, 0, Position, Velocity); - - Probe ctx = {0}; - ecs_set_ctx(world, &ctx, NULL); - - /* Ensure system is not run by ecs_progress */ - ecs_progress(world, 1); - test_int(ctx.invoked, 0); - - test_int( ecs_run(world, Iter, 1.0, NULL), 0); - - test_int(ctx.count, 0); - test_int(ctx.invoked, 0); - - ecs_fini(world); -} - -typedef struct Param { - ecs_entity_t entity; - int count; -} Param; - -static -void TestSubset(ecs_iter_t *it) { - Param *param = it->param; - - int i; - for (i = 0; i < it->count; i ++) { - test_assert(param->entity != it->entities[i]); - param->count ++; - } -} - -static -void TestAll(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - - ecs_entity_t TestSubset = ecs_field_id(it, 2); - - int i; - for (i = 0; i < it->count; i ++) { - Param param = {.entity = it->entities[i], 0}; - ecs_run_w_filter(it->world, TestSubset, 1, it->frame_offset + i + 1, 0, ¶m); - p[i].x += param.count; - } -} - -void Run_run_comb_10_entities_1_type(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_SYSTEM(world, TestSubset, 0, Position); - ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position, TestSubset()); - - int i, ENTITIES = 10; - - const ecs_entity_t *ids = ecs_bulk_new(world, Position, ENTITIES); - - for (i = 0; i < ENTITIES; i ++) { - ecs_set(world, ids[i], Position, {1, 2}); - } - - ecs_progress(world, 0); - - for (i = 0; i < ENTITIES; i ++) { - const Position *p = ecs_get(world, ids[i], Position); - test_int(p->x, ENTITIES - i); - } - - ecs_fini(world); -} - -void Run_run_comb_10_entities_2_types(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ECS_SYSTEM(world, TestSubset, 0, Position); - ECS_SYSTEM(world, TestAll, EcsOnUpdate, Position, TestSubset()); - - int i, ENTITIES = 10; - ecs_entity_t ids_1[5], ids_2[5]; - - for (int i = 0; i < ENTITIES / 2; i ++) { - ids_1[i] = ecs_set(world, 0, Position, {1, 2}); - } - for (int i = 0; i < ENTITIES / 2; i ++) { - ids_2[i] = ecs_set(world, 0, Position, {1, 2}); - ecs_add(world, ids_2[i], Velocity); - } - - ecs_progress(world, 0); - - for (i = 0; i < 5; i ++) { - const Position *p = ecs_get(world, ids_1[i], Position); - test_int(p->x, ENTITIES - i); - - p = ecs_get(world, ids_2[i], Position); - test_int(p->x, ENTITIES - (i + 5)); - } - - ecs_fini(world); -} - static void Interrupt(ecs_iter_t *it) { int i; @@ -1071,9 +178,9 @@ static void AddVelocity(ecs_iter_t *it) { ecs_world_t *world = it->world; - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -1103,8 +210,8 @@ void Run_run_staging(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {30, 40}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); ECS_SYSTEM(world, AddVelocity, 0, Position, Velocity()); @@ -1131,3 +238,28 @@ void Run_run_staging(void) { ecs_fini(world); } + +void Run_run_no_match(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ECS_ENTITY(world, e1, Position); + + ECS_SYSTEM(world, Iter, 0, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + /* Ensure system is not run by ecs_progress */ + ecs_progress(world, 1); + test_int(ctx.invoked, 0); + + test_int( ecs_run(world, Iter, 1.0, NULL), 0); + + test_int(ctx.count, 0); + test_int(ctx.invoked, 0); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/addons/src/Snapshot.c b/vendors/flecs/test/addons/src/Snapshot.c deleted file mode 100644 index 05a720a7e..000000000 --- a/vendors/flecs/test/addons/src/Snapshot.c +++ /dev/null @@ -1,879 +0,0 @@ -#include - -void Snapshot_simple_snapshot(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - Position *p = ecs_get_mut(world, e, Position); - test_int(p->x, 10); - test_int(p->y, 20); - - p->x ++; - p->y ++; - - ecs_snapshot_restore(world, s); - - test_assert(ecs_has(world, e, Position)); - p = ecs_get_mut(world, e, Position); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Snapshot_snapshot_after_new(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_entity_t e2 = ecs_new(world, Position); - test_assert(e2 != 0); - test_assert(ecs_has(world, e2, Position)); - - ecs_snapshot_restore(world, s); - - test_assert(ecs_is_alive(world, e)); - test_assert(!ecs_is_alive(world, e2)); - - test_assert(ecs_has(world, e, Position)); - test_assert(ecs_new(world, 0) == e2); - - ecs_fini(world); -} - -void Snapshot_snapshot_after_delete(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_delete(world, e); - test_assert(!ecs_is_alive(world, e)); - - ecs_snapshot_restore(world, s); - - test_assert(ecs_is_alive(world, e)); - test_assert(ecs_has(world, e, Position)); - - const Position *p = ecs_get(world, e, Position); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Snapshot_snapshot_after_new_type(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - - ecs_snapshot_restore(world, s); - - test_assert(ecs_is_alive(world, e)); - test_assert(!ecs_is_alive(world, e2)); - - test_assert(ecs_has(world, e, Position)); - - ecs_fini(world); -} - -void Snapshot_snapshot_after_add(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_add(world, e, Velocity); - test_assert(ecs_has(world, e, Velocity)); - - ecs_snapshot_restore(world, s); - - test_assert(ecs_has(world, e, Position)); - test_assert(!ecs_has(world, e, Velocity)); - - ecs_fini(world); -} - -void Snapshot_snapshot_after_remove(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_new(world, Position); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_add(world, e, Velocity); - test_assert(ecs_has(world, e, Velocity)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_remove(world, e, Velocity); - test_assert(!ecs_has(world, e, Velocity)); - - ecs_snapshot_restore(world, s); - - test_assert(ecs_has(world, e, Position)); - - ecs_fini(world); -} - -void Snapshot_snapshot_w_include_filter(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - test_assert(e1 != 0); - test_assert(ecs_has(world, e1, Position)); - - ecs_entity_t e2 = ecs_set(world, 0, Position, {15, 25}); - test_assert(e2 != 0); - ecs_add(world, e2, Velocity); - test_assert(ecs_has(world, e2, Position)); - test_assert(ecs_has(world, e2, Velocity)); - - ecs_entity_t e3 = ecs_set(world, 0, Velocity, {25, 35}); - test_assert(e3 != 0); - test_assert(ecs_has(world, e3, Velocity)); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_snapshot_t *s = ecs_snapshot_take_w_iter(&it); - - Position *p = ecs_get_mut(world, e1, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - p->x ++; - p->y ++; - - p = ecs_get_mut(world, e2, Position); - test_assert(p != NULL); - test_int(p->x, 15); - test_int(p->y, 25); - - p->x ++; - p->y ++; - - Velocity *v = ecs_get_mut(world, e3, Velocity); - test_assert(v != NULL); - test_int(v->x, 25); - test_int(v->y, 35); - - v->x ++; - v->y ++; - - ecs_snapshot_restore(world, s); - - /* Restored */ - p = ecs_get_mut(world, e1, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - /* Restored */ - p = ecs_get_mut(world, e2, Position); - test_assert(p != NULL); - test_int(p->x, 15); - test_int(p->y, 25); - - /* Not restored */ - v = ecs_get_mut(world, e3, Velocity); - test_assert(v != NULL); - test_int(v->x, 26); - test_int(v->y, 36); - - test_assert(ecs_new(world, 0) > e3); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Snapshot_snapshot_w_exclude_filter(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - test_assert(e1 != 0); - test_assert(ecs_has(world, e1, Position)); - - ecs_entity_t e2 = ecs_set(world, 0, Position, {15, 25}); - test_assert(e2 != 0); - ecs_add(world, e2, Velocity); - test_assert(ecs_has(world, e2, Position)); - test_assert(ecs_has(world, e2, Velocity)); - - ecs_entity_t e3 = ecs_set(world, 0, Velocity, {25, 35}); - test_assert(e3 != 0); - test_assert(ecs_has(world, e3, Velocity)); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position), .oper = EcsNot }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_snapshot_t *s = ecs_snapshot_take_w_iter(&it); - - Position *p = ecs_get_mut(world, e1, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - p->x ++; - p->y ++; - - p = ecs_get_mut(world, e2, Position); - test_assert(p != NULL); - test_int(p->x, 15); - test_int(p->y, 25); - - p->x ++; - p->y ++; - - Velocity *v = ecs_get_mut(world, e3, Velocity); - test_assert(v != NULL); - test_int(v->x, 25); - test_int(v->y, 35); - - v->x ++; - v->y ++; - - ecs_snapshot_restore(world, s); - - /* Not restored */ - p = ecs_get_mut(world, e1, Position); - test_assert(p != NULL); - test_int(p->x, 11); - test_int(p->y, 21); - - /* Not restored */ - p = ecs_get_mut(world, e2, Position); - test_assert(p != NULL); - test_int(p->x, 16); - test_int(p->y, 26); - - /* Restored */ - v = ecs_get_mut(world, e3, Velocity); - test_assert(v != NULL); - test_int(v->x, 25); - test_int(v->y, 35); - - test_assert(ecs_new(world, 0) > e3); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Snapshot_snapshot_w_filter_after_new(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {1, 2}); - test_assert(e1 != 0); - test_assert(ecs_has(world, e1, Position)); - - ecs_entity_t e2 = ecs_set(world, 0, Velocity, {3, 4}); - test_assert(e2 != 0); - test_assert(ecs_has(world, e2, Velocity)); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_snapshot_t *s = ecs_snapshot_take_w_iter(&it); - - ecs_set(world, e1, Position, {5, 6}); - ecs_set(world, e2, Velocity, {7, 8}); - - ecs_entity_t e3 = ecs_set(world, 0, Position, {33, 44}); - test_assert(e3 != 0); - test_assert(ecs_has(world, e3, Position)); - - ecs_entity_t e4 = ecs_set(world, 0, Velocity, {34, 45}); - test_assert(e4 != 0); - test_assert(ecs_has(world, e4, Velocity)); - - ecs_snapshot_restore(world, s); - - test_assert(ecs_has(world, e1, Position)); - const Position *p = ecs_get(world, e1, Position); - test_assert(p != NULL); - test_int(p->x, 1); - test_int(p->y, 2); - - test_assert(ecs_has(world, e2, Velocity)); - const Velocity *v = ecs_get(world, e2, Velocity); - test_assert(v != NULL); - test_int(v->x, 7); - test_int(v->y, 8); - - test_assert(ecs_has(world, e3, Position)); - p = ecs_get(world, e3, Position); - test_assert(p != NULL); - test_int(p->x, 33); - test_int(p->y, 44); - - test_assert(ecs_has(world, e4, Velocity)); - v = ecs_get(world, e4, Velocity); - test_assert(p != NULL); - test_int(v->x, 34); - test_int(v->y, 45); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Snapshot_snapshot_w_filter_after_delete(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {1, 2}); - test_assert(e1 != 0); - test_assert(ecs_has(world, e1, Position)); - - ecs_entity_t e2 = ecs_set(world, 0, Velocity, {3, 4}); - test_assert(e2 != 0); - test_assert(ecs_has(world, e2, Velocity)); - - ecs_entity_t e3 = ecs_set(world, 0, Position, {1, 2}); - test_assert(e3 != 0); - test_assert(ecs_has(world, e3, Position)); - - ecs_entity_t e4 = ecs_set(world, 0, Velocity, {3, 4}); - test_assert(e4 != 0); - test_assert(ecs_has(world, e4, Velocity)); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_snapshot_t *s = ecs_snapshot_take_w_iter(&it); - - ecs_delete(world, e3); - ecs_delete(world, e4); - - test_assert(!ecs_is_alive(world, e3)); - test_assert(!ecs_is_alive(world, e4)); - - ecs_snapshot_restore(world, s); - - test_assert(ecs_is_alive(world, e1)); - test_assert(ecs_is_alive(world, e2)); - test_assert(ecs_is_alive(world, e3)); - test_assert(!ecs_is_alive(world, e4)); - - test_assert(ecs_has(world, e1, Position)); - test_assert(ecs_has(world, e2, Velocity)); - test_assert(ecs_has(world, e3, Position)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Snapshot_snapshot_free_empty(void) { - ecs_world_t *world = ecs_init(); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - test_assert(s != NULL); - - ecs_snapshot_free(s); - - ecs_fini(world); -} - -void Snapshot_snapshot_free(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - test_assert( ecs_new(world, Position) != 0); - test_assert( ecs_new(world, Velocity) != 0); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - test_assert(s != NULL); - - ecs_snapshot_free(s); - - ecs_fini(world); -} - -void Snapshot_snapshot_free_filtered(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - test_assert( ecs_new(world, Position) != 0); - test_assert( ecs_new(world, Velocity) != 0); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_snapshot_t *s = ecs_snapshot_take_w_iter(&it); - test_assert(s != NULL); - - ecs_snapshot_free(s); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Snapshot_snapshot_free_filtered_w_dtor(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .name = "e1" - }); - - ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .name = "e2" - }); - - ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .name = "e3" - }); - - ecs_add(world, e1, Position); - ecs_add(world, e2, Position); - ecs_add(world, e3, Position); - - ecs_add(world, e1, Velocity); - ecs_add(world, e2, Velocity); - ecs_add(world, e3, Velocity); - - ecs_add(world, e3, Mass); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_snapshot_t *s = ecs_snapshot_take_w_iter(&it); - test_assert(s != NULL); - - ecs_snapshot_free(s); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -static bool invoked = false; - -static -void Dummy(ecs_iter_t *it) { - invoked = true; -} - -void Snapshot_snapshot_activate_table_w_filter(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position); - - ecs_entity_t e = ecs_set(world, 0, Position, {0, 0}); - test_assert(e != 0); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_snapshot_t *s = ecs_snapshot_take_w_iter(&it); - - ecs_snapshot_restore(world, s); - - test_assert(ecs_has(world, e, Position)); - - ecs_progress(world, 0); - test_bool(invoked, true); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Snapshot_snapshot_copy(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - ecs_iter_t it = ecs_snapshot_iter(s); - ecs_snapshot_t *s_copy = ecs_snapshot_take_w_iter(&it); - ecs_snapshot_free(s); - - Position *p = ecs_get_mut(world, e, Position); - test_int(p->x, 10); - test_int(p->y, 20); - - p->x ++; - p->y ++; - - ecs_snapshot_restore(world, s_copy); - - test_assert(ecs_has(world, e, Position)); - p = ecs_get_mut(world, e, Position); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Snapshot_snapshot_get_ref_after_restore(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_ref_t ref = ecs_ref_init(world, e, Position); - const Position *p = ecs_ref_get(world, &ref, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - Position *p_mut = ecs_get_mut(world, e, Position); - test_int(p_mut->x, 10); - test_int(p_mut->y, 20); - - p_mut->x ++; - p_mut->y ++; - - ecs_snapshot_restore(world, s); - - test_assert(ecs_has(world, e, Position)); - p = ecs_ref_get(world, &ref, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - ecs_fini(world); -} - -void Snapshot_new_after_snapshot(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_snapshot_restore(world, s); - - ecs_entity_t e2 = ecs_new(world, Position); - test_assert(e2 != 0); - test_assert(ecs_has(world, e2, Position)); - - ecs_add(world, e2, Velocity); - test_assert(ecs_has(world, e2, Velocity)); - - ecs_fini(world); -} - -void Snapshot_add_after_snapshot(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_snapshot_restore(world, s); - - ecs_add(world, e, Velocity); - test_assert(ecs_has(world, e, Velocity)); - - ecs_fini(world); -} - -void Snapshot_delete_after_snapshot(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_snapshot_restore(world, s); - - ecs_delete(world, e); - - test_assert(!ecs_is_alive(world, e)); - - ecs_fini(world); -} - -void Snapshot_new_empty_after_snapshot(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_snapshot_restore(world, s); - - ecs_entity_t e2 = ecs_new(world, 0); - test_assert(e2 != 0); - - ecs_fini(world); -} - -void Snapshot_set_after_snapshot(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_snapshot_restore(world, s); - - ecs_entity_t e2 = ecs_new(world, 0); - test_assert(e2 != 0); - - ecs_set_name(world, e2, "e2"); - test_assert(ecs_has_pair(world, e2, ecs_id(EcsIdentifier), EcsName)); - test_str(ecs_get_name(world, e2), "e2"); - - ecs_fini(world); -} - -void Snapshot_restore_recycled(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_delete(world, e); - - ecs_snapshot_restore(world, s); - - ecs_entity_t e2 = ecs_new(world, 0); - test_assert(e2 != 0); - test_assert(e != e2); - - ecs_fini(world); -} - -static -void SetP(ecs_iter_t *it) { - int i; - for (i = 0; i < it->count; i ++) { - ecs_set_name(it->world, 0, "e2"); - } -} - -void Snapshot_snapshot_w_new_in_onset(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - test_assert(e != 0); - test_assert(ecs_has(world, e, Position)); - - ECS_OBSERVER(world, SetP, EcsOnSet, Position); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_snapshot_restore(world, s); - - ecs_entity_t e2 = ecs_lookup(world, "e2"); - test_assert(e2 != 0); - - ecs_entity_t e3 = ecs_set_name(world, 0, "e3"); - test_assert(e3 != 0); - test_assert(e3 > e2); - test_str(ecs_get_name(world, e3), "e3"); - - ecs_fini(world); -} - -static ecs_entity_t v_entity = 0; - -static -void CreateV(ecs_iter_t *it) { - ecs_entity_t ecs_id(Velocity) = ecs_field_id(it, 2); - - int i; - for (i = 0; i < it->count; i ++) { - v_entity = ecs_set(it->world, 0, Velocity, {3, 4}); - } -} - -void Snapshot_snapshot_w_new_in_onset_in_snapshot_table(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Velocity, {1, 2}); - - ECS_OBSERVER(world, CreateV, EcsOnSet, Position, Velocity()); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - ecs_snapshot_restore(world, s); - - const Velocity *v = ecs_get(world, e2, Velocity); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - - v = ecs_get(world, v_entity, Velocity); - test_assert(v != NULL); - test_int(v->x, 3); - test_int(v->y, 4); - - ecs_fini(world); -} - -void Snapshot_snapshot_from_stage(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {3, 4}); - - ecs_readonly_begin(world); - - ecs_world_t *stage = ecs_get_stage(world, 0); - - ecs_snapshot_t *s = ecs_snapshot_take(stage); - - ecs_delete(stage, e1); - ecs_delete(stage, e2); - - ecs_readonly_end(world); - - test_assert(!ecs_is_alive(world, e1)); - test_assert(!ecs_is_alive(world, e2)); - - ecs_snapshot_restore(world, s); - - const Position *p = ecs_get(world, e1, Position); - test_assert(p != NULL); - test_int(p->x, 1); - test_int(p->y, 2); - - p = ecs_get(world, e2, Position); - test_assert(p != NULL); - test_int(p->x, 3); - test_int(p->y, 4); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/addons/src/Stats.c b/vendors/flecs/test/addons/src/Stats.c index e2d0c2742..3ac52285a 100644 --- a/vendors/flecs/test/addons/src/Stats.c +++ b/vendors/flecs/test/addons/src/Stats.c @@ -27,7 +27,6 @@ void Stats_get_pipeline_stats_before_progress_mini_world(void) { test_bool(ecs_pipeline_stats_get(world, pipeline, &stats), false); test_assert(ecs_vec_count(&stats.systems) == 0); - test_assert(ecs_map_count(&stats.system_stats) == 0); ecs_fini(world); } @@ -42,7 +41,6 @@ void Stats_get_pipeline_stats_before_progress(void) { test_bool(ecs_pipeline_stats_get(world, pipeline, &stats), true); test_assert(ecs_vec_count(&stats.systems) == 0); - test_assert(ecs_map_count(&stats.system_stats) != 0); /* Inactive systems */ ecs_pipeline_stats_fini(&stats); @@ -62,8 +60,6 @@ void Stats_get_pipeline_stats_after_progress_no_systems(void) { test_int(ecs_vec_count(&stats.systems), 1); test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[0], 0); /* merge */ - - test_assert(ecs_map_count(&stats.system_stats) != 0); /* Inactive systems */ ecs_pipeline_stats_fini(&stats); @@ -89,19 +85,10 @@ void Stats_get_pipeline_stats_after_progress_1_system(void) { test_int(ecs_vec_count(&stats.systems), 2); test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[0], ecs_id(FooSys)); test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[1], 0); /* merge */ - - test_assert(ecs_map_count(&stats.system_stats) != 0); - ecs_system_stats_t *sys_stats = ecs_map_get_deref( - &stats.system_stats, ecs_system_stats_t, ecs_id(FooSys)); - test_assert(sys_stats != NULL); - test_int(sys_stats->query.t, 1); - test_int(sys_stats->invoke_count.counter.value[1], 1); ecs_progress(world, 0); test_bool(ecs_pipeline_stats_get(world, pipeline, &stats), true); - test_int(sys_stats->query.t, 2); - test_int(sys_stats->invoke_count.counter.value[2], 2); ecs_pipeline_stats_fini(&stats); @@ -124,19 +111,10 @@ void Stats_get_pipeline_stats_after_progress_1_inactive_system(void) { test_int(ecs_vec_count(&stats.systems), 1); test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[0], 0); /* merge */ - - test_assert(ecs_map_count(&stats.system_stats) != 0); - ecs_system_stats_t *sys_stats = ecs_map_get_deref( - &stats.system_stats, ecs_system_stats_t, ecs_id(FooSys)); - test_assert(sys_stats != NULL); - test_int(sys_stats->query.t, 1); - test_int(sys_stats->invoke_count.counter.value[1], 0); ecs_progress(world, 0); test_bool(ecs_pipeline_stats_get(world, pipeline, &stats), true); - test_int(sys_stats->query.t, 2); - test_int(sys_stats->invoke_count.counter.value[2], 0); ecs_pipeline_stats_fini(&stats); @@ -161,30 +139,12 @@ void Stats_get_pipeline_stats_after_progress_2_systems(void) { test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[0], ecs_id(FooSys)); test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[1], ecs_id(BarSys)); test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[2], 0); /* merge */ - - test_assert(ecs_map_count(&stats.system_stats) != 0); - ecs_system_stats_t *sys_foo_stats = ecs_map_get_deref( - &stats.system_stats, ecs_system_stats_t, ecs_id(FooSys)); - test_assert(sys_foo_stats != NULL); - test_int(sys_foo_stats->query.t, 1); - test_int(sys_foo_stats->invoke_count.counter.value[1], 1); - - ecs_system_stats_t *sys_bar_stats = ecs_map_get_deref( - &stats.system_stats, ecs_system_stats_t, ecs_id(BarSys)); - test_assert(sys_bar_stats != NULL); - test_int(sys_bar_stats->query.t, 1); - test_int(sys_bar_stats->invoke_count.counter.value[1], 1); ecs_progress(world, 0); ecs_run(world, ecs_id(BarSys), 0, 0); test_bool(ecs_pipeline_stats_get(world, pipeline, &stats), true); - test_int(sys_foo_stats->query.t, 2); - test_int(sys_foo_stats->invoke_count.counter.value[2], 2); - - test_int(sys_bar_stats->query.t, 2); - test_int(sys_bar_stats->invoke_count.counter.value[2], 3); ecs_pipeline_stats_fini(&stats); @@ -196,7 +156,7 @@ void Stats_get_pipeline_stats_after_progress_2_systems_one_merge(void) { ECS_COMPONENT(world, Position); - ecs_new(world, Position); // Make sure systems are active + ecs_new_w(world, Position); // Make sure systems are active ECS_SYSTEM(world, FooSys, EcsOnUpdate, [out] Position()); ECS_SYSTEM(world, BarSys, EcsOnUpdate, Position); @@ -214,28 +174,10 @@ void Stats_get_pipeline_stats_after_progress_2_systems_one_merge(void) { test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[1], 0); /* merge */ test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[2], ecs_id(BarSys)); test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[3], 0); /* merge */ - - test_assert(ecs_map_count(&stats.system_stats) != 0); - ecs_system_stats_t *sys_foo_stats = ecs_map_get_deref( - &stats.system_stats, ecs_system_stats_t, ecs_id(FooSys)); - test_assert(sys_foo_stats != NULL); - test_int(sys_foo_stats->query.t, 1); - test_int(sys_foo_stats->invoke_count.counter.value[1], 1); - - ecs_system_stats_t *sys_bar_stats = ecs_map_get_deref( - &stats.system_stats, ecs_system_stats_t, ecs_id(BarSys)); - test_assert(sys_bar_stats != NULL); - test_int(sys_bar_stats->query.t, 1); - test_int(sys_bar_stats->invoke_count.counter.value[1], 1); ecs_progress(world, 0); test_bool(ecs_pipeline_stats_get(world, pipeline, &stats), true); - test_int(sys_foo_stats->query.t, 2); - test_int(sys_foo_stats->invoke_count.counter.value[2], 2); - - test_int(sys_bar_stats->query.t, 2); - test_int(sys_bar_stats->invoke_count.counter.value[2], 2); ecs_pipeline_stats_fini(&stats); @@ -252,7 +194,6 @@ void Stats_get_pipeline_stats_w_task_system(void) { ecs_pipeline_stats_t stats = {0}; test_bool(ecs_pipeline_stats_get(world, pipeline, &stats), true); - test_assert(ecs_map_count(&stats.system_stats) != 0); /* Inactive systems */ test_int(ecs_vec_count(&stats.systems), 0); ecs_pipeline_stats_fini(&stats); @@ -270,7 +211,7 @@ void Stats_get_entity_count(void) { float prev = count = stats.entities.count.gauge.avg[stats.t]; test_assert(count != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_world_stats_get(world, &stats); count = stats.entities.count.gauge.avg[stats.t]; @@ -296,7 +237,7 @@ void Stats_get_not_alive_entity_count(void) { float prev = count = stats.entities.not_alive_count.gauge.avg[stats.t]; test_int(count, 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); prev = count; ecs_world_stats_get(world, &stats); @@ -312,3 +253,33 @@ void Stats_get_not_alive_entity_count(void) { ecs_fini(world); } + +void Stats_progress_stats_systems(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsStats); + + for (int i = 0; i < 60 * 60; i ++) { + ecs_progress(world, 0.016); + } + + test_assert(true); // used to catch memory leaks + + ecs_fini(world); +} + +void Stats_progress_stats_systems_w_empty_table_flag(void) { + ecs_world_t *world = ecs_mini(); + + ecs_set_default_query_flags(world, EcsQueryMatchEmptyTables); + + ECS_IMPORT(world, FlecsStats); + + for (int i = 0; i < 60 * 60; i ++) { + ecs_progress(world, 0.016); + } + + test_assert(true); // used to catch memory leaks + + ecs_fini(world); +} diff --git a/vendors/flecs/test/addons/src/SystemCascade.c b/vendors/flecs/test/addons/src/SystemCascade.c index ef59b9eae..face786bb 100644 --- a/vendors/flecs/test/addons/src/SystemCascade.c +++ b/vendors/flecs/test/addons/src/SystemCascade.c @@ -2,10 +2,10 @@ static void Iter(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Position *p_parent = ecs_field(it, Position, 2); + Position *p = ecs_field(it, Position, 0); + Position *p_parent = ecs_field(it, Position, 1); - test_assert(!p_parent || !ecs_field_is_self(it, 2)); + test_assert(!p_parent || !ecs_field_is_self(it, 1)); probe_iter(it); @@ -31,10 +31,9 @@ void SystemCascade_cascade_depth_1(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(parent|cascade)); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); ecs_set(world, e1, Position, {1, 2}); @@ -99,10 +98,9 @@ void SystemCascade_cascade_depth_2(void) { ECS_ENTITY(world, e5, Position); ECS_ENTITY(world, e6, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(parent|cascade)); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); ecs_set(world, e1, Position, {1, 2}); @@ -180,10 +178,9 @@ void SystemCascade_cascade_depth_2_new_syntax(void) { ECS_ENTITY(world, e5, Position); ECS_ENTITY(world, e6, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade(ChildOf))); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade ChildOf)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); ecs_set(world, e1, Position, {1, 2}); @@ -251,10 +248,10 @@ void SystemCascade_cascade_depth_2_new_syntax(void) { static void AddParent(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Position *p_parent = ecs_field(it, Position, 2); + Position *p = ecs_field(it, Position, 0); + Position *p_parent = ecs_field(it, Position, 1); - test_assert(!p_parent || !ecs_field_is_self(it, 2)); + test_assert(!p_parent || !ecs_field_is_self(it, 1)); probe_iter(it); @@ -277,13 +274,12 @@ void SystemCascade_add_after_match(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(parent|cascade)); + ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(cascade)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = AddParent, - .query.filter.instanced = true + .entity = AddParent }); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); @@ -298,7 +294,7 @@ void SystemCascade_add_after_match(void) { ecs_progress(world, 1); /* Before adding Position to parent, it wasn't being considered for the - * column(parent|cascade), so tables could have been ordered randomly. Make sure + * column(cascade), so tables could have been ordered randomly. Make sure * that queries can handle changes to depth after all tables are matched */ ecs_set(world, parent, Position, {1, 2}); @@ -354,13 +350,12 @@ void SystemCascade_adopt_after_match(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(parent|cascade)); + ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(cascade)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = AddParent, - .query.filter.instanced = true + .entity = AddParent }); - ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {1, 2})); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); @@ -426,10 +421,9 @@ void SystemCascade_custom_relation_cascade_depth_1(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade(Rel))); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade Rel)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); ecs_set(world, e1, Position, {1, 2}); @@ -495,10 +489,9 @@ void SystemCascade_custom_relation_cascade_depth_2(void) { ECS_ENTITY(world, e5, Position); ECS_ENTITY(world, e6, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade(Rel))); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade Rel)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); ecs_set(world, e1, Position, {1, 2}); @@ -575,13 +568,12 @@ void SystemCascade_custom_relation_add_after_match(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(cascade(Rel))); + ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(cascade Rel)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = AddParent, - .query.filter.instanced = true + .entity = AddParent }); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); @@ -596,7 +588,7 @@ void SystemCascade_custom_relation_add_after_match(void) { ecs_progress(world, 1); /* Before adding Position to parent, it wasn't being considered for the - * column(parent|cascade), so tables could have been ordered randomly. Make sure + * column(cascade), so tables could have been ordered randomly. Make sure * that queries can handle changes to depth after all tables are matched */ ecs_set(world, parent, Position, {1, 2}); @@ -653,13 +645,12 @@ void SystemCascade_custom_relation_adopt_after_match(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(cascade(Rel))); + ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(cascade Rel)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = AddParent, - .query.filter.instanced = true + .entity = AddParent }); - ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {1, 2})); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); diff --git a/vendors/flecs/test/addons/src/SystemManual.c b/vendors/flecs/test/addons/src/SystemManual.c index 396446fbd..00c6af1f1 100644 --- a/vendors/flecs/test/addons/src/SystemManual.c +++ b/vendors/flecs/test/addons/src/SystemManual.c @@ -6,16 +6,16 @@ void SystemManual_setup(void) { static void Iter(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); Velocity *v = NULL; Mass *m = NULL; if (it->field_count >= 2) { - v = ecs_field(it, Velocity, 2); + v = ecs_field(it, Velocity, 1); } if (it->field_count >= 3) { - m = ecs_field(it, Mass, 3); + m = ecs_field(it, Mass, 2); } probe_iter(it); @@ -82,57 +82,10 @@ void SystemManual_1_type_1_component(void) { ecs_fini(world); } -static int normal_count; - -static -void NormalSystem(ecs_iter_t *it) { - normal_count ++; -} - -static -void AddVelocity(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); - - int i; - for (i = 0; i < it->count; i ++) { - ecs_add(it->world, it->entities[i], Velocity); - } -} - -void SystemManual_no_automerge(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ECS_SYSTEM(world, AddVelocity, 0, Position, Velocity()); - - ECS_ENTITY(world, e1, Position); - - ecs_set_automerge(world, false); - - ecs_readonly_begin(world); - ecs_world_t *stage = ecs_get_stage(world, 0); - - ecs_run(stage, AddVelocity, 1, NULL); - - test_assert(!ecs_has(stage, e1, Velocity)); - - ecs_readonly_end(world); - - test_assert(!ecs_has(world, e1, Velocity)); - - ecs_merge(world); - - test_assert(ecs_has(world, e1, Velocity)); - - ecs_fini(world); -} - static int dummy_ran = 0; void DummySystem(ecs_iter_t *it) { - ecs_entity_t Tag = ecs_field_id(it, 1); + ecs_entity_t Tag = ecs_field_id(it, 0); ecs_add_id(it->world, Tag, Tag); dummy_ran ++; } diff --git a/vendors/flecs/test/addons/src/SystemMisc.c b/vendors/flecs/test/addons/src/SystemMisc.c index 5fa50ade8..4ea33e3ee 100644 --- a/vendors/flecs/test/addons/src/SystemMisc.c +++ b/vendors/flecs/test/addons/src/SystemMisc.c @@ -5,7 +5,7 @@ int32_t dummy_invoked = false; static void Dummy(ecs_iter_t *it) { - dummy_invoked = true; + dummy_invoked ++; probe_iter(it); } @@ -47,20 +47,6 @@ void SystemMisc_invalid_entity_without_id(void) { ecs_fini(world); } -void SystemMisc_invalid_empty_without_id(void) { - install_test_abort(); - - ecs_world_t *world = ecs_init(); - - test_expect_abort(); - - ECS_SYSTEM(world, Dummy, EcsOnUpdate, $This); - - test_assert(true); - - ecs_fini(world); -} - void SystemMisc_invalid_empty_element(void) { install_test_abort(); @@ -205,7 +191,7 @@ void SystemMisc_invalid_null_string(void) { ecs_world_t *world = ecs_init(); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "Dummy", .add = {ecs_dependson(EcsOnUpdate)}}), + .entity = ecs_entity(world, { .name = "Dummy", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), .callback = Dummy }); @@ -220,8 +206,8 @@ void SystemMisc_invalid_empty_string(void) { ecs_world_t *world = ecs_init(); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "Dummy", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "", + .entity = ecs_entity(world, { .name = "Dummy", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "", .callback = Dummy }); @@ -236,8 +222,8 @@ void SystemMisc_invalid_empty_string_w_space(void) { ecs_world_t *world = ecs_init(); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "Dummy", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = " ", + .entity = ecs_entity(world, { .name = "Dummy", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = " ", .callback = Dummy }); @@ -329,11 +315,11 @@ void SystemMisc_system_w_or_disabled_and_prefab(void) { static void TableColumns(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); const ecs_type_t *type = ecs_table_get_type(it->table); test_int(2, type->count); @@ -357,7 +343,7 @@ void SystemMisc_table_columns_access(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); @@ -379,7 +365,10 @@ void SystemMisc_dont_enable_after_rematch(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position(self|up), Velocity(self|up)); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position(self|up IsA), Velocity(self|up IsA)); /* Create an entity that is watched. Whenever components are added/removed * to and/or from watched entities, a rematch is triggered. */ @@ -419,7 +408,7 @@ void SystemMisc_dont_enable_after_rematch(void) { static void SysA(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); ecs_add(it->world, it->entities[0], Velocity); } @@ -450,41 +439,6 @@ void SystemMisc_ensure_single_merge(void) { ecs_fini(world); } -static int test_table_count_invoked; - -static void TestTableCount(ecs_iter_t *it) { - test_int(it->table_count, 2); - - test_table_count_invoked ++; -} - -void SystemMisc_table_count(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ECS_SYSTEM(world, TestTableCount, EcsOnUpdate, Position); - - // Create two active tables - ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - - // Create one inactive table - ecs_entity_t e3 = ecs_new(world, Position); - ecs_add(world, e3, Velocity); - ecs_add(world, e3, Mass); - ecs_delete(world, e3); - - ecs_progress(world, 0); - - test_int(test_table_count_invoked, 2); - - ecs_fini(world); -} - void SystemMisc_match_system(void) { ecs_world_t *world = ecs_init(); @@ -527,7 +481,7 @@ void SystemMisc_add_own_component(void) { ECS_SYSTEM(world, FooSystem, 0, Position); ECS_SYSTEM(world, BarSystem, 0, Position); - ecs_set_ptr(world, BarSystem, Position, NULL); + ecs_set(world, BarSystem, Position, {0, 0}); /* Make sure code didn't assert */ test_assert(true); @@ -554,12 +508,12 @@ void SystemMisc_change_system_action(void) { ECS_COMPONENT(world, Position); ecs_entity_t sys = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "Sys", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "Position", + .entity = ecs_entity(world, { .name = "Sys", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "Position", .callback = ActionA }); - ecs_new(world, Position); + ecs_new_w(world, Position); test_bool(action_a_invoked, false); test_bool(action_b_invoked, false); @@ -594,7 +548,7 @@ void SystemMisc_system_readeactivate(void) { /* No entities, system should be deactivated */ test_assert( ecs_has_id(world, Dummy, EcsEmpty)); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_run_aperiodic(world, 0); /* System should be active, one entity is matched */ @@ -634,8 +588,8 @@ void SystemMisc_system_readeactivate_w_2_systems(void) { test_assert( ecs_has_id(world, Dummy1, EcsEmpty)); test_assert( ecs_has_id(world, Dummy2, EcsEmpty)); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_new(world, Mass); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_new_w(world, Mass); ecs_run_aperiodic(world, 0); /* Systems should be active, one entity is matched */ @@ -667,7 +621,7 @@ void SystemMisc_add_to_system_in_progress(void) { ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_defer_begin(world); @@ -688,14 +642,14 @@ void SystemMisc_redefine_null_signature(void) { ecs_world_t *world = ecs_init(); ecs_entity_t s_1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "System", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = NULL, + .entity = ecs_entity(world, { .name = "System", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = NULL, .callback = Action }); ecs_entity_t s_2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "System", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = NULL, + .entity = ecs_entity(world, { .name = "System", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = NULL, .callback = Action }); @@ -708,14 +662,14 @@ void SystemMisc_redefine_0_signature(void) { ecs_world_t *world = ecs_init(); ecs_entity_t s_1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "System", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "0", + .entity = ecs_entity(world, { .name = "System", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "0", .callback = Action }); ecs_entity_t s_2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .name = "System", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "0", + .entity = ecs_entity(world, { .name = "System", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "0", .callback = Action }); @@ -724,82 +678,6 @@ void SystemMisc_redefine_0_signature(void) { ecs_fini(world); } -void SystemMisc_one_named_column_of_two(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, .terms = { - { ecs_id(Position), .name = "pos" }, - { ecs_id(Velocity) } - }})); - - test_int(f.term_count, 2); - test_int(f.field_count, 2); - - ecs_term_t * - term = &f.terms[0]; - test_assert(term->oper == EcsAnd); - test_assert(term->src.flags == (EcsSelf|EcsUp|EcsIsVariable)); - test_assert(term->src.id == EcsThis); - test_assert(term->inout == EcsInOutDefault); - test_assert(term->id == ecs_id(Position)); - test_str(term->name, "pos"); - - term = &f.terms[1]; - test_assert(term->oper == EcsAnd); - test_assert(term->src.flags == (EcsSelf|EcsUp|EcsIsVariable)); - test_assert(term->src.id == EcsThis); - test_assert(term->inout == EcsInOutDefault); - test_assert(term->id == ecs_id(Velocity)); - test_str(term->name, NULL); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void SystemMisc_two_named_columns_of_two(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, .terms = { - { ecs_id(Position), .name = "pos" }, - { ecs_id(Velocity), .name = "vel" } - }})); - - test_int(f.term_count, 2); - test_int(f.field_count, 2); - - ecs_term_t * - term = &f.terms[0]; - test_assert(term->oper == EcsAnd); - test_assert(term->src.flags == (EcsSelf|EcsUp|EcsIsVariable)); - test_assert(term->src.id == EcsThis); - test_assert(term->inout == EcsInOutDefault); - test_assert(term->id == ecs_id(Position)); - test_str(term->name, "pos"); - - term = &f.terms[1]; - test_assert(term->oper == EcsAnd); - test_assert(term->src.flags == (EcsSelf|EcsUp|EcsIsVariable)); - test_assert(term->src.id == EcsThis); - test_assert(term->inout == EcsInOutDefault); - test_assert(term->id == ecs_id(Velocity)); - test_str(term->name, "vel"); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - void SystemMisc_redeclare_system_explicit_id(void) { ecs_world_t *world = ecs_init(); @@ -807,14 +685,14 @@ void SystemMisc_redeclare_system_explicit_id(void) { ECS_COMPONENT(world, Velocity); ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "Position, Velocity", + .entity = ecs_entity(world, {.name = "Move", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "Position, Velocity", .callback = Dummy }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = s1, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "Position, Velocity", + .entity = ecs_entity(world, {.id = s1, .name = "Move", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "Position, Velocity", .callback = Dummy }); @@ -830,14 +708,14 @@ void SystemMisc_redeclare_system_explicit_id_null_expr(void) { ECS_COMPONENT(world, Velocity); ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = NULL, + .entity = ecs_entity(world, {.name = "Move", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = NULL, .callback = Dummy }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = s1, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = NULL, + .entity = ecs_entity(world, {.id = s1, .name = "Move", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = NULL, .callback = Dummy }); @@ -853,14 +731,14 @@ void SystemMisc_redeclare_system_explicit_id_no_name(void) { ECS_COMPONENT(world, Velocity); ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "Position, Velocity", + .entity = ecs_entity(world, {.name = "Move", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "Position, Velocity", .callback = Dummy }); ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = s1, .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "Position, Velocity", + .entity = ecs_entity(world, {.id = s1, .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "Position, Velocity", .callback = Dummy }); @@ -875,12 +753,12 @@ void SystemMisc_declare_different_id_same_name(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); ecs_entity_t s_1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = e1, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "0", + .entity = ecs_entity(world, {.id = e1, .name = "Move", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "0", .callback = Dummy }); test_assert(e1 == s_1); @@ -888,8 +766,8 @@ void SystemMisc_declare_different_id_same_name(void) { test_expect_abort(); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = e2, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "0", + .entity = ecs_entity(world, {.id = e2, .name = "Move", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "0", .callback = Dummy }); } @@ -899,15 +777,15 @@ void SystemMisc_declare_different_id_same_name_w_scope(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t scope = ecs_new(world, 0); + ecs_entity_t scope = ecs_new(world); ecs_set_scope(world, scope); - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); ecs_entity_t s_1 = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = e1, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "0", + .entity = ecs_entity(world, {.id = e1, .name = "Move", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "0", .callback = Dummy }); test_assert(e1 == s_1); @@ -915,8 +793,8 @@ void SystemMisc_declare_different_id_same_name_w_scope(void) { test_expect_abort(); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.id = e2, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.expr = "0", + .entity = ecs_entity(world, {.id = e2, .name = "Move", .add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.expr = "0", .callback = Dummy }); } @@ -927,17 +805,19 @@ void SystemMisc_rw_in_implicit_any(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, Velocity(self|up)"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, Velocity(self|up)" }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_query_next(&it) == true); + test_assert(ecs_field_is_readonly(&it, 0) == false); test_assert(ecs_field_is_readonly(&it, 1) == false); - test_assert(ecs_field_is_readonly(&it, 2) == false); test_assert(ecs_query_next(&it) == false); + ecs_query_fini(q); + ecs_fini(world); } @@ -947,18 +827,22 @@ void SystemMisc_rw_in_implicit_shared(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, Velocity(up)"); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_query_t *q = ecs_query(world, { .expr = "Position, Velocity(up IsA)" }); + + ecs_entity_t base = ecs_new_w(world, Velocity); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add_pair(world, e, EcsIsA, base); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_query_next(&it) == true); - test_assert(ecs_field_is_readonly(&it, 1) == false); - test_assert(ecs_field_is_readonly(&it, 2) == true); + test_assert(ecs_field_is_readonly(&it, 0) == false); + test_assert(ecs_field_is_readonly(&it, 1) == true); test_assert(ecs_query_next(&it) == false); + ecs_query_fini(q); + ecs_fini(world); } @@ -968,17 +852,19 @@ void SystemMisc_rw_in_implicit_from_empty(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, Velocity()"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, Velocity()" }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_query_next(&it) == true); - test_assert(ecs_field_is_readonly(&it, 1) == false); - test_assert(ecs_field_is_readonly(&it, 2) == true); + test_assert(ecs_field_is_readonly(&it, 0) == false); + test_assert(ecs_field_is_readonly(&it, 1) == true); test_assert(ecs_query_next(&it) == false); + ecs_query_fini(q); + ecs_fini(world); } @@ -989,17 +875,19 @@ void SystemMisc_rw_in_implicit_from_entity(void) { ECS_COMPONENT(world, Velocity); ECS_ENTITY(world, f, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, Velocity(f)"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, Velocity(f)" }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_query_next(&it) == true); - test_assert(ecs_field_is_readonly(&it, 1) == false); - test_assert(ecs_field_is_readonly(&it, 2) == true); + test_assert(ecs_field_is_readonly(&it, 0) == false); + test_assert(ecs_field_is_readonly(&it, 1) == true); test_assert(ecs_query_next(&it) == false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1009,17 +897,21 @@ void SystemMisc_rw_out_explicit_any(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, [out] Velocity(self|up)"); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { .expr = "Position, [out] Velocity(self|up IsA)" }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_query_next(&it) == true); + test_assert(ecs_field_is_readonly(&it, 0) == false); test_assert(ecs_field_is_readonly(&it, 1) == false); - test_assert(ecs_field_is_readonly(&it, 2) == false); test_assert(ecs_query_next(&it) == false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1029,18 +921,22 @@ void SystemMisc_rw_out_explicit_shared(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, [out] Velocity(up)"); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_query_t *q = ecs_query(world, { .expr = "Position, [out] Velocity(up IsA)" }); + + ecs_entity_t base = ecs_new_w(world, Velocity); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add_pair(world, e, EcsIsA, base); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_query_next(&it) == true); + test_assert(ecs_field_is_readonly(&it, 0) == false); test_assert(ecs_field_is_readonly(&it, 1) == false); - test_assert(ecs_field_is_readonly(&it, 2) == false); test_assert(ecs_query_next(&it) == false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1050,17 +946,19 @@ void SystemMisc_rw_out_explicit_from_empty(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, [out] Velocity()"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, [out] Velocity()" }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_query_next(&it) == true); + test_assert(ecs_field_is_readonly(&it, 0) == false); test_assert(ecs_field_is_readonly(&it, 1) == false); - test_assert(ecs_field_is_readonly(&it, 2) == false); test_assert(ecs_query_next(&it) == false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1071,17 +969,19 @@ void SystemMisc_rw_out_explicit_from_entity(void) { ECS_COMPONENT(world, Velocity); ECS_ENTITY(world, f, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, [out] Velocity(f)"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, [out] Velocity(f)" }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_query_next(&it) == true); + test_assert(ecs_field_is_readonly(&it, 0) == false); test_assert(ecs_field_is_readonly(&it, 1) == false); - test_assert(ecs_field_is_readonly(&it, 2) == false); test_assert(ecs_query_next(&it) == false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1123,18 +1023,18 @@ void SystemMisc_get_query(void) { ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position); - ecs_set(world, 0, Position, {0, 0}); - ecs_set(world, 0, Position, {1, 0}); - ecs_set(world, 0, Position, {2, 0}); + ecs_insert(world, ecs_value(Position, {0, 0})); + ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_insert(world, ecs_value(Position, {2, 0})); - ecs_query_t *q = ecs_system_get_query(world, Dummy); + ecs_query_t *q = ecs_system_get(world, Dummy)->query; test_assert(q != NULL); int32_t count = 0; ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(p != NULL); int i; @@ -1146,6 +1046,8 @@ void SystemMisc_get_query(void) { test_int(count, 3); + ecs_query_fini(q); + ecs_fini(world); } @@ -1158,20 +1060,20 @@ void SystemMisc_set_get_context(void) { ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ .entity = ecs_entity(world, {.name = "MySystem"}), - .query.filter.terms = {{Tag}}, + .query.terms = {{Tag}}, .callback = Dummy, .ctx = &ctx_a }); test_assert(s != 0); - test_assert(ecs_system_get_ctx(world, s) == &ctx_a); + test_assert(ecs_system_get(world, s)->ctx == &ctx_a); test_assert(ecs_system_init(world, &(ecs_system_desc_t){ .entity = s, .ctx = &ctx_b }) == s); - test_assert(ecs_system_get_ctx(world, s) == &ctx_b); + test_assert(ecs_system_get(world, s)->ctx == &ctx_b); ecs_fini(world); } @@ -1185,20 +1087,20 @@ void SystemMisc_set_get_binding_context(void) { ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ .entity = ecs_entity(world, {.name = "MySystem"}), - .query.filter.terms = {{Tag}}, + .query.terms = {{Tag}}, .callback = Dummy, - .binding_ctx = &ctx_a + .callback_ctx = &ctx_a }); test_assert(s != 0); - test_assert(ecs_system_get_binding_ctx(world, s) == &ctx_a); + test_assert(ecs_system_get(world, s)->callback_ctx == &ctx_a); test_assert(ecs_system_init(world, &(ecs_system_desc_t){ .entity = s, - .binding_ctx = &ctx_b + .callback_ctx = &ctx_b }) == s); - test_assert(ecs_system_get_binding_ctx(world, s) == &ctx_b); + test_assert(ecs_system_get(world, s)->callback_ctx == &ctx_b); ecs_fini(world); } @@ -1236,14 +1138,14 @@ void SystemMisc_delete_system(void) { Probe ctx = {0}; ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){ .entity = ecs_entity(world, {.name = "Foo"}), - .query.filter.terms = {{.id = Tag}}, + .query.terms = {{.id = Tag}}, .callback = Dummy }); test_assert(system != 0); ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, Tag); ecs_run(world, system, 0, NULL); @@ -1265,28 +1167,28 @@ void SystemMisc_delete_pipeline_system(void) { // Create system before test_assert(ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.terms = {{.id = Tag}}, + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.terms = {{.id = Tag}}, .callback = Dummy }) != 0); ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.terms = {{.id = Tag}}, + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.terms = {{.id = Tag}}, .callback = Dummy }); test_assert(system != 0); // Create system after test_assert(ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.terms = {{.id = Tag}}, + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.terms = {{.id = Tag}}, .callback = Dummy }) != 0); ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, Tag); ecs_progress(world, 0); @@ -1311,7 +1213,7 @@ void ctx_free(void *ctx) { static int binding_ctx_value; static -void binding_ctx_free(void *ctx) { +void callback_ctx_free(void *ctx) { test_assert(&binding_ctx_value == ctx); binding_ctx_value ++; } @@ -1337,22 +1239,22 @@ void SystemMisc_delete_system_w_ctx(void) { Probe ctx = {0}; ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){ - .query.filter.terms = {{.id = Tag}}, + .query.terms = {{.id = Tag}}, .callback = Dummy, .ctx = &ctx_value, .ctx_free = ctx_free, - .binding_ctx = &binding_ctx_value, - .binding_ctx_free = binding_ctx_free + .callback_ctx = &binding_ctx_value, + .callback_ctx_free = callback_ctx_free }); test_assert(system != 0); - test_assert(ecs_system_get_ctx(world, system) == &ctx_value); - test_assert(ecs_system_get_binding_ctx(world, system) + test_assert(ecs_system_get(world, system)->ctx == &ctx_value); + test_assert(ecs_system_get(world, system)->callback_ctx == &binding_ctx_value); ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, Tag); ecs_run(world, system, 0, NULL); @@ -1371,25 +1273,25 @@ void SystemMisc_update_ctx(void) { ECS_TAG(world, Tag); ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){ - .query.filter.terms = {{.id = Tag}}, + .query.terms = {{.id = Tag}}, .callback = Dummy, .ctx = &ctx_value, .ctx_free = ctx_free, - .binding_ctx = &binding_ctx_value, - .binding_ctx_free = binding_ctx_free + .callback_ctx = &binding_ctx_value, + .callback_ctx_free = callback_ctx_free }); test_assert(system != 0); - test_assert(ecs_system_get_ctx(world, system) == &ctx_value); - test_assert(ecs_system_get_binding_ctx(world, system) + test_assert(ecs_system_get(world, system)->ctx == &ctx_value); + test_assert(ecs_system_get(world, system)->callback_ctx == &binding_ctx_value); ecs_system(world, { .entity = system, .ctx = &ctx_value, .ctx_free = ctx_free, - .binding_ctx = &binding_ctx_value, - .binding_ctx_free = binding_ctx_free + .callback_ctx = &binding_ctx_value, + .callback_ctx_free = callback_ctx_free }); test_int(ctx_value, 0); @@ -1401,8 +1303,8 @@ void SystemMisc_update_ctx(void) { .entity = system, .ctx = &ctx_value_2, .ctx_free = ctx_free_2, - .binding_ctx = &binding_ctx_value_2, - .binding_ctx_free = binding_ctx_free_2 + .callback_ctx = &binding_ctx_value_2, + .callback_ctx_free = binding_ctx_free_2 }); test_int(ctx_value, 1); @@ -1457,16 +1359,16 @@ void SystemMisc_run_custom_run_action(void) { Probe ctx = {0}; ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){ - .query.filter.terms = {{ .id = TagA }}, + .query.terms = {{ .id = TagA }}, .run = Run, .callback = Dummy, .ctx = &ctx, }); test_assert(system != 0); - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); ecs_add(world, e3, TagB); // 2 tables ecs_run(world, system, 0, NULL); @@ -1483,38 +1385,6 @@ void SystemMisc_run_custom_run_action(void) { ecs_fini(world); } -void SystemMisc_run_w_offset_limit_custom_run_action(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){ - .query.filter.terms = {{ .id = TagA }}, - .run = Run, - .callback = Dummy, - .ctx = &ctx, - }); - test_assert(system != 0); - - ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add(world, e3, TagB); // 2 tables - - ecs_run_w_filter(world, system, 0, 1, 1, NULL); - - test_bool(dummy_invoked, false); - test_int(run_invoked, 1); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - - ecs_fini(world); -} - void SystemMisc_pipeline_custom_run_action(void) { ecs_world_t *world = ecs_init(); @@ -1523,17 +1393,17 @@ void SystemMisc_pipeline_custom_run_action(void) { Probe ctx = {0}; ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), - .query.filter.terms = {{ .id = TagA }}, + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}), + .query.terms = {{ .id = TagA }}, .run = Run, .callback = Dummy, .ctx = &ctx }); test_assert(system != 0); - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); ecs_add(world, e3, TagB); // 2 tables ecs_progress(world, 0); @@ -1558,14 +1428,14 @@ void SystemMisc_change_custom_run_action(void) { Probe ctx = {0}; ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){ - .query.filter.terms = {{ .id = TagA }}, + .query.terms = {{ .id = TagA }}, .run = Run, .callback = Dummy, .ctx = &ctx, }); test_assert(system != 0); - ecs_new(world, TagA); + ecs_new_w(world, TagA); ecs_run(world, system, 0, NULL); test_bool(dummy_invoked, false); @@ -1593,17 +1463,17 @@ void SystemMisc_custom_run_action_call_next(void) { Probe ctx = {0}; ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){ - .query.filter.terms = {{ .id = TagA }}, + .query.terms = {{ .id = TagA }}, .run = Run_call_callback, .callback = Dummy, .ctx = &ctx, - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}) + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}) }); test_assert(system != 0); - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); ecs_add(world, e3, TagB); // 2 tables ecs_progress(world, 0); @@ -1627,14 +1497,14 @@ void SystemMisc_system_w_short_notation(void) { Probe ctx = {0}; ecs_entity_t system = ecs_system(world, { - .query.filter.terms = {{ .id = Tag }}, + .query.terms = {{ .id = Tag }}, .callback = Dummy, .ctx = &ctx, - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}) + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}) }); test_assert(system != 0); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_progress(world, 0); @@ -1689,33 +1559,40 @@ void SystemMisc_update_rate_w_system_init(void) { void SystemMisc_system_w_interval_rate_stop_timer(void) { ecs_world_t *world = ecs_init(); + ecs_log_set_level(-4); + ecs_entity_t system = ecs_system(world, { - .entity = ecs_entity(world, { .add = {ecs_dependson(EcsOnUpdate)} }), + .entity = ecs_entity(world, { .add = ecs_ids(ecs_dependson(EcsOnUpdate)) }), .interval = 1.0, .rate = 3.0, .callback = Dummy }); - for (int i = 0; i < 5; i ++) { - ecs_progress(world, 0.5); - test_assert(dummy_invoked == 0); - } + test_assert(system == 0); - ecs_progress(world, 0.5); + ecs_fini(world); +} + +void SystemMisc_system_w_rate_filter_self(void) { + ecs_world_t *world = ecs_init(); - test_assert(dummy_invoked == 1); - dummy_invoked = 0; + ecs_system(world, { + .entity = ecs_entity(world, { .add = ecs_ids(ecs_dependson(EcsOnUpdate)) }), + .rate = 2.0, + .callback = Dummy + }); - ecs_stop_timer(world, system); + ecs_progress(world, 0); + test_int(dummy_invoked, 0); - for (int i = 0; i < 5; i ++) { - ecs_progress(world, 0.5); - test_assert(dummy_invoked == 0); - } + ecs_progress(world, 0); + test_int(dummy_invoked, 1); - ecs_progress(world, 0.5); + ecs_progress(world, 0); + test_int(dummy_invoked, 1); - test_assert(dummy_invoked == 0); + ecs_progress(world, 0); + test_int(dummy_invoked, 2); ecs_fini(world); } @@ -1748,19 +1625,19 @@ void SystemMisc_system_same_interval_same_tick(void) { ecs_world_t *world = ecs_init(); ecs_system(world, { - .entity = ecs_entity(world, { .add = {ecs_dependson(EcsOnUpdate)} }), + .entity = ecs_entity(world, { .add = ecs_ids(ecs_dependson(EcsOnUpdate)) }), .interval = 1.0, .callback = SA }); ecs_system(world, { - .entity = ecs_entity(world, { .add = {ecs_dependson(EcsOnUpdate)} }), + .entity = ecs_entity(world, { .add = ecs_ids(ecs_dependson(EcsOnUpdate)) }), .interval = 1.0, .callback = SB }); ecs_system(world, { - .entity = ecs_entity(world, { .add = {ecs_dependson(EcsOnUpdate)} }), + .entity = ecs_entity(world, { .add = ecs_ids(ecs_dependson(EcsOnUpdate)) }), .interval = 1.0, .callback = SC }); @@ -1776,3 +1653,147 @@ void SystemMisc_system_same_interval_same_tick(void) { ecs_fini(world); } + +void SystemMisc_system_no_id_in_scope(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t parent = ecs_new(world); + + ecs_set_scope(world, parent); + + ecs_entity_t s = ecs_system(world, { + .query.terms = { + { .id = TagA }, + }, + .callback = SA + }); + + ecs_set_scope(world, 0); + + test_assert(ecs_has_pair(world, s, EcsChildOf, parent)); + + ecs_fini(world); +} + +static int callback_callback_invoked = 0; +static int callback_run_invoked = 0; + +static +void callback_callback(ecs_iter_t *it) { + callback_callback_invoked ++; +} + +static +void callback_run(ecs_iter_t *it) { + callback_run_invoked ++; +} + +void SystemMisc_register_callback_after_run(void) { + ecs_world_t* world = ecs_mini(); + + ecs_entity_t s = ecs_system(world, { + .callback = callback_callback + }); + + ecs_run(world, s, 0, 0); + test_int(callback_callback_invoked, 1); + test_int(callback_run_invoked, 0); + + ecs_system(world, { + .entity = s, + .run = callback_run + }); + + ecs_run(world, s, 0, 0); + test_int(callback_callback_invoked, 1); + test_int(callback_run_invoked, 1); + + ecs_fini(world); +} + +void SystemMisc_register_run_after_callback(void) { + ecs_world_t* world = ecs_mini(); + + ecs_entity_t s = ecs_system(world, { + .run = callback_run + }); + + ecs_run(world, s, 0, 0); + test_int(callback_callback_invoked, 0); + test_int(callback_run_invoked, 1); + + ecs_system(world, { + .entity = s, + .callback = callback_callback + }); + + ecs_run(world, s, 0, 0); + test_int(callback_callback_invoked, 1); + test_int(callback_run_invoked, 1); + + ecs_fini(world); +} + +static +void ctx_free_3(void *ptr) { + int32_t *ctx = ptr; + ctx[0]++; +} + +void SystemMisc_register_callback_after_run_ctx(void) { + ecs_world_t* world = ecs_mini(); + + int32_t callback_ctx = 0, run_ctx = 0; + + ecs_entity_t s = ecs_system(world, { + .callback = callback_callback, + .callback_ctx = &callback_ctx, + .callback_ctx_free = ctx_free_3 + }); + + test_int(callback_ctx, 0); + + ecs_system(world, { + .entity = s, + .run = callback_run, + .run_ctx = &run_ctx, + .run_ctx_free = ctx_free_3 + }); + + test_int(callback_ctx, 1); + + ecs_fini(world); + + test_int(callback_ctx, 1); + test_int(run_ctx, 1); +} + +void SystemMisc_register_run_after_callback_ctx(void) { + ecs_world_t* world = ecs_mini(); + + int32_t callback_ctx = 0, run_ctx = 0; + + ecs_entity_t s = ecs_system(world, { + .run = callback_run, + .run_ctx = &run_ctx, + .run_ctx_free = ctx_free_3 + }); + + test_int(run_ctx, 0); + + ecs_system(world, { + .entity = s, + .callback = callback_callback, + .callback_ctx = &callback_ctx, + .callback_ctx_free = ctx_free_3 + }); + + test_int(run_ctx, 1); + + ecs_fini(world); + + test_int(callback_ctx, 1); + test_int(run_ctx, 1); +} diff --git a/vendors/flecs/test/addons/src/SystemPeriodic.c b/vendors/flecs/test/addons/src/SystemPeriodic.c index 949c74f75..84e6da3f5 100644 --- a/vendors/flecs/test/addons/src/SystemPeriodic.c +++ b/vendors/flecs/test/addons/src/SystemPeriodic.c @@ -2,19 +2,19 @@ static void Iter(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); Velocity *v = NULL; Mass *m = NULL; if (it->field_count >= 2) { - if (ecs_field_size(it, 2) == sizeof(Velocity)) { - v = ecs_field(it, Velocity, 2); + if (ecs_field_size(it, 1) == sizeof(Velocity)) { + v = ecs_field(it, Velocity, 1); } } if (it->field_count >= 3) { - if (ecs_field_size(it, 3) == sizeof(Mass)) { - m = ecs_field(it, Mass, 3); + if (ecs_field_size(it, 2) == sizeof(Mass)) { + m = ecs_field(it, Mass, 2); } } @@ -560,11 +560,11 @@ void SystemPeriodic_4_type_1_and_1_or(void) { ECS_ENTITY(world, e3, Position, Position_1, Velocity); ECS_ENTITY(world, e4, Velocity); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position || Position_1, Velocity); - ecs_set(world, e3, Position_1, {0, 0}); ecs_set(world, e4, Velocity, {0, 0}); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position || Position_1, Velocity); + Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); @@ -577,18 +577,18 @@ void SystemPeriodic_4_type_1_and_1_or(void) { test_null(ctx.param); test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e3); + test_int(ctx.e[1], e3); + test_int(ctx.e[2], e2); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); test_int(ctx.c[0][1], ecs_id(Velocity)); test_int(ctx.s[0][1], 0); - test_int(ctx.c[1][0], ecs_id(Position_1)); + test_int(ctx.c[1][0], ecs_id(Position)); test_int(ctx.s[1][0], 0); test_int(ctx.c[1][1], ecs_id(Velocity)); test_int(ctx.s[1][1], 0); - test_int(ctx.c[2][0], ecs_id(Position)); + test_int(ctx.c[2][0], ecs_id(Position_1)); test_int(ctx.s[2][0], 0); test_int(ctx.c[2][1], ecs_id(Velocity)); test_int(ctx.s[2][1], 0); @@ -626,18 +626,18 @@ void SystemPeriodic_4_type_1_and_1_or_of_3(void) { test_null(ctx.param); test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e3); + test_int(ctx.e[1], e3); + test_int(ctx.e[2], e2); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); test_int(ctx.c[0][1], ecs_id(Velocity)); test_int(ctx.s[0][1], 0); - test_int(ctx.c[1][0], ecs_id(Position_2)); + test_int(ctx.c[1][0], ecs_id(Position_1)); test_int(ctx.s[1][0], 0); test_int(ctx.c[1][1], ecs_id(Velocity)); test_int(ctx.s[1][1], 0); - test_int(ctx.c[2][0], ecs_id(Position_1)); + test_int(ctx.c[2][0], ecs_id(Position_2)); test_int(ctx.s[2][0], 0); test_int(ctx.c[2][1], ecs_id(Velocity)); test_int(ctx.s[2][1], 0); @@ -972,8 +972,8 @@ void SystemPeriodic_match_2_systems_w_populated_table(void) { } void TestOptional_w_column(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); test_assert(p != NULL); test_assert(v == NULL); @@ -982,8 +982,8 @@ void TestOptional_w_column(ecs_iter_t *it) { } void TestOptional_w_shared(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); test_assert(p != NULL); test_assert(v == NULL); @@ -1027,7 +1027,7 @@ void SystemPeriodic_ensure_optional_is_null_shared(void) { ECS_ENTITY(world, e, Position); - ECS_SYSTEM(world, TestOptional_w_shared, EcsOnUpdate, Position, ?Velocity(parent)); + ECS_SYSTEM(world, TestOptional_w_shared, EcsOnUpdate, Position, ?Velocity(up)); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); @@ -1236,11 +1236,11 @@ void SystemPeriodic_disabled_nested_feature(void) { } void TwoRefs(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); + test_assert(!ecs_field_is_self(it, 0)); test_assert(!ecs_field_is_self(it, 1)); - test_assert(!ecs_field_is_self(it, 2)); (void)p; (void)v; @@ -1419,7 +1419,7 @@ void SystemPeriodic_match_prefab_and_normal(void) { static void TestIsSharedOnNotSet(ecs_iter_t *it) { - test_assert(ecs_field_is_self(it, 2) != false); + test_assert(ecs_field_is_self(it, 1) != false); } void SystemPeriodic_is_shared_on_column_not_set(void) { @@ -1445,6 +1445,10 @@ void SystemPeriodic_owned_column(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, base, Velocity); ECS_ENTITY(world, e1, Position, Velocity); ECS_ENTITY(world, e2, Position, (IsA, base)); @@ -1474,6 +1478,9 @@ void SystemPeriodic_owned_not_column(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, base, Velocity); ECS_ENTITY(world, e1, Position, Velocity); ECS_ENTITY(world, e2, Position, (IsA, base)); @@ -1508,6 +1515,10 @@ void SystemPeriodic_owned_or_column(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, base, Velocity); ECS_ENTITY(world, e1, Position, Velocity); ECS_ENTITY(world, e2, Position, Mass); @@ -1542,13 +1553,14 @@ void SystemPeriodic_shared_column(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, base, Velocity); ECS_ENTITY(world, e1, Position, Velocity); ECS_ENTITY(world, e2, Position, (IsA, base)); ECS_ENTITY(world, e3, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, Velocity(up)); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, Velocity(up IsA)); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); @@ -1572,12 +1584,13 @@ void SystemPeriodic_shared_not_column(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, base, Velocity); ECS_ENTITY(world, e1, Position, Velocity); ECS_ENTITY(world, e2, Position, (IsA, base)); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, !Velocity(up)); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, !Velocity(up IsA)); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); @@ -1607,6 +1620,9 @@ void SystemPeriodic_shared_or_column(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, base1, Velocity); ECS_ENTITY(world, base2, Mass); ECS_ENTITY(world, e1, Position, Velocity); @@ -1614,7 +1630,7 @@ void SystemPeriodic_shared_or_column(void) { ECS_ENTITY(world, e3, Position, (IsA, base1)); ECS_ENTITY(world, e4, Position, (IsA, base2)); - ECS_SYSTEM(world, SharedOr, EcsOnUpdate, Position, Velocity(up) || Mass(up)); + ECS_SYSTEM(world, SharedOr, EcsOnUpdate, Position, Velocity(up IsA) || Mass(up IsA)); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); @@ -1644,11 +1660,13 @@ void SystemPeriodic_container_dont_match_inheritance(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, base, Velocity); ECS_ENTITY(world, e1, Position, (IsA, base)); ECS_ENTITY(world, e2, Position, (ChildOf, base)); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, Velocity(parent)); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, Velocity(up)); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); @@ -1673,11 +1691,13 @@ void SystemPeriodic_cascade_dont_match_inheritance(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, base, Velocity); ECS_ENTITY(world, e1, Position, (IsA, base)); ECS_ENTITY(world, e2, Position, (ChildOf, base)); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Velocity(parent|cascade)); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Velocity(cascade)); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); @@ -1749,7 +1769,7 @@ void SystemPeriodic_sys_context(void) { .ctx = ¶m }); - test_assert(ecs_system_get_ctx(world, TestContext) == ¶m); + test_assert(ecs_system_get(world, TestContext)->ctx == ¶m); ecs_fini(world); } @@ -1792,7 +1812,7 @@ void SystemPeriodic_owned_only(void) { ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position(self)); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_progress(world, 0); @@ -1805,7 +1825,7 @@ static void AssertReadonly(ecs_iter_t *it) { test_assert(dummy_invoked == 0); dummy_invoked = it->entities[0]; - test_assert( ecs_field_is_readonly(it, 1) == true); + test_assert( ecs_field_is_readonly(it, 0) == true); } void SystemPeriodic_shared_only(void) { @@ -1813,9 +1833,11 @@ void SystemPeriodic_shared_only(void) { ECS_COMPONENT(world, Position); - ECS_SYSTEM(world, AssertReadonly, EcsOnUpdate, Position(up)); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ECS_SYSTEM(world, AssertReadonly, EcsOnUpdate, Position(up IsA)); - ecs_entity_t base = ecs_new(world, Position); + ecs_entity_t base = ecs_new_w(world, Position); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_progress(world, 0); @@ -1832,7 +1854,7 @@ void SystemPeriodic_is_in_readonly(void) { ECS_SYSTEM(world, AssertReadonly, EcsOnUpdate, [in] Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_progress(world, 0); @@ -1866,10 +1888,10 @@ void SystemPeriodic_and_type(void) { ECS_COMPONENT(world, Velocity); ECS_PREFAB(world, MyType, Position, Velocity); - ECS_SYSTEM(world, TypeSystem, EcsOnUpdate, AND | MyType); + ECS_SYSTEM(world, TypeSystem, EcsOnUpdate, and | MyType); - ecs_new(world, Position); - ecs_new(world, Velocity); + ecs_new_w(world, Position); + ecs_new_w(world, Velocity); ECS_ENTITY(world, e, Position, Velocity); Probe ctx = {0}; @@ -1897,11 +1919,11 @@ void SystemPeriodic_or_type(void) { ECS_COMPONENT(world, Velocity); ECS_PREFAB(world, MyType, Position, Velocity); - ECS_SYSTEM(world, TypeSystem, EcsOnUpdate, OR | MyType); + ECS_SYSTEM(world, TypeSystem, EcsOnUpdate, or | MyType); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Velocity); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Velocity); + ecs_entity_t e3 = ecs_new_w(world, Position); ecs_add(world, e3, Velocity); Probe ctx = {0}; @@ -1918,9 +1940,9 @@ void SystemPeriodic_or_type(void) { test_int(ctx.e[0], e1); test_int(ctx.e[1], e2); test_int(ctx.e[2], e3); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.c[1][0], ecs_id(Velocity)); - test_int(ctx.c[2][0], ecs_id(Position)); + test_int(ctx.c[0][0], MyType); + test_int(ctx.c[1][0], MyType); + test_int(ctx.c[2][0], MyType); test_int(ctx.s[0][0], 0); ecs_fini(world); diff --git a/vendors/flecs/test/addons/src/System_w_Empty.c b/vendors/flecs/test/addons/src/System_w_Empty.c index 75d0e80c1..14ff943a9 100644 --- a/vendors/flecs/test/addons/src/System_w_Empty.c +++ b/vendors/flecs/test/addons/src/System_w_Empty.c @@ -2,7 +2,7 @@ static void Iter(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); probe_iter(it); @@ -24,7 +24,7 @@ void System_w_Empty_2_column_1_from_id(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_progress(world, 1); @@ -55,7 +55,7 @@ void System_w_Empty_3_column_2_from_id(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_progress(world, 1); @@ -78,9 +78,9 @@ void System_w_Empty_3_column_2_from_id(void) { static void CheckColumnType(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); - test_assert(ecs_id(Position) == ecs_field_id(it, 1)); + test_assert(ecs_id(Position) == ecs_field_id(it, 0)); probe_iter(it); } @@ -97,7 +97,7 @@ void System_w_Empty_column_type(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_progress(world, 1); diff --git a/vendors/flecs/test/addons/src/System_w_FromEntity.c b/vendors/flecs/test/addons/src/System_w_FromEntity.c index 8e300612c..66bd048d3 100644 --- a/vendors/flecs/test/addons/src/System_w_FromEntity.c +++ b/vendors/flecs/test/addons/src/System_w_FromEntity.c @@ -2,20 +2,20 @@ static void Iter(ecs_iter_t *it) { - Mass *m_ptr = ecs_field(it, Mass, 1); + Mass *m_ptr = ecs_field(it, Mass, 0); Position *p = NULL; Velocity *v = NULL; if (it->field_count >= 2) { - p = ecs_field(it, Position, 2); + p = ecs_field(it, Position, 1); } if (it->field_count >= 3) { - v = ecs_field(it, Velocity, 3); + v = ecs_field(it, Velocity, 2); } - test_assert(!m_ptr || !ecs_field_is_self(it, 1)); + test_assert(!m_ptr || !ecs_field_is_self(it, 0)); probe_iter(it); @@ -88,8 +88,8 @@ void dummy_reset(void) { static void Dummy(ecs_iter_t *it) { dummy_invoked = 1; - dummy_component = ecs_field_id(it, 1); - dummy_source = ecs_field_src(it, 1); + dummy_component = ecs_field_id(it, 0); + dummy_source = ecs_field_src(it, 0); } void System_w_FromEntity_task_from_entity(void) { diff --git a/vendors/flecs/test/addons/src/System_w_FromParent.c b/vendors/flecs/test/addons/src/System_w_FromParent.c index 02b80e0d6..625ea4f1d 100644 --- a/vendors/flecs/test/addons/src/System_w_FromParent.c +++ b/vendors/flecs/test/addons/src/System_w_FromParent.c @@ -6,22 +6,22 @@ void System_w_FromParent_setup(void) { static void Iter(ecs_iter_t *it) { - Mass *m_ptr = ecs_field(it, Mass, 1); + Mass *m_ptr = ecs_field(it, Mass, 0); bool shared = false; if (m_ptr) { - shared = !ecs_field_is_self(it, 1); + shared = !ecs_field_is_self(it, 0); } Position *p = NULL; Velocity *v = NULL; if (it->field_count >= 2) { - p = ecs_field(it, Position, 2); + p = ecs_field(it, Position, 1); } if (it->field_count >= 3) { - v = ecs_field(it, Velocity, 3); + v = ecs_field(it, Velocity, 2); } probe_iter(it); @@ -58,13 +58,12 @@ void System_w_FromParent_1_column_from_container(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ecs_entity_t parent = ecs_set(world, 0, Mass, {2}); + ecs_entity_t parent = ecs_insert(world, ecs_value(Mass, {2})); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); ecs_add_pair(world, e3, EcsChildOf, parent); @@ -118,13 +117,12 @@ void System_w_FromParent_2_column_1_from_container(void) { ECS_ENTITY(world, e3, Position, Velocity); ECS_ENTITY(world, e4, Position, Velocity); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position, Velocity); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position, Velocity); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ecs_entity_t parent = ecs_set(world, 0, Mass, {2}); + ecs_entity_t parent = ecs_insert(world, ecs_value(Mass, {2})); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); ecs_add_pair(world, e3, EcsChildOf, parent); @@ -185,22 +183,22 @@ void System_w_FromParent_2_column_1_from_container(void) { static void Iter_2_shared(ecs_iter_t *it) { - Mass *m_ptr = ecs_field(it, Mass, 1); + Mass *m_ptr = ecs_field(it, Mass, 0); Rotation *r_ptr = NULL; Position *p = NULL; Velocity *v = NULL; if (it->field_count >= 2) { - r_ptr = ecs_field(it, Rotation, 2); + r_ptr = ecs_field(it, Rotation, 1); } if (it->field_count >= 3) { - p = ecs_field(it, Position, 3); + p = ecs_field(it, Position, 2); } if (it->field_count >= 4) { - v = ecs_field(it, Velocity, 4); + v = ecs_field(it, Velocity, 3); } probe_iter(it); @@ -241,13 +239,12 @@ void System_w_FromParent_3_column_2_from_container(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, Iter_2_shared, EcsOnUpdate, Mass(parent), Rotation(parent), Position); + ECS_SYSTEM(world, Iter_2_shared, EcsOnUpdate, Mass(up), Rotation(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter_2_shared, - .query.filter.instanced = true + .entity = Iter_2_shared }); - ecs_entity_t parent = ecs_set(world, 0, Mass, {2}); + ecs_entity_t parent = ecs_insert(world, ecs_value(Mass, {2})); ecs_set(world, parent, Rotation, {3}); ecs_add_pair(world, e1, EcsChildOf, parent); @@ -306,14 +303,13 @@ void System_w_FromParent_2_column_1_from_container_w_not(void) { ECS_ENTITY(world, e4, Position); ECS_ENTITY(world, e5, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, !Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, !Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ecs_entity_t parent_1 = ecs_set(world, 0, Mass, {2}); - ecs_entity_t parent_2 = ecs_set(world, 0, Rotation, {3}); + ecs_entity_t parent_1 = ecs_insert(world, ecs_value(Mass, {2})); + ecs_entity_t parent_2 = ecs_insert(world, ecs_value(Rotation, {3})); ecs_add_pair(world, e1, EcsChildOf, parent_2); ecs_add_pair(world, e2, EcsChildOf, parent_2); @@ -367,7 +363,7 @@ void System_w_FromParent_2_column_1_from_container_w_not(void) { ecs_fini(world); } -void System_w_FromParent_3_column_1_from_comtainer_1_from_container_w_not(void) { +void System_w_FromParent_3_column_1_from_container_1_from_container_w_not(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); @@ -381,15 +377,14 @@ void System_w_FromParent_3_column_1_from_comtainer_1_from_container_w_not(void) ECS_ENTITY(world, e5, Position); ECS_ENTITY(world, e6, Position); - ECS_SYSTEM(world, Iter_2_shared, EcsOnUpdate, !Mass(parent), Rotation(parent), Position); + ECS_SYSTEM(world, Iter_2_shared, EcsOnUpdate, !Mass(up), Rotation(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter_2_shared, - .query.filter.instanced = true + .entity = Iter_2_shared }); - ecs_entity_t parent_1 = ecs_set(world, 0, Mass, {2}); - ecs_entity_t parent_2 = ecs_set(world, 0, Rotation, {3}); - ecs_entity_t parent_3 = ecs_set(world, 0, Mass, {4}); + ecs_entity_t parent_1 = ecs_insert(world, ecs_value(Mass, {2})); + ecs_entity_t parent_2 = ecs_insert(world, ecs_value(Rotation, {3})); + ecs_entity_t parent_3 = ecs_insert(world, ecs_value(Mass, {4})); ecs_set(world, parent_3, Rotation, {5}); ecs_add_pair(world, e1, EcsChildOf, parent_2); @@ -444,18 +439,19 @@ void System_w_FromParent_2_column_1_from_container_w_not_prefab(void) { ECS_COMPONENT(world, Rotation); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + ECS_PREFAB(world, Prefab, Rotation); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, !Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, !Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ecs_entity_t parent_1 = ecs_set(world, 0, Mass, {2}); + ecs_entity_t parent_1 = ecs_insert(world, ecs_value(Mass, {2})); ecs_add_pair(world, e1, EcsIsA, Prefab); ecs_add_pair(world, e1, EcsChildOf, parent_1); @@ -506,17 +502,16 @@ void System_w_FromParent_2_column_1_from_container_w_or(void) { ECS_ENTITY(world, e5, Position); ECS_ENTITY(world, e6, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent) || Rotation(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up) || Rotation(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ecs_entity_t parent_1 = ecs_set(world, 0, Mass, {2}); - ecs_entity_t parent_2 = ecs_set(world, 0, Rotation, {3}); - ecs_entity_t parent_3 = ecs_set(world, 0, Rotation, {4}); + ecs_entity_t parent_1 = ecs_insert(world, ecs_value(Mass, {2})); + ecs_entity_t parent_2 = ecs_insert(world, ecs_value(Rotation, {3})); + ecs_entity_t parent_3 = ecs_insert(world, ecs_value(Rotation, {4})); ecs_set(world, parent_3, Mass, {5}); - ecs_entity_t parent_4 = ecs_set(world, 0, Velocity, {10, 20}); + ecs_entity_t parent_4 = ecs_insert(world, ecs_value(Velocity, {10, 20})); ecs_add_pair(world, e1, EcsChildOf, parent_1); ecs_add_pair(world, e2, EcsChildOf, parent_2); @@ -555,10 +550,10 @@ void System_w_FromParent_2_column_1_from_container_w_or(void) { static void Dummy(ecs_iter_t *it) { - Mass *m_ptr = ecs_field(it, Mass, 1); - Position *p = ecs_field(it, Position, 2); + Mass *m_ptr = ecs_field(it, Mass, 0); + Position *p = ecs_field(it, Position, 1); - test_assert(!m_ptr || !ecs_field_is_self(it, 1)); + test_assert(!m_ptr || !ecs_field_is_self(it, 0)); probe_iter(it); @@ -585,13 +580,12 @@ void System_w_FromParent_add_component_after_match(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); @@ -639,13 +633,12 @@ void System_w_FromParent_add_component_after_match_and_rematch(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); @@ -698,10 +691,9 @@ void System_w_FromParent_add_component_after_match_and_rematch_w_entity_type_exp ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); ECS_ENTITY(world, Parent, 0); @@ -750,7 +742,7 @@ void System_w_FromParent_add_component_after_match_and_rematch_w_entity_type_exp static void SetMass(ecs_iter_t *it) { - ecs_id_t ecs_id(Mass) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Mass) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -769,10 +761,9 @@ void System_w_FromParent_add_component_after_match_and_rematch_w_entity_type_exp ECS_ENTITY(world, e4, Position); ECS_SYSTEM(world, SetMass, EcsOnUpdate, Velocity, Mass()); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); ECS_ENTITY(world, Parent, Velocity); @@ -821,9 +812,9 @@ void System_w_FromParent_add_component_after_match_unmatch(void) { ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, !Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, !Mass(up), Position); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); @@ -863,15 +854,14 @@ void System_w_FromParent_add_component_after_match_unmatch_match(void) { ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ECS_SYSTEM(world, Dummy, EcsOnUpdate, !Mass(parent), Position); + ECS_SYSTEM(world, Dummy, EcsOnUpdate, !Mass(up), Position); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); @@ -918,19 +908,17 @@ void System_w_FromParent_add_component_after_match_2_systems(void) { ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ECS_SYSTEM(world, Dummy, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Dummy, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Dummy, - .query.filter.instanced = true + .entity = Dummy }); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); @@ -992,10 +980,9 @@ void System_w_FromParent_add_component_in_progress_after_match(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); ECS_OBSERVER(world, AddMass, EcsOnAdd, Tag); @@ -1005,7 +992,7 @@ void System_w_FromParent_add_component_in_progress_after_match(void) { .ctx = &ecs_id(Mass) }); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); @@ -1052,13 +1039,12 @@ void System_w_FromParent_adopt_after_match(void) { ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); - ecs_entity_t parent = ecs_new(world, Mass); + ecs_entity_t parent = ecs_new_w(world, Mass); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); @@ -1103,10 +1089,9 @@ void System_w_FromParent_new_child_after_match(void) { ECS_ENTITY(world, e3, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Iter, - .query.filter.instanced = true + .entity = Iter }); ECS_ENTITY(world, parent, Mass); @@ -1147,10 +1132,10 @@ void System_w_FromParent_new_child_after_match(void) { } void IterSame(ecs_iter_t *it) { - Position *p_parent = ecs_field(it, Position, 1); - Position *p = ecs_field(it, Position, 2); + Position *p_parent = ecs_field(it, Position, 0); + Position *p = ecs_field(it, Position, 1); - test_assert(!ecs_field_is_self(it, 1)); + test_assert(!ecs_field_is_self(it, 0)); probe_iter(it); @@ -1172,13 +1157,12 @@ void System_w_FromParent_select_same_from_container(void) { ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); - ECS_SYSTEM(world, IterSame, EcsOnUpdate, Position(parent), Position); + ECS_SYSTEM(world, IterSame, EcsOnUpdate, Position(up), Position); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = IterSame, - .query.filter.instanced = true + .entity = IterSame }); - ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {1, 2})); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); ecs_add_pair(world, e3, EcsChildOf, parent); @@ -1232,9 +1216,9 @@ void System_w_FromParent_realloc_after_match(void) { ECS_ENTITY(world, e1, Position); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(parent), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(up), Position); - ecs_entity_t parent = ecs_set(world, 0, Mass, {2}); + ecs_entity_t parent = ecs_insert(world, ecs_value(Mass, {2})); ecs_add_pair(world, e1, EcsChildOf, parent); Probe ctx = {0}; diff --git a/vendors/flecs/test/addons/src/System_w_FromSystem.c b/vendors/flecs/test/addons/src/System_w_FromSystem.c index 673dcabd2..4c3c365cb 100644 --- a/vendors/flecs/test/addons/src/System_w_FromSystem.c +++ b/vendors/flecs/test/addons/src/System_w_FromSystem.c @@ -1,7 +1,7 @@ #include void InitVelocity(ecs_iter_t *it) { - Velocity *v = ecs_field(it, Velocity, 1); + Velocity *v = ecs_field(it, Velocity, 0); int i; for (i = 0; i < it->count; i ++) { @@ -11,7 +11,7 @@ void InitVelocity(ecs_iter_t *it) { } void InitMass(ecs_iter_t *it) { - Mass *m = ecs_field(it, Mass, 1); + Mass *m = ecs_field(it, Mass, 0); int i; for (i = 0; i < it->count; i ++) { m[i] = 3; @@ -19,19 +19,19 @@ void InitMass(ecs_iter_t *it) { } void Iter(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); Velocity *v = NULL; Mass *m = NULL; if (it->field_count >= 2) { - v = ecs_field(it, Velocity, 2); - test_assert(!ecs_field_is_self(it, 2)); + v = ecs_field(it, Velocity, 1); + test_assert(!ecs_field_is_self(it, 1)); } if (it->field_count >= 3) { - m = ecs_field(it, Mass, 3); - test_assert(!m || !ecs_field_is_self(it, 3)); + m = ecs_field(it, Mass, 2); + test_assert(!m || !ecs_field_is_self(it, 2)); } probe_iter(it); @@ -66,7 +66,7 @@ void System_w_FromSystem_2_column_1_from_system(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_set(world, 0, Position, {0, 0}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {0, 0})); ecs_progress(world, 1); @@ -120,7 +120,7 @@ void System_w_FromSystem_3_column_2_from_system(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_set(world, 0, Position, {0, 0}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {0, 0})); ecs_progress(world, 1); @@ -152,14 +152,14 @@ void System_w_FromSystem_3_column_2_from_system(void) { } void Iter_reactive(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); Velocity *v = it->param; Mass *m = NULL; if (it->field_count >= 2) { - v = ecs_field(it, Velocity, 2); - test_assert(!ecs_field_is_self(it, 2)); + v = ecs_field(it, Velocity, 1); + test_assert(!ecs_field_is_self(it, 1)); } probe_iter(it); diff --git a/vendors/flecs/test/addons/src/Timer.c b/vendors/flecs/test/addons/src/Timer.c index 0decef3af..fc4699236 100644 --- a/vendors/flecs/test/addons/src/Timer.c +++ b/vendors/flecs/test/addons/src/Timer.c @@ -25,7 +25,7 @@ void Timer_timeout(void) { ECS_SYSTEM(world, SystemA, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_entity_t timer = ecs_set_timeout(world, SystemA, 3.0); test_assert(timer != 0); @@ -60,7 +60,7 @@ void Timer_interval(void) { ECS_SYSTEM(world, SystemA, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_entity_t timer = ecs_set_interval(world, SystemA, 3.0); test_assert(timer != 0); @@ -95,7 +95,7 @@ void Timer_shared_timeout(void) { ECS_SYSTEM(world, SystemA, EcsOnUpdate, Position); ECS_SYSTEM(world, SystemB, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_entity_t timer = ecs_set_timeout(world, 0, 3.0); test_assert(timer != 0); @@ -140,7 +140,7 @@ void Timer_shared_interval(void) { ECS_SYSTEM(world, SystemA, EcsOnUpdate, Position); ECS_SYSTEM(world, SystemB, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_entity_t timer = ecs_set_interval(world, 0, 3.0); test_assert(timer != 0); @@ -184,7 +184,7 @@ void Timer_start_stop_one_shot(void) { ECS_SYSTEM(world, SystemA, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_entity_t timer = ecs_set_timeout(world, SystemA, 3.0); test_assert(timer != 0); @@ -226,7 +226,7 @@ void Timer_start_stop_interval(void) { ECS_SYSTEM(world, SystemA, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_entity_t timer = ecs_set_interval(world, SystemA, 3.0); test_assert(timer != 0); @@ -279,7 +279,7 @@ void Timer_rate_filter(void) { ECS_SYSTEM(world, SystemA, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_entity_t filter = ecs_set_rate(world, SystemA, 3, 0); test_assert(filter != 0); @@ -321,7 +321,7 @@ void Timer_rate_filter_w_rate_filter_src(void) { ECS_SYSTEM(world, SystemC, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_entity_t filter_a = ecs_set_rate(world, 0, 2, 0); test_assert(filter_a != 0); @@ -401,10 +401,10 @@ void Timer_rate_filter_with_empty_src(void) { ECS_SYSTEM(world, SystemC, EcsOnUpdate, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); // Not actually a tick source - ecs_entity_t filter_a = ecs_new_id(world); + ecs_entity_t filter_a = ecs_new(world); test_assert(filter_a != 0); ecs_entity_t filter_b = ecs_set_rate(world, SystemC, 6, filter_a); @@ -519,7 +519,7 @@ void Timer_rate_entity(void) { test_bool(src->tick, false); } - /* Filter should tick again */ + /* Query should tick again */ ecs_progress(world, 0); test_bool(src->tick, true); @@ -553,7 +553,7 @@ void Timer_nested_rate_entity(void) { test_bool(src->tick, false); } - /* Filter should tick again */ + /* Query should tick again */ ecs_progress(world, 0); test_bool(src->tick, true); @@ -564,7 +564,7 @@ void Timer_nested_rate_entity_empty_src(void) { ecs_world_t *world = ecs_init(); /* Rate filter with source that is not a tick source */ - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_entity_t rate = ecs_set_rate(world, 0, 4, parent); int i; @@ -587,7 +587,7 @@ void Timer_nested_rate_entity_empty_src(void) { test_bool(src->tick, false); } - /* Filter should tick again */ + /* Query should tick again */ ecs_progress(world, 0); test_bool(src->tick, true); @@ -597,7 +597,7 @@ void Timer_nested_rate_entity_empty_src(void) { void Timer_naked_tick_entity(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t tick = ecs_set(world, 0, EcsTickSource, {0}); + ecs_entity_t tick = ecs_insert(world, ecs_value(EcsTickSource, {0})); int i; for (i = 0; i < 10; i ++) { diff --git a/vendors/flecs/test/addons/src/main.c b/vendors/flecs/test/addons/src/main.c index 8ea364c20..e246f0076 100644 --- a/vendors/flecs/test/addons/src/main.c +++ b/vendors/flecs/test/addons/src/main.c @@ -8,493 +8,19 @@ #include -// Testsuite 'Parser' -void Parser_resolve_this(void); -void Parser_resolve_wildcard(void); -void Parser_resolve_any(void); -void Parser_resolve_is_a(void); -void Parser_0(void); -void Parser_component_implicit_subject(void); -void Parser_component_explicit_subject(void); -void Parser_component_explicit_subject_this(void); -void Parser_component_explicit_subject_this_by_name(void); -void Parser_component_explicit_subject_this_by_var_name(void); -void Parser_component_explicit_subject_wildcard(void); -void Parser_component_explicit_subject_any(void); -void Parser_component_explicit_subject_0(void); -void Parser_this_as_predicate(void); -void Parser_this_var_as_predicate(void); -void Parser_this_lowercase_var_as_predicate(void); -void Parser_this_as_object(void); -void Parser_this_var_as_object(void); -void Parser_pair_implicit_subject(void); -void Parser_pair_implicit_subject_wildcard_pred(void); -void Parser_pair_implicit_subject_wildcard_obj(void); -void Parser_pair_implicit_subject_any_pred(void); -void Parser_pair_implicit_subject_any_obj(void); -void Parser_pair_implicit_subject_this_pred(void); -void Parser_pair_implicit_subject_this_obj(void); -void Parser_pair_implicit_subject_pred_w_self(void); -void Parser_pair_implicit_subject_obj_w_self(void); -void Parser_pair_implicit_subject_pred_w_up(void); -void Parser_pair_implicit_subject_obj_w_up(void); -void Parser_pair_implicit_subject_pred_w_self_up(void); -void Parser_pair_implicit_subject_obj_w_self_up(void); -void Parser_pair_implicit_subject_pred_w_up_trav(void); -void Parser_pair_implicit_subject_obj_w_up_trav(void); -void Parser_pair_implicit_subject_pred_w_invalid_flags(void); -void Parser_pair_implicit_subject_obj_w_invalid_flags(void); -void Parser_pair_explicit_subject(void); -void Parser_pair_explicit_subject_this(void); -void Parser_pair_explicit_subject_this_by_name(void); -void Parser_pair_explicit_subject_this_by_var_name(void); -void Parser_pair_explicit_subject_wildcard_pred(void); -void Parser_pair_explicit_subject_wildcard_subj(void); -void Parser_pair_explicit_subject_wildcard_obj(void); -void Parser_pair_implicit_subject_0_object(void); -void Parser_pair_explicit_subject_0_object(void); -void Parser_pair_explicit_subject_0(void); -void Parser_in_component_implicit_subject(void); -void Parser_in_component_explicit_subject(void); -void Parser_in_pair_implicit_subject(void); -void Parser_in_pair_explicit_subject(void); -void Parser_inout_component_implicit_subject(void); -void Parser_inout_component_explicit_subject(void); -void Parser_inout_pair_implicit_subject(void); -void Parser_inout_pair_explicit_subject(void); -void Parser_out_component_implicit_subject(void); -void Parser_out_component_explicit_subject(void); -void Parser_out_pair_implicit_subject(void); -void Parser_out_pair_explicit_subject(void); -void Parser_inout_filter_component(void); -void Parser_component_singleton(void); -void Parser_this_singleton(void); -void Parser_component_implicit_no_subject(void); -void Parser_component_explicit_no_subject(void); -void Parser_pair_no_subject(void); -void Parser_variable_single_char(void); -void Parser_variable_multi_char(void); -void Parser_variable_multi_char_w_underscore(void); -void Parser_variable_multi_char_w_number(void); -void Parser_variable_multi_char_not_allcaps(void); -void Parser_pred_var(void); -void Parser_obj_var(void); -void Parser_component_not(void); -void Parser_pair_implicit_subject_not(void); -void Parser_pair_explicit_subject_not(void); -void Parser_2_component_not(void); -void Parser_2_component_not_no_space(void); -void Parser_component_optional(void); -void Parser_2_component_optional(void); -void Parser_2_component_optional_no_space(void); -void Parser_from_and(void); -void Parser_from_or(void); -void Parser_from_not(void); -void Parser_pair_implicit_subject_optional(void); -void Parser_pair_explicit_subject_optional(void); -void Parser_pred_implicit_subject_w_role(void); -void Parser_pred_explicit_subject_w_role(void); -void Parser_pred_no_subject_w_role(void); -void Parser_pair_implicit_subject_w_role(void); -void Parser_pair_explicit_subject_w_role(void); -void Parser_inout_role_pred_implicit_subject(void); -void Parser_inout_role_pred_no_subject(void); -void Parser_inout_role_pred_explicit_subject(void); -void Parser_inout_role_pair_implicit_subject(void); -void Parser_inout_role_pair_explicit_subject(void); -void Parser_2_pred_implicit_subject(void); -void Parser_2_pred_no_subject(void); -void Parser_2_pred_explicit_subject(void); -void Parser_2_pair_implicit_subject(void); -void Parser_2_pair_explicit_subject(void); -void Parser_2_pred_role(void); -void Parser_2_pair_implicit_subj_role(void); -void Parser_2_pair_explicit_subj_role(void); -void Parser_2_or_pred_implicit_subj(void); -void Parser_2_or_pred_explicit_subj(void); -void Parser_2_or_pair_implicit_subj(void); -void Parser_2_or_pair_explicit_subj(void); -void Parser_2_or_pred_inout(void); -void Parser_1_digit_pred_implicit_subj(void); -void Parser_1_digit_pred_no_subj(void); -void Parser_1_digit_pred_explicit_subj(void); -void Parser_1_digit_pair_implicit_subj(void); -void Parser_1_digit_pair_explicit_subj(void); -void Parser_pred_implicit_subject_self(void); -void Parser_pred_implicit_subject_superset(void); -void Parser_pred_implicit_subject_subset(void); -void Parser_pred_implicit_subject_superset_inclusive(void); -void Parser_pred_implicit_subject_subset_inclusive(void); -void Parser_pred_implicit_subject_superset_cascade(void); -void Parser_pred_implicit_subject_subset_cascade(void); -void Parser_pred_implicit_subject_superset_inclusive_cascade(void); -void Parser_pred_implicit_subject_subset_inclusive_cascade(void); -void Parser_pred_implicit_subject_implicit_superset_cascade(void); -void Parser_pred_implicit_subject_implicit_superset_inclusive_cascade(void); -void Parser_pred_implicit_subject_implicit_superset_cascade_w_rel(void); -void Parser_pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel(void); -void Parser_pred_implicit_subject_superset_childof(void); -void Parser_pred_implicit_subject_cascade_superset_childof(void); -void Parser_pred_implicit_subject_superset_cascade_childof(void); -void Parser_pred_implicit_subject_superset_cascade_childof_optional(void); -void Parser_expr_w_symbol(void); -void Parser_expr_w_newline(void); -void Parser_subj_entity_w_explicit_self(void); -void Parser_subj_entity_w_explicit_self_superset(void); -void Parser_subj_entity_w_explicit_superset_relation(void); -void Parser_subj_entity_w_explicit_self_superset_relation(void); -void Parser_obj_entity_w_explicit_self(void); -void Parser_obj_entity_w_explicit_self_superset(void); -void Parser_obj_entity_w_explicit_superset_relation(void); -void Parser_obj_entity_w_explicit_self_superset_relation(void); -void Parser_pred_entity_w_explicit_self(void); -void Parser_pred_entity_w_explicit_self_superset(void); -void Parser_pred_entity_w_explicit_superset_relation(void); -void Parser_pred_entity_w_explicit_self_superset_relation(void); -void Parser_pred_entity_no_args_w_explicit_self(void); -void Parser_pred_entity_no_args_w_explicit_self_superset(void); -void Parser_pred_entity_no_args_w_explicit_superset_relation(void); -void Parser_pred_entity_no_args_w_explicit_self_superset_relation(void); -void Parser_pred_entity_no_args_2_terms_w_explicit_self(void); -void Parser_pred_entity_no_args_2_terms_w_explicit_self_superset(void); -void Parser_pred_entity_no_args_2_terms_w_explicit_superset_relation(void); -void Parser_pred_entity_no_args_2_terms_w_explicit_self_superset_relation(void); -void Parser_newline(void); -void Parser_2_newlines(void); -void Parser_3_newlines(void); -void Parser_space(void); -void Parser_2_spaces(void); -void Parser_trailing_newline(void); -void Parser_2_trailing_newlines(void); -void Parser_trailing_space(void); -void Parser_2_trailing_spaces(void); -void Parser_template_type(void); -void Parser_predicate_w_parens(void); -void Parser_not_alive_pred(void); -void Parser_not_alive_subj(void); -void Parser_not_alive_obj(void); -void Parser_this_subj_var_kind(void); -void Parser_this_obj_var_kind(void); -void Parser_this_subj_obj_var_kind(void); -void Parser_var_w_name(void); -void Parser_entity_pred_no_name(void); -void Parser_entity_subj_no_name(void); -void Parser_entity_obj_no_name(void); -void Parser_this_pred_no_name(void); -void Parser_this_subj_no_name(void); -void Parser_this_obj_no_name(void); -void Parser_auto_object_variable(void); -void Parser_auto_object_variable_w_subj(void); -void Parser_auto_scoped_variable(void); -void Parser_invalid_variable_only(void); -void Parser_oneof_self_pred_w_relative_obj(void); -void Parser_oneof_other_pred_w_relative_obj(void); -void Parser_oneof_self_pred_w_invalid_obj(void); -void Parser_oneof_other_pred_w_invalid_obj(void); -void Parser_pair_implicit_src_missing_rel(void); -void Parser_pair_implicit_src_missing_obj(void); -void Parser_pair_explicit_src_missing_src(void); -void Parser_pair_explicit_src_missing_obj(void); -void Parser_eq_id(void); -void Parser_eq_id_var(void); -void Parser_eq_var_id(void); -void Parser_eq_var(void); -void Parser_neq_id(void); -void Parser_neq_id_var(void); -void Parser_neq_var_id(void); -void Parser_neq_var(void); -void Parser_eq_name(void); -void Parser_eq_name_var(void); -void Parser_eq_var_name(void); -void Parser_eq_var(void); -void Parser_neq_name(void); -void Parser_neq_name_var(void); -void Parser_neq_var_name(void); -void Parser_neq_var(void); -void Parser_match_name(void); -void Parser_match_name_var(void); -void Parser_match_var_name(void); -void Parser_match_var(void); -void Parser_nmatch_name(void); -void Parser_nmatch_name_var(void); -void Parser_nmatch_var_name(void); -void Parser_eq_same_var(void); -void Parser_neq_same_var(void); -void Parser_eq_same_var_this(void); -void Parser_neq_same_var_this(void); -void Parser_eq_w_optional(void); -void Parser_neq_w_optional(void); -void Parser_match_w_optional(void); -void Parser_query_scope_1_term(void); -void Parser_query_scope_1_term_spaces(void); -void Parser_query_scope_2_terms(void); -void Parser_query_nested_scope(void); -void Parser_query_nested_scope_spaces(void); -void Parser_query_scope_unbalanced(void); -void Parser_query_not_scope(void); -void Parser_query_empty_scope(void); -void Parser_override_tag(void); -void Parser_override_pair(void); -void Parser_pair_3_args(void); -void Parser_pair_3_args_implicit_this(void); -void Parser_pair_4_args(void); -void Parser_pair_4_args_implicit_this(void); -void Parser_pair_3_args_2_terms(void); -void Parser_pair_3_args_this_tgt(void); -void Parser_pair_3_args_2_terms_this_tgt(void); -void Parser_pair_3_args_2_terms_this_tgt_implicit_this(void); -void Parser_cascade_desc(void); - -// Testsuite 'Plecs' -void Plecs_null(void); -void Plecs_empty(void); -void Plecs_space(void); -void Plecs_space_newline(void); -void Plecs_two_empty_newlines(void); -void Plecs_three_empty_newlines(void); -void Plecs_newline_trailing_space(void); -void Plecs_newline_trailing_spaces(void); -void Plecs_multiple_trailing_newlines(void); -void Plecs_entity(void); -void Plecs_entity_w_entity(void); -void Plecs_entity_w_pair(void); -void Plecs_2_entities(void); -void Plecs_2_entities_w_entities(void); -void Plecs_3_entities_w_pairs(void); -void Plecs_line_comment(void); -void Plecs_line_comment_before_stmt(void); -void Plecs_line_comment_after_stmt(void); -void Plecs_line_comment_between_stmt(void); -void Plecs_multiple_line_comment(void); -void Plecs_line_comment_after_stmt_same_line(void); -void Plecs_comma_separated_pred(void); -void Plecs_comma_separated_pred_w_subj(void); -void Plecs_comma_separated_pred_w_subj_obj(void); -void Plecs_comma_separated_pred_trailing_comma(void); -void Plecs_comma_separated_pred_trailing_comma_newline(void); -void Plecs_comma_separated_pred_trailing_comma_newline_multiline(void); -void Plecs_hierarchy_1_child(void); -void Plecs_hierarchy_2_children(void); -void Plecs_hierarchy_1_child_same_line(void); -void Plecs_hierarchy_2_children_same_line(void); -void Plecs_entity_after_hierarchy(void); -void Plecs_newline_before_scope_open(void); -void Plecs_comment_before_scope_open(void); -void Plecs_comment_after_newline_before_scope_open(void); -void Plecs_hierarchy_2_levels(void); -void Plecs_hierarchy_2_levels_2_subtrees(void); -void Plecs_missing_end_of_scope(void); -void Plecs_missing_end_of_predicate_scope(void); -void Plecs_create_in_scope(void); -void Plecs_hierarchy_w_pred_subj(void); -void Plecs_hierarchy_custom_relation(void); -void Plecs_hierarchy_custom_relation_2_levels(void); -void Plecs_hierarchy_custom_relation_apply_to_object(void); -void Plecs_hierarchy_custom_relation_apply_to_object_2_levels(void); -void Plecs_entity_after_hierarchy_custom_relation(void); -void Plecs_entity_after_hierarchy_custom_relation_2_levels(void); -void Plecs_pred_scope(void); -void Plecs_pred_scope_2_levels(void); -void Plecs_pred_scope_inside_with(void); -void Plecs_pred_scope_nested_w_subj_scope(void); -void Plecs_with_tag(void); -void Plecs_with_tag_2_entities(void); -void Plecs_with_tag_same_line(void); -void Plecs_with_tag_2_entities_same_line(void); -void Plecs_with_tag_2_levels(void); -void Plecs_with_tag_2_levels_2_subtrees(void); -void Plecs_with_n_tags(void); -void Plecs_with_n_tags_2_levels(void); -void Plecs_with_after_scope(void); -void Plecs_with_after_with(void); -void Plecs_scope_inside_with_inside_scope(void); -void Plecs_with_inside_scope(void); -void Plecs_assignment_w_1(void); -void Plecs_assignment_w_2(void); -void Plecs_assignment_w_pair(void); -void Plecs_assignment_w_invalid_subject(void); -void Plecs_assignment_w_invalid_with(void); -void Plecs_inherit_w_colon(void); -void Plecs_inherit_w_colon_w_scope(void); -void Plecs_inherit_w_colon_w_assign(void); -void Plecs_assign_component_value(void); -void Plecs_assign_2_component_values(void); -void Plecs_assign_component_value_in_assign_scope(void); -void Plecs_assign_2_component_values_in_assign_scope(void); -void Plecs_type_and_assign_in_plecs(void); -void Plecs_type_and_assign_in_plecs_w_2_members(void); -void Plecs_type_and_assign_in_plecs_w_3_members(void); -void Plecs_type_and_assign_in_plecs_w_enum(void); -void Plecs_type_and_assign_in_plecs_w_enum_using_meta(void); -void Plecs_type_and_assign_in_plecs_w_enum_primitive_using_meta(void); -void Plecs_type_and_assign_in_plecs_w_enum_primitive_and_struct(void); -void Plecs_type_and_assign_in_plecs_nested_member(void); -void Plecs_dot_assign_nested_member(void); -void Plecs_dot_assign_binary_expr(void); -void Plecs_open_scope_no_parent(void); -void Plecs_create_subject_in_root_scope_w_resolvable_id(void); -void Plecs_create_subject_in_scope_w_resolvable_id(void); -void Plecs_create_subject_in_scope_w_resolvable_id_using(void); -void Plecs_using_scope(void); -void Plecs_using_nested_scope(void); -void Plecs_using_nested_in_scope(void); -void Plecs_using_with_scope(void); -void Plecs_using_w_entity_ref_in_value_2_members(void); -void Plecs_using_w_entity_ref_in_value_3_members(void); -void Plecs_2_using_scope(void); -void Plecs_2_using_in_different_scope(void); -void Plecs_empty_scope_after_using(void); -void Plecs_assignment_to_non_component(void); -void Plecs_struct_w_member_w_assignment_to_nothing(void); -void Plecs_struct_w_member_w_assignment_to_empty_scope(void); -void Plecs_scope_after_assign(void); -void Plecs_assign_after_inherit(void); -void Plecs_multiple_assignments_single_line(void); -void Plecs_2_stmts_in_scope_w_no_parent(void); -void Plecs_scope_after_assign_1_tag(void); -void Plecs_scope_after_assign_2_tags(void); -void Plecs_invalid_nested_assignment(void); -void Plecs_invalid_partial_pair_assignment(void); -void Plecs_empty_assignment(void); -void Plecs_assign_tag_to_parent(void); -void Plecs_assign_component_to_parent(void); -void Plecs_empty_assignment_before_end_of_scope(void); -void Plecs_assign_to_parent_pair_w_new_entities_in_scope(void); -void Plecs_assign_to_parent_pair_w_existing_entities_in_scope(void); -void Plecs_default_child_component(void); -void Plecs_default_child_component_w_assign(void); -void Plecs_struct_type_w_default_child_component(void); -void Plecs_struct_type_w_default_child_component_nested_member(void); -void Plecs_enum_type_w_default_child_component(void); -void Plecs_default_type_from_with(void); -void Plecs_scope_w_1_subj_and_2_pairs(void); -void Plecs_inherit_from_multiple(void); -void Plecs_assign_pair_component(void); -void Plecs_assign_pair_component_in_scope(void); -void Plecs_assign_pair_component_in_script(void); -void Plecs_assign_pair_component_in_script_update(void); -void Plecs_set_entity_names(void); -void Plecs_oneof(void); -void Plecs_invalid_oneof(void); -void Plecs_brief_annotation(void); -void Plecs_name_annotation(void); -void Plecs_link_annotation(void); -void Plecs_color_annotation(void); -void Plecs_multiple_annotations(void); -void Plecs_annotation_w_trailing_space(void); -void Plecs_multiline_string(void); -void Plecs_unterminated_multiline_string(void); -void Plecs_declaration_w_underscore_name(void); -void Plecs_annotate_declaration(void); -void Plecs_anonymous_entity(void); -void Plecs_anonymous_entity_in_scope(void); -void Plecs_anonymous_declaration(void); -void Plecs_const_var_int(void); -void Plecs_const_var_float(void); -void Plecs_const_var_bool(void); -void Plecs_const_var_string(void); -void Plecs_const_var_struct(void); -void Plecs_const_var_redeclare(void); -void Plecs_const_var_scoped(void); -void Plecs_assign_component_from_var(void); -void Plecs_assign_component_from_var_in_scope(void); -void Plecs_scope_w_component_after_const_var(void); -void Plecs_component_after_const_paren_expr(void); -void Plecs_component_after_const_add_expr(void); -void Plecs_component_after_const_sub_expr(void); -void Plecs_component_after_const_mul_expr(void); -void Plecs_component_after_const_div_expr(void); -void Plecs_parse_with(void); -void Plecs_parse_with_w_with(void); -void Plecs_parse_with_w_tag(void); -void Plecs_parse_with_value(void); -void Plecs_parse_with_2_values(void); -void Plecs_parse_with_2_nested_values(void); -void Plecs_parse_with_var(void); -void Plecs_parse_with_2_vars(void); -void Plecs_parse_with_2_nested_vars(void); -void Plecs_parse_with_var_in_scope(void); -void Plecs_assign_const_w_expr(void); -void Plecs_const_w_type(void); -void Plecs_assembly_no_scope(void); -void Plecs_assembly_empty(void); -void Plecs_assembly_no_props(void); -void Plecs_assembly_prop_no_type(void); -void Plecs_assembly_prop_no_default(void); -void Plecs_assembly_prop(void); -void Plecs_assembly_prop_space_colon(void); -void Plecs_assembly_2_props(void); -void Plecs_assembly_instance_w_default_values(void); -void Plecs_assembly_instance_w_assign_default_values(void); -void Plecs_assembly_instance_w_overridden_values(void); -void Plecs_assembly_w_child(void); -void Plecs_assembly_w_child_parse_script(void); -void Plecs_assembly_w_child_parse_script_twice(void); -void Plecs_assembly_w_child_update_after_parse(void); -void Plecs_assembly_w_nested_child(void); -void Plecs_assembly_w_prefab(void); -void Plecs_assembly_w_prefab_tree(void); -void Plecs_assembly_w_nested_assembly(void); -void Plecs_instantiate_prefab_w_assembly(void); -void Plecs_assembly_w_prefab_w_assembly(void); -void Plecs_3_assemblies(void); -void Plecs_assembly_nested_w_default_var(void); -void Plecs_assembly_w_anonymous(void); -void Plecs_assembly_w_anonymous_parse_again(void); -void Plecs_typed_const_w_composite_type_invalid_assignment(void); -void Plecs_typed_const_w_composite_type(void); -void Plecs_assign_var_to_typed_const_w_composite_type(void); -void Plecs_typed_const_w_composite_type_invalid_assignment(void); -void Plecs_assembly_w_composite_prop_invalid_assignment(void); -void Plecs_assembly_w_composite_prop(void); -void Plecs_assembly_with_with(void); -void Plecs_using_wildcard(void); -void Plecs_single_line_comment_in_value(void); -void Plecs_single_line_comment_in_value_after_scope(void); -void Plecs_multi_line_comment_in_value(void); -void Plecs_multi_line_comment_in_value_after_scope(void); -void Plecs_unterminated_multi_line_comment_in_value(void); -void Plecs_module_stmt(void); -void Plecs_nested_module_stmt(void); -void Plecs_module_stmt_w_scope(void); -void Plecs_module_stmt_w_nested_scope(void); -void Plecs_module_w_assembly(void); -void Plecs_module_w_nested_assembly(void); -void Plecs_assign_singleton_tag(void); -void Plecs_assign_singleton_component(void); -void Plecs_assign_singleton_tag_w_scope(void); -void Plecs_assign_singleton_2_tags_w_scope(void); -void Plecs_assign_singleton_component_w_scope(void); -void Plecs_assign_singleton_2_components_w_scope(void); -void Plecs_with_pair_in_scope(void); -void Plecs_assembly_redeclare_prop_as_const(void); -void Plecs_assembly_redeclare_prop_as_prop(void); -void Plecs_assembly_redeclare_const_as_const(void); -void Plecs_add_auto_override(void); -void Plecs_add_auto_override_pair(void); -void Plecs_scope_w_auto_override(void); -void Plecs_scope_w_auto_override_pair(void); -void Plecs_pair_w_rel_var(void); -void Plecs_pair_w_tgt_var(void); -void Plecs_assembly_w_pair_w_this_var(void); -void Plecs_with_value_not_a_component(void); -void Plecs_component_in_with_scope(void); -void Plecs_component_in_with_scope_nested(void); -void Plecs_component_in_with_scope_in_scope(void); -void Plecs_assign_after_with_in_scope(void); -void Plecs_array_component(void); - // Testsuite 'Doc' void Doc_get_set_name(void); void Doc_get_entity_name(void); void Doc_get_set_brief(void); void Doc_get_set_detail(void); void Doc_get_set_link(void); +void Doc_get_set_uuid(void); void Doc_set_name_nullptr(void); void Doc_set_brief_nullptr(void); void Doc_set_detail_nullptr(void); void Doc_set_link_nullptr(void); void Doc_set_color_nullptr(void); +void Doc_set_uuid_nullptr(void); // Testsuite 'Pipeline' void Pipeline_system_order_same_phase(void); @@ -579,12 +105,14 @@ void Pipeline_run_pipeline_multithreaded(void); void Pipeline_run_pipeline_multithreaded_tasks(void); void Pipeline_pipeline_init_no_terms(void); void Pipeline_pipeline_init_no_system_term(void); +void Pipeline_disable_component_from_immediate_system(void); +void Pipeline_run_w_empty_query(void); +void Pipeline_run_w_0_src_query(void); // Testsuite 'SystemMisc' void SystemMisc_invalid_not_without_id(void); void SystemMisc_invalid_optional_without_id(void); void SystemMisc_invalid_entity_without_id(void); -void SystemMisc_invalid_empty_without_id(void); void SystemMisc_invalid_empty_element(void); void SystemMisc_invalid_empty_element_w_space(void); void SystemMisc_invalid_empty_or(void); @@ -605,7 +133,6 @@ void SystemMisc_system_w_or_disabled_and_prefab(void); void SystemMisc_table_columns_access(void); void SystemMisc_dont_enable_after_rematch(void); void SystemMisc_ensure_single_merge(void); -void SystemMisc_table_count(void); void SystemMisc_match_system(void); void SystemMisc_system_initial_state(void); void SystemMisc_add_own_component(void); @@ -615,8 +142,6 @@ void SystemMisc_system_readeactivate_w_2_systems(void); void SystemMisc_add_to_system_in_progress(void); void SystemMisc_redefine_null_signature(void); void SystemMisc_redefine_0_signature(void); -void SystemMisc_one_named_column_of_two(void); -void SystemMisc_two_named_columns_of_two(void); void SystemMisc_redeclare_system_explicit_id(void); void SystemMisc_redeclare_system_explicit_id_null_expr(void); void SystemMisc_redeclare_system_explicit_id_no_name(void); @@ -640,7 +165,6 @@ void SystemMisc_delete_pipeline_system(void); void SystemMisc_delete_system_w_ctx(void); void SystemMisc_update_ctx(void); void SystemMisc_run_custom_run_action(void); -void SystemMisc_run_w_offset_limit_custom_run_action(void); void SystemMisc_pipeline_custom_run_action(void); void SystemMisc_change_custom_run_action(void); void SystemMisc_custom_run_action_call_next(void); @@ -648,702 +172,13 @@ void SystemMisc_system_w_short_notation(void); void SystemMisc_update_interval_w_system_init(void); void SystemMisc_update_rate_w_system_init(void); void SystemMisc_system_w_interval_rate_stop_timer(void); +void SystemMisc_system_w_rate_filter_self(void); void SystemMisc_system_same_interval_same_tick(void); - -// Testsuite 'RulesBasic' -void RulesBasic_1_fact_w_tag(void); -void RulesBasic_1_fact_w_component(void); -void RulesBasic_1_fact_w_tag_pair(void); -void RulesBasic_1_fact_w_component_pair(void); -void RulesBasic_2_facts_same_src_w_tag(void); -void RulesBasic_2_facts_same_src_w_component(void); -void RulesBasic_2_facts_same_src_w_tag_pair(void); -void RulesBasic_2_facts_same_src_w_component_pair(void); -void RulesBasic_2_facts_other_src_w_tag(void); -void RulesBasic_2_facts_other_src_w_component(void); -void RulesBasic_2_facts_other_src_w_tag_pair(void); -void RulesBasic_2_facts_other_src_w_component_pair(void); -void RulesBasic_1_fact_w_any(void); -void RulesBasic_1_fact_w_pair_any_tgt(void); -void RulesBasic_1_fact_w_pair_any_rel(void); -void RulesBasic_1_fact_w_pair_any_rel_tgt(void); -void RulesBasic_2_facts_same_src_w_any(void); -void RulesBasic_2_facts_same_src_w_pair_any_tgt(void); -void RulesBasic_2_facts_same_src_w_pair_any_rel(void); -void RulesBasic_2_facts_same_src_w_pair_any_rel_tgt(void); -void RulesBasic_2_facts_other_src_w_any(void); -void RulesBasic_2_facts_other_src_w_pair_any_tgt(void); -void RulesBasic_2_facts_other_src_w_pair_any_rel(void); -void RulesBasic_2_facts_other_src_w_pair_any_rel_tgt(void); -void RulesBasic_1_this_src_w_tag(void); -void RulesBasic_1_this_src_w_component(void); -void RulesBasic_1_this_src_w_tag_pair(void); -void RulesBasic_1_this_src_w_component_pair(void); -void RulesBasic_1_this_src_w_tag_2_tables(void); -void RulesBasic_1_this_src_w_component_2_tables(void); -void RulesBasic_1_this_src_w_tag_pair_2_tables(void); -void RulesBasic_1_this_src_w_component_pair_2_tables(void); -void RulesBasic_2_this_src_w_tag(void); -void RulesBasic_2_this_src_w_component(void); -void RulesBasic_2_this_src_ent_src_w_tag(void); -void RulesBasic_2_this_src_ent_src_w_component(void); -void RulesBasic_2_ent_src_this_src_w_tag(void); -void RulesBasic_2_ent_src_this_src_w_component(void); -void RulesBasic_recycled_tag(void); -void RulesBasic_recycled_src(void); -void RulesBasic_recycled_pair_rel(void); -void RulesBasic_recycled_pair_tgt(void); -void RulesBasic_this_src_w_wildcard(void); -void RulesBasic_this_src_w_pair_rel_wildcard(void); -void RulesBasic_this_src_w_pair_tgt_wildcard(void); -void RulesBasic_this_src_w_pair_rel_tgt_wildcard(void); -void RulesBasic_this_src_w_any(void); -void RulesBasic_this_src_w_any_written(void); -void RulesBasic_this_src_w_pair_rel_any(void); -void RulesBasic_this_src_w_pair_tgt_any(void); -void RulesBasic_this_src_w_pair_rel_tgt_any(void); -void RulesBasic_ent_src_w_wildcard(void); -void RulesBasic_ent_src_w_pair_rel_wildcard(void); -void RulesBasic_ent_src_w_pair_tgt_wildcard(void); -void RulesBasic_ent_src_w_pair_rel_tgt_wildcard(void); -void RulesBasic_1_wildcard_src(void); -void RulesBasic_1_wildcard_src_w_pair(void); -void RulesBasic_2_wildcard_src(void); -void RulesBasic_2_wildcard_src_w_pair(void); -void RulesBasic_1_wildcard_src_w_pair_tgt_var(void); -void RulesBasic_1_wildcard_src_w_pair_rel_var(void); -void RulesBasic_1_wildcard_src_w_pair_tgt_this(void); -void RulesBasic_1_wildcard_src_w_pair_rel_this(void); -void RulesBasic_1_any_src(void); -void RulesBasic_1_any_src_w_pair(void); -void RulesBasic_2_any_src(void); -void RulesBasic_2_any_src_w_pair(void); -void RulesBasic_1_any_src_w_pair_tgt_var(void); -void RulesBasic_1_any_src_w_pair_rel_var(void); -void RulesBasic_1_any_src_w_pair_tgt_this(void); -void RulesBasic_1_any_src_w_pair_rel_this(void); -void RulesBasic_not_any(void); -void RulesBasic_rule_w_iter_next(void); -void RulesBasic_empty_rule(void); -void RulesBasic_invalid_rule(void); -void RulesBasic_not_instanced_table_src(void); -void RulesBasic_not_instanced_entity_src(void); -void RulesBasic_not_instanced_mixed_src(void); -void RulesBasic_instanced_table_src(void); -void RulesBasic_instanced_entity_src(void); -void RulesBasic_instanced_mixed_src(void); -void RulesBasic_in_term(void); -void RulesBasic_out_term(void); -void RulesBasic_inout_term(void); -void RulesBasic_nodata_term(void); -void RulesBasic_find_this_lowercase(void); -void RulesBasic_find_this_uppercase(void); -void RulesBasic_find_this_tgt_lowercase(void); -void RulesBasic_find_this_tgt_uppercase(void); -void RulesBasic_get_filter(void); -void RulesBasic_iter_empty_source(void); -void RulesBasic_iter_empty_source_2_terms(void); -void RulesBasic_iter_empty_source_wildcard(void); -void RulesBasic_iter_empty_source_pair(void); -void RulesBasic_iter_empty_source_pair_wildcard(void); -void RulesBasic_iter_empty_source_2_terms_pair(void); -void RulesBasic_iter_empty_source_2_terms_mixed(void); -void RulesBasic_iter_empty_source_2_terms_mixed_pair(void); -void RulesBasic_iter_empty_source_2_terms_mixed_pair_wildcard(void); -void RulesBasic_this_var_w_empty_entity(void); -void RulesBasic_match_disabled(void); -void RulesBasic_match_prefab(void); -void RulesBasic_match_disabled_prefab(void); -void RulesBasic_match_disabled_this_tgt(void); -void RulesBasic_match_prefab_this_tgt(void); -void RulesBasic_match_disabled_prefab_this_tgt(void); -void RulesBasic_match_self_disabled(void); -void RulesBasic_match_self_prefab(void); -void RulesBasic_match_self_disabled_prefab(void); -void RulesBasic_inout_none_first_term(void); -void RulesBasic_inout_none_second_term(void); -void RulesBasic_no_data_rule(void); -void RulesBasic_frame_offset(void); -void RulesBasic_frame_offset_no_data(void); -void RulesBasic_match_empty_tables(void); -void RulesBasic_match_empty_tables_no_data(void); -void RulesBasic_match_empty_tables_w_not(void); -void RulesBasic_match_empty_tables_w_wildcard(void); -void RulesBasic_match_empty_tables_w_no_empty_tables(void); -void RulesBasic_match_empty_tables_trivial(void); -void RulesBasic_oneof_wildcard(void); -void RulesBasic_oneof_any(void); -void RulesBasic_instanced_w_singleton(void); -void RulesBasic_instanced_w_base(void); -void RulesBasic_not_instanced_w_singleton(void); -void RulesBasic_not_instanced_w_base(void); -void RulesBasic_unknown_before_known(void); -void RulesBasic_unknown_before_known_after_or(void); -void RulesBasic_unknown_before_known_after_not(void); -void RulesBasic_unknown_before_known_after_optional(void); -void RulesBasic_unknown_before_known_after_scope(void); -void RulesBasic_reordered_plan_1(void); -void RulesBasic_reordered_plan_2(void); -void RulesBasic_1_trivial_plan(void); -void RulesBasic_2_trivial_plan(void); -void RulesBasic_1_trivial_plan_component(void); -void RulesBasic_2_trivial_plan_component(void); -void RulesBasic_3_trivial_plan_w_pair(void); -void RulesBasic_3_trivial_plan_w_wildcard(void); -void RulesBasic_3_trivial_plan_w_any(void); -void RulesBasic_3_trivial_plan_w_pair_component(void); -void RulesBasic_3_trivial_plan_w_wildcard_component(void); -void RulesBasic_3_trivial_plan_w_any_component(void); -void RulesBasic_1_trivial_component_w_none(void); -void RulesBasic_2_trivial_component_w_none(void); - -// Testsuite 'RulesVariables' -void RulesVariables_1_ent_src_w_var(void); -void RulesVariables_1_ent_src_w_pair_rel_var(void); -void RulesVariables_1_ent_src_w_pair_tgt_var(void); -void RulesVariables_1_ent_src_w_pair_rel_tgt_var(void); -void RulesVariables_1_ent_src_w_pair_rel_tgt_same_var(void); -void RulesVariables_1_ent_src_w_pair_rel_tgt_same_var_after_write(void); -void RulesVariables_1_this_src_w_var(void); -void RulesVariables_1_this_src_w_pair_rel_var(void); -void RulesVariables_1_this_src_w_pair_tgt_var(void); -void RulesVariables_1_this_src_w_pair_rel_tgt_var(void); -void RulesVariables_1_this_src_w_pair_rel_tgt_same_var(void); -void RulesVariables_1_this_src_w_pair_rel_tgt_same_var_after_write(void); -void RulesVariables_1_src_id_same_var(void); -void RulesVariables_1_src_pair_first_same_var(void); -void RulesVariables_1_src_pair_second_same_var(void); -void RulesVariables_1_src_pair_first_and_second_same_var(void); -void RulesVariables_1_src_id_same_var_after_write(void); -void RulesVariables_1_src_pair_first_same_var_after_write(void); -void RulesVariables_1_src_pair_second_same_var_after_write(void); -void RulesVariables_1_src_pair_first_and_second_same_var_after_write(void); -void RulesVariables_1_src_pair_first_same_var_this(void); -void RulesVariables_1_src_pair_second_same_var_this(void); -void RulesVariables_1_src_pair_first_and_second_same_var_this(void); -void RulesVariables_1_src_id_same_var_this_after_write(void); -void RulesVariables_1_src_pair_first_same_var_this_after_write(void); -void RulesVariables_1_src_pair_second_same_var_this_after_write(void); -void RulesVariables_1_src_pair_first_and_second_same_var_this_after_write(void); -void RulesVariables_1_ent_src_w_this_var(void); -void RulesVariables_1_ent_src_w_pair_this_rel(void); -void RulesVariables_1_ent_src_w_pair_this_tgt(void); -void RulesVariables_1_ent_src_w_pair_this_rel_tgt(void); -void RulesVariables_1_this_src_w_this(void); -void RulesVariables_1_this_src_w_pair_this_rel_tgt(void); -void RulesVariables_1_this_src_w_this_after_write(void); -void RulesVariables_1_this_src_w_pair_this_rel_tgt_after_write(void); -void RulesVariables_2_constrain_src_from_src(void); -void RulesVariables_2_constrain_rel_from_src_w_ent(void); -void RulesVariables_2_constrain_rel_from_src_w_var(void); -void RulesVariables_2_constrain_rel_from_src_w_this(void); -void RulesVariables_2_constrain_pair_rel_from_src_w_ent(void); -void RulesVariables_2_constrain_pair_rel_from_src_w_var(void); -void RulesVariables_2_constrain_pair_rel_from_src_w_this(void); -void RulesVariables_2_constrain_pair_tgt_from_src_w_ent(void); -void RulesVariables_2_constrain_pair_tgt_from_src_w_var(void); -void RulesVariables_2_constrain_pair_tgt_from_src_w_this(void); -void RulesVariables_2_constrain_pair_rel_tgt_from_src_w_ent(void); -void RulesVariables_2_constrain_pair_rel_tgt_from_src_w_var(void); -void RulesVariables_2_constrain_pair_rel_tgt_from_src_w_this(void); -void RulesVariables_1_ent_src_set_rel_var(void); -void RulesVariables_1_ent_src_set_pair_rel_var(void); -void RulesVariables_1_ent_src_set_pair_tgt_var(void); -void RulesVariables_1_set_src_var(void); -void RulesVariables_1_set_src_var_w_pair(void); -void RulesVariables_1_set_src_var_w_pair_set_rel(void); -void RulesVariables_1_set_src_var_w_pair_set_tgt(void); -void RulesVariables_1_set_src_var_w_pair_set_rel_tgt(void); -void RulesVariables_1_set_src_this(void); -void RulesVariables_1_set_src_this_w_pair(void); -void RulesVariables_1_set_src_this_w_pair_set_rel(void); -void RulesVariables_1_set_src_this_w_pair_set_tgt(void); -void RulesVariables_1_set_src_this_w_pair_set_rel_tgt(void); -void RulesVariables_1_set_src_this_to_empty_table(void); -void RulesVariables_1_set_src_this_to_empty_table_w_component(void); -void RulesVariables_1_set_src_this_to_empty_table_w_component_self(void); -void RulesVariables_1_set_src_this_to_entiy_in_table(void); -void RulesVariables_1_set_src_this_to_entiy_in_table_self(void); -void RulesVariables_2_set_src_this(void); -void RulesVariables_2_set_src_this_self(void); -void RulesVariables_2_set_src_this_component(void); -void RulesVariables_2_set_src_this_self_component(void); -void RulesVariables_2_set_src_this_w_up(void); -void RulesVariables_2_set_src_this_self_w_up(void); -void RulesVariables_2_set_src_this_component_w_up(void); -void RulesVariables_2_set_src_this_self_component_w_up(void); -void RulesVariables_2_set_src_this_w_exclusive_wildcard(void); -void RulesVariables_2_set_src_this_self_w_exclusive_wildcard(void); -void RulesVariables_1_src_this_var_as_entity(void); -void RulesVariables_1_src_this_var_as_table(void); -void RulesVariables_1_src_this_var_as_table_range(void); -void RulesVariables_2_join_by_rel_var(void); -void RulesVariables_2_join_by_pair_rel_var(void); -void RulesVariables_2_join_by_pair_tgt_var(void); -void RulesVariables_2_cycle_w_var(void); -void RulesVariables_2_cycle_w_this_var(void); -void RulesVariables_2_cycle_w_var_this(void); -void RulesVariables_2_cycle_pair_w_var(void); -void RulesVariables_2_cycle_pair_w_this_var_var(void); -void RulesVariables_2_cycle_pair_w_var_this_var(void); -void RulesVariables_2_cycle_pair_w_var_var_this(void); -void RulesVariables_2_cycle_pair_ent_var_var(void); -void RulesVariables_2_cycle_pair_ent_this_var(void); -void RulesVariables_2_cycle_pair_ent_var_this(void); -void RulesVariables_parse_0_var(void); -void RulesVariables_parse_1_var(void); -void RulesVariables_parse_2_vars(void); -void RulesVariables_parse_0_var_paren(void); -void RulesVariables_parse_1_var_paren(void); -void RulesVariables_parse_2_vars_paren(void); -void RulesVariables_parse_1_vars_w_path(void); -void RulesVariables_parse_missing_close_paren(void); -void RulesVariables_parse_missing_open_paren(void); -void RulesVariables_parse_missing_value(void); -void RulesVariables_parse_0_var_w_spaces(void); -void RulesVariables_parse_1_var_w_spaces(void); -void RulesVariables_parse_2_vars_w_spaces(void); -void RulesVariables_parse_0_var_paren_w_spaces(void); -void RulesVariables_parse_1_var_paren_w_spaces(void); -void RulesVariables_parse_2_vars_paren_w_spaces(void); -void RulesVariables_var_count(void); -void RulesVariables_var_name(void); -void RulesVariables_var_is_entity(void); -void RulesVariables_no_this_anonymous_src(void); -void RulesVariables_no_this_anonymous_src_w_pair(void); -void RulesVariables_no_this_anonymous_component_src(void); -void RulesVariables_no_this_anonymous_component_src_w_pair(void); -void RulesVariables_lookup_from_table_this(void); -void RulesVariables_lookup_from_entity_this(void); -void RulesVariables_lookup_from_table(void); -void RulesVariables_lookup_from_entity(void); -void RulesVariables_lookup_from_not_written(void); -void RulesVariables_lookup_from_table_this_component(void); -void RulesVariables_lookup_from_entity_this_component(void); -void RulesVariables_lookup_from_table_component(void); -void RulesVariables_lookup_from_entity_component(void); -void RulesVariables_lookup_from_table_two_children(void); -void RulesVariables_lookup_from_entity_two_children(void); -void RulesVariables_lookup_from_table_same_child_twice(void); -void RulesVariables_lookup_from_entity_same_child_twice(void); -void RulesVariables_lookup_from_table_not(void); -void RulesVariables_lookup_from_entity_not(void); -void RulesVariables_lookup_from_table_w_any_component(void); -void RulesVariables_lookup_from_entity_w_any_component(void); -void RulesVariables_lookup_as_tag(void); -void RulesVariables_lookup_as_relationship(void); -void RulesVariables_lookup_as_target(void); -void RulesVariables_lookup_assign_var(void); -void RulesVariables_lookup_eq_var(void); -void RulesVariables_lookup_neq_var(void); -void RulesVariables_check_vars_this(void); -void RulesVariables_check_vars_var(void); -void RulesVariables_check_vars_wildcard(void); -void RulesVariables_check_vars_any(void); -void RulesVariables_check_vars_var_as_tgt(void); -void RulesVariables_check_vars_this_as_tgt(void); -void RulesVariables_check_vars_anonymous_var_as_tgt(void); -void RulesVariables_check_vars_wildcard_as_tgt(void); -void RulesVariables_check_vars_any_as_tgt(void); -void RulesVariables_check_vars_this_w_lookup_var(void); -void RulesVariables_check_vars_var_w_lookup_var(void); -void RulesVariables_1_trivial_1_var(void); -void RulesVariables_2_trivial_1_var(void); -void RulesVariables_1_trivial_1_var_component(void); -void RulesVariables_2_trivial_1_var_component(void); -void RulesVariables_1_trivial_1_wildcard(void); -void RulesVariables_2_trivial_1_wildcard(void); -void RulesVariables_1_trivial_1_wildcard_component(void); -void RulesVariables_2_trivial_1_wildcard_component(void); -void RulesVariables_1_trivial_1_any(void); -void RulesVariables_2_trivial_1_any(void); -void RulesVariables_1_trivial_1_any_component(void); -void RulesVariables_2_trivial_1_any_component(void); - -// Testsuite 'RulesOperators' -void RulesOperators_2_and_not(void); -void RulesOperators_3_and_not_not(void); -void RulesOperators_2_and_not_pair_rel_wildcard(void); -void RulesOperators_2_and_not_pair_tgt_wildcard(void); -void RulesOperators_2_and_not_pair_rel_tgt_wildcard(void); -void RulesOperators_2_and_not_pair_rel_var(void); -void RulesOperators_2_and_not_pair_tgt_var(void); -void RulesOperators_2_and_not_pair_rel_tgt_var(void); -void RulesOperators_2_and_not_pair_rel_tgt_same_var(void); -void RulesOperators_2_and_not_pair_rel_var_written(void); -void RulesOperators_2_and_not_pair_tgt_var_written(void); -void RulesOperators_2_and_not_pair_rel_tgt_var_written(void); -void RulesOperators_2_and_not_pair_rel_tgt_same_var_written(void); -void RulesOperators_2_and_not_pair_rel_src_tgt_same_var_written(void); -void RulesOperators_2_and_not_pair_any_rel(void); -void RulesOperators_2_and_not_pair_any_tgt(void); -void RulesOperators_2_and_not_pair_any_src(void); -void RulesOperators_2_and_optional(void); -void RulesOperators_3_and_optional_optional(void); -void RulesOperators_2_and_optional_pair_rel_wildcard(void); -void RulesOperators_2_and_optional_pair_tgt_wildcard(void); -void RulesOperators_2_and_optional_pair_rel_var(void); -void RulesOperators_2_and_optional_pair_tgt_var(void); -void RulesOperators_2_and_optional_pair_rel_tgt_var(void); -void RulesOperators_2_and_optional_pair_rel_tgt_same_var(void); -void RulesOperators_2_and_optional_pair_rel_var_written(void); -void RulesOperators_2_and_optional_pair_tgt_var_written(void); -void RulesOperators_2_and_optional_pair_rel_tgt_var_written(void); -void RulesOperators_2_and_optional_pair_rel_tgt_same_var_written(void); -void RulesOperators_2_and_optional_pair_rel_src_tgt_same_var_written(void); -void RulesOperators_3_and_optional_optional_pair_w_var(void); -void RulesOperators_2_and_optional_pair_any_rel(void); -void RulesOperators_2_and_optional_pair_any_tgt(void); -void RulesOperators_2_and_optional_pair_any_src(void); -void RulesOperators_3_and_optional_dependent_and_pair_rel(void); -void RulesOperators_3_and_optional_dependent_and_pair_tgt(void); -void RulesOperators_3_and_optional_dependent_and_pair_rel_tgt(void); -void RulesOperators_3_and_optional_dependent_and_pair_rel_tgt_same_var(void); -void RulesOperators_3_and_optional_dependent_and_pair_rel_tgt_same_other_var(void); -void RulesOperators_3_and_optional_dependent_and_pair_src(void); -void RulesOperators_3_and_optional_dependent_optional_pair_rel(void); -void RulesOperators_3_and_optional_dependent_optional_pair_tgt(void); -void RulesOperators_3_and_optional_dependent_optional_pair_src(void); -void RulesOperators_3_and_optional_dependent_not_pair_rel(void); -void RulesOperators_3_and_optional_dependent_not_pair_tgt(void); -void RulesOperators_3_and_optional_dependent_not_pair_src(void); -void RulesOperators_2_or(void); -void RulesOperators_3_or(void); -void RulesOperators_2_or_written(void); -void RulesOperators_3_or_written(void); -void RulesOperators_2_or_written_w_rel_var(void); -void RulesOperators_3_or_written_w_rel_var(void); -void RulesOperators_2_or_written_w_tgt_var(void); -void RulesOperators_2_or_written_w_rel_tgt_var(void); -void RulesOperators_2_or_written_w_rel_tgt_same_var(void); -void RulesOperators_3_or_written_w_tgt_var(void); -void RulesOperators_2_or_chains(void); -void RulesOperators_2_or_chains_written(void); -void RulesOperators_2_or_dependent(void); -void RulesOperators_2_or_dependent_reverse(void); -void RulesOperators_2_or_dependent_2_vars(void); -void RulesOperators_2_or_written_dependent(void); -void RulesOperators_2_or_written_dependent_2_vars(void); -void RulesOperators_2_or_w_dependent(void); -void RulesOperators_2_or_w_both(void); -void RulesOperators_3_or_w_both(void); -void RulesOperators_2_not_first(void); -void RulesOperators_2_optional_first(void); -void RulesOperators_root_entities_empty(void); -void RulesOperators_root_entities(void); -void RulesOperators_root_entities_w_children(void); -void RulesOperators_root_entities_w_optional_children(void); -void RulesOperators_core_entities_w_optional_children(void); -void RulesOperators_root_entities_w_not_children(void); -void RulesOperators_core_entities_w_not_children(void); -void RulesOperators_1_ent_src_not(void); -void RulesOperators_1_ent_src_not_pair(void); -void RulesOperators_1_ent_src_not_pair_rel_wildcard(void); -void RulesOperators_1_ent_src_not_pair_tgt_wildcard(void); -void RulesOperators_1_ent_src_not_pair_rel_tgt_wildcard(void); -void RulesOperators_1_ent_src_not_pair_rel_any(void); -void RulesOperators_1_ent_src_not_pair_tgt_any(void); -void RulesOperators_1_ent_src_not_pair_rel_tgt_any(void); -void RulesOperators_1_ent_src_not_pair_rel_var(void); -void RulesOperators_1_ent_src_not_pair_tgt_var(void); -void RulesOperators_1_ent_src_not_pair_rel_tgt_var(void); -void RulesOperators_1_ent_src_not_pair_rel_tgt_same_var(void); -void RulesOperators_1_this_src_not_pair_rel_var(void); -void RulesOperators_1_this_src_not_pair_tgt_var(void); -void RulesOperators_1_this_src_not_pair_rel_tgt_var(void); -void RulesOperators_1_this_src_not_pair_rel_tgt_same_var(void); -void RulesOperators_1_ent_src_not_pair_rel_var_written(void); -void RulesOperators_1_ent_src_not_pair_tgt_var_written(void); -void RulesOperators_1_ent_src_not_pair_rel_tgt_var_written(void); -void RulesOperators_1_ent_src_not_pair_rel_tgt_same_var_written(void); - -// Testsuite 'RulesTransitive' -void RulesTransitive_1_fact_0_lvl_true(void); -void RulesTransitive_1_fact_1_lvl_true(void); -void RulesTransitive_1_fact_2_lvl_true(void); -void RulesTransitive_1_fact_0_lvl_false(void); -void RulesTransitive_1_fact_1_lvl_false(void); -void RulesTransitive_1_fact_2_lvl_false(void); -void RulesTransitive_1_fact_reflexive(void); -void RulesTransitive_1_this_src_written_0_lvl(void); -void RulesTransitive_1_this_src_written_1_lvl(void); -void RulesTransitive_1_this_src_written_2_lvl(void); -void RulesTransitive_1_this_src_written_reflexive(void); -void RulesTransitive_1_this_src_0_lvl(void); -void RulesTransitive_1_this_src_1_lvl(void); -void RulesTransitive_1_this_src_2_lvl(void); -void RulesTransitive_1_this_src_reflexive(void); -void RulesTransitive_1_ent_src_tgt_var_0_lvl(void); -void RulesTransitive_1_ent_src_tgt_var_1_lvl(void); -void RulesTransitive_1_ent_src_tgt_var_2_lvl(void); -void RulesTransitive_1_ent_src_tgt_var_reflexive(void); -void RulesTransitive_1_this_src_tgt_var(void); -void RulesTransitive_1_this_src_tgt_var_reflexive(void); -void RulesTransitive_1_var_src_written_0_lvl(void); -void RulesTransitive_1_var_src_written_1_lvl(void); -void RulesTransitive_1_var_src_written_2_lvl(void); -void RulesTransitive_1_var_src_written_reflexive(void); -void RulesTransitive_1_var_src_0_lvl(void); -void RulesTransitive_1_var_src_1_lvl(void); -void RulesTransitive_1_var_src_2_lvl(void); -void RulesTransitive_1_var_src_reflexive(void); -void RulesTransitive_1_var_src_tgt_var(void); -void RulesTransitive_1_var_src_tgt_var_reflexive(void); -void RulesTransitive_1_ent_src_tgt_this_0_lvl(void); -void RulesTransitive_1_ent_src_tgt_this_1_lvl(void); -void RulesTransitive_1_ent_src_tgt_this_2_lvl(void); -void RulesTransitive_1_ent_src_tgt_this_reflexive(void); -void RulesTransitive_1_var_src_tgt_this(void); -void RulesTransitive_1_var_src_tgt_this_reflexive(void); -void RulesTransitive_2_ent_src_constrain_tgt_var_before_0_lvl(void); -void RulesTransitive_2_ent_src_constrain_tgt_var_before_1_lvl(void); -void RulesTransitive_2_ent_src_constrain_tgt_var_before_2_lvl(void); -void RulesTransitive_2_ent_src_constrain_tgt_var_after_0_lvl(void); -void RulesTransitive_2_ent_src_constrain_tgt_var_after_1_lvl(void); -void RulesTransitive_2_ent_src_constrain_tgt_var_after_2_lvl(void); -void RulesTransitive_2_this_src_constrain_tgt_var_before_0_lvl(void); -void RulesTransitive_2_this_src_constrain_tgt_var_before_1_lvl(void); -void RulesTransitive_2_this_src_constrain_tgt_var_before_2_lvl(void); -void RulesTransitive_2_this_src_constrain_tgt_var_after_0_lvl(void); -void RulesTransitive_2_this_src_constrain_tgt_var_after_1_lvl(void); -void RulesTransitive_2_this_src_constrain_tgt_var_after_2_lvl(void); -void RulesTransitive_1_src_tgt_same_var(void); -void RulesTransitive_1_src_tgt_same_var_reflexive(void); -void RulesTransitive_1_src_tgt_same_this_var_reflexive(void); -void RulesTransitive_1_any_src_tgt_var(void); -void RulesTransitive_not_transitive_ent_tgt(void); -void RulesTransitive_not_transitive_var_tgt(void); -void RulesTransitive_not_transitive_ent_tgt_written(void); -void RulesTransitive_not_transitive_var_tgt_written(void); -void RulesTransitive_optional_transitive_ent_tgt(void); -void RulesTransitive_optional_transitive_var_tgt(void); -void RulesTransitive_optional_transitive_ent_tgt_written(void); -void RulesTransitive_optional_transitive_var_tgt_written(void); -void RulesTransitive_2_var_src_w_same_tgt_ent(void); -void RulesTransitive_self_target(void); -void RulesTransitive_any_target(void); - -// Testsuite 'RulesComponentInheritance' -void RulesComponentInheritance_1_ent_0_lvl(void); -void RulesComponentInheritance_1_ent_1_lvl(void); -void RulesComponentInheritance_1_ent_2_lvl(void); -void RulesComponentInheritance_1_ent_3_lvl(void); -void RulesComponentInheritance_1_this_0_lvl(void); -void RulesComponentInheritance_1_this_1_lvl(void); -void RulesComponentInheritance_1_this_2_lvl(void); -void RulesComponentInheritance_1_this_3_lvl(void); -void RulesComponentInheritance_1_this_0_lvl_written(void); -void RulesComponentInheritance_1_this_1_lvl_written(void); -void RulesComponentInheritance_1_this_2_lvl_written(void); -void RulesComponentInheritance_1_this_3_lvl_written(void); -void RulesComponentInheritance_1_var_0_lvl(void); -void RulesComponentInheritance_1_var_1_lvl(void); -void RulesComponentInheritance_1_var_2_lvl(void); -void RulesComponentInheritance_1_var_3_lvl(void); -void RulesComponentInheritance_1_var_0_lvl_written(void); -void RulesComponentInheritance_1_var_1_lvl_written(void); -void RulesComponentInheritance_1_var_2_lvl_written(void); -void RulesComponentInheritance_1_var_3_lvl_written(void); -void RulesComponentInheritance_1_ent_1_lvl_self(void); -void RulesComponentInheritance_1_this_1_lvl_self(void); -void RulesComponentInheritance_1_this_1_lvl_written_self(void); -void RulesComponentInheritance_1_var_1_lvl_self(void); -void RulesComponentInheritance_1_var_1_lvl_written_self(void); -void RulesComponentInheritance_1_ent_src_not(void); -void RulesComponentInheritance_1_this_src_not(void); -void RulesComponentInheritance_1_var_src_not(void); -void RulesComponentInheritance_1_this_src_not_written(void); -void RulesComponentInheritance_1_var_src_not_written(void); -void RulesComponentInheritance_first_self(void); -void RulesComponentInheritance_first_down(void); -void RulesComponentInheritance_first_self_down(void); -void RulesComponentInheritance_first_rel_self(void); -void RulesComponentInheritance_first_rel_down(void); -void RulesComponentInheritance_first_rel_self_down(void); - -// Testsuite 'RulesRecycled' -void RulesRecycled_recycled_vars(void); -void RulesRecycled_recycled_pair_vars(void); -void RulesRecycled_recycled_this_ent_var(void); -void RulesRecycled_has_recycled_id_from_pair(void); - -// Testsuite 'RulesBuiltinPredicates' -void RulesBuiltinPredicates_this_eq_id(void); -void RulesBuiltinPredicates_this_eq_name(void); -void RulesBuiltinPredicates_this_eq_var(void); -void RulesBuiltinPredicates_this_eq_id_written(void); -void RulesBuiltinPredicates_this_eq_id_written_no_match(void); -void RulesBuiltinPredicates_this_eq_name_written(void); -void RulesBuiltinPredicates_this_eq_name_written_no_match(void); -void RulesBuiltinPredicates_this_eq_var_written(void); -void RulesBuiltinPredicates_var_eq_id(void); -void RulesBuiltinPredicates_var_eq_name(void); -void RulesBuiltinPredicates_var_eq_var(void); -void RulesBuiltinPredicates_var_eq_id_written(void); -void RulesBuiltinPredicates_var_eq_id_written_no_match(void); -void RulesBuiltinPredicates_var_eq_name_written(void); -void RulesBuiltinPredicates_var_eq_name_written_no_match(void); -void RulesBuiltinPredicates_var_eq_var_written(void); -void RulesBuiltinPredicates_this_neq_id(void); -void RulesBuiltinPredicates_this_neq_name(void); -void RulesBuiltinPredicates_this_neq_var(void); -void RulesBuiltinPredicates_this_neq_id_written(void); -void RulesBuiltinPredicates_this_neq_id_written_no_match(void); -void RulesBuiltinPredicates_this_neq_name_written(void); -void RulesBuiltinPredicates_this_neq_name_written_no_match(void); -void RulesBuiltinPredicates_this_neq_var_written(void); -void RulesBuiltinPredicates_var_neq_id(void); -void RulesBuiltinPredicates_var_neq_name(void); -void RulesBuiltinPredicates_var_neq_var(void); -void RulesBuiltinPredicates_var_neq_id_written(void); -void RulesBuiltinPredicates_var_neq_id_written_no_match(void); -void RulesBuiltinPredicates_var_neq_name_written(void); -void RulesBuiltinPredicates_var_neq_name_written_no_match(void); -void RulesBuiltinPredicates_var_neq_var_written(void); -void RulesBuiltinPredicates_this_2_neq_id(void); -void RulesBuiltinPredicates_this_2_neq_name(void); -void RulesBuiltinPredicates_var_2_neq_id(void); -void RulesBuiltinPredicates_var_2_neq_name(void); -void RulesBuiltinPredicates_this_2_neq_id_written(void); -void RulesBuiltinPredicates_this_2_neq_name_written(void); -void RulesBuiltinPredicates_var_2_neq_id_written(void); -void RulesBuiltinPredicates_var_2_neq_name_written(void); -void RulesBuiltinPredicates_this_2_or_id(void); -void RulesBuiltinPredicates_this_2_or_name(void); -void RulesBuiltinPredicates_var_2_or_id(void); -void RulesBuiltinPredicates_var_2_or_name(void); -void RulesBuiltinPredicates_this_2_or_id_written(void); -void RulesBuiltinPredicates_this_2_or_name_written(void); -void RulesBuiltinPredicates_var_2_or_id_written(void); -void RulesBuiltinPredicates_var_2_or_name_written(void); -void RulesBuiltinPredicates_this_match_eq(void); -void RulesBuiltinPredicates_var_match_eq(void); -void RulesBuiltinPredicates_this_match_eq_written(void); -void RulesBuiltinPredicates_var_match_eq_written(void); -void RulesBuiltinPredicates_this_match_neq(void); -void RulesBuiltinPredicates_var_match_neq(void); -void RulesBuiltinPredicates_this_match_neq_written(void); -void RulesBuiltinPredicates_var_match_neq_written(void); -void RulesBuiltinPredicates_this_match_2_neq(void); -void RulesBuiltinPredicates_var_match_2_neq(void); -void RulesBuiltinPredicates_this_match_2_neq_written(void); -void RulesBuiltinPredicates_var_match_2_neq_written(void); -void RulesBuiltinPredicates_this_match_2_or(void); -void RulesBuiltinPredicates_this_match_2_or_written(void); -void RulesBuiltinPredicates_this_match_3_or(void); -void RulesBuiltinPredicates_this_match_3_or_written(void); -void RulesBuiltinPredicates_unresolved_by_name(void); -void RulesBuiltinPredicates_var_eq_wildcard(void); -void RulesBuiltinPredicates_var_eq_any(void); -void RulesBuiltinPredicates_var_eq_wildcard_after_write(void); -void RulesBuiltinPredicates_var_eq_any_after_write(void); -void RulesBuiltinPredicates_var_eq_after_var_0_src(void); - -// Testsuite 'RulesScopes' -void RulesScopes_term_w_not_scope_1_term(void); -void RulesScopes_term_w_not_scope_2_terms(void); -void RulesScopes_term_w_not_scope_1_term_w_not(void); -void RulesScopes_term_w_not_scope_2_terms_w_not(void); -void RulesScopes_term_w_not_scope_1_term_w_var(void); -void RulesScopes_term_w_not_scope_2_terms_w_var(void); -void RulesScopes_term_w_not_scope_1_term_w_not_w_var(void); -void RulesScopes_term_w_not_scope_2_terms_w_not_w_var(void); -void RulesScopes_term_w_not_scope_2_terms_w_or(void); -void RulesScopes_term_w_not_scope_3_terms_w_or(void); - -// Testsuite 'RulesTraversal' -void RulesTraversal_this_self_up_childof(void); -void RulesTraversal_this_up_childof(void); -void RulesTraversal_this_written_self_up_childof(void); -void RulesTraversal_this_written_up_childof(void); -void RulesTraversal_var_self_up_childof(void); -void RulesTraversal_var_up_childof(void); -void RulesTraversal_var_written_self_up_childof(void); -void RulesTraversal_var_written_up_childof(void); -void RulesTraversal_set_var_self_up_childof(void); -void RulesTraversal_set_var_up_childof(void); -void RulesTraversal_set_var_written_self_up_childof(void); -void RulesTraversal_set_var_written_up_childof(void); -void RulesTraversal_ent_self_up_childof(void); -void RulesTraversal_ent_up_childof(void); -void RulesTraversal_implicit_this_self_up_isa(void); -void RulesTraversal_implicit_this_up_isa(void); -void RulesTraversal_implicit_var_self_up_isa(void); -void RulesTraversal_implicit_var_up_isa(void); -void RulesTraversal_implicit_ent_self_up_isa(void); -void RulesTraversal_implicit_ent_up_isa(void); -void RulesTraversal_self_up_2_targets(void); -void RulesTraversal_up_2_targets(void); -void RulesTraversal_self_up_2_targets_diamond(void); -void RulesTraversal_up_2_targets_diamond(void); -void RulesTraversal_written_self_up_2_targets(void); -void RulesTraversal_written_up_2_targets(void); -void RulesTraversal_written_self_up_2_targets_diamond(void); -void RulesTraversal_written_up_2_targets_diamond(void); -void RulesTraversal_2_self_up_terms(void); -void RulesTraversal_2_self_up_terms_2_targets(void); -void RulesTraversal_self_up_empty_table(void); -void RulesTraversal_up_empty_table(void); -void RulesTraversal_self_up_all_owned(void); -void RulesTraversal_up_all_owned(void); -void RulesTraversal_this_self_up_childof_inherited(void); -void RulesTraversal_this_up_childof_inherited(void); -void RulesTraversal_this_written_self_up_childof_inherited(void); -void RulesTraversal_this_written_up_childof_inherited(void); -void RulesTraversal_var_self_up_childof_inherited(void); -void RulesTraversal_var_up_childof_inherited(void); -void RulesTraversal_var_written_self_up_childof_inherited(void); -void RulesTraversal_var_written_up_childof_inherited(void); -void RulesTraversal_ent_self_up_childof_inherited(void); -void RulesTraversal_ent_up_childof_inherited(void); -void RulesTraversal_ent_written_self_up_childof_inherited(void); -void RulesTraversal_ent_written_up_childof_inherited(void); -void RulesTraversal_this_self_up_childof_component(void); -void RulesTraversal_this_up_childof_component(void); -void RulesTraversal_this_written_self_up_childof_component(void); -void RulesTraversal_this_written_up_childof_component(void); -void RulesTraversal_var_self_up_childof_component(void); -void RulesTraversal_var_up_childof_component(void); -void RulesTraversal_var_written_self_up_childof_component(void); -void RulesTraversal_var_written_up_childof_component(void); -void RulesTraversal_this_self_up_childof_recycled_parent(void); -void RulesTraversal_this_up_childof_recycled_parent(void); -void RulesTraversal_this_written_self_up_childof_recycled_parent(void); -void RulesTraversal_this_written_up_childof_recycled_parent(void); -void RulesTraversal_this_self_up_childof_recycled_parent_component(void); -void RulesTraversal_this_up_childof_recycled_parent_component(void); -void RulesTraversal_this_written_self_up_childof_recycled_parent_component(void); -void RulesTraversal_this_written_up_childof_recycled_parent_component(void); -void RulesTraversal_this_self_up_childof_pair(void); -void RulesTraversal_this_up_childof_pair(void); -void RulesTraversal_this_written_self_up_childof_pair(void); -void RulesTraversal_this_written_up_childof_pair(void); -void RulesTraversal_this_self_up_childof_pair_wildcard(void); -void RulesTraversal_this_up_childof_pair_wildcard(void); -void RulesTraversal_this_written_self_up_childof_pair_wildcard(void); -void RulesTraversal_this_written_up_childof_pair_wildcard(void); -void RulesTraversal_this_self_up_childof_pair_tgt_var(void); -void RulesTraversal_this_written_self_up_childof_pair_tgt_var(void); -void RulesTraversal_this_self_up_childof_pair_rel_var(void); -void RulesTraversal_this_written_self_up_childof_pair_rel_var(void); -void RulesTraversal_this_self_up_childof_pair_for_var_written(void); -void RulesTraversal_this_up_childof_pair_for_var_written(void); -void RulesTraversal_this_written_self_up_childof_pair_for_var_written(void); -void RulesTraversal_this_self_up_childof_pair_for_var_written_n_targets(void); -void RulesTraversal_this_written_self_up_childof_pair_for_var_written_n_targets(void); -void RulesTraversal_self_up_2_levels(void); -void RulesTraversal_not_up_disabled(void); -void RulesTraversal_up_2_rel_instances(void); -void RulesTraversal_up_2_rel_instances_match_2nd(void); -void RulesTraversal_up_only_w_owned(void); -void RulesTraversal_this_self_cascade_childof(void); -void RulesTraversal_this_cascade_childof(void); -void RulesTraversal_this_written_self_cascade_childof(void); -void RulesTraversal_this_written_cascade_childof(void); -void RulesTraversal_this_self_cascade_childof_w_parent_flag(void); -void RulesTraversal_this_cascade_childof_w_parent_flag(void); -void RulesTraversal_this_written_self_cascade_childof_w_parent_flag(void); -void RulesTraversal_this_written_cascade_childof_w_parent_flag(void); +void SystemMisc_system_no_id_in_scope(void); +void SystemMisc_register_callback_after_run(void); +void SystemMisc_register_run_after_callback(void); +void SystemMisc_register_callback_after_run_ctx(void); +void SystemMisc_register_run_after_callback_ctx(void); // Testsuite 'SystemPeriodic' void SystemPeriodic_1_type_1_component(void); @@ -1429,7 +264,6 @@ void SystemCascade_custom_relation_adopt_after_match(void); // Testsuite 'SystemManual' void SystemManual_setup(void); void SystemManual_1_type_1_component(void); -void SystemManual_no_automerge(void); void SystemManual_dont_run_w_unmatching_entity_query(void); // Testsuite 'Tasks' @@ -1445,7 +279,7 @@ void System_w_FromParent_2_column_1_from_container(void); void System_w_FromParent_3_column_2_from_container(void); void System_w_FromParent_2_column_1_from_container_w_not(void); void System_w_FromParent_2_column_1_from_container_w_not_prefab(void); -void System_w_FromParent_3_column_1_from_comtainer_1_from_container_w_not(void); +void System_w_FromParent_3_column_1_from_container_1_from_container_w_not(void); void System_w_FromParent_2_column_1_from_container_w_or(void); void System_w_FromParent_select_same_from_container(void); void System_w_FromParent_add_component_after_match(void); @@ -1487,28 +321,14 @@ void Stats_get_pipeline_stats_after_progress_2_systems_one_merge(void); void Stats_get_entity_count(void); void Stats_get_pipeline_stats_w_task_system(void); void Stats_get_not_alive_entity_count(void); +void Stats_progress_stats_systems(void); +void Stats_progress_stats_systems_w_empty_table_flag(void); // Testsuite 'Run' void Run_setup(void); void Run_run(void); void Run_run_w_param(void); void Run_run_no_match(void); -void Run_run_w_offset(void); -void Run_run_w_offset_skip_1_archetype(void); -void Run_run_w_offset_skip_1_archetype_plus_one(void); -void Run_run_w_offset_skip_2_archetypes(void); -void Run_run_w_limit_skip_1_archetype(void); -void Run_run_w_limit_skip_1_archetype_minus_one(void); -void Run_run_w_limit_skip_2_archetypes(void); -void Run_run_w_offset_1_limit_max(void); -void Run_run_w_offset_1_limit_minus_1(void); -void Run_run_w_offset_2_type_limit_max(void); -void Run_run_w_offset_2_type_limit_minus_1(void); -void Run_run_w_limit_1_all_offsets(void); -void Run_run_w_offset_out_of_bounds(void); -void Run_run_w_limit_out_of_bounds(void); -void Run_run_comb_10_entities_1_type(void); -void Run_run_comb_10_entities_2_types(void); void Run_run_w_interrupt(void); void Run_run_staging(void); @@ -1575,111 +395,11 @@ void MultiThreadStaging_6_threads_add_to_current(void); void MultiThreadStaging_2_threads_on_add(void); void MultiThreadStaging_new_w_count(void); void MultiThreadStaging_custom_thread_auto_merge(void); -void MultiThreadStaging_custom_thread_manual_merge(void); -void MultiThreadStaging_custom_thread_partial_manual_merge(void); void MultiThreadStaging_set_pair_w_new_target_readonly(void); void MultiThreadStaging_set_pair_w_new_target_tgt_component_readonly(void); void MultiThreadStaging_set_pair_w_new_target_defer(void); void MultiThreadStaging_set_pair_w_new_target_tgt_component_defer(void); -// Testsuite 'MultiTaskThread' -void MultiTaskThread_setup(void); -void MultiTaskThread_2_thread_1_entity(void); -void MultiTaskThread_2_thread_2_entity(void); -void MultiTaskThread_2_thread_5_entity(void); -void MultiTaskThread_2_thread_10_entity(void); -void MultiTaskThread_3_thread_1_entity(void); -void MultiTaskThread_3_thread_2_entity(void); -void MultiTaskThread_3_thread_5_entity(void); -void MultiTaskThread_3_thread_10_entity(void); -void MultiTaskThread_4_thread_1_entity(void); -void MultiTaskThread_4_thread_2_entity(void); -void MultiTaskThread_4_thread_5_entity(void); -void MultiTaskThread_4_thread_10_entity(void); -void MultiTaskThread_5_thread_1_entity(void); -void MultiTaskThread_5_thread_2_entity(void); -void MultiTaskThread_5_thread_5_entity(void); -void MultiTaskThread_5_thread_10_entity(void); -void MultiTaskThread_6_thread_1_entity(void); -void MultiTaskThread_6_thread_2_entity(void); -void MultiTaskThread_6_thread_5_entity(void); -void MultiTaskThread_6_thread_10_entity(void); -void MultiTaskThread_2_thread_1_entity_instanced(void); -void MultiTaskThread_2_thread_5_entity_instanced(void); -void MultiTaskThread_2_thread_10_entity_instanced(void); -void MultiTaskThread_2_thread_test_combs_100_entity_w_next_worker(void); -void MultiTaskThread_2_thread_test_combs_100_entity(void); -void MultiTaskThread_3_thread_test_combs_100_entity(void); -void MultiTaskThread_4_thread_test_combs_100_entity(void); -void MultiTaskThread_5_thread_test_combs_100_entity(void); -void MultiTaskThread_6_thread_test_combs_100_entity(void); -void MultiTaskThread_2_thread_test_combs_100_entity_2_types(void); -void MultiTaskThread_3_thread_test_combs_100_entity_2_types(void); -void MultiTaskThread_4_thread_test_combs_100_entity_2_types(void); -void MultiTaskThread_5_thread_test_combs_100_entity_2_types(void); -void MultiTaskThread_6_thread_test_combs_100_entity_2_types(void); -void MultiTaskThread_change_thread_count(void); -void MultiTaskThread_multithread_quit(void); -void MultiTaskThread_schedule_w_tasks(void); -void MultiTaskThread_reactive_system(void); -void MultiTaskThread_fini_after_set_threads(void); -void MultiTaskThread_2_threads_single_threaded_system(void); -void MultiTaskThread_no_staging_w_multithread(void); -void MultiTaskThread_multithread_w_monitor_addon(void); -void MultiTaskThread_get_ctx(void); -void MultiTaskThread_get_binding_ctx(void); -void MultiTaskThread_get_ctx_w_run(void); -void MultiTaskThread_get_binding_ctx_w_run(void); -void MultiTaskThread_bulk_new_in_no_readonly_w_multithread(void); -void MultiTaskThread_bulk_new_in_no_readonly_w_multithread_2(void); -void MultiTaskThread_run_first_worker_on_main(void); -void MultiTaskThread_run_single_thread_on_main(void); - -// Testsuite 'MultiTaskThreadStaging' -void MultiTaskThreadStaging_setup(void); -void MultiTaskThreadStaging_2_threads_add_to_current(void); -void MultiTaskThreadStaging_3_threads_add_to_current(void); -void MultiTaskThreadStaging_4_threads_add_to_current(void); -void MultiTaskThreadStaging_5_threads_add_to_current(void); -void MultiTaskThreadStaging_6_threads_add_to_current(void); -void MultiTaskThreadStaging_2_threads_on_add(void); -void MultiTaskThreadStaging_new_w_count(void); -void MultiTaskThreadStaging_custom_thread_auto_merge(void); -void MultiTaskThreadStaging_custom_thread_manual_merge(void); -void MultiTaskThreadStaging_custom_thread_partial_manual_merge(void); -void MultiTaskThreadStaging_set_pair_w_new_target_readonly(void); -void MultiTaskThreadStaging_set_pair_w_new_target_tgt_component_readonly(void); -void MultiTaskThreadStaging_set_pair_w_new_target_defer(void); -void MultiTaskThreadStaging_set_pair_w_new_target_tgt_component_defer(void); - -// Testsuite 'Snapshot' -void Snapshot_simple_snapshot(void); -void Snapshot_snapshot_after_new(void); -void Snapshot_snapshot_after_delete(void); -void Snapshot_snapshot_after_new_type(void); -void Snapshot_snapshot_after_add(void); -void Snapshot_snapshot_after_remove(void); -void Snapshot_snapshot_w_include_filter(void); -void Snapshot_snapshot_w_exclude_filter(void); -void Snapshot_snapshot_w_filter_after_new(void); -void Snapshot_snapshot_w_filter_after_delete(void); -void Snapshot_snapshot_free_empty(void); -void Snapshot_snapshot_free(void); -void Snapshot_snapshot_free_filtered(void); -void Snapshot_snapshot_free_filtered_w_dtor(void); -void Snapshot_snapshot_activate_table_w_filter(void); -void Snapshot_snapshot_copy(void); -void Snapshot_snapshot_get_ref_after_restore(void); -void Snapshot_new_after_snapshot(void); -void Snapshot_new_empty_after_snapshot(void); -void Snapshot_add_after_snapshot(void); -void Snapshot_delete_after_snapshot(void); -void Snapshot_set_after_snapshot(void); -void Snapshot_restore_recycled(void); -void Snapshot_snapshot_w_new_in_onset(void); -void Snapshot_snapshot_w_new_in_onset_in_snapshot_table(void); -void Snapshot_snapshot_from_stage(void); - // Testsuite 'Modules' void Modules_setup(void); void Modules_simple_module(void); @@ -1705,6 +425,7 @@ void Modules_module_tag_on_namespace_on_add_2_levels(void); void Modules_import_monitor_2_worlds(void); void Modules_import_monitor_after_mini(void); void Modules_import_2_worlds(void); +void Modules_component_parent_becomes_module(void); // Testsuite 'App' void App_app_w_frame_action(void); @@ -1723,6 +444,22 @@ void Http_stop_start(void); // Testsuite 'Rest' void Rest_teardown(void); void Rest_get(void); +void Rest_get_cached(void); +void Rest_get_cached_invalid(void); +void Rest_try_query(void); +void Rest_query(void); +void Rest_named_query(void); +void Rest_tables(void); +void Rest_request_commands(void); +void Rest_request_commands_2_syncs(void); +void Rest_request_commands_no_frames(void); +void Rest_request_commands_no_commands(void); +void Rest_request_commands_garbage_collect(void); +void Rest_script_error(void); +void Rest_import_rest_after_mini(void); +void Rest_get_pipeline_stats_after_delete_system(void); +void Rest_request_world_summary_before_monitor_sys_run(void); +void Rest_escape_backslash(void); // Testsuite 'Metrics' void Metrics_member_gauge_1_entity(void); @@ -1801,6513 +538,1471 @@ void Alerts_member_range_from_var_after_remove(void); void Alerts_retained_alert_w_dead_source(void); void Alerts_alert_counts(void); -bake_test_case Parser_testcases[] = { +bake_test_case Doc_testcases[] = { { - "resolve_this", - Parser_resolve_this + "get_set_name", + Doc_get_set_name }, { - "resolve_wildcard", - Parser_resolve_wildcard + "get_entity_name", + Doc_get_entity_name }, { - "resolve_any", - Parser_resolve_any + "get_set_brief", + Doc_get_set_brief }, { - "resolve_is_a", - Parser_resolve_is_a + "get_set_detail", + Doc_get_set_detail }, { - "0", - Parser_0 + "get_set_link", + Doc_get_set_link }, { - "component_implicit_subject", - Parser_component_implicit_subject + "get_set_uuid", + Doc_get_set_uuid }, { - "component_explicit_subject", - Parser_component_explicit_subject + "set_name_nullptr", + Doc_set_name_nullptr }, { - "component_explicit_subject_this", - Parser_component_explicit_subject_this + "set_brief_nullptr", + Doc_set_brief_nullptr }, { - "component_explicit_subject_this_by_name", - Parser_component_explicit_subject_this_by_name + "set_detail_nullptr", + Doc_set_detail_nullptr }, { - "component_explicit_subject_this_by_var_name", - Parser_component_explicit_subject_this_by_var_name + "set_link_nullptr", + Doc_set_link_nullptr }, { - "component_explicit_subject_wildcard", - Parser_component_explicit_subject_wildcard + "set_color_nullptr", + Doc_set_color_nullptr }, { - "component_explicit_subject_any", - Parser_component_explicit_subject_any - }, + "set_uuid_nullptr", + Doc_set_uuid_nullptr + } +}; + +bake_test_case Pipeline_testcases[] = { { - "component_explicit_subject_0", - Parser_component_explicit_subject_0 + "system_order_same_phase", + Pipeline_system_order_same_phase }, { - "this_as_predicate", - Parser_this_as_predicate + "system_order_same_phase_after_disable", + Pipeline_system_order_same_phase_after_disable }, { - "this_var_as_predicate", - Parser_this_var_as_predicate + "system_order_same_phase_after_activate", + Pipeline_system_order_same_phase_after_activate }, { - "this_lowercase_var_as_predicate", - Parser_this_lowercase_var_as_predicate + "system_order_different_phase", + Pipeline_system_order_different_phase }, { - "this_as_object", - Parser_this_as_object + "system_order_different_phase_after_disable", + Pipeline_system_order_different_phase_after_disable }, { - "this_var_as_object", - Parser_this_var_as_object + "system_order_different_phase_after_activate", + Pipeline_system_order_different_phase_after_activate }, { - "pair_implicit_subject", - Parser_pair_implicit_subject + "system_order_after_new_system_lower_id", + Pipeline_system_order_after_new_system_lower_id }, { - "pair_implicit_subject_wildcard_pred", - Parser_pair_implicit_subject_wildcard_pred + "system_order_after_new_system_inbetween_id", + Pipeline_system_order_after_new_system_inbetween_id }, { - "pair_implicit_subject_wildcard_obj", - Parser_pair_implicit_subject_wildcard_obj + "system_order_after_new_system_higher_id", + Pipeline_system_order_after_new_system_higher_id }, { - "pair_implicit_subject_any_pred", - Parser_pair_implicit_subject_any_pred + "system_reverse_order_by_phase_custom_pipeline", + Pipeline_system_reverse_order_by_phase_custom_pipeline }, { - "pair_implicit_subject_any_obj", - Parser_pair_implicit_subject_any_obj + "merge_after_staged_out", + Pipeline_merge_after_staged_out }, { - "pair_implicit_subject_this_pred", - Parser_pair_implicit_subject_this_pred + "merge_after_not_out", + Pipeline_merge_after_not_out }, { - "pair_implicit_subject_this_obj", - Parser_pair_implicit_subject_this_obj + "no_merge_after_main_out", + Pipeline_no_merge_after_main_out }, { - "pair_implicit_subject_pred_w_self", - Parser_pair_implicit_subject_pred_w_self + "merge_after_staged_in_out", + Pipeline_merge_after_staged_in_out }, { - "pair_implicit_subject_obj_w_self", - Parser_pair_implicit_subject_obj_w_self + "merge_after_staged_inout_main_implicit_inout", + Pipeline_merge_after_staged_inout_main_implicit_inout }, { - "pair_implicit_subject_pred_w_up", - Parser_pair_implicit_subject_pred_w_up + "merge_after_staged_inout_main_inout", + Pipeline_merge_after_staged_inout_main_inout }, { - "pair_implicit_subject_obj_w_up", - Parser_pair_implicit_subject_obj_w_up + "merge_after_staged_out_before_owned", + Pipeline_merge_after_staged_out_before_owned }, { - "pair_implicit_subject_pred_w_self_up", - Parser_pair_implicit_subject_pred_w_self_up + "switch_pipeline", + Pipeline_switch_pipeline }, { - "pair_implicit_subject_obj_w_self_up", - Parser_pair_implicit_subject_obj_w_self_up + "run_pipeline", + Pipeline_run_pipeline }, { - "pair_implicit_subject_pred_w_up_trav", - Parser_pair_implicit_subject_pred_w_up_trav + "get_pipeline_from_stage", + Pipeline_get_pipeline_from_stage }, { - "pair_implicit_subject_obj_w_up_trav", - Parser_pair_implicit_subject_obj_w_up_trav + "3_systems_3_types", + Pipeline_3_systems_3_types }, { - "pair_implicit_subject_pred_w_invalid_flags", - Parser_pair_implicit_subject_pred_w_invalid_flags + "random_read_after_random_write_out_in", + Pipeline_random_read_after_random_write_out_in }, { - "pair_implicit_subject_obj_w_invalid_flags", - Parser_pair_implicit_subject_obj_w_invalid_flags + "random_read_after_random_write_inout_in", + Pipeline_random_read_after_random_write_inout_in }, { - "pair_explicit_subject", - Parser_pair_explicit_subject + "random_read_after_random_write_out_inout", + Pipeline_random_read_after_random_write_out_inout }, { - "pair_explicit_subject_this", - Parser_pair_explicit_subject_this + "random_read_after_random_write_inout_inout", + Pipeline_random_read_after_random_write_inout_inout }, { - "pair_explicit_subject_this_by_name", - Parser_pair_explicit_subject_this_by_name + "random_read_after_random_write_w_not_write", + Pipeline_random_read_after_random_write_w_not_write }, { - "pair_explicit_subject_this_by_var_name", - Parser_pair_explicit_subject_this_by_var_name + "random_read_after_random_write_w_not_read", + Pipeline_random_read_after_random_write_w_not_read }, { - "pair_explicit_subject_wildcard_pred", - Parser_pair_explicit_subject_wildcard_pred + "random_read_after_random_write_w_wildcard", + Pipeline_random_read_after_random_write_w_wildcard }, { - "pair_explicit_subject_wildcard_subj", - Parser_pair_explicit_subject_wildcard_subj + "random_in_after_random_inout_after_random_out", + Pipeline_random_in_after_random_inout_after_random_out }, { - "pair_explicit_subject_wildcard_obj", - Parser_pair_explicit_subject_wildcard_obj + "stage_write_before_read", + Pipeline_stage_write_before_read }, { - "pair_implicit_subject_0_object", - Parser_pair_implicit_subject_0_object + "mixed_multithreaded", + Pipeline_mixed_multithreaded }, { - "pair_explicit_subject_0_object", - Parser_pair_explicit_subject_0_object + "mixed_multithreaded_tasks", + Pipeline_mixed_multithreaded_tasks }, { - "pair_explicit_subject_0", - Parser_pair_explicit_subject_0 + "mixed_staging", + Pipeline_mixed_staging }, { - "in_component_implicit_subject", - Parser_in_component_implicit_subject + "no_staging_system_create_query", + Pipeline_no_staging_system_create_query }, { - "in_component_explicit_subject", - Parser_in_component_explicit_subject + "single_threaded_pipeline_change", + Pipeline_single_threaded_pipeline_change }, { - "in_pair_implicit_subject", - Parser_in_pair_implicit_subject + "multi_threaded_pipeline_change", + Pipeline_multi_threaded_pipeline_change }, { - "in_pair_explicit_subject", - Parser_in_pair_explicit_subject + "multi_threaded_pipeline_change_tasks", + Pipeline_multi_threaded_pipeline_change_tasks }, { - "inout_component_implicit_subject", - Parser_inout_component_implicit_subject + "activate_after_add", + Pipeline_activate_after_add }, { - "inout_component_explicit_subject", - Parser_inout_component_explicit_subject + "match_all_after_pipeline_rebuild", + Pipeline_match_all_after_pipeline_rebuild }, { - "inout_pair_implicit_subject", - Parser_inout_pair_implicit_subject + "empty_pipeline", + Pipeline_empty_pipeline }, { - "inout_pair_explicit_subject", - Parser_inout_pair_explicit_subject + "custom_pipeline_w_system_macro", + Pipeline_custom_pipeline_w_system_macro }, { - "out_component_implicit_subject", - Parser_out_component_implicit_subject + "pipeline_w_short_notation", + Pipeline_pipeline_w_short_notation }, { - "out_component_explicit_subject", - Parser_out_component_explicit_subject + "stack_allocator_after_progress", + Pipeline_stack_allocator_after_progress }, { - "out_pair_implicit_subject", - Parser_out_pair_implicit_subject + "stack_allocator_after_progress_w_pipeline_change", + Pipeline_stack_allocator_after_progress_w_pipeline_change }, { - "out_pair_explicit_subject", - Parser_out_pair_explicit_subject + "iter_from_world_in_singlethread_system_multitead_app", + Pipeline_iter_from_world_in_singlethread_system_multitead_app }, { - "inout_filter_component", - Parser_inout_filter_component + "iter_from_world_in_singlethread_system_multitead_app_tasks", + Pipeline_iter_from_world_in_singlethread_system_multitead_app_tasks }, { - "component_singleton", - Parser_component_singleton + "no_staging_after_inactive_system", + Pipeline_no_staging_after_inactive_system }, { - "this_singleton", - Parser_this_singleton + "inactive_system_after_no_staging_system_no_defer_w_filter", + Pipeline_inactive_system_after_no_staging_system_no_defer_w_filter }, { - "component_implicit_no_subject", - Parser_component_implicit_no_subject + "inactive_system_after_2_no_staging_system_no_defer_w_filter", + Pipeline_inactive_system_after_2_no_staging_system_no_defer_w_filter }, { - "component_explicit_no_subject", - Parser_component_explicit_no_subject + "inactive_system_after_no_staging_system_no_defer_w_filter_w_no_staging_at_end", + Pipeline_inactive_system_after_no_staging_system_no_defer_w_filter_w_no_staging_at_end }, { - "pair_no_subject", - Parser_pair_no_subject + "inactive_multithread_system_after_no_staging_system_no_defer", + Pipeline_inactive_multithread_system_after_no_staging_system_no_defer }, { - "variable_single_char", - Parser_variable_single_char + "inactive_multithread_tasks_system_after_no_staging_system_no_defer", + Pipeline_inactive_multithread_tasks_system_after_no_staging_system_no_defer }, { - "variable_multi_char", - Parser_variable_multi_char + "inactive_multithread_system_after_no_staging_system_no_defer_w_no_staging_at_end", + Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_w_no_staging_at_end }, { - "variable_multi_char_w_underscore", - Parser_variable_multi_char_w_underscore + "inactive_multithread_tasks_system_after_no_staging_system_no_defer_w_no_staging_at_end", + Pipeline_inactive_multithread_tasks_system_after_no_staging_system_no_defer_w_no_staging_at_end }, { - "variable_multi_char_w_number", - Parser_variable_multi_char_w_number + "multi_threaded_pipeline_change_w_only_singlethreaded", + Pipeline_multi_threaded_pipeline_change_w_only_singlethreaded }, { - "variable_multi_char_not_allcaps", - Parser_variable_multi_char_not_allcaps + "multi_threaded_tasks_pipeline_change_w_only_singlethreaded", + Pipeline_multi_threaded_tasks_pipeline_change_w_only_singlethreaded }, { - "pred_var", - Parser_pred_var + "sync_after_not_out_for_out", + Pipeline_sync_after_not_out_for_out }, { - "obj_var", - Parser_obj_var + "pair_wildcard_read_after_staged_write", + Pipeline_pair_wildcard_read_after_staged_write }, { - "component_not", - Parser_component_not + "pair_read_after_staged_wildcard_write", + Pipeline_pair_read_after_staged_wildcard_write }, { - "pair_implicit_subject_not", - Parser_pair_implicit_subject_not + "no_sync_after_pair_wildcard_read_after_unmatching_staged_write", + Pipeline_no_sync_after_pair_wildcard_read_after_unmatching_staged_write }, { - "pair_explicit_subject_not", - Parser_pair_explicit_subject_not + "no_merge_after_from_nothing_w_default_inout", + Pipeline_no_merge_after_from_nothing_w_default_inout }, { - "2_component_not", - Parser_2_component_not + "on_merge_activate_system_before_merge", + Pipeline_on_merge_activate_system_before_merge }, { - "2_component_not_no_space", - Parser_2_component_not_no_space + "disable_phase", + Pipeline_disable_phase }, { - "component_optional", - Parser_component_optional + "disable_parent", + Pipeline_disable_parent }, { - "2_component_optional", - Parser_2_component_optional + "multi_threaded_no_staging_w_add_after_read", + Pipeline_multi_threaded_no_staging_w_add_after_read }, { - "2_component_optional_no_space", - Parser_2_component_optional_no_space + "multi_threaded_tasks_no_staging_w_add_after_read", + Pipeline_multi_threaded_tasks_no_staging_w_add_after_read }, { - "from_and", - Parser_from_and + "1_startup_system", + Pipeline_1_startup_system }, { - "from_or", - Parser_from_or + "2_startup_systems", + Pipeline_2_startup_systems }, { - "from_not", - Parser_from_not + "2_startup_phases", + Pipeline_2_startup_phases }, { - "pair_implicit_subject_optional", - Parser_pair_implicit_subject_optional + "2_startup_systems_w_merge", + Pipeline_2_startup_systems_w_merge }, { - "pair_explicit_subject_optional", - Parser_pair_explicit_subject_optional + "inactive_last_system_merge_count", + Pipeline_inactive_last_system_merge_count }, { - "pred_implicit_subject_w_role", - Parser_pred_implicit_subject_w_role + "inactive_middle_system_merge_count", + Pipeline_inactive_middle_system_merge_count }, { - "pred_explicit_subject_w_role", - Parser_pred_explicit_subject_w_role + "last_no_readonly_system_merge_count", + Pipeline_last_no_readonly_system_merge_count }, { - "pred_no_subject_w_role", - Parser_pred_no_subject_w_role + "2_pipelines_1_system", + Pipeline_2_pipelines_1_system }, { - "pair_implicit_subject_w_role", - Parser_pair_implicit_subject_w_role + "builtin_pipeline_w_self_system_term", + Pipeline_builtin_pipeline_w_self_system_term }, { - "pair_explicit_subject_w_role", - Parser_pair_explicit_subject_w_role + "custom_pipeline_w_self_system_term", + Pipeline_custom_pipeline_w_self_system_term }, { - "inout_role_pred_implicit_subject", - Parser_inout_role_pred_implicit_subject + "switch_from_threads_to_tasks", + Pipeline_switch_from_threads_to_tasks }, { - "inout_role_pred_no_subject", - Parser_inout_role_pred_no_subject + "switch_from_tasks_to_threads", + Pipeline_switch_from_tasks_to_threads }, { - "inout_role_pred_explicit_subject", - Parser_inout_role_pred_explicit_subject + "run_pipeline_multithreaded", + Pipeline_run_pipeline_multithreaded }, { - "inout_role_pair_implicit_subject", - Parser_inout_role_pair_implicit_subject + "run_pipeline_multithreaded_tasks", + Pipeline_run_pipeline_multithreaded_tasks }, { - "inout_role_pair_explicit_subject", - Parser_inout_role_pair_explicit_subject + "pipeline_init_no_terms", + Pipeline_pipeline_init_no_terms }, { - "2_pred_implicit_subject", - Parser_2_pred_implicit_subject + "pipeline_init_no_system_term", + Pipeline_pipeline_init_no_system_term }, { - "2_pred_no_subject", - Parser_2_pred_no_subject + "disable_component_from_immediate_system", + Pipeline_disable_component_from_immediate_system }, { - "2_pred_explicit_subject", - Parser_2_pred_explicit_subject + "run_w_empty_query", + Pipeline_run_w_empty_query }, { - "2_pair_implicit_subject", - Parser_2_pair_implicit_subject - }, + "run_w_0_src_query", + Pipeline_run_w_0_src_query + } +}; + +bake_test_case SystemMisc_testcases[] = { { - "2_pair_explicit_subject", - Parser_2_pair_explicit_subject + "invalid_not_without_id", + SystemMisc_invalid_not_without_id }, { - "2_pred_role", - Parser_2_pred_role + "invalid_optional_without_id", + SystemMisc_invalid_optional_without_id }, { - "2_pair_implicit_subj_role", - Parser_2_pair_implicit_subj_role + "invalid_entity_without_id", + SystemMisc_invalid_entity_without_id }, { - "2_pair_explicit_subj_role", - Parser_2_pair_explicit_subj_role + "invalid_empty_element", + SystemMisc_invalid_empty_element }, { - "2_or_pred_implicit_subj", - Parser_2_or_pred_implicit_subj + "invalid_empty_element_w_space", + SystemMisc_invalid_empty_element_w_space }, { - "2_or_pred_explicit_subj", - Parser_2_or_pred_explicit_subj + "invalid_empty_or", + SystemMisc_invalid_empty_or }, { - "2_or_pair_implicit_subj", - Parser_2_or_pair_implicit_subj + "invalid_empty_or_w_space", + SystemMisc_invalid_empty_or_w_space }, { - "2_or_pair_explicit_subj", - Parser_2_or_pair_explicit_subj + "invalid_or_w_not", + SystemMisc_invalid_or_w_not }, { - "2_or_pred_inout", - Parser_2_or_pred_inout + "invalid_not_w_or", + SystemMisc_invalid_not_w_or }, { - "1_digit_pred_implicit_subj", - Parser_1_digit_pred_implicit_subj + "invalid_0_w_and", + SystemMisc_invalid_0_w_and }, { - "1_digit_pred_no_subj", - Parser_1_digit_pred_no_subj + "invalid_0_w_from_entity", + SystemMisc_invalid_0_w_from_entity }, { - "1_digit_pred_explicit_subj", - Parser_1_digit_pred_explicit_subj + "invalid_component_id", + SystemMisc_invalid_component_id }, { - "1_digit_pair_implicit_subj", - Parser_1_digit_pair_implicit_subj + "invalid_entity_id", + SystemMisc_invalid_entity_id }, { - "1_digit_pair_explicit_subj", - Parser_1_digit_pair_explicit_subj + "invalid_null_string", + SystemMisc_invalid_null_string }, { - "pred_implicit_subject_self", - Parser_pred_implicit_subject_self + "invalid_empty_string", + SystemMisc_invalid_empty_string }, { - "pred_implicit_subject_superset", - Parser_pred_implicit_subject_superset + "invalid_empty_string_w_space", + SystemMisc_invalid_empty_string_w_space }, { - "pred_implicit_subject_subset", - Parser_pred_implicit_subject_subset + "redefine_row_system", + SystemMisc_redefine_row_system }, { - "pred_implicit_subject_superset_inclusive", - Parser_pred_implicit_subject_superset_inclusive + "system_w_or_prefab", + SystemMisc_system_w_or_prefab }, { - "pred_implicit_subject_subset_inclusive", - Parser_pred_implicit_subject_subset_inclusive + "system_w_or_disabled", + SystemMisc_system_w_or_disabled }, { - "pred_implicit_subject_superset_cascade", - Parser_pred_implicit_subject_superset_cascade + "system_w_or_disabled_and_prefab", + SystemMisc_system_w_or_disabled_and_prefab }, { - "pred_implicit_subject_subset_cascade", - Parser_pred_implicit_subject_subset_cascade + "table_columns_access", + SystemMisc_table_columns_access }, { - "pred_implicit_subject_superset_inclusive_cascade", - Parser_pred_implicit_subject_superset_inclusive_cascade + "dont_enable_after_rematch", + SystemMisc_dont_enable_after_rematch }, { - "pred_implicit_subject_subset_inclusive_cascade", - Parser_pred_implicit_subject_subset_inclusive_cascade + "ensure_single_merge", + SystemMisc_ensure_single_merge }, { - "pred_implicit_subject_implicit_superset_cascade", - Parser_pred_implicit_subject_implicit_superset_cascade - }, - { - "pred_implicit_subject_implicit_superset_inclusive_cascade", - Parser_pred_implicit_subject_implicit_superset_inclusive_cascade - }, - { - "pred_implicit_subject_implicit_superset_cascade_w_rel", - Parser_pred_implicit_subject_implicit_superset_cascade_w_rel - }, - { - "pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel", - Parser_pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel + "match_system", + SystemMisc_match_system }, { - "pred_implicit_subject_superset_childof", - Parser_pred_implicit_subject_superset_childof + "system_initial_state", + SystemMisc_system_initial_state }, { - "pred_implicit_subject_cascade_superset_childof", - Parser_pred_implicit_subject_cascade_superset_childof + "add_own_component", + SystemMisc_add_own_component }, { - "pred_implicit_subject_superset_cascade_childof", - Parser_pred_implicit_subject_superset_cascade_childof + "change_system_action", + SystemMisc_change_system_action }, { - "pred_implicit_subject_superset_cascade_childof_optional", - Parser_pred_implicit_subject_superset_cascade_childof_optional + "system_readeactivate", + SystemMisc_system_readeactivate }, { - "expr_w_symbol", - Parser_expr_w_symbol + "system_readeactivate_w_2_systems", + SystemMisc_system_readeactivate_w_2_systems }, { - "expr_w_newline", - Parser_expr_w_newline + "add_to_system_in_progress", + SystemMisc_add_to_system_in_progress }, { - "subj_entity_w_explicit_self", - Parser_subj_entity_w_explicit_self + "redefine_null_signature", + SystemMisc_redefine_null_signature }, { - "subj_entity_w_explicit_self_superset", - Parser_subj_entity_w_explicit_self_superset + "redefine_0_signature", + SystemMisc_redefine_0_signature }, { - "subj_entity_w_explicit_superset_relation", - Parser_subj_entity_w_explicit_superset_relation + "redeclare_system_explicit_id", + SystemMisc_redeclare_system_explicit_id }, { - "subj_entity_w_explicit_self_superset_relation", - Parser_subj_entity_w_explicit_self_superset_relation + "redeclare_system_explicit_id_null_expr", + SystemMisc_redeclare_system_explicit_id_null_expr }, { - "obj_entity_w_explicit_self", - Parser_obj_entity_w_explicit_self + "redeclare_system_explicit_id_no_name", + SystemMisc_redeclare_system_explicit_id_no_name }, { - "obj_entity_w_explicit_self_superset", - Parser_obj_entity_w_explicit_self_superset + "declare_different_id_same_name", + SystemMisc_declare_different_id_same_name }, { - "obj_entity_w_explicit_superset_relation", - Parser_obj_entity_w_explicit_superset_relation + "declare_different_id_same_name_w_scope", + SystemMisc_declare_different_id_same_name_w_scope }, { - "obj_entity_w_explicit_self_superset_relation", - Parser_obj_entity_w_explicit_self_superset_relation + "rw_in_implicit_any", + SystemMisc_rw_in_implicit_any }, { - "pred_entity_w_explicit_self", - Parser_pred_entity_w_explicit_self + "rw_in_implicit_shared", + SystemMisc_rw_in_implicit_shared }, { - "pred_entity_w_explicit_self_superset", - Parser_pred_entity_w_explicit_self_superset + "rw_in_implicit_from_empty", + SystemMisc_rw_in_implicit_from_empty }, { - "pred_entity_w_explicit_superset_relation", - Parser_pred_entity_w_explicit_superset_relation + "rw_in_implicit_from_entity", + SystemMisc_rw_in_implicit_from_entity }, { - "pred_entity_w_explicit_self_superset_relation", - Parser_pred_entity_w_explicit_self_superset_relation + "rw_out_explicit_any", + SystemMisc_rw_out_explicit_any }, { - "pred_entity_no_args_w_explicit_self", - Parser_pred_entity_no_args_w_explicit_self + "rw_out_explicit_shared", + SystemMisc_rw_out_explicit_shared }, { - "pred_entity_no_args_w_explicit_self_superset", - Parser_pred_entity_no_args_w_explicit_self_superset + "rw_out_explicit_from_empty", + SystemMisc_rw_out_explicit_from_empty }, { - "pred_entity_no_args_w_explicit_superset_relation", - Parser_pred_entity_no_args_w_explicit_superset_relation + "rw_out_explicit_from_entity", + SystemMisc_rw_out_explicit_from_entity }, { - "pred_entity_no_args_w_explicit_self_superset_relation", - Parser_pred_entity_no_args_w_explicit_self_superset_relation + "activate_system_for_table_w_n_pairs", + SystemMisc_activate_system_for_table_w_n_pairs }, { - "pred_entity_no_args_2_terms_w_explicit_self", - Parser_pred_entity_no_args_2_terms_w_explicit_self + "get_query", + SystemMisc_get_query }, { - "pred_entity_no_args_2_terms_w_explicit_self_superset", - Parser_pred_entity_no_args_2_terms_w_explicit_self_superset + "set_get_context", + SystemMisc_set_get_context }, { - "pred_entity_no_args_2_terms_w_explicit_superset_relation", - Parser_pred_entity_no_args_2_terms_w_explicit_superset_relation + "set_get_binding_context", + SystemMisc_set_get_binding_context }, { - "pred_entity_no_args_2_terms_w_explicit_self_superset_relation", - Parser_pred_entity_no_args_2_terms_w_explicit_self_superset_relation + "deactivate_after_disable", + SystemMisc_deactivate_after_disable }, { - "newline", - Parser_newline + "delete_system", + SystemMisc_delete_system }, { - "2_newlines", - Parser_2_newlines + "delete_pipeline_system", + SystemMisc_delete_pipeline_system }, { - "3_newlines", - Parser_3_newlines + "delete_system_w_ctx", + SystemMisc_delete_system_w_ctx }, { - "space", - Parser_space + "update_ctx", + SystemMisc_update_ctx }, { - "2_spaces", - Parser_2_spaces + "run_custom_run_action", + SystemMisc_run_custom_run_action }, { - "trailing_newline", - Parser_trailing_newline + "pipeline_custom_run_action", + SystemMisc_pipeline_custom_run_action }, { - "2_trailing_newlines", - Parser_2_trailing_newlines + "change_custom_run_action", + SystemMisc_change_custom_run_action }, { - "trailing_space", - Parser_trailing_space + "custom_run_action_call_next", + SystemMisc_custom_run_action_call_next }, { - "2_trailing_spaces", - Parser_2_trailing_spaces + "system_w_short_notation", + SystemMisc_system_w_short_notation }, { - "template_type", - Parser_template_type + "update_interval_w_system_init", + SystemMisc_update_interval_w_system_init }, { - "predicate_w_parens", - Parser_predicate_w_parens + "update_rate_w_system_init", + SystemMisc_update_rate_w_system_init }, { - "not_alive_pred", - Parser_not_alive_pred + "system_w_interval_rate_stop_timer", + SystemMisc_system_w_interval_rate_stop_timer }, { - "not_alive_subj", - Parser_not_alive_subj + "system_w_rate_filter_self", + SystemMisc_system_w_rate_filter_self }, { - "not_alive_obj", - Parser_not_alive_obj + "system_same_interval_same_tick", + SystemMisc_system_same_interval_same_tick }, { - "this_subj_var_kind", - Parser_this_subj_var_kind + "system_no_id_in_scope", + SystemMisc_system_no_id_in_scope }, { - "this_obj_var_kind", - Parser_this_obj_var_kind + "register_callback_after_run", + SystemMisc_register_callback_after_run }, { - "this_subj_obj_var_kind", - Parser_this_subj_obj_var_kind + "register_run_after_callback", + SystemMisc_register_run_after_callback }, { - "var_w_name", - Parser_var_w_name + "register_callback_after_run_ctx", + SystemMisc_register_callback_after_run_ctx }, { - "entity_pred_no_name", - Parser_entity_pred_no_name - }, + "register_run_after_callback_ctx", + SystemMisc_register_run_after_callback_ctx + } +}; + +bake_test_case SystemPeriodic_testcases[] = { { - "entity_subj_no_name", - Parser_entity_subj_no_name + "1_type_1_component", + SystemPeriodic_1_type_1_component }, { - "entity_obj_no_name", - Parser_entity_obj_no_name + "1_type_3_component", + SystemPeriodic_1_type_3_component }, { - "this_pred_no_name", - Parser_this_pred_no_name + "3_type_1_component", + SystemPeriodic_3_type_1_component }, { - "this_subj_no_name", - Parser_this_subj_no_name + "2_type_3_component", + SystemPeriodic_2_type_3_component }, { - "this_obj_no_name", - Parser_this_obj_no_name + "1_type_1_component_1_tag", + SystemPeriodic_1_type_1_component_1_tag }, { - "auto_object_variable", - Parser_auto_object_variable + "2_type_1_component_1_tag", + SystemPeriodic_2_type_1_component_1_tag }, { - "auto_object_variable_w_subj", - Parser_auto_object_variable_w_subj + "2_type_1_and_1_not", + SystemPeriodic_2_type_1_and_1_not }, { - "auto_scoped_variable", - Parser_auto_scoped_variable + "2_type_2_and_1_not", + SystemPeriodic_2_type_2_and_1_not }, { - "invalid_variable_only", - Parser_invalid_variable_only + "2_type_2_and_2_not", + SystemPeriodic_2_type_2_and_2_not }, { - "oneof_self_pred_w_relative_obj", - Parser_oneof_self_pred_w_relative_obj + "4_type_1_and_1_or", + SystemPeriodic_4_type_1_and_1_or }, { - "oneof_other_pred_w_relative_obj", - Parser_oneof_other_pred_w_relative_obj + "4_type_1_and_1_or_of_3", + SystemPeriodic_4_type_1_and_1_or_of_3 }, { - "oneof_self_pred_w_invalid_obj", - Parser_oneof_self_pred_w_invalid_obj + "1_type_1_and_1_or", + SystemPeriodic_1_type_1_and_1_or }, { - "oneof_other_pred_w_invalid_obj", - Parser_oneof_other_pred_w_invalid_obj + "2_type_1_and_1_optional", + SystemPeriodic_2_type_1_and_1_optional }, { - "pair_implicit_src_missing_rel", - Parser_pair_implicit_src_missing_rel + "2_type_2_and_1_optional", + SystemPeriodic_2_type_2_and_1_optional }, { - "pair_implicit_src_missing_obj", - Parser_pair_implicit_src_missing_obj + "6_type_1_and_2_optional", + SystemPeriodic_6_type_1_and_2_optional }, { - "pair_explicit_src_missing_src", - Parser_pair_explicit_src_missing_src + "ensure_optional_is_unset_column", + SystemPeriodic_ensure_optional_is_unset_column }, { - "pair_explicit_src_missing_obj", - Parser_pair_explicit_src_missing_obj + "ensure_optional_is_null_shared", + SystemPeriodic_ensure_optional_is_null_shared }, { - "eq_id", - Parser_eq_id + "match_2_systems_w_populated_table", + SystemPeriodic_match_2_systems_w_populated_table }, { - "eq_id_var", - Parser_eq_id_var + "on_period", + SystemPeriodic_on_period }, { - "eq_var_id", - Parser_eq_var_id + "on_period_long_delta", + SystemPeriodic_on_period_long_delta }, { - "eq_var", - Parser_eq_var + "disabled", + SystemPeriodic_disabled }, { - "neq_id", - Parser_neq_id + "disabled_feature", + SystemPeriodic_disabled_feature }, { - "neq_id_var", - Parser_neq_id_var + "disabled_nested_feature", + SystemPeriodic_disabled_nested_feature }, { - "neq_var_id", - Parser_neq_var_id + "two_refs", + SystemPeriodic_two_refs }, { - "neq_var", - Parser_neq_var + "filter_disabled", + SystemPeriodic_filter_disabled }, { - "eq_name", - Parser_eq_name + "match_disabled", + SystemPeriodic_match_disabled }, { - "eq_name_var", - Parser_eq_name_var + "match_disabled_and_enabled", + SystemPeriodic_match_disabled_and_enabled }, { - "eq_var_name", - Parser_eq_var_name + "match_prefab", + SystemPeriodic_match_prefab }, { - "eq_var", - Parser_eq_var + "match_prefab_and_normal", + SystemPeriodic_match_prefab_and_normal }, { - "neq_name", - Parser_neq_name + "is_shared_on_column_not_set", + SystemPeriodic_is_shared_on_column_not_set }, { - "neq_name_var", - Parser_neq_name_var + "owned_column", + SystemPeriodic_owned_column }, { - "neq_var_name", - Parser_neq_var_name + "owned_not_column", + SystemPeriodic_owned_not_column }, { - "neq_var", - Parser_neq_var + "owned_or_column", + SystemPeriodic_owned_or_column }, { - "match_name", - Parser_match_name + "shared_column", + SystemPeriodic_shared_column }, { - "match_name_var", - Parser_match_name_var + "shared_not_column", + SystemPeriodic_shared_not_column }, { - "match_var_name", - Parser_match_var_name + "shared_or_column", + SystemPeriodic_shared_or_column }, { - "match_var", - Parser_match_var + "container_dont_match_inheritance", + SystemPeriodic_container_dont_match_inheritance }, { - "nmatch_name", - Parser_nmatch_name + "cascade_dont_match_inheritance", + SystemPeriodic_cascade_dont_match_inheritance }, { - "nmatch_name_var", - Parser_nmatch_name_var + "not_from_entity", + SystemPeriodic_not_from_entity }, { - "nmatch_var_name", - Parser_nmatch_var_name + "sys_context", + SystemPeriodic_sys_context }, { - "eq_same_var", - Parser_eq_same_var + "get_sys_context_from_param", + SystemPeriodic_get_sys_context_from_param }, { - "neq_same_var", - Parser_neq_same_var + "owned_only", + SystemPeriodic_owned_only }, { - "eq_same_var_this", - Parser_eq_same_var_this + "shared_only", + SystemPeriodic_shared_only }, { - "neq_same_var_this", - Parser_neq_same_var_this + "is_in_readonly", + SystemPeriodic_is_in_readonly }, { - "eq_w_optional", - Parser_eq_w_optional + "get_period", + SystemPeriodic_get_period }, { - "neq_w_optional", - Parser_neq_w_optional + "and_type", + SystemPeriodic_and_type }, { - "match_w_optional", - Parser_match_w_optional - }, + "or_type", + SystemPeriodic_or_type + } +}; + +bake_test_case Timer_testcases[] = { { - "query_scope_1_term", - Parser_query_scope_1_term + "timeout", + Timer_timeout }, { - "query_scope_1_term_spaces", - Parser_query_scope_1_term_spaces + "interval", + Timer_interval }, { - "query_scope_2_terms", - Parser_query_scope_2_terms + "shared_timeout", + Timer_shared_timeout }, { - "query_nested_scope", - Parser_query_nested_scope + "shared_interval", + Timer_shared_interval }, { - "query_nested_scope_spaces", - Parser_query_nested_scope_spaces + "start_stop_one_shot", + Timer_start_stop_one_shot }, { - "query_scope_unbalanced", - Parser_query_scope_unbalanced + "start_stop_interval", + Timer_start_stop_interval }, { - "query_not_scope", - Parser_query_not_scope + "rate_filter", + Timer_rate_filter }, { - "query_empty_scope", - Parser_query_empty_scope + "rate_filter_w_rate_filter_src", + Timer_rate_filter_w_rate_filter_src }, { - "override_tag", - Parser_override_tag + "rate_filter_w_timer_src", + Timer_rate_filter_w_timer_src }, { - "override_pair", - Parser_override_pair + "rate_filter_with_empty_src", + Timer_rate_filter_with_empty_src }, { - "pair_3_args", - Parser_pair_3_args + "one_shot_timer_entity", + Timer_one_shot_timer_entity }, { - "pair_3_args_implicit_this", - Parser_pair_3_args_implicit_this + "interval_timer_entity", + Timer_interval_timer_entity }, { - "pair_4_args", - Parser_pair_4_args + "rate_entity", + Timer_rate_entity }, { - "pair_4_args_implicit_this", - Parser_pair_4_args_implicit_this + "nested_rate_entity", + Timer_nested_rate_entity }, { - "pair_3_args_2_terms", - Parser_pair_3_args_2_terms + "nested_rate_entity_empty_src", + Timer_nested_rate_entity_empty_src }, { - "pair_3_args_this_tgt", - Parser_pair_3_args_this_tgt + "naked_tick_entity", + Timer_naked_tick_entity }, { - "pair_3_args_2_terms_this_tgt", - Parser_pair_3_args_2_terms_this_tgt + "stop_timer_w_rate", + Timer_stop_timer_w_rate }, { - "pair_3_args_2_terms_this_tgt_implicit_this", - Parser_pair_3_args_2_terms_this_tgt_implicit_this + "stop_timer_w_rate_same_src", + Timer_stop_timer_w_rate_same_src }, { - "cascade_desc", - Parser_cascade_desc + "randomize_timers", + Timer_randomize_timers } }; -bake_test_case Plecs_testcases[] = { - { - "null", - Plecs_null - }, +bake_test_case SystemCascade_testcases[] = { { - "empty", - Plecs_empty + "cascade_depth_1", + SystemCascade_cascade_depth_1 }, { - "space", - Plecs_space + "cascade_depth_2", + SystemCascade_cascade_depth_2 }, { - "space_newline", - Plecs_space_newline + "cascade_depth_2_new_syntax", + SystemCascade_cascade_depth_2_new_syntax }, { - "two_empty_newlines", - Plecs_two_empty_newlines + "add_after_match", + SystemCascade_add_after_match }, { - "three_empty_newlines", - Plecs_three_empty_newlines + "adopt_after_match", + SystemCascade_adopt_after_match }, { - "newline_trailing_space", - Plecs_newline_trailing_space + "custom_relation_cascade_depth_1", + SystemCascade_custom_relation_cascade_depth_1 }, { - "newline_trailing_spaces", - Plecs_newline_trailing_spaces + "custom_relation_cascade_depth_2", + SystemCascade_custom_relation_cascade_depth_2 }, { - "multiple_trailing_newlines", - Plecs_multiple_trailing_newlines + "custom_relation_add_after_match", + SystemCascade_custom_relation_add_after_match }, { - "entity", - Plecs_entity - }, + "custom_relation_adopt_after_match", + SystemCascade_custom_relation_adopt_after_match + } +}; + +bake_test_case SystemManual_testcases[] = { { - "entity_w_entity", - Plecs_entity_w_entity + "1_type_1_component", + SystemManual_1_type_1_component }, { - "entity_w_pair", - Plecs_entity_w_pair - }, - { - "2_entities", - Plecs_2_entities - }, - { - "2_entities_w_entities", - Plecs_2_entities_w_entities - }, - { - "3_entities_w_pairs", - Plecs_3_entities_w_pairs - }, - { - "line_comment", - Plecs_line_comment - }, - { - "line_comment_before_stmt", - Plecs_line_comment_before_stmt - }, - { - "line_comment_after_stmt", - Plecs_line_comment_after_stmt - }, - { - "line_comment_between_stmt", - Plecs_line_comment_between_stmt - }, - { - "multiple_line_comment", - Plecs_multiple_line_comment - }, - { - "line_comment_after_stmt_same_line", - Plecs_line_comment_after_stmt_same_line - }, - { - "comma_separated_pred", - Plecs_comma_separated_pred - }, - { - "comma_separated_pred_w_subj", - Plecs_comma_separated_pred_w_subj - }, - { - "comma_separated_pred_w_subj_obj", - Plecs_comma_separated_pred_w_subj_obj - }, - { - "comma_separated_pred_trailing_comma", - Plecs_comma_separated_pred_trailing_comma - }, - { - "comma_separated_pred_trailing_comma_newline", - Plecs_comma_separated_pred_trailing_comma_newline - }, - { - "comma_separated_pred_trailing_comma_newline_multiline", - Plecs_comma_separated_pred_trailing_comma_newline_multiline - }, - { - "hierarchy_1_child", - Plecs_hierarchy_1_child - }, - { - "hierarchy_2_children", - Plecs_hierarchy_2_children - }, - { - "hierarchy_1_child_same_line", - Plecs_hierarchy_1_child_same_line - }, - { - "hierarchy_2_children_same_line", - Plecs_hierarchy_2_children_same_line - }, - { - "entity_after_hierarchy", - Plecs_entity_after_hierarchy - }, - { - "newline_before_scope_open", - Plecs_newline_before_scope_open - }, - { - "comment_before_scope_open", - Plecs_comment_before_scope_open - }, - { - "comment_after_newline_before_scope_open", - Plecs_comment_after_newline_before_scope_open - }, - { - "hierarchy_2_levels", - Plecs_hierarchy_2_levels - }, - { - "hierarchy_2_levels_2_subtrees", - Plecs_hierarchy_2_levels_2_subtrees - }, - { - "missing_end_of_scope", - Plecs_missing_end_of_scope - }, - { - "missing_end_of_predicate_scope", - Plecs_missing_end_of_predicate_scope - }, - { - "create_in_scope", - Plecs_create_in_scope - }, - { - "hierarchy_w_pred_subj", - Plecs_hierarchy_w_pred_subj - }, - { - "hierarchy_custom_relation", - Plecs_hierarchy_custom_relation - }, - { - "hierarchy_custom_relation_2_levels", - Plecs_hierarchy_custom_relation_2_levels - }, - { - "hierarchy_custom_relation_apply_to_object", - Plecs_hierarchy_custom_relation_apply_to_object - }, - { - "hierarchy_custom_relation_apply_to_object_2_levels", - Plecs_hierarchy_custom_relation_apply_to_object_2_levels - }, - { - "entity_after_hierarchy_custom_relation", - Plecs_entity_after_hierarchy_custom_relation - }, - { - "entity_after_hierarchy_custom_relation_2_levels", - Plecs_entity_after_hierarchy_custom_relation_2_levels - }, - { - "pred_scope", - Plecs_pred_scope - }, - { - "pred_scope_2_levels", - Plecs_pred_scope_2_levels - }, - { - "pred_scope_inside_with", - Plecs_pred_scope_inside_with - }, - { - "pred_scope_nested_w_subj_scope", - Plecs_pred_scope_nested_w_subj_scope - }, - { - "with_tag", - Plecs_with_tag - }, - { - "with_tag_2_entities", - Plecs_with_tag_2_entities - }, - { - "with_tag_same_line", - Plecs_with_tag_same_line - }, - { - "with_tag_2_entities_same_line", - Plecs_with_tag_2_entities_same_line - }, - { - "with_tag_2_levels", - Plecs_with_tag_2_levels - }, - { - "with_tag_2_levels_2_subtrees", - Plecs_with_tag_2_levels_2_subtrees - }, - { - "with_n_tags", - Plecs_with_n_tags - }, - { - "with_n_tags_2_levels", - Plecs_with_n_tags_2_levels - }, - { - "with_after_scope", - Plecs_with_after_scope - }, - { - "with_after_with", - Plecs_with_after_with - }, - { - "scope_inside_with_inside_scope", - Plecs_scope_inside_with_inside_scope - }, - { - "with_inside_scope", - Plecs_with_inside_scope - }, - { - "assignment_w_1", - Plecs_assignment_w_1 - }, - { - "assignment_w_2", - Plecs_assignment_w_2 - }, - { - "assignment_w_pair", - Plecs_assignment_w_pair - }, - { - "assignment_w_invalid_subject", - Plecs_assignment_w_invalid_subject - }, - { - "assignment_w_invalid_with", - Plecs_assignment_w_invalid_with - }, - { - "inherit_w_colon", - Plecs_inherit_w_colon - }, - { - "inherit_w_colon_w_scope", - Plecs_inherit_w_colon_w_scope - }, - { - "inherit_w_colon_w_assign", - Plecs_inherit_w_colon_w_assign - }, - { - "assign_component_value", - Plecs_assign_component_value - }, - { - "assign_2_component_values", - Plecs_assign_2_component_values - }, - { - "assign_component_value_in_assign_scope", - Plecs_assign_component_value_in_assign_scope - }, - { - "assign_2_component_values_in_assign_scope", - Plecs_assign_2_component_values_in_assign_scope - }, - { - "type_and_assign_in_plecs", - Plecs_type_and_assign_in_plecs - }, - { - "type_and_assign_in_plecs_w_2_members", - Plecs_type_and_assign_in_plecs_w_2_members - }, - { - "type_and_assign_in_plecs_w_3_members", - Plecs_type_and_assign_in_plecs_w_3_members - }, - { - "type_and_assign_in_plecs_w_enum", - Plecs_type_and_assign_in_plecs_w_enum - }, - { - "type_and_assign_in_plecs_w_enum_using_meta", - Plecs_type_and_assign_in_plecs_w_enum_using_meta - }, - { - "type_and_assign_in_plecs_w_enum_primitive_using_meta", - Plecs_type_and_assign_in_plecs_w_enum_primitive_using_meta - }, - { - "type_and_assign_in_plecs_w_enum_primitive_and_struct", - Plecs_type_and_assign_in_plecs_w_enum_primitive_and_struct - }, - { - "type_and_assign_in_plecs_nested_member", - Plecs_type_and_assign_in_plecs_nested_member - }, - { - "dot_assign_nested_member", - Plecs_dot_assign_nested_member - }, - { - "dot_assign_binary_expr", - Plecs_dot_assign_binary_expr - }, - { - "open_scope_no_parent", - Plecs_open_scope_no_parent - }, - { - "create_subject_in_root_scope_w_resolvable_id", - Plecs_create_subject_in_root_scope_w_resolvable_id - }, - { - "create_subject_in_scope_w_resolvable_id", - Plecs_create_subject_in_scope_w_resolvable_id - }, - { - "create_subject_in_scope_w_resolvable_id_using", - Plecs_create_subject_in_scope_w_resolvable_id_using - }, - { - "using_scope", - Plecs_using_scope - }, - { - "using_nested_scope", - Plecs_using_nested_scope - }, - { - "using_nested_in_scope", - Plecs_using_nested_in_scope - }, - { - "using_with_scope", - Plecs_using_with_scope - }, - { - "using_w_entity_ref_in_value_2_members", - Plecs_using_w_entity_ref_in_value_2_members - }, - { - "using_w_entity_ref_in_value_3_members", - Plecs_using_w_entity_ref_in_value_3_members - }, - { - "2_using_scope", - Plecs_2_using_scope - }, - { - "2_using_in_different_scope", - Plecs_2_using_in_different_scope - }, - { - "empty_scope_after_using", - Plecs_empty_scope_after_using - }, - { - "assignment_to_non_component", - Plecs_assignment_to_non_component - }, - { - "struct_w_member_w_assignment_to_nothing", - Plecs_struct_w_member_w_assignment_to_nothing - }, - { - "struct_w_member_w_assignment_to_empty_scope", - Plecs_struct_w_member_w_assignment_to_empty_scope - }, - { - "scope_after_assign", - Plecs_scope_after_assign - }, - { - "assign_after_inherit", - Plecs_assign_after_inherit - }, - { - "multiple_assignments_single_line", - Plecs_multiple_assignments_single_line - }, - { - "2_stmts_in_scope_w_no_parent", - Plecs_2_stmts_in_scope_w_no_parent - }, - { - "scope_after_assign_1_tag", - Plecs_scope_after_assign_1_tag - }, - { - "scope_after_assign_2_tags", - Plecs_scope_after_assign_2_tags - }, - { - "invalid_nested_assignment", - Plecs_invalid_nested_assignment - }, - { - "invalid_partial_pair_assignment", - Plecs_invalid_partial_pair_assignment - }, - { - "empty_assignment", - Plecs_empty_assignment - }, - { - "assign_tag_to_parent", - Plecs_assign_tag_to_parent - }, - { - "assign_component_to_parent", - Plecs_assign_component_to_parent - }, - { - "empty_assignment_before_end_of_scope", - Plecs_empty_assignment_before_end_of_scope - }, - { - "assign_to_parent_pair_w_new_entities_in_scope", - Plecs_assign_to_parent_pair_w_new_entities_in_scope - }, - { - "assign_to_parent_pair_w_existing_entities_in_scope", - Plecs_assign_to_parent_pair_w_existing_entities_in_scope - }, - { - "default_child_component", - Plecs_default_child_component - }, - { - "default_child_component_w_assign", - Plecs_default_child_component_w_assign - }, - { - "struct_type_w_default_child_component", - Plecs_struct_type_w_default_child_component - }, - { - "struct_type_w_default_child_component_nested_member", - Plecs_struct_type_w_default_child_component_nested_member - }, - { - "enum_type_w_default_child_component", - Plecs_enum_type_w_default_child_component - }, - { - "default_type_from_with", - Plecs_default_type_from_with - }, - { - "scope_w_1_subj_and_2_pairs", - Plecs_scope_w_1_subj_and_2_pairs - }, - { - "inherit_from_multiple", - Plecs_inherit_from_multiple - }, - { - "assign_pair_component", - Plecs_assign_pair_component - }, - { - "assign_pair_component_in_scope", - Plecs_assign_pair_component_in_scope - }, - { - "assign_pair_component_in_script", - Plecs_assign_pair_component_in_script - }, - { - "assign_pair_component_in_script_update", - Plecs_assign_pair_component_in_script_update - }, - { - "set_entity_names", - Plecs_set_entity_names - }, - { - "oneof", - Plecs_oneof - }, - { - "invalid_oneof", - Plecs_invalid_oneof - }, - { - "brief_annotation", - Plecs_brief_annotation - }, - { - "name_annotation", - Plecs_name_annotation - }, - { - "link_annotation", - Plecs_link_annotation - }, - { - "color_annotation", - Plecs_color_annotation - }, - { - "multiple_annotations", - Plecs_multiple_annotations - }, - { - "annotation_w_trailing_space", - Plecs_annotation_w_trailing_space - }, - { - "multiline_string", - Plecs_multiline_string - }, - { - "unterminated_multiline_string", - Plecs_unterminated_multiline_string - }, - { - "declaration_w_underscore_name", - Plecs_declaration_w_underscore_name - }, - { - "annotate_declaration", - Plecs_annotate_declaration - }, - { - "anonymous_entity", - Plecs_anonymous_entity - }, - { - "anonymous_entity_in_scope", - Plecs_anonymous_entity_in_scope - }, - { - "anonymous_declaration", - Plecs_anonymous_declaration - }, - { - "const_var_int", - Plecs_const_var_int - }, - { - "const_var_float", - Plecs_const_var_float - }, - { - "const_var_bool", - Plecs_const_var_bool - }, - { - "const_var_string", - Plecs_const_var_string - }, - { - "const_var_struct", - Plecs_const_var_struct - }, - { - "const_var_redeclare", - Plecs_const_var_redeclare - }, - { - "const_var_scoped", - Plecs_const_var_scoped - }, - { - "assign_component_from_var", - Plecs_assign_component_from_var - }, - { - "assign_component_from_var_in_scope", - Plecs_assign_component_from_var_in_scope - }, - { - "scope_w_component_after_const_var", - Plecs_scope_w_component_after_const_var - }, - { - "component_after_const_paren_expr", - Plecs_component_after_const_paren_expr - }, - { - "component_after_const_add_expr", - Plecs_component_after_const_add_expr - }, - { - "component_after_const_sub_expr", - Plecs_component_after_const_sub_expr - }, - { - "component_after_const_mul_expr", - Plecs_component_after_const_mul_expr - }, - { - "component_after_const_div_expr", - Plecs_component_after_const_div_expr - }, - { - "parse_with", - Plecs_parse_with - }, - { - "parse_with_w_with", - Plecs_parse_with_w_with - }, - { - "parse_with_w_tag", - Plecs_parse_with_w_tag - }, - { - "parse_with_value", - Plecs_parse_with_value - }, - { - "parse_with_2_values", - Plecs_parse_with_2_values - }, - { - "parse_with_2_nested_values", - Plecs_parse_with_2_nested_values - }, - { - "parse_with_var", - Plecs_parse_with_var - }, - { - "parse_with_2_vars", - Plecs_parse_with_2_vars - }, - { - "parse_with_2_nested_vars", - Plecs_parse_with_2_nested_vars - }, - { - "parse_with_var_in_scope", - Plecs_parse_with_var_in_scope - }, - { - "assign_const_w_expr", - Plecs_assign_const_w_expr - }, - { - "const_w_type", - Plecs_const_w_type - }, - { - "assembly_no_scope", - Plecs_assembly_no_scope - }, - { - "assembly_empty", - Plecs_assembly_empty - }, - { - "assembly_no_props", - Plecs_assembly_no_props - }, - { - "assembly_prop_no_type", - Plecs_assembly_prop_no_type - }, - { - "assembly_prop_no_default", - Plecs_assembly_prop_no_default - }, - { - "assembly_prop", - Plecs_assembly_prop - }, - { - "assembly_prop_space_colon", - Plecs_assembly_prop_space_colon - }, - { - "assembly_2_props", - Plecs_assembly_2_props - }, - { - "assembly_instance_w_default_values", - Plecs_assembly_instance_w_default_values - }, - { - "assembly_instance_w_assign_default_values", - Plecs_assembly_instance_w_assign_default_values - }, - { - "assembly_instance_w_overridden_values", - Plecs_assembly_instance_w_overridden_values - }, - { - "assembly_w_child", - Plecs_assembly_w_child - }, - { - "assembly_w_child_parse_script", - Plecs_assembly_w_child_parse_script - }, - { - "assembly_w_child_parse_script_twice", - Plecs_assembly_w_child_parse_script_twice - }, - { - "assembly_w_child_update_after_parse", - Plecs_assembly_w_child_update_after_parse - }, - { - "assembly_w_nested_child", - Plecs_assembly_w_nested_child - }, - { - "assembly_w_prefab", - Plecs_assembly_w_prefab - }, - { - "assembly_w_prefab_tree", - Plecs_assembly_w_prefab_tree - }, - { - "assembly_w_nested_assembly", - Plecs_assembly_w_nested_assembly - }, - { - "instantiate_prefab_w_assembly", - Plecs_instantiate_prefab_w_assembly - }, - { - "assembly_w_prefab_w_assembly", - Plecs_assembly_w_prefab_w_assembly - }, - { - "3_assemblies", - Plecs_3_assemblies - }, - { - "assembly_nested_w_default_var", - Plecs_assembly_nested_w_default_var - }, - { - "assembly_w_anonymous", - Plecs_assembly_w_anonymous - }, - { - "assembly_w_anonymous_parse_again", - Plecs_assembly_w_anonymous_parse_again - }, - { - "typed_const_w_composite_type_invalid_assignment", - Plecs_typed_const_w_composite_type_invalid_assignment - }, - { - "typed_const_w_composite_type", - Plecs_typed_const_w_composite_type - }, - { - "assign_var_to_typed_const_w_composite_type", - Plecs_assign_var_to_typed_const_w_composite_type - }, - { - "typed_const_w_composite_type_invalid_assignment", - Plecs_typed_const_w_composite_type_invalid_assignment - }, - { - "assembly_w_composite_prop_invalid_assignment", - Plecs_assembly_w_composite_prop_invalid_assignment - }, - { - "assembly_w_composite_prop", - Plecs_assembly_w_composite_prop - }, - { - "assembly_with_with", - Plecs_assembly_with_with - }, - { - "using_wildcard", - Plecs_using_wildcard - }, - { - "single_line_comment_in_value", - Plecs_single_line_comment_in_value - }, - { - "single_line_comment_in_value_after_scope", - Plecs_single_line_comment_in_value_after_scope - }, - { - "multi_line_comment_in_value", - Plecs_multi_line_comment_in_value - }, - { - "multi_line_comment_in_value_after_scope", - Plecs_multi_line_comment_in_value_after_scope - }, - { - "unterminated_multi_line_comment_in_value", - Plecs_unterminated_multi_line_comment_in_value - }, - { - "module_stmt", - Plecs_module_stmt - }, - { - "nested_module_stmt", - Plecs_nested_module_stmt - }, - { - "module_stmt_w_scope", - Plecs_module_stmt_w_scope - }, - { - "module_stmt_w_nested_scope", - Plecs_module_stmt_w_nested_scope - }, - { - "module_w_assembly", - Plecs_module_w_assembly - }, - { - "module_w_nested_assembly", - Plecs_module_w_nested_assembly - }, - { - "assign_singleton_tag", - Plecs_assign_singleton_tag - }, - { - "assign_singleton_component", - Plecs_assign_singleton_component - }, - { - "assign_singleton_tag_w_scope", - Plecs_assign_singleton_tag_w_scope - }, - { - "assign_singleton_2_tags_w_scope", - Plecs_assign_singleton_2_tags_w_scope - }, - { - "assign_singleton_component_w_scope", - Plecs_assign_singleton_component_w_scope - }, - { - "assign_singleton_2_components_w_scope", - Plecs_assign_singleton_2_components_w_scope - }, - { - "with_pair_in_scope", - Plecs_with_pair_in_scope - }, - { - "assembly_redeclare_prop_as_const", - Plecs_assembly_redeclare_prop_as_const - }, - { - "assembly_redeclare_prop_as_prop", - Plecs_assembly_redeclare_prop_as_prop - }, - { - "assembly_redeclare_const_as_const", - Plecs_assembly_redeclare_const_as_const - }, - { - "add_auto_override", - Plecs_add_auto_override - }, - { - "add_auto_override_pair", - Plecs_add_auto_override_pair - }, - { - "scope_w_auto_override", - Plecs_scope_w_auto_override - }, - { - "scope_w_auto_override_pair", - Plecs_scope_w_auto_override_pair - }, - { - "pair_w_rel_var", - Plecs_pair_w_rel_var - }, - { - "pair_w_tgt_var", - Plecs_pair_w_tgt_var - }, - { - "assembly_w_pair_w_this_var", - Plecs_assembly_w_pair_w_this_var - }, - { - "with_value_not_a_component", - Plecs_with_value_not_a_component - }, - { - "component_in_with_scope", - Plecs_component_in_with_scope - }, - { - "component_in_with_scope_nested", - Plecs_component_in_with_scope_nested - }, - { - "component_in_with_scope_in_scope", - Plecs_component_in_with_scope_in_scope - }, - { - "assign_after_with_in_scope", - Plecs_assign_after_with_in_scope - }, - { - "array_component", - Plecs_array_component - } -}; - -bake_test_case Doc_testcases[] = { - { - "get_set_name", - Doc_get_set_name - }, - { - "get_entity_name", - Doc_get_entity_name - }, - { - "get_set_brief", - Doc_get_set_brief - }, - { - "get_set_detail", - Doc_get_set_detail - }, - { - "get_set_link", - Doc_get_set_link - }, - { - "set_name_nullptr", - Doc_set_name_nullptr - }, - { - "set_brief_nullptr", - Doc_set_brief_nullptr - }, - { - "set_detail_nullptr", - Doc_set_detail_nullptr - }, - { - "set_link_nullptr", - Doc_set_link_nullptr - }, - { - "set_color_nullptr", - Doc_set_color_nullptr - } -}; - -bake_test_case Pipeline_testcases[] = { - { - "system_order_same_phase", - Pipeline_system_order_same_phase - }, - { - "system_order_same_phase_after_disable", - Pipeline_system_order_same_phase_after_disable - }, - { - "system_order_same_phase_after_activate", - Pipeline_system_order_same_phase_after_activate - }, - { - "system_order_different_phase", - Pipeline_system_order_different_phase - }, - { - "system_order_different_phase_after_disable", - Pipeline_system_order_different_phase_after_disable - }, - { - "system_order_different_phase_after_activate", - Pipeline_system_order_different_phase_after_activate - }, - { - "system_order_after_new_system_lower_id", - Pipeline_system_order_after_new_system_lower_id - }, - { - "system_order_after_new_system_inbetween_id", - Pipeline_system_order_after_new_system_inbetween_id - }, - { - "system_order_after_new_system_higher_id", - Pipeline_system_order_after_new_system_higher_id - }, - { - "system_reverse_order_by_phase_custom_pipeline", - Pipeline_system_reverse_order_by_phase_custom_pipeline - }, - { - "merge_after_staged_out", - Pipeline_merge_after_staged_out - }, - { - "merge_after_not_out", - Pipeline_merge_after_not_out - }, - { - "no_merge_after_main_out", - Pipeline_no_merge_after_main_out - }, - { - "merge_after_staged_in_out", - Pipeline_merge_after_staged_in_out - }, - { - "merge_after_staged_inout_main_implicit_inout", - Pipeline_merge_after_staged_inout_main_implicit_inout - }, - { - "merge_after_staged_inout_main_inout", - Pipeline_merge_after_staged_inout_main_inout - }, - { - "merge_after_staged_out_before_owned", - Pipeline_merge_after_staged_out_before_owned - }, - { - "switch_pipeline", - Pipeline_switch_pipeline - }, - { - "run_pipeline", - Pipeline_run_pipeline - }, - { - "get_pipeline_from_stage", - Pipeline_get_pipeline_from_stage - }, - { - "3_systems_3_types", - Pipeline_3_systems_3_types - }, - { - "random_read_after_random_write_out_in", - Pipeline_random_read_after_random_write_out_in - }, - { - "random_read_after_random_write_inout_in", - Pipeline_random_read_after_random_write_inout_in - }, - { - "random_read_after_random_write_out_inout", - Pipeline_random_read_after_random_write_out_inout - }, - { - "random_read_after_random_write_inout_inout", - Pipeline_random_read_after_random_write_inout_inout - }, - { - "random_read_after_random_write_w_not_write", - Pipeline_random_read_after_random_write_w_not_write - }, - { - "random_read_after_random_write_w_not_read", - Pipeline_random_read_after_random_write_w_not_read - }, - { - "random_read_after_random_write_w_wildcard", - Pipeline_random_read_after_random_write_w_wildcard - }, - { - "random_in_after_random_inout_after_random_out", - Pipeline_random_in_after_random_inout_after_random_out - }, - { - "stage_write_before_read", - Pipeline_stage_write_before_read - }, - { - "mixed_multithreaded", - Pipeline_mixed_multithreaded - }, - { - "mixed_multithreaded_tasks", - Pipeline_mixed_multithreaded_tasks - }, - { - "mixed_staging", - Pipeline_mixed_staging - }, - { - "no_staging_system_create_query", - Pipeline_no_staging_system_create_query - }, - { - "single_threaded_pipeline_change", - Pipeline_single_threaded_pipeline_change - }, - { - "multi_threaded_pipeline_change", - Pipeline_multi_threaded_pipeline_change - }, - { - "multi_threaded_pipeline_change_tasks", - Pipeline_multi_threaded_pipeline_change_tasks - }, - { - "activate_after_add", - Pipeline_activate_after_add - }, - { - "match_all_after_pipeline_rebuild", - Pipeline_match_all_after_pipeline_rebuild - }, - { - "empty_pipeline", - Pipeline_empty_pipeline - }, - { - "custom_pipeline_w_system_macro", - Pipeline_custom_pipeline_w_system_macro - }, - { - "pipeline_w_short_notation", - Pipeline_pipeline_w_short_notation - }, - { - "stack_allocator_after_progress", - Pipeline_stack_allocator_after_progress - }, - { - "stack_allocator_after_progress_w_pipeline_change", - Pipeline_stack_allocator_after_progress_w_pipeline_change - }, - { - "iter_from_world_in_singlethread_system_multitead_app", - Pipeline_iter_from_world_in_singlethread_system_multitead_app - }, - { - "iter_from_world_in_singlethread_system_multitead_app_tasks", - Pipeline_iter_from_world_in_singlethread_system_multitead_app_tasks - }, - { - "no_staging_after_inactive_system", - Pipeline_no_staging_after_inactive_system - }, - { - "inactive_system_after_no_staging_system_no_defer_w_filter", - Pipeline_inactive_system_after_no_staging_system_no_defer_w_filter - }, - { - "inactive_system_after_2_no_staging_system_no_defer_w_filter", - Pipeline_inactive_system_after_2_no_staging_system_no_defer_w_filter - }, - { - "inactive_system_after_no_staging_system_no_defer_w_filter_w_no_staging_at_end", - Pipeline_inactive_system_after_no_staging_system_no_defer_w_filter_w_no_staging_at_end - }, - { - "inactive_multithread_system_after_no_staging_system_no_defer", - Pipeline_inactive_multithread_system_after_no_staging_system_no_defer - }, - { - "inactive_multithread_tasks_system_after_no_staging_system_no_defer", - Pipeline_inactive_multithread_tasks_system_after_no_staging_system_no_defer - }, - { - "inactive_multithread_system_after_no_staging_system_no_defer_w_no_staging_at_end", - Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_w_no_staging_at_end - }, - { - "inactive_multithread_tasks_system_after_no_staging_system_no_defer_w_no_staging_at_end", - Pipeline_inactive_multithread_tasks_system_after_no_staging_system_no_defer_w_no_staging_at_end - }, - { - "multi_threaded_pipeline_change_w_only_singlethreaded", - Pipeline_multi_threaded_pipeline_change_w_only_singlethreaded - }, - { - "multi_threaded_tasks_pipeline_change_w_only_singlethreaded", - Pipeline_multi_threaded_tasks_pipeline_change_w_only_singlethreaded - }, - { - "sync_after_not_out_for_out", - Pipeline_sync_after_not_out_for_out - }, - { - "pair_wildcard_read_after_staged_write", - Pipeline_pair_wildcard_read_after_staged_write - }, - { - "pair_read_after_staged_wildcard_write", - Pipeline_pair_read_after_staged_wildcard_write - }, - { - "no_sync_after_pair_wildcard_read_after_unmatching_staged_write", - Pipeline_no_sync_after_pair_wildcard_read_after_unmatching_staged_write - }, - { - "no_merge_after_from_nothing_w_default_inout", - Pipeline_no_merge_after_from_nothing_w_default_inout - }, - { - "on_merge_activate_system_before_merge", - Pipeline_on_merge_activate_system_before_merge - }, - { - "disable_phase", - Pipeline_disable_phase - }, - { - "disable_parent", - Pipeline_disable_parent - }, - { - "multi_threaded_no_staging_w_add_after_read", - Pipeline_multi_threaded_no_staging_w_add_after_read - }, - { - "multi_threaded_tasks_no_staging_w_add_after_read", - Pipeline_multi_threaded_tasks_no_staging_w_add_after_read - }, - { - "1_startup_system", - Pipeline_1_startup_system - }, - { - "2_startup_systems", - Pipeline_2_startup_systems - }, - { - "2_startup_phases", - Pipeline_2_startup_phases - }, - { - "2_startup_systems_w_merge", - Pipeline_2_startup_systems_w_merge - }, - { - "inactive_last_system_merge_count", - Pipeline_inactive_last_system_merge_count - }, - { - "inactive_middle_system_merge_count", - Pipeline_inactive_middle_system_merge_count - }, - { - "last_no_readonly_system_merge_count", - Pipeline_last_no_readonly_system_merge_count - }, - { - "2_pipelines_1_system", - Pipeline_2_pipelines_1_system - }, - { - "builtin_pipeline_w_self_system_term", - Pipeline_builtin_pipeline_w_self_system_term - }, - { - "custom_pipeline_w_self_system_term", - Pipeline_custom_pipeline_w_self_system_term - }, - { - "switch_from_threads_to_tasks", - Pipeline_switch_from_threads_to_tasks - }, - { - "switch_from_tasks_to_threads", - Pipeline_switch_from_tasks_to_threads - }, - { - "run_pipeline_multithreaded", - Pipeline_run_pipeline_multithreaded - }, - { - "run_pipeline_multithreaded_tasks", - Pipeline_run_pipeline_multithreaded_tasks - }, - { - "pipeline_init_no_terms", - Pipeline_pipeline_init_no_terms - }, - { - "pipeline_init_no_system_term", - Pipeline_pipeline_init_no_system_term - } -}; - -bake_test_case SystemMisc_testcases[] = { - { - "invalid_not_without_id", - SystemMisc_invalid_not_without_id - }, - { - "invalid_optional_without_id", - SystemMisc_invalid_optional_without_id - }, - { - "invalid_entity_without_id", - SystemMisc_invalid_entity_without_id - }, - { - "invalid_empty_without_id", - SystemMisc_invalid_empty_without_id - }, - { - "invalid_empty_element", - SystemMisc_invalid_empty_element - }, - { - "invalid_empty_element_w_space", - SystemMisc_invalid_empty_element_w_space - }, - { - "invalid_empty_or", - SystemMisc_invalid_empty_or - }, - { - "invalid_empty_or_w_space", - SystemMisc_invalid_empty_or_w_space - }, - { - "invalid_or_w_not", - SystemMisc_invalid_or_w_not - }, - { - "invalid_not_w_or", - SystemMisc_invalid_not_w_or - }, - { - "invalid_0_w_and", - SystemMisc_invalid_0_w_and - }, - { - "invalid_0_w_from_entity", - SystemMisc_invalid_0_w_from_entity - }, - { - "invalid_component_id", - SystemMisc_invalid_component_id - }, - { - "invalid_entity_id", - SystemMisc_invalid_entity_id - }, - { - "invalid_null_string", - SystemMisc_invalid_null_string - }, - { - "invalid_empty_string", - SystemMisc_invalid_empty_string - }, - { - "invalid_empty_string_w_space", - SystemMisc_invalid_empty_string_w_space - }, - { - "redefine_row_system", - SystemMisc_redefine_row_system - }, - { - "system_w_or_prefab", - SystemMisc_system_w_or_prefab - }, - { - "system_w_or_disabled", - SystemMisc_system_w_or_disabled - }, - { - "system_w_or_disabled_and_prefab", - SystemMisc_system_w_or_disabled_and_prefab - }, - { - "table_columns_access", - SystemMisc_table_columns_access - }, - { - "dont_enable_after_rematch", - SystemMisc_dont_enable_after_rematch - }, - { - "ensure_single_merge", - SystemMisc_ensure_single_merge - }, - { - "table_count", - SystemMisc_table_count - }, - { - "match_system", - SystemMisc_match_system - }, - { - "system_initial_state", - SystemMisc_system_initial_state - }, - { - "add_own_component", - SystemMisc_add_own_component - }, - { - "change_system_action", - SystemMisc_change_system_action - }, - { - "system_readeactivate", - SystemMisc_system_readeactivate - }, - { - "system_readeactivate_w_2_systems", - SystemMisc_system_readeactivate_w_2_systems - }, - { - "add_to_system_in_progress", - SystemMisc_add_to_system_in_progress - }, - { - "redefine_null_signature", - SystemMisc_redefine_null_signature - }, - { - "redefine_0_signature", - SystemMisc_redefine_0_signature - }, - { - "one_named_column_of_two", - SystemMisc_one_named_column_of_two - }, - { - "two_named_columns_of_two", - SystemMisc_two_named_columns_of_two - }, - { - "redeclare_system_explicit_id", - SystemMisc_redeclare_system_explicit_id - }, - { - "redeclare_system_explicit_id_null_expr", - SystemMisc_redeclare_system_explicit_id_null_expr - }, - { - "redeclare_system_explicit_id_no_name", - SystemMisc_redeclare_system_explicit_id_no_name - }, - { - "declare_different_id_same_name", - SystemMisc_declare_different_id_same_name - }, - { - "declare_different_id_same_name_w_scope", - SystemMisc_declare_different_id_same_name_w_scope - }, - { - "rw_in_implicit_any", - SystemMisc_rw_in_implicit_any - }, - { - "rw_in_implicit_shared", - SystemMisc_rw_in_implicit_shared - }, - { - "rw_in_implicit_from_empty", - SystemMisc_rw_in_implicit_from_empty - }, - { - "rw_in_implicit_from_entity", - SystemMisc_rw_in_implicit_from_entity - }, - { - "rw_out_explicit_any", - SystemMisc_rw_out_explicit_any - }, - { - "rw_out_explicit_shared", - SystemMisc_rw_out_explicit_shared - }, - { - "rw_out_explicit_from_empty", - SystemMisc_rw_out_explicit_from_empty - }, - { - "rw_out_explicit_from_entity", - SystemMisc_rw_out_explicit_from_entity - }, - { - "activate_system_for_table_w_n_pairs", - SystemMisc_activate_system_for_table_w_n_pairs - }, - { - "get_query", - SystemMisc_get_query - }, - { - "set_get_context", - SystemMisc_set_get_context - }, - { - "set_get_binding_context", - SystemMisc_set_get_binding_context - }, - { - "deactivate_after_disable", - SystemMisc_deactivate_after_disable - }, - { - "delete_system", - SystemMisc_delete_system - }, - { - "delete_pipeline_system", - SystemMisc_delete_pipeline_system - }, - { - "delete_system_w_ctx", - SystemMisc_delete_system_w_ctx - }, - { - "update_ctx", - SystemMisc_update_ctx - }, - { - "run_custom_run_action", - SystemMisc_run_custom_run_action - }, - { - "run_w_offset_limit_custom_run_action", - SystemMisc_run_w_offset_limit_custom_run_action - }, - { - "pipeline_custom_run_action", - SystemMisc_pipeline_custom_run_action - }, - { - "change_custom_run_action", - SystemMisc_change_custom_run_action - }, - { - "custom_run_action_call_next", - SystemMisc_custom_run_action_call_next - }, - { - "system_w_short_notation", - SystemMisc_system_w_short_notation - }, - { - "update_interval_w_system_init", - SystemMisc_update_interval_w_system_init - }, - { - "update_rate_w_system_init", - SystemMisc_update_rate_w_system_init - }, - { - "system_w_interval_rate_stop_timer", - SystemMisc_system_w_interval_rate_stop_timer - }, - { - "system_same_interval_same_tick", - SystemMisc_system_same_interval_same_tick - } -}; - -bake_test_case RulesBasic_testcases[] = { - { - "1_fact_w_tag", - RulesBasic_1_fact_w_tag - }, - { - "1_fact_w_component", - RulesBasic_1_fact_w_component - }, - { - "1_fact_w_tag_pair", - RulesBasic_1_fact_w_tag_pair - }, - { - "1_fact_w_component_pair", - RulesBasic_1_fact_w_component_pair - }, - { - "2_facts_same_src_w_tag", - RulesBasic_2_facts_same_src_w_tag - }, - { - "2_facts_same_src_w_component", - RulesBasic_2_facts_same_src_w_component - }, - { - "2_facts_same_src_w_tag_pair", - RulesBasic_2_facts_same_src_w_tag_pair - }, - { - "2_facts_same_src_w_component_pair", - RulesBasic_2_facts_same_src_w_component_pair - }, - { - "2_facts_other_src_w_tag", - RulesBasic_2_facts_other_src_w_tag - }, - { - "2_facts_other_src_w_component", - RulesBasic_2_facts_other_src_w_component - }, - { - "2_facts_other_src_w_tag_pair", - RulesBasic_2_facts_other_src_w_tag_pair - }, - { - "2_facts_other_src_w_component_pair", - RulesBasic_2_facts_other_src_w_component_pair - }, - { - "1_fact_w_any", - RulesBasic_1_fact_w_any - }, - { - "1_fact_w_pair_any_tgt", - RulesBasic_1_fact_w_pair_any_tgt - }, - { - "1_fact_w_pair_any_rel", - RulesBasic_1_fact_w_pair_any_rel - }, - { - "1_fact_w_pair_any_rel_tgt", - RulesBasic_1_fact_w_pair_any_rel_tgt - }, - { - "2_facts_same_src_w_any", - RulesBasic_2_facts_same_src_w_any - }, - { - "2_facts_same_src_w_pair_any_tgt", - RulesBasic_2_facts_same_src_w_pair_any_tgt - }, - { - "2_facts_same_src_w_pair_any_rel", - RulesBasic_2_facts_same_src_w_pair_any_rel - }, - { - "2_facts_same_src_w_pair_any_rel_tgt", - RulesBasic_2_facts_same_src_w_pair_any_rel_tgt - }, - { - "2_facts_other_src_w_any", - RulesBasic_2_facts_other_src_w_any - }, - { - "2_facts_other_src_w_pair_any_tgt", - RulesBasic_2_facts_other_src_w_pair_any_tgt - }, - { - "2_facts_other_src_w_pair_any_rel", - RulesBasic_2_facts_other_src_w_pair_any_rel - }, - { - "2_facts_other_src_w_pair_any_rel_tgt", - RulesBasic_2_facts_other_src_w_pair_any_rel_tgt - }, - { - "1_this_src_w_tag", - RulesBasic_1_this_src_w_tag - }, - { - "1_this_src_w_component", - RulesBasic_1_this_src_w_component - }, - { - "1_this_src_w_tag_pair", - RulesBasic_1_this_src_w_tag_pair - }, - { - "1_this_src_w_component_pair", - RulesBasic_1_this_src_w_component_pair - }, - { - "1_this_src_w_tag_2_tables", - RulesBasic_1_this_src_w_tag_2_tables - }, - { - "1_this_src_w_component_2_tables", - RulesBasic_1_this_src_w_component_2_tables - }, - { - "1_this_src_w_tag_pair_2_tables", - RulesBasic_1_this_src_w_tag_pair_2_tables - }, - { - "1_this_src_w_component_pair_2_tables", - RulesBasic_1_this_src_w_component_pair_2_tables - }, - { - "2_this_src_w_tag", - RulesBasic_2_this_src_w_tag - }, - { - "2_this_src_w_component", - RulesBasic_2_this_src_w_component - }, - { - "2_this_src_ent_src_w_tag", - RulesBasic_2_this_src_ent_src_w_tag - }, - { - "2_this_src_ent_src_w_component", - RulesBasic_2_this_src_ent_src_w_component - }, - { - "2_ent_src_this_src_w_tag", - RulesBasic_2_ent_src_this_src_w_tag - }, - { - "2_ent_src_this_src_w_component", - RulesBasic_2_ent_src_this_src_w_component - }, - { - "recycled_tag", - RulesBasic_recycled_tag - }, - { - "recycled_src", - RulesBasic_recycled_src - }, - { - "recycled_pair_rel", - RulesBasic_recycled_pair_rel - }, - { - "recycled_pair_tgt", - RulesBasic_recycled_pair_tgt - }, - { - "this_src_w_wildcard", - RulesBasic_this_src_w_wildcard - }, - { - "this_src_w_pair_rel_wildcard", - RulesBasic_this_src_w_pair_rel_wildcard - }, - { - "this_src_w_pair_tgt_wildcard", - RulesBasic_this_src_w_pair_tgt_wildcard - }, - { - "this_src_w_pair_rel_tgt_wildcard", - RulesBasic_this_src_w_pair_rel_tgt_wildcard - }, - { - "this_src_w_any", - RulesBasic_this_src_w_any - }, - { - "this_src_w_any_written", - RulesBasic_this_src_w_any_written - }, - { - "this_src_w_pair_rel_any", - RulesBasic_this_src_w_pair_rel_any - }, - { - "this_src_w_pair_tgt_any", - RulesBasic_this_src_w_pair_tgt_any - }, - { - "this_src_w_pair_rel_tgt_any", - RulesBasic_this_src_w_pair_rel_tgt_any - }, - { - "ent_src_w_wildcard", - RulesBasic_ent_src_w_wildcard - }, - { - "ent_src_w_pair_rel_wildcard", - RulesBasic_ent_src_w_pair_rel_wildcard - }, - { - "ent_src_w_pair_tgt_wildcard", - RulesBasic_ent_src_w_pair_tgt_wildcard - }, - { - "ent_src_w_pair_rel_tgt_wildcard", - RulesBasic_ent_src_w_pair_rel_tgt_wildcard - }, - { - "1_wildcard_src", - RulesBasic_1_wildcard_src - }, - { - "1_wildcard_src_w_pair", - RulesBasic_1_wildcard_src_w_pair - }, - { - "2_wildcard_src", - RulesBasic_2_wildcard_src - }, - { - "2_wildcard_src_w_pair", - RulesBasic_2_wildcard_src_w_pair - }, - { - "1_wildcard_src_w_pair_tgt_var", - RulesBasic_1_wildcard_src_w_pair_tgt_var - }, - { - "1_wildcard_src_w_pair_rel_var", - RulesBasic_1_wildcard_src_w_pair_rel_var - }, - { - "1_wildcard_src_w_pair_tgt_this", - RulesBasic_1_wildcard_src_w_pair_tgt_this - }, - { - "1_wildcard_src_w_pair_rel_this", - RulesBasic_1_wildcard_src_w_pair_rel_this - }, - { - "1_any_src", - RulesBasic_1_any_src - }, - { - "1_any_src_w_pair", - RulesBasic_1_any_src_w_pair - }, - { - "2_any_src", - RulesBasic_2_any_src - }, - { - "2_any_src_w_pair", - RulesBasic_2_any_src_w_pair - }, - { - "1_any_src_w_pair_tgt_var", - RulesBasic_1_any_src_w_pair_tgt_var - }, - { - "1_any_src_w_pair_rel_var", - RulesBasic_1_any_src_w_pair_rel_var - }, - { - "1_any_src_w_pair_tgt_this", - RulesBasic_1_any_src_w_pair_tgt_this - }, - { - "1_any_src_w_pair_rel_this", - RulesBasic_1_any_src_w_pair_rel_this - }, - { - "not_any", - RulesBasic_not_any - }, - { - "rule_w_iter_next", - RulesBasic_rule_w_iter_next - }, - { - "empty_rule", - RulesBasic_empty_rule - }, - { - "invalid_rule", - RulesBasic_invalid_rule - }, - { - "not_instanced_table_src", - RulesBasic_not_instanced_table_src - }, - { - "not_instanced_entity_src", - RulesBasic_not_instanced_entity_src - }, - { - "not_instanced_mixed_src", - RulesBasic_not_instanced_mixed_src - }, - { - "instanced_table_src", - RulesBasic_instanced_table_src - }, - { - "instanced_entity_src", - RulesBasic_instanced_entity_src - }, - { - "instanced_mixed_src", - RulesBasic_instanced_mixed_src - }, - { - "in_term", - RulesBasic_in_term - }, - { - "out_term", - RulesBasic_out_term - }, - { - "inout_term", - RulesBasic_inout_term - }, - { - "nodata_term", - RulesBasic_nodata_term - }, - { - "find_this_lowercase", - RulesBasic_find_this_lowercase - }, - { - "find_this_uppercase", - RulesBasic_find_this_uppercase - }, - { - "find_this_tgt_lowercase", - RulesBasic_find_this_tgt_lowercase - }, - { - "find_this_tgt_uppercase", - RulesBasic_find_this_tgt_uppercase - }, - { - "get_filter", - RulesBasic_get_filter - }, - { - "iter_empty_source", - RulesBasic_iter_empty_source - }, - { - "iter_empty_source_2_terms", - RulesBasic_iter_empty_source_2_terms - }, - { - "iter_empty_source_wildcard", - RulesBasic_iter_empty_source_wildcard - }, - { - "iter_empty_source_pair", - RulesBasic_iter_empty_source_pair - }, - { - "iter_empty_source_pair_wildcard", - RulesBasic_iter_empty_source_pair_wildcard - }, - { - "iter_empty_source_2_terms_pair", - RulesBasic_iter_empty_source_2_terms_pair - }, - { - "iter_empty_source_2_terms_mixed", - RulesBasic_iter_empty_source_2_terms_mixed - }, - { - "iter_empty_source_2_terms_mixed_pair", - RulesBasic_iter_empty_source_2_terms_mixed_pair - }, - { - "iter_empty_source_2_terms_mixed_pair_wildcard", - RulesBasic_iter_empty_source_2_terms_mixed_pair_wildcard - }, - { - "this_var_w_empty_entity", - RulesBasic_this_var_w_empty_entity - }, - { - "match_disabled", - RulesBasic_match_disabled - }, - { - "match_prefab", - RulesBasic_match_prefab - }, - { - "match_disabled_prefab", - RulesBasic_match_disabled_prefab - }, - { - "match_disabled_this_tgt", - RulesBasic_match_disabled_this_tgt - }, - { - "match_prefab_this_tgt", - RulesBasic_match_prefab_this_tgt - }, - { - "match_disabled_prefab_this_tgt", - RulesBasic_match_disabled_prefab_this_tgt - }, - { - "match_self_disabled", - RulesBasic_match_self_disabled - }, - { - "match_self_prefab", - RulesBasic_match_self_prefab - }, - { - "match_self_disabled_prefab", - RulesBasic_match_self_disabled_prefab - }, - { - "inout_none_first_term", - RulesBasic_inout_none_first_term - }, - { - "inout_none_second_term", - RulesBasic_inout_none_second_term - }, - { - "no_data_rule", - RulesBasic_no_data_rule - }, - { - "frame_offset", - RulesBasic_frame_offset - }, - { - "frame_offset_no_data", - RulesBasic_frame_offset_no_data - }, - { - "match_empty_tables", - RulesBasic_match_empty_tables - }, - { - "match_empty_tables_no_data", - RulesBasic_match_empty_tables_no_data - }, - { - "match_empty_tables_w_not", - RulesBasic_match_empty_tables_w_not - }, - { - "match_empty_tables_w_wildcard", - RulesBasic_match_empty_tables_w_wildcard - }, - { - "match_empty_tables_w_no_empty_tables", - RulesBasic_match_empty_tables_w_no_empty_tables - }, - { - "match_empty_tables_trivial", - RulesBasic_match_empty_tables_trivial - }, - { - "oneof_wildcard", - RulesBasic_oneof_wildcard - }, - { - "oneof_any", - RulesBasic_oneof_any - }, - { - "instanced_w_singleton", - RulesBasic_instanced_w_singleton - }, - { - "instanced_w_base", - RulesBasic_instanced_w_base - }, - { - "not_instanced_w_singleton", - RulesBasic_not_instanced_w_singleton - }, - { - "not_instanced_w_base", - RulesBasic_not_instanced_w_base - }, - { - "unknown_before_known", - RulesBasic_unknown_before_known - }, - { - "unknown_before_known_after_or", - RulesBasic_unknown_before_known_after_or - }, - { - "unknown_before_known_after_not", - RulesBasic_unknown_before_known_after_not - }, - { - "unknown_before_known_after_optional", - RulesBasic_unknown_before_known_after_optional - }, - { - "unknown_before_known_after_scope", - RulesBasic_unknown_before_known_after_scope - }, - { - "reordered_plan_1", - RulesBasic_reordered_plan_1 - }, - { - "reordered_plan_2", - RulesBasic_reordered_plan_2 - }, - { - "1_trivial_plan", - RulesBasic_1_trivial_plan - }, - { - "2_trivial_plan", - RulesBasic_2_trivial_plan - }, - { - "1_trivial_plan_component", - RulesBasic_1_trivial_plan_component - }, - { - "2_trivial_plan_component", - RulesBasic_2_trivial_plan_component - }, - { - "3_trivial_plan_w_pair", - RulesBasic_3_trivial_plan_w_pair - }, - { - "3_trivial_plan_w_wildcard", - RulesBasic_3_trivial_plan_w_wildcard - }, - { - "3_trivial_plan_w_any", - RulesBasic_3_trivial_plan_w_any - }, - { - "3_trivial_plan_w_pair_component", - RulesBasic_3_trivial_plan_w_pair_component - }, - { - "3_trivial_plan_w_wildcard_component", - RulesBasic_3_trivial_plan_w_wildcard_component - }, - { - "3_trivial_plan_w_any_component", - RulesBasic_3_trivial_plan_w_any_component - }, - { - "1_trivial_component_w_none", - RulesBasic_1_trivial_component_w_none - }, - { - "2_trivial_component_w_none", - RulesBasic_2_trivial_component_w_none - } -}; - -bake_test_case RulesVariables_testcases[] = { - { - "1_ent_src_w_var", - RulesVariables_1_ent_src_w_var - }, - { - "1_ent_src_w_pair_rel_var", - RulesVariables_1_ent_src_w_pair_rel_var - }, - { - "1_ent_src_w_pair_tgt_var", - RulesVariables_1_ent_src_w_pair_tgt_var - }, - { - "1_ent_src_w_pair_rel_tgt_var", - RulesVariables_1_ent_src_w_pair_rel_tgt_var - }, - { - "1_ent_src_w_pair_rel_tgt_same_var", - RulesVariables_1_ent_src_w_pair_rel_tgt_same_var - }, - { - "1_ent_src_w_pair_rel_tgt_same_var_after_write", - RulesVariables_1_ent_src_w_pair_rel_tgt_same_var_after_write - }, - { - "1_this_src_w_var", - RulesVariables_1_this_src_w_var - }, - { - "1_this_src_w_pair_rel_var", - RulesVariables_1_this_src_w_pair_rel_var - }, - { - "1_this_src_w_pair_tgt_var", - RulesVariables_1_this_src_w_pair_tgt_var - }, - { - "1_this_src_w_pair_rel_tgt_var", - RulesVariables_1_this_src_w_pair_rel_tgt_var - }, - { - "1_this_src_w_pair_rel_tgt_same_var", - RulesVariables_1_this_src_w_pair_rel_tgt_same_var - }, - { - "1_this_src_w_pair_rel_tgt_same_var_after_write", - RulesVariables_1_this_src_w_pair_rel_tgt_same_var_after_write - }, - { - "1_src_id_same_var", - RulesVariables_1_src_id_same_var - }, - { - "1_src_pair_first_same_var", - RulesVariables_1_src_pair_first_same_var - }, - { - "1_src_pair_second_same_var", - RulesVariables_1_src_pair_second_same_var - }, - { - "1_src_pair_first_and_second_same_var", - RulesVariables_1_src_pair_first_and_second_same_var - }, - { - "1_src_id_same_var_after_write", - RulesVariables_1_src_id_same_var_after_write - }, - { - "1_src_pair_first_same_var_after_write", - RulesVariables_1_src_pair_first_same_var_after_write - }, - { - "1_src_pair_second_same_var_after_write", - RulesVariables_1_src_pair_second_same_var_after_write - }, - { - "1_src_pair_first_and_second_same_var_after_write", - RulesVariables_1_src_pair_first_and_second_same_var_after_write - }, - { - "1_src_pair_first_same_var_this", - RulesVariables_1_src_pair_first_same_var_this - }, - { - "1_src_pair_second_same_var_this", - RulesVariables_1_src_pair_second_same_var_this - }, - { - "1_src_pair_first_and_second_same_var_this", - RulesVariables_1_src_pair_first_and_second_same_var_this - }, - { - "1_src_id_same_var_this_after_write", - RulesVariables_1_src_id_same_var_this_after_write - }, - { - "1_src_pair_first_same_var_this_after_write", - RulesVariables_1_src_pair_first_same_var_this_after_write - }, - { - "1_src_pair_second_same_var_this_after_write", - RulesVariables_1_src_pair_second_same_var_this_after_write - }, - { - "1_src_pair_first_and_second_same_var_this_after_write", - RulesVariables_1_src_pair_first_and_second_same_var_this_after_write - }, - { - "1_ent_src_w_this_var", - RulesVariables_1_ent_src_w_this_var - }, - { - "1_ent_src_w_pair_this_rel", - RulesVariables_1_ent_src_w_pair_this_rel - }, - { - "1_ent_src_w_pair_this_tgt", - RulesVariables_1_ent_src_w_pair_this_tgt - }, - { - "1_ent_src_w_pair_this_rel_tgt", - RulesVariables_1_ent_src_w_pair_this_rel_tgt - }, - { - "1_this_src_w_this", - RulesVariables_1_this_src_w_this - }, - { - "1_this_src_w_pair_this_rel_tgt", - RulesVariables_1_this_src_w_pair_this_rel_tgt - }, - { - "1_this_src_w_this_after_write", - RulesVariables_1_this_src_w_this_after_write - }, - { - "1_this_src_w_pair_this_rel_tgt_after_write", - RulesVariables_1_this_src_w_pair_this_rel_tgt_after_write - }, - { - "2_constrain_src_from_src", - RulesVariables_2_constrain_src_from_src - }, - { - "2_constrain_rel_from_src_w_ent", - RulesVariables_2_constrain_rel_from_src_w_ent - }, - { - "2_constrain_rel_from_src_w_var", - RulesVariables_2_constrain_rel_from_src_w_var - }, - { - "2_constrain_rel_from_src_w_this", - RulesVariables_2_constrain_rel_from_src_w_this - }, - { - "2_constrain_pair_rel_from_src_w_ent", - RulesVariables_2_constrain_pair_rel_from_src_w_ent - }, - { - "2_constrain_pair_rel_from_src_w_var", - RulesVariables_2_constrain_pair_rel_from_src_w_var - }, - { - "2_constrain_pair_rel_from_src_w_this", - RulesVariables_2_constrain_pair_rel_from_src_w_this - }, - { - "2_constrain_pair_tgt_from_src_w_ent", - RulesVariables_2_constrain_pair_tgt_from_src_w_ent - }, - { - "2_constrain_pair_tgt_from_src_w_var", - RulesVariables_2_constrain_pair_tgt_from_src_w_var - }, - { - "2_constrain_pair_tgt_from_src_w_this", - RulesVariables_2_constrain_pair_tgt_from_src_w_this - }, - { - "2_constrain_pair_rel_tgt_from_src_w_ent", - RulesVariables_2_constrain_pair_rel_tgt_from_src_w_ent - }, - { - "2_constrain_pair_rel_tgt_from_src_w_var", - RulesVariables_2_constrain_pair_rel_tgt_from_src_w_var - }, - { - "2_constrain_pair_rel_tgt_from_src_w_this", - RulesVariables_2_constrain_pair_rel_tgt_from_src_w_this - }, - { - "1_ent_src_set_rel_var", - RulesVariables_1_ent_src_set_rel_var - }, - { - "1_ent_src_set_pair_rel_var", - RulesVariables_1_ent_src_set_pair_rel_var - }, - { - "1_ent_src_set_pair_tgt_var", - RulesVariables_1_ent_src_set_pair_tgt_var - }, - { - "1_set_src_var", - RulesVariables_1_set_src_var - }, - { - "1_set_src_var_w_pair", - RulesVariables_1_set_src_var_w_pair - }, - { - "1_set_src_var_w_pair_set_rel", - RulesVariables_1_set_src_var_w_pair_set_rel - }, - { - "1_set_src_var_w_pair_set_tgt", - RulesVariables_1_set_src_var_w_pair_set_tgt - }, - { - "1_set_src_var_w_pair_set_rel_tgt", - RulesVariables_1_set_src_var_w_pair_set_rel_tgt - }, - { - "1_set_src_this", - RulesVariables_1_set_src_this - }, - { - "1_set_src_this_w_pair", - RulesVariables_1_set_src_this_w_pair - }, - { - "1_set_src_this_w_pair_set_rel", - RulesVariables_1_set_src_this_w_pair_set_rel - }, - { - "1_set_src_this_w_pair_set_tgt", - RulesVariables_1_set_src_this_w_pair_set_tgt - }, - { - "1_set_src_this_w_pair_set_rel_tgt", - RulesVariables_1_set_src_this_w_pair_set_rel_tgt - }, - { - "1_set_src_this_to_empty_table", - RulesVariables_1_set_src_this_to_empty_table - }, - { - "1_set_src_this_to_empty_table_w_component", - RulesVariables_1_set_src_this_to_empty_table_w_component - }, - { - "1_set_src_this_to_empty_table_w_component_self", - RulesVariables_1_set_src_this_to_empty_table_w_component_self - }, - { - "1_set_src_this_to_entiy_in_table", - RulesVariables_1_set_src_this_to_entiy_in_table - }, - { - "1_set_src_this_to_entiy_in_table_self", - RulesVariables_1_set_src_this_to_entiy_in_table_self - }, - { - "2_set_src_this", - RulesVariables_2_set_src_this - }, - { - "2_set_src_this_self", - RulesVariables_2_set_src_this_self - }, - { - "2_set_src_this_component", - RulesVariables_2_set_src_this_component - }, - { - "2_set_src_this_self_component", - RulesVariables_2_set_src_this_self_component - }, - { - "2_set_src_this_w_up", - RulesVariables_2_set_src_this_w_up - }, - { - "2_set_src_this_self_w_up", - RulesVariables_2_set_src_this_self_w_up - }, - { - "2_set_src_this_component_w_up", - RulesVariables_2_set_src_this_component_w_up - }, - { - "2_set_src_this_self_component_w_up", - RulesVariables_2_set_src_this_self_component_w_up - }, - { - "2_set_src_this_w_exclusive_wildcard", - RulesVariables_2_set_src_this_w_exclusive_wildcard - }, - { - "2_set_src_this_self_w_exclusive_wildcard", - RulesVariables_2_set_src_this_self_w_exclusive_wildcard - }, - { - "1_src_this_var_as_entity", - RulesVariables_1_src_this_var_as_entity - }, - { - "1_src_this_var_as_table", - RulesVariables_1_src_this_var_as_table - }, - { - "1_src_this_var_as_table_range", - RulesVariables_1_src_this_var_as_table_range - }, - { - "2_join_by_rel_var", - RulesVariables_2_join_by_rel_var - }, - { - "2_join_by_pair_rel_var", - RulesVariables_2_join_by_pair_rel_var - }, - { - "2_join_by_pair_tgt_var", - RulesVariables_2_join_by_pair_tgt_var - }, - { - "2_cycle_w_var", - RulesVariables_2_cycle_w_var - }, - { - "2_cycle_w_this_var", - RulesVariables_2_cycle_w_this_var - }, - { - "2_cycle_w_var_this", - RulesVariables_2_cycle_w_var_this - }, - { - "2_cycle_pair_w_var", - RulesVariables_2_cycle_pair_w_var - }, - { - "2_cycle_pair_w_this_var_var", - RulesVariables_2_cycle_pair_w_this_var_var - }, - { - "2_cycle_pair_w_var_this_var", - RulesVariables_2_cycle_pair_w_var_this_var - }, - { - "2_cycle_pair_w_var_var_this", - RulesVariables_2_cycle_pair_w_var_var_this - }, - { - "2_cycle_pair_ent_var_var", - RulesVariables_2_cycle_pair_ent_var_var - }, - { - "2_cycle_pair_ent_this_var", - RulesVariables_2_cycle_pair_ent_this_var - }, - { - "2_cycle_pair_ent_var_this", - RulesVariables_2_cycle_pair_ent_var_this - }, - { - "parse_0_var", - RulesVariables_parse_0_var - }, - { - "parse_1_var", - RulesVariables_parse_1_var - }, - { - "parse_2_vars", - RulesVariables_parse_2_vars - }, - { - "parse_0_var_paren", - RulesVariables_parse_0_var_paren - }, - { - "parse_1_var_paren", - RulesVariables_parse_1_var_paren - }, - { - "parse_2_vars_paren", - RulesVariables_parse_2_vars_paren - }, - { - "parse_1_vars_w_path", - RulesVariables_parse_1_vars_w_path - }, - { - "parse_missing_close_paren", - RulesVariables_parse_missing_close_paren - }, - { - "parse_missing_open_paren", - RulesVariables_parse_missing_open_paren - }, - { - "parse_missing_value", - RulesVariables_parse_missing_value - }, - { - "parse_0_var_w_spaces", - RulesVariables_parse_0_var_w_spaces - }, - { - "parse_1_var_w_spaces", - RulesVariables_parse_1_var_w_spaces - }, - { - "parse_2_vars_w_spaces", - RulesVariables_parse_2_vars_w_spaces - }, - { - "parse_0_var_paren_w_spaces", - RulesVariables_parse_0_var_paren_w_spaces - }, - { - "parse_1_var_paren_w_spaces", - RulesVariables_parse_1_var_paren_w_spaces - }, - { - "parse_2_vars_paren_w_spaces", - RulesVariables_parse_2_vars_paren_w_spaces - }, - { - "var_count", - RulesVariables_var_count - }, - { - "var_name", - RulesVariables_var_name - }, - { - "var_is_entity", - RulesVariables_var_is_entity - }, - { - "no_this_anonymous_src", - RulesVariables_no_this_anonymous_src - }, - { - "no_this_anonymous_src_w_pair", - RulesVariables_no_this_anonymous_src_w_pair - }, - { - "no_this_anonymous_component_src", - RulesVariables_no_this_anonymous_component_src - }, - { - "no_this_anonymous_component_src_w_pair", - RulesVariables_no_this_anonymous_component_src_w_pair - }, - { - "lookup_from_table_this", - RulesVariables_lookup_from_table_this - }, - { - "lookup_from_entity_this", - RulesVariables_lookup_from_entity_this - }, - { - "lookup_from_table", - RulesVariables_lookup_from_table - }, - { - "lookup_from_entity", - RulesVariables_lookup_from_entity - }, - { - "lookup_from_not_written", - RulesVariables_lookup_from_not_written - }, - { - "lookup_from_table_this_component", - RulesVariables_lookup_from_table_this_component - }, - { - "lookup_from_entity_this_component", - RulesVariables_lookup_from_entity_this_component - }, - { - "lookup_from_table_component", - RulesVariables_lookup_from_table_component - }, - { - "lookup_from_entity_component", - RulesVariables_lookup_from_entity_component - }, - { - "lookup_from_table_two_children", - RulesVariables_lookup_from_table_two_children - }, - { - "lookup_from_entity_two_children", - RulesVariables_lookup_from_entity_two_children - }, - { - "lookup_from_table_same_child_twice", - RulesVariables_lookup_from_table_same_child_twice - }, - { - "lookup_from_entity_same_child_twice", - RulesVariables_lookup_from_entity_same_child_twice - }, - { - "lookup_from_table_not", - RulesVariables_lookup_from_table_not - }, - { - "lookup_from_entity_not", - RulesVariables_lookup_from_entity_not - }, - { - "lookup_from_table_w_any_component", - RulesVariables_lookup_from_table_w_any_component - }, - { - "lookup_from_entity_w_any_component", - RulesVariables_lookup_from_entity_w_any_component - }, - { - "lookup_as_tag", - RulesVariables_lookup_as_tag - }, - { - "lookup_as_relationship", - RulesVariables_lookup_as_relationship - }, - { - "lookup_as_target", - RulesVariables_lookup_as_target - }, - { - "lookup_assign_var", - RulesVariables_lookup_assign_var - }, - { - "lookup_eq_var", - RulesVariables_lookup_eq_var - }, - { - "lookup_neq_var", - RulesVariables_lookup_neq_var - }, - { - "check_vars_this", - RulesVariables_check_vars_this - }, - { - "check_vars_var", - RulesVariables_check_vars_var - }, - { - "check_vars_wildcard", - RulesVariables_check_vars_wildcard - }, - { - "check_vars_any", - RulesVariables_check_vars_any - }, - { - "check_vars_var_as_tgt", - RulesVariables_check_vars_var_as_tgt - }, - { - "check_vars_this_as_tgt", - RulesVariables_check_vars_this_as_tgt - }, - { - "check_vars_anonymous_var_as_tgt", - RulesVariables_check_vars_anonymous_var_as_tgt - }, - { - "check_vars_wildcard_as_tgt", - RulesVariables_check_vars_wildcard_as_tgt - }, - { - "check_vars_any_as_tgt", - RulesVariables_check_vars_any_as_tgt - }, - { - "check_vars_this_w_lookup_var", - RulesVariables_check_vars_this_w_lookup_var - }, - { - "check_vars_var_w_lookup_var", - RulesVariables_check_vars_var_w_lookup_var - }, - { - "1_trivial_1_var", - RulesVariables_1_trivial_1_var - }, - { - "2_trivial_1_var", - RulesVariables_2_trivial_1_var - }, - { - "1_trivial_1_var_component", - RulesVariables_1_trivial_1_var_component - }, - { - "2_trivial_1_var_component", - RulesVariables_2_trivial_1_var_component - }, - { - "1_trivial_1_wildcard", - RulesVariables_1_trivial_1_wildcard - }, - { - "2_trivial_1_wildcard", - RulesVariables_2_trivial_1_wildcard - }, - { - "1_trivial_1_wildcard_component", - RulesVariables_1_trivial_1_wildcard_component - }, - { - "2_trivial_1_wildcard_component", - RulesVariables_2_trivial_1_wildcard_component - }, - { - "1_trivial_1_any", - RulesVariables_1_trivial_1_any - }, - { - "2_trivial_1_any", - RulesVariables_2_trivial_1_any - }, - { - "1_trivial_1_any_component", - RulesVariables_1_trivial_1_any_component - }, - { - "2_trivial_1_any_component", - RulesVariables_2_trivial_1_any_component - } -}; - -bake_test_case RulesOperators_testcases[] = { - { - "2_and_not", - RulesOperators_2_and_not - }, - { - "3_and_not_not", - RulesOperators_3_and_not_not - }, - { - "2_and_not_pair_rel_wildcard", - RulesOperators_2_and_not_pair_rel_wildcard - }, - { - "2_and_not_pair_tgt_wildcard", - RulesOperators_2_and_not_pair_tgt_wildcard - }, - { - "2_and_not_pair_rel_tgt_wildcard", - RulesOperators_2_and_not_pair_rel_tgt_wildcard - }, - { - "2_and_not_pair_rel_var", - RulesOperators_2_and_not_pair_rel_var - }, - { - "2_and_not_pair_tgt_var", - RulesOperators_2_and_not_pair_tgt_var - }, - { - "2_and_not_pair_rel_tgt_var", - RulesOperators_2_and_not_pair_rel_tgt_var - }, - { - "2_and_not_pair_rel_tgt_same_var", - RulesOperators_2_and_not_pair_rel_tgt_same_var - }, - { - "2_and_not_pair_rel_var_written", - RulesOperators_2_and_not_pair_rel_var_written - }, - { - "2_and_not_pair_tgt_var_written", - RulesOperators_2_and_not_pair_tgt_var_written - }, - { - "2_and_not_pair_rel_tgt_var_written", - RulesOperators_2_and_not_pair_rel_tgt_var_written - }, - { - "2_and_not_pair_rel_tgt_same_var_written", - RulesOperators_2_and_not_pair_rel_tgt_same_var_written - }, - { - "2_and_not_pair_rel_src_tgt_same_var_written", - RulesOperators_2_and_not_pair_rel_src_tgt_same_var_written - }, - { - "2_and_not_pair_any_rel", - RulesOperators_2_and_not_pair_any_rel - }, - { - "2_and_not_pair_any_tgt", - RulesOperators_2_and_not_pair_any_tgt - }, - { - "2_and_not_pair_any_src", - RulesOperators_2_and_not_pair_any_src - }, - { - "2_and_optional", - RulesOperators_2_and_optional - }, - { - "3_and_optional_optional", - RulesOperators_3_and_optional_optional - }, - { - "2_and_optional_pair_rel_wildcard", - RulesOperators_2_and_optional_pair_rel_wildcard - }, - { - "2_and_optional_pair_tgt_wildcard", - RulesOperators_2_and_optional_pair_tgt_wildcard - }, - { - "2_and_optional_pair_rel_var", - RulesOperators_2_and_optional_pair_rel_var - }, - { - "2_and_optional_pair_tgt_var", - RulesOperators_2_and_optional_pair_tgt_var - }, - { - "2_and_optional_pair_rel_tgt_var", - RulesOperators_2_and_optional_pair_rel_tgt_var - }, - { - "2_and_optional_pair_rel_tgt_same_var", - RulesOperators_2_and_optional_pair_rel_tgt_same_var - }, - { - "2_and_optional_pair_rel_var_written", - RulesOperators_2_and_optional_pair_rel_var_written - }, - { - "2_and_optional_pair_tgt_var_written", - RulesOperators_2_and_optional_pair_tgt_var_written - }, - { - "2_and_optional_pair_rel_tgt_var_written", - RulesOperators_2_and_optional_pair_rel_tgt_var_written - }, - { - "2_and_optional_pair_rel_tgt_same_var_written", - RulesOperators_2_and_optional_pair_rel_tgt_same_var_written - }, - { - "2_and_optional_pair_rel_src_tgt_same_var_written", - RulesOperators_2_and_optional_pair_rel_src_tgt_same_var_written - }, - { - "3_and_optional_optional_pair_w_var", - RulesOperators_3_and_optional_optional_pair_w_var - }, - { - "2_and_optional_pair_any_rel", - RulesOperators_2_and_optional_pair_any_rel - }, - { - "2_and_optional_pair_any_tgt", - RulesOperators_2_and_optional_pair_any_tgt - }, - { - "2_and_optional_pair_any_src", - RulesOperators_2_and_optional_pair_any_src - }, - { - "3_and_optional_dependent_and_pair_rel", - RulesOperators_3_and_optional_dependent_and_pair_rel - }, - { - "3_and_optional_dependent_and_pair_tgt", - RulesOperators_3_and_optional_dependent_and_pair_tgt - }, - { - "3_and_optional_dependent_and_pair_rel_tgt", - RulesOperators_3_and_optional_dependent_and_pair_rel_tgt - }, - { - "3_and_optional_dependent_and_pair_rel_tgt_same_var", - RulesOperators_3_and_optional_dependent_and_pair_rel_tgt_same_var - }, - { - "3_and_optional_dependent_and_pair_rel_tgt_same_other_var", - RulesOperators_3_and_optional_dependent_and_pair_rel_tgt_same_other_var - }, - { - "3_and_optional_dependent_and_pair_src", - RulesOperators_3_and_optional_dependent_and_pair_src - }, - { - "3_and_optional_dependent_optional_pair_rel", - RulesOperators_3_and_optional_dependent_optional_pair_rel - }, - { - "3_and_optional_dependent_optional_pair_tgt", - RulesOperators_3_and_optional_dependent_optional_pair_tgt - }, - { - "3_and_optional_dependent_optional_pair_src", - RulesOperators_3_and_optional_dependent_optional_pair_src - }, - { - "3_and_optional_dependent_not_pair_rel", - RulesOperators_3_and_optional_dependent_not_pair_rel - }, - { - "3_and_optional_dependent_not_pair_tgt", - RulesOperators_3_and_optional_dependent_not_pair_tgt - }, - { - "3_and_optional_dependent_not_pair_src", - RulesOperators_3_and_optional_dependent_not_pair_src - }, - { - "2_or", - RulesOperators_2_or - }, - { - "3_or", - RulesOperators_3_or - }, - { - "2_or_written", - RulesOperators_2_or_written - }, - { - "3_or_written", - RulesOperators_3_or_written - }, - { - "2_or_written_w_rel_var", - RulesOperators_2_or_written_w_rel_var - }, - { - "3_or_written_w_rel_var", - RulesOperators_3_or_written_w_rel_var - }, - { - "2_or_written_w_tgt_var", - RulesOperators_2_or_written_w_tgt_var - }, - { - "2_or_written_w_rel_tgt_var", - RulesOperators_2_or_written_w_rel_tgt_var - }, - { - "2_or_written_w_rel_tgt_same_var", - RulesOperators_2_or_written_w_rel_tgt_same_var - }, - { - "3_or_written_w_tgt_var", - RulesOperators_3_or_written_w_tgt_var - }, - { - "2_or_chains", - RulesOperators_2_or_chains - }, - { - "2_or_chains_written", - RulesOperators_2_or_chains_written - }, - { - "2_or_dependent", - RulesOperators_2_or_dependent - }, - { - "2_or_dependent_reverse", - RulesOperators_2_or_dependent_reverse - }, - { - "2_or_dependent_2_vars", - RulesOperators_2_or_dependent_2_vars - }, - { - "2_or_written_dependent", - RulesOperators_2_or_written_dependent - }, - { - "2_or_written_dependent_2_vars", - RulesOperators_2_or_written_dependent_2_vars - }, - { - "2_or_w_dependent", - RulesOperators_2_or_w_dependent - }, - { - "2_or_w_both", - RulesOperators_2_or_w_both - }, - { - "3_or_w_both", - RulesOperators_3_or_w_both - }, - { - "2_not_first", - RulesOperators_2_not_first - }, - { - "2_optional_first", - RulesOperators_2_optional_first - }, - { - "root_entities_empty", - RulesOperators_root_entities_empty - }, - { - "root_entities", - RulesOperators_root_entities - }, - { - "root_entities_w_children", - RulesOperators_root_entities_w_children - }, - { - "root_entities_w_optional_children", - RulesOperators_root_entities_w_optional_children - }, - { - "core_entities_w_optional_children", - RulesOperators_core_entities_w_optional_children - }, - { - "root_entities_w_not_children", - RulesOperators_root_entities_w_not_children - }, - { - "core_entities_w_not_children", - RulesOperators_core_entities_w_not_children - }, - { - "1_ent_src_not", - RulesOperators_1_ent_src_not - }, - { - "1_ent_src_not_pair", - RulesOperators_1_ent_src_not_pair - }, - { - "1_ent_src_not_pair_rel_wildcard", - RulesOperators_1_ent_src_not_pair_rel_wildcard - }, - { - "1_ent_src_not_pair_tgt_wildcard", - RulesOperators_1_ent_src_not_pair_tgt_wildcard - }, - { - "1_ent_src_not_pair_rel_tgt_wildcard", - RulesOperators_1_ent_src_not_pair_rel_tgt_wildcard - }, - { - "1_ent_src_not_pair_rel_any", - RulesOperators_1_ent_src_not_pair_rel_any - }, - { - "1_ent_src_not_pair_tgt_any", - RulesOperators_1_ent_src_not_pair_tgt_any - }, - { - "1_ent_src_not_pair_rel_tgt_any", - RulesOperators_1_ent_src_not_pair_rel_tgt_any - }, - { - "1_ent_src_not_pair_rel_var", - RulesOperators_1_ent_src_not_pair_rel_var - }, - { - "1_ent_src_not_pair_tgt_var", - RulesOperators_1_ent_src_not_pair_tgt_var - }, - { - "1_ent_src_not_pair_rel_tgt_var", - RulesOperators_1_ent_src_not_pair_rel_tgt_var - }, - { - "1_ent_src_not_pair_rel_tgt_same_var", - RulesOperators_1_ent_src_not_pair_rel_tgt_same_var - }, - { - "1_this_src_not_pair_rel_var", - RulesOperators_1_this_src_not_pair_rel_var - }, - { - "1_this_src_not_pair_tgt_var", - RulesOperators_1_this_src_not_pair_tgt_var - }, - { - "1_this_src_not_pair_rel_tgt_var", - RulesOperators_1_this_src_not_pair_rel_tgt_var - }, - { - "1_this_src_not_pair_rel_tgt_same_var", - RulesOperators_1_this_src_not_pair_rel_tgt_same_var - }, - { - "1_ent_src_not_pair_rel_var_written", - RulesOperators_1_ent_src_not_pair_rel_var_written - }, - { - "1_ent_src_not_pair_tgt_var_written", - RulesOperators_1_ent_src_not_pair_tgt_var_written - }, - { - "1_ent_src_not_pair_rel_tgt_var_written", - RulesOperators_1_ent_src_not_pair_rel_tgt_var_written - }, - { - "1_ent_src_not_pair_rel_tgt_same_var_written", - RulesOperators_1_ent_src_not_pair_rel_tgt_same_var_written - } -}; - -bake_test_case RulesTransitive_testcases[] = { - { - "1_fact_0_lvl_true", - RulesTransitive_1_fact_0_lvl_true - }, - { - "1_fact_1_lvl_true", - RulesTransitive_1_fact_1_lvl_true - }, - { - "1_fact_2_lvl_true", - RulesTransitive_1_fact_2_lvl_true - }, - { - "1_fact_0_lvl_false", - RulesTransitive_1_fact_0_lvl_false - }, - { - "1_fact_1_lvl_false", - RulesTransitive_1_fact_1_lvl_false - }, - { - "1_fact_2_lvl_false", - RulesTransitive_1_fact_2_lvl_false - }, - { - "1_fact_reflexive", - RulesTransitive_1_fact_reflexive - }, - { - "1_this_src_written_0_lvl", - RulesTransitive_1_this_src_written_0_lvl - }, - { - "1_this_src_written_1_lvl", - RulesTransitive_1_this_src_written_1_lvl - }, - { - "1_this_src_written_2_lvl", - RulesTransitive_1_this_src_written_2_lvl - }, - { - "1_this_src_written_reflexive", - RulesTransitive_1_this_src_written_reflexive - }, - { - "1_this_src_0_lvl", - RulesTransitive_1_this_src_0_lvl - }, - { - "1_this_src_1_lvl", - RulesTransitive_1_this_src_1_lvl - }, - { - "1_this_src_2_lvl", - RulesTransitive_1_this_src_2_lvl - }, - { - "1_this_src_reflexive", - RulesTransitive_1_this_src_reflexive - }, - { - "1_ent_src_tgt_var_0_lvl", - RulesTransitive_1_ent_src_tgt_var_0_lvl - }, - { - "1_ent_src_tgt_var_1_lvl", - RulesTransitive_1_ent_src_tgt_var_1_lvl - }, - { - "1_ent_src_tgt_var_2_lvl", - RulesTransitive_1_ent_src_tgt_var_2_lvl - }, - { - "1_ent_src_tgt_var_reflexive", - RulesTransitive_1_ent_src_tgt_var_reflexive - }, - { - "1_this_src_tgt_var", - RulesTransitive_1_this_src_tgt_var - }, - { - "1_this_src_tgt_var_reflexive", - RulesTransitive_1_this_src_tgt_var_reflexive - }, - { - "1_var_src_written_0_lvl", - RulesTransitive_1_var_src_written_0_lvl - }, - { - "1_var_src_written_1_lvl", - RulesTransitive_1_var_src_written_1_lvl - }, - { - "1_var_src_written_2_lvl", - RulesTransitive_1_var_src_written_2_lvl - }, - { - "1_var_src_written_reflexive", - RulesTransitive_1_var_src_written_reflexive - }, - { - "1_var_src_0_lvl", - RulesTransitive_1_var_src_0_lvl - }, - { - "1_var_src_1_lvl", - RulesTransitive_1_var_src_1_lvl - }, - { - "1_var_src_2_lvl", - RulesTransitive_1_var_src_2_lvl - }, - { - "1_var_src_reflexive", - RulesTransitive_1_var_src_reflexive - }, - { - "1_var_src_tgt_var", - RulesTransitive_1_var_src_tgt_var - }, - { - "1_var_src_tgt_var_reflexive", - RulesTransitive_1_var_src_tgt_var_reflexive - }, - { - "1_ent_src_tgt_this_0_lvl", - RulesTransitive_1_ent_src_tgt_this_0_lvl - }, - { - "1_ent_src_tgt_this_1_lvl", - RulesTransitive_1_ent_src_tgt_this_1_lvl - }, - { - "1_ent_src_tgt_this_2_lvl", - RulesTransitive_1_ent_src_tgt_this_2_lvl - }, - { - "1_ent_src_tgt_this_reflexive", - RulesTransitive_1_ent_src_tgt_this_reflexive - }, - { - "1_var_src_tgt_this", - RulesTransitive_1_var_src_tgt_this - }, - { - "1_var_src_tgt_this_reflexive", - RulesTransitive_1_var_src_tgt_this_reflexive - }, - { - "2_ent_src_constrain_tgt_var_before_0_lvl", - RulesTransitive_2_ent_src_constrain_tgt_var_before_0_lvl - }, - { - "2_ent_src_constrain_tgt_var_before_1_lvl", - RulesTransitive_2_ent_src_constrain_tgt_var_before_1_lvl - }, - { - "2_ent_src_constrain_tgt_var_before_2_lvl", - RulesTransitive_2_ent_src_constrain_tgt_var_before_2_lvl - }, - { - "2_ent_src_constrain_tgt_var_after_0_lvl", - RulesTransitive_2_ent_src_constrain_tgt_var_after_0_lvl - }, - { - "2_ent_src_constrain_tgt_var_after_1_lvl", - RulesTransitive_2_ent_src_constrain_tgt_var_after_1_lvl - }, - { - "2_ent_src_constrain_tgt_var_after_2_lvl", - RulesTransitive_2_ent_src_constrain_tgt_var_after_2_lvl - }, - { - "2_this_src_constrain_tgt_var_before_0_lvl", - RulesTransitive_2_this_src_constrain_tgt_var_before_0_lvl - }, - { - "2_this_src_constrain_tgt_var_before_1_lvl", - RulesTransitive_2_this_src_constrain_tgt_var_before_1_lvl - }, - { - "2_this_src_constrain_tgt_var_before_2_lvl", - RulesTransitive_2_this_src_constrain_tgt_var_before_2_lvl - }, - { - "2_this_src_constrain_tgt_var_after_0_lvl", - RulesTransitive_2_this_src_constrain_tgt_var_after_0_lvl - }, - { - "2_this_src_constrain_tgt_var_after_1_lvl", - RulesTransitive_2_this_src_constrain_tgt_var_after_1_lvl - }, - { - "2_this_src_constrain_tgt_var_after_2_lvl", - RulesTransitive_2_this_src_constrain_tgt_var_after_2_lvl - }, - { - "1_src_tgt_same_var", - RulesTransitive_1_src_tgt_same_var - }, - { - "1_src_tgt_same_var_reflexive", - RulesTransitive_1_src_tgt_same_var_reflexive - }, - { - "1_src_tgt_same_this_var_reflexive", - RulesTransitive_1_src_tgt_same_this_var_reflexive - }, - { - "1_any_src_tgt_var", - RulesTransitive_1_any_src_tgt_var - }, - { - "not_transitive_ent_tgt", - RulesTransitive_not_transitive_ent_tgt - }, - { - "not_transitive_var_tgt", - RulesTransitive_not_transitive_var_tgt - }, - { - "not_transitive_ent_tgt_written", - RulesTransitive_not_transitive_ent_tgt_written - }, - { - "not_transitive_var_tgt_written", - RulesTransitive_not_transitive_var_tgt_written - }, - { - "optional_transitive_ent_tgt", - RulesTransitive_optional_transitive_ent_tgt - }, - { - "optional_transitive_var_tgt", - RulesTransitive_optional_transitive_var_tgt - }, - { - "optional_transitive_ent_tgt_written", - RulesTransitive_optional_transitive_ent_tgt_written - }, - { - "optional_transitive_var_tgt_written", - RulesTransitive_optional_transitive_var_tgt_written - }, - { - "2_var_src_w_same_tgt_ent", - RulesTransitive_2_var_src_w_same_tgt_ent - }, - { - "self_target", - RulesTransitive_self_target - }, - { - "any_target", - RulesTransitive_any_target - } -}; - -bake_test_case RulesComponentInheritance_testcases[] = { - { - "1_ent_0_lvl", - RulesComponentInheritance_1_ent_0_lvl - }, - { - "1_ent_1_lvl", - RulesComponentInheritance_1_ent_1_lvl - }, - { - "1_ent_2_lvl", - RulesComponentInheritance_1_ent_2_lvl - }, - { - "1_ent_3_lvl", - RulesComponentInheritance_1_ent_3_lvl - }, - { - "1_this_0_lvl", - RulesComponentInheritance_1_this_0_lvl - }, - { - "1_this_1_lvl", - RulesComponentInheritance_1_this_1_lvl - }, - { - "1_this_2_lvl", - RulesComponentInheritance_1_this_2_lvl - }, - { - "1_this_3_lvl", - RulesComponentInheritance_1_this_3_lvl - }, - { - "1_this_0_lvl_written", - RulesComponentInheritance_1_this_0_lvl_written - }, - { - "1_this_1_lvl_written", - RulesComponentInheritance_1_this_1_lvl_written - }, - { - "1_this_2_lvl_written", - RulesComponentInheritance_1_this_2_lvl_written - }, - { - "1_this_3_lvl_written", - RulesComponentInheritance_1_this_3_lvl_written - }, - { - "1_var_0_lvl", - RulesComponentInheritance_1_var_0_lvl - }, - { - "1_var_1_lvl", - RulesComponentInheritance_1_var_1_lvl - }, - { - "1_var_2_lvl", - RulesComponentInheritance_1_var_2_lvl - }, - { - "1_var_3_lvl", - RulesComponentInheritance_1_var_3_lvl - }, - { - "1_var_0_lvl_written", - RulesComponentInheritance_1_var_0_lvl_written - }, - { - "1_var_1_lvl_written", - RulesComponentInheritance_1_var_1_lvl_written - }, - { - "1_var_2_lvl_written", - RulesComponentInheritance_1_var_2_lvl_written - }, - { - "1_var_3_lvl_written", - RulesComponentInheritance_1_var_3_lvl_written - }, - { - "1_ent_1_lvl_self", - RulesComponentInheritance_1_ent_1_lvl_self - }, - { - "1_this_1_lvl_self", - RulesComponentInheritance_1_this_1_lvl_self - }, - { - "1_this_1_lvl_written_self", - RulesComponentInheritance_1_this_1_lvl_written_self - }, - { - "1_var_1_lvl_self", - RulesComponentInheritance_1_var_1_lvl_self - }, - { - "1_var_1_lvl_written_self", - RulesComponentInheritance_1_var_1_lvl_written_self - }, - { - "1_ent_src_not", - RulesComponentInheritance_1_ent_src_not - }, - { - "1_this_src_not", - RulesComponentInheritance_1_this_src_not - }, - { - "1_var_src_not", - RulesComponentInheritance_1_var_src_not - }, - { - "1_this_src_not_written", - RulesComponentInheritance_1_this_src_not_written - }, - { - "1_var_src_not_written", - RulesComponentInheritance_1_var_src_not_written - }, - { - "first_self", - RulesComponentInheritance_first_self - }, - { - "first_down", - RulesComponentInheritance_first_down - }, - { - "first_self_down", - RulesComponentInheritance_first_self_down - }, - { - "first_rel_self", - RulesComponentInheritance_first_rel_self - }, - { - "first_rel_down", - RulesComponentInheritance_first_rel_down - }, - { - "first_rel_self_down", - RulesComponentInheritance_first_rel_self_down - } -}; - -bake_test_case RulesRecycled_testcases[] = { - { - "recycled_vars", - RulesRecycled_recycled_vars - }, - { - "recycled_pair_vars", - RulesRecycled_recycled_pair_vars - }, - { - "recycled_this_ent_var", - RulesRecycled_recycled_this_ent_var - }, - { - "has_recycled_id_from_pair", - RulesRecycled_has_recycled_id_from_pair - } -}; - -bake_test_case RulesBuiltinPredicates_testcases[] = { - { - "this_eq_id", - RulesBuiltinPredicates_this_eq_id - }, - { - "this_eq_name", - RulesBuiltinPredicates_this_eq_name - }, - { - "this_eq_var", - RulesBuiltinPredicates_this_eq_var - }, - { - "this_eq_id_written", - RulesBuiltinPredicates_this_eq_id_written - }, - { - "this_eq_id_written_no_match", - RulesBuiltinPredicates_this_eq_id_written_no_match - }, - { - "this_eq_name_written", - RulesBuiltinPredicates_this_eq_name_written - }, - { - "this_eq_name_written_no_match", - RulesBuiltinPredicates_this_eq_name_written_no_match - }, - { - "this_eq_var_written", - RulesBuiltinPredicates_this_eq_var_written - }, - { - "var_eq_id", - RulesBuiltinPredicates_var_eq_id - }, - { - "var_eq_name", - RulesBuiltinPredicates_var_eq_name - }, - { - "var_eq_var", - RulesBuiltinPredicates_var_eq_var - }, - { - "var_eq_id_written", - RulesBuiltinPredicates_var_eq_id_written - }, - { - "var_eq_id_written_no_match", - RulesBuiltinPredicates_var_eq_id_written_no_match - }, - { - "var_eq_name_written", - RulesBuiltinPredicates_var_eq_name_written - }, - { - "var_eq_name_written_no_match", - RulesBuiltinPredicates_var_eq_name_written_no_match - }, - { - "var_eq_var_written", - RulesBuiltinPredicates_var_eq_var_written - }, - { - "this_neq_id", - RulesBuiltinPredicates_this_neq_id - }, - { - "this_neq_name", - RulesBuiltinPredicates_this_neq_name - }, - { - "this_neq_var", - RulesBuiltinPredicates_this_neq_var - }, - { - "this_neq_id_written", - RulesBuiltinPredicates_this_neq_id_written - }, - { - "this_neq_id_written_no_match", - RulesBuiltinPredicates_this_neq_id_written_no_match - }, - { - "this_neq_name_written", - RulesBuiltinPredicates_this_neq_name_written - }, - { - "this_neq_name_written_no_match", - RulesBuiltinPredicates_this_neq_name_written_no_match - }, - { - "this_neq_var_written", - RulesBuiltinPredicates_this_neq_var_written - }, - { - "var_neq_id", - RulesBuiltinPredicates_var_neq_id - }, - { - "var_neq_name", - RulesBuiltinPredicates_var_neq_name - }, - { - "var_neq_var", - RulesBuiltinPredicates_var_neq_var - }, - { - "var_neq_id_written", - RulesBuiltinPredicates_var_neq_id_written - }, - { - "var_neq_id_written_no_match", - RulesBuiltinPredicates_var_neq_id_written_no_match - }, - { - "var_neq_name_written", - RulesBuiltinPredicates_var_neq_name_written - }, - { - "var_neq_name_written_no_match", - RulesBuiltinPredicates_var_neq_name_written_no_match - }, - { - "var_neq_var_written", - RulesBuiltinPredicates_var_neq_var_written - }, - { - "this_2_neq_id", - RulesBuiltinPredicates_this_2_neq_id - }, - { - "this_2_neq_name", - RulesBuiltinPredicates_this_2_neq_name - }, - { - "var_2_neq_id", - RulesBuiltinPredicates_var_2_neq_id - }, - { - "var_2_neq_name", - RulesBuiltinPredicates_var_2_neq_name - }, - { - "this_2_neq_id_written", - RulesBuiltinPredicates_this_2_neq_id_written - }, - { - "this_2_neq_name_written", - RulesBuiltinPredicates_this_2_neq_name_written - }, - { - "var_2_neq_id_written", - RulesBuiltinPredicates_var_2_neq_id_written - }, - { - "var_2_neq_name_written", - RulesBuiltinPredicates_var_2_neq_name_written - }, - { - "this_2_or_id", - RulesBuiltinPredicates_this_2_or_id - }, - { - "this_2_or_name", - RulesBuiltinPredicates_this_2_or_name - }, - { - "var_2_or_id", - RulesBuiltinPredicates_var_2_or_id - }, - { - "var_2_or_name", - RulesBuiltinPredicates_var_2_or_name - }, - { - "this_2_or_id_written", - RulesBuiltinPredicates_this_2_or_id_written - }, - { - "this_2_or_name_written", - RulesBuiltinPredicates_this_2_or_name_written - }, - { - "var_2_or_id_written", - RulesBuiltinPredicates_var_2_or_id_written - }, - { - "var_2_or_name_written", - RulesBuiltinPredicates_var_2_or_name_written - }, - { - "this_match_eq", - RulesBuiltinPredicates_this_match_eq - }, - { - "var_match_eq", - RulesBuiltinPredicates_var_match_eq - }, - { - "this_match_eq_written", - RulesBuiltinPredicates_this_match_eq_written - }, - { - "var_match_eq_written", - RulesBuiltinPredicates_var_match_eq_written - }, - { - "this_match_neq", - RulesBuiltinPredicates_this_match_neq - }, - { - "var_match_neq", - RulesBuiltinPredicates_var_match_neq - }, - { - "this_match_neq_written", - RulesBuiltinPredicates_this_match_neq_written - }, - { - "var_match_neq_written", - RulesBuiltinPredicates_var_match_neq_written - }, - { - "this_match_2_neq", - RulesBuiltinPredicates_this_match_2_neq - }, - { - "var_match_2_neq", - RulesBuiltinPredicates_var_match_2_neq - }, - { - "this_match_2_neq_written", - RulesBuiltinPredicates_this_match_2_neq_written - }, - { - "var_match_2_neq_written", - RulesBuiltinPredicates_var_match_2_neq_written - }, - { - "this_match_2_or", - RulesBuiltinPredicates_this_match_2_or - }, - { - "this_match_2_or_written", - RulesBuiltinPredicates_this_match_2_or_written - }, - { - "this_match_3_or", - RulesBuiltinPredicates_this_match_3_or - }, - { - "this_match_3_or_written", - RulesBuiltinPredicates_this_match_3_or_written - }, - { - "unresolved_by_name", - RulesBuiltinPredicates_unresolved_by_name - }, - { - "var_eq_wildcard", - RulesBuiltinPredicates_var_eq_wildcard - }, - { - "var_eq_any", - RulesBuiltinPredicates_var_eq_any - }, - { - "var_eq_wildcard_after_write", - RulesBuiltinPredicates_var_eq_wildcard_after_write - }, - { - "var_eq_any_after_write", - RulesBuiltinPredicates_var_eq_any_after_write - }, - { - "var_eq_after_var_0_src", - RulesBuiltinPredicates_var_eq_after_var_0_src - } -}; - -bake_test_case RulesScopes_testcases[] = { - { - "term_w_not_scope_1_term", - RulesScopes_term_w_not_scope_1_term - }, - { - "term_w_not_scope_2_terms", - RulesScopes_term_w_not_scope_2_terms - }, - { - "term_w_not_scope_1_term_w_not", - RulesScopes_term_w_not_scope_1_term_w_not - }, - { - "term_w_not_scope_2_terms_w_not", - RulesScopes_term_w_not_scope_2_terms_w_not - }, - { - "term_w_not_scope_1_term_w_var", - RulesScopes_term_w_not_scope_1_term_w_var - }, - { - "term_w_not_scope_2_terms_w_var", - RulesScopes_term_w_not_scope_2_terms_w_var - }, - { - "term_w_not_scope_1_term_w_not_w_var", - RulesScopes_term_w_not_scope_1_term_w_not_w_var - }, - { - "term_w_not_scope_2_terms_w_not_w_var", - RulesScopes_term_w_not_scope_2_terms_w_not_w_var - }, - { - "term_w_not_scope_2_terms_w_or", - RulesScopes_term_w_not_scope_2_terms_w_or - }, - { - "term_w_not_scope_3_terms_w_or", - RulesScopes_term_w_not_scope_3_terms_w_or - } -}; - -bake_test_case RulesTraversal_testcases[] = { - { - "this_self_up_childof", - RulesTraversal_this_self_up_childof - }, - { - "this_up_childof", - RulesTraversal_this_up_childof - }, - { - "this_written_self_up_childof", - RulesTraversal_this_written_self_up_childof - }, - { - "this_written_up_childof", - RulesTraversal_this_written_up_childof - }, - { - "var_self_up_childof", - RulesTraversal_var_self_up_childof - }, - { - "var_up_childof", - RulesTraversal_var_up_childof - }, - { - "var_written_self_up_childof", - RulesTraversal_var_written_self_up_childof - }, - { - "var_written_up_childof", - RulesTraversal_var_written_up_childof - }, - { - "set_var_self_up_childof", - RulesTraversal_set_var_self_up_childof - }, - { - "set_var_up_childof", - RulesTraversal_set_var_up_childof - }, - { - "set_var_written_self_up_childof", - RulesTraversal_set_var_written_self_up_childof - }, - { - "set_var_written_up_childof", - RulesTraversal_set_var_written_up_childof - }, - { - "ent_self_up_childof", - RulesTraversal_ent_self_up_childof - }, - { - "ent_up_childof", - RulesTraversal_ent_up_childof - }, - { - "implicit_this_self_up_isa", - RulesTraversal_implicit_this_self_up_isa - }, - { - "implicit_this_up_isa", - RulesTraversal_implicit_this_up_isa - }, - { - "implicit_var_self_up_isa", - RulesTraversal_implicit_var_self_up_isa - }, - { - "implicit_var_up_isa", - RulesTraversal_implicit_var_up_isa - }, - { - "implicit_ent_self_up_isa", - RulesTraversal_implicit_ent_self_up_isa - }, - { - "implicit_ent_up_isa", - RulesTraversal_implicit_ent_up_isa - }, - { - "self_up_2_targets", - RulesTraversal_self_up_2_targets - }, - { - "up_2_targets", - RulesTraversal_up_2_targets - }, - { - "self_up_2_targets_diamond", - RulesTraversal_self_up_2_targets_diamond - }, - { - "up_2_targets_diamond", - RulesTraversal_up_2_targets_diamond - }, - { - "written_self_up_2_targets", - RulesTraversal_written_self_up_2_targets - }, - { - "written_up_2_targets", - RulesTraversal_written_up_2_targets - }, - { - "written_self_up_2_targets_diamond", - RulesTraversal_written_self_up_2_targets_diamond - }, - { - "written_up_2_targets_diamond", - RulesTraversal_written_up_2_targets_diamond - }, - { - "2_self_up_terms", - RulesTraversal_2_self_up_terms - }, - { - "2_self_up_terms_2_targets", - RulesTraversal_2_self_up_terms_2_targets - }, - { - "self_up_empty_table", - RulesTraversal_self_up_empty_table - }, - { - "up_empty_table", - RulesTraversal_up_empty_table - }, - { - "self_up_all_owned", - RulesTraversal_self_up_all_owned - }, - { - "up_all_owned", - RulesTraversal_up_all_owned - }, - { - "this_self_up_childof_inherited", - RulesTraversal_this_self_up_childof_inherited - }, - { - "this_up_childof_inherited", - RulesTraversal_this_up_childof_inherited - }, - { - "this_written_self_up_childof_inherited", - RulesTraversal_this_written_self_up_childof_inherited - }, - { - "this_written_up_childof_inherited", - RulesTraversal_this_written_up_childof_inherited - }, - { - "var_self_up_childof_inherited", - RulesTraversal_var_self_up_childof_inherited - }, - { - "var_up_childof_inherited", - RulesTraversal_var_up_childof_inherited - }, - { - "var_written_self_up_childof_inherited", - RulesTraversal_var_written_self_up_childof_inherited - }, - { - "var_written_up_childof_inherited", - RulesTraversal_var_written_up_childof_inherited - }, - { - "ent_self_up_childof_inherited", - RulesTraversal_ent_self_up_childof_inherited - }, - { - "ent_up_childof_inherited", - RulesTraversal_ent_up_childof_inherited - }, - { - "ent_written_self_up_childof_inherited", - RulesTraversal_ent_written_self_up_childof_inherited - }, - { - "ent_written_up_childof_inherited", - RulesTraversal_ent_written_up_childof_inherited - }, - { - "this_self_up_childof_component", - RulesTraversal_this_self_up_childof_component - }, - { - "this_up_childof_component", - RulesTraversal_this_up_childof_component - }, - { - "this_written_self_up_childof_component", - RulesTraversal_this_written_self_up_childof_component - }, - { - "this_written_up_childof_component", - RulesTraversal_this_written_up_childof_component - }, - { - "var_self_up_childof_component", - RulesTraversal_var_self_up_childof_component - }, - { - "var_up_childof_component", - RulesTraversal_var_up_childof_component - }, - { - "var_written_self_up_childof_component", - RulesTraversal_var_written_self_up_childof_component - }, - { - "var_written_up_childof_component", - RulesTraversal_var_written_up_childof_component - }, - { - "this_self_up_childof_recycled_parent", - RulesTraversal_this_self_up_childof_recycled_parent - }, - { - "this_up_childof_recycled_parent", - RulesTraversal_this_up_childof_recycled_parent - }, - { - "this_written_self_up_childof_recycled_parent", - RulesTraversal_this_written_self_up_childof_recycled_parent - }, - { - "this_written_up_childof_recycled_parent", - RulesTraversal_this_written_up_childof_recycled_parent - }, - { - "this_self_up_childof_recycled_parent_component", - RulesTraversal_this_self_up_childof_recycled_parent_component - }, - { - "this_up_childof_recycled_parent_component", - RulesTraversal_this_up_childof_recycled_parent_component - }, - { - "this_written_self_up_childof_recycled_parent_component", - RulesTraversal_this_written_self_up_childof_recycled_parent_component - }, - { - "this_written_up_childof_recycled_parent_component", - RulesTraversal_this_written_up_childof_recycled_parent_component - }, - { - "this_self_up_childof_pair", - RulesTraversal_this_self_up_childof_pair - }, - { - "this_up_childof_pair", - RulesTraversal_this_up_childof_pair - }, - { - "this_written_self_up_childof_pair", - RulesTraversal_this_written_self_up_childof_pair - }, - { - "this_written_up_childof_pair", - RulesTraversal_this_written_up_childof_pair - }, - { - "this_self_up_childof_pair_wildcard", - RulesTraversal_this_self_up_childof_pair_wildcard - }, - { - "this_up_childof_pair_wildcard", - RulesTraversal_this_up_childof_pair_wildcard - }, - { - "this_written_self_up_childof_pair_wildcard", - RulesTraversal_this_written_self_up_childof_pair_wildcard - }, - { - "this_written_up_childof_pair_wildcard", - RulesTraversal_this_written_up_childof_pair_wildcard - }, - { - "this_self_up_childof_pair_tgt_var", - RulesTraversal_this_self_up_childof_pair_tgt_var - }, - { - "this_written_self_up_childof_pair_tgt_var", - RulesTraversal_this_written_self_up_childof_pair_tgt_var - }, - { - "this_self_up_childof_pair_rel_var", - RulesTraversal_this_self_up_childof_pair_rel_var - }, - { - "this_written_self_up_childof_pair_rel_var", - RulesTraversal_this_written_self_up_childof_pair_rel_var - }, - { - "this_self_up_childof_pair_for_var_written", - RulesTraversal_this_self_up_childof_pair_for_var_written - }, - { - "this_up_childof_pair_for_var_written", - RulesTraversal_this_up_childof_pair_for_var_written - }, - { - "this_written_self_up_childof_pair_for_var_written", - RulesTraversal_this_written_self_up_childof_pair_for_var_written - }, - { - "this_self_up_childof_pair_for_var_written_n_targets", - RulesTraversal_this_self_up_childof_pair_for_var_written_n_targets - }, - { - "this_written_self_up_childof_pair_for_var_written_n_targets", - RulesTraversal_this_written_self_up_childof_pair_for_var_written_n_targets - }, - { - "self_up_2_levels", - RulesTraversal_self_up_2_levels - }, - { - "not_up_disabled", - RulesTraversal_not_up_disabled - }, - { - "up_2_rel_instances", - RulesTraversal_up_2_rel_instances - }, - { - "up_2_rel_instances_match_2nd", - RulesTraversal_up_2_rel_instances_match_2nd - }, - { - "up_only_w_owned", - RulesTraversal_up_only_w_owned - }, - { - "this_self_cascade_childof", - RulesTraversal_this_self_cascade_childof - }, - { - "this_cascade_childof", - RulesTraversal_this_cascade_childof - }, - { - "this_written_self_cascade_childof", - RulesTraversal_this_written_self_cascade_childof - }, - { - "this_written_cascade_childof", - RulesTraversal_this_written_cascade_childof - }, - { - "this_self_cascade_childof_w_parent_flag", - RulesTraversal_this_self_cascade_childof_w_parent_flag - }, - { - "this_cascade_childof_w_parent_flag", - RulesTraversal_this_cascade_childof_w_parent_flag - }, - { - "this_written_self_cascade_childof_w_parent_flag", - RulesTraversal_this_written_self_cascade_childof_w_parent_flag - }, - { - "this_written_cascade_childof_w_parent_flag", - RulesTraversal_this_written_cascade_childof_w_parent_flag - } -}; - -bake_test_case SystemPeriodic_testcases[] = { - { - "1_type_1_component", - SystemPeriodic_1_type_1_component - }, - { - "1_type_3_component", - SystemPeriodic_1_type_3_component - }, - { - "3_type_1_component", - SystemPeriodic_3_type_1_component - }, - { - "2_type_3_component", - SystemPeriodic_2_type_3_component - }, - { - "1_type_1_component_1_tag", - SystemPeriodic_1_type_1_component_1_tag - }, - { - "2_type_1_component_1_tag", - SystemPeriodic_2_type_1_component_1_tag - }, - { - "2_type_1_and_1_not", - SystemPeriodic_2_type_1_and_1_not - }, - { - "2_type_2_and_1_not", - SystemPeriodic_2_type_2_and_1_not - }, - { - "2_type_2_and_2_not", - SystemPeriodic_2_type_2_and_2_not - }, - { - "4_type_1_and_1_or", - SystemPeriodic_4_type_1_and_1_or - }, - { - "4_type_1_and_1_or_of_3", - SystemPeriodic_4_type_1_and_1_or_of_3 - }, - { - "1_type_1_and_1_or", - SystemPeriodic_1_type_1_and_1_or - }, - { - "2_type_1_and_1_optional", - SystemPeriodic_2_type_1_and_1_optional - }, - { - "2_type_2_and_1_optional", - SystemPeriodic_2_type_2_and_1_optional - }, - { - "6_type_1_and_2_optional", - SystemPeriodic_6_type_1_and_2_optional - }, - { - "ensure_optional_is_unset_column", - SystemPeriodic_ensure_optional_is_unset_column - }, - { - "ensure_optional_is_null_shared", - SystemPeriodic_ensure_optional_is_null_shared - }, - { - "match_2_systems_w_populated_table", - SystemPeriodic_match_2_systems_w_populated_table - }, - { - "on_period", - SystemPeriodic_on_period - }, - { - "on_period_long_delta", - SystemPeriodic_on_period_long_delta - }, - { - "disabled", - SystemPeriodic_disabled - }, - { - "disabled_feature", - SystemPeriodic_disabled_feature - }, - { - "disabled_nested_feature", - SystemPeriodic_disabled_nested_feature - }, - { - "two_refs", - SystemPeriodic_two_refs - }, - { - "filter_disabled", - SystemPeriodic_filter_disabled - }, - { - "match_disabled", - SystemPeriodic_match_disabled - }, - { - "match_disabled_and_enabled", - SystemPeriodic_match_disabled_and_enabled - }, - { - "match_prefab", - SystemPeriodic_match_prefab - }, - { - "match_prefab_and_normal", - SystemPeriodic_match_prefab_and_normal - }, - { - "is_shared_on_column_not_set", - SystemPeriodic_is_shared_on_column_not_set - }, - { - "owned_column", - SystemPeriodic_owned_column - }, - { - "owned_not_column", - SystemPeriodic_owned_not_column - }, - { - "owned_or_column", - SystemPeriodic_owned_or_column - }, - { - "shared_column", - SystemPeriodic_shared_column - }, - { - "shared_not_column", - SystemPeriodic_shared_not_column - }, - { - "shared_or_column", - SystemPeriodic_shared_or_column - }, - { - "container_dont_match_inheritance", - SystemPeriodic_container_dont_match_inheritance - }, - { - "cascade_dont_match_inheritance", - SystemPeriodic_cascade_dont_match_inheritance - }, - { - "not_from_entity", - SystemPeriodic_not_from_entity - }, - { - "sys_context", - SystemPeriodic_sys_context - }, - { - "get_sys_context_from_param", - SystemPeriodic_get_sys_context_from_param - }, - { - "owned_only", - SystemPeriodic_owned_only - }, - { - "shared_only", - SystemPeriodic_shared_only - }, - { - "is_in_readonly", - SystemPeriodic_is_in_readonly - }, - { - "get_period", - SystemPeriodic_get_period - }, - { - "and_type", - SystemPeriodic_and_type - }, - { - "or_type", - SystemPeriodic_or_type - } -}; - -bake_test_case Timer_testcases[] = { - { - "timeout", - Timer_timeout - }, - { - "interval", - Timer_interval - }, - { - "shared_timeout", - Timer_shared_timeout - }, - { - "shared_interval", - Timer_shared_interval - }, - { - "start_stop_one_shot", - Timer_start_stop_one_shot - }, - { - "start_stop_interval", - Timer_start_stop_interval - }, - { - "rate_filter", - Timer_rate_filter - }, - { - "rate_filter_w_rate_filter_src", - Timer_rate_filter_w_rate_filter_src - }, - { - "rate_filter_w_timer_src", - Timer_rate_filter_w_timer_src - }, - { - "rate_filter_with_empty_src", - Timer_rate_filter_with_empty_src - }, - { - "one_shot_timer_entity", - Timer_one_shot_timer_entity - }, - { - "interval_timer_entity", - Timer_interval_timer_entity - }, - { - "rate_entity", - Timer_rate_entity - }, - { - "nested_rate_entity", - Timer_nested_rate_entity - }, - { - "nested_rate_entity_empty_src", - Timer_nested_rate_entity_empty_src - }, - { - "naked_tick_entity", - Timer_naked_tick_entity - }, - { - "stop_timer_w_rate", - Timer_stop_timer_w_rate - }, - { - "stop_timer_w_rate_same_src", - Timer_stop_timer_w_rate_same_src - }, - { - "randomize_timers", - Timer_randomize_timers - } -}; - -bake_test_case SystemCascade_testcases[] = { - { - "cascade_depth_1", - SystemCascade_cascade_depth_1 - }, - { - "cascade_depth_2", - SystemCascade_cascade_depth_2 - }, - { - "cascade_depth_2_new_syntax", - SystemCascade_cascade_depth_2_new_syntax - }, - { - "add_after_match", - SystemCascade_add_after_match - }, - { - "adopt_after_match", - SystemCascade_adopt_after_match - }, - { - "custom_relation_cascade_depth_1", - SystemCascade_custom_relation_cascade_depth_1 - }, - { - "custom_relation_cascade_depth_2", - SystemCascade_custom_relation_cascade_depth_2 - }, - { - "custom_relation_add_after_match", - SystemCascade_custom_relation_add_after_match - }, - { - "custom_relation_adopt_after_match", - SystemCascade_custom_relation_adopt_after_match - } -}; - -bake_test_case SystemManual_testcases[] = { - { - "1_type_1_component", - SystemManual_1_type_1_component - }, - { - "no_automerge", - SystemManual_no_automerge - }, - { - "dont_run_w_unmatching_entity_query", - SystemManual_dont_run_w_unmatching_entity_query - } -}; - -bake_test_case Tasks_testcases[] = { - { - "no_components", - Tasks_no_components - }, - { - "one_tag", - Tasks_one_tag - }, - { - "from_system", - Tasks_from_system - }, - { - "tasks_in_phases", - Tasks_tasks_in_phases - } -}; - -bake_test_case System_w_FromParent_testcases[] = { - { - "1_column_from_container", - System_w_FromParent_1_column_from_container - }, - { - "2_column_1_from_container", - System_w_FromParent_2_column_1_from_container - }, - { - "3_column_2_from_container", - System_w_FromParent_3_column_2_from_container - }, - { - "2_column_1_from_container_w_not", - System_w_FromParent_2_column_1_from_container_w_not - }, - { - "2_column_1_from_container_w_not_prefab", - System_w_FromParent_2_column_1_from_container_w_not_prefab - }, - { - "3_column_1_from_comtainer_1_from_container_w_not", - System_w_FromParent_3_column_1_from_comtainer_1_from_container_w_not - }, - { - "2_column_1_from_container_w_or", - System_w_FromParent_2_column_1_from_container_w_or - }, - { - "select_same_from_container", - System_w_FromParent_select_same_from_container - }, - { - "add_component_after_match", - System_w_FromParent_add_component_after_match - }, - { - "add_component_after_match_and_rematch", - System_w_FromParent_add_component_after_match_and_rematch - }, - { - "add_component_after_match_unmatch", - System_w_FromParent_add_component_after_match_unmatch - }, - { - "add_component_after_match_unmatch_match", - System_w_FromParent_add_component_after_match_unmatch_match - }, - { - "add_component_after_match_2_systems", - System_w_FromParent_add_component_after_match_2_systems - }, - { - "add_component_in_progress_after_match", - System_w_FromParent_add_component_in_progress_after_match - }, - { - "add_component_after_match_and_rematch_w_entity_type_expr", - System_w_FromParent_add_component_after_match_and_rematch_w_entity_type_expr - }, - { - "add_component_after_match_and_rematch_w_entity_type_expr_in_progress", - System_w_FromParent_add_component_after_match_and_rematch_w_entity_type_expr_in_progress - }, - { - "adopt_after_match", - System_w_FromParent_adopt_after_match - }, - { - "new_child_after_match", - System_w_FromParent_new_child_after_match - }, - { - "realloc_after_match", - System_w_FromParent_realloc_after_match - } -}; - -bake_test_case System_w_Empty_testcases[] = { - { - "2_column_1_from_id", - System_w_Empty_2_column_1_from_id - }, - { - "3_column_2_from_id", - System_w_Empty_3_column_2_from_id - }, - { - "column_type", - System_w_Empty_column_type - } -}; - -bake_test_case System_w_FromSystem_testcases[] = { - { - "2_column_1_from_system", - System_w_FromSystem_2_column_1_from_system - }, - { - "3_column_2_from_system", - System_w_FromSystem_3_column_2_from_system - }, - { - "auto_add_tag", - System_w_FromSystem_auto_add_tag - } -}; - -bake_test_case System_w_FromEntity_testcases[] = { - { - "2_column_1_from_entity", - System_w_FromEntity_2_column_1_from_entity - }, - { - "task_from_entity", - System_w_FromEntity_task_from_entity - }, - { - "task_not_from_entity", - System_w_FromEntity_task_not_from_entity - } -}; - -bake_test_case Stats_testcases[] = { - { - "get_world_stats", - Stats_get_world_stats - }, - { - "get_pipeline_stats_before_progress_mini_world", - Stats_get_pipeline_stats_before_progress_mini_world - }, - { - "get_pipeline_stats_before_progress", - Stats_get_pipeline_stats_before_progress - }, - { - "get_pipeline_stats_after_progress_no_systems", - Stats_get_pipeline_stats_after_progress_no_systems - }, - { - "get_pipeline_stats_after_progress_1_system", - Stats_get_pipeline_stats_after_progress_1_system - }, - { - "get_pipeline_stats_after_progress_1_inactive_system", - Stats_get_pipeline_stats_after_progress_1_inactive_system - }, - { - "get_pipeline_stats_after_progress_2_systems", - Stats_get_pipeline_stats_after_progress_2_systems - }, - { - "get_pipeline_stats_after_progress_2_systems_one_merge", - Stats_get_pipeline_stats_after_progress_2_systems_one_merge - }, - { - "get_entity_count", - Stats_get_entity_count - }, - { - "get_pipeline_stats_w_task_system", - Stats_get_pipeline_stats_w_task_system - }, - { - "get_not_alive_entity_count", - Stats_get_not_alive_entity_count - } -}; - -bake_test_case Run_testcases[] = { - { - "run", - Run_run - }, - { - "run_w_param", - Run_run_w_param - }, - { - "run_no_match", - Run_run_no_match - }, - { - "run_w_offset", - Run_run_w_offset - }, - { - "run_w_offset_skip_1_archetype", - Run_run_w_offset_skip_1_archetype - }, - { - "run_w_offset_skip_1_archetype_plus_one", - Run_run_w_offset_skip_1_archetype_plus_one - }, - { - "run_w_offset_skip_2_archetypes", - Run_run_w_offset_skip_2_archetypes - }, - { - "run_w_limit_skip_1_archetype", - Run_run_w_limit_skip_1_archetype - }, - { - "run_w_limit_skip_1_archetype_minus_one", - Run_run_w_limit_skip_1_archetype_minus_one - }, - { - "run_w_limit_skip_2_archetypes", - Run_run_w_limit_skip_2_archetypes - }, - { - "run_w_offset_1_limit_max", - Run_run_w_offset_1_limit_max - }, - { - "run_w_offset_1_limit_minus_1", - Run_run_w_offset_1_limit_minus_1 - }, - { - "run_w_offset_2_type_limit_max", - Run_run_w_offset_2_type_limit_max - }, - { - "run_w_offset_2_type_limit_minus_1", - Run_run_w_offset_2_type_limit_minus_1 - }, - { - "run_w_limit_1_all_offsets", - Run_run_w_limit_1_all_offsets - }, - { - "run_w_offset_out_of_bounds", - Run_run_w_offset_out_of_bounds - }, - { - "run_w_limit_out_of_bounds", - Run_run_w_limit_out_of_bounds - }, - { - "run_comb_10_entities_1_type", - Run_run_comb_10_entities_1_type - }, - { - "run_comb_10_entities_2_types", - Run_run_comb_10_entities_2_types - }, - { - "run_w_interrupt", - Run_run_w_interrupt - }, - { - "run_staging", - Run_run_staging + "dont_run_w_unmatching_entity_query", + SystemManual_dont_run_w_unmatching_entity_query } }; -bake_test_case MultiThread_testcases[] = { - { - "2_thread_1_entity", - MultiThread_2_thread_1_entity - }, - { - "2_thread_2_entity", - MultiThread_2_thread_2_entity - }, - { - "2_thread_5_entity", - MultiThread_2_thread_5_entity - }, - { - "2_thread_10_entity", - MultiThread_2_thread_10_entity - }, - { - "3_thread_1_entity", - MultiThread_3_thread_1_entity - }, - { - "3_thread_2_entity", - MultiThread_3_thread_2_entity - }, - { - "3_thread_5_entity", - MultiThread_3_thread_5_entity - }, - { - "3_thread_10_entity", - MultiThread_3_thread_10_entity - }, - { - "4_thread_1_entity", - MultiThread_4_thread_1_entity - }, - { - "4_thread_2_entity", - MultiThread_4_thread_2_entity - }, - { - "4_thread_5_entity", - MultiThread_4_thread_5_entity - }, - { - "4_thread_10_entity", - MultiThread_4_thread_10_entity - }, - { - "5_thread_1_entity", - MultiThread_5_thread_1_entity - }, +bake_test_case Tasks_testcases[] = { { - "5_thread_2_entity", - MultiThread_5_thread_2_entity + "no_components", + Tasks_no_components }, { - "5_thread_5_entity", - MultiThread_5_thread_5_entity + "one_tag", + Tasks_one_tag }, { - "5_thread_10_entity", - MultiThread_5_thread_10_entity + "from_system", + Tasks_from_system }, { - "6_thread_1_entity", - MultiThread_6_thread_1_entity - }, + "tasks_in_phases", + Tasks_tasks_in_phases + } +}; + +bake_test_case System_w_FromParent_testcases[] = { { - "6_thread_2_entity", - MultiThread_6_thread_2_entity + "1_column_from_container", + System_w_FromParent_1_column_from_container }, { - "6_thread_5_entity", - MultiThread_6_thread_5_entity + "2_column_1_from_container", + System_w_FromParent_2_column_1_from_container }, { - "6_thread_10_entity", - MultiThread_6_thread_10_entity + "3_column_2_from_container", + System_w_FromParent_3_column_2_from_container }, { - "2_thread_1_entity_instanced", - MultiThread_2_thread_1_entity_instanced + "2_column_1_from_container_w_not", + System_w_FromParent_2_column_1_from_container_w_not }, { - "2_thread_5_entity_instanced", - MultiThread_2_thread_5_entity_instanced + "2_column_1_from_container_w_not_prefab", + System_w_FromParent_2_column_1_from_container_w_not_prefab }, { - "2_thread_10_entity_instanced", - MultiThread_2_thread_10_entity_instanced + "3_column_1_from_container_1_from_container_w_not", + System_w_FromParent_3_column_1_from_container_1_from_container_w_not }, { - "2_thread_test_combs_100_entity_w_next_worker", - MultiThread_2_thread_test_combs_100_entity_w_next_worker + "2_column_1_from_container_w_or", + System_w_FromParent_2_column_1_from_container_w_or }, { - "2_thread_test_combs_100_entity", - MultiThread_2_thread_test_combs_100_entity + "select_same_from_container", + System_w_FromParent_select_same_from_container }, { - "3_thread_test_combs_100_entity", - MultiThread_3_thread_test_combs_100_entity + "add_component_after_match", + System_w_FromParent_add_component_after_match }, { - "4_thread_test_combs_100_entity", - MultiThread_4_thread_test_combs_100_entity + "add_component_after_match_and_rematch", + System_w_FromParent_add_component_after_match_and_rematch }, { - "5_thread_test_combs_100_entity", - MultiThread_5_thread_test_combs_100_entity + "add_component_after_match_unmatch", + System_w_FromParent_add_component_after_match_unmatch }, { - "6_thread_test_combs_100_entity", - MultiThread_6_thread_test_combs_100_entity + "add_component_after_match_unmatch_match", + System_w_FromParent_add_component_after_match_unmatch_match }, { - "2_thread_test_combs_100_entity_2_types", - MultiThread_2_thread_test_combs_100_entity_2_types + "add_component_after_match_2_systems", + System_w_FromParent_add_component_after_match_2_systems }, { - "3_thread_test_combs_100_entity_2_types", - MultiThread_3_thread_test_combs_100_entity_2_types + "add_component_in_progress_after_match", + System_w_FromParent_add_component_in_progress_after_match }, { - "4_thread_test_combs_100_entity_2_types", - MultiThread_4_thread_test_combs_100_entity_2_types + "add_component_after_match_and_rematch_w_entity_type_expr", + System_w_FromParent_add_component_after_match_and_rematch_w_entity_type_expr }, { - "5_thread_test_combs_100_entity_2_types", - MultiThread_5_thread_test_combs_100_entity_2_types + "add_component_after_match_and_rematch_w_entity_type_expr_in_progress", + System_w_FromParent_add_component_after_match_and_rematch_w_entity_type_expr_in_progress }, { - "6_thread_test_combs_100_entity_2_types", - MultiThread_6_thread_test_combs_100_entity_2_types + "adopt_after_match", + System_w_FromParent_adopt_after_match }, { - "change_thread_count", - MultiThread_change_thread_count + "new_child_after_match", + System_w_FromParent_new_child_after_match }, { - "multithread_quit", - MultiThread_multithread_quit - }, + "realloc_after_match", + System_w_FromParent_realloc_after_match + } +}; + +bake_test_case System_w_Empty_testcases[] = { { - "schedule_w_tasks", - MultiThread_schedule_w_tasks + "2_column_1_from_id", + System_w_Empty_2_column_1_from_id }, { - "reactive_system", - MultiThread_reactive_system + "3_column_2_from_id", + System_w_Empty_3_column_2_from_id }, { - "fini_after_set_threads", - MultiThread_fini_after_set_threads - }, + "column_type", + System_w_Empty_column_type + } +}; + +bake_test_case System_w_FromSystem_testcases[] = { { - "2_threads_single_threaded_system", - MultiThread_2_threads_single_threaded_system + "2_column_1_from_system", + System_w_FromSystem_2_column_1_from_system }, { - "no_staging_w_multithread", - MultiThread_no_staging_w_multithread + "3_column_2_from_system", + System_w_FromSystem_3_column_2_from_system }, { - "multithread_w_monitor_addon", - MultiThread_multithread_w_monitor_addon - }, + "auto_add_tag", + System_w_FromSystem_auto_add_tag + } +}; + +bake_test_case System_w_FromEntity_testcases[] = { { - "get_ctx", - MultiThread_get_ctx + "2_column_1_from_entity", + System_w_FromEntity_2_column_1_from_entity }, { - "get_binding_ctx", - MultiThread_get_binding_ctx + "task_from_entity", + System_w_FromEntity_task_from_entity }, { - "get_ctx_w_run", - MultiThread_get_ctx_w_run - }, + "task_not_from_entity", + System_w_FromEntity_task_not_from_entity + } +}; + +bake_test_case Stats_testcases[] = { { - "get_binding_ctx_w_run", - MultiThread_get_binding_ctx_w_run + "get_world_stats", + Stats_get_world_stats }, { - "bulk_new_in_no_readonly_w_multithread", - MultiThread_bulk_new_in_no_readonly_w_multithread + "get_pipeline_stats_before_progress_mini_world", + Stats_get_pipeline_stats_before_progress_mini_world }, { - "bulk_new_in_no_readonly_w_multithread_2", - MultiThread_bulk_new_in_no_readonly_w_multithread_2 + "get_pipeline_stats_before_progress", + Stats_get_pipeline_stats_before_progress }, { - "run_first_worker_on_main", - MultiThread_run_first_worker_on_main + "get_pipeline_stats_after_progress_no_systems", + Stats_get_pipeline_stats_after_progress_no_systems }, { - "run_single_thread_on_main", - MultiThread_run_single_thread_on_main - } -}; - -bake_test_case MultiThreadStaging_testcases[] = { - { - "2_threads_add_to_current", - MultiThreadStaging_2_threads_add_to_current + "get_pipeline_stats_after_progress_1_system", + Stats_get_pipeline_stats_after_progress_1_system }, { - "3_threads_add_to_current", - MultiThreadStaging_3_threads_add_to_current + "get_pipeline_stats_after_progress_1_inactive_system", + Stats_get_pipeline_stats_after_progress_1_inactive_system }, { - "4_threads_add_to_current", - MultiThreadStaging_4_threads_add_to_current + "get_pipeline_stats_after_progress_2_systems", + Stats_get_pipeline_stats_after_progress_2_systems }, { - "5_threads_add_to_current", - MultiThreadStaging_5_threads_add_to_current + "get_pipeline_stats_after_progress_2_systems_one_merge", + Stats_get_pipeline_stats_after_progress_2_systems_one_merge }, { - "6_threads_add_to_current", - MultiThreadStaging_6_threads_add_to_current + "get_entity_count", + Stats_get_entity_count }, { - "2_threads_on_add", - MultiThreadStaging_2_threads_on_add + "get_pipeline_stats_w_task_system", + Stats_get_pipeline_stats_w_task_system }, { - "new_w_count", - MultiThreadStaging_new_w_count + "get_not_alive_entity_count", + Stats_get_not_alive_entity_count }, { - "custom_thread_auto_merge", - MultiThreadStaging_custom_thread_auto_merge + "progress_stats_systems", + Stats_progress_stats_systems }, { - "custom_thread_manual_merge", - MultiThreadStaging_custom_thread_manual_merge - }, + "progress_stats_systems_w_empty_table_flag", + Stats_progress_stats_systems_w_empty_table_flag + } +}; + +bake_test_case Run_testcases[] = { { - "custom_thread_partial_manual_merge", - MultiThreadStaging_custom_thread_partial_manual_merge + "run", + Run_run }, { - "set_pair_w_new_target_readonly", - MultiThreadStaging_set_pair_w_new_target_readonly + "run_w_param", + Run_run_w_param }, { - "set_pair_w_new_target_tgt_component_readonly", - MultiThreadStaging_set_pair_w_new_target_tgt_component_readonly + "run_no_match", + Run_run_no_match }, { - "set_pair_w_new_target_defer", - MultiThreadStaging_set_pair_w_new_target_defer + "run_w_interrupt", + Run_run_w_interrupt }, { - "set_pair_w_new_target_tgt_component_defer", - MultiThreadStaging_set_pair_w_new_target_tgt_component_defer + "run_staging", + Run_run_staging } }; -bake_test_case MultiTaskThread_testcases[] = { +bake_test_case MultiThread_testcases[] = { { "2_thread_1_entity", - MultiTaskThread_2_thread_1_entity + MultiThread_2_thread_1_entity }, { "2_thread_2_entity", - MultiTaskThread_2_thread_2_entity + MultiThread_2_thread_2_entity }, { "2_thread_5_entity", - MultiTaskThread_2_thread_5_entity + MultiThread_2_thread_5_entity }, { "2_thread_10_entity", - MultiTaskThread_2_thread_10_entity + MultiThread_2_thread_10_entity }, { "3_thread_1_entity", - MultiTaskThread_3_thread_1_entity + MultiThread_3_thread_1_entity }, { "3_thread_2_entity", - MultiTaskThread_3_thread_2_entity + MultiThread_3_thread_2_entity }, { "3_thread_5_entity", - MultiTaskThread_3_thread_5_entity + MultiThread_3_thread_5_entity }, { "3_thread_10_entity", - MultiTaskThread_3_thread_10_entity + MultiThread_3_thread_10_entity }, { "4_thread_1_entity", - MultiTaskThread_4_thread_1_entity + MultiThread_4_thread_1_entity }, { "4_thread_2_entity", - MultiTaskThread_4_thread_2_entity + MultiThread_4_thread_2_entity }, { "4_thread_5_entity", - MultiTaskThread_4_thread_5_entity + MultiThread_4_thread_5_entity }, { "4_thread_10_entity", - MultiTaskThread_4_thread_10_entity + MultiThread_4_thread_10_entity }, { "5_thread_1_entity", - MultiTaskThread_5_thread_1_entity + MultiThread_5_thread_1_entity }, { "5_thread_2_entity", - MultiTaskThread_5_thread_2_entity + MultiThread_5_thread_2_entity }, { "5_thread_5_entity", - MultiTaskThread_5_thread_5_entity + MultiThread_5_thread_5_entity }, { "5_thread_10_entity", - MultiTaskThread_5_thread_10_entity + MultiThread_5_thread_10_entity }, { "6_thread_1_entity", - MultiTaskThread_6_thread_1_entity + MultiThread_6_thread_1_entity }, { "6_thread_2_entity", - MultiTaskThread_6_thread_2_entity + MultiThread_6_thread_2_entity }, { "6_thread_5_entity", - MultiTaskThread_6_thread_5_entity + MultiThread_6_thread_5_entity }, { "6_thread_10_entity", - MultiTaskThread_6_thread_10_entity + MultiThread_6_thread_10_entity }, { "2_thread_1_entity_instanced", - MultiTaskThread_2_thread_1_entity_instanced + MultiThread_2_thread_1_entity_instanced }, { "2_thread_5_entity_instanced", - MultiTaskThread_2_thread_5_entity_instanced + MultiThread_2_thread_5_entity_instanced }, { "2_thread_10_entity_instanced", - MultiTaskThread_2_thread_10_entity_instanced + MultiThread_2_thread_10_entity_instanced }, { "2_thread_test_combs_100_entity_w_next_worker", - MultiTaskThread_2_thread_test_combs_100_entity_w_next_worker + MultiThread_2_thread_test_combs_100_entity_w_next_worker }, { "2_thread_test_combs_100_entity", - MultiTaskThread_2_thread_test_combs_100_entity + MultiThread_2_thread_test_combs_100_entity }, { "3_thread_test_combs_100_entity", - MultiTaskThread_3_thread_test_combs_100_entity + MultiThread_3_thread_test_combs_100_entity }, { "4_thread_test_combs_100_entity", - MultiTaskThread_4_thread_test_combs_100_entity + MultiThread_4_thread_test_combs_100_entity }, { "5_thread_test_combs_100_entity", - MultiTaskThread_5_thread_test_combs_100_entity + MultiThread_5_thread_test_combs_100_entity }, { "6_thread_test_combs_100_entity", - MultiTaskThread_6_thread_test_combs_100_entity + MultiThread_6_thread_test_combs_100_entity }, { "2_thread_test_combs_100_entity_2_types", - MultiTaskThread_2_thread_test_combs_100_entity_2_types + MultiThread_2_thread_test_combs_100_entity_2_types }, { "3_thread_test_combs_100_entity_2_types", - MultiTaskThread_3_thread_test_combs_100_entity_2_types + MultiThread_3_thread_test_combs_100_entity_2_types }, { "4_thread_test_combs_100_entity_2_types", - MultiTaskThread_4_thread_test_combs_100_entity_2_types + MultiThread_4_thread_test_combs_100_entity_2_types }, { "5_thread_test_combs_100_entity_2_types", - MultiTaskThread_5_thread_test_combs_100_entity_2_types + MultiThread_5_thread_test_combs_100_entity_2_types }, { "6_thread_test_combs_100_entity_2_types", - MultiTaskThread_6_thread_test_combs_100_entity_2_types + MultiThread_6_thread_test_combs_100_entity_2_types }, { "change_thread_count", - MultiTaskThread_change_thread_count + MultiThread_change_thread_count }, { "multithread_quit", - MultiTaskThread_multithread_quit + MultiThread_multithread_quit }, { "schedule_w_tasks", - MultiTaskThread_schedule_w_tasks + MultiThread_schedule_w_tasks }, { "reactive_system", - MultiTaskThread_reactive_system + MultiThread_reactive_system }, { "fini_after_set_threads", - MultiTaskThread_fini_after_set_threads + MultiThread_fini_after_set_threads }, { "2_threads_single_threaded_system", - MultiTaskThread_2_threads_single_threaded_system + MultiThread_2_threads_single_threaded_system }, { "no_staging_w_multithread", - MultiTaskThread_no_staging_w_multithread + MultiThread_no_staging_w_multithread }, { "multithread_w_monitor_addon", - MultiTaskThread_multithread_w_monitor_addon + MultiThread_multithread_w_monitor_addon }, { "get_ctx", - MultiTaskThread_get_ctx + MultiThread_get_ctx }, { "get_binding_ctx", - MultiTaskThread_get_binding_ctx + MultiThread_get_binding_ctx }, { "get_ctx_w_run", - MultiTaskThread_get_ctx_w_run + MultiThread_get_ctx_w_run }, { "get_binding_ctx_w_run", - MultiTaskThread_get_binding_ctx_w_run + MultiThread_get_binding_ctx_w_run }, { "bulk_new_in_no_readonly_w_multithread", - MultiTaskThread_bulk_new_in_no_readonly_w_multithread + MultiThread_bulk_new_in_no_readonly_w_multithread }, { "bulk_new_in_no_readonly_w_multithread_2", - MultiTaskThread_bulk_new_in_no_readonly_w_multithread_2 + MultiThread_bulk_new_in_no_readonly_w_multithread_2 }, { "run_first_worker_on_main", - MultiTaskThread_run_first_worker_on_main + MultiThread_run_first_worker_on_main }, { "run_single_thread_on_main", - MultiTaskThread_run_single_thread_on_main + MultiThread_run_single_thread_on_main } }; -bake_test_case MultiTaskThreadStaging_testcases[] = { +bake_test_case MultiThreadStaging_testcases[] = { { "2_threads_add_to_current", - MultiTaskThreadStaging_2_threads_add_to_current + MultiThreadStaging_2_threads_add_to_current }, { "3_threads_add_to_current", - MultiTaskThreadStaging_3_threads_add_to_current + MultiThreadStaging_3_threads_add_to_current }, { "4_threads_add_to_current", - MultiTaskThreadStaging_4_threads_add_to_current + MultiThreadStaging_4_threads_add_to_current }, { "5_threads_add_to_current", - MultiTaskThreadStaging_5_threads_add_to_current + MultiThreadStaging_5_threads_add_to_current }, { "6_threads_add_to_current", - MultiTaskThreadStaging_6_threads_add_to_current + MultiThreadStaging_6_threads_add_to_current }, { "2_threads_on_add", - MultiTaskThreadStaging_2_threads_on_add + MultiThreadStaging_2_threads_on_add }, { "new_w_count", - MultiTaskThreadStaging_new_w_count + MultiThreadStaging_new_w_count }, { "custom_thread_auto_merge", - MultiTaskThreadStaging_custom_thread_auto_merge - }, - { - "custom_thread_manual_merge", - MultiTaskThreadStaging_custom_thread_manual_merge - }, - { - "custom_thread_partial_manual_merge", - MultiTaskThreadStaging_custom_thread_partial_manual_merge + MultiThreadStaging_custom_thread_auto_merge }, { "set_pair_w_new_target_readonly", - MultiTaskThreadStaging_set_pair_w_new_target_readonly + MultiThreadStaging_set_pair_w_new_target_readonly }, { "set_pair_w_new_target_tgt_component_readonly", - MultiTaskThreadStaging_set_pair_w_new_target_tgt_component_readonly + MultiThreadStaging_set_pair_w_new_target_tgt_component_readonly }, { "set_pair_w_new_target_defer", - MultiTaskThreadStaging_set_pair_w_new_target_defer + MultiThreadStaging_set_pair_w_new_target_defer }, { "set_pair_w_new_target_tgt_component_defer", - MultiTaskThreadStaging_set_pair_w_new_target_tgt_component_defer - } -}; - -bake_test_case Snapshot_testcases[] = { - { - "simple_snapshot", - Snapshot_simple_snapshot - }, - { - "snapshot_after_new", - Snapshot_snapshot_after_new - }, - { - "snapshot_after_delete", - Snapshot_snapshot_after_delete - }, - { - "snapshot_after_new_type", - Snapshot_snapshot_after_new_type - }, - { - "snapshot_after_add", - Snapshot_snapshot_after_add - }, - { - "snapshot_after_remove", - Snapshot_snapshot_after_remove - }, - { - "snapshot_w_include_filter", - Snapshot_snapshot_w_include_filter - }, - { - "snapshot_w_exclude_filter", - Snapshot_snapshot_w_exclude_filter - }, - { - "snapshot_w_filter_after_new", - Snapshot_snapshot_w_filter_after_new - }, - { - "snapshot_w_filter_after_delete", - Snapshot_snapshot_w_filter_after_delete - }, - { - "snapshot_free_empty", - Snapshot_snapshot_free_empty - }, - { - "snapshot_free", - Snapshot_snapshot_free - }, - { - "snapshot_free_filtered", - Snapshot_snapshot_free_filtered - }, - { - "snapshot_free_filtered_w_dtor", - Snapshot_snapshot_free_filtered_w_dtor - }, - { - "snapshot_activate_table_w_filter", - Snapshot_snapshot_activate_table_w_filter - }, - { - "snapshot_copy", - Snapshot_snapshot_copy - }, - { - "snapshot_get_ref_after_restore", - Snapshot_snapshot_get_ref_after_restore - }, - { - "new_after_snapshot", - Snapshot_new_after_snapshot - }, - { - "new_empty_after_snapshot", - Snapshot_new_empty_after_snapshot - }, - { - "add_after_snapshot", - Snapshot_add_after_snapshot - }, - { - "delete_after_snapshot", - Snapshot_delete_after_snapshot - }, - { - "set_after_snapshot", - Snapshot_set_after_snapshot - }, - { - "restore_recycled", - Snapshot_restore_recycled - }, - { - "snapshot_w_new_in_onset", - Snapshot_snapshot_w_new_in_onset - }, - { - "snapshot_w_new_in_onset_in_snapshot_table", - Snapshot_snapshot_w_new_in_onset_in_snapshot_table - }, - { - "snapshot_from_stage", - Snapshot_snapshot_from_stage + MultiThreadStaging_set_pair_w_new_target_tgt_component_defer } }; @@ -8403,6 +2098,10 @@ bake_test_case Modules_testcases[] = { { "import_2_worlds", Modules_import_2_worlds + }, + { + "component_parent_becomes_module", + Modules_component_parent_becomes_module } }; @@ -8460,6 +2159,70 @@ bake_test_case Rest_testcases[] = { { "get", Rest_get + }, + { + "get_cached", + Rest_get_cached + }, + { + "get_cached_invalid", + Rest_get_cached_invalid + }, + { + "try_query", + Rest_try_query + }, + { + "query", + Rest_query + }, + { + "named_query", + Rest_named_query + }, + { + "tables", + Rest_tables + }, + { + "request_commands", + Rest_request_commands + }, + { + "request_commands_2_syncs", + Rest_request_commands_2_syncs + }, + { + "request_commands_no_frames", + Rest_request_commands_no_frames + }, + { + "request_commands_no_commands", + Rest_request_commands_no_commands + }, + { + "request_commands_garbage_collect", + Rest_request_commands_garbage_collect + }, + { + "script_error", + Rest_script_error + }, + { + "import_rest_after_mini", + Rest_import_rest_after_mini + }, + { + "get_pipeline_stats_after_delete_system", + Rest_get_pipeline_stats_after_delete_system + }, + { + "request_world_summary_before_monitor_sys_run", + Rest_request_world_summary_before_monitor_sys_run + }, + { + "escape_backslash", + Rest_escape_backslash } }; @@ -8761,105 +2524,37 @@ bake_test_case Alerts_testcases[] = { } }; +const char* MultiThread_worker_kind_param[] = {"thread", "task"}; +bake_test_param MultiThread_params[] = { + {"worker_kind", (char**)MultiThread_worker_kind_param, 2} +}; +const char* MultiThreadStaging_worker_kind_param[] = {"thread", "task"}; +bake_test_param MultiThreadStaging_params[] = { + {"worker_kind", (char**)MultiThreadStaging_worker_kind_param, 2} +}; + static bake_test_suite suites[] = { - { - "Parser", - NULL, - NULL, - 235, - Parser_testcases - }, - { - "Plecs", - NULL, - NULL, - 237, - Plecs_testcases - }, { "Doc", NULL, NULL, - 10, + 12, Doc_testcases }, { "Pipeline", NULL, NULL, - 82, + 85, Pipeline_testcases }, { "SystemMisc", NULL, NULL, - 68, + 69, SystemMisc_testcases }, - { - "RulesBasic", - NULL, - NULL, - 145, - RulesBasic_testcases - }, - { - "RulesVariables", - NULL, - NULL, - 161, - RulesVariables_testcases - }, - { - "RulesOperators", - NULL, - NULL, - 95, - RulesOperators_testcases - }, - { - "RulesTransitive", - NULL, - NULL, - 64, - RulesTransitive_testcases - }, - { - "RulesComponentInheritance", - NULL, - NULL, - 36, - RulesComponentInheritance_testcases - }, - { - "RulesRecycled", - NULL, - NULL, - 4, - RulesRecycled_testcases - }, - { - "RulesBuiltinPredicates", - NULL, - NULL, - 70, - RulesBuiltinPredicates_testcases - }, - { - "RulesScopes", - NULL, - NULL, - 10, - RulesScopes_testcases - }, - { - "RulesTraversal", - NULL, - NULL, - 92, - RulesTraversal_testcases - }, { "SystemPeriodic", NULL, @@ -8885,7 +2580,7 @@ static bake_test_suite suites[] = { "SystemManual", SystemManual_setup, NULL, - 3, + 2, SystemManual_testcases }, { @@ -8927,14 +2622,14 @@ static bake_test_suite suites[] = { "Stats", NULL, NULL, - 11, + 13, Stats_testcases }, { "Run", Run_setup, NULL, - 21, + 5, Run_testcases }, { @@ -8942,41 +2637,24 @@ static bake_test_suite suites[] = { MultiThread_setup, NULL, 50, - MultiThread_testcases + MultiThread_testcases, + 1, + MultiThread_params }, { "MultiThreadStaging", MultiThreadStaging_setup, NULL, - 14, - MultiThreadStaging_testcases - }, - { - "MultiTaskThread", - MultiTaskThread_setup, - NULL, - 50, - MultiTaskThread_testcases - }, - { - "MultiTaskThreadStaging", - MultiTaskThreadStaging_setup, - NULL, - 14, - MultiTaskThreadStaging_testcases - }, - { - "Snapshot", - NULL, - NULL, - 26, - Snapshot_testcases + 12, + MultiThreadStaging_testcases, + 1, + MultiThreadStaging_params }, { "Modules", Modules_setup, NULL, - 23, + 24, Modules_testcases }, { @@ -8997,7 +2675,7 @@ static bake_test_suite suites[] = { "Rest", NULL, NULL, - 2, + 18, Rest_testcases }, { @@ -9017,5 +2695,5 @@ static bake_test_suite suites[] = { }; int main(int argc, char *argv[]) { - return bake_test_run("addons", argc, argv, suites, 36); + return bake_test_run("addons", argc, argv, suites, 22); } diff --git a/vendors/flecs/test/addons/src/util.c b/vendors/flecs/test/addons/src/util.c index bb9edd211..a5270e72e 100644 --- a/vendors/flecs/test/addons/src/util.c +++ b/vendors/flecs/test/addons/src/util.c @@ -20,10 +20,10 @@ void probe_system_w_ctx( int i; for (i = 0; i < ctx->term_count; i ++) { ctx->c[ctx->invoked][i] = it->ids[i]; - ctx->s[ctx->invoked][i] = ecs_field_src(it, i + 1); + ctx->s[ctx->invoked][i] = ecs_field_src(it, i); - ecs_id_t e = ecs_field_id(it, i + 1); - test_assert(e != 0); + ecs_id_t field_id = ecs_field_id(it, i); + test_assert(field_id != 0); } for (i = 0; i < it->count; i ++) { @@ -62,7 +62,7 @@ void probe_has_entity(Probe *probe, ecs_entity_t e) { void install_test_abort(void) { ecs_os_set_api_defaults(); - ecs_os_api_t os_api = ecs_os_api; + ecs_os_api_t os_api = ecs_os_get_api(); os_api.abort_ = test_abort; ecs_os_set_api(&os_api); ecs_log_set_level(-5); @@ -76,13 +76,13 @@ const ecs_entity_t* bulk_new_w_type( ecs_id_t *ids = type->array; int i = 0; - while ((ecs_id_get_flags(world, ids[i]) & EcsIdDontInherit)) { + while ((ecs_id_get_flags(world, ids[i]) & EcsIdOnInstantiateDontInherit)) { i ++; } const ecs_entity_t *result = ecs_bulk_new_w_id(world, ids[i], count); for (; i < type->count; i ++) { for (int e = 0; e < count; e ++) { - if (ecs_id_get_flags(world, ids[i]) & EcsIdDontInherit) { + if (ecs_id_get_flags(world, ids[i]) & EcsIdOnInstantiateDontInherit) { continue; } ecs_add_id(world, result[e], ids[i]); @@ -91,229 +91,3 @@ const ecs_entity_t* bulk_new_w_type( return result; } - -int32_t find_entity( - ecs_world_t *world, - test_iter_result_t *expect, - ecs_entity_t e) -{ - int i; - for (i = 0; i < ITER_MAX_ENTITIES; i ++) { - if (expect->entities[i] == e) { - while (expect->matched[i]) { - i ++; - if (!if_test_assert(e == expect->entities[i])) { - return -1; - } - } - - if (expect->entity_names[i]) { - if (!if_test_str(ecs_get_name(world, e), expect->entity_names[i])) { - return -1; - } - } - return i; - } - } - - for (i = 0; i < ITER_MAX_ENTITIES; i ++) { - if (!expect->entity_names[i]) { - break; - } - - if (!strcmp(ecs_get_name(world, e), expect->entity_names[i])) { - while (expect->matched[i]) { - i ++; - - // If this fails, the entity is encountered more times than - // expected. - if (!if_test_str(ecs_get_name(world, e), - expect->entity_names[i])) - { - return -1; - } - } - - return i; - } - } - - return -1; -} - -bool test_iter( - ecs_iter_t *it, - ecs_iter_next_action_t next, - test_iter_result_t *expect) -{ - int32_t entity_index = -1; - - while (next(it)) { - int i; - - for (i = 0; (i < it->count) || (i < 1); i ++) { - ecs_entity_t e = 0; - int t; - - if (it->count) { - e = it->entities[i]; - - entity_index = find_entity(it->world, expect, e); - - // Matched unexpected entity - test_assert(entity_index != -1); - - expect->matched[entity_index] = true; - - // Test data - for (t = 0; t < it->field_count; t++) { - size_t size = ecs_field_size(it, t + 1); - if (!size) { - continue; - } - - void *expect_ptr = expect->term_columns[t]; - if (!expect_ptr) { - continue; - } - - expect_ptr = ECS_OFFSET(expect_ptr, size * entity_index); - - void *component_ptr = ecs_field_w_size(it, size, t + 1); - if (!if_test_assert(component_ptr != NULL)) { - return false; - } - - component_ptr = ECS_OFFSET(component_ptr, size * i); - if (!if_test_assert(memcpy(component_ptr, expect_ptr, size))) { - return false; - } - } - } else { - entity_index ++; - } - - - // Test ids - ecs_id_t *ids = expect->term_ids[entity_index]; - if (!ids[0]) { - ids = expect->term_ids[0]; - } - - for (t = 0; t < it->field_count; t++) { - if (!ids[t]) { - break; - } - - if (!if_test_assert(ecs_field_id(it, t + 1) == ids[t])) { - return false; - } - } - - if (!if_test_assert(ids[t] == 0)) { - return false; - } - - - // Test ids by expr - char **ids_expect = expect->term_ids_expr[entity_index]; - if (!ids_expect) { - ids_expect = expect->term_ids_expr[0]; - } - - for (t = 0; t < it->field_count; t++) { - if (!ids_expect[t]) { - break; - } - - char *id_found = ecs_id_str(it->world, ecs_field_id(it, t + 1)); - if (!if_test_str(id_found, ids_expect[t])) { - printf(" - term %d\n", t); - if (e) { - printf(" - matched entity %u (%s, [%s])\n", - (uint32_t)e, - ecs_get_name(it->world, e), - ecs_type_str(it->world, ecs_get_type(it->world, e))); - - if (expect->entities[i]) { - printf(" - expected entity %u (%s)\n", - (uint32_t)expect->entities[i], - ecs_get_name(it->world, expect->entities[i])); - } else if (expect->entity_names[i]) { - printf(" - expected entity %s\n", - expect->entity_names[i]); - } - } - - printf(" - @ result index %d\n", entity_index); - return false; - } - ecs_os_free(id_found); - } - - if (!if_test_assert(ids_expect[t] == NULL)) { - return false; - } - - - // Test variables - int v; - for (v = 0; v < ITER_MAX_VARIABLES; v++) { - int32_t id = expect->variables[v].id; - if (!id) { - break; - } - - ecs_entity_t e = expect->variables[v].entities[entity_index]; - if (!e) { - e = expect->variables[v].entities[0]; - } - if (e) { - ecs_entity_t var = ecs_iter_get_var(it, id); - if (!if_test_assert(e == var)) { - return false; - } - } - - const char *name = expect->variables[v].entity_names[entity_index]; - if (!name) { - name = expect->variables[v].entity_names[0]; - } - if (name) { - ecs_entity_t var = ecs_iter_get_var(it, id); - if (!if_test_str(name, ecs_get_name(it->world, var))) { - printf(" - variable id %d\n", id); - printf(" - index %d\n", entity_index); - return false; - } - } - - /* If a variable id is set, either an entity or entity name must - * be set. */ - if (!if_test_assert(e || name)) { - return false; - } - } - } - - expect->table_count_actual ++; - } - - for (int i = 0; i < ITER_MAX_ENTITIES; i ++) { - if (expect->entities[i] || expect->entity_names[i]) { - if (!if_test_assert(expect->matched[i])) { - printf(" - entity %u (%s) at index %d not matched\n", - (uint32_t)expect->entities[i], expect->entity_names[i], i); - return false; - } - } - } - - if (expect->table_count_expect) { - if (!if_test_assert(expect->table_count_actual == expect->table_count_expect)) { - return false; - } - } - - return true; -} diff --git a/vendors/flecs/test/api/src/EnabledComponents.c b/vendors/flecs/test/api/src/EnabledComponents.c deleted file mode 100644 index 05400fbae..000000000 --- a/vendors/flecs/test/api/src/EnabledComponents.c +++ /dev/null @@ -1,1438 +0,0 @@ -#include -#include - -void EnabledComponents_is_component_enabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_fini(world); -} - -void EnabledComponents_is_empty_entity_disabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, 0); - - test_bool(ecs_is_enabled_component(world, e, Position), false); - - ecs_fini(world); -} - -void EnabledComponents_is_0_entity_disabled(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - test_expect_abort(); - - test_bool(ecs_is_enabled_component(world, 0, Position), false); -} - -void EnabledComponents_is_0_component_disabled(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, 0); - - test_expect_abort(); - - test_bool(ecs_is_enabled_id(world, e, 0), false); -} - -void EnabledComponents_is_nonexist_component_disabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_new(world, Velocity); - - test_bool(ecs_is_enabled_component(world, e, Position), false); - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_component_enabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_fini(world); -} - -void EnabledComponents_is_disabled_component_enabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - - ecs_enable_component(world, e, Position, false); - test_bool(ecs_is_enabled_component(world, e, Position), false); - - ecs_fini(world); -} - -void EnabledComponents_has_enabled_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - - ecs_enable_component(world, e, Position, true); - - test_bool(ecs_has_id(world, e, ECS_TOGGLE | ecs_id(Position)), true); - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_after_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_add(world, e, Position); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_has_id(world, e, ECS_TOGGLE | ecs_id(Position)), true); - test_bool(ecs_has(world, e, Position), true); - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_after_remove(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_remove(world, e, Position); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_has_id(world, e, ECS_TOGGLE | ecs_id(Position)), true); - test_bool(ecs_has(world, e, Position), false); - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_after_disable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_enable_component(world, e, Position, false); - test_bool(ecs_is_enabled_component(world, e, Position), false); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_fini(world); -} - -void EnabledComponents_is_disabled_after_enable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - - ecs_enable_component(world, e, Position, false); - test_bool(ecs_is_enabled_component(world, e, Position), false); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_enable_component(world, e, Position, false); - test_bool(ecs_is_enabled_component(world, e, Position), false); - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_randomized(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t entities[100000]; - bool enabled[100000]; - - int32_t i; - for (i = 0; i < 100000; i ++) { - enabled[i] = rand() % 2; - entities[i] = ecs_new_id(world); - ecs_enable_component(world, entities[i], Position, enabled[i]); - } - - for (i = 0; i < 100000; i ++) { - test_bool( - ecs_is_enabled_component(world, entities[i], Position), enabled[i]); - } - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_after_add_randomized(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t entities[100000]; - bool enabled[100000]; - - int32_t i; - for (i = 0; i < 100000; i ++) { - enabled[i] = rand() % 2; - entities[i] = ecs_new_id(world); - ecs_enable_component(world, entities[i], Position, enabled[i]); - } - - for (i = 0; i < 100000; i ++) { - ecs_add(world, entities[i], Position); - } - - for (i = 0; i < 100000; i ++) { - test_bool( - ecs_is_enabled_component(world, entities[i], Position), enabled[i]); - } - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_after_randomized_add_randomized(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t entities[100000]; - bool enabled[100000]; - - int32_t i; - for (i = 0; i < 100000; i ++) { - enabled[i] = rand() % 2; - entities[i] = ecs_new_id(world); - ecs_enable_component(world, entities[i], Position, enabled[i]); - } - - for (i = 0; i < 100000; i ++) { - if (!(rand() % 5)) { - ecs_add(world, entities[i], Position); - } - } - - for (i = 0; i < 100000; i ++) { - test_bool( - ecs_is_enabled_component(world, entities[i], Position), enabled[i]); - } - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_2(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_new(world, 0); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_enable_component(world, e, Velocity, false); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_is_enabled_component(world, e, Velocity), false); - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_3(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t e = ecs_new(world, 0); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_enable_component(world, e, Velocity, false); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_is_enabled_component(world, e, Velocity), false); - - ecs_enable_component(world, e, Mass, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_is_enabled_component(world, e, Velocity), false); - test_bool(ecs_is_enabled_component(world, e, Mass), true); - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_2_after_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_new(world, 0); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_enable_component(world, e, Velocity, false); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_is_enabled_component(world, e, Velocity), false); - - ecs_add(world, e, Position); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_is_enabled_component(world, e, Velocity), false); - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_3_after_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t e = ecs_new(world, 0); - - ecs_enable_component(world, e, Position, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_enable_component(world, e, Velocity, false); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_is_enabled_component(world, e, Velocity), false); - - ecs_enable_component(world, e, Mass, true); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_is_enabled_component(world, e, Velocity), false); - test_bool(ecs_is_enabled_component(world, e, Mass), true); - - ecs_add(world, e, Position); - test_bool(ecs_is_enabled_component(world, e, Position), true); - test_bool(ecs_is_enabled_component(world, e, Velocity), false); - test_bool(ecs_is_enabled_component(world, e, Mass), true); - - ecs_fini(world); -} - -void EnabledComponents_is_pair_enabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t obj = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, ecs_id(Position), obj); - - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_fini(world); -} - -void EnabledComponents_is_enabled_pair_enabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_fini(world); -} - -void EnabledComponents_is_disabled_pair_enabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, false); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), false); - - ecs_fini(world); -} - -void EnabledComponents_has_enabled_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_has_id(world, e, ECS_TOGGLE | ecs_pair(ecs_id(Position), obj)), true); - - ecs_fini(world); -} - -void EnabledComponents_is_pair_enabled_after_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_add_pair(world, e, ecs_id(Position), obj); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_has_id(world, e, ECS_TOGGLE | ecs_pair(ecs_id(Position), obj)), true); - test_bool(ecs_has_pair(world, e, ecs_id(Position), obj), true); - - ecs_fini(world); -} - -void EnabledComponents_is_pair_enabled_after_remove(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_remove_pair(world, e, ecs_id(Position), obj); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_has_id(world, e, ECS_TOGGLE | ecs_pair(ecs_id(Position), obj)), true); - test_bool(ecs_has_pair(world, e, ecs_id(Position), obj), false); - - ecs_fini(world); -} - -void EnabledComponents_is_pair_enabled_after_disable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_enable_pair(world, e, Position, obj, false); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), false); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_fini(world); -} - -void EnabledComponents_is_pair_disabled_after_enable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, false); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), false); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_enable_pair(world, e, Position, obj, false); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), false); - - ecs_fini(world); -} - -void EnabledComponents_is_pair_enabled_2(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_new(world, 0); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_enable_pair(world, e, Velocity, obj, false); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_is_enabled_pair(world, e, Velocity, obj), false); - - ecs_fini(world); -} - -void EnabledComponents_is_pair_enabled_3(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t e = ecs_new(world, 0); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_enable_pair(world, e, Velocity, obj, false); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_is_enabled_pair(world, e, Velocity, obj), false); - - ecs_enable_pair(world, e, Mass, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_is_enabled_pair(world, e, Velocity, obj), false); - test_bool(ecs_is_enabled_pair(world, e, Mass, obj), true); - - ecs_fini(world); -} - -void EnabledComponents_is_pair_enabled_2_after_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_new(world, 0); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_enable_pair(world, e, Velocity, obj, false); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_is_enabled_pair(world, e, Velocity, obj), false); - - ecs_add_pair(world, e, ecs_id(Position), obj); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_is_enabled_pair(world, e, Velocity, obj), false); - - ecs_fini(world); -} - -void EnabledComponents_is_pair_enabled_3_after_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t e = ecs_new(world, 0); - ecs_entity_t obj = ecs_new_id(world); - - ecs_enable_pair(world, e, Position, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - - ecs_enable_pair(world, e, Velocity, obj, false); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_is_enabled_pair(world, e, Velocity, obj), false); - - ecs_enable_pair(world, e, Mass, obj, true); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_is_enabled_pair(world, e, Velocity, obj), false); - test_bool(ecs_is_enabled_pair(world, e, Mass, obj), true); - - ecs_add_pair(world, e, ecs_id(Position), obj); - test_bool(ecs_is_enabled_pair(world, e, Position, obj), true); - test_bool(ecs_is_enabled_pair(world, e, Velocity, obj), false); - test_bool(ecs_is_enabled_pair(world, e, Mass, obj), true); - - ecs_fini(world); -} - -void EnabledComponents_query_disabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_enable_component(world, e1, Position, true); - ecs_enable_component(world, e2, Position, false); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t table_count = 0, count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != e2); - test_assert(it.entities[i] == e1 || it.entities[i] == e3); - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - } - count += it.count; - table_count ++; - } - - test_int(count, 2); - test_int(table_count, 2); - - ecs_fini(world); -} - -void EnabledComponents_query_disabled_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new_w_pair(world, ecs_id(Position), Tgt); - ecs_entity_t e2 = ecs_new_w_pair(world, ecs_id(Position), Tgt); - ecs_entity_t e3 = ecs_new_w_pair(world, ecs_id(Position), Tgt); - - ecs_enable_pair(world, e1, Position, Tgt, true); - ecs_enable_pair(world, e2, Position, Tgt, false); - - ecs_query_t *q = ecs_query_new(world, "(Position, Tgt)"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t table_count = 0, count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != e2); - test_assert(it.entities[i] == e1 || it.entities[i] == e3); - test_assert(ecs_is_enabled_pair(world, it.entities[i], Position, Tgt)); - } - count += it.count; - table_count ++; - } - - test_int(count, 2); - test_int(table_count, 2); - - ecs_fini(world); -} - -void EnabledComponents_query_disabled_skip_initial(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_enable_component(world, e1, Position, false); - ecs_enable_component(world, e2, Position, true); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != e1); - test_assert(it.entities[i] == e2 || it.entities[i] == e3); - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - } - count += it.count; - } - - test_int(count, 2); - - ecs_fini(world); -} - -void EnabledComponents_query_disabled_pair_skip_initial(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new_w_pair(world, ecs_id(Position), Tgt); - ecs_entity_t e2 = ecs_new_w_pair(world, ecs_id(Position), Tgt); - ecs_entity_t e3 = ecs_new_w_pair(world, ecs_id(Position), Tgt); - - ecs_enable_pair(world, e1, Position, Tgt, false); - ecs_enable_pair(world, e2, Position, Tgt, true); - - ecs_query_t *q = ecs_query_new(world, "(Position, Tgt)"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != e1); - test_assert(it.entities[i] == e2 || it.entities[i] == e3); - test_assert(ecs_is_enabled_pair(world, it.entities[i], Position, Tgt)); - } - count += it.count; - } - - test_int(count, 2); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_2(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - int32_t i, total_count = 0; - for (i = 0; i < 64; i ++) { - ecs_entity_t e = ecs_new(world, Position); - if (!(e % 2)) { - ecs_enable_component(world, e, Position, false); - } else { - ecs_enable_component(world, e, Position, true); - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] % 2); - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_8(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - if (!(e % 8)) { - ecs_enable_component(world, e, Position, false); - } else { - ecs_enable_component(world, e, Position, true); - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] % 8); - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_64(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - if (!(e % 64)) { - ecs_enable_component(world, e, Position, false); - } else { - ecs_enable_component(world, e, Position, true); - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] % 64); - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_256(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - if (!(e % 256)) { - ecs_enable_component(world, e, Position, false); - } else { - ecs_enable_component(world, e, Position, true); - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] % 256); - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_1024(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - int32_t i, total_count = 0; - for (i = 0; i < 100000; i ++) { - ecs_entity_t e = ecs_new(world, Position); - if (!(e % 1024)) { - ecs_enable_component(world, e, Position, false); - } else { - ecs_enable_component(world, e, Position, true); - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] % 1024); - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_enable_mod_10(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - if (!(e % 10)) { - ecs_enable_component(world, e, Position, true); - total_count ++; - } else { - ecs_enable_component(world, e, Position, false); - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(!(it.entities[i] % 10)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_2_2_bitsets(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - if (!(e % 2)) { - ecs_enable_component(world, e, Position, true); - } else { - ecs_enable_component(world, e, Position, false); - } - - if (!(e % 3)) { - ecs_enable_component(world, e, Velocity, true); - } else { - ecs_enable_component(world, e, Velocity, false); - } - - if (!(e % 2) && !(e % 3)) { - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Velocity)); - test_assert(!(it.entities[i] % 2) && !(it.entities[i] % 3)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_8_2_bitsets(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - if (!(e % 8)) { - ecs_enable_component(world, e, Position, true); - } else { - ecs_enable_component(world, e, Position, false); - } - - if (!(e % 4)) { - ecs_enable_component(world, e, Velocity, true); - } else { - ecs_enable_component(world, e, Velocity, false); - } - - if (!(e % 8) && !(e % 4)) { - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Velocity)); - test_assert(!(it.entities[i] % 8) && !(it.entities[i] % 4)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_64_2_bitsets(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - if (!(e % 64)) { - ecs_enable_component(world, e, Position, true); - } else { - ecs_enable_component(world, e, Position, false); - } - - if (!(e % 16)) { - ecs_enable_component(world, e, Velocity, true); - } else { - ecs_enable_component(world, e, Velocity, false); - } - - if (!(e % 64) && !(e % 16)) { - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Velocity)); - test_assert(!(it.entities[i] % 64) && !(it.entities[i] % 16)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_256_2_bitsets(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - if (!(e % 256)) { - ecs_enable_component(world, e, Position, true); - } else { - ecs_enable_component(world, e, Position, false); - } - - if (!(e % 64)) { - ecs_enable_component(world, e, Velocity, true); - } else { - ecs_enable_component(world, e, Velocity, false); - } - - if (!(e % 256) && !(e % 64)) { - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Velocity)); - test_assert(!(it.entities[i] % 256) && !(it.entities[i] % 64)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_mod_1024_2_bitsets(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - if (!(e % 1024)) { - ecs_enable_component(world, e, Position, true); - } else { - ecs_enable_component(world, e, Position, false); - } - - if (!(e % 128)) { - ecs_enable_component(world, e, Velocity, true); - } else { - ecs_enable_component(world, e, Velocity, false); - } - - if (!(e % 1024) && !(e % 128)) { - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Velocity)); - test_assert(!(it.entities[i] % 1024) && !(it.entities[i] % 128)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_randomized_2_bitsets(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - - bool enable_1 = rand() % 2; - ecs_enable_component(world, e, Position, enable_1); - bool enable_2 = rand() % 2; - ecs_enable_component(world, e, Velocity, enable_2); - - if (enable_1 && enable_2) { - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Velocity)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_randomized_3_bitsets(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - ecs_add(world, e, Mass); - - bool enable_1 = rand() % 2; - ecs_enable_component(world, e, Position, enable_1); - bool enable_2 = rand() % 2; - ecs_enable_component(world, e, Velocity, enable_2); - bool enable_3 = rand() % 2; - ecs_enable_component(world, e, Mass, enable_3); - - if (enable_1 && enable_2 && enable_3) { - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity, Mass"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Velocity)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Mass)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_query_randomized_4_bitsets(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT(world, Rotation); - - int32_t i, total_count = 0; - for (i = 0; i < 65536; i ++) { - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - ecs_add(world, e, Mass); - ecs_add(world, e, Rotation); - - bool enable_1 = rand() % 2; - ecs_enable_component(world, e, Position, enable_1); - bool enable_2 = rand() % 2; - ecs_enable_component(world, e, Velocity, enable_2); - bool enable_3 = rand() % 2; - ecs_enable_component(world, e, Mass, enable_3); - bool enable_4 = rand() % 2; - ecs_enable_component(world, e, Rotation, enable_4); - - if (enable_1 && enable_2 && enable_3 && enable_4) { - total_count ++; - } - } - - test_assert(total_count != 0); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity, Mass, Rotation"); - ecs_iter_t it = ecs_query_iter(world, q); - - int32_t count = 0; - - while (ecs_query_next(&it)) { - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_is_enabled_component(world, it.entities[i], Position)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Velocity)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Mass)); - test_assert(ecs_is_enabled_component(world, it.entities[i], Rotation)); - } - count += it.count; - } - - test_int(count, total_count); - - ecs_fini(world); -} - -void EnabledComponents_defer_enable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - test_bool(ecs_is_enabled_component(world, e, Position), true); - - ecs_defer_begin(world); - ecs_enable_component(world, e, Position, false); - test_bool(ecs_is_enabled_component(world, e, Position), true); - ecs_defer_end(world); - - test_bool(ecs_is_enabled_component(world, e, Position), false); - - ecs_fini(world); -} - -static -int compare_position(ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2) { - const Position *p1 = ptr1; - const Position *p2 = ptr2; - return (p1->x > p2->x) - (p1->x < p2->x); -} - -void EnabledComponents_sort(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 2}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {2, 2}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {0, 2}); - - ecs_enable_component(world, e1, Position, true); - ecs_enable_component(world, e2, Position, true); - ecs_enable_component(world, e3, Position, false); - ecs_enable_component(world, e4, Position, false); - - test_bool(ecs_is_enabled_component(world, e1, Position), true); - test_bool(ecs_is_enabled_component(world, e2, Position), true); - test_bool(ecs_is_enabled_component(world, e3, Position), false); - test_bool(ecs_is_enabled_component(world, e4, Position), false); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position - }); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 2); - test_assert(it.entities[0] == e2); - test_assert(it.entities[1] == e1); - test_assert(!ecs_query_next(&it)); - - /* Entities will have shuffled around, ensure bits got shuffled too */ - test_bool(ecs_is_enabled_component(world, e1, Position), true); - test_bool(ecs_is_enabled_component(world, e2, Position), true); - test_bool(ecs_is_enabled_component(world, e3, Position), false); - test_bool(ecs_is_enabled_component(world, e4, Position), false); - - ecs_fini(world); -} - -void EnabledComponents_table_move_2_from_3(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query(world, { .filter.expr = "Position" }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_enable_component(world, e1, Position, true); - ecs_enable_component(world, e2, Position, false); - ecs_enable_component(world, e3, Position, true); - - test_bool(ecs_is_enabled_component(world, e1, Position), true); - test_bool(ecs_is_enabled_component(world, e2, Position), false); - test_bool(ecs_is_enabled_component(world, e3, Position), true); - - ecs_add(world, e3, Tag); - ecs_add(world, e2, Tag); - - test_bool(ecs_is_enabled_component(world, e1, Position), true); - test_bool(ecs_is_enabled_component(world, e2, Position), false); - test_bool(ecs_is_enabled_component(world, e3, Position), true); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 10); test_int(p->y, 20); - } - { - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 30); test_int(p->y, 40); - } - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/Filter.c b/vendors/flecs/test/api/src/Filter.c deleted file mode 100644 index d7c64a14f..000000000 --- a/vendors/flecs/test/api/src/Filter.c +++ /dev/null @@ -1,11687 +0,0 @@ -#include - -void Filter_filter_1_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{TagA}} - }); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].inout, EcsInOutDefault); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, TagA); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }} - }); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_id(Position)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].inout, EcsInOutDefault); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, ecs_id(Position)); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_2_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{TagA}, {TagB}} - }); - - test_int(f.term_count, 2); - test_int(f.field_count, 2); - test_assert(f.terms != NULL); - - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, TagA); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - test_int(f.terms[1].id, TagB); - test_int(f.terms[1].oper, EcsAnd); - test_int(f.terms[1].field_index, 1); - test_int(f.terms[1].first.id, TagB); - test_int(f.terms[1].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[1].src.id, EcsThis); - test_int(f.terms[1].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[1].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_3_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{TagA}, {TagB}, {TagC}} - }); - - test_int(f.term_count, 3); - test_int(f.field_count, 3); - test_assert(f.terms != NULL); - - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, TagA); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - test_int(f.terms[1].id, TagB); - test_int(f.terms[1].oper, EcsAnd); - test_int(f.terms[1].field_index, 1); - test_int(f.terms[1].first.id, TagB); - test_int(f.terms[1].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[1].src.id, EcsThis); - test_int(f.terms[1].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[1].src.trav, EcsIsA); - - test_int(f.terms[2].id, TagC); - test_int(f.terms[2].oper, EcsAnd); - test_int(f.terms[2].field_index, 2); - test_int(f.terms[2].first.id, TagC); - test_int(f.terms[2].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[2].src.id, EcsThis); - test_int(f.terms[2].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[2].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_3_terms_w_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{TagA}, {TagB, .oper = EcsOr}, {TagC }} - }); - - test_int(f.term_count, 3); - test_int(f.field_count, 2); - test_assert(f.terms != NULL); - - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, TagA); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - test_int(f.terms[1].id, TagB); - test_int(f.terms[1].oper, EcsOr); - test_int(f.terms[1].field_index, 1); - test_int(f.terms[1].first.id, TagB); - test_int(f.terms[1].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[1].src.id, EcsThis); - test_int(f.terms[1].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[1].src.trav, EcsIsA); - - test_int(f.terms[2].id, TagC); - test_int(f.terms[2].oper, EcsAnd); - test_int(f.terms[2].field_index, 1); - test_int(f.terms[2].first.id, TagC); - test_int(f.terms[2].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[2].src.id, EcsThis); - test_int(f.terms[2].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[2].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_4_terms_w_or_at_1(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{TagA}, {TagB, .oper = EcsOr}, {TagC}, {TagD}} - }); - - test_int(f.term_count, 4); - test_int(f.field_count, 3); - test_assert(f.terms != NULL); - - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, TagA); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - test_int(f.terms[1].id, TagB); - test_int(f.terms[1].oper, EcsOr); - test_int(f.terms[1].field_index, 1); - test_int(f.terms[1].first.id, TagB); - test_int(f.terms[1].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[1].src.id, EcsThis); - test_int(f.terms[1].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[1].src.trav, EcsIsA); - - test_int(f.terms[2].id, TagC); - test_int(f.terms[2].oper, EcsAnd); - test_int(f.terms[2].field_index, 1); - test_int(f.terms[2].first.id, TagC); - test_int(f.terms[2].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[2].src.id, EcsThis); - test_int(f.terms[2].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[2].src.trav, EcsIsA); - - test_int(f.terms[3].id, TagD); - test_int(f.terms[3].oper, EcsAnd); - test_int(f.terms[3].field_index, 2); - test_int(f.terms[3].first.id, TagD); - test_int(f.terms[3].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[3].src.id, EcsThis); - test_int(f.terms[3].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[3].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ EcsWildcard }} - }); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, EcsWildcard); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, EcsWildcard); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsVariable); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - test_int(f.terms[0].second.id, 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_any(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ EcsAny }} - })); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, EcsAny); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, EcsAny); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsVariable); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - test_int(f.terms[0].second.id, 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_same_subj_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.id = Rel, .src.name = "Foo", .second.name = "Foo" }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(Rel, Foo)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, Foo); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(f.terms[0].src.trav, EcsIsA); - test_int(f.terms[0].second.id, Foo); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_acyclic_same_subj_obj(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable); - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.id = Rel, .src.name = "Foo", .second.name = "Foo" }} - })); - - ecs_fini(world); -} - -void Filter_filter_1_term_acyclic_reflexive_same_subj_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable, Reflexive); - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.id = Rel, .src.name = "Foo", .second.name = "Foo" }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(Rel, Foo)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, Foo); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(f.terms[0].src.trav, EcsIsA); - test_int(f.terms[0].second.id, Foo); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_same_subj_obj_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .first.id = Rel, - .src = { .name = "X", .flags = EcsIsVariable }, - .second = { .name = "X", .flags = EcsIsVariable } - }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(Rel, EcsWildcard)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_str(f.terms[0].src.name, "X"); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - test_str(f.terms[0].second.name, "X"); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsVariable); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_acyclic_same_subj_obj_var(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .first.id = Rel, - .src = { .name = "X", .flags = EcsIsVariable }, - .second = { .name = "X", .flags = EcsIsVariable } - }} - })); - - ecs_fini(world); -} - -void Filter_filter_1_term_acyclic_reflexive_same_subj_obj_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable, Reflexive); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .first.id = Rel, - .src = { .name = "X", .flags = EcsIsVariable }, - .second = { .name = "X", .flags = EcsIsVariable } - }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(Rel, EcsWildcard)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_str(f.terms[0].src.name, "X"); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - test_str(f.terms[0].second.name, "X"); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsVariable); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_non_acyclic_superset(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = Tag, - .src.flags = EcsUp, - .src.trav = Rel - }} - })); /* cyclic superset is invalid */ - - ecs_fini(world); -} - -void Filter_filter_1_term_dont_inherit_default_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Tag, DontInherit); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = Tag }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, Tag); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Tag); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsIsVariable); - test_int(f.terms[0].src.trav, 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_dont_inherit_pair_default_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, DontInherit); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = ecs_pair(Rel, EcsWildcard) }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(Rel, EcsWildcard)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsIsVariable); - test_int(f.terms[0].src.trav, 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_cascade_implicit_isa(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = Tag, .src.flags = EcsCascade }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, Tag); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Tag); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsUp|EcsCascade|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_cascade_isa(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = Tag, - .src.trav = EcsIsA, .src.flags = EcsCascade - }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, Tag); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Tag); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsUp|EcsCascade|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_cascade_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = Tag, - .src.trav = EcsChildOf, .src.flags = EcsCascade - }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, Tag); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Tag); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsUp|EcsCascade|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsChildOf); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_cascade_down(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = Tag, - .src.trav = EcsChildOf, .src.flags = EcsCascade - }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, Tag); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Tag); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsUp|EcsCascade|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsChildOf); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_optional_only(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = Tag, - .oper = EcsOptional - }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, Tag); - test_int(f.terms[0].oper, EcsOptional); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Tag); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_transitive_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Transitive); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = ecs_pair(Rel, Tgt) - }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(Rel, Tgt)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - test_int(f.terms[0].second.id, Tgt); - test_int(f.terms[0].second.flags, EcsSelf|EcsUp|EcsTraverseAll|EcsIsEntity); - test_int(f.terms[0].second.trav, Rel); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_transitive_pair_explicit_self_tgt(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Transitive); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_t *r = ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = ecs_pair(Rel, Tgt), - .second.flags = EcsSelf - }} - }); - test_assert(r != NULL); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(Rel, Tgt)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - test_int(f.terms[0].second.id, Tgt); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].second.trav, 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_variable_as_pred_only(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = EcsVariable }} - })); - - ecs_fini(world); -} - -void Filter_filter_1_variable_as_pred_w_subj(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Src); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = EcsVariable, .src.id = Src }} - })); - - ecs_fini(world); -} - -void Filter_filter_1_variable_as_pred_w_pair(void) { - ecs_log_set_level(-4); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Src); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .first.id = EcsVariable, - .src.id = Src, - .second.id = Tgt - }} - })); - - ecs_fini(world); -} - -void Filter_filter_1_variable_as_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .first.id = Foo, - .src.id = EcsVariable, - .src.flags = EcsIsVariable - }} - })); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, Foo); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Foo); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, Foo); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(f.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_src_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .first.id = Foo, - .src.flags = EcsIsVariable, - .src.name = "Var" - }} - })); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, Foo); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Foo); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_str(f.terms[0].src.name, "Var"); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_first_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .first.flags = EcsIsVariable, - .first.name = "Var" - }} - })); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, EcsWildcard); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_str(f.terms[0].first.name, "Var"); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsVariable); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_second_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .first.id = Foo, - .second.flags = EcsIsVariable, - .second.name = "Var" - }} - })); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(Foo, EcsWildcard)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Foo); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - test_str(f.terms[0].second.name, "Var"); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsVariable); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_variable_as_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .first.id = Foo, - .second.id = EcsVariable, - .second.flags = EcsIsVariable - }} - })); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(Foo, Foo)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Foo); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - test_int(f.terms[0].second.id, Foo); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_2_terms_or_w_dontinherit(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_add_id(world, TagB, EcsDontInherit); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = {{ TagA, .oper = EcsOr }, { TagB }} - })); - - test_int(f.term_count, 2); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].oper, EcsOr); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, TagA); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - test_int(f.terms[1].id, TagB); - test_int(f.terms[1].oper, EcsAnd); - test_int(f.terms[1].field_index, 0); - test_int(f.terms[1].first.id, TagB); - test_int(f.terms[1].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[1].src.id, EcsThis); - test_int(f.terms[1].src.flags, EcsSelf|EcsIsVariable); - test_int(f.terms[1].src.trav, 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_2_terms_or_w_both_dontinherit(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_add_id(world, TagA, EcsDontInherit); - ecs_add_id(world, TagB, EcsDontInherit); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = {{ TagA, .oper = EcsOr }, { TagB }} - })); - - test_int(f.term_count, 2); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].oper, EcsOr); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, TagA); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsIsVariable); - test_int(f.terms[0].src.trav, 0); - - test_int(f.terms[1].id, TagB); - test_int(f.terms[1].oper, EcsAnd); - test_int(f.terms[1].field_index, 0); - test_int(f.terms[1].first.id, TagB); - test_int(f.terms[1].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[1].src.id, EcsThis); - test_int(f.terms[1].src.flags, EcsSelf|EcsIsVariable); - test_int(f.terms[1].src.trav, 0); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pair_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_id_t pair = ecs_pair(Rel, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{pair}} - }); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, pair); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - test_int(f.terms[0].second.id, Tgt); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pred_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_id_t pair = ecs_pair(Rel, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{.first.id = Rel, .second.id = Tgt}} - })); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, pair); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].second.id, Tgt); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pair_id_and_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - ECS_TAG(world, Src); - - ecs_id_t pair = ecs_pair(Rel, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{.id = pair, .src.id = Src}} - })); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, pair); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, Rel); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, Src); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(f.terms[0].second.id, Tgt); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_term_w_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .id = Tag - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - - ecs_fini(world); -} - -void Filter_term_w_pair_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_id_t pair = ecs_pair(Rel, Tgt); - - ecs_term_t term = { - .id = pair - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, pair); - test_int(term.id_flags, ECS_PAIR); - test_int(term.first.id, Rel); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.second.id, Tgt); - test_int(term.second.flags, EcsSelf|EcsIsEntity); - - ecs_fini(world); -} - -void Filter_term_w_pred_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_id_t pair = ecs_pair(Rel, Tgt); - - ecs_term_t term = { - .first.id = Rel, - .second.id = Tgt - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, pair); - test_int(term.id_flags, ECS_PAIR); - test_int(term.first.id, Rel); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.second.id, Tgt); - test_int(term.second.flags, EcsSelf|EcsIsEntity); - - ecs_fini(world); -} - -void Filter_term_w_pair_finalize_twice(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_id_t pair = ecs_pair(Rel, Tgt); - - ecs_term_t term = { - .first.id = Rel, - .second.id = Tgt - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, pair); - test_int(term.id_flags, ECS_PAIR); - test_int(term.first.id, Rel); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.second.id, Tgt); - test_int(term.second.flags, EcsSelf|EcsIsEntity); - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, pair); - test_int(term.id_flags, ECS_PAIR); - test_int(term.first.id, Rel); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.second.id, Tgt); - test_int(term.second.flags, EcsSelf|EcsIsEntity); - - ecs_fini(world); -} - -void Filter_term_w_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .id = Tag, - .id_flags = ECS_OVERRIDE - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag | ECS_OVERRIDE); - test_int(term.id_flags, ECS_OVERRIDE); - test_int(term.first.id, Tag); - - test_int(term.id, Tag | ECS_OVERRIDE); - test_int(term.id_flags, ECS_OVERRIDE); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - - ecs_fini(world); -} - -void Filter_term_w_pred_role(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .id_flags = ECS_OVERRIDE - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id_flags, ECS_OVERRIDE); - test_int(term.id, Tag | ECS_OVERRIDE); - test_int(term.id_flags, ECS_OVERRIDE); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - - ecs_fini(world); -} - -void Filter_term_w_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.flags = EcsSelf - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsIsVariable); - - ecs_fini(world); -} - -void Filter_term_w_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.flags = EcsUp - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_term_w_subset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.flags = EcsDown - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsDown|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_term_w_self_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.flags = EcsSelf | EcsUp - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_term_w_superset_custom_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.flags = EcsUp, - .src.trav = EcsChildOf - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsChildOf); - - ecs_fini(world); -} - -void Filter_term_w_self_superset_custom_relation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.flags = EcsSelf | EcsUp, - .src.trav = EcsChildOf - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsChildOf); - - ecs_fini(world); -} - -void Filter_filter_1_w_pred_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.name = "Tag" - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_assert(term.first.name == NULL); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_filter_1_w_final_pred_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Tag, Final); - - ecs_term_t term = { - .first.name = "Tag" - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_assert(term.first.name == NULL); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.first.trav, 0); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_filter_1_w_subj_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Src); - - ecs_term_t term = { - .first.id = Tag, - .src.name = "Src" - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, Src); - test_assert(term.src.name == NULL); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_filter_1_w_obj_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Tgt); - - ecs_term_t term = { - .first.id = Tag, - .second.name = "Tgt" - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, ecs_pair(Tag, Tgt)); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - test_int(term.second.id, Tgt); - test_assert(term.second.name == NULL); - test_int(term.second.flags, EcsSelf|EcsIsEntity); - - ecs_fini(world); -} - -void Filter_filter_w_this_implicit_variable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.id = EcsThis - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_filter_w_this_explicit_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.id = EcsThis, - .src.flags = EcsIsEntity - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_filter_w_first_this_implicit_variable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Src); - - ecs_term_t term = { - .first.id = EcsThis, - .src.id = Src - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, EcsWildcard); - test_int(term.first.id, EcsThis); - test_int(term.first.flags, EcsSelf|EcsIsVariable); - test_int(term.src.id, Src); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_filter_w_first_this_explicit_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Src); - - ecs_term_t term = { - .first.id = EcsThis, - .first.flags = EcsIsEntity, - .src.id = Src - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, EcsThis); - test_int(term.first.id, EcsThis); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, Src); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(term.src.trav, EcsIsA); - - ecs_fini(world); -} - -void Filter_filter_w_second_this_implicit_variable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .second.id = EcsThis - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, ecs_pair(Tag, EcsWildcard)); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - test_int(term.second.id, EcsThis); - test_int(term.second.flags, EcsSelf|EcsIsVariable); - - ecs_fini(world); -} - -void Filter_filter_w_second_this_explicit_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .second.id = EcsThis, - .second.flags = EcsIsEntity - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, ecs_pair(Tag, EcsThis)); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - test_int(term.second.id, EcsThis); - test_int(term.second.flags, EcsSelf|EcsIsEntity); - - ecs_fini(world); -} - -void Filter_filter_w_this_variable_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.name = "This", - .src.flags = EcsIsVariable - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(term.src.trav, EcsIsA); - test_assert(term.src.name == NULL); - - ecs_fini(world); -} - -void Filter_filter_w_0_source(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_term_t term = { - .first.id = Tag, - .src.flags = EcsIsEntity - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, Tag); - test_int(term.first.id, Tag); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, 0); - test_int(term.src.flags, EcsIsEntity); - test_int(term.src.trav, 0); - - ecs_fini(world); -} - -void Filter_filter_w_0_target(void) { - ecs_world_t *world = ecs_mini(); - - ecs_term_t term = { - .first.id = EcsChildOf, - .second.flags = EcsIsEntity - }; - - test_assert(ecs_term_finalize(world, &term) == 0); - test_int(term.id, ecs_pair(EcsChildOf, 0)); - test_int(term.first.id, EcsChildOf); - test_int(term.first.flags, EcsSelf|EcsIsEntity); - test_int(term.src.id, EcsThis); - test_int(term.src.flags, EcsSelf|EcsIsVariable); - test_int(term.src.trav, 0); - test_int(term.second.id, 0); - test_int(term.second.flags, EcsIsEntity); - test_int(term.second.trav, 0); - - ecs_fini(world); -} - -void Filter_filter_2_terms_w_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{TagA, .oper = EcsOr}, {TagB }} - })); - - test_int(f.terms[0].oper, EcsOr); - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].first.id, TagA); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[0].src.trav, EcsIsA); - - test_int(f.terms[1].oper, EcsAnd); - test_int(f.terms[1].id, TagB); - test_int(f.terms[1].first.id, TagB); - test_int(f.terms[1].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[1].src.id, EcsThis); - test_int(f.terms[1].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f.terms[1].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_2_terms_w_or_mixed_src_flags(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagA, .oper = EcsOr}, - {TagB, .oper = EcsAnd, .src.flags = EcsUp} - } - })); - - ecs_entity_t base = ecs_new(world, TagB); - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, it.ids[0]); - test_uint(0, it.sources[0]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagB, it.ids[0]); - test_uint(base, it.sources[0]); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_2_terms_w_or_mixed_src_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ECS_TAG(world, SrcA); - ECS_TAG(world, SrcB); - - ecs_log_set_level(-4); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagA, .oper = EcsOr, .src.id = SrcA}, - {TagB, .oper = EcsOr, .src.id = SrcB} - } - })); - - ecs_fini(world); -} - -void Filter_filter_2_terms_w_or_mixed_src_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ECS_TAG(world, SrcA); - ECS_TAG(world, SrcB); - - ecs_log_set_level(-4); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagA, .oper = EcsOr, .src.name = "SrcA"}, - {TagB, .oper = EcsOr, .src.name = "SrcB"} - } - })); - - ecs_fini(world); -} - -void Filter_filter_2_terms_w_or_same_src_w_id_and_name(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ECS_TAG(world, SrcA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagA, .oper = EcsOr, .src.name = "SrcA"}, - {TagB, .src.id = SrcA} - } - })); - - test_int(f.terms[0].oper, EcsOr); - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].first.id, TagA); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, SrcA); - test_int(f.terms[0].src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(f.terms[0].src.trav, EcsIsA); - - test_int(f.terms[1].oper, EcsAnd); - test_int(f.terms[1].id, TagB); - test_int(f.terms[1].first.id, TagB); - test_int(f.terms[1].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[1].src.id, SrcA); - test_int(f.terms[1].src.flags, EcsSelf|EcsUp|EcsIsEntity); - test_int(f.terms[1].src.trav, EcsIsA); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_move(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = {{TagA}, {TagB}} - })); - - test_int(f_1.term_count, 2); - test_assert(f_1.terms != NULL); - test_int(f_1.terms[0].id, TagA); - test_int(f_1.terms[1].id, TagB); - - ecs_filter_t f_2 = {0}; - ecs_filter_move(&f_2, &f_1); - - test_int(f_2.term_count, 2); - test_assert(f_2.terms != NULL); - test_int(f_2.terms[0].id, TagA); - test_int(f_2.terms[1].id, TagB); - - test_int(f_1.term_count, 0); - test_assert(f_1.terms == NULL); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_filter_copy(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = {{TagA}, {TagB}} - })); - - test_int(f_1.term_count, 2); - test_assert(f_1.terms != NULL); - test_int(f_1.terms[0].id, TagA); - test_int(f_1.terms[1].id, TagB); - - ecs_filter_t f_2 = {0}; - ecs_filter_copy(&f_2, &f_1); - - test_int(f_1.term_count, 2); - test_assert(f_1.terms != NULL); - test_int(f_1.terms[0].id, TagA); - test_int(f_1.terms[1].id, TagB); - - test_int(f_2.term_count, 2); - test_assert(f_2.terms != NULL); - test_int(f_2.terms[0].id, TagA); - test_int(f_2.terms[1].id, TagB); - - test_assert(f_1.terms != f_2.terms); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_filter_w_resources_copy(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = {{TagA, .src.name = "this"}, {TagB, .src.name = "this"}} - })); - - test_int(f_1.term_count, 2); - test_assert(f_1.terms != NULL); - test_int(f_1.terms[0].id, TagA); - test_int(f_1.terms[1].id, TagB); - - ecs_filter_t f_2 = {0}; - ecs_filter_copy(&f_2, &f_1); - - test_int(f_1.term_count, 2); - test_assert(f_1.terms != NULL); - test_int(f_1.terms[0].id, TagA); - test_int(f_1.terms[1].id, TagB); - - test_int(f_2.term_count, 2); - test_assert(f_2.terms != NULL); - test_int(f_2.terms[0].id, TagA); - test_int(f_2.terms[1].id, TagB); - - test_assert(f_1.terms != f_2.terms); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_filter_w_and_flag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {ECS_AND | TagA} - } - })); - - test_int(f.term_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].id_flags, 0); - test_int(f.terms[0].oper, EcsAndFrom); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_or_flag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TagA, .oper = EcsOrFrom } - } - })); - - test_int(f.term_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].id_flags, 0); - test_int(f.terms[0].oper, EcsOrFrom); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagA} - }, - .flags = EcsFilterNoData - })); - - test_int(f.term_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].id_flags, 0); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].inout, EcsInOutNone); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_double_init(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = {{ Foo }} - })); - - test_int(f_1.term_count, 1); - test_int(f_1.terms[0].id, Foo); - test_int(f_1.terms[0].oper, EcsAnd); - test_int(f_1.terms[0].field_index, 0); - test_int(f_1.terms[0].first.id, Foo); - test_int(f_1.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f_1.terms[0].src.id, EcsThis); - test_int(f_1.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f_1.terms[0].src.trav, EcsIsA); - - ecs_filter_t f_2 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_2, - .terms_buffer = f_1.terms, - .terms_buffer_count = 1 - })); - - test_int(f_2.term_count, 1); - test_int(f_2.terms[0].id, Foo); - test_int(f_2.terms[0].oper, EcsAnd); - test_int(f_2.terms[0].field_index, 0); - test_int(f_2.terms[0].first.id, Foo); - test_int(f_2.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f_2.terms[0].src.id, EcsThis); - test_int(f_2.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f_2.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_filter_double_init_w_expr(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .expr = "Foo" - })); - - test_int(f_1.term_count, 1); - test_int(f_1.terms[0].id, Foo); - test_int(f_1.terms[0].oper, EcsAnd); - test_int(f_1.terms[0].field_index, 0); - test_int(f_1.terms[0].first.id, Foo); - test_int(f_1.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f_1.terms[0].src.id, EcsThis); - test_int(f_1.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f_1.terms[0].src.trav, EcsIsA); - - ecs_filter_t f_2 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_2, - .terms_buffer = f_1.terms, - .terms_buffer_count = 1 - })); - - test_int(f_2.term_count, 1); - test_int(f_2.terms[0].id, Foo); - test_int(f_2.terms[0].oper, EcsAnd); - test_int(f_2.terms[0].field_index, 0); - test_int(f_2.terms[0].first.id, Foo); - test_int(f_2.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f_2.terms[0].src.id, EcsThis); - test_int(f_2.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f_2.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_filter_double_init_w_expr_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .expr = "?Foo" - })); - - test_int(f_1.term_count, 1); - test_int(f_1.terms[0].id, Foo); - test_int(f_1.terms[0].oper, EcsOptional); - test_int(f_1.terms[0].field_index, 0); - test_int(f_1.terms[0].first.id, Foo); - test_int(f_1.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f_1.terms[0].src.id, EcsThis); - test_int(f_1.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f_1.terms[0].src.trav, EcsIsA); - - ecs_filter_t f_2 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_2, - .terms_buffer = f_1.terms, - .terms_buffer_count = 1 - })); - - test_int(f_2.term_count, 1); - test_int(f_2.terms[0].id, Foo); - test_int(f_2.terms[0].oper, EcsOptional); - test_int(f_2.terms[0].field_index, 0); - test_int(f_2.terms[0].first.id, Foo); - test_int(f_2.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f_2.terms[0].src.id, EcsThis); - test_int(f_2.terms[0].src.flags, EcsSelf|EcsUp|EcsIsVariable); - test_int(f_2.terms[0].src.trav, EcsIsA); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_filter_w_tag_term_is_no_data(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ Foo }} - })); - - test_assert(f.flags & EcsFilterNoData); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(it.flags & EcsIterNoData); - ecs_iter_fini(&it); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_inout_none_term_is_no_data(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position), .inout = EcsInOutNone }} - })); - - test_assert(f.flags & EcsFilterNoData); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(it.flags & EcsIterNoData); - ecs_iter_fini(&it); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_tag_and_inout_none_term_is_no_data(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { ecs_id(Position), .inout = EcsInOutNone }, - { Foo } - } - })); - - test_assert(f.flags & EcsFilterNoData); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(it.flags & EcsIterNoData); - ecs_iter_fini(&it); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_not_term_is_no_data(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { ecs_id(Position), .oper = EcsNot } - } - })); - - test_assert(f.flags & EcsFilterNoData); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(it.flags & EcsIterNoData); - ecs_iter_fini(&it); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_no_transitive_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, LocatedIn); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .first.id = LocatedIn, .second.id = EcsWildcard } - } - })); - - test_assert(!(f.terms[0].flags & EcsTermTransitive)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_transitive_pair_any_src(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, LocatedIn, Transitive); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { - .first.id = LocatedIn, - .second.id = EcsWildcard, - .src.id = EcsAny - } - } - })); - - test_assert(!(f.terms[0].flags & EcsTermTransitive)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_transitive_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, LocatedIn, Transitive); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .first.id = LocatedIn, .second.id = EcsWildcard } - } - })); - - test_assert(f.terms[0].flags & EcsTermTransitive); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_transitive_tag_no_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, LocatedIn, Transitive); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .first.id = LocatedIn } - } - })); - - test_assert(!(f.terms[0].flags & EcsTermTransitive)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_transitive_tag_self_tgt(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, LocatedIn, Transitive); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .first.id = LocatedIn, .second.id = EcsWildcard, .second.flags = EcsSelf } - } - })); - - test_assert(!(f.terms[0].flags & EcsTermTransitive)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_transitive_tag_any_tgt(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, LocatedIn, Transitive); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .first.id = LocatedIn, .second.id = EcsAny } - } - })); - - test_assert(!(f.terms[0].flags & EcsTermTransitive)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pair_same_vars(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { - .first.id = Rel, - .src.name = "a", - .src.flags = EcsIsVariable, - .second.name = "a", - .second.flags = EcsIsVariable, - } - } - })); - - test_assert(f.terms[0].src.flags & EcsIsVariable); - test_assert(f.terms[0].second.flags & EcsIsVariable); - test_str(f.terms[0].src.name, "a"); - test_str(f.terms[0].second.name, "a"); - test_assert(f.terms[0].flags & EcsTermSrcSecondEq); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pair_not_same_vars(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { - .first.id = Rel, - .src.name = "a", - .src.flags = EcsIsVariable, - .second.name = "b", - .second.flags = EcsIsVariable, - } - } - })); - - test_assert(f.terms[0].src.flags & EcsIsVariable); - test_assert(f.terms[0].second.flags & EcsIsVariable); - test_str(f.terms[0].src.name, "a"); - test_str(f.terms[0].second.name, "b"); - test_assert(!(f.terms[0].flags & EcsTermSrcSecondEq)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pair_no_vars_not_same_vars(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, A); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { - .first.id = Rel, - .src.id = A, - .second.id = A - } - } - })); - - test_assert(f.terms[0].src.flags & EcsIsEntity); - test_assert(f.terms[0].second.flags & EcsIsEntity); - test_uint(f.terms[0].src.id, A); - test_uint(f.terms[0].second.id, A); - test_assert(f.terms[0].flags & EcsTermSrcSecondEq); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pair_wildcard_not_same_vars(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { - .first.id = Rel, - .src.id = EcsWildcard, - .second.id = EcsWildcard - } - } - })); - - test_assert(f.terms[0].src.flags & EcsIsVariable); - test_assert(f.terms[0].second.flags & EcsIsVariable); - test_uint(f.terms[0].src.id, EcsWildcard); - test_uint(f.terms[0].second.id, EcsWildcard); - test_assert(!(f.terms[0].flags & EcsTermSrcSecondEq)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pair_any_not_same_vars(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { - .first.id = Rel, - .src.id = EcsAny, - .second.id = EcsAny - } - } - })); - - test_assert(f.terms[0].src.flags & EcsIsVariable); - test_assert(f.terms[0].second.flags & EcsIsVariable); - test_uint(f.terms[0].src.id, EcsAny); - test_uint(f.terms[0].second.id, EcsAny); - test_assert(!(f.terms[0].flags & EcsTermSrcSecondEq)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_no_pair_not_same_vars(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { - .first.id = Rel, - .src.name = "a", - .src.flags = EcsIsVariable - } - } - })); - - test_assert(f.terms[0].src.flags & EcsIsVariable); - test_assert(!(f.terms[0].second.flags & EcsIsVariable)); - test_str(f.terms[0].src.name, "a"); - test_assert(!(f.terms[0].flags & EcsTermSrcSecondEq)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_not_childof_any(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { - .first.id = EcsChildOf, - .second.id = EcsAny, - .oper = EcsNot - } - } - })); - - test_int(f.term_count, 1); - test_int(f.field_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, ecs_pair(EcsChildOf, 0)); - test_int(f.terms[0].oper, EcsAnd); - test_int(f.terms[0].inout, EcsInOutDefault); - test_int(f.terms[0].field_index, 0); - test_int(f.terms[0].first.id, EcsChildOf); - test_int(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_int(f.terms[0].src.id, EcsThis); - test_int(f.terms[0].src.flags, EcsSelf|EcsIsVariable); - test_int(f.terms[0].second.id, 0); - test_int(f.terms[0].second.flags, EcsSelf|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_inherited_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (IsA, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit } - } - })); - - test_assert(f.terms[0].flags & EcsTermIdInherited); - test_int(f.terms[0].first.trav, EcsIsA); - test_int(f.terms[0].first.id, Unit); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_inherited_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (IsA, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .second.id = EcsWildcard } - } - })); - - test_assert(f.terms[0].flags & EcsTermIdInherited); - test_int(f.terms[0].first.trav, EcsIsA); - test_int(f.terms[0].first.id, Unit); - test_int(f.terms[0].second.id, EcsWildcard); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_non_inherited_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Unit); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit } - } - })); - - test_assert(!(f.terms[0].flags & EcsTermIdInherited)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_non_inherited_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Unit); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .second.id = EcsWildcard } - } - })); - - test_assert(!(f.terms[0].flags & EcsTermIdInherited)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_first_rel(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable); - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (Rel, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .first.trav = Rel } - } - })); - - test_assert(f.terms[0].flags & EcsTermIdInherited); - test_int(f.terms[0].first.trav, Rel); - test_int(f.terms[0].first.id, Unit); - test_int(f.terms[0].first.flags, EcsDown|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_first_rel_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable); - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (Rel, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .first.flags = EcsSelf, .first.trav = Rel } - } - })); - - ecs_fini(world); -} - -void Filter_filter_w_first_rel_down(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable); - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (Rel, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .first.flags = EcsDown, .first.trav = Rel } - } - })); - - test_assert(f.terms[0].flags & EcsTermIdInherited); - test_int(f.terms[0].first.trav, Rel); - test_int(f.terms[0].first.id, Unit); - test_int(f.terms[0].first.flags, EcsDown|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_first_rel_self_down(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable); - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (Rel, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .first.flags = EcsSelf|EcsDown, .first.trav = Rel } - } - })); - - test_assert(f.terms[0].flags & EcsTermIdInherited); - test_int(f.terms[0].first.trav, Rel); - test_int(f.terms[0].first.id, Unit); - test_int(f.terms[0].first.flags, EcsSelf|EcsDown|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_first_rel_reflexive(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable, Reflexive); - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (Rel, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .first.trav = Rel } - } - })); - - test_assert(f.terms[0].flags & EcsTermIdInherited); - test_int(f.terms[0].first.trav, Rel); - test_int(f.terms[0].first.id, Unit); - test_int(f.terms[0].first.flags, EcsSelf|EcsDown|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_first_rel_reflexive_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable, Reflexive); - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (Rel, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .first.flags = EcsSelf, .first.trav = Rel } - } - })); - - ecs_fini(world); -} - -void Filter_filter_w_first_rel_reflexive_down(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable, Reflexive); - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (Rel, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .first.flags = EcsDown, .first.trav = Rel } - } - })); - - test_assert(f.terms[0].flags & EcsTermIdInherited); - test_int(f.terms[0].first.trav, Rel); - test_int(f.terms[0].first.id, Unit); - test_int(f.terms[0].first.flags, EcsDown|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_first_rel_reflexive_self_down(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Traversable, Reflexive); - ECS_TAG(world, Unit); - ECS_ENTITY(world, MeleeUnit, (Rel, Unit)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .first.flags = EcsSelf|EcsDown, .first.trav = Rel } - } - })); - - test_assert(f.terms[0].flags & EcsTermIdInherited); - test_int(f.terms[0].first.trav, Rel); - test_int(f.terms[0].first.id, Unit); - test_int(f.terms[0].first.flags, EcsSelf|EcsDown|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_first_rel_non_traversable(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Unit); - - ecs_log_set_level(-4); - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = Unit, .first.trav = Rel } - } - })); - - ecs_fini(world); -} - -void Filter_filter_w_first_wildcard_inout_none(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = EcsWildcard } - } - })); - - test_int(f.terms[0].inout, EcsInOutNone); - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_first_var_inout_none(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.name = "var", .first.flags = EcsIsVariable } - } - })); - - - test_int(f.terms[0].inout, EcsInOutNone); - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pair_wildcard_inout_none(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { .first.id = EcsWildcard, .second.id = EcsWildcard } - } - })); - - test_int(f.terms[0].inout, EcsInOutNone); - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_pair_var_inout_none(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { - { - .first.name = "x", .first.flags = EcsIsVariable, - .second.name = "y", .second.flags = EcsIsVariable, - } - } - })); - - test_int(f.terms[0].inout, EcsInOutNone); - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_unresolved_by_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_log_set_level(-4); - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter(world, { - .storage = &f, - .terms = { { .first.name = "Foo" } }, - .flags = EcsFilterUnresolvedByName - })); - - ecs_fini(world); -} - -void Filter_filter_w_unresolved_by_name_eq(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .expr = "$this == Foo", - .flags = EcsFilterUnresolvedByName - })); - - test_assert(f.terms[0].second.flags & EcsIsName); - test_str(f.terms[0].second.name, "Foo"); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_childof_this(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { { .first.id = EcsChildOf, .second.id = EcsThis } } - })); - - test_assert(f.terms[0].id == ecs_pair(EcsChildOf, EcsWildcard)); - test_assert(f.terms[0].first.id == EcsChildOf); - test_uint(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_assert(f.terms[0].second.id == EcsThis); - test_uint(f.terms[0].second.flags, EcsSelf|EcsIsVariable); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_childof_this_entity(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter(world, { - .storage = &f, - .terms = { { .first.id = EcsChildOf, .second.id = EcsThis, .second.flags = EcsIsEntity } } - })); - - test_assert(f.terms[0].id == ecs_pair(EcsChildOf, EcsThis)); - test_assert(f.terms[0].first.id == EcsChildOf); - test_uint(f.terms[0].first.flags, EcsSelf|EcsIsEntity); - test_assert(f.terms[0].second.id == EcsThis); - test_uint(f.terms[0].second.flags, EcsSelf|EcsIsEntity); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_childof_this_by_id(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - - ecs_log_set_level(-4); - test_assert(NULL == ecs_filter(world, { - .storage = &f, - .terms = { { .id = ecs_pair(EcsChildOf, EcsThis) } } - })); - - ecs_fini(world); -} - -void Filter_filter_w_not_flag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TagA, .oper = EcsNotFrom } - } - })); - - test_int(f.term_count, 1); - test_assert(f.terms != NULL); - test_int(f.terms[0].id, TagA); - test_int(f.terms[0].id_flags, 0); - test_int(f.terms[0].oper, EcsNotFrom); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_term_iter_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {3, 4}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {5, 6}); - - ecs_add(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - ecs_id(Position) - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 1); - test_int(p[0].y, 2); - - test_int(p[1].x, 3); - test_int(p[1].y, 4); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 5); - test_int(p[0].y, 6); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_w_pred(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {3, 4}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {5, 6}); - - ecs_add(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .first.id = ecs_id(Position) - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 1); - test_int(p[0].y, 2); - - test_int(p[1].x, 3); - test_int(p[1].y, 4); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 5); - test_int(p[0].y, 6); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag1); - ECS_TAG(world, Tag2); - - ecs_entity_t e_1 = ecs_new(world, Tag1); - ecs_entity_t e_2 = ecs_new(world, Tag1); - ecs_entity_t e_3 = ecs_new(world, Tag1); - - ecs_add(world, e_3, Tag2); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { Tag1 }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), Tag1); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), Tag1); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel, Tgt); - - ecs_add(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { ecs_pair(Rel, Tgt) }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Tgt)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Tgt)); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_pair_w_rel_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel_1); - ECS_TAG(world, Rel_2); - ECS_TAG(world, Tgt); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel_1, Tgt); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel_1, Tgt); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel_2, Tgt); - - ecs_add(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - ecs_pair(EcsWildcard, Tgt) - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Tgt)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Tgt)); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_pair_w_obj_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, Obj_1); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, Obj_1); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel, Obj_2); - - ecs_add(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - ecs_pair(Rel, EcsWildcard) - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_1)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_2)); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_pair_w_rel_wildcard_n_matches(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel_1); - ECS_TAG(world, Rel_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_4 = ecs_new_w_pair(world, Rel_1, Obj_1); - - /* Add 2nd object for pair to ensure pairs for Obj_1 are separated */ - ecs_add_pair(world, e_3, Rel_1, Obj_2); - ecs_add_pair(world, e_4, Rel_1, Obj_2); - - ecs_add_pair(world, e_3, Rel_2, Obj_1); - ecs_add_pair(world, e_4, Rel_2, Obj_1); - - ecs_add(world, e_4, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - ecs_pair(EcsWildcard, Obj_1) - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj_1)); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_pair_w_rel_wildcard_n_matches_w_data(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Rel_1); - ECS_TAG(world, Rel_2); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_set_pair_object(world, 0, Rel_1, Position, {10, 20}); - ecs_entity_t e_2 = ecs_set_pair_object(world, 0, Rel_1, Position, {10, 20}); - ecs_entity_t e_3 = ecs_set_pair_object(world, 0, Rel_1, Position, {10, 20}); - ecs_entity_t e_4 = ecs_set_pair_object(world, 0, Rel_1, Position, {10, 20}); - - ecs_set_pair_object(world, e_3, Rel_2, Position, {20, 30}); - ecs_set_pair_object(world, e_4, Rel_2, Position, {20, 30}); - - ecs_add(world, e_4, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - ecs_pair(EcsWildcard, ecs_id(Position)) - }); - - const Position *p; - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, ecs_id(Position))); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, ecs_id(Position))); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, ecs_id(Position))); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, ecs_id(Position))); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, ecs_id(Position))); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_pair_w_obj_wildcard_n_matches(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, Obj_1); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, Obj_1); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel, Obj_1); - ecs_entity_t e_4 = ecs_new_w_pair(world, Rel, Obj_1); - - ecs_add_pair(world, e_3, Rel, Obj_2); - ecs_add_pair(world, e_4, Rel, Obj_2); - - ecs_add(world, e_4, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - ecs_pair(Rel, EcsWildcard) - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_1)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_1)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_2)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_1)); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_2)); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_pair_w_obj_wildcard_n_matches_w_data(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_set_pair(world, 0, Position, Obj_1, {10, 20}); - ecs_entity_t e_2 = ecs_set_pair(world, 0, Position, Obj_1, {10, 20}); - ecs_entity_t e_3 = ecs_set_pair(world, 0, Position, Obj_1, {10, 20}); - ecs_entity_t e_4 = ecs_set_pair(world, 0, Position, Obj_1, {10, 20}); - - ecs_set_pair(world, e_3, Position, Obj_2, {20, 30}); - ecs_set_pair(world, e_4, Position, Obj_2, {20, 30}); - - ecs_add(world, e_4, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - ecs_pair(ecs_id(Position), EcsWildcard) - }); - - const Position *p; - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(ecs_id(Position), Obj_1)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(ecs_id(Position), Obj_1)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(ecs_id(Position), Obj_2)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(ecs_id(Position), Obj_1)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(ecs_id(Position), Obj_2)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_w_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, base); - - ecs_add_id(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_id(Position), - .src.flags = EcsUp - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), base); - - Position *ptr = ecs_field(&it, Position, 1); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), base); - - test_assert(ptr == ecs_field(&it, Position, 1)); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_w_superset_base_w_2_components(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, base, Velocity, {0, 0}); - - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, base); - - ecs_add_id(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_id(Position), - .src.flags = EcsUp - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), base); - - Position *ptr = ecs_field(&it, Position, 1); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), base); - - test_assert(ptr == ecs_field(&it, Position, 1)); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_w_superset_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_entity_t parent = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_add_id(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_id(Position), - .src.flags = EcsUp, - .src.trav = EcsChildOf - }); - - { - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), parent); - - Position *ptr = ecs_field(&it, Position, 1); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - { - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), parent); - - Position *ptr = ecs_field(&it, Position, 1); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - } - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_w_superset_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_0 = ecs_set(world, 0, Position, {11, 22}); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, base); - - ecs_add_id(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_id(Position), - .src.flags = EcsSelf|EcsUp - }); - - Position *ptr; - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], base); - test_int(it.entities[1], e_0); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - - ptr = ecs_field(&it, Position, 1); - test_int(ptr[0].x, 10); - test_int(ptr[0].y, 20); - test_int(ptr[1].x, 11); - test_int(ptr[1].y, 22); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), base); - - ptr = ecs_field(&it, Position, 1); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), base); - - test_assert(ptr == ecs_field(&it, Position, 1)); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_w_superset_self_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_entity_t parent = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_0 = ecs_set(world, 0, Position, {11, 22}); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, parent); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, parent); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, parent); - - ecs_add_id(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_id(Position), - .src.flags = EcsSelf|EcsUp - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], parent); - test_int(it.entities[1], e_0); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - - Position *ptr = ecs_field(&it, Position, 1); - test_int(ptr[0].x, 10); - test_int(ptr[0].y, 20); - test_int(ptr[1].x, 11); - test_int(ptr[1].y, 22); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), parent); - - ptr = ecs_field(&it, Position, 1); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), parent); - - test_assert(ptr == ecs_field(&it, Position, 1)); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_w_superset_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, base); - - ecs_add_id(world, e_3, TagB); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = TagA, - .src.flags = EcsUp - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_src(&it, 1), base); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_src(&it, 1), base); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_w_superset_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - ECS_TAG(world, Tag); - - ecs_entity_t base = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, base); - - ecs_add_id(world, e_3, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_pair(Rel, Tgt), - .src.flags = EcsUp, - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Tgt)); - test_int(ecs_field_src(&it, 1), base); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Tgt)); - test_int(ecs_field_src(&it, 1), base); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_w_superset_pair_obj_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - ECS_TAG(world, Tag); - - ecs_entity_t base_1 = ecs_new_w_pair(world, Rel, Obj_1); - ecs_entity_t base_2 = ecs_new_w_pair(world, Rel, Obj_2); - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsIsA, base_1); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsIsA, base_1); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsIsA, base_2); - ecs_add_id(world, e_3, Tag); - - ecs_entity_t e_4 = ecs_new_w_pair(world, EcsIsA, base_2); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_pair(Rel, EcsWildcard), - .src.flags = EcsUp - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_1)); - test_int(ecs_field_src(&it, 1), base_1); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_2)); - test_int(ecs_field_src(&it, 1), base_2); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_2)); - test_int(ecs_field_src(&it, 1), base_2); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_in_stage(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_add(world, e2, TagB); - - ecs_readonly_begin(world); - - ecs_world_t *stage = ecs_get_stage(world, 0); - test_assert(stage != NULL); - - ecs_iter_t it = ecs_term_iter(stage, &(ecs_term_t) { .id = Tag }); - test_assert(it.real_world == world); - test_assert(it.world == stage); - - test_bool(ecs_term_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e1); - test_int(it.ids[0], Tag); - - test_bool(ecs_term_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e2); - test_int(it.ids[0], Tag); - - test_bool(ecs_term_next(&it), false); - - ecs_readonly_end(world); - - ecs_fini(world); -} - -void Filter_term_iter_type_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - - ecs_add_id(world, e_2, TagB); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = TagA - }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_assert(it.table != NULL); - const ecs_type_t *type = ecs_table_get_type(it.table); - test_int(type->count, 1); - test_int(type->array[0], TagA); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_assert(it.table != NULL); - type = ecs_table_get_type(it.table); - test_int(type->count, 2); - test_int(type->array[0], TagA); - test_int(type->array[1], TagB); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_term_iter_any_match_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ EcsAny }); - ecs_entity_t prev = 0; - int32_t count = 0, e1_matched = 0; - while (ecs_term_next(&it)) { - test_assert(it.count > 0); - test_assert(!prev || prev != it.entities[0]); - prev = it.entities[0]; - if (it.entities[0] == e1) { - e1_matched ++; - } - count ++; - } - - test_assert(count > 0); - test_int(e1_matched, 1); - - ecs_fini(world); -} - -void Filter_term_iter_any_match_tag_and_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e2 = ecs_new(world, Tag); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ EcsAny }); - ecs_entity_t prev = 0; - int32_t count = 0, e1_matched = 0, e2_matched = 0; - while (ecs_term_next(&it)) { - test_assert(it.count > 0); - test_assert(!prev || prev != it.entities[0]); - prev = it.entities[0]; - if (it.entities[0] == e1) { - e1_matched ++; - } - if (it.entities[0] == e2) { - e2_matched ++; - } - count ++; - } - - test_assert(count > 0); - test_int(e1_matched, 1); - test_int(e2_matched, 1); - - ecs_fini(world); -} - -void Filter_term_iter_any_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - ECS_TAG(world, ObjC); - ECS_TAG(world, ObjD); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e4 = ecs_new_w_pair(world, Rel, ObjA); - - ecs_add_pair(world, e2, Rel, ObjB); - ecs_add_pair(world, e3, Rel, ObjC); - ecs_add_pair(world, e4, Rel, ObjC); - ecs_add_pair(world, e4, Rel, ObjD); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ ecs_pair(Rel, EcsAny )}); - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e4); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_children_iter(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_iter_t it = ecs_children(world, parent); - test_bool(true, ecs_children_next(&it)); - test_int(it.count, 2); - test_uint(child_1, it.entities[0]); - test_uint(child_2, it.entities[1]); - test_bool(false, ecs_children_next(&it)); - - ecs_fini(world); -} - -void Filter_filter_iter_1_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_entity_t e_3 = ecs_new(world, TagA); - - ecs_add_id(world, e_3, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_tags(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_entity_t e_3 = ecs_new(world, TagA); - - ecs_new(world, TagA); /* Non matching entity */ - - ecs_add_id(world, e_1, TagB); - ecs_add_id(world, e_2, TagB); - ecs_add_id(world, e_3, TagB); - - ecs_add_id(world, e_3, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { TagB }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_tags_1_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_entity_t e_3 = ecs_new(world, TagA); - - ecs_add_id(world, e_3, TagC); - - ecs_entity_t e_4 = ecs_new(world, TagA); /* Non matching entity */ - ecs_add_id(world, e_4, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { TagB, .oper = EcsNot }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_3_tags_2_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_entity_t e_3 = ecs_new(world, TagA); - - ecs_add_id(world, e_1, TagB); - ecs_add_id(world, e_2, TagB); - ecs_add_id(world, e_3, TagC); - - ecs_entity_t e_4 = ecs_new(world, TagA); /* Non matching entity */ - ecs_add_id(world, e_4, TagD); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { TagB, .oper = EcsOr }, { TagC }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagC); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_only_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "?TagA" - })); - - ecs_entity_t e = ecs_new(world, TagA); - int32_t count = 0; - - ecs_iter_t it = ecs_filter_iter(world, &f); - while (ecs_filter_next(&it)) { - if (ecs_field_is_set(&it, 1)) { - test_assert(count == 0); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(TagA, ecs_field_id(&it, 1)); - count ++; - } - } - - test_int(count, 1); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_only_2_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA || TagB" - })); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, TagB); - ecs_new(world, TagC); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(TagB, ecs_field_id(&it, 1)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_only_3_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA || TagB || TagC" - })); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, TagB); - ecs_entity_t e4 = ecs_new(world, TagC); - ecs_new(world, TagD); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(TagB, ecs_field_id(&it, 1)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(TagC, ecs_field_id(&it, 1)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_or_other_type(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Position || Velocity" - })); - - test_assert(f.flags & EcsFilterNoData); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Velocity, {10, 20}); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_or_same_type(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Position || (Rel, Position)" - })); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set_pair_second(world, 0, Rel, Position, {10, 20}); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(sizeof(Position), ecs_field_size(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_uint(p->x, 10); - test_uint(p->y, 20); - } - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ecs_id(Position)), ecs_field_id(&it, 1)); - test_uint(sizeof(Position), ecs_field_size(&it, 1)); - { - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_uint(p->x, 10); - test_uint(p->y, 20); - } - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA || TagB, Foo" - })); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, TagB); - - ecs_add(world, e1, Foo); - ecs_add(world, e2, Foo); - ecs_add(world, e3, Foo); - - ecs_new(world, TagB); - ecs_new(world, TagC); - ecs_new(world, Foo); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(TagB, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_3_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA || TagB || TagC, Foo" - })); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, TagB); - ecs_add(world, e3, TagC); - ecs_entity_t e4 = ecs_new(world, TagC); - - ecs_add(world, e1, Foo); - ecs_add(world, e2, Foo); - ecs_add(world, e3, Foo); - ecs_add(world, e4, Foo); - - ecs_new(world, TagB); - ecs_new(world, TagC); - ecs_new(world, Foo); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(TagB, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(TagC, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_1_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_set(world, 0, Position, {10, 21}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {12, 23}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {14, 25}); - - ecs_add_id(world, e_3, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 21); - test_int(p[1].x, 12); - test_int(p[1].y, 23); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 14); - test_int(p[0].y, 25); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_components(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_set(world, 0, Position, {10, 21}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {12, 23}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {14, 25}); - - ecs_set(world, e_1, Velocity, {0, 1}); - ecs_set(world, e_2, Velocity, {2, 3}); - ecs_set(world, e_3, Velocity, {4, 5}); - - ecs_add_id(world, e_3, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - { - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 21); - test_int(p[1].x, 12); - test_int(p[1].y, 23); - - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v[0].x, 0); - test_int(v[0].y, 1); - test_int(v[1].x, 2); - test_int(v[1].y, 3); - } - - { - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 14); - test_int(p[0].y, 25); - - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v[0].x, 4); - test_int(v[0].y, 5); - } - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel, Tgt); - - ecs_add_id(world, e_3, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_pair(Rel, EcsWildcard) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Tgt)); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Tgt)); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_pair_ids(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, RelA); - ECS_TAG(world, ObjA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjB); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, RelA, ObjA); - ecs_entity_t e_2 = ecs_new_w_pair(world, RelA, ObjA); - ecs_entity_t e_3 = ecs_new_w_pair(world, RelA, ObjA); - - ecs_add_pair(world, e_1, RelB, ObjB); - ecs_add_pair(world, e_2, RelB, ObjB); - ecs_add_pair(world, e_3, RelB, ObjB); - - ecs_add_id(world, e_3, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { ecs_pair(RelA, ObjA) }, - { ecs_pair(RelB, EcsWildcard) } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(RelA, ObjA)); - test_int(ecs_field_id(&it, 2), ecs_pair(RelB, ObjB)); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(RelA, ObjA)); - test_int(ecs_field_id(&it, 2), ecs_pair(RelB, ObjB)); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_childof_pair_0_parent(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tgt); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, EcsChildOf, Tgt); - ecs_entity_t e_2 = ecs_new_w_pair(world, EcsChildOf, Tgt); - ecs_entity_t e_3 = ecs_new_w_pair(world, EcsChildOf, Tgt); - - ecs_entity_t e_4 = ecs_new_w_id(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_pair(EcsChildOf, 0) }} /* Special for ChildOf */ - })); - - int32_t count = 0; - - ecs_iter_t it = ecs_filter_iter(world, &f); - - while (ecs_filter_next(&it)) { - test_assert(it.count != 0); - int i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] != e_1); - test_assert(it.entities[i] != e_2); - test_assert(it.entities[i] != e_3); - - if (it.entities[i] == e_4) { - test_assert(count == 0); - count ++; - } - } - } - - test_int(count, 1); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_pred_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel, Tgt); - - ecs_add_id(world, e_3, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.id = Rel, .second.id = EcsWildcard }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Tgt)); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Tgt)); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_2_pred_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, RelA); - ECS_TAG(world, ObjA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjB); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, RelA, ObjA); - ecs_entity_t e_2 = ecs_new_w_pair(world, RelA, ObjA); - ecs_entity_t e_3 = ecs_new_w_pair(world, RelA, ObjA); - - ecs_add_pair(world, e_1, RelB, ObjB); - ecs_add_pair(world, e_2, RelB, ObjB); - ecs_add_pair(world, e_3, RelB, ObjB); - - ecs_add_id(world, e_3, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .first.id = RelA, .second.id = ObjA }, - { .first.id = RelB, .second.id = EcsWildcard } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(RelA, ObjA)); - test_int(ecs_field_id(&it, 2), ecs_pair(RelB, ObjB)); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(RelA, ObjA)); - test_int(ecs_field_id(&it, 2), ecs_pair(RelB, ObjB)); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_null(void) { - install_test_abort(); - ecs_world_t *world = ecs_mini(); - - test_expect_abort(); - ecs_filter_iter(world, NULL); -} - -void Filter_filter_iter_1_not_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - ECS_TAG(world, TagC); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, TagC); - - bool e_1_found = false; - bool e_2_found = false; - bool e_3_found = false; - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = TagC, .oper = EcsNot }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - while (ecs_filter_next(&it)) { - int i; - for (i = 0; i < it.count; i ++) { - if (it.entities[i] == e_1) { - test_bool(e_1_found, false); - e_1_found = true; - } else - if (it.entities[i] == e_2) { - test_bool(e_2_found, false); - e_2_found = true; - } else - if (it.entities[i] == e_3) { - e_3_found = true; - } - } - } - - test_bool (e_1_found, true); - test_bool (e_2_found, true); - test_bool (e_3_found, false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_tags_1_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_entity_t e_3 = ecs_new(world, TagA); - - ecs_add_id(world, e_3, TagB); - - ecs_entity_t e_4 = ecs_new(world, TagB); /* Non matching entity */ - ecs_add_id(world, e_4, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { TagB, .oper = EcsOptional }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), false); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), true); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_components_1_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_COMPONENT(world, Position); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_entity_t e_3 = ecs_new(world, TagA); - - ecs_add(world, e_3, Position); - - ecs_entity_t e_4 = ecs_new(world, Position); /* Non matching entity */ - ecs_add_id(world, e_4, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { ecs_id(Position), .oper = EcsOptional }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), ecs_id(Position)); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), false); - test_int(ecs_field_src(&it, 1), 0); - test_assert(ecs_field(&it, Position, 2) == NULL); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), ecs_id(Position)); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), true); - test_int(ecs_field_src(&it, 1), 0); - test_assert(ecs_field(&it, Position, 2) != NULL); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_in_stage(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new(world, Tag); - - ecs_readonly_begin(world); - - ecs_world_t *stage = ecs_get_stage(world, 0); - test_assert(stage != NULL); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(stage, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ Tag }} - })); - - ecs_iter_t it = ecs_filter_iter(stage, &f); - test_assert(it.real_world == world); - test_assert(it.world == stage); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], Tag); - ecs_iter_fini(&it); - - ecs_readonly_end(world); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_type_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - - ecs_add_id(world, e_2, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_assert(it.table != NULL); - const ecs_type_t *type = ecs_table_get_type(it.table); - test_int(type->count, 1); - test_int(type->array[0], TagA); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_assert(it.table != NULL); - type = ecs_table_get_type(it.table); - test_int(type->count, 2); - test_int(type->array[0], TagA); - test_int(type->array[1], TagB); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_10_tags(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - ECS_TAG(world, TagE); - ECS_TAG(world, TagF); - ECS_TAG(world, TagG); - ECS_TAG(world, TagH); - ECS_TAG(world, TagI); - ECS_TAG(world, TagJ); - ECS_TAG(world, TagK); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_add_id(world, e_1, TagC); - ecs_add_id(world, e_1, TagD); - ecs_add_id(world, e_1, TagE); - ecs_add_id(world, e_1, TagF); - ecs_add_id(world, e_1, TagG); - ecs_add_id(world, e_1, TagH); - ecs_add_id(world, e_1, TagI); - ecs_add_id(world, e_1, TagJ); - - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - ecs_add_id(world, e_2, TagC); - ecs_add_id(world, e_2, TagD); - ecs_add_id(world, e_2, TagE); - ecs_add_id(world, e_2, TagF); - ecs_add_id(world, e_2, TagG); - ecs_add_id(world, e_2, TagH); - ecs_add_id(world, e_2, TagI); - ecs_add_id(world, e_2, TagJ); - ecs_add_id(world, e_2, TagK); /* 2nd match in different table */ - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagA}, {TagB}, {TagC}, {TagD}, {TagE}, {TagF}, {TagG}, {TagH}, - {TagI}, {TagJ} - } - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_id(&it, 3), TagC); - test_int(ecs_field_id(&it, 4), TagD); - test_int(ecs_field_id(&it, 5), TagE); - test_int(ecs_field_id(&it, 6), TagF); - test_int(ecs_field_id(&it, 7), TagG); - test_int(ecs_field_id(&it, 8), TagH); - test_int(ecs_field_id(&it, 9), TagI); - test_int(ecs_field_id(&it, 10), TagJ); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_id(&it, 3), TagC); - test_int(ecs_field_id(&it, 4), TagD); - test_int(ecs_field_id(&it, 5), TagE); - test_int(ecs_field_id(&it, 6), TagF); - test_int(ecs_field_id(&it, 7), TagG); - test_int(ecs_field_id(&it, 8), TagH); - test_int(ecs_field_id(&it, 9), TagI); - test_int(ecs_field_id(&it, 10), TagJ); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_20_tags(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - ECS_TAG(world, TagE); - ECS_TAG(world, TagF); - ECS_TAG(world, TagG); - ECS_TAG(world, TagH); - ECS_TAG(world, TagI); - ECS_TAG(world, TagJ); - ECS_TAG(world, TagK); - ECS_TAG(world, TagL); - ECS_TAG(world, TagM); - ECS_TAG(world, TagN); - ECS_TAG(world, TagO); - ECS_TAG(world, TagP); - ECS_TAG(world, TagQ); - ECS_TAG(world, TagR); - ECS_TAG(world, TagS); - ECS_TAG(world, TagT); - ECS_TAG(world, TagU); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_add_id(world, e_1, TagC); - ecs_add_id(world, e_1, TagD); - ecs_add_id(world, e_1, TagE); - ecs_add_id(world, e_1, TagF); - ecs_add_id(world, e_1, TagG); - ecs_add_id(world, e_1, TagH); - ecs_add_id(world, e_1, TagI); - ecs_add_id(world, e_1, TagJ); - ecs_add_id(world, e_1, TagK); - ecs_add_id(world, e_1, TagL); - ecs_add_id(world, e_1, TagM); - ecs_add_id(world, e_1, TagN); - ecs_add_id(world, e_1, TagO); - ecs_add_id(world, e_1, TagP); - ecs_add_id(world, e_1, TagQ); - ecs_add_id(world, e_1, TagR); - ecs_add_id(world, e_1, TagS); - ecs_add_id(world, e_1, TagT); - - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - ecs_add_id(world, e_2, TagC); - ecs_add_id(world, e_2, TagD); - ecs_add_id(world, e_2, TagE); - ecs_add_id(world, e_2, TagF); - ecs_add_id(world, e_2, TagG); - ecs_add_id(world, e_2, TagH); - ecs_add_id(world, e_2, TagI); - ecs_add_id(world, e_2, TagJ); - ecs_add_id(world, e_2, TagK); - ecs_add_id(world, e_2, TagL); - ecs_add_id(world, e_2, TagM); - ecs_add_id(world, e_2, TagN); - ecs_add_id(world, e_2, TagO); - ecs_add_id(world, e_2, TagP); - ecs_add_id(world, e_2, TagQ); - ecs_add_id(world, e_2, TagR); - ecs_add_id(world, e_2, TagS); - ecs_add_id(world, e_2, TagT); - ecs_add_id(world, e_2, TagU); /* 2nd match in different table */ - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms_buffer = (ecs_term_t[]){ - {TagA}, {TagB}, {TagC}, {TagD}, {TagE}, {TagF}, {TagG}, {TagH}, - {TagI}, {TagJ}, {TagK}, {TagL}, {TagM}, {TagN}, {TagO}, {TagP}, - {TagQ}, {TagR}, {TagS}, {TagT} - }, - .terms_buffer_count = 20 - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_id(&it, 3), TagC); - test_int(ecs_field_id(&it, 4), TagD); - test_int(ecs_field_id(&it, 5), TagE); - test_int(ecs_field_id(&it, 6), TagF); - test_int(ecs_field_id(&it, 7), TagG); - test_int(ecs_field_id(&it, 8), TagH); - test_int(ecs_field_id(&it, 9), TagI); - test_int(ecs_field_id(&it, 10), TagJ); - test_int(ecs_field_id(&it, 11), TagK); - test_int(ecs_field_id(&it, 12), TagL); - test_int(ecs_field_id(&it, 13), TagM); - test_int(ecs_field_id(&it, 14), TagN); - test_int(ecs_field_id(&it, 15), TagO); - test_int(ecs_field_id(&it, 16), TagP); - test_int(ecs_field_id(&it, 17), TagQ); - test_int(ecs_field_id(&it, 18), TagR); - test_int(ecs_field_id(&it, 19), TagS); - test_int(ecs_field_id(&it, 20), TagT); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_id(&it, 3), TagC); - test_int(ecs_field_id(&it, 4), TagD); - test_int(ecs_field_id(&it, 5), TagE); - test_int(ecs_field_id(&it, 6), TagF); - test_int(ecs_field_id(&it, 7), TagG); - test_int(ecs_field_id(&it, 8), TagH); - test_int(ecs_field_id(&it, 9), TagI); - test_int(ecs_field_id(&it, 10), TagJ); - test_int(ecs_field_id(&it, 11), TagK); - test_int(ecs_field_id(&it, 12), TagL); - test_int(ecs_field_id(&it, 13), TagM); - test_int(ecs_field_id(&it, 14), TagN); - test_int(ecs_field_id(&it, 15), TagO); - test_int(ecs_field_id(&it, 16), TagP); - test_int(ecs_field_id(&it, 17), TagQ); - test_int(ecs_field_id(&it, 18), TagR); - test_int(ecs_field_id(&it, 19), TagS); - test_int(ecs_field_id(&it, 20), TagT); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -typedef struct { - float v; -} CompA, CompB, CompC, CompD, CompE, CompF, CompG, CompH, CompI, CompJ, CompK, - CompL, CompM, CompN, CompO, CompP, CompQ, CompR, CompS, CompT, CompU; - -void Filter_filter_iter_10_components(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, CompA); - ECS_COMPONENT(world, CompB); - ECS_COMPONENT(world, CompC); - ECS_COMPONENT(world, CompD); - ECS_COMPONENT(world, CompE); - ECS_COMPONENT(world, CompF); - ECS_COMPONENT(world, CompG); - ECS_COMPONENT(world, CompH); - ECS_COMPONENT(world, CompI); - ECS_COMPONENT(world, CompJ); - ECS_COMPONENT(world, CompK); - - ecs_entity_t e_1 = ecs_set(world, 0, CompA, {10}); - ecs_set(world, e_1, CompB, {10}); - ecs_set(world, e_1, CompC, {10}); - ecs_set(world, e_1, CompD, {10}); - ecs_set(world, e_1, CompE, {10}); - ecs_set(world, e_1, CompF, {10}); - ecs_set(world, e_1, CompG, {10}); - ecs_set(world, e_1, CompH, {10}); - ecs_set(world, e_1, CompI, {10}); - ecs_set(world, e_1, CompJ, {10}); - - ecs_entity_t e_2 = ecs_set(world, 0, CompA, {10}); - ecs_set(world, e_2, CompB, {10}); - ecs_set(world, e_2, CompC, {10}); - ecs_set(world, e_2, CompD, {10}); - ecs_set(world, e_2, CompE, {10}); - ecs_set(world, e_2, CompF, {10}); - ecs_set(world, e_2, CompG, {10}); - ecs_set(world, e_2, CompH, {10}); - ecs_set(world, e_2, CompI, {10}); - ecs_set(world, e_2, CompJ, {10}); - ecs_set(world, e_2, CompK, {10}); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {ecs_id(CompA)}, {ecs_id(CompB)}, {ecs_id(CompC)}, {ecs_id(CompD)}, - {ecs_id(CompE)}, {ecs_id(CompF)}, {ecs_id(CompG)}, {ecs_id(CompH)}, - {ecs_id(CompI)}, {ecs_id(CompJ)} - } - })); - - int i; - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), ecs_id(CompA)); - test_int(ecs_field_id(&it, 2), ecs_id(CompB)); - test_int(ecs_field_id(&it, 3), ecs_id(CompC)); - test_int(ecs_field_id(&it, 4), ecs_id(CompD)); - test_int(ecs_field_id(&it, 5), ecs_id(CompE)); - test_int(ecs_field_id(&it, 6), ecs_id(CompF)); - test_int(ecs_field_id(&it, 7), ecs_id(CompG)); - test_int(ecs_field_id(&it, 8), ecs_id(CompH)); - test_int(ecs_field_id(&it, 9), ecs_id(CompI)); - test_int(ecs_field_id(&it, 10), ecs_id(CompJ)); - - for (i = 0; i < 10; i ++) { - CompA *ptr = ecs_field_w_size(&it, 0, i + 1); - test_assert(ptr != NULL); - test_int(ptr[0].v, 10); - } - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(CompA)); - test_int(ecs_field_id(&it, 2), ecs_id(CompB)); - test_int(ecs_field_id(&it, 3), ecs_id(CompC)); - test_int(ecs_field_id(&it, 4), ecs_id(CompD)); - test_int(ecs_field_id(&it, 5), ecs_id(CompE)); - test_int(ecs_field_id(&it, 6), ecs_id(CompF)); - test_int(ecs_field_id(&it, 7), ecs_id(CompG)); - test_int(ecs_field_id(&it, 8), ecs_id(CompH)); - test_int(ecs_field_id(&it, 9), ecs_id(CompI)); - test_int(ecs_field_id(&it, 10), ecs_id(CompJ)); - - for (i = 0; i < 10; i ++) { - CompA *ptr = ecs_field_w_size(&it, 0, i + 1); - test_assert(ptr != NULL); - test_int(ptr[0].v, 10); - } - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_20_components(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, CompA); - ECS_COMPONENT(world, CompB); - ECS_COMPONENT(world, CompC); - ECS_COMPONENT(world, CompD); - ECS_COMPONENT(world, CompE); - ECS_COMPONENT(world, CompF); - ECS_COMPONENT(world, CompG); - ECS_COMPONENT(world, CompH); - ECS_COMPONENT(world, CompI); - ECS_COMPONENT(world, CompJ); - ECS_COMPONENT(world, CompK); - ECS_COMPONENT(world, CompL); - ECS_COMPONENT(world, CompM); - ECS_COMPONENT(world, CompN); - ECS_COMPONENT(world, CompO); - ECS_COMPONENT(world, CompP); - ECS_COMPONENT(world, CompQ); - ECS_COMPONENT(world, CompR); - ECS_COMPONENT(world, CompS); - ECS_COMPONENT(world, CompT); - ECS_COMPONENT(world, CompU); - - ecs_entity_t e_1 = ecs_set(world, 0, CompA, {10}); - ecs_set(world, e_1, CompB, {10}); - ecs_set(world, e_1, CompC, {10}); - ecs_set(world, e_1, CompD, {10}); - ecs_set(world, e_1, CompE, {10}); - ecs_set(world, e_1, CompF, {10}); - ecs_set(world, e_1, CompG, {10}); - ecs_set(world, e_1, CompH, {10}); - ecs_set(world, e_1, CompI, {10}); - ecs_set(world, e_1, CompJ, {10}); - ecs_set(world, e_1, CompK, {10}); - ecs_set(world, e_1, CompL, {10}); - ecs_set(world, e_1, CompM, {10}); - ecs_set(world, e_1, CompN, {10}); - ecs_set(world, e_1, CompO, {10}); - ecs_set(world, e_1, CompP, {10}); - ecs_set(world, e_1, CompQ, {10}); - ecs_set(world, e_1, CompR, {10}); - ecs_set(world, e_1, CompS, {10}); - ecs_set(world, e_1, CompT, {10}); - - ecs_entity_t e_2 = ecs_set(world, 0, CompA, {10}); - ecs_set(world, e_2, CompB, {10}); - ecs_set(world, e_2, CompC, {10}); - ecs_set(world, e_2, CompD, {10}); - ecs_set(world, e_2, CompE, {10}); - ecs_set(world, e_2, CompF, {10}); - ecs_set(world, e_2, CompG, {10}); - ecs_set(world, e_2, CompH, {10}); - ecs_set(world, e_2, CompI, {10}); - ecs_set(world, e_2, CompJ, {10}); - ecs_set(world, e_2, CompK, {10}); - ecs_set(world, e_2, CompL, {10}); - ecs_set(world, e_2, CompM, {10}); - ecs_set(world, e_2, CompN, {10}); - ecs_set(world, e_2, CompO, {10}); - ecs_set(world, e_2, CompP, {10}); - ecs_set(world, e_2, CompQ, {10}); - ecs_set(world, e_2, CompR, {10}); - ecs_set(world, e_2, CompS, {10}); - ecs_set(world, e_2, CompT, {10}); - ecs_set(world, e_2, CompU, {10}); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms_buffer = (ecs_term_t[]){ - {ecs_id(CompA)}, {ecs_id(CompB)}, {ecs_id(CompC)}, {ecs_id(CompD)}, - {ecs_id(CompE)}, {ecs_id(CompF)}, {ecs_id(CompG)}, {ecs_id(CompH)}, - {ecs_id(CompI)}, {ecs_id(CompJ)}, {ecs_id(CompK)}, {ecs_id(CompL)}, - {ecs_id(CompM)}, {ecs_id(CompN)}, {ecs_id(CompO)}, {ecs_id(CompP)}, - {ecs_id(CompQ)}, {ecs_id(CompR)}, {ecs_id(CompS)}, {ecs_id(CompT)} - }, - .terms_buffer_count = 20 - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), ecs_id(CompA)); - test_int(ecs_field_id(&it, 2), ecs_id(CompB)); - test_int(ecs_field_id(&it, 3), ecs_id(CompC)); - test_int(ecs_field_id(&it, 4), ecs_id(CompD)); - test_int(ecs_field_id(&it, 5), ecs_id(CompE)); - test_int(ecs_field_id(&it, 6), ecs_id(CompF)); - test_int(ecs_field_id(&it, 7), ecs_id(CompG)); - test_int(ecs_field_id(&it, 8), ecs_id(CompH)); - test_int(ecs_field_id(&it, 9), ecs_id(CompI)); - test_int(ecs_field_id(&it, 10), ecs_id(CompJ)); - test_int(ecs_field_id(&it, 11), ecs_id(CompK)); - test_int(ecs_field_id(&it, 12), ecs_id(CompL)); - test_int(ecs_field_id(&it, 13), ecs_id(CompM)); - test_int(ecs_field_id(&it, 14), ecs_id(CompN)); - test_int(ecs_field_id(&it, 15), ecs_id(CompO)); - test_int(ecs_field_id(&it, 16), ecs_id(CompP)); - test_int(ecs_field_id(&it, 17), ecs_id(CompQ)); - test_int(ecs_field_id(&it, 18), ecs_id(CompR)); - test_int(ecs_field_id(&it, 19), ecs_id(CompS)); - test_int(ecs_field_id(&it, 20), ecs_id(CompT)); - - int i; - for (i = 0; i < 20; i ++) { - CompA *ptr = ecs_field_w_size(&it, 0, i + 1); - test_assert(ptr != NULL); - test_int(ptr[0].v, 10); - } - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(CompA)); - test_int(ecs_field_id(&it, 2), ecs_id(CompB)); - test_int(ecs_field_id(&it, 3), ecs_id(CompC)); - test_int(ecs_field_id(&it, 4), ecs_id(CompD)); - test_int(ecs_field_id(&it, 5), ecs_id(CompE)); - test_int(ecs_field_id(&it, 6), ecs_id(CompF)); - test_int(ecs_field_id(&it, 7), ecs_id(CompG)); - test_int(ecs_field_id(&it, 8), ecs_id(CompH)); - test_int(ecs_field_id(&it, 9), ecs_id(CompI)); - test_int(ecs_field_id(&it, 10), ecs_id(CompJ)); - test_int(ecs_field_id(&it, 11), ecs_id(CompK)); - test_int(ecs_field_id(&it, 12), ecs_id(CompL)); - test_int(ecs_field_id(&it, 13), ecs_id(CompM)); - test_int(ecs_field_id(&it, 14), ecs_id(CompN)); - test_int(ecs_field_id(&it, 15), ecs_id(CompO)); - test_int(ecs_field_id(&it, 16), ecs_id(CompP)); - test_int(ecs_field_id(&it, 17), ecs_id(CompQ)); - test_int(ecs_field_id(&it, 18), ecs_id(CompR)); - test_int(ecs_field_id(&it, 19), ecs_id(CompS)); - test_int(ecs_field_id(&it, 20), ecs_id(CompT)); - - for (i = 0; i < 20; i ++) { - CompA *ptr = ecs_field_w_size(&it, 0, i + 1); - test_assert(ptr != NULL); - test_int(ptr[0].v, 10); - } - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_new(world, TagA); - - ecs_add_pair(world, e_1, EcsIsA, base); - ecs_add_pair(world, e_2, EcsIsA, base); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TagA }, - { TagA, .src.flags = EcsUp } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagA); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t parent = ecs_new(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_new(world, TagA); - - ecs_add_pair(world, e_1, EcsChildOf, parent); - ecs_add_pair(world, e_2, EcsChildOf, parent); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagA }, - {TagA, .src.flags = EcsUp, .src.trav = EcsChildOf} - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagA); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_term_iter_w_readonly_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { TagA, .inout = EcsIn }); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_bool(ecs_field_is_readonly(&it, 1), true); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_filter_iter_w_readonly_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e_1 = ecs_new(world, Position); - ecs_add(world, e_1, Velocity); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position), .inout = EcsIn }, { ecs_id(Velocity) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - test_bool(ecs_field_is_readonly(&it, 1), true); - test_bool(ecs_field_is_readonly(&it, 2), false); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_w_from_nothing_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA, .inout = EcsIn }, { TagB, .src.flags = EcsIsEntity }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_src(&it, 1), 0); - test_int(ecs_field_src(&it, 2), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_w_rel_wildcard_n_matches(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel_1); - ECS_TAG(world, Rel_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_4 = ecs_new_w_pair(world, Rel_1, Obj_1); - - ecs_add_pair(world, e_3, Rel_1, Obj_2); - ecs_add_pair(world, e_4, Rel_1, Obj_2); - - ecs_add_pair(world, e_3, Rel_2, Obj_1); - ecs_add_pair(world, e_4, Rel_2, Obj_1); - - ecs_add(world, e_4, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_pair(EcsWildcard, Obj_1) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj_1)); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_w_obj_wildcard_n_matches(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, Obj_1); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, Obj_1); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel, Obj_1); - ecs_entity_t e_4 = ecs_new_w_pair(world, Rel, Obj_1); - - ecs_add_pair(world, e_3, Rel, Obj_2); - ecs_add_pair(world, e_4, Rel, Obj_2); - - ecs_add(world, e_4, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_pair(Rel, EcsWildcard) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_2)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_2)); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_w_2_wildcards_1_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel_1); - ECS_TAG(world, Rel_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_4 = ecs_new_w_pair(world, Rel_1, Obj_1); - - ecs_add_pair(world, e_1, Rel_2, Obj_1); - ecs_add_pair(world, e_2, Rel_2, Obj_1); - ecs_add_pair(world, e_3, Rel_2, Obj_1); - ecs_add_pair(world, e_4, Rel_2, Obj_1); - - ecs_add(world, e_4, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { ecs_pair(Rel_1, EcsWildcard) }, - { ecs_pair(Rel_2, EcsWildcard) } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 3); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(it.entities[2], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_w_2_wildcards_2x1_matches(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel_1); - ECS_TAG(world, Rel_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_4 = ecs_new_w_pair(world, Rel_1, Obj_1); - - ecs_add_pair(world, e_3, Rel_1, Obj_2); - ecs_add_pair(world, e_4, Rel_1, Obj_2); - - ecs_add_pair(world, e_1, Rel_2, Obj_1); - ecs_add_pair(world, e_2, Rel_2, Obj_1); - ecs_add_pair(world, e_3, Rel_2, Obj_1); - ecs_add_pair(world, e_4, Rel_2, Obj_1); - - ecs_add(world, e_4, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { ecs_pair(Rel_1, EcsWildcard) }, - { ecs_pair(Rel_2, EcsWildcard) } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_2)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_2)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_w_2_wildcards_2x2_matches(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel_1); - ECS_TAG(world, Rel_2); - ECS_TAG(world, Obj_1); - ECS_TAG(world, Obj_2); - ECS_TAG(world, Tag); - - ecs_entity_t e_1 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_2 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_3 = ecs_new_w_pair(world, Rel_1, Obj_1); - ecs_entity_t e_4 = ecs_new_w_pair(world, Rel_1, Obj_1); - - ecs_add_pair(world, e_3, Rel_1, Obj_2); - ecs_add_pair(world, e_4, Rel_1, Obj_2); - - ecs_add_pair(world, e_1, Rel_2, Obj_1); - ecs_add_pair(world, e_2, Rel_2, Obj_1); - ecs_add_pair(world, e_3, Rel_2, Obj_1); - ecs_add_pair(world, e_4, Rel_2, Obj_1); - - ecs_add_pair(world, e_1, Rel_2, Obj_2); - ecs_add_pair(world, e_2, Rel_2, Obj_2); - ecs_add_pair(world, e_3, Rel_2, Obj_2); - ecs_add_pair(world, e_4, Rel_2, Obj_2); - - ecs_add(world, e_4, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { ecs_pair(Rel_1, EcsWildcard) }, - { ecs_pair(Rel_2, EcsWildcard) } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_2)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_2)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_2)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_2)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_2)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_1)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_2)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_2)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_1)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_1, Obj_2)); - test_int(ecs_field_id(&it, 2), ecs_pair(Rel_2, Obj_2)); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_w_3_wildcards_2x2x2_matches(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, X); - ECS_TAG(world, Y); - ECS_TAG(world, Z); - - ECS_TAG(world, A); - ECS_TAG(world, B); - ECS_TAG(world, C); - - ECS_ENTITY(world, E, - (X, A), (X, B), (Y, A), (Y, B), (Z, A), (Z, B)); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = ecs_pair(X, EcsWildcard) }, - { .id = ecs_pair(Y, EcsWildcard) }, - { .id = ecs_pair(Z, EcsWildcard) } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - char *result, *expect; - - test_bool(ecs_filter_next(&it), true); - result = ecs_iter_str(&it); expect = - HEAD "id: (X,A),(Y,A),(Z,A)" - LINE "src: 0,0,0" - LINE "set: true,true,true" - LINE "this:" - LINE " - E" - LINE; - test_str(result, expect); - ecs_os_free(result); - - test_bool(ecs_filter_next(&it), true); - result = ecs_iter_str(&it); expect = - HEAD "id: (X,A),(Y,A),(Z,B)" - LINE "src: 0,0,0" - LINE "set: true,true,true" - LINE "this:" - LINE " - E" - LINE; - test_str(result, expect); - ecs_os_free(result); - - test_bool(ecs_filter_next(&it), true); - result = ecs_iter_str(&it); expect = - HEAD "id: (X,A),(Y,B),(Z,A)" - LINE "src: 0,0,0" - LINE "set: true,true,true" - LINE "this:" - LINE " - E" - LINE; - test_str(result, expect); - ecs_os_free(result); - - test_bool(ecs_filter_next(&it), true); - result = ecs_iter_str(&it); expect = - HEAD "id: (X,A),(Y,B),(Z,B)" - LINE "src: 0,0,0" - LINE "set: true,true,true" - LINE "this:" - LINE " - E" - LINE; - test_str(result, expect); - ecs_os_free(result); - - test_bool(ecs_filter_next(&it), true); - result = ecs_iter_str(&it); expect = - HEAD "id: (X,B),(Y,A),(Z,A)" - LINE "src: 0,0,0" - LINE "set: true,true,true" - LINE "this:" - LINE " - E" - LINE; - test_str(result, expect); - ecs_os_free(result); - - test_bool(ecs_filter_next(&it), true); - result = ecs_iter_str(&it); expect = - HEAD "id: (X,B),(Y,A),(Z,B)" - LINE "src: 0,0,0" - LINE "set: true,true,true" - LINE "this:" - LINE " - E" - LINE; - test_str(result, expect); - ecs_os_free(result); - - test_bool(ecs_filter_next(&it), true); - result = ecs_iter_str(&it); expect = - HEAD "id: (X,B),(Y,B),(Z,A)" - LINE "src: 0,0,0" - LINE "set: true,true,true" - LINE "this:" - LINE " - E" - LINE; - test_str(result, expect); - ecs_os_free(result); - - test_bool(ecs_filter_next(&it), true); - result = ecs_iter_str(&it); expect = - HEAD "id: (X,B),(Y,B),(Z,B)" - LINE "src: 0,0,0" - LINE "set: true,true,true" - LINE "this:" - LINE " - E" - LINE; - test_str(result, expect); - ecs_os_free(result); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_w_wildcard_and_nothing(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, R1); - ECS_TAG(world, R2); - ECS_TAG(world, O); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, R1, O); - ecs_add_pair(world, e, R2, O); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = ecs_pair(EcsWildcard, O) }, - { .id = ecs_pair(EcsWildcard, O), .src.flags = EcsIsEntity } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_pair(R1, O), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, O), ecs_field_id(&it, 2)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_pair(R2, O), ecs_field_id(&it, 1)); - test_uint(ecs_pair(EcsWildcard, O), ecs_field_id(&it, 2)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_any(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { EcsAny }} - })); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - ecs_add(world, e4, TagC); - ecs_add(world, e4, TagD); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e4); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_wildcard_in_2nd_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_add_pair(world, e1, Rel, TgtA); - ecs_add_pair(world, e1, Rel, TgtB); - - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_add_pair(world, e2, Rel, TgtA); - - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_add_pair(world, e3, Rel, TgtA); - ecs_add_pair(world, e3, Rel, TgtC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ Tag }, { ecs_pair(Rel, EcsWildcard) }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtB), it.ids[1]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtC), it.ids[1]); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2nd_term_self_create_id_after_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t *f_1 = ecs_filter(world, { - .terms = { - { TagA }, - { TagB, .src.flags = EcsSelf } - } - }); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add(world, e1, TagB); - ecs_new(world, TagB); - - ecs_iter_t it = ecs_filter_iter(world, f_1); - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f_1); - - ecs_fini(world); -} - -void Filter_filter_iter_wildcard_in_2nd_term_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_add_pair(world, e1, Rel, TgtA); - ecs_add_pair(world, e1, Rel, TgtB); - - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_add_pair(world, e2, Rel, TgtA); - - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_add_pair(world, e3, Rel, TgtA); - ecs_add_pair(world, e3, Rel, TgtC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ Tag }, { ecs_pair(Rel, EcsWildcard), .src.flags = EcsSelf }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtB), it.ids[1]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtC), it.ids[1]); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_any_match_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ EcsAny }} - })); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_entity_t prev = 0; - int32_t count = 0, e1_matched = 0; - while (ecs_filter_next(&it)) { - test_assert(it.count > 0); - test_assert(!prev || prev != it.entities[0]); - prev = it.entities[0]; - if (it.entities[0] == e1) { - e1_matched ++; - } - count ++; - } - - test_assert(count > 0); - test_int(e1_matched, 1); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_any_match_tag_and_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - ECS_TAG(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ EcsAny }} - })); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); - ecs_entity_t e2 = ecs_new(world, Tag); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_entity_t prev = 0; - int32_t count = 0, e1_matched = 0, e2_matched = 0; - while (ecs_filter_next(&it)) { - test_assert(it.count > 0); - test_assert(!prev || prev != it.entities[0]); - prev = it.entities[0]; - if (it.entities[0] == e1) { - e1_matched ++; - } - if (it.entities[0] == e2) { - e2_matched ++; - } - count ++; - } - - test_assert(count > 0); - test_int(e1_matched, 1); - test_int(e2_matched, 1); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_any_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - ECS_TAG(world, ObjC); - ECS_TAG(world, ObjD); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = ecs_pair(Rel, ObjA) }, - { .id = ecs_pair(Rel, EcsAny) } - } - })); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e4 = ecs_new_w_pair(world, Rel, ObjA); - - ecs_add_pair(world, e2, Rel, ObjB); - ecs_add_pair(world, e3, Rel, ObjC); - ecs_add_pair(world, e4, Rel, ObjC); - ecs_add_pair(world, e4, Rel, ObjD); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e4); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_not_any(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = EcsAny, .oper = EcsNot }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_not_any_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = Tag }, - { .id = ecs_pair(Rel, EcsAny), .oper = EcsNot } - } - })); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - - ecs_add_pair(world, e2, Rel, ObjA); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_cascade_isa(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - ecs_table_t *t1 = ecs_get_table(world, inst); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = TagA, .src.flags = EcsCascade }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_cascade_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); - ecs_table_t *t1 = ecs_get_table(world, inst); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = TagA, - .src.flags = EcsCascade, - .src.trav = EcsChildOf - }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_2_rel_instances(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsTraversable); - - ecs_entity_t b1 = ecs_new(world, TagA); - ecs_entity_t b2 = ecs_new(world, TagA); - ecs_add(world, b1, TagB); - ecs_add(world, b2, TagB); - ecs_add(world, b1, TagC); - ecs_add(world, b2, TagC); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Rel, b1); - ecs_add_pair(world, e, Rel, b2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagC, .src.flags = EcsUp, .src.trav = Rel } - } - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(it.sources[0], b1); - test_uint(it.ids[0], TagC); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_2_rel_instances_match_2nd(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsTraversable); - - ecs_entity_t b1 = ecs_new(world, TagA); - ecs_entity_t b2 = ecs_new(world, TagA); - ecs_add(world, b1, TagB); - ecs_add(world, b2, TagB); - // ecs_add(world, b1, TagC); - ecs_add(world, b2, TagC); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Rel, b1); - ecs_add_pair(world, e, Rel, b2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagC, .src.flags = EcsUp, .src.trav = Rel } - } - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(it.sources[0], b2); - test_uint(it.ids[0], TagC); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_2_levels(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_set(world, base, Position, {10, 20}); - - ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_2, EcsIsA, base); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_2); - - ecs_filter_t *f = ecs_filter(world, { - .terms[0] = { .id = ecs_id(Position) } - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_only_w_owned(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, R, EcsTraversable); - ECS_COMPONENT(world, Position); - - ecs_entity_t e_0 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_1 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {50, 60}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {70, 80}); - - ecs_add_pair(world, e_3, R, e_2); - ecs_add_pair(world, e_2, R, e_1); - ecs_add_pair(world, e_1, R, e_0); - - ecs_filter_t *f = ecs_filter(world, { - .expr = "Position(up(R))" - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_3); - test_uint(it.sources[0], e_2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_2); - test_uint(it.sources[0], e_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_1); - test_uint(it.sources[0], e_0); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_after_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_set(world, base, Position, {10, 20}); - - ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_2, EcsIsA, base); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_2); - - ecs_filter_t *f = ecs_filter(world, { - .terms[0] = { .id = ecs_id(Position) } - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_filter_next(&it)); - - ecs_set(world, base_2, Position, {20, 30}); - - it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base_2, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_after_remove(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_set(world, base, Position, {10, 20}); - - ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_2, EcsIsA, base); - ecs_set(world, base_2, Position, {20, 30}); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_2); - - ecs_filter_t *f = ecs_filter(world, { - .terms[0] = { .id = ecs_id(Position) } - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base_2, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - test_bool(false, ecs_filter_next(&it)); - - ecs_remove(world, base_2, Position); - - it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_after_clear(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_set(world, base, Position, {10, 20}); - - ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_2, EcsIsA, base); - ecs_set(world, base_2, Position, {20, 30}); - - ecs_entity_t base_3 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_3, EcsIsA, base); - ecs_set(world, base_3, Position, {40, 50}); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_2); - ecs_add_pair(world, inst, EcsIsA, base_3); - - ecs_filter_t *f = ecs_filter(world, { - .terms[0] = { .id = ecs_id(Position) } - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base_2, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - test_bool(false, ecs_filter_next(&it)); - - ecs_clear(world, base_2); - - it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base_3, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 40); - test_int(p->y, 50); - test_bool(false, ecs_filter_next(&it)); - - ecs_clear(world, base_3); - - it = ecs_filter_iter(world, f); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_after_delete(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_set(world, base, Position, {10, 20}); - - ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_2, EcsIsA, base); - ecs_set(world, base_2, Position, {20, 30}); - - ecs_entity_t base_3 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_3, EcsIsA, base); - ecs_set(world, base_3, Position, {40, 50}); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_2); - ecs_add_pair(world, inst, EcsIsA, base_3); - - ecs_filter_t *f = ecs_filter(world, { - .terms[0] = { .id = ecs_id(Position) } - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base_2, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - test_bool(false, ecs_filter_next(&it)); - - ecs_delete(world, base_2); - - it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base_3, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 40); - test_int(p->y, 50); - test_bool(false, ecs_filter_next(&it)); - - ecs_delete(world, base_3); - - it = ecs_filter_iter(world, f); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_terms_superset_2_rel_instances(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsTraversable); - - ecs_entity_t b1 = ecs_new(world, TagA); - ecs_entity_t b2 = ecs_new(world, TagA); - ecs_add(world, b1, TagB); - ecs_add(world, b2, TagB); - ecs_add(world, b1, TagC); - ecs_add(world, b2, TagC); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, TagA); - ecs_add_pair(world, e, Rel, b1); - ecs_add_pair(world, e, Rel, b2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagA}, - {TagC, .src.flags = EcsUp, .src.trav = Rel } - } - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(it.sources[0], 0); - test_uint(it.sources[1], b1); - test_uint(it.ids[0], TagA); - test_uint(it.ids[1], TagC); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_terms_superset_2_rel_instances_match_2nd(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsTraversable); - - ecs_entity_t b1 = ecs_new(world, TagA); - ecs_entity_t b2 = ecs_new(world, TagA); - ecs_add(world, b1, TagB); - ecs_add(world, b2, TagB); - // ecs_add(world, b1, TagC); - ecs_add(world, b2, TagC); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, TagA); - ecs_add_pair(world, e, Rel, b1); - ecs_add_pair(world, e, Rel, b2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - {TagA}, - {TagC, .src.flags = EcsUp, .src.trav = Rel } - } - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(it.sources[0], 0); - test_uint(it.sources[1], b2); - test_uint(it.ids[0], TagA); - test_uint(it.ids[1], TagC); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_parent_w_isa(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base_1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t base = ecs_new_w_pair(world, EcsIsA, base_1); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); - - ecs_filter_t *f = ecs_filter(world, { - .terms[0] = { .id = ecs_id(Position), .src = { - .flags = EcsParent - }} - }); - - test_assert(f != NULL); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(base_1, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_isa_after_remove_parent(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ ecs_id(Position), .src.flags = EcsUp, .inout = EcsIn }} - }); - - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_set(world, base, Position, {10, 20}); - - ecs_entity_t parent0 = ecs_new(world, 0); - ecs_entity_t parent1 = ecs_new(world, 0); - ecs_entity_t child0 = ecs_new_w_pair(world, EcsChildOf, parent0); - ecs_entity_t child1 = ecs_new_w_pair(world, EcsChildOf, parent1); - ecs_add_pair(world, child0, EcsIsA, base); - ecs_add_pair(world, child1, EcsIsA, base); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_uint(1, it.count); - test_uint(child0, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - - test_bool(true, ecs_filter_next(&it)); - test_uint(1, it.count); - test_uint(child1, it.entities[0]); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_bool(false, ecs_filter_next(&it)); - - ecs_delete(world, parent1); - - it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_uint(1, it.count); - test_uint(child0, it.entities[0]); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_isa_create_table_after_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t tag = ecs_new_id(world); - ecs_entity_t base = ecs_new_id(world); - ecs_add_id(world, base, EcsPrefab); - ecs_set(world, base, Position, {10, 20}); - ecs_entity_t inst_1 = ecs_new_w_pair(world, EcsIsA, base); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ ecs_id(Position), .src.flags = EcsUp }} - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst_1, it.entities[0]); - test_uint(base, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_filter_next(&it)); - - ecs_entity_t inst_2 = ecs_new_w_pair(world, EcsIsA, base); - ecs_add_id(world, inst_2, tag); - - it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst_1, it.entities[0]); - test_uint(base, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(inst_2, it.entities[0]); - test_uint(base, it.sources[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_2_relations(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { .id = TagA, .src.trav = EcsChildOf, .src.flags = EcsUp }, - { .id = TagA, .src.trav = EcsIsA, .src.flags = EcsUp }, - } - }); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t parent = ecs_new(world, TagA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(2, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_2_relations_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { .id = TagA, .src.trav = EcsChildOf, .src.flags = EcsUp }, - { .id = TagA, .src.trav = EcsIsA, .src.flags = EcsUp }, - }, - .instanced = true - }); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t parent = ecs_new(world, TagA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(2, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_2_relations_w_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { .id = ecs_id(Position), .src.trav = EcsChildOf, .src.flags = EcsUp }, - { .id = ecs_id(Position), .src.trav = EcsIsA, .src.flags = EcsUp }, - } - }); - - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t parent = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_iter_t it = ecs_filter_iter(world, f); - { - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - Position *p1 = ecs_field(&it, Position, 1); - Position *p2 = ecs_field(&it, Position, 2); - test_assert(p1 != NULL); - test_assert(p2 != NULL); - test_int(p1->x, 30); - test_int(p1->y, 40); - test_int(p2->x, 10); - test_int(p2->y, 20); - } - { - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - Position *p1 = ecs_field(&it, Position, 1); - Position *p2 = ecs_field(&it, Position, 2); - test_assert(p1 != NULL); - test_assert(p2 != NULL); - test_int(p1->x, 30); - test_int(p1->y, 40); - test_int(p2->x, 10); - test_int(p2->y, 20); - } - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_superset_2_relations_instanced_w_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { .id = ecs_id(Position), .src.trav = EcsChildOf, .src.flags = EcsUp }, - { .id = ecs_id(Position), .src.trav = EcsIsA, .src.flags = EcsUp }, - }, - .instanced = true - }); - - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t parent = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_iter_t it = ecs_filter_iter(world, f); - { - test_bool(true, ecs_filter_next(&it)); - test_int(2, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - Position *p1 = ecs_field(&it, Position, 1); - Position *p2 = ecs_field(&it, Position, 2); - test_assert(p1 != NULL); - test_assert(p2 != NULL); - test_int(p1->x, 30); - test_int(p1->y, 40); - test_int(p2->x, 10); - test_int(p2->y, 20); - } - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_not_up_disabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { TagA }, - { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsChildOf, .oper = EcsNot } - } - }); - test_assert(f != NULL); - - ecs_entity_t parent = ecs_new_w_id(world, EcsDisabled); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add_id(world, e2, EcsDisabled); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, EcsChildOf, parent); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, it.ids[0]); - test_uint(0, it.sources[0]); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_iter_pair_wildcard_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Mass); - - ecs_filter_t *f = ecs_filter(world, { - .terms = { - { .id = ecs_pair(EcsWildcard, ecs_id(Position)) }, - } - }); - test_assert(f != NULL); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, ecs_id(Mass), ecs_id(Position)); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, it.sizes[0]); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_w_10_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - ECS_TAG(world, TagE); - ECS_TAG(world, TagF); - ECS_TAG(world, TagG); - ECS_TAG(world, TagH); - ECS_TAG(world, TagI); - ECS_TAG(world, TagJ); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = { - {TagA}, {TagB}, {TagC}, {TagD}, {TagE}, {TagF}, {TagG}, {TagH}, - {TagI}, {TagJ} - } - })); - - test_int(f_1.term_count, 10); - test_assert(f_1.terms != NULL); - test_int(f_1.terms[0].id, TagA); - test_int(f_1.terms[1].id, TagB); - test_int(f_1.terms[2].id, TagC); - test_int(f_1.terms[3].id, TagD); - test_int(f_1.terms[4].id, TagE); - test_int(f_1.terms[5].id, TagF); - test_int(f_1.terms[6].id, TagG); - test_int(f_1.terms[7].id, TagH); - test_int(f_1.terms[8].id, TagI); - test_int(f_1.terms[9].id, TagJ); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, TagA); - ecs_add(world, e, TagB); - ecs_add(world, e, TagC); - ecs_add(world, e, TagD); - ecs_add(world, e, TagE); - ecs_add(world, e, TagF); - ecs_add(world, e, TagG); - ecs_add(world, e, TagH); - ecs_add(world, e, TagI); - ecs_add(world, e, TagJ); - - ecs_iter_t it = ecs_filter_iter(world, &f_1); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], TagA); - test_int(it.ids[1], TagB); - test_int(it.ids[2], TagC); - test_int(it.ids[3], TagD); - test_int(it.ids[4], TagE); - test_int(it.ids[5], TagF); - test_int(it.ids[6], TagG); - test_int(it.ids[7], TagH); - test_int(it.ids[8], TagI); - test_int(it.ids[9], TagJ); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f_1); - - ecs_fini(world); -} - -void Filter_filter_w_10_terms_move(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - ECS_TAG(world, TagE); - ECS_TAG(world, TagF); - ECS_TAG(world, TagG); - ECS_TAG(world, TagH); - ECS_TAG(world, TagI); - ECS_TAG(world, TagJ); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = { - {TagA}, {TagB}, {TagC}, {TagD}, {TagE}, {TagF}, {TagG}, {TagH}, - {TagI}, {TagJ} - } - })); - - test_int(f_1.term_count, 10); - test_assert(f_1.terms != NULL); - test_int(f_1.terms[0].id, TagA); - test_int(f_1.terms[1].id, TagB); - test_int(f_1.terms[2].id, TagC); - test_int(f_1.terms[3].id, TagD); - test_int(f_1.terms[4].id, TagE); - test_int(f_1.terms[5].id, TagF); - test_int(f_1.terms[6].id, TagG); - test_int(f_1.terms[7].id, TagH); - test_int(f_1.terms[8].id, TagI); - test_int(f_1.terms[9].id, TagJ); - - ecs_filter_t f_2 = {0}; - ecs_filter_move(&f_2, &f_1); - - test_int(f_2.term_count, 10); - test_assert(f_2.terms != NULL); - test_int(f_2.terms[0].id, TagA); - test_int(f_2.terms[1].id, TagB); - test_int(f_2.terms[2].id, TagC); - test_int(f_2.terms[3].id, TagD); - test_int(f_2.terms[4].id, TagE); - test_int(f_2.terms[5].id, TagF); - test_int(f_2.terms[6].id, TagG); - test_int(f_2.terms[7].id, TagH); - test_int(f_2.terms[8].id, TagI); - test_int(f_2.terms[9].id, TagJ); - - test_int(f_1.term_count, 0); - test_assert(f_1.terms == NULL); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_filter_w_10_terms_copy(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - ECS_TAG(world, TagE); - ECS_TAG(world, TagF); - ECS_TAG(world, TagG); - ECS_TAG(world, TagH); - ECS_TAG(world, TagI); - ECS_TAG(world, TagJ); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = { - {TagA}, {TagB}, {TagC}, {TagD}, {TagE}, {TagF}, {TagG}, {TagH}, - {TagI}, {TagJ} - } - })); - - test_int(f_1.term_count, 10); - test_assert(f_1.terms != NULL); - test_int(f_1.terms[0].id, TagA); - test_int(f_1.terms[1].id, TagB); - test_int(f_1.terms[2].id, TagC); - test_int(f_1.terms[3].id, TagD); - test_int(f_1.terms[4].id, TagE); - test_int(f_1.terms[5].id, TagF); - test_int(f_1.terms[6].id, TagG); - test_int(f_1.terms[7].id, TagH); - test_int(f_1.terms[8].id, TagI); - test_int(f_1.terms[9].id, TagJ); - - ecs_filter_t f_2 = {0}; - ecs_filter_copy(&f_2, &f_1); - - test_int(f_1.term_count, 10); - test_assert(f_1.terms != NULL); - test_int(f_1.terms[0].id, TagA); - test_int(f_1.terms[1].id, TagB); - test_int(f_1.terms[2].id, TagC); - test_int(f_1.terms[3].id, TagD); - test_int(f_1.terms[4].id, TagE); - test_int(f_1.terms[5].id, TagF); - test_int(f_1.terms[6].id, TagG); - test_int(f_1.terms[7].id, TagH); - test_int(f_1.terms[8].id, TagI); - test_int(f_1.terms[9].id, TagJ); - - test_int(f_2.term_count, 10); - test_assert(f_2.terms != NULL); - test_int(f_2.terms[0].id, TagA); - test_int(f_2.terms[1].id, TagB); - test_int(f_2.terms[2].id, TagC); - test_int(f_2.terms[3].id, TagD); - test_int(f_2.terms[4].id, TagE); - test_int(f_2.terms[5].id, TagF); - test_int(f_2.terms[6].id, TagG); - test_int(f_2.terms[7].id, TagH); - test_int(f_2.terms[8].id, TagI); - test_int(f_2.terms[9].id, TagJ); - - test_assert(f_1.terms != f_2.terms); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_match_disabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = {{ TagA }} - })); - - ecs_filter_t f_2 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_2, - .terms = {{ TagA }, { EcsDisabled, .oper = EcsOptional }} - })); - - test_bool(f_1.flags & EcsFilterMatchDisabled, false); - test_bool(f_2.flags & EcsFilterMatchDisabled, true); - - ecs_iter_t it = ecs_filter_iter(world, &f_1); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_filter_next(&it)); - - it = ecs_filter_iter(world, &f_2); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_match_prefab(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, EcsDisabled); - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, EcsPrefab); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = {{ TagA }} - })); - - ecs_filter_t f_2 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_2, - .terms = {{ TagA }, { EcsPrefab, .oper = EcsOptional }} - })); - - test_bool(f_1.flags & EcsFilterMatchPrefab, false); - test_bool(f_2.flags & EcsFilterMatchPrefab, true); - - ecs_iter_t it = ecs_filter_iter(world, &f_1); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_assert(!ecs_filter_next(&it)); - - it = ecs_filter_iter(world, &f_2); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_3); - test_int(ecs_field_id(&it, 1), TagA); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_chain_term_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - - ecs_new(world, TagA); /* Matches filter 1, not filter 2 */ - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagB }} - })); - - ecs_iter_t child_it = ecs_term_iter(world, &(ecs_term_t) { TagA }); - ecs_iter_t it = ecs_filter_chain_iter(&child_it, &f); - test_int(it.field_count, 1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_chain_filter_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - - ecs_new(world, TagA); /* Matches filter 1, not filter 2 */ - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = {{ TagA }} - })); - - ecs_filter_t f_2 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_2, - .terms = {{ TagB }} - })); - - ecs_iter_t child_it = ecs_filter_iter(world, &f_1); - ecs_iter_t it = ecs_filter_chain_iter(&child_it, &f_2); - test_int(it.field_count, 1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_chain_query_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - - ecs_new(world, TagA); /* Matches filter 1, not filter 2 */ - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagB }} - })); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ TagA }} - }); - test_assert(q != NULL); - - ecs_iter_t child_it = ecs_query_iter(world, q); - ecs_iter_t it = ecs_filter_chain_iter(&child_it, &f); - test_int(it.field_count, 1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_query_fini(q); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_chain_rule_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - - ecs_new(world, TagA); /* Matches filter 1, not filter 2 */ - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagB }} - })); - - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ - .terms = {{ TagB }} - }); - - test_assert(r != NULL); - - ecs_iter_t child_it = ecs_rule_iter(world, r); - ecs_iter_t it = ecs_filter_chain_iter(&child_it, &f); - test_int(it.field_count, 1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_rule_fini(r); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_chain_iter_2_levels(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_add_id(world, e_1, TagC); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - ecs_add_id(world, e_2, TagC); - - ecs_entity_t e_3 = ecs_new(world, TagA); - ecs_add_id(world, e_3, TagB); - - ecs_new(world, TagA); - - ecs_filter_t f_2 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_2, - .terms = {{ TagB }} - })); - - ecs_filter_t f_3 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_3, - .terms = {{ TagC }} - })); - - ecs_iter_t it_a = ecs_term_iter(world, &(ecs_term_t) { TagA }); - ecs_iter_t it_b = ecs_filter_chain_iter(&it_a, &f_2); - ecs_iter_t it = ecs_filter_chain_iter(&it_b, &f_3); - test_int(it.field_count, 1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it_a, 1), TagA); - test_int(ecs_field_id(&it_b, 1), TagB); - test_int(ecs_field_id(&it, 1), TagC); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f_2); - ecs_filter_fini(&f_3); - - ecs_fini(world); -} - -void Filter_chain_term_iter_w_term_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - - ecs_new(world, TagA); /* Matches filter 1, not filter 2 */ - - ecs_iter_t child_it = ecs_term_iter(world, &(ecs_term_t) { TagA }); - ecs_iter_t it = ecs_term_chain_iter(&child_it, &(ecs_term_t) { TagB }); - test_int(it.field_count, 1); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagB); - test_int(ecs_field_src(&it, 1), 0); - test_assert(it.table != NULL); - test_assert(it.chain_it != NULL); - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_chain_filter_iter_w_term_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - - ecs_new(world, TagA); /* Matches filter 1, not filter 2 */ - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = {{ TagA }} - })); - - ecs_iter_t child_it = ecs_filter_iter(world, &f_1); - ecs_iter_t it = ecs_term_chain_iter(&child_it, &(ecs_term_t){ TagB }); - test_int(it.field_count, 1); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), TagB); - test_int(ecs_field_src(&it, 1), 0); - test_assert(it.table != NULL); - test_assert(it.chain_it != NULL); - - test_assert(!ecs_term_next(&it)); - - ecs_filter_fini(&f_1); - - ecs_fini(world); -} - -void Filter_chain_w_term_iter_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_COMPONENT(world, Position); - - ecs_entity_t e_1 = ecs_set(world, 0, Position, {10, 20}); - ecs_add_id(world, e_1, TagA); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {30, 40}); - ecs_add_id(world, e_2, TagA); - - ecs_new(world, TagA); /* Matches filter 1, not filter 2 */ - - ecs_iter_t child_it = ecs_term_iter(world, &(ecs_term_t) { TagA }); - ecs_iter_t it = ecs_term_chain_iter(&child_it, &(ecs_term_t) { ecs_id(Position) }); - test_int(it.field_count, 1); - - test_assert(ecs_term_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e_1); - test_int(it.entities[1], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - test_assert(it.table != NULL); - - Position *ptr = ecs_field(&it, Position, 1); - test_assert(ptr != NULL); - test_int(ptr[0].x, 10); - test_int(ptr[0].y, 20); - test_int(ptr[1].x, 30); - test_int(ptr[1].y, 40); - - test_assert(!ecs_term_next(&it)); - - ecs_fini(world); -} - -void Filter_chain_iter_w_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagC); - - ecs_filter_t f_1 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_1, - .terms = {{ TagA }} - })); - - ecs_filter_t f_2 = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f_2, - .terms = {{ TagB, .oper = EcsOr }, { TagC }} - })); - - ecs_iter_t child_it = ecs_filter_iter(world, &f_1); - ecs_iter_t it = ecs_filter_chain_iter(&child_it, &f_2); - test_int(it.field_count, 1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagB); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagC); - test_int(ecs_field_src(&it, 1), 0); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f_1); - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_filter_from_expr_2_terms_err(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_log_set_level(-4); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Foo, (IsA" - })); - - ecs_fini(world); -} - -void Filter_filter_w_recycled_first(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_delete(world, rel); - - ecs_entity_t rel2 = ecs_new_id(world); - test_assert(rel != rel2); - test_assert((uint32_t)rel == (uint32_t)rel2); - - ecs_entity_t e = ecs_new_w_pair(world, rel2, obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.id = rel2, .second.id = obj }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], ecs_pair(rel2, obj)); - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_recycled_second(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_delete(world, obj); - - ecs_entity_t obj2 = ecs_new_id(world); - test_assert(obj != obj2); - test_assert((uint32_t)obj == (uint32_t)obj2); - - ecs_entity_t e = ecs_new_w_pair(world, rel, obj2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.id = rel, .second.id = obj2 }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], ecs_pair(rel, obj2)); - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_recycled_first_and_id(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_delete(world, rel); - - ecs_entity_t rel2 = ecs_new_id(world); - test_assert(rel != rel2); - test_assert((uint32_t)rel == (uint32_t)rel2); - - ecs_entity_t e = ecs_new_w_pair(world, rel2, obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = ecs_pair(rel2, obj), .first.id = rel2 }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], ecs_pair(rel2, obj)); - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_recycled_second_and_id(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_delete(world, obj); - - ecs_entity_t obj2 = ecs_new_id(world); - test_assert(obj != obj2); - test_assert((uint32_t)obj == (uint32_t)obj2); - - ecs_entity_t e = ecs_new_w_pair(world, rel, obj2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = ecs_pair(rel, obj2), .second.id = obj2 }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], ecs_pair(rel, obj2)); - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_recycled_first_by_name_and_id(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_delete(world, rel); - - ecs_entity_t rel2 = ecs_new_id(world); - test_assert(rel != rel2); - test_assert((uint32_t)rel == (uint32_t)rel2); - ecs_set_name(world, rel2, "rel"); - - ecs_entity_t e = ecs_new_w_pair(world, rel2, obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = ecs_pair(rel2, obj), .first.name = "rel" }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], ecs_pair(rel2, obj)); - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_recycled_second_by_name_and_id(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_delete(world, obj); - - ecs_entity_t obj2 = ecs_new_id(world); - test_assert(obj != obj2); - test_assert((uint32_t)obj == (uint32_t)obj2); - ecs_set_name(world, obj2, "obj"); - - ecs_entity_t e = ecs_new_w_pair(world, rel, obj2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = ecs_pair(rel, obj2), .second.name = "obj" }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], ecs_pair(rel, obj2)); - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_recycled_first_by_expr(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_delete(world, rel); - - ecs_entity_t rel2 = ecs_new_id(world); - test_assert(rel != rel2); - test_assert((uint32_t)rel == (uint32_t)rel2); - - ecs_set_name(world, rel2, "rel"); - ecs_set_name(world, obj, "obj"); - - ecs_entity_t e = ecs_new_w_pair(world, rel2, obj); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(rel, obj)" - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], ecs_pair(rel2, obj)); - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_recycled_second_by_expr(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_delete(world, obj); - - ecs_entity_t obj2 = ecs_new_id(world); - test_assert(obj != obj2); - test_assert((uint32_t)obj == (uint32_t)obj2); - - ecs_set_name(world, rel, "rel"); - ecs_set_name(world, obj2, "obj"); - - ecs_entity_t e = ecs_new_w_pair(world, rel, obj2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(rel, obj)" - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], ecs_pair(rel, obj2)); - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_recycled_first_only_by_expr(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_delete(world, rel); - - ecs_entity_t rel2 = ecs_new_id(world); - test_assert(rel != rel2); - test_assert((uint32_t)rel == (uint32_t)rel2); - - ecs_set_name(world, rel2, "rel"); - - ecs_entity_t e = ecs_new_w_id(world, rel2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "rel" - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.ids[0], rel2); - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_term_iter_w_filter_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t) { - .id = ecs_id(Position), - .inout = EcsInOutNone - }); - - test_bool(ecs_term_next(&it), true); - test_assert(it.ids != NULL); - test_assert(it.ids[0] == ecs_id(Position)); - - test_int(it.count, 1); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e); - - test_assert(it.sizes != NULL); - test_int(it.sizes[0], ECS_SIZEOF(Position)); - - test_assert(it.ptrs != NULL); - test_assert(it.columns != NULL); - test_assert(it.ptrs[0] == NULL); - - test_bool(ecs_term_next(&it), false); - - ecs_fini(world); -} - -void Filter_filter_iter_w_filter_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = ecs_id(Position), .inout = EcsInOutNone }} - })); - - test_bool(f.flags & EcsFilterNoData, true); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_assert(it.ids != NULL); - test_assert(it.ids[0] == ecs_id(Position)); - - test_int(it.count, 1); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e); - test_assert(it.sizes != NULL); - test_int(it.sizes[0], ECS_SIZEOF(Position)); - - test_assert(it.ptrs != NULL); - test_assert(it.columns != NULL); - test_assert(it.ptrs[0] == NULL); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_w_2_terms_1_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = ecs_id(Position), .inout = EcsInOutNone }, - { .id = ecs_id(Velocity) } - } - })); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 1}); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_assert(it.ids != NULL); - test_assert(it.ids[0] == ecs_id(Position)); - test_assert(it.ids[1] == ecs_id(Velocity)); - - test_int(it.count, 1); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e); - - test_assert(it.sizes != NULL); - test_int(it.sizes[0], ECS_SIZEOF(Position)); - test_int(it.sizes[1], ECS_SIZEOF(Velocity)); - - test_assert(it.ptrs != NULL); - test_assert(it.columns != NULL); - - test_assert(it.ptrs[0] == NULL); - test_assert(it.ptrs[1] != NULL); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_w_3_terms_2_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = ecs_id(Position), .inout = EcsInOutNone }, - { .id = ecs_id(Velocity), .inout = EcsInOutNone }, - { .id = ecs_id(Mass) } - } - })); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 1}); - ecs_set(world, e, Mass, {1}); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_assert(it.ids != NULL); - test_assert(it.ids[0] == ecs_id(Position)); - test_assert(it.ids[1] == ecs_id(Velocity)); - test_assert(it.ids[2] == ecs_id(Mass)); - - test_int(it.count, 1); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e); - - test_assert(it.sizes != NULL); - test_int(it.sizes[0], ECS_SIZEOF(Position)); - test_int(it.sizes[1], ECS_SIZEOF(Velocity)); - test_int(it.sizes[2], ECS_SIZEOF(Mass)); - - test_assert(it.ptrs != NULL); - test_assert(it.columns != NULL); - - test_assert(it.ptrs[0] == NULL); - test_assert(it.ptrs[1] == NULL); - test_assert(it.ptrs[2] != NULL); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - - -void Filter_filter_iter_2_terms_filter_all(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = ecs_id(Position), .inout = EcsInOutNone }, - { .id = ecs_id(Velocity) } - }, - .flags = EcsFilterNoData - })); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 1}); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_assert(it.ids != NULL); - test_assert(it.ids[0] == ecs_id(Position)); - test_assert(it.ids[1] == ecs_id(Velocity)); - - test_int(it.count, 1); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e); - - test_assert(it.sizes != NULL); - test_int(it.sizes[0], ECS_SIZEOF(Position)); - test_int(it.sizes[1], ECS_SIZEOF(Velocity)); - - test_assert(it.ptrs != NULL); - test_assert(it.columns != NULL); - test_assert(it.ptrs[0] == NULL); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_terms_filter_all_w_out(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = ecs_id(Position), .inout = EcsOut }, - { .id = ecs_id(Velocity) } - }, - .flags = EcsFilterNoData - })); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 1}); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_assert(it.ids != NULL); - test_assert(it.ids[0] == ecs_id(Position)); - test_assert(it.ids[1] == ecs_id(Velocity)); - - test_int(it.count, 1); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e); - - test_assert(it.sizes != NULL); - test_int(it.sizes[0], ECS_SIZEOF(Position)); - test_int(it.sizes[1], ECS_SIZEOF(Velocity)); - - test_assert(it.ptrs != NULL); - test_assert(it.columns != NULL); - test_assert(it.ptrs[0] == NULL); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_switch_term_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Sw, Union); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e = ecs_new_w_pair(world, Sw, TagA); - ecs_table_t *table = ecs_get_table(world, e); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = ecs_pair(Sw, EcsWildcard), .inout = EcsInOutNone }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.sizes[0], ECS_SIZEOF(ecs_entity_t)); - test_assert(it.table == table); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_2_terms_switch_term_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Sw, Union); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_w_pair(world, Sw, TagA); - ecs_add(world, e, Position); - ecs_table_t *table = ecs_get_table(world, e); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = ecs_id(Position) }, - { .id = ecs_pair(Sw, EcsWildcard), .inout = EcsInOutNone } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.sizes[0], ECS_SIZEOF(Position)); - test_int(it.sizes[1], ECS_SIZEOF(ecs_entity_t)); - test_assert(it.table == table); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_instanced_w_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Position, Velocity($)", - .instanced = true - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 3); - test_int(it.entities[0], e1); - test_int(it.entities[1], e2); - test_int(it.entities[2], e3); - test_int(v->x, 1); - test_int(v->y, 2); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_int(p[2].x, 30); - test_int(p[2].y, 40); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e4); - test_int(it.entities[1], e5); - test_int(p[0].x, 40); - test_int(p[0].y, 50); - test_int(p[1].x, 50); - test_int(p[1].y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_instanced_w_base(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base_1 = ecs_set(world, 0, Velocity, {1, 2}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {80, 90}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_add_pair(world, e1, EcsIsA, base_1); - ecs_add_pair(world, e2, EcsIsA, base_1); - ecs_add_pair(world, e3, EcsIsA, base_1); - ecs_add_pair(world, e4, EcsIsA, base_1); - ecs_add_pair(world, e5, EcsIsA, base_1); - - ecs_entity_t e6 = ecs_set(world, 0, Position, {60, 70}); - ecs_entity_t e7 = ecs_set(world, 0, Position, {70, 80}); - ecs_set(world, e6, Velocity, {2, 3}); - ecs_set(world, e7, Velocity, {4, 5}); - - ecs_entity_t e8 = ecs_set(world, 0, Velocity, {6, 7}); - ecs_entity_t e9 = ecs_set(world, 0, Velocity, {8, 9}); - ecs_add_pair(world, e8, EcsIsA, base_2); - ecs_add_pair(world, e9, EcsIsA, base_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Position, Velocity", - .instanced = true - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), true); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e6); - test_int(p[0].x, 60); - test_int(p[0].y, 70); - test_int(v[0].x, 2); - test_int(v[0].y, 3); - - test_int(it.entities[1], e7); - test_int(p[1].x, 70); - test_int(p[1].y, 80); - test_int(v[1].x, 4); - test_int(v[1].y, 5); - } - - test_assert(ecs_filter_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), false); - test_bool(ecs_field_is_self(&it, 2), true); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e8); - test_int(it.entities[1], e9); - test_int(p->x, 80); - test_int(p->y, 90); - test_int(v[0].x, 6); - test_int(v[0].y, 7); - test_int(v[1].x, 8); - test_int(v[1].y, 9); - } - - test_assert(ecs_filter_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), false); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 3); - test_int(it.entities[0], e1); - test_int(it.entities[1], e2); - test_int(it.entities[2], e3); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_int(p[2].x, 30); - test_int(p[2].y, 40); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_filter_next(&it)); - { - test_bool(ecs_field_is_self(&it, 1), true); - test_bool(ecs_field_is_self(&it, 2), false); - - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e4); - test_int(it.entities[1], e5); - test_int(p[0].x, 40); - test_int(p[0].y, 50); - test_int(p[1].x, 50); - test_int(p[1].y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_no_instancing_w_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Position, Velocity($)" - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e1); - test_int(p->x, 10); - test_int(p->y, 20); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e2); - test_int(p->x, 20); - test_int(p->y, 30); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_int(p->x, 30); - test_int(p->y, 40); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_int(p->x, 40); - test_int(p->y, 50); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e5); - test_int(p->x, 50); - test_int(p->y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_no_instancing_w_base(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base_1 = ecs_set(world, 0, Velocity, {1, 2}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {80, 90}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_add_pair(world, e1, EcsIsA, base_1); - ecs_add_pair(world, e2, EcsIsA, base_1); - ecs_add_pair(world, e3, EcsIsA, base_1); - ecs_add_pair(world, e4, EcsIsA, base_1); - ecs_add_pair(world, e5, EcsIsA, base_1); - - ecs_entity_t e6 = ecs_set(world, 0, Position, {60, 70}); - ecs_entity_t e7 = ecs_set(world, 0, Position, {70, 80}); - ecs_set(world, e6, Velocity, {2, 3}); - ecs_set(world, e7, Velocity, {4, 5}); - - ecs_entity_t e8 = ecs_set(world, 0, Velocity, {6, 7}); - ecs_entity_t e9 = ecs_set(world, 0, Velocity, {8, 9}); - ecs_add_pair(world, e8, EcsIsA, base_2); - ecs_add_pair(world, e9, EcsIsA, base_2); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Position, Velocity" - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e6); - test_int(p[0].x, 60); - test_int(p[0].y, 70); - test_int(v[0].x, 2); - test_int(v[0].y, 3); - - test_int(it.entities[1], e7); - test_int(p[1].x, 70); - test_int(p[1].y, 80); - test_int(v[1].x, 4); - test_int(v[1].y, 5); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e8); - test_int(p->x, 80); - test_int(p->y, 90); - test_int(v->x, 6); - test_int(v->y, 7); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e9); - test_int(p->x, 80); - test_int(p->y, 90); - test_int(v->x, 8); - test_int(v->y, 9); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_int(it.entities[0], e1); - test_int(p->x, 10); - test_int(p->y, 20); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e2); - test_int(p->x, 20); - test_int(p->y, 30); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_int(p->x, 30); - test_int(p->y, 40); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_int(p->x, 40); - test_int(p->y, 50); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_filter_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e5); - test_int(p->x, 50); - test_int(p->y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_no_this_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_set_name(world, 0, "e"); - ecs_add(world, e, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Tag(e)", - })); - - test_assert(!(f.flags & EcsFilterMatchThis)); - test_assert(!(f.flags & EcsFilterMatchAnything)); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_int(ecs_field_id(&it, 1), Tag); - test_int(ecs_field_src(&it, 1), e); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_no_this_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_set_name(world, 0, "e"); - ecs_set(world, e, Position, {10, 20}); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Position(e)", - })); - - test_assert(!(f.flags & EcsFilterMatchThis)); - test_assert(!(f.flags & EcsFilterMatchAnything)); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), e); - - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_no_this_tag_2_ents(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_set_name(world, 0, "e1"); - ecs_add(world, e1, TagA); - ecs_entity_t e2 = ecs_set_name(world, 0, "e2"); - ecs_add(world, e2, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA(e1), TagB(e2)", - })); - - test_assert(!(f.flags & EcsFilterMatchThis)); - test_assert(!(f.flags & EcsFilterMatchAnything)); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_src(&it, 1), e1); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_src(&it, 2), e2); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_no_this_component_2_ents(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_set_name(world, 0, "e1"); - ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_set_name(world, 0, "e2"); - ecs_set(world, e2, Velocity, {1, 2}); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Position(e1), Velocity(e2)", - })); - - test_assert(!(f.flags & EcsFilterMatchThis)); - test_assert(!(f.flags & EcsFilterMatchAnything)); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), e1); - test_int(ecs_field_id(&it, 2), ecs_id(Velocity)); - test_int(ecs_field_src(&it, 2), e2); - - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(p != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_no_this_tag_2_ents_1_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_set_name(world, 0, "e1"); - ecs_add(world, e1, TagA); - ecs_entity_t e2 = ecs_set_name(world, 0, "e2"); - ecs_add(world, e2, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA(e1), TagB(e2), !TagA(e2)", - })); - - test_assert(!(f.flags & EcsFilterMatchThis)); - test_assert(!(f.flags & EcsFilterMatchAnything)); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_src(&it, 1), e1); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_src(&it, 2), e2); - test_int(ecs_field_id(&it, 3), TagA); - test_int(ecs_field_src(&it, 3), e2); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_no_this_component_2_ents_1_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_set_name(world, 0, "e1"); - ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_set_name(world, 0, "e2"); - ecs_set(world, e2, Velocity, {1, 2}); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "Position(e1), Velocity(e2), !Position(e2)", - })); - - test_assert(!(f.flags & EcsFilterMatchThis)); - test_assert(!(f.flags & EcsFilterMatchAnything)); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), e1); - test_int(ecs_field_id(&it, 2), ecs_id(Velocity)); - test_int(ecs_field_src(&it, 2), e2); - test_int(ecs_field_id(&it, 3), ecs_id(Position)); - test_int(ecs_field_src(&it, 3), e2); - - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(p != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_no_this_component_1_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_set_name(world, 0, "e1"); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "!Position(e1)", - })); - - test_assert(!(f.flags & EcsFilterMatchThis)); - test_assert(!(f.flags & EcsFilterMatchAnything)); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), e1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_entities_optional_flag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new_entity(world, "e"); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, TagB(e)", - })); - - /* e2 doesn't have TagB, so regular iteration doesn't return anything */ - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(false, ecs_filter_next(&it)); - - /* Treat terms matched on entities as optional */ - it = ecs_filter_iter(world, &f); - it.flags |= EcsIterEntityOptional; - - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], 0); - test_uint(it.sources[1], e2); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_id(&it, 2), TagB); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_frame_offset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = TagA, } - }, - })); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_entity_t e5 = ecs_new(world, TagA); - - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagB); - ecs_add(world, e5, TagC); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 2); - test_int(it.frame_offset, 0); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e1); - test_assert(it.entities[1] == e2); - test_assert(it.ids[0] == TagA); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 2); - test_int(it.frame_offset, 2); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e3); - test_assert(it.entities[1] == e4); - test_assert(it.ids[0] == TagA); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.frame_offset, 4); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e5); - test_assert(it.ids[0] == TagA); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_1_term_no_alloc(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_term_t terms[1]; - ecs_filter_t f = ECS_FILTER_INIT; - f.terms = terms; - f.term_count = 1; - - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .id = TagA, } - }, - })); - - test_assert(terms == f.terms); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_cache_size_terms_no_alloc(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - - ecs_term_t terms[4]; - ecs_filter_t f = ECS_FILTER_INIT; - f.terms = terms; - f.term_count = 4; - - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { TagB }, { TagC }, { TagD }} - })); - - test_assert(terms == f.terms); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_lt_cache_size_terms_no_alloc(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - ECS_TAG(world, TagE); - - ecs_term_t terms[5]; - ecs_filter_t f = ECS_FILTER_INIT; - f.terms = terms; - f.term_count = 5; - - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { TagB }, { TagC }, { TagD }, { TagE }} - })); - - test_assert(terms == f.terms); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_move_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - ecs_filter_t f_2 = f; - ecs_filter_move(&f_2, &f_2); - - char *str = ecs_filter_str(world, &f_2); - test_str(str, "TagA"); - ecs_os_free(str); - - ecs_filter_fini(&f_2); - - ecs_fini(world); -} - -void Filter_match_empty_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); - ecs_entity_t e4 = ecs_new(world, Position); - - ecs_add(world, e2, TagA); - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_table_t *t2 = ecs_get_table(world, e2); - ecs_table_t *t3 = ecs_get_table(world, e3); - ecs_table_t *t4 = ecs_get_table(world, e4); - - ecs_delete(world, e1); - ecs_delete(world, e2); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }}, - .flags = EcsFilterMatchEmptyTables - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool( ecs_filter_next(&it), true); - test_assert(it.table == t1); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_filter_next(&it), true); - test_assert(it.table == t2); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_filter_next(&it), true); - test_assert(it.table == t3); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_filter_next(&it), true); - test_assert(it.table == t4); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - - test_bool( ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_match_empty_tables_w_no_empty_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { Foo } - }, - .flags = EcsFilterMatchEmptyTables - })); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_add(world, e2, Bar); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_match_switch_w_switch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Sw, Union); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e = ecs_new_w_pair(world, Sw, TagA); - ecs_table_t *table = ecs_get_table(world, e); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_pair(Sw, EcsWildcard) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.sizes[0], ECS_SIZEOF(ecs_entity_t)); - test_uint(it.ids[0], ecs_pair(Sw, EcsWildcard)); - test_assert(it.table == table); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_match_switch_w_case(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Sw, Union); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e = ecs_new_w_pair(world, Sw, TagA); - ecs_table_t *table = ecs_get_table(world, e); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_pair(Sw, TagA) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_assert(it.table == table); - test_uint(it.ids[0], ecs_pair(Sw, EcsWildcard)); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_match_switch_w_case_2_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, SwX, Union); - ECS_ENTITY(world, SwY, Union); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - - ecs_entity_t e = ecs_new_w_pair(world, SwX, TagA); - ecs_add_pair(world, e, SwY, TagC); - ecs_table_t *table = ecs_get_table(world, e); - - /* Not matched */ - ecs_new_w_pair(world, SwX, TagA); - ecs_new_w_pair(world, SwY, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { ecs_pair(SwX, TagA) }, - { ecs_pair(SwY, TagC) } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_assert(it.table == table); - test_uint(it.ids[0], ecs_pair(SwX, EcsWildcard)); - test_uint(it.ids[1], ecs_pair(SwY, EcsWildcard)); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_iter_switch_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, SwX, Union); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t b = ecs_new_w_pair(world, SwX, TagA); - ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, b); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { ecs_pair(SwX, EcsWildcard), .src.flags = EcsUp } - } - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.sources[0], b); - test_uint(it.ids[0], ecs_pair(SwX, EcsWildcard)); - - ecs_entity_t *cases = ecs_field(&it, ecs_entity_t, 1); - test_assert(cases != NULL); - test_uint(cases[0], TagA); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_match_case_no_case(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Sw, Union); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e = ecs_new_w_pair(world, Sw, TagA); - ecs_table_t *table = ecs_get_table(world, e); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_pair(Sw, TagA) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_assert(it.table == table); - test_uint(it.ids[0], ecs_pair(Sw, EcsWildcard)); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_and_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_PREFAB(world, TypeX, TagA, TagB); - - ecs_entity_t e1 = ecs_new_w_id(world, TagA); - ecs_add(world, e1, TagB); - ecs_table_t *table1 = ecs_get_table(world, e1); - - ecs_entity_t e2 = ecs_new_w_id(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e2, Foo); - ecs_table_t *table2 = ecs_get_table(world, e2); - - /* Not matched */ - ecs_new(world, TagA); - ecs_new(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { ECS_AND | TypeX } - } - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.ids[0], TypeX); - test_assert(it.table == table1); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.ids[0], TypeX); - test_assert(it.table == table2); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_or_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_PREFAB(world, TypeX, TagA, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_table_t *table1 = ecs_get_table(world, e1); - - ecs_entity_t e2 = ecs_new(world, TagB); - ecs_table_t *table2 = ecs_get_table(world, e2); - - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add(world, e3, TagB); - ecs_table_t *table3 = ecs_get_table(world, e3); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TypeX, .oper = EcsOrFrom } - } - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.ids[0], TagA); - test_assert(it.table == table1); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.ids[0], TagB); - test_assert(it.table == table2); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.ids[0], TagA); - test_assert(it.table == table3); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -static -void create_ids(ecs_world_t *world, int count) { - for (int i = 0; i < count; i ++) { - ecs_entity_t e = ecs_new_id(world); - ecs_add_id(world, e, e); - } -} - -void Filter_iter_while_creating_components(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e1); - - create_ids(world, 100); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e2); - - create_ids(world, 100); - - test_bool(ecs_filter_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e3); - - test_bool(ecs_filter_next(&it), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_iter_w_this_var_as_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - ecs_add(world, e4, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - ecs_entity_t this_var = ecs_iter_get_var(&it, this_var_id); - test_assert(this_var != 0); - test_assert(this_var == e1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - this_var = ecs_iter_get_var(&it, this_var_id); - test_assert(this_var != 0); - test_assert(this_var == e2); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - this_var = ecs_iter_get_var(&it, this_var_id); - test_assert(this_var == 0); /* more than one entity matches */ - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_iter_w_this_var_as_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_table_t *t2 = ecs_get_table(world, e2); - test_assert(t2 != NULL); - test_assert(t2 != t1); - - ecs_table_t *t3 = ecs_get_table(world, e3); - test_assert(t3 != NULL); - test_assert(t3 != t1); - test_assert(t3 != t2); - test_assert(t3 == ecs_get_table(world, e4)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - ecs_table_t *this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t2); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t3); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_iter_w_this_var_as_table_range(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_table_t *t2 = ecs_get_table(world, e2); - test_assert(t2 != NULL); - test_assert(t2 != t1); - - ecs_table_t *t3 = ecs_get_table(world, e3); - test_assert(t3 != NULL); - test_assert(t3 != t1); - test_assert(t3 != t2); - test_assert(t3 == ecs_get_table(world, e4)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - ecs_table_range_t this_var = ecs_iter_get_var_as_range(&it, this_var_id); - test_assert(this_var.table != NULL); - test_assert(this_var.table == t1); - test_assert(this_var.offset == 0); - test_assert(this_var.count == 1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - this_var = ecs_iter_get_var_as_range(&it, this_var_id); - test_assert(this_var.table != NULL); - test_assert(this_var.table == t2); - test_assert(this_var.offset == 0); - test_assert(this_var.count == 1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - this_var = ecs_iter_get_var_as_range(&it, this_var_id); - test_assert(this_var.table != NULL); - test_assert(this_var.table == t3); - test_assert(this_var.offset == 0); - test_assert(this_var.count == 2); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_wo_this_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = TagA, .src.id = e1 }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id == -1); /* Filter has no This terms */ - - /* Make sure it matches for good measure */ - ecs_iter_t it = ecs_filter_iter(world, &f); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_int(it.sources[0], e1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_table_1_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - ecs_add(world, e4, TagC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_table_t *t2 = ecs_get_table(world, e2); - test_assert(t2 != NULL); - test_assert(t2 != t1); - - ecs_table_t *t3 = ecs_get_table(world, e3); - test_assert(t3 != NULL); - test_assert(t3 != t1); - test_assert(t3 != t2); - test_assert(t3 == ecs_get_table(world, e4)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_assert(!ecs_filter_next(&it)); - - it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t2); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_assert(!ecs_filter_next(&it)); - - it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t3); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_table_2_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_add(world, e1, TagB); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagB); - ecs_add(world, e2, TagC); - ecs_add(world, e3, TagD); - ecs_add(world, e4, TagD); - - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_table_t *t2 = ecs_get_table(world, e2); - test_assert(t2 != NULL); - test_assert(t2 != t1); - - ecs_table_t *t3 = ecs_get_table(world, e3); - test_assert(t3 != NULL); - test_assert(t3 != t1); - test_assert(t3 != t2); - test_assert(t3 == ecs_get_table(world, e4)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { TagB }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_assert(!ecs_filter_next(&it)); - - it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t2); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_assert(!ecs_filter_next(&it)); - - it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t3); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_table_1_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - ECS_TAG(world, ObjC); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_add_pair(world, e1, Rel, ObjA); - ecs_add_pair(world, e2, Rel, ObjA); - ecs_add_pair(world, e3, Rel, ObjA); - ecs_add_pair(world, e4, Rel, ObjA); - - ecs_add_pair(world, e2, Rel, ObjB); - ecs_add_pair(world, e3, Rel, ObjC); - ecs_add_pair(world, e4, Rel, ObjC); - - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_table_t *t2 = ecs_get_table(world, e2); - test_assert(t2 != NULL); - test_assert(t2 != t1); - - ecs_table_t *t3 = ecs_get_table(world, e3); - test_assert(t3 != NULL); - test_assert(t3 != t1); - test_assert(t3 != t2); - test_assert(t3 == ecs_get_table(world, e4)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ Tag }, { ecs_pair(Rel, EcsWildcard) }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_field_id(&it, 1), Tag); - test_uint(ecs_field_id(&it, 2), ecs_pair(Rel, ObjA)); - test_assert(!ecs_filter_next(&it)); - - it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t2); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_id(&it, 1), Tag); - test_uint(ecs_field_id(&it, 2), ecs_pair(Rel, ObjA)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_id(&it, 1), Tag); - test_uint(ecs_field_id(&it, 2), ecs_pair(Rel, ObjB)); - test_assert(!ecs_filter_next(&it)); - - it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t3); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - test_uint(ecs_field_id(&it, 1), Tag); - test_uint(ecs_field_id(&it, 2), ecs_pair(Rel, ObjA)); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e3); - test_uint(it.entities[1], e4); - test_uint(ecs_field_id(&it, 1), Tag); - test_uint(ecs_field_id(&it, 2), ecs_pair(Rel, ObjC)); - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_table_no_match_no_data(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagB); - - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - test_bool(f.flags & EcsFilterMatchThis, true); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - - -void Filter_set_this_to_table_no_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_new(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagB); - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - test_bool(f.flags & EcsFilterMatchThis, true); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_table_2_terms_no_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_new(world, TagA); - - ecs_entity_t e1 = ecs_new(world, TagB); - ecs_table_t *t1 = ecs_get_table(world, e1); - test_assert(t1 != NULL); - - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_table_t *t2 = ecs_get_table(world, e2); - test_assert(t2 != NULL); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }, { TagB }} - })); - - test_bool(f.flags & EcsFilterMatchThis, true); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - test_bool(false, ecs_filter_next(&it)); - - it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t2); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add(world, e1, TagB); - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_remove(world, e1, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_empty_table_w_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add(world, e1, TagA); - ecs_table_t *t1 = ecs_get_table(world, e1); - ecs_remove(world, e1, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Position) }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 0); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - test_assert(ecs_field(&it, Position, 1) == NULL); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_implicit_isa_superset_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - ecs_table_t *t1 = ecs_get_table(world, inst); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_self_isa_superset_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - ecs_table_t *t1 = ecs_get_table(world, inst); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = TagA, .src.flags = EcsUp | EcsSelf }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_isa_superset_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - ecs_table_t *t1 = ecs_get_table(world, inst); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = TagA, .src.flags = EcsUp }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_childof_superset_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); - ecs_table_t *t1 = ecs_get_table(world, inst); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = TagA, - .src.flags = EcsUp, - .src.trav = EcsChildOf - }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_superset_w_self_filter_no_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - ecs_table_t *t1 = ecs_get_table(world, inst); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = TagA, .src.flags = EcsSelf }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_isa_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - ecs_table_t *t1 = ecs_get_table(world, inst); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = TagA, .src.flags = EcsCascade }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_to_childof_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); - ecs_table_t *t1 = ecs_get_table(world, inst); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = TagA, - .src.flags = EcsCascade, - .src.trav = EcsChildOf - }} - })); - - int this_var_id = ecs_filter_find_this_var(&f); - test_assert(this_var_id != -1); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_set_var_as_table(&it, this_var_id, t1); - - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_assert(it.table == t1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_size(&it, 1), 0); - ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); - test_assert(this_var != NULL); - test_assert(this_var == t1); - - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_set_this_w_wildcard_2_matches(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - ECS_TAG(world, ObjC); - - ecs_filter_t *f = ecs_filter(world, { - .terms[0] = { ecs_pair(Rel, EcsWildcard) } - }); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - ecs_add_pair(world, e1, Rel, ObjC); - ecs_add_pair(world, e2, Rel, ObjC); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjC), ecs_field_id(&it, 1)); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_set_this_to_entity_superset_self_has_component(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsChildOf, parent); - ecs_set(world, e1, Position, {10, 20}); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ ecs_id(Position), .src.flags = EcsUp, .src.trav = EcsChildOf }} - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_set_this_to_1_entity_in_table(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t *f = ecs_filter(world, { - .terms[0].id = ecs_id(Position) - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - - { - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_set_var(&it, 0, e1); - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_filter_next(&it)); - } - - { - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - test_bool(false, ecs_filter_next(&it)); - } - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_oneof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_ENTITY(world, ObjA, (ChildOf, Parent)); - ECS_ENTITY(world, ObjB, (ChildOf, Parent)); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - test_assert( ecs_has_pair(world, e1, Rel, ObjA)); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - test_assert( ecs_has_pair(world, e2, Rel, ObjB)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = ecs_pair(Rel, ObjB) - }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 1)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_oneof_expr(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_ENTITY(world, ObjA, (ChildOf, Parent)); - ECS_ENTITY(world, ObjB, (ChildOf, Parent)); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - test_assert( ecs_has_pair(world, e1, Rel, ObjA)); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - test_assert( ecs_has_pair(world, e2, Rel, ObjB)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Rel, ObjB)" - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 1)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_oneof_w_mismatching_obj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_ENTITY(world, ObjA, (ChildOf, Parent)); - ECS_ENTITY(world, ObjB, (ChildOf, Parent)); - ECS_TAG(world, ObjC); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - test_assert( ecs_has_pair(world, e1, Rel, ObjA)); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - test_assert( ecs_has_pair(world, e2, Rel, ObjB)); - - ecs_log_set_level(-4); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = ecs_pair(Rel, ObjC) - }} - })); - - ecs_fini(world); -} - -void Filter_oneof_w_mismatching_obj_expr(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_ENTITY(world, ObjA, (ChildOf, Parent)); - ECS_ENTITY(world, ObjB, (ChildOf, Parent)); - ECS_TAG(world, ObjC); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - test_assert( ecs_has_pair(world, e1, Rel, ObjA)); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - test_assert( ecs_has_pair(world, e2, Rel, ObjB)); - - ecs_log_set_level(-4); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL == ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "(Rel, ObjC)" - })); - - ecs_fini(world); -} - -void Filter_oneof_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_ENTITY(world, ObjA, (ChildOf, Parent)); - ECS_ENTITY(world, ObjB, (ChildOf, Parent)); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - test_assert( ecs_has_pair(world, e1, Rel, ObjA)); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - test_assert( ecs_has_pair(world, e2, Rel, ObjB)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = ecs_pair(Rel, EcsWildcard) - }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 1)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_oneof_any(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Parent); - ECS_ENTITY(world, Rel, (OneOf, Parent)); - ECS_ENTITY(world, ObjA, (ChildOf, Parent)); - ECS_ENTITY(world, ObjB, (ChildOf, Parent)); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - test_assert( ecs_has_pair(world, e1, Rel, ObjA)); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - test_assert( ecs_has_pair(world, e2, Rel, ObjB)); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = ecs_pair(Rel, EcsAny) - }} - })); - - ecs_iter_t it = ecs_filter_iter(world, &f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 1)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_flag_match_only_this(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = Foo - }} - })); - - test_assert(f.flags & EcsFilterMatchOnlyThis); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_flag_match_only_this_w_ref(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Src); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ - .id = Foo - }, { - .id = Bar, - .src.id = Src - }} - })); - - test_assert(!(f.flags & EcsFilterMatchOnlyThis)); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Filter_filter_w_alloc(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ - .terms = {{ - .id = Foo - }} - }); - - test_assert(f != NULL); - - ecs_entity_t e = ecs_new(world, Foo); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(it.ids[0], ecs_field_id(&it, 1)); - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Filter_filter_w_short_notation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ - .id = Foo - }} - }); - - test_assert(f != NULL); - - ecs_entity_t e = ecs_new(world, Foo); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_assert(ecs_filter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(it.ids[0], ecs_field_id(&it, 1)); - test_assert(!ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/FilterStr.c b/vendors/flecs/test/api/src/FilterStr.c deleted file mode 100644 index 12bfa12d0..000000000 --- a/vendors/flecs/test/api/src/FilterStr.c +++ /dev/null @@ -1,500 +0,0 @@ -#include - -void FilterStr_one_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "TagA"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_inout(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TagA, .inout = EcsIn } - } - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "[in] TagA"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_two_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TagA }, - { TagB } - } - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "TagA, TagB"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_two_terms_w_inout(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TagA }, - { TagB, .inout = EcsIn } - } - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "TagA, [in] TagB"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_three_terms_w_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TagA }, - { TagB, .oper = EcsOr }, - { TagC } - } - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "TagA, TagB || TagC"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_three_terms_w_or_inout(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TagA }, - { TagB, .oper = EcsOr, .inout = EcsIn }, - { TagC, .inout = EcsIn } - } - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "TagA, [in] TagB || TagC"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_four_terms_three_w_or_inout(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { TagA }, - { TagB, .oper = EcsOr, .inout = EcsIn }, - { TagC, .inout = EcsIn }, - { TagD, .inout = EcsIn } - } - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "TagA, [in] TagB || TagC, [in] TagD"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = ecs_pair(Rel, Tgt) }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "(Rel,Tgt)"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_pair_entity_src(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Src); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = ecs_pair(Rel, Tgt), .src.id = Src }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "Rel(Src,Tgt)"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = Foo, .src.flags = EcsSelf }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "Foo(self)"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_up(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = Foo, .src.flags = EcsUp }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "Foo(up)"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_0(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = Foo, .src.flags = EcsIsEntity }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "Foo()"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .id = Foo, .src.id = Foo }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "Foo($)"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_final_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Foo, Final); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.id = Foo }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "Foo"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_final_dont_inherit_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Foo, Final, DontInherit); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.id = Foo }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "Foo"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_src_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ Tag, .src.flags = EcsIsVariable, .src.name = "Var" }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "Tag($Var)"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_first_var(void) { - ecs_world_t *world = ecs_mini(); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.flags = EcsIsVariable, .first.name = "Var" }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "[none] $Var"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_second_var(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.id = Rel, .second.flags = EcsIsVariable, .second.name = "Var" }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "(Rel,$Var)"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_first_var_entity_src(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Src); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ .first.flags = EcsIsVariable, .first.name = "Var", .src.id = Src }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "[none] $Var(Src)"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_one_term_w_pair_w_0_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = { - { .first.id = Rel, .second.id = Tgt, .src = { - .id = 0, .flags = EcsIsEntity - } } - } - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "Rel(0,Tgt)"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_not_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ TagA, .oper = EcsNot }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "!TagA"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_wildcard_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ EcsWildcard }} - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "[none] *"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void FilterStr_scopes(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t f = ECS_FILTER_INIT; - test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .expr = "TagA, {TagB, {TagC}}" - })); - - char *str = ecs_filter_str(world, &f); - test_str(str, "TagA, {TagB, {TagC}}"); - ecs_os_free(str); - - ecs_filter_fini(&f); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/FixedHierarchies.c b/vendors/flecs/test/api/src/FixedHierarchies.c deleted file mode 100644 index 5a1b481ca..000000000 --- a/vendors/flecs/test/api/src/FixedHierarchies.c +++ /dev/null @@ -1,3961 +0,0 @@ -#include - -void FixedHierarchies_make_fixed_1_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_1_lvl_w_init(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_1_lvl_w_init_comp(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor - }); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1_1, Position, {10, 20}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2_1, Position, {30, 40}); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - - { - const Position *p = ecs_get(world, p_1_1, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - { - const Position *p = ecs_get(world, p_2_1, Position); - test_assert(p != NULL); - test_int(p->x, 30); - test_int(p->y, 40); - } - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_1_lvl_w_init_comp_after_tree_fixed(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor - }); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1_1, Position, {10, 20}); - ecs_set_pair_second(world, p_1_1, rel, Position, {100, 200}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2_1, Position, {30, 40}); - ecs_set_pair_second(world, p_2_1, rel, Position, {300, 400}); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - - { - const Position *p = ecs_get(world, p_1_1, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - } - { - const Position *p = ecs_get(world, p_2_1, Position); - test_assert(p != NULL); - test_int(p->x, 30); - test_int(p->y, 40); - } - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_1_lvl_2_entities(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_2 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_2 = ecs_new_w_pair(world, EcsChildOf, p_2); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_1_2)); - test_assert(ecs_get_table(world, p_2_1) == ecs_get_table(world, p_2_2)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_2, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_2, EcsChildOf, 0) == p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_1_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_2)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_2, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_2, EcsChildOf, 0) == p_2); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_1_lvl_2_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_2 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_add(world, p_1_2, Tag); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_2 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_add(world, p_2_2, Tag); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_2) != ecs_get_table(world, p_2_2)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_2, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_2, EcsChildOf, 0) == p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_2) == ecs_get_table(world, p_2_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_1_2)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_2, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_2, EcsChildOf, 0) == p_2); - - test_assert(ecs_has(world, p_1_2, Tag)); - test_assert(ecs_has(world, p_2_2, Tag)); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_2_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_2_lvl_2_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_entity_t p_1_1_2 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_add(world, p_1_1_2, Tag); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_entity_t p_2_1_2 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_add(world, p_2_1_2, Tag); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_1_1_2)); - test_assert(ecs_get_table(world, p_2_1_1) != ecs_get_table(world, p_2_1_2)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_2) == ecs_get_table(world, p_2_1_2)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_1_1_2)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - - test_assert(ecs_has(world, p_1_1_2, Tag)); - test_assert(ecs_has(world, p_2_1_2, Tag)); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_3_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_entity_t p_1_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_entity_t p_2_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1_1); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_3_lvl_w_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_entity(world, "p"); - - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_1_1_1 = ecs_new_entity(world, "p._1._1._1"); - ecs_entity_t p_1_1_1_1 = ecs_new_entity(world, "p._1._1._1._1"); - - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - ecs_entity_t p_2_1_1 = ecs_new_entity(world, "p._2._1._1"); - ecs_entity_t p_2_1_1_1 = ecs_new_entity(world, "p._2._1._1._1"); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_3_2_lvl_w_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_entity(world, "p"); - - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_1_1_1 = ecs_new_entity(world, "p._1._1._1"); - ecs_entity_t p_1_1_1_1 = ecs_new_entity(world, "p._1._1._1._1"); - ecs_entity_t p_1_1_2 = ecs_new_entity(world, "p._1._1._2"); - ecs_entity_t p_1_1_2_1 = ecs_new_entity(world, "p._1._1._2._1"); - - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - ecs_entity_t p_2_1_1 = ecs_new_entity(world, "p._2._1._1"); - ecs_entity_t p_2_1_1_1 = ecs_new_entity(world, "p._2._1._1._1"); - ecs_entity_t p_2_1_2 = ecs_new_entity(world, "p._2._1._2"); - ecs_entity_t p_2_1_2_1 = ecs_new_entity(world, "p._2._1._2._1"); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1_1) != ecs_get_table(world, p_1_1_2_1)); - test_assert(ecs_get_table(world, p_2_1_1_1) != ecs_get_table(world, p_2_1_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_2, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_1_1_2_1, EcsChildOf, 0) == p_1_1_2); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_2, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - test_assert(ecs_get_target(world, p_2_1_2_1, EcsChildOf, 0) == p_2_1_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1_1) == ecs_get_table(world, p_1_1_2_1)); - test_assert(ecs_get_table(world, p_2_1_1_1) == ecs_get_table(world, p_2_1_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_2, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_1_1_2_1, EcsChildOf, 0) == p_1_1_2); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_2, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - test_assert(ecs_get_target(world, p_2_1_2_1, EcsChildOf, 0) == p_2_1_2); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_2_lvl_nested(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_entity(world, "p"); - - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_1_1_1 = ecs_new_entity(world, "p._1._1._1"); - ecs_entity_t p_1_1_2 = ecs_new_entity(world, "p._1._1._2"); - ecs_entity_t p_1_2 = ecs_new_entity(world, "p._1._2"); - ecs_entity_t p_1_2_1 = ecs_new_entity(world, "p._1._2._1"); - ecs_entity_t p_1_2_2 = ecs_new_entity(world, "p._1._2._2"); - - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - ecs_entity_t p_2_1_1 = ecs_new_entity(world, "p._2._1._1"); - ecs_entity_t p_2_1_2 = ecs_new_entity(world, "p._2._1._2"); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_1_2)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_table(world, p_1_2_2) == ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_2, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_2, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_2_1, EcsChildOf, 0) == p_1_2); - test_assert(ecs_get_target(world, p_1_2_2, EcsChildOf, 0) == p_1_2); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_2, EcsChildOf, 0) == p_2_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p_1), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_1_2)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_table(world, p_1_2_2) == ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_2, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_2, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_2_1, EcsChildOf, 0) == p_1_2); - test_assert(ecs_get_target(world, p_1_2_2, EcsChildOf, 0) == p_1_2); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_2, EcsChildOf, 0) == p_2_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_1_2)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_table(world, p_1_2_2) == ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_2, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_2, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_2_1, EcsChildOf, 0) == p_1_2); - test_assert(ecs_get_target(world, p_1_2_2, EcsChildOf, 0) == p_1_2); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_2, EcsChildOf, 0) == p_2_1); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_3_lvl_nested(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_entity(world, "p"); - - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_1_1_1 = ecs_new_entity(world, "p._1._1._1"); - ecs_entity_t p_1_1_1_1 = ecs_new_entity(world, "p._1._1._1._1"); - ecs_entity_t p_1_1_2 = ecs_new_entity(world, "p._1._1._2"); - ecs_entity_t p_1_1_2_1 = ecs_new_entity(world, "p._1._1._2._1"); - - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - ecs_entity_t p_2_1_1 = ecs_new_entity(world, "p._2._1._1"); - ecs_entity_t p_2_1_1_1 = ecs_new_entity(world, "p._2._1._1._1"); - ecs_entity_t p_2_1_2 = ecs_new_entity(world, "p._2._1._2"); - ecs_entity_t p_2_1_2_1 = ecs_new_entity(world, "p._2._1._2._1"); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1_1) != ecs_get_table(world, p_1_1_2_1)); - test_assert(ecs_get_table(world, p_2_1_1_1) != ecs_get_table(world, p_2_1_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_2, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_1_1_2_1, EcsChildOf, 0) == p_1_1_2); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_2, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - test_assert(ecs_get_target(world, p_2_1_2_1, EcsChildOf, 0) == p_2_1_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p_1_1), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1_1) == ecs_get_table(world, p_1_1_2_1)); - test_assert(ecs_get_table(world, p_2_1_1_1) != ecs_get_table(world, p_2_1_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_2, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_1_1_2_1, EcsChildOf, 0) == p_1_1_2); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_2, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - test_assert(ecs_get_target(world, p_2_1_2_1, EcsChildOf, 0) == p_2_1_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1_1) == ecs_get_table(world, p_1_1_2_1)); - test_assert(ecs_get_table(world, p_2_1_1_1) == ecs_get_table(world, p_2_1_2_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_2, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_1_1_2_1, EcsChildOf, 0) == p_1_1_2); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_2, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - test_assert(ecs_get_target(world, p_2_1_2_1, EcsChildOf, 0) == p_2_1_2); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_1_lvl_after_delete(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_2 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_3 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_4 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_delete(world, p_1_3); - ecs_delete(world, p_1_4); - - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_2, EcsChildOf, 0) == p_1); - test_assert(!ecs_is_alive(world, p_1_3)); - test_assert(!ecs_is_alive(world, p_1_4)); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_2, EcsChildOf, 0) == p_1); - - ecs_fini(world); -} - -void FixedHierarchies_get_target_1_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_uint(ecs_get_target(world, p, EcsChildOf, 0), 0); - test_uint(ecs_get_target(world, p_1, EcsChildOf, 0), p); - test_uint(ecs_get_target(world, p_2, EcsChildOf, 0), p); - - test_uint(ecs_get_target(world, p_1_1, EcsChildOf, 0), p_1); - test_uint(ecs_get_target(world, p_2_1, EcsChildOf, 0), p_2); - - ecs_fini(world); -} - -void FixedHierarchies_get_target_2_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_uint(ecs_get_target(world, p, EcsChildOf, 0), 0); - test_uint(ecs_get_target(world, p_1, EcsChildOf, 0), p); - test_uint(ecs_get_target(world, p_2, EcsChildOf, 0), p); - - test_uint(ecs_get_target(world, p_1_1, EcsChildOf, 0), p_1); - test_uint(ecs_get_target(world, p_2_1, EcsChildOf, 0), p_2); - - test_uint(ecs_get_target(world, p_1_1_1, EcsChildOf, 0), p_1_1); - test_uint(ecs_get_target(world, p_2_1_1, EcsChildOf, 0), p_2_1); - - ecs_fini(world); -} - -void FixedHierarchies_get_depth_1_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_int(ecs_get_depth(world, p, EcsChildOf), 0); - - test_int(ecs_get_depth(world, p_1, EcsChildOf), 1); - test_int(ecs_get_depth(world, p_2, EcsChildOf), 1); - - test_int(ecs_get_depth(world, p_1_1, EcsChildOf), 2); - test_int(ecs_get_depth(world, p_2_1, EcsChildOf), 2); - - ecs_fini(world); -} - -void FixedHierarchies_get_depth_2_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_int(ecs_get_depth(world, p, EcsChildOf), 0); - - test_int(ecs_get_depth(world, p_1, EcsChildOf), 1); - test_int(ecs_get_depth(world, p_2, EcsChildOf), 1); - - test_int(ecs_get_depth(world, p_1_1, EcsChildOf), 2); - test_int(ecs_get_depth(world, p_2_1, EcsChildOf), 2); - - test_int(ecs_get_depth(world, p_1_1_1, EcsChildOf), 3); - test_int(ecs_get_depth(world, p_2_1_1, EcsChildOf), 3); - - ecs_fini(world); -} - -void FixedHierarchies_get_depth_after_reparent_root(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_int(ecs_get_depth(world, p, EcsChildOf), 0); - - test_int(ecs_get_depth(world, p_1, EcsChildOf), 1); - test_int(ecs_get_depth(world, p_2, EcsChildOf), 1); - - test_int(ecs_get_depth(world, p_1_1, EcsChildOf), 2); - test_int(ecs_get_depth(world, p_2_1, EcsChildOf), 2); - - ecs_entity_t gp = ecs_new_id(world); - ecs_add_pair(world, p, EcsChildOf, gp); - - test_int(ecs_get_depth(world, p, EcsChildOf), 1); - - test_int(ecs_get_depth(world, p_1, EcsChildOf), 2); - test_int(ecs_get_depth(world, p_2, EcsChildOf), 2); - - test_int(ecs_get_depth(world, p_1_1, EcsChildOf), 3); - test_int(ecs_get_depth(world, p_2_1, EcsChildOf), 3); - - ecs_fini(world); -} - -void FixedHierarchies_delete_fixed_1_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_delete(world, p); - - test_assert(!ecs_is_alive(world, p_1)); - test_assert(!ecs_is_alive(world, p_1_1)); - test_assert(!ecs_is_alive(world, p_2)); - test_assert(!ecs_is_alive(world, p_2_1)); - - ecs_fini(world); -} - -void FixedHierarchies_delete_fixed_2_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_delete(world, p); - - test_assert(!ecs_is_alive(world, p_1)); - test_assert(!ecs_is_alive(world, p_1_1)); - test_assert(!ecs_is_alive(world, p_1_1_1)); - test_assert(!ecs_is_alive(world, p_2)); - test_assert(!ecs_is_alive(world, p_2_1)); - test_assert(!ecs_is_alive(world, p_2_1_1)); - - ecs_fini(world); -} - -void FixedHierarchies_delete_with_fixed_1_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_delete_with(world, ecs_pair(EcsChildOf, p)); - - test_assert(!ecs_is_alive(world, p_1)); - test_assert(!ecs_is_alive(world, p_1_1)); - test_assert(!ecs_is_alive(world, p_2)); - test_assert(!ecs_is_alive(world, p_2_1)); - - ecs_fini(world); -} - -void FixedHierarchies_delete_with_fixed_2_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_delete_with(world, ecs_pair(EcsChildOf, p)); - - test_assert(!ecs_is_alive(world, p_1)); - test_assert(!ecs_is_alive(world, p_1_1)); - test_assert(!ecs_is_alive(world, p_1_1_1)); - test_assert(!ecs_is_alive(world, p_2)); - test_assert(!ecs_is_alive(world, p_2_1)); - test_assert(!ecs_is_alive(world, p_2_1_1)); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_1_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_1_lvl_w_init(void) { - ecs_world_t *world = ecs_init(); /* Registers metadata/ctor for TreeFixed */ - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_1_lvl_w_init_comp_after_tree_fixed(void) { - ecs_world_t *world = ecs_init(); /* Registers metadata/ctor for TreeFixed */ - - ECS_COMPONENT(world, Position); - ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor - }); - - ecs_entity_t rel = ecs_new_id(world); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set_pair_second(world, p_1_1, rel, Position, {1115, 2115}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - ecs_set_pair_second(world, p_2_1, rel, Position, {1215, 2215}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_pair(rel, ecs_id(Position)) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(0, it.sources[1]); - test_uint(p_1, it.sources[2]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *rp = ecs_field(&it, Position, 2); - test_int(rp[0].x, 1115); test_int(rp[0].y, 2115); - - Position *pp = ecs_field(&it, Position, 3); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(0, it.sources[1]); - test_uint(p_2, it.sources[2]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *rp = ecs_field(&it, Position, 2); - test_int(rp[0].x, 1215); test_int(rp[0].y, 2215); - - Position *pp = ecs_field(&it, Position, 3); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_2_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_1_1, Position, {1111, 2111}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - ecs_set(world, p_2_1_1, Position, {1211, 2211}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1111); test_int(p[0].y, 2111); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 111); test_int(pp[0].y, 211); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1211); test_int(p[0].y, 2211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 121); test_int(pp[0].y, 221); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_cascade_field_2_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_1_1, Position, {1111, 2111}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - ecs_set(world, p_2_1_1, Position, {1211, 2211}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent|EcsCascade }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1111); test_int(p[0].y, 2111); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 111); test_int(pp[0].y, 211); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1211); test_int(p[0].y, 2211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 121); test_int(pp[0].y, 221); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_1_fixed_1_regular(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - ecs_set(world, p, Velocity, {3, 4}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - { .id = ecs_id(Velocity), .src.flags = EcsParent } - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - test_uint(p, it.sources[2]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - test_uint(p, it.sources[2]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - test_uint(p, it.sources[2]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_only_fixed_1_lvls(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_fixed_1_lvls_no_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), false); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_fixed_1_lvls_2_no_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_fixed_1_lvls_no_match_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_fixed_1_lvls_match_no_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_field_2_fixed_2_lvls(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - ecs_set(world, p, Velocity, {3, 4}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1, Mass, {5}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_1_1, Position, {1111, 2111}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2, Mass, {6}); - ecs_set(world, p_2_1, Position, {121, 221}); - ecs_set(world, p_2_1_1, Position, {1211, 2211}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - { .id = ecs_id(Velocity), .src.flags = EcsParent }, - { .id = ecs_id(Mass), .src.flags = EcsSelf|EcsParent } - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - test_uint(p, it.sources[2]); - test_uint(0, it.sources[3]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - Mass *mp = ecs_field(&it, Mass, 4); - test_int(mp[0], 5); - test_int(mp[1], 6); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1_1, it.sources[1]); - test_uint(p, it.sources[2]); - test_uint(p_1, it.sources[3]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1111); test_int(p[0].y, 2111); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 111); test_int(pp[0].y, 211); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - Mass *mp = ecs_field(&it, Mass, 4); - test_int(mp[0], 5); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2_1, it.sources[1]); - test_uint(p, it.sources[2]); - test_uint(p_2, it.sources[3]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1211); test_int(p[0].y, 2211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 121); test_int(pp[0].y, 221); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - Mass *mp = ecs_field(&it, Mass, 4); - test_int(mp[0], 6); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - test_uint(p, it.sources[2]); - test_uint(p_1, it.sources[3]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - Mass *mp = ecs_field(&it, Mass, 4); - test_int(mp[0], 5); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - test_uint(p, it.sources[2]); - test_uint(p_2, it.sources[3]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - Mass *mp = ecs_field(&it, Mass, 4); - test_int(mp[0], 6); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_next_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - test_bool(ecs_query_next_table(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_next_table_1_elem(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_uint(1, it.count); - test_uint(p_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - test_bool(ecs_query_next_table(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_next_table_1_elem_no_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterNext); - test_uint(0, it.count); - } - test_bool(ecs_query_next_table(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_nested_make_fixed(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent|EcsCascade }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_1_2 = ecs_new_entity(world, "p._1._2"); - - ecs_set(world, p, Position, {1, 2}); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_2, Position, {112, 212}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p_1), NULL); - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_uint(1, it.count); - test_uint(p_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_uint(2, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(p_1_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - test_int(p[1].x, 112); test_int(p[1].y, 212); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - test_bool(ecs_query_next_table(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_nested_make_fixed_w_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .oper = EcsOptional, .src.flags = EcsParent|EcsCascade }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_1_2 = ecs_new_entity(world, "p._1._2"); - - ecs_set(world, p, Position, {1, 2}); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_2, Position, {112, 212}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p_1), NULL); - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(0, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1); test_int(p[0].y, 2); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp == NULL); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(p_1_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - test_int(p[1].x, 112); test_int(p[1].y, 212); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_nested_make_fixed_w_optional_match_children_only(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .oper = EcsOptional, .src.flags = EcsParent|EcsCascade }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_1_2 = ecs_new_entity(world, "p._1._2"); - - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_2, Position, {112, 212}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p_1), NULL); - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(p_1_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(0, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - test_int(p[1].x, 112); test_int(p[1].y, 212); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp == NULL); - } - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_nested_w_2_parents_make_fixed(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .oper = EcsOptional, .src.flags = EcsParent|EcsCascade }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_1_1_1 = ecs_new_entity(world, "p._1._1._1"); - ecs_entity_t p_1_2 = ecs_new_entity(world, "p._1._2"); - ecs_entity_t p_1_2_1 = ecs_new_entity(world, "p._1._2._1"); - ecs_entity_t p_1_2_2 = ecs_new_entity(world, "p._1._2._2"); - - ecs_set(world, p, Position, {1, 2}); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_1_1, Position, {1111, 2111}); - ecs_set(world, p_1_2, Position, {112, 212}); - ecs_set(world, p_1_2_1, Position, {1121, 2121}); - ecs_set(world, p_1_2_2, Position, {1122, 2122}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p_1), NULL); - - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_2)); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_2)); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(0, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1); test_int(p[0].y, 2); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp == NULL); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(p_1_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - test_int(p[1].x, 112); test_int(p[1].y, 212); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1111); test_int(p[0].y, 2111); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 111); test_int(pp[0].y, 211); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1_2_1, it.entities[0]); - test_uint(p_1_2_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p_1_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1121); test_int(p[0].y, 2121); - test_int(p[1].x, 1122); test_int(p[1].y, 2122); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 112); test_int(pp[0].y, 212); - } - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_table_w_3_parents(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .oper = EcsOptional, .src.flags = EcsParent|EcsCascade }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_1_1_1 = ecs_new_entity(world, "p._1._1._1"); - ecs_entity_t p_1_1_2 = ecs_new_entity(world, "p._1._1._2"); - ecs_entity_t p_1_1_3 = ecs_new_entity(world, "p._1._1._3"); - ecs_entity_t p_1_2 = ecs_new_entity(world, "p._1._2"); - ecs_entity_t p_1_2_1 = ecs_new_entity(world, "p._1._2._1"); - ecs_entity_t p_1_2_2 = ecs_new_entity(world, "p._1._2._2"); - ecs_entity_t p_1_3 = ecs_new_entity(world, "p._1._3"); - ecs_entity_t p_1_3_1 = ecs_new_entity(world, "p._1._3._1"); - - ecs_set(world, p, Position, {1, 2}); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_1_1, Position, {1111, 2111}); - ecs_set(world, p_1_1_2, Position, {1112, 2112}); - ecs_set(world, p_1_1_3, Position, {1113, 2113}); - ecs_set(world, p_1_2, Position, {112, 212}); - ecs_set(world, p_1_2_1, Position, {1121, 2121}); - ecs_set(world, p_1_2_2, Position, {1122, 2122}); - ecs_set(world, p_1_3, Position, {113, 213}); - ecs_set(world, p_1_3_1, Position, {1131, 2131}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p_1), NULL); - - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_2)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_3_1)); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_2_2)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_3_1)); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(0, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1); test_int(p[0].y, 2); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp == NULL); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(3, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(p_1_2, it.entities[1]); - test_uint(p_1_3, it.entities[2]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - test_int(p[1].x, 112); test_int(p[1].y, 212); - test_int(p[2].x, 113); test_int(p[2].y, 213); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(3, it.count); - test_uint(p_1_1_1, it.entities[0]); - test_uint(p_1_1_2, it.entities[1]); - test_uint(p_1_1_3, it.entities[2]); - test_uint(0, it.sources[0]); - test_uint(p_1_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1111); test_int(p[0].y, 2111); - test_int(p[1].x, 1112); test_int(p[1].y, 2112); - test_int(p[2].x, 1113); test_int(p[2].y, 2113); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 111); test_int(pp[0].y, 211); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1_2_1, it.entities[0]); - test_uint(p_1_2_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p_1_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1121); test_int(p[0].y, 2121); - test_int(p[1].x, 1122); test_int(p[1].y, 2122); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 112); test_int(pp[0].y, 212); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_3_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1_3, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1131); test_int(p[0].y, 2131); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 113); test_int(pp[0].y, 213); - } - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_change_detection_1st(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }, - { - .id = ecs_id(Position), - .src.flags = EcsParent|EcsCascade, - .inout = EcsIn - }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - // offset the column of Position to verify change detection is testing - // againt the right one. - ecs_add(world, p_2, Velocity); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 11); test_int(pp->y, 21); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_1, Position, {22, 42}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 22); test_int(pp->y, 42); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), false); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_change_detection_2nd(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }, - { - .id = ecs_id(Position), - .src.flags = EcsParent|EcsCascade, - .inout = EcsIn - }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - // offset the column of Position to verify change detection is testing - // againt the right one. - ecs_add(world, p_2, Velocity); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 11); test_int(pp->y, 21); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_2, Position, {24, 44}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), false); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 11); test_int(pp->y, 21); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 24); test_int(pp->y, 44); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_change_detection_iter_twice(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }, - { - .id = ecs_id(Position), - .src.flags = EcsParent|EcsCascade, - .inout = EcsIn - }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - // offset the column of Position to verify change detection is testing - // againt the right one. - ecs_add(world, p_2, Velocity); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 11); test_int(pp->y, 21); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_1, Position, {22, 42}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 22); test_int(pp->y, 42); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), false); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), false); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 22); test_int(pp->y, 42); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), false); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_change_detection_iter_twice_each_parent(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }, - { - .id = ecs_id(Position), - .src.flags = EcsParent|EcsCascade, - .inout = EcsIn - }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - // offset the column of Position to verify change detection is testing - // againt the right one. - ecs_add(world, p_2, Velocity); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 11); test_int(pp->y, 21); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_1, Position, {22, 42}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 22); test_int(pp->y, 42); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), false); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_2, Position, {24, 44}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, false) == EcsIterYield); - test_bool(ecs_query_changed(NULL, &it), false); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 22); test_int(pp->y, 42); - } - { - test_assert(ecs_query_populate(&it, false) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 24); test_int(pp->y, 44); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_change_detection_1st_populate_when_changed(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }, - { - .id = ecs_id(Position), - .src.flags = EcsParent|EcsCascade, - .inout = EcsIn - }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - // offset the column of Position to verify change detection is testing - // againt the right one. - ecs_add(world, p_2, Velocity); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterYield); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 11); test_int(pp->y, 21); - } - { - test_assert(ecs_query_populate(&it, true) == EcsIterNextYield); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_1, Position, {22, 42}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterYield); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 22); test_int(pp->y, 42); - } - { - test_assert(ecs_query_populate(&it, true) == EcsIterNext); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_change_detection_2nd_populate_when_changed(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }, - { - .id = ecs_id(Position), - .src.flags = EcsParent|EcsCascade, - .inout = EcsIn - }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - // offset the column of Position to verify change detection is testing - // againt the right one. - ecs_add(world, p_2, Velocity); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterYield); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 11); test_int(pp->y, 21); - } - { - test_assert(ecs_query_populate(&it, true) == EcsIterNextYield); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_2, Position, {24, 44}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterNextYield); - test_bool(ecs_query_changed(NULL, &it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 24); test_int(pp->y, 44); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_change_detection_iter_twice_populate_when_changed(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }, - { - .id = ecs_id(Position), - .src.flags = EcsParent|EcsCascade, - .inout = EcsIn - }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - // offset the column of Position to verify change detection is testing - // againt the right one. - ecs_add(world, p_2, Velocity); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterYield); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 11); test_int(pp->y, 21); - } - { - test_assert(ecs_query_populate(&it, true) == EcsIterNextYield); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_1, Position, {22, 42}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterYield); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 22); test_int(pp->y, 42); - } - { - test_assert(ecs_query_populate(&it, true) == EcsIterNext); - } - test_bool(ecs_query_next_table(&it), false); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterNext); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_fini(world); -} - -void FixedHierarchies_query_w_parent_change_detection_iter_twice_each_parent_populate_when_changed(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }, - { - .id = ecs_id(Position), - .src.flags = EcsParent|EcsCascade, - .inout = EcsIn - }, - }, - .filter.instanced = true - }); - - ecs_entity_t p = ecs_new_entity(world, "p"); - ecs_entity_t p_1 = ecs_new_entity(world, "p._1"); - ecs_entity_t p_1_1 = ecs_new_entity(world, "p._1._1"); - ecs_entity_t p_2 = ecs_new_entity(world, "p._2"); - ecs_entity_t p_2_1 = ecs_new_entity(world, "p._2._1"); - - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - // offset the column of Position to verify change detection is testing - // againt the right one. - ecs_add(world, p_2, Velocity); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterYield); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 11); test_int(pp->y, 21); - } - { - test_assert(ecs_query_populate(&it, true) == EcsIterNextYield); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 12); test_int(pp->y, 22); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_1, Position, {22, 42}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterYield); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 22); test_int(pp->y, 42); - } - { - test_assert(ecs_query_populate(&it, true) == EcsIterNext); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_set(world, p_2, Position, {24, 44}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(ecs_query_next_table(&it), true); - test_assert(ecs_query_populate(&it, true) == EcsIterNextYield); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_assert(pp != NULL); - test_int(pp->x, 24); test_int(pp->y, 44); - } - test_bool(ecs_query_next_table(&it), false); - } - - ecs_fini(world); -} - -void FixedHierarchies_staged_query_w_parent_field_1_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_readonly_begin(world); - - ecs_iter_t it = ecs_query_iter(stage, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - test_bool(ecs_query_next(&it), false); - - ecs_readonly_end(world); - - ecs_fini(world); -} - -void FixedHierarchies_staged_query_w_parent_field_2_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_1_1, Position, {1111, 2111}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - ecs_set(world, p_2_1_1, Position, {1211, 2211}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - }, - .filter.instanced = true - }); - - ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_readonly_begin(world); - - ecs_iter_t it = ecs_query_iter(stage, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1111); test_int(p[0].y, 2111); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 111); test_int(pp[0].y, 211); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1211); test_int(p[0].y, 2211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 121); test_int(pp[0].y, 221); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - test_bool(ecs_query_next(&it), false); - - ecs_readonly_end(world); - - ecs_fini(world); -} - -void FixedHierarchies_staged_query_w_parent_field_1_fixed_1_regular(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - ecs_set(world, p, Velocity, {3, 4}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent }, - { .id = ecs_id(Velocity), .src.flags = EcsParent } - }, - .filter.instanced = true - }); - - ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_readonly_begin(world); - - ecs_iter_t it = ecs_query_iter(stage, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - test_uint(p, it.sources[2]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - test_uint(p, it.sources[2]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - test_uint(p, it.sources[2]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - Velocity *vp = ecs_field(&it, Velocity, 3); - test_int(vp[0].x, 3); test_int(vp[0].y, 4); - } - test_bool(ecs_query_next(&it), false); - - ecs_readonly_end(world); - - ecs_fini(world); -} - -void FixedHierarchies_staged_query_w_cascade_field_2_lvl(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t p = ecs_set(world, 0, Position, {1, 2}); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_set(world, p_1, Position, {11, 21}); - ecs_set(world, p_1_1, Position, {111, 211}); - ecs_set(world, p_1_1_1, Position, {1111, 2111}); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_set(world, p_2, Position, {12, 22}); - ecs_set(world, p_2_1, Position, {121, 221}); - ecs_set(world, p_2_1_1, Position, {1211, 2211}); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Position), .src.flags = EcsParent|EcsCascade }, - }, - .filter.instanced = true - }); - - ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_readonly_begin(world); - - ecs_iter_t it = ecs_query_iter(stage, q); - { - test_bool(ecs_query_next(&it), true); - test_uint(2, it.count); - test_uint(p_1, it.entities[0]); - test_uint(p_2, it.entities[1]); - test_uint(0, it.sources[0]); - test_uint(p, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 11); test_int(p[0].y, 21); - test_int(p[1].x, 12); test_int(p[1].y, 22); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 1); test_int(pp[0].y, 2); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 111); test_int(p[0].y, 211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 11); test_int(pp[0].y, 21); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 121); test_int(p[0].y, 221); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 12); test_int(pp[0].y, 22); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_1_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_1_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1111); test_int(p[0].y, 2111); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 111); test_int(pp[0].y, 211); - } - { - test_bool(ecs_query_next(&it), true); - test_uint(1, it.count); - test_uint(p_2_1_1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(p_2_1, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 1211); test_int(p[0].y, 2211); - - Position *pp = ecs_field(&it, Position, 2); - test_int(pp[0].x, 121); test_int(pp[0].y, 221); - } - test_bool(ecs_query_next(&it), false); - - ecs_readonly_end(world); - - ecs_fini(world); -} - -void FixedHierarchies_add_to_fixed(void) { - install_test_abort(); - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_expect_abort(); - ecs_add(world, p_1_1, Tag); -} - -void FixedHierarchies_remove_from_fixed(void) { - install_test_abort(); - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_add(world, p_1_1, Tag); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_expect_abort(); - ecs_remove(world, p_1_1, Tag); -} - -void FixedHierarchies_delete_fixed(void) { - install_test_abort(); - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_expect_abort(); - ecs_delete(world, p_1_1); -} - -void FixedHierarchies_clear_fixed(void) { - install_test_abort(); - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_expect_abort(); - ecs_clear(world, p_1_1); -} - -void FixedHierarchies_make_fixed_1_lvl_w_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - ecs_set_name(world, p, "p"); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set_name(world, p_1, "name_1"); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set_name(world, p_1_1, "child_1"); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set_name(world, p_2, "name_2"); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set_name(world, p_2_1, "child_1"); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_lookup_fullpath(world, "p.name_1") == p_1); - test_assert(ecs_lookup_fullpath(world, "p.name_2") == p_2); - test_assert(ecs_lookup_fullpath(world, "p.name_1.child_1") == 0); - test_assert(ecs_lookup_fullpath(world, "p.name_2.child_1") == 0); - - test_str(ecs_get_name(world, p_1), "name_1"); - test_str(ecs_get_name(world, p_2), "name_2"); - test_str(ecs_get_name(world, p_1_1), NULL); - test_str(ecs_get_name(world, p_2_1), NULL); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_2_lvl_w_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - ecs_set_name(world, p, "p"); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set_name(world, p_1, "name_1"); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set_name(world, p_1_1, "child_1"); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_set_name(world, p_1_1_1, "child_1_1"); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set_name(world, p_2, "name_2"); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set_name(world, p_2_1, "child_1"); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_set_name(world, p_2_1_1, "child_1_1"); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_lookup_fullpath(world, "p.name_1") == p_1); - test_assert(ecs_lookup_fullpath(world, "p.name_2") == p_2); - test_assert(ecs_lookup_fullpath(world, "p.name_1.child_1") == 0); - test_assert(ecs_lookup_fullpath(world, "p.name_2.child_1") == 0); - test_assert(ecs_lookup_fullpath(world, "p.name_1.child_1.child_1_1") == 0); - test_assert(ecs_lookup_fullpath(world, "p.name_2.child_1.child_1_1") == 0); - - test_str(ecs_get_name(world, p_1), "name_1"); - test_str(ecs_get_name(world, p_2), "name_2"); - test_str(ecs_get_name(world, p_1_1), NULL); - test_str(ecs_get_name(world, p_2_1), NULL); - test_str(ecs_get_name(world, p_1_1_1), NULL); - test_str(ecs_get_name(world, p_2_1_1), NULL); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_1_lvl_w_name_keep_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - ecs_set_name(world, p, "p"); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set_name(world, p_1, "name_1"); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set_name(world, p_1_1, "child_1_1"); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set_name(world, p_2, "name_2"); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set_name(world, p_2_1, "child_2_1"); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), &(ecs_flatten_desc_t){ - .keep_names = true - }); - - test_assert(ecs_lookup_fullpath(world, "p.name_1") == p_1); - test_assert(ecs_lookup_fullpath(world, "p.name_2") == p_2); - test_assert(ecs_lookup_fullpath(world, "p.name_1.child_1") == 0); - test_assert(ecs_lookup_fullpath(world, "p.name_2.child_1") == 0); - - test_str(ecs_get_name(world, p_1), "name_1"); - test_str(ecs_get_name(world, p_2), "name_2"); - test_str(ecs_get_name(world, p_1_1), "child_1_1"); - test_str(ecs_get_name(world, p_2_1), "child_2_1"); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_2_lvl_w_name_keep_name(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - ecs_set_name(world, p, "p"); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set_name(world, p_1, "name_1"); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_set_name(world, p_1_1, "child_1_1"); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_set_name(world, p_1_1_1, "child_1_1_1"); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_set_name(world, p_2, "name_2"); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_set_name(world, p_2_1, "child_2_1"); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_set_name(world, p_2_1_1, "child_2_1_1"); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), &(ecs_flatten_desc_t){ - .keep_names = true - }); - - test_assert(ecs_lookup_fullpath(world, "p.name_1") == p_1); - test_assert(ecs_lookup_fullpath(world, "p.name_2") == p_2); - test_assert(ecs_lookup_fullpath(world, "p.name_1.child_1") == 0); - test_assert(ecs_lookup_fullpath(world, "p.name_2.child_1") == 0); - test_assert(ecs_lookup_fullpath(world, "p.name_1.child_1.child_1_1") == 0); - test_assert(ecs_lookup_fullpath(world, "p.name_2.child_1.child_1_1") == 0); - - test_str(ecs_get_name(world, p_1), "name_1"); - test_str(ecs_get_name(world, p_2), "name_2"); - test_str(ecs_get_name(world, p_1_1), "child_1_1"); - test_str(ecs_get_name(world, p_2_1), "child_2_1"); - test_str(ecs_get_name(world, p_1_1_1), "child_1_1_1"); - test_str(ecs_get_name(world, p_2_1_1), "child_2_1_1"); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_2_lvl_lose_depth(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), &(ecs_flatten_desc_t){ - .lose_depth = true - }); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_1_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - - ecs_fini(world); -} - -void FixedHierarchies_make_fixed_3_lvl_lose_depth(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t p = ecs_new_id(world); - - ecs_entity_t p_1 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1); - ecs_entity_t p_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1); - ecs_entity_t p_1_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_1_1_1); - - ecs_entity_t p_2 = ecs_new_w_pair(world, EcsChildOf, p); - ecs_entity_t p_2_1 = ecs_new_w_pair(world, EcsChildOf, p_2); - ecs_entity_t p_2_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1); - ecs_entity_t p_2_1_1_1 = ecs_new_w_pair(world, EcsChildOf, p_2_1_1); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) != ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - - ecs_flatten(world, ecs_pair(EcsChildOf, p), NULL); - - test_assert(ecs_get_table(world, p_1) == ecs_get_table(world, p_2)); - test_assert(ecs_get_table(world, p_1_1) == ecs_get_table(world, p_2_1)); - test_assert(ecs_get_table(world, p_1_1_1) == ecs_get_table(world, p_2_1_1)); - test_assert(ecs_get_table(world, p_1_1_1) != ecs_get_table(world, p_1_1)); - test_assert(ecs_get_target(world, p_1, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_1_1, EcsChildOf, 0) == p_1); - test_assert(ecs_get_target(world, p_1_1_1, EcsChildOf, 0) == p_1_1); - test_assert(ecs_get_target(world, p_1_1_1_1, EcsChildOf, 0) == p_1_1_1); - test_assert(ecs_get_target(world, p_2, EcsChildOf, 0) == p); - test_assert(ecs_get_target(world, p_2_1, EcsChildOf, 0) == p_2); - test_assert(ecs_get_target(world, p_2_1_1, EcsChildOf, 0) == p_2_1); - test_assert(ecs_get_target(world, p_2_1_1_1, EcsChildOf, 0) == p_2_1_1); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/Id.c b/vendors/flecs/test/api/src/Id.c deleted file mode 100644 index 1a9865515..000000000 --- a/vendors/flecs/test/api/src/Id.c +++ /dev/null @@ -1,213 +0,0 @@ -#include - -void Id_0_is_wildcard(void) { - test_assert( !ecs_id_is_wildcard(0) ); -} - -void Id_wildcard_is_wildcard(void) { - test_assert( ecs_id_is_wildcard(EcsWildcard) ); -} - -void Id_any_is_wildcard(void) { - test_assert( ecs_id_is_wildcard(EcsAny) ); -} - -void Id_entity_is_wildcard(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); - test_assert( !ecs_id_is_wildcard(e) ); - ecs_fini(world); -} - -void Id_pair_is_wildcard(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); - test_assert( !ecs_id_is_wildcard(ecs_pair(r, o)) ); - ecs_fini(world); -} - -void Id_pair_w_rel_wildcard_is_wildcard(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t o = ecs_new_id(world); - test_assert( ecs_id_is_wildcard(ecs_pair(EcsWildcard, o)) ); - ecs_fini(world); -} - -void Id_pair_w_obj_wildcard_is_wildcard(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - test_assert( ecs_id_is_wildcard(ecs_pair(r, EcsWildcard)) ); - ecs_fini(world); -} - -void Id_pair_w_wildcard_wildcard_is_wildcard(void) { - test_assert( ecs_id_is_wildcard(ecs_pair(EcsWildcard, EcsWildcard)) ); -} - -void Id_pair_w_rel_any_is_wildcard(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t o = ecs_new_id(world); - test_assert( ecs_id_is_wildcard(ecs_pair(EcsAny, o)) ); - ecs_fini(world); -} - -void Id_pair_w_obj_any_is_wildcard(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - test_assert( ecs_id_is_wildcard(ecs_pair(r, EcsAny)) ); - ecs_fini(world); -} - -void Id_pair_w_any_any_is_wildcard(void) { - test_assert( ecs_id_is_wildcard(ecs_pair(EcsAny, EcsAny)) ); -} - -void Id_pair_w_override_is_wildcard(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - test_assert( !ecs_id_is_wildcard(ECS_OVERRIDE | ecs_pair(r, EcsWildcard))); - ecs_fini(world); -} - -void Id_pair_w_toggle_is_wildcard(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - test_assert( !ecs_id_is_wildcard(ECS_TOGGLE | ecs_pair(r, EcsWildcard))); - ecs_fini(world); -} - -void Id_tag_id_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t t = ecs_new_id(world); - test_assert( ecs_id_is_tag(world, t)); - ecs_fini(world); -} - -void Id_component_id_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - test_assert( !ecs_id_is_tag(world, ecs_id(Position))); - ecs_fini(world); -} - -void Id_pair_id_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); - test_assert( ecs_id_is_tag(world, ecs_pair(r, o))); - ecs_fini(world); -} - -void Id_pair_id_w_rel_component_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - ecs_entity_t o = ecs_new_id(world); - test_assert( !ecs_id_is_tag(world, ecs_pair(ecs_id(Position), o))); - ecs_fini(world); -} - -void Id_pair_id_w_obj_component_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - ecs_entity_t r = ecs_new_id(world); - test_assert( !ecs_id_is_tag(world, ecs_pair(r, ecs_id(Position)))); - ecs_fini(world); -} - -void Id_pair_id_w_rel_component_obj_wildcard_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - test_assert( !ecs_id_is_tag(world, ecs_pair(ecs_id(Position), EcsWildcard))); - ecs_fini(world); -} - -void Id_pair_id_w_obj_wildcard_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - test_assert( !ecs_id_is_tag(world, ecs_pair(r, EcsWildcard))); - ecs_fini(world); -} - -void Id_pair_id_w_tag_property_w_obj_component_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - ecs_entity_t r = ecs_new_w_id(world, EcsTag); - test_assert( ecs_id_is_tag(world, ecs_pair(r, ecs_id(Position)))); - ecs_fini(world); -} - -void Id_pair_id_w_tag_property_w_obj_wildcard_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - ecs_entity_t r = ecs_new_w_id(world, EcsTag); - test_assert( ecs_id_is_tag(world, ecs_pair(r, EcsWildcard))); - ecs_fini(world); -} - -void Id_id_w_override_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - test_assert( ecs_id_is_tag(world, ECS_OVERRIDE | ecs_id(Position))); - ecs_fini(world); -} - -void Id_id_w_toggle_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - test_assert( ecs_id_is_tag(world, ECS_TOGGLE | ecs_id(Position))); - ecs_fini(world); -} - -void Id_pair_id_override_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - ecs_entity_t o = ecs_new_id(world); - test_assert( ecs_id_is_tag(world, ECS_OVERRIDE | ecs_pair(ecs_id(Position), o))); - ecs_fini(world); -} - -void Id_pair_id_toggle_is_tag(void) { - ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); - ecs_entity_t o = ecs_new_id(world); - test_assert( ecs_id_is_tag(world, ECS_TOGGLE | ecs_pair(ecs_id(Position), o))); - ecs_fini(world); -} - -void Id_make_pair(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t t = ecs_new_id(world); - ecs_id_t id = ecs_make_pair(r, t); - - test_assert( ecs_pair_first(world, id) == r); - test_assert( ecs_pair_second(world, id) == t); - - ecs_fini(world); -} - -void Id_make_pair_of_pair(void) { - install_test_abort(); - ecs_world_t *world = ecs_mini(); - - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t t = ecs_new_id(world); - ecs_id_t id = ecs_make_pair(r, t); - - test_expect_abort(); - ecs_make_pair(id, t); -} - -void Id_make_pair_of_pair_tgt(void) { - install_test_abort(); - ecs_world_t *world = ecs_mini(); - - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t t = ecs_new_id(world); - ecs_id_t id = ecs_make_pair(r, t); - - test_expect_abort(); - ecs_make_pair(r, id); -} diff --git a/vendors/flecs/test/api/src/Iter.c b/vendors/flecs/test/api/src/Iter.c deleted file mode 100644 index 48fc42159..000000000 --- a/vendors/flecs/test/api/src/Iter.c +++ /dev/null @@ -1,2228 +0,0 @@ -#include - -void Iter_page_iter_0_0(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e3, TagA); - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 0, 0); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.frame_offset, 0); - test_int(pit.entities[0], e1); - test_int(pit.entities[1], e2); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e1); - test_int(ptr[1].value, e2); - } - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.frame_offset, 2); - test_int(pit.entities[0], e3); - test_int(pit.entities[1], e4); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - test_int(ptr[1].value, e4); - } - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.frame_offset, 4); - test_int(pit.entities[0], e5); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_page_iter_1_0(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e3, TagA); - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 1, 0); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e2); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e2); - } - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.entities[0], e3); - test_int(pit.entities[1], e4); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - test_int(ptr[1].value, e4); - } - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e5); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_page_iter_0_1(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e3, TagA); - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 0, 1); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e1); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e1); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_page_iter_n_0(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 2, 0); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e3); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - } - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e4); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e4); - } - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e5); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_page_iter_0_n(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 0, 2); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.entities[0], e1); - test_int(pit.entities[1], e2); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e1); - test_int(ptr[1].value, e2); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_page_iter_m_n(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - ecs_entity_t e6 = ecs_new_id(world); ecs_set(world, e6, Self, {e6}); - - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagA); - ecs_add(world, e6, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 2, 3); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e3); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - } - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.entities[0], e4); - test_int(pit.entities[1], e5); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e4); - test_int(ptr[1].value, e5); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_page_iter_skip_1_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e3, TagA); - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 2, 0); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.entities[0], e3); - test_int(pit.entities[1], e4); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - test_int(ptr[1].value, e4); - } - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e5); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_page_iter_skip_2_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e3, TagA); - ecs_add(world, e4, TagB); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 3, 0); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.entities[0], e4); - test_int(pit.entities[1], e5); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e4); - test_int(ptr[1].value, e5); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_worker_iter_1(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e3, TagA); - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_worker_iter(&it, 0, 1); - - { - test_bool(ecs_worker_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.entities[0], e1); - test_int(pit.entities[1], e2); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e1); - test_int(ptr[1].value, e2); - } - - { - test_bool(ecs_worker_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.entities[0], e3); - test_int(pit.entities[1], e4); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - test_int(ptr[1].value, e4); - } - - { - test_bool(ecs_worker_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e5); - test_int(pit.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - } - - test_bool(ecs_worker_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_worker_iter_2(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e3, TagA); - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it_1 = ecs_filter_iter(world, &f); - ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 2); - ecs_iter_t it_2 = ecs_filter_iter(world, &f); - ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 2); - - /* Iter 1 */ - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e1); - test_int(pit_1.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e1); - } - - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e3); - test_int(pit_1.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - } - - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e5); - test_int(pit_1.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - } - - test_bool(ecs_worker_next(&pit_1), false); - - /* Iter 2 */ - { - test_bool(ecs_worker_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e2); - test_int(pit_2.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_2, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e2); - } - - { - test_bool(ecs_worker_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e4); - test_int(pit_2.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_2, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e4); - } - - test_bool(ecs_worker_next(&pit_2), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_worker_iter_3(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - ecs_entity_t e6 = ecs_new_id(world); ecs_set(world, e6, Self, {e6}); - - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagA); - ecs_add(world, e6, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it_1 = ecs_filter_iter(world, &f); - ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 3); - ecs_iter_t it_2 = ecs_filter_iter(world, &f); - ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 3); - ecs_iter_t it_3 = ecs_filter_iter(world, &f); - ecs_iter_t pit_3 = ecs_worker_iter(&it_3, 2, 3); - - /* Iter 1 */ - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e1); - test_int(pit_1.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e1); - } - - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e4); - test_int(pit_1.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e4); - } - - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e6); - test_int(pit_1.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e6); - } - - test_bool(ecs_worker_next(&pit_1), false); - - /* Iter 2 */ - { - test_bool(ecs_worker_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e2); - test_int(pit_2.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_2, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e2); - } - - { - test_bool(ecs_worker_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e5); - test_int(pit_2.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_2, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - } - - test_bool(ecs_worker_next(&pit_2), false); - - /* Iter 3 */ - { - test_bool(ecs_worker_next(&pit_3), true); - test_int(pit_3.count, 1); - test_int(pit_3.entities[0], e3); - test_int(pit_3.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_3, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - } - - test_bool(ecs_worker_next(&pit_3), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_worker_iter_4(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - ecs_entity_t e6 = ecs_new_id(world); ecs_set(world, e6, Self, {e6}); - ecs_entity_t e7 = ecs_new_id(world); ecs_set(world, e7, Self, {e7}); - ecs_entity_t e8 = ecs_new_id(world); ecs_set(world, e8, Self, {e8}); - ecs_entity_t e9 = ecs_new_id(world); ecs_set(world, e9, Self, {e9}); - - ecs_add(world, e5, TagA); - ecs_add(world, e6, TagA); - ecs_add(world, e7, TagA); - ecs_add(world, e8, TagB); - ecs_add(world, e9, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }} - }); - - ecs_iter_t it_1 = ecs_filter_iter(world, &f); - ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 4); - ecs_iter_t it_2 = ecs_filter_iter(world, &f); - ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 4); - ecs_iter_t it_3 = ecs_filter_iter(world, &f); - ecs_iter_t pit_3 = ecs_worker_iter(&it_3, 2, 4); - ecs_iter_t it_4 = ecs_filter_iter(world, &f); - ecs_iter_t pit_4 = ecs_worker_iter(&it_4, 3, 4); - - /* Iter 1 */ - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e1); - test_int(pit_1.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e1); - } - - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e5); - test_int(pit_1.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - } - - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e8); - test_int(pit_1.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e8); - } - - test_bool(ecs_worker_next(&pit_1), false); - - /* Iter 2 */ - { - test_bool(ecs_worker_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e2); - test_int(pit_2.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_2, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e2); - } - - { - test_bool(ecs_worker_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e6); - test_int(pit_2.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_2, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e6); - } - - { - test_bool(ecs_worker_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e9); - test_int(pit_2.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_2, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e9); - } - - test_bool(ecs_worker_next(&pit_2), false); - - /* Iter 3 */ - { - test_bool(ecs_worker_next(&pit_3), true); - test_int(pit_3.count, 1); - test_int(pit_3.entities[0], e3); - test_int(pit_3.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_3, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - } - - { - test_bool(ecs_worker_next(&pit_3), true); - test_int(pit_3.count, 1); - test_int(pit_3.entities[0], e7); - test_int(pit_3.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_3, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e7); - } - - test_bool(ecs_worker_next(&pit_3), false); - - /* Iter 4 */ - { - test_bool(ecs_worker_next(&pit_4), true); - test_int(pit_4.count, 1); - test_int(pit_4.entities[0], e4); - test_int(pit_4.ids[0], ecs_id(Self)); - - Self *ptr = ecs_field(&pit_4, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e4); - } - - test_bool(ecs_worker_next(&pit_4), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_paged_iter_w_shared_comp(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Self); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t foo = ecs_set(world, 0, Position, {10, 20}); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e3, TagA); - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }, { ecs_id(Position), .src.id = foo} }, - .instanced = true - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 0, 0); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.entities[0], e1); - test_int(pit.entities[1], e2); - test_int(pit.ids[0], ecs_id(Self)); - test_int(pit.ids[1], ecs_id(Position)); - test_int(pit.sources[0], 0); - test_int(pit.sources[1], foo); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e1); - test_int(ptr[1].value, e2); - - Position *pptr = ecs_field(&pit, Position, 2); - test_int(pptr->x, 10); - test_int(pptr->y, 20); - } - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 2); - test_int(pit.entities[0], e3); - test_int(pit.entities[1], e4); - test_int(pit.ids[0], ecs_id(Self)); - test_int(pit.ids[1], ecs_id(Position)); - test_int(pit.sources[0], 0); - test_int(pit.sources[1], foo); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - test_int(ptr[1].value, e4); - - Position *pptr = ecs_field(&pit, Position, 2); - test_int(pptr->x, 10); - test_int(pptr->y, 20); - } - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 1); - test_int(pit.entities[0], e5); - test_int(pit.ids[0], ecs_id(Self)); - test_int(pit.ids[1], ecs_id(Position)); - test_int(pit.sources[0], 0); - test_int(pit.sources[1], foo); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - - Position *pptr = ecs_field(&pit, Position, 2); - test_int(pptr->x, 10); - test_int(pptr->y, 20); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_worker_iter_w_shared_comp(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t foo = ecs_set(world, 0, Position, {10, 20}); - - ecs_entity_t e1 = ecs_new_id(world); ecs_set(world, e1, Self, {e1}); - ecs_entity_t e2 = ecs_new_id(world); ecs_set(world, e2, Self, {e2}); - ecs_entity_t e3 = ecs_new_id(world); ecs_set(world, e3, Self, {e3}); - ecs_entity_t e4 = ecs_new_id(world); ecs_set(world, e4, Self, {e4}); - ecs_entity_t e5 = ecs_new_id(world); ecs_set(world, e5, Self, {e5}); - - ecs_add(world, e3, TagA); - ecs_add(world, e4, TagA); - ecs_add(world, e5, TagB); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self) }, { ecs_id(Position), .src.id = foo} }, - .instanced = true - }); - - ecs_iter_t it_1 = ecs_filter_iter(world, &f); - ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 2); - ecs_iter_t it_2 = ecs_filter_iter(world, &f); - ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 2); - - /* Iter 1 */ - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e1); - test_int(pit_1.ids[0], ecs_id(Self)); - test_int(pit_1.ids[1], ecs_id(Position)); - test_int(pit_1.sources[0], 0); - test_int(pit_1.sources[1], foo); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e1); - - Position *pptr = ecs_field(&pit_1, Position, 2); - test_int(pptr->x, 10); - test_int(pptr->y, 20); - } - - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e3); - test_int(pit_1.ids[0], ecs_id(Self)); - test_int(pit_1.ids[1], ecs_id(Position)); - test_int(pit_1.sources[0], 0); - test_int(pit_1.sources[1], foo); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e3); - - Position *pptr = ecs_field(&pit_1, Position, 2); - test_int(pptr->x, 10); - test_int(pptr->y, 20); - } - - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e5); - test_int(pit_1.ids[0], ecs_id(Self)); - test_int(pit_1.ids[1], ecs_id(Position)); - test_int(pit_1.sources[0], 0); - test_int(pit_1.sources[1], foo); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e5); - - Position *pptr = ecs_field(&pit_1, Position, 2); - test_int(pptr->x, 10); - test_int(pptr->y, 20); - } - - test_bool(ecs_worker_next(&pit_1), false); - - /* Iter 2 */ - { - test_bool(ecs_worker_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e2); - test_int(pit_2.ids[0], ecs_id(Self)); - test_int(pit_2.ids[1], ecs_id(Position)); - test_int(pit_2.sources[0], 0); - test_int(pit_2.sources[1], foo); - - Self *ptr = ecs_field(&pit_2, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e2); - - Position *pptr = ecs_field(&pit_2, Position, 2); - test_int(pptr->x, 10); - test_int(pptr->y, 20); - } - - { - test_bool(ecs_worker_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e4); - test_int(pit_2.ids[0], ecs_id(Self)); - test_int(pit_2.ids[1], ecs_id(Position)); - test_int(pit_2.sources[0], 0); - test_int(pit_2.sources[1], foo); - - Self *ptr = ecs_field(&pit_2, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, e4); - - Position *pptr = ecs_field(&pit_2, Position, 2); - test_int(pptr->x, 10); - test_int(pptr->y, 20); - } - - test_bool(ecs_worker_next(&pit_2), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_paged_iter_w_task_query(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - - ecs_entity_t foo = ecs_new_id(world); ecs_set(world, foo, Self, {foo}); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self), .src.id = foo }} - }); - - ecs_iter_t it = ecs_filter_iter(world, &f); - ecs_iter_t pit = ecs_page_iter(&it, 0, 0); - - { - test_bool(ecs_page_next(&pit), true); - test_int(pit.count, 0); - test_int(pit.ids[0], ecs_id(Self)); - test_int(pit.sources[0], foo); - - Self *ptr = ecs_field(&pit, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, foo); - } - - test_bool(ecs_page_next(&pit), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_worker_iter_w_task_query(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - - ecs_entity_t foo = ecs_new_id(world); ecs_set(world, foo, Self, {foo}); - - ecs_filter_t f = ECS_FILTER_INIT; - ecs_filter_init(world, &(ecs_filter_desc_t){ - .storage = &f, - .terms = {{ ecs_id(Self), .src.id = foo }} - }); - - ecs_iter_t it_1 = ecs_filter_iter(world, &f); - ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 2); - ecs_iter_t it_2 = ecs_filter_iter(world, &f); - ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 2); - - /* Iter 1 */ - { - test_bool(ecs_worker_next(&pit_1), true); - test_int(pit_1.count, 0); - test_int(pit_1.ids[0], ecs_id(Self)); - test_int(pit_1.sources[0], foo); - - Self *ptr = ecs_field(&pit_1, Self, 1); - test_assert(ptr != NULL); - test_int(ptr[0].value, foo); - } - - test_bool(ecs_worker_next(&pit_1), false); - - /* Iter 2 */ - - test_bool(ecs_worker_next(&pit_2), false); - - ecs_filter_fini(&f); - - ecs_fini(world); -} - -void Iter_worker_iter_w_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Singleton); - ECS_COMPONENT(world, Position); - - ecs_singleton_add(world, Singleton); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_new(world, "Position, Singleton($)"); - - Position *p; - - ecs_iter_t it_1 = ecs_query_iter(world, q); - ecs_iter_t wit_1 = ecs_worker_iter(&it_1, 0, 2); - test_bool(ecs_worker_next(&wit_1), true); - test_int(wit_1.count, 2); - test_int(wit_1.entities[0], e1); - test_int(wit_1.entities[1], e2); - p = ecs_field(&wit_1, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - - ecs_iter_t it_2 = ecs_query_iter(world, q); - ecs_iter_t wit_2 = ecs_worker_iter(&it_2, 1, 2); - test_bool(ecs_worker_next(&wit_2), true); - test_int(wit_2.count, 2); - test_int(wit_2.entities[0], e3); - test_int(wit_2.entities[1], e4); - p = ecs_field(&wit_2, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_int(p[1].x, 40); - test_int(p[1].y, 50); - - test_bool(ecs_worker_next(&wit_2), false); - test_bool(ecs_worker_next(&wit_1), false); - - ecs_fini(world); -} - -void Iter_worker_iter_w_singleton_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity($)"); - - Position *p; - Velocity *v; - - ecs_iter_t it_1 = ecs_query_iter(world, q); - ecs_iter_t wit_1 = ecs_worker_iter(&it_1, 0, 2); - test_bool(ecs_worker_next(&wit_1), true); - test_int(wit_1.count, 1); - test_int(wit_1.entities[0], e1); - p = ecs_field(&wit_1, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - v = ecs_field(&wit_1, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - - test_bool(ecs_worker_next(&wit_1), true); - test_int(wit_1.count, 1); - test_int(wit_1.entities[0], e2); - p = ecs_field(&wit_1, Position, 1); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - v = ecs_field(&wit_1, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - - test_bool(ecs_worker_next(&wit_1), false); - - ecs_iter_t it_2 = ecs_query_iter(world, q); - ecs_iter_t wit_2 = ecs_worker_iter(&it_2, 1, 2); - test_bool(ecs_worker_next(&wit_2), true); - test_int(wit_2.count, 1); - test_int(wit_2.entities[0], e3); - p = ecs_field(&wit_2, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - v = ecs_field(&wit_2, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - - test_bool(ecs_worker_next(&wit_2), true); - test_int(wit_2.count, 1); - test_int(wit_2.entities[0], e4); - p = ecs_field(&wit_2, Position, 1); - test_int(p[0].x, 40); - test_int(p[0].y, 50); - v = ecs_field(&wit_2, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - - test_bool(ecs_worker_next(&wit_2), false); - - ecs_fini(world); -} - -void Iter_worker_iter_w_singleton_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Singleton); - ECS_COMPONENT(world, Position); - - ecs_singleton_add(world, Singleton); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter = { - .expr = "Position, Singleton($)", - .instanced = true - } - }); - - Position *p; - - ecs_iter_t it_1 = ecs_query_iter(world, q); - ecs_iter_t wit_1 = ecs_worker_iter(&it_1, 0, 2); - test_bool(ecs_worker_next(&wit_1), true); - test_int(wit_1.count, 2); - test_int(wit_1.entities[0], e1); - test_int(wit_1.entities[1], e2); - p = ecs_field(&wit_1, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_bool(ecs_worker_next(&wit_1), false); - - ecs_iter_t it_2 = ecs_query_iter(world, q); - ecs_iter_t wit_2 = ecs_worker_iter(&it_2, 1, 2); - test_bool(ecs_worker_next(&wit_2), true); - test_int(wit_2.count, 2); - test_int(wit_2.entities[0], e3); - test_int(wit_2.entities[1], e4); - p = ecs_field(&wit_2, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_int(p[1].x, 40); - test_int(p[1].y, 50); - test_bool(ecs_worker_next(&wit_2), false); - - ecs_fini(world); -} - -void Iter_worker_iter_w_singleton_component_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter = { - .expr = "Position, Velocity($)", - .instanced = true - } - }); - - Position *p; - Velocity *v; - - ecs_iter_t it_1 = ecs_query_iter(world, q); - ecs_iter_t wit_1 = ecs_worker_iter(&it_1, 0, 2); - test_bool(ecs_worker_next(&wit_1), true); - test_int(wit_1.count, 2); - test_int(wit_1.entities[0], e1); - test_int(wit_1.entities[1], e2); - p = ecs_field(&wit_1, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - v = ecs_field(&wit_1, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(ecs_worker_next(&wit_1), false); - - ecs_iter_t it_2 = ecs_query_iter(world, q); - ecs_iter_t wit_2 = ecs_worker_iter(&it_2, 1, 2); - test_bool(ecs_worker_next(&wit_2), true); - test_int(wit_2.count, 2); - test_int(wit_2.entities[0], e3); - test_int(wit_2.entities[1], e4); - p = ecs_field(&wit_2, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_int(p[1].x, 40); - test_int(p[1].y, 50); - v = ecs_field(&wit_2, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(ecs_worker_next(&wit_2), false); - - ecs_fini(world); -} - -void Iter_paged_iter_w_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Singleton); - ECS_COMPONENT(world, Position); - - ecs_singleton_add(world, Singleton); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_new(world, "Position, Singleton($)"); - - Position *p; - - ecs_iter_t it_1 = ecs_query_iter(world, q); - ecs_iter_t pit_1 = ecs_page_iter(&it_1, 0, 2); - test_bool(ecs_page_next(&pit_1), true); - test_int(pit_1.count, 2); - test_int(pit_1.entities[0], e1); - test_int(pit_1.entities[1], e2); - p = ecs_field(&pit_1, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_bool(ecs_page_next(&pit_1), false); - - ecs_iter_t it_2 = ecs_query_iter(world, q); - ecs_iter_t pit_2 = ecs_page_iter(&it_2, 2, 2); - test_bool(ecs_page_next(&pit_2), true); - test_int(pit_2.count, 2); - test_int(pit_2.entities[0], e3); - test_int(pit_2.entities[1], e4); - p = ecs_field(&pit_2, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_int(p[1].x, 40); - test_int(p[1].y, 50); - test_bool(ecs_page_next(&pit_2), false); - - ecs_fini(world); -} - -void Iter_paged_iter_w_singleton_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity($)"); - - Position *p; - Velocity *v; - - ecs_iter_t it_1 = ecs_query_iter(world, q); - ecs_iter_t pit_1 = ecs_page_iter(&it_1, 0, 2); - test_bool(ecs_page_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e1); - p = ecs_field(&pit_1, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - v = ecs_field(&pit_1, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(ecs_page_next(&pit_1), true); - test_int(pit_1.count, 1); - test_int(pit_1.entities[0], e2); - p = ecs_field(&pit_1, Position, 1); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - v = ecs_field(&pit_1, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(ecs_page_next(&pit_1), false); - - ecs_iter_t it_2 = ecs_query_iter(world, q); - ecs_iter_t pit_2 = ecs_page_iter(&it_2, 2, 2); - test_bool(ecs_page_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e3); - p = ecs_field(&pit_2, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - v = ecs_field(&pit_2, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(ecs_page_next(&pit_2), true); - test_int(pit_2.count, 1); - test_int(pit_2.entities[0], e4); - p = ecs_field(&pit_2, Position, 1); - test_int(p[0].x, 40); - test_int(p[0].y, 50); - v = ecs_field(&pit_2, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(ecs_page_next(&pit_2), false); - - ecs_fini(world); -} - -void Iter_paged_iter_w_singleton_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Singleton); - ECS_COMPONENT(world, Position); - - ecs_singleton_add(world, Singleton); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter = { - .expr = "Position, Singleton($)", - .instanced = true - } - }); - - Position *p; - - ecs_iter_t it_1 = ecs_query_iter(world, q); - ecs_iter_t pit_1 = ecs_page_iter(&it_1, 0, 2); - test_bool(ecs_page_next(&pit_1), true); - test_int(pit_1.count, 2); - test_int(pit_1.entities[0], e1); - test_int(pit_1.entities[1], e2); - p = ecs_field(&pit_1, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_bool(ecs_page_next(&pit_1), false); - - ecs_iter_t it_2 = ecs_query_iter(world, q); - ecs_iter_t pit_2 = ecs_page_iter(&it_2, 2, 2); - test_bool(ecs_page_next(&pit_2), true); - test_int(pit_2.count, 2); - test_int(pit_2.entities[0], e3); - test_int(pit_2.entities[1], e4); - p = ecs_field(&pit_2, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_int(p[1].x, 40); - test_int(p[1].y, 50); - test_bool(ecs_page_next(&pit_2), false); - - ecs_fini(world); -} - -void Iter_paged_iter_w_singleton_component_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter = { - .expr = "Position, Velocity($)", - .instanced = true - } - }); - - Position *p; - Velocity *v; - - ecs_iter_t it_1 = ecs_query_iter(world, q); - ecs_iter_t pit_1 = ecs_page_iter(&it_1, 0, 2); - test_bool(ecs_page_next(&pit_1), true); - test_int(pit_1.count, 2); - test_int(pit_1.entities[0], e1); - test_int(pit_1.entities[1], e2); - p = ecs_field(&pit_1, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - v = ecs_field(&pit_1, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(ecs_page_next(&pit_1), false); - - ecs_iter_t it_2 = ecs_query_iter(world, q); - ecs_iter_t pit_2 = ecs_page_iter(&it_2, 2, 2); - test_bool(ecs_page_next(&pit_2), true); - test_int(pit_2.count, 2); - test_int(pit_2.entities[0], e3); - test_int(pit_2.entities[1], e4); - p = ecs_field(&pit_2, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_int(p[1].x, 40); - test_int(p[1].y, 50); - v = ecs_field(&pit_2, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(ecs_page_next(&pit_2), false); - - ecs_fini(world); -} - -void Iter_count(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - for (int i = 0; i < 500; i ++) { - ecs_entity_t e = ecs_new(world, TagA); - if (!(i % 2)) { - ecs_add(world, e, TagB); - } - if (!(i % 3)) { - ecs_add(world, e, TagC); - } - } - - ecs_query_t *q = ecs_query_new(world, "TagA"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_int(500, ecs_iter_count(&it)); - - ecs_fini(world); -} - -void Iter_interleaved_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ TagA }} - }); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - - /* Bit of whitebox testing, check whether the stack cursor is restored to - * its original position after the 2nd iterator is done */ - ecs_iter_t it_1 = ecs_filter_iter(world, f); - ecs_stack_cursor_t cursor = *it_1.priv.cache.stack_cursor; - ecs_iter_t it_2 = ecs_filter_iter(world, f); - - test_bool(true, ecs_filter_next(&it_1)); - test_bool(true, ecs_filter_next(&it_2)); - test_int(e1, it_1.entities[0]); - test_int(e1, it_2.entities[0]); - - test_bool(true, ecs_filter_next(&it_1)); - test_bool(true, ecs_filter_next(&it_2)); - test_int(e2, it_1.entities[0]); - test_int(e2, it_2.entities[0]); - - test_bool(false, ecs_filter_next(&it_1)); - test_bool(false, ecs_filter_next(&it_2)); - - it_1 = ecs_filter_iter(world, f); - test_assert(it_1.priv.cache.stack_cursor->page == cursor.page); - test_assert(it_1.priv.cache.stack_cursor->sp == cursor.sp); - ecs_iter_fini(&it_1); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_iter_restore_stack_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ ecs_id(Position) }} - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_stack_cursor_t cursor = *it.priv.cache.stack_cursor; - ecs_iter_fini(&it); - - it = ecs_filter_iter(world, f); - test_assert(it.priv.cache.stack_cursor->page == cursor.page); - test_assert(it.priv.cache.stack_cursor->sp == cursor.sp); - ecs_iter_fini(&it); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_get_first(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new(world, Tag); - ecs_new(world, Tag); - ecs_new(world, Tag); - - ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ - .terms = {{ Tag }} - }); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_entity_t first = ecs_iter_first(&it); - test_assert(first == e); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_page_iter_w_only_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ Tag }} - }); - - ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_t pit = ecs_page_iter(&it, 1, 0); - - test_assert(ecs_page_next(&pit)); - test_int(pit.count, 1); - test_uint(pit.entities[0], e2); - test_assert(!ecs_page_next(&pit)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_worker_iter_w_only_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ Tag }} - }); - - ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_t pit = ecs_worker_iter(&it, 1, 2); - - test_assert(ecs_worker_next(&pit)); - test_int(pit.count, 1); - test_uint(pit.entities[0], e2); - test_assert(!ecs_worker_next(&pit)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_page_iter_w_inout_none(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ ecs_id(Position), .inout = EcsInOutNone }} - }); - - ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_t pit = ecs_page_iter(&it, 1, 0); - - test_assert(ecs_page_next(&pit)); - test_int(pit.count, 1); - test_uint(pit.entities[0], e2); - test_assert(!ecs_page_next(&pit)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_worker_iter_w_inout_none(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ ecs_id(Position), .inout = EcsInOutNone }} - }); - - ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_t pit = ecs_worker_iter(&it, 1, 2); - - test_assert(ecs_worker_next(&pit)); - test_int(pit.count, 1); - test_uint(pit.entities[0], e2); - test_assert(!ecs_worker_next(&pit)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_page_iter_w_ctx(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ TagA }} - }); - - ecs_new(world, TagA); - - int ctx; - - ecs_iter_t it = ecs_filter_iter(world, f); - it.ctx = &ctx; - - ecs_iter_t pit = ecs_page_iter(&it, 0, 1); - test_assert(pit.ctx == &ctx); - - test_assert(ecs_page_next(&pit)); - test_assert(!ecs_page_next(&pit)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_page_iter_w_binding_ctx(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ TagA }} - }); - - ecs_new(world, TagA); - - int ctx; - - ecs_iter_t it = ecs_filter_iter(world, f); - it.binding_ctx = &ctx; - - ecs_iter_t pit = ecs_page_iter(&it, 0, 1); - test_assert(pit.binding_ctx == &ctx); - - test_assert(ecs_page_next(&pit)); - test_assert(!ecs_page_next(&pit)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_worker_iter_w_ctx(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ TagA }} - }); - - ecs_new(world, TagA); - - int ctx; - - ecs_iter_t it = ecs_filter_iter(world, f); - it.ctx = &ctx; - - ecs_iter_t pit = ecs_worker_iter(&it, 0, 2); - test_assert(pit.ctx == &ctx); - - test_assert(ecs_worker_next(&pit)); - test_assert(!ecs_worker_next(&pit)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_worker_iter_w_binding_ctx(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ TagA }} - }); - - ecs_new(world, TagA); - - int ctx; - - ecs_iter_t it = ecs_filter_iter(world, f); - it.binding_ctx = &ctx; - - ecs_iter_t pit = ecs_worker_iter(&it, 0, 2); - test_assert(pit.binding_ctx == &ctx); - - test_assert(ecs_worker_next(&pit)); - test_assert(!ecs_worker_next(&pit)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_column_index_owned(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ TagB }, { TagC }, { TagA }} - }); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add(world, e1, TagA); - ecs_add(world, e1, TagB); - ecs_add(world, e1, TagC); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e2, TagC); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_int(1, ecs_field_column_index(&it, 1)); - test_int(2, ecs_field_column_index(&it, 2)); - test_int(0, ecs_field_column_index(&it, 3)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_int(2, ecs_field_column_index(&it, 1)); - test_int(3, ecs_field_column_index(&it, 2)); - test_int(1, ecs_field_column_index(&it, 3)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_column_index_shared(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ TagB }, { TagC }, { TagA }} - }); - - ecs_entity_t base = ecs_new(world, TagA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add(world, e1, TagB); - ecs_add(world, e1, TagC); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add(world, e2, TagB); - ecs_add(world, e2, TagC); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_int(0, ecs_field_column_index(&it, 1)); - test_int(1, ecs_field_column_index(&it, 2)); - test_int(-1, ecs_field_column_index(&it, 3)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_int(1, ecs_field_column_index(&it, 1)); - test_int(2, ecs_field_column_index(&it, 2)); - test_int(-1, ecs_field_column_index(&it, 3)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_column_index_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ TagB }, { TagC }, { TagA, .oper = EcsNot }} - }); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add(world, e1, TagB); - ecs_add(world, e1, TagC); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, TagB); - ecs_add(world, e2, TagC); - - ecs_iter_t it = ecs_filter_iter(world, f); - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_int(0, ecs_field_column_index(&it, 1)); - test_int(1, ecs_field_column_index(&it, 2)); - test_int(-1, ecs_field_column_index(&it, 3)); - - test_bool(true, ecs_filter_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_int(1, ecs_field_column_index(&it, 1)); - test_int(2, ecs_field_column_index(&it, 2)); - test_int(-1, ecs_field_column_index(&it, 3)); - - test_bool(false, ecs_filter_next(&it)); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_page_iter_w_fini(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Foo); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ ecs_id(Position) }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Foo); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_t pit = ecs_page_iter(&it, 0, 2); - test_bool(true, ecs_page_next(&pit)); - test_int(pit.count, 1); - test_int(pit.entities[0], e1); - - ecs_iter_fini(&pit); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_worker_iter_w_fini(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Foo); - - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ ecs_id(Position) }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Foo); - - ecs_iter_t it = ecs_filter_iter(world, f); - ecs_iter_t pit = ecs_worker_iter(&it, 0, 2); - test_bool(true, ecs_worker_next(&pit)); - test_int(pit.count, 1); - test_int(pit.entities[0], e1); - ecs_iter_fini(&pit); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Iter_rule_page_iter_w_fini(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .terms = {{ ecs_id(Position) }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Foo); - - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_t pit = ecs_page_iter(&it, 0, 2); - test_bool(true, ecs_page_next(&pit)); - test_int(pit.count, 1); - test_int(pit.entities[0], e1); - ecs_iter_fini(&pit); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void Iter_rule_worker_iter_w_fini(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Foo); - - ecs_rule_t *r = ecs_rule(world, { - .terms = {{ ecs_id(Position) }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Foo); - - ecs_iter_t it = ecs_rule_iter(world, r); - ecs_iter_t pit = ecs_worker_iter(&it, 0, 2); - test_bool(true, ecs_worker_next(&pit)); - test_int(pit.count, 1); - test_int(pit.entities[0], e1); - ecs_iter_fini(&pit); - - ecs_rule_fini(r); - - ecs_fini(world); -} - -void Iter_to_str_before_next(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - char *str = ecs_iter_str(&it); - test_assert(str == NULL); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Iter_to_str(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new_entity(world, "foo"); - ecs_add(world, e, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - char *str = ecs_iter_str(&it); - test_assert(str != NULL); - test_str(str, - "id: Tag\n" - "src: 0\n" - "set: true\n" - "this:\n" - " - foo\n" - ); - ecs_os_free(str); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/Observer.c b/vendors/flecs/test/api/src/Observer.c deleted file mode 100644 index 7064639eb..000000000 --- a/vendors/flecs/test/api/src/Observer.c +++ /dev/null @@ -1,5023 +0,0 @@ -#include - -static -void Observer(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); -} - -static -void Observer_w_value(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - - test_int(it->count, 1); - test_assert(it->entities != NULL); - test_assert(it->entities[0] != 0); - - Position *p = ecs_field(it, Position, 1); - test_int(p->x, 10); - test_int(p->y, 20); - - if (it->field_count > 1) { - Velocity *v = ecs_field(it, Velocity, 2); - test_int(v->x, 1); - test_int(v->y, 2); - } -} - -static -void Observer_w_1_value(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - - test_int(it->count, 1); - test_assert(it->entities != NULL); - test_assert(it->entities[0] != 0); - - Position *p = ecs_field(it, Position, 1); - test_int(p->x, 10); - test_int(p->y, 20); -} - -static -void Observer_w_filter_term(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - - test_int(it->count, 1); - test_assert(it->entities != NULL); - test_assert(it->entities[0] != 0); - - /* Not guaranteed to be NULL */ - // test_assert(it->ptrs == NULL); -} - -static -void Observer_w_1_filter_term(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - - test_int(it->count, 1); - test_assert(it->entities != NULL); - test_assert(it->entities[0] != 0); - - test_assert(it->ptrs != NULL); - /* Not guaranteed to be NULL */ - // test_assert(it->ptrs[0] == NULL); - test_assert(it->ptrs[1] != NULL); - - Velocity *v = ecs_field(it, Velocity, 2); - test_int(v->x, 1); - test_int(v->y, 2); -} - -static -void Observer_w_2_filter_terms(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - - test_int(it->count, 1); - test_assert(it->entities != NULL); - test_assert(it->entities[0] != 0); - - test_assert(it->ptrs != NULL); - test_assert(it->sizes != NULL); - - /* Not guaranteed to be NULL */ - // test_assert(it->ptrs[0] == NULL); - // test_assert(it->ptrs[1] == NULL); - test_assert(it->ptrs[2] != NULL); - - Mass *m = ecs_field(it, Mass, 3); - test_int(m[0], 100); -} - -static bool dummy_called = false; - -static -void Dummy(ecs_iter_t *it) { - dummy_called = true; -} - -void Observer_2_terms_w_on_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagB); - - ecs_fini(world); -} - -void Observer_2_terms_w_on_remove(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB}}, - .events = {EcsOnRemove}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_remove_id(world, e, TagA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnRemove); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagB); - - ecs_fini(world); -} - -void Observer_2_terms_w_on_set_value(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_id(Position)}, {ecs_id(Velocity)}}, - .events = {EcsOnSet}, - .callback = Observer_w_value, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_set(world, e, Velocity, {1, 2}); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Position, {10, 20}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.c[0][1], ecs_id(Velocity)); - - ecs_fini(world); -} - -void Observer_2_terms_w_on_remove_value(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_id(Position)}, {ecs_id(Velocity)}}, - .events = {EcsOnRemove}, - .callback = Observer_w_value, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_set(world, e, Velocity, {1, 2}); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Position, {10, 20}); - test_int(ctx.invoked, 0); - - ecs_remove(world, e, Position); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnRemove); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.c[0][1], ecs_id(Velocity)); - - ecs_fini(world); -} - - -void Observer_2_terms_w_on_add_2nd(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagB); - - ecs_fini(world); -} - -void Observer_2_terms_w_on_remove_2nd(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB}}, - .events = {EcsOnRemove}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_remove_id(world, e, TagB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnRemove); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagB); - - ecs_fini(world); -} - -void Observer_2_pair_terms_w_on_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_id_t pair_a = ecs_pair(RelA, ObjA); - ecs_id_t pair_b = ecs_pair(RelB, ObjB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{pair_a}, {pair_b}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_pair(world, e, RelA, ObjA); - test_int(ctx.invoked, 0); - - ecs_add_pair(world, e, RelB, ObjB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], pair_a); - test_int(ctx.c[0][1], pair_b); - - ecs_fini(world); -} - -void Observer_2_pair_terms_w_on_remove(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_id_t pair_a = ecs_pair(RelA, ObjA); - ecs_id_t pair_b = ecs_pair(RelB, ObjB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{pair_a}, {pair_b}}, - .events = {EcsOnRemove}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_pair(world, e, RelA, ObjA); - test_int(ctx.invoked, 0); - - ecs_add_pair(world, e, RelB, ObjB); - test_int(ctx.invoked, 0); - - ecs_remove_pair(world, e, RelB, ObjB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnRemove); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], pair_a); - test_int(ctx.c[0][1], pair_b); - - ecs_fini(world); -} - -void Observer_2_wildcard_pair_terms_w_on_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_pair(world, e, RelA, ObjA); - test_int(ctx.invoked, 0); - - ecs_add_pair(world, e, RelB, ObjB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); - test_int(ctx.c[0][1], ecs_pair(RelB, ObjB)); - - ecs_fini(world); -} - -void Observer_2_wildcard_pair_terms_w_on_add_2_matching(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_pair(world, e, RelA, ObjA); - test_int(ctx.invoked, 0); - - ecs_add_pair(world, e, RelB, ObjA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); - test_int(ctx.c[0][1], ecs_pair(RelB, ObjA)); - - ecs_os_zeromem(&ctx); - ecs_add_pair(world, e, RelB, ObjB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); - test_int(ctx.c[0][1], ecs_pair(RelB, ObjB)); - - ecs_fini(world); -} - -void Observer_2_wildcard_pair_terms_w_on_add_3_matching(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - ECS_TAG(world, ObjC); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_pair(world, e, RelA, ObjA); - test_int(ctx.invoked, 0); - - ecs_add_pair(world, e, RelB, ObjA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); - test_int(ctx.c[0][1], ecs_pair(RelB, ObjA)); - - ecs_os_zeromem(&ctx); - ecs_add_pair(world, e, RelB, ObjC); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); - test_int(ctx.c[0][1], ecs_pair(RelB, ObjC)); - - ecs_os_zeromem(&ctx); - ecs_add_pair(world, e, RelB, ObjB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); - test_int(ctx.c[0][1], ecs_pair(RelB, ObjB)); - - ecs_fini(world); -} - - -void Observer_2_wildcard_pair_terms_w_on_remove(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}}, - .events = {EcsOnRemove}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_pair(world, e, RelA, ObjA); - test_int(ctx.invoked, 0); - - ecs_add_pair(world, e, RelB, ObjB); - test_int(ctx.invoked, 0); - - ecs_remove_pair(world, e, RelB, ObjB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnRemove); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); - test_int(ctx.c[0][1], ecs_pair(RelB, ObjB)); - - ecs_fini(world); -} - -void Observer_on_set_n_entities(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Self); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_id(Self)}}, - .events = {EcsOnSet}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e_1 = ecs_new_id(world); - test_int(ctx.invoked, 0); - ecs_set(world, e_1, Self, {e_1}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], e_1); - test_int(ctx.c[0][0], ecs_id(Self)); - - ecs_os_zeromem(&ctx); - - ecs_entity_t e_2 = ecs_new_id(world); - test_int(ctx.invoked, 0); - ecs_set(world, e_2, Self, {e_2}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], e_2); - test_int(ctx.c[0][0], ecs_id(Self)); - - ecs_os_zeromem(&ctx); - - ecs_entity_t e_3 = ecs_new_id(world); - test_int(ctx.invoked, 0); - ecs_set(world, e_3, Self, {e_3}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], e_3); - test_int(ctx.c[0][0], ecs_id(Self)); - - ecs_fini(world); -} - -void Observer_on_set_n_entities_2_comp(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Obj); - ECS_COMPONENT(world, Self); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_id(Self)}, {ecs_pair(ecs_id(Self), EcsWildcard)}}, - .events = {EcsOnSet}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e_1 = ecs_new_id(world); - ecs_set(world, e_1, Self, {e_1}); - test_int(ctx.invoked, 0); - ecs_set_pair(world, e_1, Self, Obj, {e_1}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 2); - test_null(ctx.param); - test_int(ctx.e[0], e_1); - test_int(ctx.c[0][0], ecs_id(Self)); - test_int(ctx.c[0][1], ecs_pair(ecs_id(Self), Obj)); - - ecs_os_zeromem(&ctx); - - ecs_entity_t e_2 = ecs_new_id(world); - ecs_set(world, e_2, Self, {e_2}); - test_int(ctx.invoked, 0); - ecs_set_pair(world, e_2, Self, Obj, {e_2}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 2); - test_null(ctx.param); - test_int(ctx.e[0], e_2); - test_int(ctx.c[0][0], ecs_id(Self)); - test_int(ctx.c[0][1], ecs_pair(ecs_id(Self), Obj)); - - ecs_fini(world); -} - -void Observer_wildcard_pair_w_pred_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - ECS_COMPONENT(world, Position); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_pair(ecs_id(Position), EcsWildcard)}}, - .events = {EcsOnSet}, - .callback = Observer_w_value, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - test_int(ctx.invoked, 0); - - ecs_set_pair(world, e, Position, ObjA, {10, 20}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), ObjA)); - - /* Change existing component without triggering OnSet as the callback - * expects value {10, 20}, then add a new component with {10, 20} */ - Position *p = ecs_get_mut_pair(world, e, Position, ObjA); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - p->x = 30; - p->y = 40; - - ecs_os_zeromem(&ctx); - - ecs_set_pair(world, e, Position, ObjB, {10, 20}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), ObjB)); - - ecs_fini(world); -} - -void Observer_wildcard_pair_w_obj_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_COMPONENT(world, Position); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ ecs_pair(EcsWildcard, ecs_id(Position)) }}, - .events = {EcsOnSet}, - .callback = Observer_w_value, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - test_int(ctx.invoked, 0); - - ecs_set_pair_object(world, e, RelA, Position, {10, 20}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(RelA, ecs_id(Position))); - - /* Change existing component without triggering OnSet as the callback - * expects value {10, 20}, then add a new component with {10, 20} */ - Position *p = ecs_get_mut_pair_second(world, e, RelA, Position); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - p->x = 30; - p->y = 40; - - ecs_os_zeromem(&ctx); - - ecs_set_pair_object(world, e, RelB, Position, {10, 20}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_pair(RelB, ecs_id(Position))); - - ecs_fini(world); -} - -void Observer_2_terms_1_not_w_on_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB, .oper = EcsNot}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_clear(world, e); - test_int(ctx.invoked, 0); - test_assert(!ecs_has(world, e, TagB)); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagB); - - ecs_fini(world); -} - -void Observer_2_terms_1_not_w_on_remove(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB, .oper = EcsNot}}, - .events = {EcsOnRemove}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_remove_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_clear(world, e); - test_assert(!ecs_has(world, e, TagB)); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_remove_id(world, e, TagA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnRemove); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagB); - - ecs_fini(world); -} - -void Observer_2_terms_w_on_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_id(Position)}, {TagB}}, - .events = {EcsOnSet}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_add(world, e, Position); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Position, {10, 20}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnSet); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.c[0][1], TagB); - - ecs_fini(world); -} - -void Observer_2_terms_w_un_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ecs_id(Position)}, {TagB}}, - .events = {EcsUnSet}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_add(world, e, Position); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Position, {10, 20}); - test_int(ctx.invoked, 0); - - ecs_remove(world, e, Position); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsUnSet); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.c[0][1], TagB); - - ecs_fini(world); -} - -void Observer_3_terms_2_or_on_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB, .oper = EcsOr}, {TagC }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagB); - - ecs_os_zeromem(&ctx); - - ecs_add_id(world, e, TagC); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagC); - - ecs_fini(world); -} - -void Observer_3_terms_2_or_on_remove(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB, .oper = EcsOr}, {TagC }}, - .events = {EcsOnRemove}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagC); - test_int(ctx.invoked, 0); - - ecs_remove_id(world, e, TagC); - test_int(ctx.invoked, 1); - - ecs_remove_id(world, e, TagB); - test_int(ctx.invoked, 2); - - ecs_remove_id(world, e, TagA); - test_int(ctx.invoked, 2); - - ecs_fini(world); -} - -void Observer_2_terms_w_from_entity_on_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t x = ecs_new_id(world); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB, .src.id = x}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_clear(world, e); - test_int(ctx.invoked, 0); - - ecs_add_id(world, x, TagB); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 1); - - ecs_fini(world); -} - -void Observer_2_terms_on_remove_on_clear(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB}}, - .events = {EcsOnRemove}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_clear(world, e); - test_int(ctx.invoked, 1); - - ecs_fini(world); -} - -void Observer_2_terms_on_remove_on_delete(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{TagA}, {TagB}}, - .events = {EcsOnRemove}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_delete(world, e); - test_int(ctx.invoked, 1); - - ecs_fini(world); -} - -void Observer_add_after_delete_observer(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{.id = ecs_id(Position) }}, - .events = {EcsOnAdd}, - .callback = Dummy - }); - - ecs_entity_t e1 = ecs_new(world, Position); - test_assert(e1 != 0); - test_assert(ecs_has(world, e1, Position)); - test_int(dummy_called, 1); - - dummy_called = 0; - - ecs_delete(world, observer); - test_int(dummy_called, 0); - - ecs_entity_t e2 = ecs_new(world, Position); - test_assert(e2 != 0); - test_assert(ecs_has(world, e2, Position)); - test_int(dummy_called, 0); - - ecs_fini(world); -} - -void Observer_remove_after_delete_observer(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{.id = ecs_id(Position) }}, - .events = {EcsOnRemove}, - .callback = Dummy - }); - - ecs_entity_t e1 = ecs_new(world, Position); - test_assert(e1 != 0); - test_assert(ecs_has(world, e1, Position)); - test_int(dummy_called, 0); - - ecs_remove(world, e1, Position); - test_int(dummy_called, 1); - - dummy_called = 0; - - ecs_delete(world, observer); - test_int(dummy_called, 0); - - ecs_entity_t e2 = ecs_new(world, Position); - test_assert(e2 != 0); - test_assert(ecs_has(world, e2, Position)); - test_int(dummy_called, 0); - - ecs_remove(world, e2, Position); - test_int(dummy_called, 0); - - ecs_fini(world); -} - -static int ctx_value; -static -void ctx_free(void *ctx) { - test_assert(&ctx_value == ctx); - ctx_value ++; -} - -static int binding_ctx_value; -static -void binding_ctx_free(void *ctx) { - test_assert(&binding_ctx_value == ctx); - binding_ctx_value ++; -} - -static int ctx_value_2; -static -void ctx_free_2(void *ctx) { - test_assert(&ctx_value_2 == ctx); - ctx_value_2 ++; -} - -static int binding_ctx_value_2; -static -void binding_ctx_free_2(void *ctx) { - test_assert(&binding_ctx_value_2 == ctx); - binding_ctx_value_2 ++; -} - -void Observer_delete_observer_w_ctx(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ Tag }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx_value, - .ctx_free = ctx_free, - .binding_ctx = &binding_ctx_value, - .binding_ctx_free = binding_ctx_free - }); - test_assert(o != 0); - - test_assert(ecs_observer_get_ctx(world, o) == &ctx_value); - test_assert(ecs_observer_get_binding_ctx(world, o) == &binding_ctx_value); - - ecs_delete(world, o); - - test_int(ctx_value, 1); - test_int(binding_ctx_value, 1); - - ecs_fini(world); -} - -void Observer_update_ctx(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_entity_t system = ecs_observer(world, { - .filter.terms = {{.id = Tag}}, - .callback = Dummy, - .events = {EcsOnAdd}, - .ctx = &ctx_value, - .ctx_free = ctx_free, - .binding_ctx = &binding_ctx_value, - .binding_ctx_free = binding_ctx_free - }); - test_assert(system != 0); - - test_assert(ecs_observer_get_ctx(world, system) == &ctx_value); - test_assert(ecs_observer_get_binding_ctx(world, system) - == &binding_ctx_value); - - ecs_observer(world, { - .entity = system, - .ctx = &ctx_value, - .ctx_free = ctx_free, - .binding_ctx = &binding_ctx_value, - .binding_ctx_free = binding_ctx_free - }); - - test_int(ctx_value, 0); - test_int(binding_ctx_value, 0); - test_int(ctx_value_2, 0); - test_int(binding_ctx_value_2, 0); - - ecs_observer(world, { - .entity = system, - .ctx = &ctx_value_2, - .ctx_free = ctx_free_2, - .binding_ctx = &binding_ctx_value_2, - .binding_ctx_free = binding_ctx_free_2 - }); - - test_int(ctx_value, 1); - test_int(binding_ctx_value, 1); - test_int(ctx_value_2, 0); - test_int(binding_ctx_value_2, 0); - - ecs_delete(world, system); - - test_int(ctx_value, 1); - test_int(binding_ctx_value, 1); - test_int(ctx_value_2, 1); - test_int(binding_ctx_value_2, 1); - - ecs_fini(world); -} - -void Observer_filter_w_strings(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{.first.name = "TagA"}, {.first.name = "TagB"}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(o != 0); - - ecs_entity_t e = ecs_new_id(world); - - ecs_add_id(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e, TagA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagB); - - ecs_fini(world); -} - -static ecs_table_t *trigger_table; - -void TypeObserver(ecs_iter_t *it) { - trigger_table = it->table; -} - -void Observer_iter_type_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ Tag }}, - .events = {EcsOnAdd}, - .callback = TypeObserver, - .ctx = &ctx - }); - test_assert(t != 0); - - ecs_new(world, Tag); - - test_assert(trigger_table != NULL); - test_assert(trigger_table != NULL); - const ecs_type_t *type = ecs_table_get_type(trigger_table); - test_int(type->count, 1); - test_int(type->array[0], Tag); - - ecs_fini(world); -} - -void ObserverReadonly(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - test_bool(ecs_field_is_readonly(it, 1), true); -} - -void Observer_readonly_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA, .inout = EcsIn }}, - .events = {EcsOnAdd}, - .callback = ObserverReadonly, - .ctx = &ctx - }); - - ecs_entity_t e = ecs_new_id(world); - test_assert(e != 0); - ecs_add(world, e, TagA); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.event_id, TagA); - test_int(ctx.term_count, 1); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], TagA); - - ecs_fini(world); -} - -void Observer_trigger_on_prefab(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - Probe ctx_1 = {0}; - Probe ctx_2 = {0}; - - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx_1 - }); - - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }, { EcsPrefab, .oper = EcsOptional}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx_2 - }); - - ecs_new(world, TagA); - test_int(ctx_1.invoked, 1); - test_int(ctx_2.invoked, 1); - ecs_os_zeromem(&ctx_1); - ecs_os_zeromem(&ctx_2); - - ecs_entity_t e = ecs_new_w_id(world, EcsPrefab); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - ecs_add(world, e, TagA); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 1); - ecs_os_zeromem(&ctx_2); - - e = ecs_new_w_id(world, EcsDisabled); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - ecs_add(world, e, TagA); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_fini(world); -} - -void Observer_trigger_on_disabled(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - Probe ctx_1 = {0}; - Probe ctx_2 = {0}; - - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx_1 - }); - - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }, { EcsDisabled, .oper = EcsOptional}}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx_2 - }); - - ecs_new(world, TagA); - test_int(ctx_1.invoked, 1); - test_int(ctx_2.invoked, 1); - ecs_os_zeromem(&ctx_1); - ecs_os_zeromem(&ctx_2); - - ecs_entity_t e = ecs_new_w_id(world, EcsPrefab); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - ecs_add(world, e, TagA); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - ecs_os_zeromem(&ctx_2); - - e = ecs_new_w_id(world, EcsDisabled); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - ecs_add(world, e, TagA); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 1); - - ecs_fini(world); -} - -static -void UnSet(ecs_iter_t *it) { - probe_iter(it); -} - -static -void UnSetA(ecs_iter_t *it) { } - -static -void UnSetB(ecs_iter_t *it) { -} - -void Observer_unset_1_of_1(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e = ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Position, {10, 20}); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Velocity, {1, 2}); - test_int(ctx.invoked, 0); - - ecs_remove(world, e, Velocity); - test_int(ctx.invoked, 0); - - ecs_remove(world, e, Position); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - - ecs_os_zeromem(&ctx); - - ecs_remove(world, e, Position); - test_int(ctx.invoked, 0); - - ecs_os_zeromem(&ctx); - - ecs_fini(world); -} - -void Observer_unset_1_of_2(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e = ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Position, {10, 20}); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Velocity, {1, 2}); - test_int(ctx.invoked, 0); - - ecs_remove(world, e, Position); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.s[0][1], 0); - - ecs_os_zeromem(&ctx); - - ecs_remove(world, e, Velocity); - test_int(ctx.invoked, 0); - - ecs_os_zeromem(&ctx); - - ecs_remove(world, e, Position); - test_int(ctx.invoked, 0); - - ecs_os_zeromem(&ctx); - - ecs_fini(world); - - test_int(ctx.invoked, 0); -} - -void Observer_unset_1_of_3(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity, Mass); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e = ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Position, {10, 20}); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Velocity, {1, 2}); - test_int(ctx.invoked, 0); - - ecs_set(world, e, Mass, {1}); - test_int(ctx.invoked, 0); - - ecs_remove(world, e, Position); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 3); - test_null(ctx.param); - - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.s[0][1], 0); - test_int(ctx.c[0][2], ecs_id(Mass)); - test_int(ctx.s[0][2], 0); - - ecs_os_zeromem(&ctx); - - ecs_remove(world, e, Mass); - test_int(ctx.invoked, 0); - - ecs_os_zeromem(&ctx); - - ecs_remove(world, e, Velocity); - test_int(ctx.invoked, 0); - - ecs_os_zeromem(&ctx); - - ecs_remove(world, e, Position); - test_int(ctx.invoked, 0); - - ecs_os_zeromem(&ctx); - - ecs_fini(world); - - test_int(ctx.invoked, 0); -} - -void Observer_unset_on_delete_1(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_entity_t e = ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_delete(world, e); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - - test_int(ctx.e[0], e); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - - ecs_fini(world); -} - -void Observer_unset_on_delete_2(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add(world, e1, Velocity); - test_int(ctx.invoked, 0); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - test_int(ctx.invoked, 0); - - ecs_entity_t e3 = ecs_new(world, Position); - ecs_add(world, e3, Velocity); - test_int(ctx.invoked, 0); - - ecs_delete(world, e3); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e3); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.s[0][1], 0); - - ecs_fini(world); -} - -void Observer_unset_on_delete_3(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity, Mass); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add(world, e1, Velocity); - ecs_add(world, e1, Mass); - test_int(ctx.invoked, 0); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - ecs_add(world, e2, Mass); - test_int(ctx.invoked, 0); - - ecs_entity_t e3 = ecs_new(world, Position); - ecs_add(world, e3, Velocity); - ecs_add(world, e3, Mass); - test_int(ctx.invoked, 0); - - ecs_delete(world, e3); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 3); - test_null(ctx.param); - - test_int(ctx.e[0], e3); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.s[0][1], 0); - test_int(ctx.c[0][2], ecs_id(Mass)); - test_int(ctx.s[0][2], 0); - - ecs_fini(world); -} - -void Observer_unset_on_fini_1(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e1 = ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_entity_t e2 = ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_entity_t e3 = ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_new(world, Velocity); - test_int(ctx.invoked, 0); - - ecs_fini(world); - - test_int(ctx.count, 3); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 1); - test_null(ctx.param); - - test_int(ctx.e[0], e3); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e1); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); -} - -void Observer_unset_on_fini_2(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add(world, e1, Velocity); - test_int(ctx.invoked, 0); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - test_int(ctx.invoked, 0); - - ecs_entity_t e3 = ecs_new(world, Position); - ecs_add(world, e3, Velocity); - test_int(ctx.invoked, 0); - - ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_new(world, Velocity); - test_int(ctx.invoked, 0); - - ecs_fini(world); - - test_int(ctx.count, 3); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e3); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e1); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.s[0][1], 0); -} - -void Observer_unset_on_fini_3(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity, Mass); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add(world, e1, Velocity); - ecs_add(world, e1, Mass); - test_int(ctx.invoked, 0); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - ecs_add(world, e2, Mass); - test_int(ctx.invoked, 0); - - ecs_entity_t e3 = ecs_new(world, Position); - ecs_add(world, e3, Velocity); - ecs_add(world, e3, Mass); - test_int(ctx.invoked, 0); - - ecs_new(world, Position); - test_int(ctx.invoked, 0); - - ecs_new(world, Velocity); - test_int(ctx.invoked, 0); - - ecs_new(world, Mass); - test_int(ctx.invoked, 0); - - ecs_fini(world); - - test_int(ctx.count, 3); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 3); - test_null(ctx.param); - - test_int(ctx.e[0], e3); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e1); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.s[0][1], 0); - test_int(ctx.c[0][2], ecs_id(Mass)); - test_int(ctx.s[0][2], 0); -} - -void Observer_overlapping_unset_systems(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ECS_OBSERVER(world, UnSetA, EcsUnSet, Position); - ECS_OBSERVER(world, UnSetB, EcsUnSet, Position, Velocity); - ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - test_int(ctx.invoked, 0); - - ecs_remove(world, e, Velocity); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, UnSet); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.s[0][1], 0); - - ecs_fini(world); -} - -static -void UnSet_TestComp(ecs_iter_t *it) { - if (!ecs_get_ctx(it->world)) { - return; - } - - probe_iter(it); - - test_int(it->count, 1); - - Position *p = ecs_field(it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); -} - -void Observer_unset_move_to_nonempty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ECS_OBSERVER(world, UnSet_TestComp, EcsUnSet, Position); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ECS_ENTITY(world, DummyA, Position, Velocity); - test_int(ctx.invoked, 0); - - ECS_ENTITY(world, DummyB, Position, Velocity); - test_int(ctx.invoked, 0); - - ECS_ENTITY(world, e, Position, Velocity); - ecs_set(world, e, Position, {10, 20}); - ecs_set(world, e, Velocity, {20, 10}); - test_int(ctx.invoked, 0); - - ecs_entity_t e2 = ecs_new(world, Velocity); - ecs_set(world, e2, Velocity, {30, 40}); - test_int(ctx.invoked, 0); - - ecs_remove(world, e, Position); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, UnSet_TestComp); - test_int(ctx.term_count, 1); - test_null(ctx.param); - - test_int(ctx.e[0], e); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - - /* Prevent system from getting called by fini */ - ecs_set_ctx(world, NULL, NULL); - - ecs_fini(world); -} - -static -void UnSet_WriteComp(ecs_iter_t *it) { - if (!ecs_get_ctx(it->world)) { - return; - } - - probe_iter(it); - - test_int(it->count, 1); - - Position *p = ecs_field(it, Position, 1); - test_assert(p != NULL); - - Velocity *v = ecs_field(it, Velocity, 2); - - test_int(p->x, 10); - test_int(p->y, 20); - - test_int(v->x, 1); - test_int(v->y, 2); - - v->x = 2; - v->y = 3; -} - -void Observer_write_in_unset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ECS_OBSERVER(world, UnSet_WriteComp, EcsUnSet, Position, Velocity); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 2}); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_remove(world, e, Position); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, UnSet_WriteComp); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e); - - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.s[0][0], 0); - - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.s[0][1], 0); - - /* Prevent system from getting called by fini */ - ecs_set_ctx(world, NULL, NULL); - - const Velocity *v = ecs_get(world, e, Velocity); - test_int(v->x, 2); - test_int(v->y, 3); - - ecs_fini(world); -} - -void Observer_filter_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ .id = ecs_id(Position), .inout = EcsInOutNone }}, - .events = {EcsOnSet}, - .callback = Observer_w_filter_term, - .ctx = &ctx - }); - - /* Create entities before trigger */ - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - test_assert(e1 != 0); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnSet); - test_int(ctx.event_id, ecs_id(Position)); - test_int(ctx.term_count, 1); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.c[0][0], ecs_id(Position)); - - ecs_fini(world); -} - -void Observer_2_terms_1_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = { - { .id = ecs_id(Position), .inout = EcsInOutNone }, - { .id = ecs_id(Velocity) } - }, - .events = {EcsOnSet}, - .callback = Observer_w_1_filter_term, - .ctx = &ctx - }); - - /* Create entities before trigger */ - ecs_entity_t e1 = ecs_set(world, 0, Velocity, {1, 2}); - test_assert(e1 != 0); - test_int(ctx.invoked, 0); - - ecs_set(world, e1, Position, {10, 20}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnSet); - test_int(ctx.event_id, ecs_id(Position)); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.c[0][1], ecs_id(Velocity)); - - ecs_fini(world); -} - -void Observer_3_terms_2_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = { - { .id = ecs_id(Position), .inout = EcsInOutNone }, - { .id = ecs_id(Velocity), .inout = EcsInOutNone }, - { .id = ecs_id(Mass) } - }, - .events = {EcsOnSet}, - .callback = Observer_w_2_filter_terms, - .ctx = &ctx - }); - - /* Create entities before trigger */ - ecs_entity_t e1 = ecs_set(world, 0, Velocity, {1, 2}); - test_assert(e1 != 0); - test_int(ctx.invoked, 0); - - ecs_set(world, e1, Mass, {100}); - ecs_set(world, e1, Position, {10, 20}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnSet); - test_int(ctx.event_id, ecs_id(Position)); - test_int(ctx.term_count, 3); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.c[0][1], ecs_id(Velocity)); - test_int(ctx.c[0][2], ecs_id(Mass)); - - ecs_fini(world); -} - -void Observer_and_from(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_PREFAB(world, Type, TagA, TagB); - - Probe ctx = {0}; - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = { - { ECS_AND | Type} - }, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t e = ecs_new(world, 0); - test_int(ctx.invoked, 0); - - ecs_add(world, e, TagA); - test_int(ctx.invoked, 0); - - ecs_add(world, e, TagB); - test_int(ctx.invoked, 1); - - ecs_remove(world, e, TagA); - test_int(ctx.invoked, 1); - - ecs_add(world, e, TagA); - test_int(ctx.invoked, 2); - - ecs_fini(world); -} - -void Observer_or_from(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_PREFAB(world, Type, TagA, TagB); - - Probe ctx = {0}; - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = { - { Type, .oper = EcsOrFrom } - }, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t e = ecs_new(world, 0); - test_int(ctx.invoked, 0); - - ecs_add(world, e, TagA); - test_int(ctx.invoked, 1); - - ecs_add(world, e, TagB); - test_int(ctx.invoked, 2); - - ecs_remove(world, e, TagA); - test_int(ctx.invoked, 2); - - ecs_add(world, e, TagA); - test_int(ctx.invoked, 3); - - ecs_fini(world); -} - -static int invoke_count = 0; -static ecs_entity_t base_ent = 0; -static ecs_entity_t inst_ent_a = 0; -static ecs_entity_t inst_ent_b = 0; - -static void TriggerTwice(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - - test_assert(base_ent != 0); - test_assert(inst_ent_a != 0); - test_assert(inst_ent_b != 0); - test_int(it->count, 1); - - if (invoke_count == 0) { - test_assert(it->entities[0] == base_ent); - invoke_count ++; - } else if (invoke_count == 1) { - test_assert(it->entities[0] == inst_ent_b); - invoke_count ++; - } else { - test_int(invoke_count, 2); - test_assert(it->entities[0] == inst_ent_a); - invoke_count ++; - } -} - -void Observer_notify_propagated_twice(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }}, - .events = {EcsOnAdd}, - .callback = TriggerTwice, - .ctx = &ctx - }); - test_assert(t1 != 0); - - base_ent = ecs_new_id(world); - inst_ent_a = ecs_new_w_pair(world, EcsIsA, base_ent); - ecs_add(world, inst_ent_a, TagB); - inst_ent_b = ecs_new_w_pair(world, EcsIsA, base_ent); - - test_int(ctx.invoked, 0); - - ecs_add(world, base_ent, TagA); - - test_int(ctx.invoked, 3); - test_int(invoke_count, 3); - - ecs_fini(world); -} - -void Observer_on_add_yield_existing(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - /* Create entities before trigger */ - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - - test_assert(e1 != 0); - test_assert(e2 != 0); - test_assert(e3 != 0); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ Tag }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx, - .yield_existing = true - }); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 3); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.event_id, Tag); - test_int(ctx.term_count, 1); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e3); - test_int(ctx.c[0][0], Tag); - - // Ensure normal triggering also still works - ecs_os_zeromem(&ctx); - ecs_new(world, Tag); - test_int(ctx.invoked, 1); - - ecs_fini(world); -} - -void Observer_on_add_yield_existing_2_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - /* Create entities before trigger */ - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - - test_assert(e1 != 0); - test_assert(e2 != 0); - test_assert(e3 != 0); - - ecs_add(world, e3, TagB); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx, - .yield_existing = true - }); - - test_int(ctx.invoked, 2); - test_int(ctx.count, 3); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.event_id, TagA); - test_int(ctx.term_count, 1); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e3); - test_int(ctx.c[0][0], TagA); - - // Ensure normal triggering also still works - ecs_os_zeromem(&ctx); - ecs_new(world, TagA); - test_int(ctx.invoked, 1); - - ecs_fini(world); -} - -void Observer_on_add_yield_existing_2_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - /* Create entities before trigger */ - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); /* no match */ - ecs_entity_t e5 = ecs_new(world, TagB); /* no match */ - - test_assert(e1 != 0); - test_assert(e2 != 0); - test_assert(e3 != 0); - test_assert(e4 != 0); - test_assert(e5 != 0); - - ecs_add(world, e1, TagB); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagB); - - ecs_add(world, e3, TagC); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }, { TagB }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx, - .yield_existing = true - }); - - test_int(ctx.invoked, 2); - test_int(ctx.count, 3); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.e[2], e3); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], TagB); - test_int(ctx.c[1][0], TagA); - test_int(ctx.c[1][1], TagB); - - // Ensure normal triggering also still works - ecs_os_zeromem(&ctx); - ecs_entity_t e6 = ecs_new(world, TagA); - test_int(ctx.invoked, 0); - ecs_add(world, e6, TagB); - test_int(ctx.invoked, 1); - - ecs_fini(world); -} - -void Observer_on_add_yield_existing_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ ecs_pair(Rel, EcsWildcard ) }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx, - .yield_existing = true - }); - - test_int(ctx.invoked, 2); - test_int(ctx.count, 2); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 1); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); - test_int(ctx.c[1][0], ecs_pair(Rel, TgtB)); - - ecs_fini(world); -} - -void Observer_on_add_yield_existing_wildcard_multi(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_new_w_pair(world, Rel, TgtB); - - ecs_add(world, e1, Tag); - ecs_add(world, e2, Tag); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ ecs_pair(Rel, EcsWildcard ) }, { Tag }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx, - .yield_existing = true - }); - - test_int(ctx.invoked, 2); - test_int(ctx.count, 2); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); - test_int(ctx.c[0][1], Tag); - test_int(ctx.c[1][0], ecs_pair(Rel, TgtB)); - test_int(ctx.c[1][1], Tag); - - ecs_fini(world); -} - -void Observer_on_add_yield_existing_wildcard_multi_w_wildcard_pivot(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_add(world, e1, Tag); - ecs_add(world, e2, Tag); - ecs_new(world, Tag); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ ecs_pair(Rel, EcsWildcard ) }, { Tag }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx, - .yield_existing = true - }); - - test_int(ctx.invoked, 2); - test_int(ctx.count, 2); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 2); - test_null(ctx.param); - - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); - test_int(ctx.c[0][1], Tag); - test_int(ctx.c[1][0], ecs_pair(Rel, TgtB)); - test_int(ctx.c[1][1], Tag); - - ecs_fini(world); -} - -void Observer_observer_superset_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_entity_t base = ecs_new_id(world); - ecs_entity_t inst = ecs_new_id(world); - ecs_add_pair(world, inst, EcsIsA, base); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.flags = EcsUp }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t != 0); - - test_int(ctx.invoked, 0); - - ecs_add_pair(world, base, Rel, ObjA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.event_id, ecs_pair(Rel, ObjA)); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], inst); - test_int(ctx.s[0][0], base); - - ecs_os_zeromem(&ctx); - - ecs_add_pair(world, base, Rel, ObjB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.event_id, ecs_pair(Rel, ObjB)); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], inst); - test_int(ctx.s[0][0], base); - - ecs_fini(world); -} - -void Observer_observer_superset_wildcard_add_isa(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_entity_t base = ecs_new_id(world); - ecs_add_pair(world, base, Rel, ObjA); - ecs_add_pair(world, base, Rel, ObjB); - - ecs_entity_t inst = ecs_new_id(world); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.flags = EcsUp }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t != 0); - - test_int(ctx.invoked, 0); - - ecs_add_pair(world, inst, EcsIsA, base); - - test_int(ctx.invoked, 2); - test_int(ctx.count, 2); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], inst); - test_int(ctx.s[0][0], base); - - ecs_fini(world); -} - -void Observer_observer_superset_wildcard_add_isa_at_offset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_entity_t base = ecs_new_id(world); - ecs_add(world, base, Tag); - ecs_add_pair(world, base, Rel, ObjA); - ecs_add_pair(world, base, Rel, ObjB); - - ecs_entity_t inst = ecs_new_id(world); - - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.flags = EcsUp }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t != 0); - - test_int(ctx.invoked, 0); - - ecs_add_pair(world, inst, EcsIsA, base); - - test_int(ctx.invoked, 2); - test_int(ctx.count, 2); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], inst); - test_int(ctx.s[0][0], base); - - ecs_fini(world); -} - -void Observer_on_set_w_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - Probe ctx = {0}; - ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }}, - .events = {EcsOnSet}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t1 != 0); - - ecs_entity_t e = ecs_new_id(world); - test_assert(e != 0); - - ecs_add(world, e, TagA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.event_id, TagA); - - ecs_fini(world); -} - -void Observer_mixed_on_set_w_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_COMPONENT(world, Position); - - Probe ctx = {0}; - ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }, { ecs_id(Position) }}, - .events = {EcsOnSet}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t1 != 0); - - ecs_entity_t e1 = ecs_new_id(world); - test_assert(e1 != 0); - - ecs_add(world, e1, TagA); - test_int(ctx.invoked, 0); - - ecs_add(world, e1, Position); - test_int(ctx.invoked, 0); - - ecs_set(world, e1, Position, {10, 20}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e1); - test_int(ctx.event, EcsOnSet); - test_int(ctx.event_id, ecs_id(Position)); - - ctx = (Probe){0}; - - ecs_entity_t e2 = ecs_new_id(world); - test_assert(e2 != 0); - - ecs_set(world, e2, Position, {10, 20}); - test_int(ctx.invoked, 0); - - ecs_add(world, e2, TagA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - test_int(ctx.event, EcsOnAdd); - test_int(ctx.event_id, TagA); - - ecs_fini(world); -} - -void Observer_mixed_un_set_w_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_COMPONENT(world, Position); - - Probe ctx = {0}; - ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }, { ecs_id(Position) }}, - .events = {EcsUnSet}, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t1 != 0); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add(world, e1, TagA); - ecs_set(world, e1, Position, {10, 20}); - test_int(ctx.invoked, 0); - - ecs_remove(world, e1, TagA); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e1); - test_int(ctx.event, EcsOnRemove); - test_int(ctx.event_id, TagA); - - ctx = (Probe){0}; - - ecs_entity_t e2 = ecs_new_id(world); - ecs_set(world, e2, Position, {10, 20}); - ecs_add(world, e2, TagA); - test_int(ctx.invoked, 0); - - ecs_remove(world, e2, Position); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - test_int(ctx.event, EcsUnSet); - test_int(ctx.event_id, ecs_id(Position)); - - ecs_fini(world); -} - -void Observer_match_base_w_id_at_offset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - Probe ctx_1 = {0}; - ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ ecs_id(Position) }}, - .events = {EcsOnSet}, - .callback = Observer, - .ctx = &ctx_1 - }); - test_assert(t1 != 0); - - Probe ctx_2 = {0}; - ecs_entity_t t2 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ ecs_id(Velocity) }}, - .events = {EcsOnSet}, - .callback = Observer, - .ctx = &ctx_2 - }); - test_assert(t2 != 0); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_set(world, base, Position, {10, 20}); - ecs_set(world, base, Velocity, {1, 2}); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], inst); - test_int(ctx_1.event, EcsOnSet); - test_int(ctx_1.event_id, ecs_id(Position)); - - test_int(ctx_2.invoked, 1); - test_int(ctx_2.count, 1); - test_int(ctx_2.e[0], inst); - test_int(ctx_2.event, EcsOnSet); - test_int(ctx_2.event_id, ecs_id(Velocity)); - - ecs_fini(world); -} - -static int run_invoked = 0; -static int run_invoked_matched = 0; - -static void Run(ecs_iter_t *it) { - if (ecs_observer_default_run_action(it)) { - run_invoked_matched ++; - } - run_invoked ++; -} - -static void Run_w_iter_next(ecs_iter_t *it) { - run_invoked ++; - - test_assert(it != NULL); - test_assert(it->next != NULL); - test_assert(it->callback != NULL); - - while (ecs_iter_next(it)) { - it->callback(it); - } -} - -void Observer_custom_run_action(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - Probe ctx = {0}; - ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }}, - .events = {EcsOnAdd}, - .run = Run, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t1 != 0); - - ecs_entity_t e = ecs_new(world, TagA); - - test_int(run_invoked, 1); - test_int(run_invoked_matched, 1); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_custom_run_action_w_iter_next(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - Probe ctx = {0}; - ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }}, - .events = {EcsOnAdd}, - .run = Run_w_iter_next, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t1 != 0); - - ecs_entity_t e = ecs_new(world, TagA); - - test_int(run_invoked, 1); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_custom_run_action_2_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }, { TagB }}, - .events = {EcsOnAdd}, - .run = Run, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t1 != 0); - - ecs_entity_t e = ecs_new(world, TagA); - test_int(run_invoked, 1); - run_invoked = 0; - - ecs_add(world, e, TagB); - test_int(run_invoked, 1); - test_int(run_invoked_matched, 1); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_custom_run_action_w_iter_next_2_terms(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }, { TagB }}, - .events = {EcsOnAdd}, - .run = Run_w_iter_next, - .callback = Observer, - .ctx = &ctx - }); - test_assert(t1 != 0); - - ecs_entity_t e = ecs_new(world, TagA); - test_int(run_invoked, 1); - run_invoked = 0; - - ecs_add(world, e, TagB); - test_int(run_invoked, 1); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_read_in_on_remove_after_add_other_w_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - ECS_TAG(world, O); - - Probe ctx = {0}; - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = { - { .id = ecs_pair(ecs_id(Position), O)}, - { .id = Tag, .oper = EcsNot } - }, - .events = {EcsOnRemove}, - .callback = Observer_w_1_value, - .ctx = &ctx - }); - - ecs_entity_t e = ecs_set_pair(world, 0, Position, O, { 10, 20 }); - test_int(ctx.invoked, 0); - - ecs_add(world, e, Tag); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e); - test_int(ctx.event, EcsOnRemove); - - ecs_fini(world); -} - -void Observer_observer_w_short_notation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - Probe ctx = {0}; - ecs_entity_t o = ecs_observer(world, { - .filter.terms = {{ - .id = Foo - }}, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - test_assert(o != 0); - - ecs_entity_t e = ecs_new(world, Foo); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_observer_w_filter_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = {{ .id = TagA }, { .id = TagB, .src.flags = EcsFilter }}, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t e = ecs_new_id(world); - test_int(ctx.invoked, 0); - - ecs_add(world, e, TagB); - test_int(ctx.invoked, 0); - - ecs_add(world, e, TagA); - test_int(ctx.invoked, 1); - - ecs_remove(world, e, TagB); - ecs_add(world, e, TagB); - test_int(ctx.invoked, 1); - - ecs_clear(world, e); - test_int(ctx.invoked, 1); - - ecs_add(world, e, TagA); - test_int(ctx.invoked, 1); - - ecs_fini(world); -} - -static int free_ctx_invoked = 0; - -static -void free_ctx(void *ctx) { - free_ctx_invoked ++; - ecs_os_free(ctx); -} - -static int observer_w_ctx_invoked = 0; - -static void Observer_w_ctx(ecs_iter_t *it) { - test_assert(it->ctx != NULL); - test_int(*(int*)it->ctx, 10); - observer_w_ctx_invoked ++; -} - -void Observer_multi_observer_w_ctx_free(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - int *ctx = ecs_os_malloc_t(int); - *ctx = 10; - - ecs_entity_t o = ecs_observer(world, { - .filter.terms = {{ .id = TagA }, { .id = TagB }}, - .events = { EcsOnAdd }, - .callback = Observer_w_ctx, - .ctx = ctx, - .ctx_free = free_ctx - }); - - test_int(observer_w_ctx_invoked, 0); - test_int(free_ctx_invoked, 0); - - ecs_entity_t e = ecs_new(world, TagA); - test_int(observer_w_ctx_invoked, 0); - test_int(free_ctx_invoked, 0); - ecs_add(world, e, TagB); - test_int(observer_w_ctx_invoked, 1); - test_int(free_ctx_invoked, 0); - - ecs_delete(world, o); - - test_int(free_ctx_invoked, 1); - - ecs_fini(world); - - test_int(free_ctx_invoked, 1); -} - -void Observer_propagate_after_on_delete_clear_action(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_entity_t parent = ecs_new_id(world); - ecs_add_pair(world, parent, Rel, tgt); - - Probe ctx = {0}; - - ecs_observer(world, { - .filter.terms = {{ - .id = ecs_pair(Rel, EcsWildcard), - .src.flags = EcsUp, - .src.trav = EcsChildOf - }}, - .events = { EcsOnRemove }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - test_int(ctx.invoked, 0); - - ecs_delete(world, tgt); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], child); - test_int(ctx.s[0][0], parent); - test_int(ctx.event, EcsOnRemove); - - ecs_fini(world); -} - -void Observer_on_add_after_batch_w_exclusive_adds(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Exclusive); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - Probe ctx = {0}; - - ecs_observer(world, { - .filter.terms = {{ - ecs_pair(Rel, EcsWildcard), - .src.flags = EcsSelf - }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - - - ecs_entity_t e = ecs_new_id(world); - - ecs_defer_begin(world); - ecs_add_pair(world, e, Rel, TgtA); - ecs_add_pair(world, e, Rel, TgtB); - ecs_defer_end(world); - - test_assert(!ecs_has_pair(world, e, Rel, TgtA)); - test_assert(ecs_has_pair(world, e, Rel, TgtB)); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][0], ecs_pair(Rel, TgtB)); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_propagate_match_relationship_w_self_up(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = {{ TagB, .src.flags = EcsSelf|EcsUp }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t parent = ecs_new_id(world); - ecs_add(world, parent, TagA); - ecs_new_w_pair(world, EcsChildOf, parent); - test_int(ctx.invoked, 0); - - ecs_add(world, parent, TagB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], parent); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][0], TagB); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_propagate_match_relationship_w_up(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = {{ TagB, .src.flags = EcsUp }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t parent = ecs_new_id(world); - ecs_add(world, parent, TagA); - ecs_new_w_pair(world, EcsChildOf, parent); - test_int(ctx.invoked, 0); - - ecs_add(world, parent, TagB); - test_int(ctx.invoked, 0); - - ecs_fini(world); -} - -void Observer_observer_w_2_fixed_src(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t Foo = ecs_new_id(world); - ecs_entity_t Bar = ecs_new_id(world); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - Probe ctx = {0}; - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = { - // Comment out either of these and the callback succeeds. - { Foo , .src.id = e1 }, - { Bar, .src.id = e2 }, - }, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_add_id(world, e1, Foo); - test_int(ctx.invoked, 0); - - ecs_add_id(world, e2, Bar); - test_int(ctx.invoked, 1); - - ecs_fini(world); -} - -void Observer_emit_for_recreated_id_after_remove_all(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add_pair(world, e1, Rel, Tgt); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = { - { TagA }, - { ecs_pair(Rel, Tgt) } - }, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_remove_all(world, ecs_pair(Rel, Tgt)); - test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt))); - test_assert(ecs_is_alive(world, e1)); - - test_int(0, ctx.invoked); - - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add_pair(world, e2, Rel, Tgt); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], ecs_pair(Rel, Tgt)); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_emit_for_recreated_id_after_remove_all_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add_pair(world, e1, Rel, Tgt); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = { - { TagA }, - { ecs_pair(Rel, Tgt) } - }, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_remove_all(world, ecs_pair(Rel, EcsWildcard)); - test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt))); - test_assert(ecs_is_alive(world, e1)); - - test_int(0, ctx.invoked); - - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add_pair(world, e2, Rel, Tgt); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], ecs_pair(Rel, Tgt)); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_emit_for_recreated_id_after_delete_with(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add_pair(world, e1, Rel, Tgt); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = { - { TagA }, - { ecs_pair(Rel, Tgt) } - }, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_delete_with(world, ecs_pair(Rel, Tgt)); - test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt))); - test_assert(!ecs_is_alive(world, e1)); - - test_int(0, ctx.invoked); - - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add_pair(world, e2, Rel, Tgt); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], ecs_pair(Rel, Tgt)); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_emit_for_recreated_id_after_delete_with_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add_pair(world, e1, Rel, Tgt); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = { - { TagA }, - { ecs_pair(Rel, Tgt) } - }, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_delete_with(world, ecs_pair(Rel, EcsWildcard)); - test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt))); - test_assert(!ecs_is_alive(world, e1)); - - test_int(0, ctx.invoked); - - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add_pair(world, e2, Rel, Tgt); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][0], TagA); - test_int(ctx.c[0][1], ecs_pair(Rel, Tgt)); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_delete_observed_id(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add_pair(world, e1, Rel, Tgt); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = { - { TagA }, - { ecs_pair(Rel, Tgt) } - }, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - test_expect_abort(); - ecs_delete(world, TagA); -} - -void Observer_delete_observed_rel(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add_pair(world, e1, Rel, Tgt); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = { - { TagA }, - { ecs_pair(Rel, Tgt) } - }, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - test_expect_abort(); - ecs_delete(world, Rel); -} - -void Observer_delete_observed_tgt(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add_pair(world, e1, Rel, Tgt); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = { - { TagA }, - { ecs_pair(Rel, Tgt) } - }, - .events = { EcsOnAdd }, - .callback = Observer, - .ctx = &ctx - }); - - test_expect_abort(); - ecs_delete(world, Tgt); -} - -static int pair_x = 0; -static int32_t pair_column = 0; - -static -void OnTagPair(ecs_iter_t *it) { - test_int(it->count, 1); - pair_column = it->columns[0]; - probe_iter(it); -} - -static -void OnPair(ecs_iter_t *it) { - test_int(it->count, 1); - Position *p = ecs_field(it, Position, 1); - pair_x = p->x; - pair_column = it->columns[0]; - probe_iter(it); -} - -void Observer_on_add_2_pairs_w_uni_observer(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - ECS_OBSERVER(world, OnTagPair, EcsOnAdd, (Rel, *)); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, Tag); - ecs_add_pair(world, e, Rel, TgtA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnTagPair); - test_int(ctx.term_count, 1); - test_int(ctx.event_id, ecs_pair(Rel, TgtA)); - test_int(pair_column, 2); - - ecs_os_zeromem(&ctx); - ecs_add_pair(world, e, Rel, TgtB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnTagPair); - test_int(ctx.term_count, 1); - test_int(ctx.event_id, ecs_pair(Rel, TgtB)); - test_int(pair_column, 3); - - ecs_os_zeromem(&ctx); - ecs_add_pair(world, e, Rel, TgtC); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnTagPair); - test_int(ctx.term_count, 1); - test_int(ctx.event_id, ecs_pair(Rel, TgtC)); - test_int(pair_column, 4); - - ecs_fini(world); -} - -void Observer_on_add_2_pairs_w_multi_observer(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - ECS_OBSERVER(world, OnTagPair, EcsOnAdd, (Rel, *), Tag); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, Tag); - ecs_add_pair(world, e, Rel, TgtA); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnTagPair); - test_int(ctx.term_count, 2); - test_int(ctx.event_id, ecs_pair(Rel, TgtA)); - test_int(pair_column, 2); - - ecs_os_zeromem(&ctx); - ecs_add_pair(world, e, Rel, TgtB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnTagPair); - test_int(ctx.term_count, 2); - test_int(ctx.event_id, ecs_pair(Rel, TgtB)); - test_int(pair_column, 3); - - ecs_os_zeromem(&ctx); - ecs_add_pair(world, e, Rel, TgtC); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnTagPair); - test_int(ctx.term_count, 2); - test_int(ctx.event_id, ecs_pair(Rel, TgtC)); - test_int(pair_column, 4); - - ecs_fini(world); -} - -void Observer_on_set_2_pairs_w_uni_observer(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - ECS_OBSERVER(world, OnPair, EcsOnSet, (Position, *)); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, Tag); - ecs_set_pair(world, e, Position, TgtA, {1}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnPair); - test_int(ctx.term_count, 1); - test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtA)); - test_int(pair_column, 2); - test_int(pair_x, 1); - - ecs_os_zeromem(&ctx); - ecs_set_pair(world, e, Position, TgtB, {2}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnPair); - test_int(ctx.term_count, 1); - test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtB)); - test_int(pair_column, 3); - test_int(pair_x, 2); - - ecs_os_zeromem(&ctx); - ecs_set_pair(world, e, Position, TgtC, {3}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnPair); - test_int(ctx.term_count, 1); - test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtC)); - test_int(pair_column, 4); - test_int(pair_x, 3); - - ecs_fini(world); -} - -void Observer_on_set_2_pairs_w_multi_observer(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - ECS_OBSERVER(world, OnPair, EcsOnSet, (Position, *), Tag); - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, Tag); - ecs_set_pair(world, e, Position, TgtA, {1}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnPair); - test_int(ctx.term_count, 2); - test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtA)); - test_int(pair_column, 2); - test_int(pair_x, 1); - - ecs_os_zeromem(&ctx); - ecs_set_pair(world, e, Position, TgtB, {2}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnPair); - test_int(ctx.term_count, 2); - test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtB)); - test_int(pair_column, 3); - test_int(pair_x, 2); - - ecs_os_zeromem(&ctx); - ecs_set_pair(world, e, Position, TgtC, {3}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, OnPair); - test_int(ctx.term_count, 2); - test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtC)); - test_int(pair_column, 4); - test_int(pair_x, 3); - - ecs_fini(world); -} - -void Observer_on_remove_target_from_base_at_offset(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t R = ecs_new_id(world); - ecs_entity_t T1 = ecs_new_id(world); - ecs_entity_t T2 = ecs_new_id(world); - ecs_entity_t C = ecs_new_id(world); - - Probe ctx = { 0 }; - ecs_entity_t o = ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(R, EcsWildcard), .src.flags = EcsUp }, - { .id = C }, - }, - .events = { EcsOnRemove }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t base = ecs_new_id(world); - ecs_add_pair(world, base, R, T1); - ecs_add_pair(world, base, R, T2); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, EcsIsA, base); - ecs_add_id(world, e, C); - - test_int(ctx.invoked, 0); - ecs_delete(world, T2); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.term_count, 2); - test_int(ctx.event_id, ecs_pair(R, T2)); - test_int(ctx.event, EcsOnRemove); - - ecs_fini(world); -} - -static void Observer_base_component(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - - Position *p = ecs_field(it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 30); - test_int(p->y, 40); -} - -void Observer_on_remove_target_component_from_base_at_offset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ecs_entity_t T1 = ecs_new_id(world); - ecs_entity_t T2 = ecs_new_id(world); - ecs_entity_t C = ecs_new_id(world); - - Probe ctx = { 0 }; - ecs_entity_t o = ecs_observer(world, { - .filter.terms = { - { .id = ecs_pair(ecs_id(Position), EcsWildcard), .src.flags = EcsUp }, - { .id = C }, - }, - .events = { EcsOnRemove }, - .callback = Observer_base_component, - .ctx = &ctx - }); - - ecs_entity_t base = ecs_new_id(world); - ecs_set_pair(world, base, Position, T1, {10, 20}); - ecs_set_pair(world, base, Position, T2, {30, 40}); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, EcsIsA, base); - ecs_add_id(world, e, C); - - test_int(ctx.invoked, 0); - ecs_delete(world, T2); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.system, o); - test_int(ctx.term_count, 2); - test_int(ctx.e[0], e); - test_int(ctx.s[0][0], base); - test_int(ctx.s[0][1], 0); - test_int(ctx.event_id, ecs_pair(ecs_id(Position), T2)); - test_int(ctx.event, EcsOnRemove); - - ecs_delete(world, o); - - ecs_fini(world); -} - -static void Observer_w_other_table(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - test_assert(it->table != NULL); - test_assert(it->other_table != NULL); -} - -static void Observer_dummy(ecs_iter_t *it) {} - -void Observer_wildcard_propagate_w_other_table(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag = ecs_new_id(world); - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t tgt_1 = ecs_new_id(world); - - ecs_entity_t parent = ecs_new_w_id(world, tag); - ecs_new_w_pair(world, EcsChildOf, parent); - - Probe ctx_parent = {0}; - - ecs_observer(world, { - .filter.terms = {{ ecs_pair(rel, EcsWildcard) }}, - .events = {EcsOnAdd}, - .callback = Observer_dummy - }); - - ecs_observer(world, { - .filter.terms = {{ ecs_pair(rel, EcsWildcard) }}, - .events = {EcsWildcard}, - .callback = Observer_w_other_table, - .ctx = &ctx_parent - }); - - ecs_observer(world, { - .filter.terms = {{ ecs_pair(rel, EcsWildcard), .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsWildcard}, - .callback = Observer_dummy - }); - - ecs_add_pair(world, parent, rel, tgt_1); - - test_int(ctx_parent.invoked, 1); - - ecs_fini(world); -} - -void Observer_cache_test_1(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - - Probe ctx = {0}; - - ecs_observer(world, { - .filter.terms = {{ tag, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = &ctx - }); - - ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); - ecs_add_id(world, e1, tag); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - test_int(ctx.s[0][0], e1); - test_int(ctx.c[0][0], tag); - test_int(ctx.event, EcsOnAdd); - ecs_os_zeromem(&ctx); - - ecs_entity_t e3 = ecs_new_id(world); - ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e3); - test_int(ctx.s[0][0], e1); - test_int(ctx.c[0][0], tag); - test_int(ctx.event, EcsOnAdd); - ecs_os_zeromem(&ctx); - - ecs_delete(world, e3); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e3); - test_int(ctx.s[0][0], e1); - test_int(ctx.c[0][0], tag); - test_int(ctx.event, EcsOnRemove); - ecs_os_zeromem(&ctx); - - ecs_remove_id(world, e1, tag); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - test_int(ctx.s[0][0], e1); - test_int(ctx.c[0][0], tag); - test_int(ctx.event, EcsOnRemove); - ecs_os_zeromem(&ctx); - - e3 = ecs_new_id(world); - ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); - - test_int(ctx.invoked, 0); - - ecs_fini(world); -} - -void Observer_cache_test_2(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag_1 = ecs_new_id(world); - ecs_entity_t tag_2 = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - Probe ctx_1 = {0}; - Probe ctx_2 = {0}; - - ecs_observer(world, { - .filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = &ctx_1 - }); - ecs_observer(world, { - .filter.terms = {{ tag_2, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = &ctx_2 - }); - - ecs_add_id(world, e1, tag_1); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e2); - test_int(ctx_1.s[0][0], e1); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnAdd); - ecs_os_zeromem(&ctx_1); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e2, tag_2); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e3); - test_int(ctx_1.s[0][0], e1); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnAdd); - ecs_os_zeromem(&ctx_1); - test_int(ctx_2.invoked, 1); - test_int(ctx_2.count, 1); - test_int(ctx_2.e[0], e3); - test_int(ctx_2.s[0][0], e2); - test_int(ctx_2.c[0][0], tag_2); - test_int(ctx_2.event, EcsOnAdd); - ecs_os_zeromem(&ctx_2); - - ecs_fini(world); -} - -void Observer_cache_test_3(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag_1 = ecs_new_id(world); - ecs_entity_t tag_2 = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - Probe ctx_1 = {0}; - Probe ctx_2 = {0}; - - ecs_observer(world, { - .filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = &ctx_1 - }); - ecs_observer(world, { - .filter.terms = {{ tag_2, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = &ctx_2 - }); - - ecs_add_id(world, e1, tag_1); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e2); - test_int(ctx_1.s[0][0], e1); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnAdd); - ecs_os_zeromem(&ctx_1); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e2, tag_1); - ecs_add_id(world, e2, tag_2); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e3); - test_int(ctx_1.s[0][0], e2); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnAdd); - ecs_os_zeromem(&ctx_1); - test_int(ctx_2.invoked, 1); - test_int(ctx_2.count, 1); - test_int(ctx_2.e[0], e3); - test_int(ctx_2.s[0][0], e2); - test_int(ctx_2.c[0][0], tag_2); - test_int(ctx_2.event, EcsOnAdd); - ecs_os_zeromem(&ctx_2); - - ecs_fini(world); -} - -void Observer_cache_test_4(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag_0 = ecs_new_id(world); - ecs_entity_t tag_1 = ecs_new_id(world); - ecs_entity_t tag_2 = ecs_new_id(world); - ecs_entity_t e0 = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - Probe *ctx_0 = ecs_os_calloc_t(Probe); - Probe *ctx_1 = ecs_os_calloc_t(Probe); - Probe *ctx_2 = ecs_os_calloc_t(Probe); - - ecs_observer(world, { - .filter.terms = {{ tag_0, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = ctx_0 - }); - ecs_observer(world, { - .filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = ctx_1 - }); - ecs_observer(world, { - .filter.terms = {{ tag_2, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = ctx_2 - }); - - ecs_add_id(world, e0, tag_0); - test_int(ctx_0->invoked, 0); - test_int(ctx_1->invoked, 0); - test_int(ctx_2->invoked, 0); - - ecs_add_id(world, e1, ecs_pair(EcsChildOf, e0)); - test_int(ctx_0->invoked, 1); - test_int(ctx_0->count, 1); - test_int(ctx_0->e[0], e1); - test_int(ctx_0->s[0][0], e0); - test_int(ctx_0->c[0][0], tag_0); - test_int(ctx_0->event, EcsOnAdd); - ecs_os_zeromem(ctx_0); - test_int(ctx_1->invoked, 0); - test_int(ctx_2->invoked, 0); - - ecs_add_id(world, e1, tag_1); - test_int(ctx_0->invoked, 0); - test_int(ctx_1->invoked, 0); - test_int(ctx_2->invoked, 0); - - ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); - test_int(ctx_0->invoked, 1); - test_int(ctx_0->count, 1); - test_int(ctx_0->e[0], e2); - test_int(ctx_0->s[0][0], e0); - test_int(ctx_0->c[0][0], tag_0); - test_int(ctx_0->event, EcsOnAdd); - ecs_os_zeromem(ctx_0); - test_int(ctx_1->invoked, 1); - test_int(ctx_1->count, 1); - test_int(ctx_1->e[0], e2); - test_int(ctx_1->s[0][0], e1); - test_int(ctx_1->c[0][0], tag_1); - test_int(ctx_1->event, EcsOnAdd); - ecs_os_zeromem(ctx_1); - test_int(ctx_2->invoked, 0); - - ecs_add_id(world, e2, tag_1); - ecs_add_id(world, e2, tag_2); - test_int(ctx_0->invoked, 0); - test_int(ctx_1->invoked, 0); - test_int(ctx_2->invoked, 0); - - ecs_remove_id(world, e0, tag_0); - test_int(ctx_0->invoked, 2); - ecs_os_zeromem(ctx_0); - test_int(ctx_1->invoked, 0); - test_int(ctx_2->invoked, 0); - - ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); - test_int(ctx_0->invoked, 0); - test_int(ctx_1->invoked, 1); - test_int(ctx_1->count, 1); - test_int(ctx_1->e[0], e3); - test_int(ctx_1->s[0][0], e2); - test_int(ctx_1->c[0][0], tag_1); - test_int(ctx_1->event, EcsOnAdd); - ecs_os_zeromem(ctx_1); - test_int(ctx_2->invoked, 1); - test_int(ctx_2->count, 1); - test_int(ctx_2->e[0], e3); - test_int(ctx_2->s[0][0], e2); - test_int(ctx_2->c[0][0], tag_2); - test_int(ctx_2->event, EcsOnAdd); - ecs_os_zeromem(ctx_2); - - ecs_fini(world); - - ecs_os_free(ctx_0); - ecs_os_free(ctx_1); - ecs_os_free(ctx_2); -} - -void Observer_cache_test_5(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag_0 = ecs_new_id(world); - ecs_entity_t tag_1 = ecs_new_id(world); - ecs_entity_t tag_2 = ecs_new_id(world); - ecs_entity_t e0 = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - - Probe ctx_1 = {0}; - Probe ctx_2 = {0}; - - ecs_observer(world, { - .filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = &ctx_1 - }); - ecs_observer(world, { - .filter.terms = {{ tag_2, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = &ctx_2 - }); - - ecs_add_id(world, e0, tag_0); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e1, ecs_pair(EcsChildOf, e0)); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e1, tag_1); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e2); - test_int(ctx_1.s[0][0], e1); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnAdd); - ecs_os_zeromem(&ctx_1); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e2, tag_1); - ecs_add_id(world, e2, tag_2); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_remove_id(world, e0, tag_0); - test_int(ctx_1.invoked, 0); - test_int(ctx_2.invoked, 0); - - ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e3); - test_int(ctx_1.s[0][0], e2); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnAdd); - ecs_os_zeromem(&ctx_1); - test_int(ctx_2.invoked, 1); - test_int(ctx_2.count, 1); - test_int(ctx_2.e[0], e3); - test_int(ctx_2.s[0][0], e2); - test_int(ctx_2.c[0][0], tag_2); - test_int(ctx_2.event, EcsOnAdd); - ecs_os_zeromem(&ctx_2); - - ecs_add_id(world, e4, ecs_pair(EcsChildOf, e1)); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e4); - test_int(ctx_1.s[0][0], e1); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnAdd); - ecs_os_zeromem(&ctx_1); - test_int(ctx_2.invoked, 0); - - ecs_fini(world); -} - -void Observer_cache_test_6(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag_1 = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - Probe ctx_1 = {0}; - ecs_observer(world, { - .filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = &ctx_1 - }); - - ecs_add_id(world, e1, tag_1); - test_int(ctx_1.invoked, 0); - - ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e2); - test_int(ctx_1.s[0][0], e1); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnAdd); - ecs_os_zeromem(&ctx_1); - - ecs_remove_id(world, e1, tag_1); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e2); - test_int(ctx_1.s[0][0], e1); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnRemove); - ecs_os_zeromem(&ctx_1); - - ecs_add_id(world, e3, ecs_pair(EcsChildOf, e1)); - test_int(ctx_1.invoked, 0); - - ecs_fini(world); -} - -void Observer_cache_test_7(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag_1 = ecs_new_id(world); - ecs_entity_t tag_2 = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - Probe ctx_1 = {0}; - ecs_observer(world, { - .filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }}, - .events = {EcsOnAdd, EcsOnRemove}, - .callback = Observer, - .ctx = &ctx_1 - }); - - ecs_add_id(world, e1, tag_1); - ecs_add_id(world, e1, tag_2); - test_int(ctx_1.invoked, 0); - - ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e2); - test_int(ctx_1.s[0][0], e1); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnAdd); - ecs_os_zeromem(&ctx_1); - - ecs_remove_id(world, e1, tag_1); - test_int(ctx_1.invoked, 1); - test_int(ctx_1.count, 1); - test_int(ctx_1.e[0], e2); - test_int(ctx_1.s[0][0], e1); - test_int(ctx_1.c[0][0], tag_1); - test_int(ctx_1.event, EcsOnRemove); - ecs_os_zeromem(&ctx_1); - - ecs_add_id(world, e3, ecs_pair(EcsChildOf, e1)); - test_int(ctx_1.invoked, 0); - - ecs_fini(world); -} - -void Observer_cache_test_8(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = {{ TagB }}, - .events = {EcsOnAdd}, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t parent = ecs_new_id(world); - ecs_add(world, parent, TagA); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_new_w_pair(world, EcsChildOf, child); - test_int(ctx.invoked, 0); - - ecs_add(world, parent, TagB); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], parent); - test_int(ctx.s[0][0], 0); - test_int(ctx.c[0][0], TagB); - test_int(ctx.event, EcsOnAdd); - - ecs_fini(world); -} - -void Observer_cache_test_9(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - Probe ctx = {0}; - ecs_observer(world, { - .filter.terms = {{ ecs_id(Position), .src.flags = EcsUp }}, - .events = { EcsOnRemove }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_set(world, base, Position, {10, 20}); - - ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_2, EcsIsA, base); - ecs_set(world, base_2, Position, {20, 30}); - - ecs_entity_t base_3 = ecs_new_w_id(world, EcsPrefab); - ecs_add_pair(world, base_3, EcsIsA, base); - ecs_set(world, base_3, Position, {40, 50}); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_2); - ecs_add_pair(world, inst, EcsIsA, base_3); - - ecs_clear(world, base_2); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], inst); - test_int(ctx.s[0][0], base_2); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.event, EcsOnRemove); - - ecs_os_zeromem(&ctx); - - ecs_clear(world, base_3); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], inst); - test_int(ctx.s[0][0], base_3); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.event, EcsOnRemove); - - ecs_fini(world); -} - -void Observer_cache_test_10(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag = ecs_new_id(world); - ecs_entity_t ns = ecs_new_id(world); - ecs_add_id(world, ns, tag); - - ecs_entity_t foo_parent = ecs_new_w_pair(world, EcsChildOf, ns); - ecs_entity_t foo = ecs_new_w_pair(world, EcsChildOf, foo_parent); - ecs_entity_t bar = ecs_new_id(world); - - ecs_entity_t parent = ecs_new_w_id(world, foo); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_id(world, e1, foo); - ecs_add_pair(world, e1, EcsChildOf, parent); - ecs_add_id(world, e1, bar); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_id(world, e2, foo); - ecs_add_pair(world, e2, EcsChildOf, parent); - ecs_add_id(world, e2, bar); - - ecs_entity_t c1 = ecs_new_id(world); - ecs_add_id(world, c1, foo); - ecs_add_pair(world, c1, EcsChildOf, e1); - - ecs_entity_t c2 = ecs_new_id(world); - ecs_add_id(world, c2, foo); - ecs_add_pair(world, c2, EcsChildOf, e2); - - ecs_delete(world, ns); - test_assert(!ecs_is_alive(world, ns)); - test_assert(!ecs_is_alive(world, foo)); - test_assert(!ecs_is_alive(world, foo_parent)); - test_assert(!ecs_has_id(world, parent, foo)); - test_assert(!ecs_has_id(world, e1, foo)); - test_assert(!ecs_has_id(world, e2, foo)); - test_assert(!ecs_has_id(world, c1, foo)); - test_assert(!ecs_has_id(world, c2, foo)); - - ecs_delete(world, parent); - test_assert(!ecs_is_alive(world, parent)); - test_assert(!ecs_is_alive(world, e1)); - test_assert(!ecs_is_alive(world, e2)); - test_assert(!ecs_is_alive(world, c1)); - test_assert(!ecs_is_alive(world, c2)); - - ecs_fini(world); -} - -void Observer_cache_test_11(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t ns = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, ns); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsDependsOn, e1); - ecs_add_pair(world, e2, EcsChildOf, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, ns); - ecs_add_id(world, e3, EcsModule); - - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, e3); - ecs_add_pair(world, e4, EcsDependsOn, e2); - - ecs_add_id(world, e1, ecs_new_id(world)); - - ecs_run_aperiodic(world, 0); - - test_assert(true); // ensure cache revalidation didn't assert - - ecs_fini(world); -} - -void Observer_cache_test_12(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t t0 = ecs_new_id(world); - ecs_entity_t t1 = ecs_new_id(world); - ecs_entity_t t2 = ecs_new_id(world); - - ecs_entity_t e0 = ecs_new_id(world); - ecs_add_id(world, e0, t0); - ecs_add_id(world, e0, t1); - ecs_add_id(world, e0, t2); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_id(world, e1, t0); - ecs_add_id(world, e1, t1); - ecs_add_id(world, e1, t2); - - ecs_new_w_pair(world, EcsChildOf, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e0); - ecs_add_id(world, e3, t0); - - ecs_entity_t e4 = ecs_new_id(world); - ecs_new_w_pair(world, EcsChildOf, e4); - ecs_remove_all(world, t1); - - ecs_new_w_pair(world, EcsChildOf, e3); - - ecs_run_aperiodic(world, 0); - - ecs_remove_all(world, t0); - - test_assert(true); // ensure cache revalidation didn't assert - - ecs_fini(world); -} - -void Observer_cache_test_13(void) { - install_test_abort(); - ecs_world_t *world = ecs_mini(); - - ecs_entity_t t0 = ecs_new_id(world); - ecs_entity_t e0 = ecs_new_id(world); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_id(world, e1, t0); - ecs_add_id(world, e1, ecs_pair(EcsChildOf, e0)); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_id(world, e2, t0); - - ecs_entity_t e3 = ecs_new_id(world); - ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); - - ecs_delete_with(world, t0); - test_assert(!ecs_is_alive(world, e1)); - test_assert(!ecs_is_alive(world, e2)); - test_assert(!ecs_is_alive(world, e3)); - - ecs_entity_t e4 = ecs_new_id(world); - test_expect_abort(); - ecs_add_id(world, e4, ecs_pair(EcsChildOf, e2)); -} - -void Observer_cache_test_14(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - { - ecs_entity_t e0 = ecs_new_id(world); - ecs_add(world, e0, Tag); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsChildOf, e0); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsChildOf, e1); - - ecs_delete_with(world, Tag); - - test_assert(!ecs_is_alive(world, e0)); - test_assert(!ecs_is_alive(world, e1)); - test_assert(!ecs_is_alive(world, e2)); - } - - { - ecs_entity_t e0 = ecs_new_id(world); - ecs_add(world, e0, Tag); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsChildOf, e0); - ecs_add(world, e1, Tag); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsChildOf, e1); - - ecs_remove_all(world, Tag); - - test_assert(ecs_is_alive(world, e0)); - test_assert(ecs_is_alive(world, e1)); - test_assert(ecs_is_alive(world, e2)); - - test_assert(!ecs_has(world, e0, Tag)); - } - - ecs_fini(world); -} - -void Observer_cache_test_15(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t e0 = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); - - ecs_add_pair(world, e1, r, e0); - ecs_add_pair(world, e0, EcsChildOf, e1); - - ecs_run_aperiodic(world, 0); - - ecs_delete(world, e1); - - test_assert(!ecs_is_alive(world, e1)); - test_assert(!ecs_is_alive(world, e0)); - test_assert(ecs_is_alive(world, r)); - - ecs_fini(world); -} - -static int Observer_a_invoked = 0; -static int Observer_b_invoked = 0; - -static void Observer_a(ecs_iter_t *it) { - Observer_a_invoked += it->count; -} - -static void Observer_b(ecs_iter_t *it) { - Observer_b_invoked += it->count; -} - -void Observer_filter_observer_after_observer(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_observer(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = Tag } - }, - .callback = Observer_a, - .events = { EcsOnAdd } - }); - - ecs_observer(world, { - .filter.terms = { - { .id = ecs_id(Position), .inout = EcsInOutNone }, - { .id = Tag, .inout = EcsInOutNone} - }, - .callback = Observer_b, - .events = { EcsOnAdd } - }); - - ecs_entity_t e = ecs_new(world, Tag); - ecs_add(world, e, Position); - - test_int(Observer_a_invoked, 1); - test_int(Observer_b_invoked, 1); - - ecs_fini(world); -} - -void Observer_notify_after_defer_batched(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - Probe ctx = {0}; - - ecs_observer(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Velocity) } - }, - .callback = Observer, - .events = { EcsOnAdd }, - .ctx = &ctx - }); - - ecs_defer_begin(world); - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - test_int(ctx.invoked, 0); - ecs_set(world, e1, Velocity, {1, 2}); - ecs_defer_end(world); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e1); - - ecs_fini(world); -} - -void Observer_notify_after_defer_batched_2_entities_in_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - Probe ctx = {0}; - - ecs_observer(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Velocity) } - }, - .callback = Observer, - .events = { EcsOnAdd }, - .ctx = &ctx - }); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - test_int(ctx.invoked, 0); - ecs_set(world, e1, Velocity, {1, 2}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e1); - - ecs_os_zeromem(&ctx); - - ecs_entity_t e2 = ecs_new_id(world); - - ecs_defer_begin(world); - ecs_set(world, e2, Position, {30, 40}); - ecs_set(world, e2, Velocity, {3, 4}); - test_int(ctx.count, 0); - ecs_defer_end(world); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - - ecs_fini(world); -} - -void Observer_notify_after_defer_batched_2_entities_in_table_w_tgt(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - Probe ctx = {0}; - - ecs_observer(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Velocity) } - }, - .callback = Observer, - .events = { EcsOnAdd }, - .ctx = &ctx - }); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - test_int(ctx.invoked, 0); - ecs_set(world, e1, Velocity, {1, 2}); - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e1); - - ecs_os_zeromem(&ctx); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_add_pair(world, e3, EcsChildOf, e2); - - ecs_defer_begin(world); - ecs_set(world, e2, Position, {30, 40}); - ecs_set(world, e2, Velocity, {3, 4}); - test_int(ctx.count, 0); - ecs_defer_end(world); - test_int(ctx.count, 1); - test_int(ctx.e[0], e2); - - ecs_fini(world); -} - -void Observer_multi_observer_table_fill_w_singleton(void) { - ecs_world_t* world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - Probe ctx = {0}; - - ecs_observer(world, { - .filter.terms = { - { .id = ecs_id(Position), .src.flags = EcsIsEntity, .src.id = ecs_id(Position) }, - { .id = ecs_id(Velocity) }, - }, - .filter.flags = EcsFilterNoData, - .callback = Observer, - .events = { EcsOnTableFill }, - .ctx = &ctx - }); - - ecs_singleton_add(world, Position); - test_int(ctx.invoked, 0); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, Velocity); - test_int(ctx.invoked, 0); - - ecs_run_aperiodic(world, 0); - test_int(ctx.invoked, 1); - - ecs_fini(world); -} - -ECS_COMPONENT_DECLARE(Velocity); - -static -void AddVelocity(ecs_iter_t *it) { - for (int i = 0; i < it->count; i ++) { - ecs_add(it->world, it->entities[i], Velocity); - test_assert(!ecs_has(it->world, it->entities[i], Velocity)); - } -} - -void Observer_add_in_yield_existing(void) { - ecs_world_t* world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT_DEFINE(world, Velocity); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_observer(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - }, - .callback = AddVelocity, - .events = { EcsOnAdd }, - .yield_existing = true, - }); - - test_assert(ecs_has(world, e1, Position)); - test_assert(ecs_has(world, e1, Velocity)); - - test_assert(ecs_has(world, e2, Position)); - test_assert(ecs_has(world, e2, Velocity)); - - test_assert(ecs_has(world, e3, Position)); - test_assert(ecs_has(world, e3, Velocity)); - - ecs_fini(world); -} - -void Observer_add_in_yield_existing_multi(void) { - ecs_world_t* world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Mass); - ECS_COMPONENT_DEFINE(world, Velocity); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_add(world, e1, Mass); - ecs_add(world, e2, Mass); - ecs_add(world, e3, Mass); - - ecs_observer(world, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Mass) }, - }, - .callback = AddVelocity, - .events = { EcsOnAdd }, - .yield_existing = true, - }); - - test_assert(ecs_has(world, e1, Position)); - test_assert(ecs_has(world, e1, Mass)); - test_assert(ecs_has(world, e1, Velocity)); - - test_assert(ecs_has(world, e2, Position)); - test_assert(ecs_has(world, e2, Mass)); - test_assert(ecs_has(world, e2, Velocity)); - - test_assert(ecs_has(world, e3, Position)); - test_assert(ecs_has(world, e3, Mass)); - test_assert(ecs_has(world, e3, Velocity)); - - ecs_fini(world); -} - -void Observer_emit_for_parent_w_prefab_child_and_instance(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_COMPONENT(ecs, Position); - - Probe ctx = {0}; - ecs_observer(ecs, { - .filter.terms = { - { .id = ecs_id(Position) } - }, - .events = { EcsOnSet }, - .callback = Observer, - .ctx = &ctx - }); - - ecs_entity_t e = ecs_new_id(ecs); - ecs_entity_t p = ecs_new_w_pair(ecs, EcsChildOf, e); - ecs_entity_t child_1 = ecs_new_w_pair(ecs, EcsChildOf, e); - ecs_add_pair(ecs, child_1, EcsIsA, p); - - test_int(ctx.invoked, 0); - - ecs_set(ecs, e, Position, {10, 20}); - - test_int(ctx.invoked, 1); - test_int(ctx.count, 1); - test_int(ctx.e[0], e); - test_int(ctx.c[0][0], ecs_id(Position)); - test_int(ctx.event, EcsOnSet); - - ecs_fini(ecs); -} - -static -void Observer_w_run_aperiodic(ecs_iter_t *it) { - test_int(it->count, 1); - ecs_run_aperiodic(it->world, 0); -} - -void Observer_cache_test_16(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_TAG(ecs, Foo); - - ECS_OBSERVER(ecs, Observer_w_run_aperiodic, EcsOnAdd, Foo); - - ecs_entity_t p1 = ecs_new_id(ecs); - ecs_entity_t e1 = ecs_new_w_pair(ecs, EcsIsA, p1); - - ecs_run_aperiodic(ecs, 0); - - ecs_entity_t e2 = ecs_new_id(ecs); - ecs_add_pair(ecs, e2, EcsIsA, p1); - ecs_add_pair(ecs, e2, EcsChildOf, e1); - ecs_add(ecs, e1, Foo); - - ecs_fini(ecs); -} diff --git a/vendors/flecs/test/api/src/Poly.c b/vendors/flecs/test/api/src/Poly.c deleted file mode 100644 index 9c7b3c3e1..000000000 --- a/vendors/flecs/test/api/src/Poly.c +++ /dev/null @@ -1,503 +0,0 @@ -#include - -static ECS_COMPONENT_DECLARE(Position); -static ECS_DECLARE(Tag); - -static -void test_no_chain( - ecs_world_t *world, - ecs_poly_t *poly, - ecs_id_t filter) -{ - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {50, 60}); - - ecs_add(world, e3, Tag); - - ecs_iter_t it; - if (filter) { - ecs_term_t term = { .id = filter }; - ecs_iter_poly(world, poly, &it, &term); - } else { - ecs_iter_poly(world, poly, &it, NULL); - } - - test_assert(ecs_iter_next(&it)); - test_int(it.count, 2); - test_int(it.entities[0], e1); - test_int(it.entities[1], e2); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - test_assert(it.table != NULL); - - Position *ptr = ecs_field(&it, Position, 1); - test_assert(ptr != NULL); - test_int(ptr[0].x, 10); - test_int(ptr[0].y, 20); - test_int(ptr[1].x, 30); - test_int(ptr[1].y, 40); - - test_assert(ecs_iter_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_int(ecs_field_id(&it, 1), ecs_id(Position)); - test_int(ecs_field_src(&it, 1), 0); - test_assert(it.table != NULL); - - ptr = ecs_field(&it, Position, 1); - test_assert(ptr != NULL); - test_int(ptr[0].x, 50); - test_int(ptr[0].y, 60); - - test_assert(!ecs_iter_next(&it)); -} - -static -void test_w_chain( - ecs_world_t *world, - ecs_poly_t *poly) -{ - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {30, 40}); - ecs_set(world, 0, Position, {50, 60}); // Not matched - - ecs_add(world, e1, Tag); - ecs_add(world, e2, Tag); - - ecs_iter_t it_arr[2]; - ecs_iter_t *it = it_arr; - ecs_iter_poly(world, poly, it_arr, &(ecs_term_t){ .id = Tag}); - - test_assert(ecs_iter_next(it)); - test_int(it->count, 2); - test_int(it->entities[0], e1); - test_int(it->entities[1], e2); - test_int(ecs_field_id(it, 1), Tag); - test_int(ecs_field_src(it, 1), 0); - test_assert(it->table != NULL); - - ecs_iter_t *chain_it = it->chain_it; - test_assert(chain_it != NULL); - - test_int(chain_it->count, 2); - test_int(chain_it->entities[0], e1); - test_int(chain_it->entities[1], e2); - test_int(ecs_field_id(chain_it, 1), ecs_id(Position)); - test_int(ecs_field_src(chain_it, 1), 0); - - Position *ptr = ecs_field(chain_it, Position, 1); - test_assert(ptr != NULL); - test_int(ptr[0].x, 10); - test_int(ptr[0].y, 20); - test_int(ptr[1].x, 30); - test_int(ptr[1].y, 40); - - test_assert(!ecs_iter_next(it)); -} - -void Poly_iter_query(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_TAG_DEFINE(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - test_no_chain(world, q, 0); - - ecs_fini(world); -} - -void Poly_iter_query_w_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_TAG_DEFINE(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - test_w_chain(world, q); - - ecs_fini(world); -} - -void Poly_iter_world(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_TAG_DEFINE(world, Tag); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {50, 60}); - ecs_add(world, e3, Tag); - - bool e1_found = false; - bool e2_found = false; - bool e3_found = false; - bool position_found = false; - bool tag_found = false; - - ecs_iter_t it; - ecs_iter_poly(world, world, &it, 0); - - test_assert(ecs_iter_next(&it)); - test_assert(it.count != 0); - - int i; - for (i = 0; i < it.count; i ++) { - test_assert( ecs_is_alive(world, it.entities[i])); - if (it.entities[i] == e1) e1_found = true; - if (it.entities[i] == e2) e2_found = true; - if (it.entities[i] == e3) e3_found = true; - if (it.entities[i] == ecs_id(Position)) position_found = true; - if (it.entities[i] == Tag) tag_found = true; - } - - test_assert(!ecs_iter_next(&it)); - - test_assert(e1_found); - test_assert(e2_found); - test_assert(e3_found); - test_assert(position_found); - test_assert(tag_found); - - ecs_fini(world); -} - -void Poly_iter_world_w_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_TAG_DEFINE(world, Tag); - - test_no_chain(world, world, ecs_id(Position)); - - ecs_fini(world); -} - -void Poly_iter_rule(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_TAG_DEFINE(world, Tag); - - ecs_rule_t *q = ecs_rule_new(world, "Position"); - test_assert(q != NULL); - - test_no_chain(world, q, 0); - - ecs_rule_fini(q); - - ecs_fini(world); -} - -void Poly_iter_rule_w_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_TAG_DEFINE(world, Tag); - - ecs_rule_t *q = ecs_rule_new(world, "Position"); - test_assert(q != NULL); - - test_w_chain(world, q); - - ecs_rule_fini(q); - - ecs_fini(world); -} - -void Poly_iter_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_TAG_DEFINE(world, Tag); - - ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "Position"}); - test_assert(f != NULL); - - test_no_chain(world, f, 0); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -void Poly_iter_filter_w_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT_DEFINE(world, Position); - ECS_TAG_DEFINE(world, Tag); - - ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ - .expr = "Position"}); - test_assert(f != NULL); - - test_w_chain(world, f); - - ecs_filter_fini(f); - - ecs_fini(world); -} - -static -void FooTrigger(ecs_iter_t *it) {} - -static -void PolyTrigger(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); - - EcsPoly *poly = ecs_field(it, EcsPoly, 1); - - test_int(1, it->count); - test_assert(poly->poly != NULL); -} - -void Poly_on_set_poly_observer(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag = ecs_new_id(world); - - Probe ctx = {0}; - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { ecs_pair(ecs_id(EcsPoly), EcsObserver) }, - .events = { EcsOnSet }, - .callback = PolyTrigger, - .ctx = &ctx - }); - - test_int(1, ctx.invoked); - - ecs_os_zeromem(&ctx); - - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ tag }}, - .events = { EcsOnAdd }, - .callback = FooTrigger - }); - - test_int(1, ctx.invoked); - test_int(1, ctx.count); - test_uint(t, ctx.e[0]); - - ecs_fini(world); -} - -void Poly_on_set_poly_query(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag = ecs_new_id(world); - - Probe ctx = {0}; - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { ecs_pair(ecs_id(EcsPoly), EcsQuery) }, - .events = { EcsOnSet }, - .callback = PolyTrigger, - .ctx = &ctx - }); - - test_int(0, ctx.invoked); - - ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ tag }}, - }); - - test_int(1, ctx.invoked); - test_int(1, ctx.count); - - ecs_fini(world); -} - -void Poly_on_set_poly_system(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag = ecs_new_id(world); - - Probe ctx = {0}; - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { ecs_pair(ecs_id(EcsPoly), EcsSystem) }, - .events = { EcsOnSet }, - .callback = PolyTrigger, - .ctx = &ctx - }); - - test_int(0, ctx.invoked); - - ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .query.filter.terms = {{ tag }}, - .callback = FooTrigger - }); - - test_int(1, ctx.invoked); - test_int(1, ctx.count); - test_uint(s, ctx.e[0]); - - ecs_fini(world); -} - -void Poly_iter_filter_from_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new(world, Tag); - - ecs_entity_t qe = ecs_new_id(world); - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ Tag }}, - .entity = qe - }); - - test_assert(f->entity == qe); - - const EcsPoly *poly = ecs_get_pair(world, qe, EcsPoly, EcsQuery); - test_assert(poly != NULL); - test_assert(poly->poly == f); - - ecs_iter_t it; - ecs_iter_poly(world, poly->poly, &it, NULL); - - test_bool(true, ecs_iter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_bool(false, ecs_iter_next(&it)); - - ecs_filter_fini(f); - - test_assert(!ecs_is_alive(world, qe)); - - ecs_fini(world); -} - -void Poly_iter_query_from_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new(world, Tag); - - ecs_entity_t qe = ecs_new_id(world); - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ Tag }}, - .filter.entity = qe - }); - - const EcsPoly *poly = ecs_get_pair(world, qe, EcsPoly, EcsQuery); - test_assert(poly != NULL); - test_assert(poly->poly == q); - - ecs_iter_t it; - ecs_iter_poly(world, poly->poly, &it, NULL); - - test_bool(true, ecs_iter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_bool(false, ecs_iter_next(&it)); - - ecs_query_fini(q); - - test_assert(!ecs_is_alive(world, qe)); - - ecs_fini(world); -} - -void Poly_iter_rule_from_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new(world, Tag); - - ecs_entity_t qe = ecs_new_id(world); - ecs_rule_t *q = ecs_rule(world, { - .terms = {{ Tag }}, - .entity = qe - }); - - const EcsPoly *poly = ecs_get_pair(world, qe, EcsPoly, EcsQuery); - test_assert(poly != NULL); - test_assert(poly->poly == q); - - ecs_iter_t it; - ecs_iter_poly(world, poly->poly, &it, NULL); - - test_bool(true, ecs_iter_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_bool(false, ecs_iter_next(&it)); - - ecs_rule_fini(q); - - test_assert(!ecs_is_alive(world, qe)); - - ecs_fini(world); -} - -void Poly_free_filter_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t qe = ecs_new_id(world); - ecs_filter_t *f = ecs_filter(world, { - .terms = {{ Tag }}, - .entity = qe - }); - - test_assert(f->entity == qe); - - const EcsPoly *poly = ecs_get_pair(world, qe, EcsPoly, EcsQuery); - test_assert(poly != NULL); - test_assert(poly->poly == f); - - ecs_delete(world, qe); - - ecs_fini(world); -} - -void Poly_free_query_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t qe = ecs_new_id(world); - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ Tag }}, - .filter.entity = qe - }); - - const EcsPoly *poly = ecs_get_pair(world, qe, EcsPoly, EcsQuery); - test_assert(poly != NULL); - test_assert(poly->poly == q); - - ecs_delete(world, qe); - - test_assert(!ecs_is_alive(world, qe)); - - ecs_fini(world); -} - -void Poly_free_rule_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t qe = ecs_new_id(world); - ecs_rule_t *q = ecs_rule(world, { - .terms = {{ Tag }}, - .entity = qe - }); - - const EcsPoly *poly = ecs_get_pair(world, qe, EcsPoly, EcsQuery); - test_assert(poly != NULL); - test_assert(poly->poly == q); - - ecs_delete(world, qe); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/Query.c b/vendors/flecs/test/api/src/Query.c deleted file mode 100644 index b081aff73..000000000 --- a/vendors/flecs/test/api/src/Query.c +++ /dev/null @@ -1,9945 +0,0 @@ -#include -#include - -static -int order_by_entity( - ecs_entity_t e1, - const void *ptr1, - ecs_entity_t e2, - const void *ptr2) -{ - return (e1 > e2) - (e1 < e2); -} - -static -uint64_t group_by_first_id( - ecs_world_t *world, - ecs_table_t *table, - ecs_id_t id, - void *ctx) -{ - const ecs_type_t *type = ecs_table_get_type(table); - ecs_id_t *first = type->array; - if (!first) { - return 0; - } - - return first[0]; -} - -static -uint64_t group_by_rel(ecs_world_t *world, ecs_table_t *table, ecs_id_t id, void *ctx) { - ecs_id_t match; - if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &match) != -1) { - return ecs_pair_second(world, match); - } - return 0; -} - -void Query_simple_query_existing_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e = ecs_new(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "TagA"); - 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(e, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_simple_query_2_existing_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - - ecs_query_t *q = ecs_query_new(world, "TagA"); - 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(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_simple_query_new_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "TagA"); - test_assert(q != NULL); - - ecs_entity_t e = ecs_new(world, TagA); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_simple_query_2_new_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_query_t *q = ecs_query_new(world, "TagA"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_simple_query_existing_and_new_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "TagA"); - test_assert(q != NULL); - - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_wildcard_query_existing_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - - ecs_query_t *q = ecs_query_new(world, "(Rel, *)"); - 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(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_wildcard_query_new_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_query_t *q = ecs_query_new(world, "(Rel, *)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_wildcard_query_existing_table_2_results_p_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - ECS_TAG(world, ObjC); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - ecs_add_pair(world, e1, Rel, ObjC); - ecs_add_pair(world, e2, Rel, ObjC); - - ecs_query_t *q = ecs_query_new(world, "(Rel, *)"); - 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(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjC), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjC), ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_wildcard_query_new_table_2_results_p_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - ECS_TAG(world, ObjC); - - ecs_query_t *q = ecs_query_new(world, "(Rel, *)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); - ecs_add_pair(world, e1, Rel, ObjC); - ecs_add_pair(world, e2, Rel, ObjC); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, ObjC), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, ObjC), ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_wildcard_query_2nd_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_add_pair(world, e1, Rel, TgtA); - ecs_add_pair(world, e1, Rel, TgtB); - - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_add_pair(world, e2, Rel, TgtA); - - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_add_pair(world, e3, Rel, TgtA); - ecs_add_pair(world, e3, Rel, TgtC); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ Tag }, { ecs_pair(Rel, EcsWildcard) }} - }); - 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(e2, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtB), it.ids[1]); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtC), it.ids[1]); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_wildcard_query_2nd_term_self(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_add_pair(world, e1, Rel, TgtA); - ecs_add_pair(world, e1, Rel, TgtB); - - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_add_pair(world, e2, Rel, TgtA); - - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_add_pair(world, e3, Rel, TgtA); - ecs_add_pair(world, e3, Rel, TgtC); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ Tag }, { ecs_pair(Rel, EcsWildcard), .src.flags = EcsSelf }} - }); - 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(e2, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtB), it.ids[1]); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtA), it.ids[1]); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(Tag, it.ids[0]); - test_uint(ecs_pair(Rel, TgtC), it.ids[1]); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_simple_query_existing_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add(world, e1, TagB); - - ecs_query_t *q = ecs_query_new(world, "TagA"); - test_assert(q != NULL); - - ecs_remove(world, e1, TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_simple_query_existing_empty_type(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_PREFAB(world, TypeX, TagA, TagB); - - ecs_query_t *q = ecs_query_new(world, "TagA"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add(world, e1, TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_simple_query_new_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_query_t *q = ecs_query_new(world, "TagA"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_add(world, e1, TagB); - ecs_remove(world, e1, TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_component_query_existing_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - 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(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_assert(ecs_field_size(&it, 1) == sizeof(Position)); - test_assert(ecs_field(&it, Position, 1) != NULL); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_component_query_new_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_entity_t e = ecs_new(world, Position); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - test_assert(ecs_field(&it, Position, 1) != NULL); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_component_query_existing_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, TagA); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_remove(world, e, TagA); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - test_assert(ecs_field(&it, Position, 1) != NULL); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_2_component_query_existing_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_TAG(world, Tag); - ECS_PREFAB(world, MyType, Position, Velocity); - - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - ecs_add(world, e, Tag); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - test_assert(q != NULL); - - ecs_remove(world, e, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - test_assert(ecs_field(&it, Position, 1) != NULL); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_2_component_query_existing_empty_type(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_PREFAB(world, MyType, Position, Velocity); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - test_assert(q != NULL); - - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_field_size(&it, 1), sizeof(Position)); - test_assert(ecs_field(&it, Position, 1) != NULL); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_only_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "?TagA"); - test_assert(q != NULL); - - ecs_entity_t e = ecs_new(world, TagA); - int32_t count = 0; - - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - if (ecs_field_is_set(&it, 1)) { - test_assert(count == 0); - test_int(it.count, 1); - test_uint(it.entities[0], e); - count ++; - } - } - - test_int(count, 1); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_only_optional_new_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e = ecs_new(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "?TagA"); - test_assert(q != NULL); - - int32_t count = 0, total_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - if (ecs_field_is_set(&it, 1)) { - test_assert(count == 0); - test_int(it.count, 1); - test_uint(it.entities[0], e); - count ++; - } - total_count ++; - } - - test_int(count, 1); - test_assert(total_count >= count); - - int32_t prev_total_count = total_count; - total_count = 0; - - ecs_remove(world, e, TagA); - - it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(!ecs_field_is_set(&it, 1)); - test_assert(it.count > 0); - total_count ++; - } - - test_assert(total_count == (prev_total_count - 1)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_only_optional_new_empty_non_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e = ecs_new(world, TagA); - ecs_add(world, e, TagB); - - ecs_query_t *q = ecs_query_new(world, "?TagA"); - test_assert(q != NULL); - - int32_t count = 0, total_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - if (ecs_field_is_set(&it, 1)) { - test_assert(count == 0); - test_int(it.count, 1); - test_uint(it.entities[0], e); - count ++; - } - total_count ++; - } - - test_int(count, 1); - test_assert(total_count >= count); - - int32_t prev_total_count = total_count; - count = 0; total_count = 0; - - ecs_remove(world, e, TagA); - ecs_table_t *table = ecs_get_table(world, e); - - it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(!ecs_field_is_set(&it, 1)); - test_assert(it.count > 0); - total_count ++; - if (it.entities[0] == e) { - test_assert(table == it.table); - count ++; - } - } - - test_int(count, 1); - test_assert(total_count == prev_total_count); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_only_optional_new_unset_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e = ecs_new(world, TagA); - ecs_add(world, e, TagB); - ecs_table_t *table = ecs_get_table(world, e); - - ecs_query_t *q = ecs_query_new(world, "?TagC"); - test_assert(q != NULL); - - int32_t count = 0, total_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(!ecs_field_is_set(&it, 1)); - test_assert(it.count > 0); - if (it.entities[0] == e) { - test_assert(table == it.table); - count ++; - } - total_count ++; - } - - test_int(count, 1); - test_assert(total_count >= count); - - int32_t prev_total_count = total_count; - count = 0; total_count = 0; - - ecs_remove(world, e, TagA); - table = ecs_get_table(world, e); - - it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(!ecs_field_is_set(&it, 1)); - test_assert(it.count > 0); - total_count ++; - if (it.entities[0] == e) { - test_assert(table == it.table); - count ++; - } - } - - test_int(count, 1); - test_assert(total_count == prev_total_count); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_singleton_w_optional_new_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Singleton); - ECS_TAG(world, TagA); - - ecs_singleton_add(world, Singleton); - - ecs_entity_t e = ecs_new(world, TagA); - ecs_set_name(world, e, "e"); - - ecs_query_t *q = ecs_query_new(world, "Singleton($), ?TagA"); - test_assert(q != NULL); - - int32_t count = 0, total_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - if (ecs_field_is_set(&it, 2)) { - test_assert(count == 0); - test_int(it.count, 1); - test_uint(it.entities[0], e); - count ++; - } - total_count ++; - } - - test_int(count, 1); - test_assert(total_count >= count); - - int32_t prev_total_count = total_count; - total_count = 0; - - ecs_remove(world, e, TagA); - - it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(!ecs_field_is_set(&it, 2)); - test_assert(it.count > 0); - total_count ++; - } - - test_assert(total_count == (prev_total_count - 1)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_singleton_w_optional_new_empty_non_empty_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Singleton); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_singleton_add(world, Singleton); - - ecs_entity_t e = ecs_new(world, TagA); - ecs_add(world, e, TagB); - - ecs_query_t *q = ecs_query_new(world, "Singleton($), ?TagA"); - test_assert(q != NULL); - - int32_t count = 0, total_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - if (ecs_field_is_set(&it, 2)) { - test_assert(count == 0); - test_int(it.count, 1); - test_uint(it.entities[0], e); - count ++; - } - total_count ++; - } - - test_int(count, 1); - test_assert(total_count >= count); - - int32_t prev_total_count = total_count; - count = 0; total_count = 0; - - ecs_remove(world, e, TagA); - ecs_table_t *table = ecs_get_table(world, e); - - it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(!ecs_field_is_set(&it, 2)); - test_assert(it.count > 0); - total_count ++; - if (it.entities[0] == e) { - test_assert(table == it.table); - count ++; - } - } - - test_int(count, 1); - test_assert(total_count == prev_total_count); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_singleton_w_optional_new_unset_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Singleton); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_singleton_add(world, Singleton); - - ecs_entity_t e = ecs_new(world, TagA); - ecs_add(world, e, TagB); - ecs_table_t *table = ecs_get_table(world, e); - - ecs_query_t *q = ecs_query_new(world, "Singleton($), ?TagC"); - test_assert(q != NULL); - - int32_t count = 0, total_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(!ecs_field_is_set(&it, 2)); - test_assert(it.count > 0); - if (it.entities[0] == e) { - test_assert(table == it.table); - count ++; - } - total_count ++; - } - - test_int(count, 1); - test_assert(total_count >= count); - - int32_t prev_total_count = total_count; - count = 0; total_count = 0; - - ecs_remove(world, e, TagA); - table = ecs_get_table(world, e); - - it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(!ecs_field_is_set(&it, 2)); - test_assert(it.count > 0); - total_count ++; - if (it.entities[0] == e) { - test_assert(table == it.table); - count ++; - } - } - - test_int(count, 1); - test_assert(total_count == prev_total_count); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_query_only_from_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new_entity(world, "e"); - ecs_add(world, e, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(e)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 0); - test_uint(ecs_field_src(&it, 1), e); - test_uint(ecs_field_id(&it, 1), Tag); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_entity_no_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new_entity(world, "e"); - - ecs_query_t *q = ecs_query_new(world, "Tag(e)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - ecs_add(world, e, Tag); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 0); - test_uint(ecs_field_src(&it, 1), e); - test_uint(ecs_field_id(&it, 1), Tag); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_entity_no_match_iter_alloc(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, A); - ECS_TAG(world, B); - ECS_TAG(world, C); - ECS_TAG(world, D); - ECS_TAG(world, E); - - ecs_new_entity(world, "e"); - - ecs_query_t *q = ecs_query_new(world, "A(e), B(e), C(e), D(e), E(e)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_singleton_add(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag($)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 0); - test_uint(ecs_field_src(&it, 1), Tag); - test_uint(ecs_field_id(&it, 1), Tag); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_entity_match_after(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new_entity(world, "e"); - - ecs_query_t *q = ecs_query_new(world, "Tag(e)"); - test_assert(q != NULL); - - ecs_add(world, e, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 0); - test_uint(ecs_field_src(&it, 1), e); - test_uint(ecs_field_id(&it, 1), Tag); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_singleton_match_after(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag($)"); - test_assert(q != NULL); - - ecs_singleton_add(world, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 0); - test_uint(ecs_field_src(&it, 1), Tag); - test_uint(ecs_field_id(&it, 1), Tag); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_singleton_component_match_after(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position($)"); - test_assert(q != NULL); - - ecs_singleton_add(world, Position); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 0); - test_uint(ecs_field_src(&it, 1), ecs_id(Position)); - test_uint(ecs_field_id(&it, 1), ecs_id(Position)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_nothing(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag()"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 0); - test_uint(ecs_field_id(&it, 1), Tag); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_entity_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ECS_ENTITY(world, Ent, Position); - - ecs_set(world, Ent, Position, {10, 20}); - - ecs_query_t *q = ecs_query_new(world, "?Position(Ent)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 0); - - test_bool(ecs_field_is_set(&it, 1), true); - - Position *ptr = ecs_field(&it, Position, 1); - test_assert(ptr != NULL); - test_int(ptr->x, 10); - test_int(ptr->y, 20); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_entity_no_match_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_new_entity(world, "Ent"); - - ecs_query_t *q = ecs_query_new(world, "?Position(Ent)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 0); - test_bool(ecs_field_is_set(&it, 1), false); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_entity_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t Ent = ecs_new_entity(world, "Ent"); - ecs_set(world, Ent, Position, {10, 20}); - - ecs_query_t *q = ecs_query_new(world, "Position(Ent) || Velocity(Ent)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 0); - - test_bool(ecs_field_is_set(&it, 1), true); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_entity_no_match_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ECS_ENTITY(world, Ent, Mass); - - ecs_query_t *q = ecs_query_new(world, "Position(Ent) || Velocity(Ent)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_from_entity_or_change(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t Ent = ecs_new_entity(world, "Ent"); - ecs_set(world, Ent, Position, {10, 20}); - - ecs_query_t *q = ecs_query_new(world, "Position(Ent) || Velocity(Ent)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 0); - - test_bool(ecs_field_is_set(&it, 1), true); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(Ent, it.sources[0]); - test_assert(!ecs_query_next(&it)); - - ecs_remove(world, Ent, Position); - ecs_set(world, Ent, Velocity, {1, 2}); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 0); - - test_bool(ecs_field_is_set(&it, 1), true); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); - test_uint(Ent, it.sources[0]); - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_from_entity_or_change(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_TAG(world, Tag); - - ecs_entity_t Ent = ecs_new_entity(world, "Ent"); - ecs_set(world, Ent, Position, {10, 20}); - - ecs_entity_t e = ecs_new(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Position(Ent) || Velocity(Ent), Tag"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - - test_bool(ecs_field_is_set(&it, 1), true); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e, it.entities[0]); - test_uint(Ent, it.sources[0]); - test_uint(0, it.sources[1]); - test_assert(!ecs_query_next(&it)); - - ecs_remove(world, Ent, Position); - ecs_set(world, Ent, Velocity, {1, 2}); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - - test_bool(ecs_field_is_set(&it, 1), true); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); - test_uint(e, it.entities[0]); - test_uint(Ent, it.sources[0]); - test_uint(0, it.sources[1]); - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_from_entity_w_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t g = ecs_new_entity(world, "Game"); - ecs_set(world, g, Position, {10, 20}); - ecs_set(world, g, Velocity, {1, 2}); - - ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); - ecs_set(world, p, Mass, {30}); - ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, p); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { .id = ecs_id(Velocity), .src.name = "Game" }, - { .id = ecs_id(Mass), .src.flags = EcsUp } - } - }); - - 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(e, it.entities[0]); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_singleton_tag_non_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Singleton); - ECS_COMPONENT(world, Position); - - ecs_singleton_add(world, Singleton); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_new(world, "Position, Singleton($)"); - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(true, ecs_query_next(&it)); - test_int(4, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - test_uint(e4, it.entities[3]); - test_uint(0, it.sources[0]); - test_uint(Singleton, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_int(p[2].x, 30); - test_int(p[2].y, 40); - test_int(p[3].x, 40); - test_int(p[3].y, 50); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_singleton_tag_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Singleton); - ECS_COMPONENT(world, Position); - - ecs_singleton_add(world, Singleton); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .expr = "Position, Singleton($)", - .instanced = true - }}); - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(true, ecs_query_next(&it)); - test_int(4, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - test_uint(e4, it.entities[3]); - test_uint(0, it.sources[0]); - test_uint(Singleton, it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_int(p[2].x, 30); - test_int(p[2].y, 40); - test_int(p[3].x, 40); - test_int(p[3].y, 50); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_singleton_component_non_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity($)"); - ecs_iter_t it = ecs_query_iter(world, q); - - Position *p; - Velocity *v; - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(ecs_id(Velocity), it.sources[1]); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - v = ecs_field(&it, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(true, ecs_query_next(&it)); - - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(ecs_id(Velocity), it.sources[1]); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - v = ecs_field(&it, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(true, ecs_query_next(&it)); - - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(0, it.sources[0]); - test_uint(ecs_id(Velocity), it.sources[1]); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - v = ecs_field(&it, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_bool(true, ecs_query_next(&it)); - - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(0, it.sources[0]); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 40); - test_int(p[0].y, 50); - v = ecs_field(&it, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_uint(ecs_id(Velocity), it.sources[1]); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_singleton_component_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .expr = "Position, Velocity($)", - .instanced = true - }}); - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(true, ecs_query_next(&it)); - test_int(4, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(e3, it.entities[2]); - test_uint(e4, it.entities[3]); - test_uint(0, it.sources[0]); - test_uint(ecs_id(Velocity), it.sources[1]); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_int(p[2].x, 30); - test_int(p[2].y, 40); - test_int(p[3].x, 40); - test_int(p[3].y, 50); - - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_from_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_entity(world, "e"); - ecs_add(world, e1, TagB); - - ecs_entity_t e2 = ecs_new(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "TagA, TagB(e)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_src(&it, 1), 0); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_src(&it, 2), e1); - test_uint(ecs_field_id(&it, 2), TagB); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_from_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_singleton_add(world, TagB); - - ecs_entity_t e2 = ecs_new(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "TagA, TagB($)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_src(&it, 1), 0); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_src(&it, 2), TagB); - test_uint(ecs_field_id(&it, 2), TagB); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_from_entity_match_after(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_entity(world, "e"); - ecs_entity_t e2 = ecs_new(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "TagA, TagB(e)"); - test_assert(q != NULL); - - ecs_add(world, e1, TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_src(&it, 1), 0); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_src(&it, 2), e1); - test_uint(ecs_field_id(&it, 2), TagB); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_from_singleton_match_after(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e2 = ecs_new(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "TagA, TagB($)"); - test_assert(q != NULL); - - ecs_singleton_add(world, TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_src(&it, 1), 0); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_src(&it, 2), TagB); - test_uint(ecs_field_id(&it, 2), TagB); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_from_nothing(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e2 = ecs_new(world, TagA); - - ecs_query_t *q = ecs_query_new(world, "TagA, TagB()"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_src(&it, 1), 0); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_src(&it, 2), 0); - test_uint(ecs_field_id(&it, 2), TagB); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_existing_switch_and_case(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - - ecs_query_t *q = ecs_query_new(world, "(Movement, *)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - - test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, EcsWildcard)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_new_switch_and_case(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_query_t *q = ecs_query_new(world, "(Movement, *)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Running); - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 3); - test_uint(it.entities[0], e1); - test_uint(it.entities[1], e2); - test_uint(it.entities[2], e3); - test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, EcsWildcard)); - test_assert(it.sizes != NULL); - test_int(it.sizes[0], ECS_SIZEOF(ecs_entity_t)); - test_assert(it.ptrs != NULL); - ecs_entity_t *cases = ecs_field(&it, ecs_entity_t, 1); - test_uint(cases[0], Walking); - test_uint(cases[1], Running); - test_uint(cases[2], Walking); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_for_case_existing(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - ecs_new_w_pair(world, Movement, Running); - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Walking)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, Walking)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, Walking)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_for_case_new(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - ecs_new_w_pair(world, Movement, Running); - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, Walking)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, Walking)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_case_w_generation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Union); - - ecs_entity_t tgt_1 = ecs_new_id(world); - ecs_delete(world, tgt_1); - tgt_1 = ecs_new_id(world); - test_assert(tgt_1 != (uint32_t)tgt_1); - - ecs_entity_t tgt_2 = ecs_new_id(world); - ecs_delete(world, tgt_2); - tgt_2 = ecs_new_id(world); - test_assert(tgt_2 != (uint32_t)tgt_2); - - ecs_query_t *q = ecs_query_new(world, "(Rel, *)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, tgt_1); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, tgt_2); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e1); - test_uint(it.entities[1], e2); - test_uint(ecs_field_id(&it, 1), ecs_pair(Rel, EcsWildcard)); - ecs_entity_t *cases = ecs_field(&it, ecs_entity_t, 1); - test_assert(cases != NULL); - test_uint(cases[0], tgt_1); - test_uint(cases[1], tgt_2); - } - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_case_w_not_alive(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Union); - - ecs_entity_t tgt_1 = ecs_new_id(world); - ecs_delete(world, tgt_1); - tgt_1 = ecs_new_id(world); - test_assert(tgt_1 != (uint32_t)tgt_1); - - ecs_entity_t tgt_2 = ecs_new_id(world); - ecs_delete(world, tgt_2); - tgt_2 = ecs_new_id(world); - test_assert(tgt_2 != (uint32_t)tgt_2); - - ecs_query_t *q = ecs_query_new(world, "(Rel, *)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, tgt_1); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, tgt_2); - - ecs_delete(world, tgt_1); - ecs_delete(world, tgt_2); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e1); - test_uint(it.entities[1], e2); - test_uint(ecs_field_id(&it, 1), ecs_pair(Rel, EcsWildcard)); - ecs_entity_t *cases = ecs_field(&it, ecs_entity_t, 1); - test_assert(cases != NULL); - test_uint(cases[0], tgt_1); - test_uint(cases[1], tgt_2); - } - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_for_switch_filter_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_query_t *q = ecs_query_new(world, "[none] (Movement, *)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, EcsWildcard)); - test_int(it.sizes[0], ECS_SIZEOF(ecs_entity_t)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_switch_from_nothing(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .terms = { - {Tag}, - { ecs_pair(Movement, EcsWildcard), .src.flags = EcsIsEntity } - } - }}); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_id(world, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_field_id(&it, 1), Tag); - test_uint(ecs_field_id(&it, 2), ecs_pair(Movement, EcsWildcard)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_case_from_nothing(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .terms = { - {Tag}, - {ecs_pair(Movement, Walking), .src.flags = EcsIsEntity } - } - }}); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_id(world, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_field_id(&it, 1), Tag); - test_uint(ecs_field_id(&it, 2), ecs_pair(Movement, Walking)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_case_inherited(void) { - test_quarantine("Aug 1st 2022"); - - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_entity_t base_1 = ecs_new_w_pair(world, Movement, Walking); - ecs_entity_t inst_1 = ecs_new_w_pair(world, EcsIsA, base_1); - - ecs_entity_t base_2 = ecs_new_w_pair(world, Movement, Running); - ecs_new_w_pair(world, EcsIsA, base_2); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .terms = { - { ecs_pair(Movement, Walking) } - } - }}); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], base_1); - test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, Walking)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst_1); - test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, Walking)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_disabled_from_nothing(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .terms = { - {TagA}, - {TagB, .src.flags = EcsIsEntity} - } - }}); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_id(world, TagA); - ecs_add(world, e1, TagB); - ecs_add_id(world, e1, ECS_TOGGLE | TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_field_id(&it, 1), TagA); - test_uint(ecs_field_id(&it, 2), TagB); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_2_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_query_t *q = ecs_query_new(world, "TagA || TagB"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, TagB); - ecs_new(world, TagC); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(TagB, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_only_3_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .expr = "TagA || TagB || TagC" - }}); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, TagB); - ecs_entity_t e4 = ecs_new(world, TagC); - ecs_new(world, TagD); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(TagB, ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(TagC, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_query_2_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Foo); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .expr = "TagA || TagB, Foo" - }}); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, TagB); - - ecs_add(world, e1, Foo); - ecs_add(world, e2, Foo); - ecs_add(world, e3, Foo); - - ecs_new(world, TagB); - ecs_new(world, TagC); - ecs_new(world, Foo); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(TagB, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_query_3_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, Foo); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .expr = "TagA || TagB || TagC, Foo" - }}); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add(world, e2, TagB); - ecs_entity_t e3 = ecs_new(world, TagB); - ecs_add(world, e3, TagC); - ecs_entity_t e4 = ecs_new(world, TagC); - - ecs_add(world, e1, Foo); - ecs_add(world, e2, Foo); - ecs_add(world, e3, Foo); - ecs_add(world, e4, Foo); - - ecs_new(world, TagB); - ecs_new(world, TagC); - ecs_new(world, Foo); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(TagB, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(TagC, ecs_field_id(&it, 1)); - test_uint(Foo, ecs_field_id(&it, 2)); - - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_query_and_type(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_PREFAB(world, TypeX, TagA, TagB); - - ecs_entity_t e1 = ecs_new_w_id(world, TagA); - ecs_add(world, e1, TagB); - ecs_table_t *table1 = ecs_get_table(world, e1); - - ecs_entity_t e2 = ecs_new_w_id(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e2, Foo); - ecs_table_t *table2 = ecs_get_table(world, e2); - - /* Not matched */ - ecs_new(world, TagA); - ecs_new(world, TagB); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .expr = "AND | TypeX" - }}); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.ids[0], TypeX); - test_assert(it.table == table1); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.ids[0], TypeX); - test_assert(it.table == table2); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_query_or_type(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_PREFAB(world, TypeX, TagA, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_table_t *table1 = ecs_get_table(world, e1); - - ecs_entity_t e2 = ecs_new(world, TagB); - ecs_table_t *table2 = ecs_get_table(world, e2); - - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add(world, e3, TagB); - ecs_table_t *table3 = ecs_get_table(world, e3); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .expr = "OR | TypeX" - }}); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.ids[0], TagA); - test_assert(it.table == table1); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.ids[0], TagB); - test_assert(it.table == table2); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.ids[0], TagA); - test_assert(it.table == table3); - - test_bool(ecs_query_next(&it), false); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_query_and_type_match_after(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_PREFAB(world, TypeX, TagA, TagB); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .expr = "AND | TypeX" - }}); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_id(world, TagA); - ecs_add(world, e1, TagB); - ecs_table_t *table1 = ecs_get_table(world, e1); - - ecs_entity_t e2 = ecs_new_w_id(world, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e2, Foo); - ecs_table_t *table2 = ecs_get_table(world, e2); - - /* Not matched */ - ecs_new(world, TagA); - ecs_new(world, TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.ids[0], TypeX); - test_assert(it.table == table1); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.ids[0], TypeX); - test_assert(it.table == table2); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_query_or_type_match_after(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_PREFAB(world, TypeX, TagA, TagB); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ .filter = { - .expr = "OR | TypeX" - }}); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_table_t *table1 = ecs_get_table(world, e1); - - ecs_entity_t e2 = ecs_new(world, TagB); - ecs_table_t *table2 = ecs_get_table(world, e2); - - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add(world, e3, TagB); - ecs_table_t *table3 = ecs_get_table(world, e3); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.ids[0], TagA); - test_assert(it.table == table1); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.ids[0], TagB); - test_assert(it.table == table2); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.ids[0], TagA); - test_assert(it.table == table3); - - test_bool(ecs_query_next(&it), false); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_query_changed_after_new(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_new(world, Position); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_changed_after_delete(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_delete(world, e1); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_changed_after_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new(world, 0); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_add(world, e1, Position); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_changed_after_remove(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_remove(world, e1, Position); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_changed_after_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_set(world, e1, Position, {10, 20}); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_change_after_modified(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e1 = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_modified(world, e1, Position); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Sys(ecs_iter_t *it) { } - -void Query_query_change_after_out_system(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_SYSTEM(world, Sys, EcsOnUpdate, [out] Position); - - ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_progress(world, 0); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_change_after_in_system(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_SYSTEM(world, Sys, EcsOnUpdate, [in] Position); - - ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_progress(world, 0); - test_assert(ecs_query_changed(q, 0) == false); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == false); - ecs_iter_fini(&it); - - ecs_fini(world); -} - -void Query_query_change_after_modified_out_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_new(world, Position); - ecs_add(world, e, Velocity); - - ecs_query_t *q = ecs_query_new(world, "[in] Position, [out] Velocity"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_set(world, e, Position, {10, 20}); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_set(world, e, Velocity, {1, 2}); - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_change_check_iter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_add(world, e1, TagA); - ecs_add(world, e2, TagB); - ecs_add(world, e3, TagC); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { - test_bool(ecs_query_changed(q, &it), true); - } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_modified(world, e1, Position); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e1); - test_bool(ecs_query_changed(q, &it), true); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e2); - test_bool(ecs_query_changed(q, &it), false); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_bool(ecs_query_changed(q, &it), false); - test_bool(ecs_query_changed(q, NULL), false); - test_bool(ecs_query_next(&it), false); - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == false); - while (ecs_query_next(&it)) { - test_bool(ecs_query_changed(q, &it), false); - } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_modified(world, e2, Position); - - - it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e1); - test_bool(ecs_query_changed(q, &it), false); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e2); - test_bool(ecs_query_changed(q, &it), true); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_bool(ecs_query_changed(q, &it), false); - test_bool(ecs_query_changed(q, NULL), false); - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_query_change_check_iter_after_skip_read(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_set(world, e, Position, {10, 20}); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_assert(ecs_query_changed(q, &it) == true); - ecs_query_skip(&it); - test_bool(ecs_query_next(&it), false); - - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_assert(ecs_query_changed(q, &it) == true); - test_bool(ecs_query_next(&it), false); - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_change_check_iter_after_skip_write(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_new(world, Position); - - ecs_query_t *qw = ecs_query_new(world, "[out] Position"); - - ecs_query_t *q = ecs_query_new(world, "[in] Position"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - it = ecs_query_iter(world, qw); - test_assert(ecs_query_changed(q, 0) == false); - test_bool(ecs_query_next(&it), true); - test_assert(ecs_query_changed(q, 0) == false); - ecs_query_skip(&it); - test_bool(ecs_query_next(&it), false); - test_assert(ecs_query_changed(q, 0) == false); - - test_assert(ecs_query_changed(q, 0) == false); - - it = ecs_query_iter(world, qw); - test_assert(ecs_query_changed(q, 0) == false); - test_bool(ecs_query_next(&it), true); - test_assert(ecs_query_changed(q, 0) == false); - test_bool(ecs_query_next(&it), false); - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_assert(ecs_query_changed(q, &it) == true); - test_bool(ecs_query_next(&it), false); - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_change_parent_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_new(world, Position); - ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_query_t *q = ecs_query_new(world, "[in] Position(parent)"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_set(world, parent, Position, {10, 20}); - - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_assert(ecs_query_changed(q, &it) == true); - test_bool(ecs_query_next(&it), false); - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_change_prefab_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base = ecs_new(world, Position); - ecs_new_w_pair(world, EcsIsA, base); - - ecs_query_t *q = ecs_query_new(world, "[in] Position(up)"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_set(world, base, Position, {10, 20}); - - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_assert(ecs_query_changed(q, &it) == true); - test_bool(ecs_query_next(&it), false); - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_change_parent_term_w_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_new(world, Position); - ecs_add_id(world, parent, EcsPrefab); - ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_query_t *q = ecs_query_new(world, "[in] Position(parent), ?Prefab"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_set(world, parent, Position, {10, 20}); - - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_assert(ecs_query_changed(q, &it) == true); - test_bool(ecs_query_next(&it), false); - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_change_prefab_term_w_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base = ecs_new(world, Position); - ecs_add_id(world, base, EcsPrefab); - ecs_new_w_pair(world, EcsIsA, base); - - ecs_query_t *q = ecs_query_new(world, "[in] Position(up)"); - test_assert(q != NULL); - test_assert(ecs_query_changed(q, 0) == true); - test_assert(ecs_query_changed(q, 0) == true); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_changed(q, 0) == true); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q, 0) == false); - - ecs_set(world, base, Position, {10, 20}); - - test_assert(ecs_query_changed(q, 0) == true); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_assert(ecs_query_changed(q, &it) == true); - test_bool(ecs_query_next(&it), false); - test_assert(ecs_query_changed(q, 0) == false); - - ecs_fini(world); -} - -void Query_query_change_skip_non_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_TAG(world, Tag); - - ecs_entity_t base = ecs_new(world, Position); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, base); - - ecs_add(world, e1, Velocity); - ecs_add(world, e2, Velocity); - ecs_add(world, e3, Velocity); - ecs_add(world, e4, Velocity); - - ecs_add(world, e3, Tag); - ecs_add(world, e4, Tag); - - ecs_query_t *q = ecs_query_new(world, "Position(up), [out] Velocity"); - test_assert(q != NULL); - - ecs_query_t *q_r = ecs_query_new(world, "Position(up), [in] Velocity"); - test_assert(q_r != NULL); - - test_assert(ecs_query_changed(q_r, 0) == true); - ecs_iter_t it = ecs_query_iter(world, q_r); - while (ecs_query_next(&it)) { } - test_assert(ecs_query_changed(q_r, 0) == false); - - it = ecs_query_iter(world, q); - int32_t count = 0; - - while (ecs_query_next(&it)) { - test_int(it.count, 1); - - if (it.entities[0] == e1) { - ecs_query_skip(&it); /* Skip one entity in table 1 */ - } - if (it.entities[0] == e3 || it.entities[0] == e4) { - ecs_query_skip(&it); /* Skip both entities in table 1 */ - } - - count ++; - } - - test_int(count, 4); - - test_bool(ecs_query_changed(q_r, NULL), true); - - it = ecs_query_iter(world, q_r); - count = 0; - - while (ecs_query_next(&it)) { - test_int(it.count, 1); - - if (it.entities[0] == e1 || it.entities[0] == e2) { - /* Table changed, not all entities were skipped */ - test_bool( ecs_query_changed(0, &it), true ); - } - if (it.entities[0] == e3 || it.entities[0] == e4) { - /* Table did not change, all entities were skipped */ - test_bool( ecs_query_changed(0, &it), false ); - } - - count ++; - } - - test_int(count, 4); - - test_bool(ecs_query_changed(q_r, NULL), false); - - ecs_fini(world); -} - -void Query_query_changed_w_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, Position); - ecs_add(world, e, Velocity); - ecs_add(world, e, TagA); - ecs_add(world, e, TagB); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_id(Position), .inout = EcsIn }, - { TagA, .oper = EcsOr }, - { TagB }, - { ecs_id(Velocity), .inout = EcsIn } - } - }); - test_assert(q != NULL); - - test_bool(true, ecs_query_changed(q, NULL)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_set(world, e, Position, {10, 20}); - test_bool(true, ecs_query_changed(q, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_set(world, e, Velocity, {10, 20}); - test_bool(true, ecs_query_changed(q, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_fini(world); -} - -void Query_query_changed_or(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add(world, e1, Position); - ecs_add(world, e1, TagA); - ecs_add(world, e1, TagB); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add(world, e2, Velocity); - ecs_add(world, e2, TagA); - ecs_add(world, e2, TagB); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { TagA }, - { ecs_id(Position), .inout = EcsIn, .oper = EcsOr }, - { ecs_id(Velocity), .inout = EcsIn }, - { TagB } - } - }); - test_assert(q != NULL); - - test_bool(true, ecs_query_changed(q, NULL)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_set(world, e1, Position, {10, 20}); - test_bool(true, ecs_query_changed(q, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_set(world, e2, Velocity, {10, 20}); - test_bool(true, ecs_query_changed(q, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_fini(world); -} - -void Query_query_changed_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add(world, e1, TagA); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { TagA } - } - }); - test_assert(q != NULL); - - test_bool(true, ecs_query_changed(q, NULL)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_fini(world); -} - -void Query_query_changed_no_source(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add(world, e1, TagA); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { TagA }, - { TagB, .src.flags = EcsIsEntity, .inout = EcsOut } - } - }); - test_assert(q != NULL); - - test_bool(true, ecs_query_changed(q, NULL)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_fini(world); -} - -void Query_query_changed_no_source_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add(world, e1, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_id(Position), .inout = EcsIn }, - { ecs_id(Velocity), .src.flags = EcsIsEntity, .inout = EcsOut } - } - }); - test_assert(q != NULL); - - test_bool(true, ecs_query_changed(q, NULL)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_fini(world); -} - -void Query_query_changed_w_not_out(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add(world, e1, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_id(Position), .inout = EcsIn }, - { ecs_id(Velocity), .inout = EcsOut, .oper = EcsNot } - } - }); - test_assert(q != NULL); - - test_bool(true, ecs_query_changed(q, NULL)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(q, NULL)); - - ecs_fini(world); -} - -void Query_query_changed_w_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position) - }, { - .id = ecs_id(Velocity), - .src.id = ecs_id(Velocity) - }} - }); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_uint(e, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(p->x, 10); - test_int(p->y, 20); - test_int(v->x, 1); - test_int(v->y, 2); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_changed_w_only_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_singleton_set(world, Position, {1, 2}); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .src.id = ecs_id(Position) - }} - }); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - ecs_fini(world); -} - -void Query_query_changed_w_only_singleton_after_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_singleton_set(world, Position, {1, 2}); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), .src.id = ecs_id(Position) - }} - }); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - ecs_singleton_set(world, Position, {3, 4}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 3); - test_int(p->y, 4); - test_bool(false, ecs_query_next(&it)); - } - - ecs_fini(world); -} - -void Query_query_changed_w_only_singleton_after_out_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_singleton_set(world, Position, {1, 2}); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), .src.id = ecs_id(Position) - }} - }); - - ecs_query_t *q_write = ecs_query(world, { - .filter.terms = { - { ecs_id(Position), .inout = EcsOut } - } - }); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q_write); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - Position *p = ecs_field(&it, Position, 1); - p->x = 3; - p->y = 4; - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 3); - test_int(p->y, 4); - test_bool(false, ecs_query_next(&it)); - } - - ecs_fini(world); -} - -void Query_query_changed_w_only_singleton_after_singleton_out_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_singleton_set(world, Position, {1, 2}); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), .src.id = ecs_id(Position) - }} - }); - - ecs_query_t *q_write = ecs_query(world, { - .filter.terms = { - { ecs_id(Position), .src.id = ecs_id(Position), .inout = EcsOut } - } - }); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q_write); - test_bool(true, ecs_query_next(&it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - p->x = 3; - p->y = 4; - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(0, it.count); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 3); - test_int(p->y, 4); - test_bool(false, ecs_query_next(&it)); - } - - ecs_fini(world); -} - -void Query_query_changed_w_only_parent(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .src.flags = EcsParent - }} - }); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - ecs_fini(world); -} - -void Query_query_changed_w_only_parent_after_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .src.flags = EcsParent - }} - }); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - ecs_set(world, parent, Position, {3, 4}); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 3); - test_int(p->y, 4); - test_bool(false, ecs_query_next(&it)); - } - - ecs_fini(world); -} - -void Query_query_changed_w_only_parent_after_out_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .src.flags = EcsParent - }} - }); - - ecs_query_t *q_write = ecs_query(world, { - .filter.terms = { - { ecs_id(Position), .inout = EcsOut } - } - }); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q_write); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(parent, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - p->x = 3; - p->y = 4; - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 3); - test_int(p->y, 4); - test_bool(false, ecs_query_next(&it)); - } - - ecs_fini(world); -} - -void Query_query_changed_w_only_parent_after_parent_out_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .src.flags = EcsParent - }} - }); - - ecs_query_t *q_write = ecs_query(world, { - .filter.terms = { - { ecs_id(Position), .src.flags = EcsParent, .inout = EcsOut } - } - }); - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 1); - test_int(p->y, 2); - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q_write); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - p->x = 3; - p->y = 4; - test_bool(false, ecs_query_next(&it)); - } - - { - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - Position *p = ecs_field(&it, Position, 1); - test_int(p->x, 3); - test_int(p->y, 4); - test_bool(false, ecs_query_next(&it)); - } - - ecs_fini(world); -} - -void Query_subquery_match_existing(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_PREFAB(world, Type, Position, Velocity); - - ecs_bulk_new(world, Position, 3); - ecs_bulk_new(world, Velocity, 3); - const ecs_id_t *ids = ecs_bulk_new(world, Position, 3); - ecs_add(world, ids[0], Velocity); - ecs_add(world, ids[1], Velocity); - ecs_add(world, ids[2], Velocity); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Velocity", - .parent = q - }); - - test_assert(sq != NULL); - - int32_t table_count = 0, entity_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - table_count ++; - entity_count += it.count; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_has(world, it.entities[i], Position)); - } - } - - test_int(table_count, 2); - test_int(entity_count, 6); - - table_count = 0, entity_count = 0; - it = ecs_query_iter(world, sq); - while (ecs_query_next(&it)) { - table_count ++; - entity_count += it.count; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_has(world, it.entities[i], Position)); - test_assert(ecs_has(world, it.entities[i], Velocity)); - } - } - - test_int(table_count, 1); - test_int(entity_count, 3); - - ecs_query_fini(sq); - - ecs_fini(world); -} - -void Query_subquery_match_new(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Velocity", - .parent = q - }); - test_assert(sq != NULL); - - ecs_bulk_new(world, Position, 3); - ecs_bulk_new(world, Velocity, 3); - const ecs_id_t *ids = ecs_bulk_new(world, Position, 3); - ecs_add(world, ids[0], Velocity); - ecs_add(world, ids[1], Velocity); - ecs_add(world, ids[2], Velocity); - - int32_t table_count = 0, entity_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - table_count ++; - entity_count += it.count; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_has(world, it.entities[i], Position)); - } - } - - test_int(table_count, 2); - test_int(entity_count, 6); - - table_count = 0, entity_count = 0; - it = ecs_query_iter(world, sq); - while (ecs_query_next(&it)) { - table_count ++; - entity_count += it.count; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_has(world, it.entities[i], Position)); - test_assert(ecs_has(world, it.entities[i], Velocity)); - } - } - - test_int(table_count, 1); - test_int(entity_count, 3); - - ecs_query_fini(sq); - - ecs_fini(world); -} - -void Query_subquery_inactive(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_bulk_new(world, Position, 3); - ecs_bulk_new(world, Velocity, 3); - ECS_ENTITY(world, e, Position, Velocity); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Velocity", - .parent = q - }); - test_assert(sq != NULL); - - /* Create an empty table which should deactivate it for both queries */ - ecs_delete(world, e); - - int32_t table_count = 0, entity_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - table_count ++; - entity_count += it.count; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_has(world, it.entities[i], Position)); - } - } - - test_int(table_count, 1); - test_int(entity_count, 3); - - table_count = 0, entity_count = 0; - it = ecs_query_iter(world, sq); - test_int(it.table_count, 0); - ecs_iter_fini(&it); - - table_count = 0, entity_count = 0; - it = ecs_query_iter(world, sq); - while (ecs_query_next(&it)) { - table_count ++; - } - - test_int(table_count, 0); - - ecs_query_fini(sq); - - ecs_fini(world); -} - -void Query_subquery_unmatch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t parent = ecs_new(world, 0); - ecs_add(world, parent, Position); - - ecs_entity_t e1 = ecs_new(world, 0); - ecs_add(world, e1, Position); - ecs_add(world, e1, Velocity); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new(world, 0); - ecs_add(world, e2, Position); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_query_t *q = ecs_query_new(world, "Position, Position(parent)"); - test_assert(q != NULL); - - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Velocity", - .parent = q - }); - test_assert(sq != NULL); - - int32_t table_count = 0, entity_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - table_count ++; - entity_count += it.count; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_has(world, it.entities[i], Position)); - } - } - - test_int(table_count, 2); - test_int(entity_count, 2); - - table_count = 0, entity_count = 0; - it = ecs_query_iter(world, sq); - while (ecs_query_next(&it)) { - table_count ++; - entity_count += it.count; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_has(world, it.entities[i], Position)); - test_assert(ecs_has(world, it.entities[i], Velocity)); - } - } - - test_int(table_count, 1); - test_int(entity_count, 1); - - /* Query now no longer match */ - ecs_remove(world, parent, Position); - - /* Force rematching */ - ecs_run_aperiodic(world, 0); - - /* Test if tables have been unmatched */ - it = ecs_query_iter(world, q); - test_int(it.table_count, 0); - ecs_iter_fini(&it); - - it = ecs_query_iter(world, sq); - test_int(it.table_count, 0); - ecs_iter_fini(&it); - - ecs_query_fini(sq); - - ecs_fini(world); -} - -void Query_subquery_rematch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t parent = ecs_new(world, 0); - ecs_add(world, parent, Position); - - ecs_entity_t e1 = ecs_new(world, 0); - ecs_add(world, e1, Position); - ecs_add(world, e1, Velocity); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new(world, 0); - ecs_add(world, e2, Position); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_query_t *q = ecs_query_new(world, "Position, Position(parent)"); - test_assert(q != NULL); - - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Velocity", - .parent = q - }); - test_assert(sq != NULL); - - int32_t table_count = 0, entity_count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - table_count ++; - entity_count += it.count; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_has(world, it.entities[i], Position)); - } - } - - test_int(table_count, 2); - test_int(entity_count, 2); - - table_count = 0, entity_count = 0; - it = ecs_query_iter(world, sq); - while (ecs_query_next(&it)) { - table_count ++; - entity_count += it.count; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(ecs_has(world, it.entities[i], Position)); - test_assert(ecs_has(world, it.entities[i], Velocity)); - } - } - - test_int(table_count, 1); - test_int(entity_count, 1); - - /* Query now no longer match */ - ecs_remove(world, parent, Position); - - /* Force unmatching */ - ecs_run_aperiodic(world, 0); - - /* Test if tables have been unmatched */ - it = ecs_query_iter(world, q); - test_int(it.table_count, 0); - ecs_iter_fini(&it); - - it = ecs_query_iter(world, sq); - test_int(it.table_count, 0); - ecs_iter_fini(&it); - - /* Rematch queries */ - ecs_add(world, parent, Position); - - /* Force rematching */ - ecs_run_aperiodic(world, 0); - - /* Test if tables have been rematched */ - it = ecs_query_iter(world, q); - test_int(it.table_count, 2); - ecs_iter_fini(&it); - - it = ecs_query_iter(world, sq); - test_int(it.table_count, 1); - ecs_iter_fini(&it); - - ecs_query_fini(sq); - - ecs_fini(world); -} - -void Query_subquery_rematch_w_parent_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t parent = ecs_new(world, 0); - - ecs_entity_t e1 = ecs_new(world, 0); - ecs_add(world, e1, Position); - ecs_add(world, e1, Velocity); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new(world, 0); - ecs_add(world, e2, Position); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_query_t *q = ecs_query_new(world, "Position, ?Position(parent)"); - test_assert(q != NULL); - - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Velocity", - .parent = q - }); - test_assert(sq != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_int(it.table_count, 2); - ecs_iter_fini(&it); - - it = ecs_query_iter(world, sq); - test_int(it.table_count, 1); - ecs_iter_fini(&it); - - /* Trigger rematch */ - ecs_add(world, parent, Position); - ecs_run_aperiodic(world, 0); - - /* Tables should be matched */ - it = ecs_query_iter(world, q); - test_int(it.table_count, 3); - ecs_iter_fini(&it); - - it = ecs_query_iter(world, sq); - test_int(it.table_count, 1); - ecs_iter_fini(&it); - - ecs_query_fini(sq); - - ecs_fini(world); -} - -void Query_subquery_rematch_w_sub_optional(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t parent = ecs_new(world, 0); - ecs_entity_t e1 = ecs_new(world, 0); - ecs_add(world, e1, Position); - ecs_add(world, e1, Velocity); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new(world, 0); - ecs_add(world, e2, Position); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_query_t *q = ecs_query_new(world, "Position, ?Position(parent)"); - test_assert(q != NULL); - - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Velocity", - .parent = q - }); - test_assert(sq != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_int(it.table_count, 2); - ecs_iter_fini(&it); - - it = ecs_query_iter(world, sq); - test_int(it.table_count, 1); - ecs_iter_fini(&it); - - /* Trigger rematch */ - ecs_add(world, parent, Position); - ecs_run_aperiodic(world, 0); - - /* Tables should be matched */ - it = ecs_query_iter(world, q); - test_int(it.table_count, 3); - ecs_iter_fini(&it); - - it = ecs_query_iter(world, sq); - test_int(it.table_count, 1); - ecs_iter_fini(&it); - - ecs_query_fini(sq); - - ecs_fini(world); -} - -void Query_query_single_pairs(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_TAG(world, Rel); - - ECS_ENTITY(world, e1, (Rel, Position)); - ECS_ENTITY(world, e2, (Rel, Velocity)); - ECS_ENTITY(world, e3, Position); - ECS_ENTITY(world, e4, Velocity); - - int32_t table_count = 0, entity_count = 0; - - ecs_query_t *q = ecs_query_new(world, "(Rel, Velocity)"); - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - table_count ++; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] == e2); - entity_count ++; - } - } - - test_int(table_count, 1); - test_int(entity_count, 1); - - ecs_fini(world); -} - -void Query_query_single_instanceof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, BaseA, 0); - ECS_ENTITY(world, BaseB, 0); - ECS_ENTITY(world, e1, (IsA, BaseB)); - ECS_ENTITY(world, e2, (IsA, BaseA)); - - int32_t table_count = 0, entity_count = 0; - - ecs_query_t *q = ecs_query_new(world, "(IsA, BaseA)"); - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - table_count ++; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] == e2); - entity_count ++; - } - } - - test_int(table_count, 1); - test_int(entity_count, 1); - - ecs_fini(world); -} - -void Query_query_single_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, BaseA, 0); - ECS_ENTITY(world, BaseB, 0); - ECS_ENTITY(world, e1, (ChildOf, BaseB)); - ECS_ENTITY(world, e2, (ChildOf, BaseA)); - - int32_t table_count = 0, entity_count = 0; - - ecs_query_t *q = ecs_query_new(world, "(ChildOf, BaseA)"); - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - table_count ++; - - int32_t i; - for (i = 0; i < it.count; i ++) { - test_assert(it.entities[i] == e2); - entity_count ++; - } - } - - test_int(table_count, 1); - test_int(entity_count, 1); - - ecs_fini(world); -} - -void Query_query_optional_owned(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base = ecs_new(world, Velocity); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add_pair(world, e1, EcsIsA, base); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, ?Velocity(self)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - int32_t count = 0; - - while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_assert(p != NULL); - - if (it.entities[0] == e1) { - test_assert(v == NULL); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), false); - } else if (it.entities[0] == e2) { - test_assert(v != NULL); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), true); - } else if (it.entities[0] == e3) { - test_assert(v == NULL); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), false); - } - - count ++; - } - - test_int(count, 3); - - ecs_fini(world); -} - -void Query_query_optional_shared(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base = ecs_new(world, Velocity); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add_pair(world, e1, EcsIsA, base); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, ?Velocity(up)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - int32_t count = 0; - - while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_assert(p != NULL); - - if (it.entities[0] == e1) { - test_assert(v != NULL); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), true); - } else if (it.entities[0] == e2) { - test_assert(v == NULL); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), false); - } else if (it.entities[0] == e3) { - test_assert(v == NULL); - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), false); - } - - count ++; - } - - test_int(count, 3); - - ecs_fini(world); -} - -void Query_query_optional_shared_nested(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base_base = ecs_new(world, Velocity); - ecs_entity_t base = ecs_new(world, 0); - ecs_add_pair(world, base, EcsIsA, base_base); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add_pair(world, e1, EcsIsA, base); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, ?Velocity(up)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - int32_t count = 0; - - while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_assert(p != NULL); - - if (it.entities[0] == e1) { - test_assert(v != NULL); - } else if (it.entities[0] == e2) { - test_assert(v == NULL); - } else if (it.entities[0] == e3) { - test_assert(v == NULL); - } - - count ++; - } - - test_int(count, 3); - - ecs_fini(world); -} - -void Query_query_optional_any(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base = ecs_new(world, Velocity); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add_pair(world, e1, EcsIsA, base); - - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, ?Velocity(self|up)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - int32_t count = 0; - - while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_assert(p != NULL); - - if (it.entities[0] == e1) { - test_assert(v != NULL); - test_assert(!ecs_field_is_self(&it, 2)); - } else if (it.entities[0] == e2) { - test_assert(v != NULL); - test_assert(ecs_field_is_self(&it, 2)); - } else if (it.entities[0] == e3) { - test_assert(v == NULL); - } - - count ++; - } - - test_int(count, 3); - - ecs_fini(world); -} - -void Query_query_rematch_optional_after_add(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base = ecs_new(world, 0); - - ecs_entity_t e1 = ecs_new(world, Position); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_add(world, e2, Velocity); - ecs_entity_t e3 = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, ?Velocity(up)"); - test_assert(q != NULL); - - /* First iteration, base doesn't have Velocity but query should match with - * entity anyway since the component is optional */ - ecs_iter_t it = ecs_query_iter(world, q); - int32_t count = 0; - - while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_assert(p != NULL); - - if (it.entities[0] == e1) { - test_assert(v == NULL); - } else if (it.entities[0] == e2) { - test_assert(v == NULL); - } else if (it.entities[0] == e3) { - test_assert(v == NULL); - } - - count ++; - } - test_int(count, 3); - - /* Add Velocity to base, should trigger rematch */ - ecs_add(world, base, Velocity); - - /* Trigger a merge, which triggers the rematch */ - ecs_readonly_begin(world); - ecs_readonly_end(world); - - /* Second iteration, base has Velocity and entity should be able to access - * the shared component. */ - it = ecs_query_iter(world, q); - count = 0; - - while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - - test_int(it.count, 1); - test_assert(p != NULL); - - if (it.entities[0] == e1) { - test_assert(v != NULL); - } else if (it.entities[0] == e2) { - test_assert(v == NULL); - } else if (it.entities[0] == e3) { - test_assert(v == NULL); - } - - count ++; - } - test_int(count, 3); - - ecs_fini(world); -} - -void Query_get_owned_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag"); - - int count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(ecs_field_w_size(&it, 0, 1) == NULL); - test_int(it.count, 1); - test_int(it.entities[0], e); - count += it.count; - } - - test_int(count, 1); - - ecs_fini(world); -} - -void Query_get_shared_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t instance = ecs_new_w_pair(world, EcsIsA, base); - - ecs_query_t *q = ecs_query_new(world, "Tag(up)"); - - int count = 0; - ecs_iter_t it = ecs_query_iter(world, q); - while (ecs_query_next(&it)) { - test_assert(ecs_field_w_size(&it, 0, 1) == NULL); - test_int(it.count, 1); - test_int(it.entities[0], instance); - count += it.count; - } - - test_int(count, 1); - - ecs_fini(world); -} - -void Query_explicit_delete(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - /* Ensure query isn't deleted twice when deleting world */ - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_get_column_size(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(ecs_field_size(&it, 1), sizeof(Position)); - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_orphaned_query(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - /* Nonsense subquery, doesn't matter, this is just for orphan testing */ - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .parent = q - }); - test_assert(sq != NULL); - - test_assert(!ecs_query_orphaned(sq)); - - ecs_query_fini(q); - - test_assert(ecs_query_orphaned(sq)); - - ecs_query_fini(sq); - - ecs_fini(world); -} - -void Query_nested_orphaned_query(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - /* Nonsense subquery, doesn't matter, this is just for orphan testing */ - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .parent = q - }); - test_assert(sq != NULL); - - ecs_query_t *nsq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .parent = sq - }); - test_assert(nsq != NULL); - - test_assert(!ecs_query_orphaned(sq)); - test_assert(!ecs_query_orphaned(nsq)); - - ecs_query_fini(q); - - test_assert(ecs_query_orphaned(sq)); - test_assert(ecs_query_orphaned(nsq)); - - ecs_query_fini(sq); - ecs_query_fini(nsq); - - ecs_fini(world); -} - -void Query_invalid_access_orphaned_query(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - /* Nonsense subquery, doesn't matter, this is just for orphan testing */ - ecs_query_t *sq = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .parent = q - }); - test_assert(sq != NULL); - - test_assert(!ecs_query_orphaned(sq)); - - ecs_query_fini(q); - - test_expect_abort(); - - ecs_query_iter(world, sq); -} - -void Query_stresstest_query_free(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - ECS_TAG(world, Hello); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, Foo); - ecs_add(world, e, Bar); - ecs_add(world, e, Hello); - - /* Create & delete query to test if query is properly unregistered with - * the table */ - - for (int i = 0; i < 10000; i ++) { - ecs_query_t *q = ecs_query_new(world, "Foo"); - ecs_query_fini(q); - } - - /* If code did not crash, test passes */ - test_assert(true); - - ecs_fini(world); -} - -void Query_only_from_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_set_name(world, 0, "e"); - - ecs_query_t *q = ecs_query_new(world, "Tag(e)"); - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(!ecs_query_next(&it)); - - ecs_add(world, e, Tag); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_assert(ecs_field_src(&it, 1) == e); - test_assert(ecs_field_id(&it, 1) == Tag); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_only_from_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_set_name(world, 0, "e"); - - ecs_query_t *q = ecs_query_new(world, "e($)"); - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(!ecs_query_next(&it)); - - ecs_add_id(world, e, e); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_assert(ecs_field_src(&it, 1) == e); - test_assert(ecs_field_id(&it, 1) == e); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_only_not_from_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_set_name(world, 0, "e"); - - ecs_query_t *q = ecs_query_new(world, "!Tag(e)"); - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_assert(ecs_field_src(&it, 1) == e); - test_assert(ecs_field_id(&it, 1) == Tag); - test_assert(!ecs_query_next(&it)); - - ecs_add(world, e, Tag); - - it = ecs_query_iter(world, q); - test_assert(!ecs_query_next(&it)); - - test_assert(ECS_BIT_IS_SET(it.flags, EcsIterIsValid) == false); - ecs_fini(world); -} - -void Query_only_not_from_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_set_name(world, 0, "e"); - - ecs_query_t *q = ecs_query_new(world, "!e($)"); - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_assert(ecs_field_src(&it, 1) == e); - test_assert(ecs_field_id(&it, 1) == e); - test_assert(!ecs_query_next(&it)); - - ecs_add_id(world, e, e); - - it = ecs_query_iter(world, q); - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_get_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - test_assert(q != NULL); - - const ecs_filter_t *f = ecs_query_get_filter(q); - test_assert(f != NULL); - - test_int(f->term_count, 2); - test_int(f->terms[0].id, ecs_id(Position)); - test_int(f->terms[1].id, ecs_id(Velocity)); - - ecs_fini(world); -} - -void Query_group_by(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagX); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{TagX}}, - .group_by = group_by_first_id - }); - - ecs_entity_t e1 = ecs_new(world, TagX); - ecs_entity_t e2 = ecs_new(world, TagX); - ecs_entity_t e3 = ecs_new(world, TagX); - - ecs_add_id(world, e1, TagC); - ecs_add_id(world, e2, TagB); - ecs_add_id(world, e3, TagA); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e3); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e2); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e1); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -static int ctx_value; -static int ctx_value_free_invoked = 0; -static -void ctx_value_free(void *ctx) { - test_assert(ctx == &ctx_value); - ctx_value_free_invoked ++; -} - -void Query_group_by_w_ctx(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagX); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{TagX}}, - .group_by = group_by_first_id, - .group_by_ctx = &ctx_value, - .group_by_ctx_free = ctx_value_free - }); - - ecs_entity_t e1 = ecs_new(world, TagX); - ecs_entity_t e2 = ecs_new(world, TagX); - ecs_entity_t e3 = ecs_new(world, TagX); - - ecs_add_id(world, e1, TagC); - ecs_add_id(world, e2, TagB); - ecs_add_id(world, e3, TagA); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e3); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e2); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e1); - - test_bool(ecs_query_next(&it), false); - - ecs_query_fini(q); - - test_int(ctx_value_free_invoked, 1); - - ecs_fini(world); -} - -void Query_group_by_w_sort_reverse_group_creation(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t TagA = ecs_new_id(world); - ecs_entity_t TagB = ecs_new_id(world); - ecs_entity_t TagC = ecs_new_id(world); - - ecs_entity_t TagX = ecs_new_id(world); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{TagX}}, - .order_by = order_by_entity, - .group_by = group_by_first_id - }); - - ecs_entity_t e1 = ecs_new_w_id(world, TagX); - ecs_entity_t e2 = ecs_new_w_id(world, TagX); - ecs_entity_t e3 = ecs_new_w_id(world, TagX); - - ecs_add_id(world, e1, TagC); - ecs_add_id(world, e2, TagB); - ecs_add_id(world, e3, TagA); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e3); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e2); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e1); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_group_by_iter_one(void) { - ecs_world_t* world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - - ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_new_w_pair(world, Rel, TgtC); - - ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e5 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t e6 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - ecs_add(world, e6, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_pair(Rel, EcsWildcard) } - }, - .group_by = group_by_rel, - .group_by_id = Rel - }); - - ecs_iter_t it = ecs_query_iter(world, q); - ecs_query_set_group(&it, TgtB); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, it.group_id); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, it.group_id); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_group_by_iter_one_all_groups(void) { - ecs_world_t* world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtC); - - ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e5 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t e6 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - ecs_add(world, e6, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_pair(Rel, EcsWildcard) } - }, - .group_by = group_by_rel, - .group_by_id = Rel - }); - - ecs_iter_t it = ecs_query_iter(world, q); - ecs_query_set_group(&it, TgtB); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, it.group_id); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e5, it.entities[0]); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, it.group_id); - test_bool(false, ecs_query_next(&it)); - - it = ecs_query_iter(world, q); - ecs_query_set_group(&it, TgtA); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, it.group_id); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e4, it.entities[0]); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, it.group_id); - test_bool(false, ecs_query_next(&it)); - - it = ecs_query_iter(world, q); - ecs_query_set_group(&it, TgtC); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - test_uint(TgtC, it.group_id); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e6, it.entities[0]); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - test_uint(TgtC, it.group_id); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_group_by_iter_one_empty(void) { - ecs_world_t* world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, TgtD); - ECS_TAG(world, Tag); - - ecs_new_w_pair(world, Rel, TgtA); - ecs_new_w_pair(world, Rel, TgtB); - ecs_new_w_pair(world, Rel, TgtC); - - ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e5 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t e6 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - ecs_add(world, e6, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_pair(Rel, EcsWildcard) } - }, - .group_by = group_by_rel, - .group_by_id = Rel - }); - - ecs_iter_t it = ecs_query_iter(world, q); - ecs_query_set_group(&it, TgtD); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_group_by_iter_one_empty_query(void) { - ecs_world_t* world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_pair(Rel, EcsWildcard) } - }, - .group_by = group_by_rel, - .group_by_id = Rel - }); - - ecs_iter_t it = ecs_query_iter(world, q); - ecs_query_set_group(&it, TgtA); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_group_by_iter_one_empty_table(void) { - ecs_world_t* world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - ECS_TAG(world, Tag); - - ecs_new_w_pair(world, Rel, TgtA); - ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtC); - - ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtA); - ecs_entity_t e5 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t e6 = ecs_new_w_pair(world, Rel, TgtC); - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - ecs_add(world, e6, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_pair(Rel, EcsWildcard) } - }, - .group_by = group_by_rel, - .group_by_id = Rel - }); - - ecs_delete(world, e3); - ecs_delete(world, e6); - - ecs_iter_t it = ecs_query_iter(world, q); - ecs_query_set_group(&it, TgtC); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_group_by_w_deleted_group_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_pair(Rel, EcsWildcard) } - }, - .group_by = group_by_rel, - .group_by_id = Rel - }); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); - - ecs_iter_t it = ecs_query_iter(world, q); - ecs_query_set_group(&it, tgt); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_bool(false, ecs_query_next(&it)); - - ecs_delete(world, tgt); - ecs_delete(world, e); - - it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - tgt = ecs_new_id(world); - e = ecs_new_w_pair(world, Rel, tgt); - - it = ecs_query_iter(world, q); - ecs_query_set_group(&it, tgt); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -static int group_by_ctx; -static int on_group_create_invoked; -static int on_group_delete_invoked; - -static -void* on_group_create(ecs_world_t *world, uint64_t group_id, void *group_by_arg) { - test_assert(world != NULL); - test_assert(group_id != 0); - test_assert(group_by_arg != NULL); - test_assert(group_by_arg == &group_by_ctx); - uint64_t *group_ctx = ecs_os_malloc_t(uint64_t); - *group_ctx = group_id; - on_group_create_invoked ++; - return group_ctx; -} - -static -void on_group_delete(ecs_world_t *world, uint64_t group_id, void *group_ctx, void *group_by_arg) { - test_assert(world != NULL); - test_assert(group_id != 0); - test_assert(group_ctx != NULL); - test_assert(group_by_arg != NULL); - test_assert(group_by_arg == &group_by_ctx); - test_assert(*(uint64_t*)group_ctx == group_id); - ecs_os_free(group_ctx); - on_group_delete_invoked ++; -} - -void Query_group_by_callbacks(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_pair(Rel, EcsWildcard) } - }, - .group_by = group_by_rel, - .group_by_id = Rel, - .on_group_create = on_group_create, - .on_group_delete = on_group_delete, - .group_by_ctx = &group_by_ctx - }); - - ecs_entity_t tgt_a = ecs_new_id(world); - ecs_entity_t tgt_b = ecs_new_id(world); - - test_int(on_group_create_invoked, 0); - test_int(on_group_delete_invoked, 0); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, tgt_a); - ecs_run_aperiodic(world, 0); - test_int(on_group_create_invoked, 1); - test_int(on_group_delete_invoked, 0); - - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, tgt_a); - ecs_run_aperiodic(world, 0); - test_int(on_group_create_invoked, 1); - test_int(on_group_delete_invoked, 0); - - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, tgt_b); - ecs_run_aperiodic(world, 0); - test_int(on_group_create_invoked, 2); - test_int(on_group_delete_invoked, 0); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(2, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(tgt_a, it.group_id); - const ecs_query_group_info_t *gi = ecs_query_get_group_info(q, it.group_id); - test_assert(gi != NULL); - test_assert(gi->ctx != NULL); - test_uint(tgt_a, *(uint64_t*)gi->ctx); - - test_assert(ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(tgt_b, it.group_id); - gi = ecs_query_get_group_info(q, it.group_id); - test_assert(gi != NULL); - test_assert(gi->ctx != NULL); - test_uint(tgt_b, *(uint64_t*)gi->ctx); - test_assert(!ecs_query_next(&it)); - - ecs_delete_with(world, ecs_pair(Rel, tgt_a)); - ecs_run_aperiodic(world, 0); - test_int(on_group_create_invoked, 2); - test_int(on_group_delete_invoked, 1); - - ecs_query_fini(q); - - test_int(on_group_create_invoked, 2); - test_int(on_group_delete_invoked, 2); - - ecs_fini(world); -} - -void Query_group_by_default_action(void) { - ecs_world_t* world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtC); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtA); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_pair(Rel, EcsWildcard) } - }, - // .group_by = group_by_rel, default action groups by relationship - .group_by_id = Rel - }); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e3, it.entities[0]); - test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); - test_uint(TgtA, it.group_id); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); - test_uint(TgtB, it.group_id); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); - test_uint(TgtC, it.group_id); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_group_table_count(void) { - ecs_world_t* world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, Foo); - - ecs_new_w_pair(world, Rel, TgtA); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { ecs_pair(Rel, EcsWildcard) } - }, - .group_by_id = Rel - }); - - ecs_run_aperiodic(world, 0); - - const ecs_query_group_info_t *gi_a; - const ecs_query_group_info_t *gi_b; - gi_a = ecs_query_get_group_info(q, TgtA); - gi_b = ecs_query_get_group_info(q, TgtB); - test_assert(gi_a != NULL); - test_assert(gi_b == NULL); - test_int(gi_a->table_count, 1); - test_int(gi_a->match_count, 1); - - ecs_new_w_pair(world, Rel, TgtB); - ecs_run_aperiodic(world, 0); - - gi_a = ecs_query_get_group_info(q, TgtA); - gi_b = ecs_query_get_group_info(q, TgtB); - test_assert(gi_a != NULL); - test_assert(gi_b != NULL); - test_int(gi_a->table_count, 1); - test_int(gi_a->match_count, 1); - test_int(gi_b->table_count, 1); - test_int(gi_b->match_count, 1); - - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtA); - ecs_run_aperiodic(world, 0); - - gi_a = ecs_query_get_group_info(q, TgtA); - gi_b = ecs_query_get_group_info(q, TgtB); - test_assert(gi_a != NULL); - test_assert(gi_b != NULL); - test_int(gi_a->table_count, 1); - test_int(gi_a->match_count, 1); - test_int(gi_b->table_count, 1); - test_int(gi_b->match_count, 1); - - ecs_add(world, e3, Foo); - ecs_run_aperiodic(world, 0); - - gi_a = ecs_query_get_group_info(q, TgtA); - gi_b = ecs_query_get_group_info(q, TgtB); - test_assert(gi_a != NULL); - test_assert(gi_b != NULL); - test_int(gi_a->table_count, 2); - test_int(gi_a->match_count, 2); - test_int(gi_b->table_count, 1); - test_int(gi_b->match_count, 1); - - ecs_delete_with(world, Foo); - ecs_run_aperiodic(world, 0); - - gi_a = ecs_query_get_group_info(q, TgtA); - gi_b = ecs_query_get_group_info(q, TgtB); - test_assert(gi_a != NULL); - test_assert(gi_b != NULL); - test_int(gi_a->table_count, 1); - test_int(gi_a->match_count, 3); - test_int(gi_b->table_count, 1); - test_int(gi_b->match_count, 1); - - ecs_delete_with(world, ecs_pair(Rel, TgtA)); - ecs_run_aperiodic(world, 0); - - gi_a = ecs_query_get_group_info(q, TgtA); - gi_b = ecs_query_get_group_info(q, TgtB); - test_assert(gi_a == NULL); - test_assert(gi_b != NULL); - test_int(gi_b->table_count, 1); - test_int(gi_b->match_count, 1); - - ecs_delete_with(world, ecs_pair(Rel, TgtB)); - ecs_run_aperiodic(world, 0); - - gi_a = ecs_query_get_group_info(q, TgtA); - gi_b = ecs_query_get_group_info(q, TgtB); - test_assert(gi_a == NULL); - test_assert(gi_b == NULL); - - ecs_fini(world); -} - -void Query_iter_valid(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(it.flags & EcsIterIsValid, false); - - test_bool(ecs_query_next(&it), true); - test_bool(it.flags & EcsIterIsValid, true); - - test_bool(ecs_query_next(&it), false); - test_bool(it.flags & EcsIterIsValid, false); - - ecs_fini(world); -} - -void Query_query_optional_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add_id(world, e2, TagB); - - ecs_query_t *q = ecs_query_new(world, "TagA, ?TagB"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - int32_t count = 0; - - while (ecs_query_next(&it)) { - test_uint(ecs_field_id(&it, 1), TagA); - test_assert(ecs_field_id(&it, 2) == TagB); - test_int(it.count, 1); - - if (it.entities[0] == e1) { - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), false); - } else if (it.entities[0] == e2) { - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), true); - } - - count ++; - } - - test_int(count, 2); - - ecs_fini(world); -} - -void Query_query_optional_shared_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_add_id(world, e2, TagB); - - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_add_pair(world, e3, EcsIsA, e2); - - ecs_query_t *q = ecs_query_new(world, "TagA, ?TagB(self|up)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - int32_t count = 0; - - while (ecs_query_next(&it)) { - test_uint(ecs_field_id(&it, 1), TagA); - test_assert(ecs_field_id(&it, 2) == TagB); - test_int(it.count, 1); - - if (it.entities[0] == e1) { - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), false); - } else if (it.entities[0] == e2) { - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), true); - } else if (it.entities[0] == e3) { - test_bool(ecs_field_is_set(&it, 1), true); - test_bool(ecs_field_is_set(&it, 2), true); - } - - count ++; - } - - test_int(count, 3); - - ecs_fini(world); -} - -void Query_query_iter_10_tags(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - ECS_TAG(world, TagE); - ECS_TAG(world, TagF); - ECS_TAG(world, TagG); - ECS_TAG(world, TagH); - ECS_TAG(world, TagI); - ECS_TAG(world, TagJ); - ECS_TAG(world, TagK); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_add_id(world, e_1, TagC); - ecs_add_id(world, e_1, TagD); - ecs_add_id(world, e_1, TagE); - ecs_add_id(world, e_1, TagF); - ecs_add_id(world, e_1, TagG); - ecs_add_id(world, e_1, TagH); - ecs_add_id(world, e_1, TagI); - ecs_add_id(world, e_1, TagJ); - - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - ecs_add_id(world, e_2, TagC); - ecs_add_id(world, e_2, TagD); - ecs_add_id(world, e_2, TagE); - ecs_add_id(world, e_2, TagF); - ecs_add_id(world, e_2, TagG); - ecs_add_id(world, e_2, TagH); - ecs_add_id(world, e_2, TagI); - ecs_add_id(world, e_2, TagJ); - ecs_add_id(world, e_2, TagK); /* 2nd match in different table */ - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - {TagA}, {TagB}, {TagC}, {TagD}, {TagE}, {TagF}, {TagG}, {TagH}, - {TagI}, {TagJ} - } - }); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_id(&it, 3), TagC); - test_int(ecs_field_id(&it, 4), TagD); - test_int(ecs_field_id(&it, 5), TagE); - test_int(ecs_field_id(&it, 6), TagF); - test_int(ecs_field_id(&it, 7), TagG); - test_int(ecs_field_id(&it, 8), TagH); - test_int(ecs_field_id(&it, 9), TagI); - test_int(ecs_field_id(&it, 10), TagJ); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_id(&it, 3), TagC); - test_int(ecs_field_id(&it, 4), TagD); - test_int(ecs_field_id(&it, 5), TagE); - test_int(ecs_field_id(&it, 6), TagF); - test_int(ecs_field_id(&it, 7), TagG); - test_int(ecs_field_id(&it, 8), TagH); - test_int(ecs_field_id(&it, 9), TagI); - test_int(ecs_field_id(&it, 10), TagJ); - - test_assert(!ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_query_iter_20_tags(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - ECS_TAG(world, TagD); - ECS_TAG(world, TagE); - ECS_TAG(world, TagF); - ECS_TAG(world, TagG); - ECS_TAG(world, TagH); - ECS_TAG(world, TagI); - ECS_TAG(world, TagJ); - ECS_TAG(world, TagK); - ECS_TAG(world, TagL); - ECS_TAG(world, TagM); - ECS_TAG(world, TagN); - ECS_TAG(world, TagO); - ECS_TAG(world, TagP); - ECS_TAG(world, TagQ); - ECS_TAG(world, TagR); - ECS_TAG(world, TagS); - ECS_TAG(world, TagT); - ECS_TAG(world, TagU); - - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_add_id(world, e_1, TagB); - ecs_add_id(world, e_1, TagC); - ecs_add_id(world, e_1, TagD); - ecs_add_id(world, e_1, TagE); - ecs_add_id(world, e_1, TagF); - ecs_add_id(world, e_1, TagG); - ecs_add_id(world, e_1, TagH); - ecs_add_id(world, e_1, TagI); - ecs_add_id(world, e_1, TagJ); - ecs_add_id(world, e_1, TagK); - ecs_add_id(world, e_1, TagL); - ecs_add_id(world, e_1, TagM); - ecs_add_id(world, e_1, TagN); - ecs_add_id(world, e_1, TagO); - ecs_add_id(world, e_1, TagP); - ecs_add_id(world, e_1, TagQ); - ecs_add_id(world, e_1, TagR); - ecs_add_id(world, e_1, TagS); - ecs_add_id(world, e_1, TagT); - - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_add_id(world, e_2, TagB); - ecs_add_id(world, e_2, TagC); - ecs_add_id(world, e_2, TagD); - ecs_add_id(world, e_2, TagE); - ecs_add_id(world, e_2, TagF); - ecs_add_id(world, e_2, TagG); - ecs_add_id(world, e_2, TagH); - ecs_add_id(world, e_2, TagI); - ecs_add_id(world, e_2, TagJ); - ecs_add_id(world, e_2, TagK); - ecs_add_id(world, e_2, TagL); - ecs_add_id(world, e_2, TagM); - ecs_add_id(world, e_2, TagN); - ecs_add_id(world, e_2, TagO); - ecs_add_id(world, e_2, TagP); - ecs_add_id(world, e_2, TagQ); - ecs_add_id(world, e_2, TagR); - ecs_add_id(world, e_2, TagS); - ecs_add_id(world, e_2, TagT); - ecs_add_id(world, e_2, TagU); /* 2nd match in different table */ - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter = { - .terms_buffer = (ecs_term_t[]){ - {TagA}, {TagB}, {TagC}, {TagD}, {TagE}, {TagF}, {TagG}, {TagH}, - {TagI}, {TagJ}, {TagK}, {TagL}, {TagM}, {TagN}, {TagO}, {TagP}, - {TagQ}, {TagR}, {TagS}, {TagT} - }, - .terms_buffer_count = 20 - }, - }); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_id(&it, 3), TagC); - test_int(ecs_field_id(&it, 4), TagD); - test_int(ecs_field_id(&it, 5), TagE); - test_int(ecs_field_id(&it, 6), TagF); - test_int(ecs_field_id(&it, 7), TagG); - test_int(ecs_field_id(&it, 8), TagH); - test_int(ecs_field_id(&it, 9), TagI); - test_int(ecs_field_id(&it, 10), TagJ); - test_int(ecs_field_id(&it, 11), TagK); - test_int(ecs_field_id(&it, 12), TagL); - test_int(ecs_field_id(&it, 13), TagM); - test_int(ecs_field_id(&it, 14), TagN); - test_int(ecs_field_id(&it, 15), TagO); - test_int(ecs_field_id(&it, 16), TagP); - test_int(ecs_field_id(&it, 17), TagQ); - test_int(ecs_field_id(&it, 18), TagR); - test_int(ecs_field_id(&it, 19), TagS); - test_int(ecs_field_id(&it, 20), TagT); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), TagA); - test_int(ecs_field_id(&it, 2), TagB); - test_int(ecs_field_id(&it, 3), TagC); - test_int(ecs_field_id(&it, 4), TagD); - test_int(ecs_field_id(&it, 5), TagE); - test_int(ecs_field_id(&it, 6), TagF); - test_int(ecs_field_id(&it, 7), TagG); - test_int(ecs_field_id(&it, 8), TagH); - test_int(ecs_field_id(&it, 9), TagI); - test_int(ecs_field_id(&it, 10), TagJ); - test_int(ecs_field_id(&it, 11), TagK); - test_int(ecs_field_id(&it, 12), TagL); - test_int(ecs_field_id(&it, 13), TagM); - test_int(ecs_field_id(&it, 14), TagN); - test_int(ecs_field_id(&it, 15), TagO); - test_int(ecs_field_id(&it, 16), TagP); - test_int(ecs_field_id(&it, 17), TagQ); - test_int(ecs_field_id(&it, 18), TagR); - test_int(ecs_field_id(&it, 19), TagS); - test_int(ecs_field_id(&it, 20), TagT); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -typedef struct { - float v; -} CompA, CompB, CompC, CompD, CompE, CompF, CompG, CompH, CompI, CompJ, CompK, - CompL, CompM, CompN, CompO, CompP, CompQ, CompR, CompS, CompT, CompU; - -void Query_query_iter_10_components(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, CompA); - ECS_COMPONENT(world, CompB); - ECS_COMPONENT(world, CompC); - ECS_COMPONENT(world, CompD); - ECS_COMPONENT(world, CompE); - ECS_COMPONENT(world, CompF); - ECS_COMPONENT(world, CompG); - ECS_COMPONENT(world, CompH); - ECS_COMPONENT(world, CompI); - ECS_COMPONENT(world, CompJ); - ECS_COMPONENT(world, CompK); - - ecs_entity_t e_1 = ecs_set(world, 0, CompA, {10}); - ecs_set(world, e_1, CompB, {10}); - ecs_set(world, e_1, CompC, {10}); - ecs_set(world, e_1, CompD, {10}); - ecs_set(world, e_1, CompE, {10}); - ecs_set(world, e_1, CompF, {10}); - ecs_set(world, e_1, CompG, {10}); - ecs_set(world, e_1, CompH, {10}); - ecs_set(world, e_1, CompI, {10}); - ecs_set(world, e_1, CompJ, {10}); - - ecs_entity_t e_2 = ecs_set(world, 0, CompA, {10}); - ecs_set(world, e_2, CompB, {10}); - ecs_set(world, e_2, CompC, {10}); - ecs_set(world, e_2, CompD, {10}); - ecs_set(world, e_2, CompE, {10}); - ecs_set(world, e_2, CompF, {10}); - ecs_set(world, e_2, CompG, {10}); - ecs_set(world, e_2, CompH, {10}); - ecs_set(world, e_2, CompI, {10}); - ecs_set(world, e_2, CompJ, {10}); - ecs_set(world, e_2, CompK, {10}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - {ecs_id(CompA)}, {ecs_id(CompB)}, {ecs_id(CompC)}, {ecs_id(CompD)}, - {ecs_id(CompE)}, {ecs_id(CompF)}, {ecs_id(CompG)}, {ecs_id(CompH)}, - {ecs_id(CompI)}, {ecs_id(CompJ)} - } - }); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), ecs_id(CompA)); - test_int(ecs_field_id(&it, 2), ecs_id(CompB)); - test_int(ecs_field_id(&it, 3), ecs_id(CompC)); - test_int(ecs_field_id(&it, 4), ecs_id(CompD)); - test_int(ecs_field_id(&it, 5), ecs_id(CompE)); - test_int(ecs_field_id(&it, 6), ecs_id(CompF)); - test_int(ecs_field_id(&it, 7), ecs_id(CompG)); - test_int(ecs_field_id(&it, 8), ecs_id(CompH)); - test_int(ecs_field_id(&it, 9), ecs_id(CompI)); - test_int(ecs_field_id(&it, 10), ecs_id(CompJ)); - - int i; - for (i = 0; i < 10; i ++) { - CompA *ptr = ecs_field_w_size(&it, 0, i + 1); - test_assert(ptr != NULL); - test_int(ptr[0].v, 10); - } - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(CompA)); - test_int(ecs_field_id(&it, 2), ecs_id(CompB)); - test_int(ecs_field_id(&it, 3), ecs_id(CompC)); - test_int(ecs_field_id(&it, 4), ecs_id(CompD)); - test_int(ecs_field_id(&it, 5), ecs_id(CompE)); - test_int(ecs_field_id(&it, 6), ecs_id(CompF)); - test_int(ecs_field_id(&it, 7), ecs_id(CompG)); - test_int(ecs_field_id(&it, 8), ecs_id(CompH)); - test_int(ecs_field_id(&it, 9), ecs_id(CompI)); - test_int(ecs_field_id(&it, 10), ecs_id(CompJ)); - - for (i = 0; i < 10; i ++) { - CompA *ptr = ecs_field_w_size(&it, 0, i + 1); - test_assert(ptr != NULL); - test_int(ptr[0].v, 10); - } - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_iter_20_components(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, CompA); - ECS_COMPONENT(world, CompB); - ECS_COMPONENT(world, CompC); - ECS_COMPONENT(world, CompD); - ECS_COMPONENT(world, CompE); - ECS_COMPONENT(world, CompF); - ECS_COMPONENT(world, CompG); - ECS_COMPONENT(world, CompH); - ECS_COMPONENT(world, CompI); - ECS_COMPONENT(world, CompJ); - ECS_COMPONENT(world, CompK); - ECS_COMPONENT(world, CompL); - ECS_COMPONENT(world, CompM); - ECS_COMPONENT(world, CompN); - ECS_COMPONENT(world, CompO); - ECS_COMPONENT(world, CompP); - ECS_COMPONENT(world, CompQ); - ECS_COMPONENT(world, CompR); - ECS_COMPONENT(world, CompS); - ECS_COMPONENT(world, CompT); - ECS_COMPONENT(world, CompU); - - ecs_entity_t e_1 = ecs_set(world, 0, CompA, {10}); - ecs_set(world, e_1, CompB, {10}); - ecs_set(world, e_1, CompC, {10}); - ecs_set(world, e_1, CompD, {10}); - ecs_set(world, e_1, CompE, {10}); - ecs_set(world, e_1, CompF, {10}); - ecs_set(world, e_1, CompG, {10}); - ecs_set(world, e_1, CompH, {10}); - ecs_set(world, e_1, CompI, {10}); - ecs_set(world, e_1, CompJ, {10}); - ecs_set(world, e_1, CompK, {10}); - ecs_set(world, e_1, CompL, {10}); - ecs_set(world, e_1, CompM, {10}); - ecs_set(world, e_1, CompN, {10}); - ecs_set(world, e_1, CompO, {10}); - ecs_set(world, e_1, CompP, {10}); - ecs_set(world, e_1, CompQ, {10}); - ecs_set(world, e_1, CompR, {10}); - ecs_set(world, e_1, CompS, {10}); - ecs_set(world, e_1, CompT, {10}); - - ecs_entity_t e_2 = ecs_set(world, 0, CompA, {10}); - ecs_set(world, e_2, CompB, {10}); - ecs_set(world, e_2, CompC, {10}); - ecs_set(world, e_2, CompD, {10}); - ecs_set(world, e_2, CompE, {10}); - ecs_set(world, e_2, CompF, {10}); - ecs_set(world, e_2, CompG, {10}); - ecs_set(world, e_2, CompH, {10}); - ecs_set(world, e_2, CompI, {10}); - ecs_set(world, e_2, CompJ, {10}); - ecs_set(world, e_2, CompK, {10}); - ecs_set(world, e_2, CompL, {10}); - ecs_set(world, e_2, CompM, {10}); - ecs_set(world, e_2, CompN, {10}); - ecs_set(world, e_2, CompO, {10}); - ecs_set(world, e_2, CompP, {10}); - ecs_set(world, e_2, CompQ, {10}); - ecs_set(world, e_2, CompR, {10}); - ecs_set(world, e_2, CompS, {10}); - ecs_set(world, e_2, CompT, {10}); - ecs_set(world, e_2, CompU, {10}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter = { - .terms_buffer = (ecs_term_t[]){ - {ecs_id(CompA)}, {ecs_id(CompB)}, {ecs_id(CompC)}, {ecs_id(CompD)}, - {ecs_id(CompE)}, {ecs_id(CompF)}, {ecs_id(CompG)}, {ecs_id(CompH)}, - {ecs_id(CompI)}, {ecs_id(CompJ)}, {ecs_id(CompK)}, {ecs_id(CompL)}, - {ecs_id(CompM)}, {ecs_id(CompN)}, {ecs_id(CompO)}, {ecs_id(CompP)}, - {ecs_id(CompQ)}, {ecs_id(CompR)}, {ecs_id(CompS)}, {ecs_id(CompT)} - }, - .terms_buffer_count = 20 - } - }); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_1); - test_int(ecs_field_id(&it, 1), ecs_id(CompA)); - test_int(ecs_field_id(&it, 2), ecs_id(CompB)); - test_int(ecs_field_id(&it, 3), ecs_id(CompC)); - test_int(ecs_field_id(&it, 4), ecs_id(CompD)); - test_int(ecs_field_id(&it, 5), ecs_id(CompE)); - test_int(ecs_field_id(&it, 6), ecs_id(CompF)); - test_int(ecs_field_id(&it, 7), ecs_id(CompG)); - test_int(ecs_field_id(&it, 8), ecs_id(CompH)); - test_int(ecs_field_id(&it, 9), ecs_id(CompI)); - test_int(ecs_field_id(&it, 10), ecs_id(CompJ)); - test_int(ecs_field_id(&it, 11), ecs_id(CompK)); - test_int(ecs_field_id(&it, 12), ecs_id(CompL)); - test_int(ecs_field_id(&it, 13), ecs_id(CompM)); - test_int(ecs_field_id(&it, 14), ecs_id(CompN)); - test_int(ecs_field_id(&it, 15), ecs_id(CompO)); - test_int(ecs_field_id(&it, 16), ecs_id(CompP)); - test_int(ecs_field_id(&it, 17), ecs_id(CompQ)); - test_int(ecs_field_id(&it, 18), ecs_id(CompR)); - test_int(ecs_field_id(&it, 19), ecs_id(CompS)); - test_int(ecs_field_id(&it, 20), ecs_id(CompT)); - - int i; - for (i = 0; i < 20; i ++) { - CompA *ptr = ecs_field_w_size(&it, 0, i + 1); - test_assert(ptr != NULL); - test_int(ptr[0].v, 10); - } - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e_2); - test_int(ecs_field_id(&it, 1), ecs_id(CompA)); - test_int(ecs_field_id(&it, 2), ecs_id(CompB)); - test_int(ecs_field_id(&it, 3), ecs_id(CompC)); - test_int(ecs_field_id(&it, 4), ecs_id(CompD)); - test_int(ecs_field_id(&it, 5), ecs_id(CompE)); - test_int(ecs_field_id(&it, 6), ecs_id(CompF)); - test_int(ecs_field_id(&it, 7), ecs_id(CompG)); - test_int(ecs_field_id(&it, 8), ecs_id(CompH)); - test_int(ecs_field_id(&it, 9), ecs_id(CompI)); - test_int(ecs_field_id(&it, 10), ecs_id(CompJ)); - test_int(ecs_field_id(&it, 11), ecs_id(CompK)); - test_int(ecs_field_id(&it, 12), ecs_id(CompL)); - test_int(ecs_field_id(&it, 13), ecs_id(CompM)); - test_int(ecs_field_id(&it, 14), ecs_id(CompN)); - test_int(ecs_field_id(&it, 15), ecs_id(CompO)); - test_int(ecs_field_id(&it, 16), ecs_id(CompP)); - test_int(ecs_field_id(&it, 17), ecs_id(CompQ)); - test_int(ecs_field_id(&it, 18), ecs_id(CompR)); - test_int(ecs_field_id(&it, 19), ecs_id(CompS)); - test_int(ecs_field_id(&it, 20), ecs_id(CompT)); - - for (i = 0; i < 20; i ++) { - CompA *ptr = ecs_field_w_size(&it, 0, i + 1); - test_assert(ptr != NULL); - test_int(ptr[0].v, 10); - } - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_iter_type_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_assert(it.table != NULL); - const ecs_type_t *type = ecs_table_get_type(it.table); - test_int(type->count, 1); - test_int(type->array[0], ecs_id(Position)); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_filter_term(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ .id = ecs_id(Position), .inout = EcsInOutNone }} - }); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(ecs_query_next(&it), true); - test_assert(it.ids != NULL); - test_assert(it.ids[0] == ecs_id(Position)); - - test_int(it.count, 1); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e); - - test_assert(it.ptrs != NULL); - test_assert(it.columns != NULL); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_2_terms_1_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { .id = ecs_id(Position), .inout = EcsInOutNone }, - { .id = ecs_id(Velocity) } - } - }); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 1}); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(ecs_query_next(&it), true); - test_assert(it.ids != NULL); - test_assert(it.ids[0] == ecs_id(Position)); - test_assert(it.ids[1] == ecs_id(Velocity)); - - test_int(it.count, 1); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e); - - test_assert(it.ptrs != NULL); - test_assert(it.sizes != NULL); - test_assert(it.columns != NULL); - - test_assert(it.ptrs[0] == NULL); - test_assert(it.ptrs[1] != NULL); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_3_terms_2_filter(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { .id = ecs_id(Position), .inout = EcsInOutNone }, - { .id = ecs_id(Velocity), .inout = EcsInOutNone }, - { .id = ecs_id(Mass) } - } - }); - - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, e, Velocity, {1, 1}); - ecs_set(world, e, Mass, {1}); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(ecs_query_next(&it), true); - test_assert(it.ids != NULL); - test_assert(it.ids[0] == ecs_id(Position)); - test_assert(it.ids[1] == ecs_id(Velocity)); - test_assert(it.ids[2] == ecs_id(Mass)); - - test_int(it.count, 1); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e); - - test_assert(it.ptrs != NULL); - test_assert(it.sizes != NULL); - test_assert(it.columns != NULL); - - test_assert(it.ptrs[0] == NULL); - test_assert(it.ptrs[1] == NULL); - test_assert(it.ptrs[2] != NULL); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_no_instancing_w_singleton(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_singleton_set(world, Velocity, {1, 2}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity($)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e1); - test_int(p->x, 10); - test_int(p->y, 20); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e2); - test_int(p->x, 20); - test_int(p->y, 30); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_int(p->x, 30); - test_int(p->y, 40); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_int(p->x, 40); - test_int(p->y, 50); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e5); - test_int(p->x, 50); - test_int(p->y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_no_instancing_w_shared(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t base = ecs_set(world, 0, Velocity, {1, 2}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e4 = ecs_set(world, 0, Position, {40, 50}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {50, 60}); - ecs_add(world, e4, Tag); - ecs_add(world, e5, Tag); - - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add_pair(world, e3, EcsIsA, base); - ecs_add_pair(world, e4, EcsIsA, base); - ecs_add_pair(world, e5, EcsIsA, base); - - ecs_entity_t e6 = ecs_set(world, 0, Position, {60, 70}); - ecs_entity_t e7 = ecs_set(world, 0, Position, {70, 80}); - ecs_set(world, e6, Velocity, {2, 3}); - ecs_set(world, e7, Velocity, {4, 5}); - - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 2); - test_int(it.entities[0], e6); - test_int(p[0].x, 60); - test_int(p[0].y, 70); - test_int(v[0].x, 2); - test_int(v[0].y, 3); - - test_int(it.entities[1], e7); - test_int(p[1].x, 70); - test_int(p[1].y, 80); - test_int(v[1].x, 4); - test_int(v[1].y, 5); - } - - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e1); - test_int(p->x, 10); - test_int(p->y, 20); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e2); - test_int(p->x, 20); - test_int(p->y, 30); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e3); - test_int(p->x, 30); - test_int(p->y, 40); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e4); - test_int(p->x, 40); - test_int(p->y, 50); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(ecs_query_next(&it)); - { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - test_int(it.count, 1); - test_int(it.entities[0], e5); - test_int(p->x, 50); - test_int(p->y, 60); - test_int(v->x, 1); - test_int(v->y, 2); - } - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_iter_frame_offset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { .id = TagA, } - }, - }); - - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); - ecs_entity_t e5 = ecs_new(world, TagA); - - ecs_add(world, e3, TagB); - ecs_add(world, e4, TagB); - ecs_add(world, e5, TagC); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 2); - test_int(it.frame_offset, 0); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e1); - test_assert(it.entities[1] == e2); - test_assert(it.ids[0] == TagA); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 2); - test_int(it.frame_offset, 2); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e3); - test_assert(it.entities[1] == e4); - test_assert(it.ids[0] == TagA); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.frame_offset, 4); - test_assert(it.entities != NULL); - test_assert(it.entities[0] == e5); - test_assert(it.ids[0] == TagA); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_add_singleton_after_query(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { TagA }, - { TagB, .src.id = TagB } - } - }); - test_assert(q != NULL); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, TagA); - - ecs_singleton_add(world, TagB); - test_assert(ecs_has(world, TagB, TagB)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool (ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_bool (ecs_query_next(&it), false); - - ecs_singleton_remove(world, TagB); - test_assert(!ecs_has(world, TagB, TagB)); - - it = ecs_query_iter(world, q); - test_bool (ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_query_w_component_from_parent_from_non_this(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t parent = ecs_new(world, TagB); - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { TagA }, - { TagB, .src.id = child, .src.trav = EcsChildOf, .src.flags = EcsUp } - } - }); - test_assert(q != NULL); - - ecs_entity_t e = ecs_new_id(world); - ecs_add(world, e, TagA); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool (ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e); - test_int(it.sources[0], 0); - test_int(it.sources[1], parent); - test_bool (ecs_query_next(&it), false); - - ecs_remove_pair(world, child, EcsChildOf, parent); - it = ecs_query_iter(world, q); - test_bool (ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Query_create_query_while_pending(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_entity_t e = ecs_new(world, TagA); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ TagA }} - }); - test_assert(q != NULL); - - ecs_add(world, e, TagB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert( ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e); - - test_assert( !ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_empty_query(void) { - ecs_world_t *world = ecs_mini(); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ 0 }} - }); - test_assert(q != NULL); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_implicit_existing_isa_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - - ecs_query_t *q = ecs_query_new(world, "Tag"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], base); - test_uint(it.sources[0], 0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_implicit_new_isa_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], base); - test_uint(it.sources[0], 0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(up)"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_superset_2_lvls(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(up)"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], base); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], base); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_superset_3_lvls(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(up)"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], base); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], base); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], base); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_superset_2_lvls_owned(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(up)"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - - ecs_add(world, e1, Tag); - ecs_add(world, e2, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], base); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e1); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_superset_3_lvls_owned(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(up)"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); - - ecs_add(world, e1, Tag); - ecs_add(world, e2, Tag); - ecs_add(world, e3, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], base); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e1); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e2); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_superset_owned_empty_table_after_match(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(up)"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); - - ecs_add(world, e1, Tag); - ecs_add(world, e2, Tag); - ecs_add(world, e3, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], base); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e1); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e2); - - test_bool(false, ecs_query_next(&it)); - - ecs_remove_pair(world, e3, EcsIsA, e2); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], base); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e1); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_self_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(self|up)"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], base); - test_uint(it.sources[0], 0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_childof_superset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(up(ChildOf))"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_superset_2_targets(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, R, EcsTraversable); - ECS_COMPONENT(world, Position); - - ecs_entity_t e_0 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_1 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {50, 60}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {70, 80}); - - ecs_add_pair(world, e_3, R, e_2); - ecs_add_pair(world, e_2, R, e_1); - ecs_add_pair(world, e_1, R, e_0); - - ecs_entity_t t = ecs_new_id(world); - ecs_add(world, t, Position); - ecs_add_pair(world, t, R, e_2); - ecs_add_pair(world, t, R, e_1); - ecs_delete(world, t); - - ecs_query_t *q = ecs_query_new(world, "Position(up(R))"); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_3); - test_uint(it.sources[0], e_2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_2); - test_uint(it.sources[0], e_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_1); - test_uint(it.sources[0], e_0); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_superset_2_relations(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = TagA, .src.trav = EcsChildOf, .src.flags = EcsUp }, - { .id = TagA, .src.trav = EcsIsA, .src.flags = EcsUp }, - } - }); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t parent = ecs_new(world, TagA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(2, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_superset_2_relations_instanced(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = TagA, .src.trav = EcsChildOf, .src.flags = EcsUp }, - { .id = TagA, .src.trav = EcsIsA, .src.flags = EcsUp }, - }, - .filter.instanced = true - }); - - ecs_entity_t base = ecs_new(world, TagA); - ecs_entity_t parent = ecs_new(world, TagA); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(2, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_superset_2_relations_w_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position), .src.trav = EcsChildOf, .src.flags = EcsUp }, - { .id = ecs_id(Position), .src.trav = EcsIsA, .src.flags = EcsUp }, - } - }); - - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t parent = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e1, it.entities[0]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - Position *p1 = ecs_field(&it, Position, 1); - Position *p2 = ecs_field(&it, Position, 2); - test_assert(p1 != NULL); - test_assert(p2 != NULL); - test_int(p1->x, 30); - test_int(p1->y, 40); - test_int(p2->x, 10); - test_int(p2->y, 20); - } - { - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e2, it.entities[0]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - Position *p1 = ecs_field(&it, Position, 1); - Position *p2 = ecs_field(&it, Position, 2); - test_assert(p1 != NULL); - test_assert(p2 != NULL); - test_int(p1->x, 30); - test_int(p1->y, 40); - test_int(p2->x, 10); - test_int(p2->y, 20); - } - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_superset_2_relations_instanced_w_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position), .src.trav = EcsChildOf, .src.flags = EcsUp }, - { .id = ecs_id(Position), .src.trav = EcsIsA, .src.flags = EcsUp }, - }, - .filter.instanced = true - }); - - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t parent = ecs_set(world, 0, Position, {30, 40}); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, EcsIsA, base); - ecs_add_pair(world, e1, EcsChildOf, parent); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, EcsIsA, base); - ecs_add_pair(world, e2, EcsChildOf, parent); - - ecs_iter_t it = ecs_query_iter(world, q); - { - test_bool(true, ecs_query_next(&it)); - test_int(2, it.count); - test_uint(e1, it.entities[0]); - test_uint(e2, it.entities[1]); - test_uint(parent, it.sources[0]); - test_uint(base, it.sources[1]); - Position *p1 = ecs_field(&it, Position, 1); - Position *p2 = ecs_field(&it, Position, 2); - test_assert(p1 != NULL); - test_assert(p2 != NULL); - test_int(p1->x, 30); - test_int(p1->y, 40); - test_int(p2->x, 10); - test_int(p2->y, 20); - } - test_bool(false, ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_parent(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag(parent)"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new(world, Tag); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_existing_isa_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, e0); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); - - ecs_query_t *q = ecs_query_new(world, "Tag(cascade)"); - test_assert(q != NULL); - - ecs_add_id(world, e1, Bar); /* mix up order */ - ecs_add_id(world, e2, Foo); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e0); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_new_isa_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q = ecs_query_new(world, "Tag(cascade)"); - test_assert(q != NULL); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, e0); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); - - ecs_add_id(world, e2, Foo); /* mix up order */ - ecs_add_id(world, e1, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e0); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_childof_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q = ecs_query_new(world, "Tag(cascade(ChildOf))"); - test_assert(q != NULL); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, e0); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e2); - - ecs_add_id(world, e2, Foo); /* mix up order */ - ecs_add_id(world, e1, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e0); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_parent_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q = ecs_query_new(world, "Tag(parent|cascade)"); - test_assert(q != NULL); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, e0); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e2); - - ecs_add_id(world, e2, Foo); /* mix up order */ - ecs_add_id(world, e1, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e0); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_existing_custom_rel_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_ENTITY(world, Rel, EcsTraversable); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, e0); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, e2); - - ecs_query_t *q = ecs_query_new(world, "Tag(cascade(Rel))"); - test_assert(q != NULL); - - ecs_add_id(world, e2, Foo); /* mix up order */ - ecs_add_id(world, e1, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e0); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_new_custom_rel_cascade(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_ENTITY(world, Rel, EcsTraversable); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q = ecs_query_new(world, "Tag(cascade(Rel))"); - test_assert(q != NULL); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, e0); - ecs_entity_t e2 = ecs_new_w_pair(world, Rel, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, Rel, e2); - - ecs_add_id(world, e2, Foo); /* mix up order */ - ecs_add_id(world, e1, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e0); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_w_2_depths(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_ENTITY(world, Rel, EcsTraversable); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q = ecs_query_new(world, "Tag(cascade(Rel))"); - test_assert(q != NULL); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, Rel, e0); - ecs_add_pair(world, e2, Rel, e1); - ecs_add_pair(world, e3, Rel, e2); - - ecs_add_id(world, e2, Foo); /* mix up order */ - ecs_add_id(world, e1, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e1); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e1); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_w_3_depths(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_ENTITY(world, Rel, EcsTraversable); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q = ecs_query_new(world, "Tag(cascade(Rel))"); - test_assert(q != NULL); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, Rel, e0); - ecs_add_pair(world, e2, Rel, e1); - ecs_add_pair(world, e3, Rel, e2); - - ecs_add_id(world, e2, Foo); /* mix up order */ - ecs_add_id(world, e1, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e1); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e2); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_w_2_depths_desc(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_ENTITY(world, Rel, EcsTraversable); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q = ecs_query_new(world, "Tag(cascade|desc(Rel))"); - test_assert(q != NULL); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, Rel, e0); - ecs_add_pair(world, e2, Rel, e1); - ecs_add_pair(world, e3, Rel, e2); - - ecs_add_id(world, e2, Foo); /* mix up order */ - ecs_add_id(world, e1, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e1); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e1); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_w_3_depths_desc(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_ENTITY(world, Rel, EcsTraversable); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q = ecs_query_new(world, "Tag(cascade|desc(Rel))"); - test_assert(q != NULL); - - ecs_entity_t e0 = ecs_new(world, Tag); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_add_pair(world, e1, Rel, e0); - ecs_add_pair(world, e2, Rel, e1); - ecs_add_pair(world, e3, Rel, e2); - - ecs_add_id(world, e2, Foo); /* mix up order */ - ecs_add_id(world, e1, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.sources[0], e2); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(it.sources[0], e1); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.sources[0], e0); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_not_pair_relation_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_query_t *q = ecs_query_new(world, "Foo, !(*, ObjA)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_entity_t e4 = ecs_new(world, Foo); - - ecs_add_pair(world, e1, RelA, ObjA); - ecs_add_pair(world, e2, RelA, ObjB); - ecs_add_pair(world, e3, RelB, ObjA); - ecs_add_pair(world, e4, RelB, ObjB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_id(&it, 2), ecs_pair(EcsWildcard, ObjA)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e4); - test_uint(ecs_field_id(&it, 2), ecs_pair(EcsWildcard, ObjA)); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_not_pair_object_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_query_t *q = ecs_query_new(world, "Foo, !(RelA, *)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_entity_t e4 = ecs_new(world, Foo); - - ecs_add_pair(world, e1, RelA, ObjA); - ecs_add_pair(world, e2, RelA, ObjB); - ecs_add_pair(world, e3, RelB, ObjA); - ecs_add_pair(world, e4, RelB, ObjB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(ecs_field_id(&it, 2), ecs_pair(RelA, EcsWildcard)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e4); - test_uint(ecs_field_id(&it, 2), ecs_pair(RelA, EcsWildcard)); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_two_pair_wildcards_one_not(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_query_t *q = ecs_query_new(world, "Foo, (RelA, *), !(RelB, *)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_entity_t e4 = ecs_new(world, Foo); - - ecs_add_pair(world, e1, RelA, ObjA); - ecs_add_pair(world, e1, RelB, ObjA); - - ecs_add_pair(world, e2, RelA, ObjA); - - ecs_add_pair(world, e3, RelA, ObjB); - ecs_add_pair(world, e3, RelB, ObjB); - - ecs_add_pair(world, e4, RelA, ObjB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_id(&it, 1), Foo); - test_uint(ecs_field_id(&it, 2), ecs_pair(RelA, ObjA)); - test_uint(ecs_field_id(&it, 3), ecs_pair(RelB, EcsWildcard)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e4); - test_uint(ecs_field_id(&it, 1), Foo); - test_uint(ecs_field_id(&it, 2), ecs_pair(RelA, ObjB)); - test_uint(ecs_field_id(&it, 3), ecs_pair(RelB, EcsWildcard)); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_two_pair_wildcards_one_not_any(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, RelA); - ECS_TAG(world, RelB); - ECS_TAG(world, ObjA); - ECS_TAG(world, ObjB); - - ecs_query_t *q = ecs_query_new(world, "Foo, (RelA, *), !(RelB, _)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_entity_t e3 = ecs_new(world, Foo); - ecs_entity_t e4 = ecs_new(world, Foo); - - ecs_add_pair(world, e1, RelA, ObjA); - ecs_add_pair(world, e1, RelB, ObjA); - - ecs_add_pair(world, e2, RelA, ObjA); - - ecs_add_pair(world, e3, RelA, ObjB); - ecs_add_pair(world, e3, RelB, ObjB); - - ecs_add_pair(world, e4, RelA, ObjB); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - test_uint(ecs_field_id(&it, 1), Foo); - test_uint(ecs_field_id(&it, 2), ecs_pair(RelA, ObjA)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e4); - test_uint(ecs_field_id(&it, 1), Foo); - test_uint(ecs_field_id(&it, 2), ecs_pair(RelA, ObjB)); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_rematch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base_1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_1); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], base_1); - test_uint(it.entities[1], base_2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 30); - test_int(p[1].y, 40); - test_uint(it.sources[0], 0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_uint(it.sources[0], base_1); - test_bool(false, ecs_query_next(&it)); - - ecs_remove_pair(world, inst, EcsIsA, base_1); - ecs_add_pair(world, inst, EcsIsA, base_2); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], base_1); - test_uint(it.entities[1], base_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 30); - test_int(p[1].y, 40); - test_uint(it.sources[0], 0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_uint(it.sources[0], base_2); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_childof_rematch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base_1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base_1); - - ecs_query_t *q = ecs_query_new(world, "Position(parent)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_uint(it.sources[0], base_1); - test_bool(false, ecs_query_next(&it)); - - ecs_add_pair(world, inst, EcsChildOf, base_2); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_uint(it.sources[0], base_2); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_unmatch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base_1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_1); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], base_1); - test_uint(it.entities[1], base_2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 30); - test_int(p[1].y, 40); - test_uint(it.sources[0], 0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_uint(it.sources[0], base_1); - test_bool(false, ecs_query_next(&it)); - - ecs_remove(world, base_1, Position); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], base_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_uint(it.sources[0], 0); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_childof_unmatch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base_1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base_1); - - ecs_query_t *q = ecs_query_new(world, "Position(parent)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_uint(it.sources[0], base_1); - test_bool(false, ecs_query_next(&it)); - - ecs_remove(world, base_1, Position); - - it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_isa_rematch_2_lvls(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base_1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t base = ecs_new_w_pair(world, EcsIsA, base_1); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - - ecs_query_t *q = ecs_query_new(world, "Position"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], base_1); - test_uint(it.entities[1], base_2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 30); - test_int(p[1].y, 40); - test_uint(it.sources[0], 0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], base); - test_uint(it.sources[0], base_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_bool(false, ecs_query_next(&it)); - - ecs_remove_pair(world, base, EcsIsA, base_1); - ecs_add_pair(world, base, EcsIsA, base_2); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], base_1); - test_uint(it.entities[1], base_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 30); - test_int(p[1].y, 40); - test_uint(it.sources[0], 0); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], base); - test_uint(it.sources[0], base_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_childof_rematch_2_lvls(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base_1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t base = ecs_new_w_pair(world, EcsChildOf, base_1); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); - - ecs_query_t *q = ecs_query_new(world, "Position(parent)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], base); - test_uint(it.sources[0], base_1); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_bool(false, ecs_query_next(&it)); - - ecs_add_pair(world, base, EcsChildOf, base_2); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], base); - test_uint(it.sources[0], base_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_rematch_2_lvls(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e_0 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_1 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {50, 60}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {70, 80}); - ecs_add_pair(world, e_3, EcsChildOf, e_2); - - ecs_add_pair(world, e_2, EcsChildOf, e_1); - ecs_add_pair(world, e_1, EcsChildOf, e_0); - - ecs_query_t *q = ecs_query_new(world, "Position(cascade(ChildOf))"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_1); - test_uint(it.sources[0], e_0); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_2); - test_uint(it.sources[0], e_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_3); - test_uint(it.sources[0], e_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - test_bool(false, ecs_query_next(&it)); - - ecs_remove_pair(world, e_1, EcsChildOf, EcsWildcard); - ecs_remove_pair(world, e_2, EcsChildOf, EcsWildcard); - ecs_remove_pair(world, e_3, EcsChildOf, EcsWildcard); - - ecs_add_pair(world, e_0, EcsChildOf, e_1); - ecs_add_pair(world, e_1, EcsChildOf, e_2); - ecs_add_pair(world, e_2, EcsChildOf, e_3); - - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_2); - test_uint(it.sources[0], e_3); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 70); - test_int(p[0].y, 80); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_1); - test_uint(it.sources[0], e_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_0); - test_uint(it.sources[0], e_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_rematch_2_lvls_2_relations(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, R, EcsTraversable); - ECS_COMPONENT(world, Position); - - ecs_entity_t e_0 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_1 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {50, 60}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {70, 80}); - - ecs_add_pair(world, e_3, R, e_2); - ecs_add_pair(world, e_2, R, e_1); - ecs_add_pair(world, e_1, R, e_0); - - ecs_entity_t t = ecs_new_id(world); - ecs_add(world, t, Position); - ecs_add_pair(world, t, R, e_2); - ecs_add_pair(world, t, R, e_1); - ecs_delete(world, t); - - ecs_query_t *q = ecs_query_new(world, "Position(cascade(R))"); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_1); - test_uint(it.sources[0], e_0); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_2); - test_uint(it.sources[0], e_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_3); - test_uint(it.sources[0], e_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_topological(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, R, Traversable); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - - ecs_add_pair(world, e3, R, e1); - ecs_add_pair(world, e3, R, e2); - ecs_add_pair(world, e3, R, e4); - ecs_add_pair(world, e1, R, e5); - ecs_add_pair(world, e2, R, e6); - ecs_add_pair(world, e4, R, e1); - ecs_add_pair(world, e4, R, e2); - - ecs_query_t *q = ecs_query_new(world, "Tag, ?Tag(cascade(R))"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(2, it.count); - test_uint(it.entities[0], e5); - test_uint(it.entities[1], e6); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(it.entities[0], e1); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(it.entities[0], e2); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(it.entities[0], e4); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(it.entities[0], e3); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_desc_rematch_2_lvls(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e_0 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_1 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {50, 60}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {70, 80}); - - ecs_add_pair(world, e_3, EcsChildOf, e_2); - ecs_add_pair(world, e_2, EcsChildOf, e_1); - ecs_add_pair(world, e_1, EcsChildOf, e_0); - - ecs_query_t *q = ecs_query_new(world, "Position(cascade|desc(ChildOf))"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_3); - test_uint(it.sources[0], e_2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_2); - test_uint(it.sources[0], e_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_1); - test_uint(it.sources[0], e_0); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - - test_bool(false, ecs_query_next(&it)); - - ecs_remove_pair(world, e_1, EcsChildOf, EcsWildcard); - ecs_remove_pair(world, e_2, EcsChildOf, EcsWildcard); - ecs_remove_pair(world, e_3, EcsChildOf, EcsWildcard); - - ecs_add_pair(world, e_0, EcsChildOf, e_1); - ecs_add_pair(world, e_1, EcsChildOf, e_2); - ecs_add_pair(world, e_2, EcsChildOf, e_3); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_0); - test_uint(it.sources[0], e_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_1); - test_uint(it.sources[0], e_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_2); - test_uint(it.sources[0], e_3); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 70); - test_int(p[0].y, 80); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_desc_rematch_2_lvls_2_relations(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, R, EcsTraversable); - ECS_COMPONENT(world, Position); - - ecs_entity_t e_0 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e_1 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e_2 = ecs_set(world, 0, Position, {50, 60}); - ecs_entity_t e_3 = ecs_set(world, 0, Position, {70, 80}); - - ecs_add_pair(world, e_3, R, e_2); - ecs_add_pair(world, e_2, R, e_1); - ecs_add_pair(world, e_1, R, e_0); - - ecs_entity_t t = ecs_new_id(world); - ecs_add(world, t, Position); - ecs_add_pair(world, t, R, e_2); - ecs_add_pair(world, t, R, e_1); - ecs_delete(world, t); - - ecs_query_t *q = ecs_query_new(world, "Position(cascade|desc(R))"); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_3); - test_uint(it.sources[0], e_2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 50); - test_int(p[0].y, 60); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_2); - test_uint(it.sources[0], e_1); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e_1); - test_uint(it.sources[0], e_0); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_cascade_desc_topological(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, R, Traversable); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - - ecs_add_pair(world, e3, R, e1); - ecs_add_pair(world, e3, R, e2); - ecs_add_pair(world, e3, R, e4); - ecs_add_pair(world, e1, R, e5); - ecs_add_pair(world, e2, R, e6); - ecs_add_pair(world, e4, R, e1); - ecs_add_pair(world, e4, R, e2); - - ecs_query_t *q = ecs_query_new(world, "Tag, ?Tag(cascade|desc(R))"); - 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(it.entities[0], e3); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(it.entities[0], e4); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(it.entities[0], e1); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(it.entities[0], e2); - - test_bool(true, ecs_query_next(&it)); - test_int(2, it.count); - test_uint(it.entities[0], e5); - test_uint(it.entities[1], e6); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_childof_rematch_from_isa(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base_1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t base = ecs_new_w_pair(world, EcsIsA, base_1); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); - - ecs_query_t *q = ecs_query_new(world, "Position(parent)"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base_1); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_bool(false, ecs_query_next(&it)); - - ecs_remove_pair(world, base, EcsIsA, base_1); - ecs_add_pair(world, base, EcsIsA, base_2); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_uint(it.sources[0], base_2); - p = ecs_field(&it, Position, 1); - test_int(p[0].x, 30); - test_int(p[0].y, 40); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_rematch_optional_ref(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Tag, ?Position(parent)"); - test_assert(q != NULL); - - ecs_entity_t parent = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t child = ecs_new(world, Tag); - ecs_add_pair(world, child, EcsChildOf, parent); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_uint(0, it.sources[0]); - test_uint(parent, it.sources[1]); - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_query_next(&it)); - - ecs_remove(world, parent, Position); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_uint(0, it.sources[0]); - test_uint(0, it.sources[1]); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_rematch_optional_ref_w_2_refs(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_query_t *q = ecs_query_new(world, "Tag, Velocity(parent), ?Position(parent)"); - test_assert(q != NULL); - - ecs_entity_t parent = ecs_set(world, 0, Position, {10, 20}); - ecs_set(world, parent, Velocity, {1, 2}); - ecs_entity_t child = ecs_new(world, Tag); - ecs_add_pair(world, child, EcsChildOf, parent); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_id(Position), ecs_field_id(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(0, it.sources[0]); - test_uint(parent, it.sources[1]); - Velocity *v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - Position *p = ecs_field(&it, Position, 3); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_query_next(&it)); - - ecs_remove(world, parent, Position); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - test_uint(ecs_id(Position), ecs_field_id(&it, 3)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 3)); - test_uint(0, it.sources[0]); - test_uint(parent, it.sources[1]); - test_uint(0, it.sources[2]); - v = ecs_field(&it, Velocity, 2); - test_assert(v != NULL); - test_int(v->x, 1); - test_int(v->y, 2); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_rematch_optional_ref_tag_w_ref_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_query_t *q = ecs_query_new(world, "TagA, ?Position(parent), TagB(parent)"); - test_assert(q != NULL); - - ecs_entity_t parent = ecs_set(world, 0, Position, {10, 20}); - ecs_add(world, parent, TagB); - - ecs_entity_t child = ecs_new(world, TagA); - ecs_add_pair(world, child, EcsChildOf, parent); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(0, it.sources[0]); - test_uint(parent, it.sources[1]); - test_uint(parent, it.sources[2]); - Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_bool(false, ecs_query_next(&it)); - - ecs_remove(world, parent, Position); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(child, it.entities[0]); - test_uint(TagA, ecs_field_id(&it, 1)); - test_uint(ecs_id(Position), ecs_field_id(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 3)); - test_uint(0, it.sources[0]); - test_uint(0, it.sources[1]); - test_uint(parent, it.sources[2]); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_match_query_expr_from_scope(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t foo = ecs_entity_init(world, &(ecs_entity_desc_t){ - .name = "Foo" - }); - - ecs_entity_t bar = ecs_entity_init(world, &(ecs_entity_desc_t){ - .name = "Foo.Bar" - }); - - ecs_set_scope(world, foo); - - ecs_query_t *q = ecs_query_new(world, "Bar"); - test_assert(q != NULL); - - ecs_set_scope(world, 0); - - ecs_entity_t e = ecs_new_w_id(world, bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(bar, ecs_field_id(&it, 1)); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_long_or_w_ref(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ECS_TAG(world, A); - ECS_TAG(world, B); - ECS_TAG(world, C); - ECS_TAG(world, D); - ECS_TAG(world, E); - ECS_TAG(world, F); - ECS_TAG(world, G); - ECS_TAG(world, H); - ECS_TAG(world, I); - ECS_TAG(world, J); - ECS_TAG(world, K); - ECS_TAG(world, L); - ECS_TAG(world, M); - ECS_TAG(world, N); - ECS_TAG(world, O); - ECS_TAG(world, P); - ECS_TAG(world, Q); - ECS_TAG(world, R); - - ecs_entity_t e = ecs_new_entity(world, "e"); - ecs_set(world, e, Position, {10, 20}); - - ecs_entity_t e2 = ecs_new(world, A); - - ecs_query_t *q = ecs_query_new(world, - "Position(e), A || B || C || D || E || F || G || H || I || J || K || L ||" - "M || N || O || P || Q || R"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(A, ecs_field_id(&it, 2)); - test_uint(e2, it.entities[0]); - test_uint(e, it.sources[0]); - test_uint(0, it.sources[1]); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_0_query(void) { - ecs_world_t *world = ecs_mini(); - - ecs_query_t *q = ecs_query_new(world, "0"); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_pair_id_and_subj(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Obj); - ECS_TAG(world, Subj); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ - .id = ecs_pair(Rel, Obj), .src.id = Subj - }} - }); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - ecs_add_pair(world, Subj, Rel, Obj); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 0); - test_uint(it.sources[0], Subj); - test_uint(it.ids[0], ecs_pair(Rel, Obj)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_table_count(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ - .id = TagA - }} - }); - test_assert(q != NULL); - - test_int(0, ecs_query_table_count(q)); - - ecs_new(world, TagA); - - test_int(1, ecs_query_table_count(q)); - - ecs_entity_t e = ecs_new(world, TagA); - - test_int(1, ecs_query_table_count(q)); - - ecs_add(world, e, TagB); - - test_int(2, ecs_query_table_count(q)); - - ecs_delete_with(world, TagB); - - test_int(1, ecs_query_table_count(q)); - - ecs_delete_with(world, TagA); - - test_int(0, ecs_query_table_count(q)); - - ecs_fini(world); -} - -void Query_empty_table_count(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ - .id = TagA - }} - }); - test_assert(q != NULL); - - test_int(0, ecs_query_empty_table_count(q)); - - ecs_entity_t e = ecs_new(world, TagA); - - test_int(0, ecs_query_empty_table_count(q)); - - ecs_delete(world, e); - - test_int(1, ecs_query_empty_table_count(q)); - - ecs_fini(world); -} - -void Query_entity_count(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ - .id = TagA - }} - }); - test_assert(q != NULL); - - test_int(0, ecs_query_entity_count(q)); - - ecs_entity_t e1 = ecs_new(world, TagA); - - test_int(1, ecs_query_entity_count(q)); - - ecs_entity_t e2 = ecs_new(world, TagA); - - test_int(2, ecs_query_entity_count(q)); - - ecs_add(world, e2, TagB); - - test_int(2, ecs_query_entity_count(q)); - - ecs_delete(world, e1); - - test_int(1, ecs_query_entity_count(q)); - - ecs_delete(world, e2); - - test_int(0, ecs_query_entity_count(q)); - - ecs_fini(world); -} - -void Query_rematch_after_delete_inherited_tag(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t tag = ecs_new_id(world); - ecs_entity_t base = ecs_new_w_id(world, tag); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { .id = tag } - } - }); - 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(base, it.entities[0]); - test_uint(tag, ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(tag, ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_delete_with(world, tag); - - it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_rematch_after_delete_rel_of_inherited_pair(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_entity_t base = ecs_new_w_pair(world, rel, obj); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { .id = ecs_pair(rel, obj) } - } - }); - 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(base, it.entities[0]); - test_uint(ecs_pair(rel, obj), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(ecs_pair(rel, obj), ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_delete_with(world, ecs_pair(rel, EcsWildcard)); - - it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_rematch_after_delete_obj_of_inherited_pair(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); - ecs_entity_t base = ecs_new_w_pair(world, rel, obj); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { - { .id = ecs_pair(rel, obj) } - } - }); - 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(base, it.entities[0]); - test_uint(ecs_pair(rel, obj), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(inst, it.entities[0]); - test_uint(ecs_pair(rel, obj), ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_delete_with(world, ecs_pair(EcsWildcard, obj)); - - it = ecs_query_iter(world, q); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_rematch_empty_table_w_superset(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ Foo }, { Bar, .oper = EcsNot }} - }); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_add(world, base, Foo); - test_assert( ecs_is_alive(world, base)); - - ecs_delete_with(world, Foo); - test_assert( !ecs_is_alive(world, base)); - - base = ecs_new_w_id(world, EcsPrefab); - ecs_add(world, base, Foo); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - ecs_add(world, inst, Bar); - - ecs_run_aperiodic(world, 0); // force table (IsA, base) to empty list - - ecs_remove(world, inst, Bar); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], inst); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_short_notation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = Foo - }} - }); - - test_assert(q != NULL); - - ecs_entity_t e = ecs_new(world, Foo); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(it.ids[0], ecs_field_id(&it, 1)); - test_assert(!ecs_query_next(&it)); - - ecs_query_fini(q); - - ecs_fini(world); -} - -void Query_query_w_invalid_filter_flag(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - test_expect_abort(); - ecs_query(world, { - .filter.terms = { - { .id = ecs_id(Position), .src.flags = EcsFilter } - } - }); -} - - -void Query_query_next_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position) - }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e1)); - - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e2)); - test_bool(false, ecs_query_next_table(&it)); - - ecs_fini(world); -} - -void Query_query_next_table_w_populate(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position) - }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e1)); - ecs_query_populate(&it, false); - test_int(1, it.count); - test_assert(it.table == ecs_get_table(world, e1)); - test_uint(it.entities[0], e1); - - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e2)); - ecs_query_populate(&it, false); - test_int(1, it.count); - test_assert(it.table == ecs_get_table(world, e2)); - test_uint(it.entities[0], e2); - test_bool(false, ecs_query_next_table(&it)); - - ecs_fini(world); -} - -void Query_query_next_table_w_changed(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position) - }} - }); - - ecs_query_t *qr = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Tag); - - test_bool(true, ecs_query_changed(qr, NULL)); - ecs_iter_t it = ecs_query_iter(world, qr); - while (ecs_query_next_table(&it)) { } - test_bool(false, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e1)); - - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e2)); - test_bool(false, ecs_query_next_table(&it)); - - test_bool(false, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, qr); - test_bool(true, ecs_query_next_table(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_bool(true, ecs_query_next_table(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_bool(false, ecs_query_next_table(&it)); - - ecs_fini(world); -} - -void Query_query_next_table_w_skip(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position) - }} - }); - - ecs_query_t *qr = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Tag); - - test_bool(true, ecs_query_changed(qr, NULL)); - ecs_iter_t it = ecs_query_iter(world, qr); - while (ecs_query_next(&it)) { } - test_bool(false, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e1)); - - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e2)); - ecs_query_skip(&it); - test_bool(false, ecs_query_next_table(&it)); - - test_bool(false, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, qr); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_bool(true, ecs_query_next(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_next_table_w_populate_first_changed(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position) - }} - }); - - ecs_query_t *qr = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Tag); - - test_bool(true, ecs_query_changed(qr, NULL)); - ecs_iter_t it = ecs_query_iter(world, qr); - while (ecs_query_next(&it)) { } - test_bool(false, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e1)); - ecs_query_populate(&it, false); - test_int(1, it.count); - - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e2)); - test_bool(false, ecs_query_next_table(&it)); - - test_bool(true, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, qr); - test_bool(true, ecs_query_next_table(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_bool(true, ecs_query_next_table(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_bool(false, ecs_query_next_table(&it)); - - ecs_fini(world); -} - -void Query_query_next_table_w_populate_last_changed(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position) - }} - }); - - ecs_query_t *qr = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Tag); - - test_bool(true, ecs_query_changed(qr, NULL)); - ecs_iter_t it = ecs_query_iter(world, qr); - while (ecs_query_next(&it)) { } - test_bool(false, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e1)); - - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e2)); - ecs_query_populate(&it, false); - test_int(1, it.count); - test_bool(false, ecs_query_next_table(&it)); - - test_bool(true, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, qr); - test_bool(true, ecs_query_next_table(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_bool(true, ecs_query_next_table(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_bool(false, ecs_query_next_table(&it)); - - ecs_fini(world); -} - -void Query_query_next_table_w_populate_skip_first(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position) - }} - }); - - ecs_query_t *qr = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Tag); - - test_bool(true, ecs_query_changed(qr, NULL)); - ecs_iter_t it = ecs_query_iter(world, qr); - while (ecs_query_next(&it)) { } - test_bool(false, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e1)); - ecs_query_populate(&it, false); - ecs_query_skip(&it); - test_int(1, it.count); - - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e2)); - ecs_query_populate(&it, false); - test_int(1, it.count); - test_bool(false, ecs_query_next_table(&it)); - - test_bool(true, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, qr); - test_bool(true, ecs_query_next_table(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_bool(true, ecs_query_next_table(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_bool(false, ecs_query_next_table(&it)); - - ecs_fini(world); -} - -void Query_query_next_table_w_populate_skip_last(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position) - }} - }); - - ecs_query_t *qr = ecs_query(world, { - .filter.terms = {{ - .id = ecs_id(Position), - .inout = EcsIn - }} - }); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30}); - ecs_add(world, e2, Tag); - - test_bool(true, ecs_query_changed(qr, NULL)); - ecs_iter_t it = ecs_query_iter(world, qr); - while (ecs_query_next(&it)) { } - test_bool(false, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e1)); - ecs_query_populate(&it, false); - test_int(1, it.count); - - test_bool(true, ecs_query_next_table(&it)); - test_int(0, it.count); - test_assert(it.table == ecs_get_table(world, e2)); - ecs_query_populate(&it, false); - test_int(1, it.count); - ecs_query_skip(&it); - test_bool(false, ecs_query_next_table(&it)); - - test_bool(true, ecs_query_changed(qr, NULL)); - - it = ecs_query_iter(world, qr); - test_bool(true, ecs_query_next_table(&it)); - test_bool(true, ecs_query_changed(NULL, &it)); - test_bool(true, ecs_query_next_table(&it)); - test_bool(false, ecs_query_changed(NULL, &it)); - test_bool(false, ecs_query_next_table(&it)); - - ecs_fini(world); -} - -void Query_create_query_existing_query_entity(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_query_t *q_1 = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.entity = ecs_entity(world, { .name = "q" }), - .filter.terms = { - { .id = Foo }, - { .id = Bar } - }, - }); - - test_assert(q_1 != NULL); - - ecs_query_t *q_2 = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.entity = ecs_entity(world, { .name = "q" }), - .filter.terms = { - { .id = Foo }, - { .id = Bar } - }, - }); - - test_assert(q_2 != NULL); - - ecs_fini(world); -} - -void Query_query_for_recycled_pair(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t rel = ecs_new_entity(world, "Rel"); - ecs_entity_t tgt = ecs_new_entity(world, "Tgt"); - ecs_delete(world, rel); - ecs_delete(world, tgt); - rel = ecs_new_entity(world, "Rel"); - tgt = ecs_new_entity(world, "Tgt"); - - ecs_entity_t e = ecs_new_w_pair(world, rel, tgt); - - ecs_query_t *q = ecs_query(world, { - .filter.terms[0] = { - .first.id = rel, - .second.id = tgt - } - }); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e); - test_uint(ecs_field_id(&it, 1), ecs_pair(rel, tgt)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Query_query_w_singleton_w_rule_iter(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_TAG(ecs, Tag); - ECS_TAG(ecs, Singleton); - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - {Singleton, .src.id = Singleton}, {Tag} - } - }); - - ecs_rule_t *r = ecs_rule(ecs, { - .terms = {{Tag}} - }); - - ecs_singleton_add(ecs, Singleton); - ecs_entity_t e = ecs_new(ecs, Tag); - - ecs_iter_t qit = ecs_query_iter(ecs, q); - test_bool(true, ecs_query_next(&qit)); - test_int(1, qit.count); - test_uint(e, qit.entities[0]); - - ecs_id_t id = qit.ids[0]; - ecs_iter_t rit = ecs_rule_iter(ecs, r); - test_bool(true, ecs_rule_next(&rit)); - test_int(1, rit.count); - test_uint(e, rit.entities[0]); - test_bool(false, ecs_rule_next(&rit)); - test_assert(id == qit.ids[0]); - test_bool(false, ecs_query_next(&qit)); - - ecs_rule_fini(r); - - ecs_fini(ecs); -} - -void Query_query_w_singleton_nested_iter(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_TAG(ecs, TagA); - ECS_TAG(ecs, TagB); - ECS_TAG(ecs, Singleton); - - ecs_query_t *qa = ecs_query(ecs, { - .filter.terms = { - {TagA}, {Singleton, .src.id = Singleton} - } - }); - - ecs_query_t *qb = ecs_query(ecs, { - .filter.terms = { - {TagB}, {Singleton, .src.id = Singleton} - } - }); - - ecs_singleton_add(ecs, Singleton); - - ecs_entity_t ea = ecs_new(ecs, TagA); - ecs_entity_t eb = ecs_new(ecs, TagB); - - ecs_iter_t qita = ecs_query_iter(ecs, qa); - ecs_iter_t qitb = ecs_query_iter(ecs, qb); - - test_bool(ecs_query_next(&qita), true); - test_int(1, qita.count); - test_uint(ea, qita.entities[0]); - - test_bool(ecs_query_next(&qitb), true); - test_int(1, qitb.count); - test_uint(eb, qitb.entities[0]); - - test_uint(TagA, qita.ids[0]); - test_uint(TagB, qitb.ids[0]); - - test_bool(ecs_query_next(&qita), false); - test_bool(ecs_query_next(&qitb), false); - - ecs_fini(ecs); -} - -void Query_query_w_singleton_interleaved_iter(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_TAG(ecs, TagA); - ECS_TAG(ecs, TagB); - ECS_TAG(ecs, TagC); - ECS_TAG(ecs, Singleton); - - ecs_query_t *qa = ecs_query(ecs, { - .filter.terms = { - {TagA}, {Singleton, .src.id = Singleton} - } - }); - - ecs_query_t *qb = ecs_query(ecs, { - .filter.terms = { - {TagB}, {Singleton, .src.id = Singleton} - } - }); - - ecs_query_t *qc = ecs_query(ecs, { - .filter.terms = { - {TagC}, {Singleton, .src.id = Singleton} - } - }); - - ecs_singleton_add(ecs, Singleton); - - ecs_entity_t ea = ecs_new(ecs, TagA); - ecs_entity_t eb = ecs_new(ecs, TagB); - ecs_entity_t ec = ecs_new(ecs, TagC); - - ecs_iter_t qita = ecs_query_iter(ecs, qa); - ecs_iter_t qitb = ecs_query_iter(ecs, qb); - - test_bool(ecs_query_next(&qita), true); - test_int(1, qita.count); - test_uint(ea, qita.entities[0]); - - test_bool(ecs_query_next(&qitb), true); - test_int(1, qitb.count); - test_uint(eb, qitb.entities[0]); - - test_uint(TagA, qita.ids[0]); - test_uint(TagB, qitb.ids[0]); - - test_bool(ecs_query_next(&qita), false); - - ecs_iter_t qitc = ecs_query_iter(ecs, qc); - test_bool(ecs_query_next(&qitc), true); - test_int(1, qitc.count); - test_uint(ec, qitc.entities[0]); - - test_uint(TagB, qitb.ids[0]); - test_uint(TagC, qitc.ids[0]); - - test_bool(ecs_query_next(&qitb), false); - test_bool(ecs_query_next(&qitc), false); - - ecs_fini(ecs); -} - -void Query_recycled_component_id(void) { - ecs_world_t* ecs = ecs_init(); - - for (int i = 0; i < FLECS_HI_COMPONENT_ID; i ++) { - ecs_new_low_id(ecs); - } - - ecs_entity_t e = ecs_new_id(ecs); - ecs_delete(ecs, e); - - ECS_COMPONENT(ecs, Position); - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) } - } - }); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_set(ecs, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(ecs, 0, Position, {20, 30}); - - ecs_iter_t it = ecs_query_iter(ecs, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 2); - test_uint(it.entities[0], e1); - test_uint(it.entities[1], e2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(ecs); -} - -void Query_set_get_context(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_COMPONENT(ecs, Position); - - int ctx = 0; - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) } - }, - .ctx = &ctx - }); - test_assert(q != NULL); - - test_assert(ecs_query_get_ctx(q) == &ctx); - test_assert(ecs_query_get_binding_ctx(q) == NULL); - - ecs_fini(ecs); -} - -void Query_set_get_binding_context(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_COMPONENT(ecs, Position); - - int ctx = 0; - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) } - }, - .binding_ctx = &ctx - }); - test_assert(q != NULL); - - test_assert(ecs_query_get_binding_ctx(q) == &ctx); - test_assert(ecs_query_get_ctx(q) == NULL); - - ecs_fini(ecs); -} - -static void ctx_free(void *ptr) { - *(int*)ptr = 10; -} - -void Query_set_get_context_w_free(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_COMPONENT(ecs, Position); - - int ctx = 0; - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) } - }, - .ctx = &ctx, - .ctx_free = ctx_free - }); - test_assert(q != NULL); - - test_assert(ecs_query_get_ctx(q) == &ctx); - test_assert(ecs_query_get_binding_ctx(q) == NULL); - test_int(ctx, 0); - - ecs_query_fini(q); - - test_int(ctx, 10); - - ecs_fini(ecs); -} - -void Query_set_get_binding_context_w_free(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_COMPONENT(ecs, Position); - - int ctx = 0; - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) } - }, - .binding_ctx = &ctx, - .binding_ctx_free = ctx_free - }); - test_assert(q != NULL); - - test_assert(ecs_query_get_binding_ctx(q) == &ctx); - test_assert(ecs_query_get_ctx(q) == NULL); - test_int(ctx, 0); - - ecs_query_fini(q); - - test_int(ctx, 10); - - ecs_fini(ecs); -} - -void Query_set_this(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_COMPONENT(ecs, Position); - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) } - } - }); - test_assert(q != NULL); - - /* ecs_entity_t e1 = */ ecs_set(ecs, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(ecs, 0, Position, {20, 30}); - /* ecs_entity_t e3 = */ ecs_set(ecs, 0, Position, {30, 40}); - - ecs_iter_t it = ecs_query_iter(ecs, q); - ecs_iter_set_var(&it, 0, e2); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(ecs); -} - -void Query_set_this_no_match(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_COMPONENT(ecs, Position); - ECS_COMPONENT(ecs, Velocity); - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) } - } - }); - test_assert(q != NULL); - - /* ecs_entity_t e1 = */ ecs_set(ecs, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(ecs, 0, Velocity, {20, 30}); - /* ecs_entity_t e3 = */ ecs_set(ecs, 0, Position, {30, 40}); - - ecs_iter_t it = ecs_query_iter(ecs, q); - ecs_iter_set_var(&it, 0, e2); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(ecs); -} - -void Query_set_this_is_true(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_COMPONENT(ecs, Position); - ECS_COMPONENT(ecs, Velocity); - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) } - } - }); - test_assert(q != NULL); - - /* ecs_entity_t e1 = */ ecs_set(ecs, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(ecs, 0, Position, {20, 30}); - /* ecs_entity_t e3 = */ ecs_set(ecs, 0, Position, {30, 40}); - ecs_entity_t e4 = ecs_set(ecs, 0, Velocity, {20, 30}); - - ecs_iter_t it = ecs_query_iter(ecs, q); - ecs_iter_set_var(&it, 0, e2); - test_bool(true, ecs_iter_is_true(&it)); - - it = ecs_query_iter(ecs, q); - ecs_iter_set_var(&it, 0, e4); - test_bool(false, ecs_iter_is_true(&it)); - - ecs_fini(ecs); -} - -void Query_set_this_w_wildcard(void) { - ecs_world_t* ecs = ecs_init(); - - ECS_COMPONENT(ecs, Position); - ECS_TAG(ecs, Likes); - ECS_TAG(ecs, Apples); - ECS_TAG(ecs, Pears); - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_pair(Likes, EcsWildcard) } - } - }); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_set(ecs, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(ecs, 0, Position, {20, 30}); - ecs_entity_t e3 = ecs_set(ecs, 0, Position, {30, 40}); - - ecs_add_pair(ecs, e1, Likes, Apples); - ecs_add_pair(ecs, e2, Likes, Apples); - ecs_add_pair(ecs, e3, Likes, Apples); - - ecs_add_pair(ecs, e1, Likes, Pears); - ecs_add_pair(ecs, e2, Likes, Pears); - ecs_add_pair(ecs, e3, Likes, Pears); - - ecs_iter_t it = ecs_query_iter(ecs, q); - ecs_iter_set_var(&it, 0, e2); - - { - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - test_uint(ecs_pair(Likes, Apples), ecs_field_id(&it, 2)); - } - { - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e2); - Position *p = ecs_field(&it, Position, 1); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - test_uint(ecs_pair(Likes, Pears), ecs_field_id(&it, 2)); - } - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(ecs); -} - -void Query_singleton_w_inout_none(void) { - ecs_world_t *ecs = ecs_mini(); - - ECS_COMPONENT(ecs, Position); - ECS_COMPONENT(ecs, Velocity); - - ecs_singleton_set(ecs, Position, {10, 20}); - - ecs_entity_t e = ecs_set(ecs, 0, Velocity, {20, 30}); - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position), .src.id = EcsVariable }, - { .id = ecs_id(Velocity), .inout = EcsInOutNone }, - } - }); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(ecs, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_assert(ecs_field(&it, Velocity, 2) == NULL); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(ecs); -} - -void Query_singleton_w_inout_none_or(void) { - ecs_world_t *ecs = ecs_mini(); - - ECS_COMPONENT(ecs, Position); - ECS_COMPONENT(ecs, Velocity); - ECS_COMPONENT(ecs, Mass); - - ecs_singleton_set(ecs, Position, {10, 20}); - - ecs_entity_t e = ecs_set(ecs, 0, Velocity, {20, 30}); - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position), .src.id = EcsVariable }, - { .id = ecs_id(Velocity), .inout = EcsInOutNone, .oper = EcsOr }, - { .id = ecs_id(Mass), .inout = EcsInOutNone }, - } - }); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(ecs, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_assert(ecs_field(&it, Velocity, 2) == NULL); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(ecs); -} - -void Query_component_w_inout_none_or(void) { - ecs_world_t *ecs = ecs_mini(); - - ECS_COMPONENT(ecs, Position); - ECS_COMPONENT(ecs, Velocity); - ECS_COMPONENT(ecs, Mass); - - ecs_entity_t e = ecs_set(ecs, 0, Velocity, {20, 30}); - ecs_set(ecs, e, Position, {10, 20}); - - ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { - { .id = ecs_id(Position) }, - { .id = ecs_id(Velocity), .inout = EcsInOutNone, .oper = EcsOr }, - { .id = ecs_id(Mass), .inout = EcsInOutNone }, - } - }); - test_assert(q != NULL); - - ecs_iter_t it = ecs_query_iter(ecs, q); - test_bool(true, ecs_query_next(&it)); - test_int(1, it.count); - test_uint(e, it.entities[0]); - test_uint(ecs_id(Position), ecs_field_id(&it, 1)); - test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); - Position *p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - test_assert(ecs_field(&it, Velocity, 2) == NULL); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(ecs); -} diff --git a/vendors/flecs/test/api/src/Switch.c b/vendors/flecs/test/api/src/Switch.c deleted file mode 100644 index 2015435d0..000000000 --- a/vendors/flecs/test/api/src/Switch.c +++ /dev/null @@ -1,1789 +0,0 @@ -#include - -void Switch_get_case_no_switch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_ENTITY(world, Rel, Union); - - ecs_entity_t e = ecs_new(world, Position); - test_assert(e != 0); - - test_uint(ecs_get_target(world, e, Rel, 0), 0); - - ecs_fini(world); -} - -void Switch_get_case_set(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); - test_assert(e != 0); - - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_assert( !ecs_has_pair(world, e, Movement, Running)); - test_assert( !ecs_has_pair(world, e, Movement, Jumping)); - test_uint(ecs_get_target(world, e, Movement, 0), Walking); - - ecs_fini(world); -} - -void Switch_get_case_change(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); - test_assert(e != 0); - - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_assert( !ecs_has_pair(world, e, Movement, Running)); - test_assert( !ecs_has_pair(world, e, Movement, Jumping)); - test_uint(ecs_get_target(world, e, Movement, 0), Walking); - - ecs_add_pair(world, e, Movement, Running); - - test_assert( !ecs_has_pair(world, e, Movement, Walking)); - test_assert( ecs_has_pair(world, e, Movement, Running)); - test_assert( !ecs_has_pair(world, e, Movement, Jumping)); - test_uint(ecs_get_target(world, e, Movement, 0), Running); - - ecs_add_pair(world, e, Movement, Jumping); - - test_assert( !ecs_has_pair(world, e, Movement, Walking)); - test_assert( !ecs_has_pair(world, e, Movement, Running)); - test_assert( ecs_has_pair(world, e, Movement, Jumping)); - test_uint(ecs_get_target(world, e, Movement, 0), Jumping); - - ecs_fini(world); -} - -void Switch_remove_case(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); - test_assert(e != 0); - - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_assert( !ecs_has_pair(world, e, Movement, Running)); - test_assert( !ecs_has_pair(world, e, Movement, Jumping)); - test_uint(ecs_get_target(world, e, Movement, 0), Walking); - - ecs_remove_pair(world, e, Movement, Walking); - - test_assert( !ecs_has_pair(world, e, Movement, Walking)); - test_assert( !ecs_has_pair(world, e, Movement, Running)); - test_assert( !ecs_has_pair(world, e, Movement, Jumping)); - test_uint(ecs_get_target(world, e, Movement, 0), 0); - - test_assert(ecs_get_type(world, e) == NULL); - - ecs_fini(world); -} - -void Switch_remove_last(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e1 != 0); - ecs_add(world, e1, Tag); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e2 != 0); - ecs_add(world, e2, Tag); - - ecs_remove(world, e2, Tag); - - test_assert(!ecs_has(world, e2, Tag)); - test_assert(ecs_has_pair(world, e2, Movement, Walking)); - - test_assert(ecs_has(world, e1, Tag)); - test_assert(ecs_has_pair(world, e1, Movement, Walking)); - - ecs_fini(world); -} - -void Switch_delete_first(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e1 != 0); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e2 != 0); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e3 != 0); - - ecs_delete(world, e1); - - test_assert(ecs_has_pair(world, e2, Movement, Walking)); - test_assert(ecs_has_pair(world, e3, Movement, Walking)); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e3); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e2); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_delete_last(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e1 != 0); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e2 != 0); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e3 != 0); - - ecs_delete(world, e3); - - test_assert(ecs_has_pair(world, e1, Movement, Walking)); - test_assert(ecs_has_pair(world, e2, Movement, Walking)); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e2); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e1); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_delete_first_last(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e1 != 0); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e2 != 0); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e3 != 0); - - ecs_delete(world, e1); - ecs_delete(world, e3); - - test_assert(ecs_has_pair(world, e2, Movement, Walking)); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e2); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_3_entities_same_case(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_ENTITY(world, e1, (Movement, Running)); - ECS_ENTITY(world, e2, (Movement, Running)); - ECS_ENTITY(world, e3, (Movement, Running)); - - test_assert( ecs_has_pair(world, e1, Movement, Running)); - test_assert( ecs_has_pair(world, e2, Movement, Running)); - test_assert( ecs_has_pair(world, e3, Movement, Running)); - - test_int(ecs_get_target(world, e1, Movement, 0), Running); - test_int(ecs_get_target(world, e2, Movement, 0), Running); - test_int(ecs_get_target(world, e3, Movement, 0), Running); - - ecs_fini(world); -} - -void Switch_2_entities_1_change_case(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_ENTITY(world, e1, (Movement, Running)); - ECS_ENTITY(world, e2, (Movement, Running)); - - ecs_add_pair(world, e2, Movement, Jumping); - test_assert( ecs_has_pair(world, e1, Movement, Running)); - test_assert( ecs_has_pair(world, e2, Movement, Jumping)); - - test_int(ecs_get_target(world, e1, Movement, 0), Running); - test_int(ecs_get_target(world, e2, Movement, 0), Jumping); - - ecs_fini(world); -} - -void Switch_3_entities_change_case(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_ENTITY(world, e1, (Movement, Running)); - ECS_ENTITY(world, e2, (Movement, Running)); - ECS_ENTITY(world, e3, (Movement, Running)); - - ecs_add_pair(world, e1, Movement, Walking); - test_assert( ecs_has_pair(world, e1, Movement, Walking)); - test_assert( ecs_has_pair(world, e2, Movement, Running)); - test_assert( ecs_has_pair(world, e3, Movement, Running)); - - test_int(ecs_get_target(world, e1, Movement, 0), Walking); - test_int(ecs_get_target(world, e2, Movement, 0), Running); - test_int(ecs_get_target(world, e3, Movement, 0), Running); - - ecs_add_pair(world, e2, Movement, Jumping); - test_assert( ecs_has_pair(world, e1, Movement, Walking)); - test_assert( ecs_has_pair(world, e2, Movement, Jumping)); - test_assert( ecs_has_pair(world, e3, Movement, Running)); - - test_int(ecs_get_target(world, e1, Movement, 0), Walking); - test_int(ecs_get_target(world, e2, Movement, 0), Jumping); - test_int(ecs_get_target(world, e3, Movement, 0), Running); - - ecs_add_pair(world, e3, Movement, Walking); - test_assert( ecs_has_pair(world, e1, Movement, Walking)); - test_assert( ecs_has_pair(world, e2, Movement, Jumping)); - test_assert( ecs_has_pair(world, e3, Movement, Walking)); - - test_int(ecs_get_target(world, e1, Movement, 0), Walking); - test_int(ecs_get_target(world, e2, Movement, 0), Jumping); - test_int(ecs_get_target(world, e3, Movement, 0), Walking); - - ecs_fini(world); -} - -static -void MatchSwitch(ecs_iter_t *it) { - ecs_entity_t *movement = ecs_field(it, ecs_entity_t, 1); - test_assert(movement != NULL); - probe_iter(it); -} - -void Switch_query_switch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_query_t *q = ecs_query_new(world, "(Movement, *)"); - test_assert(q != NULL); - - ECS_ENTITY(world, e1, (Movement, Running)); - ECS_ENTITY(world, e2, (Movement, Walking)); - ECS_ENTITY(world, e3, (Movement, Running)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 3); - test_uint(it.entities[0], e1); - test_uint(it.entities[1], e2); - test_uint(it.entities[2], e3); - test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_query_1_case_1_type(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Running)"); - test_assert(q != NULL); - - ECS_ENTITY(world, e1, (Movement, Running)); - ECS_ENTITY(world, e2, (Movement, Walking)); - ECS_ENTITY(world, e3, (Movement, Running)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(it.ids[0], ecs_pair(Movement, Running)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.ids[0], ecs_pair(Movement, Running)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_query_1_case_2_types(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Running)"); - test_assert(q != NULL); - - ECS_ENTITY(world, e1, (Movement, Running)); - ECS_ENTITY(world, e2, (Movement, Walking)); - ECS_ENTITY(world, e3, (Movement, Running)); - ECS_ENTITY(world, e4, (Movement, Walking), Position); - ECS_ENTITY(world, e5, (Movement, Running), Position); - ECS_ENTITY(world, e6, (Movement, Walking), Position); - ECS_ENTITY(world, e7, (Movement, Running), Position); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e3); - test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e7); - test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e5); - test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); - - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_query_2_cases_1_type(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_ENTITY(world, Direction, Union); - ECS_TAG(world, Front); - ECS_TAG(world, Back); - ECS_TAG(world, Left); - ECS_TAG(world, Right); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Running), (Direction, Front)"); - test_assert(q != NULL); - - ECS_ENTITY(world, e1, (Movement, Running), (Direction, Front)); - ECS_ENTITY(world, e2, (Movement, Walking), (Direction, Front)); - ECS_ENTITY(world, e3, (Movement, Running), (Direction, Back)); - ECS_ENTITY(world, e4, (Movement, Running), (Direction, Front)); - ECS_ENTITY(world, e5, (Movement, Walking), (Direction, Front)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e4); - test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 2)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 2)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_query_2_cases_2_types(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_ENTITY(world, Direction, Union); - ECS_TAG(world, Front); - ECS_TAG(world, Back); - ECS_TAG(world, Left); - ECS_TAG(world, Right); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Running), (Direction, Front)"); - test_assert(q != NULL); - - ECS_ENTITY(world, e1, (Movement, Running), (Direction, Front)); - ECS_ENTITY(world, e2, (Movement, Walking), (Direction, Front)); - ECS_ENTITY(world, e3, (Movement, Running), (Direction, Back)); - ECS_ENTITY(world, e4, (Movement, Running), (Direction, Front)); - ECS_ENTITY(world, e5, (Movement, Walking), (Direction, Front)); - ECS_ENTITY(world, e6, Position, (Movement, Walking), (Direction, Front)); - ECS_ENTITY(world, e7, Position, (Movement, Running), (Direction, Front)); - ECS_ENTITY(world, e8, Position, (Movement, Walking), (Direction, Front)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e4); - test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 2)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 2)); - - test_bool(true, ecs_query_next(&it)); - test_int(it.count, 1); - test_uint(it.entities[0], e7); - test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); - test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 2)); - test_bool(false, ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_query_after_remove(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_ENTITY(world, e1, (Movement, Walking)); - ECS_ENTITY(world, e2, (Movement, Walking)); - ECS_ENTITY(world, e3, (Movement, Running)); - ECS_ENTITY(world, e4, (Movement, Running)); - ECS_ENTITY(world, e5, (Movement, Running)); - ECS_ENTITY(world, e6, (Movement, Jumping)); - ECS_ENTITY(world, e7, (Movement, Jumping)); - - ecs_query_t *q_walking = ecs_query_new(world, "(Movement, Walking)"); - ecs_query_t *q_running = ecs_query_new(world, "(Movement, Running)"); - ecs_query_t *q_jumping = ecs_query_new(world, "(Movement, Jumping)"); - - /* Verify all queries are correctly matched */ - ecs_iter_t it = ecs_query_iter(world, q_walking); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e2); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e1); - test_assert(!ecs_query_next(&it)); - - it = ecs_query_iter(world, q_running); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e5); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e4); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e3); - test_assert(!ecs_query_next(&it)); - - it = ecs_query_iter(world, q_jumping); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e7); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e6); - test_assert(!ecs_query_next(&it)); - - ecs_remove_pair(world, e4, Movement, Running); - test_assert(!ecs_has_pair(world, e4, Movement, Running)); - ecs_entity_t c = ecs_get_target(world, e4, Movement, 0); - test_int(c, 0); - - /* Verify queries are still correctly matched, now excluding e4 */ - it = ecs_query_iter(world, q_walking); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e2); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e1); - test_assert(!ecs_query_next(&it)); - - it = ecs_query_iter(world, q_running); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e5); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e3); - test_assert(!ecs_query_next(&it)); - - it = ecs_query_iter(world, q_jumping); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e7); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e6); - test_assert(!ecs_query_next(&it)); - - ecs_add_pair(world, e4, Movement, Running); - test_assert(ecs_has_pair(world, e4, Movement, Running)); - c = ecs_get_target(world, e4, Movement, 0); - test_int(c, Running); - - /* Verify e4 is now matched again */ - it = ecs_query_iter(world, q_walking); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e2); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e1); - test_assert(!ecs_query_next(&it)); - - it = ecs_query_iter(world, q_running); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e4); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e5); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e3); - test_assert(!ecs_query_next(&it)); - - it = ecs_query_iter(world, q_jumping); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e7); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); test_int(it.entities[0], e6); - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -static -void SetCase(ecs_iter_t *it) { - ecs_world_t *world = it->world; - ecs_entity_t id = ecs_field_id(it, 2); - - int i; - for (i = 0; i < it->count; i ++) { - ecs_add_id(world, it->entities[i], id); - } -} - -void Switch_add_case_in_stage(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_SYSTEM(world, SetCase, EcsOnUpdate, Position, Movement(0, Walking)); - - ECS_ENTITY(world, e1, Position); - ECS_ENTITY(world, e2, Position); - ECS_ENTITY(world, e3, Position); - - ecs_progress(world, 0); - - test_assert(ecs_has_pair(world, e1, Movement, Walking)); - test_assert(ecs_has_pair(world, e2, Movement, Walking)); - test_assert(ecs_has_pair(world, e3, Movement, Walking)); - - ecs_fini(world); -} - -void Switch_change_case_in_stage(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_SYSTEM(world, SetCase, EcsOnUpdate, Position, Movement(0, Walking)); - - ECS_ENTITY(world, e1, Position, (Movement, Running)); - ECS_ENTITY(world, e2, Position, (Movement, Running)); - ECS_ENTITY(world, e3, Position, (Movement, Running)); - - ecs_progress(world, 0); - - test_assert(!ecs_has_pair(world, e1, Movement, Running)); - test_assert(!ecs_has_pair(world, e2, Movement, Running)); - test_assert(!ecs_has_pair(world, e3, Movement, Running)); - - test_assert(ecs_has_pair(world, e1, Movement, Walking)); - test_assert(ecs_has_pair(world, e2, Movement, Walking)); - test_assert(ecs_has_pair(world, e3, Movement, Walking)); - - ecs_fini(world); -} - -void Switch_change_one_case_in_stage(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_SYSTEM(world, SetCase, EcsOnUpdate, Position, Movement(0, Jumping), (Movement, Walking)); - - ECS_ENTITY(world, e0, Position, (Movement, Jumping)); - ECS_ENTITY(world, e1, Position, (Movement, Walking)); - ECS_ENTITY(world, e2, Position, (Movement, Running)); - ECS_ENTITY(world, e3, Position, (Movement, Walking)); - ECS_ENTITY(world, e4, Position, (Movement, Running)); - ECS_ENTITY(world, e5, Position, (Movement, Jumping)); - - ecs_progress(world, 0); - - test_assert(ecs_has_pair(world, e0, Movement, Jumping)); - test_assert(ecs_has_pair(world, e1, Movement, Jumping)); - test_assert(ecs_has_pair(world, e2, Movement, Running)); - test_assert(ecs_has_pair(world, e3, Movement, Jumping)); - test_assert(ecs_has_pair(world, e4, Movement, Running)); - test_assert(ecs_has_pair(world, e5, Movement, Jumping)); - - ecs_fini(world); -} - -static -void RemoveSwitch(ecs_iter_t *it) { - ecs_world_t *world = it->world; - ecs_entity_t id = ecs_field_id(it, 1); - - int i; - for (i = 0; i < it->count; i ++) { - ecs_remove_id(world, it->entities[i], id); - } -} - -void Switch_remove_switch_in_stage(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_SYSTEM(world, RemoveSwitch, EcsOnUpdate, (Movement, *)); - - ECS_ENTITY(world, e1, Position, (Movement, Walking)); - ECS_ENTITY(world, e2, Position, (Movement, Walking)); - ECS_ENTITY(world, e3, Position, (Movement, Walking)); - - ecs_progress(world, 0); - - test_assert(!ecs_has_pair(world, e1, Movement, Walking)); - test_assert(!ecs_has_pair(world, e2, Movement, Walking)); - test_assert(!ecs_has_pair(world, e3, Movement, Walking)); - - test_assert(!ecs_has_pair(world, e1, Movement, EcsWildcard)); - test_assert(!ecs_has_pair(world, e2, Movement, EcsWildcard)); - test_assert(!ecs_has_pair(world, e3, Movement, EcsWildcard)); - - ecs_fini(world); -} - -void Switch_switch_no_match_for_case(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_new_w_pair(world, Movement, Walking); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Running)"); - ecs_iter_t it = ecs_query_iter(world, q); - - int count = 0; - while (ecs_query_next(&it)) { - count ++; - } - - test_assert(count == 0); - - ecs_fini(world); -} - -void Switch_empty_entity_has_case(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - - ecs_entity_t e = ecs_new(world, 0); - test_assert(!ecs_has_pair(world, e, Movement, Walking)); - - ecs_fini(world); -} - -void Switch_zero_entity_has_case(void) { - install_test_abort(); - - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - - test_expect_abort(); - - test_assert(!ecs_has_pair(world, 0, Movement, Walking)); -} - -void Switch_add_to_entity_w_switch(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); - test_assert(e != 0); - - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_int(ecs_get_target(world, e, Movement, 0), Walking); - - ecs_add(world, e, Position); - test_assert( ecs_has(world, e, Position)); - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_int(ecs_get_target(world, e, Movement, 0), Walking); - - ecs_fini(world); -} - -void Switch_add_pair_to_entity_w_switch(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Pair); - - ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); - test_assert(e != 0); - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_int(ecs_get_target(world, e, Movement, 0), Walking); - - ecs_entity_t pair_id = ecs_pair(ecs_id(Position), Pair); - ecs_add_id(world, e, pair_id); - test_assert(ecs_has_id(world, e, pair_id)); - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_int(ecs_get_target(world, e, Movement, 0), Walking); - - ecs_fini(world); -} - -static -int compare_position(ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2) { - const Position *p1 = ptr1; - const Position *p2 = ptr2; - return (p1->x > p2->x) - (p1->x < p2->x); -} - -void Switch_sort(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - ECS_TAG(world, Sitting); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 2}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {2, 2}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {0, 2}); - - ecs_add_pair(world, e1, Movement, Walking); - ecs_add_pair(world, e2, Movement, Running); - ecs_add_pair(world, e3, Movement, Jumping); - ecs_add_pair(world, e4, Movement, Sitting); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ ecs_id(Position) }}, - .order_by_component = ecs_id(Position), - .order_by = compare_position - }); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 4); - test_assert(it.entities[0] == e4); - test_assert(it.entities[1] == e3); - test_assert(it.entities[2] == e2); - test_assert(it.entities[3] == e1); - test_assert(!ecs_query_next(&it)); - - /* Entities will have shuffled around, ensure cases got shuffled too */ - test_uint(ecs_get_target(world, e1, Movement, 0), Walking); - test_uint(ecs_get_target(world, e2, Movement, 0), Running); - test_uint(ecs_get_target(world, e3, Movement, 0), Jumping); - test_uint(ecs_get_target(world, e4, Movement, 0), Sitting); - - ecs_fini(world); -} - -void Switch_recycled_tags(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_delete(world, e1); - ecs_delete(world, e2); - ecs_delete(world, e3); - - ECS_TAG(world, Standing); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - test_assert(Standing > UINT32_MAX); - test_assert(Walking > UINT32_MAX); - test_assert(Running > UINT32_MAX); - - ecs_entity_t e = ecs_new_w_pair(world, Movement, Standing); - test_assert(ecs_has_pair(world, e, Movement, Standing)); - test_assert(!ecs_has_pair(world, e, Movement, Walking)); - test_assert(!ecs_has_pair(world, e, Movement, Running)); - - ecs_add_pair(world, e, Movement, Walking); - test_assert(!ecs_has_pair(world, e, Movement, Standing)); - test_assert(ecs_has_pair(world, e, Movement, Walking)); - test_assert(!ecs_has_pair(world, e, Movement, Running)); - - ecs_fini(world); -} - -void Switch_query_recycled_tags(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - - ecs_delete(world, e1); - ecs_delete(world, e2); - ecs_delete(world, e3); - - ECS_TAG(world, Standing); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - test_assert(Standing > UINT32_MAX); - test_assert(Walking > UINT32_MAX); - test_assert(Running > UINT32_MAX); - - ecs_entity_t e = ecs_new(world, 0); - ecs_add_pair(world, e, Movement, Standing); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Standing)"); - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e); - - ecs_entity_t *cases = ecs_field(&it, ecs_entity_t, 1); - test_assert(cases != NULL); - test_assert(ecs_get_alive(world, cases[0]) == Standing); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_single_case(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e1 != 0); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e2 != 0); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e3 != 0); - - test_assert(ecs_has_pair(world, e1, Movement, Walking)); - test_assert(ecs_has_pair(world, e2, Movement, Walking)); - test_assert(ecs_has_pair(world, e3, Movement, Walking)); - - ecs_delete(world, e1); - - test_assert(ecs_has_pair(world, e2, Movement, Walking)); - test_assert(ecs_has_pair(world, e3, Movement, Walking)); - - ecs_delete(world, e3); - - test_assert(ecs_has_pair(world, e2, Movement, Walking)); - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e2); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_match_switch_on_base_instance(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t base = ecs_new_id(world); - test_assert(base != 0); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - test_assert(e1 != 0); - ecs_add_pair(world, e1, EcsIsA, base); - - test_uint(ecs_get_target(world, e1, Movement, 0), Walking); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_int(it.entities[0], e1); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Switch_switch_w_bitset_query(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, (Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e1, Position, {10, 20}); - ecs_enable_component(world, e1, Position, true); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e2, Position, {11, 22}); - ecs_enable_component(world, e2, Position, false); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e3, Position, {13, 23}); - - test_bool(ecs_is_enabled_component(world, e1, Position), true); - test_bool(ecs_is_enabled_component(world, e2, Position), false); - - Position *p; - ecs_entity_t *c; - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e3); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e1); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_switch_w_bitset_query_inv(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, (Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e1, Position, {10, 20}); - ecs_enable_component(world, e1, Position, true); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e2, Position, {11, 22}); - ecs_enable_component(world, e2, Position, true); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e3, Position, {13, 23}); - - Position *p; - ecs_entity_t *c; - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e3); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e1); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_switch_w_bitset_query_2_elems(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, (Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e1, Position, {10, 20}); - ecs_enable_component(world, e1, Position, true); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e2, Position, {11, 22}); - ecs_enable_component(world, e2, Position, true); - - ecs_entity_t e0 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e0, Position, {11, 22}); - ecs_enable_component(world, e0, Position, false); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e3, Position, {13, 23}); - - Position *p; - ecs_entity_t *c; - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e3); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e1); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e2); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 11); - test_int(p->y, 22); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_switch_w_bitset_query_2_elems_skip(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, (Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e1, Position, {10, 20}); - ecs_enable_component(world, e1, Position, true); - - ecs_entity_t e0 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e0, Position, {11, 22}); - ecs_enable_component(world, e0, Position, false); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e2, Position, {11, 22}); - ecs_enable_component(world, e2, Position, true); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e3, Position, {13, 23}); - - Position *p; - ecs_entity_t *c; - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e3); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e1); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e2); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 11); - test_int(p->y, 22); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_switch_w_bitset_query_elems_interleaved(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - ECS_COMPONENT(world, Position); - - ecs_query_t *q = ecs_query_new(world, "Position, (Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e1, Position, {10, 20}); - ecs_enable_component(world, e1, Position, false); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e2, Position, {11, 22}); - ecs_enable_component(world, e2, Position, true); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e3, Position, {13, 23}); - ecs_enable_component(world, e3, Position, false); - - ecs_entity_t e4 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e4, Position, {13, 23}); - ecs_enable_component(world, e4, Position, true); - - ecs_entity_t e5 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e5, Position, {13, 23}); - ecs_enable_component(world, e5, Position, true); - - ecs_entity_t e6 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e6, Position, {13, 23}); - ecs_enable_component(world, e6, Position, true); - - ecs_entity_t e7 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e7, Position, {13, 23}); - ecs_enable_component(world, e7, Position, false); - - ecs_entity_t e8 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e8, Position, {13, 23}); - ecs_enable_component(world, e8, Position, true); - - Position *p; - ecs_entity_t *c; - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e5); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e8); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_switch_w_bitset_query_elems_interleaved_2_types(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Position, (Movement, Walking)"); - test_assert(q != NULL); - - ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e1, Position, {10, 20}); - ecs_enable_component(world, e1, Position, false); - - ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e2, Position, {11, 22}); - ecs_enable_component(world, e2, Position, true); - - ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e3, Position, {13, 23}); - ecs_enable_component(world, e3, Position, false); - - ecs_entity_t e4 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e4, Position, {13, 23}); - ecs_enable_component(world, e4, Position, true); - - ecs_entity_t e5 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e5, Position, {13, 23}); - ecs_enable_component(world, e5, Position, true); - - ecs_entity_t e6 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e6, Position, {13, 23}); - ecs_enable_component(world, e6, Position, true); - - ecs_entity_t e7 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e7, Position, {13, 23}); - ecs_enable_component(world, e7, Position, false); - - ecs_entity_t e8 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e8, Position, {13, 23}); - ecs_enable_component(world, e8, Position, true); - - - ecs_entity_t e1_2 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e1_2, Position, {10, 20}); - ecs_enable_component(world, e1_2, Position, false); - ecs_add_id(world, e1_2, Tag); - - ecs_entity_t e2_2 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e2_2, Position, {11, 22}); - ecs_enable_component(world, e2_2, Position, true); - ecs_add_id(world, e2_2, Tag); - - ecs_entity_t e3_2 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e3_2, Position, {13, 23}); - ecs_enable_component(world, e3_2, Position, false); - ecs_add_id(world, e3_2, Tag); - - ecs_entity_t e4_2 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e4_2, Position, {13, 23}); - ecs_enable_component(world, e4_2, Position, true); - ecs_add_id(world, e4_2, Tag); - - ecs_entity_t e5_2 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e5_2, Position, {13, 23}); - ecs_enable_component(world, e5_2, Position, true); - ecs_add_id(world, e5_2, Tag); - - ecs_entity_t e6_2 = ecs_new_w_pair(world, Movement, Running); - ecs_set(world, e6_2, Position, {13, 23}); - ecs_enable_component(world, e6_2, Position, true); - ecs_add_id(world, e6_2, Tag); - - ecs_entity_t e7_2 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e7_2, Position, {13, 23}); - ecs_enable_component(world, e7_2, Position, false); - ecs_add_id(world, e7_2, Tag); - - ecs_entity_t e8_2 = ecs_new_w_pair(world, Movement, Walking); - ecs_set(world, e8_2, Position, {13, 23}); - ecs_enable_component(world, e8_2, Position, true); - ecs_add_id(world, e8_2, Tag); - - Position *p; - ecs_entity_t *c; - - ecs_iter_t it = ecs_query_iter(world, q); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e5); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e8); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e5_2); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_int(it.entities[0], e8_2); - p = ecs_field(&it, Position, 1); - test_assert(p != NULL); - test_int(p->x, 13); - test_int(p->y, 23); - c = ecs_field(&it, ecs_entity_t, 2); - test_assert(c != NULL); - test_assert(c[0] == Walking); - - test_assert(!ecs_query_next(&it)); - - ecs_fini(world); -} - -void Switch_has_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_entity_t e = ecs_new_id(world); - test_assert( !ecs_has_pair(world, e, Movement, EcsWildcard)); - - ecs_add_pair(world, e, Movement, Walking); - test_assert( ecs_has_pair(world, e, Movement, EcsWildcard)); - - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_assert( !ecs_has_pair(world, e, Movement, Running)); - test_assert( !ecs_has_pair(world, e, Movement, Jumping)); - test_assert( ecs_has_pair(world, e, Movement, EcsWildcard)); - - ecs_fini(world); -} - -void Switch_remove_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); - test_assert(e != 0); - - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_assert( !ecs_has_pair(world, e, Movement, Running)); - test_assert( !ecs_has_pair(world, e, Movement, Jumping)); - test_uint(ecs_get_target(world, e, Movement, 0), Walking); - - ecs_remove_pair(world, e, Movement, EcsWildcard); - - test_assert( !ecs_has_pair(world, e, Movement, Walking)); - test_assert( !ecs_has_pair(world, e, Movement, Running)); - test_assert( !ecs_has_pair(world, e, Movement, Jumping)); - test_uint(ecs_get_target(world, e, Movement, 0), 0); - test_assert( !ecs_has_pair(world, e, Movement, EcsWildcard)); - - ecs_fini(world); -} - -void Switch_same_table_after_change(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); - test_assert(e != 0); - - ecs_table_t *table = ecs_get_table(world, e); - test_assert(table != NULL); - - ecs_add_pair(world, e, Movement, Running); - test_assert(ecs_get_table(world, e) == table); - - ecs_add_pair(world, e, Movement, Jumping); - test_assert(ecs_get_table(world, e) == table); - - ecs_remove_pair(world, e, Movement, EcsWildcard); - test_assert(ecs_get_table(world, e) != table); - test_assert(ecs_get_table(world, e) == NULL); - - ecs_fini(world); -} - -void Switch_component_relation(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_ENTITY(world, Direction, Union); - ECS_TAG(world, Front); - ECS_TAG(world, Back); - ECS_TAG(world, Left); - ECS_TAG(world, Right); - - ecs_set(world, Movement, EcsComponent, { .size = 1, .alignment = 1 }); - ecs_set(world, Direction, EcsComponent, { .size = 1, .alignment = 1 }); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_add_pair(world, e1, Movement, Walking); - ecs_add_pair(world, e1, Direction, Front); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_add_pair(world, e2, Movement, Running); - ecs_add_pair(world, e2, Direction, Left); - - ecs_query_t *q = ecs_query_new(world, "(Movement, Walking), (Direction, *)"); - test_assert(q != NULL); - - ecs_entity_t *m; - ecs_entity_t *d; - - ecs_iter_t it = ecs_query_iter(world, q); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_uint(it.entities[0], e1); - test_uint(it.ids[0], ecs_pair(Movement, Walking)); - test_uint(it.ids[1], ecs_pair(Direction, EcsWildcard)); - m = ecs_field(&it, ecs_entity_t, 1); - d = ecs_field(&it, ecs_entity_t, 2); - test_uint(m[0], Walking); - test_uint(d[0], Front); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -static -void Trigger(ecs_iter_t *it) { - probe_system_w_ctx(it, it->ctx); -} - -void Switch_delete_case_trigger_after_delete_switch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - Probe ctx = {0}; - - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { .id = ecs_pair(Movement, Walking), .src.flags = EcsSelf }, - .events = {EcsOnAdd}, - .callback = Trigger, - .ctx = &ctx - }); - - ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); - test_assert( ecs_has_pair(world, e, Movement, Walking)); - - ecs_delete(world, Movement); - - ecs_fini(world); -} - -void Switch_add_2(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_ENTITY(world, Direction, Union); - ECS_TAG(world, Up); - ECS_TAG(world, Down); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Movement, Walking); - ecs_add_pair(world, e, Direction, Up); - - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_assert( ecs_has_pair(world, e, Direction, Up)); - - test_assert(ecs_get_target(world, e, Movement, 0) == Walking); - test_assert(ecs_get_target(world, e, Direction, 0) == Up); - - ecs_fini(world); -} - -void Switch_add_2_reverse(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ECS_ENTITY(world, Direction, Union); - ECS_TAG(world, Up); - ECS_TAG(world, Down); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Direction, Up); - ecs_add_pair(world, e, Movement, Walking); - - test_assert( ecs_has_pair(world, e, Movement, Walking)); - test_assert( ecs_has_pair(world, e, Direction, Up)); - - test_assert(ecs_get_target(world, e, Movement, 0) == Walking); - test_assert(ecs_get_target(world, e, Direction, 0) == Up); - - ecs_fini(world); -} - -void Switch_add_switch_to_prefab_instance(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - ECS_TAG(world, Jumping); - - ecs_entity_t base = ecs_new_id(world); - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - ecs_add_pair(world, inst, Movement, Walking); - - test_assert(ecs_has_pair(world, inst, Movement, Walking)); - test_assert(ecs_get_target(world, inst, Movement, 0) == Walking); - - ecs_fini(world); -} - -void Switch_get_case_w_generation(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Union); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_delete(world, tgt); - tgt = ecs_new_id(world); - test_assert(tgt != (uint32_t)tgt); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Rel, tgt); - - ecs_entity_t t = ecs_get_target(world, e, Rel, 0); - test_assert(t == tgt); - - ecs_fini(world); -} - -void Switch_get_case_w_generation_not_alive(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Union); - - ecs_entity_t tgt = ecs_new_id(world); - ecs_delete(world, tgt); - tgt = ecs_new_id(world); - test_assert(tgt != (uint32_t)tgt); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Rel, tgt); - - ecs_entity_t t = ecs_get_target(world, e, Rel, 0); - test_assert(t == tgt); - - ecs_delete(world, tgt); - - t = ecs_get_target(world, e, Rel, 0); - test_assert(t == tgt); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/Table.c b/vendors/flecs/test/api/src/Table.c deleted file mode 100644 index c9adbc984..000000000 --- a/vendors/flecs/test/api/src/Table.c +++ /dev/null @@ -1,379 +0,0 @@ -#include - -void Table_get_index(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e = ecs_new_id(world); - ecs_set(world, e, Position, {10, 20}); - ecs_set(world, e, Velocity, {10, 20}); - - ecs_table_t *table = ecs_get_table(world, e); - test_assert(table != NULL); - test_int(ecs_table_get_type_index(world, table, ecs_id(Position)), 0); - test_int(ecs_table_get_type_index(world, table, ecs_id(Velocity)), 1); - - ecs_fini(world); -} - -void Table_get_index_not_in_table(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t e = ecs_new_id(world); - ecs_set(world, e, Position, {10, 20}); - ecs_set(world, e, Velocity, {10, 20}); - - ecs_table_t *table = ecs_get_table(world, e); - test_assert(table != NULL); - test_int(ecs_table_get_type_index(world, table, ecs_id(Position)), 0); - test_int(ecs_table_get_type_index(world, table, ecs_id(Velocity)), 1); - test_int(ecs_table_get_type_index(world, table, ecs_id(Mass)), -1); - - ecs_fini(world); -} - -void Table_get_column(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_set(world, e2, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - - ecs_table_t *table = ecs_get_table(world, e1); - test_assert(table != NULL); - int32_t pos_id = ecs_table_get_column_index(world, table, ecs_id(Position)); - int32_t vel_id = ecs_table_get_column_index(world, table, ecs_id(Velocity)); - - Position *p = ecs_table_get_column(table, pos_id, 0); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - - Velocity *v = ecs_table_get_column(table, vel_id, 0); - test_assert(v != NULL); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_int(v[1].x, 2); - test_int(v[1].y, 3); - - ecs_fini(world); -} - -void Table_get_column_for_tag(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_add(world, e1, Tag); - - ecs_table_t *table = ecs_get_table(world, e1); - test_assert(table != NULL); - - test_expect_abort(); - int32_t tag_id = ecs_table_get_type_index(world, table, Tag); - - test_expect_abort(); - ecs_table_get_column(table, tag_id, 0); -} - -void Table_get_column_for_component_after_tag(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t tag_w_low_id = ecs_entity(world, { - .use_low_id = true - }); - - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - ecs_add_id(world, e1, tag_w_low_id); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_set(world, e2, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - ecs_add_id(world, e2, tag_w_low_id); - - ecs_table_t *table = ecs_get_table(world, e1); - test_assert(table != NULL); - int32_t pos_id = ecs_table_get_column_index(world, table, ecs_id(Position)); - int32_t vel_id = ecs_table_get_column_index(world, table, ecs_id(Velocity)); - - test_assert(pos_id != -1); - test_assert(vel_id != -1); - - Position *p = ecs_table_get_column(table, pos_id, 0); - test_assert(p != NULL); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - - Velocity *v = ecs_table_get_column(table, vel_id, 0); - test_assert(v != NULL); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_int(v[1].x, 2); - test_int(v[1].y, 3); - - ecs_fini(world); -} - -void Table_get_column_w_offset(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_set(world, e2, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - - ecs_table_t *table = ecs_get_table(world, e1); - test_assert(table != NULL); - int32_t pos_id = ecs_table_get_column_index(world, table, ecs_id(Position)); - int32_t vel_id = ecs_table_get_column_index(world, table, ecs_id(Velocity)); - - Position *p = ecs_table_get_column(table, pos_id, 1); - test_assert(p != NULL); - test_int(p[0].x, 20); - test_int(p[0].y, 30); - - Velocity *v = ecs_table_get_column(table, vel_id, 1); - test_assert(v != NULL); - test_int(v[0].x, 2); - test_int(v[0].y, 3); - - ecs_fini(world); -} - -void Table_get_id(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_set(world, e2, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - - ecs_table_t *table = ecs_get_table(world, e1); - test_assert(table != NULL); - - Position *p = ecs_table_get_id(world, table, ecs_id(Position), 0); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - - Velocity *v = ecs_table_get_id(world, table, ecs_id(Velocity), 0); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_int(v[1].x, 2); - test_int(v[1].y, 3); - - ecs_fini(world); -} - -void Table_get_component(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_set(world, e2, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - - ecs_table_t *table = ecs_get_table(world, e1); - test_assert(table != NULL); - - Position *p = ecs_table_get(world, table, Position, 0); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - - Velocity *v = ecs_table_get(world, table, Velocity, 0); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_int(v[1].x, 2); - test_int(v[1].y, 3); - - ecs_fini(world); -} - -void Table_get_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set_pair(world, e1, Position, Tgt, {10, 20}); - ecs_set_pair(world, e1, Velocity, Tgt, {1, 2}); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_set_pair(world, e2, Position, Tgt, {20, 30}); - ecs_set_pair(world, e2, Velocity, Tgt, {2, 3}); - - ecs_table_t *table = ecs_get_table(world, e1); - test_assert(table != NULL); - - Position *p = ecs_table_get_pair(world, table, Position, Tgt, 0); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - - Velocity *v = ecs_table_get_pair(world, table, Velocity, Tgt, 0); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_int(v[1].x, 2); - test_int(v[1].y, 3); - - ecs_fini(world); -} - -void Table_get_from_stage(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e1, Velocity, {1, 2}); - - ecs_entity_t e2 = ecs_new_id(world); - ecs_set(world, e2, Position, {20, 30}); - ecs_set(world, e2, Velocity, {2, 3}); - - ecs_table_t *table = ecs_get_table(world, e1); - test_assert(table != NULL); - - ecs_world_t *stage = ecs_get_stage(world, 0); - test_assert(stage != NULL); - test_assert(stage != world); - - Position *p = ecs_table_get(stage, table, Position, 0); - test_int(p[0].x, 10); - test_int(p[0].y, 20); - test_int(p[1].x, 20); - test_int(p[1].y, 30); - - Velocity *v = ecs_table_get(stage, table, Velocity, 0); - test_int(v[0].x, 1); - test_int(v[0].y, 2); - test_int(v[1].x, 2); - test_int(v[1].y, 3); - - ecs_fini(world); -} - -void Table_get_depth(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, e3); - - test_int(1, ecs_table_get_depth(world, ecs_get_table(world, e2), EcsChildOf)); - test_int(2, ecs_table_get_depth(world, ecs_get_table(world, e3), EcsChildOf)); - test_int(3, ecs_table_get_depth(world, ecs_get_table(world, e4), EcsChildOf)); - - ecs_fini(world); -} - -void Table_get_depth_non_acyclic(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Rel); - ECS_TAG(world, Tgt); - - ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); - - test_expect_abort(); - ecs_table_get_depth(world, ecs_get_table(world, e1), Rel); -} - -void Table_get_depth_2_paths(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); - ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, e2); - ecs_add_pair(world, e4, EcsIsA, e3); - - test_int(1, ecs_table_get_depth(world, ecs_get_table(world, e2), EcsIsA)); - test_int(2, ecs_table_get_depth(world, ecs_get_table(world, e3), EcsIsA)); - test_int(3, ecs_table_get_depth(world, ecs_get_table(world, e4), EcsIsA)); - - ecs_fini(world); -} - -void Table_get_column_size(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_entity_t ecs_id(Mass) = ecs_component(world, { - .entity = ecs_new_id(world), - .type.size = 4, - .type.alignment = 4 - }); - - ecs_entity_t e1 = ecs_new_id(world); - ecs_set(world, e1, Position, {10, 20}); - ecs_add(world, e1, Tag); - ecs_set(world, e1, Mass, {1}); - - ecs_table_t *table = ecs_get_table(world, e1); - test_assert(table != NULL); - - test_uint(8, ecs_table_get_column_size(table, 0)); - test_uint(4, ecs_table_get_column_size(table, 1)); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/util.c b/vendors/flecs/test/api/src/util.c deleted file mode 100644 index 3d0ac9f9c..000000000 --- a/vendors/flecs/test/api/src/util.c +++ /dev/null @@ -1,319 +0,0 @@ -#include -#include - -void probe_system_w_ctx( - ecs_iter_t *it, - Probe *ctx) -{ - if (!ctx) { - return; - } - - ctx->param = it->param; - ctx->system = it->system; - ctx->event = it->event; - ctx->event_id = it->event_id; - ctx->offset = 0; - ctx->term_count = it->field_count; - ctx->term_index = it->term_index; - - int i; - for (i = 0; i < ctx->term_count; i ++) { - ctx->c[ctx->invoked][i] = it->ids[i]; - ctx->s[ctx->invoked][i] = ecs_field_src(it, i + 1); - - ecs_id_t e = ecs_field_id(it, i + 1); - test_assert(e != 0); - } - - for (i = 0; i < it->count; i ++) { - if (i + ctx->count < 256) { - ctx->e[i + ctx->count] = it->entities[i]; - } else { - /* can't store more than that, tests shouldn't rely on - * getting back more than 256 results */ - } - } - ctx->count += it->count; - - ctx->invoked ++; -} - -void probe_iter( - ecs_iter_t *it) -{ - Probe *ctx = ecs_get_ctx(it->world); - if (!ctx) { - ctx = it->ctx; - } - probe_system_w_ctx(it, ctx); -} - -void probe_has_entity(Probe *probe, ecs_entity_t e) { - int i; - for (i = 0; i < probe->count; i ++) { - if (probe->e[i] == e) { - break; - } - } - - test_assert(i != probe->count); -} - -void install_test_abort(void) { - ecs_os_set_api_defaults(); - ecs_os_api_t os_api = ecs_os_get_api(); - os_api.abort_ = test_abort; - ecs_os_set_api(&os_api); - ecs_log_set_level(-5); -} - -const ecs_entity_t* bulk_new_w_type( - ecs_world_t *world, ecs_entity_t type_ent, int32_t count) -{ - const ecs_type_t *type = ecs_get_type(world, type_ent); - test_assert(type != NULL); - - ecs_id_t *ids = type->array; - int i = 0; - while ((ecs_id_get_flags(world, ids[i]) & EcsIdDontInherit)) { - i ++; - } - const ecs_entity_t *result = ecs_bulk_new_w_id(world, ids[i], count); - for (; i < type->count; i ++) { - for (int e = 0; e < count; e ++) { - if (ecs_id_get_flags(world, ids[i]) & EcsIdDontInherit) { - continue; - } - ecs_add_id(world, result[e], ids[i]); - } - } - - return result; -} - -int32_t find_entity( - ecs_world_t *world, - test_iter_result_t *expect, - ecs_entity_t e) -{ - int i; - for (i = 0; i < ITER_MAX_ENTITIES; i ++) { - if (expect->entities[i] == e) { - while (expect->matched[i]) { - i ++; - if (!if_test_assert(e == expect->entities[i])) { - return -1; - } - } - - if (expect->entity_names[i]) { - if (!if_test_str(ecs_get_name(world, e), expect->entity_names[i])) { - return -1; - } - } - return i; - } - } - - for (i = 0; i < ITER_MAX_ENTITIES; i ++) { - if (!expect->entity_names[i]) { - break; - } - - if (!strcmp(ecs_get_name(world, e), expect->entity_names[i])) { - while (expect->matched[i]) { - i ++; - - // If this fails, the entity is encountered more times than - // expected. - if (!if_test_str(ecs_get_name(world, e), - expect->entity_names[i])) - { - return -1; - } - } - - return i; - } - } - - return -1; -} - -bool test_iter( - ecs_iter_t *it, - ecs_iter_next_action_t next, - test_iter_result_t *expect) -{ - int32_t entity_index = -1; - - while (next(it)) { - int i; - - for (i = 0; (i < it->count) || (i < 1); i ++) { - ecs_entity_t e = 0; - int t; - - if (it->count) { - e = it->entities[i]; - - entity_index = find_entity(it->world, expect, e); - - // Matched unexpected entity - test_assert(entity_index != -1); - - expect->matched[entity_index] = true; - - // Test data - for (t = 0; t < it->field_count; t++) { - size_t size = ecs_field_size(it, t + 1); - if (!size) { - continue; - } - - void *expect_ptr = expect->term_columns[t]; - if (!expect_ptr) { - continue; - } - - expect_ptr = ECS_OFFSET(expect_ptr, size * entity_index); - - void *component_ptr = ecs_field_w_size(it, size, t + 1); - if (!if_test_assert(component_ptr != NULL)) { - return false; - } - - component_ptr = ECS_OFFSET(component_ptr, size * i); - if (!if_test_assert(memcpy(component_ptr, expect_ptr, size))) { - return false; - } - } - } else { - entity_index ++; - } - - - // Test ids - ecs_id_t *ids = expect->term_ids[entity_index]; - if (!ids[0]) { - ids = expect->term_ids[0]; - } - - for (t = 0; t < it->field_count; t++) { - if (!ids[t]) { - break; - } - - if (!if_test_assert(ecs_field_id(it, t + 1) == ids[t])) { - return false; - } - } - - if (!if_test_assert(ids[t] == 0)) { - return false; - } - - - // Test ids by expr - char **ids_expect = expect->term_ids_expr[entity_index]; - if (!ids_expect) { - ids_expect = expect->term_ids_expr[0]; - } - - for (t = 0; t < it->field_count; t++) { - if (!ids_expect[t]) { - break; - } - - char *id_found = ecs_id_str(it->world, ecs_field_id(it, t + 1)); - if (!if_test_str(id_found, ids_expect[t])) { - printf(" - term %d\n", t); - if (e) { - printf(" - matched entity %u (%s, [%s])\n", - (uint32_t)e, - ecs_get_name(it->world, e), - ecs_type_str(it->world, ecs_get_type(it->world, e))); - - if (expect->entities[i]) { - printf(" - expected entity %u (%s)\n", - (uint32_t)expect->entities[i], - ecs_get_name(it->world, expect->entities[i])); - } else if (expect->entity_names[i]) { - printf(" - expected entity %s\n", - expect->entity_names[i]); - } - } - - printf(" - @ result index %d\n", entity_index); - return false; - } - ecs_os_free(id_found); - } - - if (!if_test_assert(ids_expect[t] == NULL)) { - return false; - } - - - // Test variables - int v; - for (v = 0; v < ITER_MAX_VARIABLES; v++) { - int32_t id = expect->variables[v].id; - if (!id) { - break; - } - - ecs_entity_t e = expect->variables[v].entities[entity_index]; - if (!e) { - e = expect->variables[v].entities[0]; - } - if (e) { - ecs_entity_t var = ecs_iter_get_var(it, id); - if (!if_test_assert(e == var)) { - return false; - } - } - - const char *name = expect->variables[v].entity_names[entity_index]; - if (!name) { - name = expect->variables[v].entity_names[0]; - } - if (name) { - ecs_entity_t var = ecs_iter_get_var(it, id); - if (!if_test_str(name, ecs_get_name(it->world, var))) { - printf(" - variable id %d\n", id); - printf(" - index %d\n", entity_index); - return false; - } - } - - /* If a variable id is set, either an entity or entity name must - * be set. */ - if (!if_test_assert(e || name)) { - return false; - } - } - } - - expect->table_count_actual ++; - } - - for (int i = 0; i < ITER_MAX_ENTITIES; i ++) { - if (expect->entities[i] || expect->entity_names[i]) { - if (!if_test_assert(expect->matched[i])) { - printf(" - entity %u (%s) at index %d not matched\n", - (uint32_t)expect->entities[i], expect->entity_names[i], i); - return false; - } - } - } - - if (expect->table_count_expect) { - if (!if_test_assert(expect->table_count_actual == expect->table_count_expect)) { - return false; - } - } - - return true; -} diff --git a/vendors/flecs/test/collections/project.json b/vendors/flecs/test/collections/project.json index 16825ad9f..5ea9b99ed 100644 --- a/vendors/flecs/test/collections/project.json +++ b/vendors/flecs/test/collections/project.json @@ -69,7 +69,8 @@ "count_of_null", "try_low_after_ensure_high", "is_alive_low_after_ensure_high", - "remove_low_after_ensure_high" + "remove_low_after_ensure_high", + "ensure_skip_generation" ] }, { "id": "Strbuf", @@ -83,22 +84,40 @@ "append_nested_list", "large_str", "empty_str", - "append_zerocopy", - "append_zerocopy_only", - "append_zerocopy_const", "reset", "merge", - "app_buffer", + "merge_empty", "append_char", "append_511_chars", "append_512_chars", "append_513_chars", + "append_1023_chars", + "append_1024_chars", + "append_1025_chars", + "append_2047_chars", + "append_2048_chars", + "append_2049_chars", + "append_511_str", + "append_512_str", + "append_513_str", + "append_1023_str", + "append_1024_str", + "append_1025_str", + "append_2047_str", + "append_2048_str", + "append_2049_str", "append_flt", "append_nan", "append_inf", "append_nan_delim", "append_inf_delim" ] + }, { + "id": "Allocator", + "setup": true, + "testcases": [ + "init_fini_empty" + ] }] } } diff --git a/vendors/flecs/test/collections/src/Allocator.c b/vendors/flecs/test/collections/src/Allocator.c new file mode 100644 index 000000000..4b8515561 --- /dev/null +++ b/vendors/flecs/test/collections/src/Allocator.c @@ -0,0 +1,12 @@ +#include + +void Allocator_setup(void) { + ecs_os_set_api_defaults(); +} + +void Allocator_init_fini_empty(void) { + ecs_allocator_t a; + flecs_allocator_init(&a); + flecs_allocator_fini(&a); + test_assert(true); // make sure there are no leaks, crashses +} diff --git a/vendors/flecs/test/collections/src/Sparse.c b/vendors/flecs/test/collections/src/Sparse.c index 03fe4d728..2c0a41c59 100644 --- a/vendors/flecs/test/collections/src/Sparse.c +++ b/vendors/flecs/test/collections/src/Sparse.c @@ -1,5 +1,5 @@ #include -#include +#include void Sparse_setup(void) { ecs_os_set_api_defaults(); @@ -392,3 +392,50 @@ void Sparse_remove_low_after_ensure_high(void) { flecs_sparse_free(sp); } + +void Sparse_ensure_skip_generation(void) { + ecs_sparse_t *sp1 = flecs_sparse_new(NULL, NULL, int); + ecs_sparse_t *sp2 = flecs_sparse_new(NULL, NULL, int); + + uint64_t id = flecs_sparse_new_id(sp1); + flecs_sparse_ensure(sp2, 0, id); + + test_assert(flecs_sparse_is_alive(sp1, id)); + test_assert(flecs_sparse_is_alive(sp2, id)); + + flecs_sparse_remove(sp1, 0, id); + flecs_sparse_remove(sp2, 0, id); + + test_assert(!flecs_sparse_is_alive(sp1, id)); + test_assert(!flecs_sparse_is_alive(sp2, id)); + + uint64_t id_2 = flecs_sparse_new_id(sp1); + test_assert((uint32_t)id_2 == id); + + test_assert(flecs_sparse_is_alive(sp1, id_2)); + test_assert(!flecs_sparse_is_alive(sp2, id_2)); + test_assert(!flecs_sparse_is_alive(sp1, id)); + test_assert(!flecs_sparse_is_alive(sp2, id)); + + flecs_sparse_remove(sp1, 0, id_2); + + test_assert(!flecs_sparse_is_alive(sp1, id_2)); + test_assert(!flecs_sparse_is_alive(sp2, id_2)); + test_assert(!flecs_sparse_is_alive(sp1, id)); + test_assert(!flecs_sparse_is_alive(sp2, id)); + + uint64_t id_3 = flecs_sparse_new_id(sp1); + test_assert((uint32_t)id_3 == id); + flecs_sparse_ensure(sp2, 0, id_3); + + test_assert(flecs_sparse_is_alive(sp1, id_3)); + test_assert(flecs_sparse_is_alive(sp2, id_3)); + + test_assert(!flecs_sparse_is_alive(sp1, id_2)); + test_assert(!flecs_sparse_is_alive(sp2, id_2)); + test_assert(!flecs_sparse_is_alive(sp1, id)); + test_assert(!flecs_sparse_is_alive(sp2, id)); + + flecs_sparse_free(sp1); + flecs_sparse_free(sp2); +} diff --git a/vendors/flecs/test/collections/src/Strbuf.c b/vendors/flecs/test/collections/src/Strbuf.c index 8314b8e95..32ddbe6bd 100644 --- a/vendors/flecs/test/collections/src/Strbuf.c +++ b/vendors/flecs/test/collections/src/Strbuf.c @@ -106,35 +106,6 @@ void Strbuf_empty_str(void) { test_assert(str == NULL); } -void Strbuf_append_zerocopy(void) { - ecs_strbuf_t b = ECS_STRBUF_INIT; - ecs_strbuf_appendstr(&b, "Foo"); - ecs_strbuf_appendstr_zerocpy(&b, ecs_os_strdup("Bar")); - char *str = ecs_strbuf_get(&b); - test_assert(str != NULL); - test_str(str, "FooBar"); - ecs_os_free(str); -} - -void Strbuf_append_zerocopy_const(void) { - ecs_strbuf_t b = ECS_STRBUF_INIT; - ecs_strbuf_appendstr(&b, "Foo"); - ecs_strbuf_appendstr_zerocpy_const(&b, "Bar"); - char *str = ecs_strbuf_get(&b); - test_assert(str != NULL); - test_str(str, "FooBar"); - ecs_os_free(str); -} - -void Strbuf_append_zerocopy_only(void) { - ecs_strbuf_t b = ECS_STRBUF_INIT; - ecs_strbuf_appendstr_zerocpy(&b, ecs_os_strdup("Bar")); - char *str = ecs_strbuf_get(&b); - test_assert(str != NULL); - test_str(str, "Bar"); - ecs_os_free(str); -} - void Strbuf_reset(void) { ecs_strbuf_t b = ECS_STRBUF_INIT; ecs_strbuf_appendstr(&b, "Foo"); @@ -162,18 +133,20 @@ void Strbuf_merge(void) { test_assert(str == NULL); } -void Strbuf_app_buffer(void) { - char buf[256]; - ecs_strbuf_t b = ECS_STRBUF_INIT; - b.buf = buf; - b.max = 256; - ecs_strbuf_appendstr(&b, "Foo"); - ecs_strbuf_appendstr(&b, "Bar"); +void Strbuf_merge_empty(void) { + ecs_strbuf_t b1 = ECS_STRBUF_INIT; + ecs_strbuf_appendstr(&b1, "Foo"); + ecs_strbuf_appendstr(&b1, "Bar"); - char *str = ecs_strbuf_get(&b); - test_assert(str != NULL); + ecs_strbuf_t b2 = ECS_STRBUF_INIT; + ecs_strbuf_mergebuff(&b1, &b2); + + char *str = ecs_strbuf_get(&b1); test_str(str, "FooBar"); ecs_os_free(str); + + str = ecs_strbuf_get(&b2); + test_assert(str == NULL); } void Strbuf_append_char(void) { @@ -233,6 +206,258 @@ void Strbuf_append_513_chars(void) { ecs_os_free(str); } +void Strbuf_append_1023_chars(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 1023; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + test_assert(str != NULL); + for (int i = 0; i < 1023; i ++) { + test_assert(str[i] == ('a' + i % 26)); + } + ecs_os_free(str); +} + +void Strbuf_append_1024_chars(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 1024; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + test_assert(str != NULL); + for (int i = 0; i < 1024; i ++) { + test_assert(str[i] == ('a' + i % 26)); + } + ecs_os_free(str); +} + +void Strbuf_append_1025_chars(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 1025; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + test_assert(str != NULL); + for (int i = 0; i < 1025; i ++) { + test_assert(str[i] == ('a' + i % 26)); + } + ecs_os_free(str); +} + +void Strbuf_append_2047_chars(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 2047; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + test_assert(str != NULL); + for (int i = 0; i < 2047; i ++) { + test_assert(str[i] == ('a' + i % 26)); + } + ecs_os_free(str); +} + +void Strbuf_append_2048_chars(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 2048; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + test_assert(str != NULL); + for (int i = 0; i < 2048; i ++) { + test_assert(str[i] == ('a' + i % 26)); + } + ecs_os_free(str); +} + +void Strbuf_append_2049_chars(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 2049; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + test_assert(str != NULL); + for (int i = 0; i < 2049; i ++) { + test_assert(str[i] == ('a' + i % 26)); + } + ecs_os_free(str); +} + +void Strbuf_append_511_str(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 511; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + + ecs_strbuf_appendstr(&b, str); + char *str2 = ecs_strbuf_get(&b); + + test_str(str, str2); + + ecs_os_free(str); + ecs_os_free(str2); +} + +void Strbuf_append_512_str(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 512; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + + ecs_strbuf_appendstr(&b, str); + char *str2 = ecs_strbuf_get(&b); + + test_str(str, str2); + + ecs_os_free(str); + ecs_os_free(str2); +} + +void Strbuf_append_513_str(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 513; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + + ecs_strbuf_appendstr(&b, str); + char *str2 = ecs_strbuf_get(&b); + + test_str(str, str2); + + ecs_os_free(str); + ecs_os_free(str2); +} + +void Strbuf_append_1023_str(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 1023; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + + ecs_strbuf_appendstr(&b, str); + char *str2 = ecs_strbuf_get(&b); + + test_str(str, str2); + + ecs_os_free(str); + ecs_os_free(str2); +} + +void Strbuf_append_1024_str(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 1024; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + + ecs_strbuf_appendstr(&b, str); + char *str2 = ecs_strbuf_get(&b); + + test_str(str, str2); + + ecs_os_free(str); + ecs_os_free(str2); +} + +void Strbuf_append_1025_str(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 1025; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + + ecs_strbuf_appendstr(&b, str); + char *str2 = ecs_strbuf_get(&b); + + test_str(str, str2); + + ecs_os_free(str); + ecs_os_free(str2); +} + +void Strbuf_append_2047_str(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 2047; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + + ecs_strbuf_appendstr(&b, str); + char *str2 = ecs_strbuf_get(&b); + + test_str(str, str2); + + ecs_os_free(str); + ecs_os_free(str2); +} + +void Strbuf_append_2048_str(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 2048; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + + ecs_strbuf_appendstr(&b, str); + char *str2 = ecs_strbuf_get(&b); + + test_str(str, str2); + + ecs_os_free(str); + ecs_os_free(str2); +} + +void Strbuf_append_2049_str(void) { + ecs_strbuf_t b = ECS_STRBUF_INIT; + + for (int i = 0; i < 2049; i ++) { + ecs_strbuf_appendch(&b, 'a' + (i % 26)); + } + + char *str = ecs_strbuf_get(&b); + + ecs_strbuf_appendstr(&b, str); + char *str2 = ecs_strbuf_get(&b); + + test_str(str, str2); + + ecs_os_free(str); + ecs_os_free(str2); +} + void Strbuf_append_flt(void) { ecs_strbuf_t b = ECS_STRBUF_INIT; ecs_strbuf_appendflt(&b, 10.5, 0); diff --git a/vendors/flecs/test/collections/src/main.c b/vendors/flecs/test/collections/src/main.c index 9b7e1c9fa..0358c13ef 100644 --- a/vendors/flecs/test/collections/src/main.c +++ b/vendors/flecs/test/collections/src/main.c @@ -63,6 +63,7 @@ void Sparse_count_of_null(void); void Sparse_try_low_after_ensure_high(void); void Sparse_is_alive_low_after_ensure_high(void); void Sparse_remove_low_after_ensure_high(void); +void Sparse_ensure_skip_generation(void); // Testsuite 'Strbuf' void Strbuf_setup(void); @@ -74,22 +75,38 @@ void Strbuf_append_list(void); void Strbuf_append_nested_list(void); void Strbuf_large_str(void); void Strbuf_empty_str(void); -void Strbuf_append_zerocopy(void); -void Strbuf_append_zerocopy_only(void); -void Strbuf_append_zerocopy_const(void); void Strbuf_reset(void); void Strbuf_merge(void); -void Strbuf_app_buffer(void); +void Strbuf_merge_empty(void); void Strbuf_append_char(void); void Strbuf_append_511_chars(void); void Strbuf_append_512_chars(void); void Strbuf_append_513_chars(void); +void Strbuf_append_1023_chars(void); +void Strbuf_append_1024_chars(void); +void Strbuf_append_1025_chars(void); +void Strbuf_append_2047_chars(void); +void Strbuf_append_2048_chars(void); +void Strbuf_append_2049_chars(void); +void Strbuf_append_511_str(void); +void Strbuf_append_512_str(void); +void Strbuf_append_513_str(void); +void Strbuf_append_1023_str(void); +void Strbuf_append_1024_str(void); +void Strbuf_append_1025_str(void); +void Strbuf_append_2047_str(void); +void Strbuf_append_2048_str(void); +void Strbuf_append_2049_str(void); void Strbuf_append_flt(void); void Strbuf_append_nan(void); void Strbuf_append_inf(void); void Strbuf_append_nan_delim(void); void Strbuf_append_inf_delim(void); +// Testsuite 'Allocator' +void Allocator_setup(void); +void Allocator_init_fini_empty(void); + bake_test_case Map_testcases[] = { { "count", @@ -293,6 +310,10 @@ bake_test_case Sparse_testcases[] = { { "remove_low_after_ensure_high", Sparse_remove_low_after_ensure_high + }, + { + "ensure_skip_generation", + Sparse_ensure_skip_generation } }; @@ -329,18 +350,6 @@ bake_test_case Strbuf_testcases[] = { "empty_str", Strbuf_empty_str }, - { - "append_zerocopy", - Strbuf_append_zerocopy - }, - { - "append_zerocopy_only", - Strbuf_append_zerocopy_only - }, - { - "append_zerocopy_const", - Strbuf_append_zerocopy_const - }, { "reset", Strbuf_reset @@ -350,8 +359,8 @@ bake_test_case Strbuf_testcases[] = { Strbuf_merge }, { - "app_buffer", - Strbuf_app_buffer + "merge_empty", + Strbuf_merge_empty }, { "append_char", @@ -369,6 +378,66 @@ bake_test_case Strbuf_testcases[] = { "append_513_chars", Strbuf_append_513_chars }, + { + "append_1023_chars", + Strbuf_append_1023_chars + }, + { + "append_1024_chars", + Strbuf_append_1024_chars + }, + { + "append_1025_chars", + Strbuf_append_1025_chars + }, + { + "append_2047_chars", + Strbuf_append_2047_chars + }, + { + "append_2048_chars", + Strbuf_append_2048_chars + }, + { + "append_2049_chars", + Strbuf_append_2049_chars + }, + { + "append_511_str", + Strbuf_append_511_str + }, + { + "append_512_str", + Strbuf_append_512_str + }, + { + "append_513_str", + Strbuf_append_513_str + }, + { + "append_1023_str", + Strbuf_append_1023_str + }, + { + "append_1024_str", + Strbuf_append_1024_str + }, + { + "append_1025_str", + Strbuf_append_1025_str + }, + { + "append_2047_str", + Strbuf_append_2047_str + }, + { + "append_2048_str", + Strbuf_append_2048_str + }, + { + "append_2049_str", + Strbuf_append_2049_str + }, { "append_flt", Strbuf_append_flt @@ -391,6 +460,14 @@ bake_test_case Strbuf_testcases[] = { } }; +bake_test_case Allocator_testcases[] = { + { + "init_fini_empty", + Allocator_init_fini_empty + } +}; + + static bake_test_suite suites[] = { { "Map", @@ -403,18 +480,25 @@ static bake_test_suite suites[] = { "Sparse", Sparse_setup, NULL, - 21, + 22, Sparse_testcases }, { "Strbuf", Strbuf_setup, NULL, - 23, + 35, Strbuf_testcases + }, + { + "Allocator", + Allocator_setup, + NULL, + 1, + Allocator_testcases } }; int main(int argc, char *argv[]) { - return bake_test_run("collections", argc, argv, suites, 3); + return bake_test_run("collections", argc, argv, suites, 4); } diff --git a/vendors/flecs/test/cpp_api/include/cpp_api/bake_config.h b/vendors/flecs/test/core/include/api/bake_config.h similarity index 92% rename from vendors/flecs/test/cpp_api/include/cpp_api/bake_config.h rename to vendors/flecs/test/core/include/api/bake_config.h index 85e23e0b3..71a0175fe 100644 --- a/vendors/flecs/test/cpp_api/include/cpp_api/bake_config.h +++ b/vendors/flecs/test/core/include/api/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef CPP_API_BAKE_CONFIG_H -#define CPP_API_BAKE_CONFIG_H +#ifndef CORE_BAKE_CONFIG_H +#define CORE_BAKE_CONFIG_H /* Headers of public dependencies */ #include diff --git a/vendors/flecs/test/api/include/api.h b/vendors/flecs/test/core/include/core.h similarity index 62% rename from vendors/flecs/test/api/include/api.h rename to vendors/flecs/test/core/include/core.h index 97d70f2cc..7efa0b9a7 100644 --- a/vendors/flecs/test/api/include/api.h +++ b/vendors/flecs/test/core/include/core.h @@ -1,8 +1,8 @@ -#ifndef API_H -#define API_H +#ifndef CORE_H +#define CORE_H /* This generated file contains includes for project dependencies */ -#include +#include #include #ifdef __cplusplus @@ -29,6 +29,9 @@ typedef struct Probe { ecs_entity_t e[MAX_ENTITIES]; ecs_entity_t c[MAX_INVOCATIONS][MAX_SYS_COLUMNS]; ecs_entity_t s[MAX_INVOCATIONS][MAX_SYS_COLUMNS]; + ecs_flags32_t ref_fields; + ecs_flags32_t up_fields; + ecs_flags32_t row_fields; void *param; } Probe; @@ -75,35 +78,6 @@ void probe_has_entity(Probe *probe, ecs_entity_t e); void install_test_abort(void); -#define ITER_MAX_ENTITIES (64) -#define ITER_MAX_TERMS (16) -#define ITER_MAX_VARIABLES (16) - -typedef struct test_iter_result_t { - ecs_entity_t entities[ITER_MAX_ENTITIES]; - ecs_id_t term_ids[ITER_MAX_ENTITIES][ITER_MAX_TERMS]; - void *term_columns[ITER_MAX_TERMS]; - - int32_t table_count_expect; - int32_t table_count_actual; - - char *entity_names[ITER_MAX_ENTITIES]; - char *term_ids_expr[ITER_MAX_ENTITIES][ITER_MAX_TERMS]; - int32_t matched[ITER_MAX_ENTITIES]; - - struct { - int32_t id; - ecs_entity_t entities[ITER_MAX_ENTITIES]; - char *entity_names[ITER_MAX_ENTITIES]; - } variables[ITER_MAX_VARIABLES]; -} test_iter_result_t; - -// Utility for doing order-independent validation of iterator output -bool test_iter( - ecs_iter_t *it, - ecs_iter_next_action_t next, - test_iter_result_t *expect); - const ecs_entity_t* bulk_new_w_type( ecs_world_t *world, ecs_entity_t type_ent, int32_t count); diff --git a/vendors/flecs/test/core/include/core/bake_config.h b/vendors/flecs/test/core/include/core/bake_config.h new file mode 100644 index 000000000..71a0175fe --- /dev/null +++ b/vendors/flecs/test/core/include/core/bake_config.h @@ -0,0 +1,25 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef CORE_BAKE_CONFIG_H +#define CORE_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include +#include + +#endif + diff --git a/vendors/flecs/test/api/project.json b/vendors/flecs/test/core/project.json similarity index 64% rename from vendors/flecs/test/api/project.json rename to vendors/flecs/test/core/project.json index 04bbdf45b..e0bc5dfde 100644 --- a/vendors/flecs/test/api/project.json +++ b/vendors/flecs/test/core/project.json @@ -1,9 +1,9 @@ { - "id": "api", + "id": "core", "type": "application", "value": { "author": "Sander Mertens", - "description": "Test project for flecs", + "description": "Flecs core tests", "public": false, "coverage": false, "use": [ @@ -36,13 +36,31 @@ "pair_id_w_obj_wildcard_is_tag", "pair_id_w_tag_property_w_obj_component_is_tag", "pair_id_w_tag_property_w_obj_wildcard_is_tag", + "pair_w_rel_wildcard_is_tag", + "pair_w_obj_wildcard_is_tag", + "pair_w_rel_tag_obj_wildcard_is_tag", + "pair_w_wildcard_wildcard_is_tag", + "pair_w_rel_any_is_tag", + "pair_w_obj_any_is_tag", + "pair_w_rel_tag_obj_any_is_tag", + "pair_w_any_any_is_tag", "id_w_override_is_tag", "id_w_toggle_is_tag", "pair_id_override_is_tag", "pair_id_toggle_is_tag", "make_pair", "make_pair_of_pair", - "make_pair_of_pair_tgt" + "make_pair_of_pair_tgt", + "0_entity", + "entity_from_str", + "unresolved_entity_from_str", + "scoped_entity_from_str", + "template_entity_from_str", + "pair_from_str", + "unresolved_pair_from_str", + "wildcard_pair_from_str", + "any_pair_from_str", + "invalid_pair" ] }, { "id": "Entity", @@ -100,12 +118,12 @@ "record_find_for_empty", "record_find", "record_find_from_stage", - "ensure_zero_gen", - "ensure_nonzero_gen", - "ensure_zero_gen_exists", - "ensure_nonzero_gen_exists", - "ensure_zero_gen_exists_alive", - "ensure_nonzero_gen_exists_alive", + "make_alive_zero_gen", + "make_alive_nonzero_gen", + "make_alive_zero_gen_exists", + "make_alive_nonzero_gen_exists", + "make_alive_zero_gen_exists_alive", + "make_alive_nonzero_gen_exists_alive", "set_scope_w_entity_init_from_stage", "entity_init_w_scope_twice", "entity_init_w_childof_twice", @@ -140,19 +158,122 @@ "set_name_w_same_ptr", "set_name_w_overlapping_ptr", "defer_set_name_w_overlapping_ptr", - "ensure_from_stage", - "ensure_after_deleted_1_entity", - "ensure_after_deleted_2_entities", + "make_alive_from_stage", + "make_alive_after_deleted_1_entity", + "make_alive_after_deleted_2_entities", "defer_entity_init_w_set_name_w_add_childof", "entity_w_digit_name", "entity_w_existing_digit_name", - "entity_w_conflicting_digit_name", - "set_generation_on_nonempty_entity", - "set_generation_while_deferred", + "entity_from_digit", + "entity_from_existing_digit", + "entity_from_digit_path", + "entity_from_existing_digit_path", + "entity_from_digit_0_path", + "entity_from_conflicting_digit", + "set_version_on_nonempty_entity", + "set_version_while_deferred", "commit_w_on_add", "commit_w_on_remove", "commit_w_cmd_in_observer", - "entity_init_existing_no_sep" + "entity_init_existing_no_sep", + "entity_init_w_set_1_comp", + "entity_init_w_set_2_comp", + "entity_init_w_set_1_comp_1_tag", + "entity_init_w_set_2_comp_2_tag", + "entity_init_w_set_1_comp_w_name", + "entity_init_w_set_1_comp_existing", + "entity_init_w_set_1_comp_existing_empty", + "entity_init_w_set_1_comp_1_tag_w_set", + "entity_init_w_set_w_hook", + "entity_init_w_set_w_observer", + "entity_init_w_set_1_comp_defer", + "entity_init_w_set_2_comp_defer", + "entity_init_w_set_1_comp_1_tag_defer", + "entity_init_w_set_2_comp_2_tag_defer", + "entity_init_w_set_1_comp_w_name_defer", + "entity_init_w_set_1_comp_existing_defer", + "entity_init_w_set_1_comp_existing_empty_defer", + "entity_init_w_set_1_comp_1_tag_w_set_defer", + "insert_1_comp", + "insert_2_comp", + "insert_1_comp_1_tag", + "entity_w_parent", + "entity_w_parent_w_name", + "entity_w_parent_w_add", + "entity_w_parent_w_add_w_parent", + "entity_w_parent_w_set", + "entity_w_parent_w_set_w_parent" + ] + }, { + "id": "Each", + "testcases": [ + "each_tag", + "each_component", + "each_pair", + "each_pair_rel_wildcard", + "each_pair_tgt_wildcard" + ] + }, { + "id": "Iter", + "testcases": [ + "page_iter_0_0", + "page_iter_1_0", + "page_iter_0_1", + "page_iter_n_0", + "page_iter_0_n", + "page_iter_m_n", + "page_iter_skip_1_table", + "page_iter_skip_2_tables", + "worker_iter_1", + "worker_iter_2", + "worker_iter_3", + "worker_iter_4", + "paged_iter_w_shared_comp", + "worker_iter_w_shared_comp", + "paged_iter_w_task_query", + "worker_iter_w_task_query", + "worker_iter_w_singleton", + "worker_iter_w_singleton_instanced", + "worker_iter_w_singleton_component_instanced", + "paged_iter_w_singleton", + "paged_iter_w_singleton_instanced", + "paged_iter_w_singleton_component_instanced", + "page_iter_w_offset_skip_1_archetype", + "page_iter_w_offset_skip_1_archetype_plus_one", + "page_iter_w_offset_skip_2_archetypes", + "page_iter_w_limit_skip_1_archetype", + "page_iter_w_limit_skip_1_archetype_minus_one", + "page_iter_w_limit_skip_2_archetypes", + "page_iter_w_offset_1_limit_max", + "page_iter_w_offset_1_limit_minus_1", + "page_iter_w_offset_2_type_limit_max", + "page_iter_w_offset_2_type_limit_minus_1", + "page_iter_w_limit_1_all_offsets", + "page_iter_w_offset_out_of_bounds", + "page_iter_w_limit_out_of_bounds", + "page_iter_comb_10_entities_1_type", + "page_iter_comb_10_entities_2_types", + "count", + "iter_restore_stack_iter", + "interleaved_iter", + "get_first", + "page_iter_w_only_tag", + "worker_iter_w_only_tag", + "page_iter_w_inout_none", + "worker_iter_w_inout_none", + "page_iter_w_ctx", + "page_iter_w_binding_ctx", + "worker_iter_w_ctx", + "worker_iter_w_binding_ctx", + "column_index_owned", + "column_index_shared", + "column_index_not", + "page_iter_w_fini", + "worker_iter_w_fini", + "rule_page_iter_w_fini", + "rule_worker_iter_w_fini", + "to_str_before_next", + "to_str" ] }, { "id": "Search", @@ -176,10 +297,7 @@ "search_relation_inherit_from_parent", "search_relation_dont_inherit", "search_relation_dont_inherit_from_parent", - "search_relation_exclusive", - "search_relation_union", - "search_relation_union_wildcard", - "search_relation_union_pair" + "search_relation_exclusive" ] }, { "id": "Event", @@ -199,7 +317,9 @@ "emit_custom_implicit_any", "emit_custom_empty_type", "emit_w_param", + "emit_w_param_multi_observer", "emit_w_const_param", + "emit_nested", "enqueue_event_1_id", "enqueue_event_2_ids", "enqueue_event_w_data", @@ -215,7 +335,8 @@ "enqueue_event_not_deferred", "enqueue_event_not_deferred_to_async", "enqueue_custom_implicit_any", - "enqueue_custom_after_large_cmd" + "enqueue_custom_after_large_cmd", + "enqueue_on_readonly_world" ] }, { "id": "New", @@ -238,14 +359,18 @@ "create_w_explicit_id_2_worlds", "new_w_id_0_w_with", "new_w_id_w_with", - "new_w_type_0_w_with", "new_w_type_w_with", "new_w_id_w_with_w_scope", "new_w_type_w_with_w_scope", "new_w_id_w_with_defer", "new_w_id_w_with_defer_w_scope", "new_w_type_w_with_defer", - "new_w_type_w_with_defer_w_scope" + "new_w_type_w_with_defer_w_scope", + "new_w_table", + "new_w_null_table", + "new_w_table_component", + "new_w_table_sparse_component", + "new_w_table_override" ] }, { "id": "New_w_Count", @@ -302,8 +427,144 @@ "add_random_id" ] }, { - "id": "Switch", + "id": "Remove", + "testcases": [ + "zero", + "1_of_1", + "1_of_2", + "2_of_2", + "2_of_3", + "1_of_1_again", + "2_again", + "2_overlap", + "1_from_empty", + "not_added" + ] + }, { + "id": "GlobalComponentIds", "testcases": [ + "declare", + "declare_w_entity", + "declare_2_world", + "declare_tag", + "declare_tag_w_entity", + "declare_entity", + "reuse_300_component_ids" + ] + }, { + "id": "Sparse", + "testcases": [ + "has", + "owns", + "get", + "get_mut", + "ensure", + "emplace", + "set", + "modified_no_on_set", + "insert_1", + "insert_2", + "get_ref", + "update_ref", + "get_recycled", + "get_mut_recycled", + "ensure_recycled", + "emplace_recycled", + "set_recycled", + "get_ref_recycled", + "test_stable_ptr", + "has_after_remove", + "has_after_clear", + "get_after_remove", + "get_mut_after_remove", + "sparse_w_hole", + "record_get", + "has_inherited", + "owns_inherited", + "get_inherited", + "get_mut_inherited", + "ensure_inherited", + "emplace_inherited", + "override_component", + "delete_w_override_component", + "delete_w_override_on_remove_isa", + "ctor_after_emplace", + "ctor_dtor_after_remove", + "ctor_dtor_after_clear", + "ctor_dtor_after_delete", + "ctor_dtor_after_fini", + "on_add_remove_after_remove", + "on_add_remove_after_clear", + "on_add_remove_after_delete", + "on_add_remove_after_fini", + "on_set_after_set", + "on_set_after_modified", + "on_set_at_offset", + "on_add_observer", + "on_set_observer_set", + "on_set_observer_modified", + "on_set_observer_insert", + "on_remove_observer_remove", + "on_remove_observer_clear", + "on_remove_observer_delete", + "on_remove_observer_fini", + "on_set_after_remove_override", + "on_add_observer_2_terms", + "on_set_observer_2_terms", + "on_remove_observer_2_terms", + "sparse_relationship", + "defer_ensure", + "defer_ensure_w_modified", + "defer_ensure_modified", + "defer_emplace", + "defer_emplace_w_modified", + "defer_set", + "defer_ensure_existing", + "defer_ensure_existing_twice", + "defer_ensure_w_modified_existing", + "defer_ensure_modified_existing", + "defer_emplace_existing", + "defer_emplace_w_modified_existing", + "defer_set_existing", + "defer_batched_ensure", + "defer_batched_ensure_w_modified", + "defer_batched_ensure_modified", + "defer_batched_emplace", + "defer_batched_emplace_w_modified", + "defer_batched_set", + "defer_batched_ensure_existing", + "defer_batched_ensure_existing_twice", + "defer_batched_ensure_w_modified_existing", + "defer_batched_ensure_modified_existing", + "defer_batched_emplace_existing", + "defer_batched_emplace_w_modified_existing", + "defer_batched_set_existing", + "defer_batched_set_remove", + "defer_batched_set_remove_existing" + ] + }, { + "id": "Union", + "testcases": [ + "add", + "add_twice", + "add_replace", + "add_remove", + "add_remove_recycled", + "add_remove_add", + "get_target_none", + "get_target", + "get_recycled_target", + "get_target_after_replace", + "get_target_after_remove", + "has_wildcard", + "has_any", + "add_remove_2_tgts", + "add_remove_2_tgts_join", + "add_remove_3_tgts", + "add_remove_3_tgts_join", + "remove_w_union_tgt", + "get_non_union_tgt_from_table_w_union", + "has_non_union_from_table_w_union", "get_case_no_switch", "get_case_set", "get_case_change", @@ -315,12 +576,6 @@ "3_entities_same_case", "2_entities_1_change_case", "3_entities_change_case", - "query_switch", - "query_1_case_1_type", - "query_1_case_2_types", - "query_2_cases_1_type", - "query_2_cases_2_types", - "query_after_remove", "add_case_in_stage", "change_case_in_stage", "change_one_case_in_stage", @@ -330,108 +585,18 @@ "zero_entity_has_case", "add_to_entity_w_switch", "add_pair_to_entity_w_switch", - "sort", "recycled_tags", - "query_recycled_tags", - "single_case", - "match_switch_on_base_instance", - "switch_w_bitset_query", - "switch_w_bitset_query_inv", - "switch_w_bitset_query_2_elems", - "switch_w_bitset_query_2_elems_skip", - "switch_w_bitset_query_elems_interleaved", - "switch_w_bitset_query_elems_interleaved_2_types", - "has_wildcard", - "remove_wildcard", "same_table_after_change", - "component_relation", - "delete_case_trigger_after_delete_switch", "add_2", "add_2_reverse", "add_switch_to_prefab_instance", "get_case_w_generation", - "get_case_w_generation_not_alive" - ] - }, { - "id": "EnabledComponents", - "testcases": [ - "is_component_enabled", - "is_empty_entity_disabled", - "is_0_entity_disabled", - "is_0_component_disabled", - "is_nonexist_component_disabled", - "is_enabled_component_enabled", - "is_disabled_component_enabled", - "has_enabled_component", - "is_enabled_after_add", - "is_enabled_after_remove", - "is_enabled_after_disable", - "is_disabled_after_enable", - "is_enabled_randomized", - "is_enabled_after_add_randomized", - "is_enabled_after_randomized_add_randomized", - "is_enabled_2", - "is_enabled_3", - "is_enabled_2_after_add", - "is_enabled_3_after_add", - "is_pair_enabled", - "is_enabled_pair_enabled", - "is_disabled_pair_enabled", - "has_enabled_pair", - "is_pair_enabled_after_add", - "is_pair_enabled_after_remove", - "is_pair_enabled_after_disable", - "is_pair_disabled_after_enable", - "is_pair_enabled_2", - "is_pair_enabled_3", - "is_pair_enabled_2_after_add", - "is_pair_enabled_3_after_add", - "query_disabled", - "query_disabled_skip_initial", - "query_disabled_pair", - "query_disabled_pair_skip_initial", - "query_mod_2", - "query_mod_8", - "query_mod_64", - "query_mod_256", - "query_mod_1024", - "query_enable_mod_10", - "query_mod_2_2_bitsets", - "query_mod_8_2_bitsets", - "query_mod_64_2_bitsets", - "query_mod_256_2_bitsets", - "query_mod_1024_2_bitsets", - "query_randomized_2_bitsets", - "query_randomized_3_bitsets", - "query_randomized_4_bitsets", - "defer_enable", - "sort", - "table_move_2_from_3" - ] - }, { - "id": "Remove", - "testcases": [ - "zero", - "1_of_1", - "1_of_2", - "2_of_2", - "2_of_3", - "1_of_1_again", - "2_again", - "2_overlap", - "1_from_empty", - "not_added" - ] - }, { - "id": "GlobalComponentIds", - "testcases": [ - "declare", - "declare_w_entity", - "declare_2_world", - "declare_tag", - "declare_tag_w_entity", - "declare_entity", - "reuse_300_component_ids" + "get_case_w_generation_not_alive", + "defer_add_union_relationship", + "defer_add_existing_union_relationship", + "defer_add_union_relationship_2_ops", + "defer_add_existing_union_relationship_2_ops", + "stress_test_1" ] }, { "id": "Hierarchies", @@ -463,8 +628,19 @@ "path_custom_prefix", "path_prefix_rel_match", "path_prefix_rel_no_match", + "path_escaped_sep", + "path_escaped_two_sep", + "path_escaped_two_consecutive_sep", + "path_escaped_sep_at_begin", + "path_escaped_sep_at_end", + "path_escaped_sep_w_parent", + "path_only_escaped_sep", + "path_only_escaped_sep_w_parent", + "path_only_escaped_two_sep", + "path_only_escaped_two_sep_w_parent", "fullpath_for_core", "path_w_number", + "path_w_entity_id", "lookup_depth_0", "lookup_depth_1", "lookup_depth_2", @@ -478,11 +654,10 @@ "lookup_in_parent_from_scope", "lookup_in_root_from_scope", "lookup_number", + "lookup_entity_id", "delete_children", "scope_set", "scope_set_again", - "scope_set_w_new", - "scope_set_w_new_staged", "scope_set_w_lookup", "scope_component", "scope_component_no_macro", @@ -494,7 +669,7 @@ "new_from_path_existing_depth_2", "add_path_depth_0", "add_path_depth_1", - "add_path_depth_2", + "add_path_depth_2", "add_path_existing_depth_0", "add_path_existing_depth_1", "add_path_existing_depth_2", @@ -502,14 +677,11 @@ "add_path_from_scope_new_entity", "add_root_path_to_child", "add_parent_path_from_root_to_child", - "new_w_child_in_root", "delete_child", "delete_2_children", "delete_2_children_different_type", "delete_tree_2_levels", "delete_tree_3_levels", - "delete_tree_count_tables", - "delete_tree_staged", "delete_tree_empty_table", "delete_tree_recreate", "delete_tree_w_onremove", @@ -521,8 +693,6 @@ "add_child_after_delete_tree", "add_child_to_recycled_parent", "get_type_after_recycled_parent_add", - "rematch_after_add_to_recycled_parent", - "cascade_after_recycled_parent_change", "long_name_depth_0", "long_name_depth_1", "long_name_depth_2", @@ -538,75 +708,6 @@ "defer_batch_remove_name_w_add_childof", "defer_batch_remove_childof_w_add_name" ] - }, { - "id": "FixedHierarchies", - "testcases": [ - "make_fixed_1_lvl", - "make_fixed_1_lvl_w_init", - "make_fixed_1_lvl_w_init_comp", - "make_fixed_1_lvl_w_init_comp_after_tree_fixed", - "make_fixed_1_lvl_2_entities", - "make_fixed_1_lvl_2_tables", - "make_fixed_2_lvl", - "make_fixed_2_lvl_2_tables", - "make_fixed_3_lvl", - "make_fixed_3_lvl_w_name", - "make_fixed_3_2_lvl_w_name", - "make_fixed_2_lvl_nested", - "make_fixed_3_lvl_nested", - "make_fixed_1_lvl_after_delete", - "get_target_1_lvl", - "get_target_2_lvl", - "get_depth_1_lvl", - "get_depth_2_lvl", - "get_depth_after_reparent_root", - "delete_fixed_1_lvl", - "delete_fixed_2_lvl", - "delete_with_fixed_1_lvl", - "delete_with_fixed_2_lvl", - "query_w_parent_field_1_lvl", - "query_w_parent_field_1_lvl_w_init", - "query_w_parent_field_1_lvl_w_init_comp_after_tree_fixed", - "query_w_parent_field_2_lvl", - "query_w_parent_field_1_fixed_1_regular", - "query_w_parent_field_only_fixed_1_lvls", - "query_w_parent_field_fixed_1_lvls_no_match", - "query_w_parent_field_fixed_1_lvls_2_no_match", - "query_w_parent_field_fixed_1_lvls_match_no_match", - "query_w_parent_field_fixed_1_lvls_no_match_match", - "query_w_parent_field_2_fixed_2_lvls", - "query_w_cascade_field_2_lvl", - "query_next_table", - "query_next_table_1_elem", - "query_next_table_1_elem_no_match", - "query_nested_make_fixed", - "query_nested_make_fixed_w_optional", - "query_nested_make_fixed_w_optional_match_children_only", - "query_nested_w_2_parents_make_fixed", - "query_table_w_3_parents", - "query_w_parent_change_detection_1st", - "query_w_parent_change_detection_2nd", - "query_w_parent_change_detection_iter_twice", - "query_w_parent_change_detection_iter_twice_each_parent", - "query_w_parent_change_detection_1st_populate_when_changed", - "query_w_parent_change_detection_2nd_populate_when_changed", - "query_w_parent_change_detection_iter_twice_populate_when_changed", - "query_w_parent_change_detection_iter_twice_each_parent_populate_when_changed", - "staged_query_w_parent_field_1_lvl", - "staged_query_w_parent_field_2_lvl", - "staged_query_w_parent_field_1_fixed_1_regular", - "staged_query_w_cascade_field_2_lvl", - "add_to_fixed", - "remove_from_fixed", - "delete_fixed", - "clear_fixed", - "make_fixed_1_lvl_w_name", - "make_fixed_2_lvl_w_name", - "make_fixed_1_lvl_w_name_keep_name", - "make_fixed_2_lvl_w_name_keep_name", - "make_fixed_2_lvl_lose_depth", - "make_fixed_3_lvl_lose_depth" - ] }, { "id": "Has", "testcases": [ @@ -652,7 +753,7 @@ "get_both_from_2_add_in_progress", "get_both_from_2_add_remove_in_progress", "get_childof_component", - "get_mut_equal_get", + "ensure_equal_get", "get_tag", "get_pair_tag", "get_wildcard" @@ -672,7 +773,8 @@ "get_ref_monitored", "get_ref_w_low_id_tag", "get_ref_w_low_id_tag_after_add", - "get_nonexisting" + "get_nonexisting", + "aba_table" ] }, { "id": "Delete", @@ -799,9 +901,7 @@ "delete_child_of_delete_with", "deep_clean_64", "deep_clean_256", - "id_w_switch", "id_w_disabled", - "id_to_no_switch", "id_to_no_disabled", "remove_on_delete_action", "delete_with_w_relation", @@ -828,7 +928,19 @@ "delete_w_low_rel_mixed_cleanup", "delete_w_low_rel_mixed_cleanup_interleaved_ids", "fini_query_w_singleton_in_scope_no_module", - "fini_query_w_singleton_in_module" + "fini_query_w_singleton_in_module", + "fini_observer_w_relationship_in_scope", + "add_on_delete_from_prefab", + "add_on_delete_from_disabled", + "delete_on_delete_from_prefab", + "delete_on_delete_from_disabled", + "delete_all_w_component_cycle", + "remove_all_1", + "remove_all_2", + "remove_all_3", + "delete_with_1", + "delete_with_2", + "delete_with_3" ] }, { "id": "Set", @@ -846,24 +958,30 @@ "set_remove_twice", "set_and_new", "set_null", - "get_mut_new", + "ensure_new", + "ensure_existing", + "ensure_tag_new", + "ensure_tag_existing", + "ensure_tag_new_w_comp", + "ensure_tag_existing_w_comp", + "ensure_tag_new_w_pair", + "ensure_tag_existing_w_pair", + "get_mut_not_existing", "get_mut_existing", - "get_mut_tag_new", - "get_mut_tag_existing", - "get_mut_tag_new_w_comp", - "get_mut_tag_existing_w_comp", - "get_mut_tag_new_w_pair", - "get_mut_tag_existing_w_pair", + "get_mut_tag", + "get_mut_pair", + "get_mut_pair_second", "modified_w_on_set", "modified_no_component", - "get_mut_w_add_in_on_add", - "get_mut_w_remove_in_on_add", - "get_mut_w_realloc_in_on_add", + "ensure_w_add_in_on_add", + "ensure_w_remove_in_on_add", + "ensure_w_realloc_in_on_add", "emplace", "emplace_2", "emplace_existing", "emplace_w_move", - "emplace_w_observer_w_add" + "emplace_w_observer_w_add", + "emplace_existing_w_check" ] }, { "id": "ReadWrite", @@ -891,11 +1009,19 @@ "get_name_no_name", "get_name_from_empty", "lookup_by_id", + "lookup_path_anonymous_parent", + "lookup_path_0_parent", + "lookup_path_0_parent_w_scope", "lookup_recycled_by_id", "lookup_symbol_by_id", "lookup_name_w_digit", "lookup_symbol_w_digit", "lookup_path_w_digit", + "lookup_name_w_spaces", + "lookup_path_w_spaces", + "lookup_number", + "lookup_number_path", + "lookup_number_0", "set_name_of_existing", "change_name_of_existing", "lookup_alias", @@ -926,7 +1052,16 @@ "lookup_child_invalid_digit", "lookup_digit_from_wrong_scope", "lookup_core_entity_from_wrong_scope", - "lookup_alias_w_number" + "lookup_alias_w_number", + "lookup_symbol_path", + "lookup_name_escaped_sep", + "lookup_path_escaped_sep", + "lookup_name_63_chars", + "lookup_name_64_chars", + "lookup_name_65_chars", + "lookup_path_63_chars", + "lookup_path_64_chars", + "lookup_path_65_chars" ] }, { "id": "Singleton", @@ -934,7 +1069,7 @@ "add_singleton", "remove_singleton", "set_get_singleton", - "get_mut_singleton", + "ensure_singleton", "singleton_system" ] }, { @@ -947,6 +1082,7 @@ "1_component", "2_component", "1_component_w_value", + "1_component_w_lifecycle", "2_component_w_value", "3_component", "3_component_w_value", @@ -968,9 +1104,6 @@ "copy_on_override", "copy_on_clone", "no_copy_on_move", - "ctor_copy_on_snapshot", - "copy_on_snapshot", - "dtor_on_restore", "ctor_on_tag", "dtor_on_tag", "copy_on_tag", @@ -996,6 +1129,8 @@ "ctor_w_emplace", "ctor_w_emplace_defer", "ctor_w_emplace_defer_use_move_ctor", + "ctor_w_emplace_defer_twice", + "ctor_w_emplace_defer_existing", "on_add_w_emplace", "on_add_w_emplace_existing", "on_add_w_emplace_defer", @@ -1025,14 +1160,20 @@ "ctor_move_dtor_after_resize", "ctx_free", "binding_ctx_free", + "lifecycle_ctx_free", "ctx_free_after_delete_component", "binding_ctx_free_after_delete_component", + "lifecycle_ctx_free_after_delete_component", "on_add_ctx", "on_remove_ctx", "on_set_ctx", "on_add_w_existing_component", "on_remove_w_existing_component", "component_init_set_hooks", + "component_init_name_from_type_info", + "component_init_scoped_name_from_type_info", + "component_init_w_recycled_id", + "component_init_w_recycled_non_component_id", "on_add_after_ctor_w_add", "on_add_after_ctor_w_add_to", "with_before_hooks", @@ -1046,712 +1187,13 @@ "on_set_hook_on_override", "on_set_hook_on_auto_override", "batched_set_new_component_w_lifecycle", - "batched_get_mut_new_component_w_lifecycle" - ] - }, { - "id": "Sorting", - "testcases": [ - "sort_by_component", - "sort_by_component_2_tables", - "sort_by_component_3_tables", - "sort_by_entity", - "sort_after_add", - "sort_after_remove", - "sort_after_delete", - "sort_after_set", - "sort_after_system", - "sort_after_query", - "sort_by_component_same_value_1", - "sort_by_component_same_value_2", - "sort_by_component_move_pivot", - "sort_1000_entities", - "sort_1000_entities_w_duplicates", - "sort_1000_entities_again", - "sort_1000_entities_2_types", - "sort_1500_entities_3_types", - "sort_2000_entities_4_types", - "sort_2_entities_2_types", - "sort_3_entities_3_types", - "sort_3_entities_3_types_2", - "sort_4_entities_4_types", - "sort_1000_entities_2_types_again", - "sort_1000_entities_add_type_after_sort", - "sort_shared_component", - "sort_shared_component_childof", - "sort_w_tags_only", - "sort_childof_marked", - "sort_isa_marked", - "sort_relation_marked", - "dont_resort_after_set_unsorted_component", - "dont_resort_after_set_unsorted_component_w_tag", - "dont_resort_after_set_unsorted_component_w_tag_w_out_term", - "sort_component_not_queried_for", - "sort_by_wildcard" - ] - }, { - "id": "SortingEntireTable", - "testcases": [ - "sort_by_component", - "sort_by_component_2_tables", - "sort_by_component_3_tables", - "sort_by_entity", - "sort_after_add", - "sort_after_remove", - "sort_after_delete", - "sort_after_set", - "sort_after_system", - "sort_after_query", - "sort_by_component_same_value_1", - "sort_by_component_same_value_2", - "sort_by_component_move_pivot", - "sort_1000_entities", - "sort_1000_entities_w_duplicates", - "sort_1000_entities_again", - "sort_1000_entities_2_types", - "sort_1500_entities_3_types", - "sort_2000_entities_4_types", - "sort_2_entities_2_types", - "sort_3_entities_3_types", - "sort_3_entities_3_types_2", - "sort_4_entities_4_types", - "sort_1000_entities_2_types_again", - "sort_1000_entities_add_type_after_sort", - "sort_shared_component", - "sort_w_tags_only", - "sort_childof_marked", - "sort_isa_marked", - "sort_relation_marked", - "dont_resort_after_set_unsorted_component", - "dont_resort_after_set_unsorted_component_w_tag", - "dont_resort_after_set_unsorted_component_w_tag_w_out_term" - ] - }, { - "id": "Filter", - "testcases": [ - "filter_1_term", - "filter_1_term_component", - "filter_2_terms", - "filter_3_terms", - "filter_3_terms_w_or", - "filter_4_terms_w_or_at_1", - "filter_1_term_wildcard", - "filter_1_term_any", - "filter_1_term_same_subj_obj", - "filter_1_term_acyclic_same_subj_obj", - "filter_1_term_acyclic_reflexive_same_subj_obj", - "filter_1_term_same_subj_obj_var", - "filter_1_term_acyclic_same_subj_obj_var", - "filter_1_term_acyclic_reflexive_same_subj_obj_var", - "filter_1_term_non_acyclic_superset", - "filter_1_term_dont_inherit_default_set", - "filter_1_term_dont_inherit_pair_default_set", - "filter_1_term_cascade_implicit_isa", - "filter_1_term_cascade_isa", - "filter_1_term_cascade_childof", - "filter_1_term_cascade_down", - "filter_1_term_optional_only", - "filter_1_term_transitive_pair", - "filter_1_term_transitive_pair_explicit_self_tgt", - "filter_1_variable_as_pred_only", - "filter_1_variable_as_pred_w_subj", - "filter_1_variable_as_pred_w_pair", - "filter_1_variable_as_subj", - "filter_1_variable_as_obj", - "filter_2_terms_or_w_dontinherit", - "filter_2_terms_or_w_both_dontinherit", - "filter_w_pair_id", - "filter_w_pred_obj", - "filter_w_pair_id_and_subj", - "filter_1_w_pred_name", - "filter_1_w_final_pred_name", - "filter_1_w_subj_name", - "filter_1_w_obj_name", - "filter_w_this_implicit_variable", - "filter_w_this_explicit_entity", - "filter_w_first_this_implicit_variable", - "filter_w_first_this_explicit_entity", - "filter_w_second_this_implicit_variable", - "filter_w_second_this_explicit_entity", - "filter_w_this_variable_name", - "filter_w_src_var", - "filter_w_first_var", - "filter_w_second_var", - "filter_w_0_source", - "filter_w_0_target", - "filter_2_terms_w_or", - "filter_2_terms_w_or_mixed_src_flags", - "filter_2_terms_w_or_mixed_src_id", - "filter_2_terms_w_or_mixed_src_name", - "filter_2_terms_w_or_same_src_w_id_and_name", - "filter_move", - "filter_copy", - "filter_w_resources_copy", - "filter_w_and_flag", - "filter_w_or_flag", - "filter_w_not_flag", - "filter_filter", - "filter_double_init", - "filter_double_init_w_expr", - "filter_double_init_w_expr_optional", - "filter_w_tag_term_is_no_data", - "filter_w_inout_none_term_is_no_data", - "filter_w_tag_and_inout_none_term_is_no_data", - "filter_w_not_term_is_no_data", - "filter_w_no_transitive_pair", - "filter_w_transitive_pair_any_src", - "filter_w_transitive_pair", - "filter_w_transitive_tag_no_pair", - "filter_w_transitive_tag_self_tgt", - "filter_w_transitive_tag_any_tgt", - "filter_w_pair_same_vars", - "filter_w_pair_not_same_vars", - "filter_w_pair_no_vars_not_same_vars", - "filter_w_pair_wildcard_not_same_vars", - "filter_w_pair_any_not_same_vars", - "filter_w_no_pair_not_same_vars", - "filter_not_childof_any", - "filter_w_inherited_id", - "filter_w_inherited_pair", - "filter_w_non_inherited_id", - "filter_w_non_inherited_pair", - "filter_w_first_rel", - "filter_w_first_rel_self", - "filter_w_first_rel_down", - "filter_w_first_rel_self_down", - "filter_w_first_rel_reflexive", - "filter_w_first_rel_reflexive_self", - "filter_w_first_rel_reflexive_down", - "filter_w_first_rel_reflexive_self_down", - "filter_w_first_rel_non_traversable", - "filter_w_first_wildcard_inout_none", - "filter_w_first_var_inout_none", - "filter_w_pair_wildcard_inout_none", - "filter_w_pair_var_inout_none", - "filter_w_unresolved_by_name", - "filter_w_unresolved_by_name_eq", - "filter_childof_this", - "filter_childof_this_entity", - "filter_childof_this_by_id", - "term_w_id", - "term_w_pair_id", - "term_w_pred_obj", - "term_w_pair_finalize_twice", - "term_w_role", - "term_w_pred_role", - "term_w_self", - "term_w_superset", - "term_w_subset", - "term_w_self_superset", - "term_w_superset_custom_relation", - "term_w_self_superset_custom_relation", - "term_iter_component", - "term_iter_w_pred", - "term_iter_tag", - "term_iter_pair", - "term_iter_pair_w_rel_wildcard", - "term_iter_pair_w_obj_wildcard", - "term_iter_pair_w_rel_wildcard_n_matches", - "term_iter_pair_w_rel_wildcard_n_matches_w_data", - "term_iter_pair_w_obj_wildcard_n_matches", - "term_iter_pair_w_obj_wildcard_n_matches_w_data", - "term_iter_w_superset", - "term_iter_w_superset_base_w_2_components", - "term_iter_w_superset_childof", - "term_iter_w_superset_self", - "term_iter_w_superset_self_childof", - "term_iter_w_superset_tag", - "term_iter_w_superset_pair", - "term_iter_w_superset_pair_obj_wildcard", - "term_iter_in_stage", - "term_iter_w_readonly_term", - "term_iter_type_set", - "term_iter_any_match_wildcard", - "term_iter_any_match_tag_and_wildcard", - "term_iter_any_obj", - "children_iter", - "filter_iter_1_tag", - "filter_iter_2_tags", - "filter_iter_2_tags_1_not", - "filter_iter_3_tags_2_or", - "filter_iter_only_optional", - "filter_iter_only_2_or", - "filter_iter_only_3_or", - "filter_iter_2_or", - "filter_iter_3_or", - "filter_iter_2_or_other_type", - "filter_iter_2_or_same_type", - "filter_iter_1_component", - "filter_iter_2_components", - "filter_iter_pair_id", - "filter_iter_2_pair_ids", - "filter_iter_childof_pair_0_parent", - "filter_iter_pair_pred_obj", - "filter_iter_pair_2_pred_obj", - "filter_iter_null", - "filter_iter_1_not_tag", - "filter_iter_2_tags_1_optional", - "filter_iter_2_components_1_optional", - "filter_iter_in_stage", - "filter_iter_10_tags", - "filter_iter_20_tags", - "filter_iter_10_components", - "filter_iter_20_components", - "filter_iter_superset", - "filter_iter_superset_childof", - "filter_iter_type_set", - "filter_iter_w_readonly_term", - "filter_iter_w_from_nothing_term", - "filter_iter_pair_w_rel_wildcard_n_matches", - "filter_iter_pair_w_obj_wildcard_n_matches", - "filter_iter_pair_w_2_wildcards_1_match", - "filter_iter_pair_w_2_wildcards_2x1_matches", - "filter_iter_pair_w_2_wildcards_2x2_matches", - "filter_iter_pair_w_3_wildcards_2x2x2_matches", - "filter_iter_pair_w_wildcard_and_nothing", - "filter_iter_any", - "filter_iter_any_match_wildcard", - "filter_iter_any_match_tag_and_wildcard", - "filter_iter_wildcard_in_2nd_term", - "filter_iter_wildcard_in_2nd_term_self", - "filter_iter_2nd_term_self_create_id_after_filter", - "filter_iter_any_obj", - "filter_iter_not_any", - "filter_iter_not_any_obj", - "filter_iter_cascade_isa", - "filter_iter_cascade_childof", - "filter_iter_superset_2_rel_instances", - "filter_iter_superset_2_rel_instances_match_2nd", - "filter_iter_superset_2_levels", - "filter_iter_superset_only_w_owned", - "filter_iter_superset_after_add", - "filter_iter_superset_after_remove", - "filter_iter_superset_after_clear", - "filter_iter_superset_after_delete", - "filter_iter_2_terms_superset_2_rel_instances", - "filter_iter_2_terms_superset_2_rel_instances_match_2nd", - "filter_iter_superset_parent_w_isa", - "filter_iter_superset_isa_after_remove_parent", - "filter_iter_superset_isa_create_table_after_iter", - "filter_iter_superset_2_relations", - "filter_iter_superset_2_relations_instanced", - "filter_iter_superset_2_relations_w_component", - "filter_iter_superset_2_relations_instanced_w_component", - "filter_iter_not_up_disabled", - "filter_iter_pair_wildcard_component", - "filter_w_10_terms", - "filter_w_10_terms_move", - "filter_w_10_terms_copy", - "match_disabled", - "match_prefab", - "chain_term_iter", - "chain_filter_iter", - "chain_query_iter", - "chain_rule_iter", - "chain_iter_2_levels", - "filter_from_expr_2_terms_err", - "chain_term_iter_w_term_iter", - "chain_filter_iter_w_term_iter", - "chain_w_term_iter_component", - "chain_iter_w_or", - "filter_w_recycled_first", - "filter_w_recycled_second", - "filter_w_recycled_first_and_id", - "filter_w_recycled_second_and_id", - "filter_w_recycled_first_by_name_and_id", - "filter_w_recycled_second_by_name_and_id", - "filter_w_recycled_first_by_expr", - "filter_w_recycled_second_by_expr", - "filter_w_recycled_first_only_by_expr", - "term_iter_w_filter_term", - "filter_iter_w_filter_term", - "filter_iter_w_2_terms_1_filter", - "filter_iter_w_3_terms_2_filter", - "filter_iter_2_terms_filter_all", - "filter_iter_2_terms_filter_all_w_out", - "filter_iter_switch_term_filter", - "filter_iter_2_terms_switch_term_filter", - "filter_iter_switch_superset", - "filter_instanced_w_singleton", - "filter_instanced_w_base", - "filter_no_instancing_w_singleton", - "filter_no_instancing_w_base", - "filter_no_this_tag", - "filter_no_this_component", - "filter_no_this_tag_2_ents", - "filter_no_this_component_2_ents", - "filter_no_this_tag_2_ents_1_not", - "filter_no_this_component_2_ents_1_not", - "filter_no_this_component_1_not", - "filter_iter_entities_optional_flag", - "filter_iter_frame_offset", - "filter_1_term_no_alloc", - "filter_cache_size_terms_no_alloc", - "filter_lt_cache_size_terms_no_alloc", - "move_self", - "match_empty_tables", - "match_empty_tables_w_no_empty_tables", - "match_switch_w_switch", - "match_switch_w_case", - "match_switch_w_case_2_terms", - "match_case_no_case", - "and_term", - "or_term", - "iter_while_creating_components", - "iter_w_this_var_as_entity", - "iter_w_this_var_as_table", - "iter_w_this_var_as_table_range", - "filter_wo_this_var", - "set_this_to_table_1_term", - "set_this_to_table_2_terms", - "set_this_to_table_1_wildcard", - "set_this_to_table_no_match_no_data", - "set_this_to_table_no_match", - "set_this_to_table_2_terms_no_match", - "set_this_to_empty_table", - "set_this_to_empty_table_w_component", - "set_this_to_implicit_isa_superset_match", - "set_this_to_self_isa_superset_match", - "set_this_to_isa_superset_match", - "set_this_to_childof_superset_match", - "set_this_to_superset_w_self_filter_no_match", - "set_this_to_isa_cascade", - "set_this_to_childof_cascade", - "set_this_w_wildcard_2_matches", - "set_this_to_entity_superset_self_has_component", - "set_this_to_1_entity_in_table", - "oneof", - "oneof_expr", - "oneof_w_mismatching_obj", - "oneof_w_mismatching_obj_expr", - "oneof_wildcard", - "oneof_any", - "flag_match_only_this", - "flag_match_only_this_w_ref", - "filter_w_alloc", - "filter_w_short_notation" - ] - }, { - "id": "FilterStr", - "testcases": [ - "one_term", - "one_term_w_inout", - "two_terms", - "two_terms_w_inout", - "three_terms_w_or", - "three_terms_w_or_inout", - "four_terms_three_w_or_inout", - "one_term_w_pair", - "one_term_w_pair_entity_src", - "one_term_w_self", - "one_term_w_up", - "one_term_w_0", - "one_term_w_singleton", - "one_term_w_final_pair", - "one_term_w_final_dont_inherit_pair", - "one_term_w_src_var", - "one_term_w_first_var", - "one_term_w_second_var", - "one_term_w_first_var_entity_src", - "one_term_w_pair_w_0_entity", - "not_term", - "wildcard_term", - "scopes" - ] - }, { - "id": "Query", - "testcases": [ - "simple_query_existing_table", - "simple_query_2_existing_tables", - "simple_query_new_table", - "simple_query_2_new_tables", - "simple_query_existing_and_new_table", - "wildcard_query_existing_table", - "wildcard_query_new_table", - "wildcard_query_existing_table_2_results_p_table", - "wildcard_query_new_table_2_results_p_table", - "wildcard_query_2nd_term", - "wildcard_query_2nd_term_self", - "simple_query_existing_empty_table", - "simple_query_existing_empty_type", - "simple_query_new_empty_table", - "component_query_existing_table", - "component_query_new_table", - "component_query_existing_empty_table", - "2_component_query_existing_empty_table", - "2_component_query_existing_empty_type", - "only_optional", - "only_optional_new_empty_table", - "only_optional_new_empty_non_empty_table", - "only_optional_new_unset_tables", - "singleton_w_optional_new_empty_table", - "singleton_w_optional_new_empty_non_empty_table", - "singleton_w_optional_new_unset_tables", - "query_only_from_entity", - "query_only_from_entity_no_match", - "query_only_from_entity_no_match_iter_alloc", - "query_only_from_singleton", - "query_only_from_entity_match_after", - "query_only_from_singleton_match_after", - "query_only_from_singleton_component_match_after", - "query_only_from_nothing", - "query_only_from_entity_optional", - "query_only_from_entity_no_match_optional", - "query_only_from_entity_or", - "query_only_from_entity_no_match_or", - "query_only_from_entity_or_change", - "query_from_entity_or_change", - "query_from_entity_w_superset", - "query_w_singleton_tag_non_instanced", - "query_w_singleton_tag_instanced", - "query_w_singleton_component_non_instanced", - "query_w_singleton_component_instanced", - "query_w_from_entity", - "query_w_from_singleton", - "query_w_from_entity_match_after", - "query_w_from_singleton_match_after", - "query_w_from_nothing", - "query_w_existing_switch_and_case", - "query_w_new_switch_and_case", - "query_for_case_existing", - "query_for_case_new", - "query_for_switch_filter_term", - "query_switch_from_nothing", - "query_case_from_nothing", - "query_case_inherited", - "query_case_w_generation", - "query_case_w_not_alive", - "query_disabled_from_nothing", - "query_only_2_or", - "query_only_3_or", - "query_2_or", - "query_3_or", - "query_and_type", - "query_or_type", - "query_and_type_match_after", - "query_or_type_match_after", - "query_changed_after_new", - "query_changed_after_delete", - "query_changed_after_add", - "query_changed_after_remove", - "query_changed_after_set", - "query_change_after_modified", - "query_change_after_out_system", - "query_change_after_in_system", - "query_change_after_modified_out_term", - "query_change_check_iter", - "query_change_check_iter_after_skip_read", - "query_change_check_iter_after_skip_write", - "query_change_parent_term", - "query_change_prefab_term", - "query_change_parent_term_w_tag", - "query_change_prefab_term_w_tag", - "query_change_skip_non_instanced", - "query_changed_w_or", - "query_changed_or", - "query_changed_w_singleton", - "query_changed_w_only_singleton", - "query_changed_w_only_singleton_after_set", - "query_changed_w_only_singleton_after_out_term", - "query_changed_w_only_singleton_after_singleton_out_term", - "query_changed_w_only_parent", - "query_changed_w_only_parent_after_set", - "query_changed_w_only_parent_after_out_term", - "query_changed_w_only_parent_after_parent_out_term", - "query_changed_tag", - "query_changed_no_source", - "query_changed_no_source_component", - "query_changed_w_not_out", - "subquery_match_existing", - "subquery_match_new", - "subquery_inactive", - "subquery_unmatch", - "subquery_rematch", - "subquery_rematch_w_parent_optional", - "subquery_rematch_w_sub_optional", - "query_single_pairs", - "query_single_instanceof", - "query_single_childof", - "query_optional_owned", - "query_optional_shared", - "query_optional_shared_nested", - "query_optional_any", - "query_rematch_optional_after_add", - "get_owned_tag", - "get_shared_tag", - "explicit_delete", - "get_column_size", - "orphaned_query", - "nested_orphaned_query", - "invalid_access_orphaned_query", - "stresstest_query_free", - "only_from_entity", - "only_not_from_entity", - "only_from_singleton", - "only_not_from_singleton", - "get_filter", - "group_by", - "group_by_w_ctx", - "group_by_w_sort_reverse_group_creation", - "group_by_iter_one", - "group_by_iter_one_all_groups", - "group_by_iter_one_empty", - "group_by_iter_one_empty_query", - "group_by_iter_one_empty_table", - "group_by_w_deleted_group_id", - "group_by_callbacks", - "group_by_default_action", - "group_table_count", - "iter_valid", - "query_optional_tag", - "query_optional_shared_tag", - "query_iter_10_tags", - "query_iter_20_tags", - "query_iter_10_components", - "query_iter_20_components", - "iter_type_set", - "filter_term", - "2_terms_1_filter", - "3_terms_2_filter", - "no_instancing_w_singleton", - "no_instancing_w_shared", - "query_iter_frame_offset", - "add_singleton_after_query", - "query_w_component_from_parent_from_non_this", - "create_query_while_pending", - "empty_query", - "parent_cascade", - "existing_custom_rel_cascade", - "new_custom_rel_cascade", - "cascade_w_2_depths", - "cascade_w_3_depths", - "cascade_w_2_depths_desc", - "cascade_w_3_depths_desc", - "not_pair_relation_wildcard", - "not_pair_object_wildcard", - "two_pair_wildcards_one_not", - "two_pair_wildcards_one_not_any", - "implicit_existing_isa_superset", - "implicit_new_isa_superset", - "isa_superset", - "isa_superset_2_lvls", - "isa_superset_3_lvls", - "isa_superset_2_lvls_owned", - "isa_superset_3_lvls_owned", - "isa_superset_owned_empty_table_after_match", - "isa_self_superset", - "childof_superset", - "superset_2_targets", - "superset_2_relations", - "superset_2_relations_instanced", - "superset_2_relations_w_component", - "superset_2_relations_instanced_w_component", - "parent", - "existing_isa_cascade", - "new_isa_cascade", - "childof_cascade", - "isa_rematch", - "childof_rematch", - "isa_unmatch", - "childof_unmatch", - "isa_rematch_2_lvls", - "childof_rematch_2_lvls", - "cascade_rematch_2_lvls", - "cascade_rematch_2_lvls_2_relations", - "cascade_topological", - "cascade_desc_rematch_2_lvls", - "cascade_desc_rematch_2_lvls_2_relations", - "cascade_desc_topological", - "childof_rematch_from_isa", - "rematch_optional_ref", - "rematch_optional_ref_w_2_refs", - "rematch_optional_ref_tag_w_ref_component", - "match_query_expr_from_scope", - "query_long_or_w_ref", - "0_query", - "query_w_pair_id_and_subj", - "table_count", - "empty_table_count", - "entity_count", - "rematch_after_delete_inherited_tag", - "rematch_after_delete_rel_of_inherited_pair", - "rematch_after_delete_obj_of_inherited_pair", - "rematch_empty_table_w_superset", - "query_w_short_notation", - "query_w_invalid_filter_flag", - "query_next_table", - "query_next_table_w_changed", - "query_next_table_w_populate", - "query_next_table_w_skip", - "query_next_table_w_populate_first_changed", - "query_next_table_w_populate_last_changed", - "query_next_table_w_populate_skip_first", - "query_next_table_w_populate_skip_last", - "create_query_existing_query_entity", - "query_for_recycled_pair", - "query_w_singleton_w_rule_iter", - "query_w_singleton_nested_iter", - "query_w_singleton_interleaved_iter", - "recycled_component_id", - "set_get_context", - "set_get_binding_context", - "set_get_context_w_free", - "set_get_binding_context_w_free", - "set_this", - "set_this_no_match", - "set_this_is_true", - "set_this_w_wildcard", - "singleton_w_inout_none", - "singleton_w_inout_none_or", - "component_w_inout_none_or" - ] - }, { - "id": "Iter", - "testcases": [ - "page_iter_0_0", - "page_iter_1_0", - "page_iter_0_1", - "page_iter_n_0", - "page_iter_0_n", - "page_iter_m_n", - "page_iter_skip_1_table", - "page_iter_skip_2_tables", - "worker_iter_1", - "worker_iter_2", - "worker_iter_3", - "worker_iter_4", - "paged_iter_w_shared_comp", - "worker_iter_w_shared_comp", - "paged_iter_w_task_query", - "worker_iter_w_task_query", - "worker_iter_w_singleton", - "worker_iter_w_singleton_component", - "worker_iter_w_singleton_instanced", - "worker_iter_w_singleton_component_instanced", - "paged_iter_w_singleton", - "paged_iter_w_singleton_component", - "paged_iter_w_singleton_instanced", - "paged_iter_w_singleton_component_instanced", - "count", - "iter_restore_stack_iter", - "interleaved_iter", - "get_first", - "page_iter_w_only_tag", - "worker_iter_w_only_tag", - "page_iter_w_inout_none", - "worker_iter_w_inout_none", - "page_iter_w_ctx", - "page_iter_w_binding_ctx", - "worker_iter_w_ctx", - "worker_iter_w_binding_ctx", - "column_index_owned", - "column_index_shared", - "column_index_not", - "page_iter_w_fini", - "worker_iter_w_fini", - "rule_page_iter_w_fini", - "rule_worker_iter_w_fini", - "to_str_before_next", - "to_str" + "batched_ensure_new_component_w_lifecycle", + "on_nested_prefab_copy_test_invokes_copy_count", + "no_move_no_move_ctor_with_move_dtor_with_ctor_move_dtor", + "new_w_table_ctor", + "new_w_table_on_add_hook", + "count_in_on_add", + "count_in_on_remove" ] }, { "id": "Pairs", @@ -1850,6 +1292,7 @@ "delete_entity_w_symmetric_relation", "add_symmetric_exclusive_relation", "add_symmetric_recycled_relation", + "symmetric_w_childof", "with", "2_with", "nested_with", @@ -1872,7 +1315,14 @@ "oneof_other_constraint_violated", "oneof_other_rel_parent_constraint_violated", "set_w_recycled_rel", - "set_w_recycled_tgt" + "set_w_recycled_tgt", + "force_relationship_on_component", + "force_relationship_on_target", + "force_relationship_on_target_trait", + "force_relationship_on_relationship", + "force_target_on_component", + "force_target_on_relationship", + "force_target_on_target" ] }, { "id": "Trigger", @@ -1884,6 +1334,7 @@ "on_add_tag", "on_add_component", "on_add_wildcard", + "on_add_wildcard_after_table", "on_add_pair", "on_add_pair_obj_wildcard", "on_add_pair_pred_wildcard", @@ -1958,7 +1409,6 @@ "on_add_base_superset_trigger_2_lvls", "on_add_base_2_entities", "on_add_base_2_entities_filter", - "on_set_base_w_value_2_entities", "on_set_base_w_value_2_entities_instanced", "on_add_base_w_override", "on_set_base_w_override", @@ -1987,29 +1437,57 @@ "on_set_self_auto_override", "on_set_self_superset_auto_override", "on_set_superset_auto_override", + "on_set_self_on_instantiate_override", + "on_set_self_up_on_instantiate_override", + "on_set_up_on_instantiate_override", "not_only", "not_only_w_base", "not_only_w_base_no_match", "on_set_superset_after_filter_observer", "on_set_superset_after_filter_observer_w_on_add", "on_set_superset_after_filter_observer_w_on_add_isa_after_set", - "on_set_superset_after_filter_observer_w_on_add_2", - "propagate_w_union_pair" + "on_set_superset_after_filter_observer_w_on_add_2" ] }, { "id": "Observer", "testcases": [ + "on_add_before_edge", + "on_add_after_edge", + "on_add_wildcard_before_edge", + "on_add_wildcard_after_edge", + "on_add_R_wildcard_before_edge", + "on_add_R_wildcard_after_edge", + "on_add_wildcard_T_before_edge", + "on_add_wildcard_T_after_edge", + "on_add_wildcard_wildcard_before_edge", + "on_add_wildcard_wildcard_after_edge", + "on_add_any_before_edge", + "on_add_any_after_edge", + "on_remove_before_edge", + "on_remove_after_edge", + "on_remove_wildcard_before_edge", + "on_remove_wildcard_after_edge", + "on_remove_R_wildcard_before_edge", + "on_remove_R_wildcard_after_edge", + "on_remove_wildcard_T_before_edge", + "on_remove_wildcard_T_after_edge", + "on_remove_wildcard_wildcard_before_edge", + "on_remove_wildcard_wildcard_after_edge", + "on_remove_any_before_edge", + "on_remove_any_after_edge", "2_terms_w_on_add", "2_terms_w_on_remove", "2_terms_w_on_set_value", "2_terms_w_on_remove_value", + "2_terms_w_on_set_value_self_up", + "2_terms_w_on_remove_value_self_up", "2_terms_w_on_add_2nd", "2_terms_w_on_remove_2nd", "2_pair_terms_w_on_add", "2_pair_terms_w_on_remove", "2_wildcard_pair_terms_w_on_add", "2_wildcard_pair_terms_w_on_add_2_matching", - "2_wildcard_pair_terms_w_on_add_3_matching", + "2_wildcard_pair_terms_w_on_add_3_matching", "2_wildcard_pair_terms_w_on_remove", "on_set_n_entities", "on_set_n_entities_2_comp", @@ -2050,6 +1528,10 @@ "3_terms_2_filter", "and_from", "or_from", + "and_from_empty", + "or_from_empty", + "and_from_empty_w_tag", + "or_from_empty_w_tag", "notify_propagated_twice", "on_add_yield_existing", "on_add_yield_existing_2_tables", @@ -2057,6 +1539,20 @@ "on_add_yield_existing_wildcard", "on_add_yield_existing_wildcard_multi", "on_add_yield_existing_wildcard_multi_w_wildcard_pivot", + "on_remove_yield_existing", + "on_remove_yield_existing_2_tables", + "on_remove_yield_existing_2_terms", + "on_remove_yield_existing_wildcard", + "on_remove_yield_existing_wildcard_multi", + "on_remove_yield_existing_wildcard_multi_w_wildcard_pivot", + "on_add_remove_yield_existing", + "on_add_remove_yield_existing_flags", + "on_add_remove_no_on_add_yield_existing", + "on_add_remove_no_on_remove_yield_existing", + "yield_existing_flags_w_multi_observer", + "yield_on_create_without_on_add", + "yield_on_delete_without_on_remove", + "yield_existing_w_not_first_term", "observer_superset_wildcard", "observer_superset_wildcard_add_isa", "observer_superset_wildcard_add_isa_at_offset", @@ -2065,9 +1561,15 @@ "mixed_un_set_w_tag", "match_base_w_id_at_offset", "custom_run_action", - "custom_run_action_w_iter_next", "custom_run_action_2_terms", "custom_run_action_w_iter_next_2_terms", + "custom_run_action_w_field", + "custom_run_action_w_2_fields", + "custom_run_w_yield_existing", + "custom_run_w_yield_existing_1_field", + "custom_run_w_yield_existing_1_field_w_callback", + "custom_run_w_yield_existing_2_fields", + "custom_run_w_yield_existing_2_fields_w_callback", "read_in_on_remove_after_add_other_w_not", "observer_w_short_notation", "observer_w_filter_term", @@ -2076,6 +1578,19 @@ "on_add_after_batch_w_exclusive_adds", "propagate_match_relationship_w_self_up", "propagate_match_relationship_w_up", + "propagate_isa_of_parent_add", + "propagate_isa_of_parent_remove", + "propagate_isa_of_parent_set", + "propagate_add_childof_of_parent", + "propagate_add_childof_of_parent_w_siblings", + "propagate_add_childof_w_parent_w_base", + "propagate_remove_childof_w_parent_w_base", + "propagate_remove_childof_of_parent", + "propagate_add_isa_of_parent", + "propagate_remove_isa_of_parent", + "propagate_add_childof_of_base", + "propagate_remove_childof_of_base", + "emit_for_parent_w_prefab_child_and_instance", "observer_w_2_fixed_src", "emit_for_recreated_id_after_remove_all", "emit_for_recreated_id_after_remove_all_wildcard", @@ -2096,9 +1611,59 @@ "notify_after_defer_batched_2_entities_in_table_w_tgt", "multi_observer_table_fill_w_singleton", "wildcard_propagate_w_other_table", - "add_in_yield_existing", - "add_in_yield_existing_multi", - "emit_for_parent_w_prefab_child_and_instance", + "add_in_on_add_yield_existing", + "add_in_on_add_yield_existing_multi", + "add_in_on_remove_yield_existing", + "add_in_on_remove_yield_existing_multi", + "disable_observer", + "disable_observer_module", + "disable_observer_module_nested", + "disable_observer_and_module", + "disable_multi_observer", + "disable_multi_observer_module", + "disable_multi_observer_module_nested", + "disable_multi_observer_and_module", + "tag_w_on_set_and_on_add", + "tag_w_on_set_and_on_add_reverse", + "on_add_pair_w_rel_any", + "on_remove_pair_w_rel_any", + "on_add_pair_w_tgt_any", + "on_remove_pair_w_tgt_any", + "on_add_pair_w_any_any", + "on_remove_pair_w_any_any", + "on_add_any", + "on_remove_any", + "get_filter", + "uni_observer_eval_count", + "multi_observer_eval_count", + "yield_existing_uni_no_this", + "yield_existing_multi_no_this", + "observer_no_id_in_scope", + "register_comp_in_emit_named_entity", + "register_comp_w_macro_in_emit_named_entity", + "add_to_self_in_emit_entity", + "on_set_w_not_tag", + "on_set_w_not_component", + "wildcard_event", + "register_callback_after_run", + "register_run_after_callback", + "register_callback_after_run_ctx", + "register_run_after_callback_ctx", + "on_add_after_new_w_table", + "ref_flag_term_1", + "ref_flag_term_2", + "forward_up_flag_term_1", + "forward_up_flag_term_2", + "propagate_up_flag_term_1", + "propagate_up_flag_term_2", + "row_flag_term_1", + "row_flag_term_2", + "on_add_optional", + "on_remove_optional", + "on_add_multi_optional", + "on_remove_multi_optional", + "on_add_multi_only_optional", + "on_remove_multi_only_optional", "cache_test_1", "cache_test_2", "cache_test_3", @@ -2115,7 +1680,7 @@ "cache_test_14", "cache_test_15", "cache_test_16" - ] + ] }, { "id": "ObserverOnSet", "testcases": [ @@ -2129,6 +1694,10 @@ "add_base_to_2_overridden", "add_base_to_1_of_2_overridden", "on_set_after_remove_override", + "on_set_after_remove_override_isa_before_add", + "on_set_w_override_after_delete", + "on_set_w_override_after_clear", + "on_set_w_override_after_delete_w_ecs_init", "no_set_after_remove_base", "un_set_after_remove", "un_set_after_remove_base", @@ -2137,6 +1706,7 @@ "remove_set_component_in_on_set", "match_table_created_w_add_in_on_set", "set_optional", + "set_optional_one_term", "set_from_nothing", "add_0_entity_in_on_set", "on_set_prefab" @@ -2205,7 +1775,6 @@ "on_set_after_override_w_new", "on_set_after_override_w_new_w_count", "on_set_after_override_1_of_2_overridden", - "on_set_after_snapshot_restore", "on_set_after_remove_override", "emplace", "un_set_tag_w_remove", @@ -2231,7 +1800,8 @@ "monitor_w_wildcard", "monitor_at_fini", "monitor_other_table", - "monitor_component" + "monitor_component", + "yield_existing" ] }, { "id": "Prefab", @@ -2264,7 +1834,7 @@ "prefab_w_base_w_child", "prefab_w_child_w_base", "prefab_w_child_w_base_w_children", - "prefab_w_child_new_w_count", + "prefab_w_child_new_w_count", "prefab_auto_override_child_component", "ignore_on_add", "ignore_on_remove", @@ -2313,7 +1883,7 @@ "rematch_after_prefab_delete", "add_tag_w_low_id_to_instance", "get_type_after_base_add", - "get_type_after_recycled_base_add", + "get_type_after_recycled_base_add", "new_w_recycled_base", "add_recycled_base", "remove_recycled_base", @@ -2338,7 +1908,6 @@ "get_component_pair_from_base", "get_component_pair_from_prefab_base", "override_dont_inherit", - "prefab_w_switch", "prefab_child_w_dont_inherit_component", "prefab_child_override", "prefab_child_override_w_exclusive_pair", @@ -2354,16 +1923,43 @@ "slot_has_union", "slot_override", "base_slot_override", - "prefab_child_w_union", "override_twice_w_add", "override_twice_w_set", "auto_override_copy_once", "always_override", "always_override_pair", "child_of_prefab_is_prefab", + "child_of_prefab_w_prefab_is_prefab", + "child_of_prefab_w_prefab_is_prefab_w_component", "override_exclusive", "override_exclusive_2_lvls", - "hierarchy_w_recycled_id" + "hierarchy_w_recycled_id", + "disable_ids", + "disable_nested_ids", + "prefab_w_children_w_isa_auto_override", + "prefab_child_w_override", + "prefab_child_w_override_and_higher_component", + "prefab_child_w_override_and_lower_component", + "prefab_1_child_offset_id", + "prefab_2_children_offset_id", + "prefab_3_children_offset_id", + "prefab_2_children_2_types_offset_id", + "prefab_3_children_3_types_offset_id", + "prefab_2_children_2_types_reverse_offset_id", + "prefab_3_children_3_types_reverse_offset_id", + "prefab_2_lvl_nested_children_offset_id", + "prefab_3_lvl_nested_children_offset_id", + "prefab_recycled_children_offset_id", + "prefab_recycled_instance_offset_id", + "prefab_children_recycled_offset_id", + "prefab_recycled_children_recycled_offset_id", + "prefab_recycled_children_recycled_offset_id_different_generation", + "prefab_1_child_offset_id_occupied", + "prefab_1_child_offset_id_recycled_occupied", + "prefab_child_offset_w_smaller_child_id", + "prefab_w_union", + "prefab_child_w_union", + "prefab_w_union_and_component" ] }, { "id": "World", @@ -2380,6 +1976,11 @@ "entity_range_add_out_of_range_staged", "entity_range_out_of_range_check_disabled", "entity_range_check_after_delete", + "entity_range_offset_0", + "entity_range_set_limit_to_lower", + "entity_range_set_limit_to_lower_than_offset", + "entity_range_overlapping_new_id", + "entity_range_overlapping_new_bulk_id", "dim", "phases", "phases_w_merging", @@ -2415,15 +2016,22 @@ "use_after_clear_empty_w_component", "use_after_clear_empty_w_component_w_lifecycle", "use_after_clear_unused", - "get_mut_in_at_fini", + "ensure_in_at_fini", "get_type_info", "get_type_info_after_delete_with", "get_type_info_after_reuse", "no_name_prefix_after_init", + "component_init_w_name_prefix", + "component_macro_w_name_prefix", "set_get_context", "set_get_binding_context", "set_get_context_w_free", - "set_get_binding_context_w_free" + "set_get_binding_context_w_free", + "get_entities", + "run_post_frame", + "run_post_frame_outside_of_frame", + "get_flags", + "fini_queue_overflow" ] }, { "id": "WorldInfo", @@ -2457,10 +2065,8 @@ "entity_instanceof_str", "entity_childof_str", "entity_pair_str", - "entity_and_str", "entity_str_small_buffer", "role_pair_str", - "role_and_str", "role_owned_str", "role_disabled_str", "large_type_expr", @@ -2481,15 +2087,16 @@ "defer_twice_in_progress", "run_w_defer", "system_in_progress_w_defer", - "defer_get_mut_no_modify", - "defer_get_mut_w_modify", + "defer_ensure", + "defer_ensure_no_modify", + "defer_ensure_w_modify", "defer_modify", "defer_set_pair", "defer_clear", "defer_add_after_delete", "defer_set_after_delete", - "defer_get_mut_after_delete", - "defer_get_mut_after_delete_2nd_to_last", + "defer_ensure_after_delete", + "defer_ensure_after_delete_2nd_to_last", "defer_add_child_to_deleted_parent", "recreate_deleted_entity_while_deferred", "defer_add_to_recycled_id", @@ -2514,21 +2121,22 @@ "discard_child", "discard_child_w_add", "defer_return_value", - "defer_get_mut_pair", + "defer_ensure_pair", "async_stage_add", "async_stage_add_twice", "async_stage_remove", "async_stage_clear", "async_stage_delete", "async_stage_new", - "async_stage_no_get", "async_stage_readonly", - "async_stage_is_async", "register_component_while_in_progress", "register_component_while_staged", "register_component_while_deferred", "defer_enable", "defer_disable", + "defer_enable_from_stage", + "defer_toggle", + "defer_toggle_from_stage", "defer_delete_with", "defer_remove_all", "deferred_modified_after_remove", @@ -2541,10 +2149,6 @@ "defer_set_large_component", "defer_while_suspend_readonly", "defer_while_suspend_readonly_w_existing_commands", - "defer_add_union_relationship", - "defer_add_existing_union_relationship", - "defer_add_union_relationship_2_ops", - "defer_add_existing_union_relationship_2_ops", "defer_remove_after_set", "defer_remove_after_set_w_observer", "defer_override_after_remove", @@ -2564,24 +2168,24 @@ "remove_after_add_to_nonempty", "register_while_deferred_with_n_stages", "defer_2_sets_w_multi_observer", - "defer_2_get_muts_w_multi_observer", - "defer_2_get_muts_no_modified_w_multi_observer", + "defer_2_ensures_w_multi_observer", + "defer_2_ensures_no_modified_w_multi_observer", "exists_remove_set", "absent_remove_set", "exists_set_remove", "absent_set_remove", - "exists_remove_get_mut", - "absent_remove_get_mut", - "exists_get_mut_remove", - "absent_get_mut_remove", - "exists_set_w_get_mut", + "exists_remove_ensure", + "absent_remove_ensure", + "exists_ensure_remove", + "absent_ensure_remove", + "exists_set_w_ensure", "absent_set_invoke_on_set", "exists_set_invoke_on_set", - "defer_get_mut_no_on_set", - "defer_existing_get_mut_no_on_set", - "get_mut_override", + "defer_ensure_no_on_set", + "defer_existing_ensure_no_on_set", + "ensure_override", "set_override", - "absent_get_mut_for_entity_w_tag", + "absent_ensure_for_entity_w_tag", "on_set_hook_before_on_add_for_existing_component", "defer_2_sets_w_observer_same_component", "defer_2_sets_w_observer_other_component", @@ -2591,7 +2195,27 @@ "on_add_hook_while_defer_suspended", "on_set_hook_while_defer_suspended", "on_remove_hook_while_defer_suspended", - "on_set_hook_batched_is_deferred" + "on_set_hook_batched_is_deferred", + "add_path", + "add_path_to_deleted_parent", + "add_path_nested", + "add_path_nested_to_deleted_parent", + "add_path_nested_to_created_deleted_parent", + "add_path_w_stage", + "add_path_to_deleted_parent_w_stage", + "add_path_nested_w_stage", + "add_path_nested_to_deleted_parent_w_stage", + "add_path_nested_to_created_deleted_parent_w_stage", + "defer_emplace_w_arg", + "defer_emplace_existing_w_arg", + "mixed_on_add_on_set_w_set_w_batching", + "mixed_on_add_on_set_w_emplace", + "add_isa_set_w_override_batched", + "add_set_isa_w_override_batched", + "add_batched_set_with", + "defer_emplace_after_remove", + "batched_w_table_change_in_observer", + "redefine_named_in_threaded_app" ] }, { "id": "SingleThreadStaging", @@ -2651,14 +2275,13 @@ "get_parent_in_progress", "merge_once", "clear_stage_after_merge", - "get_mutable", - "get_mutable_from_main", - "get_mutable_w_add", + "ensureable", + "ensureable_from_main", + "ensureable_w_add", "on_add_after_new_type_in_progress", "lock_table", "recursive_lock_table", "modify_after_lock", - "get_case_from_stage", "get_object_from_stage", "add_to_world_while_readonly", "add_to_world_and_stage_while_readonly", @@ -2689,9 +2312,15 @@ "id": "Table", "testcases": [ "get_index", + "get_index_after_tag", "get_index_not_in_table", + "get_index_tag", + "get_index_pair", + "get_index_pair_tag", "get_column", "get_column_for_tag", + "get_column_for_low_tag", + "get_column_for_high_component", "get_column_for_component_after_tag", "get_column_w_offset", "get_id", @@ -2701,28 +2330,24 @@ "get_depth", "get_depth_non_acyclic", "get_depth_2_paths", - "get_column_size" + "get_column_size", + "has_id", + "has_pair", + "has_wildcard_pair", + "has_any_pair", + "clear_table_kills_entities", + "clear_table_add_new", + "clear_table_check_size", + "clear_table_twice_check_size", + "clear_table_on_remove_hooks", + "clear_table_on_remove_observer" ] }, { "id": "Poly", "testcases": [ - "iter_query", - "iter_query_w_filter", - "iter_world", - "iter_world_w_filter", - "iter_rule", - "iter_rule_w_filter", - "iter_filter", - "iter_filter_w_filter", "on_set_poly_observer", "on_set_poly_query", - "on_set_poly_system", - "iter_filter_from_entity", - "iter_query_from_entity", - "iter_rule_from_entity", - "free_filter_entity", - "free_query_entity", - "free_rule_entity" + "on_set_poly_system" ] }, { "id": "Internals", @@ -2762,7 +2387,8 @@ "log_log", "log_warning", "log_error", - "last_error" + "last_error", + "set_log_level_return" ] }, { "id": "StackAlloc", diff --git a/vendors/flecs/test/api/src/Add.c b/vendors/flecs/test/core/src/Add.c similarity index 86% rename from vendors/flecs/test/api/src/Add.c rename to vendors/flecs/test/core/src/Add.c index 6ab16c33a..408710e91 100644 --- a/vendors/flecs/test/api/src/Add.c +++ b/vendors/flecs/test/core/src/Add.c @@ -1,4 +1,4 @@ -#include +#include #include void Add_zero(void) { @@ -6,11 +6,11 @@ void Add_zero(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); - ecs_add(world, e, 0); + ecs_add_id(world, e, 0); } void Add_component(void) { @@ -18,7 +18,7 @@ void Add_component(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, Position); @@ -32,7 +32,7 @@ void Add_component_again(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, Position); @@ -50,7 +50,7 @@ void Add_2_components(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, Position); @@ -70,7 +70,7 @@ void Add_2_components_again(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, Position); @@ -93,7 +93,7 @@ void Add_2_components_overlap(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, Position); @@ -117,7 +117,7 @@ void Add_component_to_nonempty(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); @@ -133,7 +133,7 @@ void Add_component_to_nonempty_again(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); @@ -167,7 +167,7 @@ void Add_tag(void) { ECS_ENTITY(world, Tag, 0); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add_id(world, e, Tag); @@ -179,10 +179,10 @@ void Add_tag(void) { void Add_add_entity(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - ecs_entity_t f = ecs_new(world, 0); + ecs_entity_t f = ecs_new(world); test_assert(f != 0); ecs_add_id(world, e, f); @@ -194,10 +194,10 @@ void Add_add_entity(void) { void Add_remove_entity(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - ecs_entity_t f = ecs_new(world, 0); + ecs_entity_t f = ecs_new(world); test_assert(f != 0); ecs_add_id(world, e, f); @@ -214,7 +214,7 @@ void Add_add_0_entity(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -229,7 +229,7 @@ void Add_remove_0_entity(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_expect_abort(); @@ -242,7 +242,7 @@ void Add_invalid_add_wildcard(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -255,7 +255,7 @@ void Add_invalid_add_pair_w_wildcard(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -270,7 +270,7 @@ void Add_invalid_add_pair_w_wildcard_rel(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -285,7 +285,7 @@ void Add_invalid_add_pair_w_wildcard_obj(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -300,7 +300,7 @@ void Add_invalid_add_any(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -315,7 +315,7 @@ void Add_invalid_add_pair_w_any(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -330,7 +330,7 @@ void Add_invalid_add_pair_w_any_rel(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -345,7 +345,7 @@ void Add_invalid_add_pair_w_any_obj(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -360,7 +360,7 @@ void Add_invalid_pair_w_0(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -375,7 +375,7 @@ void Add_invalid_pair_w_0_rel(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -390,7 +390,7 @@ void Add_invalid_pair_w_0_obj(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -403,7 +403,7 @@ void Add_add_random_id(void) { for (int i = 0; i < 10; i ++) { ecs_entity_t id = rand() % (100 * 1000) + 1000; - ecs_ensure(world, id); + ecs_make_alive(world, id); test_assert(ecs_is_alive(world, id)); ecs_entity_t e = ecs_new_w_id(world, id); test_assert(ecs_has_id(world, e, id)); diff --git a/vendors/flecs/test/api/src/Clone.c b/vendors/flecs/test/core/src/Clone.c similarity index 80% rename from vendors/flecs/test/api/src/Clone.c rename to vendors/flecs/test/core/src/Clone.c index 5c2156fe0..16f468349 100644 --- a/vendors/flecs/test/api/src/Clone.c +++ b/vendors/flecs/test/core/src/Clone.c @@ -1,9 +1,9 @@ -#include +#include void Clone_empty(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); ecs_entity_t e2 = ecs_clone(world, 0, e1, false); @@ -19,7 +19,7 @@ void Clone_empty(void) { void Clone_empty_w_value(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); ecs_entity_t e2 = ecs_clone(world, 0, e1, true); @@ -54,7 +54,7 @@ void Clone_1_component(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); test_assert(e1 != 0); ecs_entity_t e2 = ecs_clone(world, 0, e1, false); @@ -132,7 +132,7 @@ void Clone_1_component_w_value(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); ecs_set(world, e1, Position, {10, 20}); @@ -164,7 +164,7 @@ void Clone_2_component_w_value(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); ecs_set(world, e1, Position, {10, 20}); @@ -211,7 +211,7 @@ void Clone_3_component_w_value(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); ecs_set(world, e1, Position, {10, 20}); @@ -263,6 +263,82 @@ void Clone_3_component_w_value(void) { ecs_fini(world); } +static int ctor_position = 0; +static +ECS_CTOR(Position, ptr, { + ptr->x = 7; + ptr->y = 9; + ctor_position ++; +}) + +static int dtor_position = 0; +static +ECS_DTOR(Position, ptr, { + dtor_position ++; +}) + +static int copy_position = 0; +static +ECS_COPY(Position, dst, src, { + copy_position ++; + *dst = *src; +}) + +static int move_position = 0; +static +ECS_MOVE(Position, dst, src, { + move_position ++; + *dst = *src; +}) + +void Clone_1_component_w_lifecycle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .dtor = ecs_dtor(Position), + .copy = ecs_copy(Position), + .move = ecs_move(Position) + }); + + ecs_entity_t e1 = ecs_new(world); + test_assert(e1 != 0); + + Position * p = ecs_ensure(world, e1, Position); + test_int(1, ctor_position); + /* Check we get the special default values as per the ctor above */ + test_int(7, p->x); + test_int(9, p->y); + + /* Change the values to something different than default */ + p->x = 1; + p->y = 2; + + ecs_entity_t e2 = ecs_clone(world, 0, e1, true); + /* Test for leaks. Only 2 position objects should be alive */ + test_int(2, ctor_position - dtor_position); + test_assert(e2 != 0); + test_assert(e1 != e2); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e2, Position)); + + const Position *p_1 = ecs_get(world, e1, Position); + test_assert(p_1 != NULL); + test_int(1, p_1->x); + test_int(2, p_1->y); + + const Position *p_2 = ecs_get(world, e2, Position); + test_assert(p_2 != NULL); + test_assert(p_1 != p_2); + test_int(1, p_2->x); + test_int(2, p_2->y); + + ecs_fini(world); + test_int(0, ctor_position - dtor_position); /* test for leaks */ +} + void Clone_tag(void) { ecs_world_t *world = ecs_mini(); @@ -327,7 +403,7 @@ void Clone_1_tag_1_component_w_value(void) { ECS_ENTITY(world, Tag, 0); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add(world, e1, Tag); ecs_set(world, e1, Position, {10, 20}); @@ -355,7 +431,7 @@ void Clone_clone_w_name(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t t = ecs_new_entity(world, "template"); + ecs_entity_t t = ecs_entity(world, { .name = "template" }); ecs_set(world, t, Position, {10, 20}); ecs_set(world, t, Velocity, {1, 2}); @@ -363,6 +439,7 @@ void Clone_clone_w_name(void) { test_assert(ecs_has(world, i, Position)); test_assert(ecs_has(world, i, Velocity)); test_assert(!ecs_has_pair(world, i, ecs_id(EcsIdentifier), EcsName)); + test_assert(ecs_lookup(world, "template") == t); const Position *p = ecs_get(world, i, Position); test_assert(p != NULL); diff --git a/vendors/flecs/test/api/src/Commands.c b/vendors/flecs/test/core/src/Commands.c similarity index 75% rename from vendors/flecs/test/api/src/Commands.c rename to vendors/flecs/test/core/src/Commands.c index 56a7500dd..07da503e1 100644 --- a/vendors/flecs/test/api/src/Commands.c +++ b/vendors/flecs/test/core/src/Commands.c @@ -1,4 +1,4 @@ -#include +#include void Commands_defer_new(void) { ecs_world_t *world = ecs_mini(); @@ -11,7 +11,7 @@ void Commands_defer_new(void) { ecs_defer_begin(world); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(!ecs_has(world, e, Position)); @@ -76,7 +76,7 @@ void Commands_defer_add(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_frame_begin(world, 1); @@ -109,7 +109,7 @@ void Commands_defer_add_two(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_frame_begin(world, 1); @@ -146,7 +146,7 @@ void Commands_defer_remove(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_frame_begin(world, 1); @@ -179,7 +179,7 @@ void Commands_defer_remove_two(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); @@ -218,7 +218,7 @@ void Commands_defer_set(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_set(world, e, Position, {1, 2}); const Position *p = ecs_get(world, e, Position); @@ -273,7 +273,7 @@ void Commands_defer_delete(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_frame_begin(world, 1); @@ -306,7 +306,7 @@ void Commands_defer_twice(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_defer_begin(world); ecs_defer_begin(world); @@ -332,10 +332,10 @@ void Commands_defer_twice_in_progress(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_frame_begin(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_defer_begin(stage); @@ -363,7 +363,7 @@ void Commands_defer_twice_in_progress(void) { static void AddVelocity(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); ecs_defer_begin(it->world); @@ -383,7 +383,7 @@ void Commands_run_w_defer(void) { ECS_SYSTEM(world, AddVelocity, 0, Position, [in] Velocity()); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_run(world, AddVelocity, 0, NULL); @@ -400,7 +400,7 @@ void Commands_system_in_progress_w_defer(void) { ECS_SYSTEM(world, AddVelocity, EcsOnUpdate, Position, [in] Velocity()); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_progress(world, 0); @@ -409,6 +409,32 @@ void Commands_system_in_progress_w_defer(void) { ecs_fini(world); } +void Commands_defer_ensure(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + ecs_defer_end(world); + + { + Position *p = ecs_get_mut(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + static bool on_set_invoked = 0; static @@ -416,7 +442,7 @@ void OnSetTestInvoked(ecs_iter_t *it) { on_set_invoked = 1; } -void Commands_defer_get_mut_no_modify(void) { +void Commands_defer_ensure_no_modify(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); @@ -424,11 +450,11 @@ void Commands_defer_get_mut_no_modify(void) { ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_defer_begin(world); - Velocity *v = ecs_get_mut(world, e, Velocity); + Velocity *v = ecs_ensure(world, e, Velocity); v->x = 1; v->y = 2; @@ -446,7 +472,7 @@ void Commands_defer_get_mut_no_modify(void) { ecs_fini(world); } -void Commands_defer_get_mut_w_modify(void) { +void Commands_defer_ensure_w_modify(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); @@ -454,11 +480,11 @@ void Commands_defer_get_mut_w_modify(void) { ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_defer_begin(world); - Velocity *v = ecs_get_mut(world, e, Velocity); + Velocity *v = ecs_ensure(world, e, Velocity); v->x = 1; v->y = 2; test_assert(!on_set_invoked); @@ -486,7 +512,7 @@ void Commands_defer_modify(void) { ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Velocity); - ecs_entity_t e = ecs_new(world, Velocity); + ecs_entity_t e = ecs_new_w(world, Velocity); ecs_defer_begin(world); @@ -507,7 +533,7 @@ void Commands_defer_set_pair(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); @@ -525,7 +551,7 @@ void Commands_defer_clear(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_frame_begin(world, 1); @@ -558,7 +584,7 @@ void Commands_defer_add_after_delete(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_frame_begin(world, 1); @@ -591,7 +617,7 @@ void Commands_defer_set_after_delete(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_frame_begin(world, 1); @@ -618,20 +644,20 @@ void Commands_defer_set_after_delete(void) { ecs_fini(world); } -void Commands_defer_get_mut_after_delete(void) { +void Commands_defer_ensure_after_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, e); - Velocity *v = ecs_get_mut(world, e, Velocity); + Velocity *v = ecs_ensure(world, e, Velocity); v->x = 1; v->y = 2; @@ -653,24 +679,24 @@ void Commands_defer_get_mut_after_delete(void) { ecs_fini(world); } -void Commands_defer_get_mut_after_delete_2nd_to_last(void) { +void Commands_defer_ensure_after_delete_2nd_to_last(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); /* Create 2nd position. This will cause deletion of the entity in the sparse * set to take a different path since it's not the last. */ - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, e); - Velocity *v = ecs_get_mut(world, e, Velocity); + Velocity *v = ecs_ensure(world, e, Velocity); v->x = 1; v->y = 2; @@ -698,7 +724,7 @@ void Commands_defer_add_child_to_deleted_parent(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_frame_begin(world, 1); @@ -723,7 +749,7 @@ void Commands_recreate_deleted_entity_while_deferred(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e_old = ecs_new(world, 0); + ecs_entity_t e_old = ecs_new(world); test_assert(e_old != 0); ecs_delete(world, e_old); @@ -735,7 +761,7 @@ void Commands_recreate_deleted_entity_while_deferred(void) { ecs_defer_begin(world); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(e != e_old); test_assert((e & ECS_ENTITY_MASK) == (e_old & ECS_ENTITY_MASK)); @@ -763,12 +789,12 @@ void Commands_defer_add_to_recycled_id(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_delete(world, id); - ecs_entity_t id_2 = ecs_new_id(world); + ecs_entity_t id_2 = ecs_new(world); test_assert(id != id_2); test_assert((int32_t)id == (int32_t)id_2); @@ -797,12 +823,12 @@ void Commands_defer_add_to_recycled_id_w_role(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_delete(world, id); - ecs_entity_t id_2 = ecs_new_id(world); + ecs_entity_t id_2 = ecs_new(world); test_assert(id != id_2); test_assert((int32_t)id == (int32_t)id_2); @@ -831,15 +857,15 @@ void Commands_defer_add_to_recycled_relation(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t target = ecs_new_id(world); + ecs_entity_t target = ecs_new(world); test_assert(target != 0); - ecs_entity_t rel = ecs_new_id(world); + ecs_entity_t rel = ecs_new(world); test_assert(rel != 0); ecs_delete(world, rel); - ecs_entity_t rel_2 = ecs_new_id(world); + ecs_entity_t rel_2 = ecs_new(world); test_assert(rel != rel_2); test_assert((int32_t)rel == (int32_t)rel_2); @@ -870,12 +896,12 @@ void Commands_defer_add_to_recycled_object(void) { ECS_COMPONENT(world, Velocity); ECS_TAG(world, Rel); - ecs_entity_t target = ecs_new(world, 0); + ecs_entity_t target = ecs_new(world); test_assert(target != 0); ecs_delete(world, target); - ecs_entity_t object_2 = ecs_new_id(world); + ecs_entity_t object_2 = ecs_new(world); test_assert(target != object_2); test_assert((int32_t)target == (int32_t)object_2); @@ -904,12 +930,12 @@ void Commands_defer_add_to_recycled_object_childof(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_delete(world, parent); - ecs_entity_t parent_2 = ecs_new_id(world); + ecs_entity_t parent_2 = ecs_new(world); test_assert(parent != parent_2); test_assert((int32_t)parent == (int32_t)parent_2); @@ -938,7 +964,7 @@ void Commands_defer_add_to_deleted_id(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_frame_begin(world, 1); @@ -964,7 +990,7 @@ void Commands_defer_add_to_deleted_id_w_role(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_frame_begin(world, 1); @@ -992,10 +1018,10 @@ void Commands_defer_add_to_deleted_relation(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t target = ecs_new_id(world); + ecs_entity_t target = ecs_new(world); test_assert(target != 0); - ecs_entity_t rel = ecs_new_id(world); + ecs_entity_t rel = ecs_new(world); test_assert(rel != 0); ecs_frame_begin(world, 1); @@ -1026,7 +1052,7 @@ void Commands_defer_add_to_deleted_object(void) { ECS_TAG(world, Rel); - ecs_entity_t target = ecs_new(world, 0); + ecs_entity_t target = ecs_new(world); ecs_frame_begin(world, 1); @@ -1053,7 +1079,7 @@ void Commands_defer_add_to_deleted_object_childof(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_frame_begin(world, 1); @@ -1079,7 +1105,7 @@ void Commands_defer_delete_added_id(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_frame_begin(world, 1); @@ -1107,7 +1133,7 @@ void Commands_defer_delete_added_id_w_role(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_frame_begin(world, 1); @@ -1135,10 +1161,10 @@ void Commands_defer_delete_added_relation(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t target = ecs_new_id(world); + ecs_entity_t target = ecs_new(world); test_assert(target != 0); - ecs_entity_t rel = ecs_new_id(world); + ecs_entity_t rel = ecs_new(world); test_assert(rel != 0); ecs_frame_begin(world, 1); @@ -1149,7 +1175,7 @@ void Commands_defer_delete_added_relation(void) { ecs_add(world, child, Velocity); ecs_delete(world, rel); - ecs_defer_end(world); + ecs_defer_end(world); ecs_frame_end(world); @@ -1169,7 +1195,7 @@ void Commands_defer_delete_added_object(void) { ECS_TAG(world, Rel); - ecs_entity_t target = ecs_new(world, 0); + ecs_entity_t target = ecs_new(world); ecs_frame_begin(world, 1); @@ -1196,7 +1222,7 @@ void Commands_defer_delete_added_object_childof(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_frame_begin(world, 1); @@ -1221,7 +1247,7 @@ void Commands_discard_add(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_frame_begin(world, 1); @@ -1258,7 +1284,7 @@ void Commands_discard_remove(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_frame_begin(world, 1); @@ -1296,7 +1322,7 @@ void Commands_discard_add_two(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_frame_begin(world, 1); @@ -1337,7 +1363,7 @@ void Commands_discard_remove_two(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); @@ -1385,7 +1411,7 @@ void Commands_discard_child(void) { ecs_defer_begin(world); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(!ecs_has(world, e, Position)); ecs_delete(world, e); @@ -1426,13 +1452,13 @@ void Commands_discard_child_w_add(void) { ecs_defer_begin(world); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(!ecs_has(world, e, Position)); ecs_delete(world, e); test_assert(ecs_is_alive(world, e)); - ecs_entity_t child = ecs_new(world, 0); + ecs_entity_t child = ecs_new(world); test_assert(child != 0); ecs_add_pair(world, child, EcsChildOf, e); @@ -1470,17 +1496,17 @@ void Commands_defer_return_value(void) { ecs_fini(world); } -void Commands_defer_get_mut_pair(void) { +void Commands_defer_ensure_pair(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Pair); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); - Position *p = ecs_get_mut_pair(world, e, Position, Pair); + Position *p = ecs_ensure_pair(world, e, Position, Pair); test_assert(p != NULL); p->x = 10; p->y = 20; @@ -1500,15 +1526,15 @@ void Commands_async_stage_add(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); ecs_add(async, e, Position); test_assert(!ecs_has(world, e, Position)); ecs_merge(async); test_assert(ecs_has(world, e, Position)); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -1519,9 +1545,9 @@ void Commands_async_stage_add_twice(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); ecs_add(async, e, Position); test_assert(!ecs_has(world, e, Position)); ecs_merge(async); @@ -1532,7 +1558,7 @@ void Commands_async_stage_add_twice(void) { ecs_merge(async); test_assert(ecs_has(world, e, Velocity)); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -1543,15 +1569,15 @@ void Commands_async_stage_remove(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); ecs_remove(async, e, Position); test_assert(ecs_has(world, e, Position)); ecs_merge(async); test_assert(!ecs_has(world, e, Position)); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -1562,10 +1588,10 @@ void Commands_async_stage_clear(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); ecs_clear(async, e); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); @@ -1573,7 +1599,7 @@ void Commands_async_stage_clear(void) { test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -1584,10 +1610,10 @@ void Commands_async_stage_delete(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); ecs_delete(async, e); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); @@ -1595,7 +1621,7 @@ void Commands_async_stage_delete(void) { ecs_merge(async); test_assert(!ecs_is_alive(world, e)); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -1606,8 +1632,8 @@ void Commands_async_stage_new(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_world_t *async = ecs_async_stage_new(world); - ecs_entity_t e = ecs_new(async, 0); + ecs_world_t *async = ecs_stage_new(world); + ecs_entity_t e = ecs_new(async); ecs_add(async, e, Position); ecs_add(async, e, Velocity); test_assert(!ecs_has(world, e, Position)); @@ -1617,43 +1643,18 @@ void Commands_async_stage_new(void) { test_assert(ecs_has(world, e, Velocity)); test_assert(ecs_is_alive(world, e)); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } -void Commands_async_stage_no_get(void) { - install_test_abort(); - - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t e = ecs_new(world, Position); - - ecs_world_t *async = ecs_async_stage_new(world); - - test_expect_abort(); - ecs_get(async, e, Position); -} - void Commands_async_stage_readonly(void) { ecs_world_t *world = ecs_mini(); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); test_assert(!ecs_stage_is_readonly(async)); - ecs_async_stage_free(async); - ecs_fini(world); -} - -void Commands_async_stage_is_async(void) { - ecs_world_t *world = ecs_mini(); - - ecs_world_t *async = ecs_async_stage_new(world); - test_assert(ecs_stage_is_async(async)); - - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -1680,7 +1681,7 @@ void Commands_register_component_while_in_progress(void) { test_assert(ecs_id(Position) != 0); test_assert(ecs_has(world, ecs_id(Position), EcsComponent)); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {10, 20}); test_assert(ecs_has_id(world, e, ecs_id(Position))); @@ -1697,10 +1698,10 @@ void Commands_register_component_while_staged(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t canary = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); + ecs_entity_t canary = ecs_new(world); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); @@ -1736,8 +1737,8 @@ void Commands_register_component_while_deferred(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t canary = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); + ecs_entity_t canary = ecs_new(world); ecs_defer_begin(world); @@ -1764,7 +1765,7 @@ void Commands_register_component_while_deferred(void) { void Commands_defer_enable(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_enable(world, e, false); @@ -1791,15 +1792,70 @@ void Commands_defer_disable(void) { ecs_fini(world); } +void Commands_defer_enable_from_stage(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_new(world); + + ecs_world_t *stage = ecs_get_stage(world, 0); + + ecs_defer_begin(world); + ecs_enable(stage, e, false); + test_assert(!ecs_has_id(world, e, EcsDisabled)); + ecs_defer_end(world); + + test_assert(ecs_has_id(world, e, EcsDisabled)); + + ecs_fini(world); +} + +void Commands_defer_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ecs_add_id(world, Foo, EcsCanToggle); + + ecs_entity_t e = ecs_new_w(world, Foo); + + ecs_defer_begin(world); + ecs_enable_id(world, e, Foo, false); + test_assert(ecs_is_enabled_id(world, e, Foo)); + ecs_defer_end(world); + + test_assert(!ecs_is_enabled_id(world, e, Foo)); + + ecs_fini(world); +} + +void Commands_defer_toggle_from_stage(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ecs_add_id(world, Foo, EcsCanToggle); + + ecs_entity_t e = ecs_new_w(world, Foo); + + ecs_world_t *stage = ecs_get_stage(world, 0); + + ecs_defer_begin(world); + ecs_enable_id(stage, e, Foo, false); + test_assert(ecs_is_enabled_id(stage, e, Foo)); + ecs_defer_end(world); + + test_assert(!ecs_is_enabled_id(world, e, Foo)); + + ecs_fini(world); +} + void Commands_defer_delete_with(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_entity_t e_3 = ecs_new(world, TagA); + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_entity_t e_3 = ecs_new_w(world, TagA); ecs_add(world, e_1, TagB); ecs_defer_begin(world); @@ -1822,9 +1878,9 @@ void Commands_defer_remove_all(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e_1 = ecs_new(world, TagA); - ecs_entity_t e_2 = ecs_new(world, TagA); - ecs_entity_t e_3 = ecs_new(world, TagA); + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_entity_t e_3 = ecs_new_w(world, TagA); ecs_add(world, e_1, TagB); ecs_defer_begin(world); @@ -1852,12 +1908,12 @@ void Commands_deferred_modified_after_remove(void) { ECS_COMPONENT(world, Position); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = { EcsOnSet }, .callback = OnSetTestInvoked }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_modified(world, e, Position); @@ -1894,7 +1950,7 @@ static void update_counter(ecs_iter_t *it) { ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); test_assert(parent != 0); - Counter *ptr = ecs_get_mut(world, parent, Counter); + Counter *ptr = ecs_ensure(world, parent, Counter); test_assert(ptr != NULL); if (it->event == EcsOnAdd) { @@ -1908,7 +1964,7 @@ static void update_counter(ecs_iter_t *it) { } static void remove_counter(ecs_iter_t *it) { - Counter *ptr = ecs_field(it, Counter, 1); + Counter *ptr = ecs_field(it, Counter, 0); for (int i = 0; i < it->count; i ++) { test_int(ptr[i].value, 0); @@ -1923,21 +1979,21 @@ void Commands_merge_cleanup_ops_before_delete(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnAdd, EcsOnRemove}, .callback = update_counter }); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Counter), - .src.flags = EcsSelf + .src.id = EcsSelf }, .events = {EcsOnRemove}, .callback = remove_counter }); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_set(world, parent, Counter, {0}); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1964,24 +2020,24 @@ void Commands_merge_nested_cleanup_ops_before_delete(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = Tag, - .src.flags = EcsSelf + .src.id = EcsSelf }, .events = {EcsOnAdd, EcsOnRemove}, .callback = update_counter }); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Counter), - .src.flags = EcsSelf + .src.id = EcsSelf }, .events = {EcsOnRemove}, .callback = remove_counter }); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_set(world, parent, Counter, {0}); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -2015,7 +2071,7 @@ void Commands_defer_suspend_resume(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_add(world, e, TagA); @@ -2059,7 +2115,7 @@ void Commands_create_observer_while_deferred(void) { ecs_defer_begin(world); Probe ctx = {0}; ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ .id = TagA }}, + .query.terms = {{ .id = TagA }}, .events = {EcsOnAdd}, .callback = System, .ctx = &ctx @@ -2068,7 +2124,7 @@ void Commands_create_observer_while_deferred(void) { test_assert(observer != 0); test_int(ctx.invoked, 0); - ecs_new(world, TagA); + ecs_new_w(world, TagA); test_int(ctx.invoked, 1); ecs_fini(world); @@ -2080,13 +2136,13 @@ void Commands_create_query_while_deferred(void) { ECS_TAG(world, TagA); ecs_defer_begin(world); - ecs_query_t *query = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ .id = TagA }} + ecs_query_t *query = ecs_query(world, { + .terms = {{ .id = TagA }} }); ecs_defer_end(world); test_assert(query != 0); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); ecs_iter_t it = ecs_query_iter(world, query); test_bool(true, ecs_query_next(&it)); @@ -2094,6 +2150,8 @@ void Commands_create_query_while_deferred(void) { test_uint(e, it.entities[0]); test_bool(false, ecs_query_next(&it)); + ecs_query_fini(query); + ecs_fini(world); } @@ -2105,7 +2163,7 @@ void Commands_update_observer_while_deferred(void) { ecs_defer_begin(world); Probe ctx = {0}; ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ .id = TagA }}, + .query.terms = {{ .id = TagA }}, .events = {EcsOnAdd}, .callback = System, .ctx = &ctx @@ -2114,7 +2172,7 @@ void Commands_update_observer_while_deferred(void) { test_assert(observer != 0); test_int(ctx.invoked, 0); - ecs_new(world, TagA); + ecs_new_w(world, TagA); test_int(ctx.invoked, 1); test_int(system_2_invoked, 0); @@ -2126,7 +2184,7 @@ void Commands_update_observer_while_deferred(void) { }); ecs_defer_end(world); - ecs_new(world, TagA); + ecs_new_w(world, TagA); test_int(ctx.invoked, 2); test_int(system_2_invoked, 1); @@ -2142,10 +2200,10 @@ void Commands_defer_set_large_component(void) { ECS_COMPONENT(world, LargeComponent); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); - LargeComponent *ptr = ecs_get_mut(world, e, LargeComponent); + LargeComponent *ptr = ecs_ensure(world, e, LargeComponent); test_assert(ptr != NULL); for (int i = 0; i < 8096; i ++) { ptr->arr[i] = i; @@ -2153,7 +2211,7 @@ void Commands_defer_set_large_component(void) { test_assert(!ecs_has(world, e, LargeComponent)); ecs_defer_end(world); - ptr = ecs_get_mut(world, e, LargeComponent); + ptr = ecs_ensure(world, e, LargeComponent); test_assert(ptr != NULL); for (int i = 0; i < 8096; i ++) { test_int(ptr->arr[i], i); @@ -2165,7 +2223,7 @@ void Commands_defer_set_large_component(void) { static ECS_COMPONENT_DECLARE(Position); static void CreatePosition(ecs_iter_t *it) { - ecs_entity_t e = ecs_new_id(it->world); + ecs_entity_t e = ecs_new(it->world); ecs_set(it->world, e, Position, {999, 1000}); } @@ -2177,22 +2235,22 @@ void Commands_defer_while_suspend_readonly(void) { /* Create observer on EcsComponent which will defer a command while readonly * mode is suspended */ ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(EcsComponent), + .query.terms[0].id = ecs_id(EcsComponent), .events = { EcsOnAdd }, .callback = CreatePosition }); ecs_world_t *s = ecs_get_stage(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); /* Component creation suspends readonly mode so that they can be used right * after they have been registered */ ECS_COMPONENT(s, Velocity); - ecs_entity_t e = - ecs_set(s, 0, Position, {10, 20}); - ecs_set(s, e, Velocity, {1, 2}); + ecs_entity_t e = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); test_assert(!ecs_has(s, e, Position)); test_assert(!ecs_has(s, e, Velocity)); @@ -2224,25 +2282,25 @@ void Commands_defer_while_suspend_readonly_w_existing_commands(void) { /* Create observer on EcsComponent which will defer a command while readonly * mode is suspended */ ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(EcsComponent), + .query.terms[0].id = ecs_id(EcsComponent), .events = { EcsOnAdd }, .callback = CreatePosition }); ecs_world_t *s = ecs_get_stage(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); - ecs_entity_t e1 = ecs_set(s, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(s, ecs_value(Position, {10, 20})); test_assert(!ecs_has(s, e1, Position)); /* Component creation suspends readonly mode so that they can be used right * after they have been registered */ ECS_COMPONENT(s, Velocity); - ecs_entity_t e2 = - ecs_set(s, 0, Position, {20, 30}); - ecs_set(s, e2, Velocity, {1, 2}); + ecs_entity_t e2 = ecs_insert(world, + ecs_value(Position, {20, 30}), + ecs_value(Velocity, {1, 2})); test_assert(!ecs_has(s, e2, Position)); test_assert(!ecs_has(s, e2, Velocity)); @@ -2272,96 +2330,12 @@ void Commands_defer_while_suspend_readonly_w_existing_commands(void) { ecs_fini(world); } -void Commands_defer_add_union_relationship(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Union); - ECS_TAG(world, Tgt); - - ecs_entity_t e = ecs_new_id(world); - ecs_defer_begin(world); - ecs_add_pair(world, e, Rel, Tgt); - test_assert(!ecs_has_pair(world, e, Rel, Tgt)); - ecs_defer_end(world); - test_assert(ecs_has_pair(world, e, Rel, Tgt)); - - ecs_fini(world); -} - -void Commands_defer_add_existing_union_relationship(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Union); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Rel, TgtA); - test_assert(ecs_has_pair(world, e, Rel, TgtA)); - - ecs_defer_begin(world); - ecs_add_pair(world, e, Rel, TgtB); - test_assert(ecs_has_pair(world, e, Rel, TgtA)); - test_assert(!ecs_has_pair(world, e, Rel, TgtB)); - ecs_defer_end(world); - test_assert(!ecs_has_pair(world, e, Rel, TgtA)); - test_assert(ecs_has_pair(world, e, Rel, TgtB)); - - ecs_fini(world); -} - -void Commands_defer_add_union_relationship_2_ops(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Union); - ECS_TAG(world, Tgt); - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new_id(world); - ecs_defer_begin(world); - ecs_add_pair(world, e, Rel, Tgt); - ecs_add(world, e, Tag); - test_assert(!ecs_has_pair(world, e, Rel, Tgt)); - test_assert(!ecs_has(world, e, Tag)); - ecs_defer_end(world); - test_assert(ecs_has_pair(world, e, Rel, Tgt)); - test_assert(ecs_has(world, e, Tag)); - - ecs_fini(world); -} - -void Commands_defer_add_existing_union_relationship_2_ops(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Union); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, Rel, TgtA); - test_assert(ecs_has_pair(world, e, Rel, TgtA)); - - ecs_defer_begin(world); - ecs_add_pair(world, e, Rel, TgtB); - ecs_add(world, e, Tag); - test_assert(ecs_has_pair(world, e, Rel, TgtA)); - test_assert(!ecs_has_pair(world, e, Rel, TgtB)); - test_assert(!ecs_has(world, e, Tag)); - ecs_defer_end(world); - test_assert(!ecs_has_pair(world, e, Rel, TgtA)); - test_assert(ecs_has_pair(world, e, Rel, TgtB)); - test_assert(ecs_has(world, e, Tag)); - - ecs_fini(world); -} - void Commands_defer_remove_after_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); @@ -2388,7 +2362,7 @@ void Commands_defer_remove_after_set_w_observer(void) { ECS_COMPONENT(world, Position); ECS_OBSERVER(world, PositionObserver, EcsOnSet, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); @@ -2408,7 +2382,9 @@ void Commands_defer_override_after_remove(void) { ECS_COMPONENT(world, Position); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); ecs_set(world, inst, Position, {20, 30}); @@ -2433,7 +2409,9 @@ void Commands_defer_override_after_remove_3_ops(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); ecs_set(world, inst, Position, {20, 30}); @@ -2457,9 +2435,9 @@ void Commands_flush_stage_to_deferred_world(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); ecs_add(async, e, Tag); test_assert(!ecs_has(world, e, Tag)); @@ -2469,7 +2447,7 @@ void Commands_flush_stage_to_deferred_world(void) { ecs_defer_end(world); test_assert(ecs_has(world, e, Tag)); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -2488,7 +2466,7 @@ void Commands_add_in_observer_during_merge(void) { ECS_OBSERVER(world, AddPosition, EcsOnAdd, TagA); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_add(world, e, TagA); @@ -2516,9 +2494,7 @@ void Commands_add_in_observer_during_merge_2_commands(void) { ECS_OBSERVER(world, AddPosition, EcsOnAdd, TagB); - ecs_entity_t e = ecs_new_id(world); - - ecs_log_set_level(0); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_add(world, e, TagA); @@ -2537,8 +2513,6 @@ void Commands_add_in_observer_during_merge_2_commands(void) { test_int(ptr->x, 10); test_int(ptr->y, 20); - ecs_log_set_level(-1); - ecs_fini(world); } @@ -2561,7 +2535,7 @@ void Commands_add_2_in_observer_while_on_remove_for_delete(void) { ECS_OBSERVER(world, AddTwoTags, EcsOnRemove, TagA); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); @@ -2578,8 +2552,8 @@ void Commands_add_2_in_observer_while_on_remove_for_delete_child(void) { ECS_OBSERVER(world, AddTwoTags, EcsOnRemove, TagA); - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e = ecs_new_w(world, TagA); ecs_add_pair(world, e, EcsChildOf, parent); ecs_delete(world, e); @@ -2597,9 +2571,9 @@ void Commands_add_2_in_observer_while_on_remove_for_delete_recycled_id(void) { ECS_OBSERVER(world, AddTwoTags, EcsOnRemove, TagA); - ecs_delete(world, ecs_new_id(world)); + ecs_delete(world, ecs_new(world)); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); @@ -2616,9 +2590,9 @@ void Commands_add_2_in_observer_while_on_remove_for_deferred_delete_recycled_id( ECS_OBSERVER(world, AddTwoTags, EcsOnRemove, TagA); - ecs_delete(world, ecs_new_id(world)); + ecs_delete(world, ecs_new(world)); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); ecs_defer_begin(world); ecs_delete(world, e); ecs_defer_end(world); @@ -2634,7 +2608,7 @@ void Commands_defer_add_after_clear(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_clear(world, e); @@ -2651,8 +2625,8 @@ void Commands_defer_cmd_after_modified(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); ecs_defer_begin(world); ecs_set(world, e1, Position, {10, 20}); @@ -2684,11 +2658,11 @@ void Commands_defer_remove_after_emplace_different_id(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Tag); ecs_defer_begin(world); - Position *p = ecs_emplace(world, e, Position); + Position *p = ecs_emplace(world, e, Position, NULL); p->x = 10; p->y = 20; ecs_remove(world, e, Tag); @@ -2714,11 +2688,11 @@ void Commands_defer_remove_after_set_and_emplace_different_id(void) { ECS_COMPONENT(world, Velocity); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Tag); ecs_defer_begin(world); - Position *p = ecs_emplace(world, e, Position); + Position *p = ecs_emplace(world, e, Position, NULL); p->x = 10; p->y = 20; ecs_set(world, e, Velocity, {1, 2}); @@ -2751,7 +2725,7 @@ void Commands_clear_after_add_to_nonempty(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); ecs_defer_begin(world); ecs_add(world, e, TagB); @@ -2773,7 +2747,7 @@ void Commands_remove_after_add_to_nonempty(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); ecs_defer_begin(world); ecs_add(world, e, TagB); @@ -2811,8 +2785,8 @@ void PositionVelocityObserver(ecs_iter_t *it) { position_velocity_observer_invoked ++; test_int(it->count, 1); - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); test_assert(p != NULL); test_assert(v != NULL); @@ -2831,12 +2805,12 @@ void Commands_defer_2_sets_w_multi_observer(void) { ECS_COMPONENT(world, Velocity); ecs_observer(world, { - .filter.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, + .query.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, .callback = PositionVelocityObserver, .events = { EcsOnSet } }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); @@ -2855,27 +2829,27 @@ void Commands_defer_2_sets_w_multi_observer(void) { ecs_fini(world); } -void Commands_defer_2_get_muts_w_multi_observer(void) { +void Commands_defer_2_ensures_w_multi_observer(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_observer(world, { - .filter.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, + .query.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, .callback = PositionVelocityObserver, .events = { EcsOnSet } }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); p->x = 10; p->y = 20; ecs_modified(world, e, Position); - Velocity *v = ecs_get_mut(world, e, Velocity); + Velocity *v = ecs_ensure(world, e, Velocity); v->x = 1; v->y = 2; ecs_modified(world, e, Velocity); @@ -2894,26 +2868,26 @@ void Commands_defer_2_get_muts_w_multi_observer(void) { ecs_fini(world); } -void Commands_defer_2_get_muts_no_modified_w_multi_observer(void) { +void Commands_defer_2_ensures_no_modified_w_multi_observer(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_observer(world, { - .filter.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, + .query.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, .callback = PositionVelocityObserver, .events = { EcsOnSet } }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); p->x = 10; p->y = 20; - Velocity *v = ecs_get_mut(world, e, Velocity); + Velocity *v = ecs_ensure(world, e, Velocity); v->x = 1; v->y = 2; } @@ -2936,7 +2910,7 @@ void Commands_exists_remove_set(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); @@ -2957,7 +2931,7 @@ void Commands_absent_remove_set(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_remove(world, e, Position); @@ -2977,7 +2951,7 @@ void Commands_exists_set_remove(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); @@ -2996,7 +2970,7 @@ void Commands_absent_set_remove(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_set(world, e, Position, {5, 6}); @@ -3009,18 +2983,18 @@ void Commands_absent_set_remove(void) { ecs_fini(world); } -void Commands_exists_set_w_get_mut(void) { +void Commands_exists_set_w_ensure(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); { ecs_set(world, e, Position, {5, 6}); - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); p->x = 11; } ecs_defer_end(world); @@ -3033,18 +3007,18 @@ void Commands_exists_set_w_get_mut(void) { ecs_fini(world); } -void Commands_exists_remove_get_mut(void) { +void Commands_exists_remove_ensure(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); ecs_remove(world, e, Position); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); p->x = 5; p->y = 6; @@ -3059,17 +3033,17 @@ void Commands_exists_remove_get_mut(void) { ecs_fini(world); } -void Commands_absent_remove_get_mut(void) { +void Commands_absent_remove_ensure(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_remove(world, e, Position); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); p->x = 5; p->y = 6; @@ -3085,17 +3059,17 @@ void Commands_absent_remove_get_mut(void) { ecs_fini(world); } -void Commands_exists_get_mut_remove(void) { +void Commands_exists_ensure_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); p->x = 5; p->y = 6; @@ -3109,16 +3083,16 @@ void Commands_exists_get_mut_remove(void) { ecs_fini(world); } -void Commands_absent_get_mut_remove(void) { +void Commands_absent_ensure_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); p->x = 5; p->y = 6; @@ -3140,7 +3114,7 @@ void Commands_absent_set_invoke_on_set(void) { ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); /* create the component */ ecs_defer_begin(world); @@ -3164,7 +3138,7 @@ void Commands_exists_set_invoke_on_set(void) { ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {1, 2}); on_set_invoked = 0; @@ -3184,19 +3158,19 @@ void Commands_exists_set_invoke_on_set(void) { ecs_fini(world); } -void Commands_defer_get_mut_no_on_set(void) { +void Commands_defer_ensure_no_on_set(void) { ecs_world_t *world = ecs_mini(); on_set_invoked = 0; ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); /* create the component */ ecs_defer_begin(world); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); p->x = 1; p->y = 2; } @@ -3212,19 +3186,19 @@ void Commands_defer_get_mut_no_on_set(void) { ecs_fini(world); } -void Commands_defer_existing_get_mut_no_on_set(void) { +void Commands_defer_existing_ensure_no_on_set(void) { ecs_world_t *world = ecs_mini(); on_set_invoked = 0; ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); /* create the component */ ecs_defer_begin(world); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); p->x = 1; p->y = 2; } @@ -3240,17 +3214,19 @@ void Commands_defer_existing_get_mut_no_on_set(void) { ecs_fini(world); } -void Commands_get_mut_override(void) { +void Commands_ensure_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t base = ecs_set(world, 0, Position, {1, 2}); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {1, 2})); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_defer_begin(world); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); @@ -3281,7 +3257,9 @@ void Commands_set_override(void) { ECS_COMPONENT(world, Position); - ecs_entity_t base = ecs_set(world, 0, Position, {1, 2}); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {1, 2})); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_defer_begin(world); @@ -3307,18 +3285,18 @@ void Commands_set_override(void) { ecs_fini(world); } -void Commands_absent_get_mut_for_entity_w_tag(void) { +void Commands_absent_ensure_for_entity_w_tag(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); test_assert(e != 0); ecs_defer_begin(world); { - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); p->x = 10; p->y = 20; @@ -3364,12 +3342,12 @@ void Commands_on_set_hook_before_on_add_for_existing_component(void) { }); ecs_observer(world, { - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = { EcsOnAdd }, .callback = add_tag }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_defer_begin(world); ecs_add(world, e, TagB); /* 2 add commands, to trigger batching */ @@ -3392,12 +3370,12 @@ void Commands_defer_2_sets_w_observer_same_component(void) { ECS_COMPONENT(world, Position); ecs_observer(world, { - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = { EcsOnSet }, .callback = set_position_hook }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); @@ -3421,18 +3399,18 @@ void Commands_defer_2_sets_w_observer_other_component(void) { ECS_COMPONENT(world, Velocity); ecs_observer(world, { - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = { EcsOnSet }, .callback = set_position_hook }); ecs_observer(world, { - .filter.terms[0].id = ecs_id(Velocity), + .query.terms[0].id = ecs_id(Velocity), .events = { EcsOnSet }, .callback = set_velocity_hook }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); @@ -3468,12 +3446,12 @@ void Commands_on_remove_after_deferred_clear_and_add(void) { ECS_TAG(world, TagC); ecs_observer(world, { - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = { EcsOnRemove }, .callback = remove_tag }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); @@ -3492,8 +3470,8 @@ void Commands_on_remove_after_deferred_clear_and_add(void) { } static void Recycle(ecs_iter_t *it) { - ecs_new_id(it->world); - *(ecs_entity_t*)it->ctx = ecs_new_id(it->world); + ecs_new(it->world); + *(ecs_entity_t*)it->ctx = ecs_new(it->world); } void Commands_defer_delete_recycle_same_id(void) { @@ -3502,12 +3480,12 @@ void Commands_defer_delete_recycle_same_id(void) { ECS_TAG(world, Foo); ECS_TAG(world, Bar); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_observer(world, { - .filter.terms = { + .query.terms = { { .id = ecs_id(Foo) } }, .events = { EcsOnAdd }, @@ -3546,7 +3524,7 @@ void Commands_observer_while_defer_suspended(void) { ECS_COMPONENT_DEFINE(world, Velocity); ecs_observer(world, { - .filter.terms = { + .query.terms = { { .id = ecs_id(Position) } }, .events = { EcsOnAdd }, @@ -3556,7 +3534,7 @@ void Commands_observer_while_defer_suspended(void) { ecs_defer_begin(world); ecs_defer_suspend(world); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); @@ -3583,7 +3561,7 @@ void Commands_on_add_hook_while_defer_suspended(void) { ecs_defer_begin(world); ecs_defer_suspend(world); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); @@ -3610,7 +3588,7 @@ void Commands_on_set_hook_while_defer_suspended(void) { ecs_defer_begin(world); ecs_defer_suspend(world); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); @@ -3634,7 +3612,7 @@ void Commands_on_remove_hook_while_defer_suspended(void) { .on_remove = AddWhileSuspended }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); @@ -3690,7 +3668,7 @@ void Commands_on_set_hook_batched_is_deferred(void) { ECS_OBSERVER(world, DummyObserver, EcsOnSet, Position); - ecs_entity_t e = ecs_new(world, Bar); + ecs_entity_t e = ecs_new_w(world, Bar); ecs_add(world, e, Position); test_int(on_set_is_deferred_invoked, 0); @@ -3713,3 +3691,521 @@ void Commands_on_set_hook_batched_is_deferred(void) { ecs_fini(world); } + +void Commands_add_path(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_defer_begin(world); + ecs_add_path(world, e, p, "foo"); + ecs_defer_end(world); + + ecs_entity_t foo = ecs_lookup(world, "p.foo"); + test_assert(foo == e); + + char *path = ecs_get_path(world, e); + test_assert(path != NULL); + test_str(path, "p.foo"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Commands_add_path_to_deleted_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_defer_begin(world); + ecs_delete(world, p); + ecs_add_path(world, e, p, "foo"); + ecs_defer_end(world); + + ecs_entity_t foo = ecs_lookup(world, "p.foo"); + test_assert(foo == 0); + + test_assert(!ecs_is_alive(world, p)); + test_assert(!ecs_is_alive(world, e)); + + ecs_fini(world); +} + +void Commands_add_path_nested(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_defer_begin(world); + ecs_add_path(world, e, p, "foo.bar"); + ecs_defer_end(world); + + ecs_entity_t foo = ecs_lookup(world, "p.foo"); + test_assert(foo != 0); + + char *path = ecs_get_path(world, e); + test_assert(path != NULL); + test_str(path, "p.foo.bar"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Commands_add_path_nested_to_deleted_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_defer_begin(world); + ecs_delete(world, p); + ecs_add_path(world, e, p, "foo.bar"); + ecs_defer_end(world); + + ecs_entity_t foo = ecs_lookup(world, "p.foo"); + test_assert(foo == 0); + + test_assert(!ecs_is_alive(world, p)); + test_assert(!ecs_is_alive(world, e)); + + ecs_fini(world); +} + +void Commands_add_path_nested_to_created_deleted_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_defer_begin(world); + ecs_entity_t foo = ecs_entity(world, { .name = "p.foo" }); + ecs_delete(world, foo); + ecs_add_path(world, e, p, "foo.bar"); + ecs_defer_end(world); + + test_assert(ecs_is_alive(world, p)); + test_assert(!ecs_is_alive(world, e)); + test_assert(!ecs_is_alive(world, foo)); + + ecs_fini(world); +} + +void Commands_add_path_w_stage(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_world_t *s = ecs_get_stage(world, 0); + + ecs_defer_begin(world); + ecs_add_path(s, e, p, "foo"); + ecs_defer_end(world); + + ecs_entity_t foo = ecs_lookup(world, "p.foo"); + test_assert(foo == e); + + char *path = ecs_get_path(world, e); + test_assert(path != NULL); + test_str(path, "p.foo"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Commands_add_path_to_deleted_parent_w_stage(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_world_t *s = ecs_get_stage(world, 0); + + ecs_defer_begin(world); + ecs_delete(world, p); + ecs_add_path(s, e, p, "foo"); + ecs_defer_end(world); + + ecs_entity_t foo = ecs_lookup(world, "p.foo"); + test_assert(foo == 0); + + test_assert(!ecs_is_alive(world, p)); + test_assert(!ecs_is_alive(world, e)); + + ecs_fini(world); +} + +void Commands_add_path_nested_w_stage(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_world_t *s = ecs_get_stage(world, 0); + + ecs_defer_begin(world); + ecs_add_path(s, e, p, "foo.bar"); + ecs_defer_end(world); + + ecs_entity_t foo = ecs_lookup(world, "p.foo"); + test_assert(foo != 0); + + char *path = ecs_get_path(world, e); + test_assert(path != NULL); + test_str(path, "p.foo.bar"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Commands_add_path_nested_to_deleted_parent_w_stage(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_world_t *s = ecs_get_stage(world, 0); + + ecs_defer_begin(world); + ecs_delete(world, p); + ecs_add_path(s, e, p, "foo.bar"); + ecs_defer_end(world); + + ecs_entity_t foo = ecs_lookup(world, "p.foo"); + test_assert(foo == 0); + + test_assert(!ecs_is_alive(world, p)); + test_assert(!ecs_is_alive(world, e)); + + ecs_fini(world); +} + +void Commands_add_path_nested_to_created_deleted_parent_w_stage(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_world_t *s = ecs_get_stage(world, 0); + + ecs_defer_begin(world); + ecs_entity_t foo = ecs_entity(s, { .name = "p.foo" }); + ecs_delete(s, foo); + ecs_add_path(s, e, p, "foo.bar"); + ecs_defer_end(world); + + test_assert(ecs_is_alive(world, p)); + test_assert(!ecs_is_alive(world, e)); + test_assert(!ecs_is_alive(world, foo)); + + ecs_fini(world); +} + +void Commands_defer_emplace_w_arg(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + p->x = 1; p->y = 2; + test_bool(is_new, true); + } + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + p->x = 10; p->y = 20; + test_bool(is_new, true); /* Might change after cmd queue temp storage refactor */ + } + test_assert(!ecs_has(world, e, Position)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Commands_defer_emplace_existing_w_arg(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new_w(world, Position); + + ecs_defer_begin(world); + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + p->x = 1; p->y = 2; + test_bool(is_new, false); + } + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + p->x = 10; p->y = 20; + test_bool(is_new, false); /* Might change after cmd queue temp storage refactor */ + } + test_assert(ecs_has(world, e, Position)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +static int mixed_observer_invoked = 0; +static int mixed_observer_on_add_invoked = 0; +static int mixed_observer_on_set_invoked = 0; + +static void MixedObserver(ecs_iter_t *it) { + test_int(it->count, 1); + + Position *p = ecs_field(it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + mixed_observer_invoked ++; + + if (it->event == EcsOnAdd) { + mixed_observer_on_add_invoked ++; + } else if (it->event == EcsOnSet) { + mixed_observer_on_set_invoked ++; + } +} + +void Commands_mixed_on_add_on_set_w_set_w_batching(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }, { Foo }}, + .callback = MixedObserver, + .events = { EcsOnSet } + }); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + ecs_add(world, e, Foo); + ecs_set(world, e, Position, {10, 20}); + ecs_defer_end(world); + + test_int(mixed_observer_invoked, 2); + test_int(mixed_observer_on_add_invoked, 1); + test_int(mixed_observer_on_set_invoked, 1); + + ecs_fini(world); +} + +void Commands_mixed_on_add_on_set_w_emplace(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }, { Foo }}, + .callback = MixedObserver, + .events = { EcsOnSet } + }); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + ecs_add(world, e, Foo); + Position *p = ecs_emplace(world, e, Position, NULL); + p->x = 10; + p->y = 20; + ecs_modified(world, e, Position); + ecs_defer_end(world); + + test_int(mixed_observer_invoked, 1); + test_int(mixed_observer_on_add_invoked, 0); /* Entity didn't have component yet */ + test_int(mixed_observer_on_set_invoked, 1); + + ecs_fini(world); +} + +void Commands_add_isa_set_w_override_batched(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t p = ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_defer_begin(world); + ecs_entity_t i = ecs_new(world); + ecs_add_pair(world, i, EcsIsA, p); + ecs_set(world, i, Position, {20, 30}); + test_assert(!ecs_has_pair(world, i, EcsIsA, p)); + test_assert(!ecs_has(world, i, Position)); + ecs_defer_end(world); + test_assert(ecs_has_pair(world, i, EcsIsA, p)); + test_assert(ecs_has(world, i, Position)); + + const Position *ptr = ecs_get(world, i, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 20); + test_int(ptr->y, 30); + + ecs_fini(world); +} + +void Commands_add_set_isa_w_override_batched(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t p = ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_defer_begin(world); + ecs_entity_t i = ecs_new(world); + ecs_set(world, i, Position, {20, 30}); + ecs_add_pair(world, i, EcsIsA, p); + test_assert(!ecs_has_pair(world, i, EcsIsA, p)); + test_assert(!ecs_has(world, i, Position)); + ecs_defer_end(world); + test_assert(ecs_has_pair(world, i, EcsIsA, p)); + test_assert(ecs_has(world, i, Position)); + + const Position *ptr = ecs_get(world, i, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 20); + test_int(ptr->y, 30); + + ecs_fini(world); +} + +void Commands_add_batched_set_with(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsWith, ecs_id(Velocity)); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + ecs_add(world, e, Foo); + ecs_set(world, e, Position, {10, 20}); + ecs_defer_end(world); + + test_assert(ecs_has(world, e, Foo)); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Commands_defer_emplace_after_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {0, 0})); + + ecs_defer_begin(world); + ecs_remove(world, e, Position); + { + Position *p = ecs_emplace(world, e, Position, NULL); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + ecs_defer_end(world); + + test_assert(ecs_has(world, e, Position)); + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +static +void RemoveVelocity(ecs_iter_t *it) { + test_int(it->count, 1); + ecs_remove(it->world, it->entities[0], Velocity); +} + +void Commands_batched_w_table_change_in_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT_DEFINE(world, Position); + ECS_COMPONENT_DEFINE(world, Velocity); + ECS_TAG(world, Foo); + + ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }}, + .events = { EcsOnRemove }, + .callback = RemoveVelocity + }); + + ecs_observer(world, { + .query.terms = {{ Foo }}, + .events = { EcsOnAdd }, + .callback = DummyObserver + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_add(world, e1, Velocity); + + ecs_defer_begin(world); + ecs_remove(world, e1, Position); + ecs_add(world, e1, Foo); + ecs_defer_end(world); + + test_assert(ecs_has(world, e1, Foo)); + test_assert(!ecs_has(world, e1, Position)); + test_assert(!ecs_has(world, e1, Velocity)); + + ecs_fini(world); +} + +void Commands_redefine_named_in_threaded_app(void) { + ecs_world_t *world = ecs_init(); + + ecs_set_threads(world, 2); + + ecs_defer_begin(world); + ecs_entity_t c1 = ecs_entity(world, { .name = "parent.child_1" }); + ecs_entity_t c2 = ecs_entity(world, { .name = "parent.child_2" }); + ecs_defer_end(world); + + ecs_entity_t p1 = ecs_get_target(world, c1, EcsChildOf, 0); + test_assert(p1 != 0); + ecs_entity_t p2 = ecs_get_target(world, c2, EcsChildOf, 0); + test_assert(p2 != 0); + test_assert(p1 == p2); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/ComponentLifecycle.c b/vendors/flecs/test/core/src/ComponentLifecycle.c similarity index 73% rename from vendors/flecs/test/api/src/ComponentLifecycle.c rename to vendors/flecs/test/core/src/ComponentLifecycle.c index d18b75001..21e7f6279 100644 --- a/vendors/flecs/test/api/src/ComponentLifecycle.c +++ b/vendors/flecs/test/core/src/ComponentLifecycle.c @@ -1,4 +1,4 @@ -#include +#include static ECS_COMPONENT_DECLARE(Position); static ECS_COMPONENT_DECLARE(String); @@ -27,6 +27,9 @@ typedef struct cl_ctx { xtor_ctx dtor; copy_ctx copy; copy_ctx move; + copy_ctx ctor_move_dtor; + copy_ctx move_dtor; + } cl_ctx; static @@ -40,7 +43,7 @@ void comp_ctor( data->ctor.size = info->size; data->ctor.count += count; data->ctor.invoked ++; - + Position *p = ptr; int i; for (i = 0; i < count; i ++) { @@ -74,7 +77,7 @@ void comp_copy( data->copy.size = info->size; data->copy.count += count; data->copy.invoked ++; - + memcpy(dst_ptr, src_ptr, info->size * count); } @@ -90,7 +93,43 @@ void comp_move( data->move.size = info->size; data->move.count = count; data->move.invoked ++; - + + memcpy(dst_ptr, src_ptr, info->size * count); +} + +static +void comp_move_dtor( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *info) +{ + cl_ctx *data = info->hooks.ctx; + data->move_dtor.component = info->component; + data->move_dtor.size = info->size; + data->move_dtor.count += count; + data->move_dtor.invoked ++; + + memcpy(dst_ptr, src_ptr, info->size * count); +} + +static +void comp_pos_ctor_move_dtor(void *dst_ptr, void *src_ptr, + int32_t count, const ecs_type_info_t *info) +{ + cl_ctx *data = info->hooks.ctx; + data->ctor_move_dtor.component = info->component; + data->ctor_move_dtor.size = info->size; + data->ctor_move_dtor.count += count; + data->ctor_move_dtor.invoked ++; + + Position *p = src_ptr; + int i; + for (i = 0; i < count; i ++) { + p[i].x = 10; + p[i].y = 20; + } + memcpy(dst_ptr, src_ptr, info->size * count); } @@ -128,7 +167,7 @@ static void ecs_on_add(Position)(ecs_iter_t *it) { test_assert(it->count >= 1); test_assert(it->event == EcsOnAdd); - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i ++) { on_add_position ++; test_int(p[i].x, 0); @@ -136,7 +175,7 @@ static void ecs_on_add(Position)(ecs_iter_t *it) { } } -static void on_add_position_emplace(ecs_iter_t *it) { +static void on_add_position_count(ecs_iter_t *it) { on_add_position += it->count; } @@ -230,7 +269,7 @@ void ComponentLifecycle_ctor_on_add(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.ctor.invoked, 0); ecs_add(world, e, Position); @@ -254,7 +293,7 @@ void ComponentLifecycle_ctor_on_new(void) { .ctx = &ctx }); - ecs_new(world, Position); + ecs_new_w(world, Position); test_assert(ctx.ctor.invoked != 0); test_int(ctx.ctor.component, ecs_id(Position)); test_int(ctx.ctor.size, sizeof(Position)); @@ -275,7 +314,7 @@ void ComponentLifecycle_dtor_on_remove(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.dtor.invoked, 0); ecs_remove(world, e, Position); @@ -283,7 +322,7 @@ void ComponentLifecycle_dtor_on_remove(void) { test_int(ctx.dtor.component, ecs_id(Position)); test_int(ctx.dtor.size, sizeof(Position)); test_int(ctx.dtor.count, 1); - + ecs_fini(world); } @@ -299,7 +338,7 @@ void ComponentLifecycle_dtor_on_delete(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.dtor.invoked, 0); ecs_delete(world, e); @@ -323,9 +362,9 @@ void ComponentLifecycle_copy_on_set(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.copy.invoked, 0); - + ecs_set(world, e, Position, {0, 0}); test_assert(ctx.copy.invoked != 0); test_int(ctx.copy.component, ecs_id(Position)); @@ -340,6 +379,8 @@ void ComponentLifecycle_copy_on_override(void) { ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + cl_ctx ctx = { { 0 } }; ecs_set_hooks(world, Position, { @@ -347,7 +388,7 @@ void ComponentLifecycle_copy_on_override(void) { .ctx = &ctx }); - ecs_entity_t base = ecs_new(world, Position); + ecs_entity_t base = ecs_new_w(world, Position); test_int(ctx.copy.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -374,7 +415,7 @@ void ComponentLifecycle_copy_on_clone(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(ctx.copy.invoked != 0); memset(&ctx, 0, sizeof(ctx)); @@ -400,7 +441,7 @@ void ComponentLifecycle_no_copy_on_move(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(ctx.copy.invoked != 0); memset(&ctx, 0, sizeof(ctx)); @@ -410,164 +451,6 @@ void ComponentLifecycle_no_copy_on_move(void) { ecs_fini(world); } -void ComponentLifecycle_copy_on_snapshot(void) { - test_quarantine("13 March 2022"); - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - cl_ctx ctx = { { 0 } }; - ecs_set_hooks(world, Position, { - .copy = comp_copy, - .ctx = &ctx - }); - - const ecs_entity_t *ids = ecs_bulk_new(world, Position, 10); - test_assert(ids != NULL); - - int32_t i; - for (i = 0; i < 10; i ++) { - test_assert(ecs_has(world, ids[i], Position)); - ecs_set(world, ids[i], Position, {i, i * 2}); - } - - test_assert(ctx.copy.invoked != 0); - test_int(ctx.copy.component, ecs_id(Position)); - test_int(ctx.copy.size, sizeof(Position)); - test_int(ctx.copy.count, 10); - - ctx = (cl_ctx){ { 0 } }; - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - test_assert(ctx.copy.invoked != 0); - test_int(ctx.copy.component, ecs_id(Position)); - test_int(ctx.copy.size, sizeof(Position)); - test_int(ctx.copy.count, 10); - - ecs_snapshot_restore(world, s); - ecs_snapshot_free(s); - - for (i = 0; i < 10; i ++) { - test_assert(ecs_has(world, ids[i], Position)); - const Position *p = ecs_get(world, ids[i], Position); - test_assert(p != NULL); - test_int(p->x, i); - test_int(p->y, i * 2); - } - - ecs_fini(world); -} - -void ComponentLifecycle_ctor_copy_on_snapshot(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - cl_ctx ctx = { { 0 } }; - ecs_set_hooks(world, Position, { - .ctor = comp_ctor, - .copy = comp_copy, - .ctx = &ctx - }); - - const ecs_entity_t *ids = ecs_bulk_new(world, Position, 10); - test_assert(ids != NULL); - - int32_t i; - for (i = 0; i < 10; i ++) { - test_assert(ecs_has(world, ids[i], Position)); - ecs_set(world, ids[i], Position, {i, i * 2}); - } - - test_assert(ctx.copy.invoked != 0); - test_int(ctx.copy.component, ecs_id(Position)); - test_int(ctx.copy.size, sizeof(Position)); - test_int(ctx.copy.count, 10); - - ctx = (cl_ctx){ { 0 } }; - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - test_assert(ctx.ctor.invoked != 0); - test_int(ctx.ctor.component, ecs_id(Position)); - test_int(ctx.ctor.size, sizeof(Position)); - test_int(ctx.ctor.count, 10); - - test_assert(ctx.copy.invoked != 0); - test_int(ctx.copy.component, ecs_id(Position)); - test_int(ctx.copy.size, sizeof(Position)); - test_int(ctx.copy.count, 10); - - ecs_snapshot_restore(world, s); - - for (i = 0; i < 10; i ++) { - test_assert(ecs_has(world, ids[i], Position)); - const Position *p = ecs_get(world, ids[i], Position); - test_assert(p != NULL); - test_int(p->x, i); - test_int(p->y, i * 2); - } - - ecs_fini(world); -} - -void ComponentLifecycle_dtor_on_restore(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - cl_ctx ctx = { { 0 } }; - ecs_set_hooks(world, Position, { - .dtor = comp_dtor, - .ctx = &ctx - }); - - const ecs_entity_t *temp_ids = ecs_bulk_new(world, Position, 10); - test_assert(temp_ids != NULL); - ecs_entity_t ids[10]; - memcpy(ids, temp_ids, sizeof(ecs_entity_t) * 10); - - int32_t i; - for (i = 0; i < 10; i ++) { - test_assert(ecs_has(world, ids[i], Position)); - ecs_set(world, ids[i], Position, {i, i * 2}); - } - - test_int(ctx.dtor.invoked, 0); - - ctx = (cl_ctx){ { 0 } }; - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - test_int(ctx.dtor.invoked, 0); - - /* Delete one entity, so we have more confidence we're destructing the right - * entities */ - ecs_delete(world, ids[0]); - - test_assert(ctx.dtor.invoked != 0); - ctx = (cl_ctx){ { 0 } }; - - ecs_snapshot_restore(world, s); - - test_assert(ctx.dtor.invoked != 0); - test_int(ctx.dtor.component, ecs_id(Position)); - test_int(ctx.dtor.size, sizeof(Position)); - test_int(ctx.dtor.count, 9); - - for (i = 0; i < 10; i ++) { - test_assert(ecs_has(world, ids[i], Position)); - const Position *p = ecs_get(world, ids[i], Position); - test_assert(p != NULL); - - test_int(p->x, i); - test_int(p->y, i * 2); - } - - ecs_fini(world); -} - void ComponentLifecycle_ctor_on_tag(void) { install_test_abort(); @@ -634,7 +517,7 @@ void ComponentLifecycle_move_on_tag(void) { void ComponentLifecycle_merge_to_different_table(void) { ecs_world_t *world = ecs_mini(); - + ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); @@ -652,7 +535,7 @@ void ComponentLifecycle_merge_to_different_table(void) { .dtor = ecs_dtor(Velocity), .copy = ecs_copy(Velocity), .move = ecs_move(Velocity) - }); + }); ecs_set_hooks(world, Mass, { .ctor = ecs_ctor(Mass), @@ -762,7 +645,7 @@ void ComponentLifecycle_merge_to_new_table(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_set_hooks(world, Position, { .ctor = ecs_ctor(Position), @@ -785,7 +668,7 @@ void ComponentLifecycle_merge_to_new_table(void) { void ComponentLifecycle_delete_in_stage(void) { ecs_world_t *world = ecs_mini(); - + ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); @@ -802,7 +685,7 @@ void ComponentLifecycle_delete_in_stage(void) { .dtor = ecs_dtor(Velocity), .copy = ecs_copy(Velocity), .move = ecs_move(Velocity) - }); + }); ecs_set_hooks(world, Mass, { .ctor = ecs_ctor(Mass), @@ -858,7 +741,7 @@ void ComponentLifecycle_delete_in_stage(void) { test_int(ctor_mass, 0); test_int(dtor_mass, 1); test_int(copy_mass, 0); - test_int(move_mass, 0); + test_int(move_mass, 0); ecs_fini(world); } @@ -881,7 +764,7 @@ void ComponentLifecycle_ctor_on_add_pair(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.ctor.invoked, 0); ecs_add_pair(world, e, ecs_id(Pair), ecs_id(Position)); @@ -906,7 +789,7 @@ void ComponentLifecycle_ctor_on_add_pair_tag(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.ctor.invoked, 0); ecs_add_pair(world, e, Pair, ecs_id(Position)); @@ -933,7 +816,7 @@ void ComponentLifecycle_ctor_on_move_pair(void) { }); /* Create entity in existing table */ - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.ctor.invoked, 0); /* Add pair to existing table */ @@ -960,7 +843,7 @@ void ComponentLifecycle_move_on_realloc(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.ctor.invoked, 0); ecs_add(world, e, Position); @@ -973,8 +856,8 @@ void ComponentLifecycle_move_on_realloc(void) { ctx = (cl_ctx){ { 0 } }; /* Trigger realloc & move */ - ecs_new(world, Position); - ecs_new(world, Position); + ecs_new_w(world, Position); + ecs_new_w(world, Position); test_assert(ctx.ctor.invoked != 0); test_assert(ctx.move.invoked != 0); @@ -994,7 +877,7 @@ void ComponentLifecycle_move_on_bulk_new(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.ctor.invoked, 0); ecs_add(world, e, Position); @@ -1043,8 +926,8 @@ void ComponentLifecycle_move_on_delete(void) { .ctx = &ctx }); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_new_w(world, Position); test_assert(ctx.ctor.invoked != 0); ctx = (cl_ctx){ { 0 } }; @@ -1073,8 +956,8 @@ void ComponentLifecycle_move_dtor_on_delete(void) { .ctx = &ctx }); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_new_w(world, Position); ctx = (cl_ctx){ { 0 } }; @@ -1100,6 +983,8 @@ void ComponentLifecycle_copy_on_override_pair(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Pair); + ecs_add_pair(world, ecs_id(Pair), EcsOnInstantiate, EcsInherit); + cl_ctx ctx = { { 0 } }; ecs_set_hooks(world, Pair, { @@ -1108,7 +993,7 @@ void ComponentLifecycle_copy_on_override_pair(void) { .ctx = &ctx }); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); ecs_add_pair(world, base, ecs_id(Pair), ecs_id(Position)); test_assert(ctx.ctor.invoked != 0); test_int(ctx.copy.invoked, 0); @@ -1137,6 +1022,8 @@ void ComponentLifecycle_copy_on_override_pair_tag(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Pair); + ecs_add_pair(world, Pair, EcsOnInstantiate, EcsInherit); + cl_ctx ctx = { { 0 } }; ecs_set_hooks(world, Position, { @@ -1145,7 +1032,7 @@ void ComponentLifecycle_copy_on_override_pair_tag(void) { .ctx = &ctx }); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); ecs_add_pair(world, base, Pair, ecs_id(Position)); test_assert(ctx.ctor.invoked != 0); test_int(ctx.copy.invoked, 0); @@ -1181,9 +1068,9 @@ void ComponentLifecycle_copy_on_set_pair(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.copy.invoked, 0); - + ecs_set_pair(world, e, Pair, ecs_id(Position), {0, 0}); test_assert(ctx.copy.invoked != 0); test_int(ctx.copy.component, ecs_id(Pair)); @@ -1206,10 +1093,10 @@ void ComponentLifecycle_copy_on_set_pair_tag(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.copy.invoked, 0); - ecs_set_pair_object(world, e, Pair, Position, {0, 0}); + ecs_set_pair_second(world, e, Pair, Position, {0, 0}); test_assert(ctx.copy.invoked != 0); test_int(ctx.copy.component, ecs_id(Position)); test_int(ctx.copy.size, sizeof(Position)); @@ -1232,11 +1119,11 @@ void ComponentLifecycle_allow_lifecycle_overwrite_equal_callbacks(void) { .ctor = ecs_ctor(Position) }); - ecs_new(world, Position); + ecs_new_w(world, Position); test_int(ctor_position, 1); - ecs_fini(world); + ecs_fini(world); } static @@ -1252,11 +1139,11 @@ void ComponentLifecycle_set_lifecycle_after_trigger(void) { .ctor = ecs_ctor(Position) }); - ecs_new(world, Position); + ecs_new_w(world, Position); test_int(ctor_position, 1); - ecs_fini(world); + ecs_fini(world); } static int dummy_dtor_invoked = 0; @@ -1292,7 +1179,7 @@ void ComponentLifecycle_valid_entity_in_dtor_after_delete(void) { .dtor = dummy_dtor }); - ecs_entity_t e = ecs_new(world, Dummy); + ecs_entity_t e = ecs_new_w(world, Dummy); test_assert(e != 0); ecs_set(world, e, Dummy, {world, e, 0}); @@ -1302,7 +1189,7 @@ void ComponentLifecycle_valid_entity_in_dtor_after_delete(void) { test_int(dummy_dtor_invoked, 1); - ecs_fini(world); + ecs_fini(world); } void ComponentLifecycle_ctor_w_emplace(void) { @@ -1317,14 +1204,14 @@ void ComponentLifecycle_ctor_w_emplace(void) { .ctx = &ctx }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - const Position *ptr = ecs_emplace(world, e, Position); + const Position *ptr = ecs_emplace(world, e, Position, NULL); test_assert(ptr != NULL); test_int(ctx.ctor.invoked, 0); - ecs_fini(world); + ecs_fini(world); } void ComponentLifecycle_ctor_w_emplace_defer(void) { @@ -1338,11 +1225,11 @@ void ComponentLifecycle_ctor_w_emplace_defer(void) { test_int(ctor_position, 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_defer_begin(world); - Position *ptr = ecs_emplace(world, e, Position); + Position *ptr = ecs_emplace(world, e, Position, NULL); test_assert(ptr != NULL); test_int(ctor_position, 0); ptr->x = 10; @@ -1358,7 +1245,7 @@ void ComponentLifecycle_ctor_w_emplace_defer(void) { test_int(p->x, 10); test_int(p->y, 20); - ecs_fini(world); + ecs_fini(world); } void ComponentLifecycle_on_add_w_emplace(void) { @@ -1367,18 +1254,18 @@ void ComponentLifecycle_on_add_w_emplace(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .on_add = on_add_position_emplace + .on_add = on_add_position_count }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(on_add_position, 0); - const Position *ptr = ecs_emplace(world, e, Position); + const Position *ptr = ecs_emplace(world, e, Position, NULL); test_assert(ptr != NULL); test_int(on_add_position, 1); - ecs_fini(world); + ecs_fini(world); } void ComponentLifecycle_on_add_w_emplace_existing(void) { @@ -1389,20 +1276,20 @@ void ComponentLifecycle_on_add_w_emplace_existing(void) { ecs_set_hooks(world, Position, { .ctor = ecs_ctor(Position), - .on_add = on_add_position_emplace + .on_add = on_add_position_count }); - ecs_entity_t e = ecs_new(world, Velocity); + ecs_entity_t e = ecs_new_w(world, Velocity); test_assert(e != 0); test_int(ctor_position, 0); test_int(on_add_position, 0); - const Position *ptr = ecs_emplace(world, e, Position); + const Position *ptr = ecs_emplace(world, e, Position, NULL); test_assert(ptr != NULL); test_int(ctor_position, 0); test_int(on_add_position, 1); - ecs_fini(world); + ecs_fini(world); } void ComponentLifecycle_on_add_w_emplace_defer(void) { @@ -1411,22 +1298,22 @@ void ComponentLifecycle_on_add_w_emplace_defer(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .on_add = on_add_position_emplace + .on_add = on_add_position_count }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_defer_begin(world); test_int(on_add_position, 0); - const Position *ptr = ecs_emplace(world, e, Position); + const Position *ptr = ecs_emplace(world, e, Position, NULL); test_assert(ptr != NULL); test_int(on_add_position, 0); ecs_defer_end(world); test_int(on_add_position, 1); - ecs_fini(world); + ecs_fini(world); } static int move_ctor_position = 0; @@ -1441,6 +1328,18 @@ void position_move_ctor( move_ctor_position ++; } +static int move_dtor_position = 0; +static +void position_move_dtor( + void *dst, + void *src, + int32_t count, + const ecs_type_info_t *info) +{ + *((Position*)dst) = *((Position*)src); + move_dtor_position ++; +} + void ComponentLifecycle_ctor_w_emplace_defer_use_move_ctor(void) { ecs_world_t *world = ecs_mini(); @@ -1452,12 +1351,12 @@ void ComponentLifecycle_ctor_w_emplace_defer_use_move_ctor(void) { .move_ctor = position_move_ctor }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_defer_begin(world); test_int(on_add_position, 0); - Position *ptr = ecs_emplace(world, e, Position); + Position *ptr = ecs_emplace(world, e, Position, NULL); ptr->x = 10; ptr->y = 20; test_assert(ptr != NULL); @@ -1474,9 +1373,116 @@ void ComponentLifecycle_ctor_w_emplace_defer_use_move_ctor(void) { test_int(p->x, 10); test_int(p->y, 20); + ecs_fini(world); +} + +void ComponentLifecycle_ctor_w_emplace_defer_twice(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .move = ecs_move(Position), + .move_ctor = position_move_ctor, + .move_dtor = position_move_dtor + }); + + ecs_entity_t e = ecs_new(world); + test_assert(e != 0); + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *ptr = ecs_emplace(world, e, Position, &is_new); + test_bool(is_new, true); + test_assert(ptr != NULL); + ptr->x = 10; + ptr->y = 20; + } + { + bool is_new = false; + Position *ptr = ecs_emplace(world, e, Position, &is_new); + test_bool(is_new, true); + test_assert(ptr != NULL); + ptr->x = 20; + ptr->y = 30; + } + + test_int(ctor_position, 0); + test_int(move_position, 0); + test_int(move_ctor_position, 0); + test_int(move_dtor_position, 0); + ecs_defer_end(world); + + test_int(ctor_position, 0); + test_int(move_position, 0); + test_int(move_ctor_position, 1); + test_int(move_dtor_position, 1); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 20); + test_int(p->y, 30); + ecs_fini(world); } +void ComponentLifecycle_ctor_w_emplace_defer_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .move = ecs_move(Position), + .move_ctor = position_move_ctor, + .move_dtor = position_move_dtor + }); + + ecs_entity_t e = ecs_new_w(world, Position); + test_assert(e != 0); + + test_int(ctor_position, 1); + ctor_position = 0; + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *ptr = ecs_emplace(world, e, Position, &is_new); + test_bool(is_new, false); + test_assert(ptr != NULL); + ptr->x = 10; + ptr->y = 20; + } + { + bool is_new = false; + Position *ptr = ecs_emplace(world, e, Position, &is_new); + test_bool(is_new, false); + test_assert(ptr != NULL); + ptr->x = 20; + ptr->y = 30; + } + + test_int(ctor_position, 0); + test_int(move_position, 0); + test_int(move_ctor_position, 0); + test_int(move_dtor_position, 0); + ecs_defer_end(world); + + test_int(ctor_position, 0); + test_int(move_position, 0); + test_int(move_ctor_position, 0); + test_int(move_dtor_position, 0); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 20); + test_int(p->y, 30); + + ecs_fini(world); +} + void ComponentLifecycle_merge_async_stage_w_emplace(void) { ecs_world_t *world = ecs_mini(); @@ -1489,11 +1495,11 @@ void ComponentLifecycle_merge_async_stage_w_emplace(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); - Position *p = ecs_emplace(async, e, Position); + Position *p = ecs_emplace(async, e, Position, NULL); test_assert(!ecs_has(world, e, Position)); test_int(ctor_position, 0); test_int(copy_position, 0); @@ -1515,7 +1521,7 @@ void ComponentLifecycle_merge_async_stage_w_emplace(void) { test_int(ptr->x, 10); test_int(ptr->y, 20); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -1530,11 +1536,11 @@ void ComponentLifecycle_merge_async_stage_w_emplace_to_deferred_world(void) { .copy = ecs_copy(Position) }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); - Position *p = ecs_emplace(async, e, Position); + Position *p = ecs_emplace(async, e, Position, NULL); test_assert(!ecs_has(world, e, Position)); test_int(ctor_position, 0); p->x = 10; @@ -1553,7 +1559,7 @@ void ComponentLifecycle_merge_async_stage_w_emplace_to_deferred_world(void) { test_int(ptr->x, 10); test_int(ptr->y, 20); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -1577,22 +1583,22 @@ void ComponentLifecycle_emplace_grow_w_existing_component(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t e1 = ecs_new(world, Velocity); - ecs_entity_t e2 = ecs_new(world, Velocity); - ecs_entity_t e3 = ecs_new(world, Velocity); + ecs_entity_t e1 = ecs_new_w(world, Velocity); + ecs_entity_t e2 = ecs_new_w(world, Velocity); + ecs_entity_t e3 = ecs_new_w(world, Velocity); { - Position *p = ecs_emplace(world, e1, Position); + Position *p = ecs_emplace(world, e1, Position, NULL); p->x = 10; p->y = 20; } { - Position *p = ecs_emplace(world, e2, Position); + Position *p = ecs_emplace(world, e2, Position, NULL); p->x = 30; p->y = 40; } { - Position *p = ecs_emplace(world, e3, Position); + Position *p = ecs_emplace(world, e3, Position, NULL); p->x = 50; p->y = 60; } @@ -1632,14 +1638,14 @@ void ComponentLifecycle_dtor_on_fini(void) { .dtor = dummy_dtor }); - ecs_entity_t e = ecs_new(world, Dummy); + ecs_entity_t e = ecs_new_w(world, Dummy); test_assert(e != 0); ecs_set(world, e, Dummy, {world, e}); test_int(dummy_dtor_invoked, 0); - ecs_fini(world); + ecs_fini(world); test_int(dummy_dtor_invoked, 1); } @@ -1699,7 +1705,7 @@ void other_type_dtor( test_assert(ecs_is_valid(world, e)); test_assert(comp->other != 0); - + if (ecs_is_valid(world, comp->other)) { const ecs_type_t *type = ecs_get_type(world, comp->other); test_assert(type != NULL); @@ -1710,39 +1716,6 @@ void other_type_dtor( other_dtor_invoked ++; } -static -void other_comp_dtor( - void *ptr, - int32_t count, - const ecs_type_info_t *info) -{ - test_int(count, 1); - test_assert(ptr != NULL); - - Entity *comp = ptr; - - ecs_world_t *world = comp->world; - ecs_entity_t e = comp->e; - test_assert(e != 0); - test_assert(ecs_is_valid(world, e)); - - test_assert(comp->other != 0); - - if (ecs_is_valid(world, comp->other)) { - if (ecs_has(world, comp->other, String)) { - const String *str_ptr = ecs_get(world, comp->other, String); - test_assert(str_ptr != NULL); - test_assert(str_ptr->str != NULL); - test_str(str_ptr->str, "Foo"); - other_dtor_valid_entity ++; - } else { - test_assert(ecs_get(world, comp->other, String) == NULL); - } - } - - other_dtor_invoked ++; -} - ECS_MOVE(Entity, dst, src, { dst->world = src->world; dst->e = src->e; @@ -1770,7 +1743,7 @@ void other_delete_dtor( test_assert(ecs_is_valid(world, e)); test_assert(comp->other != 0); - + if (ecs_is_valid(world, comp->other)) { ecs_delete(world, comp->other); other_dtor_valid_entity ++; @@ -1797,7 +1770,7 @@ void self_delete_dtor( ecs_entity_t e = d->e; test_assert(world != 0); test_assert(e != 0); - + if (ecs_is_valid(world, e)) { ecs_delete(world, e); @@ -1838,7 +1811,7 @@ void ComponentLifecycle_valid_type_in_dtor_on_fini(void) { .dtor = type_dtor }); - ecs_entity_t e = ecs_new(world, Dummy); + ecs_entity_t e = ecs_new_w(world, Dummy); test_assert(e != 0); ecs_set(world, e, Dummy, {world, e}); ecs_add(world, e, Position); @@ -1846,7 +1819,7 @@ void ComponentLifecycle_valid_type_in_dtor_on_fini(void) { test_int(dummy_dtor_invoked, 0); - ecs_fini(world); + ecs_fini(world); test_int(dummy_dtor_invoked, 1); } @@ -1862,8 +1835,8 @@ void ComponentLifecycle_valid_other_type_of_entity_in_dtor_on_fini(void) { .dtor = other_type_dtor }); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new_w(world, Position); ecs_set(world, e2, Entity, {world, e2, e1}); ecs_add(world, e1, Velocity); @@ -1893,8 +1866,8 @@ void ComponentLifecycle_delete_in_dtor_other_type_on_fini(void) { .dtor = other_delete_dtor }); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Velocity); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Velocity); ecs_set(world, e2, Entity, {world, e2, e1}); ecs_set(world, e1, Entity, {world, e1, e2}); @@ -1926,10 +1899,10 @@ void ComponentLifecycle_delete_in_dtor_other_type_on_delete_parent(void) { .dtor = other_delete_dtor }); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Velocity); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Velocity); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); @@ -1948,7 +1921,7 @@ void ComponentLifecycle_delete_in_dtor_other_type_on_delete_parent(void) { test_int(other_dtor_valid_entity, 1); test_assert(!ecs_is_alive(world, e1)); - test_assert(!ecs_is_alive(world, e2)); + test_assert(!ecs_is_alive(world, e2)); ecs_fini(world); } @@ -1969,8 +1942,8 @@ void ComponentLifecycle_delete_in_dtor_other_type_on_delete(void) { .dtor = other_delete_dtor }); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Velocity); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Velocity); ecs_set(world, e2, Entity, {world, e2, e1}); ecs_set(world, e1, Entity, {world, e1, e2}); @@ -1998,9 +1971,9 @@ void ComponentLifecycle_delete_self_in_dtor_on_delete(void) { .dtor = self_delete_dtor }); - ecs_entity_t e1 = ecs_new(world, Dummy); - ecs_entity_t e2 = ecs_new(world, Dummy); - ecs_entity_t e3 = ecs_new(world, Dummy); + ecs_entity_t e1 = ecs_new_w(world, Dummy); + ecs_entity_t e2 = ecs_new_w(world, Dummy); + ecs_entity_t e3 = ecs_new_w(world, Dummy); ecs_set(world, e1, Dummy, {world, e1}); ecs_set(world, e2, Dummy, {world, e2}); @@ -2042,7 +2015,7 @@ void ComponentLifecycle_on_set_after_set(void) { .on_set = ecs_on_set(Position) }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctor_position, 1); test_int(position_on_set_invoked, 0); @@ -2068,15 +2041,15 @@ void ComponentLifecycle_on_add_after_new(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .on_add = ecs_on_add(Position) }); - ecs_new(world, Position); + ecs_new_w(world, Position); test_int(on_add_position, 1); - ecs_new(world, Position); + ecs_new_w(world, Position); test_int(on_add_position, 2); - ecs_new(world, Position); + ecs_new_w(world, Position); test_int(on_add_position, 3); ecs_fini(world); @@ -2088,13 +2061,13 @@ void ComponentLifecycle_on_add_after_add(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .on_add = ecs_on_add(Position) }); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); test_int(on_add_position, 1); @@ -2112,15 +2085,15 @@ void ComponentLifecycle_on_add_after_set(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .on_add = ecs_on_add(Position) }); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); test_int(on_add_position, 1); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); test_int(on_add_position, 2); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); test_int(on_add_position, 3); ecs_fini(world); @@ -2132,7 +2105,7 @@ static void ecs_on_remove(Position)(ecs_iter_t *it) { test_assert(it->count >= 1); test_assert(it->event == EcsOnRemove); - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i ++) { on_remove_position ++; test_int(p[i].x, 10); @@ -2149,9 +2122,9 @@ void ComponentLifecycle_on_remove_after_remove(void) { .on_remove = ecs_on_remove(Position) }); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_remove(world, e1, Position); test_int(on_remove_position, 1); @@ -2172,9 +2145,9 @@ void ComponentLifecycle_on_remove_after_clear(void) { .on_remove = ecs_on_remove(Position) }); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_clear(world, e1); test_int(on_remove_position, 1); @@ -2195,9 +2168,9 @@ void ComponentLifecycle_on_remove_after_delete(void) { .on_remove = ecs_on_remove(Position) }); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_delete(world, e1); test_int(on_remove_position, 1); @@ -2223,7 +2196,7 @@ static void on_remove_tag_set_position_pair(ecs_iter_t *it) { for (int i = 0; i < it->count; i ++) { ecs_set_pair(it->world, it->entities[i], - Position, ecs_new_id(it->world), {10, 20}); + Position, ecs_new(it->world), {10, 20}); on_remove_tag_set_position_invoked ++; } } @@ -2231,8 +2204,8 @@ void on_remove_tag_set_position_pair(ecs_iter_t *it) { static void on_remove_tag_set_position_obj_pair(ecs_iter_t *it) { for (int i = 0; i < it->count; i ++) { - ecs_set_pair_object(it->world, it->entities[i], - ecs_new_id(it->world), Position, {10, 20}); + ecs_set_pair_second(it->world, it->entities[i], + ecs_new(it->world), Position, {10, 20}); on_remove_tag_set_position_invoked ++; } } @@ -2244,12 +2217,12 @@ void ComponentLifecycle_free_component_new_id_while_fini(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnRemove}, .callback = on_remove_tag_set_position }); - ecs_new(world, Tag); + ecs_new_w(world, Tag); ecs_fini(world); @@ -2263,17 +2236,17 @@ void ComponentLifecycle_dtor_component_new_id_while_fini(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnRemove}, .callback = on_remove_tag_set_position }); ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(Position) }); - ecs_new(world, Tag); + ecs_new_w(world, Tag); test_int(dtor_position, 0); @@ -2290,12 +2263,12 @@ void ComponentLifecycle_free_component_new_pair_id_while_fini(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnRemove}, .callback = on_remove_tag_set_position_pair }); - ecs_new(world, Tag); + ecs_new_w(world, Tag); ecs_fini(world); @@ -2309,17 +2282,17 @@ void ComponentLifecycle_dtor_component_new_pair_id_while_fini(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnRemove}, .callback = on_remove_tag_set_position_pair }); ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(Position) }); - ecs_new(world, Tag); + ecs_new_w(world, Tag); test_int(dtor_position, 0); @@ -2336,12 +2309,12 @@ void ComponentLifecycle_free_component_new_obj_pair_id_while_fini(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnRemove}, .callback = on_remove_tag_set_position_obj_pair }); - ecs_new(world, Tag); + ecs_new_w(world, Tag); ecs_fini(world); @@ -2355,17 +2328,17 @@ void ComponentLifecycle_dtor_component_new_obj_pair_id_while_fini(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnRemove}, .callback = on_remove_tag_set_position_obj_pair }); ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(Position) }); - ecs_new(world, Tag); + ecs_new_w(world, Tag); test_int(dtor_position, 0); @@ -2390,9 +2363,9 @@ void ComponentLifecycle_ctor_move_dtor_after_resize(void) { .ctx = &ctx }); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); test_int(ctx.ctor.count, 1); @@ -2419,12 +2392,13 @@ void ComponentLifecycle_ctor_move_dtor_after_resize(void) { ecs_fini(world); } +static int component_user_ctx = 0; +static int component_binding_ctx = 0; static int component_lifecycle_ctx = 0; -static int component_lifecycle_binding_ctx = 0; -static void component_lifecycle_ctx_free(void *ctx) { - test_assert(ctx == &component_lifecycle_ctx); - component_lifecycle_ctx ++; +static void component_ctx_free(void *ctx) { + test_assert(ctx == &component_user_ctx); + component_user_ctx ++; } void ComponentLifecycle_ctx_free(void) { @@ -2433,13 +2407,13 @@ void ComponentLifecycle_ctx_free(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .ctx = &component_lifecycle_ctx, - .ctx_free = component_lifecycle_ctx_free + .ctx = &component_user_ctx, + .ctx_free = component_ctx_free }); ecs_fini(world); - test_int(1, component_lifecycle_ctx); + test_int(1, component_user_ctx); } void ComponentLifecycle_binding_ctx_free(void) { @@ -2448,13 +2422,28 @@ void ComponentLifecycle_binding_ctx_free(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .binding_ctx = &component_lifecycle_ctx, - .binding_ctx_free = component_lifecycle_ctx_free + .binding_ctx = &component_user_ctx, + .binding_ctx_free = component_ctx_free }); ecs_fini(world); - test_int(1, component_lifecycle_ctx); + test_int(1, component_user_ctx); +} + +void ComponentLifecycle_lifecycle_ctx_free(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .lifecycle_ctx = &component_user_ctx, + .lifecycle_ctx_free = component_ctx_free + }); + + ecs_fini(world); + + test_int(1, component_user_ctx); } void ComponentLifecycle_ctx_free_after_delete_component(void) { @@ -2463,14 +2452,14 @@ void ComponentLifecycle_ctx_free_after_delete_component(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .ctx = &component_lifecycle_ctx, - .ctx_free = component_lifecycle_ctx_free + .ctx = &component_user_ctx, + .ctx_free = component_ctx_free }); ecs_remove_pair(world, ecs_id(Position), EcsOnDelete, EcsPanic); ecs_delete(world, ecs_id(Position)); - test_int(1, component_lifecycle_ctx); + test_int(1, component_user_ctx); ecs_fini(world); } @@ -2481,23 +2470,42 @@ void ComponentLifecycle_binding_ctx_free_after_delete_component(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .binding_ctx = &component_lifecycle_ctx, - .binding_ctx_free = component_lifecycle_ctx_free + .binding_ctx = &component_user_ctx, + .binding_ctx_free = component_ctx_free }); ecs_remove_pair(world, ecs_id(Position), EcsOnDelete, EcsPanic); ecs_delete(world, ecs_id(Position)); - test_int(1, component_lifecycle_ctx); + test_int(1, component_user_ctx); + + ecs_fini(world); +} + +void ComponentLifecycle_lifecycle_ctx_free_after_delete_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .lifecycle_ctx = &component_user_ctx, + .lifecycle_ctx_free = component_ctx_free + }); + + ecs_remove_pair(world, ecs_id(Position), EcsOnDelete, EcsPanic); + ecs_delete(world, ecs_id(Position)); + + test_int(1, component_user_ctx); ecs_fini(world); } -static void test_lifecycle_ctx(ecs_iter_t *it) { - test_assert(it->ctx == &component_lifecycle_ctx); - test_assert(it->binding_ctx == &component_lifecycle_binding_ctx); +static void test_hook_ctx(ecs_iter_t *it) { + test_assert(it->ctx == &component_user_ctx); + test_assert(it->callback_ctx == &component_binding_ctx); + component_user_ctx ++; + component_binding_ctx ++; component_lifecycle_ctx ++; - component_lifecycle_binding_ctx ++; } void ComponentLifecycle_on_add_ctx(void) { @@ -2506,19 +2514,22 @@ void ComponentLifecycle_on_add_ctx(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .on_add = test_lifecycle_ctx, - .ctx = &component_lifecycle_ctx, - .binding_ctx = &component_lifecycle_binding_ctx + .on_add = test_hook_ctx, + .ctx = &component_user_ctx, + .binding_ctx = &component_binding_ctx, + .lifecycle_ctx = &component_lifecycle_ctx }); - ecs_new(world, Position); + ecs_new_w(world, Position); + test_int(1, component_user_ctx); + test_int(1, component_binding_ctx); test_int(1, component_lifecycle_ctx); - test_int(1, component_lifecycle_binding_ctx); ecs_fini(world); + test_int(1, component_user_ctx); + test_int(1, component_binding_ctx); test_int(1, component_lifecycle_ctx); - test_int(1, component_lifecycle_binding_ctx); } void ComponentLifecycle_on_remove_ctx(void) { @@ -2527,23 +2538,27 @@ void ComponentLifecycle_on_remove_ctx(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .on_remove = test_lifecycle_ctx, - .ctx = &component_lifecycle_ctx, - .binding_ctx = &component_lifecycle_binding_ctx + .on_remove = test_hook_ctx, + .ctx = &component_user_ctx, + .binding_ctx = &component_binding_ctx, + .lifecycle_ctx = &component_lifecycle_ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); + test_int(0, component_user_ctx); + test_int(0, component_binding_ctx); test_int(0, component_lifecycle_ctx); - test_int(0, component_lifecycle_binding_ctx); ecs_remove(world, e, Position); + test_int(1, component_user_ctx); + test_int(1, component_binding_ctx); test_int(1, component_lifecycle_ctx); - test_int(1, component_lifecycle_binding_ctx); ecs_fini(world); + test_int(1, component_user_ctx); + test_int(1, component_binding_ctx); test_int(1, component_lifecycle_ctx); - test_int(1, component_lifecycle_binding_ctx); } void ComponentLifecycle_on_set_ctx(void) { @@ -2552,23 +2567,27 @@ void ComponentLifecycle_on_set_ctx(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .on_set = test_lifecycle_ctx, - .ctx = &component_lifecycle_ctx, - .binding_ctx = &component_lifecycle_binding_ctx + .on_set = test_hook_ctx, + .ctx = &component_user_ctx, + .binding_ctx = &component_binding_ctx, + .lifecycle_ctx = &component_lifecycle_ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); + test_int(0, component_user_ctx); + test_int(0, component_binding_ctx); test_int(0, component_lifecycle_ctx); - test_int(0, component_lifecycle_binding_ctx); ecs_set(world, e, Position, {10, 20}); + test_int(1, component_user_ctx); + test_int(1, component_binding_ctx); test_int(1, component_lifecycle_ctx); - test_int(1, component_lifecycle_binding_ctx); ecs_fini(world); + test_int(1, component_user_ctx); + test_int(1, component_binding_ctx); test_int(1, component_lifecycle_ctx); - test_int(1, component_lifecycle_binding_ctx); } static int test_on_event_invoked = 0; @@ -2591,7 +2610,7 @@ void ComponentLifecycle_on_add_w_existing_component(void) { .ctx = (void*)&EcsOnAdd }); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, Position); test_int(1, test_on_event_invoked); @@ -2611,11 +2630,11 @@ void ComponentLifecycle_on_remove_w_existing_component(void) { .ctx = (void*)&EcsOnRemove }); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, Position); test_int(0, test_on_event_invoked); - + ecs_remove(world, e, Position); test_int(1, test_on_event_invoked); @@ -2659,7 +2678,7 @@ void ComponentLifecycle_component_init_set_hooks(void) { test_int(0, on_add_count); test_int(0, on_remove_count); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, c); test_int(1, on_add_count); @@ -2669,13 +2688,128 @@ void ComponentLifecycle_component_init_set_hooks(void) { test_int(1, on_add_count); test_int(1, on_remove_count); - + ecs_fini(world); test_int(1, on_add_count); test_int(1, on_remove_count); } +void ComponentLifecycle_component_init_name_from_type_info(void) { + ecs_world_t* world = ecs_mini(); + + ecs_entity_t c = ecs_component_init(world, &(ecs_component_desc_t){ + .type = { + .name = "Position", + .size = ECS_SIZEOF(Position), + .alignment = ECS_ALIGNOF(Position), + } + }); + + test_assert(c != 0); + test_assert(c == ecs_lookup(world, "Position")); + + const EcsComponent *ptr = ecs_get(world, c, EcsComponent); + test_assert(ptr != NULL); + test_int(ptr->size, ECS_SIZEOF(Position)); + test_int(ptr->size, ECS_SIZEOF(Velocity)); + + ecs_fini(world); +} + +void ComponentLifecycle_component_init_scoped_name_from_type_info(void) { + ecs_world_t* world = ecs_mini(); + + ecs_entity_t c = ecs_component_init(world, &(ecs_component_desc_t){ + .type = { + .name = "ns.Position", + .size = ECS_SIZEOF(Position), + .alignment = ECS_ALIGNOF(Position), + } + }); + + test_assert(c != 0); + test_assert(c == ecs_lookup(world, "ns.Position")); + + const EcsComponent *ptr = ecs_get(world, c, EcsComponent); + test_assert(ptr != NULL); + test_int(ptr->size, ECS_SIZEOF(Position)); + test_int(ptr->size, ECS_SIZEOF(Velocity)); + + ecs_fini(world); +} + +void ComponentLifecycle_component_init_w_recycled_id(void) { + ecs_world_t *world = ecs_init(); + + { + ecs_entity_t c = ecs_component(world, { + .entity = ecs_new(world), + .type.size = 4, + .type.alignment = 4 + }); + + test_assert(c != 0); + test_assert(ecs_has(world, c, EcsComponent)); + + ecs_delete(world, c); + } + + { + ecs_entity_t c = ecs_component(world, { + .entity = ecs_new(world), + .type.size = 4, + .type.alignment = 4 + }); + + test_assert(c != 0); + test_assert(ecs_has(world, c, EcsComponent)); + + ecs_delete(world, c); + } + + ecs_fini(world); +} + +void ComponentLifecycle_component_init_w_recycled_non_component_id(void) { + ecs_world_t *world = ecs_init(); + + { + ecs_entity_t c = ecs_component(world, { + .entity = ecs_new(world), + .type.size = 4, + .type.alignment = 4 + }); + + test_assert(c != 0); + test_assert(ecs_has(world, c, EcsComponent)); + + ecs_delete(world, c); + } + + { + ecs_entity_t c = ecs_new(world); + test_assert(c != 0); + test_assert(!ecs_has(world, c, EcsComponent)); + ecs_delete(world, c); + } + + { + ecs_entity_t c = ecs_component(world, { + .entity = ecs_new(world), + .type.size = 4, + .type.alignment = 4 + }); + + test_assert(c != 0); + test_assert(ecs_has(world, c, EcsComponent)); + + ecs_delete(world, c); + } + + ecs_fini(world); +} + static int ctor_before_on_add_count = 0; static int on_add_after_ctor_count = 0; @@ -2705,7 +2839,7 @@ void ComponentLifecycle_on_add_after_ctor_w_add(void) { test_int(0, ctor_before_on_add_count); test_int(0, on_add_after_ctor_count); - ecs_new(world, Position); + ecs_new_w(world, Position); test_int(1, ctor_before_on_add_count); test_int(1, on_add_after_ctor_count); @@ -2727,7 +2861,7 @@ void ComponentLifecycle_on_add_after_ctor_w_add_to(void) { test_int(0, ctor_before_on_add_count); test_int(0, on_add_after_ctor_count); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); test_int(0, ctor_before_on_add_count); test_int(0, on_add_after_ctor_count); @@ -2743,10 +2877,10 @@ void ComponentLifecycle_on_add_after_ctor_w_add_to(void) { void ComponentLifecycle_with_before_hooks(void) { ecs_world_t* world = ecs_mini(); - ecs_entity_t pos_id = ecs_new_id(world); + ecs_entity_t pos_id = ecs_new(world); ecs_entity_t tag = ecs_new_w_pair(world, EcsWith, pos_id); - ecs_entity_t ecs_id(Position) = + ecs_entity_t ecs_id(Position) = ecs_component_init(world, &(ecs_component_desc_t){ .entity = pos_id, .type = { @@ -2776,13 +2910,13 @@ void ComponentLifecycle_with_before_hooks(void) { void ComponentLifecycle_with_component_on_add(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t ecs_id(Position) = + ecs_entity_t ecs_id(Position) = ecs_component(world, { .type = { .size = ECS_SIZEOF(Position), .alignment = ECS_ALIGNOF(Position), .hooks = { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .on_add = ecs_on_add(Position) } } @@ -2794,7 +2928,7 @@ void ComponentLifecycle_with_component_on_add(void) { test_int(on_add_position, 0); - ecs_entity_t e = ecs_new(world, Foo); + ecs_entity_t e = ecs_new_w(world, Foo); test_assert(ecs_has(world, e, Foo)); test_assert(ecs_has(world, e, Position)); test_int(on_add_position, 1); @@ -2814,13 +2948,13 @@ void ComponentLifecycle_move_ctor_on_move(void) { .move_ctor = position_move_ctor, }); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t p = ecs_new(world); - Position *p1 = ecs_emplace(world, e1, Position); + Position *p1 = ecs_emplace(world, e1, Position, NULL); test_assert(p1 != NULL); - Position *p2 = ecs_emplace(world, e2, Position); + Position *p2 = ecs_emplace(world, e2, Position, NULL); test_assert(p2 != NULL); test_int(ctor_position, 0); @@ -2869,18 +3003,18 @@ void ComponentLifecycle_ptr_to_self(void) { .dtor = ecs_dtor(TestSelf) }); - ecs_entity_t role = ecs_new_entity(world, "MyRole"); + ecs_entity_t role = ecs_entity(world, { .name = "MyRole" }); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, TestSelf, {"a"}); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, TestSelf, {"a"}); - ecs_entity_t e3 = ecs_new_id(world); + ecs_entity_t e3 = ecs_new(world); ecs_add_pair(world, e2, e3, role); - ecs_entity_t e4 = ecs_new_id(world); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, TestSelf, {"a"}); ecs_delete(world, role); @@ -2901,11 +3035,11 @@ void ComponentLifecycle_ctor_move_dtor_from_move_ctor(void) { .move_ctor = position_move_ctor }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); - ecs_world_t *async = ecs_async_stage_new(world); + ecs_world_t *async = ecs_stage_new(world); - Position *p = ecs_emplace(async, e, Position); + Position *p = ecs_emplace(async, e, Position, NULL); test_assert(!ecs_has(world, e, Position)); test_int(ctor_position, 0); test_int(copy_position, 0); @@ -2923,7 +3057,7 @@ void ComponentLifecycle_ctor_move_dtor_from_move_ctor(void) { test_int(ptr->x, 10); test_int(ptr->y, 20); - ecs_async_stage_free(async); + ecs_stage_free(async); ecs_fini(world); } @@ -2934,7 +3068,7 @@ static Position hook_w_offset_position; static void hook_w_offset(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_int(it->count, 1); hook_w_offset_offset = it->offset; hook_w_offset_invoked ++; @@ -2950,15 +3084,15 @@ void ComponentLifecycle_on_add_hook_check_offset(void) { .on_add = hook_w_offset }); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); test_int(hook_w_offset_invoked, 1); test_int(hook_w_offset_offset, 0); - ecs_set(world, 0, Position, {30, 40}); + ecs_insert(world, ecs_value(Position, {30, 40})); test_int(hook_w_offset_invoked, 2); test_int(hook_w_offset_offset, 1); - ecs_set(world, 0, Position, {50, 60}); + ecs_insert(world, ecs_value(Position, {50, 60})); test_int(hook_w_offset_invoked, 3); test_int(hook_w_offset_offset, 2); @@ -2974,9 +3108,9 @@ void ComponentLifecycle_on_remove_hook_check_offset(void) { .on_remove = hook_w_offset }); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {50, 60}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); test_int(hook_w_offset_invoked, 0); ecs_remove(world, e3, Position); @@ -3009,19 +3143,19 @@ void ComponentLifecycle_on_set_hook_check_offset(void) { .on_set = hook_w_offset }); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); test_int(hook_w_offset_invoked, 1); test_int(hook_w_offset_offset, 0); test_int(hook_w_offset_position.x, 10); test_int(hook_w_offset_position.y, 20); - ecs_set(world, 0, Position, {30, 40}); + ecs_insert(world, ecs_value(Position, {30, 40})); test_int(hook_w_offset_invoked, 2); test_int(hook_w_offset_offset, 1); test_int(hook_w_offset_position.x, 30); test_int(hook_w_offset_position.y, 40); - ecs_set(world, 0, Position, {50, 60}); + ecs_insert(world, ecs_value(Position, {50, 60})); test_int(hook_w_offset_invoked, 3); test_int(hook_w_offset_offset, 2); test_int(hook_w_offset_position.x, 50); @@ -3034,7 +3168,7 @@ static int on_set_position_invoked = 0; static void on_set_position(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_int(it->count, 1); test_int(p->x, 10); @@ -3052,7 +3186,9 @@ void ComponentLifecycle_on_set_hook_on_override(void) { .on_set = on_set_position }); - ecs_entity_t p = ecs_set(world, 0, Position, {10, 20}); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t p = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_add_id(world, p, EcsPrefab); test_int(on_set_position_invoked, 1); @@ -3077,12 +3213,14 @@ void ComponentLifecycle_on_set_hook_on_auto_override(void) { ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_set_hooks(world, Position, { .on_set = on_set_position }); - ecs_entity_t p = ecs_set(world, 0, Position, {10, 20}); - ecs_add_id(world, p, ECS_OVERRIDE | ecs_id(Position)); + ecs_entity_t p = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_add_id(world, p, ECS_AUTO_OVERRIDE | ecs_id(Position)); ecs_add_id(world, p, EcsPrefab); test_int(on_set_position_invoked, 1); @@ -3114,7 +3252,7 @@ void ComponentLifecycle_batched_set_new_component_w_lifecycle(void) { ecs_defer_begin(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {10, 20}); ecs_add(world, e, Tag); // to hit command batching @@ -3140,7 +3278,7 @@ void ComponentLifecycle_batched_set_new_component_w_lifecycle(void) { ecs_fini(world); } -void ComponentLifecycle_batched_get_mut_new_component_w_lifecycle(void) { +void ComponentLifecycle_batched_ensure_new_component_w_lifecycle(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); @@ -3155,8 +3293,8 @@ void ComponentLifecycle_batched_get_mut_new_component_w_lifecycle(void) { ecs_defer_begin(world); - ecs_entity_t e = ecs_new_id(world); - ecs_get_mut(world, e, Position); + ecs_entity_t e = ecs_new(world); + ecs_ensure(world, e, Position); ecs_add(world, e, Tag); // to hit command batching test_int(ctor_position, 1); @@ -3180,3 +3318,198 @@ void ComponentLifecycle_batched_get_mut_new_component_w_lifecycle(void) { ecs_fini(world); } + +void ComponentLifecycle_on_nested_prefab_copy_test_invokes_copy_count(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + cl_ctx ctx = { { 0 } }; + + ecs_set_hooks(world, Position, { + .copy = comp_copy, + .ctx = &ctx + }); + + ecs_entity_t child = ecs_new_w(world, Position); + ecs_add_id(world, child, ECS_AUTO_OVERRIDE | ecs_id(Position)); + ecs_add_id(world, child, EcsPrefab); + test_int(ctx.copy.invoked, 0); + + ecs_entity_t parent = ecs_entity(world, { .name = "parent"}); + ecs_add_id(world, parent, EcsPrefab); + + ecs_entity_t child_e1 = ecs_entity(world, { .name = "e1"}); + ecs_add_id(world, child_e1, EcsPrefab); + ecs_add_pair(world, child_e1, EcsIsA, child); + ecs_add_pair(world, child_e1, EcsChildOf, parent); + + test_int(ctx.copy.invoked, 1); + + ecs_entity_t child_e2 = ecs_entity(world, { .name = "e2"}); + ecs_add_id(world, child_e2, EcsPrefab); + ecs_add_pair(world, child_e2, EcsIsA, child); + ecs_add_pair(world, child_e2, EcsChildOf, parent); + + test_int(ctx.copy.invoked, 2); + + ecs_entity_t child_e3 = ecs_entity(world, { .name = "e3"}); + ecs_add_id(world, child_e3, EcsPrefab); + ecs_add_pair(world, child_e3, EcsIsA, child); + ecs_add_pair(world, child_e3, EcsChildOf, parent); + + test_int(ctx.copy.invoked, 3); + + ecs_entity_t a_parent = ecs_new_w_pair(world, EcsIsA, parent); + (void)a_parent; + + test_int(ctx.copy.invoked, 7); + test_int(ctx.copy.count, 9); + + ecs_fini(world); +} + +// Tests if neither move nor move_ctor are set but move_dtor and ctor_move_dtor are set, +// the move_dtor does not get invoked when a component is moved within the table. +// Instead ctor_move_dtor should be invoked for a destructive move operation. +// Hence in total ctor_move_dtor should be invoked 2 times. +// Once for the initial move, and once for the move within the table. +void ComponentLifecycle_no_move_no_move_ctor_with_move_dtor_with_ctor_move_dtor(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + cl_ctx ctx = { { 0 } }; + + ecs_set_hooks(world, Position, { + .ctor = NULL, + .move = NULL, + .move_ctor = NULL, + .dtor = comp_dtor, + .ctor_move_dtor = comp_pos_ctor_move_dtor, + .move_dtor = comp_move_dtor, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + ecs_entity_t e3 = ecs_new(world); + ecs_add(world, e3, Position); + + ctx.ctor_move_dtor.invoked = 0; + ctx.move_dtor.invoked = 0; + + ecs_add(world, e2, Velocity); + + test_int(ctx.move_dtor.invoked, 0); + test_int(ctx.ctor_move_dtor.invoked, 2); + + ecs_fini(world); +} + +void ComponentLifecycle_new_w_table_ctor(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position) + }); + + ecs_table_t *table = ecs_table_add_id(world, NULL, ecs_id(Position)); + table = ecs_table_add_id(world, table, Bar); + + test_int(ctor_position, 0); + + ecs_entity_t e = ecs_new_w_table(world, table); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Bar)); + test_assert(ecs_get(world, e, Position) != NULL); + test_assert(table == ecs_get_table(world, e)); + test_int(ctor_position, 1); + + ecs_fini(world); +} + +void ComponentLifecycle_new_w_table_on_add_hook(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_set_hooks(world, Position, { + .on_add = on_add_position_count + }); + + ecs_table_t *table = ecs_table_add_id(world, NULL, ecs_id(Position)); + table = ecs_table_add_id(world, table, Bar); + + test_int(ctor_position, 0); + + ecs_entity_t e = ecs_new_w_table(world, table); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Bar)); + test_assert(ecs_get(world, e, Position) != NULL); + test_assert(table == ecs_get_table(world, e)); + test_int(on_add_position, 1); + + ecs_fini(world); +} + +static int hook_count = 0; +static void hook_w_count(ecs_iter_t *it) { + hook_count = ecs_count_id(it->world, ecs_field_id(it, 0)); + test_int(hook_count, 1); +} + +void ComponentLifecycle_count_in_on_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .on_add = hook_w_count + }); + + ecs_entity_t e = ecs_new_w(world, Position); + test_int(hook_count, 1); + + ecs_iter_t it = ecs_each(world, Position); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_bool(false, ecs_each_next(&it)); + + ecs_fini(world); +} + +void ComponentLifecycle_count_in_on_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .on_remove = hook_w_count + }); + + ecs_entity_t e = ecs_new_w(world, Position); + test_int(hook_count, 0); + + ecs_delete(world, e); + test_int(hook_count, 1); + + ecs_iter_t it = ecs_each(world, Position); + test_bool(false, ecs_each_next(&it)); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/Count.c b/vendors/flecs/test/core/src/Count.c similarity index 81% rename from vendors/flecs/test/api/src/Count.c rename to vendors/flecs/test/core/src/Count.c index a5ce7f116..b0b1bc8c4 100644 --- a/vendors/flecs/test/api/src/Count.c +++ b/vendors/flecs/test/core/src/Count.c @@ -1,9 +1,9 @@ -#include +#include void Count_count_empty(void) { ecs_world_t *world = ecs_mini(); - test_int(ecs_count(world, 0), 0); + test_int(ecs_count_id(world, 0), 0); ecs_fini(world); } @@ -34,8 +34,8 @@ void Count_count_disabled(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_new_w(world, Position); test_int(ecs_count(world, Position), 2); @@ -51,8 +51,8 @@ void Count_count_prefab(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_new_w(world, Position); test_int(ecs_count(world, Position), 2); diff --git a/vendors/flecs/test/api/src/Delete.c b/vendors/flecs/test/core/src/Delete.c similarity index 86% rename from vendors/flecs/test/api/src/Delete.c rename to vendors/flecs/test/core/src/Delete.c index ef1794534..f534b982e 100644 --- a/vendors/flecs/test/api/src/Delete.c +++ b/vendors/flecs/test/core/src/Delete.c @@ -1,4 +1,4 @@ -#include +#include void Delete_setup(void) { ecs_log_set_level(-3); @@ -9,7 +9,7 @@ void Delete_delete_1(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_delete(world, e); @@ -25,7 +25,7 @@ void Delete_delete_1_again(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_delete(world, e); @@ -42,14 +42,14 @@ void Delete_delete_1_again(void) { void Delete_delete_recycled_tag_again(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_delete(world, e); test_assert(ecs_exists(world, e)); test_assert(!ecs_is_alive(world, e)); - ecs_entity_t t = ecs_new_id(world); + ecs_entity_t t = ecs_new(world); test_assert(t != 0); test_assert((uint32_t)t == (uint32_t)e); test_assert(ecs_exists(world, e)); @@ -57,7 +57,7 @@ void Delete_delete_recycled_tag_again(void) { test_assert(ecs_exists(world, t)); test_assert(ecs_is_alive(world, t)); - ecs_entity_t f = ecs_new_id(world); + ecs_entity_t f = ecs_new(world); ecs_add_id(world, f, t); ecs_delete(world, e); @@ -76,7 +76,7 @@ void Delete_delete_empty(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_delete(world, e); @@ -101,9 +101,9 @@ void Delete_delete_1st_of_3(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); test_assert(e1 != 0); test_assert(e2 != 0); test_assert(e3 != 0); @@ -126,9 +126,9 @@ void Delete_delete_2nd_of_3(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); test_assert(e1 != 0); test_assert(e2 != 0); test_assert(e3 != 0); @@ -152,9 +152,9 @@ void Delete_delete_3rd_of_3(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); test_assert(e1 != 0); test_assert(e2 != 0); test_assert(e3 != 0); @@ -178,9 +178,9 @@ void Delete_delete_2_of_3(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); test_assert(e1 != 0); test_assert(e2 != 0); test_assert(e3 != 0); @@ -205,9 +205,9 @@ void Delete_delete_3_of_3(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); test_assert(e1 != 0); test_assert(e2 != 0); test_assert(e3 != 0); @@ -230,7 +230,7 @@ void Delete_delete_3_of_3(void) { static void CreateEntity(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); const ecs_entity_t *ids = ecs_bulk_new(it->world, Position, 10); test_assert(ids != NULL); } @@ -275,13 +275,13 @@ void Delete_clear_1_component(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_clear(world, e); test_assert(!ecs_get_type(world, e)); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); test_assert(e2 > e); ecs_fini(world); @@ -293,14 +293,14 @@ void Delete_clear_2_components(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); test_assert(e != 0); ecs_clear(world, e); test_assert(!ecs_get_type(world, e)); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); test_assert(e2 > e); ecs_fini(world); @@ -311,7 +311,7 @@ void Delete_alive_after_delete(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); @@ -328,7 +328,7 @@ void Delete_alive_after_clear(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); @@ -346,7 +346,7 @@ void Delete_alive_after_staged_delete(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); @@ -365,7 +365,7 @@ void Delete_alive_while_staged(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); @@ -381,7 +381,7 @@ void Delete_alive_while_staged_w_delete(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); @@ -402,12 +402,12 @@ void Delete_alive_while_staged_w_delete_recycled_id(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); ecs_delete(world, e); - e = ecs_new(world, 0); + e = ecs_new(world); test_assert(ecs_is_alive(world, e)); ecs_defer_begin(world); @@ -427,7 +427,7 @@ void Delete_alive_after_recycle(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); @@ -435,7 +435,7 @@ void Delete_alive_after_recycle(void) { test_assert(!ecs_is_alive(world, e)); test_assert(ecs_exists(world, e)); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); test_assert(e2 != 0); test_assert(ecs_is_alive(world, e2)); test_assert(e != e2); @@ -447,12 +447,12 @@ void Delete_alive_after_recycle(void) { void Delete_delete_recycled(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); test_assert(ecs_exists(world, e)); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); test_assert(e != e2); test_assert((e2 & ECS_ENTITY_MASK) == (e & ECS_ENTITY_MASK)); test_assert(!ecs_is_alive(world, e)); @@ -468,7 +468,7 @@ void Delete_delete_recycled(void) { void Delete_get_alive_for_alive(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); test_assert(ecs_exists(world, e)); @@ -483,14 +483,14 @@ void Delete_get_alive_for_alive(void) { void Delete_get_alive_for_recycled(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); test_assert(ecs_exists(world, e)); - e = ecs_new(world, 0); + e = ecs_new(world); test_assert(ecs_entity_t_lo(e) != e); // Ensure id is recycled ecs_entity_t a = ecs_get_alive(world, ecs_entity_t_lo(e)); @@ -503,7 +503,7 @@ void Delete_get_alive_for_recycled(void) { void Delete_get_alive_for_not_alive(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_delete(world, e); @@ -522,14 +522,14 @@ void Delete_get_alive_for_not_alive(void) { void Delete_get_alive_w_generation_for_recycled_alive(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); test_assert(ecs_exists(world, e)); - e = ecs_new(world, 0); + e = ecs_new(world); test_assert(ecs_entity_t_lo(e) != e); ecs_entity_t a = ecs_get_alive(world, e); @@ -542,14 +542,14 @@ void Delete_get_alive_w_generation_for_recycled_alive(void) { void Delete_get_alive_w_generation_for_recycled_not_alive(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); test_assert(ecs_exists(world, e)); - e = ecs_new(world, 0); + e = ecs_new(world); test_assert(ecs_entity_t_lo(e) != e); ecs_delete(world, e); @@ -613,10 +613,10 @@ void Delete_move_w_dtor_move(void) { .move = ecs_move(Position), }); - ecs_entity_t e_1 = ecs_new_id(world); + ecs_entity_t e_1 = ecs_new(world); test_assert(e_1 != 0); - ecs_entity_t e_2 = ecs_new_id(world); + ecs_entity_t e_2 = ecs_new(world); test_assert(e_2 != 0); @@ -659,10 +659,10 @@ void Delete_move_w_dtor_no_move(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t e_1 = ecs_new_id(world); + ecs_entity_t e_1 = ecs_new(world); test_assert(e_1 != 0); - ecs_entity_t e_2 = ecs_new_id(world); + ecs_entity_t e_2 = ecs_new(world); test_assert(e_2 != 0); ecs_set(world, e_1, Position, {10, 20}); @@ -694,10 +694,10 @@ void Delete_move_w_no_dtor_move(void) { .move = ecs_move(Position) }); - ecs_entity_t e_1 = ecs_new_id(world); + ecs_entity_t e_1 = ecs_new(world); test_assert(e_1 != 0); - ecs_entity_t e_2 = ecs_new_id(world); + ecs_entity_t e_2 = ecs_new(world); test_assert(e_2 != 0); ecs_set(world, e_1, Position, {10, 20}); @@ -725,29 +725,29 @@ void Delete_move_w_no_dtor_move(void) { void Delete_wrap_generation_count(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t start = ecs_new_id(world); + ecs_entity_t start = ecs_new(world); ecs_entity_t e = start; for (int i = 0; i < 65535; i ++) { ecs_delete(world, e); - e = ecs_new_id(world); + e = ecs_new(world); test_assert(e != start); test_assert((uint32_t)e == start); } ecs_delete(world, e); - e = ecs_new_id(world); + e = ecs_new(world); test_assert(e == start); for (int i = 0; i < 65535; i ++) { ecs_delete(world, e); - e = ecs_new_id(world); + e = ecs_new(world); test_assert(e != start); test_assert((uint32_t)e == start); } ecs_delete(world, e); - e = ecs_new_id(world); + e = ecs_new(world); test_assert(e == start); ecs_fini(world); diff --git a/vendors/flecs/test/core/src/Each.c b/vendors/flecs/test/core/src/Each.c new file mode 100644 index 000000000..fc14421e2 --- /dev/null +++ b/vendors/flecs/test/core/src/Each.c @@ -0,0 +1,179 @@ +#include + +void Each_each_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_add(world, e3, Bar); + + ecs_iter_t it = ecs_each(world, Foo); + test_bool(true, ecs_iter_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_iter_next(&it)); + + ecs_fini(world); +} + +void Each_each_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add(world, e3, Bar); + + ecs_iter_t it = ecs_each(world, Position); + test_bool(true, ecs_iter_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 20); test_int(p[1].y, 30); + } + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); test_int(p[0].y, 40); + } + + test_bool(false, ecs_iter_next(&it)); + + ecs_fini(world); +} + +void Each_each_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Foo); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtA); + ecs_add(world, e3, Foo); + ecs_new_w_pair(world, Rel, TgtB); + + ecs_iter_t it = ecs_each_pair(world, Rel, TgtA); + test_bool(true, ecs_iter_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_iter_next(&it)); + + ecs_fini(world); +} + +void Each_each_pair_rel_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Foo); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtA); + ecs_add(world, e3, Foo); + ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtB); + + ecs_iter_t it = ecs_each_pair(world, Rel, EcsWildcard); + test_bool(true, ecs_iter_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_iter_next(&it)); + + ecs_fini(world); +} + +void Each_each_pair_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + ECS_TAG(world, Foo); + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, Tgt); + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, Tgt); + ecs_entity_t e3 = ecs_new_w_pair(world, RelA, Tgt); + ecs_add(world, e3, Foo); + ecs_entity_t e4 = ecs_new_w_pair(world, RelB, Tgt); + + ecs_iter_t it = ecs_each_pair(world, EcsWildcard, Tgt); + test_bool(true, ecs_iter_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_iter_next(&it)); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/Entity.c b/vendors/flecs/test/core/src/Entity.c similarity index 67% rename from vendors/flecs/test/api/src/Entity.c rename to vendors/flecs/test/core/src/Entity.c index 18c05b8c2..99c4c737f 100644 --- a/vendors/flecs/test/api/src/Entity.c +++ b/vendors/flecs/test/core/src/Entity.c @@ -1,4 +1,4 @@ -#include +#include void Entity_init_id(void) { ecs_world_t *world = ecs_mini(); @@ -19,7 +19,7 @@ void Entity_init_id_name(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "foo"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "foo"); ecs_os_free(path); @@ -36,7 +36,7 @@ void Entity_init_id_path(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "child"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child"); ecs_os_free(path); @@ -50,7 +50,7 @@ void Entity_init_id_add_1_comp(void) { ECS_TAG(world, TagA); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {TagA} + .add = ecs_ids(TagA) }); test_assert(e != 0); test_assert(ecs_has(world, e, TagA)); @@ -65,7 +65,7 @@ void Entity_init_id_add_2_comp(void) { ECS_TAG(world, TagB); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {TagA, TagB} + .add = ecs_ids(TagA, TagB) }); test_assert(e != 0); test_assert(ecs_has(world, e, TagA)); @@ -77,7 +77,7 @@ void Entity_init_id_add_2_comp(void) { void Entity_init_id_w_scope(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t scope = ecs_new_id(world); + ecs_entity_t scope = ecs_new(world); test_assert(scope != 0); ecs_set_scope(world, scope); @@ -111,7 +111,7 @@ void Entity_init_id_name_w_scope(void) { test_assert(ecs_has_pair(world, e, EcsChildOf, scope)); test_str(ecs_get_name(world, e), "child"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child"); ecs_os_free(path); @@ -138,7 +138,7 @@ void Entity_init_id_path_w_scope(void) { test_str(ecs_get_name(world, e), "grandchild"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child.grandchild"); ecs_os_free(path); @@ -166,7 +166,7 @@ void Entity_init_id_fullpath_w_scope(void) { test_str(ecs_get_name(world, e), "grandchild"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child.grandchild"); ecs_os_free(path); @@ -202,7 +202,7 @@ void Entity_init_id_fullpath_w_scope_existing(void) { test_str(ecs_get_name(world, e), "grandchild"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child.grandchild"); ecs_os_free(path); @@ -217,13 +217,13 @@ void Entity_init_id_name_1_comp(void) { ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "foo", - .add = {TagA} + .add = ecs_ids(TagA) }); test_assert(e != 0); test_assert(ecs_has_id(world, e, TagA)); test_str(ecs_get_name(world, e), "foo"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "foo"); ecs_os_free(path); @@ -239,13 +239,13 @@ void Entity_init_id_name_2_comp(void) { ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "foo", - .add = {TagA, TagB} + .add = ecs_ids(TagA, TagB) }); test_assert(e != 0); test_assert(ecs_has_id(world, e, TagA)); test_str(ecs_get_name(world, e), "foo"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "foo"); ecs_os_free(path); @@ -270,13 +270,13 @@ void Entity_init_id_name_2_comp_w_scope(void) { ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "child", - .add = {TagA, TagB} + .add = ecs_ids(TagA, TagB) }); test_assert(e != 0); test_assert(ecs_has_id(world, e, TagA)); test_str(ecs_get_name(world, e), "child"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child"); ecs_os_free(path); @@ -289,12 +289,12 @@ void Entity_id_add_1_comp(void) { ECS_TAG(world, TagA); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_entity_t r = ecs_entity_init(world, &(ecs_entity_desc_t){ .id = e, - .add = {TagA} + .add = ecs_ids(TagA) }); test_assert(r != 0); test_assert(e == r); @@ -309,12 +309,12 @@ void Entity_id_add_2_comp(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_entity_t r = ecs_entity_init(world, &(ecs_entity_desc_t){ .id = e, - .add = {TagA, TagB} + .add = ecs_ids(TagA, TagB) }); test_assert(r != 0); test_assert(e == r); @@ -334,7 +334,7 @@ void Entity_init_id_path_w_sep(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "child"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child"); ecs_os_free(path); @@ -363,7 +363,7 @@ void Entity_find_id_name(void) { void Entity_find_w_existing_id_name(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ .id = id, @@ -400,7 +400,7 @@ void Entity_find_id_name_w_scope(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "child"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child"); ecs_os_free(path); @@ -423,7 +423,7 @@ void Entity_find_id_path(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "child"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child"); ecs_os_free(path); @@ -455,7 +455,7 @@ void Entity_find_id_path_w_scope(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "grandchild"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child.grandchild"); ecs_os_free(path); @@ -506,7 +506,7 @@ void Entity_find_id_name_match_w_scope(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "child"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_assert(path != NULL); test_str(path, "parent.child"); ecs_os_free(path); @@ -705,7 +705,7 @@ void Entity_find_id_add_1_comp(void) { ecs_entity_t r = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "foo", - .add = {TagA} + .add = ecs_ids(TagA) }); test_assert(r == e); test_assert(ecs_has_id(world, e, TagA)); @@ -727,7 +727,7 @@ void Entity_find_id_add_2_comp(void) { ecs_entity_t r = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "foo", - .add = {TagA, TagB} + .add = ecs_ids(TagA, TagB) }); test_assert(r == e); test_assert(ecs_has_id(world, e, TagA)); @@ -755,7 +755,7 @@ void Entity_init_w_scope_name(void) { test_assert(child != 0); test_str(ecs_get_name(world, child), "foo"); - char *path = ecs_get_fullpath(world, child); + char *path = ecs_get_path(world, child); test_assert(path != NULL); test_str(path, "parent.foo.foo"); ecs_os_free(path); @@ -817,7 +817,7 @@ void Entity_init_w_with_w_scope(void) { ECS_TAG(world, Tag); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_set_with(world, Tag); ecs_set_scope(world, parent); @@ -838,7 +838,7 @@ void Entity_init_w_with_w_name_scope(void) { ECS_TAG(world, Tag); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_set_with(world, Tag); ecs_set_scope(world, parent); @@ -861,7 +861,7 @@ void Entity_init_w_with_w_name_scope(void) { void Entity_is_valid(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_bool(ecs_is_valid(world, e), true); @@ -871,14 +871,14 @@ void Entity_is_valid(void) { void Entity_is_recycled_valid(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_bool(ecs_is_valid(world, e), true); ecs_delete(world, e); test_bool(ecs_is_valid(world, e), false); - e = ecs_new_id(world); + e = ecs_new(world); test_assert(e != 0); test_assert((uint32_t)e != e); test_bool(ecs_is_valid(world, e), true); @@ -897,12 +897,15 @@ void Entity_is_0_valid(void) { void Entity_is_junk_valid(void) { ecs_world_t *world = ecs_mini(); - test_bool(ecs_is_valid(world, 1000), true); - test_bool(ecs_is_valid(world, 0xFFFFFFFF), true); + test_bool(ecs_is_valid(world, 1000), false); + test_bool(ecs_is_valid(world, 0xFFFFFFFF), false); test_bool(ecs_is_valid(world, 0x4DCDCDCDCDCD), false); + + ecs_make_alive(world, 1000); + ecs_make_alive(world, 0xFFFFFFFF); - test_bool(ecs_is_alive(world, 1000), false); - test_bool(ecs_is_alive(world, 0xFFFFFFFF), false); + test_bool(ecs_is_alive(world, 1000), true); + test_bool(ecs_is_alive(world, 0xFFFFFFFF), true); test_bool(ecs_is_alive(world, 0x4DCDCDCDCDCD), false); test_bool(ecs_is_valid(world, 0xFFFFFFFFFFFFFFFF), false); @@ -915,7 +918,7 @@ void Entity_is_junk_valid(void) { void Entity_is_not_alive_valid(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_bool(ecs_is_valid(world, e), true); test_bool(ecs_is_alive(world, e), true); @@ -1042,13 +1045,13 @@ void Entity_init_w_childof_name_twice_deferred(void) { ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Bar", - .add = {ecs_pair(EcsChildOf, parent)} + .parent = parent }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Bar", - .add = {ecs_pair(EcsChildOf, parent)} + .parent = parent }); test_assert(e2 != 0); @@ -1076,13 +1079,13 @@ void Entity_init_w_childof_nested_name_twice_deferred(void) { ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Bar.Hello", - .add = {ecs_pair(EcsChildOf, parent)} + .parent = parent }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Bar.Hello", - .add = {ecs_pair(EcsChildOf, parent)} + .parent = parent }); test_assert(e2 != 0); @@ -1090,7 +1093,7 @@ void Entity_init_w_childof_nested_name_twice_deferred(void) { test_str(ecs_get_name(world, e1), "Hello"); - char *path = ecs_get_fullpath(world, e1); + char *path = ecs_get_path(world, e1); test_str(path, "Foo.Bar.Hello"); ecs_os_free(path); @@ -1107,7 +1110,7 @@ void Entity_init_w_childof_nested_name_twice_deferred(void) { void Entity_init_w_name_staged(void) { ecs_world_t *world = ecs_mini(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); @@ -1126,7 +1129,7 @@ void Entity_init_w_name_staged(void) { void Entity_record_find_for_empty(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_record_t *r = ecs_record_find(world, e); @@ -1141,7 +1144,7 @@ void Entity_record_find(void) { ECS_TAG(world, TagA); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); ecs_record_t *r = ecs_record_find(world, e); @@ -1156,10 +1159,10 @@ void Entity_record_find_from_stage(void) { ECS_TAG(world, TagA); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); @@ -1172,15 +1175,15 @@ void Entity_record_find_from_stage(void) { ecs_fini(world); } -void Entity_ensure_zero_gen(void) { +void Entity_make_alive_zero_gen(void) { ecs_world_t *world = ecs_mini(); ecs_entity_t id = 1000; test_bool(ecs_is_alive(world, id), false); - test_bool(ecs_is_valid(world, id), true); + test_bool(ecs_is_valid(world, id), false); test_bool(ecs_exists(world, id), false); - ecs_ensure(world, id); + ecs_make_alive(world, id); test_bool(ecs_is_alive(world, id), true); test_bool(ecs_is_valid(world, id), true); test_bool(ecs_exists(world, id), true); @@ -1188,7 +1191,7 @@ void Entity_ensure_zero_gen(void) { ecs_fini(world); } -void Entity_ensure_nonzero_gen(void) { +void Entity_make_alive_nonzero_gen(void) { ecs_world_t *world = ecs_mini(); ecs_entity_t id = ECS_GENERATION_INC(1000); @@ -1197,7 +1200,7 @@ void Entity_ensure_nonzero_gen(void) { test_bool(ecs_is_valid(world, id), false); test_bool(ecs_exists(world, id), false); - ecs_ensure(world, id); + ecs_make_alive(world, id); test_bool(ecs_is_alive(world, id), true); test_bool(ecs_is_valid(world, id), true); test_bool(ecs_exists(world, id), true); @@ -1205,16 +1208,16 @@ void Entity_ensure_nonzero_gen(void) { ecs_fini(world); } -void Entity_ensure_zero_gen_exists(void) { +void Entity_make_alive_zero_gen_exists(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_delete(world, e); test_assert(ecs_exists(world, e)); test_assert(!ecs_is_valid(world, e)); test_assert(!ecs_is_alive(world, e)); - ecs_ensure(world, e); + ecs_make_alive(world, e); test_assert(ecs_exists(world, e)); test_assert(ecs_is_valid(world, e)); test_assert(ecs_is_alive(world, e)); @@ -1222,10 +1225,10 @@ void Entity_ensure_zero_gen_exists(void) { ecs_fini(world); } -void Entity_ensure_nonzero_gen_exists(void) { +void Entity_make_alive_nonzero_gen_exists(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_delete(world, e); test_assert(ecs_exists(world, e)); test_assert(!ecs_is_valid(world, e)); @@ -1237,7 +1240,7 @@ void Entity_ensure_nonzero_gen_exists(void) { test_assert(!ecs_is_valid(world, e)); test_assert(!ecs_is_alive(world, e)); - ecs_ensure(world, e); + ecs_make_alive(world, e); test_assert(ecs_exists(world, e)); test_assert(ecs_is_valid(world, e)); test_assert(ecs_is_alive(world, e)); @@ -1245,39 +1248,39 @@ void Entity_ensure_nonzero_gen_exists(void) { ecs_fini(world); } -void Entity_ensure_zero_gen_exists_alive(void) { +void Entity_make_alive_zero_gen_exists_alive(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_delete(world, e); test_assert(ecs_exists(world, e)); test_assert(!ecs_is_valid(world, e)); test_assert(!ecs_is_alive(world, e)); ecs_delete(world, e); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != e); test_assert(ecs_strip_generation(e1) == e); test_expect_abort(); - ecs_ensure(world, e); // not allowed, can't ensure gen 0 if gen 1 is alive + ecs_make_alive(world, e); // not allowed, can't ensure gen 0 if gen 1 is alive } -void Entity_ensure_nonzero_gen_exists_alive(void) { +void Entity_make_alive_nonzero_gen_exists_alive(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_delete(world, e); test_assert(ecs_exists(world, e)); test_assert(!ecs_is_valid(world, e)); test_assert(!ecs_is_alive(world, e)); ecs_delete(world, e); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != e); test_assert(ecs_strip_generation(e1) == e); @@ -1285,7 +1288,7 @@ void Entity_ensure_nonzero_gen_exists_alive(void) { e = ECS_GENERATION_INC(e); test_expect_abort(); - ecs_ensure(world, e); // not allowed, can't ensure gen 2 if gen 1 is alive + ecs_make_alive(world, e); // not allowed, can't ensure gen 2 if gen 1 is alive } void Entity_set_scope_w_entity_init_from_stage(void) { @@ -1293,7 +1296,7 @@ void Entity_set_scope_w_entity_init_from_stage(void) { ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t parent = ecs_set_name(stage, 0, "Parent"); ecs_set_scope(stage, parent); ecs_entity_t child = ecs_entity_init(stage, &(ecs_entity_desc_t){ @@ -1330,7 +1333,7 @@ void Entity_entity_init_w_scope_twice(void) { test_assert( ecs_has_pair(world, e1, EcsChildOf, parent) ); test_str(ecs_get_name(world, e1), "Child"); - char *path = ecs_get_fullpath(world, e1); + char *path = ecs_get_path(world, e1); test_str(path, "Parent.Child"); ecs_os_free(path); @@ -1346,17 +1349,17 @@ void Entity_entity_init_w_childof_twice(void) { ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Child", - .add = { ecs_childof(parent) } + .parent = parent }); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Child", - .add = { ecs_childof(parent) } + .parent = parent }); test_assert( ecs_has_pair(world, e1, EcsChildOf, parent) ); test_str(ecs_get_name(world, e1), "Child"); - char *path = ecs_get_fullpath(world, e1); + char *path = ecs_get_path(world, e1); test_str(path, "Parent.Child"); ecs_os_free(path); @@ -1372,11 +1375,11 @@ void Entity_entity_init_w_childof_nested_name_twice(void) { ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Foo.Child", - .add = { ecs_childof(parent) } + .parent = parent }); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Foo.Child", - .add = { ecs_childof(parent) } + .parent = parent }); ecs_entity_t foo = ecs_lookup_child(world, parent, "Foo"); @@ -1385,7 +1388,7 @@ void Entity_entity_init_w_childof_nested_name_twice(void) { test_assert( ecs_has_pair(world, e1, EcsChildOf, foo) ); test_str(ecs_get_name(world, e1), "Child"); - char *path = ecs_get_fullpath(world, e1); + char *path = ecs_get_path(world, e1); test_str(path, "Parent.Foo.Child"); ecs_os_free(path); @@ -1403,7 +1406,7 @@ void Entity_entity_init_w_childof_and_scope(void) { ecs_set_scope(world, parent_a); ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Child", - .add = { ecs_childof(parent_b) } + .parent = parent_b }); ecs_set_scope(world, 0); @@ -1411,7 +1414,7 @@ void Entity_entity_init_w_childof_and_scope(void) { test_assert( ecs_has_pair(world, child, EcsChildOf, parent_b) ); test_str(ecs_get_name(world, child), "Child"); - char *path = ecs_get_fullpath(world, child); + char *path = ecs_get_path(world, child); test_str(path, "ParentB.Child"); ecs_os_free(path); @@ -1427,7 +1430,7 @@ void Entity_entity_init_w_childof_and_scope_and_scoped_name(void) { ecs_set_scope(world, parent_a); ecs_entity_t grand_child = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Child.GrandChild", - .add = { ecs_childof(parent_b) } + .parent = parent_b }); ecs_set_scope(world, 0); @@ -1439,7 +1442,7 @@ void Entity_entity_init_w_childof_and_scope_and_scoped_name(void) { test_assert(child != 0); test_assert( ecs_has_pair(world, grand_child, EcsChildOf, child)); - char *path = ecs_get_fullpath(world, grand_child); + char *path = ecs_get_path(world, grand_child); test_str(path, "ParentB.Child.GrandChild"); ecs_os_free(path); @@ -1449,10 +1452,10 @@ void Entity_entity_init_w_childof_and_scope_and_scoped_name(void) { void Entity_entity_init_w_childof_and_no_name(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = { ecs_pair(EcsChildOf, parent) } + .parent = parent }); test_assert(child != 0); @@ -1472,7 +1475,7 @@ void Entity_deferred_entity_init_w_childof_and_scope(void) { ecs_set_scope(world, parent_a); ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Child", - .add = { ecs_childof(parent_b) } + .parent = parent_b }); ecs_set_scope(world, 0); @@ -1482,7 +1485,7 @@ void Entity_deferred_entity_init_w_childof_and_scope(void) { test_assert( ecs_has_pair(world, child, EcsChildOf, parent_b) ); test_str(ecs_get_name(world, child), "Child"); - char *path = ecs_get_fullpath(world, child); + char *path = ecs_get_path(world, child); test_str(path, "ParentB.Child"); ecs_os_free(path); @@ -1500,7 +1503,7 @@ void Entity_deferred_entity_init_w_childof_and_scope_and_scoped_name(void) { ecs_set_scope(world, parent_a); ecs_entity_t grand_child = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Child.GrandChild", - .add = { ecs_childof(parent_b) } + .parent = parent_b }); ecs_set_scope(world, 0); @@ -1514,7 +1517,7 @@ void Entity_deferred_entity_init_w_childof_and_scope_and_scoped_name(void) { test_assert(child != 0); test_assert( ecs_has_pair(world, grand_child, EcsChildOf, child)); - char *path = ecs_get_fullpath(world, grand_child); + char *path = ecs_get_path(world, grand_child); test_str(path, "ParentB.Child.GrandChild"); ecs_os_free(path); @@ -1524,11 +1527,11 @@ void Entity_deferred_entity_init_w_childof_and_scope_and_scoped_name(void) { void Entity_deferred_entity_init_w_childof_and_no_name(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_defer_begin(world); ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = { ecs_pair(EcsChildOf, parent) } + .parent = parent }); test_assert(child != 0); test_assert(!ecs_has_pair(world, child, EcsChildOf, parent)); @@ -1580,8 +1583,8 @@ void Entity_set_name_w_0_twice(void) { void Entity_new_entity_twice(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Foo"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Foo" }); test_str(ecs_get_name(world, e1), "Foo"); test_assert(e1 == e2); @@ -1592,8 +1595,8 @@ void Entity_new_entity_twice(void) { void Entity_new_entity_scoped_twice(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new_entity(world, "Foo.Bar"); - ecs_entity_t e2 = ecs_new_entity(world, "Foo.Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo.Bar" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Foo.Bar" }); test_str(ecs_get_name(world, e1), "Bar"); test_assert(e1 == e2); @@ -1680,7 +1683,7 @@ void Entity_staged_set_name_n_stages(void) { ecs_set_stage_count(world, 2); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *s = ecs_get_stage(world, 1); @@ -1701,7 +1704,7 @@ void Entity_staged_set_symbol_n_stages(void) { ecs_set_stage_count(world, 2); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *s = ecs_get_stage(world, 1); @@ -1722,10 +1725,10 @@ void Entity_staged_set_symbol_n_stages(void) { void Entity_entity_init_w_add_childof_no_name(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = { ecs_childof(parent) } + .parent = parent }); test_assert( ecs_has_pair(world, e, EcsChildOf, parent)); @@ -1743,7 +1746,6 @@ void Entity_entity_w_short_notation(void) { ecs_fini(world); } - void Entity_override_inherited_symbol(void) { ecs_world_t *world = ecs_mini(); @@ -1756,7 +1758,7 @@ void Entity_override_inherited_symbol(void) { ecs_entity_t Bar = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "Bar", .symbol = "BarSymbol", - .add = { ecs_pair(EcsIsA, Foo) } + .parent = EcsIsA }); test_assert(Bar != 0); @@ -1781,7 +1783,7 @@ void Entity_use_low_id_for_component(void) { void Entity_get_depth(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e2); ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, e3); @@ -1800,7 +1802,7 @@ void Entity_get_depth_non_acyclic(void) { ECS_TAG(world, Rel); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); test_expect_abort(); ecs_get_depth(world, e1, Rel); @@ -1809,7 +1811,7 @@ void Entity_get_depth_non_acyclic(void) { void Entity_get_depth_empty(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); test_int(0, ecs_get_depth(world, e1, EcsChildOf)); ecs_fini(world); @@ -1818,7 +1820,7 @@ void Entity_get_depth_empty(void) { void Entity_get_depth_2_paths(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, e2); @@ -1853,7 +1855,7 @@ void Entity_entity_init_w_empty_sep(void) { void Entity_entity_init_w_empty_sep_from_scope(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); ecs_set_scope(world, parent); ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "foo.bar", @@ -1875,7 +1877,7 @@ void Entity_entity_init_w_empty_sep_from_scope(void) { void Entity_entity_init_w_empty_sep_w_prefix(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = "foo.bar", .sep = "" @@ -1895,7 +1897,7 @@ void Entity_entity_init_w_empty_sep_w_prefix(void) { void Entity_set_name_w_same_ptr(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); const char *name = ecs_get_name(world, e); test_assert(name != NULL); test_str(name, "foo"); @@ -1911,7 +1913,7 @@ void Entity_set_name_w_same_ptr(void) { void Entity_set_name_w_overlapping_ptr(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); const char *name = ecs_get_name(world, e); test_assert(name != NULL); test_str(name, "foo"); @@ -1927,7 +1929,7 @@ void Entity_set_name_w_overlapping_ptr(void) { void Entity_defer_set_name_w_overlapping_ptr(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); const char *name = ecs_get_name(world, e); test_assert(name != NULL); test_str(name, "foo"); @@ -1943,48 +1945,48 @@ void Entity_defer_set_name_w_overlapping_ptr(void) { ecs_fini(world); } -void Entity_ensure_from_stage(void) { +void Entity_make_alive_from_stage(void) { ecs_world_t *world = ecs_mini(); ecs_world_t *stage = ecs_get_stage(world, 0); test_assert(!ecs_is_alive(world, 1000)); - ecs_ensure(stage, 1000); + ecs_make_alive(stage, 1000); test_assert(ecs_is_alive(world, 1000)); ecs_fini(world); } -void Entity_ensure_after_deleted_1_entity(void) { +void Entity_make_alive_after_deleted_1_entity(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_delete(world, e1); - e1 = ecs_new_id(world); + e1 = ecs_new(world); ecs_delete(world, e1); test_assert(!ecs_is_alive(world, e1)); - ecs_ensure(world, e1); + ecs_make_alive(world, e1); test_assert(ecs_is_alive(world, e1)); ecs_fini(world); } -void Entity_ensure_after_deleted_2_entities(void) { +void Entity_make_alive_after_deleted_2_entities(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_delete(world, e1); - e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); + e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); ecs_delete(world, e1); ecs_delete(world, e2); test_assert(!ecs_is_alive(world, e1)); - ecs_ensure(world, e1); + ecs_make_alive(world, e1); test_assert(ecs_is_alive(world, e1)); test_assert(!ecs_is_alive(world, e2)); - ecs_ensure(world, e2); + ecs_make_alive(world, e2); test_assert(ecs_is_alive(world, e2)); ecs_fini(world); @@ -1993,8 +1995,8 @@ void Entity_ensure_after_deleted_2_entities(void) { void Entity_defer_entity_init_w_set_name_w_add_childof(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t e = ecs_new(world); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); ecs_defer_begin(world); ecs_entity(world, { .id = e, .name = "Foo" }); @@ -2003,7 +2005,7 @@ void Entity_defer_entity_init_w_set_name_w_add_childof(void) { ecs_defer_end(world); test_str(ecs_get_name(world, e), "FooBar"); - test_assert(e == ecs_lookup_fullpath(world, "parent.FooBar")); + test_assert(e == ecs_lookup(world, "parent.FooBar")); ecs_fini(world); } @@ -2016,7 +2018,7 @@ void Entity_entity_w_digit_name(void) { }); test_assert(e != 0); - test_uint(e, 10000); + test_str(ecs_get_name(world, e), "10000"); test_assert(ecs_is_alive(world, e)); ecs_fini(world); @@ -2030,20 +2032,103 @@ void Entity_entity_w_existing_digit_name(void) { }); test_assert(e != 0); - test_uint(e, 7); + test_str(ecs_get_name(world, e), "7"); + test_assert(ecs_is_alive(world, e)); + + ecs_fini(world); +} + +void Entity_entity_from_digit(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "#10000" + }); + + test_assert(e != 0); + test_uint(e, 10000); + test_assert(ecs_is_alive(world, e)); + + ecs_fini(world); +} + +void Entity_entity_from_existing_digit(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "#10000" + }); + + ecs_entity_t f = ecs_entity(world, { + .name = "#10000" + }); + + test_assert(e != 0); + test_assert(e == f); + test_uint(e, 10000); + test_assert(ecs_is_alive(world, e)); + + ecs_fini(world); +} + +void Entity_entity_from_digit_path(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "#10000.foo" + }); + + test_assert(e != 0); + test_assert(ecs_is_alive(world, e)); + test_assert(ecs_has_pair(world, e, EcsChildOf, 10000)); + test_str(ecs_get_name(world, e), "foo"); + + ecs_fini(world); +} + +void Entity_entity_from_existing_digit_path(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "#10000.foo" + }); + + ecs_entity_t f = ecs_entity(world, { + .name = "#10000.foo" + }); + + test_assert(e != 0); + test_assert(e == f); + test_assert(ecs_is_alive(world, e)); + test_assert(ecs_has_pair(world, e, EcsChildOf, 10000)); + test_str(ecs_get_name(world, e), "foo"); + + ecs_fini(world); +} + +void Entity_entity_from_digit_0_path(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "#0.foo" + }); + + test_assert(e != 0); test_assert(ecs_is_alive(world, e)); + test_assert(!ecs_has_pair(world, e, EcsChildOf, EcsWildcard)); + test_str(ecs_get_name(world, e), "foo"); ecs_fini(world); } -void Entity_entity_w_conflicting_digit_name(void) { +void Entity_entity_from_conflicting_digit(void) { ecs_world_t *world = ecs_mini(); ecs_log_set_level(-4); ecs_entity_t e = ecs_entity(world, { .id = 6, - .name = "7" + .name = "#7" }); test_assert(e == 0); @@ -2051,7 +2136,7 @@ void Entity_entity_w_conflicting_digit_name(void) { ecs_fini(world); } -void Entity_set_generation_on_nonempty_entity(void) { +void Entity_set_version_on_nonempty_entity(void) { ecs_world_t* world = ecs_mini(); ECS_TAG(world, Tag); @@ -2063,24 +2148,24 @@ void Entity_set_generation_on_nonempty_entity(void) { ecs_add(world, e1, Tag); e1 |= 0x200000000ul; - ecs_set_entity_generation(world, e1); + ecs_set_version(world, e1); - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ .id = Tag }); + ecs_iter_t it = ecs_each(world, Tag); ecs_entity_t first = ecs_iter_first(&it); test_uint(first, e1); ecs_fini(world); } -void Entity_set_generation_while_deferred(void) { +void Entity_set_version_while_deferred(void) { install_test_abort(); ecs_world_t* world = ecs_mini(); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_defer_begin(world); test_expect_abort(); - ecs_set_entity_generation(world, e1 |= 0x200000000ul); + ecs_set_version(world, e1 |= 0x200000000ul); } static @@ -2097,7 +2182,7 @@ void Entity_commit_w_on_add(void) { int invoked = 0; ecs_observer(world, { - .filter.terms = { + .query.terms = { { .id = Tag } }, .events = { EcsOnAdd }, @@ -2113,7 +2198,7 @@ void Entity_commit_w_on_add(void) { .count = 1 }; - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_commit(world, e, NULL, dst, &added, NULL); test_assert(ecs_has(world, e, Tag)); @@ -2131,7 +2216,7 @@ void Entity_commit_w_on_remove(void) { int invoked = 0; ecs_observer(world, { - .filter.terms = { + .query.terms = { { .id = Tag } }, .events = { EcsOnRemove }, @@ -2147,7 +2232,7 @@ void Entity_commit_w_on_remove(void) { ecs_table_t *dst = ecs_table_add_id(world, NULL, Foo); test_assert(dst != NULL); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_add_id(world, e, Foo); ecs_commit(world, e, NULL, dst, NULL, &removed); @@ -2183,7 +2268,7 @@ void Entity_commit_w_cmd_in_observer(void) { int invoked = 0; ecs_observer(world, { - .filter.terms = { + .query.terms = { { .id = Tag } }, .events = { EcsOnAdd }, @@ -2199,7 +2284,7 @@ void Entity_commit_w_cmd_in_observer(void) { .count = 1 }; - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_commit(world, e, NULL, dst, &added, NULL); test_assert(ecs_has(world, e, Tag)); @@ -2228,3 +2313,708 @@ void Entity_entity_init_existing_no_sep(void) { ecs_fini(world); } + +void Entity_entity_init_w_set_1_comp(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_entity(world, { + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_2_comp(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e = ecs_entity(world, { + .set = ecs_values( + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2}) + ) + }); + + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, e, Velocity); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_1_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + + ecs_entity_t e = ecs_entity(world, { + .add = ecs_ids(TagA), + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, TagA)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_2_comp_2_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e = ecs_entity(world, { + .add = ecs_ids(TagA, TagB), + .set = ecs_values( + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2}) + ) + }); + + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + test_assert(ecs_has(world, e, TagA)); + test_assert(ecs_has(world, e, TagB)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, e, Velocity); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_w_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_entity(world, { + .name = "Foo", + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_str(ecs_get_name(world, e), "Foo"); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_entity(world, { + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + ecs_entity_t e2 = ecs_entity(world, { + .id = e1, + .set = ecs_values( ecs_value(Velocity, {1, 2}) ) + }); + + test_assert(e1 == e2); + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e1, Velocity)); + + const Position *p = ecs_get(world, e1, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, e1, Velocity); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_existing_empty(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_entity(world, { + .id = e1, + .set = ecs_values( ecs_value(Velocity, {1, 2}) ) + }); + + test_assert(e1 == e2); + test_assert(ecs_has(world, e1, Velocity)); + + const Velocity *v = ecs_get(world, e1, Velocity); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +static int add_position_invoked = 0; +static int set_position_invoked = 0; + +static void add_position_hook(ecs_iter_t *it) { + add_position_invoked ++; +} + +static void set_position_hook(ecs_iter_t *it) { + set_position_invoked ++; +} + +void Entity_entity_init_w_set_w_hook(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .on_add = add_position_hook, + .on_set = set_position_hook + }); + + ecs_entity_t e = ecs_entity(world, { + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_int(add_position_invoked, 1); + test_int(set_position_invoked, 1); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_w_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }}, + .events = { EcsOnAdd }, + .callback = add_position_hook + }); + + ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }}, + .events = { EcsOnSet }, + .callback = set_position_hook + }); + + ecs_entity_t e = ecs_entity(world, { + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_int(add_position_invoked, 1); + test_int(set_position_invoked, 1); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_1_tag_w_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + + ecs_entity_t e = ecs_entity(world, { + .set = ecs_values( ecs_value(Position, {10, 20}), {TagA} ) + }); + + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, TagA)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_defer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_defer_begin(world); + + ecs_entity_t e = ecs_entity(world, { + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + test_assert(e != 0); + test_assert(!ecs_has(world, e, Position)); + + ecs_defer_end(world); + + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_2_comp_defer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_defer_begin(world); + + ecs_entity_t e = ecs_entity(world, { + .set = ecs_values( + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2}) + ) + }); + + test_assert(e != 0); + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, Velocity)); + + ecs_defer_end(world); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, e, Velocity); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_1_tag_defer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + + ecs_defer_begin(world); + + ecs_entity_t e = ecs_entity(world, { + .add = ecs_ids(TagA), + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + test_assert(e != 0); + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, TagA)); + + ecs_defer_end(world); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, TagA)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_2_comp_2_tag_defer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_defer_begin(world); + + ecs_entity_t e = ecs_entity(world, { + .add = ecs_ids(TagA, TagB), + .set = ecs_values( + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2}) + ) + }); + + test_assert(e != 0); + + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, Velocity)); + test_assert(!ecs_has(world, e, TagA)); + test_assert(!ecs_has(world, e, TagB)); + + ecs_defer_end(world); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + test_assert(ecs_has(world, e, TagA)); + test_assert(ecs_has(world, e, TagB)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, e, Velocity); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_w_name_defer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_defer_begin(world); + + ecs_entity_t e = ecs_entity(world, { + .name = "Foo", + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + test_assert(e != 0); + + test_assert(!ecs_has(world, e, Position)); + test_str(ecs_get_name(world, e), "Foo"); + + ecs_defer_end(world); + + test_assert(ecs_has(world, e, Position)); + test_str(ecs_get_name(world, e), "Foo"); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_existing_defer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_defer_begin(world); + + ecs_entity_t e1 = ecs_entity(world, { + .set = ecs_values( ecs_value(Position, {10, 20}) ) + }); + + ecs_entity_t e2 = ecs_entity(world, { + .id = e1, + .set = ecs_values( ecs_value(Velocity, {1, 2}) ) + }); + + test_assert(e1 == e2); + test_assert(!ecs_has(world, e1, Position)); + test_assert(!ecs_has(world, e1, Velocity)); + + ecs_defer_end(world); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e1, Velocity)); + + const Position *p = ecs_get(world, e1, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, e1, Velocity); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_existing_empty_defer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_defer_begin(world); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_entity(world, { + .id = e1, + .set = ecs_values( ecs_value(Velocity, {1, 2}) ) + }); + + test_assert(e1 == e2); + test_assert(!ecs_has(world, e1, Velocity)); + + ecs_defer_end(world); + + test_assert(ecs_has(world, e1, Velocity)); + + const Velocity *v = ecs_get(world, e1, Velocity); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Entity_entity_init_w_set_1_comp_1_tag_w_set_defer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + + ecs_defer_begin(world); + + ecs_entity_t e = ecs_entity(world, { + .set = ecs_values( ecs_value(Position, {10, 20}), {TagA} ) + }); + + test_assert(e != 0); + + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, TagA)); + + ecs_defer_end(world); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, TagA)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_insert_1_comp(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_insert_2_comp(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2}) + ); + + test_assert(e != 0); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, e, Velocity); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Entity_insert_1_comp_1_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + + ecs_entity_t e = ecs_insert(world, + ecs_value(Position, {10, 20}), + {TagA} + ); + + test_assert(e != 0); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, TagA)); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Entity_entity_w_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t parent = ecs_new(world); + + ecs_entity_t e = ecs_entity(world, { + .parent = parent + }); + + test_assert(e != 0); + + test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Entity_entity_w_parent_w_name(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t parent = ecs_new(world); + + ecs_entity_t e = ecs_entity(world, { + .parent = parent, + .name = "Foo" + }); + + test_assert(e != 0); + + test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_str(ecs_get_name(world, e), "Foo"); + + ecs_fini(world); +} + +void Entity_entity_w_parent_w_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t parent = ecs_new(world); + + ecs_entity_t e = ecs_entity(world, { + .parent = parent, + .add = (ecs_id_t[]){TagA, 0} + }); + + test_assert(e != 0); + + test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_assert(ecs_has(world, e, TagA)); + + ecs_fini(world); +} + +void Entity_entity_w_parent_w_add_w_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t parent = ecs_new(world); + + ecs_entity_t e = ecs_entity(world, { + .parent = parent, + .name = "Foo", + .add = (ecs_id_t[]){TagA, 0} + }); + + test_assert(e != 0); + + test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_assert(ecs_has(world, e, TagA)); + test_str(ecs_get_name(world, e), "Foo"); + + ecs_fini(world); +} + +void Entity_entity_w_parent_w_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t parent = ecs_new(world); + + ecs_entity_t e = ecs_entity(world, { + .parent = parent, + .set = (ecs_value_t[]){{ .type = TagA}, {0}} + }); + + test_assert(e != 0); + + test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_assert(ecs_has(world, e, TagA)); + + ecs_fini(world); +} + +void Entity_entity_w_parent_w_set_w_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t parent = ecs_new(world); + + ecs_entity_t e = ecs_entity(world, { + .parent = parent, + .name = "Foo", + .set = (ecs_value_t[]){{ .type = TagA}, {0}} + }); + + test_assert(e != 0); + + test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_assert(ecs_has(world, e, TagA)); + test_str(ecs_get_name(world, e), "Foo"); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/Error.c b/vendors/flecs/test/core/src/Error.c similarity index 95% rename from vendors/flecs/test/api/src/Error.c rename to vendors/flecs/test/core/src/Error.c index 68ffc4e26..e1188b823 100644 --- a/vendors/flecs/test/api/src/Error.c +++ b/vendors/flecs/test/core/src/Error.c @@ -1,4 +1,4 @@ -#include +#include #include void Error_setup(void) { @@ -107,3 +107,9 @@ void Error_last_error(void) { test_int(10, err); test_int(0, ecs_log_last_error()); } + +void Error_set_log_level_return(void) { + ecs_log_set_level(1); + int prev = ecs_log_set_level(-1); + test_int(prev, 1); +} diff --git a/vendors/flecs/test/api/src/Event.c b/vendors/flecs/test/core/src/Event.c similarity index 78% rename from vendors/flecs/test/api/src/Event.c rename to vendors/flecs/test/core/src/Event.c index 5a936707d..69e1ac2bf 100644 --- a/vendors/flecs/test/api/src/Event.c +++ b/vendors/flecs/test/core/src/Event.c @@ -1,4 +1,4 @@ -#include +#include static void system_callback(ecs_iter_t *it) { @@ -20,15 +20,15 @@ void system_w_param_callback(ecs_iter_t *it) { void Event_table_1_id_w_trigger(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); ecs_table_t *table = ecs_get_table(world, e); Probe ctx = {0}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {evt}, .callback = system_callback, .ctx = &ctx @@ -54,9 +54,9 @@ void Event_table_1_id_w_trigger(void) { void Event_table_2_ids_w_trigger(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id_a = ecs_new_id(world); - ecs_entity_t id_b = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id_a = ecs_new(world); + ecs_entity_t id_b = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id_a); ecs_add_id(world, e, id_b); ecs_table_t *table = ecs_get_table(world, e); @@ -64,7 +64,7 @@ void Event_table_2_ids_w_trigger(void) { Probe ctx = {0}; ecs_entity_t s_a = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id_a, + .query.terms[0].id = id_a, .events = {evt}, .callback = system_callback, .ctx = &ctx, @@ -87,7 +87,7 @@ void Event_table_2_ids_w_trigger(void) { ecs_delete(world, s_a); ecs_entity_t s_b = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id_b, + .query.terms[0].id = id_b, .events = {evt}, .callback = system_callback, .ctx = &ctx @@ -115,15 +115,15 @@ void Event_table_2_ids_w_trigger(void) { void Event_table_1_id_w_observer(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); ecs_table_t *table = ecs_get_table(world, e); Probe ctx = {0}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ id }}, + .query.terms = {{ id }}, .events = {evt}, .callback = system_callback, .ctx = &ctx @@ -149,9 +149,9 @@ void Event_table_1_id_w_observer(void) { void Event_table_2_ids_w_observer(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id_a = ecs_new_id(world); - ecs_entity_t id_b = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id_a = ecs_new(world); + ecs_entity_t id_b = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id_a); ecs_add_id(world, e, id_b); ecs_table_t *table = ecs_get_table(world, e); @@ -159,7 +159,7 @@ void Event_table_2_ids_w_observer(void) { Probe ctx = {0}; ecs_entity_t s_a = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ id_a }}, + .query.terms = {{ id_a }}, .events = {evt}, .callback = system_callback, .ctx = &ctx @@ -196,14 +196,14 @@ void Event_emit_event_for_empty_table(void) { ECS_TAG(world, TagA); - ecs_entity_t evt = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); ecs_table_t *table = ecs_get_table(world, e); ecs_delete(world, e); ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }}, + .query.terms = {{ TagA }}, .events = {evt}, .callback = empty_table_callback, .ctx = table @@ -228,28 +228,31 @@ void Event_emit_table_event(void) { ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_entity_t base = ecs_new_w_id(world, TagA); ecs_table_t *base_table = ecs_get_table(world, base); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsIsA, base); Probe ctx_a = {0}; Probe ctx_b = {0}; - ecs_entity_t evt = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].src.flags = EcsSelf, + .query.terms[0].id = TagA, + .query.terms[0].src.id = EcsSelf, .events = {evt}, .callback = system_callback, .ctx = &ctx_a }); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].src.flags = EcsUp, + .query.terms[0].id = TagA, + .query.terms[0].src.id = EcsUp, + .query.terms[0].trav = EcsIsA, .events = {evt}, .callback = system_callback, .ctx = &ctx_b @@ -282,20 +285,20 @@ void Event_emit_table_event(void) { void Event_emit_staged_from_world(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); ecs_table_t *table = ecs_get_table(world, e); Probe ctx = {0}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {evt}, .callback = system_callback, .ctx = &ctx }); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_emit(world, &(ecs_event_desc_t){ .event = evt, .ids = &(ecs_type_t){.count = 1, .array = (ecs_id_t[]){ id }}, @@ -317,20 +320,20 @@ void Event_emit_staged_from_world(void) { void Event_emit_staged_from_stage(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); ecs_table_t *table = ecs_get_table(world, e); Probe ctx = {0}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {evt}, .callback = system_callback, .ctx = &ctx }); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_emit(stage, &(ecs_event_desc_t){ .event = evt, @@ -353,20 +356,20 @@ void Event_emit_staged_from_stage(void) { void Event_emit_staged_from_world_observer(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); ecs_table_t *table = ecs_get_table(world, e); Probe ctx = {0}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ id }}, + .query.terms = {{ id }}, .events = {evt}, .callback = system_callback, .ctx = &ctx }); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_emit(world, &(ecs_event_desc_t){ .event = evt, .ids = &(ecs_type_t){.count = 1, .array = (ecs_id_t[]){ id }}, @@ -388,21 +391,21 @@ void Event_emit_staged_from_world_observer(void) { void Event_emit_staged_from_stage_observer(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); ecs_table_t *table = ecs_get_table(world, e); Probe ctx = {0}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ id }}, + .query.terms = {{ id }}, .events = {evt}, .callback = system_callback, .ctx = &ctx }); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_emit(stage, &(ecs_event_desc_t){ @@ -426,14 +429,14 @@ void Event_emit_staged_from_stage_observer(void) { void Event_emit_for_entity(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {evt}, .callback = system_callback, .ctx = &ctx @@ -470,19 +473,19 @@ void Event_emit_custom_for_any(void) { ECS_TAG(world, Tag); - ecs_entity_t MyEvent = ecs_new_id(world); + ecs_entity_t MyEvent = ecs_new(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); ecs_observer(world, { - .filter.terms = {{ .id = EcsAny, .src.id = e1 }}, + .query.terms = {{ .id = EcsAny, .src.id = e1 }}, .events = {MyEvent}, .callback = ObserverA }); ecs_observer(world, { - .filter.terms = {{ .id = EcsAny, .src.id = e2 }}, + .query.terms = {{ .id = EcsAny, .src.id = e2 }}, .events = {MyEvent}, .callback = ObserverB }); @@ -513,19 +516,19 @@ void Event_emit_custom_implicit_any(void) { ECS_TAG(world, Tag); - ecs_entity_t MyEvent = ecs_new_id(world); + ecs_entity_t MyEvent = ecs_new(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); ecs_observer(world, { - .filter.terms = {{ .id = EcsAny, .src.id = e1 }}, + .query.terms = {{ .id = EcsAny, .src.id = e1 }}, .events = {MyEvent}, .callback = ObserverA }); ecs_observer(world, { - .filter.terms = {{ .id = EcsAny, .src.id = e2 }}, + .query.terms = {{ .id = EcsAny, .src.id = e2 }}, .events = {MyEvent}, .callback = ObserverB }); @@ -554,19 +557,19 @@ void Event_emit_custom_empty_type(void) { ECS_TAG(world, Tag); - ecs_entity_t MyEvent = ecs_new_id(world); + ecs_entity_t MyEvent = ecs_new(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); ecs_observer(world, { - .filter.terms = {{ .id = EcsAny, .src.id = e1 }}, + .query.terms = {{ .id = EcsAny, .src.id = e1 }}, .events = {MyEvent}, .callback = ObserverA }); ecs_observer(world, { - .filter.terms = {{ .id = EcsAny, .src.id = e2 }}, + .query.terms = {{ .id = EcsAny, .src.id = e2 }}, .events = {MyEvent}, .callback = ObserverB }); @@ -595,16 +598,54 @@ void Event_emit_custom_empty_type(void) { void Event_emit_w_param(void) { ecs_world_t *world = ecs_mini(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Position); /* Used as event type */ - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, + .events = {ecs_id(Position)}, + .callback = system_w_param_callback, + .ctx = &ctx + }); + + ecs_emit(world, &(ecs_event_desc_t){ + .event = ecs_id(Position), + .ids = &(ecs_type_t){.count = 1, .array = (ecs_id_t[]){ id }}, + .entity = e, + .param = &p + }); + + test_int(ctx.invoked, 1); + test_assert(ctx.system == s); + test_assert(ctx.event == ecs_id(Position)); + test_assert(ctx.event_id == id); + test_int(ctx.count, 1); + test_assert(ctx.e[0] == e); + test_assert(system_param == &p); + + ecs_fini(world); +} + +void Event_emit_w_param_multi_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); /* Used as event type */ + ECS_TAG(world, Foo); + + ecs_entity_t id = ecs_new(world); + ecs_entity_t e = ecs_new_w_id(world, id); + ecs_add(world, e, Foo); + + Probe ctx = {0}; + Position p = {10, 20}; + + ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ id }, { Foo }}, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx @@ -633,14 +674,14 @@ void Event_emit_w_const_param(void) { ECS_COMPONENT(world, Position); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx @@ -664,17 +705,87 @@ void Event_emit_w_const_param(void) { ecs_fini(world); } +static ecs_entity_t other = 0; + +static void Nested1(ecs_iter_t *it) { + int *ctx = it->ctx; + (*ctx) ++; + + test_int(it->count, 1); + + if (it->entities[0] != other) { + ecs_emit(it->world, &(ecs_event_desc_t) { + .event = it->event, + .ids = &(ecs_type_t){ .count = 1, .array = &it->event_id }, + .entity = other, + .observable = it->real_world + }); + } +} + +static void Nested2(ecs_iter_t *it) { + int *ctx = it->ctx; + (*ctx) ++; +} + +void Event_emit_nested(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Event); + + int ctx_1 = 0; + int ctx_2 = 0; + + ecs_entity_t e1 = ecs_new(world); + other = ecs_new(world); + + ecs_add(world, e1, Foo); + ecs_add(world, e1, Bar); + ecs_add(world, other, Foo); + ecs_add(world, other, Bar); + + ecs_observer(world, { + .entity = ecs_entity(world, { .name = "o1" }), + .query.terms = {{ Foo }, { Bar }}, + .events = { Event }, + .ctx = &ctx_1, + .callback = Nested1 + }); + + ecs_observer(world, { + .entity = ecs_entity(world, { .name = "o2" }), + .query.terms = {{ Foo }, { Bar }}, + .events = { Event }, + .ctx = &ctx_2, + .callback = Nested2 + }); + + ecs_emit(world, &(ecs_event_desc_t) { + .event = Event, + .ids = &(ecs_type_t){ .count = 1, .array = &Foo }, + .entity = e1, + .observable = world + }); + + test_int(ctx_1, 2); + test_int(ctx_2, 2); + + ecs_fini(world); +} + void Event_enqueue_event_1_id(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {evt}, .callback = system_callback, .ctx = &ctx @@ -705,9 +816,9 @@ void Event_enqueue_event_1_id(void) { void Event_enqueue_event_2_ids(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id_a = ecs_new_id(world); - ecs_entity_t id_b = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id_a = ecs_new(world); + ecs_entity_t id_b = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id_a); ecs_add_id(world, e, id_b); @@ -715,14 +826,14 @@ void Event_enqueue_event_2_ids(void) { Probe *ctx_b = ecs_os_calloc_t(Probe); ecs_entity_t s_a = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id_a, + .query.terms[0].id = id_a, .events = {evt}, .callback = system_callback, .ctx = ctx_a }); ecs_entity_t s_b = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id_b, + .query.terms[0].id = id_b, .events = {evt}, .callback = system_callback, .ctx = ctx_b @@ -766,14 +877,14 @@ void Event_enqueue_event_w_data(void) { ECS_COMPONENT(world, Position); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx @@ -835,14 +946,14 @@ void Event_enqueue_event_w_data_move(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx @@ -888,14 +999,14 @@ void Event_enqueue_event_w_data_copy(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx @@ -936,14 +1047,14 @@ void Event_enqueue_event_w_const_data_no_copy(void) { ECS_COMPONENT(world, Position); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx @@ -980,14 +1091,14 @@ void Event_enqueue_event_w_const_data_no_copy(void) { void Event_enqueue_event_not_alive(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {evt}, .callback = system_callback, .ctx = &ctx @@ -1023,14 +1134,14 @@ void Event_enqueue_event_not_alive_w_data_move(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx @@ -1067,14 +1178,14 @@ void Event_enqueue_event_not_alive_w_data_copy(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx @@ -1112,22 +1223,22 @@ void system_delete_callback(ecs_iter_t *it) { void Event_enqueue_event_not_alive_after_delete_during_merge(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t delete_evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t delete_evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {evt}, .callback = system_callback, .ctx = &ctx }); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {delete_evt}, .callback = system_delete_callback, }); @@ -1166,22 +1277,22 @@ void Event_enqueue_event_not_alive_w_data_move_after_delete_during_merge(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t delete_evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t delete_evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx }); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {delete_evt}, .callback = system_delete_callback, }); @@ -1224,22 +1335,22 @@ void Event_enqueue_event_not_alive_w_data_copy_after_delete_during_merge(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t delete_evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t delete_evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; Position p = {10, 20}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {ecs_id(Position)}, .callback = system_w_param_callback, .ctx = &ctx }); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {delete_evt}, .callback = system_delete_callback, }); @@ -1274,40 +1385,56 @@ void Event_enqueue_event_not_alive_w_data_copy_after_delete_during_merge(void) { } void Event_enqueue_event_not_deferred(void) { - install_test_abort(); - ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); + ecs_table_t *table = ecs_get_table(world, e); - test_expect_abort(); + Probe ctx = {0}; + + ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms[0].id = id, + .events = {evt}, + .callback = system_callback, + .ctx = &ctx + }); ecs_enqueue(world, &(ecs_event_desc_t){ .event = evt, .ids = &(ecs_type_t){.count = 1, .array = (ecs_id_t[]){ id }}, - .entity = e + .table = table, + .observable = world }); + + test_int(ctx.invoked, 1); + test_assert(ctx.system == s); + test_assert(ctx.event == evt); + test_assert(ctx.event_id == id); + test_int(ctx.count, 1); + test_assert(ctx.e[0] == e); + + ecs_fini(world); } void Event_enqueue_event_not_deferred_to_async(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t evt = ecs_new_id(world); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, id); Probe ctx = {0}; ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = id, + .query.terms[0].id = id, .events = {evt}, .callback = system_callback, .ctx = &ctx }); - ecs_world_t *my_stage = ecs_async_stage_new(world); + ecs_world_t *my_stage = ecs_stage_new(world); ecs_enqueue(my_stage, &(ecs_event_desc_t){ .event = evt, @@ -1326,7 +1453,7 @@ void Event_enqueue_event_not_deferred_to_async(void) { test_int(ctx.count, 1); test_assert(ctx.e[0] == e); - ecs_async_stage_free(my_stage); + ecs_stage_free(my_stage); ecs_fini(world); } @@ -1336,19 +1463,19 @@ void Event_enqueue_custom_implicit_any(void) { ECS_TAG(world, Tag); - ecs_entity_t MyEvent = ecs_new_id(world); + ecs_entity_t MyEvent = ecs_new(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); ecs_observer(world, { - .filter.terms = {{ .id = EcsAny, .src.id = e1 }}, + .query.terms = {{ .id = EcsAny, .src.id = e1 }}, .events = {MyEvent}, .callback = ObserverA }); ecs_observer(world, { - .filter.terms = {{ .id = EcsAny, .src.id = e2 }}, + .query.terms = {{ .id = EcsAny, .src.id = e2 }}, .events = {MyEvent}, .callback = ObserverB }); @@ -1409,7 +1536,7 @@ void Event_enqueue_custom_after_large_cmd(void) { ECS_OBSERVER(world, OnPosition, ecs_id(Position), LargeType); ecs_defer_begin(world); - ecs_entity_t e = ecs_set(world, 0, LargeType, {{0}}); + ecs_entity_t e = ecs_insert(world, ecs_value(LargeType, {{0}})); ecs_defer_end(world); test_assert(ecs_has(world, e, LargeType)); @@ -1428,3 +1555,41 @@ void Event_enqueue_custom_after_large_cmd(void) { ecs_fini(world); } + +void Event_enqueue_on_readonly_world(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t evt = ecs_new(world); + ecs_entity_t id = ecs_new(world); + ecs_entity_t e = ecs_new_w_id(world, id); + + Probe ctx = {0}; + + ecs_entity_t s = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms[0].id = id, + .events = {evt}, + .callback = system_callback, + .ctx = &ctx + }); + + ecs_readonly_begin(world, false); + + ecs_enqueue(world, &(ecs_event_desc_t){ + .event = evt, + .ids = &(ecs_type_t){.count = 1, .array = (ecs_id_t[]){ id }}, + .entity = e + }); + + test_int(ctx.invoked, 0); + + ecs_readonly_end(world); + + test_int(ctx.invoked, 1); + test_assert(ctx.system == s); + test_assert(ctx.event == evt); + test_assert(ctx.event_id == id); + test_int(ctx.count, 1); + test_assert(ctx.e[0] == e); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/Get_component.c b/vendors/flecs/test/core/src/Get_component.c similarity index 87% rename from vendors/flecs/test/api/src/Get_component.c rename to vendors/flecs/test/core/src/Get_component.c index 6b3b4b968..4827d7de7 100644 --- a/vendors/flecs/test/api/src/Get_component.c +++ b/vendors/flecs/test/core/src/Get_component.c @@ -1,4 +1,4 @@ -#include +#include void Get_component_setup(void) { ecs_log_set_level(-3); @@ -7,7 +7,7 @@ void Get_component_setup(void) { void Get_component_get_empty(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(ecs_get_type(world, e) == NULL); @@ -20,7 +20,7 @@ void Get_component_get_1_from_1(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_get_type(world, e)->array[0] == ecs_id(Position)); @@ -73,7 +73,7 @@ void Get_component_get_2_from_3(void) { static void Test_main_stage(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); for (int i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; @@ -86,7 +86,7 @@ void Get_component_get_1_from_2_in_progress_from_main_stage(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ECS_SYSTEM(world, Test_main_stage, EcsOnUpdate, Position); @@ -101,7 +101,7 @@ void Add_in_progress(ecs_iter_t *it) { ecs_id_t ecs_id(Velocity) = 0; if (it->field_count >= 2) { - ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id(Velocity) = ecs_field_id(it, 1); } for (int i = 0; i < it->count; i ++) { @@ -117,7 +117,7 @@ void Get_component_get_1_from_2_add_in_progress(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ECS_SYSTEM(world, Add_in_progress, EcsOnUpdate, Position, Velocity()); @@ -131,8 +131,8 @@ void Get_component_get_1_from_2_add_in_progress(void) { static void Add_in_progress_test_main(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); for (int i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; @@ -147,7 +147,7 @@ void Get_component_get_both_from_2_add_in_progress(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ECS_SYSTEM(world, Add_in_progress_test_main, EcsOnUpdate, Position, Velocity()); @@ -163,8 +163,8 @@ void Get_component_get_both_from_2_add_in_progress(void) { static void Add_remove_in_progress_test_main(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); for (int i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; @@ -180,7 +180,7 @@ void Get_component_get_both_from_2_add_remove_in_progress(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ECS_SYSTEM(world, Add_remove_in_progress_test_main, EcsOnUpdate, Position, Velocity()); @@ -210,7 +210,7 @@ void Get_component_get_childof_component(void) { ecs_get(world, ecs_pair(EcsChildOf, ecs_id(Position)), EcsComponent); } -void Get_component_get_mut_equal_get(void) { +void Get_component_ensure_equal_get(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); @@ -219,7 +219,7 @@ void Get_component_get_mut_equal_get(void) { ECS_ENTITY(world, e, Position); test_assert(e != 0); - test_assert(ecs_get_mut(world, e, Position) == ecs_get(world, e, + test_assert(ecs_ensure(world, e, Position) == ecs_get(world, e, Position)); ecs_fini(world); @@ -231,7 +231,7 @@ void Get_component_get_tag(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); test_assert(e != 0); test_expect_abort(); @@ -244,7 +244,7 @@ void Get_component_get_pair_tag(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent); test_assert(e != 0); @@ -260,7 +260,7 @@ void Get_component_get_wildcard(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tgt); - ecs_entity_t e = ecs_set_pair(world, 0, Position, Tgt, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value_pair(Position, Tgt, {10, 20})); test_assert(e != 0); test_assert(ecs_has_pair(world, e, ecs_id(Position), Tgt)); diff --git a/vendors/flecs/test/api/src/GlobalComponentIds.c b/vendors/flecs/test/core/src/GlobalComponentIds.c similarity index 95% rename from vendors/flecs/test/api/src/GlobalComponentIds.c rename to vendors/flecs/test/core/src/GlobalComponentIds.c index f21321920..cba003f18 100644 --- a/vendors/flecs/test/api/src/GlobalComponentIds.c +++ b/vendors/flecs/test/core/src/GlobalComponentIds.c @@ -1,4 +1,4 @@ -#include +#include static ECS_DECLARE(MyTag); static ECS_DECLARE(MyEntity); @@ -7,7 +7,7 @@ static ECS_COMPONENT_DECLARE(Velocity); static ecs_entity_t create_entity(ecs_world_t *world) { - return ecs_new(world, Position); + return ecs_new_w(world, Position); } static @@ -17,7 +17,7 @@ ecs_entity_t create_entity_w_entity(ecs_world_t *world) { static ecs_entity_t create_tag_entity(ecs_world_t *world) { - return ecs_new(world, MyTag); + return ecs_new_w(world, MyTag); } static @@ -128,7 +128,7 @@ void GlobalComponentIds_reuse_300_component_ids(void) { const ecs_world_info_t *info = ecs_get_world_info(world); ecs_entity_t info_last_component_id = info->last_component_id; ecs_entity_t info_last_id = ecs_get_max_id(world); - ecs_entity_t next_id = ecs_new_id(world); + ecs_entity_t next_id = ecs_new(world); ecs_fini(world); @@ -145,7 +145,7 @@ void GlobalComponentIds_reuse_300_component_ids(void) { info = ecs_get_world_info(world); test_int(info->last_component_id, info_last_component_id); test_int(ecs_get_max_id(world), info_last_id); - test_int(next_id, ecs_new_id(world)); + test_int(next_id, ecs_new(world)); ecs_os_free(ids); diff --git a/vendors/flecs/test/api/src/Has.c b/vendors/flecs/test/core/src/Has.c similarity index 81% rename from vendors/flecs/test/api/src/Has.c rename to vendors/flecs/test/core/src/Has.c index c90eeb8a2..67f46f771 100644 --- a/vendors/flecs/test/api/src/Has.c +++ b/vendors/flecs/test/core/src/Has.c @@ -1,15 +1,15 @@ -#include +#include void Has_zero(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); - ecs_has(world, e, 0); + ecs_has_id(world, e, 0); } void Has_1_of_0(void) { @@ -17,7 +17,7 @@ void Has_1_of_0(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(!ecs_has(world, e, Position)); @@ -30,7 +30,7 @@ void Has_1_of_1(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); @@ -58,7 +58,7 @@ void Has_1_of_empty(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(!ecs_has(world, e, Position)); @@ -67,7 +67,7 @@ void Has_1_of_empty(void) { } void TestHas(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -81,7 +81,7 @@ void Has_has_in_progress(void) { ECS_COMPONENT(world, Position); ECS_SYSTEM(world, TestHas, EcsOnUpdate, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert( ecs_has(world, e, Position)); @@ -97,7 +97,7 @@ void Has_has_of_zero(void) { test_expect_abort(); - ecs_has(world, 0, 0); + ecs_has_id(world, 0, 0); } void Has_owns(void) { @@ -106,12 +106,14 @@ void Has_owns(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert( ecs_has(world, e, Position)); test_assert( ecs_owns(world, e, Position)); - ecs_entity_t base = ecs_new(world, Velocity); + ecs_entity_t base = ecs_new_w(world, Velocity); test_assert( ecs_has(world, base, Velocity)); test_assert( ecs_owns(world, base, Velocity)); @@ -128,10 +130,10 @@ void Has_owns_wildcard(void) { ECS_TAG(world, Tag_1); ECS_TAG(world, Tag_2); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); test_assert(e2 != 0); ecs_add_id(world, e1, Tag_1); @@ -154,7 +156,7 @@ void Has_owns_wildcard_pair(void) { ECS_TAG(world, Obj_1); ECS_TAG(world, Obj_2); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add_pair(world, e, Rel, Obj_1); @@ -172,10 +174,10 @@ void Has_owns_wildcard_pair(void) { void Has_has_entity(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); ecs_add_pair(world, e, EcsIsA, base); @@ -190,7 +192,7 @@ void Has_has_entity_0(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); test_expect_abort(); @@ -203,7 +205,7 @@ void Has_has_entity_0_component(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -213,18 +215,21 @@ void Has_has_entity_0_component(void) { void Has_has_entity_owned(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - ecs_entity_t f = ecs_new(world, 0); + ecs_entity_t f = ecs_new(world); test_assert(f != 0); - ecs_entity_t g = ecs_new(world, 0); + ecs_entity_t g = ecs_new(world); test_assert(g != 0); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); + ecs_add_pair(world, f, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, g, EcsOnInstantiate, EcsInherit); + ecs_add_id(world, e, f); ecs_add_id(world, base, g); ecs_add_pair(world, e, EcsIsA, base); @@ -247,7 +252,7 @@ void Has_has_entity_owned_0(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -256,14 +261,15 @@ void Has_has_entity_owned_0(void) { } void Has_has_entity_owned_0_component(void) { + install_test_abort(); ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - test_assert( ecs_owns_id(world, e, 0) == false); - - ecs_fini(world); + test_expect_abort(); + bool dummy = ecs_owns_id(world, e, 0); + test_assert(!dummy); // should not reach this code } void Has_has_wildcard(void) { @@ -272,10 +278,10 @@ void Has_has_wildcard(void) { ECS_TAG(world, Tag_1); ECS_TAG(world, Tag_2); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); test_assert(e2 != 0); ecs_add_id(world, e1, Tag_1); @@ -298,7 +304,7 @@ void Has_has_wildcard_pair(void) { ECS_TAG(world, Obj_1); ECS_TAG(world, Obj_2); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add_pair(world, e, Rel, Obj_1); diff --git a/vendors/flecs/test/api/src/Hierarchies.c b/vendors/flecs/test/core/src/Hierarchies.c similarity index 72% rename from vendors/flecs/test/api/src/Hierarchies.c rename to vendors/flecs/test/core/src/Hierarchies.c index f2375482c..7f7cb3924 100644 --- a/vendors/flecs/test/api/src/Hierarchies.c +++ b/vendors/flecs/test/core/src/Hierarchies.c @@ -1,4 +1,4 @@ -#include +#include void Hierarchies_setup(void) { ecs_log_set_level(-2); @@ -59,7 +59,7 @@ void Hierarchies_get_object_from_0(void) { void Hierarchies_delete_children(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -78,8 +78,8 @@ void Hierarchies_tree_iter_empty(void) { ECS_ENTITY(world, Parent, 0); - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ ecs_childof(Parent) }); - test_assert( !ecs_term_next(&it)); + ecs_iter_t it = ecs_children(world, Parent); + test_assert( !ecs_children_next(&it)); ecs_fini(world); } @@ -93,15 +93,15 @@ void Hierarchies_tree_iter_1_table(void) { ECS_ENTITY(world, Child2, (ChildOf, Parent)); ECS_ENTITY(world, Child3, (ChildOf, Parent)); - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ ecs_childof(Parent) }); - test_assert( ecs_term_next(&it) == true); + ecs_iter_t it = ecs_children(world, Parent); + test_assert( ecs_children_next(&it) == true); test_int( it.count, 3); test_assert(it.entities[0] == Child1); test_assert(it.entities[1] == Child2); test_assert(it.entities[2] == Child3); - test_assert( !ecs_term_next(&it)); + test_assert( !ecs_children_next(&it)); ecs_fini(world); } @@ -118,19 +118,19 @@ void Hierarchies_tree_iter_2_tables(void) { ECS_ENTITY(world, Child3, (ChildOf, Parent), Position); ECS_ENTITY(world, Child4, (ChildOf, Parent), Position); - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ ecs_childof(Parent) }); + ecs_iter_t it = ecs_children(world, Parent); - test_assert( ecs_term_next(&it) == true); + test_assert( ecs_children_next(&it) == true); test_int( it.count, 2); test_int(it.entities[0], Child1); test_int(it.entities[1], Child2); - test_assert( ecs_term_next(&it) == true); + test_assert( ecs_children_next(&it) == true); test_int( it.count, 2); test_int(it.entities[0], Child3); test_int(it.entities[1], Child4); - test_assert( !ecs_term_next(&it)); + test_assert( !ecs_children_next(&it)); ecs_fini(world); } @@ -140,7 +140,7 @@ void Hierarchies_path_depth_0(void) { ECS_ENTITY(world, Parent, 0); - char *path = ecs_get_fullpath(world, Parent); + char *path = ecs_get_path(world, Parent); test_str(path, "Parent"); ecs_os_free(path); @@ -153,7 +153,7 @@ void Hierarchies_path_depth_1(void) { ECS_ENTITY(world, Parent, 0); ECS_ENTITY(world, Child, (ChildOf, Parent)); - char *path = ecs_get_fullpath(world, Child); + char *path = ecs_get_path(world, Child); test_str(path, "Parent.Child"); ecs_os_free(path); @@ -167,7 +167,7 @@ void Hierarchies_path_depth_2(void) { ECS_ENTITY(world, Child, (ChildOf, Parent)); ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child)); - char *path = ecs_get_fullpath(world, GrandChild); + char *path = ecs_get_path(world, GrandChild); test_str(path, "Parent.Child.GrandChild"); ecs_os_free(path); @@ -239,7 +239,7 @@ void Hierarchies_rel_path_from_root(void) { ECS_ENTITY(world, Parent, 0); - char *path = ecs_get_path(world, 0, Parent); + char *path = ecs_get_path_from(world, 0, Parent); test_str(path, "Parent"); ecs_os_free(path); @@ -251,7 +251,7 @@ void Hierarchies_rel_path_from_self(void) { ECS_ENTITY(world, Parent, 0); - char *path = ecs_get_path(world, Parent, Parent); + char *path = ecs_get_path_from(world, Parent, Parent); test_str(path, ""); ecs_os_free(path); @@ -264,7 +264,7 @@ void Hierarchies_rel_path_depth_1(void) { ECS_ENTITY(world, Parent, 0); ECS_ENTITY(world, Child, (ChildOf, Parent)); - char *path = ecs_get_path(world, Parent, Child); + char *path = ecs_get_path_from(world, Parent, Child); test_str(path, "Child"); ecs_os_free(path); @@ -278,7 +278,7 @@ void Hierarchies_rel_path_depth_2(void) { ECS_ENTITY(world, Child, (ChildOf, Parent)); ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child)); - char *path = ecs_get_path(world, Parent, GrandChild); + char *path = ecs_get_path_from(world, Parent, GrandChild); test_str(path, "Child.GrandChild"); ecs_os_free(path); @@ -292,7 +292,7 @@ void Hierarchies_rel_path_no_match(void) { ECS_ENTITY(world, Parent2, 0); ECS_ENTITY(world, Child, (ChildOf, Parent)); - char *path = ecs_get_path(world, Parent2, Child); + char *path = ecs_get_path_from(world, Parent2, Child); test_str(path, "Parent.Child"); ecs_os_free(path); @@ -357,23 +357,207 @@ void Hierarchies_path_prefix_rel_no_match(void) { void Hierarchies_path_w_number(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, 1000); + ecs_entity_t p = ecs_set_name(world, 0, "1000"); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); ecs_set_name(world, e, "Foo"); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, "1000.Foo"); ecs_os_free(path); ecs_fini(world); } +void Hierarchies_path_w_entity_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, 1000); + ecs_set_name(world, e, "Foo"); + + char *path = ecs_get_path(world, e); + test_str(path, "#1000.Foo"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "foo\\.bar"}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "foo.bar"); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "foo\\.bar") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "foo\\.bar"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_two_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "foo\\.bar\\.woo"}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "foo.bar.woo"); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "foo\\.bar\\.woo") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "foo\\.bar\\.woo"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_two_consecutive_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "foo\\.\\.bar\\.woo"}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "foo..bar.woo"); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "foo\\.\\.bar\\.woo") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "foo\\.\\.bar\\.woo"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_sep_at_begin(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "\\.foo\\.bar"}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), ".foo.bar"); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "\\.foo\\.bar") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "\\.foo\\.bar"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_sep_at_end(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "foo\\.bar\\."}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "foo.bar."); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "foo\\.bar\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "foo\\.bar\\."); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_sep_w_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "parent.foo\\.bar"}); + test_assert(e != 0); + ecs_entity_t p = ecs_lookup(world, "parent"); + test_assert(p != 0); + + test_str(ecs_get_name(world, e), "foo.bar"); + test_assert(ecs_get_parent(world, e) == p); + test_assert(ecs_lookup(world, "parent.foo\\.bar") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "parent.foo\\.bar"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_only_escaped_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "\\."}); + test_assert(e != 0); + + test_str(ecs_get_name(world, e), "."); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "\\."); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_only_escaped_sep_w_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "parent.\\."}); + test_assert(e != 0); + ecs_entity_t p = ecs_lookup(world, "parent"); + test_assert(p != 0); + + test_str(ecs_get_name(world, e), "."); + test_assert(ecs_get_parent(world, e) == p); + test_assert(ecs_lookup(world, "parent.\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "parent.\\."); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_only_escaped_two_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "\\.\\."}); + test_assert(e != 0); + + test_str(ecs_get_name(world, e), ".."); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "\\.\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "\\.\\."); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_only_escaped_two_sep_w_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "parent.\\.\\."}); + test_assert(e != 0); + ecs_entity_t p = ecs_lookup(world, "parent"); + test_assert(p != 0); + + test_str(ecs_get_name(world, e), ".."); + test_assert(ecs_get_parent(world, e) == p); + test_assert(ecs_lookup(world, "parent.\\.\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "parent.\\.\\."); + ecs_os_free(path); + + ecs_fini(world); +} void Hierarchies_lookup_depth_0(void) { ecs_world_t *world = ecs_mini(); ECS_ENTITY(world, Parent, 0); - ecs_entity_t e = ecs_lookup_fullpath(world, "Parent"); + ecs_entity_t e = ecs_lookup(world, "Parent"); test_assert(e == Parent); ecs_fini(world); @@ -385,7 +569,7 @@ void Hierarchies_lookup_depth_1(void) { ECS_ENTITY(world, Parent, 0); ECS_ENTITY(world, Child, (ChildOf, Parent)); - ecs_entity_t e = ecs_lookup_fullpath(world, "Parent.Child"); + ecs_entity_t e = ecs_lookup(world, "Parent.Child"); test_assert(e == Child); ecs_fini(world); @@ -398,7 +582,7 @@ void Hierarchies_lookup_depth_2(void) { ECS_ENTITY(world, Child, (ChildOf, Parent)); ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child)); - ecs_entity_t e = ecs_lookup_fullpath(world, "Parent.Child.GrandChild"); + ecs_entity_t e = ecs_lookup(world, "Parent.Child.GrandChild"); test_assert(e == GrandChild); ecs_fini(world); @@ -410,7 +594,7 @@ void Hierarchies_lookup_rel_0(void) { ECS_ENTITY(world, Parent, 0); ECS_ENTITY(world, Child, (ChildOf, Parent)); - ecs_entity_t e = ecs_lookup_path(world, Parent, "Child"); + ecs_entity_t e = ecs_lookup_from(world, Parent, "Child"); test_assert(e == Child); ecs_fini(world); @@ -423,7 +607,7 @@ void Hierarchies_lookup_rel_1(void) { ECS_ENTITY(world, Child, (ChildOf, Parent)); ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child)); - ecs_entity_t e = ecs_lookup_path(world, Parent, "Child.GrandChild"); + ecs_entity_t e = ecs_lookup_from(world, Parent, "Child.GrandChild"); test_assert(e == GrandChild); ecs_fini(world); @@ -437,7 +621,7 @@ void Hierarchies_lookup_rel_2(void) { ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child)); ECS_ENTITY(world, GrandGrandChild, (ChildOf, Parent.Child.GrandChild)); - ecs_entity_t e = ecs_lookup_path(world, Parent, "Child.GrandChild.GrandGrandChild"); + ecs_entity_t e = ecs_lookup_from(world, Parent, "Child.GrandChild.GrandGrandChild"); test_assert(e == GrandGrandChild); ecs_fini(world); @@ -484,7 +668,7 @@ void Hierarchies_lookup_self(void) { ECS_ENTITY(world, Parent, 0); - ecs_entity_t e = ecs_lookup_path(world, Parent, ""); + ecs_entity_t e = ecs_lookup_from(world, Parent, ""); test_assert(e == Parent); ecs_fini(world); @@ -498,74 +682,43 @@ void Hierarchies_lookup_number(void) { ECS_ENTITY(world, Child, (ChildOf, Parent)); ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child)); - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, 1000); + ecs_entity_t p = ecs_set_name(world, 0, "1000"); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); ecs_set_name(world, e, "Foo"); - ecs_entity_t c = ecs_lookup_path(world, 0, "1000.Foo"); + ecs_entity_t c = ecs_lookup_from(world, 0, "1000.Foo"); test_assert(e == c); ecs_fini(world); } -void Hierarchies_scope_set(void) { +void Hierarchies_lookup_entity_id(void) { ecs_world_t *world = ecs_mini(); - ECS_ENTITY(world, Scope, 0); - - ecs_entity_t old_scope = ecs_set_scope(world, Scope); - test_assert(old_scope == 0); - - ecs_entity_t scope = ecs_get_scope(world); - test_assert(scope == Scope); - - ecs_fini(world); -} - -void Hierarchies_scope_set_w_new(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Scope, 0); - - ecs_entity_t old_scope = ecs_set_scope(world, Scope); - test_assert(old_scope == 0); - - ecs_entity_t e = ecs_new(world, 0); - test_assert(e != 0); - test_assert(ecs_has_pair(world, e, EcsChildOf, Scope)); + ECS_ENTITY(world, Parent, 0); + ECS_ENTITY(world, Parent2, 0); + ECS_ENTITY(world, Child, (ChildOf, Parent)); + ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child)); - old_scope = ecs_set_scope(world, 0); - test_assert(old_scope == Scope); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, 1000); + ecs_set_name(world, e, "Foo"); - e = ecs_new(world, 0); - test_assert(e != 0); - test_assert(ecs_get_type(world, e) == NULL); + ecs_entity_t c = ecs_lookup_from(world, 0, "#1000.Foo"); + test_assert(e == c); ecs_fini(world); } -void Hierarchies_scope_set_w_new_staged(void) { +void Hierarchies_scope_set(void) { ecs_world_t *world = ecs_mini(); ECS_ENTITY(world, Scope, 0); - ecs_defer_begin(world); - ecs_entity_t old_scope = ecs_set_scope(world, Scope); test_assert(old_scope == 0); - ecs_entity_t e1 = ecs_new(world, 0); - test_assert(e1 != 0); - - old_scope = ecs_set_scope(world, 0); - test_assert(old_scope == Scope); - - ecs_entity_t e2 = ecs_new(world, 0); - test_assert(e2 != 0); - - ecs_defer_end(world); - - test_assert(ecs_has_pair(world, e1, EcsChildOf, Scope)); - test_assert(ecs_get_type(world, e2) == NULL); + ecs_entity_t scope = ecs_get_scope(world); + test_assert(scope == Scope); ecs_fini(world); } @@ -591,19 +744,19 @@ void Hierarchies_scope_set_w_lookup(void) { ECS_ENTITY(world, Scope, 0); ECS_ENTITY(world, Child, (ChildOf, Scope)); - test_assert( ecs_lookup_fullpath(world, "Child") == 0); + test_assert( ecs_lookup(world, "Child") == 0); ecs_entity_t old_scope = ecs_set_scope(world, Scope); test_assert(old_scope == 0); - ecs_entity_t e = ecs_lookup_fullpath(world, "Child"); + ecs_entity_t e = ecs_lookup(world, "Child"); test_assert(e != 0); test_assert(e == Child); old_scope = ecs_set_scope(world, 0); test_assert(old_scope == Scope); - test_assert( ecs_lookup_fullpath(world, "Child") == 0); + test_assert( ecs_lookup(world, "Child") == 0); ecs_fini(world); } @@ -615,19 +768,19 @@ void Hierarchies_lookup_in_parent_from_scope(void) { ECS_ENTITY(world, ChildScope, (ChildOf, Scope)); ECS_ENTITY(world, Child, (ChildOf, Scope)); - test_assert( ecs_lookup_fullpath(world, "Child") == 0); + test_assert( ecs_lookup(world, "Child") == 0); ecs_entity_t old_scope = ecs_set_scope(world, ChildScope); test_assert(old_scope == 0); - ecs_entity_t e = ecs_lookup_fullpath(world, "Child"); + ecs_entity_t e = ecs_lookup(world, "Child"); test_assert(e != 0); test_assert(e == Child); old_scope = ecs_set_scope(world, 0); test_assert(old_scope == ChildScope); - test_assert( ecs_lookup_fullpath(world, "Child") == 0); + test_assert( ecs_lookup(world, "Child") == 0); ecs_fini(world); } @@ -642,7 +795,7 @@ void Hierarchies_lookup_in_root_from_scope(void) { ecs_entity_t old_scope = ecs_set_scope(world, ChildScope); test_assert(old_scope == 0); - ecs_entity_t e = ecs_lookup_fullpath(world, "Child"); + ecs_entity_t e = ecs_lookup(world, "Child"); test_assert(e != 0); test_assert(e == Child); @@ -662,17 +815,17 @@ void Hierarchies_scope_component(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_lookup_fullpath(world, "Position"); + ecs_entity_t e = ecs_lookup(world, "Position"); test_assert(e != 0); test_assert(e == ecs_id(Position)); old_scope = ecs_set_scope(world, 0); test_assert(old_scope == Scope); - e = ecs_lookup_fullpath(world, "Position"); + e = ecs_lookup(world, "Position"); test_assert(e == 0); - e = ecs_lookup_fullpath(world, "Scope.Position"); + e = ecs_lookup(world, "Scope.Position"); test_assert(e == ecs_id(Position)); ecs_fini(world); @@ -681,7 +834,7 @@ void Hierarchies_scope_component(void) { void Hierarchies_scope_component_no_macro(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t Scope = ecs_new_id(world); + ecs_entity_t Scope = ecs_new(world); ecs_entity_t old_scope = ecs_set_scope(world, Scope); test_assert(old_scope == 0); @@ -706,7 +859,7 @@ void Hierarchies_fullpath_for_core(void) { ECS_ENTITY(world, Parent, 0); ECS_ENTITY(world, Child, (ChildOf, Parent)); - char *path = ecs_get_fullpath(world, ecs_id(EcsComponent)); + char *path = ecs_get_path(world, ecs_id(EcsComponent)); test_str(path, "Component"); ecs_os_free(path); @@ -724,7 +877,7 @@ void Hierarchies_new_from_path_depth_0(void) { test_assert(type != NULL); test_int(type->count, 1); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, "foo"); ecs_os_free(path); @@ -742,7 +895,7 @@ void Hierarchies_new_from_path_depth_1(void) { test_assert(type != NULL); test_int(type->count, 2); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, "foo.bar"); ecs_os_free(path); @@ -760,7 +913,7 @@ void Hierarchies_new_from_path_depth_2(void) { test_assert(type != NULL); test_int(type->count, 2); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, "foo.bar.hello"); ecs_os_free(path); @@ -816,7 +969,7 @@ void Hierarchies_new_from_path_existing_depth_2(void) { void Hierarchies_add_path_depth_0(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t id = ecs_new(world, 0); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_entity_t e = ecs_add_path(world, id, 0, "foo"); @@ -828,7 +981,7 @@ void Hierarchies_add_path_depth_0(void) { test_assert(type != NULL); test_int(type->count, 1); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, "foo"); ecs_os_free(path); @@ -838,7 +991,7 @@ void Hierarchies_add_path_depth_0(void) { void Hierarchies_add_path_depth_1(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t id = ecs_new(world, 0); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_entity_t e = ecs_add_path(world, id, 0, "foo.bar"); @@ -850,7 +1003,7 @@ void Hierarchies_add_path_depth_1(void) { test_assert(type != NULL); test_int(type->count, 2); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, "foo.bar"); ecs_os_free(path); @@ -860,7 +1013,7 @@ void Hierarchies_add_path_depth_1(void) { void Hierarchies_add_path_depth_2(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t id = ecs_new(world, 0); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_entity_t e = ecs_add_path(world, id, 0, "foo.bar.hello"); @@ -872,7 +1025,7 @@ void Hierarchies_add_path_depth_2(void) { test_assert(type != NULL); test_int(type->count, 2); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, "foo.bar.hello"); ecs_os_free(path); @@ -888,7 +1041,7 @@ void Hierarchies_add_path_existing_depth_0(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "foo"); - ecs_entity_t id = ecs_new(world, 0); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); test_expect_abort(); @@ -903,7 +1056,7 @@ void Hierarchies_add_path_existing_depth_1(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "bar"); - ecs_entity_t id = ecs_new(world, 0); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); test_expect_abort(); @@ -918,7 +1071,7 @@ void Hierarchies_add_path_existing_depth_2(void) { test_assert(e != 0); test_str(ecs_get_name(world, e), "hello"); - ecs_entity_t id = ecs_new(world, 0); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); test_expect_abort(); @@ -934,14 +1087,14 @@ void Hierarchies_add_path_from_scope(void) { ecs_set_scope(world, e); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); test_assert(id != 0); ecs_entity_t f = ecs_add_path(world, id, 0, "hello.world"); test_assert(f != 0); test_str(ecs_get_name(world, f), "world"); - char *path = ecs_get_fullpath(world, f); + char *path = ecs_get_path(world, f); test_str(path, "hello.world"); ecs_os_free(path); @@ -963,7 +1116,7 @@ void Hierarchies_add_path_from_scope_new_entity(void) { test_assert(id != 0); test_str(ecs_get_name(world, id), "world"); - char *path = ecs_get_fullpath(world, id); + char *path = ecs_get_path(world, id); test_str(path, "foo.bar.hello.world"); ecs_os_free(path); @@ -975,8 +1128,8 @@ void Hierarchies_add_path_from_scope_new_entity(void) { void Hierarchies_add_root_path_to_child(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); - ecs_entity_t child = ecs_new_entity(world, "parent.child"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + ecs_entity_t child = ecs_entity(world, { .name = "parent.child" }); test_assert(ecs_get_target(world, child, EcsChildOf, 0) == parent); test_str(ecs_get_name(world, child), "child"); @@ -990,10 +1143,10 @@ void Hierarchies_add_root_path_to_child(void) { void Hierarchies_add_parent_path_from_root_to_child(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t gparent = ecs_new_entity(world, "gparent"); - ecs_entity_t parent = ecs_new_entity(world, "gparent.parent"); + ecs_entity_t gparent = ecs_entity(world, { .name = "gparent" }); + ecs_entity_t parent = ecs_entity(world, { .name = "gparent.parent" }); test_assert(ecs_get_target(world, parent, EcsChildOf, 0) == gparent); - ecs_entity_t child = ecs_new_entity(world, "gparent.parent.child"); + ecs_entity_t child = ecs_entity(world, { .name = "gparent.parent.child" }); test_assert(ecs_get_target(world, child, EcsChildOf, 0) == parent); test_str(ecs_get_name(world, child), "child"); @@ -1004,29 +1157,10 @@ void Hierarchies_add_parent_path_from_root_to_child(void) { ecs_fini(world); } -void Hierarchies_new_w_child_in_root(void) { - ecs_world_t *world = ecs_mini(); - - ecs_entity_t scope = ecs_new(world, 0); - ecs_entity_t parent = ecs_new(world, 0); - - ecs_set_scope(world, scope); - - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - test_assert( ecs_has_pair(world, child, EcsChildOf, parent)); - test_assert( !ecs_has_pair(world, child, EcsChildOf, scope)); - - ecs_entity_t child_2 = ecs_new(world, 0); - test_assert( !ecs_has_pair(world, child_2, EcsChildOf, parent)); - test_assert( ecs_has_pair(world, child_2, EcsChildOf, scope)); - - ecs_fini(world); -} - void Hierarchies_delete_child(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1045,7 +1179,7 @@ void Hierarchies_delete_child(void) { void Hierarchies_delete_2_children(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1071,7 +1205,7 @@ void Hierarchies_delete_2_children_different_type(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1096,7 +1230,7 @@ void Hierarchies_delete_2_children_different_type(void) { void Hierarchies_delete_tree_2_levels(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1117,7 +1251,7 @@ void Hierarchies_delete_tree_2_levels(void) { void Hierarchies_delete_tree_3_levels(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1139,76 +1273,6 @@ void Hierarchies_delete_tree_3_levels(void) { ecs_fini(world); } -void Hierarchies_delete_tree_count_tables(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_new(world, Position); - test_assert(parent != 0); - - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - test_assert(ecs_get_type(world, child) != NULL); - ecs_add(world, child, Position); - - ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child); - test_assert(ecs_get_type(world, grand_child) != NULL); - ecs_add(world, grand_child, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - test_int(it.table_count, 3); - ecs_iter_fini(&it); - - ecs_delete(world, parent); - - test_bool(ecs_is_alive(world, parent), false); - test_bool(ecs_is_alive(world, child), false); - test_bool(ecs_is_alive(world, grand_child), false); - - it = ecs_query_iter(world, q); - test_int(it.table_count, 0); - ecs_iter_fini(&it); - - ecs_fini(world); -} - -void Hierarchies_delete_tree_staged(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t parent = ecs_new(world, Position); - test_assert(parent != 0); - - ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); - test_assert(ecs_get_type(world, child) != NULL); - ecs_add(world, child, Position); - - ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child); - test_assert(ecs_get_type(world, grand_child) != NULL); - ecs_add(world, grand_child, Position); - - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); - test_int(it.table_count, 3); - ecs_iter_fini(&it); - - ecs_defer_begin(world); - ecs_delete(world, parent); - ecs_defer_end(world); - - test_bool(ecs_is_alive(world, parent), false); - test_bool(ecs_is_alive(world, child), false); - test_bool(ecs_is_alive(world, grand_child), false); - - it = ecs_query_iter(world, q); - test_int(it.table_count, 0); - ecs_iter_fini(&it); - - ecs_fini(world); -} - void Hierarchies_delete_tree_empty_table(void) { ecs_world_t *world = ecs_mini(); @@ -1230,15 +1294,18 @@ void Hierarchies_delete_tree_recreate(void) { ECS_COMPONENT(world, Position); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); test_assert(parent != 0); test_assert(child != 0); test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - ecs_delete_children(world, parent); + ecs_delete_with(world, ecs_childof(parent)); - ecs_new(world, Position); + ecs_iter_t it = ecs_children(world, parent); + test_assert(!ecs_children_next(&it)); + + ecs_new_w(world, Position); ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent); test_assert(child_2 != 0); @@ -1294,16 +1361,16 @@ void Hierarchies_scope_iter_after_delete_tree(void) { ECS_COMPONENT(world, Position); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); test_assert(parent != 0); test_assert(child != 0); test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - ecs_delete_children(world, parent); + ecs_delete_with(world, ecs_childof(parent)); - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ ecs_childof(parent) }); - test_assert(!ecs_term_next(&it)); + ecs_iter_t it = ecs_children(world, parent); + test_assert(!ecs_children_next(&it)); ecs_fini(world); } @@ -1313,13 +1380,16 @@ void Hierarchies_add_child_after_delete_tree(void) { ECS_COMPONENT(world, Position); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); test_assert(parent != 0); test_assert(child != 0); test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); - ecs_delete_children(world, parent); + ecs_delete_with(world, ecs_childof(parent)); + + ecs_iter_t it = ecs_children(world, parent); + test_assert(!ecs_children_next(&it)); child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1343,7 +1413,7 @@ void Hierarchies_delete_tree_w_onremove(void) { ECS_OBSERVER(world, RemovePosition, EcsOnRemove, Position); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent); ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1382,7 +1452,7 @@ void Hierarchies_delete_tree_w_dtor(void) { .dtor = ecs_dtor(Position) }); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent); ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1411,7 +1481,7 @@ void Hierarchies_add_child_to_recycled_parent(void) { ECS_COMPONENT(world, Position); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1422,7 +1492,7 @@ void Hierarchies_add_child_to_recycled_parent(void) { test_assert( !ecs_is_alive(world, parent)); test_assert( !ecs_is_alive(world, child)); - ecs_entity_t new_parent = ecs_new(world, 0); + ecs_entity_t new_parent = ecs_new(world); test_assert(new_parent != 0); /* Make sure we have a recycled identifier */ test_assert(ecs_entity_t_lo(new_parent) != new_parent); @@ -1441,14 +1511,14 @@ void Hierarchies_get_type_after_recycled_parent_add(void) { ECS_COMPONENT(world, Position); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t parent = ecs_new(world); test_assert(parent != 0); test_assert( ecs_get_type(world, parent) == NULL); ecs_delete(world, parent); test_assert( !ecs_is_alive(world, parent)); - parent = ecs_new(world, Position); + parent = ecs_new_w(world, Position); test_assert(parent != 0); test_assert(ecs_entity_t_lo(parent) != parent); // Ensure recycled test_assert( ecs_get_type(world, parent) != NULL); @@ -1460,140 +1530,6 @@ void Hierarchies_get_type_after_recycled_parent_add(void) { ecs_fini(world); } -void Hierarchies_rematch_after_add_to_recycled_parent(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag, Position(parent)"); - - ecs_entity_t parent = ecs_new(world, 0); - test_assert(parent != 0); - - ecs_delete(world, parent); - test_assert( !ecs_is_alive(world, parent)); - - parent = ecs_new(world, 0); - test_assert(parent != 0); - - ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent); - test_assert(e != 0); - test_assert( ecs_has_pair(world, e, EcsChildOf, parent)); - ecs_add(world, e, Tag); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), false); - - ecs_set(world, parent, Position, {10, 20}); - - ecs_run_aperiodic(world, 0); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - - const Position *p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_assert(ecs_field_src(&it, 2) == parent); - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - -void Hierarchies_cascade_after_recycled_parent_change(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_TAG(world, Tag); - - ecs_query_t *q = ecs_query_new(world, "Tag, ?Position(parent|cascade)"); - - ecs_entity_t parent = ecs_new(world, 0); - test_assert(parent != 0); - ecs_entity_t child = ecs_new(world, 0); - test_assert(child != 0); - - ecs_delete(world, parent); - test_assert( !ecs_is_alive(world, parent)); - ecs_delete(world, child); - test_assert( !ecs_is_alive(world, child)); - - parent = ecs_new_w_id(world, Tag); - test_assert(parent != 0); - child = ecs_new_w_id(world, Tag); - test_assert(child != 0); - ecs_add_pair(world, child, EcsChildOf, parent); - - ecs_entity_t e = ecs_new_w_id(world, Tag); - test_assert(e != 0); - - ecs_add_pair(world, e, EcsChildOf, child); - test_assert( ecs_has_pair(world, e, EcsChildOf, child)); - - ecs_iter_t it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_assert(it.entities[0] == parent); - test_assert(ecs_field_src(&it, 2) == 0); - const Position *p = ecs_field(&it, Position, 2); - test_assert(p == NULL); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_assert(it.entities[0] == child); - test_assert(ecs_field_src(&it, 2) == 0); - p = ecs_field(&it, Position, 2); - test_assert(p == NULL); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_assert(it.entities[0] == e); - test_assert(ecs_field_src(&it, 2) == 0); - p = ecs_field(&it, Position, 2); - test_assert(p == NULL); - - test_bool(ecs_query_next(&it), false); - - ecs_set(world, parent, Position, {10, 20}); - ecs_set(world, child, Position, {20, 30}); - - ecs_run_aperiodic(world, 0); - - it = ecs_query_iter(world, q); - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_assert(it.entities[0] == parent); - test_assert(ecs_field_src(&it, 2) == 0); - p = ecs_field(&it, Position, 2); - test_assert(p == NULL); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_assert(it.entities[0] == child); - test_assert(ecs_field_src(&it, 2) == parent); - p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); - - test_bool(ecs_query_next(&it), true); - test_int(it.count, 1); - test_assert(it.entities[0] == e); - test_assert(ecs_field_src(&it, 2) == child); - p = ecs_field(&it, Position, 2); - test_assert(p != NULL); - test_int(p->x, 20); - test_int(p->y, 30); - - test_bool(ecs_query_next(&it), false); - - ecs_fini(world); -} - void Hierarchies_long_name_depth_0(void) { ecs_world_t *world = ecs_mini(); @@ -1606,10 +1542,10 @@ void Hierarchies_long_name_depth_0(void) { test_str(ecs_get_name(world, parent), parent_name); - ecs_entity_t e = ecs_lookup_fullpath(world, parent_name); + ecs_entity_t e = ecs_lookup(world, parent_name); test_assert(e == parent); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, parent_name); ecs_os_free(path); @@ -1632,7 +1568,7 @@ void Hierarchies_long_name_depth_1(void) { test_assert(strlen(child_name) >= 64); ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = child_name, - .add = { ecs_pair(EcsChildOf, parent) } + .parent = parent }); test_str(ecs_get_name(world, child), child_name); @@ -1640,10 +1576,10 @@ void Hierarchies_long_name_depth_1(void) { "a_parent_entity_with_an_identifier_longer_than_sixty_four_characters." "a_child_entity_with_an_identifier_longer_than_sixty_four_characters"; - ecs_entity_t e = ecs_lookup_fullpath(world, search_path); + ecs_entity_t e = ecs_lookup(world, search_path); test_assert(e == child); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, search_path); ecs_os_free(path); @@ -1666,7 +1602,7 @@ void Hierarchies_long_name_depth_2(void) { test_assert(strlen(child_name) >= 64); ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = child_name, - .add = { ecs_pair(EcsChildOf, parent) } + .parent = parent }); test_str(ecs_get_name(world, child), child_name); @@ -1675,7 +1611,7 @@ void Hierarchies_long_name_depth_2(void) { test_assert(strlen(grand_child_name) >= 64); ecs_entity_t grand_child = ecs_entity_init(world, &(ecs_entity_desc_t){ .name = grand_child_name, - .add = { ecs_pair(EcsChildOf, child) } + .parent = child }); test_str(ecs_get_name(world, grand_child), grand_child_name); @@ -1684,10 +1620,10 @@ void Hierarchies_long_name_depth_2(void) { "a_child_entity_with_an_identifier_longer_than_sixty_four_characters." "a_grand_child_entity_with_an_identifier_longer_than_sixty_four_characters"; - ecs_entity_t e = ecs_lookup_fullpath(world, search_path); + ecs_entity_t e = ecs_lookup(world, search_path); test_assert(e == grand_child); - char *path = ecs_get_fullpath(world, e); + char *path = ecs_get_path(world, e); test_str(path, search_path); ecs_os_free(path); @@ -1697,10 +1633,10 @@ void Hierarchies_long_name_depth_2(void) { void Hierarchies_ensure_1_parent_after_adding_2(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent_1 = ecs_new(world, 0); + ecs_entity_t parent_1 = ecs_new(world); test_assert(parent_1 != 0); - ecs_entity_t parent_2 = ecs_new(world, 0); + ecs_entity_t parent_2 = ecs_new(world); test_assert(parent_2 != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent_1); @@ -1717,10 +1653,10 @@ void Hierarchies_ensure_1_parent_after_adding_2(void) { void Hierarchies_ensure_child_alive_after_deleting_prev_parent(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent_1 = ecs_new(world, 0); + ecs_entity_t parent_1 = ecs_new(world); test_assert(parent_1 != 0); - ecs_entity_t parent_2 = ecs_new(world, 0); + ecs_entity_t parent_2 = ecs_new(world); test_assert(parent_2 != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent_1); @@ -1741,30 +1677,30 @@ void Hierarchies_ensure_child_alive_after_deleting_prev_parent(void) { void Hierarchies_lookup_after_root_to_parent_move(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t p = ecs_new_entity(world, "Parent"); - ecs_entity_t c = ecs_new_entity(world, "Child"); + ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); + ecs_entity_t c = ecs_entity(world, { .name = "Child" }); test_str("Parent", ecs_get_name(world, p)); test_str("Child", ecs_get_name(world, c)); - test_uint(p, ecs_lookup_fullpath(world, "Parent")); - test_uint(c, ecs_lookup_fullpath(world, "Child")); + test_uint(p, ecs_lookup(world, "Parent")); + test_uint(c, ecs_lookup(world, "Child")); - char *path = ecs_get_fullpath(world, p); + char *path = ecs_get_path(world, p); test_str(path, "Parent"); ecs_os_free(path); - path = ecs_get_fullpath(world, c); + path = ecs_get_path(world, c); test_str(path, "Child"); ecs_os_free(path); ecs_add_pair(world, c, EcsChildOf, p); test_str("Child", ecs_get_name(world, c)); - test_uint(0, ecs_lookup_fullpath(world, "Child")); - test_uint(c, ecs_lookup_fullpath(world, "Parent.Child")); + test_uint(0, ecs_lookup(world, "Child")); + test_uint(c, ecs_lookup(world, "Parent.Child")); - path = ecs_get_fullpath(world, c); + path = ecs_get_path(world, c); test_str(path, "Parent.Child"); ecs_os_free(path); @@ -1774,31 +1710,31 @@ void Hierarchies_lookup_after_root_to_parent_move(void) { void Hierarchies_lookup_after_parent_to_root_move(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t p = ecs_new_entity(world, "Parent"); - ecs_entity_t c = ecs_new_entity(world, "Parent.Child"); + ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); + ecs_entity_t c = ecs_entity(world, { .name = "Parent.Child" }); test_str("Parent", ecs_get_name(world, p)); test_str("Child", ecs_get_name(world, c)); - test_uint(p, ecs_lookup_fullpath(world, "Parent")); - test_uint(0, ecs_lookup_fullpath(world, "Child")); - test_uint(c, ecs_lookup_fullpath(world, "Parent.Child")); + test_uint(p, ecs_lookup(world, "Parent")); + test_uint(0, ecs_lookup(world, "Child")); + test_uint(c, ecs_lookup(world, "Parent.Child")); - char *path = ecs_get_fullpath(world, p); + char *path = ecs_get_path(world, p); test_str(path, "Parent"); ecs_os_free(path); - path = ecs_get_fullpath(world, c); + path = ecs_get_path(world, c); test_str(path, "Parent.Child"); ecs_os_free(path); ecs_remove_pair(world, c, EcsChildOf, p); test_str("Child", ecs_get_name(world, c)); - test_uint(c, ecs_lookup_fullpath(world, "Child")); - test_uint(0, ecs_lookup_fullpath(world, "Parent.Child")); + test_uint(c, ecs_lookup(world, "Child")); + test_uint(0, ecs_lookup(world, "Parent.Child")); - path = ecs_get_fullpath(world, c); + path = ecs_get_path(world, c); test_str(path, "Child"); ecs_os_free(path); @@ -1808,35 +1744,35 @@ void Hierarchies_lookup_after_parent_to_root_move(void) { void Hierarchies_lookup_after_parent_to_parent_move(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t p = ecs_new_entity(world, "Parent"); - ecs_entity_t p2 = ecs_new_entity(world, "Parent2"); - ecs_entity_t c = ecs_new_entity(world, "Parent.Child"); + ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); + ecs_entity_t p2 = ecs_entity(world, { .name = "Parent2" }); + ecs_entity_t c = ecs_entity(world, { .name = "Parent.Child" }); test_str("Parent", ecs_get_name(world, p)); test_str("Parent2", ecs_get_name(world, p2)); test_str("Child", ecs_get_name(world, c)); - test_uint(p, ecs_lookup_fullpath(world, "Parent")); - test_uint(0, ecs_lookup_fullpath(world, "Child")); - test_uint(c, ecs_lookup_fullpath(world, "Parent.Child")); - test_uint(0, ecs_lookup_fullpath(world, "Parent2.Child")); + test_uint(p, ecs_lookup(world, "Parent")); + test_uint(0, ecs_lookup(world, "Child")); + test_uint(c, ecs_lookup(world, "Parent.Child")); + test_uint(0, ecs_lookup(world, "Parent2.Child")); - char *path = ecs_get_fullpath(world, p); + char *path = ecs_get_path(world, p); test_str(path, "Parent"); ecs_os_free(path); - path = ecs_get_fullpath(world, c); + path = ecs_get_path(world, c); test_str(path, "Parent.Child"); ecs_os_free(path); ecs_add_pair(world, c, EcsChildOf, p2); test_str("Child", ecs_get_name(world, c)); - test_uint(0, ecs_lookup_fullpath(world, "Child")); - test_uint(0, ecs_lookup_fullpath(world, "Parent.Child")); - test_uint(c, ecs_lookup_fullpath(world, "Parent2.Child")); + test_uint(0, ecs_lookup(world, "Child")); + test_uint(0, ecs_lookup(world, "Parent.Child")); + test_uint(c, ecs_lookup(world, "Parent2.Child")); - path = ecs_get_fullpath(world, c); + path = ecs_get_path(world, c); test_str(path, "Parent2.Child"); ecs_os_free(path); @@ -1846,20 +1782,20 @@ void Hierarchies_lookup_after_parent_to_parent_move(void) { void Hierarchies_lookup_after_clear_from_root(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_entity(world, "Child"); + ecs_entity_t c = ecs_entity(world, { .name = "Child" }); test_str("Child", ecs_get_name(world, c)); - test_uint(c, ecs_lookup_fullpath(world, "Child")); + test_uint(c, ecs_lookup(world, "Child")); - char *path = ecs_get_fullpath(world, c); + char *path = ecs_get_path(world, c); test_str(path, "Child"); ecs_os_free(path); ecs_clear(world, c); test_str(NULL, ecs_get_name(world, c)); - test_uint(0, ecs_lookup_fullpath(world, "Child")); + test_uint(0, ecs_lookup(world, "Child")); ecs_fini(world); } @@ -1867,29 +1803,29 @@ void Hierarchies_lookup_after_clear_from_root(void) { void Hierarchies_lookup_after_clear_from_parent(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t p = ecs_new_entity(world, "Parent"); - ecs_entity_t c = ecs_new_entity(world, "Parent.Child"); + ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); + ecs_entity_t c = ecs_entity(world, { .name = "Parent.Child" }); test_str("Parent", ecs_get_name(world, p)); test_str("Child", ecs_get_name(world, c)); - test_uint(p, ecs_lookup_fullpath(world, "Parent")); - test_uint(0, ecs_lookup_fullpath(world, "Child")); - test_uint(c, ecs_lookup_fullpath(world, "Parent.Child")); + test_uint(p, ecs_lookup(world, "Parent")); + test_uint(0, ecs_lookup(world, "Child")); + test_uint(c, ecs_lookup(world, "Parent.Child")); - char *path = ecs_get_fullpath(world, p); + char *path = ecs_get_path(world, p); test_str(path, "Parent"); ecs_os_free(path); - path = ecs_get_fullpath(world, c); + path = ecs_get_path(world, c); test_str(path, "Parent.Child"); ecs_os_free(path); ecs_clear(world, c); test_str(NULL, ecs_get_name(world, c)); - test_uint(0, ecs_lookup_fullpath(world, "Child")); - test_uint(0, ecs_lookup_fullpath(world, "Parent.Child")); + test_uint(0, ecs_lookup(world, "Child")); + test_uint(0, ecs_lookup(world, "Parent.Child")); ecs_fini(world); } @@ -1897,19 +1833,19 @@ void Hierarchies_lookup_after_clear_from_parent(void) { void Hierarchies_lookup_after_delete_from_root(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_entity(world, "Child"); + ecs_entity_t c = ecs_entity(world, { .name = "Child" }); test_str("Child", ecs_get_name(world, c)); - test_uint(c, ecs_lookup_fullpath(world, "Child")); + test_uint(c, ecs_lookup(world, "Child")); - char *path = ecs_get_fullpath(world, c); + char *path = ecs_get_path(world, c); test_str(path, "Child"); ecs_os_free(path); ecs_delete(world, c); - test_uint(0, ecs_lookup_fullpath(world, "Child")); + test_uint(0, ecs_lookup(world, "Child")); ecs_fini(world); } @@ -1917,28 +1853,28 @@ void Hierarchies_lookup_after_delete_from_root(void) { void Hierarchies_lookup_after_delete_from_parent(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t p = ecs_new_entity(world, "Parent"); - ecs_entity_t c = ecs_new_entity(world, "Parent.Child"); + ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); + ecs_entity_t c = ecs_entity(world, { .name = "Parent.Child" }); test_str("Parent", ecs_get_name(world, p)); test_str("Child", ecs_get_name(world, c)); - test_uint(p, ecs_lookup_fullpath(world, "Parent")); - test_uint(0, ecs_lookup_fullpath(world, "Child")); - test_uint(c, ecs_lookup_fullpath(world, "Parent.Child")); + test_uint(p, ecs_lookup(world, "Parent")); + test_uint(0, ecs_lookup(world, "Child")); + test_uint(c, ecs_lookup(world, "Parent.Child")); - char *path = ecs_get_fullpath(world, p); + char *path = ecs_get_path(world, p); test_str(path, "Parent"); ecs_os_free(path); - path = ecs_get_fullpath(world, c); + path = ecs_get_path(world, c); test_str(path, "Parent.Child"); ecs_os_free(path); ecs_delete(world, c); - test_uint(0, ecs_lookup_fullpath(world, "Child")); - test_uint(0, ecs_lookup_fullpath(world, "Parent.Child")); + test_uint(0, ecs_lookup(world, "Child")); + test_uint(0, ecs_lookup(world, "Parent.Child")); ecs_fini(world); } @@ -1946,18 +1882,18 @@ void Hierarchies_lookup_after_delete_from_parent(void) { void Hierarchies_defer_batch_remove_name_w_add_childof(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_entity(world, "e"); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); test_assert(e != 0); test_str("e", ecs_get_name(world, e)); - test_assert(ecs_lookup_fullpath(world, "e") == e); + test_assert(ecs_lookup(world, "e") == e); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_defer_begin(world); ecs_clear(world, e); ecs_add_pair(world, e, EcsChildOf, parent); ecs_defer_end(world); - test_assert(ecs_lookup_fullpath(world, "e") == 0); + test_assert(ecs_lookup(world, "e") == 0); test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); test_assert(ecs_get_name(world, e) == NULL); @@ -1967,7 +1903,7 @@ void Hierarchies_defer_batch_remove_name_w_add_childof(void) { void Hierarchies_defer_batch_remove_childof_w_add_name(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent); test_assert(e != 0); @@ -1977,7 +1913,7 @@ void Hierarchies_defer_batch_remove_childof_w_add_name(void) { ecs_defer_end(world); test_assert(ecs_get_name(world, e) != NULL); - test_assert(ecs_lookup_fullpath(world, "e") == e); + test_assert(ecs_lookup(world, "e") == e); test_assert(!ecs_has_pair(world, e, EcsChildOf, parent)); ecs_fini(world); diff --git a/vendors/flecs/test/core/src/Id.c b/vendors/flecs/test/core/src/Id.c new file mode 100644 index 000000000..6a55fa697 --- /dev/null +++ b/vendors/flecs/test/core/src/Id.c @@ -0,0 +1,384 @@ +#include + +void Id_0_is_wildcard(void) { + test_assert( !ecs_id_is_wildcard(0) ); +} + +void Id_wildcard_is_wildcard(void) { + test_assert( ecs_id_is_wildcard(EcsWildcard) ); +} + +void Id_any_is_wildcard(void) { + test_assert( ecs_id_is_wildcard(EcsAny) ); +} + +void Id_entity_is_wildcard(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t e = ecs_new(world); + test_assert( !ecs_id_is_wildcard(e) ); + ecs_fini(world); +} + +void Id_pair_is_wildcard(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); + test_assert( !ecs_id_is_wildcard(ecs_pair(r, o)) ); + ecs_fini(world); +} + +void Id_pair_w_rel_wildcard_is_wildcard(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t o = ecs_new(world); + test_assert( ecs_id_is_wildcard(ecs_pair(EcsWildcard, o)) ); + ecs_fini(world); +} + +void Id_pair_w_obj_wildcard_is_wildcard(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + test_assert( ecs_id_is_wildcard(ecs_pair(r, EcsWildcard)) ); + ecs_fini(world); +} + +void Id_pair_w_wildcard_wildcard_is_wildcard(void) { + test_assert( ecs_id_is_wildcard(ecs_pair(EcsWildcard, EcsWildcard)) ); +} + +void Id_pair_w_rel_any_is_wildcard(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t o = ecs_new(world); + test_assert( ecs_id_is_wildcard(ecs_pair(EcsAny, o)) ); + ecs_fini(world); +} + +void Id_pair_w_obj_any_is_wildcard(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + test_assert( ecs_id_is_wildcard(ecs_pair(r, EcsAny)) ); + ecs_fini(world); +} + +void Id_pair_w_any_any_is_wildcard(void) { + test_assert( ecs_id_is_wildcard(ecs_pair(EcsAny, EcsAny)) ); +} + +void Id_pair_w_override_is_wildcard(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + test_assert( !ecs_id_is_wildcard(ECS_AUTO_OVERRIDE | ecs_pair(r, EcsWildcard))); + ecs_fini(world); +} + +void Id_pair_w_toggle_is_wildcard(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + test_assert( !ecs_id_is_wildcard(ECS_TOGGLE | ecs_pair(r, EcsWildcard))); + ecs_fini(world); +} + +void Id_tag_id_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t t = ecs_new(world); + test_assert( ecs_id_is_tag(world, t)); + ecs_fini(world); +} + +void Id_component_id_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + test_assert( !ecs_id_is_tag(world, ecs_id(Position))); + ecs_fini(world); +} + +void Id_pair_id_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); + test_assert( ecs_id_is_tag(world, ecs_pair(r, o))); + ecs_fini(world); +} + +void Id_pair_id_w_rel_component_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + ecs_entity_t o = ecs_new(world); + test_assert( !ecs_id_is_tag(world, ecs_pair(ecs_id(Position), o))); + ecs_fini(world); +} + +void Id_pair_id_w_obj_component_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + ecs_entity_t r = ecs_new(world); + test_assert( !ecs_id_is_tag(world, ecs_pair(r, ecs_id(Position)))); + ecs_fini(world); +} + +void Id_pair_id_w_rel_component_obj_wildcard_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + test_assert( !ecs_id_is_tag(world, ecs_pair(ecs_id(Position), EcsWildcard))); + ecs_fini(world); +} + +void Id_pair_id_w_obj_wildcard_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + test_assert( !ecs_id_is_tag(world, ecs_pair(r, EcsWildcard))); + ecs_fini(world); +} + +void Id_pair_id_w_tag_property_w_obj_component_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + ecs_entity_t r = ecs_new_w_id(world, EcsPairIsTag); + test_assert( ecs_id_is_tag(world, ecs_pair(r, ecs_id(Position)))); + ecs_fini(world); +} + +void Id_pair_id_w_tag_property_w_obj_wildcard_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + ecs_entity_t r = ecs_new_w_id(world, EcsPairIsTag); + test_assert( ecs_id_is_tag(world, ecs_pair(r, EcsWildcard))); + ecs_fini(world); +} + +void Id_pair_w_rel_wildcard_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t o = ecs_new(world); + test_assert( !ecs_id_is_tag(world, ecs_pair(EcsWildcard, o))); + ecs_fini(world); +} + +void Id_pair_w_obj_wildcard_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + test_assert( !ecs_id_is_tag(world, ecs_pair(r, EcsWildcard))); + ecs_fini(world); +} + +void Id_pair_w_rel_tag_obj_wildcard_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + ecs_add_id(world, r, EcsPairIsTag); + test_assert( ecs_id_is_tag(world, ecs_pair(r, EcsWildcard))); + ecs_fini(world); +} + +void Id_pair_w_wildcard_wildcard_is_tag(void) { + ecs_world_t *world = ecs_mini(); + test_assert( !ecs_id_is_tag(world, ecs_pair(EcsWildcard, EcsWildcard))); + ecs_fini(world); +} + +void Id_pair_w_rel_any_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t o = ecs_new(world); + test_assert( !ecs_id_is_tag(world, ecs_pair(EcsAny, o))); + ecs_fini(world); +} + +void Id_pair_w_obj_any_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + test_assert( !ecs_id_is_tag(world, ecs_pair(r, EcsAny))); + ecs_fini(world); +} + +void Id_pair_w_rel_tag_obj_any_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ecs_entity_t r = ecs_new(world); + ecs_add_id(world, r, EcsPairIsTag); + test_assert( ecs_id_is_tag(world, ecs_pair(r, EcsAny))); + ecs_fini(world); +} + +void Id_pair_w_any_any_is_tag(void) { + ecs_world_t *world = ecs_mini(); + test_assert( !ecs_id_is_tag(world, ecs_pair(EcsAny, EcsAny))); + ecs_fini(world); +} + +void Id_id_w_override_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + test_assert( ecs_id_is_tag(world, ECS_AUTO_OVERRIDE | ecs_id(Position))); + ecs_fini(world); +} + +void Id_id_w_toggle_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + test_assert( ecs_id_is_tag(world, ECS_TOGGLE | ecs_id(Position))); + ecs_fini(world); +} + +void Id_pair_id_override_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + ecs_entity_t o = ecs_new(world); + test_assert( ecs_id_is_tag(world, ECS_AUTO_OVERRIDE | ecs_pair(ecs_id(Position), o))); + ecs_fini(world); +} + +void Id_pair_id_toggle_is_tag(void) { + ecs_world_t *world = ecs_mini(); + ECS_COMPONENT(world, Position); + ecs_entity_t o = ecs_new(world); + test_assert( ecs_id_is_tag(world, ECS_TOGGLE | ecs_pair(ecs_id(Position), o))); + ecs_fini(world); +} + +void Id_make_pair(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t r = ecs_new(world); + ecs_entity_t t = ecs_new(world); + ecs_id_t id = ecs_make_pair(r, t); + + test_assert( ecs_pair_first(world, id) == r); + test_assert( ecs_pair_second(world, id) == t); + + ecs_fini(world); +} + +void Id_make_pair_of_pair(void) { + install_test_abort(); + ecs_world_t *world = ecs_mini(); + + ecs_entity_t r = ecs_new(world); + ecs_entity_t t = ecs_new(world); + ecs_id_t id = ecs_make_pair(r, t); + + test_expect_abort(); + ecs_make_pair(id, t); +} + +void Id_make_pair_of_pair_tgt(void) { + install_test_abort(); + ecs_world_t *world = ecs_mini(); + + ecs_entity_t r = ecs_new(world); + ecs_entity_t t = ecs_new(world); + ecs_id_t id = ecs_make_pair(r, t); + + test_expect_abort(); + ecs_make_pair(r, id); +} + +void Id_0_entity(void) { + ecs_world_t *world = ecs_mini(); + + ecs_id_t id = ecs_id_from_str(world, "#0"); + test_assert(id == 0); + + ecs_fini(world); +} + +void Id_entity_from_str(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_id_t id = ecs_id_from_str(world, "Foo"); + test_assert(id != 0); + test_assert(id == Foo); + + ecs_fini(world); +} + +void Id_unresolved_entity_from_str(void) { + ecs_world_t *world = ecs_mini(); + + ecs_id_t id = ecs_id_from_str(world, "Foo"); + test_assert(id == 0); + + ecs_fini(world); +} + +void Id_scoped_entity_from_str(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t parent = ecs_entity(world, {.name = "Parent"}); + ecs_entity_t child = ecs_entity(world, {.name = "Child", .parent = parent}); + + ecs_id_t id = ecs_id_from_str(world, "Parent.Child"); + test_assert(id != 0); + test_assert(id == child); + + ecs_fini(world); +} + +void Id_template_entity_from_str(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t foo = ecs_entity(world, {.name = "Foo"}); + + ecs_id_t id = ecs_id_from_str(world, "Foo"); + test_assert(id != 0); + test_assert(id == foo); + + ecs_fini(world); +} + +void Id_pair_from_str(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_id_t id = ecs_id_from_str(world, "(Rel, Tgt)"); + test_assert(id != 0); + test_assert(id == ecs_pair(Rel, Tgt)); + + ecs_fini(world); +} + +void Id_unresolved_pair_from_str(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_id_t id = ecs_id_from_str(world, "(Rel, Tgt)"); + test_assert(id == 0); + + ecs_fini(world); +} + +void Id_wildcard_pair_from_str(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_id_t id = ecs_id_from_str(world, "(Rel, *)"); + test_assert(id != 0); + test_assert(id == ecs_pair(Rel, EcsWildcard)); + + ecs_fini(world); +} + +void Id_any_pair_from_str(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_id_t id = ecs_id_from_str(world, "(Rel, _)"); + test_assert(id != 0); + test_assert(id == ecs_pair(Rel, EcsAny)); + + ecs_fini(world); +} + +void Id_invalid_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_id_t id = ecs_id_from_str(world, "(Rel, Tgt"); + test_assert(id == 0); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/Internals.c b/vendors/flecs/test/core/src/Internals.c similarity index 89% rename from vendors/flecs/test/api/src/Internals.c rename to vendors/flecs/test/core/src/Internals.c index b6f546fe3..82f7ef04a 100644 --- a/vendors/flecs/test/api/src/Internals.c +++ b/vendors/flecs/test/core/src/Internals.c @@ -1,4 +1,4 @@ -#include +#include void Internals_setup(void) { ecs_log_set_level(-3); @@ -6,9 +6,9 @@ void Internals_setup(void) { static void Iter(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); - Mass *m = ecs_field(it, Mass, 3); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); + Mass *m = ecs_field(it, Mass, 2); probe_iter(it); @@ -140,7 +140,7 @@ static int invoked = 0; static void CreateNewTable(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); int32_t i; for (i = 0; i < it->count; i ++) { @@ -191,7 +191,7 @@ void Internals_recreate_deleted_table(void) { ECS_COMPONENT(world, Position); - ecs_entity_t parent_A = ecs_new(world, 0); + ecs_entity_t parent_A = ecs_new(world); ecs_entity_t child_A = ecs_new_w_pair(world, EcsChildOf, parent_A); test_assert(parent_A != 0); test_assert(child_A != 0); @@ -200,7 +200,7 @@ void Internals_recreate_deleted_table(void) { test_assert( !ecs_is_alive(world, parent_A)); test_assert( !ecs_is_alive(world, child_A)); - ecs_entity_t parent_B = ecs_new(world, 0); + ecs_entity_t parent_B = ecs_new(world); ecs_entity_t child_B = ecs_new_w_pair(world, EcsChildOf, parent_B); test_assert(parent_B != 0); test_assert(child_B != 0); @@ -213,7 +213,7 @@ void Internals_create_65k_tables(void) { int32_t i; for (i = 0; i <= 65536; i ++) { - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, e); test_assert(ecs_has_id(world, e, e)); test_int(ecs_get_type(world, e)->count, 1); @@ -231,7 +231,7 @@ void Internals_no_duplicate_root_table_id(void) { int32_t i; for (i = 0; i <= 50; i ++) { - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, i + 1000); test_assert(e != 0); test_assert(ecs_has_id(world, e, i + 1000)); @@ -265,26 +265,26 @@ void Internals_override_os_api_w_addon(void) { void Internals_records_resize_on_override(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t TagA = ecs_new_id(world); - ecs_entity_t TagB = ecs_new_id(world); - ecs_entity_t TagC = ecs_new_id(world); - ecs_entity_t TagD = ecs_new_id(world); - ecs_entity_t TagE = ecs_new_id(world); - ecs_entity_t TagF = ecs_new_id(world); - ecs_entity_t TagG = ecs_new_id(world); - ecs_entity_t TagH = ecs_new_id(world); - - ecs_entity_t RelA = ecs_new_id(world); - ecs_entity_t RelB = ecs_new_id(world); - ecs_entity_t RelC = ecs_new_id(world); - ecs_entity_t RelD = ecs_new_id(world); - ecs_entity_t RelE = ecs_new_id(world); - ecs_entity_t RelF = ecs_new_id(world); - ecs_entity_t RelG = ecs_new_id(world); - ecs_entity_t TgtA = ecs_new_id(world); - ecs_entity_t TgtB = ecs_new_id(world); - - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t TagA = ecs_new(world); + ecs_entity_t TagB = ecs_new(world); + ecs_entity_t TagC = ecs_new(world); + ecs_entity_t TagD = ecs_new(world); + ecs_entity_t TagE = ecs_new(world); + ecs_entity_t TagF = ecs_new(world); + ecs_entity_t TagG = ecs_new(world); + ecs_entity_t TagH = ecs_new(world); + + ecs_entity_t RelA = ecs_new(world); + ecs_entity_t RelB = ecs_new(world); + ecs_entity_t RelC = ecs_new(world); + ecs_entity_t RelD = ecs_new(world); + ecs_entity_t RelE = ecs_new(world); + ecs_entity_t RelF = ecs_new(world); + ecs_entity_t RelG = ecs_new(world); + ecs_entity_t TgtA = ecs_new(world); + ecs_entity_t TgtB = ecs_new(world); + + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, TagA); ecs_add_id(world, e, TagB); ecs_add_id(world, e, TagC); @@ -299,7 +299,7 @@ void Internals_records_resize_on_override(void) { ecs_add_pair(world, e, RelD, TgtA); ecs_add_pair(world, e, RelE, TgtA); ecs_add_pair(world, e, RelF, TgtA); - ecs_override_pair(world, e, RelG, TgtB); + ecs_auto_override_pair(world, e, RelG, TgtB); test_assert(ecs_has_id(world, e, TagA)); test_assert(ecs_has_id(world, e, TagB)); @@ -325,7 +325,7 @@ void Internals_table_observed_after_add(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); ecs_table_t *pt = ecs_get_table(world, p); ecs_table_t *ct = ecs_get_table(world, c); @@ -353,7 +353,7 @@ void Internals_table_observed_after_remove(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); ecs_table_t *pt = ecs_get_table(world, p); ecs_table_t *ct = ecs_get_table(world, c); @@ -388,7 +388,7 @@ void Internals_table_observed_after_clear(void) { ECS_TAG(world, TagA); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); ecs_table_t *pt = ecs_get_table(world, p); ecs_table_t *ct = ecs_get_table(world, c); @@ -415,7 +415,7 @@ void Internals_table_observed_after_delete(void) { ECS_TAG(world, TagA); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); ecs_table_t *pt = ecs_get_table(world, p); ecs_table_t *ct = ecs_get_table(world, c); @@ -439,7 +439,7 @@ void Internals_table_observed_after_on_remove(void) { ECS_TAG(world, TagA); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); ecs_table_t *pt = ecs_get_table(world, p); ecs_table_t *ct = ecs_get_table(world, c); @@ -447,7 +447,7 @@ void Internals_table_observed_after_on_remove(void) { test_assert(ct != NULL); test_int(flecs_table_observed_count(ct), 0); - ecs_entity_t t = ecs_new_id(world); + ecs_entity_t t = ecs_new(world); ecs_add_id(world, p, t); ecs_table_t *pt_t = ecs_get_table(world, p); test_assert(pt_t != NULL); @@ -476,7 +476,7 @@ void Internals_table_observed_after_entity_flag(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_add(world, p, TagA); ecs_table_t *p_a = ecs_get_table(world, p); @@ -498,7 +498,7 @@ void Internals_table_create_leak_check(void) { int64_t max_block_count; - ecs_entity_t tag = ecs_new_id(world); + ecs_entity_t tag = ecs_new(world); ecs_entity_t e = ecs_new_w_id(world, tag); ecs_delete(world, tag); @@ -506,7 +506,7 @@ void Internals_table_create_leak_check(void) { ecs_block_allocator_free_count; for (int i = 0; i < 25000; i ++) { - tag = ecs_new_id(world); + tag = ecs_new(world); ecs_add_id(world, e, tag); ecs_delete(world, tag); } diff --git a/vendors/flecs/test/core/src/Iter.c b/vendors/flecs/test/core/src/Iter.c new file mode 100644 index 000000000..9496a9f51 --- /dev/null +++ b/vendors/flecs/test/core/src/Iter.c @@ -0,0 +1,2872 @@ +#include + +void Iter_page_iter_0_0(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 0, 0); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.frame_offset, 0); + test_int(pit.entities[0], e1); + test_int(pit.entities[1], e2); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e1); + test_int(ptr[1].value, e2); + } + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.frame_offset, 2); + test_int(pit.entities[0], e3); + test_int(pit.entities[1], e4); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + test_int(ptr[1].value, e4); + } + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.frame_offset, 4); + test_int(pit.entities[0], e5); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_page_iter_1_0(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 1, 0); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e2); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e2); + } + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.entities[0], e3); + test_int(pit.entities[1], e4); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + test_int(ptr[1].value, e4); + } + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e5); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_page_iter_0_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 0, 1); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e1); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e1); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_page_iter_n_0(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 2, 0); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e3); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + } + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e4); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e4); + } + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e5); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_page_iter_0_n(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 0, 2); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.entities[0], e1); + test_int(pit.entities[1], e2); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e1); + test_int(ptr[1].value, e2); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_page_iter_m_n(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + ecs_entity_t e6 = ecs_new(world); ecs_set(world, e6, Self, {e6}); + + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 2, 3); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e3); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + } + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.entities[0], e4); + test_int(pit.entities[1], e5); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e4); + test_int(ptr[1].value, e5); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_page_iter_skip_1_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 2, 0); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.entities[0], e3); + test_int(pit.entities[1], e4); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + test_int(ptr[1].value, e4); + } + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e5); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_page_iter_skip_2_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagB); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 3, 0); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.entities[0], e4); + test_int(pit.entities[1], e5); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e4); + test_int(ptr[1].value, e5); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_worker_iter_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_worker_iter(&it, 0, 1); + + { + test_bool(ecs_worker_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.entities[0], e1); + test_int(pit.entities[1], e2); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e1); + test_int(ptr[1].value, e2); + } + + { + test_bool(ecs_worker_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.entities[0], e3); + test_int(pit.entities[1], e4); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + test_int(ptr[1].value, e4); + } + + { + test_bool(ecs_worker_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e5); + test_int(pit.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + } + + test_bool(ecs_worker_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_worker_iter_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 2); + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 2); + + /* Iter 1 */ + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e1); + test_int(pit_1.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e1); + } + + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e3); + test_int(pit_1.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + } + + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e5); + test_int(pit_1.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + } + + test_bool(ecs_worker_next(&pit_1), false); + + /* Iter 2 */ + { + test_bool(ecs_worker_next(&pit_2), true); + test_int(pit_2.count, 1); + test_int(pit_2.entities[0], e2); + test_int(pit_2.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_2, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e2); + } + + { + test_bool(ecs_worker_next(&pit_2), true); + test_int(pit_2.count, 1); + test_int(pit_2.entities[0], e4); + test_int(pit_2.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_2, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e4); + } + + test_bool(ecs_worker_next(&pit_2), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_worker_iter_3(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + ecs_entity_t e6 = ecs_new(world); ecs_set(world, e6, Self, {e6}); + + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 3); + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 3); + ecs_iter_t it_3 = ecs_query_iter(world, q); + ecs_iter_t pit_3 = ecs_worker_iter(&it_3, 2, 3); + + /* Iter 1 */ + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e1); + test_int(pit_1.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e1); + } + + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e4); + test_int(pit_1.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e4); + } + + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e6); + test_int(pit_1.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e6); + } + + test_bool(ecs_worker_next(&pit_1), false); + + /* Iter 2 */ + { + test_bool(ecs_worker_next(&pit_2), true); + test_int(pit_2.count, 1); + test_int(pit_2.entities[0], e2); + test_int(pit_2.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_2, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e2); + } + + { + test_bool(ecs_worker_next(&pit_2), true); + test_int(pit_2.count, 1); + test_int(pit_2.entities[0], e5); + test_int(pit_2.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_2, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + } + + test_bool(ecs_worker_next(&pit_2), false); + + /* Iter 3 */ + { + test_bool(ecs_worker_next(&pit_3), true); + test_int(pit_3.count, 1); + test_int(pit_3.entities[0], e3); + test_int(pit_3.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_3, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + } + + test_bool(ecs_worker_next(&pit_3), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_worker_iter_4(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + ecs_entity_t e6 = ecs_new(world); ecs_set(world, e6, Self, {e6}); + ecs_entity_t e7 = ecs_new(world); ecs_set(world, e7, Self, {e7}); + ecs_entity_t e8 = ecs_new(world); ecs_set(world, e8, Self, {e8}); + ecs_entity_t e9 = ecs_new(world); ecs_set(world, e9, Self, {e9}); + + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + ecs_add(world, e7, TagA); + ecs_add(world, e8, TagB); + ecs_add(world, e9, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }} + }); + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 4); + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 4); + ecs_iter_t it_3 = ecs_query_iter(world, q); + ecs_iter_t pit_3 = ecs_worker_iter(&it_3, 2, 4); + ecs_iter_t it_4 = ecs_query_iter(world, q); + ecs_iter_t pit_4 = ecs_worker_iter(&it_4, 3, 4); + + /* Iter 1 */ + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e1); + test_int(pit_1.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e1); + } + + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e5); + test_int(pit_1.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + } + + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e8); + test_int(pit_1.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e8); + } + + test_bool(ecs_worker_next(&pit_1), false); + + /* Iter 2 */ + { + test_bool(ecs_worker_next(&pit_2), true); + test_int(pit_2.count, 1); + test_int(pit_2.entities[0], e2); + test_int(pit_2.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_2, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e2); + } + + { + test_bool(ecs_worker_next(&pit_2), true); + test_int(pit_2.count, 1); + test_int(pit_2.entities[0], e6); + test_int(pit_2.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_2, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e6); + } + + { + test_bool(ecs_worker_next(&pit_2), true); + test_int(pit_2.count, 1); + test_int(pit_2.entities[0], e9); + test_int(pit_2.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_2, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e9); + } + + test_bool(ecs_worker_next(&pit_2), false); + + /* Iter 3 */ + { + test_bool(ecs_worker_next(&pit_3), true); + test_int(pit_3.count, 1); + test_int(pit_3.entities[0], e3); + test_int(pit_3.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_3, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + } + + { + test_bool(ecs_worker_next(&pit_3), true); + test_int(pit_3.count, 1); + test_int(pit_3.entities[0], e7); + test_int(pit_3.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_3, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e7); + } + + test_bool(ecs_worker_next(&pit_3), false); + + /* Iter 4 */ + { + test_bool(ecs_worker_next(&pit_4), true); + test_int(pit_4.count, 1); + test_int(pit_4.entities[0], e4); + test_int(pit_4.ids[0], ecs_id(Self)); + + Self *ptr = ecs_field(&pit_4, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e4); + } + + test_bool(ecs_worker_next(&pit_4), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_paged_iter_w_shared_comp(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Self); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t foo = ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }, { ecs_id(Position), .src.id = foo} } + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 0, 0); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.entities[0], e1); + test_int(pit.entities[1], e2); + test_int(pit.ids[0], ecs_id(Self)); + test_int(pit.ids[1], ecs_id(Position)); + test_int(pit.sources[0], 0); + test_int(pit.sources[1], foo); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e1); + test_int(ptr[1].value, e2); + + Position *pptr = ecs_field(&pit, Position, 1); + test_int(pptr->x, 10); + test_int(pptr->y, 20); + } + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 2); + test_int(pit.entities[0], e3); + test_int(pit.entities[1], e4); + test_int(pit.ids[0], ecs_id(Self)); + test_int(pit.ids[1], ecs_id(Position)); + test_int(pit.sources[0], 0); + test_int(pit.sources[1], foo); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + test_int(ptr[1].value, e4); + + Position *pptr = ecs_field(&pit, Position, 1); + test_int(pptr->x, 10); + test_int(pptr->y, 20); + } + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 1); + test_int(pit.entities[0], e5); + test_int(pit.ids[0], ecs_id(Self)); + test_int(pit.ids[1], ecs_id(Position)); + test_int(pit.sources[0], 0); + test_int(pit.sources[1], foo); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + + Position *pptr = ecs_field(&pit, Position, 1); + test_int(pptr->x, 10); + test_int(pptr->y, 20); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_worker_iter_w_shared_comp(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t foo = ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Self, {e1}); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Self, {e2}); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Self, {e3}); + ecs_entity_t e4 = ecs_new(world); ecs_set(world, e4, Self, {e4}); + ecs_entity_t e5 = ecs_new(world); ecs_set(world, e5, Self, {e5}); + + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self) }, { ecs_id(Position), .src.id = foo} } + }); + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 2); + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 2); + + /* Iter 1 */ + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e1); + test_int(pit_1.ids[0], ecs_id(Self)); + test_int(pit_1.ids[1], ecs_id(Position)); + test_int(pit_1.sources[0], 0); + test_int(pit_1.sources[1], foo); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e1); + + Position *pptr = ecs_field(&pit_1, Position, 1); + test_int(pptr->x, 10); + test_int(pptr->y, 20); + } + + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e3); + test_int(pit_1.ids[0], ecs_id(Self)); + test_int(pit_1.ids[1], ecs_id(Position)); + test_int(pit_1.sources[0], 0); + test_int(pit_1.sources[1], foo); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e3); + + Position *pptr = ecs_field(&pit_1, Position, 1); + test_int(pptr->x, 10); + test_int(pptr->y, 20); + } + + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 1); + test_int(pit_1.entities[0], e5); + test_int(pit_1.ids[0], ecs_id(Self)); + test_int(pit_1.ids[1], ecs_id(Position)); + test_int(pit_1.sources[0], 0); + test_int(pit_1.sources[1], foo); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e5); + + Position *pptr = ecs_field(&pit_1, Position, 1); + test_int(pptr->x, 10); + test_int(pptr->y, 20); + } + + test_bool(ecs_worker_next(&pit_1), false); + + /* Iter 2 */ + { + test_bool(ecs_worker_next(&pit_2), true); + test_int(pit_2.count, 1); + test_int(pit_2.entities[0], e2); + test_int(pit_2.ids[0], ecs_id(Self)); + test_int(pit_2.ids[1], ecs_id(Position)); + test_int(pit_2.sources[0], 0); + test_int(pit_2.sources[1], foo); + + Self *ptr = ecs_field(&pit_2, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e2); + + Position *pptr = ecs_field(&pit_2, Position, 1); + test_int(pptr->x, 10); + test_int(pptr->y, 20); + } + + { + test_bool(ecs_worker_next(&pit_2), true); + test_int(pit_2.count, 1); + test_int(pit_2.entities[0], e4); + test_int(pit_2.ids[0], ecs_id(Self)); + test_int(pit_2.ids[1], ecs_id(Position)); + test_int(pit_2.sources[0], 0); + test_int(pit_2.sources[1], foo); + + Self *ptr = ecs_field(&pit_2, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, e4); + + Position *pptr = ecs_field(&pit_2, Position, 1); + test_int(pptr->x, 10); + test_int(pptr->y, 20); + } + + test_bool(ecs_worker_next(&pit_2), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_paged_iter_w_task_query(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + + ecs_entity_t foo = ecs_new(world); ecs_set(world, foo, Self, {foo}); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self), .src.id = foo }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, 0, 0); + + { + test_bool(ecs_page_next(&pit), true); + test_int(pit.count, 0); + test_int(pit.ids[0], ecs_id(Self)); + test_int(pit.sources[0], foo); + + Self *ptr = ecs_field(&pit, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, foo); + } + + test_bool(ecs_page_next(&pit), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_worker_iter_w_task_query(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + + ecs_entity_t foo = ecs_new(world); ecs_set(world, foo, Self, {foo}); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Self), .src.id = foo }} + }); + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t pit_1 = ecs_worker_iter(&it_1, 0, 2); + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t pit_2 = ecs_worker_iter(&it_2, 1, 2); + + /* Iter 1 */ + { + test_bool(ecs_worker_next(&pit_1), true); + test_int(pit_1.count, 0); + test_int(pit_1.ids[0], ecs_id(Self)); + test_int(pit_1.sources[0], foo); + + Self *ptr = ecs_field(&pit_1, Self, 0); + test_assert(ptr != NULL); + test_int(ptr[0].value, foo); + } + + test_bool(ecs_worker_next(&pit_1), false); + + /* Iter 2 */ + + test_bool(ecs_worker_next(&pit_2), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_worker_iter_w_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Singleton); + ECS_COMPONENT(world, Position); + + ecs_singleton_add(world, Singleton); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {40, 50})); + + ecs_query_t *q = ecs_query(world, { .expr = "Position, Singleton($)" }); + + Position *p; + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t wit_1 = ecs_worker_iter(&it_1, 0, 2); + test_bool(ecs_worker_next(&wit_1), true); + test_int(wit_1.count, 2); + test_int(wit_1.entities[0], e1); + test_int(wit_1.entities[1], e2); + p = ecs_field(&wit_1, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t wit_2 = ecs_worker_iter(&it_2, 1, 2); + test_bool(ecs_worker_next(&wit_2), true); + test_int(wit_2.count, 2); + test_int(wit_2.entities[0], e3); + test_int(wit_2.entities[1], e4); + p = ecs_field(&wit_2, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_int(p[1].x, 40); + test_int(p[1].y, 50); + + test_bool(ecs_worker_next(&wit_2), false); + test_bool(ecs_worker_next(&wit_1), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_worker_iter_w_singleton_instanced(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Singleton); + ECS_COMPONENT(world, Position); + + ecs_singleton_add(world, Singleton); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {40, 50})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Singleton($)" + }); + + Position *p; + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t wit_1 = ecs_worker_iter(&it_1, 0, 2); + test_bool(ecs_worker_next(&wit_1), true); + test_int(wit_1.count, 2); + test_int(wit_1.entities[0], e1); + test_int(wit_1.entities[1], e2); + p = ecs_field(&wit_1, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + test_bool(ecs_worker_next(&wit_1), false); + + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t wit_2 = ecs_worker_iter(&it_2, 1, 2); + test_bool(ecs_worker_next(&wit_2), true); + test_int(wit_2.count, 2); + test_int(wit_2.entities[0], e3); + test_int(wit_2.entities[1], e4); + p = ecs_field(&wit_2, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_int(p[1].x, 40); + test_int(p[1].y, 50); + test_bool(ecs_worker_next(&wit_2), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_worker_iter_w_singleton_component_instanced(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_singleton_set(world, Velocity, {1, 2}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {40, 50})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity($)" + }); + + Position *p; + Velocity *v; + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t wit_1 = ecs_worker_iter(&it_1, 0, 2); + test_bool(ecs_worker_next(&wit_1), true); + test_int(wit_1.count, 2); + test_int(wit_1.entities[0], e1); + test_int(wit_1.entities[1], e2); + p = ecs_field(&wit_1, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + v = ecs_field(&wit_1, Velocity, 1); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_bool(ecs_worker_next(&wit_1), false); + + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t wit_2 = ecs_worker_iter(&it_2, 1, 2); + test_bool(ecs_worker_next(&wit_2), true); + test_int(wit_2.count, 2); + test_int(wit_2.entities[0], e3); + test_int(wit_2.entities[1], e4); + p = ecs_field(&wit_2, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_int(p[1].x, 40); + test_int(p[1].y, 50); + v = ecs_field(&wit_2, Velocity, 1); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_bool(ecs_worker_next(&wit_2), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_paged_iter_w_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Singleton); + ECS_COMPONENT(world, Position); + + ecs_singleton_add(world, Singleton); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {40, 50})); + + ecs_query_t *q = ecs_query(world, { .expr = "Position, Singleton($)" }); + + Position *p; + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t pit_1 = ecs_page_iter(&it_1, 0, 2); + test_bool(ecs_page_next(&pit_1), true); + test_int(pit_1.count, 2); + test_int(pit_1.entities[0], e1); + test_int(pit_1.entities[1], e2); + p = ecs_field(&pit_1, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + test_bool(ecs_page_next(&pit_1), false); + + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t pit_2 = ecs_page_iter(&it_2, 2, 2); + test_bool(ecs_page_next(&pit_2), true); + test_int(pit_2.count, 2); + test_int(pit_2.entities[0], e3); + test_int(pit_2.entities[1], e4); + p = ecs_field(&pit_2, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_int(p[1].x, 40); + test_int(p[1].y, 50); + test_bool(ecs_page_next(&pit_2), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_paged_iter_w_singleton_instanced(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Singleton); + ECS_COMPONENT(world, Position); + + ecs_singleton_add(world, Singleton); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {40, 50})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Singleton($)" + }); + + Position *p; + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t pit_1 = ecs_page_iter(&it_1, 0, 2); + test_bool(ecs_page_next(&pit_1), true); + test_int(pit_1.count, 2); + test_int(pit_1.entities[0], e1); + test_int(pit_1.entities[1], e2); + p = ecs_field(&pit_1, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + test_bool(ecs_page_next(&pit_1), false); + + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t pit_2 = ecs_page_iter(&it_2, 2, 2); + test_bool(ecs_page_next(&pit_2), true); + test_int(pit_2.count, 2); + test_int(pit_2.entities[0], e3); + test_int(pit_2.entities[1], e4); + p = ecs_field(&pit_2, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_int(p[1].x, 40); + test_int(p[1].y, 50); + test_bool(ecs_page_next(&pit_2), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_paged_iter_w_singleton_component_instanced(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_singleton_set(world, Velocity, {1, 2}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {40, 50})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity($)" + }); + + Position *p; + Velocity *v; + + ecs_iter_t it_1 = ecs_query_iter(world, q); + ecs_iter_t pit_1 = ecs_page_iter(&it_1, 0, 2); + test_bool(ecs_page_next(&pit_1), true); + test_int(pit_1.count, 2); + test_int(pit_1.entities[0], e1); + test_int(pit_1.entities[1], e2); + p = ecs_field(&pit_1, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + v = ecs_field(&pit_1, Velocity, 1); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_bool(ecs_page_next(&pit_1), false); + + ecs_iter_t it_2 = ecs_query_iter(world, q); + ecs_iter_t pit_2 = ecs_page_iter(&it_2, 2, 2); + test_bool(ecs_page_next(&pit_2), true); + test_int(pit_2.count, 2); + test_int(pit_2.entities[0], e3); + test_int(pit_2.entities[1], e4); + p = ecs_field(&pit_2, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_int(p[1].x, 40); + test_int(p[1].y, 50); + v = ecs_field(&pit_2, Velocity, 1); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_bool(ecs_page_next(&pit_2), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +static +void Iter(ecs_world_t *world, ecs_query_t *q, int32_t offset, int32_t limit) { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_t pit = ecs_page_iter(&it, offset, limit); + while (ecs_page_next(&pit)) { + Position *p = ecs_field(&pit, Position, 0); + Velocity *v = NULL; + Mass *m = NULL; + + if (pit.field_count >= 2) { + v = ecs_field(&pit, Velocity, 1); + } + + if (pit.field_count >= 3) { + m = ecs_field(&pit, Mass, 2); + } + + int *param = pit.param; + + probe_iter(&pit); + + int i; + for (i = 0; i < pit.count; i ++) { + p[i].x = 10; + p[i].y = 20; + + if (param) { + p[i].x += *param; + p[i].y += *param; + } + + if (v) { + v[i].x = 30; + v[i].y = 40; + } + + if (m) { + m[i] = 50; + } + } + } +} + +void Iter_page_iter_w_offset_skip_1_archetype(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 3, 0); + + test_int(ctx.count, 3); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e4); + test_int(ctx.e[1], e5); + test_int(ctx.e[2], e6); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_offset_skip_1_archetype_plus_one(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 4, 0); + + test_int(ctx.count, 2); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e5); + test_int(ctx.e[1], e6); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_offset_skip_2_archetypes(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 5, 0); + + test_int(ctx.count, 1); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e6); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][1], 0); + + const Position *p = ecs_get(world, e6, Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, e6, Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + + ecs_fini(world); +} + +void Iter_page_iter_w_limit_skip_1_archetype(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 0, 5); + + test_int(ctx.count, 5); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.e[3], e4); + test_int(ctx.e[4], e5); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_limit_skip_1_archetype_minus_one(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 0, 4); + + test_int(ctx.count, 4); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.e[3], e4); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_limit_skip_2_archetypes(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 0, 3); + + test_int(ctx.count, 3); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_offset_1_limit_max(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 1, 5); + + test_int(ctx.count, 5); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e2); + test_int(ctx.e[1], e3); + test_int(ctx.e[2], e4); + test_int(ctx.e[3], e5); + test_int(ctx.e[4], e6); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_offset_1_limit_minus_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 1, 4); + + test_int(ctx.count, 4); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e2); + test_int(ctx.e[1], e3); + test_int(ctx.e[2], e4); + test_int(ctx.e[3], e5); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_offset_2_type_limit_max(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 3, 3); + + test_int(ctx.count, 3); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e4); + test_int(ctx.e[1], e5); + test_int(ctx.e[2], e6); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_offset_2_type_limit_minus_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 3, 2); + + test_int(ctx.count, 2); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e4); + test_int(ctx.e[1], e5); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_limit_1_all_offsets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 0, 1); + Iter(world, q, 2, 1); + Iter(world, q, 1, 1); + Iter(world, q, 3, 1); + Iter(world, q, 5, 1); + Iter(world, q, 4, 1); + + test_int(ctx.count, 6); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e3); + test_int(ctx.e[2], e2); + test_int(ctx.e[3], e4); + test_int(ctx.e[4], e6); + test_int(ctx.e[5], e5); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_w_offset_out_of_bounds(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 6, 1); + + test_int(ctx.count, 0); + + ecs_fini(world); +} + +void Iter_page_iter_w_limit_out_of_bounds(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ECS_ENTITY(world, e1, Position, Velocity); + ECS_ENTITY(world, e2, Position, Velocity); + ECS_ENTITY(world, e3, Position, Velocity); + ECS_ENTITY(world, e4, Position, Velocity, Mass); + ECS_ENTITY(world, e5, Position, Velocity, Mass); + ECS_ENTITY(world, e6, Position, Velocity, Rotation); + ECS_ENTITY(world, e7, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 5, 2); + + test_int(ctx.count, 1); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e6); + + int i; + for (i = 0; i < ctx.invoked; i ++) { + test_int(ctx.c[i][0], ecs_id(Position)); + test_int(ctx.s[i][0], 0); + test_int(ctx.c[i][1], ecs_id(Velocity)); + test_int(ctx.s[i][1], 0); + } + + for (i = 0; i < ctx.count; i ++) { + const Position *p = ecs_get(world, ctx.e[i], Position); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_get(world, ctx.e[i], Velocity); + test_int(v->x, 30); + test_int(v->y, 40); + } + + ecs_fini(world); +} + +void Iter_page_iter_no_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ECS_ENTITY(world, e1, Position); + + ECS_QUERY(world, q, Position, Velocity); + + Probe ctx = {0}; + ecs_set_ctx(world, &ctx, NULL); + + Iter(world, q, 0, 0); + + test_int(ctx.count, 0); + + ecs_fini(world); +} + +typedef struct Param { + ecs_entity_t entity; + int count; +} Param; + +static ECS_QUERY_DECLARE(qTestSubset); + +static +void TestSubset(ecs_world_t *world, ecs_query_t *query, int32_t offset, + int32_t limit, Param *param) +{ + ecs_iter_t it = ecs_query_iter(world, query); + ecs_iter_t pit = ecs_page_iter(&it, offset, limit); + while (ecs_page_next(&pit)) { + int i; + for (i = 0; i < pit.count; i ++) { + test_assert(param->entity != pit.entities[i]); + param->count ++; + } + } +} + +static +void TestAll(ecs_world_t *world, ecs_query_t *query) { + ecs_iter_t it = ecs_query_iter(world, query); + while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + + int i; + for (i = 0; i < it.count; i ++) { + Param param = {.entity = it.entities[i], 0}; + TestSubset(world, qTestSubset, it.frame_offset + i + 1, 0, ¶m); + p[i].x += param.count; + } + } +} + +void Iter_page_iter_comb_10_entities_1_type(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ECS_QUERY_DEFINE(world, qTestSubset, Position); + ECS_QUERY(world, qTestAll, Position); + + int i, ENTITIES = 10; + + const ecs_entity_t *ids = ecs_bulk_new(world, Position, ENTITIES); + + for (i = 0; i < ENTITIES; i ++) { + ecs_set(world, ids[i], Position, {1, 2}); + } + + TestAll(world, qTestAll); + + for (i = 0; i < ENTITIES; i ++) { + const Position *p = ecs_get(world, ids[i], Position); + test_int(p->x, ENTITIES - i); + } + + ecs_fini(world); +} + +void Iter_page_iter_comb_10_entities_2_types(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ECS_QUERY_DEFINE(world, qTestSubset, Position); + ECS_QUERY(world, qTestAll, Position); + + int i, ENTITIES = 10; + ecs_entity_t ids_1[5], ids_2[5]; + + for (int i = 0; i < ENTITIES / 2; i ++) { + ids_1[i] = ecs_insert(world, ecs_value(Position, {1, 2})); + } + for (int i = 0; i < ENTITIES / 2; i ++) { + ids_2[i] = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_add(world, ids_2[i], Velocity); + } + + TestAll(world, qTestAll); + + for (i = 0; i < 5; i ++) { + const Position *p = ecs_get(world, ids_1[i], Position); + test_int(p->x, ENTITIES - i); + + p = ecs_get(world, ids_2[i], Position); + test_int(p->x, ENTITIES - (i + 5)); + } + + ecs_fini(world); +} + +void Iter_count(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + for (int i = 0; i < 500; i ++) { + ecs_entity_t e = ecs_new_w(world, TagA); + if (!(i % 2)) { + ecs_add(world, e, TagB); + } + if (!(i % 3)) { + ecs_add(world, e, TagC); + } + } + + ecs_query_t *q = ecs_query(world, { .expr = "TagA" }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_int(500, ecs_iter_count(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_interleaved_iter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ TagA }} + }); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + + /* Bit of whitebox testing, check whether the stack cursor is restored to + * its original position after the 2nd iterator is done */ + ecs_iter_t it_1 = ecs_query_iter(world, f); + ecs_stack_cursor_t cursor = *it_1.priv_.cache.stack_cursor; + ecs_iter_t it_2 = ecs_query_iter(world, f); + + test_bool(true, ecs_query_next(&it_1)); + test_bool(true, ecs_query_next(&it_2)); + test_int(e1, it_1.entities[0]); + test_int(e1, it_2.entities[0]); + + test_bool(true, ecs_query_next(&it_1)); + test_bool(true, ecs_query_next(&it_2)); + test_int(e2, it_1.entities[0]); + test_int(e2, it_2.entities[0]); + + test_bool(false, ecs_query_next(&it_1)); + test_bool(false, ecs_query_next(&it_2)); + + it_1 = ecs_query_iter(world, f); + test_assert(it_1.priv_.cache.stack_cursor->page == cursor.page); + test_assert(it_1.priv_.cache.stack_cursor->sp == cursor.sp); + ecs_iter_fini(&it_1); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_iter_restore_stack_iter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ ecs_id(Position) }} + }); + + ecs_iter_t it = ecs_query_iter(world, f); + ecs_stack_cursor_t cursor = *it.priv_.cache.stack_cursor; + ecs_iter_fini(&it); + + it = ecs_query_iter(world, f); + test_assert(it.priv_.cache.stack_cursor->page == cursor.page); + test_assert(it.priv_.cache.stack_cursor->sp == cursor.sp); + ecs_iter_fini(&it); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_get_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t e = ecs_new_w(world, Tag); + ecs_new_w(world, Tag); + ecs_new_w(world, Tag); + + ecs_query_t *f = ecs_query_init(world, &(ecs_query_desc_t){ + .terms = {{ Tag }} + }); + + ecs_iter_t it = ecs_query_iter(world, f); + ecs_entity_t first = ecs_iter_first(&it); + test_assert(first == e); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_page_iter_w_only_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ Tag }} + }); + + ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + + ecs_iter_t it = ecs_query_iter(world, f); + ecs_iter_t pit = ecs_page_iter(&it, 1, 0); + + test_assert(ecs_page_next(&pit)); + test_int(pit.count, 1); + test_uint(pit.entities[0], e2); + test_assert(!ecs_page_next(&pit)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_worker_iter_w_only_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ Tag }} + }); + + ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + + ecs_iter_t it = ecs_query_iter(world, f); + ecs_iter_t pit = ecs_worker_iter(&it, 1, 2); + + test_assert(ecs_worker_next(&pit)); + test_int(pit.count, 1); + test_uint(pit.entities[0], e2); + test_assert(!ecs_worker_next(&pit)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_page_iter_w_inout_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ ecs_id(Position), .inout = EcsInOutNone }} + }); + + ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + + ecs_iter_t it = ecs_query_iter(world, f); + ecs_iter_t pit = ecs_page_iter(&it, 1, 0); + + test_assert(ecs_page_next(&pit)); + test_int(pit.count, 1); + test_uint(pit.entities[0], e2); + test_assert(!ecs_page_next(&pit)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_worker_iter_w_inout_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ ecs_id(Position), .inout = EcsInOutNone }} + }); + + ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + + ecs_iter_t it = ecs_query_iter(world, f); + ecs_iter_t pit = ecs_worker_iter(&it, 1, 2); + + test_assert(ecs_worker_next(&pit)); + test_int(pit.count, 1); + test_uint(pit.entities[0], e2); + test_assert(!ecs_worker_next(&pit)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_page_iter_w_ctx(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ TagA }} + }); + + ecs_new_w(world, TagA); + + int ctx; + + ecs_iter_t it = ecs_query_iter(world, f); + it.ctx = &ctx; + + ecs_iter_t pit = ecs_page_iter(&it, 0, 1); + test_assert(pit.ctx == &ctx); + + test_assert(ecs_page_next(&pit)); + test_assert(!ecs_page_next(&pit)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_page_iter_w_binding_ctx(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ TagA }} + }); + + ecs_new_w(world, TagA); + + int ctx; + + ecs_iter_t it = ecs_query_iter(world, f); + it.callback_ctx = &ctx; + + ecs_iter_t pit = ecs_page_iter(&it, 0, 1); + test_assert(pit.callback_ctx == &ctx); + + test_assert(ecs_page_next(&pit)); + test_assert(!ecs_page_next(&pit)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_worker_iter_w_ctx(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ TagA }} + }); + + ecs_new_w(world, TagA); + + int ctx; + + ecs_iter_t it = ecs_query_iter(world, f); + it.ctx = &ctx; + + ecs_iter_t pit = ecs_worker_iter(&it, 0, 2); + test_assert(pit.ctx == &ctx); + + test_assert(ecs_worker_next(&pit)); + test_assert(!ecs_worker_next(&pit)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_worker_iter_w_binding_ctx(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ TagA }} + }); + + ecs_new_w(world, TagA); + + int ctx; + + ecs_iter_t it = ecs_query_iter(world, f); + it.callback_ctx = &ctx; + + ecs_iter_t pit = ecs_worker_iter(&it, 0, 2); + test_assert(pit.callback_ctx == &ctx); + + test_assert(ecs_worker_next(&pit)); + test_assert(!ecs_worker_next(&pit)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_column_index_owned(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ TagB }, { TagC }, { TagA }} + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, TagA); + ecs_add(world, e1, TagB); + ecs_add(world, e1, TagC); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, TagA); + ecs_add(world, e2, TagB); + ecs_add(world, e2, TagC); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_int(1, ecs_field_column(&it, 0)); + test_int(2, ecs_field_column(&it, 1)); + test_int(0, ecs_field_column(&it, 2)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_int(2, ecs_field_column(&it, 0)); + test_int(3, ecs_field_column(&it, 1)); + test_int(1, ecs_field_column(&it, 2)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_column_index_shared(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ TagB }, { TagC }, { TagA }} + }); + + ecs_entity_t base = ecs_new_w(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, base); + ecs_add(world, e1, TagB); + ecs_add(world, e1, TagC); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add_pair(world, e2, EcsIsA, base); + ecs_add(world, e2, TagB); + ecs_add(world, e2, TagC); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_int(0, ecs_field_column(&it, 0)); + test_int(1, ecs_field_column(&it, 1)); + test_int(0, ecs_field_column(&it, 2)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_int(1, ecs_field_column(&it, 0)); + test_int(2, ecs_field_column(&it, 1)); + test_int(0, ecs_field_column(&it, 2)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_column_index_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ TagB }, { TagC }, { TagA, .oper = EcsNot }} + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, TagB); + ecs_add(world, e1, TagC); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, TagB); + ecs_add(world, e2, TagC); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_int(0, ecs_field_column(&it, 0)); + test_int(1, ecs_field_column(&it, 1)); + test_int(-1, ecs_field_column(&it, 2)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_int(1, ecs_field_column(&it, 0)); + test_int(2, ecs_field_column(&it, 1)); + test_int(-1, ecs_field_column(&it, 2)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_page_iter_w_fini(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ ecs_id(Position) }} + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_add(world, e2, Foo); + + ecs_iter_t it = ecs_query_iter(world, f); + ecs_iter_t pit = ecs_page_iter(&it, 0, 2); + test_bool(true, ecs_page_next(&pit)); + test_int(pit.count, 1); + test_int(pit.entities[0], e1); + + ecs_iter_fini(&pit); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_worker_iter_w_fini(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ ecs_id(Position) }} + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_add(world, e2, Foo); + + ecs_iter_t it = ecs_query_iter(world, f); + ecs_iter_t pit = ecs_worker_iter(&it, 0, 2); + test_bool(true, ecs_worker_next(&pit)); + test_int(pit.count, 1); + test_int(pit.entities[0], e1); + ecs_iter_fini(&pit); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Iter_rule_page_iter_w_fini(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *r = ecs_query(world, { + .terms = {{ ecs_id(Position) }} + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_add(world, e2, Foo); + + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_t pit = ecs_page_iter(&it, 0, 2); + test_bool(true, ecs_page_next(&pit)); + test_int(pit.count, 1); + test_int(pit.entities[0], e1); + ecs_iter_fini(&pit); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Iter_rule_worker_iter_w_fini(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *r = ecs_query(world, { + .terms = {{ ecs_id(Position) }} + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_add(world, e2, Foo); + + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_t pit = ecs_worker_iter(&it, 0, 2); + test_bool(true, ecs_worker_next(&pit)); + test_int(pit.count, 1); + test_int(pit.entities[0], e1); + ecs_iter_fini(&pit); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Iter_to_str_before_next(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { .expr = "Tag" }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + char *str = ecs_iter_str(&it); + test_assert(str == NULL); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Iter_to_str(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); + ecs_add(world, e, Tag); + + ecs_query_t *q = ecs_query(world, { .expr = "Tag" }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + char *str = ecs_iter_str(&it); + test_assert(str != NULL); + test_str(str, + "id: Tag\n" + "src: #0\n" + "set: true\n" + "this:\n" + " - foo\n" + ); + ecs_os_free(str); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/Lookup.c b/vendors/flecs/test/core/src/Lookup.c similarity index 55% rename from vendors/flecs/test/api/src/Lookup.c rename to vendors/flecs/test/core/src/Lookup.c index 75cc820f7..910142197 100644 --- a/vendors/flecs/test/api/src/Lookup.c +++ b/vendors/flecs/test/core/src/Lookup.c @@ -1,4 +1,4 @@ -#include +#include #include void Lookup_setup(void) { @@ -35,7 +35,7 @@ void Lookup_lookup_w_null_name(void) { void Lookup_lookup_after_name_reset(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); ecs_entity_t lookup = ecs_lookup(world, "foo"); test_assert(e == lookup); @@ -113,7 +113,7 @@ void Lookup_get_name_no_name(void) { ECS_COMPONENT(world, Position); /* Ensure this doesn't crash the lookup function */ - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); const char *id = ecs_get_name(world, e); test_assert(id == NULL); @@ -124,7 +124,7 @@ void Lookup_get_name_from_empty(void) { ecs_world_t *world = ecs_mini(); /* Ensure this doesn't crash the lookup function */ - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); const char *id = ecs_get_name(world, e); test_assert(id == NULL); @@ -134,27 +134,98 @@ void Lookup_get_name_from_empty(void) { void Lookup_lookup_by_id(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_lookup(world, "1000"); + ecs_entity_t e = ecs_lookup(world, "#1000"); + test_int(e, 0); + + ecs_make_alive(world, 1000); + + e = ecs_lookup(world, "#1000"); test_int(e, 1000); ecs_fini(world); } +void Lookup_lookup_path_anonymous_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_make_alive(world, 1000); + + ecs_entity_t e = ecs_entity(world, { + .name = "foo", + .parent = 1000 + }); + test_assert(e != 0); + + test_assert(ecs_has_pair(world, e, EcsChildOf, 1000)); + test_assert(ecs_lookup(world, "#1000") == 1000); + test_assert(ecs_lookup(world, "#1000.foo") == e); + + ecs_fini(world); +} + +void Lookup_lookup_path_0_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "foo", + }); + test_assert(e != 0); + + test_assert(ecs_lookup(world, "#0.foo") == e); + + ecs_fini(world); +} + +void Lookup_lookup_path_0_parent_w_scope(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { + .name = "parent", + }); + test_assert(p != 0); + + ecs_entity_t e = ecs_entity(world, { + .name = "foo", + }); + test_assert(e != 0); + + ecs_entity_t c = ecs_entity(world, { + .name = "foo", + .parent = p + }); + test_assert(c != 0); + test_assert(e != c); + + test_assert(ecs_lookup(world, "foo") == e); + test_assert(ecs_lookup(world, "#0.foo") == e); + test_assert(ecs_lookup(world, "parent.foo") == c); + + ecs_set_scope(world, p); + + test_assert(ecs_lookup(world, "foo") == c); + test_assert(ecs_lookup(world, "#0.foo") == e); + test_assert(ecs_lookup(world, "parent.foo") == c); + + ecs_set_scope(world, 0); + + ecs_fini(world); +} + void Lookup_lookup_recycled_by_id(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); char buf[20]; - sprintf(buf, "%u", (uint32_t)e); + sprintf(buf, "#%u", (uint32_t)e); ecs_entity_t l = ecs_lookup(world, buf); test_assert(l != 0); test_assert(l == e); ecs_delete(world, e); - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); test_assert((uint32_t)r == e); test_assert(r != e); @@ -177,8 +248,8 @@ void Lookup_lookup_name_w_digit(void) { void Lookup_lookup_symbol_by_id(void) { ecs_world_t *world = ecs_mini(); - ecs_ensure(world, 1000); - ecs_entity_t e = ecs_lookup_symbol(world, "1000", true, true); + ecs_make_alive(world, 1000); + ecs_entity_t e = ecs_lookup_symbol(world, "#1000", true, true); test_int(e, 1000); ecs_fini(world); @@ -201,16 +272,84 @@ void Lookup_lookup_path_w_digit(void) { ecs_entity_t e1 = ecs_set_name(world, 0, "10_id"); ecs_add_pair(world, e1, EcsChildOf, parent); - ecs_entity_t e2 = ecs_lookup_fullpath(world, "parent.10_id"); + ecs_entity_t e2 = ecs_lookup(world, "parent.10_id"); test_assert(e2 == e1); ecs_fini(world); } +void Lookup_lookup_name_w_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_set_name(world, 0, "hello world"); + ecs_entity_t e2 = ecs_set_name(world, 0, "hello mars"); + test_assert(e1 != e2); + test_str(ecs_get_name(world, e1), "hello world"); + test_str(ecs_get_name(world, e2), "hello mars"); + + ecs_fini(world); +} + +void Lookup_lookup_path_w_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_set_name(world, 0, "hello world"); + ecs_entity_t e2 = ecs_set_name(world, 0, "hello mars"); + ecs_add_pair(world, e2, EcsChildOf, e1); + + test_str(ecs_get_name(world, e1), "hello world"); + test_str(ecs_get_name(world, e2), "hello mars"); + + test_assert(e2 == ecs_lookup(world, "hello world.hello mars")); + + ecs_fini(world); +} + +void Lookup_lookup_number(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_set_name(world, 0, "10"); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "10"); + + test_uint(ecs_lookup(world, "10"), e); + + ecs_fini(world); +} + +void Lookup_lookup_number_path(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_set_name(world, 0, "10"); + ecs_entity_t e = ecs_set_name(world, 0, "20"); + ecs_add_pair(world, e, EcsChildOf, p); + + test_str(ecs_get_name(world, p), "10"); + test_str(ecs_get_name(world, e), "20"); + + test_uint(ecs_lookup(world, "20"), 0); + test_uint(ecs_lookup(world, "10"), p); + test_uint(ecs_lookup(world, "10.20"), e); + + ecs_fini(world); +} + +void Lookup_lookup_number_0(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_set_name(world, 0, "0"); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "0"); + + test_uint(ecs_lookup(world, "0"), e); + + ecs_fini(world); +} + void Lookup_set_name_of_existing(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(ecs_get_name(world, e) == NULL); @@ -365,7 +504,7 @@ void Lookup_lookup_path_wildcard(void) { void Lookup_lookup_path_this_from_scope(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t scope = ecs_new_id(world); + ecs_entity_t scope = ecs_new(world); ecs_entity_t lookup = ecs_lookup_path_w_sep(world, scope, ".", NULL, NULL, false); test_assert(lookup != 0); @@ -377,7 +516,7 @@ void Lookup_lookup_path_this_from_scope(void) { void Lookup_lookup_path_wildcard_from_scope(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t scope = ecs_new_id(world); + ecs_entity_t scope = ecs_new(world); ecs_entity_t lookup = ecs_lookup_path_w_sep(world, scope, ".", NULL, NULL, false); test_assert(lookup != 0); @@ -405,15 +544,15 @@ void Lookup_lookup_from_scope_staged(void) { ecs_entity_t child = ecs_set_name(world, 0, "Child"); ecs_add_pair(world, child, EcsChildOf, parent); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); test_assert(ecs_set_scope(stage, parent) == 0); - test_assert(ecs_lookup_path(stage, 0, "Child") == child); - test_assert(ecs_lookup_fullpath(stage, "Child") == child); + test_assert(ecs_lookup_from(stage, 0, "Child") == child); + test_assert(ecs_lookup(stage, "Child") == child); - test_assert(ecs_lookup_path(stage, parent, "Child") == child); - test_assert(ecs_lookup_fullpath(stage, "Child") == child); + test_assert(ecs_lookup_from(stage, parent, "Child") == child); + test_assert(ecs_lookup(stage, "Child") == child); test_assert(ecs_set_scope(stage, 0) == parent); ecs_readonly_end(world); @@ -424,7 +563,7 @@ void Lookup_lookup_from_scope_staged(void) { void Lookup_lookup_core(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_lookup_fullpath(world, "Component"); + ecs_entity_t c = ecs_lookup(world, "Component"); test_assert(c != 0); test_assert(c == ecs_id(EcsComponent)); @@ -434,18 +573,18 @@ void Lookup_lookup_core(void) { void Lookup_lookup_core_from_stage(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_lookup_fullpath(world, "Component"); + ecs_entity_t c = ecs_lookup(world, "Component"); test_assert(c != 0); test_assert(c == ecs_id(EcsComponent)); ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_readonly_begin(world); - ecs_entity_t d = ecs_lookup_fullpath(stage, "Component"); + ecs_readonly_begin(world, false); + ecs_entity_t d = ecs_lookup(stage, "Component"); test_assert(d != 0); test_assert(d == ecs_id(EcsComponent)); ecs_readonly_end(world); - ecs_entity_t e = ecs_lookup_fullpath(world, "Component"); + ecs_entity_t e = ecs_lookup(world, "Component"); test_assert(e != 0); test_assert(e == ecs_id(EcsComponent)); @@ -459,16 +598,16 @@ void Lookup_lookup_custom_search_path(void) { ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ecs_set_name(world, child, "Child"); - test_assert(ecs_lookup_fullpath(world, "Parent") == parent); - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - test_assert(ecs_lookup_fullpath(world, "Parent.Child") == child); + test_assert(ecs_lookup(world, "Parent") == parent); + test_assert(ecs_lookup(world, "Child") == 0); + test_assert(ecs_lookup(world, "Parent.Child") == child); ecs_entity_t lookup_path[] = { parent, 0 }; ecs_entity_t *old_path = ecs_set_lookup_path(world, lookup_path); - test_assert(ecs_lookup_fullpath(world, "Parent") == parent); - test_assert(ecs_lookup_fullpath(world, "Child") == child); - test_assert(ecs_lookup_fullpath(world, "Parent.Child") == child); + test_assert(ecs_lookup(world, "Parent") == parent); + test_assert(ecs_lookup(world, "Child") == child); + test_assert(ecs_lookup(world, "Parent.Child") == child); ecs_set_lookup_path(world, old_path); @@ -482,18 +621,18 @@ void Lookup_lookup_custom_search_path_from_stage(void) { ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ecs_set_name(world, child, "Child"); - test_assert(ecs_lookup_fullpath(world, "Parent") == parent); - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - test_assert(ecs_lookup_fullpath(world, "Parent.Child") == child); + test_assert(ecs_lookup(world, "Parent") == parent); + test_assert(ecs_lookup(world, "Child") == 0); + test_assert(ecs_lookup(world, "Parent.Child") == child); ecs_entity_t lookup_path[] = { parent, 0 }; ecs_entity_t *old_path = ecs_set_lookup_path(world, lookup_path); ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_readonly_begin(world); - test_assert(ecs_lookup_fullpath(stage, "Parent") == parent); - test_assert(ecs_lookup_fullpath(stage, "Child") == child); - test_assert(ecs_lookup_fullpath(stage, "Parent.Child") == child); + ecs_readonly_begin(world, false); + test_assert(ecs_lookup(stage, "Parent") == parent); + test_assert(ecs_lookup(stage, "Child") == child); + test_assert(ecs_lookup(stage, "Parent.Child") == child); ecs_readonly_end(world); ecs_set_lookup_path(world, old_path); @@ -510,29 +649,29 @@ void Lookup_lookup_custom_search_path_n_elems(void) { ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child); ecs_set_name(world, grand_child, "Child"); - test_assert(ecs_lookup_fullpath(world, "Parent") == parent); - test_assert(ecs_lookup_fullpath(world, "Child") == 0); - test_assert(ecs_lookup_fullpath(world, "Child.Child") == 0); - test_assert(ecs_lookup_fullpath(world, "Parent.Child") == child); - test_assert(ecs_lookup_fullpath(world, "Parent.Child.Child") == grand_child); + test_assert(ecs_lookup(world, "Parent") == parent); + test_assert(ecs_lookup(world, "Child") == 0); + test_assert(ecs_lookup(world, "Child.Child") == 0); + test_assert(ecs_lookup(world, "Parent.Child") == child); + test_assert(ecs_lookup(world, "Parent.Child.Child") == grand_child); ecs_entity_t lookup_path[] = { child, parent, 0 }; ecs_entity_t *old_path = ecs_set_lookup_path(world, lookup_path); - test_assert(ecs_lookup_fullpath(world, "Parent") == parent); - test_assert(ecs_lookup_fullpath(world, "Child") == child); - test_assert(ecs_lookup_fullpath(world, "Child.Child") == grand_child); - test_assert(ecs_lookup_fullpath(world, "Parent.Child") == child); - test_assert(ecs_lookup_fullpath(world, "Parent.Child.Child") == grand_child); + test_assert(ecs_lookup(world, "Parent") == parent); + test_assert(ecs_lookup(world, "Child") == child); + test_assert(ecs_lookup(world, "Child.Child") == grand_child); + test_assert(ecs_lookup(world, "Parent.Child") == child); + test_assert(ecs_lookup(world, "Parent.Child.Child") == grand_child); lookup_path[0] = parent; lookup_path[1] = child; - test_assert(ecs_lookup_fullpath(world, "Parent") == parent); - test_assert(ecs_lookup_fullpath(world, "Child") == grand_child); - test_assert(ecs_lookup_fullpath(world, "Child.Child") == grand_child); - test_assert(ecs_lookup_fullpath(world, "Parent.Child") == child); - test_assert(ecs_lookup_fullpath(world, "Parent.Child.Child") == grand_child); + test_assert(ecs_lookup(world, "Parent") == parent); + test_assert(ecs_lookup(world, "Child") == grand_child); + test_assert(ecs_lookup(world, "Child.Child") == grand_child); + test_assert(ecs_lookup(world, "Parent.Child") == child); + test_assert(ecs_lookup(world, "Parent.Child.Child") == grand_child); ecs_set_lookup_path(world, old_path); @@ -542,7 +681,7 @@ void Lookup_lookup_custom_search_path_n_elems(void) { void Lookup_set_same_name(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_entity(world, "MyName"); + ecs_entity_t e = ecs_entity(world, { .name = "MyName" }); test_assert(e != 0); test_str("MyName", ecs_get_name(world, e)); test_uint(e, ecs_lookup(world, "MyName")); @@ -557,19 +696,19 @@ void Lookup_set_same_name(void) { void Lookup_set_same_name_after_reparenting(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); - ecs_entity_t e = ecs_new_entity(world, "MyName"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + ecs_entity_t e = ecs_entity(world, { .name = "MyName" }); test_assert(e != 0); test_str("MyName", ecs_get_name(world, e)); test_uint(e, ecs_lookup(world, "MyName")); ecs_add_pair(world, e, EcsChildOf, parent); test_str("MyName", ecs_get_name(world, e)); - test_uint(e, ecs_lookup_fullpath(world, "parent.MyName")); + test_uint(e, ecs_lookup(world, "parent.MyName")); ecs_set_name(world, e, "MyName"); test_str("MyName", ecs_get_name(world, e)); - test_uint(e, ecs_lookup_fullpath(world, "parent.MyName")); + test_uint(e, ecs_lookup(world, "parent.MyName")); ecs_fini(world); } @@ -577,7 +716,7 @@ void Lookup_set_same_name_after_reparenting(void) { void Lookup_defer_set_name(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); test_assert(e != 0); test_str("Foo", ecs_get_name(world, e)); test_uint(e, ecs_lookup(world, "Foo")); @@ -595,7 +734,7 @@ void Lookup_defer_set_name(void) { void Lookup_defer_set_same_name(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_entity(world, "MyName"); + ecs_entity_t e = ecs_entity(world, { .name = "MyName" }); test_assert(e != 0); test_str("MyName", ecs_get_name(world, e)); test_uint(e, ecs_lookup(world, "MyName")); @@ -622,7 +761,7 @@ void Lookup_lookup_invalid_digit(void) { void Lookup_lookup_child_invalid_digit(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_entity(world, "p"); + ecs_entity_t parent = ecs_entity(world, { .name = "p" }); test_assert(parent != 0); ecs_entity_t child = ecs_lookup_child(world, parent, "111111111111"); test_assert(child == 0); @@ -633,9 +772,9 @@ void Lookup_lookup_child_invalid_digit(void) { void Lookup_lookup_digit_from_wrong_scope(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_entity(world, "p"); + ecs_entity_t parent = ecs_entity(world, { .name = "p" }); test_assert(parent != 0); - ecs_entity_t child = ecs_lookup_fullpath(world, "p.1"); + ecs_entity_t child = ecs_lookup(world, "p.1"); test_assert(child == 0); ecs_fini(world); @@ -644,9 +783,9 @@ void Lookup_lookup_digit_from_wrong_scope(void) { void Lookup_lookup_core_entity_from_wrong_scope(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_entity(world, "p"); + ecs_entity_t parent = ecs_entity(world, { .name = "p" }); test_assert(parent != 0); - ecs_entity_t child = ecs_lookup_fullpath(world, "p.10.Component"); + ecs_entity_t child = ecs_lookup(world, "p.10.Component"); test_assert(child == 0); ecs_fini(world); @@ -655,12 +794,158 @@ void Lookup_lookup_core_entity_from_wrong_scope(void) { void Lookup_lookup_alias_w_number(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_entity(world, "p"); + ecs_entity_t parent = ecs_entity(world, { .name = "p" }); test_assert(parent != 0); ecs_set_alias(world, parent, "10p"); - test_assert(parent == ecs_lookup_fullpath(world, "p")); - test_assert(parent == ecs_lookup_fullpath(world, "10p")); + test_assert(parent == ecs_lookup(world, "p")); + test_assert(parent == ecs_lookup(world, "10p")); + + ecs_fini(world); +} + +void Lookup_lookup_symbol_path(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t foo = ecs_entity(world, { .name = "foo" }); + ecs_entity_t bar = ecs_entity(world, { .name = "bar" }); + ecs_add_pair(world, bar, EcsChildOf, foo); + + ecs_entity_t e = ecs_set_symbol(world, 0, "foo.bar"); + + test_assert(ecs_lookup_symbol(world, "foo.bar", false, false) == e); + test_assert(ecs_lookup_symbol(world, "foo.bar", true, false) == bar); + + ecs_delete(world, bar); + + test_assert(ecs_lookup_symbol(world, "foo.bar", false, false) == e); + test_assert(ecs_lookup_symbol(world, "foo.bar", true, false) == e); + + ecs_fini(world); +} + +void Lookup_lookup_name_escaped_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "foo.bar", + .sep = "" + }); + test_assert(e != 0); + + test_assert(ecs_lookup(world, "foo.bar") == 0); + test_assert(ecs_lookup(world, "foo\\.bar") == e); + + ecs_fini(world); +} + +void Lookup_lookup_path_escaped_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { .name = "parent" }); + + ecs_entity_t e = ecs_entity(world, { + .name = "foo.bar", + .parent = p, + .sep = "" + }); + test_assert(e != 0); + + test_assert(ecs_lookup(world, "parent.foo.bar") == 0); + test_assert(ecs_lookup(world, "parent.foo\\.bar") == e); + + ecs_fini(world); +} + +void Lookup_lookup_name_63_chars(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij" }); + + test_assert(e != 0); + + test_assert(ecs_lookup(world, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij") == e); + + ecs_fini(world); +} + +void Lookup_lookup_name_64_chars(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk" }); + + test_assert(e != 0); + + test_assert(ecs_lookup(world, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk") == e); + + ecs_fini(world); +} + +void Lookup_lookup_name_65_chars(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl" }); + + test_assert(e != 0); + + test_assert(ecs_lookup(world, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl") == e); + + ecs_fini(world); +} + +void Lookup_lookup_path_63_chars(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { + .name = "Abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij" }); + + ecs_entity_t e = ecs_entity(world, { + .name = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij" }); + ecs_add_pair(world, e, EcsChildOf, p); + + test_assert(e != 0); + + test_assert(ecs_lookup(world, "Abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij." + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij") == e); + + ecs_fini(world); +} + +void Lookup_lookup_path_64_chars(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { + .name = "Abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk" }); + + ecs_entity_t e = ecs_entity(world, { + .name = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk" }); + ecs_add_pair(world, e, EcsChildOf, p); + + test_assert(e != 0); + + test_assert(ecs_lookup(world, "Abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk." + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk") == e); + + ecs_fini(world); +} + +void Lookup_lookup_path_65_chars(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_entity(world, { + .name = "Abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl" }); + + ecs_entity_t e = ecs_entity(world, { + .name = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl" }); + ecs_add_pair(world, e, EcsChildOf, p); + + test_assert(e != 0); + + test_assert(ecs_lookup(world, "Abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl." + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl") == e); ecs_fini(world); } diff --git a/vendors/flecs/test/api/src/Monitor.c b/vendors/flecs/test/core/src/Monitor.c similarity index 85% rename from vendors/flecs/test/api/src/Monitor.c rename to vendors/flecs/test/core/src/Monitor.c index a3a72b85d..a336abd51 100644 --- a/vendors/flecs/test/api/src/Monitor.c +++ b/vendors/flecs/test/core/src/Monitor.c @@ -1,4 +1,4 @@ -#include +#include static void OnPosition(ecs_iter_t *it) { @@ -15,7 +15,7 @@ void Monitor_1_comp(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, OnPosition); @@ -47,7 +47,7 @@ void Monitor_2_comps(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_add(world, e, Velocity); @@ -94,7 +94,7 @@ void Monitor_1_comp_1_not(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, OnPosition); @@ -133,15 +133,15 @@ void Monitor_1_parent(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ECS_OBSERVER(world, OnPosition, EcsOnAdd, Position(parent)); + ECS_OBSERVER(world, OnPosition, EcsOnAdd, Position(up)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t parent = ecs_new(world, Position); + ecs_entity_t parent = ecs_new_w(world, Position); test_int(ctx.invoked, 0); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsChildOf, parent); @@ -164,15 +164,15 @@ void Monitor_1_comp_1_parent(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnPosition, EcsMonitor, - Position, Position(up(ChildOf))); + Position, Position(up ChildOf)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t parent = ecs_new(world, Position); + ecs_entity_t parent = ecs_new_w(world, Position); test_int(ctx.invoked, 0); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsChildOf, parent); @@ -278,7 +278,7 @@ void Monitor_monitor_w_and(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -318,7 +318,7 @@ void Monitor_monitor_w_or(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); // No match, filter doesn't match yet @@ -355,7 +355,7 @@ void Monitor_monitor_w_not(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -389,7 +389,7 @@ void Monitor_monitor_w_optional(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 1); // First match, trigger OnAdd @@ -424,20 +424,23 @@ void Monitor_monitor_w_superset(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Velocity(up)); + + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Velocity(up IsA)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t base_1 = ecs_new(world, Velocity); + ecs_entity_t base_1 = ecs_new_w(world, Velocity); test_assert(base_1 != 0); test_int(ctx.invoked, 0); - ecs_entity_t base_2 = ecs_new(world, Velocity); + ecs_entity_t base_2 = ecs_new_w(world, Velocity); test_assert(base_2 != 0); test_int(ctx.invoked, 0); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -471,20 +474,23 @@ void Monitor_monitor_w_self_superset(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Velocity(self|up)); + + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Velocity(self|up IsA)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t base_1 = ecs_new(world, Velocity); + ecs_entity_t base_1 = ecs_new_w(world, Velocity); test_assert(base_1 != 0); test_int(ctx.invoked, 0); - ecs_entity_t base_2 = ecs_new(world, Velocity); + ecs_entity_t base_2 = ecs_new_w(world, Velocity); test_assert(base_2 != 0); test_int(ctx.invoked, 0); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -537,7 +543,7 @@ void Monitor_monitor_w_wildcard(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -585,7 +591,7 @@ void Monitor_monitor_at_fini(void) { Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }}, + .query.terms = {{ TagA }}, .events = {EcsMonitor}, .callback = Monitor, .ctx = &ctx @@ -593,7 +599,7 @@ void Monitor_monitor_at_fini(void) { test_int(ctx.invoked, 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagA); test_int(ctx.invoked, 1); @@ -630,13 +636,13 @@ void Monitor_monitor_other_table(void) { ECS_TAG(world, X); ECS_TAG(world, Y); - ecs_entity_t x = ecs_new(world, X); - ecs_entity_t xy = ecs_new(world, X); + ecs_entity_t x = ecs_new_w(world, X); + ecs_entity_t xy = ecs_new_w(world, X); ecs_add(world, xy, Y); check_table_t ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = { + .query.terms = { { X }, { Y, .oper = EcsNot } }, @@ -648,7 +654,7 @@ void Monitor_monitor_other_table(void) { ctx.table = ecs_get_table(world, x); ctx.other_table = NULL; ctx.event = EcsOnAdd; - ecs_entity_t e = ecs_new(world, X); + ecs_entity_t e = ecs_new_w(world, X); test_int(ctx.invoked, 1); ctx.table = ecs_get_table(world, xy); @@ -681,7 +687,7 @@ static void CheckComponent(ecs_iter_t *it) { test_assert(it->event == ctx->event); - ctx->result = ecs_field(it, Position, 1); + ctx->result = ecs_field(it, Position, 0); ctx->invoked ++; } @@ -693,7 +699,7 @@ void Monitor_monitor_component(void) { check_component_t ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = { + .query.terms = { { ecs_id(Position) }, { Tag, .oper = EcsNot } }, @@ -703,7 +709,7 @@ void Monitor_monitor_component(void) { }); ctx.event = EcsOnAdd; - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); const Position *expect = ecs_get(world, e, Position); test_int(ctx.invoked, 1); test_assert(ctx.result == expect); @@ -727,3 +733,59 @@ void Monitor_monitor_component(void) { test_int(ctx.invoked, 4); test_assert(ctx.result == expect); } + +void Monitor_yield_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsMonitor}, + .callback = Monitor, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_os_zeromem(&ctx); + + ecs_delete(world, t); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/New.c b/vendors/flecs/test/core/src/New.c similarity index 68% rename from vendors/flecs/test/api/src/New.c rename to vendors/flecs/test/core/src/New.c index c7fd13b80..9da94bb90 100644 --- a/vendors/flecs/test/api/src/New.c +++ b/vendors/flecs/test/core/src/New.c @@ -1,4 +1,4 @@ -#include +#include void New_setup(void) { ecs_log_set_level(-2); @@ -7,7 +7,7 @@ void New_setup(void) { void New_empty(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(!ecs_get_type(world, e)); @@ -19,7 +19,7 @@ void New_component(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); @@ -59,11 +59,11 @@ void New_redefine_component(void) { void New_recycle_id_empty(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); ecs_delete(world, e1); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); test_assert(e2 != 0); test_assert(e1 != e2); test_assert((e1 & ECS_ENTITY_MASK) == (e2 & ECS_ENTITY_MASK)); @@ -74,7 +74,7 @@ void New_recycle_id_empty(void) { void New_recycle_id_w_entity(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t tag = ecs_new(world, 0); + ecs_entity_t tag = ecs_new(world); ecs_entity_t e1 = ecs_new_w_id(world, tag); test_assert(e1 != 0); @@ -91,14 +91,14 @@ void New_recycle_id_w_entity(void) { void New_recycle_empty_staged_delete(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); ecs_defer_begin(world); ecs_delete(world, e1); ecs_defer_end(world); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); test_assert(e2 != 0); test_assert(e1 != e2); test_assert((e1 & ECS_ENTITY_MASK) == (e2 & ECS_ENTITY_MASK)); @@ -111,14 +111,14 @@ void New_recycle_staged_delete(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); test_assert(e1 != 0); ecs_defer_begin(world); ecs_delete(world, e1); ecs_defer_end(world); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); test_assert(e2 != 0); test_assert(e1 != e2); test_assert((e1 & ECS_ENTITY_MASK) == (e2 & ECS_ENTITY_MASK)); @@ -129,7 +129,7 @@ void New_recycle_staged_delete(void) { void New_new_id(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(!ecs_get_type(world, e)); @@ -158,7 +158,7 @@ void New_new_component_id_skip_used(void) { test_assert(!ecs_get_type(world, e)); /* Explicitly set an id that is one above the last issued id */ - ecs_ensure(world, e + 1); + ecs_make_alive(world, e + 1); ecs_add_id(world, e + 1, Foo); ecs_entity_t e2 = ecs_new_low_id(world); @@ -181,7 +181,7 @@ void New_new_component_id_skip_to_hi_id(void) { /* Use up all low component ids */ int i; for (i = (int)e; i < FLECS_HI_COMPONENT_ID; i ++) { - ecs_ensure(world, i); + ecs_make_alive(world, i); ecs_add_id(world, i, Foo); } @@ -190,7 +190,7 @@ void New_new_component_id_skip_to_hi_id(void) { test_assert(e2 > FLECS_HI_COMPONENT_ID); test_assert(!ecs_get_type(world, e2)); - ecs_entity_t e3 = ecs_new_id(world); + ecs_entity_t e3 = ecs_new(world); test_assert(e3 != e2); test_assert(e3 > e2); test_assert(!ecs_get_type(world, e3)); @@ -201,7 +201,7 @@ void New_new_component_id_skip_to_hi_id(void) { void New_new_hi_component_id(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_entity_t c; @@ -216,11 +216,12 @@ void New_new_hi_component_id(void) { } void New_new_w_entity_0(void) { + install_test_abort(); + ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_w_id(world, 0); - test_assert(e != 0); - test_assert(ecs_get_type(world, e) == NULL); + test_expect_abort(); + ecs_new_w_id(world, 0); ecs_fini(world); } @@ -240,11 +241,11 @@ void New_create_w_explicit_id_2_worlds(void) { ECS_ENTITY_DEFINE(world_1, Foo, 0); ECS_ENTITY_DEFINE(world_2, Foo, 0); - char *path = ecs_get_fullpath(world_1, Foo); + char *path = ecs_get_path(world_1, Foo); test_str(path, "Parent.Foo"); ecs_os_free(path); - path = ecs_get_fullpath(world_2, Foo); + path = ecs_get_path(world_2, Foo); test_str(path, "Parent.Foo"); ecs_os_free(path); @@ -253,19 +254,16 @@ void New_create_w_explicit_id_2_worlds(void) { } void New_new_w_id_0_w_with(void) { + install_test_abort(); + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ecs_set_with(world, Tag); - ecs_entity_t e = ecs_new_w_id(world, 0); - test_assert(e != 0); - test_assert(ecs_has(world, e, Tag)); - - test_int(ecs_set_with(world, 0), Tag); - - ecs_fini(world); + test_expect_abort(); + ecs_new_w_id(world, 0); } void New_new_w_id_w_with(void) { @@ -278,7 +276,7 @@ void New_new_w_id_w_with(void) { ecs_entity_t e = ecs_new_w_id(world, Tag2); test_assert(e != 0); - test_assert(ecs_has(world, e, Tag)); + test_assert(!ecs_has(world, e, Tag)); test_assert(ecs_has(world, e, Tag2)); test_int(ecs_set_with(world, 0), Tag); @@ -294,14 +292,14 @@ void New_new_w_id_w_with_w_scope(void) { ecs_set_with(world, Tag); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_set_scope(world, parent); ecs_entity_t e = ecs_new_w_id(world, Tag2); test_assert(e != 0); - test_assert(ecs_has(world, e, Tag)); + test_assert(!ecs_has(world, e, Tag)); test_assert(ecs_has(world, e, Tag2)); - test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_assert(!ecs_has_pair(world, e, EcsChildOf, parent)); test_int(ecs_set_with(world, 0), Tag); test_int(ecs_set_scope(world, 0), parent); @@ -327,7 +325,7 @@ void New_new_w_id_w_with_defer(void) { ecs_defer_end(world); - test_assert(ecs_has(world, e, Tag)); + test_assert(!ecs_has(world, e, Tag)); test_assert(ecs_has(world, e, Tag2)); test_int(ecs_set_with(world, 0), Tag); @@ -343,7 +341,7 @@ void New_new_w_id_w_with_defer_w_scope(void) { ecs_set_with(world, Tag); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_set_scope(world, parent); ecs_defer_begin(world); @@ -357,9 +355,9 @@ void New_new_w_id_w_with_defer_w_scope(void) { ecs_defer_end(world); - test_assert(ecs_has(world, e, Tag)); + test_assert(!ecs_has(world, e, Tag)); test_assert(ecs_has(world, e, Tag2)); - test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_assert(!ecs_has_pair(world, e, EcsChildOf, parent)); test_int(ecs_set_with(world, 0), Tag); test_int(ecs_set_scope(world, 0), parent); @@ -367,22 +365,6 @@ void New_new_w_id_w_with_defer_w_scope(void) { ecs_fini(world); } -void New_new_w_type_0_w_with(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - - ecs_set_with(world, Tag); - - ecs_entity_t e = ecs_new(world, 0); - test_assert(e != 0); - test_assert(ecs_has(world, e, Tag)); - - test_int(ecs_set_with(world, 0), Tag); - - ecs_fini(world); -} - void New_new_w_type_w_with(void) { ecs_world_t *world = ecs_mini(); @@ -391,9 +373,9 @@ void New_new_w_type_w_with(void) { ecs_set_with(world, Tag); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); - test_assert(ecs_has(world, e, Tag)); + test_assert(!ecs_has(world, e, Tag)); test_assert(ecs_has(world, e, Position)); test_int(ecs_set_with(world, 0), Tag); @@ -409,14 +391,14 @@ void New_new_w_type_w_with_w_scope(void) { ecs_set_with(world, Tag); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_set_scope(world, parent); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); - test_assert(ecs_has(world, e, Tag)); + test_assert(!ecs_has(world, e, Tag)); test_assert(ecs_has(world, e, Position)); - test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_assert(!ecs_has_pair(world, e, EcsChildOf, parent)); test_int(ecs_set_with(world, 0), Tag); test_int(ecs_set_scope(world, 0), parent); @@ -434,7 +416,7 @@ void New_new_w_type_w_with_defer(void) { ecs_defer_begin(world); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(!ecs_has(world, e, Tag)); @@ -442,7 +424,7 @@ void New_new_w_type_w_with_defer(void) { ecs_defer_end(world); - test_assert(ecs_has(world, e, Tag)); + test_assert(!ecs_has(world, e, Tag)); test_assert(ecs_has(world, e, Position)); test_int(ecs_set_with(world, 0), Tag); @@ -458,12 +440,12 @@ void New_new_w_type_w_with_defer_w_scope(void) { ecs_set_with(world, Tag); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_set_scope(world, parent); ecs_defer_begin(world); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(!ecs_has(world, e, Tag)); @@ -472,12 +454,99 @@ void New_new_w_type_w_with_defer_w_scope(void) { ecs_defer_end(world); - test_assert(ecs_has(world, e, Tag)); + test_assert(!ecs_has(world, e, Tag)); test_assert(ecs_has(world, e, Position)); - test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_assert(!ecs_has_pair(world, e, EcsChildOf, parent)); test_int(ecs_set_with(world, 0), Tag); test_int(ecs_set_scope(world, 0), parent); ecs_fini(world); } + +void New_new_w_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_table_t *table = ecs_table_add_id(world, NULL, Foo); + table = ecs_table_add_id(world, table, Bar); + + ecs_entity_t e = ecs_new_w_table(world, table); + test_assert(e != 0); + test_assert(ecs_has(world, e, Foo)); + test_assert(ecs_has(world, e, Bar)); + test_assert(table == ecs_get_table(world, e)); + + ecs_fini(world); +} + +void New_new_w_null_table(void) { + install_test_abort(); + ecs_world_t *world = ecs_mini(); + + test_expect_abort(); + ecs_new_w_table(world, NULL); +} + +void New_new_w_table_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_table_t *table = ecs_table_add_id(world, NULL, ecs_id(Position)); + table = ecs_table_add_id(world, table, Bar); + + ecs_entity_t e = ecs_new_w_table(world, table); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Bar)); + test_assert(ecs_get(world, e, Position) != NULL); + test_assert(table == ecs_get_table(world, e)); + + ecs_fini(world); +} + +void New_new_w_table_sparse_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_table_t *table = ecs_table_add_id(world, NULL, ecs_id(Position)); + table = ecs_table_add_id(world, table, Bar); + + ecs_entity_t e = ecs_new_w_table(world, table); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Bar)); + test_assert(ecs_get(world, e, Position) != NULL); + test_assert(table == ecs_get_table(world, e)); + + ecs_fini(world); +} + +void New_new_w_table_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_table_t *table = ecs_table_add_id(world, NULL, ecs_pair(EcsIsA, base)); + + ecs_entity_t inst = ecs_new_w_table(world, table); + test_assert(ecs_has(world, inst, Position)); + test_assert(ecs_has_pair(world, inst, EcsIsA, base)); + + const Position *p = ecs_get(world, inst, Position); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/New_w_Count.c b/vendors/flecs/test/core/src/New_w_Count.c similarity index 95% rename from vendors/flecs/test/api/src/New_w_Count.c rename to vendors/flecs/test/core/src/New_w_Count.c index 412eb13c1..005846e14 100644 --- a/vendors/flecs/test/api/src/New_w_Count.c +++ b/vendors/flecs/test/core/src/New_w_Count.c @@ -1,9 +1,9 @@ -#include +#include void New_w_Count_empty(void) { ecs_world_t *world = ecs_mini(); - const ecs_entity_t *ids = ecs_bulk_new(world, 0, 1000); + const ecs_entity_t *ids = ecs_bulk_new_w_id(world, 0, 1000); test_assert(ids != NULL); int i; @@ -545,7 +545,7 @@ void New_w_Count_add_after_bulk(void) { test_assert(ecs_has_id(world, e, Tag)); } - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Tag); test_assert(ecs_has(world, e, Tag)); @@ -568,7 +568,7 @@ void New_w_Count_add_after_bulk_w_component(void) { test_assert(ecs_has(world, e, Position)); } - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); test_assert(ecs_has(world, e, Position)); @@ -581,7 +581,7 @@ void New_w_Count_add_after_bulk_w_ctor(void) { ECS_COMPONENT(world, Position); ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor + .ctor = flecs_default_ctor }); const ecs_entity_t *ids = ecs_bulk_new(world, Position, 10); @@ -595,7 +595,7 @@ void New_w_Count_add_after_bulk_w_ctor(void) { test_assert(ecs_has(world, e, Position)); } - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); test_assert(ecs_has(world, e, Position)); @@ -604,12 +604,12 @@ void New_w_Count_add_after_bulk_w_ctor(void) { } void New_w_Count_recycle_1_of_2(void) { - ecs_world_t *world = ecs_init(); + ecs_world_t *world = ecs_mini(); - ecs_entity_t tag = ecs_new_id(world); + ecs_entity_t tag = ecs_new(world); - /* ecs_entity_t e0 = */ ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); + /* ecs_entity_t e0 = */ ecs_new(world); + ecs_entity_t e1 = ecs_new(world); ecs_delete(world, e1); const ecs_entity_t *ids = ecs_bulk_new_w_id(world, tag, 2); @@ -622,12 +622,12 @@ void New_w_Count_recycle_1_of_2(void) { } void New_w_Count_recycle_1_of_3(void) { - ecs_world_t *world = ecs_init(); + ecs_world_t *world = ecs_mini(); - ecs_entity_t tag = ecs_new_id(world); + ecs_entity_t tag = ecs_new(world); - /* ecs_entity_t e0 = */ ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); + /* ecs_entity_t e0 = */ ecs_new(world); + ecs_entity_t e1 = ecs_new(world); ecs_delete(world, e1); const ecs_entity_t *ids = ecs_bulk_new_w_id(world, tag, 3); @@ -641,12 +641,12 @@ void New_w_Count_recycle_1_of_3(void) { } void New_w_Count_recycle_2_of_3(void) { - ecs_world_t *world = ecs_init(); + ecs_world_t *world = ecs_mini(); - ecs_entity_t tag = ecs_new_id(world); + ecs_entity_t tag = ecs_new(world); - ecs_entity_t e0 = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e0 = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); ecs_delete(world, e1); ecs_delete(world, e0); @@ -663,7 +663,7 @@ void New_w_Count_recycle_2_of_3(void) { } void New_w_Count_bulk_init_w_table(void) { - ecs_world_t *world = ecs_init(); + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); diff --git a/vendors/flecs/test/core/src/Observer.c b/vendors/flecs/test/core/src/Observer.c new file mode 100644 index 000000000..5ddc4f141 --- /dev/null +++ b/vendors/flecs/test/core/src/Observer.c @@ -0,0 +1,9614 @@ +#include + +static +void Observer(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); +} + +static +void Observer_w_value_1(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + test_int(it->count, 1); + test_assert(it->entities != NULL); + test_assert(it->entities[0] != 0); + + Position *p = ecs_field(it, Position, 0); + test_int(p->x, 10); + test_int(p->y, 20); +} + +static +void Observer_w_value_2(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + test_int(it->count, 1); + test_assert(it->entities != NULL); + test_assert(it->entities[0] != 0); + + Position *p = ecs_field(it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + Velocity *v = ecs_field(it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); +} + +static +void Observer_w_1_value(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + test_int(it->count, 1); + test_assert(it->entities != NULL); + test_assert(it->entities[0] != 0); + + Position *p = ecs_field(it, Position, 0); + test_int(p->x, 10); + test_int(p->y, 20); +} + +static +void Observer_w_filter_term(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + test_int(it->count, 1); + test_assert(it->entities != NULL); + test_assert(it->entities[0] != 0); +} + +static +void Observer_w_1_filter_term(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + test_int(it->count, 1); + test_assert(it->entities != NULL); + test_assert(it->entities[0] != 0); + + Velocity *v = ecs_field(it, Velocity, 1); + test_int(v->x, 1); + test_int(v->y, 2); +} + +static +void Observer_w_2_filter_terms(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + test_int(it->count, 1); + test_assert(it->entities != NULL); + test_assert(it->entities[0] != 0); + test_assert(it->sizes != NULL); + + Mass *m = ecs_field(it, Mass, 2); + test_int(m[0], 100); +} + +static bool dummy_called = false; + +static +void Dummy(ecs_iter_t *it) { + dummy_called = true; +} + +void Observer_on_add_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{TagA}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_on_add_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, TagA); // create edge from [] -> [TagA] + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{TagA}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_on_add_wildcard_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{EcsWildcard}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_on_add_wildcard_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, TagA); // create edge from [] -> [TagA] + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{EcsWildcard}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_on_add_R_wildcard_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(Rel, EcsWildcard) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, Rel, TgtA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e, Rel, TgtB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtB)); + + ecs_fini(world); +} + +void Observer_on_add_R_wildcard_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, Rel, TgtA); + ecs_add_pair(world, e1, Rel, TgtB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(Rel, EcsWildcard) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, Rel, TgtA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e2, Rel, TgtB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtB)); + + ecs_fini(world); +} + +void Observer_on_add_wildcard_T_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsWildcard, Tgt) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_add_wildcard_T_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, Tgt); + ecs_add_pair(world, e1, RelB, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsWildcard, Tgt) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e2, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_add_wildcard_wildcard_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsWildcard, EcsWildcard) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_add_wildcard_wildcard_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, Tgt); + ecs_add_pair(world, e1, RelB, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsWildcard, EcsWildcard) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e2, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_add_any_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ EcsAny }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_add_any_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, Tgt); + ecs_add_pair(world, e1, RelB, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ EcsAny }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e2, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_remove_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{TagA}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_remove_id(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_on_remove_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, TagA); + ecs_remove(world, e1, TagA); // create edge from [TagA] -> [] + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{TagA}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, TagA); + test_int(ctx.invoked, 0); + + ecs_remove(world, e2, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_on_remove_wildcard_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{EcsWildcard}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_remove_id(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_on_remove_wildcard_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, TagA); + ecs_remove(world, e1, TagA); // create edge from [TagA] -> [] + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{EcsWildcard}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, TagA); + test_int(ctx.invoked, 0); + + ecs_remove(world, e2, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_on_remove_R_wildcard_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(Rel, EcsWildcard) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, Rel, TgtA); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, Rel, TgtA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e, Rel, TgtB); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, Rel, TgtB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtB)); + + ecs_fini(world); +} + +void Observer_on_remove_R_wildcard_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, Rel, TgtA); + ecs_add_pair(world, e1, Rel, TgtB); + ecs_remove_pair(world, e1, Rel, TgtA); + ecs_remove_pair(world, e1, Rel, TgtB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(Rel, EcsWildcard) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, Rel, TgtA); + ecs_add_pair(world, e2, Rel, TgtB); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e2, Rel, TgtA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + + ecs_os_zeromem(&ctx); + + ecs_remove_pair(world, e2, Rel, TgtB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtB)); + + ecs_fini(world); +} + +void Observer_on_remove_wildcard_T_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsWildcard, Tgt) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, Tgt); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e, RelB, Tgt); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_remove_wildcard_T_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, Tgt); + ecs_add_pair(world, e1, RelB, Tgt); + ecs_remove_pair(world, e1, RelA, Tgt); + ecs_remove_pair(world, e1, RelB, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsWildcard, Tgt) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, RelA, Tgt); + ecs_add_pair(world, e2, RelB, Tgt); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e2, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_remove_pair(world, e2, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_remove_wildcard_wildcard_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsWildcard, EcsWildcard) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, Tgt); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e, RelB, Tgt); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_remove_wildcard_wildcard_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, Tgt); + ecs_add_pair(world, e1, RelB, Tgt); + ecs_remove_pair(world, e1, RelA, Tgt); + ecs_remove_pair(world, e1, RelB, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsWildcard, EcsWildcard) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, RelA, Tgt); + ecs_add_pair(world, e2, RelB, Tgt); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e2, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_remove_pair(world, e2, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_remove_any_before_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ EcsAny }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, Tgt); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, e, RelB, Tgt); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_on_remove_any_after_edge(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, Tgt); + ecs_add_pair(world, e1, RelB, Tgt); + ecs_remove_pair(world, e1, RelA, Tgt); + ecs_remove_pair(world, e1, RelB, Tgt); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ EcsAny }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, RelA, Tgt); + ecs_add_pair(world, e2, RelB, Tgt); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e2, RelA, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelA, Tgt)); + + ecs_os_zeromem(&ctx); + + ecs_remove_pair(world, e2, RelB, Tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e2); + test_int(ctx.c[0][0], ecs_pair(RelB, Tgt)); + + ecs_fini(world); +} + +void Observer_2_terms_w_on_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +void Observer_2_terms_w_on_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_remove_id(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +void Observer_2_terms_w_on_set_value(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_id(Position)}, {ecs_id(Velocity)}}, + .events = {EcsOnSet}, + .callback = Observer_w_value_2, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_set(world, e, Velocity, {1, 2}); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.c[0][1], ecs_id(Velocity)); + + ecs_fini(world); +} + +void Observer_2_terms_w_on_remove_value(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_id(Position)}, {ecs_id(Velocity)}}, + .events = {EcsOnRemove}, + .callback = Observer_w_value_2, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_set(world, e, Velocity, {1, 2}); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.c[0][1], ecs_id(Velocity)); + + ecs_fini(world); +} + +void Observer_2_terms_w_on_set_value_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + {ecs_id(Position), .src.id = EcsSelf|EcsUp}, + {ecs_id(Velocity), .src.id = EcsSelf|EcsUp} + }, + .events = {EcsOnSet}, + .callback = Observer_w_value_2, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_set(world, e, Velocity, {1, 2}); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.c[0][1], ecs_id(Velocity)); + + ecs_fini(world); +} + +void Observer_2_terms_w_on_remove_value_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + {ecs_id(Position), .src.id = EcsSelf|EcsUp}, + {ecs_id(Velocity), .src.id = EcsSelf|EcsUp} + }, + .events = {EcsOnRemove}, + .callback = Observer_w_value_2, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_set(world, e, Velocity, {1, 2}); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.c[0][1], ecs_id(Velocity)); + + ecs_fini(world); +} + +void Observer_2_terms_w_on_add_2nd(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +void Observer_2_terms_w_on_remove_2nd(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_remove_id(world, e, TagB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +void Observer_2_pair_terms_w_on_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + + ecs_id_t pair_a = ecs_pair(RelA, ObjA); + ecs_id_t pair_b = ecs_pair(RelB, ObjB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{pair_a}, {pair_b}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, ObjA); + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e, RelB, ObjB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], pair_a); + test_int(ctx.c[0][1], pair_b); + + ecs_fini(world); +} + +void Observer_2_pair_terms_w_on_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + + ecs_id_t pair_a = ecs_pair(RelA, ObjA); + ecs_id_t pair_b = ecs_pair(RelB, ObjB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{pair_a}, {pair_b}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, ObjA); + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e, RelB, ObjB); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, RelB, ObjB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], pair_a); + test_int(ctx.c[0][1], pair_b); + + ecs_fini(world); +} + +void Observer_2_wildcard_pair_terms_w_on_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, ObjA); + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e, RelB, ObjB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); + test_int(ctx.c[0][1], ecs_pair(RelB, ObjB)); + + ecs_fini(world); +} + +void Observer_2_wildcard_pair_terms_w_on_add_2_matching(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, ObjA); + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e, RelB, ObjA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); + test_int(ctx.c[0][1], ecs_pair(RelB, ObjA)); + + ecs_os_zeromem(&ctx); + ecs_add_pair(world, e, RelB, ObjB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); + test_int(ctx.c[0][1], ecs_pair(RelB, ObjB)); + + ecs_fini(world); +} + +void Observer_2_wildcard_pair_terms_w_on_add_3_matching(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + ECS_TAG(world, ObjC); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, ObjA); + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e, RelB, ObjA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); + test_int(ctx.c[0][1], ecs_pair(RelB, ObjA)); + + ecs_os_zeromem(&ctx); + ecs_add_pair(world, e, RelB, ObjC); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); + test_int(ctx.c[0][1], ecs_pair(RelB, ObjC)); + + ecs_os_zeromem(&ctx); + ecs_add_pair(world, e, RelB, ObjB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); + test_int(ctx.c[0][1], ecs_pair(RelB, ObjB)); + + ecs_fini(world); +} + +void Observer_2_wildcard_pair_terms_w_on_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_pair(world, e, RelA, ObjA); + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e, RelB, ObjB); + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e, RelB, ObjB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, ObjA)); + test_int(ctx.c[0][1], ecs_pair(RelB, ObjB)); + + ecs_fini(world); +} + +void Observer_on_set_n_entities(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Self); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_id(Self)}}, + .events = {EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e_1 = ecs_new(world); + test_int(ctx.invoked, 0); + ecs_set(world, e_1, Self, {e_1}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e_1); + test_int(ctx.c[0][0], ecs_id(Self)); + + ecs_os_zeromem(&ctx); + + ecs_entity_t e_2 = ecs_new(world); + test_int(ctx.invoked, 0); + ecs_set(world, e_2, Self, {e_2}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e_2); + test_int(ctx.c[0][0], ecs_id(Self)); + + ecs_os_zeromem(&ctx); + + ecs_entity_t e_3 = ecs_new(world); + test_int(ctx.invoked, 0); + ecs_set(world, e_3, Self, {e_3}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e_3); + test_int(ctx.c[0][0], ecs_id(Self)); + + ecs_fini(world); +} + +void Observer_on_set_n_entities_2_comp(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Obj); + ECS_COMPONENT(world, Self); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_id(Self)}, {ecs_pair(ecs_id(Self), EcsWildcard)}}, + .events = {EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e_1 = ecs_new(world); + ecs_set(world, e_1, Self, {e_1}); + test_int(ctx.invoked, 0); + ecs_set_pair(world, e_1, Self, Obj, {e_1}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 2); + test_null(ctx.param); + test_int(ctx.e[0], e_1); + test_int(ctx.c[0][0], ecs_id(Self)); + test_int(ctx.c[0][1], ecs_pair(ecs_id(Self), Obj)); + + ecs_os_zeromem(&ctx); + + ecs_entity_t e_2 = ecs_new(world); + ecs_set(world, e_2, Self, {e_2}); + test_int(ctx.invoked, 0); + ecs_set_pair(world, e_2, Self, Obj, {e_2}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 2); + test_null(ctx.param); + test_int(ctx.e[0], e_2); + test_int(ctx.c[0][0], ecs_id(Self)); + test_int(ctx.c[0][1], ecs_pair(ecs_id(Self), Obj)); + + ecs_fini(world); +} + +void Observer_wildcard_pair_w_pred_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_pair(ecs_id(Position), EcsWildcard)}}, + .events = {EcsOnSet}, + .callback = Observer_w_value_1, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + test_int(ctx.invoked, 0); + + ecs_set_pair(world, e, Position, ObjA, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), ObjA)); + + /* Change existing component without triggering OnSet as the callback + * expects value {10, 20}, then add a new component with {10, 20} */ + Position *p = ecs_ensure_pair(world, e, Position, ObjA); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + p->x = 30; + p->y = 40; + + ecs_os_zeromem(&ctx); + + ecs_set_pair(world, e, Position, ObjB, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), ObjB)); + + ecs_fini(world); +} + +void Observer_wildcard_pair_w_obj_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_pair(EcsWildcard, ecs_id(Position)) }}, + .events = {EcsOnSet}, + .callback = Observer_w_value_1, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + test_int(ctx.invoked, 0); + + ecs_set_pair_second(world, e, RelA, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelA, ecs_id(Position))); + + /* Change existing component without triggering OnSet as the callback + * expects value {10, 20}, then add a new component with {10, 20} */ + Position *p = ecs_ensure_pair_second(world, e, RelA, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + p->x = 30; + p->y = 40; + + ecs_os_zeromem(&ctx); + + ecs_set_pair_second(world, e, RelB, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_pair(RelB, ecs_id(Position))); + + ecs_fini(world); +} + +void Observer_2_terms_1_not_w_on_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB, .oper = EcsNot}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_clear(world, e); + test_int(ctx.invoked, 0); + test_assert(!ecs_has(world, e, TagB)); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +void Observer_2_terms_1_not_w_on_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB, .oper = EcsNot}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_remove_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_clear(world, e); + test_assert(!ecs_has(world, e, TagB)); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_remove_id(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +void Observer_2_terms_w_on_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_id(Position)}, {TagB}}, + .events = {EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_add(world, e, Position); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnSet); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +void Observer_2_terms_w_un_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ecs_id(Position)}, {TagB}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_add(world, e, Position); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +void Observer_3_terms_2_or_on_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + {TagA, .src.id = EcsSelf|EcsUp}, + {TagB, .oper = EcsOr, .src.id = EcsSelf|EcsUp}, + {TagC, .src.id = EcsSelf|EcsUp} + }, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_os_zeromem(&ctx); + + ecs_add_id(world, e, TagC); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagC); + + ecs_fini(world); +} + +void Observer_3_terms_2_or_on_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB, .oper = EcsOr}, {TagC }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagC); + test_int(ctx.invoked, 0); + + ecs_remove_id(world, e, TagC); + test_int(ctx.invoked, 1); + + ecs_remove_id(world, e, TagB); + test_int(ctx.invoked, 2); + + ecs_remove_id(world, e, TagA); + test_int(ctx.invoked, 2); + + ecs_fini(world); +} + +void Observer_2_terms_w_from_entity_on_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t x = ecs_new(world); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB, .src.id = x}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_clear(world, e); + test_int(ctx.invoked, 0); + + ecs_add_id(world, x, TagB); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +void Observer_2_terms_on_remove_on_clear(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_clear(world, e); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +void Observer_2_terms_on_remove_on_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{TagA}, {TagB}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_delete(world, e); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +void Observer_add_after_delete_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{.id = ecs_id(Position) }}, + .events = {EcsOnAdd}, + .callback = Dummy + }); + + ecs_entity_t e1 = ecs_new_w(world, Position); + test_assert(e1 != 0); + test_assert(ecs_has(world, e1, Position)); + test_int(dummy_called, 1); + + dummy_called = 0; + + ecs_delete(world, observer); + test_int(dummy_called, 0); + + ecs_entity_t e2 = ecs_new_w(world, Position); + test_assert(e2 != 0); + test_assert(ecs_has(world, e2, Position)); + test_int(dummy_called, 0); + + ecs_fini(world); +} + +void Observer_remove_after_delete_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{.id = ecs_id(Position) }}, + .events = {EcsOnRemove}, + .callback = Dummy + }); + + ecs_entity_t e1 = ecs_new_w(world, Position); + test_assert(e1 != 0); + test_assert(ecs_has(world, e1, Position)); + test_int(dummy_called, 0); + + ecs_remove(world, e1, Position); + test_int(dummy_called, 1); + + dummy_called = 0; + + ecs_delete(world, observer); + test_int(dummy_called, 0); + + ecs_entity_t e2 = ecs_new_w(world, Position); + test_assert(e2 != 0); + test_assert(ecs_has(world, e2, Position)); + test_int(dummy_called, 0); + + ecs_remove(world, e2, Position); + test_int(dummy_called, 0); + + ecs_fini(world); +} + +static int ctx_value; +static +void ctx_free(void *ctx) { + test_assert(&ctx_value == ctx); + ctx_value ++; +} + +static int binding_ctx_value; +static +void binding_ctx_free(void *ctx) { + test_assert(&binding_ctx_value == ctx); + binding_ctx_value ++; +} + +static int ctx_value_2; +static +void ctx_free_2(void *ctx) { + test_assert(&ctx_value_2 == ctx); + ctx_value_2 ++; +} + +static int binding_ctx_value_2; +static +void binding_ctx_free_2(void *ctx) { + test_assert(&binding_ctx_value_2 == ctx); + binding_ctx_value_2 ++; +} + +void Observer_delete_observer_w_ctx(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx_value, + .ctx_free = ctx_free, + .callback_ctx = &binding_ctx_value, + .callback_ctx_free = binding_ctx_free + }); + test_assert(o != 0); + + test_assert(ecs_observer_get(world, o)->ctx == &ctx_value); + test_assert(ecs_observer_get(world, o)->callback_ctx == &binding_ctx_value); + + ecs_delete(world, o); + + test_int(ctx_value, 1); + test_int(binding_ctx_value, 1); + + ecs_fini(world); +} + +void Observer_update_ctx(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + ecs_entity_t system = ecs_observer(world, { + .query.terms = {{.id = Tag}}, + .callback = Dummy, + .events = {EcsOnAdd}, + .ctx = &ctx_value, + .ctx_free = ctx_free, + .callback_ctx = &binding_ctx_value, + .callback_ctx_free = binding_ctx_free + }); + test_assert(system != 0); + + test_assert(ecs_observer_get(world, system)->ctx == &ctx_value); + test_assert(ecs_observer_get(world, system)->callback_ctx + == &binding_ctx_value); + + ecs_observer(world, { + .entity = system, + .ctx = &ctx_value, + .ctx_free = ctx_free, + .callback_ctx = &binding_ctx_value, + .callback_ctx_free = binding_ctx_free + }); + + test_int(ctx_value, 0); + test_int(binding_ctx_value, 0); + test_int(ctx_value_2, 0); + test_int(binding_ctx_value_2, 0); + + ecs_observer(world, { + .entity = system, + .ctx = &ctx_value_2, + .ctx_free = ctx_free_2, + .callback_ctx = &binding_ctx_value_2, + .callback_ctx_free = binding_ctx_free_2 + }); + + test_int(ctx_value, 1); + test_int(binding_ctx_value, 1); + test_int(ctx_value_2, 0); + test_int(binding_ctx_value_2, 0); + + ecs_delete(world, system); + + test_int(ctx_value, 1); + test_int(binding_ctx_value, 1); + test_int(ctx_value_2, 1); + test_int(binding_ctx_value_2, 1); + + ecs_fini(world); +} + +void Observer_filter_w_strings(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{.first.name = "TagA"}, {.first.name = "TagB"}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_add_id(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +static ecs_table_t *trigger_table; + +void TypeObserver(ecs_iter_t *it) { + trigger_table = it->table; +} + +void Observer_iter_type_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd}, + .callback = TypeObserver, + .ctx = &ctx + }); + test_assert(t != 0); + + ecs_new_w(world, Tag); + + test_assert(trigger_table != NULL); + test_assert(trigger_table != NULL); + const ecs_type_t *type = ecs_table_get_type(trigger_table); + test_int(type->count, 1); + test_int(type->array[0], Tag); + + ecs_fini(world); +} + +void ObserverReadonly(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + test_bool(ecs_field_is_readonly(it, 0), true); +} + +void Observer_readonly_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA, .inout = EcsIn }}, + .events = {EcsOnAdd}, + .callback = ObserverReadonly, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + test_assert(e != 0); + ecs_add(world, e, TagA); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, TagA); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_trigger_on_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx_1 = {0}; + Probe ctx_2 = {0}; + + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx_1 + }); + + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }, { EcsPrefab, .oper = EcsOptional}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx_2 + }); + + ecs_new_w(world, TagA); + test_int(ctx_1.invoked, 1); + test_int(ctx_2.invoked, 1); + ecs_os_zeromem(&ctx_1); + ecs_os_zeromem(&ctx_2); + + ecs_entity_t e = ecs_new_w_id(world, EcsPrefab); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + ecs_add(world, e, TagA); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 1); + ecs_os_zeromem(&ctx_2); + + e = ecs_new_w_id(world, EcsDisabled); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + ecs_add(world, e, TagA); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_fini(world); +} + +void Observer_trigger_on_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx_1 = {0}; + Probe ctx_2 = {0}; + + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx_1 + }); + + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }, { EcsDisabled, .oper = EcsOptional}}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx_2 + }); + + ecs_new_w(world, TagA); + test_int(ctx_1.invoked, 1); + test_int(ctx_2.invoked, 1); + ecs_os_zeromem(&ctx_1); + ecs_os_zeromem(&ctx_2); + + ecs_entity_t e = ecs_new_w_id(world, EcsPrefab); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + ecs_add(world, e, TagA); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + ecs_os_zeromem(&ctx_2); + + e = ecs_new_w_id(world, EcsDisabled); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + ecs_add(world, e, TagA); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 1); + + ecs_fini(world); +} + +static +void UnSet(ecs_iter_t *it) { + probe_iter(it); +} + +static +void UnSetA(ecs_iter_t *it) { } + +static +void UnSetB(ecs_iter_t *it) { +} + +void Observer_unset_1_of_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Velocity, {1, 2}); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Velocity); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + + ecs_os_zeromem(&ctx); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 0); + + ecs_os_zeromem(&ctx); + + ecs_fini(world); +} + +void Observer_unset_1_of_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position, Velocity); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Velocity, {1, 2}); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][1], 0); + + ecs_os_zeromem(&ctx); + + ecs_remove(world, e, Velocity); + test_int(ctx.invoked, 0); + + ecs_os_zeromem(&ctx); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 0); + + ecs_os_zeromem(&ctx); + + ecs_fini(world); + + test_int(ctx.invoked, 0); +} + +void Observer_unset_1_of_3(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position, Velocity, Mass); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Velocity, {1, 2}); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Mass, {1}); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 3); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][1], 0); + test_int(ctx.c[0][2], ecs_id(Mass)); + test_int(ctx.s[0][2], 0); + + ecs_os_zeromem(&ctx); + + ecs_remove(world, e, Mass); + test_int(ctx.invoked, 0); + + ecs_os_zeromem(&ctx); + + ecs_remove(world, e, Velocity); + test_int(ctx.invoked, 0); + + ecs_os_zeromem(&ctx); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 0); + + ecs_os_zeromem(&ctx); + + ecs_fini(world); + + test_int(ctx.invoked, 0); +} + +void Observer_unset_on_delete_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_entity_t e = ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_delete(world, e); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + + ecs_fini(world); +} + +void Observer_unset_on_delete_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position, Velocity); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add(world, e1, Velocity); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + test_int(ctx.invoked, 0); + + ecs_entity_t e3 = ecs_new_w(world, Position); + ecs_add(world, e3, Velocity); + test_int(ctx.invoked, 0); + + ecs_delete(world, e3); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e3); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][1], 0); + + ecs_fini(world); +} + +void Observer_unset_on_delete_3(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position, Velocity, Mass); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add(world, e1, Velocity); + ecs_add(world, e1, Mass); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + ecs_add(world, e2, Mass); + test_int(ctx.invoked, 0); + + ecs_entity_t e3 = ecs_new_w(world, Position); + ecs_add(world, e3, Velocity); + ecs_add(world, e3, Mass); + test_int(ctx.invoked, 0); + + ecs_delete(world, e3); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 3); + test_null(ctx.param); + + test_int(ctx.e[0], e3); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][1], 0); + test_int(ctx.c[0][2], ecs_id(Mass)); + test_int(ctx.s[0][2], 0); + + ecs_fini(world); +} + +void Observer_unset_on_fini_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e1 = ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_entity_t e3 = ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_new_w(world, Velocity); + test_int(ctx.invoked, 0); + + ecs_fini(world); + + test_int(ctx.count, 3); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e3); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e1); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); +} + +void Observer_unset_on_fini_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position, Velocity); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add(world, e1, Velocity); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + test_int(ctx.invoked, 0); + + ecs_entity_t e3 = ecs_new_w(world, Position); + ecs_add(world, e3, Velocity); + test_int(ctx.invoked, 0); + + ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_new_w(world, Velocity); + test_int(ctx.invoked, 0); + + ecs_fini(world); + + test_int(ctx.count, 3); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e3); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e1); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][1], 0); +} + +void Observer_unset_on_fini_3(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position, Velocity, Mass); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add(world, e1, Velocity); + ecs_add(world, e1, Mass); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + ecs_add(world, e2, Mass); + test_int(ctx.invoked, 0); + + ecs_entity_t e3 = ecs_new_w(world, Position); + ecs_add(world, e3, Velocity); + ecs_add(world, e3, Mass); + test_int(ctx.invoked, 0); + + ecs_new_w(world, Position); + test_int(ctx.invoked, 0); + + ecs_new_w(world, Velocity); + test_int(ctx.invoked, 0); + + ecs_new_w(world, Mass); + test_int(ctx.invoked, 0); + + ecs_fini(world); + + test_int(ctx.count, 3); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 3); + test_null(ctx.param); + + test_int(ctx.e[0], e3); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e1); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][1], 0); + test_int(ctx.c[0][2], ecs_id(Mass)); + test_int(ctx.s[0][2], 0); +} + +void Observer_overlapping_unset_systems(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + + ECS_OBSERVER(world, UnSetA, EcsOnRemove, Position); + ECS_OBSERVER(world, UnSetB, EcsOnRemove, Position, Velocity); + ECS_OBSERVER(world, UnSet, EcsOnRemove, Position, Velocity); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Velocity); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, UnSet); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][1], 0); + + ecs_fini(world); +} + +static +void UnSet_TestComp(ecs_iter_t *it) { + if (!ecs_get_ctx(it->world)) { + return; + } + + probe_iter(it); + + test_int(it->count, 1); + + Position *p = ecs_field(it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); +} + +void Observer_unset_move_to_nonempty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ECS_OBSERVER(world, UnSet_TestComp, EcsOnRemove, Position); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ECS_ENTITY(world, DummyA, Position, Velocity); + test_int(ctx.invoked, 0); + + ECS_ENTITY(world, DummyB, Position, Velocity); + test_int(ctx.invoked, 0); + + ECS_ENTITY(world, e, Position, Velocity); + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {20, 10}); + test_int(ctx.invoked, 0); + + ecs_entity_t e2 = ecs_new_w(world, Velocity); + ecs_set(world, e2, Velocity, {30, 40}); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, UnSet_TestComp); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + + /* Prevent system from getting called by fini */ + ecs_set_ctx(world, NULL, NULL); + + ecs_fini(world); +} + +static +void UnSet_WriteComp(ecs_iter_t *it) { + if (!ecs_get_ctx(it->world)) { + return; + } + + probe_iter(it); + + test_int(it->count, 1); + + Position *p = ecs_field(it, Position, 0); + test_assert(p != NULL); + + Velocity *v = ecs_field(it, Velocity, 1); + + test_int(p->x, 10); + test_int(p->y, 20); + + test_int(v->x, 1); + test_int(v->y, 2); + + v->x = 2; + v->y = 3; +} + +void Observer_write_in_unset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ECS_OBSERVER(world, UnSet_WriteComp, EcsOnRemove, Position, Velocity); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set(world, e, Velocity, {1, 2}); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_remove(world, e, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, UnSet_WriteComp); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][1], 0); + + /* Prevent system from getting called by fini */ + ecs_set_ctx(world, NULL, NULL); + + const Velocity *v = ecs_get(world, e, Velocity); + test_int(v->x, 2); + test_int(v->y, 3); + + ecs_fini(world); +} + +void Observer_filter_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ .id = ecs_id(Position), .inout = EcsInOutNone }}, + .events = {EcsOnSet}, + .callback = Observer_w_filter_term, + .ctx = &ctx + }); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + test_assert(e1 != 0); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnSet); + test_int(ctx.event_id, ecs_id(Position)); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.c[0][0], ecs_id(Position)); + + ecs_fini(world); +} + +void Observer_2_terms_1_filter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Velocity) } + }, + .events = {EcsOnSet}, + .callback = Observer_w_1_filter_term, + .ctx = &ctx + }); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_insert(world, ecs_value(Velocity, {1, 2})); + test_assert(e1 != 0); + test_int(ctx.invoked, 0); + + ecs_set(world, e1, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnSet); + test_int(ctx.event_id, ecs_id(Position)); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.c[0][1], ecs_id(Velocity)); + + ecs_fini(world); +} + +void Observer_3_terms_2_filter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Velocity), .inout = EcsInOutNone }, + { .id = ecs_id(Mass) } + }, + .events = {EcsOnSet}, + .callback = Observer_w_2_filter_terms, + .ctx = &ctx + }); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_insert(world, ecs_value(Velocity, {1, 2})); + test_assert(e1 != 0); + test_int(ctx.invoked, 0); + + ecs_set(world, e1, Mass, {100}); + ecs_set(world, e1, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnSet); + test_int(ctx.event_id, ecs_id(Position)); + test_int(ctx.term_count, 3); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.c[0][2], ecs_id(Mass)); + + ecs_fini(world); +} + +void Observer_and_from(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_PREFAB(world, Type, TagA, TagB); + + Probe ctx = {0}; + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { Type, .oper = EcsAndFrom } + }, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + test_int(ctx.invoked, 0); + + ecs_add(world, e, TagA); + test_int(ctx.invoked, 0); + + ecs_add(world, e, TagB); + test_int(ctx.invoked, 1); + + ecs_remove(world, e, TagA); + test_int(ctx.invoked, 1); + + ecs_add(world, e, TagA); + test_int(ctx.invoked, 2); + + ecs_fini(world); +} + +void Observer_or_from(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_PREFAB(world, Type, TagA, TagB); + + Probe ctx = {0}; + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { Type, .oper = EcsOrFrom } + }, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + test_int(ctx.invoked, 0); + + ecs_add(world, e, TagA); + test_int(ctx.invoked, 1); + + ecs_add(world, e, TagB); + test_int(ctx.invoked, 2); + + ecs_remove(world, e, TagA); + test_int(ctx.invoked, 2); + + ecs_add(world, e, TagA); + test_int(ctx.invoked, 3); + + ecs_fini(world); +} + +void Observer_and_from_empty(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t Type = ecs_new(world); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { Type, .oper = EcsAndFrom } + }, + .events = {EcsOnAdd, EcsOnRemove, EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + + test_assert(o != 0); + + ecs_fini(world); + + test_int(ctx.invoked, 0); +} + +void Observer_or_from_empty(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t Type = ecs_new(world); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { Type, .oper = EcsOrFrom } + }, + .events = {EcsOnAdd, EcsOnRemove, EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + + test_assert(o != 0); + + ecs_fini(world); + + test_int(ctx.invoked, 0); +} + +void Observer_and_from_empty_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ecs_entity_t Type = ecs_new(world); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { Type, .oper = EcsAndFrom }, + { TagA } + }, + .events = {EcsOnAdd, EcsOnRemove, EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + + test_assert(o != 0); + + ecs_entity_t e = ecs_new_w(world, TagA); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, TagA); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][1], TagA); + + ecs_fini(world); +} + +void Observer_or_from_empty_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ecs_entity_t Type = ecs_new(world); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { Type, .oper = EcsOrFrom }, + { TagA } + }, + .events = {EcsOnAdd, EcsOnRemove, EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + + test_assert(o != 0); + + ecs_entity_t e = ecs_new_w(world, TagA); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, TagA); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][1], TagA); + + ecs_fini(world); +} + +static int invoke_count = 0; +static ecs_entity_t base_ent = 0; +static ecs_entity_t inst_ent_a = 0; +static ecs_entity_t inst_ent_b = 0; + +static void TriggerTwice(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + test_assert(base_ent != 0); + test_assert(inst_ent_a != 0); + test_assert(inst_ent_b != 0); + test_int(it->count, 1); + + if (invoke_count == 0) { + test_assert(it->entities[0] == base_ent); + invoke_count ++; + } else if (invoke_count == 1) { + test_assert(it->entities[0] == inst_ent_b); + invoke_count ++; + } else { + test_int(invoke_count, 2); + test_assert(it->entities[0] == inst_ent_a); + invoke_count ++; + } +} + +void Observer_notify_propagated_twice(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }}, + .events = {EcsOnAdd}, + .callback = TriggerTwice, + .ctx = &ctx + }); + test_assert(t1 != 0); + + base_ent = ecs_new(world); + inst_ent_a = ecs_new_w_pair(world, EcsIsA, base_ent); + ecs_add(world, inst_ent_a, TagB); + inst_ent_b = ecs_new_w_pair(world, EcsIsA, base_ent); + + test_int(ctx.invoked, 0); + + ecs_add(world, base_ent, TagA); + + test_int(ctx.invoked, 3); + test_int(invoke_count, 3); + + ecs_fini(world); +} + +void Observer_on_add_yield_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + // Ensure normal triggering also still works + ecs_os_zeromem(&ctx); + ecs_new_w(world, Tag); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +void Observer_on_add_yield_existing_2_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + ecs_add(world, e3, TagB); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, TagA); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], TagA); + + // Ensure normal triggering also still works + ecs_os_zeromem(&ctx); + ecs_new_w(world, TagA); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +void Observer_on_add_yield_existing_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); /* no match */ + ecs_entity_t e5 = ecs_new_w(world, TagB); /* no match */ + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + test_assert(e4 != 0); + test_assert(e5 != 0); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_add(world, e3, TagC); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }, { TagB }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + test_int(ctx.c[1][0], TagA); + test_int(ctx.c[1][1], TagB); + + // Ensure normal triggering also still works + ecs_os_zeromem(&ctx); + ecs_entity_t e6 = ecs_new_w(world, TagA); + test_int(ctx.invoked, 0); + ecs_add(world, e6, TagB); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +void Observer_on_add_yield_existing_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_pair(Rel, EcsWildcard ) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + test_int(ctx.c[1][0], ecs_pair(Rel, TgtB)); + + ecs_fini(world); +} + +void Observer_on_add_yield_existing_wildcard_multi(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + ecs_new_w_pair(world, Rel, TgtB); + + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_pair(Rel, EcsWildcard ) }, { Tag }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + test_int(ctx.c[0][1], Tag); + test_int(ctx.c[1][0], ecs_pair(Rel, TgtB)); + test_int(ctx.c[1][1], Tag); + + ecs_fini(world); +} + +void Observer_on_add_yield_existing_wildcard_multi_w_wildcard_pivot(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + ecs_new_w(world, Tag); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_pair(Rel, EcsWildcard ) }, { Tag }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + test_int(ctx.c[0][1], Tag); + test_int(ctx.c[1][0], ecs_pair(Rel, TgtB)); + test_int(ctx.c[1][1], Tag); + + ecs_fini(world); +} + +void Observer_on_remove_yield_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_assert(t != 0); + + test_int(ctx.invoked, 0); + + ecs_delete(world, t); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_fini(world); +} + +void Observer_on_remove_yield_existing_2_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + ecs_add(world, e3, TagB); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_assert(t != 0); + + test_int(ctx.invoked, 0); + + ecs_delete(world, t); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, TagA); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], TagA); + + ecs_fini(world); +} + +void Observer_on_remove_yield_existing_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); /* no match */ + ecs_entity_t e5 = ecs_new_w(world, TagB); /* no match */ + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + test_assert(e4 != 0); + test_assert(e5 != 0); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_add(world, e3, TagC); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }, { TagB }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_assert(t != 0); + + test_int(ctx.invoked, 0); + + ecs_delete(world, t); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + test_int(ctx.c[1][0], TagA); + test_int(ctx.c[1][1], TagB); + + ecs_fini(world); +} + +void Observer_on_remove_yield_existing_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_pair(Rel, EcsWildcard ) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_assert(t != 0); + + test_int(ctx.invoked, 0); + + ecs_delete(world, t); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + test_int(ctx.c[1][0], ecs_pair(Rel, TgtB)); + + ecs_fini(world); +} + +void Observer_on_remove_yield_existing_wildcard_multi(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + ecs_new_w_pair(world, Rel, TgtB); + + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_pair(Rel, EcsWildcard ) }, { Tag }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_assert(t != 0); + + test_int(ctx.invoked, 0); + + ecs_delete(world, t); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + test_int(ctx.c[0][1], Tag); + test_int(ctx.c[1][0], ecs_pair(Rel, TgtB)); + test_int(ctx.c[1][1], Tag); + + ecs_fini(world); +} + +void Observer_on_remove_yield_existing_wildcard_multi_w_wildcard_pivot(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + ecs_new_w(world, Tag); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_pair(Rel, EcsWildcard ) }, { Tag }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_assert(t != 0); + + test_int(ctx.invoked, 0); + + ecs_delete(world, t); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtA)); + test_int(ctx.c[0][1], Tag); + test_int(ctx.c[1][0], ecs_pair(Rel, TgtB)); + test_int(ctx.c[1][1], Tag); + + ecs_fini(world); +} + +void Observer_on_add_remove_yield_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_os_zeromem(&ctx); + + ecs_delete(world, t); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_fini(world); +} + +void Observer_on_add_remove_yield_existing_flags(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .flags_ = EcsObserverYieldOnCreate|EcsObserverYieldOnDelete + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_os_zeromem(&ctx); + + ecs_delete(world, t); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_fini(world); +} + +void Observer_on_add_remove_no_on_add_yield_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .flags_ = EcsObserverYieldOnDelete + }); + + test_int(ctx.invoked, 0); + + ecs_delete(world, t); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_fini(world); +} + +void Observer_on_add_remove_no_on_remove_yield_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .flags_ = EcsObserverYieldOnCreate + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_os_zeromem(&ctx); + + ecs_delete(world, t); + + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + +void Observer_yield_existing_flags_w_multi_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add(world, e1, TagB); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add(world, e3, TagB); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }, { TagB }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .flags_ = EcsObserverYieldOnCreate|EcsObserverYieldOnDelete + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_os_zeromem(&ctx); + + ecs_delete(world, t); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], TagB); + + ecs_fini(world); +} + +void Observer_yield_on_create_without_on_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .flags_ = EcsObserverYieldOnCreate|EcsObserverYieldOnDelete + }); + + test_int(ctx.invoked, 0); + + ecs_delete(world, t); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_fini(world); +} + +void Observer_yield_on_delete_without_on_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .flags_ = EcsObserverYieldOnCreate|EcsObserverYieldOnDelete + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_os_zeromem(&ctx); + + ecs_delete(world, t); + + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + +void Observer_yield_existing_w_not_first_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + ecs_add(world, e3, Bar); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Bar, .oper = EcsNot }, { Foo }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 2); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 2); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.c[0][0], Bar); + test_int(ctx.c[0][1], Foo); + + ecs_fini(world); +} + +void Observer_observer_superset_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_entity_t inst = ecs_new(world); + ecs_add_pair(world, inst, EcsIsA, base); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ + .id = ecs_pair(Rel, EcsWildcard), + .src.id = EcsUp, + .trav = EcsIsA + }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(t != 0); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, base, Rel, ObjA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, ecs_pair(Rel, ObjA)); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], inst); + test_int(ctx.s[0][0], base); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, base, Rel, ObjB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, ecs_pair(Rel, ObjB)); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], inst); + test_int(ctx.s[0][0], base); + + ecs_fini(world); +} + +void Observer_observer_superset_wildcard_add_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_add_pair(world, base, Rel, ObjA); + ecs_add_pair(world, base, Rel, ObjB); + + ecs_entity_t inst = ecs_new(world); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ + .id = ecs_pair(Rel, EcsWildcard), + .src.id = EcsUp, + .trav = EcsIsA + }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(t != 0); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, inst, EcsIsA, base); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], inst); + test_int(ctx.s[0][0], base); + + ecs_fini(world); +} + +void Observer_observer_superset_wildcard_add_isa_at_offset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ECS_TAG(world, Rel); + ECS_TAG(world, ObjA); + ECS_TAG(world, ObjB); + + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_add(world, base, Tag); + ecs_add_pair(world, base, Rel, ObjA); + ecs_add_pair(world, base, Rel, ObjB); + + ecs_entity_t inst = ecs_new(world); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ + .id = ecs_pair(Rel, EcsWildcard), + .src.id = EcsUp, + .trav = EcsIsA + }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(t != 0); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, inst, EcsIsA, base); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], inst); + test_int(ctx.s[0][0], base); + + ecs_fini(world); +} + +void Observer_on_set_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }}, + .events = {EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(t1 != 0); + + ecs_entity_t e = ecs_new(world); + test_assert(e != 0); + + ecs_add(world, e, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, TagA); + + ecs_fini(world); +} + +void Observer_mixed_on_set_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }, { ecs_id(Position) }}, + .events = {EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(t1 != 0); + + ecs_entity_t e1 = ecs_new(world); + test_assert(e1 != 0); + + ecs_add(world, e1, TagA); + test_int(ctx.invoked, 0); + + ecs_add(world, e1, Position); + test_int(ctx.invoked, 0); + + ecs_set(world, e1, Position, {10, 20}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.event, EcsOnSet); + test_int(ctx.event_id, ecs_id(Position)); + + ctx = (Probe){0}; + + ecs_entity_t e2 = ecs_new(world); + test_assert(e2 != 0); + + ecs_set(world, e2, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_add(world, e2, TagA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, TagA); + + ecs_fini(world); +} + +void Observer_mixed_un_set_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }, { ecs_id(Position) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(t1 != 0); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, TagA); + ecs_set(world, e1, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_remove(world, e1, TagA); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, TagA); + + ctx = (Probe){0}; + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {10, 20}); + ecs_add(world, e2, TagA); + test_int(ctx.invoked, 0); + + ecs_remove(world, e2, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, ecs_id(Position)); + + ecs_fini(world); +} + +void Observer_match_base_w_id_at_offset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + Probe ctx_1 = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_id(Position) }}, + .events = {EcsOnSet}, + .callback = Observer, + .ctx = &ctx_1 + }); + test_assert(t1 != 0); + + Probe ctx_2 = {0}; + ecs_entity_t t2 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_id(Velocity) }}, + .events = {EcsOnSet}, + .callback = Observer, + .ctx = &ctx_2 + }); + test_assert(t2 != 0); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + ecs_set(world, base, Position, {10, 20}); + ecs_set(world, base, Velocity, {1, 2}); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], inst); + test_int(ctx_1.event, EcsOnSet); + test_int(ctx_1.event_id, ecs_id(Position)); + + test_int(ctx_2.invoked, 1); + test_int(ctx_2.count, 1); + test_int(ctx_2.e[0], inst); + test_int(ctx_2.event, EcsOnSet); + test_int(ctx_2.event_id, ecs_id(Velocity)); + + ecs_fini(world); +} + +static int run_invoked = 0; +static int run_invoked_matched = 0; + +static void Run(ecs_iter_t *it) { + run_invoked ++; + + test_assert(it != NULL); + test_assert(it->next != NULL); + test_assert(it->callback != NULL); + + while (ecs_iter_next(it)) { + it->callback(it); + run_invoked_matched ++; + } +} + +static void Run_w_1_field(ecs_iter_t *it) { + run_invoked ++; + + test_assert(it != NULL); + test_assert(it->next != NULL); + + while (ecs_iter_next(it)) { + test_int(it->count, 1); + Position *p = ecs_field(it, Position, 0); + test_int(p->x, 10); + test_int(p->y, 20); + + run_invoked_matched ++; + } +} + +static void Run_w_1_field_w_callback(ecs_iter_t *it) { + run_invoked ++; + + test_assert(it != NULL); + test_assert(it->next != NULL); + + while (ecs_iter_next(it)) { + test_int(it->count, 1); + Position *p = ecs_field(it, Position, 0); + test_int(p->x, 10); + test_int(p->y, 20); + + it->callback(it); + + run_invoked_matched ++; + } +} + +static void Run_w_2_fields(ecs_iter_t *it) { + run_invoked ++; + + test_assert(it != NULL); + test_assert(it->next != NULL); + + while (ecs_iter_next(it)) { + test_int(it->count, 1); + Position *p = ecs_field(it, Position, 0); + test_int(p->x, 10); + test_int(p->y, 20); + + Velocity *v = ecs_field(it, Velocity, 1); + test_int(v->x, 1); + test_int(v->y, 2); + + run_invoked_matched ++; + } +} + +static void Run_w_2_fields_w_callback(ecs_iter_t *it) { + run_invoked ++; + + test_assert(it != NULL); + test_assert(it->next != NULL); + + while (ecs_iter_next(it)) { + test_int(it->count, 1); + Position *p = ecs_field(it, Position, 0); + test_int(p->x, 10); + test_int(p->y, 20); + + Velocity *v = ecs_field(it, Velocity, 1); + test_int(v->x, 1); + test_int(v->y, 2); + + it->callback(it); + + run_invoked_matched ++; + } +} + +void Observer_custom_run_action(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }}, + .events = {EcsOnAdd}, + .run = Run, + .callback = Observer, + .ctx = &ctx + }); + test_assert(t1 != 0); + + ecs_entity_t e = ecs_new_w(world, TagA); + + test_int(run_invoked, 1); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_custom_run_action_w_field(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_id(Position) }}, + .events = {EcsOnSet}, + .run = Run_w_1_field, + }); + test_assert(t1 != 0); + + ecs_insert(world, ecs_value(Position, {10, 20})); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + + ecs_fini(world); +} + +void Observer_custom_run_action_w_2_fields(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, + .events = {EcsOnSet}, + .run = Run_w_2_fields, + }); + test_assert(t1 != 0); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + test_int(run_invoked, 0); + + ecs_set(world, e, Velocity, {1, 2}); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + + ecs_fini(world); +} + +void Observer_custom_run_w_yield_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_id(Position) }}, + .events = {EcsOnSet}, + .run = Run, + .callback = Observer, + .yield_existing = true, + .ctx = &ctx + }); + test_assert(t1 != 0); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.event, EcsOnSet); + + run_invoked = 0; + run_invoked_matched = 0; + ecs_os_memset(&ctx, 0, sizeof(Probe)); + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.event, EcsOnSet); + + ecs_fini(world); +} + +void Observer_custom_run_w_yield_existing_1_field(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_id(Position) }}, + .events = {EcsOnSet}, + .run = Run_w_1_field, + .yield_existing = true + }); + test_assert(t1 != 0); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + + run_invoked = 0; + run_invoked_matched = 0; + + ecs_insert(world, ecs_value(Position, {10, 20})); + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + + ecs_fini(world); +} + +void Observer_custom_run_w_yield_existing_1_field_w_callback(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_id(Position) }}, + .events = {EcsOnSet}, + .run = Run_w_1_field_w_callback, + .callback = Observer_w_value_1, + .yield_existing = true, + .ctx = &ctx + }); + test_assert(t1 != 0); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.event, EcsOnSet); + + run_invoked = 0; + run_invoked_matched = 0; + ecs_os_memset(&ctx, 0, sizeof(Probe)); + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.event, EcsOnSet); + + ecs_fini(world); +} + +void Observer_custom_run_w_yield_existing_2_fields(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, + .events = {EcsOnSet}, + .run = Run_w_2_fields, + .callback = Observer, + .yield_existing = true, + }); + test_assert(t1 != 0); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + + run_invoked = 0; + run_invoked_matched = 0; + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + test_int(run_invoked, 0); + + ecs_set(world, e2, Velocity, {1, 2}); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + + ecs_fini(world); +} + +void Observer_custom_run_w_yield_existing_2_fields_w_callback(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, + .events = {EcsOnSet}, + .run = Run_w_2_fields_w_callback, + .callback = Observer_w_value_2, + .yield_existing = true, + .ctx = &ctx + }); + test_assert(t1 != 0); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.event, EcsOnSet); + + run_invoked = 0; + run_invoked_matched = 0; + ecs_os_memset(&ctx, 0, sizeof(Probe)); + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + test_int(run_invoked, 0); + + ecs_set(world, e2, Velocity, {1, 2}); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.event, EcsOnSet); + + ecs_fini(world); +} + +void Observer_custom_run_action_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }, { TagB }}, + .events = {EcsOnAdd}, + .run = Run, + .callback = Observer, + .ctx = &ctx + }); + test_assert(t1 != 0); + + ecs_entity_t e = ecs_new_w(world, TagA); + test_int(run_invoked, 0); + + ecs_add(world, e, TagB); + + test_int(run_invoked, 1); + test_int(run_invoked_matched, 1); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_custom_run_action_w_iter_next_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ TagA }, { TagB }}, + .events = {EcsOnAdd}, + .run = Run, + .callback = Observer, + .ctx = &ctx + }); + test_assert(t1 != 0); + + ecs_entity_t e = ecs_new_w(world, TagA); + test_int(run_invoked, 0); + + ecs_add(world, e, TagB); + test_int(run_invoked, 1); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_read_in_on_remove_after_add_other_w_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + ECS_TAG(world, Tgt); + + Probe ctx = {0}; + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { .id = ecs_pair_t(Position, Tgt)}, + { .id = Tag, .oper = EcsNot } + }, + .events = {EcsOnRemove}, + .callback = Observer_w_1_value, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_insert(world, ecs_value_pair(Position, Tgt, { 10, 20 })); + test_int(ctx.invoked, 0); + + ecs_add(world, e, Tag); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_observer_w_short_notation(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ + .id = Foo + }}, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + test_assert(o != 0); + + ecs_entity_t e = ecs_new_w(world, Foo); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_observer_w_filter_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ .id = TagA }, { .id = TagB, .inout = EcsInOutFilter }}, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + test_int(ctx.invoked, 0); + + ecs_add(world, e, TagB); + test_int(ctx.invoked, 0); + + ecs_add(world, e, TagA); + test_int(ctx.invoked, 1); + + ecs_remove(world, e, TagB); + ecs_add(world, e, TagB); + test_int(ctx.invoked, 1); + + ecs_clear(world, e); + test_int(ctx.invoked, 1); + + ecs_add(world, e, TagA); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +static int free_ctx_invoked = 0; + +static +void free_ctx(void *ctx) { + free_ctx_invoked ++; + ecs_os_free(ctx); +} + +static int observer_w_ctx_invoked = 0; + +static void Observer_w_ctx(ecs_iter_t *it) { + test_assert(it->ctx != NULL); + test_int(*(int*)it->ctx, 10); + observer_w_ctx_invoked ++; +} + +void Observer_multi_observer_w_ctx_free(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + int *ctx = ecs_os_malloc_t(int); + *ctx = 10; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ .id = TagA }, { .id = TagB }}, + .events = { EcsOnAdd }, + .callback = Observer_w_ctx, + .ctx = ctx, + .ctx_free = free_ctx + }); + + test_int(observer_w_ctx_invoked, 0); + test_int(free_ctx_invoked, 0); + + ecs_entity_t e = ecs_new_w(world, TagA); + test_int(observer_w_ctx_invoked, 0); + test_int(free_ctx_invoked, 0); + ecs_add(world, e, TagB); + test_int(observer_w_ctx_invoked, 1); + test_int(free_ctx_invoked, 0); + + ecs_delete(world, o); + + test_int(free_ctx_invoked, 1); + + ecs_fini(world); + + test_int(free_ctx_invoked, 1); +} + +void Observer_propagate_after_on_delete_clear_action(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_entity_t tgt = ecs_new(world); + ecs_entity_t parent = ecs_new(world); + ecs_add_pair(world, parent, Rel, tgt); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ + .id = ecs_pair(Rel, EcsWildcard), + .src.id = EcsUp, + .trav = EcsChildOf + }}, + .events = { EcsOnRemove }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + test_int(ctx.invoked, 0); + + ecs_delete(world, tgt); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], child); + test_int(ctx.s[0][0], parent); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_on_add_after_batch_w_exclusive_adds(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Exclusive); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ + ecs_pair(Rel, EcsWildcard), + .src.id = EcsSelf + }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + ecs_add_pair(world, e, Rel, TgtA); + ecs_add_pair(world, e, Rel, TgtB); + ecs_defer_end(world); + + test_assert(!ecs_has_pair(world, e, Rel, TgtA)); + test_assert(ecs_has_pair(world, e, Rel, TgtB)); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], ecs_pair(Rel, TgtB)); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_propagate_match_relationship_w_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagB, .src.id = EcsSelf|EcsUp, .trav = EcsIsA }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t parent = ecs_new(world); + ecs_add(world, parent, TagA); + ecs_new_w_pair(world, EcsChildOf, parent); + test_int(ctx.invoked, 0); + + ecs_add(world, parent, TagB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], parent); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], TagB); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_propagate_match_relationship_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagB, .src.id = EcsUp, .trav = EcsIsA }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t parent = ecs_new(world); + ecs_add(world, parent, TagA); + ecs_new_w_pair(world, EcsChildOf, parent); + test_int(ctx.invoked, 0); + + ecs_add(world, parent, TagB); + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + +void Observer_propagate_isa_of_parent_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new(world); + ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 0); + + ecs_add(world, base, TagA); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], child); + test_int(ctx.s[0][0], base); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_propagate_isa_of_parent_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new(world); + ecs_add(world, base, TagA); + ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 0); + + ecs_remove(world, base, TagA); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], child); + test_int(ctx.s[0][0], base); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_propagate_isa_of_parent_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ ecs_id(Position), .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnSet}, + .callback = Observer_w_1_value, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {0, 0})); + ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 0); + + ecs_set(world, base, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], child); + test_int(ctx.s[0][0], base); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.event, EcsOnSet); + + ecs_fini(world); +} + +void Observer_propagate_add_childof_of_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t grandparent = ecs_new(world); + ecs_add(world, grandparent, TagA); + ecs_entity_t parent = ecs_new(world); + ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, parent, EcsChildOf, grandparent); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.s[0][0], grandparent); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_propagate_add_childof_of_parent_w_siblings(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t grandparent = ecs_new(world); + ecs_add(world, grandparent, TagA); + + { + ecs_entity_t parent = ecs_new_w_pair(world, EcsChildOf, grandparent); + ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.e[0], parent); + test_int(ctx.s[0][0], grandparent); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + } + + ecs_os_zeromem(&ctx); + + { + ecs_entity_t parent = ecs_new_w_pair(world, EcsChildOf, grandparent); + ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.e[0], parent); + test_int(ctx.s[0][0], grandparent); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + } + + ecs_os_zeromem(&ctx); + + { + ecs_entity_t parent = ecs_new_w_pair(world, EcsChildOf, grandparent); + ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.e[0], parent); + test_int(ctx.s[0][0], grandparent); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + } + + ecs_fini(world); +} + +void Observer_propagate_add_childof_w_parent_w_base(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new_w(world, TagA); + ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); + + test_int(ctx.invoked, 0); + + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], child); + test_int(ctx.s[0][0], base); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_propagate_remove_childof_w_parent_w_base(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new_w(world, TagA); + ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, child, EcsChildOf, parent); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], child); + test_int(ctx.s[0][0], base); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_propagate_remove_childof_of_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t grandparent = ecs_new(world); + ecs_add(world, grandparent, TagA); + ecs_entity_t parent = ecs_new_w_pair(world, EcsChildOf, grandparent); + ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, parent, EcsChildOf, grandparent); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.s[0][0], grandparent); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_propagate_add_isa_of_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new(world); + ecs_add(world, base, TagA); + ecs_entity_t parent = ecs_new(world); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, parent, EcsIsA, base); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], child); + test_int(ctx.s[0][0], base); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_propagate_remove_isa_of_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new(world); + ecs_add(world, base, TagA); + ecs_entity_t parent = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, parent, EcsIsA, base); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], child); + test_int(ctx.s[0][0], base); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_propagate_add_childof_of_base(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new(world); + ecs_add(world, base, TagA); + ecs_entity_t parent = ecs_new(world); + ecs_new_w_pair(world, EcsIsA, parent); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, parent, EcsChildOf, base); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], parent); + test_int(ctx.s[0][0], base); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_propagate_remove_childof_of_base(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagA, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new(world); + ecs_add(world, base, TagA); + ecs_entity_t parent = ecs_new_w_pair(world, EcsChildOf, base); + ecs_new_w_pair(world, EcsIsA, parent); + + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, parent, EcsChildOf, base); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], parent); + test_int(ctx.s[0][0], base); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_emit_for_parent_w_prefab_child_and_instance(void) { + ecs_world_t* world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) } + }, + .events = { EcsOnSet }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + ecs_entity_t p = ecs_new_w_pair(world, EcsChildOf, e); + ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, e); + ecs_add_pair(world, child_1, EcsIsA, p); + + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.event, EcsOnSet); + + ecs_fini(world); +} + +void Observer_observer_w_2_fixed_src(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t Foo = ecs_new(world); + ecs_entity_t Bar = ecs_new(world); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + Probe ctx = {0}; + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = { + { Foo, .src.id = e1 }, + { Bar, .src.id = e2 }, + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_add_id(world, e1, Foo); + test_int(ctx.invoked, 0); + + ecs_add_id(world, e2, Bar); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +void Observer_emit_for_recreated_id_after_remove_all(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add_pair(world, e1, Rel, Tgt); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = { + { TagA }, + { ecs_pair(Rel, Tgt) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_remove_all(world, ecs_pair(Rel, Tgt)); + test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt))); + test_assert(ecs_is_alive(world, e1)); + + test_int(0, ctx.invoked); + + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add_pair(world, e2, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_emit_for_recreated_id_after_remove_all_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add_pair(world, e1, Rel, Tgt); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = { + { TagA }, + { ecs_pair(Rel, Tgt) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_remove_all(world, ecs_pair(Rel, EcsWildcard)); + test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt))); + test_assert(ecs_is_alive(world, e1)); + + test_int(0, ctx.invoked); + + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add_pair(world, e2, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_emit_for_recreated_id_after_delete_with(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add_pair(world, e1, Rel, Tgt); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = { + { TagA }, + { ecs_pair(Rel, Tgt) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_delete_with(world, ecs_pair(Rel, Tgt)); + test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt))); + test_assert(!ecs_is_alive(world, e1)); + + test_int(0, ctx.invoked); + + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add_pair(world, e2, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_emit_for_recreated_id_after_delete_with_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add_pair(world, e1, Rel, Tgt); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = { + { TagA }, + { ecs_pair(Rel, Tgt) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_delete_with(world, ecs_pair(Rel, EcsWildcard)); + test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt))); + test_assert(!ecs_is_alive(world, e1)); + + test_int(0, ctx.invoked); + + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add_pair(world, e2, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], TagA); + test_int(ctx.c[0][1], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_delete_observed_id(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add_pair(world, e1, Rel, Tgt); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = { + { TagA }, + { ecs_pair(Rel, Tgt) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + test_expect_abort(); + ecs_delete(world, TagA); +} + +void Observer_delete_observed_rel(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add_pair(world, e1, Rel, Tgt); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = { + { TagA }, + { ecs_pair(Rel, Tgt) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + test_expect_abort(); + ecs_delete(world, Rel); +} + +void Observer_delete_observed_tgt(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add_pair(world, e1, Rel, Tgt); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = { + { TagA }, + { ecs_pair(Rel, Tgt) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + test_expect_abort(); + ecs_delete(world, Tgt); +} + +static int pair_x = 0; +static int32_t pair_column = 0; + +static +void OnTagPair(ecs_iter_t *it) { + test_int(it->count, 1); + pair_column = it->trs[0]->index; + probe_iter(it); +} + +static +void OnPair(ecs_iter_t *it) { + test_int(it->count, 1); + Position *p = ecs_field(it, Position, 0); + pair_x = p->x; + pair_column = it->trs[0]->index; + probe_iter(it); +} + +void Observer_on_add_2_pairs_w_uni_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, Tag); + ECS_OBSERVER(world, OnTagPair, EcsOnAdd, (Rel, *)); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Tag); + ecs_add_pair(world, e, Rel, TgtA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnTagPair); + test_int(ctx.term_count, 1); + test_int(ctx.event_id, ecs_pair(Rel, TgtA)); + test_int(pair_column, 1); + + ecs_os_zeromem(&ctx); + ecs_add_pair(world, e, Rel, TgtB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnTagPair); + test_int(ctx.term_count, 1); + test_int(ctx.event_id, ecs_pair(Rel, TgtB)); + test_int(pair_column, 2); + + ecs_os_zeromem(&ctx); + ecs_add_pair(world, e, Rel, TgtC); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnTagPair); + test_int(ctx.term_count, 1); + test_int(ctx.event_id, ecs_pair(Rel, TgtC)); + test_int(pair_column, 3); + + ecs_fini(world); +} + +void Observer_on_add_2_pairs_w_multi_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, Tag); + ECS_OBSERVER(world, OnTagPair, EcsOnAdd, (Rel, *), Tag); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Tag); + ecs_add_pair(world, e, Rel, TgtA); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnTagPair); + test_int(ctx.term_count, 2); + test_int(ctx.event_id, ecs_pair(Rel, TgtA)); + test_int(pair_column, 1); + + ecs_os_zeromem(&ctx); + ecs_add_pair(world, e, Rel, TgtB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnTagPair); + test_int(ctx.term_count, 2); + test_int(ctx.event_id, ecs_pair(Rel, TgtB)); + test_int(pair_column, 2); + + ecs_os_zeromem(&ctx); + ecs_add_pair(world, e, Rel, TgtC); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnTagPair); + test_int(ctx.term_count, 2); + test_int(ctx.event_id, ecs_pair(Rel, TgtC)); + test_int(pair_column, 3); + + ecs_fini(world); +} + +void Observer_on_set_2_pairs_w_uni_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, Tag); + ECS_OBSERVER(world, OnPair, EcsOnSet, (Position, *)); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Tag); + ecs_set_pair(world, e, Position, TgtA, {1}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnPair); + test_int(ctx.term_count, 1); + test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtA)); + test_int(pair_column, 1); + test_int(pair_x, 1); + + ecs_os_zeromem(&ctx); + ecs_set_pair(world, e, Position, TgtB, {2}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnPair); + test_int(ctx.term_count, 1); + test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtB)); + test_int(pair_column, 2); + test_int(pair_x, 2); + + ecs_os_zeromem(&ctx); + ecs_set_pair(world, e, Position, TgtC, {3}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnPair); + test_int(ctx.term_count, 1); + test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtC)); + test_int(pair_column, 3); + test_int(pair_x, 3); + + ecs_fini(world); +} + +void Observer_on_set_2_pairs_w_multi_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, Tag); + ECS_OBSERVER(world, OnPair, EcsOnSet, (Position, *), Tag); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Tag); + ecs_set_pair(world, e, Position, TgtA, {1}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnPair); + test_int(ctx.term_count, 2); + test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtA)); + test_int(pair_column, 1); + test_int(pair_x, 1); + + ecs_os_zeromem(&ctx); + ecs_set_pair(world, e, Position, TgtB, {2}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnPair); + test_int(ctx.term_count, 2); + test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtB)); + test_int(pair_column, 2); + test_int(pair_x, 2); + + ecs_os_zeromem(&ctx); + ecs_set_pair(world, e, Position, TgtC, {3}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnPair); + test_int(ctx.term_count, 2); + test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtC)); + test_int(pair_column, 3); + test_int(pair_x, 3); + + ecs_fini(world); +} + +void Observer_on_remove_target_from_base_at_offset(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t R = ecs_new(world); + ecs_entity_t T1 = ecs_new(world); + ecs_entity_t T2 = ecs_new(world); + ecs_entity_t C = ecs_new(world); + + ecs_add_pair(world, R, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, C, EcsOnInstantiate, EcsInherit); + + Probe ctx = { 0 }; + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_pair(R, EcsWildcard), .src.id = EcsUp, .trav = EcsIsA }, + { .id = C }, + }, + .events = { EcsOnRemove }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new(world); + ecs_add_pair(world, base, R, T1); + ecs_add_pair(world, base, R, T2); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsIsA, base); + ecs_add_id(world, e, C); + + test_int(ctx.invoked, 0); + ecs_delete(world, T2); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.term_count, 2); + test_int(ctx.event_id, ecs_pair(R, T2)); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +static void Observer_base_component(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + Position *p = ecs_field(it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); +} + +void Observer_on_remove_target_component_from_base_at_offset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t T1 = ecs_new(world); + ecs_entity_t T2 = ecs_new(world); + ecs_entity_t C = ecs_new(world); + + ecs_add_pair(world, C, EcsOnInstantiate, EcsInherit); + + Probe ctx = { 0 }; + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_pair(ecs_id(Position), EcsWildcard), .src.id = EcsUp, .trav = EcsIsA }, + { .id = C }, + }, + .events = { EcsOnRemove }, + .callback = Observer_base_component, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new(world); + ecs_set_pair(world, base, Position, T1, {10, 20}); + ecs_set_pair(world, base, Position, T2, {30, 40}); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsIsA, base); + ecs_add_id(world, e, C); + + test_int(ctx.invoked, 0); + ecs_delete(world, T2); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.term_count, 2); + test_int(ctx.e[0], e); + test_int(ctx.s[0][0], base); + test_int(ctx.s[0][1], 0); + test_int(ctx.event_id, ecs_pair(ecs_id(Position), T2)); + test_int(ctx.event, EcsOnRemove); + + ecs_delete(world, o); + + ecs_fini(world); +} + +static void Observer_w_other_table(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + test_assert(it->table != NULL); + test_assert(it->other_table != NULL); +} + +static void Observer_dummy(ecs_iter_t *it) {} + +void Observer_wildcard_propagate_w_other_table(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + ecs_entity_t rel = ecs_new(world); + ecs_entity_t tgt_1 = ecs_new(world); + + ecs_entity_t parent = ecs_new_w_id(world, tag); + ecs_new_w_pair(world, EcsChildOf, parent); + + Probe ctx_parent = {0}; + + ecs_observer(world, { + .query.terms = {{ ecs_pair(rel, EcsWildcard) }}, + .events = {EcsOnAdd}, + .callback = Observer_dummy + }); + + ecs_observer(world, { + .query.terms = {{ ecs_pair(rel, EcsWildcard) }}, + .events = {EcsWildcard}, + .callback = Observer_w_other_table, + .ctx = &ctx_parent + }); + + ecs_observer(world, { + .query.terms = {{ ecs_pair(rel, EcsWildcard), .src.id = EcsUp }}, + .events = {EcsWildcard}, + .callback = Observer_dummy + }); + + ecs_add_pair(world, parent, rel, tgt_1); + + test_int(ctx_parent.invoked, 1); + + ecs_fini(world); +} + +void Observer_disable_observer(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(ecs, { + .query.terms = { + { .id = ecs_id(Position) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, o, false); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, o, true); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_fini(ecs); +} + +void Observer_disable_observer_module(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + + ecs_entity_t module = ecs_new_w_id(ecs, EcsModule); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(ecs, { + .entity = ecs_entity(ecs, { .parent = module }), + .query.terms = { + { .id = ecs_id(Position) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, false); + test_assert(!ecs_has_id(ecs, o, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, true); + test_assert(!ecs_has_id(ecs, o, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_fini(ecs); +} + +void Observer_disable_observer_module_nested(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + + ecs_entity_t module = ecs_new_w_id(ecs, EcsModule); + ecs_entity_t module_child = ecs_new_w_id(ecs, EcsModule); + ecs_add_pair(ecs, module_child, EcsChildOf, module); + + Probe ctx = {0}; + ecs_observer(ecs, { + .entity = ecs_entity(ecs, { .parent = module_child }), + .query.terms = { + { .id = ecs_id(Position) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, false); + test_assert(!ecs_has_id(ecs, module_child, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module_child, false); + test_assert(ecs_has_id(ecs, module_child, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, true); + test_assert(ecs_has_id(ecs, module_child, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module_child, true); + test_assert(!ecs_has_id(ecs, module_child, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_fini(ecs); +} + +void Observer_disable_observer_and_module(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + + ecs_entity_t module = ecs_new_w_id(ecs, EcsModule); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(ecs, { + .entity = ecs_entity(ecs, { .parent = module }), + .query.terms = { + { .id = ecs_id(Position) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, false); + test_assert(!ecs_has_id(ecs, o, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, o, false); + test_assert(ecs_has_id(ecs, o, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, true); + test_assert(ecs_has_id(ecs, o, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, o, true); + test_assert(!ecs_has_id(ecs, o, EcsDisabled)); + + ecs_new_w(ecs, Position); + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_fini(ecs); +} + +void Observer_disable_multi_observer(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(ecs, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) }, + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, o, false); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, o, true); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_fini(ecs); +} + +void Observer_disable_multi_observer_module(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t module = ecs_new_w_id(ecs, EcsModule); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(ecs, { + .entity = ecs_entity(ecs, { .parent = module }), + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) }, + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, false); + test_assert(!ecs_has_id(ecs, o, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, true); + test_assert(!ecs_has_id(ecs, o, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_fini(ecs); +} + +void Observer_disable_multi_observer_module_nested(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t module = ecs_new_w_id(ecs, EcsModule); + ecs_entity_t module_child = ecs_new_w_id(ecs, EcsModule); + ecs_add_pair(ecs, module_child, EcsChildOf, module); + + Probe ctx = {0}; + ecs_observer(ecs, { + .entity = ecs_entity(ecs, { .parent = module_child }), + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, false); + test_assert(!ecs_has_id(ecs, module_child, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module_child, false); + test_assert(ecs_has_id(ecs, module_child, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, true); + test_assert(ecs_has_id(ecs, module_child, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module_child, true); + test_assert(!ecs_has_id(ecs, module_child, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_fini(ecs); +} + +void Observer_disable_multi_observer_and_module(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t module = ecs_new_w_id(ecs, EcsModule); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(ecs, { + .entity = ecs_entity(ecs, { .parent = module }), + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) } + }, + .events = { EcsOnAdd }, + .callback = Observer, + .ctx = &ctx + }); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, false); + test_assert(!ecs_has_id(ecs, o, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, o, false); + test_assert(ecs_has_id(ecs, o, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, module, true); + test_assert(ecs_has_id(ecs, o, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 0); + ecs_os_zeromem(&ctx); + + ecs_enable(ecs, o, true); + test_assert(!ecs_has_id(ecs, o, EcsDisabled)); + + { + ecs_entity_t e = ecs_new_w(ecs, Position); + ecs_add(ecs, e, Velocity); + } + test_int(ctx.invoked, 1); + ecs_os_zeromem(&ctx); + + ecs_fini(ecs); +} + +void Observer_tag_w_on_set_and_on_add(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ tag }}, + .events = {EcsOnAdd, EcsOnSet}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_add_id(world, e1, tag); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], tag); + test_int(ctx.event, EcsOnAdd); + + ecs_delete(world, o); + ecs_os_zeromem(&ctx); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_id(world, e2, tag); + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + +void Observer_tag_w_on_set_and_on_add_reverse(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{ tag }}, + .events = {EcsOnSet, EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_add_id(world, e1, tag); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], tag); + test_int(ctx.event, EcsOnAdd); + + ecs_delete(world, o); + ecs_os_zeromem(&ctx); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_id(world, e2, tag); + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + +void Observer_on_add_pair_w_rel_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsAny, Tgt) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e1, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_on_remove_pair_w_rel_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsAny, Tgt) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e1, Rel, Tgt); + + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e1, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_on_add_pair_w_tgt_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ ecs_pair(Rel, EcsAny) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e1, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_on_remove_pair_w_tgt_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ ecs_pair(Rel, EcsAny) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e1, Rel, Tgt); + + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e1, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_on_add_pair_w_any_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsAny, EcsAny) }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e1, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_on_remove_pair_w_any_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsAny, EcsAny) }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_add_pair(world, e1, Rel, Tgt); + + test_int(ctx.invoked, 0); + + ecs_remove_pair(world, e1, Rel, Tgt); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], ecs_pair(Rel, Tgt)); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_on_add_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ EcsAny }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_add(world, e1, TagA); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_on_remove_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ EcsAny }}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_remove(world, e1, TagA); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_yield_existing_uni_no_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = { + { TagA, .src.id = e1 }, + }, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 1); + test_int(ctx.e[0], 0); + test_int(ctx.s[0][0], e1); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_yield_existing_multi_no_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add(world, e1, TagB); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = { + { TagA, .src.id = e1 }, + { TagB, .src.id = e1 }, + }, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx, + .yield_existing = true + }); + + test_int(ctx.invoked, 1); + test_int(ctx.e[0], 0); + test_int(ctx.s[0][0], e1); + test_int(ctx.c[0][0], TagA); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_observer_no_id_in_scope(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t parent = ecs_new(world); + + ecs_set_scope(world, parent); + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = TagA }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + }); + + ecs_set_scope(world, 0); + + test_assert(ecs_has_pair(world, o, EcsChildOf, parent)); + + ecs_fini(world); +} + +static +void ObserverRegisterComp(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + ecs_component(it->world, { + .entity = ecs_entity(it->world, { .name = "Position", .symbol = "Position" }), + .type.size = 8, + .type.alignment = 4, + }); +} + +void Observer_register_comp_in_emit_named_entity(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Event); + + Probe ctx; + + ecs_observer(world, { + .query.terms = { + { .id = EcsAny }, + }, + .callback = ObserverRegisterComp, + .events = { Event }, + .ctx = &ctx, + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e1" }); + + ecs_emit(world, &(ecs_event_desc_t){ + .event = Event, + .entity = e + }); + + test_int(ctx.invoked, 1); + + ecs_entity_t pos = ecs_lookup(world, "Position"); + test_assert(pos != 0); + const EcsComponent *ptr = ecs_get(world, pos, EcsComponent); + test_assert(ptr != NULL); + test_int(ptr->size, 8); + test_int(ptr->alignment, 4); + + ecs_fini(world); +} + +static +void ObserverRegisterCompMacro(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + ECS_COMPONENT(it->world, Position); +} + +void Observer_register_comp_w_macro_in_emit_named_entity(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Event); + + Probe ctx; + + ecs_observer(world, { + .query.terms = { + { .id = EcsAny }, + }, + .callback = ObserverRegisterCompMacro, + .events = { Event }, + .ctx = &ctx, + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e1" }); + + ecs_emit(world, &(ecs_event_desc_t){ + .event = Event, + .entity = e + }); + + test_int(ctx.invoked, 1); + + ecs_entity_t pos = ecs_lookup(world, "Position"); + test_assert(pos != 0); + const EcsComponent *ptr = ecs_get(world, pos, EcsComponent); + test_assert(ptr != NULL); + test_int(ptr->size, 8); + test_int(ptr->alignment, 4); + + ecs_fini(world); +} + +static ECS_TAG_DECLARE(Foo); + +static +void ObserverAddToSelf(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + test_int(it->count, 1); + ecs_add(it->world, it->entities[0], Foo); +} + +void Observer_add_to_self_in_emit_entity(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, Event); + ECS_TAG_DEFINE(world, Foo); + + Probe ctx; + + ecs_observer(world, { + .query.terms = { + { .id = EcsAny }, + }, + .callback = ObserverAddToSelf, + .events = { Event }, + .ctx = &ctx, + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e1" }); + + ecs_emit(world, &(ecs_event_desc_t){ + .event = Event, + .entity = e + }); + + test_int(ctx.invoked, 1); + test_assert(ecs_has(world, e, Foo)); + + ecs_fini(world); +} + +void Observer_on_set_w_not_tag(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + Probe ctx; + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = Foo, .oper = EcsNot }, + }, + .callback = Observer_w_1_value, + .events = { EcsOnSet }, + .ctx = &ctx, + }); + + ecs_entity_t e = ecs_new(world); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 1); + + ecs_os_zeromem(&ctx); + + ecs_add(world, e, Foo); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Foo); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +void Observer_on_set_w_not_component(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx; + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity), .oper = EcsNot }, + }, + .callback = Observer_w_1_value, + .events = { EcsOnSet }, + .ctx = &ctx, + }); + + ecs_entity_t e = ecs_new(world); + + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 1); + + ecs_os_zeromem(&ctx); + + ecs_set(world, e, Velocity, {1, 2}); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Velocity); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +void Observer_wildcard_event(void) { + ecs_world_t* world = ecs_init(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + }, + .callback = Observer, + .events = { EcsWildcard }, + .ctx = &ctx, + }); + + test_int(ctx.invoked, 0); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + test_int(ctx.invoked, 2); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_uint(ctx.event, EcsOnAdd); + + ecs_os_zeromem(&ctx); + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_uint(ctx.event, EcsOnSet); + + ecs_os_zeromem(&ctx); + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_uint(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +static int callback_callback_invoked = 0; +static int callback_run_invoked = 0; + +static +void callback_callback(ecs_iter_t *it) { + callback_callback_invoked ++; +} + +static +void callback_run(ecs_iter_t *it) { + callback_run_invoked ++; +} + +void Observer_register_callback_after_run(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t s = ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnSet}, + .callback = callback_callback + }); + + ecs_entity_t e = ecs_new(world); + + ecs_set(world, e, Position, {10, 20}); + test_int(callback_callback_invoked, 1); + test_int(callback_run_invoked, 0); + + ecs_observer(world, { + .entity = s, + .run = callback_run + }); + + ecs_set(world, e, Position, {10, 20}); + test_int(callback_callback_invoked, 1); + test_int(callback_run_invoked, 1); + + ecs_fini(world); +} + +void Observer_register_run_after_callback(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t s = ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnSet}, + .run = callback_run + }); + + ecs_entity_t e = ecs_new(world); + + ecs_set(world, e, Position, {10, 20}); + test_int(callback_run_invoked, 1); + test_int(callback_callback_invoked, 0); + + ecs_observer(world, { + .entity = s, + .callback = callback_callback + }); + + ecs_set(world, e, Position, {10, 20}); + test_int(callback_run_invoked, 1); + test_int(callback_callback_invoked, 1); + + ecs_fini(world); +} + +static +void ctx_free_3(void *ptr) { + int32_t *ctx = ptr; + ctx[0]++; +} + +void Observer_register_callback_after_run_ctx(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + int32_t callback_ctx = 0, run_ctx = 0; + + ecs_entity_t s = ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnSet}, + .callback = callback_callback, + .callback_ctx = &callback_ctx, + .callback_ctx_free = ctx_free_3 + }); + + test_int(callback_ctx, 0); + + ecs_observer(world, { + .entity = s, + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnSet}, + .run = callback_run, + .run_ctx = &run_ctx, + .run_ctx_free = ctx_free_3 + }); + + test_int(callback_ctx, 1); + + ecs_fini(world); + + test_int(callback_ctx, 1); + test_int(run_ctx, 1); +} + +void Observer_register_run_after_callback_ctx(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + int32_t callback_ctx = 0, run_ctx = 0; + + ecs_entity_t s = ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnSet}, + .run = callback_run, + .run_ctx = &run_ctx, + .run_ctx_free = ctx_free_3 + }); + + test_int(run_ctx, 0); + + ecs_observer(world, { + .entity = s, + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnSet}, + .callback = callback_callback, + .callback_ctx = &callback_ctx, + .callback_ctx_free = ctx_free_3 + }); + + test_int(run_ctx, 1); + test_int(callback_ctx, 0); + + ecs_fini(world); + + test_int(callback_ctx, 1); + test_int(run_ctx, 1); +} + +void Observer_on_add_after_new_w_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) } + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_table_t *table = ecs_table_add_id(world, NULL, ecs_id(Position)); + table = ecs_table_add_id(world, table, Bar); + + test_int(ctx.invoked, 0); + + ecs_entity_t e = ecs_new_w_table(world, table); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Bar)); + test_assert(ecs_get(world, e, Position) != NULL); + test_assert(table == ecs_get_table(world, e)); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_cache_test_1(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = {{ tag, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); + ecs_add_id(world, e1, tag); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.s[0][0], e1); + test_int(ctx.c[0][0], tag); + test_int(ctx.event, EcsOnAdd); + ecs_os_zeromem(&ctx); + + ecs_entity_t e3 = ecs_new(world); + ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e3); + test_int(ctx.s[0][0], e1); + test_int(ctx.c[0][0], tag); + test_int(ctx.event, EcsOnAdd); + ecs_os_zeromem(&ctx); + + ecs_delete(world, e3); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e3); + test_int(ctx.s[0][0], e1); + test_int(ctx.c[0][0], tag); + test_int(ctx.event, EcsOnRemove); + ecs_os_zeromem(&ctx); + + ecs_remove_id(world, e1, tag); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + test_int(ctx.s[0][0], e1); + test_int(ctx.c[0][0], tag); + test_int(ctx.event, EcsOnRemove); + ecs_os_zeromem(&ctx); + + e3 = ecs_new(world); + ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); + + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + +void Observer_cache_test_2(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag_1 = ecs_new(world); + ecs_entity_t tag_2 = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + Probe ctx_1 = {0}; + Probe ctx_2 = {0}; + + ecs_observer(world, { + .query.terms = {{ tag_1, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx_1 + }); + ecs_observer(world, { + .query.terms = {{ tag_2, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx_2 + }); + + ecs_add_id(world, e1, tag_1); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e2); + test_int(ctx_1.s[0][0], e1); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnAdd); + ecs_os_zeromem(&ctx_1); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e2, tag_2); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e3); + test_int(ctx_1.s[0][0], e1); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnAdd); + ecs_os_zeromem(&ctx_1); + test_int(ctx_2.invoked, 1); + test_int(ctx_2.count, 1); + test_int(ctx_2.e[0], e3); + test_int(ctx_2.s[0][0], e2); + test_int(ctx_2.c[0][0], tag_2); + test_int(ctx_2.event, EcsOnAdd); + ecs_os_zeromem(&ctx_2); + + ecs_fini(world); +} + +void Observer_cache_test_3(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag_1 = ecs_new(world); + ecs_entity_t tag_2 = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + Probe ctx_1 = {0}; + Probe ctx_2 = {0}; + + ecs_observer(world, { + .query.terms = {{ tag_1, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx_1 + }); + ecs_observer(world, { + .query.terms = {{ tag_2, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx_2 + }); + + ecs_add_id(world, e1, tag_1); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e2); + test_int(ctx_1.s[0][0], e1); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnAdd); + ecs_os_zeromem(&ctx_1); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e2, tag_1); + ecs_add_id(world, e2, tag_2); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e3); + test_int(ctx_1.s[0][0], e2); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnAdd); + ecs_os_zeromem(&ctx_1); + test_int(ctx_2.invoked, 1); + test_int(ctx_2.count, 1); + test_int(ctx_2.e[0], e3); + test_int(ctx_2.s[0][0], e2); + test_int(ctx_2.c[0][0], tag_2); + test_int(ctx_2.event, EcsOnAdd); + ecs_os_zeromem(&ctx_2); + + ecs_fini(world); +} + +void Observer_cache_test_4(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag_0 = ecs_new(world); + ecs_entity_t tag_1 = ecs_new(world); + ecs_entity_t tag_2 = ecs_new(world); + ecs_entity_t e0 = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + Probe *ctx_0 = ecs_os_calloc_t(Probe); + Probe *ctx_1 = ecs_os_calloc_t(Probe); + Probe *ctx_2 = ecs_os_calloc_t(Probe); + + ecs_observer(world, { + .query.terms = {{ tag_0, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = ctx_0 + }); + ecs_observer(world, { + .query.terms = {{ tag_1, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = ctx_1 + }); + ecs_observer(world, { + .query.terms = {{ tag_2, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = ctx_2 + }); + + ecs_add_id(world, e0, tag_0); + test_int(ctx_0->invoked, 0); + test_int(ctx_1->invoked, 0); + test_int(ctx_2->invoked, 0); + + ecs_add_id(world, e1, ecs_pair(EcsChildOf, e0)); + test_int(ctx_0->invoked, 1); + test_int(ctx_0->count, 1); + test_int(ctx_0->e[0], e1); + test_int(ctx_0->s[0][0], e0); + test_int(ctx_0->c[0][0], tag_0); + test_int(ctx_0->event, EcsOnAdd); + ecs_os_zeromem(ctx_0); + test_int(ctx_1->invoked, 0); + test_int(ctx_2->invoked, 0); + + ecs_add_id(world, e1, tag_1); + test_int(ctx_0->invoked, 0); + test_int(ctx_1->invoked, 0); + test_int(ctx_2->invoked, 0); + + ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); + test_int(ctx_0->invoked, 1); + test_int(ctx_0->count, 1); + test_int(ctx_0->e[0], e2); + test_int(ctx_0->s[0][0], e0); + test_int(ctx_0->c[0][0], tag_0); + test_int(ctx_0->event, EcsOnAdd); + ecs_os_zeromem(ctx_0); + test_int(ctx_1->invoked, 1); + test_int(ctx_1->count, 1); + test_int(ctx_1->e[0], e2); + test_int(ctx_1->s[0][0], e1); + test_int(ctx_1->c[0][0], tag_1); + test_int(ctx_1->event, EcsOnAdd); + ecs_os_zeromem(ctx_1); + test_int(ctx_2->invoked, 0); + + ecs_add_id(world, e2, tag_1); + ecs_add_id(world, e2, tag_2); + test_int(ctx_0->invoked, 0); + test_int(ctx_1->invoked, 0); + test_int(ctx_2->invoked, 0); + + ecs_remove_id(world, e0, tag_0); + test_int(ctx_0->invoked, 2); + ecs_os_zeromem(ctx_0); + test_int(ctx_1->invoked, 0); + test_int(ctx_2->invoked, 0); + + ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); + test_int(ctx_0->invoked, 0); + test_int(ctx_1->invoked, 1); + test_int(ctx_1->count, 1); + test_int(ctx_1->e[0], e3); + test_int(ctx_1->s[0][0], e2); + test_int(ctx_1->c[0][0], tag_1); + test_int(ctx_1->event, EcsOnAdd); + ecs_os_zeromem(ctx_1); + test_int(ctx_2->invoked, 1); + test_int(ctx_2->count, 1); + test_int(ctx_2->e[0], e3); + test_int(ctx_2->s[0][0], e2); + test_int(ctx_2->c[0][0], tag_2); + test_int(ctx_2->event, EcsOnAdd); + ecs_os_zeromem(ctx_2); + + ecs_fini(world); + + ecs_os_free(ctx_0); + ecs_os_free(ctx_1); + ecs_os_free(ctx_2); +} + +void Observer_cache_test_5(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag_0 = ecs_new(world); + ecs_entity_t tag_1 = ecs_new(world); + ecs_entity_t tag_2 = ecs_new(world); + ecs_entity_t e0 = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + + Probe ctx_1 = {0}; + Probe ctx_2 = {0}; + + ecs_observer(world, { + .query.terms = {{ tag_1, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx_1 + }); + ecs_observer(world, { + .query.terms = {{ tag_2, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx_2 + }); + + ecs_add_id(world, e0, tag_0); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e1, ecs_pair(EcsChildOf, e0)); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e1, tag_1); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e2); + test_int(ctx_1.s[0][0], e1); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnAdd); + ecs_os_zeromem(&ctx_1); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e2, tag_1); + ecs_add_id(world, e2, tag_2); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_remove_id(world, e0, tag_0); + test_int(ctx_1.invoked, 0); + test_int(ctx_2.invoked, 0); + + ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e3); + test_int(ctx_1.s[0][0], e2); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnAdd); + ecs_os_zeromem(&ctx_1); + test_int(ctx_2.invoked, 1); + test_int(ctx_2.count, 1); + test_int(ctx_2.e[0], e3); + test_int(ctx_2.s[0][0], e2); + test_int(ctx_2.c[0][0], tag_2); + test_int(ctx_2.event, EcsOnAdd); + ecs_os_zeromem(&ctx_2); + + ecs_add_id(world, e4, ecs_pair(EcsChildOf, e1)); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e4); + test_int(ctx_1.s[0][0], e1); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnAdd); + ecs_os_zeromem(&ctx_1); + test_int(ctx_2.invoked, 0); + + ecs_fini(world); +} + +void Observer_cache_test_6(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag_1 = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + Probe ctx_1 = {0}; + ecs_observer(world, { + .query.terms = {{ tag_1, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx_1 + }); + + ecs_add_id(world, e1, tag_1); + test_int(ctx_1.invoked, 0); + + ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e2); + test_int(ctx_1.s[0][0], e1); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnAdd); + ecs_os_zeromem(&ctx_1); + + ecs_remove_id(world, e1, tag_1); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e2); + test_int(ctx_1.s[0][0], e1); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnRemove); + ecs_os_zeromem(&ctx_1); + + ecs_add_id(world, e3, ecs_pair(EcsChildOf, e1)); + test_int(ctx_1.invoked, 0); + + ecs_fini(world); +} + +void Observer_cache_test_7(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag_1 = ecs_new(world); + ecs_entity_t tag_2 = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + Probe ctx_1 = {0}; + ecs_observer(world, { + .query.terms = {{ tag_1, .src.id = EcsUp, .trav = EcsChildOf }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx_1 + }); + + ecs_add_id(world, e1, tag_1); + ecs_add_id(world, e1, tag_2); + test_int(ctx_1.invoked, 0); + + ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1)); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e2); + test_int(ctx_1.s[0][0], e1); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnAdd); + ecs_os_zeromem(&ctx_1); + + ecs_remove_id(world, e1, tag_1); + test_int(ctx_1.invoked, 1); + test_int(ctx_1.count, 1); + test_int(ctx_1.e[0], e2); + test_int(ctx_1.s[0][0], e1); + test_int(ctx_1.c[0][0], tag_1); + test_int(ctx_1.event, EcsOnRemove); + ecs_os_zeromem(&ctx_1); + + ecs_add_id(world, e3, ecs_pair(EcsChildOf, e1)); + test_int(ctx_1.invoked, 0); + + ecs_fini(world); +} + +void Observer_cache_test_8(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ TagB }}, + .events = {EcsOnAdd}, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t parent = ecs_new(world); + ecs_add(world, parent, TagA); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + ecs_new_w_pair(world, EcsChildOf, child); + test_int(ctx.invoked, 0); + + ecs_add(world, parent, TagB); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], parent); + test_int(ctx.s[0][0], 0); + test_int(ctx.c[0][0], TagB); + test_int(ctx.event, EcsOnAdd); + + ecs_fini(world); +} + +void Observer_cache_test_9(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms = {{ ecs_id(Position), .src.id = EcsUp, .trav = EcsIsA }}, + .events = { EcsOnRemove }, + .callback = Observer, + .ctx = &ctx + }); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); + ecs_add_pair(world, base_2, EcsIsA, base); + ecs_set(world, base_2, Position, {20, 30}); + + ecs_entity_t base_3 = ecs_new_w_id(world, EcsPrefab); + ecs_add_pair(world, base_3, EcsIsA, base); + ecs_set(world, base_3, Position, {40, 50}); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_2); + ecs_add_pair(world, inst, EcsIsA, base_3); + + ecs_clear(world, base_2); + + /* Once for Position of base_2, twice because Position is reachable through + * two paths. */ + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.e[0], inst); + test_int(ctx.s[0][0], base_2); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.event, EcsOnRemove); + + ecs_os_zeromem(&ctx); + + ecs_clear(world, base_3); + + /* Once for Position of base_2, twice because Position is reachable through + * two paths. */ + test_int(ctx.invoked, 2); + test_int(ctx.count, 2); + test_int(ctx.e[0], inst); + test_int(ctx.s[0][0], base_3); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.event, EcsOnRemove); + + ecs_fini(world); +} + +void Observer_cache_test_10(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + ecs_entity_t ns = ecs_new(world); + ecs_add_id(world, ns, tag); + + ecs_entity_t foo_parent = ecs_new_w_pair(world, EcsChildOf, ns); + ecs_entity_t foo = ecs_new_w_pair(world, EcsChildOf, foo_parent); + ecs_entity_t bar = ecs_new(world); + + ecs_entity_t parent = ecs_new_w_id(world, foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_id(world, e1, foo); + ecs_add_pair(world, e1, EcsChildOf, parent); + ecs_add_id(world, e1, bar); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_id(world, e2, foo); + ecs_add_pair(world, e2, EcsChildOf, parent); + ecs_add_id(world, e2, bar); + + ecs_entity_t c1 = ecs_new(world); + ecs_add_id(world, c1, foo); + ecs_add_pair(world, c1, EcsChildOf, e1); + + ecs_entity_t c2 = ecs_new(world); + ecs_add_id(world, c2, foo); + ecs_add_pair(world, c2, EcsChildOf, e2); + + ecs_delete(world, ns); + test_assert(!ecs_is_alive(world, ns)); + test_assert(!ecs_is_alive(world, foo)); + test_assert(!ecs_is_alive(world, foo_parent)); + test_assert(!ecs_has_id(world, parent, foo)); + test_assert(!ecs_has_id(world, e1, foo)); + test_assert(!ecs_has_id(world, e2, foo)); + test_assert(!ecs_has_id(world, c1, foo)); + test_assert(!ecs_has_id(world, c2, foo)); + + ecs_delete(world, parent); + test_assert(!ecs_is_alive(world, parent)); + test_assert(!ecs_is_alive(world, e1)); + test_assert(!ecs_is_alive(world, e2)); + test_assert(!ecs_is_alive(world, c1)); + test_assert(!ecs_is_alive(world, c2)); + + ecs_fini(world); +} + +void Observer_cache_test_11(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t ns = ecs_new(world); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, ns); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsDependsOn, e1); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, ns); + ecs_add_id(world, e3, EcsModule); + + ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, e3); + ecs_add_pair(world, e4, EcsDependsOn, e2); + + ecs_add_id(world, e1, ecs_new(world)); + + ecs_run_aperiodic(world, 0); + + test_assert(true); // ensure cache revalidation didn't assert + + ecs_fini(world); +} + +void Observer_cache_test_12(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t t0 = ecs_new(world); + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + + ecs_entity_t e0 = ecs_new(world); + ecs_add_id(world, e0, t0); + ecs_add_id(world, e0, t1); + ecs_add_id(world, e0, t2); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_id(world, e1, t0); + ecs_add_id(world, e1, t1); + ecs_add_id(world, e1, t2); + + ecs_new_w_pair(world, EcsChildOf, e1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e0); + ecs_add_id(world, e3, t0); + + ecs_entity_t e4 = ecs_new(world); + ecs_new_w_pair(world, EcsChildOf, e4); + ecs_remove_all(world, t1); + + ecs_new_w_pair(world, EcsChildOf, e3); + + ecs_run_aperiodic(world, 0); + + ecs_remove_all(world, t0); + + test_assert(true); // ensure cache revalidation didn't assert + + ecs_fini(world); +} + +void Observer_cache_test_13(void) { + install_test_abort(); + ecs_world_t *world = ecs_mini(); + + ecs_entity_t t0 = ecs_new(world); + ecs_entity_t e0 = ecs_new(world); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_id(world, e1, t0); + ecs_add_id(world, e1, ecs_pair(EcsChildOf, e0)); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_id(world, e2, t0); + + ecs_entity_t e3 = ecs_new(world); + ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2)); + + ecs_delete_with(world, t0); + test_assert(!ecs_is_alive(world, e1)); + test_assert(!ecs_is_alive(world, e2)); + test_assert(!ecs_is_alive(world, e3)); + + ecs_entity_t e4 = ecs_new(world); + test_expect_abort(); + ecs_add_id(world, e4, ecs_pair(EcsChildOf, e2)); +} + +void Observer_cache_test_14(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + { + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Tag); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsChildOf, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_delete_with(world, Tag); + + test_assert(!ecs_is_alive(world, e0)); + test_assert(!ecs_is_alive(world, e1)); + test_assert(!ecs_is_alive(world, e2)); + } + + { + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Tag); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsChildOf, e0); + ecs_add(world, e1, Tag); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_remove_all(world, Tag); + + test_assert(ecs_is_alive(world, e0)); + test_assert(ecs_is_alive(world, e1)); + test_assert(ecs_is_alive(world, e2)); + + test_assert(!ecs_has(world, e0, Tag)); + } + + ecs_fini(world); +} + +void Observer_cache_test_15(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t r = ecs_new(world); + ecs_entity_t e0 = ecs_new(world); + ecs_entity_t e1 = ecs_new(world); + + ecs_add_pair(world, e1, r, e0); + ecs_add_pair(world, e0, EcsChildOf, e1); + + ecs_run_aperiodic(world, 0); + + ecs_delete(world, e1); + + test_assert(!ecs_is_alive(world, e1)); + test_assert(!ecs_is_alive(world, e0)); + test_assert(ecs_is_alive(world, r)); + + ecs_fini(world); +} + +static +void Observer_w_run_aperiodic(ecs_iter_t *it) { + test_int(it->count, 1); + ecs_run_aperiodic(it->world, 0); +} + +void Observer_cache_test_16(void) { + ecs_world_t* world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ECS_OBSERVER(world, Observer_w_run_aperiodic, EcsOnAdd, Foo); + + ecs_entity_t p1 = ecs_new(world); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, p1); + + ecs_run_aperiodic(world, 0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, p1); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_add(world, e1, Foo); + + ecs_fini(world); +} + +static int Observer_a_invoked = 0; +static int Observer_b_invoked = 0; + +static void Observer_a(ecs_iter_t *it) { + Observer_a_invoked += it->count; +} + +static void Observer_b(ecs_iter_t *it) { + Observer_b_invoked += it->count; +} + +void Observer_filter_observer_after_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = Tag } + }, + .callback = Observer_a, + .events = { EcsOnAdd } + }); + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = Tag, .inout = EcsInOutNone} + }, + .callback = Observer_b, + .events = { EcsOnAdd } + }); + + ecs_entity_t e = ecs_new_w(world, Tag); + ecs_add(world, e, Position); + + test_int(Observer_a_invoked, 1); + test_int(Observer_b_invoked, 1); + + ecs_fini(world); +} + +void Observer_notify_after_defer_batched(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) } + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_defer_begin(world); + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + test_int(ctx.invoked, 0); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_defer_end(world); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + + ecs_fini(world); +} + +void Observer_notify_after_defer_batched_2_entities_in_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) } + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + test_int(ctx.invoked, 0); + ecs_set(world, e1, Velocity, {1, 2}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + + ecs_os_zeromem(&ctx); + + ecs_entity_t e2 = ecs_new(world); + + ecs_defer_begin(world); + ecs_set(world, e2, Position, {30, 40}); + ecs_set(world, e2, Velocity, {3, 4}); + test_int(ctx.count, 0); + ecs_defer_end(world); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + + ecs_fini(world); +} + +void Observer_notify_after_defer_batched_2_entities_in_table_w_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) } + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + test_int(ctx.invoked, 0); + ecs_set(world, e1, Velocity, {1, 2}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], e1); + + ecs_os_zeromem(&ctx); + + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_add_pair(world, e3, EcsChildOf, e2); + + ecs_defer_begin(world); + ecs_set(world, e2, Position, {30, 40}); + ecs_set(world, e2, Velocity, {3, 4}); + test_int(ctx.count, 0); + ecs_defer_end(world); + test_int(ctx.count, 1); + test_int(ctx.e[0], e2); + + ecs_fini(world); +} + +void Observer_multi_observer_table_fill_w_singleton(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .src.id = ecs_id(Position)|EcsIsEntity }, + { .id = ecs_id(Velocity) }, + }, + .callback = Observer, + .events = { EcsOnTableFill }, + .ctx = &ctx + }); + + ecs_singleton_add(world, Position); + test_int(ctx.invoked, 0); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Velocity); + test_int(ctx.invoked, 0); + + ecs_run_aperiodic(world, 0); + test_int(ctx.invoked, 1); + + ecs_fini(world); +} + +ECS_COMPONENT_DECLARE(Velocity); + +static +void AddVelocity(ecs_iter_t *it) { + for (int i = 0; i < it->count; i ++) { + ecs_add(it->world, it->entities[i], Velocity); + test_assert(!ecs_has(it->world, it->entities[i], Velocity)); + } +} + +void Observer_add_in_on_add_yield_existing(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT_DEFINE(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + }, + .callback = AddVelocity, + .events = { EcsOnAdd }, + .yield_existing = true, + }); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e1, Velocity)); + + test_assert(ecs_has(world, e2, Position)); + test_assert(ecs_has(world, e2, Velocity)); + + test_assert(ecs_has(world, e3, Position)); + test_assert(ecs_has(world, e3, Velocity)); + + ecs_fini(world); +} + +void Observer_add_in_on_add_yield_existing_multi(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT_DEFINE(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_add(world, e1, Mass); + ecs_add(world, e2, Mass); + ecs_add(world, e3, Mass); + + ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Mass) }, + }, + .callback = AddVelocity, + .events = { EcsOnAdd }, + .yield_existing = true, + }); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e1, Mass)); + test_assert(ecs_has(world, e1, Velocity)); + + test_assert(ecs_has(world, e2, Position)); + test_assert(ecs_has(world, e2, Mass)); + test_assert(ecs_has(world, e2, Velocity)); + + test_assert(ecs_has(world, e3, Position)); + test_assert(ecs_has(world, e3, Mass)); + test_assert(ecs_has(world, e3, Velocity)); + + ecs_fini(world); +} + +void Observer_add_in_on_remove_yield_existing(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT_DEFINE(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + }, + .callback = AddVelocity, + .events = { EcsOnRemove }, + .yield_existing = true, + }); + + test_assert(ecs_has(world, e1, Position)); + test_assert(!ecs_has(world, e1, Velocity)); + + test_assert(ecs_has(world, e2, Position)); + test_assert(!ecs_has(world, e2, Velocity)); + + test_assert(ecs_has(world, e3, Position)); + test_assert(!ecs_has(world, e3, Velocity)); + + ecs_delete(world, o); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e1, Velocity)); + + test_assert(ecs_has(world, e2, Position)); + test_assert(ecs_has(world, e2, Velocity)); + + test_assert(ecs_has(world, e3, Position)); + test_assert(ecs_has(world, e3, Velocity)); + + ecs_fini(world); +} + +void Observer_add_in_on_remove_yield_existing_multi(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT_DEFINE(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_add(world, e1, Mass); + ecs_add(world, e2, Mass); + ecs_add(world, e3, Mass); + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Mass) }, + }, + .callback = AddVelocity, + .events = { EcsOnRemove }, + .yield_existing = true, + }); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e1, Mass)); + test_assert(!ecs_has(world, e1, Velocity)); + + test_assert(ecs_has(world, e2, Position)); + test_assert(ecs_has(world, e2, Mass)); + test_assert(!ecs_has(world, e2, Velocity)); + + test_assert(ecs_has(world, e3, Position)); + test_assert(ecs_has(world, e3, Mass)); + test_assert(!ecs_has(world, e3, Velocity)); + + ecs_delete(world, o); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e1, Mass)); + test_assert(ecs_has(world, e1, Velocity)); + + test_assert(ecs_has(world, e2, Position)); + test_assert(ecs_has(world, e2, Mass)); + test_assert(ecs_has(world, e2, Velocity)); + + test_assert(ecs_has(world, e3, Position)); + test_assert(ecs_has(world, e3, Mass)); + test_assert(ecs_has(world, e3, Velocity)); + + ecs_fini(world); +} + +void Observer_get_filter(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) } + }, + .callback = Observer, + .events = { EcsOnAdd }, + }); + + const ecs_query_t *f = ecs_observer_get(world, o)->query; + test_assert(f != NULL); + test_int(f->term_count, 1); + test_uint(ecs_id(Position), f->terms[0].id); + + ecs_fini(world); +} + +void Observer_uni_observer_eval_count(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + }); + + const ecs_query_t *q = ecs_observer_get(world, o)->query; + test_assert(q != NULL); + test_int(q->eval_count, 0); + + ecs_new_w(world, Position); + + test_int(q->eval_count, 1); + + ecs_fini(world); +} + +void Observer_multi_observer_eval_count(void) { + ecs_world_t* world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + }); + + const ecs_query_t *q = ecs_observer_get(world, o)->query; + test_assert(q != NULL); + test_int(q->eval_count, 0); + + ecs_entity_t e = ecs_new_w(world, Position); + test_int(q->eval_count, 0); + + ecs_add(world, e, Velocity); + test_int(q->eval_count, 1); + + ecs_fini(world); +} + +void Observer_ref_flag_term_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + + ecs_entity_t s = ecs_new(world); + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .src.id = s }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_set(world, s, Position, {10, 20}); + + test_int(ctx.invoked, 1); + // test_int(ctx.count, 0); // TODO + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], s); + test_uint(ctx.ref_fields, (1llu << 0)); + test_uint(ctx.up_fields, 0); + test_uint(ctx.row_fields, 0); + + ecs_fini(world); +} + +void Observer_ref_flag_term_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_entity_t s = ecs_new(world); + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .src.id = s }, + { .id = ecs_id(Velocity), .src.id = s } + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_set(world, s, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_set(world, s, Velocity, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 0); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], s); + test_uint(ctx.s[0][1], s); + test_uint(ctx.ref_fields, (1llu << 0) | (1llu << 1)); + test_uint(ctx.up_fields, 0); + test_uint(ctx.row_fields, 0); + + ecs_fini(world); +} + +void Observer_forward_up_flag_term_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .src.id = EcsUp }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + ecs_set(world, s, Position, {10, 20}); + + test_int(ctx.invoked, 0); + + ecs_new_w_pair(world, EcsChildOf, s); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], s); + test_uint(ctx.ref_fields, 0); + test_uint(ctx.up_fields, (1llu << 0)); + test_uint(ctx.row_fields, 0); + + ecs_fini(world); +} + +void Observer_forward_up_flag_term_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .src.id = EcsUp }, + { .id = ecs_id(Velocity), .src.id = EcsUp }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + ecs_set(world, s, Position, {10, 20}); + ecs_set(world, s, Velocity, {1, 2}); + + test_int(ctx.invoked, 0); + + ecs_new_w_pair(world, EcsChildOf, s); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], s); + test_uint(ctx.ref_fields, 0); + test_uint(ctx.up_fields, (1llu << 0) | (1llu << 1)); + test_uint(ctx.row_fields, 0); + + ecs_fini(world); +} + +void Observer_propagate_up_flag_term_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .src.id = EcsUp }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + ecs_new_w_pair(world, EcsChildOf, s); + test_int(ctx.invoked, 0); + + ecs_set(world, s, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], s); + test_uint(ctx.ref_fields, 0); + test_uint(ctx.up_fields, (1llu << 0)); + test_uint(ctx.row_fields, 0); + + ecs_fini(world); +} + +void Observer_propagate_up_flag_term_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .src.id = EcsUp }, + { .id = ecs_id(Velocity), .src.id = EcsUp }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + ecs_new_w_pair(world, EcsChildOf, s); + test_int(ctx.invoked, 0); + + ecs_set(world, s, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_set(world, s, Velocity, {1, 2}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], s); + test_uint(ctx.ref_fields, 0); + test_uint(ctx.up_fields, (1llu << 0) | (1llu << 1)); + test_uint(ctx.row_fields, 0); + + ecs_fini(world); +} + +void Observer_row_flag_term_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + ecs_set(world, s, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], 0); + test_uint(ctx.ref_fields, (1llu << 0)); + test_uint(ctx.up_fields, 0); + test_uint(ctx.row_fields, (1llu << 0)); + + ecs_fini(world); +} + +void Observer_row_flag_term_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) }, + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + + ecs_set(world, s, Position, {10, 20}); + test_int(ctx.invoked, 0); + + ecs_set(world, s, Velocity, {1, 2}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], 0); + test_uint(ctx.ref_fields, (1llu << 0) | (1llu << 1)); + test_uint(ctx.up_fields, 0); + test_uint(ctx.row_fields, (1llu << 0) | (1llu << 1)); + + ecs_fini(world); +} + +void Observer_on_add_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .oper = EcsOptional } + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + ecs_add(world, s, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], 0); + + ecs_fini(world); +} + +void Observer_on_remove_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .oper = EcsOptional } + }, + .callback = Observer, + .events = { EcsOnRemove }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + ecs_add(world, s, Position); + test_int(ctx.invoked, 0); + + ecs_remove(world, s, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_uint(ctx.s[0][0], 0); + + ecs_fini(world); +} + +void Observer_on_add_multi_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .oper = EcsOptional }, + { .id = ecs_id(Velocity) } + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + + ecs_add(world, s, Velocity); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], 0); + + ecs_os_zeromem(&ctx); + + ecs_add(world, s, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], 0); + + ecs_fini(world); +} + +void Observer_on_remove_multi_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .oper = EcsOptional }, + { .id = ecs_id(Velocity) } + }, + .callback = Observer, + .events = { EcsOnRemove }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + + ecs_add(world, s, Velocity); + ecs_add(world, s, Position); + test_int(ctx.invoked, 0); + + ecs_remove(world, s, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_uint(ctx.s[0][0], 0); + + ecs_os_zeromem(&ctx); + + ecs_remove(world, s, Velocity); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_uint(ctx.s[0][0], 0); + + ecs_fini(world); +} + +void Observer_on_add_multi_only_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .oper = EcsOptional }, + { .id = ecs_id(Velocity), .oper = EcsOptional } + }, + .callback = Observer, + .events = { EcsOnAdd }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + + ecs_add(world, s, Velocity); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], 0); + + ecs_os_zeromem(&ctx); + + ecs_add(world, s, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], 0); + + ecs_fini(world); +} + +void Observer_on_remove_multi_only_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { .id = ecs_id(Position), .oper = EcsOptional }, + { .id = ecs_id(Velocity), .oper = EcsOptional } + }, + .callback = Observer, + .events = { EcsOnRemove }, + .ctx = &ctx + }); + + ecs_entity_t s = ecs_new(world); + + ecs_add(world, s, Velocity); + ecs_add(world, s, Position); + test_int(ctx.invoked, 0); + + ecs_remove(world, s, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_uint(ctx.s[0][0], 0); + + ecs_os_zeromem(&ctx); + + ecs_remove(world, s, Velocity); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnRemove); + test_uint(ctx.s[0][0], 0); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/ObserverOnSet.c b/vendors/flecs/test/core/src/ObserverOnSet.c similarity index 72% rename from vendors/flecs/test/api/src/ObserverOnSet.c rename to vendors/flecs/test/core/src/ObserverOnSet.c index 923e66165..f59016403 100644 --- a/vendors/flecs/test/api/src/ObserverOnSet.c +++ b/vendors/flecs/test/core/src/ObserverOnSet.c @@ -1,7 +1,8 @@ -#include +#include static void OnPosition(ecs_iter_t *it) { + test_assert(ecs_field(it, Position, 0) != NULL); probe_iter(it); } @@ -49,8 +50,8 @@ static Probe pv_probe; static void On_PV(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); probe_system_w_ctx(it, &pv_probe); @@ -71,7 +72,7 @@ void ObserverOnSet_set_1_of_1(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_set(world, e, Position, {10, 20}); @@ -117,7 +118,7 @@ void ObserverOnSet_set_1_of_2(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_set(world, e, Position, {10, 20}); @@ -168,7 +169,7 @@ void ObserverOnSet_set_1_of_3(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_set(world, e, Position, {10, 20}); @@ -237,8 +238,12 @@ void ObserverOnSet_set_1_of_2_1_from_base(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position); - ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up), Velocity); + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up IsA), Velocity); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); @@ -271,8 +276,13 @@ void ObserverOnSet_set_1_of_3_1_from_base(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position); - ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up), Velocity, Mass); + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up IsA), Velocity, Mass); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); @@ -326,13 +336,17 @@ void ObserverOnSet_add_base(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position); - ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up), Velocity); + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up IsA), Velocity); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Velocity); + ecs_entity_t e = ecs_new_w(world, Velocity); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, Base); @@ -355,13 +369,15 @@ void ObserverOnSet_add_base_to_1_overridden(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position); - ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up)); + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up IsA)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, Base); @@ -375,13 +391,17 @@ void ObserverOnSet_add_base_to_2_overridden(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position, Velocity); - ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up), Velocity(self|up)); + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up IsA), Velocity(self|up IsA)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_add(world, e, Velocity); test_int(ctx.invoked, 0); @@ -396,13 +416,17 @@ void ObserverOnSet_add_base_to_1_of_2_overridden(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position, Velocity); - ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up)); + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up IsA)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Velocity); + ecs_entity_t e = ecs_new_w(world, Velocity); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, Base); @@ -418,7 +442,7 @@ void ObserverOnSet_add_base_to_1_of_2_overridden(void) { ecs_os_zeromem(&ctx); - e = ecs_new(world, Position); + e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, Base); @@ -431,14 +455,16 @@ void ObserverOnSet_on_set_after_remove_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position); ECS_OBSERVER(world, OnPosition, EcsOnSet, Position); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, Base); @@ -458,18 +484,122 @@ void ObserverOnSet_on_set_after_remove_override(void) { ecs_fini(world); } +void ObserverOnSet_on_set_after_remove_override_isa_before_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ECS_ENTITY(world, Base, Position); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Base); + ecs_add(world, e, Position); + + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position); + + ecs_remove(world, e, Position); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnPosition); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], Base); + + ecs_fini(world); +} + +void ObserverOnSet_on_set_w_override_after_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ECS_ENTITY(world, Base, Position); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Base); + ecs_add(world, e, Position); + + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position); + + ecs_delete(world, e); + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + +void ObserverOnSet_on_set_w_override_after_clear(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ECS_ENTITY(world, Base, Position); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Base); + ecs_add(world, e, Position); + + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position); + + ecs_clear(world, e); + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + +void ObserverOnSet_on_set_w_override_after_delete_w_ecs_init(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ECS_ENTITY(world, Base, Position); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Base); + ecs_add(world, e, Position); + + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position); + + ecs_delete(world, e); + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + void ObserverOnSet_no_set_after_remove_base(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position); - ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up)); + ECS_OBSERVER(world, OnPosition, EcsOnSet, Position(self|up IsA)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, Base); @@ -485,12 +615,12 @@ void ObserverOnSet_un_set_after_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ECS_OBSERVER(world, OnPosition, EcsUnSet, Position); + ECS_OBSERVER(world, OnPosition, EcsOnRemove, Position); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_remove(world, e, Position); @@ -503,13 +633,16 @@ void ObserverOnSet_un_set_after_remove_base(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position); - ECS_OBSERVER(world, OnPosition, EcsUnSet, Position(self|up)); + ECS_OBSERVER(world, OnPosition, EcsOnRemove, Position(self|up IsA)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, Base); @@ -532,9 +665,9 @@ void ObserverOnSet_add_to_current_in_on_set(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {11, 21}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {12, 22}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {11, 21})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {12, 22})); test_assert( ecs_has(world, e1, Position)); test_assert( ecs_has(world, e2, Position)); @@ -574,9 +707,9 @@ void ObserverOnSet_remove_from_current_in_on_set(void) { ECS_ENTITY(world, e2, Position, Velocity); ECS_ENTITY(world, e3, Position, Velocity); - e1 = ecs_set(world, e1, Position, {10, 20}); - e2 = ecs_set(world, e2, Position, {11, 21}); - e3 = ecs_set(world, e3, Position, {12, 22}); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {11, 21}); + ecs_set(world, e3, Position, {12, 22}); test_assert( ecs_has(world, e1, Position)); test_assert( ecs_has(world, e2, Position)); @@ -616,9 +749,9 @@ void ObserverOnSet_remove_set_component_in_on_set(void) { ECS_ENTITY(world, e2, Position, Velocity); ECS_ENTITY(world, e3, Position, Velocity); - e1 = ecs_set(world, e1, Position, {10, 20}); - e2 = ecs_set(world, e2, Position, {11, 21}); - e3 = ecs_set(world, e3, Position, {12, 22}); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {11, 21}); + ecs_set(world, e3, Position, {12, 22}); test_assert( !ecs_has(world, e1, Position)); test_assert( !ecs_has(world, e2, Position)); @@ -644,9 +777,9 @@ void ObserverOnSet_match_table_created_w_add_in_on_set(void) { IterData add_ctx = {.component = ecs_id(Velocity)}; ecs_set_ctx(world, &add_ctx, NULL); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_progress(world, 1); @@ -677,7 +810,7 @@ void ObserverOnSet_set_optional(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_set(world, e, Position, {10, 20}); @@ -686,7 +819,6 @@ void ObserverOnSet_set_optional(void) { test_int(ctx.system, OnPosition); test_int(ctx.term_count, 2); test_null(ctx.param); - test_int(ctx.e[0], e); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); @@ -694,8 +826,41 @@ void ObserverOnSet_set_optional(void) { ecs_os_zeromem(&ctx); ecs_set(world, e, Velocity, {10, 20}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnPosition); + test_int(ctx.term_count, 2); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.c[0][1], ecs_id(Velocity)); + test_int(ctx.s[0][0], 0); + + ecs_fini(world); +} + +void ObserverOnSet_set_optional_one_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_OBSERVER(world, OnPosition, EcsOnSet, ?Position); + + Probe ctx = { 0 }; + ecs_set_ctx(world, &ctx, NULL); + + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); + ecs_set(world, e, Position, {10, 20}); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, OnPosition); + test_int(ctx.term_count, 1); + test_null(ctx.param); + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], ecs_id(Position)); + test_int(ctx.s[0][0], 0); + ecs_fini(world); } @@ -709,7 +874,7 @@ void ObserverOnSet_set_from_nothing(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, OnPosition); @@ -752,7 +917,7 @@ void ObserverOnSet_add_0_entity_in_on_set(void) { ecs_set_ctx(world, &ctx, NULL); test_expect_abort(); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); } static int dummy_invoked = 0; @@ -768,7 +933,8 @@ void ObserverOnSet_on_set_prefab(void) { ECS_COMPONENT(world, Position); ECS_OBSERVER(world, Dummy, EcsOnSet, Position); - ECS_PREFAB(world, Prefab, 0); + ECS_PREFAB(world, Prefab, #0); + test_assert(ecs_has_id(world, Prefab, EcsPrefab)); ecs_set(world, Prefab, Position, {10, 20}); test_int(dummy_invoked, 0); diff --git a/vendors/flecs/test/api/src/OnDelete.c b/vendors/flecs/test/core/src/OnDelete.c similarity index 71% rename from vendors/flecs/test/api/src/OnDelete.c rename to vendors/flecs/test/core/src/OnDelete.c index 811e84537..848cf7087 100644 --- a/vendors/flecs/test/api/src/OnDelete.c +++ b/vendors/flecs/test/core/src/OnDelete.c @@ -1,4 +1,4 @@ -#include +#include void OnDelete_flags(void) { ecs_flags32_t f_remove = EcsIdOnDeleteRemove; @@ -37,8 +37,8 @@ void OnDelete_flags(void) { void OnDelete_id_default(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, c); test_assert(ecs_has_id(world, e, c)); @@ -56,10 +56,10 @@ void OnDelete_id_default(void) { void OnDelete_id_remove(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); ecs_add_pair(world, c, EcsOnDelete, EcsRemove); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, c); test_assert(ecs_has_id(world, e, c)); @@ -75,10 +75,10 @@ void OnDelete_id_remove(void) { void OnDelete_id_delete(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); ecs_add_pair(world, c, EcsOnDelete, EcsDelete); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, c); test_assert(ecs_has_id(world, e, c)); @@ -93,10 +93,10 @@ void OnDelete_id_delete(void) { void OnDelete_relation_default(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, o); test_assert(ecs_has_pair(world, e, r, o)); @@ -113,11 +113,11 @@ void OnDelete_relation_default(void) { void OnDelete_relation_remove(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); ecs_add_pair(world, r, EcsOnDelete, EcsRemove); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, o); test_assert(ecs_has_pair(world, e, r, o)); @@ -134,11 +134,11 @@ void OnDelete_relation_remove(void) { void OnDelete_relation_delete(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); ecs_add_pair(world, r, EcsOnDelete, EcsDelete); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, o); test_assert(ecs_has_pair(world, e, r, o)); @@ -154,10 +154,10 @@ void OnDelete_relation_delete(void) { void OnDelete_object_default(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, o); test_assert(ecs_has_pair(world, e, r, o)); @@ -174,11 +174,11 @@ void OnDelete_object_default(void) { void OnDelete_object_remove(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); ecs_add_pair(world, r, EcsOnDeleteTarget, EcsRemove); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, o); test_assert(ecs_has_pair(world, e, r, o)); @@ -195,11 +195,11 @@ void OnDelete_object_remove(void) { void OnDelete_object_delete(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); ecs_add_pair(world, r, EcsOnDeleteTarget, EcsDelete); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, o); test_assert(ecs_has_pair(world, e, r, o)); @@ -215,13 +215,13 @@ void OnDelete_object_delete(void) { void OnDelete_object_mixed(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t t_1 = ecs_new_id(world); - ecs_entity_t t_2 = ecs_new_id(world); + ecs_entity_t t_1 = ecs_new(world); + ecs_entity_t t_2 = ecs_new(world); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t o = ecs_new(world); ecs_add_pair(world, r_1, EcsOnDeleteTarget, EcsDelete); ecs_entity_t e_1 = ecs_new_w_pair(world, r_1, o); @@ -255,10 +255,10 @@ void OnDelete_id_throw(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); ecs_add_pair(world, c, EcsOnDelete, EcsPanic); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, c); test_assert(ecs_has_id(world, e, c)); @@ -271,11 +271,11 @@ void OnDelete_relation_throw(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); ecs_add_pair(world, r, EcsOnDelete, EcsPanic); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, o); test_assert(ecs_has_pair(world, e, r, o)); @@ -288,11 +288,11 @@ void OnDelete_object_throw(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); ecs_add_pair(world, r, EcsOnDeleteTarget, EcsPanic); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, o); test_assert(ecs_has_pair(world, e, r, o)); @@ -303,7 +303,7 @@ void OnDelete_object_throw(void) { void OnDelete_id_remove_no_instances(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); ecs_add_pair(world, c, EcsOnDelete, EcsRemove); ecs_delete(world, c); @@ -316,7 +316,7 @@ void OnDelete_id_remove_no_instances(void) { void OnDelete_id_delete_no_instances(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); ecs_add_pair(world, c, EcsOnDelete, EcsDelete); ecs_delete(world, c); @@ -330,7 +330,7 @@ void OnDelete_id_throw_no_instances(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); ecs_add_pair(world, c, EcsOnDelete, EcsPanic); test_expect_abort(); @@ -340,8 +340,8 @@ void OnDelete_id_throw_no_instances(void) { void OnDelete_cyclic_self(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); + ecs_entity_t r = ecs_new(world); ecs_add_pair(world, e, r, e); test_assert(ecs_has_pair(world, e, r, e)); @@ -357,8 +357,8 @@ void OnDelete_nonempty_cyclic_self(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new(world, Tag); - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t e = ecs_new_w(world, Tag); + ecs_entity_t r = ecs_new(world); ecs_add_pair(world, e, r, e); test_assert(ecs_has_pair(world, e, r, e)); @@ -372,8 +372,8 @@ void OnDelete_nonempty_cyclic_self(void) { void OnDelete_cyclic_id_default(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); ecs_add_id(world, a, b); ecs_add_id(world, b, a); @@ -393,8 +393,8 @@ void OnDelete_cyclic_id_default(void) { void OnDelete_cyclic_id_remove(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); ecs_add_pair(world, a, EcsOnDelete, EcsRemove); @@ -428,8 +428,8 @@ void OnDelete_cyclic_id_remove(void) { void OnDelete_cyclic_id_remove_both(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); ecs_add_pair(world, a, EcsOnDelete, EcsRemove); ecs_add_pair(world, b, EcsOnDelete, EcsRemove); @@ -451,8 +451,8 @@ void OnDelete_cyclic_id_remove_both(void) { void OnDelete_cyclic_id_delete(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); ecs_add_pair(world, a, EcsOnDelete, EcsDelete); ecs_add_id(world, a, b); @@ -468,8 +468,8 @@ void OnDelete_cyclic_id_delete(void) { void OnDelete_cyclic_id_delete_both(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); ecs_add_pair(world, a, EcsOnDelete, EcsDelete); ecs_add_pair(world, b, EcsOnDelete, EcsDelete); @@ -486,8 +486,8 @@ void OnDelete_cyclic_id_delete_both(void) { void OnDelete_cyclic_relation_default(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); ecs_add_id(world, a, b); ecs_add_id(world, b, a); @@ -507,10 +507,10 @@ void OnDelete_cyclic_relation_default(void) { void OnDelete_cyclic_relation_remove(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); - ecs_entity_t o_a = ecs_new_id(world); - ecs_entity_t o_b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); + ecs_entity_t o_a = ecs_new(world); + ecs_entity_t o_b = ecs_new(world); ecs_add_pair(world, a, EcsOnDelete, EcsRemove); ecs_add_pair(world, a, b, o_b); @@ -531,10 +531,10 @@ void OnDelete_cyclic_relation_remove(void) { void OnDelete_cyclic_relation_remove_both(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); - ecs_entity_t o_a = ecs_new_id(world); - ecs_entity_t o_b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); + ecs_entity_t o_a = ecs_new(world); + ecs_entity_t o_b = ecs_new(world); ecs_add_pair(world, a, EcsOnDelete, EcsRemove); ecs_add_pair(world, b, EcsOnDelete, EcsRemove); @@ -557,10 +557,10 @@ void OnDelete_cyclic_relation_remove_both(void) { void OnDelete_cyclic_relation_delete(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); - ecs_entity_t o_a = ecs_new_id(world); - ecs_entity_t o_b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); + ecs_entity_t o_a = ecs_new(world); + ecs_entity_t o_b = ecs_new(world); ecs_add_pair(world, a, EcsOnDelete, EcsDelete); ecs_add_pair(world, a, b, o_b); @@ -576,10 +576,10 @@ void OnDelete_cyclic_relation_delete(void) { void OnDelete_cyclic_relation_delete_both(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); - ecs_entity_t o_a = ecs_new_id(world); - ecs_entity_t o_b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); + ecs_entity_t o_a = ecs_new(world); + ecs_entity_t o_b = ecs_new(world); ecs_add_pair(world, a, EcsOnDelete, EcsDelete); ecs_add_pair(world, b, EcsOnDelete, EcsDelete); @@ -596,9 +596,9 @@ void OnDelete_cyclic_relation_delete_both(void) { void OnDelete_cyclic_object_default(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); + ecs_entity_t r = ecs_new(world); ecs_add_pair(world, a, r, b); ecs_add_pair(world, b, r, a); @@ -618,9 +618,9 @@ void OnDelete_cyclic_object_default(void) { void OnDelete_cyclic_object_remove(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); + ecs_entity_t r = ecs_new(world); ecs_add_pair(world, r, EcsOnDeleteTarget, EcsRemove); ecs_add_pair(world, a, r, b); @@ -641,9 +641,9 @@ void OnDelete_cyclic_object_remove(void) { void OnDelete_cyclic_object_delete(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); + ecs_entity_t r = ecs_new(world); ecs_add_pair(world, r, EcsOnDeleteTarget, EcsDelete); ecs_add_pair(world, a, r, b); @@ -660,9 +660,9 @@ void OnDelete_cyclic_overlapping_table(void) { ecs_world_t *world = ecs_mini(); ecs_entity_t rel = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); - ecs_entity_t obj_a = ecs_new_id(world); - ecs_entity_t obj_b = ecs_new_id(world); - ecs_entity_t obj_c = ecs_new_id(world); + ecs_entity_t obj_a = ecs_new(world); + ecs_entity_t obj_b = ecs_new(world); + ecs_entity_t obj_c = ecs_new(world); ecs_add_pair(world, obj_a, rel, obj_b); ecs_add_pair(world, obj_b, rel, obj_a); @@ -681,17 +681,17 @@ void OnDelete_cyclic_overlapping_table(void) { void OnDelete_cyclic_overlapping_new_tables(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t tag = ecs_new_id(world); + ecs_entity_t tag = ecs_new(world); ecs_entity_t rel_1 = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); - ecs_entity_t rel_2 = ecs_new_id(world); - ecs_entity_t obj_a = ecs_new_id(world); - ecs_entity_t obj_b = ecs_new_id(world); + ecs_entity_t rel_2 = ecs_new(world); + ecs_entity_t obj_a = ecs_new(world); + ecs_entity_t obj_b = ecs_new(world); ecs_add_pair(world, obj_a, rel_1, obj_b); ecs_add_pair(world, obj_b, rel_1, obj_a); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, rel_2, obj_a); ecs_add_pair(world, e, rel_2, obj_b); ecs_add_id(world, e, tag); @@ -709,14 +709,14 @@ void OnDelete_cyclic_object_mixed(void) { ecs_world_t *world = ecs_mini(); ecs_entity_t rel_1 = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); - ecs_entity_t rel_2 = ecs_new_id(world); - ecs_entity_t obj_a = ecs_new_id(world); - ecs_entity_t obj_b = ecs_new_id(world); + ecs_entity_t rel_2 = ecs_new(world); + ecs_entity_t obj_a = ecs_new(world); + ecs_entity_t obj_b = ecs_new(world); ecs_add_pair(world, obj_a, rel_1, obj_b); ecs_add_pair(world, obj_b, rel_1, obj_a); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, rel_2, obj_a); ecs_add_pair(world, e, rel_2, obj_b); @@ -737,8 +737,8 @@ void OnDelete_cyclic_storage_table(void) { ecs_entity_t rel_1 = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); ecs_set(world, rel_1, EcsComponent, {1, 1}); - ecs_entity_t obj_a = ecs_new_id(world); - ecs_entity_t obj_b = ecs_new_id(world); + ecs_entity_t obj_a = ecs_new(world); + ecs_entity_t obj_b = ecs_new(world); ecs_add_pair(world, obj_a, rel_1, obj_b); @@ -746,7 +746,7 @@ void OnDelete_cyclic_storage_table(void) { ecs_add_pair(world, obj_b, rel_1, obj_a); ecs_add_pair(world, obj_b, rel_1, obj_b); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, rel_1, obj_a); ecs_add_pair(world, e, rel_1, obj_b); @@ -767,8 +767,8 @@ void OnDelete_cyclic_storage_table_2(void) { ecs_entity_t rel_1 = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); ecs_set(world, rel_1, EcsComponent, {1, 1}); - ecs_entity_t obj_a = ecs_new_id(world); - ecs_entity_t obj_b = ecs_new_id(world); + ecs_entity_t obj_a = ecs_new(world); + ecs_entity_t obj_b = ecs_new(world); ecs_add_pair(world, obj_a, rel_1, obj_b); @@ -776,7 +776,7 @@ void OnDelete_cyclic_storage_table_2(void) { ecs_add_pair(world, obj_b, rel_1, obj_a); ecs_add_pair(world, obj_b, rel_1, obj_b); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, rel_1, obj_a); ecs_add_pair(world, e, rel_1, obj_b); @@ -799,8 +799,8 @@ void OnDelete_cyclic_storage_table_3(void) { ecs_entity_t rel_1 = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); ecs_set(world, rel_1, EcsComponent, {1, 1}); - ecs_entity_t obj_a = ecs_new_id(world); - ecs_entity_t obj_b = ecs_new_id(world); + ecs_entity_t obj_a = ecs_new(world); + ecs_entity_t obj_b = ecs_new(world); ecs_add_pair(world, obj_a, rel_1, obj_b); @@ -808,7 +808,7 @@ void OnDelete_cyclic_storage_table_3(void) { ecs_add_pair(world, obj_b, rel_1, obj_a); ecs_add_pair(world, obj_b, rel_1, obj_b); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, rel_1, obj_a); ecs_add_pair(world, e, rel_1, obj_b); @@ -827,15 +827,15 @@ void OnDelete_cyclic_set_empty(void) { ECS_TAG(world, Tag); ecs_entity_t rel_1 = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); - ecs_entity_t rel_2 = ecs_new_id(world); - ecs_entity_t obj_a = ecs_new_id(world); - ecs_entity_t obj_b = ecs_new_id(world); + ecs_entity_t rel_2 = ecs_new(world); + ecs_entity_t obj_a = ecs_new(world); + ecs_entity_t obj_b = ecs_new(world); ecs_add_pair(world, obj_a, rel_1, obj_b); ecs_add_pair(world, obj_b, rel_1, obj_a); ecs_add_pair(world, obj_b, rel_2, obj_b); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); ecs_add_pair(world, e, rel_2, obj_a); ecs_delete(world, obj_a); @@ -855,8 +855,8 @@ void OnDelete_2_acyclic_relations_w_cycle(void) { ecs_entity_t r1 = ecs_new_w_id(world, EcsTraversable); ecs_entity_t r2 = ecs_new_w_id(world, EcsTraversable); - ecs_entity_t a = ecs_new_id(world); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); ecs_add_pair(world, a, r1, b); ecs_add_pair(world, b, r2, a); @@ -869,8 +869,8 @@ void OnDelete_remove_2_comps(void) { ECS_COMPONENT(world, Position); - ecs_entity_t c = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, c); ecs_set(world, e, Position, {10, 20}); @@ -893,15 +893,15 @@ void OnDelete_remove_2_comps_to_existing_table(void) { ECS_COMPONENT(world, Position); - ecs_entity_t c = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, c); ecs_set(world, e, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Position, {20, 30}); - ecs_entity_t e3 = ecs_new_id(world); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Position, {40, 50}); ecs_delete(world, c); @@ -928,15 +928,15 @@ void OnDelete_remove_2_comps_to_existing_table(void) { void OnDelete_delete_recursive(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); ecs_add_pair(world, r, EcsOnDeleteTarget, EcsDelete); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); - ecs_entity_t c = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); ecs_add_pair(world, c, r, p); - ecs_entity_t gc = ecs_new_id(world); + ecs_entity_t gc = ecs_new(world); ecs_add_pair(world, gc, r, c); ecs_delete(world, p); @@ -954,7 +954,9 @@ void OnDelete_component_throw(void) { ECS_COMPONENT(world, Position); - ecs_set(world, 0, Position, {10, 20}); + ecs_add_pair(world, ecs_id(Position), EcsOnDelete, EcsPanic); + + ecs_insert(world, ecs_value(Position, {10, 20})); test_expect_abort(); ecs_delete(world, ecs_id(Position)); @@ -963,11 +965,11 @@ void OnDelete_component_throw(void) { void OnDelete_remove_2_relations(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o_1 = ecs_new_id(world); - ecs_entity_t o_2 = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o_1 = ecs_new(world); + ecs_entity_t o_2 = ecs_new(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, o_1); ecs_add_pair(world, e, r, o_2); @@ -985,11 +987,11 @@ void OnDelete_remove_2_relations(void) { void OnDelete_remove_object_w_2_relations(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); + ecs_entity_t o = ecs_new(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r_1, o); ecs_add_pair(world, e, r_2, o); @@ -1007,14 +1009,14 @@ void OnDelete_remove_object_w_2_relations(void) { void OnDelete_remove_object_w_5_relations(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); - ecs_entity_t r_3 = ecs_new_id(world); - ecs_entity_t r_4 = ecs_new_id(world); - ecs_entity_t r_5 = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); + ecs_entity_t r_3 = ecs_new(world); + ecs_entity_t r_4 = ecs_new(world); + ecs_entity_t r_5 = ecs_new(world); + ecs_entity_t o = ecs_new(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r_1, o); ecs_add_pair(world, e, r_2, o); ecs_add_pair(world, e, r_3, o); @@ -1043,11 +1045,11 @@ void OnDelete_remove_object_w_50_relations(void) { ecs_entity_t r[NUM]; int i; for (i = 0; i < NUM; i ++) { - r[i] = ecs_new_id(world); + r[i] = ecs_new(world); } - ecs_entity_t o = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t o = ecs_new(world); + ecs_entity_t e = ecs_new(world); for (i = 0; i < NUM; i ++) { ecs_add_pair(world, e, r[i], o); @@ -1076,10 +1078,10 @@ void OnDelete_remove_object_w_50_relations_3_tables(void) { ecs_entity_t r[NUM]; int i; for (i = 0; i < NUM; i ++) { - r[i] = ecs_new_id(world); + r[i] = ecs_new(world); } - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t o = ecs_new(world); ecs_entity_t e_1 = ecs_new_w_id(world, TagA); ecs_entity_t e_2 = ecs_new_w_id(world, TagB); ecs_entity_t e_3 = ecs_new_w_id(world, TagC); @@ -1110,12 +1112,12 @@ void OnDelete_remove_object_w_50_relations_3_tables(void) { void OnDelete_remove_object_w_3_relations_interleaved(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); - ecs_entity_t r_3 = ecs_new_id(world); - ecs_entity_t t_1 = ecs_new_id(world); - ecs_entity_t t_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); + ecs_entity_t r_3 = ecs_new(world); + ecs_entity_t t_1 = ecs_new(world); + ecs_entity_t t_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r_1, t_1); ecs_add_pair(world, e, r_2, t_1); @@ -1158,12 +1160,12 @@ void OnDelete_remove_id_from_2_tables(void) { ECS_TAG(world, Tag); - ecs_entity_t c = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add_id(world, e1, c); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_add_id(world, e2, c); ecs_add_id(world, e2, Tag); @@ -1184,13 +1186,13 @@ void OnDelete_remove_relation_from_2_tables(void) { ECS_TAG(world, Tag); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add_pair(world, e1, r, o); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_add_pair(world, e1, r, o); ecs_add_id(world, e2, Tag); @@ -1211,13 +1213,13 @@ void OnDelete_remove_object_from_2_tables(void) { ECS_TAG(world, Tag); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add_pair(world, e1, r, o); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_add_pair(world, e1, r, o); ecs_add_id(world, e2, Tag); @@ -1236,10 +1238,10 @@ void OnDelete_remove_object_from_2_tables(void) { void OnDelete_remove_id_and_relation(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, r); ecs_add_pair(world, e, r, o); @@ -1257,14 +1259,14 @@ void OnDelete_remove_id_and_relation_from_2_tables(void) { ECS_TAG(world, Tag); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t o = ecs_new(world); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add_pair(world, e1, r, o); ecs_add_id(world, e1, r); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_add_pair(world, e1, r, o); ecs_add_id(world, e2, r); ecs_add_id(world, e2, Tag); @@ -1296,13 +1298,13 @@ void OnDelete_stresstest_many_objects(void) { /* Precreate relationships so we get different relationship ids */ ecs_entity_t relationships[100]; for (i = 0; i < 100; i ++) { - relationships[i] = ecs_new_id(world); + relationships[i] = ecs_new(world); } for (ri = 0; ri < 100; ri ++) { for (i = 0; i < 100; i ++) { - ecs_entity_t o = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t o = ecs_new(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, Tag); ecs_add_pair(world, e, relationships[ri], o); } @@ -1332,13 +1334,13 @@ void OnDelete_stresstest_many_relations(void) { /* Precreate objects so we get different relationship ids */ ecs_entity_t objects[100]; for (i = 0; i < 100; i ++) { - objects[i] = ecs_new_id(world); + objects[i] = ecs_new(world); } for (oi = 0; oi < 100; oi ++) { for (i = 0; i < 100; i ++) { - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, Tag); ecs_add_pair(world, e, r, objects[oi]); } @@ -1363,7 +1365,7 @@ void OnDelete_stresstest_many_objects_on_delete(void) { /* Precreate relationships so we get different relationship ids */ ecs_entity_t relationships[100]; for (i = 0; i < 100; i ++) { - relationships[i] = ecs_new_id(world); + relationships[i] = ecs_new(world); ecs_add_pair(world, relationships[i], EcsOnDelete, EcsDelete); } @@ -1373,8 +1375,8 @@ void OnDelete_stresstest_many_objects_on_delete(void) { for (ri = 0; ri < 100; ri ++) { for (i = 0; i < 100; i ++) { - ecs_entity_t o = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t o = ecs_new(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, relationships[ri], o); ecs_add_id(world, e, Tag); } @@ -1400,7 +1402,7 @@ void OnDelete_stresstest_many_relations_on_delete(void) { /* Precreate objects so we get different relationship ids */ ecs_entity_t objects[COUNT]; for (i = 0; i < COUNT; i ++) { - objects[i] = ecs_new_id(world); + objects[i] = ecs_new(world); } /* Add (OnDeleteTarget, Delete) to a dummy object so that it won't mess up @@ -1413,10 +1415,10 @@ void OnDelete_stresstest_many_relations_on_delete(void) { for (oi = 0; oi < COUNT; oi ++) { for (i = 0; i < COUNT; i ++) { - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); ecs_add_pair(world, r, EcsOnDeleteTarget, EcsDelete); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, objects[oi]); ecs_add_id(world, e, Tag); } @@ -1450,14 +1452,14 @@ void OnDelete_empty_table_w_on_remove(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); ecs_add_id(world, e1, e2); ecs_add_id(world, e1, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = e2, + .query.terms[0].id = e2, .events = {EcsOnRemove}, .callback = dummy_on_remove }); @@ -1484,7 +1486,7 @@ typedef struct { static void delete_on_remove(ecs_iter_t *it) { test_int(it->count, 1); - Entity *comp = ecs_field(it, Entity, 1); + Entity *comp = ecs_field(it, Entity, 0); test_assert(comp != NULL); test_assert(comp->other != 0); ecs_delete(it->world, comp->other); @@ -1506,8 +1508,8 @@ void OnDelete_delete_table_in_on_remove_during_fini(void) { /* Setup scenario so that the order in which tables are deleted doesn't * matter for reproducing the issue */ - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); /* Add entities to each other so that deleting one will delete the table of * the other. */ @@ -1519,14 +1521,14 @@ void OnDelete_delete_table_in_on_remove_during_fini(void) { ecs_observer_init(world, &(ecs_observer_desc_t){ .events = {EcsOnRemove}, - .filter.terms[0].id = e1, + .query.terms[0].id = e1, .callback = delete_self_on_remove, .ctx = &e2 }); ecs_observer_init(world, &(ecs_observer_desc_t){ .events = {EcsOnRemove}, - .filter.terms[0].id = e2, + .query.terms[0].id = e2, .callback = delete_self_on_remove, .ctx = &e1 }); @@ -1546,8 +1548,8 @@ void OnDelete_delete_other_in_on_remove_during_fini(void) { /* Setup scenario so that the order in which tables are deleted doesn't * matter for reproducing the issue */ - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); /* Removing Entity from one will delete the other */ ecs_set(world, e1, Entity, {e2}); @@ -1558,7 +1560,7 @@ void OnDelete_delete_other_in_on_remove_during_fini(void) { ecs_observer_init(world, &(ecs_observer_desc_t){ .events = {EcsOnRemove}, - .filter.terms[0].id = ecs_id(Entity), + .query.terms[0].id = ecs_id(Entity), .callback = delete_on_remove }); @@ -1571,9 +1573,9 @@ void OnDelete_delete_other_in_on_remove_during_fini(void) { void OnDelete_remove_id_w_role(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t c = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, ECS_TOGGLE | c); test_assert(ecs_has_id(world, e, ECS_TOGGLE | c)); @@ -1591,16 +1593,16 @@ void OnDelete_remove_id_w_role(void) { void OnDelete_remove_rel_w_override_pair(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); - ecs_entity_t t = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); + ecs_entity_t t = ecs_new(world); + ecs_entity_t e = ecs_new(world); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t)); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t))); ecs_delete(world, r_1); @@ -1609,8 +1611,8 @@ void OnDelete_remove_rel_w_override_pair(void) { test_assert(ecs_is_alive(world, t)); test_assert(ecs_is_alive(world, e)); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t))); ecs_fini(world); } @@ -1618,16 +1620,16 @@ void OnDelete_remove_rel_w_override_pair(void) { void OnDelete_remove_obj_w_override_pair(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); - ecs_entity_t t_1 = ecs_new_id(world); - ecs_entity_t t_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); + ecs_entity_t t_1 = ecs_new(world); + ecs_entity_t t_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r, t_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r, t_2)); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r, t_2))); ecs_delete(world, t_1); @@ -1636,8 +1638,8 @@ void OnDelete_remove_obj_w_override_pair(void) { test_assert(ecs_is_alive(world, t_2)); test_assert(ecs_is_alive(world, e)); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r, t_2))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r, t_2))); ecs_fini(world); } @@ -1645,18 +1647,18 @@ void OnDelete_remove_obj_w_override_pair(void) { void OnDelete_remove_rel_w_override_pair_after_on_delete_target(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); - ecs_entity_t t = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); + ecs_entity_t t = ecs_new(world); + ecs_entity_t e = ecs_new(world); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t)); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t))); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_add_pair(world, r_1, EcsChildOf, p); ecs_delete(world, p); @@ -1667,8 +1669,8 @@ void OnDelete_remove_rel_w_override_pair_after_on_delete_target(void) { test_assert(ecs_is_alive(world, t)); test_assert(ecs_is_alive(world, e)); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t))); ecs_fini(world); } @@ -1676,19 +1678,19 @@ void OnDelete_remove_rel_w_override_pair_after_on_delete_target(void) { void OnDelete_remove_rel_w_override_pair_2_ids(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); - ecs_entity_t t_1 = ecs_new_id(world); - ecs_entity_t t_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); + ecs_entity_t t_1 = ecs_new(world); + ecs_entity_t t_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1)); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1))); ecs_delete(world, r_1); @@ -1698,9 +1700,9 @@ void OnDelete_remove_rel_w_override_pair_2_ids(void) { test_assert(ecs_is_alive(world, t_2)); test_assert(ecs_is_alive(world, e)); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1))); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1))); ecs_fini(world); } @@ -1708,19 +1710,19 @@ void OnDelete_remove_rel_w_override_pair_2_ids(void) { void OnDelete_remove_obj_w_override_pair_2_ids(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); - ecs_entity_t t_1 = ecs_new_id(world); - ecs_entity_t t_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); + ecs_entity_t t_1 = ecs_new(world); + ecs_entity_t t_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2)); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2))); ecs_delete(world, t_1); @@ -1730,9 +1732,9 @@ void OnDelete_remove_obj_w_override_pair_2_ids(void) { test_assert(ecs_is_alive(world, t_2)); test_assert(ecs_is_alive(world, e)); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1))); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2))); ecs_fini(world); } @@ -1740,28 +1742,28 @@ void OnDelete_remove_obj_w_override_pair_2_ids(void) { void OnDelete_remove_obj_w_override_pair_3_ids(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); - ecs_entity_t r_3 = ecs_new_id(world); - ecs_entity_t t_1 = ecs_new_id(world); - ecs_entity_t t_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); + ecs_entity_t r_3 = ecs_new(world); + ecs_entity_t t_1 = ecs_new(world); + ecs_entity_t t_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_2)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_2)); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_2))); ecs_delete(world, t_1); @@ -1772,13 +1774,13 @@ void OnDelete_remove_obj_w_override_pair_3_ids(void) { test_assert(ecs_is_alive(world, t_2)); test_assert(ecs_is_alive(world, e)); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1))); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1))); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_2))); ecs_fini(world); } @@ -1786,35 +1788,35 @@ void OnDelete_remove_obj_w_override_pair_3_ids(void) { void OnDelete_remove_mixed_w_override_pair_3_ids(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r_1 = ecs_new_id(world); - ecs_entity_t r_2 = ecs_new_id(world); - ecs_entity_t r_3 = ecs_new_id(world); + ecs_entity_t r_1 = ecs_new(world); + ecs_entity_t r_2 = ecs_new(world); + ecs_entity_t r_3 = ecs_new(world); - ecs_entity_t t_1 = ecs_new_id(world); - ecs_entity_t t_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t t_1 = ecs_new(world); + ecs_entity_t t_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(t_1, r_1)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(t_1, r_2)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(t_1, r_3)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(t_1, r_1)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(t_1, r_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(t_1, r_3)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_2)); - ecs_add_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_2)); + ecs_add_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_2)); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(t_1, r_1))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(t_1, r_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(t_1, r_3))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(t_1, r_1))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(t_1, r_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(t_1, r_3))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_2))); ecs_delete(world, t_1); @@ -1825,16 +1827,16 @@ void OnDelete_remove_mixed_w_override_pair_3_ids(void) { test_assert(ecs_is_alive(world, t_2)); test_assert(ecs_is_alive(world, e)); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_1))); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_1))); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_1))); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(t_1, r_1))); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(t_1, r_2))); - test_assert(!ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(t_1, r_3))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(t_1, r_1))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(t_1, r_2))); + test_assert(!ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(t_1, r_3))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_1, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_2, t_2))); - test_assert(ecs_has_id(world, e, ECS_OVERRIDE | ecs_pair(r_3, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_1, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_2, t_2))); + test_assert(ecs_has_id(world, e, ECS_AUTO_OVERRIDE | ecs_pair(r_3, t_2))); ecs_fini(world); } @@ -1844,9 +1846,9 @@ void OnDelete_merge_pair_component(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e_1 = ecs_new_id(world); - ecs_entity_t e_2 = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); + ecs_entity_t e_1 = ecs_new(world); + ecs_entity_t e_2 = ecs_new(world); + ecs_entity_t obj = ecs_new(world); ecs_add_id(world, e_1, e_2); ecs_set_pair(world, e_1, Position, obj, {10, 20}); @@ -1895,9 +1897,9 @@ void OnDelete_delete_with_component(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e_1 = ecs_new(world, Position); - ecs_entity_t e_2 = ecs_new(world, Position); - ecs_entity_t e_3 = ecs_new(world, Position); + ecs_entity_t e_1 = ecs_new_w(world, Position); + ecs_entity_t e_2 = ecs_new_w(world, Position); + ecs_entity_t e_3 = ecs_new_w(world, Position); ecs_add(world, e_3, Velocity); @@ -2073,8 +2075,8 @@ void OnDelete_remove_childof_entity(void) { ECS_TAG(world, Tag); - ecs_entity_t parent_1 = ecs_new_id(world); - ecs_entity_t parent_2 = ecs_new_id(world); + ecs_entity_t parent_1 = ecs_new(world); + ecs_entity_t parent_2 = ecs_new(world); ecs_entity_t e_1 = ecs_new_w_pair(world, EcsChildOf, parent_1); ecs_entity_t e_2 = ecs_new_w_pair(world, EcsChildOf, parent_1); @@ -2121,13 +2123,13 @@ void OnDelete_remove_childof_wildcard(void) { /* Create custom childof relationship so we won't try to remove ChildOf from * builtin entities */ - ecs_entity_t child_of = ecs_new_id(world); + ecs_entity_t child_of = ecs_new(world); /* Simulate same behavior as ChildOf */ ecs_add_pair(world, child_of, EcsOnDeleteTarget, EcsDelete); - ecs_entity_t parent_1 = ecs_new_id(world); - ecs_entity_t parent_2 = ecs_new_id(world); + ecs_entity_t parent_1 = ecs_new(world); + ecs_entity_t parent_2 = ecs_new(world); ecs_entity_t e_1 = ecs_new_w_pair(world, child_of, parent_1); ecs_entity_t e_2 = ecs_new_w_pair(world, child_of, parent_1); @@ -2171,7 +2173,7 @@ void OnDelete_delete_child_of_delete_with(void) { ECS_TAG(world, Rel); - ecs_entity_t obj = ecs_new_id(world); + ecs_entity_t obj = ecs_new(world); ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, obj); ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, obj); @@ -2216,8 +2218,8 @@ void OnDelete_delete_with_component_after_delete_cyclic_self(void) { ECS_TAG(world, Rel); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); ecs_add_pair(world, e2, Rel, e2); test_assert(ecs_is_alive(world, e1)); @@ -2237,9 +2239,9 @@ void OnDelete_delete_with_component_after_delete_cyclic(void) { ECS_TAG(world, Rel); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); ecs_add_pair(world, e2, Rel, e3); ecs_add_pair(world, e3, Rel, e2); @@ -2263,18 +2265,18 @@ void OnDelete_delete_with_component_after_delete_cyclic_w_alive_moved(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); - ecs_entity_t e4 = ecs_new_id(world); + ecs_entity_t e4 = ecs_new(world); ecs_add_pair(world, e4, Rel, e2); ecs_add(world, e4, Velocity); ecs_add_pair(world, e2, Rel, e3); ecs_add_pair(world, e3, Rel, e2); - ecs_entity_t e5 = ecs_new_id(world); + ecs_entity_t e5 = ecs_new(world); ecs_add_pair(world, e5, Rel, e2); ecs_add_pair(world, e5, Rel, e3); ecs_add(world, e5, Velocity); @@ -2305,12 +2307,12 @@ void OnDelete_deep_clean_64(void) { ecs_entity_t r = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); test_assert(r != 0); - ecs_entity_t first = ecs_new_id(world); + ecs_entity_t first = ecs_new(world); ecs_entity_t prev = first; ecs_entity_t entities[64]; for (int i = 0; i < 64; i ++) { - entities[i] = ecs_new_id(world); + entities[i] = ecs_new(world); ecs_add_pair(world, entities[i], r, prev); prev = entities[i]; } @@ -2336,12 +2338,12 @@ void OnDelete_deep_clean_256(void) { ecs_entity_t r = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); test_assert(r != 0); - ecs_entity_t first = ecs_new_id(world); + ecs_entity_t first = ecs_new(world); ecs_entity_t prev = first; ecs_entity_t entities[256]; for (int i = 0; i < 256; i ++) { - entities[i] = ecs_new_id(world); + entities[i] = ecs_new(world); ecs_add_pair(world, entities[i], r, prev); prev = entities[i]; } @@ -2361,118 +2363,51 @@ void OnDelete_deep_clean_256(void) { ecs_fini(world); } -void OnDelete_id_w_switch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Sw, Union); - ECS_TAG(world, C1); - ECS_TAG(world, C2); - ECS_TAG(world, C3); - ECS_TAG(world, C4); - - ecs_entity_t e1 = ecs_new_w_pair(world, Sw, C1); - ecs_entity_t e2 = ecs_new_w_pair(world, Sw, C2); - ecs_entity_t e3 = ecs_new_w_pair(world, Sw, C3); - ecs_entity_t e4 = ecs_new_w_pair(world, Sw, C4); - - ecs_entity_t t = ecs_new_id(world); - - ecs_add_id(world, e3, t); - ecs_add_id(world, e4, t); - - test_assert( ecs_has_pair(world, e1, Sw, C1)); - test_assert( ecs_has_pair(world, e2, Sw, C2)); - test_assert( ecs_has_pair(world, e3, Sw, C3)); - test_assert( ecs_has_pair(world, e4, Sw, C4)); - - ecs_delete(world, t); - - test_assert( ecs_has_pair(world, e1, Sw, C1)); - test_assert( ecs_has_pair(world, e2, Sw, C2)); - test_assert( ecs_has_pair(world, e3, Sw, C3)); - test_assert( ecs_has_pair(world, e4, Sw, C4)); - - ecs_fini(world); -} - void OnDelete_id_w_disabled(void) { ecs_world_t *world = ecs_mini(); - ECS_TAG(world, Tag); + ECS_ENTITY(world, Tag, CanToggle); + - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); ecs_add_id(world, e1, ECS_TOGGLE | Tag); ecs_add_id(world, e2, ECS_TOGGLE | Tag); ecs_add_id(world, e3, ECS_TOGGLE | Tag); ecs_add_id(world, e4, ECS_TOGGLE | Tag); - test_bool(false, ecs_is_enabled_component(world, e1, Tag)); - test_bool(false, ecs_is_enabled_component(world, e2, Tag)); - test_bool(false, ecs_is_enabled_component(world, e3, Tag)); - test_bool(false, ecs_is_enabled_component(world, e4, Tag)); + test_bool(false, ecs_is_enabled(world, e1, Tag)); + test_bool(false, ecs_is_enabled(world, e2, Tag)); + test_bool(false, ecs_is_enabled(world, e3, Tag)); + test_bool(false, ecs_is_enabled(world, e4, Tag)); ecs_enable_component(world, e1, Tag, true); ecs_enable_component(world, e3, Tag, true); - test_bool(true, ecs_is_enabled_component(world, e1, Tag)); - test_bool(false, ecs_is_enabled_component(world, e2, Tag)); - test_bool(true, ecs_is_enabled_component(world, e3, Tag)); - test_bool(false, ecs_is_enabled_component(world, e4, Tag)); + test_bool(true, ecs_is_enabled(world, e1, Tag)); + test_bool(false, ecs_is_enabled(world, e2, Tag)); + test_bool(true, ecs_is_enabled(world, e3, Tag)); + test_bool(false, ecs_is_enabled(world, e4, Tag)); - ecs_entity_t t = ecs_new_id(world); + ecs_entity_t t = ecs_new(world); ecs_add_id(world, e3, t); ecs_add_id(world, e4, t); - test_bool(true, ecs_is_enabled_component(world, e1, Tag)); - test_bool(false, ecs_is_enabled_component(world, e2, Tag)); - test_bool(true, ecs_is_enabled_component(world, e3, Tag)); - test_bool(false, ecs_is_enabled_component(world, e4, Tag)); + test_bool(true, ecs_is_enabled(world, e1, Tag)); + test_bool(false, ecs_is_enabled(world, e2, Tag)); + test_bool(true, ecs_is_enabled(world, e3, Tag)); + test_bool(false, ecs_is_enabled(world, e4, Tag)); ecs_delete(world, t); - test_bool(true, ecs_is_enabled_component(world, e1, Tag)); - test_bool(false, ecs_is_enabled_component(world, e2, Tag)); - test_bool(true, ecs_is_enabled_component(world, e3, Tag)); - test_bool(false, ecs_is_enabled_component(world, e4, Tag)); - - ecs_fini(world); -} - -void OnDelete_id_to_no_switch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Sw, Union); - ECS_TAG(world, C1); - ECS_TAG(world, C2); - ECS_TAG(world, C3); - ECS_TAG(world, C4); - ECS_TAG(world, Tag); - - ecs_entity_t e1 = ecs_new_w_pair(world, Sw, C1); - ecs_entity_t e2 = ecs_new_w_pair(world, Sw, C2); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - - ecs_add(world, e1, Tag); - ecs_add(world, e2, Tag); - - test_assert( ecs_has_pair(world, e1, Sw, C1)); - test_assert( ecs_has_pair(world, e2, Sw, C2)); - - ecs_delete(world, Sw); - - test_assert( !ecs_has_pair(world, e1, Sw, C1)); - test_assert( !ecs_has_pair(world, e2, Sw, C2)); - - test_assert( ecs_has_id(world, e1, Tag)); - test_assert( ecs_has_id(world, e2, Tag)); - test_assert( ecs_has_id(world, e3, Tag)); - test_assert( ecs_has_id(world, e4, Tag)); + test_bool(true, ecs_is_enabled(world, e1, Tag)); + test_bool(false, ecs_is_enabled(world, e2, Tag)); + test_bool(true, ecs_is_enabled(world, e3, Tag)); + test_bool(false, ecs_is_enabled(world, e4, Tag)); ecs_fini(world); } @@ -2483,10 +2418,10 @@ void OnDelete_id_to_no_disabled(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); - ecs_entity_t e4 = ecs_new(world, TagA); + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); ecs_add(world, e1, TagB); ecs_add(world, e2, TagB); @@ -2494,8 +2429,8 @@ void OnDelete_id_to_no_disabled(void) { ecs_add_id(world, e1, ECS_TOGGLE | TagB); ecs_add_id(world, e2, ECS_TOGGLE | TagB); - test_bool(false, ecs_is_enabled_component(world, e1, TagB)); - test_bool(false, ecs_is_enabled_component(world, e2, TagB)); + test_bool(false, ecs_is_enabled(world, e1, TagB)); + test_bool(false, ecs_is_enabled(world, e2, TagB)); ecs_delete(world, TagB); @@ -2517,13 +2452,9 @@ void OnDelete_remove_on_delete_action(void) { ECS_COMPONENT(world, Position); - test_assert( ecs_has_pair(world, ecs_id(Position), EcsOnDelete, EcsPanic)); - - ecs_remove_pair(world, ecs_id(Position), EcsOnDelete, EcsWildcard); - test_assert( !ecs_has_pair(world, ecs_id(Position), EcsOnDelete, EcsPanic)); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert( ecs_has(world, e, Position)); ecs_delete(world, ecs_id(Position)); @@ -2539,8 +2470,8 @@ void OnDelete_delete_with_w_relation(void) { ECS_TAG(world, Tag); ECS_TAG(world, Rel); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); ecs_add_pair(world, e2, Rel, e1); ecs_delete_with(world, Tag); @@ -2555,7 +2486,7 @@ static int delete_target_invoked = 0; static void DeleteTarget(ecs_iter_t *it) { - ecs_id_t pair = ecs_field_id(it, 1); + ecs_id_t pair = ecs_field_id(it, 0); test_assert(ecs_id_is_pair(pair)); ecs_delete(it->world, ECS_PAIR_SECOND(pair)); delete_target_invoked ++; @@ -2567,12 +2498,12 @@ void OnDelete_delete_self_in_on_remove(void) { ecs_entity_t Rel = ecs_new_w_pair(world, EcsOnDeleteTarget, EcsDelete); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = { EcsOnRemove }, .callback = DeleteTarget }); - ecs_entity_t a = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); ecs_entity_t b = ecs_new_w_pair(world, Rel, a); test_int(0, delete_target_invoked); @@ -2607,15 +2538,15 @@ void OnDelete_delete_nested_in_on_remove(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = { EcsOnRemove }, .callback = DeleteOther, .ctx = &Tag }); - ecs_entity_t a = ecs_new_id(world); + ecs_entity_t a = ecs_new(world); ecs_entity_t b = ecs_new_w_pair(world, Rel, a); - ecs_entity_t c = ecs_new(world, Tag); + ecs_entity_t c = ecs_new_w(world, Tag); test_int(0, delete_target_invoked); @@ -2636,7 +2567,7 @@ void OnDelete_delete_nested_in_on_remove(void) { static void AddRemoved(ecs_iter_t *it) { - ecs_id_t id = ecs_field_id(it, 1); + ecs_id_t id = ecs_field_id(it, 0); for (int i = 0; i < it->count; i ++) { ecs_new_w_id(it->world, id); /* create entity with removed id */ } @@ -2647,11 +2578,11 @@ void OnDelete_add_deleted_in_on_remove(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t t = ecs_new_id(world); + ecs_entity_t t = ecs_new(world); ecs_new_w_id(world, t); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = t, + .query.terms[0].id = t, .events = { EcsOnRemove }, .callback = AddRemoved }); @@ -2664,18 +2595,20 @@ void OnDelete_delete_tree_w_query(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); - ECS_ENTITY(world, Rel, EcsTraversable); + ECS_ENTITY(world, Rel, Traversable); ECS_TAG(world, Foo); - ecs_query_t *q = ecs_query_new(world, "Tag(up(Rel))"); + ecs_query_t *q = ecs_query(world, { .expr = "Tag(up Rel)" }); test_assert(q != NULL); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); ecs_add_pair(world, e2, Rel, e1); ecs_add_id(world, e1, Foo); + ecs_query_fini(q); + ecs_fini(world); // Make sure query can handle matching with tables while entities are @@ -2707,7 +2640,7 @@ void OnDelete_fini_cleanup_order(void) { ECS_TAG(world, Foo); ECS_TAG(world, Bar); - ecs_entity_t e1 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e2); ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, e3); @@ -2717,7 +2650,7 @@ void OnDelete_fini_cleanup_order(void) { ecs_add(world, e2, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnRemove}, .callback = TestAlive }); @@ -2730,11 +2663,11 @@ void OnDelete_fini_cleanup_order(void) { void OnDelete_fini_cleanup_order_root_id_w_trait(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); ecs_add_id(world, r, EcsExclusive); - ecs_entity_t t = ecs_new_id(world); + ecs_entity_t t = ecs_new(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, r, t); ecs_fini(world); @@ -2762,7 +2695,7 @@ void OnDelete_fini_cleanup_order_entity_after_singleton(void) { ecs_singleton_set(world, Position, {10, 20}); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Velocity); ecs_fini(world); @@ -2780,7 +2713,7 @@ void OnDelete_fini_cleanup_order_entity_after_component(void) { .on_remove = ecs_on_remove(Velocity) }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Velocity); ecs_fini(world); @@ -2793,7 +2726,7 @@ void OnDelete_on_delete_parent_w_in_use_id_w_remove(void) { ECS_TAG(world, Tag); - ecs_entity_t p = ecs_new(world, Tag); + ecs_entity_t p = ecs_new_w(world, Tag); ecs_entity_t r = ecs_new_w_pair(world, EcsChildOf, p); ecs_entity_t o = ecs_new_w_pair(world, EcsChildOf, p); @@ -2817,7 +2750,7 @@ void OnDelete_on_delete_parent_w_in_use_id_w_delete(void) { ECS_TAG(world, Tag); - ecs_entity_t p = ecs_new(world, Tag); + ecs_entity_t p = ecs_new_w(world, Tag); ecs_entity_t r = ecs_new_w_pair(world, EcsChildOf, p); ecs_entity_t o = ecs_new_w_pair(world, EcsChildOf, p); @@ -2843,7 +2776,7 @@ void OnDelete_create_after_delete_with(void) { const ecs_type_info_t *ti = ecs_get_type_info(world, ecs_id(Position)); test_assert(ti != NULL); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(ecs_is_alive(world, e)); test_assert(ecs_has(world, e, Position)); @@ -2854,9 +2787,9 @@ void OnDelete_create_after_delete_with(void) { ti = ecs_get_type_info(world, ecs_id(Position)); test_assert(ti != NULL); - ecs_entity_t id = ecs_new_id(world); + ecs_entity_t id = ecs_new(world); test_assert(ecs_get_table(world, id) == NULL); - e = ecs_set(world, 0, Position, {10, 20}); + e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(ecs_is_alive(world, e)); test_assert(ecs_has(world, e, Position)); @@ -2867,8 +2800,9 @@ void OnDelete_delete_with_inherited_tag(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, Tag); + ecs_entity_t base = ecs_new_w(world, Tag); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_assert(ecs_is_alive(world, base)); @@ -2887,12 +2821,13 @@ void OnDelete_delete_with_inherited_tag_w_query(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, Tag); + ecs_entity_t base = ecs_new_w(world, Tag); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - ecs_query_t *query = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ Tag }} + ecs_query_t *query = ecs_query(world, { + .terms = {{ Tag }} }); test_assert(query != NULL); @@ -2918,6 +2853,8 @@ void OnDelete_delete_with_inherited_tag_w_query(void) { it = ecs_query_iter(world, query); test_bool(false, ecs_query_next(&it)); + ecs_query_fini(query); + ecs_fini(world); } @@ -2930,12 +2867,14 @@ void OnDelete_delete_with_inherited_tag_w_observer(void) { ECS_TAG(world, Tag); - ecs_entity_t base = ecs_new(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Tag); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); Probe ctx; ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ Tag }}, + .query.terms = {{ Tag }}, .events = { EcsOnRemove }, .callback = Observer, .ctx = &ctx @@ -2962,7 +2901,7 @@ void OnDelete_delete_with_inherited_tag_w_observer(void) { void OnDelete_delete_symmetric_relation(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, EcsSymmetric); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); @@ -2975,8 +2914,8 @@ void OnDelete_delete_symmetric_relation(void) { void OnDelete_delete_observed_symmetric_relation(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); - ecs_add_id(world, e, EcsTag); + ecs_entity_t e = ecs_new(world); + ecs_add_id(world, e, EcsPairIsTag); ecs_add_id(world, e, EcsSymmetric); ecs_delete(world, e); @@ -3001,7 +2940,7 @@ void OnDelete_nested_delete_with(void) { .on_remove = on_remove_delete_with }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, e); ecs_delete_with(world, ecs_id(Position)); @@ -3017,12 +2956,12 @@ void OnDelete_deferred_delete_with_after_create_named(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Tag); ecs_defer_begin(world); ecs_delete_with(world, Tag); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e2, Tag); ecs_defer_end(world); @@ -3039,15 +2978,15 @@ void OnDelete_deferred_delete_with_childof_after_create_named(void) { ECS_TAG(world, Tag); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); ecs_add(world, parent, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "parent.e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "parent.e1" }); ecs_add(world, e1, Tag); ecs_defer_begin(world); ecs_delete_with(world, ecs_childof(parent)); - ecs_entity_t e2 = ecs_new_entity(world, "parent.e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "parent.e2" }); ecs_add(world, e2, Tag); ecs_defer_end(world); @@ -3062,25 +3001,31 @@ void OnDelete_deferred_delete_with_childof_after_create_named(void) { void OnDelete_match_marked_for_deletion(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t ns = ecs_new_entity(world, "ns"); + ecs_entity_t ns = ecs_entity(world, { .name = "ns" }); ecs_set_scope(world, ns); ECS_COMPONENT(world, Position); - ecs_entity_t Foo = ecs_component(world, { .entity = ecs_new(world, 0) }); - ecs_entity_t Bar = ecs_component(world, { .entity = ecs_new(world, 0) }); + ecs_entity_t Foo = ecs_component(world, { .entity = ecs_new(world) }); + ecs_entity_t Bar = ecs_component(world, { .entity = ecs_new(world) }); ecs_set_scope(world, 0); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Bar, EcsOnInstantiate, EcsInherit); + /* During cleanup a new table will be created that matches the query */ - ecs_query_t *q = ecs_query_new(world, "ns.Position"); + ecs_query_t *q = ecs_query(world, { .expr = "ns.Position" }); test_assert(q != NULL); - ecs_entity_t prefab = ecs_new_id(world); + ecs_entity_t prefab = ecs_new(world); ecs_add_id(world, prefab, EcsPrefab); ecs_add(world, prefab, Position); - ecs_override_id(world, prefab, Foo); + ecs_auto_override_id(world, prefab, Foo); ecs_entity_t newEnemy = ecs_new_w_pair(world, EcsIsA, prefab); ecs_add_id(world, newEnemy, Bar); + ecs_query_fini(q); + ecs_fini(world); test_assert(true); /* Ensure cleanup was successful */ @@ -3090,8 +3035,8 @@ void OnDelete_delete_w_low_rel_mixed_cleanup(void) { ecs_world_t *world = ecs_mini(); ecs_entity_t rel = ecs_new_low_id(world); - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t child = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); + ecs_entity_t child = ecs_new(world); ecs_add_pair(world, child, EcsChildOf, parent); ecs_add_pair(world, child, rel, parent); @@ -3107,9 +3052,9 @@ void OnDelete_delete_w_low_rel_mixed_cleanup_interleaved_ids(void) { ecs_world_t *world = ecs_mini(); ecs_entity_t rel = ecs_new_low_id(world); - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t other = ecs_new_id(world); - ecs_entity_t child = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); + ecs_entity_t other = ecs_new(world); + ecs_entity_t child = ecs_new(world); ecs_add_pair(world, child, EcsChildOf, parent); ecs_add_pair(world, child, rel, other); ecs_add_pair(world, child, rel, parent); @@ -3127,19 +3072,21 @@ void OnDelete_fini_query_w_singleton_in_scope_no_module(void) { ECS_COMPONENT(world, Position); - ecs_entity_t t = ecs_new_entity(world, "ns.foo"); - ecs_entity_t s = ecs_new_entity(world, "ns.singleton"); + ecs_entity_t t = ecs_entity(world, { .name = "ns.foo" }); + ecs_entity_t s = ecs_entity(world, { .name = "ns.singleton" }); ecs_add_id(world, s, s); - ecs_query_t *q = ecs_query_new(world, "Position, ns.singleton($)"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, ns.singleton($)" }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, t); ecs_add(world, e, Position); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_iter_is_true(&it)); + ecs_query_fini(q); + ecs_fini(world); } @@ -3148,21 +3095,273 @@ void OnDelete_fini_query_w_singleton_in_module(void) { ECS_COMPONENT(world, Position); - ecs_entity_t n = ecs_new_entity(world, "ns"); + ecs_entity_t n = ecs_entity(world, { .name = "ns" }); ecs_add_id(world, n, EcsModule); - ecs_entity_t t = ecs_new_entity(world, "ns.foo"); - ecs_entity_t s = ecs_new_entity(world, "ns.singleton"); + ecs_entity_t t = ecs_entity(world, { .name = "ns.foo" }); + ecs_entity_t s = ecs_entity(world, { .name = "ns.singleton" }); ecs_add_id(world, s, s); - ecs_query_t *q = ecs_query_new(world, "Position, ns.singleton($)"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, ns.singleton($)" }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, t); ecs_add(world, e, Position); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_iter_is_true(&it)); + ecs_query_fini(q); + + ecs_fini(world); +} + +void OnDelete_fini_observer_w_relationship_in_scope(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t Tag = ecs_new(world); + + ecs_entity_t ns = ecs_entity(world, { .name = "ns" }); + ecs_entity_t Rel = ecs_component(world, { + .entity = ecs_entity(world, { .name = "Rel" }), + }); + + ecs_add_pair(world, Rel, EcsChildOf, ns); + + ecs_observer(world, { + .query.terms = { + { ecs_pair(Rel, EcsWildcard) }, + { Tag } + }, + .events = { EcsOnRemove }, + .callback = Observer + }); + + ecs_new_w_id(world, Tag); + + ecs_fini(world); + + // Tests edge case where above code would cause a crash + test_assert(true); +} + +void OnDelete_add_on_delete_from_prefab(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_insert(world, {EcsPrefab}); + ecs_add_pair(world, e, EcsOnDelete, EcsPanic); + + test_expect_abort(); + ecs_delete(world, e); +} + +void OnDelete_add_on_delete_from_disabled(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_insert(world, {EcsDisabled}); + ecs_add_pair(world, e, EcsOnDelete, EcsPanic); + + test_expect_abort(); + ecs_delete(world, e); +} + +void OnDelete_delete_on_delete_from_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsOnDelete, EcsPanic); + ecs_add_id(world, e, EcsPrefab); + ecs_remove_pair(world, e, EcsOnDelete, EcsPanic); + ecs_delete(world, e); + + test_assert(true); // shouldn't panic + + ecs_fini(world); +} + +void OnDelete_delete_on_delete_from_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsOnDelete, EcsPanic); + ecs_add_id(world, e, EcsDisabled); + ecs_remove_pair(world, e, EcsOnDelete, EcsPanic); + ecs_delete(world, e); + + test_assert(true); // shouldn't panic + + ecs_fini(world); +} + +void OnDelete_remove_all_1(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {10, 20}); + + ecs_remove_all(world, ecs_id(Position)); + + test_assert(ecs_is_alive(world, e1)); + + test_assert(!ecs_has(world, e1, Position)); + + test_assert(ecs_lookup(world, "e1") == e1); + + test_int(0, ecs_count(world, Position)); + + ecs_fini(world); +} + +void OnDelete_remove_all_2(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + + ecs_remove_all(world, ecs_id(Position)); + + test_assert(ecs_is_alive(world, e1)); + test_assert(ecs_is_alive(world, e2)); + + test_assert(!ecs_has(world, e1, Position)); + test_assert(!ecs_has(world, e2, Position)); + + test_assert(ecs_lookup(world, "e1") == e1); + test_assert(ecs_lookup(world, "e2") == e2); + + test_int(0, ecs_count(world, Position)); + + ecs_fini(world); +} + +void OnDelete_remove_all_3(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + ecs_set(world, e3, Position, {50, 60}); + + ecs_remove_all(world, ecs_id(Position)); + + test_assert(ecs_is_alive(world, e1)); + test_assert(ecs_is_alive(world, e2)); + test_assert(ecs_is_alive(world, e3)); + + test_assert(!ecs_has(world, e1, Position)); + test_assert(!ecs_has(world, e2, Position)); + test_assert(!ecs_has(world, e3, Position)); + + test_assert(ecs_lookup(world, "e1") == e1); + test_assert(ecs_lookup(world, "e2") == e2); + test_assert(ecs_lookup(world, "e3") == e3); + + test_int(0, ecs_count(world, Position)); + + ecs_fini(world); +} + +void OnDelete_delete_all_w_component_cycle(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + + ecs_entity_t comp_a = ecs_component(world, { + .type.size = 4, + .type.alignment = 4 + }); + ecs_add_id(world, comp_a, tag); + + ecs_entity_t comp_b = ecs_component(world, { + .type.size = 4, + .type.alignment = 4 + }); + ecs_add_id(world, comp_b, tag); + + ecs_add_id(world, comp_a, comp_b); + ecs_add_id(world, comp_b, comp_a); + + ecs_delete_with(world, tag); + + test_assert(!ecs_is_alive(world, comp_a)); + test_assert(!ecs_is_alive(world, comp_b)); + + test_assert(ecs_get_type_info(world, comp_a) == NULL); + test_assert(ecs_get_type_info(world, comp_b) == NULL); + + ecs_fini(world); +} + +void OnDelete_delete_with_1(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {10, 20}); + + ecs_delete_with(world, ecs_id(Position)); + + test_assert(!ecs_is_alive(world, e1)); + + test_int(0, ecs_count(world, Position)); + + ecs_fini(world); +} + +void OnDelete_delete_with_2(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + + ecs_delete_with(world, ecs_id(Position)); + + test_assert(!ecs_is_alive(world, e1)); + test_assert(!ecs_is_alive(world, e2)); + + test_int(0, ecs_count(world, Position)); + + ecs_fini(world); +} + +void OnDelete_delete_with_3(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + ecs_set(world, e3, Position, {50, 60}); + + ecs_delete_with(world, ecs_id(Position)); + + test_assert(!ecs_is_alive(world, e1)); + test_assert(!ecs_is_alive(world, e2)); + test_assert(!ecs_is_alive(world, e3)); + + test_int(0, ecs_count(world, Position)); + ecs_fini(world); } diff --git a/vendors/flecs/test/api/src/Pairs.c b/vendors/flecs/test/core/src/Pairs.c similarity index 80% rename from vendors/flecs/test/api/src/Pairs.c rename to vendors/flecs/test/core/src/Pairs.c index 225d43b0c..a157e190b 100644 --- a/vendors/flecs/test/api/src/Pairs.c +++ b/vendors/flecs/test/core/src/Pairs.c @@ -1,4 +1,4 @@ -#include +#include typedef struct Rel { float value; @@ -17,7 +17,7 @@ typedef struct Obj { } Obj; void ProcessPairs(ecs_iter_t *it) { - Rel *tr = ecs_field(it, Rel, 1); + Rel *tr = ecs_field(it, Rel, 0); probe_iter(it); @@ -39,16 +39,16 @@ void Pairs_type_w_one_pair(void) { ECS_SYSTEM(world, ProcessPairs, EcsOnUpdate, (Rel, *)); /* Ensure that pair is matched against different components */ - ecs_entity_t e1 = ecs_set_pair(world, 0, Rel, ecs_id(Position), { + ecs_entity_t e1 = ecs_insert(world, ecs_value_pair(Rel, ecs_id(Position), { .value = 10 - }); + })); test_assert(e1 != 0); test_assert( ecs_has_pair(world, e1, ecs_id(Rel), ecs_id(Position))); - ecs_entity_t e2 = ecs_set_pair(world, 0, Rel, ecs_id(Velocity), { + ecs_entity_t e2 = ecs_insert(world, ecs_value_pair(Rel, ecs_id(Velocity), { .value = 20 - }); + })); test_assert(e2 != 0); test_assert( ecs_has_pair(world, e2, ecs_id(Rel), ecs_id(Velocity))); @@ -102,7 +102,7 @@ void Pairs_type_w_two_pairs(void) { ECS_SYSTEM(world, ProcessPairs, EcsOnUpdate, (Rel, *)); /* Ensure that pair is matched against different components on same entity */ - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); ecs_set_pair(world, e1, Rel, ecs_id(Position), { .value = 10 }); @@ -113,7 +113,7 @@ void Pairs_type_w_two_pairs(void) { }); test_assert( ecs_has_pair(world, e1, ecs_id(Rel), ecs_id(Velocity))); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); ecs_set_pair(world, e2, Rel, ecs_id(Position), { .value = 30 }); @@ -183,7 +183,7 @@ void Pairs_add_pair(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Rel); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); ecs_add_pair(world, e1, ecs_id(Rel), ecs_id(Position)); @@ -199,9 +199,9 @@ void Pairs_remove_pair(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Rel); - ecs_entity_t e1 = ecs_set_pair(world, 0, Rel, ecs_id(Position), { + ecs_entity_t e1 = ecs_insert(world, ecs_value_pair(Rel, ecs_id(Position), { .value = 10 - }); + })); test_assert(e1 != 0); test_assert( ecs_has_pair(world, e1, ecs_id(Rel), ecs_id(Position))); @@ -225,12 +225,12 @@ void Pairs_add_tag_pair_for_tag(void) { ECS_SYSTEM(world, ProcessPairTags, EcsOnUpdate, (Rel, *)); /* Ensure that pair is matched against different components */ - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); ecs_add_pair(world, e1, Rel, Tag1); test_assert(e1 != 0); test_assert( ecs_has_pair(world, e1, Rel, Tag1)); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); ecs_add_pair(world, e2, Rel, Tag2); test_assert(e2 != 0); test_assert( ecs_has_pair(world, e2, Rel, Tag2)); @@ -271,7 +271,7 @@ void Pairs_add_tag_pair_for_tag(void) { void ProcessValuePairs(ecs_iter_t *it) { /* Strictly speaking this can be either Position or Velocity, but they have * the same layout. */ - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); probe_iter(it); @@ -293,16 +293,16 @@ void Pairs_add_tag_pair_for_component(void) { ECS_SYSTEM(world, ProcessValuePairs, EcsOnUpdate, (Rel, *)); - ecs_entity_t e1 = ecs_set_pair_object(world, 0, Rel, Position, { + ecs_entity_t e1 = ecs_insert(world, ecs_value_pair_2nd(Rel, Position, { .x = 1, .y = 2 - }); + })); test_assert( ecs_has_pair(world, e1, Rel, ecs_id(Position))); - ecs_entity_t e2 = ecs_set_pair_object(world, 0, Rel, Velocity, { + ecs_entity_t e2 = ecs_insert(world, ecs_value_pair_2nd(Rel, Velocity, { .x = 3, .y = 4 - }); + })); test_assert( ecs_has_pair(world, e2, Rel, ecs_id(Velocity))); Probe ctx = {0}; @@ -338,8 +338,8 @@ void Pairs_add_tag_pair_for_component(void) { } void ProcessTwoPairs(ecs_iter_t *it) { - RelA *tr_a = ecs_field(it, RelA, 1); - RelB *tr_b = ecs_field(it, RelB, 2); + RelA *tr_a = ecs_field(it, RelA, 0); + RelB *tr_b = ecs_field(it, RelB, 1); probe_iter(it); @@ -360,9 +360,9 @@ void Pairs_query_2_pairs(void) { ECS_SYSTEM(world, ProcessTwoPairs, EcsOnUpdate, (RelA, *), (RelB, *)); /* Create entity with both RelA and RelB */ - ecs_entity_t e1 = ecs_set_pair(world, 0, RelA, ecs_id(Position), { + ecs_entity_t e1 = ecs_insert(world, ecs_value_pair(RelA, ecs_id(Position), { .value = 1 - }); + })); ecs_set_pair(world, e1, RelB, ecs_id(Position), { .value = 2 @@ -372,9 +372,9 @@ void Pairs_query_2_pairs(void) { test_assert( ecs_has_pair(world, e1, ecs_id(RelB), ecs_id(Position))); /* Create entity with only RelA. Should not be matched with system */ - ecs_entity_t e2 = ecs_set_pair(world, 0, RelA, ecs_id(Position), { + ecs_entity_t e2 = ecs_insert(world, ecs_value_pair(RelA, ecs_id(Position), { .value = 3 - }); + })); test_assert( ecs_has_pair(world, e2, ecs_id(RelA), ecs_id(Position))); /* Run system */ @@ -425,9 +425,9 @@ void Pairs_query_2_pairs_2_instances_per_type(void) { ECS_SYSTEM(world, ProcessTwoPairs, EcsOnUpdate, (RelA, *), (RelB, *)); /* Create entity with both RelA and RelB, applied to two components*/ - ecs_entity_t e1 = ecs_set_pair(world, 0, RelA, ecs_id(Position), { + ecs_entity_t e1 = ecs_insert(world, ecs_value_pair(RelA, ecs_id(Position), { .value = 1 - }); + })); ecs_set_pair(world, e1, RelB, ecs_id(Position), { .value = 2 @@ -483,7 +483,10 @@ void Pairs_override_pair(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Rel); - ecs_entity_t base = ecs_new(world, 0); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Rel), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); ecs_set_pair(world, base, Rel, ecs_id(Position), {.value = 10}); ecs_entity_t instance = ecs_new_w_pair(world, EcsIsA, base); @@ -512,23 +515,26 @@ void Pairs_override_tag_pair(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Rel); - ecs_entity_t base = ecs_new(world, 0); - ecs_set_pair_object(world, base, Rel, Position, {.x = 10, .y = 20}); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_set_pair_second(world, base, Rel, Position, {.x = 10, .y = 20}); ecs_entity_t instance = ecs_new_w_pair(world, EcsIsA, base); test_assert(ecs_has_pair(world, instance, Rel, ecs_id(Position))); - const Position *t = ecs_get_pair_object(world, instance, Rel, Position); + const Position *t = ecs_get_pair_second(world, instance, Rel, Position); test_assert(t != NULL); test_int(t->x, 10); test_int(t->y, 20); - const Position *t_2 = ecs_get_pair_object(world, base, Rel, Position); + const Position *t_2 = ecs_get_pair_second(world, base, Rel, Position); test_assert(t_2 != NULL); test_assert(t == t_2); ecs_add_pair(world, instance, Rel, ecs_id(Position)); - t = ecs_get_pair_object(world, instance, Rel, Position); + t = ecs_get_pair_second(world, instance, Rel, Position); test_assert(t != NULL); test_int(t->x, 10); test_int(t->y, 20); @@ -554,7 +560,7 @@ void Pairs_on_add_pair(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, ecs_id(Rel), ecs_id(Position)); test_int(ctx.count, 1); @@ -600,7 +606,7 @@ void Pairs_on_add_pair_tag(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, ecs_id(Position)); test_int(ctx.count, 1); @@ -631,7 +637,7 @@ void Pairs_on_remove_pair(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, ecs_id(Rel), ecs_id(Position)); ecs_add_pair(world, e, ecs_id(Rel), ecs_id(Velocity)); test_int(ctx.count, 0); @@ -679,7 +685,7 @@ void Pairs_on_remove_pair_tag(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, ecs_id(Position)); test_int(ctx.count, 0); @@ -712,7 +718,7 @@ void Pairs_on_remove_pair_on_delete(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, ecs_id(Rel), ecs_id(Position)); ecs_add_pair(world, e, ecs_id(Rel), ecs_id(Velocity)); test_int(ctx.count, 0); @@ -765,7 +771,7 @@ void Pairs_on_remove_pair_tag_on_delete(void) { Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, ecs_id(Position)); ecs_add_pair(world, e, Rel, ecs_id(Velocity)); test_int(ctx.count, 0); @@ -801,15 +807,15 @@ void Pairs_pair_w_component_query(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Rel); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, ecs_id(Rel), ecs_id(Position)); - ecs_query_t *q = ecs_query_new(world, "(Rel, Position)"); + ecs_query_t *q = ecs_query(world, { .expr = "(Rel, Position)" }); int32_t count = 0; ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Rel *t = ecs_field(&it, Rel, 1); + Rel *t = ecs_field(&it, Rel, 0); test_assert(t != NULL); int i; @@ -821,6 +827,8 @@ void Pairs_pair_w_component_query(void) { test_assert(count == 1); + ecs_query_fini(q); + ecs_fini(world); } @@ -830,11 +838,11 @@ void Pairs_query_pair_or_component(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Rel); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); ecs_add_pair(world, e1, Rel, ecs_id(Position)); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); - ecs_query_t *q = ecs_query_new(world, "(Rel, Position) || Position"); + ecs_query_t *q = ecs_query(world, { .expr = "(Rel, Position) || Position" }); int32_t count = 0; ecs_iter_t it = ecs_query_iter(world, q); @@ -848,6 +856,8 @@ void Pairs_query_pair_or_component(void) { test_assert(count == 2); + ecs_query_fini(q); + ecs_fini(world); } @@ -858,12 +868,12 @@ void Pairs_query_pair_or_pair(void) { ECS_TAG(world, RelA); ECS_TAG(world, RelB); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); ecs_add_pair(world, e1, RelA, ecs_id(Position)); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); ecs_add_pair(world, e2, RelB, ecs_id(Position)); - ecs_query_t *q = ecs_query_new(world, "(RelA, Position) || (RelB, Position)"); + ecs_query_t *q = ecs_query(world, { .expr = "(RelA, Position) || (RelB, Position)" }); int32_t count = 0; ecs_iter_t it = ecs_query_iter(world, q); @@ -877,6 +887,8 @@ void Pairs_query_pair_or_pair(void) { test_int(count, 2); + ecs_query_fini(q); + ecs_fini(world); } @@ -886,18 +898,18 @@ void Pairs_query_not_pair(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Rel); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); ecs_add_pair(world, e2, Rel, ecs_id(Position)); - ecs_query_t *q = ecs_query_new(world, "!(Rel, Position), Position"); + ecs_query_t *q = ecs_query(world, { .expr = "!(Rel, Position), Position" }); int32_t count = 0; ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *t = ecs_field(&it, Position, 1); + Position *t = ecs_field(&it, Position, 0); test_assert(t == NULL); - Position *p = ecs_field(&it, Position, 2); + Position *p = ecs_field(&it, Position, 1); test_assert(p != NULL); int i; @@ -909,20 +921,22 @@ void Pairs_query_not_pair(void) { test_assert(count == 1); + ecs_query_fini(q); + ecs_fini(world); } void Pairs_get_typeid_w_recycled_rel(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t o = ecs_new(world); test_assert(o != 0); - ecs_entity_t dummy = ecs_new_id(world); + ecs_entity_t dummy = ecs_new(world); ecs_delete(world, dummy); // force recycle // don't use ECS_COMPONENT, because it will try to get low ids first - ecs_entity_t pos = ecs_set(world, 0, EcsComponent, {1, 1}); + ecs_entity_t pos = ecs_insert(world, ecs_value(EcsComponent, {1, 1})); test_assert(pos != 0); test_assert((uint32_t)pos != pos); // ensure recycled @@ -937,14 +951,14 @@ void Pairs_get_typeid_w_recycled_rel(void) { void Pairs_get_typeid_w_recycled_obj(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_new_id(world); + ecs_entity_t r = ecs_new(world); test_assert(r != 0); - ecs_entity_t dummy = ecs_new_id(world); + ecs_entity_t dummy = ecs_new(world); ecs_delete(world, dummy); // force recycle // don't use ECS_COMPONENT, because it will try to get low ids first - ecs_entity_t pos = ecs_set(world, 0, EcsComponent, {1, 1}); + ecs_entity_t pos = ecs_insert(world, ecs_value(EcsComponent, {1, 1})); test_assert(pos != 0); test_assert((uint32_t)pos != pos); // ensure recycled @@ -962,7 +976,7 @@ void Pairs_id_str_w_recycled_rel(void) { ecs_entity_t o = ecs_set_name(world, 0, "o"); test_assert(o != 0); - ecs_entity_t dummy = ecs_new_id(world); + ecs_entity_t dummy = ecs_new(world); ecs_delete(world, dummy); // force recycle ecs_entity_t r = ecs_set_name(world, 0, "r"); @@ -982,7 +996,7 @@ void Pairs_id_str_w_recycled_obj(void) { ecs_entity_t r = ecs_set_name(world, 0, "r"); test_assert(r != 0); - ecs_entity_t dummy = ecs_new_id(world); + ecs_entity_t dummy = ecs_new(world); ecs_delete(world, dummy); // force recycle ecs_entity_t o = ecs_set_name(world, 0, "o"); @@ -999,13 +1013,13 @@ void Pairs_id_str_w_recycled_obj(void) { void Pairs_set_object_w_zero_sized_rel_comp(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t r = ecs_set(world, 0, EcsComponent, {0}); + ecs_entity_t r = ecs_insert(world, ecs_value(EcsComponent, {0})); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_set_pair_object(world, 0, r, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value_pair_2nd(r, Position, {10, 20})); - const Position *p = ecs_get_pair_object(world, e, r, Position); + const Position *p = ecs_get_pair_second(world, e, r, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); @@ -1021,26 +1035,26 @@ void Pairs_dsl_pair(void) { ECS_TAG(world, Obj); ECS_TAG(world, Obj_2); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "(Rel, Obj)" + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, Obj)" }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2)) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1048,10 +1062,12 @@ void Pairs_dsl_pair(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e1); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1063,26 +1079,26 @@ void Pairs_dsl_pair_w_pred_wildcard(void) { ECS_TAG(world, Obj); ECS_TAG(world, Obj_2); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "(*, Obj)" + ecs_query_t *q = ecs_query(world, { + .expr = "(*, Obj)" }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2)) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1090,15 +1106,17 @@ void Pairs_dsl_pair_w_pred_wildcard(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e1); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj)); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1110,26 +1128,26 @@ void Pairs_dsl_pair_w_obj_wildcard(void) { ECS_TAG(world, Obj); ECS_TAG(world, Obj_2); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "(Rel_2, *)" + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel_2, *)" }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2)) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1137,14 +1155,16 @@ void Pairs_dsl_pair_w_obj_wildcard(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj_2)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj_2)); test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); ecs_fini(world); } @@ -1159,26 +1179,26 @@ void Pairs_dsl_pair_w_both_wildcard(void) { ECS_TAG(world, Tag); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "(*, *), Tag" // add Tag or we'd match builtin entities + ecs_query_t *q = ecs_query(world, { + .expr = "(*, *), Tag" // add Tag or we'd match builtin entities }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj), Tag} }); + .add = ecs_ids(ecs_pair(Rel, Obj), Tag) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2), Tag} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2), Tag) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj), Tag} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj), Tag) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2), Tag} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2), Tag) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1187,25 +1207,27 @@ void Pairs_dsl_pair_w_both_wildcard(void) { test_int(it.count, 1); test_int(it.entities[0], e1); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_2)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj_2)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj_2)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj_2)); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1217,26 +1239,26 @@ void Pairs_dsl_pair_w_explicit_subj_this(void) { ECS_TAG(world, Obj); ECS_TAG(world, Obj_2); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Rel($This, Obj)" + ecs_query_t *q = ecs_query(world, { + .expr = "Rel($this, Obj)" }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2)) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1244,9 +1266,11 @@ void Pairs_dsl_pair_w_explicit_subj_this(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e1); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); - test_bool(ecs_query_next(&it), false); + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); ecs_fini(world); } @@ -1262,29 +1286,29 @@ void Pairs_dsl_pair_w_explicit_subj(void) { ECS_TAG(world, Tag); ecs_entity_t Subj = ecs_entity_init(world, &(ecs_entity_desc_t){ - .name = "Subj", .add = {ecs_pair(Rel, Obj)} }); + .name = "Subj", .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(Subj != 0); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Rel(Subj, Obj), Tag" + ecs_query_t *q = ecs_query(world, { + .expr = "Rel(Subj, Obj), Tag" }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2), Tag} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2), Tag) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1292,11 +1316,13 @@ void Pairs_dsl_pair_w_explicit_subj(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); - test_int(ecs_field_src(&it, 1), Subj); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); + test_int(ecs_field_src(&it, 0), Subj); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1308,26 +1334,26 @@ void Pairs_api_pair(void) { ECS_TAG(world, Obj); ECS_TAG(world, Obj_2); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ecs_pair(Rel, Obj)}} + ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_pair(Rel, Obj)}} }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2)) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1335,10 +1361,12 @@ void Pairs_api_pair(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e1); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1350,26 +1378,26 @@ void Pairs_api_pair_w_pred_wildcard(void) { ECS_TAG(world, Obj); ECS_TAG(world, Obj_2); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ecs_pair(EcsWildcard, Obj)}} + ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_pair(EcsWildcard, Obj)}} }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2)) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1377,15 +1405,17 @@ void Pairs_api_pair_w_pred_wildcard(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e1); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj)); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1397,26 +1427,26 @@ void Pairs_api_pair_w_obj_wildcard(void) { ECS_TAG(world, Obj); ECS_TAG(world, Obj_2); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ecs_pair(Rel_2, EcsWildcard)}} + ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_pair(Rel_2, EcsWildcard)}} }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2)) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1424,14 +1454,16 @@ void Pairs_api_pair_w_obj_wildcard(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj_2)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj_2)); - test_bool(ecs_query_next(&it), false); + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); ecs_fini(world); } @@ -1446,27 +1478,27 @@ void Pairs_api_pair_w_both_wildcard(void) { ECS_TAG(world, Tag); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + ecs_query_t *q = ecs_query(world, { // add Tag or we'd match builtin entities - .filter.terms = {{ecs_pair(EcsWildcard, EcsWildcard)}, {Tag}} + .terms = {{ecs_pair(EcsWildcard, EcsWildcard)}, {Tag}} }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj), Tag} }); + .add = ecs_ids(ecs_pair(Rel, Obj), Tag) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2), Tag} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2), Tag) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj), Tag} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj), Tag) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2), Tag} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2), Tag) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1474,25 +1506,27 @@ void Pairs_api_pair_w_both_wildcard(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e1); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e2); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj_2)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj_2)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e3); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj)); test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel_2, Obj_2)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel_2, Obj_2)); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1504,8 +1538,8 @@ void Pairs_api_pair_w_explicit_subj_this(void) { ECS_TAG(world, Obj); ECS_TAG(world, Obj_2); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { + ecs_query_t *q = ecs_query(world, { + .terms = { { ecs_pair(Rel, Obj), .src.id = EcsThis } } }); @@ -1513,19 +1547,19 @@ void Pairs_api_pair_w_explicit_subj_this(void) { test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2)) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1533,10 +1567,12 @@ void Pairs_api_pair_w_explicit_subj_this(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e1); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -1551,29 +1587,29 @@ void Pairs_api_pair_w_explicit_subj(void) { ECS_TAG(world, Tag); ecs_entity_t Subj = ecs_entity_init(world, &(ecs_entity_desc_t){ - .name = "Subj", .add = {ecs_pair(Rel, Obj)} }); + .name = "Subj", .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(Subj != 0); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ecs_pair(Rel, Obj), .src.id = Subj}, {Tag}} + ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_pair(Rel, Obj), .src.id = Subj}, {Tag}} }); test_assert(q != NULL); ecs_entity_t e1 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} }); + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e1 != 0); ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj_2)} }); + .add = ecs_ids(ecs_pair(Rel, Obj_2)) }); test_assert(e2 != 0); ecs_entity_t e3 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj)} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj)) }); test_assert(e3 != 0); ecs_entity_t e4 = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel_2, Obj_2), Tag} }); + .add = ecs_ids(ecs_pair(Rel_2, Obj_2), Tag) }); test_assert(e4 != 0); ecs_iter_t it = ecs_query_iter(world, q); @@ -1581,18 +1617,20 @@ void Pairs_api_pair_w_explicit_subj(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); test_int(it.entities[0], e4); - test_int(ecs_field_id(&it, 1), ecs_pair(Rel, Obj)); - test_int(ecs_field_src(&it, 1), Subj); + test_int(ecs_field_id(&it, 0), ecs_pair(Rel, Obj)); + test_int(ecs_field_src(&it, 0), Subj); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } void Pairs_typeid_from_tag(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t tag = ecs_new_id(world); + ecs_entity_t tag = ecs_new(world); test_assert(tag != 0); ecs_entity_t id = ecs_get_typeid(world, tag); @@ -1618,10 +1656,10 @@ void Pairs_typeid_from_component(void) { void Pairs_typeid_from_pair(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t rel_id = ecs_new_id(world); + ecs_entity_t rel_id = ecs_new(world); test_assert(rel_id != 0); - ecs_entity_t obj_id = ecs_new_id(world); + ecs_entity_t obj_id = ecs_new(world); test_assert(obj_id != 0); ecs_id_t pair_id = ecs_pair(rel_id, obj_id); @@ -1639,7 +1677,7 @@ void Pairs_typeid_from_pair_w_rel_type(void) { ecs_id_t rel_id = ecs_id(Rel); test_assert(rel_id != 0); - ecs_entity_t obj_id = ecs_new_id(world); + ecs_entity_t obj_id = ecs_new(world); test_assert(obj_id != 0); ecs_id_t pair_id = ecs_pair(rel_id, obj_id); @@ -1658,7 +1696,7 @@ void Pairs_typeid_from_pair_w_obj_type(void) { ecs_id_t obj_id = ecs_id(Obj); test_assert(obj_id != 0); - ecs_entity_t rel_id = ecs_new_id(world); + ecs_entity_t rel_id = ecs_new(world); test_assert(rel_id != 0); ecs_id_t pair_id = ecs_pair(rel_id, obj_id); @@ -1693,7 +1731,7 @@ void Pairs_typeid_from_pair_w_rel_obj_type(void) { void Pairs_typeid_from_pair_w_rel_0_obj_type(void) { ecs_world_t *world = ecs_mini(); - ecs_id_t rel_id = ecs_set(world, 0, EcsComponent, {0}); + ecs_id_t rel_id = ecs_insert(world, ecs_value(EcsComponent, {0})); test_assert(rel_id != 0); ECS_COMPONENT(world, Obj); @@ -1716,7 +1754,7 @@ void Pairs_typeid_from_pair_w_rel_obj_0_type(void) { ecs_id_t rel_id = ecs_id(Rel); test_assert(rel_id != 0); - ecs_id_t obj_id = ecs_set(world, 0, EcsComponent, {0}); + ecs_id_t obj_id = ecs_insert(world, ecs_value(EcsComponent, {0})); test_assert(obj_id != 0); ecs_id_t pair_id = ecs_pair(rel_id, obj_id); @@ -1731,10 +1769,10 @@ void Pairs_typeid_from_pair_w_rel_obj_0_type(void) { void Pairs_typeid_from_pair_w_rel_0_obj_0_type(void) { ecs_world_t *world = ecs_mini(); - ecs_id_t rel_id = ecs_set(world, 0, EcsComponent, {0}); + ecs_id_t rel_id = ecs_insert(world, ecs_value(EcsComponent, {0})); test_assert(rel_id != 0); - ecs_id_t obj_id = ecs_set(world, 0, EcsComponent, {0}); + ecs_id_t obj_id = ecs_insert(world, ecs_value(EcsComponent, {0})); test_assert(obj_id != 0); ecs_id_t pair_id = ecs_pair(rel_id, obj_id); @@ -1763,10 +1801,10 @@ void Pairs_typeid_from_pair_w_override(void) { ecs_id_t rel_id = ecs_id(Rel); test_assert(rel_id != 0); - ecs_entity_t obj_id = ecs_new_id(world); + ecs_entity_t obj_id = ecs_new(world); test_assert(obj_id != 0); - ecs_id_t pair_id = ECS_OVERRIDE | ecs_pair(rel_id, obj_id); + ecs_id_t pair_id = ECS_AUTO_OVERRIDE | ecs_pair(rel_id, obj_id); ecs_entity_t id = ecs_get_typeid(world, pair_id); test_assert(id == 0); @@ -1781,7 +1819,7 @@ void Pairs_typeid_from_pair_w_toggle(void) { ecs_id_t rel_id = ecs_id(Rel); test_assert(rel_id != 0); - ecs_entity_t obj_id = ecs_new_id(world); + ecs_entity_t obj_id = ecs_new(world); test_assert(obj_id != 0); ecs_id_t pair_id = ECS_TOGGLE | ecs_pair(rel_id, obj_id); @@ -1798,9 +1836,9 @@ void Pairs_tag_pair_w_rel_comp(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Obj); - ecs_add_id(world, ecs_id(Position), EcsTag); + ecs_add_id(world, ecs_id(Position), EcsPairIsTag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_id_t pair_id = ecs_pair(ecs_id(Position), Obj); ecs_add_id(world, e, pair_id); @@ -1817,9 +1855,9 @@ void Pairs_tag_pair_w_obj_comp(void) { ECS_TAG(world, Rel); ECS_COMPONENT(world, Position); - ecs_add_id(world, Rel, EcsTag); + ecs_add_id(world, Rel, EcsPairIsTag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_id_t pair_id = ecs_pair(Rel, ecs_id(Position)); ecs_add_id(world, e, pair_id); @@ -1836,9 +1874,9 @@ void Pairs_tag_pair_w_rel_obj_comp(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_add_id(world, ecs_id(Position), EcsTag); + ecs_add_id(world, ecs_id(Position), EcsPairIsTag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_id_t pair_id = ecs_pair(ecs_id(Position), ecs_id(Velocity)); ecs_add_id(world, e, pair_id); @@ -1857,9 +1895,9 @@ void Pairs_get_tag_pair_w_rel_comp(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Obj); - ecs_add_id(world, ecs_id(Position), EcsTag); + ecs_add_id(world, ecs_id(Position), EcsPairIsTag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_id_t pair_id = ecs_pair(ecs_id(Position), Obj); ecs_add_id(world, e, pair_id); @@ -1876,9 +1914,9 @@ void Pairs_get_tag_pair_w_obj_comp(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_add_id(world, ecs_id(Position), EcsTag); + ecs_add_id(world, ecs_id(Position), EcsPairIsTag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_id_t pair_id = ecs_pair(ecs_id(Position), ecs_id(Velocity)); ecs_add_id(world, e, pair_id); @@ -1895,9 +1933,9 @@ void Pairs_get_tag_pair_w_rel_obj_comp(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_add_id(world, ecs_id(Position), EcsTag); + ecs_add_id(world, ecs_id(Position), EcsPairIsTag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_id_t pair_id = ecs_pair(ecs_id(Position), ecs_id(Velocity)); ecs_add_id(world, e, pair_id); @@ -1911,7 +1949,7 @@ void Pairs_tag_pair_w_childof_w_comp(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_id_t pair_id = ecs_pair(EcsChildOf, ecs_id(Position)); ecs_add_id(world, e, pair_id); @@ -1927,7 +1965,7 @@ void Pairs_tag_pair_w_isa_w_comp(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_id_t pair_id = ecs_pair(EcsIsA, ecs_id(Position)); ecs_add_id(world, e, pair_id); @@ -1941,9 +1979,9 @@ void Pairs_tag_pair_w_isa_w_comp(void) { void Pairs_get_1_target(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); + ecs_entity_t rel = ecs_new(world); + ecs_entity_t obj = ecs_new(world); ecs_add_pair(world, e, rel, obj); test_assert(ecs_has_pair(world, e, rel, obj)); @@ -1955,8 +1993,8 @@ void Pairs_get_1_target(void) { void Pairs_get_1_target_not_found(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t rel = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); + ecs_entity_t rel = ecs_new(world); test_assert(ecs_get_target(world, e, rel, 0) == 0); @@ -1966,11 +2004,11 @@ void Pairs_get_1_target_not_found(void) { void Pairs_get_n_targets(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj_1 = ecs_new_id(world); - ecs_entity_t obj_2 = ecs_new_id(world); - ecs_entity_t obj_3 = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); + ecs_entity_t rel = ecs_new(world); + ecs_entity_t obj_1 = ecs_new(world); + ecs_entity_t obj_2 = ecs_new(world); + ecs_entity_t obj_3 = ecs_new(world); ecs_add_pair(world, e, rel, obj_1); ecs_add_pair(world, e, rel, obj_2); @@ -1989,8 +2027,11 @@ void Pairs_get_n_targets(void) { void Pairs_get_target_from_base(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t tgt = ecs_new_id(world); + ecs_entity_t rel = ecs_new(world); + ecs_entity_t tgt = ecs_new(world); + + ecs_add_pair(world, rel, EcsOnInstantiate, EcsInherit); + ecs_entity_t base = ecs_new_w_pair(world, rel, tgt); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); @@ -2003,9 +2044,12 @@ void Pairs_get_target_from_base(void) { void Pairs_get_target_from_2nd_base(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t tgt = ecs_new_id(world); - ecs_entity_t base_1 = ecs_new_id(world); + ecs_entity_t rel = ecs_new(world); + ecs_entity_t tgt = ecs_new(world); + + ecs_add_pair(world, rel, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base_1 = ecs_new(world); ecs_entity_t base_2 = ecs_new_w_pair(world, rel, tgt); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_1); ecs_add_pair(world, inst, EcsIsA, base_2); @@ -2020,9 +2064,12 @@ void Pairs_get_target_from_2nd_base(void) { void Pairs_get_target_from_base_w_pair_on_instance(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t tgt_a = ecs_new_id(world); - ecs_entity_t tgt_b = ecs_new_id(world); + ecs_entity_t rel = ecs_new(world); + ecs_entity_t tgt_a = ecs_new(world); + ecs_entity_t tgt_b = ecs_new(world); + + ecs_add_pair(world, rel, EcsOnInstantiate, EcsInherit); + ecs_entity_t base = ecs_new_w_pair(world, rel, tgt_b); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); ecs_add_pair(world, inst, rel, tgt_a); @@ -2037,7 +2084,7 @@ void Pairs_get_target_from_base_w_pair_on_instance(void) { void Pairs_get_childof_target_from_base(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t base = ecs_new_w_pair(world, EcsChildOf, parent); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -2052,9 +2099,9 @@ void Pairs_get_childof_target_from_base(void) { void Pairs_get_dontinherit_target_from_base(void) { ecs_world_t *world = ecs_mini(); - ECS_ENTITY(world, Rel, DontInherit); + ECS_ENTITY(world, Rel, (OnInstantiate, DontInherit)); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t base = ecs_new_w_pair(world, Rel, parent); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -2069,7 +2116,7 @@ void Pairs_get_dontinherit_target_from_base(void) { void Pairs_get_parent(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent); test_assert(ecs_get_target(world, e, EcsChildOf, 0) == parent); @@ -2081,7 +2128,7 @@ void Pairs_get_parent(void) { void Pairs_get_parent_from_root(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(ecs_get_parent(world, e) == 0); @@ -2093,7 +2140,9 @@ void Pairs_get_target_for_id_from_self(void) { ECS_TAG(world, Tag); - ecs_entity_t base = ecs_new(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Tag); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_add_id(world, e, Tag); @@ -2109,7 +2158,9 @@ void Pairs_get_target_for_id_from_base(void) { ECS_TAG(world, Tag); - ecs_entity_t base = ecs_new(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Tag); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t result = ecs_get_target_for_id(world, e, EcsIsA, Tag); @@ -2124,7 +2175,9 @@ void Pairs_get_target_for_id_from_nested_base(void) { ECS_TAG(world, Tag); - ecs_entity_t base = ecs_new(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Tag); ecs_entity_t base_2 = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base_2); @@ -2140,7 +2193,9 @@ void Pairs_get_target_for_id_not_found(void) { ECS_TAG(world, Tag); - ecs_entity_t base = ecs_new_id(world); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); ecs_entity_t base_2 = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base_2); @@ -2155,6 +2210,8 @@ void Pairs_get_target_for_wildcard_from_self(void) { ECS_TAG(world, Rel); ECS_TAG(world, Obj); + + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); ecs_id_t pair = ecs_pair(Rel, Obj); ecs_entity_t base = ecs_new_w_id(world, pair); @@ -2175,6 +2232,8 @@ void Pairs_get_target_for_wildcard_from_base(void) { ECS_TAG(world, Rel); ECS_TAG(world, Obj); + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); + ecs_id_t pair = ecs_pair(Rel, Obj); ecs_entity_t base = ecs_new_w_id(world, pair); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -2193,6 +2252,8 @@ void Pairs_get_target_for_wildcard_from_nested_base(void) { ECS_TAG(world, Rel); ECS_TAG(world, Obj); + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); + ecs_id_t pair = ecs_pair(Rel, Obj); ecs_entity_t base = ecs_new_w_id(world, pair); ecs_entity_t base_2 = ecs_new_w_pair(world, EcsIsA, base); @@ -2212,8 +2273,10 @@ void Pairs_ignore_childof_from_base(void) { ECS_TAG(world, Rel); ECS_TAG(world, Obj); + + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t base = ecs_new_w_pair(world, EcsChildOf, parent); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -2229,7 +2292,9 @@ void Pairs_get_target_for_id_from_empty(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e = ecs_new(world); ecs_entity_t result = ecs_get_target_for_id(world, e, EcsIsA, Tag); test_assert(result == 0); @@ -2241,7 +2306,9 @@ void Pairs_get_target_for_id_from_empty_no_rel(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e = ecs_new(world); ecs_entity_t result = ecs_get_target_for_id(world, e, 0, Tag); test_assert(result == 0); @@ -2253,8 +2320,11 @@ void Pairs_get_target_for_id_not_empty_not_found(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); ecs_entity_t result = ecs_get_target_for_id(world, e, 0, TagB); test_assert(result == 0); @@ -2266,11 +2336,13 @@ void Pairs_get_target_for_id_from_stage(void) { ECS_TAG(world, Tag); - ecs_entity_t base = ecs_new(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Tag); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t result = ecs_get_target_for_id(stage, e, EcsIsA, Tag); test_assert(result != 0); test_assert(result == base); @@ -2284,7 +2356,9 @@ void Pairs_get_target_for_id_no_id(void) { ECS_TAG(world, Tag); - ecs_entity_t base = ecs_new(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Tag); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_add_id(world, e, Tag); @@ -2302,7 +2376,7 @@ void Pairs_add_exclusive_relation_twice(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, ObjA); test_assert( ecs_has_pair(world, e, Rel, ObjA)); @@ -2320,7 +2394,7 @@ void Pairs_add_same_exclusive_relation_twice(void) { ECS_ENTITY(world, Rel, Exclusive); ECS_TAG(world, ObjA); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, ObjA); test_assert( ecs_has_pair(world, e, Rel, ObjA)); @@ -2340,7 +2414,7 @@ void Pairs_set_exclusive_relation_twice(void) { ecs_add_id(world, ecs_id(Position), EcsExclusive); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_set_pair(world, e, Position, Start, {10, 20}); test_assert( ecs_has_pair(world, e, ecs_id(Position), Start)); @@ -2372,7 +2446,7 @@ void Pairs_add_exclusive_non_empty_table(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); ecs_add(world, e, TagC); @@ -2399,7 +2473,7 @@ void Pairs_add_exclusive_non_empty_table_w_pairs(void) { ECS_TAG(world, RelB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, RelA, ObjA); ecs_add_pair(world, e, RelB, ObjA); @@ -2421,7 +2495,7 @@ void Pairs_add_pair_to_entity_w_exclusive_pair(void) { ECS_TAG(world, RelA); ECS_TAG(world, RelB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsChildOf, ObjA); test_assert( ecs_has_pair(world, e, EcsChildOf, ObjA)); @@ -2444,7 +2518,7 @@ void Pairs_add_pair_to_entity_w_scope(void) { ecs_set_scope(world, ObjA); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, RelA, ObjA); test_assert( ecs_has_pair(world, e, RelA, ObjA)); @@ -2464,7 +2538,7 @@ void Pairs_add_existing_exclusive_pair_after_pair(void) { ECS_TAG(world, RelB); ECS_TAG(world, ObjA); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent); ecs_add_pair(world, e, EcsChildOf, ObjA); @@ -2493,10 +2567,10 @@ void Pairs_add_remove_exclusive_property(void) { ecs_entity_t rel = ecs_new_w_id(world, EcsExclusive); ecs_remove_id(world, rel, EcsExclusive); - ecs_entity_t tgt_1 = ecs_new_id(world); - ecs_entity_t tgt_2 = ecs_new_id(world); + ecs_entity_t tgt_1 = ecs_new(world); + ecs_entity_t tgt_2 = ecs_new(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, rel, tgt_1); test_assert( ecs_has_pair(world, e, rel, tgt_1)); @@ -2589,15 +2663,15 @@ void Pairs_add_symmetric_exclusive_relation(void) { void Pairs_add_symmetric_recycled_relation(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj_a = ecs_new_id(world); - ecs_entity_t obj_b = ecs_new_id(world); + ecs_entity_t rel = ecs_new(world); + ecs_entity_t obj_a = ecs_new(world); + ecs_entity_t obj_b = ecs_new(world); ecs_delete(world, rel); - rel = ecs_new_id(world); + rel = ecs_new(world); ecs_delete(world, obj_a); - obj_a = ecs_new_id(world); + obj_a = ecs_new(world); ecs_delete(world, obj_b); - obj_b = ecs_new_id(world); + obj_b = ecs_new(world); test_assert(rel != (uint32_t)rel); test_assert(obj_a != (uint32_t)obj_a); @@ -2613,13 +2687,31 @@ void Pairs_add_symmetric_recycled_relation(void) { ecs_fini(world); } +void Pairs_symmetric_w_childof(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t buddies = ecs_entity(world, { .name = "buddies" }); + ecs_add_id(world, buddies, EcsSymmetric); + + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + ecs_entity_t bob = ecs_entity(world, { .name = "bob", .parent = parent }); + ecs_entity_t alice = ecs_entity(world, { .name = "alice" }); + ecs_add_pair(world, alice, buddies, bob); + + test_assert(ecs_has_pair(world, bob, buddies, alice)); + + ecs_fini(world); + + test_assert(true); +} + void Pairs_with(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_ENTITY(world, TagB, (With, TagA)); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagB); test_assert( ecs_has(world, e, TagA)); test_assert( ecs_has(world, e, TagB)); @@ -2634,7 +2726,7 @@ void Pairs_2_with(void) { ECS_TAG(world, TagB); ECS_ENTITY(world, TagC, (With, TagA), (With, TagB)); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagC); test_assert( ecs_has(world, e, TagA)); test_assert( ecs_has(world, e, TagB)); @@ -2650,7 +2742,7 @@ void Pairs_nested_with(void) { ECS_ENTITY(world, TagB, (With, TagA)); ECS_ENTITY(world, TagC, (With, TagB)); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagC); test_assert( ecs_has(world, e, TagA)); test_assert( ecs_has(world, e, TagB)); @@ -2711,7 +2803,7 @@ void Pairs_with_for_existing(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagB); test_assert( !ecs_has(world, e, TagA)); test_assert( ecs_has(world, e, TagB)); @@ -2725,8 +2817,8 @@ void Pairs_65k_relations(void) { ecs_set_entity_range(world, 65536, 0); - ecs_entity_t rel = ecs_new_id(world); - ecs_entity_t obj = ecs_new_id(world); + ecs_entity_t rel = ecs_new(world); + ecs_entity_t obj = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, rel, obj); test_assert(ecs_has_pair(world, e, rel, obj)); @@ -2741,7 +2833,7 @@ void Pairs_remove_wildcard(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, RelA, ObjA); ecs_add_pair(world, e, RelA, ObjB); ecs_add_pair(world, e, RelB, ObjA); @@ -2770,7 +2862,7 @@ void Pairs_remove_relation_wildcard(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, RelA, ObjA); ecs_add_pair(world, e, RelA, ObjB); ecs_add_pair(world, e, RelB, ObjA); @@ -2794,7 +2886,7 @@ void Pairs_remove_relation_wildcard(void) { void Pairs_add_not_alive_relation(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t obj = ecs_new_id(world); + ecs_entity_t obj = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, 2000, obj); test_assert(ecs_has_pair(world, e, 2000, obj)); @@ -2809,7 +2901,7 @@ void Pairs_remove_wildcard_all(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, RelA, ObjA); ecs_add_pair(world, e, RelA, ObjB); ecs_add_pair(world, e, RelB, ObjA); @@ -2837,6 +2929,8 @@ void Pairs_inherit_exclusive(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); + ecs_entity_t base = ecs_new_w_pair(world, Rel, ObjA); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); @@ -2854,9 +2948,11 @@ void Pairs_dont_inherit(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); - ecs_add_id(world, TagA, EcsDontInherit); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + ecs_entity_t base = ecs_new_w_id(world, TagA); ecs_add(world, base, TagB); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); @@ -2892,7 +2988,7 @@ void Pairs_has_pair_wildcard_w_tag(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); test_bool(ecs_has_pair(world, e, EcsWildcard, EcsWildcard), false); test_bool(ecs_has_id(world, e, Tag), true); @@ -2908,7 +3004,7 @@ void Pairs_oneof_self(void) { ECS_ENTITY(world, ObjA, (ChildOf, Rel)); ECS_ENTITY(world, ObjB, (ChildOf, Rel)); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, ObjA); test_assert(ecs_has_pair(world, e, Rel, ObjA)); @@ -2926,7 +3022,7 @@ void Pairs_oneof_other(void) { ECS_ENTITY(world, ObjA, (ChildOf, Parent)); ECS_ENTITY(world, ObjB, (ChildOf, Parent)); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, ObjA); test_assert(ecs_has_pair(world, e, Rel, ObjA)); @@ -2946,7 +3042,7 @@ void Pairs_oneof_self_constraint_violated(void) { ECS_ENTITY(world, ObjB, (ChildOf, Rel)); ECS_TAG(world, ObjC); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_expect_abort(); ecs_add_pair(world, e, Rel, ObjC); @@ -2963,7 +3059,7 @@ void Pairs_oneof_other_constraint_violated(void) { ECS_ENTITY(world, ObjB, (ChildOf, Parent)); ECS_TAG(world, ObjC); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_expect_abort(); ecs_add_pair(world, e, Rel, ObjC); @@ -2980,7 +3076,7 @@ void Pairs_oneof_other_rel_parent_constraint_violated(void) { ECS_ENTITY(world, ObjB, (ChildOf, Parent)); ECS_ENTITY(world, ObjC, (ChildOf, Rel)); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_expect_abort(); ecs_add_pair(world, e, Rel, ObjC); @@ -2989,19 +3085,19 @@ void Pairs_oneof_other_rel_parent_constraint_violated(void) { void Pairs_set_w_recycled_rel(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t dummy = ecs_new_id(world); + ecs_entity_t dummy = ecs_new(world); ecs_delete(world, dummy); ecs_entity_t ecs_id(Position) = ecs_component(world, { - .entity = ecs_new_id(world), + .entity = ecs_new(world), .type.size = ECS_SIZEOF(Position), .type.alignment = ECS_ALIGNOF(Position) }); test_assert(ecs_id(Position) != (uint32_t)ecs_id(Position)); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t tgt = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); + ecs_entity_t tgt = ecs_new(world); ecs_set_pair(world, e, Position, tgt, {10, 20}); test_assert(ecs_has_pair(world, e, ecs_id(Position), tgt)); @@ -3016,19 +3112,19 @@ void Pairs_set_w_recycled_rel(void) { void Pairs_set_w_recycled_tgt(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t dummy = ecs_new_id(world); + ecs_entity_t dummy = ecs_new(world); ecs_delete(world, dummy); ecs_entity_t ecs_id(Position) = ecs_component(world, { - .entity = ecs_new_id(world), + .entity = ecs_new(world), .type.size = ECS_SIZEOF(Position), .type.alignment = ECS_ALIGNOF(Position) }); test_assert(ecs_id(Position) != (uint32_t)ecs_id(Position)); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t rel = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); + ecs_entity_t rel = ecs_new(world); ecs_set_pair_second(world, e, rel, Position, {10, 20}); test_assert(ecs_has_pair(world, e, rel, ecs_id(Position))); @@ -3039,3 +3135,86 @@ void Pairs_set_w_recycled_tgt(void) { ecs_fini(world); } + +void Pairs_force_relationship_on_component(void) { + install_test_abort(); + + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Rel, Relationship); + + test_expect_abort(); + ecs_new_w(world, Rel); +} + +void Pairs_force_relationship_on_target(void) { + install_test_abort(); + + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_ENTITY(world, Rel, Relationship); + + test_expect_abort(); + ecs_new_w_pair(world, RelA, Rel); +} + +void Pairs_force_relationship_on_target_trait(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Trait, Trait); + ECS_ENTITY(world, Rel, Relationship); + + ecs_entity_t e = ecs_new_w_pair(world, Trait, Rel); + test_assert(ecs_has_pair(world, e, Trait, Rel)); + + ecs_fini(world); +} + +void Pairs_force_relationship_on_relationship(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Rel, Relationship); + ECS_TAG(world, Tgt); + + ecs_entity_t e = ecs_new_w_pair(world, Rel, Tgt); + test_assert(ecs_has_pair(world, e, Rel, Tgt)); + + ecs_fini(world); +} + +void Pairs_force_target_on_component(void) { + install_test_abort(); + + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Tgt, Target); + + test_expect_abort(); + ecs_new_w(world, Tgt); +} + +void Pairs_force_target_on_relationship(void) { + install_test_abort(); + + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_ENTITY(world, Tgt, Target); + + test_expect_abort(); + ecs_new_w_pair(world, Tgt, RelA); +} + +void Pairs_force_target_on_target(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_ENTITY(world, Tgt, Target); + + ecs_entity_t e = ecs_new_w_pair(world, RelA, Tgt); + test_assert(ecs_has_pair(world, e, RelA, Tgt)); + + ecs_fini(world); +} + diff --git a/vendors/flecs/test/core/src/Poly.c b/vendors/flecs/test/core/src/Poly.c new file mode 100644 index 000000000..316dc8e74 --- /dev/null +++ b/vendors/flecs/test/core/src/Poly.c @@ -0,0 +1,119 @@ +#include + +static +void FooTrigger(ecs_iter_t *it) {} + +static +void PolyTrigger(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + EcsPoly *poly = ecs_field(it, EcsPoly, 0); + + test_int(1, it->count); + test_assert(poly->poly != NULL); +} + +void Poly_on_set_poly_observer(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms[0] = { ecs_pair(ecs_id(EcsPoly), EcsObserver) }, + .events = { EcsOnSet }, + .callback = PolyTrigger, + .ctx = &ctx + }); + + test_int(1, ctx.invoked); + + ecs_os_zeromem(&ctx); + + ecs_entity_t t = ecs_observer(world, { + .query.terms = {{ tag }}, + .events = { EcsOnAdd }, + .callback = FooTrigger + }); + + test_int(1, ctx.invoked); + test_int(1, ctx.count); + test_uint(t, ctx.e[0]); + + ecs_fini(world); +} + +void Poly_on_set_poly_query(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms[0] = { ecs_pair(ecs_id(EcsPoly), EcsQuery) }, + .events = { EcsOnSet }, + .callback = PolyTrigger, + .ctx = &ctx + }); + + test_int(0, ctx.invoked); + + ecs_query(world, { + .terms = {{ tag }}, + .entity = ecs_new(world) + }); + + test_int(1, ctx.invoked); + test_int(1, ctx.count); + + ecs_fini(world); +} + +void Poly_on_set_poly_system(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + + Probe ctx = {0}; + ecs_observer(world, { + .query.terms[0] = { ecs_pair(ecs_id(EcsPoly), EcsSystem) }, + .events = { EcsOnSet }, + .callback = PolyTrigger, + .ctx = &ctx + }); + + test_int(0, ctx.invoked); + + ecs_entity_t s = ecs_system(world, { + .query.terms = {{ tag }}, + .callback = FooTrigger + }); + + test_int(1, ctx.invoked); + test_int(1, ctx.count); + test_uint(s, ctx.e[0]); + + ecs_fini(world); +} + +void Poly_free_query_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t qe = ecs_new(world); + ecs_query_t *q = ecs_query(world, { + .terms = {{ Tag }}, + .entity = qe + }); + + const EcsPoly *poly = ecs_get_pair(world, qe, EcsPoly, EcsQuery); + test_assert(poly != NULL); + test_assert(poly->poly == q); + + ecs_delete(world, qe); + + test_assert(!ecs_is_alive(world, qe)); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/Prefab.c b/vendors/flecs/test/core/src/Prefab.c similarity index 70% rename from vendors/flecs/test/api/src/Prefab.c rename to vendors/flecs/test/core/src/Prefab.c index e43716daf..9f56319dc 100644 --- a/vendors/flecs/test/api/src/Prefab.c +++ b/vendors/flecs/test/core/src/Prefab.c @@ -1,4 +1,4 @@ -#include +#include static ecs_id_t NamePair; @@ -8,14 +8,14 @@ void Prefab_setup(void) { static void Iter(ecs_iter_t *it) { - Mass *m_ptr = ecs_field(it, Mass, 1); - bool shared = !ecs_field_is_self(it, 1); + Mass *m_ptr = ecs_field(it, Mass, 0); + bool shared = !ecs_field_is_self(it, 0); - Position *p = ecs_field(it, Position, 2); + Position *p = ecs_field(it, Position, 1); Velocity *v = NULL; if (it->field_count >= 3) { - v = ecs_field(it, Velocity, 3); + v = ecs_field(it, Velocity, 2); } probe_iter(it); @@ -45,7 +45,9 @@ void Prefab_new_w_prefab(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {10, 20}); @@ -106,7 +108,9 @@ void Prefab_new_w_count_prefab(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); @@ -136,8 +140,10 @@ void Prefab_new_w_type_w_prefab(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); - ECS_PREFAB(world, Prefab, Position, OVERRIDE | Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_PREFAB(world, Prefab, Position, auto_override | Velocity); ecs_set(world, Prefab, Position, {10, 20}); @@ -182,12 +188,14 @@ void Prefab_add_prefab(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {10, 20}); - ecs_entity_t e1 = ecs_new(world, Velocity); + ecs_entity_t e1 = ecs_new_w(world, Velocity); test_assert(e1 != 0); ecs_add_pair(world, e1, EcsIsA, Prefab); @@ -197,7 +205,7 @@ void Prefab_add_prefab(void) { test_assert( !ecs_has_id(world, e1, EcsPrefab)); test_assert( !ecs_has_id(world, e1, NamePair)); - ecs_entity_t e2 = ecs_new(world, Velocity); + ecs_entity_t e2 = ecs_new_w(world, Velocity); test_assert(e2 != 0); ecs_add_pair(world, e2, EcsIsA, Prefab); @@ -232,6 +240,7 @@ void Prefab_remove_prefab_after_new(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {10, 20}); @@ -260,11 +269,12 @@ void Prefab_remove_prefab_after_add(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {10, 20}); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); test_assert(e1 != 0); ecs_add_pair(world, e1, EcsIsA, Prefab); @@ -290,6 +300,7 @@ void Prefab_override_component(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {10, 20}); @@ -325,6 +336,7 @@ void Prefab_override_remove_component(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {10, 20}); @@ -368,9 +380,13 @@ void Prefab_override_2_of_3_components_1_self(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position, Velocity, Mass); ecs_set(world, Prefab, Position, {10, 20}); @@ -441,8 +457,10 @@ void Prefab_new_type_w_1_override(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); - ECS_PREFAB(world, Prefab, Position, Velocity, OVERRIDE | Position); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_PREFAB(world, Prefab, Position, Velocity, auto_override | Position); ecs_set(world, Prefab, Position, {10, 20}); ecs_set(world, Prefab, Velocity, {30, 40}); @@ -487,8 +505,10 @@ void Prefab_new_type_w_2_overrides(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); - ECS_PREFAB(world, Prefab, Position, Velocity, OVERRIDE | Position, OVERRIDE | Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_PREFAB(world, Prefab, Position, Velocity, auto_override | Position, auto_override | Velocity); ecs_set(world, Prefab, Position, {10, 20}); ecs_set(world, Prefab, Velocity, {30, 40}); @@ -533,6 +553,7 @@ void Prefab_get_ptr_prefab(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {10, 20}); @@ -549,20 +570,20 @@ static void Prefab_w_shared(ecs_iter_t *it) { Velocity *v = NULL; if (it->field_count >= 2) { - v = ecs_field(it, Velocity, 2); + v = ecs_field(it, Velocity, 1); if (v) { - test_assert(!ecs_field_is_self(it, 2)); + test_assert(!ecs_field_is_self(it, 1)); } } Mass *m = NULL; if (it->field_count >= 3) { - m = ecs_field(it, Mass, 3); + m = ecs_field(it, Mass, 2); } probe_iter(it); - Position *pos = ecs_field(it, Position, 1); + Position *pos = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i ++) { Position *p = &pos[i]; @@ -580,9 +601,13 @@ void Prefab_iterate_w_prefab_shared(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Velocity); - ECS_SYSTEM(world, Prefab_w_shared, EcsOnUpdate, Position, Velocity(up|self)); + ECS_SYSTEM(world, Prefab_w_shared, EcsOnUpdate, + Position, + Velocity(self|up IsA)); ecs_set(world, Prefab, Velocity, {1, 2}); @@ -619,14 +644,17 @@ void Prefab_match_entity_prefab_w_system_optional(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Velocity, Mass); ECS_SYSTEM(world, Prefab_w_shared, EcsOnUpdate, Position, - Velocity(self|up), - ?Mass(self|up)); + Velocity(self|up IsA), + ?Mass(self|up IsA)); ecs_set(world, Prefab, Velocity, {1, 2}); ecs_set(world, Prefab, Mass, {3}); @@ -666,15 +694,17 @@ void Prefab_prefab_in_system_expr(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab1, Velocity); ECS_PREFAB(world, Prefab2, Velocity); - ECS_SYSTEM(world, Prefab_w_shared, EcsOnUpdate, Position, Velocity(self|up), Mass, (IsA, Prefab1)); + ECS_SYSTEM(world, Prefab_w_shared, EcsOnUpdate, Position, Velocity(self|up IsA), Mass, (IsA, Prefab1)); ecs_system_init(world, &(ecs_system_desc_t){ - .entity = Prefab_w_shared, - .query.filter.instanced = true + .entity = Prefab_w_shared }); ecs_set(world, Prefab1, Velocity, {1, 2}); @@ -740,6 +770,7 @@ void Prefab_dont_match_prefab(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position); @@ -759,8 +790,10 @@ void Prefab_new_w_count_w_override(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); - ECS_PREFAB(world, Prefab, Position, Velocity, OVERRIDE | Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_PREFAB(world, Prefab, Position, Velocity, auto_override | Velocity); ecs_set(world, Prefab, Position, {10, 20}); ecs_set(world, Prefab, Velocity, {30, 40}); @@ -799,9 +832,12 @@ void Prefab_override_2_components_different_size(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Color); - ECS_PREFAB(world, Prefab, Position, Velocity, Color, OVERRIDE | Velocity, OVERRIDE | Color); + ecs_add_pair(world, ecs_id(Color), EcsOnInstantiate, EcsInherit); + ECS_PREFAB(world, Prefab, Position, Velocity, Color, auto_override | Velocity, auto_override | Color); ecs_set(world, Prefab, Position, {10, 20}); ecs_set(world, Prefab, Velocity, {30, 40}); @@ -853,7 +889,9 @@ void Prefab_ignore_prefab_parent_component(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, parent, Prefab, Position); ECS_ENTITY(world, child, Prefab, (ChildOf, parent), Velocity); @@ -867,8 +905,8 @@ void Prefab_ignore_prefab_parent_component(void) { static void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); int i; for (i = 0; i < it->count; i ++) { @@ -879,7 +917,7 @@ void Move(ecs_iter_t *it) { static void AddVelocity(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -891,8 +929,11 @@ void Prefab_match_table_created_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Mass); ecs_set(world, Prefab, Mass, {10}); @@ -900,7 +941,7 @@ void Prefab_match_table_created_in_progress(void) { ECS_ENTITY(world, e, (IsA, Prefab), Position); ECS_SYSTEM(world, AddVelocity, EcsOnUpdate, Position, [out] !Velocity); - ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity, Mass(self|up)); + ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity, Mass(self|up IsA)); ecs_set(world, e, Position, {0, 0}); @@ -931,6 +972,7 @@ void Prefab_prefab_w_1_child(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Parent, Position); ecs_set(world, Parent, Position, {1, 2}); @@ -981,6 +1023,7 @@ void Prefab_prefab_w_2_children(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, Parent, Prefab, Position); ecs_set(world, Parent, Position, {1, 2}); @@ -1027,7 +1070,9 @@ void Prefab_prefab_w_grandchild(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, Parent, Prefab, Position); ecs_set(world, Parent, Position, {1, 2}); @@ -1074,7 +1119,9 @@ void Prefab_prefab_tree_1_2_1(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, parent, Prefab, Position); ecs_set(world, parent, Position, {1, 2}); @@ -1138,7 +1185,9 @@ void Prefab_prefab_w_base_w_child(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, Base, Prefab, Velocity); ecs_set(world, Base, Velocity, {3, 4}); @@ -1182,7 +1231,9 @@ void Prefab_prefab_w_child_w_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, Base, Prefab, Velocity); ecs_set(world, Base, Velocity, {3, 4}); @@ -1225,7 +1276,9 @@ void Prefab_prefab_w_child_w_base_w_children(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, Base, Prefab, Velocity); ecs_set(world, Base, Velocity, {3, 4}); @@ -1283,6 +1336,7 @@ void Prefab_prefab_w_child_new_w_count(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, Parent, Prefab, Position); ecs_set(world, Parent, Position, {1, 2}); @@ -1321,7 +1375,9 @@ void Prefab_prefab_auto_override_child_component(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, Parent, Prefab, Position); ecs_set(world, Parent, Position, {1, 2}); @@ -1397,6 +1453,7 @@ void Prefab_ignore_on_add(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_OBSERVER(world, PrefabReactiveTest, EcsOnAdd, Position); @@ -1411,6 +1468,7 @@ void Prefab_ignore_on_remove(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_OBSERVER(world, PrefabReactiveTest, EcsOnRemove, Position); @@ -1429,6 +1487,7 @@ void Prefab_ignore_on_set(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_OBSERVER(world, PrefabReactiveTest, EcsOnSet, Position); @@ -1447,8 +1506,9 @@ void Prefab_on_set_on_instance(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ECS_OBSERVER(world, PrefabReactiveTest, EcsOnSet, Position(self|up)); + ECS_OBSERVER(world, PrefabReactiveTest, EcsOnSet, Position(self|up IsA)); ECS_PREFAB(world, Prefab, Position); @@ -1472,7 +1532,7 @@ void Prefab_on_set_on_instance(void) { } void InstantiateInProgress(ecs_iter_t *it) { - ecs_id_t Prefab = ecs_field_id(it, 2); + ecs_id_t Prefab = ecs_field_id(it, 1); ecs_entity_t *ids = ecs_get_ctx(it->world); int i; @@ -1485,7 +1545,9 @@ void Prefab_instantiate_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Velocity); ecs_set(world, Prefab, Velocity, {1, 2}); @@ -1514,7 +1576,7 @@ void Prefab_instantiate_in_progress(void) { } void NewInProgress(ecs_iter_t *it) { - ecs_id_t Prefab = ecs_field_id(it, 2); + ecs_id_t Prefab = ecs_field_id(it, 1); ecs_entity_t *ids = ecs_get_ctx(it->world); @@ -1528,9 +1590,11 @@ void Prefab_copy_from_prefab_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, Prefab, Velocity, OVERRIDE | Velocity); + ECS_PREFAB(world, Prefab, Velocity, auto_override | Velocity); ecs_set(world, Prefab, Velocity, {1, 2}); ECS_SYSTEM(world, NewInProgress, EcsOnUpdate, Position, Prefab()); @@ -1564,9 +1628,11 @@ void Prefab_copy_from_prefab_first_instance_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, Prefab, Velocity, OVERRIDE | Velocity); + ECS_PREFAB(world, Prefab, Velocity, auto_override | Velocity); ecs_set(world, Prefab, Velocity, {1, 2}); ECS_SYSTEM(world, NewInProgress, EcsOnUpdate, Position, Prefab()); @@ -1595,8 +1661,11 @@ void Prefab_ref_after_realloc(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Mass); ecs_set(world, Prefab, Mass, {2}); @@ -1606,7 +1675,7 @@ void Prefab_ref_after_realloc(void) { ECS_ENTITY(world, e2, Position, (IsA, Prefab)); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(self|up), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(self|up IsA), Position); /* Trigger a realloc of the table in which the prefab is stored. This should * cause systems with refs to re-resolve their cached ref ptrs */ @@ -1633,8 +1702,11 @@ void Prefab_revalidate_ref_w_mixed_table_refs(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Mass); ecs_set(world, Prefab, Mass, {1}); @@ -1643,7 +1715,7 @@ void Prefab_revalidate_ref_w_mixed_table_refs(void) { ECS_ENTITY(world, e2, Position, Mass); ecs_set(world, e2, Mass, {3}); - ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(self|up), Position); + ECS_SYSTEM(world, Iter, EcsOnUpdate, Mass(self|up IsA), Position); /* Trigger a realloc of the table in which the prefab is stored. This should * cause systems with refs to re-resolve their cached ref ptrs */ @@ -1671,8 +1743,11 @@ void Prefab_no_overwrite_on_2nd_add(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Mass); ecs_set(world, Prefab, Mass, {1}); @@ -1692,7 +1767,7 @@ void Prefab_no_overwrite_on_2nd_add(void) { } void AddPrefab(ecs_iter_t *it) { - ecs_id_t Prefab = ecs_field_id(it, 2); + ecs_id_t Prefab = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -1704,8 +1779,11 @@ void Prefab_no_overwrite_on_2nd_add_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Mass); ecs_set(world, Prefab, Mass, {1}); @@ -1730,8 +1808,11 @@ void Prefab_no_instantiate_on_2nd_add(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {1, 2}); @@ -1777,8 +1858,11 @@ void Prefab_no_instantiate_on_2nd_add_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {1, 2}); @@ -1822,7 +1906,7 @@ void Prefab_no_instantiate_on_2nd_add_in_progress(void) { void NewPrefab_w_count(ecs_iter_t *it) { ecs_entity_t *ids = ecs_get_ctx(it->world); - ecs_id_t Prefab = ecs_field_id(it, 1); + ecs_id_t Prefab = ecs_field_id(it, 0); const ecs_entity_t *new_ids = ecs_bulk_new_w_id(it->world, ecs_pair(EcsIsA, Prefab), 3); test_assert(new_ids != NULL); @@ -1833,8 +1917,11 @@ void Prefab_nested_prefab_in_progress_w_count(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {1, 2}); @@ -1874,8 +1961,8 @@ static int on_set_velocity_invoked; static void OnSetVelocity(ecs_iter_t *it) { - Velocity *v = ecs_field(it, Velocity, 1); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); + Velocity *v = ecs_field(it, Velocity, 0); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 0); on_set_velocity_invoked ++; @@ -1883,7 +1970,7 @@ void OnSetVelocity(ecs_iter_t *it) { for (i = 0; i < it->count; i ++) { ecs_add(it->world, it->entities[i], Velocity); - if (ecs_field_is_self(it, 1)) { + if (ecs_field_is_self(it, 0)) { v[i].x ++; v[i].y ++; } @@ -1894,8 +1981,11 @@ void Prefab_nested_prefab_in_progress_w_count_set_after_override(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {1, 2}); @@ -1938,8 +2028,8 @@ void Prefab_nested_prefab_in_progress_w_count_set_after_override(void) { } void AddPrefabInProgress(ecs_iter_t *it) { - ecs_id_t Prefab = ecs_field_id(it, 2); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 3); + ecs_id_t Prefab = ecs_field_id(it, 1); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); int i; for (i = 0; i < it->count; i ++) { @@ -1960,7 +2050,9 @@ void Prefab_get_ptr_from_prefab_from_new_table_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Velocity); ecs_set(world, Prefab, Velocity, {1, 2}); @@ -1984,10 +2076,10 @@ void Prefab_get_ptr_from_prefab_from_new_table_in_progress(void) { } void TestBase(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); - test_assert(!ecs_field_is_self(it, 2)); + test_assert(!ecs_field_is_self(it, 1)); test_assert(p != NULL); test_assert(v != NULL); @@ -1998,7 +2090,9 @@ void Prefab_match_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Base, Velocity); ecs_set(world, Base, Velocity, {1, 2}); @@ -2007,7 +2101,7 @@ void Prefab_match_base(void) { ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Child); ecs_add(world, e, Position); - ECS_SYSTEM(world, TestBase, EcsOnUpdate, Position, Velocity(self|up)); + ECS_SYSTEM(world, TestBase, EcsOnUpdate, Position, Velocity(self|up IsA)); ecs_progress(world, 1); @@ -2016,7 +2110,7 @@ void Prefab_match_base(void) { static void AddMass(ecs_iter_t *it) { - ecs_id_t ecs_id(Mass) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Mass) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -2028,8 +2122,11 @@ void Prefab_match_base_after_add_in_prev_phase(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Base, Velocity); ecs_set(world, Base, Velocity, {1, 2}); @@ -2039,7 +2136,7 @@ void Prefab_match_base_after_add_in_prev_phase(void) { ecs_add(world, e, Position); ECS_SYSTEM(world, AddMass, EcsPreUpdate, Position, !Mass); - ECS_SYSTEM(world, TestBase, EcsOnUpdate, Position, Velocity(self|up)); + ECS_SYSTEM(world, TestBase, EcsOnUpdate, Position, Velocity(self|up IsA)); ecs_progress(world, 1); @@ -2050,6 +2147,7 @@ void Prefab_override_watched_prefab(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); /* Create a system that listens for Position */ ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position(self|up)); @@ -2080,10 +2178,16 @@ void Prefab_rematch_twice(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); - ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position(self|up), Velocity(self|up), Mass(self|up)); + ECS_SYSTEM(world, Dummy, EcsOnUpdate, + Position(self|up IsA), + Velocity(self|up IsA), + Mass(self|up IsA)); ECS_PREFAB(world, Prefab, Position); ECS_ENTITY(world, Entity, (IsA, Prefab)); @@ -2111,7 +2215,7 @@ void Prefab_rematch_twice(void) { static void AddPosition(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); ecs_entity_t *base = ecs_get_ctx(it->world); @@ -2122,9 +2226,10 @@ void Prefab_add_to_empty_base_in_system(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, 0); - ecs_entity_t e1 = ecs_new(world, Position); + ecs_entity_t base = ecs_new(world); + ecs_entity_t e1 = ecs_new_w(world, Position); ecs_add_pair(world, e1, EcsIsA, base); ECS_SYSTEM(world, AddPosition, EcsOnUpdate, Position()); @@ -2142,10 +2247,12 @@ void Prefab_inherit_disabled(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Velocity); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Velocity); ecs_add_id(world, e2, EcsDisabled); ecs_add_pair(world, e1, EcsIsA, e2); @@ -2166,7 +2273,7 @@ static bool has_cloned = false; static void CloneInOnAdd(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { @@ -2190,9 +2297,10 @@ void Prefab_clone_after_inherit_in_on_add(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_OBSERVER(world, CloneInOnAdd, EcsOnAdd, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); const Position *p = ecs_get(world, e, Position); @@ -2208,12 +2316,14 @@ void Prefab_override_from_nested(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, BasePrefab, Position); ecs_set(world, BasePrefab, Position, {10, 20}); - ECS_PREFAB(world, SubPrefab, (IsA, BasePrefab), Velocity, OVERRIDE | Position, OVERRIDE | Velocity); + ECS_PREFAB(world, SubPrefab, (IsA, BasePrefab), Velocity, auto_override | Position, auto_override | Velocity); ecs_set(world, SubPrefab, Velocity, {30, 40}); ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, SubPrefab); @@ -2240,7 +2350,7 @@ void Prefab_override_from_nested(void) { static void OnAddEntity(ecs_iter_t *it) { - ecs_entity_t *e = ecs_field(it, ecs_entity_t, 1); + ecs_entity_t *e = ecs_field(it, ecs_entity_t, 0); int i; for (i = 0; i < it->count; i ++) { @@ -2252,9 +2362,13 @@ void Prefab_create_multiple_nested_w_on_set(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, ecs_entity_t); + ecs_add_pair(world, ecs_id(ecs_entity_t), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {1, 2}); @@ -2315,7 +2429,7 @@ static ecs_entity_t new_instance_1, new_instance_2; static void CreateInstances(ecs_iter_t *it) { - ecs_id_t Prefab = ecs_field_id(it, 1); + ecs_id_t Prefab = ecs_field_id(it, 0); new_instance_1 = ecs_new_w_pair(it->world, EcsIsA, Prefab); new_instance_2 = ecs_new_w_pair(it->world, EcsIsA, Prefab); @@ -2325,9 +2439,13 @@ void Prefab_create_multiple_nested_w_on_set_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, ecs_entity_t); + ecs_add_pair(world, ecs_id(ecs_entity_t), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {1, 2}); @@ -2339,7 +2457,7 @@ void Prefab_create_multiple_nested_w_on_set_in_progress(void) { ECS_SYSTEM(world, CreateInstances, EcsOnUpdate, Prefab()); ECS_OBSERVER(world, OnAddEntity, EcsOnSet, ecs_entity_t); - ECS_OBSERVER(world, OnSetVelocity, EcsOnSet, Velocity(self|up)); + ECS_OBSERVER(world, OnSetVelocity, EcsOnSet, Velocity(self|up IsA)); ecs_progress(world, 1); @@ -2390,9 +2508,13 @@ void Prefab_single_on_set_on_child_w_override(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, ecs_entity_t); + ecs_add_pair(world, ecs_id(ecs_entity_t), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {1, 2}); @@ -2434,9 +2556,11 @@ void Prefab_auto_override(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, Prefab, Position, Velocity, OVERRIDE | Position); + ECS_PREFAB(world, Prefab, Position, Velocity, auto_override | Position); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Prefab); test_assert(ecs_has(world, e, Position)); @@ -2451,9 +2575,11 @@ void Prefab_auto_override_2(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, Prefab, Position, Velocity, OVERRIDE | Position, OVERRIDE | Velocity); + ECS_PREFAB(world, Prefab, Position, Velocity, auto_override | Position, auto_override | Velocity); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Prefab); test_assert(ecs_has(world, e, Position)); @@ -2468,9 +2594,11 @@ void Prefab_auto_override_nested(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, Prefab, Position, Velocity, OVERRIDE | Position); + ECS_PREFAB(world, Prefab, Position, Velocity, auto_override | Position); ECS_PREFAB(world, Prefab_2, (IsA, Prefab)); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Prefab_2); @@ -2486,21 +2614,24 @@ void Prefab_auto_override_pair(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, TgtA); + ecs_add_pair(world, TgtA, EcsOnInstantiate, EcsInherit); ECS_TAG(world, TgtB); + ecs_add_pair(world, TgtB, EcsOnInstantiate, EcsInherit); ECS_TAG(world, Rel); + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new_id(world); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_pair(Rel, TgtA)); + ecs_entity_t base = ecs_new(world); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(Rel, TgtA)); test_assert(!ecs_has_pair(world, base, Rel, TgtA)); test_assert(!ecs_has_pair(world, base, Rel, TgtB)); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(Rel, TgtA))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(Rel, TgtA))); ecs_add_pair(world, base, Rel, TgtA); ecs_add_pair(world, base, Rel, TgtB); test_assert(ecs_has_pair(world, base, Rel, TgtA)); test_assert(ecs_has_pair(world, base, Rel, TgtB)); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(Rel, TgtA))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(Rel, TgtA))); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_assert(ecs_has_pair(world, inst, Rel, TgtA)); @@ -2516,12 +2647,15 @@ void Prefab_auto_override_pair_w_component(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, TgtA); + ecs_add_pair(world, TgtA, EcsOnInstantiate, EcsInherit); ECS_TAG(world, TgtB); + ecs_add_pair(world, TgtB, EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new_id(world); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_pair(ecs_id(Position), TgtA)); + ecs_entity_t base = ecs_new(world); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(ecs_id(Position), TgtA)); test_assert(ecs_get_pair(world, base, Position, TgtA) == NULL); ecs_set_pair(world, base, Position, TgtA, {10, 20}); @@ -2555,24 +2689,27 @@ void Prefab_auto_override_2_pairs(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, TgtA); + ecs_add_pair(world, TgtA, EcsOnInstantiate, EcsInherit); ECS_TAG(world, TgtB); + ecs_add_pair(world, TgtB, EcsOnInstantiate, EcsInherit); ECS_TAG(world, Rel); + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new_id(world); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_pair(Rel, TgtA)); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_pair(Rel, TgtB)); + ecs_entity_t base = ecs_new(world); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(Rel, TgtA)); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(Rel, TgtB)); test_assert(!ecs_has_pair(world, base, Rel, TgtA)); test_assert(!ecs_has_pair(world, base, Rel, TgtB)); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(Rel, TgtA))); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(Rel, TgtB))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(Rel, TgtA))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(Rel, TgtB))); ecs_add_pair(world, base, Rel, TgtA); ecs_add_pair(world, base, Rel, TgtB); test_assert(ecs_has_pair(world, base, Rel, TgtA)); test_assert(ecs_has_pair(world, base, Rel, TgtB)); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(Rel, TgtA))); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(Rel, TgtB))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(Rel, TgtA))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(Rel, TgtB))); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_assert(ecs_has_pair(world, inst, Rel, TgtA)); @@ -2588,13 +2725,16 @@ void Prefab_auto_override_2_pairs_w_component(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, TgtA); + ecs_add_pair(world, TgtA, EcsOnInstantiate, EcsInherit); ECS_TAG(world, TgtB); + ecs_add_pair(world, TgtB, EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new_id(world); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_pair(ecs_id(Position), TgtA)); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_pair(ecs_id(Position), TgtB)); + ecs_entity_t base = ecs_new(world); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(ecs_id(Position), TgtA)); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(ecs_id(Position), TgtB)); test_assert(ecs_get_pair(world, base, Position, TgtA) == NULL); test_assert(ecs_get_pair(world, base, Position, TgtB) == NULL); @@ -2631,24 +2771,27 @@ void Prefab_auto_override_2_pairs_same_obj(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tgt); + ecs_add_pair(world, Tgt, EcsOnInstantiate, EcsInherit); ECS_TAG(world, RelA); + ecs_add_pair(world, RelA, EcsOnInstantiate, EcsInherit); ECS_TAG(world, RelB); + ecs_add_pair(world, RelB, EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new_id(world); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_pair(RelA, Tgt)); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_pair(RelB, Tgt)); + ecs_entity_t base = ecs_new(world); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(RelA, Tgt)); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(RelB, Tgt)); test_assert(!ecs_has_pair(world, base, RelA, Tgt)); test_assert(!ecs_has_pair(world, base, RelB, Tgt)); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(RelA, Tgt))); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(RelB, Tgt))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(RelA, Tgt))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(RelB, Tgt))); ecs_add_pair(world, base, RelA, Tgt); ecs_add_pair(world, base, RelB, Tgt); test_assert(ecs_has_pair(world, base, RelA, Tgt)); test_assert(ecs_has_pair(world, base, RelB, Tgt)); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(RelA, Tgt))); - test_assert(ecs_has_id(world, base, ECS_OVERRIDE | ecs_pair(RelB, Tgt))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(RelA, Tgt))); + test_assert(ecs_has_id(world, base, ECS_AUTO_OVERRIDE | ecs_pair(RelB, Tgt))); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_assert(ecs_has_pair(world, inst, RelA, Tgt)); @@ -2664,7 +2807,9 @@ void Prefab_prefab_instanceof_hierarchy(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Base, Position); ECS_PREFAB(world, BaseChild, Position, (ChildOf, Base)); @@ -2673,7 +2818,7 @@ void Prefab_prefab_instanceof_hierarchy(void) { /* Ensure that child has not been instantiated by making * sure there are no matching entities for Position up to this point */ - ecs_query_t *q = ecs_query_new(world, "Position(self|up)"); + ecs_query_t *q = ecs_query(world, { .expr = "Position(self|up IsA)" }); ecs_iter_t qit = ecs_query_iter(world, q); test_assert(!ecs_query_next(&qit)); @@ -2692,6 +2837,8 @@ void Prefab_prefab_instanceof_hierarchy(void) { test_int(qit.count, 1); test_assert(ecs_query_next(&qit) == false); + ecs_query_fini(q); + ecs_fini(world); } @@ -2699,9 +2846,11 @@ void Prefab_override_tag(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, Tag); + ecs_entity_t base = ecs_new_w(world, Tag); ecs_set(world, base, Position, {10, 20}); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -2719,8 +2868,9 @@ void Prefab_empty_prefab(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_add(world, e, Position); @@ -2746,7 +2896,7 @@ void Prefab_instanceof_0(void) { void Prefab_instantiate_empty_child_table(void) { ecs_world_t *world = ecs_init(); - ECS_PREFAB(world, Prefab, 0); + ECS_PREFAB(world, Prefab, #0); /* Forces creation of child table without children */ ecs_table_t *table = ecs_table_add_id(world, 0, ecs_childof(Prefab)); @@ -2767,7 +2917,7 @@ void Prefab_instantiate_empty_child_table(void) { void Prefab_instantiate_emptied_child_table(void) { ecs_world_t *world = ecs_init(); - ECS_PREFAB(world, Prefab, 0); + ECS_PREFAB(world, Prefab, #0); /* Create & remove prefab child */ ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, Prefab); @@ -2793,7 +2943,9 @@ void Prefab_override_2_prefabs(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, PrefabA, Position); ECS_PREFAB(world, PrefabB, Velocity); @@ -2801,7 +2953,7 @@ void Prefab_override_2_prefabs(void) { ecs_set(world, PrefabA, Position, {10, 20}); ecs_set(world, PrefabB, Velocity, {1, 2}); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsIsA, PrefabA); ecs_add_pair(world, e, EcsIsA, PrefabB); @@ -2826,12 +2978,13 @@ void Prefab_rematch_after_add_instanceof_to_parent(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_query_t *q = ecs_query_new(world, "Position(parent)"); + ecs_query_t *q = ecs_query(world, { .expr = "Position(up)" }); test_assert(q != NULL); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t parent = ecs_new(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); test_assert(base != 0); test_assert(parent != 0); @@ -2844,12 +2997,14 @@ void Prefab_rematch_after_add_instanceof_to_parent(void) { test_int(it.count, 1); test_int(it.entities[0], child); - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -2859,7 +3014,7 @@ void Prefab_child_of_instance(void) { ecs_world_t *world = ecs_init(); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsChildOf, base); @@ -2875,17 +3030,18 @@ void Prefab_rematch_after_prefab_delete(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); - ecs_query_t *q = ecs_query_new(world, "Position(up)"); + ecs_query_t *q = ecs_query(world, { .expr = "Position(up IsA)" }); ecs_iter_t it = ecs_query_iter(world, q); test_assert(ecs_query_next(&it)); test_int(it.count, 1); test_int(it.entities[0], e); - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); @@ -2896,6 +3052,8 @@ void Prefab_rematch_after_prefab_delete(void) { it = ecs_query_iter(world, q); test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } @@ -2904,8 +3062,9 @@ void Prefab_add_tag_w_low_id_to_instance(void) { ecs_entity_t Tag = ecs_new_low_id(world); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_add_id(world, base, Tag); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -2921,8 +3080,9 @@ void Prefab_get_type_after_base_add(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, Position); + ecs_entity_t base = ecs_new_w(world, Position); test_assert(base != 0); test_assert( ecs_get_type(world, base) != NULL); @@ -2937,15 +3097,16 @@ void Prefab_get_type_after_recycled_base_add(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); test_assert( ecs_get_type(world, base) == NULL); ecs_delete(world, base); test_assert( !ecs_is_alive(world, base)); - base = ecs_new(world, Position); + base = ecs_new_w(world, Position); test_assert(base != 0); test_assert(ecs_entity_t_lo(base) != base); // Ensure recycled test_assert( ecs_get_type(world, base) != NULL); @@ -2960,13 +3121,13 @@ void Prefab_get_type_after_recycled_base_add(void) { void Prefab_new_w_recycled_base(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); ecs_delete(world, base); test_assert( !ecs_is_alive(world, base)); - base = ecs_new(world, 0); + base = ecs_new(world); test_assert(base != 0); test_assert(ecs_entity_t_lo(base) != base); // Ensure recycled @@ -2980,17 +3141,17 @@ void Prefab_new_w_recycled_base(void) { void Prefab_add_recycled_base(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); ecs_delete(world, base); test_assert( !ecs_is_alive(world, base)); - base = ecs_new(world, 0); + base = ecs_new(world); test_assert(base != 0); test_assert(ecs_entity_t_lo(base) != base); // Ensure recycled - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add_pair(world, e, EcsIsA, base); test_assert( ecs_has_pair(world, e, EcsIsA, base)); @@ -3001,17 +3162,17 @@ void Prefab_add_recycled_base(void) { void Prefab_remove_recycled_base(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); ecs_delete(world, base); test_assert( !ecs_is_alive(world, base)); - base = ecs_new(world, 0); + base = ecs_new(world); test_assert(base != 0); test_assert(ecs_entity_t_lo(base) != base); // Ensure recycled - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add_pair(world, e, EcsIsA, base); test_assert( ecs_has_pair(world, e, EcsIsA, base)); @@ -3026,14 +3187,15 @@ void Prefab_get_from_recycled_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); ecs_delete(world, base); test_assert( !ecs_is_alive(world, base)); - base = ecs_set(world, 0, Position, {10, 20}); + base = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(base != 0); test_assert(ecs_entity_t_lo(base) != base); // Ensure recycled test_assert( ecs_get_type(world, base) != NULL); @@ -3058,14 +3220,15 @@ void Prefab_override_from_recycled_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); ecs_delete(world, base); test_assert( !ecs_is_alive(world, base)); - base = ecs_set(world, 0, Position, {10, 20}); + base = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(base != 0); test_assert(ecs_entity_t_lo(base) != base); // Ensure recycled @@ -3091,14 +3254,15 @@ void Prefab_remove_override_from_recycled_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); ecs_delete(world, base); test_assert( !ecs_is_alive(world, base)); - base = ecs_set(world, 0, Position, {10, 20}); + base = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(base != 0); test_assert(ecs_entity_t_lo(base) != base); // Ensure recycled @@ -3123,6 +3287,7 @@ void Prefab_instantiate_tree_from_recycled_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); test_assert(base != 0); @@ -3130,7 +3295,7 @@ void Prefab_instantiate_tree_from_recycled_base(void) { ecs_delete(world, base); test_assert( !ecs_is_alive(world, base)); - base = ecs_set(world, 0, Position, {10, 20}); + base = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(base != 0); ecs_add_id(world, base, EcsPrefab); @@ -3163,17 +3328,19 @@ void Prefab_rematch_after_add_to_recycled_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); - ecs_query_t *q = ecs_query_new(world, "Tag, Position(up)"); + ecs_query_t *q = ecs_query(world, { .expr = "Tag, Position(up IsA)" }); - ecs_entity_t base = ecs_new(world, 0); + ecs_entity_t base = ecs_new(world); test_assert(base != 0); ecs_delete(world, base); test_assert( !ecs_is_alive(world, base)); - base = ecs_new(world, 0); + base = ecs_new(world); test_assert(base != 0); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -3192,13 +3359,15 @@ void Prefab_rematch_after_add_to_recycled_base(void) { test_bool(ecs_query_next(&it), true); test_int(it.count, 1); - const Position *p = ecs_field(&it, Position, 2); + const Position *p = ecs_field(&it, Position, 1); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); - test_assert(ecs_field_src(&it, 2) == base); + test_assert(ecs_field_src(&it, 1) == base); test_bool(ecs_query_next(&it), false); + ecs_query_fini(q); + ecs_fini(world); } @@ -3206,11 +3375,12 @@ void Prefab_get_tag_from_2nd_base(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); - ecs_entity_t base_1 = ecs_new_id(world); + ecs_entity_t base_1 = ecs_new(world); ecs_entity_t base_2 = ecs_new_w_id(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsIsA, base_1); ecs_add_pair(world, e, EcsIsA, base_2); @@ -3226,11 +3396,12 @@ void Prefab_get_component_from_2nd_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base_1 = ecs_new_id(world); - ecs_entity_t base_2 = ecs_new(world, Position); + ecs_entity_t base_1 = ecs_new(world); + ecs_entity_t base_2 = ecs_new_w(world, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsIsA, base_1); ecs_add_pair(world, e, EcsIsA, base_2); @@ -3249,11 +3420,12 @@ void Prefab_get_component_from_1st_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base_1 = ecs_new(world, Position); - ecs_entity_t base_2 = ecs_new(world, Position); + ecs_entity_t base_1 = ecs_new_w(world, Position); + ecs_entity_t base_2 = ecs_new_w(world, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsIsA, base_1); ecs_add_pair(world, e, EcsIsA, base_2); @@ -3273,11 +3445,12 @@ void Prefab_get_component_from_2nd_base_of_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base_1 = ecs_new_id(world); - ecs_entity_t base_2 = ecs_new(world, Position); + ecs_entity_t base_1 = ecs_new(world); + ecs_entity_t base_2 = ecs_new_w(world, Position); - ecs_entity_t base_3 = ecs_new_id(world); + ecs_entity_t base_3 = ecs_new(world); ecs_add_pair(world, base_3, EcsIsA, base_1); ecs_add_pair(world, base_3, EcsIsA, base_2); @@ -3299,11 +3472,12 @@ void Prefab_get_component_from_1st_base_of_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base_1 = ecs_new(world, Position); - ecs_entity_t base_2 = ecs_new(world, Position); + ecs_entity_t base_1 = ecs_new_w(world, Position); + ecs_entity_t base_2 = ecs_new_w(world, Position); - ecs_entity_t base_3 = ecs_new_id(world); + ecs_entity_t base_3 = ecs_new(world); ecs_add_pair(world, base_3, EcsIsA, base_1); ecs_add_pair(world, base_3, EcsIsA, base_2); @@ -3325,11 +3499,12 @@ void Prefab_get_component_from_2nd_base_prefab_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, base_1, 0); + ECS_PREFAB(world, base_1, #0); ECS_PREFAB(world, base_2, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsIsA, base_1); ecs_add_pair(world, e, EcsIsA, base_2); @@ -3349,11 +3524,12 @@ void Prefab_get_component_from_1st_base_prefab_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, base_1, Position); ECS_PREFAB(world, base_2, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsIsA, base_1); ecs_add_pair(world, e, EcsIsA, base_2); @@ -3373,8 +3549,9 @@ void Prefab_get_component_from_2nd_base_of_base_prefab_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, base_1, 0); + ECS_PREFAB(world, base_1, #0); ECS_PREFAB(world, base_2, Position); ECS_PREFAB(world, base_3, (IsA, base_1), (IsA, base_2)); @@ -3396,6 +3573,7 @@ void Prefab_get_component_from_1st_base_of_base_prefab_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, base_1, Position); ECS_PREFAB(world, base_2, Position); @@ -3429,25 +3607,24 @@ void Prefab_fail_on_override_final(void) { static int child_count(ecs_world_t *world, ecs_entity_t e) { int32_t count = 0; - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ - ecs_pair(EcsChildOf, e - )}); + ecs_iter_t it = ecs_each_pair(world, EcsChildOf, e); - while (ecs_term_next(&it)) { + while (ecs_each_next(&it)) { count += it.count; } + return count; } void Prefab_instantiate_tree_once(void) { ecs_world_t *world = ecs_init(); - ECS_PREFAB(world, Cannon, 0); - ECS_PREFAB(world, Turret, 0); + ECS_PREFAB(world, Cannon, #0); + ECS_PREFAB(world, Turret, #0); ECS_PREFAB(world, CannonA, (IsA, Cannon), (ChildOf, Turret)); ECS_PREFAB(world, CannonB, (IsA, Cannon), (ChildOf, Turret)); - ECS_PREFAB(world, SpaceShip, 0); + ECS_PREFAB(world, SpaceShip, #0); ECS_PREFAB(world, TurretA, (IsA, Turret), (ChildOf, SpaceShip)); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, SpaceShip); @@ -3464,14 +3641,14 @@ void Prefab_instantiate_tree_once(void) { void Prefab_nested_prefab_w_named_children(void) { ecs_world_t *world = ecs_init(); - ECS_PREFAB(world, Cannon, 0); + ECS_PREFAB(world, Cannon, #0); - ECS_PREFAB(world, Turret, 0); + ECS_PREFAB(world, Turret, #0); ecs_set_scope(world, Turret); ECS_ENTITY(world, CannonA, (IsA, Cannon)); ecs_set_scope(world, 0); - ECS_PREFAB(world, SpaceShip, 0); + ECS_PREFAB(world, SpaceShip, #0); ecs_set_scope(world, SpaceShip); ECS_PREFAB(world, TurretA, (IsA, Turret)); ecs_set_scope(world, 0); @@ -3491,6 +3668,7 @@ void Prefab_dont_copy_children_for_non_prefab_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_ENTITY(world, Base, Position); ECS_ENTITY(world, Child, (ChildOf, Base)); @@ -3508,9 +3686,11 @@ void Prefab_get_component_pair_from_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_TAG(world, Obj); + ecs_add_pair(world, Obj, EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_set_pair(world, 0, Position, Obj, {10, 20}); + ecs_entity_t base = ecs_insert(world, ecs_value_pair(Position, Obj, {10, 20})); test_assert(ecs_has_pair(world, base, ecs_id(Position), Obj)); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); @@ -3530,7 +3710,9 @@ void Prefab_get_component_pair_from_prefab_base(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_TAG(world, Obj); + ecs_add_pair(world, Obj, EcsOnInstantiate, EcsInherit); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_set_pair(world, base, Position, Obj, {10, 20}); @@ -3553,14 +3735,13 @@ void Prefab_override_dont_inherit(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - - ecs_add_id(world, ecs_id(Position), EcsDontInherit); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsDontInherit); ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor + .ctor = flecs_default_ctor }); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_assert( !ecs_has(world, inst, Position)); @@ -3573,31 +3754,15 @@ void Prefab_override_dont_inherit(void) { ecs_fini(world); } -void Prefab_prefab_w_switch(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Walking); - ECS_TAG(world, Running); - - ecs_entity_t p = ecs_new_w_pair(world, Movement, Running); - test_assert( ecs_has_pair(world, p, Movement, Running)); - - ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); - test_assert( ecs_has_pair(world, i, Movement, Running)); - test_assert( !ecs_has_pair(world, i, Movement, Walking)); - - ecs_fini(world); -} - void Prefab_prefab_child_w_dont_inherit_component(void) { ecs_world_t *world = ecs_mini(); - ECS_ENTITY(world, TagA, DontInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, DontInherit)); ECS_TAG(world, TagB); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_child = ecs_new_prefab(world, "Base.Child"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_child = ecs_entity(world, { .name = "Base.Child", .add = ecs_ids( EcsPrefab ) }); ecs_add(world, base_child, TagA); ecs_add(world, base_child, TagB); test_assert(base != 0); @@ -3618,18 +3783,20 @@ void Prefab_prefab_child_override(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Foo); + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); ECS_TAG(world, Bar); + ecs_add_pair(world, Bar, EcsOnInstantiate, EcsInherit); - ecs_entity_t turret = ecs_new_prefab(world, "Turret"); - ecs_entity_t turret_head = ecs_new_prefab(world, "Turret.Head"); + ecs_entity_t turret = ecs_entity(world, { .name = "Turret", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t turret_head = ecs_entity(world, { .name = "Turret.Head", .add = ecs_ids( EcsPrefab ) }); ecs_add(world, turret_head, Foo); - ecs_entity_t railgun = ecs_new_prefab(world, "Railgun"); + ecs_entity_t railgun = ecs_entity(world, { .name = "Railgun", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, railgun, EcsIsA, turret); - ecs_entity_t railgun_head = ecs_new_prefab(world, "Railgun.Head"); + ecs_entity_t railgun_head = ecs_entity(world, { .name = "Railgun.Head", .add = ecs_ids( EcsPrefab ) }); ecs_add(world, railgun_head, Bar); - ecs_entity_t inst = ecs_new_entity(world, "inst"); + ecs_entity_t inst = ecs_entity(world, { .name = "inst" }); ecs_add_pair(world, inst, EcsIsA, railgun); ecs_entity_t head = ecs_lookup_child(world, inst, "Head"); @@ -3643,23 +3810,25 @@ void Prefab_prefab_child_override(void) { void Prefab_prefab_child_override_w_exclusive_pair(void) { ecs_world_t *world = ecs_mini(); - ECS_ENTITY(world, Rel, Exclusive); + ECS_ENTITY(world, Rel, Exclusive, (OnInstantiate, Inherit)); ECS_TAG(world, ObjA); + ecs_add_pair(world, ObjA, EcsOnInstantiate, EcsInherit); ECS_TAG(world, ObjB); + ecs_add_pair(world, ObjB, EcsOnInstantiate, EcsInherit); - ecs_entity_t turret = ecs_new_prefab(world, "Turret"); - ecs_entity_t turret_head = ecs_new_prefab(world, "Turret.Head"); + ecs_entity_t turret = ecs_entity(world, { .name = "Turret", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t turret_head = ecs_entity(world, { .name = "Turret.Head", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, turret_head, Rel, ObjA); test_assert(ecs_has_pair(world, turret_head, Rel, ObjA)); - ecs_entity_t railgun = ecs_new_prefab(world, "Railgun"); + ecs_entity_t railgun = ecs_entity(world, { .name = "Railgun", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, railgun, EcsIsA, turret); - ecs_entity_t railgun_head = ecs_new_prefab(world, "Railgun.Head"); + ecs_entity_t railgun_head = ecs_entity(world, { .name = "Railgun.Head", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, railgun_head, Rel, ObjB); test_assert(!ecs_has_pair(world, railgun_head, Rel, ObjA)); test_assert(ecs_has_pair(world, railgun_head, Rel, ObjB)); - ecs_entity_t inst = ecs_new_entity(world, "inst"); + ecs_entity_t inst = ecs_entity(world, { .name = "inst" }); ecs_add_pair(world, inst, EcsIsA, railgun); ecs_entity_t head = ecs_lookup_child(world, inst, "Head"); @@ -3673,8 +3842,8 @@ void Prefab_prefab_child_override_w_exclusive_pair(void) { void Prefab_prefab_1_slot(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_slot = ecs_new_prefab(world, "Base.Slot"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot = ecs_entity(world, { .name = "Base.Slot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, base_slot, EcsSlotOf, base); test_assert(ecs_has_pair(world, base_slot, EcsChildOf, base)); test_assert(ecs_has_pair(world, base_slot, EcsSlotOf, base)); @@ -3694,9 +3863,9 @@ void Prefab_prefab_1_slot(void) { void Prefab_prefab_2_slots(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_slot_a = ecs_new_prefab(world, "Base.SlotA"); - ecs_entity_t base_slot_b = ecs_new_prefab(world, "Base.SlotB"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot_a = ecs_entity(world, { .name = "Base.SlotA", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot_b = ecs_entity(world, { .name = "Base.SlotB", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, base_slot_a, EcsSlotOf, base); ecs_add_pair(world, base_slot_b, EcsSlotOf, base); test_assert(ecs_has_pair(world, base_slot_a, EcsChildOf, base)); @@ -3725,9 +3894,9 @@ void Prefab_prefab_2_slots(void) { void Prefab_prefab_w_nested_slot(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_slot = ecs_new_prefab(world, "Base.Slot"); - ecs_entity_t base_slot_slot = ecs_new_prefab(world, "Base.Slot.Slot"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot = ecs_entity(world, { .name = "Base.Slot", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot_slot = ecs_entity(world, { .name = "Base.Slot.Slot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, base_slot, EcsSlotOf, base); ecs_add_pair(world, base_slot_slot, EcsSlotOf, base); test_assert(ecs_has_pair(world, base_slot, EcsChildOf, base)); @@ -3757,9 +3926,9 @@ void Prefab_prefab_w_nested_slot(void) { void Prefab_prefab_w_mixed_slots(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_slot = ecs_new_prefab(world, "Base.Slot"); - ecs_entity_t base_slot_slot = ecs_new_prefab(world, "Base.Slot.Slot"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot = ecs_entity(world, { .name = "Base.Slot", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot_slot = ecs_entity(world, { .name = "Base.Slot.Slot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, base_slot, EcsSlotOf, base); ecs_add_pair(world, base_slot_slot, EcsSlotOf, base_slot); test_assert(ecs_has_pair(world, base_slot, EcsChildOf, base)); @@ -3792,9 +3961,9 @@ void Prefab_prefab_w_mixed_slots(void) { void Prefab_prefab_variant_w_slot(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t variant = ecs_new_prefab(world, "Variant"); - ecs_entity_t variant_slot = ecs_new_prefab(world, "Variant.Slot"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t variant = ecs_entity(world, { .name = "Variant", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t variant_slot = ecs_entity(world, { .name = "Variant.Slot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, variant, EcsIsA, base); ecs_add_pair(world, variant_slot, EcsSlotOf, variant); test_assert(ecs_has_pair(world, variant_slot, EcsChildOf, variant)); @@ -3815,13 +3984,13 @@ void Prefab_prefab_variant_w_slot(void) { void Prefab_prefab_variant_w_base_slot(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_slot = ecs_new_prefab(world, "Base.Slot"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot = ecs_entity(world, { .name = "Base.Slot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, base_slot, EcsSlotOf, base); test_assert(ecs_has_pair(world, base_slot, EcsChildOf, base)); test_assert(ecs_has_pair(world, base_slot, EcsSlotOf, base)); - ecs_entity_t variant = ecs_new_prefab(world, "Variant"); + ecs_entity_t variant = ecs_entity(world, { .name = "Variant", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, variant, EcsIsA, base); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, variant); @@ -3839,15 +4008,15 @@ void Prefab_prefab_variant_w_base_slot(void) { void Prefab_prefab_variant_w_mixed_slots(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_slot = ecs_new_prefab(world, "Base.BaseSlot"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot = ecs_entity(world, { .name = "Base.BaseSlot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, base_slot, EcsSlotOf, base); test_assert(ecs_has_pair(world, base_slot, EcsChildOf, base)); test_assert(ecs_has_pair(world, base_slot, EcsSlotOf, base)); - ecs_entity_t variant = ecs_new_prefab(world, "Variant"); + ecs_entity_t variant = ecs_entity(world, { .name = "Variant", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, variant, EcsIsA, base); - ecs_entity_t variant_slot = ecs_new_prefab(world, "Variant.VariantSlot"); + ecs_entity_t variant_slot = ecs_entity(world, { .name = "Variant.VariantSlot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, variant, EcsIsA, base); ecs_add_pair(world, variant_slot, EcsSlotOf, variant); test_assert(ecs_has_pair(world, variant_slot, EcsChildOf, variant)); @@ -3875,8 +4044,8 @@ void Prefab_prefab_variant_w_mixed_slots(void) { void Prefab_override_slot(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_slot = ecs_new_prefab(world, "Base.Slot"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot = ecs_entity(world, { .name = "Base.Slot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, base_slot, EcsSlotOf, base); test_assert(ecs_has_pair(world, base_slot, EcsChildOf, base)); test_assert(ecs_has_pair(world, base_slot, EcsSlotOf, base)); @@ -3890,7 +4059,7 @@ void Prefab_override_slot(void) { test_assert(!ecs_has_pair(world, inst_slot, EcsSlotOf, EcsWildcard)); test_assert(ecs_has_pair(world, inst_slot, EcsChildOf, inst)); - ecs_entity_t slot_override = ecs_new_id(world); + ecs_entity_t slot_override = ecs_new(world); ecs_add_pair(world, inst, base_slot, slot_override); test_assert(ecs_has_pair(world, inst, base_slot, slot_override)); @@ -3902,8 +4071,8 @@ void Prefab_override_slot(void) { void Prefab_2_instances_w_slots_same_table(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_slot = ecs_new_prefab(world, "Base.Slot"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot = ecs_entity(world, { .name = "Base.Slot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, base_slot, EcsSlotOf, base); test_assert(ecs_has_pair(world, base_slot, EcsChildOf, base)); test_assert(ecs_has_pair(world, base_slot, EcsSlotOf, base)); @@ -3928,8 +4097,8 @@ void Prefab_2_instances_w_slots_same_table(void) { void Prefab_slot_has_union(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_slot = ecs_new_prefab(world, "Base.Slot"); + ecs_entity_t base = ecs_entity(world, { .name = "Base", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t base_slot = ecs_entity(world, { .name = "Base.Slot", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, base_slot, EcsSlotOf, base); test_assert(ecs_has_pair(world, base_slot, EcsChildOf, base)); test_assert(ecs_has_pair(world, base_slot, EcsSlotOf, base)); @@ -3950,25 +4119,25 @@ void Prefab_slot_has_union(void) { void Prefab_slot_override(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t turret = ecs_new_prefab(world, "Turret"); - ecs_entity_t turret_base = ecs_new_prefab(world, "Turret.Base"); + ecs_entity_t turret = ecs_entity(world, { .name = "Turret", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t turret_base = ecs_entity(world, { .name = "Turret.Base", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, turret_base, EcsSlotOf, turret); - ecs_entity_t turret_head = ecs_new_prefab(world, "Turret.Head"); + ecs_entity_t turret_head = ecs_entity(world, { .name = "Turret.Head", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, turret_head, EcsSlotOf, turret); - ecs_entity_t railgun = ecs_new_prefab(world, "Railgun"); + ecs_entity_t railgun = ecs_entity(world, { .name = "Railgun", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, railgun, EcsIsA, turret); - ecs_entity_t railgun_head = ecs_new_prefab(world, "Railgun.Head"); + ecs_entity_t railgun_head = ecs_entity(world, { .name = "Railgun.Head", .add = ecs_ids( EcsPrefab ) }); test_assert(ecs_has_pair(world, railgun_head, EcsSlotOf, turret)); ecs_add_pair(world, railgun_head, EcsSlotOf, railgun); test_assert(ecs_has_pair(world, railgun_head, EcsSlotOf, railgun)); test_assert(!ecs_has_pair(world, railgun_head, EcsSlotOf, turret)); - ecs_entity_t railgun_beam = ecs_new_prefab(world, "Railgun.Beam"); + ecs_entity_t railgun_beam = ecs_entity(world, { .name = "Railgun.Beam", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, railgun_beam, EcsSlotOf, railgun); - ecs_entity_t inst = ecs_new_entity(world, "inst"); + ecs_entity_t inst = ecs_entity(world, { .name = "inst" }); ecs_add_pair(world, inst, EcsIsA, railgun); ecs_entity_t head = ecs_get_target(world, inst, turret_head, 0); @@ -3981,15 +4150,15 @@ void Prefab_slot_override(void) { test_assert(base != 0); test_assert(beam != 0); - char *path = ecs_get_fullpath(world, head_r); + char *path = ecs_get_path(world, head_r); test_str(path, "inst.Head"); ecs_os_free(path); - path = ecs_get_fullpath(world, base); + path = ecs_get_path(world, base); test_str(path, "inst.Base"); ecs_os_free(path); - path = ecs_get_fullpath(world, beam); + path = ecs_get_path(world, beam); test_str(path, "inst.Beam"); ecs_os_free(path); @@ -3999,21 +4168,21 @@ void Prefab_slot_override(void) { void Prefab_base_slot_override(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t turret = ecs_new_prefab(world, "Turret"); - ecs_entity_t turret_base = ecs_new_prefab(world, "Turret.Base"); + ecs_entity_t turret = ecs_entity(world, { .name = "Turret", .add = ecs_ids( EcsPrefab ) }); + ecs_entity_t turret_base = ecs_entity(world, { .name = "Turret.Base", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, turret_base, EcsSlotOf, turret); - ecs_entity_t turret_head = ecs_new_prefab(world, "Turret.Head"); + ecs_entity_t turret_head = ecs_entity(world, { .name = "Turret.Head", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, turret_head, EcsSlotOf, turret); - ecs_entity_t railgun = ecs_new_prefab(world, "Railgun"); + ecs_entity_t railgun = ecs_entity(world, { .name = "Railgun", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, railgun, EcsIsA, turret); - ecs_entity_t railgun_head = ecs_new_prefab(world, "Railgun.Head"); + ecs_entity_t railgun_head = ecs_entity(world, { .name = "Railgun.Head", .add = ecs_ids( EcsPrefab ) }); test_assert(ecs_has_pair(world, railgun_head, EcsSlotOf, turret)); - ecs_entity_t railgun_beam = ecs_new_prefab(world, "Railgun.Beam"); + ecs_entity_t railgun_beam = ecs_entity(world, { .name = "Railgun.Beam", .add = ecs_ids( EcsPrefab ) }); ecs_add_pair(world, railgun_beam, EcsSlotOf, railgun); - ecs_entity_t inst = ecs_new_entity(world, "inst"); + ecs_entity_t inst = ecs_entity(world, { .name = "inst" }); ecs_add_pair(world, inst, EcsIsA, railgun); ecs_entity_t head = ecs_get_target(world, inst, turret_head, 0); @@ -4026,62 +4195,30 @@ void Prefab_base_slot_override(void) { test_assert(base != 0); test_assert(beam != 0); - char *path = ecs_get_fullpath(world, head); + char *path = ecs_get_path(world, head); test_str(path, "inst.Head"); ecs_os_free(path); - path = ecs_get_fullpath(world, base); + path = ecs_get_path(world, base); test_str(path, "inst.Base"); ecs_os_free(path); - path = ecs_get_fullpath(world, beam); + path = ecs_get_path(world, beam); test_str(path, "inst.Beam"); ecs_os_free(path); ecs_fini(world); } -void Prefab_prefab_child_w_union(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Rel, Union); - ECS_TAG(world, TgtA); - ECS_TAG(world, TgtB); - ECS_TAG(world, TgtC); - - ecs_entity_t base = ecs_new_prefab(world, "Base"); - ecs_entity_t base_child_a = ecs_new_prefab(world, "Base.ChildA"); - ecs_entity_t base_child_b = ecs_new_prefab(world, "Base.ChildB"); - ecs_entity_t base_child_c = ecs_new_prefab(world, "Base.ChildC"); - ecs_add_pair(world, base_child_a, Rel, TgtA); - ecs_add_pair(world, base_child_b, Rel, TgtB); - ecs_add_pair(world, base_child_c, Rel, TgtC); - - ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); - test_assert(inst != 0); - - ecs_entity_t child_a = ecs_lookup_child(world, inst, "ChildA"); - ecs_entity_t child_b = ecs_lookup_child(world, inst, "ChildB"); - ecs_entity_t child_c = ecs_lookup_child(world, inst, "ChildC"); - test_assert(child_a != 0); - test_assert(child_b != 0); - test_assert(child_c != 0); - - test_assert(ecs_has_pair(world, child_a, Rel, TgtA)); - test_assert(ecs_has_pair(world, child_b, Rel, TgtB)); - test_assert(ecs_has_pair(world, child_c, Rel, TgtC)); - - ecs_fini(world); -} - void Prefab_override_twice_w_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new_id(world); + ecs_entity_t base = ecs_new(world); ecs_set(world, base, Position, {10}); - ecs_override(world, base, Position); + ecs_auto_override(world, base, Position); ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); ecs_set(world, e1, Position, { 20 }); @@ -4104,10 +4241,11 @@ void Prefab_override_twice_w_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_new_id(world); + ecs_entity_t base = ecs_new(world); ecs_set(world, base, Position, {10}); - ecs_override(world, base, Position); + ecs_auto_override(world, base, Position); ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); ecs_set(world, e1, Position, { 20 }); @@ -4138,14 +4276,15 @@ void Prefab_auto_override_copy_once(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ecs_set_hooks(world, Position, { .copy = ecs_copy(Position) }); - ecs_entity_t base = ecs_new_id(world); + ecs_entity_t base = ecs_new(world); ecs_set(world, base, Position, {10, 20}); - ecs_override(world, base, Position); + ecs_auto_override(world, base, Position); test_int(position_copy_invoked, 1); position_copy_invoked = 0; @@ -4165,11 +4304,11 @@ void Prefab_always_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsOverride); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ecs_add_id(world, ecs_id(Position), EcsAlwaysOverride); - - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t b = ecs_new(world); ecs_set(world, b, Position, {10, 20}); ecs_set(world, b, Velocity, {1, 2}); @@ -4200,13 +4339,15 @@ void Prefab_always_override_pair(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, RelA); + ecs_add_pair(world, RelA, EcsOnInstantiate, EcsOverride); ECS_TAG(world, RelB); + ecs_add_pair(world, RelB, EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); - ecs_add_id(world, RelA, EcsAlwaysOverride); - - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t b = ecs_new(world); ecs_set_pair_second(world, b, RelA, Position, {10, 20}); ecs_set_pair_second(world, b, RelB, Velocity, {1, 2}); @@ -4237,10 +4378,43 @@ void Prefab_child_of_prefab_is_prefab(void) { ecs_world_t *world = ecs_mini(); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); - test_assert(ecs_has_id(world, inst, EcsPrefab)); - ecs_entity_t inst_child = ecs_new_w_pair(world, EcsChildOf, inst); - test_assert(ecs_has_id(world, inst_child, EcsPrefab)); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, base); + test_assert(ecs_has_id(world, child, EcsPrefab)); + test_assert(ecs_has_pair(world, child, EcsChildOf, base)); + + ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child); + test_assert(ecs_has_id(world, grand_child, EcsPrefab)); + test_assert(ecs_has_pair(world, grand_child, EcsChildOf, child)); + + ecs_fini(world); +} + +void Prefab_child_of_prefab_w_prefab_is_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t child = ecs_new_w_id(world, EcsPrefab); + ecs_add_pair(world, child, EcsChildOf, base); + test_assert(ecs_has_id(world, child, EcsPrefab)); + test_assert(ecs_has_pair(world, child, EcsChildOf, base)); + + ecs_fini(world); +} + +void Prefab_child_of_prefab_w_prefab_is_prefab_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t child = ecs_new_w_id(world, EcsPrefab); + ecs_add(world, child, Position); + ecs_add_pair(world, child, EcsChildOf, base); + + test_assert(ecs_has_id(world, child, EcsPrefab)); + test_assert(ecs_has(world, child, Position)); + test_assert(ecs_has_pair(world, child, EcsChildOf, base)); ecs_fini(world); } @@ -4248,16 +4422,16 @@ void Prefab_child_of_prefab_is_prefab(void) { void Prefab_override_exclusive(void) { ecs_world_t* ecs = ecs_mini(); - ECS_ENTITY(ecs, Rel, Exclusive); + ECS_ENTITY(ecs, Rel, Exclusive, (OnInstantiate, Inherit)); - ecs_entity_t t1 = ecs_new_id(ecs); - ecs_entity_t t2 = ecs_new_id(ecs); + ecs_entity_t t1 = ecs_new(ecs); + ecs_entity_t t2 = ecs_new(ecs); - ecs_entity_t e = ecs_new_id(ecs); + ecs_entity_t e = ecs_new(ecs); ecs_add_pair(ecs, e, Rel, t1); - ecs_entity_t p = ecs_new_id(ecs); - ecs_override_pair(ecs, p, Rel, t2); + ecs_entity_t p = ecs_new(ecs); + ecs_auto_override_pair(ecs, p, Rel, t2); ecs_add_pair(ecs, e, EcsIsA, p); @@ -4270,20 +4444,20 @@ void Prefab_override_exclusive(void) { void Prefab_override_exclusive_2_lvls(void) { ecs_world_t* ecs = ecs_mini(); - ECS_ENTITY(ecs, Rel, Exclusive); + ECS_ENTITY(ecs, Rel, Exclusive, (OnInstantiate, Inherit)); - ecs_entity_t t1 = ecs_new_id(ecs); - ecs_entity_t t2 = ecs_new_id(ecs); - ecs_entity_t t3 = ecs_new_id(ecs); + ecs_entity_t t1 = ecs_new(ecs); + ecs_entity_t t2 = ecs_new(ecs); + ecs_entity_t t3 = ecs_new(ecs); - ecs_entity_t e = ecs_new_id(ecs); + ecs_entity_t e = ecs_new(ecs); ecs_add_pair(ecs, e, Rel, t1); - ecs_entity_t p = ecs_new_id(ecs); - ecs_override_pair(ecs, p, Rel, t2); + ecs_entity_t p = ecs_new(ecs); + ecs_auto_override_pair(ecs, p, Rel, t2); - ecs_entity_t p2 = ecs_new_id(ecs); - ecs_override_pair(ecs, p2, Rel, t3); + ecs_entity_t p2 = ecs_new(ecs); + ecs_auto_override_pair(ecs, p2, Rel, t3); ecs_add_pair(ecs, p2, EcsIsA, p); ecs_add_pair(ecs, e, EcsIsA, p2); @@ -4323,3 +4497,639 @@ void Prefab_hierarchy_w_recycled_id(void) { ecs_fini(ecs); } + +void Prefab_disable_ids(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, A, 0); + ECS_ENTITY(world, B, 0); + ECS_PREFAB(world, Prefab, A, B); + + test_assert(!ecs_has_id(world, A, EcsDisabled)); + test_assert(!ecs_has_id(world, B, EcsDisabled)); + test_assert(!ecs_has_id(world, EcsPrefab, EcsDisabled)); + + ecs_enable(world, Prefab, false); + + test_assert(ecs_has_id(world, A, EcsDisabled)); + test_assert(ecs_has_id(world, B, EcsDisabled)); + test_assert(!ecs_has_id(world, EcsPrefab, EcsDisabled)); + + ecs_enable(world, Prefab, true); + + test_assert(!ecs_has_id(world, A, EcsDisabled)); + test_assert(!ecs_has_id(world, B, EcsDisabled)); + test_assert(!ecs_has_id(world, EcsPrefab, EcsDisabled)); + + ecs_fini(world); +} + +void Prefab_disable_nested_ids(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, A, 0); + ECS_ENTITY(world, B, 0); + ECS_PREFAB(world, PrefabX, A); + ECS_PREFAB(world, PrefabY, B, PrefabX); + + test_assert(!ecs_has_id(world, A, EcsDisabled)); + test_assert(!ecs_has_id(world, B, EcsDisabled)); + test_assert(!ecs_has_id(world, PrefabX, EcsDisabled)); + test_assert(!ecs_has_id(world, EcsPrefab, EcsDisabled)); + + ecs_enable(world, PrefabY, false); + + test_assert(ecs_has_id(world, A, EcsDisabled)); + test_assert(ecs_has_id(world, B, EcsDisabled)); + test_assert(!ecs_has_id(world, PrefabX, EcsDisabled)); + test_assert(!ecs_has_id(world, EcsPrefab, EcsDisabled)); + + ecs_enable(world, PrefabY, true); + + test_assert(!ecs_has_id(world, A, EcsDisabled)); + test_assert(!ecs_has_id(world, B, EcsDisabled)); + test_assert(!ecs_has_id(world, PrefabX, EcsDisabled)); + test_assert(!ecs_has_id(world, EcsPrefab, EcsDisabled)); + + ecs_fini(world); +} + +void Prefab_prefab_w_children_w_isa_auto_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t child = ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + + ecs_entity_t child_1 = ecs_new_w_pair(world, EcsIsA, child); + ecs_add_pair(world, child_1, EcsChildOf, base); + ecs_set_name(world, child_1, "child_1"); + + ecs_entity_t child_2 = ecs_new_w_pair(world, EcsIsA, child); + ecs_add_pair(world, child_2, EcsChildOf, base); + ecs_set_name(world, child_2, "child_2"); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + + { + ecs_entity_t inst_child = ecs_lookup_from(world, inst, "child_1"); + test_assert(inst_child != 0); + test_assert(ecs_owns(world, inst_child, Position)); + + const Position *p = ecs_get(world, inst_child, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + { + ecs_entity_t inst_child = ecs_lookup_from(world, inst, "child_2"); + test_assert(inst_child != 0); + test_assert(ecs_owns(world, inst_child, Position)); + + const Position *p = ecs_get(world, inst_child, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Prefab_prefab_child_w_override(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + + ecs_entity_t p = ecs_entity(ecs, { .name = "p", .add = ecs_ids(EcsPrefab) }); + ecs_entity_t c = ecs_new_w_pair(ecs, EcsChildOf, p); + ecs_set_name(ecs, c, "c"); + ecs_auto_override(ecs, c, Position); + + ecs_entity_t i = ecs_new_w_pair(ecs, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(ecs, i, "c"); + test_assert(ic != 0); + test_assert(ecs_has(ecs, ic, Position)); + test_assert(!ecs_has_id(ecs, ic, ECS_AUTO_OVERRIDE | ecs_id(Position))); + + ecs_fini(ecs); +} + +void Prefab_prefab_child_w_override_and_higher_component(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t p = ecs_entity(ecs, { .name = "p", .add = ecs_ids(EcsPrefab) }); + ecs_entity_t c = ecs_new_w_pair(ecs, EcsChildOf, p); + ecs_set_name(ecs, c, "c"); + ecs_auto_override(ecs, c, Position); + ecs_add(ecs, c, Velocity); + + ecs_entity_t i = ecs_new_w_pair(ecs, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(ecs, i, "c"); + test_assert(ic != 0); + test_assert(ecs_has(ecs, ic, Position)); + test_assert(ecs_has(ecs, ic, Velocity)); + test_assert(!ecs_has_id(ecs, ic, ECS_AUTO_OVERRIDE | ecs_id(Position))); + test_assert(!ecs_has_id(ecs, ic, ECS_AUTO_OVERRIDE | ecs_id(Velocity))); + + ecs_fini(ecs); +} + +void Prefab_prefab_child_w_override_and_lower_component(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Velocity); + ECS_COMPONENT(ecs, Position); + + ecs_entity_t p = ecs_entity(ecs, { .name = "p", .add = ecs_ids(EcsPrefab) }); + ecs_entity_t c = ecs_new_w_pair(ecs, EcsChildOf, p); + ecs_set_name(ecs, c, "c"); + ecs_auto_override(ecs, c, Position); + ecs_add(ecs, c, Velocity); + + ecs_entity_t i = ecs_new_w_pair(ecs, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(ecs, i, "c"); + test_assert(ic != 0); + test_assert(ecs_has(ecs, ic, Position)); + test_assert(ecs_has(ecs, ic, Velocity)); + test_assert(!ecs_has_id(ecs, ic, ECS_AUTO_OVERRIDE | ecs_id(Position))); + test_assert(!ecs_has_id(ecs, ic, ECS_AUTO_OVERRIDE | ecs_id(Velocity))); + + ecs_fini(ecs); +} + +void Prefab_prefab_1_child_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c, "c"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(world, i, "c"); + test_assert(ic != 0); + + test_assert((c - p) == (ic - i)); + + ecs_fini(world); +} + +void Prefab_prefab_2_children_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c1, "c1"); + ecs_entity_t c2 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c2, "c2"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic1 = ecs_lookup_from(world, i, "c1"); + test_assert(ic1 != 0); + test_assert((c1 - p) == (ic1 - i)); + + ecs_entity_t ic2 = ecs_lookup_from(world, i, "c2"); + test_assert(ic2 != 0); + test_assert((c2 - p) == (ic2 - i)); + + ecs_fini(world); +} + +void Prefab_prefab_3_children_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c1, "c1"); + ecs_entity_t c2 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c2, "c2"); + ecs_entity_t c3 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c3, "c3"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic1 = ecs_lookup_from(world, i, "c1"); + test_assert(ic1 != 0); + test_assert((c1 - p) == (ic1 - i)); + + ecs_entity_t ic2 = ecs_lookup_from(world, i, "c2"); + test_assert(ic2 != 0); + test_assert((c2 - p) == (ic2 - i)); + + ecs_entity_t ic3 = ecs_lookup_from(world, i, "c3"); + test_assert(ic3 != 0); + test_assert((c3 - p) == (ic3 - i)); + + ecs_fini(world); +} + +void Prefab_prefab_2_children_2_types_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c1, "c1"); + ecs_entity_t c2 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c2, "c2"); + + ecs_add(world, c1, Foo); + ecs_add(world, c2, Bar); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic1 = ecs_lookup_from(world, i, "c1"); + test_assert(ic1 != 0); + test_assert((c1 - p) == (ic1 - i)); + + ecs_entity_t ic2 = ecs_lookup_from(world, i, "c2"); + test_assert(ic2 != 0); + test_assert((c2 - p) == (ic2 - i)); + + ecs_fini(world); +} + +void Prefab_prefab_3_children_3_types_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Zoo); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c1, "c1"); + ecs_entity_t c2 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c2, "c2"); + ecs_entity_t c3 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c3, "c3"); + + ecs_add(world, c1, Foo); + ecs_add(world, c2, Bar); + ecs_add(world, c3, Zoo); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic1 = ecs_lookup_from(world, i, "c1"); + test_assert(ic1 != 0); + test_assert((c1 - p) == (ic1 - i)); + + ecs_entity_t ic2 = ecs_lookup_from(world, i, "c2"); + test_assert(ic2 != 0); + test_assert((c2 - p) == (ic2 - i)); + + ecs_entity_t ic3 = ecs_lookup_from(world, i, "c3"); + test_assert(ic3 != 0); + test_assert((c3 - p) == (ic3 - i)); + + ecs_fini(world); +} + +void Prefab_prefab_2_children_2_types_reverse_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c1, "c1"); + ecs_entity_t c2 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c2, "c2"); + + ecs_add(world, c2, Bar); + ecs_add(world, c1, Foo); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic1 = ecs_lookup_from(world, i, "c1"); + test_assert(ic1 != 0); + test_assert((c1 - p) == (ic1 - i)); + + ecs_entity_t ic2 = ecs_lookup_from(world, i, "c2"); + test_assert(ic2 != 0); + test_assert((c2 - p) == (ic2 - i)); + + ecs_fini(world); +} + +void Prefab_prefab_3_children_3_types_reverse_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Zoo); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c1, "c1"); + ecs_entity_t c2 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c2, "c2"); + ecs_entity_t c3 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c3, "c3"); + + ecs_add(world, c3, Zoo); + ecs_add(world, c1, Foo); + ecs_add(world, c2, Bar); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic1 = ecs_lookup_from(world, i, "c1"); + test_assert(ic1 != 0); + test_assert((c1 - p) == (ic1 - i)); + + ecs_entity_t ic2 = ecs_lookup_from(world, i, "c2"); + test_assert(ic2 != 0); + test_assert((c2 - p) == (ic2 - i)); + + ecs_entity_t ic3 = ecs_lookup_from(world, i, "c3"); + test_assert(ic3 != 0); + test_assert((c3 - p) == (ic3 - i)); + + ecs_fini(world); +} + +void Prefab_prefab_2_lvl_nested_children_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c1, "c1"); + ecs_entity_t c2 = ecs_new_w_pair(world, EcsChildOf, c1); + ecs_set_name(world, c2, "c2"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic1 = ecs_lookup_from(world, i, "c1"); + test_assert(ic1 != 0); + test_assert((c1 - p) == (ic1 - i)); + + ecs_entity_t ic2 = ecs_lookup_from(world, ic1, "c2"); + test_assert(ic2 != 0); + test_assert((c2 - p) == (ic2 - i)); + + ecs_fini(world); +} + +void Prefab_prefab_3_lvl_nested_children_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c1, "c1"); + ecs_entity_t c2 = ecs_new_w_pair(world, EcsChildOf, c1); + ecs_set_name(world, c2, "c2"); + ecs_entity_t c3 = ecs_new_w_pair(world, EcsChildOf, c2); + ecs_set_name(world, c3, "c3"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic1 = ecs_lookup_from(world, i, "c1"); + test_assert(ic1 != 0); + test_assert((c1 - p) == (ic1 - i)); + + ecs_entity_t ic2 = ecs_lookup_from(world, ic1, "c2"); + test_assert(ic2 != 0); + test_assert((c2 - p) == (ic2 - i)); + + ecs_entity_t ic3 = ecs_lookup_from(world, ic2, "c3"); + test_assert(ic3 != 0); + test_assert((c3 - p) == (ic3 - i)); + + ecs_fini(world); +} + +void Prefab_prefab_recycled_children_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_delete(world, ecs_new(world)); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + test_assert(p != (uint32_t)p); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c, "c"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(world, i, "c"); + test_assert(ic != 0); + + test_assert((c - (uint32_t)p) == (ic - i)); + + ecs_fini(world); +} + +void Prefab_prefab_recycled_instance_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c, "c"); + + ecs_delete(world, ecs_new(world)); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + test_assert(i != (uint32_t)i); + ecs_entity_t ic = ecs_lookup_from(world, i, "c"); + test_assert(ic != 0); + + test_assert((c - p) == ((uint32_t)ic - (uint32_t)i)); + + ecs_fini(world); +} + +void Prefab_prefab_children_recycled_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_delete(world, ecs_new(world)); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + test_assert(c != (uint32_t)c); + ecs_set_name(world, c, "c"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(world, i, "c"); + test_assert(ic != 0); + + test_assert(((uint32_t)c - p) == (ic - i)); + + ecs_fini(world); +} + +void Prefab_prefab_recycled_children_recycled_offset_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_delete(world, ecs_new(world)); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + test_assert(p != (uint32_t)p); + + ecs_delete(world, ecs_new(world)); + + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + test_assert(c != (uint32_t)c); + ecs_set_name(world, c, "c"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(world, i, "c"); + test_assert(ic != 0); + + test_assert(((uint32_t)c - (uint32_t)p) == (ic - i)); + + ecs_fini(world); +} + +void Prefab_prefab_recycled_children_recycled_offset_id_different_generation(void) { + ecs_world_t *world = ecs_mini(); + + ecs_delete(world, ecs_new(world)); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + test_assert(p != (uint32_t)p); + + ecs_delete(world, ecs_new(world)); + ecs_delete(world, ecs_new(world)); + + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + test_assert(c != (uint32_t)c); + test_assert((c >> 32) != (p >> 32)); + ecs_set_name(world, c, "c"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(world, i, "c"); + test_assert(ic != 0); + + test_assert(((uint32_t)c - (uint32_t)p) == (ic - i)); + + ecs_fini(world); +} + +void Prefab_prefab_1_child_offset_id_occupied(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c, "c"); + + ecs_entity_t i = ecs_new(world); + ecs_entity_t x = ecs_new(world); // Occupy instance child id + + test_assert((c - p) == (x - i)); + + ecs_add_pair(world, i, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(world, i, "c"); + test_assert(ic != 0); + test_assert(ic != x); + + test_assert((c - p) != (ic - i)); + + ecs_fini(world); +} + +void Prefab_prefab_1_child_offset_id_recycled_occupied(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set_name(world, c, "c"); + + ecs_entity_t i = ecs_new(world); + ecs_delete(world, ecs_new(world)); + ecs_entity_t x = ecs_new(world); // Occupy instance child id + + test_assert((c - p) == ((uint32_t)x - i)); + + ecs_add_pair(world, i, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(world, i, "c"); + test_assert(ic != 0); + test_assert(ic != x); + test_assert((uint32_t)ic != (uint32_t)x); + + test_assert((c - p) != (ic - i)); + + ecs_fini(world); +} + +void Prefab_prefab_child_offset_w_smaller_child_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t a = ecs_new(world); + ecs_entity_t b = ecs_new(world); + ecs_delete(world, a); + ecs_delete(world, b); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + test_assert(c < p); + ecs_set_name(world, c, "c"); + + ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); + ecs_entity_t ic = ecs_lookup_from(world, i, "c"); + test_assert(ic != 0); + + test_assert((c - p) != (ic - i)); + + ecs_fini(world); +} + +void Prefab_prefab_w_union(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t rel = ecs_new(world); + ecs_add_id(world, rel, EcsUnion); + ecs_entity_t tgt = ecs_new(world); + + ecs_entity_t base = ecs_new(world); + ecs_add_id(world, base, EcsPrefab); + ecs_add_pair(world, base, rel, tgt); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + test_assert(ecs_has_pair(world, inst, rel, tgt)); + + ecs_fini(world); +} + +void Prefab_prefab_child_w_union(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t rel = ecs_new(world); + ecs_add_id(world, rel, EcsUnion); + ecs_entity_t tgt = ecs_new(world); + + ecs_entity_t base = ecs_new(world); + ecs_add_id(world, base, EcsPrefab); + + ecs_entity_t base_child = ecs_entity(world, { .name = "child", .parent = base }); + ecs_add_pair(world, base_child, rel, tgt); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t inst_child = ecs_lookup_from(world, inst, "child"); + test_assert(inst_child != 0); + test_assert(ecs_has_pair(world, inst_child, rel, tgt)); + + ecs_fini(world); +} + +void Prefab_prefab_w_union_and_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t rel = ecs_new(world); + ecs_add_id(world, rel, EcsUnion); + ecs_entity_t tgt = ecs_new(world); + + ecs_entity_t base = ecs_new(world); + ecs_add_id(world, base, EcsPrefab); + ecs_add_pair(world, base, rel, tgt); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + test_assert(ecs_has_pair(world, inst, rel, tgt)); + + const Position *p = ecs_get(world, inst, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/ReadWrite.c b/vendors/flecs/test/core/src/ReadWrite.c similarity index 81% rename from vendors/flecs/test/api/src/ReadWrite.c rename to vendors/flecs/test/core/src/ReadWrite.c index cb4200877..e37e425b7 100644 --- a/vendors/flecs/test/api/src/ReadWrite.c +++ b/vendors/flecs/test/core/src/ReadWrite.c @@ -1,4 +1,4 @@ -#include +#include void ReadWrite_read(void) { ecs_world_t *world = ecs_mini(); @@ -6,7 +6,7 @@ void ReadWrite_read(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); const ecs_record_t *r = ecs_read_begin(world, e); test_assert(r != NULL); @@ -28,7 +28,7 @@ void ReadWrite_nested_read(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); const ecs_record_t *r = ecs_read_begin(world, e); const ecs_record_t *r_2 = ecs_read_begin(world, e); @@ -54,11 +54,11 @@ void ReadWrite_write(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_record_t *r = ecs_write_begin(world, e); test_assert(r != NULL); - Position *p = ecs_record_get_mut(world, r, Position); + Position *p = ecs_record_ensure(world, r, Position); test_assert(p != NULL); test_assert(p == ecs_get(world, e, Position)); test_int(p->x, 10); @@ -78,7 +78,7 @@ void ReadWrite_nested_write(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_record_t *r = ecs_write_begin(world, e); test_assert(r != NULL); @@ -94,7 +94,7 @@ void ReadWrite_add_while_read(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); const ecs_record_t *r = ecs_read_begin(world, e); test_assert(r != NULL); @@ -110,7 +110,7 @@ void ReadWrite_add_while_write(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_record_t *r = ecs_write_begin(world, e); test_assert(r != NULL); @@ -125,10 +125,10 @@ void ReadWrite_read_from_stage(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_world_t *s = ecs_get_stage(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); const ecs_record_t *r = ecs_read_begin(s, e); test_assert(r != NULL); @@ -152,14 +152,14 @@ void ReadWrite_write_from_stage(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_world_t *s = ecs_get_stage(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_record_t *r = ecs_write_begin(s, e); test_assert(r != NULL); - Position *p = ecs_record_get_mut(s, r, Position); + Position *p = ecs_record_ensure(s, r, Position); test_assert(p != NULL); test_assert(p == ecs_get(world, e, Position)); test_int(p->x, 10); diff --git a/vendors/flecs/test/api/src/Reference.c b/vendors/flecs/test/core/src/Reference.c similarity index 82% rename from vendors/flecs/test/api/src/Reference.c rename to vendors/flecs/test/core/src/Reference.c index de3631aa4..941210f77 100644 --- a/vendors/flecs/test/api/src/Reference.c +++ b/vendors/flecs/test/core/src/Reference.c @@ -1,4 +1,4 @@ -#include +#include void Reference_setup(void) { ecs_log_set_level(-3); @@ -9,7 +9,7 @@ void Reference_get_ref(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_ref_t ref = ecs_ref_init(world, e, Position); const Position *p = ecs_ref_get(world, &ref, Position); @@ -26,7 +26,7 @@ void Reference_get_ref_after_add(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_ref_t ref = ecs_ref_init(world, e, Position); const Position *p = ecs_ref_get(world, &ref, Position); @@ -50,7 +50,7 @@ void Reference_get_ref_after_remove(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_add(world, e, Velocity); ecs_ref_t ref = ecs_ref_init(world, e, Position); @@ -74,8 +74,8 @@ void Reference_get_ref_after_delete(void) { ECS_COMPONENT(world, Position); - ecs_entity_t dummy = ecs_new(world, Position); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t dummy = ecs_new_w(world, Position); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_ref_t ref = ecs_ref_init(world, e, Position); const Position *p = ecs_ref_get(world, &ref, Position); @@ -98,7 +98,7 @@ void Reference_get_ref_after_realloc(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_ref_t ref = ecs_ref_init(world, e, Position); const Position *p = ecs_ref_get(world, &ref, Position); @@ -109,7 +109,7 @@ void Reference_get_ref_after_realloc(void) { int i; for (i = 0; i < 1000; i ++) { // Creating lots of entities which will trigger allocations - ecs_new(world, Position); + ecs_new_w(world, Position); } p = ecs_ref_get(world, &ref, Position); @@ -125,7 +125,7 @@ void Reference_get_ref_staged(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_ref_t ref = ecs_ref_init(world, e, Position); const Position *p = ecs_ref_get(world, &ref, Position); @@ -158,7 +158,7 @@ void Reference_get_ref_after_new_in_stage(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_ref_t ref = ecs_ref_init(world, e, Position); const Position *p = ecs_ref_get(world, &ref, Position); @@ -168,7 +168,7 @@ void Reference_get_ref_after_new_in_stage(void) { ecs_defer_begin(world); - ecs_new(world, Position); + ecs_new_w(world, Position); /* ecs_set() makes immediate changes */ ecs_set(world, e, Position, {30, 40}); @@ -194,7 +194,7 @@ void Reference_get_ref_monitored(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); ecs_new_w_pair(world, EcsChildOf, e); ecs_ref_t ref = ecs_ref_init(world, e, Position); @@ -212,7 +212,7 @@ void Reference_get_nonexisting(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_ref_t ref = ecs_ref_init(world, e, Velocity); const Velocity *p = ecs_ref_get(world, &ref, Velocity); @@ -269,7 +269,7 @@ void Reference_get_ref_w_low_id_tag(void) { .type.alignment = 4 }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, tag); ecs_add_id(world, e, comp); @@ -297,7 +297,7 @@ void Reference_get_ref_w_low_id_tag_after_add(void) { .type.alignment = 4 }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, tag); ecs_add_id(world, e, comp); @@ -312,3 +312,29 @@ void Reference_get_ref_w_low_id_tag_after_add(void) { ecs_fini(world); } + +void Reference_aba_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + ecs_entity_t t3 = ecs_new(world); + ecs_entity_t e = ecs_new(world); + + ecs_set_pair(world, e, Position, t1, {10, 20}); + ecs_set_pair(world, e, Position, t2, {20, 30}); + + ecs_ref_t r = ecs_ref_init_id(world, e, ecs_pair_t(Position, t2)); + + ecs_delete(world, t1); + ecs_set_pair(world, e, Position, t3, {30, 40}); + + Position *p = ecs_ref_get_id(world, &r, ecs_pair_t(Position, t2)); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/Remove.c b/vendors/flecs/test/core/src/Remove.c similarity index 93% rename from vendors/flecs/test/api/src/Remove.c rename to vendors/flecs/test/core/src/Remove.c index 7f092ffb4..50c99b010 100644 --- a/vendors/flecs/test/api/src/Remove.c +++ b/vendors/flecs/test/core/src/Remove.c @@ -1,15 +1,15 @@ -#include +#include void Remove_zero(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); - ecs_remove(world, e, 0); + ecs_remove_id(world, e, 0); } void Remove_1_of_1(void) { @@ -17,7 +17,7 @@ void Remove_1_of_1(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_remove(world, e, Position); @@ -31,7 +31,7 @@ void Remove_1_of_1_again(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_remove(world, e, Position); @@ -147,7 +147,7 @@ void Remove_1_from_empty(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_remove(world, e, Position); @@ -162,7 +162,7 @@ void Remove_not_added(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_remove(world, e, Velocity); diff --git a/vendors/flecs/test/api/src/Search.c b/vendors/flecs/test/core/src/Search.c similarity index 76% rename from vendors/flecs/test/api/src/Search.c rename to vendors/flecs/test/core/src/Search.c index 51faac46d..d3291be6e 100644 --- a/vendors/flecs/test/api/src/Search.c +++ b/vendors/flecs/test/core/src/Search.c @@ -1,4 +1,4 @@ -#include +#include void Search_search(void) { ecs_world_t *world = ecs_mini(); @@ -164,7 +164,7 @@ void Search_search_follow_relation_lvl_0(void) { ECS_TAG(world, TagB); ECS_TAG(world, Rel); - ecs_entity_t e_0 = ecs_new(world, TagA); + ecs_entity_t e_0 = ecs_new_w(world, TagA); ecs_add(world, e_0, TagB); ecs_table_t *t = ecs_get_table(world, e_0); @@ -186,7 +186,7 @@ void Search_search_follow_relation_lvl_1(void) { ECS_TAG(world, TagB); ECS_TAG(world, Rel); - ecs_entity_t e_0 = ecs_new(world, TagA); + ecs_entity_t e_0 = ecs_new_w(world, TagA); ecs_add(world, e_0, TagB); ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, e_0); @@ -209,7 +209,7 @@ void Search_search_follow_relation_lvl_2(void) { ECS_TAG(world, TagB); ECS_TAG(world, Rel); - ecs_entity_t e_0 = ecs_new(world, TagA); + ecs_entity_t e_0 = ecs_new_w(world, TagA); ecs_add(world, e_0, TagB); ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, e_0); ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, e_1); @@ -233,7 +233,7 @@ void Search_search_follow_relation_lvl_3(void) { ECS_TAG(world, TagB); ECS_TAG(world, Rel); - ecs_entity_t e_0 = ecs_new(world, TagA); + ecs_entity_t e_0 = ecs_new_w(world, TagA); ecs_add(world, e_0, TagB); ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, e_0); ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, e_1); @@ -258,7 +258,7 @@ void Search_search_first_lvl_0(void) { ECS_TAG(world, TagB); ECS_TAG(world, Rel); - ecs_entity_t e_0 = ecs_new(world, TagA); + ecs_entity_t e_0 = ecs_new_w(world, TagA); ecs_add(world, e_0, TagB); ecs_table_t *t = ecs_get_table(world, e_0); @@ -280,7 +280,7 @@ void Search_search_first_lvl_1(void) { ECS_TAG(world, TagB); ECS_TAG(world, Rel); - ecs_entity_t e_0 = ecs_new(world, TagA); + ecs_entity_t e_0 = ecs_new_w(world, TagA); ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, e_0); ecs_add(world, e_0, TagB); @@ -303,7 +303,7 @@ void Search_search_first_lvl_2(void) { ECS_TAG(world, TagB); ECS_TAG(world, Rel); - ecs_entity_t e_0 = ecs_new(world, TagA); + ecs_entity_t e_0 = ecs_new_w(world, TagA); ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, e_0); ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, e_1); ecs_add(world, e_0, TagB); @@ -328,7 +328,7 @@ void Search_search_first_lvl_3(void) { ECS_TAG(world, TagB); ECS_TAG(world, Rel); - ecs_entity_t e_0 = ecs_new(world, TagA); + ecs_entity_t e_0 = ecs_new_w(world, TagA); ecs_entity_t e_1 = ecs_new_w_pair(world, Rel, e_0); ecs_entity_t e_2 = ecs_new_w_pair(world, Rel, e_1); ecs_entity_t e_3 = ecs_new_w_pair(world, Rel, e_2); @@ -356,12 +356,12 @@ void Search_search_relation_wildcard(void) { ECS_TAG(world, TgtB); ECS_TAG(world, Rel); - ecs_entity_t b = ecs_new(world, Tag); + ecs_entity_t b = ecs_new_w(world, Tag); ecs_add(world, b, Tag); ecs_add_pair(world, b, Rel, TgtA); ecs_add_pair(world, b, Rel, TgtB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsChildOf, b); ecs_table_t *table = ecs_get_table(world, e); @@ -384,10 +384,10 @@ void Search_search_relation_at_offset(void) { ECS_TAG(world, Tag); ECS_TAG(world, Rel); - ecs_entity_t b1 = ecs_new(world, Tag); - ecs_entity_t b2 = ecs_new(world, Tag); + ecs_entity_t b1 = ecs_new_w(world, Tag); + ecs_entity_t b2 = ecs_new_w(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, b1); ecs_add_pair(world, e, Rel, b2); @@ -414,19 +414,19 @@ void Search_search_relation_at_offset(void) { void Search_search_relation_inherit_from_parent(void) { ecs_world_t *world = ecs_mini(); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t b = ecs_new(world); ecs_add(world, b, TagA); ecs_add(world, b, TagB); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_add_pair(world, p, EcsIsA, b); ecs_add(world, p, TagC); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); ecs_add(world, e, TagC); @@ -456,18 +456,16 @@ void Search_search_relation_inherit_from_parent(void) { void Search_search_relation_dont_inherit(void) { ecs_world_t *world = ecs_mini(); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_add_id(world, TagB, EcsDontInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, DontInherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t b = ecs_new(world); ecs_add(world, b, TagA); ecs_add(world, b, TagB); ecs_add(world, b, TagC); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); ecs_add(world, e, TagC); @@ -506,21 +504,19 @@ void Search_search_relation_dont_inherit(void) { void Search_search_relation_dont_inherit_from_parent(void) { ecs_world_t *world = ecs_mini(); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, TagC); - - ecs_add_id(world, TagB, EcsDontInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, DontInherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t b = ecs_new(world); ecs_add(world, b, TagA); ecs_add(world, b, TagB); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_add_pair(world, p, EcsIsA, b); ecs_add(world, p, TagC); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); ecs_add(world, e, TagC); @@ -558,16 +554,14 @@ void Search_search_relation_dont_inherit_from_parent(void) { void Search_search_relation_exclusive(void) { ecs_world_t *world = ecs_mini(); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsExclusive); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, Exclusive, (OnInstantiate, Inherit)); - ecs_entity_t b = ecs_new_id(world); + ecs_entity_t b = ecs_new(world); ecs_add_pair(world, b, Rel, TagA); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, TagB); ecs_add_pair(world, e, EcsIsA, b); @@ -592,111 +586,3 @@ void Search_search_relation_exclusive(void) { ecs_fini(world); } - -void Search_search_relation_union(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsUnion); - - ecs_entity_t b = ecs_new_id(world); - ecs_add_pair(world, b, Rel, TagA); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, EcsIsA, b); - - ecs_table_t *table = ecs_get_table(world, e); - ecs_entity_t subj = 0; - ecs_id_t id = 0; - int32_t column = 0; - - column = ecs_search_relation(world, table, 0, ecs_pair(Rel, TagA), EcsIsA, EcsSelf|EcsUp, &subj, &id, 0); - test_int(column, 0); - test_int(subj, b); - test_int(id, ecs_pair(Rel, EcsWildcard)); - - column = ecs_search_relation(world, table, 0, ecs_pair(Rel, TagA), EcsIsA, EcsUp, &subj, &id, 0); - test_int(column, 0); - test_int(subj, b); - test_int(id, ecs_pair(Rel, EcsWildcard)); - - column = ecs_search_relation(world, table, 0, ecs_pair(Rel, TagA), EcsIsA, EcsSelf, &subj, &id, 0); - test_int(column, -1); - - ecs_fini(world); -} - -void Search_search_relation_union_wildcard(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsUnion); - - ecs_entity_t b = ecs_new_id(world); - ecs_add_pair(world, b, Rel, TagA); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, EcsIsA, b); - - ecs_table_t *table = ecs_get_table(world, e); - ecs_entity_t subj = 0; - ecs_id_t id = 0; - int32_t column = 0; - - column = ecs_search_relation(world, table, 0, ecs_pair(Rel, EcsWildcard), EcsIsA, EcsSelf|EcsUp, &subj, &id, 0); - test_int(column, 0); - test_int(subj, b); - test_int(id, ecs_pair(Rel, EcsWildcard)); - - column = ecs_search_relation(world, table, 0, ecs_pair(Rel, EcsWildcard), EcsIsA, EcsUp, &subj, &id, 0); - test_int(column, 0); - test_int(subj, b); - test_int(id, ecs_pair(Rel, EcsWildcard)); - - column = ecs_search_relation(world, table, 0, ecs_pair(Rel, EcsWildcard), EcsIsA, EcsSelf, &subj, &id, 0); - test_int(column, -1); - - ecs_fini(world); -} - -void Search_search_relation_union_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); - ECS_TAG(world, Rel); - - ecs_add_id(world, Rel, EcsUnion); - - ecs_entity_t b = ecs_new_id(world); - ecs_add_pair(world, b, Rel, TagA); - - ecs_entity_t e = ecs_new_id(world); - ecs_add_pair(world, e, EcsIsA, b); - - ecs_table_t *table = ecs_get_table(world, e); - ecs_entity_t subj = 0; - ecs_id_t id = 0; - int32_t column = 0; - - column = ecs_search_relation(world, table, 0, ecs_pair(EcsUnion, Rel), EcsIsA, EcsSelf|EcsUp, &subj, &id, 0); - test_int(column, 0); - test_int(subj, b); - test_int(id, ecs_pair(EcsUnion, Rel)); - - column = ecs_search_relation(world, table, 0, ecs_pair(EcsUnion, Rel), EcsIsA, EcsUp, &subj, &id, 0); - test_int(column, 0); - test_int(subj, b); - test_int(id, ecs_pair(EcsUnion, Rel)); - - column = ecs_search_relation(world, table, 0, ecs_pair(EcsUnion, Rel), EcsIsA, EcsSelf, &subj, &id, 0); - test_int(column, -1); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/Set.c b/vendors/flecs/test/core/src/Set.c similarity index 75% rename from vendors/flecs/test/api/src/Set.c rename to vendors/flecs/test/core/src/Set.c index eef67ad14..87e6ee9bf 100644 --- a/vendors/flecs/test/api/src/Set.c +++ b/vendors/flecs/test/core/src/Set.c @@ -1,11 +1,11 @@ -#include +#include void Set_set_empty(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -25,7 +25,7 @@ void Set_set_nonempty(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_set(world, e, Velocity, {10, 20}); @@ -45,7 +45,7 @@ void Set_set_non_empty_override(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -64,7 +64,7 @@ void Set_set_again(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -92,7 +92,7 @@ void Set_set_2(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -125,7 +125,7 @@ void Set_add_set(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, Position); @@ -147,7 +147,7 @@ void Set_set_add(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -175,7 +175,7 @@ void Set_set_add_other(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -203,7 +203,7 @@ void Set_set_remove(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -229,7 +229,7 @@ void Set_set_remove_other(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Velocity); + ecs_entity_t e = ecs_new_w(world, Velocity); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -258,7 +258,7 @@ void Set_set_remove_twice(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -291,7 +291,7 @@ void Set_set_and_new(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); @@ -304,32 +304,27 @@ void Set_set_and_new(void) { } void Set_set_null(void) { + install_test_abort(); + ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); - ecs_set_ptr(world, e, Position, NULL); - test_assert(e != 0); - - test_assert(ecs_has(world, e, Position)); - const Position *p = ecs_get(world, e, Position); - test_assert(p != NULL); - test_int(p->x, 0); - test_int(p->y, 0); + ecs_entity_t e = ecs_new(world); - ecs_fini(world); + test_expect_abort(); + ecs_set_ptr(world, e, Position, NULL); } -void Set_get_mut_new(void) { +void Set_ensure_new(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); test_assert( ecs_has(world, e, Position)); test_assert(p == ecs_get(world, e, Position)); @@ -337,17 +332,17 @@ void Set_get_mut_new(void) { ecs_fini(world); } -void Set_get_mut_existing(void) { +void Set_ensure_existing(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert( ecs_has(world, e, Position)); const Position *p_prev = ecs_get(world, e, Position); - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); test_assert( ecs_has(world, e, Position)); test_assert(p == p_prev); @@ -355,38 +350,38 @@ void Set_get_mut_existing(void) { ecs_fini(world); } -void Set_get_mut_tag_new(void) { +void Set_ensure_tag_new(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); ECS_TAG(world, MyTag); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); - ecs_get_mut_id(world, e, MyTag); + ecs_ensure_id(world, e, MyTag); } -void Set_get_mut_tag_existing(void) { +void Set_ensure_tag_existing(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); ECS_TAG(world, MyTag); - ecs_entity_t e = ecs_new(world, MyTag); + ecs_entity_t e = ecs_new_w(world, MyTag); test_assert(e != 0); test_assert( ecs_has(world, e, MyTag)); test_expect_abort(); - ecs_get_mut_id(world, e, MyTag); + ecs_ensure_id(world, e, MyTag); } -void Set_get_mut_tag_new_w_comp(void) { +void Set_ensure_tag_new_w_comp(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -394,15 +389,15 @@ void Set_get_mut_tag_new_w_comp(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, MyTag); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_expect_abort(); - ecs_get_mut_id(world, e, MyTag); + ecs_ensure_id(world, e, MyTag); } -void Set_get_mut_tag_existing_w_comp(void) { +void Set_ensure_tag_existing_w_comp(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -410,17 +405,17 @@ void Set_get_mut_tag_existing_w_comp(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, MyTag); - ecs_entity_t e = ecs_new(world, MyTag); + ecs_entity_t e = ecs_new_w(world, MyTag); test_assert(e != 0); test_assert( ecs_has(world, e, MyTag)); ecs_add(world, e, Position); test_expect_abort(); - ecs_get_mut_id(world, e, MyTag); + ecs_ensure_id(world, e, MyTag); } -void Set_get_mut_tag_new_w_pair(void) { +void Set_ensure_tag_new_w_pair(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -434,10 +429,10 @@ void Set_get_mut_tag_new_w_pair(void) { test_expect_abort(); - ecs_get_mut_id(world, e, MyTag); + ecs_ensure_id(world, e, MyTag); } -void Set_get_mut_tag_existing_w_pair(void) { +void Set_ensure_tag_existing_w_pair(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -446,14 +441,81 @@ void Set_get_mut_tag_existing_w_pair(void) { ECS_TAG(world, Pair); ECS_TAG(world, MyTag); - ecs_entity_t e = ecs_new(world, MyTag); + ecs_entity_t e = ecs_new_w(world, MyTag); test_assert(e != 0); test_assert( ecs_has(world, e, MyTag)); ecs_add_pair(world, e, Pair, ecs_id(Position)); test_expect_abort(); - ecs_get_mut_id(world, e, MyTag); + ecs_ensure_id(world, e, MyTag); +} + +void Set_get_mut_not_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new(world); + Position *p = ecs_get_mut(world, e, Position); + test_assert(p == NULL); + + ecs_fini(world); +} + +void Set_get_mut_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + Position *p = ecs_get_mut(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Set_get_mut_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_entity_t e = ecs_new_w(world, Foo); + test_assert(NULL == ecs_get_mut_id(world, e, Foo)); + + ecs_fini(world); +} + +void Set_get_mut_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tgt); + + ecs_entity_t e = ecs_insert(world, ecs_value_pair(Position, Tgt, {10, 20})); + Position *p = ecs_get_mut_pair(world, e, Position, Tgt); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Set_get_mut_pair_second(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tgt); + + ecs_entity_t e = ecs_insert(world, ecs_value_pair_2nd(Tgt, Position, {10, 20})); + Position *p = ecs_get_mut_pair_second(world, e, Tgt, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); } static bool is_invoked = false; @@ -468,10 +530,10 @@ void Set_modified_w_on_set(void) { ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetPosition, EcsOnSet, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); test_assert( ecs_has(world, e, Position)); test_assert(p == ecs_get(world, e, Position)); @@ -491,7 +553,7 @@ void Set_modified_no_component(void) { ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetPosition, EcsOnSet, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_expect_abort(); @@ -521,7 +583,7 @@ void OnAddRemove(ecs_iter_t *it) { } } -void Set_get_mut_w_add_in_on_add(void) { +void Set_ensure_w_add_in_on_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Position); @@ -529,9 +591,9 @@ void Set_get_mut_w_add_in_on_add(void) { ECS_OBSERVER(world, OnAdd, EcsOnAdd, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); - Position *p = ecs_get_mut(world, e, Position); + Position *p = ecs_ensure(world, e, Position); test_assert(p != NULL); p->x = 10; p->y = 20; @@ -543,7 +605,7 @@ void Set_get_mut_w_add_in_on_add(void) { ecs_fini(world); } -void Set_get_mut_w_remove_in_on_add(void) { +void Set_ensure_w_remove_in_on_add(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -551,13 +613,13 @@ void Set_get_mut_w_remove_in_on_add(void) { ECS_COMPONENT_DEFINE(world, Position); ECS_OBSERVER(world, OnAddRemove, EcsOnAdd, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_expect_abort(); - /* get_mut is guaranteed to always return a valid pointer, so removing the + /* ensure is guaranteed to always return a valid pointer, so removing the * component from the OnAdd trigger is not allowed */ - ecs_get_mut(world, e, Position); + ecs_ensure(world, e, Position); } static @@ -570,7 +632,7 @@ void OnAddRealloc(ecs_iter_t *it) { } } -void Set_get_mut_w_realloc_in_on_add(void) { +void Set_ensure_w_realloc_in_on_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Position); @@ -578,18 +640,18 @@ void Set_get_mut_w_realloc_in_on_add(void) { ecs_entity_t *entities = ecs_os_malloc_n(ecs_entity_t, 1000); for (int i = 0; i < 1000; i ++) { - entities[i] = ecs_new(world, Position); + entities[i] = ecs_new_w(world, Position); } ecs_observer_init(world, &(ecs_observer_desc_t){ .callback = OnAddRealloc, .events = {EcsOnAdd}, - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .ctx = entities }); - ecs_entity_t e = ecs_new(world, Velocity); - const Position *ptr = ecs_get_mut(world, e, Position); + ecs_entity_t e = ecs_new_w(world, Velocity); + const Position *ptr = ecs_ensure(world, e, Position); test_assert(ptr == ecs_get(world, e, Position)); for (int i = 0; i < 1000; i ++) { @@ -613,10 +675,10 @@ void Set_emplace(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - Position *p = ecs_emplace(world, e, Position); + Position *p = ecs_emplace(world, e, Position, NULL); test_assert(p != NULL); test_assert(ecs_has(world, e, Position)); test_assert(p == ecs_get(world, e, Position)); @@ -630,15 +692,15 @@ void Set_emplace_2(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - Position *p = ecs_emplace(world, e, Position); + Position *p = ecs_emplace(world, e, Position, NULL); test_assert(p != NULL); test_assert(ecs_has(world, e, Position)); test_assert(p == ecs_get(world, e, Position)); - Velocity *v = ecs_emplace(world, e, Velocity); + Velocity *v = ecs_emplace(world, e, Velocity, NULL); test_assert(v != NULL); test_assert(ecs_has(world, e, Velocity)); test_assert(v == ecs_get(world, e, Velocity)); @@ -653,12 +715,12 @@ void Set_emplace_existing(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(ecs_has(world, e, Position)); test_assert(e != 0); test_expect_abort(); - ecs_emplace(world, e, Position); + ecs_emplace(world, e, Position, NULL); } void Set_emplace_w_move(void) { @@ -671,7 +733,7 @@ void Set_emplace_w_move(void) { test_assert(e != 0); test_str("Foo", ecs_get_name(world, e)); - Position *p = ecs_emplace(world, e, Position); + Position *p = ecs_emplace(world, e, Position, NULL); test_assert(p != NULL); test_assert(ecs_has(world, e, Position)); test_assert(p == ecs_get(world, e, Position)); @@ -698,13 +760,13 @@ void Set_emplace_w_observer_w_add(void) { ecs_observer_init(world, &(ecs_observer_desc_t){ .callback = OnAddMove, .events = {EcsOnAdd}, - .filter.terms[0].id = ecs_id(Position) + .query.terms[0].id = ecs_id(Position) }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); - Position *p = ecs_emplace(world, e, Position); + Position *p = ecs_emplace(world, e, Position, NULL); test_assert(p != NULL); p->x = 10; p->y = 20; @@ -725,3 +787,22 @@ void Set_emplace_w_observer_w_add(void) { ecs_fini(world); } + +void Set_emplace_existing_w_check(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new(world); + test_assert(e != 0); + + bool is_new; + ecs_emplace(world, e, Position, &is_new); + test_bool(is_new, true); + ecs_emplace(world, e, Position, &is_new); + test_bool(is_new, false); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/SingleThreadStaging.c b/vendors/flecs/test/core/src/SingleThreadStaging.c similarity index 90% rename from vendors/flecs/test/api/src/SingleThreadStaging.c rename to vendors/flecs/test/core/src/SingleThreadStaging.c index cfc80d49e..fe3f9ce81 100644 --- a/vendors/flecs/test/api/src/SingleThreadStaging.c +++ b/vendors/flecs/test/core/src/SingleThreadStaging.c @@ -1,4 +1,4 @@ -#include +#include static ECS_COMPONENT_DECLARE(Velocity); @@ -12,7 +12,7 @@ void NewEmpty(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { - ctx->new_entities[ctx->entity_count] = ecs_new(it->world, 0); + ctx->new_entities[ctx->entity_count] = ecs_new(it->world); ctx->entity_count ++; } } @@ -90,7 +90,7 @@ static void NewEmpty_w_count(ecs_iter_t *it) { IterData *ctx = ecs_get_ctx(it->world); - ctx->new_entities[ctx->entity_count] = ecs_bulk_new(it->world, 0, 1000)[0]; + ctx->new_entities[ctx->entity_count] = ecs_bulk_new_w_id(it->world, 0, 1000)[0]; ctx->entity_count ++; } @@ -157,7 +157,7 @@ void Add_to_new_empty(ecs_iter_t *it) { IterData *ctx = ecs_get_ctx(it->world); int i; for (i = 0; i < it->count; i ++) { - ecs_entity_t e = ecs_new(it->world, 0); + ecs_entity_t e = ecs_new(it->world); if (ctx->component) { ecs_add_id(it->world, e, ctx->component); } @@ -232,7 +232,7 @@ void Add_remove_same_from_new(ecs_iter_t *it) { IterData *ctx = ecs_get_ctx(it->world); int i; for (i = 0; i < it->count; i ++) { - ecs_entity_t e = ecs_new(it->world, 0); + ecs_entity_t e = ecs_new(it->world); test_assert( !ecs_get_type(it->world, e)); @@ -356,7 +356,7 @@ void Add_remove_different_from_new_empty(ecs_iter_t *it) { IterData *ctx = ecs_get_ctx(it->world); int i; for (i = 0; i < it->count; i ++) { - ecs_entity_t e = ecs_new(it->world, 0); + ecs_entity_t e = ecs_new(it->world); if (ctx->component_3) { ecs_add_id(it->world, e, ctx->component_3); @@ -500,9 +500,9 @@ void SingleThreadStaging_clone_w_value(void) { ECS_COMPONENT(world, Rotation); ECS_SYSTEM(world, Clone_current_w_value, EcsOnUpdate, Position); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e1, Position, {10, 20}); @@ -662,9 +662,9 @@ void SingleThreadStaging_remove_from_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -701,9 +701,9 @@ void SingleThreadStaging_remove_2_from_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); @@ -764,9 +764,9 @@ void SingleThreadStaging_add_remove_same_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -805,9 +805,9 @@ void SingleThreadStaging_add_remove_same_existing_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -863,9 +863,9 @@ void SingleThreadStaging_remove_add_same_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -904,9 +904,9 @@ void SingleThreadStaging_remove_add_same_existing_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -942,9 +942,9 @@ void SingleThreadStaging_add_remove_2_same_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -985,9 +985,9 @@ void SingleThreadStaging_add_remove_2_same_existing_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); @@ -1029,9 +1029,9 @@ void SingleThreadStaging_remove_add_2_same_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1061,7 +1061,7 @@ void SingleThreadStaging_remove_add_2_same_to_current(void) { static void AddRemoveAdd(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -1080,9 +1080,9 @@ void SingleThreadStaging_add_remove_add_same_to_current(void) { ECS_SYSTEM(world, AddRemoveAdd, EcsOnUpdate, Position, Velocity()); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); ecs_progress(world, 1); @@ -1106,9 +1106,9 @@ void SingleThreadStaging_remove_add_2_same_existing_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1168,9 +1168,9 @@ void SingleThreadStaging_add_remove_different_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1211,9 +1211,9 @@ void SingleThreadStaging_2_add_1_remove_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1277,9 +1277,9 @@ void SingleThreadStaging_1_add_2_remove_to_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1426,7 +1426,7 @@ void Delete_new_empty(ecs_iter_t *it) { IterData *ctx = ecs_get_ctx(it->world); int i; for (i = 0; i < it->count; i ++) { - ecs_entity_t e = ecs_new(it->world, 0); + ecs_entity_t e = ecs_new(it->world); ecs_delete(it->world, e); ctx->new_entities[ctx->entity_count] = e; ctx->entity_count ++; @@ -1524,9 +1524,9 @@ void SingleThreadStaging_set_current(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1564,7 +1564,7 @@ void Set_new_empty(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { - ecs_entity_t e = ecs_new(it->world, 0); + ecs_entity_t e = ecs_new(it->world); ecs_set(it->world, e, Rotation, {10 + e}); ctx->new_entities[ctx->entity_count] = e; ctx->entity_count ++; @@ -1584,9 +1584,9 @@ void SingleThreadStaging_set_new_empty(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1638,9 +1638,9 @@ void SingleThreadStaging_set_new_w_component(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1693,9 +1693,9 @@ void SingleThreadStaging_set_existing_new_w_component(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1728,7 +1728,7 @@ void Set_new_after_add(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { - ecs_entity_t e = ecs_new(it->world, 0); + ecs_entity_t e = ecs_new(it->world); ecs_add_id(it->world, e, ctx->component); ecs_set(it->world, e, Position, {10 + e, 20 + e}); ctx->new_entities[ctx->entity_count] = e; @@ -1749,9 +1749,9 @@ void SingleThreadStaging_set_new_after_add(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1784,7 +1784,7 @@ void Remove_after_set(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { - ecs_entity_t e = ecs_new(it->world, 0); + ecs_entity_t e = ecs_new(it->world); ecs_set(it->world, e, Position, {10 + e, 20 + e}); ecs_remove_id(it->world, e, ctx->component); @@ -1806,9 +1806,9 @@ void SingleThreadStaging_remove_after_set(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1839,7 +1839,7 @@ void Delete_after_set(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { - ecs_entity_t e = ecs_new(it->world, 0); + ecs_entity_t e = ecs_new(it->world); test_assert( !ecs_get_type(it->world, e)); ecs_set(it->world, e, Position, {10 + e, 20 + e}); @@ -1863,9 +1863,9 @@ void SingleThreadStaging_delete_after_set(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, 0); - ecs_entity_t e2 = ecs_new(world, 0); - ecs_entity_t e3 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -1898,9 +1898,9 @@ void SingleThreadStaging_add_to_current_in_on_add(void) { ecs_set_ctx(world, &ctx, NULL); /* Create entities from scratch so they don't have the EcsName component */ - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); test_assert( ecs_has(world, e1, Position)); test_assert( ecs_has(world, e2, Position)); @@ -1969,8 +1969,8 @@ static Probe pv_probe; static void On_PV(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); probe_system_w_ctx(it, &pv_probe); @@ -1994,9 +1994,9 @@ void SingleThreadStaging_match_table_created_in_progress(void) { IterData add_ctx = {.component = ecs_id(Velocity)}; ecs_set_ctx(world, &add_ctx, NULL); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); ecs_progress(world, 1); @@ -2021,13 +2021,13 @@ static void Set_velocity_on_new(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { - ecs_set(it->world, 0, Velocity, {10, 20}); + ecs_insert(it->world, ecs_value(Velocity, {10, 20})); } } static void On_V(ecs_iter_t *it) { - Velocity *v = ecs_field(it, Velocity, 1); + Velocity *v = ecs_field(it, Velocity, 0); probe_iter(it); @@ -2046,7 +2046,7 @@ void SingleThreadStaging_match_table_created_w_new_in_progress(void) { ECS_SYSTEM(world, Set_velocity_on_new, EcsOnUpdate, Position); ECS_SYSTEM(world, On_V, EcsOnUpdate, Velocity); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); @@ -2076,7 +2076,7 @@ void SingleThreadStaging_match_table_created_w_new_in_on_set(void) { IterData add_ctx = {.component = ecs_id(Velocity)}; ecs_set_ctx(world, &add_ctx, NULL); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); @@ -2173,8 +2173,8 @@ void Create_container_reverse(ecs_iter_t *it) { int i; for (i = 0; i < it->count; i ++) { - ecs_entity_t child = ecs_new(world, 0); - ecs_entity_t parent = ecs_new(world, 0); + ecs_entity_t child = ecs_new(world); + ecs_entity_t parent = ecs_new(world); test_assert(child != 0); test_assert(parent != 0); @@ -2220,7 +2220,7 @@ void SingleThreadStaging_merge_table_w_container_added_on_set_reverse(void) { static void Task(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); ecs_entity_t *e = ecs_get_ctx(it->world); @@ -2232,7 +2232,7 @@ void SingleThreadStaging_merge_after_tasks(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ECS_SYSTEM(world, Task, EcsOnUpdate, Position()); @@ -2245,10 +2245,14 @@ void SingleThreadStaging_merge_after_tasks(void) { ecs_fini(world); } +static int override_after_remove_invoked = 0; + static void OverrideAfterRemove(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); + + override_after_remove_invoked ++; int i; for (i = 0; i < it->count; i ++) { @@ -2263,19 +2267,18 @@ void SingleThreadStaging_override_after_remove_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ECS_ENTITY(world, base, Position, Disabled); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, base, Position, Prefab); ECS_ENTITY(world, e, Position, (IsA, base)); ECS_SYSTEM(world, OverrideAfterRemove, EcsOnUpdate, Position); ecs_set(world, base, Position, {10, 20}); - ecs_set(world, e, Position, {30, 40}); ecs_progress(world, 1); + test_int(override_after_remove_invoked, 1); test_assert( ecs_has(world, e, Position)); const Position *p_ptr = ecs_get(world, e, Position); @@ -2288,12 +2291,12 @@ void SingleThreadStaging_override_after_remove_in_progress(void) { static void GetParentInProgress(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); ecs_world_t *world = it->world; /* Create parent */ - ecs_entity_t parent = ecs_new(world, Velocity); + ecs_entity_t parent = ecs_new_w(world, Velocity); int i; for (i = 0; i < it->count; i ++) { @@ -2310,7 +2313,7 @@ void SingleThreadStaging_get_parent_in_progress(void) { ECS_SYSTEM(world, GetParentInProgress, EcsOnUpdate, Position, Velocity()); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_progress(world, 1); @@ -2324,8 +2327,8 @@ static void AddInProgress(ecs_iter_t *it) { ecs_world_t *world = it->world; - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -2336,8 +2339,8 @@ void AddInProgress(ecs_iter_t *it) { static void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); int i; for (i = 0; i < it->count; i ++) { @@ -2355,7 +2358,7 @@ void SingleThreadStaging_merge_once(void) { ECS_SYSTEM(world, AddInProgress, EcsOnUpdate, Position, [out] !Velocity); ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); - ecs_entity_t e = ecs_set(world, 0, Position, {0, 0}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {0, 0})); ecs_progress(world, 1); const Position *p = ecs_get(world, e, Position); @@ -2397,7 +2400,7 @@ void SingleThreadStaging_clear_stage_after_merge(void) { .move = ecs_move(Position) }); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); @@ -2425,13 +2428,13 @@ void SingleThreadStaging_clear_stage_after_merge(void) { void MutableTest(ecs_iter_t *it) { ecs_world_t *world = it->world; - Velocity *v = ecs_field(it, Velocity, 2); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + Velocity *v = ecs_field(it, Velocity, 1); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); int32_t i; for (i = 0; i < it->count; i ++) { bool is_added = !ecs_has(world, it->entities[i], Velocity); - Velocity *v_mut = ecs_get_mut(world, it->entities[i], Velocity); + Velocity *v_mut = ecs_ensure(world, it->entities[i], Velocity); test_assert(v_mut != NULL); @@ -2455,14 +2458,14 @@ void MutableTest(ecs_iter_t *it) { } } -void SingleThreadStaging_get_mutable(void) { +void SingleThreadStaging_ensureable(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_SYSTEM(world, MutableTest, EcsOnUpdate, Position, ?Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_progress(world, 0); @@ -2484,14 +2487,14 @@ void SingleThreadStaging_get_mutable(void) { ecs_fini(world); } -void SingleThreadStaging_get_mutable_from_main(void) { +void SingleThreadStaging_ensureable_from_main(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_SYSTEM(world, MutableTest, EcsOnUpdate, Position, ?Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); // Add velocity on main stage @@ -2519,16 +2522,16 @@ void SingleThreadStaging_get_mutable_from_main(void) { void MutableTest_w_Add(ecs_iter_t *it) { ecs_world_t *world = it->world; - Velocity *v = ecs_field(it, Velocity, 2); - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); - ecs_id_t ecs_id(MyTag) = ecs_field_id(it, 3); + Velocity *v = ecs_field(it, Velocity, 1); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); + ecs_id_t ecs_id(MyTag) = ecs_field_id(it, 2); int32_t i; for (i = 0; i < it->count; i ++) { ecs_add(world, it->entities[i], MyTag); bool is_added = !ecs_has(world, it->entities[i], Velocity); - Velocity *v_mut = ecs_get_mut(world, it->entities[i], Velocity); + Velocity *v_mut = ecs_ensure(world, it->entities[i], Velocity); test_assert(v_mut != NULL); @@ -2554,7 +2557,7 @@ void MutableTest_w_Add(ecs_iter_t *it) { typedef bool MyBool; -void SingleThreadStaging_get_mutable_w_add(void) { +void SingleThreadStaging_ensureable_w_add(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); @@ -2562,7 +2565,7 @@ void SingleThreadStaging_get_mutable_w_add(void) { ECS_COMPONENT(world, MyBool); ECS_SYSTEM(world, MutableTest_w_Add, EcsOnUpdate, Position, ?Velocity, MyBool()); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_progress(world, 0); @@ -2594,7 +2597,7 @@ void SingleThreadStaging_get_mutable_w_add(void) { static void OnAdd(ecs_iter_t *it) { - Velocity *v = ecs_field(it, Velocity, 1); + Velocity *v = ecs_field(it, Velocity, 0); int i; for (i = 0; i < it->count; i ++) { @@ -2605,7 +2608,7 @@ void OnAdd(ecs_iter_t *it) { static void AddInProgress2(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -2621,7 +2624,7 @@ void SingleThreadStaging_on_add_after_new_type_in_progress(void) { ECS_SYSTEM(world, AddInProgress2, EcsOnUpdate, Position, Velocity()); ECS_OBSERVER(world, OnAdd, EcsOnAdd, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); ecs_progress(world, 1); @@ -2641,10 +2644,10 @@ void SingleThreadStaging_lock_table(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ecs_id(Position)}} + ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_id(Position)}} }); ecs_iter_t it = ecs_query_iter(world, q); @@ -2668,10 +2671,10 @@ void SingleThreadStaging_recursive_lock_table(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ecs_id(Position)}} + ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_id(Position)}} }); ecs_iter_t it = ecs_query_iter(world, q); @@ -2695,10 +2698,10 @@ void SingleThreadStaging_modify_after_lock(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ecs_id(Position)}} + ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_id(Position)}} }); int32_t count = 0; @@ -2718,33 +2721,7 @@ void SingleThreadStaging_modify_after_lock(void) { ecs_remove(world, e, Position); test_assert(!ecs_has(world, e, Position)); - ecs_fini(world); -} - -void SingleThreadStaging_get_case_from_stage(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Switch, Union); - ECS_TAG(world, CaseOne); - ECS_TAG(world, CaseTwo); - - ecs_entity_t e = ecs_new_w_pair(world, Switch, CaseOne); - - ecs_frame_begin(world, 1); - - ecs_readonly_begin(world); - - ecs_world_t *stage = ecs_get_stage(world, 0); - - ecs_entity_t c = ecs_get_target(world, e, Switch, 0); - test_assert(c == CaseOne); - - c = ecs_get_target(stage, e, Switch, 0); - test_assert(c == CaseOne); - - ecs_readonly_end(world); - - ecs_frame_end(world); + ecs_query_fini(q); ecs_fini(world); } @@ -2752,10 +2729,10 @@ void SingleThreadStaging_get_case_from_stage(void) { void SingleThreadStaging_get_object_from_stage(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_id(world); + ecs_entity_t parent = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); @@ -2771,9 +2748,9 @@ void SingleThreadStaging_add_to_world_while_readonly(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_add(world, e, Tag); test_assert(!ecs_has(world, e, Tag)); ecs_readonly_end(world); @@ -2789,10 +2766,10 @@ void SingleThreadStaging_add_to_world_and_stage_while_readonly(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_world_t *stage = ecs_get_stage(world, 0); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_add(world, e, TagA); ecs_add(stage, e, TagB); test_assert(!ecs_has(world, e, TagA)); @@ -2811,7 +2788,7 @@ void SingleThreadStaging_lookup_after_stage_count_change(void) { ecs_set_stage_count(world, 2); /* Make sure we can still lookup entities from flecs.core */ - test_assert(ecs_lookup_fullpath(world, "$") != 0); + test_assert(ecs_lookup(world, "$") != 0); ecs_fini(world); } @@ -2819,19 +2796,19 @@ void SingleThreadStaging_lookup_after_stage_count_change(void) { void SingleThreadStaging_lookup_w_scope_after_stage_count_change(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); - ecs_entity_t child = ecs_new_entity(world, "child"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + ecs_entity_t child = ecs_entity(world, { .name = "child" }); ecs_add_pair(world, child, EcsChildOf, parent); - test_assert(ecs_lookup_fullpath(world, "parent.child") != 0); + test_assert(ecs_lookup(world, "parent.child") != 0); ecs_set_scope(world, parent); - test_assert(ecs_lookup_fullpath(world, "parent.child") != 0); - test_assert(ecs_lookup_fullpath(world, "child") != 0); + test_assert(ecs_lookup(world, "parent.child") != 0); + test_assert(ecs_lookup(world, "child") != 0); ecs_set_stage_count(world, 2); - test_assert(ecs_lookup_fullpath(world, "parent.child") != 0); - test_assert(ecs_lookup_fullpath(world, "child") != 0); + test_assert(ecs_lookup(world, "parent.child") != 0); + test_assert(ecs_lookup(world, "child") != 0); ecs_fini(world); } @@ -2839,15 +2816,15 @@ void SingleThreadStaging_lookup_w_scope_after_stage_count_change(void) { void SingleThreadStaging_with_after_stage_count_change(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t tag = ecs_new_id(world); + ecs_entity_t tag = ecs_new(world); ecs_set_with(world, tag); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_entity(world, { 0 }); test_assert(ecs_has_id(world, e1, tag)); ecs_set_stage_count(world, 2); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_entity(world, { 0 }); test_assert(ecs_has_id(world, e2, tag)); ecs_fini(world); diff --git a/vendors/flecs/test/api/src/Singleton.c b/vendors/flecs/test/core/src/Singleton.c similarity index 91% rename from vendors/flecs/test/api/src/Singleton.c rename to vendors/flecs/test/core/src/Singleton.c index 0e722e02d..fb9edd0ef 100644 --- a/vendors/flecs/test/api/src/Singleton.c +++ b/vendors/flecs/test/core/src/Singleton.c @@ -1,4 +1,4 @@ -#include +#include void Singleton_add_singleton(void) { ecs_world_t *world = ecs_mini(); @@ -47,12 +47,12 @@ void Singleton_set_get_singleton(void) { ecs_fini(world); } -void Singleton_get_mut_singleton(void) { +void Singleton_ensure_singleton(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - Position *p_mut = ecs_singleton_get_mut(world, Position); + Position *p_mut = ecs_singleton_ensure(world, Position); p_mut->x = 10; p_mut->y = 20; @@ -66,7 +66,7 @@ void Singleton_get_mut_singleton(void) { static void IncSingleton(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); p->x ++; p->y ++; } diff --git a/vendors/flecs/test/core/src/Sparse.c b/vendors/flecs/test/core/src/Sparse.c new file mode 100644 index 000000000..3ab5c5914 --- /dev/null +++ b/vendors/flecs/test/core/src/Sparse.c @@ -0,0 +1,2849 @@ +#include + +void Sparse_has(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_bool(false, ecs_has(world, e, Position)); + + ecs_add(world, e, Position); + test_bool(true, ecs_has(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_owns(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_bool(false, ecs_owns(world, e, Position)); + + ecs_add(world, e, Position); + test_bool(true, ecs_owns(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_get(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_assert(NULL == ecs_get(world, e, Position)); + + ecs_add(world, e, Position); + test_assert(NULL != ecs_get(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_get_mut(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_assert(NULL == ecs_get_mut(world, e, Position)); + + ecs_add(world, e, Position); + test_assert(NULL != ecs_get_mut(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_ensure(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + Position *p = ecs_ensure(world, e, Position); + test_assert(NULL != p); + + ecs_add(world, e, Position); + test_assert(p == ecs_get_mut(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_emplace(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + bool is_new; + ecs_entity_t e = ecs_new(world); + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(NULL != p); + test_bool(true, is_new); + + test_assert(p == ecs_emplace(world, e, Position, &is_new)); + test_bool(false, is_new); + + ecs_add(world, e, Foo); + + test_assert(p == ecs_emplace(world, e, Position, &is_new)); + test_bool(false, is_new); + + ecs_fini(world); +} + +void Sparse_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Sparse_modified_no_on_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + Position *p = ecs_ensure(world, e, Position); + test_assert(NULL != p); + + ecs_modified(world, e, Position); + + test_assert(true); // no crash + + ecs_fini(world); +} + +void Sparse_insert_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Sparse_insert_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, e, Velocity); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Sparse_get_ref(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + + ecs_ref_t ref = ecs_ref_init(world, e, Position); + + { + const Position *p = ecs_ref_get(world, &ref, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_add(world, e, Foo); + + { + const Position *p = ecs_ref_get(world, &ref, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_update_ref(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + + ecs_ref_t ref = ecs_ref_init(world, e, Position); + + { + const Position *p = ecs_ref_get(world, &ref, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_add(world, e, Foo); + ecs_ref_update(world, &ref); + + { + const Position *p = ecs_ref_get(world, &ref, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_get_recycled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e0 = ecs_new_w(world, Position); + ecs_delete(world, e0); + + ecs_entity_t e = ecs_new(world); + test_assert(e != e0); + test_assert(e0 == (uint32_t)e); + + test_assert(NULL == ecs_get(world, e, Position)); + + ecs_add(world, e, Position); + test_assert(NULL != ecs_get(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_get_mut_recycled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e0 = ecs_new_w(world, Position); + ecs_delete(world, e0); + + ecs_entity_t e = ecs_new(world); + test_assert(e != e0); + test_assert(e0 == (uint32_t)e); + + test_assert(NULL == ecs_get_mut(world, e, Position)); + + ecs_add(world, e, Position); + test_assert(NULL != ecs_get_mut(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_ensure_recycled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e0 = ecs_new_w(world, Position); + ecs_delete(world, e0); + + ecs_entity_t e = ecs_new(world); + test_assert(e != e0); + test_assert(e0 == (uint32_t)e); + + Position *p = ecs_ensure(world, e, Position); + test_assert(NULL != p); + + ecs_add(world, e, Position); + test_assert(p == ecs_get_mut(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_emplace_recycled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e0 = ecs_new_w(world, Position); + ecs_delete(world, e0); + + ecs_entity_t e = ecs_new(world); + test_assert(e != e0); + test_assert(e0 == (uint32_t)e); + + bool is_new; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(NULL != p); + test_bool(true, is_new); + + test_assert(p == ecs_emplace(world, e, Position, &is_new)); + test_bool(false, is_new); + + ecs_add(world, e, Foo); + + test_assert(p == ecs_emplace(world, e, Position, &is_new)); + test_bool(false, is_new); + + ecs_fini(world); +} + +void Sparse_set_recycled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e0 = ecs_new_w(world, Position); + ecs_delete(world, e0); + + ecs_entity_t e = ecs_new(world); + test_assert(e != e0); + test_assert(e0 == (uint32_t)e); + + ecs_set(world, e, Position, {10, 20}); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Sparse_get_ref_recycled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e0 = ecs_new_w(world, Position); + ecs_delete(world, e0); + + ecs_entity_t e = ecs_new(world); + test_assert(e != e0); + test_assert(e0 == (uint32_t)e); + + ecs_set(world, e, Position, {10, 20}); + + ecs_ref_t ref = ecs_ref_init(world, e, Position); + + { + const Position *p = ecs_ref_get(world, &ref, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_add(world, e, Foo); + + { + const Position *p = ecs_ref_get(world, &ref, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_test_stable_ptr(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_assert(NULL == ecs_get(world, e, Position)); + + ecs_add(world, e, Position); + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + + const Velocity *v = ecs_ensure(world, e, Velocity); + test_assert(v != NULL); + test_assert(p == ecs_get(world, e, Position)); + + ecs_add(world, e, Foo); + test_assert(p == ecs_get(world, e, Position)); + test_assert(v != ecs_get(world, e, Velocity)); + + ecs_fini(world); +} + +void Sparse_has_after_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_bool(false, ecs_has(world, e, Position)); + + ecs_add(world, e, Position); + test_bool(true, ecs_has(world, e, Position)); + + ecs_remove(world, e, Position); + test_bool(false, ecs_has(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_has_after_clear(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_bool(false, ecs_has(world, e, Position)); + + ecs_add(world, e, Position); + test_bool(true, ecs_has(world, e, Position)); + + ecs_clear(world, e); + test_bool(false, ecs_has(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_get_after_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_assert(NULL == ecs_get(world, e, Position)); + + ecs_add(world, e, Position); + test_assert(NULL != ecs_get(world, e, Position)); + + ecs_remove(world, e, Position); + test_assert(NULL == ecs_get(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_get_mut_after_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_assert(NULL == ecs_get_mut(world, e, Position)); + + ecs_add(world, e, Position); + test_assert(NULL != ecs_get_mut(world, e, Position)); + + ecs_remove(world, e, Position); + test_assert(NULL == ecs_get_mut(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_sparse_w_hole(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Velocity); + ecs_add(world, e1, Position); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Velocity); + ecs_add(world, e2, Position); + + ecs_delete(world, e1); + ecs_delete(world, e2); + + e1 = ecs_new(world); // create hole in sparse set + + e2 = ecs_new(world); + ecs_add(world, e2, Velocity); + + test_assert(!ecs_has(world, e1, Velocity)); + test_assert(ecs_has(world, e2, Velocity)); + + ecs_fini(world); +} + +void Sparse_record_get(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Tag); + + { + const ecs_record_t *r = ecs_read_begin(world, e); + test_assert(r != NULL); + test_assert(NULL == ecs_record_get(world, r, Position)); + ecs_read_end(r); + } + + ecs_add(world, e, Position); + + { + const ecs_record_t *r = ecs_read_begin(world, e); + test_assert(r != NULL); + const Position *p = ecs_record_get(world, r, Position); + test_assert(p != NULL); + ecs_read_end(r); + } + + ecs_fini(world); +} + +void Sparse_has_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t b = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, b); + test_bool(false, ecs_has(world, e, Position)); + + ecs_set(world, b, Position, {10, 20}); + test_bool(true, ecs_has(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_owns_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t b = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, b); + test_bool(false, ecs_has(world, e, Position)); + test_bool(false, ecs_owns(world, e, Position)); + + ecs_set(world, b, Position, {10, 20}); + test_bool(true, ecs_has(world, e, Position)); + test_bool(false, ecs_owns(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_get_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t b = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, b); + test_assert(NULL == ecs_get(world, e, Position)); + + ecs_set(world, b, Position, {10, 20}); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_assert(p == ecs_get(world, b, Position)); + + ecs_fini(world); +} + +void Sparse_get_mut_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t b = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, b); + test_assert(NULL == ecs_get_mut(world, e, Position)); + + ecs_set(world, b, Position, {10, 20}); + test_assert(NULL == ecs_get_mut(world, e, Position)); + + ecs_fini(world); +} + +void Sparse_ensure_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t b = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, b); + test_assert(NULL == ecs_get(world, e, Position)); + + ecs_set(world, b, Position, {10, 20}); + + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_assert(p != ecs_get(world, b, Position)); + + ecs_fini(world); +} + +void Sparse_emplace_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t b = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, b); + test_assert(NULL == ecs_get(world, e, Position)); + + ecs_set(world, b, Position, {10, 20}); + + bool is_new; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_bool(is_new, true); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_assert(p != ecs_get(world, b, Position)); + + ecs_fini(world); +} + +void Sparse_override_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t base = ecs_new(world); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsIsA, base); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_owns(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_assert(p != ecs_get(world, base, Position)); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Sparse_delete_w_override_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t base = ecs_new(world); + ecs_set(world, base, Position, {10, 20}); + + ecs_log_set_level(0); + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsIsA, base); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_owns(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_assert(p != ecs_get(world, base, Position)); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_delete(world, e); + ecs_log_set_level(-1); + + ecs_fini(world); +} + +static int on_remove_isa_invoked = 0; + +static +void on_remove_isa(ecs_iter_t *it) { + on_remove_isa_invoked ++; +} + +void Sparse_delete_w_override_on_remove_isa(void) { + ecs_world_t *world = ecs_mini(); + + ecs_observer(world, { + .query.terms = {{ ecs_pair(EcsIsA, EcsWildcard) }}, + .events = { EcsOnRemove }, + .callback = on_remove_isa + }); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t base = ecs_new(world); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsIsA, base); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_owns(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_assert(p != ecs_get(world, base, Position)); + test_int(p->x, 10); + test_int(p->y, 20); + + test_int(0, on_remove_isa_invoked); + + ecs_delete(world, e); + + test_int(1, on_remove_isa_invoked); + + ecs_fini(world); +} + +static int position_ctor_invoked = 0; +static int position_dtor_invoked = 0; +static int position_on_add_invoked = 0; +static int position_on_remove_invoked = 0; +static int position_on_set_invoked = 0; +static Position position_on_set_value = {0, 0}; + +static ECS_CTOR(Position, ptr, { + position_ctor_invoked ++; + ptr->x = 10; + ptr->y = 20; +}) + +static ECS_DTOR(Position, ptr, { + test_int(ptr->x, 10); + test_int(ptr->y, 20); + position_dtor_invoked ++; +}) + +static void Position_on_add(ecs_iter_t *it) { + test_int(1, position_ctor_invoked); + Position *p = ecs_field_at(it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_uint(it->event, EcsOnAdd); + + position_on_add_invoked ++; +} + +static void Position_on_remove(ecs_iter_t *it) { + test_int(0, position_dtor_invoked); + Position *p = ecs_field_at(it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_uint(it->event, EcsOnRemove); + + position_on_remove_invoked ++; +} + +static void Position_on_set(ecs_iter_t *it) { + test_int(1, position_ctor_invoked); + test_int(0, position_dtor_invoked); + Position *p = ecs_field_at(it, Position, 0, 0); + test_assert(p != NULL); + position_on_set_value = *p; + test_uint(it->event, EcsOnSet); + + position_on_set_invoked ++; +} + +void Sparse_ctor_after_emplace(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + }); + + ecs_entity_t e = ecs_new(world); + + bool is_new; + ecs_emplace(world, e, Position, &is_new); + test_bool(true, is_new); + + test_int(0, position_ctor_invoked); + test_int(0, position_dtor_invoked); + + ecs_fini(world); +} + +void Sparse_ctor_dtor_after_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .dtor = ecs_dtor(Position) + }); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(0, position_dtor_invoked); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_remove(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(1, position_dtor_invoked); + + ecs_fini(world); +} + +void Sparse_ctor_dtor_after_clear(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .dtor = ecs_dtor(Position) + }); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(0, position_dtor_invoked); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_clear(world, e); + + test_int(1, position_ctor_invoked); + test_int(1, position_dtor_invoked); + + ecs_fini(world); +} + +void Sparse_ctor_dtor_after_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .dtor = ecs_dtor(Position) + }); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(0, position_dtor_invoked); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_delete(world, e); + + test_int(1, position_ctor_invoked); + test_int(1, position_dtor_invoked); + + ecs_fini(world); +} + +void Sparse_ctor_dtor_after_fini(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .dtor = ecs_dtor(Position) + }); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(0, position_dtor_invoked); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); + + test_int(1, position_ctor_invoked); + test_int(1, position_dtor_invoked); +} + +void Sparse_on_add_remove_after_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .dtor = ecs_dtor(Position), + .on_add = Position_on_add, + .on_remove = Position_on_remove, + }); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(0, position_dtor_invoked); + test_int(0, position_on_remove_invoked); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_remove(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(1, position_dtor_invoked); + test_int(1, position_on_remove_invoked); + + ecs_fini(world); +} + +void Sparse_on_add_remove_after_clear(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .dtor = ecs_dtor(Position), + .on_add = Position_on_add, + .on_remove = Position_on_remove, + }); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(0, position_dtor_invoked); + test_int(0, position_on_remove_invoked); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_clear(world, e); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(1, position_dtor_invoked); + test_int(1, position_on_remove_invoked); + + ecs_fini(world); +} + +void Sparse_on_add_remove_after_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .dtor = ecs_dtor(Position), + .on_add = Position_on_add, + .on_remove = Position_on_remove, + }); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(0, position_dtor_invoked); + test_int(0, position_on_remove_invoked); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_delete(world, e); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(1, position_dtor_invoked); + test_int(1, position_on_remove_invoked); + + ecs_fini(world); +} + +void Sparse_on_add_remove_after_fini(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .dtor = ecs_dtor(Position), + .on_add = Position_on_add, + .on_remove = Position_on_remove, + }); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(0, position_dtor_invoked); + test_int(0, position_on_remove_invoked); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(1, position_dtor_invoked); + test_int(1, position_on_remove_invoked); +} + +void Sparse_on_set_after_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_add = Position_on_add, + .on_set = Position_on_set + }); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {30, 40}); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(1, position_on_set_invoked); + test_int(30, position_on_set_value.x); + test_int(40, position_on_set_value.y); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 30); + test_int(ptr->y, 40); + } + + ecs_fini(world); +} + +void Sparse_on_set_after_modified(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_add = Position_on_add, + .on_set = Position_on_set + }); + + ecs_entity_t e = ecs_new(world); + + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 30; + p->y = 40; + ecs_modified(world, e, Position); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(1, position_on_set_invoked); + test_int(30, position_on_set_value.x); + test_int(40, position_on_set_value.y); + + { + const Position *ptr = ecs_get(world, e, Position); + test_int(ptr->x, 30); + test_int(ptr->y, 40); + } + + ecs_fini(world); +} + +void Sparse_on_set_at_offset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_add = Position_on_add, + .on_set = Position_on_set + }); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(1, position_on_set_invoked); + test_int(10, position_on_set_value.x); + test_int(20, position_on_set_value.y); + + position_ctor_invoked = 0; + position_on_add_invoked = 0; + position_on_set_invoked = 0; + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {30, 40}); + + test_int(1, position_ctor_invoked); + test_int(1, position_on_add_invoked); + test_int(1, position_on_set_invoked); + test_int(30, position_on_set_value.x); + test_int(40, position_on_set_value.y); + + ecs_fini(world); +} + +static +void Position_add_observer(ecs_iter_t *it) { + probe_iter(it); + test_int(it->count, 1); + Position *p = ecs_field_at(it, Position, 0, 0); + test_assert(p != NULL); +} + +static +void Position_set_observer(ecs_iter_t *it) { + probe_iter(it); + test_int(it->count, 1); + Position *p = ecs_field_at(it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); +} + +void Sparse_on_add_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnAdd}, + .callback = Position_add_observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + test_assert(e != 0); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnAdd); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + + ecs_fini(world); +} + +void Sparse_on_set_observer_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnSet}, + .callback = Position_set_observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + + test_int(ctx.invoked, 0); + + ecs_set(world, e, Position, {10, 20}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnSet); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + + ecs_fini(world); +} + +void Sparse_on_set_observer_modified(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnSet}, + .callback = Position_set_observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + + test_int(ctx.invoked, 0); + + ecs_modified(world, e, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnSet); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + + ecs_fini(world); +} + +void Sparse_on_set_observer_insert(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnSet}, + .callback = Position_set_observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + test_assert(e != 0); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnSet); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + + ecs_fini(world); +} + +void Sparse_on_remove_observer_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnRemove}, + .callback = Position_set_observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + test_assert(e != 0); + + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnRemove); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + + ecs_fini(world); +} + +void Sparse_on_remove_observer_clear(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnRemove}, + .callback = Position_set_observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + test_assert(e != 0); + + test_int(ctx.invoked, 0); + + ecs_clear(world, e); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnRemove); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + + ecs_fini(world); +} + +void Sparse_on_remove_observer_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnRemove}, + .callback = Position_set_observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + test_assert(e != 0); + + test_int(ctx.invoked, 0); + + ecs_delete(world, e); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnRemove); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + + ecs_fini(world); +} + +void Sparse_on_remove_observer_fini(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }}, + .events = {EcsOnRemove}, + .callback = Position_set_observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + test_assert(e != 0); + + test_int(ctx.invoked, 0); + + ecs_fini(world); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnRemove); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); +} + +void Sparse_on_set_after_remove_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_set(world, base, Position, {10, 20}); + ecs_auto_override(world, base, Position); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsIsA, base); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_owns(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_assert(p != ecs_get(world, base, Position)); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_set(world, e, Position, {30, 40}); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ ecs_id(Position) }}, + .events = { EcsOnSet }, + .callback = Position_set_observer, + .ctx = &ctx + }); + + ecs_remove(world, e, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnRemove); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + + ecs_fini(world); +} + +static +void Position_Velocity_add_observer(ecs_iter_t *it) { + probe_iter(it); + test_int(it->count, 1); + Position *p = ecs_field_at(it, Position, 0, 0); + test_assert(p != NULL); + Velocity *v = ecs_field_at(it, Velocity, 1, 0); + test_assert(v != NULL); +} + +static +void Position_Velocity_set_observer(ecs_iter_t *it) { + probe_iter(it); + test_int(it->count, 1); + Position *p = ecs_field_at(it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + Velocity *v = ecs_field_at(it, Velocity, 1, 0); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); +} + +void Sparse_on_add_observer_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, + .events = {EcsOnAdd}, + .callback = Position_Velocity_add_observer, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + ecs_entity_t e = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + test_assert(e != 0); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnAdd); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + test_uint(ctx.c[0][1], ecs_id(Velocity)); + + ecs_fini(world); +} + +void Sparse_on_set_observer_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, + .events = {EcsOnSet}, + .callback = Position_Velocity_set_observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + + test_int(ctx.invoked, 0); + + ecs_set(world, e, Velocity, {1, 2}); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnSet); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + test_uint(ctx.c[0][1], ecs_id(Velocity)); + + ecs_fini(world); +} + +void Sparse_on_remove_observer_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + Probe ctx; + + ecs_observer(world, { + .query.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, + .events = {EcsOnRemove}, + .callback = Position_Velocity_set_observer, + .ctx = &ctx + }); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + + test_int(ctx.invoked, 0); + + ecs_set(world, e, Velocity, {1, 2}); + + test_int(ctx.invoked, 0); + + ecs_remove(world, e, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_uint(ctx.event, EcsOnRemove); + test_uint(ctx.e[0], e); + test_uint(ctx.c[0][0], ecs_id(Position)); + test_uint(ctx.c[0][1], ecs_id(Velocity)); + + ecs_fini(world); + + test_int(ctx.invoked, 1); +} + +void Sparse_sparse_relationship(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + ECS_TAG(world, Tgt); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + test_assert(NULL == ecs_get_pair(world, e, Position, Tgt)); + + ecs_set_pair(world, e, Position, Tgt, {10, 20}); + const Position *ptr = ecs_get_pair(world, e, Position, Tgt); + test_assert(ptr != NULL); + + ecs_add(world, e, Foo); + test_assert(ptr == ecs_get_pair(world, e, Position, Tgt)); + + ecs_fini(world); +} + +void Sparse_defer_ensure(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + + test_assert(!ecs_has(world, e, Position)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_ensure_w_modified(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + + ecs_modified(world, e, Position); + + test_assert(!ecs_has(world, e, Position)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_ensure_modified(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure_modified_id(world, e, ecs_id(Position)); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + + test_assert(!ecs_has(world, e, Position)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + ecs_set(world, e, Position, {10, 20}); + + test_assert(!ecs_has(world, e, Position)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + + ecs_fini(world); +} + +void Sparse_defer_emplace(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(p != NULL); + test_bool(is_new, true); + p->x = 10; + p->y = 20; + } + + test_assert(!ecs_has(world, e, Position)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_emplace_w_modified(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(p != NULL); + test_bool(is_new, true); + p->x = 10; + p->y = 20; + } + + ecs_modified(world, e, Position); + + test_assert(!ecs_has(world, e, Position)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_ensure_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_ensure_existing_twice(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + p->x = 30; + p->y = 40; + } + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + } + + ecs_fini(world); +} + +void Sparse_defer_ensure_w_modified_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + + ecs_modified(world, e, Position); + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_ensure_modified_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure_modified_id(world, e, ecs_id(Position)); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_set_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + ecs_defer_begin(world); + + ecs_set(world, e, Position, {10, 20}); + + ecs_defer_end(world); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + + ecs_fini(world); +} + +void Sparse_defer_emplace_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(p != NULL); + test_bool(is_new, false); + p->x = 10; + p->y = 20; + } + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_emplace_w_modified_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(p != NULL); + test_bool(is_new, false); + p->x = 10; + p->y = 20; + } + + ecs_modified(world, e, Position); + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_ensure(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + { + Velocity *p = ecs_ensure(world, e, Velocity); + test_assert(p != NULL); + p->x = 1; + p->y = 2; + } + + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, Velocity)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_ensure_w_modified(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + ecs_modified(world, e, Position); + + { + Velocity *p = ecs_ensure(world, e, Velocity); + test_assert(p != NULL); + p->x = 1; + p->y = 2; + } + ecs_modified(world, e, Velocity); + + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, Velocity)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_ensure_modified(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure_modified_id(world, e, ecs_id(Position)); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + { + Velocity *p = ecs_ensure_modified_id(world, e, ecs_id(Velocity)); + test_assert(p != NULL); + p->x = 1; + p->y = 2; + } + + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, Velocity)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_emplace(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(p != NULL); + test_bool(is_new, true); + p->x = 10; + p->y = 20; + } + + { + bool is_new = false; + Velocity *p = ecs_emplace(world, e, Velocity, &is_new); + test_assert(p != NULL); + test_bool(is_new, true); + p->x = 1; + p->y = 2; + } + + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, Velocity)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_emplace_w_modified(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(p != NULL); + test_bool(is_new, true); + p->x = 10; + p->y = 20; + } + ecs_modified(world, e, Position); + + { + bool is_new = false; + Velocity *p = ecs_emplace(world, e, Velocity, &is_new); + test_assert(p != NULL); + test_bool(is_new, true); + p->x = 1; + p->y = 2; + } + ecs_modified(world, e, Velocity); + + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, Velocity)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {1, 2}); + + test_assert(!ecs_has(world, e, Position)); + test_assert(!ecs_has(world, e, Velocity)); + ecs_defer_end(world); + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_ensure_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add(world, e, Velocity); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + { + Velocity *p = ecs_ensure(world, e, Velocity); + test_assert(p != NULL); + p->x = 1; + p->y = 2; + } + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_ensure_existing_twice(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add(world, e, Velocity); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + { + Velocity *p = ecs_ensure(world, e, Velocity); + test_assert(p != NULL); + p->x = 1; + p->y = 2; + } + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + p->x = 30; + p->y = 40; + } + { + Velocity *p = ecs_ensure(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + p->x = 3; + p->y = 4; + } + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 3); + test_int(p->y, 4); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_ensure_w_modified_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add(world, e, Velocity); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure(world, e, Position); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + ecs_modified(world, e, Position); + + { + Velocity *p = ecs_ensure(world, e, Velocity); + test_assert(p != NULL); + p->x = 1; + p->y = 2; + } + ecs_modified(world, e, Velocity); + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_ensure_modified_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add(world, e, Velocity); + + ecs_defer_begin(world); + + { + Position *p = ecs_ensure_modified_id(world, e, ecs_id(Position)); + test_assert(p != NULL); + p->x = 10; + p->y = 20; + } + { + Velocity *p = ecs_ensure_modified_id(world, e, ecs_id(Velocity)); + test_assert(p != NULL); + p->x = 1; + p->y = 2; + } + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_emplace_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add(world, e, Velocity); + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(p != NULL); + test_bool(is_new, false); + p->x = 10; + p->y = 20; + } + + { + bool is_new = false; + Velocity *p = ecs_emplace(world, e, Velocity, &is_new); + test_assert(p != NULL); + test_bool(is_new, false); + p->x = 1; + p->y = 2; + } + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_emplace_w_modified_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add(world, e, Velocity); + + ecs_defer_begin(world); + + { + bool is_new = false; + Position *p = ecs_emplace(world, e, Position, &is_new); + test_assert(p != NULL); + test_bool(is_new, false); + p->x = 10; + p->y = 20; + } + ecs_modified(world, e, Position); + + { + bool is_new = false; + Velocity *p = ecs_emplace(world, e, Velocity, &is_new); + test_assert(p != NULL); + test_bool(is_new, false); + p->x = 1; + p->y = 2; + } + ecs_modified(world, e, Velocity); + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_set_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add(world, e, Velocity); + + ecs_defer_begin(world); + + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {1, 2}); + + ecs_defer_end(world); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *p = ecs_get(world, e, Velocity); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_set_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + + ecs_defer_begin(world); + + ecs_set(world, e, Position, {10, 20}); + ecs_remove(world, e, Position); + + ecs_defer_end(world); + + test_assert(!ecs_has(world, e, Position)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p == NULL); + } + + ecs_fini(world); +} + +void Sparse_defer_batched_set_remove_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + + ecs_defer_begin(world); + + ecs_set(world, e, Position, {10, 20}); + ecs_remove(world, e, Position); + + ecs_defer_end(world); + + test_assert(!ecs_has(world, e, Position)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p == NULL); + } + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/StackAlloc.c b/vendors/flecs/test/core/src/StackAlloc.c similarity index 99% rename from vendors/flecs/test/api/src/StackAlloc.c rename to vendors/flecs/test/core/src/StackAlloc.c index 6852b32af..a7e342c43 100644 --- a/vendors/flecs/test/api/src/StackAlloc.c +++ b/vendors/flecs/test/core/src/StackAlloc.c @@ -1,4 +1,4 @@ -#include +#include #include "../../../src/private_api.h" void StackAlloc_init_fini(void) { diff --git a/vendors/flecs/test/api/src/Stresstests.c b/vendors/flecs/test/core/src/Stresstests.c similarity index 95% rename from vendors/flecs/test/api/src/Stresstests.c rename to vendors/flecs/test/core/src/Stresstests.c index 0a150ad19..4640dcc8c 100644 --- a/vendors/flecs/test/api/src/Stresstests.c +++ b/vendors/flecs/test/core/src/Stresstests.c @@ -1,4 +1,4 @@ -#include +#include #include void Stresstests_setup(void) { @@ -59,7 +59,7 @@ void Add_random(ecs_iter_t *it) { static void Set_velocity_callback(ecs_iter_t *it) { - Velocity *v = ecs_field(it, Velocity, 1); + Velocity *v = ecs_field(it, Velocity, 0); int i; for (i = 0; i < it->count; i ++) { @@ -191,7 +191,7 @@ void Stresstests_create_delete_entity_random_components(void) { ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Rotation); - const ecs_entity_t *ids = ecs_bulk_new(world, 0, 1000); + const ecs_entity_t *ids = ecs_bulk_new_w_id(world, 0, 1000); test_assert(ids != NULL); int i; @@ -213,7 +213,7 @@ void Stresstests_set_entity_random_components(void) { ECS_SYSTEM(world, Set_velocity_callback, EcsOnSet, Velocity); - const ecs_entity_t *ids = ecs_bulk_new(world, 0, 1000); + const ecs_entity_t *ids = ecs_bulk_new_w_id(world, 0, 1000); test_assert(ids != NULL); int i; @@ -271,7 +271,7 @@ void Stresstests_create_2m_entities_1_comp(void) { int32_t i; for (i = 0; i < 2000 * 1000; i ++) { - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); } @@ -298,7 +298,7 @@ void Stresstests_create_2m_entities_bulk_1_comp(void) { void Stresstests_add_1k_tags(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); int i; for (i = 0; i < 1000; i ++) { @@ -319,10 +319,10 @@ void Stresstests_create_1m_set_two_components(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_entity_t i, j; for (i = e; i < 1000000 + e; i ++) { - ecs_ensure(world, i); + ecs_make_alive(world, i); ecs_set(world, i, Position, {10, 20}); } @@ -331,7 +331,7 @@ void Stresstests_create_1m_set_two_components(void) { } for (i = e; i < e + 1000000 + e; i ++) { - ecs_ensure(world, i); + ecs_make_alive(world, i); ecs_set(world, i, Velocity, {1, 2}); } diff --git a/vendors/flecs/test/core/src/Table.c b/vendors/flecs/test/core/src/Table.c new file mode 100644 index 000000000..63fecbfbb --- /dev/null +++ b/vendors/flecs/test/core/src/Table.c @@ -0,0 +1,795 @@ +#include + +void Table_get_index(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {10, 20}); + + ecs_table_t *table = ecs_get_table(world, e); + test_assert(table != NULL); + test_int(ecs_table_get_type_index(world, table, ecs_id(Position)), 0); + test_int(ecs_table_get_type_index(world, table, ecs_id(Velocity)), 1); + + ecs_fini(world); +} + +void Table_get_index_after_tag(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t Tag = ecs_new_low_id(world); + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + test_assert(Tag < ecs_id(Position)); + test_assert(Tag < ecs_id(Velocity)); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {10, 20}); + ecs_add_id(world, e, Tag); + + ecs_table_t *table = ecs_get_table(world, e); + test_assert(table != NULL); + test_int(ecs_table_get_type_index(world, table, Tag), 0); + test_int(ecs_table_get_type_index(world, table, ecs_id(Position)), 1); + test_int(ecs_table_get_type_index(world, table, ecs_id(Velocity)), 2); + + ecs_fini(world); +} + +void Table_get_index_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = ecs_new(world); + ecs_add_id(world, e, Foo); + ecs_add_id(world, e, Bar); + + ecs_table_t *table = ecs_get_table(world, e); + test_assert(table != NULL); + test_int(ecs_table_get_type_index(world, table, Foo), 0); + test_int(ecs_table_get_type_index(world, table, Bar), 1); + + ecs_fini(world); +} + +void Table_get_index_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tgt); + + ecs_entity_t e = ecs_new(world); + ecs_set_pair(world, e, Position, Tgt, {10, 20}); + ecs_set_pair(world, e, Velocity, Tgt, {10, 20}); + + ecs_table_t *table = ecs_get_table(world, e); + test_assert(table != NULL); + test_int(ecs_table_get_type_index(world, table, ecs_pair_t(Position, Tgt)), 0); + test_int(ecs_table_get_type_index(world, table, ecs_pair_t(Velocity, Tgt)), 1); + + ecs_fini(world); +} + +void Table_get_index_pair_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, RelA, Tgt); + ecs_add_pair(world, e, RelB, Tgt); + + ecs_table_t *table = ecs_get_table(world, e); + test_assert(table != NULL); + test_int(ecs_table_get_type_index(world, table, ecs_pair(RelA, Tgt)), 0); + test_int(ecs_table_get_type_index(world, table, ecs_pair(RelB, Tgt)), 1); + + ecs_fini(world); +} + +void Table_get_index_not_in_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {10, 20}); + + ecs_table_t *table = ecs_get_table(world, e); + test_assert(table != NULL); + test_int(ecs_table_get_type_index(world, table, ecs_id(Position)), 0); + test_int(ecs_table_get_type_index(world, table, ecs_id(Velocity)), 1); + test_int(ecs_table_get_type_index(world, table, ecs_id(Mass)), -1); + + ecs_fini(world); +} + +void Table_get_column(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e2, Velocity, {2, 3}); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + int32_t pos_id = ecs_table_get_column_index(world, table, ecs_id(Position)); + int32_t vel_id = ecs_table_get_column_index(world, table, ecs_id(Velocity)); + + Position *p = ecs_table_get_column(table, pos_id, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + + Velocity *v = ecs_table_get_column(table, vel_id, 0); + test_assert(v != NULL); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_int(v[1].x, 2); + test_int(v[1].y, 3); + + ecs_fini(world); +} + +void Table_get_column_for_low_tag(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_entity_t Tag = ecs_new_low_id(world); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_add_id(world, e1, Tag); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + int32_t tag_id = ecs_table_get_type_index(world, table, Tag); + test_assert(tag_id == 1); + + int32_t tag_column_id = ecs_table_get_column_index(world, table, Tag); + test_assert(tag_column_id == -1); + + test_expect_abort(); + ecs_table_get_column(table, tag_id, 0); +} + +void Table_get_column_for_tag(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_add(world, e1, Tag); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + int32_t tag_id = ecs_table_get_type_index(world, table, Tag); + test_assert(tag_id == 1); + + int32_t tag_column_id = ecs_table_get_column_index(world, table, Tag); + test_assert(tag_column_id == -1); + + test_expect_abort(); + ecs_table_get_column(table, tag_id, 0); +} + +void Table_get_column_for_high_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_entity_t ecs_id(Position) = ecs_component(world, { + .entity = ecs_new(world), + .type.size = ECS_SIZEOF(Position), + .type.alignment = ECS_ALIGNOF(Position) + }); + + ecs_entity_t ecs_id(Velocity) = ecs_component(world, { + .entity = ecs_new(world), + .type.size = ECS_SIZEOF(Velocity), + .type.alignment = ECS_ALIGNOF(Velocity) + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_add(world, e1, Foo); + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e2, Velocity, {2, 3}); + ecs_add(world, e2, Foo); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + test_int(0, ecs_table_get_type_index(world, table, Foo)); + test_int(1, ecs_table_get_type_index(world, table, ecs_id(Position))); + test_int(2, ecs_table_get_type_index(world, table, ecs_id(Velocity))); + + test_int(-1, ecs_table_get_column_index(world, table, Foo)); + int32_t pos_id = ecs_table_get_column_index(world, table, ecs_id(Position)); + int32_t vel_id = ecs_table_get_column_index(world, table, ecs_id(Velocity)); + test_int(pos_id, 0); + test_int(vel_id, 1); + + Position *p = ecs_table_get_column(table, pos_id, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + + Velocity *v = ecs_table_get_column(table, vel_id, 0); + test_assert(v != NULL); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_int(v[1].x, 2); + test_int(v[1].y, 3); + + ecs_fini(world); +} + +void Table_get_column_for_component_after_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t tag_w_low_id = ecs_entity(world, { + .use_low_id = true + }); + + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_add_id(world, e1, tag_w_low_id); + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e2, Velocity, {2, 3}); + ecs_add_id(world, e2, tag_w_low_id); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + int32_t pos_id = ecs_table_get_column_index(world, table, ecs_id(Position)); + int32_t vel_id = ecs_table_get_column_index(world, table, ecs_id(Velocity)); + + test_assert(pos_id != -1); + test_assert(vel_id != -1); + + Position *p = ecs_table_get_column(table, pos_id, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + + Velocity *v = ecs_table_get_column(table, vel_id, 0); + test_assert(v != NULL); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_int(v[1].x, 2); + test_int(v[1].y, 3); + + ecs_fini(world); +} + +void Table_get_column_w_offset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e2, Velocity, {2, 3}); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + int32_t pos_id = ecs_table_get_column_index(world, table, ecs_id(Position)); + int32_t vel_id = ecs_table_get_column_index(world, table, ecs_id(Velocity)); + + Position *p = ecs_table_get_column(table, pos_id, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + + Velocity *v = ecs_table_get_column(table, vel_id, 1); + test_assert(v != NULL); + test_int(v[0].x, 2); + test_int(v[0].y, 3); + + ecs_fini(world); +} + +void Table_get_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e2, Velocity, {2, 3}); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + Position *p = ecs_table_get_id(world, table, ecs_id(Position), 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + + Velocity *v = ecs_table_get_id(world, table, ecs_id(Velocity), 0); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_int(v[1].x, 2); + test_int(v[1].y, 3); + + ecs_fini(world); +} + +void Table_get_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e2, Velocity, {2, 3}); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + Position *p = ecs_table_get(world, table, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + + Velocity *v = ecs_table_get(world, table, Velocity, 0); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_int(v[1].x, 2); + test_int(v[1].y, 3); + + ecs_fini(world); +} + +void Table_get_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new(world); + ecs_set_pair(world, e1, Position, Tgt, {10, 20}); + ecs_set_pair(world, e1, Velocity, Tgt, {1, 2}); + + ecs_entity_t e2 = ecs_new(world); + ecs_set_pair(world, e2, Position, Tgt, {20, 30}); + ecs_set_pair(world, e2, Velocity, Tgt, {2, 3}); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + Position *p = ecs_table_get_pair(world, table, Position, Tgt, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + + Velocity *v = ecs_table_get_pair(world, table, Velocity, Tgt, 0); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_int(v[1].x, 2); + test_int(v[1].y, 3); + + ecs_fini(world); +} + +void Table_get_from_stage(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e2, Velocity, {2, 3}); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + ecs_world_t *stage = ecs_get_stage(world, 0); + test_assert(stage != NULL); + test_assert(stage != world); + + Position *p = ecs_table_get(stage, table, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + + Velocity *v = ecs_table_get(stage, table, Velocity, 0); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + test_int(v[1].x, 2); + test_int(v[1].y, 3); + + ecs_fini(world); +} + +void Table_get_depth(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e2); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, e3); + + test_int(1, ecs_table_get_depth(world, ecs_get_table(world, e2), EcsChildOf)); + test_int(2, ecs_table_get_depth(world, ecs_get_table(world, e3), EcsChildOf)); + test_int(3, ecs_table_get_depth(world, ecs_get_table(world, e4), EcsChildOf)); + + ecs_fini(world); +} + +void Table_get_depth_non_acyclic(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); + + test_expect_abort(); + ecs_table_get_depth(world, ecs_get_table(world, e1), Rel); +} + +void Table_get_depth_2_paths(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, e2); + ecs_add_pair(world, e4, EcsIsA, e3); + + test_int(1, ecs_table_get_depth(world, ecs_get_table(world, e2), EcsIsA)); + test_int(2, ecs_table_get_depth(world, ecs_get_table(world, e3), EcsIsA)); + test_int(3, ecs_table_get_depth(world, ecs_get_table(world, e4), EcsIsA)); + + ecs_fini(world); +} + +void Table_get_column_size(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_entity_t ecs_id(Mass) = ecs_component(world, { + .entity = ecs_new(world), + .type.size = 4, + .type.alignment = 4 + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_add(world, e1, Tag); + ecs_set(world, e1, Mass, {1}); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + test_uint(8, ecs_table_get_column_size(table, 0)); + test_uint(4, ecs_table_get_column_size(table, 1)); + + ecs_fini(world); +} + +void Table_has_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Foo); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + test_assert(ecs_table_has_id(world, table, Foo)); + test_assert(!ecs_table_has_id(world, table, Tag)); + + ecs_fini(world); +} + +void Table_has_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, Rel, Tgt); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + test_assert(ecs_table_has_id(world, table, ecs_pair(Rel, Tgt))); + test_assert(!ecs_table_has_id(world, table, Tag)); + + ecs_fini(world); +} + +void Table_has_wildcard_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, Rel, Tgt); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + test_assert(ecs_table_has_id(world, table, ecs_pair(Rel, EcsWildcard))); + test_assert(!ecs_table_has_id(world, table, Tag)); + + ecs_fini(world); +} + +void Table_has_any_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, Rel, Tgt); + + ecs_table_t *table = ecs_get_table(world, e1); + test_assert(table != NULL); + + test_assert(ecs_table_has_id(world, table, ecs_pair(Rel, EcsAny))); + test_assert(!ecs_table_has_id(world, table, Tag)); + + ecs_fini(world); +} + +void Table_clear_table_kills_entities(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + ecs_table_t *table = ecs_get_table(world, e1); + + ecs_table_clear_entities(world, table); + + test_assert(!ecs_is_alive(world, e1)); + test_assert(!ecs_is_alive(world, e2)); + test_int(0, ecs_table_count(table)); + + ecs_fini(world); +} + +void Table_clear_table_add_new(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t ecs_id(Position) = ecs_component(world, { + .entity = ecs_new(world), + .type.size = ECS_SIZEOF(Position), + .type.alignment = ECS_ALIGNOF(Position) + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + ecs_table_t *table = ecs_get_table(world, e1); + + ecs_table_clear_entities(world, table); + + ecs_id(Position) = ecs_component(world, { + .entity = ecs_new(world), + .type.size = ECS_SIZEOF(Position), + .type.alignment = ECS_ALIGNOF(Position) + }); + + ecs_entity_t e3 = ecs_new(world); + ecs_add(world, e3, Position); + ecs_entity_t e4 = ecs_new(world); + ecs_add(world, e4, Position); + test_assert(ecs_is_alive(world, e3)); + test_assert(ecs_is_alive(world, e4)); + + ecs_table_t *table_e3 = ecs_get_table(world, e3); + ecs_table_t *table_e4 = ecs_get_table(world, e4); + + test_assert(table_e3 == table_e4); + test_int(2, ecs_table_count(table_e3)); + + ecs_fini(world); +} + +void Table_clear_table_check_size(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + ecs_table_t *table = ecs_get_table(world, e1); + const int32_t size = ecs_table_size(table); + + ecs_table_clear_entities(world, table); + + test_int(size, ecs_table_size(table)); + test_int(0, ecs_table_count(table)); + + ecs_fini(world); +} + + +void Table_clear_table_twice_check_size(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + ecs_table_t *table = ecs_get_table(world, e1); + const int32_t size = ecs_table_size(table); + + ecs_table_clear_entities(world, table); + ecs_table_clear_entities(world, table); + + test_int(size, ecs_table_size(table)); + test_int(0, ecs_table_count(table)); + + ecs_fini(world); +} + +static int on_remove_position = 0; + +static void ecs_on_remove(Position)(ecs_iter_t *it) { + test_assert(it->count >= 1); + test_assert(it->event == EcsOnRemove); + + Position *p = ecs_field(it, Position, 0); + for (int i = 0; i < it->count; i ++) { + on_remove_position ++; + test_int(p[i].x, 10); + test_int(p[i].y, 20); + } +} + +void Table_clear_table_on_remove_hooks(void) { + ecs_world_t *world = ecs_mini(); + on_remove_position = 0; + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .on_remove = ecs_on_remove(Position) + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_table_t *table = ecs_get_table(world, e1); + ecs_table_clear_entities(world, table); + + test_int(on_remove_position, 3); + + ecs_fini(world); +} + +static +void Observer(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); +} + +void Table_clear_table_on_remove_observer(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{EcsWildcard}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + test_int(ctx.invoked, 0); + + ecs_table_t *table = ecs_get_table(world, e1); + ecs_table_clear_entities(world, table); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 2); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/Trigger.c b/vendors/flecs/test/core/src/Trigger.c similarity index 83% rename from vendors/flecs/test/api/src/Trigger.c rename to vendors/flecs/test/core/src/Trigger.c index c2d8c3f2d..b22443166 100644 --- a/vendors/flecs/test/api/src/Trigger.c +++ b/vendors/flecs/test/core/src/Trigger.c @@ -1,4 +1,4 @@ -#include +#include static int on_remove_count = 0; @@ -15,7 +15,7 @@ void Trigger_w_value(ecs_iter_t *it) { test_assert(it->entities != NULL); test_assert(it->entities[0] != 0); - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_int(p->x, 10); test_int(p->y, 20); } @@ -29,7 +29,7 @@ void Trigger_w_value_from_entity(ecs_iter_t *it) { test_assert(it->entities[0] == 0); test_assert(it->sources[0] != 0); - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_int(p->x, 10); test_int(p->y, 20); } @@ -40,7 +40,7 @@ void Trigger_w_value_instanced(ecs_iter_t *it) { test_assert(it->count > 1); - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_int(p->x, 10); test_int(p->y, 20); } @@ -59,7 +59,7 @@ static void Trigger_n_w_values(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_assert(it->entities != NULL); @@ -115,14 +115,14 @@ void Trigger_on_add_trigger_before_table(void) { /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); /* Create entity/table after trigger, should invoke trigger */ - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); @@ -146,19 +146,19 @@ void Trigger_on_add_trigger_after_table(void) { /* Create entity/before trigger */ ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {TagA} + .add = ecs_ids(TagA) }); /* Create trigger after table, should send notification to table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); @@ -183,14 +183,14 @@ void Trigger_on_remove_trigger_before_table(void) { /* Create trigger after table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); /* Create entity/table after trigger, should invoke trigger */ - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -217,13 +217,13 @@ void Trigger_on_remove_trigger_after_table(void) { ECS_TAG(world, TagA); /* Create entity/before trigger */ - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); /* Create trigger after table, should send notification to table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx @@ -252,13 +252,13 @@ void Trigger_on_add_tag(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); @@ -282,13 +282,13 @@ void Trigger_on_add_component(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 1); @@ -313,13 +313,64 @@ void Trigger_on_add_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = EcsWildcard, + .query.terms[0].id = EcsWildcard, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); + test_assert(e != 0); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, TagA); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagA); + + ecs_os_zeromem(&ctx); + + ecs_add_id(world, e, TagB); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, TagB); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e); + test_int(ctx.c[0][0], TagB); + + ecs_fini(world); +} + +void Trigger_on_add_wildcard_after_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t dummy = ecs_new_w(world, TagA); + ecs_delete(world, dummy); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms[0].id = EcsWildcard, + .events = {EcsOnAdd}, + .callback = Trigger, + .ctx = &ctx + }); + + test_int(ctx.invoked, 0); + + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); @@ -359,14 +410,14 @@ void Trigger_on_add_pair(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Pred, Obj), + .query.terms[0].id = ecs_pair(Pred, Obj), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Pred, Obj)} + .add = ecs_ids(ecs_pair(Pred, Obj)) }); test_assert(e != 0); @@ -393,14 +444,14 @@ void Trigger_on_add_pair_obj_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Pred, EcsWildcard), + .query.terms[0].id = ecs_pair(Pred, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Pred, ObjA)} + .add = ecs_ids(ecs_pair(Pred, ObjA)) }); test_assert(e != 0); @@ -442,14 +493,14 @@ void Trigger_on_add_pair_pred_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, Obj), + .query.terms[0].id = ecs_pair(EcsWildcard, Obj), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(PredA, Obj)} + .add = ecs_ids(ecs_pair(PredA, Obj)) }); test_assert(e != 0); @@ -490,14 +541,14 @@ void Trigger_on_add_pair_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), + .query.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Pred, Obj)} + .add = ecs_ids(ecs_pair(Pred, Obj)) }); test_assert(e != 0); @@ -524,7 +575,7 @@ void Trigger_on_add_any(void) { Probe *ctx_any = ecs_os_calloc_t(Probe); ecs_entity_t t_any = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = EcsAny, + .query.terms[0].id = EcsAny, .events = {EcsOnAdd}, .callback = Trigger, .ctx = ctx_any @@ -532,7 +583,7 @@ void Trigger_on_add_any(void) { Probe *ctx_wc = ecs_os_calloc_t(Probe); ecs_entity_t t_wc = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = EcsWildcard, + .query.terms[0].id = EcsWildcard, .events = {EcsOnAdd}, .callback = Trigger, .ctx = ctx_wc @@ -540,7 +591,7 @@ void Trigger_on_add_any(void) { Probe *ctx_wc_pair = ecs_os_calloc_t(Probe); ecs_entity_t t_wc_pair = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), + .query.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = ctx_wc_pair @@ -550,7 +601,7 @@ void Trigger_on_add_any(void) { ecs_os_zeromem(ctx_wc); ecs_os_zeromem(ctx_wc_pair); - ecs_new(world, Tag); + ecs_new_w(world, Tag); test_int(ctx_any->invoked, 1); test_int(ctx_any->count, 1); test_int(ctx_any->system, t_any); @@ -593,13 +644,13 @@ void Trigger_on_remove_tag(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -626,13 +677,13 @@ void Trigger_on_remove_component(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -660,14 +711,14 @@ void Trigger_on_remove_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = EcsWildcard, + .query.terms[0].id = EcsWildcard, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {TagA, TagB} + .add = ecs_ids(TagA, TagB) }); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -711,14 +762,14 @@ void Trigger_on_remove_pair(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Pred, Obj), + .query.terms[0].id = ecs_pair(Pred, Obj), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Pred, Obj)} + .add = ecs_ids(ecs_pair(Pred, Obj)) }); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -748,14 +799,14 @@ void Trigger_on_remove_pair_obj_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Pred, EcsWildcard), + .query.terms[0].id = ecs_pair(Pred, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Pred, ObjA), ecs_pair(Pred, ObjB)} + .add = ecs_ids(ecs_pair(Pred, ObjA), ecs_pair(Pred, ObjB)) }); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -802,14 +853,14 @@ void Trigger_on_remove_pair_pred_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, Obj), + .query.terms[0].id = ecs_pair(EcsWildcard, Obj), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(PredA, Obj), ecs_pair(PredB, Obj)} + .add = ecs_ids(ecs_pair(PredA, Obj), ecs_pair(PredB, Obj)) }); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -853,14 +904,14 @@ void Trigger_on_remove_pair_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), + .query.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Pred, Obj)} + .add = ecs_ids(ecs_pair(Pred, Obj)) }); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -893,14 +944,14 @@ void Trigger_wildcard_pair_w_pred_component(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(ecs_id(Position), EcsWildcard), + .query.terms[0].id = ecs_pair(ecs_id(Position), EcsWildcard), .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); test_assert(t != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_int(ctx.invoked, 0); ecs_set_pair(world, e, Position, ObjA, {10, 20}); @@ -916,7 +967,7 @@ void Trigger_wildcard_pair_w_pred_component(void) { /* Change existing component without triggering OnSet as the callback * expects value {10, 20}, then add a new component with {10, 20} */ - Position *p = ecs_get_mut_pair(world, e, Position, ObjA); + Position *p = ecs_ensure_pair(world, e, Position, ObjA); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); @@ -948,17 +999,17 @@ void Trigger_wildcard_pair_w_obj_component(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, ecs_id(Position)), + .query.terms[0].id = ecs_pair(EcsWildcard, ecs_id(Position)), .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); test_assert(t != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_int(ctx.invoked, 0); - ecs_set_pair_object(world, e, RelA, Position, {10, 20}); + ecs_set_pair_second(world, e, RelA, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); @@ -971,7 +1022,7 @@ void Trigger_wildcard_pair_w_obj_component(void) { /* Change existing component without triggering OnSet as the callback * expects value {10, 20}, then add a new component with {10, 20} */ - Position *p = ecs_get_mut_pair_second(world, e, RelA, Position); + Position *p = ecs_ensure_pair_second(world, e, RelA, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); @@ -980,7 +1031,7 @@ void Trigger_wildcard_pair_w_obj_component(void) { ecs_os_zeromem(&ctx); - ecs_set_pair_object(world, e, RelB, Position, {10, 20}); + ecs_set_pair_second(world, e, RelB, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); @@ -1002,13 +1053,13 @@ void Trigger_on_set_component(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -1035,13 +1086,13 @@ void Trigger_on_set_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = EcsWildcard, + .query.terms[0].id = EcsWildcard, .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -1070,14 +1121,14 @@ void Trigger_on_set_pair(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(ecs_id(Position), Obj), + .query.terms[0].id = ecs_pair(ecs_id(Position), Obj), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(ecs_id(Position), Obj)} + .add = ecs_ids(ecs_pair(ecs_id(Position), Obj)) }); test_assert(e != 0); @@ -1107,14 +1158,14 @@ void Trigger_on_set_pair_w_obj_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(ecs_id(Position), EcsWildcard), + .query.terms[0].id = ecs_pair(ecs_id(Position), EcsWildcard), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(ecs_id(Position), Obj)} + .add = ecs_ids(ecs_pair(ecs_id(Position), Obj)) }); test_assert(e != 0); @@ -1144,14 +1195,14 @@ void Trigger_on_set_pair_pred_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, Obj), + .query.terms[0].id = ecs_pair(EcsWildcard, Obj), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(ecs_id(Position), Obj)} + .add = ecs_ids(ecs_pair(ecs_id(Position), Obj)) }); test_assert(e != 0); @@ -1181,7 +1232,7 @@ void Trigger_on_set_pair_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), + .query.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx @@ -1191,7 +1242,7 @@ void Trigger_on_set_pair_wildcard(void) { ecs_os_zeromem(&ctx); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(ecs_id(Position), Obj)} + .add = ecs_ids(ecs_pair(ecs_id(Position), Obj)) }); test_assert(e != 0); @@ -1220,13 +1271,13 @@ void Trigger_on_add_remove(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd, EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); @@ -1265,13 +1316,13 @@ void Trigger_on_set_component_after_modified(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -1299,13 +1350,13 @@ void Trigger_un_set_component(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), - .events = {EcsUnSet}, + .query.terms[0].id = ecs_id(Position), + .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -1315,7 +1366,7 @@ void Trigger_un_set_component(void) { test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); - test_int(ctx.event, EcsUnSet); + test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); @@ -1333,13 +1384,13 @@ void Trigger_un_set_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = EcsWildcard, - .events = {EcsUnSet}, + .query.terms[0].id = EcsWildcard, + .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -1349,7 +1400,7 @@ void Trigger_un_set_wildcard(void) { test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); - test_int(ctx.event, EcsUnSet); + test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); @@ -1371,14 +1422,14 @@ void Trigger_un_set_pair(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, Obj), - .events = {EcsUnSet}, + .query.terms[0].id = ecs_pair(Rel, Obj), + .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e != 0); @@ -1389,7 +1440,7 @@ void Trigger_un_set_pair(void) { test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); - test_int(ctx.event, EcsUnSet); + test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(Rel, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); @@ -1411,14 +1462,14 @@ void Trigger_un_set_pair_w_obj_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), - .events = {EcsUnSet}, + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), + .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e != 0); @@ -1429,7 +1480,7 @@ void Trigger_un_set_pair_w_obj_wildcard(void) { test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); - test_int(ctx.event, EcsUnSet); + test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(Rel, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); @@ -1451,14 +1502,14 @@ void Trigger_un_set_pair_pred_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, Obj), - .events = {EcsUnSet}, + .query.terms[0].id = ecs_pair(EcsWildcard, Obj), + .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e != 0); @@ -1469,7 +1520,7 @@ void Trigger_un_set_pair_pred_wildcard(void) { test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); - test_int(ctx.event, EcsUnSet); + test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(Rel, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); @@ -1491,14 +1542,14 @@ void Trigger_un_set_pair_wildcard(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), - .events = {EcsUnSet}, + .query.terms[0].id = ecs_pair(EcsWildcard, EcsWildcard), + .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {ecs_pair(Rel, Obj)} + .add = ecs_ids(ecs_pair(Rel, Obj)) }); test_assert(e != 0); @@ -1509,7 +1560,7 @@ void Trigger_un_set_pair_wildcard(void) { test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); - test_int(ctx.event, EcsUnSet); + test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_pair(Rel, Obj)); test_int(ctx.term_count, 1); test_null(ctx.param); @@ -1530,14 +1581,14 @@ void Trigger_on_add_not_tag(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].oper = EcsNot, + .query.terms[0].id = TagA, + .query.terms[0].oper = EcsNot, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -1570,14 +1621,14 @@ void Trigger_on_remove_not_tag(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].oper = EcsNot, + .query.terms[0].id = TagA, + .query.terms[0].oper = EcsNot, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); test_int(ctx.count, 1); @@ -1603,20 +1654,22 @@ void Trigger_on_add_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].src.flags = EcsUp, + .query.terms[0].id = TagA, + .query.terms[0].src.id = EcsUp, + .query.terms[0].trav = EcsIsA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t base_no_comp = ecs_new_id(world); + ecs_entity_t base_no_comp = ecs_new(world); test_int(ctx.invoked, 0); - ecs_entity_t base = ecs_new(world, TagA); + ecs_entity_t base = ecs_new_w(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base_no_comp); @@ -1650,20 +1703,22 @@ void Trigger_on_add_superset_2_levels(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].src.flags = EcsUp, + .query.terms[0].id = TagA, + .query.terms[0].src.id = EcsUp, + .query.terms[0].trav = EcsIsA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t base_no_comp = ecs_new_id(world); + ecs_entity_t base_no_comp = ecs_new(world); test_int(ctx.invoked, 0); - ecs_entity_t base_of_base = ecs_new(world, TagA); + ecs_entity_t base_of_base = ecs_new_w(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t base = ecs_new_w_pair(world, EcsIsA, base_of_base); @@ -1701,20 +1756,22 @@ void Trigger_on_remove_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].src.flags = EcsUp, + .query.terms[0].id = TagA, + .query.terms[0].src.id = EcsUp, + .query.terms[0].trav = EcsIsA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t base_no_comp = ecs_new_id(world); + ecs_entity_t base_no_comp = ecs_new(world); test_int(ctx.invoked, 0); - ecs_entity_t base = ecs_new(world, TagA); + ecs_entity_t base = ecs_new_w(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base_no_comp); @@ -1753,20 +1810,20 @@ void Trigger_on_add_superset_childof(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = TagA, - .src.trav = EcsChildOf, - .src.flags = EcsUp + .trav = EcsChildOf, + .src.id = EcsUp }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t base_no_comp = ecs_new_id(world); + ecs_entity_t base_no_comp = ecs_new(world); test_int(ctx.invoked, 0); - ecs_entity_t base = ecs_new(world, TagA); + ecs_entity_t base = ecs_new_w(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, base_no_comp); @@ -1802,20 +1859,20 @@ void Trigger_on_remove_superset_childof(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = TagA, - .src.trav = EcsChildOf, - .src.flags = EcsUp + .trav = EcsChildOf, + .src.id = EcsUp }, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t base_no_comp = ecs_new_id(world); + ecs_entity_t base_no_comp = ecs_new(world); test_int(ctx.invoked, 0); - ecs_entity_t base = ecs_new(world, TagA); + ecs_entity_t base = ecs_new_w(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, base_no_comp); @@ -1851,20 +1908,22 @@ void Trigger_on_add_self_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].src.flags = EcsSelf | EcsUp, + .query.terms[0].id = TagA, + .query.terms[0].src.id = EcsSelf | EcsUp, + .query.terms[0].trav = EcsIsA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t base_no_comp = ecs_new_id(world); + ecs_entity_t base_no_comp = ecs_new(world); test_int(ctx.invoked, 0); - ecs_entity_t base = ecs_new(world, TagA); + ecs_entity_t base = ecs_new_w(world, TagA); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); @@ -1924,20 +1983,22 @@ void Trigger_on_remove_self_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].src.flags = EcsSelf | EcsUp, + .query.terms[0].id = TagA, + .query.terms[0].src.id = EcsSelf | EcsUp, + .query.terms[0].trav = EcsIsA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t base_no_comp = ecs_new_id(world); + ecs_entity_t base_no_comp = ecs_new(world); test_int(ctx.invoked, 0); - ecs_entity_t base = ecs_new(world, TagA); + ecs_entity_t base = ecs_new_w(world, TagA); test_int(ctx.invoked, 0); ecs_os_zeromem(&ctx); @@ -2002,13 +2063,13 @@ void Trigger_add_twice(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); @@ -2038,13 +2099,13 @@ void Trigger_remove_twice(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -2076,13 +2137,13 @@ void Trigger_on_remove_w_clear(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -2109,13 +2170,13 @@ void Trigger_on_remove_w_delete(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -2142,13 +2203,13 @@ void Trigger_on_remove_w_world_fini(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 0); @@ -2173,13 +2234,13 @@ void Trigger_on_add_w_clone(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); @@ -2218,13 +2279,13 @@ void Trigger_add_in_trigger(void) { ECS_TAG(world, TagB); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerAdd, .ctx = &TagB }); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_assert(ecs_has_id(world, e, TagB)); @@ -2239,14 +2300,14 @@ void Trigger_remove_in_trigger(void) { ECS_TAG(world, TagB); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerRemove, .ctx = &TagB }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {TagA, TagB} + .add = ecs_ids(TagA, TagB) }); test_assert(e != 0); @@ -2262,13 +2323,13 @@ void Trigger_clear_in_trigger(void) { ECS_TAG(world, TagB); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerClear, }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {TagA, TagB} + .add = ecs_ids(TagA, TagB) }); test_assert(e != 0); @@ -2285,13 +2346,13 @@ void Trigger_delete_in_trigger(void) { ECS_TAG(world, TagB); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerDelete, }); ecs_entity_t e = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = {TagA, TagB} + .add = ecs_ids(TagA, TagB) }); test_assert(e != 0); @@ -2309,14 +2370,14 @@ void Trigger_trigger_w_named_entity(void) { Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = ecs_entity(world, {.name = "MyTrigger"}), - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); /* Create entity/table after trigger, should invoke trigger */ - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_int(ctx.invoked, 1); @@ -2325,10 +2386,10 @@ void Trigger_trigger_w_named_entity(void) { } void RemoveSelf(ecs_iter_t *it) { - Self *s = ecs_field(it, Self, 1); + Self *s = ecs_field(it, Self, 0); test_assert(s != NULL); - ecs_id_t ecs_id(Self) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Self) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -2352,7 +2413,7 @@ void Trigger_on_remove_tree(void) { ECS_OBSERVER(world, RemoveSelf, EcsOnRemove, Self); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, root); ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e1); @@ -2401,22 +2462,22 @@ void Trigger_set_get_context(void) { int32_t ctx_a, ctx_b; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .entity = ecs_entity(world, {.name = "MyTrigger"}), - .filter.terms[0].id = Tag, + .entity = ecs_entity(world, { .name = "MyTrigger" }), + .query.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_a }); test_assert(t != 0); - test_assert(ecs_observer_get_ctx(world, t) == &ctx_a); + test_assert(ecs_observer_get(world, t)->ctx == &ctx_a); test_assert(ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = t, .ctx = &ctx_b }) == t); - test_assert(ecs_observer_get_ctx(world, t) == &ctx_b); + test_assert(ecs_observer_get(world, t)->ctx == &ctx_b); ecs_fini(world); } @@ -2429,21 +2490,21 @@ void Trigger_set_get_binding_context(void) { int32_t ctx_a, ctx_b; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = ecs_entity(world, {.name = "MyTrigger"}), - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = Trigger, - .binding_ctx = &ctx_a + .callback_ctx = &ctx_a }); test_assert(t != 0); - test_assert(ecs_observer_get_binding_ctx(world, t) == &ctx_a); + test_assert(ecs_observer_get(world, t)->callback_ctx == &ctx_a); test_assert(ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = t, - .binding_ctx = &ctx_b + .callback_ctx = &ctx_b }) == t); - test_assert(ecs_observer_get_binding_ctx(world, t) == &ctx_b); + test_assert(ecs_observer_get(world, t)->callback_ctx == &ctx_b); ecs_fini(world); } @@ -2468,18 +2529,18 @@ void Trigger_delete_trigger_w_delete_ctx(void) { ECS_TAG(world, Tag); ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_value, .ctx_free = ctx_free, - .binding_ctx = &binding_ctx_value, - .binding_ctx_free = binding_ctx_free + .callback_ctx = &binding_ctx_value, + .callback_ctx_free = binding_ctx_free }); test_assert(t != 0); - test_assert(ecs_observer_get_ctx(world, t) == &ctx_value); - test_assert(ecs_observer_get_binding_ctx(world, t) == &binding_ctx_value); + test_assert(ecs_observer_get(world, t)->ctx == &ctx_value); + test_assert(ecs_observer_get(world, t)->callback_ctx == &binding_ctx_value); ecs_delete(world, t); @@ -2496,15 +2557,15 @@ void Trigger_trigger_w_index(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, - .term_index = 50, + .query.terms[0].id = Tag, + .term_index_ = 50, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); test_assert(t != 0); - ecs_new(world, Tag); + ecs_new_w(world, Tag); test_int(ctx.invoked, 1); test_int(ctx.term_index, 50); @@ -2525,14 +2586,14 @@ void Trigger_iter_type_set(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = TypeTrigger, .ctx = &ctx }); test_assert(t != 0); - ecs_new(world, Tag); + ecs_new_w(world, Tag); test_assert(trigger_table != NULL); const ecs_type_t *type = ecs_table_get_type(trigger_table); @@ -2544,7 +2605,7 @@ void Trigger_iter_type_set(void) { void TriggerReadonly(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); - test_bool(ecs_field_is_readonly(it, 1), true); + test_bool(ecs_field_is_readonly(it, 0), true); } void Trigger_readonly_term(void) { @@ -2554,13 +2615,13 @@ void Trigger_readonly_term(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { TagA, .inout = EcsIn }, + .query.terms[0] = { TagA, .inout = EcsIn }, .events = {EcsOnAdd}, .callback = TriggerReadonly, .ctx = &ctx }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, TagA); @@ -2587,20 +2648,20 @@ void Trigger_trigger_on_prefab(void) { Probe ctx_2 = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { TagA }, + .query.terms[0] = { TagA }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_1 }); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }, { EcsPrefab, .oper = EcsOptional }}, + .query.terms = {{ TagA }, { EcsPrefab, .oper = EcsOptional }}, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_2 }); - ecs_new(world, TagA); + ecs_new_w(world, TagA); test_int(ctx_1.invoked, 1); test_int(ctx_2.invoked, 1); ecs_os_zeromem(&ctx_1); @@ -2633,20 +2694,20 @@ void Trigger_trigger_on_disabled(void) { Probe ctx_2 = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { TagA }, + .query.terms[0] = { TagA }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_1 }); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms = {{ TagA }, { EcsDisabled, .oper = EcsOptional }}, + .query.terms = {{ TagA }, { EcsDisabled, .oper = EcsOptional }}, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_2 }); - ecs_new(world, TagA); + ecs_new_w(world, TagA); test_int(ctx_1.invoked, 1); test_int(ctx_2.invoked, 1); ecs_os_zeromem(&ctx_1); @@ -2676,7 +2737,7 @@ void Trigger_trigger_on_prefab_tag(void) { Probe ctx_1 = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { EcsPrefab }, + .query.terms[0] = { EcsPrefab }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_1 @@ -2694,7 +2755,7 @@ void Trigger_trigger_on_disabled_tag(void) { Probe ctx_1 = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { EcsDisabled }, + .query.terms[0] = { EcsDisabled }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_1 @@ -2712,13 +2773,13 @@ void Trigger_trigger_cleanup_2_w_self_super_id(void) { ECS_TAG(world, Tag); ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { .id = Tag, .src.flags = EcsUp }, + .query.terms[0] = { .id = Tag, .src.id = EcsUp }, .events = { EcsOnAdd }, .callback = Trigger }); ecs_entity_t t2 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { Tag }, + .query.terms[0] = { Tag }, .events = { EcsOnAdd }, .callback = Trigger }); @@ -2737,9 +2798,9 @@ void Trigger_on_add_yield_existing(void) { ECS_TAG(world, Tag); /* Create entities before trigger */ - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); test_assert(e1 != 0); test_assert(e2 != 0); @@ -2747,7 +2808,7 @@ void Trigger_on_add_yield_existing(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx, @@ -2769,7 +2830,7 @@ void Trigger_on_add_yield_existing(void) { // Ensure normal triggering also still works ecs_os_zeromem(&ctx); - ecs_new(world, Tag); + ecs_new_w(world, Tag); test_int(ctx.invoked, 1); ecs_fini(world); @@ -2782,9 +2843,9 @@ void Trigger_on_add_yield_existing_2_tables(void) { ECS_TAG(world, TagB); /* Create entities before trigger */ - ecs_entity_t e1 = ecs_new(world, TagA); - ecs_entity_t e2 = ecs_new(world, TagA); - ecs_entity_t e3 = ecs_new(world, TagA); + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); test_assert(e1 != 0); test_assert(e2 != 0); @@ -2794,7 +2855,7 @@ void Trigger_on_add_yield_existing_2_tables(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx, @@ -2817,7 +2878,7 @@ void Trigger_on_add_yield_existing_2_tables(void) { // Ensure normal triggering also still works ecs_os_zeromem(&ctx); - ecs_new(world, TagA); + ecs_new_w(world, TagA); test_int(ctx.invoked, 1); ecs_fini(world); @@ -2841,7 +2902,7 @@ void Trigger_on_add_yield_existing_wildcard_pair(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx, @@ -2876,9 +2937,9 @@ void Trigger_on_set_yield_existing(void) { ECS_COMPONENT(world, Position); /* Create entities before trigger */ - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {50, 60}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); test_assert(e1 != 0); test_assert(e2 != 0); @@ -2886,7 +2947,7 @@ void Trigger_on_set_yield_existing(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = Trigger_n_w_values, .ctx = &ctx, @@ -2908,7 +2969,7 @@ void Trigger_on_set_yield_existing(void) { // Ensure normal triggering also still works ecs_os_zeromem(&ctx); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_set(world, e, Position, {10, 20}); @@ -2925,14 +2986,14 @@ void Trigger_filter_term(void) { Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { .id = ecs_id(Position), .inout = EcsInOutNone }, + .query.terms[0] = { .id = ecs_id(Position), .inout = EcsInOutNone }, .events = {EcsOnSet}, .callback = Trigger_w_filter_term, .ctx = &ctx }); /* Create entities before trigger */ - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(e1 != 0); test_int(ctx.invoked, 1); @@ -2958,7 +3019,7 @@ void Trigger_on_add_remove_after_exclusive_add(void) { Probe ctx_add = {0}; ecs_entity_t t_add = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx_add @@ -2966,13 +3027,13 @@ void Trigger_on_add_remove_after_exclusive_add(void) { Probe ctx_remove = {0}; ecs_entity_t t_remove = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx_remove }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, ObjA); test_assert( ecs_has_pair(world, e, Rel, ObjA)); @@ -3018,10 +3079,13 @@ void Trigger_on_add_base(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ + .query.terms[0].id = TagA, /* Implicitly also listens to IsA */ .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx @@ -3058,10 +3122,13 @@ void Trigger_on_remove_base(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ + .query.terms[0].id = TagA, /* Implicitly also listens to IsA */ .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx @@ -3102,10 +3169,13 @@ void Trigger_on_set_base(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ + .query.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx @@ -3153,11 +3223,14 @@ void Trigger_on_unset_base(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ - .events = {EcsUnSet}, + .query.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ + .events = {EcsOnRemove}, .callback = Trigger_w_value, .ctx = &ctx }); @@ -3174,7 +3247,7 @@ void Trigger_on_unset_base(void) { test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, t); - test_int(ctx.event, EcsUnSet); + test_int(ctx.event, EcsOnRemove); test_int(ctx.event_id, ecs_id(Position)); test_int(ctx.term_count, 1); test_null(ctx.param); @@ -3197,10 +3270,14 @@ void Trigger_on_add_base_superset_trigger(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].src.flags = EcsUp, + .query.terms[0].id = TagA, + .query.terms[0].src.id = EcsUp, + .query.terms[0].trav = EcsIsA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx @@ -3236,10 +3313,14 @@ void Trigger_on_add_base_superset_trigger_2_lvls(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, - .filter.terms[0].src.flags = EcsUp, + .query.terms[0].id = TagA, + .query.terms[0].src.id = EcsUp, + .query.terms[0].trav = EcsIsA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx @@ -3248,6 +3329,7 @@ void Trigger_on_add_base_superset_trigger_2_lvls(void) { ecs_entity_t base_of_base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_add_pair(world, base, EcsIsA, base_of_base); + test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); @@ -3279,10 +3361,13 @@ void Trigger_on_add_base_2_entities(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ + .query.terms[0].id = TagA, /* Implicitly also listens to IsA */ .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx @@ -3321,11 +3406,14 @@ void Trigger_on_add_base_2_entities_filter(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ - .filter.terms[0].inout = EcsInOutNone, + .query.terms[0].id = TagA, /* Implicitly also listens to IsA */ + .query.terms[0].inout = EcsInOutNone, .events = {EcsOnAdd}, .callback = Trigger_w_filter_term, .ctx = &ctx @@ -3358,69 +3446,19 @@ void Trigger_on_add_base_2_entities_filter(void) { test_int(ctx.invoked, 0); } -void Trigger_on_set_base_w_value_2_entities(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - /* Create trigger before table */ - Probe ctx = {0}; - ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ - .events = {EcsOnSet}, - .callback = Trigger_w_value, - .ctx = &ctx - }); - - ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); - ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); - test_int(ctx.invoked, 0); - - ecs_set(world, base, Position, {10, 20}); - test_int(ctx.invoked, 2); - test_int(ctx.count, 2); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnSet); - test_int(ctx.event_id, ecs_id(Position)); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - - ecs_os_zeromem(&ctx); - - ecs_set(world, base, Position, {10, 20}); - test_int(ctx.invoked, 2); - test_int(ctx.count, 2); - test_int(ctx.system, t); - test_int(ctx.event, EcsOnSet); - test_int(ctx.event_id, ecs_id(Position)); - test_int(ctx.term_count, 1); - test_null(ctx.param); - test_int(ctx.e[0], e1); - test_int(ctx.e[1], e2); - - ecs_os_zeromem(&ctx); - - ecs_set(world, base, Velocity, {1, 2}); - test_int(ctx.invoked, 0); - - ecs_fini(world); -} - void Trigger_on_set_base_w_value_2_entities_instanced(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ - .filter.instanced = true, + .query.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ .events = {EcsOnSet}, .callback = Trigger_w_value_instanced, .ctx = &ctx @@ -3469,10 +3507,13 @@ void Trigger_on_add_base_w_override(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, /* Implicitly also listens to IsA */ + .query.terms[0].id = TagA, /* Implicitly also listens to IsA */ .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx @@ -3524,10 +3565,13 @@ void Trigger_on_set_base_w_override(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ + .query.terms[0].id = ecs_id(Position), /* Implicitly also listens to IsA */ .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx @@ -3579,14 +3623,14 @@ void Trigger_entity_source_1_trigger(void) { ECS_COMPONENT(world, Position); - ecs_entity_t subj = ecs_new_id(world); - ecs_entity_t dummy = ecs_new_id(world); + ecs_entity_t subj = ecs_new(world); + ecs_entity_t dummy = ecs_new(world); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), - .filter.terms[0].src.id = subj, + .query.terms[0].id = ecs_id(Position), + .query.terms[0].src.id = subj, .events = {EcsOnSet}, .callback = Trigger_w_value_from_entity, .ctx = &ctx @@ -3614,23 +3658,23 @@ void Trigger_entity_source_2_triggers(void) { ECS_COMPONENT(world, Position); - ecs_entity_t subj_a = ecs_new_id(world); - ecs_entity_t subj_b = ecs_new_id(world); - ecs_entity_t dummy = ecs_new_id(world); + ecs_entity_t subj_a = ecs_new(world); + ecs_entity_t subj_b = ecs_new(world); + ecs_entity_t dummy = ecs_new(world); /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), - .filter.terms[0].src.id = subj_a, + .query.terms[0].id = ecs_id(Position), + .query.terms[0].src.id = subj_a, .events = {EcsOnSet}, .callback = Trigger_w_value_from_entity, .ctx = &ctx }); ecs_entity_t t2 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), - .filter.terms[0].src.id = subj_b, + .query.terms[0].id = ecs_id(Position), + .query.terms[0].src.id = subj_b, .events = {EcsOnSet}, .callback = Trigger_w_value_from_entity, .ctx = &ctx @@ -3671,6 +3715,8 @@ void Trigger_entity_source_base_set(void) { ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t subj = ecs_new_w_pair(world, EcsIsA, base); ecs_entity_t dummy = ecs_new_w_pair(world, EcsIsA, base); @@ -3679,8 +3725,8 @@ void Trigger_entity_source_base_set(void) { /* Create trigger before table */ Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), - .filter.terms[0].src.id = subj, + .query.terms[0].id = ecs_id(Position), + .query.terms[0].src.id = subj, .events = {EcsOnSet}, .callback = Trigger_w_value_from_entity, .ctx = &ctx @@ -3710,18 +3756,20 @@ void Trigger_not_from_superset(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, - .filter.terms[0].oper = EcsNot, - .filter.terms[0].src.flags = EcsUp, + .query.terms[0].id = Tag, + .query.terms[0].oper = EcsNot, + .query.terms[0].src.id = EcsUp, + .query.terms[0].trav = EcsIsA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t base = ecs_new(world, Tag); + ecs_entity_t base = ecs_new_w(world, Tag); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx.invoked, 0); @@ -3741,8 +3789,8 @@ void Trigger_create_stresstest(void) { * builds don't detect leaks that are cleaned up @ ecs_fini). */ for (int i = 0; i < 100 * 1000; i ++) { ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, - .filter.terms[0].src.flags = EcsSelf | EcsUp, + .query.terms[0].id = Tag, + .query.terms[0].src.id = EcsSelf | EcsUp, .events = {EcsOnAdd}, .callback = Trigger }); @@ -3767,13 +3815,13 @@ void Trigger_add_non_existing_entity(void) { Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnAdd}, .callback = Trigger_w_new_entity_value, .ctx = &ctx }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, Rel, 1000); test_assert( ecs_has_pair(world, e, Rel, 1000)); @@ -3794,11 +3842,12 @@ void Trigger_on_add_self_trigger_with_add_isa(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, - .filter.terms[0].src.flags = EcsSelf, + .query.terms[0].id = Tag, + .query.terms[0].src.id = EcsSelf, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx @@ -3807,13 +3856,13 @@ void Trigger_on_add_self_trigger_with_add_isa(void) { test_assert(t1 != 0); test_int(ctx.invoked, 0); - ecs_entity_t base = ecs_new_id(world); + ecs_entity_t base = ecs_new(world); ecs_add(world, base, Tag); test_int(ctx.invoked, 1); ctx = (Probe){0}; - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); @@ -3824,11 +3873,12 @@ void Trigger_on_set_self_trigger_with_add_isa(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), - .filter.terms[0].src.flags = EcsSelf, + .query.terms[0].id = ecs_id(Position), + .query.terms[0].src.id = EcsSelf, .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx @@ -3837,12 +3887,12 @@ void Trigger_on_set_self_trigger_with_add_isa(void) { test_assert(t1 != 0); test_int(ctx.invoked, 0); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); test_int(ctx.invoked, 1); ctx = (Probe){0}; - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add_pair(world, e, EcsIsA, base); test_int(ctx.invoked, 0); @@ -3881,16 +3931,19 @@ void Trigger_notify_propagated_twice(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = TriggerTwice, .ctx = &ctx }); test_assert(t1 != 0); - base_ent = ecs_new_id(world); + base_ent = ecs_new(world); inst_ent_a = ecs_new_w_pair(world, EcsIsA, base_ent); ecs_add(world, inst_ent_a, TagB); inst_ent_b = ecs_new_w_pair(world, EcsIsA, base_ent); @@ -3912,14 +3965,17 @@ void Trigger_trigger_superset_wildcard(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); - ecs_entity_t base = ecs_new_id(world); - ecs_entity_t inst = ecs_new_id(world); + ecs_add_pair(world, Rel, EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_entity_t inst = ecs_new(world); ecs_add_pair(world, inst, EcsIsA, base); Probe ctx = {0}; ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), - .filter.terms[0].src.flags = EcsUp, + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), + .query.terms[0].src.id = EcsUp, + .query.terms[0].trav = EcsIsA, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx @@ -3967,7 +4023,7 @@ void Trigger_remove_wildcard_1_id(void) { Probe ctx_a = {0}; ecs_entity_t t_a = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, ObjA), + .query.terms[0].id = ecs_pair(Rel, ObjA), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx_a @@ -3976,7 +4032,7 @@ void Trigger_remove_wildcard_1_id(void) { Probe ctx_b = {0}; ecs_entity_t t_b = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = &ctx_b @@ -4026,7 +4082,7 @@ void Trigger_remove_wildcard_2_ids(void) { Probe *ctx_a = &ctx_array[0]; ecs_entity_t t_a = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, EcsWildcard), + .query.terms[0].id = ecs_pair(Rel, EcsWildcard), .events = {EcsOnRemove}, .callback = Trigger, .ctx = ctx_a @@ -4035,7 +4091,7 @@ void Trigger_remove_wildcard_2_ids(void) { Probe *ctx_b = &ctx_array[1]; ecs_entity_t t_b = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, ObjA), + .query.terms[0].id = ecs_pair(Rel, ObjA), .events = {EcsOnRemove}, .callback = Trigger, .ctx = ctx_b @@ -4044,7 +4100,7 @@ void Trigger_remove_wildcard_2_ids(void) { Probe *ctx_c = &ctx_array[2]; ecs_entity_t t_c = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_pair(Rel, ObjB), + .query.terms[0].id = ecs_pair(Rel, ObjB), .events = {EcsOnRemove}, .callback = Trigger, .ctx = ctx_c @@ -4096,14 +4152,14 @@ void Trigger_on_set_w_tag(void) { Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnSet}, .callback = Trigger, .ctx = &ctx }); test_assert(t1 != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, TagA); @@ -4134,16 +4190,16 @@ static void CreateTriggers(ecs_iter_t *it) { create_trigger_invoked ++; - ctx->first = ecs_new_id(it->world); + ctx->first = ecs_new(it->world); int i; for (i = 0; i < ctx->count - 1; i ++) { - ecs_new_id(it->world); + ecs_new(it->world); } for (i = 0; i < ctx->count; i ++) { ecs_observer_init(it->world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ctx->first + i, + .query.terms[0].id = ctx->first + i, .events = {EcsOnAdd}, .callback = NestedTrigger }); @@ -4160,14 +4216,14 @@ void Trigger_create_triggers_in_trigger(void) { CreateTriggers_ctx ctx = { .count = CreateCount }; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnAdd}, .callback = CreateTriggers, .ctx = &ctx }); test_assert(t1 != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, TagA); @@ -4176,7 +4232,7 @@ void Trigger_create_triggers_in_trigger(void) { int i; for (i = 0; i < CreateCount; i ++) { - ecs_entity_t t = ecs_new_id(world); + ecs_entity_t t = ecs_new(world); ecs_add_id(world, t, ctx.first + i); test_int(nested_trigger_invoked, i + 1); } @@ -4194,8 +4250,9 @@ void Trigger_w_nonzero_value(ecs_iter_t *it) { test_int(it->count, 1); test_assert(it->entities != NULL); test_assert(it->entities[0] != 0); + // test_assert(ecs_field_is_set(it, 0)); - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_assert(p != NULL); } @@ -4203,16 +4260,17 @@ void Trigger_on_add_superset_w_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnAdd}, .callback = Trigger_w_nonzero_value, .ctx = &ctx }); - ecs_entity_t b = ecs_new(world, 0); + ecs_entity_t b = ecs_new(world); ecs_set(world, b, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); @@ -4234,16 +4292,17 @@ void Trigger_on_set_superset_w_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = Trigger_w_value, .ctx = &ctx }); - ecs_entity_t b = ecs_new(world, 0); + ecs_entity_t b = ecs_new(world); ecs_set(world, b, Position, {10, 20}); test_int(ctx.invoked, 1); test_int(ctx.count, 1); @@ -4265,23 +4324,25 @@ void Trigger_on_add_base_superset_w_owned(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Tag), - .src.flags = EcsUp, + .src.id = EcsUp, + .trav = EcsIsA, }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t b = ecs_new(world, 0); + ecs_entity_t b = ecs_new(world); ecs_add(world, b, Tag); test_int(ctx.invoked, 0); - ecs_entity_t i = ecs_new(world, Tag); + ecs_entity_t i = ecs_new_w(world, Tag); test_int(ctx.invoked, 0); ecs_add_pair(world, i, EcsIsA, b); @@ -4297,19 +4358,21 @@ void Trigger_on_add_base_self_superset_w_owned(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Tag), - .src.flags = EcsSelf|EcsUp, + .src.id = EcsSelf|EcsUp, + .trav = EcsIsA, }, .events = {EcsOnAdd}, .callback = Trigger, .ctx = &ctx }); - ecs_entity_t b = ecs_new(world, 0); + ecs_entity_t b = ecs_new(world); ecs_add(world, b, Tag); test_int(ctx.invoked, 1); test_int(ctx.count, 1); @@ -4318,7 +4381,7 @@ void Trigger_on_add_base_self_superset_w_owned(void) { ecs_os_zeromem(&ctx); - ecs_entity_t i1 = ecs_new(world, Tag); + ecs_entity_t i1 = ecs_new_w(world, Tag); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.e[0], i1); @@ -4342,18 +4405,20 @@ void Trigger_on_set_self_from_child_of_prefab(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_entity_t base_child = ecs_new_entity(world, "Child"); + ecs_entity_t base_child = ecs_entity(world, { .name = "Child" }); ecs_add_id(world, base_child, EcsPrefab); ecs_add_pair(world, base_child, EcsChildOf, base); ecs_set(world, base_child, Position, {10, 20}); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Position), - .src.flags = EcsSelf + .src.id = EcsSelf, + .trav = EcsIsA, }, .events = {EcsOnSet}, .callback = Trigger_w_value, @@ -4379,18 +4444,20 @@ void Trigger_on_set_self_superset_from_child_of_prefab(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); - ecs_entity_t base_child = ecs_new_entity(world, "Child"); + ecs_entity_t base_child = ecs_entity(world, { .name = "Child" }); ecs_add_id(world, base_child, EcsPrefab); ecs_add_pair(world, base_child, EcsChildOf, base); ecs_set(world, base_child, Position, {10, 20}); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Position), - .src.flags = EcsSelf | EcsUp + .src.id = EcsSelf|EcsUp, + .trav = EcsIsA, }, .events = {EcsOnSet}, .callback = Trigger_w_value, @@ -4415,21 +4482,22 @@ void Trigger_on_set_self_from_child_base_of_prefab(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t base_child_base = ecs_new_w_id(world, EcsPrefab); ecs_add_id(world, base_child_base, EcsPrefab); ecs_set(world, base_child_base, Position, {10, 20}); - ecs_entity_t base_child = ecs_new_entity(world, "Child"); + ecs_entity_t base_child = ecs_entity(world, { .name = "Child" }); ecs_add_pair(world, base_child, EcsChildOf, base); ecs_add_pair(world, base_child, EcsIsA, base_child_base); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Position), - .src.flags = EcsSelf + .src.id = EcsSelf }, .events = {EcsOnSet}, .callback = Trigger_w_value, @@ -4451,21 +4519,23 @@ void Trigger_on_set_self_superset_from_child_base_of_prefab(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); ecs_entity_t base_child_base = ecs_new_w_id(world, EcsPrefab); ecs_add_id(world, base_child_base, EcsPrefab); ecs_set(world, base_child_base, Position, {10, 20}); - ecs_entity_t base_child = ecs_new_entity(world, "Child"); + ecs_entity_t base_child = ecs_entity(world, { .name = "Child" }); ecs_add_pair(world, base_child, EcsChildOf, base); ecs_add_pair(world, base_child, EcsIsA, base_child_base); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Position), - .src.flags = EcsSelf | EcsUp + .src.id = EcsSelf|EcsUp, + .trav = EcsIsA }, .events = {EcsOnSet}, .callback = Trigger_w_value, @@ -4490,15 +4560,16 @@ void Trigger_on_set_self_auto_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_id(Position)); + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_id(Position)); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Position), - .src.flags = EcsSelf + .src.id = EcsSelf }, .events = {EcsOnSet}, .callback = Trigger_w_value, @@ -4521,15 +4592,17 @@ void Trigger_on_set_self_superset_auto_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_id(Position)); + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_id(Position)); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Position), - .src.flags = EcsSelf | EcsUp + .src.id = EcsSelf|EcsUp, + .trav = EcsIsA, }, .events = {EcsOnSet}, .callback = Trigger_w_value, @@ -4552,15 +4625,17 @@ void Trigger_on_set_superset_auto_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); - ecs_add_id(world, base, ECS_OVERRIDE | ecs_id(Position)); + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_add_id(world, base, ECS_AUTO_OVERRIDE | ecs_id(Position)); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { + .query.terms[0] = { .id = ecs_id(Position), - .src.flags = EcsUp + .src.id = EcsUp, + .trav = EcsIsA, }, .events = {EcsOnSet}, .callback = Trigger_w_value, @@ -4579,6 +4654,93 @@ void Trigger_on_set_superset_auto_override(void) { ecs_fini(world); } +void Trigger_on_set_self_on_instantiate_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + + Probe ctx = {0}; + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms[0] = { + .id = ecs_id(Position), + .src.id = EcsSelf + }, + .events = {EcsOnSet}, + .callback = Trigger_w_value, + .ctx = &ctx + }); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + test_assert( ecs_has(world, inst, Position)); + test_assert( ecs_owns(world, inst, Position)); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], inst); + test_int(ctx.s[0][0], 0); + + ecs_fini(world); +} + +void Trigger_on_set_self_up_on_instantiate_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + + Probe ctx = {0}; + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms[0] = { + .id = ecs_id(Position), + .src.id = EcsSelf|EcsUp + }, + .events = {EcsOnSet}, + .callback = Trigger_w_value, + .ctx = &ctx + }); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + test_assert( ecs_has(world, inst, Position)); + test_assert( ecs_owns(world, inst, Position)); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], inst); + test_int(ctx.s[0][0], 0); + + ecs_fini(world); +} + +void Trigger_on_set_up_on_instantiate_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + + Probe ctx = {0}; + ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms[0] = { + .id = ecs_id(Position), + .src.id = EcsUp + }, + .events = {EcsOnSet}, + .callback = Trigger_w_value, + .ctx = &ctx + }); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + test_assert( ecs_has(world, inst, Position)); + test_assert( ecs_owns(world, inst, Position)); + + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + void Trigger_not_only(void) { ecs_world_t *world = ecs_mini(); @@ -4587,7 +4749,7 @@ void Trigger_not_only(void) { Probe ctx = {0}; ecs_observer(world, { - .filter.terms = { + .query.terms = { { TagA, .oper = EcsNot } }, .events = { EcsOnAdd }, @@ -4597,7 +4759,7 @@ void Trigger_not_only(void) { test_int(ctx.invoked, 0); - ecs_entity_t i = ecs_new(world, TagA); + ecs_entity_t i = ecs_new_w(world, TagA); test_int(ctx.invoked, 0); ecs_delete(world, i); @@ -4610,10 +4772,11 @@ void Trigger_not_only_w_base(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); Probe ctx = {0}; ecs_observer(world, { - .filter.terms = { + .query.terms = { { TagA, .oper = EcsNot } }, .events = { EcsOnAdd }, @@ -4623,7 +4786,7 @@ void Trigger_not_only_w_base(void) { test_int(ctx.invoked, 0); - ecs_entity_t p = ecs_new(world, TagA); + ecs_entity_t p = ecs_new_w(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); @@ -4641,9 +4804,12 @@ void Trigger_not_only_w_base_no_match(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + Probe ctx = {0}; ecs_observer(world, { - .filter.terms = { + .query.terms = { { TagB, .oper = EcsNot } }, .events = { EcsOnAdd }, @@ -4653,7 +4819,7 @@ void Trigger_not_only_w_base_no_match(void) { test_int(ctx.invoked, 0); - ecs_entity_t p = ecs_new(world, TagA); + ecs_entity_t p = ecs_new_w(world, TagA); test_int(ctx.invoked, 0); ecs_entity_t i = ecs_new_w_pair(world, EcsIsA, p); @@ -4669,13 +4835,13 @@ void Trigger_on_set_superset_after_filter_observer(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); Probe ctx_1 = {0}; ecs_observer(world, { - .filter.terms = { - { ecs_id(Position) } + .query.terms = { + { ecs_id(Position), .inout = EcsInOutNone } }, - .filter.flags = EcsFilterNoData, .events = { EcsOnSet }, .callback = Trigger, .ctx = &ctx_1 @@ -4683,15 +4849,15 @@ void Trigger_on_set_superset_after_filter_observer(void) { Probe ctx_2 = {0}; ecs_observer(world, { - .filter.terms = { - { ecs_id(Position), .src.flags = EcsUp } + .query.terms = { + { ecs_id(Position), .src.id = EcsUp, .trav = EcsIsA } }, .events = { EcsOnSet }, .callback = Trigger_w_value, .ctx = &ctx_2 }); - ecs_entity_t base = ecs_new_id(world); + ecs_entity_t base = ecs_new(world); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx_1.invoked, 0); @@ -4714,13 +4880,13 @@ void Trigger_on_set_superset_after_filter_observer_w_on_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); Probe ctx_1 = {0}; ecs_observer(world, { - .filter.terms = { - { ecs_id(Position) } + .query.terms = { + { ecs_id(Position), .inout = EcsInOutNone } }, - .filter.flags = EcsFilterNoData, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_1 @@ -4728,15 +4894,15 @@ void Trigger_on_set_superset_after_filter_observer_w_on_add(void) { Probe ctx_2 = {0}; ecs_observer(world, { - .filter.terms = { - { ecs_id(Position), .src.flags = EcsUp } + .query.terms = { + { ecs_id(Position), .src.id = EcsUp, .trav = EcsIsA } }, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_2 }); - ecs_entity_t base = ecs_new_id(world); + ecs_entity_t base = ecs_new(world); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); test_int(ctx_1.invoked, 0); @@ -4759,13 +4925,13 @@ void Trigger_on_set_superset_after_filter_observer_w_on_add_isa_after_set(void) ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); Probe ctx_1 = {0}; ecs_observer(world, { - .filter.terms = { - { ecs_id(Position), .src.flags = EcsUp } + .query.terms = { + { ecs_id(Position), .src.id = EcsUp, .trav = EcsIsA, .inout = EcsInOutNone } }, - .filter.flags = EcsFilterNoData, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_1 @@ -4773,15 +4939,15 @@ void Trigger_on_set_superset_after_filter_observer_w_on_add_isa_after_set(void) Probe ctx_2 = {0}; ecs_observer(world, { - .filter.terms = { - { ecs_id(Position), .src.flags = EcsUp } + .query.terms = { + { ecs_id(Position), .src.id = EcsUp, .trav = EcsIsA, } }, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_2 }); - ecs_entity_t base = ecs_new_id(world); + ecs_entity_t base = ecs_new(world); ecs_set(world, base, Position, {10, 20}); test_int(ctx_1.invoked, 0); test_int(ctx_2.invoked, 0); @@ -4809,13 +4975,15 @@ void Trigger_on_set_superset_after_filter_observer_w_on_add_2(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + // first observer causes observer to be initialized with filter flag Probe ctx_1 = {0}; ecs_observer(world, { - .filter.terms = { - { ecs_id(Position) } + .query.terms = { + { ecs_id(Position), .inout = EcsInOutNone } }, - .filter.flags = EcsFilterNoData, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_1 @@ -4824,17 +4992,17 @@ void Trigger_on_set_superset_after_filter_observer_w_on_add_2(void) { // second observer is not a filter, so value should be avaulable Probe ctx_2 = {0}; ecs_observer(world, { - .filter.terms = { - { ecs_id(Position), .src.flags = EcsUp } + .query.terms = { + { ecs_id(Position), .src.id = EcsUp, .trav = EcsIsA } }, .events = { EcsOnAdd }, .callback = Trigger, .ctx = &ctx_2 }); - ecs_entity_t base = ecs_new_id(world); + ecs_entity_t base = ecs_new(world); ecs_set(world, base, Position, {10, 20}); - ecs_override(world, base, Position); // override causes emit with 2 ids + ecs_auto_override(world, base, Position); // override causes emit with 2 ids test_int(ctx_1.invoked, 1); test_int(ctx_2.invoked, 0); @@ -4857,39 +5025,3 @@ void Trigger_on_set_superset_after_filter_observer_w_on_add_2(void) { ecs_fini(world); } - -void Trigger_propagate_w_union_pair(void) { - ecs_world_t *world = ecs_mini(); - - ECS_TAG(world, Tag); - ECS_TAG(world, RelX); - ECS_ENTITY(world, RelY, Union); - - ecs_entity_t base = ecs_new_id(world); - ecs_entity_t tgt = ecs_new_id(world); - ecs_new_w_pair(world, EcsIsA, base); - - Probe ctx = {0}; - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0] = { - .id = Tag, - .src.flags = EcsUp - }, - .events = {EcsOnAdd}, - .callback = Trigger, - .ctx = &ctx - }); - test_int(ctx.invoked, 0); - - ecs_add_id(world, base, Tag); - test_int(ctx.invoked, 1); - - ecs_os_zeromem(&ctx); - ecs_add_pair(world, base, RelX, tgt); - test_int(ctx.invoked, 0); - - ecs_add_pair(world, base, RelY, tgt); - test_int(ctx.invoked, 0); - - ecs_fini(world); -} diff --git a/vendors/flecs/test/api/src/TriggerOnAdd.c b/vendors/flecs/test/core/src/TriggerOnAdd.c similarity index 89% rename from vendors/flecs/test/api/src/TriggerOnAdd.c rename to vendors/flecs/test/core/src/TriggerOnAdd.c index cdbe466fd..d0b173b27 100644 --- a/vendors/flecs/test/api/src/TriggerOnAdd.c +++ b/vendors/flecs/test/core/src/TriggerOnAdd.c @@ -1,15 +1,15 @@ -#include +#include void TriggerOnAdd_setup(void) { ecs_log_set_level(-3); } void Init(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); Velocity *v = NULL; if (it->field_count >= 2) { - v = ecs_field(it, Velocity, 2); + v = ecs_field(it, Velocity, 1); } probe_iter(it); @@ -95,7 +95,7 @@ void TriggerOnAdd_new_match_1_of_1(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.count, 1); @@ -155,7 +155,7 @@ void TriggerOnAdd_new_no_match_1(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Velocity); + ecs_entity_t e = ecs_new_w(world, Velocity); test_assert(e != 0); test_int(ctx.count, 0); @@ -172,7 +172,7 @@ void TriggerOnAdd_add_match_1_of_1(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(ctx.count, 0); @@ -206,7 +206,7 @@ void TriggerOnAdd_add_match_1_of_2(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(ctx.count, 0); @@ -241,7 +241,7 @@ void TriggerOnAdd_add_no_match_1(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(ctx.count, 0); @@ -262,7 +262,7 @@ void TriggerOnAdd_set_match_1_of_1(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(ctx.count, 0); @@ -296,7 +296,7 @@ void TriggerOnAdd_set_no_match_1(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(ctx.count, 0); @@ -314,7 +314,7 @@ void TriggerOnAdd_clone_match_1_of_1(void) { ECS_COMPONENT(world, Position); ECS_OBSERVER(world, Init, EcsOnAdd, Position); - ecs_entity_t e1 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); test_assert(e1 != 0); Probe ctx = {0}; @@ -377,7 +377,7 @@ void TriggerOnAdd_add_again_1(void) { ECS_COMPONENT(world, Position); ECS_OBSERVER(world, Init, EcsOnAdd, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, Position); @@ -398,7 +398,7 @@ void TriggerOnAdd_set_again_1(void) { ECS_COMPONENT(world, Position); ECS_OBSERVER(world, Init, EcsOnAdd, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_set(world, e, Position, {10, 20}); @@ -420,7 +420,7 @@ void TriggerOnAdd_add_again_2(void) { ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, Init, EcsOnAdd, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, Position); @@ -473,7 +473,7 @@ void TriggerOnAdd_new_w_count_match_1_of_1(void) { static void AddVelocity(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); ecs_id_t v = 0; if (it->ctx) { @@ -481,7 +481,7 @@ void AddVelocity(ecs_iter_t *it) { } if (!v) { - v = ecs_field_id(it, 2); + v = ecs_field_id(it, 1); } probe_iter(it); @@ -500,12 +500,15 @@ void TriggerOnAdd_override_after_add_in_on_add(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {1, 2}); ECS_OBSERVER(world, AddVelocity, EcsOnAdd, Position(self)); - ecs_observer_init(world, &(ecs_observer_desc_t){ + ecs_observer(world, { .entity = ecs_entity(world, {.id = AddVelocity}), .ctx = &ecs_id(Velocity) }); @@ -540,7 +543,7 @@ void TriggerOnAdd_override_after_add_in_on_add(void) { static void OnSetPosition(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { @@ -558,7 +561,7 @@ void TriggerOnAdd_set_after_add_in_on_add(void) { ECS_OBSERVER(world, AddVelocity, EcsOnAdd, Position); ECS_OBSERVER(world, OnSetPosition, EcsOnSet, Position); - ecs_observer_init(world, &(ecs_observer_desc_t){ + ecs_observer(world, { .entity = ecs_entity(world, {.id = AddVelocity}), .ctx = &ecs_id(Velocity) }); @@ -566,7 +569,7 @@ void TriggerOnAdd_set_after_add_in_on_add(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(ctx.count, 0); @@ -593,7 +596,7 @@ void TriggerOnAdd_set_after_add_in_on_add(void) { static void AddAgain(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -608,7 +611,7 @@ void TriggerOnAdd_add_again_in_progress(void) { ECS_OBSERVER(world, Init, EcsOnAdd, Position); ECS_SYSTEM(world, AddAgain, EcsOnUpdate, Position); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); ecs_add(world, e, Position); @@ -625,7 +628,7 @@ void TriggerOnAdd_add_again_in_progress(void) { static void AddMass(ecs_iter_t *it) { - Mass *m = ecs_field(it, Mass, 1); + Mass *m = ecs_field(it, Mass, 0); int i; for (i = 0; i < it->count; i ++) { @@ -640,7 +643,7 @@ void TriggerOnAdd_add_in_progress_before_system_def(void) { ECS_COMPONENT(world, Velocity); ECS_SYSTEM(world, AddVelocity, EcsOnUpdate, Position, Velocity()); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert( ecs_has(world, e, Position)); test_assert( !ecs_has(world, e, Velocity)); @@ -673,7 +676,7 @@ void SystemA(ecs_iter_t *it) { } void SystemB(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -688,16 +691,16 @@ void TriggerOnAdd_2_systems_w_table_creation(void) { ECS_OBSERVER(world, SystemA, EcsOnAdd, Position); ECS_OBSERVER(world, SystemB, EcsOnAdd, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_fini(world); } void NewWithPosition(ecs_iter_t *it) { - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); - ecs_entity_t e = ecs_new(it->world, Position); + ecs_entity_t e = ecs_new_w(it->world, Position); test_assert(e != 0); } @@ -730,11 +733,11 @@ void TriggerOnAdd_sys_context(void) { ECS_OBSERVER(world, TestContext, EcsOnAdd, Position); - ecs_observer_init(world, &(ecs_observer_desc_t){ + ecs_observer(world, { .entity = ecs_entity(world, {.id = TestContext}), .ctx = ¶m }); - test_assert(ecs_observer_get_ctx(world, TestContext) == ¶m); + test_assert(ecs_observer_get(world, TestContext)->ctx == ¶m); ecs_fini(world); } @@ -747,7 +750,7 @@ void TriggerOnAdd_get_sys_context_from_param(void) { ECS_OBSERVER(world, TestContext, EcsOnAdd, Position); - ecs_observer_init(world, &(ecs_observer_desc_t){ + ecs_observer(world, { .entity = ecs_entity(world, {.id = TestContext}), .ctx = ¶m }); @@ -755,7 +758,7 @@ void TriggerOnAdd_get_sys_context_from_param(void) { ecs_set_ctx(world, ¶m, NULL); /* Trigger system */ - ecs_new(world, Position); + ecs_new_w(world, Position); test_int(param, 1); @@ -775,7 +778,7 @@ void TriggerOnAdd_remove_added_component_in_on_add_w_set(void) { test_expect_abort(); - ecs_set(world, 0, Position, {0, 0}); + ecs_insert(world, ecs_value(Position, {0, 0})); } void Add_3_to_current(ecs_iter_t *it) { @@ -802,9 +805,9 @@ void TriggerOnAdd_on_add_in_on_add(void) { IterData ctx = {.component = ecs_id(Velocity), .component_3 = ecs_id(Mass)}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); test_assert( ecs_has(world, e1, Position)); test_assert( ecs_has(world, e2, Position)); @@ -863,9 +866,9 @@ void TriggerOnAdd_on_set_in_on_add(void) { IterData ctx = {.component = ecs_id(Rotation), .component_3 = ecs_id(Mass)}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); test_assert( ecs_has(world, e1, Position)); test_assert( ecs_has(world, e2, Position)); @@ -907,9 +910,9 @@ void TriggerOnAdd_on_add_in_on_update(void) { IterData ctx = {.component = ecs_id(Velocity), .component_3 = ecs_id(Mass)}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); - ecs_entity_t e3 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); ecs_progress(world, 1); @@ -933,17 +936,17 @@ void TriggerOnAdd_emplace(void) { ECS_COMPONENT(world, Position); - ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + ecs_observer(world, { + .query.terms[0].id = ecs_id(Position), .events = {EcsOnAdd}, .callback = Dummy }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(dummy_called, 0); - Position *p = ecs_emplace(world, e, Position); + Position *p = ecs_emplace(world, e, Position, NULL); test_assert(p != NULL); test_bool(dummy_called, true); @@ -955,13 +958,13 @@ void TriggerOnAdd_add_after_delete_trigger(void) { ECS_COMPONENT(world, Position); - ecs_entity_t trigger = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + ecs_entity_t trigger = ecs_observer(world, { + .query.terms[0].id = ecs_id(Position), .events = {EcsOnAdd}, .callback = Dummy }); - ecs_entity_t e1 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); test_assert(e1 != 0); test_assert(ecs_has(world, e1, Position)); test_int(dummy_called, 1); @@ -971,7 +974,7 @@ void TriggerOnAdd_add_after_delete_trigger(void) { ecs_delete(world, trigger); test_int(dummy_called, 0); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); test_assert(e2 != 0); test_assert(ecs_has(world, e2, Position)); test_int(dummy_called, 0); @@ -984,13 +987,13 @@ void TriggerOnAdd_add_after_delete_wildcard_id_trigger(void) { ECS_COMPONENT(world, Position); - ecs_entity_t trigger = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = EcsWildcard, + ecs_entity_t trigger = ecs_observer(world, { + .query.terms[0].id = EcsWildcard, .events = {EcsOnAdd}, .callback = Dummy }); - ecs_entity_t e1 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); test_assert(e1 != 0); test_assert(ecs_has(world, e1, Position)); test_int(dummy_called, 1); @@ -1000,7 +1003,7 @@ void TriggerOnAdd_add_after_delete_wildcard_id_trigger(void) { ecs_delete(world, trigger); test_int(dummy_called, 0); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); test_assert(e2 != 0); test_assert(ecs_has(world, e2, Position)); test_int(dummy_called, 0); diff --git a/vendors/flecs/test/api/src/TriggerOnRemove.c b/vendors/flecs/test/core/src/TriggerOnRemove.c similarity index 89% rename from vendors/flecs/test/api/src/TriggerOnRemove.c rename to vendors/flecs/test/core/src/TriggerOnRemove.c index 13ef50873..ea3f31287 100644 --- a/vendors/flecs/test/api/src/TriggerOnRemove.c +++ b/vendors/flecs/test/core/src/TriggerOnRemove.c @@ -1,11 +1,11 @@ -#include +#include void Deinit(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); Velocity *v = NULL; if (it->field_count >= 2) { - v = ecs_field(it, Velocity, 2); + v = ecs_field(it, Velocity, 1); } probe_iter(it); @@ -52,7 +52,7 @@ void TriggerOnRemove_remove(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.count, 0); @@ -80,7 +80,7 @@ void TriggerOnRemove_remove_no_match(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.count, 0); @@ -101,7 +101,7 @@ void TriggerOnRemove_delete(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_int(ctx.count, 0); @@ -129,7 +129,7 @@ void TriggerOnRemove_delete_no_match(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Velocity); + ecs_entity_t e = ecs_new_w(world, Velocity); test_assert(e != 0); test_int(ctx.count, 0); @@ -145,7 +145,7 @@ static Position old_position = {0}; static void RemovePosition(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_assert(it->count == 1); @@ -159,7 +159,7 @@ void TriggerOnRemove_remove_watched(void) { ECS_OBSERVER(world, RemovePosition, EcsOnRemove, Position); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(e != 0); /* Make parent entity watched */ @@ -181,7 +181,7 @@ void TriggerOnRemove_delete_watched(void) { ECS_OBSERVER(world, RemovePosition, EcsOnRemove, Position); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(e != 0); /* Make parent entity watched */ @@ -256,7 +256,7 @@ void TriggerOnRemove_valid_entity_after_delete(void) { ECS_COMPONENT(world, DummyComp); ECS_OBSERVER(world, RemoveDummyComp, EcsOnRemove, DummyComp); - ecs_entity_t e = ecs_new(world, DummyComp); + ecs_entity_t e = ecs_new_w(world, DummyComp); test_assert(e != 0); ecs_delete(world, e); @@ -274,12 +274,12 @@ void TriggerOnRemove_remove_after_delete_trigger(void) { ECS_COMPONENT(world, Position); ecs_entity_t trigger = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnRemove}, .callback = Dummy }); - ecs_entity_t e1 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); test_assert(e1 != 0); test_assert(ecs_has(world, e1, Position)); test_int(dummy_called, 0); @@ -292,7 +292,7 @@ void TriggerOnRemove_remove_after_delete_trigger(void) { ecs_delete(world, trigger); test_int(dummy_called, 0); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); test_assert(e2 != 0); test_assert(ecs_has(world, e2, Position)); test_int(dummy_called, 0); @@ -309,12 +309,12 @@ void TriggerOnRemove_remove_after_delete_wildcard_id_trigger(void) { ECS_COMPONENT(world, Position); ecs_entity_t trigger = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = EcsWildcard, + .query.terms[0].id = EcsWildcard, .events = {EcsOnRemove}, .callback = Dummy }); - ecs_entity_t e1 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, Position); test_assert(e1 != 0); test_assert(ecs_has(world, e1, Position)); test_int(dummy_called, 0); @@ -330,7 +330,7 @@ void TriggerOnRemove_remove_after_delete_wildcard_id_trigger(void) { dummy_called = 0; - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); test_assert(e2 != 0); test_assert(ecs_has(world, e2, Position)); test_int(dummy_called, 0); @@ -353,7 +353,7 @@ void OnRemoveHasTag(ecs_iter_t *it) { test_int(it->count, 1); test_assert(it->entities[0] == ctx->ent); - test_assert(ecs_field_id(it, 1) == ctx->tag); + test_assert(ecs_field_id(it, 0) == ctx->tag); test_bool(ecs_has_id(it->world, ctx->ent, ctx->tag), true); dummy_called = true; @@ -364,7 +364,7 @@ void TriggerOnRemove_has_removed_tag_trigger_1_tag(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); test_assert(e != 0); test_assert(ecs_has(world, e, Tag)); @@ -374,7 +374,7 @@ void TriggerOnRemove_has_removed_tag_trigger_1_tag(void) { }; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, + .query.terms[0].id = Tag, .events = {EcsOnRemove}, .callback = OnRemoveHasTag, .ctx = &ctx @@ -393,7 +393,7 @@ void TriggerOnRemove_has_removed_tag_trigger_2_tags(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new(world, TagA); + ecs_entity_t e = ecs_new_w(world, TagA); test_assert(e != 0); test_assert(ecs_has(world, e, TagA)); @@ -406,7 +406,7 @@ void TriggerOnRemove_has_removed_tag_trigger_2_tags(void) { }; ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = TagA, + .query.terms[0].id = TagA, .events = {EcsOnRemove}, .callback = OnRemoveHasTag, .ctx = &ctx diff --git a/vendors/flecs/test/api/src/TriggerOnSet.c b/vendors/flecs/test/core/src/TriggerOnSet.c similarity index 83% rename from vendors/flecs/test/api/src/TriggerOnSet.c rename to vendors/flecs/test/core/src/TriggerOnSet.c index 07d1aa1d4..f8a8f3154 100644 --- a/vendors/flecs/test/api/src/TriggerOnSet.c +++ b/vendors/flecs/test/core/src/TriggerOnSet.c @@ -1,4 +1,4 @@ -#include +#include static void Trigger(ecs_iter_t *it) { @@ -7,11 +7,11 @@ void Trigger(ecs_iter_t *it) { static void OnSet(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); Velocity *v = NULL; if (it->field_count >= 2) { - v = ecs_field(it, Velocity, 2); + v = ecs_field(it, Velocity, 1); } probe_iter(it); @@ -43,7 +43,7 @@ void TriggerOnSet_set(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.count, 0); ecs_set(world, e, Position, {10, 20}); @@ -76,7 +76,7 @@ void TriggerOnSet_set_new(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_int(ctx.count, 1); test_int(ctx.invoked, 1); @@ -106,7 +106,7 @@ void TriggerOnSet_set_again(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.count, 0); ecs_set(world, e, Position, {10, 20}); @@ -146,7 +146,7 @@ void TriggerOnSet_clone(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); test_int(ctx.count, 1); test_int(ctx.invoked, 1); @@ -184,7 +184,7 @@ void TriggerOnSet_clone_w_value(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); test_int(ctx.count, 1); test_int(ctx.invoked, 1); @@ -228,7 +228,7 @@ static bool set_called; static void OnAdd_check_order(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_assert(!add_called); test_assert(!set_called); @@ -246,7 +246,7 @@ void OnAdd_check_order(ecs_iter_t *it) { static void OnSet_check_order(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); probe_iter(it); @@ -272,7 +272,7 @@ void TriggerOnSet_set_and_add_system(void) { Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(ctx.count, 0); ecs_set(world, e, Position, {10, 20}); @@ -302,11 +302,11 @@ void TriggerOnSet_set_and_add_system(void) { static void OnSetShared(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); Velocity *v = NULL; if (it->field_count >= 2) { - v = ecs_field(it, Velocity, 2); + v = ecs_field(it, Velocity, 1); } probe_iter(it); @@ -324,6 +324,7 @@ void TriggerOnSet_on_set_after_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); ECS_PREFAB(world, Prefab, Position); ecs_set(world, Prefab, Position, {1, 3}); @@ -397,13 +398,14 @@ void TriggerOnSet_on_set_after_override_w_new(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, Prefab, Position, OVERRIDE | Position); + ECS_PREFAB(world, Prefab, Position, auto_override | Position); ecs_set(world, Prefab, Position, {1, 3}); Probe ctx = {0}; ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = OnSet, .ctx = &ctx @@ -433,8 +435,9 @@ void TriggerOnSet_on_set_after_override_w_new_w_count(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, Prefab, Position, OVERRIDE | Position); + ECS_PREFAB(world, Prefab, Position, auto_override | Position); ecs_set(world, Prefab, Position, {1, 3}); ECS_OBSERVER(world, OnSet, EcsOnSet, Position); @@ -470,8 +473,9 @@ void TriggerOnSet_on_set_after_override_1_of_2_overridden(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ECS_PREFAB(world, Prefab, Position, OVERRIDE | Position); + ECS_PREFAB(world, Prefab, Position, auto_override | Position); ecs_set(world, Prefab, Position, {1, 3}); ECS_OBSERVER(world, OnSet, EcsOnSet, Position); @@ -498,74 +502,22 @@ void TriggerOnSet_on_set_after_override_1_of_2_overridden(void) { ecs_fini(world); } -static -void SetPosition(ecs_iter_t *it) { - probe_iter(it); -} - -void TriggerOnSet_on_set_after_snapshot_restore(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - ECS_OBSERVER(world, SetPosition, EcsOnSet, Position); - - const ecs_entity_t *ids = ecs_bulk_new(world, Position, 10); - test_assert(ids != NULL); - - ecs_entity_t id_arr[10]; - memcpy(id_arr, ids, sizeof(ecs_entity_t) * 10); - - int32_t i; - for (i = 0; i < 10; i ++) { - test_assert(ecs_has(world, ids[i], Position)); - ecs_set(world, ids[i], Position, {i, i * 2}); - } - - Probe ctx = { 0 }; - ecs_set_ctx(world, &ctx, NULL); - - ecs_snapshot_t *s = ecs_snapshot_take(world); - - test_int(ctx.invoked, 0); - - /* Delete one entity, so we have more confidence we're triggering on the - * right entities */ - ecs_delete(world, id_arr[0]); - - test_int(ctx.invoked, 0); - - ecs_snapshot_restore(world, s); - - test_int(ctx.count, 10); - test_int(ctx.invoked, 1); - test_int(ctx.system, SetPosition); - test_int(ctx.term_count, 1); - test_int(ctx.c[0][0], ecs_id(Position)); - test_null(ctx.param); - - for (i = 0; i < 10; i ++) { - test_int(ctx.e[i], id_arr[i]); - } - - ecs_fini(world); -} - void TriggerOnSet_emplace(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = {EcsOnSet}, .callback = Dummy }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(dummy_called, 0); - Position *p = ecs_emplace(world, e, Position); + Position *p = ecs_emplace(world, e, Position, NULL); test_assert(p != NULL); test_int(dummy_called, 0); @@ -581,12 +533,12 @@ void TriggerOnSet_un_set_tag_w_remove(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, - .events = {EcsUnSet}, + .query.terms[0].id = Tag, + .events = {EcsOnRemove}, .callback = Dummy }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(dummy_called, 0); @@ -605,12 +557,12 @@ void TriggerOnSet_un_set_tag_w_clear(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, - .events = {EcsUnSet}, + .query.terms[0].id = Tag, + .events = {EcsOnRemove}, .callback = Dummy }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(dummy_called, 0); @@ -629,12 +581,12 @@ void TriggerOnSet_un_set_tag_w_delete(void) { ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ - .filter.terms[0].id = Tag, - .events = {EcsUnSet}, + .query.terms[0].id = Tag, + .events = {EcsOnRemove}, .callback = Dummy }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_int(dummy_called, 0); @@ -652,13 +604,17 @@ void TriggerOnSet_on_set_after_remove_override(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Base, Position); ECS_OBSERVER(world, Trigger, EcsOnSet, Position); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, Base); diff --git a/vendors/flecs/test/api/src/Type.c b/vendors/flecs/test/core/src/Type.c similarity index 88% rename from vendors/flecs/test/api/src/Type.c rename to vendors/flecs/test/core/src/Type.c index d3cca7b4f..9e8dfd787 100644 --- a/vendors/flecs/test/api/src/Type.c +++ b/vendors/flecs/test/core/src/Type.c @@ -1,13 +1,6 @@ -#include +#include #include -static -char* type_str(ecs_world_t *world, ecs_entity_t type_ent) { - const ecs_type_t *t = ecs_get_type(world, type_ent); - test_assert(t != NULL); - return ecs_type_str(world, t); -} - void Type_setup(void) { ecs_log_set_level(-2); } @@ -50,13 +43,13 @@ void Type_type_of_2_tostr(void) { void Type_type_of_2_tostr_no_id(void) { ecs_world_t *world = ecs_mini(); - ecs_ensure(world, 100); - ecs_ensure(world, 200); + ecs_make_alive(world, 100); + ecs_make_alive(world, 200); ecs_type_t t = { .array = (ecs_id_t[]){ 100, 200 }, .count = 2 }; char *str = ecs_type_str(world, &t); - test_str(str, "100, 200"); + test_str(str, "#100, #200"); ecs_os_free(str); @@ -100,7 +93,7 @@ void Type_get_type(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); const ecs_type_t *t = ecs_get_type(world, e); test_assert(t != NULL); @@ -116,7 +109,7 @@ void Type_get_type(void) { void Type_get_type_from_empty(void) { ecs_world_t *world = ecs_mini(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); const ecs_type_t *t = ecs_get_type(world, e); test_assert(t == NULL); @@ -137,13 +130,13 @@ void Type_get_type_from_0(void) { void Type_type_to_expr_pair(void) { ecs_world_t *world = ecs_mini(); - ecs_ensure(world, 100); - ecs_ensure(world, 200); + ecs_make_alive(world, 100); + ecs_make_alive(world, 200); ecs_type_t t = { .array = (ecs_id_t[]){ ecs_pair(100, 200) }, .count = 1 }; char *str = ecs_type_str(world, &t); - test_str(str, "(100,200)"); + test_str(str, "(#100,#200)"); ecs_os_free(str); @@ -199,12 +192,12 @@ void Type_type_to_expr_pair_w_override(void) { ECS_COMPONENT(world, Velocity); ecs_type_t t = { .array = (ecs_id_t[]){ - ECS_OVERRIDE | ecs_pair(ecs_id(Position), ecs_id(Velocity)) + ECS_AUTO_OVERRIDE | ecs_pair(ecs_id(Position), ecs_id(Velocity)) }, .count = 1 }; char *str = ecs_type_str(world, &t); - test_str(str, "OVERRIDE|(Position,Velocity)"); + test_str(str, "AUTO_OVERRIDE|(Position,Velocity)"); ecs_os_free(str); @@ -279,20 +272,6 @@ void Type_entity_pair_str(void) { ecs_fini(world); } -void Type_entity_and_str(void) { - ecs_world_t *world = ecs_mini(); - - ECS_ENTITY(world, Foo, 0); - - ecs_entity_t e = ECS_AND | Foo; - - char *str = ecs_id_str(world, e); - test_str(str, "AND|Foo"); - ecs_os_free(str); - - ecs_fini(world); -} - void Type_entity_str_small_buffer(void) { ecs_world_t *world = ecs_mini(); @@ -312,14 +291,9 @@ void Type_role_pair_str(void) { test_str(ecs_id_flag_str(e), "PAIR"); } -void Type_role_and_str(void) { - ecs_entity_t e = ECS_AND; - test_str(ecs_id_flag_str(e), "AND"); -} - void Type_role_owned_str(void) { - ecs_entity_t e = ECS_OVERRIDE; - test_str(ecs_id_flag_str(e), "OVERRIDE"); + ecs_entity_t e = ECS_AUTO_OVERRIDE; + test_str(ecs_id_flag_str(e), "AUTO_OVERRIDE"); } void Type_role_disabled_str(void) { diff --git a/vendors/flecs/test/core/src/Union.c b/vendors/flecs/test/core/src/Union.c new file mode 100644 index 000000000..c86013bed --- /dev/null +++ b/vendors/flecs/test/core/src/Union.c @@ -0,0 +1,1499 @@ +#include + +static +void SetCase(ecs_iter_t *it) { + ecs_world_t *world = it->world; + ecs_entity_t id = ecs_field_id(it, 1); + + int i; + for (i = 0; i < it->count; i ++) { + ecs_add_id(world, it->entities[i], id); + } +} + +void Union_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_fini(world); +} + +void Union_add_twice(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_fini(world); +} + +void Union_add_replace(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Running); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(ecs_has_pair(world, e, Movement, Running)); + + ecs_fini(world); +} + +void Union_add_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_remove_pair(world, e, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_fini(world); +} + +void Union_add_remove_recycled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + + ecs_entity_t ent = ecs_new(world); + ecs_delete(world, ent); + ecs_entity_t recycled = ecs_new(world); + test_assert(ent != recycled); + test_assert(ent == (uint32_t)recycled); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, recycled)); + + ecs_add_pair(world, e, Movement, recycled); + test_assert(ecs_has_pair(world, e, Movement, recycled)); + + ecs_remove_pair(world, e, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e, Movement, recycled)); + + ecs_fini(world); +} + +void Union_add_remove_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_remove_pair(world, e, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_fini(world); +} + +void Union_get_target_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_uint(0, ecs_get_target(world, e, Movement, 0)); + + ecs_fini(world); +} + +void Union_get_target(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_uint(0, ecs_get_target(world, e, Movement, 0)); + + ecs_add_pair(world, e, Movement, Walking); + test_uint(Walking, ecs_get_target(world, e, Movement, 0)); + + ecs_fini(world); +} + +void Union_get_recycled_target(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + + ecs_entity_t ent = ecs_new(world); + ecs_delete(world, ent); + ecs_entity_t recycled = ecs_new(world); + test_assert(ent != recycled); + test_assert(ent == (uint32_t)recycled); + + ecs_entity_t e = ecs_new(world); + test_uint(0, ecs_get_target(world, e, Movement, 0)); + + ecs_add_pair(world, e, Movement, recycled); + test_uint(recycled, ecs_get_target(world, e, Movement, 0)); + + ecs_remove_pair(world, e, Movement, EcsWildcard); + test_uint(0, ecs_get_target(world, e, Movement, 0)); + + ecs_fini(world); +} + +void Union_get_target_after_replace(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_uint(0, ecs_get_target(world, e, Movement, 0)); + + ecs_add_pair(world, e, Movement, Walking); + test_uint(Walking, ecs_get_target(world, e, Movement, 0)); + + ecs_add_pair(world, e, Movement, Running); + test_uint(Running, ecs_get_target(world, e, Movement, 0)); + + ecs_fini(world); +} + +void Union_get_target_after_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_uint(0, ecs_get_target(world, e, Movement, 0)); + + ecs_add_pair(world, e, Movement, Walking); + test_uint(Walking, ecs_get_target(world, e, Movement, 0)); + + ecs_remove_pair(world, e, Movement, EcsWildcard); + test_uint(0, ecs_get_target(world, e, Movement, 0)); + + ecs_fini(world); +} + +void Union_has_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, EcsWildcard)); + + ecs_remove_pair(world, e, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e, Movement, EcsWildcard)); + + ecs_fini(world); +} + +void Union_has_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, EcsAny)); + + ecs_remove_pair(world, e, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e, Movement, EcsAny)); + + ecs_fini(world); +} + +void Union_add_remove_2_tgts(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e1 = ecs_new(world); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + + ecs_entity_t e2 = ecs_new(world); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + + ecs_add_pair(world, e1, Movement, Walking); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + + ecs_add_pair(world, e2, Movement, Running); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + + ecs_remove_pair(world, e1, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + + ecs_remove_pair(world, e2, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + + ecs_fini(world); +} + +void Union_add_remove_2_tgts_join(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e1 = ecs_new(world); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + + ecs_entity_t e2 = ecs_new(world); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + + ecs_add_pair(world, e1, Movement, Walking); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + + ecs_add_pair(world, e2, Movement, Running); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + + ecs_add_pair(world, e2, Movement, Walking); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + + ecs_remove_pair(world, e1, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + + ecs_remove_pair(world, e2, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + + ecs_fini(world); +} + +void Union_add_remove_3_tgts(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_new(world); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e1, Movement, Sitting)); + + ecs_entity_t e2 = ecs_new(world); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Sitting)); + + ecs_entity_t e3 = ecs_new(world); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_add_pair(world, e1, Movement, Walking); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e1, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_add_pair(world, e2, Movement, Running); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_add_pair(world, e3, Movement, Sitting); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_remove_pair(world, e1, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_remove_pair(world, e2, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_remove_pair(world, e3, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_fini(world); +} + +void Union_add_remove_3_tgts_join(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_new(world); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e1, Movement, Sitting)); + + ecs_entity_t e2 = ecs_new(world); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Sitting)); + + ecs_entity_t e3 = ecs_new(world); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_add_pair(world, e1, Movement, Walking); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e1, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_add_pair(world, e2, Movement, Running); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e1, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_add_pair(world, e3, Movement, Sitting); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e1, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_add_pair(world, e1, Movement, Sitting); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(ecs_has_pair(world, e1, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_add_pair(world, e2, Movement, Sitting); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(ecs_has_pair(world, e1, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(ecs_has_pair(world, e2, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_remove_pair(world, e1, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e1, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(ecs_has_pair(world, e2, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_remove_pair(world, e2, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e1, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_remove_pair(world, e3, Movement, EcsWildcard); + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e1, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Sitting)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Sitting)); + + ecs_fini(world); +} + +void Union_remove_w_union_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_remove_pair(world, e, Movement, EcsUnion); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_fini(world); +} + +void Union_get_non_union_tgt_from_table_w_union(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Likes); + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_entity_t movement = ecs_get_target(world, e, Movement, 0); + test_assert(movement != 0); + test_assert(movement == Walking); + + ecs_entity_t likes = ecs_get_target(world, e, Likes, 0); + test_assert(likes == 0); + + ecs_fini(world); +} + +void Union_has_non_union_from_table_w_union(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Likes); + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Likes, Walking)); + + ecs_fini(world); +} + +void Union_get_case_no_switch(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_ENTITY(world, Rel, Union); + + ecs_entity_t e = ecs_new_w(world, Position); + test_assert(e != 0); + + test_uint(ecs_get_target(world, e, Rel, 0), 0); + + ecs_fini(world); +} + +void Union_get_case_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); + test_assert(e != 0); + + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_assert( !ecs_has_pair(world, e, Movement, Running)); + test_assert( !ecs_has_pair(world, e, Movement, Jumping)); + test_uint(ecs_get_target(world, e, Movement, 0), Walking); + + ecs_fini(world); +} + +void Union_get_case_change(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); + test_assert(e != 0); + + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_assert( !ecs_has_pair(world, e, Movement, Running)); + test_assert( !ecs_has_pair(world, e, Movement, Jumping)); + test_uint(ecs_get_target(world, e, Movement, 0), Walking); + + ecs_add_pair(world, e, Movement, Running); + + test_assert( !ecs_has_pair(world, e, Movement, Walking)); + test_assert( ecs_has_pair(world, e, Movement, Running)); + test_assert( !ecs_has_pair(world, e, Movement, Jumping)); + test_uint(ecs_get_target(world, e, Movement, 0), Running); + + ecs_add_pair(world, e, Movement, Jumping); + + test_assert( !ecs_has_pair(world, e, Movement, Walking)); + test_assert( !ecs_has_pair(world, e, Movement, Running)); + test_assert( ecs_has_pair(world, e, Movement, Jumping)); + test_uint(ecs_get_target(world, e, Movement, 0), Jumping); + + ecs_fini(world); +} + +void Union_remove_case(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); + test_assert(e != 0); + + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_assert( !ecs_has_pair(world, e, Movement, Running)); + test_assert( !ecs_has_pair(world, e, Movement, Jumping)); + test_uint(ecs_get_target(world, e, Movement, 0), Walking); + + ecs_remove_pair(world, e, Movement, Walking); + + test_assert( !ecs_has_pair(world, e, Movement, Walking)); + test_assert( !ecs_has_pair(world, e, Movement, Running)); + test_assert( !ecs_has_pair(world, e, Movement, Jumping)); + test_uint(ecs_get_target(world, e, Movement, 0), 0); + + test_assert(ecs_get_type(world, e) == NULL); + + ecs_fini(world); +} + +void Union_remove_last(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e1 != 0); + ecs_add(world, e1, Tag); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e2 != 0); + ecs_add(world, e2, Tag); + + ecs_remove(world, e2, Tag); + + test_assert(!ecs_has(world, e2, Tag)); + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + + test_assert(ecs_has(world, e1, Tag)); + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + + ecs_fini(world); +} + +void Union_delete_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e1 != 0); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e2 != 0); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e3 != 0); + + ecs_delete(world, e1); + + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e3, Movement, Walking)); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e3); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e2); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_delete_last(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e1 != 0); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e2 != 0); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e3 != 0); + + ecs_delete(world, e3); + + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e1); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_delete_first_last(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e1 != 0); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e2 != 0); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e3 != 0); + + ecs_delete(world, e1); + ecs_delete(world, e3); + + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e2); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_3_entities_same_case(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_ENTITY(world, e1, (Movement, Running)); + ECS_ENTITY(world, e2, (Movement, Running)); + ECS_ENTITY(world, e3, (Movement, Running)); + + test_assert( ecs_has_pair(world, e1, Movement, Running)); + test_assert( ecs_has_pair(world, e2, Movement, Running)); + test_assert( ecs_has_pair(world, e3, Movement, Running)); + + test_int(ecs_get_target(world, e1, Movement, 0), Running); + test_int(ecs_get_target(world, e2, Movement, 0), Running); + test_int(ecs_get_target(world, e3, Movement, 0), Running); + + ecs_fini(world); +} + +void Union_2_entities_1_change_case(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_ENTITY(world, e1, (Movement, Running)); + ECS_ENTITY(world, e2, (Movement, Running)); + + ecs_add_pair(world, e2, Movement, Jumping); + test_assert( ecs_has_pair(world, e1, Movement, Running)); + test_assert( ecs_has_pair(world, e2, Movement, Jumping)); + + test_int(ecs_get_target(world, e1, Movement, 0), Running); + test_int(ecs_get_target(world, e2, Movement, 0), Jumping); + + ecs_fini(world); +} + +void Union_3_entities_change_case(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_ENTITY(world, e1, (Movement, Running)); + ECS_ENTITY(world, e2, (Movement, Running)); + ECS_ENTITY(world, e3, (Movement, Running)); + + ecs_add_pair(world, e1, Movement, Walking); + test_assert( ecs_has_pair(world, e1, Movement, Walking)); + test_assert( ecs_has_pair(world, e2, Movement, Running)); + test_assert( ecs_has_pair(world, e3, Movement, Running)); + + test_int(ecs_get_target(world, e1, Movement, 0), Walking); + test_int(ecs_get_target(world, e2, Movement, 0), Running); + test_int(ecs_get_target(world, e3, Movement, 0), Running); + + ecs_add_pair(world, e2, Movement, Jumping); + test_assert( ecs_has_pair(world, e1, Movement, Walking)); + test_assert( ecs_has_pair(world, e2, Movement, Jumping)); + test_assert( ecs_has_pair(world, e3, Movement, Running)); + + test_int(ecs_get_target(world, e1, Movement, 0), Walking); + test_int(ecs_get_target(world, e2, Movement, 0), Jumping); + test_int(ecs_get_target(world, e3, Movement, 0), Running); + + ecs_add_pair(world, e3, Movement, Walking); + test_assert( ecs_has_pair(world, e1, Movement, Walking)); + test_assert( ecs_has_pair(world, e2, Movement, Jumping)); + test_assert( ecs_has_pair(world, e3, Movement, Walking)); + + test_int(ecs_get_target(world, e1, Movement, 0), Walking); + test_int(ecs_get_target(world, e2, Movement, 0), Jumping); + test_int(ecs_get_target(world, e3, Movement, 0), Walking); + + ecs_fini(world); +} + +void Union_add_case_in_stage(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_SYSTEM(world, SetCase, EcsOnUpdate, Position, Movement(#0, Walking)); + + ECS_ENTITY(world, e1, Position); + ECS_ENTITY(world, e2, Position); + ECS_ENTITY(world, e3, Position); + + ecs_progress(world, 0); + + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e3, Movement, Walking)); + + ecs_fini(world); +} + +void Union_change_case_in_stage(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_SYSTEM(world, SetCase, EcsOnUpdate, Position, Movement(#0, Walking)); + + ECS_ENTITY(world, e1, Position, (Movement, Running)); + ECS_ENTITY(world, e2, Position, (Movement, Running)); + ECS_ENTITY(world, e3, Position, (Movement, Running)); + + ecs_progress(world, 0); + + test_assert(!ecs_has_pair(world, e1, Movement, Running)); + test_assert(!ecs_has_pair(world, e2, Movement, Running)); + test_assert(!ecs_has_pair(world, e3, Movement, Running)); + + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e3, Movement, Walking)); + + ecs_fini(world); +} + +void Union_change_one_case_in_stage(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_SYSTEM(world, SetCase, EcsOnUpdate, Position, Movement(#0, Jumping), (Movement, Walking)); + + ECS_ENTITY(world, e0, Position, (Movement, Jumping)); + ECS_ENTITY(world, e1, Position, (Movement, Walking)); + ECS_ENTITY(world, e2, Position, (Movement, Running)); + ECS_ENTITY(world, e3, Position, (Movement, Walking)); + ECS_ENTITY(world, e4, Position, (Movement, Running)); + ECS_ENTITY(world, e5, Position, (Movement, Jumping)); + + ecs_progress(world, 0); + + test_assert(ecs_has_pair(world, e0, Movement, Jumping)); + test_assert(ecs_has_pair(world, e1, Movement, Jumping)); + test_assert(ecs_has_pair(world, e2, Movement, Running)); + test_assert(ecs_has_pair(world, e3, Movement, Jumping)); + test_assert(ecs_has_pair(world, e4, Movement, Running)); + test_assert(ecs_has_pair(world, e5, Movement, Jumping)); + + ecs_fini(world); +} + +static +void RemoveSwitch(ecs_iter_t *it) { + ecs_world_t *world = it->world; + ecs_entity_t id = ecs_field_id(it, 0); + + int i; + for (i = 0; i < it->count; i ++) { + ecs_remove_id(world, it->entities[i], id); + } +} + +void Union_remove_switch_in_stage(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_SYSTEM(world, RemoveSwitch, EcsOnUpdate, (Movement, *)); + + ECS_ENTITY(world, e1, Position, (Movement, Walking)); + ECS_ENTITY(world, e2, Position, (Movement, Walking)); + ECS_ENTITY(world, e3, Position, (Movement, Walking)); + + ecs_progress(world, 0); + + test_assert(!ecs_has_pair(world, e1, Movement, Walking)); + test_assert(!ecs_has_pair(world, e2, Movement, Walking)); + test_assert(!ecs_has_pair(world, e3, Movement, Walking)); + + test_assert(!ecs_has_pair(world, e1, Movement, EcsWildcard)); + test_assert(!ecs_has_pair(world, e2, Movement, EcsWildcard)); + test_assert(!ecs_has_pair(world, e3, Movement, EcsWildcard)); + + ecs_fini(world); +} + +void Union_switch_no_match_for_case(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_new_w_pair(world, Movement, Walking); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Running)" + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int count = 0; + while (ecs_query_next(&it)) { + count ++; + } + + test_assert(count == 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_empty_entity_has_case(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + + ecs_entity_t e = ecs_new(world); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + + ecs_fini(world); +} + +void Union_zero_entity_has_case(void) { + install_test_abort(); + + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + + test_expect_abort(); + + test_assert(!ecs_has_pair(world, 0, Movement, Walking)); +} + +void Union_add_to_entity_w_switch(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); + test_assert(e != 0); + + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_int(ecs_get_target(world, e, Movement, 0), Walking); + + ecs_add(world, e, Position); + test_assert( ecs_has(world, e, Position)); + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_int(ecs_get_target(world, e, Movement, 0), Walking); + + ecs_fini(world); +} + +void Union_add_pair_to_entity_w_switch(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Pair); + + ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); + test_assert(e != 0); + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_int(ecs_get_target(world, e, Movement, 0), Walking); + + ecs_entity_t pair_id = ecs_pair(ecs_id(Position), Pair); + ecs_add_id(world, e, pair_id); + test_assert(ecs_has_id(world, e, pair_id)); + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_int(ecs_get_target(world, e, Movement, 0), Walking); + + ecs_fini(world); +} + +void Union_recycled_tags(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + + ECS_TAG(world, Standing); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + test_assert(Standing > UINT32_MAX); + test_assert(Walking > UINT32_MAX); + test_assert(Running > UINT32_MAX); + + ecs_entity_t e = ecs_new_w_pair(world, Movement, Standing); + test_assert(ecs_has_pair(world, e, Movement, Standing)); + test_assert(!ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert(!ecs_has_pair(world, e, Movement, Standing)); + test_assert(ecs_has_pair(world, e, Movement, Walking)); + test_assert(!ecs_has_pair(world, e, Movement, Running)); + + ecs_fini(world); +} + +void Union_same_table_after_change(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); + test_assert(e != 0); + + ecs_table_t *table = ecs_get_table(world, e); + test_assert(table != NULL); + + ecs_add_pair(world, e, Movement, Running); + test_assert(ecs_get_table(world, e) == table); + + ecs_add_pair(world, e, Movement, Jumping); + test_assert(ecs_get_table(world, e) == table); + + ecs_remove_pair(world, e, Movement, EcsWildcard); + test_assert(ecs_get_table(world, e) != table); + test_assert(ecs_get_table(world, e) == NULL); + + ecs_fini(world); +} + +void Union_add_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_ENTITY(world, Direction, Union); + ECS_TAG(world, Up); + ECS_TAG(world, Down); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Movement, Walking); + ecs_add_pair(world, e, Direction, Up); + + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_assert( ecs_has_pair(world, e, Direction, Up)); + + test_assert(ecs_get_target(world, e, Movement, 0) == Walking); + test_assert(ecs_get_target(world, e, Direction, 0) == Up); + + ecs_fini(world); +} + +void Union_add_2_reverse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_ENTITY(world, Direction, Union); + ECS_TAG(world, Up); + ECS_TAG(world, Down); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Direction, Up); + ecs_add_pair(world, e, Movement, Walking); + + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_assert( ecs_has_pair(world, e, Direction, Up)); + + test_assert(ecs_get_target(world, e, Movement, 0) == Walking); + test_assert(ecs_get_target(world, e, Direction, 0) == Up); + + ecs_fini(world); +} + +void Union_add_switch_to_prefab_instance(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union, (OnInstantiate, Inherit)); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_entity_t base = ecs_new(world); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + ecs_add_pair(world, inst, Movement, Walking); + + test_assert(ecs_has_pair(world, inst, Movement, Walking)); + test_assert(ecs_get_target(world, inst, Movement, 0) == Walking); + + ecs_fini(world); +} + +void Union_get_case_w_generation(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Union); + + ecs_entity_t tgt = ecs_new(world); + ecs_delete(world, tgt); + tgt = ecs_new(world); + test_assert(tgt != (uint32_t)tgt); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Rel, tgt); + + ecs_entity_t t = ecs_get_target(world, e, Rel, 0); + test_assert(t == tgt); + + ecs_fini(world); +} + +void Union_get_case_w_generation_not_alive(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Union); + + ecs_entity_t tgt = ecs_new(world); + ecs_delete(world, tgt); + tgt = ecs_new(world); + test_assert(tgt != (uint32_t)tgt); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Rel, tgt); + + ecs_entity_t t = ecs_get_target(world, e, Rel, 0); + test_assert(t == tgt); + + ecs_delete(world, tgt); + + t = ecs_get_target(world, e, Rel, 0); + test_assert(t == tgt); + + ecs_fini(world); +} + +void Union_defer_add_union_relationship(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Union); + ECS_TAG(world, Tgt); + + ecs_entity_t e = ecs_new(world); + ecs_defer_begin(world); + ecs_add_pair(world, e, Rel, Tgt); + test_assert(!ecs_has_pair(world, e, Rel, Tgt)); + ecs_defer_end(world); + test_assert(ecs_has_pair(world, e, Rel, Tgt)); + + ecs_fini(world); +} + +void Union_defer_add_existing_union_relationship(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Union); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Rel, TgtA); + test_assert(ecs_has_pair(world, e, Rel, TgtA)); + + ecs_defer_begin(world); + ecs_add_pair(world, e, Rel, TgtB); + test_assert(ecs_has_pair(world, e, Rel, TgtA)); + test_assert(!ecs_has_pair(world, e, Rel, TgtB)); + ecs_defer_end(world); + test_assert(!ecs_has_pair(world, e, Rel, TgtA)); + test_assert(ecs_has_pair(world, e, Rel, TgtB)); + + ecs_fini(world); +} + +void Union_defer_add_union_relationship_2_ops(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Union); + ECS_TAG(world, Tgt); + ECS_TAG(world, Tag); + + ecs_entity_t e = ecs_new(world); + ecs_defer_begin(world); + ecs_add_pair(world, e, Rel, Tgt); + ecs_add(world, e, Tag); + test_assert(!ecs_has_pair(world, e, Rel, Tgt)); + test_assert(!ecs_has(world, e, Tag)); + ecs_defer_end(world); + test_assert(ecs_has_pair(world, e, Rel, Tgt)); + test_assert(ecs_has(world, e, Tag)); + + ecs_fini(world); +} + +void Union_defer_add_existing_union_relationship_2_ops(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Union); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Tag); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Rel, TgtA); + test_assert(ecs_has_pair(world, e, Rel, TgtA)); + + ecs_defer_begin(world); + ecs_add_pair(world, e, Rel, TgtB); + ecs_add(world, e, Tag); + test_assert(ecs_has_pair(world, e, Rel, TgtA)); + test_assert(!ecs_has_pair(world, e, Rel, TgtB)); + test_assert(!ecs_has(world, e, Tag)); + ecs_defer_end(world); + test_assert(!ecs_has_pair(world, e, Rel, TgtA)); + test_assert(ecs_has_pair(world, e, Rel, TgtB)); + test_assert(ecs_has(world, e, Tag)); + + ecs_fini(world); +} + +void Union_stress_test_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Union); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_add_pair(world, e1, Rel, TgtA); + ecs_add_pair(world, e2, Rel, TgtA); + ecs_add_pair(world, e2, Rel, TgtB); + + ecs_add_pair(world, e2, Rel, TgtA); + ecs_add_pair(world, e2, Rel, TgtB); + ecs_add_pair(world, e1, Rel, TgtC); + + ecs_add_pair(world, e1, Rel, TgtA); + ecs_add_pair(world, e2, Rel, TgtB); + ecs_add_pair(world, e1, Rel, TgtC); + + ecs_add_pair(world, e1, Rel, TgtA); + ecs_add_pair(world, e2, Rel, TgtA); + ecs_add_pair(world, e1, Rel, TgtC); + + test_assert(ecs_has_pair(world, e1, Rel, TgtC)); + test_assert(ecs_has_pair(world, e2, Rel, TgtA)); + + ecs_query_t *q_tgtA = ecs_query(world, { + .terms = {{ ecs_pair(Rel, TgtA) }} + }); + + ecs_query_t *q_tgtB = ecs_query(world, { + .terms = {{ ecs_pair(Rel, TgtB) }} + }); + + ecs_query_t *q_tgtC = ecs_query(world, { + .terms = {{ ecs_pair(Rel, TgtC) }} + }); + + test_int(ecs_query_count(q_tgtA).entities, 1); + test_int(ecs_query_count(q_tgtB).entities, 0); + test_int(ecs_query_count(q_tgtC).entities, 1); + + ecs_query_fini(q_tgtA); + ecs_query_fini(q_tgtB); + ecs_query_fini(q_tgtC); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/World.c b/vendors/flecs/test/core/src/World.c similarity index 77% rename from vendors/flecs/test/api/src/World.c rename to vendors/flecs/test/core/src/World.c index 966959f03..357b0795d 100644 --- a/vendors/flecs/test/api/src/World.c +++ b/vendors/flecs/test/core/src/World.c @@ -1,4 +1,4 @@ -#include +#include #include void World_setup(void) { @@ -7,8 +7,8 @@ void World_setup(void) { static void Move(ecs_iter_t *it) { - Position *pos = ecs_field(it, Position, 1); - Velocity *vel = ecs_field(it, Velocity, 2); + Position *pos = ecs_field(it, Position, 0); + Velocity *vel = ecs_field(it, Velocity, 1); probe_iter(it); int row; @@ -101,7 +101,7 @@ void World_entity_range_offset(void) { ecs_set_entity_range(world, 5000, 0); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(e, 5000); ecs_fini(world); @@ -146,18 +146,18 @@ void World_entity_range_out_of_range_check_disabled(void) { ECS_COMPONENT(world, Position); - ecs_ensure(world, 4999); + ecs_make_alive(world, 4999); ecs_enable_range_check(world, false); ecs_set_entity_range(world, 5000, 10000); /* Validate that range is being used when issuing new ids */ - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_int(e, 5000); /* Validate that application does not abort when changing out of range */ - ecs_entity_t e2 = ecs_set(world, 4999, Position, {10, 20}); - test_int(e2, 4999); + ecs_entity_t e2 = 4999; + ecs_set(world, e2, Position, {10, 20}); test_assert( ecs_has(world, e2, Position)); const Position *p = ecs_get(world, e2, Position); @@ -176,13 +176,13 @@ void World_entity_range_check_after_delete(void) { ecs_enable_range_check(world, true); ecs_set_entity_range(world, 5000, 10000); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert(e != 0); test_assert(e == 5000); ecs_delete(world, e); - e = ecs_new(world, 0); + e = ecs_new(world); test_assert(e != 0); test_assert((uint32_t)e == 5000); @@ -195,13 +195,13 @@ void World_entity_range_add_existing_staged(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert(e < 1000); ecs_set_entity_range(world, 1000, 1500); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_add(stage, e, Velocity); ecs_readonly_end(world); @@ -217,10 +217,10 @@ void World_entity_range_add_in_range_staged(void) { ecs_set_entity_range(world, 500, 1000); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e == 500); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_add(stage, e, Velocity); ecs_readonly_end(world); @@ -229,7 +229,7 @@ void World_entity_range_add_in_range_staged(void) { } void AddOutOfRange(ecs_iter_t *it) { - ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); + ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { @@ -250,10 +250,10 @@ void World_entity_range_add_out_of_range_staged(void) { ecs_set_entity_range(world, 500, 1000); /* Dummy entity to invoke the system */ - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e == 500); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_add(stage, e, Velocity); ecs_readonly_end(world); @@ -261,6 +261,99 @@ void World_entity_range_add_out_of_range_staged(void) { ecs_fini(world); } +void World_entity_range_offset_0(void) { + ecs_world_t *world = ecs_mini(); + + const ecs_world_info_t *info = ecs_get_world_info(world); + test_assert(info != NULL); + + ecs_set_entity_range(world, 0, 1000); + + test_uint(info->min_id, ecs_get_max_id(world) + 1); + test_uint(info->max_id, 1000); + + ecs_fini(world); +} + +void World_entity_range_set_limit_to_lower(void) { + ecs_world_t *world = ecs_mini(); + + const ecs_world_info_t *info = ecs_get_world_info(world); + test_assert(info != NULL); + + ecs_set_entity_range(world, 0, 2000); + + test_uint(info->max_id, 2000); + + ecs_set_entity_range(world, 0, 1000); + + test_uint(info->max_id, 1000); + + ecs_fini(world); +} + +void World_entity_range_set_limit_to_lower_than_offset(void) { + ecs_world_t *world = ecs_mini(); + + const ecs_world_info_t *info = ecs_get_world_info(world); + test_assert(info != NULL); + + ecs_set_entity_range(world, 2000, 3000); + + test_uint(info->max_id, 3000); + + ecs_set_entity_range(world, 0, 1000); + + test_uint(info->max_id, 1000); + + ecs_fini(world); +} + +void World_entity_range_overlapping_new_id(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + const ecs_world_info_t *info = ecs_get_world_info(world); + test_assert(info != NULL); + + ecs_set_entity_range(world, 2000, 3000); + test_uint(info->max_id, 3000); + + ecs_entity_t e1 = ecs_new(world); + test_assert(e1 == 2000); + + ecs_set_entity_range(world, 1999, 0); + + ecs_entity_t e2 = ecs_new(world); + test_assert(e2 == 1999); + + test_expect_abort(); + ecs_new(world); +} + +void World_entity_range_overlapping_new_bulk_id(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + const ecs_world_info_t *info = ecs_get_world_info(world); + test_assert(info != NULL); + + ecs_set_entity_range(world, 2000, 3000); + test_uint(info->max_id, 3000); + + ecs_entity_t e1 = ecs_new(world); + test_assert(e1 == 2000); + + ecs_set_entity_range(world, 1999, 0); + + test_expect_abort(); + ecs_bulk_new(world, Position, 2); +} + void World_get_tick(void) { ecs_world_t *world = ecs_init(); @@ -312,7 +405,7 @@ void World_dim(void) { /* Create single entity so that the table exists. This makes the allocation * counts more predictable, as new_w_count won't trigger table creation */ - ecs_new(world, Position); + ecs_new_w(world, Position); ecs_dim(world, 1100); @@ -333,7 +426,7 @@ void World_dim(void) { static void TOnLoad(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 0); @@ -343,7 +436,7 @@ void TOnLoad(ecs_iter_t *it) { static void TPostLoad(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 1); @@ -353,7 +446,7 @@ void TPostLoad(ecs_iter_t *it) { static void TPreUpdate(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 2); @@ -363,7 +456,7 @@ void TPreUpdate(ecs_iter_t *it) { static void TOnUpdate(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 3); @@ -373,7 +466,7 @@ void TOnUpdate(ecs_iter_t *it) { static void TOnValidate(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 4); @@ -383,7 +476,7 @@ void TOnValidate(ecs_iter_t *it) { static void TPostUpdate(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 5); @@ -393,7 +486,7 @@ void TPostUpdate(ecs_iter_t *it) { static void TPreStore(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 6); @@ -403,7 +496,7 @@ void TPreStore(ecs_iter_t *it) { static void TOnStore(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 7); @@ -413,7 +506,7 @@ void TOnStore(ecs_iter_t *it) { static void TManual(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 8); @@ -436,7 +529,7 @@ void World_phases(void) { ECS_SYSTEM(world, TOnStore, EcsOnStore, Position); ECS_SYSTEM(world, TManual, 0, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_set(world, e, Position, {0, 0}); @@ -458,7 +551,7 @@ void World_phases_match_in_create(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_set(world, e, Position, {0, 0}); @@ -487,8 +580,8 @@ void World_phases_match_in_create(void) { static void TMergeOnLoad(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -499,8 +592,8 @@ void TMergeOnLoad(ecs_iter_t *it) { static void TMergePostLoad(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -511,8 +604,8 @@ void TMergePostLoad(ecs_iter_t *it) { static void TMergePreUpdate(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -523,8 +616,8 @@ void TMergePreUpdate(ecs_iter_t *it) { static void TMergeOnUpdate(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -535,8 +628,8 @@ void TMergeOnUpdate(ecs_iter_t *it) { static void TMergeOnValidate(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -547,8 +640,8 @@ void TMergeOnValidate(ecs_iter_t *it) { static void TMergePostUpdate(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -559,8 +652,8 @@ void TMergePostUpdate(ecs_iter_t *it) { static void TMergePreStore(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -571,8 +664,8 @@ void TMergePreStore(ecs_iter_t *it) { static void TMergeOnStore(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -583,8 +676,8 @@ void TMergeOnStore(ecs_iter_t *it) { static void TMergeManual(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); + Position *p = ecs_field(it, Position, 0); + ecs_id_t ecs_id(Position) = ecs_field_id(it, 0); int i; for (i = 0; i < it->count; i ++) { @@ -608,7 +701,7 @@ void World_phases_w_merging(void) { ECS_SYSTEM(world, TMergeOnStore, EcsOnStore, Position, [out] Position()); ECS_SYSTEM(world, TMergeManual, 0, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_set(world, e, Position, {0, 0}); @@ -638,7 +731,7 @@ void World_measure_time(void) { ECS_SYSTEM(world, TimeCheck, EcsOnLoad, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); int i = 0; @@ -658,7 +751,7 @@ void World_control_fps(void) { ECS_SYSTEM(world, TimeCheck, EcsOnLoad, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); double start, now = 0; @@ -991,7 +1084,7 @@ void World_no_time(void) { void World_is_entity_enabled(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); test_assert( ecs_has_id(world, e, EcsDisabled) == false); @@ -1030,7 +1123,7 @@ void World_system_time_scale(void) { void World_ensure_empty_root(void) { ecs_world_t *world = ecs_init(); - ecs_query_t *q = ecs_query_new(world, "!(ChildOf, *)"); + ecs_query_t *q = ecs_query(world, { .expr = "!(ChildOf, *)" }); ecs_iter_t it = ecs_query_iter(world, q); /* Make sure that the only entity in the root is the flecs module */ @@ -1038,20 +1131,17 @@ void World_ensure_empty_root(void) { test_int(it.count, 1); test_assert(it.entities[0] == EcsFlecs); - /* Entity for the query */ - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_assert(ecs_has_id(world, it.entities[0], EcsQuery)); - test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } void World_register_alias_twice_same_entity(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_set_alias(world, e, "Foo"); ecs_set_alias(world, e, "Foo"); @@ -1067,8 +1157,8 @@ void World_register_alias_twice_different_entity(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); - ecs_entity_t f = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); + ecs_entity_t f = ecs_new(world); ecs_set_alias(world, e, "Foo"); @@ -1132,9 +1222,9 @@ void World_delete_1000_empty_tables(void) { const ecs_world_info_t *info = ecs_get_world_info(world); int32_t old_empty_table_count = info->empty_table_count; - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); for (int i = 0; i < 1000; i ++) { - ecs_add_id(world, e, ecs_new_id(world)); + ecs_add_id(world, e, ecs_new(world)); } ecs_run_aperiodic(world, 0); @@ -1163,14 +1253,14 @@ void World_delete_empty_tables_for_id(void) { const ecs_world_info_t *info = ecs_get_world_info(world); int32_t old_empty_table_count = info->empty_table_count; - ecs_entity_t e1 = ecs_new(world, TagA); + ecs_entity_t e1 = ecs_new_w(world, TagA); for (int i = 0; i < 500; i ++) { - ecs_add_id(world, e1, ecs_new_id(world)); + ecs_add_id(world, e1, ecs_new(world)); } - ecs_entity_t e2 = ecs_new(world, TagB); + ecs_entity_t e2 = ecs_new_w(world, TagB); for (int i = 0; i < 500; i ++) { - ecs_add_id(world, e2, ecs_new_id(world)); + ecs_add_id(world, e2, ecs_new(world)); } ecs_run_aperiodic(world, 0); @@ -1196,7 +1286,7 @@ void World_use_after_delete_empty(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); @@ -1220,7 +1310,7 @@ void World_use_after_clear_empty(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); @@ -1244,7 +1334,7 @@ void World_use_after_delete_empty_w_component(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); @@ -1281,7 +1371,7 @@ void World_use_after_clear_empty_w_component(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); @@ -1319,13 +1409,13 @@ void World_use_after_clear_empty_w_component_w_lifecycle(void) { ECS_COMPONENT(world, Velocity); ecs_set_hooks(world, Position, { - .ctor = ecs_default_ctor + .ctor = flecs_default_ctor }); ecs_set_hooks(world, Velocity, { - .ctor = ecs_default_ctor + .ctor = flecs_default_ctor }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); @@ -1368,7 +1458,7 @@ void World_use_after_clear_unused(void) { deleted = ecs_delete_empty_tables(world, 0, 1, 0, 0, 0); test_assert(deleted == 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); @@ -1391,11 +1481,11 @@ void at_fini_test( ecs_world_t* world, void* context) { - test_assert(ecs_singleton_get_mut(world, Test) != NULL); + test_assert(ecs_singleton_ensure(world, Test) != NULL); at_fini_test_invoked = 1; } -void World_get_mut_in_at_fini(void) { +void World_ensure_in_at_fini(void) { ecs_world_t* world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Test); @@ -1445,7 +1535,7 @@ void World_get_type_info_after_reuse(void) { ecs_delete_with(world, ecs_id(Position)); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); test_assert( ecs_has(world, e, Position)); @@ -1467,6 +1557,42 @@ void World_no_name_prefix_after_init(void) { ecs_fini(world); } +void World_component_init_w_name_prefix(void) { + ecs_world_t *world = ecs_mini(); + + ecs_set_name_prefix(world, "Nested"); + + ecs_entity_t pos = ecs_component(world, { + .type = { + .name = "NestedPosition", + .size = 8, + .alignment = 4 + } + }); + + test_assert(pos != 0); + test_str(ecs_get_name(world, pos), "Position"); + + ecs_fini(world); +} + +typedef struct NestedPosition { + float x, y; +} NestedPosition; + +void World_component_macro_w_name_prefix(void) { + ecs_world_t *world = ecs_mini(); + + ecs_set_name_prefix(world, "Nested"); + + ECS_COMPONENT(world, NestedPosition); + + test_assert(ecs_id(NestedPosition) != 0); + test_str(ecs_get_name(world, ecs_id(NestedPosition)), "Position"); + + ecs_fini(world); +} + void World_set_get_context(void) { ecs_world_t *world = ecs_mini(); @@ -1520,3 +1646,139 @@ void World_set_get_binding_context_w_free(void) { test_int(ctx, 10); } + +void World_get_entities(void) { + ecs_world_t *world = ecs_mini(); + + int32_t count; + int32_t alive_count; + + { + ecs_entities_t entities = ecs_get_entities(world); + test_assert(entities.alive_count != 0); + test_assert(entities.count != 0); + test_assert(entities.count == entities.alive_count); + test_assert(entities.ids != NULL); + test_assert(entities.ids[0] != 0); + + count = entities.count; + alive_count = entities.alive_count; + } + + ecs_entity_t e = ecs_new(world); + + { + ecs_entities_t entities = ecs_get_entities(world); + test_assert(entities.alive_count != 0); + test_assert(entities.count != 0); + test_assert(entities.count == entities.alive_count); + test_assert(entities.count == count + 1); + test_assert(entities.ids != NULL); + test_assert(entities.ids[0] != 0); + test_assert(entities.ids[entities.count - 1] == e); + + count = entities.count; + alive_count = entities.alive_count; + } + + ecs_delete(world, e); + + { + ecs_entities_t entities = ecs_get_entities(world); + test_assert(entities.alive_count != 0); + test_assert(entities.count != 0); + test_assert(entities.count == (entities.alive_count + 1)); + test_assert(entities.count == count); + test_assert(entities.alive_count == (alive_count - 1)); + test_assert(entities.ids != NULL); + test_assert(entities.ids[0] != 0); + test_assert((uint32_t)entities.ids[entities.count - 1] == (uint32_t)e); + test_assert(entities.ids[entities.count - 1] != e); + + count = entities.count; + alive_count = entities.alive_count; + } + + ecs_fini(world); +} + +static +int post_frame_action_invoked = 0; + +static +void post_frame_action( + ecs_world_t *world, + void *ctx) +{ + test_int(*(int*)ctx, 10); + post_frame_action_invoked ++; +} + +void World_run_post_frame(void) { + ecs_world_t *world = ecs_mini(); + + ecs_frame_begin(world, 0); + + int ctx = 10; + ecs_run_post_frame(world, post_frame_action, &ctx); + + test_int(post_frame_action_invoked, 0); + ecs_frame_end(world); + test_int(post_frame_action_invoked, 1); + + ecs_fini(world); +} + +void World_run_post_frame_outside_of_frame(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + test_expect_abort(); + int ctx = 10; + ecs_run_post_frame(world, post_frame_action, &ctx); +} + +void World_get_flags(void) { + ecs_world_t *world = ecs_mini(); + + test_assert(!(ecs_world_get_flags(world) & EcsWorldFrameInProgress)); + + ecs_frame_begin(world, 0); + + test_assert((ecs_world_get_flags(world) & EcsWorldFrameInProgress)); + + ecs_frame_end(world); + + test_assert(!(ecs_world_get_flags(world) & EcsWorldFrameInProgress)); + + ecs_fini(world); +} + +void World_fini_queue_overflow(void) { + /* This test verifies command flushing happens in batches during + world fini to avoid overflowing the vector holding the + command queue */ + + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + /* create a prefab entity: */ + ecs_entity_t prefab = ecs_new(world); + ecs_add_id(world, prefab, EcsPrefab); + ecs_add_id(world, prefab, ecs_id(Position)); + + /* Create a large amount of entities. A number greater than 16777216, + 2^30 / sizeof(ecs_cmd_t) would overflow the command queue vector */ + + ecs_bulk_init(world, &(ecs_bulk_desc_t) { + .count = 17000000, + .ids = { ecs_isa(prefab) } + }); + + /* on world fini, all entities must be destroyed in batches. */ + ecs_fini(world); + + test_assert(true); /* if ecs_fini did not crash we're good */ +} \ No newline at end of file diff --git a/vendors/flecs/test/api/src/WorldInfo.c b/vendors/flecs/test/core/src/WorldInfo.c similarity index 86% rename from vendors/flecs/test/api/src/WorldInfo.c rename to vendors/flecs/test/core/src/WorldInfo.c index d2fd11c64..a5eb5726d 100644 --- a/vendors/flecs/test/api/src/WorldInfo.c +++ b/vendors/flecs/test/core/src/WorldInfo.c @@ -1,4 +1,4 @@ -#include +#include #define test_delta(prev, cur, field, value)\ test_int(value, (cur)->field - (prev)->field);\ @@ -27,8 +27,8 @@ void WorldInfo_table_count(void) { ecs_world_info_t prev = *cur; - ecs_entity_t c = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); + ecs_entity_t e = ecs_new(world); test_delta(&prev, cur, table_count, 0); ecs_add_id(world, e, c); @@ -48,8 +48,8 @@ void WorldInfo_empty_table_count(void) { ecs_world_info_t prev = *cur; - ecs_entity_t c = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t c = ecs_new(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, c); ecs_run_aperiodic(world, 0); @@ -74,9 +74,9 @@ void WorldInfo_table_create_count(void) { ecs_world_info_t prev = *cur; - ecs_entity_t c_1 = ecs_new_id(world); - ecs_entity_t c_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t c_1 = ecs_new(world); + ecs_entity_t c_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); test_delta(&prev, cur, table_create_total, 0); ecs_add_id(world, e, c_1); @@ -100,16 +100,16 @@ void WorldInfo_table_delete_count(void) { ecs_world_info_t prev = *cur; - ecs_entity_t c_1 = ecs_new_id(world); - ecs_entity_t c_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t c_1 = ecs_new(world); + ecs_entity_t c_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, c_1); test_delta(&prev, cur, table_delete_total, 0); ecs_add_id(world, e, c_2); test_delta(&prev, cur, table_delete_total, 0); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_add_id(world, e2, c_2); test_delta(&prev, cur, table_delete_total, 0); @@ -135,9 +135,9 @@ void WorldInfo_id_tag_component_count(void) { ecs_world_info_t prev_1 = *cur; ecs_world_info_t prev_2 = *cur; - ecs_entity_t c_1 = ecs_new_id(world); - ecs_entity_t c_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t c_1 = ecs_new(world); + ecs_entity_t c_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); test_delta(&prev_1, cur, tag_id_count, 0); test_delta(&prev_2, cur, component_id_count, 0); @@ -176,9 +176,9 @@ void WorldInfo_id_pair_count(void) { ecs_world_info_t prev = *cur; - ecs_entity_t c_1 = ecs_new_id(world); - ecs_entity_t c_2 = ecs_new_id(world); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t c_1 = ecs_new(world); + ecs_entity_t c_2 = ecs_new(world); + ecs_entity_t e = ecs_new(world); test_delta(&prev, cur, pair_id_count, 0); ecs_add_id(world, e, c_1); diff --git a/vendors/flecs/test/api/src/main.c b/vendors/flecs/test/core/src/main.c similarity index 66% rename from vendors/flecs/test/api/src/main.c rename to vendors/flecs/test/core/src/main.c index aa18c195a..99d1d7e77 100644 --- a/vendors/flecs/test/api/src/main.c +++ b/vendors/flecs/test/core/src/main.c @@ -6,7 +6,7 @@ * ---------------------------------------------------------------------------- */ -#include +#include // Testsuite 'Id' void Id_0_is_wildcard(void); @@ -31,6 +31,14 @@ void Id_pair_id_w_rel_component_obj_wildcard_is_tag(void); void Id_pair_id_w_obj_wildcard_is_tag(void); void Id_pair_id_w_tag_property_w_obj_component_is_tag(void); void Id_pair_id_w_tag_property_w_obj_wildcard_is_tag(void); +void Id_pair_w_rel_wildcard_is_tag(void); +void Id_pair_w_obj_wildcard_is_tag(void); +void Id_pair_w_rel_tag_obj_wildcard_is_tag(void); +void Id_pair_w_wildcard_wildcard_is_tag(void); +void Id_pair_w_rel_any_is_tag(void); +void Id_pair_w_obj_any_is_tag(void); +void Id_pair_w_rel_tag_obj_any_is_tag(void); +void Id_pair_w_any_any_is_tag(void); void Id_id_w_override_is_tag(void); void Id_id_w_toggle_is_tag(void); void Id_pair_id_override_is_tag(void); @@ -38,6 +46,16 @@ void Id_pair_id_toggle_is_tag(void); void Id_make_pair(void); void Id_make_pair_of_pair(void); void Id_make_pair_of_pair_tgt(void); +void Id_0_entity(void); +void Id_entity_from_str(void); +void Id_unresolved_entity_from_str(void); +void Id_scoped_entity_from_str(void); +void Id_template_entity_from_str(void); +void Id_pair_from_str(void); +void Id_unresolved_pair_from_str(void); +void Id_wildcard_pair_from_str(void); +void Id_any_pair_from_str(void); +void Id_invalid_pair(void); // Testsuite 'Entity' void Entity_init_id(void); @@ -93,12 +111,12 @@ void Entity_init_w_name_staged(void); void Entity_record_find_for_empty(void); void Entity_record_find(void); void Entity_record_find_from_stage(void); -void Entity_ensure_zero_gen(void); -void Entity_ensure_nonzero_gen(void); -void Entity_ensure_zero_gen_exists(void); -void Entity_ensure_nonzero_gen_exists(void); -void Entity_ensure_zero_gen_exists_alive(void); -void Entity_ensure_nonzero_gen_exists_alive(void); +void Entity_make_alive_zero_gen(void); +void Entity_make_alive_nonzero_gen(void); +void Entity_make_alive_zero_gen_exists(void); +void Entity_make_alive_nonzero_gen_exists(void); +void Entity_make_alive_zero_gen_exists_alive(void); +void Entity_make_alive_nonzero_gen_exists_alive(void); void Entity_set_scope_w_entity_init_from_stage(void); void Entity_entity_init_w_scope_twice(void); void Entity_entity_init_w_childof_twice(void); @@ -133,19 +151,118 @@ void Entity_entity_init_w_empty_sep_w_prefix(void); void Entity_set_name_w_same_ptr(void); void Entity_set_name_w_overlapping_ptr(void); void Entity_defer_set_name_w_overlapping_ptr(void); -void Entity_ensure_from_stage(void); -void Entity_ensure_after_deleted_1_entity(void); -void Entity_ensure_after_deleted_2_entities(void); +void Entity_make_alive_from_stage(void); +void Entity_make_alive_after_deleted_1_entity(void); +void Entity_make_alive_after_deleted_2_entities(void); void Entity_defer_entity_init_w_set_name_w_add_childof(void); void Entity_entity_w_digit_name(void); void Entity_entity_w_existing_digit_name(void); -void Entity_entity_w_conflicting_digit_name(void); -void Entity_set_generation_on_nonempty_entity(void); -void Entity_set_generation_while_deferred(void); +void Entity_entity_from_digit(void); +void Entity_entity_from_existing_digit(void); +void Entity_entity_from_digit_path(void); +void Entity_entity_from_existing_digit_path(void); +void Entity_entity_from_digit_0_path(void); +void Entity_entity_from_conflicting_digit(void); +void Entity_set_version_on_nonempty_entity(void); +void Entity_set_version_while_deferred(void); void Entity_commit_w_on_add(void); void Entity_commit_w_on_remove(void); void Entity_commit_w_cmd_in_observer(void); void Entity_entity_init_existing_no_sep(void); +void Entity_entity_init_w_set_1_comp(void); +void Entity_entity_init_w_set_2_comp(void); +void Entity_entity_init_w_set_1_comp_1_tag(void); +void Entity_entity_init_w_set_2_comp_2_tag(void); +void Entity_entity_init_w_set_1_comp_w_name(void); +void Entity_entity_init_w_set_1_comp_existing(void); +void Entity_entity_init_w_set_1_comp_existing_empty(void); +void Entity_entity_init_w_set_1_comp_1_tag_w_set(void); +void Entity_entity_init_w_set_w_hook(void); +void Entity_entity_init_w_set_w_observer(void); +void Entity_entity_init_w_set_1_comp_defer(void); +void Entity_entity_init_w_set_2_comp_defer(void); +void Entity_entity_init_w_set_1_comp_1_tag_defer(void); +void Entity_entity_init_w_set_2_comp_2_tag_defer(void); +void Entity_entity_init_w_set_1_comp_w_name_defer(void); +void Entity_entity_init_w_set_1_comp_existing_defer(void); +void Entity_entity_init_w_set_1_comp_existing_empty_defer(void); +void Entity_entity_init_w_set_1_comp_1_tag_w_set_defer(void); +void Entity_insert_1_comp(void); +void Entity_insert_2_comp(void); +void Entity_insert_1_comp_1_tag(void); +void Entity_entity_w_parent(void); +void Entity_entity_w_parent_w_name(void); +void Entity_entity_w_parent_w_add(void); +void Entity_entity_w_parent_w_add_w_parent(void); +void Entity_entity_w_parent_w_set(void); +void Entity_entity_w_parent_w_set_w_parent(void); + +// Testsuite 'Each' +void Each_each_tag(void); +void Each_each_component(void); +void Each_each_pair(void); +void Each_each_pair_rel_wildcard(void); +void Each_each_pair_tgt_wildcard(void); + +// Testsuite 'Iter' +void Iter_page_iter_0_0(void); +void Iter_page_iter_1_0(void); +void Iter_page_iter_0_1(void); +void Iter_page_iter_n_0(void); +void Iter_page_iter_0_n(void); +void Iter_page_iter_m_n(void); +void Iter_page_iter_skip_1_table(void); +void Iter_page_iter_skip_2_tables(void); +void Iter_worker_iter_1(void); +void Iter_worker_iter_2(void); +void Iter_worker_iter_3(void); +void Iter_worker_iter_4(void); +void Iter_paged_iter_w_shared_comp(void); +void Iter_worker_iter_w_shared_comp(void); +void Iter_paged_iter_w_task_query(void); +void Iter_worker_iter_w_task_query(void); +void Iter_worker_iter_w_singleton(void); +void Iter_worker_iter_w_singleton_instanced(void); +void Iter_worker_iter_w_singleton_component_instanced(void); +void Iter_paged_iter_w_singleton(void); +void Iter_paged_iter_w_singleton_instanced(void); +void Iter_paged_iter_w_singleton_component_instanced(void); +void Iter_page_iter_w_offset_skip_1_archetype(void); +void Iter_page_iter_w_offset_skip_1_archetype_plus_one(void); +void Iter_page_iter_w_offset_skip_2_archetypes(void); +void Iter_page_iter_w_limit_skip_1_archetype(void); +void Iter_page_iter_w_limit_skip_1_archetype_minus_one(void); +void Iter_page_iter_w_limit_skip_2_archetypes(void); +void Iter_page_iter_w_offset_1_limit_max(void); +void Iter_page_iter_w_offset_1_limit_minus_1(void); +void Iter_page_iter_w_offset_2_type_limit_max(void); +void Iter_page_iter_w_offset_2_type_limit_minus_1(void); +void Iter_page_iter_w_limit_1_all_offsets(void); +void Iter_page_iter_w_offset_out_of_bounds(void); +void Iter_page_iter_w_limit_out_of_bounds(void); +void Iter_page_iter_comb_10_entities_1_type(void); +void Iter_page_iter_comb_10_entities_2_types(void); +void Iter_count(void); +void Iter_iter_restore_stack_iter(void); +void Iter_interleaved_iter(void); +void Iter_get_first(void); +void Iter_page_iter_w_only_tag(void); +void Iter_worker_iter_w_only_tag(void); +void Iter_page_iter_w_inout_none(void); +void Iter_worker_iter_w_inout_none(void); +void Iter_page_iter_w_ctx(void); +void Iter_page_iter_w_binding_ctx(void); +void Iter_worker_iter_w_ctx(void); +void Iter_worker_iter_w_binding_ctx(void); +void Iter_column_index_owned(void); +void Iter_column_index_shared(void); +void Iter_column_index_not(void); +void Iter_page_iter_w_fini(void); +void Iter_worker_iter_w_fini(void); +void Iter_rule_page_iter_w_fini(void); +void Iter_rule_worker_iter_w_fini(void); +void Iter_to_str_before_next(void); +void Iter_to_str(void); // Testsuite 'Search' void Search_search(void); @@ -168,9 +285,6 @@ void Search_search_relation_inherit_from_parent(void); void Search_search_relation_dont_inherit(void); void Search_search_relation_dont_inherit_from_parent(void); void Search_search_relation_exclusive(void); -void Search_search_relation_union(void); -void Search_search_relation_union_wildcard(void); -void Search_search_relation_union_pair(void); // Testsuite 'Event' void Event_table_1_id_w_trigger(void); @@ -188,7 +302,9 @@ void Event_emit_custom_for_any(void); void Event_emit_custom_implicit_any(void); void Event_emit_custom_empty_type(void); void Event_emit_w_param(void); +void Event_emit_w_param_multi_observer(void); void Event_emit_w_const_param(void); +void Event_emit_nested(void); void Event_enqueue_event_1_id(void); void Event_enqueue_event_2_ids(void); void Event_enqueue_event_w_data(void); @@ -205,6 +321,7 @@ void Event_enqueue_event_not_deferred(void); void Event_enqueue_event_not_deferred_to_async(void); void Event_enqueue_custom_implicit_any(void); void Event_enqueue_custom_after_large_cmd(void); +void Event_enqueue_on_readonly_world(void); // Testsuite 'New' void New_setup(void); @@ -225,7 +342,6 @@ void New_new_w_entity_0(void); void New_create_w_explicit_id_2_worlds(void); void New_new_w_id_0_w_with(void); void New_new_w_id_w_with(void); -void New_new_w_type_0_w_with(void); void New_new_w_type_w_with(void); void New_new_w_id_w_with_w_scope(void); void New_new_w_type_w_with_w_scope(void); @@ -233,6 +349,11 @@ void New_new_w_id_w_with_defer(void); void New_new_w_id_w_with_defer_w_scope(void); void New_new_w_type_w_with_defer(void); void New_new_w_type_w_with_defer_w_scope(void); +void New_new_w_table(void); +void New_new_w_null_table(void); +void New_new_w_table_component(void); +void New_new_w_table_sparse_component(void); +void New_new_w_table_override(void); // Testsuite 'New_w_Count' void New_w_Count_empty(void); @@ -284,109 +405,6 @@ void Add_invalid_pair_w_0_rel(void); void Add_invalid_pair_w_0_obj(void); void Add_add_random_id(void); -// Testsuite 'Switch' -void Switch_get_case_no_switch(void); -void Switch_get_case_set(void); -void Switch_get_case_change(void); -void Switch_remove_case(void); -void Switch_remove_last(void); -void Switch_delete_first(void); -void Switch_delete_last(void); -void Switch_delete_first_last(void); -void Switch_3_entities_same_case(void); -void Switch_2_entities_1_change_case(void); -void Switch_3_entities_change_case(void); -void Switch_query_switch(void); -void Switch_query_1_case_1_type(void); -void Switch_query_1_case_2_types(void); -void Switch_query_2_cases_1_type(void); -void Switch_query_2_cases_2_types(void); -void Switch_query_after_remove(void); -void Switch_add_case_in_stage(void); -void Switch_change_case_in_stage(void); -void Switch_change_one_case_in_stage(void); -void Switch_remove_switch_in_stage(void); -void Switch_switch_no_match_for_case(void); -void Switch_empty_entity_has_case(void); -void Switch_zero_entity_has_case(void); -void Switch_add_to_entity_w_switch(void); -void Switch_add_pair_to_entity_w_switch(void); -void Switch_sort(void); -void Switch_recycled_tags(void); -void Switch_query_recycled_tags(void); -void Switch_single_case(void); -void Switch_match_switch_on_base_instance(void); -void Switch_switch_w_bitset_query(void); -void Switch_switch_w_bitset_query_inv(void); -void Switch_switch_w_bitset_query_2_elems(void); -void Switch_switch_w_bitset_query_2_elems_skip(void); -void Switch_switch_w_bitset_query_elems_interleaved(void); -void Switch_switch_w_bitset_query_elems_interleaved_2_types(void); -void Switch_has_wildcard(void); -void Switch_remove_wildcard(void); -void Switch_same_table_after_change(void); -void Switch_component_relation(void); -void Switch_delete_case_trigger_after_delete_switch(void); -void Switch_add_2(void); -void Switch_add_2_reverse(void); -void Switch_add_switch_to_prefab_instance(void); -void Switch_get_case_w_generation(void); -void Switch_get_case_w_generation_not_alive(void); - -// Testsuite 'EnabledComponents' -void EnabledComponents_is_component_enabled(void); -void EnabledComponents_is_empty_entity_disabled(void); -void EnabledComponents_is_0_entity_disabled(void); -void EnabledComponents_is_0_component_disabled(void); -void EnabledComponents_is_nonexist_component_disabled(void); -void EnabledComponents_is_enabled_component_enabled(void); -void EnabledComponents_is_disabled_component_enabled(void); -void EnabledComponents_has_enabled_component(void); -void EnabledComponents_is_enabled_after_add(void); -void EnabledComponents_is_enabled_after_remove(void); -void EnabledComponents_is_enabled_after_disable(void); -void EnabledComponents_is_disabled_after_enable(void); -void EnabledComponents_is_enabled_randomized(void); -void EnabledComponents_is_enabled_after_add_randomized(void); -void EnabledComponents_is_enabled_after_randomized_add_randomized(void); -void EnabledComponents_is_enabled_2(void); -void EnabledComponents_is_enabled_3(void); -void EnabledComponents_is_enabled_2_after_add(void); -void EnabledComponents_is_enabled_3_after_add(void); -void EnabledComponents_is_pair_enabled(void); -void EnabledComponents_is_enabled_pair_enabled(void); -void EnabledComponents_is_disabled_pair_enabled(void); -void EnabledComponents_has_enabled_pair(void); -void EnabledComponents_is_pair_enabled_after_add(void); -void EnabledComponents_is_pair_enabled_after_remove(void); -void EnabledComponents_is_pair_enabled_after_disable(void); -void EnabledComponents_is_pair_disabled_after_enable(void); -void EnabledComponents_is_pair_enabled_2(void); -void EnabledComponents_is_pair_enabled_3(void); -void EnabledComponents_is_pair_enabled_2_after_add(void); -void EnabledComponents_is_pair_enabled_3_after_add(void); -void EnabledComponents_query_disabled(void); -void EnabledComponents_query_disabled_skip_initial(void); -void EnabledComponents_query_disabled_pair(void); -void EnabledComponents_query_disabled_pair_skip_initial(void); -void EnabledComponents_query_mod_2(void); -void EnabledComponents_query_mod_8(void); -void EnabledComponents_query_mod_64(void); -void EnabledComponents_query_mod_256(void); -void EnabledComponents_query_mod_1024(void); -void EnabledComponents_query_enable_mod_10(void); -void EnabledComponents_query_mod_2_2_bitsets(void); -void EnabledComponents_query_mod_8_2_bitsets(void); -void EnabledComponents_query_mod_64_2_bitsets(void); -void EnabledComponents_query_mod_256_2_bitsets(void); -void EnabledComponents_query_mod_1024_2_bitsets(void); -void EnabledComponents_query_randomized_2_bitsets(void); -void EnabledComponents_query_randomized_3_bitsets(void); -void EnabledComponents_query_randomized_4_bitsets(void); -void EnabledComponents_defer_enable(void); -void EnabledComponents_sort(void); -void EnabledComponents_table_move_2_from_3(void); - // Testsuite 'Remove' void Remove_zero(void); void Remove_1_of_1(void); @@ -408,6 +426,149 @@ void GlobalComponentIds_declare_tag_w_entity(void); void GlobalComponentIds_declare_entity(void); void GlobalComponentIds_reuse_300_component_ids(void); +// Testsuite 'Sparse' +void Sparse_has(void); +void Sparse_owns(void); +void Sparse_get(void); +void Sparse_get_mut(void); +void Sparse_ensure(void); +void Sparse_emplace(void); +void Sparse_set(void); +void Sparse_modified_no_on_set(void); +void Sparse_insert_1(void); +void Sparse_insert_2(void); +void Sparse_get_ref(void); +void Sparse_update_ref(void); +void Sparse_get_recycled(void); +void Sparse_get_mut_recycled(void); +void Sparse_ensure_recycled(void); +void Sparse_emplace_recycled(void); +void Sparse_set_recycled(void); +void Sparse_get_ref_recycled(void); +void Sparse_test_stable_ptr(void); +void Sparse_has_after_remove(void); +void Sparse_has_after_clear(void); +void Sparse_get_after_remove(void); +void Sparse_get_mut_after_remove(void); +void Sparse_sparse_w_hole(void); +void Sparse_record_get(void); +void Sparse_has_inherited(void); +void Sparse_owns_inherited(void); +void Sparse_get_inherited(void); +void Sparse_get_mut_inherited(void); +void Sparse_ensure_inherited(void); +void Sparse_emplace_inherited(void); +void Sparse_override_component(void); +void Sparse_delete_w_override_component(void); +void Sparse_delete_w_override_on_remove_isa(void); +void Sparse_ctor_after_emplace(void); +void Sparse_ctor_dtor_after_remove(void); +void Sparse_ctor_dtor_after_clear(void); +void Sparse_ctor_dtor_after_delete(void); +void Sparse_ctor_dtor_after_fini(void); +void Sparse_on_add_remove_after_remove(void); +void Sparse_on_add_remove_after_clear(void); +void Sparse_on_add_remove_after_delete(void); +void Sparse_on_add_remove_after_fini(void); +void Sparse_on_set_after_set(void); +void Sparse_on_set_after_modified(void); +void Sparse_on_set_at_offset(void); +void Sparse_on_add_observer(void); +void Sparse_on_set_observer_set(void); +void Sparse_on_set_observer_modified(void); +void Sparse_on_set_observer_insert(void); +void Sparse_on_remove_observer_remove(void); +void Sparse_on_remove_observer_clear(void); +void Sparse_on_remove_observer_delete(void); +void Sparse_on_remove_observer_fini(void); +void Sparse_on_set_after_remove_override(void); +void Sparse_on_add_observer_2_terms(void); +void Sparse_on_set_observer_2_terms(void); +void Sparse_on_remove_observer_2_terms(void); +void Sparse_sparse_relationship(void); +void Sparse_defer_ensure(void); +void Sparse_defer_ensure_w_modified(void); +void Sparse_defer_ensure_modified(void); +void Sparse_defer_emplace(void); +void Sparse_defer_emplace_w_modified(void); +void Sparse_defer_set(void); +void Sparse_defer_ensure_existing(void); +void Sparse_defer_ensure_existing_twice(void); +void Sparse_defer_ensure_w_modified_existing(void); +void Sparse_defer_ensure_modified_existing(void); +void Sparse_defer_emplace_existing(void); +void Sparse_defer_emplace_w_modified_existing(void); +void Sparse_defer_set_existing(void); +void Sparse_defer_batched_ensure(void); +void Sparse_defer_batched_ensure_w_modified(void); +void Sparse_defer_batched_ensure_modified(void); +void Sparse_defer_batched_emplace(void); +void Sparse_defer_batched_emplace_w_modified(void); +void Sparse_defer_batched_set(void); +void Sparse_defer_batched_ensure_existing(void); +void Sparse_defer_batched_ensure_existing_twice(void); +void Sparse_defer_batched_ensure_w_modified_existing(void); +void Sparse_defer_batched_ensure_modified_existing(void); +void Sparse_defer_batched_emplace_existing(void); +void Sparse_defer_batched_emplace_w_modified_existing(void); +void Sparse_defer_batched_set_existing(void); +void Sparse_defer_batched_set_remove(void); +void Sparse_defer_batched_set_remove_existing(void); + +// Testsuite 'Union' +void Union_add(void); +void Union_add_twice(void); +void Union_add_replace(void); +void Union_add_remove(void); +void Union_add_remove_recycled(void); +void Union_add_remove_add(void); +void Union_get_target_none(void); +void Union_get_target(void); +void Union_get_recycled_target(void); +void Union_get_target_after_replace(void); +void Union_get_target_after_remove(void); +void Union_has_wildcard(void); +void Union_has_any(void); +void Union_add_remove_2_tgts(void); +void Union_add_remove_2_tgts_join(void); +void Union_add_remove_3_tgts(void); +void Union_add_remove_3_tgts_join(void); +void Union_remove_w_union_tgt(void); +void Union_get_non_union_tgt_from_table_w_union(void); +void Union_has_non_union_from_table_w_union(void); +void Union_get_case_no_switch(void); +void Union_get_case_set(void); +void Union_get_case_change(void); +void Union_remove_case(void); +void Union_remove_last(void); +void Union_delete_first(void); +void Union_delete_last(void); +void Union_delete_first_last(void); +void Union_3_entities_same_case(void); +void Union_2_entities_1_change_case(void); +void Union_3_entities_change_case(void); +void Union_add_case_in_stage(void); +void Union_change_case_in_stage(void); +void Union_change_one_case_in_stage(void); +void Union_remove_switch_in_stage(void); +void Union_switch_no_match_for_case(void); +void Union_empty_entity_has_case(void); +void Union_zero_entity_has_case(void); +void Union_add_to_entity_w_switch(void); +void Union_add_pair_to_entity_w_switch(void); +void Union_recycled_tags(void); +void Union_same_table_after_change(void); +void Union_add_2(void); +void Union_add_2_reverse(void); +void Union_add_switch_to_prefab_instance(void); +void Union_get_case_w_generation(void); +void Union_get_case_w_generation_not_alive(void); +void Union_defer_add_union_relationship(void); +void Union_defer_add_existing_union_relationship(void); +void Union_defer_add_union_relationship_2_ops(void); +void Union_defer_add_existing_union_relationship_2_ops(void); +void Union_stress_test_1(void); + // Testsuite 'Hierarchies' void Hierarchies_setup(void); void Hierarchies_empty_scope(void); @@ -436,8 +597,19 @@ void Hierarchies_path_custom_sep(void); void Hierarchies_path_custom_prefix(void); void Hierarchies_path_prefix_rel_match(void); void Hierarchies_path_prefix_rel_no_match(void); +void Hierarchies_path_escaped_sep(void); +void Hierarchies_path_escaped_two_sep(void); +void Hierarchies_path_escaped_two_consecutive_sep(void); +void Hierarchies_path_escaped_sep_at_begin(void); +void Hierarchies_path_escaped_sep_at_end(void); +void Hierarchies_path_escaped_sep_w_parent(void); +void Hierarchies_path_only_escaped_sep(void); +void Hierarchies_path_only_escaped_sep_w_parent(void); +void Hierarchies_path_only_escaped_two_sep(void); +void Hierarchies_path_only_escaped_two_sep_w_parent(void); void Hierarchies_fullpath_for_core(void); void Hierarchies_path_w_number(void); +void Hierarchies_path_w_entity_id(void); void Hierarchies_lookup_depth_0(void); void Hierarchies_lookup_depth_1(void); void Hierarchies_lookup_depth_2(void); @@ -451,11 +623,10 @@ void Hierarchies_lookup_self(void); void Hierarchies_lookup_in_parent_from_scope(void); void Hierarchies_lookup_in_root_from_scope(void); void Hierarchies_lookup_number(void); +void Hierarchies_lookup_entity_id(void); void Hierarchies_delete_children(void); void Hierarchies_scope_set(void); void Hierarchies_scope_set_again(void); -void Hierarchies_scope_set_w_new(void); -void Hierarchies_scope_set_w_new_staged(void); void Hierarchies_scope_set_w_lookup(void); void Hierarchies_scope_component(void); void Hierarchies_scope_component_no_macro(void); @@ -475,14 +646,11 @@ void Hierarchies_add_path_from_scope(void); void Hierarchies_add_path_from_scope_new_entity(void); void Hierarchies_add_root_path_to_child(void); void Hierarchies_add_parent_path_from_root_to_child(void); -void Hierarchies_new_w_child_in_root(void); void Hierarchies_delete_child(void); void Hierarchies_delete_2_children(void); void Hierarchies_delete_2_children_different_type(void); void Hierarchies_delete_tree_2_levels(void); void Hierarchies_delete_tree_3_levels(void); -void Hierarchies_delete_tree_count_tables(void); -void Hierarchies_delete_tree_staged(void); void Hierarchies_delete_tree_empty_table(void); void Hierarchies_delete_tree_recreate(void); void Hierarchies_delete_tree_w_onremove(void); @@ -494,8 +662,6 @@ void Hierarchies_scope_iter_after_delete_tree(void); void Hierarchies_add_child_after_delete_tree(void); void Hierarchies_add_child_to_recycled_parent(void); void Hierarchies_get_type_after_recycled_parent_add(void); -void Hierarchies_rematch_after_add_to_recycled_parent(void); -void Hierarchies_cascade_after_recycled_parent_change(void); void Hierarchies_long_name_depth_0(void); void Hierarchies_long_name_depth_1(void); void Hierarchies_long_name_depth_2(void); @@ -511,73 +677,6 @@ void Hierarchies_lookup_after_delete_from_parent(void); void Hierarchies_defer_batch_remove_name_w_add_childof(void); void Hierarchies_defer_batch_remove_childof_w_add_name(void); -// Testsuite 'FixedHierarchies' -void FixedHierarchies_make_fixed_1_lvl(void); -void FixedHierarchies_make_fixed_1_lvl_w_init(void); -void FixedHierarchies_make_fixed_1_lvl_w_init_comp(void); -void FixedHierarchies_make_fixed_1_lvl_w_init_comp_after_tree_fixed(void); -void FixedHierarchies_make_fixed_1_lvl_2_entities(void); -void FixedHierarchies_make_fixed_1_lvl_2_tables(void); -void FixedHierarchies_make_fixed_2_lvl(void); -void FixedHierarchies_make_fixed_2_lvl_2_tables(void); -void FixedHierarchies_make_fixed_3_lvl(void); -void FixedHierarchies_make_fixed_3_lvl_w_name(void); -void FixedHierarchies_make_fixed_3_2_lvl_w_name(void); -void FixedHierarchies_make_fixed_2_lvl_nested(void); -void FixedHierarchies_make_fixed_3_lvl_nested(void); -void FixedHierarchies_make_fixed_1_lvl_after_delete(void); -void FixedHierarchies_get_target_1_lvl(void); -void FixedHierarchies_get_target_2_lvl(void); -void FixedHierarchies_get_depth_1_lvl(void); -void FixedHierarchies_get_depth_2_lvl(void); -void FixedHierarchies_get_depth_after_reparent_root(void); -void FixedHierarchies_delete_fixed_1_lvl(void); -void FixedHierarchies_delete_fixed_2_lvl(void); -void FixedHierarchies_delete_with_fixed_1_lvl(void); -void FixedHierarchies_delete_with_fixed_2_lvl(void); -void FixedHierarchies_query_w_parent_field_1_lvl(void); -void FixedHierarchies_query_w_parent_field_1_lvl_w_init(void); -void FixedHierarchies_query_w_parent_field_1_lvl_w_init_comp_after_tree_fixed(void); -void FixedHierarchies_query_w_parent_field_2_lvl(void); -void FixedHierarchies_query_w_parent_field_1_fixed_1_regular(void); -void FixedHierarchies_query_w_parent_field_only_fixed_1_lvls(void); -void FixedHierarchies_query_w_parent_field_fixed_1_lvls_no_match(void); -void FixedHierarchies_query_w_parent_field_fixed_1_lvls_2_no_match(void); -void FixedHierarchies_query_w_parent_field_fixed_1_lvls_match_no_match(void); -void FixedHierarchies_query_w_parent_field_fixed_1_lvls_no_match_match(void); -void FixedHierarchies_query_w_parent_field_2_fixed_2_lvls(void); -void FixedHierarchies_query_w_cascade_field_2_lvl(void); -void FixedHierarchies_query_next_table(void); -void FixedHierarchies_query_next_table_1_elem(void); -void FixedHierarchies_query_next_table_1_elem_no_match(void); -void FixedHierarchies_query_nested_make_fixed(void); -void FixedHierarchies_query_nested_make_fixed_w_optional(void); -void FixedHierarchies_query_nested_make_fixed_w_optional_match_children_only(void); -void FixedHierarchies_query_nested_w_2_parents_make_fixed(void); -void FixedHierarchies_query_table_w_3_parents(void); -void FixedHierarchies_query_w_parent_change_detection_1st(void); -void FixedHierarchies_query_w_parent_change_detection_2nd(void); -void FixedHierarchies_query_w_parent_change_detection_iter_twice(void); -void FixedHierarchies_query_w_parent_change_detection_iter_twice_each_parent(void); -void FixedHierarchies_query_w_parent_change_detection_1st_populate_when_changed(void); -void FixedHierarchies_query_w_parent_change_detection_2nd_populate_when_changed(void); -void FixedHierarchies_query_w_parent_change_detection_iter_twice_populate_when_changed(void); -void FixedHierarchies_query_w_parent_change_detection_iter_twice_each_parent_populate_when_changed(void); -void FixedHierarchies_staged_query_w_parent_field_1_lvl(void); -void FixedHierarchies_staged_query_w_parent_field_2_lvl(void); -void FixedHierarchies_staged_query_w_parent_field_1_fixed_1_regular(void); -void FixedHierarchies_staged_query_w_cascade_field_2_lvl(void); -void FixedHierarchies_add_to_fixed(void); -void FixedHierarchies_remove_from_fixed(void); -void FixedHierarchies_delete_fixed(void); -void FixedHierarchies_clear_fixed(void); -void FixedHierarchies_make_fixed_1_lvl_w_name(void); -void FixedHierarchies_make_fixed_2_lvl_w_name(void); -void FixedHierarchies_make_fixed_1_lvl_w_name_keep_name(void); -void FixedHierarchies_make_fixed_2_lvl_w_name_keep_name(void); -void FixedHierarchies_make_fixed_2_lvl_lose_depth(void); -void FixedHierarchies_make_fixed_3_lvl_lose_depth(void); - // Testsuite 'Has' void Has_zero(void); void Has_1_of_0(void); @@ -617,7 +716,7 @@ void Get_component_get_1_from_2_add_in_progress(void); void Get_component_get_both_from_2_add_in_progress(void); void Get_component_get_both_from_2_add_remove_in_progress(void); void Get_component_get_childof_component(void); -void Get_component_get_mut_equal_get(void); +void Get_component_ensure_equal_get(void); void Get_component_get_tag(void); void Get_component_get_pair_tag(void); void Get_component_get_wildcard(void); @@ -636,6 +735,7 @@ void Reference_get_ref_monitored(void); void Reference_get_ref_w_low_id_tag(void); void Reference_get_ref_w_low_id_tag_after_add(void); void Reference_get_nonexisting(void); +void Reference_aba_table(void); // Testsuite 'Delete' void Delete_setup(void); @@ -758,9 +858,7 @@ void OnDelete_remove_childof_wildcard(void); void OnDelete_delete_child_of_delete_with(void); void OnDelete_deep_clean_64(void); void OnDelete_deep_clean_256(void); -void OnDelete_id_w_switch(void); void OnDelete_id_w_disabled(void); -void OnDelete_id_to_no_switch(void); void OnDelete_id_to_no_disabled(void); void OnDelete_remove_on_delete_action(void); void OnDelete_delete_with_w_relation(void); @@ -788,6 +886,18 @@ void OnDelete_delete_w_low_rel_mixed_cleanup(void); void OnDelete_delete_w_low_rel_mixed_cleanup_interleaved_ids(void); void OnDelete_fini_query_w_singleton_in_scope_no_module(void); void OnDelete_fini_query_w_singleton_in_module(void); +void OnDelete_fini_observer_w_relationship_in_scope(void); +void OnDelete_add_on_delete_from_prefab(void); +void OnDelete_add_on_delete_from_disabled(void); +void OnDelete_delete_on_delete_from_prefab(void); +void OnDelete_delete_on_delete_from_disabled(void); +void OnDelete_delete_all_w_component_cycle(void); +void OnDelete_remove_all_1(void); +void OnDelete_remove_all_2(void); +void OnDelete_remove_all_3(void); +void OnDelete_delete_with_1(void); +void OnDelete_delete_with_2(void); +void OnDelete_delete_with_3(void); // Testsuite 'Set' void Set_set_empty(void); @@ -803,24 +913,30 @@ void Set_set_remove_other(void); void Set_set_remove_twice(void); void Set_set_and_new(void); void Set_set_null(void); -void Set_get_mut_new(void); +void Set_ensure_new(void); +void Set_ensure_existing(void); +void Set_ensure_tag_new(void); +void Set_ensure_tag_existing(void); +void Set_ensure_tag_new_w_comp(void); +void Set_ensure_tag_existing_w_comp(void); +void Set_ensure_tag_new_w_pair(void); +void Set_ensure_tag_existing_w_pair(void); +void Set_get_mut_not_existing(void); void Set_get_mut_existing(void); -void Set_get_mut_tag_new(void); -void Set_get_mut_tag_existing(void); -void Set_get_mut_tag_new_w_comp(void); -void Set_get_mut_tag_existing_w_comp(void); -void Set_get_mut_tag_new_w_pair(void); -void Set_get_mut_tag_existing_w_pair(void); +void Set_get_mut_tag(void); +void Set_get_mut_pair(void); +void Set_get_mut_pair_second(void); void Set_modified_w_on_set(void); void Set_modified_no_component(void); -void Set_get_mut_w_add_in_on_add(void); -void Set_get_mut_w_remove_in_on_add(void); -void Set_get_mut_w_realloc_in_on_add(void); +void Set_ensure_w_add_in_on_add(void); +void Set_ensure_w_remove_in_on_add(void); +void Set_ensure_w_realloc_in_on_add(void); void Set_emplace(void); void Set_emplace_2(void); void Set_emplace_existing(void); void Set_emplace_w_move(void); void Set_emplace_w_observer_w_add(void); +void Set_emplace_existing_w_check(void); // Testsuite 'ReadWrite' void ReadWrite_read(void); @@ -844,11 +960,19 @@ void Lookup_get_name(void); void Lookup_get_name_no_name(void); void Lookup_get_name_from_empty(void); void Lookup_lookup_by_id(void); +void Lookup_lookup_path_anonymous_parent(void); +void Lookup_lookup_path_0_parent(void); +void Lookup_lookup_path_0_parent_w_scope(void); void Lookup_lookup_recycled_by_id(void); void Lookup_lookup_symbol_by_id(void); void Lookup_lookup_name_w_digit(void); void Lookup_lookup_symbol_w_digit(void); void Lookup_lookup_path_w_digit(void); +void Lookup_lookup_name_w_spaces(void); +void Lookup_lookup_path_w_spaces(void); +void Lookup_lookup_number(void); +void Lookup_lookup_number_path(void); +void Lookup_lookup_number_0(void); void Lookup_set_name_of_existing(void); void Lookup_change_name_of_existing(void); void Lookup_lookup_alias(void); @@ -880,12 +1004,21 @@ void Lookup_lookup_child_invalid_digit(void); void Lookup_lookup_digit_from_wrong_scope(void); void Lookup_lookup_core_entity_from_wrong_scope(void); void Lookup_lookup_alias_w_number(void); +void Lookup_lookup_symbol_path(void); +void Lookup_lookup_name_escaped_sep(void); +void Lookup_lookup_path_escaped_sep(void); +void Lookup_lookup_name_63_chars(void); +void Lookup_lookup_name_64_chars(void); +void Lookup_lookup_name_65_chars(void); +void Lookup_lookup_path_63_chars(void); +void Lookup_lookup_path_64_chars(void); +void Lookup_lookup_path_65_chars(void); // Testsuite 'Singleton' void Singleton_add_singleton(void); void Singleton_remove_singleton(void); void Singleton_set_get_singleton(void); -void Singleton_get_mut_singleton(void); +void Singleton_ensure_singleton(void); void Singleton_singleton_system(void); // Testsuite 'Clone' @@ -896,6 +1029,7 @@ void Clone_null_w_value(void); void Clone_1_component(void); void Clone_2_component(void); void Clone_1_component_w_value(void); +void Clone_1_component_w_lifecycle(void); void Clone_2_component_w_value(void); void Clone_3_component(void); void Clone_3_component_w_value(void); @@ -915,9 +1049,6 @@ void ComponentLifecycle_copy_on_set(void); void ComponentLifecycle_copy_on_override(void); void ComponentLifecycle_copy_on_clone(void); void ComponentLifecycle_no_copy_on_move(void); -void ComponentLifecycle_ctor_copy_on_snapshot(void); -void ComponentLifecycle_copy_on_snapshot(void); -void ComponentLifecycle_dtor_on_restore(void); void ComponentLifecycle_ctor_on_tag(void); void ComponentLifecycle_dtor_on_tag(void); void ComponentLifecycle_copy_on_tag(void); @@ -943,6 +1074,8 @@ void ComponentLifecycle_valid_entity_in_dtor_after_delete(void); void ComponentLifecycle_ctor_w_emplace(void); void ComponentLifecycle_ctor_w_emplace_defer(void); void ComponentLifecycle_ctor_w_emplace_defer_use_move_ctor(void); +void ComponentLifecycle_ctor_w_emplace_defer_twice(void); +void ComponentLifecycle_ctor_w_emplace_defer_existing(void); void ComponentLifecycle_on_add_w_emplace(void); void ComponentLifecycle_on_add_w_emplace_existing(void); void ComponentLifecycle_on_add_w_emplace_defer(void); @@ -972,14 +1105,20 @@ void ComponentLifecycle_dtor_component_new_obj_pair_id_while_fini(void); void ComponentLifecycle_ctor_move_dtor_after_resize(void); void ComponentLifecycle_ctx_free(void); void ComponentLifecycle_binding_ctx_free(void); +void ComponentLifecycle_lifecycle_ctx_free(void); void ComponentLifecycle_ctx_free_after_delete_component(void); void ComponentLifecycle_binding_ctx_free_after_delete_component(void); +void ComponentLifecycle_lifecycle_ctx_free_after_delete_component(void); void ComponentLifecycle_on_add_ctx(void); void ComponentLifecycle_on_remove_ctx(void); void ComponentLifecycle_on_set_ctx(void); void ComponentLifecycle_on_add_w_existing_component(void); void ComponentLifecycle_on_remove_w_existing_component(void); void ComponentLifecycle_component_init_set_hooks(void); +void ComponentLifecycle_component_init_name_from_type_info(void); +void ComponentLifecycle_component_init_scoped_name_from_type_info(void); +void ComponentLifecycle_component_init_w_recycled_id(void); +void ComponentLifecycle_component_init_w_recycled_non_component_id(void); void ComponentLifecycle_on_add_after_ctor_w_add(void); void ComponentLifecycle_on_add_after_ctor_w_add_to(void); void ComponentLifecycle_with_before_hooks(void); @@ -993,700 +1132,13 @@ void ComponentLifecycle_on_set_hook_check_offset(void); void ComponentLifecycle_on_set_hook_on_override(void); void ComponentLifecycle_on_set_hook_on_auto_override(void); void ComponentLifecycle_batched_set_new_component_w_lifecycle(void); -void ComponentLifecycle_batched_get_mut_new_component_w_lifecycle(void); - -// Testsuite 'Sorting' -void Sorting_sort_by_component(void); -void Sorting_sort_by_component_2_tables(void); -void Sorting_sort_by_component_3_tables(void); -void Sorting_sort_by_entity(void); -void Sorting_sort_after_add(void); -void Sorting_sort_after_remove(void); -void Sorting_sort_after_delete(void); -void Sorting_sort_after_set(void); -void Sorting_sort_after_system(void); -void Sorting_sort_after_query(void); -void Sorting_sort_by_component_same_value_1(void); -void Sorting_sort_by_component_same_value_2(void); -void Sorting_sort_by_component_move_pivot(void); -void Sorting_sort_1000_entities(void); -void Sorting_sort_1000_entities_w_duplicates(void); -void Sorting_sort_1000_entities_again(void); -void Sorting_sort_1000_entities_2_types(void); -void Sorting_sort_1500_entities_3_types(void); -void Sorting_sort_2000_entities_4_types(void); -void Sorting_sort_2_entities_2_types(void); -void Sorting_sort_3_entities_3_types(void); -void Sorting_sort_3_entities_3_types_2(void); -void Sorting_sort_4_entities_4_types(void); -void Sorting_sort_1000_entities_2_types_again(void); -void Sorting_sort_1000_entities_add_type_after_sort(void); -void Sorting_sort_shared_component(void); -void Sorting_sort_shared_component_childof(void); -void Sorting_sort_w_tags_only(void); -void Sorting_sort_childof_marked(void); -void Sorting_sort_isa_marked(void); -void Sorting_sort_relation_marked(void); -void Sorting_dont_resort_after_set_unsorted_component(void); -void Sorting_dont_resort_after_set_unsorted_component_w_tag(void); -void Sorting_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void); -void Sorting_sort_component_not_queried_for(void); -void Sorting_sort_by_wildcard(void); - -// Testsuite 'SortingEntireTable' -void SortingEntireTable_sort_by_component(void); -void SortingEntireTable_sort_by_component_2_tables(void); -void SortingEntireTable_sort_by_component_3_tables(void); -void SortingEntireTable_sort_by_entity(void); -void SortingEntireTable_sort_after_add(void); -void SortingEntireTable_sort_after_remove(void); -void SortingEntireTable_sort_after_delete(void); -void SortingEntireTable_sort_after_set(void); -void SortingEntireTable_sort_after_system(void); -void SortingEntireTable_sort_after_query(void); -void SortingEntireTable_sort_by_component_same_value_1(void); -void SortingEntireTable_sort_by_component_same_value_2(void); -void SortingEntireTable_sort_by_component_move_pivot(void); -void SortingEntireTable_sort_1000_entities(void); -void SortingEntireTable_sort_1000_entities_w_duplicates(void); -void SortingEntireTable_sort_1000_entities_again(void); -void SortingEntireTable_sort_1000_entities_2_types(void); -void SortingEntireTable_sort_1500_entities_3_types(void); -void SortingEntireTable_sort_2000_entities_4_types(void); -void SortingEntireTable_sort_2_entities_2_types(void); -void SortingEntireTable_sort_3_entities_3_types(void); -void SortingEntireTable_sort_3_entities_3_types_2(void); -void SortingEntireTable_sort_4_entities_4_types(void); -void SortingEntireTable_sort_1000_entities_2_types_again(void); -void SortingEntireTable_sort_1000_entities_add_type_after_sort(void); -void SortingEntireTable_sort_shared_component(void); -void SortingEntireTable_sort_w_tags_only(void); -void SortingEntireTable_sort_childof_marked(void); -void SortingEntireTable_sort_isa_marked(void); -void SortingEntireTable_sort_relation_marked(void); -void SortingEntireTable_dont_resort_after_set_unsorted_component(void); -void SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag(void); -void SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void); - -// Testsuite 'Filter' -void Filter_filter_1_term(void); -void Filter_filter_1_term_component(void); -void Filter_filter_2_terms(void); -void Filter_filter_3_terms(void); -void Filter_filter_3_terms_w_or(void); -void Filter_filter_4_terms_w_or_at_1(void); -void Filter_filter_1_term_wildcard(void); -void Filter_filter_1_term_any(void); -void Filter_filter_1_term_same_subj_obj(void); -void Filter_filter_1_term_acyclic_same_subj_obj(void); -void Filter_filter_1_term_acyclic_reflexive_same_subj_obj(void); -void Filter_filter_1_term_same_subj_obj_var(void); -void Filter_filter_1_term_acyclic_same_subj_obj_var(void); -void Filter_filter_1_term_acyclic_reflexive_same_subj_obj_var(void); -void Filter_filter_1_term_non_acyclic_superset(void); -void Filter_filter_1_term_dont_inherit_default_set(void); -void Filter_filter_1_term_dont_inherit_pair_default_set(void); -void Filter_filter_1_term_cascade_implicit_isa(void); -void Filter_filter_1_term_cascade_isa(void); -void Filter_filter_1_term_cascade_childof(void); -void Filter_filter_1_term_cascade_down(void); -void Filter_filter_1_term_optional_only(void); -void Filter_filter_1_term_transitive_pair(void); -void Filter_filter_1_term_transitive_pair_explicit_self_tgt(void); -void Filter_filter_1_variable_as_pred_only(void); -void Filter_filter_1_variable_as_pred_w_subj(void); -void Filter_filter_1_variable_as_pred_w_pair(void); -void Filter_filter_1_variable_as_subj(void); -void Filter_filter_1_variable_as_obj(void); -void Filter_filter_2_terms_or_w_dontinherit(void); -void Filter_filter_2_terms_or_w_both_dontinherit(void); -void Filter_filter_w_pair_id(void); -void Filter_filter_w_pred_obj(void); -void Filter_filter_w_pair_id_and_subj(void); -void Filter_filter_1_w_pred_name(void); -void Filter_filter_1_w_final_pred_name(void); -void Filter_filter_1_w_subj_name(void); -void Filter_filter_1_w_obj_name(void); -void Filter_filter_w_this_implicit_variable(void); -void Filter_filter_w_this_explicit_entity(void); -void Filter_filter_w_first_this_implicit_variable(void); -void Filter_filter_w_first_this_explicit_entity(void); -void Filter_filter_w_second_this_implicit_variable(void); -void Filter_filter_w_second_this_explicit_entity(void); -void Filter_filter_w_this_variable_name(void); -void Filter_filter_w_src_var(void); -void Filter_filter_w_first_var(void); -void Filter_filter_w_second_var(void); -void Filter_filter_w_0_source(void); -void Filter_filter_w_0_target(void); -void Filter_filter_2_terms_w_or(void); -void Filter_filter_2_terms_w_or_mixed_src_flags(void); -void Filter_filter_2_terms_w_or_mixed_src_id(void); -void Filter_filter_2_terms_w_or_mixed_src_name(void); -void Filter_filter_2_terms_w_or_same_src_w_id_and_name(void); -void Filter_filter_move(void); -void Filter_filter_copy(void); -void Filter_filter_w_resources_copy(void); -void Filter_filter_w_and_flag(void); -void Filter_filter_w_or_flag(void); -void Filter_filter_w_not_flag(void); -void Filter_filter_filter(void); -void Filter_filter_double_init(void); -void Filter_filter_double_init_w_expr(void); -void Filter_filter_double_init_w_expr_optional(void); -void Filter_filter_w_tag_term_is_no_data(void); -void Filter_filter_w_inout_none_term_is_no_data(void); -void Filter_filter_w_tag_and_inout_none_term_is_no_data(void); -void Filter_filter_w_not_term_is_no_data(void); -void Filter_filter_w_no_transitive_pair(void); -void Filter_filter_w_transitive_pair_any_src(void); -void Filter_filter_w_transitive_pair(void); -void Filter_filter_w_transitive_tag_no_pair(void); -void Filter_filter_w_transitive_tag_self_tgt(void); -void Filter_filter_w_transitive_tag_any_tgt(void); -void Filter_filter_w_pair_same_vars(void); -void Filter_filter_w_pair_not_same_vars(void); -void Filter_filter_w_pair_no_vars_not_same_vars(void); -void Filter_filter_w_pair_wildcard_not_same_vars(void); -void Filter_filter_w_pair_any_not_same_vars(void); -void Filter_filter_w_no_pair_not_same_vars(void); -void Filter_filter_not_childof_any(void); -void Filter_filter_w_inherited_id(void); -void Filter_filter_w_inherited_pair(void); -void Filter_filter_w_non_inherited_id(void); -void Filter_filter_w_non_inherited_pair(void); -void Filter_filter_w_first_rel(void); -void Filter_filter_w_first_rel_self(void); -void Filter_filter_w_first_rel_down(void); -void Filter_filter_w_first_rel_self_down(void); -void Filter_filter_w_first_rel_reflexive(void); -void Filter_filter_w_first_rel_reflexive_self(void); -void Filter_filter_w_first_rel_reflexive_down(void); -void Filter_filter_w_first_rel_reflexive_self_down(void); -void Filter_filter_w_first_rel_non_traversable(void); -void Filter_filter_w_first_wildcard_inout_none(void); -void Filter_filter_w_first_var_inout_none(void); -void Filter_filter_w_pair_wildcard_inout_none(void); -void Filter_filter_w_pair_var_inout_none(void); -void Filter_filter_w_unresolved_by_name(void); -void Filter_filter_w_unresolved_by_name_eq(void); -void Filter_filter_childof_this(void); -void Filter_filter_childof_this_entity(void); -void Filter_filter_childof_this_by_id(void); -void Filter_term_w_id(void); -void Filter_term_w_pair_id(void); -void Filter_term_w_pred_obj(void); -void Filter_term_w_pair_finalize_twice(void); -void Filter_term_w_role(void); -void Filter_term_w_pred_role(void); -void Filter_term_w_self(void); -void Filter_term_w_superset(void); -void Filter_term_w_subset(void); -void Filter_term_w_self_superset(void); -void Filter_term_w_superset_custom_relation(void); -void Filter_term_w_self_superset_custom_relation(void); -void Filter_term_iter_component(void); -void Filter_term_iter_w_pred(void); -void Filter_term_iter_tag(void); -void Filter_term_iter_pair(void); -void Filter_term_iter_pair_w_rel_wildcard(void); -void Filter_term_iter_pair_w_obj_wildcard(void); -void Filter_term_iter_pair_w_rel_wildcard_n_matches(void); -void Filter_term_iter_pair_w_rel_wildcard_n_matches_w_data(void); -void Filter_term_iter_pair_w_obj_wildcard_n_matches(void); -void Filter_term_iter_pair_w_obj_wildcard_n_matches_w_data(void); -void Filter_term_iter_w_superset(void); -void Filter_term_iter_w_superset_base_w_2_components(void); -void Filter_term_iter_w_superset_childof(void); -void Filter_term_iter_w_superset_self(void); -void Filter_term_iter_w_superset_self_childof(void); -void Filter_term_iter_w_superset_tag(void); -void Filter_term_iter_w_superset_pair(void); -void Filter_term_iter_w_superset_pair_obj_wildcard(void); -void Filter_term_iter_in_stage(void); -void Filter_term_iter_w_readonly_term(void); -void Filter_term_iter_type_set(void); -void Filter_term_iter_any_match_wildcard(void); -void Filter_term_iter_any_match_tag_and_wildcard(void); -void Filter_term_iter_any_obj(void); -void Filter_children_iter(void); -void Filter_filter_iter_1_tag(void); -void Filter_filter_iter_2_tags(void); -void Filter_filter_iter_2_tags_1_not(void); -void Filter_filter_iter_3_tags_2_or(void); -void Filter_filter_iter_only_optional(void); -void Filter_filter_iter_only_2_or(void); -void Filter_filter_iter_only_3_or(void); -void Filter_filter_iter_2_or(void); -void Filter_filter_iter_3_or(void); -void Filter_filter_iter_2_or_other_type(void); -void Filter_filter_iter_2_or_same_type(void); -void Filter_filter_iter_1_component(void); -void Filter_filter_iter_2_components(void); -void Filter_filter_iter_pair_id(void); -void Filter_filter_iter_2_pair_ids(void); -void Filter_filter_iter_childof_pair_0_parent(void); -void Filter_filter_iter_pair_pred_obj(void); -void Filter_filter_iter_pair_2_pred_obj(void); -void Filter_filter_iter_null(void); -void Filter_filter_iter_1_not_tag(void); -void Filter_filter_iter_2_tags_1_optional(void); -void Filter_filter_iter_2_components_1_optional(void); -void Filter_filter_iter_in_stage(void); -void Filter_filter_iter_10_tags(void); -void Filter_filter_iter_20_tags(void); -void Filter_filter_iter_10_components(void); -void Filter_filter_iter_20_components(void); -void Filter_filter_iter_superset(void); -void Filter_filter_iter_superset_childof(void); -void Filter_filter_iter_type_set(void); -void Filter_filter_iter_w_readonly_term(void); -void Filter_filter_iter_w_from_nothing_term(void); -void Filter_filter_iter_pair_w_rel_wildcard_n_matches(void); -void Filter_filter_iter_pair_w_obj_wildcard_n_matches(void); -void Filter_filter_iter_pair_w_2_wildcards_1_match(void); -void Filter_filter_iter_pair_w_2_wildcards_2x1_matches(void); -void Filter_filter_iter_pair_w_2_wildcards_2x2_matches(void); -void Filter_filter_iter_pair_w_3_wildcards_2x2x2_matches(void); -void Filter_filter_iter_pair_w_wildcard_and_nothing(void); -void Filter_filter_iter_any(void); -void Filter_filter_iter_any_match_wildcard(void); -void Filter_filter_iter_any_match_tag_and_wildcard(void); -void Filter_filter_iter_wildcard_in_2nd_term(void); -void Filter_filter_iter_wildcard_in_2nd_term_self(void); -void Filter_filter_iter_2nd_term_self_create_id_after_filter(void); -void Filter_filter_iter_any_obj(void); -void Filter_filter_iter_not_any(void); -void Filter_filter_iter_not_any_obj(void); -void Filter_filter_iter_cascade_isa(void); -void Filter_filter_iter_cascade_childof(void); -void Filter_filter_iter_superset_2_rel_instances(void); -void Filter_filter_iter_superset_2_rel_instances_match_2nd(void); -void Filter_filter_iter_superset_2_levels(void); -void Filter_filter_iter_superset_only_w_owned(void); -void Filter_filter_iter_superset_after_add(void); -void Filter_filter_iter_superset_after_remove(void); -void Filter_filter_iter_superset_after_clear(void); -void Filter_filter_iter_superset_after_delete(void); -void Filter_filter_iter_2_terms_superset_2_rel_instances(void); -void Filter_filter_iter_2_terms_superset_2_rel_instances_match_2nd(void); -void Filter_filter_iter_superset_parent_w_isa(void); -void Filter_filter_iter_superset_isa_after_remove_parent(void); -void Filter_filter_iter_superset_isa_create_table_after_iter(void); -void Filter_filter_iter_superset_2_relations(void); -void Filter_filter_iter_superset_2_relations_instanced(void); -void Filter_filter_iter_superset_2_relations_w_component(void); -void Filter_filter_iter_superset_2_relations_instanced_w_component(void); -void Filter_filter_iter_not_up_disabled(void); -void Filter_filter_iter_pair_wildcard_component(void); -void Filter_filter_w_10_terms(void); -void Filter_filter_w_10_terms_move(void); -void Filter_filter_w_10_terms_copy(void); -void Filter_match_disabled(void); -void Filter_match_prefab(void); -void Filter_chain_term_iter(void); -void Filter_chain_filter_iter(void); -void Filter_chain_query_iter(void); -void Filter_chain_rule_iter(void); -void Filter_chain_iter_2_levels(void); -void Filter_filter_from_expr_2_terms_err(void); -void Filter_chain_term_iter_w_term_iter(void); -void Filter_chain_filter_iter_w_term_iter(void); -void Filter_chain_w_term_iter_component(void); -void Filter_chain_iter_w_or(void); -void Filter_filter_w_recycled_first(void); -void Filter_filter_w_recycled_second(void); -void Filter_filter_w_recycled_first_and_id(void); -void Filter_filter_w_recycled_second_and_id(void); -void Filter_filter_w_recycled_first_by_name_and_id(void); -void Filter_filter_w_recycled_second_by_name_and_id(void); -void Filter_filter_w_recycled_first_by_expr(void); -void Filter_filter_w_recycled_second_by_expr(void); -void Filter_filter_w_recycled_first_only_by_expr(void); -void Filter_term_iter_w_filter_term(void); -void Filter_filter_iter_w_filter_term(void); -void Filter_filter_iter_w_2_terms_1_filter(void); -void Filter_filter_iter_w_3_terms_2_filter(void); -void Filter_filter_iter_2_terms_filter_all(void); -void Filter_filter_iter_2_terms_filter_all_w_out(void); -void Filter_filter_iter_switch_term_filter(void); -void Filter_filter_iter_2_terms_switch_term_filter(void); -void Filter_filter_iter_switch_superset(void); -void Filter_filter_instanced_w_singleton(void); -void Filter_filter_instanced_w_base(void); -void Filter_filter_no_instancing_w_singleton(void); -void Filter_filter_no_instancing_w_base(void); -void Filter_filter_no_this_tag(void); -void Filter_filter_no_this_component(void); -void Filter_filter_no_this_tag_2_ents(void); -void Filter_filter_no_this_component_2_ents(void); -void Filter_filter_no_this_tag_2_ents_1_not(void); -void Filter_filter_no_this_component_2_ents_1_not(void); -void Filter_filter_no_this_component_1_not(void); -void Filter_filter_iter_entities_optional_flag(void); -void Filter_filter_iter_frame_offset(void); -void Filter_filter_1_term_no_alloc(void); -void Filter_filter_cache_size_terms_no_alloc(void); -void Filter_filter_lt_cache_size_terms_no_alloc(void); -void Filter_move_self(void); -void Filter_match_empty_tables(void); -void Filter_match_empty_tables_w_no_empty_tables(void); -void Filter_match_switch_w_switch(void); -void Filter_match_switch_w_case(void); -void Filter_match_switch_w_case_2_terms(void); -void Filter_match_case_no_case(void); -void Filter_and_term(void); -void Filter_or_term(void); -void Filter_iter_while_creating_components(void); -void Filter_iter_w_this_var_as_entity(void); -void Filter_iter_w_this_var_as_table(void); -void Filter_iter_w_this_var_as_table_range(void); -void Filter_filter_wo_this_var(void); -void Filter_set_this_to_table_1_term(void); -void Filter_set_this_to_table_2_terms(void); -void Filter_set_this_to_table_1_wildcard(void); -void Filter_set_this_to_table_no_match_no_data(void); -void Filter_set_this_to_table_no_match(void); -void Filter_set_this_to_table_2_terms_no_match(void); -void Filter_set_this_to_empty_table(void); -void Filter_set_this_to_empty_table_w_component(void); -void Filter_set_this_to_implicit_isa_superset_match(void); -void Filter_set_this_to_self_isa_superset_match(void); -void Filter_set_this_to_isa_superset_match(void); -void Filter_set_this_to_childof_superset_match(void); -void Filter_set_this_to_superset_w_self_filter_no_match(void); -void Filter_set_this_to_isa_cascade(void); -void Filter_set_this_to_childof_cascade(void); -void Filter_set_this_w_wildcard_2_matches(void); -void Filter_set_this_to_entity_superset_self_has_component(void); -void Filter_set_this_to_1_entity_in_table(void); -void Filter_oneof(void); -void Filter_oneof_expr(void); -void Filter_oneof_w_mismatching_obj(void); -void Filter_oneof_w_mismatching_obj_expr(void); -void Filter_oneof_wildcard(void); -void Filter_oneof_any(void); -void Filter_flag_match_only_this(void); -void Filter_flag_match_only_this_w_ref(void); -void Filter_filter_w_alloc(void); -void Filter_filter_w_short_notation(void); - -// Testsuite 'FilterStr' -void FilterStr_one_term(void); -void FilterStr_one_term_w_inout(void); -void FilterStr_two_terms(void); -void FilterStr_two_terms_w_inout(void); -void FilterStr_three_terms_w_or(void); -void FilterStr_three_terms_w_or_inout(void); -void FilterStr_four_terms_three_w_or_inout(void); -void FilterStr_one_term_w_pair(void); -void FilterStr_one_term_w_pair_entity_src(void); -void FilterStr_one_term_w_self(void); -void FilterStr_one_term_w_up(void); -void FilterStr_one_term_w_0(void); -void FilterStr_one_term_w_singleton(void); -void FilterStr_one_term_w_final_pair(void); -void FilterStr_one_term_w_final_dont_inherit_pair(void); -void FilterStr_one_term_w_src_var(void); -void FilterStr_one_term_w_first_var(void); -void FilterStr_one_term_w_second_var(void); -void FilterStr_one_term_w_first_var_entity_src(void); -void FilterStr_one_term_w_pair_w_0_entity(void); -void FilterStr_not_term(void); -void FilterStr_wildcard_term(void); -void FilterStr_scopes(void); - -// Testsuite 'Query' -void Query_simple_query_existing_table(void); -void Query_simple_query_2_existing_tables(void); -void Query_simple_query_new_table(void); -void Query_simple_query_2_new_tables(void); -void Query_simple_query_existing_and_new_table(void); -void Query_wildcard_query_existing_table(void); -void Query_wildcard_query_new_table(void); -void Query_wildcard_query_existing_table_2_results_p_table(void); -void Query_wildcard_query_new_table_2_results_p_table(void); -void Query_wildcard_query_2nd_term(void); -void Query_wildcard_query_2nd_term_self(void); -void Query_simple_query_existing_empty_table(void); -void Query_simple_query_existing_empty_type(void); -void Query_simple_query_new_empty_table(void); -void Query_component_query_existing_table(void); -void Query_component_query_new_table(void); -void Query_component_query_existing_empty_table(void); -void Query_2_component_query_existing_empty_table(void); -void Query_2_component_query_existing_empty_type(void); -void Query_only_optional(void); -void Query_only_optional_new_empty_table(void); -void Query_only_optional_new_empty_non_empty_table(void); -void Query_only_optional_new_unset_tables(void); -void Query_singleton_w_optional_new_empty_table(void); -void Query_singleton_w_optional_new_empty_non_empty_table(void); -void Query_singleton_w_optional_new_unset_tables(void); -void Query_query_only_from_entity(void); -void Query_query_only_from_entity_no_match(void); -void Query_query_only_from_entity_no_match_iter_alloc(void); -void Query_query_only_from_singleton(void); -void Query_query_only_from_entity_match_after(void); -void Query_query_only_from_singleton_match_after(void); -void Query_query_only_from_singleton_component_match_after(void); -void Query_query_only_from_nothing(void); -void Query_query_only_from_entity_optional(void); -void Query_query_only_from_entity_no_match_optional(void); -void Query_query_only_from_entity_or(void); -void Query_query_only_from_entity_no_match_or(void); -void Query_query_only_from_entity_or_change(void); -void Query_query_from_entity_or_change(void); -void Query_query_from_entity_w_superset(void); -void Query_query_w_singleton_tag_non_instanced(void); -void Query_query_w_singleton_tag_instanced(void); -void Query_query_w_singleton_component_non_instanced(void); -void Query_query_w_singleton_component_instanced(void); -void Query_query_w_from_entity(void); -void Query_query_w_from_singleton(void); -void Query_query_w_from_entity_match_after(void); -void Query_query_w_from_singleton_match_after(void); -void Query_query_w_from_nothing(void); -void Query_query_w_existing_switch_and_case(void); -void Query_query_w_new_switch_and_case(void); -void Query_query_for_case_existing(void); -void Query_query_for_case_new(void); -void Query_query_for_switch_filter_term(void); -void Query_query_switch_from_nothing(void); -void Query_query_case_from_nothing(void); -void Query_query_case_inherited(void); -void Query_query_case_w_generation(void); -void Query_query_case_w_not_alive(void); -void Query_query_disabled_from_nothing(void); -void Query_query_only_2_or(void); -void Query_query_only_3_or(void); -void Query_query_2_or(void); -void Query_query_3_or(void); -void Query_query_and_type(void); -void Query_query_or_type(void); -void Query_query_and_type_match_after(void); -void Query_query_or_type_match_after(void); -void Query_query_changed_after_new(void); -void Query_query_changed_after_delete(void); -void Query_query_changed_after_add(void); -void Query_query_changed_after_remove(void); -void Query_query_changed_after_set(void); -void Query_query_change_after_modified(void); -void Query_query_change_after_out_system(void); -void Query_query_change_after_in_system(void); -void Query_query_change_after_modified_out_term(void); -void Query_query_change_check_iter(void); -void Query_query_change_check_iter_after_skip_read(void); -void Query_query_change_check_iter_after_skip_write(void); -void Query_query_change_parent_term(void); -void Query_query_change_prefab_term(void); -void Query_query_change_parent_term_w_tag(void); -void Query_query_change_prefab_term_w_tag(void); -void Query_query_change_skip_non_instanced(void); -void Query_query_changed_w_or(void); -void Query_query_changed_or(void); -void Query_query_changed_w_singleton(void); -void Query_query_changed_w_only_singleton(void); -void Query_query_changed_w_only_singleton_after_set(void); -void Query_query_changed_w_only_singleton_after_out_term(void); -void Query_query_changed_w_only_singleton_after_singleton_out_term(void); -void Query_query_changed_w_only_parent(void); -void Query_query_changed_w_only_parent_after_set(void); -void Query_query_changed_w_only_parent_after_out_term(void); -void Query_query_changed_w_only_parent_after_parent_out_term(void); -void Query_query_changed_tag(void); -void Query_query_changed_no_source(void); -void Query_query_changed_no_source_component(void); -void Query_query_changed_w_not_out(void); -void Query_subquery_match_existing(void); -void Query_subquery_match_new(void); -void Query_subquery_inactive(void); -void Query_subquery_unmatch(void); -void Query_subquery_rematch(void); -void Query_subquery_rematch_w_parent_optional(void); -void Query_subquery_rematch_w_sub_optional(void); -void Query_query_single_pairs(void); -void Query_query_single_instanceof(void); -void Query_query_single_childof(void); -void Query_query_optional_owned(void); -void Query_query_optional_shared(void); -void Query_query_optional_shared_nested(void); -void Query_query_optional_any(void); -void Query_query_rematch_optional_after_add(void); -void Query_get_owned_tag(void); -void Query_get_shared_tag(void); -void Query_explicit_delete(void); -void Query_get_column_size(void); -void Query_orphaned_query(void); -void Query_nested_orphaned_query(void); -void Query_invalid_access_orphaned_query(void); -void Query_stresstest_query_free(void); -void Query_only_from_entity(void); -void Query_only_not_from_entity(void); -void Query_only_from_singleton(void); -void Query_only_not_from_singleton(void); -void Query_get_filter(void); -void Query_group_by(void); -void Query_group_by_w_ctx(void); -void Query_group_by_w_sort_reverse_group_creation(void); -void Query_group_by_iter_one(void); -void Query_group_by_iter_one_all_groups(void); -void Query_group_by_iter_one_empty(void); -void Query_group_by_iter_one_empty_query(void); -void Query_group_by_iter_one_empty_table(void); -void Query_group_by_w_deleted_group_id(void); -void Query_group_by_callbacks(void); -void Query_group_by_default_action(void); -void Query_group_table_count(void); -void Query_iter_valid(void); -void Query_query_optional_tag(void); -void Query_query_optional_shared_tag(void); -void Query_query_iter_10_tags(void); -void Query_query_iter_20_tags(void); -void Query_query_iter_10_components(void); -void Query_query_iter_20_components(void); -void Query_iter_type_set(void); -void Query_filter_term(void); -void Query_2_terms_1_filter(void); -void Query_3_terms_2_filter(void); -void Query_no_instancing_w_singleton(void); -void Query_no_instancing_w_shared(void); -void Query_query_iter_frame_offset(void); -void Query_add_singleton_after_query(void); -void Query_query_w_component_from_parent_from_non_this(void); -void Query_create_query_while_pending(void); -void Query_empty_query(void); -void Query_parent_cascade(void); -void Query_existing_custom_rel_cascade(void); -void Query_new_custom_rel_cascade(void); -void Query_cascade_w_2_depths(void); -void Query_cascade_w_3_depths(void); -void Query_cascade_w_2_depths_desc(void); -void Query_cascade_w_3_depths_desc(void); -void Query_not_pair_relation_wildcard(void); -void Query_not_pair_object_wildcard(void); -void Query_two_pair_wildcards_one_not(void); -void Query_two_pair_wildcards_one_not_any(void); -void Query_implicit_existing_isa_superset(void); -void Query_implicit_new_isa_superset(void); -void Query_isa_superset(void); -void Query_isa_superset_2_lvls(void); -void Query_isa_superset_3_lvls(void); -void Query_isa_superset_2_lvls_owned(void); -void Query_isa_superset_3_lvls_owned(void); -void Query_isa_superset_owned_empty_table_after_match(void); -void Query_isa_self_superset(void); -void Query_childof_superset(void); -void Query_superset_2_targets(void); -void Query_superset_2_relations(void); -void Query_superset_2_relations_instanced(void); -void Query_superset_2_relations_w_component(void); -void Query_superset_2_relations_instanced_w_component(void); -void Query_parent(void); -void Query_existing_isa_cascade(void); -void Query_new_isa_cascade(void); -void Query_childof_cascade(void); -void Query_isa_rematch(void); -void Query_childof_rematch(void); -void Query_isa_unmatch(void); -void Query_childof_unmatch(void); -void Query_isa_rematch_2_lvls(void); -void Query_childof_rematch_2_lvls(void); -void Query_cascade_rematch_2_lvls(void); -void Query_cascade_rematch_2_lvls_2_relations(void); -void Query_cascade_topological(void); -void Query_cascade_desc_rematch_2_lvls(void); -void Query_cascade_desc_rematch_2_lvls_2_relations(void); -void Query_cascade_desc_topological(void); -void Query_childof_rematch_from_isa(void); -void Query_rematch_optional_ref(void); -void Query_rematch_optional_ref_w_2_refs(void); -void Query_rematch_optional_ref_tag_w_ref_component(void); -void Query_match_query_expr_from_scope(void); -void Query_query_long_or_w_ref(void); -void Query_0_query(void); -void Query_query_w_pair_id_and_subj(void); -void Query_table_count(void); -void Query_empty_table_count(void); -void Query_entity_count(void); -void Query_rematch_after_delete_inherited_tag(void); -void Query_rematch_after_delete_rel_of_inherited_pair(void); -void Query_rematch_after_delete_obj_of_inherited_pair(void); -void Query_rematch_empty_table_w_superset(void); -void Query_query_w_short_notation(void); -void Query_query_w_invalid_filter_flag(void); -void Query_query_next_table(void); -void Query_query_next_table_w_changed(void); -void Query_query_next_table_w_populate(void); -void Query_query_next_table_w_skip(void); -void Query_query_next_table_w_populate_first_changed(void); -void Query_query_next_table_w_populate_last_changed(void); -void Query_query_next_table_w_populate_skip_first(void); -void Query_query_next_table_w_populate_skip_last(void); -void Query_create_query_existing_query_entity(void); -void Query_query_for_recycled_pair(void); -void Query_query_w_singleton_w_rule_iter(void); -void Query_query_w_singleton_nested_iter(void); -void Query_query_w_singleton_interleaved_iter(void); -void Query_recycled_component_id(void); -void Query_set_get_context(void); -void Query_set_get_binding_context(void); -void Query_set_get_context_w_free(void); -void Query_set_get_binding_context_w_free(void); -void Query_set_this(void); -void Query_set_this_no_match(void); -void Query_set_this_is_true(void); -void Query_set_this_w_wildcard(void); -void Query_singleton_w_inout_none(void); -void Query_singleton_w_inout_none_or(void); -void Query_component_w_inout_none_or(void); - -// Testsuite 'Iter' -void Iter_page_iter_0_0(void); -void Iter_page_iter_1_0(void); -void Iter_page_iter_0_1(void); -void Iter_page_iter_n_0(void); -void Iter_page_iter_0_n(void); -void Iter_page_iter_m_n(void); -void Iter_page_iter_skip_1_table(void); -void Iter_page_iter_skip_2_tables(void); -void Iter_worker_iter_1(void); -void Iter_worker_iter_2(void); -void Iter_worker_iter_3(void); -void Iter_worker_iter_4(void); -void Iter_paged_iter_w_shared_comp(void); -void Iter_worker_iter_w_shared_comp(void); -void Iter_paged_iter_w_task_query(void); -void Iter_worker_iter_w_task_query(void); -void Iter_worker_iter_w_singleton(void); -void Iter_worker_iter_w_singleton_component(void); -void Iter_worker_iter_w_singleton_instanced(void); -void Iter_worker_iter_w_singleton_component_instanced(void); -void Iter_paged_iter_w_singleton(void); -void Iter_paged_iter_w_singleton_component(void); -void Iter_paged_iter_w_singleton_instanced(void); -void Iter_paged_iter_w_singleton_component_instanced(void); -void Iter_count(void); -void Iter_iter_restore_stack_iter(void); -void Iter_interleaved_iter(void); -void Iter_get_first(void); -void Iter_page_iter_w_only_tag(void); -void Iter_worker_iter_w_only_tag(void); -void Iter_page_iter_w_inout_none(void); -void Iter_worker_iter_w_inout_none(void); -void Iter_page_iter_w_ctx(void); -void Iter_page_iter_w_binding_ctx(void); -void Iter_worker_iter_w_ctx(void); -void Iter_worker_iter_w_binding_ctx(void); -void Iter_column_index_owned(void); -void Iter_column_index_shared(void); -void Iter_column_index_not(void); -void Iter_page_iter_w_fini(void); -void Iter_worker_iter_w_fini(void); -void Iter_rule_page_iter_w_fini(void); -void Iter_rule_worker_iter_w_fini(void); -void Iter_to_str_before_next(void); -void Iter_to_str(void); +void ComponentLifecycle_batched_ensure_new_component_w_lifecycle(void); +void ComponentLifecycle_on_nested_prefab_copy_test_invokes_copy_count(void); +void ComponentLifecycle_no_move_no_move_ctor_with_move_dtor_with_ctor_move_dtor(void); +void ComponentLifecycle_new_w_table_ctor(void); +void ComponentLifecycle_new_w_table_on_add_hook(void); +void ComponentLifecycle_count_in_on_add(void); +void ComponentLifecycle_count_in_on_remove(void); // Testsuite 'Pairs' void Pairs_type_w_one_pair(void); @@ -1783,6 +1235,7 @@ void Pairs_remove_symmetric_relation(void); void Pairs_delete_entity_w_symmetric_relation(void); void Pairs_add_symmetric_exclusive_relation(void); void Pairs_add_symmetric_recycled_relation(void); +void Pairs_symmetric_w_childof(void); void Pairs_with(void); void Pairs_2_with(void); void Pairs_nested_with(void); @@ -1806,6 +1259,13 @@ void Pairs_oneof_other_constraint_violated(void); void Pairs_oneof_other_rel_parent_constraint_violated(void); void Pairs_set_w_recycled_rel(void); void Pairs_set_w_recycled_tgt(void); +void Pairs_force_relationship_on_component(void); +void Pairs_force_relationship_on_target(void); +void Pairs_force_relationship_on_target_trait(void); +void Pairs_force_relationship_on_relationship(void); +void Pairs_force_target_on_component(void); +void Pairs_force_target_on_relationship(void); +void Pairs_force_target_on_target(void); // Testsuite 'Trigger' void Trigger_on_add_trigger_before_table(void); @@ -1815,6 +1275,7 @@ void Trigger_on_remove_trigger_after_table(void); void Trigger_on_add_tag(void); void Trigger_on_add_component(void); void Trigger_on_add_wildcard(void); +void Trigger_on_add_wildcard_after_table(void); void Trigger_on_add_pair(void); void Trigger_on_add_pair_obj_wildcard(void); void Trigger_on_add_pair_pred_wildcard(void); @@ -1889,7 +1350,6 @@ void Trigger_on_add_base_superset_trigger(void); void Trigger_on_add_base_superset_trigger_2_lvls(void); void Trigger_on_add_base_2_entities(void); void Trigger_on_add_base_2_entities_filter(void); -void Trigger_on_set_base_w_value_2_entities(void); void Trigger_on_set_base_w_value_2_entities_instanced(void); void Trigger_on_add_base_w_override(void); void Trigger_on_set_base_w_override(void); @@ -1918,6 +1378,9 @@ void Trigger_on_set_self_superset_from_child_base_of_prefab(void); void Trigger_on_set_self_auto_override(void); void Trigger_on_set_self_superset_auto_override(void); void Trigger_on_set_superset_auto_override(void); +void Trigger_on_set_self_on_instantiate_override(void); +void Trigger_on_set_self_up_on_instantiate_override(void); +void Trigger_on_set_up_on_instantiate_override(void); void Trigger_not_only(void); void Trigger_not_only_w_base(void); void Trigger_not_only_w_base_no_match(void); @@ -1925,13 +1388,38 @@ void Trigger_on_set_superset_after_filter_observer(void); void Trigger_on_set_superset_after_filter_observer_w_on_add(void); void Trigger_on_set_superset_after_filter_observer_w_on_add_isa_after_set(void); void Trigger_on_set_superset_after_filter_observer_w_on_add_2(void); -void Trigger_propagate_w_union_pair(void); // Testsuite 'Observer' +void Observer_on_add_before_edge(void); +void Observer_on_add_after_edge(void); +void Observer_on_add_wildcard_before_edge(void); +void Observer_on_add_wildcard_after_edge(void); +void Observer_on_add_R_wildcard_before_edge(void); +void Observer_on_add_R_wildcard_after_edge(void); +void Observer_on_add_wildcard_T_before_edge(void); +void Observer_on_add_wildcard_T_after_edge(void); +void Observer_on_add_wildcard_wildcard_before_edge(void); +void Observer_on_add_wildcard_wildcard_after_edge(void); +void Observer_on_add_any_before_edge(void); +void Observer_on_add_any_after_edge(void); +void Observer_on_remove_before_edge(void); +void Observer_on_remove_after_edge(void); +void Observer_on_remove_wildcard_before_edge(void); +void Observer_on_remove_wildcard_after_edge(void); +void Observer_on_remove_R_wildcard_before_edge(void); +void Observer_on_remove_R_wildcard_after_edge(void); +void Observer_on_remove_wildcard_T_before_edge(void); +void Observer_on_remove_wildcard_T_after_edge(void); +void Observer_on_remove_wildcard_wildcard_before_edge(void); +void Observer_on_remove_wildcard_wildcard_after_edge(void); +void Observer_on_remove_any_before_edge(void); +void Observer_on_remove_any_after_edge(void); void Observer_2_terms_w_on_add(void); void Observer_2_terms_w_on_remove(void); void Observer_2_terms_w_on_set_value(void); void Observer_2_terms_w_on_remove_value(void); +void Observer_2_terms_w_on_set_value_self_up(void); +void Observer_2_terms_w_on_remove_value_self_up(void); void Observer_2_terms_w_on_add_2nd(void); void Observer_2_terms_w_on_remove_2nd(void); void Observer_2_pair_terms_w_on_add(void); @@ -1979,6 +1467,10 @@ void Observer_2_terms_1_filter(void); void Observer_3_terms_2_filter(void); void Observer_and_from(void); void Observer_or_from(void); +void Observer_and_from_empty(void); +void Observer_or_from_empty(void); +void Observer_and_from_empty_w_tag(void); +void Observer_or_from_empty_w_tag(void); void Observer_notify_propagated_twice(void); void Observer_on_add_yield_existing(void); void Observer_on_add_yield_existing_2_tables(void); @@ -1986,6 +1478,20 @@ void Observer_on_add_yield_existing_2_terms(void); void Observer_on_add_yield_existing_wildcard(void); void Observer_on_add_yield_existing_wildcard_multi(void); void Observer_on_add_yield_existing_wildcard_multi_w_wildcard_pivot(void); +void Observer_on_remove_yield_existing(void); +void Observer_on_remove_yield_existing_2_tables(void); +void Observer_on_remove_yield_existing_2_terms(void); +void Observer_on_remove_yield_existing_wildcard(void); +void Observer_on_remove_yield_existing_wildcard_multi(void); +void Observer_on_remove_yield_existing_wildcard_multi_w_wildcard_pivot(void); +void Observer_on_add_remove_yield_existing(void); +void Observer_on_add_remove_yield_existing_flags(void); +void Observer_on_add_remove_no_on_add_yield_existing(void); +void Observer_on_add_remove_no_on_remove_yield_existing(void); +void Observer_yield_existing_flags_w_multi_observer(void); +void Observer_yield_on_create_without_on_add(void); +void Observer_yield_on_delete_without_on_remove(void); +void Observer_yield_existing_w_not_first_term(void); void Observer_observer_superset_wildcard(void); void Observer_observer_superset_wildcard_add_isa(void); void Observer_observer_superset_wildcard_add_isa_at_offset(void); @@ -1994,9 +1500,15 @@ void Observer_mixed_on_set_w_tag(void); void Observer_mixed_un_set_w_tag(void); void Observer_match_base_w_id_at_offset(void); void Observer_custom_run_action(void); -void Observer_custom_run_action_w_iter_next(void); void Observer_custom_run_action_2_terms(void); void Observer_custom_run_action_w_iter_next_2_terms(void); +void Observer_custom_run_action_w_field(void); +void Observer_custom_run_action_w_2_fields(void); +void Observer_custom_run_w_yield_existing(void); +void Observer_custom_run_w_yield_existing_1_field(void); +void Observer_custom_run_w_yield_existing_1_field_w_callback(void); +void Observer_custom_run_w_yield_existing_2_fields(void); +void Observer_custom_run_w_yield_existing_2_fields_w_callback(void); void Observer_read_in_on_remove_after_add_other_w_not(void); void Observer_observer_w_short_notation(void); void Observer_observer_w_filter_term(void); @@ -2005,6 +1517,19 @@ void Observer_propagate_after_on_delete_clear_action(void); void Observer_on_add_after_batch_w_exclusive_adds(void); void Observer_propagate_match_relationship_w_self_up(void); void Observer_propagate_match_relationship_w_up(void); +void Observer_propagate_isa_of_parent_add(void); +void Observer_propagate_isa_of_parent_remove(void); +void Observer_propagate_isa_of_parent_set(void); +void Observer_propagate_add_childof_of_parent(void); +void Observer_propagate_add_childof_of_parent_w_siblings(void); +void Observer_propagate_add_childof_w_parent_w_base(void); +void Observer_propagate_remove_childof_w_parent_w_base(void); +void Observer_propagate_remove_childof_of_parent(void); +void Observer_propagate_add_isa_of_parent(void); +void Observer_propagate_remove_isa_of_parent(void); +void Observer_propagate_add_childof_of_base(void); +void Observer_propagate_remove_childof_of_base(void); +void Observer_emit_for_parent_w_prefab_child_and_instance(void); void Observer_observer_w_2_fixed_src(void); void Observer_emit_for_recreated_id_after_remove_all(void); void Observer_emit_for_recreated_id_after_remove_all_wildcard(void); @@ -2025,9 +1550,59 @@ void Observer_notify_after_defer_batched_2_entities_in_table(void); void Observer_notify_after_defer_batched_2_entities_in_table_w_tgt(void); void Observer_multi_observer_table_fill_w_singleton(void); void Observer_wildcard_propagate_w_other_table(void); -void Observer_add_in_yield_existing(void); -void Observer_add_in_yield_existing_multi(void); -void Observer_emit_for_parent_w_prefab_child_and_instance(void); +void Observer_add_in_on_add_yield_existing(void); +void Observer_add_in_on_add_yield_existing_multi(void); +void Observer_add_in_on_remove_yield_existing(void); +void Observer_add_in_on_remove_yield_existing_multi(void); +void Observer_disable_observer(void); +void Observer_disable_observer_module(void); +void Observer_disable_observer_module_nested(void); +void Observer_disable_observer_and_module(void); +void Observer_disable_multi_observer(void); +void Observer_disable_multi_observer_module(void); +void Observer_disable_multi_observer_module_nested(void); +void Observer_disable_multi_observer_and_module(void); +void Observer_tag_w_on_set_and_on_add(void); +void Observer_tag_w_on_set_and_on_add_reverse(void); +void Observer_on_add_pair_w_rel_any(void); +void Observer_on_remove_pair_w_rel_any(void); +void Observer_on_add_pair_w_tgt_any(void); +void Observer_on_remove_pair_w_tgt_any(void); +void Observer_on_add_pair_w_any_any(void); +void Observer_on_remove_pair_w_any_any(void); +void Observer_on_add_any(void); +void Observer_on_remove_any(void); +void Observer_get_filter(void); +void Observer_uni_observer_eval_count(void); +void Observer_multi_observer_eval_count(void); +void Observer_yield_existing_uni_no_this(void); +void Observer_yield_existing_multi_no_this(void); +void Observer_observer_no_id_in_scope(void); +void Observer_register_comp_in_emit_named_entity(void); +void Observer_register_comp_w_macro_in_emit_named_entity(void); +void Observer_add_to_self_in_emit_entity(void); +void Observer_on_set_w_not_tag(void); +void Observer_on_set_w_not_component(void); +void Observer_wildcard_event(void); +void Observer_register_callback_after_run(void); +void Observer_register_run_after_callback(void); +void Observer_register_callback_after_run_ctx(void); +void Observer_register_run_after_callback_ctx(void); +void Observer_on_add_after_new_w_table(void); +void Observer_ref_flag_term_1(void); +void Observer_ref_flag_term_2(void); +void Observer_forward_up_flag_term_1(void); +void Observer_forward_up_flag_term_2(void); +void Observer_propagate_up_flag_term_1(void); +void Observer_propagate_up_flag_term_2(void); +void Observer_row_flag_term_1(void); +void Observer_row_flag_term_2(void); +void Observer_on_add_optional(void); +void Observer_on_remove_optional(void); +void Observer_on_add_multi_optional(void); +void Observer_on_remove_multi_optional(void); +void Observer_on_add_multi_only_optional(void); +void Observer_on_remove_multi_only_optional(void); void Observer_cache_test_1(void); void Observer_cache_test_2(void); void Observer_cache_test_3(void); @@ -2056,6 +1631,10 @@ void ObserverOnSet_add_base_to_1_overridden(void); void ObserverOnSet_add_base_to_2_overridden(void); void ObserverOnSet_add_base_to_1_of_2_overridden(void); void ObserverOnSet_on_set_after_remove_override(void); +void ObserverOnSet_on_set_after_remove_override_isa_before_add(void); +void ObserverOnSet_on_set_w_override_after_delete(void); +void ObserverOnSet_on_set_w_override_after_clear(void); +void ObserverOnSet_on_set_w_override_after_delete_w_ecs_init(void); void ObserverOnSet_no_set_after_remove_base(void); void ObserverOnSet_un_set_after_remove(void); void ObserverOnSet_un_set_after_remove_base(void); @@ -2064,6 +1643,7 @@ void ObserverOnSet_remove_from_current_in_on_set(void); void ObserverOnSet_remove_set_component_in_on_set(void); void ObserverOnSet_match_table_created_w_add_in_on_set(void); void ObserverOnSet_set_optional(void); +void ObserverOnSet_set_optional_one_term(void); void ObserverOnSet_set_from_nothing(void); void ObserverOnSet_add_0_entity_in_on_set(void); void ObserverOnSet_on_set_prefab(void); @@ -2126,7 +1706,6 @@ void TriggerOnSet_on_set_after_override(void); void TriggerOnSet_on_set_after_override_w_new(void); void TriggerOnSet_on_set_after_override_w_new_w_count(void); void TriggerOnSet_on_set_after_override_1_of_2_overridden(void); -void TriggerOnSet_on_set_after_snapshot_restore(void); void TriggerOnSet_on_set_after_remove_override(void); void TriggerOnSet_emplace(void); void TriggerOnSet_un_set_tag_w_remove(void); @@ -2151,6 +1730,7 @@ void Monitor_monitor_w_wildcard(void); void Monitor_monitor_at_fini(void); void Monitor_monitor_other_table(void); void Monitor_monitor_component(void); +void Monitor_yield_existing(void); // Testsuite 'Prefab' void Prefab_setup(void); @@ -2255,7 +1835,6 @@ void Prefab_dont_copy_children_for_non_prefab_base(void); void Prefab_get_component_pair_from_base(void); void Prefab_get_component_pair_from_prefab_base(void); void Prefab_override_dont_inherit(void); -void Prefab_prefab_w_switch(void); void Prefab_prefab_child_w_dont_inherit_component(void); void Prefab_prefab_child_override(void); void Prefab_prefab_child_override_w_exclusive_pair(void); @@ -2271,16 +1850,43 @@ void Prefab_2_instances_w_slots_same_table(void); void Prefab_slot_has_union(void); void Prefab_slot_override(void); void Prefab_base_slot_override(void); -void Prefab_prefab_child_w_union(void); void Prefab_override_twice_w_add(void); void Prefab_override_twice_w_set(void); void Prefab_auto_override_copy_once(void); void Prefab_always_override(void); void Prefab_always_override_pair(void); void Prefab_child_of_prefab_is_prefab(void); +void Prefab_child_of_prefab_w_prefab_is_prefab(void); +void Prefab_child_of_prefab_w_prefab_is_prefab_w_component(void); void Prefab_override_exclusive(void); void Prefab_override_exclusive_2_lvls(void); void Prefab_hierarchy_w_recycled_id(void); +void Prefab_disable_ids(void); +void Prefab_disable_nested_ids(void); +void Prefab_prefab_w_children_w_isa_auto_override(void); +void Prefab_prefab_child_w_override(void); +void Prefab_prefab_child_w_override_and_higher_component(void); +void Prefab_prefab_child_w_override_and_lower_component(void); +void Prefab_prefab_1_child_offset_id(void); +void Prefab_prefab_2_children_offset_id(void); +void Prefab_prefab_3_children_offset_id(void); +void Prefab_prefab_2_children_2_types_offset_id(void); +void Prefab_prefab_3_children_3_types_offset_id(void); +void Prefab_prefab_2_children_2_types_reverse_offset_id(void); +void Prefab_prefab_3_children_3_types_reverse_offset_id(void); +void Prefab_prefab_2_lvl_nested_children_offset_id(void); +void Prefab_prefab_3_lvl_nested_children_offset_id(void); +void Prefab_prefab_recycled_children_offset_id(void); +void Prefab_prefab_recycled_instance_offset_id(void); +void Prefab_prefab_children_recycled_offset_id(void); +void Prefab_prefab_recycled_children_recycled_offset_id(void); +void Prefab_prefab_recycled_children_recycled_offset_id_different_generation(void); +void Prefab_prefab_1_child_offset_id_occupied(void); +void Prefab_prefab_1_child_offset_id_recycled_occupied(void); +void Prefab_prefab_child_offset_w_smaller_child_id(void); +void Prefab_prefab_w_union(void); +void Prefab_prefab_child_w_union(void); +void Prefab_prefab_w_union_and_component(void); // Testsuite 'World' void World_setup(void); @@ -2295,6 +1901,11 @@ void World_entity_range_add_in_range_staged(void); void World_entity_range_add_out_of_range_staged(void); void World_entity_range_out_of_range_check_disabled(void); void World_entity_range_check_after_delete(void); +void World_entity_range_offset_0(void); +void World_entity_range_set_limit_to_lower(void); +void World_entity_range_set_limit_to_lower_than_offset(void); +void World_entity_range_overlapping_new_id(void); +void World_entity_range_overlapping_new_bulk_id(void); void World_dim(void); void World_phases(void); void World_phases_w_merging(void); @@ -2330,15 +1941,22 @@ void World_use_after_delete_empty_w_component(void); void World_use_after_clear_empty_w_component(void); void World_use_after_clear_empty_w_component_w_lifecycle(void); void World_use_after_clear_unused(void); -void World_get_mut_in_at_fini(void); +void World_ensure_in_at_fini(void); void World_get_type_info(void); void World_get_type_info_after_delete_with(void); void World_get_type_info_after_reuse(void); void World_no_name_prefix_after_init(void); +void World_component_init_w_name_prefix(void); +void World_component_macro_w_name_prefix(void); void World_set_get_context(void); void World_set_get_binding_context(void); void World_set_get_context_w_free(void); void World_set_get_binding_context_w_free(void); +void World_get_entities(void); +void World_run_post_frame(void); +void World_run_post_frame_outside_of_frame(void); +void World_get_flags(void); +void World_fini_queue_overflow(void); // Testsuite 'WorldInfo' void WorldInfo_get_tick(void); @@ -2368,10 +1986,8 @@ void Type_entity_path_str(void); void Type_entity_instanceof_str(void); void Type_entity_childof_str(void); void Type_entity_pair_str(void); -void Type_entity_and_str(void); void Type_entity_str_small_buffer(void); void Type_role_pair_str(void); -void Type_role_and_str(void); void Type_role_owned_str(void); void Type_role_disabled_str(void); void Type_large_type_expr(void); @@ -2390,15 +2006,16 @@ void Commands_defer_twice(void); void Commands_defer_twice_in_progress(void); void Commands_run_w_defer(void); void Commands_system_in_progress_w_defer(void); -void Commands_defer_get_mut_no_modify(void); -void Commands_defer_get_mut_w_modify(void); +void Commands_defer_ensure(void); +void Commands_defer_ensure_no_modify(void); +void Commands_defer_ensure_w_modify(void); void Commands_defer_modify(void); void Commands_defer_set_pair(void); void Commands_defer_clear(void); void Commands_defer_add_after_delete(void); void Commands_defer_set_after_delete(void); -void Commands_defer_get_mut_after_delete(void); -void Commands_defer_get_mut_after_delete_2nd_to_last(void); +void Commands_defer_ensure_after_delete(void); +void Commands_defer_ensure_after_delete_2nd_to_last(void); void Commands_defer_add_child_to_deleted_parent(void); void Commands_recreate_deleted_entity_while_deferred(void); void Commands_defer_add_to_recycled_id(void); @@ -2423,21 +2040,22 @@ void Commands_discard_remove_two(void); void Commands_discard_child(void); void Commands_discard_child_w_add(void); void Commands_defer_return_value(void); -void Commands_defer_get_mut_pair(void); +void Commands_defer_ensure_pair(void); void Commands_async_stage_add(void); void Commands_async_stage_add_twice(void); void Commands_async_stage_remove(void); void Commands_async_stage_clear(void); void Commands_async_stage_delete(void); void Commands_async_stage_new(void); -void Commands_async_stage_no_get(void); void Commands_async_stage_readonly(void); -void Commands_async_stage_is_async(void); void Commands_register_component_while_in_progress(void); void Commands_register_component_while_staged(void); void Commands_register_component_while_deferred(void); void Commands_defer_enable(void); void Commands_defer_disable(void); +void Commands_defer_enable_from_stage(void); +void Commands_defer_toggle(void); +void Commands_defer_toggle_from_stage(void); void Commands_defer_delete_with(void); void Commands_defer_remove_all(void); void Commands_deferred_modified_after_remove(void); @@ -2450,10 +2068,6 @@ void Commands_update_observer_while_deferred(void); void Commands_defer_set_large_component(void); void Commands_defer_while_suspend_readonly(void); void Commands_defer_while_suspend_readonly_w_existing_commands(void); -void Commands_defer_add_union_relationship(void); -void Commands_defer_add_existing_union_relationship(void); -void Commands_defer_add_union_relationship_2_ops(void); -void Commands_defer_add_existing_union_relationship_2_ops(void); void Commands_defer_remove_after_set(void); void Commands_defer_remove_after_set_w_observer(void); void Commands_defer_override_after_remove(void); @@ -2473,24 +2087,24 @@ void Commands_clear_after_add_to_nonempty(void); void Commands_remove_after_add_to_nonempty(void); void Commands_register_while_deferred_with_n_stages(void); void Commands_defer_2_sets_w_multi_observer(void); -void Commands_defer_2_get_muts_w_multi_observer(void); -void Commands_defer_2_get_muts_no_modified_w_multi_observer(void); +void Commands_defer_2_ensures_w_multi_observer(void); +void Commands_defer_2_ensures_no_modified_w_multi_observer(void); void Commands_exists_remove_set(void); void Commands_absent_remove_set(void); void Commands_exists_set_remove(void); void Commands_absent_set_remove(void); -void Commands_exists_remove_get_mut(void); -void Commands_absent_remove_get_mut(void); -void Commands_exists_get_mut_remove(void); -void Commands_absent_get_mut_remove(void); -void Commands_exists_set_w_get_mut(void); +void Commands_exists_remove_ensure(void); +void Commands_absent_remove_ensure(void); +void Commands_exists_ensure_remove(void); +void Commands_absent_ensure_remove(void); +void Commands_exists_set_w_ensure(void); void Commands_absent_set_invoke_on_set(void); void Commands_exists_set_invoke_on_set(void); -void Commands_defer_get_mut_no_on_set(void); -void Commands_defer_existing_get_mut_no_on_set(void); -void Commands_get_mut_override(void); +void Commands_defer_ensure_no_on_set(void); +void Commands_defer_existing_ensure_no_on_set(void); +void Commands_ensure_override(void); void Commands_set_override(void); -void Commands_absent_get_mut_for_entity_w_tag(void); +void Commands_absent_ensure_for_entity_w_tag(void); void Commands_on_set_hook_before_on_add_for_existing_component(void); void Commands_defer_2_sets_w_observer_same_component(void); void Commands_defer_2_sets_w_observer_other_component(void); @@ -2501,6 +2115,26 @@ void Commands_on_add_hook_while_defer_suspended(void); void Commands_on_set_hook_while_defer_suspended(void); void Commands_on_remove_hook_while_defer_suspended(void); void Commands_on_set_hook_batched_is_deferred(void); +void Commands_add_path(void); +void Commands_add_path_to_deleted_parent(void); +void Commands_add_path_nested(void); +void Commands_add_path_nested_to_deleted_parent(void); +void Commands_add_path_nested_to_created_deleted_parent(void); +void Commands_add_path_w_stage(void); +void Commands_add_path_to_deleted_parent_w_stage(void); +void Commands_add_path_nested_w_stage(void); +void Commands_add_path_nested_to_deleted_parent_w_stage(void); +void Commands_add_path_nested_to_created_deleted_parent_w_stage(void); +void Commands_defer_emplace_w_arg(void); +void Commands_defer_emplace_existing_w_arg(void); +void Commands_mixed_on_add_on_set_w_set_w_batching(void); +void Commands_mixed_on_add_on_set_w_emplace(void); +void Commands_add_isa_set_w_override_batched(void); +void Commands_add_set_isa_w_override_batched(void); +void Commands_add_batched_set_with(void); +void Commands_defer_emplace_after_remove(void); +void Commands_batched_w_table_change_in_observer(void); +void Commands_redefine_named_in_threaded_app(void); // Testsuite 'SingleThreadStaging' void SingleThreadStaging_setup(void); @@ -2558,14 +2192,13 @@ void SingleThreadStaging_override_after_remove_in_progress(void); void SingleThreadStaging_get_parent_in_progress(void); void SingleThreadStaging_merge_once(void); void SingleThreadStaging_clear_stage_after_merge(void); -void SingleThreadStaging_get_mutable(void); -void SingleThreadStaging_get_mutable_from_main(void); -void SingleThreadStaging_get_mutable_w_add(void); +void SingleThreadStaging_ensureable(void); +void SingleThreadStaging_ensureable_from_main(void); +void SingleThreadStaging_ensureable_w_add(void); void SingleThreadStaging_on_add_after_new_type_in_progress(void); void SingleThreadStaging_lock_table(void); void SingleThreadStaging_recursive_lock_table(void); void SingleThreadStaging_modify_after_lock(void); -void SingleThreadStaging_get_case_from_stage(void); void SingleThreadStaging_get_object_from_stage(void); void SingleThreadStaging_add_to_world_while_readonly(void); void SingleThreadStaging_add_to_world_and_stage_while_readonly(void); @@ -2592,9 +2225,15 @@ void Stresstests_add_1k_tags(void); // Testsuite 'Table' void Table_get_index(void); +void Table_get_index_after_tag(void); void Table_get_index_not_in_table(void); +void Table_get_index_tag(void); +void Table_get_index_pair(void); +void Table_get_index_pair_tag(void); void Table_get_column(void); void Table_get_column_for_tag(void); +void Table_get_column_for_low_tag(void); +void Table_get_column_for_high_component(void); void Table_get_column_for_component_after_tag(void); void Table_get_column_w_offset(void); void Table_get_id(void); @@ -2605,25 +2244,21 @@ void Table_get_depth(void); void Table_get_depth_non_acyclic(void); void Table_get_depth_2_paths(void); void Table_get_column_size(void); +void Table_has_id(void); +void Table_has_pair(void); +void Table_has_wildcard_pair(void); +void Table_has_any_pair(void); +void Table_clear_table_kills_entities(void); +void Table_clear_table_add_new(void); +void Table_clear_table_check_size(void); +void Table_clear_table_twice_check_size(void); +void Table_clear_table_on_remove_hooks(void); +void Table_clear_table_on_remove_observer(void); // Testsuite 'Poly' -void Poly_iter_query(void); -void Poly_iter_query_w_filter(void); -void Poly_iter_world(void); -void Poly_iter_world_w_filter(void); -void Poly_iter_rule(void); -void Poly_iter_rule_w_filter(void); -void Poly_iter_filter(void); -void Poly_iter_filter_w_filter(void); void Poly_on_set_poly_observer(void); void Poly_on_set_poly_query(void); void Poly_on_set_poly_system(void); -void Poly_iter_filter_from_entity(void); -void Poly_iter_query_from_entity(void); -void Poly_iter_rule_from_entity(void); -void Poly_free_filter_entity(void); -void Poly_free_query_entity(void); -void Poly_free_rule_entity(void); // Testsuite 'Internals' void Internals_setup(void); @@ -2660,6 +2295,7 @@ void Error_log_log(void); void Error_log_warning(void); void Error_log_error(void); void Error_last_error(void); +void Error_set_log_level_return(void); // Testsuite 'StackAlloc' void StackAlloc_init_fini(void); @@ -2754,6 +2390,38 @@ bake_test_case Id_testcases[] = { "pair_id_w_tag_property_w_obj_wildcard_is_tag", Id_pair_id_w_tag_property_w_obj_wildcard_is_tag }, + { + "pair_w_rel_wildcard_is_tag", + Id_pair_w_rel_wildcard_is_tag + }, + { + "pair_w_obj_wildcard_is_tag", + Id_pair_w_obj_wildcard_is_tag + }, + { + "pair_w_rel_tag_obj_wildcard_is_tag", + Id_pair_w_rel_tag_obj_wildcard_is_tag + }, + { + "pair_w_wildcard_wildcard_is_tag", + Id_pair_w_wildcard_wildcard_is_tag + }, + { + "pair_w_rel_any_is_tag", + Id_pair_w_rel_any_is_tag + }, + { + "pair_w_obj_any_is_tag", + Id_pair_w_obj_any_is_tag + }, + { + "pair_w_rel_tag_obj_any_is_tag", + Id_pair_w_rel_tag_obj_any_is_tag + }, + { + "pair_w_any_any_is_tag", + Id_pair_w_any_any_is_tag + }, { "id_w_override_is_tag", Id_id_w_override_is_tag @@ -2781,6 +2449,46 @@ bake_test_case Id_testcases[] = { { "make_pair_of_pair_tgt", Id_make_pair_of_pair_tgt + }, + { + "0_entity", + Id_0_entity + }, + { + "entity_from_str", + Id_entity_from_str + }, + { + "unresolved_entity_from_str", + Id_unresolved_entity_from_str + }, + { + "scoped_entity_from_str", + Id_scoped_entity_from_str + }, + { + "template_entity_from_str", + Id_template_entity_from_str + }, + { + "pair_from_str", + Id_pair_from_str + }, + { + "unresolved_pair_from_str", + Id_unresolved_pair_from_str + }, + { + "wildcard_pair_from_str", + Id_wildcard_pair_from_str + }, + { + "any_pair_from_str", + Id_any_pair_from_str + }, + { + "invalid_pair", + Id_invalid_pair } }; @@ -2998,28 +2706,28 @@ bake_test_case Entity_testcases[] = { Entity_record_find_from_stage }, { - "ensure_zero_gen", - Entity_ensure_zero_gen + "make_alive_zero_gen", + Entity_make_alive_zero_gen }, { - "ensure_nonzero_gen", - Entity_ensure_nonzero_gen + "make_alive_nonzero_gen", + Entity_make_alive_nonzero_gen }, { - "ensure_zero_gen_exists", - Entity_ensure_zero_gen_exists + "make_alive_zero_gen_exists", + Entity_make_alive_zero_gen_exists }, { - "ensure_nonzero_gen_exists", - Entity_ensure_nonzero_gen_exists + "make_alive_nonzero_gen_exists", + Entity_make_alive_nonzero_gen_exists }, { - "ensure_zero_gen_exists_alive", - Entity_ensure_zero_gen_exists_alive + "make_alive_zero_gen_exists_alive", + Entity_make_alive_zero_gen_exists_alive }, { - "ensure_nonzero_gen_exists_alive", - Entity_ensure_nonzero_gen_exists_alive + "make_alive_nonzero_gen_exists_alive", + Entity_make_alive_nonzero_gen_exists_alive }, { "set_scope_w_entity_init_from_stage", @@ -3158,16 +2866,16 @@ bake_test_case Entity_testcases[] = { Entity_defer_set_name_w_overlapping_ptr }, { - "ensure_from_stage", - Entity_ensure_from_stage + "make_alive_from_stage", + Entity_make_alive_from_stage }, { - "ensure_after_deleted_1_entity", - Entity_ensure_after_deleted_1_entity + "make_alive_after_deleted_1_entity", + Entity_make_alive_after_deleted_1_entity }, { - "ensure_after_deleted_2_entities", - Entity_ensure_after_deleted_2_entities + "make_alive_after_deleted_2_entities", + Entity_make_alive_after_deleted_2_entities }, { "defer_entity_init_w_set_name_w_add_childof", @@ -3182,16 +2890,36 @@ bake_test_case Entity_testcases[] = { Entity_entity_w_existing_digit_name }, { - "entity_w_conflicting_digit_name", - Entity_entity_w_conflicting_digit_name + "entity_from_digit", + Entity_entity_from_digit + }, + { + "entity_from_existing_digit", + Entity_entity_from_existing_digit + }, + { + "entity_from_digit_path", + Entity_entity_from_digit_path + }, + { + "entity_from_existing_digit_path", + Entity_entity_from_existing_digit_path + }, + { + "entity_from_digit_0_path", + Entity_entity_from_digit_0_path + }, + { + "entity_from_conflicting_digit", + Entity_entity_from_conflicting_digit }, { - "set_generation_on_nonempty_entity", - Entity_set_generation_on_nonempty_entity + "set_version_on_nonempty_entity", + Entity_set_version_on_nonempty_entity }, { - "set_generation_while_deferred", - Entity_set_generation_while_deferred + "set_version_while_deferred", + Entity_set_version_while_deferred }, { "commit_w_on_add", @@ -3208,5997 +2936,3738 @@ bake_test_case Entity_testcases[] = { { "entity_init_existing_no_sep", Entity_entity_init_existing_no_sep - } -}; - -bake_test_case Search_testcases[] = { - { - "search", - Search_search }, { - "search_wildcard", - Search_search_wildcard + "entity_init_w_set_1_comp", + Entity_entity_init_w_set_1_comp }, { - "search_wildcard_w_offset", - Search_search_wildcard_w_offset + "entity_init_w_set_2_comp", + Entity_entity_init_w_set_2_comp }, { - "search_relation_wildcard_w_offset", - Search_search_relation_wildcard_w_offset + "entity_init_w_set_1_comp_1_tag", + Entity_entity_init_w_set_1_comp_1_tag }, { - "search_pair_w_any_rel", - Search_search_pair_w_any_rel + "entity_init_w_set_2_comp_2_tag", + Entity_entity_init_w_set_2_comp_2_tag }, { - "search_pair_w_any_obj", - Search_search_pair_w_any_obj + "entity_init_w_set_1_comp_w_name", + Entity_entity_init_w_set_1_comp_w_name }, { - "search_follow_relation_lvl_0", - Search_search_follow_relation_lvl_0 + "entity_init_w_set_1_comp_existing", + Entity_entity_init_w_set_1_comp_existing }, { - "search_follow_relation_lvl_1", - Search_search_follow_relation_lvl_1 + "entity_init_w_set_1_comp_existing_empty", + Entity_entity_init_w_set_1_comp_existing_empty }, { - "search_follow_relation_lvl_2", - Search_search_follow_relation_lvl_2 + "entity_init_w_set_1_comp_1_tag_w_set", + Entity_entity_init_w_set_1_comp_1_tag_w_set }, { - "search_follow_relation_lvl_3", - Search_search_follow_relation_lvl_3 + "entity_init_w_set_w_hook", + Entity_entity_init_w_set_w_hook }, { - "search_first_lvl_0", - Search_search_first_lvl_0 + "entity_init_w_set_w_observer", + Entity_entity_init_w_set_w_observer }, { - "search_first_lvl_1", - Search_search_first_lvl_1 + "entity_init_w_set_1_comp_defer", + Entity_entity_init_w_set_1_comp_defer }, { - "search_first_lvl_2", - Search_search_first_lvl_2 + "entity_init_w_set_2_comp_defer", + Entity_entity_init_w_set_2_comp_defer }, { - "search_first_lvl_3", - Search_search_first_lvl_3 + "entity_init_w_set_1_comp_1_tag_defer", + Entity_entity_init_w_set_1_comp_1_tag_defer }, { - "search_relation_wildcard", - Search_search_relation_wildcard + "entity_init_w_set_2_comp_2_tag_defer", + Entity_entity_init_w_set_2_comp_2_tag_defer }, { - "search_relation_at_offset", - Search_search_relation_at_offset + "entity_init_w_set_1_comp_w_name_defer", + Entity_entity_init_w_set_1_comp_w_name_defer }, { - "search_relation_inherit_from_parent", - Search_search_relation_inherit_from_parent + "entity_init_w_set_1_comp_existing_defer", + Entity_entity_init_w_set_1_comp_existing_defer }, { - "search_relation_dont_inherit", - Search_search_relation_dont_inherit + "entity_init_w_set_1_comp_existing_empty_defer", + Entity_entity_init_w_set_1_comp_existing_empty_defer }, { - "search_relation_dont_inherit_from_parent", - Search_search_relation_dont_inherit_from_parent + "entity_init_w_set_1_comp_1_tag_w_set_defer", + Entity_entity_init_w_set_1_comp_1_tag_w_set_defer }, { - "search_relation_exclusive", - Search_search_relation_exclusive + "insert_1_comp", + Entity_insert_1_comp }, { - "search_relation_union", - Search_search_relation_union + "insert_2_comp", + Entity_insert_2_comp }, { - "search_relation_union_wildcard", - Search_search_relation_union_wildcard + "insert_1_comp_1_tag", + Entity_insert_1_comp_1_tag }, { - "search_relation_union_pair", - Search_search_relation_union_pair - } -}; - -bake_test_case Event_testcases[] = { - { - "table_1_id_w_trigger", - Event_table_1_id_w_trigger + "entity_w_parent", + Entity_entity_w_parent }, { - "table_2_ids_w_trigger", - Event_table_2_ids_w_trigger + "entity_w_parent_w_name", + Entity_entity_w_parent_w_name }, { - "table_1_id_w_observer", - Event_table_1_id_w_observer + "entity_w_parent_w_add", + Entity_entity_w_parent_w_add }, { - "table_2_ids_w_observer", - Event_table_2_ids_w_observer + "entity_w_parent_w_add_w_parent", + Entity_entity_w_parent_w_add_w_parent }, { - "emit_event_for_empty_table", - Event_emit_event_for_empty_table + "entity_w_parent_w_set", + Entity_entity_w_parent_w_set }, { - "emit_table_event", - Event_emit_table_event - }, + "entity_w_parent_w_set_w_parent", + Entity_entity_w_parent_w_set_w_parent + } +}; + +bake_test_case Each_testcases[] = { { - "emit_staged_from_world", - Event_emit_staged_from_world + "each_tag", + Each_each_tag }, { - "emit_staged_from_stage", - Event_emit_staged_from_stage + "each_component", + Each_each_component }, { - "emit_staged_from_world_observer", - Event_emit_staged_from_world_observer + "each_pair", + Each_each_pair }, { - "emit_staged_from_stage_observer", - Event_emit_staged_from_stage_observer + "each_pair_rel_wildcard", + Each_each_pair_rel_wildcard }, { - "emit_for_entity", - Event_emit_for_entity - }, + "each_pair_tgt_wildcard", + Each_each_pair_tgt_wildcard + } +}; + +bake_test_case Iter_testcases[] = { { - "emit_custom_for_any", - Event_emit_custom_for_any + "page_iter_0_0", + Iter_page_iter_0_0 }, { - "emit_custom_implicit_any", - Event_emit_custom_implicit_any + "page_iter_1_0", + Iter_page_iter_1_0 }, { - "emit_custom_empty_type", - Event_emit_custom_empty_type + "page_iter_0_1", + Iter_page_iter_0_1 }, { - "emit_w_param", - Event_emit_w_param + "page_iter_n_0", + Iter_page_iter_n_0 }, { - "emit_w_const_param", - Event_emit_w_const_param + "page_iter_0_n", + Iter_page_iter_0_n }, { - "enqueue_event_1_id", - Event_enqueue_event_1_id + "page_iter_m_n", + Iter_page_iter_m_n }, { - "enqueue_event_2_ids", - Event_enqueue_event_2_ids + "page_iter_skip_1_table", + Iter_page_iter_skip_1_table }, { - "enqueue_event_w_data", - Event_enqueue_event_w_data + "page_iter_skip_2_tables", + Iter_page_iter_skip_2_tables }, { - "enqueue_event_w_data_move", - Event_enqueue_event_w_data_move + "worker_iter_1", + Iter_worker_iter_1 }, { - "enqueue_event_w_data_copy", - Event_enqueue_event_w_data_copy + "worker_iter_2", + Iter_worker_iter_2 }, { - "enqueue_event_w_const_data_no_copy", - Event_enqueue_event_w_const_data_no_copy + "worker_iter_3", + Iter_worker_iter_3 }, { - "enqueue_event_not_alive", - Event_enqueue_event_not_alive + "worker_iter_4", + Iter_worker_iter_4 }, { - "enqueue_event_not_alive_w_data_move", - Event_enqueue_event_not_alive_w_data_move + "paged_iter_w_shared_comp", + Iter_paged_iter_w_shared_comp }, { - "enqueue_event_not_alive_w_data_copy", - Event_enqueue_event_not_alive_w_data_copy + "worker_iter_w_shared_comp", + Iter_worker_iter_w_shared_comp }, { - "enqueue_event_not_alive_after_delete_during_merge", - Event_enqueue_event_not_alive_after_delete_during_merge + "paged_iter_w_task_query", + Iter_paged_iter_w_task_query }, { - "enqueue_event_not_alive_w_data_move_after_delete_during_merge", - Event_enqueue_event_not_alive_w_data_move_after_delete_during_merge + "worker_iter_w_task_query", + Iter_worker_iter_w_task_query }, { - "enqueue_event_not_alive_w_data_copy_after_delete_during_merge", - Event_enqueue_event_not_alive_w_data_copy_after_delete_during_merge + "worker_iter_w_singleton", + Iter_worker_iter_w_singleton }, { - "enqueue_event_not_deferred", - Event_enqueue_event_not_deferred + "worker_iter_w_singleton_instanced", + Iter_worker_iter_w_singleton_instanced }, { - "enqueue_event_not_deferred_to_async", - Event_enqueue_event_not_deferred_to_async + "worker_iter_w_singleton_component_instanced", + Iter_worker_iter_w_singleton_component_instanced }, { - "enqueue_custom_implicit_any", - Event_enqueue_custom_implicit_any + "paged_iter_w_singleton", + Iter_paged_iter_w_singleton }, { - "enqueue_custom_after_large_cmd", - Event_enqueue_custom_after_large_cmd - } -}; - -bake_test_case New_testcases[] = { - { - "empty", - New_empty + "paged_iter_w_singleton_instanced", + Iter_paged_iter_w_singleton_instanced }, { - "component", - New_component + "paged_iter_w_singleton_component_instanced", + Iter_paged_iter_w_singleton_component_instanced }, { - "tag", - New_tag + "page_iter_w_offset_skip_1_archetype", + Iter_page_iter_w_offset_skip_1_archetype }, { - "redefine_component", - New_redefine_component + "page_iter_w_offset_skip_1_archetype_plus_one", + Iter_page_iter_w_offset_skip_1_archetype_plus_one }, { - "recycle_id_empty", - New_recycle_id_empty + "page_iter_w_offset_skip_2_archetypes", + Iter_page_iter_w_offset_skip_2_archetypes }, { - "recycle_id_w_entity", - New_recycle_id_w_entity + "page_iter_w_limit_skip_1_archetype", + Iter_page_iter_w_limit_skip_1_archetype }, { - "recycle_empty_staged_delete", - New_recycle_empty_staged_delete + "page_iter_w_limit_skip_1_archetype_minus_one", + Iter_page_iter_w_limit_skip_1_archetype_minus_one }, { - "recycle_staged_delete", - New_recycle_staged_delete + "page_iter_w_limit_skip_2_archetypes", + Iter_page_iter_w_limit_skip_2_archetypes }, { - "new_id", - New_new_id + "page_iter_w_offset_1_limit_max", + Iter_page_iter_w_offset_1_limit_max }, { - "new_component_id", - New_new_component_id + "page_iter_w_offset_1_limit_minus_1", + Iter_page_iter_w_offset_1_limit_minus_1 }, { - "new_hi_component_id", - New_new_hi_component_id + "page_iter_w_offset_2_type_limit_max", + Iter_page_iter_w_offset_2_type_limit_max }, { - "new_component_id_skip_used", - New_new_component_id_skip_used + "page_iter_w_offset_2_type_limit_minus_1", + Iter_page_iter_w_offset_2_type_limit_minus_1 }, { - "new_component_id_skip_to_hi_id", - New_new_component_id_skip_to_hi_id + "page_iter_w_limit_1_all_offsets", + Iter_page_iter_w_limit_1_all_offsets }, { - "new_w_entity_0", - New_new_w_entity_0 + "page_iter_w_offset_out_of_bounds", + Iter_page_iter_w_offset_out_of_bounds }, { - "create_w_explicit_id_2_worlds", - New_create_w_explicit_id_2_worlds + "page_iter_w_limit_out_of_bounds", + Iter_page_iter_w_limit_out_of_bounds }, { - "new_w_id_0_w_with", - New_new_w_id_0_w_with + "page_iter_comb_10_entities_1_type", + Iter_page_iter_comb_10_entities_1_type }, { - "new_w_id_w_with", - New_new_w_id_w_with + "page_iter_comb_10_entities_2_types", + Iter_page_iter_comb_10_entities_2_types }, { - "new_w_type_0_w_with", - New_new_w_type_0_w_with + "count", + Iter_count }, { - "new_w_type_w_with", - New_new_w_type_w_with + "iter_restore_stack_iter", + Iter_iter_restore_stack_iter }, { - "new_w_id_w_with_w_scope", - New_new_w_id_w_with_w_scope + "interleaved_iter", + Iter_interleaved_iter }, { - "new_w_type_w_with_w_scope", - New_new_w_type_w_with_w_scope + "get_first", + Iter_get_first }, { - "new_w_id_w_with_defer", - New_new_w_id_w_with_defer + "page_iter_w_only_tag", + Iter_page_iter_w_only_tag }, { - "new_w_id_w_with_defer_w_scope", - New_new_w_id_w_with_defer_w_scope + "worker_iter_w_only_tag", + Iter_worker_iter_w_only_tag }, { - "new_w_type_w_with_defer", - New_new_w_type_w_with_defer + "page_iter_w_inout_none", + Iter_page_iter_w_inout_none }, { - "new_w_type_w_with_defer_w_scope", - New_new_w_type_w_with_defer_w_scope - } -}; - -bake_test_case New_w_Count_testcases[] = { - { - "empty", - New_w_Count_empty + "worker_iter_w_inout_none", + Iter_worker_iter_w_inout_none }, { - "component", - New_w_Count_component + "page_iter_w_ctx", + Iter_page_iter_w_ctx }, { - "tag", - New_w_Count_tag + "page_iter_w_binding_ctx", + Iter_page_iter_w_binding_ctx }, { - "bulk_init_empty", - New_w_Count_bulk_init_empty + "worker_iter_w_ctx", + Iter_worker_iter_w_ctx }, { - "bulk_init_empty_w_entities", - New_w_Count_bulk_init_empty_w_entities + "worker_iter_w_binding_ctx", + Iter_worker_iter_w_binding_ctx }, { - "bulk_init_1_tag", - New_w_Count_bulk_init_1_tag + "column_index_owned", + Iter_column_index_owned }, { - "bulk_init_1_tag_w_entities", - New_w_Count_bulk_init_1_tag_w_entities + "column_index_shared", + Iter_column_index_shared }, { - "bulk_init_2_tags", - New_w_Count_bulk_init_2_tags + "column_index_not", + Iter_column_index_not }, { - "bulk_init_1_component", - New_w_Count_bulk_init_1_component + "page_iter_w_fini", + Iter_page_iter_w_fini }, { - "bulk_init_2_components", - New_w_Count_bulk_init_2_components + "worker_iter_w_fini", + Iter_worker_iter_w_fini }, { - "bulk_init_1_component_w_value", - New_w_Count_bulk_init_1_component_w_value + "rule_page_iter_w_fini", + Iter_rule_page_iter_w_fini }, { - "bulk_init_2_components_w_value", - New_w_Count_bulk_init_2_components_w_value + "rule_worker_iter_w_fini", + Iter_rule_worker_iter_w_fini }, { - "bulk_init_2_components_tag_w_value", - New_w_Count_bulk_init_2_components_tag_w_value + "to_str_before_next", + Iter_to_str_before_next }, { - "add_after_bulk", - New_w_Count_add_after_bulk - }, + "to_str", + Iter_to_str + } +}; + +bake_test_case Search_testcases[] = { { - "add_after_bulk_w_component", - New_w_Count_add_after_bulk_w_component + "search", + Search_search }, { - "add_after_bulk_w_ctor", - New_w_Count_add_after_bulk_w_ctor + "search_wildcard", + Search_search_wildcard }, { - "recycle_1_of_2", - New_w_Count_recycle_1_of_2 + "search_wildcard_w_offset", + Search_search_wildcard_w_offset }, { - "recycle_1_of_3", - New_w_Count_recycle_1_of_3 + "search_relation_wildcard_w_offset", + Search_search_relation_wildcard_w_offset }, { - "recycle_2_of_3", - New_w_Count_recycle_2_of_3 + "search_pair_w_any_rel", + Search_search_pair_w_any_rel }, { - "bulk_init_w_table", - New_w_Count_bulk_init_w_table - } -}; - -bake_test_case Add_testcases[] = { + "search_pair_w_any_obj", + Search_search_pair_w_any_obj + }, { - "zero", - Add_zero + "search_follow_relation_lvl_0", + Search_search_follow_relation_lvl_0 }, { - "component", - Add_component + "search_follow_relation_lvl_1", + Search_search_follow_relation_lvl_1 }, { - "component_again", - Add_component_again + "search_follow_relation_lvl_2", + Search_search_follow_relation_lvl_2 }, { - "2_components", - Add_2_components + "search_follow_relation_lvl_3", + Search_search_follow_relation_lvl_3 }, { - "2_components_again", - Add_2_components_again + "search_first_lvl_0", + Search_search_first_lvl_0 }, { - "2_components_overlap", - Add_2_components_overlap + "search_first_lvl_1", + Search_search_first_lvl_1 }, { - "component_to_nonempty", - Add_component_to_nonempty + "search_first_lvl_2", + Search_search_first_lvl_2 }, { - "component_to_nonempty_again", - Add_component_to_nonempty_again + "search_first_lvl_3", + Search_search_first_lvl_3 }, { - "component_to_nonempty_overlap", - Add_component_to_nonempty_overlap - }, - { - "tag", - Add_tag - }, - { - "add_entity", - Add_add_entity - }, - { - "remove_entity", - Add_remove_entity - }, - { - "add_0_entity", - Add_add_0_entity - }, - { - "remove_0_entity", - Add_remove_0_entity - }, - { - "invalid_add_wildcard", - Add_invalid_add_wildcard - }, - { - "invalid_add_pair_w_wildcard", - Add_invalid_add_pair_w_wildcard - }, - { - "invalid_add_pair_w_wildcard_rel", - Add_invalid_add_pair_w_wildcard_rel - }, - { - "invalid_add_pair_w_wildcard_obj", - Add_invalid_add_pair_w_wildcard_obj - }, - { - "invalid_add_any", - Add_invalid_add_any - }, - { - "invalid_add_pair_w_any", - Add_invalid_add_pair_w_any - }, - { - "invalid_add_pair_w_any_rel", - Add_invalid_add_pair_w_any_rel + "search_relation_wildcard", + Search_search_relation_wildcard }, { - "invalid_add_pair_w_any_obj", - Add_invalid_add_pair_w_any_obj + "search_relation_at_offset", + Search_search_relation_at_offset }, { - "invalid_pair_w_0", - Add_invalid_pair_w_0 + "search_relation_inherit_from_parent", + Search_search_relation_inherit_from_parent }, { - "invalid_pair_w_0_rel", - Add_invalid_pair_w_0_rel + "search_relation_dont_inherit", + Search_search_relation_dont_inherit }, { - "invalid_pair_w_0_obj", - Add_invalid_pair_w_0_obj + "search_relation_dont_inherit_from_parent", + Search_search_relation_dont_inherit_from_parent }, { - "add_random_id", - Add_add_random_id + "search_relation_exclusive", + Search_search_relation_exclusive } }; -bake_test_case Switch_testcases[] = { - { - "get_case_no_switch", - Switch_get_case_no_switch - }, - { - "get_case_set", - Switch_get_case_set - }, - { - "get_case_change", - Switch_get_case_change - }, - { - "remove_case", - Switch_remove_case - }, - { - "remove_last", - Switch_remove_last - }, - { - "delete_first", - Switch_delete_first - }, - { - "delete_last", - Switch_delete_last - }, - { - "delete_first_last", - Switch_delete_first_last - }, - { - "3_entities_same_case", - Switch_3_entities_same_case - }, - { - "2_entities_1_change_case", - Switch_2_entities_1_change_case - }, - { - "3_entities_change_case", - Switch_3_entities_change_case - }, - { - "query_switch", - Switch_query_switch - }, +bake_test_case Event_testcases[] = { { - "query_1_case_1_type", - Switch_query_1_case_1_type + "table_1_id_w_trigger", + Event_table_1_id_w_trigger }, { - "query_1_case_2_types", - Switch_query_1_case_2_types + "table_2_ids_w_trigger", + Event_table_2_ids_w_trigger }, { - "query_2_cases_1_type", - Switch_query_2_cases_1_type + "table_1_id_w_observer", + Event_table_1_id_w_observer }, { - "query_2_cases_2_types", - Switch_query_2_cases_2_types + "table_2_ids_w_observer", + Event_table_2_ids_w_observer }, { - "query_after_remove", - Switch_query_after_remove + "emit_event_for_empty_table", + Event_emit_event_for_empty_table }, { - "add_case_in_stage", - Switch_add_case_in_stage + "emit_table_event", + Event_emit_table_event }, { - "change_case_in_stage", - Switch_change_case_in_stage + "emit_staged_from_world", + Event_emit_staged_from_world }, { - "change_one_case_in_stage", - Switch_change_one_case_in_stage + "emit_staged_from_stage", + Event_emit_staged_from_stage }, { - "remove_switch_in_stage", - Switch_remove_switch_in_stage + "emit_staged_from_world_observer", + Event_emit_staged_from_world_observer }, { - "switch_no_match_for_case", - Switch_switch_no_match_for_case + "emit_staged_from_stage_observer", + Event_emit_staged_from_stage_observer }, { - "empty_entity_has_case", - Switch_empty_entity_has_case + "emit_for_entity", + Event_emit_for_entity }, { - "zero_entity_has_case", - Switch_zero_entity_has_case + "emit_custom_for_any", + Event_emit_custom_for_any }, { - "add_to_entity_w_switch", - Switch_add_to_entity_w_switch + "emit_custom_implicit_any", + Event_emit_custom_implicit_any }, { - "add_pair_to_entity_w_switch", - Switch_add_pair_to_entity_w_switch + "emit_custom_empty_type", + Event_emit_custom_empty_type }, { - "sort", - Switch_sort + "emit_w_param", + Event_emit_w_param }, { - "recycled_tags", - Switch_recycled_tags + "emit_w_param_multi_observer", + Event_emit_w_param_multi_observer }, { - "query_recycled_tags", - Switch_query_recycled_tags + "emit_w_const_param", + Event_emit_w_const_param }, { - "single_case", - Switch_single_case + "emit_nested", + Event_emit_nested }, { - "match_switch_on_base_instance", - Switch_match_switch_on_base_instance + "enqueue_event_1_id", + Event_enqueue_event_1_id }, { - "switch_w_bitset_query", - Switch_switch_w_bitset_query + "enqueue_event_2_ids", + Event_enqueue_event_2_ids }, { - "switch_w_bitset_query_inv", - Switch_switch_w_bitset_query_inv + "enqueue_event_w_data", + Event_enqueue_event_w_data }, { - "switch_w_bitset_query_2_elems", - Switch_switch_w_bitset_query_2_elems + "enqueue_event_w_data_move", + Event_enqueue_event_w_data_move }, { - "switch_w_bitset_query_2_elems_skip", - Switch_switch_w_bitset_query_2_elems_skip + "enqueue_event_w_data_copy", + Event_enqueue_event_w_data_copy }, { - "switch_w_bitset_query_elems_interleaved", - Switch_switch_w_bitset_query_elems_interleaved + "enqueue_event_w_const_data_no_copy", + Event_enqueue_event_w_const_data_no_copy }, { - "switch_w_bitset_query_elems_interleaved_2_types", - Switch_switch_w_bitset_query_elems_interleaved_2_types + "enqueue_event_not_alive", + Event_enqueue_event_not_alive }, { - "has_wildcard", - Switch_has_wildcard + "enqueue_event_not_alive_w_data_move", + Event_enqueue_event_not_alive_w_data_move }, { - "remove_wildcard", - Switch_remove_wildcard + "enqueue_event_not_alive_w_data_copy", + Event_enqueue_event_not_alive_w_data_copy }, { - "same_table_after_change", - Switch_same_table_after_change + "enqueue_event_not_alive_after_delete_during_merge", + Event_enqueue_event_not_alive_after_delete_during_merge }, { - "component_relation", - Switch_component_relation + "enqueue_event_not_alive_w_data_move_after_delete_during_merge", + Event_enqueue_event_not_alive_w_data_move_after_delete_during_merge }, { - "delete_case_trigger_after_delete_switch", - Switch_delete_case_trigger_after_delete_switch + "enqueue_event_not_alive_w_data_copy_after_delete_during_merge", + Event_enqueue_event_not_alive_w_data_copy_after_delete_during_merge }, { - "add_2", - Switch_add_2 + "enqueue_event_not_deferred", + Event_enqueue_event_not_deferred }, { - "add_2_reverse", - Switch_add_2_reverse + "enqueue_event_not_deferred_to_async", + Event_enqueue_event_not_deferred_to_async }, { - "add_switch_to_prefab_instance", - Switch_add_switch_to_prefab_instance + "enqueue_custom_implicit_any", + Event_enqueue_custom_implicit_any }, { - "get_case_w_generation", - Switch_get_case_w_generation + "enqueue_custom_after_large_cmd", + Event_enqueue_custom_after_large_cmd }, { - "get_case_w_generation_not_alive", - Switch_get_case_w_generation_not_alive + "enqueue_on_readonly_world", + Event_enqueue_on_readonly_world } }; -bake_test_case EnabledComponents_testcases[] = { - { - "is_component_enabled", - EnabledComponents_is_component_enabled - }, +bake_test_case New_testcases[] = { { - "is_empty_entity_disabled", - EnabledComponents_is_empty_entity_disabled + "empty", + New_empty }, { - "is_0_entity_disabled", - EnabledComponents_is_0_entity_disabled + "component", + New_component }, { - "is_0_component_disabled", - EnabledComponents_is_0_component_disabled + "tag", + New_tag }, { - "is_nonexist_component_disabled", - EnabledComponents_is_nonexist_component_disabled + "redefine_component", + New_redefine_component }, { - "is_enabled_component_enabled", - EnabledComponents_is_enabled_component_enabled + "recycle_id_empty", + New_recycle_id_empty }, { - "is_disabled_component_enabled", - EnabledComponents_is_disabled_component_enabled + "recycle_id_w_entity", + New_recycle_id_w_entity }, { - "has_enabled_component", - EnabledComponents_has_enabled_component + "recycle_empty_staged_delete", + New_recycle_empty_staged_delete }, { - "is_enabled_after_add", - EnabledComponents_is_enabled_after_add + "recycle_staged_delete", + New_recycle_staged_delete }, { - "is_enabled_after_remove", - EnabledComponents_is_enabled_after_remove + "new_id", + New_new_id }, { - "is_enabled_after_disable", - EnabledComponents_is_enabled_after_disable + "new_component_id", + New_new_component_id }, { - "is_disabled_after_enable", - EnabledComponents_is_disabled_after_enable + "new_hi_component_id", + New_new_hi_component_id }, { - "is_enabled_randomized", - EnabledComponents_is_enabled_randomized + "new_component_id_skip_used", + New_new_component_id_skip_used }, { - "is_enabled_after_add_randomized", - EnabledComponents_is_enabled_after_add_randomized + "new_component_id_skip_to_hi_id", + New_new_component_id_skip_to_hi_id }, { - "is_enabled_after_randomized_add_randomized", - EnabledComponents_is_enabled_after_randomized_add_randomized + "new_w_entity_0", + New_new_w_entity_0 }, { - "is_enabled_2", - EnabledComponents_is_enabled_2 + "create_w_explicit_id_2_worlds", + New_create_w_explicit_id_2_worlds }, { - "is_enabled_3", - EnabledComponents_is_enabled_3 + "new_w_id_0_w_with", + New_new_w_id_0_w_with }, { - "is_enabled_2_after_add", - EnabledComponents_is_enabled_2_after_add + "new_w_id_w_with", + New_new_w_id_w_with }, { - "is_enabled_3_after_add", - EnabledComponents_is_enabled_3_after_add + "new_w_type_w_with", + New_new_w_type_w_with }, { - "is_pair_enabled", - EnabledComponents_is_pair_enabled + "new_w_id_w_with_w_scope", + New_new_w_id_w_with_w_scope }, { - "is_enabled_pair_enabled", - EnabledComponents_is_enabled_pair_enabled + "new_w_type_w_with_w_scope", + New_new_w_type_w_with_w_scope }, { - "is_disabled_pair_enabled", - EnabledComponents_is_disabled_pair_enabled + "new_w_id_w_with_defer", + New_new_w_id_w_with_defer }, { - "has_enabled_pair", - EnabledComponents_has_enabled_pair + "new_w_id_w_with_defer_w_scope", + New_new_w_id_w_with_defer_w_scope }, { - "is_pair_enabled_after_add", - EnabledComponents_is_pair_enabled_after_add + "new_w_type_w_with_defer", + New_new_w_type_w_with_defer }, { - "is_pair_enabled_after_remove", - EnabledComponents_is_pair_enabled_after_remove + "new_w_type_w_with_defer_w_scope", + New_new_w_type_w_with_defer_w_scope }, { - "is_pair_enabled_after_disable", - EnabledComponents_is_pair_enabled_after_disable + "new_w_table", + New_new_w_table }, { - "is_pair_disabled_after_enable", - EnabledComponents_is_pair_disabled_after_enable + "new_w_null_table", + New_new_w_null_table }, { - "is_pair_enabled_2", - EnabledComponents_is_pair_enabled_2 + "new_w_table_component", + New_new_w_table_component }, { - "is_pair_enabled_3", - EnabledComponents_is_pair_enabled_3 + "new_w_table_sparse_component", + New_new_w_table_sparse_component }, { - "is_pair_enabled_2_after_add", - EnabledComponents_is_pair_enabled_2_after_add - }, + "new_w_table_override", + New_new_w_table_override + } +}; + +bake_test_case New_w_Count_testcases[] = { { - "is_pair_enabled_3_after_add", - EnabledComponents_is_pair_enabled_3_after_add + "empty", + New_w_Count_empty }, { - "query_disabled", - EnabledComponents_query_disabled + "component", + New_w_Count_component }, { - "query_disabled_skip_initial", - EnabledComponents_query_disabled_skip_initial + "tag", + New_w_Count_tag }, { - "query_disabled_pair", - EnabledComponents_query_disabled_pair + "bulk_init_empty", + New_w_Count_bulk_init_empty }, { - "query_disabled_pair_skip_initial", - EnabledComponents_query_disabled_pair_skip_initial + "bulk_init_empty_w_entities", + New_w_Count_bulk_init_empty_w_entities }, { - "query_mod_2", - EnabledComponents_query_mod_2 - }, - { - "query_mod_8", - EnabledComponents_query_mod_8 - }, - { - "query_mod_64", - EnabledComponents_query_mod_64 - }, - { - "query_mod_256", - EnabledComponents_query_mod_256 - }, - { - "query_mod_1024", - EnabledComponents_query_mod_1024 - }, - { - "query_enable_mod_10", - EnabledComponents_query_enable_mod_10 - }, - { - "query_mod_2_2_bitsets", - EnabledComponents_query_mod_2_2_bitsets - }, - { - "query_mod_8_2_bitsets", - EnabledComponents_query_mod_8_2_bitsets - }, - { - "query_mod_64_2_bitsets", - EnabledComponents_query_mod_64_2_bitsets - }, - { - "query_mod_256_2_bitsets", - EnabledComponents_query_mod_256_2_bitsets - }, - { - "query_mod_1024_2_bitsets", - EnabledComponents_query_mod_1024_2_bitsets - }, - { - "query_randomized_2_bitsets", - EnabledComponents_query_randomized_2_bitsets - }, - { - "query_randomized_3_bitsets", - EnabledComponents_query_randomized_3_bitsets - }, - { - "query_randomized_4_bitsets", - EnabledComponents_query_randomized_4_bitsets - }, - { - "defer_enable", - EnabledComponents_defer_enable - }, - { - "sort", - EnabledComponents_sort - }, - { - "table_move_2_from_3", - EnabledComponents_table_move_2_from_3 - } -}; - -bake_test_case Remove_testcases[] = { - { - "zero", - Remove_zero - }, - { - "1_of_1", - Remove_1_of_1 - }, - { - "1_of_2", - Remove_1_of_2 - }, - { - "2_of_2", - Remove_2_of_2 - }, - { - "2_of_3", - Remove_2_of_3 - }, - { - "1_of_1_again", - Remove_1_of_1_again - }, - { - "2_again", - Remove_2_again - }, - { - "2_overlap", - Remove_2_overlap - }, - { - "1_from_empty", - Remove_1_from_empty - }, - { - "not_added", - Remove_not_added - } -}; - -bake_test_case GlobalComponentIds_testcases[] = { - { - "declare", - GlobalComponentIds_declare - }, - { - "declare_w_entity", - GlobalComponentIds_declare_w_entity - }, - { - "declare_2_world", - GlobalComponentIds_declare_2_world - }, - { - "declare_tag", - GlobalComponentIds_declare_tag - }, - { - "declare_tag_w_entity", - GlobalComponentIds_declare_tag_w_entity - }, - { - "declare_entity", - GlobalComponentIds_declare_entity - }, - { - "reuse_300_component_ids", - GlobalComponentIds_reuse_300_component_ids - } -}; - -bake_test_case Hierarchies_testcases[] = { - { - "empty_scope", - Hierarchies_empty_scope - }, - { - "get_parent", - Hierarchies_get_parent - }, - { - "get_parent_from_nested", - Hierarchies_get_parent_from_nested - }, - { - "get_parent_from_nested_2", - Hierarchies_get_parent_from_nested_2 - }, - { - "get_object_from_0", - Hierarchies_get_object_from_0 - }, - { - "tree_iter_empty", - Hierarchies_tree_iter_empty - }, - { - "tree_iter_1_table", - Hierarchies_tree_iter_1_table - }, - { - "tree_iter_2_tables", - Hierarchies_tree_iter_2_tables - }, - { - "path_depth_0", - Hierarchies_path_depth_0 - }, - { - "path_depth_1", - Hierarchies_path_depth_1 - }, - { - "path_depth_2", - Hierarchies_path_depth_2 - }, - { - "path_core", - Hierarchies_path_core - }, - { - "path_core_w_prefix", - Hierarchies_path_core_w_prefix - }, - { - "path_core_w_empty_prefix", - Hierarchies_path_core_w_empty_prefix - }, - { - "path_this_w_empty_prefix", - Hierarchies_path_this_w_empty_prefix - }, - { - "path_wildcard_w_empty_prefix", - Hierarchies_path_wildcard_w_empty_prefix - }, - { - "path_any_w_empty_prefix", - Hierarchies_path_any_w_empty_prefix - }, - { - "rel_path_from_root", - Hierarchies_rel_path_from_root - }, - { - "rel_path_from_self", - Hierarchies_rel_path_from_self - }, - { - "rel_path_depth_1", - Hierarchies_rel_path_depth_1 - }, - { - "rel_path_depth_2", - Hierarchies_rel_path_depth_2 - }, - { - "rel_path_no_match", - Hierarchies_rel_path_no_match - }, - { - "path_custom_sep", - Hierarchies_path_custom_sep - }, - { - "path_custom_prefix", - Hierarchies_path_custom_prefix - }, - { - "path_prefix_rel_match", - Hierarchies_path_prefix_rel_match - }, - { - "path_prefix_rel_no_match", - Hierarchies_path_prefix_rel_no_match - }, - { - "fullpath_for_core", - Hierarchies_fullpath_for_core - }, - { - "path_w_number", - Hierarchies_path_w_number - }, - { - "lookup_depth_0", - Hierarchies_lookup_depth_0 - }, - { - "lookup_depth_1", - Hierarchies_lookup_depth_1 - }, - { - "lookup_depth_2", - Hierarchies_lookup_depth_2 - }, - { - "lookup_rel_0", - Hierarchies_lookup_rel_0 - }, - { - "lookup_rel_1", - Hierarchies_lookup_rel_1 - }, - { - "lookup_rel_2", - Hierarchies_lookup_rel_2 - }, - { - "lookup_custom_sep", - Hierarchies_lookup_custom_sep - }, - { - "lookup_custom_prefix", - Hierarchies_lookup_custom_prefix - }, - { - "lookup_custom_prefix_from_root", - Hierarchies_lookup_custom_prefix_from_root - }, - { - "lookup_self", - Hierarchies_lookup_self - }, - { - "lookup_in_parent_from_scope", - Hierarchies_lookup_in_parent_from_scope - }, - { - "lookup_in_root_from_scope", - Hierarchies_lookup_in_root_from_scope - }, - { - "lookup_number", - Hierarchies_lookup_number - }, - { - "delete_children", - Hierarchies_delete_children - }, - { - "scope_set", - Hierarchies_scope_set - }, - { - "scope_set_again", - Hierarchies_scope_set_again - }, - { - "scope_set_w_new", - Hierarchies_scope_set_w_new - }, - { - "scope_set_w_new_staged", - Hierarchies_scope_set_w_new_staged - }, - { - "scope_set_w_lookup", - Hierarchies_scope_set_w_lookup - }, - { - "scope_component", - Hierarchies_scope_component - }, - { - "scope_component_no_macro", - Hierarchies_scope_component_no_macro - }, - { - "new_from_path_depth_0", - Hierarchies_new_from_path_depth_0 - }, - { - "new_from_path_depth_1", - Hierarchies_new_from_path_depth_1 - }, - { - "new_from_path_depth_2", - Hierarchies_new_from_path_depth_2 - }, - { - "new_from_path_existing_depth_0", - Hierarchies_new_from_path_existing_depth_0 - }, - { - "new_from_path_existing_depth_1", - Hierarchies_new_from_path_existing_depth_1 - }, - { - "new_from_path_existing_depth_2", - Hierarchies_new_from_path_existing_depth_2 - }, - { - "add_path_depth_0", - Hierarchies_add_path_depth_0 - }, - { - "add_path_depth_1", - Hierarchies_add_path_depth_1 - }, - { - "add_path_depth_2", - Hierarchies_add_path_depth_2 - }, - { - "add_path_existing_depth_0", - Hierarchies_add_path_existing_depth_0 - }, - { - "add_path_existing_depth_1", - Hierarchies_add_path_existing_depth_1 - }, - { - "add_path_existing_depth_2", - Hierarchies_add_path_existing_depth_2 - }, - { - "add_path_from_scope", - Hierarchies_add_path_from_scope - }, - { - "add_path_from_scope_new_entity", - Hierarchies_add_path_from_scope_new_entity - }, - { - "add_root_path_to_child", - Hierarchies_add_root_path_to_child - }, - { - "add_parent_path_from_root_to_child", - Hierarchies_add_parent_path_from_root_to_child - }, - { - "new_w_child_in_root", - Hierarchies_new_w_child_in_root - }, - { - "delete_child", - Hierarchies_delete_child - }, - { - "delete_2_children", - Hierarchies_delete_2_children - }, - { - "delete_2_children_different_type", - Hierarchies_delete_2_children_different_type - }, - { - "delete_tree_2_levels", - Hierarchies_delete_tree_2_levels - }, - { - "delete_tree_3_levels", - Hierarchies_delete_tree_3_levels - }, - { - "delete_tree_count_tables", - Hierarchies_delete_tree_count_tables - }, - { - "delete_tree_staged", - Hierarchies_delete_tree_staged - }, - { - "delete_tree_empty_table", - Hierarchies_delete_tree_empty_table - }, - { - "delete_tree_recreate", - Hierarchies_delete_tree_recreate - }, - { - "delete_tree_w_onremove", - Hierarchies_delete_tree_w_onremove - }, - { - "delete_tree_w_dtor", - Hierarchies_delete_tree_w_dtor - }, - { - "get_child_count", - Hierarchies_get_child_count - }, - { - "get_child_count_2_tables", - Hierarchies_get_child_count_2_tables - }, - { - "get_child_count_no_children", - Hierarchies_get_child_count_no_children - }, - { - "scope_iter_after_delete_tree", - Hierarchies_scope_iter_after_delete_tree - }, - { - "add_child_after_delete_tree", - Hierarchies_add_child_after_delete_tree - }, - { - "add_child_to_recycled_parent", - Hierarchies_add_child_to_recycled_parent - }, - { - "get_type_after_recycled_parent_add", - Hierarchies_get_type_after_recycled_parent_add - }, - { - "rematch_after_add_to_recycled_parent", - Hierarchies_rematch_after_add_to_recycled_parent - }, - { - "cascade_after_recycled_parent_change", - Hierarchies_cascade_after_recycled_parent_change - }, - { - "long_name_depth_0", - Hierarchies_long_name_depth_0 - }, - { - "long_name_depth_1", - Hierarchies_long_name_depth_1 - }, - { - "long_name_depth_2", - Hierarchies_long_name_depth_2 - }, - { - "ensure_1_parent_after_adding_2", - Hierarchies_ensure_1_parent_after_adding_2 - }, - { - "ensure_child_alive_after_deleting_prev_parent", - Hierarchies_ensure_child_alive_after_deleting_prev_parent - }, - { - "lookup_after_root_to_parent_move", - Hierarchies_lookup_after_root_to_parent_move - }, - { - "lookup_after_parent_to_root_move", - Hierarchies_lookup_after_parent_to_root_move - }, - { - "lookup_after_parent_to_parent_move", - Hierarchies_lookup_after_parent_to_parent_move - }, - { - "lookup_after_clear_from_root", - Hierarchies_lookup_after_clear_from_root - }, - { - "lookup_after_clear_from_parent", - Hierarchies_lookup_after_clear_from_parent - }, - { - "lookup_after_delete_from_root", - Hierarchies_lookup_after_delete_from_root - }, - { - "lookup_after_delete_from_parent", - Hierarchies_lookup_after_delete_from_parent - }, - { - "defer_batch_remove_name_w_add_childof", - Hierarchies_defer_batch_remove_name_w_add_childof - }, - { - "defer_batch_remove_childof_w_add_name", - Hierarchies_defer_batch_remove_childof_w_add_name - } -}; - -bake_test_case FixedHierarchies_testcases[] = { - { - "make_fixed_1_lvl", - FixedHierarchies_make_fixed_1_lvl - }, - { - "make_fixed_1_lvl_w_init", - FixedHierarchies_make_fixed_1_lvl_w_init - }, - { - "make_fixed_1_lvl_w_init_comp", - FixedHierarchies_make_fixed_1_lvl_w_init_comp - }, - { - "make_fixed_1_lvl_w_init_comp_after_tree_fixed", - FixedHierarchies_make_fixed_1_lvl_w_init_comp_after_tree_fixed - }, - { - "make_fixed_1_lvl_2_entities", - FixedHierarchies_make_fixed_1_lvl_2_entities - }, - { - "make_fixed_1_lvl_2_tables", - FixedHierarchies_make_fixed_1_lvl_2_tables - }, - { - "make_fixed_2_lvl", - FixedHierarchies_make_fixed_2_lvl - }, - { - "make_fixed_2_lvl_2_tables", - FixedHierarchies_make_fixed_2_lvl_2_tables - }, - { - "make_fixed_3_lvl", - FixedHierarchies_make_fixed_3_lvl - }, - { - "make_fixed_3_lvl_w_name", - FixedHierarchies_make_fixed_3_lvl_w_name - }, - { - "make_fixed_3_2_lvl_w_name", - FixedHierarchies_make_fixed_3_2_lvl_w_name - }, - { - "make_fixed_2_lvl_nested", - FixedHierarchies_make_fixed_2_lvl_nested - }, - { - "make_fixed_3_lvl_nested", - FixedHierarchies_make_fixed_3_lvl_nested - }, - { - "make_fixed_1_lvl_after_delete", - FixedHierarchies_make_fixed_1_lvl_after_delete - }, - { - "get_target_1_lvl", - FixedHierarchies_get_target_1_lvl - }, - { - "get_target_2_lvl", - FixedHierarchies_get_target_2_lvl - }, - { - "get_depth_1_lvl", - FixedHierarchies_get_depth_1_lvl - }, - { - "get_depth_2_lvl", - FixedHierarchies_get_depth_2_lvl - }, - { - "get_depth_after_reparent_root", - FixedHierarchies_get_depth_after_reparent_root - }, - { - "delete_fixed_1_lvl", - FixedHierarchies_delete_fixed_1_lvl - }, - { - "delete_fixed_2_lvl", - FixedHierarchies_delete_fixed_2_lvl - }, - { - "delete_with_fixed_1_lvl", - FixedHierarchies_delete_with_fixed_1_lvl - }, - { - "delete_with_fixed_2_lvl", - FixedHierarchies_delete_with_fixed_2_lvl - }, - { - "query_w_parent_field_1_lvl", - FixedHierarchies_query_w_parent_field_1_lvl - }, - { - "query_w_parent_field_1_lvl_w_init", - FixedHierarchies_query_w_parent_field_1_lvl_w_init - }, - { - "query_w_parent_field_1_lvl_w_init_comp_after_tree_fixed", - FixedHierarchies_query_w_parent_field_1_lvl_w_init_comp_after_tree_fixed - }, - { - "query_w_parent_field_2_lvl", - FixedHierarchies_query_w_parent_field_2_lvl - }, - { - "query_w_parent_field_1_fixed_1_regular", - FixedHierarchies_query_w_parent_field_1_fixed_1_regular - }, - { - "query_w_parent_field_only_fixed_1_lvls", - FixedHierarchies_query_w_parent_field_only_fixed_1_lvls - }, - { - "query_w_parent_field_fixed_1_lvls_no_match", - FixedHierarchies_query_w_parent_field_fixed_1_lvls_no_match - }, - { - "query_w_parent_field_fixed_1_lvls_2_no_match", - FixedHierarchies_query_w_parent_field_fixed_1_lvls_2_no_match - }, - { - "query_w_parent_field_fixed_1_lvls_match_no_match", - FixedHierarchies_query_w_parent_field_fixed_1_lvls_match_no_match - }, - { - "query_w_parent_field_fixed_1_lvls_no_match_match", - FixedHierarchies_query_w_parent_field_fixed_1_lvls_no_match_match - }, - { - "query_w_parent_field_2_fixed_2_lvls", - FixedHierarchies_query_w_parent_field_2_fixed_2_lvls - }, - { - "query_w_cascade_field_2_lvl", - FixedHierarchies_query_w_cascade_field_2_lvl - }, - { - "query_next_table", - FixedHierarchies_query_next_table - }, - { - "query_next_table_1_elem", - FixedHierarchies_query_next_table_1_elem - }, - { - "query_next_table_1_elem_no_match", - FixedHierarchies_query_next_table_1_elem_no_match - }, - { - "query_nested_make_fixed", - FixedHierarchies_query_nested_make_fixed - }, - { - "query_nested_make_fixed_w_optional", - FixedHierarchies_query_nested_make_fixed_w_optional - }, - { - "query_nested_make_fixed_w_optional_match_children_only", - FixedHierarchies_query_nested_make_fixed_w_optional_match_children_only - }, - { - "query_nested_w_2_parents_make_fixed", - FixedHierarchies_query_nested_w_2_parents_make_fixed - }, - { - "query_table_w_3_parents", - FixedHierarchies_query_table_w_3_parents - }, - { - "query_w_parent_change_detection_1st", - FixedHierarchies_query_w_parent_change_detection_1st - }, - { - "query_w_parent_change_detection_2nd", - FixedHierarchies_query_w_parent_change_detection_2nd - }, - { - "query_w_parent_change_detection_iter_twice", - FixedHierarchies_query_w_parent_change_detection_iter_twice - }, - { - "query_w_parent_change_detection_iter_twice_each_parent", - FixedHierarchies_query_w_parent_change_detection_iter_twice_each_parent - }, - { - "query_w_parent_change_detection_1st_populate_when_changed", - FixedHierarchies_query_w_parent_change_detection_1st_populate_when_changed - }, - { - "query_w_parent_change_detection_2nd_populate_when_changed", - FixedHierarchies_query_w_parent_change_detection_2nd_populate_when_changed - }, - { - "query_w_parent_change_detection_iter_twice_populate_when_changed", - FixedHierarchies_query_w_parent_change_detection_iter_twice_populate_when_changed - }, - { - "query_w_parent_change_detection_iter_twice_each_parent_populate_when_changed", - FixedHierarchies_query_w_parent_change_detection_iter_twice_each_parent_populate_when_changed - }, - { - "staged_query_w_parent_field_1_lvl", - FixedHierarchies_staged_query_w_parent_field_1_lvl - }, - { - "staged_query_w_parent_field_2_lvl", - FixedHierarchies_staged_query_w_parent_field_2_lvl - }, - { - "staged_query_w_parent_field_1_fixed_1_regular", - FixedHierarchies_staged_query_w_parent_field_1_fixed_1_regular - }, - { - "staged_query_w_cascade_field_2_lvl", - FixedHierarchies_staged_query_w_cascade_field_2_lvl - }, - { - "add_to_fixed", - FixedHierarchies_add_to_fixed - }, - { - "remove_from_fixed", - FixedHierarchies_remove_from_fixed - }, - { - "delete_fixed", - FixedHierarchies_delete_fixed - }, - { - "clear_fixed", - FixedHierarchies_clear_fixed - }, - { - "make_fixed_1_lvl_w_name", - FixedHierarchies_make_fixed_1_lvl_w_name - }, - { - "make_fixed_2_lvl_w_name", - FixedHierarchies_make_fixed_2_lvl_w_name - }, - { - "make_fixed_1_lvl_w_name_keep_name", - FixedHierarchies_make_fixed_1_lvl_w_name_keep_name - }, - { - "make_fixed_2_lvl_w_name_keep_name", - FixedHierarchies_make_fixed_2_lvl_w_name_keep_name - }, - { - "make_fixed_2_lvl_lose_depth", - FixedHierarchies_make_fixed_2_lvl_lose_depth - }, - { - "make_fixed_3_lvl_lose_depth", - FixedHierarchies_make_fixed_3_lvl_lose_depth - } -}; - -bake_test_case Has_testcases[] = { - { - "zero", - Has_zero - }, - { - "1_of_0", - Has_1_of_0 - }, - { - "1_of_1", - Has_1_of_1 - }, - { - "1_of_2", - Has_1_of_2 - }, - { - "1_of_empty", - Has_1_of_empty - }, - { - "has_in_progress", - Has_has_in_progress - }, - { - "has_of_zero", - Has_has_of_zero - }, - { - "owns", - Has_owns - }, - { - "owns_wildcard", - Has_owns_wildcard - }, - { - "owns_wildcard_pair", - Has_owns_wildcard_pair - }, - { - "has_entity", - Has_has_entity - }, - { - "has_entity_0", - Has_has_entity_0 - }, - { - "has_entity_0_component", - Has_has_entity_0_component - }, - { - "has_entity_owned", - Has_has_entity_owned - }, - { - "has_entity_owned_0", - Has_has_entity_owned_0 - }, - { - "has_entity_owned_0_component", - Has_has_entity_owned_0_component - }, - { - "has_wildcard", - Has_has_wildcard - }, - { - "has_wildcard_pair", - Has_has_wildcard_pair - } -}; - -bake_test_case Count_testcases[] = { - { - "count_empty", - Count_count_empty - }, - { - "count_w_entity_0", - Count_count_w_entity_0 - }, - { - "count_1_component", - Count_count_1_component - }, - { - "count_disabled", - Count_count_disabled - }, - { - "count_prefab", - Count_count_prefab - } -}; - -bake_test_case Get_component_testcases[] = { - { - "get_empty", - Get_component_get_empty - }, - { - "get_1_from_1", - Get_component_get_1_from_1 - }, - { - "get_1_from_2", - Get_component_get_1_from_2 - }, - { - "get_2_from_2", - Get_component_get_2_from_2 - }, - { - "get_2_from_3", - Get_component_get_2_from_3 - }, - { - "get_1_from_2_in_progress_from_main_stage", - Get_component_get_1_from_2_in_progress_from_main_stage - }, - { - "get_1_from_2_add_in_progress", - Get_component_get_1_from_2_add_in_progress - }, - { - "get_both_from_2_add_in_progress", - Get_component_get_both_from_2_add_in_progress - }, - { - "get_both_from_2_add_remove_in_progress", - Get_component_get_both_from_2_add_remove_in_progress - }, - { - "get_childof_component", - Get_component_get_childof_component - }, - { - "get_mut_equal_get", - Get_component_get_mut_equal_get - }, - { - "get_tag", - Get_component_get_tag - }, - { - "get_pair_tag", - Get_component_get_pair_tag - }, - { - "get_wildcard", - Get_component_get_wildcard - } -}; - -bake_test_case Reference_testcases[] = { - { - "get_ref", - Reference_get_ref - }, - { - "get_ref_after_add", - Reference_get_ref_after_add - }, - { - "get_ref_after_remove", - Reference_get_ref_after_remove - }, - { - "get_ref_after_delete", - Reference_get_ref_after_delete - }, - { - "get_ref_after_realloc", - Reference_get_ref_after_realloc - }, - { - "get_ref_after_realloc_w_lifecycle", - Reference_get_ref_after_realloc_w_lifecycle - }, - { - "get_ref_staged", - Reference_get_ref_staged - }, - { - "get_ref_after_new_in_stage", - Reference_get_ref_after_new_in_stage - }, - { - "get_ref_monitored", - Reference_get_ref_monitored - }, - { - "get_ref_w_low_id_tag", - Reference_get_ref_w_low_id_tag - }, - { - "get_ref_w_low_id_tag_after_add", - Reference_get_ref_w_low_id_tag_after_add - }, - { - "get_nonexisting", - Reference_get_nonexisting - } -}; - -bake_test_case Delete_testcases[] = { - { - "delete_1", - Delete_delete_1 - }, - { - "delete_1_again", - Delete_delete_1_again - }, - { - "delete_recycled_tag_again", - Delete_delete_recycled_tag_again - }, - { - "delete_empty", - Delete_delete_empty - }, - { - "delete_nonexist", - Delete_delete_nonexist - }, - { - "delete_1st_of_3", - Delete_delete_1st_of_3 - }, - { - "delete_2nd_of_3", - Delete_delete_2nd_of_3 - }, - { - "delete_3rd_of_3", - Delete_delete_3rd_of_3 - }, - { - "delete_2_of_3", - Delete_delete_2_of_3 - }, - { - "delete_3_of_3", - Delete_delete_3_of_3 - }, - { - "delete_w_on_remove", - Delete_delete_w_on_remove - }, - { - "clear_1_component", - Delete_clear_1_component - }, - { - "clear_2_components", - Delete_clear_2_components - }, - { - "alive_after_delete", - Delete_alive_after_delete - }, - { - "alive_after_clear", - Delete_alive_after_clear - }, - { - "alive_after_staged_delete", - Delete_alive_after_staged_delete - }, - { - "alive_while_staged", - Delete_alive_while_staged - }, - { - "alive_while_staged_w_delete", - Delete_alive_while_staged_w_delete - }, - { - "alive_while_staged_w_delete_recycled_id", - Delete_alive_while_staged_w_delete_recycled_id - }, - { - "alive_after_recycle", - Delete_alive_after_recycle - }, - { - "delete_recycled", - Delete_delete_recycled - }, - { - "get_alive_for_alive", - Delete_get_alive_for_alive - }, - { - "get_alive_for_recycled", - Delete_get_alive_for_recycled - }, - { - "get_alive_for_not_alive", - Delete_get_alive_for_not_alive - }, - { - "get_alive_w_generation_for_recycled_alive", - Delete_get_alive_w_generation_for_recycled_alive - }, - { - "get_alive_w_generation_for_recycled_not_alive", - Delete_get_alive_w_generation_for_recycled_not_alive - }, - { - "get_alive_for_0", - Delete_get_alive_for_0 - }, - { - "get_alive_for_nonexistent", - Delete_get_alive_for_nonexistent - }, - { - "move_w_dtor_move", - Delete_move_w_dtor_move - }, - { - "move_w_dtor_no_move", - Delete_move_w_dtor_no_move - }, - { - "move_w_no_dtor_move", - Delete_move_w_no_dtor_move - }, - { - "wrap_generation_count", - Delete_wrap_generation_count - } -}; - -bake_test_case OnDelete_testcases[] = { - { - "flags", - OnDelete_flags - }, - { - "id_default", - OnDelete_id_default - }, - { - "id_remove", - OnDelete_id_remove - }, - { - "id_delete", - OnDelete_id_delete - }, - { - "relation_default", - OnDelete_relation_default - }, - { - "relation_remove", - OnDelete_relation_remove - }, - { - "relation_delete", - OnDelete_relation_delete - }, - { - "object_default", - OnDelete_object_default - }, - { - "object_remove", - OnDelete_object_remove - }, - { - "object_delete", - OnDelete_object_delete - }, - { - "id_throw", - OnDelete_id_throw - }, - { - "relation_throw", - OnDelete_relation_throw - }, - { - "object_throw", - OnDelete_object_throw - }, - { - "object_mixed", - OnDelete_object_mixed - }, - { - "id_remove_no_instances", - OnDelete_id_remove_no_instances - }, - { - "id_delete_no_instances", - OnDelete_id_delete_no_instances - }, - { - "id_throw_no_instances", - OnDelete_id_throw_no_instances - }, - { - "cyclic_self", - OnDelete_cyclic_self - }, - { - "nonempty_cyclic_self", - OnDelete_nonempty_cyclic_self - }, - { - "cyclic_id_default", - OnDelete_cyclic_id_default - }, - { - "cyclic_id_remove", - OnDelete_cyclic_id_remove - }, - { - "cyclic_id_remove_both", - OnDelete_cyclic_id_remove_both - }, - { - "cyclic_id_delete", - OnDelete_cyclic_id_delete - }, - { - "cyclic_id_delete_both", - OnDelete_cyclic_id_delete_both - }, - { - "cyclic_relation_default", - OnDelete_cyclic_relation_default - }, - { - "cyclic_relation_remove", - OnDelete_cyclic_relation_remove - }, - { - "cyclic_relation_remove_both", - OnDelete_cyclic_relation_remove_both - }, - { - "cyclic_relation_delete", - OnDelete_cyclic_relation_delete - }, - { - "cyclic_relation_delete_both", - OnDelete_cyclic_relation_delete_both - }, - { - "cyclic_object_default", - OnDelete_cyclic_object_default - }, - { - "cyclic_object_remove", - OnDelete_cyclic_object_remove - }, - { - "cyclic_object_delete", - OnDelete_cyclic_object_delete - }, - { - "cyclic_overlapping_table", - OnDelete_cyclic_overlapping_table - }, - { - "cyclic_overlapping_new_tables", - OnDelete_cyclic_overlapping_new_tables - }, - { - "cyclic_object_mixed", - OnDelete_cyclic_object_mixed - }, - { - "cyclic_storage_table", - OnDelete_cyclic_storage_table - }, - { - "cyclic_storage_table_2", - OnDelete_cyclic_storage_table_2 - }, - { - "cyclic_storage_table_3", - OnDelete_cyclic_storage_table_3 - }, - { - "cyclic_set_empty", - OnDelete_cyclic_set_empty - }, - { - "2_acyclic_relations_w_cycle", - OnDelete_2_acyclic_relations_w_cycle - }, - { - "remove_2_comps", - OnDelete_remove_2_comps - }, - { - "remove_2_comps_to_existing_table", - OnDelete_remove_2_comps_to_existing_table - }, - { - "delete_recursive", - OnDelete_delete_recursive - }, - { - "component_throw", - OnDelete_component_throw - }, - { - "remove_2_relations", - OnDelete_remove_2_relations - }, - { - "remove_object_w_2_relations", - OnDelete_remove_object_w_2_relations - }, - { - "remove_object_w_5_relations", - OnDelete_remove_object_w_5_relations - }, - { - "remove_object_w_50_relations", - OnDelete_remove_object_w_50_relations - }, - { - "remove_object_w_50_relations_3_tables", - OnDelete_remove_object_w_50_relations_3_tables - }, - { - "remove_object_w_3_relations_interleaved", - OnDelete_remove_object_w_3_relations_interleaved - }, - { - "remove_id_from_2_tables", - OnDelete_remove_id_from_2_tables - }, - { - "remove_relation_from_2_tables", - OnDelete_remove_relation_from_2_tables - }, - { - "remove_object_from_2_tables", - OnDelete_remove_object_from_2_tables - }, - { - "remove_id_and_relation", - OnDelete_remove_id_and_relation - }, - { - "remove_id_and_relation_from_2_tables", - OnDelete_remove_id_and_relation_from_2_tables - }, - { - "stresstest_many_objects", - OnDelete_stresstest_many_objects - }, - { - "stresstest_many_relations", - OnDelete_stresstest_many_relations - }, - { - "stresstest_many_objects_on_delete", - OnDelete_stresstest_many_objects_on_delete - }, - { - "stresstest_many_relations_on_delete", - OnDelete_stresstest_many_relations_on_delete - }, - { - "empty_table_w_on_remove", - OnDelete_empty_table_w_on_remove - }, - { - "delete_table_in_on_remove_during_fini", - OnDelete_delete_table_in_on_remove_during_fini - }, - { - "delete_other_in_on_remove_during_fini", - OnDelete_delete_other_in_on_remove_during_fini - }, - { - "remove_id_w_role", - OnDelete_remove_id_w_role - }, - { - "remove_rel_w_override_pair", - OnDelete_remove_rel_w_override_pair - }, - { - "remove_obj_w_override_pair", - OnDelete_remove_obj_w_override_pair - }, - { - "remove_rel_w_override_pair_after_on_delete_target", - OnDelete_remove_rel_w_override_pair_after_on_delete_target - }, - { - "remove_rel_w_override_pair_2_ids", - OnDelete_remove_rel_w_override_pair_2_ids - }, - { - "remove_obj_w_override_pair_2_ids", - OnDelete_remove_obj_w_override_pair_2_ids - }, - { - "remove_obj_w_override_pair_3_ids", - OnDelete_remove_obj_w_override_pair_3_ids - }, - { - "remove_mixed_w_override_pair_3_ids", - OnDelete_remove_mixed_w_override_pair_3_ids - }, - { - "merge_pair_component", - OnDelete_merge_pair_component - }, - { - "delete_with_tag", - OnDelete_delete_with_tag - }, - { - "delete_with_component", - OnDelete_delete_with_component - }, - { - "delete_with_pair", - OnDelete_delete_with_pair - }, - { - "delete_with_object_wildcard", - OnDelete_delete_with_object_wildcard - }, - { - "delete_with_relation_wildcard", - OnDelete_delete_with_relation_wildcard - }, - { - "delete_with_component_after_delete_cyclic_self", - OnDelete_delete_with_component_after_delete_cyclic_self - }, - { - "delete_with_component_after_delete_cyclic", - OnDelete_delete_with_component_after_delete_cyclic - }, - { - "delete_with_component_after_delete_cyclic_w_alive_moved", - OnDelete_delete_with_component_after_delete_cyclic_w_alive_moved - }, - { - "delete_all_with_entity", - OnDelete_delete_all_with_entity - }, - { - "remove_childof_entity", - OnDelete_remove_childof_entity - }, - { - "remove_childof_wildcard", - OnDelete_remove_childof_wildcard - }, - { - "delete_child_of_delete_with", - OnDelete_delete_child_of_delete_with - }, - { - "deep_clean_64", - OnDelete_deep_clean_64 - }, - { - "deep_clean_256", - OnDelete_deep_clean_256 - }, - { - "id_w_switch", - OnDelete_id_w_switch - }, - { - "id_w_disabled", - OnDelete_id_w_disabled - }, - { - "id_to_no_switch", - OnDelete_id_to_no_switch - }, - { - "id_to_no_disabled", - OnDelete_id_to_no_disabled - }, - { - "remove_on_delete_action", - OnDelete_remove_on_delete_action - }, - { - "delete_with_w_relation", - OnDelete_delete_with_w_relation - }, - { - "delete_self_in_on_remove", - OnDelete_delete_self_in_on_remove - }, - { - "delete_nested_in_on_remove", - OnDelete_delete_nested_in_on_remove - }, - { - "add_deleted_in_on_remove", - OnDelete_add_deleted_in_on_remove - }, - { - "delete_tree_w_query", - OnDelete_delete_tree_w_query - }, - { - "fini_cleanup_order", - OnDelete_fini_cleanup_order - }, - { - "fini_cleanup_order_root_id_w_trait", - OnDelete_fini_cleanup_order_root_id_w_trait - }, - { - "fini_cleanup_order_entity_after_singleton", - OnDelete_fini_cleanup_order_entity_after_singleton - }, - { - "fini_cleanup_order_entity_after_component", - OnDelete_fini_cleanup_order_entity_after_component - }, - { - "on_delete_parent_w_in_use_id_w_remove", - OnDelete_on_delete_parent_w_in_use_id_w_remove - }, - { - "on_delete_parent_w_in_use_id_w_delete", - OnDelete_on_delete_parent_w_in_use_id_w_delete - }, - { - "create_after_delete_with", - OnDelete_create_after_delete_with - }, - { - "delete_with_inherited_tag", - OnDelete_delete_with_inherited_tag - }, - { - "delete_with_inherited_tag_w_query", - OnDelete_delete_with_inherited_tag_w_query - }, - { - "delete_with_inherited_tag_w_observer", - OnDelete_delete_with_inherited_tag_w_observer - }, - { - "delete_symmetric_relation", - OnDelete_delete_symmetric_relation - }, - { - "delete_observed_symmetric_relation", - OnDelete_delete_observed_symmetric_relation - }, - { - "nested_delete_with", - OnDelete_nested_delete_with - }, - { - "deferred_delete_with_after_create_named", - OnDelete_deferred_delete_with_after_create_named - }, - { - "deferred_delete_with_childof_after_create_named", - OnDelete_deferred_delete_with_childof_after_create_named - }, - { - "match_marked_for_deletion", - OnDelete_match_marked_for_deletion - }, - { - "delete_w_low_rel_mixed_cleanup", - OnDelete_delete_w_low_rel_mixed_cleanup - }, - { - "delete_w_low_rel_mixed_cleanup_interleaved_ids", - OnDelete_delete_w_low_rel_mixed_cleanup_interleaved_ids - }, - { - "fini_query_w_singleton_in_scope_no_module", - OnDelete_fini_query_w_singleton_in_scope_no_module - }, - { - "fini_query_w_singleton_in_module", - OnDelete_fini_query_w_singleton_in_module - } -}; - -bake_test_case Set_testcases[] = { - { - "set_empty", - Set_set_empty - }, - { - "set_nonempty", - Set_set_nonempty - }, - { - "set_non_empty_override", - Set_set_non_empty_override - }, - { - "set_again", - Set_set_again - }, - { - "set_2", - Set_set_2 - }, - { - "add_set", - Set_add_set - }, - { - "set_add", - Set_set_add - }, - { - "set_add_other", - Set_set_add_other - }, - { - "set_remove", - Set_set_remove - }, - { - "set_remove_other", - Set_set_remove_other - }, - { - "set_remove_twice", - Set_set_remove_twice - }, - { - "set_and_new", - Set_set_and_new - }, - { - "set_null", - Set_set_null - }, - { - "get_mut_new", - Set_get_mut_new - }, - { - "get_mut_existing", - Set_get_mut_existing - }, - { - "get_mut_tag_new", - Set_get_mut_tag_new - }, - { - "get_mut_tag_existing", - Set_get_mut_tag_existing - }, - { - "get_mut_tag_new_w_comp", - Set_get_mut_tag_new_w_comp - }, - { - "get_mut_tag_existing_w_comp", - Set_get_mut_tag_existing_w_comp - }, - { - "get_mut_tag_new_w_pair", - Set_get_mut_tag_new_w_pair - }, - { - "get_mut_tag_existing_w_pair", - Set_get_mut_tag_existing_w_pair - }, - { - "modified_w_on_set", - Set_modified_w_on_set - }, - { - "modified_no_component", - Set_modified_no_component - }, - { - "get_mut_w_add_in_on_add", - Set_get_mut_w_add_in_on_add - }, - { - "get_mut_w_remove_in_on_add", - Set_get_mut_w_remove_in_on_add - }, - { - "get_mut_w_realloc_in_on_add", - Set_get_mut_w_realloc_in_on_add - }, - { - "emplace", - Set_emplace - }, - { - "emplace_2", - Set_emplace_2 - }, - { - "emplace_existing", - Set_emplace_existing - }, - { - "emplace_w_move", - Set_emplace_w_move - }, - { - "emplace_w_observer_w_add", - Set_emplace_w_observer_w_add - } -}; - -bake_test_case ReadWrite_testcases[] = { - { - "read", - ReadWrite_read - }, - { - "nested_read", - ReadWrite_nested_read - }, - { - "write", - ReadWrite_write - }, - { - "nested_write", - ReadWrite_nested_write - }, - { - "add_while_read", - ReadWrite_add_while_read - }, - { - "add_while_write", - ReadWrite_add_while_write - }, - { - "read_from_stage", - ReadWrite_read_from_stage - }, - { - "write_from_stage", - ReadWrite_write_from_stage - } -}; - -bake_test_case Lookup_testcases[] = { - { - "lookup", - Lookup_lookup - }, - { - "lookup_component", - Lookup_lookup_component - }, - { - "lookup_not_found", - Lookup_lookup_not_found - }, - { - "lookup_child", - Lookup_lookup_child - }, - { - "lookup_w_null_name", - Lookup_lookup_w_null_name - }, - { - "lookup_after_name_reset", - Lookup_lookup_after_name_reset - }, - { - "get_name", - Lookup_get_name - }, - { - "get_name_no_name", - Lookup_get_name_no_name - }, - { - "get_name_from_empty", - Lookup_get_name_from_empty - }, - { - "lookup_by_id", - Lookup_lookup_by_id - }, - { - "lookup_recycled_by_id", - Lookup_lookup_recycled_by_id - }, - { - "lookup_symbol_by_id", - Lookup_lookup_symbol_by_id - }, - { - "lookup_name_w_digit", - Lookup_lookup_name_w_digit - }, - { - "lookup_symbol_w_digit", - Lookup_lookup_symbol_w_digit - }, - { - "lookup_path_w_digit", - Lookup_lookup_path_w_digit - }, - { - "set_name_of_existing", - Lookup_set_name_of_existing - }, - { - "change_name_of_existing", - Lookup_change_name_of_existing - }, - { - "lookup_alias", - Lookup_lookup_alias - }, - { - "lookup_scoped_alias", - Lookup_lookup_scoped_alias - }, - { - "define_duplicate_alias", - Lookup_define_duplicate_alias - }, - { - "lookup_null", - Lookup_lookup_null - }, - { - "lookup_symbol_null", - Lookup_lookup_symbol_null - }, - { - "lookup_this", - Lookup_lookup_this - }, - { - "lookup_wildcard", - Lookup_lookup_wildcard - }, - { - "lookup_any", - Lookup_lookup_any - }, - { - "lookup_variable", - Lookup_lookup_variable - }, - { - "lookup_path_this", - Lookup_lookup_path_this - }, - { - "lookup_path_wildcard", - Lookup_lookup_path_wildcard - }, - { - "lookup_path_this_from_scope", - Lookup_lookup_path_this_from_scope - }, - { - "lookup_path_wildcard_from_scope", - Lookup_lookup_path_wildcard_from_scope - }, - { - "resolve_builtin_symbols", - Lookup_resolve_builtin_symbols - }, - { - "lookup_from_scope_staged", - Lookup_lookup_from_scope_staged - }, - { - "lookup_core", - Lookup_lookup_core - }, - { - "lookup_core_from_stage", - Lookup_lookup_core_from_stage - }, - { - "lookup_custom_search_path", - Lookup_lookup_custom_search_path - }, - { - "lookup_custom_search_path_from_stage", - Lookup_lookup_custom_search_path_from_stage - }, - { - "lookup_custom_search_path_n_elems", - Lookup_lookup_custom_search_path_n_elems - }, - { - "set_same_name", - Lookup_set_same_name - }, - { - "set_same_name_after_reparenting", - Lookup_set_same_name_after_reparenting - }, - { - "defer_set_name", - Lookup_defer_set_name - }, - { - "defer_set_same_name", - Lookup_defer_set_same_name - }, - { - "lookup_invalid_digit", - Lookup_lookup_invalid_digit - }, - { - "lookup_child_invalid_digit", - Lookup_lookup_child_invalid_digit - }, - { - "lookup_digit_from_wrong_scope", - Lookup_lookup_digit_from_wrong_scope - }, - { - "lookup_core_entity_from_wrong_scope", - Lookup_lookup_core_entity_from_wrong_scope - }, - { - "lookup_alias_w_number", - Lookup_lookup_alias_w_number - } -}; - -bake_test_case Singleton_testcases[] = { - { - "add_singleton", - Singleton_add_singleton - }, - { - "remove_singleton", - Singleton_remove_singleton - }, - { - "set_get_singleton", - Singleton_set_get_singleton - }, - { - "get_mut_singleton", - Singleton_get_mut_singleton - }, - { - "singleton_system", - Singleton_singleton_system - } -}; - -bake_test_case Clone_testcases[] = { - { - "empty", - Clone_empty - }, - { - "empty_w_value", - Clone_empty_w_value - }, - { - "null", - Clone_null - }, - { - "null_w_value", - Clone_null_w_value - }, - { - "1_component", - Clone_1_component - }, - { - "2_component", - Clone_2_component - }, - { - "1_component_w_value", - Clone_1_component_w_value - }, - { - "2_component_w_value", - Clone_2_component_w_value - }, - { - "3_component", - Clone_3_component - }, - { - "3_component_w_value", - Clone_3_component_w_value - }, - { - "tag", - Clone_tag - }, - { - "tag_w_value", - Clone_tag_w_value - }, - { - "1_tag_1_component", - Clone_1_tag_1_component - }, - { - "1_tag_1_component_w_value", - Clone_1_tag_1_component_w_value - }, - { - "clone_w_name", - Clone_clone_w_name - } -}; - -bake_test_case ComponentLifecycle_testcases[] = { - { - "ctor_on_add", - ComponentLifecycle_ctor_on_add - }, - { - "ctor_on_new", - ComponentLifecycle_ctor_on_new - }, - { - "dtor_on_remove", - ComponentLifecycle_dtor_on_remove - }, - { - "dtor_on_delete", - ComponentLifecycle_dtor_on_delete - }, - { - "copy_on_set", - ComponentLifecycle_copy_on_set - }, - { - "copy_on_override", - ComponentLifecycle_copy_on_override - }, - { - "copy_on_clone", - ComponentLifecycle_copy_on_clone - }, - { - "no_copy_on_move", - ComponentLifecycle_no_copy_on_move - }, - { - "ctor_copy_on_snapshot", - ComponentLifecycle_ctor_copy_on_snapshot - }, - { - "copy_on_snapshot", - ComponentLifecycle_copy_on_snapshot - }, - { - "dtor_on_restore", - ComponentLifecycle_dtor_on_restore - }, - { - "ctor_on_tag", - ComponentLifecycle_ctor_on_tag - }, - { - "dtor_on_tag", - ComponentLifecycle_dtor_on_tag - }, - { - "copy_on_tag", - ComponentLifecycle_copy_on_tag - }, - { - "move_on_tag", - ComponentLifecycle_move_on_tag - }, - { - "merge_to_different_table", - ComponentLifecycle_merge_to_different_table - }, - { - "merge_to_new_table", - ComponentLifecycle_merge_to_new_table - }, - { - "delete_in_stage", - ComponentLifecycle_delete_in_stage - }, - { - "ctor_on_add_pair", - ComponentLifecycle_ctor_on_add_pair - }, - { - "ctor_on_add_pair_tag", - ComponentLifecycle_ctor_on_add_pair_tag - }, - { - "ctor_on_move_pair", - ComponentLifecycle_ctor_on_move_pair - }, - { - "move_on_realloc", - ComponentLifecycle_move_on_realloc - }, - { - "move_on_bulk_new", - ComponentLifecycle_move_on_bulk_new - }, - { - "on_add_on_bulk_new", - ComponentLifecycle_on_add_on_bulk_new - }, - { - "move_on_delete", - ComponentLifecycle_move_on_delete - }, - { - "move_dtor_on_delete", - ComponentLifecycle_move_dtor_on_delete - }, - { - "copy_on_override_pair", - ComponentLifecycle_copy_on_override_pair - }, - { - "copy_on_override_pair_tag", - ComponentLifecycle_copy_on_override_pair_tag - }, - { - "copy_on_set_pair", - ComponentLifecycle_copy_on_set_pair - }, - { - "copy_on_set_pair_tag", - ComponentLifecycle_copy_on_set_pair_tag - }, - { - "allow_lifecycle_overwrite_equal_callbacks", - ComponentLifecycle_allow_lifecycle_overwrite_equal_callbacks - }, - { - "set_lifecycle_after_trigger", - ComponentLifecycle_set_lifecycle_after_trigger + "bulk_init_1_tag", + New_w_Count_bulk_init_1_tag }, { - "valid_entity_in_dtor_after_delete", - ComponentLifecycle_valid_entity_in_dtor_after_delete + "bulk_init_1_tag_w_entities", + New_w_Count_bulk_init_1_tag_w_entities }, { - "ctor_w_emplace", - ComponentLifecycle_ctor_w_emplace + "bulk_init_2_tags", + New_w_Count_bulk_init_2_tags }, { - "ctor_w_emplace_defer", - ComponentLifecycle_ctor_w_emplace_defer + "bulk_init_1_component", + New_w_Count_bulk_init_1_component }, { - "ctor_w_emplace_defer_use_move_ctor", - ComponentLifecycle_ctor_w_emplace_defer_use_move_ctor + "bulk_init_2_components", + New_w_Count_bulk_init_2_components }, { - "on_add_w_emplace", - ComponentLifecycle_on_add_w_emplace + "bulk_init_1_component_w_value", + New_w_Count_bulk_init_1_component_w_value }, { - "on_add_w_emplace_existing", - ComponentLifecycle_on_add_w_emplace_existing + "bulk_init_2_components_w_value", + New_w_Count_bulk_init_2_components_w_value }, { - "on_add_w_emplace_defer", - ComponentLifecycle_on_add_w_emplace_defer + "bulk_init_2_components_tag_w_value", + New_w_Count_bulk_init_2_components_tag_w_value }, { - "merge_async_stage_w_emplace", - ComponentLifecycle_merge_async_stage_w_emplace + "add_after_bulk", + New_w_Count_add_after_bulk }, { - "merge_async_stage_w_emplace_to_deferred_world", - ComponentLifecycle_merge_async_stage_w_emplace_to_deferred_world + "add_after_bulk_w_component", + New_w_Count_add_after_bulk_w_component }, { - "emplace_grow_w_existing_component", - ComponentLifecycle_emplace_grow_w_existing_component + "add_after_bulk_w_ctor", + New_w_Count_add_after_bulk_w_ctor }, { - "dtor_on_fini", - ComponentLifecycle_dtor_on_fini + "recycle_1_of_2", + New_w_Count_recycle_1_of_2 }, { - "valid_type_in_dtor_on_fini", - ComponentLifecycle_valid_type_in_dtor_on_fini + "recycle_1_of_3", + New_w_Count_recycle_1_of_3 }, { - "valid_other_type_of_entity_in_dtor_on_fini", - ComponentLifecycle_valid_other_type_of_entity_in_dtor_on_fini + "recycle_2_of_3", + New_w_Count_recycle_2_of_3 }, { - "delete_in_dtor_other_type_on_fini", - ComponentLifecycle_delete_in_dtor_other_type_on_fini - }, + "bulk_init_w_table", + New_w_Count_bulk_init_w_table + } +}; + +bake_test_case Add_testcases[] = { { - "delete_in_dtor_other_type_on_delete_parent", - ComponentLifecycle_delete_in_dtor_other_type_on_delete_parent + "zero", + Add_zero }, { - "delete_in_dtor_other_type_on_delete", - ComponentLifecycle_delete_in_dtor_other_type_on_delete + "component", + Add_component }, { - "delete_self_in_dtor_on_delete", - ComponentLifecycle_delete_self_in_dtor_on_delete + "component_again", + Add_component_again }, { - "on_set_after_set", - ComponentLifecycle_on_set_after_set + "2_components", + Add_2_components }, { - "on_add_after_new", - ComponentLifecycle_on_add_after_new + "2_components_again", + Add_2_components_again }, { - "on_add_after_add", - ComponentLifecycle_on_add_after_add + "2_components_overlap", + Add_2_components_overlap }, { - "on_add_after_set", - ComponentLifecycle_on_add_after_set + "component_to_nonempty", + Add_component_to_nonempty }, { - "on_remove_after_remove", - ComponentLifecycle_on_remove_after_remove + "component_to_nonempty_again", + Add_component_to_nonempty_again }, { - "on_remove_after_clear", - ComponentLifecycle_on_remove_after_clear + "component_to_nonempty_overlap", + Add_component_to_nonempty_overlap }, { - "on_remove_after_delete", - ComponentLifecycle_on_remove_after_delete + "tag", + Add_tag }, { - "free_component_new_id_while_fini", - ComponentLifecycle_free_component_new_id_while_fini + "add_entity", + Add_add_entity }, { - "dtor_component_new_id_while_fini", - ComponentLifecycle_dtor_component_new_id_while_fini + "remove_entity", + Add_remove_entity }, { - "free_component_new_pair_id_while_fini", - ComponentLifecycle_free_component_new_pair_id_while_fini + "add_0_entity", + Add_add_0_entity }, { - "dtor_component_new_pair_id_while_fini", - ComponentLifecycle_dtor_component_new_pair_id_while_fini + "remove_0_entity", + Add_remove_0_entity }, { - "free_component_new_obj_pair_id_while_fini", - ComponentLifecycle_free_component_new_obj_pair_id_while_fini + "invalid_add_wildcard", + Add_invalid_add_wildcard }, { - "dtor_component_new_obj_pair_id_while_fini", - ComponentLifecycle_dtor_component_new_obj_pair_id_while_fini + "invalid_add_pair_w_wildcard", + Add_invalid_add_pair_w_wildcard }, { - "ctor_move_dtor_after_resize", - ComponentLifecycle_ctor_move_dtor_after_resize + "invalid_add_pair_w_wildcard_rel", + Add_invalid_add_pair_w_wildcard_rel }, { - "ctx_free", - ComponentLifecycle_ctx_free + "invalid_add_pair_w_wildcard_obj", + Add_invalid_add_pair_w_wildcard_obj }, { - "binding_ctx_free", - ComponentLifecycle_binding_ctx_free + "invalid_add_any", + Add_invalid_add_any }, { - "ctx_free_after_delete_component", - ComponentLifecycle_ctx_free_after_delete_component + "invalid_add_pair_w_any", + Add_invalid_add_pair_w_any }, { - "binding_ctx_free_after_delete_component", - ComponentLifecycle_binding_ctx_free_after_delete_component + "invalid_add_pair_w_any_rel", + Add_invalid_add_pair_w_any_rel }, { - "on_add_ctx", - ComponentLifecycle_on_add_ctx + "invalid_add_pair_w_any_obj", + Add_invalid_add_pair_w_any_obj }, { - "on_remove_ctx", - ComponentLifecycle_on_remove_ctx + "invalid_pair_w_0", + Add_invalid_pair_w_0 }, { - "on_set_ctx", - ComponentLifecycle_on_set_ctx + "invalid_pair_w_0_rel", + Add_invalid_pair_w_0_rel }, { - "on_add_w_existing_component", - ComponentLifecycle_on_add_w_existing_component + "invalid_pair_w_0_obj", + Add_invalid_pair_w_0_obj }, { - "on_remove_w_existing_component", - ComponentLifecycle_on_remove_w_existing_component + "add_random_id", + Add_add_random_id + } +}; + +bake_test_case Remove_testcases[] = { + { + "zero", + Remove_zero }, { - "component_init_set_hooks", - ComponentLifecycle_component_init_set_hooks + "1_of_1", + Remove_1_of_1 }, { - "on_add_after_ctor_w_add", - ComponentLifecycle_on_add_after_ctor_w_add + "1_of_2", + Remove_1_of_2 }, { - "on_add_after_ctor_w_add_to", - ComponentLifecycle_on_add_after_ctor_w_add_to + "2_of_2", + Remove_2_of_2 }, { - "with_before_hooks", - ComponentLifecycle_with_before_hooks + "2_of_3", + Remove_2_of_3 }, { - "with_component_on_add", - ComponentLifecycle_with_component_on_add + "1_of_1_again", + Remove_1_of_1_again }, { - "move_ctor_on_move", - ComponentLifecycle_move_ctor_on_move + "2_again", + Remove_2_again }, { - "ptr_to_self", - ComponentLifecycle_ptr_to_self + "2_overlap", + Remove_2_overlap }, { - "ctor_move_dtor_from_move_ctor", - ComponentLifecycle_ctor_move_dtor_from_move_ctor + "1_from_empty", + Remove_1_from_empty }, { - "on_add_hook_check_offset", - ComponentLifecycle_on_add_hook_check_offset + "not_added", + Remove_not_added + } +}; + +bake_test_case GlobalComponentIds_testcases[] = { + { + "declare", + GlobalComponentIds_declare }, { - "on_remove_hook_check_offset", - ComponentLifecycle_on_remove_hook_check_offset + "declare_w_entity", + GlobalComponentIds_declare_w_entity }, { - "on_set_hook_check_offset", - ComponentLifecycle_on_set_hook_check_offset + "declare_2_world", + GlobalComponentIds_declare_2_world }, { - "on_set_hook_on_override", - ComponentLifecycle_on_set_hook_on_override + "declare_tag", + GlobalComponentIds_declare_tag }, { - "on_set_hook_on_auto_override", - ComponentLifecycle_on_set_hook_on_auto_override + "declare_tag_w_entity", + GlobalComponentIds_declare_tag_w_entity }, { - "batched_set_new_component_w_lifecycle", - ComponentLifecycle_batched_set_new_component_w_lifecycle + "declare_entity", + GlobalComponentIds_declare_entity }, { - "batched_get_mut_new_component_w_lifecycle", - ComponentLifecycle_batched_get_mut_new_component_w_lifecycle + "reuse_300_component_ids", + GlobalComponentIds_reuse_300_component_ids } }; -bake_test_case Sorting_testcases[] = { - { - "sort_by_component", - Sorting_sort_by_component - }, +bake_test_case Sparse_testcases[] = { { - "sort_by_component_2_tables", - Sorting_sort_by_component_2_tables + "has", + Sparse_has }, { - "sort_by_component_3_tables", - Sorting_sort_by_component_3_tables + "owns", + Sparse_owns }, { - "sort_by_entity", - Sorting_sort_by_entity + "get", + Sparse_get }, { - "sort_after_add", - Sorting_sort_after_add + "get_mut", + Sparse_get_mut }, { - "sort_after_remove", - Sorting_sort_after_remove + "ensure", + Sparse_ensure }, { - "sort_after_delete", - Sorting_sort_after_delete + "emplace", + Sparse_emplace }, { - "sort_after_set", - Sorting_sort_after_set + "set", + Sparse_set }, { - "sort_after_system", - Sorting_sort_after_system + "modified_no_on_set", + Sparse_modified_no_on_set }, { - "sort_after_query", - Sorting_sort_after_query + "insert_1", + Sparse_insert_1 }, { - "sort_by_component_same_value_1", - Sorting_sort_by_component_same_value_1 + "insert_2", + Sparse_insert_2 }, { - "sort_by_component_same_value_2", - Sorting_sort_by_component_same_value_2 + "get_ref", + Sparse_get_ref }, { - "sort_by_component_move_pivot", - Sorting_sort_by_component_move_pivot + "update_ref", + Sparse_update_ref }, { - "sort_1000_entities", - Sorting_sort_1000_entities + "get_recycled", + Sparse_get_recycled }, { - "sort_1000_entities_w_duplicates", - Sorting_sort_1000_entities_w_duplicates + "get_mut_recycled", + Sparse_get_mut_recycled }, { - "sort_1000_entities_again", - Sorting_sort_1000_entities_again + "ensure_recycled", + Sparse_ensure_recycled }, { - "sort_1000_entities_2_types", - Sorting_sort_1000_entities_2_types + "emplace_recycled", + Sparse_emplace_recycled }, { - "sort_1500_entities_3_types", - Sorting_sort_1500_entities_3_types + "set_recycled", + Sparse_set_recycled }, { - "sort_2000_entities_4_types", - Sorting_sort_2000_entities_4_types + "get_ref_recycled", + Sparse_get_ref_recycled }, { - "sort_2_entities_2_types", - Sorting_sort_2_entities_2_types + "test_stable_ptr", + Sparse_test_stable_ptr }, { - "sort_3_entities_3_types", - Sorting_sort_3_entities_3_types + "has_after_remove", + Sparse_has_after_remove }, { - "sort_3_entities_3_types_2", - Sorting_sort_3_entities_3_types_2 + "has_after_clear", + Sparse_has_after_clear }, { - "sort_4_entities_4_types", - Sorting_sort_4_entities_4_types + "get_after_remove", + Sparse_get_after_remove }, { - "sort_1000_entities_2_types_again", - Sorting_sort_1000_entities_2_types_again + "get_mut_after_remove", + Sparse_get_mut_after_remove }, { - "sort_1000_entities_add_type_after_sort", - Sorting_sort_1000_entities_add_type_after_sort + "sparse_w_hole", + Sparse_sparse_w_hole }, { - "sort_shared_component", - Sorting_sort_shared_component + "record_get", + Sparse_record_get }, { - "sort_shared_component_childof", - Sorting_sort_shared_component_childof + "has_inherited", + Sparse_has_inherited }, { - "sort_w_tags_only", - Sorting_sort_w_tags_only + "owns_inherited", + Sparse_owns_inherited }, { - "sort_childof_marked", - Sorting_sort_childof_marked + "get_inherited", + Sparse_get_inherited }, { - "sort_isa_marked", - Sorting_sort_isa_marked + "get_mut_inherited", + Sparse_get_mut_inherited }, { - "sort_relation_marked", - Sorting_sort_relation_marked + "ensure_inherited", + Sparse_ensure_inherited }, { - "dont_resort_after_set_unsorted_component", - Sorting_dont_resort_after_set_unsorted_component + "emplace_inherited", + Sparse_emplace_inherited }, { - "dont_resort_after_set_unsorted_component_w_tag", - Sorting_dont_resort_after_set_unsorted_component_w_tag + "override_component", + Sparse_override_component }, { - "dont_resort_after_set_unsorted_component_w_tag_w_out_term", - Sorting_dont_resort_after_set_unsorted_component_w_tag_w_out_term + "delete_w_override_component", + Sparse_delete_w_override_component }, { - "sort_component_not_queried_for", - Sorting_sort_component_not_queried_for + "delete_w_override_on_remove_isa", + Sparse_delete_w_override_on_remove_isa }, { - "sort_by_wildcard", - Sorting_sort_by_wildcard - } -}; - -bake_test_case SortingEntireTable_testcases[] = { - { - "sort_by_component", - SortingEntireTable_sort_by_component + "ctor_after_emplace", + Sparse_ctor_after_emplace }, { - "sort_by_component_2_tables", - SortingEntireTable_sort_by_component_2_tables + "ctor_dtor_after_remove", + Sparse_ctor_dtor_after_remove }, { - "sort_by_component_3_tables", - SortingEntireTable_sort_by_component_3_tables + "ctor_dtor_after_clear", + Sparse_ctor_dtor_after_clear }, { - "sort_by_entity", - SortingEntireTable_sort_by_entity + "ctor_dtor_after_delete", + Sparse_ctor_dtor_after_delete }, { - "sort_after_add", - SortingEntireTable_sort_after_add + "ctor_dtor_after_fini", + Sparse_ctor_dtor_after_fini }, { - "sort_after_remove", - SortingEntireTable_sort_after_remove + "on_add_remove_after_remove", + Sparse_on_add_remove_after_remove }, { - "sort_after_delete", - SortingEntireTable_sort_after_delete + "on_add_remove_after_clear", + Sparse_on_add_remove_after_clear }, { - "sort_after_set", - SortingEntireTable_sort_after_set + "on_add_remove_after_delete", + Sparse_on_add_remove_after_delete }, { - "sort_after_system", - SortingEntireTable_sort_after_system + "on_add_remove_after_fini", + Sparse_on_add_remove_after_fini }, { - "sort_after_query", - SortingEntireTable_sort_after_query + "on_set_after_set", + Sparse_on_set_after_set }, { - "sort_by_component_same_value_1", - SortingEntireTable_sort_by_component_same_value_1 + "on_set_after_modified", + Sparse_on_set_after_modified }, { - "sort_by_component_same_value_2", - SortingEntireTable_sort_by_component_same_value_2 + "on_set_at_offset", + Sparse_on_set_at_offset }, { - "sort_by_component_move_pivot", - SortingEntireTable_sort_by_component_move_pivot + "on_add_observer", + Sparse_on_add_observer }, { - "sort_1000_entities", - SortingEntireTable_sort_1000_entities + "on_set_observer_set", + Sparse_on_set_observer_set }, { - "sort_1000_entities_w_duplicates", - SortingEntireTable_sort_1000_entities_w_duplicates + "on_set_observer_modified", + Sparse_on_set_observer_modified }, { - "sort_1000_entities_again", - SortingEntireTable_sort_1000_entities_again + "on_set_observer_insert", + Sparse_on_set_observer_insert }, { - "sort_1000_entities_2_types", - SortingEntireTable_sort_1000_entities_2_types + "on_remove_observer_remove", + Sparse_on_remove_observer_remove }, { - "sort_1500_entities_3_types", - SortingEntireTable_sort_1500_entities_3_types + "on_remove_observer_clear", + Sparse_on_remove_observer_clear }, { - "sort_2000_entities_4_types", - SortingEntireTable_sort_2000_entities_4_types + "on_remove_observer_delete", + Sparse_on_remove_observer_delete }, { - "sort_2_entities_2_types", - SortingEntireTable_sort_2_entities_2_types + "on_remove_observer_fini", + Sparse_on_remove_observer_fini }, { - "sort_3_entities_3_types", - SortingEntireTable_sort_3_entities_3_types + "on_set_after_remove_override", + Sparse_on_set_after_remove_override }, { - "sort_3_entities_3_types_2", - SortingEntireTable_sort_3_entities_3_types_2 + "on_add_observer_2_terms", + Sparse_on_add_observer_2_terms }, { - "sort_4_entities_4_types", - SortingEntireTable_sort_4_entities_4_types + "on_set_observer_2_terms", + Sparse_on_set_observer_2_terms }, { - "sort_1000_entities_2_types_again", - SortingEntireTable_sort_1000_entities_2_types_again + "on_remove_observer_2_terms", + Sparse_on_remove_observer_2_terms }, { - "sort_1000_entities_add_type_after_sort", - SortingEntireTable_sort_1000_entities_add_type_after_sort + "sparse_relationship", + Sparse_sparse_relationship }, { - "sort_shared_component", - SortingEntireTable_sort_shared_component + "defer_ensure", + Sparse_defer_ensure }, { - "sort_w_tags_only", - SortingEntireTable_sort_w_tags_only + "defer_ensure_w_modified", + Sparse_defer_ensure_w_modified }, { - "sort_childof_marked", - SortingEntireTable_sort_childof_marked + "defer_ensure_modified", + Sparse_defer_ensure_modified }, { - "sort_isa_marked", - SortingEntireTable_sort_isa_marked + "defer_emplace", + Sparse_defer_emplace }, { - "sort_relation_marked", - SortingEntireTable_sort_relation_marked + "defer_emplace_w_modified", + Sparse_defer_emplace_w_modified }, { - "dont_resort_after_set_unsorted_component", - SortingEntireTable_dont_resort_after_set_unsorted_component + "defer_set", + Sparse_defer_set }, { - "dont_resort_after_set_unsorted_component_w_tag", - SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag + "defer_ensure_existing", + Sparse_defer_ensure_existing }, { - "dont_resort_after_set_unsorted_component_w_tag_w_out_term", - SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag_w_out_term - } -}; - -bake_test_case Filter_testcases[] = { - { - "filter_1_term", - Filter_filter_1_term + "defer_ensure_existing_twice", + Sparse_defer_ensure_existing_twice }, { - "filter_1_term_component", - Filter_filter_1_term_component + "defer_ensure_w_modified_existing", + Sparse_defer_ensure_w_modified_existing }, { - "filter_2_terms", - Filter_filter_2_terms + "defer_ensure_modified_existing", + Sparse_defer_ensure_modified_existing }, { - "filter_3_terms", - Filter_filter_3_terms + "defer_emplace_existing", + Sparse_defer_emplace_existing }, { - "filter_3_terms_w_or", - Filter_filter_3_terms_w_or + "defer_emplace_w_modified_existing", + Sparse_defer_emplace_w_modified_existing }, { - "filter_4_terms_w_or_at_1", - Filter_filter_4_terms_w_or_at_1 + "defer_set_existing", + Sparse_defer_set_existing }, { - "filter_1_term_wildcard", - Filter_filter_1_term_wildcard + "defer_batched_ensure", + Sparse_defer_batched_ensure }, { - "filter_1_term_any", - Filter_filter_1_term_any + "defer_batched_ensure_w_modified", + Sparse_defer_batched_ensure_w_modified }, { - "filter_1_term_same_subj_obj", - Filter_filter_1_term_same_subj_obj + "defer_batched_ensure_modified", + Sparse_defer_batched_ensure_modified }, { - "filter_1_term_acyclic_same_subj_obj", - Filter_filter_1_term_acyclic_same_subj_obj + "defer_batched_emplace", + Sparse_defer_batched_emplace }, { - "filter_1_term_acyclic_reflexive_same_subj_obj", - Filter_filter_1_term_acyclic_reflexive_same_subj_obj + "defer_batched_emplace_w_modified", + Sparse_defer_batched_emplace_w_modified }, { - "filter_1_term_same_subj_obj_var", - Filter_filter_1_term_same_subj_obj_var + "defer_batched_set", + Sparse_defer_batched_set }, { - "filter_1_term_acyclic_same_subj_obj_var", - Filter_filter_1_term_acyclic_same_subj_obj_var + "defer_batched_ensure_existing", + Sparse_defer_batched_ensure_existing }, { - "filter_1_term_acyclic_reflexive_same_subj_obj_var", - Filter_filter_1_term_acyclic_reflexive_same_subj_obj_var + "defer_batched_ensure_existing_twice", + Sparse_defer_batched_ensure_existing_twice }, { - "filter_1_term_non_acyclic_superset", - Filter_filter_1_term_non_acyclic_superset + "defer_batched_ensure_w_modified_existing", + Sparse_defer_batched_ensure_w_modified_existing }, { - "filter_1_term_dont_inherit_default_set", - Filter_filter_1_term_dont_inherit_default_set + "defer_batched_ensure_modified_existing", + Sparse_defer_batched_ensure_modified_existing }, { - "filter_1_term_dont_inherit_pair_default_set", - Filter_filter_1_term_dont_inherit_pair_default_set + "defer_batched_emplace_existing", + Sparse_defer_batched_emplace_existing }, { - "filter_1_term_cascade_implicit_isa", - Filter_filter_1_term_cascade_implicit_isa + "defer_batched_emplace_w_modified_existing", + Sparse_defer_batched_emplace_w_modified_existing }, { - "filter_1_term_cascade_isa", - Filter_filter_1_term_cascade_isa + "defer_batched_set_existing", + Sparse_defer_batched_set_existing }, { - "filter_1_term_cascade_childof", - Filter_filter_1_term_cascade_childof + "defer_batched_set_remove", + Sparse_defer_batched_set_remove }, { - "filter_1_term_cascade_down", - Filter_filter_1_term_cascade_down + "defer_batched_set_remove_existing", + Sparse_defer_batched_set_remove_existing + } +}; + +bake_test_case Union_testcases[] = { + { + "add", + Union_add }, { - "filter_1_term_optional_only", - Filter_filter_1_term_optional_only + "add_twice", + Union_add_twice }, { - "filter_1_term_transitive_pair", - Filter_filter_1_term_transitive_pair + "add_replace", + Union_add_replace }, { - "filter_1_term_transitive_pair_explicit_self_tgt", - Filter_filter_1_term_transitive_pair_explicit_self_tgt + "add_remove", + Union_add_remove }, { - "filter_1_variable_as_pred_only", - Filter_filter_1_variable_as_pred_only + "add_remove_recycled", + Union_add_remove_recycled }, { - "filter_1_variable_as_pred_w_subj", - Filter_filter_1_variable_as_pred_w_subj + "add_remove_add", + Union_add_remove_add }, { - "filter_1_variable_as_pred_w_pair", - Filter_filter_1_variable_as_pred_w_pair + "get_target_none", + Union_get_target_none }, { - "filter_1_variable_as_subj", - Filter_filter_1_variable_as_subj + "get_target", + Union_get_target }, { - "filter_1_variable_as_obj", - Filter_filter_1_variable_as_obj + "get_recycled_target", + Union_get_recycled_target }, { - "filter_2_terms_or_w_dontinherit", - Filter_filter_2_terms_or_w_dontinherit + "get_target_after_replace", + Union_get_target_after_replace }, { - "filter_2_terms_or_w_both_dontinherit", - Filter_filter_2_terms_or_w_both_dontinherit + "get_target_after_remove", + Union_get_target_after_remove }, { - "filter_w_pair_id", - Filter_filter_w_pair_id + "has_wildcard", + Union_has_wildcard }, { - "filter_w_pred_obj", - Filter_filter_w_pred_obj + "has_any", + Union_has_any }, { - "filter_w_pair_id_and_subj", - Filter_filter_w_pair_id_and_subj + "add_remove_2_tgts", + Union_add_remove_2_tgts }, { - "filter_1_w_pred_name", - Filter_filter_1_w_pred_name + "add_remove_2_tgts_join", + Union_add_remove_2_tgts_join }, { - "filter_1_w_final_pred_name", - Filter_filter_1_w_final_pred_name + "add_remove_3_tgts", + Union_add_remove_3_tgts }, { - "filter_1_w_subj_name", - Filter_filter_1_w_subj_name + "add_remove_3_tgts_join", + Union_add_remove_3_tgts_join }, { - "filter_1_w_obj_name", - Filter_filter_1_w_obj_name + "remove_w_union_tgt", + Union_remove_w_union_tgt }, { - "filter_w_this_implicit_variable", - Filter_filter_w_this_implicit_variable + "get_non_union_tgt_from_table_w_union", + Union_get_non_union_tgt_from_table_w_union }, { - "filter_w_this_explicit_entity", - Filter_filter_w_this_explicit_entity + "has_non_union_from_table_w_union", + Union_has_non_union_from_table_w_union }, { - "filter_w_first_this_implicit_variable", - Filter_filter_w_first_this_implicit_variable + "get_case_no_switch", + Union_get_case_no_switch }, { - "filter_w_first_this_explicit_entity", - Filter_filter_w_first_this_explicit_entity + "get_case_set", + Union_get_case_set }, { - "filter_w_second_this_implicit_variable", - Filter_filter_w_second_this_implicit_variable + "get_case_change", + Union_get_case_change }, { - "filter_w_second_this_explicit_entity", - Filter_filter_w_second_this_explicit_entity + "remove_case", + Union_remove_case }, { - "filter_w_this_variable_name", - Filter_filter_w_this_variable_name + "remove_last", + Union_remove_last }, { - "filter_w_src_var", - Filter_filter_w_src_var + "delete_first", + Union_delete_first }, { - "filter_w_first_var", - Filter_filter_w_first_var + "delete_last", + Union_delete_last }, { - "filter_w_second_var", - Filter_filter_w_second_var + "delete_first_last", + Union_delete_first_last }, { - "filter_w_0_source", - Filter_filter_w_0_source + "3_entities_same_case", + Union_3_entities_same_case }, { - "filter_w_0_target", - Filter_filter_w_0_target + "2_entities_1_change_case", + Union_2_entities_1_change_case }, { - "filter_2_terms_w_or", - Filter_filter_2_terms_w_or + "3_entities_change_case", + Union_3_entities_change_case }, { - "filter_2_terms_w_or_mixed_src_flags", - Filter_filter_2_terms_w_or_mixed_src_flags + "add_case_in_stage", + Union_add_case_in_stage }, { - "filter_2_terms_w_or_mixed_src_id", - Filter_filter_2_terms_w_or_mixed_src_id + "change_case_in_stage", + Union_change_case_in_stage }, { - "filter_2_terms_w_or_mixed_src_name", - Filter_filter_2_terms_w_or_mixed_src_name + "change_one_case_in_stage", + Union_change_one_case_in_stage }, { - "filter_2_terms_w_or_same_src_w_id_and_name", - Filter_filter_2_terms_w_or_same_src_w_id_and_name + "remove_switch_in_stage", + Union_remove_switch_in_stage }, { - "filter_move", - Filter_filter_move + "switch_no_match_for_case", + Union_switch_no_match_for_case }, { - "filter_copy", - Filter_filter_copy + "empty_entity_has_case", + Union_empty_entity_has_case }, { - "filter_w_resources_copy", - Filter_filter_w_resources_copy + "zero_entity_has_case", + Union_zero_entity_has_case }, { - "filter_w_and_flag", - Filter_filter_w_and_flag + "add_to_entity_w_switch", + Union_add_to_entity_w_switch }, { - "filter_w_or_flag", - Filter_filter_w_or_flag + "add_pair_to_entity_w_switch", + Union_add_pair_to_entity_w_switch }, { - "filter_w_not_flag", - Filter_filter_w_not_flag + "recycled_tags", + Union_recycled_tags }, { - "filter_filter", - Filter_filter_filter + "same_table_after_change", + Union_same_table_after_change }, { - "filter_double_init", - Filter_filter_double_init + "add_2", + Union_add_2 }, { - "filter_double_init_w_expr", - Filter_filter_double_init_w_expr + "add_2_reverse", + Union_add_2_reverse }, { - "filter_double_init_w_expr_optional", - Filter_filter_double_init_w_expr_optional + "add_switch_to_prefab_instance", + Union_add_switch_to_prefab_instance }, { - "filter_w_tag_term_is_no_data", - Filter_filter_w_tag_term_is_no_data + "get_case_w_generation", + Union_get_case_w_generation }, { - "filter_w_inout_none_term_is_no_data", - Filter_filter_w_inout_none_term_is_no_data + "get_case_w_generation_not_alive", + Union_get_case_w_generation_not_alive }, { - "filter_w_tag_and_inout_none_term_is_no_data", - Filter_filter_w_tag_and_inout_none_term_is_no_data + "defer_add_union_relationship", + Union_defer_add_union_relationship }, { - "filter_w_not_term_is_no_data", - Filter_filter_w_not_term_is_no_data + "defer_add_existing_union_relationship", + Union_defer_add_existing_union_relationship }, { - "filter_w_no_transitive_pair", - Filter_filter_w_no_transitive_pair + "defer_add_union_relationship_2_ops", + Union_defer_add_union_relationship_2_ops }, { - "filter_w_transitive_pair_any_src", - Filter_filter_w_transitive_pair_any_src + "defer_add_existing_union_relationship_2_ops", + Union_defer_add_existing_union_relationship_2_ops }, { - "filter_w_transitive_pair", - Filter_filter_w_transitive_pair + "stress_test_1", + Union_stress_test_1 + } +}; + +bake_test_case Hierarchies_testcases[] = { + { + "empty_scope", + Hierarchies_empty_scope }, { - "filter_w_transitive_tag_no_pair", - Filter_filter_w_transitive_tag_no_pair + "get_parent", + Hierarchies_get_parent }, { - "filter_w_transitive_tag_self_tgt", - Filter_filter_w_transitive_tag_self_tgt + "get_parent_from_nested", + Hierarchies_get_parent_from_nested }, { - "filter_w_transitive_tag_any_tgt", - Filter_filter_w_transitive_tag_any_tgt + "get_parent_from_nested_2", + Hierarchies_get_parent_from_nested_2 }, { - "filter_w_pair_same_vars", - Filter_filter_w_pair_same_vars + "get_object_from_0", + Hierarchies_get_object_from_0 }, { - "filter_w_pair_not_same_vars", - Filter_filter_w_pair_not_same_vars + "tree_iter_empty", + Hierarchies_tree_iter_empty }, { - "filter_w_pair_no_vars_not_same_vars", - Filter_filter_w_pair_no_vars_not_same_vars + "tree_iter_1_table", + Hierarchies_tree_iter_1_table }, { - "filter_w_pair_wildcard_not_same_vars", - Filter_filter_w_pair_wildcard_not_same_vars + "tree_iter_2_tables", + Hierarchies_tree_iter_2_tables }, { - "filter_w_pair_any_not_same_vars", - Filter_filter_w_pair_any_not_same_vars + "path_depth_0", + Hierarchies_path_depth_0 }, { - "filter_w_no_pair_not_same_vars", - Filter_filter_w_no_pair_not_same_vars + "path_depth_1", + Hierarchies_path_depth_1 }, { - "filter_not_childof_any", - Filter_filter_not_childof_any + "path_depth_2", + Hierarchies_path_depth_2 }, { - "filter_w_inherited_id", - Filter_filter_w_inherited_id + "path_core", + Hierarchies_path_core }, { - "filter_w_inherited_pair", - Filter_filter_w_inherited_pair + "path_core_w_prefix", + Hierarchies_path_core_w_prefix }, { - "filter_w_non_inherited_id", - Filter_filter_w_non_inherited_id + "path_core_w_empty_prefix", + Hierarchies_path_core_w_empty_prefix }, { - "filter_w_non_inherited_pair", - Filter_filter_w_non_inherited_pair + "path_this_w_empty_prefix", + Hierarchies_path_this_w_empty_prefix }, { - "filter_w_first_rel", - Filter_filter_w_first_rel + "path_wildcard_w_empty_prefix", + Hierarchies_path_wildcard_w_empty_prefix }, { - "filter_w_first_rel_self", - Filter_filter_w_first_rel_self + "path_any_w_empty_prefix", + Hierarchies_path_any_w_empty_prefix }, { - "filter_w_first_rel_down", - Filter_filter_w_first_rel_down + "rel_path_from_root", + Hierarchies_rel_path_from_root }, { - "filter_w_first_rel_self_down", - Filter_filter_w_first_rel_self_down + "rel_path_from_self", + Hierarchies_rel_path_from_self }, { - "filter_w_first_rel_reflexive", - Filter_filter_w_first_rel_reflexive + "rel_path_depth_1", + Hierarchies_rel_path_depth_1 }, { - "filter_w_first_rel_reflexive_self", - Filter_filter_w_first_rel_reflexive_self + "rel_path_depth_2", + Hierarchies_rel_path_depth_2 }, { - "filter_w_first_rel_reflexive_down", - Filter_filter_w_first_rel_reflexive_down + "rel_path_no_match", + Hierarchies_rel_path_no_match }, { - "filter_w_first_rel_reflexive_self_down", - Filter_filter_w_first_rel_reflexive_self_down + "path_custom_sep", + Hierarchies_path_custom_sep }, { - "filter_w_first_rel_non_traversable", - Filter_filter_w_first_rel_non_traversable + "path_custom_prefix", + Hierarchies_path_custom_prefix }, { - "filter_w_first_wildcard_inout_none", - Filter_filter_w_first_wildcard_inout_none + "path_prefix_rel_match", + Hierarchies_path_prefix_rel_match }, { - "filter_w_first_var_inout_none", - Filter_filter_w_first_var_inout_none + "path_prefix_rel_no_match", + Hierarchies_path_prefix_rel_no_match }, { - "filter_w_pair_wildcard_inout_none", - Filter_filter_w_pair_wildcard_inout_none + "path_escaped_sep", + Hierarchies_path_escaped_sep }, { - "filter_w_pair_var_inout_none", - Filter_filter_w_pair_var_inout_none + "path_escaped_two_sep", + Hierarchies_path_escaped_two_sep }, { - "filter_w_unresolved_by_name", - Filter_filter_w_unresolved_by_name + "path_escaped_two_consecutive_sep", + Hierarchies_path_escaped_two_consecutive_sep }, { - "filter_w_unresolved_by_name_eq", - Filter_filter_w_unresolved_by_name_eq + "path_escaped_sep_at_begin", + Hierarchies_path_escaped_sep_at_begin }, { - "filter_childof_this", - Filter_filter_childof_this + "path_escaped_sep_at_end", + Hierarchies_path_escaped_sep_at_end }, { - "filter_childof_this_entity", - Filter_filter_childof_this_entity + "path_escaped_sep_w_parent", + Hierarchies_path_escaped_sep_w_parent }, { - "filter_childof_this_by_id", - Filter_filter_childof_this_by_id + "path_only_escaped_sep", + Hierarchies_path_only_escaped_sep }, { - "term_w_id", - Filter_term_w_id + "path_only_escaped_sep_w_parent", + Hierarchies_path_only_escaped_sep_w_parent }, { - "term_w_pair_id", - Filter_term_w_pair_id + "path_only_escaped_two_sep", + Hierarchies_path_only_escaped_two_sep }, { - "term_w_pred_obj", - Filter_term_w_pred_obj + "path_only_escaped_two_sep_w_parent", + Hierarchies_path_only_escaped_two_sep_w_parent }, { - "term_w_pair_finalize_twice", - Filter_term_w_pair_finalize_twice + "fullpath_for_core", + Hierarchies_fullpath_for_core }, { - "term_w_role", - Filter_term_w_role + "path_w_number", + Hierarchies_path_w_number }, { - "term_w_pred_role", - Filter_term_w_pred_role + "path_w_entity_id", + Hierarchies_path_w_entity_id }, { - "term_w_self", - Filter_term_w_self + "lookup_depth_0", + Hierarchies_lookup_depth_0 }, { - "term_w_superset", - Filter_term_w_superset + "lookup_depth_1", + Hierarchies_lookup_depth_1 }, { - "term_w_subset", - Filter_term_w_subset + "lookup_depth_2", + Hierarchies_lookup_depth_2 }, { - "term_w_self_superset", - Filter_term_w_self_superset + "lookup_rel_0", + Hierarchies_lookup_rel_0 }, { - "term_w_superset_custom_relation", - Filter_term_w_superset_custom_relation + "lookup_rel_1", + Hierarchies_lookup_rel_1 }, { - "term_w_self_superset_custom_relation", - Filter_term_w_self_superset_custom_relation + "lookup_rel_2", + Hierarchies_lookup_rel_2 }, { - "term_iter_component", - Filter_term_iter_component + "lookup_custom_sep", + Hierarchies_lookup_custom_sep }, { - "term_iter_w_pred", - Filter_term_iter_w_pred + "lookup_custom_prefix", + Hierarchies_lookup_custom_prefix }, { - "term_iter_tag", - Filter_term_iter_tag + "lookup_custom_prefix_from_root", + Hierarchies_lookup_custom_prefix_from_root }, { - "term_iter_pair", - Filter_term_iter_pair + "lookup_self", + Hierarchies_lookup_self }, { - "term_iter_pair_w_rel_wildcard", - Filter_term_iter_pair_w_rel_wildcard + "lookup_in_parent_from_scope", + Hierarchies_lookup_in_parent_from_scope }, { - "term_iter_pair_w_obj_wildcard", - Filter_term_iter_pair_w_obj_wildcard + "lookup_in_root_from_scope", + Hierarchies_lookup_in_root_from_scope }, { - "term_iter_pair_w_rel_wildcard_n_matches", - Filter_term_iter_pair_w_rel_wildcard_n_matches + "lookup_number", + Hierarchies_lookup_number }, { - "term_iter_pair_w_rel_wildcard_n_matches_w_data", - Filter_term_iter_pair_w_rel_wildcard_n_matches_w_data + "lookup_entity_id", + Hierarchies_lookup_entity_id }, { - "term_iter_pair_w_obj_wildcard_n_matches", - Filter_term_iter_pair_w_obj_wildcard_n_matches + "delete_children", + Hierarchies_delete_children }, { - "term_iter_pair_w_obj_wildcard_n_matches_w_data", - Filter_term_iter_pair_w_obj_wildcard_n_matches_w_data + "scope_set", + Hierarchies_scope_set }, { - "term_iter_w_superset", - Filter_term_iter_w_superset + "scope_set_again", + Hierarchies_scope_set_again }, { - "term_iter_w_superset_base_w_2_components", - Filter_term_iter_w_superset_base_w_2_components + "scope_set_w_lookup", + Hierarchies_scope_set_w_lookup }, { - "term_iter_w_superset_childof", - Filter_term_iter_w_superset_childof + "scope_component", + Hierarchies_scope_component }, { - "term_iter_w_superset_self", - Filter_term_iter_w_superset_self + "scope_component_no_macro", + Hierarchies_scope_component_no_macro }, { - "term_iter_w_superset_self_childof", - Filter_term_iter_w_superset_self_childof + "new_from_path_depth_0", + Hierarchies_new_from_path_depth_0 }, { - "term_iter_w_superset_tag", - Filter_term_iter_w_superset_tag + "new_from_path_depth_1", + Hierarchies_new_from_path_depth_1 }, { - "term_iter_w_superset_pair", - Filter_term_iter_w_superset_pair + "new_from_path_depth_2", + Hierarchies_new_from_path_depth_2 }, { - "term_iter_w_superset_pair_obj_wildcard", - Filter_term_iter_w_superset_pair_obj_wildcard + "new_from_path_existing_depth_0", + Hierarchies_new_from_path_existing_depth_0 }, { - "term_iter_in_stage", - Filter_term_iter_in_stage + "new_from_path_existing_depth_1", + Hierarchies_new_from_path_existing_depth_1 }, { - "term_iter_w_readonly_term", - Filter_term_iter_w_readonly_term + "new_from_path_existing_depth_2", + Hierarchies_new_from_path_existing_depth_2 }, { - "term_iter_type_set", - Filter_term_iter_type_set + "add_path_depth_0", + Hierarchies_add_path_depth_0 }, { - "term_iter_any_match_wildcard", - Filter_term_iter_any_match_wildcard + "add_path_depth_1", + Hierarchies_add_path_depth_1 }, { - "term_iter_any_match_tag_and_wildcard", - Filter_term_iter_any_match_tag_and_wildcard + "add_path_depth_2", + Hierarchies_add_path_depth_2 }, { - "term_iter_any_obj", - Filter_term_iter_any_obj + "add_path_existing_depth_0", + Hierarchies_add_path_existing_depth_0 }, { - "children_iter", - Filter_children_iter + "add_path_existing_depth_1", + Hierarchies_add_path_existing_depth_1 }, { - "filter_iter_1_tag", - Filter_filter_iter_1_tag + "add_path_existing_depth_2", + Hierarchies_add_path_existing_depth_2 }, { - "filter_iter_2_tags", - Filter_filter_iter_2_tags + "add_path_from_scope", + Hierarchies_add_path_from_scope }, { - "filter_iter_2_tags_1_not", - Filter_filter_iter_2_tags_1_not + "add_path_from_scope_new_entity", + Hierarchies_add_path_from_scope_new_entity }, { - "filter_iter_3_tags_2_or", - Filter_filter_iter_3_tags_2_or + "add_root_path_to_child", + Hierarchies_add_root_path_to_child }, { - "filter_iter_only_optional", - Filter_filter_iter_only_optional + "add_parent_path_from_root_to_child", + Hierarchies_add_parent_path_from_root_to_child }, { - "filter_iter_only_2_or", - Filter_filter_iter_only_2_or + "delete_child", + Hierarchies_delete_child }, { - "filter_iter_only_3_or", - Filter_filter_iter_only_3_or + "delete_2_children", + Hierarchies_delete_2_children }, { - "filter_iter_2_or", - Filter_filter_iter_2_or + "delete_2_children_different_type", + Hierarchies_delete_2_children_different_type }, { - "filter_iter_3_or", - Filter_filter_iter_3_or + "delete_tree_2_levels", + Hierarchies_delete_tree_2_levels }, { - "filter_iter_2_or_other_type", - Filter_filter_iter_2_or_other_type + "delete_tree_3_levels", + Hierarchies_delete_tree_3_levels }, { - "filter_iter_2_or_same_type", - Filter_filter_iter_2_or_same_type + "delete_tree_empty_table", + Hierarchies_delete_tree_empty_table }, { - "filter_iter_1_component", - Filter_filter_iter_1_component + "delete_tree_recreate", + Hierarchies_delete_tree_recreate }, { - "filter_iter_2_components", - Filter_filter_iter_2_components + "delete_tree_w_onremove", + Hierarchies_delete_tree_w_onremove }, { - "filter_iter_pair_id", - Filter_filter_iter_pair_id + "delete_tree_w_dtor", + Hierarchies_delete_tree_w_dtor }, { - "filter_iter_2_pair_ids", - Filter_filter_iter_2_pair_ids + "get_child_count", + Hierarchies_get_child_count }, { - "filter_iter_childof_pair_0_parent", - Filter_filter_iter_childof_pair_0_parent + "get_child_count_2_tables", + Hierarchies_get_child_count_2_tables }, { - "filter_iter_pair_pred_obj", - Filter_filter_iter_pair_pred_obj + "get_child_count_no_children", + Hierarchies_get_child_count_no_children }, { - "filter_iter_pair_2_pred_obj", - Filter_filter_iter_pair_2_pred_obj + "scope_iter_after_delete_tree", + Hierarchies_scope_iter_after_delete_tree }, { - "filter_iter_null", - Filter_filter_iter_null + "add_child_after_delete_tree", + Hierarchies_add_child_after_delete_tree }, { - "filter_iter_1_not_tag", - Filter_filter_iter_1_not_tag + "add_child_to_recycled_parent", + Hierarchies_add_child_to_recycled_parent }, { - "filter_iter_2_tags_1_optional", - Filter_filter_iter_2_tags_1_optional + "get_type_after_recycled_parent_add", + Hierarchies_get_type_after_recycled_parent_add }, { - "filter_iter_2_components_1_optional", - Filter_filter_iter_2_components_1_optional + "long_name_depth_0", + Hierarchies_long_name_depth_0 }, { - "filter_iter_in_stage", - Filter_filter_iter_in_stage + "long_name_depth_1", + Hierarchies_long_name_depth_1 }, { - "filter_iter_10_tags", - Filter_filter_iter_10_tags + "long_name_depth_2", + Hierarchies_long_name_depth_2 }, { - "filter_iter_20_tags", - Filter_filter_iter_20_tags + "ensure_1_parent_after_adding_2", + Hierarchies_ensure_1_parent_after_adding_2 }, { - "filter_iter_10_components", - Filter_filter_iter_10_components + "ensure_child_alive_after_deleting_prev_parent", + Hierarchies_ensure_child_alive_after_deleting_prev_parent }, { - "filter_iter_20_components", - Filter_filter_iter_20_components + "lookup_after_root_to_parent_move", + Hierarchies_lookup_after_root_to_parent_move }, { - "filter_iter_superset", - Filter_filter_iter_superset + "lookup_after_parent_to_root_move", + Hierarchies_lookup_after_parent_to_root_move }, { - "filter_iter_superset_childof", - Filter_filter_iter_superset_childof + "lookup_after_parent_to_parent_move", + Hierarchies_lookup_after_parent_to_parent_move }, { - "filter_iter_type_set", - Filter_filter_iter_type_set + "lookup_after_clear_from_root", + Hierarchies_lookup_after_clear_from_root }, { - "filter_iter_w_readonly_term", - Filter_filter_iter_w_readonly_term + "lookup_after_clear_from_parent", + Hierarchies_lookup_after_clear_from_parent }, { - "filter_iter_w_from_nothing_term", - Filter_filter_iter_w_from_nothing_term + "lookup_after_delete_from_root", + Hierarchies_lookup_after_delete_from_root }, { - "filter_iter_pair_w_rel_wildcard_n_matches", - Filter_filter_iter_pair_w_rel_wildcard_n_matches + "lookup_after_delete_from_parent", + Hierarchies_lookup_after_delete_from_parent }, { - "filter_iter_pair_w_obj_wildcard_n_matches", - Filter_filter_iter_pair_w_obj_wildcard_n_matches + "defer_batch_remove_name_w_add_childof", + Hierarchies_defer_batch_remove_name_w_add_childof }, { - "filter_iter_pair_w_2_wildcards_1_match", - Filter_filter_iter_pair_w_2_wildcards_1_match + "defer_batch_remove_childof_w_add_name", + Hierarchies_defer_batch_remove_childof_w_add_name + } +}; + +bake_test_case Has_testcases[] = { + { + "zero", + Has_zero }, { - "filter_iter_pair_w_2_wildcards_2x1_matches", - Filter_filter_iter_pair_w_2_wildcards_2x1_matches + "1_of_0", + Has_1_of_0 }, { - "filter_iter_pair_w_2_wildcards_2x2_matches", - Filter_filter_iter_pair_w_2_wildcards_2x2_matches + "1_of_1", + Has_1_of_1 }, { - "filter_iter_pair_w_3_wildcards_2x2x2_matches", - Filter_filter_iter_pair_w_3_wildcards_2x2x2_matches + "1_of_2", + Has_1_of_2 }, { - "filter_iter_pair_w_wildcard_and_nothing", - Filter_filter_iter_pair_w_wildcard_and_nothing + "1_of_empty", + Has_1_of_empty }, { - "filter_iter_any", - Filter_filter_iter_any + "has_in_progress", + Has_has_in_progress }, { - "filter_iter_any_match_wildcard", - Filter_filter_iter_any_match_wildcard + "has_of_zero", + Has_has_of_zero }, { - "filter_iter_any_match_tag_and_wildcard", - Filter_filter_iter_any_match_tag_and_wildcard + "owns", + Has_owns }, { - "filter_iter_wildcard_in_2nd_term", - Filter_filter_iter_wildcard_in_2nd_term + "owns_wildcard", + Has_owns_wildcard }, { - "filter_iter_wildcard_in_2nd_term_self", - Filter_filter_iter_wildcard_in_2nd_term_self + "owns_wildcard_pair", + Has_owns_wildcard_pair }, { - "filter_iter_2nd_term_self_create_id_after_filter", - Filter_filter_iter_2nd_term_self_create_id_after_filter + "has_entity", + Has_has_entity }, { - "filter_iter_any_obj", - Filter_filter_iter_any_obj + "has_entity_0", + Has_has_entity_0 }, { - "filter_iter_not_any", - Filter_filter_iter_not_any + "has_entity_0_component", + Has_has_entity_0_component }, { - "filter_iter_not_any_obj", - Filter_filter_iter_not_any_obj + "has_entity_owned", + Has_has_entity_owned }, { - "filter_iter_cascade_isa", - Filter_filter_iter_cascade_isa + "has_entity_owned_0", + Has_has_entity_owned_0 }, { - "filter_iter_cascade_childof", - Filter_filter_iter_cascade_childof + "has_entity_owned_0_component", + Has_has_entity_owned_0_component }, { - "filter_iter_superset_2_rel_instances", - Filter_filter_iter_superset_2_rel_instances + "has_wildcard", + Has_has_wildcard }, { - "filter_iter_superset_2_rel_instances_match_2nd", - Filter_filter_iter_superset_2_rel_instances_match_2nd - }, + "has_wildcard_pair", + Has_has_wildcard_pair + } +}; + +bake_test_case Count_testcases[] = { { - "filter_iter_superset_2_levels", - Filter_filter_iter_superset_2_levels + "count_empty", + Count_count_empty }, { - "filter_iter_superset_only_w_owned", - Filter_filter_iter_superset_only_w_owned + "count_w_entity_0", + Count_count_w_entity_0 }, { - "filter_iter_superset_after_add", - Filter_filter_iter_superset_after_add + "count_1_component", + Count_count_1_component }, { - "filter_iter_superset_after_remove", - Filter_filter_iter_superset_after_remove + "count_disabled", + Count_count_disabled }, { - "filter_iter_superset_after_clear", - Filter_filter_iter_superset_after_clear - }, + "count_prefab", + Count_count_prefab + } +}; + +bake_test_case Get_component_testcases[] = { { - "filter_iter_superset_after_delete", - Filter_filter_iter_superset_after_delete + "get_empty", + Get_component_get_empty }, { - "filter_iter_2_terms_superset_2_rel_instances", - Filter_filter_iter_2_terms_superset_2_rel_instances + "get_1_from_1", + Get_component_get_1_from_1 }, { - "filter_iter_2_terms_superset_2_rel_instances_match_2nd", - Filter_filter_iter_2_terms_superset_2_rel_instances_match_2nd + "get_1_from_2", + Get_component_get_1_from_2 }, { - "filter_iter_superset_parent_w_isa", - Filter_filter_iter_superset_parent_w_isa + "get_2_from_2", + Get_component_get_2_from_2 }, { - "filter_iter_superset_isa_after_remove_parent", - Filter_filter_iter_superset_isa_after_remove_parent + "get_2_from_3", + Get_component_get_2_from_3 }, { - "filter_iter_superset_isa_create_table_after_iter", - Filter_filter_iter_superset_isa_create_table_after_iter + "get_1_from_2_in_progress_from_main_stage", + Get_component_get_1_from_2_in_progress_from_main_stage }, { - "filter_iter_superset_2_relations", - Filter_filter_iter_superset_2_relations + "get_1_from_2_add_in_progress", + Get_component_get_1_from_2_add_in_progress }, { - "filter_iter_superset_2_relations_instanced", - Filter_filter_iter_superset_2_relations_instanced + "get_both_from_2_add_in_progress", + Get_component_get_both_from_2_add_in_progress }, { - "filter_iter_superset_2_relations_w_component", - Filter_filter_iter_superset_2_relations_w_component + "get_both_from_2_add_remove_in_progress", + Get_component_get_both_from_2_add_remove_in_progress }, { - "filter_iter_superset_2_relations_instanced_w_component", - Filter_filter_iter_superset_2_relations_instanced_w_component + "get_childof_component", + Get_component_get_childof_component }, { - "filter_iter_not_up_disabled", - Filter_filter_iter_not_up_disabled + "ensure_equal_get", + Get_component_ensure_equal_get }, { - "filter_iter_pair_wildcard_component", - Filter_filter_iter_pair_wildcard_component + "get_tag", + Get_component_get_tag }, { - "filter_w_10_terms", - Filter_filter_w_10_terms + "get_pair_tag", + Get_component_get_pair_tag }, { - "filter_w_10_terms_move", - Filter_filter_w_10_terms_move - }, + "get_wildcard", + Get_component_get_wildcard + } +}; + +bake_test_case Reference_testcases[] = { { - "filter_w_10_terms_copy", - Filter_filter_w_10_terms_copy + "get_ref", + Reference_get_ref }, { - "match_disabled", - Filter_match_disabled + "get_ref_after_add", + Reference_get_ref_after_add }, { - "match_prefab", - Filter_match_prefab + "get_ref_after_remove", + Reference_get_ref_after_remove }, { - "chain_term_iter", - Filter_chain_term_iter + "get_ref_after_delete", + Reference_get_ref_after_delete }, { - "chain_filter_iter", - Filter_chain_filter_iter + "get_ref_after_realloc", + Reference_get_ref_after_realloc }, { - "chain_query_iter", - Filter_chain_query_iter + "get_ref_after_realloc_w_lifecycle", + Reference_get_ref_after_realloc_w_lifecycle }, { - "chain_rule_iter", - Filter_chain_rule_iter + "get_ref_staged", + Reference_get_ref_staged }, { - "chain_iter_2_levels", - Filter_chain_iter_2_levels + "get_ref_after_new_in_stage", + Reference_get_ref_after_new_in_stage }, { - "filter_from_expr_2_terms_err", - Filter_filter_from_expr_2_terms_err + "get_ref_monitored", + Reference_get_ref_monitored }, { - "chain_term_iter_w_term_iter", - Filter_chain_term_iter_w_term_iter + "get_ref_w_low_id_tag", + Reference_get_ref_w_low_id_tag }, { - "chain_filter_iter_w_term_iter", - Filter_chain_filter_iter_w_term_iter + "get_ref_w_low_id_tag_after_add", + Reference_get_ref_w_low_id_tag_after_add }, { - "chain_w_term_iter_component", - Filter_chain_w_term_iter_component + "get_nonexisting", + Reference_get_nonexisting }, { - "chain_iter_w_or", - Filter_chain_iter_w_or - }, + "aba_table", + Reference_aba_table + } +}; + +bake_test_case Delete_testcases[] = { { - "filter_w_recycled_first", - Filter_filter_w_recycled_first + "delete_1", + Delete_delete_1 }, { - "filter_w_recycled_second", - Filter_filter_w_recycled_second + "delete_1_again", + Delete_delete_1_again }, { - "filter_w_recycled_first_and_id", - Filter_filter_w_recycled_first_and_id + "delete_recycled_tag_again", + Delete_delete_recycled_tag_again }, { - "filter_w_recycled_second_and_id", - Filter_filter_w_recycled_second_and_id + "delete_empty", + Delete_delete_empty }, { - "filter_w_recycled_first_by_name_and_id", - Filter_filter_w_recycled_first_by_name_and_id + "delete_nonexist", + Delete_delete_nonexist }, { - "filter_w_recycled_second_by_name_and_id", - Filter_filter_w_recycled_second_by_name_and_id + "delete_1st_of_3", + Delete_delete_1st_of_3 }, { - "filter_w_recycled_first_by_expr", - Filter_filter_w_recycled_first_by_expr + "delete_2nd_of_3", + Delete_delete_2nd_of_3 }, { - "filter_w_recycled_second_by_expr", - Filter_filter_w_recycled_second_by_expr + "delete_3rd_of_3", + Delete_delete_3rd_of_3 }, { - "filter_w_recycled_first_only_by_expr", - Filter_filter_w_recycled_first_only_by_expr + "delete_2_of_3", + Delete_delete_2_of_3 }, { - "term_iter_w_filter_term", - Filter_term_iter_w_filter_term + "delete_3_of_3", + Delete_delete_3_of_3 }, { - "filter_iter_w_filter_term", - Filter_filter_iter_w_filter_term + "delete_w_on_remove", + Delete_delete_w_on_remove }, { - "filter_iter_w_2_terms_1_filter", - Filter_filter_iter_w_2_terms_1_filter + "clear_1_component", + Delete_clear_1_component }, { - "filter_iter_w_3_terms_2_filter", - Filter_filter_iter_w_3_terms_2_filter + "clear_2_components", + Delete_clear_2_components }, { - "filter_iter_2_terms_filter_all", - Filter_filter_iter_2_terms_filter_all + "alive_after_delete", + Delete_alive_after_delete }, { - "filter_iter_2_terms_filter_all_w_out", - Filter_filter_iter_2_terms_filter_all_w_out + "alive_after_clear", + Delete_alive_after_clear }, { - "filter_iter_switch_term_filter", - Filter_filter_iter_switch_term_filter + "alive_after_staged_delete", + Delete_alive_after_staged_delete }, { - "filter_iter_2_terms_switch_term_filter", - Filter_filter_iter_2_terms_switch_term_filter + "alive_while_staged", + Delete_alive_while_staged }, { - "filter_iter_switch_superset", - Filter_filter_iter_switch_superset + "alive_while_staged_w_delete", + Delete_alive_while_staged_w_delete }, { - "filter_instanced_w_singleton", - Filter_filter_instanced_w_singleton + "alive_while_staged_w_delete_recycled_id", + Delete_alive_while_staged_w_delete_recycled_id }, { - "filter_instanced_w_base", - Filter_filter_instanced_w_base + "alive_after_recycle", + Delete_alive_after_recycle }, { - "filter_no_instancing_w_singleton", - Filter_filter_no_instancing_w_singleton + "delete_recycled", + Delete_delete_recycled }, { - "filter_no_instancing_w_base", - Filter_filter_no_instancing_w_base + "get_alive_for_alive", + Delete_get_alive_for_alive }, { - "filter_no_this_tag", - Filter_filter_no_this_tag + "get_alive_for_recycled", + Delete_get_alive_for_recycled }, { - "filter_no_this_component", - Filter_filter_no_this_component + "get_alive_for_not_alive", + Delete_get_alive_for_not_alive }, { - "filter_no_this_tag_2_ents", - Filter_filter_no_this_tag_2_ents + "get_alive_w_generation_for_recycled_alive", + Delete_get_alive_w_generation_for_recycled_alive }, { - "filter_no_this_component_2_ents", - Filter_filter_no_this_component_2_ents + "get_alive_w_generation_for_recycled_not_alive", + Delete_get_alive_w_generation_for_recycled_not_alive }, { - "filter_no_this_tag_2_ents_1_not", - Filter_filter_no_this_tag_2_ents_1_not + "get_alive_for_0", + Delete_get_alive_for_0 }, { - "filter_no_this_component_2_ents_1_not", - Filter_filter_no_this_component_2_ents_1_not + "get_alive_for_nonexistent", + Delete_get_alive_for_nonexistent }, { - "filter_no_this_component_1_not", - Filter_filter_no_this_component_1_not + "move_w_dtor_move", + Delete_move_w_dtor_move }, { - "filter_iter_entities_optional_flag", - Filter_filter_iter_entities_optional_flag + "move_w_dtor_no_move", + Delete_move_w_dtor_no_move }, { - "filter_iter_frame_offset", - Filter_filter_iter_frame_offset + "move_w_no_dtor_move", + Delete_move_w_no_dtor_move }, { - "filter_1_term_no_alloc", - Filter_filter_1_term_no_alloc - }, + "wrap_generation_count", + Delete_wrap_generation_count + } +}; + +bake_test_case OnDelete_testcases[] = { { - "filter_cache_size_terms_no_alloc", - Filter_filter_cache_size_terms_no_alloc + "flags", + OnDelete_flags }, { - "filter_lt_cache_size_terms_no_alloc", - Filter_filter_lt_cache_size_terms_no_alloc + "id_default", + OnDelete_id_default }, { - "move_self", - Filter_move_self + "id_remove", + OnDelete_id_remove }, { - "match_empty_tables", - Filter_match_empty_tables + "id_delete", + OnDelete_id_delete }, { - "match_empty_tables_w_no_empty_tables", - Filter_match_empty_tables_w_no_empty_tables + "relation_default", + OnDelete_relation_default }, { - "match_switch_w_switch", - Filter_match_switch_w_switch + "relation_remove", + OnDelete_relation_remove }, { - "match_switch_w_case", - Filter_match_switch_w_case + "relation_delete", + OnDelete_relation_delete }, { - "match_switch_w_case_2_terms", - Filter_match_switch_w_case_2_terms + "object_default", + OnDelete_object_default }, { - "match_case_no_case", - Filter_match_case_no_case + "object_remove", + OnDelete_object_remove }, { - "and_term", - Filter_and_term + "object_delete", + OnDelete_object_delete }, { - "or_term", - Filter_or_term + "id_throw", + OnDelete_id_throw }, { - "iter_while_creating_components", - Filter_iter_while_creating_components + "relation_throw", + OnDelete_relation_throw }, { - "iter_w_this_var_as_entity", - Filter_iter_w_this_var_as_entity + "object_throw", + OnDelete_object_throw }, { - "iter_w_this_var_as_table", - Filter_iter_w_this_var_as_table + "object_mixed", + OnDelete_object_mixed }, { - "iter_w_this_var_as_table_range", - Filter_iter_w_this_var_as_table_range + "id_remove_no_instances", + OnDelete_id_remove_no_instances }, { - "filter_wo_this_var", - Filter_filter_wo_this_var + "id_delete_no_instances", + OnDelete_id_delete_no_instances }, { - "set_this_to_table_1_term", - Filter_set_this_to_table_1_term + "id_throw_no_instances", + OnDelete_id_throw_no_instances }, { - "set_this_to_table_2_terms", - Filter_set_this_to_table_2_terms + "cyclic_self", + OnDelete_cyclic_self }, { - "set_this_to_table_1_wildcard", - Filter_set_this_to_table_1_wildcard + "nonempty_cyclic_self", + OnDelete_nonempty_cyclic_self }, { - "set_this_to_table_no_match_no_data", - Filter_set_this_to_table_no_match_no_data + "cyclic_id_default", + OnDelete_cyclic_id_default }, { - "set_this_to_table_no_match", - Filter_set_this_to_table_no_match + "cyclic_id_remove", + OnDelete_cyclic_id_remove }, { - "set_this_to_table_2_terms_no_match", - Filter_set_this_to_table_2_terms_no_match + "cyclic_id_remove_both", + OnDelete_cyclic_id_remove_both }, { - "set_this_to_empty_table", - Filter_set_this_to_empty_table + "cyclic_id_delete", + OnDelete_cyclic_id_delete }, { - "set_this_to_empty_table_w_component", - Filter_set_this_to_empty_table_w_component + "cyclic_id_delete_both", + OnDelete_cyclic_id_delete_both }, { - "set_this_to_implicit_isa_superset_match", - Filter_set_this_to_implicit_isa_superset_match + "cyclic_relation_default", + OnDelete_cyclic_relation_default }, { - "set_this_to_self_isa_superset_match", - Filter_set_this_to_self_isa_superset_match + "cyclic_relation_remove", + OnDelete_cyclic_relation_remove }, { - "set_this_to_isa_superset_match", - Filter_set_this_to_isa_superset_match + "cyclic_relation_remove_both", + OnDelete_cyclic_relation_remove_both }, { - "set_this_to_childof_superset_match", - Filter_set_this_to_childof_superset_match + "cyclic_relation_delete", + OnDelete_cyclic_relation_delete }, { - "set_this_to_superset_w_self_filter_no_match", - Filter_set_this_to_superset_w_self_filter_no_match + "cyclic_relation_delete_both", + OnDelete_cyclic_relation_delete_both }, { - "set_this_to_isa_cascade", - Filter_set_this_to_isa_cascade + "cyclic_object_default", + OnDelete_cyclic_object_default }, { - "set_this_to_childof_cascade", - Filter_set_this_to_childof_cascade + "cyclic_object_remove", + OnDelete_cyclic_object_remove }, { - "set_this_w_wildcard_2_matches", - Filter_set_this_w_wildcard_2_matches + "cyclic_object_delete", + OnDelete_cyclic_object_delete }, { - "set_this_to_entity_superset_self_has_component", - Filter_set_this_to_entity_superset_self_has_component + "cyclic_overlapping_table", + OnDelete_cyclic_overlapping_table }, { - "set_this_to_1_entity_in_table", - Filter_set_this_to_1_entity_in_table + "cyclic_overlapping_new_tables", + OnDelete_cyclic_overlapping_new_tables }, { - "oneof", - Filter_oneof + "cyclic_object_mixed", + OnDelete_cyclic_object_mixed }, { - "oneof_expr", - Filter_oneof_expr + "cyclic_storage_table", + OnDelete_cyclic_storage_table }, { - "oneof_w_mismatching_obj", - Filter_oneof_w_mismatching_obj + "cyclic_storage_table_2", + OnDelete_cyclic_storage_table_2 }, { - "oneof_w_mismatching_obj_expr", - Filter_oneof_w_mismatching_obj_expr + "cyclic_storage_table_3", + OnDelete_cyclic_storage_table_3 }, { - "oneof_wildcard", - Filter_oneof_wildcard + "cyclic_set_empty", + OnDelete_cyclic_set_empty }, { - "oneof_any", - Filter_oneof_any + "2_acyclic_relations_w_cycle", + OnDelete_2_acyclic_relations_w_cycle }, { - "flag_match_only_this", - Filter_flag_match_only_this + "remove_2_comps", + OnDelete_remove_2_comps }, { - "flag_match_only_this_w_ref", - Filter_flag_match_only_this_w_ref + "remove_2_comps_to_existing_table", + OnDelete_remove_2_comps_to_existing_table }, { - "filter_w_alloc", - Filter_filter_w_alloc + "delete_recursive", + OnDelete_delete_recursive }, { - "filter_w_short_notation", - Filter_filter_w_short_notation - } -}; - -bake_test_case FilterStr_testcases[] = { - { - "one_term", - FilterStr_one_term + "component_throw", + OnDelete_component_throw }, { - "one_term_w_inout", - FilterStr_one_term_w_inout + "remove_2_relations", + OnDelete_remove_2_relations }, { - "two_terms", - FilterStr_two_terms + "remove_object_w_2_relations", + OnDelete_remove_object_w_2_relations }, { - "two_terms_w_inout", - FilterStr_two_terms_w_inout + "remove_object_w_5_relations", + OnDelete_remove_object_w_5_relations }, { - "three_terms_w_or", - FilterStr_three_terms_w_or + "remove_object_w_50_relations", + OnDelete_remove_object_w_50_relations }, { - "three_terms_w_or_inout", - FilterStr_three_terms_w_or_inout + "remove_object_w_50_relations_3_tables", + OnDelete_remove_object_w_50_relations_3_tables }, { - "four_terms_three_w_or_inout", - FilterStr_four_terms_three_w_or_inout + "remove_object_w_3_relations_interleaved", + OnDelete_remove_object_w_3_relations_interleaved }, { - "one_term_w_pair", - FilterStr_one_term_w_pair + "remove_id_from_2_tables", + OnDelete_remove_id_from_2_tables }, { - "one_term_w_pair_entity_src", - FilterStr_one_term_w_pair_entity_src + "remove_relation_from_2_tables", + OnDelete_remove_relation_from_2_tables }, { - "one_term_w_self", - FilterStr_one_term_w_self + "remove_object_from_2_tables", + OnDelete_remove_object_from_2_tables }, { - "one_term_w_up", - FilterStr_one_term_w_up + "remove_id_and_relation", + OnDelete_remove_id_and_relation }, { - "one_term_w_0", - FilterStr_one_term_w_0 + "remove_id_and_relation_from_2_tables", + OnDelete_remove_id_and_relation_from_2_tables }, { - "one_term_w_singleton", - FilterStr_one_term_w_singleton + "stresstest_many_objects", + OnDelete_stresstest_many_objects }, { - "one_term_w_final_pair", - FilterStr_one_term_w_final_pair + "stresstest_many_relations", + OnDelete_stresstest_many_relations }, { - "one_term_w_final_dont_inherit_pair", - FilterStr_one_term_w_final_dont_inherit_pair + "stresstest_many_objects_on_delete", + OnDelete_stresstest_many_objects_on_delete }, { - "one_term_w_src_var", - FilterStr_one_term_w_src_var + "stresstest_many_relations_on_delete", + OnDelete_stresstest_many_relations_on_delete }, { - "one_term_w_first_var", - FilterStr_one_term_w_first_var + "empty_table_w_on_remove", + OnDelete_empty_table_w_on_remove }, { - "one_term_w_second_var", - FilterStr_one_term_w_second_var + "delete_table_in_on_remove_during_fini", + OnDelete_delete_table_in_on_remove_during_fini }, { - "one_term_w_first_var_entity_src", - FilterStr_one_term_w_first_var_entity_src + "delete_other_in_on_remove_during_fini", + OnDelete_delete_other_in_on_remove_during_fini }, { - "one_term_w_pair_w_0_entity", - FilterStr_one_term_w_pair_w_0_entity + "remove_id_w_role", + OnDelete_remove_id_w_role }, { - "not_term", - FilterStr_not_term + "remove_rel_w_override_pair", + OnDelete_remove_rel_w_override_pair }, { - "wildcard_term", - FilterStr_wildcard_term + "remove_obj_w_override_pair", + OnDelete_remove_obj_w_override_pair }, { - "scopes", - FilterStr_scopes - } -}; - -bake_test_case Query_testcases[] = { - { - "simple_query_existing_table", - Query_simple_query_existing_table + "remove_rel_w_override_pair_after_on_delete_target", + OnDelete_remove_rel_w_override_pair_after_on_delete_target }, { - "simple_query_2_existing_tables", - Query_simple_query_2_existing_tables + "remove_rel_w_override_pair_2_ids", + OnDelete_remove_rel_w_override_pair_2_ids }, { - "simple_query_new_table", - Query_simple_query_new_table + "remove_obj_w_override_pair_2_ids", + OnDelete_remove_obj_w_override_pair_2_ids }, { - "simple_query_2_new_tables", - Query_simple_query_2_new_tables + "remove_obj_w_override_pair_3_ids", + OnDelete_remove_obj_w_override_pair_3_ids }, { - "simple_query_existing_and_new_table", - Query_simple_query_existing_and_new_table + "remove_mixed_w_override_pair_3_ids", + OnDelete_remove_mixed_w_override_pair_3_ids }, { - "wildcard_query_existing_table", - Query_wildcard_query_existing_table + "merge_pair_component", + OnDelete_merge_pair_component }, { - "wildcard_query_new_table", - Query_wildcard_query_new_table + "delete_with_tag", + OnDelete_delete_with_tag }, { - "wildcard_query_existing_table_2_results_p_table", - Query_wildcard_query_existing_table_2_results_p_table + "delete_with_component", + OnDelete_delete_with_component }, { - "wildcard_query_new_table_2_results_p_table", - Query_wildcard_query_new_table_2_results_p_table + "delete_with_pair", + OnDelete_delete_with_pair }, { - "wildcard_query_2nd_term", - Query_wildcard_query_2nd_term + "delete_with_object_wildcard", + OnDelete_delete_with_object_wildcard }, { - "wildcard_query_2nd_term_self", - Query_wildcard_query_2nd_term_self + "delete_with_relation_wildcard", + OnDelete_delete_with_relation_wildcard }, { - "simple_query_existing_empty_table", - Query_simple_query_existing_empty_table + "delete_with_component_after_delete_cyclic_self", + OnDelete_delete_with_component_after_delete_cyclic_self }, { - "simple_query_existing_empty_type", - Query_simple_query_existing_empty_type + "delete_with_component_after_delete_cyclic", + OnDelete_delete_with_component_after_delete_cyclic }, { - "simple_query_new_empty_table", - Query_simple_query_new_empty_table + "delete_with_component_after_delete_cyclic_w_alive_moved", + OnDelete_delete_with_component_after_delete_cyclic_w_alive_moved }, { - "component_query_existing_table", - Query_component_query_existing_table + "delete_all_with_entity", + OnDelete_delete_all_with_entity }, { - "component_query_new_table", - Query_component_query_new_table + "remove_childof_entity", + OnDelete_remove_childof_entity }, { - "component_query_existing_empty_table", - Query_component_query_existing_empty_table + "remove_childof_wildcard", + OnDelete_remove_childof_wildcard }, { - "2_component_query_existing_empty_table", - Query_2_component_query_existing_empty_table + "delete_child_of_delete_with", + OnDelete_delete_child_of_delete_with }, { - "2_component_query_existing_empty_type", - Query_2_component_query_existing_empty_type + "deep_clean_64", + OnDelete_deep_clean_64 }, { - "only_optional", - Query_only_optional + "deep_clean_256", + OnDelete_deep_clean_256 }, { - "only_optional_new_empty_table", - Query_only_optional_new_empty_table + "id_w_disabled", + OnDelete_id_w_disabled }, { - "only_optional_new_empty_non_empty_table", - Query_only_optional_new_empty_non_empty_table + "id_to_no_disabled", + OnDelete_id_to_no_disabled }, { - "only_optional_new_unset_tables", - Query_only_optional_new_unset_tables + "remove_on_delete_action", + OnDelete_remove_on_delete_action }, { - "singleton_w_optional_new_empty_table", - Query_singleton_w_optional_new_empty_table + "delete_with_w_relation", + OnDelete_delete_with_w_relation }, { - "singleton_w_optional_new_empty_non_empty_table", - Query_singleton_w_optional_new_empty_non_empty_table + "delete_self_in_on_remove", + OnDelete_delete_self_in_on_remove }, { - "singleton_w_optional_new_unset_tables", - Query_singleton_w_optional_new_unset_tables + "delete_nested_in_on_remove", + OnDelete_delete_nested_in_on_remove }, { - "query_only_from_entity", - Query_query_only_from_entity + "add_deleted_in_on_remove", + OnDelete_add_deleted_in_on_remove }, { - "query_only_from_entity_no_match", - Query_query_only_from_entity_no_match + "delete_tree_w_query", + OnDelete_delete_tree_w_query }, { - "query_only_from_entity_no_match_iter_alloc", - Query_query_only_from_entity_no_match_iter_alloc + "fini_cleanup_order", + OnDelete_fini_cleanup_order }, { - "query_only_from_singleton", - Query_query_only_from_singleton + "fini_cleanup_order_root_id_w_trait", + OnDelete_fini_cleanup_order_root_id_w_trait }, { - "query_only_from_entity_match_after", - Query_query_only_from_entity_match_after + "fini_cleanup_order_entity_after_singleton", + OnDelete_fini_cleanup_order_entity_after_singleton }, { - "query_only_from_singleton_match_after", - Query_query_only_from_singleton_match_after + "fini_cleanup_order_entity_after_component", + OnDelete_fini_cleanup_order_entity_after_component }, { - "query_only_from_singleton_component_match_after", - Query_query_only_from_singleton_component_match_after + "on_delete_parent_w_in_use_id_w_remove", + OnDelete_on_delete_parent_w_in_use_id_w_remove }, { - "query_only_from_nothing", - Query_query_only_from_nothing + "on_delete_parent_w_in_use_id_w_delete", + OnDelete_on_delete_parent_w_in_use_id_w_delete }, { - "query_only_from_entity_optional", - Query_query_only_from_entity_optional + "create_after_delete_with", + OnDelete_create_after_delete_with }, { - "query_only_from_entity_no_match_optional", - Query_query_only_from_entity_no_match_optional + "delete_with_inherited_tag", + OnDelete_delete_with_inherited_tag }, { - "query_only_from_entity_or", - Query_query_only_from_entity_or + "delete_with_inherited_tag_w_query", + OnDelete_delete_with_inherited_tag_w_query }, { - "query_only_from_entity_no_match_or", - Query_query_only_from_entity_no_match_or + "delete_with_inherited_tag_w_observer", + OnDelete_delete_with_inherited_tag_w_observer }, { - "query_only_from_entity_or_change", - Query_query_only_from_entity_or_change + "delete_symmetric_relation", + OnDelete_delete_symmetric_relation }, { - "query_from_entity_or_change", - Query_query_from_entity_or_change + "delete_observed_symmetric_relation", + OnDelete_delete_observed_symmetric_relation }, { - "query_from_entity_w_superset", - Query_query_from_entity_w_superset + "nested_delete_with", + OnDelete_nested_delete_with }, { - "query_w_singleton_tag_non_instanced", - Query_query_w_singleton_tag_non_instanced + "deferred_delete_with_after_create_named", + OnDelete_deferred_delete_with_after_create_named }, { - "query_w_singleton_tag_instanced", - Query_query_w_singleton_tag_instanced + "deferred_delete_with_childof_after_create_named", + OnDelete_deferred_delete_with_childof_after_create_named }, { - "query_w_singleton_component_non_instanced", - Query_query_w_singleton_component_non_instanced + "match_marked_for_deletion", + OnDelete_match_marked_for_deletion }, { - "query_w_singleton_component_instanced", - Query_query_w_singleton_component_instanced + "delete_w_low_rel_mixed_cleanup", + OnDelete_delete_w_low_rel_mixed_cleanup }, { - "query_w_from_entity", - Query_query_w_from_entity + "delete_w_low_rel_mixed_cleanup_interleaved_ids", + OnDelete_delete_w_low_rel_mixed_cleanup_interleaved_ids }, { - "query_w_from_singleton", - Query_query_w_from_singleton + "fini_query_w_singleton_in_scope_no_module", + OnDelete_fini_query_w_singleton_in_scope_no_module }, { - "query_w_from_entity_match_after", - Query_query_w_from_entity_match_after + "fini_query_w_singleton_in_module", + OnDelete_fini_query_w_singleton_in_module }, { - "query_w_from_singleton_match_after", - Query_query_w_from_singleton_match_after + "fini_observer_w_relationship_in_scope", + OnDelete_fini_observer_w_relationship_in_scope }, { - "query_w_from_nothing", - Query_query_w_from_nothing + "add_on_delete_from_prefab", + OnDelete_add_on_delete_from_prefab }, { - "query_w_existing_switch_and_case", - Query_query_w_existing_switch_and_case + "add_on_delete_from_disabled", + OnDelete_add_on_delete_from_disabled }, { - "query_w_new_switch_and_case", - Query_query_w_new_switch_and_case + "delete_on_delete_from_prefab", + OnDelete_delete_on_delete_from_prefab }, { - "query_for_case_existing", - Query_query_for_case_existing + "delete_on_delete_from_disabled", + OnDelete_delete_on_delete_from_disabled }, { - "query_for_case_new", - Query_query_for_case_new + "delete_all_w_component_cycle", + OnDelete_delete_all_w_component_cycle }, { - "query_for_switch_filter_term", - Query_query_for_switch_filter_term + "remove_all_1", + OnDelete_remove_all_1 }, { - "query_switch_from_nothing", - Query_query_switch_from_nothing + "remove_all_2", + OnDelete_remove_all_2 }, { - "query_case_from_nothing", - Query_query_case_from_nothing + "remove_all_3", + OnDelete_remove_all_3 }, { - "query_case_inherited", - Query_query_case_inherited + "delete_with_1", + OnDelete_delete_with_1 }, { - "query_case_w_generation", - Query_query_case_w_generation + "delete_with_2", + OnDelete_delete_with_2 }, { - "query_case_w_not_alive", - Query_query_case_w_not_alive - }, + "delete_with_3", + OnDelete_delete_with_3 + } +}; + +bake_test_case Set_testcases[] = { { - "query_disabled_from_nothing", - Query_query_disabled_from_nothing + "set_empty", + Set_set_empty }, { - "query_only_2_or", - Query_query_only_2_or + "set_nonempty", + Set_set_nonempty }, { - "query_only_3_or", - Query_query_only_3_or + "set_non_empty_override", + Set_set_non_empty_override }, { - "query_2_or", - Query_query_2_or + "set_again", + Set_set_again }, { - "query_3_or", - Query_query_3_or + "set_2", + Set_set_2 }, { - "query_and_type", - Query_query_and_type + "add_set", + Set_add_set }, { - "query_or_type", - Query_query_or_type + "set_add", + Set_set_add }, { - "query_and_type_match_after", - Query_query_and_type_match_after + "set_add_other", + Set_set_add_other }, { - "query_or_type_match_after", - Query_query_or_type_match_after + "set_remove", + Set_set_remove }, { - "query_changed_after_new", - Query_query_changed_after_new + "set_remove_other", + Set_set_remove_other }, { - "query_changed_after_delete", - Query_query_changed_after_delete + "set_remove_twice", + Set_set_remove_twice }, { - "query_changed_after_add", - Query_query_changed_after_add + "set_and_new", + Set_set_and_new }, { - "query_changed_after_remove", - Query_query_changed_after_remove + "set_null", + Set_set_null }, { - "query_changed_after_set", - Query_query_changed_after_set + "ensure_new", + Set_ensure_new }, { - "query_change_after_modified", - Query_query_change_after_modified + "ensure_existing", + Set_ensure_existing }, { - "query_change_after_out_system", - Query_query_change_after_out_system + "ensure_tag_new", + Set_ensure_tag_new }, { - "query_change_after_in_system", - Query_query_change_after_in_system + "ensure_tag_existing", + Set_ensure_tag_existing }, { - "query_change_after_modified_out_term", - Query_query_change_after_modified_out_term + "ensure_tag_new_w_comp", + Set_ensure_tag_new_w_comp }, { - "query_change_check_iter", - Query_query_change_check_iter + "ensure_tag_existing_w_comp", + Set_ensure_tag_existing_w_comp }, { - "query_change_check_iter_after_skip_read", - Query_query_change_check_iter_after_skip_read + "ensure_tag_new_w_pair", + Set_ensure_tag_new_w_pair }, { - "query_change_check_iter_after_skip_write", - Query_query_change_check_iter_after_skip_write + "ensure_tag_existing_w_pair", + Set_ensure_tag_existing_w_pair }, { - "query_change_parent_term", - Query_query_change_parent_term + "get_mut_not_existing", + Set_get_mut_not_existing }, { - "query_change_prefab_term", - Query_query_change_prefab_term + "get_mut_existing", + Set_get_mut_existing }, { - "query_change_parent_term_w_tag", - Query_query_change_parent_term_w_tag + "get_mut_tag", + Set_get_mut_tag }, { - "query_change_prefab_term_w_tag", - Query_query_change_prefab_term_w_tag + "get_mut_pair", + Set_get_mut_pair }, { - "query_change_skip_non_instanced", - Query_query_change_skip_non_instanced + "get_mut_pair_second", + Set_get_mut_pair_second }, { - "query_changed_w_or", - Query_query_changed_w_or + "modified_w_on_set", + Set_modified_w_on_set }, { - "query_changed_or", - Query_query_changed_or + "modified_no_component", + Set_modified_no_component }, { - "query_changed_w_singleton", - Query_query_changed_w_singleton + "ensure_w_add_in_on_add", + Set_ensure_w_add_in_on_add }, { - "query_changed_w_only_singleton", - Query_query_changed_w_only_singleton + "ensure_w_remove_in_on_add", + Set_ensure_w_remove_in_on_add }, { - "query_changed_w_only_singleton_after_set", - Query_query_changed_w_only_singleton_after_set + "ensure_w_realloc_in_on_add", + Set_ensure_w_realloc_in_on_add }, { - "query_changed_w_only_singleton_after_out_term", - Query_query_changed_w_only_singleton_after_out_term + "emplace", + Set_emplace }, { - "query_changed_w_only_singleton_after_singleton_out_term", - Query_query_changed_w_only_singleton_after_singleton_out_term + "emplace_2", + Set_emplace_2 }, { - "query_changed_w_only_parent", - Query_query_changed_w_only_parent + "emplace_existing", + Set_emplace_existing }, { - "query_changed_w_only_parent_after_set", - Query_query_changed_w_only_parent_after_set + "emplace_w_move", + Set_emplace_w_move }, { - "query_changed_w_only_parent_after_out_term", - Query_query_changed_w_only_parent_after_out_term + "emplace_w_observer_w_add", + Set_emplace_w_observer_w_add }, { - "query_changed_w_only_parent_after_parent_out_term", - Query_query_changed_w_only_parent_after_parent_out_term - }, + "emplace_existing_w_check", + Set_emplace_existing_w_check + } +}; + +bake_test_case ReadWrite_testcases[] = { { - "query_changed_tag", - Query_query_changed_tag + "read", + ReadWrite_read }, { - "query_changed_no_source", - Query_query_changed_no_source + "nested_read", + ReadWrite_nested_read }, { - "query_changed_no_source_component", - Query_query_changed_no_source_component + "write", + ReadWrite_write }, { - "query_changed_w_not_out", - Query_query_changed_w_not_out + "nested_write", + ReadWrite_nested_write }, { - "subquery_match_existing", - Query_subquery_match_existing + "add_while_read", + ReadWrite_add_while_read }, { - "subquery_match_new", - Query_subquery_match_new + "add_while_write", + ReadWrite_add_while_write }, { - "subquery_inactive", - Query_subquery_inactive + "read_from_stage", + ReadWrite_read_from_stage }, { - "subquery_unmatch", - Query_subquery_unmatch - }, + "write_from_stage", + ReadWrite_write_from_stage + } +}; + +bake_test_case Lookup_testcases[] = { { - "subquery_rematch", - Query_subquery_rematch + "lookup", + Lookup_lookup }, { - "subquery_rematch_w_parent_optional", - Query_subquery_rematch_w_parent_optional + "lookup_component", + Lookup_lookup_component }, { - "subquery_rematch_w_sub_optional", - Query_subquery_rematch_w_sub_optional + "lookup_not_found", + Lookup_lookup_not_found }, { - "query_single_pairs", - Query_query_single_pairs + "lookup_child", + Lookup_lookup_child }, { - "query_single_instanceof", - Query_query_single_instanceof + "lookup_w_null_name", + Lookup_lookup_w_null_name }, { - "query_single_childof", - Query_query_single_childof + "lookup_after_name_reset", + Lookup_lookup_after_name_reset }, { - "query_optional_owned", - Query_query_optional_owned + "get_name", + Lookup_get_name }, { - "query_optional_shared", - Query_query_optional_shared + "get_name_no_name", + Lookup_get_name_no_name }, { - "query_optional_shared_nested", - Query_query_optional_shared_nested + "get_name_from_empty", + Lookup_get_name_from_empty }, { - "query_optional_any", - Query_query_optional_any + "lookup_by_id", + Lookup_lookup_by_id }, { - "query_rematch_optional_after_add", - Query_query_rematch_optional_after_add + "lookup_path_anonymous_parent", + Lookup_lookup_path_anonymous_parent }, { - "get_owned_tag", - Query_get_owned_tag + "lookup_path_0_parent", + Lookup_lookup_path_0_parent }, { - "get_shared_tag", - Query_get_shared_tag + "lookup_path_0_parent_w_scope", + Lookup_lookup_path_0_parent_w_scope }, { - "explicit_delete", - Query_explicit_delete + "lookup_recycled_by_id", + Lookup_lookup_recycled_by_id }, { - "get_column_size", - Query_get_column_size + "lookup_symbol_by_id", + Lookup_lookup_symbol_by_id }, { - "orphaned_query", - Query_orphaned_query + "lookup_name_w_digit", + Lookup_lookup_name_w_digit }, { - "nested_orphaned_query", - Query_nested_orphaned_query + "lookup_symbol_w_digit", + Lookup_lookup_symbol_w_digit }, { - "invalid_access_orphaned_query", - Query_invalid_access_orphaned_query + "lookup_path_w_digit", + Lookup_lookup_path_w_digit }, { - "stresstest_query_free", - Query_stresstest_query_free + "lookup_name_w_spaces", + Lookup_lookup_name_w_spaces }, { - "only_from_entity", - Query_only_from_entity + "lookup_path_w_spaces", + Lookup_lookup_path_w_spaces }, { - "only_not_from_entity", - Query_only_not_from_entity + "lookup_number", + Lookup_lookup_number }, { - "only_from_singleton", - Query_only_from_singleton + "lookup_number_path", + Lookup_lookup_number_path }, { - "only_not_from_singleton", - Query_only_not_from_singleton + "lookup_number_0", + Lookup_lookup_number_0 }, { - "get_filter", - Query_get_filter + "set_name_of_existing", + Lookup_set_name_of_existing }, { - "group_by", - Query_group_by + "change_name_of_existing", + Lookup_change_name_of_existing }, { - "group_by_w_ctx", - Query_group_by_w_ctx + "lookup_alias", + Lookup_lookup_alias }, { - "group_by_w_sort_reverse_group_creation", - Query_group_by_w_sort_reverse_group_creation + "lookup_scoped_alias", + Lookup_lookup_scoped_alias }, { - "group_by_iter_one", - Query_group_by_iter_one + "define_duplicate_alias", + Lookup_define_duplicate_alias }, { - "group_by_iter_one_all_groups", - Query_group_by_iter_one_all_groups + "lookup_null", + Lookup_lookup_null }, { - "group_by_iter_one_empty", - Query_group_by_iter_one_empty + "lookup_symbol_null", + Lookup_lookup_symbol_null }, { - "group_by_iter_one_empty_query", - Query_group_by_iter_one_empty_query + "lookup_this", + Lookup_lookup_this }, { - "group_by_iter_one_empty_table", - Query_group_by_iter_one_empty_table + "lookup_wildcard", + Lookup_lookup_wildcard }, { - "group_by_w_deleted_group_id", - Query_group_by_w_deleted_group_id + "lookup_any", + Lookup_lookup_any }, { - "group_by_callbacks", - Query_group_by_callbacks + "lookup_variable", + Lookup_lookup_variable }, { - "group_by_default_action", - Query_group_by_default_action + "lookup_path_this", + Lookup_lookup_path_this }, { - "group_table_count", - Query_group_table_count + "lookup_path_wildcard", + Lookup_lookup_path_wildcard }, { - "iter_valid", - Query_iter_valid + "lookup_path_this_from_scope", + Lookup_lookup_path_this_from_scope }, { - "query_optional_tag", - Query_query_optional_tag + "lookup_path_wildcard_from_scope", + Lookup_lookup_path_wildcard_from_scope }, { - "query_optional_shared_tag", - Query_query_optional_shared_tag + "resolve_builtin_symbols", + Lookup_resolve_builtin_symbols }, { - "query_iter_10_tags", - Query_query_iter_10_tags + "lookup_from_scope_staged", + Lookup_lookup_from_scope_staged }, { - "query_iter_20_tags", - Query_query_iter_20_tags + "lookup_core", + Lookup_lookup_core }, { - "query_iter_10_components", - Query_query_iter_10_components + "lookup_core_from_stage", + Lookup_lookup_core_from_stage }, { - "query_iter_20_components", - Query_query_iter_20_components + "lookup_custom_search_path", + Lookup_lookup_custom_search_path }, { - "iter_type_set", - Query_iter_type_set + "lookup_custom_search_path_from_stage", + Lookup_lookup_custom_search_path_from_stage }, { - "filter_term", - Query_filter_term + "lookup_custom_search_path_n_elems", + Lookup_lookup_custom_search_path_n_elems }, { - "2_terms_1_filter", - Query_2_terms_1_filter + "set_same_name", + Lookup_set_same_name }, { - "3_terms_2_filter", - Query_3_terms_2_filter + "set_same_name_after_reparenting", + Lookup_set_same_name_after_reparenting }, { - "no_instancing_w_singleton", - Query_no_instancing_w_singleton + "defer_set_name", + Lookup_defer_set_name }, { - "no_instancing_w_shared", - Query_no_instancing_w_shared + "defer_set_same_name", + Lookup_defer_set_same_name }, { - "query_iter_frame_offset", - Query_query_iter_frame_offset + "lookup_invalid_digit", + Lookup_lookup_invalid_digit }, { - "add_singleton_after_query", - Query_add_singleton_after_query + "lookup_child_invalid_digit", + Lookup_lookup_child_invalid_digit }, { - "query_w_component_from_parent_from_non_this", - Query_query_w_component_from_parent_from_non_this + "lookup_digit_from_wrong_scope", + Lookup_lookup_digit_from_wrong_scope }, { - "create_query_while_pending", - Query_create_query_while_pending + "lookup_core_entity_from_wrong_scope", + Lookup_lookup_core_entity_from_wrong_scope }, { - "empty_query", - Query_empty_query + "lookup_alias_w_number", + Lookup_lookup_alias_w_number }, { - "parent_cascade", - Query_parent_cascade + "lookup_symbol_path", + Lookup_lookup_symbol_path }, { - "existing_custom_rel_cascade", - Query_existing_custom_rel_cascade + "lookup_name_escaped_sep", + Lookup_lookup_name_escaped_sep }, { - "new_custom_rel_cascade", - Query_new_custom_rel_cascade + "lookup_path_escaped_sep", + Lookup_lookup_path_escaped_sep }, { - "cascade_w_2_depths", - Query_cascade_w_2_depths + "lookup_name_63_chars", + Lookup_lookup_name_63_chars }, { - "cascade_w_3_depths", - Query_cascade_w_3_depths + "lookup_name_64_chars", + Lookup_lookup_name_64_chars }, { - "cascade_w_2_depths_desc", - Query_cascade_w_2_depths_desc + "lookup_name_65_chars", + Lookup_lookup_name_65_chars }, { - "cascade_w_3_depths_desc", - Query_cascade_w_3_depths_desc + "lookup_path_63_chars", + Lookup_lookup_path_63_chars }, { - "not_pair_relation_wildcard", - Query_not_pair_relation_wildcard + "lookup_path_64_chars", + Lookup_lookup_path_64_chars }, { - "not_pair_object_wildcard", - Query_not_pair_object_wildcard - }, + "lookup_path_65_chars", + Lookup_lookup_path_65_chars + } +}; + +bake_test_case Singleton_testcases[] = { { - "two_pair_wildcards_one_not", - Query_two_pair_wildcards_one_not + "add_singleton", + Singleton_add_singleton }, { - "two_pair_wildcards_one_not_any", - Query_two_pair_wildcards_one_not_any + "remove_singleton", + Singleton_remove_singleton }, { - "implicit_existing_isa_superset", - Query_implicit_existing_isa_superset + "set_get_singleton", + Singleton_set_get_singleton }, { - "implicit_new_isa_superset", - Query_implicit_new_isa_superset + "ensure_singleton", + Singleton_ensure_singleton }, { - "isa_superset", - Query_isa_superset - }, + "singleton_system", + Singleton_singleton_system + } +}; + +bake_test_case Clone_testcases[] = { { - "isa_superset_2_lvls", - Query_isa_superset_2_lvls + "empty", + Clone_empty }, { - "isa_superset_3_lvls", - Query_isa_superset_3_lvls + "empty_w_value", + Clone_empty_w_value }, { - "isa_superset_2_lvls_owned", - Query_isa_superset_2_lvls_owned + "null", + Clone_null }, { - "isa_superset_3_lvls_owned", - Query_isa_superset_3_lvls_owned + "null_w_value", + Clone_null_w_value }, { - "isa_superset_owned_empty_table_after_match", - Query_isa_superset_owned_empty_table_after_match + "1_component", + Clone_1_component }, { - "isa_self_superset", - Query_isa_self_superset + "2_component", + Clone_2_component }, { - "childof_superset", - Query_childof_superset + "1_component_w_value", + Clone_1_component_w_value }, { - "superset_2_targets", - Query_superset_2_targets + "1_component_w_lifecycle", + Clone_1_component_w_lifecycle }, { - "superset_2_relations", - Query_superset_2_relations + "2_component_w_value", + Clone_2_component_w_value }, { - "superset_2_relations_instanced", - Query_superset_2_relations_instanced + "3_component", + Clone_3_component }, { - "superset_2_relations_w_component", - Query_superset_2_relations_w_component + "3_component_w_value", + Clone_3_component_w_value }, { - "superset_2_relations_instanced_w_component", - Query_superset_2_relations_instanced_w_component + "tag", + Clone_tag }, { - "parent", - Query_parent + "tag_w_value", + Clone_tag_w_value }, { - "existing_isa_cascade", - Query_existing_isa_cascade + "1_tag_1_component", + Clone_1_tag_1_component }, { - "new_isa_cascade", - Query_new_isa_cascade + "1_tag_1_component_w_value", + Clone_1_tag_1_component_w_value }, { - "childof_cascade", - Query_childof_cascade - }, + "clone_w_name", + Clone_clone_w_name + } +}; + +bake_test_case ComponentLifecycle_testcases[] = { { - "isa_rematch", - Query_isa_rematch + "ctor_on_add", + ComponentLifecycle_ctor_on_add }, { - "childof_rematch", - Query_childof_rematch + "ctor_on_new", + ComponentLifecycle_ctor_on_new }, { - "isa_unmatch", - Query_isa_unmatch + "dtor_on_remove", + ComponentLifecycle_dtor_on_remove }, { - "childof_unmatch", - Query_childof_unmatch + "dtor_on_delete", + ComponentLifecycle_dtor_on_delete }, { - "isa_rematch_2_lvls", - Query_isa_rematch_2_lvls + "copy_on_set", + ComponentLifecycle_copy_on_set }, { - "childof_rematch_2_lvls", - Query_childof_rematch_2_lvls + "copy_on_override", + ComponentLifecycle_copy_on_override }, { - "cascade_rematch_2_lvls", - Query_cascade_rematch_2_lvls + "copy_on_clone", + ComponentLifecycle_copy_on_clone }, { - "cascade_rematch_2_lvls_2_relations", - Query_cascade_rematch_2_lvls_2_relations + "no_copy_on_move", + ComponentLifecycle_no_copy_on_move }, { - "cascade_topological", - Query_cascade_topological + "ctor_on_tag", + ComponentLifecycle_ctor_on_tag }, { - "cascade_desc_rematch_2_lvls", - Query_cascade_desc_rematch_2_lvls + "dtor_on_tag", + ComponentLifecycle_dtor_on_tag }, { - "cascade_desc_rematch_2_lvls_2_relations", - Query_cascade_desc_rematch_2_lvls_2_relations + "copy_on_tag", + ComponentLifecycle_copy_on_tag }, { - "cascade_desc_topological", - Query_cascade_desc_topological + "move_on_tag", + ComponentLifecycle_move_on_tag }, { - "childof_rematch_from_isa", - Query_childof_rematch_from_isa + "merge_to_different_table", + ComponentLifecycle_merge_to_different_table }, { - "rematch_optional_ref", - Query_rematch_optional_ref + "merge_to_new_table", + ComponentLifecycle_merge_to_new_table }, { - "rematch_optional_ref_w_2_refs", - Query_rematch_optional_ref_w_2_refs + "delete_in_stage", + ComponentLifecycle_delete_in_stage }, { - "rematch_optional_ref_tag_w_ref_component", - Query_rematch_optional_ref_tag_w_ref_component + "ctor_on_add_pair", + ComponentLifecycle_ctor_on_add_pair }, { - "match_query_expr_from_scope", - Query_match_query_expr_from_scope + "ctor_on_add_pair_tag", + ComponentLifecycle_ctor_on_add_pair_tag }, { - "query_long_or_w_ref", - Query_query_long_or_w_ref + "ctor_on_move_pair", + ComponentLifecycle_ctor_on_move_pair }, { - "0_query", - Query_0_query + "move_on_realloc", + ComponentLifecycle_move_on_realloc }, { - "query_w_pair_id_and_subj", - Query_query_w_pair_id_and_subj + "move_on_bulk_new", + ComponentLifecycle_move_on_bulk_new }, { - "table_count", - Query_table_count + "on_add_on_bulk_new", + ComponentLifecycle_on_add_on_bulk_new }, { - "empty_table_count", - Query_empty_table_count + "move_on_delete", + ComponentLifecycle_move_on_delete }, { - "entity_count", - Query_entity_count + "move_dtor_on_delete", + ComponentLifecycle_move_dtor_on_delete }, { - "rematch_after_delete_inherited_tag", - Query_rematch_after_delete_inherited_tag + "copy_on_override_pair", + ComponentLifecycle_copy_on_override_pair }, { - "rematch_after_delete_rel_of_inherited_pair", - Query_rematch_after_delete_rel_of_inherited_pair + "copy_on_override_pair_tag", + ComponentLifecycle_copy_on_override_pair_tag }, { - "rematch_after_delete_obj_of_inherited_pair", - Query_rematch_after_delete_obj_of_inherited_pair + "copy_on_set_pair", + ComponentLifecycle_copy_on_set_pair }, { - "rematch_empty_table_w_superset", - Query_rematch_empty_table_w_superset + "copy_on_set_pair_tag", + ComponentLifecycle_copy_on_set_pair_tag }, { - "query_w_short_notation", - Query_query_w_short_notation + "allow_lifecycle_overwrite_equal_callbacks", + ComponentLifecycle_allow_lifecycle_overwrite_equal_callbacks }, { - "query_w_invalid_filter_flag", - Query_query_w_invalid_filter_flag + "set_lifecycle_after_trigger", + ComponentLifecycle_set_lifecycle_after_trigger }, { - "query_next_table", - Query_query_next_table + "valid_entity_in_dtor_after_delete", + ComponentLifecycle_valid_entity_in_dtor_after_delete }, { - "query_next_table_w_changed", - Query_query_next_table_w_changed + "ctor_w_emplace", + ComponentLifecycle_ctor_w_emplace }, { - "query_next_table_w_populate", - Query_query_next_table_w_populate + "ctor_w_emplace_defer", + ComponentLifecycle_ctor_w_emplace_defer }, { - "query_next_table_w_skip", - Query_query_next_table_w_skip + "ctor_w_emplace_defer_use_move_ctor", + ComponentLifecycle_ctor_w_emplace_defer_use_move_ctor }, { - "query_next_table_w_populate_first_changed", - Query_query_next_table_w_populate_first_changed + "ctor_w_emplace_defer_twice", + ComponentLifecycle_ctor_w_emplace_defer_twice }, { - "query_next_table_w_populate_last_changed", - Query_query_next_table_w_populate_last_changed + "ctor_w_emplace_defer_existing", + ComponentLifecycle_ctor_w_emplace_defer_existing }, { - "query_next_table_w_populate_skip_first", - Query_query_next_table_w_populate_skip_first + "on_add_w_emplace", + ComponentLifecycle_on_add_w_emplace }, { - "query_next_table_w_populate_skip_last", - Query_query_next_table_w_populate_skip_last + "on_add_w_emplace_existing", + ComponentLifecycle_on_add_w_emplace_existing }, { - "create_query_existing_query_entity", - Query_create_query_existing_query_entity + "on_add_w_emplace_defer", + ComponentLifecycle_on_add_w_emplace_defer }, { - "query_for_recycled_pair", - Query_query_for_recycled_pair + "merge_async_stage_w_emplace", + ComponentLifecycle_merge_async_stage_w_emplace }, { - "query_w_singleton_w_rule_iter", - Query_query_w_singleton_w_rule_iter + "merge_async_stage_w_emplace_to_deferred_world", + ComponentLifecycle_merge_async_stage_w_emplace_to_deferred_world }, { - "query_w_singleton_nested_iter", - Query_query_w_singleton_nested_iter + "emplace_grow_w_existing_component", + ComponentLifecycle_emplace_grow_w_existing_component }, { - "query_w_singleton_interleaved_iter", - Query_query_w_singleton_interleaved_iter + "dtor_on_fini", + ComponentLifecycle_dtor_on_fini }, { - "recycled_component_id", - Query_recycled_component_id + "valid_type_in_dtor_on_fini", + ComponentLifecycle_valid_type_in_dtor_on_fini }, { - "set_get_context", - Query_set_get_context + "valid_other_type_of_entity_in_dtor_on_fini", + ComponentLifecycle_valid_other_type_of_entity_in_dtor_on_fini }, { - "set_get_binding_context", - Query_set_get_binding_context + "delete_in_dtor_other_type_on_fini", + ComponentLifecycle_delete_in_dtor_other_type_on_fini }, { - "set_get_context_w_free", - Query_set_get_context_w_free + "delete_in_dtor_other_type_on_delete_parent", + ComponentLifecycle_delete_in_dtor_other_type_on_delete_parent }, { - "set_get_binding_context_w_free", - Query_set_get_binding_context_w_free + "delete_in_dtor_other_type_on_delete", + ComponentLifecycle_delete_in_dtor_other_type_on_delete }, { - "set_this", - Query_set_this + "delete_self_in_dtor_on_delete", + ComponentLifecycle_delete_self_in_dtor_on_delete }, { - "set_this_no_match", - Query_set_this_no_match + "on_set_after_set", + ComponentLifecycle_on_set_after_set }, { - "set_this_is_true", - Query_set_this_is_true + "on_add_after_new", + ComponentLifecycle_on_add_after_new }, { - "set_this_w_wildcard", - Query_set_this_w_wildcard + "on_add_after_add", + ComponentLifecycle_on_add_after_add }, { - "singleton_w_inout_none", - Query_singleton_w_inout_none + "on_add_after_set", + ComponentLifecycle_on_add_after_set }, { - "singleton_w_inout_none_or", - Query_singleton_w_inout_none_or + "on_remove_after_remove", + ComponentLifecycle_on_remove_after_remove }, { - "component_w_inout_none_or", - Query_component_w_inout_none_or - } -}; - -bake_test_case Iter_testcases[] = { - { - "page_iter_0_0", - Iter_page_iter_0_0 + "on_remove_after_clear", + ComponentLifecycle_on_remove_after_clear }, { - "page_iter_1_0", - Iter_page_iter_1_0 + "on_remove_after_delete", + ComponentLifecycle_on_remove_after_delete }, { - "page_iter_0_1", - Iter_page_iter_0_1 + "free_component_new_id_while_fini", + ComponentLifecycle_free_component_new_id_while_fini }, { - "page_iter_n_0", - Iter_page_iter_n_0 + "dtor_component_new_id_while_fini", + ComponentLifecycle_dtor_component_new_id_while_fini }, { - "page_iter_0_n", - Iter_page_iter_0_n + "free_component_new_pair_id_while_fini", + ComponentLifecycle_free_component_new_pair_id_while_fini }, { - "page_iter_m_n", - Iter_page_iter_m_n + "dtor_component_new_pair_id_while_fini", + ComponentLifecycle_dtor_component_new_pair_id_while_fini }, { - "page_iter_skip_1_table", - Iter_page_iter_skip_1_table + "free_component_new_obj_pair_id_while_fini", + ComponentLifecycle_free_component_new_obj_pair_id_while_fini }, { - "page_iter_skip_2_tables", - Iter_page_iter_skip_2_tables + "dtor_component_new_obj_pair_id_while_fini", + ComponentLifecycle_dtor_component_new_obj_pair_id_while_fini }, { - "worker_iter_1", - Iter_worker_iter_1 + "ctor_move_dtor_after_resize", + ComponentLifecycle_ctor_move_dtor_after_resize }, { - "worker_iter_2", - Iter_worker_iter_2 + "ctx_free", + ComponentLifecycle_ctx_free }, { - "worker_iter_3", - Iter_worker_iter_3 + "binding_ctx_free", + ComponentLifecycle_binding_ctx_free }, { - "worker_iter_4", - Iter_worker_iter_4 + "lifecycle_ctx_free", + ComponentLifecycle_lifecycle_ctx_free }, { - "paged_iter_w_shared_comp", - Iter_paged_iter_w_shared_comp + "ctx_free_after_delete_component", + ComponentLifecycle_ctx_free_after_delete_component }, { - "worker_iter_w_shared_comp", - Iter_worker_iter_w_shared_comp + "binding_ctx_free_after_delete_component", + ComponentLifecycle_binding_ctx_free_after_delete_component }, { - "paged_iter_w_task_query", - Iter_paged_iter_w_task_query + "lifecycle_ctx_free_after_delete_component", + ComponentLifecycle_lifecycle_ctx_free_after_delete_component }, { - "worker_iter_w_task_query", - Iter_worker_iter_w_task_query + "on_add_ctx", + ComponentLifecycle_on_add_ctx }, { - "worker_iter_w_singleton", - Iter_worker_iter_w_singleton + "on_remove_ctx", + ComponentLifecycle_on_remove_ctx }, { - "worker_iter_w_singleton_component", - Iter_worker_iter_w_singleton_component + "on_set_ctx", + ComponentLifecycle_on_set_ctx }, { - "worker_iter_w_singleton_instanced", - Iter_worker_iter_w_singleton_instanced + "on_add_w_existing_component", + ComponentLifecycle_on_add_w_existing_component }, { - "worker_iter_w_singleton_component_instanced", - Iter_worker_iter_w_singleton_component_instanced + "on_remove_w_existing_component", + ComponentLifecycle_on_remove_w_existing_component }, { - "paged_iter_w_singleton", - Iter_paged_iter_w_singleton + "component_init_set_hooks", + ComponentLifecycle_component_init_set_hooks }, { - "paged_iter_w_singleton_component", - Iter_paged_iter_w_singleton_component + "component_init_name_from_type_info", + ComponentLifecycle_component_init_name_from_type_info }, { - "paged_iter_w_singleton_instanced", - Iter_paged_iter_w_singleton_instanced + "component_init_scoped_name_from_type_info", + ComponentLifecycle_component_init_scoped_name_from_type_info }, { - "paged_iter_w_singleton_component_instanced", - Iter_paged_iter_w_singleton_component_instanced + "component_init_w_recycled_id", + ComponentLifecycle_component_init_w_recycled_id }, { - "count", - Iter_count + "component_init_w_recycled_non_component_id", + ComponentLifecycle_component_init_w_recycled_non_component_id }, { - "iter_restore_stack_iter", - Iter_iter_restore_stack_iter + "on_add_after_ctor_w_add", + ComponentLifecycle_on_add_after_ctor_w_add }, { - "interleaved_iter", - Iter_interleaved_iter + "on_add_after_ctor_w_add_to", + ComponentLifecycle_on_add_after_ctor_w_add_to }, { - "get_first", - Iter_get_first + "with_before_hooks", + ComponentLifecycle_with_before_hooks }, { - "page_iter_w_only_tag", - Iter_page_iter_w_only_tag + "with_component_on_add", + ComponentLifecycle_with_component_on_add }, { - "worker_iter_w_only_tag", - Iter_worker_iter_w_only_tag + "move_ctor_on_move", + ComponentLifecycle_move_ctor_on_move }, { - "page_iter_w_inout_none", - Iter_page_iter_w_inout_none + "ptr_to_self", + ComponentLifecycle_ptr_to_self }, { - "worker_iter_w_inout_none", - Iter_worker_iter_w_inout_none + "ctor_move_dtor_from_move_ctor", + ComponentLifecycle_ctor_move_dtor_from_move_ctor }, { - "page_iter_w_ctx", - Iter_page_iter_w_ctx + "on_add_hook_check_offset", + ComponentLifecycle_on_add_hook_check_offset }, { - "page_iter_w_binding_ctx", - Iter_page_iter_w_binding_ctx + "on_remove_hook_check_offset", + ComponentLifecycle_on_remove_hook_check_offset }, { - "worker_iter_w_ctx", - Iter_worker_iter_w_ctx + "on_set_hook_check_offset", + ComponentLifecycle_on_set_hook_check_offset }, { - "worker_iter_w_binding_ctx", - Iter_worker_iter_w_binding_ctx + "on_set_hook_on_override", + ComponentLifecycle_on_set_hook_on_override }, { - "column_index_owned", - Iter_column_index_owned + "on_set_hook_on_auto_override", + ComponentLifecycle_on_set_hook_on_auto_override }, { - "column_index_shared", - Iter_column_index_shared + "batched_set_new_component_w_lifecycle", + ComponentLifecycle_batched_set_new_component_w_lifecycle }, { - "column_index_not", - Iter_column_index_not + "batched_ensure_new_component_w_lifecycle", + ComponentLifecycle_batched_ensure_new_component_w_lifecycle }, { - "page_iter_w_fini", - Iter_page_iter_w_fini + "on_nested_prefab_copy_test_invokes_copy_count", + ComponentLifecycle_on_nested_prefab_copy_test_invokes_copy_count }, { - "worker_iter_w_fini", - Iter_worker_iter_w_fini + "no_move_no_move_ctor_with_move_dtor_with_ctor_move_dtor", + ComponentLifecycle_no_move_no_move_ctor_with_move_dtor_with_ctor_move_dtor }, { - "rule_page_iter_w_fini", - Iter_rule_page_iter_w_fini + "new_w_table_ctor", + ComponentLifecycle_new_w_table_ctor }, { - "rule_worker_iter_w_fini", - Iter_rule_worker_iter_w_fini + "new_w_table_on_add_hook", + ComponentLifecycle_new_w_table_on_add_hook }, { - "to_str_before_next", - Iter_to_str_before_next + "count_in_on_add", + ComponentLifecycle_count_in_on_add }, { - "to_str", - Iter_to_str + "count_in_on_remove", + ComponentLifecycle_count_in_on_remove } }; @@ -9579,6 +7048,10 @@ bake_test_case Pairs_testcases[] = { "add_symmetric_recycled_relation", Pairs_add_symmetric_recycled_relation }, + { + "symmetric_w_childof", + Pairs_symmetric_w_childof + }, { "with", Pairs_with @@ -9670,6 +7143,34 @@ bake_test_case Pairs_testcases[] = { { "set_w_recycled_tgt", Pairs_set_w_recycled_tgt + }, + { + "force_relationship_on_component", + Pairs_force_relationship_on_component + }, + { + "force_relationship_on_target", + Pairs_force_relationship_on_target + }, + { + "force_relationship_on_target_trait", + Pairs_force_relationship_on_target_trait + }, + { + "force_relationship_on_relationship", + Pairs_force_relationship_on_relationship + }, + { + "force_target_on_component", + Pairs_force_target_on_component + }, + { + "force_target_on_relationship", + Pairs_force_target_on_relationship + }, + { + "force_target_on_target", + Pairs_force_target_on_target } }; @@ -9702,6 +7203,10 @@ bake_test_case Trigger_testcases[] = { "on_add_wildcard", Trigger_on_add_wildcard }, + { + "on_add_wildcard_after_table", + Trigger_on_add_wildcard_after_table + }, { "on_add_pair", Trigger_on_add_pair @@ -9998,10 +7503,6 @@ bake_test_case Trigger_testcases[] = { "on_add_base_2_entities_filter", Trigger_on_add_base_2_entities_filter }, - { - "on_set_base_w_value_2_entities", - Trigger_on_set_base_w_value_2_entities - }, { "on_set_base_w_value_2_entities_instanced", Trigger_on_set_base_w_value_2_entities_instanced @@ -10114,6 +7615,18 @@ bake_test_case Trigger_testcases[] = { "on_set_superset_auto_override", Trigger_on_set_superset_auto_override }, + { + "on_set_self_on_instantiate_override", + Trigger_on_set_self_on_instantiate_override + }, + { + "on_set_self_up_on_instantiate_override", + Trigger_on_set_self_up_on_instantiate_override + }, + { + "on_set_up_on_instantiate_override", + Trigger_on_set_up_on_instantiate_override + }, { "not_only", Trigger_not_only @@ -10141,14 +7654,106 @@ bake_test_case Trigger_testcases[] = { { "on_set_superset_after_filter_observer_w_on_add_2", Trigger_on_set_superset_after_filter_observer_w_on_add_2 - }, - { - "propagate_w_union_pair", - Trigger_propagate_w_union_pair } }; bake_test_case Observer_testcases[] = { + { + "on_add_before_edge", + Observer_on_add_before_edge + }, + { + "on_add_after_edge", + Observer_on_add_after_edge + }, + { + "on_add_wildcard_before_edge", + Observer_on_add_wildcard_before_edge + }, + { + "on_add_wildcard_after_edge", + Observer_on_add_wildcard_after_edge + }, + { + "on_add_R_wildcard_before_edge", + Observer_on_add_R_wildcard_before_edge + }, + { + "on_add_R_wildcard_after_edge", + Observer_on_add_R_wildcard_after_edge + }, + { + "on_add_wildcard_T_before_edge", + Observer_on_add_wildcard_T_before_edge + }, + { + "on_add_wildcard_T_after_edge", + Observer_on_add_wildcard_T_after_edge + }, + { + "on_add_wildcard_wildcard_before_edge", + Observer_on_add_wildcard_wildcard_before_edge + }, + { + "on_add_wildcard_wildcard_after_edge", + Observer_on_add_wildcard_wildcard_after_edge + }, + { + "on_add_any_before_edge", + Observer_on_add_any_before_edge + }, + { + "on_add_any_after_edge", + Observer_on_add_any_after_edge + }, + { + "on_remove_before_edge", + Observer_on_remove_before_edge + }, + { + "on_remove_after_edge", + Observer_on_remove_after_edge + }, + { + "on_remove_wildcard_before_edge", + Observer_on_remove_wildcard_before_edge + }, + { + "on_remove_wildcard_after_edge", + Observer_on_remove_wildcard_after_edge + }, + { + "on_remove_R_wildcard_before_edge", + Observer_on_remove_R_wildcard_before_edge + }, + { + "on_remove_R_wildcard_after_edge", + Observer_on_remove_R_wildcard_after_edge + }, + { + "on_remove_wildcard_T_before_edge", + Observer_on_remove_wildcard_T_before_edge + }, + { + "on_remove_wildcard_T_after_edge", + Observer_on_remove_wildcard_T_after_edge + }, + { + "on_remove_wildcard_wildcard_before_edge", + Observer_on_remove_wildcard_wildcard_before_edge + }, + { + "on_remove_wildcard_wildcard_after_edge", + Observer_on_remove_wildcard_wildcard_after_edge + }, + { + "on_remove_any_before_edge", + Observer_on_remove_any_before_edge + }, + { + "on_remove_any_after_edge", + Observer_on_remove_any_after_edge + }, { "2_terms_w_on_add", Observer_2_terms_w_on_add @@ -10165,6 +7770,14 @@ bake_test_case Observer_testcases[] = { "2_terms_w_on_remove_value", Observer_2_terms_w_on_remove_value }, + { + "2_terms_w_on_set_value_self_up", + Observer_2_terms_w_on_set_value_self_up + }, + { + "2_terms_w_on_remove_value_self_up", + Observer_2_terms_w_on_remove_value_self_up + }, { "2_terms_w_on_add_2nd", Observer_2_terms_w_on_add_2nd @@ -10353,6 +7966,22 @@ bake_test_case Observer_testcases[] = { "or_from", Observer_or_from }, + { + "and_from_empty", + Observer_and_from_empty + }, + { + "or_from_empty", + Observer_or_from_empty + }, + { + "and_from_empty_w_tag", + Observer_and_from_empty_w_tag + }, + { + "or_from_empty_w_tag", + Observer_or_from_empty_w_tag + }, { "notify_propagated_twice", Observer_notify_propagated_twice @@ -10381,6 +8010,62 @@ bake_test_case Observer_testcases[] = { "on_add_yield_existing_wildcard_multi_w_wildcard_pivot", Observer_on_add_yield_existing_wildcard_multi_w_wildcard_pivot }, + { + "on_remove_yield_existing", + Observer_on_remove_yield_existing + }, + { + "on_remove_yield_existing_2_tables", + Observer_on_remove_yield_existing_2_tables + }, + { + "on_remove_yield_existing_2_terms", + Observer_on_remove_yield_existing_2_terms + }, + { + "on_remove_yield_existing_wildcard", + Observer_on_remove_yield_existing_wildcard + }, + { + "on_remove_yield_existing_wildcard_multi", + Observer_on_remove_yield_existing_wildcard_multi + }, + { + "on_remove_yield_existing_wildcard_multi_w_wildcard_pivot", + Observer_on_remove_yield_existing_wildcard_multi_w_wildcard_pivot + }, + { + "on_add_remove_yield_existing", + Observer_on_add_remove_yield_existing + }, + { + "on_add_remove_yield_existing_flags", + Observer_on_add_remove_yield_existing_flags + }, + { + "on_add_remove_no_on_add_yield_existing", + Observer_on_add_remove_no_on_add_yield_existing + }, + { + "on_add_remove_no_on_remove_yield_existing", + Observer_on_add_remove_no_on_remove_yield_existing + }, + { + "yield_existing_flags_w_multi_observer", + Observer_yield_existing_flags_w_multi_observer + }, + { + "yield_on_create_without_on_add", + Observer_yield_on_create_without_on_add + }, + { + "yield_on_delete_without_on_remove", + Observer_yield_on_delete_without_on_remove + }, + { + "yield_existing_w_not_first_term", + Observer_yield_existing_w_not_first_term + }, { "observer_superset_wildcard", Observer_observer_superset_wildcard @@ -10398,32 +8083,56 @@ bake_test_case Observer_testcases[] = { Observer_on_set_w_tag }, { - "mixed_on_set_w_tag", - Observer_mixed_on_set_w_tag + "mixed_on_set_w_tag", + Observer_mixed_on_set_w_tag + }, + { + "mixed_un_set_w_tag", + Observer_mixed_un_set_w_tag + }, + { + "match_base_w_id_at_offset", + Observer_match_base_w_id_at_offset + }, + { + "custom_run_action", + Observer_custom_run_action + }, + { + "custom_run_action_2_terms", + Observer_custom_run_action_2_terms + }, + { + "custom_run_action_w_iter_next_2_terms", + Observer_custom_run_action_w_iter_next_2_terms + }, + { + "custom_run_action_w_field", + Observer_custom_run_action_w_field }, { - "mixed_un_set_w_tag", - Observer_mixed_un_set_w_tag + "custom_run_action_w_2_fields", + Observer_custom_run_action_w_2_fields }, { - "match_base_w_id_at_offset", - Observer_match_base_w_id_at_offset + "custom_run_w_yield_existing", + Observer_custom_run_w_yield_existing }, { - "custom_run_action", - Observer_custom_run_action + "custom_run_w_yield_existing_1_field", + Observer_custom_run_w_yield_existing_1_field }, { - "custom_run_action_w_iter_next", - Observer_custom_run_action_w_iter_next + "custom_run_w_yield_existing_1_field_w_callback", + Observer_custom_run_w_yield_existing_1_field_w_callback }, { - "custom_run_action_2_terms", - Observer_custom_run_action_2_terms + "custom_run_w_yield_existing_2_fields", + Observer_custom_run_w_yield_existing_2_fields }, { - "custom_run_action_w_iter_next_2_terms", - Observer_custom_run_action_w_iter_next_2_terms + "custom_run_w_yield_existing_2_fields_w_callback", + Observer_custom_run_w_yield_existing_2_fields_w_callback }, { "read_in_on_remove_after_add_other_w_not", @@ -10457,6 +8166,58 @@ bake_test_case Observer_testcases[] = { "propagate_match_relationship_w_up", Observer_propagate_match_relationship_w_up }, + { + "propagate_isa_of_parent_add", + Observer_propagate_isa_of_parent_add + }, + { + "propagate_isa_of_parent_remove", + Observer_propagate_isa_of_parent_remove + }, + { + "propagate_isa_of_parent_set", + Observer_propagate_isa_of_parent_set + }, + { + "propagate_add_childof_of_parent", + Observer_propagate_add_childof_of_parent + }, + { + "propagate_add_childof_of_parent_w_siblings", + Observer_propagate_add_childof_of_parent_w_siblings + }, + { + "propagate_add_childof_w_parent_w_base", + Observer_propagate_add_childof_w_parent_w_base + }, + { + "propagate_remove_childof_w_parent_w_base", + Observer_propagate_remove_childof_w_parent_w_base + }, + { + "propagate_remove_childof_of_parent", + Observer_propagate_remove_childof_of_parent + }, + { + "propagate_add_isa_of_parent", + Observer_propagate_add_isa_of_parent + }, + { + "propagate_remove_isa_of_parent", + Observer_propagate_remove_isa_of_parent + }, + { + "propagate_add_childof_of_base", + Observer_propagate_add_childof_of_base + }, + { + "propagate_remove_childof_of_base", + Observer_propagate_remove_childof_of_base + }, + { + "emit_for_parent_w_prefab_child_and_instance", + Observer_emit_for_parent_w_prefab_child_and_instance + }, { "observer_w_2_fixed_src", Observer_observer_w_2_fixed_src @@ -10538,16 +8299,216 @@ bake_test_case Observer_testcases[] = { Observer_wildcard_propagate_w_other_table }, { - "add_in_yield_existing", - Observer_add_in_yield_existing + "add_in_on_add_yield_existing", + Observer_add_in_on_add_yield_existing }, { - "add_in_yield_existing_multi", - Observer_add_in_yield_existing_multi + "add_in_on_add_yield_existing_multi", + Observer_add_in_on_add_yield_existing_multi }, { - "emit_for_parent_w_prefab_child_and_instance", - Observer_emit_for_parent_w_prefab_child_and_instance + "add_in_on_remove_yield_existing", + Observer_add_in_on_remove_yield_existing + }, + { + "add_in_on_remove_yield_existing_multi", + Observer_add_in_on_remove_yield_existing_multi + }, + { + "disable_observer", + Observer_disable_observer + }, + { + "disable_observer_module", + Observer_disable_observer_module + }, + { + "disable_observer_module_nested", + Observer_disable_observer_module_nested + }, + { + "disable_observer_and_module", + Observer_disable_observer_and_module + }, + { + "disable_multi_observer", + Observer_disable_multi_observer + }, + { + "disable_multi_observer_module", + Observer_disable_multi_observer_module + }, + { + "disable_multi_observer_module_nested", + Observer_disable_multi_observer_module_nested + }, + { + "disable_multi_observer_and_module", + Observer_disable_multi_observer_and_module + }, + { + "tag_w_on_set_and_on_add", + Observer_tag_w_on_set_and_on_add + }, + { + "tag_w_on_set_and_on_add_reverse", + Observer_tag_w_on_set_and_on_add_reverse + }, + { + "on_add_pair_w_rel_any", + Observer_on_add_pair_w_rel_any + }, + { + "on_remove_pair_w_rel_any", + Observer_on_remove_pair_w_rel_any + }, + { + "on_add_pair_w_tgt_any", + Observer_on_add_pair_w_tgt_any + }, + { + "on_remove_pair_w_tgt_any", + Observer_on_remove_pair_w_tgt_any + }, + { + "on_add_pair_w_any_any", + Observer_on_add_pair_w_any_any + }, + { + "on_remove_pair_w_any_any", + Observer_on_remove_pair_w_any_any + }, + { + "on_add_any", + Observer_on_add_any + }, + { + "on_remove_any", + Observer_on_remove_any + }, + { + "get_filter", + Observer_get_filter + }, + { + "uni_observer_eval_count", + Observer_uni_observer_eval_count + }, + { + "multi_observer_eval_count", + Observer_multi_observer_eval_count + }, + { + "yield_existing_uni_no_this", + Observer_yield_existing_uni_no_this + }, + { + "yield_existing_multi_no_this", + Observer_yield_existing_multi_no_this + }, + { + "observer_no_id_in_scope", + Observer_observer_no_id_in_scope + }, + { + "register_comp_in_emit_named_entity", + Observer_register_comp_in_emit_named_entity + }, + { + "register_comp_w_macro_in_emit_named_entity", + Observer_register_comp_w_macro_in_emit_named_entity + }, + { + "add_to_self_in_emit_entity", + Observer_add_to_self_in_emit_entity + }, + { + "on_set_w_not_tag", + Observer_on_set_w_not_tag + }, + { + "on_set_w_not_component", + Observer_on_set_w_not_component + }, + { + "wildcard_event", + Observer_wildcard_event + }, + { + "register_callback_after_run", + Observer_register_callback_after_run + }, + { + "register_run_after_callback", + Observer_register_run_after_callback + }, + { + "register_callback_after_run_ctx", + Observer_register_callback_after_run_ctx + }, + { + "register_run_after_callback_ctx", + Observer_register_run_after_callback_ctx + }, + { + "on_add_after_new_w_table", + Observer_on_add_after_new_w_table + }, + { + "ref_flag_term_1", + Observer_ref_flag_term_1 + }, + { + "ref_flag_term_2", + Observer_ref_flag_term_2 + }, + { + "forward_up_flag_term_1", + Observer_forward_up_flag_term_1 + }, + { + "forward_up_flag_term_2", + Observer_forward_up_flag_term_2 + }, + { + "propagate_up_flag_term_1", + Observer_propagate_up_flag_term_1 + }, + { + "propagate_up_flag_term_2", + Observer_propagate_up_flag_term_2 + }, + { + "row_flag_term_1", + Observer_row_flag_term_1 + }, + { + "row_flag_term_2", + Observer_row_flag_term_2 + }, + { + "on_add_optional", + Observer_on_add_optional + }, + { + "on_remove_optional", + Observer_on_remove_optional + }, + { + "on_add_multi_optional", + Observer_on_add_multi_optional + }, + { + "on_remove_multi_optional", + Observer_on_remove_multi_optional + }, + { + "on_add_multi_only_optional", + Observer_on_add_multi_only_optional + }, + { + "on_remove_multi_only_optional", + Observer_on_remove_multi_only_optional }, { "cache_test_1", @@ -10656,6 +8617,22 @@ bake_test_case ObserverOnSet_testcases[] = { "on_set_after_remove_override", ObserverOnSet_on_set_after_remove_override }, + { + "on_set_after_remove_override_isa_before_add", + ObserverOnSet_on_set_after_remove_override_isa_before_add + }, + { + "on_set_w_override_after_delete", + ObserverOnSet_on_set_w_override_after_delete + }, + { + "on_set_w_override_after_clear", + ObserverOnSet_on_set_w_override_after_clear + }, + { + "on_set_w_override_after_delete_w_ecs_init", + ObserverOnSet_on_set_w_override_after_delete_w_ecs_init + }, { "no_set_after_remove_base", ObserverOnSet_no_set_after_remove_base @@ -10688,6 +8665,10 @@ bake_test_case ObserverOnSet_testcases[] = { "set_optional", ObserverOnSet_set_optional }, + { + "set_optional_one_term", + ObserverOnSet_set_optional_one_term + }, { "set_from_nothing", ObserverOnSet_set_from_nothing @@ -10917,10 +8898,6 @@ bake_test_case TriggerOnSet_testcases[] = { "on_set_after_override_1_of_2_overridden", TriggerOnSet_on_set_after_override_1_of_2_overridden }, - { - "on_set_after_snapshot_restore", - TriggerOnSet_on_set_after_snapshot_restore - }, { "on_set_after_remove_override", TriggerOnSet_on_set_after_remove_override @@ -11011,6 +8988,10 @@ bake_test_case Monitor_testcases[] = { { "monitor_component", Monitor_monitor_component + }, + { + "yield_existing", + Monitor_yield_existing } }; @@ -11419,10 +9400,6 @@ bake_test_case Prefab_testcases[] = { "override_dont_inherit", Prefab_override_dont_inherit }, - { - "prefab_w_switch", - Prefab_prefab_w_switch - }, { "prefab_child_w_dont_inherit_component", Prefab_prefab_child_w_dont_inherit_component @@ -11436,92 +9413,200 @@ bake_test_case Prefab_testcases[] = { Prefab_prefab_child_override_w_exclusive_pair }, { - "prefab_1_slot", - Prefab_prefab_1_slot + "prefab_1_slot", + Prefab_prefab_1_slot + }, + { + "prefab_2_slots", + Prefab_prefab_2_slots + }, + { + "prefab_w_nested_slot", + Prefab_prefab_w_nested_slot + }, + { + "prefab_w_mixed_slots", + Prefab_prefab_w_mixed_slots + }, + { + "prefab_variant_w_slot", + Prefab_prefab_variant_w_slot + }, + { + "prefab_variant_w_base_slot", + Prefab_prefab_variant_w_base_slot + }, + { + "prefab_variant_w_mixed_slots", + Prefab_prefab_variant_w_mixed_slots + }, + { + "override_slot", + Prefab_override_slot + }, + { + "2_instances_w_slots_same_table", + Prefab_2_instances_w_slots_same_table + }, + { + "slot_has_union", + Prefab_slot_has_union + }, + { + "slot_override", + Prefab_slot_override + }, + { + "base_slot_override", + Prefab_base_slot_override + }, + { + "override_twice_w_add", + Prefab_override_twice_w_add + }, + { + "override_twice_w_set", + Prefab_override_twice_w_set + }, + { + "auto_override_copy_once", + Prefab_auto_override_copy_once + }, + { + "always_override", + Prefab_always_override + }, + { + "always_override_pair", + Prefab_always_override_pair + }, + { + "child_of_prefab_is_prefab", + Prefab_child_of_prefab_is_prefab + }, + { + "child_of_prefab_w_prefab_is_prefab", + Prefab_child_of_prefab_w_prefab_is_prefab + }, + { + "child_of_prefab_w_prefab_is_prefab_w_component", + Prefab_child_of_prefab_w_prefab_is_prefab_w_component + }, + { + "override_exclusive", + Prefab_override_exclusive + }, + { + "override_exclusive_2_lvls", + Prefab_override_exclusive_2_lvls + }, + { + "hierarchy_w_recycled_id", + Prefab_hierarchy_w_recycled_id + }, + { + "disable_ids", + Prefab_disable_ids + }, + { + "disable_nested_ids", + Prefab_disable_nested_ids + }, + { + "prefab_w_children_w_isa_auto_override", + Prefab_prefab_w_children_w_isa_auto_override + }, + { + "prefab_child_w_override", + Prefab_prefab_child_w_override + }, + { + "prefab_child_w_override_and_higher_component", + Prefab_prefab_child_w_override_and_higher_component }, { - "prefab_2_slots", - Prefab_prefab_2_slots + "prefab_child_w_override_and_lower_component", + Prefab_prefab_child_w_override_and_lower_component }, { - "prefab_w_nested_slot", - Prefab_prefab_w_nested_slot + "prefab_1_child_offset_id", + Prefab_prefab_1_child_offset_id }, { - "prefab_w_mixed_slots", - Prefab_prefab_w_mixed_slots + "prefab_2_children_offset_id", + Prefab_prefab_2_children_offset_id }, { - "prefab_variant_w_slot", - Prefab_prefab_variant_w_slot + "prefab_3_children_offset_id", + Prefab_prefab_3_children_offset_id }, { - "prefab_variant_w_base_slot", - Prefab_prefab_variant_w_base_slot + "prefab_2_children_2_types_offset_id", + Prefab_prefab_2_children_2_types_offset_id }, { - "prefab_variant_w_mixed_slots", - Prefab_prefab_variant_w_mixed_slots + "prefab_3_children_3_types_offset_id", + Prefab_prefab_3_children_3_types_offset_id }, { - "override_slot", - Prefab_override_slot + "prefab_2_children_2_types_reverse_offset_id", + Prefab_prefab_2_children_2_types_reverse_offset_id }, { - "2_instances_w_slots_same_table", - Prefab_2_instances_w_slots_same_table + "prefab_3_children_3_types_reverse_offset_id", + Prefab_prefab_3_children_3_types_reverse_offset_id }, { - "slot_has_union", - Prefab_slot_has_union + "prefab_2_lvl_nested_children_offset_id", + Prefab_prefab_2_lvl_nested_children_offset_id }, { - "slot_override", - Prefab_slot_override + "prefab_3_lvl_nested_children_offset_id", + Prefab_prefab_3_lvl_nested_children_offset_id }, { - "base_slot_override", - Prefab_base_slot_override + "prefab_recycled_children_offset_id", + Prefab_prefab_recycled_children_offset_id }, { - "prefab_child_w_union", - Prefab_prefab_child_w_union + "prefab_recycled_instance_offset_id", + Prefab_prefab_recycled_instance_offset_id }, { - "override_twice_w_add", - Prefab_override_twice_w_add + "prefab_children_recycled_offset_id", + Prefab_prefab_children_recycled_offset_id }, { - "override_twice_w_set", - Prefab_override_twice_w_set + "prefab_recycled_children_recycled_offset_id", + Prefab_prefab_recycled_children_recycled_offset_id }, { - "auto_override_copy_once", - Prefab_auto_override_copy_once + "prefab_recycled_children_recycled_offset_id_different_generation", + Prefab_prefab_recycled_children_recycled_offset_id_different_generation }, { - "always_override", - Prefab_always_override + "prefab_1_child_offset_id_occupied", + Prefab_prefab_1_child_offset_id_occupied }, { - "always_override_pair", - Prefab_always_override_pair + "prefab_1_child_offset_id_recycled_occupied", + Prefab_prefab_1_child_offset_id_recycled_occupied }, { - "child_of_prefab_is_prefab", - Prefab_child_of_prefab_is_prefab + "prefab_child_offset_w_smaller_child_id", + Prefab_prefab_child_offset_w_smaller_child_id }, { - "override_exclusive", - Prefab_override_exclusive + "prefab_w_union", + Prefab_prefab_w_union }, { - "override_exclusive_2_lvls", - Prefab_override_exclusive_2_lvls + "prefab_child_w_union", + Prefab_prefab_child_w_union }, { - "hierarchy_w_recycled_id", - Prefab_hierarchy_w_recycled_id + "prefab_w_union_and_component", + Prefab_prefab_w_union_and_component } }; @@ -11570,6 +9655,26 @@ bake_test_case World_testcases[] = { "entity_range_check_after_delete", World_entity_range_check_after_delete }, + { + "entity_range_offset_0", + World_entity_range_offset_0 + }, + { + "entity_range_set_limit_to_lower", + World_entity_range_set_limit_to_lower + }, + { + "entity_range_set_limit_to_lower_than_offset", + World_entity_range_set_limit_to_lower_than_offset + }, + { + "entity_range_overlapping_new_id", + World_entity_range_overlapping_new_id + }, + { + "entity_range_overlapping_new_bulk_id", + World_entity_range_overlapping_new_bulk_id + }, { "dim", World_dim @@ -11711,8 +9816,8 @@ bake_test_case World_testcases[] = { World_use_after_clear_unused }, { - "get_mut_in_at_fini", - World_get_mut_in_at_fini + "ensure_in_at_fini", + World_ensure_in_at_fini }, { "get_type_info", @@ -11730,6 +9835,14 @@ bake_test_case World_testcases[] = { "no_name_prefix_after_init", World_no_name_prefix_after_init }, + { + "component_init_w_name_prefix", + World_component_init_w_name_prefix + }, + { + "component_macro_w_name_prefix", + World_component_macro_w_name_prefix + }, { "set_get_context", World_set_get_context @@ -11745,6 +9858,26 @@ bake_test_case World_testcases[] = { { "set_get_binding_context_w_free", World_set_get_binding_context_w_free + }, + { + "get_entities", + World_get_entities + }, + { + "run_post_frame", + World_run_post_frame + }, + { + "run_post_frame_outside_of_frame", + World_run_post_frame_outside_of_frame + }, + { + "get_flags", + World_get_flags + }, + { + "fini_queue_overflow", + World_fini_queue_overflow } }; @@ -11848,10 +9981,6 @@ bake_test_case Type_testcases[] = { "entity_pair_str", Type_entity_pair_str }, - { - "entity_and_str", - Type_entity_and_str - }, { "entity_str_small_buffer", Type_entity_str_small_buffer @@ -11860,10 +9989,6 @@ bake_test_case Type_testcases[] = { "role_pair_str", Type_role_pair_str }, - { - "role_and_str", - Type_role_and_str - }, { "role_owned_str", Type_role_owned_str @@ -11932,12 +10057,16 @@ bake_test_case Commands_testcases[] = { Commands_system_in_progress_w_defer }, { - "defer_get_mut_no_modify", - Commands_defer_get_mut_no_modify + "defer_ensure", + Commands_defer_ensure + }, + { + "defer_ensure_no_modify", + Commands_defer_ensure_no_modify }, { - "defer_get_mut_w_modify", - Commands_defer_get_mut_w_modify + "defer_ensure_w_modify", + Commands_defer_ensure_w_modify }, { "defer_modify", @@ -11960,12 +10089,12 @@ bake_test_case Commands_testcases[] = { Commands_defer_set_after_delete }, { - "defer_get_mut_after_delete", - Commands_defer_get_mut_after_delete + "defer_ensure_after_delete", + Commands_defer_ensure_after_delete }, { - "defer_get_mut_after_delete_2nd_to_last", - Commands_defer_get_mut_after_delete_2nd_to_last + "defer_ensure_after_delete_2nd_to_last", + Commands_defer_ensure_after_delete_2nd_to_last }, { "defer_add_child_to_deleted_parent", @@ -12064,8 +10193,8 @@ bake_test_case Commands_testcases[] = { Commands_defer_return_value }, { - "defer_get_mut_pair", - Commands_defer_get_mut_pair + "defer_ensure_pair", + Commands_defer_ensure_pair }, { "async_stage_add", @@ -12091,18 +10220,10 @@ bake_test_case Commands_testcases[] = { "async_stage_new", Commands_async_stage_new }, - { - "async_stage_no_get", - Commands_async_stage_no_get - }, { "async_stage_readonly", Commands_async_stage_readonly }, - { - "async_stage_is_async", - Commands_async_stage_is_async - }, { "register_component_while_in_progress", Commands_register_component_while_in_progress @@ -12123,6 +10244,18 @@ bake_test_case Commands_testcases[] = { "defer_disable", Commands_defer_disable }, + { + "defer_enable_from_stage", + Commands_defer_enable_from_stage + }, + { + "defer_toggle", + Commands_defer_toggle + }, + { + "defer_toggle_from_stage", + Commands_defer_toggle_from_stage + }, { "defer_delete_with", Commands_defer_delete_with @@ -12171,22 +10304,6 @@ bake_test_case Commands_testcases[] = { "defer_while_suspend_readonly_w_existing_commands", Commands_defer_while_suspend_readonly_w_existing_commands }, - { - "defer_add_union_relationship", - Commands_defer_add_union_relationship - }, - { - "defer_add_existing_union_relationship", - Commands_defer_add_existing_union_relationship - }, - { - "defer_add_union_relationship_2_ops", - Commands_defer_add_union_relationship_2_ops - }, - { - "defer_add_existing_union_relationship_2_ops", - Commands_defer_add_existing_union_relationship_2_ops - }, { "defer_remove_after_set", Commands_defer_remove_after_set @@ -12264,12 +10381,12 @@ bake_test_case Commands_testcases[] = { Commands_defer_2_sets_w_multi_observer }, { - "defer_2_get_muts_w_multi_observer", - Commands_defer_2_get_muts_w_multi_observer + "defer_2_ensures_w_multi_observer", + Commands_defer_2_ensures_w_multi_observer }, { - "defer_2_get_muts_no_modified_w_multi_observer", - Commands_defer_2_get_muts_no_modified_w_multi_observer + "defer_2_ensures_no_modified_w_multi_observer", + Commands_defer_2_ensures_no_modified_w_multi_observer }, { "exists_remove_set", @@ -12288,24 +10405,24 @@ bake_test_case Commands_testcases[] = { Commands_absent_set_remove }, { - "exists_remove_get_mut", - Commands_exists_remove_get_mut + "exists_remove_ensure", + Commands_exists_remove_ensure }, { - "absent_remove_get_mut", - Commands_absent_remove_get_mut + "absent_remove_ensure", + Commands_absent_remove_ensure }, { - "exists_get_mut_remove", - Commands_exists_get_mut_remove + "exists_ensure_remove", + Commands_exists_ensure_remove }, { - "absent_get_mut_remove", - Commands_absent_get_mut_remove + "absent_ensure_remove", + Commands_absent_ensure_remove }, { - "exists_set_w_get_mut", - Commands_exists_set_w_get_mut + "exists_set_w_ensure", + Commands_exists_set_w_ensure }, { "absent_set_invoke_on_set", @@ -12316,24 +10433,24 @@ bake_test_case Commands_testcases[] = { Commands_exists_set_invoke_on_set }, { - "defer_get_mut_no_on_set", - Commands_defer_get_mut_no_on_set + "defer_ensure_no_on_set", + Commands_defer_ensure_no_on_set }, { - "defer_existing_get_mut_no_on_set", - Commands_defer_existing_get_mut_no_on_set + "defer_existing_ensure_no_on_set", + Commands_defer_existing_ensure_no_on_set }, { - "get_mut_override", - Commands_get_mut_override + "ensure_override", + Commands_ensure_override }, { "set_override", Commands_set_override }, { - "absent_get_mut_for_entity_w_tag", - Commands_absent_get_mut_for_entity_w_tag + "absent_ensure_for_entity_w_tag", + Commands_absent_ensure_for_entity_w_tag }, { "on_set_hook_before_on_add_for_existing_component", @@ -12374,6 +10491,86 @@ bake_test_case Commands_testcases[] = { { "on_set_hook_batched_is_deferred", Commands_on_set_hook_batched_is_deferred + }, + { + "add_path", + Commands_add_path + }, + { + "add_path_to_deleted_parent", + Commands_add_path_to_deleted_parent + }, + { + "add_path_nested", + Commands_add_path_nested + }, + { + "add_path_nested_to_deleted_parent", + Commands_add_path_nested_to_deleted_parent + }, + { + "add_path_nested_to_created_deleted_parent", + Commands_add_path_nested_to_created_deleted_parent + }, + { + "add_path_w_stage", + Commands_add_path_w_stage + }, + { + "add_path_to_deleted_parent_w_stage", + Commands_add_path_to_deleted_parent_w_stage + }, + { + "add_path_nested_w_stage", + Commands_add_path_nested_w_stage + }, + { + "add_path_nested_to_deleted_parent_w_stage", + Commands_add_path_nested_to_deleted_parent_w_stage + }, + { + "add_path_nested_to_created_deleted_parent_w_stage", + Commands_add_path_nested_to_created_deleted_parent_w_stage + }, + { + "defer_emplace_w_arg", + Commands_defer_emplace_w_arg + }, + { + "defer_emplace_existing_w_arg", + Commands_defer_emplace_existing_w_arg + }, + { + "mixed_on_add_on_set_w_set_w_batching", + Commands_mixed_on_add_on_set_w_set_w_batching + }, + { + "mixed_on_add_on_set_w_emplace", + Commands_mixed_on_add_on_set_w_emplace + }, + { + "add_isa_set_w_override_batched", + Commands_add_isa_set_w_override_batched + }, + { + "add_set_isa_w_override_batched", + Commands_add_set_isa_w_override_batched + }, + { + "add_batched_set_with", + Commands_add_batched_set_with + }, + { + "defer_emplace_after_remove", + Commands_defer_emplace_after_remove + }, + { + "batched_w_table_change_in_observer", + Commands_batched_w_table_change_in_observer + }, + { + "redefine_named_in_threaded_app", + Commands_redefine_named_in_threaded_app } }; @@ -12595,16 +10792,16 @@ bake_test_case SingleThreadStaging_testcases[] = { SingleThreadStaging_clear_stage_after_merge }, { - "get_mutable", - SingleThreadStaging_get_mutable + "ensureable", + SingleThreadStaging_ensureable }, { - "get_mutable_from_main", - SingleThreadStaging_get_mutable_from_main + "ensureable_from_main", + SingleThreadStaging_ensureable_from_main }, { - "get_mutable_w_add", - SingleThreadStaging_get_mutable_w_add + "ensureable_w_add", + SingleThreadStaging_ensureable_w_add }, { "on_add_after_new_type_in_progress", @@ -12622,10 +10819,6 @@ bake_test_case SingleThreadStaging_testcases[] = { "modify_after_lock", SingleThreadStaging_modify_after_lock }, - { - "get_case_from_stage", - SingleThreadStaging_get_case_from_stage - }, { "get_object_from_stage", SingleThreadStaging_get_object_from_stage @@ -12716,10 +10909,26 @@ bake_test_case Table_testcases[] = { "get_index", Table_get_index }, + { + "get_index_after_tag", + Table_get_index_after_tag + }, { "get_index_not_in_table", Table_get_index_not_in_table }, + { + "get_index_tag", + Table_get_index_tag + }, + { + "get_index_pair", + Table_get_index_pair + }, + { + "get_index_pair_tag", + Table_get_index_pair_tag + }, { "get_column", Table_get_column @@ -12728,6 +10937,14 @@ bake_test_case Table_testcases[] = { "get_column_for_tag", Table_get_column_for_tag }, + { + "get_column_for_low_tag", + Table_get_column_for_low_tag + }, + { + "get_column_for_high_component", + Table_get_column_for_high_component + }, { "get_column_for_component_after_tag", Table_get_column_for_component_after_tag @@ -12767,42 +10984,50 @@ bake_test_case Table_testcases[] = { { "get_column_size", Table_get_column_size - } -}; - -bake_test_case Poly_testcases[] = { + }, + { + "has_id", + Table_has_id + }, { - "iter_query", - Poly_iter_query + "has_pair", + Table_has_pair }, { - "iter_query_w_filter", - Poly_iter_query_w_filter + "has_wildcard_pair", + Table_has_wildcard_pair }, { - "iter_world", - Poly_iter_world + "has_any_pair", + Table_has_any_pair }, { - "iter_world_w_filter", - Poly_iter_world_w_filter + "clear_table_kills_entities", + Table_clear_table_kills_entities }, { - "iter_rule", - Poly_iter_rule + "clear_table_add_new", + Table_clear_table_add_new }, { - "iter_rule_w_filter", - Poly_iter_rule_w_filter + "clear_table_check_size", + Table_clear_table_check_size }, { - "iter_filter", - Poly_iter_filter + "clear_table_twice_check_size", + Table_clear_table_twice_check_size }, { - "iter_filter_w_filter", - Poly_iter_filter_w_filter + "clear_table_on_remove_hooks", + Table_clear_table_on_remove_hooks }, + { + "clear_table_on_remove_observer", + Table_clear_table_on_remove_observer + } +}; + +bake_test_case Poly_testcases[] = { { "on_set_poly_observer", Poly_on_set_poly_observer @@ -12814,30 +11039,6 @@ bake_test_case Poly_testcases[] = { { "on_set_poly_system", Poly_on_set_poly_system - }, - { - "iter_filter_from_entity", - Poly_iter_filter_from_entity - }, - { - "iter_query_from_entity", - Poly_iter_query_from_entity - }, - { - "iter_rule_from_entity", - Poly_iter_rule_from_entity - }, - { - "free_filter_entity", - Poly_free_filter_entity - }, - { - "free_query_entity", - Poly_free_query_entity - }, - { - "free_rule_entity", - Poly_free_rule_entity } }; @@ -12964,6 +11165,10 @@ bake_test_case Error_testcases[] = { { "last_error", Error_last_error + }, + { + "set_log_level_return", + Error_set_log_level_return } }; @@ -12978,40 +11183,55 @@ bake_test_case StackAlloc_testcases[] = { } }; + static bake_test_suite suites[] = { { "Id", NULL, NULL, - 29, + 47, Id_testcases }, { "Entity", NULL, NULL, - 106, + 138, Entity_testcases }, + { + "Each", + NULL, + NULL, + 5, + Each_testcases + }, + { + "Iter", + NULL, + NULL, + 58, + Iter_testcases + }, { "Search", NULL, NULL, - 23, + 20, Search_testcases }, { "Event", NULL, NULL, - 32, + 35, Event_testcases }, { "New", New_setup, NULL, - 25, + 29, New_testcases }, { @@ -13029,47 +11249,40 @@ static bake_test_suite suites[] = { Add_testcases }, { - "Switch", + "Remove", NULL, NULL, - 47, - Switch_testcases + 10, + Remove_testcases }, { - "EnabledComponents", + "GlobalComponentIds", NULL, NULL, - 52, - EnabledComponents_testcases + 7, + GlobalComponentIds_testcases }, { - "Remove", + "Sparse", NULL, NULL, - 10, - Remove_testcases + 87, + Sparse_testcases }, { - "GlobalComponentIds", + "Union", NULL, NULL, - 7, - GlobalComponentIds_testcases + 52, + Union_testcases }, { "Hierarchies", Hierarchies_setup, NULL, - 100, + 105, Hierarchies_testcases }, - { - "FixedHierarchies", - NULL, - NULL, - 65, - FixedHierarchies_testcases - }, { "Has", NULL, @@ -13095,7 +11308,7 @@ static bake_test_suite suites[] = { "Reference", Reference_setup, NULL, - 12, + 13, Reference_testcases }, { @@ -13109,14 +11322,14 @@ static bake_test_suite suites[] = { "OnDelete", NULL, NULL, - 115, + 125, OnDelete_testcases }, { "Set", NULL, NULL, - 31, + 37, Set_testcases }, { @@ -13130,7 +11343,7 @@ static bake_test_suite suites[] = { "Lookup", Lookup_setup, NULL, - 46, + 63, Lookup_testcases }, { @@ -13144,84 +11357,42 @@ static bake_test_suite suites[] = { "Clone", NULL, NULL, - 15, + 16, Clone_testcases }, { "ComponentLifecycle", ComponentLifecycle_setup, NULL, - 87, + 98, ComponentLifecycle_testcases }, - { - "Sorting", - NULL, - NULL, - 36, - Sorting_testcases - }, - { - "SortingEntireTable", - NULL, - NULL, - 33, - SortingEntireTable_testcases - }, - { - "Filter", - NULL, - NULL, - 301, - Filter_testcases - }, - { - "FilterStr", - NULL, - NULL, - 23, - FilterStr_testcases - }, - { - "Query", - NULL, - NULL, - 243, - Query_testcases - }, - { - "Iter", - NULL, - NULL, - 45, - Iter_testcases - }, { "Pairs", NULL, NULL, - 117, + 125, Pairs_testcases }, { "Trigger", NULL, NULL, - 118, + 120, Trigger_testcases }, { "Observer", NULL, NULL, - 116, + 229, Observer_testcases }, { "ObserverOnSet", NULL, NULL, - 21, + 26, ObserverOnSet_testcases }, { @@ -13242,28 +11413,28 @@ static bake_test_suite suites[] = { "TriggerOnSet", NULL, NULL, - 16, + 15, TriggerOnSet_testcases }, { "Monitor", NULL, NULL, - 17, + 18, Monitor_testcases }, { "Prefab", Prefab_setup, NULL, - 127, + 153, Prefab_testcases }, { "World", World_setup, NULL, - 55, + 67, World_testcases }, { @@ -13277,21 +11448,21 @@ static bake_test_suite suites[] = { "Type", Type_setup, NULL, - 25, + 23, Type_testcases }, { "Commands", NULL, NULL, - 123, + 141, Commands_testcases }, { "SingleThreadStaging", SingleThreadStaging_setup, NULL, - 68, + 67, SingleThreadStaging_testcases }, { @@ -13305,14 +11476,14 @@ static bake_test_suite suites[] = { "Table", NULL, NULL, - 14, + 30, Table_testcases }, { "Poly", NULL, NULL, - 17, + 3, Poly_testcases }, { @@ -13326,7 +11497,7 @@ static bake_test_suite suites[] = { "Error", Error_setup, NULL, - 12, + 13, Error_testcases }, { @@ -13339,5 +11510,5 @@ static bake_test_suite suites[] = { }; int main(int argc, char *argv[]) { - return bake_test_run("api", argc, argv, suites, 51); + return bake_test_run("core", argc, argv, suites, 46); } diff --git a/vendors/flecs/test/core/src/util.c b/vendors/flecs/test/core/src/util.c new file mode 100644 index 000000000..7a176f21f --- /dev/null +++ b/vendors/flecs/test/core/src/util.c @@ -0,0 +1,107 @@ +#include +#include + +void probe_system_w_ctx( + ecs_iter_t *it, + Probe *ctx) +{ + if (!ctx) { + return; + } + + ctx->param = it->param; + ctx->system = it->system; + ctx->event = it->event; + ctx->event_id = it->event_id; + ctx->offset = 0; + ctx->term_count = it->field_count; + ctx->term_index = it->term_index; + ctx->ref_fields = it->ref_fields; + ctx->up_fields = it->up_fields; + ctx->row_fields = it->row_fields; + + int i; + for (i = 0; i < ctx->term_count; i ++) { + ctx->c[ctx->invoked][i] = it->ids[i]; + ctx->s[ctx->invoked][i] = ecs_field_src(it, i); + + ecs_id_t field_id = ecs_field_id(it, i); + test_assert(field_id != 0); + + if (ecs_field_is_set(it, i)) { + if (it->trs[i]) { + if (it->sources[i]) { + ecs_table_t *table = ecs_get_table(it->world, it->sources[i]); + test_assert(it->trs[i]->hdr.table == table); + } else { + test_assert(it->trs[i]->hdr.table == it->table); + } + } + } + } + + for (i = 0; i < it->count; i ++) { + if (i + ctx->count < 256) { + ctx->e[i + ctx->count] = it->entities[i]; + } else { + /* can't store more than that, tests shouldn't rely on + * getting back more than 256 results */ + } + } + ctx->count += it->count; + + ctx->invoked ++; +} + +void probe_iter( + ecs_iter_t *it) +{ + Probe *ctx = ecs_get_ctx(it->world); + if (!ctx) { + ctx = it->ctx; + } + probe_system_w_ctx(it, ctx); +} + +void probe_has_entity(Probe *probe, ecs_entity_t e) { + int i; + for (i = 0; i < probe->count; i ++) { + if (probe->e[i] == e) { + break; + } + } + + test_assert(i != probe->count); +} + +void install_test_abort(void) { + ecs_os_set_api_defaults(); + ecs_os_api_t os_api = ecs_os_get_api(); + os_api.abort_ = test_abort; + ecs_os_set_api(&os_api); + ecs_log_set_level(-5); +} + +const ecs_entity_t* bulk_new_w_type( + ecs_world_t *world, ecs_entity_t type_ent, int32_t count) +{ + const ecs_type_t *type = ecs_get_type(world, type_ent); + test_assert(type != NULL); + + ecs_id_t *ids = type->array; + int i = 0; + while ((ecs_id_get_flags(world, ids[i]) & EcsIdOnInstantiateDontInherit)) { + i ++; + } + const ecs_entity_t *result = ecs_bulk_new_w_id(world, ids[i], count); + for (; i < type->count; i ++) { + for (int e = 0; e < count; e ++) { + if (ecs_id_get_flags(world, ids[i]) & EcsIdOnInstantiateDontInherit) { + continue; + } + ecs_add_id(world, result[e], ids[i]); + } + } + + return result; +} diff --git a/vendors/flecs/test/cpp_api/include/cpp_api.h b/vendors/flecs/test/cpp/include/cpp.h similarity index 89% rename from vendors/flecs/test/cpp_api/include/cpp_api.h rename to vendors/flecs/test/cpp/include/cpp.h index d8fd73ba8..ca7ca84c0 100644 --- a/vendors/flecs/test/cpp_api/include/cpp_api.h +++ b/vendors/flecs/test/cpp/include/cpp.h @@ -1,8 +1,8 @@ -#ifndef CPP_API_H -#define CPP_API_H +#ifndef CPP_H +#define CPP_H /* This generated file contains includes for project dependencies */ -#include +#include #include #include @@ -59,7 +59,7 @@ class Pod { this->value = obj.value; } - Pod(Pod&& obj) { + Pod(Pod&& obj) noexcept { move_ctor_invoked ++; this->value = obj.value; } @@ -70,7 +70,7 @@ class Pod { return *this; } - Pod& operator=(Pod&& obj) { + Pod& operator=(Pod&& obj) noexcept { move_invoked ++; this->value = obj.value; return *this; @@ -86,26 +86,39 @@ class Pod { static int move_ctor_invoked; }; -struct TagA { }; -struct TagB { }; -struct TagC { }; -struct TagD { }; -struct TagE { }; -struct TagF { }; -struct TagG { }; -struct TagH { }; -struct TagI { }; -struct TagJ { }; -struct TagK { }; -struct TagL { }; -struct TagM { }; -struct TagN { }; -struct TagO { }; -struct TagP { }; -struct TagQ { }; -struct TagR { }; -struct TagS { }; -struct TagT { }; +struct Tag0 { }; +struct Tag1 { }; +struct Tag2 { }; +struct Tag3 { }; +struct Tag4 { }; +struct Tag5 { }; +struct Tag6 { }; +struct Tag7 { }; +struct Tag8 { }; +struct Tag9 { }; +struct Tag10 { }; +struct Tag11 { }; +struct Tag12 { }; +struct Tag13 { }; +struct Tag14 { }; +struct Tag15 { }; +struct Tag16 { }; +struct Tag17 { }; +struct Tag18 { }; +struct Tag19 { }; +struct Tag20 { }; +struct Tag21 { }; +struct Tag22 { }; +struct Tag23 { }; +struct Tag24 { }; +struct Tag25 { }; +struct Tag26 { }; +struct Tag27 { }; +struct Tag28 { }; +struct Tag29 { }; +struct Tag30 { }; +struct Tag31 { }; +struct Tag32 { }; template struct Template { @@ -317,7 +330,7 @@ class CountNoDefaultCtor { this->value = obj.value; } - CountNoDefaultCtor(CountNoDefaultCtor&& obj) { + CountNoDefaultCtor(CountNoDefaultCtor&& obj) noexcept { move_ctor_invoked ++; this->value = obj.value; } @@ -328,7 +341,7 @@ class CountNoDefaultCtor { return *this; } - CountNoDefaultCtor& operator=(CountNoDefaultCtor&& obj) { + CountNoDefaultCtor& operator=(CountNoDefaultCtor&& obj) noexcept { move_invoked ++; this->value = obj.value; return *this; diff --git a/vendors/flecs/test/api/include/api/bake_config.h b/vendors/flecs/test/cpp/include/cpp/bake_config.h similarity index 93% rename from vendors/flecs/test/api/include/api/bake_config.h rename to vendors/flecs/test/cpp/include/cpp/bake_config.h index 45462eccd..7d5292dc1 100644 --- a/vendors/flecs/test/api/include/api/bake_config.h +++ b/vendors/flecs/test/cpp/include/cpp/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef API_BAKE_CONFIG_H -#define API_BAKE_CONFIG_H +#ifndef CPP_BAKE_CONFIG_H +#define CPP_BAKE_CONFIG_H /* Headers of public dependencies */ #include diff --git a/vendors/flecs/test/cpp_api/project.json b/vendors/flecs/test/cpp/project.json similarity index 84% rename from vendors/flecs/test/cpp_api/project.json rename to vendors/flecs/test/cpp/project.json index 46aeab9a0..6e498e8a8 100644 --- a/vendors/flecs/test/cpp_api/project.json +++ b/vendors/flecs/test/cpp/project.json @@ -1,5 +1,5 @@ { - "id": "cpp_api", + "id": "cpp", "type": "application", "value": { "author": "Sander Mertens", @@ -51,11 +51,17 @@ "emplace_pair_type", "emplace_pair_second", "get_generic", - "get_mut_generic", + "ensure_generic", "get_generic_w_id", "get_generic_w_id_t", - "get_mut_generic_w_id", - "get_mut_generic_w_id_t", + "ensure_generic_w_id", + "ensure_generic_w_id_t", + "get_mut_w_id", + "get_mut_T", + "get_mut_r_t", + "get_mut_R_t", + "get_mut_R_T", + "get_mut_r_T", "set_generic", "set_generic_w_id", "set_generic_w_id_t", @@ -138,10 +144,10 @@ "set_1_component_w_callback", "set_2_components_w_callback", "set_3_components_w_callback", - "get_mut_1_component_w_callback", - "get_mut_2_components_w_callback", + "ensure_1_component_w_callback", + "ensure_2_components_w_callback", "get_component_w_callback_nested", - "get_mut_component_w_callback_nested", + "ensure_component_w_callback_nested", "defer_set_1_component", "defer_set_2_components", "defer_set_3_components", @@ -174,6 +180,7 @@ "defer_new_w_name_scope_with", "defer_w_with_implicit_component", "defer_suspend_resume", + "defer_ensure", "entity_id_str", "pair_id_str", "role_id_str", @@ -267,7 +274,29 @@ "emplace_w_observer", "scoped_world", "entity_lookup_not_recursive", - "world_lookup_not_recursive" + "world_lookup_not_recursive", + "world_lookup_custom_sep", + "world_lookup_custom_root_sep", + "depends_on", + "depends_on_type", + "const_entity_add_remove", + "const_entity_set", + "const_entity_get_mut", + "const_entity_ensure", + "const_entity_destruct", + "const_entity_emit_after_build", + "const_entity_set_doc", + "set_sparse", + "insert_1_sparse", + "insert_2_w_1_sparse", + "emplace_sparse", + "override_sparse", + "delete_w_override_sparse", + "get_pair_second_invalid_type", + "get_mut_pair_second_invalid_type", + "ensure_pair_second_invalid_type", + "set_pair_second_invalid_type", + "get_ref_pair_second_invalid_type" ] }, { "id": "Pairs", @@ -284,11 +313,11 @@ "system_2_pair_instances", "override_pair", "override_tag_pair", - "get_mut_pair", - "get_mut_pair_existing", - "get_mut_pair_tag", - "get_mut_pair_tag_existing", - "get_mut_R_tag_O", + "ensure_pair", + "ensure_pair_existing", + "ensure_pair_tag", + "ensure_pair_tag_existing", + "ensure_R_tag_O", "get_relation_from_id", "get_second_from_id", "get_recycled_relation_from_id", @@ -338,13 +367,19 @@ "deref_const_pair", "deref_pair_obj", "deref_const_pair_obj", - "set_R_existing_value" + "set_R_existing_value", + "symmetric_w_childof", + "modified_tag_second", + "modified_tag_first" ] }, { "id": "Enum", "testcases": [ "standard_enum_reflection", "sparse_enum_reflection", + "bitmask_enum_reflection", + "bitmask_enum_with_type_reflection", + "enum_with_mixed_constants_and_bitmask", "enum_class_reflection", "prefixed_enum_reflection", "constant_with_num_reflection", @@ -381,14 +416,10 @@ "enum_child_count" ] }, { - "id": "Switch", + "id": "Union", "testcases": [ "add_case", "get_case", - "system_w_case", - "system_w_case_builder", - "system_w_sw_type_builder", - "system_w_switch", "add_case_w_type", "add_switch_w_type", "add_remove_switch_w_type", @@ -411,7 +442,17 @@ "alias_entity", "alias_entity_by_name", "alias_entity_by_scoped_name", - "alias_entity_empty" + "alias_entity_empty", + "id_from_str_0_entity", + "id_from_str_entity_from_str", + "id_from_str_unresolved_entity_from_str", + "id_from_str_scoped_entity_from_str", + "id_from_str_template_entity_from_str", + "id_from_str_pair_from_str", + "id_from_str_unresolved_pair_from_str", + "id_from_str_wildcard_pair_from_str", + "id_from_str_any_pair_from_str", + "id_from_str_invalid_pair" ] }, { "id": "System", @@ -450,6 +491,7 @@ "each_w_mut_children_it", "readonly_children_iter", "rate_filter", + "self_rate_filter", "update_rate_filter", "default_ctor", "test_auto_defer_each", @@ -458,12 +500,8 @@ "custom_pipeline_w_kind", "instanced_query_w_singleton_each", "instanced_query_w_base_each", - "un_instanced_query_w_singleton_each", - "un_instanced_query_w_base_each", "instanced_query_w_singleton_iter", "instanced_query_w_base_iter", - "un_instanced_query_w_singleton_iter", - "un_instanced_query_w_base_iter", "create_w_no_template_args", "system_w_type_kind_type_pipeline", "default_ctor", @@ -483,7 +521,15 @@ "table_get", "range_get", "randomize_timers", - "optional_pair_term" + "optional_pair_term", + "singleton_tick_source", + "pipeline_step_with_kind_enum", + "pipeline_step_depends_on_pipeline_step_with_enum", + "register_twice_w_each", + "register_twice_w_run", + "register_twice_w_run_each", + "register_twice_w_each_run", + "run_w_0_src_query" ] }, { "id": "Event", @@ -515,7 +561,10 @@ "enqueue_event", "enqueue_entity_event", "enqueue_event_w_payload", - "enqueue_entity_event_w_payload" + "enqueue_entity_event_w_payload", + "enqueue_entity_from_readonly_world", + "enqueue_entity_w_payload_from_readonly_world", + "enum_event" ] }, { "id": "Iterable", @@ -528,26 +577,37 @@ }, { "id": "Query", "testcases": [ - "action", - "action_const", - "action_shared", - "action_optional", + "term_each_component", + "term_each_tag", + "term_each_id", + "term_each_pair_type", + "term_each_pair_id", + "term_each_pair_relation_wildcard", + "term_each_pair_object_wildcard", + "term_get_id", + "term_get_subj", + "term_get_pred", + "term_get_obj", + "set_this_var", + "run", + "run_const", + "run_shared", + "run_optional", + "run_sparse", "each", "each_const", "each_shared", "each_optional", + "each_sparse", "signature", "signature_const", "signature_shared", "signature_optional", - "subquery", - "subquery_w_expr", "query_single_pair", "tag_w_each", "shared_tag_w_each", "sort_by", "changed", - "orphaned", "default_ctor", "default_ctor_no_assign", "expr_w_template", @@ -577,12 +637,8 @@ "iter_type", "instanced_query_w_singleton_each", "instanced_query_w_base_each", - "un_instanced_query_w_singleton_each", - "un_instanced_query_w_base_each", "instanced_query_w_singleton_iter", "instanced_query_w_base_iter", - "un_instanced_query_w_singleton_iter", - "un_instanced_query_w_base_iter", "query_each_from_component", "query_iter_from_component", "query_each_w_func_ptr", @@ -590,6 +646,14 @@ "query_each_w_func_no_ptr", "query_iter_w_func_no_ptr", "query_each_w_iter", + "each_w_iter_no_this", + "invalid_field_from_each_w_iter", + "invalid_field_T_from_each_w_iter", + "invalid_field_const_T_from_each_w_iter", + "field_at_from_each_w_iter", + "field_at_T_from_each_w_iter", + "field_at_const_T_from_each_w_iter", + "field_at_from_each_w_iter_w_base_type", "change_tracking", "not_w_write", "get_first", @@ -597,8 +661,6 @@ "get_is_true_direct", "get_first_direct", "each_w_no_this", - "each_w_iter_no_this", - "invalid_each_w_no_this", "named_query", "named_scoped_query", "instanced_nested_query_w_iter", @@ -612,83 +674,32 @@ "find", "find_not_found", "find_w_entity", - "optional_pair_term" + "optional_pair_term", + "empty_tables_each", + "empty_tables_each_w_entity", + "empty_tables_each_w_iter", + "query_from_entity", + "query_from_entity_name", + "run_w_iter_fini", + "run_w_iter_fini_interrupt", + "run_w_iter_fini_empty", + "run_w_iter_fini_no_query", + "add_to_match_from_staged_query", + "add_to_match_from_staged_query_readonly_threaded", + "pair_with_variable_src", + "pair_with_variable_src_no_row_fields", + "iter_targets", + "iter_targets_2nd_field", + "iter_targets_field_out_of_range", + "iter_targets_field_not_a_pair", + "iter_targets_field_not_set" ] }, { "id": "QueryBuilder", - "testcases": [ - "builder_assign_same_type", - "builder_assign_from_empty", - "builder_assign_to_empty", - "builder_build", - "builder_build_to_auto", - "builder_build_n_statements", - "1_type", - "add_1_type", - "add_2_types", - "add_1_type_w_1_type", - "add_2_types_w_1_type", - "add_pair", - "add_not", - "add_or", - "add_optional", - "ptr_type", - "const_type", - "string_term", - "singleton_term", - "isa_superset_term", - "isa_self_superset_term", - "childof_superset_term", - "childof_self_superset_term", - "isa_superset_term_w_each", - "isa_self_superset_term_w_each", - "childof_superset_term_w_each", - "childof_self_superset_term_w_each", - "isa_superset_shortcut", - "isa_superset_shortcut_w_self", - "childof_superset_shortcut", - "childof_superset_shortcut_w_self", - "relation", - "relation_w_object_wildcard", - "relation_w_predicate_wildcard", - "add_pair_w_rel_type", - "template_term", - "explicit_subject_w_id", - "explicit_subject_w_type", - "explicit_object_w_id", - "explicit_object_w_type", - "explicit_term", - "explicit_term_w_type", - "explicit_term_w_pair_type", - "explicit_term_w_id", - "explicit_term_w_pair_id", - "1_term_to_empty", - "2_subsequent_args", - "optional_tag_is_set", - "10_terms", - "20_terms", - "group_by_raw", - "group_by_template", - "group_by_iter_one", - "group_by_iter_one_template", - "group_by_iter_one_all_groups", - "group_by_default_func_w_id", - "group_by_default_func_w_type", - "group_by_callbacks", - "create_w_no_template_args", - "any_wildcard", - "cascade", - "cascade_w_relationship", - "up_w_type", - "cascade_w_type", - "cascade_desc", - "named_query", - "term_w_write", - "term_w_read", - "iter_w_stage" - ] - }, { - "id": "FilterBuilder", + "params": { + "cache_kind": ["default", "auto"] + }, + "setup": true, "testcases": [ "builder_assign_same_type", "builder_assign_from_empty", @@ -698,6 +709,25 @@ "builder_build_n_statements", "builder_force_assign_operator", "1_type", + "2_types", + "id_term", + "type_term", + "id_pair_term", + "id_pair_wildcard_term", + "type_pair_term", + "pair_term_w_var", + "2_pair_terms_w_var", + "set_var", + "set_2_vars", + "set_var_by_name", + "set_2_vars_by_name", + "expr_w_var", + "set_var_on_query", + "set_var_by_name_on_query", + "set_table_var", + "set_range_var", + "set_table_var_chained", + "set_range_var_chained", "add_1_type", "add_2_types", "add_1_type_w_1_type", @@ -738,7 +768,6 @@ "explicit_term_w_pair_id", "1_term_to_empty", "2_subsequent_args", - "filter_as_arg", "filter_as_move_arg", "filter_as_return", "filter_copy", @@ -747,7 +776,9 @@ "world_each_filter_1_component_no_entity", "world_each_filter_2_components_no_entity", "10_terms", - "20_terms", + "16_terms", + "32_terms", + "33_terms", "term_after_arg", "name_arg", "const_in_term", @@ -768,6 +799,7 @@ "with_pair_components", "with_pair_component_id", "with_pair_component_name", + "with_pair_name_component_id", "with_enum", "without_id", "without_name", @@ -777,6 +809,7 @@ "without_pair_components", "without_pair_component_id", "without_pair_component_name", + "without_pair_name_component_id", "without_enum", "write_id", "write_name", @@ -802,29 +835,28 @@ "with_T_inout", "with_R_T_inout", "with_R_t_inout", - "with_r_t_inout" - ] - }, { - "id": "RuleBuilder", - "testcases": [ - "1_type", - "2_types", - "id_term", - "type_term", - "id_pair_term", - "id_pair_wildcard_term", - "type_pair_term", - "pair_term_w_var", - "2_pair_terms_w_var", - "set_var", - "set_2_vars", - "set_var_by_name", - "set_2_vars_by_name", - "expr_w_var", - "get_first", - "get_count_direct", - "get_is_true_direct", - "get_first_direct", + "with_r_t_inout", + "optional_tag_is_set", + "group_by_raw", + "group_by_template", + "group_by_iter_one", + "group_by_iter_one_template", + "group_by_iter_one_all_groups", + "group_by_default_func_w_id", + "group_by_default_func_w_type", + "group_by_callbacks", + "set_group_on_query", + "set_group_type_on_query", + "create_w_no_template_args", + "any_wildcard", + "cascade", + "cascade_w_relationship", + "up_w_type", + "cascade_w_type", + "cascade_desc", + "named_query", + "term_w_write", + "term_w_read", "var_src_w_prefixed_name", "var_first_w_prefixed_name", "var_second_w_prefixed_name", @@ -834,12 +866,7 @@ "named_scoped_rule", "is_valid", "unresolved_by_name", - "scope", - "iter_w_stage", - "inspect_terms_w_expr", - "find", - "find_not_found", - "find_w_entity" + "scope" ] }, { "id": "SystemBuilder", @@ -861,7 +888,7 @@ "string_term", "singleton_term", "10_terms", - "20_terms", + "16_terms", "name_arg", "create_w_no_template_args", "write_annotation", @@ -875,13 +902,16 @@ "2_terms_on_set", "2_terms_un_set", "10_terms", - "20_terms", + "16_terms", "2_entities_iter", "2_entities_table_column", "2_entities_each", "create_w_no_template_args", "yield_existing", "yield_existing_2_terms", + "yield_existing_on_create_flag", + "yield_existing_on_delete_flag", + "yield_existing_on_create_delete_flag", "default_ctor", "entity_ctor", "on_add", @@ -892,47 +922,40 @@ "on_add_expr", "observer_w_filter_term", "run_callback", + "run_callback_w_1_field", + "run_callback_w_2_fields", + "run_callback_w_yield_existing_1_field", + "run_callback_w_yield_existing_2_fields", "get_query", "on_set_w_set", "on_set_w_defer_set", + "on_set_w_set_sparse", "on_add_singleton", "on_add_pair_singleton", "on_add_pair_wildcard_singleton", "on_add_with_pair_singleton", "add_in_yield_existing", "add_in_yield_existing_multi", - "name_from_root" - ] - }, { - "id": "Filter", - "testcases": [ - "term_each_component", - "term_each_tag", - "term_each_id", - "term_each_pair_type", - "term_each_pair_id", - "term_each_pair_relation_wildcard", - "term_each_pair_object_wildcard", - "default_ctor_no_assign", - "term_get_id", - "term_get_subj", - "term_get_pred", - "term_get_obj", - "get_first", - "get_count_direct", - "get_is_true_direct", - "get_first_direct", - "each_w_no_this", - "each_w_iter_no_this", - "invalid_each_w_no_this", - "named_filter", - "named_scoped_filter", - "set_this_var", - "inspect_terms_w_expr", - "find", - "find_not_found", - "find_w_entity", - "optional_pair_term" + "name_from_root", + "term_index", + "implicit_register_in_emit_for_named_entity", + "add_to_named_in_emit_for_named_entity", + "implicit_register_in_emit_for_named_entity_w_defer", + "add_to_named_in_emit_for_named_entity_w_defer", + "register_twice_w_each", + "register_twice_w_run", + "register_twice_w_run_each", + "register_twice_w_each_run", + "other_table", + "other_table_w_pair", + "other_table_w_pair_wildcard", + "on_add_inherited", + "on_set_inherited", + "on_remove_inherited", + "on_set_after_remove_override", + "on_set_after_remove_override_create_observer_before", + "on_set_w_override_after_delete", + "on_set_w_override_after_clear" ] }, { "id": "ComponentLifecycle", @@ -959,8 +982,8 @@ "struct_w_vector_set_2_remove", "struct_w_vector_add_2_remove_w_tag", "struct_w_vector_set_2_remove_w_tag", - "get_mut_new", - "get_mut_existing", + "ensure_new", + "ensure_existing", "implicit_component", "implicit_after_query", "deleted_copy", @@ -996,6 +1019,15 @@ "on_add_hook_w_entity", "on_remove_hook_w_entity", "on_set_hook_w_entity", + "on_add_hook_sparse", + "on_remove_hook_sparse", + "on_set_hook_sparse", + "on_add_hook_sparse_w_entity", + "on_remove_hook_sparse_w_entity", + "on_set_hook_sparse_w_entity", + "on_add_hook_sparse_w_iter", + "on_remove_hook_sparse_w_iter", + "on_set_hook_sparse_w_iter", "chained_hooks", "ctor_w_2_worlds", "ctor_w_2_worlds_explicit_registration", @@ -1010,8 +1042,12 @@ "set_override_pair_w_entity_no_copy", "dtor_after_defer_set", "dtor_with_relation", + "dtor_relation_target", "register_parent_after_child_w_hooks", - "register_parent_after_child_w_hooks_implicit" + "register_parent_after_child_w_hooks_implicit", + "sparse_component", + "count_in_add_hook", + "count_in_remove_hook" ] }, { "id": "Refs", @@ -1028,7 +1064,11 @@ "pair_ref_second", "from_stage", "default_ctor", - "try_get" + "ctor_from_entity", + "implicit_operator_bool", + "try_get", + "has", + "bool_operator" ] }, { "id": "Module", @@ -1045,7 +1085,15 @@ "module_as_entity", "module_as_component", "module_with_core_name", - "import_addons_two_worlds" + "import_addons_two_worlds", + "lookup_module_after_reparent", + "reparent_module_in_ctor", + "implicitly_add_module_to_scopes_component", + "implicitly_add_module_to_scopes_entity", + "rename_namespace_shorter", + "rename_namespace_longer", + "rename_namespace_nested", + "rename_reparent_root_module" ] }, { "id": "ImplicitComponents", @@ -1075,15 +1123,9 @@ "implicit_base", "implicit_const", "implicit_ref", - "implicit_ptr", "implicit_const_ref", "vector_elem_type" ] - }, { - "id": "Snapshot", - "testcases": [ - "simple_snapshot" - ] }, { "id": "WorldFactory", "testcases": [ @@ -1097,7 +1139,6 @@ "system_w_expr", "query", "query_w_expr", - "snapshot", "module" ] }, { @@ -1133,6 +1174,7 @@ "reregister_after_reset_w_hooks_and_in_use_implicit", "register_component_w_reset_in_multithreaded", "register_component_w_core_name", + "register_nested_component_in_module", "count", "count_id", "count_pair", @@ -1187,7 +1229,7 @@ "is_valid", "exists", "get_alive", - "ensure", + "make_alive", "reset_all", "get_tick", "register_from_scope", @@ -1209,12 +1251,21 @@ "make_pair_of_pair_id", "make_pair_of_pair_id_tgt", "make_pair_of_pair_type", - "delta_time" + "delta_time", + "atfini", + "atfini_w_ctx", + "get_mut_T", + "get_mut_R_T", + "world_mini", + "copy_world", + "fini_reentrancy", + "fini_copy_move_assign" ] }, { "id": "Singleton", "testcases": [ "set_get_singleton", + "ensure_singleton", "get_mut_singleton", "emplace_singleton", "modified_singleton", @@ -1348,7 +1399,8 @@ "error_range", "struct_member_ptr", "struct_member_ptr_packed_struct", - "component_as_array" + "component_as_array", + "out_of_order_member_declaration" ] }, { "id": "Table", @@ -1393,6 +1445,7 @@ "set_name", "set_link", "set_color", + "set_uuid", "get_name_no_doc_name" ] }] diff --git a/vendors/flecs/test/cpp_api/src/ComponentLifecycle.cpp b/vendors/flecs/test/cpp/src/ComponentLifecycle.cpp similarity index 80% rename from vendors/flecs/test/cpp_api/src/ComponentLifecycle.cpp rename to vendors/flecs/test/cpp/src/ComponentLifecycle.cpp index 49e261e12..129313704 100644 --- a/vendors/flecs/test/cpp_api/src/ComponentLifecycle.cpp +++ b/vendors/flecs/test/cpp/src/ComponentLifecycle.cpp @@ -2,7 +2,7 @@ #define flecs_static_assert(cond, str)\ ecs_assert(cond, ECS_INVALID_OPERATION, str) -#include +#include int Pod::ctor_invoked = 0; int Pod::dtor_invoked = 0; @@ -134,7 +134,7 @@ void ComponentLifecycle_copy_on_set(void) { void ComponentLifecycle_copy_on_override(void) { flecs::world world; - world.component(); + world.component().add(flecs::OnInstantiate, flecs::Inherit); auto base = world.entity(); test_assert(base.id() != 0); @@ -497,7 +497,7 @@ void ComponentLifecycle_struct_w_vector_set_2_remove_w_tag(void) { test_assert(ptr2 == NULL); } -void ComponentLifecycle_get_mut_new(void) { +void ComponentLifecycle_ensure_new(void) { flecs::world world; world.component(); @@ -505,8 +505,7 @@ void ComponentLifecycle_get_mut_new(void) { auto e = world.entity(); test_assert(e.id() != 0); - Pod* value = e.get_mut(); - test_assert(value != NULL); + e.ensure(); Pod::ctor_invoked = 1; Pod::dtor_invoked = 0; @@ -521,7 +520,7 @@ void ComponentLifecycle_get_mut_new(void) { Pod::move_invoked = 0; } -void ComponentLifecycle_get_mut_existing(void) { +void ComponentLifecycle_ensure_existing(void) { flecs::world world; world.component(); @@ -529,18 +528,16 @@ void ComponentLifecycle_get_mut_existing(void) { auto e = world.entity(); test_assert(e.id() != 0); - Pod* value = e.get_mut(); - test_assert(value != NULL); + e.ensure(); Pod::ctor_invoked = 1; Pod::dtor_invoked = 0; Pod::copy_invoked = 0; Pod::move_invoked = 0; - value = e.get_mut(); - test_assert(value != NULL); + e.ensure(); - /* Repeated calls to get_mut should not invoke constructor */ + /* Repeated calls to ensure should not invoke constructor */ Pod::ctor_invoked = 1; Pod::dtor_invoked = 0; Pod::copy_invoked = 0; @@ -764,54 +761,81 @@ void ComponentLifecycle_no_copy_ctor(void) { try_set(ecs); } -void ComponentLifecycle_no_move_ctor(void) { - install_test_abort(); - +void ComponentLifecycle_no_move(void) { flecs::world ecs; - test_expect_abort(); + ecs.component(); + ecs.component(); - ecs.component(); + test_assert(ecs.component().has(flecs::Sparse)); + + flecs::entity e = ecs.entity().add(); + const NoMove *ptr = e.get(); + test_assert(ptr != NULL); + test_int(ptr->x_, 99); + + e.add(); + + test_assert(ptr == e.get()); + test_int(ptr->x_, 99); } -void ComponentLifecycle_no_copy_assign(void) { +void ComponentLifecycle_no_move_ctor(void) { flecs::world ecs; - ecs.component(); + ecs.component(); + ecs.component(); - try_add(ecs); + test_assert(ecs.component().has(flecs::Sparse)); - try_set(ecs); + flecs::entity e = ecs.entity().add(); + const NoMoveCtor *ptr = e.get(); + test_assert(ptr != NULL); + test_int(ptr->x_, 99); + + e.add(); + + test_assert(ptr == e.get()); + test_int(ptr->x_, 99); } void ComponentLifecycle_no_move_assign(void) { - install_test_abort(); - flecs::world ecs; - test_expect_abort(); - ecs.component(); + ecs.component(); + + test_assert(ecs.component().has(flecs::Sparse)); + + flecs::entity e = ecs.entity().add(); + const NoMoveAssign *ptr = e.get(); + test_assert(ptr != NULL); + test_int(ptr->x_, 99); + + e.add(); + + test_assert(ptr == e.get()); + test_int(ptr->x_, 99); } -void ComponentLifecycle_no_copy(void) { +void ComponentLifecycle_no_copy_assign(void) { flecs::world ecs; - ecs.component(); + ecs.component(); - try_add(ecs); + try_add(ecs); - try_set(ecs); + try_set(ecs); } -void ComponentLifecycle_no_move(void) { - install_test_abort(); - +void ComponentLifecycle_no_copy(void) { flecs::world ecs; - test_expect_abort(); + ecs.component(); - ecs.component(); + try_add(ecs); + + try_set(ecs); } void ComponentLifecycle_no_dtor(void) { @@ -1045,7 +1069,7 @@ class CtorDtor_w_MoveAssign { CtorDtor_w_MoveAssign(CtorDtor_w_MoveAssign&& obj) = default; CtorDtor_w_MoveAssign& operator=(const CtorDtor_w_MoveAssign& obj) = default; - CtorDtor_w_MoveAssign& operator=(CtorDtor_w_MoveAssign&& obj) { + CtorDtor_w_MoveAssign& operator=(CtorDtor_w_MoveAssign&& obj) noexcept { move_value = this->x_; this->x_ = obj.x_; @@ -1424,9 +1448,100 @@ void ComponentLifecycle_on_add_hook_w_entity(void) { test_int(0, count); test_assert(e_arg == 0); + auto e1 = ecs.entity().add(); + test_int(1, count); + test_assert(e_arg == e1); + + e1.add(); + test_int(1, count); + + auto e2 = ecs.entity().add(); + test_int(2, count); + test_assert(e_arg == e2); + } + + test_int(2, count); +} + +void ComponentLifecycle_on_remove_hook_w_entity(void) { + int count = 0; + flecs::entity e_arg; + flecs::entity e2; + + { + flecs::world ecs; + + ecs.component().on_remove([&](flecs::entity arg, Position& p){ + e_arg = arg; + count ++; + }); + + test_int(0, count); + test_assert(e_arg == 0); + + auto e1 = ecs.entity().add(); + e2 = ecs.entity().add(); + test_int(0, count); + + e1.remove(); + test_int(1, count); + test_assert(e_arg == e1); + } + + test_int(2, count); + test_assert(e_arg == e2); +} + +void ComponentLifecycle_on_set_hook_w_entity(void) { + int count = 0; + Position v = {0}; + flecs::entity e_arg; + + { + flecs::world ecs; + + ecs.component().on_set([&](flecs::entity arg, Position& p) { + count ++; + v = p; + e_arg = arg; + }); + + test_int(0, count); + + auto e1 = ecs.entity().add(); + test_int(0, count); + + e1.set({10, 20}); + test_int(1, count); + test_assert(e_arg == e1); + test_int(10, v.x); + test_int(20, v.y); + + auto e2 = ecs.entity().set({30, 40}); + test_int(2, count); + test_assert(e_arg == e2); + test_int(30, v.x); + test_int(40, v.y); + } + + test_int(2, count); +} + +void ComponentLifecycle_on_add_hook_sparse(void) { + int count = 0; + + { + flecs::world ecs; + + ecs.component().add(flecs::Sparse); + ecs.component().on_add([&](Position& p) { + count ++; + }); + + test_int(0, count); + auto e = ecs.entity().add(); test_int(1, count); - test_assert(e_arg == e); e.add(); test_int(1, count); @@ -1435,7 +1550,94 @@ void ComponentLifecycle_on_add_hook_w_entity(void) { test_int(1, count); } -void ComponentLifecycle_on_remove_hook_w_entity(void) { +void ComponentLifecycle_on_remove_hook_sparse(void) { + int count = 0; + + { + flecs::world ecs; + + ecs.component().add(flecs::Sparse); + ecs.component().on_remove([&](Position& p) { + count ++; + }); + + test_int(0, count); + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + test_int(0, count); + + e1.remove(); + test_int(1, count); + } + + test_int(2, count); +} + +void ComponentLifecycle_on_set_hook_sparse(void) { + int count = 0; + Position v = {0}; + + { + flecs::world ecs; + + ecs.component().add(flecs::Sparse); + ecs.component().on_set([&](Position& p) { + count ++; + v = p; + }); + + test_int(0, count); + + auto e1 = ecs.entity().add(); + test_int(0, count); + + e1.set({10, 20}); + test_int(1, count); + test_int(10, v.x); + test_int(20, v.y); + + ecs.entity().set({30, 40}); + test_int(2, count); + test_int(30, v.x); + test_int(40, v.y); + } + + test_int(2, count); +} + +void ComponentLifecycle_on_add_hook_sparse_w_entity(void) { + int count = 0; + flecs::entity e_arg; + + { + flecs::world ecs; + + ecs.component().add(flecs::Sparse); + ecs.component().on_add([&](flecs::entity arg, Position& p) { + e_arg = arg; + count ++; + }); + + test_int(0, count); + test_assert(e_arg == 0); + + auto e1 = ecs.entity().add(); + test_int(1, count); + test_assert(e_arg == e1); + + e1.add(); + test_int(1, count); + + auto e2 = ecs.entity().add(); + test_int(2, count); + test_assert(e_arg == e2); + } + + test_int(2, count); +} + +void ComponentLifecycle_on_remove_hook_sparse_w_entity(void) { int count = 0; flecs::entity e_arg; flecs::entity e2; @@ -1443,6 +1645,7 @@ void ComponentLifecycle_on_remove_hook_w_entity(void) { { flecs::world ecs; + ecs.component().add(flecs::Sparse); ecs.component().on_remove([&](flecs::entity arg, Position& p){ e_arg = arg; count ++; @@ -1464,7 +1667,7 @@ void ComponentLifecycle_on_remove_hook_w_entity(void) { test_assert(e_arg == e2); } -void ComponentLifecycle_on_set_hook_w_entity(void) { +void ComponentLifecycle_on_set_hook_sparse_w_entity(void) { int count = 0; Position v = {0}; flecs::entity e_arg; @@ -1472,6 +1675,7 @@ void ComponentLifecycle_on_set_hook_w_entity(void) { { flecs::world ecs; + ecs.component().add(flecs::Sparse); ecs.component().on_set([&](flecs::entity arg, Position& p) { count ++; v = p; @@ -1499,6 +1703,106 @@ void ComponentLifecycle_on_set_hook_w_entity(void) { test_int(2, count); } +void ComponentLifecycle_on_add_hook_sparse_w_iter(void) { + int count = 0; + flecs::entity e_arg; + + { + flecs::world ecs; + + ecs.component().add(flecs::Sparse); + ecs.component() + .on_add([&](flecs::iter& it, size_t row, Position& p) { + e_arg = it.entity(row); + count ++; + }); + + test_int(0, count); + test_assert(e_arg == 0); + + auto e1 = ecs.entity().add(); + test_int(1, count); + test_assert(e_arg == e1); + + e1.add(); + test_int(1, count); + + auto e2 = ecs.entity().add(); + test_int(2, count); + test_assert(e_arg == e2); + } + + test_int(2, count); +} + +void ComponentLifecycle_on_remove_hook_sparse_w_iter(void) { + int count = 0; + flecs::entity e_arg; + flecs::entity e2; + + { + flecs::world ecs; + + ecs.component().add(flecs::Sparse); + ecs.component() + .on_remove([&](flecs::iter& it, size_t row, Position& p){ + e_arg = it.entity(row); + count ++; + }); + + test_int(0, count); + test_assert(e_arg == 0); + + auto e1 = ecs.entity().add(); + e2 = ecs.entity().add(); + test_int(0, count); + + e1.remove(); + test_int(1, count); + test_assert(e_arg == e1); + } + + test_int(2, count); + test_assert(e_arg == e2); +} + +void ComponentLifecycle_on_set_hook_sparse_w_iter(void) { + int count = 0; + Position v = {0}; + flecs::entity e_arg; + + { + flecs::world ecs; + + ecs.component().add(flecs::Sparse); + ecs.component() + .on_set([&](flecs::iter& it, size_t row, Position& p) { + count ++; + v = p; + e_arg = it.entity(row); + }); + + test_int(0, count); + + auto e1 = ecs.entity().add(); + test_int(0, count); + + e1.set({10, 20}); + test_int(1, count); + test_assert(e_arg == e1); + test_int(10, v.x); + test_int(20, v.y); + + auto e2 = ecs.entity().set({30, 40}); + test_int(2, count); + test_assert(e_arg == e2); + test_int(30, v.x); + test_int(40, v.y); + } + + test_int(2, count); +} + void ComponentLifecycle_chained_hooks(void) { flecs::world ecs; @@ -1685,26 +1989,26 @@ void ComponentLifecycle_set_override_no_copy(void) { flecs::world ecs; flecs::entity e = ecs.entity() - .set_override({ 10 }); + .set_auto_override({ 10 }); const NoCopy *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->x_, 10); - test_assert(e.has(flecs::Override | ecs.id())); + test_assert(e.has(flecs::AUTO_OVERRIDE | ecs.id())); } void ComponentLifecycle_set_override_pair_no_copy(void) { flecs::world ecs; flecs::entity e = ecs.entity() - .set_override({ 10 }); + .set_auto_override({ 10 }); const NoCopy *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->x_, 10); - test_assert(e.has(flecs::Override | ecs.pair())); + test_assert(e.has(flecs::AUTO_OVERRIDE | ecs.pair())); } void ComponentLifecycle_set_override_pair_w_entity_no_copy(void) { @@ -1713,13 +2017,13 @@ void ComponentLifecycle_set_override_pair_w_entity_no_copy(void) { flecs::entity tag = ecs.entity(); flecs::entity e = ecs.entity() - .set_override(tag, { 10 }); + .set_auto_override(tag, { 10 }); const NoCopy *ptr = e.get(tag); test_assert(ptr != NULL); test_int(ptr->x_, 10); - test_assert(e.has(flecs::Override | ecs.pair(tag))); + test_assert(e.has(flecs::AUTO_OVERRIDE | ecs.pair(tag))); } void ComponentLifecycle_dtor_after_defer_set(void) { @@ -1783,10 +2087,10 @@ void ComponentLifecycle_dtor_with_relation(void) { test_int(Pod::move_ctor_invoked, 1); } - test_int(Pod::ctor_invoked, 5); + test_int(Pod::ctor_invoked, 4); test_int(Pod::dtor_invoked, 6); - test_int(Pod::move_invoked, 4); - test_int(Pod::move_ctor_invoked, 1); + test_int(Pod::move_invoked, 3); + test_int(Pod::move_ctor_invoked, 2); } void ComponentLifecycle_register_parent_after_child_w_hooks(void) { @@ -1821,3 +2125,121 @@ void ComponentLifecycle_register_parent_after_child_w_hooks_implicit(void) { test_int(Pod::copy_invoked, 0); test_int(Pod::copy_ctor_invoked, 0); } + +void ComponentLifecycle_dtor_relation_target(void) { + { + flecs::world ecs; + + auto e = ecs.entity(); + auto e2 = ecs.entity().emplace(5).add(e); + ecs.entity().emplace(5); + + test_int(CountNoDefaultCtor::ctor_invoked, 2); + test_int(CountNoDefaultCtor::dtor_invoked, 1); + test_int(CountNoDefaultCtor::move_invoked, 0); + test_int(CountNoDefaultCtor::move_ctor_invoked, 1); + + const CountNoDefaultCtor *ptr = e2.get(); + test_assert(ptr != NULL); + test_int(ptr->value, 5); + + test_int(CountNoDefaultCtor::ctor_invoked, 2); + test_int(CountNoDefaultCtor::dtor_invoked, 1); + test_int(CountNoDefaultCtor::move_invoked, 0); + test_int(CountNoDefaultCtor::move_ctor_invoked, 1); + + e.destruct(); + + test_int(CountNoDefaultCtor::ctor_invoked, 2); + test_int(CountNoDefaultCtor::dtor_invoked, 2); + test_int(CountNoDefaultCtor::move_invoked, 0); + test_int(CountNoDefaultCtor::move_ctor_invoked, 2); + } + + test_int(CountNoDefaultCtor::ctor_invoked, 2); + test_int(CountNoDefaultCtor::dtor_invoked, 4); + test_int(CountNoDefaultCtor::move_invoked, 0); + test_int(CountNoDefaultCtor::move_ctor_invoked, 2); +} + +void ComponentLifecycle_sparse_component(void) { + flecs::world world; + + world.component().add(flecs::Sparse); + + auto e = world.entity().add(); + test_assert(e.id() != 0); + test_assert(e.has()); + + const Pod *pod = e.get(); + test_assert(pod != NULL); + test_int(Pod::ctor_invoked, 1); + test_int(Pod::dtor_invoked, 0); + test_int(Pod::copy_invoked, 0); + test_int(Pod::move_invoked, 0); + test_int(Pod::copy_ctor_invoked, 0); + test_int(Pod::move_ctor_invoked, 0); + + e.add(); + + test_int(Pod::ctor_invoked, 1); + test_int(Pod::dtor_invoked, 0); + test_int(Pod::copy_invoked, 0); + test_int(Pod::move_invoked, 0); + test_int(Pod::copy_ctor_invoked, 0); + test_int(Pod::move_ctor_invoked, 0); + + test_int(pod->value, 10); + + e.remove(); + + test_int(Pod::ctor_invoked, 1); + test_int(Pod::dtor_invoked, 1); + test_int(Pod::copy_invoked, 0); + test_int(Pod::move_invoked, 0); + test_int(Pod::copy_ctor_invoked, 0); + test_int(Pod::move_ctor_invoked, 0); +} + +void ComponentLifecycle_count_in_add_hook(void) { + flecs::world ecs; + + int count = 0; + + ecs.component().on_add([&](Position& p) { + count = ecs.count(); + }); + + ecs.entity().set({1, 2}); + test_int(count, 1); + + int matched = 0; + ecs.query().each([&](Position& p) { + matched ++; + }); + + test_int(matched, 1); +} + +void ComponentLifecycle_count_in_remove_hook(void) { + flecs::world ecs; + + int count = 0; + + ecs.component().on_remove([&](Position& p) { + count = ecs.count(); + }); + + auto ent = ecs.entity().set({1, 2}); + test_int(count, 0); + + ent.destruct(); + test_int(count, 1); + + int matched = 0; + ecs.query().each([&](Position& p) { + matched ++; + }); + + test_int(matched, 0); +} diff --git a/vendors/flecs/test/cpp_api/src/Doc.cpp b/vendors/flecs/test/cpp/src/Doc.cpp similarity index 78% rename from vendors/flecs/test/cpp_api/src/Doc.cpp rename to vendors/flecs/test/cpp/src/Doc.cpp index 9f2939630..3e582be24 100644 --- a/vendors/flecs/test/cpp_api/src/Doc.cpp +++ b/vendors/flecs/test/cpp/src/Doc.cpp @@ -1,4 +1,4 @@ -#include +#include void Doc_set_brief(void) { flecs::world ecs; @@ -48,6 +48,18 @@ void Doc_set_color(void) { test_str(e.doc_color(), "A color"); } +void Doc_set_uuid(void) { + flecs::world ecs; + + auto e = ecs.entity("Foo"); + + e.set_doc_uuid("81f50b40-09ff-4ce0-a388-4a52a14052c7"); + + test_assert(e.has(flecs::doc::Uuid)); + + test_str(e.doc_uuid(), "81f50b40-09ff-4ce0-a388-4a52a14052c7"); +} + void Doc_get_name_no_doc_name(void) { flecs::world ecs; diff --git a/vendors/flecs/test/cpp_api/src/Entity.cpp b/vendors/flecs/test/cpp/src/Entity.cpp similarity index 87% rename from vendors/flecs/test/cpp_api/src/Entity.cpp rename to vendors/flecs/test/cpp/src/Entity.cpp index 67d492dfb..7b03b7c62 100644 --- a/vendors/flecs/test/cpp_api/src/Entity.cpp +++ b/vendors/flecs/test/cpp/src/Entity.cpp @@ -1,4 +1,4 @@ -#include +#include struct Parent { struct EntityType { }; @@ -465,7 +465,7 @@ void Entity_get_generic(void) { test_int(p->y, 20); } -void Entity_get_mut_generic(void) { +void Entity_ensure_generic(void) { flecs::world world; auto position = world.component(); @@ -483,7 +483,7 @@ void Entity_get_mut_generic(void) { invoked = true; }); - void *void_p = entity.get_mut(position); + void *void_p = entity.ensure(position); test_assert(void_p != nullptr); Position *p = static_cast(void_p); @@ -534,7 +534,7 @@ void Entity_get_generic_w_id_t(void) { test_int(p->y, 20); } -void Entity_get_mut_generic_w_id(void) { +void Entity_ensure_generic_w_id(void) { flecs::world world; auto position = world.component(); @@ -553,7 +553,7 @@ void Entity_get_mut_generic_w_id(void) { invoked = true; }); - void *void_p = entity.get_mut(id); + void *void_p = entity.ensure(id); test_assert(void_p != nullptr); Position *p = static_cast(void_p); @@ -564,7 +564,7 @@ void Entity_get_mut_generic_w_id(void) { test_bool(invoked, true); } -void Entity_get_mut_generic_w_id_t(void) { +void Entity_ensure_generic_w_id_t(void) { flecs::world world; auto position = world.component(); @@ -583,7 +583,7 @@ void Entity_get_mut_generic_w_id_t(void) { invoked = true; }); - void *void_p = entity.get_mut(id); + void *void_p = entity.ensure(id); test_assert(void_p != nullptr); Position *p = static_cast(void_p); @@ -594,6 +594,113 @@ void Entity_get_mut_generic_w_id_t(void) { test_bool(invoked, true); } +void Entity_get_mut_w_id(void) { + flecs::world world; + + flecs::entity e = world.entity(); + + Position *p = static_cast(e.get_mut(world.id())); + test_assert(p == nullptr); + + e.set({10, 20}); + + p = static_cast(e.get_mut(world.id())); + test_assert(p != nullptr); + + test_int(p->x, 10); + test_int(p->y, 20); +} + +void Entity_get_mut_T(void) { + flecs::world world; + + flecs::entity e = world.entity(); + + Position *p = e.get_mut(); + test_assert(p == nullptr); + + e.set({10, 20}); + + p = e.get_mut(); + test_assert(p != nullptr); + + test_int(p->x, 10); + test_int(p->y, 20); +} + +void Entity_get_mut_r_t(void) { + flecs::world world; + + flecs::entity tgt = world.entity(); + flecs::entity e = world.entity(); + + Position *p = static_cast(e.get_mut(world.id(), tgt)); + test_assert(p == nullptr); + + e.set(tgt, {10, 20}); + + p = static_cast(e.get_mut(world.id(), tgt)); + test_assert(p != nullptr); + + test_int(p->x, 10); + test_int(p->y, 20); +} + +void Entity_get_mut_R_t(void) { + flecs::world world; + + flecs::entity tgt = world.entity(); + flecs::entity e = world.entity(); + + Position *p = e.get_mut(tgt); + test_assert(p == nullptr); + + e.set(tgt, {10, 20}); + + p = e.get_mut(tgt); + test_assert(p != nullptr); + + test_int(p->x, 10); + test_int(p->y, 20); +} + +void Entity_get_mut_R_T(void) { + flecs::world world; + + struct Tgt { }; + + flecs::entity e = world.entity(); + + Position *p = e.get_mut(); + test_assert(p == nullptr); + + e.set({10, 20}); + + p = e.get_mut(); + test_assert(p != nullptr); + + test_int(p->x, 10); + test_int(p->y, 20); +} + +void Entity_get_mut_r_T(void) { + flecs::world world; + + flecs::entity rel = world.entity(); + flecs::entity e = world.entity(); + + Position *p = e.get_mut_second(rel); + test_assert(p == nullptr); + + e.set_second(rel, {10, 20}); + + p = e.get_mut_second(rel); + test_assert(p != nullptr); + + test_int(p->x, 10); + test_int(p->y, 20); +} + void Entity_set_generic(void) { flecs::world world; @@ -711,7 +818,7 @@ void Entity_add_role(void) { auto entity = world.entity(); - entity = entity.add_flags(flecs::Pair); + entity = entity.add_flags(flecs::PAIR); test_assert(entity & ECS_PAIR); } @@ -723,7 +830,7 @@ void Entity_remove_role(void) { flecs::entity_t id = entity; - entity = entity.add_flags(flecs::Pair); + entity = entity.add_flags(flecs::PAIR); test_assert(entity & ECS_PAIR); @@ -737,13 +844,13 @@ void Entity_has_role(void) { auto entity = world.entity(); - entity = entity.add_flags(flecs::Pair); + entity = entity.add_flags(flecs::PAIR); - test_assert(entity.has_flags(flecs::Pair)); + test_assert(entity.has_flags(flecs::PAIR)); entity = entity.remove_flags(); - test_assert(!entity.has_flags(flecs::Pair)); + test_assert(!entity.has_flags(flecs::PAIR)); } void Entity_pair_role(void) { @@ -753,9 +860,9 @@ void Entity_pair_role(void) { auto b = world.entity(); auto pair = flecs::id(a, b); - pair = pair.add_flags(flecs::Pair); + pair = pair.add_flags(flecs::PAIR); - test_assert(pair.has_flags(flecs::Pair)); + test_assert(pair.has_flags(flecs::PAIR)); auto rel = pair.first(); auto obj = pair.second(); @@ -1069,10 +1176,13 @@ void Entity_clear(void) { void Entity_foce_owned(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto prefab = world.prefab() .add() .add() - .override(); + .auto_override(); auto e = world.entity() .add(flecs::IsA, prefab); @@ -1086,11 +1196,14 @@ void Entity_foce_owned(void) { void Entity_force_owned_2(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto prefab = world.prefab() .add() .add() - .override() - .override(); + .auto_override() + .auto_override(); auto e = world.entity() .add(flecs::IsA, prefab); @@ -1104,10 +1217,13 @@ void Entity_force_owned_2(void) { void Entity_force_owned_nested(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto prefab = world.prefab() .add() .add() - .override(); + .auto_override(); auto prefab_2 = world.prefab() .add(flecs::IsA, prefab); @@ -1194,6 +1310,8 @@ void Entity_get_parent(void) { void Entity_is_component_enabled(void) { flecs::world world; + world.component().add(flecs::CanToggle); + auto e = world.entity() .add(); @@ -1203,6 +1321,8 @@ void Entity_is_component_enabled(void) { void Entity_is_enabled_component_enabled(void) { flecs::world world; + + world.component().add(flecs::CanToggle); auto e = world.entity() .add() @@ -1214,6 +1334,8 @@ void Entity_is_enabled_component_enabled(void) { void Entity_is_disabled_component_enabled(void) { flecs::world world; + world.component().add(flecs::CanToggle); + auto e = world.entity() .add() .disable(); @@ -1224,6 +1346,8 @@ void Entity_is_disabled_component_enabled(void) { void Entity_is_pair_enabled(void) { flecs::world world; + world.component().add(flecs::CanToggle); + struct TgtA { }; struct TgtB { }; @@ -1237,8 +1361,12 @@ void Entity_is_pair_enabled(void) { void Entity_is_enabled_pair_enabled(void) { flecs::world world; + world.component().add(flecs::CanToggle); + struct Tgt { }; + world.component().add(flecs::CanToggle); + auto e = world.entity() .add() .enable(); @@ -1249,6 +1377,8 @@ void Entity_is_enabled_pair_enabled(void) { void Entity_is_disabled_pair_enabled(void) { flecs::world world; + world.component().add(flecs::CanToggle); + struct Tgt { }; auto e = world.entity() @@ -1261,6 +1391,8 @@ void Entity_is_disabled_pair_enabled(void) { void Entity_is_pair_enabled_w_ids(void) { flecs::world world; + world.component().add(flecs::CanToggle); + auto rel = world.entity(); auto tgt_a = world.entity(); auto tgt_b = world.entity(); @@ -1275,7 +1407,7 @@ void Entity_is_pair_enabled_w_ids(void) { void Entity_is_enabled_pair_enabled_w_ids(void) { flecs::world world; - auto rel = world.entity(); + auto rel = world.entity().add(flecs::CanToggle); auto tgt = world.entity(); auto e = world.entity() @@ -1288,7 +1420,7 @@ void Entity_is_enabled_pair_enabled_w_ids(void) { void Entity_is_disabled_pair_enabled_w_ids(void) { flecs::world world; - auto rel = world.entity(); + auto rel = world.entity().add(flecs::CanToggle); auto tgt = world.entity(); auto e = world.entity() @@ -1301,6 +1433,8 @@ void Entity_is_disabled_pair_enabled_w_ids(void) { void Entity_is_pair_enabled_w_tgt_id(void) { flecs::world world; + world.component().add(flecs::CanToggle); + auto tgt_a = world.entity(); auto tgt_b = world.entity(); @@ -1314,6 +1448,8 @@ void Entity_is_pair_enabled_w_tgt_id(void) { void Entity_is_enabled_pair_enabled_w_tgt_id(void) { flecs::world world; + world.component().add(flecs::CanToggle); + auto tgt = world.entity(); auto e = world.entity() @@ -1326,6 +1462,8 @@ void Entity_is_enabled_pair_enabled_w_tgt_id(void) { void Entity_is_disabled_pair_enabled_w_tgt_id(void) { flecs::world world; + world.component().add(flecs::CanToggle); + auto tgt = world.entity(); auto e = world.entity() @@ -1408,8 +1546,10 @@ void Entity_set_deduced(void) { void Entity_override(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto base = world.entity() - .override(); + .auto_override(); auto e = world.entity() .add(flecs::IsA, base); @@ -1421,11 +1561,11 @@ void Entity_override(void) { void Entity_override_id(void) { flecs::world world; - auto tag_a = world.entity(); - auto tag_b = world.entity(); + auto tag_a = world.entity().add(flecs::OnInstantiate, flecs::Inherit); + auto tag_b = world.entity().add(flecs::OnInstantiate, flecs::Inherit); auto base = world.entity() - .override(tag_a) + .auto_override(tag_a) .add(tag_b); auto e = world.entity() @@ -1441,11 +1581,12 @@ void Entity_override_id(void) { void Entity_override_pair_w_tgt_id(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); auto tgt_a = world.entity(); auto tgt_b = world.entity(); auto base = world.entity() - .override(tgt_a) + .auto_override(tgt_a) .add(tgt_b); auto e = world.entity() @@ -1461,12 +1602,12 @@ void Entity_override_pair_w_tgt_id(void) { void Entity_override_pair_w_ids(void) { flecs::world world; - auto rel = world.entity(); + auto rel = world.entity().add(flecs::OnInstantiate, flecs::Inherit); auto tgt_a = world.entity(); auto tgt_b = world.entity(); auto base = world.entity() - .override(rel, tgt_a) + .auto_override(rel, tgt_a) .add(rel, tgt_b); auto e = world.entity() @@ -1485,8 +1626,10 @@ void Entity_override_pair(void) { struct TagA { }; struct TagB { }; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto base = world.entity() - .override() + .auto_override() .add(); auto e = world.entity() @@ -1502,8 +1645,10 @@ void Entity_override_pair(void) { void Entity_set_override(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto base = world.entity() - .set_override({10, 20}); + .set_auto_override({10, 20}); auto e = world.entity() .add(flecs::IsA, base); @@ -1524,10 +1669,12 @@ void Entity_set_override(void) { void Entity_set_override_lvalue(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + Position plvalue = {10, 20}; auto base = world.entity() - .set_override(plvalue); + .set_auto_override(plvalue); auto e = world.entity() .add(flecs::IsA, base); @@ -1548,10 +1695,12 @@ void Entity_set_override_lvalue(void) { void Entity_set_override_pair(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + struct Tgt { }; auto base = world.entity() - .set_override({10, 20}); + .set_auto_override({10, 20}); auto e = world.entity() .add(flecs::IsA, base); @@ -1572,10 +1721,12 @@ void Entity_set_override_pair(void) { void Entity_set_override_pair_w_tgt_id(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto tgt = world.entity(); auto base = world.entity() - .set_override(tgt, {10, 20}); + .set_auto_override(tgt, {10, 20}); auto e = world.entity() .add(flecs::IsA, base); @@ -1598,8 +1749,10 @@ void Entity_set_override_pair_w_rel_tag(void) { struct Tgt { }; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto base = world.entity() - .set_override({10, 20}); + .set_auto_override({10, 20}); auto e = world.entity() .add(flecs::IsA, base); @@ -1620,7 +1773,7 @@ void Entity_set_override_pair_w_rel_tag(void) { void Entity_emplace_override(void) { flecs::world world; - auto e = world.entity().emplace_override(10); + auto e = world.entity().emplace_auto_override(10); test_assert(e.has()); const NoDefaultCtor *ptr = e.get(); @@ -1631,7 +1784,7 @@ void Entity_emplace_override(void) { void Entity_emplace_override_pair(void) { flecs::world world; - auto e = world.entity().emplace_override(10); + auto e = world.entity().emplace_auto_override(10); test_assert((e.has())); const NoDefaultCtor *ptr = e.get(); @@ -1873,7 +2026,7 @@ void Entity_get_2_components_w_callback(void) { test_bool(e_3.get([](const Position& p, const Velocity& v) {}), false); } -void Entity_get_mut_1_component_w_callback(void) { +void Entity_ensure_1_component_w_callback(void) { flecs::world ecs; auto e_1 = ecs.entity() @@ -1912,7 +2065,7 @@ void Entity_get_mut_1_component_w_callback(void) { test_bool(e_3.get([](const Position& p) {}), false); } -void Entity_get_mut_2_components_w_callback(void) { +void Entity_ensure_2_components_w_callback(void) { flecs::world ecs; auto e_1 = ecs.entity() @@ -1969,7 +2122,7 @@ void Entity_get_component_w_callback_nested(void) { }), true); } -void Entity_get_mut_component_w_callback_nested(void) { +void Entity_ensure_component_w_callback_nested(void) { install_test_abort(); flecs::world ecs; @@ -1992,7 +2145,7 @@ void Entity_set_1_component_w_callback(void) { flecs::world ecs; auto e = ecs.entity() - .set([](Position& p){ + .insert([](Position& p){ p.x = 10; p.y = 20; }); @@ -2009,7 +2162,7 @@ void Entity_set_2_components_w_callback(void) { flecs::world ecs; auto e = ecs.entity() - .set([](Position& p, Velocity& v){ + .insert([](Position& p, Velocity& v){ p = {10, 20}; v = {1, 2}; }); @@ -2031,7 +2184,7 @@ void Entity_set_3_components_w_callback(void) { flecs::world ecs; auto e = ecs.entity() - .set([](Position& p, Velocity& v, Mass& m){ + .insert([](Position& p, Velocity& v, Mass& m){ p = {10, 20}; v = {1, 2}; m = {50}; @@ -2060,7 +2213,7 @@ void Entity_defer_set_1_component(void) { ecs.defer_begin(); auto e = ecs.entity() - .set([](Position& p){ + .insert([](Position& p){ p.x = 10; p.y = 20; }); @@ -2083,7 +2236,7 @@ void Entity_defer_set_2_components(void) { ecs.defer_begin(); auto e = ecs.entity() - .set([](Position& p, Velocity& v){ + .insert([](Position& p, Velocity& v){ p = {10, 20}; v = {1, 2}; }); @@ -2111,7 +2264,7 @@ void Entity_defer_set_3_components(void) { ecs.defer_begin(); auto e = ecs.entity() - .set([](Position& p, Velocity& v, Mass& m){ + .insert([](Position& p, Velocity& v, Mass& m){ p = {10, 20}; v = {1, 2}; m = {50}; @@ -2161,7 +2314,7 @@ void Entity_set_2_w_on_set(void) { }); auto e = ecs.entity() - .set([](Position& p, Velocity& v){ + .insert([](Position& p, Velocity& v){ p = {10, 20}; v = {1, 2}; }); @@ -2203,7 +2356,7 @@ void Entity_defer_set_2_w_on_set(void) { ecs.defer_begin(); auto e = ecs.entity() - .set([](Position& p, Velocity& v){ + .insert([](Position& p, Velocity& v){ p = {10, 20}; v = {1, 2}; }); @@ -2230,7 +2383,7 @@ void Entity_set_2_after_fluent(void) { auto e = ecs.entity() .set({50}) - .set([](Position& p, Velocity& v){ + .insert([](Position& p, Velocity& v){ p = {10, 20}; v = {1, 2}; }); @@ -2254,7 +2407,7 @@ void Entity_set_2_before_fluent(void) { flecs::world ecs; auto e = ecs.entity() - .set([](Position& p, Velocity& v){ + .insert([](Position& p, Velocity& v){ p = {10, 20}; v = {1, 2}; }) @@ -2289,7 +2442,7 @@ void Entity_set_2_after_set_1(void) { }), true); // Set both Position and Velocity - e.set([](Position& p, Velocity& v) { + e.insert([](Position& p, Velocity& v) { p = {10, 20}; v = {1, 2}; }); @@ -2331,7 +2484,7 @@ void Entity_set_2_after_set_2(void) { test_int(called, 1); // Set both Position and Velocity (doesn't add any components) - e.set([](Position& p, Velocity& v) { + e.insert([](Position& p, Velocity& v) { p = {10, 20}; v = {3, 4}; }); @@ -2364,7 +2517,9 @@ void Entity_with_self(void) { test_assert(!self.has(Tag)); int count = 0; - auto q = ecs.query_builder<>().term(Tag).build(); + auto q = ecs.query_builder<>() + .with(Tag) + .build(); q.each([&](flecs::entity e) { test_assert(e.has(Tag)); @@ -2396,7 +2551,9 @@ void Entity_with_relation_type_self(void) { test_assert(!self.has(Bob)); int count = 0; - auto q = ecs.query_builder<>().term(Bob).build(); + auto q = ecs.query_builder<>() + .with(Bob) + .build(); q.each([&](flecs::entity e) { test_assert(e.has(Bob)); @@ -2428,7 +2585,9 @@ void Entity_with_relation_self(void) { test_assert(!self.has(Likes, Bob)); int count = 0; - auto q = ecs.query_builder<>().term(Likes, Bob).build(); + auto q = ecs.query_builder<>() + .with(Likes, Bob) + .build(); q.each([&](flecs::entity e) { test_assert(e.has(Likes, Bob)); @@ -2513,7 +2672,9 @@ void Entity_with_scope(void) { test_assert(!self.has(flecs::ChildOf, parent)); int count = 0; - auto q = ecs.query_builder<>().term(flecs::ChildOf, parent).build(); + auto q = ecs.query_builder<>() + .with(flecs::ChildOf, parent) + .build(); q.each([&](flecs::entity e) { test_assert(e.has(flecs::ChildOf, parent)); @@ -2804,6 +2965,25 @@ void Entity_defer_suspend_resume(void) { test_assert(e.has()); } +void Entity_defer_ensure(void) { + flecs::world world; + + flecs::entity e = world.entity(); + + { + world.defer_begin(); + Position& p = e.ensure(); + p.x = 10; + p.y = 20; + world.defer_end(); + } + + Position* p = e.get_mut(); + test_assert(p != nullptr); + test_int(p->x, 10); + test_int(p->y, 20); +} + void Entity_with_after_builder_method(void) { flecs::world ecs; @@ -2961,9 +3141,9 @@ void Entity_pair_id_str(void) { void Entity_role_id_str(void) { flecs::world ecs; - flecs::id id = flecs::id(ecs, ECS_OVERRIDE | ecs.entity("Foo")); + flecs::id id = flecs::id(ecs, ECS_AUTO_OVERRIDE | ecs.entity("Foo")); - test_str("OVERRIDE|Foo", id.str()); + test_str("AUTO_OVERRIDE|Foo", id.str()); } void Entity_id_str_from_entity_view(void) { @@ -4385,5 +4565,301 @@ void Entity_world_lookup_not_recursive(void) { flecs::entity foo = world.scope(parent).entity("foo"); test_assert(world.scope(child).lookup("foo") == foo); - test_assert(world.scope(child).lookup("foo", false) == 0); + test_assert(world.scope(child).lookup("foo", "::", "::", false) == 0); +} + +void Entity_world_lookup_custom_sep(void) { + flecs::world world; + + flecs::entity parent = world.entity("parent"); + flecs::entity child = world.scope(parent).entity("child"); + flecs::entity foo = world.scope(parent).entity("foo"); + + test_assert(world.lookup("parent.child", ".") == child); + test_assert(world.lookup("parent.foo", ".") == foo); +} + +void Entity_world_lookup_custom_root_sep(void) { + flecs::world world; + + flecs::entity parent = world.entity("parent"); + flecs::entity child = world.scope(parent).entity("child"); + flecs::entity foo = world.scope(parent).entity("foo"); + + test_assert(world.lookup(".parent.child", ".", ".") == child); + test_assert(world.lookup(".parent.foo", ".", ".") == foo); +} + +void Entity_depends_on(void) { + flecs::world world; + + flecs::entity a = world.entity(); + flecs::entity b = world.entity().depends_on(a); + test_assert(b.has(flecs::DependsOn, a)); +} + +void Entity_depends_on_type(void) { + flecs::world world; + + flecs::entity b = world.entity().depends_on(); + test_assert(b.has(flecs::DependsOn, world.id())); +} + +void Entity_const_entity_add_remove(void) { + flecs::world world; + + const flecs::entity e = world.entity(); + + e.add(); + test_assert(e.has()); + + e.remove(); + test_assert(!e.has()); +} + +void Entity_const_entity_set(void) { + flecs::world world; + + const flecs::entity e = world.entity(); + + e.set({10, 20}); + test_assert(e.get() != nullptr); + test_int(e.get()->x, 10); + test_int(e.get()->y, 20); +} + +void Entity_const_entity_get_mut(void) { + flecs::world world; + + const flecs::entity e = world.entity(); + + Position *p = e.get_mut(); + test_assert(p == nullptr); + test_assert(!e.has()); + + e.add(); + p = e.get_mut(); + test_assert(p != nullptr); + test_assert(e.has()); + + e.modified(); +} + +void Entity_const_entity_ensure(void) { + flecs::world world; + + const flecs::entity e = world.entity(); + + e.ensure(); + test_assert(e.has()); + e.modified(); +} + +void Entity_const_entity_destruct(void) { + flecs::world world; + + const flecs::entity e = world.entity(); + + e.destruct(); + test_assert(!e.is_alive()); +} + +void Entity_const_entity_emit_after_build(void) { + flecs::world world; + + const flecs::entity e = world.entity(); + + int32_t count = 0; + e.observe([&](Velocity& v) { + test_int(v.x, 1); + test_int(v.y, 2); + count ++; + }); + + e.set({10, 20}).emit({1, 2}); + + test_assert(e.get() != nullptr); + test_int(e.get()->x, 10); + test_int(e.get()->y, 20); + + test_int(count, 1); +} + +void Entity_const_entity_set_doc(void) { + flecs::world world; + + const flecs::entity e = world.entity(); + + e.set_doc_name("name"); + e.set_doc_color("color"); + e.set_doc_detail("detail"); + e.set_doc_brief("brief"); + e.set_doc_link("link"); + + test_str(e.doc_name(), "name"); + test_str(e.doc_color(), "color"); + test_str(e.doc_detail(), "detail"); + test_str(e.doc_brief(), "brief"); + test_str(e.doc_link(), "link"); +} + +void Entity_set_sparse(void) { + flecs::world world; + + world.component().add(flecs::Sparse); + + flecs::entity e = world.entity().set(Velocity{1, 2}); + + test_assert(e.has()); + + const Velocity *v = e.get(); + test_int(v->x, 1); + test_int(v->y, 2); +} + +void Entity_insert_1_sparse(void) { + flecs::world world; + + world.component().add(flecs::Sparse); + + flecs::entity e = world.entity().insert([](Velocity& v) { + v.x = 1; + v.y = 2; + }); + + test_assert(e.has()); + + const Velocity *v = e.get(); + test_int(v->x, 1); + test_int(v->y, 2); +} + +void Entity_insert_2_w_1_sparse(void) { + flecs::world world; + + world.component(); + world.component().add(flecs::Sparse); + + flecs::entity e = world.entity().insert([](Position& p, Velocity& v) { + p.x = 10; + p.y = 20; + v.x = 1; + v.y = 2; + }); + + test_assert(e.has()); + test_assert(e.has()); + + const Position *p = e.get(); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = e.get(); + test_int(v->x, 1); + test_int(v->y, 2); +} + +void Entity_emplace_sparse(void) { + flecs::world world; + + world.component().add(flecs::Sparse); + + flecs::entity e = world.entity().emplace(1.0f, 2.0f); + + test_assert(e.has()); + + const Velocity *v = e.get(); + test_int(v->x, 1); + test_int(v->y, 2); +} + +void Entity_override_sparse(void) { + flecs::world world; + + world.component().add(flecs::Sparse); + + flecs::entity base = world.entity().set(Velocity{1, 2}); + + flecs::entity e = world.entity().is_a(base); + + test_assert(e.has()); + test_assert(e.owns()); + + const Velocity *v = e.get(); + test_int(v->x, 1); + test_int(v->y, 2); +} + +void Entity_delete_w_override_sparse(void) { + flecs::world world; + + world.component().add(flecs::Sparse); + + flecs::entity base = world.entity().set(Velocity{1, 2}); + + flecs::entity e = world.entity().is_a(base); + + test_assert(e.has()); + test_assert(e.owns()); + + const Velocity *v = e.get(); + test_int(v->x, 1); + test_int(v->y, 2); + + e.destruct(); +} + +void Entity_get_pair_second_invalid_type(void) { + install_test_abort(); + + flecs::world world; + + auto v = world.component(); + + test_expect_abort(); + world.entity().get_second(v); +} + +void Entity_get_mut_pair_second_invalid_type(void) { + install_test_abort(); + + flecs::world world; + + auto v = world.component(); + + test_expect_abort(); + world.entity().get_mut_second(v); +} + +void Entity_ensure_pair_second_invalid_type(void) { + install_test_abort(); + + flecs::world world; + + auto v = world.component(); + + test_expect_abort(); + world.entity().ensure_second(v); +} + +void Entity_set_pair_second_invalid_type(void) { + install_test_abort(); + + flecs::world world; + + auto v = world.component(); + + test_expect_abort(); + world.entity().set_second(v, {0}); +} + +void Entity_get_ref_pair_second_invalid_type(void) { + install_test_abort(); + + flecs::world world; + + auto v = world.component(); + + test_expect_abort(); + world.entity().get_ref_second(v); } diff --git a/vendors/flecs/test/cpp_api/src/Enum.cpp b/vendors/flecs/test/cpp/src/Enum.cpp similarity index 72% rename from vendors/flecs/test/cpp_api/src/Enum.cpp rename to vendors/flecs/test/cpp/src/Enum.cpp index 600780597..69a07a2b5 100644 --- a/vendors/flecs/test/cpp_api/src/Enum.cpp +++ b/vendors/flecs/test/cpp/src/Enum.cpp @@ -1,4 +1,4 @@ -#include +#include enum StandardEnum { Red, Green, Blue @@ -12,6 +12,58 @@ enum SparseEnum { Black = 1, White = 3, Grey = 5 }; +enum BitMaskEnum { + ZERO = 0, + bit_LS_0, + bit_LS_1 = bit_LS_0<<1, + bit_LS_2 = bit_LS_0<<2, + bit_LS_3 = bit_LS_0<<3, + bit_LS_4 = bit_LS_0<<4, + bit_LS_5 = bit_LS_0<<5, + bit_LS_6 = bit_LS_0<<6, + bit_LS_7 = bit_LS_0<<7, + bit_LS_8 = bit_LS_0<<8, + bit_LS_14 = bit_LS_0<<14, + bit_LS_15 = bit_LS_0<<15, + bit_LS_28 = bit_LS_0<<28, + bit_LS_29 = bit_LS_0<<29, + bit_LS_30 = bit_LS_0<<30, + // bit_LS_31 = bit_LS_0<<31, +}; + +enum class TypedBitMaskEnum : uint32_t { + ZERO = 0, + bit_LS_0, + bit_LS_1 = bit_LS_0<<1, + bit_LS_2 = bit_LS_0<<2, + bit_LS_3 = bit_LS_0<<3, + bit_LS_4 = bit_LS_0<<4, + bit_LS_5 = bit_LS_0<<5, + bit_LS_6 = bit_LS_0<<6, + bit_LS_7 = bit_LS_0<<7, + bit_LS_8 = bit_LS_0<<8, + bit_LS_9 = bit_LS_0<<9, + bit_LS_10 = bit_LS_0<<10, + bit_LS_11 = bit_LS_0<<11, + bit_LS_12 = bit_LS_0<<12, + bit_LS_13 = bit_LS_0<<13, + bit_LS_14 = bit_LS_0<<14, + bit_LS_15 = bit_LS_0<<15, + bit_LS_31 = bit_LS_0<<31, +}; + +enum MixedConstantBitmaskEnum { + Vim, + Emacs, + Nano, + VsCode, + Editor = 1 << 7, + OperatingSystem = 1 << 8, + Terminal = 1 << 9, + GUI = 1 << 30, + BAD_BEEF = -0xBADBEEF +}; + enum class EnumClass { Grass, Sand, Stone }; @@ -47,6 +99,7 @@ void Enum_standard_enum_reflection(void) { flecs::world ecs; auto enum_type = flecs::enum_type(ecs); + test_int(enum_type.impl_.constants_size, 3); auto e = enum_type.entity(); test_assert(e != 0); @@ -55,6 +108,11 @@ void Enum_standard_enum_reflection(void) { test_int(enum_type.first(), Red); test_int(enum_type.last(), Blue); + test_int(enum_type.index_by_value(Red), 0); + test_int(enum_type.index_by_value(Green), 1); + test_int(enum_type.index_by_value(Blue), 2); + test_int(enum_type.index_by_value(Blue+1), -1); + auto e_red = enum_type.entity(Red); auto e_green = enum_type.entity(Green); auto e_blue = enum_type.entity(Blue); @@ -84,13 +142,19 @@ void Enum_sparse_enum_reflection(void) { flecs::world ecs; auto enum_type = flecs::enum_type(ecs); + test_int(enum_type.impl_.constants_size, 3); auto e = enum_type.entity(); test_assert(e != 0); test_assert(e == ecs.component()); test_str(e.path().c_str(), "::SparseEnum"); - test_int(enum_type.first(), Black); - test_int(enum_type.last(), Grey); + test_int(enum_type.first(), 0); + test_int(enum_type.last(), 2); + + test_int(enum_type.index_by_value(Black), 0); + test_int(enum_type.index_by_value(White), 1); + test_int(enum_type.index_by_value(Grey), 2); + test_int(enum_type.index_by_value(Grey+1), -1); auto e_black = enum_type.entity(Black); auto e_white = enum_type.entity(White); @@ -120,10 +184,158 @@ void Enum_sparse_enum_reflection(void) { test_bool(enum_type.is_valid(6), false); } +void Enum_bitmask_enum_reflection(void) { + flecs::world ecs; + + auto enum_type = flecs::enum_type(ecs); + test_int(enum_type.impl_.constants_size, 15); + + auto e = enum_type.entity(); + test_assert(e != 0); + test_assert(e == ecs.component()); + test_str(e.path().c_str(), "::BitMaskEnum"); + test_int(enum_type.first(), 0); + test_int(enum_type.last(), 14); + + test_int(enum_type.index_by_value(ZERO), 0); + test_int(enum_type.index_by_value(bit_LS_0), 1); + test_int(enum_type.index_by_value(bit_LS_1), 2); + test_int(enum_type.index_by_value(2), 2); + test_int(enum_type.index_by_value(4), 3); + test_int(enum_type.index_by_value(bit_LS_30), 14); + test_int(enum_type.index_by_value(7), -1); + + auto e_8 = enum_type.entity(8); + auto e_16 = enum_type.entity(bit_LS_4); + auto e_32 = enum_type.entity(0x20); + auto e_ls_30 = enum_type.entity(bit_LS_30); + + test_assert(e_8 != 0); + test_str(e_8.path().c_str(), "::BitMaskEnum::bit_LS_3"); + test_bool(enum_type.is_valid(bit_LS_3), true); + test_assert(e_8.get() != nullptr); + test_assert(e_8.get()[0] == bit_LS_3); + + test_assert(e_16 != 0); + test_str(e_16.path().c_str(), "::BitMaskEnum::bit_LS_4"); + test_bool(enum_type.is_valid(bit_LS_4), true); + test_assert(e_16.get() != nullptr); + test_assert(e_16.get()[0] == bit_LS_4); + + test_assert(e_32 != 0); + test_str(e_32.path().c_str(), "::BitMaskEnum::bit_LS_5"); + test_bool(enum_type.is_valid(bit_LS_5), true); + test_assert(e_32.get() != nullptr); + test_assert(e_32.get()[0] == bit_LS_5); + + test_assert(e_ls_30 != 0); + test_str(e_ls_30.path().c_str(), "::BitMaskEnum::bit_LS_30"); + test_bool(enum_type.is_valid(bit_LS_30), true); + test_assert(e_ls_30.get() != nullptr); + test_assert(e_ls_30.get()[0] == bit_LS_30); + + test_bool(enum_type.is_valid(3), false); + test_bool(enum_type.is_valid(5), false); + test_bool(enum_type.is_valid((1 << 31) + 1), false); +} + +void Enum_bitmask_enum_with_type_reflection(void) { + flecs::world ecs; + + auto enum_type = flecs::enum_type(ecs); + test_int(enum_type.impl_.constants_size, 18); + + auto e = enum_type.entity(); + test_assert(e != 0); + test_assert(e == ecs.component()); + test_str(e.path().c_str(), "::TypedBitMaskEnum"); + test_int(enum_type.first(), 0); + test_int(enum_type.last(), 17); + + test_int(enum_type.index_by_value(TypedBitMaskEnum::ZERO), 0); + test_int(enum_type.index_by_value(TypedBitMaskEnum::bit_LS_0), 1); + test_int(enum_type.index_by_value(TypedBitMaskEnum::bit_LS_1), 2); + test_int(enum_type.index_by_value(2), 2); + test_int(enum_type.index_by_value(4), 3); + test_int(enum_type.index_by_value(7), -1); + + auto e_8 = enum_type.entity(8); + auto e_16 = enum_type.entity(TypedBitMaskEnum::bit_LS_4); + auto e_32 = enum_type.entity(0x20); + auto e_ls_15 = enum_type.entity(TypedBitMaskEnum::bit_LS_15); + auto e_ls_31 = enum_type.entity(TypedBitMaskEnum::bit_LS_31); + + test_assert(e_8 != 0); + test_str(e_8.path().c_str(), "::TypedBitMaskEnum::bit_LS_3"); + test_bool(enum_type.is_valid(TypedBitMaskEnum::bit_LS_3), true); + test_assert(e_8.get() != nullptr); + test_assert(e_8.get()[0] == TypedBitMaskEnum::bit_LS_3); + + test_assert(e_16 != 0); + test_str(e_16.path().c_str(), "::TypedBitMaskEnum::bit_LS_4"); + test_bool(enum_type.is_valid(TypedBitMaskEnum::bit_LS_4), true); + test_assert(e_16.get() != nullptr); + test_assert(e_16.get()[0] == TypedBitMaskEnum::bit_LS_4); + + test_assert(e_32 != 0); + test_str(e_32.path().c_str(), "::TypedBitMaskEnum::bit_LS_5"); + test_bool(enum_type.is_valid(TypedBitMaskEnum::bit_LS_5), true); + test_assert(e_32.get() != nullptr); + test_assert(e_32.get()[0] == TypedBitMaskEnum::bit_LS_5); + + test_assert(e_ls_15 != 0); + test_str(e_ls_15.path().c_str(), "::TypedBitMaskEnum::bit_LS_15"); + test_bool(enum_type.is_valid(TypedBitMaskEnum::bit_LS_15), true); + test_assert(e_ls_15.get() != nullptr); + test_assert(e_ls_15.get()[0] == TypedBitMaskEnum::bit_LS_15); + + test_assert(e_ls_31 != 0); + test_str(e_ls_31.path().c_str(), "::TypedBitMaskEnum::bit_LS_31"); + test_bool(enum_type.is_valid(TypedBitMaskEnum::bit_LS_31), true); + test_assert(e_ls_31.get() != nullptr); + test_assert(e_ls_31.get()[0] == TypedBitMaskEnum::bit_LS_31); + + test_bool(enum_type.is_valid(3), false); + test_bool(enum_type.is_valid(5), false); + test_bool(enum_type.is_valid(6), false); +} + +void Enum_enum_with_mixed_constants_and_bitmask(void) { + flecs::world ecs; + auto ec = ecs.component() + .constant("BAD_BEEF", BAD_BEEF); + + auto bad_beef_e = ec.lookup("BAD_BEEF"); + test_assert(bad_beef_e != 0); + + flecs::entity vim = ecs.entity().add(Vim); + test_assert(vim.has(Vim)); + + flecs::entity vs_code = ecs.entity().add(MixedConstantBitmaskEnum::VsCode); + test_assert(vs_code.has(VsCode)); + + flecs::entity terminal = ecs.entity().add(MixedConstantBitmaskEnum::Terminal); + test_assert(terminal.has(Terminal)); + + flecs::entity gui = ecs.entity().add(MixedConstantBitmaskEnum::GUI); + test_assert(gui.has(GUI)); + + vim.add(Editor); + test_assert(vim.has(Editor)); + test_assert(!vim.has(Vim)); + + flecs::entity bad_beef = ecs.entity().add(MixedConstantBitmaskEnum::BAD_BEEF); + test_assert(bad_beef.has(BAD_BEEF)); + bad_beef.add(Nano); + test_assert(bad_beef.has(Nano)); + test_assert(!bad_beef.has(BAD_BEEF)); +} + void Enum_enum_class_reflection(void) { flecs::world ecs; auto enum_type = flecs::enum_type(ecs); + test_int(enum_type.impl_.constants_size, 3); auto e = enum_type.entity(); test_assert(e != 0); @@ -132,6 +344,11 @@ void Enum_enum_class_reflection(void) { test_int(enum_type.first(), (int)EnumClass::Grass); test_int(enum_type.last(), (int)EnumClass::Stone); + test_int(enum_type.index_by_value(EnumClass::Grass), 0); + test_int(enum_type.index_by_value(EnumClass::Sand), 1); + test_int(enum_type.index_by_value(EnumClass::Stone), 2); + test_int(enum_type.index_by_value((int)EnumClass::Stone + 1), -1); + auto e_grass = enum_type.entity(EnumClass::Grass); auto e_sand = enum_type.entity(EnumClass::Sand); auto e_stone = enum_type.entity(EnumClass::Stone); @@ -161,6 +378,7 @@ void Enum_prefixed_enum_reflection(void) { flecs::world ecs; auto enum_type = flecs::enum_type(ecs); + test_int(enum_type.impl_.constants_size, 2); auto e = enum_type.entity(); test_assert(e != 0); @@ -169,6 +387,9 @@ void Enum_prefixed_enum_reflection(void) { test_int(enum_type.first(), PrefixEnum::PrefixEnumFoo); test_int(enum_type.last(), PrefixEnum::PrefixEnumBar); + test_int(enum_type.index_by_value(PrefixEnum::PrefixEnumFoo), 0); + test_int(enum_type.index_by_value(PrefixEnum::PrefixEnumBar), 1); + auto e_foo = enum_type.entity(PrefixEnum::PrefixEnumFoo); auto e_bar = enum_type.entity(PrefixEnum::PrefixEnumBar); @@ -180,7 +401,7 @@ void Enum_prefixed_enum_reflection(void) { test_assert(e_bar != 0); test_str(e_bar.path().c_str(), "::PrefixEnum::Bar"); - test_bool(enum_type.is_valid(PrefixEnum::PrefixEnumFoo), true); + test_bool(enum_type.is_valid(PrefixEnum::PrefixEnumBar), true); test_assert(e_bar.get() != nullptr); test_assert(e_bar.get()[0] == PrefixEnum::PrefixEnumBar); @@ -191,6 +412,7 @@ void Enum_constant_with_num_reflection(void) { flecs::world ecs; auto enum_type = flecs::enum_type(ecs); + test_int(enum_type.impl_.constants_size, 3); auto e = enum_type.entity(); test_assert(e != 0); @@ -199,6 +421,10 @@ void Enum_constant_with_num_reflection(void) { test_int(enum_type.first(), ConstantsWithNum::Num1); test_int(enum_type.last(), ConstantsWithNum::Num3); + test_int(enum_type.index_by_value(ConstantsWithNum::Num1), 0); + test_int(enum_type.index_by_value(ConstantsWithNum::Num2), 1); + test_int(enum_type.index_by_value(ConstantsWithNum::Num3), 2); + auto num_1 = enum_type.entity(ConstantsWithNum::Num1); auto num_2 = enum_type.entity(ConstantsWithNum::Num2); auto num_3 = enum_type.entity(ConstantsWithNum::Num3); @@ -389,21 +615,21 @@ void Enum_query_enum_wildcard(void) { auto e3 = ecs.entity().add(StandardEnum::Blue); auto q = ecs.query_builder() - .term(flecs::Wildcard) + .with(flecs::Wildcard) .build(); int32_t count = 0; q.each([&](flecs::iter& it, size_t index) { if (it.entity(index) == e1) { - test_assert(it.pair(1).second() == ecs.id(StandardEnum::Red)); + test_assert(it.pair(0).second() == ecs.id(StandardEnum::Red)); count ++; } if (it.entity(index) == e2) { - test_assert(it.pair(1).second() == ecs.id(StandardEnum::Green)); + test_assert(it.pair(0).second() == ecs.id(StandardEnum::Green)); count ++; } if (it.entity(index) == e3) { - test_assert(it.pair(1).second() == ecs.id(StandardEnum::Blue)); + test_assert(it.pair(0).second() == ecs.id(StandardEnum::Blue)); count ++; } }); @@ -419,13 +645,13 @@ void Enum_query_enum_constant(void) { auto e1 = ecs.entity().add(StandardEnum::Blue); auto q = ecs.query_builder() - .term(StandardEnum::Blue) + .with(StandardEnum::Blue) .build(); int32_t count = 0; q.each([&](flecs::iter& it, size_t index) { test_assert(it.entity(index) == e1); - test_assert(it.pair(1).second() == ecs.id(StandardEnum::Blue)); + test_assert(it.pair(0).second() == ecs.id(StandardEnum::Blue)); count ++; }); @@ -766,21 +992,36 @@ void Enum_query_union_enum(void) { flecs::entity e2 = ecs.entity().add(StandardEnum::Green); flecs::entity e3 = ecs.entity().add(StandardEnum::Blue); + auto q = ecs.query_builder() - .term().second(flecs::Wildcard) + .with().second(flecs::Wildcard) .build(); - q.iter([&](flecs::iter& it) { - flecs::column colors = it.field(1); - test_int(it.count(), 3); - test_uint(it.entity(0), e1); - test_uint(it.entity(1), e2); - test_uint(it.entity(2), e3); - test_uint(colors[0], ecs.to_entity(StandardEnum::Red)); - test_uint(colors[1], ecs.to_entity(StandardEnum::Green)); - test_uint(colors[2], ecs.to_entity(StandardEnum::Blue)); + // Entity order in the query is not guaranteed. Thus, + // we store the query result in a map and check afterwards. + ecs_map_t map; + ecs_map_init(&map, nullptr); + q.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + ecs_map_insert(&map, it.entity(0), it.id(0)); + } }); + + //Helper function to query the map: + auto get_from_map = [&map](const flecs::entity &e) -> flecs::id_t { + ecs_map_val_t* val = ecs_map_get(&map, e); + return val ? flecs::id_t(*val) : 0; + }; + + test_int(ecs_map_count(&map), 3); + + test_assert(get_from_map(e1) == ecs.pair(ecs.to_entity(StandardEnum::Red))); + test_assert(get_from_map(e2) == ecs.pair(ecs.to_entity(StandardEnum::Green))); + test_assert(get_from_map(e3) == ecs.pair(ecs.to_entity(StandardEnum::Blue))); + + ecs_map_fini(&map); } void Enum_query_union_enum_invalid_query_type(void) { @@ -793,7 +1034,7 @@ void Enum_query_union_enum_invalid_query_type(void) { test_expect_abort(); ecs.query_builder() - .term_at(1).second(flecs::Wildcard) + .term_at(0).second(flecs::Wildcard) .build(); } @@ -804,7 +1045,8 @@ void Enum_component_registered_as_enum(void) { test_assert(e.has()); - const flecs::MetaType *mt = e.get(); + const flecs::Type *mt = e.get(); + test_assert(mt != nullptr); test_assert(mt->kind == flecs::meta::EnumType); @@ -856,7 +1098,7 @@ void Enum_mixed_auto_manual_constants(void) { test_assert(e.has()); - const flecs::MetaType *mt = e.get(); + const flecs::Type *mt = e.get(); test_assert(mt != nullptr); test_assert(mt->kind == flecs::meta::EnumType); @@ -908,7 +1150,7 @@ void Enum_enum_class_mixed_auto_manual_constants(void) { test_assert(e.has()); - const flecs::MetaType *mt = e.get(); + const flecs::Type *mt = e.get(); test_assert(mt != nullptr); test_assert(mt->kind == flecs::meta::EnumType); @@ -924,8 +1166,6 @@ void Enum_enum_class_mixed_auto_manual_constants(void) { test_assert(vi != nullptr); test_assert(*vi == static_cast(EnumClassWithLargeConstant::X)); } - - } void Enum_enum_child_count(void) { @@ -933,7 +1173,7 @@ void Enum_enum_child_count(void) { flecs::entity e = ecs.component(); - flecs::filter<> f = ecs.filter_builder() + flecs::query<> f = ecs.query_builder() .with(flecs::ChildOf, e) .build(); diff --git a/vendors/flecs/test/cpp_api/src/Event.cpp b/vendors/flecs/test/cpp/src/Event.cpp similarity index 71% rename from vendors/flecs/test/cpp_api/src/Event.cpp rename to vendors/flecs/test/cpp/src/Event.cpp index 920a83880..2cad9648a 100644 --- a/vendors/flecs/test/cpp_api/src/Event.cpp +++ b/vendors/flecs/test/cpp/src/Event.cpp @@ -1,4 +1,4 @@ -#include +#include struct Evt { }; @@ -16,7 +16,7 @@ void Event_evt_1_id_entity(void) { ecs.observer() .event(evt) - .term(id) + .with(id) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -42,7 +42,7 @@ void Event_evt_2_ids_entity(void) { ecs.observer() .event(evt) - .term(id_a) + .with(id_a) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -50,7 +50,7 @@ void Event_evt_2_ids_entity(void) { ecs.observer() .event(evt) - .term(id_b) + .with(id_b) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -78,7 +78,7 @@ void Event_evt_1_id_table(void) { ecs.observer() .event(evt) - .term(id) + .with(id) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -105,7 +105,7 @@ void Event_evt_2_ids_table(void) { ecs.observer() .event(evt) - .term(id_a) + .with(id_a) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -113,7 +113,7 @@ void Event_evt_2_ids_table(void) { ecs.observer() .event(evt) - .term(id_b) + .with(id_b) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -138,7 +138,7 @@ void Event_evt_type(void) { ecs.observer() .event() - .term(id) + .with(id) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -161,7 +161,7 @@ void Event_evt_1_component(void) { ecs.observer() .event() - .term() + .with() .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -184,7 +184,7 @@ void Event_evt_2_components(void) { ecs.observer() .event() - .term() + .with() .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -192,7 +192,7 @@ void Event_evt_2_components(void) { ecs.observer() .event() - .term() + .with() .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -222,11 +222,13 @@ void Event_evt_void_ctx(void) { ecs.observer() .event(evt) - .term(id) - .iter([&](flecs::iter& it) { - test_assert(it.entity(0) == e1); - test_int(it.param()->value, 10); - count ++; + .with(id) + .run([&](flecs::iter& it) { + while (it.next()) { + test_assert(it.entity(0) == e1); + test_int(it.param()->value, 10); + count ++; + } }); EvtData data = {10}; @@ -250,11 +252,13 @@ void Event_evt_typed_ctx(void) { ecs.observer() .event() - .term(id) - .iter([&](flecs::iter& it) { - test_assert(it.entity(0) == e1); - test_int(it.param()->value, 10); - count ++; + .with(id) + .run([&](flecs::iter& it) { + while (it.next()) { + test_assert(it.entity(0) == e1); + test_int(it.param()->value, 10); + count ++; + } }); ecs.event() @@ -276,11 +280,13 @@ void Event_evt_implicit_typed_ctx(void) { ecs.observer() .event() - .term(id) - .iter([&](flecs::iter& it) { - test_assert(it.entity(0) == e1); - test_int(it.param()->value, 10); - count ++; + .with(id) + .run([&](flecs::iter& it) { + while (it.next()) { + test_assert(it.entity(0) == e1); + test_int(it.param()->value, 10); + count ++; + } }); ecs.event() @@ -304,7 +310,7 @@ void Event_evt_1_id_pair_rel_id_obj_id_entity(void) { ecs.observer() .event(evt) - .term(rel, obj) + .with(rel, obj) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -329,7 +335,7 @@ void Event_evt_1_id_pair_rel_obj_id_entity(void) { ecs.observer() .event(evt) - .term(obj) + .with(obj) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -353,7 +359,7 @@ void Event_evt_1_id_pair_rel_obj_entity(void) { ecs.observer() .event(evt) - .term() + .with() .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -377,7 +383,7 @@ void Event_emit_staged_from_world(void) { ecs.observer() .event(evt) - .term() + .with() .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -405,7 +411,7 @@ void Event_emit_staged_from_stage(void) { ecs.observer() .event(evt) - .term() + .with() .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -625,6 +631,40 @@ void Event_entity_emit_event_w_payload_derived_event_type_no_src(void) { test_int(count, 1); } +// Generic lambdas are a C++14 feature. + +struct GenericLambdaObserveEntityPayload { + template + void operator()(E&&, P&&) const { + static_assert(flecs::is_same(), ""); + static_assert(flecs::is_same(), ""); + } +}; + +struct GenericLambdaObservePayload { + template + void operator()(P&&) const { + static_assert(flecs::is_same(), ""); + } +}; + +struct GenericLambdaObserveEntity { + template + void operator()(E&&) const { + static_assert(flecs::is_same(), ""); + } +}; + +void Event_entity_observe_generic(void) { + flecs::world ecs; + + flecs::entity e = ecs.entity(); + + e.observe(GenericLambdaObserveEntityPayload{}); + e.observe(GenericLambdaObservePayload{}); + e.observe(GenericLambdaObserveEntity{}); +} + void Event_enqueue_event(void) { flecs::world ecs; @@ -636,7 +676,7 @@ void Event_enqueue_event(void) { ecs.observer() .event(evt) - .term(id_a) + .with(id_a) .each([&](flecs::entity e) { test_assert(e == e1); count ++; @@ -690,7 +730,7 @@ void Event_enqueue_event_w_payload(void) { ecs.observer() .event() - .term(id_a) + .with(id_a) .each([&](flecs::iter& it, size_t i) { test_assert(it.entity(i) == e1); test_int(it.param()->x, 10); @@ -737,3 +777,152 @@ void Event_enqueue_entity_event_w_payload(void) { test_int(count, 1); } + +void Event_enqueue_entity_from_readonly_world(void) { + flecs::world ecs; + + int32_t count = 0; + + flecs::entity evt = ecs.entity(); + flecs::entity id_a = ecs.entity(); + flecs::entity e1 = ecs.entity().add(id_a); + + e1.observe(evt, [&]() { + count ++; + }); + + ecs.readonly_begin(); + + e1.enqueue(evt); + + test_int(count, 0); + + ecs.readonly_end(); + + test_int(count, 1); +} + +void Event_enqueue_entity_w_payload_from_readonly_world(void) { + flecs::world ecs; + + int32_t count = 0; + + flecs::entity id_a = ecs.entity(); + flecs::entity e1 = ecs.entity().add(id_a); + + e1.observe([&](Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + ecs.readonly_begin(); + + e1.enqueue({10, 20}); + + test_int(count, 0); + + ecs.readonly_end(); + + test_int(count, 1); +} + +enum Type { A, B = 42, C }; +void Event_enum_event(void) { + struct Event {}; + + + struct Data { + Type type; + int value; + }; + + + struct { + size_t _any = 0u; + size_t type_a = 0u; + size_t type_b = 0u; + size_t data = 0u; + size_t data_type_a = 0u; + size_t data_type_b = 0u; + } observed; + + flecs::world ecs; + + ecs.component(); + ecs.component(); + ecs.component(); + + ecs.observer() + .with(flecs::Wildcard) + .event() + .each([&](flecs::entity) { + ++observed._any; + }); + + ecs.observer() + .with(Type::A) + .event() + .each([&](flecs::entity) { + ++observed.type_a; + }); + + ecs.observer() + .with(Type::B) + .event() + .each([&](flecs::entity) { + ++observed.type_b; + }); + + ecs.observer() + .event() + .each([&](flecs::entity, Data data) { + ++observed.data; + test_assert(data.value >= 1 && data.value <= 2); + }); + + + ecs.observer() + .with(Type::A) + .event() + .each([&](flecs::entity, Data data) { + ++observed.data_type_a; + test_int(data.value, 1); + }); + + ecs.observer() + .with(Type::B) + .event() + .each([&](flecs::entity, Data data) { + ++observed.data_type_b; + test_int(data.value, 2); + }); + + { + auto event1 = ecs.entity() + .add(Type::A) + .emplace(Type::A, 1); + + ecs.event() + .id(Type::A) + .id() + .entity(event1).emit(); + } + + { + auto event2 = ecs.entity() + .add(Type::B) + .emplace(Type::B, 2); + + ecs.event() + .id(Type::B) + .id() + .entity(event2).emit(); + } + + test_int(observed._any, 2u); + test_int(observed.type_a, 1u); + test_int(observed.type_b, 1u); + test_int(observed.data_type_a, 1u); + test_int(observed.data_type_b, 1u); +} diff --git a/vendors/flecs/test/cpp_api/src/ImplicitComponents.cpp b/vendors/flecs/test/cpp/src/ImplicitComponents.cpp similarity index 93% rename from vendors/flecs/test/cpp_api/src/ImplicitComponents.cpp rename to vendors/flecs/test/cpp/src/ImplicitComponents.cpp index 8c77dd450..2abd95f3a 100644 --- a/vendors/flecs/test/cpp_api/src/ImplicitComponents.cpp +++ b/vendors/flecs/test/cpp/src/ImplicitComponents.cpp @@ -1,4 +1,4 @@ -#include +#include struct Pair { int value; @@ -227,7 +227,7 @@ void ImplicitComponents_reinit(void) { // Reset component id using internals (currently the only way to simulate // registration across translation units) - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); world.entity() .add(); @@ -251,7 +251,7 @@ void ImplicitComponents_reinit_scoped(void) { // Reset component id using internals (currently the only way to simulate // registration across translation units) - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); world.entity() .add(); @@ -284,7 +284,7 @@ void ImplicitComponents_reinit_w_lifecycle(void) { // Reset component id using internals (currently the only way to simulate // registration across translation units) - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); e = world.entity() .add(); @@ -423,7 +423,6 @@ void ImplicitComponents_implicit_base(void) { test_int(v.id(), flecs::type_id()); test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); test_int(v.id(), flecs::type_id()); } @@ -434,7 +433,6 @@ void ImplicitComponents_implicit_const(void) { test_int(v.id(), flecs::type_id()); test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); test_int(v.id(), flecs::type_id()); } @@ -445,18 +443,6 @@ void ImplicitComponents_implicit_ref(void) { test_int(v.id(), flecs::type_id()); test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); -} - -void ImplicitComponents_implicit_ptr(void) { - flecs::world world; - - auto v = world.use(); - - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); test_int(v.id(), flecs::type_id()); } @@ -467,7 +453,6 @@ void ImplicitComponents_implicit_const_ref(void) { test_int(v.id(), flecs::type_id()); test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); test_int(v.id(), flecs::type_id()); } diff --git a/vendors/flecs/test/cpp_api/src/Iterable.cpp b/vendors/flecs/test/cpp/src/Iterable.cpp similarity index 61% rename from vendors/flecs/test/cpp_api/src/Iterable.cpp rename to vendors/flecs/test/cpp/src/Iterable.cpp index b1d98c0f1..162ceab7d 100644 --- a/vendors/flecs/test/cpp_api/src/Iterable.cpp +++ b/vendors/flecs/test/cpp/src/Iterable.cpp @@ -1,4 +1,4 @@ -#include +#include void Iterable_page_each(void) { flecs::world ecs; @@ -34,15 +34,18 @@ void Iterable_page_iter(void) { auto q = ecs.query(); int32_t count = 0; - q.page(1, 3).iter([&](flecs::iter it, Self* self) { - test_int(it.count(), 3); - test_assert(it.entity(0) == e2); - test_assert(it.entity(1) == e3); - test_assert(it.entity(2) == e4); - test_assert(it.entity(0) == self[0].value); - test_assert(it.entity(1) == self[1].value); - test_assert(it.entity(2) == self[2].value); - count += it.count(); + q.page(1, 3).run([&](flecs::iter it) { + while (it.next()) { + auto self = it.field(0); + test_int(it.count(), 3); + test_assert(it.entity(0) == e2); + test_assert(it.entity(1) == e3); + test_assert(it.entity(2) == e4); + test_assert(it.entity(0) == self[0].value); + test_assert(it.entity(1) == self[1].value); + test_assert(it.entity(2) == self[2].value); + count += it.count(); + } }); test_int(count, 3); @@ -93,27 +96,33 @@ void Iterable_worker_iter(void) { auto q = ecs.query(); int32_t count = 0; - q.worker(0, 2).iter([&](flecs::iter it, Self* self) { - test_int(it.count(), 3); - test_assert(it.entity(0) == e1); - test_assert(it.entity(1) == e2); - test_assert(it.entity(2) == e3); - test_assert(it.entity(0) == self[0].value); - test_assert(it.entity(1) == self[1].value); - test_assert(it.entity(2) == self[2].value); - count += it.count(); + q.worker(0, 2).run([&](flecs::iter it) { + while (it.next()) { + auto self = it.field(0); + test_int(it.count(), 3); + test_assert(it.entity(0) == e1); + test_assert(it.entity(1) == e2); + test_assert(it.entity(2) == e3); + test_assert(it.entity(0) == self[0].value); + test_assert(it.entity(1) == self[1].value); + test_assert(it.entity(2) == self[2].value); + count += it.count(); + } }); test_int(count, 3); count = 0; - q.worker(1, 2).iter([&](flecs::iter it, Self* self) { - test_int(it.count(), 2); - test_assert(it.entity(0) == e4); - test_assert(it.entity(1) == e5); - test_assert(it.entity(0) == self[0].value); - test_assert(it.entity(1) == self[1].value); - count += it.count(); + q.worker(1, 2).run([&](flecs::iter it) { + while (it.next()) { + auto self = it.field(0); + test_int(it.count(), 2); + test_assert(it.entity(0) == e4); + test_assert(it.entity(1) == e5); + test_assert(it.entity(0) == self[0].value); + test_assert(it.entity(1) == self[1].value); + count += it.count(); + } }); test_int(count, 2); diff --git a/vendors/flecs/test/cpp_api/src/Meta.cpp b/vendors/flecs/test/cpp/src/Meta.cpp similarity index 94% rename from vendors/flecs/test/cpp_api/src/Meta.cpp rename to vendors/flecs/test/cpp/src/Meta.cpp index 7c22d392d..5b280b89f 100644 --- a/vendors/flecs/test/cpp_api/src/Meta.cpp +++ b/vendors/flecs/test/cpp/src/Meta.cpp @@ -1,4 +1,4 @@ -#include +#include flecs::opaque std_string_support(flecs::world&) { flecs::opaque ts; @@ -337,11 +337,11 @@ void Meta_bitmask(void) { ecs.component() .member("toppings"); - // Create entity with Position as usual + // Create entity with Sandwich as usual auto e = ecs.entity() .set({Toppings::Bacon | Toppings::Lettuce}); - // Convert position component to flecs expression string + // Convert Sandwidth component to flecs expression string const Sandwich *ptr = e.get(); test_str(ecs.to_expr(ptr).c_str(), "{toppings: Lettuce|Bacon}"); } @@ -604,7 +604,7 @@ void Meta_primitive_type(void) { test_assert(t != 0); test_assert(t.has()); - test_assert(t.has()); + test_assert(t.has()); test_assert(t.has()); const flecs::Component *c = t.get(); @@ -612,7 +612,7 @@ void Meta_primitive_type(void) { test_int(c->size, 4); test_int(c->alignment, 4); - const flecs::MetaType *mt = t.get(); + const flecs::Type *mt = t.get(); test_assert(mt != nullptr); test_assert(mt->kind == flecs::meta::PrimitiveType); @@ -628,7 +628,7 @@ void Meta_array_type(void) { test_assert(t != 0); test_assert(t.has()); - test_assert(t.has()); + test_assert(t.has()); test_assert(t.has()); const flecs::Component *c = t.get(); @@ -636,7 +636,7 @@ void Meta_array_type(void) { test_int(c->size, 3 * 4); test_int(c->alignment, 4); - const flecs::MetaType *mt = t.get(); + const flecs::Type *mt = t.get(); test_assert(mt != nullptr); test_assert(mt->kind == flecs::meta::ArrayType); @@ -653,7 +653,7 @@ void Meta_vector_type(void) { test_assert(t != 0); test_assert(t.has()); - test_assert(t.has()); + test_assert(t.has()); test_assert(t.has()); const flecs::Component *c = t.get(); @@ -661,7 +661,7 @@ void Meta_vector_type(void) { test_int(c->size, ECS_SIZEOF(ecs_vec_t)); test_int(c->alignment, ECS_SIZEOF(void*)); - const flecs::MetaType *mt = t.get(); + const flecs::Type *mt = t.get(); test_assert(mt != nullptr); test_assert(mt->kind == flecs::meta::VectorType); @@ -730,7 +730,7 @@ void Meta_entity_from_json_w_path(void) { flecs::entity e = ecs.entity(); - const char *r = e.from_json("{\"path\":\"ent\"}"); + const char *r = e.from_json("{\"name\":\"ent\"}"); test_str(r, ""); test_assert(e != 0); @@ -746,7 +746,7 @@ void Meta_entity_from_json_w_ids(void) { flecs::entity e = ecs.entity(); - const char *r = e.from_json("{\"path\":\"ent\", \"ids\":[[\"Position\"]]}"); + const char *r = e.from_json("{\"name\":\"ent\", \"tags\":[\"Position\"]}"); test_str(r, ""); test_assert(e != 0); @@ -763,7 +763,7 @@ void Meta_entity_from_json_w_values(void) { flecs::entity e = ecs.entity(); - const char *r = e.from_json("{\"path\":\"ent\", \"ids\":[[\"Position\"]], \"values\":[{\"x\":10, \"y\":20}]}"); + const char *r = e.from_json("{\"name\":\"ent\", \"components\":{\"Position\": {\"x\":10, \"y\":20}}}"); test_str(r, ""); test_assert(e != 0); @@ -1041,7 +1041,7 @@ void Meta_new_world_ser_deser_empty_flecs_entity(void) { { const CppEntity *ptr = e1.get(); test_assert(ptr != nullptr); - test_str(world.to_json(ptr).c_str(), "{\"entity\":0}"); + test_str(world.to_json(ptr).c_str(), "{\"entity\":\"#0\"}"); } auto json = world.to_json(); @@ -1061,7 +1061,7 @@ void Meta_new_world_ser_deser_empty_flecs_entity(void) { { const CppEntity *ptr = e1.get(); test_assert(ptr != nullptr); - test_str(world2.to_json(ptr).c_str(), "{\"entity\":0}"); + test_str(world2.to_json(ptr).c_str(), "{\"entity\":\"#0\"}"); } } @@ -1369,3 +1369,41 @@ void Meta_component_as_array(void) { test_assert(ptr->type == ecs.id()); test_int(ptr->count, 2); } + +void Meta_out_of_order_member_declaration(void) { + flecs::world ecs; + + auto c = ecs.component() + .member("y", 1, offsetof(Position, y)) + .member("x", 1, offsetof(Position, x)); + test_assert(c != 0); + + const flecs::Component *ptr = c.get(); + test_int(ptr->size, 8); + test_int(ptr->alignment, 4); + + auto xe = c.lookup("x"); + test_assert(xe != 0); + test_assert( xe.has() ); + const flecs::Member *x = xe.get(); + test_uint(x->type, flecs::F32); + test_uint(x->offset, 0); + + auto ye = c.lookup("y"); + test_assert(ye != 0); + test_assert( ye.has() ); + const flecs::Member *y = ye.get(); + test_uint(y->type, flecs::F32); + test_uint(y->offset, 4); + + flecs::entity e2 = ecs.entity("ent2").set({10, 20}); + const Position *p = e2.get(); + + auto json = ecs.to_json(p); + test_str(json.c_str(), "{\"y\":20, \"x\":10}"); + + Position p2 = {}; + ecs.from_json(&p2, json.c_str()); + test_int(p2.x, 10); + test_int(p2.y, 20); +} diff --git a/vendors/flecs/test/cpp_api/src/Misc.cpp b/vendors/flecs/test/cpp/src/Misc.cpp similarity index 71% rename from vendors/flecs/test/cpp_api/src/Misc.cpp rename to vendors/flecs/test/cpp/src/Misc.cpp index 7a66e891b..85ecd7823 100644 --- a/vendors/flecs/test/cpp_api/src/Misc.cpp +++ b/vendors/flecs/test/cpp/src/Misc.cpp @@ -1,4 +1,4 @@ -#include +#include enum Color { Red, Green, Blue @@ -122,8 +122,8 @@ static int obs_invoked_count = 0; static int trig_invoked_count = 0; void Sys(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); test_assert(p != NULL); test_assert(v != NULL); @@ -132,8 +132,8 @@ void Sys(ecs_iter_t *it) { } void Obs(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); test_assert(p != NULL); test_assert(v != NULL); @@ -142,7 +142,7 @@ void Obs(ecs_iter_t *it) { } void Trig(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); test_assert(p != NULL); @@ -209,9 +209,11 @@ void Misc_app_run(void) { flecs::world ecs; bool system_ran = false; - ecs.system().iter([&](flecs::iter& it) { - system_ran = true; - it.world().quit(); + ecs.system().run([&](flecs::iter& it) { + while (it.next()) { + system_ran = true; + it.world().quit(); + } }); test_int(ecs.app().run(), 0); @@ -222,10 +224,12 @@ void Misc_app_run_target_fps(void) { flecs::world ecs; int32_t count = 0; - ecs.system().iter([&](flecs::iter& it) { - count ++; - test_int(it.world().get_info()->target_fps, 10); - it.world().quit(); + ecs.system().run([&](flecs::iter& it) { + while (it.next()) { + count ++; + test_int(it.world().get_info()->target_fps, 10); + it.world().quit(); + } }); ecs.set_target_fps(10); @@ -239,8 +243,10 @@ void Misc_app_run_set_frames(void) { flecs::world ecs; int32_t count = 0; - ecs.system().iter([&](flecs::iter& it) { - count ++; + ecs.system().run([&](flecs::iter& it) { + while (it.next()) { + count ++; + } }); ecs.app().frames(100).run(); @@ -286,19 +292,23 @@ void Misc_member_gauge_metric(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 20); - test_int(i[1].value, 30); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 20); + test_int(i[1].value, 30); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -319,36 +329,44 @@ void Misc_id_gauge_metric(void) { ecs.progress(1.0); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 1); - test_int(i[1].value, 1); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 1); + test_int(i[1].value, 1); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); ecs.progress(1.0); test_int(count, 2); count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 1); - test_int(i[1].value, 1); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 1); + test_int(i[1].value, 1); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -375,47 +393,53 @@ void Misc_oneof_gauge_metric(void) { ecs.progress(1.0); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s) { - ColorMetric *i = static_cast(it.range().get(m, ecs.id())); - test_assert(i != nullptr); - - count += it.count(); - test_int(count, 3); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - test_uint(s[2].entity, e3); - - test_str(ecs.to_json(m, &i[0]), "{\"blue\":0, \"green\":0, \"red\":1}"); - test_str(ecs.to_json(m, &i[1]), "{\"blue\":0, \"green\":1, \"red\":0}"); - test_str(ecs.to_json(m, &i[2]), "{\"blue\":1, \"green\":0, \"red\":0}"); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); - test_assert(it.entity(2).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + ColorMetric *i = static_cast(it.range().get(m, ecs.id())); + test_assert(i != nullptr); + + count += it.count(); + test_int(count, 3); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + test_uint(s[2].entity, e3); + + test_str(ecs.to_json(m, &i[0]), "{\"red\":1, \"green\":0, \"blue\":0}"); + test_str(ecs.to_json(m, &i[1]), "{\"red\":0, \"green\":1, \"blue\":0}"); + test_str(ecs.to_json(m, &i[2]), "{\"red\":0, \"green\":0, \"blue\":1}"); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + test_assert(it.entity(2).has()); + } }); test_int(count, 3); count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s) { - ColorMetric *i = static_cast(it.range().get(m, ecs.id())); - test_assert(i != nullptr); - - count += it.count(); - test_int(count, 3); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - test_uint(s[2].entity, e3); - - test_str(ecs.to_json(m, &i[0]), "{\"blue\":0, \"green\":0, \"red\":1}"); - test_str(ecs.to_json(m, &i[1]), "{\"blue\":0, \"green\":1, \"red\":0}"); - test_str(ecs.to_json(m, &i[2]), "{\"blue\":1, \"green\":0, \"red\":0}"); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); - test_assert(it.entity(2).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + ColorMetric *i = static_cast(it.range().get(m, ecs.id())); + test_assert(i != nullptr); + + count += it.count(); + test_int(count, 3); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + test_uint(s[2].entity, e3); + + test_str(ecs.to_json(m, &i[0]), "{\"red\":1, \"green\":0, \"blue\":0}"); + test_str(ecs.to_json(m, &i[1]), "{\"red\":0, \"green\":1, \"blue\":0}"); + test_str(ecs.to_json(m, &i[2]), "{\"red\":0, \"green\":0, \"blue\":1}"); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + test_assert(it.entity(2).has()); + } }); test_int(count, 3); @@ -436,36 +460,44 @@ void Misc_id_counter_metric(void) { ecs.progress(1.0); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 1); - test_int(i[1].value, 1); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 1); + test_int(i[1].value, 1); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); ecs.progress(1.0); test_int(count, 2); count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 2); - test_int(i[1].value, 2); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 2); + test_int(i[1].value, 2); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -492,48 +524,54 @@ void Misc_oneof_counter_metric(void) { ecs.progress(1.0); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s) { - ColorMetric *i = static_cast(it.range().get(m, ecs.id())); - test_assert(i != nullptr); - - count += it.count(); - test_int(count, 3); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - test_uint(s[2].entity, e3); - - test_str(ecs.to_json(m, &i[0]), "{\"blue\":0, \"green\":0, \"red\":1}"); - test_str(ecs.to_json(m, &i[1]), "{\"blue\":0, \"green\":1, \"red\":0}"); - test_str(ecs.to_json(m, &i[2]), "{\"blue\":1, \"green\":0, \"red\":0}"); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); - test_assert(it.entity(2).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + ColorMetric *i = static_cast(it.range().get(m, ecs.id())); + test_assert(i != nullptr); + + count += it.count(); + test_int(count, 3); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + test_uint(s[2].entity, e3); + + test_str(ecs.to_json(m, &i[0]), "{\"red\":1, \"green\":0, \"blue\":0}"); + test_str(ecs.to_json(m, &i[1]), "{\"red\":0, \"green\":1, \"blue\":0}"); + test_str(ecs.to_json(m, &i[2]), "{\"red\":0, \"green\":0, \"blue\":1}"); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + test_assert(it.entity(2).has()); + } }); test_int(count, 3); ecs.progress(1.0); count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s) { - ColorMetric *i = static_cast(it.range().get(m, ecs.id())); - test_assert(i != nullptr); - - count += it.count(); - test_int(count, 3); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - test_uint(s[2].entity, e3); - - test_str(ecs.to_json(m, &i[0]), "{\"blue\":0, \"green\":0, \"red\":2}"); - test_str(ecs.to_json(m, &i[1]), "{\"blue\":0, \"green\":2, \"red\":0}"); - test_str(ecs.to_json(m, &i[2]), "{\"blue\":2, \"green\":0, \"red\":0}"); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); - test_assert(it.entity(2).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + ColorMetric *i = static_cast(it.range().get(m, ecs.id())); + test_assert(i != nullptr); + + count += it.count(); + test_int(count, 3); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + test_uint(s[2].entity, e3); + + test_str(ecs.to_json(m, &i[0]), "{\"red\":2, \"green\":0, \"blue\":0}"); + test_str(ecs.to_json(m, &i[1]), "{\"red\":0, \"green\":2, \"blue\":0}"); + test_str(ecs.to_json(m, &i[2]), "{\"red\":0, \"green\":0, \"blue\":2}"); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + test_assert(it.entity(2).has()); + } }); test_int(count, 3); @@ -554,19 +592,23 @@ void Misc_component_mixin_member_metric(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 20); - test_int(i[1].value, 30); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 20); + test_int(i[1].value, 30); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -591,22 +633,26 @@ void Misc_component_mixin_member_metric_custom_parent_entity(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 20); - test_int(i[1].value, 30); - - test_assert(it.entity(0).parent().parent() == parent); - test_assert(it.entity(1).parent().parent() == parent); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 20); + test_int(i[1].value, 30); + + test_assert(it.entity(0).parent().parent() == parent); + test_assert(it.entity(1).parent().parent() == parent); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -634,19 +680,23 @@ void Misc_metric_description(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 20); - test_int(i[1].value, 30); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 20); + test_int(i[1].value, 30); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -670,19 +720,23 @@ void Misc_component_mixin_member_metric_description(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 20); - test_int(i[1].value, 30); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 20); + test_int(i[1].value, 30); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -707,22 +761,26 @@ void Misc_member_metric_w_value_name(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 10); - test_int(i[1].value, 20); - - test_assert(it.entity(0).parent().parent() == parent); - test_assert(it.entity(1).parent().parent() == parent); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 10); + test_int(i[1].value, 20); + + test_assert(it.entity(0).parent().parent() == parent); + test_assert(it.entity(1).parent().parent() == parent); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -751,22 +809,26 @@ void Misc_member_metric_w_value_name_camel_case_type(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 10); - test_int(i[1].value, 20); - - test_assert(it.entity(0).parent().parent() == parent); - test_assert(it.entity(1).parent().parent() == parent); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 10); + test_int(i[1].value, 20); + + test_assert(it.entity(0).parent().parent() == parent); + test_assert(it.entity(1).parent().parent() == parent); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -791,22 +853,26 @@ void Misc_member_metric_w_custom_name(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 10); - test_int(i[1].value, 20); - - test_assert(it.entity(0).parent().parent() == parent); - test_assert(it.entity(1).parent().parent() == parent); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 10); + test_int(i[1].value, 20); + + test_assert(it.entity(0).parent().parent() == parent); + test_assert(it.entity(1).parent().parent() == parent); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -844,16 +910,20 @@ void Misc_dotmember_metric(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); - test_int(count, 1); - test_uint(s[0].entity, e1); + test_int(count, 1); + test_uint(s[0].entity, e1); - test_int(i[0].value, 30); + test_int(i[0].value, 30); - test_assert(it.entity(0).has()); + test_assert(it.entity(0).has()); + } }); test_int(count, 1); @@ -957,7 +1027,7 @@ void Misc_alert(void) { test_int(e2.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -973,7 +1043,7 @@ void Misc_alert(void) { ecs.progress(1.0); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1006,7 +1076,7 @@ void Misc_alert_w_message(void) { test_int(e2.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1024,7 +1094,7 @@ void Misc_alert_w_message(void) { ecs.progress(1.0); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1147,7 +1217,7 @@ void Misc_alert_w_retain_period(void) { flecs::entity ai; { - ai = ecs.filter_builder() + ai = ecs.query_builder() .with() .build() .first(); @@ -1198,7 +1268,7 @@ void Misc_alert_w_severity_filter(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1219,7 +1289,7 @@ void Misc_alert_w_severity_filter(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1240,7 +1310,7 @@ void Misc_alert_w_severity_filter(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1275,7 +1345,7 @@ void Misc_alert_w_severity_filter_severity_type(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1296,7 +1366,7 @@ void Misc_alert_w_severity_filter_severity_type(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1317,7 +1387,7 @@ void Misc_alert_w_severity_filter_severity_type(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1352,7 +1422,7 @@ void Misc_alert_w_severity_filter_severity_type_id_type(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1373,7 +1443,7 @@ void Misc_alert_w_severity_filter_severity_type_id_type(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1394,7 +1464,7 @@ void Misc_alert_w_severity_filter_severity_type_id_type(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1429,7 +1499,7 @@ void Misc_alert_w_severity_filter_severity_type_enum_constant(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1450,7 +1520,7 @@ void Misc_alert_w_severity_filter_severity_type_enum_constant(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1471,7 +1541,7 @@ void Misc_alert_w_severity_filter_severity_type_enum_constant(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1492,7 +1562,7 @@ void Misc_alert_w_severity_filter_severity_type_enum_constant(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1529,7 +1599,7 @@ void Misc_alert_w_severity_filter_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1550,7 +1620,7 @@ void Misc_alert_w_severity_filter_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1571,7 +1641,7 @@ void Misc_alert_w_severity_filter_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1608,7 +1678,7 @@ void Misc_alert_w_severity_filter_severity_type_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1629,7 +1699,7 @@ void Misc_alert_w_severity_filter_severity_type_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1650,7 +1720,7 @@ void Misc_alert_w_severity_filter_severity_type_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1687,7 +1757,7 @@ void Misc_alert_w_severity_filter_severity_type_id_type_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1708,7 +1778,7 @@ void Misc_alert_w_severity_filter_severity_type_id_type_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1729,7 +1799,7 @@ void Misc_alert_w_severity_filter_severity_type_id_type_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1766,7 +1836,7 @@ void Misc_alert_w_severity_filter_severity_type_enum_constant_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1787,7 +1857,7 @@ void Misc_alert_w_severity_filter_severity_type_enum_constant_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1808,7 +1878,7 @@ void Misc_alert_w_severity_filter_severity_type_enum_constant_w_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1853,7 +1923,7 @@ void Misc_alert_for_member_range(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1871,7 +1941,7 @@ void Misc_alert_for_member_range(void) { ecs.progress(1.0); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1913,7 +1983,7 @@ void Misc_alert_w_member_range_from_var(void) { test_int(e1.alert_count(a), 1); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1929,7 +1999,7 @@ void Misc_alert_w_member_range_from_var(void) { ecs.progress(1.0); { - flecs::entity ai = ecs.filter_builder() + flecs::entity ai = ecs.query_builder() .with() .build() .first(); @@ -1994,22 +2064,26 @@ void Misc_member_metric_w_pair_R_T(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 10); - test_int(i[1].value, 20); - - test_assert(it.entity(0).parent() == m); - test_assert(it.entity(1).parent() == m); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 10); + test_int(i[1].value, 20); + + test_assert(it.entity(0).parent() == m); + test_assert(it.entity(1).parent() == m); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -2034,22 +2108,26 @@ void Misc_member_metric_w_pair_R_t(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 10); - test_int(i[1].value, 20); - - test_assert(it.entity(0).parent() == m); - test_assert(it.entity(1).parent() == m); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 10); + test_int(i[1].value, 20); + + test_assert(it.entity(0).parent() == m); + test_assert(it.entity(1).parent() == m); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -2074,22 +2152,26 @@ void Misc_member_metric_w_pair_r_t(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 10); - test_int(i[1].value, 20); - - test_assert(it.entity(0).parent() == m); - test_assert(it.entity(1).parent() == m); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 10); + test_int(i[1].value, 20); + + test_assert(it.entity(0).parent() == m); + test_assert(it.entity(1).parent() == m); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); @@ -2114,22 +2196,26 @@ void Misc_member_metric_w_pair_r_T(void) { ecs.progress(); int32_t count = 0; - ecs.filter() - .iter([&](flecs::iter& it, flecs::metrics::Source *s, flecs::metrics::Value *i) { - count += it.count(); - - test_int(count, 2); - test_uint(s[0].entity, e1); - test_uint(s[1].entity, e2); - - test_int(i[0].value, 10); - test_int(i[1].value, 20); - - test_assert(it.entity(0).parent() == m); - test_assert(it.entity(1).parent() == m); - - test_assert(it.entity(0).has()); - test_assert(it.entity(1).has()); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto i = it.field(1); + count += it.count(); + + test_int(count, 2); + test_uint(s[0].entity, e1); + test_uint(s[1].entity, e2); + + test_int(i[0].value, 10); + test_int(i[1].value, 20); + + test_assert(it.entity(0).parent() == m); + test_assert(it.entity(1).parent() == m); + + test_assert(it.entity(0).has()); + test_assert(it.entity(1).has()); + } }); test_int(count, 2); diff --git a/vendors/flecs/test/cpp/src/Module.cpp b/vendors/flecs/test/cpp/src/Module.cpp new file mode 100644 index 000000000..de40ed262 --- /dev/null +++ b/vendors/flecs/test/cpp/src/Module.cpp @@ -0,0 +1,508 @@ +#include + +namespace ns { +struct NestedNameSpaceType { }; + +class NestedModule { +public: + NestedModule(flecs::world& world) { + world.module(); + flecs::component(world, "Velocity"); + } +}; + +class SimpleModule { +public: + SimpleModule(flecs::world& world) { + world.module(); + world.import(); + flecs::component(world, "Position"); + } +}; + +class NestedTypeModule { +public: + struct NestedType { }; + + NestedTypeModule(flecs::world& world) { + world.module(); + world.component(); + world.component(); + } +}; + +class NamedModule { +public: + NamedModule(flecs::world& world) { + world.module("::my_scope::NamedModule"); + flecs::component(world, "Position"); + } +}; + +class ImplicitModule { +public: + ImplicitModule(flecs::world& world) { + world.component(); + } +}; + +class NamedModuleInRoot { +public: + NamedModuleInRoot(flecs::world& world) { + world.module("::NamedModuleInRoot"); + world.component(); + } +}; + +class ReparentModule { +public: + ReparentModule(flecs::world& world) { + flecs::entity m = world.module(); + m.child_of(world.entity("::parent")); + + flecs::entity other = world.entity("::ns::ReparentModule"); + test_assert(other != 0); + test_assert(other != m); + } +}; + +} + +class ReparentRootModule { +public: + ReparentRootModule(flecs::world& world) { + world.module("ns::ReparentRootModule"); + } +}; + +namespace ns_parent { + struct NsType { + float x; + }; + + struct ShorterParent { + ShorterParent(flecs::world& world) { + world.module("ns::ShorterParent"); + world.component(); + } + }; + + struct LongerParent { + LongerParent(flecs::world& world) { + world.module("ns_parent_namespace::LongerParent"); + world.component(); + } + }; + + namespace ns_child { + struct Nested { + Nested(flecs::world& world) { + world.module("ns::child::Nested"); + world.component(); + } + }; + } +} + +struct Module { + Module(flecs::world& world) { + world.module(); + world.component(); + } +}; + +void Module_import(void) { + flecs::world world; + auto m = world.import(); + test_assert(m.id() != 0); + test_str(m.path().c_str(), "::ns::SimpleModule"); + test_assert(m.has(flecs::Module)); + + auto e = flecs::entity(world) + .add(); + test_assert(e.id() != 0); + test_assert(e.has()); +} + +void Module_lookup_from_scope(void) { + flecs::world world; + world.import(); + + auto ns_entity = world.lookup("ns"); + test_assert(ns_entity.id() != 0); + + auto module_entity = world.lookup("ns::SimpleModule"); + test_assert(module_entity.id() != 0); + + auto position_entity = world.lookup("ns::SimpleModule::Position"); + test_assert(position_entity.id() != 0); + + auto nested_module = ns_entity.lookup("SimpleModule"); + test_assert(module_entity.id() == nested_module.id()); + + auto module_position = module_entity.lookup("Position"); + test_assert(position_entity.id() == module_position.id()); + + auto ns_position = ns_entity.lookup("SimpleModule::Position"); + test_assert(position_entity.id() == ns_position.id()); +} + +void Module_nested_module(void) { + flecs::world world; + world.import(); + + auto velocity = world.lookup("ns::NestedModule::Velocity"); + test_assert(velocity.id() != 0); + + test_str(velocity.path().c_str(), "::ns::NestedModule::Velocity"); +} + +void Module_nested_type_module(void) { + flecs::world world; + world.import(); + + auto ns_entity = world.lookup("ns"); + test_assert(ns_entity.id() != 0); + + auto module_entity = world.lookup("ns::NestedTypeModule"); + test_assert(module_entity.id() != 0); + + auto type_entity = world.lookup("ns::NestedTypeModule::NestedType"); + test_assert(type_entity.id() != 0); + + auto ns_type_entity = world.lookup("ns::NestedTypeModule::NestedNameSpaceType"); + test_assert(ns_type_entity.id() != 0); + + int32_t childof_count = 0; + type_entity.each(flecs::ChildOf, [&](flecs::entity) { + childof_count ++; + }); + + test_int(childof_count, 1); + + childof_count = 0; + ns_type_entity.each(flecs::ChildOf, [&](flecs::entity) { + childof_count ++; + }); + + test_int(childof_count, 1); +} + +void Module_component_redefinition_outside_module(void) { + flecs::world world; + + world.import(); + + auto pos_comp = world.lookup("ns::SimpleModule::Position"); + test_assert(pos_comp.id() != 0); + + auto pos = world.component(); + test_assert(pos.id() != 0); + test_assert(pos.id() == pos_comp.id()); + + int32_t childof_count = 0; + pos_comp.each(flecs::ChildOf, [&](flecs::entity) { + childof_count ++; + }); + + test_int(childof_count, 1); +} + +void Module_module_tag_on_namespace(void) { + flecs::world world; + + auto mid = world.import(); + test_assert(mid.has(flecs::Module)); + + auto nsid = world.lookup("ns"); + test_assert(nsid.has(flecs::Module)); +} + +static int module_ctor_invoked = 0; +static int module_dtor_invoked = 0; + +class Module_w_dtor { +public: + Module_w_dtor(flecs::world& world) { + world.module(); + module_ctor_invoked ++; + + world.system<>().run([](flecs::iter& it) { }); + } + + ~Module_w_dtor() { + module_dtor_invoked ++; + } +}; + +void Module_dtor_on_fini(void) { + { + flecs::world ecs; + + test_int(module_ctor_invoked, 0); + test_int(module_dtor_invoked, 0); + + ecs.import(); + + test_int(module_ctor_invoked, 1); + test_int(module_dtor_invoked, 0); + } + + test_int(module_dtor_invoked, 1); +} + +void Module_register_w_root_name() { + flecs::world ecs; + + auto m = ecs.import(); + + auto m_lookup = ecs.lookup("::my_scope::NamedModule"); + test_assert(m != 0); + test_assert(m == m_lookup); + + test_assert(ecs.lookup("::ns::NamedModule") == 0); +} + +void Module_implicit_module(void) { + flecs::world ecs; + + auto m = ecs.import(); + auto m_lookup = ecs.lookup("::ns::ImplicitModule"); + test_assert(m != 0); + test_assert(m == m_lookup); + + auto p = ecs.component(); + auto p_lookup = ecs.lookup("::ns::ImplicitModule::Position"); + test_assert(p != 0); + test_assert(p == p_lookup); +} + +void Module_module_in_namespace_w_root_name(void) { + flecs::world ecs; + + auto m = ecs.import(); + auto m_lookup = ecs.lookup("::NamedModuleInRoot"); + test_assert(m != 0); + test_assert(m == m_lookup); + test_str(m.path(), "::NamedModuleInRoot"); + + auto p = ecs.component(); + auto p_lookup = ecs.lookup("::NamedModuleInRoot::Position"); + test_assert(p != 0); + test_assert(p == p_lookup); +} + +void Module_module_as_entity(void) { + flecs::world world; + + auto m = world.import(); + test_assert(m != 0); + + auto e = world.entity(); + test_assert(m == e); +} + +void Module_module_as_component(void) { + flecs::world world; + + auto m = world.import(); + test_assert(m != 0); + + auto e = world.component(); + test_assert(m == e); +} + +void Module_module_with_core_name(void) { + flecs::world world; + + flecs::entity m = world.import(); + test_assert(m != 0); + test_str(m.path().c_str(), "::Module"); + + flecs::entity pos = m.lookup("Position"); + test_assert(pos != 0); + test_str(pos.path().c_str(), "::Module::Position"); + test_assert(pos == world.id()); +} + +void Module_import_addons_two_worlds(void) { + flecs::world a; + auto m1 = a.import(); + auto u1 = a.import(); + + flecs::world b; + auto m2 = b.import(); + auto u2 = b.import(); + + test_assert(m1 == m2); + test_assert(u1 == u2); +} + +void Module_lookup_module_after_reparent(void) { + flecs::world world; + + flecs::entity m = world.import(); + test_str(m.path().c_str(), "::ns::NestedModule"); + test_assert(world.lookup("::ns::NestedModule") == m); + test_assert(ecs_lookup(world, "ns.NestedModule") == m); + + flecs::entity p = world.entity("p"); + m.child_of(p); + test_str(m.path().c_str(), "::p::NestedModule"); + test_assert(world.lookup("::p::NestedModule") == m); + test_assert(ecs_lookup(world, "p.NestedModule") == m); + + test_assert(world.lookup("::ns::NestedModule") == 0); + test_assert(ecs_lookup(world, "ns.NestedModule") == 0); + + flecs::entity e = world.entity("::ns::NestedModule"); + test_assert(e != m); + + // Tests if symbol resolving (used by query DSL) interferes with getting the + // correct object + test_int(world.query_builder() + .expr("(ChildOf, p.NestedModule)").build().count(), 1); + test_int(world.query_builder() + .expr("(ChildOf, ns.NestedModule)").build().count(), 0); +} + +void Module_reparent_module_in_ctor(void) { + flecs::world world; + + flecs::entity m = world.import(); + test_str(m.path().c_str(), "::parent::ReparentModule"); + + flecs::entity other = world.lookup("::ns::ReparentModule"); + test_assert(other != 0); + test_assert(other != m); +} +namespace NamespaceLvl1 { + namespace NamespaceLvl2 { + struct StructLvl1 { + struct StructLvl2_1 {}; + struct StructLvl2_2 {}; + }; + } +} +void Module_implicitly_add_module_to_scopes_component(void) { + flecs::world ecs; + + using StructLvl2_1 = NamespaceLvl1::NamespaceLvl2::StructLvl1::StructLvl2_1; + + flecs::entity current = ecs.component(); + test_assert(current.id() != 0); + test_assert(!current.has(flecs::Module)); + test_assert(current.has()); + test_assert(current.path() == "::NamespaceLvl1::NamespaceLvl2::StructLvl1::StructLvl2_1"); + + current = current.parent(); + test_assert(current.id() != 0); + test_assert(current.has(flecs::Module)); + test_assert(current.path() == "::NamespaceLvl1::NamespaceLvl2::StructLvl1"); + + current = current.parent(); + test_assert(current.id() != 0); + test_assert(current.has(flecs::Module)); + test_assert(current.path() == "::NamespaceLvl1::NamespaceLvl2"); + + current = current.parent(); + test_assert(current.id() != 0); + test_assert(current.has(flecs::Module)); + test_assert(current.path() == "::NamespaceLvl1"); + + current = current.parent(); + test_assert(current.id() == 0); +} + +void Module_implicitly_add_module_to_scopes_entity(void) { + flecs::world ecs; + + using StructLvl2_2 = NamespaceLvl1::NamespaceLvl2::StructLvl1::StructLvl2_2; + + flecs::entity current = ecs.entity().set({}); + test_assert(current.id() != 0); + test_assert(!current.has(flecs::Module)); + test_assert(current.has()); + test_assert(current.path() == "::NamespaceLvl1::NamespaceLvl2::StructLvl1::StructLvl2_2"); + + current = current.parent(); + test_assert(current.id() != 0); + test_assert(current.has(flecs::Module)); + test_assert(current.path() == "::NamespaceLvl1::NamespaceLvl2::StructLvl1"); + + current = current.parent(); + test_assert(current.id() != 0); + test_assert(current.has(flecs::Module)); + test_assert(current.path() == "::NamespaceLvl1::NamespaceLvl2"); + + current = current.parent(); + test_assert(current.id() != 0); + test_assert(current.has(flecs::Module)); + test_assert(current.path() == "::NamespaceLvl1"); + + current = current.parent(); + test_assert(current.id() == 0); +} + +void Module_rename_namespace_shorter(void) { + flecs::world ecs; + + auto m = ecs.import(); + test_assert(m.has(flecs::Module)); + test_str(m.path(), "::ns::ShorterParent"); + test_assert(ecs.lookup("::ns_parent") == 0); + test_assert(ecs.lookup("::ns_parent::ShorterParent") == 0); + test_assert(ecs.lookup("::ns_parent::ShorterParent::NsType") == 0); + test_assert(ecs.lookup("::ns::ShorterParent::NsType") != 0); + + auto ns = ecs.lookup("::ns"); + test_assert(ns != 0); + test_assert(ns.has(flecs::Module)); +} + +void Module_rename_namespace_longer(void) { + flecs::world ecs; + + auto m = ecs.import(); + test_assert(m.has(flecs::Module)); + test_str(m.path(), "::ns_parent_namespace::LongerParent"); + test_assert(ecs.lookup("::ns_parent") == 0); + test_assert(ecs.lookup("::ns_parent::LongerParent") == 0); + test_assert(ecs.lookup("::ns_parent::LongerParent::NsType") == 0); + test_assert(ecs.lookup("::ns_parent_namespace::LongerParent::NsType") != 0); + + auto ns = ecs.lookup("::ns_parent_namespace"); + test_assert(ns != 0); + test_assert(ns.has(flecs::Module)); +} + +void Module_rename_namespace_nested(void) { + flecs::world ecs; + + auto m = ecs.import(); + test_assert(m.has(flecs::Module)); + test_str(m.path(), "::ns::child::Nested"); + test_assert(ecs.lookup("::ns::child::Nested::NsType") != 0); + test_assert(ecs.lookup("::ns_parent::ns_child::Nested::NsType") == 0); + test_assert(ecs.lookup("::ns_parent::ns_child::Nested") == 0); + test_assert(ecs.lookup("::ns_parent::ns_child") == 0); + test_assert(ecs.lookup("::ns_parent") == 0); + + auto ns = ecs.lookup("::ns"); + test_assert(ns != 0); + test_assert(ns.has(flecs::Module)); + + auto ns_child = ecs.lookup("::ns::child"); + test_assert(ns_child != 0); + test_assert(ns_child.has(flecs::Module)); +} + +void Module_rename_reparent_root_module(void) { + flecs::world ecs; + + flecs::entity m = ecs.import(); + flecs::entity p = m.parent(); + test_assert(p != 0); + test_str(p.name(), "ns"); + test_str(m.name(), "ReparentRootModule"); +} diff --git a/vendors/flecs/test/cpp/src/Observer.cpp b/vendors/flecs/test/cpp/src/Observer.cpp new file mode 100644 index 000000000..02958d9c8 --- /dev/null +++ b/vendors/flecs/test/cpp/src/Observer.cpp @@ -0,0 +1,1476 @@ +#include + +void Observer_2_terms_on_add(void) { + flecs::world ecs; + + int32_t count = 0; + + ecs.observer() + .event(flecs::OnAdd) + .each([&](Position& p, Velocity& v) { + count ++; + }); + + auto e = ecs.entity(); + test_int(count, 0); + + e.set({10, 20}); + test_int(count, 0); + + e.set({1, 2}); + test_int(count, 1); +} + +void Observer_2_terms_on_remove(void) { + flecs::world ecs; + + int32_t count = 0; + + ecs.observer() + .event(flecs::OnRemove) + .each([&](Position& p, Velocity& v) { + count ++; + test_int(p.x, 10); + test_int(p.y, 20); + test_int(v.x, 1); + test_int(v.y, 2); + }); + + auto e = ecs.entity(); + test_int(count, 0); + + e.set({10, 20}); + test_int(count, 0); + + e.set({1, 2}); + test_int(count, 0); + + e.remove(); + test_int(count, 1); + + e.remove(); + test_int(count, 1); +} + +void Observer_2_terms_on_set(void) { + flecs::world ecs; + + int32_t count = 0; + + ecs.observer() + .event(flecs::OnSet) + .each([&](Position& p, Velocity& v) { + count ++; + test_int(p.x, 10); + test_int(p.y, 20); + test_int(v.x, 1); + test_int(v.y, 2); + }); + + auto e = ecs.entity(); + test_int(count, 0); + + e.set({10, 20}); + test_int(count, 0); + + e.set({1, 2}); + test_int(count, 1); +} + +void Observer_2_terms_un_set(void) { + flecs::world ecs; + + int32_t count = 0; + + ecs.observer() + .event(flecs::OnRemove) + .each([&](Position& p, Velocity& v) { + count ++; + test_int(p.x, 10); + test_int(p.y, 20); + test_int(v.x, 1); + test_int(v.y, 2); + }); + + auto e = ecs.entity(); + test_int(count, 0); + + e.set({10, 20}); + test_int(count, 0); + + e.set({1, 2}); + test_int(count, 0); + + e.remove(); + test_int(count, 1); + + e.remove(); + test_int(count, 1); +} + +void Observer_10_terms(void) { + flecs::world ecs; + + int count = 0; + + auto e = ecs.entity(); + + ecs.observer<>() + .event(flecs::OnAdd) + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .each([&](flecs::iter& it, size_t) { + test_int(it.count(), 1); + test_assert(it.entity(0) == e); + test_int(it.field_count(), 10); + count ++; + }); + + e.add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add(); + + test_int(count, 1); +} + +void Observer_16_terms(void) { + flecs::world ecs; + + int count = 0; + + auto e = ecs.entity(); + + ecs.observer<>() + .event(flecs::OnAdd) + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .each([&](flecs::iter& it, size_t) { + test_int(it.count(), 1); + test_assert(it.entity(0) == e); + test_int(it.field_count(), 16); + count ++; + }); + + e.add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add(); + + test_int(count, 1); +} + +void Observer_2_entities_iter(void) { + flecs::world ecs; + + auto e1 = ecs.entity(); + auto e2 = ecs.entity(); + + int32_t count = 0; + flecs::entity last; + + ecs.observer() + .event(flecs::OnSet) + .run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + + for (auto i : it) { + count ++; + if (it.entity(i) == e1) { + test_int(p[i].x, 10); + test_int(p[i].y, 20); + } else if (it.entity(i) == e2) { + test_int(p[i].x, 30); + test_int(p[i].y, 40); + } else { + test_assert(false); + } + + last = it.entity(i); + } + } + }); + + e1.set({ 10, 20 }); + test_int(count, 1); + test_assert(last == e1); + + e2.set({ 30, 40 }); + test_int(count, 2); + test_assert(last == e2); +} + +void Observer_2_entities_table_column(void) { + flecs::world ecs; + + auto e1 = ecs.entity(); + auto e2 = ecs.entity(); + + int32_t count = 0; + flecs::entity last; + + ecs.observer() + .event(flecs::OnSet) + .run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.range().get(); + + for (auto i : it) { + count ++; + if (it.entity(i) == e1) { + test_int(p[i].x, 10); + test_int(p[i].y, 20); + } else if (it.entity(i) == e2) { + test_int(p[i].x, 30); + test_int(p[i].y, 40); + } else { + test_assert(false); + } + + last = it.entity(i); + } + } + }); + + e1.set({ 10, 20 }); + test_int(count, 1); + test_assert(last == e1); + + e2.set({ 30, 40 }); + test_int(count, 2); + test_assert(last == e2); +} + +void Observer_2_entities_each(void) { + flecs::world ecs; + + auto e1 = ecs.entity(); + auto e2 = ecs.entity(); + + int32_t count = 0; + flecs::entity last; + + ecs.observer() + .event(flecs::OnSet) + .each([&](flecs::entity e, const Position& p) { + count ++; + if (e == e1) { + test_int(p.x, 10); + test_int(p.y, 20); + } else if (e == e2) { + test_int(p.x, 30); + test_int(p.y, 40); + } else { + test_assert(false); + } + + last = e; + }); + + e1.set({ 10, 20 }); + test_int(count, 1); + test_assert(last == e1); + + e2.set({ 30, 40 }); + test_int(count, 2); + test_assert(last == e2); +} + +void Observer_create_w_no_template_args(void) { + flecs::world ecs; + + auto e1 = ecs.entity(); + + int32_t count = 0; + + ecs.observer() + .with() + .event(flecs::OnAdd) + .each([&](flecs::entity e) { + test_assert(e == e1); + count ++; + }); + + e1.set({10, 20}); + test_int(count, 1); +} + +void Observer_yield_existing(void) { + flecs::world world; + + struct Tag0 { }; + struct Tag1 { }; + + auto e1 = world.entity().add(); + auto e2 = world.entity().add(); + auto e3 = world.entity().add().add(); + + int32_t count = 0; + + world.observer() + .event(flecs::OnAdd) + .yield_existing() + .each([&](flecs::entity e, Tag0) { + if (e == e1) count ++; + if (e == e2) count += 2; + if (e == e3) count += 3; + }); + + test_int(count, 6); +} + +void Observer_yield_existing_2_terms(void) { + flecs::world world; + + struct Tag0 { }; + struct Tag1 { }; + + auto e1 = world.entity().add().add(); + auto e2 = world.entity().add().add(); + auto e3 = world.entity().add().add().add(); + world.entity().add(); + world.entity().add(); + + int32_t count = 0; + + world.observer() + .event(flecs::OnAdd) + .yield_existing() + .each([&](flecs::entity e, Tag0, Tag1) { + if (e == e1) count ++; + if (e == e2) count += 2; + if (e == e3) count += 3; + }); + + test_int(count, 6); +} + + +void Observer_yield_existing_on_create_flag(void) { + flecs::world world; + + struct Tag0 { }; + struct Tag1 { }; + + auto e1 = world.entity().add(); + auto e2 = world.entity().add(); + auto e3 = world.entity().add().add(); + + int32_t count = 0; + + auto o = world.observer() + .event(flecs::OnAdd) + .event(flecs::OnRemove) + .observer_flags(EcsObserverYieldOnCreate) + .each([&](flecs::entity e, Tag0) { + if (e == e1) count ++; + if (e == e2) count += 2; + if (e == e3) count += 3; + }); + + test_int(count, 6); + + o.destruct(); + + test_int(count, 6); +} + +void Observer_yield_existing_on_delete_flag(void) { + flecs::world world; + + struct Tag0 { }; + struct Tag1 { }; + + auto e1 = world.entity().add(); + auto e2 = world.entity().add(); + auto e3 = world.entity().add().add(); + + int32_t count = 0; + + auto o = world.observer() + .event(flecs::OnAdd) + .event(flecs::OnRemove) + .observer_flags(EcsObserverYieldOnDelete) + .each([&](flecs::entity e, Tag0) { + if (e == e1) count ++; + if (e == e2) count += 2; + if (e == e3) count += 3; + }); + + test_int(count, 0); + + o.destruct(); + + test_int(count, 6); +} + +void Observer_yield_existing_on_create_delete_flag(void) { + flecs::world world; + + struct Tag0 { }; + struct Tag1 { }; + + auto e1 = world.entity().add(); + auto e2 = world.entity().add(); + auto e3 = world.entity().add().add(); + + int32_t count = 0; + + auto o = world.observer() + .event(flecs::OnAdd) + .event(flecs::OnRemove) + .observer_flags(EcsObserverYieldOnCreate|EcsObserverYieldOnDelete) + .each([&](flecs::entity e, Tag0) { + if (e == e1) count ++; + if (e == e2) count += 2; + if (e == e3) count += 3; + }); + + test_int(count, 6); + + o.destruct(); + + test_int(count, 12); +} + +void Observer_default_ctor(void) { + flecs::world world; + + struct Tag0 { }; + + flecs::observer o; + test_assert(o == 0); + + int32_t count = 0; + o = world.observer() + .event(flecs::OnAdd) + .each([&](flecs::entity e, Tag0) { + count ++; + }); + + world.entity().add(); + + test_int(count, 1); +} + +void Observer_entity_ctor(void) { + flecs::world world; + + struct Tag0 { }; + + flecs::observer o = world.observer() + .event(flecs::OnAdd) + .each([&](flecs::entity e, Tag0) { }); + + flecs::entity oe = o; + + flecs::observer eo = world.observer(oe); + test_assert(eo == o); +} + +void Observer_on_add(void) { + flecs::world world; + + int invoked = 0; + + world.observer() + .event(flecs::OnAdd) + .each([&](flecs::entity e, Position& p) { + invoked ++; + }); + + world.entity() + .add(); + + test_int(invoked, 1); +} + +void Observer_on_remove(void) { + flecs::world world; + + int invoked = 0; + + world.observer() + .event(flecs::OnRemove) + .each([&](flecs::entity e, Position& p) { + invoked ++; + }); + + auto e = world.entity() + .add(); + + test_int(invoked, 0); + + e.remove(); + + test_int(invoked, 1); +} + +struct MyTag { }; + +void Observer_on_add_tag_action(void) { + flecs::world world; + + int invoked = 0; + + world.observer() + .event(flecs::OnAdd) + .run([&](flecs::iter it) { + while (it.next()) { + invoked ++; + } + }); + + world.entity() + .add(); + + test_int(invoked, 1); +} + +void Observer_on_add_tag_iter(void) { + flecs::world world; + + int invoked = 0; + + world.observer() + .event(flecs::OnAdd) + .run([&](flecs::iter it) { + while (it.next()) { + invoked ++; + } + }); + + world.entity() + .add(); + + test_int(invoked, 1); +} + +void Observer_on_add_tag_each(void) { + flecs::world world; + + int invoked = 0; + + world.observer() + .event(flecs::OnAdd) + .each([&](flecs::entity e, MyTag) { + invoked ++; + }); + + world.entity() + .add(); + + test_int(invoked, 1); +} + +void Observer_on_add_expr(void) { + flecs::world world; + + int invoked = 0; + + world.component(); + + world.observer<>().expr("Tag") + .event(flecs::OnAdd) + .each([&](flecs::entity e) { + invoked ++; + }); + + auto e = world.entity().add(); + + test_int(invoked, 1); + + e.remove(); + + test_int(invoked, 1); +} + +void Observer_observer_w_filter_term(void) { + flecs::world world; + + flecs::entity Tag0 = world.entity(); + flecs::entity Tag1 = world.entity(); + + int invoked = 0; + + world.observer() + .with(Tag0) + .with(Tag1).filter() + .event(flecs::OnAdd) + .each([&](flecs::entity e) { + invoked ++; + }); + + flecs::entity e = world.entity(); + test_int(invoked, 0); + + e.add(Tag1); + test_int(invoked, 0); + + e.add(Tag0); + test_int(invoked, 1); + + e.remove(Tag1); + test_int(invoked, 1); + + e.add(Tag1); + test_int(invoked, 1); + + e.clear(); + test_int(invoked, 1); + + e.add(Tag0); + test_int(invoked, 1); +} + +void Observer_run_callback(void) { + flecs::world ecs; + + int32_t count = 0; + + ecs.observer() + .event(flecs::OnAdd) + .run([](flecs::iter& it) { + while (it.next()) { + it.each(); + } + }, + [&](Position& p) { + count ++; + }); + + auto e = ecs.entity(); + test_int(count, 0); + + e.set({10, 20}); + test_int(count, 1); +} + +void Observer_run_callback_w_1_field(void) { + flecs::world ecs; + + int32_t count = 0; + + ecs.observer() + .event(flecs::OnSet) + .run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + test_int(p->x, 10); + test_int(p->y, 20); + + count ++; + } + }); + + auto e = ecs.entity(); + test_int(count, 0); + + e.set({10, 20}); + test_int(count, 1); +} + +void Observer_run_callback_w_2_fields(void) { + flecs::world ecs; + + int32_t count = 0; + + ecs.observer() + .event(flecs::OnSet) + .run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + test_int(p->x, 10); + test_int(p->y, 20); + test_int(v->x, 1); + test_int(v->y, 2); + + count ++; + } + }); + + auto e = ecs.entity(); + test_int(count, 0); + + e.set({10, 20}); + test_int(count, 0); + + e.set({1, 2}); + test_int(count, 1); +} + +void Observer_run_callback_w_yield_existing_1_field(void) { + flecs::world ecs; + + int32_t count = 0; + ecs.entity().set({10, 20}); + + ecs.observer() + .event(flecs::OnSet) + .yield_existing() + .run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + + test_int(p->x, 10); + test_int(p->y, 20); + + count ++; + } + }); + + test_int(count, 1); +} + +void Observer_run_callback_w_yield_existing_2_fields(void) { + flecs::world ecs; + + int32_t count = 0; + ecs.entity().set({10, 20}).set({1, 2}); + + ecs.observer() + .event(flecs::OnSet) + .yield_existing() + .run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + test_int(p->x, 10); + test_int(p->y, 20); + test_int(v->x, 1); + test_int(v->y, 2); + + count ++; + } + }); + + test_int(count, 1); +} + +void Observer_get_query(void) { + flecs::world world; + + world.entity().set({0, 0}); + world.entity().set({1, 0}); + world.entity().set({2, 0}); + + int32_t count = 0; + + auto sys = world.observer() + .event(flecs::OnSet) + .each([&](flecs::entity e, const Position& p) { + // Not used + }); + + auto q = sys.query(); + + q.run([&](flecs::iter& it) { + while (it.next()) { + auto pos = it.field(0); + for (auto i : it) { + test_int(i, pos[i].x); + count ++; + } + } + }); + + test_int(count, 3); +} + +void Observer_on_set_w_set(void) { + flecs::world world; + + int32_t count = 0; + + world.observer() + .event(flecs::OnSet) + .each([&](flecs::entity e, Position& p) { + count ++; + }); + + flecs::entity e = world.entity(); + test_int(count, 0); + + e.set({10, 20}); + test_int(count, 1); +} + +void Observer_on_set_w_defer_set(void) { + flecs::world world; + + int32_t count = 0; + + world.observer() + .event(flecs::OnSet) + .each([&](flecs::entity e, Position& p) { + count ++; + }); + + flecs::entity e = world.entity(); + test_int(count, 0); + + world.defer_begin(); + e.set({10, 20}); + + test_int(count, 0); + world.defer_end(); + + test_int(count, 1); +} + +void Observer_on_set_w_set_sparse(void) { + flecs::world world; + + world.component().add(flecs::Sparse); + + int32_t count = 0; + + world.observer() + .event(flecs::OnSet) + .each([&](flecs::entity e, Position& p) { + count ++; + }); + + flecs::entity e = world.entity(); + test_int(count, 0); + + e.set({10, 20}); + test_int(count, 1); +} + +#include + +void Observer_on_add_singleton(void) { + flecs::world world; + + int32_t count = 0; + + world.observer() + .term_at(0).singleton() + .event(flecs::OnSet) + .each([&](Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + world.set({10, 20}); + + test_int(count, 1); +} + +void Observer_on_add_pair_singleton(void) { + flecs::world world; + + int32_t count = 0; + + flecs::entity tgt = world.entity(); + + world.observer() + .term_at(0).second(tgt).singleton() + .event(flecs::OnSet) + .each([&](Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + world.set(tgt, {10, 20}); + + test_int(count, 1); +} + +void Observer_on_add_pair_wildcard_singleton(void) { + flecs::world world; + + int32_t count = 0; + + flecs::entity tgt_1 = world.entity(); + flecs::entity tgt_2 = world.entity(); + + world.observer() + .term_at(0).second(flecs::Wildcard).singleton() + .event(flecs::OnSet) + .each([&](Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + world.set(tgt_1, {10, 20}); + test_int(count, 1); + + world.set(tgt_2, {10, 20}); + test_int(count, 2); +} + +void Observer_on_add_with_pair_singleton(void) { + flecs::world world; + + int32_t count = 0; + + flecs::entity tgt = world.entity(); + + world.observer() + .with(tgt).singleton() + .event(flecs::OnSet) + .each([&](flecs::entity) { + count ++; + }); + + world.set(tgt, {10, 20}); + test_int(count, 1); +} + +void Observer_add_in_yield_existing(void) { + flecs::world world; + + flecs::entity e1 = world.entity().set({}); + flecs::entity e2 = world.entity().set({}); + flecs::entity e3 = world.entity().set({}); + + world.observer() + .with() + .event(flecs::OnAdd) + .yield_existing() + .each([](flecs::entity e) { + e.add(); + }); + + test_assert(e1.has()); + test_assert(e1.has()); + + test_assert(e2.has()); + test_assert(e2.has()); + + test_assert(e3.has()); + test_assert(e3.has()); +} + +void Observer_add_in_yield_existing_multi(void) { + flecs::world world; + + flecs::entity e1 = world.entity().set({}).set({}); + flecs::entity e2 = world.entity().set({}).set({}); + flecs::entity e3 = world.entity().set({}).set({}); + + world.observer() + .with() + .with() + .event(flecs::OnAdd) + .yield_existing() + .each([](flecs::entity e) { + e.add(); + }); + + test_assert(e1.has()); + test_assert(e1.has()); + test_assert(e1.has()); + + test_assert(e2.has()); + test_assert(e2.has()); + test_assert(e2.has()); + + test_assert(e3.has()); + test_assert(e3.has()); + test_assert(e3.has()); +} + +void Observer_name_from_root(void) { + flecs::world ecs; + + flecs::entity sys = ecs.observer("::ns::MySystem") + .event(flecs::OnSet) + .each([](Position& p) { }); + + test_str(sys.name(), "MySystem"); + + flecs::entity ns = ecs.entity("::ns"); + test_assert(ns == sys.parent()); +} + +void Observer_term_index(void) { + flecs::world ecs; + + auto e1 = ecs.entity().add().add(); + + int32_t last_term = -1; + + ecs.observer() + .event(flecs::OnSet) + .each([&](flecs::iter& it, size_t row, const Position &p, const Velocity &v) { + last_term = it.term_index(); + }); + + e1.set({ 10, 20 }); + test_int(last_term, 0); + + e1.set({ 30, 40 }); + test_int(last_term, 1); +} + +void Observer_implicit_register_in_emit_for_named_entity(void) { + flecs::world world; + + struct MyEvent { float value; }; + + flecs::entity e1 = world.entity("e1"); + flecs::entity e2 = world.entity(); + + e1.observe([&](MyEvent& evt) { + test_int(evt.value, 10); + e2.set({10, 20}); + }); + + e1.emit({ 10 }); +} + +void Observer_add_to_named_in_emit_for_named_entity(void) { + flecs::world world; + + world.component(); + + struct MyEvent { float value; }; + + flecs::entity e1 = world.entity("e1"); + flecs::entity e2 = world.entity("e2"); + + e1.observe([&](MyEvent& evt) { + test_int(evt.value, 10); + e2.set({10, 20}); + }); + + e1.emit({ 10 }); +} + +void Observer_implicit_register_in_emit_for_named_entity_w_defer(void) { + flecs::world world; + + struct MyEvent { float value; }; + + flecs::entity e1 = world.entity("e1"); + flecs::entity e2 = world.entity(); + + e1.observe([&](MyEvent& evt) { + test_int(evt.value, 10); + e2.set({10, 20}); + }); + + world.defer_begin(); + e1.emit({ 10 }); + world.defer_end(); +} + +void Observer_add_to_named_in_emit_for_named_entity_w_defer(void) { + flecs::world world; + + world.component(); + + struct MyEvent { float value; }; + + flecs::entity e1 = world.entity("e1"); + flecs::entity e2 = world.entity("e2"); + + e1.observe([&](MyEvent& evt) { + test_int(evt.value, 10); + e2.set({10, 20}); + }); + + world.defer_begin(); + e1.emit({ 10 }); + world.defer_end(); +} + +void Observer_register_twice_w_each(void) { + flecs::world ecs; + + int count1 = 0, count2 = 0; + + ecs.observer("Test") + .event(flecs::OnSet) + .each([&](Position&) { + count1 ++; + }); + + ecs.entity().set(Position{10, 20}); + test_int(count1, 1); + + ecs.observer("Test") + .event(flecs::OnSet) + .each([&](Position&) { + count2 ++; + }); + + ecs.entity().set(Position{10, 20}); + test_int(count2, 1); +} + +void Observer_register_twice_w_run(void) { + flecs::world ecs; + + int count1 = 0, count2 = 0; + + ecs.observer("Test") + .event(flecs::OnSet) + .run([&](flecs::iter&) { + count1 ++; + }); + + ecs.entity().set(Position{10, 20}); + test_int(count1, 1); + + ecs.observer("Test") + .event(flecs::OnSet) + .run([&](flecs::iter&) { + count2 ++; + }); + + ecs.entity().set(Position{10, 20}); + test_int(count2, 1); +} + +void Observer_register_twice_w_run_each(void) { + flecs::world ecs; + + int count1 = 0, count2 = 0; + + ecs.observer("Test") + .event(flecs::OnSet) + .run([&](flecs::iter&) { + count1 ++; + }); + + ecs.entity().set(Position{10, 20}); + test_int(count1, 1); + + ecs.observer("Test") + .event(flecs::OnSet) + .each([&](Position&) { + count2 ++; + }); + + ecs.entity().set(Position{10, 20}); + test_int(count2, 1); +} + +void Observer_register_twice_w_each_run(void) { + flecs::world ecs; + + int count1 = 0, count2 = 0; + + ecs.observer("Test") + .event(flecs::OnSet) + .each([&](Position&) { + count1 ++; + }); + + ecs.entity().set(Position{10, 20}); + test_int(count1, 1); + + ecs.observer("Test") + .event(flecs::OnSet) + .run([&](flecs::iter&) { + count2 ++; + }); + + ecs.entity().set(Position{10, 20}); + test_int(count2, 1); +} + +void Observer_other_table(void) { + flecs::world ecs; + + int32_t count = 0; + flecs::entity matched; + + ecs.observer() + .event(flecs::OnAdd) + .each([&](flecs::iter& it, size_t row, Velocity&) { + test_assert(it.table().has()); + test_assert(!it.other_table().has()); + matched = it.entity(row); + count ++; + }); + + flecs::entity e = ecs.entity().add().add(); + test_assert(e == matched); + test_int(count, 1); +} + +void Observer_other_table_w_pair(void) { + flecs::world ecs; + + struct Likes {}; + struct Apples {}; + + int32_t count = 0; + flecs::entity matched; + + ecs.observer() + .with() + .event(flecs::OnAdd) + .each([&](flecs::iter& it, size_t row) { + test_assert((it.table().has())); + test_assert((!it.other_table().has())); + matched = it.entity(row); + count ++; + }); + + flecs::entity e = ecs.entity().add().add(); + test_assert(e == matched); + test_int(count, 1); +} + +void Observer_other_table_w_pair_wildcard(void) { + flecs::world ecs; + + struct Likes {}; + struct Apples {}; + + int32_t count = 0; + flecs::entity matched; + + ecs.observer() + .with() + .event(flecs::OnAdd) + .each([&](flecs::iter& it, size_t row) { + test_assert((it.table().has(flecs::Wildcard))); + test_assert((!it.other_table().has(flecs::Wildcard))); + matched = it.entity(row); + count ++; + }); + + flecs::entity e = ecs.entity().add().add(); + test_assert(e == matched); + test_int(count, 1); +} + +void Observer_on_add_inherited(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + int32_t count = 0; + flecs::entity matched; + + ecs.observer() + .event(flecs::OnAdd) + .each([&](flecs::entity e, Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + matched = e; + }); + + auto p = ecs.prefab().set(Position{10, 20}); + test_int(count, 0); + + auto i = ecs.entity().is_a(p); + test_int(count, 1); + test_assert(i == matched); +} + +void Observer_on_set_inherited(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + int32_t count = 0; + flecs::entity matched; + + ecs.observer() + .event(flecs::OnSet) + .each([&](flecs::entity e, Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + matched = e; + }); + + auto p = ecs.prefab().set(Position{10, 20}); + test_int(count, 0); + + auto i = ecs.entity().is_a(p); + test_int(count, 1); + test_assert(i == matched); +} + +void Observer_on_remove_inherited(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + int32_t count = 0; + flecs::entity matched; + + ecs.observer() + .event(flecs::OnRemove) + .each([&](flecs::entity e, Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + matched = e; + }); + + auto p = ecs.prefab().set(Position{10, 20}); + test_int(count, 0); + + auto i = ecs.entity().is_a(p); + test_int(count, 0); + + p.remove(); + test_int(count, 1); + test_assert(i == matched); +} + +void Observer_on_set_after_remove_override(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + auto base = ecs.prefab() + .set(Position{1, 2}); + + auto e1 = ecs.entity().is_a(base) + .set(Position{10, 20}); + + int count = 0; + + ecs.observer() + .event(flecs::OnSet) + .each([&](flecs::iter& it, size_t row, Position& p) { + test_assert(it.entity(row) == e1); + test_assert(it.src(0) == base); + test_int(p.x, 1); + test_int(p.y, 2); + count ++; + }); + + e1.remove(); + + test_int(count, 1); +} + +void Observer_on_set_after_remove_override_create_observer_before(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + int count = 0; + + flecs::entity base = ecs.prefab(); + flecs::entity e1 = ecs.entity(); + + ecs.observer() + .event(flecs::OnSet) + .each([&](flecs::iter& it, size_t row, Position& p) { + test_assert(it.entity(row) == e1); + test_assert(it.src(0) == base); + count ++; + }); + + base.set(Position{1, 2}); + e1.add().is_a(base); + + test_int(count, 0); + + e1.remove(); + + test_int(count, 1); +} + +void Observer_on_set_w_override_after_delete(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + auto base = ecs.prefab() + .set(Position{1, 2}); + + auto e1 = ecs.entity().is_a(base) + .set(Position{10, 20}); + + int count = 0; + + ecs.observer() + .event(flecs::OnSet) + .each([&](flecs::iter& it, size_t row, Position& p) { + count ++; + }); + + e1.destruct(); + + test_int(count, 0); +} + +void Observer_on_set_w_override_after_clear(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + auto base = ecs.prefab() + .set(Position{1, 2}); + + auto e1 = ecs.entity().is_a(base) + .set(Position{10, 20}); + + int count = 0; + + ecs.observer() + .event(flecs::OnSet) + .each([&](flecs::iter& it, size_t row, Position& p) { + count ++; + }); + + e1.clear(); + + test_int(count, 0); +} diff --git a/vendors/flecs/test/cpp_api/src/Pairs.cpp b/vendors/flecs/test/cpp/src/Pairs.cpp similarity index 87% rename from vendors/flecs/test/cpp_api/src/Pairs.cpp rename to vendors/flecs/test/cpp/src/Pairs.cpp index 386f5ec20..48694723e 100644 --- a/vendors/flecs/test/cpp_api/src/Pairs.cpp +++ b/vendors/flecs/test/cpp/src/Pairs.cpp @@ -1,4 +1,4 @@ -#include +#include typedef struct Pair { float value; @@ -146,12 +146,14 @@ void Pairs_system_1_pair_instance(void) { ecs.system<>() .expr("(Pair, *)") - .iter([&](flecs::iter it) { - flecs::column tr(it, 1); - invoke_count ++; - for (auto i : it) { - entity_count ++; - trait_value = tr[i].value; + .run([&](flecs::iter& it) { + while (it.next()) { + flecs::field tr(it, 0); + invoke_count ++; + for (auto i : it) { + entity_count ++; + trait_value = tr[i].value; + } } }); @@ -175,12 +177,14 @@ void Pairs_system_2_pair_instances(void) { ecs.system<>() .expr("(Pair, *)") - .iter([&](flecs::iter it) { - flecs::column tr(it, 1); - invoke_count ++; - for (auto i : it) { - entity_count ++; - trait_value += tr[i].value; + .run([&](flecs::iter it) { + while (it.next()) { + flecs::field tr(it, 0); + invoke_count ++; + for (auto i : it) { + entity_count ++; + trait_value += tr[i].value; + } } }); @@ -194,6 +198,8 @@ void Pairs_system_2_pair_instances(void) { void Pairs_override_pair(void) { flecs::world ecs; + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + auto base = ecs.entity() .set({10}); @@ -221,7 +227,7 @@ void Pairs_override_pair(void) { void Pairs_override_tag_pair(void) { flecs::world ecs; - auto Pair = ecs.entity(); + auto Pair = ecs.entity().add(flecs::OnInstantiate, flecs::Inherit); auto base = ecs.entity() .set_second(Pair, {10, 20}); @@ -250,55 +256,53 @@ void Pairs_override_tag_pair(void) { test_assert(t == t_2); } -void Pairs_get_mut_pair(void) { +void Pairs_ensure_pair(void) { flecs::world ecs; auto e = ecs.entity(); - Pair *t = e.get_mut(); - test_assert(t != NULL); - t->value = 10; + Pair& t = e.ensure(); + t.value = 10; const Pair *t_2 = e.get(); - test_assert(t == t_2); - test_int(t->value, 10); + test_int(t.value, 10); + test_int(t_2->value, 10); } -void Pairs_get_mut_pair_existing(void) { +void Pairs_ensure_pair_existing(void) { flecs::world ecs; auto e = ecs.entity() .set({20}); - Pair *t = e.get_mut(); - test_assert(t != NULL); - test_int(t->value, 20); - t->value = 10; + Pair& t = e.ensure(); + test_int(t.value, 20); + t.value = 10; const Pair *t_2 = e.get(); - test_assert(t == t_2); - test_int(t->value, 10); + test_int(t.value, 10); + test_int(t_2->value, 10); } -void Pairs_get_mut_pair_tag(void) { +void Pairs_ensure_pair_tag(void) { flecs::world ecs; auto Pair = ecs.entity(); auto e = ecs.entity(); - Position *p = e.get_mut_second(Pair); - test_assert(p != NULL); - p->x = 10; - p->y = 20; + Position& p = e.ensure_second(Pair); + p.x = 10; + p.y = 20; const Position *p_2 = e.get_second(Pair); - test_assert(p == p_2); - test_int(p->x, 10); - test_int(p->y, 20); + test_int(p.x, 10); + test_int(p.y, 20); + test_int(p_2->x, 10); + test_int(p_2->y, 20); } -void Pairs_get_mut_pair_tag_existing(void) { +void Pairs_ensure_pair_tag_existing(void) { flecs::world ecs; auto Pair = ecs.entity(); @@ -306,34 +310,34 @@ void Pairs_get_mut_pair_tag_existing(void) { auto e = ecs.entity() .set_second(Pair, {10, 20}); - Position *p = e.get_mut_second(Pair); - test_assert(p != NULL); - test_int(p->x, 10); - test_int(p->y, 20); + Position& p = e.ensure_second(Pair); + test_int(p.x, 10); + test_int(p.y, 20); const Position *p_2 = e.get_second(Pair); - test_assert(p == p_2); - test_int(p->x, 10); - test_int(p->y, 20); + test_int(p.x, 10); + test_int(p.y, 20); + test_int(p_2->x, 10); + test_int(p_2->y, 20); } -void Pairs_get_mut_R_tag_O(void) { +void Pairs_ensure_R_tag_O(void) { flecs::world ecs; auto e = ecs.entity() .set({10, 20}); - Position *t = e.get_mut(); - test_assert(t != NULL); - test_int(t->x, 10); - test_int(t->y, 20); - t->x = 30; - t->y = 40; + Position& t = e.ensure(); + test_int(t.x, 10); + test_int(t.y, 20); + t.x = 30; + t.y = 40; const Position *t_2 = e.get(); - test_assert(t == t_2); - test_int(t->x, 30); - test_int(t->y, 40); + test_int(t.x, 30); + test_int(t.y, 40); + test_int(t_2->x, 30); + test_int(t_2->y, 40); } void Pairs_get_relation_from_id(void) { @@ -993,7 +997,7 @@ void Pairs_set_1_pair_arg(void) { flecs::world ecs; auto e = ecs.entity() - .set([](EatsApples&& a) { + .insert([](EatsApples&& a) { a->amount = 10; }); @@ -1005,7 +1009,7 @@ void Pairs_set_2_pair_arg(void) { flecs::world ecs; auto e = ecs.entity() - .set([](EatsApples&& a, EatsPears&& p) { + .insert([](EatsApples&& a, EatsPears&& p) { a->amount = 10; p->amount = 20; }); @@ -1033,7 +1037,7 @@ void Pairs_set_inline_pair_type(void) { flecs::world ecs; auto e = ecs.entity() - .set([](flecs::pair&& a) { + .insert([](flecs::pair&& a) { a->amount = 10; }); @@ -1056,7 +1060,7 @@ void Pairs_set_pair_type_object(void) { flecs::world ecs; auto e = ecs.entity() - .set([](flecs::pair_object&& a) { + .insert([](flecs::pair_object&& a) { a->amount = 10; }); @@ -1105,6 +1109,8 @@ void Pairs_set_get_second_variants(void) { void Pairs_get_object_for_type_self(void) { flecs::world ecs; + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + auto base = ecs.entity().add(); auto self = ecs.entity().is_a(base).add(); @@ -1116,6 +1122,8 @@ void Pairs_get_object_for_type_self(void) { void Pairs_get_object_for_type_base(void) { flecs::world ecs; + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + auto base = ecs.entity().add(); auto self = ecs.entity().is_a(base); @@ -1127,7 +1135,7 @@ void Pairs_get_object_for_type_base(void) { void Pairs_get_object_for_id_self(void) { flecs::world ecs; - auto tag = ecs.entity(); + auto tag = ecs.entity().add(flecs::OnInstantiate, flecs::Inherit); auto base = ecs.entity().add(tag); auto self = ecs.entity().is_a(base).add(tag); @@ -1139,7 +1147,7 @@ void Pairs_get_object_for_id_self(void) { void Pairs_get_object_for_id_base(void) { flecs::world ecs; - auto tag = ecs.entity(); + auto tag = ecs.entity().add(flecs::OnInstantiate, flecs::Inherit); auto base = ecs.entity().add(tag); auto self = ecs.entity().is_a(base); @@ -1151,7 +1159,7 @@ void Pairs_get_object_for_id_base(void) { void Pairs_get_object_for_id_not_found(void) { flecs::world ecs; - auto tag = ecs.entity(); + auto tag = ecs.entity().add(flecs::OnInstantiate, flecs::Inherit); auto base = ecs.entity(); auto self = ecs.entity().is_a(base); @@ -1226,3 +1234,69 @@ void Pairs_set_R_existing_value(void) { test_int(ptr->x, 10); test_int(ptr->y, 20); } + +void Pairs_symmetric_w_childof(void) { + flecs::world ecs; + + struct Likes { }; + + ecs.component().add(flecs::Symmetric); + + flecs::entity idk = ecs.entity("Idk"); + + flecs::entity bob = ecs.entity("Bob") + .child_of(idk); + + flecs::entity alice = ecs.entity("Alice") + .child_of(idk) + .add(bob); + + test_assert(bob.has(alice)); +} + +void Pairs_modified_tag_second(void) { + flecs::world ecs; + + int32_t count = 0; + ecs.observer() + .term_at(0).second() + .event(flecs::OnSet) + .each([&](Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + flecs::entity e = ecs.entity(); + + Position& p = e.ensure(); + p.x = 10; + p.y = 20; + e.modified(); + + test_int(count, 1); +} + +void Pairs_modified_tag_first(void) { + flecs::world ecs; + + int32_t count = 0; + ecs.observer() + .with() + .event(flecs::OnSet) + .each([&](flecs::iter& it, size_t row) { + auto p = it.field_at(0, row); + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + flecs::entity e = ecs.entity(); + + Position& p = e.ensure(); + p.x = 10; + p.y = 20; + e.modified(); + + test_int(count, 1); +} diff --git a/vendors/flecs/test/cpp_api/src/Paths.cpp b/vendors/flecs/test/cpp/src/Paths.cpp similarity index 72% rename from vendors/flecs/test/cpp_api/src/Paths.cpp rename to vendors/flecs/test/cpp/src/Paths.cpp index 7e01916a5..9d65dafd7 100644 --- a/vendors/flecs/test/cpp_api/src/Paths.cpp +++ b/vendors/flecs/test/cpp/src/Paths.cpp @@ -1,4 +1,4 @@ -#include +#include void Paths_name(void) { flecs::world world; @@ -233,3 +233,97 @@ void Paths_alias_entity_empty(void) { test_assert(e.id() != 0); } + +void Paths_id_from_str_0_entity(void) { + flecs::world ecs; + + flecs::id id = ecs.id("#0"); + test_assert(id == 0); +} + +void Paths_id_from_str_entity_from_str(void) { + flecs::world ecs; + + flecs::entity foo = ecs.entity("foo"); + + flecs::id id = ecs.id("foo"); + test_assert(id != 0); + test_assert(id == foo); +} + +void Paths_id_from_str_unresolved_entity_from_str(void) { + flecs::world ecs; + + flecs::id id = ecs.id("foo"); + test_assert(id == 0); +} + +void Paths_id_from_str_scoped_entity_from_str(void) { + flecs::world ecs; + + flecs::entity foo = ecs.entity("foo::bar"); + + flecs::id id = ecs.id("foo.bar"); + test_assert(id != 0); + test_assert(id == foo); +} + +void Paths_id_from_str_template_entity_from_str(void) { + flecs::world ecs; + + flecs::entity foo = ecs.entity("foo"); + + flecs::id id = ecs.id("foo"); + test_assert(id != 0); + test_assert(id == foo); +} + +void Paths_id_from_str_pair_from_str(void) { + flecs::world ecs; + + flecs::entity rel = ecs.entity("Rel"); + flecs::entity tgt = ecs.entity("Tgt"); + + flecs::id id = ecs.id("(Rel, Tgt)"); + test_assert(id != 0); + test_assert(id == ecs.pair(rel, tgt)); +} + +void Paths_id_from_str_unresolved_pair_from_str(void) { + flecs::world ecs; + + flecs::entity rel = ecs.entity("Rel"); + + flecs::id id = ecs.id("(Rel, Tgt)"); + test_assert(id == 0); +} + +void Paths_id_from_str_wildcard_pair_from_str(void) { + flecs::world ecs; + + flecs::entity rel = ecs.entity("Rel"); + + flecs::id id = ecs.id("(Rel, *)"); + test_assert(id != 0); + test_assert(id == ecs.pair(rel, flecs::Wildcard)); +} + +void Paths_id_from_str_any_pair_from_str(void) { + flecs::world ecs; + + flecs::entity rel = ecs.entity("Rel"); + + flecs::id id = ecs.id("(Rel, _)"); + test_assert(id != 0); + test_assert(id == ecs.pair(rel, flecs::Any)); +} + +void Paths_id_from_str_invalid_pair(void) { + flecs::world ecs; + + flecs::entity rel = ecs.entity("Rel"); + flecs::entity tgt = ecs.entity("Tgt"); + + flecs::id id = ecs.id("(Rel, Tgt"); + test_assert(id == 0); +} diff --git a/vendors/flecs/test/cpp_api/src/PrettyFunction.cpp b/vendors/flecs/test/cpp/src/PrettyFunction.cpp similarity index 96% rename from vendors/flecs/test/cpp_api/src/PrettyFunction.cpp rename to vendors/flecs/test/cpp/src/PrettyFunction.cpp index 65939581f..b4cee714b 100644 --- a/vendors/flecs/test/cpp_api/src/PrettyFunction.cpp +++ b/vendors/flecs/test/cpp/src/PrettyFunction.cpp @@ -1,4 +1,4 @@ -#include +#include #include // Helper to debug differences in function name strings across compilers diff --git a/vendors/flecs/test/cpp_api/src/Query.cpp b/vendors/flecs/test/cpp/src/Query.cpp similarity index 50% rename from vendors/flecs/test/cpp_api/src/Query.cpp rename to vendors/flecs/test/cpp/src/Query.cpp index 8e1c05390..4cfce1bbb 100644 --- a/vendors/flecs/test/cpp_api/src/Query.cpp +++ b/vendors/flecs/test/cpp/src/Query.cpp @@ -1,25 +1,625 @@ -#include +#include struct Pair { float value; }; -void Query_action(void) { +void Query_term_each_component(void) { + flecs::world ecs; + + auto e_1 = ecs.entity().set({1, 2}); + auto e_2 = ecs.entity().set({3, 4}); + auto e_3 = ecs.entity().set({5, 6}); + + e_3.add(); + + int32_t count = 0; + ecs.each([&](flecs::entity e, Position& p) { + if (e == e_1) { + test_int(p.x, 1); + test_int(p.y, 2); + count ++; + } + if (e == e_2) { + test_int(p.x, 3); + test_int(p.y, 4); + count ++; + } + if (e == e_3) { + test_int(p.x, 5); + test_int(p.y, 6); + count ++; + } + }); + + test_int(count, 3); +} + +void Query_term_each_tag(void) { + flecs::world ecs; + + struct Foo { }; + + auto e_1 = ecs.entity().add(); + auto e_2 = ecs.entity().add(); + auto e_3 = ecs.entity().add(); + + e_3.add(); + + int32_t count = 0; + ecs.each([&](flecs::entity e, Foo) { + if (e == e_1 || e == e_2 || e == e_3) { + count ++; + } + }); + + test_int(count, 3); +} + +void Query_term_each_id(void) { + flecs::world ecs; + + auto foo = ecs.entity(); + + auto e_1 = ecs.entity().add(foo); + auto e_2 = ecs.entity().add(foo); + auto e_3 = ecs.entity().add(foo); + + e_3.add(); + + int32_t count = 0; + ecs.each(foo, [&](flecs::entity e) { + if (e == e_1 || e == e_2 || e == e_3) { + count ++; + } + }); + + test_int(count, 3); +} + +void Query_term_each_pair_type(void) { + flecs::world ecs; + + struct Rel { }; + struct Obj { }; + + auto e_1 = ecs.entity().add(); + auto e_2 = ecs.entity().add(); + auto e_3 = ecs.entity().add(); + + e_3.add(); + + int32_t count = 0; + ecs.each>([&](flecs::entity e, flecs::pair) { + if (e == e_1 || e == e_2 || e == e_3) { + count ++; + } + }); + + test_int(count, 3); +} + +void Query_term_each_pair_id(void) { + flecs::world ecs; + + auto rel = ecs.entity(); + auto obj = ecs.entity(); + + auto e_1 = ecs.entity().add(rel, obj); + auto e_2 = ecs.entity().add(rel, obj); + auto e_3 = ecs.entity().add(rel, obj); + + e_3.add(); + + int32_t count = 0; + ecs.each(ecs.pair(rel, obj), [&](flecs::entity e) { + if (e == e_1 || e == e_2 || e == e_3) { + count ++; + } + }); + + test_int(count, 3); +} + +void Query_term_each_pair_relation_wildcard(void) { + flecs::world ecs; + + auto rel_1 = ecs.entity(); + auto rel_2 = ecs.entity(); + auto obj = ecs.entity(); + + auto e_1 = ecs.entity().add(rel_1, obj); + auto e_2 = ecs.entity().add(rel_1, obj); + auto e_3 = ecs.entity().add(rel_2, obj); + + e_3.add(); + + int32_t count = 0; + ecs.each(ecs.pair(flecs::Wildcard, obj), [&](flecs::entity e) { + if (e == e_1 || e == e_2 || e == e_3) { + count ++; + } + }); + + test_int(count, 3); +} + +void Query_term_each_pair_object_wildcard(void) { + flecs::world ecs; + + auto rel = ecs.entity(); + auto obj_1 = ecs.entity(); + auto obj_2 = ecs.entity(); + + auto e_1 = ecs.entity().add(rel, obj_1); + auto e_2 = ecs.entity().add(rel, obj_1); + auto e_3 = ecs.entity().add(rel, obj_2); + + e_3.add(); + + int32_t count = 0; + ecs.each(ecs.pair(rel, flecs::Wildcard), [&](flecs::entity e) { + if (e == e_1 || e == e_2 || e == e_3) { + count ++; + } + }); + + test_int(count, 3); +} + +void Query_default_ctor_no_assign(void) { + flecs::query<> f; + + // Make sure code compiles & works + test_assert(true); +} + +void Query_term_get_id(void) { + flecs::world ecs; + + auto Foo = ecs.entity(); + auto Bar = ecs.entity(); + + auto q = ecs.query_builder() + .with() + .with() + .with(Foo, Bar) + .build(); + + test_int(q.field_count(), 3); + + flecs::term + t = q.term(0); + test_assert(t.id() == ecs.id()); + t = q.term(1); + test_assert(t.id() == ecs.id()); + t = q.term(2); + test_assert(t.id() == ecs.pair(Foo, Bar)); +} + +void Query_term_get_subj(void) { + flecs::world ecs; + + auto Foo = ecs.entity(); + auto Bar = ecs.entity(); + auto Src = ecs.entity(); + + auto q = ecs.query_builder() + .with() + .with().src(Src) + .with(Foo, Bar) + .build(); + + test_int(q.field_count(), 3); + + flecs::term + t = q.term(0); + test_assert(t.get_src() == flecs::This); + t = q.term(1); + test_assert(t.get_src() == Src); + t = q.term(2); + test_assert(t.get_src() == flecs::This); +} + +void Query_term_get_pred(void) { + flecs::world ecs; + + auto Foo = ecs.entity(); + auto Bar = ecs.entity(); + auto Src = ecs.entity(); + + auto q = ecs.query_builder() + .with() + .with().src(Src) + .with(Foo, Bar) + .build(); + + test_int(q.field_count(), 3); + + flecs::term + t = q.term(0); + test_assert(t.get_first() == ecs.id()); + t = q.term(1); + test_assert(t.get_first() == ecs.id()); + t = q.term(2); + test_assert(t.get_first() == Foo); +} + +void Query_term_get_obj(void) { + flecs::world ecs; + + auto Foo = ecs.entity(); + auto Bar = ecs.entity(); + auto Src = ecs.entity(); + + auto q = ecs.query_builder() + .with() + .with().src(Src) + .with(Foo, Bar) + .build(); + + test_int(q.field_count(), 3); + + flecs::term + t = q.term(0); + test_assert(t.get_second() == 0); + t = q.term(1); + test_assert(t.get_second() == 0); + t = q.term(2); + test_assert(t.get_second() == Bar); +} + +void Query_get_first(void) { + flecs::world ecs; + + struct A {}; + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + ecs.entity().add(); + + auto q = ecs.query(); + + auto first = q.iter().first(); + test_assert(first != 0); + test_assert(first == e1); +} + +void Query_get_count_direct(void) { + flecs::world ecs; + + struct A {}; + + ecs.entity().add(); + ecs.entity().add(); + ecs.entity().add(); + + auto q = ecs.query(); + + test_int(3, q.count()); +} + +void Query_get_is_true_direct(void) { + flecs::world ecs; + + struct A {}; + struct B {}; + + ecs.entity().add(); + ecs.entity().add(); + ecs.entity().add(); + + auto q_1 = ecs.query(); + auto q_2 = ecs.query(); + + test_bool(true, q_1.is_true()); + test_bool(false, q_2.is_true()); +} + +void Query_get_first_direct(void) { + flecs::world ecs; + + struct A {}; + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + ecs.entity().add(); + + auto q = ecs.query(); + + auto first = q.first(); + test_assert(first != 0); + test_assert(first == e1); +} + +void Query_each_w_no_this(void) { + flecs::world ecs; + + auto e = ecs.entity() + .set({10, 20}) + .set({1, 2}); + + auto f = ecs.query_builder() + .term_at(0).src(e) + .term_at(1).src(e) + .build(); + + int32_t count = 0; + + f.each([&](Position& p, Velocity& v) { + count ++; + test_int(p.x, 10); + test_int(p.y, 20); + test_int(v.x, 1); + test_int(v.y, 2); + }); + + test_int(count, 1); +} + +void Query_each_w_iter_no_this(void) { + flecs::world ecs; + + auto e = ecs.entity() + .set({10, 20}) + .set({1, 2}); + + auto f = ecs.query_builder() + .term_at(0).src(e) + .term_at(1).src(e) + .build(); + + int32_t count = 0; + + f.each([&](flecs::iter& it, size_t index, Position& p, Velocity& v) { + count ++; + test_int(p.x, 10); + test_int(p.y, 20); + test_int(v.x, 1); + test_int(v.y, 2); + test_int(index, 0); + test_int(it.count(), 0); + }); + + test_int(count, 1); +} + +void Query_invalid_each_w_no_this(void) { + install_test_abort(); + + flecs::world ecs; + + auto e = ecs.entity() + .set({10, 20}) + .set({1, 2}); + + auto f = ecs.query_builder() + .term_at(0).src(e) + .term_at(1).src(e) + .build(); + + test_expect_abort(); + + f.each([&](flecs::entity e, Position& p, Velocity& v) { }); +} + +void Query_named_query(void) { + flecs::world ecs; + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add(); + + auto q = ecs.query("my_query"); + + int32_t count = 0; + q.each([&](flecs::entity e, Position&) { + test_assert(e == e1 || e == e2); + count ++; + }); + test_int(count, 2); + + flecs::entity qe = q.entity(); + test_assert(qe != 0); + test_str(qe.name(), "my_query"); +} + +void Query_named_scoped_query(void) { + flecs::world ecs; + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add(); + + auto q = ecs.query("my::query"); + + int32_t count = 0; + q.each([&](flecs::entity e, Position&) { + test_assert(e == e1 || e == e2); + count ++; + }); + test_int(count, 2); + + flecs::entity qe = q.entity(); + test_assert(qe != 0); + test_str(qe.name(), "query"); + test_str(qe.path(), "::my::query"); +} + +void Query_set_this_var(void) { + flecs::world ecs; + + /* auto e_1 = */ ecs.entity().set({1, 2}); + auto e_2 = ecs.entity().set({3, 4}); + /* auto e_3 = */ ecs.entity().set({5, 6}); + + auto q = ecs.query("my::query"); + + int32_t count = 0; + q.iter().set_var(0, e_2).each([&](flecs::entity e, Position&) { + test_assert(e == e_2); + count ++; + }); + test_int(count, 1); +} + +void Query_inspect_terms_w_expr(void) { + flecs::world ecs; + + flecs::query<> f = ecs.query_builder() + .expr("(ChildOf,#0)") + .build(); + + int32_t count = 0; + f.each_term([&](flecs::term &term) { + test_assert(term.id().is_pair()); + count ++; + }); + + test_int(count, 1); +} + +void Query_find(void) { + flecs::world ecs; + + /* auto e1 = */ ecs.entity().set({10, 20}); + auto e2 = ecs.entity().set({20, 30}); + + auto q = ecs.query(); + + auto r = q.find([](Position& p) { + return p.x == 20; + }); + + test_assert(r == e2); +} + +void Query_find_not_found(void) { + flecs::world ecs; + + /* auto e1 = */ ecs.entity().set({10, 20}); + /* auto e2 = */ ecs.entity().set({20, 30}); + + auto q = ecs.query(); + + auto r = q.find([](Position& p) { + return p.x == 30; + }); + + test_assert(!r); +} + +void Query_find_w_entity(void) { + flecs::world ecs; + + /* auto e1 = */ ecs.entity().set({10, 20}).set({20, 30}); + auto e2 = ecs.entity().set({20, 30}).set({20, 30}); + + auto q = ecs.query(); + + auto r = q.find([](flecs::entity e, Position& p) { + return p.x == e.get()->x && + p.y == e.get()->y; + }); + + test_assert(r == e2); +} + +// Generic lambdas are a C++14 feature. + +struct GenericLambdaFindEntity { + template + bool operator()(E&&, P&&, V&&, M&&) const { + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + return true; + } +}; + +struct GenericLambdaFindIter { + template + bool operator()(T&&, I&&, P&&, V&&, M&&) const { + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + return true; + } +}; + +struct GenericLambdaFindComps { + template + bool operator()(P&&, V&&, M&&) const { + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + return true; + } +}; + +void Query_find_generic(void) { + flecs::world world; + + auto q = world.query(); + + q.find(GenericLambdaFindEntity{}); + q.find(GenericLambdaFindIter{}); + q.find(GenericLambdaFindComps{}); +} + +void Query_optional_pair_term(void) { + flecs::world ecs; + + ecs.entity() + .add() + .emplace(1.0f, 2.0f); + ecs.entity() + .add(); + + int32_t with_pair = 0, without_pair = 0; + + auto f = ecs.query_builder*>() + .with() + .build(); + + f.each([&](flecs::entity e, Position* p) { + if (p) { + with_pair++; + test_flt(1.0f, p->x); + test_flt(2.0f, p->y); + } else { + without_pair++; + } + }); + + ecs.progress(1.0); + + test_int(1, with_pair); + test_int(1, without_pair); +} + +void Query_run(void) { flecs::world world; world.component(); world.component(); - auto entity = flecs::entity(world) + auto entity = world.entity() .set({10, 20}) .set({1, 2}); auto q = world.query(); - q.iter([](flecs::iter& it, Position *p, Velocity *v) { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -28,22 +628,27 @@ void Query_action(void) { test_int(p->y, 22); } -void Query_action_const(void) { +void Query_run_const(void) { flecs::world world; world.component(); world.component(); - auto entity = flecs::entity(world) + auto entity = world.entity() .set({10, 20}) .set({1, 2}); auto q = world.query(); - q.iter([](flecs::iter& it, Position *p, const Velocity *v) { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -52,31 +657,33 @@ void Query_action_const(void) { test_int(p->y, 22); } -void Query_action_shared(void) { +void Query_run_shared(void) { flecs::world world; - world.component(); - world.component(); + world.component().add(flecs::OnInstantiate, flecs::Inherit); + world.component().add(flecs::OnInstantiate, flecs::Inherit); - auto base = flecs::entity(world) + auto base = world.entity() .set({1, 2}); - auto e1 = flecs::entity(world) + auto e1 = world.entity() .set({10, 20}) .add(flecs::IsA, base); - auto e2 = flecs::entity(world) + auto e2 = world.entity() .set({10, 20}) .set({3, 4}); auto q = world.query_builder() - .expr("Velocity(self|up)") + .expr("Velocity(self|up IsA)") .build(); - q.iter([](flecs::iter&it, Position *p) { - auto v = it.field(2); + q.run([](flecs::iter&it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); - if (!it.is_self(2)) { + if (!it.is_self(1)) { for (auto i : it) { p[i].x += v->x; p[i].y += v->y; @@ -87,7 +694,8 @@ void Query_action_shared(void) { p[i].y += v[i].y; } } - }); + } + }); const Position *p = e1.get(); test_int(p->x, 11); @@ -98,41 +706,47 @@ void Query_action_shared(void) { test_int(p->y, 24); } -void Query_action_optional(void) { +void Query_run_optional(void) { flecs::world world; world.component(); world.component(); flecs::component(world, "Mass"); - auto e1 = flecs::entity(world) + auto e1 = world.entity() .set({10, 20}) .set({1, 2}) .set({1}); - auto e2 = flecs::entity(world) + auto e2 = world.entity() .set({30, 40}) .set({3, 4}) .set({1}); - auto e3 = flecs::entity(world) + auto e3 = world.entity() .set({50, 60}); - auto e4 = flecs::entity(world) + auto e4 = world.entity() .set({70, 80}); auto q = world.query(); - q.iter([](flecs::iter& it, Position *p, Velocity *v, Mass *m) { - if (it.is_set(2) && it.is_set(3)) { - for (auto i : it) { - p[i].x += v[i].x * m[i].value; - p[i].y += v[i].y * m[i].value; - } - } else { - for (auto i : it) { - p[i].x ++; - p[i].y ++; + q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + auto m = it.field(2); + + if (it.is_set(1) && it.is_set(2)) { + for (auto i : it) { + p[i].x += v[i].x * m[i].value; + p[i].y += v[i].y * m[i].value; + } + } else { + for (auto i : it) { + p[i].x ++; + p[i].y ++; + } } } }); @@ -154,13 +768,42 @@ void Query_action_optional(void) { test_int(p->y, 81); } +void Query_run_sparse(void) { + flecs::world world; + + world.component().add(flecs::Sparse); + world.component(); + + auto entity = world.entity() + .set({10, 20}) + .set({1, 2}); + + auto q = world.query(); + + q.run([](flecs::iter& it) { + while (it.next()) { + auto v = it.field(1); + + for (auto i : it) { + auto& p = it.field_at(0, i); + p.x += v[i].x; + p.y += v[i].y; + } + } + }); + + const Position *p = entity.get(); + test_int(p->x, 11); + test_int(p->y, 22); +} + void Query_each(void) { flecs::world world; world.component(); world.component(); - auto entity = flecs::entity(world) + auto entity = world.entity() .set({10, 20}) .set({1, 2}); @@ -182,7 +825,7 @@ void Query_each_const(void) { world.component(); world.component(); - auto entity = flecs::entity(world) + auto entity = world.entity() .set({10, 20}) .set({1, 2}); @@ -204,18 +847,18 @@ void Query_each_shared(void) { world.component(); world.component(); - auto base = flecs::entity(world) + auto base = world.entity() .set({1, 2}); - auto e1 = flecs::entity(world) + auto e1 = world.entity() .set({10, 20}) .add(flecs::IsA, base); - auto e2 = flecs::entity(world) + auto e2 = world.entity() .set({20, 30}) .add(flecs::IsA, base); - auto e3 = flecs::entity(world) + auto e3 = world.entity() .set({10, 20}) .set({3, 4}); @@ -246,20 +889,20 @@ void Query_each_optional(void) { world.component(); flecs::component(world, "Mass"); - auto e1 = flecs::entity(world) + auto e1 = world.entity() .set({10, 20}) .set({1, 2}) .set({1}); - auto e2 = flecs::entity(world) + auto e2 = world.entity() .set({30, 40}) .set({3, 4}) .set({1}); - auto e3 = flecs::entity(world) + auto e3 = world.entity() .set({50, 60}); - auto e4 = flecs::entity(world) + auto e4 = world.entity() .set({70, 80}); auto q = world.query(); @@ -291,25 +934,91 @@ void Query_each_optional(void) { test_int(p->y, 81); } +void Query_each_sparse(void) { + flecs::world world; + + world.component().add(flecs::Sparse); + world.component(); + + auto entity = world.entity() + .set({10, 20}) + .set({1, 2}); + + auto q = world.query(); + + q.each([](Position& p, Velocity& v) { + p.x += v.x; + p.y += v.y; + }); + + const Position *p = entity.get(); + test_int(p->x, 11); + test_int(p->y, 22); +} + +// Generic lambdas are a C++14 feature. + +struct GenericLambdaEachEntity { + template + void operator()(E&&, P&&, V&&, M&&) const { + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + } +}; + +struct GenericLambdaEachIter { + template + void operator()(T&&, I&&, P&&, V&&, M&&) const { + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + } +}; + +struct GenericLambdaEachComps { + template + void operator()(P&&, V&&, M&&) const { + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + static_assert(flecs::is_same::value, ""); + } +}; + +void Query_each_generic(void) { + flecs::world world; + + auto q = world.query(); + + q.each(GenericLambdaEachEntity{}); + q.each(GenericLambdaEachIter{}); + q.each(GenericLambdaEachComps{}); +} + void Query_signature(void) { flecs::world world; world.component(); world.component(); - auto entity = flecs::entity(world) + auto entity = world.entity() .set({10, 20}) .set({1, 2}); auto q = world.query_builder<>().expr("Position, Velocity").build(); - q.iter([](flecs::iter& it) { - auto p = it.field(1); - auto v = it.field(2); + q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -324,19 +1033,21 @@ void Query_signature_const(void) { world.component(); world.component(); - auto entity = flecs::entity(world) + auto entity = world.entity() .set({10, 20}) .set({1, 2}); auto q = world.query_builder<>().expr("Position, [in] Velocity").build(); - q.iter([](flecs::iter& it) { - auto p = it.field(1); - auto v = it.field(2); - - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -348,38 +1059,40 @@ void Query_signature_const(void) { void Query_signature_shared(void) { flecs::world world; - world.component(); - world.component(); + world.component().add(flecs::OnInstantiate, flecs::Inherit); + world.component().add(flecs::OnInstantiate, flecs::Inherit); - auto base = flecs::entity(world) + auto base = world.entity() .set({1, 2}); - auto e1 = flecs::entity(world) + auto e1 = world.entity() .set({10, 20}) .add(flecs::IsA, base); - auto e2 = flecs::entity(world) + auto e2 = world.entity() .set({10, 20}) .set({3, 4}); auto q = world.query_builder<>() - .expr("Position, [in] Velocity(self|up)") + .expr("Position, [in] Velocity(self|up IsA)") .build(); - q.iter([](flecs::iter&it) { - auto p = it.field(1); - auto v = it.field(2); + q.run([](flecs::iter&it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); - if (!it.is_self(2)) { - for (auto i : it) { - p[i].x += v->x; - p[i].y += v->y; + if (!it.is_self(1)) { + for (auto i : it) { + p[i].x += v->x; + p[i].y += v->y; + } + } else { + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } - } else { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - } } }); @@ -399,39 +1112,41 @@ void Query_signature_optional(void) { world.component(); flecs::component(world, "Mass"); - auto e1 = flecs::entity(world) + auto e1 = world.entity() .set({10, 20}) .set({1, 2}) .set({1}); - auto e2 = flecs::entity(world) + auto e2 = world.entity() .set({30, 40}) .set({3, 4}) .set({1}); - auto e3 = flecs::entity(world) + auto e3 = world.entity() .set({50, 60}); - auto e4 = flecs::entity(world) + auto e4 = world.entity() .set({70, 80}); auto q = world.query_builder<>().expr("Position, ?Velocity, ?Mass").build(); - q.iter([](flecs::iter& it) { - auto p = it.field(1); - auto v = it.field(2); - auto m = it.field(3); + q.run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + auto m = it.field(2); - if (it.is_set(2) && it.is_set(3)) { - for (auto i : it) { - p[i].x += v[i].x * m[i].value; - p[i].y += v[i].y * m[i].value; + if (it.is_set(1) && it.is_set(2)) { + for (auto i : it) { + p[i].x += v[i].x * m[i].value; + p[i].y += v[i].y * m[i].value; + } + } else { + for (auto i : it) { + p[i].x ++; + p[i].y ++; + } } - } else { - for (auto i : it) { - p[i].x ++; - p[i].y ++; - } } }); @@ -452,69 +1167,11 @@ void Query_signature_optional(void) { test_int(p->y, 81); } -void Query_subquery(void) { - flecs::world world; - - auto e1 = flecs::entity(world) - .set({10, 20}) - .set({1, 2}); - - auto e2 = flecs::entity(world) - .set({1, 2}); - - auto q = world.query(); - auto sq = world.query_builder().observable(q).build(); - - sq.each([](flecs::entity e, Velocity& v) { - v.x ++; - v.y ++; - }); - - const Velocity *v = e1.get(); - test_int(v->x, 2); - test_int(v->y, 3); - - v = e2.get(); - test_int(v->x, 1); - test_int(v->y, 2); -} - -void Query_subquery_w_expr(void) { - flecs::world world; - - auto e1 = flecs::entity(world) - .set({10, 20}) - .set({1, 2}); - - auto e2 = flecs::entity(world) - .set({1, 2}); - - auto q = world.query(); - auto sq = world.query_builder<>().observable(q).expr("Velocity").build(); - - sq.iter([](flecs::iter it) { - auto v = it.field(1); - - for (auto i : it) { - v[i].x ++; - v[i].y ++; - } - }); - - const Velocity *v = e1.get(); - test_int(v->x, 2); - test_int(v->y, 3); - - v = e2.get(); - test_int(v->x, 1); - test_int(v->y, 2); -} - void Query_query_single_pair(void) { flecs::world world; - flecs::entity(world).add(); - auto e2 = flecs::entity(world).add(); + world.entity().add(); + auto e2 = world.entity().add(); auto q = world.query_builder<>() .expr("(Pair, Velocity)") @@ -523,11 +1180,13 @@ void Query_query_single_pair(void) { int32_t table_count = 0; int32_t entity_count = 0; - q.iter([&](flecs::iter it) { - table_count ++; - for (auto i : it) { - test_assert(it.entity(i) == e2); - entity_count ++; + q.run([&](flecs::iter it) { + while (it.next()) { + table_count ++; + for (auto i : it) { + test_assert(it.entity(i) == e2); + entity_count ++; + } } }); @@ -587,13 +1246,16 @@ void Query_sort_by(void) { .order_by(compare_position) .build(); - q.iter([](flecs::iter it, Position *p) { - test_int(it.count(), 5); - test_int(p[0].x, 1); - test_int(p[1].x, 2); - test_int(p[2].x, 4); - test_int(p[3].x, 5); - test_int(p[4].x, 6); + q.run([](flecs::iter it) { + while (it.next()) { + auto p = it.field(0); + test_int(it.count(), 5); + test_int(p[0].x, 1); + test_int(p[1].x, 2); + test_int(p[2].x, 4); + test_int(p[3].x, 5); + test_int(p[4].x, 6); + } }); } @@ -602,7 +1264,10 @@ void Query_changed(void) { auto e = world.entity().set({1, 0}); - auto q = world.query(); + auto q = world.query_builder() + .cached() + .build(); + auto q_w = world.query(); test_bool(q.changed(), true); @@ -620,20 +1285,6 @@ void Query_changed(void) { test_bool(q.changed(), true); } -void Query_orphaned(void) { - flecs::world world; - - auto q = world.query(); - auto sq = world.query_builder().observable(q).build(); - - test_assert(!q.orphaned()); - test_assert(!sq.orphaned()); - - q.destruct(); - - test_assert(sq.orphaned()); -} - void Query_default_ctor(void) { flecs::world world; @@ -715,12 +1366,14 @@ void Query_compare_term_id(void) { auto e = world.entity().add(); auto q = world.query_builder<>() - .term() + .with() .build(); - q.iter([&](flecs::iter& it) { - test_assert(it.id(1) == it.world().id()); - test_assert(it.entity(0) == e); + q.run([&](flecs::iter& it) { + while (it.next()) { + test_assert(it.id(0) == it.world().id()); + test_assert(it.entity(0) == e); + } count ++; }); @@ -736,7 +1389,9 @@ void Query_test_no_defer_each(void) { world.entity().add().set({10}); - auto q = world.query_builder().term().build(); + auto q = world.query_builder() + .with() + .build(); q.each([](flecs::entity e, Value& v) { test_expect_abort(); @@ -755,12 +1410,16 @@ void Query_test_no_defer_iter(void) { world.entity().add().set({10}); - auto q = world.query_builder().term().build(); + auto q = world.query_builder() + .with() + .build(); - q.iter([](flecs::iter& it, Value *v) { - for (auto i : it) { - test_expect_abort(); - it.entity(i).remove(); + q.run([](flecs::iter& it) { + while (it.next()) { + for (auto i : it) { + test_expect_abort(); + it.entity(i).remove(); + } } }); @@ -773,8 +1432,8 @@ void Query_inspect_terms(void) { auto p = world.entity(); auto q = world.query_builder() - .term() - .term(flecs::ChildOf, p) + .with() + .with(flecs::ChildOf, p) .build(); test_int(3, q.field_count()); @@ -787,12 +1446,12 @@ void Query_inspect_terms(void) { t = q.term(1); test_int(t.id(), world.id()); test_int(t.oper(), flecs::And); - test_int(t.inout(), flecs::InOutDefault); + test_int(t.inout(), flecs::InOutNone); t = q.term(2); test_int(t.id(), world.pair(flecs::ChildOf, p)); test_int(t.oper(), flecs::And); - test_int(t.inout(), flecs::InOutDefault); + test_int(t.inout(), flecs::InOutNone); test_assert(t.id().second() == p); } @@ -802,25 +1461,27 @@ void Query_inspect_terms_w_each(void) { auto p = world.entity(); auto q = world.query_builder() - .term() - .term(flecs::ChildOf, p) + .with() + .with(flecs::ChildOf, p) .build(); int32_t count = 0; q.each_term([&](flecs::term& t) { if (count == 0) { test_int(t.id(), world.id()); + test_int(t.inout(), flecs::InOutDefault); } else if (count == 1) { test_int(t.id(), world.id()); + test_int(t.inout(), flecs::InOutNone); } else if (count == 2) { test_int(t.id(), world.pair(flecs::ChildOf, p)); test_assert(t.id().second() == p); + test_int(t.inout(), flecs::InOutNone); } else { test_assert(false); } test_int(t.oper(), flecs::And); - test_int(t.inout(), flecs::InOutDefault); count ++; }); @@ -828,29 +1489,13 @@ void Query_inspect_terms_w_each(void) { test_int(count, 3); } -void Query_inspect_terms_w_expr(void) { - flecs::world ecs; - - flecs::query<> f = ecs.query_builder() - .expr("(ChildOf,0)") - .build(); - - int32_t count = 0; - f.each_term([&](flecs::term &term) { - test_assert(term.id().is_pair()); - count ++; - }); - - test_int(count, 1); -} - void Query_comp_to_str(void) { flecs::world ecs; auto q = ecs.query_builder() - .term() + .with() .build(); - test_str(q.str(), "Position, Velocity"); + test_str(q.str(), "Position($this), [none] Velocity($this)"); } struct Eats { int amount; }; @@ -861,38 +1506,38 @@ void Query_pair_to_str(void) { flecs::world ecs; auto q = ecs.query_builder() - .term() - .term() + .with() + .with() .build(); - test_str(q.str(), "Position, Velocity, (Eats,Apples)"); + test_str(q.str(), "Position($this), [none] Velocity($this), [none] Eats($this,Apples)"); } void Query_oper_not_to_str(void) { flecs::world ecs; auto q = ecs.query_builder() - .term().oper(flecs::Not) + .with().oper(flecs::Not) .build(); - test_str(q.str(), "Position, !Velocity"); + test_str(q.str(), "Position($this), !Velocity($this)"); } void Query_oper_optional_to_str(void) { flecs::world ecs; auto q = ecs.query_builder() - .term().oper(flecs::Optional) + .with().oper(flecs::Optional) .build(); - test_str(q.str(), "Position, ?Velocity"); + test_str(q.str(), "Position($this), [none] ?Velocity($this)"); } void Query_oper_or_to_str(void) { flecs::world ecs; auto q = ecs.query_builder<>() - .term().oper(flecs::Or) - .term() + .with().oper(flecs::Or) + .with() .build(); - test_str(q.str(), "Position || Velocity"); + test_str(q.str(), "[none] Position($this) || Velocity($this)"); } using EatsApples = flecs::pair; @@ -936,14 +1581,17 @@ void Query_iter_pair_type(void) { auto q = ecs.query(); int count = 0; - q.iter([&](flecs::iter& it, Eats* a) { - test_int(it.count(), 1); + q.run([&](flecs::iter& it) { + while (it.next()) { + auto a = it.field(0); + test_int(it.count(), 1); - test_int(a->amount, 10); - test_assert(it.entity(0) == e1); + test_int(a->amount, 10); + test_assert(it.entity(0) == e1); - a->amount ++; - count ++; + a->amount ++; + count ++; + } }); test_int(count, 1); @@ -963,20 +1611,22 @@ void Query_term_pair_type(void) { .set({20}); auto q = ecs.query_builder<>() - .term() + .with().inout() .build(); int count = 0; - q.iter([&](flecs::iter& it) { - test_int(it.count(), 1); + q.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); - auto a = it.field(1); + auto a = it.field(0); - test_int(a->amount, 10); - test_assert(it.entity(0) == e1); + test_int(a->amount, 10); + test_assert(it.entity(0) == e1); - a->amount ++; - count ++; + a->amount ++; + count ++; + } }); test_int(count, 1); @@ -1057,8 +1707,10 @@ void Query_iter_no_comps_1_comp(void) { auto q = ecs.query(); int32_t count = 0; - q.iter([&](flecs::iter& it) { - count += it.count(); + q.run([&](flecs::iter& it) { + while (it.next()) { + count += it.count(); + } }); test_int(count, 3); @@ -1075,9 +1727,10 @@ void Query_iter_no_comps_2_comps(void) { auto q = ecs.query(); int32_t count = 0; - q.iter([&](flecs::iter& it) { - - count += it.count(); + q.run([&](flecs::iter& it) { + while (it.next()) { + count += it.count(); + } }); test_int(count, 2); @@ -1092,12 +1745,14 @@ void Query_iter_no_comps_no_comps(void) { ecs.entity().add().add(); auto q = ecs.query_builder<>() - .term() + .with() .build(); int32_t count = 0; - q.iter([&](flecs::iter& it) { - count += it.count(); + q.run([&](flecs::iter& it) { + while (it.next()) { + count += it.count(); + } }); test_int(count, 3); @@ -1145,287 +1800,60 @@ void Query_iter_pair_object(void) { auto q = ecs.query(); int32_t count = 0; - q.iter([&](flecs::iter it, Event *b_e, Event *e_e) { - for (auto i : it) { - test_assert(it.entity(i) == e1); - test_str(b_e[i].value, "Big Bang"); - test_str(e_e[i].value, "Heat Death"); - count ++; - } - }); - - test_int(count, 1); -} - -void Query_iter_query_in_system(void) { - flecs::world ecs; - - ecs.entity().add().add(); - - auto q = ecs.query(); - - int32_t count = 0; - ecs.system() - .each([&](flecs::entity e1, Position&) { - q.each([&](flecs::entity e2, Velocity&) { - count ++; - }); - }); - - ecs.progress(); - - test_int(count, 1); -} - -void Query_iter_type(void) { - flecs::world ecs; - - ecs.entity().add(); - ecs.entity().add().add(); - - auto q = ecs.query(); - - q.iter([&](flecs::iter it) { - test_assert(it.type().count() >= 1); - test_assert(it.table().has()); - }); -} - -void Query_instanced_query_w_singleton_each(void) { - flecs::world ecs; - - ecs.set({1, 2}); - - auto e1 = ecs.entity().set({10, 20}); e1.set({e1}); - auto e2 = ecs.entity().set({20, 30}); e2.set({e2}); - auto e3 = ecs.entity().set({30, 40}); e3.set({e3}); - auto e4 = ecs.entity().set({40, 50}); e4.set({e4}); - auto e5 = ecs.entity().set({50, 60}); e5.set({e5}); - - e4.add(); - e5.add(); - - auto q = ecs.query_builder() - .arg(3).singleton() - .instanced() - .build(); - - int32_t count = 0; - q.each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { - test_assert(e == s.value); - p.x += v.x; - p.y += v.y; - count ++; - }); - - test_int(count, 5); - - test_assert(e1.get([](const Position& p) { - test_int(p.x, 11); - test_int(p.y, 22); - })); - - test_assert(e2.get([](const Position& p) { - test_int(p.x, 21); - test_int(p.y, 32); - })); - - test_assert(e3.get([](const Position& p) { - test_int(p.x, 31); - test_int(p.y, 42); - })); - - test_assert(e4.get([](const Position& p) { - test_int(p.x, 41); - test_int(p.y, 52); - })); - - test_assert(e5.get([](const Position& p) { - test_int(p.x, 51); - test_int(p.y, 62); - })); -} - -void Query_instanced_query_w_base_each(void) { - flecs::world ecs; - - auto base = ecs.entity().set({1, 2}); - - auto e1 = ecs.entity().is_a(base).set({10, 20}); e1.set({e1}); - auto e2 = ecs.entity().is_a(base).set({20, 30}); e2.set({e2}); - auto e3 = ecs.entity().is_a(base).set({30, 40}); e3.set({e3}); - auto e4 = ecs.entity().is_a(base).set({40, 50}).add(); e4.set({e4}); - auto e5 = ecs.entity().is_a(base).set({50, 60}).add(); e5.set({e5}); - auto e6 = ecs.entity().set({60, 70}).set({2, 3}); e6.set({e6}); - auto e7 = ecs.entity().set({70, 80}).set({4, 5}); e7.set({e7}); - - auto q = ecs.query_builder() - .instanced() - .build(); - - int32_t count = 0; - q.each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { - test_assert(e == s.value); - p.x += v.x; - p.y += v.y; - count ++; - }); - - test_int(count, 7); - - test_assert(e1.get([](const Position& p) { - test_int(p.x, 11); - test_int(p.y, 22); - })); - - test_assert(e2.get([](const Position& p) { - test_int(p.x, 21); - test_int(p.y, 32); - })); - - test_assert(e3.get([](const Position& p) { - test_int(p.x, 31); - test_int(p.y, 42); - })); - - test_assert(e4.get([](const Position& p) { - test_int(p.x, 41); - test_int(p.y, 52); - })); - - test_assert(e5.get([](const Position& p) { - test_int(p.x, 51); - test_int(p.y, 62); - })); - - test_assert(e6.get([](const Position& p) { - test_int(p.x, 62); - test_int(p.y, 73); - })); - - test_assert(e7.get([](const Position& p) { - test_int(p.x, 74); - test_int(p.y, 85); - })); -} - -void Query_un_instanced_query_w_singleton_each(void) { - flecs::world ecs; - - ecs.set({1, 2}); - - auto e1 = ecs.entity().set({10, 20}); e1.set({e1}); - auto e2 = ecs.entity().set({20, 30}); e2.set({e2}); - auto e3 = ecs.entity().set({30, 40}); e3.set({e3}); - auto e4 = ecs.entity().set({40, 50}); e4.set({e4}); - auto e5 = ecs.entity().set({50, 60}); e5.set({e5}); - - e4.add(); - e5.add(); - - auto q = ecs.query_builder() - .arg(3).singleton() - .build(); - - int32_t count = 0; - q.each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { - test_assert(e == s.value); - p.x += v.x; - p.y += v.y; - count ++; - }); - - test_int(count, 5); - - test_assert(e1.get([](const Position& p) { - test_int(p.x, 11); - test_int(p.y, 22); - })); - - test_assert(e2.get([](const Position& p) { - test_int(p.x, 21); - test_int(p.y, 32); - })); - - test_assert(e3.get([](const Position& p) { - test_int(p.x, 31); - test_int(p.y, 42); - })); - - test_assert(e4.get([](const Position& p) { - test_int(p.x, 41); - test_int(p.y, 52); - })); - - test_assert(e5.get([](const Position& p) { - test_int(p.x, 51); - test_int(p.y, 62); - })); -} - -void Query_un_instanced_query_w_base_each(void) { - flecs::world ecs; - - auto base = ecs.entity().set({1, 2}); - - auto e1 = ecs.entity().is_a(base).set({10, 20}); e1.set({e1}); - auto e2 = ecs.entity().is_a(base).set({20, 30}); e2.set({e2}); - auto e3 = ecs.entity().is_a(base).set({30, 40}); e3.set({e3}); - auto e4 = ecs.entity().is_a(base).set({40, 50}).add(); e4.set({e4}); - auto e5 = ecs.entity().is_a(base).set({50, 60}).add(); e5.set({e5}); - auto e6 = ecs.entity().set({60, 70}).set({2, 3}); e6.set({e6}); - auto e7 = ecs.entity().set({70, 80}).set({4, 5}); e7.set({e7}); - - auto q = ecs.query_builder() - .build(); + q.run([&](flecs::iter it) { + while (it.next()) { + auto b_e = it.field(0); + auto e_e = it.field(1); - int32_t count = 0; - q.each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { - test_assert(e == s.value); - p.x += v.x; - p.y += v.y; - count ++; + for (auto i : it) { + test_assert(it.entity(i) == e1); + test_str(b_e[i].value, "Big Bang"); + test_str(e_e[i].value, "Heat Death"); + count ++; + } + } }); - test_int(count, 7); + test_int(count, 1); +} - test_assert(e1.get([](const Position& p) { - test_int(p.x, 11); - test_int(p.y, 22); - })); +void Query_iter_query_in_system(void) { + flecs::world ecs; - test_assert(e2.get([](const Position& p) { - test_int(p.x, 21); - test_int(p.y, 32); - })); + ecs.entity().add().add(); - test_assert(e3.get([](const Position& p) { - test_int(p.x, 31); - test_int(p.y, 42); - })); + auto q = ecs.query(); - test_assert(e4.get([](const Position& p) { - test_int(p.x, 41); - test_int(p.y, 52); - })); + int32_t count = 0; + ecs.system() + .each([&](flecs::entity e1, Position&) { + q.each([&](flecs::entity e2, Velocity&) { + count ++; + }); + }); - test_assert(e5.get([](const Position& p) { - test_int(p.x, 51); - test_int(p.y, 62); - })); + ecs.progress(); + + test_int(count, 1); +} - test_assert(e6.get([](const Position& p) { - test_int(p.x, 62); - test_int(p.y, 73); - })); +void Query_iter_type(void) { + flecs::world ecs; - test_assert(e7.get([](const Position& p) { - test_int(p.x, 74); - test_int(p.y, 85); - })); + ecs.entity().add(); + ecs.entity().add().add(); + + auto q = ecs.query(); + + q.run([&](flecs::iter it) { + while (it.next()) { + test_assert(it.type().count() >= 1); + test_assert(it.table().has()); + } + }); } -void Query_instanced_query_w_singleton_iter(void) { +void Query_instanced_query_w_singleton_each(void) { flecs::world ecs; ecs.set({1, 2}); @@ -1440,20 +1868,15 @@ void Query_instanced_query_w_singleton_iter(void) { e5.add(); auto q = ecs.query_builder() - .arg(3).singleton() - .instanced() + .term_at(2).singleton() .build(); int32_t count = 0; - q.iter([&](flecs::iter it, Self* s, Position* p, const Velocity* v) { - test_assert(it.count() > 1); - - for (auto i : it) { - p[i].x += v->x; - p[i].y += v->y; - test_assert(it.entity(i) == s[i].value); - count ++; - } + q.each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { + test_assert(e == s.value); + p.x += v.x; + p.y += v.y; + count ++; }); test_int(count, 5); @@ -1484,7 +1907,7 @@ void Query_instanced_query_w_singleton_iter(void) { })); } -void Query_instanced_query_w_base_iter(void) { +void Query_instanced_query_w_base_each(void) { flecs::world ecs; auto base = ecs.entity().set({1, 2}); @@ -1498,25 +1921,14 @@ void Query_instanced_query_w_base_iter(void) { auto e7 = ecs.entity().set({70, 80}).set({4, 5}); e7.set({e7}); auto q = ecs.query_builder() - .instanced() .build(); int32_t count = 0; - q.iter([&](flecs::iter it, Self* s, Position* p, const Velocity* v) { - test_assert(it.count() > 1); - - for (auto i : it) { - if (it.is_self(3)) { - p[i].x += v[i].x; - p[i].y += v[i].y; - } else { - p[i].x += v->x; - p[i].y += v->y; - } - - test_assert(it.entity(i) == s[i].value); - count ++; - } + q.each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { + test_assert(e == s.value); + p.x += v.x; + p.y += v.y; + count ++; }); test_int(count, 7); @@ -1557,7 +1969,7 @@ void Query_instanced_query_w_base_iter(void) { })); } -void Query_un_instanced_query_w_singleton_iter(void) { +void Query_instanced_query_w_singleton_iter(void) { flecs::world ecs; ecs.set({1, 2}); @@ -1572,18 +1984,24 @@ void Query_un_instanced_query_w_singleton_iter(void) { e5.add(); auto q = ecs.query_builder() - .arg(3).singleton() + .term_at(2).singleton() .build(); int32_t count = 0; - q.iter([&](flecs::iter it, Self* s, Position* p, const Velocity* v) { - test_assert(it.count() == 1); - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - test_assert(it.entity(i) == s[i].value); - count ++; + q.run([&](flecs::iter it) { + while (it.next()) { + auto s = it.field(0); + auto p = it.field(1); + auto v = it.field(2); + + test_assert(it.count() > 1); + for (auto i : it) { + p[i].x += v->x; + p[i].y += v->y; + test_assert(it.entity(i) == s[i].value); + count ++; + } } }); @@ -1615,7 +2033,7 @@ void Query_un_instanced_query_w_singleton_iter(void) { })); } -void Query_un_instanced_query_w_base_iter(void) { +void Query_instanced_query_w_base_iter(void) { flecs::world ecs; auto base = ecs.entity().set({1, 2}); @@ -1632,12 +2050,25 @@ void Query_un_instanced_query_w_base_iter(void) { .build(); int32_t count = 0; - q.iter([&](flecs::iter it, Self* s, Position* p, const Velocity* v) { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - test_assert(it.entity(i) == s[i].value); - count ++; + q.run([&](flecs::iter it) { + while (it.next()) { + auto s = it.field(0); + auto p = it.field(1); + auto v = it.field(2); + + test_assert(it.count() > 1); + for (auto i : it) { + if (it.is_self(2)) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } else { + p[i].x += v->x; + p[i].y += v->y; + } + + test_assert(it.entity(i) == s[i].value); + count ++; + } } }); @@ -1679,13 +2110,6 @@ void Query_un_instanced_query_w_base_iter(void) { })); } -void Query_default_ctor_no_assign(void) { - flecs::query<> q; - - // Make sure code compiles & works - test_assert(true); -} - void Query_query_each_from_component(void) { flecs::world w; @@ -1726,8 +2150,10 @@ void Query_query_iter_from_component(void) { test_assert(qc != nullptr); int count = 0; - qc->q.iter([&](flecs::iter& it) { // Ensure we can iterate const query - count += it.count(); + qc->q.run([&](flecs::iter& it) { // Ensure we can iterate const query + while (it.next()) { + count += it.count(); + } }); test_int(count, 2); } @@ -1740,11 +2166,14 @@ void EachFunc(flecs::entity e, Position& p) { p.y ++; } -void IterFunc(flecs::iter& it, Position* p) { +void RunFunc(flecs::iter& it) { + test_bool(true, it.next()); test_int(it.count(), 1); + auto p = it.field(0); invoked_count ++; p->x ++; p->y ++; + test_bool(false, it.next()); } void Query_query_each_w_func_ptr(void) { @@ -1770,7 +2199,7 @@ void Query_query_iter_w_func_ptr(void) { auto q = w.query(); - q.iter(&IterFunc); + q.run(&RunFunc); test_int(invoked_count, 1); @@ -1802,7 +2231,7 @@ void Query_query_iter_w_func_no_ptr(void) { auto q = w.query(); - q.iter(IterFunc); + q.run(RunFunc); test_int(invoked_count, 1); @@ -1830,43 +2259,251 @@ void Query_query_each_w_iter(void) { invoked ++; }); - test_int(invoked, 2); - - const Position *ptr = e1.get(); - test_int(ptr->x, 11); - test_int(ptr->y, 21); - - ptr = e2.get(); - test_int(ptr->x, 21); - test_int(ptr->y, 31); + test_int(invoked, 2); + + const Position *ptr = e1.get(); + test_int(ptr->x, 11); + test_int(ptr->y, 21); + + ptr = e2.get(); + test_int(ptr->x, 21); + test_int(ptr->y, 31); +} + +void Query_invalid_field_from_each_w_iter(void) { + install_test_abort(); + + flecs::world ecs; + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + auto q = ecs.query_builder() + .with().inout() + .build(); + + test_expect_abort(); + + q.each([&](flecs::iter& it, size_t index, Position& p) { + it.field(1); // not allowed from each + }); +} + +void Query_invalid_field_T_from_each_w_iter(void) { + install_test_abort(); + + flecs::world ecs; + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + auto q = ecs.query_builder() + .with().inout() + .build(); + + test_expect_abort(); + + q.each([&](flecs::iter& it, size_t index, Position& p) { + it.field(1); // not allowed from each + }); +} + +void Query_invalid_field_const_T_from_each_w_iter(void) { + install_test_abort(); + + flecs::world ecs; + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + auto q = ecs.query_builder() + .with().inout() + .build(); + + test_expect_abort(); + + q.each([&](flecs::iter& it, size_t index, Position& p) { + it.field(1); // not allowed from each + }); +} + +void Query_field_at_from_each_w_iter(void) { + flecs::world ecs; + + flecs::entity e1 = ecs.entity() + .set({10, 20}) + .set({1, 2}); + + flecs::entity e2 = ecs.entity() + .set({20, 30}) + .set({3, 4}); + + auto q = ecs.query_builder() + .with().inout() + .build(); + + int32_t count = 0; + + q.each([&](flecs::iter& it, size_t row, Position& p) { + Velocity* v = static_cast(it.field_at(1, row)); + if (it.entity(row) == e1) { + test_int(v->x, 1); + test_int(v->y, 2); + count ++; + } else if (it.entity(row) == e2) { + test_int(v->x, 3); + test_int(v->y, 4); + count ++; + } + }); + + test_int(count, 2); +} + +void Query_field_at_T_from_each_w_iter(void) { + flecs::world ecs; + + flecs::entity e1 = ecs.entity() + .set({10, 20}) + .set({1, 2}); + + flecs::entity e2 = ecs.entity() + .set({20, 30}) + .set({3, 4}); + + auto q = ecs.query_builder() + .with().inout() + .build(); + + int32_t count = 0; + + q.each([&](flecs::iter& it, size_t row, Position& p) { + Velocity& v = it.field_at(1, row); + if (it.entity(row) == e1) { + test_int(v.x, 1); + test_int(v.y, 2); + count ++; + } else if (it.entity(row) == e2) { + test_int(v.x, 3); + test_int(v.y, 4); + count ++; + } + }); + + test_int(count, 2); +} + +void Query_field_at_const_T_from_each_w_iter(void) { + flecs::world ecs; + + flecs::entity e1 = ecs.entity() + .set({10, 20}) + .set({1, 2}); + + flecs::entity e2 = ecs.entity() + .set({20, 30}) + .set({3, 4}); + + auto q = ecs.query_builder() + .with().inout() + .build(); + + int32_t count = 0; + + q.each([&](flecs::iter& it, size_t row, Position& p) { + const Velocity& v = it.field_at(1, row); + if (it.entity(row) == e1) { + test_int(v.x, 1); + test_int(v.y, 2); + count ++; + } else if (it.entity(row) == e2) { + test_int(v.x, 3); + test_int(v.y, 4); + count ++; + } + }); + + test_int(count, 2); +} + +struct VelocityDerived : public Velocity { + VelocityDerived() { } + + VelocityDerived(float _x, float _y, float _z) { + x = _x; + y = _y; + z = _z; + } + + float z; +}; + +void Query_field_at_from_each_w_iter_w_base_type(void) { + flecs::world ecs; + + flecs::entity e1 = ecs.entity() + .set({10, 20}) + .set({1, 2, 3}); + + flecs::entity e2 = ecs.entity() + .set({20, 30}) + .set({3, 4, 5}); + + auto q = ecs.query_builder() + .with().inout() + .build(); + + int32_t count = 0; + + q.each([&](flecs::iter& it, size_t row, Position& p) { + Velocity* v = static_cast(it.field_at(1, row)); + if (it.entity(row) == e1) { + test_int(v->x, 1); + test_int(v->y, 2); + count ++; + } else if (it.entity(row) == e2) { + test_int(v->x, 3); + test_int(v->y, 4); + count ++; + } + }); + + test_int(count, 2); } void Query_change_tracking(void) { flecs::world w; auto qw = w.query(); - auto qr = w.query(); + auto qr = w.query_builder() + .cached() + .build(); auto e1 = w.entity().add().set({10, 20}); w.entity().set({20, 30}); test_bool(qr.changed(), true); - qr.iter([](flecs::iter&) { }); + qr.run([](flecs::iter &it) { while (it.next()) {} }); test_bool(qr.changed(), false); int32_t count = 0, change_count = 0; - qw.iter([&](flecs::iter& it, Position* p) { - test_int(it.count(), 1); + qw.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); - count ++; + count ++; - if (it.entity(0) == e1) { - it.skip(); - return; - } + if (it.entity(0) == e1) { + it.skip(); + continue; + } - change_count ++; + change_count ++; + } }); test_int(count, 2); @@ -1877,16 +2514,18 @@ void Query_change_tracking(void) { test_bool(qr.changed(), true); - qr.iter([&](flecs::iter& it, const Position* p) { - test_int(it.count(), 1); + qr.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); - count ++; + count ++; - if (it.entity(0) == e1) { - test_bool(it.changed(), false); - } else { - test_bool(it.changed(), true); - change_count ++; + if (it.entity(0) == e1) { + test_bool(it.changed(), false); + } else { + test_bool(it.changed(), true); + change_count ++; + } } }); @@ -1901,8 +2540,8 @@ void Query_not_w_write(void) { struct B {}; auto q = ecs.query_builder() - .term() - .term().oper(flecs::Not).write() + .with() + .with().oper(flecs::Not).write() .build(); auto e = ecs.entity().add(); @@ -1925,441 +2564,709 @@ void Query_not_w_write(void) { test_int(1, count); } -void Query_get_first(void) { +void Query_instanced_nested_query_w_iter(void) { flecs::world ecs; - struct A {}; + flecs::query<> q1 = ecs.query_builder() + .with() + .with().singleton().inout() + .build(); - auto e1 = ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); + flecs::query<> q2 = ecs.query_builder() + .with() + .build(); - auto q = ecs.query(); + ecs.add(); + ecs.entity().add(); + ecs.entity().add(); + ecs.entity().add(); - auto first = q.iter().first(); - test_assert(first != 0); - test_assert(first == e1); + int32_t count = 0; + + q2.run([&](flecs::iter& it_2) { + while (it_2.next()) { + q1.iter(it_2).run([&](flecs::iter& it_1) { + while (it_1.next()) { + test_int(it_1.count(), 2); + count += it_1.count(); + } + }); + } + }); + + test_int(count, 2); } -void Query_get_first_direct(void) { +void Query_instanced_nested_query_w_entity(void) { flecs::world ecs; - struct A {}; + flecs::query<> q1 = ecs.query_builder() + .with() + .with().singleton().inout() + .build(); - auto e1 = ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); + flecs::query<> q2 = ecs.query_builder() + .with() + .build(); - auto q = ecs.query(); + ecs.add(); + ecs.entity().add(); + ecs.entity().add(); + ecs.entity().add(); - auto first = q.first(); - test_assert(first != 0); - test_assert(first == e1); + int32_t count = 0; + + q2.each([&](flecs::entity e_2) { + q1.iter(e_2).run([&](flecs::iter& it_1) { + while (it_1.next()) { + test_int(it_1.count(), 2); + count += it_1.count(); + } + }); + }); + + test_int(count, 2); } -void Query_get_count_direct(void) { +void Query_instanced_nested_query_w_world(void) { flecs::world ecs; - struct A {}; + flecs::query<> q1 = ecs.query_builder() + .with() + .with().singleton().inout() + .build(); - ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); + flecs::query<> q2 = ecs.query_builder() + .with() + .build(); - auto q = ecs.query(); + ecs.add(); + ecs.entity().add(); + ecs.entity().add(); + ecs.entity().add(); - test_int(3, q.count()); + int32_t count = 0; + + q2.run([&](flecs::iter& it_2) { + while (it_2.next()) { + q1.iter(it_2.world()).run([&](flecs::iter& it_1) { + while (it_1.next()) { + test_int(it_1.count(), 2); + count += it_1.count(); + } + }); + } + }); + + test_int(count, 2); } -void Query_get_is_true_direct(void) { +void Query_captured_query(void) { flecs::world ecs; - struct A {}; - struct B {}; + flecs::query q = ecs.query(); + flecs::entity e_1 = ecs.entity().set({10, 20}); - ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); + [=]() { + int count = 0; + q.each([&](flecs::entity e, Position& p) { + test_assert(e == e_1); + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + test_int(count, 1); + }(); +} - auto q_1 = ecs.query(); - auto q_2 = ecs.query(); +void Query_page_iter_captured_query(void) { + flecs::world ecs; - test_bool(true, q_1.is_true()); - test_bool(false, q_2.is_true()); + flecs::query q = ecs.query(); + /* flecs::entity e_1 = */ ecs.entity().set({10, 20}); + flecs::entity e_2 = ecs.entity().set({20, 30}); + /* flecs::entity e_3 = */ ecs.entity().set({10, 20}); + + [=]() { + int count = 0; + q.iter().page(1, 1).each([&](flecs::entity e, Position& p) { + test_assert(e == e_2); + test_int(p.x, 20); + test_int(p.y, 30); + count ++; + }); + test_int(count, 1); + }(); } -void Query_each_w_no_this(void) { +void Query_worker_iter_captured_query(void) { flecs::world ecs; - auto e = ecs.entity() - .set({10, 20}) - .set({1, 2}); + flecs::query q = ecs.query(); + /* flecs::entity e_1 = */ ecs.entity().set({10, 20}); + flecs::entity e_2 = ecs.entity().set({20, 30}); + /* flecs::entity e_3 = */ ecs.entity().set({10, 20}); + + [=]() { + int count = 0; + q.iter().worker(1, 3).each([&](flecs::entity e, Position& p) { + test_assert(e == e_2); + test_int(p.x, 20); + test_int(p.y, 30); + count ++; + }); + test_int(count, 1); + }(); +} + +void Query_iter_entities(void) { + flecs::world ecs; + + auto e1 = ecs.entity().set({10, 20}); + auto e2 = ecs.entity().set({10, 20}); + auto e3 = ecs.entity().set({10, 20}); + + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 3); + + auto entities = it.entities(); + test_assert(entities[0] == e1); + test_assert(entities[1] == e2); + test_assert(entities[2] == e3); + } + }); +} + +void Query_iter_get_pair_w_id(void) { + flecs::world ecs; - auto q = ecs.query_builder() - .arg(1).src(e) - .arg(2).src(e) + flecs::entity Rel = ecs.entity(); + flecs::entity Tgt = ecs.entity(); + flecs::entity e = ecs.entity().add(Rel, Tgt); + + flecs::query<> q = ecs.query_builder() + .with(Rel, flecs::Wildcard) .build(); int32_t count = 0; - q.each([&](Position& p, Velocity& v) { + q.each([&](flecs::iter& it, size_t i) { + test_bool(true, it.id(0).is_pair()); + test_assert(it.id(0).first() == Rel); + test_assert(it.id(0).second() == Tgt); + test_assert(e == it.entity(i)); count ++; - test_int(p.x, 10); - test_int(p.y, 20); - test_int(v.x, 1); - test_int(v.y, 2); }); test_int(count, 1); } -void Query_each_w_iter_no_this(void) { +void Query_query_from_entity(void) { flecs::world ecs; - auto e = ecs.entity() - .set({10, 20}) - .set({1, 2}); - - auto q = ecs.query_builder() - .arg(1).src(e) - .arg(2).src(e) + flecs::entity qe = ecs.entity(); + flecs::query q1 = ecs.query_builder(qe) .build(); + /* flecs::entity e1 = */ ecs.entity().add(); + flecs::entity e2 = ecs.entity().add().add(); + int32_t count = 0; + q1.each([&](flecs::entity e, Position&, Velocity&) { + count ++; + test_assert(e == e2); + }); + test_int(count, 1); - q.each([&](flecs::iter& it, size_t index, Position& p, Velocity& v) { + flecs::query<> q2 = ecs.query(qe); + q2.each([&](flecs::entity e) { count ++; - test_int(p.x, 10); - test_int(p.y, 20); - test_int(v.x, 1); - test_int(v.y, 2); - test_int(index, 0); - test_int(it.count(), 0); + test_assert(e == e2); }); + test_int(count, 2); +} + +void Query_query_from_entity_name(void) { + flecs::world ecs; + flecs::query q1 = ecs.query_builder("qe") + .build(); + + /* flecs::entity e1 = */ ecs.entity().add(); + flecs::entity e2 = ecs.entity().add().add(); + + int32_t count = 0; + q1.each([&](flecs::entity e, Position&, Velocity&) { + count ++; + test_assert(e == e2); + }); test_int(count, 1); -} -void Query_invalid_each_w_no_this(void) { - install_test_abort(); + flecs::query<> q2 = ecs.query("qe"); + q2.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + test_int(count, 2); +} +void Query_run_w_iter_fini(void) { flecs::world ecs; - auto e = ecs.entity() - .set({10, 20}) - .set({1, 2}); + flecs::query q = ecs.query(); - auto q = ecs.query_builder() - .arg(1).src(e) - .arg(2).src(e) - .build(); + int32_t count = 0; + q.run([&](flecs::iter& it) { + it.fini(); + count ++; + }); - test_expect_abort(); + test_int(count, 1); - q.each([&](flecs::entity e, Position& p, Velocity& v) { }); + // should be no leakage assert } -void Query_named_query(void) { +void Query_run_w_iter_fini_interrupt(void) { flecs::world ecs; - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add(); + struct Foo {}; + struct Bar {}; + struct Hello {}; - auto q = ecs.query("my_query"); + flecs::entity e1 = ecs.entity() + .set({10, 20}) + .add(); + /* flecs::entity e2 = */ ecs.entity() + .set({10, 20}) + .add(); + /* flecs::entity e3 = */ ecs.entity() + .set({10, 20}) + .add(); + + flecs::query q = ecs.query(); int32_t count = 0; - q.each([&](flecs::entity e, Position&) { - test_assert(e == e1 || e == e2); + q.run([&](flecs::iter& it) { + test_bool(true, it.next()); + test_int(1, it.count()); + test_uint(e1, it.entity(0)); + + test_bool(true, it.next()); count ++; + it.fini(); }); - test_int(count, 2); - flecs::entity qe = q.entity(); - test_assert(qe != 0); - test_str(qe.name(), "my_query"); + test_int(count, 1); +} + +void Query_run_w_iter_fini_empty(void) { + flecs::world ecs; + + flecs::query q = ecs.query(); + + int32_t count = 0; + q.run([&](flecs::iter& it) { + count ++; + it.fini(); + }); + + test_int(count, 1); } -void Query_named_scoped_query(void) { +void Query_run_w_iter_fini_no_query(void) { flecs::world ecs; - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add(); - - auto q = ecs.query("my::query"); + flecs::query<> q = ecs.query(); int32_t count = 0; - q.each([&](flecs::entity e, Position&) { - test_assert(e == e1 || e == e2); + q.run([&](flecs::iter& it) { count ++; + it.fini(); }); - test_int(count, 2); - flecs::entity qe = q.entity(); - test_assert(qe != 0); - test_str(qe.name(), "query"); - test_str(qe.path(), "::my::query"); + test_int(count, 1); } -void Query_instanced_nested_query_w_iter(void) { +void Query_add_to_match_from_staged_query(void) { flecs::world ecs; - flecs::query<> q1 = ecs.query_builder() - .term() - .term().singleton() - .build(); + ecs.component(); + ecs.component(); - flecs::query<> q2 = ecs.query_builder() - .term() - .build(); + flecs::entity e = ecs.entity().add(); - ecs.add(); - ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); + flecs::world stage = ecs.get_stage(0); - int32_t count = 0; + ecs.readonly_begin(false); - q2.iter([&](flecs::iter& it_2) { - q1.iter(it_2, [&](flecs::iter& it_1) { - test_int(it_1.count(), 1); - count += it_1.count(); + stage.query() + .each([](flecs::entity e, Position&) { + e.add(); + test_assert(!e.has()); }); - }); - test_int(count, 2); + ecs.readonly_end(); + + test_assert(e.has()); + test_assert(e.has()); } -void Query_instanced_nested_query_w_entity(void) { +void Query_add_to_match_from_staged_query_readonly_threaded(void) { flecs::world ecs; - flecs::query<> q1 = ecs.query_builder() - .term() - .term().singleton() - .build(); + ecs.component(); + ecs.component(); - flecs::query<> q2 = ecs.query_builder() - .term() - .build(); + flecs::entity e = ecs.entity().add(); - ecs.add(); - ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); + flecs::world stage = ecs.get_stage(0); - int32_t count = 0; + ecs.readonly_begin(true); - q2.each([&](flecs::entity e_2) { - q1.iter(e_2, [&](flecs::iter& it_1) { - test_int(it_1.count(), 1); - count += it_1.count(); + stage.query() + .each([](flecs::entity e, Position&) { + e.add(); + test_assert(!e.has()); }); - }); - test_int(count, 2); + ecs.readonly_end(); + + test_assert(e.has()); + test_assert(e.has()); } -void Query_instanced_nested_query_w_world(void) { - flecs::world ecs; +void Query_empty_tables_each(void) { + flecs::world world; - flecs::query<> q1 = ecs.query_builder() - .term() - .term().singleton() - .build(); + world.component(); + world.component(); + world.component(); - flecs::query<> q2 = ecs.query_builder() - .term() - .build(); + auto e1 = world.entity() + .set({10, 20}) + .set({1, 2}); - ecs.add(); - ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); + auto e2 = world.entity() + .set({20, 30}) + .set({2, 3}); - int32_t count = 0; + e2.add(); + e2.remove(); - q2.iter([&](flecs::iter& it_2) { - q1.iter(it_2.world(), [&](flecs::iter& it_1) { - test_int(it_1.count(), 1); - count += it_1.count(); - }); + auto q = world.query_builder() + .query_flags(EcsQueryMatchEmptyTables) + .build(); + + q.each([](Position& p, Velocity& v) { + p.x += v.x; + p.y += v.y; }); - test_int(count, 2); + { + const Position *p = e1.get(); + test_int(p->x, 11); + test_int(p->y, 22); + } + { + const Position *p = e2.get(); + test_int(p->x, 22); + test_int(p->y, 33); + } } -void Query_captured_query(void) { - flecs::world ecs; +void Query_empty_tables_each_w_entity(void) { + flecs::world world; - flecs::query q = ecs.query(); - flecs::entity e_1 = ecs.entity().set({10, 20}); + world.component(); + world.component(); + world.component(); - [=]() { - int count = 0; - q.each([&](flecs::entity e, Position& p) { - test_assert(e == e_1); - test_int(p.x, 10); - test_int(p.y, 20); - count ++; - }); - test_int(count, 1); - }(); + auto e1 = world.entity() + .set({10, 20}) + .set({1, 2}); + + auto e2 = world.entity() + .set({20, 30}) + .set({2, 3}); + + e2.add(); + e2.remove(); + + auto q = world.query_builder() + .query_flags(EcsQueryMatchEmptyTables) + .build(); + + q.each([](flecs::entity e, Position& p, Velocity& v) { + p.x += v.x; + p.y += v.y; + }); + + { + const Position *p = e1.get(); + test_int(p->x, 11); + test_int(p->y, 22); + } + { + const Position *p = e2.get(); + test_int(p->x, 22); + test_int(p->y, 33); + } } -void Query_page_iter_captured_query(void) { - flecs::world ecs; +void Query_empty_tables_each_w_iter(void) { + flecs::world world; - flecs::query q = ecs.query(); - /* flecs::entity e_1 = */ ecs.entity().set({10, 20}); - flecs::entity e_2 = ecs.entity().set({20, 30}); - /* flecs::entity e_3 = */ ecs.entity().set({10, 20}); + world.component(); + world.component(); + world.component(); - [=]() { - int count = 0; - q.iter().page(1, 1).each([&](flecs::entity e, Position& p) { - test_assert(e == e_2); - test_int(p.x, 20); - test_int(p.y, 30); - count ++; - }); - test_int(count, 1); - }(); + auto e1 = world.entity() + .set({10, 20}) + .set({1, 2}); + + auto e2 = world.entity() + .set({20, 30}) + .set({2, 3}); + + e2.add(); + e2.remove(); + + auto q = world.query_builder() + .query_flags(EcsQueryMatchEmptyTables) + .build(); + + q.each([](flecs::iter&, size_t, Position& p, Velocity& v) { + p.x += v.x; + p.y += v.y; + }); + + { + const Position *p = e1.get(); + test_int(p->x, 11); + test_int(p->y, 22); + } + { + const Position *p = e2.get(); + test_int(p->x, 22); + test_int(p->y, 33); + } } -void Query_worker_iter_captured_query(void) { - flecs::world ecs; +void Query_pair_with_variable_src(void) { + flecs::world world; - flecs::query q = ecs.query(); - /* flecs::entity e_1 = */ ecs.entity().set({10, 20}); - flecs::entity e_2 = ecs.entity().set({20, 30}); - /* flecs::entity e_3 = */ ecs.entity().set({10, 20}); + struct Rel {}; + struct ThisComp { int x; }; + struct OtherComp { int x; }; - [=]() { - int count = 0; - q.iter().worker(1, 3).each([&](flecs::entity e, Position& p) { - test_assert(e == e_2); - test_int(p.x, 20); - test_int(p.y, 30); - count ++; - }); - test_int(count, 1); - }(); + world.component(); + world.component(); + world.component(); + + auto other = world.entity() + .set(OtherComp{10}); + + for (int i = 0; i < 3; ++i) { + world.entity() + .set(ThisComp{i}) + .add(other); + } + + auto q = world.query_builder() + .term_at(0).second("$other") + .term_at(2).src("$other") + .build(); + + size_t isPresent = 0; + q.each([&isPresent](const Rel& rel, const ThisComp& thisComp, const OtherComp& otherComp) { + isPresent |= (1 << thisComp.x); + test_int(otherComp.x, 10); + }); + + test_int(isPresent, 7); } -void Query_iter_entities(void) { - flecs::world ecs; +void Query_pair_with_variable_src_no_row_fields(void) { + flecs::world world; - auto e1 = ecs.entity().set({10, 20}); - auto e2 = ecs.entity().set({10, 20}); - auto e3 = ecs.entity().set({10, 20}); + struct Rel { int dummy; }; // make sure this isn't a tag, so that the query contains no row field + struct ThisComp { int x; }; + struct OtherComp { int x; }; - ecs.query() - .iter([&](flecs::iter& it) { - test_int(it.count(), 3); + world.component(); + world.component(); + world.component(); - auto entities = it.entities(); - test_assert(entities[0] == e1); - test_assert(entities[1] == e2); - test_assert(entities[2] == e3); - }); + auto other = world.entity() + .set(OtherComp{0}); + + // Guarantee we don't luckily hit zero if we read the wrong component + world.entity() + .set(OtherComp{1}); + + for (int i = 0; i < 3; ++i) { + world.entity() + .set(ThisComp{i}) + .add(other); + } + + auto q = world.query_builder() + .term_at(0).second("$other") + .term_at(2).src("$other") + .build(); + + size_t isPresent = 0; + q.each([&isPresent](const Rel& rel, const ThisComp& thisComp, const OtherComp& otherComp) { + isPresent |= (1 << thisComp.x); + test_int(otherComp.x, 0); + }); + + test_int(isPresent, 7); } -void Query_iter_get_pair_w_id(void) { - flecs::world ecs; +void Query_iter_targets(void) { + flecs::world world; - flecs::entity Rel = ecs.entity(); - flecs::entity Tgt = ecs.entity(); - flecs::entity e = ecs.entity().add(Rel, Tgt); + flecs::entity Likes = world.entity(); + flecs::entity pizza = world.entity(); + flecs::entity salad = world.entity(); + flecs::entity alice = world.entity().add(Likes, pizza).add(Likes, salad); - flecs::query<> q = ecs.query_builder() - .with(Rel, flecs::Wildcard) + auto q = world.query_builder() + .with(Likes, flecs::Any) .build(); - int32_t count = 0; + int count = 0, tgt_count = 0; + + q.each([&](flecs::iter& it, size_t row) { + flecs::entity e = it.entity(row); + test_assert(e == alice); + + it.targets(0, [&](flecs::entity tgt) { + if (tgt_count == 0) { + test_assert(tgt == pizza); + } + if (tgt_count == 1) { + test_assert(tgt == salad); + } + tgt_count ++; + }); - q.each([&](flecs::iter& it, size_t i) { - test_bool(true, it.id(1).is_pair()); - test_assert(it.id(1).first() == Rel); - test_assert(it.id(1).second() == Tgt); - test_assert(e == it.entity(i)); count ++; }); test_int(count, 1); + test_int(tgt_count, 2); } -void Query_find(void) { - flecs::world ecs; +void Query_iter_targets_2nd_field(void) { + flecs::world world; - /* auto e1 = */ ecs.entity().set({10, 20}); - auto e2 = ecs.entity().set({20, 30}); + flecs::entity Likes = world.entity(); + flecs::entity pizza = world.entity(); + flecs::entity salad = world.entity(); + flecs::entity alice = world.entity() + .add() + .add(Likes, pizza).add(Likes, salad); - auto q = ecs.query(); + auto q = world.query_builder() + .with() + .with(Likes, flecs::Any) + .build(); - auto r = q.find([](Position& p) { - return p.x == 20; + int count = 0, tgt_count = 0; + + q.each([&](flecs::iter& it, size_t row) { + flecs::entity e = it.entity(row); + test_assert(e == alice); + + it.targets(1, [&](flecs::entity tgt) { + if (tgt_count == 0) { + test_assert(tgt == pizza); + } + if (tgt_count == 1) { + test_assert(tgt == salad); + } + tgt_count ++; + }); + + count ++; }); - test_assert(r == e2); + test_int(count, 1); + test_int(tgt_count, 2); } -void Query_find_not_found(void) { - flecs::world ecs; +void Query_iter_targets_field_out_of_range(void) { + install_test_abort(); - /* auto e1 = */ ecs.entity().set({10, 20}); - /* auto e2 = */ ecs.entity().set({20, 30}); + flecs::world world; - auto q = ecs.query(); + flecs::entity Likes = world.entity(); + flecs::entity pizza = world.entity(); + flecs::entity salad = world.entity(); + flecs::entity alice = world.entity().add(Likes, pizza).add(Likes, salad); - auto r = q.find([](Position& p) { - return p.x == 30; - }); + auto q = world.query_builder() + .with(Likes, flecs::Any) + .build(); - test_assert(!r); + q.each([&](flecs::iter& it, size_t row) { + flecs::entity e = it.entity(row); + test_assert(e == alice); + + test_expect_abort(); + it.targets(1, [&](flecs::entity tgt) { }); + }); } -void Query_find_w_entity(void) { - flecs::world ecs; +void Query_iter_targets_field_not_a_pair(void) { + install_test_abort(); - /* auto e1 = */ ecs.entity().set({10, 20}).set({20, 30}); - auto e2 = ecs.entity().set({20, 30}).set({20, 30}); + flecs::world world; - auto q = ecs.query(); + flecs::entity Likes = world.entity(); + flecs::entity pizza = world.entity(); + flecs::entity salad = world.entity(); + flecs::entity alice = world.entity() + .add() + .add(Likes, pizza).add(Likes, salad); - auto r = q.find([](flecs::entity e, Position& p) { - return p.x == e.get()->x && - p.y == e.get()->y; - }); + auto q = world.query_builder() + .with() + .build(); - test_assert(r == e2); + q.each([&](flecs::iter& it, size_t row) { + flecs::entity e = it.entity(row); + test_assert(e == alice); + + test_expect_abort(); + it.targets(1, [&](flecs::entity tgt) { }); + }); } -void Query_optional_pair_term(void) { - flecs::world ecs; +void Query_iter_targets_field_not_set(void) { + install_test_abort(); - ecs.entity() - .add() - .emplace(1.0f, 2.0f); - ecs.entity() - .add(); + flecs::world world; - int32_t with_pair = 0, without_pair = 0; + flecs::entity Likes = world.entity(); + flecs::entity alice = world.entity() + .add(); - auto f = ecs.query_builder*>() - .with() + auto q = world.query_builder() + .with() + .with(Likes, flecs::Any).optional() .build(); - - f.each([&](flecs::entity e, Position* p) { - if (p) { - with_pair++; - test_flt(1.0f, p->x); - test_flt(2.0f, p->y); - } else { - without_pair++; - } - }); - ecs.progress(1.0); + q.each([&](flecs::iter& it, size_t row) { + flecs::entity e = it.entity(row); + test_assert(e == alice); + test_assert(!it.is_set(1)); - test_int(1, with_pair); - test_int(1, without_pair); + test_expect_abort(); + it.targets(1, [&](flecs::entity tgt) { }); + }); } diff --git a/vendors/flecs/test/cpp/src/QueryBuilder.cpp b/vendors/flecs/test/cpp/src/QueryBuilder.cpp new file mode 100644 index 000000000..c540008f3 --- /dev/null +++ b/vendors/flecs/test/cpp/src/QueryBuilder.cpp @@ -0,0 +1,4977 @@ +#include + +static flecs::query_cache_kind_t cache_kind = flecs::QueryCacheDefault; + +void QueryBuilder_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = flecs::QueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +struct Other { + int32_t value; +}; + +enum Color { + Red, Green, Blue +}; + +void QueryBuilder_builder_assign_same_type(void) { + flecs::world ecs; + + flecs::query q = + ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p, Velocity& v) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_builder_assign_to_empty(void) { + flecs::world ecs; + + flecs::query<> q = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_builder_assign_from_empty(void) { + flecs::world ecs; + + flecs::query<> q = ecs.query_builder<>() + .cache_kind(cache_kind) + .with() + .with() + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_builder_build(void) { + flecs::world ecs; + + flecs::query q = + ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p, Velocity& v) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_builder_build_to_auto(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p, Velocity& v) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_builder_build_n_statements(void) { + flecs::world ecs; + + auto qb = ecs.query_builder<>(); + qb.with(); + qb.with(); + qb.cache_kind(cache_kind); + auto q = qb.build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_1_type(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_2_types(void) { + flecs::world ecs; + + auto e1 = ecs.entity() + .set({10, 20}) + .set({1, 2}); + + ecs.entity().set({10, 20}); + + auto r = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + int count = 0; + r.each([&](flecs::entity e, Position& p, const Velocity& v) { + count ++; + test_assert(e == e1); + test_int(p.x, 10); + test_int(p.y, 20); + + test_int(v.x, 1); + test_int(v.y, 2); + }); + + test_int(count, 1); + +} + +void QueryBuilder_id_term(void) { + flecs::world ecs; + + auto Tag = ecs.entity(); + + auto e1 = ecs.entity() + .add(Tag); + + ecs.entity().set({10, 20}); + + auto r = ecs.query_builder() + .with(Tag) + .cache_kind(cache_kind) + .build(); + + int count = 0; + r.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); + +} + +void QueryBuilder_type_term(void) { + flecs::world ecs; + + auto e1 = ecs.entity() + .set({10, 20}); + + ecs.entity().set({10, 20}); + + auto r = ecs.query_builder() + .with() + .cache_kind(cache_kind) + .build(); + + int count = 0; + r.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); + +} + +void QueryBuilder_id_pair_term(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + auto e1 = ecs.entity() + .add(Likes, Apples); + + ecs.entity() + .add(Likes, Pears); + + auto r = ecs.query_builder() + .with(Likes, Apples) + .cache_kind(cache_kind) + .build(); + + int count = 0; + r.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); + +} + +void QueryBuilder_id_pair_wildcard_term(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + auto e1 = ecs.entity() + .add(Likes, Apples); + + auto e2 = ecs.entity() + .add(Likes, Pears); + + auto r = ecs.query_builder() + .with(Likes, flecs::Wildcard) + .cache_kind(cache_kind) + .build(); + + int count = 0; + r.each([&](flecs::iter& it, size_t index) { + if (it.entity(index) == e1) { + test_assert(it.id(0) == ecs.pair(Likes, Apples)); + count ++; + } + if (it.entity(index) == e2) { + test_assert(it.id(0) == ecs.pair(Likes, Pears)); + count ++; + } + }); + test_int(count, 2); + +} + +void QueryBuilder_type_pair_term(void) { + flecs::world ecs; + + struct Likes { }; + struct Apples { }; + struct Pears { }; + + auto e1 = ecs.entity() + .add(); + + auto e2 = ecs.entity() + .add(); + + auto r = ecs.query_builder() + .with(flecs::Wildcard) + .cache_kind(cache_kind) + .build(); + + int count = 0; + r.each([&](flecs::iter& it, size_t index) { + if (it.entity(index) == e1) { + test_assert((it.id(0) == ecs.pair())); + count ++; + } + if (it.entity(index) == e2) { + test_assert((it.id(0) == ecs.pair())); + count ++; + } + }); + test_int(count, 2); + +} + +void QueryBuilder_pair_term_w_var(void) { + flecs::world ecs; + + struct Likes { }; + struct Apples { }; + struct Pears { }; + + auto e1 = ecs.entity() + .add(); + + auto e2 = ecs.entity() + .add(); + + auto r = ecs.query_builder() + .with().second().var("Food") + .cache_kind(cache_kind) + .build(); + + int food_var = r.find_var("Food"); + + int count = 0; + r.each([&](flecs::iter& it, size_t index) { + if (it.entity(index) == e1) { + test_assert((it.id(0) == ecs.pair())); + test_assert(it.get_var("Food") == ecs.id()); + test_assert(it.get_var(food_var) == ecs.id()); + count ++; + } + if (it.entity(index) == e2) { + test_assert((it.id(0) == ecs.pair())); + test_assert(it.get_var("Food") == ecs.id()); + test_assert(it.get_var(food_var) == ecs.id()); + count ++; + } + }); + test_int(count, 2); + +} + +void QueryBuilder_2_pair_terms_w_var(void) { + flecs::world ecs; + + struct Likes { }; + struct Eats { }; + struct Apples { }; + struct Pears { }; + + auto Bob = ecs.entity() + .add(); + + auto Alice = ecs.entity() + .add() + .add(Bob); + + Bob.add(Alice); + + auto r = ecs.query_builder() + .with().second().var("Food") + .with().second().var("Person") + .cache_kind(cache_kind) + .build(); + + int food_var = r.find_var("Food"); + int person_var = r.find_var("Person"); + + int count = 0; + r.each([&](flecs::iter& it, size_t index) { + if (it.entity(index) == Bob) { + test_assert((it.id(0) == ecs.pair())); + test_assert(it.get_var("Food") == ecs.id()); + test_assert(it.get_var(food_var) == ecs.id()); + + test_assert((it.id(1) == ecs.pair(Alice))); + test_assert(it.get_var("Person") == Alice); + test_assert(it.get_var(person_var) == Alice); + count ++; + } + if (it.entity(index) == Alice) { + test_assert((it.id(0) == ecs.pair())); + test_assert(it.get_var("Food") == ecs.id()); + test_assert(it.get_var(food_var) == ecs.id()); + + test_assert((it.id(1) == ecs.pair(Bob))); + test_assert(it.get_var("Person") == Bob); + test_assert(it.get_var(person_var) == Bob); + count ++; + } + }); + test_int(count, 2); + +} + +void QueryBuilder_set_var(void) { + flecs::world ecs; + + struct Likes { }; + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + ecs.entity() + .add(Apples); + + auto e2 = ecs.entity() + .add(Pears); + + auto r = ecs.query_builder() + .with().second().var("Food") + .cache_kind(cache_kind) + .build(); + + int food_var = r.find_var("Food"); + + int count = 0; + r.iter() + .set_var(food_var, Pears) + .each([&](flecs::iter& it, size_t index) { + test_assert(it.entity(index) == e2); + test_assert((it.id(0) == ecs.pair(Pears))); + test_assert(it.get_var("Food") == Pears); + test_assert(it.get_var(food_var) == Pears); + count ++; + }); + + test_int(count, 1); +} + +void QueryBuilder_set_2_vars(void) { + flecs::world ecs; + + struct Likes { }; + struct Eats { }; + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + auto Bob = ecs.entity() + .add(Apples); + + auto Alice = ecs.entity() + .add(Pears) + .add(Bob); + + Bob.add(Alice); + + auto r = ecs.query_builder() + .with().second().var("Food") + .with().second().var("Person") + .cache_kind(cache_kind) + .build(); + + int food_var = r.find_var("Food"); + int person_var = r.find_var("Person"); + + int count = 0; + r.iter() + .set_var(food_var, Pears) + .set_var(person_var, Bob) + .each([&](flecs::iter& it, size_t index) { + test_assert(it.entity(index) == Alice); + test_assert((it.id(0) == ecs.pair(Pears))); + test_assert((it.id(1) == ecs.pair(Bob))); + test_assert(it.get_var("Food") == Pears); + test_assert(it.get_var(food_var) == Pears); + test_assert(it.get_var("Person") == Bob); + test_assert(it.get_var(person_var) == Bob); + count ++; + }); + test_int(count, 1); + +} + +void QueryBuilder_set_var_by_name(void) { + flecs::world ecs; + + struct Likes { }; + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + ecs.entity() + .add(Apples); + + auto e2 = ecs.entity() + .add(Pears); + + auto r = ecs.query_builder() + .with().second().var("Food") + .cache_kind(cache_kind) + .build(); + + int count = 0; + r.iter() + .set_var("Food", Pears) + .each([&](flecs::iter& it, size_t index) { + test_assert(it.entity(index) == e2); + test_assert((it.id(0) == ecs.pair(Pears))); + count ++; + }); + test_int(count, 1); + +} + +void QueryBuilder_set_2_vars_by_name(void) { + flecs::world ecs; + + struct Likes { }; + struct Eats { }; + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + auto Bob = ecs.entity() + .add(Apples); + + auto Alice = ecs.entity() + .add(Pears) + .add(Bob); + + Bob.add(Alice); + + auto r = ecs.query_builder() + .with().second().var("Food") + .with().second().var("Person") + .cache_kind(cache_kind) + .build(); + + int food_var = r.find_var("Food"); + int person_var = r.find_var("Person"); + + int count = 0; + r.iter() + .set_var("Food", Pears) + .set_var("Person", Bob) + .each([&](flecs::iter& it, size_t index) { + test_assert(it.entity(index) == Alice); + test_assert((it.id(0) == ecs.pair(Pears))); + test_assert((it.id(1) == ecs.pair(Bob))); + test_assert(it.get_var("Food") == Pears); + test_assert(it.get_var(food_var) == Pears); + test_assert(it.get_var("Person") == Bob); + test_assert(it.get_var(person_var) == Bob); + count ++; + }); + test_int(count, 1); + +} + +void QueryBuilder_set_var_on_query(void) { + flecs::world ecs; + + struct Likes { }; + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + ecs.entity() + .add(Apples); + + auto e2 = ecs.entity() + .add(Pears); + + auto r = ecs.query_builder() + .with().second().var("Food") + .cache_kind(cache_kind) + .build(); + + int food_var = r.find_var("Food"); + + int count = 0; + r.set_var(food_var, Pears) + .each([&](flecs::iter& it, size_t index) { + test_assert(it.entity(index) == e2); + test_assert((it.id(0) == ecs.pair(Pears))); + test_assert(it.get_var("Food") == Pears); + test_assert(it.get_var(food_var) == Pears); + count ++; + }); + + test_int(count, 1); +} + +void QueryBuilder_set_var_by_name_on_query(void) { + flecs::world ecs; + + struct Likes { }; + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + ecs.entity() + .add(Apples); + + auto e2 = ecs.entity() + .add(Pears); + + auto r = ecs.query_builder() + .with().second().var("Food") + .cache_kind(cache_kind) + .build(); + + int count = 0; + r.set_var("Food", Pears) + .each([&](flecs::iter& it, size_t index) { + test_assert(it.entity(index) == e2); + test_assert((it.id(0) == ecs.pair(Pears))); + test_assert(it.get_var("Food") == Pears); + count ++; + }); + + test_int(count, 1); +} + +void QueryBuilder_set_table_var(void) { + flecs::world ecs; + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add(); + auto e3 = ecs.entity().add().add(); + + auto r = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + int count = 0; + r.set_var("this", e1.table()) + .each([&](flecs::iter& it, size_t index, Position&) { + if (index == 0) { + test_assert(it.entity(index) == e1); + } else if (index == 1) { + test_assert(it.entity(index) == e2); + } + count ++; + }); + + test_int(count, 2); + + r.set_var("this", e3.table()) + .each([&](flecs::iter& it, size_t index, Position&) { + test_assert(it.entity(index) == e3); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_set_range_var(void) { + flecs::world ecs; + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add(); + auto e3 = ecs.entity().add().add(); + + auto r = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + int count = 0; + + r.set_var("this", e1.range()) + .each([&](flecs::iter& it, size_t index, Position&) { + test_assert(it.entity(index) == e1); + count ++; + }); + + test_int(count, 1); + + r.set_var("this", e2.range()) + .each([&](flecs::iter& it, size_t index, Position&) { + test_assert(it.entity(index) == e2); + count ++; + }); + + test_int(count, 2); + + r.set_var("this", e3.range()) + .each([&](flecs::iter& it, size_t index, Position&) { + test_assert(it.entity(index) == e3); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_set_table_var_chained(void) { + flecs::world ecs; + + /* auto e1 = */ ecs.entity().add(); + /* auto e2 = */ ecs.entity().add(); + auto e3 = ecs.entity().add().add(); + /* auto e4 = */ ecs.entity().add(); + + auto q1 = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto q2 = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + int count = 0; + + q1.run([&](flecs::iter& it) { + while (it.next()) { + q2.set_var("this", it.table()).each([&](flecs::entity e, Velocity&) { + test_assert(e == e3); + count ++; + }); + } + }); + + test_int(count, 1); +} + +void QueryBuilder_set_range_var_chained(void) { + flecs::world ecs; + + /* auto e1 = */ ecs.entity().add(); + /* auto e2 = */ ecs.entity().add(); + auto e3 = ecs.entity().add().add(); + /* auto e4 = */ ecs.entity().add(); + + auto q1 = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto q2 = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + int count = 0; + + q1.run([&](flecs::iter& it) { + while (it.next()) { + q2.set_var("this", it.range()).each([&](flecs::entity e, Velocity&) { + test_assert(e == e3); + count ++; + }); + } + }); + + test_int(count, 1); +} + +void QueryBuilder_expr_w_var(void) { + flecs::world ecs; + + auto rel = ecs.entity("Rel"); + auto obj = ecs.entity(); + auto e = ecs.entity().add(rel, obj); + + auto r = ecs.query_builder() + .expr("(Rel, $X)") + .cache_kind(cache_kind) + .build(); + + int x_var = r.find_var("X"); + test_assert(x_var != -1); + + int32_t count = 0; + r.each([&](flecs::iter& it, size_t index) { + test_assert(it.entity(index) == e); + test_assert(it.pair(0).second() == obj); + count ++; + }); + + test_int(count, 1); + +} + +void QueryBuilder_add_1_type(void) { + flecs::world ecs; + + auto q = ecs.query_builder<>() + .with() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_add_2_types(void) { + flecs::world ecs; + + auto q = ecs.query_builder<>() + .with() + .with() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_add_1_type_w_1_type(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_add_2_types_w_1_type(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with() + .with() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_add_pair(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Bob = ecs.entity(); + auto Alice = ecs.entity(); + + auto q = ecs.query_builder<>() + .with(Likes, Bob) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(Likes, Bob); + ecs.entity().add(Likes, Alice); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_add_not(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with().oper(flecs::Not) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + ecs.entity().add().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_add_or(void) { + flecs::world ecs; + + auto q = ecs.query_builder<>() + .with().oper(flecs::Or) + .with() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1 || e == e2); + }); + + test_int(count, 2); +} + +void QueryBuilder_add_optional(void) { + flecs::world ecs; + + auto q = ecs.query_builder<>() + .with() + .with().oper(flecs::Optional) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add().add(); + ecs.entity().add().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1 || e == e2); + }); + + test_int(count, 2); +} + +void QueryBuilder_ptr_type(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add().add(); + ecs.entity().add().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p, Velocity* v) { + count ++; + test_assert(e == e1 || e == e2); + }); + + test_int(count, 2); +} + +void QueryBuilder_const_type(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, const Position& p) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_string_term(void) { + flecs::world ecs; + + ecs.component(); + + auto q = ecs.query_builder<>() + .expr("Position") + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_singleton_term(void) { + flecs::world ecs; + + ecs.set({10}); + + auto q = ecs.query_builder() + .with().singleton().inout() + .cache_kind(cache_kind) + .build(); + + auto + e = ecs.entity(); e.set({e}); + e = ecs.entity(); e.set({e}); + e = ecs.entity(); e.set({e}); + + int32_t count = 0; + + q.run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto o = it.field(1); + test_assert(!it.is_self(1)); + test_int(o->value, 10); + + const Other& o_ref = *o; + test_int(o_ref.value, 10); + + for (auto i : it) { + test_assert(it.entity(i) == s[i].value); + count ++; + } + } + }); + + test_int(count, 3); +} + +void QueryBuilder_isa_superset_term(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + auto q = ecs.query_builder() + .with().src().up(flecs::IsA).in() + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + + int32_t count = 0; + + q.run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto o = it.field(1); + test_assert(!it.is_self(1)); + test_int(o->value, 10); + + for (auto i : it) { + test_assert(it.entity(i) == s[i].value); + count ++; + } + } + }); + + test_int(count, 3); +} + +void QueryBuilder_isa_self_superset_term(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + auto q = ecs.query_builder() + .with().src().self().up(flecs::IsA).in() + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().set({20}); e.set({e}); + e = ecs.entity().set({20}); e.set({e}); + + int32_t count = 0; + int32_t owned_count = 0; + + q.run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto o = it.field(1); + + if (!it.is_self(1)) { + test_int(o->value, 10); + } else { + for (auto i : it) { + test_int(o[i].value, 20); + owned_count ++; + } + } + + for (auto i : it) { + test_assert(it.entity(i) == s[i].value); + count ++; + } + } + }); + + test_int(count, 5); + test_int(owned_count, 2); +} + +void QueryBuilder_childof_superset_term(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with().src().up().in() + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + + int32_t count = 0; + + q.run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto o = it.field(1); + test_assert(!it.is_self(1)); + test_int(o->value, 10); + + for (auto i : it) { + test_assert(it.entity(i) == s[i].value); + count ++; + } + } + }); + + test_int(count, 3); +} + +void QueryBuilder_childof_self_superset_term(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with().src().self().up().in() + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().set({20}); e.set({e}); + e = ecs.entity().set({20}); e.set({e}); + + int32_t count = 0; + int32_t owned_count = 0; + + q.run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto o = it.field(1); + + if (!it.is_self(1)) { + test_int(o->value, 10); + } else { + for (auto i : it) { + test_int(o[i].value, 20); + owned_count ++; + } + } + + for (auto i : it) { + test_assert(it.entity(i) == s[i].value); + count ++; + } + } + }); + + test_int(count, 5); + test_int(owned_count, 2); +} + +void QueryBuilder_isa_superset_term_w_each(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + auto q = ecs.query_builder() + .term_at(1).src().up(flecs::IsA) + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s, Other& o) { + test_assert(e == s.value); + test_int(o.value, 10); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_isa_self_superset_term_w_each(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + auto q = ecs.query_builder() + .term_at(1).src().self().up(flecs::IsA) + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().add(flecs::IsA, base); e.set({e}); + e = ecs.entity().set({10}); e.set({e}); + e = ecs.entity().set({10}); e.set({e}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s, Other& o) { + test_assert(e == s.value); + test_int(o.value, 10); + count ++; + }); + + test_int(count, 5); +} + +void QueryBuilder_childof_superset_term_w_each(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .term_at(1).src().up() + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s, Other& o) { + test_assert(e == s.value); + test_int(o.value, 10); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_childof_self_superset_term_w_each(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .term_at(1).src().self().up() + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().set({10}); e.set({e}); + e = ecs.entity().set({10}); e.set({e}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s, Other& o) { + test_assert(e == s.value); + test_int(o.value, 10); + count ++; + }); + + test_int(count, 5); +} + +void QueryBuilder_isa_superset_shortcut(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + auto q = ecs.query_builder() + .term_at(1).up(flecs::IsA) + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().is_a(base); e.set({e}); + e = ecs.entity().is_a(base); e.set({e}); + e = ecs.entity().is_a(base); e.set({e}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s, Other& o) { + test_assert(e == s.value); + test_int(o.value, 10); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_isa_superset_shortcut_w_self(void) { + flecs::world ecs; + + ecs.component().add(flecs::OnInstantiate, flecs::Inherit); + + auto q = ecs.query_builder() + .term_at(1).self().up(flecs::IsA) + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().is_a(base); e.set({e}); + e = ecs.entity().is_a(base); e.set({e}); + e = ecs.entity().is_a(base); e.set({e}); + e = ecs.entity().set({10}); e.set({e}); + e = ecs.entity().set({10}); e.set({e}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s, Other& o) { + test_assert(e == s.value); + test_int(o.value, 10); + count ++; + }); + + test_int(count, 5); +} + +void QueryBuilder_childof_superset_shortcut(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .term_at(1).up() + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s, Other& o) { + test_assert(e == s.value); + test_int(o.value, 10); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_childof_superset_shortcut_w_self(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .term_at(1).self().up() + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().child_of(base); e.set({e}); + e = ecs.entity().set({10}); e.set({e}); + e = ecs.entity().set({10}); e.set({e}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s, Other& o) { + test_assert(e == s.value); + test_int(o.value, 10); + count ++; + }); + + test_int(count, 5); +} + +void QueryBuilder_relation(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Bob = ecs.entity(); + auto Alice = ecs.entity(); + + auto q = ecs.query_builder() + .with(Likes, Bob) + .cache_kind(cache_kind) + .build(); + + auto + e = ecs.entity().add(Likes, Bob); e.set({e}); + e = ecs.entity().add(Likes, Bob); e.set({e}); + + e = ecs.entity().add(Likes, Alice); e.set({0}); + e = ecs.entity().add(Likes, Alice); e.set({0}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 2); +} + +void QueryBuilder_relation_w_object_wildcard(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Bob = ecs.entity(); + auto Alice = ecs.entity(); + + auto q = ecs.query_builder() + .with(Likes, flecs::Wildcard) + .cache_kind(cache_kind) + .build(); + + auto + e = ecs.entity().add(Likes, Bob); e.set({e}); + e = ecs.entity().add(Likes, Bob); e.set({e}); + + e = ecs.entity().add(Likes, Alice); e.set({e}); + e = ecs.entity().add(Likes, Alice); e.set({e}); + + e = ecs.entity(); e.set({0}); + e = ecs.entity(); e.set({0}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 4); +} + +void QueryBuilder_relation_w_predicate_wildcard(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Dislikes = ecs.entity(); + auto Bob = ecs.entity(); + auto Alice = ecs.entity(); + + auto q = ecs.query_builder() + .with(flecs::Wildcard, Alice) + .cache_kind(cache_kind) + .build(); + + auto + e = ecs.entity().add(Likes, Alice); e.set({e}); + e = ecs.entity().add(Dislikes, Alice); e.set({e}); + + e = ecs.entity().add(Likes, Bob); e.set({0}); + e = ecs.entity().add(Dislikes, Bob); e.set({0}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 2); +} + +void QueryBuilder_add_pair_w_rel_type(void) { + flecs::world ecs; + + struct Likes { }; + + auto Dislikes = ecs.entity(); + auto Bob = ecs.entity(); + auto Alice = ecs.entity(); + + auto q = ecs.query_builder() + .with(flecs::Wildcard) + .cache_kind(cache_kind) + .build(); + + auto + e = ecs.entity().add(Alice); e.set({e}); + e = ecs.entity().add(Dislikes, Alice); e.set({0}); + + e = ecs.entity().add(Bob); e.set({e}); + e = ecs.entity().add(Dislikes, Bob); e.set({0}); + + int32_t count = 0; + + q.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 2); +} + +void QueryBuilder_template_term(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with>() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add>(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_explicit_subject_w_id(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with().id(flecs::This) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_explicit_subject_w_type(void) { + flecs::world ecs; + + ecs.set({10, 20}); + + auto q = ecs.query_builder() + .with().src() + .cache_kind(cache_kind) + .build(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + test_assert(e == ecs.singleton()); + }); + + test_int(count, 1); +} + +void QueryBuilder_explicit_object_w_id(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Alice = ecs.entity(); + auto Bob = ecs.entity(); + + auto q = ecs.query_builder<>() + .with(Likes).second(Alice) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(Likes, Alice); + ecs.entity().add(Likes, Bob); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_explicit_object_w_type(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + struct Alice { }; + auto Bob = ecs.entity(); + + auto q = ecs.query_builder<>() + .with(Likes).second() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(Likes, ecs.id()); + ecs.entity().add(Likes, Bob); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_explicit_term(void) { + flecs::world ecs; + + auto q = ecs.query_builder<>() + .with(ecs.term(ecs.id())) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_explicit_term_w_type(void) { + flecs::world ecs; + + auto q = ecs.query_builder<>() + .with(ecs.term()) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_explicit_term_w_pair_type(void) { + flecs::world ecs; + + struct Likes { }; + struct Alice { }; + struct Bob { }; + + auto q = ecs.query_builder<>() + .with(ecs.term()) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_explicit_term_w_id(void) { + flecs::world ecs; + + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + auto q = ecs.query_builder<>() + .with(ecs.term(Apples)) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(Apples); + ecs.entity().add(Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_explicit_term_w_pair_id(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Apples = ecs.entity(); + auto Pears = ecs.entity(); + + auto q = ecs.query_builder<>() + .with(ecs.term(Likes, Apples)) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(Likes, Apples); + ecs.entity().add(Likes, Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_1_term_to_empty(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Apples = ecs.entity(); + + auto qb = ecs.query_builder<>() + .with() + .cache_kind(cache_kind); + + qb.with(Likes, Apples); + + auto q = qb.build(); + + test_int(q.field_count(), 2); + test_int(q.term(0).id(), ecs.id()); + test_int(q.term(1).id(), ecs.pair(Likes, Apples)); +} + +void QueryBuilder_2_subsequent_args(void) { + flecs::world ecs; + + struct Rel { int foo; }; + + int32_t count = 0; + + auto s = ecs.system() + .term_at(0).second(flecs::Wildcard) + .term_at(1).singleton() + .run([&](flecs::iter it){ + while (it.next()) { + count += it.count(); + } + }); + + ecs.entity().add(); + ecs.set({}); + + s.run(); + + test_int(count, 1); +} + +void QueryBuilder_optional_tag_is_set(void) { + flecs::world ecs; + + struct TagA { }; + struct TagB { }; + + auto q = ecs.query_builder() + .with() + .with().oper(flecs::Optional) + .cache_kind(cache_kind) + .build(); + + auto e_1 = ecs.entity().add().add(); + auto e_2 = ecs.entity().add(); + + int count = 0; + + q.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + + count += it.count(); + + if (it.entity(0) == e_1) { + test_bool(it.is_set(0), true); + test_bool(it.is_set(1), true); + } else { + test_assert(it.entity(0) == e_2); + test_bool(it.is_set(0), true); + test_bool(it.is_set(1), false); + } + } + }); + + test_int(count, 2); +} + +void QueryBuilder_10_terms(void) { + flecs::world ecs; + + auto f = ecs.query_builder<>() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .cache_kind(cache_kind) + .build(); + + test_int(f.field_count(), 10); + + auto e = ecs.entity() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add(); + + int count = 0; + f.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + test_assert(it.entity(0) == e); + test_int(it.field_count(), 10); + count ++; + } + }); + + test_int(count, 1); +} + +void QueryBuilder_16_terms(void) { + flecs::world ecs; + + auto f = ecs.query_builder<>() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .cache_kind(cache_kind) + .build(); + + test_int(f.field_count(), 16); + + auto e = ecs.entity() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add(); + + int count = 0; + f.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + test_assert(it.entity(0) == e); + test_int(it.field_count(), 16); + count ++; + } + }); + + test_int(count, 1); +} + +void QueryBuilder_32_terms(void) { + flecs::world ecs; + + auto f = ecs.query_builder<>() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .cache_kind(cache_kind) + .build(); + + test_int(f.field_count(), 32); + + auto e = ecs.entity() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add(); + + int count = 0; + f.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + test_assert(it.entity(0) == e); + test_int(it.field_count(), 32); + count ++; + } + }); + + test_int(count, 1); +} + +void QueryBuilder_33_terms(void) { + install_test_abort(); + + flecs::world ecs; + + test_expect_abort(); + auto f = ecs.query_builder<>() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .cache_kind(cache_kind) + .build(); +} + +uint64_t group_by_first_id( + flecs::world_t *m_world, + flecs::table_t *m_table, + flecs::entity_t id, + void *ctx) +{ + const flecs::type_t *type = ecs_table_get_type(m_table); + return type->array[0]; +} + +uint64_t group_by_first_id_negated( + flecs::world_t *m_world, + flecs::table_t *m_table, + flecs::entity_t id, + void *ctx) +{ + return ~group_by_first_id(m_world, m_table, id, ctx); +} + +void QueryBuilder_group_by_raw(void) { + flecs::world ecs; + + struct TagA { }; + struct TagB { }; + struct TagC { }; + struct TagX { }; + + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + + auto q = ecs.query_builder() + .with() + .group_by(flecs::type_id(), group_by_first_id) + .build(); + + auto q_reverse = ecs.query_builder() + .with() + .group_by(flecs::type_id(), group_by_first_id_negated) + .build(); + + auto e3 = ecs.entity().add().add(); + auto e2 = ecs.entity().add().add(); + auto e1 = ecs.entity().add().add(); + + int count = 0; + + q.run([&](flecs::iter& it){ + while (it.next()) { + test_int(it.count(), 1); + if(count == 0){ + test_bool(it.entity(0) == e1, true); + }else if(count == 1){ + test_bool(it.entity(0) == e2, true); + }else if(count == 2){ + test_bool(it.entity(0) == e3, true); + }else{ + test_assert(false); + } + count++; + } + }); + test_int(count, 3); + + count = 0; + q_reverse.run([&](flecs::iter& it){ + while (it.next()) { + test_int(it.count(), 1); + if(count == 0){ + test_bool(it.entity(0) == e3, true); + }else if(count == 1){ + test_bool(it.entity(0) == e2, true); + }else if(count == 2){ + test_bool(it.entity(0) == e1, true); + }else{ + test_assert(false); + } + count++; + } + }); + test_int(count, 3); +} + +void QueryBuilder_group_by_template(void) { + flecs::world ecs; + + struct TagA { }; + struct TagB { }; + struct TagC { }; + struct TagX { }; + + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + + auto q = ecs.query_builder() + .with() + .group_by(group_by_first_id) + .build(); + + auto q_reverse = ecs.query_builder() + .with() + .group_by( group_by_first_id_negated) + .build(); + + auto e3 = ecs.entity().add().add(); + auto e2 = ecs.entity().add().add(); + auto e1 = ecs.entity().add().add(); + + int count = 0; + + q.run([&](flecs::iter& it){ + while (it.next()) { + test_int(it.count(), 1); + if(count == 0){ + test_bool(it.entity(0) == e1, true); + }else if(count == 1){ + test_bool(it.entity(0) == e2, true); + }else if(count == 2){ + test_bool(it.entity(0) == e3, true); + }else{ + test_assert(false); + } + count++; + } + }); + test_int(count, 3); + + count = 0; + q_reverse.run([&](flecs::iter& it){ + while (it.next()) { + test_int(it.count(), 1); + if(count == 0){ + test_bool(it.entity(0) == e3, true); + }else if(count == 1){ + test_bool(it.entity(0) == e2, true); + }else if(count == 2){ + test_bool(it.entity(0) == e1, true); + }else{ + test_assert(false); + } + count++; + } + }); + test_int(count, 3); +} + +static +uint64_t group_by_rel(flecs::world_t *world, flecs::table_t *table, flecs::entity_t id, void *ctx) { + ecs_id_t match; + if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &match) != -1) { + return ECS_PAIR_SECOND(match); + } + return 0; +} + +void QueryBuilder_group_by_iter_one(void) { + flecs::world ecs; + + auto Rel = ecs.entity(); + auto TgtA = ecs.entity(); + auto TgtB = ecs.entity(); + auto TgtC = ecs.entity(); + auto Tag = ecs.entity(); + + ecs.entity().add(Rel, TgtA); + auto e2 = ecs.entity().add(Rel, TgtB); + ecs.entity().add(Rel, TgtC); + + ecs.entity().add(Rel, TgtA).add(Tag); + auto e5 = ecs.entity().add(Rel, TgtB).add(Tag); + ecs.entity().add(Rel, TgtC).add(Tag); + + auto q = ecs.query_builder() + .with(Rel, flecs::Wildcard) + .group_by(Rel, group_by_rel) + .build(); + + bool e2_found = false; + bool e5_found = false; + int32_t count = 0; + + q.iter().set_group(TgtB).each([&](flecs::iter& it, size_t i) { + flecs::entity e = it.entity(i); + test_assert(it.group_id() == TgtB); + + if (e == e2) e2_found = true; + if (e == e5) e5_found = true; + count ++; + }); + + test_int(2, count); + test_bool(true, e2_found); + test_bool(true, e5_found); +} + +void QueryBuilder_group_by_iter_one_template(void) { + flecs::world ecs; + + struct Rel { }; + struct TgtA { }; + struct TgtB { }; + struct TgtC { }; + struct Tag { }; + + ecs.entity().add(); + auto e2 = ecs.entity().add(); + ecs.entity().add(); + + ecs.entity().add().add(); + auto e5 = ecs.entity().add().add(); + ecs.entity().add().add(); + + auto q = ecs.query_builder() + .with(flecs::Wildcard) + .group_by(group_by_rel) + .build(); + + bool e2_found = false; + bool e5_found = false; + int32_t count = 0; + + q.iter().set_group().each([&](flecs::iter& it, size_t i) { + flecs::entity e = it.entity(i); + test_assert(it.group_id() == ecs.id()); + + if (e == e2) e2_found = true; + if (e == e5) e5_found = true; + count ++; + }); + + test_int(2, count); + test_bool(true, e2_found); + test_bool(true, e5_found); +} + +void QueryBuilder_group_by_iter_one_all_groups(void) { + flecs::world ecs; + + auto Rel = ecs.entity(); + auto TgtA = ecs.entity(); + auto TgtB = ecs.entity(); + auto TgtC = ecs.entity(); + auto Tag = ecs.entity(); + + auto e1 = ecs.entity().add(Rel, TgtA); + auto e2 = ecs.entity().add(Rel, TgtB); + auto e3 = ecs.entity().add(Rel, TgtC); + + auto e4 = ecs.entity().add(Rel, TgtA).add(Tag); + auto e5 = ecs.entity().add(Rel, TgtB).add(Tag); + auto e6 = ecs.entity().add(Rel, TgtC).add(Tag); + + auto q = ecs.query_builder() + .with(Rel, flecs::Wildcard) + .group_by(Rel, group_by_rel) + .build(); + + int e1_found = 0; + int e2_found = 0; + int e3_found = 0; + int e4_found = 0; + int e5_found = 0; + int e6_found = 0; + int32_t count = 0; + uint64_t group_id = 0; + + const auto func = [&](flecs::iter& it, size_t i) { + flecs::entity e = it.entity(i); + test_assert(it.group_id() == group_id); + if (e == e1) e1_found ++; + if (e == e2) e2_found ++; + if (e == e3) e3_found ++; + if (e == e4) e4_found ++; + if (e == e5) e5_found ++; + if (e == e6) e6_found ++; + count ++; + }; + + group_id = TgtB; + q.iter().set_group(TgtB).each(func); + test_int(2, count); + test_int(1, e2_found); + test_int(1, e5_found); + + group_id = TgtA; + q.iter().set_group(TgtA).each(func); + test_int(4, count); + test_int(1, e1_found); + test_int(1, e4_found); + + group_id = TgtC; + q.iter().set_group(TgtC).each(func); + test_int(6, count); + test_int(1, e3_found); + test_int(1, e6_found); + + test_int(1, e1_found); + test_int(1, e2_found); + test_int(1, e3_found); + test_int(1, e4_found); + test_int(1, e5_found); + test_int(1, e6_found); +} + +void QueryBuilder_group_by_default_func_w_id(void) { + flecs::world ecs; + + auto Rel = ecs.entity(); + auto TgtA = ecs.entity(); + auto TgtB = ecs.entity(); + auto TgtC = ecs.entity(); + + auto e1 = ecs.entity().add(Rel, TgtC); + auto e2 = ecs.entity().add(Rel, TgtB); + auto e3 = ecs.entity().add(Rel, TgtA); + + auto q = ecs.query_builder() + .with(Rel, flecs::Wildcard) + .group_by(Rel) + .build(); + + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + int32_t count = 0; + + q.each([&](flecs::iter& it, size_t i) { + flecs::entity e = it.entity(i); + if (e == e1) { + test_assert(it.group_id() == TgtC); + test_assert(!e1_found); + test_assert(e2_found); + test_assert(e3_found); + e1_found = true; + } + if (e == e2) { + test_assert(it.group_id() == TgtB); + test_assert(!e1_found); + test_assert(!e2_found); + test_assert(e3_found); + e2_found = true; + } + if (e == e3) { + test_assert(it.group_id() == TgtA); + test_assert(!e1_found); + test_assert(!e2_found); + test_assert(!e3_found); + e3_found = true; + } + count ++; + }); + + test_int(3, count); + test_bool(true, e1_found); + test_bool(true, e2_found); + test_bool(true, e3_found); +} + +void QueryBuilder_group_by_default_func_w_type(void) { + flecs::world ecs; + + struct Rel { }; + auto TgtA = ecs.entity(); + auto TgtB = ecs.entity(); + auto TgtC = ecs.entity(); + + auto e1 = ecs.entity().add(TgtC); + auto e2 = ecs.entity().add(TgtB); + auto e3 = ecs.entity().add(TgtA); + + auto q = ecs.query_builder() + .with(flecs::Wildcard) + .group_by() + .build(); + + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + int32_t count = 0; + + q.each([&](flecs::iter& it, size_t i) { + flecs::entity e = it.entity(i); + if (e == e1) { + test_assert(it.group_id() == TgtC); + test_assert(!e1_found); + test_assert(e2_found); + test_assert(e3_found); + e1_found = true; + } + if (e == e2) { + test_assert(it.group_id() == TgtB); + test_assert(!e1_found); + test_assert(!e2_found); + test_assert(e3_found); + e2_found = true; + } + if (e == e3) { + test_assert(it.group_id() == TgtA); + test_assert(!e1_found); + test_assert(!e2_found); + test_assert(!e3_found); + e3_found = true; + } + count ++; + }); + + test_int(3, count); + test_bool(true, e1_found); + test_bool(true, e2_found); + test_bool(true, e3_found); +} + +static int group_by_ctx = 0; + +void QueryBuilder_group_by_callbacks(void) { + flecs::world ecs; + + struct Rel { }; + auto TgtA = ecs.entity(); + auto TgtB = ecs.entity(); + auto TgtC = ecs.entity(); + + auto e1 = ecs.entity().add(TgtC); + auto e2 = ecs.entity().add(TgtB); + auto e3 = ecs.entity().add(TgtA); + + auto q = ecs.query_builder() + .with(flecs::Wildcard) + .group_by() + .group_by_ctx(&group_by_ctx) + .on_group_create( + [](flecs::world_t *world, uint64_t id, void *group_by_arg) { + test_assert(world != nullptr); + test_assert(id != 0); + test_assert(group_by_arg != nullptr); + test_assert(group_by_arg == &group_by_ctx); + uint64_t *ctx = ecs_os_malloc_t(uint64_t); + *ctx = id; + return (void*)ctx; + }) + .on_group_delete( + [](flecs::world_t *world, uint64_t id, void *ctx, void *group_by_arg) { + test_assert(world != nullptr); + test_assert(id != 0); + test_assert(group_by_arg != nullptr); + test_assert(group_by_arg == &group_by_ctx); + test_assert(ctx != NULL); + test_uint(*(uint64_t*)ctx, id); + ecs_os_free(ctx); + }) + .build(); + + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + int32_t count = 0; + + q.each([&](flecs::iter& it, size_t i) { + flecs::entity e = it.entity(i); + if (e == e1) { + test_assert(it.group_id() == TgtC); + test_assert(!e1_found); + test_assert(e2_found); + test_assert(e3_found); + e1_found = true; + uint64_t *ctx = (uint64_t*)q.group_ctx(it.group_id()); + test_uint(*ctx, it.group_id()); + } + if (e == e2) { + test_assert(it.group_id() == TgtB); + test_assert(!e1_found); + test_assert(!e2_found); + test_assert(e3_found); + e2_found = true; + uint64_t *ctx = (uint64_t*)q.group_ctx(it.group_id()); + test_uint(*ctx, it.group_id()); + } + if (e == e3) { + test_assert(it.group_id() == TgtA); + test_assert(!e1_found); + test_assert(!e2_found); + test_assert(!e3_found); + e3_found = true; + uint64_t *ctx = (uint64_t*)q.group_ctx(it.group_id()); + test_uint(*ctx, it.group_id()); + } + count ++; + }); + + test_int(3, count); + test_bool(true, e1_found); + test_bool(true, e2_found); + test_bool(true, e3_found); +} + +void QueryBuilder_set_group_on_query(void) { + flecs::world ecs; + + auto Rel = ecs.entity(); + auto TgtA = ecs.entity(); + auto TgtB = ecs.entity(); + auto TgtC = ecs.entity(); + auto Tag = ecs.entity(); + + ecs.entity().add(Rel, TgtA); + auto e2 = ecs.entity().add(Rel, TgtB); + ecs.entity().add(Rel, TgtC); + + ecs.entity().add(Rel, TgtA).add(Tag); + auto e5 = ecs.entity().add(Rel, TgtB).add(Tag); + ecs.entity().add(Rel, TgtC).add(Tag); + + auto q = ecs.query_builder() + .with(Rel, flecs::Wildcard) + .group_by(Rel, group_by_rel) + .build(); + + bool e2_found = false; + bool e5_found = false; + int32_t count = 0; + + q.set_group(TgtB).each([&](flecs::iter& it, size_t i) { + flecs::entity e = it.entity(i); + test_assert(it.group_id() == TgtB); + + if (e == e2) e2_found = true; + if (e == e5) e5_found = true; + count ++; + }); + + test_int(2, count); + test_bool(true, e2_found); + test_bool(true, e5_found); +} + +void QueryBuilder_set_group_type_on_query(void) { + flecs::world ecs; + + struct Rel { }; + struct TgtA { }; + struct TgtB { }; + struct TgtC { }; + + auto Tag = ecs.entity(); + + ecs.entity().add(); + auto e2 = ecs.entity().add(); + ecs.entity().add(); + + ecs.entity().add().add(Tag); + auto e5 = ecs.entity().add().add(Tag); + ecs.entity().add().add(Tag); + + auto q = ecs.query_builder() + .with(flecs::Wildcard) + .group_by(group_by_rel) + .build(); + + bool e2_found = false; + bool e5_found = false; + int32_t count = 0; + + q.set_group().each([&](flecs::iter& it, size_t i) { + flecs::entity e = it.entity(i); + test_assert(it.group_id() == ecs.id()); + + if (e == e2) e2_found = true; + if (e == e5) e5_found = true; + count ++; + }); + + test_int(2, count); + test_bool(true, e2_found); + test_bool(true, e5_found); +} + +void QueryBuilder_create_w_no_template_args(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_any_wildcard(void) { + flecs::world ecs; + + auto Likes = ecs.entity(); + auto Apple = ecs.entity(); + auto Mango = ecs.entity(); + + auto e1 = ecs.entity() + .add(Likes, Apple) + .add(Likes, Mango); + + auto q = ecs.query_builder() + .with(Likes, flecs::Any) + .cache_kind(cache_kind) + .build(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_cascade(void) { + flecs::world ecs; + + auto Tag = ecs.entity().add(flecs::OnInstantiate, flecs::Inherit); + auto Foo = ecs.entity(); + auto Bar = ecs.entity(); + + auto e0 = ecs.entity().add(Tag); + auto e1 = ecs.entity().is_a(e0); + auto e2 = ecs.entity().is_a(e1); + auto e3 = ecs.entity().is_a(e2); + + auto q = ecs.query_builder() + .with(Tag).cascade(flecs::IsA) + .cached() + .build(); + + e1.add(Bar); + e2.add(Foo); + + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + + if (e == e1) { + test_bool(e1_found, false); + test_bool(e2_found, false); + test_bool(e3_found, false); + e1_found = true; + } + if (e == e2) { + test_bool(e1_found, true); + test_bool(e2_found, false); + test_bool(e3_found, false); + e2_found = true; + } + if (e == e3) { + test_bool(e1_found, true); + test_bool(e2_found, true); + test_bool(e3_found, false); + e3_found = true; + } + }); + + test_bool(e1_found, true); + test_bool(e2_found, true); + test_bool(e3_found, true); + test_int(count, 3); +} + +void QueryBuilder_cascade_desc(void) { + flecs::world ecs; + + auto Tag = ecs.entity().add(flecs::OnInstantiate, flecs::Inherit); + auto Foo = ecs.entity(); + auto Bar = ecs.entity(); + + auto e0 = ecs.entity().add(Tag); + auto e1 = ecs.entity().is_a(e0); + auto e2 = ecs.entity().is_a(e1); + auto e3 = ecs.entity().is_a(e2); + + auto q = ecs.query_builder() + .with(Tag).cascade(flecs::IsA).desc() + .cached() + .build(); + + e1.add(Bar); + e2.add(Foo); + + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + + if (e == e1) { + test_bool(e1_found, false); + test_bool(e2_found, true); + test_bool(e3_found, true); + e1_found = true; + } + if (e == e2) { + test_bool(e1_found, false); + test_bool(e2_found, false); + test_bool(e3_found, true); + e2_found = true; + } + if (e == e3) { + test_bool(e1_found, false); + test_bool(e2_found, false); + test_bool(e3_found, false); + e3_found = true; + } + }); + + test_bool(e1_found, true); + test_bool(e2_found, true); + test_bool(e3_found, true); + test_int(count, 3); +} + +void QueryBuilder_cascade_w_relationship(void) { + flecs::world ecs; + + auto Tag = ecs.entity(); + auto Foo = ecs.entity(); + auto Bar = ecs.entity(); + + auto e0 = ecs.entity().add(Tag); + auto e1 = ecs.entity().child_of(e0); + auto e2 = ecs.entity().child_of(e1); + auto e3 = ecs.entity().child_of(e2); + + auto q = ecs.query_builder() + .with(Tag).cascade(flecs::ChildOf) + .cached() + .build(); + + e1.add(Bar); + e2.add(Foo); + + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + + if (e == e1) { + test_bool(e1_found, false); + test_bool(e2_found, false); + test_bool(e3_found, false); + e1_found = true; + } + if (e == e2) { + test_bool(e1_found, true); + test_bool(e2_found, false); + test_bool(e3_found, false); + e2_found = true; + } + if (e == e3) { + test_bool(e1_found, true); + test_bool(e2_found, true); + test_bool(e3_found, false); + e3_found = true; + } + }); + + test_bool(e1_found, true); + test_bool(e2_found, true); + test_bool(e3_found, true); + test_int(count, 3); +} + +void QueryBuilder_up_w_type(void) { + flecs::world ecs; + + struct Rel { }; + + ecs.component().add(flecs::Traversable); + + auto q = ecs.query_builder() + .with().src().up().in() + .cache_kind(cache_kind) + .build(); + + auto base = ecs.entity().set({10}); + + auto + e = ecs.entity().add(base); e.set({e}); + e = ecs.entity().add(base); e.set({e}); + e = ecs.entity().add(base); e.set({e}); + + int32_t count = 0; + + q.run([&](flecs::iter& it) { + while (it.next()) { + auto s = it.field(0); + auto o = it.field(1); + test_assert(!it.is_self(1)); + test_int(o->value, 10); + + for (auto i : it) { + test_assert(it.entity(i) == s[i].value); + count ++; + } + } + }); + + test_int(count, 3); +} + +void QueryBuilder_cascade_w_type(void) { + flecs::world ecs; + + struct Rel { }; + + ecs.component().add(flecs::Traversable); + + auto Tag = ecs.entity(); + auto Foo = ecs.entity(); + auto Bar = ecs.entity(); + + auto e0 = ecs.entity().add(Tag); + auto e1 = ecs.entity().add(e0); + auto e2 = ecs.entity().add(e1); + auto e3 = ecs.entity().add(e2); + + auto q = ecs.query_builder() + .with(Tag).cascade() + .cached() + .build(); + + e1.add(Bar); + e2.add(Foo); + + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + + if (e == e1) { + test_bool(e1_found, false); + test_bool(e2_found, false); + test_bool(e3_found, false); + e1_found = true; + } + if (e == e2) { + test_bool(e1_found, true); + test_bool(e2_found, false); + test_bool(e3_found, false); + e2_found = true; + } + if (e == e3) { + test_bool(e1_found, true); + test_bool(e2_found, true); + test_bool(e3_found, false); + e3_found = true; + } + }); + + test_bool(e1_found, true); + test_bool(e2_found, true); + test_bool(e3_found, true); + test_int(count, 3); +} + +void QueryBuilder_named_query(void) { + flecs::world ecs; + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add(); + + auto q = ecs.query_builder("my_query") + .with() + .cache_kind(cache_kind) + .build(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + test_assert(e == e1 || e == e2); + count ++; + }); + test_int(count, 2); + + flecs::entity qe = q.entity(); + test_assert(qe != 0); + test_str(qe.name(), "my_query"); +} + +void QueryBuilder_term_w_write(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with() + .with().write() + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(0).inout() == flecs::InOutNone); + test_assert(q.term(0).get_src() == flecs::This); + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_term_w_read(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .with() + .with().read() + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(0).inout() == flecs::InOutNone); + test_assert(q.term(0).get_src() == flecs::This); + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_iter_w_stage(void) { + flecs::world ecs; + + ecs.set_stage_count(2); + flecs::world stage = ecs.get_stage(1); + + auto e1 = ecs.entity().add(); + + auto q = ecs.query(); + + int32_t count = 0; + q.iter(stage).each([&](flecs::iter& it, size_t i, Position&) { + test_assert(it.world() == stage); + test_assert(it.entity(i) == e1); + count ++; + }); + + test_int(count, 1); +} + +template +struct QueryWrapper +{ + QueryWrapper(flecs::query f) : f_(f) {} + flecs::query f_; +}; + +void QueryBuilder_builder_force_assign_operator(void) { + flecs::world ecs; + + auto e1 = ecs.entity().set({10, 20}); + + auto f = ecs.entity().emplace>( + ecs.query_builder() + .with() + .cache_kind(cache_kind) + .build() + ); + + int32_t count = 0; + f.get>()->f_.each([&](flecs::entity e) { + test_assert(e == e1); + count ++; + }); +} + +static +int query_arg(flecs::query f) { + int32_t count = 0; + + f.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + return count; +} + +void QueryBuilder_query_as_arg(void) { + flecs::world ecs; + + auto f = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + test_int(query_arg(f), 3); +} + +static +int query_move_arg(flecs::query&& f) { + int32_t count = 0; + + f.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + return count; +} + +void QueryBuilder_query_as_move_arg(void) { + flecs::world ecs; + + auto f = ecs.query(); + + auto e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + test_int(query_move_arg(ecs.query()), 3); +} + +static +flecs::query query_return(flecs::world& ecs) { + return ecs.query(); +} + +void QueryBuilder_query_as_return(void) { + flecs::world ecs; + + auto e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + auto f = query_return(ecs); + + int32_t count = 0; + + f.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_query_copy(void) { + flecs::world ecs; + + auto e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + auto f = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto f_2 = f; + + int32_t count = 0; + + f_2.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_world_each_query_1_component(void) { + flecs::world ecs; + + auto e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + int32_t count = 0; + + ecs.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_world_each_query_2_components(void) { + flecs::world ecs; + + auto e = ecs.entity(); + e.set({e}) + .set({10, 20}); + + e = ecs.entity(); + e.set({e}) + .set({10, 20}); + + e = ecs.entity(); + e.set({e}) + .set({10, 20}); + + int32_t count = 0; + + ecs.each([&](flecs::entity e, Self& s, Position& p) { + test_assert(e == s.value); + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_world_each_query_1_component_no_entity(void) { + flecs::world ecs; + + ecs.entity() + .set({10, 20}); + + ecs.entity() + .set({10, 20}); + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + int32_t count = 0; + + ecs.each([&](Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_world_each_query_2_components_no_entity(void) { + flecs::world ecs; + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + ecs.entity() + .set({3, 5}); + + ecs.entity() + .set({20, 40}); + + int32_t count = 0; + + ecs.each([&](Position& p, Velocity& v) { + test_int(p.x, 10); + test_int(p.y, 20); + test_int(v.x, 1); + test_int(v.y, 2); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_term_after_arg(void) { + flecs::world ecs; + + auto e_1 = ecs.entity() + .add() + .add() + .add(); + + ecs.entity() + .add() + .add(); + + auto f = ecs.query_builder() + .term_at(0).src(flecs::This) // dummy + .with() + .cache_kind(cache_kind) + .build(); + + test_int(f.field_count(), 3); + + int count = 0; + f.each([&](flecs::entity e, Tag0, Tag1) { + test_assert(e == e_1); + count ++; + }); + + test_int(count, 1); +} + +void QueryBuilder_name_arg(void) { + flecs::world ecs; + + auto e = ecs.entity("Foo").set({10, 20}); + + auto f = ecs.query_builder() + .term_at(0).src().name("Foo") + .cache_kind(cache_kind) + .build(); + + int32_t count = 0; + f.run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + count ++; + test_int(p->x, 10); + test_int(p->y, 20); + test_assert(it.src(0) == e); + } + }); + + test_int(count, 1); +} + +void QueryBuilder_const_in_term(void) { + flecs::world ecs; + + ecs.entity().set({10, 20}); + + auto f = ecs.query_builder<>() + .with() + .cache_kind(cache_kind) + .build(); + + int32_t count = 0; + f.run([&](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + test_assert(it.is_readonly(0)); + for (auto i : it) { + count ++; + test_int(p[i].x, 10); + test_int(p[i].y, 20); + } + } + }); + + test_int(count, 1); +} + +void QueryBuilder_const_optional(void) { + flecs::world ecs; + + ecs.entity().set({10, 20}).add(); + ecs.entity().add(); + + auto f = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + int32_t count = 0, set_count = 0; + f.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + if (it.is_set(1)) { + auto p = it.field(1); + test_assert(it.is_readonly(1)); + test_int(p->x, 10); + test_int(p->y, 20); + set_count ++; + } + count++; + } + }); + + test_int(count, 2); + test_int(set_count, 1); +} + +void QueryBuilder_2_terms_w_expr(void) { + flecs::world ecs; + + auto a = ecs.entity("A"); + auto b = ecs.entity("B"); + + auto e1 = ecs.entity().add(a).add(b); + + auto f = ecs.query_builder() + .expr("A, B") + .cache_kind(cache_kind) + .build(); + + test_int(f.field_count(), 2); + + int32_t count = 0; + f.each([&](flecs::iter& it, size_t index) { + if (it.entity(index) == e1) { + test_assert(it.id(0) == a); + test_assert(it.id(1) == b); + count ++; + } + }); + + test_int(count, 1); +} + +void QueryBuilder_assert_on_uninitialized_term(void) { + install_test_abort(); + + flecs::world ecs; + + ecs.entity("A"); + ecs.entity("B"); + + test_expect_abort(); + + auto f = ecs.query_builder() + .term() + .term() + .cache_kind(cache_kind) + .build(); +} + +void QueryBuilder_operator_shortcuts(void) { + flecs::world ecs; + + flecs::entity a = ecs.entity(); + flecs::entity b = ecs.entity(); + flecs::entity c = ecs.entity(); + flecs::entity d = ecs.entity(); + flecs::entity e = ecs.entity(); + flecs::entity f = ecs.entity(); + flecs::entity g = ecs.entity(); + flecs::entity h = ecs.entity(); + + auto query = ecs.query_builder() + .with(a).and_() + .with(b).or_() + .with(c) + .with(d).not_() + .with(e).optional() + .with(f).and_from() + .with(g).or_from() + .with(h).not_from() + .cache_kind(cache_kind) + .build(); + + auto t = query.term(0); + test_int(t.id(), a); + test_int(t.oper(), flecs::And); + + t = query.term(1); + test_int(t.id(), b); + test_int(t.oper(), flecs::Or); + + t = query.term(2); + test_int(t.id(), c); + test_int(t.oper(), flecs::And); + + t = query.term(3); + test_int(t.id(), d); + test_int(t.oper(), flecs::Not); + + t = query.term(4); + test_int(t.id(), e); + test_int(t.oper(), flecs::Optional); + + t = query.term(5); + test_int(t.id(), f); + test_int(t.oper(), flecs::AndFrom); + + t = query.term(6); + test_int(t.id(), g); + test_int(t.oper(), flecs::OrFrom); + + t = query.term(7); + test_int(t.id(), h); + test_int(t.oper(), flecs::NotFrom); +} + +void QueryBuilder_inout_shortcuts(void) { + flecs::world ecs; + + flecs::entity a = ecs.entity(); + flecs::entity b = ecs.entity(); + flecs::entity c = ecs.entity(); + flecs::entity d = ecs.entity(); + + auto query = ecs.query_builder() + .with(a).in() + .with(b).out() + .with(c).inout() + .with(d).inout_none() + .cache_kind(cache_kind) + .build(); + + auto t = query.term(0); + test_int(t.id(), a); + test_int(t.inout(), flecs::In); + + t = query.term(1); + test_int(t.id(), b); + test_int(t.inout(), flecs::Out); + + t = query.term(2); + test_int(t.id(), c); + test_int(t.inout(), flecs::InOut); + + t = query.term(3); + test_int(t.id(), d); + test_int(t.inout(), flecs::InOutNone); +} + +void QueryBuilder_iter_column_w_const_as_array(void) { + flecs::world world; + + auto f = world.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e1 = world.entity().set({10, 20}); + auto e2 = world.entity().set({20, 30}); + + int32_t count = 0; + f.run([&](flecs::iter& it) { + while (it.next()) { + const auto p = it.field(0); + for (auto i : it) { + p[i].x += 1; + p[i].y += 2; + + count ++; + } + } + }); + + test_int(count, 2); + + const Position *p = e1.get(); + test_int(p->x, 11); + test_int(p->y, 22); + + p = e2.get(); + test_int(p->x, 21); + test_int(p->y, 32); +} + +void QueryBuilder_iter_column_w_const_as_ptr(void) { + flecs::world world; + + auto f = world.query_builder() + .cache_kind(cache_kind) + .build(); + + auto base = world.prefab().set({10, 20}); + world.entity().is_a(base); + world.entity().is_a(base); + + int32_t count = 0; + f.run([&](flecs::iter& it) { + while (it.next()) { + const auto p = it.field(0); + for (size_t i = 0; i < it.count(); i ++) { + test_int(p->x, 10); + test_int(p->y, 20); + count ++; + } + } + }); + + test_int(count, 2); +} + +void QueryBuilder_iter_column_w_const_deref(void) { + flecs::world world; + + auto f = world.query_builder() + .cache_kind(cache_kind) + .build(); + + auto base = world.prefab().set({10, 20}); + world.entity().is_a(base); + world.entity().is_a(base); + + int32_t count = 0; + f.run([&](flecs::iter& it) { + while (it.next()) { + const auto p = it.field(0); + Position pv = *p; + for (size_t i = 0; i < it.count(); i ++) { + test_int(pv.x, 10); + test_int(pv.y, 20); + count ++; + } + } + }); + + test_int(count, 2); +} + +void QueryBuilder_with_id(void) { + flecs::world ecs; + + flecs::query<> q = + ecs.query_builder() + .with(ecs.id()) + .with(ecs.id()) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_with_name(void) { + flecs::world ecs; + + ecs.component(); + + flecs::query<> q = + ecs.query_builder() + .with() + .with("Velocity") + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_with_component(void) { + flecs::world ecs; + + flecs::query<> q = + ecs.query_builder() + .with() + .with() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_with_pair_id(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity(); + flecs::entity Apples = ecs.entity(); + flecs::entity Pears = ecs.entity(); + + flecs::query<> q = + ecs.query_builder() + .with() + .with(Likes, Apples) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(Likes, Apples); + ecs.entity().add().add(Likes, Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_with_pair_name(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity("Likes"); + flecs::entity Apples = ecs.entity("Apples"); + flecs::entity Pears = ecs.entity("Pears"); + + flecs::query<> q = + ecs.query_builder() + .with() + .with("Likes", "Apples") + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(Likes, Apples); + ecs.entity().add().add(Likes, Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_with_pair_components(void) { + flecs::world ecs; + + struct Likes { }; + struct Apples { }; + struct Pears { }; + + flecs::query<> q = + ecs.query_builder() + .with() + .with() + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(); + ecs.entity().add().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_with_pair_component_id(void) { + flecs::world ecs; + + struct Likes { }; + flecs::entity Apples = ecs.entity(); + flecs::entity Pears = ecs.entity(); + + flecs::query<> q = + ecs.query_builder() + .with() + .with(Apples) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(Apples); + ecs.entity().add().add(Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_with_pair_name_component_id(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity("Likes"); + flecs::entity Apples = ecs.entity("Apples"); + flecs::entity Pears = ecs.entity("Pears"); + + flecs::query<> q = + ecs.query_builder() + .with() + .with("Likes", Apples) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(Likes, Apples); + ecs.entity().add().add(Likes, Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_with_pair_component_name(void) { + flecs::world ecs; + + struct Likes { }; + flecs::entity Apples = ecs.entity("Apples"); + flecs::entity Pears = ecs.entity("Pears"); + + flecs::query<> q = + ecs.query_builder() + .with() + .with("Apples") + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(Apples); + ecs.entity().add().add(Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_with_enum(void) { + flecs::world ecs; + + flecs::query<> q = + ecs.query_builder() + .with() + .with(Green) + .cache_kind(cache_kind) + .build(); + + auto e1 = ecs.entity().add().add(Green); + ecs.entity().add().add(Red); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e1); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_id(void) { + flecs::world ecs; + + flecs::query<> q = + ecs.query_builder() + .with(ecs.id()) + .without(ecs.id()) + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(); + auto e2 = ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_name(void) { + flecs::world ecs; + + ecs.component(); + + flecs::query<> q = + ecs.query_builder() + .with(ecs.id()) + .without("Velocity") + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(); + auto e2 = ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_component(void) { + flecs::world ecs; + + flecs::query<> q = + ecs.query_builder() + .with() + .without() + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(); + auto e2 = ecs.entity().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_pair_id(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity(); + flecs::entity Apples = ecs.entity(); + flecs::entity Pears = ecs.entity(); + + flecs::query<> q = + ecs.query_builder() + .with() + .without(Likes, Apples) + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(Likes, Apples); + auto e2 = ecs.entity().add().add(Likes, Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_pair_name(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity("Likes"); + flecs::entity Apples = ecs.entity("Apples"); + flecs::entity Pears = ecs.entity("Pears"); + + flecs::query<> q = + ecs.query_builder() + .with() + .without("Likes", "Apples") + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(Likes, Apples); + auto e2 = ecs.entity().add().add(Likes, Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_pair_components(void) { + flecs::world ecs; + + struct Likes { }; + struct Apples { }; + struct Pears { }; + + flecs::query<> q = + ecs.query_builder() + .with() + .without() + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(); + auto e2 = ecs.entity().add().add(); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_pair_component_id(void) { + flecs::world ecs; + + struct Likes { }; + flecs::entity Apples = ecs.entity(); + flecs::entity Pears = ecs.entity(); + + flecs::query<> q = + ecs.query_builder() + .with() + .without(Apples) + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(Apples); + auto e2 = ecs.entity().add().add(Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_pair_component_name(void) { + flecs::world ecs; + + struct Likes { }; + flecs::entity Apples = ecs.entity("Apples"); + flecs::entity Pears = ecs.entity("Pears"); + + flecs::query<> q = + ecs.query_builder() + .with() + .without("Apples") + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(Apples); + auto e2 = ecs.entity().add().add(Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_pair_name_component_id(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity("Likes"); + flecs::entity Apples = ecs.entity("Apples"); + flecs::entity Pears = ecs.entity("Pears"); + + flecs::query<> q = + ecs.query_builder() + .with() + .without("Likes", Apples) + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(Likes, Apples); + auto e2 = ecs.entity().add().add(Likes, Pears); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_without_enum(void) { + flecs::world ecs; + + flecs::query<> q = + ecs.query_builder() + .with() + .without(Green) + .cache_kind(cache_kind) + .build(); + + ecs.entity().add().add(Green); + auto e2 = ecs.entity().add().add(Red); + + int32_t count = 0; + q.each([&](flecs::entity e) { + count ++; + test_assert(e == e2); + }); + + test_int(count, 1); +} + +void QueryBuilder_write_id(void) { + flecs::world ecs; + + auto q = + ecs.query_builder() + .with() + .write(ecs.id()) + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_write_name(void) { + flecs::world ecs; + + ecs.component(); + + auto q = + ecs.query_builder() + .with() + .write("Position") + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_write_component(void) { + flecs::world ecs; + + ecs.component(); + + auto q = + ecs.query_builder() + .with() + .write() + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_write_pair_id(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity(); + flecs::entity Apples = ecs.entity(); + + flecs::query<> q = + ecs.query_builder() + .with() + .write(Likes, Apples) + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_first() == Likes); + test_assert(q.term(1).get_second() == Apples); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_write_pair_name(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity("Likes"); + flecs::entity Apples = ecs.entity("Apples"); + + flecs::query<> q = + ecs.query_builder() + .with() + .write("Likes", "Apples") + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_first() == Likes); + test_assert(q.term(1).get_second() == Apples); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_write_pair_components(void) { + flecs::world ecs; + + struct Likes { }; + struct Apples { }; + + flecs::query<> q = + ecs.query_builder() + .with() + .write() + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_second() == ecs.id()); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_write_pair_component_id(void) { + flecs::world ecs; + + struct Likes { }; + flecs::entity Apples = ecs.entity(); + + flecs::query<> q = + ecs.query_builder() + .with() + .write(Apples) + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_second() == Apples); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_write_pair_component_name(void) { + flecs::world ecs; + + struct Likes { }; + flecs::entity Apples = ecs.entity("Apples"); + + flecs::query<> q = + ecs.query_builder() + .with() + .write("Apples") + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_second() == Apples); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_write_enum(void) { + flecs::world ecs; + + flecs::query<> q = + ecs.query_builder() + .with() + .write(Green) + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::Out); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_second() == ecs.to_entity(Green)); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_read_id(void) { + flecs::world ecs; + + auto q = + ecs.query_builder() + .with() + .read(ecs.id()) + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_read_name(void) { + flecs::world ecs; + + ecs.component(); + + auto q = + ecs.query_builder() + .with() + .read("Position") + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_read_component(void) { + flecs::world ecs; + + ecs.component(); + + auto q = + ecs.query_builder() + .with() + .read() + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_read_pair_id(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity(); + flecs::entity Apples = ecs.entity(); + + flecs::query<> q = + ecs.query_builder() + .with() + .read(Likes, Apples) + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_first() == Likes); + test_assert(q.term(1).get_second() == Apples); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_read_pair_name(void) { + flecs::world ecs; + + flecs::entity Likes = ecs.entity("Likes"); + flecs::entity Apples = ecs.entity("Apples"); + + flecs::query<> q = + ecs.query_builder() + .with() + .read("Likes", "Apples") + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_first() == Likes); + test_assert(q.term(1).get_second() == Apples); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_read_pair_components(void) { + flecs::world ecs; + + struct Likes { }; + struct Apples { }; + + flecs::query<> q = + ecs.query_builder() + .with() + .read() + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_second() == ecs.id()); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_read_pair_component_id(void) { + flecs::world ecs; + + struct Likes { }; + flecs::entity Apples = ecs.entity(); + + flecs::query<> q = + ecs.query_builder() + .with() + .read(Apples) + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_second() == Apples); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_read_pair_component_name(void) { + flecs::world ecs; + + struct Likes { }; + flecs::entity Apples = ecs.entity("Apples"); + + flecs::query<> q = + ecs.query_builder() + .with() + .read("Apples") + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_second() == Apples); + + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_read_enum(void) { + flecs::world ecs; + + flecs::query<> q = + ecs.query_builder() + .with() + .read(Green) + .cache_kind(cache_kind) + .build(); + + test_assert(q.term(1).inout() == flecs::In); + test_assert(q.term(1).get_first() == ecs.id()); + test_assert(q.term(1).get_second() == ecs.to_entity(Green)); + test_assert(q.term(1).get_src() == 0); +} + +void QueryBuilder_assign_after_init(void) { + flecs::world ecs; + + flecs::query<> f; + flecs::query_builder<> fb = ecs.query_builder(); + fb.with(); + fb.cache_kind(cache_kind); + f = fb.build(); + + flecs::entity e1 = ecs.entity().set({10, 20}); + + int32_t count = 0; + f.each([&](flecs::entity e) { + test_assert(e == e1); + count ++; + }); + + test_int(count, 1); +} + +void QueryBuilder_with_t_inout(void) { + flecs::world ecs; + + flecs::query<> f = ecs.query_builder() + .with(ecs.id()) + .cache_kind(cache_kind) + .build(); + + test_assert(f.term(0).inout() == flecs::InOutNone); +} + +void QueryBuilder_with_T_inout(void) { + flecs::world ecs; + + flecs::query<> f = ecs.query_builder() + .with() + .cache_kind(cache_kind) + .build(); + + test_assert(f.term(0).inout() == flecs::InOutNone); +} + +void QueryBuilder_with_R_T_inout(void) { + flecs::world ecs; + + flecs::query<> f = ecs.query_builder() + .with() + .cache_kind(cache_kind) + .build(); + + test_assert(f.term(0).inout() == flecs::InOutNone); +} + +void QueryBuilder_with_R_t_inout(void) { + flecs::world ecs; + + flecs::query<> f = ecs.query_builder() + .with(ecs.id()) + .cache_kind(cache_kind) + .build(); + + test_assert(f.term(0).inout() == flecs::InOutNone); +} + +void QueryBuilder_with_r_t_inout(void) { + flecs::world ecs; + + flecs::query<> f = ecs.query_builder() + .with(ecs.id(), ecs.id()) + .cache_kind(cache_kind) + .build(); + + test_assert(f.term(0).inout() == flecs::InOutNone); +} + +static +int filter_move_arg(flecs::query&& f) { + int32_t count = 0; + + f.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + return count; +} + +void QueryBuilder_filter_as_move_arg(void) { + flecs::world ecs; + + auto f = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + test_int(filter_move_arg(ecs.query()), 3); +} + +static +flecs::query filter_return(flecs::world& ecs) { + return ecs.query_builder() + .cache_kind(cache_kind) + .build(); +} + +void QueryBuilder_filter_as_return(void) { + flecs::world ecs; + + auto e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + auto f = filter_return(ecs); + + int32_t count = 0; + + f.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_filter_copy(void) { + flecs::world ecs; + + auto e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + auto f = ecs.query_builder() + .cache_kind(cache_kind) + .build(); + + auto f_2 = f; + + int32_t count = 0; + + f_2.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_world_each_filter_1_component(void) { + flecs::world ecs; + + auto e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + e = ecs.entity(); + e.set({e}); + + int32_t count = 0; + + ecs.each([&](flecs::entity e, Self& s) { + test_assert(e == s.value); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_world_each_filter_2_components(void) { + flecs::world ecs; + + auto e = ecs.entity(); + e.set({e}) + .set({10, 20}); + + e = ecs.entity(); + e.set({e}) + .set({10, 20}); + + e = ecs.entity(); + e.set({e}) + .set({10, 20}); + + int32_t count = 0; + + ecs.each([&](flecs::entity e, Self& s, Position& p) { + test_assert(e == s.value); + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_world_each_filter_1_component_no_entity(void) { + flecs::world ecs; + + ecs.entity() + .set({10, 20}); + + ecs.entity() + .set({10, 20}); + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + int32_t count = 0; + + ecs.each([&](Position& p) { + test_int(p.x, 10); + test_int(p.y, 20); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_world_each_filter_2_components_no_entity(void) { + flecs::world ecs; + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + ecs.entity() + .set({10, 20}) + .set({1, 2}); + + ecs.entity() + .set({3, 5}); + + ecs.entity() + .set({20, 40}); + + int32_t count = 0; + + ecs.each([&](Position& p, Velocity& v) { + test_int(p.x, 10); + test_int(p.y, 20); + test_int(v.x, 1); + test_int(v.y, 2); + count ++; + }); + + test_int(count, 3); +} + +void QueryBuilder_var_src_w_prefixed_name(void) { + flecs::world ecs; + + struct Foo { }; + + auto r = ecs.query_builder() + .with().src("$Var") + .cache_kind(cache_kind) + .build(); + + auto e = ecs.entity().add(); + + int32_t count = 0; + r.run([&](flecs::iter& it) { + while (it.next()) { + test_assert(it.get_var("Var") == e); + count ++; + } + }); + + test_int(count, 1); + +} + +void QueryBuilder_var_first_w_prefixed_name(void) { + flecs::world ecs; + + struct Foo { }; + + auto r = ecs.query_builder() + .with() + .term().first("$Var") + .cache_kind(cache_kind) + .build(); + + auto e = ecs.entity().add(); + + int32_t count = 0; + r.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + test_uint(it.entity(0), e); + test_assert(it.get_var("Var") == ecs.id()); + count ++; + } + }); + + test_int(count, 1); + +} + +void QueryBuilder_var_second_w_prefixed_name(void) { + flecs::world ecs; + + struct Foo { }; + + auto r = ecs.query_builder() + .with().second("$Var") + .cache_kind(cache_kind) + .build(); + + auto t = ecs.entity(); + auto e = ecs.entity().add(t); + + int32_t count = 0; + r.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + test_uint(it.entity(0), e); + test_assert(it.get_var("Var") == t); + count ++; + } + }); + + test_int(count, 1); + +} + +void QueryBuilder_term_w_second_var_string(void) { + flecs::world ecs; + + flecs::entity Foo = ecs.entity(); + + auto r = ecs.query_builder() + .with(Foo, "$Var") + .cache_kind(cache_kind) + .build(); + + auto t = ecs.entity(); + auto e = ecs.entity().add(Foo, t); + + int32_t count = 0; + r.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + test_uint(it.entity(0), e); + test_assert(it.get_var("Var") == t); + count ++; + } + }); + + test_int(count, 1); + +} + +void QueryBuilder_term_type_w_second_var_string(void) { + flecs::world ecs; + + struct Foo { }; + + auto r = ecs.query_builder() + .with("$Var") + .cache_kind(cache_kind) + .build(); + + auto t = ecs.entity(); + auto e = ecs.entity().add(t); + + int32_t count = 0; + r.run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + test_uint(it.entity(0), e); + test_assert(it.get_var("Var") == t); + count ++; + } + }); + + test_int(count, 1); + +} + +void QueryBuilder_named_rule(void) { + flecs::world ecs; + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add(); + + auto q = ecs.query_builder("my_query") + .cache_kind(cache_kind) + .build(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position&) { + test_assert(e == e1 || e == e2); + count ++; + }); + test_int(count, 2); + + flecs::entity qe = q.entity(); + test_assert(qe != 0); + test_str(qe.name(), "my_query"); + +} + +void QueryBuilder_named_scoped_rule(void) { + flecs::world ecs; + + auto e1 = ecs.entity().add(); + auto e2 = ecs.entity().add(); + + auto q = ecs.query_builder("my::query") + .cache_kind(cache_kind) + .build(); + + int32_t count = 0; + q.each([&](flecs::entity e, Position&) { + test_assert(e == e1 || e == e2); + count ++; + }); + test_int(count, 2); + + flecs::entity qe = q.entity(); + test_assert(qe != 0); + test_str(qe.name(), "query"); + test_str(qe.path(), "::my::query"); + +} + +void QueryBuilder_is_valid(void) { + flecs::world ecs; + + auto q_1 = ecs.query(); + test_assert(q_1); + + flecs::log::set_level(-4); + auto q_2 = ecs.query_builder() + .expr("foo") + .cache_kind(cache_kind) + .build(); + test_assert(!q_2); +} + +void QueryBuilder_unresolved_by_name(void) { + flecs::world ecs; + + auto q = ecs.query_builder() + .query_flags(EcsQueryAllowUnresolvedByName) + .expr("$this == Foo") + .cache_kind(cache_kind) + .build(); + + test_assert(q); + + test_false(q.iter().is_true()); + + ecs.entity("Foo"); + + test_true(q.iter().is_true()); + +} + +void QueryBuilder_scope(void) { + flecs::world ecs; + + flecs::entity Root = ecs.entity(); + flecs::entity TagA = ecs.entity(); + flecs::entity TagB = ecs.entity(); + + ecs.entity() + .add(Root) + .add(TagA) + .add(TagB); + + auto e2 = ecs.entity() + .add(Root) + .add(TagA); + + ecs.entity() + .add(Root) + .add(TagB); + + ecs.entity() + .add(Root); + + auto r = ecs.query_builder() + .with(Root) + .scope_open().not_() + .with(TagA) + .without(TagB) + .scope_close() + .cache_kind(cache_kind) + .build(); + + int32_t count = 0; + r.each([&](flecs::entity e) { + test_assert(e != e2); + count ++; + }); + + test_int(count, 3); + +} diff --git a/vendors/flecs/test/cpp_api/src/Refs.cpp b/vendors/flecs/test/cpp/src/Refs.cpp similarity index 66% rename from vendors/flecs/test/cpp_api/src/Refs.cpp rename to vendors/flecs/test/cpp/src/Refs.cpp index 2d12e3e6f..8d2913be8 100644 --- a/vendors/flecs/test/cpp_api/src/Refs.cpp +++ b/vendors/flecs/test/cpp/src/Refs.cpp @@ -1,4 +1,4 @@ -#include +#include void Refs_get_ref_by_ptr(void) { flecs::world world; @@ -94,6 +94,30 @@ void Refs_pair_ref(void) { test_int((e.get()->x), 11); } +using PositionTag = flecs::pair; + +void Refs_pair_ref_w_pair_type(void) { + flecs::world world; + + auto e = world.entity().set({10, 20}); + auto ref = e.get_ref(); + ref->x++; + + test_int((e.get()->x), 11); +} + +using TagPosition = flecs::pair; + +void Refs_pair_ref_w_pair_type_second(void) { + flecs::world world; + + auto e = world.entity().set({10, 20}); + auto ref = e.get_ref(); + ref->x++; + + test_int((e.get()->x), 11); +} + void Refs_pair_ref_w_entity(void) { flecs::world world; @@ -138,6 +162,27 @@ void Refs_default_ctor(void) { test_int(p->y, 20); } +void Refs_ctor_from_entity(void) { + flecs::world world; + + flecs::entity e = world.entity().set({10, 20}); + + flecs::ref p(e); + + test_int(p->x, 10); + test_int(p->y, 20); +} + +void Refs_implicit_operator_bool(void) { + flecs::world world; + + flecs::entity e = world.entity().set({10, 20}); + + flecs::ref p(e); + + test_assert(p); +} + void Refs_try_get(void) { flecs::world world; @@ -145,3 +190,39 @@ void Refs_try_get(void) { test_assert(p.try_get() == nullptr); } + +void Refs_has(void) { + flecs::world world; + + flecs::entity e = world.entity(); + + { + flecs::ref p = e.get_ref(); + test_assert(!p.has()); + } + + e.set({10, 20}); + + { + flecs::ref p = e.get_ref(); + test_assert(p.has()); + } +} + +void Refs_bool_operator(void) { + flecs::world world; + + flecs::entity e = world.entity(); + + { + flecs::ref p = e.get_ref(); + test_assert(!p); + } + + e.set({10, 20}); + + { + flecs::ref p = e.get_ref(); + test_assert(p); + } +} diff --git a/vendors/flecs/test/cpp_api/src/Singleton.cpp b/vendors/flecs/test/cpp/src/Singleton.cpp similarity index 88% rename from vendors/flecs/test/cpp_api/src/Singleton.cpp rename to vendors/flecs/test/cpp/src/Singleton.cpp index 991144302..5e013b19c 100644 --- a/vendors/flecs/test/cpp_api/src/Singleton.cpp +++ b/vendors/flecs/test/cpp/src/Singleton.cpp @@ -1,4 +1,4 @@ -#include +#include void Singleton_set_get_singleton(void) { flecs::world world; @@ -11,12 +11,12 @@ void Singleton_set_get_singleton(void) { test_int(p->y, 20); } -void Singleton_get_mut_singleton(void) { +void Singleton_ensure_singleton(void) { flecs::world world; - Position *p_mut = world.get_mut(); - p_mut->x = 10; - p_mut->y = 20; + Position& p_mut = world.ensure(); + p_mut.x = 10; + p_mut.y = 20; const Position *p = world.get(); test_assert(p != NULL); @@ -24,6 +24,19 @@ void Singleton_get_mut_singleton(void) { test_int(p->y, 20); } +void Singleton_get_mut_singleton(void) { + flecs::world world; + + Position *p = world.get_mut(); + test_assert(p == nullptr); + + world.set({10, 20}); + p = world.get_mut(); + test_assert(p != nullptr); + test_int(p->x, 10); + test_int(p->y, 20); +} + void Singleton_emplace_singleton(void) { flecs::world world; @@ -42,13 +55,12 @@ void Singleton_modified_singleton(void) { world.observer() .event(flecs::OnSet) - .iter([&](flecs::iter it, Position *p) { + .each([&](Position&) { invoked ++; }); auto e = world.entity(); - Position *p = e.get_mut(); - test_assert(p != NULL); + e.ensure(); test_int(invoked, 0); e.modified(); @@ -62,7 +74,7 @@ void Singleton_add_singleton(void) { world.observer() .event(flecs::OnAdd) - .iter([&](flecs::iter it, Position *p) { + .each([&](Position& p) { invoked ++; }); @@ -79,15 +91,14 @@ void Singleton_remove_singleton(void) { world.observer() .event(flecs::OnRemove) - .iter([&](flecs::iter it, Position *p) { + .each([&](Position& p) { invoked ++; }); - Position *p_mut = world.get_mut(); - test_assert(p_mut != NULL); + world.ensure(); + test_int(invoked, 0); world.remove(); - test_int(invoked, 1); } @@ -108,13 +119,15 @@ void Singleton_singleton_system(void) { world.system<>() .expr("[inout] Position($)") - .iter([](flecs::iter it) { - auto p = it.field(1); - test_int(p->x, 10); - test_int(p->y, 20); - - p->x ++; - p->y ++; + .run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + test_int(p->x, 10); + test_int(p->y, 20); + + p->x ++; + p->y ++; + } }); world.progress(); diff --git a/vendors/flecs/test/cpp_api/src/System.cpp b/vendors/flecs/test/cpp/src/System.cpp similarity index 73% rename from vendors/flecs/test/cpp_api/src/System.cpp rename to vendors/flecs/test/cpp/src/System.cpp index feaa2e035..6474c67bc 100644 --- a/vendors/flecs/test/cpp_api/src/System.cpp +++ b/vendors/flecs/test/cpp/src/System.cpp @@ -1,4 +1,4 @@ -#include +#include void System_iter(void) { flecs::world world; @@ -8,10 +8,14 @@ void System_iter(void) { .set({1, 2}); world.system() - .iter([](flecs::iter&it, Position *p, Velocity *v) { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + .run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -34,10 +38,14 @@ void System_iter_const(void) { .set({1, 2}); world.system() - .iter([](flecs::iter&it, Position *p, const Velocity* v) { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + .run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -55,6 +63,9 @@ void System_iter_const(void) { void System_iter_shared(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto base = world.entity() .set({1, 2}); @@ -66,19 +77,22 @@ void System_iter_shared(void) { .set({10, 20}) .set({3, 4}); - world.system().expr("Velocity(self|up)") - .iter([](flecs::iter&it, Position *p) { - auto v = it.field(2); - - if (!it.is_self(2)) { - for (auto i : it) { - p[i].x += v->x; - p[i].y += v->y; - } - } else { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + world.system().expr("Velocity(self|up IsA)") + .run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + + if (!it.is_self(1)) { + for (auto i : it) { + p[i].x += v->x; + p[i].y += v->y; + } + } else { + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } } }); @@ -115,18 +129,24 @@ void System_iter_optional(void) { .set({70, 80}); world.system() - .iter([](flecs::iter& it, Position *p, Velocity *v, Mass *m) { - if (it.is_set(2) && it.is_set(3)) { - for (auto i : it) { - p[i].x += v[i].x * m[i].value; - p[i].y += v[i].y * m[i].value; - } - } else { - for (auto i : it) { - p[i].x ++; - p[i].y ++; + .run([](flecs::iter& it) { + while (it.next()) { + auto p = it.field(0); + auto v = it.field(1); + auto m = it.field(2); + + if (it.is_set(1) && it.is_set(2)) { + for (auto i : it) { + p[i].x += v[i].x * m[i].value; + p[i].y += v[i].y * m[i].value; + } + } else { + for (auto i : it) { + p[i].x ++; + p[i].y ++; + } + } } - } }); world.progress(); @@ -278,13 +298,15 @@ void System_signature(void) { .set({1, 2}); world.system<>().expr("Position, Velocity") - .iter([](flecs::iter&it) { - flecs::column p(it, 1); - flecs::column v(it, 2); + .run([](flecs::iter& it) { + while (it.next()) { + flecs::field p(it, 0); + flecs::field v(it, 1); - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -307,13 +329,15 @@ void System_signature_const(void) { .set({1, 2}); world.system<>().expr("Position, [in] Velocity") - .iter([](flecs::iter&it) { - flecs::column p(it, 1); - flecs::column v(it, 2); + .run([](flecs::iter& it) { + while (it.next()) { + flecs::field p(it, 0); + flecs::field v(it, 1); - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -331,6 +355,9 @@ void System_signature_const(void) { void System_signature_shared(void) { flecs::world world; + world.component().add(flecs::OnInstantiate, flecs::Inherit); + world.component().add(flecs::OnInstantiate, flecs::Inherit); + auto base = world.entity() .set({1, 2}); @@ -342,20 +369,22 @@ void System_signature_shared(void) { .set({10, 20}) .set({3, 4}); - world.system<>().expr("Position, [in] Velocity(self|up)") - .iter([](flecs::iter&it) { - flecs::column p(it, 1); - flecs::column v(it, 2); - - if (!it.is_self(2)) { - for (auto i : it) { - p[i].x += v->x; - p[i].y += v->y; - } - } else { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + world.system<>().expr("Position, [in] Velocity(self|up IsA)") + .run([](flecs::iter& it) { + while (it.next()) { + flecs::field p(it, 0); + flecs::field v(it, 1); + + if (!it.is_self(1)) { + for (auto i : it) { + p[i].x += v->x; + p[i].y += v->y; + } + } else { + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } } }); @@ -392,20 +421,22 @@ void System_signature_optional(void) { .set({70, 80}); world.system<>().expr("Position, ?Velocity, ?Mass") - .iter([](flecs::iter& it) { - flecs::column p(it, 1); - flecs::column v(it, 2); - flecs::column m(it, 3); - - if (it.is_set(2) && it.is_set(3)) { - for (auto i : it) { - p[i].x += v[i].x * m[i].value; - p[i].y += v[i].y * m[i].value; - } - } else { - for (auto i : it) { - p[i].x ++; - p[i].y ++; + .run([](flecs::iter& it) { + while (it.next()) { + flecs::field p(it, 0); + flecs::field v(it, 1); + flecs::field m(it, 2); + + if (it.is_set(1) && it.is_set(2)) { + for (auto i : it) { + p[i].x += v[i].x * m[i].value; + p[i].y += v[i].y * m[i].value; + } + } else { + for (auto i : it) { + p[i].x ++; + p[i].y ++; + } } } }); @@ -437,11 +468,11 @@ void System_copy_name_on_create(void) { strcpy(name, "Hello"); auto system_1 = world.system(name) - .iter([](flecs::iter&it, Position *p) {}); + .run([](flecs::iter& it) { while (it.next()) {} }); strcpy(name, "World"); auto system_2 = world.system(name) - .iter([](flecs::iter&it, Position *p) {}); + .run([](flecs::iter& it) { while (it.next()) {} }); test_assert(system_1.id() != system_2.id()); } @@ -450,7 +481,7 @@ void System_nested_system(void) { flecs::world world; auto system_1 = world.system("foo::bar") - .iter([](flecs::iter&it, Position *p) {}); + .run([](flecs::iter& it) { while (it.next()) {} }); test_str(system_1.name().c_str(), "bar"); @@ -469,8 +500,10 @@ void System_empty_signature(void) { int count = 0; world.system<>() - .iter([&](flecs::iter it) { - count ++; + .run([&](flecs::iter it) { + while (it.next()) { + count ++; + } }); world.progress(); @@ -486,8 +519,10 @@ void System_iter_tag(void) { int invoked = 0; world.system() - .iter([&](flecs::iter it, MyTag*) { - invoked ++; + .run([&](flecs::iter it) { + while (it.next()) { + invoked ++; + } }); world.entity() @@ -522,7 +557,7 @@ void System_set_interval(void) { auto sys = world.system<>() .kind(0) .interval(1.0f) - .iter([&](flecs::iter& it) { }); + .run([&](flecs::iter& it) { }); float i = sys.interval(); test_int(i, 1.0f); @@ -670,11 +705,13 @@ void System_get_query(void) { auto q = sys.query(); - q.iter([&](flecs::iter &it) { - auto pos = it.field(1); - for (auto i : it) { - test_int(i, pos[i].x); - count ++; + q.run([&](flecs::iter &it) { + while (it.next()) { + auto pos = it.field(0); + for (auto i : it) { + test_int(i, pos[i].x); + count ++; + } } }); @@ -779,10 +816,12 @@ void System_add_from_iter(void) { auto e3 = world.entity().set({2, 0}); world.system() - .iter([](flecs::iter& it, const Position* p) { - for (auto i : it) { - it.entity(i).add(); - test_assert(!it.entity(i).has()); + .run([](flecs::iter& it) { + while (it.next()) { + for (auto i : it) { + it.entity(i).add(); + test_assert(!it.entity(i).has()); + } } }); @@ -801,11 +840,13 @@ void System_delete_from_iter(void) { auto e3 = world.entity().set({2, 0}); world.system() - .iter([](const flecs::iter& it, const Position* p) { - for (auto i : it) { - it.entity(i).destruct(); - // Delete is deferred - test_assert(it.entity(i).is_alive()); + .run([](flecs::iter& it) { + while (it.next()) { + for (auto i : it) { + it.entity(i).destruct(); + // Delete is deferred + test_assert(it.entity(i).is_alive()); + } } }); @@ -824,9 +865,12 @@ void System_add_from_iter_world_handle(void) { auto e3 = world.entity().set({world.entity()}); world.system() - .iter([](const flecs::iter& it, const Entity* c) { - for (auto i : it) { - c[i].e.mut(it).add(); + .run([](flecs::iter& it) { + while (it.next()) { + auto c = it.field(0); + for (auto i : it) { + c[i].e.mut(it).add(); + } } }); @@ -845,11 +889,13 @@ void System_new_from_iter(void) { auto e3 = world.entity().set({0, 0}); world.system() - .iter([](const flecs::iter& it, const Position* p) { - for (auto i : it) { - it.entity(i).set({ - it.world().entity().add() - }); + .run([](flecs::iter& it) { + while (it.next()) { + for (auto i : it) { + it.entity(i).set({ + it.world().entity().add() + }); + } } }); @@ -875,12 +921,14 @@ void System_each_w_mut_children_it(void) { int32_t count = 0; world.system() - .iter([&](const flecs::iter& it, const Position* p) { - for (auto i : it) { - it.entity(i).children([&](flecs::entity child) { - child.add(); - count ++; - }); + .run([&](flecs::iter& it) { + while (it.next()) { + for (auto i : it) { + it.entity(i).children([&](flecs::entity child) { + child.add(); + count ++; + }); + } } }); @@ -905,16 +953,19 @@ void System_readonly_children_iter(void) { int32_t count = 0; world.system() - .iter([&](const flecs::iter& it, const Entity* c) { - for (auto i : it) { - c[i].e.children([&](flecs::entity child) { - // Dummy code to ensure we can access the entity - const Position *p = child.get(); - test_int(p->x, 1); - test_int(p->y, 0); - - count ++; - }); + .run([&](flecs::iter& it) { + while (it.next()) { + auto c = it.field(0); + for (auto i : it) { + c[i].e.children([&](flecs::entity child) { + // Dummy code to ensure we can access the entity + const Position *p = child.get(); + test_int(p->x, 1); + test_int(p->y, 0); + + count ++; + }); + } } }); @@ -936,38 +987,50 @@ void System_rate_filter(void) { frame_count = 0; auto root = world.system<>("root") - .iter([&](flecs::iter& it) { - root_count ++; + .run([&](flecs::iter& it) { + while (it.next()) { + root_count ++; + } }); auto l1_a = world.system<>("l1_a") .rate(root, 1) - .iter([&](flecs::iter& it) { - l1_a_count ++; + .run([&](flecs::iter& it) { + while (it.next()) { + l1_a_count ++; + } }); auto l1_b = world.system<>("l1_b") .rate(root, 2) - .iter([&](flecs::iter& it) { - l1_b_count ++; + .run([&](flecs::iter& it) { + while (it.next()) { + l1_b_count ++; + } }); world.system<>("l1_c") .rate(root, 3) - .iter([&](flecs::iter& it) { - l1_c_count ++; + .run([&](flecs::iter& it) { + while (it.next()) { + l1_c_count ++; + } }); world.system<>("l2_a") .rate(l1_a, 2) - .iter([&](flecs::iter& it) { - l2_a_count ++; + .run([&](flecs::iter& it) { + while (it.next()) { + l2_a_count ++; + } }); world.system<>("l2_b") .rate(l1_b, 2) - .iter([&](flecs::iter& it) { - l2_b_count ++; + .run([&](flecs::iter& it) { + while (it.next()) { + l2_b_count ++; + } }); for (int i = 0; i < 30; i ++) { @@ -981,6 +1044,26 @@ void System_rate_filter(void) { } } +void System_self_rate_filter(void) { + flecs::world world; + + int32_t count = 0; + + world.system("sys") + .rate(2) + .each([&](Position & p){ + count ++; + }); + + world.entity().set({1.0, 2.0}); + + for(int i = 0; i < 10; i++) { + world.progress(); + } + + test_int(count, 5); +} + void System_update_rate_filter(void) { flecs::world world; @@ -991,20 +1074,26 @@ void System_update_rate_filter(void) { frame_count = 0; auto root = world.system<>("root") - .iter([&](flecs::iter& it) { - root_count ++; + .run([&](flecs::iter& it) { + while (it.next()) { + root_count ++; + } }); auto l1 = world.system<>("l1") .rate(root, 2) - .iter([&](flecs::iter& it) { - l1_count ++; + .run([&](flecs::iter& it) { + while (it.next()) { + l1_count ++; + } }); world.system<>("l2") .rate(l1, 3) - .iter([&](flecs::iter& it) { - l2_count ++; + .run([&](flecs::iter& it) { + while (it.next()) { + l2_count ++; + } }); for (int i = 0; i < 12; i ++) { @@ -1041,7 +1130,7 @@ void System_test_auto_defer_each(void) { auto e3 = world.entity().add().set({30}); auto s = world.system() - .term() + .with() .each([](flecs::entity e, Value& v) { v.value ++; e.remove(); @@ -1072,11 +1161,14 @@ void System_test_auto_defer_iter(void) { auto e3 = world.entity().add().set({30}); auto s = world.system() - .term() - .iter([](flecs::iter& it, Value *v) { - for (auto i : it) { - v[i].value ++; - it.entity(i).remove(); + .with() + .run([](flecs::iter& it) { + while (it.next()) { + auto v = it.field(0); + for (auto i : it) { + v[i].value ++; + it.entity(i).remove(); + } } }); @@ -1104,34 +1196,40 @@ void System_custom_pipeline(void) { auto Tag = world.entity(); flecs::entity pip = world.pipeline() - .term(flecs::System) - .term(flecs::Phase).cascade(flecs::DependsOn) - .term(Tag) + .with(flecs::System) + .with(flecs::Phase).cascade(flecs::DependsOn) + .with(Tag) .build(); int count = 0; world.system() .kind(PostFrame) - .iter([&](flecs::iter it) { - test_int(count, 2); - count ++; + .run([&](flecs::iter it) { + while (it.next()) { + test_int(count, 2); + count ++; + } }) .add(Tag); world.system() .kind(OnFrame) - .iter([&](flecs::iter it) { - test_int(count, 1); - count ++; + .run([&](flecs::iter it) { + while (it.next()) { + test_int(count, 1); + count ++; + } }) .add(Tag); world.system() .kind(PreFrame) - .iter([&](flecs::iter it) { - test_int(count, 0); - count ++; + .run([&](flecs::iter it) { + while (it.next()) { + test_int(count, 0); + count ++; + } }) .add(Tag); @@ -1150,31 +1248,37 @@ void System_custom_pipeline_w_kind(void) { auto Tag = world.entity(); flecs::entity pip = world.pipeline() - .term(flecs::System) - .term(Tag) + .with(flecs::System) + .with(Tag) .build(); int count = 0; world.system() .kind(Tag) - .iter([&](flecs::iter it) { - test_int(count, 0); - count ++; + .run([&](flecs::iter it) { + while (it.next()) { + test_int(count, 0); + count ++; + } }); world.system() .kind(Tag) - .iter([&](flecs::iter it) { - test_int(count, 1); - count ++; + .run([&](flecs::iter it) { + while (it.next()) { + test_int(count, 1); + count ++; + } }); world.system() .kind(Tag) - .iter([&](flecs::iter it) { - test_int(count, 2); - count ++; + .run([&](flecs::iter it) { + while (it.next()) { + test_int(count, 2); + count ++; + } }); test_int(count, 0); @@ -1203,8 +1307,7 @@ void System_instanced_query_w_singleton_each(void) { int32_t count = 0; auto s = ecs.system() - .arg(3).singleton() - .instanced() + .term_at(2).singleton() .each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { test_assert(e == s.value); p.x += v.x; @@ -1256,125 +1359,6 @@ void System_instanced_query_w_base_each(void) { auto e7 = ecs.entity().set({70, 80}).set({4, 5}); e7.set({e7}); int32_t count = 0; - auto s = ecs.system() - .instanced() - .each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { - test_assert(e == s.value); - p.x += v.x; - p.y += v.y; - count ++; - }); - - s.run(); - - test_int(count, 7); - - test_assert(e1.get([](const Position& p) { - test_int(p.x, 11); - test_int(p.y, 22); - })); - - test_assert(e2.get([](const Position& p) { - test_int(p.x, 21); - test_int(p.y, 32); - })); - - test_assert(e3.get([](const Position& p) { - test_int(p.x, 31); - test_int(p.y, 42); - })); - - test_assert(e4.get([](const Position& p) { - test_int(p.x, 41); - test_int(p.y, 52); - })); - - test_assert(e5.get([](const Position& p) { - test_int(p.x, 51); - test_int(p.y, 62); - })); - - test_assert(e6.get([](const Position& p) { - test_int(p.x, 62); - test_int(p.y, 73); - })); - - test_assert(e7.get([](const Position& p) { - test_int(p.x, 74); - test_int(p.y, 85); - })); -} - -void System_un_instanced_query_w_singleton_each(void) { - flecs::world ecs; - - ecs.set({1, 2}); - - auto e1 = ecs.entity().set({10, 20}); e1.set({e1}); - auto e2 = ecs.entity().set({20, 30}); e2.set({e2}); - auto e3 = ecs.entity().set({30, 40}); e3.set({e3}); - auto e4 = ecs.entity().set({40, 50}); e4.set({e4}); - auto e5 = ecs.entity().set({50, 60}); e5.set({e5}); - - e4.add(); - e5.add(); - - int32_t count = 0; - - auto s = ecs.system() - .arg(3).singleton() - .each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { - test_assert(e == s.value); - p.x += v.x; - p.y += v.y; - count ++; - }); - - s.run(); - - test_int(count, 5); - - test_assert(e1.get([](const Position& p) { - test_int(p.x, 11); - test_int(p.y, 22); - })); - - test_assert(e2.get([](const Position& p) { - test_int(p.x, 21); - test_int(p.y, 32); - })); - - test_assert(e3.get([](const Position& p) { - test_int(p.x, 31); - test_int(p.y, 42); - })); - - test_assert(e4.get([](const Position& p) { - test_int(p.x, 41); - test_int(p.y, 52); - })); - - test_assert(e5.get([](const Position& p) { - test_int(p.x, 51); - test_int(p.y, 62); - })); -} - -void System_un_instanced_query_w_base_each(void) { - flecs::world ecs; - - auto base = ecs.entity().set({1, 2}); - - auto e1 = ecs.entity().is_a(base).set({10, 20}); e1.set({e1}); - auto e2 = ecs.entity().is_a(base).set({20, 30}); e2.set({e2}); - auto e3 = ecs.entity().is_a(base).set({30, 40}); e3.set({e3}); - auto e4 = ecs.entity().is_a(base).set({40, 50}).add(); e4.set({e4}); - auto e5 = ecs.entity().is_a(base).set({50, 60}).add(); e5.set({e5}); - auto e6 = ecs.entity().set({60, 70}).set({2, 3}); e6.set({e6}); - auto e7 = ecs.entity().set({70, 80}).set({4, 5}); e7.set({e7}); - - int32_t count = 0; - auto s = ecs.system() .each([&](flecs::entity e, Self& s, Position&p, const Velocity& v) { test_assert(e == s.value); @@ -1440,149 +1424,21 @@ void System_instanced_query_w_singleton_iter(void) { int32_t count = 0; auto s = ecs.system() - .arg(3).singleton() - .instanced() - .iter([&](flecs::iter it, Self* s, Position* p, const Velocity* v) { - test_assert(it.count() > 1); - - for (auto i : it) { - p[i].x += v->x; - p[i].y += v->y; - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - s.run(); - - test_int(count, 5); - - test_assert(e1.get([](const Position& p) { - test_int(p.x, 11); - test_int(p.y, 22); - })); - - test_assert(e2.get([](const Position& p) { - test_int(p.x, 21); - test_int(p.y, 32); - })); - - test_assert(e3.get([](const Position& p) { - test_int(p.x, 31); - test_int(p.y, 42); - })); - - test_assert(e4.get([](const Position& p) { - test_int(p.x, 41); - test_int(p.y, 52); - })); + .term_at(2).singleton() + .run([&](flecs::iter it) { + while (it.next()) { + auto s = it.field(0); + auto p = it.field(1); + auto v = it.field(2); - test_assert(e5.get([](const Position& p) { - test_int(p.x, 51); - test_int(p.y, 62); - })); -} - -void System_instanced_query_w_base_iter(void) { - flecs::world ecs; - - auto base = ecs.entity().set({1, 2}); - - auto e1 = ecs.entity().is_a(base).set({10, 20}); e1.set({e1}); - auto e2 = ecs.entity().is_a(base).set({20, 30}); e2.set({e2}); - auto e3 = ecs.entity().is_a(base).set({30, 40}); e3.set({e3}); - auto e4 = ecs.entity().is_a(base).set({40, 50}).add(); e4.set({e4}); - auto e5 = ecs.entity().is_a(base).set({50, 60}).add(); e5.set({e5}); - auto e6 = ecs.entity().set({60, 70}).set({2, 3}); e6.set({e6}); - auto e7 = ecs.entity().set({70, 80}).set({4, 5}); e7.set({e7}); - - int32_t count = 0; - - auto s = ecs.system() - .instanced() - .iter([&](flecs::iter it, Self* s, Position* p, const Velocity* v) { - test_assert(it.count() > 1); + test_assert(it.count() > 1); - for (auto i : it) { - if (it.is_self(3)) { - p[i].x += v[i].x; - p[i].y += v[i].y; - } else { + for (auto i : it) { p[i].x += v->x; p[i].y += v->y; + test_assert(it.entity(i) == s[i].value); + count ++; } - - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - s.run(); - - test_int(count, 7); - - test_assert(e1.get([](const Position& p) { - test_int(p.x, 11); - test_int(p.y, 22); - })); - - test_assert(e2.get([](const Position& p) { - test_int(p.x, 21); - test_int(p.y, 32); - })); - - test_assert(e3.get([](const Position& p) { - test_int(p.x, 31); - test_int(p.y, 42); - })); - - test_assert(e4.get([](const Position& p) { - test_int(p.x, 41); - test_int(p.y, 52); - })); - - test_assert(e5.get([](const Position& p) { - test_int(p.x, 51); - test_int(p.y, 62); - })); - - test_assert(e6.get([](const Position& p) { - test_int(p.x, 62); - test_int(p.y, 73); - })); - - test_assert(e7.get([](const Position& p) { - test_int(p.x, 74); - test_int(p.y, 85); - })); -} - -void System_un_instanced_query_w_singleton_iter(void) { - flecs::world ecs; - - ecs.set({1, 2}); - - auto e1 = ecs.entity().set({10, 20}); e1.set({e1}); - auto e2 = ecs.entity().set({20, 30}); e2.set({e2}); - auto e3 = ecs.entity().set({30, 40}); e3.set({e3}); - auto e4 = ecs.entity().set({40, 50}); e4.set({e4}); - auto e5 = ecs.entity().set({50, 60}); e5.set({e5}); - - e4.add(); - e5.add(); - - int32_t count = 0; - - auto s = ecs.system() - .arg(3).singleton() - .iter([&](flecs::iter it, Self* s, Position* p, const Velocity* v) { - test_assert(it.count() == 1); - - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - test_assert(it.entity(i) == s[i].value); - count ++; } }); @@ -1616,7 +1472,7 @@ void System_un_instanced_query_w_singleton_iter(void) { })); } -void System_un_instanced_query_w_base_iter(void) { +void System_instanced_query_w_base_iter(void) { flecs::world ecs; auto base = ecs.entity().set({1, 2}); @@ -1632,12 +1488,25 @@ void System_un_instanced_query_w_base_iter(void) { int32_t count = 0; auto s = ecs.system() - .iter([&](flecs::iter it, Self* s, Position* p, const Velocity* v) { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - test_assert(it.entity(i) == s[i].value); - count ++; + .run([&](flecs::iter it) { + while (it.next()) { + auto s = it.field(0); + auto p = it.field(1); + auto v = it.field(2); + test_assert(it.count() > 1); + + for (auto i : it) { + if (it.is_self(2)) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } else { + p[i].x += v->x; + p[i].y += v->y; + } + + test_assert(it.entity(i) == s[i].value); + count ++; + } } }); @@ -1690,7 +1559,7 @@ void System_create_w_no_template_args(void) { int32_t count = 0; auto s = world.system() - .term() + .with() .each([&](flecs::entity e) { test_assert(e == entity); count ++; @@ -1716,8 +1585,8 @@ void System_system_w_type_kind_type_pipeline(void) { ); world.pipeline() - .term(flecs::System) - .term(flecs::Phase).cascade(flecs::DependsOn) + .with(flecs::System) + .with(flecs::Phase).cascade(flecs::DependsOn) .build(); world.set_pipeline(); @@ -1778,8 +1647,10 @@ void System_entity_ctor(void) { uint32_t invoked = 0; flecs::entity sys = world.system<>() - .iter([&](flecs::iter& it) { - invoked ++; + .run([&](flecs::iter& it) { + while (it.next()) { + invoked ++; + } }); auto sys_from_id = world.system(sys); @@ -1796,15 +1667,11 @@ void System_ensure_instanced_w_each(void) { int32_t count = 0; auto sys = world.system() .each([&](flecs::iter& it, size_t i, Position&) { - test_assert(it.c_ptr()->flags & EcsIterIsInstanced); test_assert(it.entity(i) == e1); count ++; }); auto q = sys.query(); - auto f = q.filter(); - const ecs_filter_t *c_f = f; - test_assert(c_f->flags & EcsIterIsInstanced); test_int(count, 0); sys.run(); @@ -1825,7 +1692,7 @@ void System_multithread_system_w_query_each(void) { world.system() .multi_threaded() .each([&](flecs::entity e, Position& p) { - q.each(e, [&](Velocity& v) { + q.iter(e).each([&](Velocity& v) { p.x += v.x; p.y += v.y; }); @@ -1852,7 +1719,7 @@ void System_multithread_system_w_query_each_w_iter(void) { world.system() .multi_threaded() .each([&](flecs::iter& it, size_t, Position& p) { - q.each(it, [&](Velocity& v) { + q.iter(it).each([&](Velocity& v) { p.x += v.x; p.y += v.y; }); @@ -1879,7 +1746,7 @@ void System_multithread_system_w_query_each_w_world(void) { world.system() .multi_threaded() .each([&](flecs::iter& it, size_t, Position& p) { - q.each(it.world(), [&](Velocity& v) { + q.iter(it.world()).each([&](Velocity& v) { p.x += v.x; p.y += v.y; }); @@ -1906,10 +1773,14 @@ void System_multithread_system_w_query_iter(void) { world.system() .multi_threaded() .each([&](flecs::entity e, Position& p) { - q.iter(e, [&](flecs::iter& it, Velocity* v) { - for (auto i : it) { - p.x += v[i].x; - p.y += v[i].y; + q.iter(e).run([&](flecs::iter& it) { + while (it.next()) { + auto v = it.field(0); + + for (auto i : it) { + p.x += v[i].x; + p.y += v[i].y; + } } }); }); @@ -1935,10 +1806,13 @@ void System_multithread_system_w_query_iter_w_iter(void) { world.system() .multi_threaded() .each([&](flecs::iter& it, size_t, Position& p) { - q.iter(it, [&](flecs::iter& it, Velocity* v) { - for (auto i : it) { - p.x += v[i].x; - p.y += v[i].y; + q.iter(it).run([&](flecs::iter& it) { + while (it.next()) { + auto v = it.field(0); + for (auto i : it) { + p.x += v[i].x; + p.y += v[i].y; + } } }); }); @@ -1964,10 +1838,13 @@ void System_multithread_system_w_query_iter_w_world(void) { world.system() .multi_threaded() .each([&](flecs::iter& it, size_t, Position& p) { - q.iter(it.world(), [&](flecs::iter& it, Velocity* v) { - for (auto i : it) { - p.x += v[i].x; - p.y += v[i].y; + q.iter(it.world()).run([&](flecs::iter& it) { + while (it.next()) { + auto v = it.field(0); + for (auto i : it) { + p.x += v[i].x; + p.y += v[i].y; + } } }); }); @@ -1987,17 +1864,16 @@ void System_run_callback(void) { .set({1, 2}); world.system() - .run([](flecs::iter_t *it) { - while (ecs_iter_next(it)) { - it->callback(it); - } - }) - .iter([](flecs::iter&it, Position *p, const Velocity* v) { - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; - } - }); + .run( + [](flecs::iter& it) { + while (it.next()) { + it.each(); + } + }, + [](Position& p, const Velocity& v) { + p.x += v.x; + p.y += v.y; + }); world.progress(); @@ -2017,16 +1893,20 @@ void System_startup_system(void) { ecs.system() .kind(flecs::OnStart) - .iter([&](flecs::iter& it) { - test_assert(it.delta_time() == 0); - count_a ++; + .run([&](flecs::iter& it) { + while (it.next()) { + test_assert(it.delta_time() == 0); + count_a ++; + } }); ecs.system() .kind(flecs::OnUpdate) - .iter([&](flecs::iter& it) { - test_assert(it.delta_time() != 0); - count_b ++; + .run([&](flecs::iter& it) { + while (it.next()) { + test_assert(it.delta_time() != 0); + count_b ++; + } }); ecs.progress(); @@ -2043,21 +1923,25 @@ void System_interval_tick_source(void) { flecs::timer t = ecs.timer().interval(2.1); - flecs::Timer *timer = t.get_mut(); - timer->time = 0; + flecs::Timer& timer = t.ensure(); + timer.time = 0; int32_t sys_a_invoked = 0, sys_b_invoked = 0; ecs.system() .tick_source(t) - .iter([&](flecs::iter& it) { - sys_a_invoked ++; + .run([&](flecs::iter& it) { + while (it.next()) { + sys_a_invoked ++; + } }); ecs.system() .tick_source(t) - .iter([&](flecs::iter& it) { - sys_b_invoked ++; + .run([&](flecs::iter& it) { + while (it.next()) { + sys_b_invoked ++; + } }); ecs.progress(1.0); @@ -2082,14 +1966,18 @@ void System_rate_tick_source(void) { ecs.system() .tick_source(t) - .iter([&](flecs::iter& it) { - sys_a_invoked ++; + .run([&](flecs::iter& it) { + while (it.next()) { + sys_a_invoked ++; + } }); ecs.system() .tick_source(t) - .iter([&](flecs::iter& it) { - sys_b_invoked ++; + .run([&](flecs::iter& it) { + while (it.next()) { + sys_b_invoked ++; + } }); ecs.progress(1.0); @@ -2115,14 +2003,18 @@ void System_nested_rate_tick_source(void) { ecs.system() .tick_source(t_3) - .iter([&](flecs::iter& it) { - sys_a_invoked ++; + .run([&](flecs::iter& it) { + while (it.next()) { + sys_a_invoked ++; + } }); ecs.system() .tick_source(t_6) - .iter([&](flecs::iter& it) { - sys_b_invoked ++; + .run([&](flecs::iter& it) { + while (it.next()) { + sys_b_invoked ++; + } }); ecs.progress(1.0); @@ -2205,7 +2097,7 @@ void System_randomize_timers(void) { flecs::entity s1 = ecs.system() .interval(1.0) - .iter([](flecs::iter& it) { }); + .run([](flecs::iter& it) { while (it.next()) {} }); { const flecs::Timer *t = s1.get(); @@ -2217,7 +2109,7 @@ void System_randomize_timers(void) { flecs::entity s2 = ecs.system() .interval(1.0) - .iter([](flecs::iter& it) { }); + .run([](flecs::iter& it) { while (it.next()) {} }); { const flecs::Timer *t = s1.get(); @@ -2236,15 +2128,15 @@ void System_optional_pair_term(void) { flecs::world ecs; ecs.entity() - .add() + .add() .emplace(1.0f, 2.0f); ecs.entity() - .add(); + .add(); int32_t with_pair = 0, without_pair = 0; ecs.system*>() - .with() + .with() .each([&](flecs::entity e, Position* p) { if (p) @@ -2264,3 +2156,172 @@ void System_optional_pair_term(void) { test_int(1, with_pair); test_int(1, without_pair); } + +void System_singleton_tick_source(void) { + flecs::world ecs; + + ecs.timer().timeout(1.5); + + int32_t sys_invoked = 0; + + ecs.system() + .tick_source() + .run([&](flecs::iter& it) { + sys_invoked ++; + }); + + ecs.progress(1.0); + test_int(0, sys_invoked); + + ecs.progress(1.0); + test_int(1, sys_invoked); + + ecs.progress(2.0); + test_int(1, sys_invoked); +} + +enum class PipelineStepEnum +{ + CustomStep, + CustomStep2 +}; + +void System_pipeline_step_with_kind_enum(void) { + flecs::world ecs; + + ecs.entity(PipelineStepEnum::CustomStep).add(flecs::Phase).depends_on(flecs::OnStart); + + bool ran_test = false; + + ecs.system().kind(PipelineStepEnum::CustomStep) + .run([&ran_test](flecs::iter& it) { + while (it.next()) { + ran_test = true; + } + }); + + ecs.progress(); + test_assert(ran_test); +} + +void System_pipeline_step_depends_on_pipeline_step_with_enum(void) { + flecs::world ecs; + + ecs.entity(PipelineStepEnum::CustomStep).add(flecs::Phase).depends_on(flecs::OnStart); + ecs.entity(PipelineStepEnum::CustomStep2).add(flecs::Phase).depends_on(PipelineStepEnum::CustomStep); + + bool ran_test = false; + + ecs.system().kind(PipelineStepEnum::CustomStep2) + .run([&ran_test](flecs::iter& it) { + while (it.next()) { + ran_test = true; + } + }); + + ecs.progress(); + test_assert(ran_test); +} + +void System_register_twice_w_each(void) { + flecs::world ecs; + + int count1 = 0, count2 = 0; + + flecs::system sys1 = ecs.system("Test") + .each([&](flecs::iter&, size_t) { + count1 ++; + }); + + sys1.run(); + test_int(count1, 1); + + flecs::system sys2 = ecs.system("Test") + .each([&](flecs::iter&, size_t) { + count2 ++; + }); + + sys2.run(); + test_int(count2, 1); +} + +void System_register_twice_w_run(void) { + flecs::world ecs; + + int count1 = 0, count2 = 0; + + flecs::system sys1 = ecs.system("Test") + .run([&](flecs::iter&) { + count1 ++; + }); + + sys1.run(); + test_int(count1, 1); + + flecs::system sys2 = ecs.system("Test") + .run([&](flecs::iter&) { + count2 ++; + }); + + sys2.run(); + test_int(count2, 1); +} + +void System_register_twice_w_run_each(void) { + flecs::world ecs; + + int count1 = 0, count2 = 0; + + flecs::system sys1 = ecs.system("Test") + .run([&](flecs::iter&) { + count1 ++; + }); + + sys1.run(); + test_int(count1, 1); + + flecs::system sys2 = ecs.system("Test") + .each([&](flecs::iter&, size_t) { + count2 ++; + }); + + sys2.run(); + test_int(count2, 1); +} + +void System_register_twice_w_each_run(void) { + flecs::world ecs; + + int count1 = 0, count2 = 0; + + flecs::system sys1 = ecs.system("Test") + .each([&](flecs::iter&, size_t) { + count1 ++; + }); + + sys1.run(); + test_int(count1, 1); + + flecs::system sys2 = ecs.system("Test") + .run([&](flecs::iter&) { + count2 ++; + }); + + sys2.run(); + test_int(count2, 1); +} + +void System_run_w_0_src_query(void) { + flecs::world world; + + int count = 0; + + world.system() + .write() + .run([&](flecs::iter&){ + count ++; + }); + + world.progress(); + test_int(count, 1); +} diff --git a/vendors/flecs/test/cpp_api/src/SystemBuilder.cpp b/vendors/flecs/test/cpp/src/SystemBuilder.cpp similarity index 72% rename from vendors/flecs/test/cpp_api/src/SystemBuilder.cpp rename to vendors/flecs/test/cpp/src/SystemBuilder.cpp index 6356f5a62..4ef55bc73 100644 --- a/vendors/flecs/test/cpp_api/src/SystemBuilder.cpp +++ b/vendors/flecs/test/cpp/src/SystemBuilder.cpp @@ -1,4 +1,4 @@ -#include +#include void SystemBuilder_builder_assign_same_type(void) { flecs::world ecs; @@ -48,8 +48,8 @@ void SystemBuilder_builder_build_n_statements(void) { int32_t count = 0; auto qb = ecs.system<>(); - qb.term(); - qb.term(); + qb.with(); + qb.with(); auto s = qb.each([&](flecs::entity e) { count ++; test_assert(e == e1); @@ -88,7 +88,7 @@ void SystemBuilder_add_1_type(void) { int32_t count = 0; auto s = ecs.system<>() - .term() + .with() .each([&](flecs::entity e) { count ++; test_assert(e == e1); @@ -108,8 +108,8 @@ void SystemBuilder_add_2_types(void) { int32_t count = 0; auto s = ecs.system<>() - .term() - .term() + .with() + .with() .each([&](flecs::entity e) { count ++; test_assert(e == e1); @@ -129,7 +129,7 @@ void SystemBuilder_add_1_type_w_1_type(void) { int32_t count = 0; auto s = ecs.system() - .term() + .with() .each([&](flecs::entity e, Position& p) { count ++; test_assert(e == e1); @@ -149,8 +149,8 @@ void SystemBuilder_add_2_types_w_1_type(void) { int32_t count = 0; auto s = ecs.system() - .term() - .term() + .with() + .with() .each([&](flecs::entity e, Position& p) { count ++; test_assert(e == e1); @@ -174,7 +174,7 @@ void SystemBuilder_add_pair(void) { int32_t count = 0; auto s = ecs.system<>() - .term(Likes, Bob) + .with(Likes, Bob) .each([&](flecs::entity e) { count ++; test_assert(e == e1); @@ -194,7 +194,7 @@ void SystemBuilder_add_not(void) { int32_t count = 0; auto s = ecs.system() - .term().oper(flecs::Not) + .with().oper(flecs::Not) .each([&](flecs::entity e, Position& p) { count ++; test_assert(e == e1); @@ -215,8 +215,8 @@ void SystemBuilder_add_or(void) { int32_t count = 0; auto s = ecs.system<>() - .term().oper(flecs::Or) - .term() + .with().oper(flecs::Or) + .with() .each([&](flecs::entity e) { count ++; test_assert(e == e1 || e == e2); @@ -237,8 +237,8 @@ void SystemBuilder_add_optional(void) { int32_t count = 0; auto s = ecs.system<>() - .term() - .term().oper(flecs::Optional) + .with() + .with().oper(flecs::Optional) .each([&](flecs::entity e) { count ++; test_assert(e == e1 || e == e2); @@ -324,18 +324,21 @@ void SystemBuilder_singleton_term(void) { int32_t count = 0; auto s = ecs.system() - .term().singleton() - .iter([&](flecs::iter& it, Entity *e) { - auto s = it.field(2); - test_assert(!it.is_self(2)); - test_int(s->value, 10); - - const Singleton& s_ref = *s; - test_int(s_ref.value, 10); - - for (auto i : it) { - test_assert(it.entity(i) == e[i].value); - count ++; + .with().singleton().in() + .run([&](flecs::iter& it) { + while (it.next()) { + auto e = it.field(0); + auto s = it.field(1); + test_assert(!it.is_self(1)); + test_int(s->value, 10); + + const Singleton& s_ref = *s; + test_int(s_ref.value, 10); + + for (auto i : it) { + test_assert(it.entity(i) == e[i].value); + count ++; + } } }); @@ -355,33 +358,35 @@ void SystemBuilder_10_terms(void) { int count = 0; auto e = ecs.entity() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add(); + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add(); auto s = ecs.system<>() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_assert(it.entity(0) == e); - test_int(it.field_count(), 10); - count ++; + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + test_assert(it.entity(0) == e); + test_int(it.field_count(), 10); + count ++; + } }); s.run(); @@ -389,59 +394,53 @@ void SystemBuilder_10_terms(void) { test_int(count, 1); } -void SystemBuilder_20_terms(void) { +void SystemBuilder_16_terms(void) { flecs::world ecs; int count = 0; auto e = ecs.entity() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add(); + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add() + .add(); auto s = ecs.system<>() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_assert(it.entity(0) == e); - test_int(it.field_count(), 20); - count ++; + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .with() + .run([&](flecs::iter& it) { + while (it.next()) { + test_int(it.count(), 1); + test_assert(it.entity(0) == e); + test_int(it.field_count(), 16); + count ++; + } }); s.run(); @@ -453,9 +452,10 @@ void SystemBuilder_name_arg(void) { flecs::world ecs; auto s = ecs.system("MySystem") - .arg(1).src().name("MySystem") - .iter([](flecs::iter& Iter, const Position* Config) - { }); + .term_at(0).src().name("MySystem") + .run([](flecs::iter& it) { + while (it.next()) {} + }); test_assert(s.has()); } @@ -468,7 +468,7 @@ void SystemBuilder_create_w_no_template_args(void) { int32_t count = 0; auto s = ecs.system() - .term() + .with() .each([&](flecs::entity e) { count ++; test_assert(e == e1); @@ -482,26 +482,26 @@ void SystemBuilder_create_w_no_template_args(void) { void SystemBuilder_write_annotation(void) { flecs::world ecs; - struct TagA { }; - struct TagB { }; + struct Tag0 { }; + struct Tag1 { }; - auto e1 = ecs.entity().add(); + auto e1 = ecs.entity().add(); int32_t a_count = 0, b_count = 0; - ecs.system() - .term().write() - .each([&](flecs::entity e, TagA) { + ecs.system() + .with().write() + .each([&](flecs::entity e, Tag0) { a_count ++; test_assert(e == e1); - e.add(); + e.add(); }); - ecs.system() - .each([&](flecs::entity e, TagB) { + ecs.system() + .each([&](flecs::entity e, Tag1) { b_count ++; test_assert(e == e1); - test_assert(e.has()); + test_assert(e.has()); }); test_int(a_count, 0); @@ -512,7 +512,7 @@ void SystemBuilder_write_annotation(void) { test_int(a_count, 1); test_int(b_count, 1); - test_assert(e1.has()); + test_assert(e1.has()); } void SystemBuilder_name_from_root(void) { diff --git a/vendors/flecs/test/cpp_api/src/Table.cpp b/vendors/flecs/test/cpp/src/Table.cpp similarity index 94% rename from vendors/flecs/test/cpp_api/src/Table.cpp rename to vendors/flecs/test/cpp/src/Table.cpp index c3b2baf19..042d61bbb 100644 --- a/vendors/flecs/test/cpp_api/src/Table.cpp +++ b/vendors/flecs/test/cpp/src/Table.cpp @@ -1,4 +1,4 @@ -#include +#include void Table_each(void) { flecs::world ecs; @@ -6,7 +6,7 @@ void Table_each(void) { ecs.entity().add(); auto e2 = ecs.entity().add(); - ecs.filter() + ecs.query() .each([&](flecs::entity e, Position& p) { e2.add(); }); @@ -21,7 +21,7 @@ void Table_each_locked(void) { auto e1 = ecs.entity().add(); - ecs.filter() + ecs.query() .each([&](flecs::entity e, Position& p) { test_expect_abort(); e1.add(); @@ -36,7 +36,7 @@ void Table_each_without_entity(void) { ecs.entity().add(); auto e2 = ecs.entity().add(); - ecs.filter() + ecs.query() .each([&](Position& p) { e2.add(); }); @@ -51,7 +51,7 @@ void Table_each_without_entity_locked(void) { auto e1 = ecs.entity().add(); - ecs.filter() + ecs.query() .each([&](Position& p) { test_expect_abort(); e1.add(); @@ -66,9 +66,11 @@ void Table_iter(void) { ecs.entity().add(); auto e2 = ecs.entity().add(); - ecs.filter() - .iter([&](flecs::iter& it, Position* p) { - e2.add(); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + e2.add(); + } }); test_assert(e2.has()); @@ -81,10 +83,12 @@ void Table_iter_locked(void) { auto e1 = ecs.entity().add(); - ecs.filter() - .iter([&](flecs::iter& it, Position* p) { - test_expect_abort(); - e1.add(); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + test_expect_abort(); + e1.add(); + } }); test_assert(false); @@ -96,9 +100,11 @@ void Table_iter_without_components(void) { ecs.entity().add(); auto e2 = ecs.entity().add(); - ecs.filter() - .iter([&](flecs::iter& it) { - e2.add(); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + e2.add(); + } }); test_assert(e2.has()); @@ -111,10 +117,12 @@ void Table_iter_without_components_locked(void) { auto e1 = ecs.entity().add(); - ecs.filter() - .iter([&](flecs::iter& it) { - test_expect_abort(); - e1.add(); + ecs.query() + .run([&](flecs::iter& it) { + while (it.next()) { + test_expect_abort(); + e1.add(); + } }); test_assert(false); @@ -155,7 +163,7 @@ void Table_multi_set(void) { auto e1 = ecs.entity().add().add(); auto e2 = ecs.entity().add(); - e1.set([&](Position& p, Velocity& v) { + e1.insert([&](Position& p, Velocity& v) { e2.add(); }); @@ -170,7 +178,7 @@ void Table_multi_set_locked(void) { auto e1 = ecs.entity().add().add(); auto e2 = ecs.entity().add(); - e1.set([&](Position& p, Velocity& v) { + e1.insert([&](Position& p, Velocity& v) { test_expect_abort(); e2.add(); }); diff --git a/vendors/flecs/test/cpp_api/src/Trigger.cpp b/vendors/flecs/test/cpp/src/Trigger.cpp similarity index 100% rename from vendors/flecs/test/cpp_api/src/Trigger.cpp rename to vendors/flecs/test/cpp/src/Trigger.cpp diff --git a/vendors/flecs/test/cpp/src/Union.cpp b/vendors/flecs/test/cpp/src/Union.cpp new file mode 100644 index 000000000..7df651331 --- /dev/null +++ b/vendors/flecs/test/cpp/src/Union.cpp @@ -0,0 +1,135 @@ +#include + +void Union_add_case(void) { + flecs::world world; + + auto Standing = world.entity("Standing"); + auto Walking = world.entity("Walking"); + auto Movement = world.entity().add(flecs::Union); + + auto e = world.entity() + .add(Movement, Standing); + test_assert(e.has(Movement, Standing)); + + auto table = e.table(); + + e.add(Movement, Walking); + test_assert(e.table() == table); + + test_assert(e.has(Movement, Walking)); + test_assert(!e.has(Movement, Standing)); +} + +void Union_get_case(void) { + flecs::world world; + + auto Standing = world.entity("Standing"); + world.entity("Walking"); + auto Movement = world.entity().add(flecs::Union); + + auto e = world.entity() + .add(Movement, Standing); + test_assert(e.has(Movement, Standing)); + + test_assert(e.target(Movement) == Standing); +} + +struct Movement { }; +struct Standing { }; +struct Walking { }; + +void Union_add_case_w_type(void) { + flecs::world world; + + world.component().add(flecs::Union); + + auto e = world.entity().add(); + test_assert((e.has())); + + e.add(); + + test_assert((e.has())); + test_assert((!e.has())); +} + +void Union_add_switch_w_type(void) { + flecs::world world; + + world.component().add(flecs::Union); + + auto e = world.entity().add(); + test_assert((e.has())); + + e.add(); + + test_assert((e.has())); + test_assert((!e.has())); +} + +void Union_add_remove_switch_w_type(void) { + flecs::world world; + + world.component().add(flecs::Union); + + auto e = world.entity().add(); + test_assert(e.has(flecs::Wildcard)); + test_assert((e.has())); + + auto table = e.table(); + + e.add(); + + test_assert((e.has())); + test_assert((!e.has())); + test_assert(e.table() == table); + + auto c = e.target(); + test_assert(c != 0); + test_assert(c == world.id()); + + e.remove(flecs::Wildcard); + test_assert(!e.has(flecs::Wildcard)); + test_assert((!e.has())); + test_assert(e.table() != table); +} + +enum Color { + Red, + Green, + Blue +}; + +void Union_switch_enum_type(void) { + flecs::world world; + + world.component().add(flecs::Union); + + auto e = world.entity().add(Red); + test_assert(e.has(Red)); + test_assert(!e.has(Green)); + test_assert(!e.has(Blue)); + test_assert(e.has(flecs::Wildcard)); + + auto table = e.table(); + + e.add(Green); + test_assert(!e.has(Red)); + test_assert(e.has(Green)); + test_assert(!e.has(Blue)); + test_assert(e.has(flecs::Wildcard)); + test_assert(e.table() == table); + + e.add(Blue); + test_assert(!e.has(Red)); + test_assert(!e.has(Green)); + test_assert(e.has(Blue)); + test_assert(e.has(flecs::Wildcard)); + test_assert(e.table() == table); + + e.remove(); + test_assert(!e.has(Red)); + test_assert(!e.has(Green)); + test_assert(!e.has(Blue)); + test_assert(!e.has(flecs::Wildcard)); + test_assert(e.table() != table); +} diff --git a/vendors/flecs/test/cpp_api/src/World.cpp b/vendors/flecs/test/cpp/src/World.cpp similarity index 85% rename from vendors/flecs/test/cpp_api/src/World.cpp rename to vendors/flecs/test/cpp/src/World.cpp index aaafc0c20..838eade12 100644 --- a/vendors/flecs/test/cpp_api/src/World.cpp +++ b/vendors/flecs/test/cpp/src/World.cpp @@ -1,4 +1,4 @@ -#include +#include void World_multi_world_empty(void) { flecs::world *w1 = new flecs::world(); @@ -75,13 +75,23 @@ namespace ns { int namespace_module::system_invoke_count = 0; } +struct nested_component_module { + struct Foo { + struct Bar { }; + }; + + nested_component_module(flecs::world& ecs) { + ecs.component(); + ecs.component(); + } +}; + void World_builtin_components(void) { flecs::world ecs; test_assert(ecs.component() == ecs_id(EcsComponent)); test_assert(ecs.component() == ecs_id(EcsIdentifier)); test_assert(ecs.component() == ecs_id(EcsPoly)); - test_assert(ecs.component() == ecs_id(EcsTarget)); test_assert(ecs.component() == ecs_id(EcsRateFilter)); test_assert(ecs.component() == ecs_id(EcsTickSource)); test_assert(flecs::Name == EcsName); @@ -211,7 +221,7 @@ void World_reregister_after_reset(void) { auto p1 = ecs.component("Position"); // Simulate different binary - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); auto p2 = ecs.component("Position"); @@ -226,7 +236,7 @@ void World_implicit_reregister_after_reset(void) { flecs::entity_t p_id_1 = flecs::type_id(); // Simulate different binary - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); ecs.entity().add(); @@ -243,7 +253,7 @@ void World_reregister_after_reset_w_namespace(void) { flecs::entity_t p_id_1 = flecs::type_id(); // Simulate different binary - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); ecs.component(); @@ -276,7 +286,7 @@ void World_reregister_after_reset_different_name(void) { ecs.component("Position"); // Simulate different binary - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); ecs.component("Velocity"); } @@ -289,7 +299,7 @@ void World_register_component_w_reset_in_multithreaded(void) { flecs::entity pos = ecs.component(); flecs::entity e = ecs.entity(); - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); ecs.readonly_begin(); e.set({10, 20}); @@ -346,7 +356,7 @@ void World_reimport_module_after_reset(void) { auto m1 = ecs.import(); // Simulate different binary - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); auto m2 = ecs.import(); @@ -404,7 +414,7 @@ void World_c_interop_after_reset(void) { auto e_pos = ecs.lookup("test::interop::module::Position"); test_assert(e_pos.id() != 0); - flecs::_::cpp_type::reset(); + flecs::_::type::reset(); ecs.import(); } @@ -631,7 +641,7 @@ void World_with_tag(void) { auto self = ecs.component(); test_assert(!self.has(Tag)); - auto q = ecs.query_builder<>().term(Tag).build(); + auto q = ecs.query_builder<>().with(Tag).build(); int32_t count = 0; @@ -664,7 +674,7 @@ void World_with_tag_type(void) { auto self = ecs.component(); test_assert(!self.has()); - auto q = ecs.query_builder<>().term().build(); + auto q = ecs.query_builder<>().with().build(); int32_t count = 0; @@ -698,7 +708,7 @@ void World_with_relation(void) { auto self = ecs.component(); test_assert(!self.has(Likes, Bob)); - auto q = ecs.query_builder<>().term(Likes, Bob).build(); + auto q = ecs.query_builder<>().with(Likes, Bob).build(); int32_t count = 0; @@ -732,7 +742,7 @@ void World_with_relation_type(void) { auto self = ecs.component(); test_assert(!self.has(Bob)); - auto q = ecs.query_builder<>().term(Bob).build(); + auto q = ecs.query_builder<>().with(Bob).build(); int32_t count = 0; @@ -766,7 +776,7 @@ void World_with_relation_object_type(void) { auto self = ecs.component(); test_assert(!(self.has())); - auto q = ecs.query_builder<>().term().build(); + auto q = ecs.query_builder<>().with().build(); int32_t count = 0; @@ -821,7 +831,7 @@ void World_with_scope(void) { test_assert(!self.has(flecs::ChildOf, parent)); int count = 0; - auto q = ecs.query_builder<>().term(flecs::ChildOf, parent).build(); + auto q = ecs.query_builder<>().with(flecs::ChildOf, parent).build(); q.each([&](flecs::entity e) { test_assert(e.has(flecs::ChildOf, parent)); @@ -973,7 +983,7 @@ void World_type_w_tag_name(void) { auto c = ecs.component(); test_assert(c != flecs::entity()); test_str(c.path().c_str(), "::Tag"); - test_assert(c != flecs::Tag); + test_assert(c != flecs::PairIsTag); } void World_entity_w_tag_name(void) { @@ -982,7 +992,7 @@ void World_entity_w_tag_name(void) { auto c = ecs.entity("Tag"); test_assert(c != flecs::entity()); test_str(c.path().c_str(), "::Tag"); - test_assert(c != flecs::Tag); + test_assert(c != flecs::PairIsTag); } template @@ -1469,12 +1479,16 @@ void World_is_valid(void) { auto e = ecs.entity(); test_bool(ecs.is_valid(e), true); - test_bool(ecs.is_valid(1000), true); + test_bool(ecs.is_valid(1000), false); test_bool(ecs.is_valid(0), false); e.destruct(); test_bool(ecs.is_valid(e), false); + + ecs.make_alive(1000); + + test_bool(ecs.is_valid(1000), true); } void World_exists(void) { @@ -1501,7 +1515,7 @@ void World_get_alive(void) { test_assert(ecs.get_alive(e_no_gen) == e_2); } -void World_ensure(void) { +void World_make_alive(void) { flecs::world ecs; auto e_1 = ecs.entity(); @@ -1514,7 +1528,7 @@ void World_ensure(void) { e_2.destruct(); test_assert(!e_2.is_alive()); - auto e_3 = ecs.ensure(e_2); + auto e_3 = ecs.make_alive(e_2); test_assert(e_2 == e_3); test_assert(e_3.is_alive()); } @@ -1629,12 +1643,14 @@ void World_run_post_frame(void) { int ctx = 10; ecs.system() - .iter([&](flecs::iter& it) { - it.world().run_post_frame([](flecs::world_t *w, void *ctx) { - int *i = static_cast(ctx); - test_int(*i, 10); - i[0] ++; - }, &ctx); + .run([&](flecs::iter& it) { + while (it.next()) { + it.world().run_post_frame([](flecs::world_t *w, void *ctx) { + int *i = static_cast(ctx); + test_int(*i, 10); + i[0] ++; + }, &ctx); + } }); test_int(ctx, 10); @@ -1841,3 +1857,194 @@ void World_delta_time(void) { test_int(dt, 2); } + +void World_register_nested_component_in_module(void) { + flecs::world ecs; + + ecs.import(); + + test_assert(flecs::type_id() != 0); + test_assert(flecs::type_id() != 0); + + flecs::entity foo = ecs.component(); + flecs::entity bar = ecs.component(); + + test_str(foo.path().c_str(), "::nested_component_module::Foo"); + test_str(bar.path().c_str(), "::nested_component_module::Foo::Bar"); +} + +static void *atfini_ctx = nullptr; +static int atfini_invoked = 0; +static void atfini_callback(flecs::world_t *world, void *ctx) { + test_assert(world != nullptr); + atfini_ctx = ctx; + atfini_invoked ++; +} + +void World_atfini(void) { + { + flecs::world ecs; + ecs.atfini(atfini_callback); + } + test_int(atfini_invoked, 1); + test_assert(atfini_ctx == nullptr); +} + +void World_atfini_w_ctx(void) { + int ctx; + { + flecs::world ecs; + ecs.atfini(atfini_callback, &ctx); + } + test_int(atfini_invoked, 1); + test_assert(atfini_ctx == &ctx); +} + +void World_get_mut_T(void) { + flecs::world world; + + Position *p = world.get_mut(); + + test_assert(p == nullptr); + + world.set({10, 20}); + p = world.get_mut(); + + test_assert(p != nullptr); + test_int(p->x, 10); + test_int(p->y, 20); +} + +void World_get_mut_R_T(void) { + flecs::world world; + + struct Tgt { }; + + Position *p = world.get_mut(); + test_assert(p == nullptr); + + world.set({10, 20}); + + p = world.get_mut(); + test_assert(p != nullptr); + + test_int(p->x, 10); + test_int(p->y, 20); +} + +void World_world_mini(void) { + int32_t count = 0; + { + flecs::world world(ecs_mini()); + world.make_owner(); + + world.atfini([](flecs::world_t* world, void *ctx) { + int32_t *data = static_cast(ctx); + data[0] ++; + }, &count); + + test_assert(world.lookup("flecs.system") == 0); + test_assert(world.lookup("flecs.pipeline") == 0); + test_assert(world.lookup("flecs.timer") == 0); + test_assert(world.lookup("flecs.meta") == 0); + } + test_int(count, 1); +} + +void World_copy_world(void) { + flecs::world world_1; + flecs::world world_2 = world_1; + + test_assert(world_1.c_ptr() == world_2.c_ptr()); +} + +void World_fini_reentrancy(void) { + { + struct A { + int a; + }; + + flecs::world world; + + // declare on remove hook for component A: + world.component().on_remove([&world](flecs::entity e, A &) { // + // This code runs on world destroy, since we did not remove this component manually before the world was destroyed. + + // before we make a copy of the world, the refcount has to be 1 since this is the special case where + // we will be copying a world object precisely when the world is being destroyed. + // see world::~world() code and notes. + const ecs_header_t *hdr = (const ecs_header_t*) world.c_ptr(); + test_assert(hdr->refcount == 1); + + // obtain the entity's world. This increments the world's hdr refcount + flecs::world world_copy = e.world(); + + test_assert(hdr->refcount == 2); + // here world_copy object wrapping c world is destroyed + // therefore, world destroy will be called again wreaking havoc. + }); + + world.entity().add(); + + // world will be destroyed here, and hook above will be called. + } + test_assert(true); // if this runs, ecs_fini did not abort and we're good. + +} + +// Test whether overwriting a world instance with another via +// copy-assign or move-assign properly destroys the overwritten instance +void World_fini_copy_move_assign(void) { + // helper component to tag worlds: + struct ID { + int id; + }; + + auto fini_handler = [](ecs_world_t *world, void *ctx) { + bool *finished = (bool *) ctx; + *finished = true; + }; + + auto get_world_id = [](const flecs::world &world) { return world.get()->id; }; + + bool finished_1 = false; + bool finished_2 = false; + bool finished_3 = false; + + // Create three worlds '1', '2' and '3', that initially start in 'A', 'B' and 'C' respectively: + flecs::world world_A; + world_A.set({1}); + world_A.atfini(fini_handler, &finished_1); + + flecs::world world_B; + world_B.set(ID{2}); + world_B.atfini(fini_handler, &finished_2); + + flecs::world world_C; + world_C.set(ID{3}); + world_C.atfini(fini_handler, &finished_3); + + // now overwrite one with another using copy-assign + world_A = world_B; // (copy assign). Overwrite world_A with world_B + // world with ID '1' contained in world_A should have been destroyed: + test_assert(finished_1 == true); + + // now world_A and world_B point to the same world '2' + test_assert(world_A.c_ptr() == world_B.c_ptr()); + test_assert(get_world_id(world_A) == 2); + test_assert(get_world_id(world_B) == 2); + + // test move-assign properly releases existing world '3' in C: + world_C = FLECS_MOV(world_B); // move-assign, overwrite what is in C. + test_assert(world_B.c_ptr() == nullptr); // B is now empty + test_assert(finished_2 == false); // still '2' is alive (in 'A') + test_assert(finished_3 == true); // world '3' destroyed after being overwritten + test_assert(get_world_id(world_C) == 2); // world '2' is now in 'C' + + world_A.release(); + test_assert(world_A.c_ptr() == nullptr); // A is now empty + test_assert(finished_2 == false); // '2' is still alive in 'C', though. + + world_C.release(); + test_assert(finished_2 == true); // '2' is now finished as well. +} diff --git a/vendors/flecs/test/cpp_api/src/WorldFactory.cpp b/vendors/flecs/test/cpp/src/WorldFactory.cpp similarity index 82% rename from vendors/flecs/test/cpp_api/src/WorldFactory.cpp rename to vendors/flecs/test/cpp/src/WorldFactory.cpp index e1ef0e2d8..b9f86fb60 100644 --- a/vendors/flecs/test/cpp_api/src/WorldFactory.cpp +++ b/vendors/flecs/test/cpp/src/WorldFactory.cpp @@ -1,4 +1,4 @@ -#include +#include void WorldFactory_entity(void) { flecs::world ecs; @@ -92,13 +92,15 @@ void WorldFactory_system_w_expr(void) { auto s = ecs.system<>("MySystem") .expr("Position, [in] Velocity") - .iter([](flecs::iter it) { - flecs::column p(it, 1); - flecs::column v(it, 2); - - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + .run([](flecs::iter it) { + while (it.next()) { + flecs::field p(it, 0); + flecs::field v(it, 1); + + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -147,13 +149,15 @@ void WorldFactory_query_w_expr(void) { .set({10, 20}) .set({1, 2}); - q.iter([](flecs::iter it) { - flecs::column p(it, 1); - flecs::column v(it, 2); + q.run([](flecs::iter it) { + while (it.next()) { + flecs::field p(it, 0); + flecs::field v(it, 1); - for (auto i : it) { - p[i].x += v[i].x; - p[i].y += v[i].y; + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } } }); @@ -162,27 +166,6 @@ void WorldFactory_query_w_expr(void) { test_int(p->y, 22); } -void WorldFactory_snapshot(void) { - flecs::world ecs; - - ecs.component(); - ecs.component(); - - auto e = ecs.entity() - .set({10, 20}) - .set({1, 2}); - - auto s = ecs.snapshot(); - - e.set({11, 22}); - - s.restore(); - - const Position *p = e.get(); - test_int(p->x, 11); - test_int(p->y, 22); -} - class MyModule { public: MyModule(flecs::world& ecs) { diff --git a/vendors/flecs/test/cpp_api/src/main.cpp b/vendors/flecs/test/cpp/src/main.cpp similarity index 81% rename from vendors/flecs/test/cpp_api/src/main.cpp rename to vendors/flecs/test/cpp/src/main.cpp index 072b0ae15..3ff9913bb 100644 --- a/vendors/flecs/test/cpp_api/src/main.cpp +++ b/vendors/flecs/test/cpp/src/main.cpp @@ -6,7 +6,7 @@ * ---------------------------------------------------------------------------- */ -#include +#include // Testsuite 'PrettyFunction' void PrettyFunction_component(void); @@ -43,11 +43,17 @@ void Entity_emplace_pair_w_entity(void); void Entity_emplace_pair_type(void); void Entity_emplace_pair_second(void); void Entity_get_generic(void); -void Entity_get_mut_generic(void); +void Entity_ensure_generic(void); void Entity_get_generic_w_id(void); void Entity_get_generic_w_id_t(void); -void Entity_get_mut_generic_w_id(void); -void Entity_get_mut_generic_w_id_t(void); +void Entity_ensure_generic_w_id(void); +void Entity_ensure_generic_w_id_t(void); +void Entity_get_mut_w_id(void); +void Entity_get_mut_T(void); +void Entity_get_mut_r_t(void); +void Entity_get_mut_R_t(void); +void Entity_get_mut_R_T(void); +void Entity_get_mut_r_T(void); void Entity_set_generic(void); void Entity_set_generic_w_id(void); void Entity_set_generic_w_id_t(void); @@ -130,10 +136,10 @@ void Entity_get_2_components_w_callback(void); void Entity_set_1_component_w_callback(void); void Entity_set_2_components_w_callback(void); void Entity_set_3_components_w_callback(void); -void Entity_get_mut_1_component_w_callback(void); -void Entity_get_mut_2_components_w_callback(void); +void Entity_ensure_1_component_w_callback(void); +void Entity_ensure_2_components_w_callback(void); void Entity_get_component_w_callback_nested(void); -void Entity_get_mut_component_w_callback_nested(void); +void Entity_ensure_component_w_callback_nested(void); void Entity_defer_set_1_component(void); void Entity_defer_set_2_components(void); void Entity_defer_set_3_components(void); @@ -166,6 +172,7 @@ void Entity_defer_new_w_with(void); void Entity_defer_new_w_name_scope_with(void); void Entity_defer_w_with_implicit_component(void); void Entity_defer_suspend_resume(void); +void Entity_defer_ensure(void); void Entity_entity_id_str(void); void Entity_pair_id_str(void); void Entity_role_id_str(void); @@ -260,6 +267,28 @@ void Entity_emplace_w_observer(void); void Entity_scoped_world(void); void Entity_entity_lookup_not_recursive(void); void Entity_world_lookup_not_recursive(void); +void Entity_world_lookup_custom_sep(void); +void Entity_world_lookup_custom_root_sep(void); +void Entity_depends_on(void); +void Entity_depends_on_type(void); +void Entity_const_entity_add_remove(void); +void Entity_const_entity_set(void); +void Entity_const_entity_get_mut(void); +void Entity_const_entity_ensure(void); +void Entity_const_entity_destruct(void); +void Entity_const_entity_emit_after_build(void); +void Entity_const_entity_set_doc(void); +void Entity_set_sparse(void); +void Entity_insert_1_sparse(void); +void Entity_insert_2_w_1_sparse(void); +void Entity_emplace_sparse(void); +void Entity_override_sparse(void); +void Entity_delete_w_override_sparse(void); +void Entity_get_pair_second_invalid_type(void); +void Entity_get_mut_pair_second_invalid_type(void); +void Entity_ensure_pair_second_invalid_type(void); +void Entity_set_pair_second_invalid_type(void); +void Entity_get_ref_pair_second_invalid_type(void); // Testsuite 'Pairs' void Pairs_add_component_pair(void); @@ -274,11 +303,11 @@ void Pairs_system_1_pair_instance(void); void Pairs_system_2_pair_instances(void); void Pairs_override_pair(void); void Pairs_override_tag_pair(void); -void Pairs_get_mut_pair(void); -void Pairs_get_mut_pair_existing(void); -void Pairs_get_mut_pair_tag(void); -void Pairs_get_mut_pair_tag_existing(void); -void Pairs_get_mut_R_tag_O(void); +void Pairs_ensure_pair(void); +void Pairs_ensure_pair_existing(void); +void Pairs_ensure_pair_tag(void); +void Pairs_ensure_pair_tag_existing(void); +void Pairs_ensure_R_tag_O(void); void Pairs_get_relation_from_id(void); void Pairs_get_second_from_id(void); void Pairs_get_recycled_relation_from_id(void); @@ -329,10 +358,16 @@ void Pairs_deref_const_pair(void); void Pairs_deref_pair_obj(void); void Pairs_deref_const_pair_obj(void); void Pairs_set_R_existing_value(void); +void Pairs_symmetric_w_childof(void); +void Pairs_modified_tag_second(void); +void Pairs_modified_tag_first(void); // Testsuite 'Enum' void Enum_standard_enum_reflection(void); void Enum_sparse_enum_reflection(void); +void Enum_bitmask_enum_reflection(void); +void Enum_bitmask_enum_with_type_reflection(void); +void Enum_enum_with_mixed_constants_and_bitmask(void); void Enum_enum_class_reflection(void); void Enum_prefixed_enum_reflection(void); void Enum_constant_with_num_reflection(void); @@ -368,17 +403,13 @@ void Enum_mixed_auto_manual_constants(void); void Enum_enum_class_mixed_auto_manual_constants(void); void Enum_enum_child_count(void); -// Testsuite 'Switch' -void Switch_add_case(void); -void Switch_get_case(void); -void Switch_system_w_case(void); -void Switch_system_w_case_builder(void); -void Switch_system_w_sw_type_builder(void); -void Switch_system_w_switch(void); -void Switch_add_case_w_type(void); -void Switch_add_switch_w_type(void); -void Switch_add_remove_switch_w_type(void); -void Switch_switch_enum_type(void); +// Testsuite 'Union' +void Union_add_case(void); +void Union_get_case(void); +void Union_add_case_w_type(void); +void Union_add_switch_w_type(void); +void Union_add_remove_switch_w_type(void); +void Union_switch_enum_type(void); // Testsuite 'Paths' void Paths_name(void); @@ -396,6 +427,16 @@ void Paths_alias_entity(void); void Paths_alias_entity_by_name(void); void Paths_alias_entity_by_scoped_name(void); void Paths_alias_entity_empty(void); +void Paths_id_from_str_0_entity(void); +void Paths_id_from_str_entity_from_str(void); +void Paths_id_from_str_unresolved_entity_from_str(void); +void Paths_id_from_str_scoped_entity_from_str(void); +void Paths_id_from_str_template_entity_from_str(void); +void Paths_id_from_str_pair_from_str(void); +void Paths_id_from_str_unresolved_pair_from_str(void); +void Paths_id_from_str_wildcard_pair_from_str(void); +void Paths_id_from_str_any_pair_from_str(void); +void Paths_id_from_str_invalid_pair(void); // Testsuite 'System' void System_iter(void); @@ -432,6 +473,7 @@ void System_new_from_iter(void); void System_each_w_mut_children_it(void); void System_readonly_children_iter(void); void System_rate_filter(void); +void System_self_rate_filter(void); void System_update_rate_filter(void); void System_default_ctor(void); void System_test_auto_defer_each(void); @@ -440,12 +482,8 @@ void System_custom_pipeline(void); void System_custom_pipeline_w_kind(void); void System_instanced_query_w_singleton_each(void); void System_instanced_query_w_base_each(void); -void System_un_instanced_query_w_singleton_each(void); -void System_un_instanced_query_w_base_each(void); void System_instanced_query_w_singleton_iter(void); void System_instanced_query_w_base_iter(void); -void System_un_instanced_query_w_singleton_iter(void); -void System_un_instanced_query_w_base_iter(void); void System_create_w_no_template_args(void); void System_system_w_type_kind_type_pipeline(void); void System_default_ctor(void); @@ -466,6 +504,14 @@ void System_table_get(void); void System_range_get(void); void System_randomize_timers(void); void System_optional_pair_term(void); +void System_singleton_tick_source(void); +void System_pipeline_step_with_kind_enum(void); +void System_pipeline_step_depends_on_pipeline_step_with_enum(void); +void System_register_twice_w_each(void); +void System_register_twice_w_run(void); +void System_register_twice_w_run_each(void); +void System_register_twice_w_each_run(void); +void System_run_w_0_src_query(void); // Testsuite 'Event' void Event_evt_1_id_entity(void); @@ -496,6 +542,9 @@ void Event_enqueue_event(void); void Event_enqueue_entity_event(void); void Event_enqueue_event_w_payload(void); void Event_enqueue_entity_event_w_payload(void); +void Event_enqueue_entity_from_readonly_world(void); +void Event_enqueue_entity_w_payload_from_readonly_world(void); +void Event_enum_event(void); // Testsuite 'Iterable' void Iterable_page_each(void); @@ -504,26 +553,37 @@ void Iterable_worker_each(void); void Iterable_worker_iter(void); // Testsuite 'Query' -void Query_action(void); -void Query_action_const(void); -void Query_action_shared(void); -void Query_action_optional(void); +void Query_term_each_component(void); +void Query_term_each_tag(void); +void Query_term_each_id(void); +void Query_term_each_pair_type(void); +void Query_term_each_pair_id(void); +void Query_term_each_pair_relation_wildcard(void); +void Query_term_each_pair_object_wildcard(void); +void Query_term_get_id(void); +void Query_term_get_subj(void); +void Query_term_get_pred(void); +void Query_term_get_obj(void); +void Query_set_this_var(void); +void Query_run(void); +void Query_run_const(void); +void Query_run_shared(void); +void Query_run_optional(void); +void Query_run_sparse(void); void Query_each(void); void Query_each_const(void); void Query_each_shared(void); void Query_each_optional(void); +void Query_each_sparse(void); void Query_signature(void); void Query_signature_const(void); void Query_signature_shared(void); void Query_signature_optional(void); -void Query_subquery(void); -void Query_subquery_w_expr(void); void Query_query_single_pair(void); void Query_tag_w_each(void); void Query_shared_tag_w_each(void); void Query_sort_by(void); void Query_changed(void); -void Query_orphaned(void); void Query_default_ctor(void); void Query_default_ctor_no_assign(void); void Query_expr_w_template(void); @@ -553,12 +613,8 @@ void Query_iter_query_in_system(void); void Query_iter_type(void); void Query_instanced_query_w_singleton_each(void); void Query_instanced_query_w_base_each(void); -void Query_un_instanced_query_w_singleton_each(void); -void Query_un_instanced_query_w_base_each(void); void Query_instanced_query_w_singleton_iter(void); void Query_instanced_query_w_base_iter(void); -void Query_un_instanced_query_w_singleton_iter(void); -void Query_un_instanced_query_w_base_iter(void); void Query_query_each_from_component(void); void Query_query_iter_from_component(void); void Query_query_each_w_func_ptr(void); @@ -566,6 +622,14 @@ void Query_query_iter_w_func_ptr(void); void Query_query_each_w_func_no_ptr(void); void Query_query_iter_w_func_no_ptr(void); void Query_query_each_w_iter(void); +void Query_each_w_iter_no_this(void); +void Query_invalid_field_from_each_w_iter(void); +void Query_invalid_field_T_from_each_w_iter(void); +void Query_invalid_field_const_T_from_each_w_iter(void); +void Query_field_at_from_each_w_iter(void); +void Query_field_at_T_from_each_w_iter(void); +void Query_field_at_const_T_from_each_w_iter(void); +void Query_field_at_from_each_w_iter_w_base_type(void); void Query_change_tracking(void); void Query_not_w_write(void); void Query_get_first(void); @@ -573,8 +637,6 @@ void Query_get_count_direct(void); void Query_get_is_true_direct(void); void Query_get_first_direct(void); void Query_each_w_no_this(void); -void Query_each_w_iter_no_this(void); -void Query_invalid_each_w_no_this(void); void Query_named_query(void); void Query_named_scoped_query(void); void Query_instanced_nested_query_w_iter(void); @@ -589,15 +651,54 @@ void Query_find(void); void Query_find_not_found(void); void Query_find_w_entity(void); void Query_optional_pair_term(void); +void Query_empty_tables_each(void); +void Query_empty_tables_each_w_entity(void); +void Query_empty_tables_each_w_iter(void); +void Query_query_from_entity(void); +void Query_query_from_entity_name(void); +void Query_run_w_iter_fini(void); +void Query_run_w_iter_fini_interrupt(void); +void Query_run_w_iter_fini_empty(void); +void Query_run_w_iter_fini_no_query(void); +void Query_add_to_match_from_staged_query(void); +void Query_add_to_match_from_staged_query_readonly_threaded(void); +void Query_pair_with_variable_src(void); +void Query_pair_with_variable_src_no_row_fields(void); +void Query_iter_targets(void); +void Query_iter_targets_2nd_field(void); +void Query_iter_targets_field_out_of_range(void); +void Query_iter_targets_field_not_a_pair(void); +void Query_iter_targets_field_not_set(void); // Testsuite 'QueryBuilder' +void QueryBuilder_setup(void); void QueryBuilder_builder_assign_same_type(void); void QueryBuilder_builder_assign_from_empty(void); void QueryBuilder_builder_assign_to_empty(void); void QueryBuilder_builder_build(void); void QueryBuilder_builder_build_to_auto(void); void QueryBuilder_builder_build_n_statements(void); +void QueryBuilder_builder_force_assign_operator(void); void QueryBuilder_1_type(void); +void QueryBuilder_2_types(void); +void QueryBuilder_id_term(void); +void QueryBuilder_type_term(void); +void QueryBuilder_id_pair_term(void); +void QueryBuilder_id_pair_wildcard_term(void); +void QueryBuilder_type_pair_term(void); +void QueryBuilder_pair_term_w_var(void); +void QueryBuilder_2_pair_terms_w_var(void); +void QueryBuilder_set_var(void); +void QueryBuilder_set_2_vars(void); +void QueryBuilder_set_var_by_name(void); +void QueryBuilder_set_2_vars_by_name(void); +void QueryBuilder_expr_w_var(void); +void QueryBuilder_set_var_on_query(void); +void QueryBuilder_set_var_by_name_on_query(void); +void QueryBuilder_set_table_var(void); +void QueryBuilder_set_range_var(void); +void QueryBuilder_set_table_var_chained(void); +void QueryBuilder_set_range_var_chained(void); void QueryBuilder_add_1_type(void); void QueryBuilder_add_2_types(void); void QueryBuilder_add_1_type_w_1_type(void); @@ -638,9 +739,75 @@ void QueryBuilder_explicit_term_w_id(void); void QueryBuilder_explicit_term_w_pair_id(void); void QueryBuilder_1_term_to_empty(void); void QueryBuilder_2_subsequent_args(void); -void QueryBuilder_optional_tag_is_set(void); +void QueryBuilder_filter_as_move_arg(void); +void QueryBuilder_filter_as_return(void); +void QueryBuilder_filter_copy(void); +void QueryBuilder_world_each_filter_1_component(void); +void QueryBuilder_world_each_filter_2_components(void); +void QueryBuilder_world_each_filter_1_component_no_entity(void); +void QueryBuilder_world_each_filter_2_components_no_entity(void); void QueryBuilder_10_terms(void); -void QueryBuilder_20_terms(void); +void QueryBuilder_16_terms(void); +void QueryBuilder_32_terms(void); +void QueryBuilder_33_terms(void); +void QueryBuilder_term_after_arg(void); +void QueryBuilder_name_arg(void); +void QueryBuilder_const_in_term(void); +void QueryBuilder_const_optional(void); +void QueryBuilder_create_w_no_template_args(void); +void QueryBuilder_2_terms_w_expr(void); +void QueryBuilder_assert_on_uninitialized_term(void); +void QueryBuilder_operator_shortcuts(void); +void QueryBuilder_inout_shortcuts(void); +void QueryBuilder_iter_column_w_const_as_array(void); +void QueryBuilder_iter_column_w_const_as_ptr(void); +void QueryBuilder_iter_column_w_const_deref(void); +void QueryBuilder_with_id(void); +void QueryBuilder_with_name(void); +void QueryBuilder_with_component(void); +void QueryBuilder_with_pair_id(void); +void QueryBuilder_with_pair_name(void); +void QueryBuilder_with_pair_components(void); +void QueryBuilder_with_pair_component_id(void); +void QueryBuilder_with_pair_component_name(void); +void QueryBuilder_with_pair_name_component_id(void); +void QueryBuilder_with_enum(void); +void QueryBuilder_without_id(void); +void QueryBuilder_without_name(void); +void QueryBuilder_without_component(void); +void QueryBuilder_without_pair_id(void); +void QueryBuilder_without_pair_name(void); +void QueryBuilder_without_pair_components(void); +void QueryBuilder_without_pair_component_id(void); +void QueryBuilder_without_pair_component_name(void); +void QueryBuilder_without_pair_name_component_id(void); +void QueryBuilder_without_enum(void); +void QueryBuilder_write_id(void); +void QueryBuilder_write_name(void); +void QueryBuilder_write_component(void); +void QueryBuilder_write_pair_id(void); +void QueryBuilder_write_pair_name(void); +void QueryBuilder_write_pair_components(void); +void QueryBuilder_write_pair_component_id(void); +void QueryBuilder_write_pair_component_name(void); +void QueryBuilder_write_enum(void); +void QueryBuilder_read_id(void); +void QueryBuilder_read_name(void); +void QueryBuilder_read_component(void); +void QueryBuilder_read_pair_id(void); +void QueryBuilder_read_pair_name(void); +void QueryBuilder_read_pair_components(void); +void QueryBuilder_read_pair_component_id(void); +void QueryBuilder_read_pair_component_name(void); +void QueryBuilder_read_enum(void); +void QueryBuilder_assign_after_init(void); +void QueryBuilder_iter_w_stage(void); +void QueryBuilder_with_t_inout(void); +void QueryBuilder_with_T_inout(void); +void QueryBuilder_with_R_T_inout(void); +void QueryBuilder_with_R_t_inout(void); +void QueryBuilder_with_r_t_inout(void); +void QueryBuilder_optional_tag_is_set(void); void QueryBuilder_group_by_raw(void); void QueryBuilder_group_by_template(void); void QueryBuilder_group_by_iter_one(void); @@ -649,6 +816,8 @@ void QueryBuilder_group_by_iter_one_all_groups(void); void QueryBuilder_group_by_default_func_w_id(void); void QueryBuilder_group_by_default_func_w_type(void); void QueryBuilder_group_by_callbacks(void); +void QueryBuilder_set_group_on_query(void); +void QueryBuilder_set_group_type_on_query(void); void QueryBuilder_create_w_no_template_args(void); void QueryBuilder_any_wildcard(void); void QueryBuilder_cascade(void); @@ -659,157 +828,16 @@ void QueryBuilder_cascade_desc(void); void QueryBuilder_named_query(void); void QueryBuilder_term_w_write(void); void QueryBuilder_term_w_read(void); -void QueryBuilder_iter_w_stage(void); - -// Testsuite 'FilterBuilder' -void FilterBuilder_builder_assign_same_type(void); -void FilterBuilder_builder_assign_from_empty(void); -void FilterBuilder_builder_assign_to_empty(void); -void FilterBuilder_builder_build(void); -void FilterBuilder_builder_build_to_auto(void); -void FilterBuilder_builder_build_n_statements(void); -void FilterBuilder_builder_force_assign_operator(void); -void FilterBuilder_1_type(void); -void FilterBuilder_add_1_type(void); -void FilterBuilder_add_2_types(void); -void FilterBuilder_add_1_type_w_1_type(void); -void FilterBuilder_add_2_types_w_1_type(void); -void FilterBuilder_add_pair(void); -void FilterBuilder_add_not(void); -void FilterBuilder_add_or(void); -void FilterBuilder_add_optional(void); -void FilterBuilder_ptr_type(void); -void FilterBuilder_const_type(void); -void FilterBuilder_string_term(void); -void FilterBuilder_singleton_term(void); -void FilterBuilder_isa_superset_term(void); -void FilterBuilder_isa_self_superset_term(void); -void FilterBuilder_childof_superset_term(void); -void FilterBuilder_childof_self_superset_term(void); -void FilterBuilder_isa_superset_term_w_each(void); -void FilterBuilder_isa_self_superset_term_w_each(void); -void FilterBuilder_childof_superset_term_w_each(void); -void FilterBuilder_childof_self_superset_term_w_each(void); -void FilterBuilder_isa_superset_shortcut(void); -void FilterBuilder_isa_superset_shortcut_w_self(void); -void FilterBuilder_childof_superset_shortcut(void); -void FilterBuilder_childof_superset_shortcut_w_self(void); -void FilterBuilder_relation(void); -void FilterBuilder_relation_w_object_wildcard(void); -void FilterBuilder_relation_w_predicate_wildcard(void); -void FilterBuilder_add_pair_w_rel_type(void); -void FilterBuilder_template_term(void); -void FilterBuilder_explicit_subject_w_id(void); -void FilterBuilder_explicit_subject_w_type(void); -void FilterBuilder_explicit_object_w_id(void); -void FilterBuilder_explicit_object_w_type(void); -void FilterBuilder_explicit_term(void); -void FilterBuilder_explicit_term_w_type(void); -void FilterBuilder_explicit_term_w_pair_type(void); -void FilterBuilder_explicit_term_w_id(void); -void FilterBuilder_explicit_term_w_pair_id(void); -void FilterBuilder_1_term_to_empty(void); -void FilterBuilder_2_subsequent_args(void); -void FilterBuilder_filter_as_arg(void); -void FilterBuilder_filter_as_move_arg(void); -void FilterBuilder_filter_as_return(void); -void FilterBuilder_filter_copy(void); -void FilterBuilder_world_each_filter_1_component(void); -void FilterBuilder_world_each_filter_2_components(void); -void FilterBuilder_world_each_filter_1_component_no_entity(void); -void FilterBuilder_world_each_filter_2_components_no_entity(void); -void FilterBuilder_10_terms(void); -void FilterBuilder_20_terms(void); -void FilterBuilder_term_after_arg(void); -void FilterBuilder_name_arg(void); -void FilterBuilder_const_in_term(void); -void FilterBuilder_const_optional(void); -void FilterBuilder_create_w_no_template_args(void); -void FilterBuilder_2_terms_w_expr(void); -void FilterBuilder_assert_on_uninitialized_term(void); -void FilterBuilder_operator_shortcuts(void); -void FilterBuilder_inout_shortcuts(void); -void FilterBuilder_iter_column_w_const_as_array(void); -void FilterBuilder_iter_column_w_const_as_ptr(void); -void FilterBuilder_iter_column_w_const_deref(void); -void FilterBuilder_with_id(void); -void FilterBuilder_with_name(void); -void FilterBuilder_with_component(void); -void FilterBuilder_with_pair_id(void); -void FilterBuilder_with_pair_name(void); -void FilterBuilder_with_pair_components(void); -void FilterBuilder_with_pair_component_id(void); -void FilterBuilder_with_pair_component_name(void); -void FilterBuilder_with_enum(void); -void FilterBuilder_without_id(void); -void FilterBuilder_without_name(void); -void FilterBuilder_without_component(void); -void FilterBuilder_without_pair_id(void); -void FilterBuilder_without_pair_name(void); -void FilterBuilder_without_pair_components(void); -void FilterBuilder_without_pair_component_id(void); -void FilterBuilder_without_pair_component_name(void); -void FilterBuilder_without_enum(void); -void FilterBuilder_write_id(void); -void FilterBuilder_write_name(void); -void FilterBuilder_write_component(void); -void FilterBuilder_write_pair_id(void); -void FilterBuilder_write_pair_name(void); -void FilterBuilder_write_pair_components(void); -void FilterBuilder_write_pair_component_id(void); -void FilterBuilder_write_pair_component_name(void); -void FilterBuilder_write_enum(void); -void FilterBuilder_read_id(void); -void FilterBuilder_read_name(void); -void FilterBuilder_read_component(void); -void FilterBuilder_read_pair_id(void); -void FilterBuilder_read_pair_name(void); -void FilterBuilder_read_pair_components(void); -void FilterBuilder_read_pair_component_id(void); -void FilterBuilder_read_pair_component_name(void); -void FilterBuilder_read_enum(void); -void FilterBuilder_assign_after_init(void); -void FilterBuilder_iter_w_stage(void); -void FilterBuilder_with_t_inout(void); -void FilterBuilder_with_T_inout(void); -void FilterBuilder_with_R_T_inout(void); -void FilterBuilder_with_R_t_inout(void); -void FilterBuilder_with_r_t_inout(void); - -// Testsuite 'RuleBuilder' -void RuleBuilder_1_type(void); -void RuleBuilder_2_types(void); -void RuleBuilder_id_term(void); -void RuleBuilder_type_term(void); -void RuleBuilder_id_pair_term(void); -void RuleBuilder_id_pair_wildcard_term(void); -void RuleBuilder_type_pair_term(void); -void RuleBuilder_pair_term_w_var(void); -void RuleBuilder_2_pair_terms_w_var(void); -void RuleBuilder_set_var(void); -void RuleBuilder_set_2_vars(void); -void RuleBuilder_set_var_by_name(void); -void RuleBuilder_set_2_vars_by_name(void); -void RuleBuilder_expr_w_var(void); -void RuleBuilder_get_first(void); -void RuleBuilder_get_count_direct(void); -void RuleBuilder_get_is_true_direct(void); -void RuleBuilder_get_first_direct(void); -void RuleBuilder_var_src_w_prefixed_name(void); -void RuleBuilder_var_first_w_prefixed_name(void); -void RuleBuilder_var_second_w_prefixed_name(void); -void RuleBuilder_term_w_second_var_string(void); -void RuleBuilder_term_type_w_second_var_string(void); -void RuleBuilder_named_rule(void); -void RuleBuilder_named_scoped_rule(void); -void RuleBuilder_is_valid(void); -void RuleBuilder_unresolved_by_name(void); -void RuleBuilder_scope(void); -void RuleBuilder_iter_w_stage(void); -void RuleBuilder_inspect_terms_w_expr(void); -void RuleBuilder_find(void); -void RuleBuilder_find_not_found(void); -void RuleBuilder_find_w_entity(void); +void QueryBuilder_var_src_w_prefixed_name(void); +void QueryBuilder_var_first_w_prefixed_name(void); +void QueryBuilder_var_second_w_prefixed_name(void); +void QueryBuilder_term_w_second_var_string(void); +void QueryBuilder_term_type_w_second_var_string(void); +void QueryBuilder_named_rule(void); +void QueryBuilder_named_scoped_rule(void); +void QueryBuilder_is_valid(void); +void QueryBuilder_unresolved_by_name(void); +void QueryBuilder_scope(void); // Testsuite 'SystemBuilder' void SystemBuilder_builder_assign_same_type(void); @@ -829,7 +857,7 @@ void SystemBuilder_const_type(void); void SystemBuilder_string_term(void); void SystemBuilder_singleton_term(void); void SystemBuilder_10_terms(void); -void SystemBuilder_20_terms(void); +void SystemBuilder_16_terms(void); void SystemBuilder_name_arg(void); void SystemBuilder_create_w_no_template_args(void); void SystemBuilder_write_annotation(void); @@ -841,13 +869,16 @@ void Observer_2_terms_on_remove(void); void Observer_2_terms_on_set(void); void Observer_2_terms_un_set(void); void Observer_10_terms(void); -void Observer_20_terms(void); +void Observer_16_terms(void); void Observer_2_entities_iter(void); void Observer_2_entities_table_column(void); void Observer_2_entities_each(void); void Observer_create_w_no_template_args(void); void Observer_yield_existing(void); void Observer_yield_existing_2_terms(void); +void Observer_yield_existing_on_create_flag(void); +void Observer_yield_existing_on_delete_flag(void); +void Observer_yield_existing_on_create_delete_flag(void); void Observer_default_ctor(void); void Observer_entity_ctor(void); void Observer_on_add(void); @@ -858,9 +889,14 @@ void Observer_on_add_tag_each(void); void Observer_on_add_expr(void); void Observer_observer_w_filter_term(void); void Observer_run_callback(void); +void Observer_run_callback_w_1_field(void); +void Observer_run_callback_w_2_fields(void); +void Observer_run_callback_w_yield_existing_1_field(void); +void Observer_run_callback_w_yield_existing_2_fields(void); void Observer_get_query(void); void Observer_on_set_w_set(void); void Observer_on_set_w_defer_set(void); +void Observer_on_set_w_set_sparse(void); void Observer_on_add_singleton(void); void Observer_on_add_pair_singleton(void); void Observer_on_add_pair_wildcard_singleton(void); @@ -868,35 +904,25 @@ void Observer_on_add_with_pair_singleton(void); void Observer_add_in_yield_existing(void); void Observer_add_in_yield_existing_multi(void); void Observer_name_from_root(void); - -// Testsuite 'Filter' -void Filter_term_each_component(void); -void Filter_term_each_tag(void); -void Filter_term_each_id(void); -void Filter_term_each_pair_type(void); -void Filter_term_each_pair_id(void); -void Filter_term_each_pair_relation_wildcard(void); -void Filter_term_each_pair_object_wildcard(void); -void Filter_default_ctor_no_assign(void); -void Filter_term_get_id(void); -void Filter_term_get_subj(void); -void Filter_term_get_pred(void); -void Filter_term_get_obj(void); -void Filter_get_first(void); -void Filter_get_count_direct(void); -void Filter_get_is_true_direct(void); -void Filter_get_first_direct(void); -void Filter_each_w_no_this(void); -void Filter_each_w_iter_no_this(void); -void Filter_invalid_each_w_no_this(void); -void Filter_named_filter(void); -void Filter_named_scoped_filter(void); -void Filter_set_this_var(void); -void Filter_inspect_terms_w_expr(void); -void Filter_find(void); -void Filter_find_not_found(void); -void Filter_find_w_entity(void); -void Filter_optional_pair_term(void); +void Observer_term_index(void); +void Observer_implicit_register_in_emit_for_named_entity(void); +void Observer_add_to_named_in_emit_for_named_entity(void); +void Observer_implicit_register_in_emit_for_named_entity_w_defer(void); +void Observer_add_to_named_in_emit_for_named_entity_w_defer(void); +void Observer_register_twice_w_each(void); +void Observer_register_twice_w_run(void); +void Observer_register_twice_w_run_each(void); +void Observer_register_twice_w_each_run(void); +void Observer_other_table(void); +void Observer_other_table_w_pair(void); +void Observer_other_table_w_pair_wildcard(void); +void Observer_on_add_inherited(void); +void Observer_on_set_inherited(void); +void Observer_on_remove_inherited(void); +void Observer_on_set_after_remove_override(void); +void Observer_on_set_after_remove_override_create_observer_before(void); +void Observer_on_set_w_override_after_delete(void); +void Observer_on_set_w_override_after_clear(void); // Testsuite 'ComponentLifecycle' void ComponentLifecycle_ctor_on_add(void); @@ -921,8 +947,8 @@ void ComponentLifecycle_struct_w_vector_add_2_remove(void); void ComponentLifecycle_struct_w_vector_set_2_remove(void); void ComponentLifecycle_struct_w_vector_add_2_remove_w_tag(void); void ComponentLifecycle_struct_w_vector_set_2_remove_w_tag(void); -void ComponentLifecycle_get_mut_new(void); -void ComponentLifecycle_get_mut_existing(void); +void ComponentLifecycle_ensure_new(void); +void ComponentLifecycle_ensure_existing(void); void ComponentLifecycle_implicit_component(void); void ComponentLifecycle_implicit_after_query(void); void ComponentLifecycle_deleted_copy(void); @@ -958,6 +984,15 @@ void ComponentLifecycle_on_set_hook(void); void ComponentLifecycle_on_add_hook_w_entity(void); void ComponentLifecycle_on_remove_hook_w_entity(void); void ComponentLifecycle_on_set_hook_w_entity(void); +void ComponentLifecycle_on_add_hook_sparse(void); +void ComponentLifecycle_on_remove_hook_sparse(void); +void ComponentLifecycle_on_set_hook_sparse(void); +void ComponentLifecycle_on_add_hook_sparse_w_entity(void); +void ComponentLifecycle_on_remove_hook_sparse_w_entity(void); +void ComponentLifecycle_on_set_hook_sparse_w_entity(void); +void ComponentLifecycle_on_add_hook_sparse_w_iter(void); +void ComponentLifecycle_on_remove_hook_sparse_w_iter(void); +void ComponentLifecycle_on_set_hook_sparse_w_iter(void); void ComponentLifecycle_chained_hooks(void); void ComponentLifecycle_ctor_w_2_worlds(void); void ComponentLifecycle_ctor_w_2_worlds_explicit_registration(void); @@ -972,8 +1007,12 @@ void ComponentLifecycle_set_override_pair_no_copy(void); void ComponentLifecycle_set_override_pair_w_entity_no_copy(void); void ComponentLifecycle_dtor_after_defer_set(void); void ComponentLifecycle_dtor_with_relation(void); +void ComponentLifecycle_dtor_relation_target(void); void ComponentLifecycle_register_parent_after_child_w_hooks(void); void ComponentLifecycle_register_parent_after_child_w_hooks_implicit(void); +void ComponentLifecycle_sparse_component(void); +void ComponentLifecycle_count_in_add_hook(void); +void ComponentLifecycle_count_in_remove_hook(void); // Testsuite 'Refs' void Refs_get_ref_by_ptr(void); @@ -988,7 +1027,11 @@ void Refs_pair_ref_w_entity(void); void Refs_pair_ref_second(void); void Refs_from_stage(void); void Refs_default_ctor(void); +void Refs_ctor_from_entity(void); +void Refs_implicit_operator_bool(void); void Refs_try_get(void); +void Refs_has(void); +void Refs_bool_operator(void); // Testsuite 'Module' void Module_import(void); @@ -1004,6 +1047,14 @@ void Module_module_as_entity(void); void Module_module_as_component(void); void Module_module_with_core_name(void); void Module_import_addons_two_worlds(void); +void Module_lookup_module_after_reparent(void); +void Module_reparent_module_in_ctor(void); +void Module_implicitly_add_module_to_scopes_component(void); +void Module_implicitly_add_module_to_scopes_entity(void); +void Module_rename_namespace_shorter(void); +void Module_rename_namespace_longer(void); +void Module_rename_namespace_nested(void); +void Module_rename_reparent_root_module(void); // Testsuite 'ImplicitComponents' void ImplicitComponents_add(void); @@ -1031,13 +1082,9 @@ void ImplicitComponents_use_const_w_threads(void); void ImplicitComponents_implicit_base(void); void ImplicitComponents_implicit_const(void); void ImplicitComponents_implicit_ref(void); -void ImplicitComponents_implicit_ptr(void); void ImplicitComponents_implicit_const_ref(void); void ImplicitComponents_vector_elem_type(void); -// Testsuite 'Snapshot' -void Snapshot_simple_snapshot(void); - // Testsuite 'WorldFactory' void WorldFactory_entity(void); void WorldFactory_entity_w_name(void); @@ -1049,7 +1096,6 @@ void WorldFactory_system_w_name(void); void WorldFactory_system_w_expr(void); void WorldFactory_query(void); void WorldFactory_query_w_expr(void); -void WorldFactory_snapshot(void); void WorldFactory_module(void); // Testsuite 'World' @@ -1083,6 +1129,7 @@ void World_reregister_after_reset_w_hooks_and_in_use(void); void World_reregister_after_reset_w_hooks_and_in_use_implicit(void); void World_register_component_w_reset_in_multithreaded(void); void World_register_component_w_core_name(void); +void World_register_nested_component_in_module(void); void World_count(void); void World_count_id(void); void World_count_pair(void); @@ -1137,7 +1184,7 @@ void World_is_alive(void); void World_is_valid(void); void World_exists(void); void World_get_alive(void); -void World_ensure(void); +void World_make_alive(void); void World_reset_all(void); void World_get_tick(void); void World_register_from_scope(void); @@ -1160,9 +1207,18 @@ void World_make_pair_of_pair_id(void); void World_make_pair_of_pair_id_tgt(void); void World_make_pair_of_pair_type(void); void World_delta_time(void); +void World_atfini(void); +void World_atfini_w_ctx(void); +void World_get_mut_T(void); +void World_get_mut_R_T(void); +void World_world_mini(void); +void World_copy_world(void); +void World_fini_reentrancy(void); +void World_fini_copy_move_assign(void); // Testsuite 'Singleton' void Singleton_set_get_singleton(void); +void Singleton_ensure_singleton(void); void Singleton_get_mut_singleton(void); void Singleton_emplace_singleton(void); void Singleton_modified_singleton(void); @@ -1293,6 +1349,7 @@ void Meta_error_range(void); void Meta_struct_member_ptr(void); void Meta_struct_member_ptr_packed_struct(void); void Meta_component_as_array(void); +void Meta_out_of_order_member_declaration(void); // Testsuite 'Table' void Table_each(void); @@ -1333,6 +1390,7 @@ void Doc_set_brief(void); void Doc_set_name(void); void Doc_set_link(void); void Doc_set_color(void); +void Doc_set_uuid(void); void Doc_get_name_no_doc_name(void); bake_test_case PrettyFunction_testcases[] = { @@ -1468,8 +1526,8 @@ bake_test_case Entity_testcases[] = { Entity_get_generic }, { - "get_mut_generic", - Entity_get_mut_generic + "ensure_generic", + Entity_ensure_generic }, { "get_generic_w_id", @@ -1480,12 +1538,36 @@ bake_test_case Entity_testcases[] = { Entity_get_generic_w_id_t }, { - "get_mut_generic_w_id", - Entity_get_mut_generic_w_id + "ensure_generic_w_id", + Entity_ensure_generic_w_id + }, + { + "ensure_generic_w_id_t", + Entity_ensure_generic_w_id_t + }, + { + "get_mut_w_id", + Entity_get_mut_w_id + }, + { + "get_mut_T", + Entity_get_mut_T + }, + { + "get_mut_r_t", + Entity_get_mut_r_t + }, + { + "get_mut_R_t", + Entity_get_mut_R_t + }, + { + "get_mut_R_T", + Entity_get_mut_R_T }, { - "get_mut_generic_w_id_t", - Entity_get_mut_generic_w_id_t + "get_mut_r_T", + Entity_get_mut_r_T }, { "set_generic", @@ -1816,20 +1898,20 @@ bake_test_case Entity_testcases[] = { Entity_set_3_components_w_callback }, { - "get_mut_1_component_w_callback", - Entity_get_mut_1_component_w_callback + "ensure_1_component_w_callback", + Entity_ensure_1_component_w_callback }, { - "get_mut_2_components_w_callback", - Entity_get_mut_2_components_w_callback + "ensure_2_components_w_callback", + Entity_ensure_2_components_w_callback }, { "get_component_w_callback_nested", Entity_get_component_w_callback_nested }, { - "get_mut_component_w_callback_nested", - Entity_get_mut_component_w_callback_nested + "ensure_component_w_callback_nested", + Entity_ensure_component_w_callback_nested }, { "defer_set_1_component", @@ -1959,6 +2041,10 @@ bake_test_case Entity_testcases[] = { "defer_suspend_resume", Entity_defer_suspend_resume }, + { + "defer_ensure", + Entity_defer_ensure + }, { "entity_id_str", Entity_entity_id_str @@ -2334,6 +2420,94 @@ bake_test_case Entity_testcases[] = { { "world_lookup_not_recursive", Entity_world_lookup_not_recursive + }, + { + "world_lookup_custom_sep", + Entity_world_lookup_custom_sep + }, + { + "world_lookup_custom_root_sep", + Entity_world_lookup_custom_root_sep + }, + { + "depends_on", + Entity_depends_on + }, + { + "depends_on_type", + Entity_depends_on_type + }, + { + "const_entity_add_remove", + Entity_const_entity_add_remove + }, + { + "const_entity_set", + Entity_const_entity_set + }, + { + "const_entity_get_mut", + Entity_const_entity_get_mut + }, + { + "const_entity_ensure", + Entity_const_entity_ensure + }, + { + "const_entity_destruct", + Entity_const_entity_destruct + }, + { + "const_entity_emit_after_build", + Entity_const_entity_emit_after_build + }, + { + "const_entity_set_doc", + Entity_const_entity_set_doc + }, + { + "set_sparse", + Entity_set_sparse + }, + { + "insert_1_sparse", + Entity_insert_1_sparse + }, + { + "insert_2_w_1_sparse", + Entity_insert_2_w_1_sparse + }, + { + "emplace_sparse", + Entity_emplace_sparse + }, + { + "override_sparse", + Entity_override_sparse + }, + { + "delete_w_override_sparse", + Entity_delete_w_override_sparse + }, + { + "get_pair_second_invalid_type", + Entity_get_pair_second_invalid_type + }, + { + "get_mut_pair_second_invalid_type", + Entity_get_mut_pair_second_invalid_type + }, + { + "ensure_pair_second_invalid_type", + Entity_ensure_pair_second_invalid_type + }, + { + "set_pair_second_invalid_type", + Entity_set_pair_second_invalid_type + }, + { + "get_ref_pair_second_invalid_type", + Entity_get_ref_pair_second_invalid_type } }; @@ -2387,24 +2561,24 @@ bake_test_case Pairs_testcases[] = { Pairs_override_tag_pair }, { - "get_mut_pair", - Pairs_get_mut_pair + "ensure_pair", + Pairs_ensure_pair }, { - "get_mut_pair_existing", - Pairs_get_mut_pair_existing + "ensure_pair_existing", + Pairs_ensure_pair_existing }, { - "get_mut_pair_tag", - Pairs_get_mut_pair_tag + "ensure_pair_tag", + Pairs_ensure_pair_tag }, { - "get_mut_pair_tag_existing", - Pairs_get_mut_pair_tag_existing + "ensure_pair_tag_existing", + Pairs_ensure_pair_tag_existing }, { - "get_mut_R_tag_O", - Pairs_get_mut_R_tag_O + "ensure_R_tag_O", + Pairs_ensure_R_tag_O }, { "get_relation_from_id", @@ -2605,6 +2779,18 @@ bake_test_case Pairs_testcases[] = { { "set_R_existing_value", Pairs_set_R_existing_value + }, + { + "symmetric_w_childof", + Pairs_symmetric_w_childof + }, + { + "modified_tag_second", + Pairs_modified_tag_second + }, + { + "modified_tag_first", + Pairs_modified_tag_first } }; @@ -2617,6 +2803,18 @@ bake_test_case Enum_testcases[] = { "sparse_enum_reflection", Enum_sparse_enum_reflection }, + { + "bitmask_enum_reflection", + Enum_bitmask_enum_reflection + }, + { + "bitmask_enum_with_type_reflection", + Enum_bitmask_enum_with_type_reflection + }, + { + "enum_with_mixed_constants_and_bitmask", + Enum_enum_with_mixed_constants_and_bitmask + }, { "enum_class_reflection", Enum_enum_class_reflection @@ -2755,46 +2953,30 @@ bake_test_case Enum_testcases[] = { } }; -bake_test_case Switch_testcases[] = { +bake_test_case Union_testcases[] = { { "add_case", - Switch_add_case + Union_add_case }, { "get_case", - Switch_get_case - }, - { - "system_w_case", - Switch_system_w_case - }, - { - "system_w_case_builder", - Switch_system_w_case_builder - }, - { - "system_w_sw_type_builder", - Switch_system_w_sw_type_builder - }, - { - "system_w_switch", - Switch_system_w_switch + Union_get_case }, { "add_case_w_type", - Switch_add_case_w_type + Union_add_case_w_type }, { "add_switch_w_type", - Switch_add_switch_w_type + Union_add_switch_w_type }, { "add_remove_switch_w_type", - Switch_add_remove_switch_w_type + Union_add_remove_switch_w_type }, { "switch_enum_type", - Switch_switch_enum_type + Union_switch_enum_type } }; @@ -2858,6 +3040,46 @@ bake_test_case Paths_testcases[] = { { "alias_entity_empty", Paths_alias_entity_empty + }, + { + "id_from_str_0_entity", + Paths_id_from_str_0_entity + }, + { + "id_from_str_entity_from_str", + Paths_id_from_str_entity_from_str + }, + { + "id_from_str_unresolved_entity_from_str", + Paths_id_from_str_unresolved_entity_from_str + }, + { + "id_from_str_scoped_entity_from_str", + Paths_id_from_str_scoped_entity_from_str + }, + { + "id_from_str_template_entity_from_str", + Paths_id_from_str_template_entity_from_str + }, + { + "id_from_str_pair_from_str", + Paths_id_from_str_pair_from_str + }, + { + "id_from_str_unresolved_pair_from_str", + Paths_id_from_str_unresolved_pair_from_str + }, + { + "id_from_str_wildcard_pair_from_str", + Paths_id_from_str_wildcard_pair_from_str + }, + { + "id_from_str_any_pair_from_str", + Paths_id_from_str_any_pair_from_str + }, + { + "id_from_str_invalid_pair", + Paths_id_from_str_invalid_pair } }; @@ -2998,6 +3220,10 @@ bake_test_case System_testcases[] = { "rate_filter", System_rate_filter }, + { + "self_rate_filter", + System_self_rate_filter + }, { "update_rate_filter", System_update_rate_filter @@ -3030,14 +3256,6 @@ bake_test_case System_testcases[] = { "instanced_query_w_base_each", System_instanced_query_w_base_each }, - { - "un_instanced_query_w_singleton_each", - System_un_instanced_query_w_singleton_each - }, - { - "un_instanced_query_w_base_each", - System_un_instanced_query_w_base_each - }, { "instanced_query_w_singleton_iter", System_instanced_query_w_singleton_iter @@ -3046,14 +3264,6 @@ bake_test_case System_testcases[] = { "instanced_query_w_base_iter", System_instanced_query_w_base_iter }, - { - "un_instanced_query_w_singleton_iter", - System_un_instanced_query_w_singleton_iter - }, - { - "un_instanced_query_w_base_iter", - System_un_instanced_query_w_base_iter - }, { "create_w_no_template_args", System_create_w_no_template_args @@ -3133,6 +3343,38 @@ bake_test_case System_testcases[] = { { "optional_pair_term", System_optional_pair_term + }, + { + "singleton_tick_source", + System_singleton_tick_source + }, + { + "pipeline_step_with_kind_enum", + System_pipeline_step_with_kind_enum + }, + { + "pipeline_step_depends_on_pipeline_step_with_enum", + System_pipeline_step_depends_on_pipeline_step_with_enum + }, + { + "register_twice_w_each", + System_register_twice_w_each + }, + { + "register_twice_w_run", + System_register_twice_w_run + }, + { + "register_twice_w_run_each", + System_register_twice_w_run_each + }, + { + "register_twice_w_each_run", + System_register_twice_w_each_run + }, + { + "run_w_0_src_query", + System_run_w_0_src_query } }; @@ -3248,6 +3490,18 @@ bake_test_case Event_testcases[] = { { "enqueue_entity_event_w_payload", Event_enqueue_entity_event_w_payload + }, + { + "enqueue_entity_from_readonly_world", + Event_enqueue_entity_from_readonly_world + }, + { + "enqueue_entity_w_payload_from_readonly_world", + Event_enqueue_entity_w_payload_from_readonly_world + }, + { + "enum_event", + Event_enum_event } }; @@ -3272,20 +3526,72 @@ bake_test_case Iterable_testcases[] = { bake_test_case Query_testcases[] = { { - "action", - Query_action + "term_each_component", + Query_term_each_component + }, + { + "term_each_tag", + Query_term_each_tag + }, + { + "term_each_id", + Query_term_each_id + }, + { + "term_each_pair_type", + Query_term_each_pair_type + }, + { + "term_each_pair_id", + Query_term_each_pair_id + }, + { + "term_each_pair_relation_wildcard", + Query_term_each_pair_relation_wildcard + }, + { + "term_each_pair_object_wildcard", + Query_term_each_pair_object_wildcard + }, + { + "term_get_id", + Query_term_get_id + }, + { + "term_get_subj", + Query_term_get_subj + }, + { + "term_get_pred", + Query_term_get_pred + }, + { + "term_get_obj", + Query_term_get_obj + }, + { + "set_this_var", + Query_set_this_var + }, + { + "run", + Query_run + }, + { + "run_const", + Query_run_const }, { - "action_const", - Query_action_const + "run_shared", + Query_run_shared }, { - "action_shared", - Query_action_shared + "run_optional", + Query_run_optional }, { - "action_optional", - Query_action_optional + "run_sparse", + Query_run_sparse }, { "each", @@ -3303,6 +3609,10 @@ bake_test_case Query_testcases[] = { "each_optional", Query_each_optional }, + { + "each_sparse", + Query_each_sparse + }, { "signature", Query_signature @@ -3319,14 +3629,6 @@ bake_test_case Query_testcases[] = { "signature_optional", Query_signature_optional }, - { - "subquery", - Query_subquery - }, - { - "subquery_w_expr", - Query_subquery_w_expr - }, { "query_single_pair", Query_query_single_pair @@ -3347,10 +3649,6 @@ bake_test_case Query_testcases[] = { "changed", Query_changed }, - { - "orphaned", - Query_orphaned - }, { "default_ctor", Query_default_ctor @@ -3467,14 +3765,6 @@ bake_test_case Query_testcases[] = { "instanced_query_w_base_each", Query_instanced_query_w_base_each }, - { - "un_instanced_query_w_singleton_each", - Query_un_instanced_query_w_singleton_each - }, - { - "un_instanced_query_w_base_each", - Query_un_instanced_query_w_base_each - }, { "instanced_query_w_singleton_iter", Query_instanced_query_w_singleton_iter @@ -3483,14 +3773,6 @@ bake_test_case Query_testcases[] = { "instanced_query_w_base_iter", Query_instanced_query_w_base_iter }, - { - "un_instanced_query_w_singleton_iter", - Query_un_instanced_query_w_singleton_iter - }, - { - "un_instanced_query_w_base_iter", - Query_un_instanced_query_w_base_iter - }, { "query_each_from_component", Query_query_each_from_component @@ -3519,6 +3801,38 @@ bake_test_case Query_testcases[] = { "query_each_w_iter", Query_query_each_w_iter }, + { + "each_w_iter_no_this", + Query_each_w_iter_no_this + }, + { + "invalid_field_from_each_w_iter", + Query_invalid_field_from_each_w_iter + }, + { + "invalid_field_T_from_each_w_iter", + Query_invalid_field_T_from_each_w_iter + }, + { + "invalid_field_const_T_from_each_w_iter", + Query_invalid_field_const_T_from_each_w_iter + }, + { + "field_at_from_each_w_iter", + Query_field_at_from_each_w_iter + }, + { + "field_at_T_from_each_w_iter", + Query_field_at_T_from_each_w_iter + }, + { + "field_at_const_T_from_each_w_iter", + Query_field_at_const_T_from_each_w_iter + }, + { + "field_at_from_each_w_iter_w_base_type", + Query_field_at_from_each_w_iter_w_base_type + }, { "change_tracking", Query_change_tracking @@ -3547,14 +3861,6 @@ bake_test_case Query_testcases[] = { "each_w_no_this", Query_each_w_no_this }, - { - "each_w_iter_no_this", - Query_each_w_iter_no_this - }, - { - "invalid_each_w_no_this", - Query_invalid_each_w_no_this - }, { "named_query", Query_named_query @@ -3610,875 +3916,745 @@ bake_test_case Query_testcases[] = { { "optional_pair_term", Query_optional_pair_term - } -}; - -bake_test_case QueryBuilder_testcases[] = { + }, { - "builder_assign_same_type", - QueryBuilder_builder_assign_same_type + "empty_tables_each", + Query_empty_tables_each }, { - "builder_assign_from_empty", - QueryBuilder_builder_assign_from_empty + "empty_tables_each_w_entity", + Query_empty_tables_each_w_entity }, { - "builder_assign_to_empty", - QueryBuilder_builder_assign_to_empty + "empty_tables_each_w_iter", + Query_empty_tables_each_w_iter }, { - "builder_build", - QueryBuilder_builder_build + "query_from_entity", + Query_query_from_entity }, { - "builder_build_to_auto", - QueryBuilder_builder_build_to_auto + "query_from_entity_name", + Query_query_from_entity_name }, { - "builder_build_n_statements", - QueryBuilder_builder_build_n_statements + "run_w_iter_fini", + Query_run_w_iter_fini }, { - "1_type", - QueryBuilder_1_type + "run_w_iter_fini_interrupt", + Query_run_w_iter_fini_interrupt }, { - "add_1_type", - QueryBuilder_add_1_type + "run_w_iter_fini_empty", + Query_run_w_iter_fini_empty }, { - "add_2_types", - QueryBuilder_add_2_types + "run_w_iter_fini_no_query", + Query_run_w_iter_fini_no_query }, { - "add_1_type_w_1_type", - QueryBuilder_add_1_type_w_1_type + "add_to_match_from_staged_query", + Query_add_to_match_from_staged_query }, { - "add_2_types_w_1_type", - QueryBuilder_add_2_types_w_1_type + "add_to_match_from_staged_query_readonly_threaded", + Query_add_to_match_from_staged_query_readonly_threaded }, { - "add_pair", - QueryBuilder_add_pair + "pair_with_variable_src", + Query_pair_with_variable_src }, { - "add_not", - QueryBuilder_add_not + "pair_with_variable_src_no_row_fields", + Query_pair_with_variable_src_no_row_fields }, { - "add_or", - QueryBuilder_add_or + "iter_targets", + Query_iter_targets }, { - "add_optional", - QueryBuilder_add_optional + "iter_targets_2nd_field", + Query_iter_targets_2nd_field }, { - "ptr_type", - QueryBuilder_ptr_type + "iter_targets_field_out_of_range", + Query_iter_targets_field_out_of_range }, { - "const_type", - QueryBuilder_const_type - }, - { - "string_term", - QueryBuilder_string_term - }, - { - "singleton_term", - QueryBuilder_singleton_term - }, - { - "isa_superset_term", - QueryBuilder_isa_superset_term - }, - { - "isa_self_superset_term", - QueryBuilder_isa_self_superset_term - }, - { - "childof_superset_term", - QueryBuilder_childof_superset_term - }, - { - "childof_self_superset_term", - QueryBuilder_childof_self_superset_term - }, - { - "isa_superset_term_w_each", - QueryBuilder_isa_superset_term_w_each - }, - { - "isa_self_superset_term_w_each", - QueryBuilder_isa_self_superset_term_w_each - }, - { - "childof_superset_term_w_each", - QueryBuilder_childof_superset_term_w_each - }, - { - "childof_self_superset_term_w_each", - QueryBuilder_childof_self_superset_term_w_each - }, - { - "isa_superset_shortcut", - QueryBuilder_isa_superset_shortcut - }, - { - "isa_superset_shortcut_w_self", - QueryBuilder_isa_superset_shortcut_w_self - }, - { - "childof_superset_shortcut", - QueryBuilder_childof_superset_shortcut - }, - { - "childof_superset_shortcut_w_self", - QueryBuilder_childof_superset_shortcut_w_self - }, - { - "relation", - QueryBuilder_relation - }, - { - "relation_w_object_wildcard", - QueryBuilder_relation_w_object_wildcard - }, - { - "relation_w_predicate_wildcard", - QueryBuilder_relation_w_predicate_wildcard - }, - { - "add_pair_w_rel_type", - QueryBuilder_add_pair_w_rel_type - }, - { - "template_term", - QueryBuilder_template_term - }, - { - "explicit_subject_w_id", - QueryBuilder_explicit_subject_w_id - }, - { - "explicit_subject_w_type", - QueryBuilder_explicit_subject_w_type - }, - { - "explicit_object_w_id", - QueryBuilder_explicit_object_w_id - }, - { - "explicit_object_w_type", - QueryBuilder_explicit_object_w_type - }, - { - "explicit_term", - QueryBuilder_explicit_term - }, - { - "explicit_term_w_type", - QueryBuilder_explicit_term_w_type - }, - { - "explicit_term_w_pair_type", - QueryBuilder_explicit_term_w_pair_type - }, - { - "explicit_term_w_id", - QueryBuilder_explicit_term_w_id - }, - { - "explicit_term_w_pair_id", - QueryBuilder_explicit_term_w_pair_id - }, - { - "1_term_to_empty", - QueryBuilder_1_term_to_empty - }, - { - "2_subsequent_args", - QueryBuilder_2_subsequent_args - }, - { - "optional_tag_is_set", - QueryBuilder_optional_tag_is_set + "iter_targets_field_not_a_pair", + Query_iter_targets_field_not_a_pair }, { - "10_terms", - QueryBuilder_10_terms - }, + "iter_targets_field_not_set", + Query_iter_targets_field_not_set + } +}; + +bake_test_case QueryBuilder_testcases[] = { { - "20_terms", - QueryBuilder_20_terms + "builder_assign_same_type", + QueryBuilder_builder_assign_same_type }, { - "group_by_raw", - QueryBuilder_group_by_raw + "builder_assign_from_empty", + QueryBuilder_builder_assign_from_empty }, { - "group_by_template", - QueryBuilder_group_by_template + "builder_assign_to_empty", + QueryBuilder_builder_assign_to_empty }, { - "group_by_iter_one", - QueryBuilder_group_by_iter_one + "builder_build", + QueryBuilder_builder_build }, { - "group_by_iter_one_template", - QueryBuilder_group_by_iter_one_template + "builder_build_to_auto", + QueryBuilder_builder_build_to_auto }, { - "group_by_iter_one_all_groups", - QueryBuilder_group_by_iter_one_all_groups + "builder_build_n_statements", + QueryBuilder_builder_build_n_statements }, { - "group_by_default_func_w_id", - QueryBuilder_group_by_default_func_w_id + "builder_force_assign_operator", + QueryBuilder_builder_force_assign_operator }, { - "group_by_default_func_w_type", - QueryBuilder_group_by_default_func_w_type + "1_type", + QueryBuilder_1_type }, { - "group_by_callbacks", - QueryBuilder_group_by_callbacks + "2_types", + QueryBuilder_2_types }, { - "create_w_no_template_args", - QueryBuilder_create_w_no_template_args + "id_term", + QueryBuilder_id_term }, { - "any_wildcard", - QueryBuilder_any_wildcard + "type_term", + QueryBuilder_type_term }, { - "cascade", - QueryBuilder_cascade + "id_pair_term", + QueryBuilder_id_pair_term }, { - "cascade_w_relationship", - QueryBuilder_cascade_w_relationship + "id_pair_wildcard_term", + QueryBuilder_id_pair_wildcard_term }, { - "up_w_type", - QueryBuilder_up_w_type + "type_pair_term", + QueryBuilder_type_pair_term }, { - "cascade_w_type", - QueryBuilder_cascade_w_type + "pair_term_w_var", + QueryBuilder_pair_term_w_var }, { - "cascade_desc", - QueryBuilder_cascade_desc + "2_pair_terms_w_var", + QueryBuilder_2_pair_terms_w_var }, { - "named_query", - QueryBuilder_named_query + "set_var", + QueryBuilder_set_var }, { - "term_w_write", - QueryBuilder_term_w_write + "set_2_vars", + QueryBuilder_set_2_vars }, { - "term_w_read", - QueryBuilder_term_w_read + "set_var_by_name", + QueryBuilder_set_var_by_name }, { - "iter_w_stage", - QueryBuilder_iter_w_stage - } -}; - -bake_test_case FilterBuilder_testcases[] = { - { - "builder_assign_same_type", - FilterBuilder_builder_assign_same_type + "set_2_vars_by_name", + QueryBuilder_set_2_vars_by_name }, { - "builder_assign_from_empty", - FilterBuilder_builder_assign_from_empty + "expr_w_var", + QueryBuilder_expr_w_var }, { - "builder_assign_to_empty", - FilterBuilder_builder_assign_to_empty + "set_var_on_query", + QueryBuilder_set_var_on_query }, { - "builder_build", - FilterBuilder_builder_build + "set_var_by_name_on_query", + QueryBuilder_set_var_by_name_on_query }, { - "builder_build_to_auto", - FilterBuilder_builder_build_to_auto + "set_table_var", + QueryBuilder_set_table_var }, { - "builder_build_n_statements", - FilterBuilder_builder_build_n_statements + "set_range_var", + QueryBuilder_set_range_var }, { - "builder_force_assign_operator", - FilterBuilder_builder_force_assign_operator + "set_table_var_chained", + QueryBuilder_set_table_var_chained }, { - "1_type", - FilterBuilder_1_type + "set_range_var_chained", + QueryBuilder_set_range_var_chained }, { "add_1_type", - FilterBuilder_add_1_type + QueryBuilder_add_1_type }, { "add_2_types", - FilterBuilder_add_2_types + QueryBuilder_add_2_types }, { "add_1_type_w_1_type", - FilterBuilder_add_1_type_w_1_type + QueryBuilder_add_1_type_w_1_type }, { "add_2_types_w_1_type", - FilterBuilder_add_2_types_w_1_type + QueryBuilder_add_2_types_w_1_type }, { "add_pair", - FilterBuilder_add_pair + QueryBuilder_add_pair }, { "add_not", - FilterBuilder_add_not + QueryBuilder_add_not }, { "add_or", - FilterBuilder_add_or + QueryBuilder_add_or }, { "add_optional", - FilterBuilder_add_optional + QueryBuilder_add_optional }, { "ptr_type", - FilterBuilder_ptr_type + QueryBuilder_ptr_type }, { "const_type", - FilterBuilder_const_type + QueryBuilder_const_type }, { "string_term", - FilterBuilder_string_term + QueryBuilder_string_term }, { "singleton_term", - FilterBuilder_singleton_term + QueryBuilder_singleton_term }, { "isa_superset_term", - FilterBuilder_isa_superset_term + QueryBuilder_isa_superset_term }, { "isa_self_superset_term", - FilterBuilder_isa_self_superset_term + QueryBuilder_isa_self_superset_term }, { "childof_superset_term", - FilterBuilder_childof_superset_term + QueryBuilder_childof_superset_term }, { "childof_self_superset_term", - FilterBuilder_childof_self_superset_term + QueryBuilder_childof_self_superset_term }, { "isa_superset_term_w_each", - FilterBuilder_isa_superset_term_w_each + QueryBuilder_isa_superset_term_w_each }, { "isa_self_superset_term_w_each", - FilterBuilder_isa_self_superset_term_w_each + QueryBuilder_isa_self_superset_term_w_each }, { "childof_superset_term_w_each", - FilterBuilder_childof_superset_term_w_each + QueryBuilder_childof_superset_term_w_each }, { "childof_self_superset_term_w_each", - FilterBuilder_childof_self_superset_term_w_each + QueryBuilder_childof_self_superset_term_w_each }, { "isa_superset_shortcut", - FilterBuilder_isa_superset_shortcut + QueryBuilder_isa_superset_shortcut }, { "isa_superset_shortcut_w_self", - FilterBuilder_isa_superset_shortcut_w_self + QueryBuilder_isa_superset_shortcut_w_self }, { "childof_superset_shortcut", - FilterBuilder_childof_superset_shortcut + QueryBuilder_childof_superset_shortcut }, { "childof_superset_shortcut_w_self", - FilterBuilder_childof_superset_shortcut_w_self + QueryBuilder_childof_superset_shortcut_w_self }, { "relation", - FilterBuilder_relation + QueryBuilder_relation }, { "relation_w_object_wildcard", - FilterBuilder_relation_w_object_wildcard + QueryBuilder_relation_w_object_wildcard }, { "relation_w_predicate_wildcard", - FilterBuilder_relation_w_predicate_wildcard + QueryBuilder_relation_w_predicate_wildcard }, { "add_pair_w_rel_type", - FilterBuilder_add_pair_w_rel_type + QueryBuilder_add_pair_w_rel_type }, { "template_term", - FilterBuilder_template_term + QueryBuilder_template_term }, { "explicit_subject_w_id", - FilterBuilder_explicit_subject_w_id + QueryBuilder_explicit_subject_w_id }, { "explicit_subject_w_type", - FilterBuilder_explicit_subject_w_type + QueryBuilder_explicit_subject_w_type }, { "explicit_object_w_id", - FilterBuilder_explicit_object_w_id + QueryBuilder_explicit_object_w_id }, { "explicit_object_w_type", - FilterBuilder_explicit_object_w_type + QueryBuilder_explicit_object_w_type }, { "explicit_term", - FilterBuilder_explicit_term + QueryBuilder_explicit_term }, { "explicit_term_w_type", - FilterBuilder_explicit_term_w_type + QueryBuilder_explicit_term_w_type }, { "explicit_term_w_pair_type", - FilterBuilder_explicit_term_w_pair_type + QueryBuilder_explicit_term_w_pair_type }, { "explicit_term_w_id", - FilterBuilder_explicit_term_w_id + QueryBuilder_explicit_term_w_id }, { "explicit_term_w_pair_id", - FilterBuilder_explicit_term_w_pair_id + QueryBuilder_explicit_term_w_pair_id }, { "1_term_to_empty", - FilterBuilder_1_term_to_empty + QueryBuilder_1_term_to_empty }, { "2_subsequent_args", - FilterBuilder_2_subsequent_args - }, - { - "filter_as_arg", - FilterBuilder_filter_as_arg + QueryBuilder_2_subsequent_args }, { "filter_as_move_arg", - FilterBuilder_filter_as_move_arg + QueryBuilder_filter_as_move_arg }, { "filter_as_return", - FilterBuilder_filter_as_return + QueryBuilder_filter_as_return }, { "filter_copy", - FilterBuilder_filter_copy + QueryBuilder_filter_copy }, { "world_each_filter_1_component", - FilterBuilder_world_each_filter_1_component + QueryBuilder_world_each_filter_1_component }, { "world_each_filter_2_components", - FilterBuilder_world_each_filter_2_components + QueryBuilder_world_each_filter_2_components }, { "world_each_filter_1_component_no_entity", - FilterBuilder_world_each_filter_1_component_no_entity + QueryBuilder_world_each_filter_1_component_no_entity }, { "world_each_filter_2_components_no_entity", - FilterBuilder_world_each_filter_2_components_no_entity + QueryBuilder_world_each_filter_2_components_no_entity }, { "10_terms", - FilterBuilder_10_terms + QueryBuilder_10_terms + }, + { + "16_terms", + QueryBuilder_16_terms + }, + { + "32_terms", + QueryBuilder_32_terms }, { - "20_terms", - FilterBuilder_20_terms + "33_terms", + QueryBuilder_33_terms }, { "term_after_arg", - FilterBuilder_term_after_arg + QueryBuilder_term_after_arg }, { "name_arg", - FilterBuilder_name_arg + QueryBuilder_name_arg }, { "const_in_term", - FilterBuilder_const_in_term + QueryBuilder_const_in_term }, { "const_optional", - FilterBuilder_const_optional + QueryBuilder_const_optional }, { "create_w_no_template_args", - FilterBuilder_create_w_no_template_args + QueryBuilder_create_w_no_template_args }, { "2_terms_w_expr", - FilterBuilder_2_terms_w_expr + QueryBuilder_2_terms_w_expr }, { "assert_on_uninitialized_term", - FilterBuilder_assert_on_uninitialized_term + QueryBuilder_assert_on_uninitialized_term }, { "operator_shortcuts", - FilterBuilder_operator_shortcuts + QueryBuilder_operator_shortcuts }, { "inout_shortcuts", - FilterBuilder_inout_shortcuts + QueryBuilder_inout_shortcuts }, { "iter_column_w_const_as_array", - FilterBuilder_iter_column_w_const_as_array + QueryBuilder_iter_column_w_const_as_array }, { "iter_column_w_const_as_ptr", - FilterBuilder_iter_column_w_const_as_ptr + QueryBuilder_iter_column_w_const_as_ptr }, { "iter_column_w_const_deref", - FilterBuilder_iter_column_w_const_deref + QueryBuilder_iter_column_w_const_deref }, { "with_id", - FilterBuilder_with_id + QueryBuilder_with_id }, { "with_name", - FilterBuilder_with_name + QueryBuilder_with_name }, { "with_component", - FilterBuilder_with_component + QueryBuilder_with_component }, { "with_pair_id", - FilterBuilder_with_pair_id + QueryBuilder_with_pair_id }, { "with_pair_name", - FilterBuilder_with_pair_name + QueryBuilder_with_pair_name }, { "with_pair_components", - FilterBuilder_with_pair_components + QueryBuilder_with_pair_components }, { "with_pair_component_id", - FilterBuilder_with_pair_component_id + QueryBuilder_with_pair_component_id }, { "with_pair_component_name", - FilterBuilder_with_pair_component_name + QueryBuilder_with_pair_component_name + }, + { + "with_pair_name_component_id", + QueryBuilder_with_pair_name_component_id }, { "with_enum", - FilterBuilder_with_enum + QueryBuilder_with_enum }, { "without_id", - FilterBuilder_without_id + QueryBuilder_without_id }, { "without_name", - FilterBuilder_without_name + QueryBuilder_without_name }, { "without_component", - FilterBuilder_without_component + QueryBuilder_without_component }, { "without_pair_id", - FilterBuilder_without_pair_id + QueryBuilder_without_pair_id }, { "without_pair_name", - FilterBuilder_without_pair_name + QueryBuilder_without_pair_name }, { "without_pair_components", - FilterBuilder_without_pair_components + QueryBuilder_without_pair_components }, { "without_pair_component_id", - FilterBuilder_without_pair_component_id + QueryBuilder_without_pair_component_id }, { "without_pair_component_name", - FilterBuilder_without_pair_component_name + QueryBuilder_without_pair_component_name + }, + { + "without_pair_name_component_id", + QueryBuilder_without_pair_name_component_id }, { "without_enum", - FilterBuilder_without_enum + QueryBuilder_without_enum }, { "write_id", - FilterBuilder_write_id + QueryBuilder_write_id }, { "write_name", - FilterBuilder_write_name + QueryBuilder_write_name }, { "write_component", - FilterBuilder_write_component + QueryBuilder_write_component }, { "write_pair_id", - FilterBuilder_write_pair_id + QueryBuilder_write_pair_id }, { "write_pair_name", - FilterBuilder_write_pair_name + QueryBuilder_write_pair_name }, { "write_pair_components", - FilterBuilder_write_pair_components + QueryBuilder_write_pair_components }, { "write_pair_component_id", - FilterBuilder_write_pair_component_id + QueryBuilder_write_pair_component_id }, { "write_pair_component_name", - FilterBuilder_write_pair_component_name + QueryBuilder_write_pair_component_name }, { "write_enum", - FilterBuilder_write_enum + QueryBuilder_write_enum }, { "read_id", - FilterBuilder_read_id + QueryBuilder_read_id }, { "read_name", - FilterBuilder_read_name + QueryBuilder_read_name }, { "read_component", - FilterBuilder_read_component + QueryBuilder_read_component }, { "read_pair_id", - FilterBuilder_read_pair_id + QueryBuilder_read_pair_id }, { "read_pair_name", - FilterBuilder_read_pair_name + QueryBuilder_read_pair_name }, { "read_pair_components", - FilterBuilder_read_pair_components + QueryBuilder_read_pair_components }, { "read_pair_component_id", - FilterBuilder_read_pair_component_id + QueryBuilder_read_pair_component_id }, { "read_pair_component_name", - FilterBuilder_read_pair_component_name + QueryBuilder_read_pair_component_name }, { "read_enum", - FilterBuilder_read_enum + QueryBuilder_read_enum }, { "assign_after_init", - FilterBuilder_assign_after_init + QueryBuilder_assign_after_init }, { "iter_w_stage", - FilterBuilder_iter_w_stage + QueryBuilder_iter_w_stage }, { "with_t_inout", - FilterBuilder_with_t_inout + QueryBuilder_with_t_inout }, { "with_T_inout", - FilterBuilder_with_T_inout + QueryBuilder_with_T_inout }, { "with_R_T_inout", - FilterBuilder_with_R_T_inout + QueryBuilder_with_R_T_inout }, { "with_R_t_inout", - FilterBuilder_with_R_t_inout + QueryBuilder_with_R_t_inout }, { "with_r_t_inout", - FilterBuilder_with_r_t_inout - } -}; - -bake_test_case RuleBuilder_testcases[] = { + QueryBuilder_with_r_t_inout + }, { - "1_type", - RuleBuilder_1_type + "optional_tag_is_set", + QueryBuilder_optional_tag_is_set }, { - "2_types", - RuleBuilder_2_types + "group_by_raw", + QueryBuilder_group_by_raw }, { - "id_term", - RuleBuilder_id_term + "group_by_template", + QueryBuilder_group_by_template }, { - "type_term", - RuleBuilder_type_term + "group_by_iter_one", + QueryBuilder_group_by_iter_one }, { - "id_pair_term", - RuleBuilder_id_pair_term + "group_by_iter_one_template", + QueryBuilder_group_by_iter_one_template }, { - "id_pair_wildcard_term", - RuleBuilder_id_pair_wildcard_term + "group_by_iter_one_all_groups", + QueryBuilder_group_by_iter_one_all_groups }, { - "type_pair_term", - RuleBuilder_type_pair_term + "group_by_default_func_w_id", + QueryBuilder_group_by_default_func_w_id }, { - "pair_term_w_var", - RuleBuilder_pair_term_w_var + "group_by_default_func_w_type", + QueryBuilder_group_by_default_func_w_type }, { - "2_pair_terms_w_var", - RuleBuilder_2_pair_terms_w_var + "group_by_callbacks", + QueryBuilder_group_by_callbacks }, { - "set_var", - RuleBuilder_set_var + "set_group_on_query", + QueryBuilder_set_group_on_query }, { - "set_2_vars", - RuleBuilder_set_2_vars + "set_group_type_on_query", + QueryBuilder_set_group_type_on_query }, { - "set_var_by_name", - RuleBuilder_set_var_by_name + "create_w_no_template_args", + QueryBuilder_create_w_no_template_args }, { - "set_2_vars_by_name", - RuleBuilder_set_2_vars_by_name + "any_wildcard", + QueryBuilder_any_wildcard }, { - "expr_w_var", - RuleBuilder_expr_w_var + "cascade", + QueryBuilder_cascade }, { - "get_first", - RuleBuilder_get_first + "cascade_w_relationship", + QueryBuilder_cascade_w_relationship }, { - "get_count_direct", - RuleBuilder_get_count_direct + "up_w_type", + QueryBuilder_up_w_type }, { - "get_is_true_direct", - RuleBuilder_get_is_true_direct + "cascade_w_type", + QueryBuilder_cascade_w_type }, { - "get_first_direct", - RuleBuilder_get_first_direct + "cascade_desc", + QueryBuilder_cascade_desc + }, + { + "named_query", + QueryBuilder_named_query + }, + { + "term_w_write", + QueryBuilder_term_w_write + }, + { + "term_w_read", + QueryBuilder_term_w_read }, { "var_src_w_prefixed_name", - RuleBuilder_var_src_w_prefixed_name + QueryBuilder_var_src_w_prefixed_name }, { "var_first_w_prefixed_name", - RuleBuilder_var_first_w_prefixed_name + QueryBuilder_var_first_w_prefixed_name }, { "var_second_w_prefixed_name", - RuleBuilder_var_second_w_prefixed_name + QueryBuilder_var_second_w_prefixed_name }, { "term_w_second_var_string", - RuleBuilder_term_w_second_var_string + QueryBuilder_term_w_second_var_string }, { "term_type_w_second_var_string", - RuleBuilder_term_type_w_second_var_string + QueryBuilder_term_type_w_second_var_string }, { "named_rule", - RuleBuilder_named_rule + QueryBuilder_named_rule }, { "named_scoped_rule", - RuleBuilder_named_scoped_rule + QueryBuilder_named_scoped_rule }, { "is_valid", - RuleBuilder_is_valid + QueryBuilder_is_valid }, { "unresolved_by_name", - RuleBuilder_unresolved_by_name + QueryBuilder_unresolved_by_name }, { "scope", - RuleBuilder_scope - }, - { - "iter_w_stage", - RuleBuilder_iter_w_stage - }, - { - "inspect_terms_w_expr", - RuleBuilder_inspect_terms_w_expr - }, - { - "find", - RuleBuilder_find - }, - { - "find_not_found", - RuleBuilder_find_not_found - }, - { - "find_w_entity", - RuleBuilder_find_w_entity + QueryBuilder_scope } }; @@ -4552,8 +4728,8 @@ bake_test_case SystemBuilder_testcases[] = { SystemBuilder_10_terms }, { - "20_terms", - SystemBuilder_20_terms + "16_terms", + SystemBuilder_16_terms }, { "name_arg", @@ -4595,8 +4771,8 @@ bake_test_case Observer_testcases[] = { Observer_10_terms }, { - "20_terms", - Observer_20_terms + "16_terms", + Observer_16_terms }, { "2_entities_iter", @@ -4622,6 +4798,18 @@ bake_test_case Observer_testcases[] = { "yield_existing_2_terms", Observer_yield_existing_2_terms }, + { + "yield_existing_on_create_flag", + Observer_yield_existing_on_create_flag + }, + { + "yield_existing_on_delete_flag", + Observer_yield_existing_on_delete_flag + }, + { + "yield_existing_on_create_delete_flag", + Observer_yield_existing_on_create_delete_flag + }, { "default_ctor", Observer_default_ctor @@ -4662,6 +4850,22 @@ bake_test_case Observer_testcases[] = { "run_callback", Observer_run_callback }, + { + "run_callback_w_1_field", + Observer_run_callback_w_1_field + }, + { + "run_callback_w_2_fields", + Observer_run_callback_w_2_fields + }, + { + "run_callback_w_yield_existing_1_field", + Observer_run_callback_w_yield_existing_1_field + }, + { + "run_callback_w_yield_existing_2_fields", + Observer_run_callback_w_yield_existing_2_fields + }, { "get_query", Observer_get_query @@ -4674,6 +4878,10 @@ bake_test_case Observer_testcases[] = { "on_set_w_defer_set", Observer_on_set_w_defer_set }, + { + "on_set_w_set_sparse", + Observer_on_set_w_set_sparse + }, { "on_add_singleton", Observer_on_add_singleton @@ -4701,117 +4909,82 @@ bake_test_case Observer_testcases[] = { { "name_from_root", Observer_name_from_root - } -}; - -bake_test_case Filter_testcases[] = { - { - "term_each_component", - Filter_term_each_component }, { - "term_each_tag", - Filter_term_each_tag + "term_index", + Observer_term_index }, { - "term_each_id", - Filter_term_each_id + "implicit_register_in_emit_for_named_entity", + Observer_implicit_register_in_emit_for_named_entity }, { - "term_each_pair_type", - Filter_term_each_pair_type + "add_to_named_in_emit_for_named_entity", + Observer_add_to_named_in_emit_for_named_entity }, { - "term_each_pair_id", - Filter_term_each_pair_id + "implicit_register_in_emit_for_named_entity_w_defer", + Observer_implicit_register_in_emit_for_named_entity_w_defer }, { - "term_each_pair_relation_wildcard", - Filter_term_each_pair_relation_wildcard + "add_to_named_in_emit_for_named_entity_w_defer", + Observer_add_to_named_in_emit_for_named_entity_w_defer }, { - "term_each_pair_object_wildcard", - Filter_term_each_pair_object_wildcard + "register_twice_w_each", + Observer_register_twice_w_each }, { - "default_ctor_no_assign", - Filter_default_ctor_no_assign + "register_twice_w_run", + Observer_register_twice_w_run }, { - "term_get_id", - Filter_term_get_id + "register_twice_w_run_each", + Observer_register_twice_w_run_each }, { - "term_get_subj", - Filter_term_get_subj + "register_twice_w_each_run", + Observer_register_twice_w_each_run }, { - "term_get_pred", - Filter_term_get_pred + "other_table", + Observer_other_table }, { - "term_get_obj", - Filter_term_get_obj + "other_table_w_pair", + Observer_other_table_w_pair }, { - "get_first", - Filter_get_first + "other_table_w_pair_wildcard", + Observer_other_table_w_pair_wildcard }, { - "get_count_direct", - Filter_get_count_direct + "on_add_inherited", + Observer_on_add_inherited }, { - "get_is_true_direct", - Filter_get_is_true_direct + "on_set_inherited", + Observer_on_set_inherited }, { - "get_first_direct", - Filter_get_first_direct + "on_remove_inherited", + Observer_on_remove_inherited }, { - "each_w_no_this", - Filter_each_w_no_this + "on_set_after_remove_override", + Observer_on_set_after_remove_override }, { - "each_w_iter_no_this", - Filter_each_w_iter_no_this + "on_set_after_remove_override_create_observer_before", + Observer_on_set_after_remove_override_create_observer_before }, { - "invalid_each_w_no_this", - Filter_invalid_each_w_no_this + "on_set_w_override_after_delete", + Observer_on_set_w_override_after_delete }, { - "named_filter", - Filter_named_filter - }, - { - "named_scoped_filter", - Filter_named_scoped_filter - }, - { - "set_this_var", - Filter_set_this_var - }, - { - "inspect_terms_w_expr", - Filter_inspect_terms_w_expr - }, - { - "find", - Filter_find - }, - { - "find_not_found", - Filter_find_not_found - }, - { - "find_w_entity", - Filter_find_w_entity - }, - { - "optional_pair_term", - Filter_optional_pair_term + "on_set_w_override_after_clear", + Observer_on_set_w_override_after_clear } }; @@ -4905,12 +5078,12 @@ bake_test_case ComponentLifecycle_testcases[] = { ComponentLifecycle_struct_w_vector_set_2_remove_w_tag }, { - "get_mut_new", - ComponentLifecycle_get_mut_new + "ensure_new", + ComponentLifecycle_ensure_new }, { - "get_mut_existing", - ComponentLifecycle_get_mut_existing + "ensure_existing", + ComponentLifecycle_ensure_existing }, { "implicit_component", @@ -5052,6 +5225,42 @@ bake_test_case ComponentLifecycle_testcases[] = { "on_set_hook_w_entity", ComponentLifecycle_on_set_hook_w_entity }, + { + "on_add_hook_sparse", + ComponentLifecycle_on_add_hook_sparse + }, + { + "on_remove_hook_sparse", + ComponentLifecycle_on_remove_hook_sparse + }, + { + "on_set_hook_sparse", + ComponentLifecycle_on_set_hook_sparse + }, + { + "on_add_hook_sparse_w_entity", + ComponentLifecycle_on_add_hook_sparse_w_entity + }, + { + "on_remove_hook_sparse_w_entity", + ComponentLifecycle_on_remove_hook_sparse_w_entity + }, + { + "on_set_hook_sparse_w_entity", + ComponentLifecycle_on_set_hook_sparse_w_entity + }, + { + "on_add_hook_sparse_w_iter", + ComponentLifecycle_on_add_hook_sparse_w_iter + }, + { + "on_remove_hook_sparse_w_iter", + ComponentLifecycle_on_remove_hook_sparse_w_iter + }, + { + "on_set_hook_sparse_w_iter", + ComponentLifecycle_on_set_hook_sparse_w_iter + }, { "chained_hooks", ComponentLifecycle_chained_hooks @@ -5108,6 +5317,10 @@ bake_test_case ComponentLifecycle_testcases[] = { "dtor_with_relation", ComponentLifecycle_dtor_with_relation }, + { + "dtor_relation_target", + ComponentLifecycle_dtor_relation_target + }, { "register_parent_after_child_w_hooks", ComponentLifecycle_register_parent_after_child_w_hooks @@ -5115,6 +5328,18 @@ bake_test_case ComponentLifecycle_testcases[] = { { "register_parent_after_child_w_hooks_implicit", ComponentLifecycle_register_parent_after_child_w_hooks_implicit + }, + { + "sparse_component", + ComponentLifecycle_sparse_component + }, + { + "count_in_add_hook", + ComponentLifecycle_count_in_add_hook + }, + { + "count_in_remove_hook", + ComponentLifecycle_count_in_remove_hook } }; @@ -5167,9 +5392,25 @@ bake_test_case Refs_testcases[] = { "default_ctor", Refs_default_ctor }, + { + "ctor_from_entity", + Refs_ctor_from_entity + }, + { + "implicit_operator_bool", + Refs_implicit_operator_bool + }, { "try_get", Refs_try_get + }, + { + "has", + Refs_has + }, + { + "bool_operator", + Refs_bool_operator } }; @@ -5225,6 +5466,38 @@ bake_test_case Module_testcases[] = { { "import_addons_two_worlds", Module_import_addons_two_worlds + }, + { + "lookup_module_after_reparent", + Module_lookup_module_after_reparent + }, + { + "reparent_module_in_ctor", + Module_reparent_module_in_ctor + }, + { + "implicitly_add_module_to_scopes_component", + Module_implicitly_add_module_to_scopes_component + }, + { + "implicitly_add_module_to_scopes_entity", + Module_implicitly_add_module_to_scopes_entity + }, + { + "rename_namespace_shorter", + Module_rename_namespace_shorter + }, + { + "rename_namespace_longer", + Module_rename_namespace_longer + }, + { + "rename_namespace_nested", + Module_rename_namespace_nested + }, + { + "rename_reparent_root_module", + Module_rename_reparent_root_module } }; @@ -5329,10 +5602,6 @@ bake_test_case ImplicitComponents_testcases[] = { "implicit_ref", ImplicitComponents_implicit_ref }, - { - "implicit_ptr", - ImplicitComponents_implicit_ptr - }, { "implicit_const_ref", ImplicitComponents_implicit_const_ref @@ -5343,13 +5612,6 @@ bake_test_case ImplicitComponents_testcases[] = { } }; -bake_test_case Snapshot_testcases[] = { - { - "simple_snapshot", - Snapshot_simple_snapshot - } -}; - bake_test_case WorldFactory_testcases[] = { { "entity", @@ -5391,10 +5653,6 @@ bake_test_case WorldFactory_testcases[] = { "query_w_expr", WorldFactory_query_w_expr }, - { - "snapshot", - WorldFactory_snapshot - }, { "module", WorldFactory_module @@ -5522,6 +5780,10 @@ bake_test_case World_testcases[] = { "register_component_w_core_name", World_register_component_w_core_name }, + { + "register_nested_component_in_module", + World_register_nested_component_in_module + }, { "count", World_count @@ -5739,8 +6001,8 @@ bake_test_case World_testcases[] = { World_get_alive }, { - "ensure", - World_ensure + "make_alive", + World_make_alive }, { "reset_all", @@ -5829,6 +6091,38 @@ bake_test_case World_testcases[] = { { "delta_time", World_delta_time + }, + { + "atfini", + World_atfini + }, + { + "atfini_w_ctx", + World_atfini_w_ctx + }, + { + "get_mut_T", + World_get_mut_T + }, + { + "get_mut_R_T", + World_get_mut_R_T + }, + { + "world_mini", + World_world_mini + }, + { + "copy_world", + World_copy_world + }, + { + "fini_reentrancy", + World_fini_reentrancy + }, + { + "fini_copy_move_assign", + World_fini_copy_move_assign } }; @@ -5837,6 +6131,10 @@ bake_test_case Singleton_testcases[] = { "set_get_singleton", Singleton_set_get_singleton }, + { + "ensure_singleton", + Singleton_ensure_singleton + }, { "get_mut_singleton", Singleton_get_mut_singleton @@ -6342,6 +6640,10 @@ bake_test_case Meta_testcases[] = { { "component_as_array", Meta_component_as_array + }, + { + "out_of_order_member_declaration", + Meta_out_of_order_member_declaration } }; @@ -6493,12 +6795,21 @@ bake_test_case Doc_testcases[] = { "set_color", Doc_set_color }, + { + "set_uuid", + Doc_set_uuid + }, { "get_name_no_doc_name", Doc_get_name_no_doc_name } }; +const char* QueryBuilder_cache_kind_param[] = {"default", "auto"}; +bake_test_param QueryBuilder_params[] = { + {"cache_kind", (char**)QueryBuilder_cache_kind_param, 2} +}; + static bake_test_suite suites[] = { { "PrettyFunction", @@ -6511,49 +6822,49 @@ static bake_test_suite suites[] = { "Entity", NULL, NULL, - 247, + 276, Entity_testcases }, { "Pairs", NULL, NULL, - 67, + 70, Pairs_testcases }, { "Enum", NULL, NULL, - 36, + 39, Enum_testcases }, { - "Switch", + "Union", NULL, NULL, - 10, - Switch_testcases + 6, + Union_testcases }, { "Paths", NULL, NULL, - 15, + 25, Paths_testcases }, { "System", NULL, NULL, - 68, + 73, System_testcases }, { "Event", NULL, NULL, - 28, + 31, Event_testcases }, { @@ -6567,29 +6878,17 @@ static bake_test_suite suites[] = { "Query", NULL, NULL, - 85, + 116, Query_testcases }, { "QueryBuilder", + QueryBuilder_setup, NULL, - NULL, - 69, - QueryBuilder_testcases - }, - { - "FilterBuilder", - NULL, - NULL, - 113, - FilterBuilder_testcases - }, - { - "RuleBuilder", - NULL, - NULL, - 33, - RuleBuilder_testcases + 166, + QueryBuilder_testcases, + 1, + QueryBuilder_params }, { "SystemBuilder", @@ -6602,70 +6901,56 @@ static bake_test_suite suites[] = { "Observer", NULL, NULL, - 32, + 59, Observer_testcases }, - { - "Filter", - NULL, - NULL, - 27, - Filter_testcases - }, { "ComponentLifecycle", NULL, NULL, - 75, + 88, ComponentLifecycle_testcases }, { "Refs", NULL, NULL, - 13, + 17, Refs_testcases }, { "Module", NULL, NULL, - 13, + 21, Module_testcases }, { "ImplicitComponents", NULL, NULL, - 28, + 27, ImplicitComponents_testcases }, - { - "Snapshot", - NULL, - NULL, - 1, - Snapshot_testcases - }, { "WorldFactory", NULL, NULL, - 12, + 11, WorldFactory_testcases }, { "World", NULL, NULL, - 107, + 116, World_testcases }, { "Singleton", NULL, NULL, - 19, + 20, Singleton_testcases }, { @@ -6679,7 +6964,7 @@ static bake_test_suite suites[] = { "Meta", NULL, NULL, - 55, + 56, Meta_testcases }, { @@ -6693,11 +6978,11 @@ static bake_test_suite suites[] = { "Doc", NULL, NULL, - 5, + 6, Doc_testcases } }; int main(int argc, char *argv[]) { - return bake_test_run("cpp_api", argc, argv, suites, 28); + return bake_test_run("cpp", argc, argv, suites, 24); } diff --git a/vendors/flecs/test/cpp_api/src/util.cpp b/vendors/flecs/test/cpp/src/util.cpp similarity index 90% rename from vendors/flecs/test/cpp_api/src/util.cpp rename to vendors/flecs/test/cpp/src/util.cpp index 4c2f2a3ab..7a212ca90 100644 --- a/vendors/flecs/test/cpp_api/src/util.cpp +++ b/vendors/flecs/test/cpp/src/util.cpp @@ -1,4 +1,4 @@ -#include +#include void install_test_abort(void) { ecs_os_set_api_defaults(); diff --git a/vendors/flecs/test/cpp_api/src/Filter.cpp b/vendors/flecs/test/cpp_api/src/Filter.cpp deleted file mode 100644 index 4054b556e..000000000 --- a/vendors/flecs/test/cpp_api/src/Filter.cpp +++ /dev/null @@ -1,551 +0,0 @@ -#include - -void Filter_term_each_component(void) { - flecs::world ecs; - - auto e_1 = ecs.entity().set({1, 2}); - auto e_2 = ecs.entity().set({3, 4}); - auto e_3 = ecs.entity().set({5, 6}); - - e_3.add(); - - int32_t count = 0; - ecs.each([&](flecs::entity e, Position& p) { - if (e == e_1) { - test_int(p.x, 1); - test_int(p.y, 2); - count ++; - } - if (e == e_2) { - test_int(p.x, 3); - test_int(p.y, 4); - count ++; - } - if (e == e_3) { - test_int(p.x, 5); - test_int(p.y, 6); - count ++; - } - }); - - test_int(count, 3); -} - -void Filter_term_each_tag(void) { - flecs::world ecs; - - struct Foo { }; - - auto e_1 = ecs.entity().add(); - auto e_2 = ecs.entity().add(); - auto e_3 = ecs.entity().add(); - - e_3.add(); - - int32_t count = 0; - ecs.each([&](flecs::entity e, Foo) { - if (e == e_1 || e == e_2 || e == e_3) { - count ++; - } - }); - - test_int(count, 3); -} - -void Filter_term_each_id(void) { - flecs::world ecs; - - auto foo = ecs.entity(); - - auto e_1 = ecs.entity().add(foo); - auto e_2 = ecs.entity().add(foo); - auto e_3 = ecs.entity().add(foo); - - e_3.add(); - - int32_t count = 0; - ecs.each(foo, [&](flecs::entity e) { - if (e == e_1 || e == e_2 || e == e_3) { - count ++; - } - }); - - test_int(count, 3); -} - -void Filter_term_each_pair_type(void) { - flecs::world ecs; - - struct Rel { }; - struct Obj { }; - - auto e_1 = ecs.entity().add(); - auto e_2 = ecs.entity().add(); - auto e_3 = ecs.entity().add(); - - e_3.add(); - - int32_t count = 0; - ecs.each>([&](flecs::entity e, flecs::pair) { - if (e == e_1 || e == e_2 || e == e_3) { - count ++; - } - }); - - test_int(count, 3); -} - -void Filter_term_each_pair_id(void) { - flecs::world ecs; - - auto rel = ecs.entity(); - auto obj = ecs.entity(); - - auto e_1 = ecs.entity().add(rel, obj); - auto e_2 = ecs.entity().add(rel, obj); - auto e_3 = ecs.entity().add(rel, obj); - - e_3.add(); - - int32_t count = 0; - ecs.each(ecs.pair(rel, obj), [&](flecs::entity e) { - if (e == e_1 || e == e_2 || e == e_3) { - count ++; - } - }); - - test_int(count, 3); -} - -void Filter_term_each_pair_relation_wildcard(void) { - flecs::world ecs; - - auto rel_1 = ecs.entity(); - auto rel_2 = ecs.entity(); - auto obj = ecs.entity(); - - auto e_1 = ecs.entity().add(rel_1, obj); - auto e_2 = ecs.entity().add(rel_1, obj); - auto e_3 = ecs.entity().add(rel_2, obj); - - e_3.add(); - - int32_t count = 0; - ecs.each(ecs.pair(flecs::Wildcard, obj), [&](flecs::entity e) { - if (e == e_1 || e == e_2 || e == e_3) { - count ++; - } - }); - - test_int(count, 3); -} - -void Filter_term_each_pair_object_wildcard(void) { - flecs::world ecs; - - auto rel = ecs.entity(); - auto obj_1 = ecs.entity(); - auto obj_2 = ecs.entity(); - - auto e_1 = ecs.entity().add(rel, obj_1); - auto e_2 = ecs.entity().add(rel, obj_1); - auto e_3 = ecs.entity().add(rel, obj_2); - - e_3.add(); - - int32_t count = 0; - ecs.each(ecs.pair(rel, flecs::Wildcard), [&](flecs::entity e) { - if (e == e_1 || e == e_2 || e == e_3) { - count ++; - } - }); - - test_int(count, 3); -} - -void Filter_default_ctor_no_assign(void) { - flecs::filter<> f; - - // Make sure code compiles & works - test_assert(true); -} - -void Filter_term_get_id(void) { - flecs::world ecs; - - auto Foo = ecs.entity(); - auto Bar = ecs.entity(); - - auto q = ecs.query_builder() - .term() - .term() - .term(Foo, Bar) - .build(); - - test_int(q.field_count(), 3); - - flecs::term - t = q.term(0); - test_assert(t.id() == ecs.id()); - t = q.term(1); - test_assert(t.id() == ecs.id()); - t = q.term(2); - test_assert(t.id() == ecs.pair(Foo, Bar)); -} - -void Filter_term_get_subj(void) { - flecs::world ecs; - - auto Foo = ecs.entity(); - auto Bar = ecs.entity(); - auto Src = ecs.entity(); - - auto q = ecs.query_builder() - .term() - .term().src(Src) - .term(Foo, Bar) - .build(); - - test_int(q.field_count(), 3); - - flecs::term - t = q.term(0); - test_assert(t.get_src() == flecs::This); - t = q.term(1); - test_assert(t.get_src() == Src); - t = q.term(2); - test_assert(t.get_src() == flecs::This); -} - -void Filter_term_get_pred(void) { - flecs::world ecs; - - auto Foo = ecs.entity(); - auto Bar = ecs.entity(); - auto Src = ecs.entity(); - - auto q = ecs.query_builder() - .term() - .term().src(Src) - .term(Foo, Bar) - .build(); - - test_int(q.field_count(), 3); - - flecs::term - t = q.term(0); - test_assert(t.get_first() == ecs.id()); - t = q.term(1); - test_assert(t.get_first() == ecs.id()); - t = q.term(2); - test_assert(t.get_first() == Foo); -} - -void Filter_term_get_obj(void) { - flecs::world ecs; - - auto Foo = ecs.entity(); - auto Bar = ecs.entity(); - auto Src = ecs.entity(); - - auto q = ecs.query_builder() - .term() - .term().src(Src) - .term(Foo, Bar) - .build(); - - test_int(q.field_count(), 3); - - flecs::term - t = q.term(0); - test_assert(t.get_second() == 0); - t = q.term(1); - test_assert(t.get_second() == 0); - t = q.term(2); - test_assert(t.get_second() == Bar); -} - -void Filter_get_first(void) { - flecs::world ecs; - - struct A {}; - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); - - auto q = ecs.filter(); - - auto first = q.iter().first(); - test_assert(first != 0); - test_assert(first == e1); -} - -void Filter_get_count_direct(void) { - flecs::world ecs; - - struct A {}; - - ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); - - auto q = ecs.filter(); - - test_int(3, q.count()); -} - -void Filter_get_is_true_direct(void) { - flecs::world ecs; - - struct A {}; - struct B {}; - - ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); - - auto q_1 = ecs.filter(); - auto q_2 = ecs.filter(); - - test_bool(true, q_1.is_true()); - test_bool(false, q_2.is_true()); -} - -void Filter_get_first_direct(void) { - flecs::world ecs; - - struct A {}; - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); - - auto q = ecs.filter(); - - auto first = q.first(); - test_assert(first != 0); - test_assert(first == e1); -} - -void Filter_each_w_no_this(void) { - flecs::world ecs; - - auto e = ecs.entity() - .set({10, 20}) - .set({1, 2}); - - auto f = ecs.filter_builder() - .arg(1).src(e) - .arg(2).src(e) - .build(); - - int32_t count = 0; - - f.each([&](Position& p, Velocity& v) { - count ++; - test_int(p.x, 10); - test_int(p.y, 20); - test_int(v.x, 1); - test_int(v.y, 2); - }); - - test_int(count, 1); -} - -void Filter_each_w_iter_no_this(void) { - flecs::world ecs; - - auto e = ecs.entity() - .set({10, 20}) - .set({1, 2}); - - auto f = ecs.filter_builder() - .arg(1).src(e) - .arg(2).src(e) - .build(); - - int32_t count = 0; - - f.each([&](flecs::iter& it, size_t index, Position& p, Velocity& v) { - count ++; - test_int(p.x, 10); - test_int(p.y, 20); - test_int(v.x, 1); - test_int(v.y, 2); - test_int(index, 0); - test_int(it.count(), 0); - }); - - test_int(count, 1); -} - -void Filter_invalid_each_w_no_this(void) { - install_test_abort(); - - flecs::world ecs; - - auto e = ecs.entity() - .set({10, 20}) - .set({1, 2}); - - auto f = ecs.filter_builder() - .arg(1).src(e) - .arg(2).src(e) - .build(); - - test_expect_abort(); - - f.each([&](flecs::entity e, Position& p, Velocity& v) { }); -} - -void Filter_named_filter(void) { - flecs::world ecs; - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add(); - - auto q = ecs.filter("my_query"); - - int32_t count = 0; - q.each([&](flecs::entity e, Position&) { - test_assert(e == e1 || e == e2); - count ++; - }); - test_int(count, 2); - - flecs::entity qe = q.entity(); - test_assert(qe != 0); - test_str(qe.name(), "my_query"); -} - -void Filter_named_scoped_filter(void) { - flecs::world ecs; - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add(); - - auto q = ecs.filter("my::query"); - - int32_t count = 0; - q.each([&](flecs::entity e, Position&) { - test_assert(e == e1 || e == e2); - count ++; - }); - test_int(count, 2); - - flecs::entity qe = q.entity(); - test_assert(qe != 0); - test_str(qe.name(), "query"); - test_str(qe.path(), "::my::query"); -} - -void Filter_set_this_var(void) { - flecs::world ecs; - - /* auto e_1 = */ ecs.entity().set({1, 2}); - auto e_2 = ecs.entity().set({3, 4}); - /* auto e_3 = */ ecs.entity().set({5, 6}); - - auto q = ecs.filter("my::query"); - - int32_t count = 0; - q.iter().set_var(0, e_2).each([&](flecs::entity e, Position&) { - test_assert(e == e_2); - count ++; - }); - test_int(count, 1); -} - -void Filter_inspect_terms_w_expr(void) { - flecs::world ecs; - - flecs::filter<> f = ecs.filter_builder() - .expr("(ChildOf,0)") - .build(); - - int32_t count = 0; - f.each_term([&](flecs::term &term) { - test_assert(term.id().is_pair()); - count ++; - }); - - test_int(count, 1); -} - -void Filter_find(void) { - flecs::world ecs; - - /* auto e1 = */ ecs.entity().set({10, 20}); - auto e2 = ecs.entity().set({20, 30}); - - auto q = ecs.filter(); - - auto r = q.find([](Position& p) { - return p.x == 20; - }); - - test_assert(r == e2); -} - -void Filter_find_not_found(void) { - flecs::world ecs; - - /* auto e1 = */ ecs.entity().set({10, 20}); - /* auto e2 = */ ecs.entity().set({20, 30}); - - auto q = ecs.filter(); - - auto r = q.find([](Position& p) { - return p.x == 30; - }); - - test_assert(!r); -} - -void Filter_find_w_entity(void) { - flecs::world ecs; - - /* auto e1 = */ ecs.entity().set({10, 20}).set({20, 30}); - auto e2 = ecs.entity().set({20, 30}).set({20, 30}); - - auto q = ecs.filter(); - - auto r = q.find([](flecs::entity e, Position& p) { - return p.x == e.get()->x && - p.y == e.get()->y; - }); - - test_assert(r == e2); -} - -void Filter_optional_pair_term(void) { - flecs::world ecs; - - ecs.entity() - .add() - .emplace(1.0f, 2.0f); - ecs.entity() - .add(); - - int32_t with_pair = 0, without_pair = 0; - - auto f = ecs.filter_builder*>() - .with() - .build(); - - f.each([&](flecs::entity e, Position* p) { - if (p) { - with_pair++; - test_flt(1.0f, p->x); - test_flt(2.0f, p->y); - } else { - without_pair++; - } - }); - - ecs.progress(1.0); - - test_int(1, with_pair); - test_int(1, without_pair); -} diff --git a/vendors/flecs/test/cpp_api/src/FilterBuilder.cpp b/vendors/flecs/test/cpp_api/src/FilterBuilder.cpp deleted file mode 100644 index 0eb09fb12..000000000 --- a/vendors/flecs/test/cpp_api/src/FilterBuilder.cpp +++ /dev/null @@ -1,2581 +0,0 @@ -#include - -struct Other { - int32_t value; -}; - -enum Color { - Red, Green, Blue -}; - -void FilterBuilder_builder_assign_same_type(void) { - flecs::world ecs; - - flecs::filter q = - ecs.filter_builder().build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p, Velocity& v) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_builder_assign_to_empty(void) { - flecs::world ecs; - - flecs::filter<> q = ecs.filter_builder().build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_builder_assign_from_empty(void) { - flecs::world ecs; - - flecs::filter<> q = ecs.filter_builder<>() - .term() - .term() - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -template -struct FilterWrapper -{ - FilterWrapper(flecs::filter f) : f_(f) {} - flecs::filter f_; -}; - -void FilterBuilder_builder_force_assign_operator(void) { - flecs::world ecs; - - auto e1 = ecs.entity().set({10, 20}); - - auto f = ecs.entity().emplace>( - ecs.filter_builder().term().build() - ); - - int32_t count = 0; - f.get>()->f_.each([&](flecs::entity e) { - test_assert(e == e1); - count ++; - }); -} - -void FilterBuilder_builder_build(void) { - flecs::world ecs; - - flecs::filter q = - ecs.filter_builder().build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p, Velocity& v) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_builder_build_to_auto(void) { - flecs::world ecs; - - auto q = ecs.filter_builder().build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p, Velocity& v) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_builder_build_n_statements(void) { - flecs::world ecs; - - auto qb = ecs.filter_builder<>(); - qb.term(); - qb.term(); - auto q = qb.build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_1_type(void) { - flecs::world ecs; - - auto q = ecs.filter_builder().build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_add_1_type(void) { - flecs::world ecs; - - auto q = ecs.filter_builder<>() - .term() - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_add_2_types(void) { - flecs::world ecs; - - auto q = ecs.filter_builder<>() - .term() - .term() - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_add_1_type_w_1_type(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .term() - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_add_2_types_w_1_type(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .term() - .term() - .build(); - - auto e1 = ecs.entity().add().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_add_pair(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.filter_builder<>() - .term(Likes, Bob) - .build(); - - auto e1 = ecs.entity().add(Likes, Bob); - ecs.entity().add(Likes, Alice); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_add_not(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .term().oper(flecs::Not) - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_add_or(void) { - flecs::world ecs; - - auto q = ecs.filter_builder<>() - .term().oper(flecs::Or) - .term() - .build(); - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1 || e == e2); - }); - - test_int(count, 2); -} - -void FilterBuilder_add_optional(void) { - flecs::world ecs; - - auto q = ecs.filter_builder<>() - .term() - .term().oper(flecs::Optional) - .build(); - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add().add(); - ecs.entity().add().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1 || e == e2); - }); - - test_int(count, 2); -} - -void FilterBuilder_ptr_type(void) { - flecs::world ecs; - - auto q = ecs.filter_builder().build(); - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add().add(); - ecs.entity().add().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p, Velocity* v) { - count ++; - test_assert(e == e1 || e == e2); - }); - - test_int(count, 2); -} - -void FilterBuilder_const_type(void) { - flecs::world ecs; - - auto q = ecs.filter_builder().build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, const Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_string_term(void) { - flecs::world ecs; - - ecs.component(); - - auto q = ecs.filter_builder<>() - .expr("Position") - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_singleton_term(void) { - flecs::world ecs; - - ecs.set({10}); - - auto q = ecs.filter_builder() - .term().singleton() - .build(); - - auto - e = ecs.entity(); e.set({e}); - e = ecs.entity(); e.set({e}); - e = ecs.entity(); e.set({e}); - - int32_t count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - test_assert(!it.is_self(2)); - test_int(o->value, 10); - - const Other& o_ref = *o; - test_int(o_ref.value, 10); - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 3); -} - -void FilterBuilder_isa_superset_term(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .term().src().up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - - int32_t count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - test_assert(!it.is_self(2)); - test_int(o->value, 10); - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 3); -} - -void FilterBuilder_isa_self_superset_term(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .term().src().self().up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().set({20}); e.set({e}); - e = ecs.entity().set({20}); e.set({e}); - - int32_t count = 0; - int32_t owned_count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - - if (!it.is_self(2)) { - test_int(o->value, 10); - } else { - for (auto i : it) { - test_int(o[i].value, 20); - owned_count ++; - } - } - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 5); - test_int(owned_count, 2); -} - -void FilterBuilder_childof_superset_term(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .term().src().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - - int32_t count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - test_assert(!it.is_self(2)); - test_int(o->value, 10); - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 3); -} - -void FilterBuilder_childof_self_superset_term(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .term().src().self().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().set({20}); e.set({e}); - e = ecs.entity().set({20}); e.set({e}); - - int32_t count = 0; - int32_t owned_count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - - if (!it.is_self(2)) { - test_int(o->value, 10); - } else { - for (auto i : it) { - test_int(o[i].value, 20); - owned_count ++; - } - } - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 5); - test_int(owned_count, 2); -} - -void FilterBuilder_isa_superset_term_w_each(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .arg(2).src().up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_isa_self_superset_term_w_each(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .arg(2).src().self().up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 5); -} - -void FilterBuilder_childof_superset_term_w_each(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .arg(2).src().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_childof_self_superset_term_w_each(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .arg(2).src().self().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 5); -} - -void FilterBuilder_isa_superset_shortcut(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .arg(2).up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().is_a(base); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_isa_superset_shortcut_w_self(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .arg(2).self().up(flecs::IsA) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 5); -} - -void FilterBuilder_childof_superset_shortcut(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .arg(2).up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_childof_superset_shortcut_w_self(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .arg(2).self().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 5); -} - -void FilterBuilder_relation(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.filter_builder() - .term(Likes, Bob) - .build(); - - auto - e = ecs.entity().add(Likes, Bob); e.set({e}); - e = ecs.entity().add(Likes, Bob); e.set({e}); - - e = ecs.entity().add(Likes, Alice); e.set({0}); - e = ecs.entity().add(Likes, Alice); e.set({0}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 2); -} - -void FilterBuilder_relation_w_object_wildcard(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.filter_builder() - .term(Likes, flecs::Wildcard) - .build(); - - auto - e = ecs.entity().add(Likes, Bob); e.set({e}); - e = ecs.entity().add(Likes, Bob); e.set({e}); - - e = ecs.entity().add(Likes, Alice); e.set({e}); - e = ecs.entity().add(Likes, Alice); e.set({e}); - - e = ecs.entity(); e.set({0}); - e = ecs.entity(); e.set({0}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 4); -} - -void FilterBuilder_relation_w_predicate_wildcard(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Dislikes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.filter_builder() - .term(flecs::Wildcard, Alice) - .build(); - - auto - e = ecs.entity().add(Likes, Alice); e.set({e}); - e = ecs.entity().add(Dislikes, Alice); e.set({e}); - - e = ecs.entity().add(Likes, Bob); e.set({0}); - e = ecs.entity().add(Dislikes, Bob); e.set({0}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 2); -} - -void FilterBuilder_add_pair_w_rel_type(void) { - flecs::world ecs; - - struct Likes { }; - - auto Dislikes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.filter_builder() - .term(flecs::Wildcard) - .build(); - - auto - e = ecs.entity().add(Alice); e.set({e}); - e = ecs.entity().add(Dislikes, Alice); e.set({0}); - - e = ecs.entity().add(Bob); e.set({e}); - e = ecs.entity().add(Dislikes, Bob); e.set({0}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 2); -} - -void FilterBuilder_template_term(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .term>() - .build(); - - auto e1 = ecs.entity().add().add>(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_explicit_subject_w_id(void) { - flecs::world ecs; - - auto q = ecs.filter_builder() - .term().id(flecs::This) - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_explicit_subject_w_type(void) { - flecs::world ecs; - - ecs.set({10, 20}); - - auto q = ecs.filter_builder() - .term().src() - .build(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - test_int(p.x, 10); - test_int(p.y, 20); - count ++; - test_assert(e == ecs.singleton()); - }); - - test_int(count, 1); -} - -void FilterBuilder_explicit_object_w_id(void) { - flecs::world ecs; - - auto Likes = ecs.entity("Likes"); - auto Alice = ecs.entity("Alice"); - auto Bob = ecs.entity("Bob"); - - auto q = ecs.filter_builder<>() - .term(Likes).second(Alice) - .build(); - - auto e1 = ecs.entity().add(Likes, Alice); - ecs.entity().add(Likes, Bob); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_explicit_object_w_type(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - struct Alice { }; - auto Bob = ecs.entity(); - - auto q = ecs.filter_builder<>() - .term(Likes).second() - .build(); - - auto e1 = ecs.entity().add(Likes, ecs.id()); - ecs.entity().add(Likes, Bob); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_explicit_term(void) { - flecs::world ecs; - - auto q = ecs.filter_builder<>() - .term(ecs.term()) - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_explicit_term_w_type(void) { - flecs::world ecs; - - auto q = ecs.filter_builder<>() - .term(ecs.term()) - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_explicit_term_w_pair_type(void) { - flecs::world ecs; - - struct Likes { }; - struct Alice { }; - struct Bob { }; - - auto q = ecs.filter_builder<>() - .term(ecs.term()) - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_explicit_term_w_id(void) { - flecs::world ecs; - - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - auto q = ecs.filter_builder<>() - .term(ecs.term(Apples)) - .build(); - - auto e1 = ecs.entity().add(Apples); - ecs.entity().add(Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_explicit_term_w_pair_id(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - auto q = ecs.filter_builder<>() - .term(ecs.term(Likes, Apples)) - .build(); - - auto e1 = ecs.entity().add(Likes, Apples); - ecs.entity().add(Likes, Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_1_term_to_empty(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Apples = ecs.entity(); - - auto qb = ecs.filter_builder<>() - .term(); - - qb.term(Likes, Apples); - - auto q = qb.build(); - - test_int(q.field_count(), 2); - test_int(q.term(0).id(), ecs.id()); - test_int(q.term(1).id(), ecs.pair(Likes, Apples)); -} - -void FilterBuilder_2_subsequent_args(void) { - flecs::world ecs; - - struct Rel { int foo; }; - - int32_t count = 0; - - auto s = ecs.system() - .arg(1).second(flecs::Wildcard) - .arg(2).singleton() - .iter([&](flecs::iter it){ - count += it.count(); - }); - - ecs.entity().add(); - ecs.set({}); - - s.run(); - - test_int(count, 1); -} - -static -int filter_arg(flecs::filter f) { - int32_t count = 0; - - f.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - return count; -} - -void FilterBuilder_filter_as_arg(void) { - flecs::world ecs; - - auto f = ecs.filter(); - - auto e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - test_int(filter_arg(f), 3); -} - -static -int filter_move_arg(flecs::filter&& f) { - int32_t count = 0; - - f.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - return count; -} - -void FilterBuilder_filter_as_move_arg(void) { - flecs::world ecs; - - auto f = ecs.filter(); - - auto e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - test_int(filter_move_arg(ecs.filter()), 3); -} - -static -flecs::filter filter_return(flecs::world& ecs) { - return ecs.filter(); -} - -void FilterBuilder_filter_as_return(void) { - flecs::world ecs; - - auto e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - auto f = filter_return(ecs); - - int32_t count = 0; - - f.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_filter_copy(void) { - flecs::world ecs; - - auto e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - auto f = ecs.filter(); - - auto f_2 = f; - - int32_t count = 0; - - f_2.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_world_each_filter_1_component(void) { - flecs::world ecs; - - auto e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - e = ecs.entity(); - e.set({e}); - - int32_t count = 0; - - ecs.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_world_each_filter_2_components(void) { - flecs::world ecs; - - auto e = ecs.entity(); - e.set({e}) - .set({10, 20}); - - e = ecs.entity(); - e.set({e}) - .set({10, 20}); - - e = ecs.entity(); - e.set({e}) - .set({10, 20}); - - int32_t count = 0; - - ecs.each([&](flecs::entity e, Self& s, Position& p) { - test_assert(e == s.value); - test_int(p.x, 10); - test_int(p.y, 20); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_world_each_filter_1_component_no_entity(void) { - flecs::world ecs; - - ecs.entity() - .set({10, 20}); - - ecs.entity() - .set({10, 20}); - - ecs.entity() - .set({10, 20}) - .set({1, 2}); - - int32_t count = 0; - - ecs.each([&](Position& p) { - test_int(p.x, 10); - test_int(p.y, 20); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_world_each_filter_2_components_no_entity(void) { - flecs::world ecs; - - ecs.entity() - .set({10, 20}) - .set({1, 2}); - - ecs.entity() - .set({10, 20}) - .set({1, 2}); - - ecs.entity() - .set({10, 20}) - .set({1, 2}); - - ecs.entity() - .set({3, 5}); - - ecs.entity() - .set({20, 40}); - - int32_t count = 0; - - ecs.each([&](Position& p, Velocity& v) { - test_int(p.x, 10); - test_int(p.y, 20); - test_int(v.x, 1); - test_int(v.y, 2); - count ++; - }); - - test_int(count, 3); -} - -void FilterBuilder_10_terms(void) { - flecs::world ecs; - - auto f = ecs.filter_builder<>() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .build(); - - test_int(f.field_count(), 10); - - auto e = ecs.entity() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add(); - - int count = 0; - f.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_assert(it.entity(0) == e); - test_int(it.field_count(), 10); - count ++; - }); - - test_int(count, 1); -} - -void FilterBuilder_20_terms(void) { - flecs::world ecs; - - auto f = ecs.filter_builder<>() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .build(); - - test_int(f.field_count(), 20); - - auto e = ecs.entity() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add(); - - int count = 0; - f.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_assert(it.entity(0) == e); - test_int(it.field_count(), 20); - count ++; - }); - - test_int(count, 1); -} - -void FilterBuilder_term_after_arg(void) { - flecs::world ecs; - - auto e_1 = ecs.entity() - .add() - .add() - .add(); - - ecs.entity() - .add() - .add(); - - auto f = ecs.filter_builder() - .arg(1).src(flecs::This) // dummy - .term() - .build(); - - test_int(f.field_count(), 3); - - int count = 0; - f.each([&](flecs::entity e, TagA, TagB) { - test_assert(e == e_1); - count ++; - }); - - test_int(count, 1); -} - -void FilterBuilder_name_arg(void) { - flecs::world ecs; - - auto e = ecs.entity("Foo").set({10, 20}); - - auto f = ecs.filter_builder() - .arg(1).src().name("Foo") - .build(); - - int32_t count = 0; - f.iter([&](flecs::iter& it, Position* p) { - count ++; - test_int(p->x, 10); - test_int(p->y, 20); - test_assert(it.src(1) == e); - }); - - test_int(count, 1); -} - -void FilterBuilder_const_in_term(void) { - flecs::world ecs; - - ecs.entity().set({10, 20}); - - auto f = ecs.filter_builder<>() - .term() - .build(); - - int32_t count = 0; - f.iter([&](flecs::iter& it) { - auto p = it.field(1); - test_assert(it.is_readonly(1)); - for (auto i : it) { - count ++; - test_int(p[i].x, 10); - test_int(p[i].y, 20); - } - }); - - test_int(count, 1); -} - -void FilterBuilder_const_optional(void) { - flecs::world ecs; - - ecs.entity().set({10, 20}).add(); - ecs.entity().add(); - - auto f = ecs.filter_builder().build(); - - int32_t count = 0, set_count = 0; - f.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - if (it.is_set(2)) { - auto p = it.field(2); - test_assert(it.is_readonly(2)); - test_int(p->x, 10); - test_int(p->y, 20); - set_count ++; - } - count++; - }); - - test_int(count, 2); - test_int(set_count, 1); -} - -void FilterBuilder_create_w_no_template_args(void) { - flecs::world ecs; - - auto q = ecs.filter_builder().term().build(); - - auto e1 = ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_2_terms_w_expr(void) { - flecs::world ecs; - - auto a = ecs.entity("A"); - auto b = ecs.entity("B"); - - auto e1 = ecs.entity().add(a).add(b); - - auto f = ecs.filter_builder() - .expr("A, B") - .build(); - - test_int(f.field_count(), 2); - - int32_t count = 0; - f.each([&](flecs::iter& it, size_t index) { - if (it.entity(index) == e1) { - test_assert(it.id(1) == a); - test_assert(it.id(2) == b); - count ++; - } - }); - - test_int(count, 1); -} - -void FilterBuilder_assert_on_uninitialized_term(void) { - install_test_abort(); - - flecs::world ecs; - - ecs.entity("A"); - ecs.entity("B"); - - test_expect_abort(); - - auto f = ecs.filter_builder() - .term() - .term() - .build(); -} - -void FilterBuilder_operator_shortcuts(void) { - flecs::world ecs; - - flecs::entity a = ecs.entity(); - flecs::entity b = ecs.entity(); - flecs::entity c = ecs.entity(); - flecs::entity d = ecs.entity(); - flecs::entity e = ecs.entity(); - flecs::entity f = ecs.entity(); - flecs::entity g = ecs.entity(); - flecs::entity h = ecs.entity(); - - auto filter = ecs.filter_builder() - .term(a).and_() - .term(b).or_() - .term(c) - .term(d).not_() - .term(e).optional() - .term(f).and_from() - .term(g).or_from() - .term(h).not_from() - .build(); - - auto t = filter.term(0); - test_int(t.id(), a); - test_int(t.oper(), flecs::And); - - t = filter.term(1); - test_int(t.id(), b); - test_int(t.oper(), flecs::Or); - - t = filter.term(2); - test_int(t.id(), c); - test_int(t.oper(), flecs::And); - - t = filter.term(3); - test_int(t.id(), d); - test_int(t.oper(), flecs::Not); - - t = filter.term(4); - test_int(t.id(), e); - test_int(t.oper(), flecs::Optional); - - t = filter.term(5); - test_int(t.id(), f); - test_int(t.oper(), flecs::AndFrom); - - t = filter.term(6); - test_int(t.id(), g); - test_int(t.oper(), flecs::OrFrom); - - t = filter.term(7); - test_int(t.id(), h); - test_int(t.oper(), flecs::NotFrom); -} - -void FilterBuilder_inout_shortcuts(void) { - flecs::world ecs; - - flecs::entity a = ecs.entity(); - flecs::entity b = ecs.entity(); - flecs::entity c = ecs.entity(); - flecs::entity d = ecs.entity(); - - auto filter = ecs.filter_builder() - .term(a).in() - .term(b).out() - .term(c).inout() - .term(d).inout_none() - .build(); - - auto t = filter.term(0); - test_int(t.id(), a); - test_int(t.inout(), flecs::In); - - t = filter.term(1); - test_int(t.id(), b); - test_int(t.inout(), flecs::Out); - - t = filter.term(2); - test_int(t.id(), c); - test_int(t.inout(), flecs::InOut); - - t = filter.term(3); - test_int(t.id(), d); - test_int(t.inout(), flecs::InOutNone); -} - -void FilterBuilder_iter_column_w_const_as_array(void) { - flecs::world world; - - auto f = world.filter(); - - auto e1 = world.entity().set({10, 20}); - auto e2 = world.entity().set({20, 30}); - - int32_t count = 0; - f.iter([&](flecs::iter& it) { - const auto p = it.field(1); - for (auto i : it) { - p[i].x += 1; - p[i].y += 2; - - count ++; - } - }); - - test_int(count, 2); - - const Position *p = e1.get(); - test_int(p->x, 11); - test_int(p->y, 22); - - p = e2.get(); - test_int(p->x, 21); - test_int(p->y, 32); -} - -void FilterBuilder_iter_column_w_const_as_ptr(void) { - flecs::world world; - - auto f = world.filter(); - - auto base = world.prefab().set({10, 20}); - world.entity().is_a(base); - world.entity().is_a(base); - - int32_t count = 0; - f.iter([&](flecs::iter& it) { - const auto p = it.field(1); - for (size_t i = 0; i < it.count(); i ++) { - test_int(p->x, 10); - test_int(p->y, 20); - count ++; - } - }); - - test_int(count, 2); -} - -void FilterBuilder_iter_column_w_const_deref(void) { - flecs::world world; - - auto f = world.filter(); - - auto base = world.prefab().set({10, 20}); - world.entity().is_a(base); - world.entity().is_a(base); - - int32_t count = 0; - f.iter([&](flecs::iter& it) { - const auto p = it.field(1); - Position pv = *p; - for (size_t i = 0; i < it.count(); i ++) { - test_int(pv.x, 10); - test_int(pv.y, 20); - count ++; - } - }); - - test_int(count, 2); -} - -void FilterBuilder_with_id(void) { - flecs::world ecs; - - flecs::filter<> q = - ecs.filter_builder() - .with(ecs.id()) - .with(ecs.id()) - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_with_name(void) { - flecs::world ecs; - - ecs.component(); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .with("Velocity") - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_with_component(void) { - flecs::world ecs; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .with() - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_with_pair_id(void) { - flecs::world ecs; - - flecs::entity Likes = ecs.entity(); - flecs::entity Apples = ecs.entity(); - flecs::entity Pears = ecs.entity(); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .with(Likes, Apples) - .build(); - - auto e1 = ecs.entity().add().add(Likes, Apples); - ecs.entity().add().add(Likes, Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_with_pair_name(void) { - flecs::world ecs; - - flecs::entity Likes = ecs.entity("Likes"); - flecs::entity Apples = ecs.entity("Apples"); - flecs::entity Pears = ecs.entity("Pears"); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .with("Likes", "Apples") - .build(); - - auto e1 = ecs.entity().add().add(Likes, Apples); - ecs.entity().add().add(Likes, Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_with_pair_components(void) { - flecs::world ecs; - - struct Likes { }; - struct Apples { }; - struct Pears { }; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .with() - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_with_pair_component_id(void) { - flecs::world ecs; - - struct Likes { }; - flecs::entity Apples = ecs.entity(); - flecs::entity Pears = ecs.entity(); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .with(Apples) - .build(); - - auto e1 = ecs.entity().add().add(Apples); - ecs.entity().add().add(Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_with_pair_component_name(void) { - flecs::world ecs; - - struct Likes { }; - flecs::entity Apples = ecs.entity("Apples"); - flecs::entity Pears = ecs.entity("Pears"); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .with("Apples") - .build(); - - auto e1 = ecs.entity().add().add(Apples); - ecs.entity().add().add(Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_with_enum(void) { - flecs::world ecs; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .with(Green) - .build(); - - auto e1 = ecs.entity().add().add(Green); - ecs.entity().add().add(Red); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void FilterBuilder_without_id(void) { - flecs::world ecs; - - flecs::filter<> q = - ecs.filter_builder() - .with(ecs.id()) - .without(ecs.id()) - .build(); - - ecs.entity().add().add(); - auto e2 = ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e2); - }); - - test_int(count, 1); -} - -void FilterBuilder_without_name(void) { - flecs::world ecs; - - ecs.component(); - - flecs::filter<> q = - ecs.filter_builder() - .with(ecs.id()) - .without("Velocity") - .build(); - - ecs.entity().add().add(); - auto e2 = ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e2); - }); - - test_int(count, 1); -} - -void FilterBuilder_without_component(void) { - flecs::world ecs; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .without() - .build(); - - ecs.entity().add().add(); - auto e2 = ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e2); - }); - - test_int(count, 1); -} - -void FilterBuilder_without_pair_id(void) { - flecs::world ecs; - - flecs::entity Likes = ecs.entity(); - flecs::entity Apples = ecs.entity(); - flecs::entity Pears = ecs.entity(); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .without(Likes, Apples) - .build(); - - ecs.entity().add().add(Likes, Apples); - auto e2 = ecs.entity().add().add(Likes, Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e2); - }); - - test_int(count, 1); -} - -void FilterBuilder_without_pair_name(void) { - flecs::world ecs; - - flecs::entity Likes = ecs.entity("Likes"); - flecs::entity Apples = ecs.entity("Apples"); - flecs::entity Pears = ecs.entity("Pears"); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .without("Likes", "Apples") - .build(); - - ecs.entity().add().add(Likes, Apples); - auto e2 = ecs.entity().add().add(Likes, Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e2); - }); - - test_int(count, 1); -} - -void FilterBuilder_without_pair_components(void) { - flecs::world ecs; - - struct Likes { }; - struct Apples { }; - struct Pears { }; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .without() - .build(); - - ecs.entity().add().add(); - auto e2 = ecs.entity().add().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e2); - }); - - test_int(count, 1); -} - -void FilterBuilder_without_pair_component_id(void) { - flecs::world ecs; - - struct Likes { }; - flecs::entity Apples = ecs.entity(); - flecs::entity Pears = ecs.entity(); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .without(Apples) - .build(); - - ecs.entity().add().add(Apples); - auto e2 = ecs.entity().add().add(Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e2); - }); - - test_int(count, 1); -} - -void FilterBuilder_without_pair_component_name(void) { - flecs::world ecs; - - struct Likes { }; - flecs::entity Apples = ecs.entity("Apples"); - flecs::entity Pears = ecs.entity("Pears"); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .without("Apples") - .build(); - - ecs.entity().add().add(Apples); - auto e2 = ecs.entity().add().add(Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e2); - }); - - test_int(count, 1); -} - -void FilterBuilder_without_enum(void) { - flecs::world ecs; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .without(Green) - .build(); - - ecs.entity().add().add(Green); - auto e2 = ecs.entity().add().add(Red); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e2); - }); - - test_int(count, 1); -} - -void FilterBuilder_write_id(void) { - flecs::world ecs; - - auto q = - ecs.filter_builder() - .with() - .write(ecs.id()) - .build(); - - test_assert(q.term(1).inout() == flecs::Out); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_write_name(void) { - flecs::world ecs; - - ecs.component(); - - auto q = - ecs.filter_builder() - .with() - .write("Position") - .build(); - - test_assert(q.term(1).inout() == flecs::Out); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_write_component(void) { - flecs::world ecs; - - ecs.component(); - - auto q = - ecs.filter_builder() - .with() - .write() - .build(); - - test_assert(q.term(1).inout() == flecs::Out); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_write_pair_id(void) { - flecs::world ecs; - - flecs::entity Likes = ecs.entity(); - flecs::entity Apples = ecs.entity(); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .write(Likes, Apples) - .build(); - - test_assert(q.term(1).inout() == flecs::Out); - test_assert(q.term(1).get_first() == Likes); - test_assert(q.term(1).get_second() == Apples); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_write_pair_name(void) { - flecs::world ecs; - - flecs::entity Likes = ecs.entity("Likes"); - flecs::entity Apples = ecs.entity("Apples"); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .write("Likes", "Apples") - .build(); - - test_assert(q.term(1).inout() == flecs::Out); - test_assert(q.term(1).get_first() == Likes); - test_assert(q.term(1).get_second() == Apples); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_write_pair_components(void) { - flecs::world ecs; - - struct Likes { }; - struct Apples { }; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .write() - .build(); - - test_assert(q.term(1).inout() == flecs::Out); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_second() == ecs.id()); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_write_pair_component_id(void) { - flecs::world ecs; - - struct Likes { }; - flecs::entity Apples = ecs.entity(); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .write(Apples) - .build(); - - test_assert(q.term(1).inout() == flecs::Out); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_second() == Apples); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_write_pair_component_name(void) { - flecs::world ecs; - - struct Likes { }; - flecs::entity Apples = ecs.entity("Apples"); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .write("Apples") - .build(); - - test_assert(q.term(1).inout() == flecs::Out); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_second() == Apples); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_write_enum(void) { - flecs::world ecs; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .write(Green) - .build(); - - test_assert(q.term(1).inout() == flecs::Out); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_second() == ecs.to_entity(Green)); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_read_id(void) { - flecs::world ecs; - - auto q = - ecs.filter_builder() - .with() - .read(ecs.id()) - .build(); - - test_assert(q.term(1).inout() == flecs::In); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_read_name(void) { - flecs::world ecs; - - ecs.component(); - - auto q = - ecs.filter_builder() - .with() - .read("Position") - .build(); - - test_assert(q.term(1).inout() == flecs::In); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_read_component(void) { - flecs::world ecs; - - ecs.component(); - - auto q = - ecs.filter_builder() - .with() - .read() - .build(); - - test_assert(q.term(1).inout() == flecs::In); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_read_pair_id(void) { - flecs::world ecs; - - flecs::entity Likes = ecs.entity(); - flecs::entity Apples = ecs.entity(); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .read(Likes, Apples) - .build(); - - test_assert(q.term(1).inout() == flecs::In); - test_assert(q.term(1).get_first() == Likes); - test_assert(q.term(1).get_second() == Apples); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_read_pair_name(void) { - flecs::world ecs; - - flecs::entity Likes = ecs.entity("Likes"); - flecs::entity Apples = ecs.entity("Apples"); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .read("Likes", "Apples") - .build(); - - test_assert(q.term(1).inout() == flecs::In); - test_assert(q.term(1).get_first() == Likes); - test_assert(q.term(1).get_second() == Apples); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_read_pair_components(void) { - flecs::world ecs; - - struct Likes { }; - struct Apples { }; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .read() - .build(); - - test_assert(q.term(1).inout() == flecs::In); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_second() == ecs.id()); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_read_pair_component_id(void) { - flecs::world ecs; - - struct Likes { }; - flecs::entity Apples = ecs.entity(); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .read(Apples) - .build(); - - test_assert(q.term(1).inout() == flecs::In); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_second() == Apples); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_read_pair_component_name(void) { - flecs::world ecs; - - struct Likes { }; - flecs::entity Apples = ecs.entity("Apples"); - - flecs::filter<> q = - ecs.filter_builder() - .with() - .read("Apples") - .build(); - - test_assert(q.term(1).inout() == flecs::In); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_second() == Apples); - - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_read_enum(void) { - flecs::world ecs; - - flecs::filter<> q = - ecs.filter_builder() - .with() - .read(Green) - .build(); - - test_assert(q.term(1).inout() == flecs::In); - test_assert(q.term(1).get_first() == ecs.id()); - test_assert(q.term(1).get_second() == ecs.to_entity(Green)); - test_assert(q.term(1).get_src() == 0); -} - -void FilterBuilder_assign_after_init(void) { - flecs::world ecs; - - flecs::filter<> f; - flecs::filter_builder<> fb = ecs.filter_builder(); - fb.with(); - f = fb.build(); - - flecs::entity e1 = ecs.entity().set({10, 20}); - - int32_t count = 0; - f.each([&](flecs::entity e) { - test_assert(e == e1); - count ++; - }); - - test_int(count, 1); -} - -void FilterBuilder_iter_w_stage(void) { - flecs::world ecs; - - ecs.set_stage_count(2); - flecs::world stage = ecs.get_stage(1); - - auto e1 = ecs.entity().add(); - - auto q = ecs.filter(); - - int32_t count = 0; - q.each(stage, [&](flecs::iter& it, size_t i, Position&) { - test_assert(it.world() == stage); - test_assert(it.entity(i) == e1); - count ++; - }); - - test_int(count, 1); -} - -void FilterBuilder_with_t_inout(void) { - flecs::world ecs; - - flecs::filter<> f = ecs.filter_builder() - .with(ecs.id()) - .build(); - - test_assert(f.term(0).inout() == flecs::InOutNone); -} - -void FilterBuilder_with_T_inout(void) { - flecs::world ecs; - - flecs::filter<> f = ecs.filter_builder() - .with() - .build(); - - test_assert(f.term(0).inout() == flecs::InOutNone); -} - -void FilterBuilder_with_R_T_inout(void) { - flecs::world ecs; - - flecs::filter<> f = ecs.filter_builder() - .with() - .build(); - - test_assert(f.term(0).inout() == flecs::InOutNone); -} - -void FilterBuilder_with_R_t_inout(void) { - flecs::world ecs; - - flecs::filter<> f = ecs.filter_builder() - .with(ecs.id()) - .build(); - - test_assert(f.term(0).inout() == flecs::InOutNone); -} - -void FilterBuilder_with_r_t_inout(void) { - flecs::world ecs; - - flecs::filter<> f = ecs.filter_builder() - .with(ecs.id(), ecs.id()) - .build(); - - test_assert(f.term(0).inout() == flecs::InOutNone); -} diff --git a/vendors/flecs/test/cpp_api/src/Module.cpp b/vendors/flecs/test/cpp_api/src/Module.cpp deleted file mode 100644 index 843386cf6..000000000 --- a/vendors/flecs/test/cpp_api/src/Module.cpp +++ /dev/null @@ -1,291 +0,0 @@ -#include - -namespace ns { -struct NestedNameSpaceType { }; - -class NestedModule { -public: - NestedModule(flecs::world& world) { - world.module(); - flecs::component(world, "Velocity"); - } -}; - -class SimpleModule { -public: - SimpleModule(flecs::world& world) { - world.module(); - world.import(); - flecs::component(world, "Position"); - } -}; - -class NestedTypeModule { -public: - struct NestedType { }; - - NestedTypeModule(flecs::world& world) { - world.module(); - world.component(); - world.component(); - } -}; - -class NamedModule { -public: - NamedModule(flecs::world& world) { - world.module("::my_scope::NamedModule"); - flecs::component(world, "Position"); - } -}; - -class ImplicitModule { -public: - ImplicitModule(flecs::world& world) { - world.component(); - } -}; - -class NamedModuleInRoot { -public: - NamedModuleInRoot(flecs::world& world) { - world.module("::NamedModuleInRoot"); - world.component(); - } -}; - -} - -struct Module { - Module(flecs::world& world) { - world.module(); - world.component(); - } -}; - -void Module_import(void) { - flecs::world world; - auto m = world.import(); - test_assert(m.id() != 0); - test_str(m.path().c_str(), "::ns::SimpleModule"); - test_assert(m.has(flecs::Module)); - - auto e = flecs::entity(world) - .add(); - test_assert(e.id() != 0); - test_assert(e.has()); -} - -void Module_lookup_from_scope(void) { - flecs::world world; - world.import(); - - auto ns_entity = world.lookup("ns"); - test_assert(ns_entity.id() != 0); - - auto module_entity = world.lookup("ns::SimpleModule"); - test_assert(module_entity.id() != 0); - - auto position_entity = world.lookup("ns::SimpleModule::Position"); - test_assert(position_entity.id() != 0); - - auto nested_module = ns_entity.lookup("SimpleModule"); - test_assert(module_entity.id() == nested_module.id()); - - auto module_position = module_entity.lookup("Position"); - test_assert(position_entity.id() == module_position.id()); - - auto ns_position = ns_entity.lookup("SimpleModule::Position"); - test_assert(position_entity.id() == ns_position.id()); -} - -void Module_nested_module(void) { - flecs::world world; - world.import(); - - auto velocity = world.lookup("ns::NestedModule::Velocity"); - test_assert(velocity.id() != 0); - - test_str(velocity.path().c_str(), "::ns::NestedModule::Velocity"); -} - -void Module_nested_type_module(void) { - flecs::world world; - world.import(); - - auto ns_entity = world.lookup("ns"); - test_assert(ns_entity.id() != 0); - - auto module_entity = world.lookup("ns::NestedTypeModule"); - test_assert(module_entity.id() != 0); - - auto type_entity = world.lookup("ns::NestedTypeModule::NestedType"); - test_assert(type_entity.id() != 0); - - auto ns_type_entity = world.lookup("ns::NestedTypeModule::NestedNameSpaceType"); - test_assert(ns_type_entity.id() != 0); - - int32_t childof_count = 0; - type_entity.each(flecs::ChildOf, [&](flecs::entity) { - childof_count ++; - }); - - test_int(childof_count, 1); - - childof_count = 0; - ns_type_entity.each(flecs::ChildOf, [&](flecs::entity) { - childof_count ++; - }); - - test_int(childof_count, 1); -} - -void Module_component_redefinition_outside_module(void) { - flecs::world world; - - world.import(); - - auto pos_comp = world.lookup("ns::SimpleModule::Position"); - test_assert(pos_comp.id() != 0); - - auto pos = world.component(); - test_assert(pos.id() != 0); - test_assert(pos.id() == pos_comp.id()); - - int32_t childof_count = 0; - pos_comp.each(flecs::ChildOf, [&](flecs::entity) { - childof_count ++; - }); - - test_int(childof_count, 1); -} - -void Module_module_tag_on_namespace(void) { - flecs::world world; - - auto mid = world.import(); - test_assert(mid.has(flecs::Module)); - - auto nsid = world.lookup("ns"); - test_assert(nsid.has(flecs::Module)); -} - -static int module_ctor_invoked = 0; -static int module_dtor_invoked = 0; - -class Module_w_dtor { -public: - Module_w_dtor(flecs::world& world) { - world.module(); - module_ctor_invoked ++; - - world.system<>().iter([](flecs::iter& it) { }); - } - - ~Module_w_dtor() { - module_dtor_invoked ++; - } -}; - -void Module_dtor_on_fini(void) { - { - flecs::world ecs; - - test_int(module_ctor_invoked, 0); - test_int(module_dtor_invoked, 0); - - ecs.import(); - - test_int(module_ctor_invoked, 1); - test_int(module_dtor_invoked, 0); - } - - test_int(module_dtor_invoked, 1); -} - -void Module_register_w_root_name() { - flecs::world ecs; - - auto m = ecs.import(); - - auto m_lookup = ecs.lookup("::my_scope::NamedModule"); - test_assert(m != 0); - test_assert(m == m_lookup); - - test_assert(ecs.lookup("::ns::NamedModule") == 0); -} - -void Module_implicit_module(void) { - flecs::world ecs; - - auto m = ecs.import(); - auto m_lookup = ecs.lookup("::ns::ImplicitModule"); - test_assert(m != 0); - test_assert(m == m_lookup); - - auto p = ecs.component(); - auto p_lookup = ecs.lookup("::ns::ImplicitModule::Position"); - test_assert(p != 0); - test_assert(p == p_lookup); -} - -void Module_module_in_namespace_w_root_name(void) { - flecs::world ecs; - - auto m = ecs.import(); - auto m_lookup = ecs.lookup("::NamedModuleInRoot"); - test_assert(m != 0); - test_assert(m == m_lookup); - test_str(m.path(), "::NamedModuleInRoot"); - - auto p = ecs.component(); - auto p_lookup = ecs.lookup("::NamedModuleInRoot::Position"); - test_assert(p != 0); - test_assert(p == p_lookup); -} - -void Module_module_as_entity(void) { - flecs::world world; - - auto m = world.import(); - test_assert(m != 0); - - auto e = world.entity(); - test_assert(m == e); -} - -void Module_module_as_component(void) { - flecs::world world; - - auto m = world.import(); - test_assert(m != 0); - - auto e = world.component(); - test_assert(m == e); -} - -void Module_module_with_core_name(void) { - flecs::world world; - - flecs::entity m = world.import(); - test_assert(m != 0); - test_str(m.path().c_str(), "::Module"); - - flecs::entity pos = m.lookup("Position"); - test_assert(pos != 0); - test_str(pos.path().c_str(), "::Module::Position"); - test_assert(pos == world.id()); -} - -void Module_import_addons_two_worlds(void) { - flecs::world a; - auto m1 = a.import(); - auto u1 = a.import(); - - flecs::world b; - auto m2 = b.import(); - auto u2 = b.import(); - - test_assert(m1 == m2); - test_assert(u1 == u2); -} diff --git a/vendors/flecs/test/cpp_api/src/Observer.cpp b/vendors/flecs/test/cpp_api/src/Observer.cpp deleted file mode 100644 index 438dd99e1..000000000 --- a/vendors/flecs/test/cpp_api/src/Observer.cpp +++ /dev/null @@ -1,818 +0,0 @@ -#include - -void Observer_2_terms_on_add(void) { - flecs::world ecs; - - int32_t count = 0; - - ecs.observer() - .event(flecs::OnAdd) - .each([&](Position& p, Velocity& v) { - count ++; - }); - - auto e = ecs.entity(); - test_int(count, 0); - - e.set({10, 20}); - test_int(count, 0); - - e.set({1, 2}); - test_int(count, 1); -} - -void Observer_2_terms_on_remove(void) { - flecs::world ecs; - - int32_t count = 0; - - ecs.observer() - .event(flecs::OnRemove) - .each([&](Position& p, Velocity& v) { - count ++; - test_int(p.x, 10); - test_int(p.y, 20); - test_int(v.x, 1); - test_int(v.y, 2); - }); - - auto e = ecs.entity(); - test_int(count, 0); - - e.set({10, 20}); - test_int(count, 0); - - e.set({1, 2}); - test_int(count, 0); - - e.remove(); - test_int(count, 1); - - e.remove(); - test_int(count, 1); -} - -void Observer_2_terms_on_set(void) { - flecs::world ecs; - - int32_t count = 0; - - ecs.observer() - .event(flecs::OnSet) - .each([&](Position& p, Velocity& v) { - count ++; - test_int(p.x, 10); - test_int(p.y, 20); - test_int(v.x, 1); - test_int(v.y, 2); - }); - - auto e = ecs.entity(); - test_int(count, 0); - - e.set({10, 20}); - test_int(count, 0); - - e.set({1, 2}); - test_int(count, 1); -} - -void Observer_2_terms_un_set(void) { - flecs::world ecs; - - int32_t count = 0; - - ecs.observer() - .event(flecs::UnSet) - .each([&](Position& p, Velocity& v) { - count ++; - test_int(p.x, 10); - test_int(p.y, 20); - test_int(v.x, 1); - test_int(v.y, 2); - }); - - auto e = ecs.entity(); - test_int(count, 0); - - e.set({10, 20}); - test_int(count, 0); - - e.set({1, 2}); - test_int(count, 0); - - e.remove(); - test_int(count, 1); - - e.remove(); - test_int(count, 1); -} - -void Observer_10_terms(void) { - flecs::world ecs; - - int count = 0; - - auto e = ecs.entity(); - - ecs.observer<>() - .event(flecs::OnAdd) - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_assert(it.entity(0) == e); - test_int(it.field_count(), 10); - count ++; - }); - - e.add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add(); - - test_int(count, 1); -} - -void Observer_20_terms(void) { - flecs::world ecs; - - int count = 0; - - auto e = ecs.entity(); - - ecs.observer<>() - .event(flecs::OnAdd) - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_assert(it.entity(0) == e); - test_int(it.field_count(), 20); - count ++; - }); - - e.add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add(); - - test_int(count, 1); -} - -void Observer_2_entities_iter(void) { - flecs::world ecs; - - auto e1 = ecs.entity(); - auto e2 = ecs.entity(); - - int32_t count = 0; - flecs::entity last; - - ecs.observer() - .event(flecs::OnSet) - .iter([&](flecs::iter& it, const Position *p) { - for (auto i : it) { - count ++; - if (it.entity(i) == e1) { - test_int(p[i].x, 10); - test_int(p[i].y, 20); - } else if (it.entity(i) == e2) { - test_int(p[i].x, 30); - test_int(p[i].y, 40); - } else { - test_assert(false); - } - - last = it.entity(i); - } - }); - - e1.set({ 10, 20 }); - test_int(count, 1); - test_assert(last == e1); - - e2.set({ 30, 40 }); - test_int(count, 2); - test_assert(last == e2); -} - -void Observer_2_entities_table_column(void) { - flecs::world ecs; - - auto e1 = ecs.entity(); - auto e2 = ecs.entity(); - - int32_t count = 0; - flecs::entity last; - - ecs.observer() - .event(flecs::OnSet) - .iter([&](flecs::iter& it) { - auto p = it.range().get(); - - for (auto i : it) { - count ++; - if (it.entity(i) == e1) { - test_int(p[i].x, 10); - test_int(p[i].y, 20); - } else if (it.entity(i) == e2) { - test_int(p[i].x, 30); - test_int(p[i].y, 40); - } else { - test_assert(false); - } - - last = it.entity(i); - } - }); - - e1.set({ 10, 20 }); - test_int(count, 1); - test_assert(last == e1); - - e2.set({ 30, 40 }); - test_int(count, 2); - test_assert(last == e2); -} - -void Observer_2_entities_each(void) { - flecs::world ecs; - - auto e1 = ecs.entity(); - auto e2 = ecs.entity(); - - int32_t count = 0; - flecs::entity last; - - ecs.observer() - .event(flecs::OnSet) - .each([&](flecs::entity e, const Position& p) { - count ++; - if (e == e1) { - test_int(p.x, 10); - test_int(p.y, 20); - } else if (e == e2) { - test_int(p.x, 30); - test_int(p.y, 40); - } else { - test_assert(false); - } - - last = e; - }); - - e1.set({ 10, 20 }); - test_int(count, 1); - test_assert(last == e1); - - e2.set({ 30, 40 }); - test_int(count, 2); - test_assert(last == e2); -} - -void Observer_create_w_no_template_args(void) { - flecs::world ecs; - - auto e1 = ecs.entity(); - - int32_t count = 0; - - ecs.observer() - .term() - .event(flecs::OnAdd) - .each([&](flecs::entity e) { - test_assert(e == e1); - count ++; - }); - - e1.set({10, 20}); - test_int(count, 1); -} - -void Observer_yield_existing(void) { - flecs::world world; - - struct TagA { }; - struct TagB { }; - - auto e1 = world.entity().add(); - auto e2 = world.entity().add(); - auto e3 = world.entity().add().add(); - - int32_t count = 0; - - world.observer() - .event(flecs::OnAdd) - .yield_existing() - .each([&](flecs::entity e, TagA) { - if (e == e1) count ++; - if (e == e2) count += 2; - if (e == e3) count += 3; - }); - - test_int(count, 6); -} - -void Observer_yield_existing_2_terms(void) { - flecs::world world; - - struct TagA { }; - struct TagB { }; - - auto e1 = world.entity().add().add(); - auto e2 = world.entity().add().add(); - auto e3 = world.entity().add().add().add(); - world.entity().add(); - world.entity().add(); - - int32_t count = 0; - - world.observer() - .event(flecs::OnAdd) - .yield_existing() - .each([&](flecs::entity e, TagA, TagB) { - if (e == e1) count ++; - if (e == e2) count += 2; - if (e == e3) count += 3; - }); - - test_int(count, 6); -} - -void Observer_default_ctor(void) { - flecs::world world; - - struct TagA { }; - - flecs::observer o; - test_assert(o == 0); - - int32_t count = 0; - o = world.observer() - .event(flecs::OnAdd) - .each([&](flecs::entity e, TagA) { - count ++; - }); - - world.entity().add(); - - test_int(count, 1); -} - -void Observer_entity_ctor(void) { - flecs::world world; - - struct TagA { }; - - flecs::observer o = world.observer() - .event(flecs::OnAdd) - .each([&](flecs::entity e, TagA) { }); - - flecs::entity oe = o; - - flecs::observer eo = world.observer(oe); - test_assert(eo == o); -} - -void Observer_on_add(void) { - flecs::world world; - - int invoked = 0; - - world.observer() - .event(flecs::OnAdd) - .each([&](flecs::entity e, Position& p) { - invoked ++; - }); - - world.entity() - .add(); - - test_int(invoked, 1); -} - -void Observer_on_remove(void) { - flecs::world world; - - int invoked = 0; - - world.observer() - .event(flecs::OnRemove) - .each([&](flecs::entity e, Position& p) { - invoked ++; - }); - - auto e = world.entity() - .add(); - - test_int(invoked, 0); - - e.remove(); - - test_int(invoked, 1); -} - -struct MyTag { }; - -void Observer_on_add_tag_action(void) { - flecs::world world; - - int invoked = 0; - - world.observer() - .event(flecs::OnAdd) - .iter([&](flecs::iter it, MyTag*) { - invoked ++; - }); - - world.entity() - .add(); - - test_int(invoked, 1); -} - -void Observer_on_add_tag_iter(void) { - flecs::world world; - - int invoked = 0; - - world.observer() - .event(flecs::OnAdd) - .iter([&](flecs::iter it, MyTag*) { - invoked ++; - }); - - world.entity() - .add(); - - test_int(invoked, 1); -} - -void Observer_on_add_tag_each(void) { - flecs::world world; - - int invoked = 0; - - world.observer() - .event(flecs::OnAdd) - .each([&](flecs::entity e, MyTag) { - invoked ++; - }); - - world.entity() - .add(); - - test_int(invoked, 1); -} - -void Observer_on_add_expr(void) { - flecs::world world; - - int invoked = 0; - - world.component(); - - world.observer<>().expr("Tag") - .event(flecs::OnAdd) - .each([&](flecs::entity e) { - invoked ++; - }); - - auto e = world.entity().add(); - - test_int(invoked, 1); - - e.remove(); - - test_int(invoked, 1); -} - -void Observer_observer_w_filter_term(void) { - flecs::world world; - - flecs::entity TagA = world.entity(); - flecs::entity TagB = world.entity(); - - int invoked = 0; - - world.observer() - .term(TagA) - .term(TagB).filter() - .event(flecs::OnAdd) - .each([&](flecs::entity e) { - invoked ++; - }); - - flecs::entity e = world.entity(); - test_int(invoked, 0); - - e.add(TagB); - test_int(invoked, 0); - - e.add(TagA); - test_int(invoked, 1); - - e.remove(TagB); - test_int(invoked, 1); - - e.add(TagB); - test_int(invoked, 1); - - e.clear(); - test_int(invoked, 1); - - e.add(TagA); - test_int(invoked, 1); -} - -void Observer_run_callback(void) { - flecs::world ecs; - - int32_t count = 0; - - ecs.observer() - .event(flecs::OnAdd) - .run([](flecs::iter_t *it) { - while (ecs_iter_next(it)) { - it->callback(it); - } - }) - .each([&](Position& p) { - count ++; - }); - - auto e = ecs.entity(); - test_int(count, 0); - - e.set({10, 20}); - test_int(count, 1); -} - -void Observer_get_query(void) { - flecs::world world; - - world.entity().set({0, 0}); - world.entity().set({1, 0}); - world.entity().set({2, 0}); - - int32_t count = 0; - - auto sys = world.observer() - .event(flecs::OnSet) - .each([&](flecs::entity e, const Position& p) { - // Not used - }); - - auto q = sys.query(); - - q.iter([&](flecs::iter &it) { - auto pos = it.field(1); - for (auto i : it) { - test_int(i, pos[i].x); - count ++; - } - }); - - test_int(count, 3); -} - -void Observer_on_set_w_set(void) { - flecs::world world; - - int32_t count = 0; - - world.observer() - .event(flecs::OnSet) - .each([&](flecs::entity e, Position& p) { - count ++; - }); - - flecs::entity e = world.entity(); - test_int(count, 0); - - e.set({10, 20}); - test_int(count, 1); -} - -void Observer_on_set_w_defer_set(void) { - flecs::world world; - - int32_t count = 0; - - world.observer() - .event(flecs::OnSet) - .each([&](flecs::entity e, Position& p) { - count ++; - }); - - flecs::entity e = world.entity(); - test_int(count, 0); - - world.defer_begin(); - e.set({10, 20}); - - test_int(count, 0); - world.defer_end(); - - test_int(count, 1); -} - -#include - -void Observer_on_add_singleton(void) { - flecs::world world; - - int32_t count = 0; - - world.observer() - .term_at(1).singleton() - .event(flecs::OnSet) - .each([&](Position& p) { - test_int(p.x, 10); - test_int(p.y, 20); - count ++; - }); - - world.set({10, 20}); - - test_int(count, 1); -} - -void Observer_on_add_pair_singleton(void) { - flecs::world world; - - int32_t count = 0; - - flecs::entity tgt = world.entity(); - - world.observer() - .term_at(1).second(tgt).singleton() - .event(flecs::OnSet) - .each([&](Position& p) { - test_int(p.x, 10); - test_int(p.y, 20); - count ++; - }); - - world.set(tgt, {10, 20}); - - test_int(count, 1); -} - -void Observer_on_add_pair_wildcard_singleton(void) { - flecs::world world; - - int32_t count = 0; - - flecs::entity tgt_1 = world.entity(); - flecs::entity tgt_2 = world.entity(); - - world.observer() - .term_at(1).second(flecs::Wildcard).singleton() - .event(flecs::OnSet) - .each([&](Position& p) { - test_int(p.x, 10); - test_int(p.y, 20); - count ++; - }); - - world.set(tgt_1, {10, 20}); - test_int(count, 1); - - world.set(tgt_2, {10, 20}); - test_int(count, 2); -} - -void Observer_on_add_with_pair_singleton(void) { - flecs::world world; - - int32_t count = 0; - - flecs::entity tgt = world.entity(); - - world.observer() - .with(tgt).singleton() - .event(flecs::OnSet) - .each([&](flecs::entity) { - count ++; - }); - - world.set(tgt, {10, 20}); - test_int(count, 1); -} - -void Observer_add_in_yield_existing(void) { - flecs::world world; - - flecs::entity e1 = world.entity().set({}); - flecs::entity e2 = world.entity().set({}); - flecs::entity e3 = world.entity().set({}); - - world.observer() - .with() - .event(flecs::OnAdd) - .yield_existing() - .each([](flecs::entity e) { - e.add(); - }); - - test_assert(e1.has()); - test_assert(e1.has()); - - test_assert(e2.has()); - test_assert(e2.has()); - - test_assert(e3.has()); - test_assert(e3.has()); -} - -void Observer_add_in_yield_existing_multi(void) { - flecs::world world; - - flecs::entity e1 = world.entity().set({}).set({}); - flecs::entity e2 = world.entity().set({}).set({}); - flecs::entity e3 = world.entity().set({}).set({}); - - world.observer() - .with() - .with() - .event(flecs::OnAdd) - .yield_existing() - .each([](flecs::entity e) { - e.add(); - }); - - test_assert(e1.has()); - test_assert(e1.has()); - test_assert(e1.has()); - - test_assert(e2.has()); - test_assert(e2.has()); - test_assert(e2.has()); - - test_assert(e3.has()); - test_assert(e3.has()); - test_assert(e3.has()); -} - -void Observer_name_from_root(void) { - flecs::world ecs; - - flecs::entity sys = ecs.observer("::ns::MySystem") - .event(flecs::OnSet) - .each([](Position& p) { }); - - test_str(sys.name(), "MySystem"); - - flecs::entity ns = ecs.entity("::ns"); - test_assert(ns == sys.parent()); -} diff --git a/vendors/flecs/test/cpp_api/src/QueryBuilder.cpp b/vendors/flecs/test/cpp_api/src/QueryBuilder.cpp deleted file mode 100644 index b7b54bb3e..000000000 --- a/vendors/flecs/test/cpp_api/src/QueryBuilder.cpp +++ /dev/null @@ -1,2089 +0,0 @@ -#include - -struct Other { - int32_t value; -}; - -void QueryBuilder_builder_assign_same_type(void) { - flecs::world ecs; - - flecs::query q = - ecs.query_builder().build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p, Velocity& v) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_builder_assign_to_empty(void) { - flecs::world ecs; - - flecs::query<> q = ecs.query_builder().build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_builder_assign_from_empty(void) { - flecs::world ecs; - - flecs::query<> q = ecs.query_builder<>() - .term() - .term() - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_builder_build(void) { - flecs::world ecs; - - flecs::query q = - ecs.query_builder().build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p, Velocity& v) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_builder_build_to_auto(void) { - flecs::world ecs; - - auto q = ecs.query_builder().build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p, Velocity& v) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_builder_build_n_statements(void) { - flecs::world ecs; - - auto qb = ecs.query_builder<>(); - qb.term(); - qb.term(); - auto q = qb.build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_1_type(void) { - flecs::world ecs; - - auto q = ecs.query_builder().build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_add_1_type(void) { - flecs::world ecs; - - auto q = ecs.query_builder<>() - .term() - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_add_2_types(void) { - flecs::world ecs; - - auto q = ecs.query_builder<>() - .term() - .term() - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_add_1_type_w_1_type(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term() - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_add_2_types_w_1_type(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term() - .term() - .build(); - - auto e1 = ecs.entity().add().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_add_pair(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.query_builder<>() - .term(Likes, Bob) - .build(); - - auto e1 = ecs.entity().add(Likes, Bob); - ecs.entity().add(Likes, Alice); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_add_not(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term().oper(flecs::Not) - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_add_or(void) { - flecs::world ecs; - - auto q = ecs.query_builder<>() - .term().oper(flecs::Or) - .term() - .build(); - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1 || e == e2); - }); - - test_int(count, 2); -} - -void QueryBuilder_add_optional(void) { - flecs::world ecs; - - auto q = ecs.query_builder<>() - .term() - .term().oper(flecs::Optional) - .build(); - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add().add(); - ecs.entity().add().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1 || e == e2); - }); - - test_int(count, 2); -} - -void QueryBuilder_ptr_type(void) { - flecs::world ecs; - - auto q = ecs.query_builder().build(); - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add().add(); - ecs.entity().add().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p, Velocity* v) { - count ++; - test_assert(e == e1 || e == e2); - }); - - test_int(count, 2); -} - -void QueryBuilder_const_type(void) { - flecs::world ecs; - - auto q = ecs.query_builder().build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, const Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_string_term(void) { - flecs::world ecs; - - ecs.component(); - - auto q = ecs.query_builder<>() - .expr("Position") - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_singleton_term(void) { - flecs::world ecs; - - ecs.set({10}); - - auto q = ecs.query_builder() - .term().singleton() - .build(); - - auto - e = ecs.entity(); e.set({e}); - e = ecs.entity(); e.set({e}); - e = ecs.entity(); e.set({e}); - - int32_t count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - test_assert(!it.is_self(2)); - test_int(o->value, 10); - - const Other& o_ref = *o; - test_int(o_ref.value, 10); - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 3); -} - -void QueryBuilder_isa_superset_term(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term().src().up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - - int32_t count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - test_assert(!it.is_self(2)); - test_int(o->value, 10); - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 3); -} - -void QueryBuilder_isa_self_superset_term(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term().src().self().up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().set({20}); e.set({e}); - e = ecs.entity().set({20}); e.set({e}); - - int32_t count = 0; - int32_t owned_count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - - if (!it.is_self(2)) { - test_int(o->value, 10); - } else { - for (auto i : it) { - test_int(o[i].value, 20); - owned_count ++; - } - } - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 5); - test_int(owned_count, 2); -} - -void QueryBuilder_childof_superset_term(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term().src().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - - int32_t count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - test_assert(!it.is_self(2)); - test_int(o->value, 10); - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 3); -} - -void QueryBuilder_childof_self_superset_term(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term().src().self().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().set({20}); e.set({e}); - e = ecs.entity().set({20}); e.set({e}); - - int32_t count = 0; - int32_t owned_count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - - if (!it.is_self(2)) { - test_int(o->value, 10); - } else { - for (auto i : it) { - test_int(o[i].value, 20); - owned_count ++; - } - } - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 5); - test_int(owned_count, 2); -} - -void QueryBuilder_isa_superset_term_w_each(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .arg(2).src().up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 3); -} - -void QueryBuilder_isa_self_superset_term_w_each(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .arg(2).src().self().up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().add(flecs::IsA, base); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 5); -} - -void QueryBuilder_childof_superset_term_w_each(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .arg(2).src().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 3); -} - -void QueryBuilder_childof_self_superset_term_w_each(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .arg(2).src().self().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 5); -} - -void QueryBuilder_isa_superset_shortcut(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .arg(2).up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().is_a(base); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 3); -} - -void QueryBuilder_isa_superset_shortcut_w_self(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .arg(2).self().up(flecs::IsA) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().is_a(base); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 5); -} - -void QueryBuilder_childof_superset_shortcut(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .arg(2).up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 3); -} - -void QueryBuilder_childof_superset_shortcut_w_self(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .arg(2).self().up(flecs::ChildOf) - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().child_of(base); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - e = ecs.entity().set({10}); e.set({e}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s, Other& o) { - test_assert(e == s.value); - test_int(o.value, 10); - count ++; - }); - - test_int(count, 5); -} - -void QueryBuilder_relation(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.query_builder() - .term(Likes, Bob) - .build(); - - auto - e = ecs.entity().add(Likes, Bob); e.set({e}); - e = ecs.entity().add(Likes, Bob); e.set({e}); - - e = ecs.entity().add(Likes, Alice); e.set({0}); - e = ecs.entity().add(Likes, Alice); e.set({0}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 2); -} - -void QueryBuilder_relation_w_object_wildcard(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.query_builder() - .term(Likes, flecs::Wildcard) - .build(); - - auto - e = ecs.entity().add(Likes, Bob); e.set({e}); - e = ecs.entity().add(Likes, Bob); e.set({e}); - - e = ecs.entity().add(Likes, Alice); e.set({e}); - e = ecs.entity().add(Likes, Alice); e.set({e}); - - e = ecs.entity(); e.set({0}); - e = ecs.entity(); e.set({0}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 4); -} - -void QueryBuilder_relation_w_predicate_wildcard(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Dislikes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.query_builder() - .term(flecs::Wildcard, Alice) - .build(); - - auto - e = ecs.entity().add(Likes, Alice); e.set({e}); - e = ecs.entity().add(Dislikes, Alice); e.set({e}); - - e = ecs.entity().add(Likes, Bob); e.set({0}); - e = ecs.entity().add(Dislikes, Bob); e.set({0}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 2); -} - -void QueryBuilder_add_pair_w_rel_type(void) { - flecs::world ecs; - - struct Likes { }; - - auto Dislikes = ecs.entity(); - auto Bob = ecs.entity(); - auto Alice = ecs.entity(); - - auto q = ecs.query_builder() - .term(flecs::Wildcard) - .build(); - - auto - e = ecs.entity().add(Alice); e.set({e}); - e = ecs.entity().add(Dislikes, Alice); e.set({0}); - - e = ecs.entity().add(Bob); e.set({e}); - e = ecs.entity().add(Dislikes, Bob); e.set({0}); - - int32_t count = 0; - - q.each([&](flecs::entity e, Self& s) { - test_assert(e == s.value); - count ++; - }); - - test_int(count, 2); -} - -void QueryBuilder_template_term(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term>() - .build(); - - auto e1 = ecs.entity().add().add>(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_explicit_subject_w_id(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term().id(flecs::This) - .build(); - - auto e1 = ecs.entity().add().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_explicit_subject_w_type(void) { - flecs::world ecs; - - ecs.set({10, 20}); - - auto q = ecs.query_builder() - .term().src() - .build(); - - int32_t count = 0; - q.each([&](flecs::entity e, Position& p) { - test_int(p.x, 10); - test_int(p.y, 20); - count ++; - test_assert(e == ecs.singleton()); - }); - - test_int(count, 1); -} - -void QueryBuilder_explicit_object_w_id(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Alice = ecs.entity(); - auto Bob = ecs.entity(); - - auto q = ecs.query_builder<>() - .term(Likes).second(Alice) - .build(); - - auto e1 = ecs.entity().add(Likes, Alice); - ecs.entity().add(Likes, Bob); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_explicit_object_w_type(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - struct Alice { }; - auto Bob = ecs.entity(); - - auto q = ecs.query_builder<>() - .term(Likes).second() - .build(); - - auto e1 = ecs.entity().add(Likes, ecs.id()); - ecs.entity().add(Likes, Bob); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_explicit_term(void) { - flecs::world ecs; - - auto q = ecs.query_builder<>() - .term(ecs.term(ecs.id())) - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_explicit_term_w_type(void) { - flecs::world ecs; - - auto q = ecs.query_builder<>() - .term(ecs.term()) - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_explicit_term_w_pair_type(void) { - flecs::world ecs; - - struct Likes { }; - struct Alice { }; - struct Bob { }; - - auto q = ecs.query_builder<>() - .term(ecs.term()) - .build(); - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_explicit_term_w_id(void) { - flecs::world ecs; - - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - auto q = ecs.query_builder<>() - .term(ecs.term(Apples)) - .build(); - - auto e1 = ecs.entity().add(Apples); - ecs.entity().add(Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_explicit_term_w_pair_id(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - auto q = ecs.query_builder<>() - .term(ecs.term(Likes, Apples)) - .build(); - - auto e1 = ecs.entity().add(Likes, Apples); - ecs.entity().add(Likes, Pears); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_1_term_to_empty(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Apples = ecs.entity(); - - auto qb = ecs.query_builder<>() - .term(); - - qb.term(Likes, Apples); - - auto q = qb.build(); - - test_int(q.field_count(), 2); - test_int(q.term(0).id(), ecs.id()); - test_int(q.term(1).id(), ecs.pair(Likes, Apples)); -} - -void QueryBuilder_2_subsequent_args(void) { - flecs::world ecs; - - struct Rel { int foo; }; - - int32_t count = 0; - - auto s = ecs.system() - .arg(1).second(flecs::Wildcard) - .arg(2).singleton() - .iter([&](flecs::iter it){ - count += it.count(); - }); - - ecs.entity().add(); - ecs.set({}); - - s.run(); - - test_int(count, 1); -} - -void QueryBuilder_optional_tag_is_set(void) { - flecs::world ecs; - - struct TagA { }; - struct TagB { }; - - auto q = ecs.query_builder() - .term() - .term().oper(flecs::Optional) - .build(); - - auto e_1 = ecs.entity().add().add(); - auto e_2 = ecs.entity().add(); - - int count = 0; - - q.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - - count += it.count(); - - if (it.entity(0) == e_1) { - test_bool(it.is_set(1), true); - test_bool(it.is_set(2), true); - } else { - test_assert(it.entity(0) == e_2); - test_bool(it.is_set(1), true); - test_bool(it.is_set(2), false); - } - }); - - test_int(count, 2); -} - -void QueryBuilder_10_terms(void) { - flecs::world ecs; - - auto f = ecs.query_builder<>() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .build(); - - test_int(f.field_count(), 10); - - auto e = ecs.entity() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add(); - - int count = 0; - f.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_assert(it.entity(0) == e); - test_int(it.field_count(), 10); - count ++; - }); - - test_int(count, 1); -} - -void QueryBuilder_20_terms(void) { - flecs::world ecs; - - auto f = ecs.query_builder<>() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .term() - .build(); - - test_int(f.field_count(), 20); - - auto e = ecs.entity() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add() - .add(); - - int count = 0; - f.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_assert(it.entity(0) == e); - test_int(it.field_count(), 20); - count ++; - }); - - test_int(count, 1); -} - -uint64_t group_by_first_id( - flecs::world_t *m_world, - flecs::table_t *m_table, - flecs::entity_t id, - void *ctx) -{ - const flecs::type_t *type = ecs_table_get_type(m_table); - return type->array[0]; -} - -uint64_t group_by_first_id_negated( - flecs::world_t *m_world, - flecs::table_t *m_table, - flecs::entity_t id, - void *ctx) -{ - return ~group_by_first_id(m_world, m_table, id, ctx); -} - -void QueryBuilder_group_by_raw(void) { - flecs::world ecs; - - struct TagA { }; - struct TagB { }; - struct TagC { }; - struct TagX { }; - - ecs.component(); - ecs.component(); - ecs.component(); - ecs.component(); - - auto q = ecs.query_builder() - .term() - .group_by(flecs::type_id(), group_by_first_id) - .build(); - - auto q_reverse = ecs.query_builder() - .term() - .group_by(flecs::type_id(), group_by_first_id_negated) - .build(); - - auto e3 = ecs.entity().add().add(); - auto e2 = ecs.entity().add().add(); - auto e1 = ecs.entity().add().add(); - - int count = 0; - - q.iter([&](flecs::iter& it){ - test_int(it.count(), 1); - if(count == 0){ - test_bool(it.entity(0) == e1, true); - }else if(count == 1){ - test_bool(it.entity(0) == e2, true); - }else if(count == 2){ - test_bool(it.entity(0) == e3, true); - }else{ - test_assert(false); - } - count++; - }); - test_int(count, 3); - - count = 0; - q_reverse.iter([&](flecs::iter& it){ - test_int(it.count(), 1); - if(count == 0){ - test_bool(it.entity(0) == e3, true); - }else if(count == 1){ - test_bool(it.entity(0) == e2, true); - }else if(count == 2){ - test_bool(it.entity(0) == e1, true); - }else{ - test_assert(false); - } - count++; - }); - test_int(count, 3); -} - -void QueryBuilder_group_by_template(void) { - flecs::world ecs; - - struct TagA { }; - struct TagB { }; - struct TagC { }; - struct TagX { }; - - ecs.component(); - ecs.component(); - ecs.component(); - ecs.component(); - - auto q = ecs.query_builder() - .term() - .group_by(group_by_first_id) - .build(); - - auto q_reverse = ecs.query_builder() - .term() - .group_by( group_by_first_id_negated) - .build(); - - auto e3 = ecs.entity().add().add(); - auto e2 = ecs.entity().add().add(); - auto e1 = ecs.entity().add().add(); - - int count = 0; - - q.iter([&](flecs::iter& it){ - test_int(it.count(), 1); - if(count == 0){ - test_bool(it.entity(0) == e1, true); - }else if(count == 1){ - test_bool(it.entity(0) == e2, true); - }else if(count == 2){ - test_bool(it.entity(0) == e3, true); - }else{ - test_assert(false); - } - count++; - }); - test_int(count, 3); - - count = 0; - q_reverse.iter([&](flecs::iter& it){ - test_int(it.count(), 1); - if(count == 0){ - test_bool(it.entity(0) == e3, true); - }else if(count == 1){ - test_bool(it.entity(0) == e2, true); - }else if(count == 2){ - test_bool(it.entity(0) == e1, true); - }else{ - test_assert(false); - } - count++; - }); - test_int(count, 3); -} - -static -uint64_t group_by_rel(flecs::world_t *world, flecs::table_t *table, flecs::entity_t id, void *ctx) { - ecs_id_t match; - if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &match) != -1) { - return ECS_PAIR_SECOND(match); - } - return 0; -} - -void QueryBuilder_group_by_iter_one(void) { - flecs::world ecs; - - auto Rel = ecs.entity(); - auto TgtA = ecs.entity(); - auto TgtB = ecs.entity(); - auto TgtC = ecs.entity(); - auto Tag = ecs.entity(); - - ecs.entity().add(Rel, TgtA); - auto e2 = ecs.entity().add(Rel, TgtB); - ecs.entity().add(Rel, TgtC); - - ecs.entity().add(Rel, TgtA).add(Tag); - auto e5 = ecs.entity().add(Rel, TgtB).add(Tag); - ecs.entity().add(Rel, TgtC).add(Tag); - - auto q = ecs.query_builder() - .term(Rel, flecs::Wildcard) - .group_by(Rel, group_by_rel) - .build(); - - bool e2_found = false; - bool e5_found = false; - int32_t count = 0; - - q.iter().set_group(TgtB).each([&](flecs::iter& it, size_t i) { - flecs::entity e = it.entity(i); - test_assert(it.group_id() == TgtB); - - if (e == e2) e2_found = true; - if (e == e5) e5_found = true; - count ++; - }); - - test_int(2, count); - test_bool(true, e2_found); - test_bool(true, e5_found); -} - -void QueryBuilder_group_by_iter_one_template(void) { - flecs::world ecs; - - struct Rel { }; - struct TgtA { }; - struct TgtB { }; - struct TgtC { }; - struct Tag { }; - - ecs.entity().add(); - auto e2 = ecs.entity().add(); - ecs.entity().add(); - - ecs.entity().add().add(); - auto e5 = ecs.entity().add().add(); - ecs.entity().add().add(); - - auto q = ecs.query_builder() - .term(flecs::Wildcard) - .group_by(group_by_rel) - .build(); - - bool e2_found = false; - bool e5_found = false; - int32_t count = 0; - - q.iter().set_group().each([&](flecs::iter& it, size_t i) { - flecs::entity e = it.entity(i); - test_assert(it.group_id() == ecs.id()); - - if (e == e2) e2_found = true; - if (e == e5) e5_found = true; - count ++; - }); - - test_int(2, count); - test_bool(true, e2_found); - test_bool(true, e5_found); -} - -void QueryBuilder_group_by_iter_one_all_groups(void) { - flecs::world ecs; - - auto Rel = ecs.entity(); - auto TgtA = ecs.entity(); - auto TgtB = ecs.entity(); - auto TgtC = ecs.entity(); - auto Tag = ecs.entity(); - - auto e1 = ecs.entity().add(Rel, TgtA); - auto e2 = ecs.entity().add(Rel, TgtB); - auto e3 = ecs.entity().add(Rel, TgtC); - - auto e4 = ecs.entity().add(Rel, TgtA).add(Tag); - auto e5 = ecs.entity().add(Rel, TgtB).add(Tag); - auto e6 = ecs.entity().add(Rel, TgtC).add(Tag); - - auto q = ecs.query_builder() - .term(Rel, flecs::Wildcard) - .group_by(Rel, group_by_rel) - .build(); - - int e1_found = 0; - int e2_found = 0; - int e3_found = 0; - int e4_found = 0; - int e5_found = 0; - int e6_found = 0; - int32_t count = 0; - uint64_t group_id = 0; - - const auto func = [&](flecs::iter& it, size_t i) { - flecs::entity e = it.entity(i); - test_assert(it.group_id() == group_id); - if (e == e1) e1_found ++; - if (e == e2) e2_found ++; - if (e == e3) e3_found ++; - if (e == e4) e4_found ++; - if (e == e5) e5_found ++; - if (e == e6) e6_found ++; - count ++; - }; - - group_id = TgtB; - q.iter().set_group(TgtB).each(func); - test_int(2, count); - test_int(1, e2_found); - test_int(1, e5_found); - - group_id = TgtA; - q.iter().set_group(TgtA).each(func); - test_int(4, count); - test_int(1, e1_found); - test_int(1, e4_found); - - group_id = TgtC; - q.iter().set_group(TgtC).each(func); - test_int(6, count); - test_int(1, e3_found); - test_int(1, e6_found); - - test_int(1, e1_found); - test_int(1, e2_found); - test_int(1, e3_found); - test_int(1, e4_found); - test_int(1, e5_found); - test_int(1, e6_found); -} - -void QueryBuilder_group_by_default_func_w_id(void) { - flecs::world ecs; - - auto Rel = ecs.entity(); - auto TgtA = ecs.entity(); - auto TgtB = ecs.entity(); - auto TgtC = ecs.entity(); - - auto e1 = ecs.entity().add(Rel, TgtC); - auto e2 = ecs.entity().add(Rel, TgtB); - auto e3 = ecs.entity().add(Rel, TgtA); - - auto q = ecs.query_builder() - .term(Rel, flecs::Wildcard) - .group_by(Rel) - .build(); - - bool e1_found = false; - bool e2_found = false; - bool e3_found = false; - int32_t count = 0; - - q.each([&](flecs::iter& it, size_t i) { - flecs::entity e = it.entity(i); - if (e == e1) { - test_assert(it.group_id() == TgtC); - test_assert(!e1_found); - test_assert(e2_found); - test_assert(e3_found); - e1_found = true; - } - if (e == e2) { - test_assert(it.group_id() == TgtB); - test_assert(!e1_found); - test_assert(!e2_found); - test_assert(e3_found); - e2_found = true; - } - if (e == e3) { - test_assert(it.group_id() == TgtA); - test_assert(!e1_found); - test_assert(!e2_found); - test_assert(!e3_found); - e3_found = true; - } - count ++; - }); - - test_int(3, count); - test_bool(true, e1_found); - test_bool(true, e2_found); - test_bool(true, e3_found); -} - -void QueryBuilder_group_by_default_func_w_type(void) { - flecs::world ecs; - - struct Rel { }; - auto TgtA = ecs.entity(); - auto TgtB = ecs.entity(); - auto TgtC = ecs.entity(); - - auto e1 = ecs.entity().add(TgtC); - auto e2 = ecs.entity().add(TgtB); - auto e3 = ecs.entity().add(TgtA); - - auto q = ecs.query_builder() - .term(flecs::Wildcard) - .group_by() - .build(); - - bool e1_found = false; - bool e2_found = false; - bool e3_found = false; - int32_t count = 0; - - q.each([&](flecs::iter& it, size_t i) { - flecs::entity e = it.entity(i); - if (e == e1) { - test_assert(it.group_id() == TgtC); - test_assert(!e1_found); - test_assert(e2_found); - test_assert(e3_found); - e1_found = true; - } - if (e == e2) { - test_assert(it.group_id() == TgtB); - test_assert(!e1_found); - test_assert(!e2_found); - test_assert(e3_found); - e2_found = true; - } - if (e == e3) { - test_assert(it.group_id() == TgtA); - test_assert(!e1_found); - test_assert(!e2_found); - test_assert(!e3_found); - e3_found = true; - } - count ++; - }); - - test_int(3, count); - test_bool(true, e1_found); - test_bool(true, e2_found); - test_bool(true, e3_found); -} - -static int group_by_ctx = 0; - -void QueryBuilder_group_by_callbacks(void) { - flecs::world ecs; - - struct Rel { }; - auto TgtA = ecs.entity(); - auto TgtB = ecs.entity(); - auto TgtC = ecs.entity(); - - auto e1 = ecs.entity().add(TgtC); - auto e2 = ecs.entity().add(TgtB); - auto e3 = ecs.entity().add(TgtA); - - auto q = ecs.query_builder() - .term(flecs::Wildcard) - .group_by() - .group_by_ctx(&group_by_ctx) - .on_group_create( - [](flecs::world_t *world, uint64_t id, void *group_by_arg) { - test_assert(world != nullptr); - test_assert(id != 0); - test_assert(group_by_arg != nullptr); - test_assert(group_by_arg == &group_by_ctx); - uint64_t *ctx = ecs_os_malloc_t(uint64_t); - *ctx = id; - return (void*)ctx; - }) - .on_group_delete( - [](flecs::world_t *world, uint64_t id, void *ctx, void *group_by_arg) { - test_assert(world != nullptr); - test_assert(id != 0); - test_assert(group_by_arg != nullptr); - test_assert(group_by_arg == &group_by_ctx); - test_assert(ctx != NULL); - test_uint(*(uint64_t*)ctx, id); - ecs_os_free(ctx); - }) - .build(); - - bool e1_found = false; - bool e2_found = false; - bool e3_found = false; - int32_t count = 0; - - q.each([&](flecs::iter& it, size_t i) { - flecs::entity e = it.entity(i); - if (e == e1) { - test_assert(it.group_id() == TgtC); - test_assert(!e1_found); - test_assert(e2_found); - test_assert(e3_found); - e1_found = true; - uint64_t *ctx = (uint64_t*)q.group_ctx(it.group_id()); - test_uint(*ctx, it.group_id()); - } - if (e == e2) { - test_assert(it.group_id() == TgtB); - test_assert(!e1_found); - test_assert(!e2_found); - test_assert(e3_found); - e2_found = true; - uint64_t *ctx = (uint64_t*)q.group_ctx(it.group_id()); - test_uint(*ctx, it.group_id()); - } - if (e == e3) { - test_assert(it.group_id() == TgtA); - test_assert(!e1_found); - test_assert(!e2_found); - test_assert(!e3_found); - e3_found = true; - uint64_t *ctx = (uint64_t*)q.group_ctx(it.group_id()); - test_uint(*ctx, it.group_id()); - } - count ++; - }); - - test_int(3, count); - test_bool(true, e1_found); - test_bool(true, e2_found); - test_bool(true, e3_found); -} - -void QueryBuilder_create_w_no_template_args(void) { - flecs::world ecs; - - auto q = ecs.query_builder().term().build(); - - auto e1 = ecs.entity().add(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_any_wildcard(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Apple = ecs.entity(); - auto Mango = ecs.entity(); - - auto e1 = ecs.entity() - .add(Likes, Apple) - .add(Likes, Mango); - - auto q = ecs.query_builder() - .term(Likes, flecs::Any) - .build(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); -} - -void QueryBuilder_cascade(void) { - flecs::world ecs; - - auto Tag = ecs.entity(); - auto Foo = ecs.entity(); - auto Bar = ecs.entity(); - - auto e0 = ecs.entity().add(Tag); - auto e1 = ecs.entity().is_a(e0); - auto e2 = ecs.entity().is_a(e1); - auto e3 = ecs.entity().is_a(e2); - - auto q = ecs.query_builder() - .term(Tag).cascade() - .build(); - - e1.add(Bar); - e2.add(Foo); - - bool e1_found = false; - bool e2_found = false; - bool e3_found = false; - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - - if (e == e1) { - test_bool(e1_found, false); - test_bool(e2_found, false); - test_bool(e3_found, false); - e1_found = true; - } - if (e == e2) { - test_bool(e1_found, true); - test_bool(e2_found, false); - test_bool(e3_found, false); - e2_found = true; - } - if (e == e3) { - test_bool(e1_found, true); - test_bool(e2_found, true); - test_bool(e3_found, false); - e3_found = true; - } - }); - - test_bool(e1_found, true); - test_bool(e2_found, true); - test_bool(e3_found, true); - test_int(count, 3); -} - -void QueryBuilder_cascade_desc(void) { - flecs::world ecs; - - auto Tag = ecs.entity(); - auto Foo = ecs.entity(); - auto Bar = ecs.entity(); - - auto e0 = ecs.entity().add(Tag); - auto e1 = ecs.entity().is_a(e0); - auto e2 = ecs.entity().is_a(e1); - auto e3 = ecs.entity().is_a(e2); - - auto q = ecs.query_builder() - .term(Tag).cascade().desc() - .build(); - - e1.add(Bar); - e2.add(Foo); - - bool e1_found = false; - bool e2_found = false; - bool e3_found = false; - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - - if (e == e1) { - test_bool(e1_found, false); - test_bool(e2_found, true); - test_bool(e3_found, true); - e1_found = true; - } - if (e == e2) { - test_bool(e1_found, false); - test_bool(e2_found, false); - test_bool(e3_found, true); - e2_found = true; - } - if (e == e3) { - test_bool(e1_found, false); - test_bool(e2_found, false); - test_bool(e3_found, false); - e3_found = true; - } - }); - - test_bool(e1_found, true); - test_bool(e2_found, true); - test_bool(e3_found, true); - test_int(count, 3); -} - -void QueryBuilder_cascade_w_relationship(void) { - flecs::world ecs; - - auto Tag = ecs.entity(); - auto Foo = ecs.entity(); - auto Bar = ecs.entity(); - - auto e0 = ecs.entity().add(Tag); - auto e1 = ecs.entity().child_of(e0); - auto e2 = ecs.entity().child_of(e1); - auto e3 = ecs.entity().child_of(e2); - - auto q = ecs.query_builder() - .term(Tag).cascade(flecs::ChildOf) - .build(); - - e1.add(Bar); - e2.add(Foo); - - bool e1_found = false; - bool e2_found = false; - bool e3_found = false; - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - - if (e == e1) { - test_bool(e1_found, false); - test_bool(e2_found, false); - test_bool(e3_found, false); - e1_found = true; - } - if (e == e2) { - test_bool(e1_found, true); - test_bool(e2_found, false); - test_bool(e3_found, false); - e2_found = true; - } - if (e == e3) { - test_bool(e1_found, true); - test_bool(e2_found, true); - test_bool(e3_found, false); - e3_found = true; - } - }); - - test_bool(e1_found, true); - test_bool(e2_found, true); - test_bool(e3_found, true); - test_int(count, 3); -} - -void QueryBuilder_up_w_type(void) { - flecs::world ecs; - - struct Rel { }; - - ecs.component().add(flecs::Traversable); - - auto q = ecs.query_builder() - .term().src().up() - .build(); - - auto base = ecs.entity().set({10}); - - auto - e = ecs.entity().add(base); e.set({e}); - e = ecs.entity().add(base); e.set({e}); - e = ecs.entity().add(base); e.set({e}); - - int32_t count = 0; - - q.iter([&](flecs::iter& it, Self *s) { - auto o = it.field(2); - test_assert(!it.is_self(2)); - test_int(o->value, 10); - - for (auto i : it) { - test_assert(it.entity(i) == s[i].value); - count ++; - } - }); - - test_int(count, 3); -} - -void QueryBuilder_cascade_w_type(void) { - flecs::world ecs; - - struct Rel { }; - - ecs.component().add(flecs::Traversable); - - auto Tag = ecs.entity(); - auto Foo = ecs.entity(); - auto Bar = ecs.entity(); - - auto e0 = ecs.entity().add(Tag); - auto e1 = ecs.entity().add(e0); - auto e2 = ecs.entity().add(e1); - auto e3 = ecs.entity().add(e2); - - auto q = ecs.query_builder() - .term(Tag).cascade() - .build(); - - e1.add(Bar); - e2.add(Foo); - - bool e1_found = false; - bool e2_found = false; - bool e3_found = false; - - int32_t count = 0; - q.each([&](flecs::entity e) { - count ++; - - if (e == e1) { - test_bool(e1_found, false); - test_bool(e2_found, false); - test_bool(e3_found, false); - e1_found = true; - } - if (e == e2) { - test_bool(e1_found, true); - test_bool(e2_found, false); - test_bool(e3_found, false); - e2_found = true; - } - if (e == e3) { - test_bool(e1_found, true); - test_bool(e2_found, true); - test_bool(e3_found, false); - e3_found = true; - } - }); - - test_bool(e1_found, true); - test_bool(e2_found, true); - test_bool(e3_found, true); - test_int(count, 3); -} - -void QueryBuilder_named_query(void) { - flecs::world ecs; - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add(); - - auto q = ecs.query_builder("my_query") - .term() - .build(); - - int32_t count = 0; - q.each([&](flecs::entity e) { - test_assert(e == e1 || e == e2); - count ++; - }); - test_int(count, 2); - - flecs::entity qe = q.entity(); - test_assert(qe != 0); - test_str(qe.name(), "my_query"); -} - -void QueryBuilder_term_w_write(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term() - .term().write() - .build(); - - auto f = q.filter(); - test_assert(f.term(0).inout() == flecs::InOutDefault); - test_assert(f.term(0).get_src() == flecs::This); - test_assert(f.term(1).inout() == flecs::Out); - test_assert(f.term(1).get_src() == 0); -} - -void QueryBuilder_term_w_read(void) { - flecs::world ecs; - - auto q = ecs.query_builder() - .term() - .term().read() - .build(); - - auto f = q.filter(); - test_assert(f.term(0).inout() == flecs::InOutDefault); - test_assert(f.term(0).get_src() == flecs::This); - test_assert(f.term(1).inout() == flecs::In); - test_assert(f.term(1).get_src() == 0); -} - -void QueryBuilder_iter_w_stage(void) { - flecs::world ecs; - - ecs.set_stage_count(2); - flecs::world stage = ecs.get_stage(1); - - auto e1 = ecs.entity().add(); - - auto q = ecs.query(); - - int32_t count = 0; - q.each(stage, [&](flecs::iter& it, size_t i, Position&) { - test_assert(it.world() == stage); - test_assert(it.entity(i) == e1); - count ++; - }); - - test_int(count, 1); -} diff --git a/vendors/flecs/test/cpp_api/src/RuleBuilder.cpp b/vendors/flecs/test/cpp_api/src/RuleBuilder.cpp deleted file mode 100644 index 4b7da4302..000000000 --- a/vendors/flecs/test/cpp_api/src/RuleBuilder.cpp +++ /dev/null @@ -1,869 +0,0 @@ -#include - -void RuleBuilder_1_type(void) { - flecs::world ecs; - - auto e1 = ecs.entity() - .set({10, 20}); - - ecs.entity().set({10, 20}); - - auto r = ecs.rule(); - - int count = 0; - r.each([&](flecs::entity e, Position& p) { - count ++; - test_assert(e == e1); - test_int(p.x, 10); - test_int(p.y, 20); - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_2_types(void) { - flecs::world ecs; - - auto e1 = ecs.entity() - .set({10, 20}) - .set({1, 2}); - - ecs.entity().set({10, 20}); - - auto r = ecs.rule(); - - int count = 0; - r.each([&](flecs::entity e, Position& p, const Velocity& v) { - count ++; - test_assert(e == e1); - test_int(p.x, 10); - test_int(p.y, 20); - - test_int(v.x, 1); - test_int(v.y, 2); - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_id_term(void) { - flecs::world ecs; - - auto Tag = ecs.entity(); - - auto e1 = ecs.entity() - .add(Tag); - - ecs.entity().set({10, 20}); - - auto r = ecs.rule_builder() - .term(Tag) - .build(); - - int count = 0; - r.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_type_term(void) { - flecs::world ecs; - - auto e1 = ecs.entity() - .set({10, 20}); - - ecs.entity().set({10, 20}); - - auto r = ecs.rule_builder() - .term() - .build(); - - int count = 0; - r.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_id_pair_term(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - auto e1 = ecs.entity() - .add(Likes, Apples); - - ecs.entity() - .add(Likes, Pears); - - auto r = ecs.rule_builder() - .term(Likes, Apples) - .build(); - - int count = 0; - r.each([&](flecs::entity e) { - count ++; - test_assert(e == e1); - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_id_pair_wildcard_term(void) { - flecs::world ecs; - - auto Likes = ecs.entity(); - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - auto e1 = ecs.entity() - .add(Likes, Apples); - - auto e2 = ecs.entity() - .add(Likes, Pears); - - auto r = ecs.rule_builder() - .term(Likes, flecs::Wildcard) - .build(); - - int count = 0; - r.each([&](flecs::iter& it, size_t index) { - if (it.entity(index) == e1) { - test_assert(it.id(1) == ecs.pair(Likes, Apples)); - count ++; - } - if (it.entity(index) == e2) { - test_assert(it.id(1) == ecs.pair(Likes, Pears)); - count ++; - } - }); - test_int(count, 2); - - r.destruct(); -} - -void RuleBuilder_type_pair_term(void) { - flecs::world ecs; - - struct Likes { }; - struct Apples { }; - struct Pears { }; - - auto e1 = ecs.entity() - .add(); - - auto e2 = ecs.entity() - .add(); - - auto r = ecs.rule_builder() - .term(flecs::Wildcard) - .build(); - - int count = 0; - r.each([&](flecs::iter& it, size_t index) { - if (it.entity(index) == e1) { - test_assert((it.id(1) == ecs.pair())); - count ++; - } - if (it.entity(index) == e2) { - test_assert((it.id(1) == ecs.pair())); - count ++; - } - }); - test_int(count, 2); - - r.destruct(); -} - -void RuleBuilder_pair_term_w_var(void) { - flecs::world ecs; - - struct Likes { }; - struct Apples { }; - struct Pears { }; - - auto e1 = ecs.entity() - .add(); - - auto e2 = ecs.entity() - .add(); - - auto r = ecs.rule_builder() - .term().second().var("Food") - .build(); - - int food_var = r.find_var("Food"); - - int count = 0; - r.each([&](flecs::iter& it, size_t index) { - if (it.entity(index) == e1) { - test_assert((it.id(1) == ecs.pair())); - test_assert(it.get_var("Food") == ecs.id()); - test_assert(it.get_var(food_var) == ecs.id()); - count ++; - } - if (it.entity(index) == e2) { - test_assert((it.id(1) == ecs.pair())); - test_assert(it.get_var("Food") == ecs.id()); - test_assert(it.get_var(food_var) == ecs.id()); - count ++; - } - }); - test_int(count, 2); - - r.destruct(); -} - -void RuleBuilder_2_pair_terms_w_var(void) { - flecs::world ecs; - - struct Likes { }; - struct Eats { }; - struct Apples { }; - struct Pears { }; - - auto Bob = ecs.entity() - .add(); - - auto Alice = ecs.entity() - .add() - .add(Bob); - - Bob.add(Alice); - - auto r = ecs.rule_builder() - .term().second().var("Food") - .term().second().var("Person") - .build(); - - int food_var = r.find_var("Food"); - int person_var = r.find_var("Person"); - - int count = 0; - r.each([&](flecs::iter& it, size_t index) { - if (it.entity(index) == Bob) { - test_assert((it.id(1) == ecs.pair())); - test_assert(it.get_var("Food") == ecs.id()); - test_assert(it.get_var(food_var) == ecs.id()); - - test_assert((it.id(2) == ecs.pair(Alice))); - test_assert(it.get_var("Person") == Alice); - test_assert(it.get_var(person_var) == Alice); - count ++; - } - if (it.entity(index) == Alice) { - test_assert((it.id(1) == ecs.pair())); - test_assert(it.get_var("Food") == ecs.id()); - test_assert(it.get_var(food_var) == ecs.id()); - - test_assert((it.id(2) == ecs.pair(Bob))); - test_assert(it.get_var("Person") == Bob); - test_assert(it.get_var(person_var) == Bob); - count ++; - } - }); - test_int(count, 2); - - r.destruct(); -} - -void RuleBuilder_set_var(void) { - flecs::world ecs; - - struct Likes { }; - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - ecs.entity() - .add(Apples); - - auto e2 = ecs.entity() - .add(Pears); - - auto r = ecs.rule_builder() - .term().second().var("Food") - .build(); - - int food_var = r.find_var("Food"); - - int count = 0; - r.iter() - .set_var(food_var, Pears) - .each([&](flecs::iter& it, size_t index) { - test_assert(it.entity(index) == e2); - test_assert((it.id(1) == ecs.pair(Pears))); - test_assert(it.get_var("Food") == Pears); - test_assert(it.get_var(food_var) == Pears); - count ++; - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_set_2_vars(void) { - flecs::world ecs; - - struct Likes { }; - struct Eats { }; - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - auto Bob = ecs.entity() - .add(Apples); - - auto Alice = ecs.entity() - .add(Pears) - .add(Bob); - - Bob.add(Alice); - - auto r = ecs.rule_builder() - .term().second().var("Food") - .term().second().var("Person") - .build(); - - int food_var = r.find_var("Food"); - int person_var = r.find_var("Person"); - - int count = 0; - r.iter() - .set_var(food_var, Pears) - .set_var(person_var, Bob) - .each([&](flecs::iter& it, size_t index) { - test_assert(it.entity(index) == Alice); - test_assert((it.id(1) == ecs.pair(Pears))); - test_assert((it.id(2) == ecs.pair(Bob))); - test_assert(it.get_var("Food") == Pears); - test_assert(it.get_var(food_var) == Pears); - test_assert(it.get_var("Person") == Bob); - test_assert(it.get_var(person_var) == Bob); - count ++; - }); - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_set_var_by_name(void) { - flecs::world ecs; - - struct Likes { }; - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - ecs.entity() - .add(Apples); - - auto e2 = ecs.entity() - .add(Pears); - - auto r = ecs.rule_builder() - .term().second().var("Food") - .build(); - - int count = 0; - r.iter() - .set_var("Food", Pears) - .each([&](flecs::iter& it, size_t index) { - test_assert(it.entity(index) == e2); - test_assert((it.id(1) == ecs.pair(Pears))); - count ++; - }); - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_set_2_vars_by_name(void) { - flecs::world ecs; - - struct Likes { }; - struct Eats { }; - auto Apples = ecs.entity(); - auto Pears = ecs.entity(); - - auto Bob = ecs.entity() - .add(Apples); - - auto Alice = ecs.entity() - .add(Pears) - .add(Bob); - - Bob.add(Alice); - - auto r = ecs.rule_builder() - .term().second().var("Food") - .term().second().var("Person") - .build(); - - int food_var = r.find_var("Food"); - int person_var = r.find_var("Person"); - - int count = 0; - r.iter() - .set_var("Food", Pears) - .set_var("Person", Bob) - .each([&](flecs::iter& it, size_t index) { - test_assert(it.entity(index) == Alice); - test_assert((it.id(1) == ecs.pair(Pears))); - test_assert((it.id(2) == ecs.pair(Bob))); - test_assert(it.get_var("Food") == Pears); - test_assert(it.get_var(food_var) == Pears); - test_assert(it.get_var("Person") == Bob); - test_assert(it.get_var(person_var) == Bob); - count ++; - }); - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_expr_w_var(void) { - flecs::world ecs; - - auto rel = ecs.entity("Rel"); - auto obj = ecs.entity(); - auto e = ecs.entity().add(rel, obj); - - auto r = ecs.rule_builder() - .expr("(Rel, $X)") - .build(); - - int x_var = r.find_var("X"); - test_assert(x_var != -1); - - int32_t count = 0; - r.each([&](flecs::iter& it, size_t index) { - test_assert(it.entity(index) == e); - test_assert(it.pair(1).second() == obj); - count ++; - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_get_first(void) { - flecs::world ecs; - - struct A {}; - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); - - auto q = ecs.rule(); - - auto first = q.iter().first(); - test_assert(first != 0); - test_assert(first == e1); - - q.destruct(); -} - -void RuleBuilder_get_count_direct(void) { - flecs::world ecs; - - struct A {}; - - ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); - - auto q = ecs.rule(); - - test_int(3, q.count()); - - q.destruct(); -} - -void RuleBuilder_get_is_true_direct(void) { - flecs::world ecs; - - struct A {}; - struct B {}; - - ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); - - auto q_1 = ecs.rule(); - auto q_2 = ecs.rule(); - - test_bool(true, q_1.is_true()); - test_bool(false, q_2.is_true()); - - q_1.destruct(); - q_2.destruct(); -} - -void RuleBuilder_get_first_direct(void) { - flecs::world ecs; - - struct A {}; - - auto e1 = ecs.entity().add(); - ecs.entity().add(); - ecs.entity().add(); - - auto q = ecs.rule(); - - auto first = q.first(); - test_assert(first != 0); - test_assert(first == e1); - - q.destruct(); -} - -void RuleBuilder_var_src_w_prefixed_name(void) { - flecs::world ecs; - - struct Foo { }; - - auto r = ecs.rule_builder() - .term().src("$Var") - .build(); - - auto e = ecs.entity().add(); - - int32_t count = 0; - r.iter([&](flecs::iter& it) { - test_assert(it.get_var("Var") == e); - count ++; - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_var_first_w_prefixed_name(void) { - flecs::world ecs; - - struct Foo { }; - - auto r = ecs.rule_builder() - .term() - .term().first("$Var") - .build(); - - auto e = ecs.entity().add(); - - int32_t count = 0; - r.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_uint(it.entity(0), e); - test_assert(it.get_var("Var") == ecs.id()); - count ++; - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_var_second_w_prefixed_name(void) { - flecs::world ecs; - - struct Foo { }; - - auto r = ecs.rule_builder() - .term().second("$Var") - .build(); - - auto t = ecs.entity(); - auto e = ecs.entity().add(t); - - int32_t count = 0; - r.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_uint(it.entity(0), e); - test_assert(it.get_var("Var") == t); - count ++; - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_term_w_second_var_string(void) { - flecs::world ecs; - - flecs::entity Foo = ecs.entity(); - - auto r = ecs.rule_builder() - .term(Foo, "$Var") - .build(); - - auto t = ecs.entity(); - auto e = ecs.entity().add(Foo, t); - - int32_t count = 0; - r.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_uint(it.entity(0), e); - test_assert(it.get_var("Var") == t); - count ++; - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_term_type_w_second_var_string(void) { - flecs::world ecs; - - struct Foo { }; - - auto r = ecs.rule_builder() - .term("$Var") - .build(); - - auto t = ecs.entity(); - auto e = ecs.entity().add(t); - - int32_t count = 0; - r.iter([&](flecs::iter& it) { - test_int(it.count(), 1); - test_uint(it.entity(0), e); - test_assert(it.get_var("Var") == t); - count ++; - }); - - test_int(count, 1); - - r.destruct(); -} - -void RuleBuilder_named_rule(void) { - flecs::world ecs; - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add(); - - auto q = ecs.rule("my_query"); - - int32_t count = 0; - q.each([&](flecs::entity e, Position&) { - test_assert(e == e1 || e == e2); - count ++; - }); - test_int(count, 2); - - flecs::entity qe = q.entity(); - test_assert(qe != 0); - test_str(qe.name(), "my_query"); - - q.destruct(); -} - -void RuleBuilder_named_scoped_rule(void) { - flecs::world ecs; - - auto e1 = ecs.entity().add(); - auto e2 = ecs.entity().add(); - - auto q = ecs.rule("my::query"); - - int32_t count = 0; - q.each([&](flecs::entity e, Position&) { - test_assert(e == e1 || e == e2); - count ++; - }); - test_int(count, 2); - - flecs::entity qe = q.entity(); - test_assert(qe != 0); - test_str(qe.name(), "query"); - test_str(qe.path(), "::my::query"); - - q.destruct(); -} - -void RuleBuilder_is_valid(void) { - flecs::world ecs; - - auto q_1 = ecs.rule(); - test_assert(q_1.is_valid()); - - flecs::log::set_level(-4); - auto q_2 = ecs.rule_builder().expr("foo").build(); - test_assert(!q_2.is_valid()); - - q_1.destruct(); -} - -void RuleBuilder_unresolved_by_name(void) { - flecs::world ecs; - - auto q = ecs.rule_builder() - .filter_flags(EcsFilterUnresolvedByName) - .expr("$this == Foo") - .build(); - - test_assert(q.is_valid()); - - test_false(q.iter().is_true()); - - ecs.entity("Foo"); - - test_true(q.iter().is_true()); - - q.destruct(); -} - -void RuleBuilder_scope(void) { - flecs::world ecs; - - flecs::entity Root = ecs.entity(); - flecs::entity TagA = ecs.entity(); - flecs::entity TagB = ecs.entity(); - - ecs.entity() - .add(Root) - .add(TagA) - .add(TagB); - - auto e2 = ecs.entity() - .add(Root) - .add(TagA); - - ecs.entity() - .add(Root) - .add(TagB); - - ecs.entity() - .add(Root); - - auto r = ecs.rule_builder() - .with(Root) - .scope_open().not_() - .with(TagA) - .without(TagB) - .scope_close() - .build(); - - int32_t count = 0; - r.each([&](flecs::entity e) { - test_assert(e != e2); - count ++; - }); - - test_int(count, 3); - - r.destruct(); -} - -void RuleBuilder_iter_w_stage(void) { - flecs::world ecs; - - ecs.set_stage_count(2); - flecs::world stage = ecs.get_stage(1); - - auto e1 = ecs.entity().add(); - - auto q = ecs.rule(); - - int32_t count = 0; - q.each(stage, [&](flecs::iter& it, size_t i, Position&) { - test_assert(it.world() == stage); - test_assert(it.entity(i) == e1); - count ++; - }); - - test_int(count, 1); - - q.destruct(); -} - -void RuleBuilder_inspect_terms_w_expr(void) { - flecs::world ecs; - - flecs::rule<> f = ecs.rule_builder() - .expr("(ChildOf,0)") - .build(); - - int32_t count = 0; - f.each_term([&](flecs::term &term) { - test_assert(term.id().is_pair()); - count ++; - }); - - test_int(count, 1); - - f.destruct(); -} - -void RuleBuilder_find(void) { - flecs::world ecs; - - /* auto e1 = */ ecs.entity().set({10, 20}); - auto e2 = ecs.entity().set({20, 30}); - - auto q = ecs.rule(); - - auto r = q.find([](Position& p) { - return p.x == 20; - }); - - test_assert(r == e2); - - q.destruct(); -} - -void RuleBuilder_find_not_found(void) { - flecs::world ecs; - - /* auto e1 = */ ecs.entity().set({10, 20}); - /* auto e2 = */ ecs.entity().set({20, 30}); - - auto q = ecs.rule(); - - auto r = q.find([](Position& p) { - return p.x == 30; - }); - - test_assert(!r); - - q.destruct(); -} - -void RuleBuilder_find_w_entity(void) { - flecs::world ecs; - - /* auto e1 = */ ecs.entity().set({10, 20}).set({20, 30}); - auto e2 = ecs.entity().set({20, 30}).set({20, 30}); - - auto q = ecs.rule(); - - auto r = q.find([](flecs::entity e, Position& p) { - return p.x == e.get()->x && - p.y == e.get()->y; - }); - - test_assert(r == e2); - - q.destruct(); -} diff --git a/vendors/flecs/test/cpp_api/src/Snapshot.cpp b/vendors/flecs/test/cpp_api/src/Snapshot.cpp deleted file mode 100644 index 189221094..000000000 --- a/vendors/flecs/test/cpp_api/src/Snapshot.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include - -void Snapshot_simple_snapshot(void) { - flecs::world world; - - auto e = flecs::entity(world) - .set({10, 20}) - .set({1, 1}); - - flecs::snapshot s(world); - s.take(); - - e.set({30, 40}); - e.set({2, 2}); - - s.restore(); - - const Position *p = e.get(); - const Velocity *v = e.get(); - - test_assert(p != NULL); - test_assert(v != NULL); - - test_int(p->x, 10); - test_int(p->y, 20); - - test_int(v->x, 1); - test_int(v->y, 1); -} diff --git a/vendors/flecs/test/cpp_api/src/Switch.cpp b/vendors/flecs/test/cpp_api/src/Switch.cpp deleted file mode 100644 index 4c939060f..000000000 --- a/vendors/flecs/test/cpp_api/src/Switch.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include - -void Switch_add_case(void) { - flecs::world world; - - auto Standing = world.entity("Standing"); - auto Walking = world.entity("Walking"); - auto Movement = world.entity().add(flecs::Union); - - auto e = world.entity() - .add(Movement, Standing); - test_assert(e.has(Movement, Standing)); - - auto table = e.table(); - - e.add(Movement, Walking); - test_assert(e.table() == table); - - test_assert(e.has(Movement, Walking)); - test_assert(!e.has(Movement, Standing)); -} - -void Switch_get_case(void) { - flecs::world world; - - auto Standing = world.entity("Standing"); - world.entity("Walking"); - auto Movement = world.entity().add(flecs::Union); - - auto e = world.entity() - .add(Movement, Standing); - test_assert(e.has(Movement, Standing)); - - test_assert(e.target(Movement) == Standing); -} - -void Switch_system_w_case(void) { - flecs::world world; - - auto Standing = world.entity("Standing"); - auto Walking = world.entity("Walking"); - auto Movement = world.entity("Movement").add(flecs::Union); - - world.entity().add(Movement, Walking); - world.entity().add(Movement, Walking); - world.entity().add(Movement, Standing); - - int count = 0, invoke_count = 0; - world.system() - .expr("(Movement, Walking)") - .iter([&](flecs::iter it) { - auto movement = it.field(1); - - invoke_count ++; - for (auto i : it) { - test_assert(movement[i] == Walking.id()); - count ++; - } - }); - - world.progress(); - - test_int(invoke_count, 2); - test_int(count, 2); -} - -void Switch_system_w_case_builder(void) { - flecs::world world; - - auto Standing = world.entity("Standing"); - auto Walking = world.entity("Walking"); - auto Movement = world.entity().add(flecs::Union); - - world.entity().add(Movement, Walking); - world.entity().add(Movement, Walking); - world.entity().add(Movement, Standing); - - int count = 0, invoke_count = 0; - world.system() - .term(Movement, Walking) - .iter([&](flecs::iter it) { - auto movement = it.field(1); - - invoke_count ++; - for (auto i : it) { - test_assert(movement[i] == Walking.id()); - count ++; - } - }); - - world.progress(); - - test_int(invoke_count, 2); - test_int(count, 2); -} - -void Switch_system_w_switch(void) { - flecs::world world; - - auto Standing = world.entity("Standing"); - auto Walking = world.entity("Walking"); - auto Movement = world.entity("Movement").add(flecs::Union); - - auto e1 = world.entity().add(Movement, Walking); - auto e2 = world.entity().add(Movement, Walking); - auto e3 = world.entity().add(Movement, Standing); - - int count = 0, invoke_count = 0; - world.system() - .expr("(Movement, *)") - .iter([&](flecs::iter it) { - flecs::column movement(it, 1); - - invoke_count ++; - for (auto i : it) { - if (it.entity(i) == e1 || it.entity(i) == e2) { - test_int(movement[i], Walking.id()); - } else if (it.entity(i) == e3) { - test_int(movement[i], Standing.id()); - } - count ++; - } - }); - - world.progress(); - - test_int(invoke_count, 1); - test_int(count, 3); -} - -struct Movement { }; -struct Standing { }; -struct Walking { }; - -void Switch_system_w_sw_type_builder(void) { - flecs::world world; - - world.component().add(flecs::Union); - - world.entity().add(); - world.entity().add(); - world.entity().add(); - - int count = 0, invoke_count = 0; - world.system<>() - .term() - .iter([&](flecs::iter it) { - auto movement = it.field(1); - - invoke_count ++; - for (auto i : it) { - test_assert(movement[i] == world.id()); - count ++; - } - }); - - world.progress(); - - test_int(invoke_count, 2); - test_int(count, 2); -} - -void Switch_add_case_w_type(void) { - flecs::world world; - - world.component().add(flecs::Union); - - auto e = world.entity().add(); - test_assert((e.has())); - - e.add(); - - test_assert((e.has())); - test_assert((!e.has())); -} - -void Switch_add_switch_w_type(void) { - flecs::world world; - - world.component().add(flecs::Union); - - auto e = world.entity().add(); - test_assert((e.has())); - - e.add(); - - test_assert((e.has())); - test_assert((!e.has())); -} - -void Switch_add_remove_switch_w_type(void) { - flecs::world world; - - world.component().add(flecs::Union); - - auto e = world.entity().add(); - test_assert(e.has(flecs::Wildcard)); - test_assert((e.has())); - - auto table = e.table(); - - e.add(); - - test_assert((e.has())); - test_assert((!e.has())); - test_assert(e.table() == table); - - auto c = e.target(); - test_assert(c != 0); - test_assert(c == world.id()); - - e.remove(flecs::Wildcard); - test_assert(!e.has(flecs::Wildcard)); - test_assert((!e.has())); - test_assert(e.table() != table); -} - -enum Color { - Red, - Green, - Blue -}; - -void Switch_switch_enum_type(void) { - flecs::world world; - - world.component().add(flecs::Union); - - auto e = world.entity().add(Red); - test_assert(e.has(Red)); - test_assert(!e.has(Green)); - test_assert(!e.has(Blue)); - test_assert(e.has(flecs::Wildcard)); - - auto table = e.table(); - - e.add(Green); - test_assert(!e.has(Red)); - test_assert(e.has(Green)); - test_assert(!e.has(Blue)); - test_assert(e.has(flecs::Wildcard)); - test_assert(e.table() == table); - - e.add(Blue); - test_assert(!e.has(Red)); - test_assert(!e.has(Green)); - test_assert(e.has(Blue)); - test_assert(e.has(flecs::Wildcard)); - test_assert(e.table() == table); - - e.remove(); - test_assert(!e.has(Red)); - test_assert(!e.has(Green)); - test_assert(!e.has(Blue)); - test_assert(!e.has(flecs::Wildcard)); - test_assert(e.table() != table); -} diff --git a/vendors/flecs/test/custom_builds/c/custom_header/include/custom_header.h b/vendors/flecs/test/custom_builds/c/custom_header/include/custom_header.h new file mode 100644 index 000000000..27cb892c5 --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/custom_header/include/custom_header.h @@ -0,0 +1,16 @@ +#ifndef CUSTOM_HEADER_H +#define CUSTOM_HEADER_H + +/* This generated file contains includes for project dependencies */ +#include "custom_header/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/test/custom_builds/c/plecs_w_parser/include/plecs_w_parser/bake_config.h b/vendors/flecs/test/custom_builds/c/custom_header/include/custom_header/bake_config.h similarity index 90% rename from vendors/flecs/test/custom_builds/c/plecs_w_parser/include/plecs_w_parser/bake_config.h rename to vendors/flecs/test/custom_builds/c/custom_header/include/custom_header/bake_config.h index 92e93f177..036dbd048 100644 --- a/vendors/flecs/test/custom_builds/c/plecs_w_parser/include/plecs_w_parser/bake_config.h +++ b/vendors/flecs/test/custom_builds/c/custom_header/include/custom_header/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef PLECS_W_PARSER_BAKE_CONFIG_H -#define PLECS_W_PARSER_BAKE_CONFIG_H +#ifndef CUSTOM_HEADER_BAKE_CONFIG_H +#define CUSTOM_HEADER_BAKE_CONFIG_H /* Headers of public dependencies */ #include "../../deps/flecs.h" diff --git a/vendors/flecs/test/custom_builds/c/custom_header/include/flecs_config.h b/vendors/flecs/test/custom_builds/c/custom_header/include/flecs_config.h new file mode 100644 index 000000000..63b83f133 --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/custom_header/include/flecs_config.h @@ -0,0 +1,2 @@ + +#define HELLOWORLD 1 diff --git a/vendors/flecs/test/custom_builds/c/meta_c/project.json b/vendors/flecs/test/custom_builds/c/custom_header/project.json similarity index 69% rename from vendors/flecs/test/custom_builds/c/meta_c/project.json rename to vendors/flecs/test/custom_builds/c/custom_header/project.json index f860cf273..5768a6c38 100644 --- a/vendors/flecs/test/custom_builds/c/meta_c/project.json +++ b/vendors/flecs/test/custom_builds/c/custom_header/project.json @@ -1,5 +1,5 @@ { - "id": "meta_c", + "id": "custom_header", "type": "application", "value": { "public": false, @@ -9,6 +9,6 @@ "standalone": true }, "lang.c": { - "defines": ["FLECS_CUSTOM_BUILD", "FLECS_META_C"] + "defines": ["FLECS_CONFIG_HEADER"] } } \ No newline at end of file diff --git a/vendors/flecs/test/custom_builds/c/custom_header/src/main.c b/vendors/flecs/test/custom_builds/c/custom_header/src/main.c new file mode 100644 index 000000000..7f2f19877 --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/custom_header/src/main.c @@ -0,0 +1,9 @@ +#include +#include + +int main(int argc, char *argv[]) { + (void)argc; + (void)argv; + assert(HELLOWORLD); + return 0; +} diff --git a/vendors/flecs/test/custom_builds/c/int_time_type/include/int_time_type.h b/vendors/flecs/test/custom_builds/c/int_time_type/include/int_time_type.h new file mode 100644 index 000000000..89d42fc8f --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/int_time_type/include/int_time_type.h @@ -0,0 +1,16 @@ +#ifndef INT_TIME_TYPE_H +#define INT_TIME_TYPE_H + +/* This generated file contains includes for project dependencies */ +#include "int_time_type/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/test/custom_builds/c/int_time_type/include/int_time_type/bake_config.h b/vendors/flecs/test/custom_builds/c/int_time_type/include/int_time_type/bake_config.h new file mode 100644 index 000000000..d7d5c6add --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/int_time_type/include/int_time_type/bake_config.h @@ -0,0 +1,24 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef INT_TIME_TYPE_BAKE_CONFIG_H +#define INT_TIME_TYPE_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include "../../deps/flecs.h" + +#endif + diff --git a/vendors/flecs/test/custom_builds/c/int_time_type/project.json b/vendors/flecs/test/custom_builds/c/int_time_type/project.json new file mode 100644 index 000000000..af8fdf286 --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/int_time_type/project.json @@ -0,0 +1,14 @@ +{ + "id": "int_time_type", + "type": "application", + "value": { + "public": false, + "use": [ + "flecs" + ], + "standalone": true + }, + "lang.c": { + "defines": ["ecs_ftime_t=int32_t"] + } +} \ No newline at end of file diff --git a/vendors/flecs/test/custom_builds/c/meta_c/src/main.c b/vendors/flecs/test/custom_builds/c/int_time_type/src/main.c similarity index 51% rename from vendors/flecs/test/custom_builds/c/meta_c/src/main.c rename to vendors/flecs/test/custom_builds/c/int_time_type/src/main.c index 6595fe530..89610bac5 100644 --- a/vendors/flecs/test/custom_builds/c/meta_c/src/main.c +++ b/vendors/flecs/test/custom_builds/c/int_time_type/src/main.c @@ -1,14 +1,10 @@ -#include - -ECS_STRUCT(Position, { - float x; - float y; -}); +#include +#include int main(int argc, char *argv[]) { ecs_world_t *world = ecs_init_w_args(argc, argv); - ECS_META_COMPONENT(world, Position); + ecs_progress(world, 0); return ecs_fini(world); } diff --git a/vendors/flecs/examples/cpp/rules/basics/include/basics.h b/vendors/flecs/test/custom_builds/c/json/include/json.h similarity index 69% rename from vendors/flecs/examples/cpp/rules/basics/include/basics.h rename to vendors/flecs/test/custom_builds/c/json/include/json.h index 25fdbeffa..1bb1bc327 100644 --- a/vendors/flecs/examples/cpp/rules/basics/include/basics.h +++ b/vendors/flecs/test/custom_builds/c/json/include/json.h @@ -1,8 +1,8 @@ -#ifndef BASICS_H -#define BASICS_H +#ifndef JSON_H +#define JSON_H /* This generated file contains includes for project dependencies */ -#include "basics/bake_config.h" +#include "json/bake_config.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/test/custom_builds/c/meta_c/include/meta_c/bake_config.h b/vendors/flecs/test/custom_builds/c/json/include/json/bake_config.h similarity index 92% rename from vendors/flecs/test/custom_builds/c/meta_c/include/meta_c/bake_config.h rename to vendors/flecs/test/custom_builds/c/json/include/json/bake_config.h index 87840d4c7..cdaa2bdf9 100644 --- a/vendors/flecs/test/custom_builds/c/meta_c/include/meta_c/bake_config.h +++ b/vendors/flecs/test/custom_builds/c/json/include/json/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef META_C_BAKE_CONFIG_H -#define META_C_BAKE_CONFIG_H +#ifndef JSON_BAKE_CONFIG_H +#define JSON_BAKE_CONFIG_H /* Headers of public dependencies */ #include "../../deps/flecs.h" diff --git a/vendors/flecs/test/custom_builds/c/plecs_w_parser/project.json b/vendors/flecs/test/custom_builds/c/json/project.json similarity index 63% rename from vendors/flecs/test/custom_builds/c/plecs_w_parser/project.json rename to vendors/flecs/test/custom_builds/c/json/project.json index 963427d09..e65010e4c 100644 --- a/vendors/flecs/test/custom_builds/c/plecs_w_parser/project.json +++ b/vendors/flecs/test/custom_builds/c/json/project.json @@ -1,5 +1,5 @@ { - "id": "plecs_w_parser", + "id": "json", "type": "application", "value": { "public": false, @@ -9,6 +9,6 @@ "standalone": true }, "lang.c": { - "defines": ["FLECS_CUSTOM_BUILD", "FLECS_PLECS", "FLECS_PARSER"] + "defines": ["FLECS_CUSTOM_BUILD", "FLECS_JSON"] } } diff --git a/vendors/flecs/test/custom_builds/c/json/src/main.c b/vendors/flecs/test/custom_builds/c/json/src/main.c new file mode 100644 index 000000000..942c4686b --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/json/src/main.c @@ -0,0 +1,11 @@ +#include +#include + +int main(int argc, char *argv[]) { + ecs_world_t *world = ecs_init_w_args(argc, argv); + + char *json = ecs_world_to_json(world, NULL); + ecs_os_free(json); + + return ecs_fini(world); +} diff --git a/vendors/flecs/test/custom_builds/c/monitor/project.json b/vendors/flecs/test/custom_builds/c/monitor/project.json index 4224b1e22..c67c7f9d4 100644 --- a/vendors/flecs/test/custom_builds/c/monitor/project.json +++ b/vendors/flecs/test/custom_builds/c/monitor/project.json @@ -9,6 +9,6 @@ "standalone": true }, "lang.c": { - "defines": ["FLECS_CUSTOM_BUILD", "FLECS_MONITOR", "FLECS_LOG"] + "defines": ["FLECS_CUSTOM_BUILD", "FLECS_STATS", "FLECS_LOG"] } } \ No newline at end of file diff --git a/vendors/flecs/test/custom_builds/c/monitor/src/main.c b/vendors/flecs/test/custom_builds/c/monitor/src/main.c index a6c78d832..94d39ad97 100644 --- a/vendors/flecs/test/custom_builds/c/monitor/src/main.c +++ b/vendors/flecs/test/custom_builds/c/monitor/src/main.c @@ -3,7 +3,7 @@ int main(int argc, char *argv[]) { ecs_world_t *world = ecs_init_w_args(argc, argv); - ECS_IMPORT(world, FlecsMonitor); + ECS_IMPORT(world, FlecsStats); return ecs_fini(world); } diff --git a/vendors/flecs/test/custom_builds/c/monitor_rest/project.json b/vendors/flecs/test/custom_builds/c/monitor_rest/project.json index e4e3b08b3..779ed42cb 100644 --- a/vendors/flecs/test/custom_builds/c/monitor_rest/project.json +++ b/vendors/flecs/test/custom_builds/c/monitor_rest/project.json @@ -9,6 +9,6 @@ "standalone": true }, "lang.c": { - "defines": ["FLECS_CUSTOM_BUILD", "FLECS_MONITOR", "FLECS_REST", "FLECS_LOG"] + "defines": ["FLECS_CUSTOM_BUILD", "FLECS_STATS", "FLECS_REST", "FLECS_LOG"] } } \ No newline at end of file diff --git a/vendors/flecs/test/custom_builds/c/monitor_rest/src/main.c b/vendors/flecs/test/custom_builds/c/monitor_rest/src/main.c index 2e0381e45..692020ac2 100644 --- a/vendors/flecs/test/custom_builds/c/monitor_rest/src/main.c +++ b/vendors/flecs/test/custom_builds/c/monitor_rest/src/main.c @@ -3,7 +3,7 @@ int main(int argc, char *argv[]) { ecs_world_t *world = ecs_init_w_args(argc, argv); - ECS_IMPORT(world, FlecsMonitor); + ECS_IMPORT(world, FlecsStats); ECS_IMPORT(world, FlecsRest); return ecs_fini(world); diff --git a/vendors/flecs/examples/c/queries/instancing/include/instancing.h b/vendors/flecs/test/custom_builds/c/perf_trace/include/perf_trace.h similarity index 65% rename from vendors/flecs/examples/c/queries/instancing/include/instancing.h rename to vendors/flecs/test/custom_builds/c/perf_trace/include/perf_trace.h index e1620a5bb..e40d32c4c 100644 --- a/vendors/flecs/examples/c/queries/instancing/include/instancing.h +++ b/vendors/flecs/test/custom_builds/c/perf_trace/include/perf_trace.h @@ -1,8 +1,8 @@ -#ifndef INSTANCING_H -#define INSTANCING_H +#ifndef PERF_TRACE_H +#define PERF_TRACE_H /* This generated file contains includes for project dependencies */ -#include "instancing/bake_config.h" +#include "perf_trace/bake_config.h" #ifdef __cplusplus extern "C" { diff --git a/vendors/flecs/test/custom_builds/cpp/snapshot/include/snapshot/bake_config.h b/vendors/flecs/test/custom_builds/c/perf_trace/include/perf_trace/bake_config.h similarity index 91% rename from vendors/flecs/test/custom_builds/cpp/snapshot/include/snapshot/bake_config.h rename to vendors/flecs/test/custom_builds/c/perf_trace/include/perf_trace/bake_config.h index 9e10624d8..1371e0899 100644 --- a/vendors/flecs/test/custom_builds/cpp/snapshot/include/snapshot/bake_config.h +++ b/vendors/flecs/test/custom_builds/c/perf_trace/include/perf_trace/bake_config.h @@ -14,8 +14,8 @@ * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ -#ifndef SNAPSHOT_BAKE_CONFIG_H -#define SNAPSHOT_BAKE_CONFIG_H +#ifndef PERF_TRACE_BAKE_CONFIG_H +#define PERF_TRACE_BAKE_CONFIG_H /* Headers of public dependencies */ #include "../../deps/flecs.h" diff --git a/vendors/flecs/test/custom_builds/c/perf_trace/project.json b/vendors/flecs/test/custom_builds/c/perf_trace/project.json new file mode 100644 index 000000000..f347f850f --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/perf_trace/project.json @@ -0,0 +1,14 @@ +{ + "id": "perf_trace", + "type": "application", + "value": { + "public": false, + "use": [ + "flecs" + ], + "standalone": true + }, + "lang.c": { + "defines": ["FLECS_PERF_TRACE"] + } +} diff --git a/vendors/flecs/test/custom_builds/c/perf_trace/src/main.c b/vendors/flecs/test/custom_builds/c/perf_trace/src/main.c new file mode 100644 index 000000000..0a62999cf --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/perf_trace/src/main.c @@ -0,0 +1,40 @@ +#include +#include + +int push_invoked = 0; +int pop_invoked = 0; + +static +void trace_push(const char *file, size_t line, const char *name) { + (void)file; + (void)line; + (void)name; + push_invoked ++; +} + +static +void trace_pop(const char *file, size_t line, const char *name) { + (void)file; + (void)line; + (void)name; + pop_invoked ++; +} + +int main(int argc, char *argv[]) { + ecs_os_set_api_defaults(); + ecs_os_api_t os_api = ecs_os_get_api(); + os_api.perf_trace_push_ = trace_push; + os_api.perf_trace_pop_ = trace_pop; + ecs_os_set_api(&os_api); + + ecs_world_t *world = ecs_init_w_args(argc, argv); + + /* Run systems */ + ecs_progress(world, 0); + + assert(push_invoked != 0); + assert(pop_invoked != 0); + assert(push_invoked == pop_invoked); + + return ecs_fini(world); +} diff --git a/vendors/flecs/test/custom_builds/c/pipeline/src/main.c b/vendors/flecs/test/custom_builds/c/pipeline/src/main.c index efd22b4d3..47dab3dbd 100644 --- a/vendors/flecs/test/custom_builds/c/pipeline/src/main.c +++ b/vendors/flecs/test/custom_builds/c/pipeline/src/main.c @@ -5,8 +5,8 @@ typedef struct { } Position, Velocity; void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -21,13 +21,13 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(world, Velocity); ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, {.add = { ecs_dependson(EcsOnUpdate) }}), - .query.filter.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, + .entity = ecs_entity(world, {.add = ecs_ids( ecs_dependson(EcsOnUpdate) )}), + .query.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, .callback = Move }); assert(s != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {10, 20}); ecs_set(world, e, Velocity, {1, 2}); diff --git a/vendors/flecs/test/custom_builds/c/pipeline_w_system/src/main.c b/vendors/flecs/test/custom_builds/c/pipeline_w_system/src/main.c index 10c351ac6..fa0320cfb 100644 --- a/vendors/flecs/test/custom_builds/c/pipeline_w_system/src/main.c +++ b/vendors/flecs/test/custom_builds/c/pipeline_w_system/src/main.c @@ -5,8 +5,8 @@ typedef struct { } Position, Velocity; void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -21,13 +21,13 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(world, Velocity); ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .entity = ecs_entity(world, { .add = {ecs_dependson(EcsOnUpdate)} }), - .query.filter.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, + .entity = ecs_entity(world, { .add = ecs_ids(ecs_dependson(EcsOnUpdate)) }), + .query.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, .callback = Move }); assert(s != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {10, 20}); ecs_set(world, e, Velocity, {1, 2}); diff --git a/vendors/flecs/test/custom_builds/c/plecs/project.json b/vendors/flecs/test/custom_builds/c/plecs/project.json index 5148e5019..7da9c8965 100644 --- a/vendors/flecs/test/custom_builds/c/plecs/project.json +++ b/vendors/flecs/test/custom_builds/c/plecs/project.json @@ -9,6 +9,6 @@ "standalone": true }, "lang.c": { - "defines": ["FLECS_CUSTOM_BUILD", "FLECS_PLECS"] + "defines": ["FLECS_CUSTOM_BUILD", "FLECS_SCRIPT"] } } \ No newline at end of file diff --git a/vendors/flecs/test/custom_builds/c/plecs/src/main.c b/vendors/flecs/test/custom_builds/c/plecs/src/main.c index 35823ff1f..0b8fdc66d 100644 --- a/vendors/flecs/test/custom_builds/c/plecs/src/main.c +++ b/vendors/flecs/test/custom_builds/c/plecs/src/main.c @@ -3,7 +3,7 @@ int main(int argc, char *argv[]) { ecs_world_t *world = ecs_init_w_args(argc, argv); - int ret = ecs_plecs_from_str(world, NULL, "Foo"); + int ret = ecs_script_run(world, NULL, "Foo {}"); assert(ret == 0); (void)ret; diff --git a/vendors/flecs/test/custom_builds/c/plecs_w_parser/include/plecs_w_parser.h b/vendors/flecs/test/custom_builds/c/plecs_w_parser/include/plecs_w_parser.h deleted file mode 100644 index 3176d99bd..000000000 --- a/vendors/flecs/test/custom_builds/c/plecs_w_parser/include/plecs_w_parser.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef PLECS_W_PARSER_H -#define PLECS_W_PARSER_H - -/* This generated file contains includes for project dependencies */ -#include "plecs_w_parser/bake_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/vendors/flecs/test/custom_builds/c/plecs_w_parser/src/main.c b/vendors/flecs/test/custom_builds/c/plecs_w_parser/src/main.c deleted file mode 100644 index 382b8af02..000000000 --- a/vendors/flecs/test/custom_builds/c/plecs_w_parser/src/main.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -int main(int argc, char *argv[]) { - ecs_world_t *world = ecs_init_w_args(argc, argv); - - int ret = ecs_plecs_from_str(world, NULL, "Foo"); - assert(ret == 0); - (void)ret; - - ecs_fini(world); -} diff --git a/vendors/flecs/test/custom_builds/c/rest/src/main.c b/vendors/flecs/test/custom_builds/c/rest/src/main.c index de33f0182..9d4d09c57 100644 --- a/vendors/flecs/test/custom_builds/c/rest/src/main.c +++ b/vendors/flecs/test/custom_builds/c/rest/src/main.c @@ -3,7 +3,7 @@ int main(int argc, char *argv[]) { ecs_world_t *world = ecs_init_w_args(argc, argv); - ecs_set(world, 0, EcsRest, {0}); + ecs_insert(world, ecs_value(EcsRest, {0})); return ecs_fini(world); } diff --git a/vendors/flecs/test/custom_builds/c/system/src/main.c b/vendors/flecs/test/custom_builds/c/system/src/main.c index a9c8585cb..173b0e93b 100644 --- a/vendors/flecs/test/custom_builds/c/system/src/main.c +++ b/vendors/flecs/test/custom_builds/c/system/src/main.c @@ -5,8 +5,8 @@ typedef struct { } Position, Velocity; void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -21,12 +21,12 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(world, Velocity); ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .query.filter.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, + .query.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, .callback = Move, }); assert(s != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {10, 20}); ecs_set(world, e, Velocity, {1, 2}); diff --git a/vendors/flecs/test/custom_builds/c/timer/src/main.c b/vendors/flecs/test/custom_builds/c/timer/src/main.c index 8e3534c6b..93804df1b 100644 --- a/vendors/flecs/test/custom_builds/c/timer/src/main.c +++ b/vendors/flecs/test/custom_builds/c/timer/src/main.c @@ -5,8 +5,8 @@ typedef struct { } Position, Velocity; void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i ++) { p[i].x += v[i].x; @@ -21,17 +21,17 @@ int main(int argc, char *argv[]) { ECS_COMPONENT(world, Velocity); ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){ - .query.filter.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, + .query.terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, .callback = Move, - .entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}), + .entity = ecs_entity(world, {.add = ecs_ids(ecs_dependson(EcsOnUpdate))}), .interval = 2.9f }); assert(s != 0); - EcsTimer *timer = ecs_get_mut(world, s, EcsTimer); + EcsTimer *timer = ecs_ensure(world, s, EcsTimer); timer->time = 0; - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {10, 20}); ecs_set(world, e, Velocity, {1, 2}); diff --git a/vendors/flecs/test/custom_builds/c/use_os_alloc/include/use_os_alloc.h b/vendors/flecs/test/custom_builds/c/use_os_alloc/include/use_os_alloc.h new file mode 100644 index 000000000..af511d10c --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/use_os_alloc/include/use_os_alloc.h @@ -0,0 +1,16 @@ +#ifndef USE_OS_ALLOC_H +#define USE_OS_ALLOC_H + +/* This generated file contains includes for project dependencies */ +#include "use_os_alloc/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/test/custom_builds/c/use_os_alloc/include/use_os_alloc/bake_config.h b/vendors/flecs/test/custom_builds/c/use_os_alloc/include/use_os_alloc/bake_config.h new file mode 100644 index 000000000..6d11ab7b2 --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/use_os_alloc/include/use_os_alloc/bake_config.h @@ -0,0 +1,24 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef USE_OS_ALLOC_BAKE_CONFIG_H +#define USE_OS_ALLOC_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include "../../deps/flecs.h" + +#endif + diff --git a/vendors/flecs/test/custom_builds/c/use_os_alloc/project.json b/vendors/flecs/test/custom_builds/c/use_os_alloc/project.json new file mode 100644 index 000000000..09b72be9d --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/use_os_alloc/project.json @@ -0,0 +1,14 @@ +{ + "id": "use_os_alloc", + "type": "application", + "value": { + "public": false, + "use": [ + "flecs" + ], + "standalone": true + }, + "lang.c": { + "defines": ["FLECS_USE_OS_ALLOC"] + } +} diff --git a/vendors/flecs/test/custom_builds/c/use_os_alloc/src/main.c b/vendors/flecs/test/custom_builds/c/use_os_alloc/src/main.c new file mode 100644 index 000000000..92c9a796f --- /dev/null +++ b/vendors/flecs/test/custom_builds/c/use_os_alloc/src/main.c @@ -0,0 +1,10 @@ +#include +#include + +int main(int argc, char *argv[]) { + ecs_world_t *world = ecs_init_w_args(argc, argv); + + ecs_progress(world, 0.0); + + return ecs_fini(world); +} diff --git a/vendors/flecs/test/custom_builds/cpp/app/src/main.cpp b/vendors/flecs/test/custom_builds/cpp/app/src/main.cpp index e715dcaea..bf24afd90 100644 --- a/vendors/flecs/test/custom_builds/cpp/app/src/main.cpp +++ b/vendors/flecs/test/custom_builds/cpp/app/src/main.cpp @@ -3,7 +3,7 @@ int main(int, char *[]) { flecs::world ecs; - ecs.system().iter([](flecs::iter& it) { + ecs.system().run([](flecs::iter& it) { it.world().quit(); }); diff --git a/vendors/flecs/test/api/.gitignore b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/.gitignore similarity index 87% rename from vendors/flecs/test/api/.gitignore rename to vendors/flecs/test/custom_builds/cpp/define_malloc_free/.gitignore index 335606867..fff6aadd6 100644 --- a/vendors/flecs/test/api/.gitignore +++ b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/.gitignore @@ -1,4 +1,5 @@ .bake_cache .DS_Store .vscode +gcov bin diff --git a/vendors/flecs/test/custom_builds/cpp/define_malloc_free/include/define_malloc_free.h b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/include/define_malloc_free.h new file mode 100644 index 000000000..8f25b88eb --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/include/define_malloc_free.h @@ -0,0 +1,16 @@ +#ifndef DEFINE_MALLOC_FREE_H +#define DEFINE_MALLOC_FREE_H + +/* This generated file contains includes for project dependencies */ +#include "define_malloc_free/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/test/custom_builds/cpp/define_malloc_free/include/define_malloc_free/bake_config.h b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/include/define_malloc_free/bake_config.h new file mode 100644 index 000000000..582678e89 --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/include/define_malloc_free/bake_config.h @@ -0,0 +1,24 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef DEFINE_MALLOC_FREE_BAKE_CONFIG_H +#define DEFINE_MALLOC_FREE_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include + +#endif + diff --git a/vendors/flecs/test/custom_builds/cpp/define_malloc_free/project.json b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/project.json new file mode 100644 index 000000000..2d9b72ed6 --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/project.json @@ -0,0 +1,10 @@ +{ + "id": "define_malloc_free", + "type": "application", + "value": { + "use": [ + "flecs" + ], + "language": "c++" + } +} \ No newline at end of file diff --git a/vendors/flecs/test/custom_builds/cpp/define_malloc_free/src/main.cpp b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/src/main.cpp new file mode 100644 index 000000000..c202dbe20 --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/define_malloc_free/src/main.cpp @@ -0,0 +1,25 @@ +#include + +namespace foo { + void* my_malloc(size_t size) { + return malloc(size); + } + + void my_free(void* ptr) { + free(ptr); + } +} + +#define malloc(size) foo::my_malloc(size) +#define free(ptr) foo::my_free(ptr) + +#include + +int main(int, char *[]) { + flecs::world world; + + void *ptr = malloc(10); + free(ptr); + + return 0; +} diff --git a/vendors/flecs/test/custom_builds/cpp/dll_module/dll_module/src/main.cpp b/vendors/flecs/test/custom_builds/cpp/dll_module/dll_module/src/main.cpp index a611001b1..4f4278d3c 100644 --- a/vendors/flecs/test/custom_builds/cpp/dll_module/dll_module/src/main.cpp +++ b/vendors/flecs/test/custom_builds/cpp/dll_module/dll_module/src/main.cpp @@ -3,9 +3,9 @@ namespace movement { module::module(flecs::world& ecs) { - ecs.module(); + ecs.module(); - ecs.component(); - ecs.component(); + ecs.component(); + ecs.component(); } } diff --git a/vendors/flecs/test/custom_builds/cpp/doc/src/main.cpp b/vendors/flecs/test/custom_builds/cpp/doc/src/main.cpp index f1046c0cd..85924fd99 100644 --- a/vendors/flecs/test/custom_builds/cpp/doc/src/main.cpp +++ b/vendors/flecs/test/custom_builds/cpp/doc/src/main.cpp @@ -8,8 +8,10 @@ int main(int, char *[]) { flecs::doc::set_brief(e, "brief"); flecs::doc::set_detail(e, "detail"); flecs::doc::set_link(e, "link"); + flecs::doc::set_color(e, "#000000"); assert(!strcmp(flecs::doc::get_brief(e), "brief")); assert(!strcmp(flecs::doc::get_detail(e), "detail")); assert(!strcmp(flecs::doc::get_link(e), "link")); + assert(!strcmp(flecs::doc::get_color(e), "#000000")); } diff --git a/vendors/flecs/test/custom_builds/cpp/ensure_define/include/ensure_define.h b/vendors/flecs/test/custom_builds/cpp/ensure_define/include/ensure_define.h new file mode 100644 index 000000000..bd03c0597 --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/ensure_define/include/ensure_define.h @@ -0,0 +1,16 @@ +#ifndef ENSURE_DEFINE_H +#define ENSURE_DEFINE_H + +/* This generated file contains includes for project dependencies */ +#include "ensure_define/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/test/custom_builds/cpp/ensure_define/include/ensure_define/bake_config.h b/vendors/flecs/test/custom_builds/cpp/ensure_define/include/ensure_define/bake_config.h new file mode 100644 index 000000000..0e978a425 --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/ensure_define/include/ensure_define/bake_config.h @@ -0,0 +1,24 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef ENSURE_DEFINE_BAKE_CONFIG_H +#define ENSURE_DEFINE_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include + +#endif + diff --git a/vendors/flecs/examples/cpp/queries/instancing/project.json b/vendors/flecs/test/custom_builds/cpp/ensure_define/project.json similarity index 84% rename from vendors/flecs/examples/cpp/queries/instancing/project.json rename to vendors/flecs/test/custom_builds/cpp/ensure_define/project.json index c57e07476..c9ebee168 100644 --- a/vendors/flecs/examples/cpp/queries/instancing/project.json +++ b/vendors/flecs/test/custom_builds/cpp/ensure_define/project.json @@ -1,5 +1,5 @@ { - "id": "instancing", + "id": "ensure_define", "type": "application", "value": { "use": [ diff --git a/vendors/flecs/test/custom_builds/cpp/ensure_define/src/main.cpp b/vendors/flecs/test/custom_builds/cpp/ensure_define/src/main.cpp new file mode 100644 index 000000000..6312f900c --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/ensure_define/src/main.cpp @@ -0,0 +1,9 @@ +// Unreal (apparently) defines an ensure macro which conflicts with ensure +// methods in the C++ API. This test makes sure this doesn't break compilation. +#define ensure foobar +#include +#include + +int main(int argc, char *argv[]) { + flecs::world world(argc, argv); +} diff --git a/vendors/flecs/test/custom_builds/cpp/no_auto_registration/.gitignore b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/.gitignore new file mode 100644 index 000000000..fff6aadd6 --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/.gitignore @@ -0,0 +1,5 @@ +.bake_cache +.DS_Store +.vscode +gcov +bin diff --git a/vendors/flecs/test/custom_builds/cpp/no_auto_registration/include/no_auto_registration.h b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/include/no_auto_registration.h new file mode 100644 index 000000000..871a08e48 --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/include/no_auto_registration.h @@ -0,0 +1,16 @@ +#ifndef NO_AUTO_REGISTRATION_H +#define NO_AUTO_REGISTRATION_H + +/* This generated file contains includes for project dependencies */ +#include "no_auto_registration/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/test/custom_builds/cpp/no_auto_registration/include/no_auto_registration/bake_config.h b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/include/no_auto_registration/bake_config.h new file mode 100644 index 000000000..3754be807 --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/include/no_auto_registration/bake_config.h @@ -0,0 +1,24 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef NO_AUTO_REGISTRATION_BAKE_CONFIG_H +#define NO_AUTO_REGISTRATION_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include "../../deps/flecs.h" + +#endif + diff --git a/vendors/flecs/test/custom_builds/cpp/snapshot/project.json b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/project.json similarity index 68% rename from vendors/flecs/test/custom_builds/cpp/snapshot/project.json rename to vendors/flecs/test/custom_builds/cpp/no_auto_registration/project.json index b4929f232..c9920a526 100644 --- a/vendors/flecs/test/custom_builds/cpp/snapshot/project.json +++ b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/project.json @@ -1,5 +1,5 @@ { - "id": "snapshot", + "id": "no_auto_registration", "type": "application", "value": { "use": [ @@ -10,6 +10,6 @@ "language": "c++" }, "lang.cpp": { - "defines": ["FLECS_CUSTOM_BUILD", "FLECS_CPP", "FLECS_SNAPSHOT"] + "defines": ["FLECS_CPP_NO_AUTO_REGISTRATION"] } } diff --git a/vendors/flecs/test/custom_builds/cpp/no_auto_registration/src/main.cpp b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/src/main.cpp new file mode 100644 index 000000000..4e7fba8c9 --- /dev/null +++ b/vendors/flecs/test/custom_builds/cpp/no_auto_registration/src/main.cpp @@ -0,0 +1,212 @@ +#include +#include + +int main(int, char *[]) { + flecs::world world; + + world.import(); + world.import(); + world.import(); + + // check if builtin components are registered + + // core + world.id(); + world.id(); + world.id(); + + // alerts + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + // metrics + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + // doc + world.id(); + + // monitor + world.id(); + world.id(); + world.id(); + + // meta + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + // rest + world.id(); + + // timer + world.id(); + world.id(); + + // units + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + + world.id(); + + world.id(); + world.id(); + + world.id(); + + world.id(); + + world.id(); + + world.id(); + + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + + world.id(); + world.id(); + world.id(); + + return 0; +} diff --git a/vendors/flecs/test/custom_builds/cpp/pipeline_no_timer/project.json b/vendors/flecs/test/custom_builds/cpp/pipeline_no_timer/project.json index 0b3dda086..3a456c938 100644 --- a/vendors/flecs/test/custom_builds/cpp/pipeline_no_timer/project.json +++ b/vendors/flecs/test/custom_builds/cpp/pipeline_no_timer/project.json @@ -17,8 +17,6 @@ "FLECS_MODULE", "FLECS_SYSTEM", "FLECS_PIPELINE", - "FLECS_EXPR", - "FLECS_PARSER", "FLECS_LOG" ] } diff --git a/vendors/flecs/test/custom_builds/cpp/snapshot/include/snapshot.h b/vendors/flecs/test/custom_builds/cpp/snapshot/include/snapshot.h deleted file mode 100644 index 5a1b0ca48..000000000 --- a/vendors/flecs/test/custom_builds/cpp/snapshot/include/snapshot.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef SNAPSHOT_H -#define SNAPSHOT_H - -/* This generated file contains includes for project dependencies */ -#include "snapshot/bake_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/vendors/flecs/test/custom_builds/cpp/snapshot/src/main.cpp b/vendors/flecs/test/custom_builds/cpp/snapshot/src/main.cpp deleted file mode 100644 index 5a0332969..000000000 --- a/vendors/flecs/test/custom_builds/cpp/snapshot/src/main.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include - -int main(int, char *[]) { - flecs::world world; - - auto s = world.snapshot(); - - s.restore(); -} diff --git a/vendors/flecs/test/custom_builds/cpp/timer/src/main.cpp b/vendors/flecs/test/custom_builds/cpp/timer/src/main.cpp index 3f4e2703c..0f0c8ef36 100644 --- a/vendors/flecs/test/custom_builds/cpp/timer/src/main.cpp +++ b/vendors/flecs/test/custom_builds/cpp/timer/src/main.cpp @@ -19,8 +19,8 @@ int main(int, char *[]) { p.y += v.y; }); - flecs::Timer *t = s.get_mut(); - t->time = 0; + flecs::Timer& t = s.ensure(); + t.time = 0; auto e = ecs.entity() .set({10, 20}) diff --git a/vendors/flecs/test/meta/include/meta.h b/vendors/flecs/test/meta/include/meta.h index e781962bc..ad1befaa1 100644 --- a/vendors/flecs/test/meta/include/meta.h +++ b/vendors/flecs/test/meta/include/meta.h @@ -61,6 +61,9 @@ typedef struct { void install_test_abort(void); +#define test_json(v1, v2) _test_json(v1, v2, #v1, #v2, __FILE__, __LINE__) +void _test_json(const char* str1, const char *str2, const char* vstr1, const char *vstr2, const char *file, int line); + #ifdef __cplusplus } #endif diff --git a/vendors/flecs/test/meta/project.json b/vendors/flecs/test/meta/project.json index 2fa699ebe..7219f43e9 100644 --- a/vendors/flecs/test/meta/project.json +++ b/vendors/flecs/test/meta/project.json @@ -81,7 +81,8 @@ "struct_w_enum", "zero_initialized", "enum_relation", - "enum_w_short_notation" + "enum_w_short_notation", + "enum_modified_event" ] }, { "id": "BitmaskTypes", @@ -94,6 +95,49 @@ "struct_w_bitmask", "bitmask_w_short_notation" ] + }, { + "id": "RuntimeTypes", + "testcases": [ + "trivial_struct", + "ctor", + "dtor", + "move", + "copy", + "trivial_array", + "array_ctor", + "array_dtor", + "array_move", + "array_copy", + "vector_lifecycle", + "vector_lifecycle_trivial_type", + "opaque", + "struct_with_ints", + "struct_with_strings", + "struct_with_opaque", + "nested_struct_with_strings", + "struct_with_array_of_strings", + "struct_with_array_of_array_of_strings", + "struct_with_vector_of_ints", + "struct_with_vector_of_strings", + "nested_struct_with_vector_of_ints", + "nested_struct_with_vector_of_strings", + "array_of_ints", + "array_of_strings", + "array_of_struct_with_ints", + "array_of_struct_with_strings", + "array_of_struct_with_opaques", + "array_of_array_of_strings", + "array_of_array_of_struct_with_strings", + "array_of_vectors_of_ints", + "array_of_vectors_of_strings", + "array_of_opaque", + "vector_of_ints", + "vector_of_strings", + "vector_of_struct_with_ints", + "vector_of_struct_with_strings", + "vector_of_arrays_of_strings", + "vector_of_opaque" + ] }, { "id": "StructTypes", "testcases": [ @@ -124,7 +168,8 @@ "overlapping_error_warning_range", "overlapping_value_error_range", "overlapping_value_warning_range", - "struct_w_16_alignment" + "struct_w_16_alignment", + "struct_w_use_offset" ] }, { "id": "NestedStructTypes", @@ -216,7 +261,8 @@ "builtin_units", "unit_w_short_notation", "unit_prefix_w_short_notation", - "quantity_w_short_notation" + "quantity_w_short_notation", + "import_units_after_mini" ] }, { "id": "Serialized", @@ -281,7 +327,8 @@ "ops_bitmask", "ops_struct_w_bitmask", "ops_enum", - "ops_struct_w_enum" + "ops_struct_w_enum", + "ops_missing_metatype" ] }, { "id": "Cursor", @@ -382,7 +429,7 @@ "opaque_set_int", "opaque_set_uint", "opaque_set_float", - "opaque_set_string", + "opaque_get_set_string", "opaque_set_entity", "opaque_set_id", "opaque_set_int_vec", @@ -422,125 +469,6 @@ "set_out_of_bounds", "get_member_id" ] - }, { - "id": "DeserializeFromExpr", - "testcases": [ - "bool", - "byte", - "char", - "char_literal", - "i8", - "i16", - "i32", - "i64", - "iptr", - "u8", - "u16", - "u32", - "u64", - "uptr", - "float", - "double", - "negative_int", - "negative_float", - "string", - "entity", - "id", - "enum", - "bitmask", - "struct_enum", - "struct_bitmask", - "struct_i32", - "struct_i32_neg", - "struct_i32_i32", - "struct_entity", - "struct_id", - "struct_nested_i32", - "struct_nested_i32_i32", - "struct_2_nested_i32_i32", - "struct_member_i32", - "struct_member_i32_neg", - "struct_member_i32_i32", - "struct_member_nested_i32", - "struct_member_nested_i32_i32", - "struct_member_2_nested_i32_i32", - "struct_member_2_nested_i32_i32_reverse", - "struct_i32_array_3", - "struct_struct_i32_array_3", - "struct_struct_i32_i32_array_3", - "struct_w_array_type_i32_i32", - "struct_w_array_type_struct", - "struct_w_2_array_type_i32_i32", - "struct_w_2_array_type_struct", - "discover_type_int", - "discover_type_negative_int", - "discover_type_float", - "discover_type_negative_float", - "discover_type_string", - "discover_type_multiline_string", - "discover_type_entity", - "discover_type_bool", - "discover_type_unknown", - "discover_type_invalid" - ] - }, { - "id": "SerializeToExpr", - "testcases": [ - "bool", - "byte", - "char", - "i8", - "i16", - "i32", - "i64", - "iptr", - "u8", - "u16", - "u32", - "u64", - "uptr", - "float", - "double", - "string", - "entity", - "id", - "enum", - "bitmask", - "float_nan", - "float_inf", - "double_nan", - "double_inf", - "struct_enum", - "struct_bitmask", - "struct_i32", - "struct_i32_i32", - "struct_entity", - "struct_id", - "array_i32_3", - "array_struct_i32_i32", - "array_array_i32_3", - "vector_i32_3", - "vector_struct_i32_i32", - "vector_array_i32_3", - "entity_entity_after_float", - "struct_nested_i32", - "struct_nested_i32_i32", - "struct_2_nested_i32_i32", - "struct_i32_array_3", - "struct_struct_i32_array_3", - "struct_struct_i32_i32_array_3", - "struct_w_array_type_i32_i32", - "struct_w_array_type_struct", - "struct_w_2_array_type_i32_i32", - "struct_w_2_array_type_struct", - "struct_partial", - "escape_simple_string", - "escape_newline", - "escape_2_newlines", - "escape_string_w_trailing_newline", - "escape_string_w_2_trailing_newlines", - "escape_string_w_delim" - ] }, { "id": "DeserializeFromJson", "testcases": [ @@ -580,14 +508,19 @@ "struct_w_2_nested_members_i32", "struct_w_nested_members_struct", "struct_w_2_nested_members_struct", + "ser_deser_entity_named", + "ser_deser_entity_named_child", + "ser_deser_entity_namespaced_component", "deser_entity_1_component_1_member", "deser_entity_1_component_1_member_w_spaces", "deser_entity_1_component_2_members", "deser_entity_2_components", + "deser_entity_2_components_missing_object_close", "deser_entity_1_component_composite_member", "deser_entity_1_component_nested_member", "deser_entity_1_pair", "deser_entity_2_pairs", + "deser_entity_1_pair_2_targets", "deser_entity_empty", "deser_entity_w_path", "deser_entity_w_path_and_ids", @@ -626,6 +559,8 @@ "ser_deser_new_world_2_entities_w_named_parent", "ser_deser_new_world_2_entities_w_anon_parent_w_cycle", "ser_deser_new_world_2_entities_w_named_parent_w_cycle", + "ser_deser_new_world_w_prefab", + "ser_deser_new_world_w_disabled", "ser_deser_restore_1_entity_to_empty_table", "ser_deser_restore_1_entity_to_non_empty_table", "ser_deser_restore_1_anon_entity_to_empty_table", @@ -646,7 +581,31 @@ "ser_deser_3_entities_after_remove_all", "ser_deser_3_entities_after_delete_with", "ser_deser_w_hooks", - "ser_deser_large_data" + "ser_deser_large_data", + "ser_deser_different_component_order", + "ser_deser_no_reflection_data", + "ser_deser_no_reflection_data_strict", + "ser_deser_value_for_tag", + "ser_deser_value_for_tag_strict", + "ser_deser_value_for_non_existing", + "ser_deser_value_for_non_existing_strict", + "ser_deser_cpp_typename", + "ser_deser_cpp_template", + "ser_deser_cpp_template_1_param", + "ser_deser_cpp_template_n_params", + "ser_deser_cpp_template_nested", + "ser_deser_cpp_template_n_params_nested", + "ser_deser_long_name", + "ser_deser_long_name_256_chars", + "ser_deser_w_alerts", + "ser_deser_w_alerts_w_progress", + "ser_deser_struct", + "ser_deser_anon_w_same_id_as_existing_named", + "ser_deser_named_to_different_table", + "ser_deser_named_child_to_different_table", + "ser_deser_with_child_tgt", + "ser_deser_with_child_tgt_no_child", + "deser_invalid_entity_name" ] }, { "id": "SerializeToJson", @@ -668,6 +627,8 @@ "struct_double", "struct_string", "struct_entity", + "struct_entity_0", + "struct_entity_10k", "struct_entity_after_float", "struct_id", "struct_float_nan", @@ -693,7 +654,8 @@ "array_array_i32_3", "vector_i32_3", "vector_struct_i32_i32", - "vector_array_i32_3" + "vector_array_i32_3", + "serialize_from_stage" ] }, { "id": "SerializeEntityToJson", @@ -704,8 +666,16 @@ "serialize_w_name_2_tags", "serialize_w_name_1_pair", "serialize_w_base", - "serialize_w_base_override", + "serialize_w_base_dont_inherit_tag", + "serialize_w_base_dont_inherit_component", + "serialize_w_base_dont_inherit_pair", "serialize_w_2_base", + "serialize_component_w_base", + "serialize_component_w_base_no_reflection_data", + "serialize_component_w_base_w_owned", + "serialize_component_w_base_w_owned_no_reflection_data", + "serialize_component_w_base_w_owned_override", + "serialize_component_w_base_w_owned_no_reflection_data_override", "serialize_w_nested_base", "serialize_w_1_component", "serialize_w_2_components", @@ -717,23 +687,16 @@ "serialize_w_type_info_unit", "serialize_w_type_info_unit_quantity", "serialize_w_type_info_unit_over", - "serialize_wo_private", - "serialize_w_private", + "serialize_w_type_info_no_types", + "serialize_w_type_info_no_components", "serialize_w_label", "serialize_w_label_no_name", - "serialize_w_id_labels", "serialize_w_brief", "serialize_w_brief_no_brief", "serialize_w_link", "serialize_w_link_no_link", "serialize_color", "serialize_w_doc_w_quotes", - "serialize_union_relationship", - "serialize_union_relationship_w_labels", - "serialize_union_relationship_invalid_entity", - "serialize_union_relationship_invalid_entity_w_labels", - "serialize_w_union_property", - "serialize_w_union_property", "serialize_from_core", "serialize_w_1_alert", "serialize_w_2_alerts", @@ -744,11 +707,32 @@ "serialize_refs_childof", "serialize_refs_custom", "serialize_refs_wildcard", - "serialize_no_ids", "serialize_matches_filter", "serialize_matches_query", "serialize_matches_rule", - "serialize_no_matches" + "serialize_no_matches", + "serialize_id_recycled", + "serialize_union_target", + "serialize_union_target_recycled", + "serialize_anonymous_w_builtin", + "serialize_named_w_builtin", + "serialize_named_child_w_builtin", + "serialize_named_child_w_builtin_w_type_info", + "serialize_from_stage", + "serialize_sparse", + "serialize_sparse_pair", + "serialize_sparse_mixed", + "serialize_sparse_inherited", + "serialize_sparse_inherited_pair", + "serialize_sparse_inherited_mixed", + "serialize_sparse_w_type_info", + "serialize_auto_override_w_inherited", + "serialize_auto_override", + "serialize_auto_override_pair", + "serialize_auto_override_fullpath", + "serialize_auto_override_pair_fullpath", + "serialize_toggle", + "serialize_toggle_pair" ] }, { "id": "SerializeIterToJson", @@ -774,6 +758,8 @@ "serialize_type_info_w_unit_quantity", "serialize_type_info_w_unit_over", "serialize_w_entity_label", + "serialize_w_entity_label_w_str", + "serialize_entity_label_w_newline", "serialize_w_var_labels", "serialize_w_var_component", "serialize_w_optional_tag", @@ -789,8 +775,13 @@ "serialize_color", "serialize_ids", "serialize_ids_2_entities", - "serialize_variable_ids", - "serialize_variable_ids_2_entities", + "serialize_anonymous", + "serialize_anonymous_ids", + "serialize_variable_anonymous_no_full_path", + "serialize_variable_anonymous", + "serialize_anonymous_tag", + "serialize_anonymous_component", + "serialize_anonymous_pair", "serialize_invalid_value", "serialize_recycled_pair_id", "serialize_w_alert", @@ -806,16 +797,86 @@ "serialize_table", "serialize_table_w_id_labels", "serialize_table_w_var_labels", - "serialize_table_w_private", "serialize_world", - "serialize_term_labels", - "serialize_id_labels", "serialize_vars_for_query", "serialize_var_labels_for_query", - "serialize_var_ids_for_query", "serialize_null_doc_name", "serialize_rule_w_optional", - "serialize_rule_w_optional_component" + "serialize_rule_w_optional_component", + "serialize_entity_w_flecs_core_parent", + "no_fields", + "no_fields_w_vars", + "serialize_from_stage", + "serialize_sparse", + "serialize_15_fields", + "serialize_16_fields", + "serialize_31_fields", + "serialize_32_fields", + "serialize_field_w_escaped_sep" + ] + }, { + "id": "SerializeIterToRowJson", + "testcases": [ + "serialize_this_w_1_tag", + "serialize_this_w_1_tag_w_parent", + "serialize_this_w_1_tag_no_name", + "serialize_this_w_2_tag", + "serialize_this_w_1_component", + "serialize_this_w_2_component", + "serialize_this_w_2_component_1_shared", + "serialize_this_w_1_pair", + "serialize_this_w_2_pair", + "serialize_this_w_1_pair_component", + "serialize_this_w_1_var", + "serialize_this_w_2_var", + "serialize_this_w_2_var_doc_name", + "serialize_this_w_1_tag_component_pair_var", + "serialize_this_w_2_tag_component_pair_var", + "serialize_var_w_1_tag", + "serialize_var_w_1_component", + "serialize_var_w_1_pair", + "serialize_var_w_1_var", + "serialize_var_w_2_component_1_shared", + "serialize_var_w_1_tag_component_pair_var", + "serialize_var_w_2_tag_component_pair_var", + "serialize_fixed_w_1_tag", + "serialize_fixed_w_1_component", + "serialize_fixed_w_1_pair", + "serialize_fixed_w_1_var", + "serialize_fixed_w_2_component_1_shared", + "serialize_fixed_w_1_tag_component_pair_var", + "serialize_fixed_w_2_tag_component_pair_var", + "serialize_not", + "serialize_not_pair_wildcard", + "serialize_not_pair_var", + "serialize_not_pair_var_constrained", + "serialize_optional", + "serialize_optional_pair_wildcard", + "serialize_optional_pair_var", + "serialize_optional_pair_var_constrained", + "serialize_or", + "serialize_scope", + "serialize_eq", + "serialize_neq", + "serialize_eq_m", + "serialize_table", + "serialize_table_w_eq", + "serialize_table_w_neq", + "serialize_table_w_2_pair_targets", + "serialize_table_w_2_pair_targets_2_rel", + "serialize_table_w_3_pair_targets", + "serialize_table_w_3_pair_targets_2_rel", + "serialize_everything", + "serialize_everything_table", + "serialize_w_type_info", + "serialize_w_field_info", + "serialize_w_field_info_pair_w_0_target", + "serialize_w_field_info_pair_w_not_tag", + "serialize_w_field_info_pair_w_not_pair", + "serialize_w_field_info_pair_w_not_component", + "serialize_w_field_info_w_or", + "serialize_recycled_id", + "serialize_entity_w_flecs_core_parent" ] }, { "id": "SerializeTypeInfoToJson", @@ -861,6 +922,37 @@ "struct_nested_2_members", "struct_nested_3_members" ] + }, { + "id": "SerializeQueryInfoToJson", + "testcases": [ + "1_tag", + "1_component", + "1_pair", + "1_pair_w_wildcard", + "1_pair_w_any", + "1_tag_fixed_src", + "1_tag_var_src", + "1_component_in", + "1_component_inout", + "1_component_out", + "1_component_none", + "1_tag_not", + "2_tags_or", + "1_tag_optional", + "1_tag_self", + "1_tag_self_dont_inherit", + "1_tag_up", + "1_tag_cascade", + "0_term", + "anonymous_tag", + "anonymous_pair", + "anonymous_component", + "anonymous_tag_recycled", + "anonymous_pair_recycled", + "anonymous_component_recycled", + "serialize_plan_trivial_query", + "serialize_plan_nontrivial_query" + ] }, { "id": "MetaUtils", "testcases": [ @@ -887,155 +979,6 @@ "enum_constant_w_type_prefix", "enum_constant_w_name_type_prefix" ] - }, { - "id": "Vars", - "testcases": [ - "declare_1_var", - "declare_2_vars", - "declare_vars_nested_scope", - "declare_vars_2_scopes", - "redeclare_var", - "i32_expr_w_i32_var", - "i32_expr_w_f32_var", - "i32_expr_w_string_var", - "string_expr_w_string_var", - "struct_expr_w_i32_vars", - "struct_expr_w_struct_var", - "nested_struct_expr_w_struct_var", - "declare_w_value", - "redeclare_in_scope", - "init_fini_vars" - ] - }, { - "id": "DeserExprOperators", - "testcases": [ - "add_2_int_literals", - "add_2_int_literals_twice", - "sub_2_int_literals", - "mul_2_int_literals", - "div_2_int_literals", - "add_3_int_literals", - "add_3_int_literals_twice", - "sub_3_int_literals", - "mul_3_int_literals", - "div_3_int_literals", - "int_to_bool", - "bool_to_int", - "bool_to_uint", - "add_mul_3_int_literals", - "sub_mul_3_int_literals", - "div_mul_3_int_literals", - "add_div_3_int_literals", - "sub_div_3_int_literals", - "mul_div_3_int_literals", - "mul_add_mul_add_int_literals", - "mul_sub_mul_sub_int_literals", - "mul_div_mul_div_int_literals", - "div_add_div_add_int_literals", - "div_sub_div_sub_int_literals", - "div_sub_div_mul_int_literals", - "div_mul_div_mul_int_literals", - "add_2_flt_literals", - "sub_2_flt_literals", - "mul_2_flt_literals", - "div_2_flt_literals", - "add_2_int_neg_literals", - "sub_2_int_neg_literals", - "mul_2_int_neg_literals", - "div_2_int_neg_literals", - "mul_lparen_add_add_rparen_int_literals", - "mul_lparen_add_add_add_rparen_int_literals", - "mul_lparen_add_add_rparen_add_int_literals", - "lparen_add_add_rparen_mul_int_literals", - "lparen_add_add_add_rparen_mul_int_literals", - "double_paren_add_add", - "double_paren_literal", - "lparen_add_add_rparen_mul_lparen_add_add_rparen", - "float_result_add_2_int_literals", - "struct_result_add_2_int_literals", - "struct_result_add_2_2_fields_int_literals", - "struct_result_add_3_int_literals", - "struct_result_lparen_int_rparen", - "add_to_var", - "add_var_to", - "var_member", - "bool_cond_and_bool", - "bool_cond_or_bool", - "int_cond_and_int", - "int_cond_or_int", - "bool_cond_and_int", - "int_cond_and_bool", - "bool_cond_or_int", - "int_cond_or_bool", - "cond_eq_bool", - "cond_eq_int", - "cond_neq_bool", - "cond_neq_int", - "cond_eq_bool_int", - "cond_eq_int_flt", - "cond_eq_cond_and", - "cond_eq_cond_or", - "cond_gt_bool", - "cond_gt_int", - "cond_gt_flt", - "cond_gteq_bool", - "cond_gteq_int", - "cond_gteq_flt", - "cond_lt_bool", - "cond_lt_int", - "cond_lt_flt", - "cond_lteq_bool", - "cond_lteq_int", - "cond_lteq_flt", - "min_lparen_int_rparen", - "min_lparen_int_add_int_rparen", - "min_var", - "min_lparen_int_rparen_to_i64", - "min_lparen_int_rparen_to_i32", - "struct_w_min_var", - "struct_w_min_lparen_int_rparen", - "struct_w_min_lparen_var_rparen", - "shift_left_int", - "shift_right_int", - "shift_left_int_add_int", - "shift_left_int_mul_int", - "add_int_shift_left_int", - "mul_int_shift_left_int", - "add_int_shift_left_int_add_int", - "mul_int_shift_left_int_mul_int", - "entity_expr", - "entity_path_expr", - "entity_parent_func", - "entity_name_func", - "entity_doc_name_func", - "entity_chain_func", - "var_parent_func", - "var_name_func", - "var_doc_name_func", - "var_chain_func", - "interpolate_string_w_i32_var", - "interpolate_string_w_string_var", - "interpolate_string_w_entity_var", - "interpolate_string_w_id_var", - "interpolate_string_w_var_not_found", - "interpolate_string_w_entity_var_0", - "interpolate_string_w_var_special_chars", - "interpolate_string_w_var_before_after_text", - "interpolate_string_w_curly_brackets_var", - "interpolate_string_w_curly_brackets_expr", - "interpolate_string_w_curly_brackets_expr_w_var", - "interpolate_string_w_curly_brackets_expr_w_composite_var", - "interpolate_string_w_escape_var_operator", - "interpolate_string_w_escape_curly_brackets", - "interpolate_string_w_func", - "interpolate_string_w_func_chain", - "iter_to_vars_no_data", - "iter_to_vars_1_comp", - "iter_to_vars_2_comps", - "iter_to_vars_1_comp_1_tag", - "iter_to_vars_w_1_query_var", - "iter_to_vars_w_2_query_vars" - ] }, { "id": "OpaqueTypes", "testcases": [ @@ -1055,7 +998,8 @@ "deser_entity_from_json", "ser_deser_world_w_ser_opaque", "ser_deser_entity", - "ser_deser_0_entity" + "ser_deser_0_entity", + "const_string" ] }, { "id": "Misc", diff --git a/vendors/flecs/test/meta/src/BitmaskTypes.c b/vendors/flecs/test/meta/src/BitmaskTypes.c index 76759cc42..50889652e 100644 --- a/vendors/flecs/test/meta/src/BitmaskTypes.c +++ b/vendors/flecs/test/meta/src/BitmaskTypes.c @@ -11,7 +11,7 @@ void meta_test_bitmask( test_int(ct->size, ECS_SIZEOF(ecs_i32_t)); test_int(ct->alignment, ECS_ALIGNOF(ecs_i32_t)); - const EcsMetaType *mt = ecs_get(world, t, EcsMetaType); + const EcsType *mt = ecs_get(world, t, EcsType); test_assert(mt != NULL); test_assert(mt->kind == EcsBitmaskType); @@ -48,7 +48,7 @@ void meta_test_constant( if (!ecs_os_strcmp(c->name, name)) { test_int(c->value, value); - const ecs_u32_t *vptr = ecs_get_pair_object(world, c->constant, + const ecs_u32_t *vptr = ecs_get_pair_second(world, c->constant, EcsConstant, ecs_u32_t); if (vptr) { test_int(*vptr, value); diff --git a/vendors/flecs/test/meta/src/Cursor.c b/vendors/flecs/test/meta/src/Cursor.c index 9938dc50a..8f79b2332 100644 --- a/vendors/flecs/test/meta/src/Cursor.c +++ b/vendors/flecs/test/meta/src/Cursor.c @@ -454,7 +454,7 @@ void Cursor_set_entity_as_signed(void) { ecs_world_t *world = ecs_init(); ecs_entity_t value = 0; - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_meta_cursor_t cur = ecs_meta_cursor(world, ecs_id(ecs_entity_t), &value); test_ok( ecs_meta_set_int(&cur, (int64_t)e) ); @@ -468,7 +468,7 @@ void Cursor_set_entity_as_unsigned(void) { ecs_world_t *world = ecs_init(); ecs_entity_t value = 0; - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_meta_cursor_t cur = ecs_meta_cursor(world, ecs_id(ecs_entity_t), &value); test_ok( ecs_meta_set_uint(&cur, e) ); @@ -496,7 +496,7 @@ void Cursor_set_id_as_signed(void) { ecs_world_t *world = ecs_init(); ecs_id_t value = 0; - ecs_id_t e = ecs_new_id(world); + ecs_id_t e = ecs_new(world); ecs_meta_cursor_t cur = ecs_meta_cursor(world, ecs_id(ecs_id_t), &value); test_ok( ecs_meta_set_int(&cur, (int64_t)e) ); @@ -510,7 +510,7 @@ void Cursor_set_id_as_unsigned(void) { ecs_world_t *world = ecs_init(); ecs_id_t value = 0; - ecs_id_t e = ecs_new_id(world); + ecs_id_t e = ecs_new(world); ecs_meta_cursor_t cur = ecs_meta_cursor(world, ecs_id(ecs_id_t), &value); test_ok( ecs_meta_set_uint(&cur, e) ); @@ -2249,7 +2249,7 @@ void Cursor_struct_pop_after_dotmember(void) { } }); - T value = {{0}}; + T value = {{{0, 0},{0, 0}}, {{0, 0},{0, 0}}}; ecs_meta_cursor_t cur = ecs_meta_cursor(world, t, &value); test_ok( ecs_meta_push(&cur) ); @@ -2941,7 +2941,14 @@ void Cursor_opaque_set_float(void) { ecs_fini(world); } -void Cursor_opaque_set_string(void) { +static +int const_string_t_serialize(const ecs_serializer_t *ser, const void *ptr) { + char **data = ECS_CONST_CAST(char**, ptr); + ser->value(ser, ecs_id(ecs_string_t), data); + return 0; +} + +void Cursor_opaque_get_set_string(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Opaque_const_string_t); @@ -2949,7 +2956,8 @@ void Cursor_opaque_set_string(void) { ecs_opaque(world, { .entity = ecs_id(Opaque_const_string_t), .type.as_type = ecs_id(ecs_string_t), - .type.assign_string = const_string_t_set + .type.assign_string = const_string_t_set, + .type.serialize = const_string_t_serialize }); Opaque_const_string_t v = { 0 }; @@ -2958,6 +2966,9 @@ void Cursor_opaque_set_string(void) { test_int(0, ecs_meta_set_string(&cur, "Hello World")); test_str(v.value, "Hello World"); + const char* str = ecs_meta_get_string(&cur); + test_str(str, "Hello World"); + ecs_fini(world); } @@ -2973,8 +2984,8 @@ void Cursor_opaque_set_entity(void) { }); Opaque_entity v = { 0 }; - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); ecs_meta_cursor_t cur = ecs_meta_cursor(world, ecs_id(Opaque_entity), &v); test_int(0, ecs_meta_set_entity(&cur, e1)); @@ -2997,8 +3008,8 @@ void Cursor_opaque_set_id(void) { }); Opaque_id v = { 0 }; - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); ecs_meta_cursor_t cur = ecs_meta_cursor(world, ecs_id(Opaque_id), &v); test_int(0, ecs_meta_set_id(&cur, e1)); @@ -4557,10 +4568,10 @@ void Cursor_get_member_id(void) { ecs_meta_cursor_t cur = ecs_meta_cursor(world, t, &value); test_ok( ecs_meta_push(&cur) ); test_assert(ecs_meta_get_member_id(&cur) != 0); - test_assert(ecs_meta_get_member_id(&cur) == ecs_lookup_fullpath(world, "T.x")); + test_assert(ecs_meta_get_member_id(&cur) == ecs_lookup(world, "T.x")); test_ok( ecs_meta_next(&cur) ); test_assert(ecs_meta_get_member_id(&cur) != 0); - test_assert(ecs_meta_get_member_id(&cur) == ecs_lookup_fullpath(world, "T.y")); + test_assert(ecs_meta_get_member_id(&cur) == ecs_lookup(world, "T.y")); test_ok( ecs_meta_pop(&cur) ); ecs_fini(world); diff --git a/vendors/flecs/test/meta/src/DeserializeFromJson.c b/vendors/flecs/test/meta/src/DeserializeFromJson.c index 15a721434..30ec8db13 100644 --- a/vendors/flecs/test/meta/src/DeserializeFromJson.c +++ b/vendors/flecs/test/meta/src/DeserializeFromJson.c @@ -896,9 +896,9 @@ void DeserializeFromJson_struct_i32_array_3(void) { T value = {0}; - ecs_ensure(world, 10); - ecs_ensure(world, 20); - ecs_ensure(world, 30); + ecs_make_alive(world, 10); + ecs_make_alive(world, 20); + ecs_make_alive(world, 30); const char *ptr = ecs_ptr_from_json(world, t, &value, "{\"x\": [10, 20, 30]}", NULL); test_assert(ptr != NULL); @@ -1246,6 +1246,72 @@ void DeserializeFromJson_struct_w_2_nested_members_struct(void) { ecs_fini(world); } +void DeserializeFromJson_ser_deser_entity_named(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + char *json = ecs_entity_to_json(world, e, NULL); + + const char *r = ecs_entity_from_json(world, e, json, NULL); + test_assert(r != NULL); + ecs_os_free(json); + + test_str(ecs_get_name(world, e), "e"); + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_entity_named_child(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t e = ecs_entity(world, { .name = "parent.e" }); + char *json = ecs_entity_to_json(world, e, NULL); + + const char *r = ecs_entity_from_json(world, e, json, NULL); + test_assert(r != NULL); + ecs_os_free(json); + + ecs_entity_t parent = ecs_lookup(world, "parent"); + test_assert(parent != 0); + + test_str(ecs_get_name(world, e), "e"); + test_uint(parent, ecs_get_target(world, e, EcsChildOf, 0)); + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_entity_namespaced_component(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, { .name = "ns.Position" }), + .members = { + { "x", .type = ecs_id(ecs_i32_t) }, + { "y", .type = ecs_id(ecs_i32_t) } + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + char *json = ecs_entity_to_json(world, e, NULL); + ecs_set(world, e, Position, {0, 0}); + const char *r = ecs_entity_from_json(world, e, json, NULL); + test_assert(r != NULL); + ecs_os_free(json); + + test_str(ecs_get_name(world, e), "e"); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + void DeserializeFromJson_deser_entity_1_component_1_member(void) { ecs_world_t *world = ecs_init(); @@ -1259,9 +1325,9 @@ void DeserializeFromJson_deser_entity_1_component_1_member(void) { test_assert(ecs_id(Position) != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *ptr = ecs_entity_from_json(world, e, - "{\"ids\":[[\"Position\"]],\"values\":[{\"x\":10}]}", NULL); + "{\"components\": {\"Position\": {\"x\": 10}}}", NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1285,9 +1351,9 @@ void DeserializeFromJson_deser_entity_1_component_1_member_w_spaces(void) { test_assert(ecs_id(Position) != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *ptr = ecs_entity_from_json(world, e, - " { \"ids\" : [ [ \"Position\" ] ] , \"values\" : [ { \"x\" : 10 } ] }", NULL); + "{ \"components\" : { \"Position\" : { \"x\" : 10 } } }", NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1311,9 +1377,9 @@ void DeserializeFromJson_deser_entity_1_component_2_members(void) { test_assert(ecs_id(Position) != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *ptr = ecs_entity_from_json(world, e, - "{\"ids\":[[\"Position\"]],\"values\":[{\"x\":10, \"y\":20}]}", NULL); + "{\"components\": {\"Position\": {\"x\": 10, \"y\": 20}}}", NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1325,6 +1391,39 @@ void DeserializeFromJson_deser_entity_1_component_2_members(void) { ecs_fini(world); } +void DeserializeFromJson_deser_entity_2_components_missing_object_close(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + test_assert(ecs_id(Position) != 0); + + ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + test_assert(ecs_id(Velocity) != 0); + + ecs_entity_t e = ecs_new(world); + + ecs_log_set_level(-4); + const char *ptr = ecs_entity_from_json(world, e, + "{\"components\": {\"Position\": {\"x\": 10, \"y\": 20}, \"Velocity\": {\"x\": 1, \"y\": 2}}", NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + void DeserializeFromJson_deser_entity_2_components(void) { ecs_world_t *world = ecs_init(); @@ -1348,14 +1447,9 @@ void DeserializeFromJson_deser_entity_2_components(void) { test_assert(ecs_id(Velocity) != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *ptr = ecs_entity_from_json(world, e, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]]," - "\"values\":[" - "{\"x\":10, \"y\":20}, " - "{\"x\":1, \"y\":2}" - "]}", NULL); + "{\"components\": {\"Position\": {\"x\": 10, \"y\": 20}, \"Velocity\": {\"x\": 1, \"y\": 2}}}", NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1395,9 +1489,9 @@ void DeserializeFromJson_deser_entity_1_component_composite_member(void) { test_assert(ecs_id(Line) != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *ptr = ecs_entity_from_json(world, e, - "{\"ids\":[[\"Line\"]],\"values\":[{\"start\": {\"x\":10, \"y\":20}, \"stop\": {\"x\":30, \"y\":40}}]}", NULL); + "{\"components\": {\"Line\": {\"start\": {\"x\": 10, \"y\": 20}, \"stop\": {\"x\": 30, \"y\": 40}}}}", NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1434,9 +1528,9 @@ void DeserializeFromJson_deser_entity_1_component_nested_member(void) { test_assert(ecs_id(Line) != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *ptr = ecs_entity_from_json(world, e, - "{\"ids\":[[\"Line\"]],\"values\":[{\"start.x\": 10, \"start.y\": 20, \"stop.x\": 30, \"stop.y\": 40}]}", NULL); + "{\"components\": {\"Line\": {\"start.x\": 10, \"start.y\": 20, \"stop.x\": 30, \"stop.y\": 40}}}", NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1465,9 +1559,9 @@ void DeserializeFromJson_deser_entity_1_pair(void) { test_assert(ecs_id(Position) != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *ptr = ecs_entity_from_json(world, e, - "{\"ids\":[[\"Position\", \"Tgt\"]],\"values\":[{\"x\":10, \"y\":20}]}", NULL); + "{\"components\": {\"(Position,Tgt)\": {\"x\": 10, \"y\": 20}}}", NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1504,14 +1598,9 @@ void DeserializeFromJson_deser_entity_2_pairs(void) { test_assert(ecs_id(Velocity) != 0); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *ptr = ecs_entity_from_json(world, e, - "{" - "\"ids\":[[\"Position\", \"Tgt\"], [\"Velocity\", \"Tgt\"]]," - "\"values\":[" - "{\"x\":10, \"y\":20}, " - "{\"x\":1, \"y\":2}" - "]}", NULL); + "{\"components\": {\"(Position,Tgt)\": {\"x\": 10, \"y\": 20}, \"(Velocity,Tgt)\": {\"x\": 1, \"y\": 2}}}", NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1531,7 +1620,7 @@ void DeserializeFromJson_deser_entity_2_pairs(void) { void DeserializeFromJson_deser_entity_empty(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *r = ecs_entity_from_json(world, e, "{}", NULL); test_str(r, ""); @@ -1541,8 +1630,8 @@ void DeserializeFromJson_deser_entity_empty(void) { void DeserializeFromJson_deser_entity_w_path(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); - const char *r = ecs_entity_from_json(world, e, "{\"path\":\"ent\"}", NULL); + ecs_entity_t e = ecs_new(world); + const char *r = ecs_entity_from_json(world, e, "{\"name\":\"ent\"}", NULL); test_str(r, ""); test_str(ecs_get_name(world, e), "ent"); @@ -1554,9 +1643,9 @@ void DeserializeFromJson_deser_entity_w_path_and_ids(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *r = ecs_entity_from_json(world, e, - "{\"path\":\"ent\", \"ids\":[[\"Position\"]]}", NULL); + "{\"name\": \"ent\", \"components\": {\"Position\": null}}", NULL); test_str(r, ""); test_str(ecs_get_name(world, e), "ent"); test_assert(ecs_has(world, e, Position)); @@ -1577,9 +1666,9 @@ void DeserializeFromJson_deser_entity_w_path_and_ids_and_values(void) { } }); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *r = ecs_entity_from_json(world, e, - "{\"path\":\"ent\", \"ids\":[[\"Position\"]], \"values\":[{\"x\":10, \"y\":20}]}", NULL); + "{\"name\": \"ent\", \"components\": {\"Position\":{\"x\": 10, \"y\": 20}}}", NULL); test_str(r, ""); test_str(ecs_get_name(world, e), "ent"); test_assert(ecs_has(world, e, Position)); @@ -1596,9 +1685,9 @@ void DeserializeFromJson_deser_entity_w_ids(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); const char *r = ecs_entity_from_json(world, e, - "{\"ids\":[[\"Position\"]]}", NULL); + "{\"tags\": [\"Position\"]}", NULL); test_str(r, ""); test_str(ecs_get_name(world, e), NULL); test_assert(ecs_has(world, e, Position)); @@ -1606,6 +1695,33 @@ void DeserializeFromJson_deser_entity_w_ids(void) { ecs_fini(world); } +void DeserializeFromJson_deser_entity_1_pair_2_targets(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add_pair(world, e, Rel, TgtA); + ecs_add_pair(world, e, Rel, TgtB); + + char *json = ecs_entity_to_json(world, e, NULL); + + ecs_remove_pair(world, e, Rel, EcsWildcard); + test_assert(!ecs_has_pair(world, e, Rel, TgtA)); + test_assert(!ecs_has_pair(world, e, Rel, TgtB)); + + const char *r = ecs_entity_from_json(world, e, json, NULL); + test_assert(r != NULL); + ecs_os_free(json); + + test_assert(ecs_has_pair(world, e, Rel, TgtA)); + test_assert(ecs_has_pair(world, e, Rel, TgtB)); + + ecs_fini(world); +} + void DeserializeFromJson_ser_deser_mini(void) { ecs_world_t *world = ecs_mini(); @@ -1733,6 +1849,8 @@ void DeserializeFromJson_ser_deser_new_world(void) { char *json = ecs_world_to_json(world, &desc); test_assert(json != NULL); + // printf("%s\n", json); + ecs_fini(world); world = ecs_init(); @@ -1748,7 +1866,7 @@ void DeserializeFromJson_ser_deser_new_world_1_entity_w_tag(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); test_assert(e != 0); char *json = ecs_world_to_json(world, NULL); @@ -1775,7 +1893,7 @@ void DeserializeFromJson_ser_deser_new_world_1_entity_w_component(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); char *json = ecs_world_to_json(world, NULL); @@ -1812,7 +1930,7 @@ void DeserializeFromJson_ser_deser_new_world_1_entity_w_component_meta(void) { } }); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(e != 0); char *json = ecs_world_to_json(world, NULL); @@ -1852,7 +1970,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_tag(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); ecs_add(world, e, Tag); test_assert(e != 0); @@ -1867,7 +1985,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_tag(void) { test_str(r, ""); ecs_os_free(json); - e = ecs_lookup_fullpath(world, "foo"); + e = ecs_lookup(world, "foo"); test_assert(e != 0); test_assert(ecs_is_alive(world, Tag)); test_assert(ecs_has(world, e, Tag)); @@ -1881,7 +1999,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_component(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); ecs_add(world, e, Position); test_assert(e != 0); @@ -1896,7 +2014,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_component(void) { test_str(r, ""); ecs_os_free(json); - e = ecs_lookup_fullpath(world, "foo"); + e = ecs_lookup(world, "foo"); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); test_assert(ecs_is_alive(world, ecs_id(Position))); @@ -1921,7 +2039,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_component_meta(voi } }); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); ecs_set(world, e, Position, {10, 20}); test_assert(e != 0); @@ -1944,7 +2062,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_component_meta(voi test_str(r, ""); ecs_os_free(json); - e = ecs_lookup_fullpath(world, "foo"); + e = ecs_lookup(world, "foo"); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); test_assert(ecs_is_alive(world, ecs_id(Position))); @@ -1964,7 +2082,7 @@ void DeserializeFromJson_ser_deser_new_world_1_entity_w_tag_serialize_all(void) ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new(world, Tag); + ecs_entity_t e = ecs_new_w(world, Tag); test_assert(e != 0); ecs_world_to_json_desc_t desc = {0}; @@ -1994,7 +2112,7 @@ void DeserializeFromJson_ser_deser_new_world_1_entity_w_component_serialize_all( ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); test_assert(e != 0); ecs_world_to_json_desc_t desc = {0}; @@ -2034,7 +2152,7 @@ void DeserializeFromJson_ser_deser_new_world_1_entity_w_component_meta_serialize } }); - ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); test_assert(e != 0); ecs_world_to_json_desc_t desc = {0}; @@ -2077,7 +2195,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_tag_serialize_all( ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); ecs_add(world, e, Tag); test_assert(e != 0); @@ -2095,7 +2213,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_tag_serialize_all( test_str(r, ""); ecs_os_free(json); - e = ecs_lookup_fullpath(world, "foo"); + e = ecs_lookup(world, "foo"); test_assert(e != 0); test_assert(ecs_is_alive(world, Tag)); test_assert(ecs_has(world, e, Tag)); @@ -2109,7 +2227,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_component_serializ ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); ecs_add(world, e, Position); test_assert(e != 0); @@ -2127,7 +2245,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_component_serializ test_str(r, ""); ecs_os_free(json); - e = ecs_lookup_fullpath(world, "foo"); + e = ecs_lookup(world, "foo"); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); test_assert(ecs_is_alive(world, ecs_id(Position))); @@ -2152,7 +2270,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_component_meta_ser } }); - ecs_entity_t e = ecs_new_entity(world, "foo"); + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); ecs_set(world, e, Position, {10, 20}); test_assert(e != 0); @@ -2178,7 +2296,7 @@ void DeserializeFromJson_ser_deser_new_world_1_named_entity_w_component_meta_ser test_str(r, ""); ecs_os_free(json); - e = ecs_lookup_fullpath(world, "foo"); + e = ecs_lookup(world, "foo"); test_assert(e != 0); test_assert(ecs_is_alive(world, e)); test_assert(ecs_is_alive(world, ecs_id(Position))); @@ -2205,9 +2323,9 @@ void DeserializeFromJson_ser_deser_new_world_3_entities_w_component_meta(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); @@ -2232,9 +2350,9 @@ void DeserializeFromJson_ser_deser_new_world_3_entities_w_component_meta(void) { test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); - e2 = ecs_lookup_fullpath(world, "e2"); - e3 = ecs_lookup_fullpath(world, "e3"); + e1 = ecs_lookup(world, "e1"); + e2 = ecs_lookup(world, "e2"); + e3 = ecs_lookup(world, "e3"); test_assert(e1 != 0); test_assert(e2 != 0); @@ -2286,9 +2404,9 @@ void DeserializeFromJson_ser_deser_new_world_3_entities_w_2_components_meta(void } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); @@ -2325,9 +2443,9 @@ void DeserializeFromJson_ser_deser_new_world_3_entities_w_2_components_meta(void test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); - e2 = ecs_lookup_fullpath(world, "e2"); - e3 = ecs_lookup_fullpath(world, "e3"); + e1 = ecs_lookup(world, "e1"); + e2 = ecs_lookup(world, "e2"); + e3 = ecs_lookup(world, "e3"); test_assert(e1 != 0); test_assert(e2 != 0); @@ -2393,10 +2511,10 @@ void DeserializeFromJson_ser_deser_new_world_4_entities_2_tables_w_component_met ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - ecs_entity_t e4 = ecs_new_entity(world, "e4"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); @@ -2427,10 +2545,10 @@ void DeserializeFromJson_ser_deser_new_world_4_entities_2_tables_w_component_met test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); - e2 = ecs_lookup_fullpath(world, "e2"); - e3 = ecs_lookup_fullpath(world, "e3"); - e4 = ecs_lookup_fullpath(world, "e4"); + e1 = ecs_lookup(world, "e1"); + e2 = ecs_lookup(world, "e2"); + e3 = ecs_lookup(world, "e3"); + e4 = ecs_lookup(world, "e4"); test_assert(e1 != 0); test_assert(e2 != 0); @@ -2492,15 +2610,15 @@ void DeserializeFromJson_ser_deser_new_world_component_w_anon_entity_member(void } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t a1 = ecs_new_id(world); - ecs_entity_t a2 = ecs_new_id(world); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t a1 = ecs_new(world); + ecs_entity_t a2 = ecs_new(world); ecs_set(world, e1, EntityType, {a1, a2}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_id(world, e2, a1); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_id(world, e3, a2); char *json = ecs_world_to_json(world, NULL); @@ -2522,9 +2640,9 @@ void DeserializeFromJson_ser_deser_new_world_component_w_anon_entity_member(void test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); - e2 = ecs_lookup_fullpath(world, "e2"); - e3 = ecs_lookup_fullpath(world, "e3"); + e1 = ecs_lookup(world, "e1"); + e2 = ecs_lookup(world, "e2"); + e3 = ecs_lookup(world, "e3"); test_assert(e1 != 0); test_assert(e2 != 0); @@ -2560,15 +2678,15 @@ void DeserializeFromJson_ser_deser_new_world_component_w_named_entity_member(voi } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t n1 = ecs_new_entity(world, "n1"); - ecs_entity_t n2 = ecs_new_entity(world, "n2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t n1 = ecs_entity(world, { .name = "n1" }); + ecs_entity_t n2 = ecs_entity(world, { .name = "n2" }); ecs_set(world, e1, EntityType, {n1, n2}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_id(world, e2, n1); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_id(world, e3, n2); char *json = ecs_world_to_json(world, NULL); @@ -2590,9 +2708,9 @@ void DeserializeFromJson_ser_deser_new_world_component_w_named_entity_member(voi test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); - e2 = ecs_lookup_fullpath(world, "e2"); - e3 = ecs_lookup_fullpath(world, "e3"); + e1 = ecs_lookup(world, "e1"); + e2 = ecs_lookup(world, "e2"); + e3 = ecs_lookup(world, "e3"); test_assert(e1 != 0); test_assert(e2 != 0); @@ -2628,15 +2746,15 @@ void DeserializeFromJson_ser_deser_new_world_component_w_anon_and_named_entity_m } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t n1 = ecs_new_entity(world, "n1"); - ecs_entity_t a1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t n1 = ecs_entity(world, { .name = "n1" }); + ecs_entity_t a1 = ecs_new(world); ecs_set(world, e1, EntityType, {n1, a1}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_id(world, e2, n1); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_id(world, e3, a1); char *json = ecs_world_to_json(world, NULL); @@ -2658,9 +2776,9 @@ void DeserializeFromJson_ser_deser_new_world_component_w_anon_and_named_entity_m test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); - e2 = ecs_lookup_fullpath(world, "e2"); - e3 = ecs_lookup_fullpath(world, "e3"); + e1 = ecs_lookup(world, "e1"); + e2 = ecs_lookup(world, "e2"); + e3 = ecs_lookup(world, "e3"); test_assert(e1 != 0); test_assert(e2 != 0); @@ -2687,10 +2805,10 @@ void DeserializeFromJson_ser_deser_new_world_component_w_anon_and_named_entity_m void DeserializeFromJson_ser_deser_new_world_component_w_anon_entity_with_self(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add_id(world, e1, e1); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_id(world, e2, e1); char *json = ecs_world_to_json(world, NULL); @@ -2703,7 +2821,7 @@ void DeserializeFromJson_ser_deser_new_world_component_w_anon_entity_with_self(v test_str(r, ""); ecs_os_free(json); - e2 = ecs_lookup_fullpath(world, "e2"); + e2 = ecs_lookup(world, "e2"); test_assert(e2 != 0); const ecs_type_t *type = ecs_get_type(world, e2); @@ -2721,10 +2839,10 @@ void DeserializeFromJson_ser_deser_new_world_component_w_anon_entity_with_self(v void DeserializeFromJson_ser_deser_new_world_component_w_named_entity_with_self(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add_id(world, e1, e1); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_id(world, e2, e1); char *json = ecs_world_to_json(world, NULL); @@ -2737,10 +2855,10 @@ void DeserializeFromJson_ser_deser_new_world_component_w_named_entity_with_self( test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); + e1 = ecs_lookup(world, "e1"); test_assert(e1 != 0); - e2 = ecs_lookup_fullpath(world, "e2"); + e2 = ecs_lookup(world, "e2"); test_assert(e2 != 0); test_assert(ecs_has_id(world, e2, e1)); @@ -2755,15 +2873,15 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_anon_parent(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); ecs_add(world, e1, TagA); ecs_add(world, e2, TagB); - ecs_entity_t foo = ecs_new_entity(world, "foo"); + ecs_entity_t foo = ecs_entity(world, { .name = "foo" }); ecs_add_pair(world, foo, TagA, e1); ecs_add_pair(world, foo, TagB, e2); @@ -2777,7 +2895,7 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_anon_parent(void) { test_str(r, ""); ecs_os_free(json); - foo = ecs_lookup_fullpath(world, "foo"); + foo = ecs_lookup(world, "foo"); test_assert(foo != 0); e1 = ecs_get_target(world, foo, TagA, 0); @@ -2804,9 +2922,9 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_named_parent(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t parent = ecs_new_entity(world, "parent"); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); ecs_add(world, e1, TagA); @@ -2822,15 +2940,15 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_named_parent(void) { test_str(r, ""); ecs_os_free(json); - parent = ecs_lookup_fullpath(world, "parent"); + parent = ecs_lookup(world, "parent"); test_assert(parent != 0); test_str(ecs_get_name(world, parent), "parent"); - e1 = ecs_lookup_fullpath(world, "parent.e1"); + e1 = ecs_lookup(world, "parent.e1"); test_assert(e1 != 0); test_str(ecs_get_name(world, e1), "e1"); - e2 = ecs_lookup_fullpath(world, "parent.e2"); + e2 = ecs_lookup(world, "parent.e2"); test_assert(e2 != 0); test_str(ecs_get_name(world, e2), "e2"); @@ -2846,15 +2964,15 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_anon_parent_w_cycle(vo ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); ecs_add(world, e1, TagA); ecs_add(world, e2, TagB); - ecs_entity_t foo = ecs_new_entity(world, "foo"); + ecs_entity_t foo = ecs_entity(world, { .name = "foo" }); ecs_add_pair(world, foo, TagA, e1); ecs_add_pair(world, foo, TagB, e2); @@ -2871,7 +2989,7 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_anon_parent_w_cycle(vo test_str(r, ""); ecs_os_free(json); - foo = ecs_lookup_fullpath(world, "foo"); + foo = ecs_lookup(world, "foo"); test_assert(foo != 0); e1 = ecs_get_target(world, foo, TagA, 0); @@ -2902,9 +3020,9 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_named_parent_w_cycle(v ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t parent = ecs_new_entity(world, "parent"); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); ecs_add(world, e1, TagA); @@ -2923,15 +3041,15 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_named_parent_w_cycle(v test_str(r, ""); ecs_os_free(json); - parent = ecs_lookup_fullpath(world, "parent"); + parent = ecs_lookup(world, "parent"); test_assert(parent != 0); test_str(ecs_get_name(world, parent), "parent"); - e1 = ecs_lookup_fullpath(world, "parent.e1"); + e1 = ecs_lookup(world, "parent.e1"); test_assert(e1 != 0); test_str(ecs_get_name(world, e1), "e1"); - e2 = ecs_lookup_fullpath(world, "parent.e2"); + e2 = ecs_lookup(world, "parent.e2"); test_assert(e2 != 0); test_str(ecs_get_name(world, e2), "e2"); @@ -2944,6 +3062,81 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_named_parent_w_cycle(v ecs_fini(world); } +void DeserializeFromJson_ser_deser_new_world_w_prefab(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t b = ecs_new_w(world, Foo); + test_assert(b != 0); + ecs_add_id(world, b, EcsPrefab); + ecs_entity_t e = ecs_new_w(world, Bar); + test_assert(e != 0); + ecs_add_pair(world, e, EcsIsA, b); + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + ECS_TAG_DEFINE(world, Foo); /* pin tags */ + ECS_TAG_DEFINE(world, Bar); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + test_assert(ecs_is_alive(world, Foo)); + test_assert(ecs_is_alive(world, Bar)); + test_assert(ecs_is_alive(world, b)); + test_assert(ecs_is_alive(world, e)); + + test_assert(ecs_has(world, b, Foo)); + test_assert(ecs_has_id(world, b, EcsPrefab)); + test_assert(ecs_has(world, e, Foo)); + test_assert(ecs_has(world, e, Bar)); + test_assert(ecs_has_pair(world, e, EcsIsA, b)); + test_str(ecs_get_name(world, Foo), "Foo"); + test_str(ecs_get_name(world, Bar), "Bar"); + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_new_world_w_disabled(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t b = ecs_new_w(world, Foo); + test_assert(b != 0); + ecs_enable(world, b, false); + test_assert(ecs_has_id(world, b, EcsDisabled)); + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + ECS_TAG_DEFINE(world, Foo); /* pin tags */ + ECS_TAG_DEFINE(world, Bar); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + test_assert(ecs_is_alive(world, Foo)); + test_assert(ecs_is_alive(world, Bar)); + test_assert(ecs_is_alive(world, b)); + + test_assert(ecs_has(world, b, Foo)); + test_assert(ecs_has_id(world, b, EcsDisabled)); + test_str(ecs_get_name(world, Foo), "Foo"); + + ecs_fini(world); +} + void DeserializeFromJson_ser_deser_restore_1_entity_to_empty_table(void) { ecs_world_t *world = ecs_init(); @@ -2958,13 +3151,13 @@ void DeserializeFromJson_ser_deser_restore_1_entity_to_empty_table(void) { ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); char *json = ecs_world_to_json(world, NULL); test_assert(json != NULL); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_set(world, e2, Position, {30, 40}); ecs_set(world, e2, Velocity, {3, 4}); ecs_set(world, e1, Velocity, {1, 2}); @@ -2973,8 +3166,8 @@ void DeserializeFromJson_ser_deser_restore_1_entity_to_empty_table(void) { test_str(r, ""); ecs_os_free(json); - test_assert(e1 == ecs_lookup_fullpath(world, "e1")); - test_assert(e2 == ecs_lookup_fullpath(world, "e2")); + test_assert(e1 == ecs_lookup(world, "e1")); + test_assert(e2 == ecs_lookup(world, "e2")); test_assert(ecs_has(world, e1, Position)); test_assert(ecs_has(world, e2, Position)); @@ -3024,11 +3217,11 @@ void DeserializeFromJson_ser_deser_restore_1_entity_to_non_empty_table(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e1, Velocity, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_set(world, e2, Position, {30, 40}); ecs_set(world, e2, Velocity, {3, 4}); @@ -3041,8 +3234,8 @@ void DeserializeFromJson_ser_deser_restore_1_entity_to_non_empty_table(void) { test_str(r, ""); ecs_os_free(json); - test_assert(e1 == ecs_lookup_fullpath(world, "e1")); - test_assert(e2 == ecs_lookup_fullpath(world, "e2")); + test_assert(e1 == ecs_lookup(world, "e1")); + test_assert(e2 == ecs_lookup(world, "e2")); test_assert(ecs_has(world, e1, Position)); test_assert(ecs_has(world, e2, Position)); @@ -3091,13 +3284,13 @@ void DeserializeFromJson_ser_deser_restore_1_anon_entity_to_empty_table(void) { ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Position, {10, 20}); char *json = ecs_world_to_json(world, NULL); test_assert(json != NULL); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Position, {30, 40}); ecs_set(world, e2, Velocity, {3, 4}); ecs_set(world, e1, Velocity, {1, 2}); @@ -3157,11 +3350,11 @@ void DeserializeFromJson_ser_deser_restore_1_anon_entity_to_non_empty_table(void } }); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e1, Velocity, {1, 2}); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Position, {30, 40}); ecs_set(world, e2, Velocity, {3, 4}); @@ -3222,7 +3415,7 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_entity_to_empty_table(void) } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); char *json = ecs_world_to_json(world, NULL); @@ -3234,7 +3427,7 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_entity_to_empty_table(void) test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); + e1 = ecs_lookup(world, "e1"); test_assert(e1 != 0); test_assert(ecs_has(world, e1, Position)); @@ -3258,10 +3451,10 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_entity_to_non_empty_table(v } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_set(world, e2, Position, {30, 40}); char *json = ecs_world_to_json(world, NULL); @@ -3274,7 +3467,7 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_entity_to_non_empty_table(v ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); + e1 = ecs_lookup(world, "e1"); test_assert(e1 != 0); test_assert(ecs_has(world, e1, Position)); @@ -3309,7 +3502,7 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_anon_entity_to_empty_table( } }); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Position, {10, 20}); char *json = ecs_world_to_json(world, NULL); @@ -3322,7 +3515,7 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_anon_entity_to_empty_table( test_str(r, ""); ecs_os_free(json); - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ .id = ecs_id(Position) }); + ecs_iter_t it = ecs_each(world, Position); e1 = ecs_iter_first(&it); test_assert(e1 != 0); @@ -3349,10 +3542,10 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_anon_entity_to_non_empty_ta } }); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Position, {30, 40}); char *json = ecs_world_to_json(world, NULL); @@ -3365,15 +3558,15 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_anon_entity_to_non_empty_ta test_str(r, ""); ecs_os_free(json); - ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ .id = ecs_id(Position) }); - test_assert(ecs_term_next(&it) == true); + ecs_iter_t it = ecs_each(world, Position); + test_assert(ecs_each_next(&it) == true); test_int(it.count, 2); if (it.entities[0] == e2) { e1 = it.entities[1]; } else { e1 = it.entities[0]; } - test_assert(ecs_term_next(&it) == false); + test_assert(ecs_each_next(&it) == false); test_assert(ecs_has(world, e1, Position)); test_assert(ecs_is_alive(world, e2)); @@ -3409,10 +3602,10 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_w_ref(void) { ECS_TAG(world, TagA); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_set(world, e2, Position, {30, 40}); ecs_add_pair(world, e2, TagA, e1); @@ -3427,7 +3620,7 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_w_ref(void) { test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); + e1 = ecs_lookup(world, "e1"); test_assert(e1 != 0); test_assert(ecs_get_target(world, e2, TagA, 0) == e1); @@ -3468,10 +3661,10 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_anon_w_ref(void) { ECS_TAG(world, TagA); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Position, {30, 40}); ecs_add_pair(world, e2, TagA, e1); @@ -3522,10 +3715,10 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_w_cycle_ref(void) { ECS_TAG(world, TagA); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_set(world, e2, Position, {30, 40}); ecs_add_pair(world, e1, TagA, e2); @@ -3542,7 +3735,7 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_w_cycle_ref(void) { test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); + e1 = ecs_lookup(world, "e1"); test_assert(e1 != 0); test_assert(ecs_get_target(world, e2, TagA, 0) == e1); test_assert(ecs_get_target(world, e1, TagA, 0) == e2); @@ -3584,10 +3777,10 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_anon_w_cycle_ref(void) { ECS_TAG(world, TagA); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Position, {30, 40}); ecs_add_pair(world, e1, TagA, e2); @@ -3604,6 +3797,11 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_anon_w_cycle_ref(void) { test_str(r, ""); ecs_os_free(json); + ecs_entity_t tagA = ecs_lookup(world, "TagA"); + + test_assert(tagA != 0); + test_assert(tagA == TagA); + e1 = ecs_get_target(world, e2, TagA, 0); test_assert(e1 != 0); test_assert(ecs_get_target(world, e1, TagA, 0) == e2); @@ -3642,10 +3840,10 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_w_recycled(void) { ECS_TAG(world, TagA); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_set(world, e2, Position, {30, 40}); ecs_add_pair(world, e1, TagA, e2); @@ -3658,14 +3856,14 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_w_recycled(void) { test_assert(!ecs_is_alive(world, e1)); test_assert(ecs_get_target(world, e2, TagA, 0) == 0); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e3, Position, {50, 60}); const char *r = ecs_world_from_json(world, json, NULL); test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); + e1 = ecs_lookup(world, "e1"); test_assert(e1 != 0); test_assert(e1 != e3); test_assert(ecs_get_target(world, e2, TagA, 0) == e1); @@ -3718,10 +3916,10 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_anon_w_recycled(void) { ECS_TAG(world, TagA); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Position, {30, 40}); ecs_add_pair(world, e1, TagA, e2); @@ -3734,7 +3932,7 @@ void DeserializeFromJson_ser_deser_restore_1_deleted_anon_w_recycled(void) { test_assert(!ecs_is_alive(world, e1)); test_assert(ecs_get_target(world, e2, TagA, 0) == 0); - ecs_entity_t e3 = ecs_new_id(world); + ecs_entity_t e3 = ecs_new(world); ecs_set(world, e3, Position, {50, 60}); const char *r = ecs_world_from_json(world, json, NULL); @@ -3796,15 +3994,15 @@ void DeserializeFromJson_ser_deser_on_set_3_entities(void) { int32_t count = 0; ecs_observer(world, { - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = { EcsOnSet }, .callback = OnSet_count, .ctx = &count }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); ecs_set(world, e3, Position, {50, 60}); @@ -3839,14 +4037,14 @@ void DeserializeFromJson_ser_deser_on_set_3_entities_2_restored(void) { int32_t count = 0; ecs_observer(world, { - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = { EcsOnSet }, .callback = OnSet_count, .ctx = &count }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); @@ -3856,7 +4054,7 @@ void DeserializeFromJson_ser_deser_on_set_3_entities_2_restored(void) { char *json = ecs_world_to_json(world, NULL); test_assert(json != NULL); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e3, Position, {50, 60}); test_int(count, 1); @@ -3886,13 +4084,13 @@ void DeserializeFromJson_ser_deser_on_set_3_entities_1_restored(void) { int32_t count = 0; ecs_observer(world, { - .filter.terms[0].id = ecs_id(Position), + .query.terms[0].id = ecs_id(Position), .events = { EcsOnSet }, .callback = OnSet_count, .ctx = &count }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); test_int(count, 1); @@ -3901,8 +4099,8 @@ void DeserializeFromJson_ser_deser_on_set_3_entities_1_restored(void) { char *json = ecs_world_to_json(world, NULL); test_assert(json != NULL); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e2, Position, {30, 40}); ecs_set(world, e3, Position, {50, 60}); @@ -3930,9 +4128,9 @@ void DeserializeFromJson_ser_deser_3_entities_after_remove_all(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); ecs_set(world, e3, Position, {50, 60}); @@ -3952,9 +4150,9 @@ void DeserializeFromJson_ser_deser_3_entities_after_remove_all(void) { test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); - e2 = ecs_lookup_fullpath(world, "e2"); - e3 = ecs_lookup_fullpath(world, "e3"); + e1 = ecs_lookup(world, "e1"); + e2 = ecs_lookup(world, "e2"); + e3 = ecs_lookup(world, "e3"); test_assert(ecs_is_alive(world, e1)); test_assert(ecs_is_alive(world, e2)); @@ -3997,9 +4195,9 @@ void DeserializeFromJson_ser_deser_3_entities_after_delete_with(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); ecs_set(world, e3, Position, {50, 60}); @@ -4016,9 +4214,9 @@ void DeserializeFromJson_ser_deser_3_entities_after_delete_with(void) { test_str(r, ""); ecs_os_free(json); - e1 = ecs_lookup_fullpath(world, "e1"); - e2 = ecs_lookup_fullpath(world, "e2"); - e3 = ecs_lookup_fullpath(world, "e3"); + e1 = ecs_lookup(world, "e1"); + e2 = ecs_lookup(world, "e2"); + e3 = ecs_lookup(world, "e3"); test_assert(ecs_is_alive(world, e1)); test_assert(ecs_is_alive(world, e2)); @@ -4091,16 +4289,16 @@ void DeserializeFromJson_ser_deser_w_hooks(void) { }); ecs_set_hooks(world, StringType, { - .ctor = ecs_default_ctor, + .ctor = flecs_default_ctor, .dtor = ecs_dtor(StringType), .move = ecs_move(StringType), .copy = ecs_copy(StringType) }); - ecs_entity_t e1 = ecs_set(world, 0, StringType, {"foo"}); - ecs_entity_t e2 = ecs_set(world, 0, StringType, {"bar"}); - ecs_entity_t e3 = ecs_set(world, 0, StringType, {"hello"}); - ecs_entity_t e4 = ecs_set(world, 0, StringType, {"world"}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(StringType, {"foo"})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(StringType, {"bar"})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(StringType, {"hello"})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(StringType, {"world"})); ecs_set_name(world, e1, "e1"); ecs_set_name(world, e2, "e2"); @@ -4119,10 +4317,10 @@ void DeserializeFromJson_ser_deser_w_hooks(void) { test_str(r, ""); ecs_os_free(json); - test_assert(ecs_lookup_fullpath(world, "e1") == e1); - test_assert(ecs_lookup_fullpath(world, "e2") == e2); - test_assert(ecs_lookup_fullpath(world, "e3") == e3); - e4 = ecs_lookup_fullpath(world, "e4"); + test_assert(ecs_lookup(world, "e1") == e1); + test_assert(ecs_lookup(world, "e2") == e2); + test_assert(ecs_lookup(world, "e3") == e3); + e4 = ecs_lookup(world, "e4"); test_assert(e4 != 0); test_assert(ecs_is_alive(world, e1)); @@ -4182,7 +4380,7 @@ void DeserializeFromJson_ser_deser_large_data(void) { .v = long_str }; - char *long_str_ser = ecs_asprintf("{\"v\":\"%s\"}", long_str); + char *long_str_ser = flecs_asprintf("{\"v\":\"%s\"}", long_str); char *json = ecs_ptr_to_json(world, t, &value); test_assert(json != NULL); test_str(json, long_str_ser); @@ -4200,3 +4398,1731 @@ void DeserializeFromJson_ser_deser_large_data(void) { ecs_fini(world); } + +void DeserializeFromJson_ser_deser_different_component_order(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {11, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_set(world, e1, Mass, {100}); + ecs_add(world, e1, Foo); + + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_set(world, e2, Position, {12, 20}); + ecs_set(world, e2, Velocity, {2, 2}); + ecs_set(world, e2, Mass, {200}); + ecs_add(world, e2, Foo); + + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_set(world, e3, Position, {13, 20}); + ecs_set(world, e3, Mass, {300}); + ecs_add(world, e3, Foo); + + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); + ecs_set(world, e4, Position, {14, 20}); + ecs_set(world, e4, Mass, {400}); + ecs_add(world, e4, Foo); + + ecs_entity_t e5 = ecs_entity(world, { .name = "e5" }); + ecs_set(world, e5, Velocity, {5, 2}); + ecs_set(world, e5, Mass, {500}); + ecs_add(world, e5, Foo); + + ecs_entity_t e6 = ecs_entity(world, { .name = "e6" }); + ecs_set(world, e6, Velocity, {6, 2}); + ecs_set(world, e6, Mass, {600}); + ecs_add(world, e6, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + test_assert(ecs_is_alive(world, ecs_id(Position))); + test_assert(ecs_is_alive(world, ecs_id(Velocity))); + test_assert(ecs_is_alive(world, ecs_id(Mass))); + + test_str(ecs_get_name(world, ecs_id(Position)), "Position"); + test_str(ecs_get_name(world, ecs_id(Velocity)), "Velocity"); + test_str(ecs_get_name(world, ecs_id(Mass)), "Mass"); + + ecs_entity_t e1 = ecs_lookup(world, "e1"); + ecs_entity_t e2 = ecs_lookup(world, "e2"); + ecs_entity_t e3 = ecs_lookup(world, "e3"); + ecs_entity_t e4 = ecs_lookup(world, "e4"); + ecs_entity_t e5 = ecs_lookup(world, "e5"); + ecs_entity_t e6 = ecs_lookup(world, "e6"); + + test_assert(ecs_is_alive(world, e1)); + test_assert(ecs_is_alive(world, e2)); + test_assert(ecs_is_alive(world, e3)); + test_assert(ecs_is_alive(world, e4)); + test_assert(ecs_is_alive(world, e5)); + test_assert(ecs_is_alive(world, e6)); + + test_str(ecs_get_name(world, e1), "e1"); + test_str(ecs_get_name(world, e2), "e2"); + test_str(ecs_get_name(world, e3), "e3"); + test_str(ecs_get_name(world, e4), "e4"); + test_str(ecs_get_name(world, e5), "e5"); + test_str(ecs_get_name(world, e6), "e6"); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e2, Position)); + test_assert(ecs_has(world, e3, Position)); + test_assert(ecs_has(world, e4, Position)); + + test_assert(ecs_has(world, e1, Velocity)); + test_assert(ecs_has(world, e2, Velocity)); + test_assert(ecs_has(world, e5, Velocity)); + test_assert(ecs_has(world, e6, Velocity)); + + test_assert(ecs_has(world, e1, Mass)); + test_assert(ecs_has(world, e2, Mass)); + test_assert(ecs_has(world, e3, Mass)); + test_assert(ecs_has(world, e4, Mass)); + test_assert(ecs_has(world, e5, Mass)); + test_assert(ecs_has(world, e6, Mass)); + + { + const Position *p = ecs_get(world, e1, Position); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e2, Position); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e3, Position); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e4, Position); + test_assert(p != NULL); + test_int(p->x, 14); + test_int(p->y, 20); + } + + { + const Velocity *v = ecs_get(world, e1, Velocity); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + { + const Velocity *v = ecs_get(world, e2, Velocity); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 2); + } + { + const Velocity *v = ecs_get(world, e5, Velocity); + test_assert(v != NULL); + test_int(v->x, 5); + test_int(v->y, 2); + } + { + const Velocity *v = ecs_get(world, e6, Velocity); + test_assert(v != NULL); + test_int(v->x, 6); + test_int(v->y, 2); + } + + { + const Mass *m = ecs_get(world, e1, Mass); + test_assert(m != NULL); + test_int(m->value, 100); + } + { + const Mass *m = ecs_get(world, e2, Mass); + test_assert(m != NULL); + test_int(m->value, 200); + } + { + const Mass *m = ecs_get(world, e3, Mass); + test_assert(m != NULL); + test_int(m->value, 300); + } + { + const Mass *m = ecs_get(world, e4, Mass); + test_assert(m != NULL); + test_int(m->value, 400); + } + { + const Mass *m = ecs_get(world, e5, Mass); + test_assert(m != NULL); + test_int(m->value, 500); + } + { + const Mass *m = ecs_get(world, e6, Mass); + test_assert(m != NULL); + test_int(m->value, 600); + } + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_no_reflection_data(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {11, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_set(world, e1, Mass, {100}); + ecs_add(world, e1, Foo); + + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_set(world, e2, Position, {12, 20}); + ecs_set(world, e2, Velocity, {2, 2}); + ecs_set(world, e2, Mass, {200}); + ecs_add(world, e2, Foo); + + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_set(world, e3, Position, {13, 20}); + ecs_set(world, e3, Mass, {300}); + ecs_add(world, e3, Foo); + + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); + ecs_set(world, e4, Position, {14, 20}); + ecs_set(world, e4, Mass, {400}); + ecs_add(world, e4, Foo); + + ecs_entity_t e5 = ecs_entity(world, { .name = "e5" }); + ecs_set(world, e5, Velocity, {5, 2}); + ecs_set(world, e5, Mass, {500}); + ecs_add(world, e5, Foo); + + ecs_entity_t e6 = ecs_entity(world, { .name = "e6" }); + ecs_set(world, e6, Velocity, {6, 2}); + ecs_set(world, e6, Mass, {600}); + ecs_add(world, e6, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_log_set_level(-4); + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + test_assert(ecs_is_alive(world, ecs_id(Position))); + test_assert(ecs_is_alive(world, ecs_id(Velocity))); + test_assert(ecs_is_alive(world, ecs_id(Mass))); + + test_str(ecs_get_name(world, ecs_id(Position)), "Position"); + test_str(ecs_get_name(world, ecs_id(Velocity)), "Velocity"); + test_str(ecs_get_name(world, ecs_id(Mass)), "Mass"); + + ecs_entity_t e1 = ecs_lookup(world, "e1"); + ecs_entity_t e2 = ecs_lookup(world, "e2"); + ecs_entity_t e3 = ecs_lookup(world, "e3"); + ecs_entity_t e4 = ecs_lookup(world, "e4"); + ecs_entity_t e5 = ecs_lookup(world, "e5"); + ecs_entity_t e6 = ecs_lookup(world, "e6"); + + test_assert(ecs_is_alive(world, e1)); + test_assert(ecs_is_alive(world, e2)); + test_assert(ecs_is_alive(world, e3)); + test_assert(ecs_is_alive(world, e4)); + test_assert(ecs_is_alive(world, e5)); + test_assert(ecs_is_alive(world, e6)); + + test_str(ecs_get_name(world, e1), "e1"); + test_str(ecs_get_name(world, e2), "e2"); + test_str(ecs_get_name(world, e3), "e3"); + test_str(ecs_get_name(world, e4), "e4"); + test_str(ecs_get_name(world, e5), "e5"); + test_str(ecs_get_name(world, e6), "e6"); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e2, Position)); + test_assert(ecs_has(world, e3, Position)); + test_assert(ecs_has(world, e4, Position)); + + test_assert(ecs_has(world, e1, Velocity)); + test_assert(ecs_has(world, e2, Velocity)); + test_assert(ecs_has(world, e5, Velocity)); + test_assert(ecs_has(world, e6, Velocity)); + + test_assert(ecs_has(world, e1, Mass)); + test_assert(ecs_has(world, e2, Mass)); + test_assert(ecs_has(world, e3, Mass)); + test_assert(ecs_has(world, e4, Mass)); + test_assert(ecs_has(world, e5, Mass)); + test_assert(ecs_has(world, e6, Mass)); + + { + const Position *p = ecs_get(world, e1, Position); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e2, Position); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e3, Position); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e4, Position); + test_assert(p != NULL); + test_int(p->x, 14); + test_int(p->y, 20); + } + + { + const Mass *m = ecs_get(world, e1, Mass); + test_assert(m != NULL); + test_int(m->value, 100); + } + { + const Mass *m = ecs_get(world, e2, Mass); + test_assert(m != NULL); + test_int(m->value, 200); + } + { + const Mass *m = ecs_get(world, e3, Mass); + test_assert(m != NULL); + test_int(m->value, 300); + } + { + const Mass *m = ecs_get(world, e4, Mass); + test_assert(m != NULL); + test_int(m->value, 400); + } + { + const Mass *m = ecs_get(world, e5, Mass); + test_assert(m != NULL); + test_int(m->value, 500); + } + { + const Mass *m = ecs_get(world, e6, Mass); + test_assert(m != NULL); + test_int(m->value, 600); + } + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_no_reflection_data_strict(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {11, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_set(world, e1, Mass, {100}); + ecs_add(world, e1, Foo); + + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_set(world, e2, Position, {12, 20}); + ecs_set(world, e2, Velocity, {2, 2}); + ecs_set(world, e2, Mass, {200}); + ecs_add(world, e2, Foo); + + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_set(world, e3, Position, {13, 20}); + ecs_set(world, e3, Mass, {300}); + ecs_add(world, e3, Foo); + + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); + ecs_set(world, e4, Position, {14, 20}); + ecs_set(world, e4, Mass, {400}); + ecs_add(world, e4, Foo); + + ecs_entity_t e5 = ecs_entity(world, { .name = "e5" }); + ecs_set(world, e5, Velocity, {5, 2}); + ecs_set(world, e5, Mass, {500}); + ecs_add(world, e5, Foo); + + ecs_entity_t e6 = ecs_entity(world, { .name = "e6" }); + ecs_set(world, e6, Velocity, {6, 2}); + ecs_set(world, e6, Mass, {600}); + ecs_add(world, e6, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_log_set_level(-4); + ecs_from_json_desc_t desc = { + .strict = true + }; + const char *r = ecs_world_from_json(world, json, &desc); + test_assert(r == NULL); + ecs_os_free(json); + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_value_for_tag(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {11, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_set(world, e1, Mass, {100}); + ecs_add(world, e1, Foo); + + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_set(world, e2, Position, {12, 20}); + ecs_set(world, e2, Velocity, {2, 2}); + ecs_set(world, e2, Mass, {200}); + ecs_add(world, e2, Foo); + + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_set(world, e3, Position, {13, 20}); + ecs_set(world, e3, Mass, {300}); + ecs_add(world, e3, Foo); + + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); + ecs_set(world, e4, Position, {14, 20}); + ecs_set(world, e4, Mass, {400}); + ecs_add(world, e4, Foo); + + ecs_entity_t e5 = ecs_entity(world, { .name = "e5" }); + ecs_set(world, e5, Velocity, {5, 2}); + ecs_set(world, e5, Mass, {500}); + ecs_add(world, e5, Foo); + + ecs_entity_t e6 = ecs_entity(world, { .name = "e6" }); + ecs_set(world, e6, Velocity, {6, 2}); + ecs_set(world, e6, Mass, {600}); + ecs_add(world, e6, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_TAG(world, Velocity); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_log_set_level(-4); + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + test_assert(ecs_is_alive(world, ecs_id(Position))); + test_assert(ecs_is_alive(world, ecs_id(Velocity))); + test_assert(ecs_is_alive(world, ecs_id(Mass))); + + test_str(ecs_get_name(world, ecs_id(Position)), "Position"); + test_str(ecs_get_name(world, ecs_id(Velocity)), "Velocity"); + test_str(ecs_get_name(world, ecs_id(Mass)), "Mass"); + + ecs_entity_t e1 = ecs_lookup(world, "e1"); + ecs_entity_t e2 = ecs_lookup(world, "e2"); + ecs_entity_t e3 = ecs_lookup(world, "e3"); + ecs_entity_t e4 = ecs_lookup(world, "e4"); + ecs_entity_t e5 = ecs_lookup(world, "e5"); + ecs_entity_t e6 = ecs_lookup(world, "e6"); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + test_assert(e4 != 0); + test_assert(e5 != 0); + test_assert(e6 != 0); + + test_assert(ecs_is_alive(world, e1)); + test_assert(ecs_is_alive(world, e2)); + test_assert(ecs_is_alive(world, e3)); + test_assert(ecs_is_alive(world, e4)); + test_assert(ecs_is_alive(world, e5)); + test_assert(ecs_is_alive(world, e6)); + + test_str(ecs_get_name(world, e1), "e1"); + test_str(ecs_get_name(world, e2), "e2"); + test_str(ecs_get_name(world, e3), "e3"); + test_str(ecs_get_name(world, e4), "e4"); + test_str(ecs_get_name(world, e5), "e5"); + test_str(ecs_get_name(world, e6), "e6"); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e2, Position)); + test_assert(ecs_has(world, e3, Position)); + test_assert(ecs_has(world, e4, Position)); + + test_assert(ecs_has(world, e1, Velocity)); + test_assert(ecs_has(world, e2, Velocity)); + test_assert(ecs_has(world, e5, Velocity)); + test_assert(ecs_has(world, e6, Velocity)); + + test_assert(ecs_has(world, e1, Mass)); + test_assert(ecs_has(world, e2, Mass)); + test_assert(ecs_has(world, e3, Mass)); + test_assert(ecs_has(world, e4, Mass)); + test_assert(ecs_has(world, e5, Mass)); + test_assert(ecs_has(world, e6, Mass)); + + { + const Position *p = ecs_get(world, e1, Position); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e2, Position); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e3, Position); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e4, Position); + test_assert(p != NULL); + test_int(p->x, 14); + test_int(p->y, 20); + } + + { + const Mass *m = ecs_get(world, e1, Mass); + test_assert(m != NULL); + test_int(m->value, 100); + } + { + const Mass *m = ecs_get(world, e2, Mass); + test_assert(m != NULL); + test_int(m->value, 200); + } + { + const Mass *m = ecs_get(world, e3, Mass); + test_assert(m != NULL); + test_int(m->value, 300); + } + { + const Mass *m = ecs_get(world, e4, Mass); + test_assert(m != NULL); + test_int(m->value, 400); + } + { + const Mass *m = ecs_get(world, e5, Mass); + test_assert(m != NULL); + test_int(m->value, 500); + } + { + const Mass *m = ecs_get(world, e6, Mass); + test_assert(m != NULL); + test_int(m->value, 600); + } + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_value_for_tag_strict(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {11, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_set(world, e1, Mass, {100}); + ecs_add(world, e1, Foo); + + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_set(world, e2, Position, {12, 20}); + ecs_set(world, e2, Velocity, {2, 2}); + ecs_set(world, e2, Mass, {200}); + ecs_add(world, e2, Foo); + + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_set(world, e3, Position, {13, 20}); + ecs_set(world, e3, Mass, {300}); + ecs_add(world, e3, Foo); + + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); + ecs_set(world, e4, Position, {14, 20}); + ecs_set(world, e4, Mass, {400}); + ecs_add(world, e4, Foo); + + ecs_entity_t e5 = ecs_entity(world, { .name = "e5" }); + ecs_set(world, e5, Velocity, {5, 2}); + ecs_set(world, e5, Mass, {500}); + ecs_add(world, e5, Foo); + + ecs_entity_t e6 = ecs_entity(world, { .name = "e6" }); + ecs_set(world, e6, Velocity, {6, 2}); + ecs_set(world, e6, Mass, {600}); + ecs_add(world, e6, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_TAG(world, Velocity); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_log_set_level(-4); + ecs_from_json_desc_t desc = { + .strict = true + }; + const char *r = ecs_world_from_json(world, json, &desc); + test_assert(r == NULL); + ecs_os_free(json); + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_value_for_non_existing(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {11, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_set(world, e1, Mass, {100}); + ecs_add(world, e1, Foo); + + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_set(world, e2, Position, {12, 20}); + ecs_set(world, e2, Velocity, {2, 2}); + ecs_set(world, e2, Mass, {200}); + ecs_add(world, e2, Foo); + + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_set(world, e3, Position, {13, 20}); + ecs_set(world, e3, Mass, {300}); + ecs_add(world, e3, Foo); + + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); + ecs_set(world, e4, Position, {14, 20}); + ecs_set(world, e4, Mass, {400}); + ecs_add(world, e4, Foo); + + ecs_entity_t e5 = ecs_entity(world, { .name = "e5" }); + ecs_set(world, e5, Velocity, {5, 2}); + ecs_set(world, e5, Mass, {500}); + ecs_add(world, e5, Foo); + + ecs_entity_t e6 = ecs_entity(world, { .name = "e6" }); + ecs_set(world, e6, Velocity, {6, 2}); + ecs_set(world, e6, Mass, {600}); + ecs_add(world, e6, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_log_set_level(-4); + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + ecs_entity_t ecs_id(Velocity) = ecs_lookup( + world, "module.Velocity"); + test_assert(ecs_id(Velocity) != 0); + + test_assert(ecs_is_alive(world, ecs_id(Position))); + test_assert(ecs_is_alive(world, ecs_id(Velocity))); + test_assert(ecs_is_alive(world, ecs_id(Mass))); + + test_str(ecs_get_name(world, ecs_id(Position)), "Position"); + test_str(ecs_get_name(world, ecs_id(Velocity)), "Velocity"); + test_str(ecs_get_name(world, ecs_id(Mass)), "Mass"); + + ecs_entity_t e1 = ecs_lookup(world, "e1"); + ecs_entity_t e2 = ecs_lookup(world, "e2"); + ecs_entity_t e3 = ecs_lookup(world, "e3"); + ecs_entity_t e4 = ecs_lookup(world, "e4"); + ecs_entity_t e5 = ecs_lookup(world, "e5"); + ecs_entity_t e6 = ecs_lookup(world, "e6"); + + test_assert(ecs_is_alive(world, e1)); + test_assert(ecs_is_alive(world, e2)); + test_assert(ecs_is_alive(world, e3)); + test_assert(ecs_is_alive(world, e4)); + test_assert(ecs_is_alive(world, e5)); + test_assert(ecs_is_alive(world, e6)); + + test_str(ecs_get_name(world, e1), "e1"); + test_str(ecs_get_name(world, e2), "e2"); + test_str(ecs_get_name(world, e3), "e3"); + test_str(ecs_get_name(world, e4), "e4"); + test_str(ecs_get_name(world, e5), "e5"); + test_str(ecs_get_name(world, e6), "e6"); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e2, Position)); + test_assert(ecs_has(world, e3, Position)); + test_assert(ecs_has(world, e4, Position)); + + test_assert(ecs_has(world, e1, Velocity)); + test_assert(ecs_has(world, e2, Velocity)); + test_assert(ecs_has(world, e5, Velocity)); + test_assert(ecs_has(world, e6, Velocity)); + + test_assert(ecs_has(world, e1, Mass)); + test_assert(ecs_has(world, e2, Mass)); + test_assert(ecs_has(world, e3, Mass)); + test_assert(ecs_has(world, e4, Mass)); + test_assert(ecs_has(world, e5, Mass)); + test_assert(ecs_has(world, e6, Mass)); + + { + const Position *p = ecs_get(world, e1, Position); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e2, Position); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e3, Position); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 20); + } + { + const Position *p = ecs_get(world, e4, Position); + test_assert(p != NULL); + test_int(p->x, 14); + test_int(p->y, 20); + } + + { + const Mass *m = ecs_get(world, e1, Mass); + test_assert(m != NULL); + test_int(m->value, 100); + } + { + const Mass *m = ecs_get(world, e2, Mass); + test_assert(m != NULL); + test_int(m->value, 200); + } + { + const Mass *m = ecs_get(world, e3, Mass); + test_assert(m != NULL); + test_int(m->value, 300); + } + { + const Mass *m = ecs_get(world, e4, Mass); + test_assert(m != NULL); + test_int(m->value, 400); + } + { + const Mass *m = ecs_get(world, e5, Mass); + test_assert(m != NULL); + test_int(m->value, 500); + } + { + const Mass *m = ecs_get(world, e6, Mass); + test_assert(m != NULL); + test_int(m->value, 600); + } + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_value_for_non_existing_strict(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Velocity); + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {11, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_set(world, e1, Mass, {100}); + ecs_add(world, e1, Foo); + + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_set(world, e2, Position, {12, 20}); + ecs_set(world, e2, Velocity, {2, 2}); + ecs_set(world, e2, Mass, {200}); + ecs_add(world, e2, Foo); + + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_set(world, e3, Position, {13, 20}); + ecs_set(world, e3, Mass, {300}); + ecs_add(world, e3, Foo); + + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); + ecs_set(world, e4, Position, {14, 20}); + ecs_set(world, e4, Mass, {400}); + ecs_add(world, e4, Foo); + + ecs_entity_t e5 = ecs_entity(world, { .name = "e5" }); + ecs_set(world, e5, Velocity, {5, 2}); + ecs_set(world, e5, Mass, {500}); + ecs_add(world, e5, Foo); + + ecs_entity_t e6 = ecs_entity(world, { .name = "e6" }); + ecs_set(world, e6, Velocity, {6, 2}); + ecs_set(world, e6, Mass, {600}); + ecs_add(world, e6, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ecs_entity_t module = ecs_entity(world, { .name = "module" }); + ecs_add_id(world, module, EcsModule); + ecs_set_scope(world, module); + + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_set_scope(world, 0); + + ecs_log_set_level(-4); + ecs_from_json_desc_t desc = { + .strict = true + }; + const char *r = ecs_world_from_json(world, json, &desc); + test_assert(r == NULL); + ecs_os_free(json); + } + + ecs_fini(world); +} + +static +void ser_deser_cpp_name_test( + const char *typename) +{ + ecs_world_t *world = ecs_init(); + + { + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ecs_entity_t t = ecs_new_low_id(world); + ecs_set_name(world, t, typename); + test_assert(t != 0); + test_str(ecs_get_name(world, t), typename); + + ecs_entity_t child = ecs_entity(world, { .name = "c" }); + ecs_add_pair(world, child, EcsChildOf, t); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Mass, {100}); + ecs_add_id(world, e, t); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ECS_COMPONENT(world, Mass); + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + {"value", ecs_id(ecs_i32_t)}, + } + }); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + ecs_entity_t t = ecs_lookup(world, typename); + test_assert(t != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + ecs_entity_t c = ecs_lookup_child(world, t, "c"); + test_assert(c != 0); + + test_assert(ecs_has_id(world, e, t)); + + { + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Mass *m = ecs_get(world, e, Mass); + test_assert(m != NULL); + test_int(m->value, 100); + } + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_cpp_typename(void) { + ser_deser_cpp_name_test("std::vector"); +} + +void DeserializeFromJson_ser_deser_cpp_template(void) { + ser_deser_cpp_name_test("std::vector<>"); +} + +void DeserializeFromJson_ser_deser_cpp_template_1_param(void) { + ser_deser_cpp_name_test("std::vector"); +} + +void DeserializeFromJson_ser_deser_cpp_template_n_params(void) { + ser_deser_cpp_name_test("std::vector"); +} + +void DeserializeFromJson_ser_deser_cpp_template_nested(void) { + ser_deser_cpp_name_test("std::vector >"); +} + +void DeserializeFromJson_ser_deser_cpp_template_n_params_nested(void) { + ser_deser_cpp_name_test("std::vector, std::vector >"); +} + +void DeserializeFromJson_ser_deser_long_name(void) { + ser_deser_cpp_name_test( + "std::vector::anothertype, " + "std::allocator::anothertype> >"); +} + +void DeserializeFromJson_ser_deser_long_name_256_chars(void) { + ser_deser_cpp_name_test( + "abcdefghij1234567890abcdefghij1234567890" + "abcdefghij1234567890abcdefghij1234567890" + "abcdefghij1234567890abcdefghij1234567890" + "abcdefghij1234567890abcdefghij1234567890" + "abcdefghij1234567890abcdefghij1234567890" + "abcdefghij1234567890abcdefghij1234567890" + "abcdefghij123456"); +} + +void DeserializeFromJson_ser_deser_w_alerts(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_IMPORT(world, FlecsAlerts); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t alert = ecs_alert(world, { + .entity = ecs_entity(world, { .name = "module.my_alert" }), + .query.expr = "Foo, !Bar" + }); + test_assert(alert != 0); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add(world, e, Foo); + + ecs_progress(world, 2.0); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ECS_IMPORT(world, FlecsAlerts); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_assert(ecs_has(world, e, Foo)); + test_assert(!ecs_has(world, e, Bar)); + + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_struct(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_COMPONENT(world, Position); + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + ecs_entity_t t = ecs_lookup(world, "Position"); + test_assert(t != 0); + + const EcsComponent *c = ecs_get(world, t, EcsComponent); + test_assert(c != NULL); + test_int(c->size, 8); + test_int(c->alignment, 4); + + ecs_entity_t x = ecs_lookup_child(world, t, "x"); + test_assert(x != 0); + ecs_entity_t y = ecs_lookup_child(world, t, "y"); + test_assert(y != 0); + + test_assert(ecs_has(world, x, EcsMember)); + test_assert(ecs_has(world, y, EcsMember)); + + { + const EcsMember *m = ecs_get(world, x, EcsMember); + test_assert(m != NULL); + test_int(m->offset, 0); + test_uint(m->type, ecs_id(ecs_i32_t)); + } + { + const EcsMember *m = ecs_get(world, y, EcsMember); + test_assert(m != NULL); + test_int(m->offset, 4); + test_uint(m->type, ecs_id(ecs_i32_t)); + } + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_w_alerts_w_progress(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_IMPORT(world, FlecsAlerts); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t alert = ecs_alert(world, { + .entity = ecs_entity(world, { .name = "module.my_alert" }), + .query.expr = "Foo, !Bar" + }); + test_assert(alert != 0); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add(world, e, Foo); + + ecs_progress(world, 2.0); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ECS_IMPORT(world, FlecsAlerts); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_assert(ecs_has(world, e, Foo)); + test_assert(!ecs_has(world, e, Bar)); + } + + ecs_progress(world, 2.0); + + test_assert(true); + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_anon_w_same_id_as_existing_named(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = 2000; + ecs_make_alive(world, e); + ecs_add(world, e, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ECS_IMPORT(world, FlecsAlerts); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = 2000; + ecs_make_alive(world, e); + ecs_set_name(world, e, "e"); + ecs_add(world, e, Bar); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + test_assert(!ecs_has(world, e, Foo)); + test_assert(ecs_has(world, e, Bar)); + + test_int(ecs_count(world, Foo), 1); + } + + ecs_fini(world); +} + +void DeserializeFromJson_deser_invalid_entity_name(void) { + char *json = "{\"results\":[" + "{\"ids\":[[\"flecs.core.Identifier\",\"flecs.core.Name\"]], \"entities\":[\"Foo\"], \"values\":[0]}, " + "{\"ids\":[[\"Foo\"]], \"entities\":[\"1e4\", 20000]}]}"; + + ecs_world_t *world = ecs_init(); + + { + ecs_log_set_level(-4); + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, NULL); + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_named_to_different_table(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add(world, e, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ECS_TAG(world, Foo); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + test_assert(ecs_has(world, e, Foo)); + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_named_child_to_different_table(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Foo); + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add_pair(world, e, EcsChildOf, p); + ecs_add(world, e, Foo); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ECS_TAG(world, Foo); + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add_pair(world, e, EcsChildOf, p); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + test_assert(ecs_has(world, e, Foo)); + test_assert(ecs_has_pair(world, e, EcsChildOf, p)); + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_with_child_tgt(void) { + ecs_world_t *world = ecs_init(); + + { + ECS_TAG(world, Rel); + ECS_TAG(world, Foo); + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add_pair(world, e, EcsChildOf, p); + ecs_add(world, e, Foo); + + ecs_entity_t c = ecs_entity(world, { .name = "c" }); + ecs_add_pair(world, c, EcsChildOf, e); + ecs_add_pair(world, e, Rel, c); + } + + char *json = ecs_world_to_json(world, NULL); + test_assert(json != NULL); + + ecs_fini(world); + world = ecs_init(); + + { + ECS_TAG(world, Rel); + ECS_TAG(world, Foo); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + ecs_os_free(json); + + ecs_entity_t p = ecs_lookup(world, "p"); + test_assert(p != 0); + ecs_entity_t e = ecs_lookup(world, "p.e"); + test_assert(e != 0); + ecs_entity_t c = ecs_lookup(world, "p.e.c"); + test_assert(c != 0); + + test_assert(ecs_has(world, e, Foo)); + test_assert(ecs_has_pair(world, e, EcsChildOf, p)); + test_assert(ecs_has_pair(world, e, Rel, c)); + + test_assert(ecs_has_pair(world, c, EcsChildOf, e)); + } + + ecs_fini(world); +} + +void DeserializeFromJson_ser_deser_with_child_tgt_no_child(void) { + ecs_world_t *world = ecs_init(); + + char *json = "{\"results\": [{\"parent\": \"p\", \"name\": \"e\", \"id\": 1000, \"tags\": [\"Foo\"], \"pairs\": {\"flecs.core.ChildOf\": \"p\", \"Rel\": \"p.e.c\"}, \"components\": {\"(flecs.core.Identifier,flecs.core.Name)\": null } } ] }"; + + ecs_fini(world); + world = ecs_init(); + + { + ECS_TAG(world, Rel); + ECS_TAG(world, Foo); + + const char *r = ecs_world_from_json(world, json, NULL); + test_str(r, ""); + + ecs_entity_t p = ecs_lookup(world, "p"); + test_assert(p != 0); + ecs_entity_t e = ecs_lookup(world, "p.e"); + test_assert(e != 0); + ecs_entity_t c = ecs_lookup(world, "p.e.c"); + test_assert(c != 0); + + test_assert(ecs_has(world, e, Foo)); + test_assert(ecs_has_pair(world, e, EcsChildOf, p)); + test_assert(ecs_has_pair(world, e, Rel, c)); + + test_assert(ecs_has_pair(world, c, EcsChildOf, e)); + } + + ecs_fini(world); +} diff --git a/vendors/flecs/test/meta/src/EnumTypes.c b/vendors/flecs/test/meta/src/EnumTypes.c index 7128fb42a..86650d934 100644 --- a/vendors/flecs/test/meta/src/EnumTypes.c +++ b/vendors/flecs/test/meta/src/EnumTypes.c @@ -11,7 +11,7 @@ void meta_test_enum( test_int(ct->size, ECS_SIZEOF(ecs_i32_t)); test_int(ct->alignment, ECS_ALIGNOF(ecs_i32_t)); - const EcsMetaType *mt = ecs_get(world, t, EcsMetaType); + const EcsType *mt = ecs_get(world, t, EcsType); test_assert(mt != NULL); test_assert(mt->kind == EcsEnumType); @@ -48,7 +48,7 @@ void meta_test_constant( if (!ecs_os_strcmp(c->name, name)) { test_int(c->value, value); - const ecs_i32_t *vptr = ecs_get_pair_object(world, c->constant, + const ecs_i32_t *vptr = ecs_get_pair_second(world, c->constant, EcsConstant, ecs_i32_t); if (vptr) { test_int(*vptr, value); @@ -183,7 +183,7 @@ void EnumTypes_zero_initialized(void) { test_assert(e != 0); test_assert(ecs_has(world, e, EcsComponent)); - ecs_entity_t ent = ecs_new_id(world); + ecs_entity_t ent = ecs_new(world); ecs_add_id(world, ent, e); const int32_t *ptr = ecs_get_id(world, ent, e); @@ -209,7 +209,7 @@ void EnumTypes_enum_relation(void) { test_assert(e != 0); test_assert(ecs_has(world, e, EcsComponent)); test_assert(ecs_has_id(world, e, EcsExclusive)); - test_assert(ecs_has_id(world, e, EcsTag)); + test_assert(ecs_has_id(world, e, EcsPairIsTag)); ecs_entity_t red = ecs_lookup_child(world, e, "Red"); ecs_entity_t green = ecs_lookup_child(world, e, "Green"); @@ -221,7 +221,7 @@ void EnumTypes_enum_relation(void) { test_assert(ecs_get_typeid(world, ecs_pair(e, red)) == 0); - ecs_entity_t ent = ecs_new_id(world); + ecs_entity_t ent = ecs_new(world); ecs_add_pair(world, ent, e, red); test_assert( ecs_has_pair(world, ent, e, red)); @@ -263,3 +263,51 @@ void EnumTypes_constant_w_type_prefix(void) { void EnumTypes_constant_w_name_type_prefix(void) { // Implement testcase } + + +int enum_modified_calls = 0; + +static +void enum_modified(ecs_iter_t *it) { + enum_modified_calls ++; +} + +/* Checks that observers watching enum changes are notified */ +void EnumTypes_enum_modified_event(void) { + ecs_world_t *world = ecs_init(); + + ecs_observer(world, { + .query.terms[0] = { .id = ecs_id(EcsEnum) }, + .events = {EcsOnSet}, + .callback = enum_modified + }); + + ecs_entity_t e = ecs_enum_init(world, &(ecs_enum_desc_t){ + .constants = { + {"Red"}, {"Blue"} + } + }); + + test_assert(e != 0); + /* must receive two calls, one for each enum member added */ + test_int(enum_modified_calls, 2); + + /* run-time add a new member constant to the enum: */ + ecs_entity_t old_scope = ecs_set_scope(world, e); + ecs_entity_t c = ecs_entity(world, { + .name = "Orange" + }); + ecs_add_id(world, c, EcsConstant); + ecs_set_scope(world, old_scope); + + /* check if observer was called after adding */ + /* a new member constant */ + test_int(enum_modified_calls, 3); + + meta_test_enum(world, e, 3); + meta_test_constant(world, e, "Red", 0); + meta_test_constant(world, e, "Blue", 1); + meta_test_constant(world, e, "Orange", 2); + + ecs_fini(world); +} \ No newline at end of file diff --git a/vendors/flecs/test/meta/src/MetaUtils.c b/vendors/flecs/test/meta/src/MetaUtils.c index 301783db4..ddfd31c1d 100644 --- a/vendors/flecs/test/meta/src/MetaUtils.c +++ b/vendors/flecs/test/meta/src/MetaUtils.c @@ -290,7 +290,7 @@ void MetaUtils_struct_w_3_entities(void) { Struct_3_entities v = {0, EcsFlecs, EcsFlecsCore}; char *expr = ecs_ptr_to_expr(world, ecs_id(Struct_3_entities), &v); test_assert(expr != NULL); - test_str(expr, "{one: 0, two: flecs, three: flecs.core}"); + test_str(expr, "{one: #0, two: flecs, three: flecs.core}"); ecs_os_free(expr); ecs_fini(world); diff --git a/vendors/flecs/test/meta/src/Misc.c b/vendors/flecs/test/meta/src/Misc.c index a2c85aabc..7e8cc605a 100644 --- a/vendors/flecs/test/meta/src/Misc.c +++ b/vendors/flecs/test/meta/src/Misc.c @@ -331,7 +331,7 @@ void Misc_quantity_from_defer(void) { void Misc_primitive_from_readonly(void) { ecs_world_t *world = ecs_init(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_primitive(world, { .kind = EcsI32 }); test_assert(t != 0); @@ -345,7 +345,7 @@ void Misc_primitive_from_readonly(void) { void Misc_enum_from_readonly(void) { ecs_world_t *world = ecs_init(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_enum(world, { .constants = { @@ -363,7 +363,7 @@ void Misc_enum_from_readonly(void) { void Misc_bitmask_from_readonly(void) { ecs_world_t *world = ecs_init(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_bitmask(world, { .constants = { @@ -381,7 +381,7 @@ void Misc_bitmask_from_readonly(void) { void Misc_array_from_readonly(void) { ecs_world_t *world = ecs_init(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_array(world, { .type = ecs_id(ecs_i32_t), @@ -398,7 +398,7 @@ void Misc_array_from_readonly(void) { void Misc_vector_from_readonly(void) { ecs_world_t *world = ecs_init(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_vector(world, { .type = ecs_id(ecs_i32_t) @@ -414,7 +414,7 @@ void Misc_vector_from_readonly(void) { void Misc_struct_from_readonly(void) { ecs_world_t *world = ecs_init(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_struct(world, { .members = { {"x", ecs_id(ecs_i32_t)}, @@ -433,7 +433,7 @@ void Misc_opaque_from_readonly(void) { ECS_COMPONENT(world, Position); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_opaque(world, { .entity = ecs_id(Position), @@ -450,7 +450,7 @@ void Misc_opaque_from_readonly(void) { void Misc_unit_from_readonly(void) { ecs_world_t *world = ecs_init(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_unit(world, { .symbol = "f" @@ -466,7 +466,7 @@ void Misc_unit_from_readonly(void) { void Misc_unit_prefix_from_readonly(void) { ecs_world_t *world = ecs_init(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_unit_prefix(world, { .symbol = "f" @@ -482,7 +482,7 @@ void Misc_unit_prefix_from_readonly(void) { void Misc_quantity_from_readonly(void) { ecs_world_t *world = ecs_init(); - ecs_readonly_begin(world); + ecs_readonly_begin(world, false); ecs_entity_t t = ecs_quantity(world, { .name = "q" diff --git a/vendors/flecs/test/meta/src/OpaqueTypes.c b/vendors/flecs/test/meta/src/OpaqueTypes.c index 9e39c924e..f5808172f 100644 --- a/vendors/flecs/test/meta/src/OpaqueTypes.c +++ b/vendors/flecs/test/meta/src/OpaqueTypes.c @@ -523,8 +523,8 @@ void OpaqueTypes_deser_entity_from_json(void) { .type.assign_entity = Opaque_entity_set }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); Opaque_entity v = { 0 }; { @@ -597,8 +597,8 @@ void OpaqueTypes_ser_deser_entity(void) { .type.assign_entity = Entity_assign }); - ecs_entity_t e1 = ecs_new_entity(world, "ent1"); - ecs_entity_t e2 = ecs_new_entity(world, "ent2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "ent1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "ent2" }); Entity v = { e1 }; char *json = ecs_ptr_to_json(world, ecs_id(Entity), &v); @@ -628,7 +628,7 @@ void OpaqueTypes_ser_deser_0_entity(void) { Entity v = { 0 }; char *json = ecs_ptr_to_json(world, ecs_id(Entity), &v); test_assert(json != NULL); - test_str(json, "0"); + test_str(json, "\"#0\""); const char *r = ecs_ptr_from_json(world, ecs_id(Entity), &v, json, NULL); test_str(r, ""); @@ -637,3 +637,51 @@ void OpaqueTypes_ser_deser_0_entity(void) { ecs_fini(world); } + + +void OpaqueTypes_const_string(void) { + + typedef struct test_string_struct { + const char *value; + } test_string_struct; + + const char *test_string = "Const String. Don't try to free me!"; + + ecs_world_t *world = ecs_init(); + + // Lookup const string type: + ecs_entity_t const_string = ecs_lookup(world, "flecs.core.const_string_t"); + + // Create a reflection interface for `test_struct`: + ecs_entity_t test_struct = ecs_struct(world, { + .entity = ecs_entity(world, { + .name = "test_struct", .root_sep = ""}), + .members = { + { + .name = "value", + .type = const_string }, + } + }); + + ecs_entity_t e = ecs_entity(world, {.name = "MyEntity"}); + test_string_struct *st = (test_string_struct *) ecs_ensure_id(world, e, test_struct); + st->value = test_string; + + // we should be able to retrieve the string with a cursor: + ecs_meta_cursor_t cursor = ecs_meta_cursor(world, test_struct, st); + ecs_meta_push(&cursor); + ecs_meta_member(&cursor, "value"); + + const char *retrieved_string = ecs_meta_get_string(&cursor); + + test_assert(!ecs_os_strcmp(retrieved_string, test_string)); + + // When deleting the entity, Flecs must not try to free the above const string, + // otherwise the below will segfault: + ecs_delete(world, e); + + test_assert(true); // If we get to this point without a crash, we're good. + + ecs_fini(world); + +} diff --git a/vendors/flecs/test/meta/src/PrimitiveTypes.c b/vendors/flecs/test/meta/src/PrimitiveTypes.c index 26e4c51a6..747260db9 100644 --- a/vendors/flecs/test/meta/src/PrimitiveTypes.c +++ b/vendors/flecs/test/meta/src/PrimitiveTypes.c @@ -12,7 +12,7 @@ static void meta_test_primitive( test_assert(ct->size == size); test_assert(ct->alignment == alignment); - const EcsMetaType *mt = ecs_get(world, t, EcsMetaType); + const EcsType *mt = ecs_get(world, t, EcsType); test_assert(mt != NULL); test_assert(mt->kind == EcsPrimitiveType); @@ -51,7 +51,7 @@ meta_test_type(EcsId, id) void PrimitiveTypes_bool(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsBool}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsBool})); test_assert(t != 0); meta_test_bool(world, t); @@ -62,7 +62,7 @@ void PrimitiveTypes_bool(void) { void PrimitiveTypes_byte(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsByte}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsByte})); test_assert(t != 0); meta_test_byte(world, t); @@ -73,7 +73,7 @@ void PrimitiveTypes_byte(void) { void PrimitiveTypes_char(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsChar}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsChar})); test_assert(t != 0); meta_test_char(world, t); @@ -84,7 +84,7 @@ void PrimitiveTypes_char(void) { void PrimitiveTypes_i8(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsI8}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsI8})); test_assert(t != 0); meta_test_i8(world, t); @@ -95,7 +95,7 @@ void PrimitiveTypes_i8(void) { void PrimitiveTypes_i16(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsI16}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsI16})); test_assert(t != 0); meta_test_i16(world, t); @@ -106,7 +106,7 @@ void PrimitiveTypes_i16(void) { void PrimitiveTypes_i32(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsI32}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsI32})); test_assert(t != 0); meta_test_i32(world, t); @@ -117,7 +117,7 @@ void PrimitiveTypes_i32(void) { void PrimitiveTypes_i64(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsI64}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsI64})); test_assert(t != 0); meta_test_i64(world, t); @@ -128,7 +128,7 @@ void PrimitiveTypes_i64(void) { void PrimitiveTypes_iptr(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsIPtr}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsIPtr})); test_assert(t != 0); meta_test_iptr(world, t); @@ -139,7 +139,7 @@ void PrimitiveTypes_iptr(void) { void PrimitiveTypes_u8(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsU8}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsU8})); test_assert(t != 0); meta_test_u8(world, t); @@ -150,7 +150,7 @@ void PrimitiveTypes_u8(void) { void PrimitiveTypes_u16(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsU16}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsU16})); test_assert(t != 0); meta_test_u16(world, t); @@ -161,7 +161,7 @@ void PrimitiveTypes_u16(void) { void PrimitiveTypes_u32(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsU32}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsU32})); test_assert(t != 0); meta_test_u32(world, t); @@ -172,7 +172,7 @@ void PrimitiveTypes_u32(void) { void PrimitiveTypes_u64(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsU64}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsU64})); test_assert(t != 0); meta_test_u64(world, t); @@ -183,7 +183,7 @@ void PrimitiveTypes_u64(void) { void PrimitiveTypes_uptr(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsUPtr}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsUPtr})); test_assert(t != 0); meta_test_uptr(world, t); @@ -194,7 +194,7 @@ void PrimitiveTypes_uptr(void) { void PrimitiveTypes_float(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsF32}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsF32})); test_assert(t != 0); meta_test_f32(world, t); @@ -205,7 +205,7 @@ void PrimitiveTypes_float(void) { void PrimitiveTypes_double(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsF64}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsF64})); test_assert(t != 0); meta_test_f64(world, t); @@ -216,7 +216,7 @@ void PrimitiveTypes_double(void) { void PrimitiveTypes_string(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsString}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsString})); test_assert(t != 0); meta_test_string(world, t); @@ -227,7 +227,7 @@ void PrimitiveTypes_string(void) { void PrimitiveTypes_entity(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsEntity}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsEntity})); test_assert(t != 0); meta_test_entity(world, t); @@ -238,7 +238,7 @@ void PrimitiveTypes_entity(void) { void PrimitiveTypes_id(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_set(world, 0, EcsPrimitive, {.kind = EcsId}); + ecs_entity_t t = ecs_insert(world, ecs_value(EcsPrimitive, {.kind = EcsId})); test_assert(t != 0); meta_test_id(world, t); @@ -505,7 +505,7 @@ void PrimitiveTypes_primitive_init(void) { test_assert(prim_ptr != NULL); test_int(prim_ptr->kind, EcsU16); - const EcsMetaType *type_ptr = ecs_get(world, t, EcsMetaType); + const EcsType *type_ptr = ecs_get(world, t, EcsType); test_assert(type_ptr != NULL); test_int(type_ptr->kind, EcsPrimitiveType); @@ -527,10 +527,9 @@ void PrimitiveTypes_primitive_w_short_notation(void) { test_assert(prim_ptr != NULL); test_int(prim_ptr->kind, EcsU16); - const EcsMetaType *type_ptr = ecs_get(world, t, EcsMetaType); + const EcsType *type_ptr = ecs_get(world, t, EcsType); test_assert(type_ptr != NULL); test_int(type_ptr->kind, EcsPrimitiveType); ecs_fini(world); } - diff --git a/vendors/flecs/test/meta/src/RuntimeTypes.c b/vendors/flecs/test/meta/src/RuntimeTypes.c new file mode 100644 index 000000000..b06b2e057 --- /dev/null +++ b/vendors/flecs/test/meta/src/RuntimeTypes.c @@ -0,0 +1,3153 @@ +#include +#include + +#define UINT32_PATTERN(x) \ + (((uint32_t) (x)) + ((uint32_t) (x) << 8) + ((uint32_t) (x) << 16) + \ + ((uint32_t) (x) << 24)) + +static +bool is_memory_filled_with( + const void *ptr, + size_t size, + unsigned char byte) +{ + const unsigned char *byte_ptr = (const unsigned char *)ptr; + + for (size_t i = 0; i < size; i++) { + if (byte_ptr[i] != byte) { + return false; + } + } + + return true; +} + +static +bool memory_is_zero( + const void *ptr, + size_t size) +{ + return is_memory_filled_with(ptr, size, 0); +} + +#define test_memory_zero(ptr, size) test_assert(memory_is_zero((ptr), (size))) + +/* Test hooks for runtime trivial structs + These structs have no special copy/move/dtor logic and are zero-initialized + Trivial structs should get flecs default constructor and nothing else */ +void RuntimeTypes_trivial_struct(void) { + ecs_world_t *world = ecs_init(); + + /* Create a component to use as subtype in the trivial struct + Create a new component: */ + ecs_entity_t subtype = ecs_entity(world, {.name = "Some Subtype"}); + + /* Configure as a struct: */ + ecs_struct(world, { + .entity = subtype, + .members = { + {.name = "x", .type = ecs_id(ecs_i32_t)}, + {.name = "r", .type = ecs_id(ecs_f32_t)} + } + }); + + /* Create a new component: */ + ecs_entity_t trivial_struct = ecs_entity(world, {.name = "Trivial Struct"}); + + ecs_struct(world, { + .entity = trivial_struct, + .members = { + {.name = "a", .type = ecs_id(ecs_i32_t)}, + {.name = "b", .type = ecs_id(ecs_f32_t)}, + {.name = "s", .type = subtype} + } + }); + + const ecs_type_info_t *ti = ecs_get_type_info(world, trivial_struct); + + test_assert(ti->hooks.copy == NULL); + test_assert(ti->hooks.move == NULL); + test_assert(ti->hooks.dtor == NULL); + + /* Check if the ctor initialized everything to 0. + Do this plenty of times to make sure we don't get a zero-initialized + struct out of sheer luck: */ + for (int i = 0; i < 100; i++) { + ecs_entity_t e = ecs_new(world); + ecs_add_id(world, e, trivial_struct); + const void *data = ecs_get_id(world, e, trivial_struct); + test_assert(is_memory_filled_with(data, ti->size, 0)); + if (i % 3 == 0) + ecs_delete(world, e); + } + + ecs_fini(world); +} + +static uint8_t ctor_pattern = 0xAF; +static int ctor_calls = 0; +static uintptr_t ctor_ptr = 0; +static int32_t ctor_count = 0; +static ecs_entity_t ctor_component = 0; + +static +void test_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + ctor_calls++; + ctor_ptr = (uintptr_t) ptr; + ctor_count = count; + ctor_component = type_info->component; + ecs_os_memset(ptr, ctor_pattern, count * type_info->size); +} + +static int dtor_calls = 0; +static uintptr_t dtor_ptr = 0; +static int32_t dtor_count = 0; +static ecs_entity_t dtor_component = 0; + +static +void test_dtor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + dtor_calls++; + dtor_ptr = (uintptr_t) ptr; + dtor_count = count; + dtor_component = type_info->component; + ecs_os_memset(ptr, 0xDE, count * type_info->size); +} + +static int move_calls = 0; +static uintptr_t move_dst_ptr = 0; +static uintptr_t move_src_ptr = 0; +static int32_t move_count = 0; +static ecs_entity_t move_component = 0; + +static +void test_move( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + move_calls++; + move_dst_ptr = (uintptr_t) dst_ptr; + move_src_ptr = (uintptr_t) src_ptr; + move_count = count; + move_component = type_info->component; + ecs_os_memcpy(dst_ptr, src_ptr, count * type_info->size); + ecs_os_memset(src_ptr, 0xBC, count * type_info->size); +} + +static int copy_calls = 0; +static uintptr_t copy_dst_ptr = 0; +static uintptr_t copy_src_ptr = 0; +static int32_t copy_count = 0; +static ecs_entity_t copy_component = 0; + +static +void test_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + copy_calls++; + copy_dst_ptr = (uintptr_t) dst_ptr; + copy_src_ptr = (uintptr_t) src_ptr; + copy_count = count; + copy_component = type_info->component; + ecs_os_memcpy(dst_ptr, src_ptr, count * type_info->size); +} + +/* Define a nested struct that will have one or more hooks depending on the test + */ +typedef struct NestedStruct { + uint32_t a; + uint32_t b; +} NestedStruct; + +static ecs_entity_t nested_struct; + +static +const ecs_type_info_t *define_nested_struct( + ecs_world_t *world) +{ + nested_struct = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "NestedStruct"}), + .members = { + { .name = "a", .type = ecs_id(ecs_u32_t)}, + { .name = "b", .type = ecs_id(ecs_u32_t)} + } + }); + + return ecs_get_type_info(world, nested_struct); +} + +/* Define a test struct that will have automatically generated hooks. */ +typedef struct TestStruct { + uint32_t x; + NestedStruct s1; + uint32_t y; + NestedStruct s2; +} TestStruct; + +static ecs_entity_t test_struct; + +static +const ecs_type_info_t *define_test_struct( + ecs_world_t *world) +{ + test_struct = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "TestStruct"}), + .members = { + {.name = "x", .type = ecs_id(ecs_u32_t)}, + {.name = "s1", .type = nested_struct}, + {.name = "y", .type = ecs_id(ecs_u32_t)}, + {.name = "s2", .type = nested_struct} + } + }); + + return ecs_get_type_info(world, test_struct); +} + +/* Tests that a constructor is generated for a struct if at least a member has + * itself a constructor Also tests if the generated constructor works. */ +void RuntimeTypes_ctor(void) { + ecs_world_t *world = ecs_init(); + + /* Define NestedStruct: */ + const ecs_type_info_t *nested_struct_ti = define_nested_struct(world); + + ecs_type_hooks_t hooks = nested_struct_ti->hooks; + hooks.ctor = test_ctor; /* only define a constructor for "NestedStruct" */ + ecs_set_hooks_id(world, nested_struct, &hooks); + + /* Define TestStruct, which has two "NestedStruct" members. + * A constructor should be automatically generated for TestStruct to invoke + * NestedStruct's specific constructor: */ + const ecs_type_info_t *test_struct_ti = define_test_struct(world); + + /* TestStruct should only have a constructor: */ + test_assert(test_struct_ti->hooks.ctor != NULL); + + /* No other hooks should've been set: */ + test_assert(test_struct_ti->hooks.dtor == NULL); + test_assert(test_struct_ti->hooks.move == NULL); + test_assert(test_struct_ti->hooks.copy == NULL); + + /* Now instantiate TestStruct and see the constructor for NestedStruct is + * called twice, since TestStruct contains two NestedStructs: */ + ecs_entity_t e = ecs_new(world); + + test_int(0, ctor_calls); + ecs_add_id(world, e, test_struct); + test_int(2, ctor_calls); /* called twice because there are two "nested_struct" */ + + const TestStruct *ss = (TestStruct *) ecs_get_id(world, e, test_struct); + + uint32_t test_pattern = UINT32_PATTERN(ctor_pattern); + test_int(0, ss->x); /* trivial field was zero-initialized */ + test_int(0, ss->y); + test_int(test_pattern, ss->s1.a); /* these should've been filled by + test_ctor with a pattern. */ + test_int(test_pattern, ss->s1.b); + test_int(test_pattern, ss->s2.a); + test_int(test_pattern, ss->s2.b); + + ecs_fini(world); +} + +/* Tests that a destructor is generated for a struct if at least a member has + * itself a destructor Also tests if the generated destructor works. */ +void RuntimeTypes_dtor(void) { + ecs_world_t *world = ecs_init(); + + /* Define NestedStruct: */ + const ecs_type_info_t *nested_struct_ti = define_nested_struct(world); + + ecs_type_hooks_t hooks = nested_struct_ti->hooks; + hooks.dtor = test_dtor; /* only set a destructor */ + + ecs_set_hooks_id(world, nested_struct, &hooks); + + /* Define TestStruct, which has two "NestedStruct" members. + * A destructor should be automatically generated for TestStruct to invoke + * NestedStruct's specific destructor: */ + const ecs_type_info_t *test_struct_ti = define_test_struct(world); + + /* TestStruct should have a default constructor and the generated + * destructor: */ + test_assert(test_struct_ti->hooks.ctor != NULL); + test_assert(test_struct_ti->hooks.dtor != NULL); + + /* No other hooks should've been set: */ + test_assert(test_struct_ti->hooks.move == NULL); + test_assert(test_struct_ti->hooks.copy == NULL); + + /* Now instantiate TestStruct to test its destructor. */ + ecs_entity_t e = ecs_new(world); + + test_int(0, dtor_calls); + ecs_add_id(world, e, test_struct); + test_int(0, dtor_calls); /* Destructor not called yet. */ + + const void *data = ecs_get_id(world, e, test_struct); + is_memory_filled_with( + data, test_struct_ti->size, + 0); /* should be zero-initialized, since there are no special ctors. */ + + ecs_remove_id(world, e, test_struct); + test_int(2, dtor_calls); /* should be called twice, since there are two + "struct_with_dtor" */ + + ecs_fini(world); +} + +/* Tests that a move hook is generated for a struct if at least a member has + * itself a move hook Also tests if the generated move hook works. */ +void RuntimeTypes_move(void) { + ecs_world_t *world = ecs_init(); + define_nested_struct(world); + + /* Define NestedStruct: */ + const ecs_type_info_t *nested_struct_ti = define_nested_struct(world); + ecs_type_hooks_t hooks = nested_struct_ti->hooks; + + hooks.move = test_move; /* Only define a move hook. */ + ecs_set_hooks_id(world, nested_struct, &hooks); + + /* Define TestStruct, which has two "NestedStruct" members. + * A move hook should be automatically generated for TestStruct to invoke + * NestedStruct's specific move hook: */ + const ecs_type_info_t *test_struct_ti = define_test_struct(world); + + /* TestStruct should have a default constructor and the generated move hook: */ + test_assert(test_struct_ti->hooks.ctor != NULL); + test_assert(test_struct_ti->hooks.move != NULL); + + /* No other hooks should've been set: */ + test_assert(test_struct_ti->hooks.dtor == NULL); + test_assert(test_struct_ti->hooks.copy == NULL); + + ecs_entity_t e = ecs_new(world); + + test_int(0, move_calls); + void *ptr = ecs_ensure_id(world, e, test_struct); + test_int(0, move_calls); + + /* use a cursor to set the x and s1.a field to specific numbers */ + ecs_meta_cursor_t cur = ecs_meta_cursor(world, test_struct, ptr); + ecs_meta_push(&cur); + ecs_meta_member(&cur, "x"); + ecs_meta_set_int(&cur, 82); + ecs_meta_member(&cur, "s1"); + ecs_meta_push(&cur); + ecs_meta_member(&cur, "a"); + ecs_meta_set_int(&cur, 79); + + /* now trigger a move by adding another component to e, forcing an archetype + * change: */ + ecs_add_id(world, e, ecs_new(world)); + test_int(2, move_calls); /* should be called twice, since there are two + structs with a move hook. */ + + const TestStruct *ss = (TestStruct *) ecs_get_id(world, e, test_struct); + + test_assert((void *) ss != + ptr); /* should be a different memory location. */ + + test_int(82, ss->x); /* trivial move should have copied this value. */ + test_int(0, ss->y); + test_int(79, ss->s1.a); /* these should contain the specified value, done by + the move hook. */ + test_int(0, ss->s1.b); /* other fields should have been 0-initialized. */ + test_int(0, ss->s2.a); + test_int(0, ss->s2.b); + + ecs_fini(world); +} + +/* Tests that a copy hook is generated for a struct if at least a member has + * itself a copy hook Also tests if the generated copy hook works. */ +void RuntimeTypes_copy(void) { + ecs_world_t *world = ecs_init(); + + /* Define NestedStruct: */ + define_nested_struct(world); + const ecs_type_info_t *nested_struct_ti = define_nested_struct(world); + ecs_type_hooks_t hooks = nested_struct_ti->hooks; + + hooks.copy = test_copy; /* only set a copy hook */ + ecs_set_hooks_id(world, nested_struct, &hooks); + + /* Define TestStruct, which has two "NestedStruct" members. + * A copy hook should be automatically generated for TestStruct to invoke + * NestedStruct's specific copy hook: */ + const ecs_type_info_t *test_struct_ti = define_test_struct(world); + + /* TestStruct should have a default constructor and the generated copy hook: */ + test_assert(test_struct_ti->hooks.ctor != NULL); + test_assert(test_struct_ti->hooks.copy != NULL); + + /* No other hooks should've been set: */ + test_assert(test_struct_ti->hooks.dtor == NULL); + test_assert(test_struct_ti->hooks.move == NULL); + + /* create a prefab entity: */ + ecs_entity_t prefab = ecs_new(world); + ecs_add_id(world, prefab, EcsPrefab); + + test_int(0, copy_calls); + void *original_ptr = ecs_ensure_id(world, prefab, test_struct); + test_int(0, copy_calls); + + /* use a cursor to set the x and s1.a field to specific numbers */ + ecs_meta_cursor_t cur = ecs_meta_cursor(world, test_struct, original_ptr); + ecs_meta_push(&cur); + ecs_meta_member(&cur, "x"); + ecs_meta_set_int(&cur, 82); + ecs_meta_member(&cur, "s1"); + ecs_meta_push(&cur); + ecs_meta_member(&cur, "a"); + ecs_meta_set_int(&cur, 79); + + /* now trigger a copy by instantiating this prefab: */ + ecs_entity_t instance = ecs_new_w_pair(world, EcsIsA, prefab); + test_int(2, copy_calls); /* should be called twice, since there are two + structs with a copy hook. */ + + const void *copied_ptr = ecs_get_id(world, instance, test_struct); + + test_assert((void *) copied_ptr != + original_ptr); /* should be a different memory location. */ + + test_assert(0 == + ecs_os_memcmp(original_ptr, copied_ptr, test_struct_ti->size)); + + ecs_fini(world); +} + +/* For the following tests, we model a "ResourceHandle" that must allocate an id + * from a pool of resource ids. When constructing a handle, it must get a unique + * id. When destroying a handle, it must return the id. When copying a handle, + * it must copy the payload value and get a new unique id for the handle. When + * moving the handle, it should keep the same id and payload value. Two Resource + * Handles are considered "equal" if both have a valid id (id>0) and their + * payload value is the same + */ +typedef struct ResourceHandle { + int id; + uint32_t value; +} ResourceHandle; + +/* This vector will hold all available ids. */ +static ecs_vec_t resource_ids; + +/* Initializes the available "resource ids" */ +static +void initialize_resource_ids( + int num_resources) +{ + ecs_vec_init_t(NULL, &resource_ids, int, num_resources); + ecs_vec_set_count_t(NULL, &resource_ids, int, num_resources); + for (int i = 0; i < num_resources; i++) { + *ecs_vec_get_t(&resource_ids, int, i) = num_resources - i; + } +} + +static +void free_resource_ids() { + ecs_vec_fini_t(NULL, &resource_ids, int); +} + +/* Gets a resource id from the vector. It will return 0 if no more resources + * available. */ +static +int get_resource_id() +{ + size_t idcount = ecs_vec_count(&resource_ids); + if (idcount == 0) + return 0; + int id = *ecs_vec_get_t(&resource_ids, int, idcount - 1); + ecs_vec_set_count_t(NULL, &resource_ids, int, idcount - 1); + return id; +} + +/* Returns a resource for reuse. */ +static +void return_resource_id( + int id) +{ + if (id == 0) + return; + *(ecs_vec_append_t(NULL, &resource_ids, int)) = id; +} + +/* Returns true if a specific resource id is in the pool. */ +static +bool resource_id_available( + int id) +{ + for (int i = 0; i < ecs_vec_count(&resource_ids); i++) { + if (id == *ecs_vec_get_t(&resource_ids, int, i)) + return true; + } + return false; +} + +/* Returns the numer of currently available resources */ +static +int resources_left() +{ + return ecs_vec_count(&resource_ids); +} + +/* Define a ResourceHandle constructor that sets the payload value to 0 and + * obtains a unique resource id: */ +static +void ResourceHandle_ctor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + for (int i = 0; i < count; i++) { + ResourceHandle *r = + (ResourceHandle *) ECS_ELEM_T(ptr, ResourceHandle, i); + r->id = get_resource_id(); + test_assert(r->id); + r->value = 0; + } +} + +/* Define a ResourceHandle destructor that returns the borrowed resource id: */ +static +void ResourceHandle_dtor( + void *ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + for (int i = 0; i < count; i++) { + ResourceHandle *r = + (ResourceHandle *) ECS_ELEM_T(ptr, ResourceHandle, i); + return_resource_id(r->id); + } +} + +/* Define a ResourceHandle move operation that transfers the resource id: */ +static +void ResourceHandle_move( + void *dst_ptr, + void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + for (int i = 0; i < count; i++) { + ResourceHandle *src_r = + (ResourceHandle *) ECS_ELEM_T(src_ptr, ResourceHandle, i); + ResourceHandle *dst_r = + (ResourceHandle *) ECS_ELEM_T(dst_ptr, ResourceHandle, i); + return_resource_id(dst_r->id); + *dst_r = *src_r; + src_r->id = 0; + } +} + +/* Define a ResourceHandle copy operation that copies the payload value but + * obtains its own resource id: */ +static +void ResourceHandle_copy( + void *dst_ptr, + const void *src_ptr, + int32_t count, + const ecs_type_info_t *type_info) +{ + for (int i = 0; i < count; i++) { + const ResourceHandle *src_r = + (const ResourceHandle *) ECS_ELEM_T(src_ptr, ResourceHandle, i); + ResourceHandle *dst_r = + (ResourceHandle *) ECS_ELEM_T(dst_ptr, ResourceHandle, i); + return_resource_id(dst_r->id); + *dst_r = *src_r; + dst_r->id = get_resource_id(); + test_assert(dst_r->id); + } +} + +/* Defines a struct in Flecs to model the ResourceHandle struct + * For different tests, it can set specific hooks or not. */ +static ecs_entity_t resource_handle; + +static +void define_resource_handle( + ecs_world_t *world, + bool ctor, + bool dtor, + bool move, + bool copy) +{ + resource_handle = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "ResourceHandle"}), + .members = { + {.name = "id", .type = ecs_id(ecs_i32_t)}, + {.name = "value", .type = ecs_id(ecs_u32_t)} + } + }); + + const ecs_type_info_t *ti = ecs_get_type_info(world, resource_handle); + ecs_type_hooks_t hooks = ti->hooks; + + if (ctor) + hooks.ctor = ResourceHandle_ctor; + if (dtor) + hooks.dtor = ResourceHandle_dtor; + if (move) + hooks.move = ResourceHandle_move; + if (copy) + hooks.copy = ResourceHandle_copy; + + ecs_set_hooks_id(world, resource_handle, &hooks); +} + +/* Test hooks for runtime array of trivial structs + * These structs have no special copy/move/dtor logic and are zero-initialized + * Trivial structs should get flecs default constructor and nothing else + * An array of trivial struct should also only get flecs default constructor and + * nothing else. */ +void RuntimeTypes_trivial_array(void) { + ecs_world_t *world = ecs_init(); + + /* Create a new component: */ + ecs_entity_t trivial_struct = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Trivial Struct"}), + .members = { + {.name = "a", .type = ecs_id(ecs_i32_t)}, + {.name = "b", .type = ecs_id(ecs_f32_t)} + } + }); + + /* create an array of 3 trivial structs: */ + ecs_array_desc_t desc = {.entity = 0, .type = trivial_struct, .count = 3}; + ecs_entity_t arr_of_struct = ecs_array_init(world, &desc); + + const ecs_type_info_t *ti = ecs_get_type_info(world, arr_of_struct); + + test_assert(ti->hooks.ctor != NULL); /* should have got flecs default constructor. */ + + /* no other hooks should've been set: */ + test_assert(ti->hooks.copy == NULL); + test_assert(ti->hooks.move == NULL); + test_assert(ti->hooks.dtor == NULL); + + /* Check if the ctor initialized everything to 0. + * Do this a couple of times to make sure we don't get a zero-initialized + * struct out of sheer luck: */ + for (int i = 0; i < 30; i++) { + ecs_entity_t e = ecs_new(world); + ecs_add_id(world, e, arr_of_struct); + const void *data = ecs_get_id(world, e, arr_of_struct); + test_assert(is_memory_filled_with(data, ti->size, 0)); + if (i % 3 == 0) + ecs_delete(world, e); + } + + ecs_fini(world); +} + +/* Tests that if on the array's underlying type only a ctor is defined, only a + * ctor is defined for the array itself Tests that the specified constructor is + * called for each array element. */ +void RuntimeTypes_array_ctor(void) { + ecs_world_t *world = ecs_init(); + + /* create some "resources" */ + initialize_resource_ids(10); + test_int(10, resources_left()); + + /* Define the Resource with only a constructor hook. */ + define_resource_handle(world, true, false, false, false); + + /* create an array of 3 ResourceHandles */ + ecs_array_desc_t desc = {.entity = 0, .type = resource_handle, .count = 3}; + ecs_entity_t arr_of_resources = ecs_array_init(world, &desc); + + const ecs_type_hooks_t *hooks = + &ecs_get_type_info(world, arr_of_resources)->hooks; + + test_assert(hooks->ctor != NULL); /* a ctor should've been generated */ + + /* no other hooks should have been generated, since the depending type, + * "resource_handle" only has a ctor hook: */ + test_assert(hooks->dtor == NULL); + test_assert(hooks->move == NULL); + test_assert(hooks->copy == NULL); + + /* Test that the set ctor hook is indeed working: */ + ecs_entity_t e = ecs_new(world); + ecs_add_id(world, e, arr_of_resources); + test_int(7, resources_left()); /* 3 resources were used out of 10, since the array has a size of 3 */ + + ecs_fini(world); + + free_resource_ids(); +} + +/* Tests that if on the array's underlying type only a dtor is defined, only a + * dtor is defined for the array itself Tests that the specified destructor is + * called for each array element. */ +void RuntimeTypes_array_dtor(void) { + ecs_world_t *world = ecs_init(); + + /* create some "resources" */ + initialize_resource_ids(10); + + /* Define the Resource with only a destructor hook. */ + define_resource_handle(world, false, true, false, false); + + /* create an array of ResourceHandle */ + ecs_array_desc_t desc = {.entity = 0, .type = resource_handle, .count = 3}; + ecs_entity_t arr_of_resources = ecs_array_init(world, &desc); + + const ecs_type_hooks_t *hooks = + &ecs_get_type_info(world, arr_of_resources)->hooks; + + test_assert(hooks->ctor != NULL); /* ctor should exist and set to flecs' + default memset-to-zero constructor */ + test_assert(hooks->dtor != NULL); /* should be set to call the resource + handle dtor for each item in the array. + + no other hooks should have been generated, since the depending type, + "resource_handle" only has a dtor hook: */ + test_assert(hooks->move == NULL); + test_assert(hooks->copy == NULL); + + /* Test that the set dtor hook is indeed working. */ + ecs_entity_t e = ecs_new(world); + ecs_add_id(world, e, arr_of_resources); + test_int(10, resources_left()); /* since no special ctor is + defined, it won't pick up resources from the pool, and it stays the same. + + The default constructor should've set the whole array to zero: */ + ResourceHandle *handles = + (ResourceHandle *) ecs_get_mut_id(world, e, arr_of_resources); + test_assert(is_memory_filled_with(handles, sizeof(ResourceHandle) * 3, 0)); + + /* Make up some resource ids so they're artificially returned to the pool by + * the destructor: */ + handles[0].id = 100; + handles[1].id = 200; + handles[2].id = 300; + + ecs_delete(world, e); /* destroy the entity, which will in turn destroy the + component, invoking the destructors + + check that made-up resources are now in the pool: */ + test_int(13, resources_left()); + test_assert(resource_id_available(100)); + test_assert(resource_id_available(200)); + test_assert(resource_id_available(300)); + + ecs_fini(world); + + free_resource_ids(); +} + +/* compares two resource handles */ +static +bool resource_handle_compare( + ecs_world_t *world, + void *a, + void *b, + int32_t count, + const ecs_type_info_t *ti) +{ + for (int j = 0; j < count; j++) { + const ResourceHandle *ra = + (const ResourceHandle *) ECS_ELEM(a, ti->size, j); + const ResourceHandle *rb = + (const ResourceHandle *) ECS_ELEM(b, ti->size, j); + if (ra->value != rb->value) { + return false; + } + } + return true; +} + +/* Tests that if on the array's underlying type only a move hook is defined, + * only a move hook is defined for the array itself. Tests that the specified + * move hook is called for each array element. */ +void RuntimeTypes_array_move(void) { + ecs_world_t *world = ecs_init(); + + /* create some "resources" */ + initialize_resource_ids(10); + + /* Define the Resource with only a move hook. */ + define_resource_handle(world, false, false, true, false); + + /* create an array of ResourceHandle */ + ecs_array_desc_t desc = {.entity = 0, .type = resource_handle, .count = 3}; + ecs_entity_t arr_of_resources = ecs_array_init(world, &desc); + + const ecs_type_hooks_t *hooks = + &ecs_get_type_info(world, arr_of_resources)->hooks; + + test_assert(hooks->ctor != NULL); /* ctor should exist and set to flecs' + default memset-to-zero constructor */ + test_assert(hooks->move != + NULL); /* should be set to call the resource handle move hook + for each item in the array. + + no other hooks should have been generated, since the underlying type, + "resource_handle" only has a move hook: */ + test_assert(hooks->dtor == NULL); + test_assert(hooks->copy == NULL); + + /* Test that the set move hook is indeed working. */ + ecs_entity_t e = ecs_new(world); + ecs_add_id(world, e, arr_of_resources); + test_int(10, resources_left()); /* since no special ctor is + defined, it won't pick up resources from the pool, and it stays the same. + + The default constructor should've set the whole array to zero: */ + ResourceHandle *handles = + (ResourceHandle *) ecs_get_mut_id(world, e, arr_of_resources); + test_assert(is_memory_filled_with(handles, sizeof(ResourceHandle) * 3, 0)); + + /* Make up some resource ids so we can test they moved: */ + handles[0] = (ResourceHandle){.id = 100, .value = 111}; + handles[1] = (ResourceHandle){.id = 200, .value = 222}; + handles[2] = (ResourceHandle){.id = 300, .value = 333}; + + /* add another entity to the test entity to trigger an archetype change and + * thus a move: */ + ecs_add_id(world, e, ecs_new(world)); + handles = (ResourceHandle *) ecs_get_mut_id(world, e, arr_of_resources); + + const ecs_type_info_t *rh_ti = ecs_get_type_info(world, resource_handle); + /* we should retrieve the same values: */ + test_assert(resource_handle_compare( + world, &(ResourceHandle){.id = 100, .value = 111}, &handles[0], 1, + rh_ti)); + test_assert(resource_handle_compare( + world, &(ResourceHandle){.id = 200, .value = 222}, &handles[1], 1, + rh_ti)); + test_assert(resource_handle_compare( + world, &(ResourceHandle){.id = 300, .value = 333}, &handles[2], 1, + rh_ti)); + + test_int(10, resources_left()); /* pool stays the same */ + + ecs_delete(world, e); + + /* since no special destructor is defined, the resources 100,200,300 are not + * returned to the pool: */ + test_int(10, resources_left()); + test_assert(!resource_id_available(100)); + test_assert(!resource_id_available(200)); + test_assert(!resource_id_available(300)); + + ecs_fini(world); + + free_resource_ids(); +} + +/* Tests that if on the array's underlying type only a copy hook is defined, + only a copy hook is defined for the array itself. Tests that the specified + copy hook is called for each array element. */ +void RuntimeTypes_array_copy(void) { + ecs_world_t *world = ecs_init(); + + /* create some "resources" */ + initialize_resource_ids(10); + test_int(10, resources_left()); + + /* Define the Resource with only a move hook. */ + define_resource_handle(world, false, false, false, true); + + /* create an array of ResourceHandle */ + ecs_array_desc_t desc = {.entity = 0, .type = resource_handle, .count = 3}; + ecs_entity_t arr_of_resources = ecs_array_init(world, &desc); + + const ecs_type_hooks_t *hooks = + &ecs_get_type_info(world, arr_of_resources)->hooks; + + test_assert(hooks->ctor != NULL); /* ctor should exist and set to flecs' + default memset-to-zero constructor */ + test_assert(hooks->copy != + NULL); /* should be set to call the resource handle copy hook + for each item in the array. + + no other hooks should have been generated, since the underlying type, + "resource_handle" only has a move hook: */ + test_assert(hooks->dtor == NULL); + test_assert(hooks->move == NULL); + + /* Test that the set copy hook is indeed working. */ + ecs_entity_t prefab = ecs_new(world); + ecs_add_id(world, prefab, arr_of_resources); + test_int(10, resources_left()); /* since no special ctor is + defined, it won't pick up resources from the pool, and it stays the same. + + The default constructor should've set the whole array to zero: */ + ResourceHandle *prefab_handles = + (ResourceHandle *) ecs_get_mut_id(world, prefab, arr_of_resources); + test_assert( + is_memory_filled_with(prefab_handles, sizeof(ResourceHandle) * 3, 0)); + + /* Make up some resource ids so we can test they are copied: */ + prefab_handles[0] = (ResourceHandle){.id = 100, .value = 111}; + prefab_handles[1] = (ResourceHandle){.id = 200, .value = 222}; + prefab_handles[2] = (ResourceHandle){.id = 300, .value = 333}; + + /* Instantiate a prefab to trigger a copy: */ + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, prefab); + test_int(7, resources_left()); /* should've taken 3 resources from + the pool */ + + ResourceHandle *handles = + (ResourceHandle *) ecs_get_mut_id(world, e, arr_of_resources); + + const ecs_type_info_t *rh_ti = ecs_get_type_info(world, resource_handle); + test_assert(resource_handle_compare(world, &prefab_handles[0], &handles[0], + 1, rh_ti)); + test_assert(resource_handle_compare(world, &prefab_handles[1], &handles[1], + 1, rh_ti)); + test_assert(resource_handle_compare(world, &prefab_handles[2], &handles[2], + 1, rh_ti)); + + ecs_delete(world, e); + test_int(7, resources_left()); /* resources not returned since we + did not hook a destructor */ + + ecs_fini(world); + + free_resource_ids(); +} + +/* Test vector types */ +void RuntimeTypes_vector_lifecycle(void) { + ecs_world_t *world = ecs_init(); + + /* create some "resources" */ + int initial_resources = 10; + initialize_resource_ids(initial_resources); + test_int(10, resources_left()); + + define_resource_handle(world, true, true, true, true); + + /* create a vector of ResourceHandles */ + ecs_vector_desc_t desc = {.entity = 0, .type = resource_handle}; + ecs_entity_t vector_of_resources = ecs_vector_init(world, &desc); + + const ecs_type_hooks_t *hooks = + &ecs_get_type_info(world, vector_of_resources)->hooks; + + /* a vector type requires al 4 hooks: */ + test_assert(hooks->ctor != NULL); + test_assert(hooks->dtor != NULL); + test_assert(hooks->move != NULL); + test_assert(hooks->copy != NULL); + + /* Test the vector type is working: */ + ecs_entity_t prefab = ecs_new(world); + ecs_add_id(world, prefab, EcsPrefab); + ecs_vec_t *v = + (ecs_vec_t *) ecs_ensure_id(world, prefab, vector_of_resources); + test_assert(v != NULL); + test_assert(v->array == NULL); + test_assert(v->count == 0); + test_int(initial_resources, + resources_left()); /* still have 10 resources since +vector is empty + +manually add some items to the vector. These must be constructed by hand: */ + ecs_vec_set_count_t(NULL, v, ResourceHandle, 3); + ResourceHandle_ctor(ecs_vec_first(v), 3, NULL); + test_int(initial_resources - 3, resources_left()); /* Used up 3 + resources that are now in the vector + + test vector copy assign by instantiating the prefab */ + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, prefab); + test_int(initial_resources - 6, resources_left()); /* Used up 3 + more resources after copying the vector + + test vector move assign by forcing a move via archetype change: */ + ecs_add_id(world, e, ecs_new(world)); + test_int(initial_resources - 6, + resources_left()); /* No more resources consumed */ + + ecs_delete(world, e); + test_int(initial_resources - 3, resources_left()); /* Frees 3 resources held + by the instance */ + + ecs_delete(world, prefab); + test_int(initial_resources, resources_left()); /* Frees another 3 + resources held by the prefab + + check if all resources were returned: */ + for (int i = 0; i < initial_resources; i++) { + test_assert(resource_id_available(i + 1)); + } + + ecs_fini(world); + + free_resource_ids(); +} + +/* Test vectors of trivial types */ +void RuntimeTypes_vector_lifecycle_trivial_type(void) { + ecs_world_t *world = ecs_init(); + + /* create a vector of ints */ + ecs_vector_desc_t desc = {.entity = 0, .type = ecs_id(ecs_i32_t)}; + ecs_entity_t vector_of_ints = ecs_vector_init(world, &desc); + + const ecs_type_hooks_t *hooks = + &ecs_get_type_info(world, vector_of_ints)->hooks; + + /* a vector type requires al 4 hooks, even for trivial types: */ + test_assert(hooks->ctor != NULL); + test_assert(hooks->dtor != NULL); + test_assert(hooks->move != NULL); + test_assert(hooks->copy != NULL); + + /* Test the vector type is working: */ + ecs_entity_t prefab = ecs_new(world); + ecs_add_id(world, prefab, EcsPrefab); + ecs_vec_t *v = (ecs_vec_t *) ecs_ensure_id(world, prefab, vector_of_ints); + test_assert(v != NULL); + test_assert(v->array == NULL); + test_assert(v->count == 0); + + /* manually add some items to the vector. These must be constructed by hand: + */ + for (int i = 0; i < 3; i++) { + *ecs_vec_append_t(NULL, v, int) = 79; + } + + /* test vector copy assign by instantiating the prefab */ + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, prefab); + + /* verify we got a copy: */ + const ecs_vec_t *vcopy = + (const ecs_vec_t *) ecs_get_id(world, e, vector_of_ints); + int32_t count = ecs_vec_count(vcopy); + test_int(ecs_vec_count(v), count); + test_assert(0 == ecs_os_memcmp(ecs_vec_first(v), ecs_vec_first(vcopy), + sizeof(int32_t) * count)); + + /* test vector move assign by forcing a move via archetype change: */ + ecs_add_id(world, e, ecs_new(world)); + const ecs_vec_t *vcopy_moved = + (const ecs_vec_t *) ecs_get_id(world, e, vector_of_ints); + count = ecs_vec_count(vcopy_moved); + test_assert(vcopy != vcopy_moved); + test_int(ecs_vec_count(v), count); + test_assert(0 == ecs_os_memcmp(ecs_vec_first(v), ecs_vec_first(vcopy_moved), + sizeof(int32_t) * count)); + + ecs_delete(world, e); + ecs_delete(world, prefab); + + ecs_fini(world); + + free_resource_ids(); +} + +/* Configure an opaque type that consumes resources */ +ecs_entity_t define_ResourceHandle_opaque( + ecs_world_t *world) +{ + ECS_COMPONENT(world, ResourceHandle); + + ecs_type_hooks_t hooks = *ecs_get_hooks(world, ResourceHandle); + hooks.ctor = ResourceHandle_ctor; + hooks.dtor = ResourceHandle_dtor; + hooks.move = ResourceHandle_move; + hooks.copy = ResourceHandle_copy; + ecs_set_hooks_id(world, ecs_id(ResourceHandle), &hooks); + + /* Create struct type that describes the structure of ResourceHandle */ + ecs_entity_t resource_handle_descriptor = ecs_struct(world, { + .members = { + {.name = "value", .type = ecs_id(ecs_u32_t)}, + } + }); + + /* Register ResourceHandle as opaque type. */ + ecs_opaque(world, { + .entity = ecs_id(ResourceHandle), + .type = {.as_type = resource_handle_descriptor} + }); + + return ecs_id(ResourceHandle); +} + +/* Tests that RTT ignores opaque types */ +void RuntimeTypes_opaque(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t resource_handle_opaque = define_ResourceHandle_opaque(world); + + /* Now test that hooks defined for the opaque type are intact: */ + ecs_type_hooks_t hooks = *ecs_get_hooks_id(world, resource_handle_opaque); + test_assert(hooks.ctor == ResourceHandle_ctor); + test_assert(hooks.dtor == ResourceHandle_dtor); + test_assert(hooks.move == ResourceHandle_move); + test_assert(hooks.copy == ResourceHandle_copy); + + ecs_fini(world); +} + +/* Helper function used in the tests below to invoke a type's registered + * constructor, if any, when adding items to vectors */ +static +void invoke_type_ctor( + ecs_world_t *world, + void *ptr, + int32_t count, + ecs_entity_t component) +{ + const ecs_type_info_t *ti = ecs_get_type_info(world, component); + if (ti) { + if (ti->hooks.ctor) { + ti->hooks.ctor(ptr, count, ti); + } else { + ecs_os_memset(ptr, 0, ti->size * count); + } + } +} + +/* Test RTT combinations */ +void RuntimeTypes_struct_with_ints(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_i32_t a; + ecs_i32_t b; + } StructWithInts; + + ecs_entity_t struct_with_ints = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_i32_t)}, + {"b", ecs_id(ecs_i32_t)}, + } + }); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithInts *ptr = ecs_ensure_id(world, e, struct_with_ints); + test_memory_zero(ptr, sizeof(StructWithInts)); + ptr->a = 100; + ptr->b = 101; + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithInts *ptr = + ecs_get_id(world, instance, struct_with_ints); + test_int(100, ptr->a); + test_int(101, ptr->b); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + + { + const StructWithInts *ptr = + ecs_get_id(world, instance, struct_with_ints); + test_int(100, ptr->a); + test_int(101, ptr->b); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + } + }); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithStrings *ptr = ecs_ensure_id(world, e, struct_with_strings); + test_memory_zero(ptr, sizeof(StructWithStrings)); + ptr->a = ecs_os_strdup("String100"); + ptr->b = 101; + ptr->c = ecs_os_strdup("String102"); + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithStrings *ptr = + ecs_get_id(world, instance, struct_with_strings); + test_str("String100", ptr->a); + test_int(101, ptr->b); + test_str("String102", ptr->c); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithStrings *ptr = + ecs_get_id(world, instance, struct_with_strings); + test_str("String100", ptr->a); + test_int(101, ptr->b); + test_str("String102", ptr->c); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_struct_with_opaque(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t resource_handle_opaque = define_ResourceHandle_opaque(world); + + typedef struct { + ResourceHandle a; + } StructWithOpaque; + + ecs_entity_t struct_with_opaque = ecs_struct(world, { + .members = { + {"a", resource_handle_opaque}, + } + }); + + /* struct_with_opaque consumes 1 test resources per instance */ + const int initial_resources = 4; + initialize_resource_ids(initial_resources); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithOpaque *ptr = ecs_ensure_id(world, e, struct_with_opaque); + test_assert(ptr->a.id != 0); + test_int(0, ptr->a.value); + ptr->a.value = 100; + } + /* 1 resource(s) should have been used */ + test_int(1, initial_resources - resources_left()); + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithOpaque *ptr = + ecs_get_id(world, instance, struct_with_opaque); + test_int(100, ptr->a.value); + } + /* 2 resource(s) should be in use now */ + test_int(2, initial_resources - resources_left()); + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithOpaque *ptr = + ecs_get_id(world, instance, struct_with_opaque); + test_int(100, ptr->a.value); + } + /* 2 resource(s) should still be in use after a move */ + test_int(2, initial_resources - resources_left()); + + /* Test deleting: */ + ecs_delete(world, e); + + /* After destroying the first entity, only 1 resource(s) should be in use */ + test_int(1, initial_resources - resources_left()); + ecs_delete(world, instance); + + /* check if all 2 test resources were returned and exactly all resource ids + * are back: */ + test_int(initial_resources, resources_left()); + int i; + for (i = 1; i <= initial_resources; i++) { + test_assert(resource_id_available(i)); + } + + ecs_fini(world); + + free_resource_ids(); +} + +void RuntimeTypes_nested_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + }}); + + typedef struct { + StructWithStrings a; + ecs_i32_t b; + StructWithStrings c; + } NestedStructWithStrings; + + ecs_entity_t nested_struct_with_strings = ecs_struct(world, { + .members = { + {"a", struct_with_strings}, + {"b", ecs_id(ecs_i32_t)}, + {"c", struct_with_strings}, + } + }); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + NestedStructWithStrings *ptr = + ecs_ensure_id(world, e, nested_struct_with_strings); + test_memory_zero(ptr, sizeof(NestedStructWithStrings)); + ptr->a.a = ecs_os_strdup("String100"); + ptr->a.b = 101; + ptr->a.c = ecs_os_strdup("String102"); + ptr->b = 103; + ptr->c.a = ecs_os_strdup("String104"); + ptr->c.b = 105; + ptr->c.c = ecs_os_strdup("String106"); + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const NestedStructWithStrings *ptr = + ecs_get_id(world, instance, nested_struct_with_strings); + test_str("String100", ptr->a.a); + test_int(101, ptr->a.b); + test_str("String102", ptr->a.c); + test_int(103, ptr->b); + test_str("String104", ptr->c.a); + test_int(105, ptr->c.b); + test_str("String106", ptr->c.c); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const NestedStructWithStrings *ptr = + ecs_get_id(world, instance, nested_struct_with_strings); + test_str("String100", ptr->a.a); + test_int(101, ptr->a.b); + test_str("String102", ptr->a.c); + test_int(103, ptr->b); + test_str("String104", ptr->c.a); + test_int(105, ptr->c.b); + test_str("String106", ptr->c.c); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_struct_with_array_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + typedef struct { + ecs_string_t a[3]; + ecs_i32_t b; + } StructWithArrayOfStrings; + + ecs_entity_t struct_with_array_of_strings = ecs_struct(world, { + .members = { + {"a", array_of_strings}, + {"b", ecs_id(ecs_i32_t)}, + } + }); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithArrayOfStrings *ptr = + ecs_ensure_id(world, e, struct_with_array_of_strings); + test_memory_zero(ptr, sizeof(StructWithArrayOfStrings)); + ptr->a[0] = ecs_os_strdup("String100"); + ptr->a[1] = ecs_os_strdup("String101"); + ptr->a[2] = ecs_os_strdup("String102"); + ptr->b = 103; + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithArrayOfStrings *ptr = + ecs_get_id(world, instance, struct_with_array_of_strings); + test_str("String100", ptr->a[0]); + test_str("String101", ptr->a[1]); + test_str("String102", ptr->a[2]); + test_int(103, ptr->b); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithArrayOfStrings *ptr = + ecs_get_id(world, instance, struct_with_array_of_strings); + test_str("String100", ptr->a[0]); + test_str("String101", ptr->a[1]); + test_str("String102", ptr->a[2]); + test_int(103, ptr->b); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_struct_with_array_of_array_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + ecs_entity_t /* ecs_string_t[3][3] */ array_of_array_of_strings = + ecs_array(world, {.type = array_of_strings, .count = 3}); + + typedef struct { + ecs_string_t a[3][3]; + ecs_string_t b; + } StructWithArrayOfArrayOfStrings; + + ecs_entity_t struct_with_array_of_array_of_strings = ecs_struct(world, { + .members = { + {"a", array_of_array_of_strings}, + {"b", ecs_id(ecs_string_t)}, + } + }); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithArrayOfArrayOfStrings *ptr = + ecs_ensure_id(world, e, struct_with_array_of_array_of_strings); + test_memory_zero(ptr, sizeof(StructWithArrayOfArrayOfStrings)); + int i; + for (i = 0; i < 3; i++) { + ptr->a[i][0] = ecs_os_strdup("String100"); + ptr->a[i][1] = ecs_os_strdup("String101"); + ptr->a[i][2] = ecs_os_strdup("String102"); + } + ptr->b = ecs_os_strdup("String103"); + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithArrayOfArrayOfStrings *ptr = + ecs_get_id(world, instance, struct_with_array_of_array_of_strings); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", ptr->a[i][0]); + test_str("String101", ptr->a[i][1]); + test_str("String102", ptr->a[i][2]); + } + test_str("String103", ptr->b); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithArrayOfArrayOfStrings *ptr = + ecs_get_id(world, instance, struct_with_array_of_array_of_strings); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", ptr->a[i][0]); + test_str("String101", ptr->a[i][1]); + test_str("String102", ptr->a[i][2]); + } + test_str("String103", ptr->b); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_struct_with_vector_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_ints = + ecs_vector(world, {.type = ecs_id(ecs_i32_t)}); + + typedef struct { + ecs_vec_t a; + } StructWithVectorOfInts; + + ecs_entity_t struct_with_vector_of_ints = ecs_struct(world, { + .members = { + {"a", vector_of_ints}, + } + }); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithVectorOfInts *ptr = + ecs_ensure_id(world, e, struct_with_vector_of_ints); + { + test_int(0, ecs_vec_count(&ptr->a)); + ecs_vec_set_count(NULL, &ptr->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *va = ecs_vec_first(&ptr->a); + invoke_type_ctor(world, va, 3, ecs_id(ecs_i32_t)); + test_memory_zero(va, sizeof(ecs_i32_t) * 3); + va[0] = 100; + va[1] = 101; + va[2] = 102; + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithVectorOfInts *ptr = + ecs_get_id(world, instance, struct_with_vector_of_ints); + { + test_int(3, ecs_vec_count(&ptr->a)); + const ecs_i32_t *va = ecs_vec_first(&ptr->a); + test_assert(va != NULL); + test_int(100, va[0]); + test_int(101, va[1]); + test_int(102, va[2]); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithVectorOfInts *ptr = + ecs_get_id(world, instance, struct_with_vector_of_ints); + { + test_int(3, ecs_vec_count(&ptr->a)); + const ecs_i32_t *va = ecs_vec_first(&ptr->a); + test_assert(va != NULL); + test_int(100, va[0]); + test_int(101, va[1]); + test_int(102, va[2]); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_struct_with_vector_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_strings = + ecs_vector(world, {.type = ecs_id(ecs_string_t)}); + + typedef struct { + ecs_vec_t a; + } StructWithVectorOfStrings; + + ecs_entity_t struct_with_vector_of_strings = ecs_struct(world, { + .members = { + {"a", vector_of_strings}, + } + }); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithVectorOfStrings *ptr = + ecs_ensure_id(world, e, struct_with_vector_of_strings); + { + test_int(0, ecs_vec_count(&ptr->a)); + ecs_vec_set_count(NULL, &ptr->a, sizeof(ecs_string_t), 3); + ecs_string_t *va = ecs_vec_first(&ptr->a); + invoke_type_ctor(world, va, 3, ecs_id(ecs_string_t)); + test_memory_zero(va, sizeof(ecs_string_t) * 3); + va[0] = ecs_os_strdup("String100"); + va[1] = ecs_os_strdup("String101"); + va[2] = ecs_os_strdup("String102"); + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithVectorOfStrings *ptr = + ecs_get_id(world, instance, struct_with_vector_of_strings); + { + test_int(3, ecs_vec_count(&ptr->a)); + const ecs_string_t *va = ecs_vec_first(&ptr->a); + test_assert(va != NULL); + test_str("String100", va[0]); + test_str("String101", va[1]); + test_str("String102", va[2]); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithVectorOfStrings *ptr = + ecs_get_id(world, instance, struct_with_vector_of_strings); + { + test_int(3, ecs_vec_count(&ptr->a)); + const ecs_string_t *va = ecs_vec_first(&ptr->a); + test_assert(va != NULL); + test_str("String100", va[0]); + test_str("String101", va[1]); + test_str("String102", va[2]); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_nested_struct_with_vector_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_ints = + ecs_vector(world, {.type = ecs_id(ecs_i32_t)}); + + typedef struct { + ecs_vec_t a; + ecs_i32_t b; + ecs_vec_t c; + } InnerStruct1; + + ecs_entity_t inner_struct_1 = ecs_struct(world, { + .members = { + {"a", vector_of_ints}, + {"b", ecs_id(ecs_i32_t)}, + {"c", vector_of_ints}, + } + }); + + typedef struct { + ecs_vec_t a; + ecs_i32_t b; + InnerStruct1 c; + } NestedStructWithVectorOfInts; + + ecs_entity_t nested_struct_with_vector_of_ints = ecs_struct(world, { + .members = { + {"a", vector_of_ints}, + {"b", ecs_id(ecs_i32_t)}, + {"c", inner_struct_1}, + } + }); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + NestedStructWithVectorOfInts *ptr = + ecs_ensure_id(world, e, nested_struct_with_vector_of_ints); + { + test_int(0, ecs_vec_count(&ptr->a)); + ecs_vec_set_count(NULL, &ptr->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *va = ecs_vec_first(&ptr->a); + invoke_type_ctor(world, va, 3, ecs_id(ecs_i32_t)); + test_memory_zero(va, sizeof(ecs_i32_t) * 3); + va[0] = 100; + va[1] = 101; + va[2] = 102; + } + test_int(0, ptr->b); + ptr->b = 103; + { + test_int(0, ecs_vec_count(&ptr->c.a)); + ecs_vec_set_count(NULL, &ptr->c.a, sizeof(ecs_i32_t), 3); + ecs_i32_t *vca = ecs_vec_first(&ptr->c.a); + invoke_type_ctor(world, vca, 3, ecs_id(ecs_i32_t)); + test_memory_zero(vca, sizeof(ecs_i32_t) * 3); + vca[0] = 104; + vca[1] = 105; + vca[2] = 106; + } + test_int(0, ptr->c.b); + ptr->c.b = 107; + { + test_int(0, ecs_vec_count(&ptr->c.c)); + ecs_vec_set_count(NULL, &ptr->c.c, sizeof(ecs_i32_t), 3); + ecs_i32_t *vcc = ecs_vec_first(&ptr->c.c); + invoke_type_ctor(world, vcc, 3, ecs_id(ecs_i32_t)); + test_memory_zero(vcc, sizeof(ecs_i32_t) * 3); + vcc[0] = 108; + vcc[1] = 109; + vcc[2] = 110; + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const NestedStructWithVectorOfInts *ptr = + ecs_get_id(world, instance, nested_struct_with_vector_of_ints); + { + test_int(3, ecs_vec_count(&ptr->a)); + const ecs_i32_t *va = ecs_vec_first(&ptr->a); + test_assert(va != NULL); + test_int(100, va[0]); + test_int(101, va[1]); + test_int(102, va[2]); + } + test_int(103, ptr->b); + { + test_int(3, ecs_vec_count(&ptr->c.a)); + const ecs_i32_t *vca = ecs_vec_first(&ptr->c.a); + test_assert(vca != NULL); + test_int(104, vca[0]); + test_int(105, vca[1]); + test_int(106, vca[2]); + } + test_int(107, ptr->c.b); + { + test_int(3, ecs_vec_count(&ptr->c.c)); + const ecs_i32_t *vcc = ecs_vec_first(&ptr->c.c); + test_assert(vcc != NULL); + test_int(108, vcc[0]); + test_int(109, vcc[1]); + test_int(110, vcc[2]); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const NestedStructWithVectorOfInts *ptr = + ecs_get_id(world, instance, nested_struct_with_vector_of_ints); + { + test_int(3, ecs_vec_count(&ptr->a)); + const ecs_i32_t *va = ecs_vec_first(&ptr->a); + test_assert(va != NULL); + test_int(100, va[0]); + test_int(101, va[1]); + test_int(102, va[2]); + } + test_int(103, ptr->b); + { + test_int(3, ecs_vec_count(&ptr->c.a)); + const ecs_i32_t *vca = ecs_vec_first(&ptr->c.a); + test_assert(vca != NULL); + test_int(104, vca[0]); + test_int(105, vca[1]); + test_int(106, vca[2]); + } + test_int(107, ptr->c.b); + { + test_int(3, ecs_vec_count(&ptr->c.c)); + const ecs_i32_t *vcc = ecs_vec_first(&ptr->c.c); + test_assert(vcc != NULL); + test_int(108, vcc[0]); + test_int(109, vcc[1]); + test_int(110, vcc[2]); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_nested_struct_with_vector_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_strings = + ecs_vector(world, {.type = ecs_id(ecs_string_t)}); + + typedef struct { + ecs_vec_t a; + ecs_i32_t b; + ecs_vec_t c; + } InnerStruct2; + + ecs_entity_t inner_struct_2 = ecs_struct(world, { + .members = { + {"a", vector_of_strings}, + {"b", ecs_id(ecs_i32_t)}, + {"c", vector_of_strings}, + } + }); + + typedef struct { + ecs_vec_t a; + ecs_i32_t b; + InnerStruct2 c; + } NestedStructWithVectorOfStrings; + + ecs_entity_t nested_struct_with_vector_of_strings = ecs_struct(world, { + .members = { + {"a", vector_of_strings}, + {"b", ecs_id(ecs_i32_t)}, + {"c", inner_struct_2}, + } + }); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + NestedStructWithVectorOfStrings *ptr = + ecs_ensure_id(world, e, nested_struct_with_vector_of_strings); + { + test_int(0, ecs_vec_count(&ptr->a)); + ecs_vec_set_count(NULL, &ptr->a, sizeof(ecs_string_t), 3); + ecs_string_t *va = ecs_vec_first(&ptr->a); + invoke_type_ctor(world, va, 3, ecs_id(ecs_string_t)); + test_memory_zero(va, sizeof(ecs_string_t) * 3); + va[0] = ecs_os_strdup("String100"); + va[1] = ecs_os_strdup("String101"); + va[2] = ecs_os_strdup("String102"); + } + test_int(0, ptr->b); + ptr->b = 103; + { + test_int(0, ecs_vec_count(&ptr->c.a)); + ecs_vec_set_count(NULL, &ptr->c.a, sizeof(ecs_string_t), 3); + ecs_string_t *vca = ecs_vec_first(&ptr->c.a); + invoke_type_ctor(world, vca, 3, ecs_id(ecs_string_t)); + test_memory_zero(vca, sizeof(ecs_string_t) * 3); + vca[0] = ecs_os_strdup("String104"); + vca[1] = ecs_os_strdup("String105"); + vca[2] = ecs_os_strdup("String106"); + } + test_int(0, ptr->c.b); + ptr->c.b = 107; + { + test_int(0, ecs_vec_count(&ptr->c.c)); + ecs_vec_set_count(NULL, &ptr->c.c, sizeof(ecs_string_t), 3); + ecs_string_t *vcc = ecs_vec_first(&ptr->c.c); + invoke_type_ctor(world, vcc, 3, ecs_id(ecs_string_t)); + test_memory_zero(vcc, sizeof(ecs_string_t) * 3); + vcc[0] = ecs_os_strdup("String108"); + vcc[1] = ecs_os_strdup("String109"); + vcc[2] = ecs_os_strdup("String110"); + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const NestedStructWithVectorOfStrings *ptr = + ecs_get_id(world, instance, nested_struct_with_vector_of_strings); + { + test_int(3, ecs_vec_count(&ptr->a)); + const ecs_string_t *va = ecs_vec_first(&ptr->a); + test_assert(va != NULL); + test_str("String100", va[0]); + test_str("String101", va[1]); + test_str("String102", va[2]); + } + test_int(103, ptr->b); + { + test_int(3, ecs_vec_count(&ptr->c.a)); + const ecs_string_t *vca = ecs_vec_first(&ptr->c.a); + test_assert(vca != NULL); + test_str("String104", vca[0]); + test_str("String105", vca[1]); + test_str("String106", vca[2]); + } + test_int(107, ptr->c.b); + { + test_int(3, ecs_vec_count(&ptr->c.c)); + const ecs_string_t *vcc = ecs_vec_first(&ptr->c.c); + test_assert(vcc != NULL); + test_str("String108", vcc[0]); + test_str("String109", vcc[1]); + test_str("String110", vcc[2]); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const NestedStructWithVectorOfStrings *ptr = + ecs_get_id(world, instance, nested_struct_with_vector_of_strings); + { + test_int(3, ecs_vec_count(&ptr->a)); + const ecs_string_t *va = ecs_vec_first(&ptr->a); + test_assert(va != NULL); + test_str("String100", va[0]); + test_str("String101", va[1]); + test_str("String102", va[2]); + } + test_int(103, ptr->b); + { + test_int(3, ecs_vec_count(&ptr->c.a)); + const ecs_string_t *vca = ecs_vec_first(&ptr->c.a); + test_assert(vca != NULL); + test_str("String104", vca[0]); + test_str("String105", vca[1]); + test_str("String106", vca[2]); + } + test_int(107, ptr->c.b); + { + test_int(3, ecs_vec_count(&ptr->c.c)); + const ecs_string_t *vcc = ecs_vec_first(&ptr->c.c); + test_assert(vcc != NULL); + test_str("String108", vcc[0]); + test_str("String109", vcc[1]); + test_str("String110", vcc[2]); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_array_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_i32_t[3] */ array_of_ints = + ecs_array(world, {.type = ecs_id(ecs_i32_t), .count = 3}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_i32_t *arr = ecs_ensure_id(world, e, array_of_ints); + test_memory_zero(arr, sizeof(ecs_i32_t[3])); + arr[0] = 100; + arr[1] = 101; + arr[2] = 102; + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_i32_t *arr = ecs_get_id(world, e, array_of_ints); + test_int(100, arr[0]); + test_int(101, arr[1]); + test_int(102, arr[2]); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_i32_t *arr = ecs_get_id(world, e, array_of_ints); + test_int(100, arr[0]); + test_int(101, arr[1]); + test_int(102, arr[2]); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_array_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_string_t *arr = ecs_ensure_id(world, e, array_of_strings); + test_memory_zero(arr, sizeof(ecs_string_t[3])); + arr[0] = ecs_os_strdup("String100"); + arr[1] = ecs_os_strdup("String101"); + arr[2] = ecs_os_strdup("String102"); + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_string_t *arr = ecs_get_id(world, e, array_of_strings); + test_str("String100", arr[0]); + test_str("String101", arr[1]); + test_str("String102", arr[2]); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_string_t *arr = ecs_get_id(world, e, array_of_strings); + test_str("String100", arr[0]); + test_str("String101", arr[1]); + test_str("String102", arr[2]); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_array_of_struct_with_ints(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_i32_t a; + ecs_i32_t b; + } StructWithInts; + + ecs_entity_t struct_with_ints = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_i32_t)}, + {"b", ecs_id(ecs_i32_t)}, + } + }); + + ecs_entity_t /* StructWithInts[3] */ array_of_struct_with_ints = + ecs_array(world, {.type = struct_with_ints, .count = 3}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithInts *arr = + ecs_ensure_id(world, e, array_of_struct_with_ints); + test_memory_zero(arr, sizeof(StructWithInts[3])); + arr[0].a = 100; + arr[0].b = 101; + arr[1].a = 102; + arr[1].b = 103; + arr[2].a = 104; + arr[2].b = 105; + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithInts *arr = + ecs_get_id(world, e, array_of_struct_with_ints); + test_int(100, arr[0].a); + test_int(101, arr[0].b); + test_int(102, arr[1].a); + test_int(103, arr[1].b); + test_int(104, arr[2].a); + test_int(105, arr[2].b); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithInts *arr = + ecs_get_id(world, e, array_of_struct_with_ints); + test_int(100, arr[0].a); + test_int(101, arr[0].b); + test_int(102, arr[1].a); + test_int(103, arr[1].b); + test_int(104, arr[2].a); + test_int(105, arr[2].b); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_array_of_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + } + }); + + ecs_entity_t /* StructWithStrings[3] */ array_of_struct_with_strings = + ecs_array(world, {.type = struct_with_strings, .count = 3}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithStrings *arr = + ecs_ensure_id(world, e, array_of_struct_with_strings); + test_memory_zero(arr, sizeof(StructWithStrings[3])); + arr[0].a = ecs_os_strdup("String100"); + arr[0].b = 101; + arr[0].c = ecs_os_strdup("String102"); + arr[1].a = ecs_os_strdup("String103"); + arr[1].b = 104; + arr[1].c = ecs_os_strdup("String105"); + arr[2].a = ecs_os_strdup("String106"); + arr[2].b = 107; + arr[2].c = ecs_os_strdup("String108"); + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithStrings *arr = + ecs_get_id(world, e, array_of_struct_with_strings); + test_str("String100", arr[0].a); + test_int(101, arr[0].b); + test_str("String102", arr[0].c); + test_str("String103", arr[1].a); + test_int(104, arr[1].b); + test_str("String105", arr[1].c); + test_str("String106", arr[2].a); + test_int(107, arr[2].b); + test_str("String108", arr[2].c); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithStrings *arr = + ecs_get_id(world, e, array_of_struct_with_strings); + test_str("String100", arr[0].a); + test_int(101, arr[0].b); + test_str("String102", arr[0].c); + test_str("String103", arr[1].a); + test_int(104, arr[1].b); + test_str("String105", arr[1].c); + test_str("String106", arr[2].a); + test_int(107, arr[2].b); + test_str("String108", arr[2].c); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_array_of_struct_with_opaques(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t resource_handle_opaque = define_ResourceHandle_opaque(world); + + typedef struct { + ResourceHandle a; + } StructWithOpaque; + + ecs_entity_t struct_with_opaque = ecs_struct(world, { + .members = { + {"a", resource_handle_opaque}, + } + }); + + ecs_entity_t /* StructWithOpaque[3] */ array_of_struct_with_opaques = + ecs_array(world, {.type = struct_with_opaque, .count = 3}); + + /* array_of_struct_with_opaques consumes 3 test resources per instance */ + const int initial_resources = 12; + initialize_resource_ids(initial_resources); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithOpaque *arr = + ecs_ensure_id(world, e, array_of_struct_with_opaques); + test_assert(arr[0].a.id != 0); + test_int(0, arr[0].a.value); + arr[0].a.value = 100; + test_assert(arr[1].a.id != 0); + test_int(0, arr[1].a.value); + arr[1].a.value = 101; + test_assert(arr[2].a.id != 0); + test_int(0, arr[2].a.value); + arr[2].a.value = 102; + } + /* 3 resource(s) should have been used */ + test_int(3, initial_resources - resources_left()); + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithOpaque *arr = + ecs_get_id(world, e, array_of_struct_with_opaques); + test_int(100, arr[0].a.value); + test_int(101, arr[1].a.value); + test_int(102, arr[2].a.value); + } + /* 6 resource(s) should be in use now */ + test_int(6, initial_resources - resources_left()); + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithOpaque *arr = + ecs_get_id(world, e, array_of_struct_with_opaques); + test_int(100, arr[0].a.value); + test_int(101, arr[1].a.value); + test_int(102, arr[2].a.value); + } + /* 6 resource(s) should still be in use after a move */ + test_int(6, initial_resources - resources_left()); + + /* Test deleting: */ + ecs_delete(world, e); + + /* After destroying the first entity, only 3 resource(s) should be in use */ + test_int(3, initial_resources - resources_left()); + ecs_delete(world, instance); + + /* check if all 6 test resources were returned and exactly all resource ids + * are back: */ + test_int(initial_resources, resources_left()); + int i; + for (i = 1; i <= initial_resources; i++) { + test_assert(resource_id_available(i)); + } + + ecs_fini(world); + + free_resource_ids(); +} + +void RuntimeTypes_array_of_array_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + ecs_entity_t /* ecs_string_t[3][3] */ array_of_array_of_strings = + ecs_array(world, {.type = array_of_strings, .count = 3}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_string_t(*arr)[3] = + ecs_ensure_id(world, e, array_of_array_of_strings); + test_memory_zero(arr, sizeof(ecs_string_t[3][3])); + int i; + for (i = 0; i < 3; i++) { + arr[i][0] = ecs_os_strdup("String100"); + arr[i][1] = ecs_os_strdup("String101"); + arr[i][2] = ecs_os_strdup("String102"); + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_string_t(*arr)[3] = (const ecs_string_t(*)[3]) ecs_get_id( + world, e, array_of_array_of_strings); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", arr[i][0]); + test_str("String101", arr[i][1]); + test_str("String102", arr[i][2]); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_string_t(*arr)[3] = (const ecs_string_t(*)[3]) ecs_get_id( + world, e, array_of_array_of_strings); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", arr[i][0]); + test_str("String101", arr[i][1]); + test_str("String102", arr[i][2]); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_array_of_array_of_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + } + }); + + ecs_entity_t /* StructWithStrings[3] */ array_of_struct_with_strings = + ecs_array(world, {.type = struct_with_strings, .count = 3}); + + ecs_entity_t /* StructWithStrings[3][3] */ + array_of_array_of_struct_with_strings = ecs_array( + world, {.type = array_of_struct_with_strings, .count = 3}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + StructWithStrings(*arr)[3] = + ecs_ensure_id(world, e, array_of_array_of_struct_with_strings); + test_memory_zero(arr, sizeof(StructWithStrings[3][3])); + int i; + for (i = 0; i < 3; i++) { + arr[i][0].a = ecs_os_strdup("String100"); + arr[i][0].b = 101; + arr[i][0].c = ecs_os_strdup("String102"); + arr[i][1].a = ecs_os_strdup("String103"); + arr[i][1].b = 104; + arr[i][1].c = ecs_os_strdup("String105"); + arr[i][2].a = ecs_os_strdup("String106"); + arr[i][2].b = 107; + arr[i][2].c = ecs_os_strdup("String108"); + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const StructWithStrings(*arr)[3] = + (const StructWithStrings(*)[3]) ecs_get_id( + world, e, array_of_array_of_struct_with_strings); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", arr[i][0].a); + test_int(101, arr[i][0].b); + test_str("String102", arr[i][0].c); + test_str("String103", arr[i][1].a); + test_int(104, arr[i][1].b); + test_str("String105", arr[i][1].c); + test_str("String106", arr[i][2].a); + test_int(107, arr[i][2].b); + test_str("String108", arr[i][2].c); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const StructWithStrings(*arr)[3] = + (const StructWithStrings(*)[3]) ecs_get_id( + world, e, array_of_array_of_struct_with_strings); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", arr[i][0].a); + test_int(101, arr[i][0].b); + test_str("String102", arr[i][0].c); + test_str("String103", arr[i][1].a); + test_int(104, arr[i][1].b); + test_str("String105", arr[i][1].c); + test_str("String106", arr[i][2].a); + test_int(107, arr[i][2].b); + test_str("String108", arr[i][2].c); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_array_of_vectors_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_ints = + ecs_vector(world, {.type = ecs_id(ecs_i32_t)}); + + ecs_entity_t /* ecs_vec_t[3] */ array_of_vectors_of_ints = + ecs_array(world, {.type = vector_of_ints, .count = 3}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_vec_t *arr = ecs_ensure_id(world, e, array_of_vectors_of_ints); + { + test_int(0, ecs_vec_count(&arr[0])); + ecs_vec_set_count(NULL, &arr[0], sizeof(ecs_i32_t), 3); + ecs_i32_t *v = ecs_vec_first(&arr[0]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); + test_memory_zero(v, sizeof(ecs_i32_t) * 3); + v[0] = 100; + v[1] = 101; + v[2] = 102; + } + { + test_int(0, ecs_vec_count(&arr[1])); + ecs_vec_set_count(NULL, &arr[1], sizeof(ecs_i32_t), 3); + ecs_i32_t *v = ecs_vec_first(&arr[1]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); + test_memory_zero(v, sizeof(ecs_i32_t) * 3); + v[0] = 103; + v[1] = 104; + v[2] = 105; + } + { + test_int(0, ecs_vec_count(&arr[2])); + ecs_vec_set_count(NULL, &arr[2], sizeof(ecs_i32_t), 3); + ecs_i32_t *v = ecs_vec_first(&arr[2]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); + test_memory_zero(v, sizeof(ecs_i32_t) * 3); + v[0] = 106; + v[1] = 107; + v[2] = 108; + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_vec_t *arr = ecs_get_id(world, e, array_of_vectors_of_ints); + { + test_int(3, ecs_vec_count(&arr[0])); + const ecs_i32_t *v = ecs_vec_first(&arr[0]); + test_assert(v != NULL); + test_int(100, v[0]); + test_int(101, v[1]); + test_int(102, v[2]); + } + { + test_int(3, ecs_vec_count(&arr[1])); + const ecs_i32_t *v = ecs_vec_first(&arr[1]); + test_assert(v != NULL); + test_int(103, v[0]); + test_int(104, v[1]); + test_int(105, v[2]); + } + { + test_int(3, ecs_vec_count(&arr[2])); + const ecs_i32_t *v = ecs_vec_first(&arr[2]); + test_assert(v != NULL); + test_int(106, v[0]); + test_int(107, v[1]); + test_int(108, v[2]); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_vec_t *arr = ecs_get_id(world, e, array_of_vectors_of_ints); + { + test_int(3, ecs_vec_count(&arr[0])); + const ecs_i32_t *v = ecs_vec_first(&arr[0]); + test_assert(v != NULL); + test_int(100, v[0]); + test_int(101, v[1]); + test_int(102, v[2]); + } + { + test_int(3, ecs_vec_count(&arr[1])); + const ecs_i32_t *v = ecs_vec_first(&arr[1]); + test_assert(v != NULL); + test_int(103, v[0]); + test_int(104, v[1]); + test_int(105, v[2]); + } + { + test_int(3, ecs_vec_count(&arr[2])); + const ecs_i32_t *v = ecs_vec_first(&arr[2]); + test_assert(v != NULL); + test_int(106, v[0]); + test_int(107, v[1]); + test_int(108, v[2]); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_array_of_vectors_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_strings = + ecs_vector(world, {.type = ecs_id(ecs_string_t)}); + + ecs_entity_t /* ecs_vec_t[3] */ array_of_vectors_of_strings = + ecs_array(world, {.type = vector_of_strings, .count = 3}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_vec_t *arr = ecs_ensure_id(world, e, array_of_vectors_of_strings); + { + test_int(0, ecs_vec_count(&arr[0])); + ecs_vec_set_count(NULL, &arr[0], sizeof(ecs_string_t), 3); + ecs_string_t *v = ecs_vec_first(&arr[0]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); + test_memory_zero(v, sizeof(ecs_string_t) * 3); + v[0] = ecs_os_strdup("String100"); + v[1] = ecs_os_strdup("String101"); + v[2] = ecs_os_strdup("String102"); + } + { + test_int(0, ecs_vec_count(&arr[1])); + ecs_vec_set_count(NULL, &arr[1], sizeof(ecs_string_t), 3); + ecs_string_t *v = ecs_vec_first(&arr[1]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); + test_memory_zero(v, sizeof(ecs_string_t) * 3); + v[0] = ecs_os_strdup("String103"); + v[1] = ecs_os_strdup("String104"); + v[2] = ecs_os_strdup("String105"); + } + { + test_int(0, ecs_vec_count(&arr[2])); + ecs_vec_set_count(NULL, &arr[2], sizeof(ecs_string_t), 3); + ecs_string_t *v = ecs_vec_first(&arr[2]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); + test_memory_zero(v, sizeof(ecs_string_t) * 3); + v[0] = ecs_os_strdup("String106"); + v[1] = ecs_os_strdup("String107"); + v[2] = ecs_os_strdup("String108"); + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_vec_t *arr = + ecs_get_id(world, e, array_of_vectors_of_strings); + { + test_int(3, ecs_vec_count(&arr[0])); + const ecs_string_t *v = ecs_vec_first(&arr[0]); + test_assert(v != NULL); + test_str("String100", v[0]); + test_str("String101", v[1]); + test_str("String102", v[2]); + } + { + test_int(3, ecs_vec_count(&arr[1])); + const ecs_string_t *v = ecs_vec_first(&arr[1]); + test_assert(v != NULL); + test_str("String103", v[0]); + test_str("String104", v[1]); + test_str("String105", v[2]); + } + { + test_int(3, ecs_vec_count(&arr[2])); + const ecs_string_t *v = ecs_vec_first(&arr[2]); + test_assert(v != NULL); + test_str("String106", v[0]); + test_str("String107", v[1]); + test_str("String108", v[2]); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_vec_t *arr = + ecs_get_id(world, e, array_of_vectors_of_strings); + { + test_int(3, ecs_vec_count(&arr[0])); + const ecs_string_t *v = ecs_vec_first(&arr[0]); + test_assert(v != NULL); + test_str("String100", v[0]); + test_str("String101", v[1]); + test_str("String102", v[2]); + } + { + test_int(3, ecs_vec_count(&arr[1])); + const ecs_string_t *v = ecs_vec_first(&arr[1]); + test_assert(v != NULL); + test_str("String103", v[0]); + test_str("String104", v[1]); + test_str("String105", v[2]); + } + { + test_int(3, ecs_vec_count(&arr[2])); + const ecs_string_t *v = ecs_vec_first(&arr[2]); + test_assert(v != NULL); + test_str("String106", v[0]); + test_str("String107", v[1]); + test_str("String108", v[2]); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_array_of_opaque(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t resource_handle_opaque = define_ResourceHandle_opaque(world); + + ecs_entity_t /* ResourceHandle[3] */ array_of_opaque = + ecs_array(world, {.type = resource_handle_opaque, .count = 3}); + + /* array_of_opaque consumes 3 test resources per instance */ + const int initial_resources = 12; + initialize_resource_ids(initial_resources); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ResourceHandle *arr = ecs_ensure_id(world, e, array_of_opaque); + test_assert(arr[0].id != 0); + test_int(0, arr[0].value); + arr[0].value = 100; + test_assert(arr[1].id != 0); + test_int(0, arr[1].value); + arr[1].value = 101; + test_assert(arr[2].id != 0); + test_int(0, arr[2].value); + arr[2].value = 102; + } + /* 3 resource(s) should have been used */ + test_int(3, initial_resources - resources_left()); + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ResourceHandle *arr = ecs_get_id(world, e, array_of_opaque); + test_int(100, arr[0].value); + test_int(101, arr[1].value); + test_int(102, arr[2].value); + } + /* 6 resource(s) should be in use now */ + test_int(6, initial_resources - resources_left()); + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ResourceHandle *arr = ecs_get_id(world, e, array_of_opaque); + test_int(100, arr[0].value); + test_int(101, arr[1].value); + test_int(102, arr[2].value); + } + /* 6 resource(s) should still be in use after a move */ + test_int(6, initial_resources - resources_left()); + + /* Test deleting: */ + ecs_delete(world, e); + + /* After destroying the first entity, only 3 resource(s) should be in use */ + test_int(3, initial_resources - resources_left()); + ecs_delete(world, instance); + + /* check if all 6 test resources were returned and exactly all resource ids + * are back: */ + test_int(initial_resources, resources_left()); + int i; + for (i = 1; i <= initial_resources; i++) { + test_assert(resource_id_available(i)); + } + + ecs_fini(world); + + free_resource_ids(); +} + +void RuntimeTypes_vector_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_ints = + ecs_vector(world, {.type = ecs_id(ecs_i32_t)}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_ints); + { + test_int(0, ecs_vec_count(vec)); + ecs_vec_set_count(NULL, vec, sizeof(ecs_i32_t), 3); + ecs_i32_t *v = ecs_vec_first(vec); + invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); + test_memory_zero(v, sizeof(ecs_i32_t) * 3); + v[0] = 100; + v[1] = 101; + v[2] = 102; + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_ints); + { + test_int(3, ecs_vec_count(vec)); + const ecs_i32_t *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_int(100, v[0]); + test_int(101, v[1]); + test_int(102, v[2]); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_ints); + { + test_int(3, ecs_vec_count(vec)); + const ecs_i32_t *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_int(100, v[0]); + test_int(101, v[1]); + test_int(102, v[2]); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_vector_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_strings = + ecs_vector(world, {.type = ecs_id(ecs_string_t)}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_strings); + { + test_int(0, ecs_vec_count(vec)); + ecs_vec_set_count(NULL, vec, sizeof(ecs_string_t), 3); + ecs_string_t *v = ecs_vec_first(vec); + invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); + test_memory_zero(v, sizeof(ecs_string_t) * 3); + v[0] = ecs_os_strdup("String100"); + v[1] = ecs_os_strdup("String101"); + v[2] = ecs_os_strdup("String102"); + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_strings); + { + test_int(3, ecs_vec_count(vec)); + const ecs_string_t *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_str("String100", v[0]); + test_str("String101", v[1]); + test_str("String102", v[2]); + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_strings); + { + test_int(3, ecs_vec_count(vec)); + const ecs_string_t *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_str("String100", v[0]); + test_str("String101", v[1]); + test_str("String102", v[2]); + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_vector_of_struct_with_ints(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_i32_t a; + ecs_i32_t b; + } StructWithInts; + + ecs_entity_t struct_with_ints = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_i32_t)}, + {"b", ecs_id(ecs_i32_t)}, + } + }); + + ecs_entity_t /* vector */ vector_of_struct_with_ints = + ecs_vector(world, {.type = struct_with_ints}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_struct_with_ints); + test_int(0, ecs_vec_count(vec)); + ecs_vec_set_count(NULL, vec, sizeof(StructWithInts), 3); + StructWithInts *v = ecs_vec_first(vec); + invoke_type_ctor(world, v, 3, struct_with_ints); + test_memory_zero(v, sizeof(StructWithInts) * 3); + v[0].a = 100; + v[0].b = 101; + v[1].a = 102; + v[1].b = 103; + v[2].a = 104; + v[2].b = 105; + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_struct_with_ints); + test_int(3, ecs_vec_count(vec)); + const StructWithInts *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_int(100, v[0].a); + test_int(101, v[0].b); + test_int(102, v[1].a); + test_int(103, v[1].b); + test_int(104, v[2].a); + test_int(105, v[2].b); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_struct_with_ints); + test_int(3, ecs_vec_count(vec)); + const StructWithInts *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_int(100, v[0].a); + test_int(101, v[0].b); + test_int(102, v[1].a); + test_int(103, v[1].b); + test_int(104, v[2].a); + test_int(105, v[2].b); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_vector_of_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + } + }); + + ecs_entity_t /* vector */ vector_of_struct_with_strings = + ecs_vector(world, {.type = struct_with_strings}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_struct_with_strings); + test_int(0, ecs_vec_count(vec)); + ecs_vec_set_count(NULL, vec, sizeof(StructWithStrings), 3); + StructWithStrings *v = ecs_vec_first(vec); + invoke_type_ctor(world, v, 3, struct_with_strings); + test_memory_zero(v, sizeof(StructWithStrings) * 3); + v[0].a = ecs_os_strdup("String100"); + v[0].b = 101; + v[0].c = ecs_os_strdup("String102"); + v[1].a = ecs_os_strdup("String103"); + v[1].b = 104; + v[1].c = ecs_os_strdup("String105"); + v[2].a = ecs_os_strdup("String106"); + v[2].b = 107; + v[2].c = ecs_os_strdup("String108"); + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_vec_t *vec = + ecs_get_id(world, e, vector_of_struct_with_strings); + test_int(3, ecs_vec_count(vec)); + const StructWithStrings *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_str("String100", v[0].a); + test_int(101, v[0].b); + test_str("String102", v[0].c); + test_str("String103", v[1].a); + test_int(104, v[1].b); + test_str("String105", v[1].c); + test_str("String106", v[2].a); + test_int(107, v[2].b); + test_str("String108", v[2].c); + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_vec_t *vec = + ecs_get_id(world, e, vector_of_struct_with_strings); + test_int(3, ecs_vec_count(vec)); + const StructWithStrings *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_str("String100", v[0].a); + test_int(101, v[0].b); + test_str("String102", v[0].c); + test_str("String103", v[1].a); + test_int(104, v[1].b); + test_str("String105", v[1].c); + test_str("String106", v[2].a); + test_int(107, v[2].b); + test_str("String108", v[2].c); + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_vector_of_arrays_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + ecs_entity_t /* vector */ vector_of_arrays_of_strings = + ecs_vector(world, {.type = array_of_strings}); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_arrays_of_strings); + { + test_int(0, ecs_vec_count(vec)); + ecs_vec_set_count(NULL, vec, sizeof(ecs_string_t[3]), 3); + ecs_string_t(*v)[3] = ecs_vec_first(vec); + invoke_type_ctor(world, v, 3, array_of_strings); + test_memory_zero(v, sizeof(ecs_string_t[3]) * 3); + int i; + for (i = 0; i < 3; i++) { + v[i][0] = ecs_os_strdup("String100"); + v[i][1] = ecs_os_strdup("String101"); + v[i][2] = ecs_os_strdup("String102"); + } + } + } + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_vec_t *vec = + ecs_get_id(world, e, vector_of_arrays_of_strings); + { + test_int(3, ecs_vec_count(vec)); + const ecs_string_t(*v)[3] = + (const ecs_string_t(*)[3]) ecs_vec_first(vec); + test_assert(v != NULL); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", v[i][0]); + test_str("String101", v[i][1]); + test_str("String102", v[i][2]); + } + } + } + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_vec_t *vec = + ecs_get_id(world, e, vector_of_arrays_of_strings); + { + test_int(3, ecs_vec_count(vec)); + const ecs_string_t(*v)[3] = + (const ecs_string_t(*)[3]) ecs_vec_first(vec); + test_assert(v != NULL); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", v[i][0]); + test_str("String101", v[i][1]); + test_str("String102", v[i][2]); + } + } + } + + /* Test deleting: */ + ecs_delete(world, e); + ecs_delete(world, instance); + + ecs_fini(world); +} + +void RuntimeTypes_vector_of_opaque(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t resource_handle_opaque = define_ResourceHandle_opaque(world); + + ecs_entity_t /* vector */ vector_of_opaque = + ecs_vector(world, {.type = resource_handle_opaque}); + + /* vector_of_opaque consumes 3 test resources per instance */ + const int initial_resources = 12; + initialize_resource_ids(initial_resources); + + /* Test constructor: */ + ecs_entity_t e = ecs_new(world); + { + ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_opaque); + { + test_int(0, ecs_vec_count(vec)); + ecs_vec_set_count(NULL, vec, sizeof(ResourceHandle), 3); + ResourceHandle *v = ecs_vec_first(vec); + invoke_type_ctor(world, v, 3, resource_handle_opaque); + test_assert(v[0].id != 0); + test_int(0, v[0].value); + v[0].value = 100; + test_assert(v[1].id != 0); + test_int(0, v[1].value); + v[1].value = 101; + test_assert(v[2].id != 0); + test_int(0, v[2].value); + v[2].value = 102; + } + } + /* 3 resource(s) should have been used */ + test_int(3, initial_resources - resources_left()); + + /* Test copying: */ + ecs_entity_t instance = ecs_clone(world, 0, e, true); + { + const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_opaque); + { + test_int(3, ecs_vec_count(vec)); + const ResourceHandle *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_int(100, v[0].value); + test_int(101, v[1].value); + test_int(102, v[2].value); + } + } + /* 6 resource(s) should be in use now */ + test_int(6, initial_resources - resources_left()); + + /* Test moving by forcing an archetype change: */ + ECS_TAG(world, MakeMeMove); + ecs_add(world, e, MakeMeMove); + { + const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_opaque); + { + test_int(3, ecs_vec_count(vec)); + const ResourceHandle *v = ecs_vec_first(vec); + test_assert(v != NULL); + test_int(100, v[0].value); + test_int(101, v[1].value); + test_int(102, v[2].value); + } + } + /* 6 resource(s) should still be in use after a move */ + test_int(6, initial_resources - resources_left()); + + /* Test deleting: */ + ecs_delete(world, e); + + /* After destroying the first entity, only 3 resource(s) should be in use */ + test_int(3, initial_resources - resources_left()); + ecs_delete(world, instance); + + /* check if all 6 test resources were returned and exactly all resource ids + * are back: */ + test_int(initial_resources, resources_left()); + int i; + for (i = 1; i <= initial_resources; i++) { + test_assert(resource_id_available(i)); + } + + ecs_fini(world); + + free_resource_ids(); +} diff --git a/vendors/flecs/test/meta/src/SerializeEntityToJson.c b/vendors/flecs/test/meta/src/SerializeEntityToJson.c index c94085967..6c6c33a00 100644 --- a/vendors/flecs/test/meta/src/SerializeEntityToJson.c +++ b/vendors/flecs/test/meta/src/SerializeEntityToJson.c @@ -6,15 +6,12 @@ void SerializeEntityToJson_serialize_empty(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_id(world); - - char *str = ecs_os_strdup("{\"path\":\"446\", \"ids\":[]}"); - ecs_os_sprintf( - str, "{\"path\":\"%u\", \"ids\":[]}", (uint32_t)e); + ecs_entity_t e = ecs_new(world); + char *str = flecs_asprintf("{\"name\":\"#%u\"}", (uint32_t)e); char *json = ecs_entity_to_json(world, e, NULL); test_assert(json != NULL); - test_str(json, str); + test_json(json, str); ecs_os_free(json); ecs_os_free(str); @@ -24,11 +21,11 @@ void SerializeEntityToJson_serialize_empty(void) { void SerializeEntityToJson_serialize_w_name(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); char *json = ecs_entity_to_json(world, e, NULL); test_assert(json != NULL); - test_str(json, "{\"path\":\"Foo\", \"ids\":[]}"); + test_json(json, "{\"name\":\"Foo\"}"); ecs_os_free(json); ecs_fini(world); @@ -39,12 +36,12 @@ void SerializeEntityToJson_serialize_w_name_1_tag(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, Tag); char *json = ecs_entity_to_json(world, e, NULL); test_assert(json != NULL); - test_str(json, "{\"path\":\"Foo\", \"ids\":[[\"Tag\"]]}"); + test_json(json, "{\"name\":\"Foo\", \"tags\":[\"Tag\"]}"); ecs_os_free(json); ecs_fini(world); @@ -56,14 +53,14 @@ void SerializeEntityToJson_serialize_w_name_2_tags(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, TagA); ecs_add(world, e, TagB); char *json = ecs_entity_to_json(world, e, NULL); test_assert(json != NULL); - test_str(json, - "{\"path\":\"Foo\", \"ids\":[[\"TagA\"], [\"TagB\"]]}"); + test_json(json, + "{\"name\":\"Foo\", \"tags\":[\"TagA\", \"TagB\"]}"); ecs_os_free(json); ecs_fini(world); @@ -75,13 +72,13 @@ void SerializeEntityToJson_serialize_w_name_1_pair(void) { ECS_TAG(world, Rel); ECS_TAG(world, Obj); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add_pair(world, e, Rel, Obj); char *json = ecs_entity_to_json(world, e, NULL); test_assert(json != NULL); - test_str(json, - "{\"path\":\"Foo\", \"ids\":[[\"Rel\", \"Obj\"]]}"); + test_json(json, + "{\"name\":\"Foo\", \"pairs\":{\"Rel\":\"Obj\"}}"); ecs_os_free(json); ecs_fini(world); @@ -90,54 +87,101 @@ void SerializeEntityToJson_serialize_w_name_1_pair(void) { void SerializeEntityToJson_serialize_w_base(void) { ecs_world_t *world = ecs_init(); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); - ecs_entity_t base = ecs_new_entity(world, "Base"); + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); ecs_add(world, base, TagA); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add_pair(world, e, EcsIsA, base); ecs_add(world, e, TagB); - char *json = ecs_entity_to_json(world, e, NULL); + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true + }; + char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"is_a\":[{\"path\":\"Base\", \"ids\":[[\"TagA\"]]}], " - "\"ids\":[[\"TagB\"]]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"tags\":[\"TagB\"],\"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"tags\":[\"TagA\"]}}}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_w_base_override(void) { +void SerializeEntityToJson_serialize_w_base_dont_inherit_tag(void) { ecs_world_t *world = ecs_init(); - ECS_TAG(world, TagA); - ECS_TAG(world, TagB); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, DontInherit)); - ecs_entity_t base = ecs_new_entity(world, "Base"); + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); ecs_add(world, base, TagA); + ecs_add(world, base, TagB); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add_pair(world, e, EcsIsA, base); - ecs_add(world, e, TagA); - ecs_add(world, e, TagB); - ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_hidden = true; + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"tags\":[\"TagA\"]}}}"); + + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_w_base_dont_inherit_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsDontInherit); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_add(world, base, Position); + ecs_add(world, base, Velocity); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"Position\":null}}}}"); + + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_w_base_dont_inherit_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, RelA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelB, (OnInstantiate, DontInherit)); + ECS_TAG(world, Tgt); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_add_pair(world, base, RelA, Tgt); + ecs_add_pair(world, base, RelB, Tgt); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true + }; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"is_a\":[{\"path\":\"Base\", \"ids\":[[\"TagA\"]], \"hidden\":[true]}], " - "\"ids\":[" - "[\"TagA\"], [\"TagB\"]" - "]}"); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"pairs\":{\"RelA\":\"Tgt\"}}}}"); ecs_os_free(json); @@ -150,21 +194,210 @@ void SerializeEntityToJson_serialize_w_2_base(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t base_a = ecs_new_entity(world, "BaseA"); - ecs_entity_t base_b = ecs_new_entity(world, "BaseB"); + ecs_entity_t base_a = ecs_entity(world, { .name = "BaseA" }); + ecs_entity_t base_b = ecs_entity(world, { .name = "BaseB" }); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add_pair(world, e, EcsIsA, base_a); ecs_add_pair(world, e, EcsIsA, base_b); char *json = ecs_entity_to_json(world, e, NULL); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"is_a\":[{\"path\":\"BaseA\", \"ids\":[]}, " - "{\"path\":\"BaseB\", \"ids\":[]}], " - "\"ids\":[]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"flecs.core.IsA\":[\"BaseA\", \"BaseB\"]}}"); + + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_component_w_base(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true, + .serialize_values = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"Position\":{\"x\":10, \"y\":20}}}}}"); + + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_component_w_base_no_reflection_data(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true, + .serialize_values = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"Position\":null}}}}"); + + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_component_w_base_w_owned(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_entity_t ecs_id(Velocity) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + ecs_set(world, e, Velocity, {1, 2}); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true, + .serialize_values = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"Position\":{\"x\":10, \"y\":20}}}}, \"components\":{\"Velocity\":{\"x\":1, \"y\":2}}}"); + + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_component_w_base_w_owned_no_reflection_data(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + ecs_set(world, e, Velocity, {1, 2}); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true, + .serialize_values = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"Position\":null}}}, \"components\":{\"Velocity\":null}}"); + + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_component_w_base_w_owned_override(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_entity_t ecs_id(Velocity) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + ecs_set(world, e, Velocity, {1, 2}); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true, + .serialize_values = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"Position\":{\"x\":10, \"y\":20}}}}, \"components\":{\"Position\":{\"x\":10, \"y\":20}, \"Velocity\":{\"x\":1, \"y\":2}}}"); + + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_component_w_base_w_owned_no_reflection_data_override(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t base = ecs_new(world); + ecs_set(world, base, Position, {10, 20}); + /* Give base a name so we don't have to test unstable entity ids */ + ecs_set_name(world, base, "Base"); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsIsA, base); + ecs_set(world, e, Velocity, {1, 2}); + /* Give our test entity a name so we don't have to test unstable entity ids */ + ecs_set_name(world, e, "SomeEntity"); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true, + .serialize_values = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"SomeEntity\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"Position\":null}}}, \"components\":{\"Position\":null, \"Velocity\":null}}"); ecs_os_free(json); @@ -177,21 +410,16 @@ void SerializeEntityToJson_serialize_w_nested_base(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t base_of_base = ecs_new_entity(world, "BaseOfBase"); - ecs_entity_t base = ecs_new_entity(world, "Base"); + ecs_entity_t base_of_base = ecs_entity(world, { .name = "BaseOfBase" }); + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); ecs_add_pair(world, base, EcsIsA, base_of_base); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add_pair(world, e, EcsIsA, base); char *json = ecs_entity_to_json(world, e, NULL); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"is_a\":[{\"path\":\"BaseOfBase\", \"ids\":[]}, " - "{\"path\":\"Base\", \"ids\":[]}], " - "\"ids\":[]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"flecs.core.IsA\":\"Base\"}}"); ecs_os_free(json); @@ -201,7 +429,7 @@ void SerializeEntityToJson_serialize_w_nested_base(void) { void SerializeEntityToJson_serialize_w_1_component(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + ecs_entity_t ecs_id(Position) = ecs_struct(world, { .entity = ecs_entity(world, {.name = "Position"}), .members = { {"x", ecs_id(ecs_i32_t)}, @@ -209,7 +437,7 @@ void SerializeEntityToJson_serialize_w_1_component(void) { } }); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, Position, {10, 20}); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -217,11 +445,7 @@ void SerializeEntityToJson_serialize_w_1_component(void) { char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"Position\"]], " - "\"values\":[{\"x\":10, \"y\":20}]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"components\":{\"Position\":{\"x\":10, \"y\":20}}}"); ecs_os_free(json); @@ -231,7 +455,7 @@ void SerializeEntityToJson_serialize_w_1_component(void) { void SerializeEntityToJson_serialize_w_2_components(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + ecs_entity_t ecs_id(Position) = ecs_struct(world, { .entity = ecs_entity(world, {.name = "Position"}), .members = { {"x", ecs_id(ecs_i32_t)}, @@ -239,14 +463,14 @@ void SerializeEntityToJson_serialize_w_2_components(void) { } }); - ecs_entity_t ecs_id(Mass) = ecs_struct_init(world, &(ecs_struct_desc_t){ + ecs_entity_t ecs_id(Mass) = ecs_struct(world, { .entity = ecs_entity(world, {.name = "Mass"}), .members = { {"value", ecs_id(ecs_i32_t)} } }); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, Position, {10, 20}); ecs_set(world, e, Mass, {1234}); @@ -255,11 +479,7 @@ void SerializeEntityToJson_serialize_w_2_components(void) { char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"Position\"], [\"Mass\"]], " - "\"values\":[{\"x\":10, \"y\":20}, {\"value\":1234}]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"components\":{\"Position\":{\"x\":10, \"y\":20}, \"Mass\":{\"value\":1234}}}"); ecs_os_free(json); @@ -269,7 +489,7 @@ void SerializeEntityToJson_serialize_w_2_components(void) { void SerializeEntityToJson_serialize_w_primitive_component(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, ecs_i32_t, {10}); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -277,11 +497,7 @@ void SerializeEntityToJson_serialize_w_primitive_component(void) { char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"flecs.meta.i32\"]], " - "\"values\":[10]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"components\":{\"flecs.meta.i32\":10}}"); ecs_os_free(json); @@ -304,7 +520,7 @@ void SerializeEntityToJson_serialize_w_enum_component(void) { test_assert(ecs_id(Color) != 0); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, Color, {1}); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -312,11 +528,7 @@ void SerializeEntityToJson_serialize_w_enum_component(void) { char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"Color\"]], " - "\"values\":[\"Blue\"]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"components\":{\"Color\":\"Blue\"}}"); ecs_os_free(json); @@ -330,7 +542,7 @@ void SerializeEntityToJson_serialize_w_struct_and_enum_component(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + ecs_entity_t ecs_id(Position) = ecs_struct(world, { .entity = ecs_entity(world, {.name = "Position"}), .members = { {"x", ecs_id(ecs_i32_t)}, @@ -349,7 +561,7 @@ void SerializeEntityToJson_serialize_w_struct_and_enum_component(void) { test_assert(ecs_id(Color) != 0); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, Color, {1}); ecs_set(world, e, Position, {10, 20}); @@ -358,11 +570,7 @@ void SerializeEntityToJson_serialize_w_struct_and_enum_component(void) { char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"Position\"], [\"Color\"]], " - "\"values\":[{\"x\":10, \"y\":20}, \"Blue\"]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"components\":{\"Position\":{\"x\":10, \"y\":20}, \"Color\":\"Blue\"}}"); ecs_os_free(json); @@ -385,7 +593,7 @@ void SerializeEntityToJson_serialize_w_invalid_enum_component(void) { test_assert(ecs_id(Color) != 0); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, Color, {100}); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -401,7 +609,7 @@ void SerializeEntityToJson_serialize_w_invalid_enum_component(void) { void SerializeEntityToJson_serialize_w_type_info(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + ecs_entity_t ecs_id(Position) = ecs_struct(world, { .entity = ecs_entity(world, {.name = "Position"}), .members = { {"x", ecs_id(ecs_i32_t)}, @@ -409,7 +617,7 @@ void SerializeEntityToJson_serialize_w_type_info(void) { } }); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, Position, {10, 20}); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -418,12 +626,7 @@ void SerializeEntityToJson_serialize_w_type_info(void) { char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"Position\"]], " - "\"values\":[{\"x\":10, \"y\":20}], " - "\"type_info\":[{\"x\":[\"int\"], \"y\":[\"int\"]}]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"type_info\":{\"Position\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, \"components\":{\"Position\":{\"x\":10, \"y\":20}}}"); ecs_os_free(json); @@ -443,7 +646,7 @@ void SerializeEntityToJson_serialize_w_type_info_unit(void) { }); test_assert(u != 0); - ecs_entity_t ecs_id(T) = ecs_struct_init(world, &(ecs_struct_desc_t){ + ecs_entity_t ecs_id(T) = ecs_struct(world, { .entity = ecs_entity(world, {.name = "T"}), .members = { {"value", ecs_id(ecs_i32_t), .unit = u} @@ -451,7 +654,7 @@ void SerializeEntityToJson_serialize_w_type_info_unit(void) { }); test_assert(ecs_id(T) != 0); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, T, {24}); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -460,14 +663,7 @@ void SerializeEntityToJson_serialize_w_type_info_unit(void) { char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"T\"]], " - "\"values\":[{\"value\":24}], " - "\"type_info\":[{\"value\":[\"int\", {" - "\"unit\":\"celsius\", \"symbol\":\"°\"}]" - "}]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"type_info\":{\"T\":{\"value\":[\"int\", {\"unit\":\"celsius\", \"symbol\":\"°\"}]}}, \"components\":{\"T\":{\"value\":24}}}"); ecs_os_free(json); @@ -493,7 +689,7 @@ void SerializeEntityToJson_serialize_w_type_info_unit_quantity(void) { }); test_assert(u != 0); - ecs_entity_t ecs_id(T) = ecs_struct_init(world, &(ecs_struct_desc_t){ + ecs_entity_t ecs_id(T) = ecs_struct(world, { .entity = ecs_entity(world, {.name = "T"}), .members = { {"value", ecs_id(ecs_i32_t), .unit = u} @@ -501,7 +697,7 @@ void SerializeEntityToJson_serialize_w_type_info_unit_quantity(void) { }); test_assert(ecs_id(T) != 0); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, T, {24}); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -510,14 +706,7 @@ void SerializeEntityToJson_serialize_w_type_info_unit_quantity(void) { char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"T\"]], " - "\"values\":[{\"value\":24}], " - "\"type_info\":[{\"value\":[\"int\", {" - "\"unit\":\"celsius\", \"symbol\":\"°\", \"quantity\":\"temperature\"}]" - "}]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"type_info\":{\"T\":{\"value\":[\"int\", {\"unit\":\"celsius\", \"symbol\":\"°\", \"quantity\":\"temperature\"}]}}, \"components\":{\"T\":{\"value\":24}}}"); ecs_os_free(json); @@ -551,7 +740,7 @@ void SerializeEntityToJson_serialize_w_type_info_unit_over(void) { }); test_assert(u_3 != 0); - ecs_entity_t ecs_id(T) = ecs_struct_init(world, &(ecs_struct_desc_t){ + ecs_entity_t ecs_id(T) = ecs_struct(world, { .entity = ecs_entity(world, {.name = "T"}), .members = { {"value", ecs_id(ecs_i32_t), .unit = u_3} @@ -559,7 +748,7 @@ void SerializeEntityToJson_serialize_w_type_info_unit_over(void) { }); test_assert(ecs_id(T) != 0); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e, T, {24}); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -569,62 +758,54 @@ void SerializeEntityToJson_serialize_w_type_info_unit_over(void) { char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"T\"]], " - "\"values\":[{\"value\":24}], " - "\"type_info\":[{\"value\":[\"int\", {" - "\"unit\":\"meters_per_second\", \"symbol\":\"m/s\"}]" - "}]" - "}"); + test_json(json, "{\"name\":\"Foo\", \"type_info\":{\"T\":{\"value\":[\"int\", {\"unit\":\"meters_per_second\", \"symbol\":\"m/s\"}]}}, \"components\":{\"T\":{\"value\":24}}}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_wo_private(void) { +void SerializeEntityToJson_serialize_w_type_info_no_types(void) { ecs_world_t *world = ecs_init(); - ECS_TAG(world, Tag); + ECS_COMPONENT(world, Position); - ecs_add_id(world, Tag, EcsPrivate); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_set(world, e, Position, {10, 20}); - ecs_entity_t e = ecs_new_entity(world, "Foo"); - ecs_add(world, e, Tag); + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_values = true; + desc.serialize_type_info = true; - char *json = ecs_entity_to_json(world, e, NULL); + char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[]}"); + test_json(json, "{\"name\":\"Foo\", \"type_info\":{\"Position\":0}, \"components\":{\"Position\":null}}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_w_private(void) { +void SerializeEntityToJson_serialize_w_type_info_no_components(void) { ecs_world_t *world = ecs_init(); - ECS_TAG(world, Tag); - - ecs_add_id(world, Tag, EcsPrivate); + ECS_TAG(world, Foo); - ecs_entity_t e = ecs_new_entity(world, "Foo"); - ecs_add(world, e, Tag); + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Foo); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_private = true; + desc.serialize_values = true; + desc.serialize_type_info = true; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[[\"Tag\"], [\"flecs.core.Identifier\", \"flecs.core.Name\"]]}"); + char *expect = flecs_asprintf( + "{\"name\":\"#%u\", \"tags\":[\"Foo\"],\"type_info\":{}}", (uint32_t)e); + test_json(json, expect); ecs_os_free(json); + ecs_os_free(expect); ecs_fini(world); } @@ -634,21 +815,16 @@ void SerializeEntityToJson_serialize_w_label(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, Tag); ecs_doc_set_name(world, e, "My name"); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_label = true; + desc.serialize_doc = true; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"label\":\"My name\", " - "\"ids\":[" - "[\"Tag\"]" - "]}"); + test_json(json, "{\"name\":\"Foo\", \"doc\":{\"label\":\"My name\"}, \"tags\":[\"Tag\"], \"components\":{\"(flecs.doc.Description,flecs.core.Name)\":{\"value\":\"My name\"}}}"); ecs_os_free(json); @@ -660,33 +836,17 @@ void SerializeEntityToJson_serialize_w_label_no_name(void) { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_id(world); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Tag); - char *str = ecs_os_malloc( - ecs_os_strlen( - "{" - "\"path\":\"000\", " - "\"label\":\"000\", " - "\"ids\":[" - "[\"Tag\"]" - "]}" - ) + 1 - ); - - ecs_os_sprintf(str, - "{" - "\"path\":\"%u\", " - "\"label\":\"%u\", " - "\"ids\":[" - "[\"Tag\"]" - "]}", (uint32_t)e, (uint32_t)e); + char *str = flecs_asprintf("{\"name\":\"#%u\", \"doc\":{\"label\":\"#%u\"}, \"tags\":[\"Tag\"]}", + (uint32_t)e, (uint32_t)e); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_label = true; + desc.serialize_doc = true; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, str); + test_json(json, str); ecs_os_free(json); ecs_os_free(str); @@ -694,430 +854,235 @@ void SerializeEntityToJson_serialize_w_label_no_name(void) { ecs_fini(world); } -void SerializeEntityToJson_serialize_w_id_labels(void) { +void SerializeEntityToJson_serialize_w_brief(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, Tag); - ecs_doc_set_name(world, Tag, "My tag"); + ecs_doc_set_brief(world, e, "Brief description"); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_id_labels = true; + desc.serialize_doc = true; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[" - "[\"Tag\"]" - "], " - "\"id_labels\":[" - "[\"My tag\"]" - "]}"); + test_json(json, "{\"name\":\"Foo\", \"doc\":{\"label\":\"Foo\", \"brief\":\"Brief description\"}, \"tags\":[\"Tag\"], \"components\":{\"(flecs.doc.Description,flecs.doc.Brief)\":{\"value\":\"Brief description\"}}}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_w_brief(void) { +void SerializeEntityToJson_serialize_w_brief_no_brief(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, Tag); - ecs_doc_set_brief(world, e, "Brief description"); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_brief = true; + desc.serialize_doc = true; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"brief\":\"Brief description\", " - "\"ids\":[" - "[\"Tag\"]" - "]}"); + test_json(json, "{\"name\":\"Foo\", \"doc\":{\"label\":\"Foo\"}, \"tags\":[\"Tag\"]}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_w_brief_no_brief(void) { +void SerializeEntityToJson_serialize_w_link(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, Tag); + ecs_doc_set_link(world, e, "Link"); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_brief = true; + desc.serialize_doc = true; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[" - "[\"Tag\"]" - "]}"); + test_json(json, "{\"name\":\"Foo\", \"doc\":{\"label\":\"Foo\", \"link\":\"Link\"}, \"tags\":[\"Tag\"], \"components\":{\"(flecs.doc.Description,flecs.doc.Link)\":{\"value\":\"Link\"}}}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_w_link(void) { +void SerializeEntityToJson_serialize_w_link_no_link(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, Tag); - ecs_doc_set_link(world, e, "Link"); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_link = true; + desc.serialize_doc = true; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"link\":\"Link\", " - "\"ids\":[" - "[\"Tag\"]" - "]}"); + test_json(json, "{\"name\":\"Foo\", \"doc\":{\"label\":\"Foo\"}, \"tags\":[\"Tag\"]}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_w_link_no_link(void) { +void SerializeEntityToJson_serialize_color(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add(world, e, Tag); + ecs_doc_set_color(world, e, "#47B576"); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_link = true; + desc.serialize_doc = true; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[" - "[\"Tag\"]" - "]}"); + test_json(json, "{\"name\":\"Foo\", \"doc\":{\"label\":\"Foo\", \"color\":\"#47B576\"}, \"tags\":[\"Tag\"], \"components\":{\"(flecs.doc.Description,flecs.doc.Color)\":{\"value\":\"#47B576\"}}}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_color(void) { +void SerializeEntityToJson_serialize_w_doc_w_quotes(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); + ecs_entity_t e = ecs_entity(world, { .name = "Child" }); ecs_add(world, e, Tag); - ecs_doc_set_color(world, e, "#47B576"); + ecs_add_pair(world, e, EcsChildOf, p); + ecs_doc_set_name(world, e, "Doc \"name\""); + ecs_doc_set_brief(world, e, "Doc \"brief\""); + ecs_doc_set_link(world, e, "Doc \"link\""); + ecs_doc_set_color(world, e, "Doc \"color\""); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_color = true; + desc.serialize_doc = true; char *json = ecs_entity_to_json(world, e, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"color\":\"#47B576\", " - "\"ids\":[" - "[\"Tag\"]" - "]}"); + test_json(json, "{\"parent\":\"Parent\", \"name\":\"Child\", \"doc\":{\"label\":\"Doc \\\"name\\\"\", \"brief\":\"Doc \\\"brief\\\"\", \"color\":\"Doc \\\"color\\\"\", \"link\":\"Doc \\\"link\\\"\"}, \"tags\":[\"Tag\"], \"components\":{\"(flecs.doc.Description,flecs.core.Name)\":{\"value\":\"Doc \\\"name\\\"\"}, \"(flecs.doc.Description,flecs.doc.Brief)\":{\"value\":\"Doc \\\"brief\\\"\"}, \"(flecs.doc.Description,flecs.doc.Link)\":{\"value\":\"Doc \\\"link\\\"\"}, \"(flecs.doc.Description,flecs.doc.Color)\":{\"value\":\"Doc \\\"color\\\"\"}}}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_union_relationship(void) { +void SerializeEntityToJson_serialize_from_core(void) { ecs_world_t *world = ecs_init(); - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Running); - ECS_TAG(world, Walking); - - ecs_entity_t e = ecs_new_entity(world, "Foo"); - ecs_add_pair(world, e, Movement, Running); - - ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - char *json = ecs_entity_to_json(world, e, &desc); + char *json = ecs_entity_to_json(world, EcsWorld, NULL); test_assert(json != NULL); - - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[" - "[\"Movement\", \"Running\"]" - "]}"); - + test_json(json, "{\"parent\":\"flecs.core\", \"name\":\"World\", \"components\":{\"(flecs.core.Identifier,flecs.core.Symbol)\":null, \"(flecs.doc.Description,flecs.doc.Brief)\":{\"value\":\"Entity associated with world\"}}}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_union_relationship_invalid_entity(void) { +void SerializeEntityToJson_serialize_w_1_alert(void) { ecs_world_t *world = ecs_init(); - ECS_ENTITY(world, Movement, Union); - ecs_entity_t Running = ecs_new_entity(world, "Running"); + ECS_IMPORT(world, FlecsAlerts); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t alert = ecs_alert(world, { + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", + .message = "$this has Position but not Velocity" + }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t e = ecs_new_entity(world, "Foo"); - ecs_add_pair(world, e, Movement, Running); - ecs_delete(world, Running); + ecs_progress(world, 1.0); /* Evaluate alert logic */ + + /* Give alert instance name so we don't have to test unstable entity ids */ + ecs_entity_t ai = ecs_get_alert(world, e1, alert); + test_assert(ai != 0); + ecs_set_name(world, ai, "e1_alert"); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - char *json = ecs_entity_to_json(world, e, &desc); + desc.serialize_alerts = true; + char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[]}"); - + test_json(json, "{\"name\":\"e1\", \"has_alerts\":true, \"components\":{\"flecs.alerts.AlertsActive\":{\"info_count\":0, \"warning_count\":0, \"error_count\":1}, \"Position\":null}, \"alerts\":[{\"alert\":\"position_without_velocity.e1_alert\", \"message\":\"e1 has Position but not Velocity\", \"severity\":\"Error\"}]}"); ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_union_relationship_invalid_entity_w_labels(void) { +void SerializeEntityToJson_serialize_w_2_alerts(void) { ecs_world_t *world = ecs_init(); - ECS_ENTITY(world, Movement, Union); - ecs_entity_t Running = ecs_new_entity(world, "Running"); + ECS_IMPORT(world, FlecsAlerts); - ecs_entity_t e = ecs_new_entity(world, "Foo"); - ecs_add_pair(world, e, Movement, Running); - ecs_delete(world, Running); + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); - ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_id_labels = true; - char *json = ecs_entity_to_json(world, e, &desc); - test_assert(json != NULL); + ecs_entity_t alert_1 = ecs_alert(world, { + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", + .message = "$this has Position but not Velocity" + }); - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[], " - "\"id_labels\":[]}"); + ecs_entity_t alert_2 = ecs_alert(world, { + .entity = ecs_entity(world, { .name = "position_without_mass" }), + .query.expr = "Position, !Mass", + .message = "$this has Position but not Mass" + }); - ecs_os_free(json); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e1, Position, {10, 20}); - ecs_fini(world); -} + ecs_progress(world, 1.0); /* Evaluate alert logic */ -void SerializeEntityToJson_serialize_w_union_property(void) { - ecs_world_t *world = ecs_init(); + /* Give alert instance name so we don't have to test unstable entity ids */ + ecs_entity_t ai = ecs_get_alert(world, e1, alert_1); + test_assert(ai != 0); + ecs_set_name(world, ai, "e1_alert_1"); - ECS_ENTITY(world, Movement, Union); + ai = ecs_get_alert(world, e1, alert_2); + test_assert(ai != 0); + ecs_set_name(world, ai, "e1_alert_2"); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - char *json = ecs_entity_to_json(world, Movement, &desc); + desc.serialize_alerts = true; + char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"Movement\", " - "\"ids\":[" - "[\"flecs.core.Union\"]" - "]}"); + /* Since alerts can come in any order, test against the two possibilities: */ + const char* alerts_option1 = "{\"name\":\"e1\", \"has_alerts\":true, \"components\":{\"flecs.alerts.AlertsActive\":{\"info_count\":0, \"warning_count\":0, \"error_count\":2}, \"Position\":null}, \"alerts\":[{\"alert\":\"position_without_velocity.e1_alert_1\", \"message\":\"e1 has Position but not Velocity\", \"severity\":\"Error\"}, {\"alert\":\"position_without_mass.e1_alert_2\", \"message\":\"e1 has Position but not Mass\", \"severity\":\"Error\"}]}"; + const char* alerts_option2 = "{\"name\":\"e1\", \"has_alerts\":true, \"components\":{\"flecs.alerts.AlertsActive\":{\"info_count\":0, \"warning_count\":0, \"error_count\":2}, \"Position\":null}, \"alerts\":[{\"alert\":\"position_without_mass.e1_alert_2\", \"message\":\"e1 has Position but not Mass\", \"severity\":\"Error\"}, {\"alert\":\"position_without_velocity.e1_alert_1\", \"message\":\"e1 has Position but not Velocity\", \"severity\":\"Error\"}]}"; + + if(ecs_os_strcmp(json, alerts_option1) && ecs_os_strcmp(json, alerts_option2)) { + // neither matched, so throw an assert. + test_json(json, alerts_option1); + } ecs_os_free(json); ecs_fini(world); } -void SerializeEntityToJson_serialize_union_relationship_w_labels(void) { - ecs_world_t *world = ecs_init(); - - ECS_ENTITY(world, Movement, Union); - ECS_TAG(world, Running); - ECS_TAG(world, Walking); - - ecs_entity_t e = ecs_new_entity(world, "Foo"); - ecs_add_pair(world, e, Movement, Running); - - ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_id_labels = true; - char *json = ecs_entity_to_json(world, e, &desc); - test_assert(json != NULL); - - test_str(json, "{" - "\"path\":\"Foo\", " - "\"ids\":[" - "[\"Movement\", \"Running\"]" - "], " - "\"id_labels\":[" - "[\"Movement\", \"Running\"]" - "]}"); - - ecs_os_free(json); - - ecs_fini(world); -} - -void SerializeEntityToJson_serialize_w_doc_w_quotes(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_entity_t p = ecs_new_entity(world, "Parent"); - ecs_entity_t e = ecs_new_entity(world, "Child"); - ecs_add(world, e, Tag); - ecs_add_pair(world, e, EcsChildOf, p); - ecs_doc_set_name(world, e, "Doc \"name\""); - ecs_doc_set_brief(world, e, "Doc \"brief\""); - ecs_doc_set_link(world, e, "Doc \"link\""); - ecs_doc_set_color(world, e, "Doc \"color\""); - - ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_label = true; - desc.serialize_brief = true; - desc.serialize_color = true; - desc.serialize_link = true; - char *json = ecs_entity_to_json(world, e, &desc); - test_assert(json != NULL); - - test_str(json, "{" - "\"path\":\"Parent.Child\", " - "\"label\":\"Doc \\\"name\\\"\", " - "\"brief\":\"Doc \\\"brief\\\"\", " - "\"link\":\"Doc \\\"link\\\"\", " - "\"color\":\"Doc \\\"color\\\"\", " - "\"ids\":[[\"Tag\"]]" - "}"); - - ecs_os_free(json); - - ecs_fini(world); -} - -void SerializeEntityToJson_serialize_from_core(void) { - ecs_world_t *world = ecs_init(); - - char *json = ecs_entity_to_json(world, EcsWorld, NULL); - test_assert(json != NULL); - test_str(json, "{\"path\":\"flecs.core.World\", \"ids\":[]}"); - ecs_os_free(json); - - ecs_fini(world); -} - -void SerializeEntityToJson_serialize_w_1_alert(void) { - ecs_world_t *world = ecs_init(); - - ECS_IMPORT(world, FlecsAlerts); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - - ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", - .message = "$this has Position but not Velocity" - }); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_set(world, e1, Position, {10, 20}); - - ecs_progress(world, 1.0); /* Evaluate alert logic */ - - /* Give alert instance name so we don't have to test unstable entity ids */ - ecs_entity_t ai = ecs_get_alert(world, e1, alert); - test_assert(ai != 0); - ecs_set_name(world, ai, "e1_alert"); - - ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_alerts = true; - char *json = ecs_entity_to_json(world, e1, &desc); - test_assert(json != NULL); - - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"Position\"]], " - "\"alerts\":[{" - "\"alert\":\"position_without_velocity.e1_alert\", " - "\"message\":\"e1 has Position but not Velocity\", " - "\"severity\":\"Error\"" - "}]" - "}"); - ecs_os_free(json); - - ecs_fini(world); -} - -void SerializeEntityToJson_serialize_w_2_alerts(void) { - ecs_world_t *world = ecs_init(); - - ECS_IMPORT(world, FlecsAlerts); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - - ecs_entity_t alert_1 = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", - .message = "$this has Position but not Velocity" - }); - - ecs_entity_t alert_2 = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_mass"), - .filter.expr = "Position, !Mass", - .message = "$this has Position but not Mass" - }); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_set(world, e1, Position, {10, 20}); - - ecs_progress(world, 1.0); /* Evaluate alert logic */ - - /* Give alert instance name so we don't have to test unstable entity ids */ - ecs_entity_t ai = ecs_get_alert(world, e1, alert_1); - test_assert(ai != 0); - ecs_set_name(world, ai, "e1_alert_1"); - - ai = ecs_get_alert(world, e1, alert_2); - test_assert(ai != 0); - ecs_set_name(world, ai, "e1_alert_2"); - - ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_alerts = true; - char *json = ecs_entity_to_json(world, e1, &desc); - test_assert(json != NULL); - - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"Position\"]], " - "\"alerts\":[{" - "\"alert\":\"position_without_mass.e1_alert_2\", " - "\"message\":\"e1 has Position but not Mass\", " - "\"severity\":\"Error\"" - "}, {" - "\"alert\":\"position_without_velocity.e1_alert_1\", " - "\"message\":\"e1 has Position but not Velocity\", " - "\"severity\":\"Error\"" - "}]" - "}"); - ecs_os_free(json); - - ecs_fini(world); -} - void SerializeEntityToJson_serialize_w_child_alerts(void) { ecs_world_t *world = ecs_init(); @@ -1127,23 +1092,23 @@ void SerializeEntityToJson_serialize_w_child_alerts(void) { ECS_COMPONENT(world, Velocity); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .message = "$this has Position but not Velocity" }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); - ecs_entity_t child_1 = ecs_new_entity(world, "child1"); + ecs_entity_t child_1 = ecs_entity(world, { .name = "child1" }); ecs_set(world, child_1, Position, {10, 20}); ecs_add_pair(world, child_1, EcsChildOf, e1); - ecs_entity_t child_2 = ecs_new_entity(world, "child2"); + ecs_entity_t child_2 = ecs_entity(world, { .name = "child2" }); ecs_set(world, child_2, Position, {10, 20}); ecs_add_pair(world, child_2, EcsChildOf, e1); - ecs_entity_t grandchild_1 = ecs_new_entity(world, "grandchild1"); + ecs_entity_t grandchild_1 = ecs_entity(world, { .name = "grandchild1" }); ecs_set(world, grandchild_1, Position, {10, 20}); ecs_add_pair(world, grandchild_1, EcsChildOf, child_1); @@ -1171,30 +1136,7 @@ void SerializeEntityToJson_serialize_w_child_alerts(void) { char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"Position\"]], " - "\"alerts\":[{" - "\"alert\":\"position_without_velocity.e1_alert\", " - "\"message\":\"e1 has Position but not Velocity\", " - "\"severity\":\"Error\"" - "}, {" - "\"alert\":\"position_without_velocity.child1_alert\", " - "\"message\":\"e1.child1 has Position but not Velocity\", " - "\"severity\":\"Error\", " - "\"path\":\"e1.child1\"" - "}, {" - "\"alert\":\"position_without_velocity.grandchild1_alert\", " - "\"message\":\"e1.child1.grandchild1 has Position but not Velocity\", " - "\"severity\":\"Error\", " - "\"path\":\"e1.child1.grandchild1\"" - "}, {" - "\"alert\":\"position_without_velocity.child2_alert\", " - "\"message\":\"e1.child2 has Position but not Velocity\", " - "\"severity\":\"Error\", " - "\"path\":\"e1.child2\"" - "}]" - "}"); + test_json(json, "{\"name\":\"e1\", \"has_alerts\":true, \"components\":{\"flecs.alerts.AlertsActive\":{\"info_count\":0, \"warning_count\":0, \"error_count\":1}, \"Position\":null}, \"alerts\":[{\"alert\":\"position_without_velocity.e1_alert\", \"message\":\"e1 has Position but not Velocity\", \"severity\":\"Error\"}, {\"alert\":\"position_without_velocity.child1_alert\", \"message\":\"e1.child1 has Position but not Velocity\", \"severity\":\"Error\", \"path\":\"e1.child1\"}, {\"alert\":\"position_without_velocity.grandchild1_alert\", \"message\":\"e1.child1.grandchild1 has Position but not Velocity\", \"severity\":\"Error\", \"path\":\"e1.child1.grandchild1\"}, {\"alert\":\"position_without_velocity.child2_alert\", \"message\":\"e1.child2 has Position but not Velocity\", \"severity\":\"Error\", \"path\":\"e1.child2\"}]}"); ecs_os_free(json); ecs_fini(world); @@ -1210,8 +1152,8 @@ void SerializeEntityToJson_serialize_w_severity_filter_alert(void) { ECS_TAG(world, Tag); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", .message = "$this has Position but not Velocity", .severity_filters[0] = { .severity = EcsAlertWarning, @@ -1219,7 +1161,7 @@ void SerializeEntityToJson_serialize_w_severity_filter_alert(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); ecs_add(world, e1, Tag); @@ -1235,15 +1177,7 @@ void SerializeEntityToJson_serialize_w_severity_filter_alert(void) { char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"Position\"], [\"Tag\"]], " - "\"alerts\":[{" - "\"alert\":\"position_without_velocity.e1_alert\", " - "\"message\":\"e1 has Position but not Velocity\", " - "\"severity\":\"Warning\"" - "}]" - "}"); + test_json(json, "{\"name\":\"e1\", \"has_alerts\":true, \"tags\":[\"Tag\"], \"components\":{\"flecs.alerts.AlertsActive\":{\"info_count\":0, \"warning_count\":0, \"error_count\":1}, \"Position\":null}, \"alerts\":[{\"alert\":\"position_without_velocity.e1_alert\", \"message\":\"e1 has Position but not Velocity\", \"severity\":\"Warning\"}]}"); ecs_os_free(json); ecs_fini(world); @@ -1254,7 +1188,7 @@ void SerializeEntityToJson_serialize_w_alerts_not_imported(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); ecs_new_w_pair(world, EcsChildOf, e1); @@ -1263,10 +1197,7 @@ void SerializeEntityToJson_serialize_w_alerts_not_imported(void) { char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"Position\"]]" - "}"); + test_json(json, "{\"name\":\"e1\", \"components\":{\"Position\":null}}"); ecs_os_free(json); ecs_fini(world); @@ -1282,11 +1213,11 @@ void SerializeEntityToJson_serialize_w_alerts_no_message(void) { ECS_TAG(world, Tag); ecs_entity_t alert = ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity" + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity" }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); ecs_add(world, e1, Tag); @@ -1302,14 +1233,7 @@ void SerializeEntityToJson_serialize_w_alerts_no_message(void) { char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"Position\"], [\"Tag\"]], " - "\"alerts\":[{" - "\"alert\":\"position_without_velocity.e1_alert\", " - "\"severity\":\"Error\"" - "}]" - "}"); + test_json(json, "{\"name\":\"e1\", \"has_alerts\":true, \"tags\":[\"Tag\"], \"components\":{\"flecs.alerts.AlertsActive\":{\"info_count\":0, \"warning_count\":0, \"error_count\":1}, \"Position\":null}, \"alerts\":[{\"alert\":\"position_without_velocity.e1_alert\", \"severity\":\"Error\"}]}"); ecs_os_free(json); ecs_fini(world); @@ -1320,15 +1244,15 @@ void SerializeEntityToJson_serialize_refs_childof(void) { ECS_TAG(world, Rel); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_entity_t child_1 = ecs_new_entity(world, "child1"); + ecs_entity_t child_1 = ecs_entity(world, { .name = "child1" }); ecs_add_pair(world, child_1, EcsChildOf, e1); - ecs_entity_t child_2 = ecs_new_entity(world, "child2"); + ecs_entity_t child_2 = ecs_entity(world, { .name = "child2" }); ecs_add_pair(world, child_2, EcsChildOf, e1); - ecs_entity_t child_3 = ecs_new_entity(world, "child3"); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child3" }); ecs_add_pair(world, child_3, Rel, e1); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -1336,16 +1260,7 @@ void SerializeEntityToJson_serialize_refs_childof(void) { char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[], " - "\"refs\":{" - "\"ChildOf\":[" - "\"e1.child1\", " - "\"e1.child2\"" - "]" - "}" - "}"); + test_json(json, "{\"name\":\"e1\", \"refs\":{\"ChildOf\":[\"e1.child1\", \"e1.child2\"]}}"); ecs_os_free(json); ecs_fini(world); @@ -1356,15 +1271,15 @@ void SerializeEntityToJson_serialize_refs_custom(void) { ECS_TAG(world, Rel); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_entity_t child_1 = ecs_new_entity(world, "child1"); + ecs_entity_t child_1 = ecs_entity(world, { .name = "child1" }); ecs_add_pair(world, child_1, Rel, e1); - ecs_entity_t child_2 = ecs_new_entity(world, "child2"); + ecs_entity_t child_2 = ecs_entity(world, { .name = "child2" }); ecs_add_pair(world, child_2, Rel, e1); - ecs_entity_t child_3 = ecs_new_entity(world, "child3"); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child3" }); ecs_add_pair(world, child_3, EcsChildOf, e1); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -1372,16 +1287,7 @@ void SerializeEntityToJson_serialize_refs_custom(void) { char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[], " - "\"refs\":{" - "\"Rel\":[" - "\"child1\", " - "\"child2\"" - "]" - "}" - "}"); + test_json(json, "{\"name\":\"e1\", \"refs\":{\"Rel\":[\"child1\", \"child2\"]}}"); ecs_os_free(json); ecs_fini(world); @@ -1392,15 +1298,15 @@ void SerializeEntityToJson_serialize_refs_wildcard(void) { ECS_TAG(world, Rel); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_entity_t child_1 = ecs_new_entity(world, "child1"); + ecs_entity_t child_1 = ecs_entity(world, { .name = "child1" }); ecs_add_pair(world, child_1, Rel, e1); - ecs_entity_t child_2 = ecs_new_entity(world, "child2"); + ecs_entity_t child_2 = ecs_entity(world, { .name = "child2" }); ecs_add_pair(world, child_2, Rel, e1); - ecs_entity_t child_3 = ecs_new_entity(world, "child3"); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child3" }); ecs_add_pair(world, child_3, EcsChildOf, e1); ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; @@ -1408,37 +1314,7 @@ void SerializeEntityToJson_serialize_refs_wildcard(void) { char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[], " - "\"refs\":{" - "\"ChildOf\":[" - "\"e1.child3\"" - "], " - "\"Rel\":[" - "\"child1\", " - "\"child2\"" - "]" - "}" - "}"); - ecs_os_free(json); - - ecs_fini(world); -} - -void SerializeEntityToJson_serialize_no_ids(void) { - ecs_world_t *world = ecs_init(); - - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new_entity(world, "Foo"); - ecs_add(world, e, Tag); - - ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - desc.serialize_ids = false; - char *json = ecs_entity_to_json(world, e, &desc); - test_assert(json != NULL); - test_str(json, "{\"path\":\"Foo\"}"); + test_json(json, "{\"name\":\"e1\", \"refs\":{\"ChildOf\":[\"e1.child3\"], \"Rel\":[\"child1\", \"child2\"]}}"); ecs_os_free(json); ecs_fini(world); @@ -1451,24 +1327,24 @@ void SerializeEntityToJson_serialize_matches_filter(void) { ECS_TAG(world, TagB); ECS_TAG(world, TagC); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, TagA); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e2, TagB); - ecs_filter_t *f_a = ecs_filter(world, { + ecs_query_t *f_a = ecs_query(world, { .entity = ecs_entity(world, { .name = "f_a"} ), .terms = {{ TagA }} }); test_assert(f_a != NULL); - ecs_filter_t *f_b = ecs_filter(world, { + ecs_query_t *f_b = ecs_query(world, { .entity = ecs_entity(world, { .name = "f_b"} ), .terms = {{ TagB }} }); test_assert(f_b != NULL); - ecs_filter_t *f_c = ecs_filter(world, { + ecs_query_t *f_c = ecs_query(world, { .entity = ecs_entity(world, { .name = "f_c"} ), .terms = {{ TagC }} }); @@ -1478,18 +1354,12 @@ void SerializeEntityToJson_serialize_matches_filter(void) { desc.serialize_matches = true; char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"TagA\"]], " - "\"matches\":[" - "\"f_a\"" - "]" - "}"); + test_json(json, "{\"name\":\"e1\", \"tags\":[\"TagA\"], \"matches\":[\"f_a\"]}"); ecs_os_free(json); - ecs_filter_fini(f_a); - ecs_filter_fini(f_b); - ecs_filter_fini(f_c); + ecs_query_fini(f_a); + ecs_query_fini(f_b); + ecs_query_fini(f_c); ecs_fini(world); } @@ -1501,26 +1371,26 @@ void SerializeEntityToJson_serialize_matches_query(void) { ECS_TAG(world, TagB); ECS_TAG(world, TagC); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, TagA); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e2, TagB); ecs_query_t *f_a = ecs_query(world, { - .filter.entity = ecs_entity(world, { .name = "f_a"} ), - .filter.terms = {{ TagA }} + .entity = ecs_entity(world, { .name = "f_a"} ), + .terms = {{ TagA }} }); test_assert(f_a != NULL); ecs_query_t *f_b = ecs_query(world, { - .filter.entity = ecs_entity(world, { .name = "f_b"} ), - .filter.terms = {{ TagB }} + .entity = ecs_entity(world, { .name = "f_b"} ), + .terms = {{ TagB }} }); test_assert(f_b != NULL); ecs_query_t *f_c = ecs_query(world, { - .filter.entity = ecs_entity(world, { .name = "f_c"} ), - .filter.terms = {{ TagC }} + .entity = ecs_entity(world, { .name = "f_c"} ), + .terms = {{ TagC }} }); test_assert(f_c != NULL); @@ -1528,13 +1398,7 @@ void SerializeEntityToJson_serialize_matches_query(void) { desc.serialize_matches = true; char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"TagA\"]], " - "\"matches\":[" - "\"f_a\"" - "]" - "}"); + test_json(json, "{\"name\":\"e1\", \"tags\":[\"TagA\"], \"matches\":[\"f_a\"]}"); ecs_os_free(json); ecs_query_fini(f_a); @@ -1551,24 +1415,24 @@ void SerializeEntityToJson_serialize_matches_rule(void) { ECS_TAG(world, TagB); ECS_TAG(world, TagC); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, TagA); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e2, TagB); - ecs_rule_t *f_a = ecs_rule(world, { + ecs_query_t *f_a = ecs_query(world, { .entity = ecs_entity(world, { .name = "f_a"} ), .terms = {{ TagA }} }); test_assert(f_a != NULL); - ecs_rule_t *f_b = ecs_rule(world, { + ecs_query_t *f_b = ecs_query(world, { .entity = ecs_entity(world, { .name = "f_b"} ), .terms = {{ TagB }} }); test_assert(f_b != NULL); - ecs_rule_t *f_c = ecs_rule(world, { + ecs_query_t *f_c = ecs_query(world, { .entity = ecs_entity(world, { .name = "f_c"} ), .terms = {{ TagC }} }); @@ -1578,18 +1442,12 @@ void SerializeEntityToJson_serialize_matches_rule(void) { desc.serialize_matches = true; char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"TagA\"]], " - "\"matches\":[" - "\"f_a\"" - "]" - "}"); + test_json(json, "{\"name\":\"e1\", \"tags\":[\"TagA\"], \"matches\":[\"f_a\"]}"); ecs_os_free(json); - ecs_rule_fini(f_a); - ecs_rule_fini(f_b); - ecs_rule_fini(f_c); + ecs_query_fini(f_a); + ecs_query_fini(f_b); + ecs_query_fini(f_c); ecs_fini(world); } @@ -1601,18 +1459,18 @@ void SerializeEntityToJson_serialize_no_matches(void) { ECS_TAG(world, TagB); ECS_TAG(world, TagC); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, TagA); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e2, TagB); - ecs_filter_t *f_b = ecs_filter(world, { + ecs_query_t *f_b = ecs_query(world, { .entity = ecs_entity(world, { .name = "f_b"} ), .terms = {{ TagB }} }); test_assert(f_b != NULL); - ecs_filter_t *f_c = ecs_filter(world, { + ecs_query_t *f_c = ecs_query(world, { .entity = ecs_entity(world, { .name = "f_c"} ), .terms = {{ TagC }} }); @@ -1622,16 +1480,571 @@ void SerializeEntityToJson_serialize_no_matches(void) { desc.serialize_matches = true; char *json = ecs_entity_to_json(world, e1, &desc); test_assert(json != NULL); - test_str(json, "{" - "\"path\":\"e1\", " - "\"ids\":[[\"TagA\"]], " - "\"matches\":[" - "]" - "}"); + test_json(json, "{\"name\":\"e1\", \"tags\":[\"TagA\"], \"matches\":[]}"); + ecs_os_free(json); + + ecs_query_fini(f_b); + ecs_query_fini(f_c); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_id_recycled(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_delete(world, e); + e = ecs_entity(world, { .name = "Foo" }); + + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_entity_id = true; + char *expect = flecs_asprintf("{\"name\":\"Foo\", \"id\":%u}", + (uint32_t)e); + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_union_target(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Rel, Union); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, Rel, TgtA); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_add_pair(world, e2, Rel, TgtB); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_add_pair(world, e3, Rel, TgtC); + + { + char *json = ecs_entity_to_json(world, e1, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e1\", \"pairs\":{\"Rel\":\"TgtA\"}}"); + ecs_os_free(json); + } + { + char *json = ecs_entity_to_json(world, e2, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e2\", \"pairs\":{\"Rel\":\"TgtB\"}}"); + ecs_os_free(json); + } + { + char *json = ecs_entity_to_json(world, e3, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e3\", \"pairs\":{\"Rel\":\"TgtC\"}}"); + ecs_os_free(json); + } + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_union_target_recycled(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Rel, Union); + + ecs_entity_t e = ecs_new(world); + ecs_delete(world, e); + ECS_TAG(world, TgtA); + + test_assert(TgtA != (uint32_t)TgtA); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, Rel, TgtA); + + { + char *json = ecs_entity_to_json(world, e1, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e1\", \"pairs\":{\"Rel\":\"TgtA\"}}"); + ecs_os_free(json); + } + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_anonymous_w_builtin(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Foo); + + { + char *expect = flecs_asprintf("{\"name\":\"#%u\", \"tags\":[\"Foo\"]}", + (uint32_t)e); + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_builtin = true; + char *json = ecs_entity_to_json(world, e, &desc); + test_json(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + } + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_named_w_builtin(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add(world, e, Foo); + + { + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_builtin = true; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"Foo\"], \"components\":{\"(flecs.core.Identifier,flecs.core.Name)\":null}}"); + ecs_os_free(json); + } + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_named_child_w_builtin(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .parent = p, .name = "e" }); + ecs_add(world, e, Foo); + + { + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_builtin = true; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"parent\":\"p\", \"name\":\"e\", \"tags\":[\"Foo\"],\"pairs\":{\"flecs.core.ChildOf\":\"p\"}, \"components\":{\"(flecs.core.Identifier,flecs.core.Name)\":null}}"); + ecs_os_free(json); + } + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_named_child_w_builtin_w_type_info(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_entity_t p = ecs_entity(world, { .name = "p" }); + ecs_entity_t e = ecs_entity(world, { .parent = p, .name = "e" }); + ecs_add(world, e, Foo); + + { + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_builtin = true; + desc.serialize_type_info = true; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"parent\":\"p\", \"name\":\"e\", \"tags\":[\"Foo\"],\"pairs\":{\"flecs.core.ChildOf\":\"p\"},\"type_info\":{\"(flecs.core.Identifier,flecs.core.Name)\":0}, \"components\":{\"(flecs.core.Identifier,flecs.core.Name)\":null}}"); + ecs_os_free(json); + } + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_from_stage(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + + ecs_world_t *stage = ecs_get_stage(world, 0); + + char *json = ecs_entity_to_json(stage, e, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\"}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_sparse(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set_name(world, e, "Foo"); + + char *json = ecs_entity_to_json(world, e, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"components\":{\"Position\":{\"x\":10, \"y\":20}}}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_sparse_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tgt); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_set_pair(world, e, Position, Tgt, {10, 20}); + + char *json = ecs_entity_to_json(world, e, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"components\":{\"(Position,Tgt)\":{\"x\":10, \"y\":20}}}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_sparse_mixed(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_entity_t ecs_id(Velocity) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + ecs_set_name(world, e, "Foo"); + + char *json = ecs_entity_to_json(world, e, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"components\":{\"Position\":{\"x\":10, \"y\":20}, \"Velocity\":{\"x\":1, \"y\":2}}}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_sparse_inherited(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_set(world, base, Position, {10, 20}); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true, + .serialize_values = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"Position\":{\"x\":10, \"y\":20}}}}}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_sparse_inherited_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tgt); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_set_pair(world, base, Position, Tgt, {10, 20}); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true, + .serialize_values = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"(Position,Tgt)\":{\"x\":10, \"y\":20}}}}}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_sparse_inherited_mixed(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_entity_t ecs_id(Velocity) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_set(world, base, Position, {10, 20}); + ecs_set(world, base, Velocity, {1, 2}); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); + ecs_add_pair(world, e, EcsIsA, base); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true, + .serialize_values = true + }; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"pairs\":{\"IsA\":\"Base\"},\"inherited\":{\"Base\":{\"components\":{\"Position\":{\"x\":10, \"y\":20}, \"Velocity\":{\"x\":1, \"y\":2}}}}, \"components\":{\"Velocity\":{\"x\":1, \"y\":2}}}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_sparse_w_type_info(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set_name(world, e, "Foo"); + + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_type_info = true; + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"Foo\", \"type_info\":{\"Position\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, \"components\":{\"Position\":{\"x\":10, \"y\":20}}}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_auto_override_w_inherited(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_entity(world, { .name = "e"}); + ecs_auto_override(world, e, Position); + + ecs_entity_to_json_desc_t desc = { + .serialize_inherited = true + }; + + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"auto_override|Position\"]}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_auto_override(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_entity(world, { .name = "e"}); + ecs_auto_override(world, e, Position); + + char *json = ecs_entity_to_json(world, e, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"auto_override|Position\"]}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_auto_override_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t e = ecs_entity(world, { .name = "e"}); + ecs_auto_override_pair(world, e, Rel, Tgt); + + char *json = ecs_entity_to_json(world, e, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"auto_override|(Rel,Tgt)\"]}"); ecs_os_free(json); - ecs_filter_fini(f_b); - ecs_filter_fini(f_c); + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_auto_override_fullpath(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t tag = ecs_entity(world, { .name = "tags.foo"}); + + ecs_entity_t e = ecs_entity(world, { .name = "e"}); + ecs_auto_override_id(world, e, tag); + + { + ecs_entity_to_json_desc_t desc = { + .serialize_full_paths = true + }; + + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"auto_override|tags.foo\"]}"); + ecs_os_free(json); + } + + { + ecs_entity_to_json_desc_t desc = { + .serialize_full_paths = false + }; + + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"auto_override|foo\"]}"); + ecs_os_free(json); + } + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_auto_override_pair_fullpath(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t rel = ecs_entity(world, { .name = "tags.rel"}); + ecs_entity_t tgt = ecs_entity(world, { .name = "tags.tgt"}); + + ecs_entity_t e = ecs_entity(world, { .name = "e"}); + ecs_auto_override_pair(world, e, rel, tgt); + + { + ecs_entity_to_json_desc_t desc = { + .serialize_full_paths = true + }; + + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"auto_override|(tags.rel,tags.tgt)\"]}"); + ecs_os_free(json); + } + + { + ecs_entity_to_json_desc_t desc = { + .serialize_full_paths = false + }; + + char *json = ecs_entity_to_json(world, e, &desc); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"auto_override|(rel,tgt)\"]}"); + ecs_os_free(json); + } + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_toggle(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t e = ecs_entity(world, { .name = "e"}); + ecs_add(world, e, Position); + ecs_enable_component(world, e, Position, false); + + char *json = ecs_entity_to_json(world, e, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"toggle|Position\"], \"components\":{\"Position\":null}}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeEntityToJson_serialize_toggle_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_add_id(world, Rel, EcsCanToggle); + + ecs_entity_t e = ecs_entity(world, { .name = "e"}); + ecs_add_pair(world, e, Rel, Tgt); + ecs_enable_pair(world, e, Rel, Tgt, false); + + char *json = ecs_entity_to_json(world, e, NULL); + test_assert(json != NULL); + test_json(json, "{\"name\":\"e\", \"tags\":[\"toggle|(Rel,Tgt)\"],\"pairs\":{\"Rel\":[\"Tgt\"]}}"); + ecs_os_free(json); ecs_fini(world); } diff --git a/vendors/flecs/test/meta/src/SerializeIterToJson.c b/vendors/flecs/test/meta/src/SerializeIterToJson.c index e4c5890c1..3d4f8085f 100644 --- a/vendors/flecs/test/meta/src/SerializeIterToJson.c +++ b/vendors/flecs/test/meta/src/SerializeIterToJson.c @@ -16,15 +16,17 @@ void SerializeIterToJson_serialize_1_comps_empty(void) { } }); - ecs_query_t *q = ecs_query_new(world, "Position"); + ecs_query_t *q = ecs_query(world, { .expr = "Position" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); test_assert(json != NULL); - test_str(json, "{\"ids\":[[\"Position\"]], \"results\":[]}"); + test_json(json, "{\"results\":[]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -41,34 +43,22 @@ void SerializeIterToJson_serialize_1_comps_2_ents_same_table(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); - ecs_query_t *q = ecs_query_new(world, "Position"); + ecs_query_t *q = ecs_query(world, { .expr = "Position" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"x\":10, \"y\":20}, " - "{\"x\":30, \"y\":40}" - "]]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"x\":10, \"y\":20}]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"x\":30, \"y\":40}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -93,8 +83,8 @@ void SerializeIterToJson_serialize_2_comps_2_ents_same_table(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); @@ -102,31 +92,16 @@ void SerializeIterToJson_serialize_2_comps_2_ents_same_table(void) { ecs_set(world, e1, Mass, {1}); ecs_set(world, e2, Mass, {2}); - ecs_query_t *q = ecs_query_new(world, "Position, Mass"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, Mass" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Mass\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Mass\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"x\":10, \"y\":20}, " - "{\"x\":30, \"y\":40}" - "], [" - "{\"value\":1}, " - "{\"value\":2}" - "]]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"x\":10, \"y\":20}, {\"value\":1}]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"x\":30, \"y\":40}, {\"value\":2}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -135,30 +110,22 @@ void SerializeIterToJson_serialize_1_tag_2_ents_same_table(void) { ECS_TAG(world, MyTag); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add(world, e1, MyTag); ecs_add(world, e2, MyTag); - ecs_query_t *q = ecs_query_new(world, "MyTag"); + ecs_query_t *q = ecs_query(world, { .expr = "MyTag" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"MyTag\"]], " - "\"results\":[{" - "\"ids\":[[\"MyTag\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"fields\":{}}, {\"name\":\"Bar\", \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -176,8 +143,8 @@ void SerializeIterToJson_serialize_1_tag_1_comp_2_ents_same_table(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); @@ -185,28 +152,16 @@ void SerializeIterToJson_serialize_1_tag_1_comp_2_ents_same_table(void) { ecs_add(world, e1, MyTag); ecs_add(world, e2, MyTag); - ecs_query_t *q = ecs_query_new(world, "Position, MyTag"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, MyTag" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"MyTag\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"MyTag\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"x\":10, \"y\":20}, " - "{\"x\":30, \"y\":40}" - "], 0]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"x\":10, \"y\":20}, 0]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"x\":30, \"y\":40}, 0]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -225,10 +180,10 @@ void SerializeIterToJson_serialize_1_tag_1_comp_4_ents_two_tables(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); - ecs_entity_t e3 = ecs_new_entity(world, "Hello"); - ecs_entity_t e4 = ecs_new_entity(world, "World"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "Hello" }); + ecs_entity_t e4 = ecs_entity(world, { .name = "World" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {30, 40}); @@ -244,38 +199,16 @@ void SerializeIterToJson_serialize_1_tag_1_comp_4_ents_two_tables(void) { ecs_add(world, e3, TagB); ecs_add(world, e4, TagB); - ecs_query_t *q = ecs_query_new(world, "Position, TagA"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, TagA" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"TagA\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"TagA\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"x\":10, \"y\":20}, " - "{\"x\":30, \"y\":40}" - "], 0]" - "}, {" - "\"ids\":[[\"Position\"], [\"TagA\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"Hello\", \"World\"" - "], " - "\"values\":[[" - "{\"x\":50, \"y\":60}, " - "{\"x\":70, \"y\":80}" - "], 0]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"x\":10, \"y\":20}, 0]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"x\":30, \"y\":40}, 0]}}, {\"name\":\"Hello\", \"fields\":{\"values\":[{\"x\":50, \"y\":60}, 0]}}, {\"name\":\"World\", \"fields\":{\"values\":[{\"x\":70, \"y\":80}, 0]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -285,6 +218,9 @@ void SerializeIterToJson_serialize_2_comps_1_owned_2_ents(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ecs_struct_init(world, &(ecs_struct_desc_t){ .entity = ecs_id(Position), .members = { @@ -300,9 +236,9 @@ void SerializeIterToJson_serialize_2_comps_1_owned_2_ents(void) { } }); - ecs_entity_t base = ecs_new_entity(world, "Base"); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t base = ecs_entity(world, { .name = "Base" }); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_set(world, base, Mass, {100}); ecs_set(world, e1, Position, {10, 20}); @@ -311,30 +247,16 @@ void SerializeIterToJson_serialize_2_comps_1_owned_2_ents(void) { ecs_add_pair(world, e1, EcsIsA, base); ecs_add_pair(world, e2, EcsIsA, base); - ecs_query_t *q = ecs_query_new(world, "Position, Mass(up)"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, Mass(up IsA)" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Mass\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Mass\"]], " - "\"sources\":[0, \"Base\"], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"x\":10, \"y\":20}, " - "{\"x\":30, \"y\":40}" - "], {" - "\"value\":100" - "}]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"fields\":{\"sources\":[0, \"Base\"], \"values\":[{\"x\":10, \"y\":20}, {\"value\":100}]}}, {\"name\":\"Bar\", \"fields\":{\"sources\":[0, \"Base\"], \"values\":[{\"x\":30, \"y\":40}, {\"value\":100}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -345,36 +267,22 @@ void SerializeIterToJson_serialize_w_pair_wildcard(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add_pair(world, e1, Rel, ObjA); ecs_add_pair(world, e2, Rel, ObjB); - ecs_query_t *q = ecs_query_new(world, "(Rel, *)"); + ecs_query_t *q = ecs_query(world, { .expr = "(Rel, *)" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Rel\",\"*\"]], " - "\"results\":[{" - "\"ids\":[[\"Rel\",\"ObjA\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Foo\"" - "]" - "}, {" - "\"ids\":[[\"Rel\",\"ObjB\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Bar\"" - "]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"fields\":{\"ids\":[[\"Rel\",\"ObjA\"]]}}, {\"name\":\"Bar\", \"fields\":{\"ids\":[[\"Rel\",\"ObjB\"]]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -385,43 +293,24 @@ void SerializeIterToJson_serialize_w_var(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add_pair(world, e1, Rel, ObjA); ecs_add_pair(world, e2, Rel, ObjB); - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ .expr = "(Rel, $X)" }); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Rel\",\"*\"]], " - "\"vars\":[\"X\"], " - "\"results\":[{" - "\"ids\":[[\"Rel\",\"ObjA\"]], " - "\"sources\":[0], " - "\"vars\":[\"ObjA\"], " - "\"entities\":[" - "\"Foo\"" - "]" - "}, {" - "\"ids\":[[\"Rel\",\"ObjB\"]], " - "\"sources\":[0], " - "\"vars\":[\"ObjB\"], " - "\"entities\":[" - "\"Bar\"" - "]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"vars\":{\"X\":\"ObjA\"},\"fields\":{\"ids\":[[\"Rel\",\"ObjA\"]]}}, {\"name\":\"Bar\", \"vars\":{\"X\":\"ObjB\"},\"fields\":{\"ids\":[[\"Rel\",\"ObjB\"]]}}]}"); ecs_os_free(json); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -437,8 +326,8 @@ void SerializeIterToJson_serialize_w_2_vars(void) { ECS_TAG(world, ObjC); ECS_TAG(world, ObjD); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add_pair(world, e1, RelX, ObjA); ecs_add_pair(world, e2, RelX, ObjB); @@ -446,37 +335,18 @@ void SerializeIterToJson_serialize_w_2_vars(void) { ecs_add_pair(world, e1, RelY, ObjC); ecs_add_pair(world, e2, RelY, ObjD); - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ .expr = "(RelX, $X), (RelY, $Y)" }); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"RelX\",\"*\"], [\"RelY\",\"*\"]], " - "\"vars\":[\"X\", \"Y\"], " - "\"results\":[{" - "\"ids\":[[\"RelX\",\"ObjA\"], [\"RelY\",\"ObjC\"]], " - "\"sources\":[0, 0], " - "\"vars\":[\"ObjA\", \"ObjC\"], " - "\"entities\":[" - "\"Foo\"" - "]" - "}, {" - "\"ids\":[[\"RelX\",\"ObjB\"], [\"RelY\",\"ObjD\"]], " - "\"sources\":[0, 0], " - "\"vars\":[\"ObjB\", \"ObjD\"], " - "\"entities\":[" - "\"Bar\"" - "]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"vars\":{\"X\":\"ObjA\", \"Y\":\"ObjC\"},\"fields\":{\"ids\":[[\"RelX\",\"ObjA\"], [\"RelY\",\"ObjC\"]]}}, {\"name\":\"Bar\", \"vars\":{\"X\":\"ObjB\", \"Y\":\"ObjD\"},\"fields\":{\"ids\":[[\"RelX\",\"ObjB\"], [\"RelY\",\"ObjD\"]]}}]}"); ecs_os_free(json); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -486,33 +356,25 @@ void SerializeIterToJson_serialize_type_info_1_tags(void) { ECS_TAG(world, TagA); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add(world, e1, TagA); ecs_add(world, e2, TagA); - ecs_query_t *q = ecs_query_new(world, "TagA"); + ecs_query_t *q = ecs_query(world, { .expr = "TagA" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"TagA\"]], " - "\"results\":[{" - "\"ids\":[[\"TagA\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "]" - "}]" - "}"); + test_json(json, "{\"type_info\":{}, \"results\":[{\"name\":\"Foo\", \"fields\":{}}, {\"name\":\"Bar\", \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -522,35 +384,26 @@ void SerializeIterToJson_serialize_type_info_2_tags(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add(world, e1, TagA); ecs_add(world, e1, TagB); ecs_add(world, e2, TagA); ecs_add(world, e2, TagB); - ecs_query_t *q = ecs_query_new(world, "TagA, TagB"); + ecs_query_t *q = ecs_query(world, { .expr = "TagA, TagB" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); - - test_str(json, - "{" - "\"ids\":[[\"TagA\"], [\"TagB\"]], " - "\"results\":[{" - "\"ids\":[[\"TagA\"], [\"TagB\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, &desc); + test_json(json, "{\"type_info\":{}, \"results\":[{\"name\":\"Foo\", \"fields\":{}}, {\"name\":\"Bar\", \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -559,35 +412,25 @@ void SerializeIterToJson_serialize_type_info_1_component(void) { ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add(world, e1, Position); ecs_add(world, e2, Position); - ecs_query_t *q = ecs_query_new(world, "Position"); + ecs_query_t *q = ecs_query(world, { .expr = "Position" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"Position\"]], " - "\"type_info\":{\"Position\":0}, " - "\"results\":[{" - "\"ids\":[[\"Position\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[0]" - "}]" - "}"); + test_json(json, "{\"type_info\":{\"Position\":0}, \"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[0]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[0]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -597,8 +440,8 @@ void SerializeIterToJson_serialize_type_info_2_components(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -606,29 +449,19 @@ void SerializeIterToJson_serialize_type_info_2_components(void) { ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, Velocity" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"type_info\":{\"Position\":0, \"Velocity\":0}, " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[0, 0]" - "}]" - "}"); + test_json(json, "{\"type_info\":{\"Position\":0, \"Velocity\":0}, \"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[0, 0]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[0, 0]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -647,38 +480,25 @@ void SerializeIterToJson_serialize_type_info_1_struct(void) { test_assert(t == ecs_id(Position)); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add(world, e1, Position); ecs_add(world, e2, Position); - ecs_query_t *q = ecs_query_new(world, "Position"); + ecs_query_t *q = ecs_query(world, { .expr = "Position" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"Position\"]], " - "\"type_info\":{\"Position\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, " - "\"results\":[{" - "\"ids\":[[\"Position\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"x\":0, \"y\":0}, " - "{\"x\":0, \"y\":0}" - "]]" - "}]" - "}"); + test_json(json, "{\"type_info\":{\"Position\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, \"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -698,8 +518,8 @@ void SerializeIterToJson_serialize_type_info_1_component_1_struct(void) { test_assert(t == ecs_id(Position)); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -707,35 +527,19 @@ void SerializeIterToJson_serialize_type_info_1_component_1_struct(void) { ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, Velocity" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"type_info\":{" - "\"Position\":{\"x\":[\"int\"], \"y\":[\"int\"]}, " - "\"Velocity\":0" - "}, " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"x\":0, \"y\":0}, " - "{\"x\":0, \"y\":0}" - "], 0]" - "}]" - "}"); + test_json(json, "{\"type_info\":{\"Position\":{\"x\":[\"int\"], \"y\":[\"int\"]}, \"Velocity\":0}, \"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}, 0]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}, 0]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -763,8 +567,8 @@ void SerializeIterToJson_serialize_type_info_2_structs(void) { }); test_assert(t == ecs_id(Velocity)); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add(world, e1, Position); ecs_add(world, e2, Position); @@ -772,38 +576,19 @@ void SerializeIterToJson_serialize_type_info_2_structs(void) { ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); - ecs_query_t *q = ecs_query_new(world, "Position, Velocity"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, Velocity" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"type_info\":{" - "\"Position\":{\"x\":[\"int\"], \"y\":[\"int\"]}, " - "\"Velocity\":{\"x\":[\"int\"], \"y\":[\"int\"]}" - "}, " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"x\":0, \"y\":0}, " - "{\"x\":0, \"y\":0}" - "], [" - "{\"x\":0, \"y\":0}, " - "{\"x\":0, \"y\":0}" - "]]" - "}]" - "}"); + test_json(json, "{\"type_info\":{\"Position\":{\"x\":[\"int\"], \"y\":[\"int\"]}, \"Velocity\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, \"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}, {\"x\":0, \"y\":0}]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}, {\"x\":0, \"y\":0}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -828,42 +613,25 @@ void SerializeIterToJson_serialize_type_info_w_unit(void) { }); test_assert(ecs_id(T) != 0); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_set(world, e1, T, {24}); ecs_set(world, e2, T, {16}); - ecs_query_t *q = ecs_query_new(world, "T"); + ecs_query_t *q = ecs_query(world, { .expr = "T" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"T\"]], " - "\"type_info\":{" - "\"T\":{\"value\":[\"int\", {" - "\"unit\":\"celsius\", \"symbol\":\"°\"}]" - "}" - "}, " - "\"results\":[{" - "\"ids\":[[\"T\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"value\":24}, " - "{\"value\":16}" - "]]" - "}]" - "}"); + test_json(json, "{\"type_info\":{\"T\":{\"value\":[\"int\", {\"unit\":\"celsius\", \"symbol\":\"°\"}]}}, \"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"value\":24}]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"value\":16}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -894,42 +662,25 @@ void SerializeIterToJson_serialize_type_info_w_unit_quantity(void) { }); test_assert(ecs_id(T) != 0); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_set(world, e1, T, {24}); ecs_set(world, e2, T, {16}); - ecs_query_t *q = ecs_query_new(world, "T"); + ecs_query_t *q = ecs_query(world, { .expr = "T" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"T\"]], " - "\"type_info\":{" - "\"T\":{\"value\":[\"int\", {" - "\"unit\":\"celsius\", \"symbol\":\"°\", \"quantity\":\"temperature\"}]" - "}" - "}, " - "\"results\":[{" - "\"ids\":[[\"T\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"value\":24}, " - "{\"value\":16}" - "]]" - "}]" - "}"); + test_json(json, "{\"type_info\":{\"T\":{\"value\":[\"int\", {\"unit\":\"celsius\", \"symbol\":\"°\", \"quantity\":\"temperature\"}]}}, \"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"value\":24}]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"value\":16}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -967,42 +718,25 @@ void SerializeIterToJson_serialize_type_info_w_unit_over(void) { }); test_assert(ecs_id(T) != 0); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_set(world, e1, T, {24}); ecs_set(world, e2, T, {16}); - ecs_query_t *q = ecs_query_new(world, "T"); + ecs_query_t *q = ecs_query(world, { .expr = "T" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_type_info = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"T\"]], " - "\"type_info\":{" - "\"T\":{\"value\":[\"int\", {" - "\"unit\":\"meters_per_second\", \"symbol\":\"m/s\"}]" - "}" - "}, " - "\"results\":[{" - "\"ids\":[[\"T\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"Foo\", \"Bar\"" - "], " - "\"values\":[[" - "{\"value\":24}, " - "{\"value\":16}" - "]]" - "}]" - "}"); + test_json(json, "{\"type_info\":{\"T\":{\"value\":[\"int\", {\"unit\":\"meters_per_second\", \"symbol\":\"m/s\"}]}}, \"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"value\":24}]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"value\":16}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1011,43 +745,83 @@ void SerializeIterToJson_serialize_w_entity_label(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "foo_bar"); - ecs_entity_t e2 = ecs_new_entity(world, "hello_world"); + ecs_entity_t e1 = ecs_entity(world, { .name = "foo_bar" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "hello_world" }); ecs_doc_set_name(world, e2, "Hello World"); ecs_add(world, e1, Tag); ecs_add(world, e2, Tag); - ecs_query_t *q = ecs_query_new(world, "Tag"); + ecs_query_t *q = ecs_query(world, { .expr = "Tag" }); + ecs_iter_t it = ecs_query_iter(world, q); + + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_doc = true; + char *json = ecs_iter_to_json(&it, &desc); + + test_json(json, "{\"results\":[{\"name\":\"foo_bar\", \"doc\":{\"label\":\"foo_bar\"}, \"fields\":{}}, {\"name\":\"hello_world\", \"doc\":{\"label\":\"Hello World\"}, \"fields\":{}}]}"); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_w_entity_label_w_str(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_entity(world, { .name = "foo_bar" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "hello_world" }); + ecs_doc_set_name(world, e2, "Hello \"World\""); + + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + + ecs_query_t *q = ecs_query(world, { .expr = "Tag" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; - desc.serialize_entity_labels = true; - char *json = ecs_iter_to_json(world, &it, &desc); + desc.serialize_doc = true; + char *json = ecs_iter_to_json(&it, &desc); + + test_json(json, "{\"results\":[{\"name\":\"foo_bar\", \"doc\":{\"label\":\"foo_bar\"}, \"fields\":{}}, {\"name\":\"hello_world\", \"doc\":{\"label\":\"Hello \\\"World\\\"\"}, \"fields\":{}}]}"); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_entity_label_w_newline(void) { + ecs_world_t *world = ecs_init(); - test_str(json, - "{" - "\"ids\":[[\"Tag\"]], " - "\"results\":[{" - "\"ids\":[[\"Tag\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"foo_bar\"" - "]" - "}, {" - "\"ids\":[[\"Tag\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"hello_world\"" - "], " - "\"entity_labels\":[" - "\"Hello World\"" - "]" - "}]" - "}"); + ECS_TAG(world, Foo); + + ecs_entity_t e = ecs_set_name(world, 0, "e"); + ecs_doc_set_name(world, e, "foo\nbar"); + ecs_add(world, e, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_doc = true; + char *json = ecs_iter_to_json(&it, &desc); + test_json(json, "{\"results\":[{\"name\":\"e\", \"doc\":{\"label\":\"foo\\nbar\"}, \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1058,50 +832,30 @@ void SerializeIterToJson_serialize_w_var_labels(void) { ECS_TAG(world, ObjA); ECS_TAG(world, ObjB); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); - ecs_entity_t e2 = ecs_new_entity(world, "Bar"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); ecs_add_pair(world, e1, Rel, ObjA); ecs_add_pair(world, e2, Rel, ObjB); ecs_doc_set_name(world, ObjA, "Object A"); - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ .expr = "(Rel, $X)" }); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; - desc.serialize_variable_labels = true; - char *json = ecs_iter_to_json(world, &it, &desc); + desc.serialize_doc = true; + desc.serialize_full_paths = false; + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"Rel\",\"*\"]], " - "\"vars\":[\"X\"], " - "\"results\":[{" - "\"ids\":[[\"Rel\",\"ObjA\"]], " - "\"sources\":[0], " - "\"vars\":[\"ObjA\"], " - "\"var_labels\":[\"Object A\"], " - "\"entities\":[" - "\"Foo\"" - "]" - "}, {" - "\"ids\":[[\"Rel\",\"ObjB\"]], " - "\"sources\":[0], " - "\"vars\":[\"ObjB\"], " - "\"var_labels\":[\"ObjB\"], " - "\"entities\":[" - "\"Bar\"" - "]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"doc\":{\"label\":\"Foo\"}, \"vars\":{\"X\":\"Object A\"},\"fields\":{\"ids\":[[\"Rel\",\"ObjA\"]]}}, {\"name\":\"Bar\", \"doc\":{\"label\":\"Bar\"}, \"vars\":{\"X\":\"ObjB\"},\"fields\":{\"ids\":[[\"Rel\",\"ObjB\"]]}}]}"); ecs_os_free(json); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -1111,43 +865,26 @@ void SerializeIterToJson_serialize_color(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "foo_bar"); - ecs_entity_t e2 = ecs_new_entity(world, "hello_world"); + ecs_entity_t e1 = ecs_entity(world, { .name = "foo_bar" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "hello_world" }); ecs_doc_set_color(world, e2, "#47B576"); ecs_add(world, e1, Tag); ecs_add(world, e2, Tag); - ecs_query_t *q = ecs_query_new(world, "Tag"); + ecs_query_t *q = ecs_query(world, { .expr = "Tag" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; - desc.serialize_colors = true; - char *json = ecs_iter_to_json(world, &it, &desc); + desc.serialize_doc = true; + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{" - "\"ids\":[[\"Tag\"]], " - "\"results\":[{" - "\"ids\":[[\"Tag\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"foo_bar\"" - "]" - "}, {" - "\"ids\":[[\"Tag\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"hello_world\"" - "], " - "\"colors\":[" - "\"#47B576\"" - "]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"foo_bar\", \"doc\":{\"label\":\"foo_bar\"}, \"fields\":{}}, {\"name\":\"hello_world\", \"doc\":{\"label\":\"hello_world\", \"color\":\"#47B576\"}, \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1168,36 +905,23 @@ void SerializeIterToJson_serialize_w_var_component(void) { } }); - ecs_entity_t e = ecs_new_entity(world, "Foo"); + ecs_entity_t e = ecs_entity(world, { .name = "Foo" }); ecs_add_pair(world, e, Rel, Obj); ecs_set(world, Obj, T, {10}); - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ .expr = "(Rel, $X), T($X)" }); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Rel\",\"*\"], [\"T\"]], " - "\"vars\":[\"X\"], " - "\"results\":[{" - "\"ids\":[[\"Rel\",\"Obj\"], [\"T\"]], " - "\"sources\":[0, \"Obj\"], " - "\"vars\":[\"Obj\"], " - "\"entities\":[" - "\"Foo\"" - "], " - "\"values\":[0, {\"x\":10}]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"vars\":{\"X\":\"Obj\"},\"fields\":{\"ids\":[[\"Rel\",\"Obj\"], 0], \"sources\":[0, \"Obj\"], \"values\":[0, {\"x\":10}]}}]}"); ecs_os_free(json); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -1208,40 +932,24 @@ void SerializeIterToJson_serialize_w_optional_tag(void) { ECS_TAG(world, TagA); ECS_TAG(world, TagB); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e1, TagB); ecs_add(world, e1, TagA); ecs_add(world, e2, TagA); - ecs_query_t *q = ecs_query_new(world, "TagA, ?TagB"); + ecs_query_t *q = ecs_query(world, { .expr = "TagA, ?TagB" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"TagA\"], [\"TagB\"]], " - "\"results\":[{" - "\"ids\":[[\"TagA\"], [\"TagB\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, true], " - "\"entities\":[" - "\"e1\"" - "]" - "}, {" - "\"ids\":[[\"TagA\"], [\"TagB\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, false], " - "\"entities\":[" - "\"e2\"" - "]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, true]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1251,42 +959,24 @@ void SerializeIterToJson_serialize_w_optional_component(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e1, Velocity); ecs_add(world, e1, Position); ecs_add(world, e2, Position); - ecs_query_t *q = ecs_query_new(world, "Position, ?Velocity"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, ?Velocity" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, true], " - "\"entities\":[" - "\"e1\"" - "], " - "\"values\":[0, 0]" - "}, {" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, false], " - "\"entities\":[" - "\"e2\"" - "], " - "\"values\":[0, 0]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, true], \"values\":[0, 0]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false], \"values\":[0, 0]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1309,42 +999,24 @@ void SerializeIterToJson_serialize_w_optional_reflected_component(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_set(world, e1, Velocity, {1, 2}); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {10, 20}); - ecs_query_t *q = ecs_query_new(world, "Position, ?Velocity"); + ecs_query_t *q = ecs_query(world, { .expr = "Position, ?Velocity" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, true], " - "\"entities\":[" - "\"e1\"" - "], " - "\"values\":[[{\"x\":10, \"y\":20}], [{\"x\":1, \"y\":2}]]" - "}, {" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, false], " - "\"entities\":[" - "\"e2\"" - "], " - "\"values\":[[{\"x\":10, \"y\":20}], []]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, true], \"values\":[{\"x\":10, \"y\":20}, {\"x\":1, \"y\":2}]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false], \"values\":[{\"x\":10, \"y\":20}, 0]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1353,29 +1025,21 @@ void SerializeIterToJson_serialize_w_inout_filter_tag(void) { ECS_TAG(world, TagA); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, TagA); - ecs_query_t *q = ecs_query_new(world, "[none] TagA"); + ecs_query_t *q = ecs_query(world, { .expr = "[none] TagA" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"TagA\"]], " - "\"results\":[{" - "\"ids\":[[\"TagA\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"e1\"" - "]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1393,31 +1057,22 @@ void SerializeIterToJson_serialize_w_inout_filter_component(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e1, Velocity, {1, 2}); - ecs_query_t *q = ecs_query_new(world, "[none] Position, Velocity"); + ecs_query_t *q = ecs_query(world, { .expr = "[none] Position, Velocity" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"e1\"" - "], " - "\"values\":[0, [{\"x\":1, \"y\":2}]]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{\"values\":[0, {\"x\":1, \"y\":2}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1443,31 +1098,22 @@ void SerializeIterToJson_serialize_w_inout_filter_reflected_component(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e1, Velocity, {1, 2}); - ecs_query_t *q = ecs_query_new(world, "[none] Position, Velocity"); + ecs_query_t *q = ecs_query(world, { .expr = "[none] Position, Velocity" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"e1\"" - "], " - "\"values\":[0, [{\"x\":1, \"y\":2}]]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{\"values\":[0, {\"x\":1, \"y\":2}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1476,29 +1122,21 @@ void SerializeIterToJson_serialize_w_inout_out_tag(void) { ECS_TAG(world, TagA); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, TagA); - ecs_query_t *q = ecs_query_new(world, "[out] TagA"); + ecs_query_t *q = ecs_query(world, { .expr = "[out] TagA" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"TagA\"]], " - "\"results\":[{" - "\"ids\":[[\"TagA\"]], " - "\"sources\":[0], " - "\"entities\":[" - "\"e1\"" - "]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1516,31 +1154,22 @@ void SerializeIterToJson_serialize_w_inout_out_component(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e1, Velocity, {1, 2}); - ecs_query_t *q = ecs_query_new(world, "[out] Position, Velocity"); + ecs_query_t *q = ecs_query(world, { .expr = "[out] Position, Velocity" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"e1\"" - "], " - "\"values\":[0, [{\"x\":1, \"y\":2}]]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{\"values\":[0, {\"x\":1, \"y\":2}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1566,31 +1195,22 @@ void SerializeIterToJson_serialize_w_inout_out_reflected_component(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e1, Velocity, {1, 2}); - ecs_query_t *q = ecs_query_new(world, "[out] Position, Velocity"); + ecs_query_t *q = ecs_query(world, { .expr = "[out] Position, Velocity" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"entities\":[" - "\"e1\"" - "], " - "\"values\":[0, [{\"x\":1, \"y\":2}]]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{\"values\":[{\"x\":10, \"y\":20}, {\"x\":1, \"y\":2}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1608,30 +1228,20 @@ void SerializeIterToJson_serialize_component_from_var(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_set(world, e1, Position, {10, 20}); - ecs_rule_t *r = ecs_rule_new(world, "Position($E)"); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_query_t *r = ecs_query(world, { .expr = "Position($E)" }); + ecs_iter_t it = ecs_query_iter(world, r); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"]], " - "\"vars\":[\"E\"], " - "\"results\":[{" - "\"ids\":[[\"Position\"]], " - "\"sources\":[\"e1\"], " - "\"vars\":[\"e1\"], " - "\"values\":[{\"x\":10, \"y\":20}]" - "}]" - "}"); + test_json(json, "{\"results\":[{\"vars\":{\"E\":\"e1\"},\"fields\":{\"sources\":[\"e1\"], \"values\":[{\"x\":10, \"y\":20}]}}]}"); ecs_os_free(json); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -1641,29 +1251,28 @@ void SerializeIterToJson_serialize_ids(void) { ECS_TAG(world, Tag); - ecs_query_t *q = ecs_query_new(world, "Tag"); + ecs_query_t *q = ecs_query(world, { .expr = "Tag" }); - ecs_entity_t e = ecs_new_entity(world, "e"); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); ecs_add(world, e, Tag); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; - desc.serialize_ids = false; desc.serialize_entity_ids = true; desc.serialize_values = false; - desc.serialize_is_set = false; - desc.serialize_sources = false; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - char *expect = ecs_asprintf( - "{\"ids\":[[\"Tag\"]], \"results\":[{\"entities\":[\"e\"], \"entity_ids\":[%u]}]}", + char *expect = flecs_asprintf( + "{\"results\":[{\"name\":\"e\", \"id\":%u, \"fields\":{}}]}", (uint32_t)e); - test_str(json, expect); + test_json(json, expect); ecs_os_free(json); ecs_os_free(expect); + ecs_query_fini(q); + ecs_fini(world); } @@ -1672,100 +1281,256 @@ void SerializeIterToJson_serialize_ids_2_entities(void) { ECS_TAG(world, Tag); - ecs_query_t *q = ecs_query_new(world, "Tag"); + ecs_query_t *q = ecs_query(world, { .expr = "Tag" }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Tag); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e2, Tag); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; - desc.serialize_ids = false; desc.serialize_entity_ids = true; - desc.serialize_values = false; - desc.serialize_is_set = false; - desc.serialize_sources = false; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - char *expect = ecs_asprintf( - "{\"ids\":[[\"Tag\"]], \"results\":[{\"entities\":[\"e1\", \"e2\"], \"entity_ids\":[%u, %u]}]}", + char *expect = flecs_asprintf( + "{\"results\":[{\"name\":\"e1\", \"id\":%u, \"fields\":{}}, {\"name\":\"e2\", \"id\":%u, \"fields\":{}}]}", (uint32_t)e1, (uint32_t)e2); - test_str(json, expect); + test_json(json, expect); ecs_os_free(json); ecs_os_free(expect); + ecs_query_fini(q); + ecs_fini(world); } -void SerializeIterToJson_serialize_variable_ids(void) { +void SerializeIterToJson_serialize_anonymous(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); - ecs_rule_t *q = ecs_rule_new(world, "Tag($Entity)"); + ecs_query_t *q = ecs_query(world, { .expr = "Tag" }); - ecs_entity_t e = ecs_new_entity(world, "e"); - ecs_add(world, e, Tag); + ecs_entity_t e1 = 5000; + ecs_entity_t e2 = 10000; + ecs_entity_t e3 = 100000; + ecs_entity_t e4 = 1000000; + ecs_make_alive(world, 5000); + ecs_make_alive(world, 10000); + ecs_make_alive(world, 100000); + ecs_make_alive(world, 1000000); + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + ecs_add(world, e3, Tag); + ecs_add(world, e4, Tag); - ecs_iter_t it = ecs_rule_iter(world, q); + ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; - desc.serialize_ids = false; - desc.serialize_variable_ids = true; - desc.serialize_values = false; - desc.serialize_is_set = false; - desc.serialize_sources = false; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - char *expect = ecs_asprintf( - "{\"ids\":[[\"Tag\"]], \"vars\":[\"Entity\"], \"results\":[{\"vars\":[\"e\"], \"var_ids\":[%u]}]}", - (uint32_t)e); - test_str(json, expect); + char *expect = flecs_asprintf( + "{\"results\":[{\"name\":\"#5000\", \"fields\":{}}, {\"name\":\"#10000\", \"fields\":{}}, {\"name\":\"#100000\", \"fields\":{}}, {\"name\":\"#1000000\", \"fields\":{}}]}"); + test_json(json, expect); ecs_os_free(json); ecs_os_free(expect); - ecs_rule_fini(q); + ecs_query_fini(q); ecs_fini(world); } -void SerializeIterToJson_serialize_variable_ids_2_entities(void) { +void SerializeIterToJson_serialize_anonymous_ids(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); - ecs_rule_t *q = ecs_rule_new(world, "Tag($Entity)"); + ecs_query_t *q = ecs_query(world, { .expr = "Tag" }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = 5000; + ecs_entity_t e2 = 10000; + ecs_entity_t e3 = 100000; + ecs_entity_t e4 = 1000000; + ecs_make_alive(world, 5000); + ecs_make_alive(world, 10000); + ecs_make_alive(world, 100000); + ecs_make_alive(world, 1000000); ecs_add(world, e1, Tag); - - ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add(world, e2, Tag); + ecs_add(world, e3, Tag); + ecs_add(world, e4, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_entity_ids = true; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + + char *expect = flecs_asprintf( + "{\"results\":[{\"name\":\"#5000\", \"id\":5000, \"fields\":{}}, {\"name\":\"#10000\", \"id\":10000, \"fields\":{}}, {\"name\":\"#100000\", \"id\":100000, \"fields\":{}}, {\"name\":\"#1000000\", \"id\":1000000, \"fields\":{}}]}"); + test_json(json, expect); + + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_variable_anonymous(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { .expr = "Tag($Entity)" }); - ecs_iter_t it = ecs_rule_iter(world, q); + ecs_entity_t e = 10000; + ecs_make_alive(world, 10000); + ecs_add(world, e, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; - desc.serialize_ids = false; - desc.serialize_variable_ids = true; desc.serialize_values = false; - desc.serialize_is_set = false; - desc.serialize_sources = false; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - char *expect = ecs_asprintf( - "{\"ids\":[[\"Tag\"]], \"vars\":[\"Entity\"], \"results\":[{\"vars\":[\"e1\"], \"var_ids\":[%u]}, {\"vars\":[\"e2\"], \"var_ids\":[%u]}]}", - (uint32_t)e1, (uint32_t)e2); - test_str(json, expect); + char *expect = flecs_asprintf( + "{\"results\":[{\"vars\":{\"Entity\":\"#10000\"},\"fields\":{\"sources\":[\"#10000\"]}}]}"); + test_json(json, expect); + + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_variable_anonymous_no_full_path(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { .expr = "Tag($Entity)" }); + + ecs_entity_t e = 10000; + ecs_make_alive(world, 10000); + ecs_add(world, e, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_values = false; + desc.serialize_full_paths = false; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + + char *expect = flecs_asprintf( + "{\"results\":[{\"vars\":{\"Entity\":\"#10000\"},\"fields\":{\"sources\":[\"#10000\"]}}]}"); + test_json(json, expect); + + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_anonymous_tag(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t tag = 10000; + ecs_make_alive(world, tag); + ecs_query_t *q = ecs_query(world, { .expr = "#10000" }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add_id(world, e, tag); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + + char *expect = flecs_asprintf( + "{\"results\":[{\"name\":\"e\", \"fields\":{}}]}"); + test_json(json, expect); + + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_anonymous_component(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t tag = 10000; + ecs_make_alive(world, tag); + ecs_struct(world, { + .entity = tag, + .members = { + { "value", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { .expr = "#10000" }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + int32_t *ptr = ecs_ensure_id(world, e, tag); + *ptr = 10; + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_values = true; + desc.serialize_type_info = true; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + + char *expect = flecs_asprintf( + "{\"type_info\":{\"#10000\":{\"value\":[\"int\"]}}, \"results\":[{\"name\":\"e\", \"fields\":{\"values\":[{\"value\":10}]}}]}"); + test_json(json, expect); + + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_anonymous_pair(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t rel = 10000; + ecs_make_alive(world, rel); + ecs_entity_t tgt = 20000; + ecs_make_alive(world, tgt); + ecs_query_t *q = ecs_query(world, { .expr = "(#10000, *)" }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add_id(world, e, ecs_pair(rel, tgt)); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + + char *expect = flecs_asprintf( + "{\"results\":[{\"name\":\"e\", \"fields\":{\"ids\":[[\"#10000\",\"#20000\"]]}}]}"); + test_json(json, expect); ecs_os_free(json); ecs_os_free(expect); - ecs_rule_fini(q); + ecs_query_fini(q); ecs_fini(world); } @@ -1795,48 +1560,50 @@ void SerializeIterToJson_serialize_invalid_value(void) { }); for (int i = 0; i < 10; i ++) { - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); ecs_set(world, e, T, {4}); } - ecs_query_t *q = ecs_query_new(world, "T"); + ecs_query_t *q = ecs_query(world, { .expr = "T" }); ecs_iter_t it = ecs_query_iter(world, q); ecs_log_set_level(-4); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); test_assert(json == NULL); + ecs_query_fini(q); + ecs_fini(world); } void SerializeIterToJson_serialize_recycled_pair_id(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t rel = ecs_new_entity(world, "Rel"); - ecs_entity_t tgt = ecs_new_entity(world, "Tgt"); + ecs_entity_t rel = ecs_entity(world, { .name = "Rel" }); + ecs_entity_t tgt = ecs_entity(world, { .name = "Tgt" }); ecs_delete(world, tgt); - tgt = ecs_new_entity(world, "Tgt"); + tgt = ecs_entity(world, { .name = "Tgt" }); ecs_entity_t e = ecs_new_w_pair(world, rel, tgt); ecs_set_name(world, e, "ent"); ecs_query_t *q = ecs_query(world, { - .filter.terms[0] = { + .terms[0] = { .first.id = rel, - .second.id = tgt + .second.id = EcsWildcard } }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); + char *json = ecs_iter_to_json(&it, NULL); test_assert(json != NULL); - test_str(json, "{\"ids\":[[\"Rel\",\"Tgt\"]], \"results\":[" - "{\"ids\":[[\"Rel\",\"Tgt\"]], \"sources\":[0], \"entities\":[\"ent\"]}" - "]}"); + test_json(json, "{\"results\":[{\"name\":\"ent\", \"fields\":{\"ids\":[[\"Rel\",\"Tgt\"]]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1848,12 +1615,12 @@ void SerializeIterToJson_serialize_w_alert(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_alert(world, { - .entity = ecs_new_entity(world, "position_without_velocity"), - .filter.expr = "Position, !Velocity", + .entity = ecs_entity(world, { .name = "position_without_velocity" }), + .query.expr = "Position, !Velocity", }); ecs_set(world, e1, Position, {10, 20}); @@ -1862,24 +1629,21 @@ void SerializeIterToJson_serialize_w_alert(void) { ecs_progress(world, 1.0); /* Evaluate alert logic */ - ecs_query_t *q = ecs_query_new(world, "Position"); + ecs_query_t *q = ecs_query(world, { .expr = "Position" }); test_assert(q != NULL); ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; - desc.serialize_ids = false; desc.serialize_values = false; - desc.serialize_is_set = false; - desc.serialize_sources = false; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - test_str(json, "{\"ids\":[[\"Position\"]], \"results\":[" - "{\"entities\":[\"e1\"]}, " - "{\"entities\":[\"e2\"], \"alerts\":true}]}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{}}, {\"name\":\"e2\", \"has_alerts\":true, \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } @@ -1898,31 +1662,19 @@ void SerializeIterToJson_serialize_no_this_alert_imported(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "Foo"); + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); ecs_set(world, e1, Position, {10, 20}); - ecs_rule_t *q = ecs_rule(world, { + ecs_query_t *q = ecs_query(world, { .expr = "Position($x)" }); - ecs_iter_t it = ecs_rule_iter(world, q); + ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"]], " - "\"vars\":[\"x\"], " - "\"results\":[{" - "\"ids\":[[\"Position\"]], " - "\"sources\":[\"Foo\"], " - "\"vars\":[\"Foo\"], " - "\"values\":[" - "{\"x\":10, \"y\":20}" - "]" - "}]" - "}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"vars\":{\"x\":\"Foo\"},\"fields\":{\"sources\":[\"Foo\"], \"values\":[{\"x\":10, \"y\":20}]}}]}"); ecs_os_free(json); - ecs_rule_fini(q); + ecs_query_fini(q); ecs_fini(world); } @@ -1932,17 +1684,17 @@ void SerializeIterToJson_serialize_paged_iterator(void) { ECS_COMPONENT(world, Position); - ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {30, 40}); - ecs_set(world, 0, Position, {10, 20}); + ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_insert(world, ecs_value(Position, {10, 20})); - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ .terms = {{ .id = ecs_id(Position) }} }); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); ecs_iter_t pit = ecs_page_iter(&it, 1, 3); test_bool(true, ecs_page_next(&pit)); @@ -1951,7 +1703,7 @@ void SerializeIterToJson_serialize_paged_iterator(void) { test_uint(e3, pit.entities[1]); test_uint(e4, pit.entities[2]); - Position *p = ecs_field(&pit, Position, 1); + Position *p = ecs_field(&pit, Position, 0); test_int(p[0].x, 10); test_int(p[0].y, 20); test_int(p[1].x, 20); @@ -1961,7 +1713,7 @@ void SerializeIterToJson_serialize_paged_iterator(void) { test_bool(false, ecs_page_next(&pit)); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -1972,33 +1724,33 @@ void SerializeIterToJson_serialize_paged_iterator_w_optional_component(void) { ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {40, 50}); + ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {40, 50})); ecs_set(world, e4, Velocity, {1, 2}); ecs_set(world, e5, Velocity, {2, 3}); - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ .terms = { { .id = ecs_id(Position) }, { .id = ecs_id(Velocity), .oper = EcsOptional } } }); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); ecs_iter_t pit = ecs_page_iter(&it, 1, 3); test_bool(true, ecs_page_next(&pit)); test_int(2, pit.count); - test_bool(true, ecs_field_is_set(&pit, 1)); - test_bool(false, ecs_field_is_set(&pit, 2)); + test_bool(true, ecs_field_is_set(&pit, 0)); + test_bool(false, ecs_field_is_set(&pit, 1)); test_uint(e2, pit.entities[0]); test_uint(e3, pit.entities[1]); - Position *p = ecs_field(&pit, Position, 1); + Position *p = ecs_field(&pit, Position, 0); test_int(p[0].x, 10); test_int(p[0].y, 20); test_int(p[1].x, 20); @@ -2006,21 +1758,21 @@ void SerializeIterToJson_serialize_paged_iterator_w_optional_component(void) { test_bool(true, ecs_page_next(&pit)); test_int(1, pit.count); + test_bool(true, ecs_field_is_set(&pit, 0)); test_bool(true, ecs_field_is_set(&pit, 1)); - test_bool(true, ecs_field_is_set(&pit, 2)); test_uint(e4, pit.entities[0]); - p = ecs_field(&pit, Position, 1); + p = ecs_field(&pit, Position, 0); test_int(p[0].x, 30); test_int(p[0].y, 40); - Velocity *v = ecs_field(&pit, Velocity, 2); + Velocity *v = ecs_field(&pit, Velocity, 1); test_int(v[0].x, 1); test_int(v[0].y, 2); test_bool(false, ecs_page_next(&pit)); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -2031,33 +1783,33 @@ void SerializeIterToJson_serialize_paged_iterator_w_optional_tag(void) { ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); - ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {10, 20}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {20, 30}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {30, 40}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {40, 50}); + ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {40, 50})); ecs_add(world, e4, Tag); ecs_add(world, e5, Tag); - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ .terms = { { .id = ecs_id(Position) }, { .id = Tag, .oper = EcsOptional } } }); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); ecs_iter_t pit = ecs_page_iter(&it, 1, 3); test_bool(true, ecs_page_next(&pit)); test_int(2, pit.count); - test_bool(true, ecs_field_is_set(&pit, 1)); - test_bool(false, ecs_field_is_set(&pit, 2)); + test_bool(true, ecs_field_is_set(&pit, 0)); + test_bool(false, ecs_field_is_set(&pit, 1)); test_uint(e2, pit.entities[0]); test_uint(e3, pit.entities[1]); - Position *p = ecs_field(&pit, Position, 1); + Position *p = ecs_field(&pit, Position, 0); test_int(p[0].x, 10); test_int(p[0].y, 20); test_int(p[1].x, 20); @@ -2065,17 +1817,17 @@ void SerializeIterToJson_serialize_paged_iterator_w_optional_tag(void) { test_bool(true, ecs_page_next(&pit)); test_int(1, pit.count); + test_bool(true, ecs_field_is_set(&pit, 0)); test_bool(true, ecs_field_is_set(&pit, 1)); - test_bool(true, ecs_field_is_set(&pit, 2)); test_uint(e4, pit.entities[0]); - p = ecs_field(&pit, Position, 1); + p = ecs_field(&pit, Position, 0); test_int(p[0].x, 30); test_int(p[0].y, 40); test_bool(false, ecs_page_next(&pit)); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -2093,17 +1845,17 @@ void SerializeIterToJson_serialize_paged_iterator_w_vars(void) { ecs_entity_t e4 = ecs_new_w_pair(world, Rel, ObjB); ecs_new_w_pair(world, Rel, ObjB); - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ .expr = "(Rel, $Var)" }); - int32_t var = ecs_rule_find_var(r, "Var"); + int32_t var = ecs_query_find_var(r, "Var"); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); ecs_iter_t pit = ecs_page_iter(&it, 1, 3); test_assert(var < pit.variable_count); - test_str("Var", pit.variable_names[var]); + test_json("Var", pit.variable_names[var]); test_bool(true, ecs_page_next(&pit)); test_int(2, pit.count); @@ -2118,7 +1870,7 @@ void SerializeIterToJson_serialize_paged_iterator_w_vars(void) { test_bool(false, ecs_page_next(&pit)); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -2155,9 +1907,9 @@ void SerializeIterToJson_serialize_table(void) { } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {20, 30}); @@ -2170,39 +1922,24 @@ void SerializeIterToJson_serialize_table(void) { ecs_set(world, e2, Velocity, {1, 1}); ecs_set(world, e3, Mass, {100}); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_id(Position) } } }); - ecs_iter_t it = ecs_filter_iter(world, f); + ecs_iter_t it = ecs_query_iter(world, f); - ecs_iter_to_json_desc_t desc = {0}; + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_table = true; - desc.serialize_ids = true; - desc.serialize_entities = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - test_str(json, "{\"results\":[" - "{" - "\"ids\":[[\"Position\"], [\"Foo\"]], " - "\"entities\":[\"e1\"], " - "\"values\":[[{\"x\":10, \"y\":20}], 0]" - "}, {" - "\"ids\":[[\"Position\"], [\"Velocity\"], [\"Foo\"], [\"Bar\"]], " - "\"entities\":[\"e2\"], " - "\"values\":[[{\"x\":20, \"y\":30}], [{\"x\":1, \"y\":1}], 0, 0]" - "}, {" - "\"ids\":[[\"Position\"], [\"Mass\"]], " - "\"entities\":[\"e3\"], " - "\"values\":[[{\"x\":30, \"y\":40}], [{\"value\":100}]]}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"tags\":[\"Foo\"], \"components\":{\"Position\":{\"x\":10, \"y\":20}}}, {\"name\":\"e2\", \"tags\":[\"Foo\", \"Bar\"], \"components\":{\"Position\":{\"x\":20, \"y\":30}, \"Velocity\":{\"x\":1, \"y\":1}}}, {\"name\":\"e3\", \"components\":{\"Position\":{\"x\":30, \"y\":40}, \"Mass\":{\"value\":100}}}]}"); ecs_os_free(json); - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -2243,9 +1980,9 @@ void SerializeIterToJson_serialize_table_w_id_labels(void) { ecs_doc_set_name(world, ecs_id(Velocity), "velocity"); ecs_doc_set_name(world, ecs_id(Mass), "mass"); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_set(world, e1, Position, {10, 20}); ecs_set(world, e2, Position, {20, 30}); @@ -2258,39 +1995,25 @@ void SerializeIterToJson_serialize_table_w_id_labels(void) { ecs_set(world, e2, Velocity, {1, 1}); ecs_set(world, e3, Mass, {100}); - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_id(Position) } } }); - ecs_iter_t it = ecs_filter_iter(world, f); + ecs_iter_t it = ecs_query_iter(world, f); - ecs_iter_to_json_desc_t desc = {0}; + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_table = true; - desc.serialize_entities = true; - desc.serialize_id_labels = true; - char *json = ecs_iter_to_json(world, &it, &desc); + desc.serialize_full_paths = false; + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - test_str(json, "{\"results\":[" - "{" - "\"id_labels\":[[\"position\"], [\"Foo\"]], " - "\"entities\":[\"e1\"], " - "\"values\":[[{\"x\":10, \"y\":20}], 0]" - "}, {" - "\"id_labels\":[[\"position\"], [\"velocity\"], [\"Foo\"], [\"Bar\"]], " - "\"entities\":[\"e2\"], " - "\"values\":[[{\"x\":20, \"y\":30}], [{\"x\":1, \"y\":1}], 0, 0]" - "}, {" - "\"id_labels\":[[\"position\"], [\"mass\"]], " - "\"entities\":[\"e3\"], " - "\"values\":[[{\"x\":30, \"y\":40}], [{\"value\":100}]]}]" - "}"); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"tags\":[\"Foo\"], \"components\":{\"position\":{\"x\":10, \"y\":20}}}, {\"name\":\"e2\", \"tags\":[\"Foo\", \"Bar\"], \"components\":{\"position\":{\"x\":20, \"y\":30}, \"velocity\":{\"x\":1, \"y\":1}}}, {\"name\":\"e3\", \"components\":{\"position\":{\"x\":30, \"y\":40}, \"mass\":{\"value\":100}}}]}"); ecs_os_free(json); - ecs_filter_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -2327,111 +2050,12 @@ void SerializeIterToJson_serialize_table_w_var_labels(void) { } }); - ecs_entity_t parent = ecs_new_entity(world, "Parent"); - ecs_doc_set_name(world, parent, "parent"); - - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - - ecs_add_pair(world, e1, EcsChildOf, parent); - ecs_add_pair(world, e2, EcsChildOf, parent); - ecs_add_pair(world, e3, EcsChildOf, parent); - - ecs_set(world, e1, Position, {10, 20}); - ecs_set(world, e2, Position, {20, 30}); - ecs_set(world, e3, Position, {30, 40}); - - ecs_add(world, e1, Foo); - ecs_add(world, e2, Foo); - ecs_add(world, e2, Bar); - - ecs_set(world, e2, Velocity, {1, 1}); - ecs_set(world, e3, Mass, {100}); - - ecs_rule_t *f = ecs_rule(world, { - .terms = { - { .id = ecs_id(Position) }, - { .first.id = EcsChildOf, .second.name = "p", .second.flags = EcsIsVariable } - } - }); - - ecs_iter_t it = ecs_rule_iter(world, f); - - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_table = true; - desc.serialize_entities = true; - desc.serialize_variable_labels = true; - desc.serialize_ids = true; - char *json = ecs_iter_to_json(world, &it, &desc); - test_assert(json != NULL); - - test_str(json, "{\"vars\":[\"p\"], \"results\":[" - "{" - "\"ids\":[[\"Position\"], [\"Foo\"]], " - "\"var_labels\":[\"parent\"], " - "\"parent\":\"Parent\", " - "\"entities\":[\"e1\"], " - "\"values\":[[{\"x\":10, \"y\":20}], 0]" - "}, {" - "\"ids\":[[\"Position\"], [\"Velocity\"], [\"Foo\"], [\"Bar\"]], " - "\"var_labels\":[\"parent\"], " - "\"parent\":\"Parent\", " - "\"entities\":[\"e2\"], " - "\"values\":[[{\"x\":20, \"y\":30}], [{\"x\":1, \"y\":1}], 0, 0]" - "}, {" - "\"ids\":[[\"Position\"], [\"Mass\"]], " - "\"var_labels\":[\"parent\"], " - "\"parent\":\"Parent\", " - "\"entities\":[\"e3\"], " - "\"values\":[[{\"x\":30, \"y\":40}], [{\"value\":100}]]}]" - "}"); - - ecs_os_free(json); - - ecs_rule_fini(f); - - ecs_fini(world); -} - -void SerializeIterToJson_serialize_table_w_private(void) { - ecs_world_t *world = ecs_init(); - - ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_COMPONENT(world, Mass); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_i32_t)}, - {"y", ecs_id(ecs_i32_t)} - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(Velocity), - .members = { - {"x", ecs_id(ecs_i32_t)}, - {"y", ecs_id(ecs_i32_t)} - } - }); - - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(Mass), - .members = { - {"value", ecs_id(ecs_i32_t)} - } - }); - - ecs_entity_t parent = ecs_new_entity(world, "Parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "Parent" }); ecs_doc_set_name(world, parent, "parent"); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); @@ -2448,48 +2072,26 @@ void SerializeIterToJson_serialize_table_w_private(void) { ecs_set(world, e2, Velocity, {1, 1}); ecs_set(world, e3, Mass, {100}); - ecs_rule_t *f = ecs_rule(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_id(Position) }, - { .first.id = EcsChildOf, .second.name = "p", .second.flags = EcsIsVariable } + { .first.id = EcsChildOf, .second.name = "p", .second.id = EcsIsVariable } } }); - ecs_iter_t it = ecs_rule_iter(world, f); + ecs_iter_t it = ecs_query_iter(world, f); - ecs_iter_to_json_desc_t desc = {0}; + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; desc.serialize_table = true; - desc.serialize_entities = true; - desc.serialize_variable_labels = true; - desc.serialize_ids = true; - desc.serialize_private = true; - char *json = ecs_iter_to_json(world, &it, &desc); + desc.serialize_full_paths = false; + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - test_str(json, "{\"vars\":[\"p\"], \"results\":[" - "{" - "\"ids\":[[\"Position\"], [\"Foo\"], [\"flecs.core.Identifier\",\"flecs.core.Name\"], [\"flecs.core.ChildOf\",\"Parent\"]], " - "\"var_labels\":[\"parent\"], " - "\"parent\":\"Parent\", " - "\"entities\":[\"e1\"], " - "\"values\":[[{\"x\":10, \"y\":20}], 0, 0, 0]" - "}, {" - "\"ids\":[[\"Position\"], [\"Velocity\"], [\"Foo\"], [\"Bar\"], [\"flecs.core.Identifier\",\"flecs.core.Name\"], [\"flecs.core.ChildOf\",\"Parent\"]], " - "\"var_labels\":[\"parent\"], " - "\"parent\":\"Parent\", " - "\"entities\":[\"e2\"], " - "\"values\":[[{\"x\":20, \"y\":30}], [{\"x\":1, \"y\":1}], 0, 0, 0, 0]" - "}, {" - "\"ids\":[[\"Position\"], [\"Mass\"], [\"flecs.core.Identifier\",\"flecs.core.Name\"], [\"flecs.core.ChildOf\",\"Parent\"]], " - "\"var_labels\":[\"parent\"], " - "\"parent\":\"Parent\", " - "\"entities\":[\"e3\"], " - "\"values\":[[{\"x\":30, \"y\":40}], [{\"value\":100}], 0, 0]}]" - "}"); + test_json(json, "{\"results\":[{\"parent\":\"Parent\", \"name\":\"e1\", \"tags\":[\"Foo\"],\"vars\":{\"p\":\"parent\"}, \"components\":{\"Position\":{\"x\":10, \"y\":20}}}, {\"parent\":\"Parent\", \"name\":\"e2\", \"tags\":[\"Foo\", \"Bar\"],\"vars\":{\"p\":\"parent\"}, \"components\":{\"Position\":{\"x\":20, \"y\":30}, \"Velocity\":{\"x\":1, \"y\":1}}}, {\"parent\":\"Parent\", \"name\":\"e3\", \"vars\":{\"p\":\"parent\"}, \"components\":{\"Position\":{\"x\":30, \"y\":40}, \"Mass\":{\"value\":100}}}]}"); ecs_os_free(json); - ecs_rule_fini(f); + ecs_query_fini(f); ecs_fini(world); } @@ -2507,7 +2109,7 @@ void SerializeIterToJson_serialize_world(void) { test_assert(json_2 != NULL); test_assert(json_1 != json_2); - test_str(json_1, json_2); + test_json(json_1, json_2); ecs_os_free(json_1); ecs_os_free(json_2); @@ -2520,25 +2122,24 @@ void SerializeIterToJson_serialize_w_offset(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); ecs_add(world, e1, Tag); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); ecs_add(world, e2, Tag); - ecs_rule_t *r = ecs_rule_new(world, "Tag($this), ?$this(_)"); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), ?$this(_)" }); + ecs_iter_t it = ecs_query_iter(world, r); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_entities = true; - char *json = ecs_iter_to_json(world, &it, &desc); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{\"results\":[{\"entities\":[\"e1\"]}, {\"entities\":[\"e2\"]}]}"); + test_json(json, + "{\"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false], \"ids\":[0, 0]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false], \"ids\":[0, 0]}}]}"); ecs_os_free(json); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -2548,27 +2149,31 @@ void SerializeIterToJson_serialize_labels_w_offset(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add(world, e1, Tag); ecs_doc_set_name(world, e1, "e1"); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_add(world, e2, Tag); ecs_doc_set_name(world, e2, "e2"); - ecs_rule_t *r = ecs_rule_new(world, "Tag($this), ?$this(_)"); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), ?$this(_)" }); + ecs_iter_t it = ecs_query_iter(world, r); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_entity_labels = true; - char *json = ecs_iter_to_json(world, &it, &desc); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_doc = true; + char *json = ecs_iter_to_json(&it, &desc); - test_str(json, - "{\"results\":[{\"entity_labels\":[\"e1\"]}, {\"entity_labels\":[\"e2\"]}]}"); + char *expect = flecs_asprintf( + "{\"results\":[{\"name\":\"#%u\", \"doc\":{\"label\":\"e1\"}, \"fields\":{\"is_set\":[true, false], \"ids\":[0, 0]}}, {\"name\":\"#%u\", \"doc\":{\"label\":\"e2\"}, \"fields\":{\"is_set\":[true, false], \"ids\":[0, 0]}}]}", + (uint32_t)e1, (uint32_t)e2); - ecs_os_free(json); + test_json(json, expect); - ecs_rule_fini(r); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(r); ecs_fini(world); } @@ -2578,27 +2183,31 @@ void SerializeIterToJson_serialize_colors_w_offset(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add(world, e1, Tag); ecs_doc_set_color(world, e1, "e1"); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_add(world, e2, Tag); ecs_doc_set_color(world, e2, "e2"); - ecs_rule_t *r = ecs_rule_new(world, "Tag($this), ?$this(_)"); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), ?$this(_)" }); + ecs_iter_t it = ecs_query_iter(world, r); + + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_doc = true; + char *json = ecs_iter_to_json(&it, &desc); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_colors = true; - char *json = ecs_iter_to_json(world, &it, &desc); + char *expect = flecs_asprintf( + "{\"results\":[{\"name\":\"#%u\", \"doc\":{\"label\":\"#%u\", \"color\":\"e1\"}, \"fields\":{\"is_set\":[true, false], \"ids\":[0, 0]}}, {\"name\":\"#%u\", \"doc\":{\"label\":\"#%u\", \"color\":\"e2\"}, \"fields\":{\"is_set\":[true, false], \"ids\":[0, 0]}}]}", + (uint32_t)e1, (uint32_t)e1, (uint32_t)e2, (uint32_t)e2); - test_str(json, - "{\"results\":[{\"colors\":[\"e1\"]}, {\"colors\":[\"e2\"]}]}"); + test_json(json, expect); ecs_os_free(json); + ecs_os_free(expect); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } @@ -2608,263 +2217,420 @@ void SerializeIterToJson_serialize_anonymous_entities_w_offset(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); ecs_add(world, e1, Tag); - ecs_entity_t e2 = ecs_new_id(world); + ecs_entity_t e2 = ecs_new(world); ecs_add(world, e2, Tag); - ecs_rule_t *r = ecs_rule_new(world, "Tag($this), ?$this(_)"); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), ?$this(_)" }); + ecs_iter_t it = ecs_query_iter(world, r); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_entities = true; - char *json = ecs_iter_to_json(world, &it, &desc); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + char *json = ecs_iter_to_json(&it, &desc); - char *expect = ecs_asprintf( - "{\"results\":[{\"entities\":[%u]}, {\"entities\":[%u]}]}", + char *expect = flecs_asprintf( + "{\"results\":[{\"name\":\"#%u\", \"fields\":{\"is_set\":[true, false], \"ids\":[0, 0]}}, {\"name\":\"#%u\", \"fields\":{\"is_set\":[true, false], \"ids\":[0, 0]}}]}", (uint32_t)e1, (uint32_t)e2); - test_str(json, expect); + test_json(json, expect); ecs_os_free(json); ecs_os_free(expect); - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void SerializeIterToJson_serialize_term_labels(void) { +void SerializeIterToJson_serialize_vars_for_query(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); - ecs_doc_set_name(world, ecs_id(Position), "position"); + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (ChildOf, $p)" + }); - ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(Position), - .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} - } + ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1", .parent = p }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2", .parent = p }); + ecs_add_id(world, e1, Foo); + ecs_add_id(world, e2, Foo); + + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + test_json(json, "{\"results\":[{\"parent\":\"Parent\", \"name\":\"e1\", \"vars\":{\"p\":\"Parent\"},\"fields\":{\"ids\":[0, [\"flecs.core.ChildOf\",\"Parent\"]]}}, {\"parent\":\"Parent\", \"name\":\"e2\", \"vars\":{\"p\":\"Parent\"},\"fields\":{\"ids\":[0, [\"flecs.core.ChildOf\",\"Parent\"]]}}]}"); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_var_labels_for_query(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (ChildOf, $p)" }); - ecs_query_t *q = ecs_query_new(world, "Position"); + ecs_entity_t p = ecs_entity(world, { .name = "Parent" }); + ecs_doc_set_name(world, p, "parent"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1", .parent = p }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2", .parent = p }); + ecs_add_id(world, e1, Foo); + ecs_add_id(world, e2, Foo); + + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_full_paths = false; ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + test_json(json, "{\"results\":[{\"parent\":\"Parent\", \"name\":\"e1\", \"vars\":{\"p\":\"parent\"},\"fields\":{\"ids\":[0, [\"flecs.core.ChildOf\",\"Parent\"]]}}, {\"parent\":\"Parent\", \"name\":\"e2\", \"vars\":{\"p\":\"parent\"},\"fields\":{\"ids\":[0, [\"flecs.core.ChildOf\",\"Parent\"]]}}]}"); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_term_labels = true; - char *json = ecs_iter_to_json(world, &it, &desc); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_null_doc_name(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + ecs_entity_t e = ecs_entity(world, { .name = "foo" }); + ecs_doc_set_name(world, e, "bar"); + ecs_doc_set_name(world, e, NULL); + ecs_add(world, e, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { + .id = Tag + } + }); + test_assert(q != NULL); + + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_doc = true; + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - test_str(json, "{\"id_labels\":[[\"position\"]], \"results\":[]}"); + test_json(json, "{\"results\":[{\"name\":\"foo\", \"doc\":{\"label\":\"foo\"}, \"fields\":{}}]}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_rule_w_optional(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_set_name(world, e1, "e1"); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_set_name(world, e2, "e2"); + ecs_add(world, e2, Bar); + + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Foo, ?Bar" + }); + + ecs_iter_t it = ecs_query_iter(world, r); + + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, true]}}]}"); ecs_os_free(json); + ecs_query_fini(r); + ecs_fini(world); } -void SerializeIterToJson_serialize_id_labels(void) { +void SerializeIterToJson_serialize_rule_w_optional_component(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); - - ecs_doc_set_name(world, ecs_id(Position), "position"); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Foo); ecs_struct_init(world, &(ecs_struct_desc_t){ .entity = ecs_id(Position), .members = { - {"x", ecs_id(ecs_f32_t)}, - {"y", ecs_id(ecs_f32_t)} + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} } }); - ecs_entity_t e = ecs_new_entity(world, "ent"); - ecs_set(world, e, Position, {1, 2}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set_name(world, e1, "e1"); - ecs_query_t *q = ecs_query_new(world, "Position"); - ecs_iter_t it = ecs_query_iter(world, q); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_set_name(world, e2, "e2"); + ecs_set(world, e2, Velocity, {1, 2}); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_id_labels = true; - char *json = ecs_iter_to_json(world, &it, &desc); - test_assert(json != NULL); - test_str(json, "{\"results\":[{\"id_labels\":[[\"position\"]]}]}"); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + ecs_set_name(world, e3, "e3"); + ecs_add(world, e3, Foo); + + ecs_query_t *r = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Position, ?Velocity" + }); + + ecs_iter_t it = ecs_query_iter(world, r); + + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false], \"values\":[{\"x\":10, \"y\":20}, 0]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, true], \"values\":[{\"x\":30, \"y\":40}, {\"x\":1, \"y\":2}]}}, {\"name\":\"e3\", \"fields\":{\"is_set\":[true, false], \"values\":[{\"x\":50, \"y\":60}, 0]}}]}"); ecs_os_free(json); + ecs_query_fini(r); + ecs_fini(world); } -void SerializeIterToJson_serialize_vars_for_query(void) { +void SerializeIterToJson_serialize_entity_w_flecs_core_parent(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_set_name(world, e1, "e1"); + + ecs_entity_t flecs_core_parent = + ecs_entity(world, { .name = "flecs.core.bob" }); + ecs_add_pair(world, e1, EcsChildOf, flecs_core_parent); ecs_query_t *q = ecs_query(world, { - .filter.terms[0] = { - .id = ecs_id(Position) - } + .expr = "Foo" }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_set(world, e1, Position, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_set(world, e2, Position, {3, 4}); + test_assert(q != NULL); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_entities = true; - desc.serialize_variables = true; ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, &desc); - test_assert(json != NULL); - test_str(json, "{\"results\":[{\"entities\":[\"e1\", \"e2\"]}]}"); + + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"parent\":\"flecs.core.bob\", \"name\":\"e1\", \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } -void SerializeIterToJson_serialize_var_labels_for_query(void) { +void SerializeIterToJson_no_fields(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); - ecs_query_t *q = ecs_query(world, { - .filter.terms[0] = { - .id = ecs_id(Position) - } - }); + ecs_entity_t e = ecs_entity(world, { .name = "e1" }); + ecs_set(world, e, Position, {10, 20}); + + ecs_query_t *q = ecs_query(world, { .expr = "Position" }); + ecs_iter_t it = ecs_query_iter(world, q); + + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_fields = false; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + test_json(json, "{\"results\":[{\"name\":\"e1\"}]}"); + + ecs_os_free(json); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_set(world, e1, Position, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_set(world, e2, Position, {3, 4}); + ecs_query_fini(q); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_entities = true; - desc.serialize_variable_labels = true; + ecs_fini(world); +} + +void SerializeIterToJson_no_fields_w_vars(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t p = ecs_entity(world, { .name = "parent" }); + ecs_entity_t e = ecs_entity(world, { .name = "e1", .parent = p }); + ecs_set(world, e, Position, {10, 20}); + + ecs_query_t *q = ecs_query(world, { .expr = "Position, (ChildOf, $p)" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, &desc); + + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_fields = false; + char *json = ecs_iter_to_json(&it, &desc); test_assert(json != NULL); - test_str(json, "{\"results\":[{\"entities\":[\"e1\", \"e2\"]}]}"); + test_json(json, "{\"results\":[{\"parent\":\"parent\", \"name\":\"e1\", \"vars\":{\"p\":\"parent\"}}]}"); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_from_stage(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, MyTag); + + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); + + ecs_add(world, e1, MyTag); + ecs_add(world, e2, MyTag); + + ecs_query_t *q = ecs_query(world, { .expr = "MyTag" }); + + ecs_world_t *stage = ecs_get_stage(world, 0); + ecs_iter_t it = ecs_query_iter(stage, q); + + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"fields\":{}}, {\"name\":\"Bar\", \"fields\":{}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } -void SerializeIterToJson_serialize_var_ids_for_query(void) { +void SerializeIterToJson_serialize_sparse(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); - ecs_query_t *q = ecs_query(world, { - .filter.terms[0] = { - .id = ecs_id(Position) + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} } }); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_set(world, e1, Position, {1, 2}); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_set(world, e2, Position, {3, 4}); + ecs_add_id(world, ecs_id(Position), EcsSparse); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_entities = true; - desc.serialize_variable_ids = true; + ecs_entity_t e1 = ecs_entity(world, { .name = "Foo" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "Bar" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + + ecs_query_t *q = ecs_query(world, { .expr = "Position" }); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, &desc); - test_assert(json != NULL); - test_str(json, "{\"results\":[{\"entities\":[\"e1\", \"e2\"]}]}"); + + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"Foo\", \"fields\":{\"values\":[{\"x\":10, \"y\":20}]}}, {\"name\":\"Bar\", \"fields\":{\"values\":[{\"x\":30, \"y\":40}]}}]}"); ecs_os_free(json); + ecs_query_fini(q); + ecs_fini(world); } -void SerializeIterToJson_serialize_null_doc_name(void) { +void SerializeIterToJson_serialize_15_fields(void) { ecs_world_t *world = ecs_init(); - ECS_TAG(world, Tag); - - ecs_entity_t e = ecs_new_entity(world, "foo"); - ecs_doc_set_name(world, e, "bar"); - ecs_doc_set_name(world, e, NULL); - ecs_add(world, e, Tag); + ECS_COMPONENT(world, Position); - ecs_query_t *q = ecs_query(world, { - .filter.terms[0] = { - .id = Tag + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} } }); - test_assert(q != NULL); - ecs_iter_to_json_desc_t desc = {0}; - desc.serialize_entities = true; - desc.serialize_entity_labels = true; + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_query_desc_t desc = {0}; + + int field_count = 15; + ecs_entity_t *tgts = ecs_os_malloc_n(ecs_entity_t, field_count); + + for (int i = 0; i < field_count; i ++) { + tgts[i] = ecs_new(world); + ecs_set_pair(world, e, Position, tgts[i], {i, i * 2}); + desc.terms[i].id = ecs_pair_t(Position, tgts[i]); + } + + ecs_query_t *q = ecs_query_init(world, &desc); + test_assert(q != NULL); ecs_iter_t it = ecs_query_iter(world, q); - char *json = ecs_iter_to_json(world, &it, &desc); - test_assert(json != NULL); - test_str(json, "{\"results\":[{\"entities\":[\"foo\"]}]}"); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"e\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}, {\"x\":1, \"y\":2}, {\"x\":2, \"y\":4}, {\"x\":3, \"y\":6}, {\"x\":4, \"y\":8}, {\"x\":5, \"y\":10}, {\"x\":6, \"y\":12}, {\"x\":7, \"y\":14}, {\"x\":8, \"y\":16}, {\"x\":9, \"y\":18}, {\"x\":10, \"y\":20}, {\"x\":11, \"y\":22}, {\"x\":12, \"y\":24}, {\"x\":13, \"y\":26}, {\"x\":14, \"y\":28}]}}]}"); ecs_os_free(json); + ecs_os_free(tgts); + + ecs_query_fini(q); ecs_fini(world); } -void SerializeIterToJson_serialize_rule_w_optional(void) { +void SerializeIterToJson_serialize_16_fields(void) { ecs_world_t *world = ecs_init(); - ECS_TAG(world, Foo); - ECS_TAG(world, Bar); - - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_set_name(world, e1, "e1"); - ecs_entity_t e2 = ecs_new(world, Foo); - ecs_set_name(world, e2, "e2"); - ecs_add(world, e2, Bar); + ECS_COMPONENT(world, Position); - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ - .expr = "Foo, ?Bar" + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } }); - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_query_desc_t desc = {0}; + + int field_count = 16; + ecs_entity_t *tgts = ecs_os_malloc_n(ecs_entity_t, field_count); - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Foo\"], [\"Bar\"]], " - "\"results\":[{" - "\"ids\":[[\"Foo\"], [\"Bar\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, false], " - "\"entities\":[" - "\"e1\"" - "]" - "}, {" - "\"ids\":[[\"Foo\"], [\"Bar\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, true], " - "\"entities\":[" - "\"e2\"" - "]" - "}]" - "}"); + for (int i = 0; i < field_count; i ++) { + tgts[i] = ecs_new(world); + ecs_set_pair(world, e, Position, tgts[i], {i, i * 2}); + desc.terms[i].id = ecs_pair_t(Position, tgts[i]); + } + ecs_query_t *q = ecs_query_init(world, &desc); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"e\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}, {\"x\":1, \"y\":2}, {\"x\":2, \"y\":4}, {\"x\":3, \"y\":6}, {\"x\":4, \"y\":8}, {\"x\":5, \"y\":10}, {\"x\":6, \"y\":12}, {\"x\":7, \"y\":14}, {\"x\":8, \"y\":16}, {\"x\":9, \"y\":18}, {\"x\":10, \"y\":20}, {\"x\":11, \"y\":22}, {\"x\":12, \"y\":24}, {\"x\":13, \"y\":26}, {\"x\":14, \"y\":28}, {\"x\":15, \"y\":30}]}}]}"); ecs_os_free(json); + ecs_os_free(tgts); - ecs_rule_fini(r); + ecs_query_fini(q); ecs_fini(world); } -void SerializeIterToJson_serialize_rule_w_optional_component(void) { +void SerializeIterToJson_serialize_31_fields(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); - ECS_COMPONENT(world, Velocity); - ECS_TAG(world, Foo); ecs_struct_init(world, &(ecs_struct_desc_t){ .entity = ecs_id(Position), @@ -2874,65 +2640,91 @@ void SerializeIterToJson_serialize_rule_w_optional_component(void) { } }); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_query_desc_t desc = {0}; + + int field_count = 31; + ecs_entity_t *tgts = ecs_os_malloc_n(ecs_entity_t, field_count); + + for (int i = 0; i < field_count; i ++) { + tgts[i] = ecs_new(world); + ecs_set_pair(world, e, Position, tgts[i], {i, i * 2}); + desc.terms[i].id = ecs_pair_t(Position, tgts[i]); + } + + ecs_query_t *q = ecs_query_init(world, &desc); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"e\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}, {\"x\":1, \"y\":2}, {\"x\":2, \"y\":4}, {\"x\":3, \"y\":6}, {\"x\":4, \"y\":8}, {\"x\":5, \"y\":10}, {\"x\":6, \"y\":12}, {\"x\":7, \"y\":14}, {\"x\":8, \"y\":16}, {\"x\":9, \"y\":18}, {\"x\":10, \"y\":20}, {\"x\":11, \"y\":22}, {\"x\":12, \"y\":24}, {\"x\":13, \"y\":26}, {\"x\":14, \"y\":28}, {\"x\":15, \"y\":30}, {\"x\":16, \"y\":32}, {\"x\":17, \"y\":34}, {\"x\":18, \"y\":36}, {\"x\":19, \"y\":38}, {\"x\":20, \"y\":40}, {\"x\":21, \"y\":42}, {\"x\":22, \"y\":44}, {\"x\":23, \"y\":46}, {\"x\":24, \"y\":48}, {\"x\":25, \"y\":50}, {\"x\":26, \"y\":52}, {\"x\":27, \"y\":54}, {\"x\":28, \"y\":56}, {\"x\":29, \"y\":58}, {\"x\":30, \"y\":60}]}}]}"); + ecs_os_free(json); + ecs_os_free(tgts); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_32_fields(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(Velocity), + .entity = ecs_id(Position), .members = { {"x", ecs_id(ecs_i32_t)}, {"y", ecs_id(ecs_i32_t)} } }); - ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20}); - ecs_set_name(world, e1, "e1"); + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_query_desc_t desc = {0}; - ecs_entity_t e2 = ecs_set(world, 0, Position, {30, 40}); - ecs_set_name(world, e2, "e2"); - ecs_set(world, e2, Velocity, {1, 2}); + int field_count = 32; + ecs_entity_t *tgts = ecs_os_malloc_n(ecs_entity_t, field_count); - ecs_entity_t e3 = ecs_set(world, 0, Position, {50, 60}); - ecs_set_name(world, e3, "e3"); - ecs_add(world, e3, Foo); + for (int i = 0; i < field_count; i ++) { + tgts[i] = ecs_new(world); + ecs_set_pair(world, e, Position, tgts[i], {i, i * 2}); + desc.terms[i].id = ecs_pair_t(Position, tgts[i]); + } - ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t){ - .expr = "Position, ?Velocity" + ecs_query_t *q = ecs_query_init(world, &desc); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"e\", \"fields\":{\"values\":[{\"x\":0, \"y\":0}, {\"x\":1, \"y\":2}, {\"x\":2, \"y\":4}, {\"x\":3, \"y\":6}, {\"x\":4, \"y\":8}, {\"x\":5, \"y\":10}, {\"x\":6, \"y\":12}, {\"x\":7, \"y\":14}, {\"x\":8, \"y\":16}, {\"x\":9, \"y\":18}, {\"x\":10, \"y\":20}, {\"x\":11, \"y\":22}, {\"x\":12, \"y\":24}, {\"x\":13, \"y\":26}, {\"x\":14, \"y\":28}, {\"x\":15, \"y\":30}, {\"x\":16, \"y\":32}, {\"x\":17, \"y\":34}, {\"x\":18, \"y\":36}, {\"x\":19, \"y\":38}, {\"x\":20, \"y\":40}, {\"x\":21, \"y\":42}, {\"x\":22, \"y\":44}, {\"x\":23, \"y\":46}, {\"x\":24, \"y\":48}, {\"x\":25, \"y\":50}, {\"x\":26, \"y\":52}, {\"x\":27, \"y\":54}, {\"x\":28, \"y\":56}, {\"x\":29, \"y\":58}, {\"x\":30, \"y\":60}, {\"x\":31, \"y\":62}]}}]}"); + ecs_os_free(json); + ecs_os_free(tgts); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToJson_serialize_field_w_escaped_sep(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t rel = ecs_entity(world, { .name = "Rel" }); + ecs_entity_t tgt = ecs_entity(world, { .name = "Tgt\\.Foo" }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add_pair(world, e, rel, tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(rel, EcsWildcard) }} }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_json(json, "{\"results\":[{\"name\":\"e\", \"fields\":{\"ids\":[[\"Rel\",\"Tgt\\\\.Foo\"]]}}]}"); + ecs_os_free(json); - ecs_iter_t it = ecs_rule_iter(world, r); - - char *json = ecs_iter_to_json(world, &it, NULL); - test_str(json, - "{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"results\":[{" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, false], " - "\"entities\":[" - "\"e1\"" - "], " - "\"values\":[[{\"x\":10, \"y\":20}], []]" - "}, {" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, true], " - "\"entities\":[" - "\"e2\"" - "], " - "\"values\":[[{\"x\":30, \"y\":40}], [{\"x\":1, \"y\":2}]]" - "}, {" - "\"ids\":[[\"Position\"], [\"Velocity\"]], " - "\"sources\":[0, 0], " - "\"is_set\":[true, false], " - "\"entities\":[" - "\"e3\"" - "], " - "\"values\":[[{\"x\":50, \"y\":60}], []]" - "}]" - "}"); - - ecs_os_free(json); - - ecs_rule_fini(r); + ecs_query_fini(q); ecs_fini(world); } diff --git a/vendors/flecs/test/meta/src/SerializeIterToRowJson.c b/vendors/flecs/test/meta/src/SerializeIterToRowJson.c new file mode 100644 index 000000000..b4cfd5448 --- /dev/null +++ b/vendors/flecs/test/meta/src/SerializeIterToRowJson.c @@ -0,0 +1,2860 @@ +#include + +void SerializeIterToRowJson_serialize_this_w_1_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{}}, {\"name\":\"e2\", \"fields\":{}}, {\"name\":\"e3\", \"fields\":{}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_1_tag_w_parent(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA" + }); + + test_assert(q != NULL); + + ecs_entity_t p1 = ecs_entity(world, { .name = "p1" }); + ecs_entity_t p2 = ecs_entity(world, { .name = "p2" }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, EcsChildOf, p1); + ecs_add_pair(world, e2, EcsChildOf, p1); + ecs_add_pair(world, e3, EcsChildOf, p2); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"parent\":\"p1\", \"name\":\"e1\", \"fields\":{}}, {\"parent\":\"p1\", \"name\":\"e2\", \"fields\":{}}, {\"parent\":\"p2\", \"name\":\"e3\", \"fields\":{}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_1_tag_no_name(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA" + }); + + test_assert(q != NULL); + + ecs_entity_t p1 = ecs_entity(world, { .name = "p1" }); + ecs_entity_t p2 = ecs_entity(world, { .name = "p1" }); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, EcsChildOf, p1); + ecs_add_pair(world, e2, EcsChildOf, p1); + ecs_add_pair(world, e3, EcsChildOf, p2); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char *expect = flecs_asprintf("{\"results\":[{\"parent\":\"p1\", \"name\":\"#%u\", \"fields\":{}}, {\"parent\":\"p1\", \"name\":\"#%u\", \"fields\":{}}, {\"parent\":\"p1\", \"name\":\"#%u\", \"fields\":{}}]}", + (uint32_t)e1, + (uint32_t)e2, + (uint32_t)e3); + test_json(json, expect); + + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_2_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, TagB" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{}}, {\"name\":\"e2\", \"fields\":{}}, {\"name\":\"e3\", \"fields\":{}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_1_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{\"values\":[{\"x\":10, \"y\":20}]}}, {\"name\":\"e2\", \"fields\":{\"values\":[{\"x\":20, \"y\":30}]}}, {\"name\":\"e3\", \"fields\":{\"values\":[{\"x\":30, \"y\":40}]}}]}"; + + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_2_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Mass" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{\"values\":[{\"x\":10, \"y\":20}, {\"value\":1}]}}, {\"name\":\"e2\", \"fields\":{\"values\":[{\"x\":20, \"y\":30}, {\"value\":2}]}}, {\"name\":\"e3\", \"fields\":{\"values\":[{\"x\":30, \"y\":40}, {\"value\":3}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_2_component_1_shared(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Mass" + }); + + test_assert(q != NULL); + + ecs_entity_t base_1 = ecs_entity(world, { .name = "base_1" }); + ecs_entity_t base_2 = ecs_entity(world, { .name = "base_2" }); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_add_pair(world, e1, EcsIsA, base_1); + ecs_add_pair(world, e2, EcsIsA, base_1); + ecs_add_pair(world, e3, EcsIsA, base_2); + + ecs_set(world, base_1, Mass, {1}); + ecs_set(world, base_2, Mass, {2}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{\"sources\":[0, \"base_1\"], \"values\":[{\"x\":10, \"y\":20}, {\"value\":1}]}}, {\"name\":\"e2\", \"fields\":{\"sources\":[0, \"base_1\"], \"values\":[{\"x\":20, \"y\":30}, {\"value\":1}]}}, {\"name\":\"e3\", \"fields\":{\"sources\":[0, \"base_2\"], \"values\":[{\"x\":30, \"y\":40}, {\"value\":2}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_1_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, *)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]]}}, {\"name\":\"e2\", \"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]]}}, {\"name\":\"e3\", \"fields\":{\"ids\":[[\"RelA\",\"TgtB\"]]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_1_pair_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Position, *)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set_pair(world, e1, Position, TgtA, {10, 20}); + ecs_set_pair(world, e2, Position, TgtA, {20, 30}); + ecs_set_pair(world, e3, Position, TgtB, {30, 40}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{\"ids\":[[\"Position\",\"TgtA\"]], \"values\":[{\"x\":10, \"y\":20}]}}, {\"name\":\"e2\", \"fields\":{\"ids\":[[\"Position\",\"TgtA\"]], \"values\":[{\"x\":20, \"y\":30}]}}, {\"name\":\"e3\", \"fields\":{\"ids\":[[\"Position\",\"TgtB\"]], \"values\":[{\"x\":30, \"y\":40}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_2_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, *), (RelB, *)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{\"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}, {\"name\":\"e2\", \"fields\":{\"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}, {\"name\":\"e3\", \"fields\":{\"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtB\"]]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_1_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, $a)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"vars\":{\"a\":\"TgtA\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]]}}, {\"name\":\"e2\", \"vars\":{\"a\":\"TgtA\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]]}}, {\"name\":\"e3\", \"vars\":{\"a\":\"TgtB\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtB\"]]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_2_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, $a), (RelB, $b)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"vars\":{\"a\":\"TgtA\", \"b\":\"TgtA\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}, {\"name\":\"e2\", \"vars\":{\"a\":\"TgtA\", \"b\":\"TgtA\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}, {\"name\":\"e3\", \"vars\":{\"a\":\"TgtA\", \"b\":\"TgtB\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtB\"]]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_2_var_doc_name(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_doc_set_name(world, TgtA, "Target A"); + ecs_doc_set_name(world, TgtB, "Target B"); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, $a), (RelB, $b)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_full_paths = false; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"vars\":{\"a\":\"Target A\", \"b\":\"Target A\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}, {\"name\":\"e2\", \"vars\":{\"a\":\"Target A\", \"b\":\"Target A\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}, {\"name\":\"e3\", \"vars\":{\"a\":\"Target A\", \"b\":\"Target B\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtB\"]]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_1_tag_component_pair_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, (RelA, $a), Position" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"vars\":{\"a\":\"TgtA\"},\"fields\":{\"ids\":[0, [\"RelA\",\"TgtA\"], 0], \"values\":[0, 0, {\"x\":10, \"y\":20}]}}, {\"name\":\"e2\", \"vars\":{\"a\":\"TgtA\"},\"fields\":{\"ids\":[0, [\"RelA\",\"TgtA\"], 0], \"values\":[0, 0, {\"x\":20, \"y\":30}]}}, {\"name\":\"e3\", \"vars\":{\"a\":\"TgtA\"},\"fields\":{\"ids\":[0, [\"RelA\",\"TgtA\"], 0], \"values\":[0, 0, {\"x\":30, \"y\":40}]}}]}"; + + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_this_w_2_tag_component_pair_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, TagB, (RelA, $a), (RelB, $b), Position, Mass" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"vars\":{\"a\":\"TgtA\", \"b\":\"TgtA\"},\"fields\":{\"ids\":[0, 0, [\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"], 0, 0], \"values\":[0, 0, 0, 0, {\"x\":10, \"y\":20}, {\"value\":1}]}}, {\"name\":\"e2\", \"vars\":{\"a\":\"TgtA\", \"b\":\"TgtA\"},\"fields\":{\"ids\":[0, 0, [\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"], 0, 0], \"values\":[0, 0, 0, 0, {\"x\":20, \"y\":30}, {\"value\":2}]}}, {\"name\":\"e3\", \"vars\":{\"a\":\"TgtA\", \"b\":\"TgtB\"},\"fields\":{\"ids\":[0, 0, [\"RelA\",\"TgtA\"], [\"RelB\",\"TgtB\"], 0, 0], \"values\":[0, 0, 0, 0, {\"x\":30, \"y\":40}, {\"value\":3}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_var_w_1_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"x\":\"e1\"},\"fields\":{\"sources\":[\"e1\"]}}, {\"vars\":{\"x\":\"e2\"},\"fields\":{\"sources\":[\"e2\"]}}, {\"vars\":{\"x\":\"e3\"},\"fields\":{\"sources\":[\"e3\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_var_w_1_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($x)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"x\":\"e1\"},\"fields\":{\"sources\":[\"e1\"], \"values\":[{\"x\":10, \"y\":20}]}}, {\"vars\":{\"x\":\"e2\"},\"fields\":{\"sources\":[\"e2\"], \"values\":[{\"x\":20, \"y\":30}]}}, {\"vars\":{\"x\":\"e3\"},\"fields\":{\"sources\":[\"e3\"], \"values\":[{\"x\":30, \"y\":40}]}}]}"; + + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_var_w_1_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x, *)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"x\":\"e1\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]], \"sources\":[\"e1\"]}}, {\"vars\":{\"x\":\"e2\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]], \"sources\":[\"e2\"]}}, {\"vars\":{\"x\":\"e3\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtB\"]], \"sources\":[\"e3\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_var_w_1_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x, $a)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"x\":\"e1\", \"a\":\"TgtA\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]], \"sources\":[\"e1\"]}}, {\"vars\":{\"x\":\"e2\", \"a\":\"TgtA\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]], \"sources\":[\"e2\"]}}, {\"vars\":{\"x\":\"e3\", \"a\":\"TgtB\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtB\"]], \"sources\":[\"e3\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_var_w_2_component_1_shared(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($x), Mass($x)" + }); + + test_assert(q != NULL); + + ecs_entity_t base_1 = ecs_entity(world, { .name = "base_1" }); + ecs_entity_t base_2 = ecs_entity(world, { .name = "base_2" }); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_add_pair(world, e1, EcsIsA, base_1); + ecs_add_pair(world, e2, EcsIsA, base_1); + ecs_add_pair(world, e3, EcsIsA, base_2); + + ecs_set(world, base_1, Mass, {1}); + ecs_set(world, base_2, Mass, {2}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"x\":\"e1\"},\"fields\":{\"sources\":[\"e1\", \"base_1\"], \"values\":[{\"x\":10, \"y\":20}, {\"value\":1}]}}, {\"vars\":{\"x\":\"e2\"},\"fields\":{\"sources\":[\"e2\", \"base_1\"], \"values\":[{\"x\":20, \"y\":30}, {\"value\":1}]}}, {\"vars\":{\"x\":\"e3\"},\"fields\":{\"sources\":[\"e3\", \"base_2\"], \"values\":[{\"x\":30, \"y\":40}, {\"value\":2}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_var_w_1_tag_component_pair_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), RelA($x, $a), Position($x)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"x\":\"e1\", \"a\":\"TgtA\"},\"fields\":{\"ids\":[0, [\"RelA\",\"TgtA\"], 0], \"sources\":[\"e1\", \"e1\", \"e1\"], \"values\":[0, 0, {\"x\":10, \"y\":20}]}}, {\"vars\":{\"x\":\"e2\", \"a\":\"TgtA\"},\"fields\":{\"ids\":[0, [\"RelA\",\"TgtA\"], 0], \"sources\":[\"e2\", \"e2\", \"e2\"], \"values\":[0, 0, {\"x\":20, \"y\":30}]}}, {\"vars\":{\"x\":\"e3\", \"a\":\"TgtA\"},\"fields\":{\"ids\":[0, [\"RelA\",\"TgtA\"], 0], \"sources\":[\"e3\", \"e3\", \"e3\"], \"values\":[0, 0, {\"x\":30, \"y\":40}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_var_w_2_tag_component_pair_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), TagB($x), RelA($x, $a), RelB($x, $b), Position($x), Mass($x)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"x\":\"e1\", \"a\":\"TgtA\", \"b\":\"TgtA\"},\"fields\":{\"ids\":[0, 0, [\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"], 0, 0], \"sources\":[\"e1\", \"e1\", \"e1\", \"e1\", \"e1\", \"e1\"], \"values\":[0, 0, 0, 0, {\"x\":10, \"y\":20}, {\"value\":1}]}}, {\"vars\":{\"x\":\"e2\", \"a\":\"TgtA\", \"b\":\"TgtA\"},\"fields\":{\"ids\":[0, 0, [\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"], 0, 0], \"sources\":[\"e2\", \"e2\", \"e2\", \"e2\", \"e2\", \"e2\"], \"values\":[0, 0, 0, 0, {\"x\":20, \"y\":30}, {\"value\":2}]}}, {\"vars\":{\"x\":\"e3\", \"a\":\"TgtA\", \"b\":\"TgtB\"},\"fields\":{\"ids\":[0, 0, [\"RelA\",\"TgtA\"], [\"RelB\",\"TgtB\"], 0, 0], \"sources\":[\"e3\", \"e3\", \"e3\", \"e3\", \"e3\", \"e3\"], \"values\":[0, 0, 0, 0, {\"x\":30, \"y\":40}, {\"value\":3}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_fixed_w_1_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(e1)" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"fields\":{\"sources\":[\"e1\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_fixed_w_1_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(e1)" + }); + + test_assert(q != NULL); + + ecs_set(world, e1, Position, {10, 20}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"fields\":{\"sources\":[\"e1\"], \"values\":[{\"x\":10, \"y\":20}]}}]}"; + + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_fixed_w_1_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA(e1, *)" + }); + + test_assert(q != NULL); + + ecs_add_pair(world, e1, RelA, TgtA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]], \"sources\":[\"e1\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_fixed_w_1_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA(e1, $a)" + }); + + test_assert(q != NULL); + + ecs_add_pair(world, e1, RelA, TgtA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"a\":\"TgtA\"},\"fields\":{\"ids\":[[\"RelA\",\"TgtA\"]], \"sources\":[\"e1\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_fixed_w_2_component_1_shared(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(e1), Mass(e1)" + }); + + test_assert(q != NULL); + + ecs_entity_t base_1 = ecs_entity(world, { .name = "base_1" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_add_pair(world, e1, EcsIsA, base_1); + ecs_set(world, base_1, Mass, {1}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"fields\":{\"sources\":[\"e1\", \"base_1\"], \"values\":[{\"x\":10, \"y\":20}, {\"value\":1}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_fixed_w_1_tag_component_pair_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(e1), RelA(e1, $a), Position(e1)" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_set(world, e1, Position, {10, 20}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"a\":\"TgtA\"},\"fields\":{\"ids\":[0, [\"RelA\",\"TgtA\"], 0], \"sources\":[\"e1\", \"e1\", \"e1\"], \"values\":[0, 0, {\"x\":10, \"y\":20}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_fixed_w_2_tag_component_pair_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(e1), TagB(e1), RelA(e1, $a), RelB(e1, $b), Position(e1), Mass(e1)" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + ecs_add(world, e1, TagB); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Mass, {1}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"vars\":{\"a\":\"TgtA\", \"b\":\"TgtA\"},\"fields\":{\"ids\":[0, 0, [\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"], 0, 0], \"sources\":[\"e1\", \"e1\", \"e1\", \"e1\", \"e1\", \"e1\"], \"values\":[0, 0, 0, 0, {\"x\":10, \"y\":20}, {\"value\":1}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_not(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, !Mass" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e3\", \"fields\":{\"is_set\":[true, false], \"values\":[{\"x\":30, \"y\":40}, 0]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_not_pair_wildcard(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, *), !(RelB, *)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false], \"ids\":[[\"RelA\",\"TgtA\"], 0]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false], \"ids\":[[\"RelA\",\"TgtA\"], 0]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_not_pair_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, *), !(RelB, $var)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e3\", \"vars\":{\"var\":\"*\"},\"fields\":{\"is_set\":[true, false], \"ids\":[[\"RelA\",\"TgtA\"], 0]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_not_pair_var_constrained(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, $var), !(RelB, $var)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtB); + ecs_add_pair(world, e2, RelB, TgtB); + ecs_add_pair(world, e3, RelB, TgtA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"vars\":{\"var\":\"TgtA\"},\"fields\":{\"is_set\":[true, false], \"ids\":[[\"RelA\",\"TgtA\"], 0]}}, {\"name\":\"e2\", \"vars\":{\"var\":\"TgtA\"},\"fields\":{\"is_set\":[true, false], \"ids\":[[\"RelA\",\"TgtA\"], 0]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_optional(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Mass" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e3\", \"fields\":{\"is_set\":[true, false], \"values\":[{\"x\":30, \"y\":40}, 0]}}, {\"name\":\"e1\", \"fields\":{\"is_set\":[true, true], \"values\":[{\"x\":10, \"y\":20}, {\"value\":1}]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, true], \"values\":[{\"x\":20, \"y\":30}, {\"value\":2}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_optional_pair_wildcard(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, *), ?(RelB, *)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e3\", \"fields\":{\"is_set\":[true, false], \"ids\":[[\"RelA\",\"TgtA\"], 0]}}, {\"name\":\"e1\", \"fields\":{\"is_set\":[true, true], \"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, true], \"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_optional_pair_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, *), ?(RelB, $var)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e3\", \"vars\":{\"var\":\"*\"},\"fields\":{\"is_set\":[true, false], \"ids\":[[\"RelA\",\"TgtA\"], 0]}}, {\"name\":\"e1\", \"vars\":{\"var\":\"TgtA\"},\"fields\":{\"is_set\":[true, true], \"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}, {\"name\":\"e2\", \"vars\":{\"var\":\"TgtA\"},\"fields\":{\"is_set\":[true, true], \"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_optional_pair_var_constrained(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelA, $var), ?(RelB, $var)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtB); + ecs_add_pair(world, e2, RelB, TgtB); + ecs_add_pair(world, e3, RelB, TgtA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"vars\":{\"var\":\"TgtA\"},\"fields\":{\"is_set\":[true, false], \"ids\":[[\"RelA\",\"TgtA\"], 0]}}, {\"name\":\"e2\", \"vars\":{\"var\":\"TgtA\"},\"fields\":{\"is_set\":[true, false], \"ids\":[[\"RelA\",\"TgtA\"], 0]}}, {\"name\":\"e3\", \"vars\":{\"var\":\"TgtA\"},\"fields\":{\"is_set\":[true, true], \"ids\":[[\"RelA\",\"TgtA\"], [\"RelB\",\"TgtA\"]]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_or(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Position, TgtA) || (Position, TgtB)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set_pair(world, e1, Position, TgtA, {10, 20}); + ecs_set_pair(world, e2, Position, TgtA, {20, 30}); + ecs_set_pair(world, e3, Position, TgtB, {30, 40}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{\"ids\":[[\"Position\",\"TgtA\"]], \"values\":[{\"x\":10, \"y\":20}]}}, {\"name\":\"e2\", \"fields\":{\"ids\":[[\"Position\",\"TgtA\"]], \"values\":[{\"x\":20, \"y\":30}]}}, {\"name\":\"e3\", \"fields\":{\"ids\":[[\"Position\",\"TgtB\"]], \"values\":[{\"x\":30, \"y\":40}]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !{ChildOf($child, $this), TagB($child)}, TagB" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_entity_t child_e1_1 = ecs_entity(world, { .name = "child_1" }); + ecs_add_pair(world, child_e1_1, EcsChildOf, e1); + + ecs_entity_t child_e2_1 = ecs_entity(world, { .name = "child_1" }); + ecs_add_pair(world, child_e2_1, EcsChildOf, e2); + + ecs_entity_t child_e2_2 = ecs_entity(world, { .name = "child_2" }); + ecs_add_pair(world, child_e2_2, EcsChildOf, e2); + ecs_add(world, child_e2_2, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + /* TODO: this result is a bit wonky because rules don't correctly handle + * hiding fields within scopes yet. This test is just here to make + * the serializer doesn't crash, and will be updated after the query + * refactor. */ + char* expect = "{\"results\":[{\"name\":\"e1\", \"vars\":{\"child\":\"*\"},\"fields\":{\"is_set\":[true, false, true, true, false, true], \"ids\":[0, 0, [\"flecs.core.ChildOf\",\"e1\"], 0, 0, 0]}}, {\"name\":\"e3\", \"vars\":{\"child\":\"*\"},\"fields\":{\"is_set\":[true, false, true, true, false, true], \"ids\":[0, 0, [\"flecs.core.ChildOf\",\"e2\"], 0, 0, 0]}}]}"; + + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_eq(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, $this == e2" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e2\", \"fields\":{}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_neq(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, $this != e2" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false]}}, {\"name\":\"e3\", \"fields\":{\"is_set\":[true, false]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_eq_m(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, $this ~= \"e2\"" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e2\", \"fields\":{}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_table(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_table = true; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"tags\":[\"TagA\", \"TagB\"],\"pairs\":{\"RelA\":\"TgtA\", \"RelB\":\"TgtA\"}, \"components\":{\"Position\":{\"x\":10, \"y\":20}, \"Mass\":{\"value\":1}}}, {\"name\":\"e2\", \"tags\":[\"TagA\", \"TagB\"],\"pairs\":{\"RelA\":\"TgtA\", \"RelB\":\"TgtA\"}, \"components\":{\"Position\":{\"x\":20, \"y\":30}, \"Mass\":{\"value\":2}}}, {\"name\":\"e3\", \"tags\":[\"TagA\", \"TagB\"],\"pairs\":{\"RelA\":\"TgtA\", \"RelB\":\"TgtB\"}, \"components\":{\"Position\":{\"x\":30, \"y\":40}, \"Mass\":{\"value\":3}}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_table_w_eq(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, $this == e2" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_table = true; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e2\", \"tags\":[\"TagA\", \"TagB\"],\"pairs\":{\"RelA\":\"TgtA\", \"RelB\":\"TgtA\"}, \"components\":{\"Position\":{\"x\":20, \"y\":30}, \"Mass\":{\"value\":2}}}]}"; + test_json(json, expect); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_table_w_neq(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, $this != e2" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = ECS_ITER_TO_JSON_INIT; + desc.serialize_table = true; + char *json = ecs_iter_to_json(&it, &desc); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"tags\":[\"TagA\", \"TagB\"],\"pairs\":{\"RelA\":\"TgtA\", \"RelB\":\"TgtA\"}, \"components\":{\"Position\":{\"x\":10, \"y\":20}, \"Mass\":{\"value\":1}}}, {\"name\":\"e3\", \"tags\":[\"TagA\", \"TagB\"],\"pairs\":{\"RelA\":\"TgtA\", \"RelB\":\"TgtB\"}, \"components\":{\"Position\":{\"x\":30, \"y\":40}, \"Mass\":{\"value\":3}}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_table_w_2_pair_targets(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_table = true, + }); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":\"TgtA\"}}, {\"name\":\"e2\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":\"TgtA\"}}, {\"name\":\"e3\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":[\"TgtA\", \"TgtB\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_table_w_2_pair_targets_2_rel(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + ecs_add_pair(world, e3, RelB, TgtB); + ecs_add_pair(world, e3, RelB, TgtC); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_table = true, + }); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":\"TgtA\"}}, {\"name\":\"e2\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":\"TgtA\"}}, {\"name\":\"e3\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":[\"TgtA\", \"TgtB\"], \"RelB\":[\"TgtB\", \"TgtC\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_table_w_3_pair_targets(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, TgtD); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add_pair(world, e1, RelA, TgtA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtB); + + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelA, TgtC); + ecs_add_pair(world, e3, RelA, TgtD); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_table = true, + }); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":\"TgtA\"}}, {\"name\":\"e2\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":[\"TgtA\", \"TgtB\"]}}, {\"name\":\"e3\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":[\"TgtB\", \"TgtC\", \"TgtD\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_table_w_3_pair_targets_2_rel(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, TgtD); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add_pair(world, e1, RelA, TgtA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtB); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelA, TgtC); + + ecs_add_pair(world, e3, RelB, TgtB); + ecs_add_pair(world, e3, RelB, TgtC); + ecs_add_pair(world, e3, RelB, TgtD); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_table = true, + }); + test_assert(json != NULL); + + char* expect = "{\"results\":[{\"name\":\"e1\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":\"TgtA\"}}, {\"name\":\"e2\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":[\"TgtA\", \"TgtB\"]}}, {\"name\":\"e3\", \"tags\":[\"TagA\"],\"pairs\":{\"RelA\":[\"TgtA\", \"TgtB\", \"TgtC\"], \"RelB\":[\"TgtB\", \"TgtC\", \"TgtD\"]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_everything(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "_" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + /* JSON string would change too often to match exactly. This test is more + * intended as a catch all, to make sure the serializer doesn't crash. */ + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_everything_table(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_add_id(world, TagA, EcsPrivate); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) }, + } + }); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "_" + }); + + test_assert(q != NULL); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_table = true + }); + test_assert(json != NULL); + /* JSON string would change too often to match exactly. This test is more + * intended as a catch all, to make sure the serializer doesn't crash. */ + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_w_type_info(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_type_info = true, + .serialize_fields = true + }); + test_assert(json != NULL); + + char* expect = "{\"type_info\":{\"Position\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, \"results\":[{\"name\":\"e1\", \"fields\":{}}, {\"name\":\"e2\", \"fields\":{}}, {\"name\":\"e3\", \"fields\":{}}]}"; + + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_w_field_info(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Mass" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e3, Mass, {100}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_field_info = true, + .serialize_fields = true + }); + test_assert(json != NULL); + + char* expect = "{\"field_info\":[{\"id\":\"Position\", \"type\":\"Position\", \"symbol\":\"Position\", \"schema\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, {\"id\":\"Mass\", \"optional\":true, \"type\":\"Mass\", \"symbol\":\"Mass\", \"schema\":{\"value\":[\"int\"]}}], \"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false]}}, {\"name\":\"e3\", \"fields\":{\"is_set\":[true, true]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_w_field_info_pair_w_0_target(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Mass, (ChildOf, #0)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e3, Mass, {100}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_field_info = true, + .serialize_fields = true + }); + test_assert(json != NULL); + + char* expect = "{\"field_info\":[{\"id\":\"Position\", \"type\":\"Position\", \"symbol\":\"Position\", \"schema\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, {\"id\":\"Mass\", \"optional\":true, \"type\":\"Mass\", \"symbol\":\"Mass\", \"schema\":{\"value\":[\"int\"]}}, {\"id\":\"(flecs.core.ChildOf,#0)\", \"exclusive\":true}], \"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false, true]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false, true]}}, {\"name\":\"e3\", \"fields\":{\"is_set\":[true, true, true]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_w_field_info_pair_w_not_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, !Foo" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_add(world, e3, Foo); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_field_info = true, + .serialize_fields = true + }); + test_assert(json != NULL); + + char* expect = "{\"field_info\":[{\"id\":\"Position\", \"type\":\"Position\", \"symbol\":\"Position\", \"schema\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, {\"id\":\"Foo\", \"not\":true}], \"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_w_field_info_pair_w_not_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, !(ChildOf, flecs)" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_add(world, e3, Foo); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_field_info = true, + .serialize_fields = true + }); + test_assert(json != NULL); + + char* expect = "{\"field_info\":[{\"id\":\"Position\", \"type\":\"Position\", \"symbol\":\"Position\", \"schema\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, {\"id\":\"(flecs.core.ChildOf,flecs)\", \"not\":true}], \"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false]}}, {\"name\":\"e3\", \"fields\":{\"is_set\":[true, false]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_w_field_info_pair_w_not_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, !Mass" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {30, 40}); + + ecs_set(world, e3, Mass, {100}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_field_info = true, + .serialize_fields = true + }); + test_assert(json != NULL); + + char* expect = "{\"field_info\":[{\"id\":\"Position\", \"type\":\"Position\", \"symbol\":\"Position\", \"schema\":{\"x\":[\"int\"], \"y\":[\"int\"]}}, {\"id\":\"Mass\", \"not\":true}], \"results\":[{\"name\":\"e1\", \"fields\":{\"is_set\":[true, false]}}, {\"name\":\"e2\", \"fields\":{\"is_set\":[true, false]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_w_field_info_w_or(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + { "x", ecs_id(ecs_i32_t) }, + { "y", ecs_id(ecs_i32_t) } + } + }); + + ecs_struct(world, { + .entity = ecs_id(Mass), + .members = { + { "value", ecs_id(ecs_i32_t) } + } + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position || Mass" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Mass, {100}); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, &(ecs_iter_to_json_desc_t) { + .serialize_field_info = true, + .serialize_fields = true + }); + test_assert(json != NULL); + + char* expect = "{\"field_info\":[{\"id\":0}], \"results\":[{\"name\":\"e1\", \"fields\":{\"ids\":[[\"Position\"]]}}, {\"name\":\"e2\", \"fields\":{\"ids\":[[\"Position\"]]}}, {\"name\":\"e3\", \"fields\":{\"ids\":[[\"Mass\"]]}}]}"; + test_json(json, expect); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_recycled_id(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA" + }); + + test_assert(q != NULL); + + ecs_entity_t _e1 = ecs_new(world); + ecs_delete(world, _e1); + ecs_entity_t e1 = ecs_new(world); + test_assert(e1 != _e1); + test_assert((uint32_t)e1 == _e1); + + ecs_add(world, e1, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + char *json = ecs_iter_to_json(&it, NULL); + test_assert(json != NULL); + + char* expect = flecs_asprintf("{\"results\":[{\"name\":\"#%u\", \"fields\":{}}]}", (uint32_t)e1); + test_json(json, expect); + + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeIterToRowJson_serialize_entity_w_flecs_core_parent(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_set_name(world, e1, "e1"); + + ecs_entity_t flecs_core_parent = + ecs_entity(world, { .name = "flecs.core.bob" }); + ecs_add_pair(world, e1, EcsChildOf, flecs_core_parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + char *json = ecs_iter_to_json(&it, NULL); + + test_json(json, "{\"results\":[{\"parent\":\"flecs.core.bob\", \"name\":\"e1\", \"fields\":{}}]}"); + + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/meta/src/SerializeQueryInfoToJson.c b/vendors/flecs/test/meta/src/SerializeQueryInfoToJson.c new file mode 100644 index 000000000..b33d924dd --- /dev/null +++ b/vendors/flecs/test/meta/src/SerializeQueryInfoToJson.c @@ -0,0 +1,752 @@ +#include + +void SerializeQueryInfoToJson_1_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Foo\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":true, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Position\", \"symbol\":\"Position\", \"type\":true}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tgt, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, Tgt)" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Rel\"}, \"second\":{\"entity\":\"Tgt\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_pair_w_wildcard(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, *)" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Rel\"}, \"second\":{\"var\":\"*\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_pair_w_any(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, _)" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Rel\"}, \"second\":{\"var\":\"_\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_tag_fixed_src(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, e, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(e)" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"entity\":\"e\"}, \"first\":{\"entity\":\"Foo\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_tag_var_src(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($v)" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"v\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"v\"}, \"first\":{\"entity\":\"Foo\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_component_in(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"in\", \"has_value\":true, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Position\", \"symbol\":\"Position\", \"type\":true}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_component_inout(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .expr = "[inout] Position" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"inout\", \"has_value\":true, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Position\", \"symbol\":\"Position\", \"type\":true}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_component_out(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .expr = "[out] Position" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"out\", \"has_value\":true, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Position\", \"symbol\":\"Position\", \"type\":true}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_component_none(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .expr = "[none] Position" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"none\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Position\", \"symbol\":\"Position\", \"type\":true}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_tag_not(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Foo" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"none\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"not\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Foo\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_2_tags_or(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo || Bar" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"or\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Foo\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}, {\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Bar\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_tag_optional(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "?Foo" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"optional\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Foo\"}, \"trav\":{\"entity\":\"flecs.core.IsA\", \"symbol\":\"EcsIsA\"}, \"flags\":[\"self\", \"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_tag_self(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self)" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Foo\"}, \"flags\":[\"self\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_tag_self_dont_inherit(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, DontInherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self)" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Foo\"}, \"flags\":[\"self\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_tag_up(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(up)" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Foo\"}, \"trav\":{\"entity\":\"flecs.core.ChildOf\", \"symbol\":\"EcsChildOf\"}, \"flags\":[\"up\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_1_tag_cascade(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(cascade)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"Foo\"}, \"trav\":{\"entity\":\"flecs.core.ChildOf\", \"symbol\":\"EcsChildOf\"}, \"flags\":[\"cascade\"]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_0_term(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(#0)" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + test_str(json, "{\"query_info\":{\"vars\":[], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"can_inherit\":true, \"oper\":\"and\", \"src\":{\"entity\":\"#0\"}, \"first\":{\"entity\":\"Foo\"}, \"flags\":[]}]}}"); + ecs_os_free(json); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_anonymous_tag(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t foo = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ foo }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + char *expect = flecs_asprintf("{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"#%u\"}, \"flags\":[\"self\"]}]}}", + foo); + test_str(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_anonymous_pair(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t rel = ecs_new(world); + ecs_entity_t tgt = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(rel, tgt) }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + char *expect = flecs_asprintf("{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"#%u\"}, \"second\":{\"entity\":\"#%u\"}, \"flags\":[\"self\"]}]}}", + (uint32_t)rel, (uint32_t)tgt); + test_str(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_anonymous_component(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t comp = ecs_component(world, { + .type.size = 4, + .type.alignment = 4 + }); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ comp }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + char *expect = flecs_asprintf("{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"#%u\", \"type\":true}, \"flags\":[\"self\"]}]}}", + comp); + test_str(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_anonymous_tag_recycled(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t foo = ecs_new(world); + ecs_delete(world, foo); + foo = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ foo }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + char *expect = flecs_asprintf("{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"#%u\"}, \"flags\":[\"self\"]}]}}", + foo); + test_str(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_anonymous_pair_recycled(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t rel = ecs_new(world); + ecs_entity_t tgt = ecs_new(world); + ecs_delete(world, rel); + ecs_delete(world, tgt); + rel = ecs_new(world); + tgt = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(rel, tgt) }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + char *expect = flecs_asprintf("{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":false, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"#%u\"}, \"second\":{\"entity\":\"#%u\"}, \"flags\":[\"self\"]}]}}", + (uint32_t)rel, (uint32_t)tgt); + test_str(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_anonymous_component_recycled(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t comp = ecs_new(world); + ecs_delete(world, comp); + comp = ecs_component(world, { + .type.size = 4, + .type.alignment = 4 + }); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ comp }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_info = true, + .dont_serialize_results = true + }; + + char *json = ecs_iter_to_json(&it, &desc); + char *expect = flecs_asprintf("{\"query_info\":{\"vars\":[\"this\"], \"terms\":[{\"inout\":\"default\", \"has_value\":true, \"oper\":\"and\", \"src\":{\"var\":\"this\"}, \"first\":{\"entity\":\"#%u\", \"type\":true}, \"flags\":[\"self\"]}]}}", + comp); + test_str(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_serialize_plan_trivial_query(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position) }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_plan = true, + .query = q + }; + + char *json = ecs_iter_to_json(&it, &desc); + char *expect = flecs_asprintf("{\"query_plan\":\" [[0;37mOptimized out (trivial query)\\n\", \"results\":[]}"); + test_json(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void SerializeQueryInfoToJson_serialize_plan_nontrivial_query(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position) }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_to_json_desc_t desc = { + .serialize_query_plan = true, + .query = q + }; + + char *json = ecs_iter_to_json(&it, &desc); + char *expect = flecs_asprintf("{\"query_plan\":\"[[0;49m 0. [[[0;37m-1[[0;49m, [[0;32m 1[[0;49m] setids \\n[[0;49m 1. [[[0;37m 0[[0;49m, [[0;32m 2[[0;49m] selfup [[0;32m$[[0;49m[[[0;32mthis[[0;49m] ([[0;34mPosition[[0;49m)\\n[[0;49m 2. [[[0;37m 1[[0;49m, [[0;32m 3[[0;49m] yield \\n\", \"results\":[]}"); + test_json(json, expect); + ecs_os_free(json); + ecs_os_free(expect); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/meta/src/SerializeToJson.c b/vendors/flecs/test/meta/src/SerializeToJson.c index 22167bb96..a37d568d2 100644 --- a/vendors/flecs/test/meta/src/SerializeToJson.c +++ b/vendors/flecs/test/meta/src/SerializeToJson.c @@ -587,6 +587,54 @@ void SerializeToJson_struct_entity(void) { ecs_fini(world); } +void SerializeToJson_struct_entity_0(void) { + typedef struct { + ecs_entity_t x; + } T; + + ecs_world_t *world = ecs_init(); + + ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "T"}), + .members = { + {"x", ecs_id(ecs_entity_t)} + } + }); + + T value = {0}; + char *expr = ecs_ptr_to_json(world, t, &value); + test_assert(expr != NULL); + test_str(expr, "{\"x\":\"#0\"}"); + ecs_os_free(expr); + + ecs_fini(world); +} + +void SerializeToJson_struct_entity_10k(void) { + typedef struct { + ecs_entity_t x; + } T; + + ecs_world_t *world = ecs_init(); + + ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "T"}), + .members = { + {"x", ecs_id(ecs_entity_t)} + } + }); + + ecs_make_alive(world, 10000); + + T value = {10000}; + char *expr = ecs_ptr_to_json(world, t, &value); + test_assert(expr != NULL); + test_str(expr, "{\"x\":\"#10000\"}"); + ecs_os_free(expr); + + ecs_fini(world); +} + void SerializeToJson_struct_id(void) { typedef struct { ecs_id_t x; @@ -1253,3 +1301,29 @@ void SerializeToJson_vector_array_i32_3(void) { ecs_fini(world); } + +void SerializeToJson_serialize_from_stage(void) { + typedef struct { + ecs_char_t x; + } T; + + ecs_world_t *world = ecs_init(); + + ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "T"}), + .members = { + {"x", ecs_id(ecs_char_t)} + } + }); + + T value = {'a'}; + + ecs_world_t *stage = ecs_get_stage(world, 0); + + char *expr = ecs_ptr_to_json(stage, t, &value); + test_assert(expr != NULL); + test_str(expr, "{\"x\":\"a\"}"); + ecs_os_free(expr); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/meta/src/Serialized.c b/vendors/flecs/test/meta/src/Serialized.c index 24add8b22..e0cc137be 100644 --- a/vendors/flecs/test/meta/src/Serialized.c +++ b/vendors/flecs/test/meta/src/Serialized.c @@ -77,8 +77,8 @@ static void _test_member_op( void Serialized_ops_bool(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_bool_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_bool_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -90,8 +90,8 @@ void Serialized_ops_bool(void) { void Serialized_ops_byte(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_byte_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_byte_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -103,8 +103,8 @@ void Serialized_ops_byte(void) { void Serialized_ops_char(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_char_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_char_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -116,8 +116,8 @@ void Serialized_ops_char(void) { void Serialized_ops_i8(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_i8_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_i8_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -129,8 +129,8 @@ void Serialized_ops_i8(void) { void Serialized_ops_i16(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_i16_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_i16_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -142,8 +142,8 @@ void Serialized_ops_i16(void) { void Serialized_ops_i32(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_i32_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_i32_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -155,8 +155,8 @@ void Serialized_ops_i32(void) { void Serialized_ops_i64(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_i64_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_i64_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -168,8 +168,8 @@ void Serialized_ops_i64(void) { void Serialized_ops_iptr(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_iptr_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_iptr_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -181,8 +181,8 @@ void Serialized_ops_iptr(void) { void Serialized_ops_u8(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_u8_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_u8_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -194,8 +194,8 @@ void Serialized_ops_u8(void) { void Serialized_ops_u16(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_u16_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_u16_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -207,8 +207,8 @@ void Serialized_ops_u16(void) { void Serialized_ops_u32(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_u32_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_u32_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -220,8 +220,8 @@ void Serialized_ops_u32(void) { void Serialized_ops_u64(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_u64_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_u64_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -233,8 +233,8 @@ void Serialized_ops_u64(void) { void Serialized_ops_uptr(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_uptr_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_uptr_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -246,8 +246,8 @@ void Serialized_ops_uptr(void) { void Serialized_ops_float(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_f32_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_f32_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -259,8 +259,8 @@ void Serialized_ops_float(void) { void Serialized_ops_double(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_f64_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_f64_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -272,8 +272,8 @@ void Serialized_ops_double(void) { void Serialized_ops_string(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_string_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_string_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -285,8 +285,8 @@ void Serialized_ops_string(void) { void Serialized_ops_entity(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_entity_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_entity_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -298,8 +298,8 @@ void Serialized_ops_entity(void) { void Serialized_ops_id(void) { ecs_world_t *world = ecs_init(); - const EcsMetaTypeSerialized *s = ecs_get( - world, ecs_id(ecs_id_t), EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get( + world, ecs_id(ecs_id_t), EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -323,13 +323,13 @@ void Serialized_ops_struct_bool(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 3); test_op(&s->ops, 0, EcsOpPush, 1, 3, t); test_mp(&s->ops, 1, EcsOpBool, 1, 1, T, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 2, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 2, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -351,14 +351,14 @@ void Serialized_ops_struct_bool_bool(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpBool, 1, 1, T, x, ecs_id(ecs_bool_t)); test_mp(&s->ops, 2, EcsOpBool, 1, 1, T, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -378,13 +378,13 @@ void Serialized_ops_struct_i32(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 3); test_op(&s->ops, 0, EcsOpPush, 1, 3, t); test_mp(&s->ops, 1, EcsOpI32, 1, 1, T, x, ecs_id(ecs_i32_t)); - test_op(&s->ops, 2, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 2, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -406,14 +406,14 @@ void Serialized_ops_struct_i32_i32(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpI32, 1, 1, T, x, ecs_id(ecs_i32_t)); test_mp(&s->ops, 2, EcsOpI32, 1, 1, T, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -435,14 +435,14 @@ void Serialized_ops_struct_i32_bool(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpI32, 1, 1, T, x, ecs_id(ecs_i32_t)); test_mp(&s->ops, 2, EcsOpBool, 1, 1, T, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -464,14 +464,14 @@ void Serialized_ops_struct_bool_i32(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpBool, 1, 1, T, x, ecs_id(ecs_bool_t)); test_mp(&s->ops, 2, EcsOpI32, 1, 1, T, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -503,15 +503,15 @@ void Serialized_ops_nested_struct_1_bool(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 5); test_op(&s->ops, 0, EcsOpPush, 1, 5, t); test_mp(&s->ops, 1, EcsOpPush, 1, 3, T, n_1, n1); test_mn(&s->ops, 2, EcsOpBool, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 4, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, n1); + test_op(&s->ops, 4, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -543,15 +543,15 @@ void Serialized_ops_nested_struct_1_i32(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 5); test_op(&s->ops, 0, EcsOpPush, 1, 5, t); test_mp(&s->ops, 1, EcsOpPush, 1, 3, T, n_1, n1); test_mn(&s->ops, 2, EcsOpI32, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_i32_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 4, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, n1); + test_op(&s->ops, 4, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -585,16 +585,16 @@ void Serialized_ops_nested_struct_1_bool_w_bool_member(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 6); test_op(&s->ops, 0, EcsOpPush, 1, 6, t); test_mp(&s->ops, 1, EcsOpPush, 1, 3, T, n_1, n1); test_mn(&s->ops, 2, EcsOpBool, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 4, EcsOpBool, 1, 1, T, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 5, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 5, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -628,16 +628,16 @@ void Serialized_ops_nested_struct_1_bool_w_i32_member(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 6); test_op(&s->ops, 0, EcsOpPush, 1, 6, t); test_mp(&s->ops, 1, EcsOpPush, 1, 3, T, n_1, n1); test_mn(&s->ops, 2, EcsOpBool, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 4, EcsOpI32, 1, 1, T, x, ecs_id(ecs_i32_t)); - test_op(&s->ops, 5, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 5, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -678,18 +678,18 @@ void Serialized_ops_nested_struct_1_bool_2_bool(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 8); test_op(&s->ops, 0, EcsOpPush, 1, 8, t); test_mp(&s->ops, 1, EcsOpPush, 1, 3, T, n_1, n1); test_mn(&s->ops, 2, EcsOpBool, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 4, EcsOpPush, 1, 3, T, n_2, n2); test_mn(&s->ops, 5, EcsOpBool, 1, 1, offsetof(T, n_2), N1, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 6, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 7, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 6, EcsOpPop, 1, 1, n2); + test_op(&s->ops, 7, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -733,18 +733,18 @@ void Serialized_ops_nested_struct_1_i32_2_bool(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 8); test_op(&s->ops, 0, EcsOpPush, 1, 8, t); test_mp(&s->ops, 1, EcsOpPush, 1, 3, T, n_1, n1); test_mn(&s->ops, 2, EcsOpI32, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_i32_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 4, EcsOpPush, 1, 3, T, n_2, n2); test_mn(&s->ops, 5, EcsOpBool, 1, 1, offsetof(T, n_2), N2, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 6, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 7, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 6, EcsOpPop, 1, 1, n2); + test_op(&s->ops, 7, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -792,7 +792,7 @@ void Serialized_ops_nested_struct_1_i32_i32_2_bool_bool(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 10); @@ -800,12 +800,12 @@ void Serialized_ops_nested_struct_1_i32_i32_2_bool_bool(void) { test_mp(&s->ops, 1, EcsOpPush, 1, 4, T, n_1, n1); test_mn(&s->ops, 2, EcsOpI32, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_i32_t)); test_mn(&s->ops, 3, EcsOpI32, 1, 1, offsetof(T, n_1), N1, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 4, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 4, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 5, EcsOpPush, 1, 4, T, n_2, n2); test_mn(&s->ops, 6, EcsOpBool, 1, 1, offsetof(T, n_2), N2, x, ecs_id(ecs_bool_t)); test_mn(&s->ops, 7, EcsOpBool, 1, 1, offsetof(T, n_2), N2, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 8, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 9, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 8, EcsOpPop, 1, 1, n2); + test_op(&s->ops, 9, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -853,7 +853,7 @@ void Serialized_ops_nested_struct_1_bool_bool_2_i32_i32(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 10); @@ -861,12 +861,12 @@ void Serialized_ops_nested_struct_1_bool_bool_2_i32_i32(void) { test_mp(&s->ops, 1, EcsOpPush, 1, 4, T, n_1, n1); test_mn(&s->ops, 2, EcsOpBool, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_bool_t)); test_mn(&s->ops, 3, EcsOpBool, 1, 1, offsetof(T, n_1), N1, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 4, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 4, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 5, EcsOpPush, 1, 4, T, n_2, n2); test_mn(&s->ops, 6, EcsOpI32, 1, 1, offsetof(T, n_2), N2, x, ecs_id(ecs_i32_t)); test_mn(&s->ops, 7, EcsOpI32, 1, 1, offsetof(T, n_2), N2, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 8, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 9, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 8, EcsOpPop, 1, 1, n2); + test_op(&s->ops, 9, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -914,7 +914,7 @@ void Serialized_ops_nested_struct_1_i32_bool_2_bool_i32(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 10); @@ -922,12 +922,12 @@ void Serialized_ops_nested_struct_1_i32_bool_2_bool_i32(void) { test_mp(&s->ops, 1, EcsOpPush, 1, 4, T, n_1, n1); test_mn(&s->ops, 2, EcsOpI32, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_i32_t)); test_mn(&s->ops, 3, EcsOpBool, 1, 1, offsetof(T, n_1), N1, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 4, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 4, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 5, EcsOpPush, 1, 4, T, n_2, n2); test_mn(&s->ops, 6, EcsOpBool, 1, 1, offsetof(T, n_2), N2, x, ecs_id(ecs_bool_t)); test_mn(&s->ops, 7, EcsOpI32, 1, 1, offsetof(T, n_2), N2, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 8, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 9, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 8, EcsOpPop, 1, 1, n2); + test_op(&s->ops, 9, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -975,7 +975,7 @@ void Serialized_ops_nested_struct_1_bool_i32_2_i32_bool(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 10); @@ -983,12 +983,12 @@ void Serialized_ops_nested_struct_1_bool_i32_2_i32_bool(void) { test_mp(&s->ops, 1, EcsOpPush, 1, 4, T, n_1, n1); test_mn(&s->ops, 2, EcsOpBool, 1, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_bool_t)); test_mn(&s->ops, 3, EcsOpI32, 1, 1, offsetof(T, n_1), N1, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 4, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 4, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 5, EcsOpPush, 1, 4, T, n_2, n2); test_mn(&s->ops, 6, EcsOpI32, 1, 1, offsetof(T, n_2), N2, x, ecs_id(ecs_i32_t)); test_mn(&s->ops, 7, EcsOpBool, 1, 1, offsetof(T, n_2), N2, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 8, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 9, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 8, EcsOpPop, 1, 1, n2); + test_op(&s->ops, 9, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1055,7 +1055,7 @@ void Serialized_ops_nested_2_lvls_bool(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 12); @@ -1064,16 +1064,16 @@ void Serialized_ops_nested_2_lvls_bool(void) { test_mp(&s->ops, 1, EcsOpPush, 1, 5, T, n_1, n1); test_mn(&s->ops, 2, EcsOpPush, 1, 3, offsetof(T, n_1), N1, nn_1, nn1); test_mn(&s->ops, 3, EcsOpBool, 1, 1, offsetof(T, n_1) + offsetof(N1, nn_1), NN1, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 4, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 5, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 4, EcsOpPop, 1, 1, nn1); + test_op(&s->ops, 5, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 6, EcsOpPush, 1, 5, T, n_2, n2); test_mn(&s->ops, 7, EcsOpPush, 1, 3, offsetof(T, n_2), N2, nn_2, nn2); test_mn(&s->ops, 8, EcsOpBool, 1, 1, offsetof(T, n_2) + offsetof(N2, nn_2), NN2, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 9, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 10, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 9, EcsOpPop, 1, 1, nn2); + test_op(&s->ops, 10, EcsOpPop, 1, 1, n2); - test_op(&s->ops, 11, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 11, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1144,7 +1144,7 @@ void Serialized_ops_nested_2_lvls_bool_bool(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 14); @@ -1154,17 +1154,17 @@ void Serialized_ops_nested_2_lvls_bool_bool(void) { test_mn(&s->ops, 2, EcsOpPush, 1, 4, offsetof(T, n_1), N1, nn_1, nn1); test_mn(&s->ops, 3, EcsOpBool, 1, 1, offsetof(T, n_1) + offsetof(N1, nn_1), NN1, x, ecs_id(ecs_bool_t)); test_mn(&s->ops, 4, EcsOpBool, 1, 1, offsetof(T, n_1) + offsetof(N1, nn_1), NN1, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 5, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 6, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 5, EcsOpPop, 1, 1, nn1); + test_op(&s->ops, 6, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 7, EcsOpPush, 1, 6, T, n_2, n2); test_mn(&s->ops, 8, EcsOpPush, 1, 4, offsetof(T, n_2), N2, nn_2, nn2); test_mn(&s->ops, 9, EcsOpBool, 1, 1, offsetof(T, n_2) + offsetof(N2, nn_2), NN2, x, ecs_id(ecs_bool_t)); test_mn(&s->ops, 10, EcsOpBool, 1, 1, offsetof(T, n_2) + offsetof(N2, nn_2), NN2, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 11, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 12, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 11, EcsOpPop, 1, 1, nn2); + test_op(&s->ops, 12, EcsOpPop, 1, 1, n2); - test_op(&s->ops, 13, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 13, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1243,7 +1243,7 @@ void Serialized_ops_nested_2_lvls_i32_i32_w_member_before(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 18); @@ -1255,8 +1255,8 @@ void Serialized_ops_nested_2_lvls_i32_i32_w_member_before(void) { test_mn(&s->ops, 4, EcsOpPush, 1, 4, offsetof(T, n_1), N1, nn_1, nn1); test_mn(&s->ops, 5, EcsOpI32, 1, 1, offsetof(T, n_1) + offsetof(N1, nn_1), NN1, x, ecs_id(ecs_i32_t)); test_mn(&s->ops, 6, EcsOpI32, 1, 1, offsetof(T, n_1) + offsetof(N1, nn_1), NN1, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 7, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 8, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 7, EcsOpPop, 1, 1, nn1); + test_op(&s->ops, 8, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 9, EcsOpBool, 1, 1, T, y, ecs_id(ecs_bool_t)); test_mp(&s->ops, 10, EcsOpPush, 1, 7, T, n_2, n2); @@ -1264,10 +1264,10 @@ void Serialized_ops_nested_2_lvls_i32_i32_w_member_before(void) { test_mn(&s->ops, 12, EcsOpPush, 1, 4, offsetof(T, n_2), N2, nn_2, nn2); test_mn(&s->ops, 13, EcsOpI32, 1, 1, offsetof(T, n_2) + offsetof(N2, nn_2), NN2, x, ecs_id(ecs_i32_t)); test_mn(&s->ops, 14, EcsOpI32, 1, 1, offsetof(T, n_2) + offsetof(N2, nn_2), NN2, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 15, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 16, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 15, EcsOpPop, 1, 1, nn2); + test_op(&s->ops, 16, EcsOpPop, 1, 1, n2); - test_op(&s->ops, 17, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 17, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1346,7 +1346,7 @@ void Serialized_ops_nested_2_lvls_1_bool_i32_2_i32_bool_w_member_before(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 18); @@ -1358,8 +1358,8 @@ void Serialized_ops_nested_2_lvls_1_bool_i32_2_i32_bool_w_member_before(void) { test_mn(&s->ops, 4, EcsOpPush, 1, 4, offsetof(T, n_1), N1, nn_1, nn1); test_mn(&s->ops, 5, EcsOpBool, 1, 1, offsetof(T, n_1) + offsetof(N1, nn_1), NN1, x, ecs_id(ecs_bool_t)); test_mn(&s->ops, 6, EcsOpI32, 1, 1, offsetof(T, n_1) + offsetof(N1, nn_1), NN1, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 7, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 8, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 7, EcsOpPop, 1, 1, nn1); + test_op(&s->ops, 8, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 9, EcsOpBool, 1, 1, T, y, ecs_id(ecs_bool_t)); test_mp(&s->ops, 10, EcsOpPush, 1, 7, T, n_2, n2); @@ -1367,10 +1367,10 @@ void Serialized_ops_nested_2_lvls_1_bool_i32_2_i32_bool_w_member_before(void) { test_mn(&s->ops, 12, EcsOpPush, 1, 4, offsetof(T, n_2), N2, nn_2, nn2); test_mn(&s->ops, 13, EcsOpI32, 1, 1, offsetof(T, n_2) + offsetof(N2, nn_2), NN2, x, ecs_id(ecs_i32_t)); test_mn(&s->ops, 14, EcsOpBool, 1, 1, offsetof(T, n_2) + offsetof(N2, nn_2), NN2, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 15, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 16, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 15, EcsOpPop, 1, 1, nn2); + test_op(&s->ops, 16, EcsOpPop, 1, 1, n2); - test_op(&s->ops, 17, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 17, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1449,7 +1449,7 @@ void Serialized_ops_nested_2_lvls_1_i32_bool_2_bool_i32_w_member_before(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 18); @@ -1461,8 +1461,8 @@ void Serialized_ops_nested_2_lvls_1_i32_bool_2_bool_i32_w_member_before(void) { test_mn(&s->ops, 4, EcsOpPush, 1, 4, offsetof(T, n_1), N1, nn_1, nn1); test_mn(&s->ops, 5, EcsOpI32, 1, 1, offsetof(T, n_1) + offsetof(N1, nn_1), NN1, x, ecs_id(ecs_i32_t)); test_mn(&s->ops, 6, EcsOpBool, 1, 1, offsetof(T, n_1) + offsetof(N1, nn_1), NN1, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 7, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 8, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 7, EcsOpPop, 1, 1, nn1); + test_op(&s->ops, 8, EcsOpPop, 1, 1, n1); test_mp(&s->ops, 9, EcsOpBool, 1, 1, T, y, ecs_id(ecs_bool_t)); test_mp(&s->ops, 10, EcsOpPush, 1, 7, T, n_2, n2); @@ -1470,10 +1470,10 @@ void Serialized_ops_nested_2_lvls_1_i32_bool_2_bool_i32_w_member_before(void) { test_mn(&s->ops, 12, EcsOpPush, 1, 4, offsetof(T, n_2), N2, nn_2, nn2); test_mn(&s->ops, 13, EcsOpBool, 1, 1, offsetof(T, n_2) + offsetof(N2, nn_2), NN2, x, ecs_id(ecs_bool_t)); test_mn(&s->ops, 14, EcsOpI32, 1, 1, offsetof(T, n_2) + offsetof(N2, nn_2), NN2, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 15, EcsOpPop, 1, 1, 0); - test_op(&s->ops, 16, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 15, EcsOpPop, 1, 1, nn2); + test_op(&s->ops, 16, EcsOpPop, 1, 1, n2); - test_op(&s->ops, 17, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 17, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1493,13 +1493,13 @@ void Serialized_ops_struct_array_bool_1(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 3); test_op(&s->ops, 0, EcsOpPush, 1, 3, t); test_mp(&s->ops, 1, EcsOpBool, 1, 1, T, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 2, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 2, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1519,13 +1519,13 @@ void Serialized_ops_struct_array_bool_2(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 3); test_op(&s->ops, 0, EcsOpPush, 1, 3, t); test_mp(&s->ops, 1, EcsOpBool, 2, 1, T, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 2, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 2, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1545,13 +1545,13 @@ void Serialized_ops_struct_array_bool_3(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 3); test_op(&s->ops, 0, EcsOpPush, 1, 3, t); test_mp(&s->ops, 1, EcsOpBool, 3, 1, T, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 2, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 2, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1573,14 +1573,14 @@ void Serialized_ops_struct_array_bool_1_w_i32_after(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpBool, 1, 1, T, x, ecs_id(ecs_bool_t)); test_mp(&s->ops, 2, EcsOpI32, 1, 1, T, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1602,14 +1602,14 @@ void Serialized_ops_struct_array_bool_2_w_i32_after(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpBool, 2, 1, T, x, ecs_id(ecs_bool_t)); test_mp(&s->ops, 2, EcsOpI32, 1, 1, T, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1631,14 +1631,14 @@ void Serialized_ops_struct_array_bool_3_w_i32_after(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpBool, 3, 1, T, x, ecs_id(ecs_bool_t)); test_mp(&s->ops, 2, EcsOpI32, 1, 1, T, y, ecs_id(ecs_i32_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1672,16 +1672,16 @@ void Serialized_ops_struct_array_struct_bool_3_w_i32_after(void) { test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 6); test_op(&s->ops, 0, EcsOpPush, 1, 6, t); test_mp(&s->ops, 1, EcsOpPush, 3, 3, T, n_1, n); test_mn(&s->ops, 2, EcsOpBool, 2, 1, offsetof(T, n_1), N1, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, n); test_mp(&s->ops, 4, EcsOpI32, 1, 1, T, x, ecs_id(ecs_i32_t)); - test_op(&s->ops, 5, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 5, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1709,13 +1709,13 @@ void Serialized_ops_standalone_array_bool_1(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 3); test_op(&s->ops, 0, EcsOpPush, 1, 3, t); test_mp(&s->ops, 1, EcsOpArray, 1, 1, T, x, a); - test_op(&s->ops, 2, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 2, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1743,13 +1743,13 @@ void Serialized_ops_standalone_array_bool_2(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 3); test_op(&s->ops, 0, EcsOpPush, 1, 3, t); test_mp(&s->ops, 1, EcsOpArray, 1, 1, T, x, a); - test_op(&s->ops, 2, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 2, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1777,13 +1777,13 @@ void Serialized_ops_standalone_array_bool_3(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 3); test_op(&s->ops, 0, EcsOpPush, 1, 3, t); test_mp(&s->ops, 1, EcsOpArray, 1, 1, T, x, a); - test_op(&s->ops, 2, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 2, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1813,14 +1813,14 @@ void Serialized_ops_standalone_array_bool_1_w_bool_after(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpArray, 1, 1, T, x, a); test_mp(&s->ops, 2, EcsOpBool, 1, 1, T, y, ecs_id(ecs_bool_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1850,13 +1850,13 @@ void Serialized_ops_standalone_array_bool_2_w_bool_after(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpArray, 1, 1, T, x, a); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1886,13 +1886,13 @@ void Serialized_ops_standalone_array_bool_3_w_bool_after(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpArray, 1, 1, T, x, a); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1907,7 +1907,7 @@ void Serialized_ops_vector(void) { test_assert(v != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, v, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, v, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -1938,13 +1938,13 @@ void Serialized_ops_struct_w_vector(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 3); test_op(&s->ops, 0, EcsOpPush, 1, 3, t); test_mp(&s->ops, 1, EcsOpVector, 1, 1, T, v, v); - test_op(&s->ops, 2, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 2, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -1973,14 +1973,14 @@ void Serialized_ops_struct_w_vector_w_bool_before(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpBool, 1, 1, T, x, ecs_id(ecs_bool_t)); test_mp(&s->ops, 2, EcsOpVector, 1, 1, T, v, v); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -2009,14 +2009,14 @@ void Serialized_ops_struct_w_vector_w_bool_after(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 4); test_op(&s->ops, 0, EcsOpPush, 1, 4, t); test_mp(&s->ops, 1, EcsOpVector, 1, 1, T, v, v); test_mp(&s->ops, 2, EcsOpBool, 1, 1, T, x, ecs_id(ecs_bool_t)); - test_op(&s->ops, 3, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 3, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -2032,7 +2032,7 @@ void Serialized_ops_bitmask(void) { test_assert(b != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, b, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, b, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -2068,7 +2068,7 @@ void Serialized_ops_struct_w_bitmask(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 5); @@ -2076,7 +2076,7 @@ void Serialized_ops_struct_w_bitmask(void) { test_mp(&s->ops, 1, EcsOpBool, 1, 1, T, before, ecs_id(ecs_bool_t)); test_mp(&s->ops, 2, EcsOpBitmask, 1, 1, T, v, b); test_mp(&s->ops, 3, EcsOpBool, 1, 1, T, after, ecs_id(ecs_bool_t)); - test_op(&s->ops, 4, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 4, EcsOpPop, 1, 1, t); ecs_fini(world); } @@ -2092,7 +2092,7 @@ void Serialized_ops_enum(void) { test_assert(e != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, e, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, e, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 1); @@ -2128,7 +2128,7 @@ void Serialized_ops_struct_w_enum(void) { }); test_assert(t != 0); - const EcsMetaTypeSerialized *s = ecs_get(world, t, EcsMetaTypeSerialized); + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); test_assert(s != NULL); test_int(ecs_vec_count(&s->ops), 5); @@ -2136,7 +2136,35 @@ void Serialized_ops_struct_w_enum(void) { test_mp(&s->ops, 1, EcsOpBool, 1, 1, T, before, ecs_id(ecs_bool_t)); test_mp(&s->ops, 2, EcsOpEnum, 1, 1, T, v, e); test_mp(&s->ops, 3, EcsOpBool, 1, 1, T, after, ecs_id(ecs_bool_t)); - test_op(&s->ops, 4, EcsOpPop, 1, 1, 0); + test_op(&s->ops, 4, EcsOpPop, 1, 1, t); + + ecs_fini(world); +} + +void Serialized_ops_missing_metatype(void) { + typedef struct { + ecs_u64_t a; + } X; + + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, X); + + ecs_log_set_level(-4); + ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "T"}), + .members = { + {"x", ecs_id(X)} + } + }); + test_assert(t != 0); + + const EcsTypeSerializer *s = ecs_get(world, t, EcsTypeSerializer); + test_assert(s != NULL); + test_int(ecs_vec_count(&s->ops), 2); + + test_op(&s->ops, 0, EcsOpPush, 1, 2, t); + test_op(&s->ops, 1, EcsOpPop, 1, 1, t); ecs_fini(world); } diff --git a/vendors/flecs/test/meta/src/StructTypes.c b/vendors/flecs/test/meta/src/StructTypes.c index a0291b346..21df6da13 100644 --- a/vendors/flecs/test/meta/src/StructTypes.c +++ b/vendors/flecs/test/meta/src/StructTypes.c @@ -11,7 +11,7 @@ void _meta_test_struct( test_int(ct->size, size); test_int(ct->alignment, alignment); - const EcsMetaType *mt = ecs_get(world, t, EcsMetaType); + const EcsType *mt = ecs_get(world, t, EcsType); test_assert(mt != NULL); test_assert(mt->kind == EcsStructType); } @@ -368,7 +368,7 @@ void StructTypes_incomplete_member(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t t = ecs_new_id(world); + ecs_entity_t t = ecs_new(world); ecs_entity_t m_x = ecs_new_w_pair(world, EcsChildOf, t); ecs_set_name(world, m_x, "x"); @@ -412,7 +412,7 @@ void StructTypes_partial_type(void) { test_int(cptr->size, sizeof(Position)); test_int(cptr->alignment, ECS_ALIGNOF(Position)); - const EcsMetaType *mptr = ecs_get(world, s, EcsMetaType); + const EcsType *mptr = ecs_get(world, s, EcsType); test_assert(mptr != NULL); test_bool(mptr->partial, true); test_bool(mptr->existing, true); @@ -444,7 +444,7 @@ void StructTypes_partial_type_custom_offset(void) { test_int(cptr->size, sizeof(Vec3)); test_int(cptr->alignment, ECS_ALIGNOF(Vec3)); - const EcsMetaType *mptr = ecs_get(world, s, EcsMetaType); + const EcsType *mptr = ecs_get(world, s, EcsType); test_assert(mptr != NULL); test_bool(mptr->partial, true); test_bool(mptr->existing, true); @@ -499,9 +499,9 @@ void StructTypes_value_range(void) { test_assert(s == ecs_id(Position)); - ecs_entity_t x = ecs_lookup_fullpath(world, "Position.x"); + ecs_entity_t x = ecs_lookup(world, "Position.x"); test_assert(x != 0); - ecs_entity_t y = ecs_lookup_fullpath(world, "Position.y"); + ecs_entity_t y = ecs_lookup(world, "Position.y"); test_assert(y != 0); const EcsMemberRanges *xr = ecs_get(world, x, EcsMemberRanges); @@ -540,9 +540,9 @@ void StructTypes_error_range(void) { test_assert(s == ecs_id(Position)); - ecs_entity_t x = ecs_lookup_fullpath(world, "Position.x"); + ecs_entity_t x = ecs_lookup(world, "Position.x"); test_assert(x != 0); - ecs_entity_t y = ecs_lookup_fullpath(world, "Position.y"); + ecs_entity_t y = ecs_lookup(world, "Position.y"); test_assert(y != 0); const EcsMemberRanges *xr = ecs_get(world, x, EcsMemberRanges); @@ -577,9 +577,9 @@ void StructTypes_warning_range(void) { test_assert(s == ecs_id(Position)); - ecs_entity_t x = ecs_lookup_fullpath(world, "Position.x"); + ecs_entity_t x = ecs_lookup(world, "Position.x"); test_assert(x != 0); - ecs_entity_t y = ecs_lookup_fullpath(world, "Position.y"); + ecs_entity_t y = ecs_lookup(world, "Position.y"); test_assert(y != 0); const EcsMemberRanges *xr = ecs_get(world, x, EcsMemberRanges); @@ -614,9 +614,9 @@ void StructTypes_error_and_warning_range(void) { test_assert(s == ecs_id(Position)); - ecs_entity_t x = ecs_lookup_fullpath(world, "Position.x"); + ecs_entity_t x = ecs_lookup(world, "Position.x"); test_assert(x != 0); - ecs_entity_t y = ecs_lookup_fullpath(world, "Position.y"); + ecs_entity_t y = ecs_lookup(world, "Position.y"); test_assert(y != 0); const EcsMemberRanges *xr = ecs_get(world, x, EcsMemberRanges); @@ -813,10 +813,49 @@ void StructTypes_struct_w_16_alignment(void) { test_int(cptr->size, sizeof(T)); test_int(cptr->alignment, 16); - const EcsMetaType *mptr = ecs_get(world, t, EcsMetaType); + const EcsType *mptr = ecs_get(world, t, EcsType); test_assert(mptr != NULL); test_bool(mptr->partial, false); test_bool(mptr->existing, true); ecs_fini(world); } + +void StructTypes_struct_w_use_offset(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t s = ecs_struct(world, { + .entity = ecs_id(Position), + .members = {{ + .name = "y", + .type = ecs_id(ecs_i32_t), + .offset = offsetof(Position, y), + .use_offset = true + }, { + .name = "x", + .type = ecs_id(ecs_i32_t), + .offset = offsetof(Position, x), + .use_offset = true + }} + }); + + test_assert(s == ecs_id(Position)); + + const EcsComponent *cptr = ecs_get(world, s, EcsComponent); + test_assert(cptr != NULL); + test_int(cptr->size, sizeof(Position)); + test_int(cptr->alignment, ECS_ALIGNOF(Position)); + + const EcsType *mptr = ecs_get(world, s, EcsType); + test_assert(mptr != NULL); + // test_bool(mptr->partial, false); TODO + test_bool(mptr->existing, true); + + meta_test_struct(world, s, Position); + meta_test_member(world, s, Position, x, ecs_id(ecs_i32_t), 1); + meta_test_member(world, s, Position, y, ecs_id(ecs_i32_t), 1); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/meta/src/Units.c b/vendors/flecs/test/meta/src/Units.c index 8cc9467fc..8a6baf86d 100644 --- a/vendors/flecs/test/meta/src/Units.c +++ b/vendors/flecs/test/meta/src/Units.c @@ -51,7 +51,7 @@ void Units_member_w_unit_type(void) { }); test_assert(u != 0); test_str("percentage", ecs_get_name(world, u)); - test_assert(ecs_has(world, u, EcsMetaType)); + test_assert(ecs_has(world, u, EcsType)); ecs_unit_init(world, &(ecs_unit_desc_t){ .entity = u, @@ -106,8 +106,8 @@ void Units_cursor_get_unit(void) { } }); - ecs_entity_t e = ecs_new_id(world); - void *ptr = ecs_get_mut_id(world, e, s); + ecs_entity_t e = ecs_new(world); + void *ptr = ecs_ensure_id(world, e, s); test_assert(ptr != NULL); ecs_meta_cursor_t cur = ecs_meta_cursor(world, s, ptr); @@ -139,8 +139,8 @@ void Units_cursor_get_unit_type(void) { } }); - ecs_entity_t e = ecs_new_id(world); - void *ptr = ecs_get_mut_id(world, e, s); + ecs_entity_t e = ecs_new(world); + void *ptr = ecs_ensure_id(world, e, s); test_assert(ptr != NULL); ecs_meta_cursor_t cur = ecs_meta_cursor(world, s, ptr); @@ -351,7 +351,7 @@ void Units_unit_w_over(void) { void Units_member_w_invalid_unit(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t u = ecs_new_id(world); /* not a unit */ + ecs_entity_t u = ecs_new(world); /* not a unit */ test_assert(u != 0); ecs_log_set_level(-4); @@ -369,7 +369,7 @@ void Units_member_w_invalid_unit(void) { void Units_unit_w_invalid_quantity(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t q = ecs_new_id(world); /* not a quantity */ + ecs_entity_t q = ecs_new(world); /* not a quantity */ test_assert(q != 0); ecs_log_set_level(-4); @@ -387,7 +387,7 @@ void Units_unit_w_invalid_quantity(void) { void Units_unit_w_invalid_derived(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t u_1 = ecs_new_id(world); /* not a unit */ + ecs_entity_t u_1 = ecs_new(world); /* not a unit */ test_assert(u_1 != 0); ecs_log_set_level(-4); @@ -405,7 +405,7 @@ void Units_unit_w_invalid_derived(void) { void Units_unit_w_invalid_over(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t u_1 = ecs_new_id(world); /* not a unit */ + ecs_entity_t u_1 = ecs_new(world); /* not a unit */ test_assert(u_1 != 0); ecs_entity_t u_2 = ecs_unit_init(world, &(ecs_unit_desc_t){ @@ -631,11 +631,11 @@ void Units_set_unit(void) { ecs_world_t *world = ecs_init(); ecs_entity_t q = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = { EcsQuantity } + .add = ecs_ids( EcsQuantity ) }); test_assert(q != 0); - ecs_entity_t u = ecs_new_id(world); + ecs_entity_t u = ecs_new(world); test_assert(u != 0); ecs_set(world, u, EcsUnit, { @@ -679,18 +679,18 @@ void Units_set_unit_w_derived(void) { ecs_world_t *world = ecs_init(); ecs_entity_t q = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = { EcsQuantity } + .add = ecs_ids( EcsQuantity ) }); test_assert(q != 0); - ecs_entity_t d = ecs_new_id(world); + ecs_entity_t d = ecs_new(world); test_assert(d != 0); ecs_set(world, d, EcsUnit, { .symbol = "d" }); - ecs_entity_t u = ecs_new_id(world); + ecs_entity_t u = ecs_new(world); test_assert(u != 0); ecs_set(world, u, EcsUnit, { @@ -736,25 +736,25 @@ void Units_set_unit_w_over(void) { ecs_world_t *world = ecs_init(); ecs_entity_t q = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = { EcsQuantity } + .add = ecs_ids( EcsQuantity ) }); test_assert(q != 0); - ecs_entity_t d = ecs_new_id(world); + ecs_entity_t d = ecs_new(world); test_assert(d != 0); ecs_set(world, d, EcsUnit, { .symbol = "d" }); - ecs_entity_t o = ecs_new_id(world); + ecs_entity_t o = ecs_new(world); test_assert(o != 0); ecs_set(world, o, EcsUnit, { .symbol = "o" }); - ecs_entity_t u = ecs_new_id(world); + ecs_entity_t u = ecs_new(world); test_assert(u != 0); ecs_set(world, u, EcsUnit, { @@ -801,18 +801,18 @@ void Units_set_unit_w_prefix(void) { ecs_world_t *world = ecs_init(); ecs_entity_t q = ecs_entity_init(world, &(ecs_entity_desc_t){ - .add = { EcsQuantity } + .add = ecs_ids( EcsQuantity ) }); test_assert(q != 0); - ecs_entity_t d = ecs_new_id(world); + ecs_entity_t d = ecs_new(world); test_assert(d != 0); ecs_set(world, d, EcsUnit, { .symbol = "d" }); - ecs_entity_t p = ecs_new_id(world); + ecs_entity_t p = ecs_new(world); test_assert(p != 0); ecs_set(world, p, EcsUnitPrefix, { @@ -821,7 +821,7 @@ void Units_set_unit_w_prefix(void) { .translation.power = 2 }); - ecs_entity_t u = ecs_new_id(world); + ecs_entity_t u = ecs_new(world); test_assert(u != 0); ecs_set(world, u, EcsUnit, { @@ -912,3 +912,13 @@ void Units_quantity_w_short_notation(void) { ecs_fini(world); } + +void Units_import_units_after_mini(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsUnits); + + test_assert(EcsMeters != 0); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/meta/src/Vars.c b/vendors/flecs/test/meta/src/Vars.c deleted file mode 100644 index ca8112d06..000000000 --- a/vendors/flecs/test/meta/src/Vars.c +++ /dev/null @@ -1,406 +0,0 @@ -#include - -void Vars_declare_1_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); - test_assert(v != NULL); - test_assert(v->value.ptr != NULL); - test_assert(v->value.type != 0); - test_uint(v->value.type, ecs_id(ecs_i32_t)); - test_int(*(int32_t*)v->value.ptr, 0); - - const ecs_expr_var_t *v2 = ecs_vars_lookup(&vars, "foo"); - test_assert(v2 != NULL); - test_assert(v == v2); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_declare_2_vars(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); - test_assert(foo != NULL); - test_assert(foo->value.ptr != NULL); - test_assert(foo->value.type != 0); - test_uint(foo->value.type, ecs_id(ecs_i32_t)); - test_int(*(int32_t*)foo->value.ptr, 0); - - ecs_expr_var_t *bar = ecs_vars_declare( - &vars, "bar", ecs_id(ecs_u32_t)); - test_assert(bar != NULL); - test_assert(bar->value.ptr != NULL); - test_assert(bar->value.type != 0); - test_uint(bar->value.type, ecs_id(ecs_u32_t)); - test_uint(*(ecs_u32_t*)bar->value.ptr, 0); - - test_assert(foo == ecs_vars_lookup(&vars, "foo")); - test_assert(bar == ecs_vars_lookup(&vars, "bar")); - test_assert(foo != bar); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_declare_vars_nested_scope(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_vars_push(&vars); - - ecs_expr_var_t *foo = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); - test_assert(foo != NULL); - test_assert(foo->value.ptr != NULL); - test_assert(foo->value.type != 0); - test_uint(foo->value.type, ecs_id(ecs_i32_t)); - test_int(*(int32_t*)foo->value.ptr, 0); - test_assert(foo == ecs_vars_lookup(&vars, "foo")); - - test_int(0, ecs_vars_pop(&vars)); - - test_assert(NULL == ecs_vars_lookup(&vars, "foo")); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_declare_vars_2_scopes(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); - test_assert(foo != NULL); - test_assert(foo->value.ptr != NULL); - test_assert(foo->value.type != 0); - test_uint(foo->value.type, ecs_id(ecs_i32_t)); - test_int(*(int32_t*)foo->value.ptr, 0); - test_assert(foo == ecs_vars_lookup(&vars, "foo")); - - ecs_vars_push(&vars); - - ecs_expr_var_t *bar = ecs_vars_declare( - &vars, "bar", ecs_id(ecs_u32_t)); - test_assert(bar != NULL); - test_assert(bar->value.ptr != NULL); - test_assert(bar->value.type != 0); - test_uint(bar->value.type, ecs_id(ecs_u32_t)); - test_uint(*(ecs_u32_t*)bar->value.ptr, 0); - - test_assert(foo == ecs_vars_lookup(&vars, "foo")); - test_assert(bar == ecs_vars_lookup(&vars, "bar")); - test_assert(foo != bar); - - ecs_vars_pop(&vars); - - test_assert(foo == ecs_vars_lookup(&vars, "foo")); - test_assert(NULL == ecs_vars_lookup(&vars, "bar")); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_redeclare_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); - test_assert(foo != NULL); - test_assert(foo->value.ptr != NULL); - test_assert(foo->value.type != 0); - test_uint(foo->value.type, ecs_id(ecs_i32_t)); - test_int(*(int32_t*)foo->value.ptr, 0); - test_assert(foo == ecs_vars_lookup(&vars, "foo")); - - ecs_log_set_level(-4); - ecs_expr_var_t *foo_2 = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); - test_assert(foo_2 == NULL); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_i32_expr_w_i32_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); - *(int32_t*)foo->value.ptr = 10; - - int32_t v = 0; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr( - world, "$foo", &ecs_value(ecs_i32_t, &v), &desc); - test_assert(ptr != NULL); - test_assert(ptr[0] == 0); - test_int(v, 10); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_i32_expr_w_f32_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_f32_t)); - *(float*)foo->value.ptr = 10.5; - - int32_t v = 0; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr( - world, "$foo", &ecs_value(ecs_i32_t, &v), &desc); - test_assert(ptr != NULL); - test_assert(ptr[0] == 0); - test_int(v, 10); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_i32_expr_w_string_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare(&vars, "foo", ecs_id(ecs_string_t)); - *(char**)foo->value.ptr = "10"; - - int32_t v = 0; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr( - world, "$foo", &ecs_value(ecs_i32_t, &v), &desc); - test_assert(ptr != NULL); - test_assert(ptr[0] == 0); - test_int(v, 10); - - *(char**)foo->value.ptr = NULL; - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_string_expr_w_string_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare(&vars, "foo", ecs_id(ecs_string_t)); - *(char**)foo->value.ptr = "Hello World"; - - char* v = NULL; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr( - world, "$foo", &ecs_value(ecs_string_t, &v), &desc); - test_assert(ptr != NULL); - test_assert(ptr[0] == 0); - test_str(v, "Hello World"); - - ecs_os_free(v); - - *(char**)foo->value.ptr = NULL; - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_struct_expr_w_i32_vars(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t point = ecs_struct_init(world, &(ecs_struct_desc_t){ - .members = { - {"x", ecs_id(ecs_i32_t)}, - {"y", ecs_id(ecs_i32_t)} - } - }); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare(&vars, "foo", ecs_id(ecs_i32_t)); - *(int32_t*)foo->value.ptr = 10; - ecs_expr_var_t *bar = ecs_vars_declare(&vars, "bar", ecs_id(ecs_i32_t)); - *(int32_t*)bar->value.ptr = 20; - - Position v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr(world, "{$foo, $bar}", &(ecs_value_t){point, &v}, &desc); - test_assert(ptr != NULL); - test_assert(ptr[0] == 0); - - test_int(v.x, 10); - test_int(v.y, 20); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_struct_expr_w_struct_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t point = ecs_struct_init(world, &(ecs_struct_desc_t){ - .members = { - {"x", ecs_id(ecs_i32_t)}, - {"y", ecs_id(ecs_i32_t)} - } - }); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare(&vars, "foo", point); - *(Position*)foo->value.ptr = (Position){10, 20}; - - Position v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr(world, "$foo", &(ecs_value_t){point, &v}, &desc); - test_assert(ptr != NULL); - test_assert(ptr[0] == 0); - - test_int(v.x, 10); - test_int(v.y, 20); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_nested_struct_expr_w_struct_var(void) { - ecs_world_t *world = ecs_init(); - - ecs_entity_t point = ecs_struct_init(world, &(ecs_struct_desc_t){ - .members = { - {"x", ecs_id(ecs_i32_t)}, - {"y", ecs_id(ecs_i32_t)} - } - }); - - ecs_entity_t line = ecs_struct_init(world, &(ecs_struct_desc_t){ - .members = { - {"start", point}, - {"stop", point} - } - }); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *foo = ecs_vars_declare(&vars, "foo", point); - *(Position*)foo->value.ptr = (Position){10, 20}; - ecs_expr_var_t *bar = ecs_vars_declare(&vars, "bar", point); - *(Position*)bar->value.ptr = (Position){30, 40}; - - Line v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr(world, - "{start: $foo, stop: $bar}", &(ecs_value_t){line, &v}, &desc); - test_assert(ptr != NULL); - test_assert(ptr[0] == 0); - - test_int(v.start.x, 10); - test_int(v.start.y, 20); - test_int(v.stop.x, 30); - test_int(v.stop.y, 40); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_declare_w_value(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_value_t src; - src.type = ecs_id(ecs_i32_t); - src.ptr = ecs_value_new_t(world, ecs_i32_t); - *(int32_t*)src.ptr = 10; - void *src_ptr = src.ptr; - - ecs_expr_var_t *v = ecs_vars_declare_w_value( - &vars, "foo", &src); - test_assert(v != NULL); - test_assert(v->value.ptr != NULL); - test_assert(v->value.type != 0); - test_uint(v->value.type, ecs_id(ecs_i32_t)); - test_int(*(int32_t*)v->value.ptr, 10); - test_assert(v->value.ptr == src_ptr); - test_assert(src.ptr == NULL); - - const ecs_expr_var_t *v2 = ecs_vars_lookup(&vars, "foo"); - test_assert(v2 != NULL); - test_assert(v == v2); - - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_redeclare_in_scope(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_expr_var_t *v1 = ecs_vars_declare(&vars, "foo", ecs_id(ecs_i32_t)); - test_assert(v1 != NULL); - ecs_vars_push(&vars); - ecs_expr_var_t *v2 = ecs_vars_declare(&vars, "foo", ecs_id(ecs_i32_t)); - test_assert(v2 != NULL); - test_assert(v1 != v2); - test_assert(ecs_vars_lookup(&vars, "foo") == v2); - ecs_vars_pop(&vars); - test_assert(ecs_vars_lookup(&vars, "foo") == v1); - ecs_vars_fini(&vars); - - ecs_fini(world); -} - -void Vars_init_fini_vars(void) { - ecs_world_t *world = ecs_init(); - - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); - - ecs_vars_fini(&vars); - ecs_fini(world); - - test_assert(true); -} diff --git a/vendors/flecs/test/meta/src/VectorTypes.c b/vendors/flecs/test/meta/src/VectorTypes.c index 32bc81b17..19949106a 100644 --- a/vendors/flecs/test/meta/src/VectorTypes.c +++ b/vendors/flecs/test/meta/src/VectorTypes.c @@ -10,7 +10,7 @@ void meta_test_vector( test_int(ct->size, ECS_SIZEOF(ecs_vec_t)); test_int(ct->alignment, ECS_ALIGNOF(ecs_vec_t)); - const EcsMetaType *mt = ecs_get(world, t, EcsMetaType); + const EcsType *mt = ecs_get(world, t, EcsType); test_assert(mt != NULL); test_assert(mt->kind == EcsVectorType); diff --git a/vendors/flecs/test/meta/src/main.c b/vendors/flecs/test/meta/src/main.c index c2babd8cd..6cad106e6 100644 --- a/vendors/flecs/test/meta/src/main.c +++ b/vendors/flecs/test/meta/src/main.c @@ -75,6 +75,7 @@ void EnumTypes_struct_w_enum(void); void EnumTypes_zero_initialized(void); void EnumTypes_enum_relation(void); void EnumTypes_enum_w_short_notation(void); +void EnumTypes_enum_modified_event(void); // Testsuite 'BitmaskTypes' void BitmaskTypes_bitmask_1_constant(void); @@ -85,6 +86,47 @@ void BitmaskTypes_bitmask_4_constants_manual_values(void); void BitmaskTypes_struct_w_bitmask(void); void BitmaskTypes_bitmask_w_short_notation(void); +// Testsuite 'RuntimeTypes' +void RuntimeTypes_trivial_struct(void); +void RuntimeTypes_ctor(void); +void RuntimeTypes_dtor(void); +void RuntimeTypes_move(void); +void RuntimeTypes_copy(void); +void RuntimeTypes_trivial_array(void); +void RuntimeTypes_array_ctor(void); +void RuntimeTypes_array_dtor(void); +void RuntimeTypes_array_move(void); +void RuntimeTypes_array_copy(void); +void RuntimeTypes_vector_lifecycle(void); +void RuntimeTypes_vector_lifecycle_trivial_type(void); +void RuntimeTypes_opaque(void); +void RuntimeTypes_struct_with_ints(void); +void RuntimeTypes_struct_with_strings(void); +void RuntimeTypes_struct_with_opaque(void); +void RuntimeTypes_nested_struct_with_strings(void); +void RuntimeTypes_struct_with_array_of_strings(void); +void RuntimeTypes_struct_with_array_of_array_of_strings(void); +void RuntimeTypes_struct_with_vector_of_ints(void); +void RuntimeTypes_struct_with_vector_of_strings(void); +void RuntimeTypes_nested_struct_with_vector_of_ints(void); +void RuntimeTypes_nested_struct_with_vector_of_strings(void); +void RuntimeTypes_array_of_ints(void); +void RuntimeTypes_array_of_strings(void); +void RuntimeTypes_array_of_struct_with_ints(void); +void RuntimeTypes_array_of_struct_with_strings(void); +void RuntimeTypes_array_of_struct_with_opaques(void); +void RuntimeTypes_array_of_array_of_strings(void); +void RuntimeTypes_array_of_array_of_struct_with_strings(void); +void RuntimeTypes_array_of_vectors_of_ints(void); +void RuntimeTypes_array_of_vectors_of_strings(void); +void RuntimeTypes_array_of_opaque(void); +void RuntimeTypes_vector_of_ints(void); +void RuntimeTypes_vector_of_strings(void); +void RuntimeTypes_vector_of_struct_with_ints(void); +void RuntimeTypes_vector_of_struct_with_strings(void); +void RuntimeTypes_vector_of_arrays_of_strings(void); +void RuntimeTypes_vector_of_opaque(void); + // Testsuite 'StructTypes' void StructTypes_i32(void); void StructTypes_i32_i32(void); @@ -114,6 +156,7 @@ void StructTypes_overlapping_error_warning_range(void); void StructTypes_overlapping_value_error_range(void); void StructTypes_overlapping_value_warning_range(void); void StructTypes_struct_w_16_alignment(void); +void StructTypes_struct_w_use_offset(void); // Testsuite 'NestedStructTypes' void NestedStructTypes_1_bool(void); @@ -198,6 +241,7 @@ void Units_builtin_units(void); void Units_unit_w_short_notation(void); void Units_unit_prefix_w_short_notation(void); void Units_quantity_w_short_notation(void); +void Units_import_units_after_mini(void); // Testsuite 'Serialized' void Serialized_primitive_constants(void); @@ -261,6 +305,7 @@ void Serialized_ops_bitmask(void); void Serialized_ops_struct_w_bitmask(void); void Serialized_ops_enum(void); void Serialized_ops_struct_w_enum(void); +void Serialized_ops_missing_metatype(void); // Testsuite 'Cursor' void Cursor_set_bool(void); @@ -359,7 +404,7 @@ void Cursor_opaque_set_char(void); void Cursor_opaque_set_int(void); void Cursor_opaque_set_uint(void); void Cursor_opaque_set_float(void); -void Cursor_opaque_set_string(void); +void Cursor_opaque_get_set_string(void); void Cursor_opaque_set_entity(void); void Cursor_opaque_set_id(void); void Cursor_opaque_set_int_vec(void); @@ -399,121 +444,6 @@ void Cursor_next_out_of_bounds(void); void Cursor_set_out_of_bounds(void); void Cursor_get_member_id(void); -// Testsuite 'DeserializeFromExpr' -void DeserializeFromExpr_bool(void); -void DeserializeFromExpr_byte(void); -void DeserializeFromExpr_char(void); -void DeserializeFromExpr_char_literal(void); -void DeserializeFromExpr_i8(void); -void DeserializeFromExpr_i16(void); -void DeserializeFromExpr_i32(void); -void DeserializeFromExpr_i64(void); -void DeserializeFromExpr_iptr(void); -void DeserializeFromExpr_u8(void); -void DeserializeFromExpr_u16(void); -void DeserializeFromExpr_u32(void); -void DeserializeFromExpr_u64(void); -void DeserializeFromExpr_uptr(void); -void DeserializeFromExpr_float(void); -void DeserializeFromExpr_double(void); -void DeserializeFromExpr_negative_int(void); -void DeserializeFromExpr_negative_float(void); -void DeserializeFromExpr_string(void); -void DeserializeFromExpr_entity(void); -void DeserializeFromExpr_id(void); -void DeserializeFromExpr_enum(void); -void DeserializeFromExpr_bitmask(void); -void DeserializeFromExpr_struct_enum(void); -void DeserializeFromExpr_struct_bitmask(void); -void DeserializeFromExpr_struct_i32(void); -void DeserializeFromExpr_struct_i32_neg(void); -void DeserializeFromExpr_struct_i32_i32(void); -void DeserializeFromExpr_struct_entity(void); -void DeserializeFromExpr_struct_id(void); -void DeserializeFromExpr_struct_nested_i32(void); -void DeserializeFromExpr_struct_nested_i32_i32(void); -void DeserializeFromExpr_struct_2_nested_i32_i32(void); -void DeserializeFromExpr_struct_member_i32(void); -void DeserializeFromExpr_struct_member_i32_neg(void); -void DeserializeFromExpr_struct_member_i32_i32(void); -void DeserializeFromExpr_struct_member_nested_i32(void); -void DeserializeFromExpr_struct_member_nested_i32_i32(void); -void DeserializeFromExpr_struct_member_2_nested_i32_i32(void); -void DeserializeFromExpr_struct_member_2_nested_i32_i32_reverse(void); -void DeserializeFromExpr_struct_i32_array_3(void); -void DeserializeFromExpr_struct_struct_i32_array_3(void); -void DeserializeFromExpr_struct_struct_i32_i32_array_3(void); -void DeserializeFromExpr_struct_w_array_type_i32_i32(void); -void DeserializeFromExpr_struct_w_array_type_struct(void); -void DeserializeFromExpr_struct_w_2_array_type_i32_i32(void); -void DeserializeFromExpr_struct_w_2_array_type_struct(void); -void DeserializeFromExpr_discover_type_int(void); -void DeserializeFromExpr_discover_type_negative_int(void); -void DeserializeFromExpr_discover_type_float(void); -void DeserializeFromExpr_discover_type_negative_float(void); -void DeserializeFromExpr_discover_type_string(void); -void DeserializeFromExpr_discover_type_multiline_string(void); -void DeserializeFromExpr_discover_type_entity(void); -void DeserializeFromExpr_discover_type_bool(void); -void DeserializeFromExpr_discover_type_unknown(void); -void DeserializeFromExpr_discover_type_invalid(void); - -// Testsuite 'SerializeToExpr' -void SerializeToExpr_bool(void); -void SerializeToExpr_byte(void); -void SerializeToExpr_char(void); -void SerializeToExpr_i8(void); -void SerializeToExpr_i16(void); -void SerializeToExpr_i32(void); -void SerializeToExpr_i64(void); -void SerializeToExpr_iptr(void); -void SerializeToExpr_u8(void); -void SerializeToExpr_u16(void); -void SerializeToExpr_u32(void); -void SerializeToExpr_u64(void); -void SerializeToExpr_uptr(void); -void SerializeToExpr_float(void); -void SerializeToExpr_double(void); -void SerializeToExpr_string(void); -void SerializeToExpr_entity(void); -void SerializeToExpr_id(void); -void SerializeToExpr_enum(void); -void SerializeToExpr_bitmask(void); -void SerializeToExpr_float_nan(void); -void SerializeToExpr_float_inf(void); -void SerializeToExpr_double_nan(void); -void SerializeToExpr_double_inf(void); -void SerializeToExpr_struct_enum(void); -void SerializeToExpr_struct_bitmask(void); -void SerializeToExpr_struct_i32(void); -void SerializeToExpr_struct_i32_i32(void); -void SerializeToExpr_struct_entity(void); -void SerializeToExpr_struct_id(void); -void SerializeToExpr_array_i32_3(void); -void SerializeToExpr_array_struct_i32_i32(void); -void SerializeToExpr_array_array_i32_3(void); -void SerializeToExpr_vector_i32_3(void); -void SerializeToExpr_vector_struct_i32_i32(void); -void SerializeToExpr_vector_array_i32_3(void); -void SerializeToExpr_entity_entity_after_float(void); -void SerializeToExpr_struct_nested_i32(void); -void SerializeToExpr_struct_nested_i32_i32(void); -void SerializeToExpr_struct_2_nested_i32_i32(void); -void SerializeToExpr_struct_i32_array_3(void); -void SerializeToExpr_struct_struct_i32_array_3(void); -void SerializeToExpr_struct_struct_i32_i32_array_3(void); -void SerializeToExpr_struct_w_array_type_i32_i32(void); -void SerializeToExpr_struct_w_array_type_struct(void); -void SerializeToExpr_struct_w_2_array_type_i32_i32(void); -void SerializeToExpr_struct_w_2_array_type_struct(void); -void SerializeToExpr_struct_partial(void); -void SerializeToExpr_escape_simple_string(void); -void SerializeToExpr_escape_newline(void); -void SerializeToExpr_escape_2_newlines(void); -void SerializeToExpr_escape_string_w_trailing_newline(void); -void SerializeToExpr_escape_string_w_2_trailing_newlines(void); -void SerializeToExpr_escape_string_w_delim(void); - // Testsuite 'DeserializeFromJson' void DeserializeFromJson_struct_bool(void); void DeserializeFromJson_struct_byte(void); @@ -551,14 +481,19 @@ void DeserializeFromJson_struct_w_nested_member_i32(void); void DeserializeFromJson_struct_w_2_nested_members_i32(void); void DeserializeFromJson_struct_w_nested_members_struct(void); void DeserializeFromJson_struct_w_2_nested_members_struct(void); +void DeserializeFromJson_ser_deser_entity_named(void); +void DeserializeFromJson_ser_deser_entity_named_child(void); +void DeserializeFromJson_ser_deser_entity_namespaced_component(void); void DeserializeFromJson_deser_entity_1_component_1_member(void); void DeserializeFromJson_deser_entity_1_component_1_member_w_spaces(void); void DeserializeFromJson_deser_entity_1_component_2_members(void); void DeserializeFromJson_deser_entity_2_components(void); +void DeserializeFromJson_deser_entity_2_components_missing_object_close(void); void DeserializeFromJson_deser_entity_1_component_composite_member(void); void DeserializeFromJson_deser_entity_1_component_nested_member(void); void DeserializeFromJson_deser_entity_1_pair(void); void DeserializeFromJson_deser_entity_2_pairs(void); +void DeserializeFromJson_deser_entity_1_pair_2_targets(void); void DeserializeFromJson_deser_entity_empty(void); void DeserializeFromJson_deser_entity_w_path(void); void DeserializeFromJson_deser_entity_w_path_and_ids(void); @@ -597,6 +532,8 @@ void DeserializeFromJson_ser_deser_new_world_2_entities_w_anon_parent(void); void DeserializeFromJson_ser_deser_new_world_2_entities_w_named_parent(void); void DeserializeFromJson_ser_deser_new_world_2_entities_w_anon_parent_w_cycle(void); void DeserializeFromJson_ser_deser_new_world_2_entities_w_named_parent_w_cycle(void); +void DeserializeFromJson_ser_deser_new_world_w_prefab(void); +void DeserializeFromJson_ser_deser_new_world_w_disabled(void); void DeserializeFromJson_ser_deser_restore_1_entity_to_empty_table(void); void DeserializeFromJson_ser_deser_restore_1_entity_to_non_empty_table(void); void DeserializeFromJson_ser_deser_restore_1_anon_entity_to_empty_table(void); @@ -618,6 +555,30 @@ void DeserializeFromJson_ser_deser_3_entities_after_remove_all(void); void DeserializeFromJson_ser_deser_3_entities_after_delete_with(void); void DeserializeFromJson_ser_deser_w_hooks(void); void DeserializeFromJson_ser_deser_large_data(void); +void DeserializeFromJson_ser_deser_different_component_order(void); +void DeserializeFromJson_ser_deser_no_reflection_data(void); +void DeserializeFromJson_ser_deser_no_reflection_data_strict(void); +void DeserializeFromJson_ser_deser_value_for_tag(void); +void DeserializeFromJson_ser_deser_value_for_tag_strict(void); +void DeserializeFromJson_ser_deser_value_for_non_existing(void); +void DeserializeFromJson_ser_deser_value_for_non_existing_strict(void); +void DeserializeFromJson_ser_deser_cpp_typename(void); +void DeserializeFromJson_ser_deser_cpp_template(void); +void DeserializeFromJson_ser_deser_cpp_template_1_param(void); +void DeserializeFromJson_ser_deser_cpp_template_n_params(void); +void DeserializeFromJson_ser_deser_cpp_template_nested(void); +void DeserializeFromJson_ser_deser_cpp_template_n_params_nested(void); +void DeserializeFromJson_ser_deser_long_name(void); +void DeserializeFromJson_ser_deser_long_name_256_chars(void); +void DeserializeFromJson_ser_deser_w_alerts(void); +void DeserializeFromJson_ser_deser_w_alerts_w_progress(void); +void DeserializeFromJson_ser_deser_struct(void); +void DeserializeFromJson_ser_deser_anon_w_same_id_as_existing_named(void); +void DeserializeFromJson_ser_deser_named_to_different_table(void); +void DeserializeFromJson_ser_deser_named_child_to_different_table(void); +void DeserializeFromJson_ser_deser_with_child_tgt(void); +void DeserializeFromJson_ser_deser_with_child_tgt_no_child(void); +void DeserializeFromJson_deser_invalid_entity_name(void); // Testsuite 'SerializeToJson' void SerializeToJson_struct_bool(void); @@ -637,6 +598,8 @@ void SerializeToJson_struct_float(void); void SerializeToJson_struct_double(void); void SerializeToJson_struct_string(void); void SerializeToJson_struct_entity(void); +void SerializeToJson_struct_entity_0(void); +void SerializeToJson_struct_entity_10k(void); void SerializeToJson_struct_entity_after_float(void); void SerializeToJson_struct_id(void); void SerializeToJson_struct_float_nan(void); @@ -663,6 +626,7 @@ void SerializeToJson_array_array_i32_3(void); void SerializeToJson_vector_i32_3(void); void SerializeToJson_vector_struct_i32_i32(void); void SerializeToJson_vector_array_i32_3(void); +void SerializeToJson_serialize_from_stage(void); // Testsuite 'SerializeEntityToJson' void SerializeEntityToJson_serialize_empty(void); @@ -671,8 +635,16 @@ void SerializeEntityToJson_serialize_w_name_1_tag(void); void SerializeEntityToJson_serialize_w_name_2_tags(void); void SerializeEntityToJson_serialize_w_name_1_pair(void); void SerializeEntityToJson_serialize_w_base(void); -void SerializeEntityToJson_serialize_w_base_override(void); +void SerializeEntityToJson_serialize_w_base_dont_inherit_tag(void); +void SerializeEntityToJson_serialize_w_base_dont_inherit_component(void); +void SerializeEntityToJson_serialize_w_base_dont_inherit_pair(void); void SerializeEntityToJson_serialize_w_2_base(void); +void SerializeEntityToJson_serialize_component_w_base(void); +void SerializeEntityToJson_serialize_component_w_base_no_reflection_data(void); +void SerializeEntityToJson_serialize_component_w_base_w_owned(void); +void SerializeEntityToJson_serialize_component_w_base_w_owned_no_reflection_data(void); +void SerializeEntityToJson_serialize_component_w_base_w_owned_override(void); +void SerializeEntityToJson_serialize_component_w_base_w_owned_no_reflection_data_override(void); void SerializeEntityToJson_serialize_w_nested_base(void); void SerializeEntityToJson_serialize_w_1_component(void); void SerializeEntityToJson_serialize_w_2_components(void); @@ -684,23 +656,16 @@ void SerializeEntityToJson_serialize_w_type_info(void); void SerializeEntityToJson_serialize_w_type_info_unit(void); void SerializeEntityToJson_serialize_w_type_info_unit_quantity(void); void SerializeEntityToJson_serialize_w_type_info_unit_over(void); -void SerializeEntityToJson_serialize_wo_private(void); -void SerializeEntityToJson_serialize_w_private(void); +void SerializeEntityToJson_serialize_w_type_info_no_types(void); +void SerializeEntityToJson_serialize_w_type_info_no_components(void); void SerializeEntityToJson_serialize_w_label(void); void SerializeEntityToJson_serialize_w_label_no_name(void); -void SerializeEntityToJson_serialize_w_id_labels(void); void SerializeEntityToJson_serialize_w_brief(void); void SerializeEntityToJson_serialize_w_brief_no_brief(void); void SerializeEntityToJson_serialize_w_link(void); void SerializeEntityToJson_serialize_w_link_no_link(void); void SerializeEntityToJson_serialize_color(void); void SerializeEntityToJson_serialize_w_doc_w_quotes(void); -void SerializeEntityToJson_serialize_union_relationship(void); -void SerializeEntityToJson_serialize_union_relationship_w_labels(void); -void SerializeEntityToJson_serialize_union_relationship_invalid_entity(void); -void SerializeEntityToJson_serialize_union_relationship_invalid_entity_w_labels(void); -void SerializeEntityToJson_serialize_w_union_property(void); -void SerializeEntityToJson_serialize_w_union_property(void); void SerializeEntityToJson_serialize_from_core(void); void SerializeEntityToJson_serialize_w_1_alert(void); void SerializeEntityToJson_serialize_w_2_alerts(void); @@ -711,11 +676,32 @@ void SerializeEntityToJson_serialize_w_alerts_no_message(void); void SerializeEntityToJson_serialize_refs_childof(void); void SerializeEntityToJson_serialize_refs_custom(void); void SerializeEntityToJson_serialize_refs_wildcard(void); -void SerializeEntityToJson_serialize_no_ids(void); void SerializeEntityToJson_serialize_matches_filter(void); void SerializeEntityToJson_serialize_matches_query(void); void SerializeEntityToJson_serialize_matches_rule(void); void SerializeEntityToJson_serialize_no_matches(void); +void SerializeEntityToJson_serialize_id_recycled(void); +void SerializeEntityToJson_serialize_union_target(void); +void SerializeEntityToJson_serialize_union_target_recycled(void); +void SerializeEntityToJson_serialize_anonymous_w_builtin(void); +void SerializeEntityToJson_serialize_named_w_builtin(void); +void SerializeEntityToJson_serialize_named_child_w_builtin(void); +void SerializeEntityToJson_serialize_named_child_w_builtin_w_type_info(void); +void SerializeEntityToJson_serialize_from_stage(void); +void SerializeEntityToJson_serialize_sparse(void); +void SerializeEntityToJson_serialize_sparse_pair(void); +void SerializeEntityToJson_serialize_sparse_mixed(void); +void SerializeEntityToJson_serialize_sparse_inherited(void); +void SerializeEntityToJson_serialize_sparse_inherited_pair(void); +void SerializeEntityToJson_serialize_sparse_inherited_mixed(void); +void SerializeEntityToJson_serialize_sparse_w_type_info(void); +void SerializeEntityToJson_serialize_auto_override_w_inherited(void); +void SerializeEntityToJson_serialize_auto_override(void); +void SerializeEntityToJson_serialize_auto_override_pair(void); +void SerializeEntityToJson_serialize_auto_override_fullpath(void); +void SerializeEntityToJson_serialize_auto_override_pair_fullpath(void); +void SerializeEntityToJson_serialize_toggle(void); +void SerializeEntityToJson_serialize_toggle_pair(void); // Testsuite 'SerializeIterToJson' void SerializeIterToJson_serialize_1_comps_empty(void); @@ -739,6 +725,8 @@ void SerializeIterToJson_serialize_type_info_w_unit(void); void SerializeIterToJson_serialize_type_info_w_unit_quantity(void); void SerializeIterToJson_serialize_type_info_w_unit_over(void); void SerializeIterToJson_serialize_w_entity_label(void); +void SerializeIterToJson_serialize_w_entity_label_w_str(void); +void SerializeIterToJson_serialize_entity_label_w_newline(void); void SerializeIterToJson_serialize_w_var_labels(void); void SerializeIterToJson_serialize_w_var_component(void); void SerializeIterToJson_serialize_w_optional_tag(void); @@ -754,8 +742,13 @@ void SerializeIterToJson_serialize_component_from_var(void); void SerializeIterToJson_serialize_color(void); void SerializeIterToJson_serialize_ids(void); void SerializeIterToJson_serialize_ids_2_entities(void); -void SerializeIterToJson_serialize_variable_ids(void); -void SerializeIterToJson_serialize_variable_ids_2_entities(void); +void SerializeIterToJson_serialize_anonymous(void); +void SerializeIterToJson_serialize_anonymous_ids(void); +void SerializeIterToJson_serialize_variable_anonymous_no_full_path(void); +void SerializeIterToJson_serialize_variable_anonymous(void); +void SerializeIterToJson_serialize_anonymous_tag(void); +void SerializeIterToJson_serialize_anonymous_component(void); +void SerializeIterToJson_serialize_anonymous_pair(void); void SerializeIterToJson_serialize_invalid_value(void); void SerializeIterToJson_serialize_recycled_pair_id(void); void SerializeIterToJson_serialize_w_alert(void); @@ -771,16 +764,84 @@ void SerializeIterToJson_serialize_anonymous_entities_w_offset(void); void SerializeIterToJson_serialize_table(void); void SerializeIterToJson_serialize_table_w_id_labels(void); void SerializeIterToJson_serialize_table_w_var_labels(void); -void SerializeIterToJson_serialize_table_w_private(void); void SerializeIterToJson_serialize_world(void); -void SerializeIterToJson_serialize_term_labels(void); -void SerializeIterToJson_serialize_id_labels(void); void SerializeIterToJson_serialize_vars_for_query(void); void SerializeIterToJson_serialize_var_labels_for_query(void); -void SerializeIterToJson_serialize_var_ids_for_query(void); void SerializeIterToJson_serialize_null_doc_name(void); void SerializeIterToJson_serialize_rule_w_optional(void); void SerializeIterToJson_serialize_rule_w_optional_component(void); +void SerializeIterToJson_serialize_entity_w_flecs_core_parent(void); +void SerializeIterToJson_no_fields(void); +void SerializeIterToJson_no_fields_w_vars(void); +void SerializeIterToJson_serialize_from_stage(void); +void SerializeIterToJson_serialize_sparse(void); +void SerializeIterToJson_serialize_15_fields(void); +void SerializeIterToJson_serialize_16_fields(void); +void SerializeIterToJson_serialize_31_fields(void); +void SerializeIterToJson_serialize_32_fields(void); +void SerializeIterToJson_serialize_field_w_escaped_sep(void); + +// Testsuite 'SerializeIterToRowJson' +void SerializeIterToRowJson_serialize_this_w_1_tag(void); +void SerializeIterToRowJson_serialize_this_w_1_tag_w_parent(void); +void SerializeIterToRowJson_serialize_this_w_1_tag_no_name(void); +void SerializeIterToRowJson_serialize_this_w_2_tag(void); +void SerializeIterToRowJson_serialize_this_w_1_component(void); +void SerializeIterToRowJson_serialize_this_w_2_component(void); +void SerializeIterToRowJson_serialize_this_w_2_component_1_shared(void); +void SerializeIterToRowJson_serialize_this_w_1_pair(void); +void SerializeIterToRowJson_serialize_this_w_2_pair(void); +void SerializeIterToRowJson_serialize_this_w_1_pair_component(void); +void SerializeIterToRowJson_serialize_this_w_1_var(void); +void SerializeIterToRowJson_serialize_this_w_2_var(void); +void SerializeIterToRowJson_serialize_this_w_2_var_doc_name(void); +void SerializeIterToRowJson_serialize_this_w_1_tag_component_pair_var(void); +void SerializeIterToRowJson_serialize_this_w_2_tag_component_pair_var(void); +void SerializeIterToRowJson_serialize_var_w_1_tag(void); +void SerializeIterToRowJson_serialize_var_w_1_component(void); +void SerializeIterToRowJson_serialize_var_w_1_pair(void); +void SerializeIterToRowJson_serialize_var_w_1_var(void); +void SerializeIterToRowJson_serialize_var_w_2_component_1_shared(void); +void SerializeIterToRowJson_serialize_var_w_1_tag_component_pair_var(void); +void SerializeIterToRowJson_serialize_var_w_2_tag_component_pair_var(void); +void SerializeIterToRowJson_serialize_fixed_w_1_tag(void); +void SerializeIterToRowJson_serialize_fixed_w_1_component(void); +void SerializeIterToRowJson_serialize_fixed_w_1_pair(void); +void SerializeIterToRowJson_serialize_fixed_w_1_var(void); +void SerializeIterToRowJson_serialize_fixed_w_2_component_1_shared(void); +void SerializeIterToRowJson_serialize_fixed_w_1_tag_component_pair_var(void); +void SerializeIterToRowJson_serialize_fixed_w_2_tag_component_pair_var(void); +void SerializeIterToRowJson_serialize_not(void); +void SerializeIterToRowJson_serialize_not_pair_wildcard(void); +void SerializeIterToRowJson_serialize_not_pair_var(void); +void SerializeIterToRowJson_serialize_not_pair_var_constrained(void); +void SerializeIterToRowJson_serialize_optional(void); +void SerializeIterToRowJson_serialize_optional_pair_wildcard(void); +void SerializeIterToRowJson_serialize_optional_pair_var(void); +void SerializeIterToRowJson_serialize_optional_pair_var_constrained(void); +void SerializeIterToRowJson_serialize_or(void); +void SerializeIterToRowJson_serialize_scope(void); +void SerializeIterToRowJson_serialize_eq(void); +void SerializeIterToRowJson_serialize_neq(void); +void SerializeIterToRowJson_serialize_eq_m(void); +void SerializeIterToRowJson_serialize_table(void); +void SerializeIterToRowJson_serialize_table_w_eq(void); +void SerializeIterToRowJson_serialize_table_w_neq(void); +void SerializeIterToRowJson_serialize_table_w_2_pair_targets(void); +void SerializeIterToRowJson_serialize_table_w_2_pair_targets_2_rel(void); +void SerializeIterToRowJson_serialize_table_w_3_pair_targets(void); +void SerializeIterToRowJson_serialize_table_w_3_pair_targets_2_rel(void); +void SerializeIterToRowJson_serialize_everything(void); +void SerializeIterToRowJson_serialize_everything_table(void); +void SerializeIterToRowJson_serialize_w_type_info(void); +void SerializeIterToRowJson_serialize_w_field_info(void); +void SerializeIterToRowJson_serialize_w_field_info_pair_w_0_target(void); +void SerializeIterToRowJson_serialize_w_field_info_pair_w_not_tag(void); +void SerializeIterToRowJson_serialize_w_field_info_pair_w_not_pair(void); +void SerializeIterToRowJson_serialize_w_field_info_pair_w_not_component(void); +void SerializeIterToRowJson_serialize_w_field_info_w_or(void); +void SerializeIterToRowJson_serialize_recycled_id(void); +void SerializeIterToRowJson_serialize_entity_w_flecs_core_parent(void); // Testsuite 'SerializeTypeInfoToJson' void SerializeTypeInfoToJson_bool(void); @@ -824,6 +885,35 @@ void SerializeTypeInfoToJson_struct_nested_2_lvls(void); void SerializeTypeInfoToJson_struct_nested_2_members(void); void SerializeTypeInfoToJson_struct_nested_3_members(void); +// Testsuite 'SerializeQueryInfoToJson' +void SerializeQueryInfoToJson_1_tag(void); +void SerializeQueryInfoToJson_1_component(void); +void SerializeQueryInfoToJson_1_pair(void); +void SerializeQueryInfoToJson_1_pair_w_wildcard(void); +void SerializeQueryInfoToJson_1_pair_w_any(void); +void SerializeQueryInfoToJson_1_tag_fixed_src(void); +void SerializeQueryInfoToJson_1_tag_var_src(void); +void SerializeQueryInfoToJson_1_component_in(void); +void SerializeQueryInfoToJson_1_component_inout(void); +void SerializeQueryInfoToJson_1_component_out(void); +void SerializeQueryInfoToJson_1_component_none(void); +void SerializeQueryInfoToJson_1_tag_not(void); +void SerializeQueryInfoToJson_2_tags_or(void); +void SerializeQueryInfoToJson_1_tag_optional(void); +void SerializeQueryInfoToJson_1_tag_self(void); +void SerializeQueryInfoToJson_1_tag_self_dont_inherit(void); +void SerializeQueryInfoToJson_1_tag_up(void); +void SerializeQueryInfoToJson_1_tag_cascade(void); +void SerializeQueryInfoToJson_0_term(void); +void SerializeQueryInfoToJson_anonymous_tag(void); +void SerializeQueryInfoToJson_anonymous_pair(void); +void SerializeQueryInfoToJson_anonymous_component(void); +void SerializeQueryInfoToJson_anonymous_tag_recycled(void); +void SerializeQueryInfoToJson_anonymous_pair_recycled(void); +void SerializeQueryInfoToJson_anonymous_component_recycled(void); +void SerializeQueryInfoToJson_serialize_plan_trivial_query(void); +void SerializeQueryInfoToJson_serialize_plan_nontrivial_query(void); + // Testsuite 'MetaUtils' void MetaUtils_struct_w_2_i32(void); void MetaUtils_struct_w_2_bool(void); @@ -848,151 +938,6 @@ void MetaUtils_enum_constant_w_name_prefix(void); void MetaUtils_enum_constant_w_type_prefix(void); void MetaUtils_enum_constant_w_name_type_prefix(void); -// Testsuite 'Vars' -void Vars_declare_1_var(void); -void Vars_declare_2_vars(void); -void Vars_declare_vars_nested_scope(void); -void Vars_declare_vars_2_scopes(void); -void Vars_redeclare_var(void); -void Vars_i32_expr_w_i32_var(void); -void Vars_i32_expr_w_f32_var(void); -void Vars_i32_expr_w_string_var(void); -void Vars_string_expr_w_string_var(void); -void Vars_struct_expr_w_i32_vars(void); -void Vars_struct_expr_w_struct_var(void); -void Vars_nested_struct_expr_w_struct_var(void); -void Vars_declare_w_value(void); -void Vars_redeclare_in_scope(void); -void Vars_init_fini_vars(void); - -// Testsuite 'DeserExprOperators' -void DeserExprOperators_add_2_int_literals(void); -void DeserExprOperators_add_2_int_literals_twice(void); -void DeserExprOperators_sub_2_int_literals(void); -void DeserExprOperators_mul_2_int_literals(void); -void DeserExprOperators_div_2_int_literals(void); -void DeserExprOperators_add_3_int_literals(void); -void DeserExprOperators_add_3_int_literals_twice(void); -void DeserExprOperators_sub_3_int_literals(void); -void DeserExprOperators_mul_3_int_literals(void); -void DeserExprOperators_div_3_int_literals(void); -void DeserExprOperators_int_to_bool(void); -void DeserExprOperators_bool_to_int(void); -void DeserExprOperators_bool_to_uint(void); -void DeserExprOperators_add_mul_3_int_literals(void); -void DeserExprOperators_sub_mul_3_int_literals(void); -void DeserExprOperators_div_mul_3_int_literals(void); -void DeserExprOperators_add_div_3_int_literals(void); -void DeserExprOperators_sub_div_3_int_literals(void); -void DeserExprOperators_mul_div_3_int_literals(void); -void DeserExprOperators_mul_add_mul_add_int_literals(void); -void DeserExprOperators_mul_sub_mul_sub_int_literals(void); -void DeserExprOperators_mul_div_mul_div_int_literals(void); -void DeserExprOperators_div_add_div_add_int_literals(void); -void DeserExprOperators_div_sub_div_sub_int_literals(void); -void DeserExprOperators_div_sub_div_mul_int_literals(void); -void DeserExprOperators_div_mul_div_mul_int_literals(void); -void DeserExprOperators_add_2_flt_literals(void); -void DeserExprOperators_sub_2_flt_literals(void); -void DeserExprOperators_mul_2_flt_literals(void); -void DeserExprOperators_div_2_flt_literals(void); -void DeserExprOperators_add_2_int_neg_literals(void); -void DeserExprOperators_sub_2_int_neg_literals(void); -void DeserExprOperators_mul_2_int_neg_literals(void); -void DeserExprOperators_div_2_int_neg_literals(void); -void DeserExprOperators_mul_lparen_add_add_rparen_int_literals(void); -void DeserExprOperators_mul_lparen_add_add_add_rparen_int_literals(void); -void DeserExprOperators_mul_lparen_add_add_rparen_add_int_literals(void); -void DeserExprOperators_lparen_add_add_rparen_mul_int_literals(void); -void DeserExprOperators_lparen_add_add_add_rparen_mul_int_literals(void); -void DeserExprOperators_double_paren_add_add(void); -void DeserExprOperators_double_paren_literal(void); -void DeserExprOperators_lparen_add_add_rparen_mul_lparen_add_add_rparen(void); -void DeserExprOperators_float_result_add_2_int_literals(void); -void DeserExprOperators_struct_result_add_2_int_literals(void); -void DeserExprOperators_struct_result_add_2_2_fields_int_literals(void); -void DeserExprOperators_struct_result_add_3_int_literals(void); -void DeserExprOperators_struct_result_lparen_int_rparen(void); -void DeserExprOperators_add_to_var(void); -void DeserExprOperators_add_var_to(void); -void DeserExprOperators_var_member(void); -void DeserExprOperators_bool_cond_and_bool(void); -void DeserExprOperators_bool_cond_or_bool(void); -void DeserExprOperators_int_cond_and_int(void); -void DeserExprOperators_int_cond_or_int(void); -void DeserExprOperators_bool_cond_and_int(void); -void DeserExprOperators_int_cond_and_bool(void); -void DeserExprOperators_bool_cond_or_int(void); -void DeserExprOperators_int_cond_or_bool(void); -void DeserExprOperators_cond_eq_bool(void); -void DeserExprOperators_cond_eq_int(void); -void DeserExprOperators_cond_neq_bool(void); -void DeserExprOperators_cond_neq_int(void); -void DeserExprOperators_cond_eq_bool_int(void); -void DeserExprOperators_cond_eq_int_flt(void); -void DeserExprOperators_cond_eq_cond_and(void); -void DeserExprOperators_cond_eq_cond_or(void); -void DeserExprOperators_cond_gt_bool(void); -void DeserExprOperators_cond_gt_int(void); -void DeserExprOperators_cond_gt_flt(void); -void DeserExprOperators_cond_gteq_bool(void); -void DeserExprOperators_cond_gteq_int(void); -void DeserExprOperators_cond_gteq_flt(void); -void DeserExprOperators_cond_lt_bool(void); -void DeserExprOperators_cond_lt_int(void); -void DeserExprOperators_cond_lt_flt(void); -void DeserExprOperators_cond_lteq_bool(void); -void DeserExprOperators_cond_lteq_int(void); -void DeserExprOperators_cond_lteq_flt(void); -void DeserExprOperators_min_lparen_int_rparen(void); -void DeserExprOperators_min_lparen_int_add_int_rparen(void); -void DeserExprOperators_min_var(void); -void DeserExprOperators_min_lparen_int_rparen_to_i64(void); -void DeserExprOperators_min_lparen_int_rparen_to_i32(void); -void DeserExprOperators_struct_w_min_var(void); -void DeserExprOperators_struct_w_min_lparen_int_rparen(void); -void DeserExprOperators_struct_w_min_lparen_var_rparen(void); -void DeserExprOperators_shift_left_int(void); -void DeserExprOperators_shift_right_int(void); -void DeserExprOperators_shift_left_int_add_int(void); -void DeserExprOperators_shift_left_int_mul_int(void); -void DeserExprOperators_add_int_shift_left_int(void); -void DeserExprOperators_mul_int_shift_left_int(void); -void DeserExprOperators_add_int_shift_left_int_add_int(void); -void DeserExprOperators_mul_int_shift_left_int_mul_int(void); -void DeserExprOperators_entity_expr(void); -void DeserExprOperators_entity_path_expr(void); -void DeserExprOperators_entity_parent_func(void); -void DeserExprOperators_entity_name_func(void); -void DeserExprOperators_entity_doc_name_func(void); -void DeserExprOperators_entity_chain_func(void); -void DeserExprOperators_var_parent_func(void); -void DeserExprOperators_var_name_func(void); -void DeserExprOperators_var_doc_name_func(void); -void DeserExprOperators_var_chain_func(void); -void DeserExprOperators_interpolate_string_w_i32_var(void); -void DeserExprOperators_interpolate_string_w_string_var(void); -void DeserExprOperators_interpolate_string_w_entity_var(void); -void DeserExprOperators_interpolate_string_w_id_var(void); -void DeserExprOperators_interpolate_string_w_var_not_found(void); -void DeserExprOperators_interpolate_string_w_entity_var_0(void); -void DeserExprOperators_interpolate_string_w_var_special_chars(void); -void DeserExprOperators_interpolate_string_w_var_before_after_text(void); -void DeserExprOperators_interpolate_string_w_curly_brackets_var(void); -void DeserExprOperators_interpolate_string_w_curly_brackets_expr(void); -void DeserExprOperators_interpolate_string_w_curly_brackets_expr_w_var(void); -void DeserExprOperators_interpolate_string_w_curly_brackets_expr_w_composite_var(void); -void DeserExprOperators_interpolate_string_w_escape_var_operator(void); -void DeserExprOperators_interpolate_string_w_escape_curly_brackets(void); -void DeserExprOperators_interpolate_string_w_func(void); -void DeserExprOperators_interpolate_string_w_func_chain(void); -void DeserExprOperators_iter_to_vars_no_data(void); -void DeserExprOperators_iter_to_vars_1_comp(void); -void DeserExprOperators_iter_to_vars_2_comps(void); -void DeserExprOperators_iter_to_vars_1_comp_1_tag(void); -void DeserExprOperators_iter_to_vars_w_1_query_var(void); -void DeserExprOperators_iter_to_vars_w_2_query_vars(void); - // Testsuite 'OpaqueTypes' void OpaqueTypes_ser_i32_type_to_json(void); void OpaqueTypes_ser_string_type_to_json(void); @@ -1011,6 +956,7 @@ void OpaqueTypes_deser_entity_from_json(void); void OpaqueTypes_ser_deser_world_w_ser_opaque(void); void OpaqueTypes_ser_deser_entity(void); void OpaqueTypes_ser_deser_0_entity(void); +void OpaqueTypes_const_string(void); // Testsuite 'Misc' void Misc_primitive_from_stage(void); @@ -1313,6 +1259,10 @@ bake_test_case EnumTypes_testcases[] = { { "enum_w_short_notation", EnumTypes_enum_w_short_notation + }, + { + "enum_modified_event", + EnumTypes_enum_modified_event } }; @@ -1347,6 +1297,165 @@ bake_test_case BitmaskTypes_testcases[] = { } }; +bake_test_case RuntimeTypes_testcases[] = { + { + "trivial_struct", + RuntimeTypes_trivial_struct + }, + { + "ctor", + RuntimeTypes_ctor + }, + { + "dtor", + RuntimeTypes_dtor + }, + { + "move", + RuntimeTypes_move + }, + { + "copy", + RuntimeTypes_copy + }, + { + "trivial_array", + RuntimeTypes_trivial_array + }, + { + "array_ctor", + RuntimeTypes_array_ctor + }, + { + "array_dtor", + RuntimeTypes_array_dtor + }, + { + "array_move", + RuntimeTypes_array_move + }, + { + "array_copy", + RuntimeTypes_array_copy + }, + { + "vector_lifecycle", + RuntimeTypes_vector_lifecycle + }, + { + "vector_lifecycle_trivial_type", + RuntimeTypes_vector_lifecycle_trivial_type + }, + { + "opaque", + RuntimeTypes_opaque + }, + { + "struct_with_ints", + RuntimeTypes_struct_with_ints + }, + { + "struct_with_strings", + RuntimeTypes_struct_with_strings + }, + { + "struct_with_opaque", + RuntimeTypes_struct_with_opaque + }, + { + "nested_struct_with_strings", + RuntimeTypes_nested_struct_with_strings + }, + { + "struct_with_array_of_strings", + RuntimeTypes_struct_with_array_of_strings + }, + { + "struct_with_array_of_array_of_strings", + RuntimeTypes_struct_with_array_of_array_of_strings + }, + { + "struct_with_vector_of_ints", + RuntimeTypes_struct_with_vector_of_ints + }, + { + "struct_with_vector_of_strings", + RuntimeTypes_struct_with_vector_of_strings + }, + { + "nested_struct_with_vector_of_ints", + RuntimeTypes_nested_struct_with_vector_of_ints + }, + { + "nested_struct_with_vector_of_strings", + RuntimeTypes_nested_struct_with_vector_of_strings + }, + { + "array_of_ints", + RuntimeTypes_array_of_ints + }, + { + "array_of_strings", + RuntimeTypes_array_of_strings + }, + { + "array_of_struct_with_ints", + RuntimeTypes_array_of_struct_with_ints + }, + { + "array_of_struct_with_strings", + RuntimeTypes_array_of_struct_with_strings + }, + { + "array_of_struct_with_opaques", + RuntimeTypes_array_of_struct_with_opaques + }, + { + "array_of_array_of_strings", + RuntimeTypes_array_of_array_of_strings + }, + { + "array_of_array_of_struct_with_strings", + RuntimeTypes_array_of_array_of_struct_with_strings + }, + { + "array_of_vectors_of_ints", + RuntimeTypes_array_of_vectors_of_ints + }, + { + "array_of_vectors_of_strings", + RuntimeTypes_array_of_vectors_of_strings + }, + { + "array_of_opaque", + RuntimeTypes_array_of_opaque + }, + { + "vector_of_ints", + RuntimeTypes_vector_of_ints + }, + { + "vector_of_strings", + RuntimeTypes_vector_of_strings + }, + { + "vector_of_struct_with_ints", + RuntimeTypes_vector_of_struct_with_ints + }, + { + "vector_of_struct_with_strings", + RuntimeTypes_vector_of_struct_with_strings + }, + { + "vector_of_arrays_of_strings", + RuntimeTypes_vector_of_arrays_of_strings + }, + { + "vector_of_opaque", + RuntimeTypes_vector_of_opaque + } +}; + bake_test_case StructTypes_testcases[] = { { "i32", @@ -1459,6 +1568,10 @@ bake_test_case StructTypes_testcases[] = { { "struct_w_16_alignment", StructTypes_struct_w_16_alignment + }, + { + "struct_w_use_offset", + StructTypes_struct_w_use_offset } }; @@ -1775,6 +1888,10 @@ bake_test_case Units_testcases[] = { { "quantity_w_short_notation", Units_quantity_w_short_notation + }, + { + "import_units_after_mini", + Units_import_units_after_mini } }; @@ -2022,6 +2139,10 @@ bake_test_case Serialized_testcases[] = { { "ops_struct_w_enum", Serialized_ops_struct_w_enum + }, + { + "ops_missing_metatype", + Serialized_ops_missing_metatype } }; @@ -2411,8 +2532,8 @@ bake_test_case Cursor_testcases[] = { Cursor_opaque_set_float }, { - "opaque_set_string", - Cursor_opaque_set_string + "opaque_get_set_string", + Cursor_opaque_get_set_string }, { "opaque_set_entity", @@ -2568,668 +2689,238 @@ bake_test_case Cursor_testcases[] = { } }; -bake_test_case DeserializeFromExpr_testcases[] = { - { - "bool", - DeserializeFromExpr_bool - }, - { - "byte", - DeserializeFromExpr_byte - }, +bake_test_case DeserializeFromJson_testcases[] = { { - "char", - DeserializeFromExpr_char + "struct_bool", + DeserializeFromJson_struct_bool }, { - "char_literal", - DeserializeFromExpr_char_literal + "struct_byte", + DeserializeFromJson_struct_byte }, { - "i8", - DeserializeFromExpr_i8 + "struct_char", + DeserializeFromJson_struct_char }, { - "i16", - DeserializeFromExpr_i16 + "struct_char_literal", + DeserializeFromJson_struct_char_literal }, { - "i32", - DeserializeFromExpr_i32 + "struct_i8", + DeserializeFromJson_struct_i8 }, { - "i64", - DeserializeFromExpr_i64 + "struct_i16", + DeserializeFromJson_struct_i16 }, { - "iptr", - DeserializeFromExpr_iptr + "struct_i32", + DeserializeFromJson_struct_i32 }, { - "u8", - DeserializeFromExpr_u8 + "struct_i64", + DeserializeFromJson_struct_i64 }, { - "u16", - DeserializeFromExpr_u16 + "struct_iptr", + DeserializeFromJson_struct_iptr }, { - "u32", - DeserializeFromExpr_u32 + "struct_u8", + DeserializeFromJson_struct_u8 }, { - "u64", - DeserializeFromExpr_u64 + "struct_u16", + DeserializeFromJson_struct_u16 }, { - "uptr", - DeserializeFromExpr_uptr + "struct_u32", + DeserializeFromJson_struct_u32 }, { - "float", - DeserializeFromExpr_float + "struct_u64", + DeserializeFromJson_struct_u64 }, { - "double", - DeserializeFromExpr_double + "struct_uptr", + DeserializeFromJson_struct_uptr }, { - "negative_int", - DeserializeFromExpr_negative_int + "struct_float", + DeserializeFromJson_struct_float }, { - "negative_float", - DeserializeFromExpr_negative_float + "struct_double", + DeserializeFromJson_struct_double }, { - "string", - DeserializeFromExpr_string + "struct_negative_int", + DeserializeFromJson_struct_negative_int }, { - "entity", - DeserializeFromExpr_entity + "struct_negative_float", + DeserializeFromJson_struct_negative_float }, { - "id", - DeserializeFromExpr_id + "struct_string", + DeserializeFromJson_struct_string }, { - "enum", - DeserializeFromExpr_enum + "struct_entity", + DeserializeFromJson_struct_entity }, { - "bitmask", - DeserializeFromExpr_bitmask + "struct_id", + DeserializeFromJson_struct_id }, { "struct_enum", - DeserializeFromExpr_struct_enum + DeserializeFromJson_struct_enum }, { "struct_bitmask", - DeserializeFromExpr_struct_bitmask - }, - { - "struct_i32", - DeserializeFromExpr_struct_i32 - }, - { - "struct_i32_neg", - DeserializeFromExpr_struct_i32_neg + DeserializeFromJson_struct_bitmask }, { "struct_i32_i32", - DeserializeFromExpr_struct_i32_i32 - }, - { - "struct_entity", - DeserializeFromExpr_struct_entity - }, - { - "struct_id", - DeserializeFromExpr_struct_id + DeserializeFromJson_struct_i32_i32 }, { "struct_nested_i32", - DeserializeFromExpr_struct_nested_i32 + DeserializeFromJson_struct_nested_i32 }, { "struct_nested_i32_i32", - DeserializeFromExpr_struct_nested_i32_i32 + DeserializeFromJson_struct_nested_i32_i32 }, { "struct_2_nested_i32_i32", - DeserializeFromExpr_struct_2_nested_i32_i32 + DeserializeFromJson_struct_2_nested_i32_i32 }, { - "struct_member_i32", - DeserializeFromExpr_struct_member_i32 + "struct_i32_array_3", + DeserializeFromJson_struct_i32_array_3 }, { - "struct_member_i32_neg", - DeserializeFromExpr_struct_member_i32_neg + "struct_struct_i32_array_3", + DeserializeFromJson_struct_struct_i32_array_3 }, { - "struct_member_i32_i32", - DeserializeFromExpr_struct_member_i32_i32 + "struct_struct_i32_i32_array_3", + DeserializeFromJson_struct_struct_i32_i32_array_3 }, { - "struct_member_nested_i32", - DeserializeFromExpr_struct_member_nested_i32 + "struct_w_array_type_i32_i32", + DeserializeFromJson_struct_w_array_type_i32_i32 }, { - "struct_member_nested_i32_i32", - DeserializeFromExpr_struct_member_nested_i32_i32 + "struct_w_2_array_type_i32_i32", + DeserializeFromJson_struct_w_2_array_type_i32_i32 }, { - "struct_member_2_nested_i32_i32", - DeserializeFromExpr_struct_member_2_nested_i32_i32 + "struct_w_nested_member_i32", + DeserializeFromJson_struct_w_nested_member_i32 }, { - "struct_member_2_nested_i32_i32_reverse", - DeserializeFromExpr_struct_member_2_nested_i32_i32_reverse + "struct_w_2_nested_members_i32", + DeserializeFromJson_struct_w_2_nested_members_i32 }, { - "struct_i32_array_3", - DeserializeFromExpr_struct_i32_array_3 + "struct_w_nested_members_struct", + DeserializeFromJson_struct_w_nested_members_struct }, { - "struct_struct_i32_array_3", - DeserializeFromExpr_struct_struct_i32_array_3 + "struct_w_2_nested_members_struct", + DeserializeFromJson_struct_w_2_nested_members_struct }, { - "struct_struct_i32_i32_array_3", - DeserializeFromExpr_struct_struct_i32_i32_array_3 + "ser_deser_entity_named", + DeserializeFromJson_ser_deser_entity_named }, { - "struct_w_array_type_i32_i32", - DeserializeFromExpr_struct_w_array_type_i32_i32 + "ser_deser_entity_named_child", + DeserializeFromJson_ser_deser_entity_named_child }, { - "struct_w_array_type_struct", - DeserializeFromExpr_struct_w_array_type_struct + "ser_deser_entity_namespaced_component", + DeserializeFromJson_ser_deser_entity_namespaced_component }, { - "struct_w_2_array_type_i32_i32", - DeserializeFromExpr_struct_w_2_array_type_i32_i32 + "deser_entity_1_component_1_member", + DeserializeFromJson_deser_entity_1_component_1_member }, { - "struct_w_2_array_type_struct", - DeserializeFromExpr_struct_w_2_array_type_struct + "deser_entity_1_component_1_member_w_spaces", + DeserializeFromJson_deser_entity_1_component_1_member_w_spaces }, { - "discover_type_int", - DeserializeFromExpr_discover_type_int + "deser_entity_1_component_2_members", + DeserializeFromJson_deser_entity_1_component_2_members }, { - "discover_type_negative_int", - DeserializeFromExpr_discover_type_negative_int + "deser_entity_2_components", + DeserializeFromJson_deser_entity_2_components }, { - "discover_type_float", - DeserializeFromExpr_discover_type_float + "deser_entity_2_components_missing_object_close", + DeserializeFromJson_deser_entity_2_components_missing_object_close }, { - "discover_type_negative_float", - DeserializeFromExpr_discover_type_negative_float + "deser_entity_1_component_composite_member", + DeserializeFromJson_deser_entity_1_component_composite_member }, { - "discover_type_string", - DeserializeFromExpr_discover_type_string + "deser_entity_1_component_nested_member", + DeserializeFromJson_deser_entity_1_component_nested_member }, { - "discover_type_multiline_string", - DeserializeFromExpr_discover_type_multiline_string + "deser_entity_1_pair", + DeserializeFromJson_deser_entity_1_pair }, { - "discover_type_entity", - DeserializeFromExpr_discover_type_entity + "deser_entity_2_pairs", + DeserializeFromJson_deser_entity_2_pairs }, { - "discover_type_bool", - DeserializeFromExpr_discover_type_bool + "deser_entity_1_pair_2_targets", + DeserializeFromJson_deser_entity_1_pair_2_targets }, { - "discover_type_unknown", - DeserializeFromExpr_discover_type_unknown + "deser_entity_empty", + DeserializeFromJson_deser_entity_empty }, { - "discover_type_invalid", - DeserializeFromExpr_discover_type_invalid - } -}; - -bake_test_case SerializeToExpr_testcases[] = { - { - "bool", - SerializeToExpr_bool + "deser_entity_w_path", + DeserializeFromJson_deser_entity_w_path }, { - "byte", - SerializeToExpr_byte + "deser_entity_w_path_and_ids", + DeserializeFromJson_deser_entity_w_path_and_ids }, { - "char", - SerializeToExpr_char + "deser_entity_w_path_and_ids_and_values", + DeserializeFromJson_deser_entity_w_path_and_ids_and_values }, { - "i8", - SerializeToExpr_i8 + "deser_entity_w_ids", + DeserializeFromJson_deser_entity_w_ids }, { - "i16", - SerializeToExpr_i16 + "ser_deser_mini", + DeserializeFromJson_ser_deser_mini }, { - "i32", - SerializeToExpr_i32 + "ser_deser_init", + DeserializeFromJson_ser_deser_init }, { - "i64", - SerializeToExpr_i64 + "ser_deser_mini_serialize_builtin", + DeserializeFromJson_ser_deser_mini_serialize_builtin }, { - "iptr", - SerializeToExpr_iptr - }, - { - "u8", - SerializeToExpr_u8 - }, - { - "u16", - SerializeToExpr_u16 - }, - { - "u32", - SerializeToExpr_u32 - }, - { - "u64", - SerializeToExpr_u64 - }, - { - "uptr", - SerializeToExpr_uptr - }, - { - "float", - SerializeToExpr_float - }, - { - "double", - SerializeToExpr_double - }, - { - "string", - SerializeToExpr_string - }, - { - "entity", - SerializeToExpr_entity - }, - { - "id", - SerializeToExpr_id - }, - { - "enum", - SerializeToExpr_enum - }, - { - "bitmask", - SerializeToExpr_bitmask - }, - { - "float_nan", - SerializeToExpr_float_nan - }, - { - "float_inf", - SerializeToExpr_float_inf - }, - { - "double_nan", - SerializeToExpr_double_nan - }, - { - "double_inf", - SerializeToExpr_double_inf - }, - { - "struct_enum", - SerializeToExpr_struct_enum - }, - { - "struct_bitmask", - SerializeToExpr_struct_bitmask - }, - { - "struct_i32", - SerializeToExpr_struct_i32 - }, - { - "struct_i32_i32", - SerializeToExpr_struct_i32_i32 - }, - { - "struct_entity", - SerializeToExpr_struct_entity - }, - { - "struct_id", - SerializeToExpr_struct_id - }, - { - "array_i32_3", - SerializeToExpr_array_i32_3 - }, - { - "array_struct_i32_i32", - SerializeToExpr_array_struct_i32_i32 - }, - { - "array_array_i32_3", - SerializeToExpr_array_array_i32_3 - }, - { - "vector_i32_3", - SerializeToExpr_vector_i32_3 - }, - { - "vector_struct_i32_i32", - SerializeToExpr_vector_struct_i32_i32 - }, - { - "vector_array_i32_3", - SerializeToExpr_vector_array_i32_3 - }, - { - "entity_entity_after_float", - SerializeToExpr_entity_entity_after_float - }, - { - "struct_nested_i32", - SerializeToExpr_struct_nested_i32 - }, - { - "struct_nested_i32_i32", - SerializeToExpr_struct_nested_i32_i32 - }, - { - "struct_2_nested_i32_i32", - SerializeToExpr_struct_2_nested_i32_i32 - }, - { - "struct_i32_array_3", - SerializeToExpr_struct_i32_array_3 - }, - { - "struct_struct_i32_array_3", - SerializeToExpr_struct_struct_i32_array_3 - }, - { - "struct_struct_i32_i32_array_3", - SerializeToExpr_struct_struct_i32_i32_array_3 - }, - { - "struct_w_array_type_i32_i32", - SerializeToExpr_struct_w_array_type_i32_i32 - }, - { - "struct_w_array_type_struct", - SerializeToExpr_struct_w_array_type_struct - }, - { - "struct_w_2_array_type_i32_i32", - SerializeToExpr_struct_w_2_array_type_i32_i32 - }, - { - "struct_w_2_array_type_struct", - SerializeToExpr_struct_w_2_array_type_struct - }, - { - "struct_partial", - SerializeToExpr_struct_partial - }, - { - "escape_simple_string", - SerializeToExpr_escape_simple_string - }, - { - "escape_newline", - SerializeToExpr_escape_newline - }, - { - "escape_2_newlines", - SerializeToExpr_escape_2_newlines - }, - { - "escape_string_w_trailing_newline", - SerializeToExpr_escape_string_w_trailing_newline - }, - { - "escape_string_w_2_trailing_newlines", - SerializeToExpr_escape_string_w_2_trailing_newlines - }, - { - "escape_string_w_delim", - SerializeToExpr_escape_string_w_delim - } -}; - -bake_test_case DeserializeFromJson_testcases[] = { - { - "struct_bool", - DeserializeFromJson_struct_bool - }, - { - "struct_byte", - DeserializeFromJson_struct_byte - }, - { - "struct_char", - DeserializeFromJson_struct_char - }, - { - "struct_char_literal", - DeserializeFromJson_struct_char_literal - }, - { - "struct_i8", - DeserializeFromJson_struct_i8 - }, - { - "struct_i16", - DeserializeFromJson_struct_i16 - }, - { - "struct_i32", - DeserializeFromJson_struct_i32 - }, - { - "struct_i64", - DeserializeFromJson_struct_i64 - }, - { - "struct_iptr", - DeserializeFromJson_struct_iptr - }, - { - "struct_u8", - DeserializeFromJson_struct_u8 - }, - { - "struct_u16", - DeserializeFromJson_struct_u16 - }, - { - "struct_u32", - DeserializeFromJson_struct_u32 - }, - { - "struct_u64", - DeserializeFromJson_struct_u64 - }, - { - "struct_uptr", - DeserializeFromJson_struct_uptr - }, - { - "struct_float", - DeserializeFromJson_struct_float - }, - { - "struct_double", - DeserializeFromJson_struct_double - }, - { - "struct_negative_int", - DeserializeFromJson_struct_negative_int - }, - { - "struct_negative_float", - DeserializeFromJson_struct_negative_float - }, - { - "struct_string", - DeserializeFromJson_struct_string - }, - { - "struct_entity", - DeserializeFromJson_struct_entity - }, - { - "struct_id", - DeserializeFromJson_struct_id - }, - { - "struct_enum", - DeserializeFromJson_struct_enum - }, - { - "struct_bitmask", - DeserializeFromJson_struct_bitmask - }, - { - "struct_i32_i32", - DeserializeFromJson_struct_i32_i32 - }, - { - "struct_nested_i32", - DeserializeFromJson_struct_nested_i32 - }, - { - "struct_nested_i32_i32", - DeserializeFromJson_struct_nested_i32_i32 - }, - { - "struct_2_nested_i32_i32", - DeserializeFromJson_struct_2_nested_i32_i32 - }, - { - "struct_i32_array_3", - DeserializeFromJson_struct_i32_array_3 - }, - { - "struct_struct_i32_array_3", - DeserializeFromJson_struct_struct_i32_array_3 - }, - { - "struct_struct_i32_i32_array_3", - DeserializeFromJson_struct_struct_i32_i32_array_3 - }, - { - "struct_w_array_type_i32_i32", - DeserializeFromJson_struct_w_array_type_i32_i32 - }, - { - "struct_w_2_array_type_i32_i32", - DeserializeFromJson_struct_w_2_array_type_i32_i32 - }, - { - "struct_w_nested_member_i32", - DeserializeFromJson_struct_w_nested_member_i32 - }, - { - "struct_w_2_nested_members_i32", - DeserializeFromJson_struct_w_2_nested_members_i32 - }, - { - "struct_w_nested_members_struct", - DeserializeFromJson_struct_w_nested_members_struct - }, - { - "struct_w_2_nested_members_struct", - DeserializeFromJson_struct_w_2_nested_members_struct - }, - { - "deser_entity_1_component_1_member", - DeserializeFromJson_deser_entity_1_component_1_member - }, - { - "deser_entity_1_component_1_member_w_spaces", - DeserializeFromJson_deser_entity_1_component_1_member_w_spaces - }, - { - "deser_entity_1_component_2_members", - DeserializeFromJson_deser_entity_1_component_2_members - }, - { - "deser_entity_2_components", - DeserializeFromJson_deser_entity_2_components - }, - { - "deser_entity_1_component_composite_member", - DeserializeFromJson_deser_entity_1_component_composite_member - }, - { - "deser_entity_1_component_nested_member", - DeserializeFromJson_deser_entity_1_component_nested_member - }, - { - "deser_entity_1_pair", - DeserializeFromJson_deser_entity_1_pair - }, - { - "deser_entity_2_pairs", - DeserializeFromJson_deser_entity_2_pairs - }, - { - "deser_entity_empty", - DeserializeFromJson_deser_entity_empty - }, - { - "deser_entity_w_path", - DeserializeFromJson_deser_entity_w_path - }, - { - "deser_entity_w_path_and_ids", - DeserializeFromJson_deser_entity_w_path_and_ids - }, - { - "deser_entity_w_path_and_ids_and_values", - DeserializeFromJson_deser_entity_w_path_and_ids_and_values - }, - { - "deser_entity_w_ids", - DeserializeFromJson_deser_entity_w_ids - }, - { - "ser_deser_mini", - DeserializeFromJson_ser_deser_mini - }, - { - "ser_deser_init", - DeserializeFromJson_ser_deser_init - }, - { - "ser_deser_mini_serialize_builtin", - DeserializeFromJson_ser_deser_mini_serialize_builtin - }, - { - "ser_deser_mini_serialize_modules", - DeserializeFromJson_ser_deser_mini_serialize_modules + "ser_deser_mini_serialize_modules", + DeserializeFromJson_ser_deser_mini_serialize_modules }, { "ser_deser_mini_serialize_builtin_modules", @@ -3347,6 +3038,14 @@ bake_test_case DeserializeFromJson_testcases[] = { "ser_deser_new_world_2_entities_w_named_parent_w_cycle", DeserializeFromJson_ser_deser_new_world_2_entities_w_named_parent_w_cycle }, + { + "ser_deser_new_world_w_prefab", + DeserializeFromJson_ser_deser_new_world_w_prefab + }, + { + "ser_deser_new_world_w_disabled", + DeserializeFromJson_ser_deser_new_world_w_disabled + }, { "ser_deser_restore_1_entity_to_empty_table", DeserializeFromJson_ser_deser_restore_1_entity_to_empty_table @@ -3430,6 +3129,102 @@ bake_test_case DeserializeFromJson_testcases[] = { { "ser_deser_large_data", DeserializeFromJson_ser_deser_large_data + }, + { + "ser_deser_different_component_order", + DeserializeFromJson_ser_deser_different_component_order + }, + { + "ser_deser_no_reflection_data", + DeserializeFromJson_ser_deser_no_reflection_data + }, + { + "ser_deser_no_reflection_data_strict", + DeserializeFromJson_ser_deser_no_reflection_data_strict + }, + { + "ser_deser_value_for_tag", + DeserializeFromJson_ser_deser_value_for_tag + }, + { + "ser_deser_value_for_tag_strict", + DeserializeFromJson_ser_deser_value_for_tag_strict + }, + { + "ser_deser_value_for_non_existing", + DeserializeFromJson_ser_deser_value_for_non_existing + }, + { + "ser_deser_value_for_non_existing_strict", + DeserializeFromJson_ser_deser_value_for_non_existing_strict + }, + { + "ser_deser_cpp_typename", + DeserializeFromJson_ser_deser_cpp_typename + }, + { + "ser_deser_cpp_template", + DeserializeFromJson_ser_deser_cpp_template + }, + { + "ser_deser_cpp_template_1_param", + DeserializeFromJson_ser_deser_cpp_template_1_param + }, + { + "ser_deser_cpp_template_n_params", + DeserializeFromJson_ser_deser_cpp_template_n_params + }, + { + "ser_deser_cpp_template_nested", + DeserializeFromJson_ser_deser_cpp_template_nested + }, + { + "ser_deser_cpp_template_n_params_nested", + DeserializeFromJson_ser_deser_cpp_template_n_params_nested + }, + { + "ser_deser_long_name", + DeserializeFromJson_ser_deser_long_name + }, + { + "ser_deser_long_name_256_chars", + DeserializeFromJson_ser_deser_long_name_256_chars + }, + { + "ser_deser_w_alerts", + DeserializeFromJson_ser_deser_w_alerts + }, + { + "ser_deser_w_alerts_w_progress", + DeserializeFromJson_ser_deser_w_alerts_w_progress + }, + { + "ser_deser_struct", + DeserializeFromJson_ser_deser_struct + }, + { + "ser_deser_anon_w_same_id_as_existing_named", + DeserializeFromJson_ser_deser_anon_w_same_id_as_existing_named + }, + { + "ser_deser_named_to_different_table", + DeserializeFromJson_ser_deser_named_to_different_table + }, + { + "ser_deser_named_child_to_different_table", + DeserializeFromJson_ser_deser_named_child_to_different_table + }, + { + "ser_deser_with_child_tgt", + DeserializeFromJson_ser_deser_with_child_tgt + }, + { + "ser_deser_with_child_tgt_no_child", + DeserializeFromJson_ser_deser_with_child_tgt_no_child + }, + { + "deser_invalid_entity_name", + DeserializeFromJson_deser_invalid_entity_name } }; @@ -3502,6 +3297,14 @@ bake_test_case SerializeToJson_testcases[] = { "struct_entity", SerializeToJson_struct_entity }, + { + "struct_entity_0", + SerializeToJson_struct_entity_0 + }, + { + "struct_entity_10k", + SerializeToJson_struct_entity_10k + }, { "struct_entity_after_float", SerializeToJson_struct_entity_after_float @@ -3605,6 +3408,10 @@ bake_test_case SerializeToJson_testcases[] = { { "vector_array_i32_3", SerializeToJson_vector_array_i32_3 + }, + { + "serialize_from_stage", + SerializeToJson_serialize_from_stage } }; @@ -3634,13 +3441,45 @@ bake_test_case SerializeEntityToJson_testcases[] = { SerializeEntityToJson_serialize_w_base }, { - "serialize_w_base_override", - SerializeEntityToJson_serialize_w_base_override + "serialize_w_base_dont_inherit_tag", + SerializeEntityToJson_serialize_w_base_dont_inherit_tag + }, + { + "serialize_w_base_dont_inherit_component", + SerializeEntityToJson_serialize_w_base_dont_inherit_component + }, + { + "serialize_w_base_dont_inherit_pair", + SerializeEntityToJson_serialize_w_base_dont_inherit_pair }, { "serialize_w_2_base", SerializeEntityToJson_serialize_w_2_base }, + { + "serialize_component_w_base", + SerializeEntityToJson_serialize_component_w_base + }, + { + "serialize_component_w_base_no_reflection_data", + SerializeEntityToJson_serialize_component_w_base_no_reflection_data + }, + { + "serialize_component_w_base_w_owned", + SerializeEntityToJson_serialize_component_w_base_w_owned + }, + { + "serialize_component_w_base_w_owned_no_reflection_data", + SerializeEntityToJson_serialize_component_w_base_w_owned_no_reflection_data + }, + { + "serialize_component_w_base_w_owned_override", + SerializeEntityToJson_serialize_component_w_base_w_owned_override + }, + { + "serialize_component_w_base_w_owned_no_reflection_data_override", + SerializeEntityToJson_serialize_component_w_base_w_owned_no_reflection_data_override + }, { "serialize_w_nested_base", SerializeEntityToJson_serialize_w_nested_base @@ -3686,12 +3525,12 @@ bake_test_case SerializeEntityToJson_testcases[] = { SerializeEntityToJson_serialize_w_type_info_unit_over }, { - "serialize_wo_private", - SerializeEntityToJson_serialize_wo_private + "serialize_w_type_info_no_types", + SerializeEntityToJson_serialize_w_type_info_no_types }, { - "serialize_w_private", - SerializeEntityToJson_serialize_w_private + "serialize_w_type_info_no_components", + SerializeEntityToJson_serialize_w_type_info_no_components }, { "serialize_w_label", @@ -3701,10 +3540,6 @@ bake_test_case SerializeEntityToJson_testcases[] = { "serialize_w_label_no_name", SerializeEntityToJson_serialize_w_label_no_name }, - { - "serialize_w_id_labels", - SerializeEntityToJson_serialize_w_id_labels - }, { "serialize_w_brief", SerializeEntityToJson_serialize_w_brief @@ -3718,100 +3553,160 @@ bake_test_case SerializeEntityToJson_testcases[] = { SerializeEntityToJson_serialize_w_link }, { - "serialize_w_link_no_link", - SerializeEntityToJson_serialize_w_link_no_link + "serialize_w_link_no_link", + SerializeEntityToJson_serialize_w_link_no_link + }, + { + "serialize_color", + SerializeEntityToJson_serialize_color + }, + { + "serialize_w_doc_w_quotes", + SerializeEntityToJson_serialize_w_doc_w_quotes + }, + { + "serialize_from_core", + SerializeEntityToJson_serialize_from_core + }, + { + "serialize_w_1_alert", + SerializeEntityToJson_serialize_w_1_alert + }, + { + "serialize_w_2_alerts", + SerializeEntityToJson_serialize_w_2_alerts + }, + { + "serialize_w_child_alerts", + SerializeEntityToJson_serialize_w_child_alerts + }, + { + "serialize_w_severity_filter_alert", + SerializeEntityToJson_serialize_w_severity_filter_alert + }, + { + "serialize_w_alerts_not_imported", + SerializeEntityToJson_serialize_w_alerts_not_imported + }, + { + "serialize_w_alerts_no_message", + SerializeEntityToJson_serialize_w_alerts_no_message + }, + { + "serialize_refs_childof", + SerializeEntityToJson_serialize_refs_childof + }, + { + "serialize_refs_custom", + SerializeEntityToJson_serialize_refs_custom + }, + { + "serialize_refs_wildcard", + SerializeEntityToJson_serialize_refs_wildcard + }, + { + "serialize_matches_filter", + SerializeEntityToJson_serialize_matches_filter + }, + { + "serialize_matches_query", + SerializeEntityToJson_serialize_matches_query + }, + { + "serialize_matches_rule", + SerializeEntityToJson_serialize_matches_rule }, { - "serialize_color", - SerializeEntityToJson_serialize_color + "serialize_no_matches", + SerializeEntityToJson_serialize_no_matches }, { - "serialize_w_doc_w_quotes", - SerializeEntityToJson_serialize_w_doc_w_quotes + "serialize_id_recycled", + SerializeEntityToJson_serialize_id_recycled }, { - "serialize_union_relationship", - SerializeEntityToJson_serialize_union_relationship + "serialize_union_target", + SerializeEntityToJson_serialize_union_target }, { - "serialize_union_relationship_w_labels", - SerializeEntityToJson_serialize_union_relationship_w_labels + "serialize_union_target_recycled", + SerializeEntityToJson_serialize_union_target_recycled }, { - "serialize_union_relationship_invalid_entity", - SerializeEntityToJson_serialize_union_relationship_invalid_entity + "serialize_anonymous_w_builtin", + SerializeEntityToJson_serialize_anonymous_w_builtin }, { - "serialize_union_relationship_invalid_entity_w_labels", - SerializeEntityToJson_serialize_union_relationship_invalid_entity_w_labels + "serialize_named_w_builtin", + SerializeEntityToJson_serialize_named_w_builtin }, { - "serialize_w_union_property", - SerializeEntityToJson_serialize_w_union_property + "serialize_named_child_w_builtin", + SerializeEntityToJson_serialize_named_child_w_builtin }, { - "serialize_w_union_property", - SerializeEntityToJson_serialize_w_union_property + "serialize_named_child_w_builtin_w_type_info", + SerializeEntityToJson_serialize_named_child_w_builtin_w_type_info }, { - "serialize_from_core", - SerializeEntityToJson_serialize_from_core + "serialize_from_stage", + SerializeEntityToJson_serialize_from_stage }, { - "serialize_w_1_alert", - SerializeEntityToJson_serialize_w_1_alert + "serialize_sparse", + SerializeEntityToJson_serialize_sparse }, { - "serialize_w_2_alerts", - SerializeEntityToJson_serialize_w_2_alerts + "serialize_sparse_pair", + SerializeEntityToJson_serialize_sparse_pair }, { - "serialize_w_child_alerts", - SerializeEntityToJson_serialize_w_child_alerts + "serialize_sparse_mixed", + SerializeEntityToJson_serialize_sparse_mixed }, { - "serialize_w_severity_filter_alert", - SerializeEntityToJson_serialize_w_severity_filter_alert + "serialize_sparse_inherited", + SerializeEntityToJson_serialize_sparse_inherited }, { - "serialize_w_alerts_not_imported", - SerializeEntityToJson_serialize_w_alerts_not_imported + "serialize_sparse_inherited_pair", + SerializeEntityToJson_serialize_sparse_inherited_pair }, { - "serialize_w_alerts_no_message", - SerializeEntityToJson_serialize_w_alerts_no_message + "serialize_sparse_inherited_mixed", + SerializeEntityToJson_serialize_sparse_inherited_mixed }, { - "serialize_refs_childof", - SerializeEntityToJson_serialize_refs_childof + "serialize_sparse_w_type_info", + SerializeEntityToJson_serialize_sparse_w_type_info }, { - "serialize_refs_custom", - SerializeEntityToJson_serialize_refs_custom + "serialize_auto_override_w_inherited", + SerializeEntityToJson_serialize_auto_override_w_inherited }, { - "serialize_refs_wildcard", - SerializeEntityToJson_serialize_refs_wildcard + "serialize_auto_override", + SerializeEntityToJson_serialize_auto_override }, { - "serialize_no_ids", - SerializeEntityToJson_serialize_no_ids + "serialize_auto_override_pair", + SerializeEntityToJson_serialize_auto_override_pair }, { - "serialize_matches_filter", - SerializeEntityToJson_serialize_matches_filter + "serialize_auto_override_fullpath", + SerializeEntityToJson_serialize_auto_override_fullpath }, { - "serialize_matches_query", - SerializeEntityToJson_serialize_matches_query + "serialize_auto_override_pair_fullpath", + SerializeEntityToJson_serialize_auto_override_pair_fullpath }, { - "serialize_matches_rule", - SerializeEntityToJson_serialize_matches_rule + "serialize_toggle", + SerializeEntityToJson_serialize_toggle }, { - "serialize_no_matches", - SerializeEntityToJson_serialize_no_matches + "serialize_toggle_pair", + SerializeEntityToJson_serialize_toggle_pair } }; @@ -3900,6 +3795,14 @@ bake_test_case SerializeIterToJson_testcases[] = { "serialize_w_entity_label", SerializeIterToJson_serialize_w_entity_label }, + { + "serialize_w_entity_label_w_str", + SerializeIterToJson_serialize_w_entity_label_w_str + }, + { + "serialize_entity_label_w_newline", + SerializeIterToJson_serialize_entity_label_w_newline + }, { "serialize_w_var_labels", SerializeIterToJson_serialize_w_var_labels @@ -3961,12 +3864,32 @@ bake_test_case SerializeIterToJson_testcases[] = { SerializeIterToJson_serialize_ids_2_entities }, { - "serialize_variable_ids", - SerializeIterToJson_serialize_variable_ids + "serialize_anonymous", + SerializeIterToJson_serialize_anonymous + }, + { + "serialize_anonymous_ids", + SerializeIterToJson_serialize_anonymous_ids + }, + { + "serialize_variable_anonymous_no_full_path", + SerializeIterToJson_serialize_variable_anonymous_no_full_path + }, + { + "serialize_variable_anonymous", + SerializeIterToJson_serialize_variable_anonymous + }, + { + "serialize_anonymous_tag", + SerializeIterToJson_serialize_anonymous_tag + }, + { + "serialize_anonymous_component", + SerializeIterToJson_serialize_anonymous_component }, { - "serialize_variable_ids_2_entities", - SerializeIterToJson_serialize_variable_ids_2_entities + "serialize_anonymous_pair", + SerializeIterToJson_serialize_anonymous_pair }, { "serialize_invalid_value", @@ -4009,888 +3932,696 @@ bake_test_case SerializeIterToJson_testcases[] = { SerializeIterToJson_serialize_labels_w_offset }, { - "serialize_colors_w_offset", - SerializeIterToJson_serialize_colors_w_offset - }, - { - "serialize_anonymous_entities_w_offset", - SerializeIterToJson_serialize_anonymous_entities_w_offset - }, - { - "serialize_table", - SerializeIterToJson_serialize_table - }, - { - "serialize_table_w_id_labels", - SerializeIterToJson_serialize_table_w_id_labels - }, - { - "serialize_table_w_var_labels", - SerializeIterToJson_serialize_table_w_var_labels - }, - { - "serialize_table_w_private", - SerializeIterToJson_serialize_table_w_private - }, - { - "serialize_world", - SerializeIterToJson_serialize_world - }, - { - "serialize_term_labels", - SerializeIterToJson_serialize_term_labels - }, - { - "serialize_id_labels", - SerializeIterToJson_serialize_id_labels - }, - { - "serialize_vars_for_query", - SerializeIterToJson_serialize_vars_for_query - }, - { - "serialize_var_labels_for_query", - SerializeIterToJson_serialize_var_labels_for_query - }, - { - "serialize_var_ids_for_query", - SerializeIterToJson_serialize_var_ids_for_query - }, - { - "serialize_null_doc_name", - SerializeIterToJson_serialize_null_doc_name - }, - { - "serialize_rule_w_optional", - SerializeIterToJson_serialize_rule_w_optional - }, - { - "serialize_rule_w_optional_component", - SerializeIterToJson_serialize_rule_w_optional_component - } -}; - -bake_test_case SerializeTypeInfoToJson_testcases[] = { - { - "bool", - SerializeTypeInfoToJson_bool - }, - { - "byte", - SerializeTypeInfoToJson_byte - }, - { - "char", - SerializeTypeInfoToJson_char - }, - { - "i8", - SerializeTypeInfoToJson_i8 - }, - { - "i16", - SerializeTypeInfoToJson_i16 - }, - { - "i32", - SerializeTypeInfoToJson_i32 - }, - { - "i64", - SerializeTypeInfoToJson_i64 - }, - { - "iptr", - SerializeTypeInfoToJson_iptr - }, - { - "u8", - SerializeTypeInfoToJson_u8 - }, - { - "u16", - SerializeTypeInfoToJson_u16 - }, - { - "u32", - SerializeTypeInfoToJson_u32 - }, - { - "u64", - SerializeTypeInfoToJson_u64 - }, - { - "uptr", - SerializeTypeInfoToJson_uptr - }, - { - "float", - SerializeTypeInfoToJson_float - }, - { - "double", - SerializeTypeInfoToJson_double - }, - { - "string", - SerializeTypeInfoToJson_string - }, - { - "entity", - SerializeTypeInfoToJson_entity - }, - { - "id", - SerializeTypeInfoToJson_id - }, - { - "enum", - SerializeTypeInfoToJson_enum - }, - { - "bitmask", - SerializeTypeInfoToJson_bitmask - }, - { - "struct", - SerializeTypeInfoToJson_struct - }, - { - "nested_struct", - SerializeTypeInfoToJson_nested_struct - }, - { - "array_type", - SerializeTypeInfoToJson_array_type - }, - { - "vector_type", - SerializeTypeInfoToJson_vector_type - }, - { - "struct_array_i32_2", - SerializeTypeInfoToJson_struct_array_i32_2 - }, - { - "struct_array_struct_2", - SerializeTypeInfoToJson_struct_array_struct_2 - }, - { - "struct_array_type", - SerializeTypeInfoToJson_struct_array_type - }, - { - "struct_vector_type", - SerializeTypeInfoToJson_struct_vector_type - }, - { - "custom_primitive_type", - SerializeTypeInfoToJson_custom_primitive_type - }, - { - "custom_array_type", - SerializeTypeInfoToJson_custom_array_type - }, - { - "custom_vector_type", - SerializeTypeInfoToJson_custom_vector_type - }, - { - "custom_struct_type", - SerializeTypeInfoToJson_custom_struct_type - }, - { - "struct_w_value_range", - SerializeTypeInfoToJson_struct_w_value_range - }, - { - "struct_w_error_range", - SerializeTypeInfoToJson_struct_w_error_range - }, - { - "struct_w_warning_range", - SerializeTypeInfoToJson_struct_w_warning_range - }, - { - "struct_w_error_and_warning_range", - SerializeTypeInfoToJson_struct_w_error_and_warning_range - }, - { - "struct_nested", - SerializeTypeInfoToJson_struct_nested - }, - { - "struct_nested_2_lvls", - SerializeTypeInfoToJson_struct_nested_2_lvls + "serialize_colors_w_offset", + SerializeIterToJson_serialize_colors_w_offset }, { - "struct_nested_2_members", - SerializeTypeInfoToJson_struct_nested_2_members + "serialize_anonymous_entities_w_offset", + SerializeIterToJson_serialize_anonymous_entities_w_offset }, { - "struct_nested_3_members", - SerializeTypeInfoToJson_struct_nested_3_members - } -}; - -bake_test_case MetaUtils_testcases[] = { + "serialize_table", + SerializeIterToJson_serialize_table + }, { - "struct_w_2_i32", - MetaUtils_struct_w_2_i32 + "serialize_table_w_id_labels", + SerializeIterToJson_serialize_table_w_id_labels }, { - "struct_w_2_bool", - MetaUtils_struct_w_2_bool + "serialize_table_w_var_labels", + SerializeIterToJson_serialize_table_w_var_labels }, { - "struct_w_2_char", - MetaUtils_struct_w_2_char + "serialize_world", + SerializeIterToJson_serialize_world }, { - "struct_w_2_string", - MetaUtils_struct_w_2_string + "serialize_vars_for_query", + SerializeIterToJson_serialize_vars_for_query }, { - "struct_w_2_f32", - MetaUtils_struct_w_2_f32 + "serialize_var_labels_for_query", + SerializeIterToJson_serialize_var_labels_for_query }, { - "struct_w_2_f64", - MetaUtils_struct_w_2_f64 + "serialize_null_doc_name", + SerializeIterToJson_serialize_null_doc_name }, { - "struct_w_3_enum", - MetaUtils_struct_w_3_enum + "serialize_rule_w_optional", + SerializeIterToJson_serialize_rule_w_optional }, { - "struct_w_3_enum_multiline", - MetaUtils_struct_w_3_enum_multiline + "serialize_rule_w_optional_component", + SerializeIterToJson_serialize_rule_w_optional_component }, { - "struct_w_3_enum_w_assignment", - MetaUtils_struct_w_3_enum_w_assignment + "serialize_entity_w_flecs_core_parent", + SerializeIterToJson_serialize_entity_w_flecs_core_parent }, { - "struct_w_4_bitmask", - MetaUtils_struct_w_4_bitmask + "no_fields", + SerializeIterToJson_no_fields }, { - "struct_w_3_entities", - MetaUtils_struct_w_3_entities + "no_fields_w_vars", + SerializeIterToJson_no_fields_w_vars }, { - "struct_w_2_array_3_i32", - MetaUtils_struct_w_2_array_3_i32 + "serialize_from_stage", + SerializeIterToJson_serialize_from_stage }, { - "struct_w_nested", - MetaUtils_struct_w_nested + "serialize_sparse", + SerializeIterToJson_serialize_sparse }, { - "struct_w_2_nested", - MetaUtils_struct_w_2_nested + "serialize_15_fields", + SerializeIterToJson_serialize_15_fields }, { - "enum_nospace", - MetaUtils_enum_nospace + "serialize_16_fields", + SerializeIterToJson_serialize_16_fields }, { - "struct_nospace", - MetaUtils_struct_nospace + "serialize_31_fields", + SerializeIterToJson_serialize_31_fields }, { - "identifier_w_underscore", - MetaUtils_identifier_w_underscore + "serialize_32_fields", + SerializeIterToJson_serialize_32_fields }, { - "struct_w_ptr", - MetaUtils_struct_w_ptr + "serialize_field_w_escaped_sep", + SerializeIterToJson_serialize_field_w_escaped_sep + } +}; + +bake_test_case SerializeIterToRowJson_testcases[] = { + { + "serialize_this_w_1_tag", + SerializeIterToRowJson_serialize_this_w_1_tag }, { - "private_members", - MetaUtils_private_members + "serialize_this_w_1_tag_w_parent", + SerializeIterToRowJson_serialize_this_w_1_tag_w_parent }, { - "enum_constant_w_name_prefix", - MetaUtils_enum_constant_w_name_prefix + "serialize_this_w_1_tag_no_name", + SerializeIterToRowJson_serialize_this_w_1_tag_no_name }, { - "enum_constant_w_type_prefix", - MetaUtils_enum_constant_w_type_prefix + "serialize_this_w_2_tag", + SerializeIterToRowJson_serialize_this_w_2_tag }, { - "enum_constant_w_name_type_prefix", - MetaUtils_enum_constant_w_name_type_prefix - } -}; - -bake_test_case Vars_testcases[] = { + "serialize_this_w_1_component", + SerializeIterToRowJson_serialize_this_w_1_component + }, { - "declare_1_var", - Vars_declare_1_var + "serialize_this_w_2_component", + SerializeIterToRowJson_serialize_this_w_2_component }, { - "declare_2_vars", - Vars_declare_2_vars + "serialize_this_w_2_component_1_shared", + SerializeIterToRowJson_serialize_this_w_2_component_1_shared }, { - "declare_vars_nested_scope", - Vars_declare_vars_nested_scope + "serialize_this_w_1_pair", + SerializeIterToRowJson_serialize_this_w_1_pair }, { - "declare_vars_2_scopes", - Vars_declare_vars_2_scopes + "serialize_this_w_2_pair", + SerializeIterToRowJson_serialize_this_w_2_pair }, { - "redeclare_var", - Vars_redeclare_var + "serialize_this_w_1_pair_component", + SerializeIterToRowJson_serialize_this_w_1_pair_component }, { - "i32_expr_w_i32_var", - Vars_i32_expr_w_i32_var + "serialize_this_w_1_var", + SerializeIterToRowJson_serialize_this_w_1_var }, { - "i32_expr_w_f32_var", - Vars_i32_expr_w_f32_var + "serialize_this_w_2_var", + SerializeIterToRowJson_serialize_this_w_2_var }, { - "i32_expr_w_string_var", - Vars_i32_expr_w_string_var + "serialize_this_w_2_var_doc_name", + SerializeIterToRowJson_serialize_this_w_2_var_doc_name }, { - "string_expr_w_string_var", - Vars_string_expr_w_string_var + "serialize_this_w_1_tag_component_pair_var", + SerializeIterToRowJson_serialize_this_w_1_tag_component_pair_var }, { - "struct_expr_w_i32_vars", - Vars_struct_expr_w_i32_vars + "serialize_this_w_2_tag_component_pair_var", + SerializeIterToRowJson_serialize_this_w_2_tag_component_pair_var }, { - "struct_expr_w_struct_var", - Vars_struct_expr_w_struct_var + "serialize_var_w_1_tag", + SerializeIterToRowJson_serialize_var_w_1_tag }, { - "nested_struct_expr_w_struct_var", - Vars_nested_struct_expr_w_struct_var + "serialize_var_w_1_component", + SerializeIterToRowJson_serialize_var_w_1_component }, { - "declare_w_value", - Vars_declare_w_value + "serialize_var_w_1_pair", + SerializeIterToRowJson_serialize_var_w_1_pair }, { - "redeclare_in_scope", - Vars_redeclare_in_scope + "serialize_var_w_1_var", + SerializeIterToRowJson_serialize_var_w_1_var }, { - "init_fini_vars", - Vars_init_fini_vars - } -}; - -bake_test_case DeserExprOperators_testcases[] = { + "serialize_var_w_2_component_1_shared", + SerializeIterToRowJson_serialize_var_w_2_component_1_shared + }, { - "add_2_int_literals", - DeserExprOperators_add_2_int_literals + "serialize_var_w_1_tag_component_pair_var", + SerializeIterToRowJson_serialize_var_w_1_tag_component_pair_var }, { - "add_2_int_literals_twice", - DeserExprOperators_add_2_int_literals_twice + "serialize_var_w_2_tag_component_pair_var", + SerializeIterToRowJson_serialize_var_w_2_tag_component_pair_var }, { - "sub_2_int_literals", - DeserExprOperators_sub_2_int_literals + "serialize_fixed_w_1_tag", + SerializeIterToRowJson_serialize_fixed_w_1_tag }, { - "mul_2_int_literals", - DeserExprOperators_mul_2_int_literals + "serialize_fixed_w_1_component", + SerializeIterToRowJson_serialize_fixed_w_1_component }, { - "div_2_int_literals", - DeserExprOperators_div_2_int_literals + "serialize_fixed_w_1_pair", + SerializeIterToRowJson_serialize_fixed_w_1_pair }, { - "add_3_int_literals", - DeserExprOperators_add_3_int_literals + "serialize_fixed_w_1_var", + SerializeIterToRowJson_serialize_fixed_w_1_var }, { - "add_3_int_literals_twice", - DeserExprOperators_add_3_int_literals_twice + "serialize_fixed_w_2_component_1_shared", + SerializeIterToRowJson_serialize_fixed_w_2_component_1_shared }, { - "sub_3_int_literals", - DeserExprOperators_sub_3_int_literals + "serialize_fixed_w_1_tag_component_pair_var", + SerializeIterToRowJson_serialize_fixed_w_1_tag_component_pair_var }, { - "mul_3_int_literals", - DeserExprOperators_mul_3_int_literals + "serialize_fixed_w_2_tag_component_pair_var", + SerializeIterToRowJson_serialize_fixed_w_2_tag_component_pair_var }, { - "div_3_int_literals", - DeserExprOperators_div_3_int_literals + "serialize_not", + SerializeIterToRowJson_serialize_not }, { - "int_to_bool", - DeserExprOperators_int_to_bool + "serialize_not_pair_wildcard", + SerializeIterToRowJson_serialize_not_pair_wildcard }, { - "bool_to_int", - DeserExprOperators_bool_to_int + "serialize_not_pair_var", + SerializeIterToRowJson_serialize_not_pair_var }, { - "bool_to_uint", - DeserExprOperators_bool_to_uint + "serialize_not_pair_var_constrained", + SerializeIterToRowJson_serialize_not_pair_var_constrained }, { - "add_mul_3_int_literals", - DeserExprOperators_add_mul_3_int_literals + "serialize_optional", + SerializeIterToRowJson_serialize_optional }, { - "sub_mul_3_int_literals", - DeserExprOperators_sub_mul_3_int_literals + "serialize_optional_pair_wildcard", + SerializeIterToRowJson_serialize_optional_pair_wildcard }, { - "div_mul_3_int_literals", - DeserExprOperators_div_mul_3_int_literals + "serialize_optional_pair_var", + SerializeIterToRowJson_serialize_optional_pair_var }, { - "add_div_3_int_literals", - DeserExprOperators_add_div_3_int_literals + "serialize_optional_pair_var_constrained", + SerializeIterToRowJson_serialize_optional_pair_var_constrained }, { - "sub_div_3_int_literals", - DeserExprOperators_sub_div_3_int_literals + "serialize_or", + SerializeIterToRowJson_serialize_or }, { - "mul_div_3_int_literals", - DeserExprOperators_mul_div_3_int_literals + "serialize_scope", + SerializeIterToRowJson_serialize_scope }, { - "mul_add_mul_add_int_literals", - DeserExprOperators_mul_add_mul_add_int_literals + "serialize_eq", + SerializeIterToRowJson_serialize_eq }, { - "mul_sub_mul_sub_int_literals", - DeserExprOperators_mul_sub_mul_sub_int_literals + "serialize_neq", + SerializeIterToRowJson_serialize_neq }, { - "mul_div_mul_div_int_literals", - DeserExprOperators_mul_div_mul_div_int_literals + "serialize_eq_m", + SerializeIterToRowJson_serialize_eq_m }, { - "div_add_div_add_int_literals", - DeserExprOperators_div_add_div_add_int_literals + "serialize_table", + SerializeIterToRowJson_serialize_table }, { - "div_sub_div_sub_int_literals", - DeserExprOperators_div_sub_div_sub_int_literals + "serialize_table_w_eq", + SerializeIterToRowJson_serialize_table_w_eq }, { - "div_sub_div_mul_int_literals", - DeserExprOperators_div_sub_div_mul_int_literals + "serialize_table_w_neq", + SerializeIterToRowJson_serialize_table_w_neq }, { - "div_mul_div_mul_int_literals", - DeserExprOperators_div_mul_div_mul_int_literals + "serialize_table_w_2_pair_targets", + SerializeIterToRowJson_serialize_table_w_2_pair_targets }, { - "add_2_flt_literals", - DeserExprOperators_add_2_flt_literals + "serialize_table_w_2_pair_targets_2_rel", + SerializeIterToRowJson_serialize_table_w_2_pair_targets_2_rel }, { - "sub_2_flt_literals", - DeserExprOperators_sub_2_flt_literals + "serialize_table_w_3_pair_targets", + SerializeIterToRowJson_serialize_table_w_3_pair_targets }, { - "mul_2_flt_literals", - DeserExprOperators_mul_2_flt_literals + "serialize_table_w_3_pair_targets_2_rel", + SerializeIterToRowJson_serialize_table_w_3_pair_targets_2_rel }, { - "div_2_flt_literals", - DeserExprOperators_div_2_flt_literals + "serialize_everything", + SerializeIterToRowJson_serialize_everything }, { - "add_2_int_neg_literals", - DeserExprOperators_add_2_int_neg_literals + "serialize_everything_table", + SerializeIterToRowJson_serialize_everything_table }, { - "sub_2_int_neg_literals", - DeserExprOperators_sub_2_int_neg_literals + "serialize_w_type_info", + SerializeIterToRowJson_serialize_w_type_info }, { - "mul_2_int_neg_literals", - DeserExprOperators_mul_2_int_neg_literals + "serialize_w_field_info", + SerializeIterToRowJson_serialize_w_field_info }, { - "div_2_int_neg_literals", - DeserExprOperators_div_2_int_neg_literals + "serialize_w_field_info_pair_w_0_target", + SerializeIterToRowJson_serialize_w_field_info_pair_w_0_target }, { - "mul_lparen_add_add_rparen_int_literals", - DeserExprOperators_mul_lparen_add_add_rparen_int_literals + "serialize_w_field_info_pair_w_not_tag", + SerializeIterToRowJson_serialize_w_field_info_pair_w_not_tag }, { - "mul_lparen_add_add_add_rparen_int_literals", - DeserExprOperators_mul_lparen_add_add_add_rparen_int_literals + "serialize_w_field_info_pair_w_not_pair", + SerializeIterToRowJson_serialize_w_field_info_pair_w_not_pair }, { - "mul_lparen_add_add_rparen_add_int_literals", - DeserExprOperators_mul_lparen_add_add_rparen_add_int_literals + "serialize_w_field_info_pair_w_not_component", + SerializeIterToRowJson_serialize_w_field_info_pair_w_not_component }, { - "lparen_add_add_rparen_mul_int_literals", - DeserExprOperators_lparen_add_add_rparen_mul_int_literals + "serialize_w_field_info_w_or", + SerializeIterToRowJson_serialize_w_field_info_w_or }, { - "lparen_add_add_add_rparen_mul_int_literals", - DeserExprOperators_lparen_add_add_add_rparen_mul_int_literals + "serialize_recycled_id", + SerializeIterToRowJson_serialize_recycled_id }, { - "double_paren_add_add", - DeserExprOperators_double_paren_add_add + "serialize_entity_w_flecs_core_parent", + SerializeIterToRowJson_serialize_entity_w_flecs_core_parent + } +}; + +bake_test_case SerializeTypeInfoToJson_testcases[] = { + { + "bool", + SerializeTypeInfoToJson_bool }, { - "double_paren_literal", - DeserExprOperators_double_paren_literal + "byte", + SerializeTypeInfoToJson_byte }, { - "lparen_add_add_rparen_mul_lparen_add_add_rparen", - DeserExprOperators_lparen_add_add_rparen_mul_lparen_add_add_rparen + "char", + SerializeTypeInfoToJson_char }, { - "float_result_add_2_int_literals", - DeserExprOperators_float_result_add_2_int_literals + "i8", + SerializeTypeInfoToJson_i8 }, { - "struct_result_add_2_int_literals", - DeserExprOperators_struct_result_add_2_int_literals + "i16", + SerializeTypeInfoToJson_i16 }, { - "struct_result_add_2_2_fields_int_literals", - DeserExprOperators_struct_result_add_2_2_fields_int_literals + "i32", + SerializeTypeInfoToJson_i32 }, { - "struct_result_add_3_int_literals", - DeserExprOperators_struct_result_add_3_int_literals + "i64", + SerializeTypeInfoToJson_i64 }, { - "struct_result_lparen_int_rparen", - DeserExprOperators_struct_result_lparen_int_rparen + "iptr", + SerializeTypeInfoToJson_iptr }, { - "add_to_var", - DeserExprOperators_add_to_var + "u8", + SerializeTypeInfoToJson_u8 }, { - "add_var_to", - DeserExprOperators_add_var_to + "u16", + SerializeTypeInfoToJson_u16 }, { - "var_member", - DeserExprOperators_var_member + "u32", + SerializeTypeInfoToJson_u32 }, { - "bool_cond_and_bool", - DeserExprOperators_bool_cond_and_bool + "u64", + SerializeTypeInfoToJson_u64 }, { - "bool_cond_or_bool", - DeserExprOperators_bool_cond_or_bool + "uptr", + SerializeTypeInfoToJson_uptr }, { - "int_cond_and_int", - DeserExprOperators_int_cond_and_int + "float", + SerializeTypeInfoToJson_float }, { - "int_cond_or_int", - DeserExprOperators_int_cond_or_int + "double", + SerializeTypeInfoToJson_double }, { - "bool_cond_and_int", - DeserExprOperators_bool_cond_and_int + "string", + SerializeTypeInfoToJson_string }, { - "int_cond_and_bool", - DeserExprOperators_int_cond_and_bool + "entity", + SerializeTypeInfoToJson_entity }, { - "bool_cond_or_int", - DeserExprOperators_bool_cond_or_int + "id", + SerializeTypeInfoToJson_id }, { - "int_cond_or_bool", - DeserExprOperators_int_cond_or_bool + "enum", + SerializeTypeInfoToJson_enum }, { - "cond_eq_bool", - DeserExprOperators_cond_eq_bool + "bitmask", + SerializeTypeInfoToJson_bitmask }, { - "cond_eq_int", - DeserExprOperators_cond_eq_int + "struct", + SerializeTypeInfoToJson_struct }, { - "cond_neq_bool", - DeserExprOperators_cond_neq_bool + "nested_struct", + SerializeTypeInfoToJson_nested_struct }, { - "cond_neq_int", - DeserExprOperators_cond_neq_int + "array_type", + SerializeTypeInfoToJson_array_type }, { - "cond_eq_bool_int", - DeserExprOperators_cond_eq_bool_int + "vector_type", + SerializeTypeInfoToJson_vector_type }, { - "cond_eq_int_flt", - DeserExprOperators_cond_eq_int_flt + "struct_array_i32_2", + SerializeTypeInfoToJson_struct_array_i32_2 }, { - "cond_eq_cond_and", - DeserExprOperators_cond_eq_cond_and + "struct_array_struct_2", + SerializeTypeInfoToJson_struct_array_struct_2 }, { - "cond_eq_cond_or", - DeserExprOperators_cond_eq_cond_or + "struct_array_type", + SerializeTypeInfoToJson_struct_array_type }, { - "cond_gt_bool", - DeserExprOperators_cond_gt_bool + "struct_vector_type", + SerializeTypeInfoToJson_struct_vector_type }, { - "cond_gt_int", - DeserExprOperators_cond_gt_int + "custom_primitive_type", + SerializeTypeInfoToJson_custom_primitive_type }, { - "cond_gt_flt", - DeserExprOperators_cond_gt_flt + "custom_array_type", + SerializeTypeInfoToJson_custom_array_type }, { - "cond_gteq_bool", - DeserExprOperators_cond_gteq_bool + "custom_vector_type", + SerializeTypeInfoToJson_custom_vector_type }, { - "cond_gteq_int", - DeserExprOperators_cond_gteq_int + "custom_struct_type", + SerializeTypeInfoToJson_custom_struct_type }, { - "cond_gteq_flt", - DeserExprOperators_cond_gteq_flt + "struct_w_value_range", + SerializeTypeInfoToJson_struct_w_value_range }, { - "cond_lt_bool", - DeserExprOperators_cond_lt_bool + "struct_w_error_range", + SerializeTypeInfoToJson_struct_w_error_range }, { - "cond_lt_int", - DeserExprOperators_cond_lt_int + "struct_w_warning_range", + SerializeTypeInfoToJson_struct_w_warning_range }, { - "cond_lt_flt", - DeserExprOperators_cond_lt_flt + "struct_w_error_and_warning_range", + SerializeTypeInfoToJson_struct_w_error_and_warning_range }, { - "cond_lteq_bool", - DeserExprOperators_cond_lteq_bool + "struct_nested", + SerializeTypeInfoToJson_struct_nested }, { - "cond_lteq_int", - DeserExprOperators_cond_lteq_int + "struct_nested_2_lvls", + SerializeTypeInfoToJson_struct_nested_2_lvls }, { - "cond_lteq_flt", - DeserExprOperators_cond_lteq_flt + "struct_nested_2_members", + SerializeTypeInfoToJson_struct_nested_2_members }, { - "min_lparen_int_rparen", - DeserExprOperators_min_lparen_int_rparen + "struct_nested_3_members", + SerializeTypeInfoToJson_struct_nested_3_members + } +}; + +bake_test_case SerializeQueryInfoToJson_testcases[] = { + { + "1_tag", + SerializeQueryInfoToJson_1_tag }, { - "min_lparen_int_add_int_rparen", - DeserExprOperators_min_lparen_int_add_int_rparen + "1_component", + SerializeQueryInfoToJson_1_component }, { - "min_var", - DeserExprOperators_min_var + "1_pair", + SerializeQueryInfoToJson_1_pair }, { - "min_lparen_int_rparen_to_i64", - DeserExprOperators_min_lparen_int_rparen_to_i64 + "1_pair_w_wildcard", + SerializeQueryInfoToJson_1_pair_w_wildcard }, { - "min_lparen_int_rparen_to_i32", - DeserExprOperators_min_lparen_int_rparen_to_i32 + "1_pair_w_any", + SerializeQueryInfoToJson_1_pair_w_any }, { - "struct_w_min_var", - DeserExprOperators_struct_w_min_var + "1_tag_fixed_src", + SerializeQueryInfoToJson_1_tag_fixed_src }, { - "struct_w_min_lparen_int_rparen", - DeserExprOperators_struct_w_min_lparen_int_rparen + "1_tag_var_src", + SerializeQueryInfoToJson_1_tag_var_src }, { - "struct_w_min_lparen_var_rparen", - DeserExprOperators_struct_w_min_lparen_var_rparen + "1_component_in", + SerializeQueryInfoToJson_1_component_in }, { - "shift_left_int", - DeserExprOperators_shift_left_int + "1_component_inout", + SerializeQueryInfoToJson_1_component_inout }, { - "shift_right_int", - DeserExprOperators_shift_right_int + "1_component_out", + SerializeQueryInfoToJson_1_component_out }, { - "shift_left_int_add_int", - DeserExprOperators_shift_left_int_add_int + "1_component_none", + SerializeQueryInfoToJson_1_component_none }, { - "shift_left_int_mul_int", - DeserExprOperators_shift_left_int_mul_int + "1_tag_not", + SerializeQueryInfoToJson_1_tag_not }, { - "add_int_shift_left_int", - DeserExprOperators_add_int_shift_left_int + "2_tags_or", + SerializeQueryInfoToJson_2_tags_or }, { - "mul_int_shift_left_int", - DeserExprOperators_mul_int_shift_left_int + "1_tag_optional", + SerializeQueryInfoToJson_1_tag_optional }, { - "add_int_shift_left_int_add_int", - DeserExprOperators_add_int_shift_left_int_add_int + "1_tag_self", + SerializeQueryInfoToJson_1_tag_self }, { - "mul_int_shift_left_int_mul_int", - DeserExprOperators_mul_int_shift_left_int_mul_int + "1_tag_self_dont_inherit", + SerializeQueryInfoToJson_1_tag_self_dont_inherit }, { - "entity_expr", - DeserExprOperators_entity_expr + "1_tag_up", + SerializeQueryInfoToJson_1_tag_up }, { - "entity_path_expr", - DeserExprOperators_entity_path_expr + "1_tag_cascade", + SerializeQueryInfoToJson_1_tag_cascade }, { - "entity_parent_func", - DeserExprOperators_entity_parent_func + "0_term", + SerializeQueryInfoToJson_0_term }, { - "entity_name_func", - DeserExprOperators_entity_name_func + "anonymous_tag", + SerializeQueryInfoToJson_anonymous_tag }, { - "entity_doc_name_func", - DeserExprOperators_entity_doc_name_func + "anonymous_pair", + SerializeQueryInfoToJson_anonymous_pair }, { - "entity_chain_func", - DeserExprOperators_entity_chain_func + "anonymous_component", + SerializeQueryInfoToJson_anonymous_component }, { - "var_parent_func", - DeserExprOperators_var_parent_func + "anonymous_tag_recycled", + SerializeQueryInfoToJson_anonymous_tag_recycled }, { - "var_name_func", - DeserExprOperators_var_name_func + "anonymous_pair_recycled", + SerializeQueryInfoToJson_anonymous_pair_recycled }, { - "var_doc_name_func", - DeserExprOperators_var_doc_name_func + "anonymous_component_recycled", + SerializeQueryInfoToJson_anonymous_component_recycled }, { - "var_chain_func", - DeserExprOperators_var_chain_func + "serialize_plan_trivial_query", + SerializeQueryInfoToJson_serialize_plan_trivial_query }, { - "interpolate_string_w_i32_var", - DeserExprOperators_interpolate_string_w_i32_var + "serialize_plan_nontrivial_query", + SerializeQueryInfoToJson_serialize_plan_nontrivial_query + } +}; + +bake_test_case MetaUtils_testcases[] = { + { + "struct_w_2_i32", + MetaUtils_struct_w_2_i32 }, { - "interpolate_string_w_string_var", - DeserExprOperators_interpolate_string_w_string_var + "struct_w_2_bool", + MetaUtils_struct_w_2_bool }, { - "interpolate_string_w_entity_var", - DeserExprOperators_interpolate_string_w_entity_var + "struct_w_2_char", + MetaUtils_struct_w_2_char }, { - "interpolate_string_w_id_var", - DeserExprOperators_interpolate_string_w_id_var + "struct_w_2_string", + MetaUtils_struct_w_2_string }, { - "interpolate_string_w_var_not_found", - DeserExprOperators_interpolate_string_w_var_not_found + "struct_w_2_f32", + MetaUtils_struct_w_2_f32 }, { - "interpolate_string_w_entity_var_0", - DeserExprOperators_interpolate_string_w_entity_var_0 + "struct_w_2_f64", + MetaUtils_struct_w_2_f64 }, { - "interpolate_string_w_var_special_chars", - DeserExprOperators_interpolate_string_w_var_special_chars + "struct_w_3_enum", + MetaUtils_struct_w_3_enum }, { - "interpolate_string_w_var_before_after_text", - DeserExprOperators_interpolate_string_w_var_before_after_text + "struct_w_3_enum_multiline", + MetaUtils_struct_w_3_enum_multiline }, { - "interpolate_string_w_curly_brackets_var", - DeserExprOperators_interpolate_string_w_curly_brackets_var + "struct_w_3_enum_w_assignment", + MetaUtils_struct_w_3_enum_w_assignment }, { - "interpolate_string_w_curly_brackets_expr", - DeserExprOperators_interpolate_string_w_curly_brackets_expr + "struct_w_4_bitmask", + MetaUtils_struct_w_4_bitmask }, { - "interpolate_string_w_curly_brackets_expr_w_var", - DeserExprOperators_interpolate_string_w_curly_brackets_expr_w_var + "struct_w_3_entities", + MetaUtils_struct_w_3_entities }, { - "interpolate_string_w_curly_brackets_expr_w_composite_var", - DeserExprOperators_interpolate_string_w_curly_brackets_expr_w_composite_var + "struct_w_2_array_3_i32", + MetaUtils_struct_w_2_array_3_i32 }, { - "interpolate_string_w_escape_var_operator", - DeserExprOperators_interpolate_string_w_escape_var_operator + "struct_w_nested", + MetaUtils_struct_w_nested }, { - "interpolate_string_w_escape_curly_brackets", - DeserExprOperators_interpolate_string_w_escape_curly_brackets + "struct_w_2_nested", + MetaUtils_struct_w_2_nested }, { - "interpolate_string_w_func", - DeserExprOperators_interpolate_string_w_func + "enum_nospace", + MetaUtils_enum_nospace }, { - "interpolate_string_w_func_chain", - DeserExprOperators_interpolate_string_w_func_chain + "struct_nospace", + MetaUtils_struct_nospace }, { - "iter_to_vars_no_data", - DeserExprOperators_iter_to_vars_no_data + "identifier_w_underscore", + MetaUtils_identifier_w_underscore }, { - "iter_to_vars_1_comp", - DeserExprOperators_iter_to_vars_1_comp + "struct_w_ptr", + MetaUtils_struct_w_ptr }, { - "iter_to_vars_2_comps", - DeserExprOperators_iter_to_vars_2_comps + "private_members", + MetaUtils_private_members }, { - "iter_to_vars_1_comp_1_tag", - DeserExprOperators_iter_to_vars_1_comp_1_tag + "enum_constant_w_name_prefix", + MetaUtils_enum_constant_w_name_prefix }, { - "iter_to_vars_w_1_query_var", - DeserExprOperators_iter_to_vars_w_1_query_var + "enum_constant_w_type_prefix", + MetaUtils_enum_constant_w_type_prefix }, { - "iter_to_vars_w_2_query_vars", - DeserExprOperators_iter_to_vars_w_2_query_vars + "enum_constant_w_name_type_prefix", + MetaUtils_enum_constant_w_name_type_prefix } }; @@ -4962,6 +4693,10 @@ bake_test_case OpaqueTypes_testcases[] = { { "ser_deser_0_entity", OpaqueTypes_ser_deser_0_entity + }, + { + "const_string", + OpaqueTypes_const_string } }; @@ -5128,6 +4863,7 @@ bake_test_case Misc_testcases[] = { } }; + static bake_test_suite suites[] = { { "PrimitiveTypes", @@ -5140,7 +4876,7 @@ static bake_test_suite suites[] = { "EnumTypes", NULL, NULL, - 8, + 9, EnumTypes_testcases }, { @@ -5150,11 +4886,18 @@ static bake_test_suite suites[] = { 7, BitmaskTypes_testcases }, + { + "RuntimeTypes", + NULL, + NULL, + 39, + RuntimeTypes_testcases + }, { "StructTypes", NULL, NULL, - 28, + 29, StructTypes_testcases }, { @@ -5182,14 +4925,14 @@ static bake_test_suite suites[] = { "Units", NULL, NULL, - 28, + 29, Units_testcases }, { "Serialized", NULL, NULL, - 61, + 62, Serialized_testcases }, { @@ -5199,81 +4942,67 @@ static bake_test_suite suites[] = { 135, Cursor_testcases }, - { - "DeserializeFromExpr", - NULL, - NULL, - 57, - DeserializeFromExpr_testcases - }, - { - "SerializeToExpr", - NULL, - NULL, - 54, - SerializeToExpr_testcases - }, { "DeserializeFromJson", NULL, NULL, - 103, + 134, DeserializeFromJson_testcases }, { "SerializeToJson", NULL, NULL, - 43, + 46, SerializeToJson_testcases }, { "SerializeEntityToJson", NULL, NULL, - 51, + 73, SerializeEntityToJson_testcases }, { "SerializeIterToJson", NULL, NULL, - 63, + 76, SerializeIterToJson_testcases }, { - "SerializeTypeInfoToJson", + "SerializeIterToRowJson", NULL, NULL, - 40, - SerializeTypeInfoToJson_testcases + 60, + SerializeIterToRowJson_testcases }, { - "MetaUtils", + "SerializeTypeInfoToJson", NULL, NULL, - 22, - MetaUtils_testcases + 40, + SerializeTypeInfoToJson_testcases }, { - "Vars", + "SerializeQueryInfoToJson", NULL, NULL, - 15, - Vars_testcases + 27, + SerializeQueryInfoToJson_testcases }, { - "DeserExprOperators", + "MetaUtils", NULL, NULL, - 126, - DeserExprOperators_testcases + 22, + MetaUtils_testcases }, { "OpaqueTypes", NULL, NULL, - 17, + 18, OpaqueTypes_testcases }, { @@ -5286,5 +5015,5 @@ static bake_test_suite suites[] = { }; int main(int argc, char *argv[]) { - return bake_test_run("meta", argc, argv, suites, 22); + return bake_test_run("meta", argc, argv, suites, 21); } diff --git a/vendors/flecs/test/meta/src/utils.c b/vendors/flecs/test/meta/src/utils.c index 82deaf546..c60d394f3 100644 --- a/vendors/flecs/test/meta/src/utils.c +++ b/vendors/flecs/test/meta/src/utils.c @@ -7,3 +7,12 @@ void install_test_abort(void) { ecs_os_set_api(&os_api); ecs_log_set_level(-5); } + +void _test_json(const char* str1, const char *str2, const char* vstr1, const char *vstr2, const char *file, int line){ + if (ecs_os_strcmp(str1, str2)) { + char *esc = flecs_astresc('"', str1); + printf("esc: %s\n", esc); + ecs_os_free(esc); + } + _test_str(str1, str2, vstr1, vstr2, file, line); +} diff --git a/vendors/flecs/test/query/include/query.h b/vendors/flecs/test/query/include/query.h new file mode 100644 index 000000000..da9d7a78b --- /dev/null +++ b/vendors/flecs/test/query/include/query.h @@ -0,0 +1,83 @@ +#ifndef QUERY_H +#define QUERY_H + +/* This generated file contains includes for project dependencies */ +#include "query/bake_config.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Multiline strings */ +#define HEAD +#define LINE "\n" + +#define MAX_SYS_COLUMNS (20) +#define MAX_ENTITIES (256) +#define MAX_INVOCATIONS (1024) + +typedef struct Probe { + ecs_entity_t system; + ecs_entity_t event; + ecs_id_t event_id; + int32_t offset; + int32_t count; + int32_t invoked; + int32_t term_count; + int32_t term_index; + ecs_entity_t e[MAX_ENTITIES]; + ecs_entity_t c[MAX_INVOCATIONS][MAX_SYS_COLUMNS]; + ecs_entity_t s[MAX_INVOCATIONS][MAX_SYS_COLUMNS]; + void *param; +} Probe; + +typedef struct IterData { + ecs_entity_t component; + ecs_entity_t component_2; + ecs_entity_t component_3; + ecs_entity_t new_entities[MAX_ENTITIES]; + int32_t entity_count; +} IterData; + +typedef struct Position { + float x; + float y; +} Position; + +typedef struct Velocity { + float x; + float y; +} Velocity; + +typedef float Mass; + +typedef float Rotation; + +typedef struct Color { + float r; + float g; + float b; + float a; +} Color; + +typedef struct Self { + ecs_entity_t value; +} Self; + +void probe_system_w_ctx( + ecs_iter_t *it, + Probe *ctx); + +void probe_iter(ecs_iter_t *it); + +void probe_has_entity(Probe *probe, ecs_entity_t e); + +void install_test_abort(void); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/test/query/include/query/bake_config.h b/vendors/flecs/test/query/include/query/bake_config.h new file mode 100644 index 000000000..2b2c33ea2 --- /dev/null +++ b/vendors/flecs/test/query/include/query/bake_config.h @@ -0,0 +1,25 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef QUERY_BAKE_CONFIG_H +#define QUERY_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include +#include + +#endif + diff --git a/vendors/flecs/test/query/project.json b/vendors/flecs/test/query/project.json new file mode 100644 index 000000000..82590266e --- /dev/null +++ b/vendors/flecs/test/query/project.json @@ -0,0 +1,2205 @@ +{ + "id": "query", + "type": "application", + "value": { + "author": "Sander Mertens", + "description": "Test project for flecs queries", + "public": false, + "coverage": false, + "use": [ + "flecs" + ] + }, + "test": { + "testsuites": [{ + "id": "Validator", + "testcases": [ + "validate_1_term", + "validate_1_term_component", + "validate_2_terms", + "validate_3_terms", + "validate_3_terms_w_or", + "validate_4_terms_w_or_at_1", + "validate_1_term_wildcard", + "validate_1_term_any", + "validate_1_term_same_subj_obj", + "validate_1_term_acyclic_same_subj_obj", + "validate_1_term_acyclic_reflexive_same_subj_obj", + "validate_1_term_same_subj_obj_var", + "validate_1_term_acyclic_same_subj_obj_var", + "validate_1_term_acyclic_reflexive_same_subj_obj_var", + "validate_1_term_non_acyclic_superset", + "validate_1_term_dont_inherit_default_set", + "validate_1_term_dont_inherit_pair_default_set", + "validate_1_term_inherit_default_set", + "validate_1_term_inherit_pair_default_set", + "validate_1_term_override_default_set", + "validate_1_term_override_pair_default_set", + "validate_1_term_up_no_inherit", + "validate_1_term_up_no_inherit_pair", + "validate_1_term_up_override", + "validate_1_term_up_override_pair", + "validate_1_term_up_isa_no_inherit", + "validate_1_term_up_isa_no_inherit_pair", + "validate_1_term_up_isa_override", + "validate_1_term_up_isa_override_pair", + "validate_1_term_cascade_implicit_trav", + "validate_1_term_cascade_isa", + "validate_1_term_cascade_childof", + "validate_1_term_cascade_down", + "validate_1_term_optional_only", + "validate_1_term_transitive_pair", + "validate_1_variable_as_pred_only", + "validate_1_variable_as_pred_w_subj", + "validate_1_variable_as_pred_w_pair", + "validate_1_variable_as_subj", + "validate_1_variable_as_obj", + "validate_2_terms_or_w_dontinherit", + "validate_2_terms_or_w_both_dontinherit", + "validate_w_pair_id", + "validate_w_pred_obj", + "validate_w_pair_id_and_subj", + "validate_1_w_pred_name", + "validate_1_w_final_pred_name", + "validate_1_w_subj_name", + "validate_1_w_obj_name", + "validate_w_this_implicit_variable", + "validate_w_this_explicit_entity", + "validate_w_first_this_implicit_variable", + "validate_w_first_this_explicit_entity", + "validate_w_second_this_implicit_variable", + "validate_w_second_this_explicit_entity", + "validate_w_this_variable_name", + "validate_w_src_var", + "validate_w_first_var", + "validate_w_second_var", + "validate_w_src_var_from_name", + "validate_w_first_first_var", + "validate_w_second_second_var", + "validate_w_0_source", + "validate_w_0_target", + "validate_2_terms_w_or", + "validate_2_terms_w_or_mixed_src_flags", + "validate_2_terms_w_or_mixed_src_id", + "validate_2_terms_w_or_mixed_src_name", + "validate_2_terms_w_or_same_src_w_id_and_name", + "validate_w_and_flag", + "validate_w_or_flag", + "validate_w_not_flag", + "validate_filter", + "validate_double_init", + "validate_double_init_w_expr", + "validate_double_init_w_expr_optional", + "validate_w_tag_term_is_no_data", + "validate_w_inout_none_term_is_no_data", + "validate_w_tag_and_inout_none_term_is_no_data", + "validate_w_not_term_is_no_data", + "validate_w_no_transitive_pair", + "validate_w_transitive_pair_any_src", + "validate_w_transitive_pair", + "validate_w_transitive_tag_no_pair", + "validate_w_transitive_tag_self_tgt", + "validate_w_transitive_tag_any_tgt", + "validate_w_pair_same_vars", + "validate_w_pair_not_same_vars", + "validate_w_pair_no_vars_not_same_vars", + "validate_w_pair_wildcard_not_same_vars", + "validate_w_pair_any_not_same_vars", + "validate_w_no_pair_not_same_vars", + "validate_not_childof_any", + "validate_w_inherited_id", + "validate_w_inherited_pair", + "validate_w_non_inherited_id", + "validate_w_non_inherited_pair", + "validate_w_first_wildcard_inout_none", + "validate_w_first_var_inout_none", + "validate_w_pair_wildcard_inout_none", + "validate_w_pair_var_inout_none", + "validate_w_unresolved_by_name", + "validate_w_unresolved_by_name_eq", + "validate_childof_this", + "validate_childof_this_entity", + "validate_childof_this_by_id", + "validate_filter_flag", + "validate_first_0_name", + "validate_src_0_name", + "validate_second_0_name", + "validate_singleton_src_w_first_name", + "validate_singleton_second_w_first_name", + "not_wildcard", + "not_first_wildcard", + "not_second_wildcard", + "not_wildcard_id", + "not_wildcard_first_pair", + "not_wildcard_second_pair", + "validate_or_same_type", + "validate_or_different_types", + "validate_or_different_types", + "validate_or_different_types_1_and_2_or", + "validate_trav_isa_w_wildcard", + "validate_trav_isa_w_any", + "validate_custom_trav_w_inherit_id", + "validate_custom_trav_w_inherit_id_w_self_up", + "validate_custom_trav_w_inherit_id_w_up", + "validate_simple_1_term_is_cacheable", + "validate_simple_1_term_pair_is_cacheable", + "validate_simple_1_term_pair_recycled_is_cacheable", + "validate_simple_1_term_tag_is_cacheable", + "validate_simple_2_term_is_cacheable", + "validate_simple_w_can_inherit", + "validate_simple_w_can_toggle", + "validate_simple_w_sparse", + "validate_simple_w_union", + "validate_simple_w_union_pair", + "validate_simple_w_transitive", + "validate_simple_w_transitive_pair", + "validate_simple_w_reflexive", + "validate_simple_w_reflexive_pair", + "validate_simple_w_inherited_component" + ] + }, { + "id": "Parser", + "testcases": [ + "resolve_this", + "resolve_wildcard", + "resolve_any", + "resolve_is_a", + "0", + "component_implicit_subject", + "component_explicit_subject", + "component_explicit_subject_this", + "component_explicit_subject_this_by_name", + "component_explicit_subject_this_by_var_name", + "component_explicit_subject_wildcard", + "component_explicit_subject_any", + "component_explicit_subject_0", + "this_as_predicate", + "this_var_as_predicate", + "this_lowercase_var_as_predicate", + "this_as_object", + "this_var_as_object", + "pair_implicit_subject", + "pair_implicit_subject_wildcard_pred", + "pair_implicit_subject_wildcard_obj", + "pair_implicit_subject_any_pred", + "pair_implicit_subject_any_obj", + "pair_implicit_subject_this_pred", + "pair_implicit_subject_this_obj", + "pair_implicit_subject_pred_w_self", + "pair_implicit_subject_obj_w_self", + "pair_implicit_subject_pred_w_up", + "pair_implicit_subject_obj_w_up", + "pair_implicit_subject_pred_w_self_up", + "pair_implicit_subject_obj_w_self_up", + "pair_implicit_subject_pred_w_invalid_flags", + "pair_implicit_subject_obj_w_invalid_flags", + "pair_explicit_subject", + "pair_explicit_subject_this", + "pair_explicit_subject_this_by_name", + "pair_explicit_subject_this_by_var_name", + "pair_explicit_subject_wildcard_pred", + "pair_explicit_subject_wildcard_subj", + "pair_explicit_subject_wildcard_obj", + "pair_implicit_subject_0_name_object", + "pair_implicit_subject_0_name_not_found_object", + "pair_implicit_subject_0_digit_object", + "pair_explicit_subject_0_object", + "pair_explicit_subject_0", + "in_component_implicit_subject", + "in_component_explicit_subject", + "in_pair_implicit_subject", + "in_pair_explicit_subject", + "inout_component_implicit_subject", + "inout_component_explicit_subject", + "inout_pair_implicit_subject", + "inout_pair_explicit_subject", + "out_component_implicit_subject", + "out_component_explicit_subject", + "out_pair_implicit_subject", + "out_pair_explicit_subject", + "inout_filter_component", + "inout_w_not_operator", + "inout_w_optional_operator", + "component_singleton", + "this_singleton", + "component_implicit_no_subject", + "component_explicit_no_subject", + "pair_no_subject", + "variable_single_char", + "variable_multi_char", + "variable_multi_char_w_underscore", + "variable_multi_char_w_number", + "variable_multi_char_not_allcaps", + "pred_var", + "obj_var", + "component_not", + "pair_implicit_subject_not", + "pair_explicit_subject_not", + "2_component_not", + "2_component_not_no_space", + "component_optional", + "2_component_optional", + "2_component_optional_no_space", + "from_and", + "from_or", + "from_not", + "pair_implicit_subject_optional", + "pair_explicit_subject_optional", + "pred_implicit_subject_w_role", + "pred_explicit_subject_w_role", + "pred_no_subject_w_role", + "pair_implicit_subject_w_role", + "pair_explicit_subject_w_role", + "inout_role_pred_implicit_subject", + "inout_role_pred_no_subject", + "inout_role_pred_explicit_subject", + "inout_role_pair_implicit_subject", + "inout_role_pair_explicit_subject", + "2_pred_implicit_subject", + "2_pred_no_subject", + "2_pred_explicit_subject", + "2_pair_implicit_subject", + "2_pair_explicit_subject", + "2_pred_role", + "2_pair_implicit_subj_role", + "2_pair_explicit_subj_role", + "2_or_pred_implicit_subj", + "2_or_pred_explicit_subj", + "2_or_pair_implicit_subj", + "2_or_pair_explicit_subj", + "2_or_pred_inout", + "2_or_no_space", + "2_or_w_not_1st_arg", + "2_or_w_not_2nd_arg", + "2_or_w_optional_1st_arg", + "2_or_w_optional_2nd_arg", + "1_entity_id_pred_implicit_subj", + "1_entity_id_pred_no_subj", + "1_entity_id_pred_explicit_subj", + "1_entity_id_pair_implicit_subj", + "1_entity_id_pair_explicit_subj", + "1_digit_pred_implicit_subj", + "1_digit_pred_no_subj", + "1_digit_pred_explicit_subj", + "1_digit_pair_implicit_subj", + "1_digit_pair_explicit_subj", + "pred_implicit_subject_self", + "pred_implicit_subject_superset", + "pred_implicit_subject_superset_inclusive", + "pred_implicit_subject_superset_cascade", + "pred_implicit_subject_superset_inclusive_cascade", + "pred_implicit_subject_implicit_superset_cascade", + "pred_implicit_subject_implicit_superset_inclusive_cascade", + "pred_implicit_subject_implicit_superset_cascade_w_rel", + "pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel", + "pred_implicit_subject_superset_childof", + "pred_implicit_subject_cascade_superset_childof", + "pred_implicit_subject_superset_cascade_childof", + "pred_implicit_subject_superset_cascade_childof_optional", + "expr_w_symbol", + "expr_w_newline", + "subj_entity_w_explicit_self", + "subj_entity_w_explicit_self_superset", + "subj_entity_w_explicit_superset_relation", + "subj_entity_w_explicit_self_superset_relation", + "obj_entity_w_explicit_self", + "obj_entity_w_explicit_self_superset", + "obj_entity_w_explicit_superset_relation", + "obj_entity_w_explicit_self_superset_relation", + "pred_entity_w_explicit_self", + "pred_entity_w_explicit_self_superset", + "pred_entity_no_args_w_explicit_self", + "pred_entity_no_args_w_explicit_self_superset", + "pred_entity_no_args_2_terms_w_explicit_self", + "pred_entity_no_args_2_terms_w_explicit_self_superset", + "newline", + "2_newlines", + "3_newlines", + "space", + "2_spaces", + "trailing_newline", + "2_trailing_newlines", + "trailing_space", + "2_trailing_spaces", + "template_type", + "template_type_nested", + "template_type_multiple_args", + "template_type_multiple_args_nested", + "template_type_unbalanced_open", + "template_type_unbalanced_close", + "predicate_w_parens", + "not_alive_pred", + "not_alive_subj", + "not_alive_obj", + "this_subj_var_kind", + "this_obj_var_kind", + "this_subj_obj_var_kind", + "var_w_name", + "entity_pred_no_name", + "entity_subj_no_name", + "entity_obj_no_name", + "this_pred_no_name", + "this_subj_no_name", + "this_obj_no_name", + "invalid_variable_only", + "oneof_self_pred_w_relative_obj", + "oneof_other_pred_w_relative_obj", + "oneof_self_pred_w_invalid_obj", + "oneof_other_pred_w_invalid_obj", + "oneof_w_other_entity_w_same_name", + "oneof_w_other_entity_w_same_name_w_set_scope", + "oneof_w_wildcard", + "oneof_w_any", + "oneof_w_fullpath", + "pair_implicit_src_missing_rel", + "pair_implicit_src_missing_obj", + "pair_explicit_src_missing_src", + "pair_explicit_src_missing_obj", + "eq_id", + "eq_id_var", + "eq_var_id", + "eq_var_table", + "eq_var", + "neq_id", + "neq_id_var", + "neq_var_id", + "neq_var_table", + "neq_var", + "eq_name", + "eq_name_var", + "eq_var_name", + "neq_name", + "neq_name_var", + "neq_var_name", + "match_name", + "match_name_var", + "match_var_name", + "match_var", + "nmatch_name", + "nmatch_name_var", + "nmatch_var_name", + "eq_same_var", + "neq_same_var", + "eq_same_var_this", + "neq_same_var_this", + "eq_w_not", + "neq_w_not", + "match_w_not", + "eq_w_optional", + "neq_w_optional", + "match_w_optional", + "eq_0", + "neq_0", + "eq_wildcard", + "neq_wildcard", + "eq_any", + "neq_any", + "query_scope_1_term", + "query_scope_1_term_spaces", + "query_scope_2_terms", + "query_nested_scope", + "query_nested_scope_spaces", + "query_scope_unbalanced", + "query_not_scope", + "query_empty_scope", + "query_scope_newline_after_open", + "query_scope_newline_after_close", + "query_term_after_scope", + "override_tag", + "override_pair", + "pair_3_args", + "pair_3_args_implicit_this", + "pair_4_args", + "pair_4_args_implicit_this", + "pair_3_args_2_terms", + "pair_3_args_this_tgt", + "pair_3_args_2_terms_this_tgt", + "pair_3_args_2_terms_this_tgt_implicit_this", + "pair_3_or_args", + "pair_3_or_args_implicit_this", + "pair_4_or_args", + "pair_4_or_args_implicit_this", + "pair_3_args_w_vars", + "pair_4_args_w_vars", + "pair_3_args_w_vars_w_term_after", + "pair_4_args_w_vars_w_term_after", + "pair_or_before_and_oper", + "pair_and_before_or_oper", + "cascade_desc", + "newline_after_inout", + "newline_after_term_open", + "newline_after_term_src", + "newline_after_term_src_pair", + "newline_after_term_pair_comma", + "newline_after_term_pair_second", + "tag_w_space_implicit_this", + "tag_w_space", + "pair_first_w_space_implicit_this", + "pair_first_w_space", + "pair_second_w_space_implicit_this", + "pair_second_w_space", + "pair_src_w_space", + "empty_term", + "empty_term_w_space", + "empty_term_w_newline", + "mixed_1_desc_and_1_expr", + "mixed_2_desc_and_1_expr", + "mixed_1_desc_and_2_expr", + "not_wildcard", + "not_first_wildcard", + "not_second_wildcard", + "unterminated_paren", + "unterminated_after_first_paren", + "unterminated_after_src_var", + "eq_name_existing_entity", + "match_existing_entity", + "match_wildcard", + "match_any", + "match_variable_oper", + "escaped_identifier", + "escaped_identifier_first", + "escaped_identifier_second", + "n_tokens_test" + ] + }, { + "id": "Basic", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "0_query", + "1_fact_w_tag", + "1_fact_w_component", + "1_fact_w_tag_pair", + "1_fact_w_component_pair", + "2_facts_same_src_w_tag", + "2_facts_same_src_w_component", + "2_facts_same_src_w_tag_pair", + "2_facts_same_src_w_component_pair", + "2_facts_other_src_w_tag", + "2_facts_other_src_w_component", + "2_facts_other_src_w_tag_pair", + "2_facts_other_src_w_component_pair", + "1_fact_w_any", + "1_fact_w_pair_any_tgt", + "1_fact_w_pair_any_rel", + "1_fact_w_pair_any_rel_tgt", + "2_facts_same_src_w_any", + "2_facts_same_src_w_pair_any_tgt", + "2_facts_same_src_w_pair_any_rel", + "2_facts_same_src_w_pair_any_rel_tgt", + "2_facts_other_src_w_any", + "2_facts_other_src_w_pair_any_tgt", + "2_facts_other_src_w_pair_any_rel", + "2_facts_other_src_w_pair_any_rel_tgt", + "1_this_src_w_tag", + "1_this_src_w_component", + "1_this_src_w_tag_pair", + "1_this_src_w_component_pair", + "1_this_src_w_tag_2_tables", + "1_this_src_w_component_2_tables", + "1_this_src_w_tag_pair_2_tables", + "1_this_src_w_component_pair_2_tables", + "2_this_src_w_tag", + "2_this_src_w_component", + "2_this_src_ent_src_w_tag", + "2_this_src_ent_src_w_component", + "2_ent_src_this_src_w_tag", + "2_ent_src_this_src_w_component", + "recycled_tag", + "recycled_src", + "recycled_pair_rel", + "recycled_pair_tgt", + "this_src_w_wildcard", + "this_src_w_pair_rel_wildcard", + "this_src_w_pair_tgt_wildcard", + "this_src_w_pair_rel_tgt_wildcard", + "this_src_w_any", + "this_src_w_any_written", + "this_src_w_pair_rel_any", + "this_src_w_pair_tgt_any", + "this_src_w_pair_tgt_any_n_tgts", + "this_src_w_pair_tgt_any_n_tgts_written", + "this_src_w_pair_rel_tgt_any", + "ent_src_w_wildcard", + "ent_src_w_pair_rel_wildcard", + "ent_src_w_pair_tgt_wildcard", + "ent_src_w_pair_rel_tgt_wildcard", + "1_wildcard_src", + "1_wildcard_src_w_pair", + "2_wildcard_src", + "2_wildcard_src_w_pair", + "1_wildcard_src_w_pair_tgt_var", + "1_wildcard_src_w_pair_rel_var", + "1_wildcard_src_w_pair_tgt_this", + "1_wildcard_src_w_pair_rel_this", + "1_any_src", + "1_any_src_w_pair", + "2_any_src", + "2_any_src_w_pair", + "1_any_src_w_pair_tgt_var", + "1_any_src_w_pair_rel_var", + "1_any_src_w_pair_tgt_this", + "1_any_src_w_pair_rel_this", + "not_any", + "rule_w_iter_next", + "empty_rule", + "invalid_rule", + "instanced_table_src", + "instanced_entity_src", + "instanced_mixed_src", + "instanced_inherited", + "in_term", + "out_term", + "inout_term", + "nodata_term", + "find_this_lowercase", + "find_this_uppercase", + "find_this_tgt_lowercase", + "find_this_tgt_uppercase", + "get_filter", + "iter_empty_source", + "iter_empty_source_2_terms", + "iter_empty_source_wildcard", + "iter_empty_source_pair", + "iter_empty_source_pair_wildcard", + "iter_empty_source_2_terms_pair", + "iter_empty_source_2_terms_mixed", + "iter_empty_source_2_terms_mixed_pair", + "iter_empty_source_2_terms_mixed_pair_wildcard", + "this_var_w_empty_entity", + "match_disabled", + "match_prefab", + "match_disabled_prefab", + "match_disabled_this_tgt", + "match_prefab_this_tgt", + "match_disabled_prefab_this_tgt", + "match_self_disabled", + "match_self_prefab", + "match_self_disabled_prefab", + "match_optional_disabled", + "match_optional_prefab", + "match_optional_disabled_prefab", + "match_optional_disabled_this_tgt", + "match_optional_prefab_this_tgt", + "match_optional_disabled_prefab_this_tgt", + "match_optional_self_disabled", + "match_optional_self_prefab", + "match_optional_self_disabled_prefab", + "inout_none_first_term", + "inout_none_first_term_self_up", + "inout_none_second_term", + "inout_none_second_term_self_up", + "inout_none_singleton", + "inout_none_singleton_w_or", + "inout_none_component_w_or", + "no_data_rule", + "frame_offset", + "frame_offset_no_data", + "match_empty_tables", + "match_empty_tables_no_data", + "match_empty_tables_w_not", + "match_empty_tables_w_wildcard", + "match_empty_tables_w_no_empty_tables", + "match_empty_tables_trivial", + "oneof_wildcard", + "oneof_any", + "instanced_w_singleton", + "instanced_w_base", + "unknown_before_known", + "unknown_before_known_after_or", + "unknown_before_known_after_not", + "unknown_before_known_after_optional", + "unknown_before_known_after_scope", + "2_trivial_mixed_2_tables", + "2_trivial_mixed_2_tables_component", + "2_trivial_mixed_2_tables_wildcard", + "2_trivial_1_unused_id", + "2_trivial_one_regular", + "1_trivial_one_regular_one_trivial", + "one_regular_2_trivial", + "2_trivial_w_prefab", + "3_trivial_w_prefab", + "2_trivial_w_disabled", + "3_trivial_w_disabled", + "2_this_w_fixed_src", + "2_fixed_src_w_this", + "2_this_w_fixed_src_no_match_fixed", + "2_fixed_src_w_this_no_match_fixed", + "2_this_w_fixed_src_no_match_this", + "2_fixed_src_w_this_no_match_this", + "query_count_results", + "query_count_entities", + "query_is_true", + "implicit_cleanup_1_term", + "implicit_cleanup_2_terms", + "implicit_cleanup_1_term_w_up", + "implicit_cleanup_2_terms_w_up", + "implicit_cleanup_2_queries", + "implicit_cleanup_2_queries_1_cleanup", + "iter_valid", + "iter_frame_offset", + "iter_nested_1", + "iter_nested_2", + "iter_interleaved", + "set_get_context", + "set_get_binding_context", + "set_get_context_w_free", + "set_get_binding_context_w_free", + "create_query_w_existing_entity", + "create_query_w_existing_entity_different_term_count", + "create_multi_component_query_w_existing_entity", + "delete_query_by_entity", + "eval_count", + "no_results_after_delete_tree", + "no_results_after_delete_tree_deferred", + "add_on_self_ref", + "add_on_self_ref_by_name", + "delete_id_after_delete_query", + "pair_sweep_tag", + "pair_sweep_wildcard_first", + "pair_sweep_wildcard_second", + "create_w_entity_deferred", + "32_terms", + "33_terms_expr", + "stage_query", + "world_query_w_stage_iter", + "stage_query_w_nth_stage", + "world_query_w_nth_stage_iter", + "match_empty", + "match_new_empty", + "match_empty_w_component", + "match_new_empty_w_component", + "match_empty_w_ref", + "match_new_empty_w_ref", + "match_empty_w_order_by", + "match_new_empty_w_order_by", + "match_empty_w_bitset", + "default_query_flags", + "ref_fields_this", + "ref_fields_static_src", + "ref_fields_variable_src", + "ref_fields_up_src", + "ref_fields_self_up_src", + "0_src_match_nothing", + "0_terms_match_nothing", + "any_record", + "pair_rel_any_record", + "pair_tgt_any_record", + "pair_any_any_record", + "written_any_record", + "written_pair_rel_any_record", + "written_pair_tgt_any_record", + "written_pair_any_any_record", + "pair_rel_any_record_component", + "pair_tgt_any_record_component", + "entity_iteration_w_match_empty_tables", + "get_cache_query_uncached", + "get_cache_query_cached", + "get_cache_query_partially_cached" + ] + }, { + "id": "Combinations", + "setup": true, + "params": { + "cache_kind": ["default", "auto"], + "on_instantiate": ["override", "inherit", "dont_inherit"] + }, + "testcases": [ + "singleton_and_self_and_self", + "self_and_singleton_and_self", + "self_and_self_and_singleton", + "singleton_and_trav_and_self", + "trav_and_singleton_and_self", + "self_and_singleton_and_trav", + "self_and_trav_and_singleton", + "singleton_and_self_and_trav", + "trav_and_self_and_singleton" + ] + }, { + "id": "Plan", + "testcases": [ + "reordered_plan_1", + "reordered_plan_2", + "reordered_plan_3", + "reordered_plan_4", + "reordered_plan_5", + "reordered_plan_6", + "reordered_plan_7", + "1_trivial_plan", + "2_trivial_plan", + "1_trivial_plan_component", + "2_trivial_plan_component", + "3_trivial_plan_w_pair", + "3_trivial_plan_w_wildcard", + "3_trivial_plan_w_any", + "3_trivial_plan_w_pair_component", + "3_trivial_plan_w_wildcard_component", + "3_trivial_plan_w_any_component", + "1_trivial_component_w_none", + "2_trivial_component_w_none", + "2_trivial_plan_w_wildcard", + "this_before_fixed_src", + "fixed_src_before_this", + "var_before_fixed_src", + "fixed_src_before_var", + "this_before_fixed_src_w_not", + "this_before_fixed_src_w_first_var", + "this_before_fixed_src_w_first_var_w_not", + "this_before_fixed_src_w_second_var", + "this_before_fixed_src_w_second_var_w_not", + "populate_1_fixed", + "populate_2_fixed", + "populate_1_fixed_1_this_self", + "populate_2_fixed_2_this_self", + "populate_2_fixed_2_this_self_interleaved", + "populate_2_this_self_2_fixed", + "populate_1_fixed_1_this_up", + "populate_2_fixed_2_this_up", + "populate_2_fixed_2_this_up_interleaved", + "populate_2_this_up_2_fixed", + "populate_1_fixed_1_this_self_cached", + "populate_2_fixed_2_this_self_cached", + "populate_2_fixed_2_this_self_interleaved_cached", + "populate_2_this_self_2_fixed_cached", + "populate_1_fixed_1_this_up_cached", + "populate_2_fixed_2_this_up_cached", + "populate_2_fixed_2_this_up_interleaved_cached", + "populate_2_this_up_2_fixed_cached", + "populate_1_fixed_1_var_self", + "populate_2_fixed_2_var_self", + "populate_2_fixed_2_var_self_interleaved", + "populate_2_var_self_2_fixed", + "populate_1_fixed_1_var_up", + "populate_2_fixed_2_var_up", + "populate_2_fixed_2_var_up_interleaved", + "populate_2_var_up_2_fixed", + "cache_2_or", + "cache_2_or_w_not", + "1_plan_any_src", + "1_plan_not_any_src", + "1_plan_optional_any_src", + "pair_first_wildcard", + "pair_first_wildcard_cached", + "pair_second_wildcard", + "pair_second_wildcard_cached", + "0_src_tag", + "0_src_component", + "0_src_w_sparse", + "0_src_w_toggle", + "0_src_w_union", + "0_src_w_sparse_and_component", + "0_src_w_toggle_and_component", + "0_src_w_union_and_component", + "cached_isa_tgt", + "cached_isa_tgt_w_self_second", + "cached_isa_tgt_no_expr", + "cached_isa_tgt_w_self_second_no_expr", + "cached_w_not_and_uncacheable", + "cached_w_optional_and_uncacheable", + "cached_w_not_optional_and_uncacheable" + ] + }, { + "id": "Variables", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "1_ent_src_w_var", + "1_ent_src_w_pair_rel_var", + "1_ent_src_w_pair_tgt_var", + "1_ent_src_w_pair_rel_tgt_var", + "1_ent_src_w_pair_rel_tgt_same_var", + "1_ent_src_w_pair_rel_tgt_same_var_after_write", + "1_this_src_w_var", + "1_this_src_w_pair_rel_var", + "1_this_src_w_pair_tgt_var", + "1_this_src_w_pair_rel_tgt_var", + "1_this_src_w_pair_rel_tgt_same_var", + "1_this_src_w_pair_rel_tgt_same_var_after_write", + "1_src_id_same_var", + "1_src_pair_first_same_var", + "1_src_pair_second_same_var", + "1_src_pair_first_and_second_same_var", + "1_src_id_same_var_after_write", + "1_src_pair_first_same_var_after_write", + "1_src_pair_second_same_var_after_write", + "1_src_pair_first_and_second_same_var_after_write", + "1_src_pair_first_same_var_this", + "1_src_pair_second_same_var_this", + "1_src_pair_first_and_second_same_var_this", + "1_src_id_same_var_this_after_write", + "1_src_pair_first_same_var_this_after_write", + "1_src_pair_second_same_var_this_after_write", + "1_src_pair_first_and_second_same_var_this_after_write", + "1_ent_src_w_this_var", + "1_ent_src_w_pair_this_rel", + "1_ent_src_w_pair_this_tgt", + "1_ent_src_w_pair_this_rel_tgt", + "1_this_src_w_this", + "1_this_src_w_pair_this_rel_tgt", + "1_this_src_w_this_after_write", + "1_this_src_w_pair_this_rel_tgt_after_write", + "2_constrain_src_from_src", + "2_constrain_rel_from_src_w_ent", + "2_constrain_rel_from_src_w_var", + "2_constrain_rel_from_src_w_this", + "2_constrain_pair_rel_from_src_w_ent", + "2_constrain_pair_rel_from_src_w_var", + "2_constrain_pair_rel_from_src_w_this", + "2_constrain_pair_tgt_from_src_w_ent", + "2_constrain_pair_tgt_from_src_w_var", + "2_constrain_pair_tgt_from_src_w_this", + "2_constrain_pair_rel_tgt_from_src_w_ent", + "2_constrain_pair_rel_tgt_from_src_w_var", + "2_constrain_pair_rel_tgt_from_src_w_this", + "1_ent_src_set_rel_var", + "1_ent_src_set_pair_rel_var", + "1_ent_src_set_pair_tgt_var", + "1_set_src_var", + "1_set_src_var_w_pair", + "1_set_src_var_w_pair_set_rel", + "1_set_src_var_w_pair_set_tgt", + "1_set_src_var_w_pair_set_rel_tgt", + "1_set_src_this", + "1_set_src_this_w_pair", + "1_set_src_this_w_pair_set_rel", + "1_set_src_this_w_pair_set_tgt", + "1_set_src_this_w_pair_set_rel_tgt", + "1_set_src_this_to_empty_table", + "1_set_src_this_to_empty_table_w_component", + "1_set_src_this_to_empty_table_w_component_self", + "1_set_src_this_to_entity_in_table", + "1_set_src_this_to_entity_in_table_self", + "2_set_src_this", + "2_set_src_this_self", + "2_set_src_this_component", + "2_set_src_this_self_component", + "2_set_src_this_w_up", + "2_set_src_this_self_w_up", + "2_set_src_this_component_w_up", + "2_set_src_this_self_component_w_up", + "2_set_src_this_w_exclusive_wildcard", + "2_set_src_this_self_w_exclusive_wildcard", + "2_set_src_this_self_w_exclusive_wildcard_w_up", + "1_set_src_this_is_true", + "3_set_src_this_w_uncacheable_tag_tag_tag", + "3_set_src_this_w_uncacheable_tag_component_tag", + "3_set_src_this_w_uncacheable_tag_component_component", + "3_set_src_this_w_tag_uncacheable_tag_tag", + "3_set_src_this_w_component_uncacheable_tag_tag", + "3_set_src_this_w_component_uncacheable_tag_component", + "3_set_src_this_w_tag_tag_uncacheable_tag", + "3_set_src_this_w_component_tag_uncacheable_tag", + "3_set_src_this_w_component_component_uncacheable_tag", + "3_set_src_this_w_uncacheable_component_tag_tag", + "3_set_src_this_w_uncacheable_component_component_tag", + "3_set_src_this_w_uncacheable_component_component_component", + "3_set_src_this_w_tag_uncacheable_component_tag", + "3_set_src_this_w_component_uncacheable_component_tag", + "3_set_src_this_w_component_uncacheable_component_component", + "3_set_src_this_w_tag_tag_uncacheable_component", + "3_set_src_this_w_component_tag_uncacheable_component", + "3_set_src_this_w_component_component_uncacheable_component", + "2_set_src_this_w_wildcard", + "1_src_this_var_as_entity", + "1_src_this_var_as_table", + "1_src_this_var_as_table_range", + "1_set_src_this_w_any", + "1_set_src_this_w_any_fixed", + "1_set_src_this_w_fixed_any", + "2_join_by_rel_var", + "2_join_by_pair_rel_var", + "2_join_by_pair_tgt_var", + "2_cycle_w_var", + "2_cycle_w_this_var", + "2_cycle_w_var_this", + "2_cycle_pair_w_var", + "2_cycle_pair_w_this_var_var", + "2_cycle_pair_w_var_this_var", + "2_cycle_pair_w_var_var_this", + "2_cycle_pair_ent_var_var", + "2_cycle_pair_ent_this_var", + "2_cycle_pair_ent_var_this", + "parse_0_var", + "parse_1_var", + "parse_2_vars", + "parse_0_var_paren", + "parse_1_var_paren", + "parse_2_vars_paren", + "parse_1_vars_w_path", + "parse_missing_close_paren", + "parse_missing_open_paren", + "parse_missing_value", + "parse_0_var_w_spaces", + "parse_1_var_w_spaces", + "parse_2_vars_w_spaces", + "parse_0_var_paren_w_spaces", + "parse_1_var_paren_w_spaces", + "parse_2_vars_paren_w_spaces", + "var_count", + "var_name", + "var_is_entity", + "no_this_anonymous_src", + "no_this_anonymous_src_w_pair", + "no_this_anonymous_component_src", + "no_this_anonymous_component_src_w_pair", + "lookup_from_table_this", + "lookup_from_entity_this", + "lookup_from_table", + "lookup_from_entity", + "lookup_from_not_written", + "lookup_from_table_this_component", + "lookup_from_entity_this_component", + "lookup_from_table_component", + "lookup_from_entity_component", + "lookup_from_table_two_children", + "lookup_from_entity_two_children", + "lookup_from_table_same_child_twice", + "lookup_from_entity_same_child_twice", + "lookup_from_table_not", + "lookup_from_entity_not", + "lookup_from_table_w_any_component", + "lookup_from_entity_w_any_component", + "lookup_as_tag", + "lookup_as_relationship", + "lookup_as_target", + "lookup_assign_var", + "lookup_eq_var", + "lookup_neq_var", + "lookup_unresolved_dependent", + "check_vars_this", + "check_vars_var", + "check_vars_wildcard", + "check_vars_any", + "check_vars_var_as_tgt", + "check_vars_this_as_tgt", + "check_vars_anonymous_var_as_tgt", + "check_vars_wildcard_as_tgt", + "check_vars_any_as_tgt", + "check_vars_this_w_lookup_var", + "check_vars_var_w_lookup_var", + "1_trivial_1_var", + "2_trivial_1_var", + "1_trivial_1_var_component", + "2_trivial_1_var_component", + "1_trivial_1_wildcard", + "2_trivial_1_wildcard", + "1_trivial_1_wildcard_component", + "2_trivial_1_wildcard_component", + "1_trivial_1_any", + "2_trivial_1_any", + "1_trivial_1_any_component", + "2_trivial_1_any_component", + "first_invalid_var_name_and_id", + "src_invalid_var_name_and_id", + "second_invalid_var_name_and_id" + ] + }, { + "id": "Operators", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "2_and_not", + "2_and_not_component", + "2_and_out_not", + "2_and_out_not_component", + "3_and_not_not", + "2_and_not_pair_rel_wildcard", + "2_and_not_pair_tgt_wildcard", + "2_and_not_pair_rel_tgt_wildcard", + "2_and_not_pair_rel_var", + "2_and_not_pair_tgt_var", + "2_and_not_pair_rel_tgt_var", + "2_and_not_pair_rel_tgt_same_var", + "2_and_not_pair_rel_var_written", + "2_and_not_pair_tgt_var_written", + "2_and_not_pair_rel_tgt_var_written", + "2_and_not_pair_rel_tgt_same_var_written", + "2_and_not_pair_rel_src_tgt_same_var_written", + "2_and_not_pair_any_rel", + "2_and_not_pair_any_tgt", + "2_and_not_pair_any_src", + "1_not_any_src_fixed_first", + "1_not_any_src_any_tgt_fixed_first", + "1_not_any_src_any_first_fixed_tgt", + "1_not_any_src_any_childof_pair_any_tgt", + "1_not_any_src_any_isa_pair_any_tgt", + "1_not_match_prefab", + "1_not_match_disabled", + "1_not_match_not_queryable", + "1_not_match_prefab_w_match_prefab_flag", + "1_not_match_disabled_w_match_disabled_flag", + "1_not_match_disabled_w_match_prefab_disabled_flag", + "2_and_optional", + "3_and_optional_optional", + "2_and_optional_pair_rel_wildcard", + "2_and_optional_pair_tgt_wildcard", + "2_and_optional_pair_rel_var", + "2_and_optional_pair_tgt_var", + "2_and_optional_pair_rel_tgt_var", + "2_and_optional_pair_rel_tgt_same_var", + "2_and_optional_pair_rel_var_written", + "2_and_optional_pair_tgt_var_written", + "2_and_optional_pair_rel_tgt_var_written", + "2_and_optional_pair_rel_tgt_same_var_written", + "2_and_optional_pair_rel_src_tgt_same_var_written", + "3_and_optional_optional_pair_w_var", + "2_and_optional_pair_any_rel", + "2_and_optional_pair_any_tgt", + "2_and_optional_pair_any_src", + "3_and_optional_dependent_and_pair_rel", + "3_and_optional_dependent_and_pair_tgt", + "3_and_optional_dependent_and_pair_rel_tgt", + "3_and_optional_dependent_and_pair_rel_tgt_same_var", + "3_and_optional_dependent_and_pair_rel_tgt_same_other_var", + "3_and_optional_dependent_and_pair_src", + "3_and_optional_dependent_optional_pair_rel", + "3_and_optional_dependent_optional_pair_tgt", + "3_and_optional_dependent_optional_pair_src", + "3_and_optional_dependent_not_pair_rel", + "3_and_optional_dependent_not_pair_tgt", + "3_and_optional_dependent_not_pair_src", + "1_optional_any_src_fixed_first", + "1_optional_any_src_any_tgt_fixed_first", + "1_optional_any_src_any_first_fixed_tgt", + "1_optional_any_src_any_childof_pair_any_tgt", + "1_optional_any_src_any_isa_pair_any_tgt", + "2_or", + "3_or", + "2_or_w_and", + "and_w_2_or_w_and", + "and_w_2_or_w_and_components", + "and_w_2_or_w_and_set_this", + "and_w_2_or_w_and_components_set_this", + "3_or_w_and", + "2_or_written", + "3_or_written", + "2_or_written_w_rel_var", + "3_or_written_w_rel_var", + "2_or_written_w_tgt_var", + "2_or_written_w_rel_tgt_var", + "2_or_written_w_rel_tgt_same_var", + "3_or_written_w_tgt_var", + "2_or_chains", + "2_or_chains_written", + "2_or_dependent", + "2_or_dependent_reverse", + "2_or_dependent_2_vars", + "2_or_written_dependent", + "2_or_written_dependent_2_vars", + "2_or_w_dependent", + "2_or_w_both", + "3_or_w_both", + "2_or_w_not", + "2_or_w_not_component", + "2_or_w_not_out_component", + "2_or_w_not_out_all_components", + "2_not_first", + "2_optional_first", + "only_not", + "only_not_component", + "only_not_out_component", + "only_optional", + "only_optional_component", + "not_after_fixed_src", + "optional_after_fixed_src", + "root_entities_empty", + "root_entities", + "root_entities_w_children", + "root_entities_w_optional_children", + "core_entities_w_optional_children", + "root_entities_w_not_children", + "core_entities_w_not_children", + "1_ent_src_not", + "1_ent_src_not_pair", + "1_ent_src_not_pair_rel_wildcard", + "1_ent_src_not_pair_tgt_wildcard", + "1_ent_src_not_pair_rel_tgt_wildcard", + "1_ent_src_not_pair_rel_any", + "1_ent_src_not_pair_tgt_any", + "1_ent_src_not_pair_rel_tgt_any", + "1_ent_src_not_pair_rel_var", + "1_ent_src_not_pair_tgt_var", + "1_ent_src_not_pair_rel_tgt_var", + "1_ent_src_not_pair_rel_tgt_same_var", + "1_this_src_not_pair_rel_var", + "1_this_src_not_pair_tgt_var", + "1_this_src_not_pair_rel_tgt_var", + "1_this_src_not_pair_rel_tgt_same_var", + "1_ent_src_not_pair_rel_var_written", + "1_ent_src_not_pair_tgt_var_written", + "1_ent_src_not_pair_rel_tgt_var_written", + "1_ent_src_not_pair_rel_tgt_same_var_written", + "and_from_fixed_src", + "not_from_fixed_src", + "or_from_fixed_src", + "and_from_this", + "not_from_this", + "or_from_this", + "and_from_this_written", + "not_from_this_written", + "or_from_this_written", + "and_from_empty", + "not_from_empty", + "or_from_empty", + "and_from_empty_w_tag", + "not_from_empty_w_tag", + "or_from_empty_w_tag", + "or_w_wildcard", + "or_w_component_and_tag", + "or_w_tag_and_component" + ] + }, { + "id": "Transitive", + "testcases": [ + "1_fact_0_lvl_true", + "1_fact_1_lvl_true", + "1_fact_2_lvl_true", + "1_fact_0_lvl_false", + "1_fact_1_lvl_false", + "1_fact_2_lvl_false", + "1_fact_reflexive", + "1_isa", + "1_childof", + "1_this_src_written_0_lvl", + "1_this_src_written_1_lvl", + "1_this_src_written_2_lvl", + "1_this_src_written_reflexive", + "1_this_src_0_lvl", + "1_this_src_1_lvl", + "1_this_src_2_lvl", + "1_this_src_reflexive", + "1_ent_src_tgt_var_0_lvl", + "1_ent_src_tgt_var_1_lvl", + "1_ent_src_tgt_var_2_lvl", + "1_ent_src_tgt_var_reflexive", + "1_this_src_tgt_var", + "1_this_src_tgt_var_reflexive", + "1_var_src_written_0_lvl", + "1_var_src_written_1_lvl", + "1_var_src_written_2_lvl", + "1_var_src_written_reflexive", + "1_var_src_0_lvl", + "1_var_src_1_lvl", + "1_var_src_2_lvl", + "1_var_src_reflexive", + "1_var_src_tgt_var", + "1_var_src_tgt_var_reflexive", + "1_ent_src_tgt_this_0_lvl", + "1_ent_src_tgt_this_1_lvl", + "1_ent_src_tgt_this_2_lvl", + "1_ent_src_tgt_this_reflexive", + "1_var_src_tgt_this", + "1_var_src_tgt_this_reflexive", + "2_ent_src_constrain_tgt_var_before_0_lvl", + "2_ent_src_constrain_tgt_var_before_1_lvl", + "2_ent_src_constrain_tgt_var_before_2_lvl", + "2_ent_src_constrain_tgt_var_after_0_lvl", + "2_ent_src_constrain_tgt_var_after_1_lvl", + "2_ent_src_constrain_tgt_var_after_2_lvl", + "2_this_src_constrain_tgt_var_before_0_lvl", + "2_this_src_constrain_tgt_var_before_1_lvl", + "2_this_src_constrain_tgt_var_before_2_lvl", + "2_this_src_constrain_tgt_var_after_0_lvl", + "2_this_src_constrain_tgt_var_after_1_lvl", + "2_this_src_constrain_tgt_var_after_2_lvl", + "1_src_tgt_same_var", + "1_src_tgt_same_var_reflexive", + "1_src_tgt_same_this_var_reflexive", + "1_any_src_tgt_var", + "not_transitive_ent_tgt", + "not_transitive_var_tgt", + "not_transitive_ent_tgt_written", + "not_transitive_var_tgt_written", + "optional_transitive_ent_tgt", + "optional_transitive_var_tgt", + "optional_transitive_ent_tgt_written", + "optional_transitive_var_tgt_written", + "2_var_src_w_same_tgt_ent", + "self_target", + "any_target", + "isa_prefab", + "isa_disabled", + "isa_prefab_match_prefab_flag", + "isa_prefab_match_prefab_term", + "isa_disabled_match_disabled_flag", + "isa_disabled_match_disabled_term" + ] + }, { + "id": "ComponentInheritance", + "testcases": [ + "1_ent_0_lvl", + "1_ent_1_lvl", + "1_ent_2_lvl", + "1_ent_3_lvl", + "1_this_0_lvl", + "1_this_1_lvl", + "1_this_2_lvl", + "1_this_3_lvl", + "1_this_0_lvl_written", + "1_this_1_lvl_written", + "1_this_2_lvl_written", + "1_this_3_lvl_written", + "1_var_0_lvl", + "1_var_1_lvl", + "1_var_2_lvl", + "1_var_3_lvl", + "1_var_0_lvl_written", + "1_var_1_lvl_written", + "1_var_2_lvl_written", + "1_var_3_lvl_written", + "1_ent_1_lvl_self", + "1_this_1_lvl_self", + "1_this_1_lvl_written_self", + "1_var_1_lvl_self", + "1_var_1_lvl_written_self", + "1_ent_src_not", + "1_this_src_not", + "1_var_src_not", + "1_this_src_not_written", + "1_var_src_not_written", + "first_self" + ] + }, { + "id": "Recycled", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "recycled_vars", + "recycled_pair_vars", + "recycled_this_ent_var", + "has_recycled_id_from_pair", + "recycled_pair", + "recycled_component_id" + ] + }, { + "id": "BuiltinPredicates", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "this_eq_id", + "this_eq_name", + "this_eq_var", + "this_eq_id_written", + "this_eq_id_written_no_match", + "this_eq_name_written", + "this_eq_name_written_no_match", + "this_eq_var_written", + "var_eq_id", + "var_eq_name", + "var_eq_var", + "var_eq_id_written", + "var_eq_id_written_no_match", + "var_eq_name_written", + "var_eq_name_written_no_match", + "var_eq_var_written", + "var_eq_this", + "this_neq_id", + "this_neq_name", + "this_neq_var", + "this_neq_id_written", + "this_neq_id_written_no_match", + "this_neq_name_written", + "this_neq_name_written_no_match", + "this_neq_var_written", + "var_neq_id", + "var_neq_name", + "var_neq_var", + "var_neq_id_written", + "var_neq_id_written_no_match", + "var_neq_name_written", + "var_neq_name_written_no_match", + "var_neq_var_written", + "var_neq_this", + "this_2_neq_id", + "this_2_neq_name", + "var_2_neq_id", + "var_2_neq_name", + "this_2_neq_id_written", + "this_2_neq_name_written", + "var_2_neq_id_written", + "var_2_neq_name_written", + "this_2_or_id", + "this_3_or_id", + "this_2_or_name", + "this_3_or_name", + "this_2_or_match", + "this_3_or_match", + "var_2_or_id", + "var_2_or_name", + "this_2_or_id_written", + "this_3_or_id_written", + "this_2_or_name_written", + "var_2_or_id_written", + "var_2_or_name_written", + "this_match_eq", + "var_match_eq", + "this_match_eq_written", + "this_match_eq_written_self", + "var_match_eq_written", + "this_match_neq", + "var_match_neq", + "this_match_neq_written", + "var_match_neq_written", + "this_match_2_neq", + "var_match_2_neq", + "this_match_2_neq_written", + "var_match_2_neq_written", + "this_match_2_or", + "this_match_2_or_written", + "this_match_3_or", + "this_match_3_or_written", + "unresolved_by_name", + "var_eq_wildcard", + "var_eq_any", + "var_eq_wildcard_after_write", + "var_eq_any_after_write", + "var_eq_after_var_0_src", + "2_or_w_eq_this", + "2_or_w_eq_lookup_var", + "3_or_w_eq_lookup_var", + "eq_variable", + "eq_wildcard", + "eq_any", + "neq_variable", + "neq_wildcard", + "neq_any", + "match_variable", + "match_wildcard", + "match_any" + ] + }, { + "id": "Scopes", + "setup": true, + "params": { + "cache_kind": ["default", "auto"], + "on_instantiate": ["override", "inherit"] + }, + "testcases": [ + "term_w_not_scope_1_term", + "term_w_not_scope_2_terms", + "term_w_not_scope_1_term_w_not", + "term_w_not_scope_2_terms_w_not", + "term_w_not_scope_1_term_w_var", + "term_w_not_scope_2_terms_w_var", + "term_w_not_scope_1_term_w_not_w_var", + "term_w_not_scope_2_terms_w_not_w_var", + "term_w_not_scope_2_terms_w_or", + "term_w_not_scope_3_terms_w_or", + "term_w_not_scope_2_terms_w_before_after" + ] + }, { + "id": "Traversal", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "this_self_up_childof", + "this_up_childof", + "this_written_self_up_childof", + "this_written_up_childof", + "var_self_up_childof", + "var_up_childof", + "var_written_self_up_childof", + "var_written_up_childof", + "set_var_self_up_childof", + "set_var_up_childof", + "set_var_written_self_up_childof", + "set_var_written_up_childof", + "ent_self_up_childof", + "ent_up_childof", + "implicit_this_self_up_isa", + "implicit_this_up_isa", + "implicit_var_self_up_isa", + "implicit_var_up_isa", + "implicit_ent_self_up_isa", + "implicit_ent_up_isa", + "self_up_2_targets", + "up_2_targets", + "self_up_2_targets_diamond", + "up_2_targets_diamond", + "written_self_up_2_targets", + "written_up_2_targets", + "written_self_up_2_targets_diamond", + "written_up_2_targets_diamond", + "2_self_up_terms", + "2_self_up_terms_2_targets", + "self_up_empty_table", + "up_empty_table", + "self_up_match_empty_table", + "up_match_empty_table", + "self_up_all_owned", + "up_all_owned", + "this_self_up_childof_inherited", + "this_up_childof_inherited", + "this_self_up_childof_inherited_override", + "this_up_childof_inherited_override", + "this_written_self_up_childof_inherited", + "this_written_up_childof_inherited", + "this_written_self_up_childof_inherited_override", + "this_written_up_childof_inherited_override", + "var_self_up_childof_inherited", + "var_up_childof_inherited", + "var_written_self_up_childof_inherited", + "var_written_up_childof_inherited", + "ent_self_up_childof_inherited", + "ent_up_childof_inherited", + "ent_written_self_up_childof_inherited", + "ent_written_up_childof_inherited", + "this_self_up_childof_component", + "this_up_childof_component", + "this_written_self_up_childof_component", + "this_written_up_childof_component", + "var_self_up_childof_component", + "var_up_childof_component", + "var_written_self_up_childof_component", + "var_written_up_childof_component", + "this_self_up_childof_recycled_parent", + "this_up_childof_recycled_parent", + "this_written_self_up_childof_recycled_parent", + "this_written_up_childof_recycled_parent", + "this_self_up_childof_recycled_parent_component", + "this_up_childof_recycled_parent_component", + "this_written_self_up_childof_recycled_parent_component", + "this_written_up_childof_recycled_parent_component", + "this_self_up_childof_pair", + "this_up_childof_pair", + "this_written_self_up_childof_pair", + "this_written_up_childof_pair", + "this_self_up_childof_pair_wildcard", + "this_up_childof_pair_wildcard", + "this_written_self_up_childof_pair_wildcard", + "this_written_up_childof_pair_wildcard", + "this_self_up_childof_pair_tgt_var", + "this_written_self_up_childof_pair_tgt_var", + "this_self_up_childof_pair_rel_var", + "this_written_self_up_childof_pair_rel_var", + "this_self_up_childof_pair_for_var_written", + "this_up_childof_pair_for_var_written", + "this_written_self_up_childof_pair_for_var_written", + "this_self_up_childof_pair_for_var_written_n_targets", + "this_written_self_up_childof_pair_for_var_written_n_targets", + "self_up_2_levels_w_prefab", + "self_up_2_levels_other_trav_rel_w_prefab", + "up_2_levels_w_prefab", + "up_2_levels_other_trav_rel_w_prefab", + "self_up_2_levels", + "self_up_2_levels_other_trav_rel", + "up_2_levels", + "up_2_levels_other_trav_rel", + "self_up_mixed_traversable", + "not_up", + "not_self_up", + "not_up_wildcard", + "not_self_up_wildcard", + "not_up_disabled", + "up_2_rel_instances", + "up_2_rel_instances_match_2nd", + "up_only_w_owned", + "this_up_trav_unused_rel", + "this_optional_self", + "this_optional_up", + "this_optional_self_up", + "this_written_optional_self", + "this_written_optional_up", + "this_written_optional_self_up", + "fixed_src_w_up", + "match_empty_table_up", + "match_empty_table_up_written", + "match_empty_table_up_implicit_isa", + "match_empty_table_up_written_implicit_isa", + "match_empty_table_up_isa", + "match_empty_table_up_written_isa", + "up_after_add_batched_to_parent", + "up_component_after_parent_table_change", + "up_component_w_singleton_after_parent_table_change", + "up_component_w_var_after_parent_table_change", + "test_up_component_after_parent_table_change", + "test_up_component_w_singleton_after_parent_table_change", + "up_component_after_parent_table_change_no_data", + "up_component_w_singleton_after_parent_table_change_no_data", + "up_component_w_var_after_parent_table_change_no_data", + "test_up_component_after_parent_table_change_no_data", + "test_up_component_w_singleton_after_parent_table_change_no_data", + "this_up_childof_isa_childof", + "this_up_isa_childof", + "this_up_isa_isa_childof", + "this_up_isa_childof_isa", + "this_up_isa_childof_isa_childof", + "this_self_up_childof_isa_childof", + "this_self_up_isa_childof", + "this_self_up_isa_isa_childof", + "this_self_up_isa_childof_isa", + "this_self_up_isa_childof_isa_childof", + "this_written_up_childof_isa_childof", + "this_written_up_isa_childof", + "this_written_up_isa_isa_childof", + "this_written_up_isa_childof_isa", + "this_written_up_isa_childof_isa_childof", + "this_written_self_up_childof_isa_childof", + "this_written_self_up_isa_childof", + "this_written_self_up_isa_isa_childof", + "this_written_self_up_isa_childof_isa", + "this_written_self_up_isa_childof_isa_childof" + ] + }, { + "id": "Cascade", + "testcases": [ + "parent_cascade", + "existing_custom_rel_cascade", + "new_custom_rel_cascade", + "cascade_w_2_depths", + "cascade_w_3_depths", + "cascade_w_2_depths_desc", + "cascade_w_3_depths_desc", + "existing_isa_cascade", + "new_isa_cascade", + "childof_cascade", + "cascade_rematch_2_lvls", + "cascade_rematch_2_lvls_2_relations", + "cascade_topological", + "cascade_desc_rematch_2_lvls", + "cascade_desc_rematch_2_lvls_2_relations", + "cascade_desc_topological", + "cascade_after_recycled_parent_change", + "invalid_cascade_for_uncached", + "invalid_cascade_for_first", + "invalid_cascade_for_second", + "invalid_desc_without_cascade", + "invalid_desc_for_first", + "invalid_desc_for_second" + ] + }, { + "id": "Cached", + "testcases": [ + "simple_query_existing_table", + "simple_query_2_existing_tables", + "simple_query_new_table", + "simple_query_2_new_tables", + "simple_query_existing_and_new_table", + "wildcard_query_existing_table", + "wildcard_query_new_table", + "wildcard_query_existing_table_2_results_p_table", + "wildcard_query_new_table_2_results_p_table", + "wildcard_query_2nd_term", + "wildcard_query_2nd_term_self", + "simple_query_existing_empty_table", + "simple_query_existing_empty_type", + "simple_query_new_empty_table", + "component_query_existing_table", + "component_query_new_table", + "component_query_existing_empty_table", + "2_component_query_existing_empty_table", + "2_component_query_existing_empty_type", + "only_optional", + "only_optional_new_empty_table", + "only_optional_new_empty_non_empty_table", + "only_optional_new_unset_tables", + "singleton_w_optional_new_empty_table", + "singleton_w_optional_new_empty_non_empty_table", + "singleton_w_optional_new_unset_tables", + "query_w_from_entity_match_after", + "query_w_from_singleton_match_after", + "query_rematch_optional_after_add", + "get_owned_tag", + "get_shared_tag", + "explicit_delete", + "get_column_size", + "stresstest_query_free", + "query_optional_tag", + "query_optional_shared_tag", + "query_iter_10_tags", + "query_iter_10_components", + "iter_type_set", + "filter_term", + "2_terms_1_filter", + "3_terms_2_filter", + "add_singleton_after_query", + "query_w_component_from_parent_from_non_this", + "create_query_while_pending", + "empty_query", + "not_pair_relation_wildcard", + "not_pair_object_wildcard", + "two_pair_wildcards_one_not", + "two_pair_wildcards_one_not_any", + "implicit_existing_isa_superset", + "implicit_new_isa_superset", + "isa_superset", + "isa_superset_2_lvls", + "isa_superset_3_lvls", + "isa_superset_2_lvls_owned", + "isa_superset_3_lvls_owned", + "isa_superset_owned_empty_table_after_match", + "isa_self_superset", + "childof_superset", + "superset_2_targets", + "superset_2_relations", + "superset_2_relations_instanced", + "superset_2_relations_w_component", + "superset_2_relations_instanced_w_component", + "parent", + "isa_rematch", + "childof_rematch", + "isa_unmatch", + "childof_unmatch", + "isa_rematch_2_lvls", + "childof_rematch_2_lvls", + "childof_rematch_from_isa", + "rematch_optional_ref", + "rematch_optional_ref_w_2_refs", + "rematch_optional_ref_tag_w_ref_component", + "rematch_after_add_to_recycled_parent", + "match_query_expr_from_scope", + "query_long_or_w_ref", + "query_w_pair_id_and_subj", + "rematch_after_delete_inherited_tag", + "rematch_after_delete_rel_of_inherited_pair", + "rematch_after_delete_obj_of_inherited_pair", + "rematch_empty", + "rematch_empty_table_w_superset", + "2_self_up_terms_new_tables", + "this_self_up_childof_pair_new_tables" + ] + }, { + "id": "ChangeDetection", + "testcases": [ + "query_changed_after_new", + "query_changed_after_delete", + "query_changed_after_add", + "query_changed_after_remove", + "query_changed_after_set", + "query_change_after_modified", + "query_change_after_out_system", + "query_change_after_out_query_no_data_flag", + "query_change_after_in_system", + "query_change_after_modified_out_term", + "query_change_check_iter", + "query_change_check_iter_after_skip_read", + "query_change_check_iter_after_skip_write", + "query_change_parent_term", + "query_change_prefab_term", + "query_change_parent_term_w_tag", + "query_change_prefab_term_w_tag", + "query_changed_w_or", + "query_changed_or", + "query_changed_w_singleton", + "query_changed_w_only_singleton", + "query_changed_w_only_singleton_after_set", + "query_changed_w_only_singleton_after_out_term", + "query_changed_w_only_singleton_after_singleton_out_term", + "query_changed_w_only_parent", + "query_changed_w_only_parent_after_set", + "query_changed_w_only_parent_after_out_term", + "query_changed_w_only_parent_after_parent_out_term", + "query_changed_tag", + "query_changed_no_source", + "query_changed_no_source_component", + "query_changed_w_not_out", + "query_change_w_optional", + "query_changed_after_count" + ] + }, { + "id": "GroupBy", + "testcases": [ + "group_by", + "group_by_w_ctx", + "group_by_w_sort_reverse_group_creation", + "group_by_iter_one", + "group_by_iter_one_all_groups", + "group_by_iter_one_empty", + "group_by_iter_one_empty_query", + "group_by_iter_one_empty_table", + "group_by_w_deleted_group_id", + "group_by_callbacks", + "group_by_default_action", + "group_table_count" + ] + }, { + "id": "MemberTarget", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "this_member_eq_1", + "this_member_eq_2", + "this_member_eq_no_matches", + "this_member_eq_all_matches", + "this_member_wildcard", + "this_member_var", + "this_member_var_written", + "this_member_var_read", + "this_member_eq_1_2nd_member", + "this_member_eq_2_2nd_member", + "this_member_var_same_1", + "this_member_var_same_2", + "this_written_member_eq_1", + "this_written_member_eq_2", + "this_written_member_wildcard", + "this_written_member_var", + "this_member_neq_1", + "this_member_neq_2", + "this_member_neq_no_matches", + "this_member_neq_all_matches", + "this_member_neq_wildcard", + "this_written_member_neq_1", + "this_written_member_neq_2", + "this_written_member_neq_no_matches", + "this_written_member_neq_all_matches", + "this_written_member_neq_wildcard", + "this_member_eq_optional", + "this_member_eq_optional_wildcard", + "this_written_member_eq_optional", + "this_written_member_eq_optional_wildcard", + "this_member_eq_w_other_tag", + "this_member_eq_w_other_component", + "this_written_member_eq_w_other_tag", + "this_written_member_eq_w_other_component", + "this_written_member_eq_w_other_inherit_component", + "this_2_or", + "this_3_or", + "this_written_2_or", + "this_written_3_or", + "var_2_or", + "var_3_or", + "this_2_or_w_2_members", + "this_2_or_w_2_types", + "this_written_2_or_w_2_members", + "this_written_2_or_w_2_types", + "this_2_or_2_types_wildcard", + "this_2_or_2_types_dep_var", + "var_written_2_or", + "var_written_3_or", + "var_member_eq", + "var_member_eq_no_matches", + "var_member_eq_all_matches", + "var_member_wildcard", + "var_member_neq", + "var_member_neq_no_matches", + "var_member_neq_all_matches", + "var_written_member_eq", + "var_written_member_eq_no_matches", + "var_written_member_eq_all_matches", + "var_written_member_wildcard", + "var_written_member_neq", + "var_written_member_neq_no_matches", + "var_written_member_neq_all_matches" + ] + }, { + "id": "Toggle", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "fixed_src_1_tag_toggle", + "fixed_src_1_component_toggle", + "fixed_src_2_tag_toggle", + "fixed_src_2_component_toggle", + "fixed_2_src_w_toggle", + "this_w_fixed_src_w_toggle", + "fixed_src_w_this_w_toggle", + "this_from_nothing", + "this", + "this_skip_initial", + "this_pair", + "this_pair_skip_initial", + "this_tag", + "this_tag_pair", + "this_tag_pair_wildcard", + "this_toggle_shared_self_up", + "this_toggle_shared_up", + "this_toggle_shared_self_up_w_self", + "this_toggle_shared_up_w_self", + "this_toggle_shared_self_up_w_self_reverse", + "this_toggle_shared_up_w_self_reverse", + "this_toggle_shared_self_up_w_self_toggle", + "this_toggle_shared_up_w_self_toggle", + "this_toggle_shared_self_up_w_self_toggle_reverse", + "this_toggle_shared_up_w_self_toggle_reverse", + "this_toggle_not_shared_self_up", + "this_toggle_not_shared_up", + "this_toggle_optional_shared_self_up", + "this_toggle_optional_shared_up", + "this_64_mod_1", + "this_64_mod_2", + "this_64_mod_3", + "this_64_mod_7", + "this_64_mod_8", + "this_64_mod_10", + "this_64_mod_64", + "this_64_mod_256", + "this_64_mod_1024", + "this_100_mod_1", + "this_100_mod_2", + "this_100_mod_3", + "this_100_mod_7", + "this_100_mod_8", + "this_100_mod_10", + "this_100_mod_64", + "this_100_mod_256", + "this_100_mod_1024", + "this_128_mod_1", + "this_128_mod_2", + "this_128_mod_3", + "this_128_mod_7", + "this_128_mod_8", + "this_128_mod_10", + "this_128_mod_64", + "this_128_mod_256", + "this_128_mod_1024", + "this_200_mod_1", + "this_200_mod_2", + "this_200_mod_3", + "this_200_mod_7", + "this_200_mod_8", + "this_200_mod_10", + "this_200_mod_64", + "this_200_mod_256", + "this_200_mod_1024", + "this_1024_mod_1", + "this_1024_mod_2", + "this_1024_mod_3", + "this_1024_mod_7", + "this_1024_mod_8", + "this_1024_mod_10", + "this_1024_mod_64", + "this_1024_mod_256", + "this_1024_mod_1024", + "this_enabled_64_mod_1", + "this_enabled_64_mod_2", + "this_enabled_64_mod_3", + "this_enabled_64_mod_7", + "this_enabled_64_mod_8", + "this_enabled_64_mod_10", + "this_enabled_64_mod_64", + "this_enabled_64_mod_256", + "this_enabled_64_mod_1024", + "this_enabled_100_mod_1", + "this_enabled_100_mod_2", + "this_enabled_100_mod_3", + "this_enabled_100_mod_7", + "this_enabled_100_mod_8", + "this_enabled_100_mod_10", + "this_enabled_100_mod_64", + "this_enabled_100_mod_256", + "this_enabled_100_mod_1024", + "this_enabled_128_mod_1", + "this_enabled_128_mod_2", + "this_enabled_128_mod_3", + "this_enabled_128_mod_7", + "this_enabled_128_mod_8", + "this_enabled_128_mod_10", + "this_enabled_128_mod_64", + "this_enabled_128_mod_256", + "this_enabled_128_mod_1024", + "this_enabled_200_mod_1", + "this_enabled_200_mod_2", + "this_enabled_200_mod_3", + "this_enabled_200_mod_7", + "this_enabled_200_mod_8", + "this_enabled_200_mod_10", + "this_enabled_200_mod_64", + "this_enabled_200_mod_256", + "this_enabled_200_mod_1024", + "this_enabled_1024_mod_1", + "this_enabled_1024_mod_2", + "this_enabled_1024_mod_3", + "this_enabled_1024_mod_7", + "this_enabled_1024_mod_8", + "this_enabled_1024_mod_10", + "this_enabled_1024_mod_64", + "this_enabled_1024_mod_256", + "this_enabled_1024_mod_1024", + "this_mod_2_2_bitsets", + "this_mod_8_2_bitsets", + "this_mod_64_2_bitsets", + "this_mod_256_2_bitsets", + "this_mod_1024_2_bitsets", + "this_randomized_2_bitsets", + "this_randomized_3_bitsets", + "this_randomized_4_bitsets", + "this_w_other_tag", + "this_w_other_component", + "this_not", + "this_written_not_1024_mod_2", + "this_written_not_1024_mod_3", + "this_written_not_1024_mod_7", + "this_written_not_1024_mod_8", + "this_written_not_1024_mod_10", + "this_written_not_1024_mod_64", + "this_written_not_1024_mod_256", + "this_written_not_1024_mod_1024", + "this_optional", + "this_written_optional_1024_mod_2", + "this_written_optional_1024_mod_3", + "this_written_optional_1024_mod_7", + "this_written_optional_1024_mod_8", + "this_written_optional_1024_mod_10", + "this_written_optional_1024_mod_64", + "this_written_optional_1024_mod_256", + "this_written_optional_1024_mod_1024", + "this_written_toggle_w_not_toggle", + "this_written_not_toggle_w_toggle", + "this_written_toggle_w_optional_toggle", + "this_written_optional_toggle_w_toggle", + "this_written_not_w_optional_toggle", + "this_written_optional_w_not_toggle", + "this_written_2_not_toggle", + "this_written_2_optional_toggle", + "this_written_toggle_w_2_not_toggle", + "this_written_toggle_w_2_optional_toggle", + "this_written_2_toggle_w_not_toggle", + "this_written_2_toggle_w_optional_toggle", + "this_sort", + "this_table_move_2_from_3", + "toggle_0_src_only_term", + "toggle_0_src" + ] + }, { + "id": "Sparse", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "1_fixed_sparse", + "1_fixed_sparse_none", + "1_this_sparse_simple", + "1_this_sparse", + "1_this_sparse_none", + "1_this_sparse_written", + "1_this_sparse_written_none", + "1_var_sparse", + "1_var_sparse_none", + "1_var_sparse_written", + "1_var_sparse_written_none", + "2_sparse_simple", + "2_sparse", + "2_sparse_and_regular", + "2_regular_and_sparse", + "1_sparse_self", + "1_sparse_up", + "1_sparse_self_up", + "1_sparse_written_self", + "1_sparse_written_up", + "1_sparse_written_self_up", + "sparse_0_src_only_term", + "sparse_0_src" + ] + }, { + "id": "Union", + "setup": true, + "params": { + "cache_kind": ["default", "auto"] + }, + "testcases": [ + "1_fixed_union_any", + "1_fixed_union_wildcard", + "1_fixed_union_tgt", + "1_fixed_union_tgt_var", + "1_fixed_union_tgt_var_written", + "1_this_union_any", + "1_this_union_wildcard", + "1_this_union_tgt", + "1_this_union_tgt_var", + "1_this_union_tgt_var_written", + "1_var_union_any", + "1_var_union_wildcard", + "1_var_union_tgt", + "1_var_union_tgt_var", + "1_var_union_tgt_var_written", + "1_this_written_union_any", + "1_this_written_union_wildcard", + "1_this_written_union_tgt", + "1_this_written_union_tgt_var", + "1_this_written_union_tgt_var_written", + "1_var_written_union_any", + "1_var_written_union_wildcard", + "1_var_written_union_tgt", + "1_var_written_union_tgt_var", + "1_var_written_union_tgt_var_written", + "not_fixed_union_any", + "not_fixed_union_wildcard", + "not_fixed_union_tgt", + "not_fixed_union_var", + "not_fixed_union_var_written", + "not_this_written_union_any", + "not_this_written_union_wildcard", + "not_this_written_union_tgt", + "not_this_written_union_var", + "not_this_written_union_var_written", + "query_switch", + "query_1_case_1_type", + "query_1_case_2_types", + "query_2_cases_1_type", + "query_2_cases_2_types", + "query_after_remove", + "sort", + "query_recycled_tags", + "query_single_case", + "match_switch_on_base_instance", + "switch_w_bitset_query", + "switch_w_bitset_query_inv", + "switch_w_bitset_query_2_elems", + "switch_w_bitset_query_2_elems_skip", + "switch_w_bitset_query_elems_interleaved", + "switch_w_bitset_query_elems_interleaved_2_types", + "component_relation", + "switch_term_filter", + "2_terms_switch_term_filter", + "match_switch_w_switch", + "match_switch_w_case", + "match_switch_w_case_2_terms", + "up", + "self_up", + "up_written", + "self_up_written", + "existing_union_table", + "new_union_table", + "existing_union_table_w_tgt", + "new_union_table_w_tgt", + "tgt_w_generation", + "tgt_w_not_alive", + "for_switch_filter_term", + "union_from_nothing", + "union_tgt_from_nothing", + "tgt_inherited" + ] + }, { + "id": "OrderBy", + "testcases": [ + "sort_by_component", + "sort_by_component_2_tables", + "sort_by_component_3_tables", + "sort_by_entity", + "sort_after_add", + "sort_after_remove", + "sort_after_delete", + "sort_after_set", + "sort_after_system", + "sort_after_query", + "sort_by_component_same_value_1", + "sort_by_component_same_value_2", + "sort_by_component_move_pivot", + "sort_1000_entities", + "sort_1000_entities_w_duplicates", + "sort_1000_entities_again", + "sort_1000_entities_2_types", + "sort_1500_entities_3_types", + "sort_2000_entities_4_types", + "sort_2_entities_2_types", + "sort_3_entities_3_types", + "sort_3_entities_3_types_2", + "sort_4_entities_4_types", + "sort_1000_entities_2_types_again", + "sort_1000_entities_add_type_after_sort", + "sort_shared_component", + "sort_shared_component_childof", + "sort_w_tags_only", + "sort_childof_marked", + "sort_isa_marked", + "sort_relation_marked", + "dont_resort_after_set_unsorted_component", + "dont_resort_after_set_unsorted_component_w_tag", + "dont_resort_after_set_unsorted_component_w_tag_w_out_term", + "sort_component_not_queried_for", + "sort_by_wildcard", + "sort_shared_w_delete", + "sort_w_nontrivial_component", + "sort_by_wildcard", + "sort_not_term", + "sort_or_term", + "sort_optional_term" + ] + }, { + "id": "OrderByEntireTable", + "testcases": [ + "sort_by_component", + "sort_by_component_2_tables", + "sort_by_component_3_tables", + "sort_by_entity", + "sort_after_add", + "sort_after_remove", + "sort_after_delete", + "sort_after_set", + "sort_after_system", + "sort_after_query", + "sort_by_component_same_value_1", + "sort_by_component_same_value_2", + "sort_by_component_move_pivot", + "sort_1000_entities", + "sort_1000_entities_w_duplicates", + "sort_1000_entities_again", + "sort_1000_entities_2_types", + "sort_1500_entities_3_types", + "sort_2000_entities_4_types", + "sort_2_entities_2_types", + "sort_3_entities_3_types", + "sort_3_entities_3_types_2", + "sort_4_entities_4_types", + "sort_1000_entities_2_types_again", + "sort_1000_entities_add_type_after_sort", + "sort_shared_component", + "sort_w_tags_only", + "sort_childof_marked", + "sort_isa_marked", + "sort_relation_marked", + "dont_resort_after_set_unsorted_component", + "dont_resort_after_set_unsorted_component_w_tag", + "dont_resort_after_set_unsorted_component_w_tag_w_out_term", + "sort_shared_w_delete", + "sort_not_term", + "sort_or_term", + "sort_optional_term" + ] + }, { + "id": "QueryStr", + "testcases": [ + "one_term", + "one_term_w_inout", + "two_terms", + "two_terms_w_inout", + "three_terms_w_or", + "three_terms_w_or_inout", + "four_terms_three_w_or_inout", + "one_term_w_pair", + "one_term_w_pair_entity_src", + "one_term_w_self", + "one_term_w_up", + "one_term_w_self_up", + "one_term_w_cascade", + "one_term_w_0", + "one_term_w_singleton", + "one_term_w_final_pair", + "one_term_w_final_dont_inherit", + "one_term_w_final_inherit", + "one_term_w_final_override", + "one_term_w_src_var", + "one_term_w_first_var", + "one_term_w_second_var", + "one_term_w_first_var_entity_src", + "one_term_w_pair_w_0_entity", + "not_term", + "wildcard_term", + "scopes", + "pred_eq", + "pred_neq", + "pred_eq_name", + "pred_neq_name", + "pred_eq_m", + "pred_neq_m" + ] + }] + } +} diff --git a/vendors/flecs/test/query/src/Basic.c b/vendors/flecs/test/query/src/Basic.c new file mode 100644 index 000000000..15859eea4 --- /dev/null +++ b/vendors/flecs/test/query/src/Basic.c @@ -0,0 +1,11430 @@ +#include +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void Basic_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +void Basic_0_query(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "0", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_1_fact_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_fact_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_fact_w_tag_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_fact_w_component_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set_pair(world, ent, Position, TgtA, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_same_src_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent), RelB(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_same_src_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(ent), Velocity(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Velocity, {1, 2}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_same_src_w_tag_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent, TgtA), RelA(ent, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_same_src_w_component_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(ent, TgtA), Position(ent, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set_pair(world, ent, Position, TgtA, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set_pair(world, ent, Position, TgtB, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(ecs_id(Position), TgtB), ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_other_src_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent1 = ecs_entity(world, { .name = "ent1" }); + ecs_entity_t ent2 = ecs_entity(world, { .name = "ent2" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent1), RelB(ent2)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent1, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent2, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(ent2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_other_src_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t ent1 = ecs_entity(world, { .name = "ent1" }); + ecs_entity_t ent2 = ecs_entity(world, { .name = "ent2" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(ent1), Velocity(ent2)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent1, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent2, Velocity, {1, 2}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ent1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ent2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_other_src_w_tag_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent1 = ecs_entity(world, { .name = "ent1" }); + ecs_entity_t ent2 = ecs_entity(world, { .name = "ent2" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent1, TgtA), RelA(ent2, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent1, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent2, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(ent2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_other_src_w_component_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent1 = ecs_entity(world, { .name = "ent1" }); + ecs_entity_t ent2 = ecs_entity(world, { .name = "ent2" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(ent1, TgtA), Position(ent2, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set_pair(world, ent1, Position, TgtA, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set_pair(world, ent2, Position, TgtB, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 0)); + test_uint(ent1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(ecs_id(Position), TgtB), ecs_field_id(&it, 1)); + test_uint(ent2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_fact_w_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelA); + ecs_add(world, ent, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(EcsWildcard, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_fact_w_pair_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_fact_w_pair_any_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_fact_w_pair_any_rel_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelB, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(ent, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_same_src_w_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(ent), _(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelA); + ecs_add(world, ent, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(EcsWildcard, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_same_src_w_pair_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent, _), RelA(ent, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_same_src_w_pair_any_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(ent, TgtA), _(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_same_src_w_pair_any_rel_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelB, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(ent, _), _(ent, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_other_src_w_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent1 = ecs_entity(world, { .name = "ent1" }); + ecs_entity_t ent2 = ecs_entity(world, { .name = "ent2" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(ent1), _(ent2)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent1, RelA); + ecs_add(world, ent1, RelB); + ecs_add(world, ent2, RelA); + ecs_add(world, ent2, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(EcsWildcard, ecs_field_id(&it, 0)); + test_uint(ent1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + test_uint(ent2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_other_src_w_pair_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent1 = ecs_entity(world, { .name = "ent1" }); + ecs_entity_t ent2 = ecs_entity(world, { .name = "ent2" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent1, _), RelA(ent2, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent1, RelA, TgtA); + ecs_add_pair(world, ent1, RelA, TgtB); + ecs_add_pair(world, ent2, RelA, TgtA); + ecs_add_pair(world, ent2, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(ent1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ent2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_other_src_w_pair_any_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent1 = ecs_entity(world, { .name = "ent1" }); + ecs_entity_t ent2 = ecs_entity(world, { .name = "ent2" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent1, _), RelA(ent2, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent1, RelA, TgtA); + ecs_add_pair(world, ent1, RelA, TgtB); + ecs_add_pair(world, ent2, RelA, TgtA); + ecs_add_pair(world, ent2, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(ent1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ent2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_facts_other_src_w_pair_any_rel_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_entity_t ent1 = ecs_entity(world, { .name = "ent1" }); + ecs_add_pair(world, ent1, RelA, TgtA); + ecs_add_pair(world, ent1, RelB, TgtA); + ecs_entity_t ent2 = ecs_entity(world, { .name = "ent2" }); + ecs_add_pair(world, ent2, RelA, TgtA); + ecs_add_pair(world, ent2, RelB, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(ent1, _), _(ent2, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(ent1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ent2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_this_src_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelA); + ecs_add(world, e2, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_this_src_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 30); test_int(p[1].y, 40); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_this_src_w_tag_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_this_src_w_component_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set_pair(world, e1, Position, TgtA, {10, 20}); + ecs_set_pair(world, e2, Position, TgtA, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 30); test_int(p[1].y, 40); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_this_src_w_tag_2_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, FragmentA); + ECS_TAG(world, FragmentB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_add(world, e1, FragmentA); + ecs_add(world, e2, FragmentB); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelA); + ecs_add(world, e2, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_this_src_w_component_2_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, FragmentA); + ECS_TAG(world, FragmentB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_add(world, e1, FragmentA); + ecs_add(world, e2, FragmentB); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_this_src_w_tag_pair_2_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, FragmentA); + ECS_TAG(world, FragmentB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_add(world, e1, FragmentA); + ecs_add(world, e2, FragmentB); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_this_src_w_component_pair_2_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TgtA); + ECS_TAG(world, FragmentA); + ECS_TAG(world, FragmentB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_add(world, e1, FragmentA); + ecs_add(world, e2, FragmentB); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set_pair(world, e1, Position, TgtA, {10, 20}); + ecs_set_pair(world, e2, Position, TgtA, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(ecs_id(Position), TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_this_src_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this), RelB($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelA); + ecs_add(world, e2, RelA); + ecs_add(world, e1, RelB); + ecs_add(world, e2, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_this_src_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($this), Velocity($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + + ecs_set(world, e1, Velocity, {1, 2}); + ecs_set(world, e2, Velocity, {3, 4}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 30); test_int(p[1].y, 40); + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v[0].x, 1); test_int(v[0].y, 2); + test_int(v[1].x, 3); test_int(v[1].y, 4); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_this_src_ent_src_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this), RelB(e3)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelA); + ecs_add(world, e2, RelA); + ecs_add(world, e3, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_this_src_ent_src_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($this), Velocity(e3)", + .cache_kind = cache_kind, + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + ecs_set(world, e3, Velocity, {1, 2}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 30); test_int(p[1].y, 40); + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v[0].x, 1); test_int(v[0].y, 2); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_ent_src_this_src_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelB(e3), RelA($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelA); + ecs_add(world, e2, RelA); + ecs_add(world, e3, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_ent_src_this_src_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Velocity(e3), Position($this)", + .cache_kind = cache_kind, + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {30, 40}); + ecs_set(world, e3, Velocity, {1, 2}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v[0].x, 1); test_int(v[0].y, 2); + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 30); test_int(p[1].y, 40); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_recycled_tag(void) { + ecs_world_t *world = ecs_mini(); + + ecs_delete(world, ecs_new(world)); + ECS_TAG(world, RelA); + test_assert((uint32_t)RelA != RelA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_recycled_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_delete(world, ecs_new(world)); + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + test_assert((uint32_t)ent != ent); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_recycled_pair_rel(void) { + ecs_world_t *world = ecs_mini(); + + ecs_delete(world, ecs_new(world)); + ECS_TAG(world, RelA); + test_assert((uint32_t)RelA != RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_recycled_pair_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ecs_delete(world, ecs_new(world)); + ECS_TAG(world, TgtA); + test_assert((uint32_t)TgtA != TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this), *($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelA); + ecs_add(world, e1, RelB); + + ecs_add(world, e2, RelA); + ecs_add(world, e2, RelB); + ecs_add(world, e2, RelC); + + ecs_add(world, e3, RelA); + ecs_add(world, e3, RelB); + ecs_add(world, e3, RelC); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelC, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_pair_rel_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this, TgtA), *($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e1, RelB, TgtB); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e2, RelC, TgtA); + ecs_add_pair(world, e2, RelC, TgtB); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + ecs_add_pair(world, e3, RelC, TgtA); + ecs_add_pair(world, e3, RelC, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_pair_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this, TgtA), RelA($this, *)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add_pair(world, e1, RelB, TgtA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtB); + ecs_add_pair(world, e2, RelA, TgtC); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelA, TgtC); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_pair_rel_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this, TgtA), *($this, *)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtB); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "_", + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); + + ecs_iter_t it = ecs_query_iter(world, r); + ecs_entity_t prev = 0; + int32_t count = 0, e1_matched = 0; + while (ecs_query_next(&it)) { + test_assert(it.count > 0); + test_assert(!prev || prev != it.entities[0]); + prev = it.entities[0]; + if (it.entities[0] == e1) { + e1_matched ++; + } + count ++; + } + + test_assert(count > 0); + test_int(e1_matched, 1); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_not_any(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *r = ecs_query(world, { + .expr = "!_", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_any_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this), _($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelA); + ecs_add(world, e1, RelB); + + ecs_add(world, e2, RelA); + ecs_add(world, e2, RelB); + ecs_add(world, e2, RelC); + + ecs_add(world, e3, RelA); + ecs_add(world, e3, RelB); + ecs_add(world, e3, RelC); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_pair_rel_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this, TgtA), _($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e1, RelB, TgtB); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e2, RelC, TgtA); + ecs_add_pair(world, e2, RelC, TgtB); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + ecs_add_pair(world, e3, RelC, TgtA); + ecs_add_pair(world, e3, RelC, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_pair_tgt_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this, TgtA), RelA($this, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add_pair(world, e1, RelB, TgtA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtB); + ecs_add_pair(world, e2, RelA, TgtC); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelA, TgtC); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_pair_tgt_any_n_tgts(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e2, RelA, e1); + ecs_add_pair(world, e1, RelA, e2); + ecs_add_pair(world, e1, RelA, e3); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_pair_tgt_any_n_tgts_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + + ecs_add_pair(world, e2, RelA, e1); + ecs_add_pair(world, e1, RelA, e2); + ecs_add_pair(world, e1, RelA, e3); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, RelA($this, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_src_w_pair_rel_tgt_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this, TgtA), _($this, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtB); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_ent_src_w_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "*(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelA); + ecs_add(world, e1, RelB); + ecs_add(world, e1, RelC); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelC, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_ent_src_w_pair_rel_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "*(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e1, RelC, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_ent_src_w_pair_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent, *)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add_pair(world, e1, RelA, TgtC); + ecs_add_pair(world, e1, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_ent_src_w_pair_rel_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add_pair(world, e1, RelB, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "*(ent, *)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_wildcard_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(*)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_add_id(world, e1, Tag); + ecs_new_w(world, RelA); + ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_wildcard_src_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, Tag); + ECS_TAG(world, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(*, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_add(world, e1, Tag); + ecs_new_w_pair(world, RelA, TgtA); + ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_wildcard_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(*), RelB(*)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_add_id(world, e1, Tag); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelB); + ecs_add(world, e2, RelB); + ecs_add(world, e3, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_wildcard_src_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tag); + ECS_TAG(world, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(*, TgtA), RelB(*, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_add(world, e1, Tag); + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_rule_w_iter_next(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_wildcard_src_w_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(*, $x)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, Rel, TgtA); + ecs_new_w_pair(world, Rel, TgtB); + ecs_new_w_pair(world, Rel, TgtC); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_wildcard_src_w_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "$x(*, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, Tgt); + ecs_new_w_pair(world, RelB, Tgt); + ecs_new_w_pair(world, RelC, Tgt); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_wildcard_src_w_pair_tgt_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(*, $this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int this_var = ecs_query_find_var(r, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, Rel, TgtA); + ecs_new_w_pair(world, Rel, TgtB); + ecs_new_w_pair(world, Rel, TgtC); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, this_var)); + test_uint(TgtA, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, this_var)); + test_uint(TgtB, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtC, ecs_iter_get_var(&it, this_var)); + test_uint(TgtC, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_wildcard_src_w_pair_rel_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "$this(*, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int this_var = ecs_query_find_var(r, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, Tgt); + ecs_new_w_pair(world, RelB, Tgt); + ecs_new_w_pair(world, RelC, Tgt); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, this_var)); + test_uint(RelA, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, this_var)); + test_uint(RelB, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelC, ecs_iter_get_var(&it, this_var)); + test_uint(RelC, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_add_id(world, e1, Tag); + ecs_new_w(world, RelA); + ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(_)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add_id(world, e1, Tag); + ecs_new_w(world, Position); + ecs_new_w(world, Position); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_assert(NULL == ecs_field(&it, Position, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_component_w_this_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(_), Velocity", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t e = ecs_insert(world, ecs_value(Velocity, {10, 20})); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add_id(world, e1, Tag); + ecs_new_w(world, Position); + ecs_new_w(world, Position); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_assert(NULL == ecs_field(&it, Position, 0)); + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 10); + test_int(v->y, 20); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, Tag); + ECS_TAG(world, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_add(world, e1, Tag); + ecs_new_w_pair(world, RelA, TgtA); + ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_any_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_), RelB(_)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_add_id(world, e1, Tag); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelB); + ecs_add(world, e2, RelB); + ecs_add(world, e3, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_any_src_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tag); + ECS_TAG(world, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_, TgtA), RelB(_, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_add(world, e1, Tag); + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_w_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(_, $x)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, Rel, TgtA); + ecs_new_w_pair(world, Rel, TgtB); + ecs_new_w_pair(world, Rel, TgtC); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_w_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "$x(_, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, Tgt); + ecs_new_w_pair(world, RelB, Tgt); + ecs_new_w_pair(world, RelC, Tgt); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_w_pair_tgt_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(_, $this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int this_var = ecs_query_find_var(r, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, Rel, TgtA); + ecs_new_w_pair(world, Rel, TgtB); + ecs_new_w_pair(world, Rel, TgtC); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + test_uint(TgtC, ecs_iter_get_var(&it, this_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_uint(TgtC, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, this_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_uint(TgtB, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, this_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_uint(TgtA, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_w_pair_rel_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "$this(_, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int this_var = ecs_query_find_var(r, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, Tgt); + ecs_new_w_pair(world, RelB, Tgt); + ecs_new_w_pair(world, RelC, Tgt); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 0)); + test_uint(RelC, ecs_iter_get_var(&it, this_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_uint(RelC, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, this_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_uint(RelB, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, this_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_uint(RelA, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_any_tgt_w_rel_fixed(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_any_rel_w_tgt_fixed(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(_, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_w_childof_pair_any_tgt(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *r = ecs_query(world, { + .expr = "ChildOf(_, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_empty(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_pair_tgt_wildcard_empty(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_, *)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtC); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e2); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + ecs_delete(world, e3); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_pair_rel_wildcard_empty(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "*(_, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelC, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e2); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + ecs_delete(world, e3); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_any_tgt_w_rel_fixed_empty(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + ecs_delete(world, e2); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_any_rel_w_tgt_fixed_empty(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *r = ecs_query(world, { + .expr = "_(_, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + ecs_delete(world, e2); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_pair_w_tag_query_empty(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + ecs_delete(world, e2); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_1_any_src_tag_w_pair_query_empty(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_, _)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_empty_rule(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_query_t *r = ecs_query(world, { + .expr = "", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_invalid_rule(void) { + ecs_log_set_level(-4); + + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo", + .cache_kind = cache_kind + }); + + test_assert(r == NULL); + + ecs_fini(world); +} + +void Basic_instanced_table_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($this)", + .cache_kind = cache_kind, + }); + + test_assert(r != NULL); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + + ecs_iter_t it = ecs_query_iter(world, r); + { + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 20); test_int(p[1].y, 30); + test_int(p[2].x, 30); test_int(p[2].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_instanced_entity_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e1)", + .cache_kind = cache_kind, + }); + + test_assert(r != NULL); + + ecs_set(world, e1, Position, {10, 20}); + + ecs_iter_t it = ecs_query_iter(world, r); + { + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_instanced_mixed_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e0 = ecs_entity(world, { .name = "e0" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($this), Velocity(e0)", + .cache_kind = cache_kind, + }); + + test_assert(r != NULL); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + + ecs_set(world, e0, Velocity, {1, 2}); + + ecs_iter_t it = ecs_query_iter(world, r); + { + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 20); test_int(p[1].y, 30); + test_int(p[2].x, 30); test_int(p[2].y, 40); + + const Velocity *v = ecs_field(&it, Velocity, 1); + test_int(v->x, 1); + test_int(v->y, 2); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_instanced_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t base = ecs_insert(world, ecs_value(Velocity, {1, 2})); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {40, 50})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {50, 60})); + ecs_add(world, e4, Tag); + ecs_add(world, e5, Tag); + + ecs_add_pair(world, e1, EcsIsA, base); + ecs_add_pair(world, e2, EcsIsA, base); + ecs_add_pair(world, e3, EcsIsA, base); + ecs_add_pair(world, e4, EcsIsA, base); + ecs_add_pair(world, e5, EcsIsA, base); + + ecs_entity_t e6 = ecs_insert(world, ecs_value(Position, {60, 70})); + ecs_entity_t e7 = ecs_insert(world, ecs_value(Position, {70, 80})); + ecs_set(world, e6, Velocity, {2, 3}); + ecs_set(world, e7, Velocity, {4, 5}); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind, + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_int(it.count, 3); + test_int(it.entities[0], e1); + test_int(it.entities[1], e2); + test_int(it.entities[2], e3); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + test_int(p[2].x, 30); + test_int(p[2].y, 40); + test_int(v[0].x, 1); + test_int(v[0].y, 2); + } + + test_assert(ecs_query_next(&it)); + { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_int(it.count, 2); + test_int(it.entities[0], e4); + test_int(it.entities[1], e5); + test_int(p[0].x, 40); + test_int(p[0].y, 50); + test_int(p[1].x, 50); + test_int(p[1].y, 60); + test_int(v->x, 1); + test_int(v->y, 2); + } + + test_assert(ecs_query_next(&it)); + { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_int(it.count, 2); + test_int(it.entities[0], e6); + test_int(p[0].x, 60); + test_int(p[0].y, 70); + test_int(v[0].x, 2); + test_int(v[0].y, 3); + + test_int(it.entities[1], e7); + test_int(p[1].x, 70); + test_int(p[1].y, 80); + test_int(v[1].x, 4); + test_int(v[1].y, 5); + } + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_in_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "[in] Position(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_readonly(&it, 0)); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_out_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "[out] Position(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_readonly(&it, 0)); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_inout_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "[inout] Position(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_readonly(&it, 0)); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_nodata_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "[none] Position(ent)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_readonly(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_find_this_lowercase(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int this_var = ecs_query_find_var(r, "this"); + test_assert(this_var != -1); + test_assert(this_var == 0); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_find_this_uppercase(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel($this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int this_var = ecs_query_find_var(r, "this"); + test_assert(this_var != -1); + test_assert(this_var == 0); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_find_this_tgt_lowercase(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(*, $this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int this_var = ecs_query_find_var(r, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_find_this_tgt_uppercase(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(*, $this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int this_var = ecs_query_find_var(r, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_get_filter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + const ecs_query_t *f = r; + test_assert(f != NULL); + test_int(f->term_count, 1); + test_uint(f->terms[0].id, Rel); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_iter_empty_source(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo()", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_id(Foo), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_iter_empty_source_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *r = ecs_query(world, { + .expr = "*()", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(EcsWildcard, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_iter_empty_source_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(), Bar()", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_id(Foo), ecs_field_id(&it, 0)); + test_uint(ecs_id(Bar), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_iter_empty_source_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(#0, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Foo, Tgt), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_iter_empty_source_pair_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "*(#0, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, Tgt), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_iter_empty_source_2_terms_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(#0, Tgt), Bar(#0, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Foo, Tgt), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Bar, Tgt), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_iter_empty_source_2_terms_mixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, Bar()", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_iter_empty_source_2_terms_mixed_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "(Foo, Tgt), Bar(#0, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Foo, Tgt); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_pair(Foo, Tgt), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Bar, Tgt), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_iter_empty_source_2_terms_mixed_pair_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "(Foo, Tgt), *(#0, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Foo, Tgt); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_pair(Foo, Tgt), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, Tgt), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_this_var_w_empty_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_entity_t t = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, Rel, t); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel($x, $this)", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(t, it.entities[0]); + test_uint(ecs_pair(Rel, t), ecs_field_id(&it, 0)); + test_uint(e, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_match_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, Disabled", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, Prefab", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_disabled_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + ecs_entity_t e_4 = ecs_new_w(world, TagA); + ecs_add_id(world, e_4, EcsPrefab); + ecs_add_id(world, e_4, EcsDisabled); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, Disabled", + .cache_kind = cache_kind + }); + + ecs_query_t *r_3 = ecs_query(world, { + .expr = "TagA, Prefab", + .cache_kind = cache_kind + }); + + ecs_query_t *r_4 = ecs_query(world, { + .expr = "TagA, Prefab, Disabled", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + test_assert(r_3 != NULL); + test_assert(r_4 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + test_bool(r_3->flags & EcsQueryMatchDisabled, false); + test_bool(r_4->flags & EcsQueryMatchDisabled, true); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, false); + test_bool(r_3->flags & EcsQueryMatchPrefab, true); + test_bool(r_4->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_assert(!ecs_query_next(&it)); + + { + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_3); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_4); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_4); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + } + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + ecs_query_fini(r_3); + ecs_query_fini(r_4); + + ecs_fini(world); +} + +void Basic_match_disabled_this_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_new_w_pair(world, EcsChildOf, e_1); + ecs_new_w_pair(world, EcsChildOf, e_2); + ecs_new_w_pair(world, EcsChildOf, e_3); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), Disabled", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_prefab_this_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_new_w_pair(world, EcsChildOf, e_1); + ecs_new_w_pair(world, EcsChildOf, e_2); + ecs_new_w_pair(world, EcsChildOf, e_3); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), Prefab", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_disabled_prefab_this_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + ecs_entity_t e_4 = ecs_new_w(world, TagA); + ecs_add_id(world, e_4, EcsPrefab); + ecs_add_id(world, e_4, EcsDisabled); + + ecs_new_w_pair(world, EcsChildOf, e_1); + ecs_new_w_pair(world, EcsChildOf, e_2); + ecs_new_w_pair(world, EcsChildOf, e_3); + ecs_new_w_pair(world, EcsChildOf, e_4); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), Disabled", + .cache_kind = cache_kind + }); + + ecs_query_t *r_3 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), Prefab", + .cache_kind = cache_kind + }); + + ecs_query_t *r_4 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), Prefab, Disabled", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + test_assert(r_3 != NULL); + test_assert(r_4 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + test_bool(r_3->flags & EcsQueryMatchDisabled, false); + test_bool(r_4->flags & EcsQueryMatchDisabled, true); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, false); + test_bool(r_3->flags & EcsQueryMatchPrefab, true); + test_bool(r_4->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_assert(!ecs_query_next(&it)); + + { + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_3); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_4); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_4); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + } + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + ecs_query_fini(r_3); + ecs_query_fini(r_4); + + ecs_fini(world); +} + +void Basic_match_self_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA(self), Disabled(self)", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_self_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA(self), Prefab(self)", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_self_disabled_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + ecs_entity_t e_4 = ecs_new_w(world, TagA); + ecs_add_id(world, e_4, EcsPrefab); + ecs_add_id(world, e_4, EcsDisabled); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA(self), Disabled(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_3 = ecs_query(world, { + .expr = "TagA(self), Prefab(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_4 = ecs_query(world, { + .expr = "TagA(self), Prefab(self), Disabled(self)", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + test_assert(r_3 != NULL); + test_assert(r_4 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + test_bool(r_3->flags & EcsQueryMatchDisabled, false); + test_bool(r_4->flags & EcsQueryMatchDisabled, true); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, false); + test_bool(r_3->flags & EcsQueryMatchPrefab, true); + test_bool(r_4->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_assert(!ecs_query_next(&it)); + + { + it = ecs_query_iter(world, r_2); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_3); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_4); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_4); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + } + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + ecs_query_fini(r_3); + ecs_query_fini(r_4); + + ecs_fini(world); +} + +void Basic_match_optional_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, ?Disabled", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_optional_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, ?Prefab", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_optional_disabled_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + ecs_entity_t e_4 = ecs_new_w(world, TagA); + ecs_add_id(world, e_4, EcsPrefab); + ecs_add_id(world, e_4, EcsDisabled); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, ?Disabled", + .cache_kind = cache_kind + }); + + ecs_query_t *r_3 = ecs_query(world, { + .expr = "TagA, ?Prefab", + .cache_kind = cache_kind + }); + + ecs_query_t *r_4 = ecs_query(world, { + .expr = "TagA, ?Prefab, ?Disabled", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + test_assert(r_3 != NULL); + test_assert(r_4 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + test_bool(r_3->flags & EcsQueryMatchDisabled, false); + test_bool(r_4->flags & EcsQueryMatchDisabled, true); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, false); + test_bool(r_3->flags & EcsQueryMatchPrefab, true); + test_bool(r_4->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_assert(!ecs_query_next(&it)); + + { + it = ecs_query_iter(world, r_2); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_3); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_4); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_4); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + } + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + ecs_query_fini(r_3); + ecs_query_fini(r_4); + + ecs_fini(world); +} + +void Basic_match_optional_disabled_this_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_new_w_pair(world, EcsChildOf, e_1); + ecs_new_w_pair(world, EcsChildOf, e_2); + ecs_new_w_pair(world, EcsChildOf, e_3); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), ?Disabled", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_optional_prefab_this_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_new_w_pair(world, EcsChildOf, e_1); + ecs_new_w_pair(world, EcsChildOf, e_2); + ecs_new_w_pair(world, EcsChildOf, e_3); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), ?Prefab", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_optional_disabled_prefab_this_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + ecs_entity_t e_4 = ecs_new_w(world, TagA); + ecs_add_id(world, e_4, EcsPrefab); + ecs_add_id(world, e_4, EcsDisabled); + + ecs_new_w_pair(world, EcsChildOf, e_1); + ecs_new_w_pair(world, EcsChildOf, e_2); + ecs_new_w_pair(world, EcsChildOf, e_3); + ecs_new_w_pair(world, EcsChildOf, e_4); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), ?Disabled", + .cache_kind = cache_kind + }); + + ecs_query_t *r_3 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), ?Prefab", + .cache_kind = cache_kind + }); + + ecs_query_t *r_4 = ecs_query(world, { + .expr = "TagA, ChildOf($child, $this), ?Prefab, ?Disabled", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + test_assert(r_3 != NULL); + test_assert(r_4 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + test_bool(r_3->flags & EcsQueryMatchDisabled, false); + test_bool(r_4->flags & EcsQueryMatchDisabled, true); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, false); + test_bool(r_3->flags & EcsQueryMatchPrefab, true); + test_bool(r_4->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_assert(!ecs_query_next(&it)); + + { + it = ecs_query_iter(world, r_2); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_3); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_4); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_4); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + } + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + ecs_query_fini(r_3); + ecs_query_fini(r_4); + + ecs_fini(world); +} + +void Basic_match_optional_self_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA(self), ?Disabled(self)", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_optional_self_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA(self), ?Prefab(self)", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, r_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + + ecs_fini(world); +} + +void Basic_match_optional_self_disabled_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, EcsDisabled); + ecs_entity_t e_3 = ecs_new_w(world, TagA); + ecs_add_id(world, e_3, EcsPrefab); + ecs_entity_t e_4 = ecs_new_w(world, TagA); + ecs_add_id(world, e_4, EcsPrefab); + ecs_add_id(world, e_4, EcsDisabled); + + ecs_query_t *r_1 = ecs_query(world, { + .expr = "TagA(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_2 = ecs_query(world, { + .expr = "TagA(self), ?Disabled(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_3 = ecs_query(world, { + .expr = "TagA(self), ?Prefab(self)", + .cache_kind = cache_kind + }); + + ecs_query_t *r_4 = ecs_query(world, { + .expr = "TagA(self), ?Prefab(self), ?Disabled(self)", + .cache_kind = cache_kind + }); + + test_assert(r_1 != NULL); + test_assert(r_2 != NULL); + test_assert(r_3 != NULL); + test_assert(r_4 != NULL); + + test_bool(r_1->flags & EcsQueryMatchDisabled, false); + test_bool(r_2->flags & EcsQueryMatchDisabled, true); + test_bool(r_3->flags & EcsQueryMatchDisabled, false); + test_bool(r_4->flags & EcsQueryMatchDisabled, true); + + test_bool(r_1->flags & EcsQueryMatchPrefab, false); + test_bool(r_2->flags & EcsQueryMatchPrefab, false); + test_bool(r_3->flags & EcsQueryMatchPrefab, true); + test_bool(r_4->flags & EcsQueryMatchPrefab, true); + + ecs_iter_t it = ecs_query_iter(world, r_1); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_assert(!ecs_query_next(&it)); + + { + it = ecs_query_iter(world, r_2); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_3); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(!ecs_query_next(&it)); + } + + { + it = ecs_query_iter(world, r_4); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_3); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_4); + test_int(ecs_field_id(&it, 0), TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_assert(!ecs_query_next(&it)); + } + + ecs_query_fini(r_1); + ecs_query_fini(r_2); + ecs_query_fini(r_3); + ecs_query_fini(r_4); + + ecs_fini(world); +} + +void Basic_inout_none_first_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *r = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Velocity) } + }, + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_inout_none_first_term_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *r = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone, .src.id = EcsSelf|EcsUp }, + { .id = ecs_id(Velocity), .src.id = EcsSelf|EcsUp } + }, + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_inout_none_second_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *r = ecs_query(world, { + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity), .inout = EcsInOutNone } + }, + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_inout_none_second_term_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *r = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .src.id = EcsSelf|EcsUp }, + { .id = ecs_id(Velocity), .inout = EcsInOutNone, .src.id = EcsSelf|EcsUp } + }, + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_inout_none_singleton(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_singleton_set(ecs, Position, {10, 20}); + + ecs_entity_t e = ecs_insert(ecs, ecs_value(Velocity, {20, 30})); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position), .src.id = EcsVariable }, + { .id = ecs_id(Velocity), .inout = EcsInOutNone }, + }, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(ecs, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Basic_inout_none_singleton_w_or(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + ECS_COMPONENT(ecs, Mass); + + ecs_singleton_set(ecs, Position, {10, 20}); + + ecs_entity_t e = ecs_insert(ecs, ecs_value(Velocity, {20, 30})); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position), .src.id = EcsVariable }, + { .id = ecs_id(Velocity), .inout = EcsInOutNone, .oper = EcsOr }, + { .id = ecs_id(Mass), .inout = EcsInOutNone }, + }, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(ecs, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Basic_inout_none_component_w_or(void) { + ecs_world_t *ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + ECS_COMPONENT(ecs, Mass); + + ecs_entity_t e = ecs_insert(ecs, ecs_value(Velocity, {20, 30})); + ecs_set(ecs, e, Position, {10, 20}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity), .inout = EcsInOutNone, .oper = EcsOr }, + { .id = ecs_id(Mass), .inout = EcsInOutNone }, + }, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(ecs, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Basic_no_data_rule(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *r = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Velocity), .inout = EcsInOutNone } + }, + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_frame_offset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *f = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), } + }, + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + ecs_entity_t e4 = ecs_new_w(world, Position); + ecs_entity_t e5 = ecs_new_w(world, Position); + + ecs_add(world, e3, TagB); + ecs_add(world, e4, TagB); + ecs_add(world, e5, TagC); + + ecs_iter_t it = ecs_query_iter(world, f); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.frame_offset, 0); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e1); + test_assert(it.entities[1] == e2); + test_assert(it.ids[0] == ecs_id(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.frame_offset, 2); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e3); + test_assert(it.entities[1] == e4); + test_assert(it.ids[0] == ecs_id(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.frame_offset, 4); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e5); + test_assert(it.ids[0] == ecs_id(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_frame_offset_no_data(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *f = ecs_query(world, { + .terms = { + { .id = TagA, } + }, + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); + ecs_entity_t e5 = ecs_new_w(world, TagA); + + ecs_add(world, e3, TagB); + ecs_add(world, e4, TagB); + ecs_add(world, e5, TagC); + + ecs_iter_t it = ecs_query_iter(world, f); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.frame_offset, 0); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e1); + test_assert(it.entities[1] == e2); + test_assert(it.ids[0] == TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.frame_offset, 2); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e3); + test_assert(it.entities[1] == e4); + test_assert(it.ids[0] == TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.frame_offset, 4); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e5); + test_assert(it.ids[0] == TagA); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_match_empty_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + ecs_entity_t e4 = ecs_new_w(world, Position); + + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagB); + ecs_add(world, e4, TagC); + + ecs_table_t *t1 = ecs_get_table(world, e1); + ecs_table_t *t2 = ecs_get_table(world, e2); + ecs_table_t *t3 = ecs_get_table(world, e3); + ecs_table_t *t4 = ecs_get_table(world, e4); + + ecs_delete(world, e1); + ecs_delete(world, e2); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ ecs_id(Position) }}, + .flags = EcsQueryMatchEmptyTables, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t1); + test_int(it.count, 0); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t2); + test_int(it.count, 0); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t3); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t4); + test_int(it.count, 1); + test_int(it.entities[0], e4); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), false); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_match_empty_tables_no_data(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_entity_t e4 = ecs_new_w(world, Foo); + + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagB); + ecs_add(world, e4, TagC); + + ecs_table_t *t1 = ecs_get_table(world, e1); + ecs_table_t *t2 = ecs_get_table(world, e2); + ecs_table_t *t3 = ecs_get_table(world, e3); + ecs_table_t *t4 = ecs_get_table(world, e4); + + ecs_delete(world, e1); + ecs_delete(world, e2); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ Foo }}, + .flags = EcsQueryMatchEmptyTables, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t1); + test_int(it.count, 0); + test_uint(ecs_field_id(&it, 0), Foo); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t2); + test_int(it.count, 0); + test_uint(ecs_field_id(&it, 0), Foo); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t3); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_uint(ecs_field_id(&it, 0), Foo); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t4); + test_int(it.count, 1); + test_int(it.entities[0], e4); + test_uint(ecs_field_id(&it, 0), Foo); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), false); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_match_empty_tables_w_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_entity_t e4 = ecs_new_w(world, Foo); + + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagB); + ecs_add(world, e4, TagC); + + ecs_table_t *t1 = ecs_get_table(world, e1); + ecs_get_table(world, e2); + ecs_table_t *t3 = ecs_get_table(world, e3); + ecs_table_t *t4 = ecs_get_table(world, e4); + + ecs_delete(world, e1); + ecs_delete(world, e2); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ Foo }, { TagA, .oper = EcsNot }}, + .flags = EcsQueryMatchEmptyTables, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t1); + test_int(it.count, 0); + test_uint(ecs_field_id(&it, 0), Foo); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t3); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_uint(ecs_field_id(&it, 0), Foo); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t4); + test_int(it.count, 1); + test_int(it.entities[0], e4); + test_uint(ecs_field_id(&it, 0), Foo); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), false); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_match_empty_tables_w_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, TgtD); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtC); + ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtD); + + ecs_table_t *t1 = ecs_get_table(world, e1); + ecs_table_t *t2 = ecs_get_table(world, e2); + ecs_table_t *t3 = ecs_get_table(world, e3); + ecs_table_t *t4 = ecs_get_table(world, e4); + + ecs_delete(world, e1); + ecs_delete(world, e2); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ ecs_pair(Rel, EcsWildcard) }}, + .flags = EcsQueryMatchEmptyTables, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t1); + test_int(it.count, 0); + test_uint(ecs_field_id(&it, 0), ecs_pair(Rel, TgtA)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t2); + test_int(it.count, 0); + test_uint(ecs_field_id(&it, 0), ecs_pair(Rel, TgtB)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t3); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_uint(ecs_field_id(&it, 0), ecs_pair(Rel, TgtC)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t4); + test_int(it.count, 1); + test_int(it.entities[0], e4); + test_uint(ecs_field_id(&it, 0), ecs_pair(Rel, TgtD)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), false); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_match_empty_tables_w_no_empty_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *f = ecs_query(world, { + .terms = { + { Foo } + }, + .flags = EcsQueryMatchEmptyTables, + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add(world, e2, Bar); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_match_empty_tables_trivial(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + ecs_entity_t e4 = ecs_new_w(world, Position); + + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagB); + ecs_add(world, e4, TagC); + + ecs_table_t *t1 = ecs_get_table(world, e1); + ecs_table_t *t2 = ecs_get_table(world, e2); + ecs_table_t *t3 = ecs_get_table(world, e3); + ecs_table_t *t4 = ecs_get_table(world, e4); + + ecs_delete(world, e1); + ecs_delete(world, e2); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ ecs_id(Position), .src.id = EcsSelf }}, + .flags = EcsQueryMatchEmptyTables, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t1); + test_int(it.count, 0); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t2); + test_int(it.count, 0); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t3); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), true); + test_assert(it.table == t4); + test_int(it.count, 1); + test_int(it.entities[0], e4); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool( ecs_query_next(&it), false); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_oneof_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Parent); + ECS_ENTITY(world, Rel, (OneOf, Parent)); + ECS_ENTITY(world, ObjA, (ChildOf, Parent)); + ECS_ENTITY(world, ObjB, (ChildOf, Parent)); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); + test_assert( ecs_has_pair(world, e1, Rel, ObjA)); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); + test_assert( ecs_has_pair(world, e2, Rel, ObjB)); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ + .id = ecs_pair(Rel, EcsWildcard) + }}, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_oneof_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Parent); + ECS_ENTITY(world, Rel, (OneOf, Parent)); + ECS_ENTITY(world, ObjA, (ChildOf, Parent)); + ECS_ENTITY(world, ObjB, (ChildOf, Parent)); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); + test_assert( ecs_has_pair(world, e1, Rel, ObjA)); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); + test_assert( ecs_has_pair(world, e2, Rel, ObjB)); + + ecs_query_t *f = ecs_query(world, { + .terms = {{ + .id = ecs_pair(Rel, EcsAny) + }}, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, EcsWildcard), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, EcsWildcard), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_instanced_w_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_singleton_set(world, Velocity, {1, 2}); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {40, 50})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_add(world, e4, Tag); + ecs_add(world, e5, Tag); + + ecs_query_t *f = ecs_query(world, { + .expr = "Position, Velocity($)", + .cache_kind = cache_kind, + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_assert(ecs_query_next(&it)); + { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(p != NULL); + test_assert(v != NULL); + test_int(it.count, 3); + test_int(it.entities[0], e1); + test_int(it.entities[1], e2); + test_int(it.entities[2], e3); + test_int(v->x, 1); + test_int(v->y, 2); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + test_int(p[2].x, 30); + test_int(p[2].y, 40); + } + + test_assert(ecs_query_next(&it)); + { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(p != NULL); + test_assert(v != NULL); + test_int(it.count, 2); + test_int(it.entities[0], e4); + test_int(it.entities[1], e5); + test_int(p[0].x, 40); + test_int(p[0].y, 50); + test_int(p[1].x, 50); + test_int(p[1].y, 60); + test_int(v->x, 1); + test_int(v->y, 2); + } + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_instanced_w_base(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Velocity, {1, 2})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {80, 90})); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {40, 50})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {50, 60})); + ecs_add(world, e4, Tag); + ecs_add(world, e5, Tag); + + ecs_add_pair(world, e1, EcsIsA, base_1); + ecs_add_pair(world, e2, EcsIsA, base_1); + ecs_add_pair(world, e3, EcsIsA, base_1); + ecs_add_pair(world, e4, EcsIsA, base_1); + ecs_add_pair(world, e5, EcsIsA, base_1); + + ecs_entity_t e6 = ecs_insert(world, ecs_value(Position, {60, 70})); + ecs_entity_t e7 = ecs_insert(world, ecs_value(Position, {70, 80})); + ecs_set(world, e6, Velocity, {2, 3}); + ecs_set(world, e7, Velocity, {4, 5}); + + ecs_entity_t e8 = ecs_insert(world, ecs_value(Velocity, {6, 7})); + ecs_entity_t e9 = ecs_insert(world, ecs_value(Velocity, {8, 9})); + ecs_add_pair(world, e8, EcsIsA, base_2); + ecs_add_pair(world, e9, EcsIsA, base_2); + + ecs_query_t *f = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind, + }); + + ecs_iter_t it = ecs_query_iter(world, f); + + test_assert(ecs_query_next(&it)); + { + test_bool(ecs_field_is_self(&it, 0), false); + test_bool(ecs_field_is_self(&it, 1), true); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(p != NULL); + test_assert(v != NULL); + test_int(it.count, 2); + test_int(it.entities[0], e8); + test_int(it.entities[1], e9); + test_int(p->x, 80); + test_int(p->y, 90); + test_int(v[0].x, 6); + test_int(v[0].y, 7); + test_int(v[1].x, 8); + test_int(v[1].y, 9); + } + + test_assert(ecs_query_next(&it)); + { + test_bool(ecs_field_is_self(&it, 0), true); + test_bool(ecs_field_is_self(&it, 1), false); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(p != NULL); + test_assert(v != NULL); + test_int(it.count, 3); + test_int(it.entities[0], e1); + test_int(it.entities[1], e2); + test_int(it.entities[2], e3); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + test_int(p[2].x, 30); + test_int(p[2].y, 40); + test_int(v->x, 1); + test_int(v->y, 2); + } + + test_assert(ecs_query_next(&it)); + { + test_bool(ecs_field_is_self(&it, 0), true); + test_bool(ecs_field_is_self(&it, 1), false); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(p != NULL); + test_assert(v != NULL); + test_int(it.count, 2); + test_int(it.entities[0], e4); + test_int(it.entities[1], e5); + test_int(p[0].x, 40); + test_int(p[0].y, 50); + test_int(p[1].x, 50); + test_int(p[1].y, 60); + test_int(v->x, 1); + test_int(v->y, 2); + } + + test_assert(ecs_query_next(&it)); + { + test_bool(ecs_field_is_self(&it, 0), true); + test_bool(ecs_field_is_self(&it, 1), true); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(p != NULL); + test_assert(v != NULL); + test_int(it.count, 2); + test_int(it.entities[0], e6); + test_int(p[0].x, 60); + test_int(p[0].y, 70); + test_int(v[0].x, 2); + test_int(v[0].y, 3); + + test_int(it.entities[1], e7); + test_int(p[1].x, 70); + test_int(p[1].y, 80); + test_int(v[1].x, 4); + test_int(v[1].y, 5); + } + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Basic_unknown_before_known(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, ChildOf($gc, $c), ChildOf($c, $this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int c_var = ecs_query_find_var(r, "c"); + test_assert(c_var != -1); + int gc_var = ecs_query_find_var(r, "gc"); + test_assert(gc_var != -1); + + ecs_entity_t p = ecs_new_w(world, Foo); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); + ecs_entity_t gc = ecs_new_w_pair(world, EcsChildOf, c); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(c, ecs_iter_get_var(&it, c_var)); + test_uint(gc, ecs_iter_get_var(&it, gc_var)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_unknown_before_known_after_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, ChildOf($gc, $c), Foo($gc) || Bar($gc), ChildOf($c, $this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int c_var = ecs_query_find_var(r, "c"); + test_assert(c_var != -1); + int gc_var = ecs_query_find_var(r, "gc"); + test_assert(gc_var != -1); + + ecs_entity_t p = ecs_new_w(world, Foo); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); + ecs_entity_t gc1 = ecs_new_w_pair(world, EcsChildOf, c); + ecs_add(world, gc1, Foo); + ecs_entity_t gc2 = ecs_new_w_pair(world, EcsChildOf, c); + ecs_add(world, gc2, Bar); + /* ecs_entity_t gc3 = */ ecs_new_w_pair(world, EcsChildOf, c); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 3)); + test_uint(c, ecs_iter_get_var(&it, c_var)); + test_uint(gc1, ecs_iter_get_var(&it, gc_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 3)); + test_uint(c, ecs_iter_get_var(&it, c_var)); + test_uint(gc2, ecs_iter_get_var(&it, gc_var)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_unknown_before_known_after_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, ChildOf($gc, $c), !Foo($gc), ChildOf($c, $this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int c_var = ecs_query_find_var(r, "c"); + test_assert(c_var != -1); + int gc_var = ecs_query_find_var(r, "gc"); + test_assert(gc_var != -1); + + ecs_entity_t p = ecs_new_w(world, Foo); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); + ecs_entity_t gc1 = ecs_new_w_pair(world, EcsChildOf, c); + ecs_entity_t gc2 = ecs_new_w_pair(world, EcsChildOf, c); + ecs_add(world, gc2, Foo); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 2)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + test_uint(c, ecs_iter_get_var(&it, c_var)); + test_uint(gc1, ecs_iter_get_var(&it, gc_var)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_unknown_before_known_after_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, ChildOf($gc, $c), ?Foo($gc), ChildOf($c, $this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int c_var = ecs_query_find_var(r, "c"); + test_assert(c_var != -1); + int gc_var = ecs_query_find_var(r, "gc"); + test_assert(gc_var != -1); + + ecs_entity_t p = ecs_new_w(world, Foo); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); + ecs_entity_t gc1 = ecs_new_w_pair(world, EcsChildOf, c); + ecs_entity_t gc2 = ecs_new_w_pair(world, EcsChildOf, c); + ecs_add(world, gc2, Foo); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 2)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + test_uint(c, ecs_iter_get_var(&it, c_var)); + test_uint(gc1, ecs_iter_get_var(&it, gc_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, c), ecs_field_id(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 2)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + test_uint(c, ecs_iter_get_var(&it, c_var)); + test_uint(gc2, ecs_iter_get_var(&it, gc_var)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_unknown_before_known_after_scope(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, ChildOf($gc, $c), !{Foo($gc) || Bar($gc)}, ChildOf($c, $this)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int c_var = ecs_query_find_var(r, "c"); + test_assert(c_var != -1); + int gc_var = ecs_query_find_var(r, "gc"); + test_assert(gc_var != -1); + + ecs_entity_t p = ecs_new_w(world, Foo); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p); + /* ecs_entity_t c2 = */ ecs_new_w_pair(world, EcsChildOf, p); + ecs_entity_t gc1 = ecs_new_w_pair(world, EcsChildOf, c); + ecs_entity_t gc2 = ecs_new_w_pair(world, EcsChildOf, c); + ecs_add(world, gc2, Foo); + ecs_entity_t gc3 = ecs_new_w_pair(world, EcsChildOf, c); + ecs_add(world, gc3, Bar); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(c, ecs_iter_get_var(&it, c_var)); + test_uint(gc1, ecs_iter_get_var(&it, gc_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_trivial_mixed_2_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Mass); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Tag); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self), Bar(self), Mass(ent|self)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Foo); + ecs_add(world, e1, Bar); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Foo); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_new(world); + ecs_add(world, e3, Foo); + ecs_add(world, e3, Bar); + ecs_add(world, e3, Tag); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Mass, {100}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ent, ecs_field_src(&it, 2)); + + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 100); + } + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e3, it.entities[0]); + + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ent, ecs_field_src(&it, 2)); + + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 100); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_trivial_mixed_2_tables_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_TAG(world, Tag); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(self), Velocity(self), Mass(ent|self)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_entity_t e2 = ecs_new(world); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e2, Velocity, {2, 3}); + + ecs_entity_t e3 = ecs_new(world); + ecs_set(world, e3, Position, {30, 40}); + ecs_set(world, e3, Velocity, {3, 4}); + ecs_add(world, e3, Tag); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Mass, {100}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ent, ecs_field_src(&it, 2)); + + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 20); test_int(p[1].y, 30); + + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v[0].x, 1); test_int(v[0].y, 2); + test_int(v[1].x, 2); test_int(v[1].y, 3); + + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 100); + } + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e3, it.entities[0]); + + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ent, ecs_field_src(&it, 2)); + + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); test_int(p[0].y, 40); + + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v[0].x, 3); test_int(v[0].y, 4); + + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 100); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_trivial_mixed_2_tables_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Mass); + ECS_TAG(world, Foo); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Tag); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self), ChildOf(self, *), Mass(ent|self)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Foo); + ecs_add_pair(world, e1, EcsChildOf, TgtA); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Foo); + ecs_add_pair(world, e2, EcsChildOf, TgtA); + + ecs_entity_t e3 = ecs_new(world); + ecs_add(world, e3, Foo); + ecs_add_pair(world, e3, EcsChildOf, TgtA); + ecs_add(world, e3, Tag); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, ent, Mass, {100}); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ent, ecs_field_src(&it, 2)); + + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 100); + } + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e3, it.entities[0]); + + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ent, ecs_field_src(&it, 2)); + + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 100); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Basic_2_trivial_1_unused_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add(world, e, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, Bar", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_2_trivial_one_regular(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add(world, e1, TagB); + ecs_add(world, e1, TagC); + + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(self), TagB(self), TagC(self|up)", + .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_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_field_src(&it, 2)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_1_trivial_one_regular_one_trivial(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add(world, e1, TagB); + ecs_add(world, e1, TagC); + + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_add(world, e2, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(self), TagB(self|up), TagC(self)", + .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_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_one_regular_2_trivial(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add(world, e1, TagB); + ecs_add(world, e1, TagC); + + ecs_entity_t e2 = ecs_new_w(world, TagB); + ecs_add(world, e2, TagC); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(self|up), TagB(self), TagC(self)", + .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_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_2_trivial_w_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w_id(world, EcsPrefab); + ecs_add(world, e1, TagA); + + ecs_entity_t e2 = ecs_new_w_id(world, EcsPrefab); + ecs_add(world, e2, TagA); + ecs_add(world, e2, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(self), Prefab", + .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_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(EcsPrefab, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(EcsPrefab, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_3_trivial_w_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w_id(world, EcsPrefab); + ecs_add(world, e1, TagA); + ecs_add(world, e1, TagB); + + ecs_entity_t e2 = ecs_new_w_id(world, EcsPrefab); + ecs_add(world, e2, TagA); + ecs_add(world, e2, TagB); + ecs_add(world, e2, TagC); + + ecs_entity_t e3 = ecs_new(world); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(self), TagB(self), Prefab", + .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_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(EcsPrefab, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(EcsPrefab, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_2_trivial_w_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w_id(world, EcsDisabled); + ecs_add(world, e1, TagA); + + ecs_entity_t e2 = ecs_new_w_id(world, EcsDisabled); + ecs_add(world, e2, TagA); + ecs_add(world, e2, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(self), Disabled", + .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_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(EcsDisabled, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(EcsDisabled, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_3_trivial_w_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w_id(world, EcsDisabled); + ecs_add(world, e1, TagA); + ecs_add(world, e1, TagB); + + ecs_entity_t e2 = ecs_new_w_id(world, EcsDisabled); + ecs_add(world, e2, TagA); + ecs_add(world, e2, TagB); + ecs_add(world, e2, TagC); + + ecs_entity_t e3 = ecs_new(world); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(self), TagB(self), Disabled", + .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_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(EcsDisabled, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(EcsDisabled, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_2_this_w_fixed_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, Bar(e)", + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e, ecs_field_src(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_2_fixed_src_w_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar(e), Foo", + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + + test_uint(Bar, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_2_this_w_fixed_src_no_match_fixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, Bar(e)", + .cache_kind = cache_kind + }); + + ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_2_fixed_src_w_this_no_match_fixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar(e), Foo", + .cache_kind = cache_kind + }); + + ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_2_this_w_fixed_src_no_match_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, Bar(e)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_2_fixed_src_w_this_no_match_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar(e), Foo", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_query_count_results(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_int(0, ecs_query_count(q).results); + + ecs_new_w(world, TagA); + + test_int(1, ecs_query_count(q).results); + + ecs_entity_t e = ecs_new_w(world, TagA); + + test_int(1, ecs_query_count(q).results); + + ecs_add(world, e, TagB); + + test_int(2, ecs_query_count(q).results); + + ecs_delete_with(world, TagB); + + test_int(1, ecs_query_count(q).results); + + ecs_delete_with(world, TagA); + + test_int(0, ecs_query_count(q).results); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_query_count_entities(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_int(0, ecs_query_count(q).entities); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + + test_int(1, ecs_query_count(q).entities); + + ecs_entity_t e2 = ecs_new_w(world, TagA); + + test_int(2, ecs_query_count(q).entities); + + ecs_add(world, e2, TagB); + + test_int(2, ecs_query_count(q).entities); + + ecs_delete(world, e1); + + test_int(1, ecs_query_count(q).entities); + + ecs_delete(world, e2); + + test_int(0, ecs_query_count(q).entities); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_query_is_true(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, ecs_query_is_true(q)); + + ecs_entity_t e = ecs_new_w(world, TagA); + + test_bool(true, ecs_query_is_true(q)); + + ecs_delete(world, e); + + test_bool(false, ecs_query_is_true(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_implicit_cleanup_1_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .entity = ecs_new(world), + .expr = "TagA", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_fini(world); +} + +void Basic_implicit_cleanup_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_add(world, ecs_new_w(world, TagA), TagB); + + ecs_query_t *q = ecs_query(world, { + .entity = ecs_new(world), + .expr = "TagA, TagB", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_fini(world); +} + +void Basic_implicit_cleanup_1_term_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_new_w_pair(world, EcsChildOf, ecs_new_w(world, TagA)); + + ecs_query_t *q = ecs_query(world, { + .entity = ecs_new(world), + .expr = "TagA(up)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_fini(world); +} + +void Basic_implicit_cleanup_2_terms_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_add(world, + ecs_new_w_pair(world, EcsChildOf, + ecs_new_w(world, TagB)), TagA); + + ecs_query_t *q = ecs_query(world, { + .entity = ecs_new(world), + .expr = "TagA, TagB(up)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_fini(world); +} + +void Basic_implicit_cleanup_2_queries(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_new_w_pair(world, EcsIsA, ecs_new_w(world, Position)); + + ecs_query_t *q_1 = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + test_assert(q_1 != NULL); + + ecs_query_t *q_2 = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + test_assert(q_2 != NULL); + + ecs_query_fini(q_1); + ecs_query_fini(q_2); + + ecs_fini(world); +} + +void Basic_implicit_cleanup_2_queries_1_cleanup(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_new_w_pair(world, EcsIsA, ecs_new_w(world, Position)); + + ecs_query_t *q_1 = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + test_assert(q_1 != NULL); + + ecs_query_t *q_2 = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + test_assert(q_2 != NULL); + + ecs_query_fini(q_1); + ecs_query_fini(q_2); + + ecs_fini(world); +} + +void Basic_iter_valid(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(it.flags & EcsIterIsValid, false); + + test_bool(ecs_query_next(&it), true); + test_bool(it.flags & EcsIterIsValid, true); + + test_bool(ecs_query_next(&it), false); + test_bool(it.flags & EcsIterIsValid, false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_iter_frame_offset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); + ecs_entity_t e5 = ecs_new_w(world, TagA); + + ecs_add(world, e3, TagB); + ecs_add(world, e4, TagB); + ecs_add(world, e5, TagC); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.frame_offset, 0); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e1); + test_assert(it.entities[1] == e2); + test_assert(it.ids[0] == TagA); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.frame_offset, 2); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e3); + test_assert(it.entities[1] == e4); + test_assert(it.ids[0] == TagA); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.frame_offset, 4); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e5); + test_assert(it.ids[0] == TagA); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_iter_nested_1(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_TAG(ecs, Tag); + ECS_TAG(ecs, Singleton); + + ecs_query_t *qa = ecs_query(ecs, { + .terms = { + {Singleton, .src.id = Singleton}, {Tag} + }, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_query_t *qb = ecs_query(ecs, { + .terms = {{Tag}}, + .cache_kind = cache_kind + }); + + ecs_singleton_add(ecs, Singleton); + ecs_entity_t e = ecs_new_w(ecs, Tag); + + ecs_iter_t qit = ecs_query_iter(ecs, qa); + test_bool(true, ecs_query_next(&qit)); + test_int(1, qit.count); + test_uint(e, qit.entities[0]); + + ecs_id_t id = qit.ids[0]; + ecs_iter_t rit = ecs_query_iter(ecs, qb); + test_bool(true, ecs_query_next(&rit)); + test_int(1, rit.count); + test_uint(e, rit.entities[0]); + test_bool(false, ecs_query_next(&rit)); + test_assert(id == qit.ids[0]); + test_bool(false, ecs_query_next(&qit)); + + ecs_query_fini(qa); + ecs_query_fini(qb); + + ecs_fini(ecs); +} + +void Basic_iter_nested_2(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_TAG(ecs, TagA); + ECS_TAG(ecs, TagB); + ECS_TAG(ecs, Singleton); + + ecs_query_t *qa = ecs_query(ecs, { + .terms = { + {TagA}, {Singleton, .src.id = Singleton} + }, + .cache_kind = cache_kind + }); + + ecs_query_t *qb = ecs_query(ecs, { + .terms = { + {TagB}, {Singleton, .src.id = Singleton} + }, + .cache_kind = cache_kind + }); + + ecs_singleton_add(ecs, Singleton); + + ecs_entity_t ea = ecs_new_w(ecs, TagA); + ecs_entity_t eb = ecs_new_w(ecs, TagB); + + ecs_iter_t qita = ecs_query_iter(ecs, qa); + ecs_iter_t qitb = ecs_query_iter(ecs, qb); + + test_bool(ecs_query_next(&qita), true); + test_int(1, qita.count); + test_uint(ea, qita.entities[0]); + + test_bool(ecs_query_next(&qitb), true); + test_int(1, qitb.count); + test_uint(eb, qitb.entities[0]); + + test_uint(TagA, qita.ids[0]); + test_uint(TagB, qitb.ids[0]); + + test_bool(ecs_query_next(&qita), false); + test_bool(ecs_query_next(&qitb), false); + + ecs_query_fini(qa); + ecs_query_fini(qb); + + ecs_fini(ecs); +} + +void Basic_iter_interleaved(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_TAG(ecs, TagA); + ECS_TAG(ecs, TagB); + ECS_TAG(ecs, TagC); + ECS_TAG(ecs, Singleton); + + ecs_query_t *qa = ecs_query(ecs, { + .terms = { + {TagA}, {Singleton, .src.id = Singleton} + }, + .cache_kind = cache_kind + }); + + ecs_query_t *qb = ecs_query(ecs, { + .terms = { + {TagB}, {Singleton, .src.id = Singleton} + }, + .cache_kind = cache_kind + }); + + ecs_query_t *qc = ecs_query(ecs, { + .terms = { + {TagC}, {Singleton, .src.id = Singleton} + }, + .cache_kind = cache_kind + }); + + ecs_singleton_add(ecs, Singleton); + + ecs_entity_t ea = ecs_new_w(ecs, TagA); + ecs_entity_t eb = ecs_new_w(ecs, TagB); + ecs_entity_t ec = ecs_new_w(ecs, TagC); + + ecs_iter_t qita = ecs_query_iter(ecs, qa); + ecs_iter_t qitb = ecs_query_iter(ecs, qb); + + test_bool(ecs_query_next(&qita), true); + test_int(1, qita.count); + test_uint(ea, qita.entities[0]); + + test_bool(ecs_query_next(&qitb), true); + test_int(1, qitb.count); + test_uint(eb, qitb.entities[0]); + + test_uint(TagA, qita.ids[0]); + test_uint(TagB, qitb.ids[0]); + + test_bool(ecs_query_next(&qita), false); + + ecs_iter_t qitc = ecs_query_iter(ecs, qc); + test_bool(ecs_query_next(&qitc), true); + test_int(1, qitc.count); + test_uint(ec, qitc.entities[0]); + + test_uint(TagB, qitb.ids[0]); + test_uint(TagC, qitc.ids[0]); + + test_bool(ecs_query_next(&qitb), false); + test_bool(ecs_query_next(&qitc), false); + + ecs_query_fini(qa); + ecs_query_fini(qb); + ecs_query_fini(qc); + + ecs_fini(ecs); +} + +void Basic_set_get_context(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + + int ctx = 0; + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position) } + }, + .ctx = &ctx, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_assert(q->ctx == &ctx); + test_assert(q->binding_ctx == NULL); + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Basic_set_get_binding_context(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + + int ctx = 0; + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position) } + }, + .binding_ctx = &ctx, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_assert(q->binding_ctx == &ctx); + test_assert(q->ctx == NULL); + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +static int ctx_free_invoked = 0; + +static void ctx_free(void *ptr) { + *(int*)ptr = 10; + ctx_free_invoked ++; +} + +void Basic_set_get_context_w_free(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + + int ctx = 0; + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position) } + }, + .ctx = &ctx, + .ctx_free = ctx_free, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_assert(q->ctx == &ctx); + test_assert(q->binding_ctx == NULL); + test_int(ctx, 0); + + test_int(ctx_free_invoked, 0); + ecs_query_fini(q); + test_int(ctx_free_invoked, 1); + + test_int(ctx, 10); + + ecs_fini(ecs); +} + +void Basic_set_get_binding_context_w_free(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + + int ctx = 0; + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position) } + }, + .binding_ctx = &ctx, + .binding_ctx_free = ctx_free, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_assert(q->binding_ctx == &ctx); + test_assert(q->ctx == NULL); + test_int(ctx, 0); + + test_int(ctx_free_invoked, 0); + ecs_query_fini(q); + test_int(ctx_free_invoked, 1); + + test_int(ctx, 10); + + ecs_fini(ecs); +} + +void Basic_create_query_w_existing_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + if (cache_kind == EcsQueryCacheDefault) { + /* Make sure we also test uncached, as the default will create a cached + * query if an entity id is provided. */ + cache_kind = EcsQueryCacheNone; + } + + ecs_entity_t e = ecs_entity(world, { .name = "q" }); + + ecs_query_t *q_1 = ecs_query(world, { + .entity = e, + .terms = { + { .id = Foo }, + }, + .cache_kind = cache_kind + }); + + test_assert(q_1 != NULL); + + ecs_query_t *q_2 = ecs_query(world, { + .entity = e, + .terms = { + { .id = Foo }, + }, + .cache_kind = cache_kind + }); + + test_assert(q_2 != NULL); + + test_str(ecs_get_name(world, e), "q"); + + ecs_query_fini(q_2); + + ecs_fini(world); +} + +void Basic_create_multi_component_query_w_existing_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + if (cache_kind == EcsQueryCacheDefault) { + /* Make sure we also test uncached, as the default will create a cached + * query if an entity id is provided. */ + cache_kind = EcsQueryCacheNone; + } + + ecs_entity_t e = ecs_entity(world, { .name = "q" }); + + ecs_query_t *q_1 = ecs_query(world, { + .entity = e, + .terms = { + { .id = Foo }, + { .id = Bar } + }, + .cache_kind = cache_kind + }); + + test_assert(q_1 != NULL); + + ecs_query_t *q_2 = ecs_query(world, { + .entity = e, + .terms = { + { .id = Foo }, + { .id = Bar }, + }, + .cache_kind = cache_kind + }); + + test_assert(q_2 != NULL); + + test_str(ecs_get_name(world, e), "q"); + + ecs_query_fini(q_2); + + ecs_fini(world); +} + +void Basic_create_query_w_existing_entity_different_term_count(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + if (cache_kind == EcsQueryCacheDefault) { + /* Make sure we also test uncached, as the default will create a cached + * query if an entity id is provided. */ + cache_kind = EcsQueryCacheNone; + } + + ecs_entity_t e = ecs_entity(world, { .name = "q" }); + + ecs_query_t *q_1 = ecs_query(world, { + .entity = e, + .terms = { + { .id = Foo }, + { .id = Bar } + }, + .cache_kind = cache_kind + }); + + test_assert(q_1 != NULL); + + ecs_query_t *q_2 = ecs_query(world, { + .entity = e, + .terms = { + { .id = Foo }, + }, + .cache_kind = cache_kind + }); + + test_assert(q_2 != NULL); + + test_str(ecs_get_name(world, e), "q"); + + ecs_query_fini(q_2); + + ecs_fini(world); +} + +void Basic_delete_query_by_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_entity_t e = ecs_entity(world, { .name = "q" }); + ecs_query_t *q = ecs_query(world, { + .entity = e, + .expr = "Foo", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_delete(world, e); + + ecs_fini(world); +} + +void Basic_eval_count(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Foo }}, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + test_int(q->eval_count, 0); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + test_int(q->eval_count, 1); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_no_results_after_delete_tree(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t parent = ecs_new_w(world, Position); + test_assert(parent != 0); + + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + test_assert(ecs_get_type(world, child) != NULL); + ecs_add(world, child, Position); + + ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child); + test_assert(ecs_get_type(world, grand_child) != NULL); + ecs_add(world, grand_child, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + test_int(3, ecs_query_count(q).results); + + ecs_delete(world, parent); + + test_bool(ecs_is_alive(world, parent), false); + test_bool(ecs_is_alive(world, child), false); + test_bool(ecs_is_alive(world, grand_child), false); + + test_int(0, ecs_query_count(q).results); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_no_results_after_delete_tree_deferred(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t parent = ecs_new_w(world, Position); + test_assert(parent != 0); + + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + test_assert(ecs_get_type(world, child) != NULL); + ecs_add(world, child, Position); + + ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child); + test_assert(ecs_get_type(world, grand_child) != NULL); + ecs_add(world, grand_child, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + + test_int(3, ecs_query_count(q).results); + + ecs_defer_begin(world); + ecs_delete(world, parent); + ecs_defer_end(world); + + test_bool(ecs_is_alive(world, parent), false); + test_bool(ecs_is_alive(world, child), false); + test_bool(ecs_is_alive(world, grand_child), false); + + test_int(0, ecs_query_count(q).results); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_add_on_self_ref(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t qe = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .entity = qe, + .terms = {{ TagA, .src.id = qe }}, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + test_assert(ecs_has(world, qe, TagA)); + test_bool(true, ecs_query_is_true(q)); + + ecs_fini(world); +} + +void Basic_add_on_self_ref_by_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t qe = ecs_entity(world, { .name = "q" }); + + ecs_query_t *q = ecs_query(world, { + .entity = qe, + .expr = "TagA(q)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + test_assert(ecs_has(world, qe, TagA)); + test_bool(true, ecs_query_is_true(q)); + + ecs_fini(world); +} + +void Basic_delete_id_after_delete_query(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t i = ecs_new(world); + ecs_make_alive(world, i); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = i }}, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + test_int(ecs_query_count(q).entities, 0); + ecs_entity_t e = ecs_new_w_id(world, i); + test_int(ecs_query_count(q).entities, 1); + ecs_delete(world, e); + test_int(ecs_query_count(q).entities, 0); + + ecs_query_fini(q); + + ecs_delete(world, i); + + ecs_fini(world); +} + +void Basic_pair_sweep_tag(void) { + ecs_world_t *world = ecs_mini(); + + for (ecs_entity_t i = 1023; i < 4097; i ++) { + ecs_entity_t i = 1024; + test_assert(!ecs_is_alive(world, i)); + ecs_make_alive(world, i); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = i }}, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + test_int(ecs_query_count(q).entities, 0); + ecs_entity_t e = ecs_new_w_id(world, i); + test_int(ecs_query_count(q).entities, 1); + ecs_delete(world, e); + test_int(ecs_query_count(q).entities, 0); + + ecs_query_fini(q); + + ecs_delete(world, i); + } + + ecs_fini(world); +} + +void Basic_pair_sweep_wildcard_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + for (ecs_entity_t i = 1023; i < 4097; i ++) { + test_assert(!ecs_is_alive(world, i)); + ecs_make_alive(world, i); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(EcsWildcard, i) }}, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + test_int(ecs_query_count(q).entities, 0); + ecs_entity_t e = ecs_new_w_pair(world, Rel, i); + test_int(ecs_query_count(q).entities, 1); + ecs_delete(world, e); + test_int(ecs_query_count(q).entities, 0); + + ecs_query_fini(q); + + ecs_delete(world, i); + } + + ecs_fini(world); +} + +void Basic_pair_sweep_wildcard_second(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + for (ecs_entity_t i = 1023; i < 4097; i ++) { + test_assert(!ecs_is_alive(world, i)); + ecs_make_alive(world, i); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(i, EcsWildcard) }} + }); + + test_assert(q != NULL); + + test_int(ecs_query_count(q).entities, 0); + ecs_entity_t e = ecs_new_w_pair(world, i, Tgt); + test_int(ecs_query_count(q).entities, 1); + ecs_delete(world, e); + test_int(ecs_query_count(q).entities, 0); + + ecs_query_fini(q); + + ecs_delete(world, i); + } + + ecs_fini(world); +} + +void Basic_create_w_entity_deferred(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_entity_t qe = ecs_new(world); + + ecs_defer_begin(world); + ecs_query_t *q = ecs_query(world, { + .entity = qe, + .terms = {{ Foo }} + }); + test_assert(q != NULL); + ecs_defer_end(world); + + test_assert(ecs_has_pair(world, qe, ecs_id(EcsPoly), EcsQuery)); + + const EcsPoly *p = ecs_get_pair(world, qe, EcsPoly, EcsQuery); + test_assert(p != NULL); + test_assert(p->poly == q); + + ecs_entity_t e = ecs_new_w(world, Foo); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Basic_32_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag0); + ECS_TAG(world, Tag1); + ECS_TAG(world, Tag2); + ECS_TAG(world, Tag3); + ECS_TAG(world, Tag4); + ECS_TAG(world, Tag5); + ECS_TAG(world, Tag6); + ECS_TAG(world, Tag7); + ECS_TAG(world, Tag8); + ECS_TAG(world, Tag9); + ECS_TAG(world, Tag10); + ECS_TAG(world, Tag11); + ECS_TAG(world, Tag12); + ECS_TAG(world, Tag13); + ECS_TAG(world, Tag14); + ECS_TAG(world, Tag15); + ECS_TAG(world, Tag16); + ECS_TAG(world, Tag17); + ECS_TAG(world, Tag18); + ECS_TAG(world, Tag19); + ECS_TAG(world, Tag20); + ECS_TAG(world, Tag21); + ECS_TAG(world, Tag22); + ECS_TAG(world, Tag23); + ECS_TAG(world, Tag24); + ECS_TAG(world, Tag25); + ECS_TAG(world, Tag26); + ECS_TAG(world, Tag27); + ECS_TAG(world, Tag28); + ECS_TAG(world, Tag29); + ECS_TAG(world, Tag30); + ECS_TAG(world, Tag31); + ECS_TAG(world, Tag32); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Tag0); + ecs_add(world, e, Tag1); + ecs_add(world, e, Tag2); + ecs_add(world, e, Tag3); + ecs_add(world, e, Tag4); + ecs_add(world, e, Tag5); + ecs_add(world, e, Tag6); + ecs_add(world, e, Tag7); + ecs_add(world, e, Tag8); + ecs_add(world, e, Tag9); + ecs_add(world, e, Tag10); + ecs_add(world, e, Tag11); + ecs_add(world, e, Tag12); + ecs_add(world, e, Tag13); + ecs_add(world, e, Tag14); + ecs_add(world, e, Tag15); + ecs_add(world, e, Tag16); + ecs_add(world, e, Tag17); + ecs_add(world, e, Tag18); + ecs_add(world, e, Tag19); + ecs_add(world, e, Tag20); + ecs_add(world, e, Tag21); + ecs_add(world, e, Tag22); + ecs_add(world, e, Tag23); + ecs_add(world, e, Tag24); + ecs_add(world, e, Tag25); + ecs_add(world, e, Tag26); + ecs_add(world, e, Tag27); + ecs_add(world, e, Tag28); + ecs_add(world, e, Tag29); + ecs_add(world, e, Tag30); + ecs_add(world, e, Tag31); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag0, Tag1, Tag2, Tag3, Tag4, Tag5, Tag6, Tag7, Tag8, Tag9, Tag10, Tag11, Tag12, Tag13, Tag14, Tag15, Tag16, Tag17, Tag18, Tag19, Tag20, Tag21, Tag22, Tag23, Tag24, Tag25, Tag26, Tag27, Tag28, Tag29, Tag30, Tag31" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_int(32, it.field_count); + test_uint(e, it.entities[0]); + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_33_terms_expr(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag0); + ECS_TAG(world, Tag1); + ECS_TAG(world, Tag2); + ECS_TAG(world, Tag3); + ECS_TAG(world, Tag4); + ECS_TAG(world, Tag5); + ECS_TAG(world, Tag6); + ECS_TAG(world, Tag7); + ECS_TAG(world, Tag8); + ECS_TAG(world, Tag9); + ECS_TAG(world, Tag10); + ECS_TAG(world, Tag11); + ECS_TAG(world, Tag12); + ECS_TAG(world, Tag13); + ECS_TAG(world, Tag14); + ECS_TAG(world, Tag15); + ECS_TAG(world, Tag16); + ECS_TAG(world, Tag17); + ECS_TAG(world, Tag18); + ECS_TAG(world, Tag19); + ECS_TAG(world, Tag20); + ECS_TAG(world, Tag21); + ECS_TAG(world, Tag22); + ECS_TAG(world, Tag23); + ECS_TAG(world, Tag24); + ECS_TAG(world, Tag25); + ECS_TAG(world, Tag26); + ECS_TAG(world, Tag27); + ECS_TAG(world, Tag28); + ECS_TAG(world, Tag29); + ECS_TAG(world, Tag30); + ECS_TAG(world, Tag31); + ECS_TAG(world, Tag32); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "Tag0, Tag1, Tag2, Tag3, Tag4, Tag5, Tag6, Tag7, Tag8, Tag9, Tag10, Tag11, Tag12, Tag13, Tag14, Tag15, Tag16, Tag17, Tag18, Tag19, Tag20, Tag21, Tag22, Tag23, Tag24, Tag25, Tag26, Tag27, Tag28, Tag29, Tag30, Tag31, Tag32" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Basic_stage_query(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new_w(world, Position); + + ecs_world_t *stage = ecs_get_stage(world, 0); + + ecs_query_t *q = ecs_query(stage, { + .expr = "Position", + .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_assert(it.world == stage); + test_assert(it.real_world == world); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_world_query_w_stage_iter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new_w(world, Position); + + ecs_world_t *stage = ecs_get_stage(world, 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(stage, q); + test_bool(true, ecs_query_next(&it)); + test_assert(it.world == stage); + test_assert(it.real_world == world); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_stage_query_w_nth_stage(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new_w(world, Position); + + ecs_set_stage_count(world, 2); + ecs_world_t *stage = ecs_get_stage(world, 1); + + ecs_query_t *q = ecs_query(stage, { + .expr = "Position", + .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_assert(it.world == stage); + test_assert(it.real_world == world); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_world_query_w_nth_stage_iter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new_w(world, Position); + + ecs_set_stage_count(world, 2); + ecs_world_t *stage = ecs_get_stage(world, 1); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(stage, q); + test_bool(true, ecs_query_next(&it)); + test_assert(it.world == stage); + test_assert(it.real_world == world); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_match_empty(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_table_t *e1_table = ecs_get_table(world, e1); + + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add(world, e2, Bar); + ecs_table_t *e2_table = ecs_get_table(world, e2); + ecs_delete(world, e2); // creates empty [Foo, Bar] + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Foo }}, + .flags = EcsQueryMatchEmptyTables, + .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(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.table == e1_table); + test_uint(it.entities[0], e1); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); // creates empty [Foo] + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_assert(it.table == e1_table); + test_int(it.count, 0); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_match_new_empty(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_table_t *e1_table = ecs_get_table(world, e1); + + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add(world, e2, Bar); + ecs_table_t *e2_table = ecs_get_table(world, e2); + ecs_delete(world, e2); // creates empty [Foo, Bar] + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Foo }}, + .flags = EcsQueryMatchEmptyTables, + .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(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.table == e1_table); + test_uint(it.entities[0], e1); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); // creates empty [Foo] + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == e1_table); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_match_empty_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_table_t *e1_table = ecs_get_table(world, e1); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Bar); + ecs_table_t *e2_table = ecs_get_table(world, e2); + ecs_delete(world, e2); // creates empty [Position, Bar] + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position) }}, + .flags = EcsQueryMatchEmptyTables, + .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(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.table == e1_table); + test_uint(it.entities[0], e1); + test_assert(ecs_field(&it, Position, 0) != NULL); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); // creates empty [Position] + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_assert(it.table == e1_table); + test_int(it.count, 0); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_match_new_empty_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_table_t *e1_table = ecs_get_table(world, e1); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Bar); + ecs_table_t *e2_table = ecs_get_table(world, e2); + ecs_delete(world, e2); // creates empty [Position, Bar] + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position) }}, + .flags = EcsQueryMatchEmptyTables, + .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(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.table == e1_table); + test_uint(it.entities[0], e1); + test_assert(ecs_field(&it, Position, 0) != NULL); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); // creates empty [Position] + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == e1_table); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_match_empty_w_ref(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_entity_t p = ecs_new_w(world, Position); + + ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_table_t *e1_table = ecs_get_table(world, e1); + + ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, e2, Bar); + ecs_table_t *e2_table = ecs_get_table(world, e2); + ecs_delete(world, e2); // creates empty [Bar, (ChildOf, p)] + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position), .src.id = EcsUp, .trav = EcsChildOf }}, + .flags = EcsQueryMatchEmptyTables, + .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(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.table == e1_table); + test_uint(it.entities[0], e1); + test_assert(ecs_field(&it, Position, 0) != NULL); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); // creates empty [(ChildOf, p)] + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_assert(it.table == e1_table); + test_int(it.count, 0); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_match_new_empty_w_ref(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_entity_t p = ecs_new_w(world, Position); + + ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_table_t *e1_table = ecs_get_table(world, e1); + + ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, e2, Bar); + ecs_table_t *e2_table = ecs_get_table(world, e2); + ecs_delete(world, e2); // creates empty [Bar, (ChildOf, p)] + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position), .src.id = EcsUp, .trav = EcsChildOf }}, + .flags = EcsQueryMatchEmptyTables, + .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(it.count, 0); + test_assert(it.table == e2_table); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.table == e1_table); + test_uint(it.entities[0], e1); + test_assert(ecs_field(&it, Position, 0) != NULL); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); // creates empty [(ChildOf, p)] + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_assert(it.table == e2_table); + test_int(it.count, 0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == e1_table); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +static +int compare_position( + ecs_entity_t e1, + const void *ptr1, + ecs_entity_t e2, + const void *ptr2) +{ + const Position *p1 = ptr1; + const Position *p2 = ptr2; + return (p1->x > p2->x) - (p1->x < p2->x); +} + +void Basic_match_empty_w_order_by(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); + + ecs_add(world, e5, Tag); + ecs_remove(world, e5, Tag); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position, + .flags = EcsQueryMatchEmptyTables, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 5); + + test_assert(it.entities[0] == e2); + test_assert(it.entities[1] == e4); + test_assert(it.entities[2] == e1); + test_assert(it.entities[3] == e5); + test_assert(it.entities[4] == e3); + + test_assert(!ecs_query_next(&it)); + } + + ecs_add(world, e5, Tag); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 3); + test_assert(it.entities[0] == e2); + test_assert(it.entities[1] == e4); + test_assert(it.entities[2] == e1); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.entities[0] == e5); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.entities[0] == e3); + + test_assert(!ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_match_new_empty_w_order_by(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position, + .flags = EcsQueryMatchEmptyTables, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); + + ecs_add(world, e5, Tag); + ecs_remove(world, e5, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 5); + + test_assert(it.entities[0] == e2); + test_assert(it.entities[1] == e4); + test_assert(it.entities[2] == e1); + test_assert(it.entities[3] == e5); + test_assert(it.entities[4] == e3); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_match_empty_w_bitset(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position) }}, + .flags = EcsQueryMatchEmptyTables, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + + ecs_enable_component(world, e1, Position, true); + ecs_enable_component(world, e2, Position, false); + ecs_enable_component(world, e3, Position, true); + + ecs_add(world, e3, Tag); + ecs_remove(world, e3, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + { + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 10); test_int(p->y, 20); + } + { + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 30); test_int(p->y, 40); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_default_query_flags(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_set_default_query_flags(world, EcsQueryMatchEmptyTables); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_id(Position)}}, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_assert(q->flags & EcsQueryMatchEmptyTables); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + + ecs_add(world, e2, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e2, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + if (cache_kind == EcsQueryCacheAuto) { + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + } else { + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e2, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_ref_fields_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_id(Position) }}, + .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(it.count, 3); + test_uint(it.entities[0], e1); + test_uint(it.entities[1], e2); + test_uint(it.entities[2], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, it.ref_fields); + test_uint(0, it.row_fields); + test_uint(0, it.up_fields); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 30); test_int(p[1].y, 40); + test_int(p[2].x, 50); test_int(p[2].y, 60); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_ref_fields_static_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + /* ecs_entity_t e1 = */ ecs_insert(world, ecs_value(Position, {10, 20})); + /* ecs_entity_t e2 = */ ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_id(Position), .src.id = e3 }}, + .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(it.count, 0); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(1, it.ref_fields); + test_uint(0, it.row_fields); + test_uint(0, it.up_fields); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 50); test_int(p[0].y, 60); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_ref_fields_variable_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_id(Position), .src.name = "$x" }}, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(1, it.ref_fields); + test_uint(0, it.row_fields); + test_uint(0, it.up_fields); + { const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); } + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(1, it.ref_fields); + test_uint(0, it.row_fields); + test_uint(0, it.up_fields); + { const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); test_int(p[0].y, 40); } + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(1, it.ref_fields); + test_uint(0, it.row_fields); + test_uint(0, it.up_fields); + { const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 50); test_int(p[0].y, 60); } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_ref_fields_up_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + /* ecs_entity_t e1 = */ ecs_insert(world, ecs_value(Position, {10, 20})); + /* ecs_entity_t e2 = */ ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, e3); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_id(Position), .src.id = EcsUp }}, + .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(it.count, 1); + test_uint(it.entities[0], child); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(0, it.ref_fields); + test_uint(0, it.row_fields); + test_uint(1, it.up_fields); + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 50); test_int(p[0].y, 60); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_ref_fields_self_up_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, e3); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_id(Position), .src.id = EcsSelf|EcsUp }}, + .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(it.count, 3); + test_uint(it.entities[0], e1); + test_uint(it.entities[1], e2); + test_uint(it.entities[2], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, it.ref_fields); + test_uint(0, it.row_fields); + test_uint(0, it.up_fields); + { const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 30); test_int(p[1].y, 40); + test_int(p[2].x, 50); test_int(p[2].y, 60); } + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], child); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(0, it.ref_fields); + test_uint(0, it.row_fields); + test_uint(1, it.up_fields); + { const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 50); test_int(p[0].y, 60); } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_0_src_match_nothing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_id(Position), .src.id = EcsIsEntity }}, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_assert(q->flags & EcsQueryMatchNothing); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_0_terms_match_nothing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_assert(q->flags & EcsQueryMatchNothing); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_any_record(void) { + test_quarantine("25 Sept 2024"); + + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "_", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add_pair(world, e, RelX, TgtA); + ecs_add_pair(world, e, RelX, TgtB); + ecs_add_pair(world, e, RelX, TgtC); + ecs_add_pair(world, e, RelY, TgtA); + ecs_add_pair(world, e, RelY, TgtB); + + bool matched = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + if (it.count == 1 && it.entities[0] == e) { + test_uint(EcsWildcard, ecs_field_id(&it, 0)); + const ecs_table_record_t *tr = it.trs[0]; + test_assert(tr != NULL); + test_int(tr->index, 0); + test_int(tr->column, -1); + test_int(tr->count, 6); + matched = true; + } + } + + test_bool(true, matched); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_pair_rel_any_record(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "(_, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add_pair(world, e, RelX, TgtA); + ecs_add_pair(world, e, RelX, TgtB); + ecs_add_pair(world, e, RelX, TgtC); + ecs_add_pair(world, e, RelY, TgtA); + ecs_add_pair(world, e, RelY, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 0)); + const ecs_table_record_t *tr = it.trs[0]; + test_assert(tr != NULL); + test_int(tr->index, 2); + test_int(tr->column, -1); + test_int(tr->count, 2); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_pair_tgt_any_record(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "(RelX, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add_pair(world, e, RelX, TgtA); + ecs_add_pair(world, e, RelX, TgtB); + ecs_add_pair(world, e, RelX, TgtC); + ecs_add_pair(world, e, RelY, TgtA); + ecs_add_pair(world, e, RelY, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_pair(RelX, EcsWildcard), ecs_field_id(&it, 0)); + const ecs_table_record_t *tr = it.trs[0]; + test_assert(tr != NULL); + test_int(tr->index, 1); + test_int(tr->column, -1); + test_int(tr->count, 3); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_pair_any_any_record(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "(_, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add_pair(world, e, RelX, TgtA); + ecs_add_pair(world, e, RelX, TgtB); + ecs_add_pair(world, e, RelX, TgtC); + ecs_add_pair(world, e, RelY, TgtA); + ecs_add_pair(world, e, RelY, TgtB); + + bool matched = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + if (it.count == 1 && it.entities[0] == e) { + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 0)); + const ecs_table_record_t *tr = it.trs[0]; + test_assert(tr != NULL); + test_int(tr->index, 1); + test_int(tr->column, -1); + test_int(tr->count, 5); + matched = true; + } + } + + test_bool(true, matched); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_written_any_record(void) { + test_quarantine("25 Sept 2024"); + + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, _", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add_pair(world, e, RelX, TgtA); + ecs_add_pair(world, e, RelX, TgtB); + ecs_add_pair(world, e, RelX, TgtC); + ecs_add_pair(world, e, RelY, TgtA); + ecs_add_pair(world, e, RelY, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + const ecs_table_record_t *tr = it.trs[1]; + test_assert(tr != NULL); + test_int(tr->index, 0); + test_int(tr->column, -1); + test_int(tr->count, 6); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_written_pair_rel_any_record(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (_, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add_pair(world, e, RelX, TgtA); + ecs_add_pair(world, e, RelX, TgtB); + ecs_add_pair(world, e, RelX, TgtC); + ecs_add_pair(world, e, RelY, TgtA); + ecs_add_pair(world, e, RelY, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 1)); + const ecs_table_record_t *tr = it.trs[1]; + test_assert(tr != NULL); + test_int(tr->index, 2); + test_int(tr->column, -1); + test_int(tr->count, 2); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_written_pair_tgt_any_record(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (RelX, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add_pair(world, e, RelX, TgtA); + ecs_add_pair(world, e, RelX, TgtB); + ecs_add_pair(world, e, RelX, TgtC); + ecs_add_pair(world, e, RelY, TgtA); + ecs_add_pair(world, e, RelY, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_pair(RelX, EcsWildcard), ecs_field_id(&it, 1)); + const ecs_table_record_t *tr = it.trs[1]; + test_assert(tr != NULL); + test_int(tr->index, 1); + test_int(tr->column, -1); + test_int(tr->count, 3); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_written_pair_any_any_record(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (_, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add_pair(world, e, RelX, TgtA); + ecs_add_pair(world, e, RelX, TgtB); + ecs_add_pair(world, e, RelX, TgtC); + ecs_add_pair(world, e, RelY, TgtA); + ecs_add_pair(world, e, RelY, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + const ecs_table_record_t *tr = it.trs[1]; + test_assert(tr != NULL); + test_int(tr->index, 1); + test_int(tr->column, -1); + test_int(tr->count, 5); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_pair_rel_any_record_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "(_, Position)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_set_pair_second(world, e, RelX, Position, {10, 20}); + ecs_set_pair_second(world, e, RelY, Position, {20, 0}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_pair(EcsWildcard, ecs_id(Position)), ecs_field_id(&it, 0)); + const ecs_table_record_t *tr = it.trs[0]; + test_assert(tr != NULL); + test_int(tr->index, 1); + test_int(tr->column, 1); + test_int(tr->count, 2); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_pair_tgt_any_record_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, RelX); + ECS_TAG(world, RelY); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Position, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_set_pair(world, e, Position, RelX, {10, 20}); + ecs_set_pair(world, e, Position, RelY, {20, 30}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_pair_t(Position, EcsWildcard), ecs_field_id(&it, 0)); + const ecs_table_record_t *tr = it.trs[0]; + test_assert(tr != NULL); + test_int(tr->index, 1); + test_int(tr->column, 1); + test_int(tr->count, 2); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_entity_iteration_w_match_empty_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($x)", // non-$this variable forces entity iteration + .cache_kind = cache_kind, + .flags = EcsQueryMatchEmptyTables + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + } + + test_bool(false, ecs_query_next(&it)); + } + + // delete entity, leave empty table + + ecs_delete(world, e); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_get_cache_query_uncached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheNone + }); + + test_assert(q != NULL); + + const ecs_query_t *cq = ecs_query_get_cache_query(q); + test_assert(cq == NULL); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_get_cache_query_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAll + }); + + test_assert(q != NULL); + + const ecs_query_t *cq = ecs_query_get_cache_query(q); + test_assert(cq != NULL); + + test_int(cq->term_count, 1); + test_int(cq->terms[0].id, ecs_id(Position)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Basic_get_cache_query_partially_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (ChildOf, $this)", // 2nd term cannot be cached + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + const ecs_query_t *cq = ecs_query_get_cache_query(q); + test_assert(cq != NULL); + + test_int(cq->term_count, 1); + test_int(cq->terms[0].id, ecs_id(Position)); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/BuiltinPredicates.c b/vendors/flecs/test/query/src/BuiltinPredicates.c new file mode 100644 index 000000000..b310eac9b --- /dev/null +++ b/vendors/flecs/test/query/src/BuiltinPredicates.c @@ -0,0 +1,3694 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void BuiltinPredicates_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +void BuiltinPredicates_this_eq_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == ent", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_eq_name(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == \"ent\"", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_eq_var(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "$this == $x", + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void BuiltinPredicates_this_eq_id_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this == ent_2", + .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_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_eq_id_written_no_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelB($this), $this == ent_2", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_eq_name_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this == \"ent_2\"", + .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_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_eq_name_written_no_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelB($this), $this == \"ent_2\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_eq_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add_pair(world, ent_1, RelA, ent_2); + ecs_add_pair(world, ent_2, RelA, ent_2); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add_pair(world, ent_3, RelA, ent_2); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $x), $this == $x", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + test_uint(ent_2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_id(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == ent", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_name(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == \"ent\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_var(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "$x == $y", + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_add(world, e1, Bar); + /* ecs_entity_t e2 = */ ecs_new_w(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), $x == $this, Bar($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_id_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x == ent_2", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_id_written_no_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelB($x), $x == ent_2", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_name_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x == \"ent_2\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_name_written_no_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelB($x), $x == \"ent_2\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add_pair(world, ent_1, RelA, ent_2); + ecs_add_pair(world, ent_2, RelA, ent_2); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add_pair(world, ent_3, RelA, ent_2); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x, $y), $x == $y", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + test_uint(ent_2, ecs_iter_get_var(&it, y_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_neq_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "$this != ent_2", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + bool ent_1_found = false; + bool ent_3_found = false; + bool ent_4_found = false; + + ecs_iter_t it = ecs_query_iter(world, r); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != ent_2); + if (it.entities[i] == ent_1) { + ent_1_found = true; + } + if (it.entities[i] == ent_3) { + ent_3_found = true; + } + if (it.entities[i] == ent_4) { + ent_4_found = true; + } + } + } + + test_bool(ent_1_found, true); + test_bool(ent_3_found, true); + test_bool(ent_4_found, true); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void BuiltinPredicates_this_neq_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this != \"ent_2\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + bool ent_1_found = false; + bool ent_3_found = false; + bool ent_4_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != ent_2); + if (it.entities[i] == ent_1) { + ent_1_found = true; + } + if (it.entities[i] == ent_3) { + ent_3_found = true; + } + if (it.entities[i] == ent_4) { + ent_4_found = true; + } + } + } + + test_bool(ent_1_found, true); + test_bool(ent_3_found, true); + test_bool(ent_4_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_neq_var(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "$this != $x", + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void BuiltinPredicates_this_neq_id_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this != ent_2", + .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_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_neq_id_written_no_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelB($this), $this != ent_2", + .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_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_neq_name_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this != \"ent_2\"", + .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_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_neq_name_written_no_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelB($this), $this != \"ent_2\"", + .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_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_neq_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add_pair(world, ent_1, RelA, ent_2); + ecs_add_pair(world, ent_2, RelA, ent_2); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add_pair(world, ent_3, RelA, ent_2); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $x), $this != $x", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + test_uint(ent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_neq_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x != ent_2", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + bool ent_1_found = false; + bool ent_3_found = false; + bool ent_4_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_uint(it.count, 0); + ecs_entity_t e = ecs_iter_get_var(&it, x_var); + test_assert(e != ent_2); + if (e == ent_1) { + ent_1_found = true; + } + if (e == ent_3) { + ent_3_found = true; + } + if (e == ent_4) { + ent_4_found = true; + } + } + + test_bool(ent_1_found, true); + test_bool(ent_3_found, true); + test_bool(ent_4_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_neq_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x != \"ent_2\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + bool ent_1_found = false; + bool ent_3_found = false; + bool ent_4_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_uint(it.count, 0); + ecs_entity_t e = ecs_iter_get_var(&it, x_var); + test_assert(e != ent_2); + if (e == ent_1) { + ent_1_found = true; + } + if (e == ent_3) { + ent_3_found = true; + } + if (e == ent_4) { + ent_4_found = true; + } + } + + test_bool(ent_1_found, true); + test_bool(ent_3_found, true); + test_bool(ent_4_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_neq_var(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "$x != $y", + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void BuiltinPredicates_var_neq_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Foo); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, Rel, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "Rel($this, $x), $x != $this", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(Tgt, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_neq_id_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x != ent_2", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_neq_id_written_no_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelB($x), $x != ent_2", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_neq_name_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x != \"ent_2\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_neq_name_written_no_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelB($x), $x != \"ent_2\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_neq_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add_pair(world, ent_1, RelA, ent_2); + ecs_add_pair(world, ent_2, RelA, ent_2); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add_pair(world, ent_3, RelA, ent_2); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x, $y), $x != $y", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, ecs_iter_get_var(&it, x_var)); + test_uint(ent_2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, ent_2), ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, ecs_iter_get_var(&it, x_var)); + test_uint(ent_2, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_2_neq_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this != ent_2, $this != ent_3", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + bool ent_1_found = false; + bool ent_4_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != ent_2); + test_assert(it.entities[i] != ent_3); + if (it.entities[i] == ent_1) { + ent_1_found = true; + } + if (it.entities[i] == ent_4) { + ent_4_found = true; + } + if (it.entities[i] == ent_5) { + ent_5_found = true; + } + } + } + + test_bool(ent_1_found, true); + test_bool(ent_4_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_2_neq_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this != \"ent_2\", $this != \"ent_3\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + bool ent_1_found = false; + bool ent_4_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != ent_2); + test_assert(it.entities[i] != ent_3); + if (it.entities[i] == ent_1) { + ent_1_found = true; + } + if (it.entities[i] == ent_4) { + ent_4_found = true; + } + if (it.entities[i] == ent_5) { + ent_5_found = true; + } + } + } + + test_bool(ent_1_found, true); + test_bool(ent_4_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_2_neq_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x != ent_2, $x != ent_3", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + bool ent_1_found = false; + bool ent_4_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_uint(it.count, 0); + ecs_entity_t e = ecs_iter_get_var(&it, x_var); + test_assert(e != ent_2); + test_assert(e != ent_3); + if (e == ent_1) { + ent_1_found = true; + } + if (e == ent_4) { + ent_4_found = true; + } + if (e == ent_5) { + ent_5_found = true; + } + } + + test_bool(ent_1_found, true); + test_bool(ent_4_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_2_neq_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x != \"ent_2\", $x != \"ent_3\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + bool ent_1_found = false; + bool ent_4_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_uint(it.count, 0); + ecs_entity_t e = ecs_iter_get_var(&it, x_var); + test_assert(e != ent_2); + test_assert(e != ent_3); + if (e == ent_1) { + ent_1_found = true; + } + if (e == ent_4) { + ent_4_found = true; + } + if (e == ent_5) { + ent_5_found = true; + } + } + + test_bool(ent_1_found, true); + test_bool(ent_4_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_2_neq_id_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this != ent_2, $this != ent_3", + .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_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, it.entities[0]); + test_uint(ent_5, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_2_neq_name_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this != \"ent_2\", $this != \"ent_3\"", + .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_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, it.entities[0]); + test_uint(ent_5, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_2_neq_id_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x != ent_2, $x != ent_3", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_2_neq_name_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x != \"ent_2\", $x != \"ent_3\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_2_or_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + /* ecs_entity_t ent_1 = */ ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + /* ecs_entity_t ent_4 = */ ecs_entity(world, { .name = "ent_4" }); + /* ecs_entity_t ent_5 = */ ecs_entity(world, { .name = "ent_5" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == ent_2 || $this == ent_3", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_3_or_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + /* ecs_entity_t ent_1 = */ ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + /* ecs_entity_t ent_5 = */ ecs_entity(world, { .name = "ent_5" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == ent_2 || $this == ent_3 || $this == ent_4", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_2_or_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + /* ecs_entity_t ent_1 = */ ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + /* ecs_entity_t ent_4 = */ ecs_entity(world, { .name = "ent_4" }); + /* ecs_entity_t ent_5 = */ ecs_entity(world, { .name = "ent_5" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == \"ent_2\" || $this == \"ent_3\"", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_3_or_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + /* ecs_entity_t ent_1 = */ ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + /* ecs_entity_t ent_5 = */ ecs_entity(world, { .name = "ent_5" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == \"ent_2\" || $this == \"ent_3\" || $this == \"ent_4\"", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_2_or_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + /* ecs_entity_t ent_1 = */ ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_2_5 = ecs_entity(world, { .name = "ent_25" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_3_5 = ecs_entity(world, { .name = "ent_35" }); + /* ecs_entity_t ent_4 = */ ecs_entity(world, { .name = "ent_4" }); + /* ecs_entity_t ent_5 = */ ecs_entity(world, { .name = "ent_5" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"ent_2\" || $this ~= \"ent_3\"", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2_5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3_5, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_3_or_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + /* ecs_entity_t ent_1 = */ ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_2_5 = ecs_entity(world, { .name = "ent_25" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_entity_t ent_3_5 = ecs_entity(world, { .name = "ent_35" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_4_5 = ecs_entity(world, { .name = "ent_45" }); + /* ecs_entity_t ent_5 = */ ecs_entity(world, { .name = "ent_5" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"ent_2\" || $this ~= \"ent_3\" || $this ~= \"ent_4\"", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2_5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3_5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_4_5, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_2_or_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + /* ecs_entity_t ent_1 = */ ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + /* ecs_entity_t ent_4 = */ ecs_entity(world, { .name = "ent_4" }); + /* ecs_entity_t ent_5 = */ ecs_entity(world, { .name = "ent_5" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == ent_2 || $x == ent_3", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_2_or_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + /* ecs_entity_t ent_1 = */ ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + /* ecs_entity_t ent_4 = */ ecs_entity(world, { .name = "ent_4" }); + /* ecs_entity_t ent_5 = */ ecs_entity(world, { .name = "ent_5" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == \"ent_2\" || $x == \"ent_3\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_2_or_id_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this == ent_2 || $this == ent_3", + .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_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_3_or_id_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this == ent_2 || $this == ent_3 || $this == ent_4", + .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_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_2_or_name_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this == \"ent_2\" || $this == \"ent_3\"", + .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_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_2_or_id_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x == ent_2 || $x == ent_3", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_2_or_name_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent_2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "ent_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent_5" }); + ecs_add(world, ent_5, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x == \"ent_2\" || $x == \"ent_3\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_eq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + /* ecs_entity_t ent_2 = */ ecs_entity(world, { .name = "ent2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, Tag); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"nt_\"", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, it.entities[0]); + test_uint(ent_4, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_match_eq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + /* ecs_entity_t ent_2 = */ ecs_entity(world, { .name = "ent2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, Tag); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x ~= \"nt_\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_6, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_eq_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, RelA); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, RelA); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this ~= \"nt_\"", + .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_uint(1, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, it.entities[0]); + test_uint(ent_4, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_eq_written_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, RelA); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, RelA); + ecs_add(world, ent_6, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA($this|self), $this ~= \"nt_\"", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, it.entities[0]); + test_uint(ent_4, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void BuiltinPredicates_var_match_eq_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, RelA); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, RelA); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x ~= \"nt_\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_6, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_neq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, Tag); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"!nt_\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + bool ent_2_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != ent_1); + test_assert(it.entities[i] != ent_3); + test_assert(it.entities[i] != ent_4); + test_assert(it.entities[i] != ent_6); + if (it.entities[i] == ent_2) { + ent_2_found = true; + } + if (it.entities[i] == ent_5) { + ent_5_found = true; + } + } + } + + test_bool(ent_2_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_match_neq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, Tag); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x ~= \"!nt_\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + bool ent_2_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_uint(it.count, 0); + ecs_entity_t e = ecs_iter_get_var(&it, x_var); + test_assert(e != ent_1); + test_assert(e != ent_3); + test_assert(e != ent_4); + test_assert(e != ent_6); + if (e == ent_2) { + ent_2_found = true; + } + if (e == ent_5) { + ent_5_found = true; + } + } + + test_bool(ent_2_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_neq_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, RelA); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, RelA); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this ~= \"!nt_\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + bool ent_2_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != ent_1); + test_assert(it.entities[i] != ent_3); + test_assert(it.entities[i] != ent_4); + test_assert(it.entities[i] != ent_6); + if (it.entities[i] == ent_2) { + ent_2_found = true; + } + if (it.entities[i] == ent_5) { + ent_5_found = true; + } + } + } + + test_bool(ent_2_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_match_neq_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, RelA); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, RelA); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x ~= \"!nt_\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + bool ent_2_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_uint(it.count, 0); + ecs_entity_t e = ecs_iter_get_var(&it, x_var); + test_assert(e != ent_1); + test_assert(e != ent_3); + test_assert(e != ent_4); + test_assert(e != ent_6); + if (e == ent_2) { + ent_2_found = true; + } + if (e == ent_5) { + ent_5_found = true; + } + } + + test_bool(ent_2_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_2_neq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, Tag); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"!nt_\", $this ~= \"!_3\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + bool ent_2_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != ent_1); + test_assert(it.entities[i] != ent_3); + test_assert(it.entities[i] != ent_4); + test_assert(it.entities[i] != ent_6); + if (it.entities[i] == ent_2) { + ent_2_found = true; + } + if (it.entities[i] == ent_5) { + ent_5_found = true; + } + } + } + + test_bool(ent_2_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_match_2_neq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, Tag); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x ~= \"!nt_\", $x ~= \"!_3\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + bool ent_2_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + ecs_entity_t e = ecs_iter_get_var(&it, x_var); + test_assert(e != ent_1); + test_assert(e != ent_3); + test_assert(e != ent_4); + test_assert(e != ent_6); + if (e == ent_2) { + ent_2_found = true; + } + if (e == ent_5) { + ent_5_found = true; + } + } + + test_bool(ent_2_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_2_neq_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, RelA); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, RelA); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this ~= \"!nt_\", $this ~= \"!_3\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + bool ent_2_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != ent_1); + test_assert(it.entities[i] != ent_3); + test_assert(it.entities[i] != ent_4); + test_assert(it.entities[i] != ent_6); + if (it.entities[i] == ent_2) { + ent_2_found = true; + } + if (it.entities[i] == ent_5) { + ent_5_found = true; + } + } + } + + test_bool(ent_2_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_match_2_neq_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, RelA); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, RelA); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x ~= \"!nt_\", $x ~= \"!_3\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + bool ent_2_found = false; + bool ent_5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + ecs_entity_t e = ecs_iter_get_var(&it, x_var); + test_assert(e != ent_1); + test_assert(e != ent_3); + test_assert(e != ent_4); + test_assert(e != ent_6); + if (e == ent_2) { + ent_2_found = true; + } + if (e == ent_5) { + ent_5_found = true; + } + } + + test_bool(ent_2_found, true); + test_bool(ent_5_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_2_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + /* ecs_entity_t ent_2 = */ ecs_entity(world, { .name = "ent2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + /* ecs_entity_t ent_4 = */ ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, Tag); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"1\" || $this ~= \"_3\"", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_2_or_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, RelA); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, RelA); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this ~= \"1\" || $this ~= \"_3\"", + .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_uint(1, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_3_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + /* ecs_entity_t ent_2 = */ ecs_entity(world, { .name = "ent2" }); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + /* ecs_entity_t ent_4 = */ ecs_entity(world, { .name = "ent_4" }); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, Tag); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"1\" || $this ~= \"_3\" || $this ~= \"nt_6\"", + .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_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent_6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_this_match_3_or_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + + ecs_entity_t ent_1 = ecs_entity(world, { .name = "ent_1" }); + ecs_add(world, ent_1, RelA); + ecs_entity_t ent_2 = ecs_entity(world, { .name = "ent2" }); + ecs_add(world, ent_2, RelA); + ecs_entity_t ent_3 = ecs_entity(world, { .name = "nt_3" }); + ecs_add(world, ent_3, RelA); + ecs_entity_t ent_4 = ecs_entity(world, { .name = "ent_4" }); + ecs_add(world, ent_4, RelA); + ecs_entity_t ent_5 = ecs_entity(world, { .name = "ent5" }); + ecs_add(world, ent_5, RelA); + ecs_entity_t ent_6 = ecs_entity(world, { .name = "ent_6" }); + ecs_add(world, ent_6, RelA); + ecs_add(world, ent_6, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $this ~= \"1\" || $this ~= \"_3\" || $this ~= \"nt_6\"", + .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_uint(1, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(false, ecs_field_is_set(&it, 1)); + test_uint(ent_6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_unresolved_by_name(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == ent", + .cache_kind = cache_kind, + .flags = EcsQueryAllowUnresolvedByName + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(false, ecs_field_is_set(&it, 0)); + test_uint(ent, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_entity_t tgt = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == *, (Rel, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(tgt, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_entity_t tgt = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == _, (Rel, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(tgt, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_wildcard_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_entity_t tgt = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, $x), $x == *", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(tgt, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_any_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_entity_t tgt = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, $x), $x == _", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e, it.entities[0]); + test_uint(tgt, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_var_eq_after_var_0_src(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x(), $x == flecs", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(EcsFlecs, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_2_or_w_eq_lookup_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + ecs_entity_t child1 = ecs_entity(world, { .name = "hello" }); + ecs_add_pair(world, child1, EcsChildOf, e1); + ecs_add(world, child1, Bar); + + ecs_entity_t child2 = ecs_entity(world, { .name = "world" }); + ecs_add_pair(world, child2, EcsChildOf, e2); + ecs_add(world, child2, Bar); + + ecs_entity_t child3_1 = ecs_entity(world, { .name = "hello" }); + ecs_add_pair(world, child3_1, EcsChildOf, e3); + ecs_add(world, child3_1, Bar); + + ecs_entity_t child3_2 = ecs_entity(world, { .name = "world" }); + ecs_add_pair(world, child3_2, EcsChildOf, e3); + ecs_add(world, child3_2, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), $x == $this.hello || $x == $this.world, Bar($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int hello_var = ecs_query_find_var(q, "this.hello"); + test_assert(hello_var != -1); + + int world_var = ecs_query_find_var(q, "this.world"); + test_assert(world_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(child1, ecs_iter_get_var(&it, x_var)); + test_uint(child1, ecs_iter_get_var(&it, hello_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, world_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(child2, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, hello_var)); + test_uint(child2, ecs_iter_get_var(&it, world_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(child3_1, ecs_iter_get_var(&it, x_var)); + test_uint(child3_1, ecs_iter_get_var(&it, hello_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, world_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_3_or_w_eq_lookup_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_entity_t e4 = ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + ecs_entity_t child1 = ecs_entity(world, { .name = "hello" }); + ecs_add_pair(world, child1, EcsChildOf, e1); + ecs_add(world, child1, Bar); + + ecs_entity_t child2 = ecs_entity(world, { .name = "world" }); + ecs_add_pair(world, child2, EcsChildOf, e2); + ecs_add(world, child2, Bar); + + ecs_entity_t child3_1 = ecs_entity(world, { .name = "hello" }); + ecs_add_pair(world, child3_1, EcsChildOf, e3); + ecs_add(world, child3_1, Bar); + + ecs_entity_t child3_2 = ecs_entity(world, { .name = "world" }); + ecs_add_pair(world, child3_2, EcsChildOf, e3); + ecs_add(world, child3_2, Bar); + + ecs_entity_t child4 = ecs_entity(world, { .name = "zoo" }); + ecs_add_pair(world, child4, EcsChildOf, e4); + ecs_add(world, child4, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), $x == $this.hello || $x == $this.world || $x == $this.zoo, Bar($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int hello_var = ecs_query_find_var(q, "this.hello"); + test_assert(hello_var != -1); + + int world_var = ecs_query_find_var(q, "this.world"); + test_assert(world_var != -1); + + int zoo_var = ecs_query_find_var(q, "this.zoo"); + test_assert(zoo_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(child1, ecs_iter_get_var(&it, x_var)); + test_uint(child1, ecs_iter_get_var(&it, hello_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, world_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, zoo_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(child2, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, hello_var)); + test_uint(child2, ecs_iter_get_var(&it, world_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, zoo_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(child3_1, ecs_iter_get_var(&it, x_var)); + test_uint(child3_1, ecs_iter_get_var(&it, hello_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, world_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, zoo_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(child4, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, hello_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, world_var)); + test_uint(child4, ecs_iter_get_var(&it, zoo_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_2_or_w_eq_this(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, Rel, e1); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, Rel, e1); + + ecs_entity_t e3 = ecs_new(world); + ecs_add_pair(world, e3, Rel, Rel); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel($this, $x), $x == $this || $x == Rel", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, e1), ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Rel, Rel), ecs_field_id(&it, 0)); + test_uint(true, ecs_field_is_set(&it, 0)); + test_uint(Rel, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void BuiltinPredicates_eq_variable(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "$this == $", + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void BuiltinPredicates_eq_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == *", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_eq_any(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == _", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_neq_variable(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "$this != $", + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void BuiltinPredicates_neq_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this != *", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_neq_any(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this != _", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_match_variable(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"$\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_match_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"*\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void BuiltinPredicates_match_any(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"_\"", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Cached.c b/vendors/flecs/test/query/src/Cached.c new file mode 100644 index 000000000..70659bb1d --- /dev/null +++ b/vendors/flecs/test/query/src/Cached.c @@ -0,0 +1,3654 @@ +#include +#include + +void Cached_simple_query_existing_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_entity_t e = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = EcsQueryCacheAuto + }); + 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(e, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_simple_query_2_existing_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = EcsQueryCacheAuto + }); + 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(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_simple_query_new_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e = ecs_new_w(world, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_simple_query_2_new_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_simple_query_existing_and_new_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_wildcard_query_existing_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjB, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, *)", + .cache_kind = EcsQueryCacheAuto + }); + 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(e1, it.entities[0]); + test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_wildcard_query_new_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, *)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_wildcard_query_existing_table_2_results_p_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjC, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); + ecs_add_pair(world, e1, Rel, ObjC); + ecs_add_pair(world, e2, Rel, ObjC); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, *)", + .cache_kind = EcsQueryCacheAuto + }); + 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(e1, it.entities[0]); + test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, ObjC), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, ObjC), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_wildcard_query_new_table_2_results_p_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjC, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, *)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, ObjA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, ObjB); + ecs_add_pair(world, e1, Rel, ObjC); + ecs_add_pair(world, e2, Rel, ObjC); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, ObjA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, ObjC), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, ObjB), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, ObjC), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_wildcard_query_2nd_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add_pair(world, e1, Rel, TgtA); + ecs_add_pair(world, e1, Rel, TgtB); + + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_add_pair(world, e2, Rel, TgtA); + + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add_pair(world, e3, Rel, TgtA); + ecs_add_pair(world, e3, Rel, TgtC); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Tag }, { ecs_pair(Rel, EcsWildcard) }}, + .cache_kind = EcsQueryCacheAuto + }); + 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(e2, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtA), it.ids[1]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtA), it.ids[1]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtB), it.ids[1]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtA), it.ids[1]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtC), it.ids[1]); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_wildcard_query_2nd_term_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add_pair(world, e1, Rel, TgtA); + ecs_add_pair(world, e1, Rel, TgtB); + + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_add_pair(world, e2, Rel, TgtA); + + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add_pair(world, e3, Rel, TgtA); + ecs_add_pair(world, e3, Rel, TgtC); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Tag }, { ecs_pair(Rel, EcsWildcard), .src.id = EcsSelf }}, + .cache_kind = EcsQueryCacheAuto + }); + 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(e2, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtA), it.ids[1]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtA), it.ids[1]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtB), it.ids[1]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtA), it.ids[1]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, it.ids[0]); + test_uint(ecs_pair(Rel, TgtC), it.ids[1]); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_simple_query_existing_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add(world, e1, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_remove(world, e1, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_simple_query_existing_empty_type(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_PREFAB(world, TypeX, TagA, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add(world, e1, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_simple_query_new_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add(world, e1, TagB); + ecs_remove(world, e1, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_component_query_existing_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t e = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAuto + }); + 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(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_assert(ecs_field_size(&it, 0) == sizeof(Position)); + test_assert(ecs_field(&it, Position, 0) != NULL); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_component_query_new_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e = ecs_new_w(world, Position); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_assert(ecs_field(&it, Position, 0) != NULL); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_component_query_existing_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_remove(world, e, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_assert(ecs_field(&it, Position, 0) != NULL); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_2_component_query_existing_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + ECS_PREFAB(world, MyType, Position, Velocity); + + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + ecs_add(world, e, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_remove(world, e, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_assert(ecs_field(&it, Position, 0) != NULL); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_2_component_query_existing_empty_type(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_PREFAB(world, MyType, Position, Velocity); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + test_assert(ecs_field(&it, Position, 0) != NULL); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_only_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "?TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e = ecs_new_w(world, TagA); + int32_t count = 0; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + if (ecs_field_is_set(&it, 0)) { + test_assert(count == 0); + test_int(it.count, 1); + test_uint(it.entities[0], e); + count ++; + } + } + + test_int(count, 1); + + ecs_fini(world); +} + +void Cached_only_optional_new_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_entity_t e = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "?TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + int32_t count = 0, total_count = 0; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + if (ecs_field_is_set(&it, 0)) { + test_assert(count == 0); + test_int(it.count, 1); + test_uint(it.entities[0], e); + count ++; + } + total_count ++; + } + + test_int(count, 1); + test_assert(total_count >= count); + + int32_t prev_total_count = total_count; + total_count = 0; + + ecs_remove(world, e, TagA); + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(!ecs_field_is_set(&it, 0)); + test_assert(it.count > 0); + total_count ++; + } + + test_assert(total_count == (prev_total_count - 1)); + + + ecs_fini(world); +} + +void Cached_only_optional_new_empty_non_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t e = ecs_new_w(world, TagA); + ecs_add(world, e, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "?TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + int32_t count = 0, total_count = 0; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + if (ecs_field_is_set(&it, 0)) { + test_assert(count == 0); + test_int(it.count, 1); + test_uint(it.entities[0], e); + count ++; + } + total_count ++; + } + + test_int(count, 1); + test_assert(total_count >= count); + + int32_t prev_total_count = total_count; + count = 0; total_count = 0; + + ecs_remove(world, e, TagA); + ecs_table_t *table = ecs_get_table(world, e); + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(!ecs_field_is_set(&it, 0)); + test_assert(it.count > 0); + total_count ++; + if (it.entities[0] == e) { + test_assert(table == it.table); + count ++; + } + } + + test_int(count, 1); + test_assert(total_count == prev_total_count); + + + ecs_fini(world); +} + +void Cached_only_optional_new_unset_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); + + ecs_entity_t e = ecs_new_w(world, TagA); + ecs_add(world, e, TagB); + ecs_table_t *table = ecs_get_table(world, e); + + ecs_query_t *q = ecs_query(world, { + .expr = "?TagC", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + int32_t count = 0, total_count = 0; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(!ecs_field_is_set(&it, 0)); + test_assert(it.count > 0); + if (it.entities[0] == e) { + test_assert(table == it.table); + count ++; + } + total_count ++; + } + + test_int(count, 1); + test_assert(total_count >= count); + + int32_t prev_total_count = total_count; + count = 0; total_count = 0; + + ecs_remove(world, e, TagA); + table = ecs_get_table(world, e); + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(!ecs_field_is_set(&it, 0)); + test_assert(it.count > 0); + total_count ++; + if (it.entities[0] == e) { + test_assert(table == it.table); + count ++; + } + } + + test_int(count, 1); + test_assert(total_count == prev_total_count); + + + ecs_fini(world); +} + +void Cached_singleton_w_optional_new_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Singleton, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_singleton_add(world, Singleton); + + ecs_entity_t e = ecs_new_w(world, TagA); + ecs_set_name(world, e, "e"); + + ecs_query_t *q = ecs_query(world, { + .expr = "Singleton($), ?TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + int32_t count = 0, total_count = 0; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + if (ecs_field_is_set(&it, 1)) { + test_assert(count == 0); + test_int(it.count, 1); + test_uint(it.entities[0], e); + count ++; + } + total_count ++; + } + + test_int(count, 1); + test_assert(total_count >= count); + + int32_t prev_total_count = total_count; + total_count = 0; + + ecs_remove(world, e, TagA); + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(!ecs_field_is_set(&it, 1)); + test_assert(it.count > 0); + total_count ++; + } + + test_assert(total_count == prev_total_count); + + ecs_set_name(world, e, NULL); + total_count = 0; + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(!ecs_field_is_set(&it, 1)); + test_assert(it.count > 0); + total_count ++; + } + + test_assert(total_count == (prev_total_count - 1)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cached_singleton_w_optional_new_empty_non_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Singleton, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_singleton_add(world, Singleton); + + ecs_entity_t e = ecs_new_w(world, TagA); + ecs_add(world, e, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Singleton($), ?TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + int32_t count = 0, total_count = 0; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + if (ecs_field_is_set(&it, 1)) { + test_assert(count == 0); + test_int(it.count, 1); + test_uint(it.entities[0], e); + count ++; + } + total_count ++; + } + + test_int(count, 1); + test_assert(total_count >= count); + + int32_t prev_total_count = total_count; + count = 0; total_count = 0; + + ecs_remove(world, e, TagA); + ecs_table_t *table = ecs_get_table(world, e); + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(!ecs_field_is_set(&it, 1)); + test_assert(it.count > 0); + total_count ++; + if (it.entities[0] == e) { + test_assert(table == it.table); + count ++; + } + } + + test_int(count, 1); + test_assert(total_count == prev_total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cached_singleton_w_optional_new_unset_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Singleton, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); + + ecs_singleton_add(world, Singleton); + + ecs_entity_t e = ecs_new_w(world, TagA); + ecs_add(world, e, TagB); + ecs_table_t *table = ecs_get_table(world, e); + + ecs_query_t *q = ecs_query(world, { + .expr = "Singleton($), ?TagC", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + int32_t count = 0, total_count = 0; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(!ecs_field_is_set(&it, 1)); + test_assert(it.count > 0); + if (it.entities[0] == e) { + test_assert(table == it.table); + count ++; + } + total_count ++; + } + + test_int(count, 1); + test_assert(total_count >= count); + + int32_t prev_total_count = total_count; + count = 0; total_count = 0; + + ecs_remove(world, e, TagA); + table = ecs_get_table(world, e); + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(!ecs_field_is_set(&it, 1)); + test_assert(it.count > 0); + total_count ++; + if (it.entities[0] == e) { + test_assert(table == it.table); + count ++; + } + } + + test_int(count, 1); + test_assert(total_count == prev_total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cached_query_w_from_entity_match_after(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e" }); + ecs_entity_t e2 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, TagB(e)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_add(world, e1, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_field_src(&it, 0), 0); + test_uint(ecs_field_id(&it, 0), TagA); + test_uint(ecs_field_src(&it, 1), e1); + test_uint(ecs_field_id(&it, 1), TagB); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_query_w_from_singleton_match_after(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t e2 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, TagB($)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_singleton_add(world, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_field_src(&it, 0), 0); + test_uint(ecs_field_id(&it, 0), TagA); + test_uint(ecs_field_src(&it, 1), TagB); + test_uint(ecs_field_id(&it, 1), TagB); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_rematch_after_add_to_recycled_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_entity_t parent = ecs_new(world); + test_assert(parent != 0); + + ecs_delete(world, parent); + test_assert( !ecs_is_alive(world, parent)); + + parent = ecs_new(world); + test_assert(parent != 0); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent); + test_assert(e != 0); + test_assert( ecs_has_pair(world, e, EcsChildOf, parent)); + ecs_add(world, e, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), false); + + ecs_set(world, parent, Position, {10, 20}); + + ecs_run_aperiodic(world, 0); + + it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + test_assert(ecs_field_src(&it, 1) == parent); + test_bool(ecs_query_next(&it), false); + + ecs_fini(world); +} + +void Cached_query_rematch_optional_after_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add_pair(world, e1, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Velocity(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + /* First iteration, base doesn't have Velocity but query should match with + * entity anyway since the component is optional */ + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + + while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + + test_int(it.count, 1); + test_assert(p != NULL); + + if (it.entities[0] == e1) { + test_assert(v == NULL); + } else if (it.entities[0] == e2) { + test_assert(v == NULL); + } else if (it.entities[0] == e3) { + test_assert(v == NULL); + } + + count ++; + } + + test_int(count, 3); + + /* Add Velocity to base, should trigger rematch */ + ecs_add(world, base, Velocity); + + /* Trigger a merge, which triggers the rematch */ + ecs_readonly_begin(world, false); + ecs_readonly_end(world); + + /* Second iteration, base has Velocity and entity should be able to access + * the shared component. */ + it = ecs_query_iter(world, q); + count = 0; + + while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + + test_int(it.count, 1); + test_assert(p != NULL); + + if (it.entities[0] == e1) { + test_assert(ecs_field_is_set(&it, 1)); + test_assert(v != NULL); + } else if (it.entities[0] == e2) { + test_assert(!ecs_field_is_set(&it, 1)); + } else if (it.entities[0] == e3) { + test_assert(!ecs_field_is_set(&it, 1)); + } + + count ++; + } + test_int(count, 3); + + + ecs_fini(world); +} + +void Cached_get_owned_tag(void) { + install_test_abort(); + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_new_w(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + + test_expect_abort(); + test_assert(ecs_field_w_size(&it, 0, 0) == NULL); +} + +void Cached_get_shared_tag(void) { + install_test_abort(); + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_new_w_pair(world, EcsIsA, base); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + + test_expect_abort(); + test_assert(ecs_field_w_size(&it, 0, 0) == NULL); +} + +void Cached_explicit_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + /* Ensure query isn't deleted twice when deleting world */ + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cached_get_column_size(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(ecs_field_size(&it, 0), sizeof(Position)); + test_assert(!ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_stresstest_query_free(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Hello, (OnInstantiate, Inherit)); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Foo); + ecs_add(world, e, Bar); + ecs_add(world, e, Hello); + + /* Create & delete query to test if query is properly unregistered with + * the table */ + + for (int i = 0; i < 10000; i ++) { + ecs_query_t *q = ecs_query(world, { + .expr = "Foo", + .cache_kind = EcsQueryCacheAuto + }); + ecs_query_fini(q); + } + + /* If code did not crash, test passes */ + test_assert(true); + + ecs_fini(world); +} + +void Cached_query_optional_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add_id(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, ?TagB", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + + while (ecs_query_next(&it)) { + test_uint(ecs_field_id(&it, 0), TagA); + test_assert(ecs_field_id(&it, 1) == TagB); + test_int(it.count, 1); + + if (it.entities[0] == e1) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), false); + } else if (it.entities[0] == e2) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + } + + count ++; + } + + test_int(count, 2); + + + ecs_fini(world); +} + +void Cached_query_optional_shared_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add_id(world, e2, TagB); + + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, EcsIsA, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, ?TagB(self|up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + + while (ecs_query_next(&it)) { + test_uint(ecs_field_id(&it, 0), TagA); + test_assert(ecs_field_id(&it, 1) == TagB); + test_int(it.count, 1); + + if (it.entities[0] == e1) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), false); + } else if (it.entities[0] == e2) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + } else if (it.entities[0] == e3) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + } + + count ++; + } + + test_int(count, 3); + + + ecs_fini(world); +} + +void Cached_query_iter_10_tags(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagD, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagE, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagF, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagG, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagH, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagI, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagJ, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagK, (OnInstantiate, Inherit)); + + ecs_entity_t e_1 = ecs_new_w(world, TagA); + ecs_add_id(world, e_1, TagB); + ecs_add_id(world, e_1, TagC); + ecs_add_id(world, e_1, TagD); + ecs_add_id(world, e_1, TagE); + ecs_add_id(world, e_1, TagF); + ecs_add_id(world, e_1, TagG); + ecs_add_id(world, e_1, TagH); + ecs_add_id(world, e_1, TagI); + ecs_add_id(world, e_1, TagJ); + + ecs_entity_t e_2 = ecs_new_w(world, TagA); + ecs_add_id(world, e_2, TagB); + ecs_add_id(world, e_2, TagC); + ecs_add_id(world, e_2, TagD); + ecs_add_id(world, e_2, TagE); + ecs_add_id(world, e_2, TagF); + ecs_add_id(world, e_2, TagG); + ecs_add_id(world, e_2, TagH); + ecs_add_id(world, e_2, TagI); + ecs_add_id(world, e_2, TagJ); + ecs_add_id(world, e_2, TagK); /* 2nd match in different table */ + + ecs_query_t *q = ecs_query(world, { + .terms = { + {TagA}, {TagB}, {TagC}, {TagD}, {TagE}, {TagF}, {TagG}, {TagH}, + {TagI}, {TagJ} + }, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), TagA); + test_int(ecs_field_id(&it, 1), TagB); + test_int(ecs_field_id(&it, 2), TagC); + test_int(ecs_field_id(&it, 3), TagD); + test_int(ecs_field_id(&it, 4), TagE); + test_int(ecs_field_id(&it, 5), TagF); + test_int(ecs_field_id(&it, 6), TagG); + test_int(ecs_field_id(&it, 7), TagH); + test_int(ecs_field_id(&it, 8), TagI); + test_int(ecs_field_id(&it, 9), TagJ); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), TagA); + test_int(ecs_field_id(&it, 1), TagB); + test_int(ecs_field_id(&it, 2), TagC); + test_int(ecs_field_id(&it, 3), TagD); + test_int(ecs_field_id(&it, 4), TagE); + test_int(ecs_field_id(&it, 5), TagF); + test_int(ecs_field_id(&it, 6), TagG); + test_int(ecs_field_id(&it, 7), TagH); + test_int(ecs_field_id(&it, 8), TagI); + test_int(ecs_field_id(&it, 9), TagJ); + + test_assert(!ecs_query_next(&it)); + + + ecs_fini(world); +} + +typedef struct { + float v; +} CompA, CompB, CompC, CompD, CompE, CompF, CompG, CompH, CompI, CompJ, CompK, + CompL, CompM, CompN, CompO, CompP, CompQ, CompR, CompS, CompT, CompU; + +void Cached_query_iter_10_components(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, CompA); + ecs_add_pair(world, ecs_id(CompA), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompB); + ecs_add_pair(world, ecs_id(CompB), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompC); + ecs_add_pair(world, ecs_id(CompC), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompD); + ecs_add_pair(world, ecs_id(CompD), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompE); + ecs_add_pair(world, ecs_id(CompE), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompF); + ecs_add_pair(world, ecs_id(CompF), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompG); + ecs_add_pair(world, ecs_id(CompG), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompH); + ecs_add_pair(world, ecs_id(CompH), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompI); + ecs_add_pair(world, ecs_id(CompI), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompJ); + ecs_add_pair(world, ecs_id(CompJ), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, CompK); + ecs_add_pair(world, ecs_id(CompK), EcsOnInstantiate, EcsInherit); + + ecs_entity_t e_1 = ecs_insert(world, ecs_value(CompA, {10})); + ecs_set(world, e_1, CompB, {10}); + ecs_set(world, e_1, CompC, {10}); + ecs_set(world, e_1, CompD, {10}); + ecs_set(world, e_1, CompE, {10}); + ecs_set(world, e_1, CompF, {10}); + ecs_set(world, e_1, CompG, {10}); + ecs_set(world, e_1, CompH, {10}); + ecs_set(world, e_1, CompI, {10}); + ecs_set(world, e_1, CompJ, {10}); + + ecs_entity_t e_2 = ecs_insert(world, ecs_value(CompA, {10})); + ecs_set(world, e_2, CompB, {10}); + ecs_set(world, e_2, CompC, {10}); + ecs_set(world, e_2, CompD, {10}); + ecs_set(world, e_2, CompE, {10}); + ecs_set(world, e_2, CompF, {10}); + ecs_set(world, e_2, CompG, {10}); + ecs_set(world, e_2, CompH, {10}); + ecs_set(world, e_2, CompI, {10}); + ecs_set(world, e_2, CompJ, {10}); + ecs_set(world, e_2, CompK, {10}); + + ecs_query_t *q = ecs_query(world, { + .terms = { + {ecs_id(CompA)}, {ecs_id(CompB)}, {ecs_id(CompC)}, {ecs_id(CompD)}, + {ecs_id(CompE)}, {ecs_id(CompF)}, {ecs_id(CompG)}, {ecs_id(CompH)}, + {ecs_id(CompI)}, {ecs_id(CompJ)} + }, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_1); + test_int(ecs_field_id(&it, 0), ecs_id(CompA)); + test_int(ecs_field_id(&it, 1), ecs_id(CompB)); + test_int(ecs_field_id(&it, 2), ecs_id(CompC)); + test_int(ecs_field_id(&it, 3), ecs_id(CompD)); + test_int(ecs_field_id(&it, 4), ecs_id(CompE)); + test_int(ecs_field_id(&it, 5), ecs_id(CompF)); + test_int(ecs_field_id(&it, 6), ecs_id(CompG)); + test_int(ecs_field_id(&it, 7), ecs_id(CompH)); + test_int(ecs_field_id(&it, 8), ecs_id(CompI)); + test_int(ecs_field_id(&it, 9), ecs_id(CompJ)); + + int i; + for (i = 0; i < 10; i ++) { + CompA *ptr = ecs_field_w_size(&it, 0, i); + test_assert(ptr != NULL); + test_int(ptr[0].v, 10); + } + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e_2); + test_int(ecs_field_id(&it, 0), ecs_id(CompA)); + test_int(ecs_field_id(&it, 1), ecs_id(CompB)); + test_int(ecs_field_id(&it, 2), ecs_id(CompC)); + test_int(ecs_field_id(&it, 3), ecs_id(CompD)); + test_int(ecs_field_id(&it, 4), ecs_id(CompE)); + test_int(ecs_field_id(&it, 5), ecs_id(CompF)); + test_int(ecs_field_id(&it, 6), ecs_id(CompG)); + test_int(ecs_field_id(&it, 7), ecs_id(CompH)); + test_int(ecs_field_id(&it, 8), ecs_id(CompI)); + test_int(ecs_field_id(&it, 9), ecs_id(CompJ)); + + for (i = 0; i < 10; i ++) { + CompA *ptr = ecs_field_w_size(&it, 0, i); + test_assert(ptr != NULL); + test_int(ptr[0].v, 10); + } + + test_assert(!ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_iter_type_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t e = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e); + test_assert(it.table != NULL); + const ecs_type_t *type = ecs_table_get_type(it.table); + test_int(type->count, 1); + test_int(type->array[0], ecs_id(Position)); + + test_assert(!ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_filter_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_id(Position), .inout = EcsInOutNone }}, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_assert(it.ids != NULL); + test_assert(it.ids[0] == ecs_id(Position)); + + test_int(it.count, 1); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e); + + test_assert(it.trs != NULL); + + test_bool(ecs_query_next(&it), false); + + ecs_fini(world); +} + +void Cached_2_terms_1_filter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Velocity) } + }, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set(world, e, Velocity, {1, 1}); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_assert(it.ids != NULL); + test_assert(it.ids[0] == ecs_id(Position)); + test_assert(it.ids[1] == ecs_id(Velocity)); + + test_int(it.count, 1); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e); + + test_assert(it.sizes != NULL); + test_assert(it.trs != NULL); + + test_assert(ecs_field_w_size(&it, 0, 1) != NULL); + + test_bool(ecs_query_next(&it), false); + + ecs_fini(world); +} + +void Cached_3_terms_2_filter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Velocity), .inout = EcsInOutNone }, + { .id = ecs_id(Mass) } + }, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set(world, e, Velocity, {1, 1}); + ecs_set(world, e, Mass, {1}); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_assert(it.ids != NULL); + test_assert(it.ids[0] == ecs_id(Position)); + test_assert(it.ids[1] == ecs_id(Velocity)); + test_assert(it.ids[2] == ecs_id(Mass)); + + test_int(it.count, 1); + test_assert(it.entities != NULL); + test_assert(it.entities[0] == e); + + test_assert(it.sizes != NULL); + test_assert(it.trs != NULL); + + test_assert(ecs_field_w_size(&it, 0, 2) != NULL); + + test_bool(ecs_query_next(&it), false); + + ecs_fini(world); +} + +void Cached_add_singleton_after_query(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA }, + { TagB, .src.id = TagB } + }, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, TagA); + + ecs_singleton_add(world, TagB); + test_assert(ecs_has(world, TagB, TagB)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool (ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e); + test_bool (ecs_query_next(&it), false); + + ecs_singleton_remove(world, TagB); + test_assert(!ecs_has(world, TagB, TagB)); + + it = ecs_query_iter(world, q); + test_bool (ecs_query_next(&it), false); + + ecs_fini(world); +} + +void Cached_query_w_component_from_parent_from_non_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t parent = ecs_new_w(world, TagB); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA }, + { TagB, .src.id = child|EcsUp, .trav = EcsChildOf } + }, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool (ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e); + test_int(it.sources[0], 0); + test_int(it.sources[1], parent); + test_bool (ecs_query_next(&it), false); + + ecs_remove_pair(world, child, EcsChildOf, parent); + it = ecs_query_iter(world, q); + test_bool (ecs_query_next(&it), false); + + ecs_fini(world); +} + +void Cached_create_query_while_pending(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t e = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ TagA }}, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_add(world, e, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert( ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e); + + test_assert( !ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_empty_query(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ 0 }}, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + + ecs_fini(world); +} + +void Cached_implicit_existing_isa_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], base); + test_uint(it.sources[0], 0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_implicit_new_isa_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], base); + test_uint(it.sources[0], 0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_superset_2_lvls(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], base); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], base); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_superset_3_lvls(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], base); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], base); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], base); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_superset_2_lvls_owned(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); + + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], base); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e1); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_superset_3_lvls_owned(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); + + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + ecs_add(world, e3, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], base); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e1); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e2); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_superset_owned_empty_table_after_match(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, e2); + + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + ecs_add(world, e3, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], base); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e1); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e2); + + test_bool(false, ecs_query_next(&it)); + + ecs_remove_pair(world, e3, EcsIsA, e2); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], base); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e1); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_self_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(self|up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], base); + test_uint(it.sources[0], 0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_childof_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(up)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_superset_2_targets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, R, Traversable); + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t e_0 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e_1 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e_2 = ecs_insert(world, ecs_value(Position, {50, 60})); + ecs_entity_t e_3 = ecs_insert(world, ecs_value(Position, {70, 80})); + + ecs_add_pair(world, e_3, R, e_2); + ecs_add_pair(world, e_2, R, e_1); + ecs_add_pair(world, e_1, R, e_0); + + ecs_entity_t t = ecs_new(world); + ecs_add(world, t, Position); + ecs_add_pair(world, t, R, e_2); + ecs_add_pair(world, t, R, e_1); + ecs_delete(world, t); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up R)", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_iter_t it = ecs_query_iter(world, q); + { + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_1); + test_uint(it.sources[0], e_0); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + { + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_3); + test_uint(it.sources[0], e_2); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 50); + test_int(p[0].y, 60); + } + + { + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_2); + test_uint(it.sources[0], e_1); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_superset_2_relations(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = TagA, .trav = EcsChildOf, .src.id = EcsUp }, + { .id = TagA, .trav = EcsIsA, .src.id = EcsUp }, + }, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_entity_t base = ecs_new_w(world, TagA); + ecs_entity_t parent = ecs_new_w(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, base); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, base); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(parent, it.sources[0]); + test_uint(base, it.sources[1]); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_superset_2_relations_instanced(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = TagA, .trav = EcsChildOf, .src.id = EcsUp }, + { .id = TagA, .trav = EcsIsA, .src.id = EcsUp }, + }, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_entity_t base = ecs_new_w(world, TagA); + ecs_entity_t parent = ecs_new_w(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, base); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, base); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(parent, it.sources[0]); + test_uint(base, it.sources[1]); + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_superset_2_relations_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .trav = EcsChildOf, .src.id = EcsUp }, + { .id = ecs_id(Position), .trav = EcsIsA, .src.id = EcsUp }, + }, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {30, 40})); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, base); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, base); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_iter_t it = ecs_query_iter(world, q); + { + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(parent, it.sources[0]); + test_uint(base, it.sources[1]); + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cached_superset_2_relations_instanced_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .trav = EcsChildOf, .src.id = EcsUp }, + { .id = ecs_id(Position), .trav = EcsIsA, .src.id = EcsUp }, + }, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {30, 40})); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, base); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, base); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_iter_t it = ecs_query_iter(world, q); + { + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(parent, it.sources[0]); + test_uint(base, it.sources[1]); + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_int(p1->x, 30); + test_int(p1->y, 40); + test_int(p2->x, 10); + test_int(p2->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + + ecs_fini(world); +} + +void Cached_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(up)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new_w(world, Tag); + ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_not_pair_relation_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, !(*, ObjA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_entity_t e4 = ecs_new_w(world, Foo); + + ecs_add_pair(world, e1, RelA, ObjA); + ecs_add_pair(world, e2, RelA, ObjB); + ecs_add_pair(world, e3, RelB, ObjA); + ecs_add_pair(world, e4, RelB, ObjB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_field_id(&it, 1), ecs_pair(EcsWildcard, ObjA)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e4); + test_uint(ecs_field_id(&it, 1), ecs_pair(EcsWildcard, ObjA)); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_not_pair_object_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, !(RelA, *)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_entity_t e4 = ecs_new_w(world, Foo); + + ecs_add_pair(world, e1, RelA, ObjA); + ecs_add_pair(world, e2, RelA, ObjB); + ecs_add_pair(world, e3, RelB, ObjA); + ecs_add_pair(world, e4, RelB, ObjB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_field_id(&it, 1), ecs_pair(RelA, EcsWildcard)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e4); + test_uint(ecs_field_id(&it, 1), ecs_pair(RelA, EcsWildcard)); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_two_pair_wildcards_one_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (RelA, *), !(RelB, *)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_entity_t e4 = ecs_new_w(world, Foo); + + ecs_add_pair(world, e1, RelA, ObjA); + ecs_add_pair(world, e1, RelB, ObjA); + + ecs_add_pair(world, e2, RelA, ObjA); + + ecs_add_pair(world, e3, RelA, ObjB); + ecs_add_pair(world, e3, RelB, ObjB); + + ecs_add_pair(world, e4, RelA, ObjB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_field_id(&it, 0), Foo); + test_uint(ecs_field_id(&it, 1), ecs_pair(RelA, ObjA)); + test_uint(ecs_field_id(&it, 2), ecs_pair(RelB, EcsWildcard)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e4); + test_uint(ecs_field_id(&it, 0), Foo); + test_uint(ecs_field_id(&it, 1), ecs_pair(RelA, ObjB)); + test_uint(ecs_field_id(&it, 2), ecs_pair(RelB, EcsWildcard)); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_two_pair_wildcards_one_not_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, ObjB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (RelA, *), !(RelB, _)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_entity_t e4 = ecs_new_w(world, Foo); + + ecs_add_pair(world, e1, RelA, ObjA); + ecs_add_pair(world, e1, RelB, ObjA); + + ecs_add_pair(world, e2, RelA, ObjA); + + ecs_add_pair(world, e3, RelA, ObjB); + ecs_add_pair(world, e3, RelB, ObjB); + + ecs_add_pair(world, e4, RelA, ObjB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_field_id(&it, 0), Foo); + test_uint(ecs_field_id(&it, 1), ecs_pair(RelA, ObjA)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e4); + test_uint(ecs_field_id(&it, 0), Foo); + test_uint(ecs_field_id(&it, 1), ecs_pair(RelA, ObjB)); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_rematch(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_1); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 2); + test_uint(it.entities[0], base_1); + test_uint(it.entities[1], base_2); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 30); + test_int(p[1].y, 40); + test_uint(it.sources[0], 0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_uint(it.sources[0], base_1); + test_bool(false, ecs_query_next(&it)); + + ecs_remove_pair(world, inst, EcsIsA, base_1); + ecs_add_pair(world, inst, EcsIsA, base_2); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 2); + test_uint(it.entities[0], base_1); + test_uint(it.entities[1], base_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 30); + test_int(p[1].y, 40); + test_uint(it.sources[0], 0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_uint(it.sources[0], base_2); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_childof_rematch(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base_1); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_uint(it.sources[0], base_1); + test_bool(false, ecs_query_next(&it)); + + ecs_add_pair(world, inst, EcsChildOf, base_2); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_uint(it.sources[0], base_2); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_unmatch(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_1); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 2); + test_uint(it.entities[0], base_1); + test_uint(it.entities[1], base_2); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 30); + test_int(p[1].y, 40); + test_uint(it.sources[0], 0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_uint(it.sources[0], base_1); + test_bool(false, ecs_query_next(&it)); + + ecs_remove(world, base_1, Position); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], base_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_uint(it.sources[0], 0); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_childof_unmatch(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base_1); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_uint(it.sources[0], base_1); + test_bool(false, ecs_query_next(&it)); + + ecs_remove(world, base_1, Position); + + it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_isa_rematch_2_lvls(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t base = ecs_new_w_pair(world, EcsIsA, base_1); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 2); + test_uint(it.entities[0], base_1); + test_uint(it.entities[1], base_2); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 30); + test_int(p[1].y, 40); + test_uint(it.sources[0], 0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], base); + test_uint(it.sources[0], base_1); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base_1); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_remove_pair(world, base, EcsIsA, base_1); + ecs_add_pair(world, base, EcsIsA, base_2); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 2); + test_uint(it.entities[0], base_1); + test_uint(it.entities[1], base_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 30); + test_int(p[1].y, 40); + test_uint(it.sources[0], 0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], base); + test_uint(it.sources[0], base_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_childof_rematch_2_lvls(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t base = ecs_new_w_pair(world, EcsChildOf, base_1); + ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], base); + test_uint(it.sources[0], base_1); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base_1); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_add_pair(world, base, EcsChildOf, base_2); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], base); + test_uint(it.sources[0], base_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_childof_rematch_from_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t base = ecs_new_w_pair(world, EcsIsA, base_1); + ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, base); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base_1); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_remove_pair(world, base, EcsIsA, base_1); + ecs_add_pair(world, base, EcsIsA, base_2); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_uint(it.sources[0], base_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_rematch_optional_ref(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, ?Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t child = ecs_new_w(world, Tag); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(0, it.sources[0]); + test_uint(parent, it.sources[1]); + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_remove(world, parent, Position); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(0, it.sources[0]); + test_uint(0, it.sources[1]); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_rematch_optional_ref_w_2_refs(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, Velocity(up), ?Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set(world, parent, Velocity, {1, 2}); + ecs_entity_t child = ecs_new_w(world, Tag); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(0, it.sources[0]); + test_uint(parent, it.sources[1]); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + Position *p = ecs_field(&it, Position, 2); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_remove(world, parent, Position); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(0, it.sources[0]); + test_uint(parent, it.sources[1]); + test_uint(0, it.sources[2]); + v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_rematch_optional_ref_tag_w_ref_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, ?Position(up), TagB(up)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_add(world, parent, TagB); + + ecs_entity_t child = ecs_new_w(world, TagA); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(0, it.sources[0]); + test_uint(parent, it.sources[1]); + test_uint(parent, it.sources[2]); + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_remove(world, parent, Position); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(0, it.sources[0]); + test_uint(0, it.sources[1]); + test_uint(parent, it.sources[2]); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_match_query_expr_from_scope(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t foo = ecs_entity_init(world, &(ecs_entity_desc_t){ + .name = "Foo" + }); + + ecs_entity_t bar = ecs_entity_init(world, &(ecs_entity_desc_t){ + .name = "Foo.Bar" + }); + + ecs_set_scope(world, foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_set_scope(world, 0); + + ecs_entity_t e = ecs_new_w_id(world, bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(bar, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_query_long_or_w_ref(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ECS_ENTITY(world, A, (OnInstantiate, Inherit)); + ECS_ENTITY(world, B, (OnInstantiate, Inherit)); + ECS_ENTITY(world, C, (OnInstantiate, Inherit)); + ECS_ENTITY(world, D, (OnInstantiate, Inherit)); + ECS_ENTITY(world, E, (OnInstantiate, Inherit)); + ECS_ENTITY(world, F, (OnInstantiate, Inherit)); + ECS_ENTITY(world, G, (OnInstantiate, Inherit)); + ECS_ENTITY(world, H, (OnInstantiate, Inherit)); + ECS_ENTITY(world, I, (OnInstantiate, Inherit)); + ECS_ENTITY(world, J, (OnInstantiate, Inherit)); + ECS_ENTITY(world, K, (OnInstantiate, Inherit)); + ECS_ENTITY(world, L, (OnInstantiate, Inherit)); + ECS_ENTITY(world, M, (OnInstantiate, Inherit)); + ECS_ENTITY(world, N, (OnInstantiate, Inherit)); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + ecs_entity_t e2 = ecs_new_w(world, A); + + ecs_query_t *q = ecs_query(world, { + .expr = + "Position(e), A || B || C || D || E || F || G || H || I || J || K || L ||" + "M || N", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(A, ecs_field_id(&it, 1)); + test_uint(e2, it.entities[0]); + test_uint(e, it.sources[0]); + test_uint(0, it.sources[1]); + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_query_w_pair_id_and_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Obj, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Subj, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .id = ecs_pair(Rel, Obj), .src.id = Subj + }}, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_add_pair(world, Subj, Rel, Obj); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 0); + test_uint(it.sources[0], Subj); + test_uint(it.ids[0], ecs_pair(Rel, Obj)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cached_rematch_after_delete_inherited_tag(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_new(world); + ecs_entity_t base = ecs_new_w_id(world, tag); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = tag } + }, + .cache_kind = EcsQueryCacheAuto + }); + 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(base, it.entities[0]); + test_uint(tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(tag, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_delete_with(world, tag); + + it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cached_rematch_after_delete_rel_of_inherited_pair(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t rel = ecs_new(world); + ecs_entity_t obj = ecs_new(world); + ecs_entity_t base = ecs_new_w_pair(world, rel, obj); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_pair(rel, obj) } + }, + .cache_kind = EcsQueryCacheAuto + }); + 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(base, it.entities[0]); + test_uint(ecs_pair(rel, obj), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(ecs_pair(rel, obj), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_delete_with(world, ecs_pair(rel, EcsWildcard)); + + it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_rematch_after_delete_obj_of_inherited_pair(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t rel = ecs_new(world); + ecs_entity_t obj = ecs_new(world); + ecs_entity_t base = ecs_new_w_pair(world, rel, obj); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_pair(rel, obj) } + }, + .cache_kind = EcsQueryCacheAuto + }); + 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(base, it.entities[0]); + test_uint(ecs_pair(rel, obj), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(ecs_pair(rel, obj), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_delete_with(world, ecs_pair(EcsWildcard, obj)); + + it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_rematch_empty(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Foo }}, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_entity_t base = ecs_new(world); + ecs_table_t *table = ecs_table_add_id(world, NULL, ecs_pair(EcsIsA, base)); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, base, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e = ecs_new_w_table(world, table); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(base, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cached_rematch_empty_table_w_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Foo }, { Bar, .oper = EcsNot }}, + .cache_kind = EcsQueryCacheAuto + }); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + ecs_add(world, base, Foo); + test_assert( ecs_is_alive(world, base)); + + ecs_delete_with(world, Foo); + test_assert( !ecs_is_alive(world, base)); + + base = ecs_new_w_id(world, EcsPrefab); + ecs_add(world, base, Foo); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); + ecs_add(world, inst, Bar); + + ecs_run_aperiodic(world, 0); // force table (IsA, base) to empty list + + ecs_remove(world, inst, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void Cached_2_self_up_terms_new_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag, Foo(self|up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t parent_a = ecs_entity(world, {0}); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity_t e_3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e_2)) }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_b, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Cached_this_self_up_childof_pair_new_tables(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tgt, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(self|up, Tgt)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_entity_t p0 = ecs_new(world); + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, Tgt); + ecs_entity_t p2 = ecs_new_w_pair(world, Rel, Tgt); + ecs_entity_t p3 = ecs_new_w_pair(world, Rel, Tgt); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p1); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, p1); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, p2); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, p2); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_new_w_pair(world, EcsChildOf, e2); + ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, e2); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, p3); + + ecs_new_w_pair(world, EcsChildOf, p0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Cascade.c b/vendors/flecs/test/query/src/Cascade.c new file mode 100644 index 000000000..f5590e5cb --- /dev/null +++ b/vendors/flecs/test/query/src/Cascade.c @@ -0,0 +1,1081 @@ +#include + +void Cascade_existing_isa_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(EcsIsA, e0)) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(EcsIsA, e1)) }); + ecs_entity_t e3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(EcsIsA, e2)) }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_add_id(world, e1, Bar); /* mix up order */ + ecs_add_id(world, e2, Foo); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e0); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_new_isa_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(EcsIsA, e0)) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(EcsIsA, e1)) }); + ecs_entity_t e3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(EcsIsA, e2)) }); + + ecs_add_id(world, e2, Foo); /* mix up order */ + ecs_add_id(world, e1, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e0); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_childof_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_entity(world, { .parent = e0 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = e1 }); + ecs_entity_t e3 = ecs_entity(world, { .parent = e2 }); + + ecs_add_id(world, e2, Foo); /* mix up order */ + ecs_add_id(world, e1, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e0); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_parent_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_entity(world, { .parent = e0 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = e1 }); + ecs_entity_t e3 = ecs_entity(world, { .parent = e2 }); + + ecs_add_id(world, e2, Foo); /* mix up order */ + ecs_add_id(world, e1, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e0); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_existing_custom_rel_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_ENTITY(world, Rel, Traversable); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, e0)) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, e1)) }); + ecs_entity_t e3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, e2)) }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade Rel)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_add_id(world, e2, Foo); /* mix up order */ + ecs_add_id(world, e1, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e0); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_new_custom_rel_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_ENTITY(world, Rel, Traversable); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade Rel)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, e0)) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, e1)) }); + ecs_entity_t e3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, e2)) }); + + ecs_add_id(world, e2, Foo); /* mix up order */ + ecs_add_id(world, e1, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e0); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_w_2_depths(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_ENTITY(world, Rel, Traversable); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade Rel)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, Rel, e0); + ecs_add_pair(world, e2, Rel, e1); + ecs_add_pair(world, e3, Rel, e2); + + ecs_add_id(world, e2, Foo); /* mix up order */ + ecs_add_id(world, e1, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e1); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e1); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_w_3_depths(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_ENTITY(world, Rel, Traversable); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade Rel)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, Rel, e0); + ecs_add_pair(world, e2, Rel, e1); + ecs_add_pair(world, e3, Rel, e2); + + ecs_add_id(world, e2, Foo); /* mix up order */ + ecs_add_id(world, e1, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e1); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e2); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_w_2_depths_desc(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_ENTITY(world, Rel, Traversable); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade|desc Rel)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, Rel, e0); + ecs_add_pair(world, e2, Rel, e1); + ecs_add_pair(world, e3, Rel, e2); + + ecs_add_id(world, e2, Foo); /* mix up order */ + ecs_add_id(world, e1, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e1); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e1); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_w_3_depths_desc(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_ENTITY(world, Rel, Traversable); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag(cascade|desc Rel)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e0 = ecs_new_w(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, Rel, e0); + ecs_add_pair(world, e2, Rel, e1); + ecs_add_pair(world, e3, Rel, e2); + + ecs_add_id(world, e2, Foo); /* mix up order */ + ecs_add_id(world, e1, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.sources[0], e2); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(it.sources[0], e1); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.sources[0], e0); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_rematch_2_lvls(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e_0 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e_1 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e_2 = ecs_insert(world, ecs_value(Position, {50, 60})); + ecs_entity_t e_3 = ecs_insert(world, ecs_value(Position, {70, 80})); + ecs_add_pair(world, e_3, EcsChildOf, e_2); + + ecs_add_pair(world, e_2, EcsChildOf, e_1); + ecs_add_pair(world, e_1, EcsChildOf, e_0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(cascade)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_1); + test_uint(it.sources[0], e_0); + { + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_2); + test_uint(it.sources[0], e_1); + { + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_3); + test_uint(it.sources[0], e_2); + { + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 50); + test_int(p[0].y, 60); + test_bool(false, ecs_query_next(&it)); + } + } + + ecs_remove_pair(world, e_1, EcsChildOf, EcsWildcard); + ecs_remove_pair(world, e_2, EcsChildOf, EcsWildcard); + ecs_remove_pair(world, e_3, EcsChildOf, EcsWildcard); + + ecs_add_pair(world, e_0, EcsChildOf, e_1); + ecs_add_pair(world, e_1, EcsChildOf, e_2); + ecs_add_pair(world, e_2, EcsChildOf, e_3); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_2); + test_uint(it.sources[0], e_3); + { + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 70); + test_int(p[0].y, 80); + } + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_1); + test_uint(it.sources[0], e_2); + { + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 50); + test_int(p[0].y, 60); + } + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_0); + test_uint(it.sources[0], e_1); + { + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_rematch_2_lvls_2_relations(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, R, Traversable); + ECS_COMPONENT(world, Position); + + ecs_entity_t e_0 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e_1 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e_2 = ecs_insert(world, ecs_value(Position, {50, 60})); + ecs_entity_t e_3 = ecs_insert(world, ecs_value(Position, {70, 80})); + + ecs_add_pair(world, e_3, R, e_2); + ecs_add_pair(world, e_2, R, e_1); + ecs_add_pair(world, e_1, R, e_0); + + ecs_entity_t t = ecs_new(world); + ecs_add(world, t, Position); + ecs_add_pair(world, t, R, e_2); + ecs_add_pair(world, t, R, e_1); + ecs_delete(world, t); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(cascade R)", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_1); + test_uint(it.sources[0], e_0); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_2); + test_uint(it.sources[0], e_1); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_3); + test_uint(it.sources[0], e_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 50); + test_int(p[0].y, 60); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_topological(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, R, Traversable); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + + ecs_add_pair(world, e3, R, e1); + ecs_add_pair(world, e3, R, e2); + ecs_add_pair(world, e3, R, e4); + ecs_add_pair(world, e1, R, e5); + ecs_add_pair(world, e2, R, e6); + ecs_add_pair(world, e4, R, e1); + ecs_add_pair(world, e4, R, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, ?Tag(cascade R)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(it.entities[0], e5); + test_uint(it.entities[1], e6); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(it.entities[0], e1); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(it.entities[0], e2); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(it.entities[0], e4); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(it.entities[0], e3); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_desc_rematch_2_lvls(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e_0 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e_1 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e_2 = ecs_insert(world, ecs_value(Position, {50, 60})); + ecs_entity_t e_3 = ecs_insert(world, ecs_value(Position, {70, 80})); + + ecs_add_pair(world, e_3, EcsChildOf, e_2); + ecs_add_pair(world, e_2, EcsChildOf, e_1); + ecs_add_pair(world, e_1, EcsChildOf, e_0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(cascade|desc)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_3); + test_uint(it.sources[0], e_2); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 50); + test_int(p[0].y, 60); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_2); + test_uint(it.sources[0], e_1); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_1); + test_uint(it.sources[0], e_0); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + + test_bool(false, ecs_query_next(&it)); + + ecs_remove_pair(world, e_1, EcsChildOf, EcsWildcard); + ecs_remove_pair(world, e_2, EcsChildOf, EcsWildcard); + ecs_remove_pair(world, e_3, EcsChildOf, EcsWildcard); + + ecs_add_pair(world, e_0, EcsChildOf, e_1); + ecs_add_pair(world, e_1, EcsChildOf, e_2); + ecs_add_pair(world, e_2, EcsChildOf, e_3); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_0); + test_uint(it.sources[0], e_1); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_1); + test_uint(it.sources[0], e_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 50); + test_int(p[0].y, 60); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_2); + test_uint(it.sources[0], e_3); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 70); + test_int(p[0].y, 80); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_desc_rematch_2_lvls_2_relations(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, R, Traversable); + ECS_COMPONENT(world, Position); + + ecs_entity_t e_0 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e_1 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e_2 = ecs_insert(world, ecs_value(Position, {50, 60})); + ecs_entity_t e_3 = ecs_insert(world, ecs_value(Position, {70, 80})); + + ecs_add_pair(world, e_3, R, e_2); + ecs_add_pair(world, e_2, R, e_1); + ecs_add_pair(world, e_1, R, e_0); + + ecs_entity_t t = ecs_new(world); + ecs_add(world, t, Position); + ecs_add_pair(world, t, R, e_2); + ecs_add_pair(world, t, R, e_1); + ecs_delete(world, t); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(cascade|desc R)", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_3); + test_uint(it.sources[0], e_2); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 50); + test_int(p[0].y, 60); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_2); + test_uint(it.sources[0], e_1); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_1); + test_uint(it.sources[0], e_0); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_desc_topological(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, R, Traversable); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + + ecs_add_pair(world, e3, R, e1); + ecs_add_pair(world, e3, R, e2); + ecs_add_pair(world, e3, R, e4); + ecs_add_pair(world, e1, R, e5); + ecs_add_pair(world, e2, R, e6); + ecs_add_pair(world, e4, R, e1); + ecs_add_pair(world, e4, R, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, ?Tag(cascade|desc R)", + .cache_kind = EcsQueryCacheAuto + }); + 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(it.entities[0], e3); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(it.entities[0], e4); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(it.entities[0], e1); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(it.entities[0], e2); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(it.entities[0], e5); + test_uint(it.entities[1], e6); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Cascade_cascade_after_recycled_parent_change(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, ?Position(cascade)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_entity_t parent = ecs_new(world); + test_assert(parent != 0); + ecs_entity_t child = ecs_new(world); + test_assert(child != 0); + + ecs_delete(world, parent); + test_assert( !ecs_is_alive(world, parent)); + ecs_delete(world, child); + test_assert( !ecs_is_alive(world, child)); + + parent = ecs_new_w_id(world, Tag); + test_assert(parent != 0); + child = ecs_new_w_id(world, Tag); + test_assert(child != 0); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_entity_t e = ecs_new_w_id(world, Tag); + test_assert(e != 0); + + ecs_add_pair(world, e, EcsChildOf, child); + test_assert( ecs_has_pair(world, e, EcsChildOf, child)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_uint(it.entities[0], parent); + test_assert(ecs_field_src(&it, 1) == 0); + test_assert(!ecs_field_is_set(&it, 1)); + const Position *p = ecs_field(&it, Position, 1); + test_assert(p == NULL); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_uint(it.entities[0], child); + test_assert(ecs_field_src(&it, 1) == 0); + test_assert(!ecs_field_is_set(&it, 1)); + p = ecs_field(&it, Position, 1); + test_assert(p == NULL); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_assert(ecs_field_src(&it, 1) == 0); + test_assert(!ecs_field_is_set(&it, 1)); + p = ecs_field(&it, Position, 1); + test_assert(p == NULL); + + test_bool(ecs_query_next(&it), false); + + ecs_set(world, parent, Position, {10, 20}); + ecs_set(world, child, Position, {20, 30}); + + ecs_run_aperiodic(world, 0); + + it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_uint(it.entities[0], parent); + test_assert(!ecs_field_is_set(&it, 1)); + p = ecs_field(&it, Position, 1); + test_assert(p == NULL); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_uint(it.entities[0], child); + test_assert(ecs_field_src(&it, 1) == parent); + test_assert(ecs_field_is_set(&it, 1)); + p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_assert(ecs_field_src(&it, 1) == child); + test_assert(ecs_field_is_set(&it, 1)); + p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + + test_bool(ecs_query_next(&it), false); + + ecs_fini(world); +} + +void Cascade_invalid_cascade_for_uncached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_log_set_level(-4); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(cascade)" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Cascade_invalid_cascade_for_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Tag | EcsCascade + }}, + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Cascade_invalid_cascade_for_second(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Tag, + .second.id = EcsCascade + }}, + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Cascade_invalid_desc_without_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Tag, + .src.id = EcsDesc + }}, + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Cascade_invalid_desc_for_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Tag | EcsDesc + }}, + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Cascade_invalid_desc_for_second(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Tag, + .second.id = EcsDesc + }}, + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q == NULL); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/ChangeDetection.c b/vendors/flecs/test/query/src/ChangeDetection.c new file mode 100644 index 000000000..e81798b66 --- /dev/null +++ b/vendors/flecs/test/query/src/ChangeDetection.c @@ -0,0 +1,1522 @@ +#include + +void ChangeDetection_query_changed_after_new(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_new_w(world, Position); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_after_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_delete(world, e1); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_after_add(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_add(world, e1, Position); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { } + + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_after_remove(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_remove(world, e1, Position); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_after_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, e1, Position, {10, 20}); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_after_modified(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_modified(world, e1, Position); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +static int sys_invoked = 0; +void Sys(ecs_iter_t *it) { + sys_invoked ++; +} + +void ChangeDetection_query_change_after_out_system(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ECS_SYSTEM(world, Sys, EcsOnUpdate, [out] Position); + + ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_progress(world, 0); + test_int(sys_invoked, 1); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); + + test_int(sys_invoked, 1); +} + +void ChangeDetection_query_change_after_out_query_no_data_flag(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_new_w(world, Position); + + ecs_query_t *q_write = ecs_query(world, { + .expr = "[inout] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q_write != NULL); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q_write); + it.flags |= EcsIterNoData; + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_changed(q)); + while (ecs_query_next(&it)) { + test_bool(false, ecs_iter_changed(&it)); + } + test_bool(false, ecs_query_changed(q)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_after_in_system(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ECS_SYSTEM(world, Sys, EcsOnUpdate, [in] Position); + + ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_progress(world, 0); + test_bool(false, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(false, ecs_query_changed(q)); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_after_modified_out_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position, [out] Velocity", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, e, Position, {10, 20}); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, e, Velocity, {1, 2}); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_check_iter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { + test_bool(ecs_iter_changed(&it), true); + } + test_bool(false, ecs_query_changed(q)); + + ecs_modified(world, e1, Position); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e1); + test_bool(ecs_iter_changed(&it), true); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e2); + test_bool(ecs_iter_changed(&it), false); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_bool(ecs_iter_changed(&it), false); + test_bool(ecs_query_changed(q), false); + test_bool(ecs_query_next(&it), false); + + it = ecs_query_iter(world, q); + test_bool(false, ecs_query_changed(q)); + while (ecs_query_next(&it)) { + test_bool(ecs_iter_changed(&it), false); + } + test_bool(false, ecs_query_changed(q)); + + ecs_modified(world, e2, Position); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e1); + test_bool(ecs_iter_changed(&it), false); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e2); + test_bool(ecs_iter_changed(&it), true); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_bool(ecs_iter_changed(&it), false); + test_bool(ecs_query_changed(q), false); + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_check_iter_after_skip_read(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, e, Position, {10, 20}); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_assert(ecs_iter_changed(&it) == true); + ecs_iter_skip(&it); + test_bool(ecs_query_next(&it), false); + + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_assert(ecs_iter_changed(&it) == true); + test_bool(ecs_query_next(&it), false); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_check_iter_after_skip_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_new_w(world, Position); + + ecs_query_t *qw = ecs_query(world, { + .expr = "[out] Position", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + it = ecs_query_iter(world, qw); + test_bool(false, ecs_query_changed(q)); + test_bool(ecs_query_next(&it), true); + test_bool(false, ecs_query_changed(q)); + ecs_iter_skip(&it); + test_bool(ecs_query_next(&it), false); + test_bool(false, ecs_query_changed(q)); + + test_bool(false, ecs_query_changed(q)); + + it = ecs_query_iter(world, qw); + test_bool(false, ecs_query_changed(q)); + test_bool(ecs_query_next(&it), true); + test_bool(false, ecs_query_changed(q)); + test_bool(ecs_query_next(&it), false); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_assert(ecs_iter_changed(&it) == true); + test_bool(ecs_query_next(&it), false); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_parent_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t parent = ecs_new_w(world, Position); + ecs_new_w_pair(world, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, parent, Position, {10, 20}); + + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_assert(ecs_iter_changed(&it) == true); + test_bool(ecs_query_next(&it), false); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_prefab_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Position); + ecs_new_w_pair(world, EcsIsA, base); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, base, Position, {10, 20}); + + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_assert(ecs_iter_changed(&it) == true); + test_bool(ecs_query_next(&it), false); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_parent_term_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t parent = ecs_new_w(world, Position); + ecs_add_id(world, parent, EcsPrefab); + ecs_new_w_pair(world, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position(up), ?Prefab", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, parent, Position, {10, 20}); + + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_assert(ecs_iter_changed(&it) == true); + test_bool(ecs_query_next(&it), false); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_change_prefab_term_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Position); + ecs_add_id(world, base, EcsPrefab); + ecs_new_w_pair(world, EcsIsA, base); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_changed(q)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, base, Position, {10, 20}); + + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_assert(ecs_iter_changed(&it) == true); + test_bool(ecs_query_next(&it), false); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Position); + ecs_add(world, e, Velocity); + ecs_add(world, e, TagA); + ecs_add(world, e, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position, TagA || TagB, [in] Velocity", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, e, Position, {10, 20}); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, e, Velocity, {10, 20}); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_add(world, e1, TagA); + ecs_add(world, e1, TagB); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Velocity); + ecs_add(world, e2, TagA); + ecs_add(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, [in] Position || [in] Velocity, TagB", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, e1, Position, {10, 20}); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_set(world, e2, Velocity, {10, 20}); + test_bool(true, ecs_query_changed(q)); + + it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_no_source(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, [out] TagB()", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_no_source_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position, [out] Velocity()", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_not_out(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position, [out] !Velocity", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_bool(true, ecs_query_changed(q)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_singleton_set(world, Velocity, {1, 2}); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position, Velocity($)", + .cache_kind = EcsQueryCacheAuto + }); + + test_bool(true, ecs_query_changed(q)); + + { + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q)); + } + + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {10, 20})); + + test_bool(true, ecs_query_changed(q)); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_uint(e, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_int(p->x, 10); + test_int(p->y, 20); + test_int(v->x, 1); + test_int(v->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + test_bool(false, ecs_query_changed(q)); + + ecs_singleton_set(world, Velocity, {2, 3}); + + test_bool(true, ecs_query_changed(q)); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_query_changed(q)); + test_bool(true, ecs_iter_changed(&it)); + test_uint(e, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + test_int(p->x, 10); + test_int(p->y, 20); + test_int(v->x, 2); + test_int(v->y, 3); + test_bool(false, ecs_query_next(&it)); + } + + test_bool(false, ecs_query_changed(q)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_only_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_singleton_set(world, Position, {1, 2}); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($)", + .cache_kind = EcsQueryCacheAuto + }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_only_singleton_after_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_singleton_set(world, Position, {1, 2}); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($)", + .cache_kind = EcsQueryCacheAuto + }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + ecs_singleton_set(world, Position, {3, 4}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 3); + test_int(p->y, 4); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_only_singleton_after_out_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_singleton_set(world, Position, {1, 2}); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($)", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_query_t *q_write = ecs_query(world, { + .expr = "[out] Position" + }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q_write); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + Position *p = ecs_field(&it, Position, 0); + p->x = 3; + p->y = 4; + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 3); + test_int(p->y, 4); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + ecs_query_fini(q_write); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_only_singleton_after_singleton_out_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_singleton_set(world, Position, {1, 2}); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($)", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_query_t *q_write = ecs_query(world, { + .expr = "[out] Position($)" + }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q_write); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + p->x = 3; + p->y = 4; + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 3); + test_int(p->y, 4); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + ecs_query_fini(q_write); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_only_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_only_parent_after_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, parent, Position, {3, 4}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 3); + test_int(p->y, 4); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_only_parent_after_out_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_query_t *q_write = ecs_query(world, { + .expr = "[out] Position" + }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q_write); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + p->x = 3; + p->y = 4; + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 3); + test_int(p->y, 4); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + ecs_query_fini(q_write); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_w_only_parent_after_parent_out_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = EcsQueryCacheAuto + }); + + ecs_query_t *q_write = ecs_query(world, { + .expr = "[out] Position(up)" + }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q_write); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + p->x = 3; + p->y = 4; + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_iter_changed(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 3); + test_int(p->y, 4); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + ecs_query_fini(q_write); + + ecs_fini(world); +} + +void ChangeDetection_query_change_w_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + + ecs_query_t *q_read = ecs_query(world, { + .expr = "[in] Position, [in] Velocity", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q_read != NULL); + + ecs_query_t *q_write = ecs_query(world, { + .expr = "[in] Position, [in] Velocity, [out] ?Mass", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q_write != NULL); + + ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + + test_bool(true, ecs_query_changed(q_read)); + test_bool(true, ecs_query_changed(q_read)); + + { + ecs_iter_t it = ecs_query_iter(world, q_read); + test_bool(true, ecs_query_changed(q_read)); + while (ecs_query_next(&it)) { } + test_bool(false, ecs_query_changed(q_read)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q_write); + while (ecs_query_next(&it)) { } + } + + test_bool(false, ecs_query_changed(q_read)); + + ecs_query_fini(q_read); + ecs_query_fini(q_write); + + ecs_fini(world); +} + +void ChangeDetection_query_changed_after_count(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t foo = ecs_new(world); + + // This makes the query not entirely cacheable, which prevents query_count + // from just getting the numbers from the cache. + ecs_add_id(world, foo, EcsCanToggle); + + ecs_entity_t bar = ecs_new(world); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_id(world, e1, foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = foo, .inout = EcsIn }}, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_add_id(world, e1, bar); + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_bool(true, ecs_iter_changed(&it)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_id(world, e1, bar); + ecs_add_id(world, e1, bar); + + ecs_query_count(q); // Make sure this doesn't affect change detection + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_bool(true, ecs_iter_changed(&it)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Combinations.c b/vendors/flecs/test/query/src/Combinations.c new file mode 100644 index 000000000..629e64951 --- /dev/null +++ b/vendors/flecs/test/query/src/Combinations.c @@ -0,0 +1,531 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; +static ecs_entity_t on_instantiate = 0; + +void Combinations_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } + + const char *on_instantiate_param = test_param("on_instantiate"); + if (on_instantiate_param) { + if (!strcmp(on_instantiate_param, "override")) { + on_instantiate = EcsOverride; + } else if (!strcmp(on_instantiate_param, "inherit")) { + on_instantiate = EcsInherit; + } else if (!strcmp(on_instantiate_param, "dont_inherit")) { + on_instantiate = EcsDontInherit; + } else { + printf("unexpected value for on_instantiate '%s'\n", cache_param); + } + } +} + +void Combinations_trav_and_singleton_and_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, on_instantiate); + + ecs_singleton_set(world, Velocity, {1, 2}); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t child = ecs_insert(world, + ecs_value(Mass, {100})); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up), Velocity($), Mass", + .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(child, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(parent, ecs_field_src(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + { + Position *ptr = ecs_field(&it, Position, 0); + test_assert(ptr != NULL); + test_int(ptr->x, 10); test_int(ptr->y, 20); + } + { + Velocity *ptr = ecs_field(&it, Velocity, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 1); test_int(ptr->y, 2); + } + { + Mass *ptr = ecs_field(&it, Mass, 2); + test_assert(ptr != NULL); + test_int(ptr[0], 100); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Combinations_self_and_singleton_and_trav(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, on_instantiate); + + ecs_singleton_set(world, Velocity, {1, 2}); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Mass, {100})); + ecs_entity_t child = ecs_insert(world, + ecs_value(Position, {10, 20})); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity($), Mass(up)", + .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(child, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + { + Position *ptr = ecs_field(&it, Position, 0); + test_assert(ptr != NULL); + test_int(ptr->x, 10); test_int(ptr->y, 20); + } + { + Velocity *ptr = ecs_field(&it, Velocity, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 1); test_int(ptr->y, 2); + } + { + Mass *ptr = ecs_field(&it, Mass, 2); + test_assert(ptr != NULL); + test_int(ptr[0], 100); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Combinations_self_and_trav_and_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, on_instantiate); + + ecs_singleton_set(world, Mass, {100}); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Velocity, {1, 2})); + ecs_entity_t child = ecs_insert(world, + ecs_value(Position, {10, 20})); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity(up), Mass($)", + .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(child, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent, ecs_field_src(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_src(&it, 2)); + { + Position *ptr = ecs_field(&it, Position, 0); + test_assert(ptr != NULL); + test_int(ptr->x, 10); test_int(ptr->y, 20); + } + { + Velocity *ptr = ecs_field(&it, Velocity, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 1); test_int(ptr->y, 2); + } + { + Mass *ptr = ecs_field(&it, Mass, 2); + test_assert(ptr != NULL); + test_int(ptr[0], 100); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Combinations_singleton_and_self_and_trav(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, on_instantiate); + + ecs_singleton_set(world, Position, {10, 20}); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Mass, {100})); + ecs_entity_t child = ecs_insert(world, + ecs_value(Velocity, {1, 2})); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($), Velocity, Mass(up)", + .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(child, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(ecs_id(Position), ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + { + Position *ptr = ecs_field(&it, Position, 0); + test_assert(ptr != NULL); + test_int(ptr->x, 10); test_int(ptr->y, 20); + } + { + Velocity *ptr = ecs_field(&it, Velocity, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 1); test_int(ptr->y, 2); + } + { + Mass *ptr = ecs_field(&it, Mass, 2); + test_assert(ptr != NULL); + test_int(ptr[0], 100); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Combinations_trav_and_self_and_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, on_instantiate); + + ecs_singleton_set(world, Mass, {100}); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t child = ecs_insert(world, + ecs_value(Velocity, {1, 2})); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up), Velocity, Mass($)", + .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(child, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(parent, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_src(&it, 2)); + { + Position *ptr = ecs_field(&it, Position, 0); + test_assert(ptr != NULL); + test_int(ptr->x, 10); test_int(ptr->y, 20); + } + { + Velocity *ptr = ecs_field(&it, Velocity, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 1); test_int(ptr->y, 2); + } + { + Mass *ptr = ecs_field(&it, Mass, 2); + test_assert(ptr != NULL); + test_int(ptr[0], 100); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Combinations_singleton_and_trav_and_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, on_instantiate); + + ecs_singleton_set(world, Position, {10, 20}); + + ecs_entity_t parent = ecs_insert(world, ecs_value(Velocity, {1, 2})); + ecs_entity_t child = ecs_insert(world, + ecs_value(Mass, {100})); + ecs_add_pair(world, child, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($), Velocity(up), Mass", + .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(child, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(ecs_id(Position), ecs_field_src(&it, 0)); + test_uint(parent, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + { + Position *ptr = ecs_field(&it, Position, 0); + test_assert(ptr != NULL); + test_int(ptr->x, 10); test_int(ptr->y, 20); + } + { + Velocity *ptr = ecs_field(&it, Velocity, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 1); test_int(ptr->y, 2); + } + { + Mass *ptr = ecs_field(&it, Mass, 2); + test_assert(ptr != NULL); + test_int(ptr[0], 100); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Combinations_singleton_and_self_and_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, on_instantiate); + + ecs_singleton_set(world, Position, {10, 20}); + + ecs_entity_t child = ecs_insert(world, + ecs_value(Velocity, {1, 2}), + ecs_value(Mass, {100})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($), Velocity, Mass", + .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(child, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(ecs_id(Position), ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + { + Position *ptr = ecs_field(&it, Position, 0); + test_assert(ptr != NULL); + test_int(ptr->x, 10); test_int(ptr->y, 20); + } + { + Velocity *ptr = ecs_field(&it, Velocity, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 1); test_int(ptr->y, 2); + } + { + Mass *ptr = ecs_field(&it, Mass, 2); + test_assert(ptr != NULL); + test_int(ptr[0], 100); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Combinations_self_and_singleton_and_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, on_instantiate); + + ecs_singleton_set(world, Velocity, {1, 2}); + + ecs_entity_t child = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Mass, {100})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity($), Mass", + .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(child, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + { + Position *ptr = ecs_field(&it, Position, 0); + test_assert(ptr != NULL); + test_int(ptr->x, 10); test_int(ptr->y, 20); + } + { + Velocity *ptr = ecs_field(&it, Velocity, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 1); test_int(ptr->y, 2); + } + { + Mass *ptr = ecs_field(&it, Mass, 2); + test_assert(ptr != NULL); + test_int(ptr[0], 100); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Combinations_self_and_self_and_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, on_instantiate); + + ecs_singleton_set(world, Mass, {100}); + + ecs_entity_t child = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity, Mass($)", + .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(child, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_src(&it, 2)); + { + Position *ptr = ecs_field(&it, Position, 0); + test_assert(ptr != NULL); + test_int(ptr->x, 10); test_int(ptr->y, 20); + } + { + Velocity *ptr = ecs_field(&it, Velocity, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 1); test_int(ptr->y, 2); + } + { + Mass *ptr = ecs_field(&it, Mass, 2); + test_assert(ptr != NULL); + test_int(ptr[0], 100); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/ComponentInheritance.c b/vendors/flecs/test/query/src/ComponentInheritance.c new file mode 100644 index 000000000..e56d7f138 --- /dev/null +++ b/vendors/flecs/test/query/src/ComponentInheritance.c @@ -0,0 +1,3350 @@ +#include + +ECS_TAG_DECLARE(Unit); +ECS_TAG_DECLARE(MeleeUnit); +ECS_TAG_DECLARE(RangedUnit); +ECS_TAG_DECLARE(Warrior); +ECS_TAG_DECLARE(Archer); +ECS_TAG_DECLARE(Wizard); +ECS_TAG_DECLARE(Warlock); + +static +void populate_facts(ecs_world_t *world) { + ECS_TAG_DEFINE(world, Unit); + ECS_ENTITY_DEFINE(world, MeleeUnit, (IsA, Unit)); + ECS_ENTITY_DEFINE(world, RangedUnit, (IsA, Unit)); + ECS_ENTITY_DEFINE(world, Warrior, (IsA, MeleeUnit)); + ECS_ENTITY_DEFINE(world, Archer, (IsA, RangedUnit)); + ECS_ENTITY_DEFINE(world, Wizard, (IsA, RangedUnit)); + ECS_ENTITY_DEFINE(world, Warlock, (IsA, Wizard), (IsA, Warrior)); +} + +void ComponentInheritance_1_ent_0_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Warlock(e1)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_ent_1_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Warrior(e1)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_ent_2_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "MeleeUnit(e1)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_ent_3_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Unit(e1)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); /* not ideal, diamond problem */ + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + + +void ComponentInheritance_1_this_0_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + ecs_entity_t e2 = ecs_new_w(world, Wizard); + ecs_entity_t e3 = ecs_new_w(world, Warrior); + ecs_entity_t e4 = ecs_new_w(world, Archer); + ecs_entity_t e5 = ecs_new_w(world, MeleeUnit); + ecs_entity_t e6 = ecs_new_w(world, RangedUnit); + ecs_entity_t e7 = ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "Unit($this)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Unit, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e7, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RangedUnit, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e6, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_this_1_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + /* ecs_entity_t e2 = */ ecs_new_w(world, Wizard); + ecs_entity_t e3 = ecs_new_w(world, Warrior); + /* ecs_entity_t e4 = */ ecs_new_w(world, Archer); + ecs_entity_t e5 = ecs_new_w(world, MeleeUnit); + /* ecs_entity_t e6 = */ ecs_new_w(world, RangedUnit); + /* ecs_entity_t e7 = */ ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "MeleeUnit($this)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_this_2_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + /* ecs_entity_t e2 = */ ecs_new_w(world, Wizard); + ecs_entity_t e3 = ecs_new_w(world, Warrior); + /* ecs_entity_t e4 = */ ecs_new_w(world, Archer); + /* ecs_entity_t e5 = */ ecs_new_w(world, MeleeUnit); + /* ecs_entity_t e6 = */ ecs_new_w(world, RangedUnit); + /* ecs_entity_t e7 = */ ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "Warrior($this)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_this_3_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + /* ecs_entity_t e2 = */ ecs_new_w(world, Wizard); + /* ecs_entity_t e3 = */ ecs_new_w(world, Warrior); + /* ecs_entity_t e4 = */ ecs_new_w(world, Archer); + /* ecs_entity_t e5 = */ ecs_new_w(world, MeleeUnit); + /* ecs_entity_t e6 = */ ecs_new_w(world, RangedUnit); + /* ecs_entity_t e7 = */ ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "Warlock($this)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_this_0_lvl_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($this), Unit($this)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RangedUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Unit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e7, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_this_1_lvl_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($this), MeleeUnit($this)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_this_2_lvl_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($this), Warrior($this)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_this_3_lvl_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($this), Warlock($this)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_0_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + ecs_entity_t e2 = ecs_new_w(world, Wizard); + ecs_entity_t e3 = ecs_new_w(world, Warrior); + ecs_entity_t e4 = ecs_new_w(world, Archer); + ecs_entity_t e5 = ecs_new_w(world, MeleeUnit); + ecs_entity_t e6 = ecs_new_w(world, RangedUnit); + ecs_entity_t e7 = ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "Unit($x)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Unit, ecs_field_id(&it, 0)); + test_uint(e7, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(e5, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RangedUnit, ecs_field_id(&it, 0)); + test_uint(e6, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(e4, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_1_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + /* ecs_entity_t e2 = */ ecs_new_w(world, Wizard); + ecs_entity_t e3 = ecs_new_w(world, Warrior); + /* ecs_entity_t e4 = */ ecs_new_w(world, Archer); + ecs_entity_t e5 = ecs_new_w(world, MeleeUnit); + /* ecs_entity_t e6 = */ ecs_new_w(world, RangedUnit); + /* ecs_entity_t e7 = */ ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "MeleeUnit($x)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(e5, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_2_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + /* ecs_entity_t e2 = */ ecs_new_w(world, Wizard); + ecs_entity_t e3 = ecs_new_w(world, Warrior); + /* ecs_entity_t e4 = */ ecs_new_w(world, Archer); + /* ecs_entity_t e5 = */ ecs_new_w(world, MeleeUnit); + /* ecs_entity_t e6 = */ ecs_new_w(world, RangedUnit); + /* ecs_entity_t e7 = */ ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "Warrior($x)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_3_lvl(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + /* ecs_entity_t e2 = */ ecs_new_w(world, Wizard); + /* ecs_entity_t e3 = */ ecs_new_w(world, Warrior); + /* ecs_entity_t e4 = */ ecs_new_w(world, Archer); + /* ecs_entity_t e5 = */ ecs_new_w(world, MeleeUnit); + /* ecs_entity_t e6 = */ ecs_new_w(world, RangedUnit); + /* ecs_entity_t e7 = */ ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "Warlock($x)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_0_lvl_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x), Unit($x)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RangedUnit, ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Unit, ecs_field_id(&it, 1)); + test_uint(e7, ecs_field_src(&it, 0)); + test_uint(e7, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_1_lvl_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x), MeleeUnit($x)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_2_lvl_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x), Warrior($x)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_3_lvl_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x), Warlock($x)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_ent_1_lvl_self(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Warrior(e1|self)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_this_1_lvl_self(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + /* ecs_entity_t e2 = */ ecs_new_w(world, Wizard); + ecs_entity_t e3 = ecs_new_w(world, Warrior); + /* ecs_entity_t e4 = */ ecs_new_w(world, Archer); + ecs_entity_t e5 = ecs_new_w(world, MeleeUnit); + /* ecs_entity_t e6 = */ ecs_new_w(world, RangedUnit); + /* ecs_entity_t e7 = */ ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "MeleeUnit(self)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_this_1_lvl_written_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), MeleeUnit(self)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_1_lvl_self(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Warlock); + /* ecs_entity_t e2 = */ ecs_new_w(world, Wizard); + ecs_entity_t e3 = ecs_new_w(world, Warrior); + /* ecs_entity_t e4 = */ ecs_new_w(world, Archer); + ecs_entity_t e5 = ecs_new_w(world, MeleeUnit); + /* ecs_entity_t e6 = */ ecs_new_w(world, RangedUnit); + /* ecs_entity_t e7 = */ ecs_new_w(world, Unit); + + ecs_query_t *r = ecs_query(world, { + .expr = "MeleeUnit($x|self)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(e5, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_var_1_lvl_written_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Warlock); + ecs_add(world, e2, Wizard); + ecs_add(world, e3, Warrior); + ecs_add(world, e4, Archer); + ecs_add(world, e5, MeleeUnit); + ecs_add(world, e6, RangedUnit); + ecs_add(world, e7, Unit); + + /* entities that don't match query */ + ecs_new_w(world, Unit); + ecs_new_w(world, MeleeUnit); + ecs_new_w(world, Warrior); + ecs_new_w(world, Warlock); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x), MeleeUnit($x)" + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void ComponentInheritance_1_ent_src_not(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, Warrior); + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Unit(e1)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!MeleeUnit(e1)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Warrior(e1)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Warlock(e1)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!RangedUnit(e1)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RangedUnit, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Archer(e1)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Wizard(e1)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + ecs_fini(world); +} + +void ComponentInheritance_1_this_src_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Unit); + ecs_add(world, e2, MeleeUnit); + ecs_add(world, e3, RangedUnit); + ecs_add(world, e4, Warrior); + ecs_add(world, e5, Archer); + ecs_add(world, e6, Wizard); + ecs_add(world, e7, Warlock); + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Unit($this), Tag($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!MeleeUnit($this), Tag($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Warrior($this), Tag($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Warlock($this), Tag($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!RangedUnit($this), Tag($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RangedUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RangedUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RangedUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Archer($this), Tag($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e7, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Wizard($this), Tag($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + ecs_fini(world); +} + +void ComponentInheritance_1_var_src_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Unit); + ecs_add(world, e2, MeleeUnit); + ecs_add(world, e3, RangedUnit); + ecs_add(world, e4, Warrior); + ecs_add(world, e5, Archer); + ecs_add(world, e6, Wizard); + ecs_add(world, e7, Warlock); + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Unit($x), Tag($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!MeleeUnit($x), Tag($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(MeleeUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Warrior($x), Tag($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Warlock($x), Tag($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!RangedUnit($x), Tag($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RangedUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RangedUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RangedUnit, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Archer($x), Tag($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Archer, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e7, ecs_field_src(&it, 0)); + test_uint(e7, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "!Wizard($x), Tag($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Wizard, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + ecs_fini(world); +} + +void ComponentInheritance_1_this_src_not_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Unit); + ecs_add(world, e2, MeleeUnit); + ecs_add(world, e3, RangedUnit); + ecs_add(world, e4, Warrior); + ecs_add(world, e5, Archer); + ecs_add(world, e6, Wizard); + ecs_add(world, e7, Warlock); + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), !Unit($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), !MeleeUnit($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), !Warrior($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), !Warlock($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), !RangedUnit($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RangedUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RangedUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RangedUnit, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), !Archer($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e6, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e7, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), !Wizard($this)" }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e5, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + ecs_fini(world); +} + +void ComponentInheritance_1_var_src_not_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + populate_facts(world); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + + ecs_add(world, e1, Unit); + ecs_add(world, e2, MeleeUnit); + ecs_add(world, e3, RangedUnit); + ecs_add(world, e4, Warrior); + ecs_add(world, e5, Archer); + ecs_add(world, e6, Wizard); + ecs_add(world, e7, Warlock); + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($x), !Unit($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($x), !MeleeUnit($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(MeleeUnit, ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($x), !Warrior($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warrior, ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($x), !Warlock($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Warlock, ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($x), !RangedUnit($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RangedUnit, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RangedUnit, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RangedUnit, ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($x), !Archer($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Archer, ecs_field_id(&it, 1)); + test_uint(e7, ecs_field_src(&it, 0)); + test_uint(e7, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($x), !Wizard($x)" }); + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Wizard, ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + ecs_fini(world); +} + +void ComponentInheritance_first_self(void) { + ecs_world_t *world = ecs_mini(); + + populate_facts(world); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, Warlock); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_add(world, e2, Warrior); + + { + ecs_query_t *r = ecs_query(world, { + .expr = "Warrior|self(e1)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { + .expr = "Warlock|self(e1)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warlock, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + { + ecs_query_t *r = ecs_query(world, { + .expr = "Warrior|self(e2)" + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Warrior, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + } + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/GroupBy.c b/vendors/flecs/test/query/src/GroupBy.c new file mode 100644 index 000000000..cdf34a776 --- /dev/null +++ b/vendors/flecs/test/query/src/GroupBy.c @@ -0,0 +1,671 @@ +#include + +static +int order_by_entity( + ecs_entity_t e1, + const void *ptr1, + ecs_entity_t e2, + const void *ptr2) +{ + return (e1 > e2) - (e1 < e2); +} + +static +uint64_t group_by_first_id( + ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + void *ctx) +{ + const ecs_type_t *type = ecs_table_get_type(table); + ecs_id_t *first = type->array; + if (!first) { + return 0; + } + + return first[0]; +} + +static +uint64_t group_by_rel(ecs_world_t *world, ecs_table_t *table, ecs_id_t id, void *ctx) { + ecs_id_t match; + if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &match) != -1) { + return ecs_pair_second(world, match); + } + return 0; +} + +void GroupBy_group_by(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagX); + + ecs_query_t *q = ecs_query(world, { + .terms = {{TagX}}, + .group_by_callback = group_by_first_id + }); + + ecs_entity_t e1 = ecs_new_w(world, TagX); + ecs_entity_t e2 = ecs_new_w(world, TagX); + ecs_entity_t e3 = ecs_new_w(world, TagX); + + ecs_add_id(world, e1, TagC); + ecs_add_id(world, e2, TagB); + ecs_add_id(world, e3, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e3); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e2); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e1); + + test_bool(ecs_query_next(&it), false); + + ecs_fini(world); +} + +static int ctx_value; +static int ctx_value_free_invoked = 0; +static +void ctx_value_free(void *ctx) { + test_assert(ctx == &ctx_value); + ctx_value_free_invoked ++; +} + +void GroupBy_group_by_w_ctx(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagX); + + ecs_query_t *q = ecs_query(world, { + .terms = {{TagX}}, + .group_by_callback = group_by_first_id, + .group_by_ctx = &ctx_value, + .group_by_ctx_free = ctx_value_free + }); + + ecs_entity_t e1 = ecs_new_w(world, TagX); + ecs_entity_t e2 = ecs_new_w(world, TagX); + ecs_entity_t e3 = ecs_new_w(world, TagX); + + ecs_add_id(world, e1, TagC); + ecs_add_id(world, e2, TagB); + ecs_add_id(world, e3, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e3); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e2); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e1); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + test_int(ctx_value_free_invoked, 1); + + ecs_fini(world); +} + +void GroupBy_group_by_w_sort_reverse_group_creation(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t TagA = ecs_new(world); + ecs_entity_t TagB = ecs_new(world); + ecs_entity_t TagC = ecs_new(world); + + ecs_entity_t TagX = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .terms = {{TagX}}, + .order_by_callback = order_by_entity, + .group_by_callback = group_by_first_id + }); + + ecs_entity_t e1 = ecs_new_w_id(world, TagX); + ecs_entity_t e2 = ecs_new_w_id(world, TagX); + ecs_entity_t e3 = ecs_new_w_id(world, TagX); + + ecs_add_id(world, e1, TagC); + ecs_add_id(world, e2, TagB); + ecs_add_id(world, e3, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e3); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e2); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e1); + + test_bool(ecs_query_next(&it), false); + + ecs_fini(world); +} + +void GroupBy_group_by_iter_one(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, Tag); + + ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + ecs_new_w_pair(world, Rel, TgtC); + + ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e5 = ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e6 = ecs_new_w_pair(world, Rel, TgtC); + ecs_add(world, e4, Tag); + ecs_add(world, e5, Tag); + ecs_add(world, e6, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, EcsWildcard) } + }, + .group_by_callback = group_by_rel, + .group_by = Rel + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_group(&it, TgtB); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, it.group_id); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, it.group_id); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void GroupBy_group_by_iter_one_all_groups(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtC); + + ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e5 = ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e6 = ecs_new_w_pair(world, Rel, TgtC); + ecs_add(world, e4, Tag); + ecs_add(world, e5, Tag); + ecs_add(world, e6, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, EcsWildcard) } + }, + .group_by_callback = group_by_rel, + .group_by = Rel + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_group(&it, TgtB); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, it.group_id); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, it.group_id); + test_bool(false, ecs_query_next(&it)); + + it = ecs_query_iter(world, q); + ecs_iter_set_group(&it, TgtA); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, it.group_id); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, it.group_id); + test_bool(false, ecs_query_next(&it)); + + it = ecs_query_iter(world, q); + ecs_iter_set_group(&it, TgtC); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + test_uint(TgtC, it.group_id); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + test_uint(TgtC, it.group_id); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void GroupBy_group_by_iter_one_empty(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, TgtD); + ECS_TAG(world, Tag); + + ecs_new_w_pair(world, Rel, TgtA); + ecs_new_w_pair(world, Rel, TgtB); + ecs_new_w_pair(world, Rel, TgtC); + + ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e5 = ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e6 = ecs_new_w_pair(world, Rel, TgtC); + ecs_add(world, e4, Tag); + ecs_add(world, e5, Tag); + ecs_add(world, e6, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, EcsWildcard) } + }, + .group_by_callback = group_by_rel, + .group_by = Rel + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_group(&it, TgtD); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void GroupBy_group_by_iter_one_empty_query(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, EcsWildcard) } + }, + .group_by_callback = group_by_rel, + .group_by = Rel + }); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_group(&it, TgtA); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void GroupBy_group_by_iter_one_empty_table(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, Tag); + + ecs_new_w_pair(world, Rel, TgtA); + ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtC); + + ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e5 = ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e6 = ecs_new_w_pair(world, Rel, TgtC); + ecs_add(world, e4, Tag); + ecs_add(world, e5, Tag); + ecs_add(world, e6, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, EcsWildcard) } + }, + .group_by_callback = group_by_rel, + .group_by = Rel + }); + + ecs_delete(world, e3); + ecs_delete(world, e6); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_group(&it, TgtC); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void GroupBy_group_by_w_deleted_group_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, EcsWildcard) } + }, + .group_by_callback = group_by_rel, + .group_by = Rel + }); + + ecs_entity_t tgt = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, Rel, tgt); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_group(&it, tgt); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + + ecs_delete(world, tgt); + ecs_delete(world, e); + + it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + tgt = ecs_new(world); + e = ecs_new_w_pair(world, Rel, tgt); + + it = ecs_query_iter(world, q); + ecs_iter_set_group(&it, tgt); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +static int group_by_ctx; +static int on_group_create_invoked; +static int on_group_delete_invoked; + +static +void* on_group_create(ecs_world_t *world, uint64_t group_id, void *group_by_arg) { + test_assert(world != NULL); + test_assert(group_id != 0); + test_assert(group_by_arg != NULL); + test_assert(group_by_arg == &group_by_ctx); + uint64_t *group_ctx = ecs_os_malloc_t(uint64_t); + *group_ctx = group_id; + on_group_create_invoked ++; + return group_ctx; +} + +static +void on_group_delete(ecs_world_t *world, uint64_t group_id, void *group_ctx, void *group_by_arg) { + test_assert(world != NULL); + test_assert(group_id != 0); + test_assert(group_ctx != NULL); + test_assert(group_by_arg != NULL); + test_assert(group_by_arg == &group_by_ctx); + test_assert(*(uint64_t*)group_ctx == group_id); + ecs_os_free(group_ctx); + on_group_delete_invoked ++; +} + +void GroupBy_group_by_callbacks(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, EcsWildcard) } + }, + .group_by_callback = group_by_rel, + .group_by = Rel, + .on_group_create = on_group_create, + .on_group_delete = on_group_delete, + .group_by_ctx = &group_by_ctx + }); + + ecs_entity_t tgt_a = ecs_new(world); + ecs_entity_t tgt_b = ecs_new(world); + + test_int(on_group_create_invoked, 0); + test_int(on_group_delete_invoked, 0); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, tgt_a); + ecs_run_aperiodic(world, 0); + test_int(on_group_create_invoked, 1); + test_int(on_group_delete_invoked, 0); + + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, tgt_a); + ecs_run_aperiodic(world, 0); + test_int(on_group_create_invoked, 1); + test_int(on_group_delete_invoked, 0); + + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, tgt_b); + ecs_run_aperiodic(world, 0); + test_int(on_group_create_invoked, 2); + test_int(on_group_delete_invoked, 0); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(tgt_a, it.group_id); + const ecs_query_group_info_t *gi = ecs_query_get_group_info(q, it.group_id); + test_assert(gi != NULL); + test_assert(gi->ctx != NULL); + test_uint(tgt_a, *(uint64_t*)gi->ctx); + + test_assert(ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(tgt_b, it.group_id); + gi = ecs_query_get_group_info(q, it.group_id); + test_assert(gi != NULL); + test_assert(gi->ctx != NULL); + test_uint(tgt_b, *(uint64_t*)gi->ctx); + test_assert(!ecs_query_next(&it)); + + ecs_delete_with(world, ecs_pair(Rel, tgt_a)); + ecs_run_aperiodic(world, 0); + test_int(on_group_create_invoked, 2); + test_int(on_group_delete_invoked, 1); + + ecs_query_fini(q); + + test_int(on_group_create_invoked, 2); + test_int(on_group_delete_invoked, 2); + + ecs_fini(world); +} + +void GroupBy_group_by_default_action(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtC); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, EcsWildcard) } + }, + // .group_by_callback = group_by_rel, default action groups by relationship + .group_by = Rel + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, it.group_id); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, it.group_id); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + test_uint(TgtC, it.group_id); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(world); +} + +void GroupBy_group_table_count(void) { + ecs_world_t* world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Foo); + + ecs_new_w_pair(world, Rel, TgtA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, EcsWildcard) } + }, + .group_by = Rel + }); + + ecs_run_aperiodic(world, 0); + + const ecs_query_group_info_t *gi_a; + const ecs_query_group_info_t *gi_b; + gi_a = ecs_query_get_group_info(q, TgtA); + gi_b = ecs_query_get_group_info(q, TgtB); + test_assert(gi_a != NULL); + test_assert(gi_b == NULL); + test_int(gi_a->table_count, 1); + test_int(gi_a->match_count, 1); + + ecs_new_w_pair(world, Rel, TgtB); + ecs_run_aperiodic(world, 0); + + gi_a = ecs_query_get_group_info(q, TgtA); + gi_b = ecs_query_get_group_info(q, TgtB); + test_assert(gi_a != NULL); + test_assert(gi_b != NULL); + test_int(gi_a->table_count, 1); + test_int(gi_a->match_count, 1); + test_int(gi_b->table_count, 1); + test_int(gi_b->match_count, 1); + + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtA); + ecs_run_aperiodic(world, 0); + + gi_a = ecs_query_get_group_info(q, TgtA); + gi_b = ecs_query_get_group_info(q, TgtB); + test_assert(gi_a != NULL); + test_assert(gi_b != NULL); + test_int(gi_a->table_count, 1); + test_int(gi_a->match_count, 1); + test_int(gi_b->table_count, 1); + test_int(gi_b->match_count, 1); + + ecs_add(world, e3, Foo); + ecs_run_aperiodic(world, 0); + + gi_a = ecs_query_get_group_info(q, TgtA); + gi_b = ecs_query_get_group_info(q, TgtB); + test_assert(gi_a != NULL); + test_assert(gi_b != NULL); + test_int(gi_a->table_count, 2); + test_int(gi_a->match_count, 2); + test_int(gi_b->table_count, 1); + test_int(gi_b->match_count, 1); + + ecs_delete_with(world, Foo); + ecs_run_aperiodic(world, 0); + + gi_a = ecs_query_get_group_info(q, TgtA); + gi_b = ecs_query_get_group_info(q, TgtB); + test_assert(gi_a != NULL); + test_assert(gi_b != NULL); + test_int(gi_a->table_count, 1); + test_int(gi_a->match_count, 3); + test_int(gi_b->table_count, 1); + test_int(gi_b->match_count, 1); + + ecs_delete_with(world, ecs_pair(Rel, TgtA)); + ecs_run_aperiodic(world, 0); + + gi_a = ecs_query_get_group_info(q, TgtA); + gi_b = ecs_query_get_group_info(q, TgtB); + test_assert(gi_a == NULL); + test_assert(gi_b != NULL); + test_int(gi_b->table_count, 1); + test_int(gi_b->match_count, 1); + + ecs_delete_with(world, ecs_pair(Rel, TgtB)); + ecs_run_aperiodic(world, 0); + + gi_a = ecs_query_get_group_info(q, TgtA); + gi_b = ecs_query_get_group_info(q, TgtB); + test_assert(gi_a == NULL); + test_assert(gi_b == NULL); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/MemberTarget.c b/vendors/flecs/test/query/src/MemberTarget.c new file mode 100644 index 000000000..d09822cc6 --- /dev/null +++ b/vendors/flecs/test/query/src/MemberTarget.c @@ -0,0 +1,3952 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void MemberTarget_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +typedef struct { + ecs_entity_t value; +} Movement; + +typedef struct { + ecs_entity_t a; + ecs_entity_t b; +} TwoMembers; + +static ecs_entity_t ecs_id(Movement) = 0; +static ecs_entity_t ecs_id(TwoMembers) = 0; +static ecs_entity_t Running = 0; +static ecs_entity_t Walking = 0; +static ecs_entity_t Sitting = 0; + +static void register_types( + ecs_world_t *world) +{ + ecs_id(Movement) = ecs_struct(world, { + .entity = ecs_entity(world, { .name = "Movement" }), + .members = { + { "value", ecs_id(ecs_entity_t) } + } + }); + + ecs_id(TwoMembers) = ecs_struct(world, { + .entity = ecs_entity(world, { .name = "TwoMembers" }), + .members = { + { "a", ecs_id(ecs_entity_t) }, + { "b", ecs_id(ecs_entity_t) } + } + }); + + Running = ecs_entity(world, { .name = "Running" }); + Walking = ecs_entity(world, { .name = "Walking" }); + Sitting = ecs_entity(world, { .name = "Sitting" }); +} + +void MemberTarget_this_member_eq_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_eq_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + /* ecs_entity_t e1 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + /* ecs_entity_t e5 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_eq_no_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + /* ecs_entity_t e1 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + /* ecs_entity_t e2 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + /* ecs_entity_t e5 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_eq_all_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Sitting, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), (Movement.value, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_add(world, Running, Foo); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_var_read(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, $x), Foo($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_add(world, Running, Foo); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_eq_1_2nd_member(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(TwoMembers.b, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "TwoMembers.b"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(TwoMembers, { Walking, Walking })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(TwoMembers, { Walking, Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_eq_2_2nd_member(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(TwoMembers.b, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "TwoMembers.b"); + test_assert(member != 0); + + /* ecs_entity_t e1 = */ ecs_insert(world, ecs_value(TwoMembers, { Sitting, Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Running })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(TwoMembers, { Sitting, Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Running })); + /* ecs_entity_t e5 = */ ecs_insert(world, ecs_value(TwoMembers, { Sitting, Sitting })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_var_same_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(TwoMembers.a, $x), (TwoMembers.b, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member_a = ecs_lookup(world, "TwoMembers.a"); + test_assert(member_a != 0); + ecs_entity_t member_b = ecs_lookup(world, "TwoMembers.b"); + test_assert(member_b != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(TwoMembers, { Running, Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Walking })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(TwoMembers, { Walking, Sitting })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(TwoMembers, { Sitting, Walking })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Sitting })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_b, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member_a, Walking), ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_b, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Sitting, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member_a, Sitting), ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_b, Sitting), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_var_same_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(TwoMembers.a, $x), (TwoMembers.b, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member_a = ecs_lookup(world, "TwoMembers.a"); + test_assert(member_a != 0); + ecs_entity_t member_b = ecs_lookup(world, "TwoMembers.b"); + test_assert(member_b != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + /* ecs_entity_t e1 = */ ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(TwoMembers, { Running, Running })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(TwoMembers, { Walking, Sitting })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Walking })); + /* ecs_entity_t e5 = */ ecs_insert(world, ecs_value(TwoMembers, { Running, Sitting })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_b, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member_a, Walking), ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_b, Walking), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_eq_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_eq_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Sitting })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Sitting, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_neq_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "!(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + e0_found |= it.entities[i] == e0; + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + e3_found |= it.entities[i] == e3; + e4_found |= it.entities[i] == e4; + e5_found |= it.entities[i] == e5; + + if (it.entities[i] == e0) test_assert(it.table == ecs_get_table(world, e0)); + if (it.entities[i] == e1) test_assert(it.table == ecs_get_table(world, e1)); + if (it.entities[i] == e2) test_assert(it.table == ecs_get_table(world, e2)); + if (it.entities[i] == e3) test_assert(it.table == ecs_get_table(world, e3)); + if (it.entities[i] == e4) test_assert(it.table == ecs_get_table(world, e4)); + if (it.entities[i] == e5) test_assert(it.table == ecs_get_table(world, e5)); + } + } + + test_bool(true, e0_found); + test_bool(false, e1_found); + test_bool(false, e2_found); + test_bool(true, e3_found); + test_bool(true, e4_found); + test_bool(false, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_neq_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "!(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Sitting })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e0_found |= it.entities[i] == e0; + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + e3_found |= it.entities[i] == e3; + e4_found |= it.entities[i] == e4; + e5_found |= it.entities[i] == e5; + + if (it.entities[i] == e0) test_assert(it.table == ecs_get_table(world, e0)); + if (it.entities[i] == e1) test_assert(it.table == ecs_get_table(world, e1)); + if (it.entities[i] == e2) test_assert(it.table == ecs_get_table(world, e2)); + if (it.entities[i] == e3) test_assert(it.table == ecs_get_table(world, e3)); + if (it.entities[i] == e4) test_assert(it.table == ecs_get_table(world, e4)); + if (it.entities[i] == e5) test_assert(it.table == ecs_get_table(world, e5)); + } + } + + test_bool(true, e0_found); + test_bool(true, e1_found); + test_bool(false, e2_found); + test_bool(true, e3_found); + test_bool(false, e4_found); + test_bool(true, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_neq_no_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "!(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + e0_found |= it.entities[i] == e0; + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + e3_found |= it.entities[i] == e3; + e4_found |= it.entities[i] == e4; + e5_found |= it.entities[i] == e5; + + if (it.entities[i] == e0) test_assert(it.table == ecs_get_table(world, e0)); + } + } + + test_bool(true, e0_found); + test_bool(false, e1_found); + test_bool(false, e2_found); + test_bool(false, e3_found); + test_bool(false, e4_found); + test_bool(false, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_neq_all_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "!(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Walking })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + e0_found |= it.entities[i] == e0; + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + e3_found |= it.entities[i] == e3; + e4_found |= it.entities[i] == e4; + e5_found |= it.entities[i] == e5; + + if (it.entities[i] == e0) test_assert(it.table == ecs_get_table(world, e0)); + } + } + + test_bool(true, e0_found); + test_bool(true, e1_found); + test_bool(true, e2_found); + test_bool(true, e3_found); + test_bool(true, e4_found); + test_bool(true, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_neq_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "!(Movement.value, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + e0_found |= it.entities[i] == e0; + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + e3_found |= it.entities[i] == e3; + e4_found |= it.entities[i] == e4; + e5_found |= it.entities[i] == e5; + + if (it.entities[i] == e0) test_assert(it.table == ecs_get_table(world, e0)); + if (it.entities[i] == e1) test_assert(it.table == ecs_get_table(world, e1)); + if (it.entities[i] == e2) test_assert(it.table == ecs_get_table(world, e2)); + if (it.entities[i] == e3) test_assert(it.table == ecs_get_table(world, e3)); + if (it.entities[i] == e4) test_assert(it.table == ecs_get_table(world, e4)); + if (it.entities[i] == e5) test_assert(it.table == ecs_get_table(world, e5)); + } + } + + test_bool(true, e0_found); + test_bool(false, e1_found); + test_bool(false, e2_found); + test_bool(false, e3_found); + test_bool(false, e4_found); + test_bool(false, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_neq_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, !(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e0, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_neq_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, !(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Sitting })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e0, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_neq_no_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, !(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e0, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_neq_all_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, !(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Walking })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e0, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_neq_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, !(Movement.value, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Walking })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e0, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_eq_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "?(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + e0_found |= it.entities[i] == e0; + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + e3_found |= it.entities[i] == e3; + e4_found |= it.entities[i] == e4; + e5_found |= it.entities[i] == e5; + + if (it.entities[i] == e0) test_assert(it.table == ecs_get_table(world, e0)); + if (it.entities[i] == e1) test_assert(it.table == ecs_get_table(world, e1)); + if (it.entities[i] == e2) test_assert(it.table == ecs_get_table(world, e2)); + if (it.entities[i] == e3) test_assert(it.table == ecs_get_table(world, e3)); + if (it.entities[i] == e4) test_assert(it.table == ecs_get_table(world, e4)); + if (it.entities[i] == e5) test_assert(it.table == ecs_get_table(world, e5)); + } + } + + test_bool(true, e0_found); + test_bool(true, e1_found); + test_bool(true, e2_found); + test_bool(true, e3_found); + test_bool(true, e4_found); + test_bool(true, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_eq_optional_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "?(Movement.value, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + e0_found |= it.entities[i] == e0; + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + e3_found |= it.entities[i] == e3; + e4_found |= it.entities[i] == e4; + e5_found |= it.entities[i] == e5; + + if (it.entities[i] == e0) test_assert(it.table == ecs_get_table(world, e0)); + if (it.entities[i] == e1) test_assert(it.table == ecs_get_table(world, e1)); + if (it.entities[i] == e2) test_assert(it.table == ecs_get_table(world, e2)); + if (it.entities[i] == e3) test_assert(it.table == ecs_get_table(world, e3)); + if (it.entities[i] == e4) test_assert(it.table == ecs_get_table(world, e4)); + if (it.entities[i] == e5) test_assert(it.table == ecs_get_table(world, e5)); + } + } + + test_bool(true, e0_found); + test_bool(true, e1_found); + test_bool(true, e2_found); + test_bool(true, e3_found); + test_bool(true, e4_found); + test_bool(true, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_eq_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, ?(Movement.value, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e0, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_eq_optional_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, ?(Movement.value, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e0, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_eq_w_other_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Tag); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, Running), Tag", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + /* ecs_entity_t e5 = */ ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + ecs_add(world, e3, Tag); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_member_eq_w_other_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_COMPONENT(world, Position); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, Running), Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + /* ecs_entity_t e5 = */ ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {40, 50}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 40); + test_int(p[0].y, 50); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_eq_w_other_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + ECS_TAG(world, Tag); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, Running), Tag", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + ecs_add(world, e1, Tag); + ecs_add(world, e2, Tag); + ecs_add(world, e3, Tag); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_eq_w_other_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, Running), Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {40, 50}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 2); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 2); + test_assert(p != NULL); + test_int(p[0].x, 40); + test_int(p[0].y, 50); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_member_eq_w_other_inherit_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, Running), Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + ecs_set(world, e1, Position, {10, 20}); + ecs_set(world, e2, Position, {20, 30}); + ecs_set(world, e3, Position, {40, 50}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 2); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 2); + test_assert(p != NULL); + test_int(p[0].x, 40); + test_int(p[0].y, 50); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_2_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, Running) || (Movement.value, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + /* ecs_entity_t e0 = */ ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_3_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, Running) || (Movement.value, Walking) || (Movement.value, Sitting)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t Idle = ecs_new(world); + + /* ecs_entity_t e0 = */ ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Idle })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Idle })); + ecs_entity_t e6 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + ecs_add(world, e6, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_2_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, Running) || (Movement.value, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + /* ecs_entity_t e0 = */ ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_3_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, Running) || (Movement.value, Walking) || (Movement.value, Sitting)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + ecs_entity_t Idle = ecs_new(world); + + /* ecs_entity_t e0 = */ ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Idle })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Idle })); + ecs_entity_t e6 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + ecs_add(world, e6, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_2_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement.value($x, Running) || Movement.value($x, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + /* ecs_entity_t e0 = */ ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_3_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement.value($x, Running) || Movement.value($x, Walking) || Movement.value($x, Sitting)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t Idle = ecs_new(world); + + /* ecs_entity_t e0 = */ ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Idle })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Idle })); + ecs_entity_t e6 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + ecs_add(world, e6, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_written_2_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), Movement.value($x, Running) || Movement.value($x, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + /* ecs_entity_t e0 = */ ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_written_3_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), Movement.value($x, Running) || Movement.value($x, Walking) || Movement.value($x, Sitting)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t Idle = ecs_new(world); + + /* ecs_entity_t e0 = */ ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Idle })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Idle })); + ecs_entity_t e6 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + ecs_add(world, e6, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_2_or_w_2_members(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(TwoMembers.a, Running) || (TwoMembers.b, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member_a = ecs_lookup(world, "TwoMembers.a"); + test_assert(member_a != 0); + ecs_entity_t member_b = ecs_lookup(world, "TwoMembers.b"); + test_assert(member_b != 0); + + /* ecs_entity_t e1 = */ ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(TwoMembers, { Running, Sitting })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(TwoMembers, { Running, Walking })); + /* ecs_entity_t e5 = */ ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(member_b, Walking), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_2_or_w_2_types(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement.value, Running) || (TwoMembers.b, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member_a = ecs_lookup(world, "Movement.value"); + test_assert(member_a != 0); + ecs_entity_t member_b = ecs_lookup(world, "TwoMembers.b"); + test_assert(member_b != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Sitting })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Walking })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + + ecs_set(world, e1, Movement, { Walking }); + ecs_set(world, e2, Movement, { Running }); + ecs_set(world, e3, Movement, { Sitting }); + ecs_set(world, e4, Movement, { Running }); + ecs_set(world, e5, Movement, { Sitting }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(member_b, Walking), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_2_or_w_2_members(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (TwoMembers.a, Running) || (TwoMembers.b, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member_a = ecs_lookup(world, "TwoMembers.a"); + test_assert(member_a != 0); + ecs_entity_t member_b = ecs_lookup(world, "TwoMembers.b"); + test_assert(member_b != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(TwoMembers, { Running, Sitting })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(TwoMembers, { Running, Walking })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_b, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_written_2_or_w_2_types(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, (Movement.value, Running) || (TwoMembers.b, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member_a = ecs_lookup(world, "Movement.value"); + test_assert(member_a != 0); + ecs_entity_t member_b = ecs_lookup(world, "TwoMembers.b"); + test_assert(member_b != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Sitting })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(TwoMembers, { Sitting, Walking })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + + ecs_set(world, e1, Movement, { Walking }); + ecs_set(world, e2, Movement, { Running }); + ecs_set(world, e3, Movement, { Sitting }); + ecs_set(world, e4, Movement, { Running }); + ecs_set(world, e5, Movement, { Sitting }); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_b, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_2_or_2_types_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(TwoMembers.b, *) || (Movement.value, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member_a = ecs_lookup(world, "TwoMembers.b"); + test_assert(member_a != 0); + ecs_entity_t member_b = ecs_lookup(world, "Movement.value"); + test_assert(member_b != 0); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Sitting })); + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_set(world, e4, TwoMembers, { Running, Walking }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(member_a, Sitting), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(member_a, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(member_b, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(member_b, Sitting), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_this_2_or_2_types_dep_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "(TwoMembers.b, $x) || (Movement.value, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member_a = ecs_lookup(world, "TwoMembers.b"); + test_assert(member_a != 0); + ecs_entity_t member_b = ecs_lookup(world, "Movement.value"); + test_assert(member_b != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(TwoMembers, { Walking, Sitting })); + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_set(world, e4, TwoMembers, { Running, Walking }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + test_uint(ecs_pair(member_a, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Sitting, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + test_uint(ecs_pair(member_a, Sitting), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + test_uint(ecs_pair(member_a, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(Running, ecs_iter_get_var(&it, y_var)); + test_uint(ecs_pair(member_b, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(Sitting, ecs_iter_get_var(&it, y_var)); + test_uint(ecs_pair(member_b, Sitting), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_member_eq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_member_eq_no_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + /* ecs_entity_t e1 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + /* ecs_entity_t e2 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + /* ecs_entity_t e3 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + /* ecs_entity_t e4 = */ ecs_insert(world, ecs_value(Movement, { Sitting })); + /* ecs_entity_t e5 = */ ecs_insert(world, ecs_value(Movement, { Walking })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_member_eq_all_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_member_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement.value($x, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_member_neq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count == 0); + ecs_entity_t x = ecs_iter_get_var(&it, x_var); + e0_found |= x == e0; + e1_found |= x == e1; + e2_found |= x == e2; + e3_found |= x == e3; + e4_found |= x == e4; + e5_found |= x == e5; + } + + test_bool(true, e0_found); + test_bool(false, e1_found); + test_bool(false, e2_found); + test_bool(true, e3_found); + test_bool(true, e4_found); + test_bool(false, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_member_neq_no_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count == 0); + ecs_entity_t x = ecs_iter_get_var(&it, x_var); + e0_found |= x == e0; + e1_found |= x == e1; + e2_found |= x == e2; + e3_found |= x == e3; + e4_found |= x == e4; + e5_found |= x == e5; + } + + test_bool(true, e0_found); + test_bool(false, e1_found); + test_bool(false, e2_found); + test_bool(false, e3_found); + test_bool(false, e4_found); + test_bool(false, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_member_neq_all_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Walking })); + + bool e0_found = false; + bool e1_found = false; + bool e2_found = false; + bool e3_found = false; + bool e4_found = false; + bool e5_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count == 0); + ecs_entity_t x = ecs_iter_get_var(&it, x_var); + e0_found |= x == e0; + e1_found |= x == e1; + e2_found |= x == e2; + e3_found |= x == e3; + e4_found |= x == e4; + e5_found |= x == e5; + } + + test_bool(true, e0_found); + test_bool(true, e1_found); + test_bool(true, e2_found); + test_bool(true, e3_found); + test_bool(true, e4_found); + test_bool(true, e5_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_written_member_eq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_written_member_eq_no_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Walking })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_written_member_eq_all_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_written_member_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), Movement.value($x, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Walking })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_written_member_neq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), !Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e0, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_written_member_neq_no_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), !Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Running })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Running })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e0, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void MemberTarget_var_written_member_neq_all_matches(void) { + ecs_world_t *world = ecs_mini(); + + ECS_IMPORT(world, FlecsMeta); + ECS_TAG(world, Foo); + + register_types(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), !Movement.value($x, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t member = ecs_lookup(world, "Movement.value"); + test_assert(member != 0); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e0 = ecs_new_w(world, Foo); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Movement, { Walking })); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Movement, { Sitting })); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Movement, { Walking })); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e0, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Movement), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(member, Walking), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Operators.c b/vendors/flecs/test/query/src/Operators.c new file mode 100644 index 000000000..7e7f92998 --- /dev/null +++ b/vendors/flecs/test/query/src/Operators.c @@ -0,0 +1,9406 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void Operators_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +void Operators_2_and_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !RelB($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e1, RelB); + ecs_add(world, e2, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !Position($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e1, Position); + ecs_add(world, e2, Position); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, Position); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_out_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), [out] !RelB($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e1, RelB); + ecs_add(world, e2, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_out_not_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), [out] !Position($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e1, Position); + ecs_add(world, e2, Position); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, Position); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_not_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !RelB($this), !RelC($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e1, RelB); + ecs_add(world, e2, RelB); + ecs_add(world, e1, RelC); + ecs_add(world, e2, RelC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e2, RelC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, RelC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_rel_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !*($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !RelA($this, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add_pair(world, e2, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_rel_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !*($this, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add_pair(world, e2, RelA, TgtB); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !$x($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !RelA($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add_pair(world, e2, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_rel_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int32_t y_var = ecs_query_find_var(q, "x"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_rel_tgt_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !$x($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add_pair(world, e1, RelA, RelA); + ecs_add_pair(world, e2, RelA, RelA); + ecs_add_pair(world, e1, RelB, RelB); + ecs_add_pair(world, e2, RelB, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelB, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_rel_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), !$x($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelB); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e2, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), !RelA($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e2, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_rel_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), !$x($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); + ecs_add_pair(world, e1, TgtA, TgtA); + ecs_add_pair(world, e1, TgtB, TgtB); + ecs_add_pair(world, e2, TgtA, TgtA); + ecs_add_pair(world, e2, TgtB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, TgtA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e2, TgtB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_rel_tgt_same_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), !$x($this, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); + ecs_add_pair(world, e1, TgtA, e1); + ecs_add_pair(world, e1, TgtB, e1); + ecs_add_pair(world, e2, TgtA, e2); + ecs_add_pair(world, e2, TgtB, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, TgtA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, e1), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e2, TgtB, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, e1), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtB, e2), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + + +void Operators_2_and_not_pair_rel_src_tgt_same_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), !$x($x, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add_pair(world, e1, e1, e1); + ecs_add_pair(world, e2, e2, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e2, e2, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_any_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !_($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), !RelA($this, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_not_pair_any_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), !RelA(_, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); + ecs_new_w_pair(world, Tag, TgtB); + ecs_new_w_pair(world, Tag, TgtC); + + ecs_entity_t e4 = ecs_new_w_pair(world, RelA, TgtB); + ecs_add_pair(world, e4, RelA, TgtC); + ecs_new_w_pair(world, RelA, TgtC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_any_src_fixed_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "!RelA(_)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_any_src_any_tgt_fixed_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "!RelA(_, _)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_any_src_any_first_fixed_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "!_(_, TgtA)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_any_src_any_childof_pair_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "!ChildOf(_, _)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_any_src_any_isa_pair_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "!IsA(_, _)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsIsA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, EcsIsA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_match_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Foo" + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add_id(world, e, EcsPrefab); + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e); + } + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_match_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Foo" + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add_id(world, e, EcsDisabled); + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e); + } + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_match_not_queryable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Foo" + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != EcsWildcard); + test_assert(it.entities[i] != EcsAny); + test_assert(it.entities[i] != EcsThis); + } + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_match_prefab_w_match_prefab_flag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Foo", + .flags = EcsQueryMatchPrefab + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_add_id(world, p, EcsPrefab); + ecs_entity_t d = ecs_new(world); + ecs_add_id(world, d, EcsDisabled); + + bool prefab_matched = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != d); + if (it.entities[i] == p) { + prefab_matched = true; + } + } + } + + test_assert(prefab_matched); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_match_disabled_w_match_disabled_flag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Foo", + .flags = EcsQueryMatchDisabled + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_add_id(world, p, EcsPrefab); + ecs_entity_t d = ecs_new(world); + ecs_add_id(world, d, EcsDisabled); + + bool disabled_matched = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != p); + if (it.entities[i] == d) { + disabled_matched = true; + } + } + } + + test_assert(disabled_matched); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_not_match_disabled_w_match_prefab_disabled_flag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Foo", + .flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_add_id(world, p, EcsPrefab); + ecs_entity_t d = ecs_new(world); + ecs_add_id(world, d, EcsDisabled); + + bool prefab_matched = false; + bool disabled_matched = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + if (it.entities[i] == p) { + prefab_matched = true; + } + if (it.entities[i] == d) { + disabled_matched = true; + } + } + } + + test_assert(prefab_matched); + test_assert(disabled_matched); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelB($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e2, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelB($this), ?RelC($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_add(world, e2, RelB); + ecs_add(world, e3, RelC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_rel_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?*($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?$x($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_rel_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtB, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_rel_tgt_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?$x($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e2, TgtA, TgtA); + ecs_add_pair(world, e3, TgtA, TgtA); + ecs_add_pair(world, e3, RelA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, RelA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_rel_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), ?$x($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelA); + ecs_entity_t e3 = ecs_new_w_pair(world, Tag, RelA); + ecs_add_pair(world, e3, Tag, RelB); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), ?RelA($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtA); + ecs_add_pair(world, e3, Tag, TgtB); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtB); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_rel_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), ?$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelA); + ecs_entity_t e3 = ecs_new_w_pair(world, Tag, RelA); + ecs_add_pair(world, e3, Tag, RelB); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtB, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_rel_tgt_same_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), ?$x($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtA); + ecs_add_pair(world, e3, Tag, RelA); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_add_pair(world, e2, TgtA, TgtA); + ecs_add_pair(world, e3, TgtA, TgtA); + ecs_add_pair(world, e3, RelA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, RelA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_rel_src_tgt_same_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), ?$x($x, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_add_pair(world, e2, e2, e2); + ecs_add_pair(world, e3, e3, e3); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e3, e3), ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_optional_pair_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelB($this, $x), ?RelC($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int32_t y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelC, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelC, EcsWildcard), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelC, EcsWildcard), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelC, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(TgtB, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_any_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?_($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_and_optional_pair_any_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), ?RelA(_, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtC); + + ecs_entity_t e4 = ecs_new_w_pair(world, RelA, TgtB); + ecs_add_pair(world, e4, RelA, TgtC); + ecs_new_w_pair(world, RelA, TgtC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtC), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_and_pair_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?$x($this, TgtA), $x($this, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_entity_t e4 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e1, RelA, TgtA); + + ecs_add_pair(world, e2, RelA, TgtB); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_add_pair(world, e4, RelA, TgtA); + ecs_add_pair(world, e4, RelA, TgtB); + ecs_add_pair(world, e4, RelB, TgtA); + ecs_add_pair(world, e4, RelB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_and_pair_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, $x), RelB($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_entity_t e4 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e1, RelA, TgtA); + + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_add_pair(world, e4, RelA, TgtA); + ecs_add_pair(world, e4, RelA, TgtB); + ecs_add_pair(world, e4, RelB, TgtA); + ecs_add_pair(world, e4, RelB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_and_pair_rel_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?$x($this, $y), $y($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e2, RelA, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(TgtA, RelA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, RelA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(RelA, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_and_pair_rel_tgt_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, $x), $x($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e2, RelA, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, TgtA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_and_pair_rel_tgt_same_other_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, $x), $y($x, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new_w_pair(world, TgtA, TgtA); + ecs_entity_t t3 = ecs_new_w_pair(world, TgtB, TgtB); + ecs_add_pair(world, t3, TgtC, TgtC); + + ecs_add_pair(world, e2, RelA, t1); + ecs_add_pair(world, e3, RelA, t2); + ecs_add_pair(world, e3, RelA, t3); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(EcsWildcard, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t2), ecs_field_id(&it, 1)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t2, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(t2, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t3), ecs_field_id(&it, 1)); + test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t3, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(t3, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t3), ecs_field_id(&it, 1)); + test_uint(ecs_pair(TgtC, TgtC), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t3, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(t3, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_and_pair_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, $x), Tag($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new_w(world, Tag); + ecs_entity_t t3 = ecs_new_w(world, Tag); + + ecs_add_pair(world, e2, RelA, t1); + ecs_add_pair(world, e3, RelA, t2); + ecs_add_pair(world, e3, RelA, t3); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(EcsWildcard, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t2), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t2, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(t2, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t3), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t3, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(t3, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_optional_pair_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?$x($this, TgtA), ?$x($this, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_entity_t e4 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e1, RelA, TgtA); + + ecs_add_pair(world, e2, RelA, TgtB); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_add_pair(world, e4, RelA, TgtA); + ecs_add_pair(world, e4, RelA, TgtB); + ecs_add_pair(world, e4, RelB, TgtA); + ecs_add_pair(world, e4, RelB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_optional_pair_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, $x), ?RelB($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_entity_t e4 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e1, RelA, TgtA); + + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + ecs_add_pair(world, e4, RelA, TgtA); + ecs_add_pair(world, e4, RelA, TgtB); + ecs_add_pair(world, e4, RelB, TgtA); + ecs_add_pair(world, e4, RelB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_optional_pair_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, $x), ?Tag($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new_w(world, Tag); + ecs_entity_t t3 = ecs_new_w(world, Tag); + + ecs_add_pair(world, e2, RelA, t1); + ecs_add_pair(world, e3, RelA, t2); + ecs_add_pair(world, e3, RelA, t3); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(EcsWildcard, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t1), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t1, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(t1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t2), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t2, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(t2, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t3), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t3, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(t3, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_not_pair_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?$x($this, TgtA), !$x($this, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtB); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e3, RelB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_not_pair_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, $x), !RelB($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtA); + + ecs_add_pair(world, e3, RelA, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + ecs_add_pair(world, e3, RelA, TgtB); + ecs_add_pair(world, e3, RelB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e3, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e3, RelB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_and_optional_dependent_not_pair_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), ?RelA($this, $x), !Tag($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_entity_t t1 = ecs_new_w(world, Tag); + ecs_entity_t t2 = ecs_new_w(world, Tag); + ecs_entity_t t3 = ecs_new_w(world, Tag); + + ecs_add_pair(world, e2, RelA, t1); + ecs_add_pair(world, e3, RelA, t2); + ecs_add_pair(world, e3, RelA, t3); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(EcsWildcard, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, t1, Tag); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(EcsWildcard, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t1), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t1, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(t1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e3, RelA, t2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(EcsWildcard, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t1), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t1, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(t1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e3, RelA, t3); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e3, it.entities[1]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(EcsWildcard, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, t1), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(t1, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(t1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_optional_any_src_fixed_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "?RelA(_)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w(world, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_optional_any_src_any_tgt_fixed_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "?RelA(_, _)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_optional_any_src_any_first_fixed_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "?_(_, TgtA)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, TgtA, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_optional_any_src_any_childof_pair_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "?ChildOf(_, _)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_optional_any_src_any_isa_pair_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "?IsA(_, _)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsIsA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_new_w_pair(world, EcsIsA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsIsA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this) || RelB($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e2, RelB); + ecs_entity_t e3 = ecs_new_w(world, RelB); + ecs_entity_t e4 = ecs_new_w(world, RelB); + ecs_add(world, e4, Tag); + ecs_new_w(world, RelC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(e1, it.entities[0]); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(e2, it.entities[0]); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(e3, it.entities[0]); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(e4, it.entities[0]); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, RelD); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this) || RelB($this) || RelC($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelB); + ecs_entity_t e3 = ecs_new_w(world, RelC); + ecs_new_w(world, RelD); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelC, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_w_and(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA || TagB, Foo", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_entity_t e3 = ecs_new_w(world, TagB); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + + ecs_new_w(world, TagB); + ecs_new_w(world, TagC); + ecs_new_w(world, Foo); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_int(0, it.trs[0]->index); + test_int(1, it.trs[1]->index); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_int(0, it.trs[0]->index); + test_int(2, it.trs[1]->index); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_int(0, it.trs[0]->index); + test_int(1, it.trs[1]->index); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_and_w_2_or_w_and(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, TagA || TagB, Bar", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + test_int(q->term_count, 4); + test_int(q->field_count, 3); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_entity_t e3 = ecs_new_w(world, TagB); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + + ecs_add(world, e1, Bar); + ecs_add(world, e2, Bar); + ecs_add(world, e3, Bar); + + ecs_new_w(world, TagB); + ecs_new_w(world, TagC); + ecs_new_w(world, Foo); + ecs_new_w(world, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(Bar, ecs_field_id(&it, 2)); + test_int(1, it.trs[0]->index); + test_int(0, it.trs[1]->index); + test_int(2, it.trs[2]->index); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(Bar, ecs_field_id(&it, 2)); + test_int(2, it.trs[0]->index); + test_int(0, it.trs[1]->index); + test_int(3, it.trs[2]->index); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(Bar, ecs_field_id(&it, 2)); + test_int(1, it.trs[0]->index); + test_int(0, it.trs[1]->index); + test_int(2, it.trs[2]->index); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_and_w_2_or_w_and_components(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, TagA || TagB, Mass", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + test_int(q->term_count, 4); + test_int(q->field_count, 3); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_entity_t e3 = ecs_new_w(world, TagB); + + ecs_set(world, e1, Position, {11, 21}); + ecs_set(world, e2, Position, {12, 22}); + ecs_set(world, e3, Position, {13, 23}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_new_w(world, TagB); + ecs_new_w(world, TagC); + ecs_new_w(world, Position); + ecs_new_w(world, Mass); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_int(0, it.trs[0]->index); + test_int(2, it.trs[1]->index); + test_int(1, it.trs[2]->index); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); test_int(p->y, 21); + } + { + Mass *v = ecs_field(&it, Mass, 2); + test_assert(v != NULL); + test_int(v[0], 1); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_int(0, it.trs[0]->index); + test_int(2, it.trs[1]->index); + test_int(1, it.trs[2]->index); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); test_int(p->y, 22); + } + { + Mass *v = ecs_field(&it, Mass, 2); + test_assert(v != NULL); + test_int(v[0], 2); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_int(0, it.trs[0]->index); + test_int(2, it.trs[1]->index); + test_int(1, it.trs[2]->index); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); test_int(p->y, 23); + } + { + Mass *v = ecs_field(&it, Mass, 2); + test_assert(v != NULL); + test_int(v[0], 3); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_and_w_2_or_w_and_set_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, TagA || TagB, Bar", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + test_int(q->term_count, 4); + test_int(q->field_count, 3); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_entity_t e3 = ecs_new_w(world, TagB); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + + ecs_add(world, e1, Bar); + ecs_add(world, e2, Bar); + ecs_add(world, e3, Bar); + + ecs_new_w(world, TagB); + ecs_new_w(world, TagC); + ecs_new_w(world, Foo); + ecs_new_w(world, Bar); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(Bar, ecs_field_id(&it, 2)); + test_int(1, it.trs[0]->index); + test_int(0, it.trs[1]->index); + test_int(2, it.trs[2]->index); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(Bar, ecs_field_id(&it, 2)); + test_int(2, it.trs[0]->index); + test_int(0, it.trs[1]->index); + test_int(3, it.trs[2]->index); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(Bar, ecs_field_id(&it, 2)); + test_int(1, it.trs[0]->index); + test_int(0, it.trs[1]->index); + test_int(2, it.trs[2]->index); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_and_w_2_or_w_and_components_set_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Mass); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, TagA || TagB, Mass", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + test_int(q->term_count, 4); + test_int(q->field_count, 3); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_entity_t e3 = ecs_new_w(world, TagB); + + ecs_set(world, e1, Position, {11, 21}); + ecs_set(world, e2, Position, {12, 22}); + ecs_set(world, e3, Position, {13, 23}); + + ecs_set(world, e1, Mass, {1}); + ecs_set(world, e2, Mass, {2}); + ecs_set(world, e3, Mass, {3}); + + ecs_new_w(world, TagB); + ecs_new_w(world, TagC); + ecs_new_w(world, Position); + ecs_new_w(world, Mass); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_int(0, it.trs[0]->index); + test_int(2, it.trs[1]->index); + test_int(1, it.trs[2]->index); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); test_int(p->y, 21); + } + { + Mass *v = ecs_field(&it, Mass, 2); + test_assert(v != NULL); + test_int(v[0], 1); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_int(0, it.trs[0]->index); + test_int(2, it.trs[1]->index); + test_int(1, it.trs[2]->index); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); test_int(p->y, 22); + } + { + Mass *v = ecs_field(&it, Mass, 2); + test_assert(v != NULL); + test_int(v[0], 2); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_int(0, it.trs[0]->index); + test_int(2, it.trs[1]->index); + test_int(1, it.trs[2]->index); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); test_int(p->y, 23); + } + { + Mass *v = ecs_field(&it, Mass, 2); + test_assert(v != NULL); + test_int(v[0], 3); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_or_w_and(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA || TagB || TagC, Foo", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_entity_t e3 = ecs_new_w(world, TagB); + ecs_add(world, e3, TagC); + ecs_entity_t e4 = ecs_new_w(world, TagC); + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + + ecs_new_w(world, TagB); + ecs_new_w(world, TagC); + ecs_new_w(world, Foo); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(TagC, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this), RelA($this) || RelB($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e1, RelA); + ecs_add(world, e2, RelA); + ecs_add(world, e2, RelB); + ecs_add(world, e3, RelB); + ecs_add(world, e4, RelC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_or_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, RelD); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this), RelA($this) || RelB($this) || RelC($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e1, RelA); + ecs_add(world, e2, RelB); + ecs_add(world, e3, RelB); + ecs_add(world, e3, RelC); + ecs_add(world, e4, RelC); + ecs_add(world, e5, RelD); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelC, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_written_w_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), $x($this, TgtA) || $x($this, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelB); + ecs_entity_t e3 = ecs_new_w_pair(world, Tag, RelC); + ecs_entity_t e4 = ecs_new_w_pair(world, Tag, RelA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtB); + ecs_add_pair(world, e3, RelC, TgtA); + ecs_add_pair(world, e3, RelC, TgtB); + ecs_add_pair(world, e4, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelC), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_or_written_w_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, RelD); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), $x($this, TgtA) || $x($this, TgtB) || $x($this, TgtC)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, RelA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, RelB); + ecs_entity_t e3 = ecs_new_w_pair(world, Tag, RelC); + ecs_entity_t e4 = ecs_new_w_pair(world, Tag, RelD); + ecs_entity_t e5 = ecs_new_w_pair(world, Tag, RelA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtB); + ecs_add_pair(world, e3, RelC, TgtA); + ecs_add_pair(world, e3, RelC, TgtB); + ecs_add_pair(world, e4, RelD, TgtC); + ecs_add_pair(world, e5, RelD, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelC), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, RelD), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelD, TgtC), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(RelD, ecs_iter_get_var(&it, x_var)); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_written_w_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), RelA($this, $x) || RelB($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtC); + ecs_entity_t e4 = ecs_new_w_pair(world, Tag, TgtA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtB); + ecs_add_pair(world, e3, RelA, TgtC); + ecs_add_pair(world, e3, RelB, TgtC); + ecs_add_pair(world, e4, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtC), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_written_w_rel_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, TgtD); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $x), RelB($this, $y), $x($this, $y) || $y($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtC); + ecs_entity_t e4 = ecs_new_w_pair(world, RelA, TgtD); + + ecs_add_pair(world, e1, RelB, TgtD); + ecs_add_pair(world, e2, RelB, TgtC); + ecs_add_pair(world, e3, RelB, TgtB); + ecs_add_pair(world, e4, RelB, TgtA); + + ecs_add_pair(world, e2, TgtB, TgtC); + ecs_add_pair(world, e3, TgtB, TgtC); + ecs_add_pair(world, e4, TgtD, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtC), ecs_field_id(&it, 1)); + test_uint(ecs_pair(TgtB, TgtC), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(TgtC, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 1)); + test_uint(ecs_pair(TgtB, TgtC), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + test_uint(TgtB, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtD), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(ecs_pair(TgtD, TgtA), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtD, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_written_w_rel_tgt_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, TgtD); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $x), $x($this, $x) || RelB($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtC); + ecs_new_w_pair(world, RelA, TgtC); + + ecs_add_pair(world, e1, TgtA, TgtA); + ecs_add_pair(world, e2, RelB, TgtB); + ecs_add_pair(world, e3, TgtC, TgtC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtC, TgtC), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_or_written_w_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + ECS_TAG(world, TgtD); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this, $x), RelA($this, $x) || RelB($this, $x) || RelC($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w_pair(world, Tag, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Tag, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, Tag, TgtC); + ecs_entity_t e4 = ecs_new_w_pair(world, Tag, TgtD); + ecs_entity_t e5 = ecs_new_w_pair(world, Tag, TgtA); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelB, TgtB); + ecs_add_pair(world, e3, RelA, TgtC); + ecs_add_pair(world, e3, RelB, TgtC); + ecs_add_pair(world, e4, RelC, TgtD); + ecs_add_pair(world, e5, RelA, TgtD); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtC), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Tag, TgtD), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelC, TgtD), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(TgtD, ecs_iter_get_var(&it, x_var)); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_chains_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, RelD); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this), RelA($this) || RelB($this), RelC($this) || RelD($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + + ecs_add(world, e1, RelA); + ecs_add(world, e2, RelC); + + ecs_add(world, e3, RelA); + ecs_add(world, e3, RelC); + + ecs_add(world, e4, RelA); + ecs_add(world, e4, RelD); + + ecs_add(world, e5, RelB); + ecs_add(world, e5, RelC); + + ecs_add(world, e6, RelB); + ecs_add(world, e6, RelD); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(RelD, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e4, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e5, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(RelD, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e6, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_chains(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, RelD); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add(world, e1, RelA); + ecs_add(world, e2, RelC); + + ecs_add(world, e3, RelA); + ecs_add(world, e3, RelC); + + ecs_add(world, e4, RelA); + ecs_add(world, e4, RelD); + + ecs_add(world, e5, RelB); + ecs_add(world, e5, RelC); + + ecs_add(world, e6, RelB); + ecs_add(world, e6, RelD); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this) || RelB($this), RelC($this) || RelD($this)", + .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_uint(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelC, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelD, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(RelC, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(RelD, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_dependent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelB($this) || RelA($this, $tgt), RelC($tgt)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + int tgt_var = ecs_query_find_var(q, "tgt"); + test_assert(tgt_var != -1); + + ecs_entity_t tgt = ecs_new_w(world, RelC); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add(world, e1, RelB); + ecs_add_pair(world, e2, RelA, tgt); + ecs_add(world, e3, RelB); + ecs_add(world, e3, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, tgt), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + test_uint(tgt, ecs_iter_get_var(&it, tgt_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_dependent_reverse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $tgt) || RelB($this), RelC($tgt)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + int tgt_var = ecs_query_find_var(q, "tgt"); + test_assert(tgt_var != -1); + + ecs_entity_t tgt = ecs_new_w(world, RelC); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add(world, e1, RelB); + ecs_add_pair(world, e2, RelA, tgt); + ecs_add(world, e3, RelB); + ecs_add(world, e3, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, tgt), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, it.entities[0]); + test_uint(tgt, ecs_iter_get_var(&it, tgt_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_dependent_2_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, RelD); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $x) || RelB($this, $y), RelC($x), RelD($y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_entity_t tgt_a = ecs_new_w(world, RelC); + ecs_entity_t tgt_b = ecs_new_w(world, RelD); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + + ecs_add_pair(world, e1, RelA, tgt_a); + ecs_add_pair(world, e2, RelB, tgt_b); + + ecs_add_pair(world, e3, RelA, tgt_b); + ecs_add_pair(world, e4, RelB, tgt_a); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, tgt_a), ecs_field_id(&it, 0)); + test_uint(RelC, ecs_field_id(&it, 1)); + test_uint(RelD, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(e1, it.entities[0]); + test_uint(tgt_a, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelB, tgt_b), ecs_field_id(&it, 0)); + test_uint(RelC, ecs_field_id(&it, 1)); + test_uint(RelD, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(e2, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(tgt_b, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_written_dependent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Foo); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, RelA($this, $tgt) || RelB($this), RelC($tgt)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + int tgt_var = ecs_query_find_var(q, "tgt"); + test_assert(tgt_var != -1); + + ecs_entity_t tgt = ecs_new_w(world, RelC); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + ecs_add(world, e1, RelB); + ecs_add_pair(world, e2, RelA, tgt); + ecs_add(world, e3, RelB); + ecs_add(world, e3, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(e1, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, tgt), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(e2, it.entities[0]); + test_uint(tgt, ecs_iter_get_var(&it, tgt_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(e3, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, tgt_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_written_dependent_2_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Foo); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, RelD); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, RelA($this, $x) || RelB($this, $y), RelC($x), RelD($y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_entity_t tgt_a = ecs_new_w(world, RelC); + ecs_entity_t tgt_b = ecs_new_w(world, RelD); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + + ecs_add_pair(world, e1, RelA, tgt_a); + ecs_add_pair(world, e2, RelB, tgt_b); + + ecs_add_pair(world, e3, RelA, tgt_b); + ecs_add_pair(world, e4, RelB, tgt_a); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, tgt_a), ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_id(&it, 2)); + test_uint(RelD, ecs_field_id(&it, 3)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + test_uint(e1, it.entities[0]); + test_uint(tgt_a, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, tgt_b), ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_id(&it, 2)); + test_uint(RelD, ecs_field_id(&it, 3)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + test_uint(e2, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(tgt_b, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_w_dependent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Foo); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, RelD); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, ?RelA($this, $x), RelB($x) || RelC($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t tgt_a = ecs_new_w(world, RelB); + ecs_entity_t tgt_b = ecs_new_w(world, RelC); + ecs_entity_t tgt_c = ecs_new_w(world, RelD); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + + ecs_add_pair(world, e2, RelA, tgt_a); + ecs_add_pair(world, e3, RelA, tgt_b); + ecs_add_pair(world, e4, RelA, tgt_c); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 1)); + // test_uint(RelC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_uint(e1, it.entities[0]); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, tgt_a), ecs_field_id(&it, 1)); + test_uint(RelB, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(e2, it.entities[0]); + test_uint(tgt_a, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, tgt_b), ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_uint(e3, it.entities[0]); + test_uint(tgt_b, ecs_iter_get_var(&it, x_var)); + + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_w_both(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA || RelB", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e2, RelB); + ecs_entity_t e3 = ecs_new_w(world, RelB); + ecs_new_w(world, RelC); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(RelB, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_3_or_w_both(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, RelD); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA || RelB || RelC", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e2, RelB); + ecs_entity_t e3 = ecs_new_w(world, RelA); + ecs_add(world, e3, RelD); + + ecs_entity_t e4 = ecs_new_w(world, RelB); + ecs_entity_t e5 = ecs_new_w(world, RelB); + ecs_add(world, e5, RelC); + ecs_entity_t e6 = ecs_new_w(world, RelB); + ecs_add(world, e6, RelD); + + ecs_entity_t e7 = ecs_new_w(world, RelC); + ecs_entity_t e8 = ecs_new_w(world, RelC); + ecs_add(world, e8, RelD); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(RelB, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(RelB, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(RelB, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(RelC, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e8, it.entities[0]); + test_uint(RelC, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_w_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA || RelB, !RelC", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e2, RelB); + ecs_add(world, e2, RelC); + ecs_entity_t e3 = ecs_new_w(world, RelB); + ecs_new_w(world, RelC); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(RelB, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_w_not_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA || RelB, !Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e2, RelB); + ecs_add(world, e2, Position); + ecs_entity_t e3 = ecs_new_w(world, RelB); + ecs_new_w(world, Position); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(RelB, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_w_not_out_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA || RelB, [out] !Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_add(world, e2, RelB); + ecs_add(world, e2, Position); + ecs_entity_t e3 = ecs_new_w(world, RelB); + ecs_new_w(world, Position); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(RelA, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(RelB, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_or_w_not_out_all_components(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Velocity || Mass, [out] !Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w(world, Velocity); + ecs_entity_t e2 = ecs_new_w(world, Velocity); + ecs_add(world, e2, Mass); + ecs_add(world, e2, Position); + ecs_entity_t e3 = ecs_new_w(world, Mass); + ecs_new_w(world, Position); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_id(Mass), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_not_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "!TagB($this), TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_2_optional_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "?TagB($this), TagA($this)", + .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_uint(3, it.count); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e3, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_only_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "!TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + while (ecs_query_next(&it)) { + for (int32_t i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e1); + test_assert(it.entities[i] != e2); + count ++; + } + } + + test_assert(count != 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_only_not_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Position($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + while (ecs_query_next(&it)) { + for (int32_t i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e1); + test_assert(it.entities[i] != e2); + count ++; + } + } + + test_assert(count != 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_only_not_out_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "[out] !Position($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + while (ecs_query_next(&it)) { + for (int32_t i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e1); + test_assert(it.entities[i] != e2); + count ++; + } + } + + test_assert(count != 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_only_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "?TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int32_t i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + count ++; + } + } + + test_assert(count > 2); + test_bool(e1_found, true); + test_bool(e2_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_only_optional_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "?Position($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int32_t i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + count ++; + } + } + + test_assert(count > 2); + test_bool(e1_found, true); + test_bool(e2_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_not_after_fixed_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagC(ent), !TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TagC); + + { + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + while (ecs_query_next(&it)) { + for (int32_t i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e1); + test_assert(it.entities[i] != e2); + count ++; + } + } + test_assert(count != 0); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_optional_after_fixed_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagC(ent), ?TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TagC); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int32_t i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + count ++; + } + } + + test_assert(count > 2); + test_bool(e1_found, true); + test_bool(e2_found, true); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_root_entities_empty(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "!ChildOf($this, _)", + .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_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(EcsFlecs, it.entities[0]); + + if (ecs_query_next(&it)) { + test_uint(1, it.count); + test_uint(q->entity, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_root_entities(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity(world, { .name = "e1.e2" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "!ChildOf($this, _)", + .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_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(EcsFlecs, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(e1, it.entities[0]); + + if (ecs_query_next(&it)) { + test_uint(1, it.count); + test_uint(q->entity, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_root_entities_w_children(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "!ChildOf($this, _), ChildOf(_, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add_pair(world, e3, EcsChildOf, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, EcsFlecs), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(EcsFlecs, ecs_iter_get_var(&it, this_var)); + test_uint(EcsFlecs, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, e2), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_root_entities_w_optional_children(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add_pair(world, e3, EcsChildOf, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "!ChildOf($this, _), ?ChildOf(_, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, EcsFlecs), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(EcsFlecs, ecs_iter_get_var(&it, this_var)); + test_uint(EcsFlecs, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, Tag), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(Tag, ecs_iter_get_var(&it, this_var)); + test_uint(Tag, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, e1), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, e2), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, e4), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, this_var)); + test_uint(e4, it.entities[0]); + + if (ecs_query_next(&it)) { + test_uint(1, it.count); + test_uint(q->entity, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_core_entities_w_optional_children(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "ChildOf($this, flecs.core), ?ChildOf(_, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(ecs_field_is_set(&it, 0)); + for (int i = 0; i < it.count; i ++) { + test_assert(ecs_has_pair( + world, it.entities[i], EcsChildOf, EcsFlecsCore)); + if (ecs_field_is_set(&it, 1)) { + test_assert(ecs_count_id( + world, ecs_pair(EcsChildOf, it.entities[i])) != 0); + } else { + test_assert(ecs_count_id( + world, ecs_pair(EcsChildOf, it.entities[i])) == 0); + } + } + } + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_root_entities_w_not_children(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add_pair(world, e3, EcsChildOf, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "!ChildOf($this, _), !ChildOf(_, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, Tag), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(Tag, ecs_iter_get_var(&it, this_var)); + test_uint(Tag, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, e1), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(EcsChildOf, 0), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, e4), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, this_var)); + test_uint(e4, it.entities[0]); + + if (ecs_query_next(&it)) { + test_uint(1, it.count); + test_uint(q->entity, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_core_entities_w_not_children(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "ChildOf($this, flecs.core), !ChildOf(_, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(ecs_field_is_set(&it, 0)); + test_assert(!ecs_field_is_set(&it, 1)); + for (int i = 0; i < it.count; i ++) { + test_assert(ecs_has_pair( + world, it.entities[i], EcsChildOf, EcsFlecsCore)); + test_assert(ecs_count_id( + world, ecs_pair(EcsChildOf, it.entities[i])) == 0); + } + } + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_ent_src_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, RelA); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelA(e1)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelB(e1)", + .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_uint(0, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, Tgt); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, Tgt); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelA(e1, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelB(e1, Tgt)", + .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_uint(0, it.count); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!*(e1, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!*(e1, TgtB)", + .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_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelA(e1, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelB(e1, *)", + .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_uint(0, it.count); + test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_tgt_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!*(e1, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!_(e1, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!_(e1, TgtB)", + .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_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_tgt_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelA(e1, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelB(e1, _)", + .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_uint(0, it.count); + test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_tgt_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!_(e1, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!$x(e1, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!$x(e1, TgtB)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, TgtB), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelA(e1, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!RelB(e1, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!$x(e1, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_tgt_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "!$x(e1, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Operators_1_this_src_not_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "!$x($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, RelA, TgtB); + + bool e2_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e1); + if (it.entities[i] == e2) { + e2_found = true; + } + } + + test_uint(ecs_pair(EcsWildcard, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + } + + test_assert(e2_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_this_src_not_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "!RelA($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, RelB, TgtA); + + bool e2_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e1); + if (it.entities[i] == e2) { + e2_found = true; + } + } + + test_uint(ecs_pair(RelA, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + } + + test_assert(e2_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_this_src_not_pair_rel_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "!$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, TgtA); + + bool e2_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e1); + if (it.entities[i] == e2) { + e2_found = true; + } + } + + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, y_var)); + } + + test_assert(e2_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_this_src_not_pair_rel_tgt_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e2, RelA, RelA); + + ecs_entity_t e3 = ecs_new(world); + ecs_add(world, e3, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "!$x($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + bool e1_found = false; + + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + test_assert(it.count != 0); + for (int i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e2); + if (it.entities[i] == e1) { + e1_found = true; + } + } + + test_uint(ecs_pair(EcsWildcard, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + } + + test_assert(e1_found); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_ENTITY(world, RelA, Tag); + ECS_ENTITY(world, RelB, Tag); + ECS_ENTITY(world, RelC, Tag); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e1, RelB, TgtB); + ecs_add_pair(world, e1, RelC, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($x), !$x(e1, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelC, TgtA), ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_ENTITY(world, TgtA, Tag); + ECS_ENTITY(world, TgtB, Tag); + ECS_ENTITY(world, TgtC, Tag); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add_pair(world, e1, RelB, TgtB); + ecs_add_pair(world, e1, RelB, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($x), !RelA(e1, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtC), ecs_field_id(&it, 1)); + test_uint(TgtC, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_ENTITY(world, RelA, (Tag, TgtA)); + ECS_ENTITY(world, RelB, (Tag, TgtA)); + ECS_ENTITY(world, RelC, (Tag, TgtB)); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e1, RelC, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($x, $y), !$x(e1, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Tag, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelC, TgtB), ecs_field_id(&it, 1)); + test_uint(RelC, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + test_uint(TgtB, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_1_ent_src_not_pair_rel_tgt_same_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, RelA); + ECS_ENTITY(world, RelB, Tag); + ECS_ENTITY(world, TgtA, Tag); + ECS_ENTITY(world, TgtB, Tag); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e1, TgtA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add_pair(world, e1, RelB, TgtB); + ecs_add_pair(world, e1, TgtB, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($x), !$x(e1, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, RelB), ecs_field_id(&it, 1)); + test_uint(RelB, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_and_from_fixed_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_PREFAB(world, Type, TagA, TagB); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "and | Type(e)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_not_from_fixed_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_PREFAB(world, Type, TagA, TagB); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "not | Type(e)", + .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(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_or_from_fixed_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_PREFAB(world, Type, TagA, TagB); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "or | Type(e)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_and_from_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_PREFAB(world, Type, TagA, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_add(world, e2, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "and | Type", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, TagA); + ecs_remove(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_not_from_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_PREFAB(world, Type, TagA, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_add(world, e2, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "not | Type", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + } + test_assert(e1_found == true); + test_assert(e2_found == true); + } + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + } + test_assert(e1_found == false); + test_assert(e2_found == false); + } + + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + } + test_assert(e1_found == false); + test_assert(e2_found == false); + } + + ecs_remove(world, e1, TagA); + ecs_remove(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + } + test_assert(e1_found == false); + test_assert(e2_found == false); + } + + ecs_remove(world, e1, TagB); + ecs_remove(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + } + test_assert(e1_found == true); + test_assert(e2_found == true); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_or_from_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_PREFAB(world, Type, TagA, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_add(world, e2, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "or | Type", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, TagB); + ecs_remove(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_and_from_this_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagD); + ECS_PREFAB(world, Type, TagA, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, TagD); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_add(world, e2, TagC); + ecs_add(world, e2, TagD); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagD, and | Type", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, TagA); + ecs_remove(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_not_from_this_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagD); + ECS_PREFAB(world, Type, TagA, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, TagD); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_add(world, e2, TagC); + ecs_add(world, e2, TagD); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagD, not | Type", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + } + test_assert(e1_found == true); + test_assert(e2_found == true); + } + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + } + test_assert(e1_found == false); + test_assert(e2_found == false); + } + + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + } + test_assert(e1_found == false); + test_assert(e2_found == false); + } + + ecs_remove(world, e1, TagA); + ecs_remove(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + } + test_assert(e1_found == false); + test_assert(e2_found == false); + } + + ecs_remove(world, e1, TagB); + ecs_remove(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + bool e1_found = false; + bool e2_found = false; + while (ecs_query_next(&it)) { + for (int i = 0; i < it.count; i ++) { + e1_found |= it.entities[i] == e1; + e2_found |= it.entities[i] == e2; + } + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + } + test_assert(e1_found == true); + test_assert(e2_found == true); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_or_from_this_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagD); + ECS_PREFAB(world, Type, TagA, TagB); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_add(world, e1, TagD); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_add(world, e2, TagC); + ecs_add(world, e2, TagD); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagD, or | Type", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e2, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, TagA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e1, TagB); + ecs_remove(world, e2, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_and_from_empty(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity(world, { .name = "Type" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "and | Type", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_not_from_empty(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity(world, { .name = "Type" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "not | Type", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_or_from_empty(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity(world, { .name = "Type" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "or | Type", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_and_from_empty_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity(world, { .name = "Type" }); + + ecs_entity_t e = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "and | Type, TagA", + .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(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_not_from_empty_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity(world, { .name = "Type" }); + + ecs_entity_t e = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "not | Type, TagA", + .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(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_or_from_empty_w_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity(world, { .name = "Type" }); + + ecs_entity_t e = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "or | Type, TagA", + .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(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_or_w_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA, .oper = EcsOr }, + { TagB }, + { ecs_pair(Rel, EcsWildcard) } + } + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, TagA); + ecs_add(world, e, TagB); + ecs_add_pair(world, e, Rel, TgtA); + ecs_add_pair(world, e, Rel, TgtB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_or_w_component_and_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position), .oper = EcsOr }, + { TagA } + } + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + ecs_add(world, e, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Operators_or_w_tag_and_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA, .oper = EcsOr }, + { ecs_id(Position) } + } + }); + + test_assert(q != NULL); + + ecs_entity_t e = ecs_new(world); + ecs_set(world, e, Position, {10, 20}); + ecs_add(world, e, TagA); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/api/src/SortingEntireTable.c b/vendors/flecs/test/query/src/OrderBy.c similarity index 56% rename from vendors/flecs/test/api/src/SortingEntireTable.c rename to vendors/flecs/test/query/src/OrderBy.c index c7caa3d00..37c9e111f 100644 --- a/vendors/flecs/test/api/src/SortingEntireTable.c +++ b/vendors/flecs/test/query/src/OrderBy.c @@ -1,41 +1,43 @@ -#include +#include #include static -int compare_entity( +int compare_position( ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2) { - return (e1 > e2) - (e1 < e2); -} - -ECS_COMPARE(Position, { const Position *p1 = ptr1; const Position *p2 = ptr2; return (p1->x > p2->x) - (p1->x < p2->x); -}) -ECS_SORT_TABLE(Position) +} -ECS_SORT_TABLE_WITH_COMPARE(Position, PositionSortByEntity, compare_entity) +static +int compare_entity( + ecs_entity_t e1, + const void *ptr1, + ecs_entity_t e2, + const void *ptr2) +{ + return (e1 > e2) - (e1 < e2); +} -void SortingEntireTable_sort_by_component(void) { +void OrderBy_sort_by_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -48,28 +50,28 @@ void SortingEntireTable_sort_by_component(void) { test_assert(it.entities[2] == e1); test_assert(it.entities[3] == e5); test_assert(it.entities[4] == e3); - test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_by_component_same_value_1(void) { +void OrderBy_sort_by_component_same_value_1(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {1, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {1, 0})); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -86,26 +88,27 @@ void SortingEntireTable_sort_by_component_same_value_1(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_by_component_same_value_2(void) { +void OrderBy_sort_by_component_same_value_2(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e6 = ecs_set(world, 0, Position, {1, 0}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e6 = ecs_insert(world, ecs_value(Position, {1, 0})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -122,30 +125,31 @@ void SortingEntireTable_sort_by_component_same_value_2(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_by_component_2_tables(void) { +void OrderBy_sort_by_component_2_tables(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); ecs_add(world, e3, Velocity); ecs_add(world, e4, Velocity); ecs_add(world, e5, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -169,33 +173,34 @@ void SortingEntireTable_sort_by_component_2_tables(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_by_component_3_tables(void) { +void OrderBy_sort_by_component_3_tables(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); - ecs_entity_t e6 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e7 = ecs_set(world, 0, Position, {7, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); + ecs_entity_t e6 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e7 = ecs_insert(world, ecs_value(Position, {7, 0})); ecs_add(world, e5, Velocity); ecs_add(world, e6, Mass); ecs_add(world, e7, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -224,30 +229,31 @@ void SortingEntireTable_sort_by_component_3_tables(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_by_entity(void) { +void OrderBy_sort_by_entity(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); ecs_add(world, e2, Velocity); ecs_add(world, e4, Velocity); ecs_add(world, e5, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by = compare_entity, - .sort_table = PositionSortByEntity + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by_callback = compare_entity }); ecs_iter_t it = ecs_query_iter(world, q); @@ -271,21 +277,23 @@ void SortingEntireTable_sort_by_entity(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_after_add(void) { +void OrderBy_sort_after_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 0})); ecs_add(world, e3, Velocity); ecs_add(world, e4, Velocity); @@ -293,11 +301,10 @@ void SortingEntireTable_sort_after_add(void) { ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, Velocity", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -348,23 +355,25 @@ void SortingEntireTable_sort_after_add(void) { test_int(it.count, 1); test_assert(it.entities[0] == e3); - test_assert(!ecs_query_next(&it)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); ecs_fini(world); } -void SortingEntireTable_sort_after_remove(void) { +void OrderBy_sort_after_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 0})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -376,11 +385,10 @@ void SortingEntireTable_sort_after_remove(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, Velocity", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -451,21 +459,23 @@ void SortingEntireTable_sort_after_remove(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_after_delete(void) { +void OrderBy_sort_after_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 0})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -477,11 +487,10 @@ void SortingEntireTable_sort_after_delete(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, Velocity", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -536,21 +545,23 @@ void SortingEntireTable_sort_after_delete(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_after_set(void) { +void OrderBy_sort_after_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 0})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -562,11 +573,10 @@ void SortingEntireTable_sort_after_set(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, Velocity", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -585,7 +595,7 @@ void SortingEntireTable_sort_after_set(void) { test_int(it.count, 1); test_assert(it.entities[0] == e3); - test_assert(!ecs_query_next(&it)); + test_assert(!ecs_query_next(&it)); ecs_set(world, e1, Position, {7, 0}); @@ -608,14 +618,16 @@ void SortingEntireTable_sort_after_set(void) { test_int(it.count, 1); test_assert(it.entities[0] == e1); - test_assert(!ecs_query_next(&it)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); ecs_fini(world); } static void FlipP(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int32_t i; for (i = 0; i < it->count; i ++) { @@ -625,7 +637,7 @@ void FlipP(ecs_iter_t *it) { } } -void SortingEntireTable_sort_after_system(void) { +void OrderBy_sort_after_system(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Velocity); @@ -634,11 +646,11 @@ void SortingEntireTable_sort_after_system(void) { ECS_SYSTEM(world, FlipP, EcsOnUpdate, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 5}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 6}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 1}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 3}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 5})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 6})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 1})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 3})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -650,11 +662,10 @@ void SortingEntireTable_sort_after_system(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, [in] Velocity", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position, [in] Velocity", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -720,21 +731,23 @@ void SortingEntireTable_sort_after_system(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_after_query(void) { +void OrderBy_sort_after_query(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 5}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 6}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 1}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 3}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 5})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 6})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 1})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 3})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -746,12 +759,12 @@ void SortingEntireTable_sort_after_query(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *flip_q = ecs_query_new(world, "Position"); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, [in] Velocity", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *flip_q = ecs_query(world, { .expr = "Position" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, [in] Velocity", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -825,24 +838,26 @@ void SortingEntireTable_sort_after_query(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_query_fini(flip_q); + ecs_fini(world); } -void SortingEntireTable_sort_by_component_move_pivot(void) { +void OrderBy_sort_by_component_move_pivot(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {10, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {1, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {1, 0})); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); ecs_iter_t it = ecs_query_iter(world, q); @@ -857,29 +872,31 @@ void SortingEntireTable_sort_by_component_move_pivot(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_1000_entities(void) { +void OrderBy_sort_1000_entities(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); for (int i = 0; i < 1000; i ++) { int32_t v = rand(); - ecs_set(world, 0, Position, {v}); + ecs_insert(world, ecs_value(Position, {v})); int32_t x = 0; ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(it.count == (i + 1)); @@ -891,30 +908,32 @@ void SortingEntireTable_sort_1000_entities(void) { } } + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_1000_entities_w_duplicates(void) { +void OrderBy_sort_1000_entities_w_duplicates(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); for (int i = 0; i < 500; i ++) { int32_t v = rand(); - ecs_set(world, 0, Position, {v}); - ecs_set(world, 0, Position, {v}); + ecs_insert(world, ecs_value(Position, {v})); + ecs_insert(world, ecs_value(Position, {v})); int32_t x = 0; ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(it.count == (i + 1) * 2); @@ -926,31 +945,32 @@ void SortingEntireTable_sort_1000_entities_w_duplicates(void) { } } + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_1000_entities_again(void) { +void OrderBy_sort_1000_entities_again(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); - ecs_entity_t start = ecs_new(world, 0); + ecs_entity_t start = ecs_new(world); for (int i = 0; i < 1000; i ++) { int32_t v = rand(); - ecs_ensure(world, i + start); + ecs_make_alive(world, i + start); ecs_set(world, i + start, Position, {v}); ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(it.count == (i + 1)); @@ -971,7 +991,7 @@ void SortingEntireTable_sort_1000_entities_again(void) { ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(it.count == 1000); @@ -980,35 +1000,35 @@ void SortingEntireTable_sort_1000_entities_again(void) { test_assert(x <= p[j].x); x = p[j].x; } - } + } + ecs_query_fini(q); ecs_fini(world); } -void SortingEntireTable_sort_1000_entities_2_types(void) { +void OrderBy_sort_1000_entities_2_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); for (int i = 0; i < 500; i ++) { int32_t v = rand(); - ecs_set(world, 0, Position, {v}); - ecs_entity_t e = ecs_set(world, 0, Position, {v}); + ecs_insert(world, ecs_value(Position, {v})); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {v})); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1022,27 +1042,28 @@ void SortingEntireTable_sort_1000_entities_2_types(void) { test_int(count, (i + 1) * 2); } + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_1000_entities_2_types_again(void) { +void OrderBy_sort_1000_entities_2_types_again(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); - ecs_entity_t start = ecs_new(world, 0); + ecs_entity_t start = ecs_new(world); for (int i = 0; i < 1000; i ++) { int32_t v = rand(); - ecs_ensure(world, i + start); + ecs_make_alive(world, i + start); ecs_set(world, i + start, Position, {v}); if (!(i % 2)) { @@ -1051,7 +1072,7 @@ void SortingEntireTable_sort_1000_entities_2_types_again(void) { ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); int32_t j, x = 0; for (j = 0; j < it.count; j ++) { @@ -1069,7 +1090,7 @@ void SortingEntireTable_sort_1000_entities_2_types_again(void) { ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1082,33 +1103,34 @@ void SortingEntireTable_sort_1000_entities_2_types_again(void) { test_int(count, 1000); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_1000_entities_add_type_after_sort(void) { +void OrderBy_sort_1000_entities_add_type_after_sort(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); - ecs_entity_t start = ecs_new(world, 0); + ecs_entity_t start = ecs_new(world); for (int i = 0; i < 500; i ++) { int32_t v = rand(); - ecs_ensure(world, i + start); + ecs_make_alive(world, i + start); ecs_set(world, i + start, Position, {v}); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1129,14 +1151,14 @@ void SortingEntireTable_sort_1000_entities_add_type_after_sort(void) { for (int i = 0; i < 500; i ++) { int32_t v = rand(); - ecs_ensure(world, i + start + 500); + ecs_make_alive(world, i + start + 500); ecs_set(world, i + start + 500, Position, {v}); ecs_add(world, i + start + 500, Velocity); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1148,37 +1170,38 @@ void SortingEntireTable_sort_1000_entities_add_type_after_sort(void) { } test_int(count, i + 500 + 1); - } + } + + ecs_query_fini(q); ecs_fini(world); } -void SortingEntireTable_sort_1500_entities_3_types(void) { +void OrderBy_sort_1500_entities_3_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); for (int i = 0; i < 500; i ++) { - ecs_set(world, 0, Position, {rand()}); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Mass); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1192,40 +1215,41 @@ void SortingEntireTable_sort_1500_entities_3_types(void) { test_int(count, (i + 1) * 3); } + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_2000_entities_4_types(void) { +void OrderBy_sort_2000_entities_4_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); for (int i = 0; i < 500; i ++) { - ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Mass); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); ecs_add(world, e, Mass); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1239,33 +1263,35 @@ void SortingEntireTable_sort_2000_entities_4_types(void) { test_int(count, (i + 1) * 4); } + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_shared_component(void) { +void OrderBy_sort_shared_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base_1 = ecs_set(world, 0, Position, {0, 0}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t base_3 = ecs_set(world, 0, Position, {7, 0}); + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {0, 0})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t base_3 = ecs_insert(world, ecs_value(Position, {7, 0})); - ecs_entity_t e1 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); ecs_entity_t e6 = ecs_new_w_pair(world, EcsIsA, base_3); ecs_entity_t e7 = ecs_new_w_pair(world, EcsIsA, base_2); ecs_entity_t e8 = ecs_new_w_pair(world, EcsIsA, base_1); ecs_entity_t e9 = ecs_new_w_pair(world, EcsIsA, base_1); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position(self|up)", - .filter.instanced = true, - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self|up IsA)", + .order_by = ecs_id(Position), + .order_by_callback = compare_position, }); ecs_iter_t it = ecs_query_iter(world, q); @@ -1301,31 +1327,95 @@ void SortingEntireTable_sort_shared_component(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_2_entities_2_types(void) { +void OrderBy_sort_shared_component_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {0, 0})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t base_3 = ecs_insert(world, ecs_value(Position, {7, 0})); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); + ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, base_3); + ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, base_2); + ecs_entity_t e8 = ecs_new_w_pair(world, EcsChildOf, base_1); + ecs_entity_t e9 = ecs_new_w_pair(world, EcsChildOf, base_1); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self|up ChildOf)", + .order_by = ecs_id(Position), + .order_by_callback = compare_position, + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.entities[0] == base_1); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 2); + test_assert(it.entities[0] == e8); + test_assert(it.entities[1] == e9); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 3); + test_assert(it.entities[0] == e2); + test_assert(it.entities[1] == e4); + test_assert(it.entities[2] == base_2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.entities[0] == e7); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 4); + test_assert(it.entities[0] == e5); + test_assert(it.entities[1] == e3); + test_assert(it.entities[2] == e1); + test_assert(it.entities[3] == base_3); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_assert(it.entities[0] == e6); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void OrderBy_sort_2_entities_2_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); - ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1338,35 +1428,36 @@ void SortingEntireTable_sort_2_entities_2_types(void) { test_int(count, 2); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_3_entities_3_types(void) { +void OrderBy_sort_3_entities_3_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); - ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Mass); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1379,25 +1470,26 @@ void SortingEntireTable_sort_3_entities_3_types(void) { test_int(count, 3); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_3_entities_3_types_2(void) { +void OrderBy_sort_3_entities_3_types_2(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ECS_TAG(world, Foo); ECS_TAG(world, Bar); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Tag", - .order_by = compare_entity, - .sort_table = PositionSortByEntity + ecs_query_t *q = ecs_query(world, { + .expr = "Tag", + .order_by_callback = compare_entity }); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); ecs_add_id(world, e1, Foo); ecs_add_id(world, e2, Bar); @@ -1418,39 +1510,40 @@ void SortingEntireTable_sort_3_entities_3_types_2(void) { test_int(count, 3); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_4_entities_4_types(void) { +void OrderBy_sort_4_entities_4_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = ecs_compare(Position), - .sort_table = ecs_sort_table(Position) + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position }); - ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Mass); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); ecs_add(world, e, Mass); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1463,21 +1556,22 @@ void SortingEntireTable_sort_4_entities_4_types(void) { test_int(count, 4); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_w_tags_only(void) { +void OrderBy_sort_w_tags_only(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{Tag}}, - .order_by = compare_entity, - .sort_table = PositionSortByEntity + ecs_query_t *q = ecs_query(world, { + .terms = {{Tag}}, + .order_by_callback = compare_entity }); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, root); ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, root); @@ -1491,21 +1585,22 @@ void SortingEntireTable_sort_w_tags_only(void) { test_int(it.entities[1], e2); ecs_iter_fini(&it); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_childof_marked(void) { +void OrderBy_sort_childof_marked(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{Tag}}, - .order_by = compare_entity, - .sort_table = PositionSortByEntity + ecs_query_t *q = ecs_query(world, { + .terms = {{Tag}}, + .order_by_callback = compare_entity }); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, root); ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, root); @@ -1524,21 +1619,23 @@ void SortingEntireTable_sort_childof_marked(void) { test_assert(!ecs_is_alive(world, e2)); test_assert(!ecs_is_alive(world, e3)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_isa_marked(void) { +void OrderBy_sort_isa_marked(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{Tag}}, - .order_by = compare_entity, - .sort_table = PositionSortByEntity + ecs_query_t *q = ecs_query(world, { + .terms = {{Tag}}, + .order_by_callback = compare_entity }); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, root); ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, root); @@ -1548,9 +1645,8 @@ void SortingEntireTable_sort_isa_marked(void) { // Trigger sorting ecs_iter_t it = ecs_query_iter(world, q); - ecs_iter_fini(&it); - ecs_delete(world, root); + ecs_iter_fini(&it); test_assert(!ecs_is_alive(world, root)); test_int(1, ecs_get_type(world, e1)->count); @@ -1560,22 +1656,23 @@ void SortingEntireTable_sort_isa_marked(void) { test_int(1, ecs_get_type(world, e3)->count); test_assert(ecs_has(world, e3, Tag)); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_sort_relation_marked(void) { +void OrderBy_sort_relation_marked(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ECS_TAG(world, Rel); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{Tag}}, - .order_by = compare_entity, - .sort_table = PositionSortByEntity + ecs_query_t *q = ecs_query(world, { + .terms = {{Tag}}, + .order_by_callback = compare_entity }); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, Rel, root); ecs_entity_t e2 = ecs_new_w_pair(world, Rel, e1); ecs_entity_t e3 = ecs_new_w_pair(world, Rel, root); @@ -1597,6 +1694,8 @@ void SortingEntireTable_sort_relation_marked(void) { test_int(1, ecs_get_type(world, e3)->count); test_assert(ecs_has(world, e3, Tag)); + ecs_query_fini(q); + ecs_fini(world); } @@ -1613,23 +1712,23 @@ int dummy_compare( return (e1 > e2) - (e1 < e2); } -void SortingEntireTable_dont_resort_after_set_unsorted_component(void) { +void OrderBy_dont_resort_after_set_unsorted_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "[in] Position", - .order_by_component = ecs_id(Position), - .order_by = dummy_compare + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .order_by = ecs_id(Position), + .order_by_callback = dummy_compare }); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Position, {0, 0}); ecs_set(world, e1, Velocity, {0, 0}); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Position, {0, 0}); ecs_set(world, e2, Velocity, {0, 0}); @@ -1656,10 +1755,12 @@ void SortingEntireTable_dont_resort_after_set_unsorted_component(void) { test_bool(dummy_compare_invoked, true); ecs_iter_fini(&it); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag(void) { +void OrderBy_dont_resort_after_set_unsorted_component_w_tag(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Velocity); @@ -1674,10 +1775,10 @@ void SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag(void) { test_assert(Tag < ecs_id(Position)); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "[in] Position", - .order_by_component = ecs_id(Position), - .order_by = dummy_compare + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .order_by = ecs_id(Position), + .order_by_callback = dummy_compare }); ecs_entity_t e1 = ecs_new_w_id(world, Tag); @@ -1711,10 +1812,12 @@ void SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag(void) { test_bool(dummy_compare_invoked, true); ecs_iter_fini(&it); + ecs_query_fini(q); + ecs_fini(world); } -void SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void) { +void OrderBy_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Velocity); @@ -1728,16 +1831,16 @@ void SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag_w_out_ter test_assert(Tag < ecs_id(Position)); // Sorted query - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "[in] Position", - .order_by_component = ecs_id(Position), - .order_by = dummy_compare + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .order_by = ecs_id(Position), + .order_by_callback = dummy_compare }); // Dummy queries that mutate - ecs_query_t *q_a = ecs_query_new(world, "Position"); // [inout] - ecs_query_t *q_b = ecs_query_new(world, "[out] Position"); - ecs_query_t *q_c = ecs_query_new(world, "[out] Velocity"); + ecs_query_t *q_a = ecs_query(world, { .expr = "Position" }); // [inout] + ecs_query_t *q_b = ecs_query(world, { .expr = "[out] Position" }); + ecs_query_t *q_c = ecs_query(world, { .expr = "[out] Velocity" }); ecs_entity_t e1 = ecs_new_w_id(world, Tag); ecs_set(world, e1, Position, {0, 0}); @@ -1754,15 +1857,15 @@ void SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag_w_out_ter dummy_compare_invoked = false; // No changes, shouldn't sort - itq = ecs_query_iter(world, q); + ecs_iter_t it = ecs_query_iter(world, q); test_bool(dummy_compare_invoked, false); - ecs_iter_fini(&itq); + ecs_iter_fini(&it); // No change in sorted component, shouldn't sort { ecs_iter_t it = ecs_query_iter(world, q_c); while (ecs_query_next(&it)); } - itq = ecs_query_iter(world, q); + it = ecs_query_iter(world, q); test_bool(dummy_compare_invoked, false); - ecs_iter_fini(&itq); + ecs_iter_fini(&it); // Change in sorted component (inout), should sort { ecs_iter_t it = ecs_query_iter(world, q_a); while (ecs_query_next(&it)); } @@ -1773,10 +1876,299 @@ void SortingEntireTable_dont_resort_after_set_unsorted_component_w_tag_w_out_ter // Change in sorted component (out), should sort { ecs_iter_t it = ecs_query_iter(world, q_b); while (ecs_query_next(&it)); } - itq = ecs_query_iter(world, q); + it = ecs_query_iter(world, q); test_bool(dummy_compare_invoked, true); dummy_compare_invoked = false; - ecs_iter_fini(&itq); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_query_fini(q_a); + ecs_query_fini(q_b); + ecs_query_fini(q_c); + + ecs_fini(world); +} + +void OrderBy_sort_component_not_queried_for(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Velocity), + .order_by_callback = compare_position + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void OrderBy_sort_by_wildcard(void) { + install_test_abort(); + + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + test_expect_abort(); + ecs_query(world, { + .expr = "(Position, *)", + .order_by = ecs_pair(ecs_id(Position), EcsWildcard), + .order_by_callback = compare_position + }); +} + +void Sorting_sort_shared_w_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsIn } + }, + .order_by_callback = compare_position, + .order_by = ecs_id(Position) + }); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {0, 0})); + ecs_add_id(world, base, EcsPrefab); + + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e5 = ecs_new_w_pair(world, EcsIsA, base); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(5, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_uint(e4, it.entities[3]); + test_uint(e5, it.entities[4]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[4]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void OrderBy_sort_not_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "!Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void OrderBy_sort_or_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "Position || Velocity", + .order_by = ecs_id(Position), + .order_by_callback = compare_position + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void OrderBy_sort_optional_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "?Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void OrderBy_sort_shared_w_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsIn } + }, + .order_by_callback = compare_position, + .order_by = ecs_id(Position) + }); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {0, 0})); + ecs_add_id(world, base, EcsPrefab); + + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e5 = ecs_new_w_pair(world, EcsIsA, base); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(5, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_uint(e4, it.entities[3]); + test_uint(e5, it.entities[4]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[4]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_fini(world); +} + +static int ctor_invoked = 0; +static int move_invoked = 0; +static int dtor_invoked = 0; + +ECS_CTOR(Velocity, ptr, { + ctor_invoked ++; +}) + +ECS_MOVE(Velocity, dst, src, { + *dst = *src; + move_invoked ++; +}) + +ECS_DTOR(Velocity, ptr, { + dtor_invoked ++; +}) + +void OrderBy_sort_w_nontrivial_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_set_hooks(world, Velocity, { + .ctor = ecs_ctor(Velocity), + .move = ecs_move(Velocity), + .dtor = ecs_dtor(Velocity) + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); + + ecs_set(world, e1, Velocity, {0, 3}); + ecs_set(world, e2, Velocity, {0, 1}); + ecs_set(world, e3, Velocity, {0, 5}); + ecs_set(world, e4, Velocity, {0, 2}); + ecs_set(world, e5, Velocity, {0, 4}); + + ctor_invoked = 0; + move_invoked = 0; + dtor_invoked = 0; + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position + }); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 5); + + test_assert(it.entities[0] == e2); + test_assert(it.entities[1] == e4); + test_assert(it.entities[2] == e1); + test_assert(it.entities[3] == e5); + test_assert(it.entities[4] == e3); + + test_assert(!ecs_query_next(&it)); + + { + const Velocity *v = ecs_get(world, e1, Velocity); + test_int(v->x, 0); test_int(v->y, 3); + } + { + const Velocity *v = ecs_get(world, e2, Velocity); + test_int(v->x, 0); test_int(v->y, 1); + } + { + const Velocity *v = ecs_get(world, e3, Velocity); + test_int(v->x, 0); test_int(v->y, 5); + } + { + const Velocity *v = ecs_get(world, e4, Velocity); + test_int(v->x, 0); test_int(v->y, 2); + } + { + const Velocity *v = ecs_get(world, e5, Velocity); + test_int(v->x, 0); test_int(v->y, 4); + } + + test_int(ctor_invoked, 4); + test_int(move_invoked, 12); + test_int(dtor_invoked, 4); ecs_fini(world); } diff --git a/vendors/flecs/test/api/src/Sorting.c b/vendors/flecs/test/query/src/OrderByEntireTable.c similarity index 59% rename from vendors/flecs/test/api/src/Sorting.c rename to vendors/flecs/test/query/src/OrderByEntireTable.c index ec619e4ec..62c53e0c8 100644 --- a/vendors/flecs/test/api/src/Sorting.c +++ b/vendors/flecs/test/query/src/OrderByEntireTable.c @@ -1,43 +1,42 @@ -#include +#include #include static -int compare_position( +int compare_entity( ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2) { + return (e1 > e2) - (e1 < e2); +} + +static ECS_COMPARE(Position, { const Position *p1 = ptr1; const Position *p2 = ptr2; return (p1->x > p2->x) - (p1->x < p2->x); -} +}) -static -int compare_entity( - ecs_entity_t e1, - const void *ptr1, - ecs_entity_t e2, - const void *ptr2) -{ - return (e1 > e2) - (e1 < e2); -} +ECS_SORT_TABLE(Position) + +ECS_SORT_TABLE_WITH_COMPARE(Position, PositionSortByEntity, compare_entity) -void Sorting_sort_by_component(void) { +void OrderByEntireTable_sort_by_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -53,24 +52,27 @@ void Sorting_sort_by_component(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_by_component_same_value_1(void) { +void OrderByEntireTable_sort_by_component_same_value_1(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {1, 0}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {1, 0})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -87,25 +89,28 @@ void Sorting_sort_by_component_same_value_1(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_by_component_same_value_2(void) { +void OrderByEntireTable_sort_by_component_same_value_2(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e6 = ecs_set(world, 0, Position, {1, 0}); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e6 = ecs_insert(world, ecs_value(Position, {1, 0})); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -122,29 +127,32 @@ void Sorting_sort_by_component_same_value_2(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_by_component_2_tables(void) { +void OrderByEntireTable_sort_by_component_2_tables(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); ecs_add(world, e3, Velocity); ecs_add(world, e4, Velocity); ecs_add(world, e5, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -168,32 +176,35 @@ void Sorting_sort_by_component_2_tables(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_by_component_3_tables(void) { +void OrderByEntireTable_sort_by_component_3_tables(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); - ecs_entity_t e6 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e7 = ecs_set(world, 0, Position, {7, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); + ecs_entity_t e6 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e7 = ecs_insert(world, ecs_value(Position, {7, 0})); ecs_add(world, e5, Velocity); ecs_add(world, e6, Mass); ecs_add(world, e7, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -222,29 +233,32 @@ void Sorting_sort_by_component_3_tables(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_by_entity(void) { +void OrderByEntireTable_sort_by_entity(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); ecs_add(world, e2, Velocity); ecs_add(world, e4, Velocity); ecs_add(world, e5, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by = compare_entity + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by_callback = compare_entity, + .order_by_table_callback = PositionSortByEntity }); ecs_iter_t it = ecs_query_iter(world, q); @@ -268,21 +282,23 @@ void Sorting_sort_by_entity(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_after_add(void) { +void OrderByEntireTable_sort_after_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 0})); ecs_add(world, e3, Velocity); ecs_add(world, e4, Velocity); @@ -290,10 +306,11 @@ void Sorting_sort_after_add(void) { ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, Velocity", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -344,23 +361,25 @@ void Sorting_sort_after_add(void) { test_int(it.count, 1); test_assert(it.entities[0] == e3); - test_assert(!ecs_query_next(&it)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); ecs_fini(world); } -void Sorting_sort_after_remove(void) { +void OrderByEntireTable_sort_after_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 0})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -372,10 +391,11 @@ void Sorting_sort_after_remove(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, Velocity", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -446,21 +466,23 @@ void Sorting_sort_after_remove(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_after_delete(void) { +void OrderByEntireTable_sort_after_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 0})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -472,10 +494,11 @@ void Sorting_sort_after_delete(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, Velocity", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -530,21 +553,23 @@ void Sorting_sort_after_delete(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_after_set(void) { +void OrderByEntireTable_sort_after_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 0})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -556,10 +581,11 @@ void Sorting_sort_after_set(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, Velocity", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -601,14 +627,16 @@ void Sorting_sort_after_set(void) { test_int(it.count, 1); test_assert(it.entities[0] == e1); - test_assert(!ecs_query_next(&it)); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); ecs_fini(world); } static void FlipP(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); int32_t i; for (i = 0; i < it->count; i ++) { @@ -618,7 +646,7 @@ void FlipP(ecs_iter_t *it) { } } -void Sorting_sort_after_system(void) { +void OrderByEntireTable_sort_after_system(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Velocity); @@ -627,11 +655,11 @@ void Sorting_sort_after_system(void) { ECS_SYSTEM(world, FlipP, EcsOnUpdate, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 5}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 6}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 1}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 3}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 5})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 6})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 1})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 3})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -643,10 +671,11 @@ void Sorting_sort_after_system(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, [in] Velocity", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position, [in] Velocity", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -712,21 +741,23 @@ void Sorting_sort_after_system(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_after_query(void) { +void OrderByEntireTable_sort_after_query(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Mass); - ecs_entity_t e1 = ecs_set(world, 0, Position, {3, 5}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 2}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {6, 6}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 1}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {5, 3}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 5})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {6, 6})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 1})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {5, 3})); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); @@ -738,11 +769,12 @@ void Sorting_sort_after_query(void) { ecs_add(world, e2, Mass); ecs_add(world, e3, Mass); - ecs_query_t *flip_q = ecs_query_new(world, "Position"); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position, [in] Velocity", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *flip_q = ecs_query(world, { .expr = "Position" }); + ecs_query_t *q = ecs_query(world, { + .expr = "Position, [in] Velocity", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -816,23 +848,27 @@ void Sorting_sort_after_query(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_query_fini(flip_q); + ecs_fini(world); } -void Sorting_sort_by_component_move_pivot(void) { +void OrderByEntireTable_sort_by_component_move_pivot(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_entity_t e1 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {10, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {1, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {1, 0})); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); ecs_iter_t it = ecs_query_iter(world, q); @@ -847,29 +883,31 @@ void Sorting_sort_by_component_move_pivot(void) { test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_1000_entities(void) { +void OrderByEntireTable_sort_1000_entities(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position) }); for (int i = 0; i < 1000; i ++) { int32_t v = rand(); - ecs_set(world, 0, Position, {v}); + ecs_insert(world, ecs_value(Position, {v})); int32_t x = 0; ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(it.count == (i + 1)); @@ -881,30 +919,32 @@ void Sorting_sort_1000_entities(void) { } } + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_1000_entities_w_duplicates(void) { +void OrderByEntireTable_sort_1000_entities_w_duplicates(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position) }); for (int i = 0; i < 500; i ++) { int32_t v = rand(); - ecs_set(world, 0, Position, {v}); - ecs_set(world, 0, Position, {v}); + ecs_insert(world, ecs_value(Position, {v})); + ecs_insert(world, ecs_value(Position, {v})); int32_t x = 0; ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(it.count == (i + 1) * 2); @@ -916,30 +956,33 @@ void Sorting_sort_1000_entities_w_duplicates(void) { } } + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_1000_entities_again(void) { +void OrderByEntireTable_sort_1000_entities_again(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); - ecs_entity_t start = ecs_new(world, 0); + ecs_entity_t start = ecs_new(world); for (int i = 0; i < 1000; i ++) { int32_t v = rand(); - ecs_ensure(world, i + start); + ecs_make_alive(world, i + start); ecs_set(world, i + start, Position, {v}); ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(it.count == (i + 1)); @@ -960,7 +1003,7 @@ void Sorting_sort_1000_entities_again(void) { ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); test_assert(it.count == 1000); @@ -969,34 +1012,36 @@ void Sorting_sort_1000_entities_again(void) { test_assert(x <= p[j].x); x = p[j].x; } - } + } + ecs_query_fini(q); ecs_fini(world); } -void Sorting_sort_1000_entities_2_types(void) { +void OrderByEntireTable_sort_1000_entities_2_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); for (int i = 0; i < 500; i ++) { int32_t v = rand(); - ecs_set(world, 0, Position, {v}); - ecs_entity_t e = ecs_set(world, 0, Position, {v}); + ecs_insert(world, ecs_value(Position, {v})); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {v})); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1010,26 +1055,29 @@ void Sorting_sort_1000_entities_2_types(void) { test_int(count, (i + 1) * 2); } + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_1000_entities_2_types_again(void) { +void OrderByEntireTable_sort_1000_entities_2_types_again(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); - ecs_entity_t start = ecs_new(world, 0); + ecs_entity_t start = ecs_new(world); for (int i = 0; i < 1000; i ++) { int32_t v = rand(); - ecs_ensure(world, i + start); + ecs_make_alive(world, i + start); ecs_set(world, i + start, Position, {v}); if (!(i % 2)) { @@ -1038,7 +1086,7 @@ void Sorting_sort_1000_entities_2_types_again(void) { ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); int32_t j, x = 0; for (j = 0; j < it.count; j ++) { @@ -1056,7 +1104,7 @@ void Sorting_sort_1000_entities_2_types_again(void) { ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1069,32 +1117,35 @@ void Sorting_sort_1000_entities_2_types_again(void) { test_int(count, 1000); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_1000_entities_add_type_after_sort(void) { +void OrderByEntireTable_sort_1000_entities_add_type_after_sort(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); - ecs_entity_t start = ecs_new(world, 0); + ecs_entity_t start = ecs_new(world); for (int i = 0; i < 500; i ++) { int32_t v = rand(); - ecs_ensure(world, i + start); + ecs_make_alive(world, i + start); ecs_set(world, i + start, Position, {v}); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1115,14 +1166,14 @@ void Sorting_sort_1000_entities_add_type_after_sort(void) { for (int i = 0; i < 500; i ++) { int32_t v = rand(); - ecs_ensure(world, i + start + 500); + ecs_make_alive(world, i + start + 500); ecs_set(world, i + start + 500, Position, {v}); ecs_add(world, i + start + 500, Velocity); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1134,36 +1185,39 @@ void Sorting_sort_1000_entities_add_type_after_sort(void) { } test_int(count, i + 500 + 1); - } + } + + ecs_query_fini(q); ecs_fini(world); } -void Sorting_sort_1500_entities_3_types(void) { +void OrderByEntireTable_sort_1500_entities_3_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); for (int i = 0; i < 500; i ++) { - ecs_set(world, 0, Position, {rand()}); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Mass); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1177,39 +1231,42 @@ void Sorting_sort_1500_entities_3_types(void) { test_int(count, (i + 1) * 3); } + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_2000_entities_4_types(void) { +void OrderByEntireTable_sort_2000_entities_4_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); for (int i = 0; i < 500; i ++) { - ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Mass); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); ecs_add(world, e, Mass); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0, x = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1223,33 +1280,35 @@ void Sorting_sort_2000_entities_4_types(void) { test_int(count, (i + 1) * 4); } + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_shared_component(void) { +void OrderByEntireTable_sort_shared_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); - ecs_entity_t base_1 = ecs_set(world, 0, Position, {0, 0}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t base_3 = ecs_set(world, 0, Position, {7, 0}); + ecs_entity_t base_1 = ecs_insert(world, ecs_value(Position, {0, 0})); + ecs_entity_t base_2 = ecs_insert(world, ecs_value(Position, {3, 0})); + ecs_entity_t base_3 = ecs_insert(world, ecs_value(Position, {7, 0})); - ecs_entity_t e1 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {6, 0})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {1, 0})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {5, 0})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {2, 0})); + ecs_entity_t e5 = ecs_insert(world, ecs_value(Position, {4, 0})); ecs_entity_t e6 = ecs_new_w_pair(world, EcsIsA, base_3); ecs_entity_t e7 = ecs_new_w_pair(world, EcsIsA, base_2); ecs_entity_t e8 = ecs_new_w_pair(world, EcsIsA, base_1); ecs_entity_t e9 = ecs_new_w_pair(world, EcsIsA, base_1); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position(self|up)", - .filter.instanced = true, - .order_by_component = ecs_id(Position), - .order_by = compare_position, + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self|up IsA)", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), }); ecs_iter_t it = ecs_query_iter(world, q); @@ -1285,92 +1344,33 @@ void Sorting_sort_shared_component(void) { test_assert(!ecs_query_next(&it)); - ecs_fini(world); -} - -void Sorting_sort_shared_component_childof(void) { - ecs_world_t *world = ecs_mini(); - - ECS_COMPONENT(world, Position); - - ecs_entity_t base_1 = ecs_set(world, 0, Position, {0, 0}); - ecs_entity_t base_2 = ecs_set(world, 0, Position, {3, 0}); - ecs_entity_t base_3 = ecs_set(world, 0, Position, {7, 0}); - - ecs_entity_t e1 = ecs_set(world, 0, Position, {6, 0}); - ecs_entity_t e2 = ecs_set(world, 0, Position, {1, 0}); - ecs_entity_t e3 = ecs_set(world, 0, Position, {5, 0}); - ecs_entity_t e4 = ecs_set(world, 0, Position, {2, 0}); - ecs_entity_t e5 = ecs_set(world, 0, Position, {4, 0}); - ecs_entity_t e6 = ecs_new_w_pair(world, EcsChildOf, base_3); - ecs_entity_t e7 = ecs_new_w_pair(world, EcsChildOf, base_2); - ecs_entity_t e8 = ecs_new_w_pair(world, EcsChildOf, base_1); - ecs_entity_t e9 = ecs_new_w_pair(world, EcsChildOf, base_1); - - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position(self|up(ChildOf))", - .filter.instanced = true, - .order_by_component = ecs_id(Position), - .order_by = compare_position, - }); - - ecs_iter_t it = ecs_query_iter(world, q); - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_assert(it.entities[0] == base_1); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 2); - test_assert(it.entities[0] == e8); - test_assert(it.entities[1] == e9); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 3); - test_assert(it.entities[0] == e2); - test_assert(it.entities[1] == e4); - test_assert(it.entities[2] == base_2); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_assert(it.entities[0] == e7); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 4); - test_assert(it.entities[0] == e5); - test_assert(it.entities[1] == e3); - test_assert(it.entities[2] == e1); - test_assert(it.entities[3] == base_3); - - test_assert(ecs_query_next(&it)); - test_int(it.count, 1); - test_assert(it.entities[0] == e6); - - test_assert(!ecs_query_next(&it)); + ecs_query_fini(q); ecs_fini(world); } -void Sorting_sort_2_entities_2_types(void) { +void OrderByEntireTable_sort_2_entities_2_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); - ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1383,34 +1383,37 @@ void Sorting_sort_2_entities_2_types(void) { test_int(count, 2); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_3_entities_3_types(void) { +void OrderByEntireTable_sort_3_entities_3_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); - ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Mass); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1423,24 +1426,27 @@ void Sorting_sort_3_entities_3_types(void) { test_int(count, 3); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_3_entities_3_types_2(void) { +void OrderByEntireTable_sort_3_entities_3_types_2(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ECS_TAG(world, Foo); ECS_TAG(world, Bar); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Tag", - .order_by = compare_entity + ecs_query_t *q = ecs_query(world, { + .expr = "Tag", + .order_by_callback = compare_entity, + .order_by_table_callback = PositionSortByEntity }); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); ecs_add_id(world, e1, Foo); ecs_add_id(world, e2, Bar); @@ -1461,38 +1467,41 @@ void Sorting_sort_3_entities_3_types_2(void) { test_int(count, 3); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_4_entities_4_types(void) { +void OrderByEntireTable_sort_4_entities_4_types(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Position), - .order_by = compare_position + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); - ecs_set(world, 0, Position, {rand()}); + ecs_insert(world, ecs_value(Position, {rand()})); - ecs_entity_t e = ecs_set(world, 0, Position, {rand()}); + ecs_entity_t e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Mass); - e = ecs_set(world, 0, Position, {rand()}); + e = ecs_insert(world, ecs_value(Position, {rand()})); ecs_add(world, e, Velocity); ecs_add(world, e, Mass); ecs_iter_t it = ecs_query_iter(world, q); int32_t count = 0; while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); count += it.count; @@ -1505,20 +1514,23 @@ void Sorting_sort_4_entities_4_types(void) { test_int(count, 4); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_w_tags_only(void) { +void OrderByEntireTable_sort_w_tags_only(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{Tag}}, - .order_by = compare_entity + ecs_query_t *q = ecs_query(world, { + .terms = {{Tag}}, + .order_by_callback = compare_entity, + .order_by_table_callback = PositionSortByEntity }); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, root); ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, root); @@ -1532,20 +1544,23 @@ void Sorting_sort_w_tags_only(void) { test_int(it.entities[1], e2); ecs_iter_fini(&it); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_childof_marked(void) { +void OrderByEntireTable_sort_childof_marked(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{Tag}}, - .order_by = compare_entity + ecs_query_t *q = ecs_query(world, { + .terms = {{Tag}}, + .order_by_callback = compare_entity, + .order_by_table_callback = PositionSortByEntity }); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, root); ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, e1); ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, root); @@ -1564,20 +1579,24 @@ void Sorting_sort_childof_marked(void) { test_assert(!ecs_is_alive(world, e2)); test_assert(!ecs_is_alive(world, e3)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_isa_marked(void) { +void OrderByEntireTable_sort_isa_marked(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{Tag}}, - .order_by = compare_entity + ecs_query_t *q = ecs_query(world, { + .terms = {{Tag}}, + .order_by_callback = compare_entity, + .order_by_table_callback = PositionSortByEntity }); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, root); ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, e1); ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, root); @@ -1587,9 +1606,10 @@ void Sorting_sort_isa_marked(void) { // Trigger sorting ecs_iter_t it = ecs_query_iter(world, q); - ecs_delete(world, root); ecs_iter_fini(&it); + ecs_delete(world, root); + test_assert(!ecs_is_alive(world, root)); test_int(1, ecs_get_type(world, e1)->count); test_assert(ecs_has(world, e1, Tag)); @@ -1598,21 +1618,24 @@ void Sorting_sort_isa_marked(void) { test_int(1, ecs_get_type(world, e3)->count); test_assert(ecs_has(world, e3, Tag)); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_sort_relation_marked(void) { +void OrderByEntireTable_sort_relation_marked(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ECS_TAG(world, Rel); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{Tag}}, - .order_by = compare_entity + ecs_query_t *q = ecs_query(world, { + .terms = {{Tag}}, + .order_by_callback = compare_entity, + .order_by_table_callback = PositionSortByEntity }); - ecs_entity_t root = ecs_new_id(world); + ecs_entity_t root = ecs_new(world); ecs_entity_t e1 = ecs_new_w_pair(world, Rel, root); ecs_entity_t e2 = ecs_new_w_pair(world, Rel, e1); ecs_entity_t e3 = ecs_new_w_pair(world, Rel, root); @@ -1634,6 +1657,8 @@ void Sorting_sort_relation_marked(void) { test_int(1, ecs_get_type(world, e3)->count); test_assert(ecs_has(world, e3, Tag)); + ecs_query_fini(q); + ecs_fini(world); } @@ -1650,23 +1675,23 @@ int dummy_compare( return (e1 > e2) - (e1 < e2); } -void Sorting_dont_resort_after_set_unsorted_component(void) { +void OrderByEntireTable_dont_resort_after_set_unsorted_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "[in] Position", - .order_by_component = ecs_id(Position), - .order_by = dummy_compare + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .order_by = ecs_id(Position), + .order_by_callback = dummy_compare }); - ecs_entity_t e1 = ecs_new(world, 0); + ecs_entity_t e1 = ecs_new(world); ecs_set(world, e1, Position, {0, 0}); ecs_set(world, e1, Velocity, {0, 0}); - ecs_entity_t e2 = ecs_new(world, 0); + ecs_entity_t e2 = ecs_new(world); ecs_set(world, e2, Position, {0, 0}); ecs_set(world, e2, Velocity, {0, 0}); @@ -1683,20 +1708,22 @@ void Sorting_dont_resort_after_set_unsorted_component(void) { // No change in sorted component, shouldn't sort ecs_modified(world, e2, Velocity); - ecs_query_iter(world, q); + it = ecs_query_iter(world, q); test_bool(dummy_compare_invoked, false); ecs_iter_fini(&it); // Change in sorted component, should sort ecs_modified(world, e2, Position); - ecs_query_iter(world, q); + it = ecs_query_iter(world, q); test_bool(dummy_compare_invoked, true); ecs_iter_fini(&it); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_dont_resort_after_set_unsorted_component_w_tag(void) { +void OrderByEntireTable_dont_resort_after_set_unsorted_component_w_tag(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Velocity); @@ -1711,10 +1738,10 @@ void Sorting_dont_resort_after_set_unsorted_component_w_tag(void) { test_assert(Tag < ecs_id(Position)); - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "[in] Position", - .order_by_component = ecs_id(Position), - .order_by = dummy_compare + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .order_by = ecs_id(Position), + .order_by_callback = dummy_compare }); ecs_entity_t e1 = ecs_new_w_id(world, Tag); @@ -1748,10 +1775,12 @@ void Sorting_dont_resort_after_set_unsorted_component_w_tag(void) { test_bool(dummy_compare_invoked, true); ecs_iter_fini(&it); + ecs_query_fini(q); + ecs_fini(world); } -void Sorting_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void) { +void OrderByEntireTable_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Velocity); @@ -1765,16 +1794,16 @@ void Sorting_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void) { test_assert(Tag < ecs_id(Position)); // Sorted query - ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "[in] Position", - .order_by_component = ecs_id(Position), - .order_by = dummy_compare + ecs_query_t *q = ecs_query(world, { + .expr = "[in] Position", + .order_by = ecs_id(Position), + .order_by_callback = dummy_compare, }); // Dummy queries that mutate - ecs_query_t *q_a = ecs_query_new(world, "Position"); // [inout] - ecs_query_t *q_b = ecs_query_new(world, "[out] Position"); - ecs_query_t *q_c = ecs_query_new(world, "[out] Velocity"); + ecs_query_t *q_a = ecs_query(world, { .expr = "Position" }); // [inou }t] + ecs_query_t *q_b = ecs_query(world, { .expr = "[out] Position" }); + ecs_query_t *q_c = ecs_query(world, { .expr = "[out] Velocity" }); ecs_entity_t e1 = ecs_new_w_id(world, Tag); ecs_set(world, e1, Position, {0, 0}); @@ -1791,15 +1820,15 @@ void Sorting_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void) { dummy_compare_invoked = false; // No changes, shouldn't sort - ecs_iter_t it = ecs_query_iter(world, q); + itq = ecs_query_iter(world, q); test_bool(dummy_compare_invoked, false); - ecs_iter_fini(&it); + ecs_iter_fini(&itq); // No change in sorted component, shouldn't sort { ecs_iter_t it = ecs_query_iter(world, q_c); while (ecs_query_next(&it)); } - it = ecs_query_iter(world, q); + itq = ecs_query_iter(world, q); test_bool(dummy_compare_invoked, false); - ecs_iter_fini(&it); + ecs_iter_fini(&itq); // Change in sorted component (inout), should sort { ecs_iter_t it = ecs_query_iter(world, q_a); while (ecs_query_next(&it)); } @@ -1810,41 +1839,174 @@ void Sorting_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void) { // Change in sorted component (out), should sort { ecs_iter_t it = ecs_query_iter(world, q_b); while (ecs_query_next(&it)); } - it = ecs_query_iter(world, q); + itq = ecs_query_iter(world, q); test_bool(dummy_compare_invoked, true); dummy_compare_invoked = false; - ecs_iter_fini(&it); + ecs_iter_fini(&itq); + + ecs_query_fini(q); + ecs_query_fini(q_a); + ecs_query_fini(q_b); + ecs_query_fini(q_c); ecs_fini(world); } -void Sorting_sort_component_not_queried_for(void) { - install_test_abort(); +void OrderByEntireTable_sort_not_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "!Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) + }); + + test_assert(q == NULL); + ecs_fini(world); +} + +void OrderByEntireTable_sort_or_term(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); - test_expect_abort(); - ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "Position", - .order_by_component = ecs_id(Velocity), - .order_by = compare_position + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "Position || Velocity", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); + + test_assert(q == NULL); + + ecs_fini(world); } -void Sorting_sort_by_wildcard(void) { - install_test_abort(); +void OrderByEntireTable_sort_optional_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "?Position", + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) + }); + + test_assert(q == NULL); + + ecs_fini(world); +} +void SortingEntireTable_sort_shared_w_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsIn } + }, + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) + }); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {0, 0})); + ecs_add_id(world, base, EcsPrefab); + + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e5 = ecs_new_w_pair(world, EcsIsA, base); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(5, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_uint(e4, it.entities[3]); + test_uint(e5, it.entities[4]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); - test_expect_abort(); - ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "(Position, *)", - .order_by_component = ecs_pair(ecs_id(Position), EcsWildcard), - .order_by = compare_position + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[4]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_fini(world); +} + +void OrderByEntireTable_sort_shared_w_delete(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsIn } + }, + .order_by = ecs_id(Position), + .order_by_callback = ecs_compare(Position), + .order_by_table_callback = ecs_sort_table(Position) }); + + ecs_entity_t base = ecs_insert(world, ecs_value(Position, {0, 0})); + ecs_add_id(world, base, EcsPrefab); + + ecs_entity_t e1 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsIsA, base); + ecs_entity_t e5 = ecs_new_w_pair(world, EcsIsA, base); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(5, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_uint(e4, it.entities[3]); + test_uint(e5, it.entities[4]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[4]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_fini(world); } diff --git a/vendors/flecs/test/query/src/Parser.c b/vendors/flecs/test/query/src/Parser.c new file mode 100644 index 000000000..ec92bfdd7 --- /dev/null +++ b/vendors/flecs/test/query/src/Parser.c @@ -0,0 +1,7075 @@ +#include + +static +int term_count(ecs_query_t *q) { + test_assert(q != NULL); + return q->term_count; +} + +static +ecs_term_t* query_terms(ecs_query_t *q) { + test_assert(q != NULL); + return q->terms; +} + +static +void print_term_id_flags(ecs_id_t value) { + if (value & EcsIsEntity) { + printf("|EcsIsEntity"); + } + if (value & EcsIsVariable) { + printf("|EcsIsVariable"); + } + if (value & EcsIsName) { + printf("|EcsIsName"); + } + if (value & EcsSelf) { + printf("|EcsSelf"); + } + if (value & EcsUp) { + printf("|EcsUp"); + } + if (value & EcsCascade) { + printf("|EcsCascade"); + } + if (value & EcsDesc) { + printf("|EcsDesc"); + } +} + +#define test_id(actual, value)\ + if ((value) != (actual)) {\ + printf("actual: %u", (uint32_t)(actual));\ + print_term_id_flags(actual);\ + printf("\n");\ + printf("expected: %u", (uint32_t)(value));\ + print_term_id_flags(value);\ + printf("\n");\ + test_assert(false);\ + } + +#define test_first(column, e, flgs)\ + test_id(column.first.id, e | flgs); + +#define test_src(column, e, flgs)\ + test_id(column.src.id, e | flgs); + +#define test_second(column, e, flgs)\ + test_id(column.second.id, e | flgs); + +#define test_first_var(column, e, flgs, str)\ + test_first(column, e, flgs|EcsIsVariable);\ + test_str(column.first.name, str); + +#define test_src_var(column, e, flgs, str)\ + test_src(column, e, flgs|EcsIsVariable);\ + test_str(column.src.name, str); + +#define test_second_var(column, e, flgs, str)\ + test_second(column, e, flgs|EcsIsVariable);\ + test_str(column.second.name, str); + +void Parser_resolve_this(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_lookup(world, "."); + test_assert(e != 0); + test_assert(e == EcsThis); + + ecs_fini(world); +} + +void Parser_resolve_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_lookup(world, "*"); + test_assert(e != 0); + test_assert(e == EcsWildcard); + + ecs_fini(world); +} + +void Parser_resolve_any(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_lookup(world, "_"); + test_assert(e != 0); + test_assert(e == EcsAny); + + ecs_fini(world); +} + +void Parser_resolve_is_a(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_lookup(world, "IsA"); + test_assert(e != 0); + test_assert(e == EcsIsA); + + ecs_fini(world); +} + +void Parser_0(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "0" + }); + test_assert(q != NULL); + + test_int(term_count(q), 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_explicit_subject_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($this)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_assert(terms[0].src.name == NULL); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_explicit_subject_this_by_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(this)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_explicit_subject_this_by_var_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($this)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_explicit_subject_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(*)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_explicit_subject_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(_)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsAny, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_explicit_subject_0(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(#0)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_this_as_predicate(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "this(Subj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsThis, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_this_var_as_predicate(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "$this(Subj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_this_lowercase_var_as_predicate(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "$this(Subj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_this_as_object(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj, this)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], EcsThis, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_this_var_as_object(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj, $this)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_wildcard_pred(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(*, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_wildcard_obj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, *)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_any_pred(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(_, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsAny, EcsSelf|EcsIsVariable); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_any_obj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, _)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], EcsAny, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_this_pred(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "($this, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_this_obj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, $this)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_pred_w_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred|self, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_obj_w_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, Obj|self)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_pred_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred|up, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsUp|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_obj_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, Obj|up)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsUp|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_pred_w_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred|self|up, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_obj_w_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, Obj|self|up)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsUp|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_pred_w_invalid_flags(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred|, Obj)" + }); + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_obj_w_invalid_flags(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, Obj|)" + }); + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($this, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_this_by_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(this, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_this_by_var_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($this, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_wildcard_pred(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "*($this, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_wildcard_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(*, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_wildcard_obj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($this, *)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], EcsWildcard, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_0_name_object(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_set_name(world, 0, "0"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(ChildOf, 0)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], e, EcsIsEntity|EcsSelf); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].id, ecs_pair(EcsChildOf, e)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_0_name_not_found_object(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(ChildOf, 0)" + }); + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_0_digit_object(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(ChildOf, #0)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], 0, EcsIsEntity|EcsSelf); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].id, ecs_pair(EcsChildOf, 0)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_0_object(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "ChildOf($this, #0)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], 0, EcsIsEntity|EcsSelf); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].id, ecs_pair(EcsChildOf, 0)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_0(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(#0, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_in_component_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[in] Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_in_component_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[in] Pred(Subj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_in_pair_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[in] (Pred, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_in_pair_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[in] Pred(Subj, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_component_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_component_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] Pred(Subj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_pair_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] (Pred, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_pair_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] Pred(Subj, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_out_component_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[out] Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_out_component_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[out] Pred(Subj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_out_pair_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[out] (Pred, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_out_pair_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[out] Pred(Subj, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_filter_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[none] Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_w_not_operator(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[out] !Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsNot); + test_int(terms[0].inout, EcsOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_w_optional_operator(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[in] ?Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsOptional); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Pred, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_this_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "$this($)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_implicit_no_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred()" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_explicit_no_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(#0)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_no_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(#0, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_variable_single_char(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, X); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($X)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf, "X"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_variable_multi_char(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, XYZ); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($XYZ)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf, "XYZ"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_variable_multi_char_w_underscore(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, XY_Z); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($XY_Z)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf, "XY_Z"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_variable_multi_char_w_number(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, XY_1); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($XY_1)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf, "XY_1"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_variable_multi_char_not_allcaps(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, XYZ); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($xyZ)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf, "xyZ"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "($Pred, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first_var(terms[0], 0, EcsSelf, "Pred"); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_obj_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, $Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second_var(terms[0], 0, EcsSelf, "Obj"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "!Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsNot); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "!(Pred, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsNot); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "!Pred(Subj, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsNot); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_component_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "!Pred_1, !Pred_2" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsNot); + test_int(terms[0].inout, EcsInOutNone); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsNot); + test_int(terms[1].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_component_not_no_space(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "!Pred_1,!Pred_2" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsNot); + test_int(terms[0].inout, EcsInOutNone); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsNot); + test_int(terms[1].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_component_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "?Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsOptional); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_component_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "?Pred_1, ?Pred_2" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsOptional); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsOptional); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_component_optional_no_space(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "?Pred_1,?Pred_2" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsOptional); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsOptional); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_from_and(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "and | Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAndFrom); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_from_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "or | Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsOrFrom); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_from_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "not | Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsNotFrom); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "?(Pred, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsOptional); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "?Pred(Subj, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsOptional); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_w_role(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "toggle | Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_explicit_subject_w_role(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "toggle | Pred($this)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_no_subject_w_role(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "toggle | Pred()" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_implicit_subject_w_role(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_explicit_subject_w_role(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred($this, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_role_pred_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] toggle | Pred" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_role_pred_no_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] toggle | Pred()" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_role_pred_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] toggle | Pred($this)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_role_pair_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] (Pred, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_inout_role_pair_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] Pred($this, Obj)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOut); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_pred_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1, Pred_2" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_pred_no_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1(), Pred_2()" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], 0, EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_pred_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1($this), Pred_2($this)" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_pair_implicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + ECS_TAG(world, Obj_1); + ECS_TAG(world, Obj_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred_1, Obj_1), (Pred_2, Obj_2)" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_pair_explicit_subject(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + ECS_TAG(world, Obj_1); + ECS_TAG(world, Obj_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1($this, Obj_1), Pred_2($this, Obj_2)" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_pred_role(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "toggle | Pred_1, toggle | Pred_2" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_pair_implicit_subj_role(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + ECS_TAG(world, Obj_1); + ECS_TAG(world, Obj_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred_1, Obj_1), (Pred_2, Obj_2)" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_pair_explicit_subj_role(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + ECS_TAG(world, Obj_1); + ECS_TAG(world, Obj_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1($this, Obj_1), Pred_2($this, Obj_2)" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_or_pred_implicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1 || Pred_2" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_or_pred_explicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1($this) || Pred_2($this)" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_or_pair_implicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + ECS_TAG(world, Obj_1); + ECS_TAG(world, Obj_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(Pred_1, Obj_1) || (Pred_2, Obj_2)" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_or_pair_explicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + ECS_TAG(world, Obj_1); + ECS_TAG(world, Obj_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1($this, Obj_1) || Pred_2($this, Obj_2)" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Obj_1, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Obj_2, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_or_pred_inout(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "[inout] Pred_1 || Pred_2" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOut); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_or_no_space(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1||Pred_2" + }); + test_assert(q != NULL); + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_or_w_not_1st_arg(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "!Pred_1 || Pred_2" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_2_or_w_not_2nd_arg(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1 || !Pred_2" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_2_or_w_optional_1st_arg(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "?Pred_1 || Pred_2" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_2_or_w_optional_2nd_arg(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1 || ?Pred_2" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_1_entity_id_pred_implicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ecs_make_alive(world, 100); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "#100" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], 100, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_1_entity_id_pred_no_subj(void) { + ecs_world_t *world = ecs_mini(); + + ecs_make_alive(world, 100); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "#100()" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], 100, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_1_entity_id_pred_explicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ecs_make_alive(world, 100); + ecs_make_alive(world, 200); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "#100(#200)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], 100, EcsSelf|EcsIsEntity); + test_src(terms[0], 200, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_1_entity_id_pair_implicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_make_alive(world, 100); + ecs_make_alive(world, 300); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(#100, #300)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], 100, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], 300, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_1_entity_id_pair_explicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_make_alive(world, 100); + ecs_make_alive(world, 200); + ecs_make_alive(world, 300); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "#100(#200, #300)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], 100, EcsSelf|EcsIsEntity); + test_src(terms[0], 200, EcsSelf|EcsIsEntity); + test_second(terms[0], 300, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_1_digit_pred_implicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_set_name(world, 0, "100"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "100" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], e, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_1_digit_pred_no_subj(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_set_name(world, 0, "100"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "100()" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], e, EcsSelf|EcsIsEntity); + test_src(terms[0], 0, EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_1_digit_pred_explicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_set_name(world, 0, "100"); + ecs_entity_t e2 = ecs_set_name(world, 0, "200"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "100(200)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], e1, EcsSelf|EcsIsEntity); + test_src(terms[0], e2, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_1_digit_pair_implicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_entity_t e1 = ecs_set_name(world, 0, "100"); + ecs_entity_t e2 = ecs_set_name(world, 0, "300"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(100, 300)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], e1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], e2, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_1_digit_pair_explicit_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_entity_t e1 = ecs_set_name(world, 0, "100"); + ecs_entity_t e2 = ecs_set_name(world, 0, "200"); + ecs_entity_t e3 = ecs_set_name(world, 0, "300"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "100(200, 300)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], e1, EcsSelf|EcsIsEntity); + test_src(terms[0], e2, EcsSelf|EcsIsEntity); + test_second(terms[0], e3, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(self)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(up)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_superset_inclusive(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(self|up)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_superset_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(up|cascade)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_superset_inclusive_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(up|cascade|self)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsUp|EcsCascade|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_implicit_superset_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(cascade)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_implicit_superset_inclusive_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(self|cascade)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsSelf|EcsUp|EcsCascade|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_implicit_superset_cascade_w_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_ENTITY(world, Rel, Traversable); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(cascade Rel)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, Rel); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_ENTITY(world, Rel, Traversable); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(self|cascade Rel)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsSelf|EcsCascade|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, Rel); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_superset_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Pred, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsIsA); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_cascade_superset_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Pred, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(cascade|up IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsIsA); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_superset_cascade_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Pred, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(up|cascade IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsIsA); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_implicit_subject_superset_cascade_childof_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Pred, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "?Pred(up|cascade IsA)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsIsVariable); + test_int(terms[0].oper, EcsOptional); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsIsA); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_expr_w_symbol(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_component_init(world, &(ecs_component_desc_t){ + .entity = ecs_entity(world, { + .name = "Foo", + .symbol = "FooBar" + }) + }); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "FooBar" + }); + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_expr_w_newline(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity(world, { .name = "Foo" }); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Foo\n" + }); + test_assert(q != NULL); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_subj_entity_w_explicit_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj|self)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_subj_entity_w_explicit_self_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj|self|up)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_uint(terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_subj_entity_w_explicit_superset_relation(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Pred, (OnInstantiate, Inherit)); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj|up IsA)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsUp|EcsIsEntity); + test_int(terms[0].trav, EcsIsA); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_subj_entity_w_explicit_self_superset_relation(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Pred, (OnInstantiate, Inherit)); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj|self|up IsA)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsUp|EcsIsEntity); + test_int(terms[0].trav, EcsIsA); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_obj_entity_w_explicit_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj, Obj|self)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_obj_entity_w_explicit_self_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj, Obj|self|up)" + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_second(terms[0], Obj, EcsSelf|EcsUp|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_obj_entity_w_explicit_superset_relation(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj, Obj|up ChildOf)" + }); + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_obj_entity_w_explicit_self_superset_relation(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + ECS_TAG(world, Obj); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(Subj, Obj|self|up ChildOf)" + }); + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_pred_entity_w_explicit_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred|self(Subj)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_entity_w_explicit_self_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Subj); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred|self|up(Subj)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); + test_src(terms[0], Subj, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_entity_no_args_w_explicit_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred|self" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_entity_no_args_w_explicit_self_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred|self|up" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_entity_no_args_2_terms_w_explicit_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Pred2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred|self, Pred2" + }); + test_assert(q != NULL); + + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pred_entity_no_args_2_terms_w_explicit_self_superset(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + ECS_TAG(world, Pred2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred|self|up, Pred2" + }); + test_assert(q != NULL); + + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsUp|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_newline(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "\n" + }); + test_assert(q != NULL); + + test_int(term_count(q), 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_newlines(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "\n\n" + }); + test_assert(q != NULL); + + test_int(term_count(q), 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_3_newlines(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "\n\n" + }); + test_assert(q != NULL); + + test_int(term_count(q), 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_space(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = " " + }); + test_assert(q != NULL); + + test_int(term_count(q), 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = " " + }); + test_assert(q != NULL); + + test_int(term_count(q), 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_trailing_newline(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred\n" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_trailing_newlines(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred\n\n" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_trailing_space(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred " + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_2_trailing_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred " + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_template_type(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t Pred = ecs_entity_init(world, &(ecs_entity_desc_t){ + .name = "Template" + }); + test_assert(Pred != 0); + test_str(ecs_get_name(world, Pred), "Template"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Template" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_template_type_nested(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t Pred = ecs_entity_init(world, &(ecs_entity_desc_t){ + .name = "Template>" + }); + test_assert(Pred != 0); + test_str(ecs_get_name(world, Pred), "Template>"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Template>" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_template_type_multiple_args(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t Pred = ecs_entity_init(world, &(ecs_entity_desc_t){ + .name = "Template" + }); + test_assert(Pred != 0); + test_str(ecs_get_name(world, Pred), "Template"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Template" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_template_type_multiple_args_nested(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t Pred = ecs_entity_init(world, &(ecs_entity_desc_t){ + .name = "Template, Foo>" + }); + test_assert(Pred != 0); + test_str(ecs_get_name(world, Pred), "Template, Foo>"); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Template, Foo>" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_template_type_unbalanced_open(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Templateterm_count, 4); + + ecs_term_t *terms = q->terms; + test_first(terms[0], TagA, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsScopeOpen, EcsSelf|EcsIsEntity); + test_src(terms[1], 0, EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutNone); + + test_first(terms[2], TagB, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + test_first(terms[3], EcsScopeClose, EcsSelf|EcsIsEntity); + test_src(terms[3], 0, EcsIsEntity); + test_int(terms[3].oper, EcsAnd); + test_int(terms[3].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_query_scope_newline_after_close(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, {TagB\n}" + }); + test_assert(q != NULL); + test_int(q->term_count, 4); + + ecs_term_t *terms = q->terms; + test_first(terms[0], TagA, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsScopeOpen, EcsSelf|EcsIsEntity); + test_src(terms[1], 0, EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutNone); + + test_first(terms[2], TagB, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + test_first(terms[3], EcsScopeClose, EcsSelf|EcsIsEntity); + test_src(terms[3], 0, EcsIsEntity); + test_int(terms[3].oper, EcsAnd); + test_int(terms[3].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_query_term_after_scope(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, {TagB}, TagC" + }); + + test_assert(q != NULL); + test_int(q->term_count, 5); + + ecs_term_t *terms = q->terms; + test_first(terms[0], TagA, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsScopeOpen, EcsSelf|EcsIsEntity); + test_src(terms[1], 0, EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutNone); + + test_first(terms[2], TagB, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + test_first(terms[3], EcsScopeClose, EcsSelf|EcsIsEntity); + test_src(terms[3], 0, EcsIsEntity); + test_int(terms[3].oper, EcsAnd); + test_int(terms[3].inout, EcsInOutNone); + + test_first(terms[4], TagC, EcsSelf|EcsIsEntity); + test_src(terms[4], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[4].oper, EcsAnd); + test_int(terms[4].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_override_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "auto_override|Tag" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Tag, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_override_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "auto_override|(Rel, Tgt)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Rel, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Tgt, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_3_args(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "ChildOf($this, $parent, $grandparent)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); + test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_3_args_implicit_this(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(ChildOf, $parent, $grandparent)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); + test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_4_args(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "ChildOf($this, $parent, $grandparent, $greatgrandparent)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 3); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); + test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[2], 0, EcsSelf|EcsIsVariable, "grandparent"); + test_second_var(terms[2], 0, EcsSelf|EcsIsVariable, "greatgrandparent"); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_4_args_implicit_this(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "(ChildOf, $parent, $grandparent, $greatgrandparent)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 3); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); + test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[2], 0, EcsSelf|EcsIsVariable, "grandparent"); + test_second_var(terms[2], 0, EcsSelf|EcsIsVariable, "greatgrandparent"); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_3_args_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "ChildOf($this, $parent, $grandparent), Rel($this, $parent)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 3); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "parent"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "parent"); + test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "grandparent"); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], Rel, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_second_var(terms[2], 0, EcsSelf|EcsIsVariable, "parent"); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_cascade_desc(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred(cascade|desc)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Pred, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsUp|EcsCascade|EcsDesc|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + test_int(terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_3_args_this_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "ChildOf($grandchild, $child, $this)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 2); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "grandchild"); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "child"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "child"); + test_second(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_3_args_2_terms_this_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "ChildOf($grandchild, $child, $this), Rel($this)" + }); + test_assert(q != NULL); + + test_int(term_count(q), 3); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "grandchild"); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "child"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "child"); + test_second(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], Rel, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_3_args_2_terms_this_tgt_implicit_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "ChildOf($grandchild, $child, $this), Rel" + }); + test_assert(q != NULL); + + test_int(term_count(q), 3); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "grandchild"); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "child"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "child"); + test_second(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], Rel, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_3_or_args(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "ChildOf($this, Foo || Bar)" + }); + + test_int(q->term_count, 2); + + ecs_term_t *terms = q->terms; + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Foo, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Bar, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_3_or_args_implicit_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "(ChildOf, Foo || Bar)" + }); + + test_int(q->term_count, 2); + + ecs_term_t *terms = q->terms; + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Foo, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Bar, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_4_or_args(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_query_t *q = ecs_query(world, { + .expr = "ChildOf($this, Foo || Bar || Hello)" + }); + + test_int(q->term_count, 3); + + ecs_term_t *terms = q->terms; + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Foo, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Bar, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsOr); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[2], Hello, EcsSelf|EcsIsEntity); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_4_or_args_implicit_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_query_t *q = ecs_query(world, { + .expr = "(ChildOf, Foo || Bar || Hello)" + }); + + test_int(q->term_count, 3); + + ecs_term_t *terms = q->terms; + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Foo, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsOr); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[1], Bar, EcsSelf|EcsIsEntity); + test_int(terms[1].oper, EcsOr); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[2], Hello, EcsSelf|EcsIsEntity); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_or_before_and_oper(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_log_set_level(-4); + test_assert(NULL == ecs_query(world, { + .expr = "ChildOf($this, Foo || Bar, Hello)" + })); + + ecs_fini(world); +} + +void Parser_pair_and_before_or_oper(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_log_set_level(-4); + test_assert(NULL == ecs_query(world, { + .expr = "ChildOf($this, Foo, Bar || Hello)" + })); + + ecs_fini(world); +} + +void Parser_pair_3_args_w_vars(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "ChildOf($a, $bb, $ccc)" + }); + + test_int(q->term_count, 2); + + ecs_term_t *terms = q->terms; + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "a"); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "bb"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "bb"); + test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "ccc"); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_4_args_w_vars(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "ChildOf($a, $bb, $ccc, $dddd)" + }); + + test_int(q->term_count, 3); + + ecs_term_t *terms = q->terms; + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "a"); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "bb"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "bb"); + test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "ccc"); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[2], 0, EcsSelf|EcsIsVariable, "ccc"); + test_second_var(terms[2], 0, EcsSelf|EcsIsVariable, "dddd"); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_3_args_w_vars_w_term_after(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "ChildOf($a, $bb, $ccc), Foo($ccc)" + }); + + test_int(q->term_count, 3); + + ecs_term_t *terms = q->terms; + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "a"); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "bb"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "bb"); + test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "ccc"); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], Foo, EcsSelf|EcsIsEntity); + test_src_var(terms[2], 0, EcsSelf|EcsIsVariable, "ccc"); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_4_args_w_vars_w_term_after(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "ChildOf($a, $bb, $ccc, $dddd), Foo($dddd)" + }); + + test_int(q->term_count, 4); + + ecs_term_t *terms = q->terms; + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[0], 0, EcsSelf|EcsIsVariable, "a"); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "bb"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[1], 0, EcsSelf|EcsIsVariable, "bb"); + test_second_var(terms[1], 0, EcsSelf|EcsIsVariable, "ccc"); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], EcsChildOf, EcsSelf|EcsIsEntity); + test_src_var(terms[2], 0, EcsSelf|EcsIsVariable, "ccc"); + test_second_var(terms[2], 0, EcsSelf|EcsIsVariable, "dddd"); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + test_first(terms[3], Foo, EcsSelf|EcsIsEntity); + test_src_var(terms[3], 0, EcsSelf|EcsIsVariable, "dddd"); + test_int(terms[3].oper, EcsAnd); + test_int(terms[3].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_newline_after_inout(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in]\nTagA($this)" + }); + + test_assert(q != NULL); + + test_int(q->term_count, 1); + + ecs_term_t *terms = q->terms; + test_first(terms[0], TagA, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_newline_after_term_open(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] TagA(\n$this)" + }); + + test_assert(q != NULL); + + test_int(q->term_count, 1); + + ecs_term_t *terms = q->terms; + test_first(terms[0], TagA, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_newline_after_term_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] TagA($this\n)" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], TagA, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_newline_after_term_src_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] TagA($this\n,Tgt)" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], TagA, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Tgt, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_newline_after_term_pair_comma(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] TagA($this,\nTgt)" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], TagA, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Tgt, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_newline_after_term_pair_second(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "[in] TagA($this,Tgt\n)" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], TagA, EcsSelf|EcsIsEntity); + test_second(terms[0], Tgt, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsIn); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_tag_w_space_implicit_this(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_set_name(world, 0, "Tag A"); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag\\ A" + }); + + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], tag, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_tag_w_space(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t tag = ecs_set_name(world, 0, "Tag A"); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag\\ A($this)" + }); + + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], tag, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_first_w_space_implicit_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + ecs_entity_t tag = ecs_set_name(world, 0, "Tag A"); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Tag\\ A, Tgt)" + }); + + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], tag, EcsSelf|EcsIsEntity); + test_second(terms[0], Tgt, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_first_w_space(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + ecs_entity_t tag = ecs_set_name(world, 0, "Tag A"); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag\\ A($this, Tgt)" + }); + + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], tag, EcsSelf|EcsIsEntity); + test_second(terms[0], Tgt, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_second_w_space_implicit_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_entity_t tgt = ecs_set_name(world, 0, "Tgt A"); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, Tgt\\ A)" + }); + + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Rel, EcsSelf|EcsIsEntity); + test_second(terms[0], tgt, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_second_w_space(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_entity_t tgt = ecs_set_name(world, 0, "Tgt A"); + + ecs_query_t *q = ecs_query(world, { + .expr = "Rel($this, Tgt\\ A)" + }); + + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Rel, EcsSelf|EcsIsEntity); + test_second(terms[0], tgt, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_pair_src_w_space(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_entity_t src = ecs_set_name(world, 0, "Src A"); + + ecs_query_t *q = ecs_query(world, { + .expr = "Rel(Src\\ A, Tgt)" + }); + + test_assert(q != NULL); + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Rel, EcsSelf|EcsIsEntity); + test_second(terms[0], Tgt, EcsSelf|EcsIsEntity); + test_src(terms[0], src, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_empty_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1,, Pred_2" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_empty_term_w_space(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1, , Pred_2" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_empty_term_w_newline(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .expr = "Pred_1,\n, Pred_2" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_mixed_1_desc_and_1_expr(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .terms = {{ Pred_1 }}, + .expr = "Pred_2" + }); + + test_assert(q != NULL); + + test_int(q->term_count, 2); + + ecs_term_t *terms = q->terms; + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_mixed_2_desc_and_1_expr(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + ECS_TAG(world, Pred_3); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .terms = {{ Pred_1 }, { Pred_2 }}, + .expr = "Pred_3" + }); + + test_assert(q != NULL); + + test_int(q->term_count, 3); + + ecs_term_t *terms = q->terms; + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], Pred_3, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_mixed_1_desc_and_2_expr(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Pred_1); + ECS_TAG(world, Pred_2); + ECS_TAG(world, Pred_3); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .terms = {{ Pred_1 }}, + .expr = "Pred_2, Pred_3" + }); + + test_assert(q != NULL); + + test_int(q->term_count, 3); + + ecs_term_t *terms = q->terms; + test_first(terms[0], Pred_1, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + test_first(terms[1], Pred_2, EcsSelf|EcsIsEntity); + test_src(terms[1], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[1].oper, EcsAnd); + test_int(terms[1].inout, EcsInOutDefault); + + test_first(terms[2], Pred_3, EcsSelf|EcsIsEntity); + test_src(terms[2], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[2].oper, EcsAnd); + test_int(terms[2].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_not_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "!*" + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, EcsAny); + test_int(q->terms[0].oper, EcsNot); + test_int(q->terms[0].inout, EcsInOutNone); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_not_first_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "!(_, Position)" + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(EcsAny, ecs_id(Position))); + test_int(q->terms[0].oper, EcsNot); + test_int(q->terms[0].inout, EcsInOutNone); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_not_second_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "!(Position, _)" + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(ecs_id(Position), EcsAny)); + test_int(q->terms[0].oper, EcsNot); + test_int(q->terms[0].inout, EcsInOutNone); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_unterminated_paren(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "(" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_unterminated_after_first_paren(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "Position(" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_unterminated_after_src_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "Position($" + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Parser_eq_name_existing_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == \"Foo\"" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsPredEq, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_assert((terms[0].second.id & ~EcsTermRefFlags) == 0); + test_assert((terms[0].second.id & EcsTermRefFlags) == (EcsSelf|EcsIsName)); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_match_existing_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"Foo\"" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsPredMatch, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_assert((terms[0].second.id & ~EcsTermRefFlags) == 0); + test_assert((terms[0].second.id & EcsTermRefFlags) == (EcsSelf|EcsIsName)); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_match_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"*\"" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsPredMatch, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_assert((terms[0].second.id & ~EcsTermRefFlags) == 0); + test_assert((terms[0].second.id & EcsTermRefFlags) == (EcsSelf|EcsIsName)); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_match_any(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"_\"" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsPredMatch, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_assert((terms[0].second.id & ~EcsTermRefFlags) == 0); + test_assert((terms[0].second.id & EcsTermRefFlags) == (EcsSelf|EcsIsName)); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_match_variable_oper(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"$\"" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsPredMatch, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_assert((terms[0].second.id & ~EcsTermRefFlags) == 0); + test_assert((terms[0].second.id & EcsTermRefFlags) == (EcsSelf|EcsIsName)); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutNone); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_escaped_identifier(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { + .name = "foo.bar", + .sep = "" + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "foo\\.bar" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], e, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_escaped_identifier_first(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + ecs_entity_t e = ecs_entity(world, { + .name = "foo.bar", + .sep = "" + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "(foo\\.bar, Tgt)" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], e, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], Tgt, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_escaped_identifier_second(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_entity_t e = ecs_entity(world, { + .name = "foo.bar", + .sep = "" + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, foo\\.bar)" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], Rel, EcsSelf|EcsIsEntity); + test_src(terms[0], EcsThis, EcsSelf|EcsIsVariable); + test_second(terms[0], e, EcsSelf|EcsIsEntity); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Parser_n_tokens_test(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t c1 = ecs_entity(world, { + .name = "e_foo.e_bar.e_foobar.e_hello" + }); + + ecs_query_t *q = ecs_query(world, { + .expr = "ChildOf(e_foo.e_bar.e_foobar.e_hello,$variable)" + }); + + test_assert(q != NULL); + + test_int(term_count(q), 1); + + ecs_term_t *terms = query_terms(q); + test_first(terms[0], EcsChildOf, EcsSelf|EcsIsEntity); + test_src(terms[0], c1, EcsSelf|EcsIsEntity); + test_second_var(terms[0], 0, EcsSelf|EcsIsVariable, "variable"); + test_int(terms[0].oper, EcsAnd); + test_int(terms[0].inout, EcsInOutDefault); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Plan.c b/vendors/flecs/test/query/src/Plan.c new file mode 100644 index 000000000..d4a9dcd62 --- /dev/null +++ b/vendors/flecs/test/query/src/Plan.c @@ -0,0 +1,2691 @@ +#include + +void Plan_reordered_plan_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, ChildOf($this, $p, $gp, $ggp), Bar($ggp)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] selfup $[this] (Foo)" + LINE " 2. [ 1, 3] and $[this] (ChildOf, $p)" + LINE " 3. [ 2, 4] and $p (ChildOf, $gp)" + LINE " 4. [ 3, 5] and $gp (ChildOf, $ggp)" + LINE " 5. [ 4, 6] selfup $ggp (Bar)" + LINE " 6. [ 5, 7] setvars " + LINE " 7. [ 6, 8] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_reordered_plan_2(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($ggp), ChildOf($this, $p, $gp, $ggp), Bar($this)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] selfup $[ggp] (Foo)" + LINE " 2. [ 1, 3] each $ggp ($[ggp])" + LINE " 3. [ 2, 4] and $[gp] (ChildOf, $ggp)" + LINE " 4. [ 3, 5] each $gp ($[gp])" + LINE " 5. [ 4, 6] and $[p] (ChildOf, $gp)" + LINE " 6. [ 5, 7] each $p ($[p])" + LINE " 7. [ 6, 8] and $[this] (ChildOf, $p)" + LINE " 8. [ 7, 9] selfup $[this] (Bar)" + LINE " 9. [ 8, 10] setvars " + LINE "10. [ 9, 11] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_reordered_plan_3(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *r = ecs_query(world, { + .expr = "?Foo, Bar" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] and $[this] (Bar)" + LINE " 2. [ 1, 4] option " + LINE " 3. [ 2, 4] and $[this] (Foo)" + LINE " 4. [ 2, 5] end $[this] (Foo)" + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_reordered_plan_4(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + ECS_TAG(world, World); + + ecs_query_t *r = ecs_query(world, { + .expr = "?Foo, ?Bar, Hello" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] and $[this] (Hello)" + LINE " 2. [ 1, 4] option " + LINE " 3. [ 2, 4] and $[this] (Foo)" + LINE " 4. [ 2, 5] end $[this] (Foo)" + LINE " 5. [ 4, 7] option " + LINE " 6. [ 5, 7] and $[this] (Bar)" + LINE " 7. [ 5, 8] end $[this] (Bar)" + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_reordered_plan_5(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_query_t *r = ecs_query(world, { + .expr = "?Foo, Hello, ?Bar" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] and $[this] (Hello)" + LINE " 2. [ 1, 4] option " + LINE " 3. [ 2, 4] and $[this] (Foo)" + LINE " 4. [ 2, 5] end $[this] (Foo)" + LINE " 5. [ 4, 7] option " + LINE " 6. [ 5, 7] and $[this] (Bar)" + LINE " 7. [ 5, 8] end $[this] (Bar)" + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_reordered_plan_6(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + ECS_TAG(world, World); + + ecs_query_t *r = ecs_query(world, { + .expr = "?Foo, Hello, ?Bar, World" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] triv {1,3}" + LINE " 2. [ 1, 4] option " + LINE " 3. [ 2, 4] and $[this] (Foo)" + LINE " 4. [ 2, 5] end $[this] (Foo)" + LINE " 5. [ 4, 7] option " + LINE " 6. [ 5, 7] and $[this] (Bar)" + LINE " 7. [ 5, 8] end $[this] (Bar)" + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_reordered_plan_7(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + ECS_TAG(world, World); + + ecs_query_t *r = ecs_query(world, { + .expr = "?Foo, Hello, ?Bar($this, $foo), World($foo)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] and $[this] (Hello)" + LINE " 2. [ 1, 4] option " + LINE " 3. [ 2, 4] and $[this] (Foo)" + LINE " 4. [ 2, 5] end $[this] (Foo)" + LINE " 5. [ 4, 7] option " + LINE " 6. [ 5, 7] and $[this] (Bar, $foo)" + LINE " 7. [ 5, 8] end $[this] (Bar, $foo)" + LINE " 8. [ 7, 10] ifvar $foo" + LINE " 9. [ 8, 10] and $foo (World)" + LINE "10. [ 8, 11] end $foo (World)" + LINE "11. [10, 12] setvars " + LINE "12. [11, 13] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_1_trivial_plan(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + char *plan = ecs_query_plan(r); + test_str(NULL, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_2_trivial_plan(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self), Bar(self)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + char *plan = ecs_query_plan(r); + test_str(NULL, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_1_trivial_plan_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(self)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + char *plan = ecs_query_plan(r); + test_str(NULL, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_2_trivial_plan_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(self), Velocity(self)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + char *plan = ecs_query_plan(r); + test_str(NULL, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_3_trivial_plan_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "p" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self), Bar(self), ChildOf(self, p)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + char *plan = ecs_query_plan(r); + test_str(NULL, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_3_trivial_plan_w_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "p" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self), Bar(self), ChildOf(self, *)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] triv {0,1}" + LINE " 2. [ 1, 3] and $[this] (ChildOf, $*'1)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_3_trivial_plan_w_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "p" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self), Bar(self), ChildOf(self, _)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] triv {0,1}" + LINE " 2. [ 1, 3] andany $[this] (ChildOf, $_'1)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_3_trivial_plan_w_pair_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "p" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(self), Velocity(self), ChildOf(self, p)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + char *plan = ecs_query_plan(r); + test_str(NULL, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_3_trivial_plan_w_wildcard_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "p" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(self), Velocity(self), ChildOf(self, *)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] triv {0,1}" + LINE " 2. [ 1, 3] and $[this] (ChildOf, $*'1)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_3_trivial_plan_w_any_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "p" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(self), Velocity(self), ChildOf(self, _)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] triv {0,1}" + LINE " 2. [ 1, 3] andany $[this] (ChildOf, $_'1)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_1_trivial_component_w_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "p" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "[none] Position(self)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + char *plan = ecs_query_plan(r); + test_str(NULL, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_2_trivial_component_w_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "p" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "[none] Position(self), [none] Velocity(self)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + char *plan = ecs_query_plan(r); + test_str(NULL, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_2_trivial_plan_w_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self), ChildOf(self, *)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] and $[this] (Foo)" + LINE " 2. [ 1, 3] and $[this] (ChildOf, $*'1)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_this_before_fixed_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, Bar(e)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Bar)" + LINE " 3. [ 2, 4] selfup $[this] (Foo)" + LINE " 4. [ 3, 5] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_fixed_src_before_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Bar(e), Foo" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Bar)" + LINE " 3. [ 2, 4] selfup $[this] (Foo)" + LINE " 4. [ 3, 5] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_var_before_fixed_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($var), Bar(e)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Bar)" + LINE " 3. [ 2, 4] selfup $[var] (Foo)" + LINE " 4. [ 3, 5] each $var ($[var])" + LINE " 5. [ 4, 6] setvars " + LINE " 6. [ 5, 7] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_fixed_src_before_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Bar(e), Foo($var)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Bar)" + LINE " 3. [ 2, 4] selfup $[var] (Foo)" + LINE " 4. [ 3, 5] each $var ($[var])" + LINE " 5. [ 4, 6] setvars " + LINE " 6. [ 5, 7] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_this_before_fixed_src_w_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, !Bar(e)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 4] not " + LINE " 3. [ 2, 4] selfup e (Bar)" + LINE " 4. [ 2, 5] end e (Bar)" + LINE " 5. [ 4, 6] selfup $[this] (Foo)" + LINE " 6. [ 5, 7] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_this_before_fixed_src_w_first_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, $this(e)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] with e ($this)" + LINE " 3. [ 2, 4] selfup $this (Foo)" + LINE " 4. [ 3, 5] setthis ($this)" + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_this_before_fixed_src_w_first_var_w_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, !$this(e)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup $[this] (Foo)" + LINE " 3. [ 2, 4] each $this ($[this])" + LINE " 4. [ 3, 6] not " + LINE " 5. [ 4, 6] with e ($this)" + LINE " 6. [ 4, 7] end e ($this)" + LINE " 7. [ 6, 8] setthis ($this)" + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_this_before_fixed_src_w_second_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, Bar(e, $this)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Bar, $this)" + LINE " 3. [ 2, 4] selfup $this (Foo)" + LINE " 4. [ 3, 5] setthis ($this)" + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_this_before_fixed_src_w_second_var_w_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo, !Bar(e, $this)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup $[this] (Foo)" + LINE " 3. [ 2, 4] each $this ($[this])" + LINE " 4. [ 3, 6] not " + LINE " 5. [ 4, 6] selfup e (Bar, $this)" + LINE " 6. [ 4, 7] end e (Bar, $this)" + LINE " 7. [ 6, 8] setthis ($this)" + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_1_fixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(e)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_1_fixed_1_this_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(self)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] and $[this] (Velocity)" + LINE " 4. [ 3, 5] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_this_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(e), Mass(self), Rotation(self)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] triv {2,3}" + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_this_self_interleaved(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Mass(self), Velocity(e), Rotation(self)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] triv {1,3}" + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_this_self_2_fixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Mass(self), Rotation(self), Position(e), Velocity(e)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] triv {0,1}" + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_1_fixed_1_this_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(up)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] up $[this] (Velocity)" + LINE " 4. [ 3, 5] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_this_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(e), Mass(up), Rotation(up)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] up $[this] (Mass)" + LINE " 5. [ 4, 6] up $[this] (Rotation)" + LINE " 6. [ 5, 7] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_this_up_interleaved(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Mass(up), Velocity(e), Rotation(up)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] up $[this] (Mass)" + LINE " 5. [ 4, 6] up $[this] (Rotation)" + LINE " 6. [ 5, 7] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_this_up_2_fixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Mass(up), Rotation(up), Position(e), Velocity(e)" + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] up $[this] (Mass)" + LINE " 5. [ 4, 6] up $[this] (Rotation)" + LINE " 6. [ 5, 7] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_1_fixed_1_this_self_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(self)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] cache " + LINE " 4. [ 3, 5] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_this_self_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(e), Mass(self), Rotation(self)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] cache " + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_this_self_interleaved_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Mass(self), Velocity(e), Rotation(self)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] cache " + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_this_self_2_fixed_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Mass(self), Rotation(self), Position(e), Velocity(e)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] cache " + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_1_fixed_1_this_up_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(up)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] cache " + LINE " 4. [ 3, 5] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_this_up_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(e), Mass(up), Rotation(up)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] cache " + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_this_up_interleaved_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Mass(up), Velocity(e), Rotation(up)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] cache " + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_this_up_2_fixed_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Mass(up), Rotation(up), Position(e), Velocity(e)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] cache " + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_1_fixed_1_var_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity($var|self)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] and $[var] (Velocity)" + LINE " 4. [ 3, 5] each $var ($[var])" + LINE " 5. [ 4, 6] setvars " + LINE " 6. [ 5, 7] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_var_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(e), Mass($var|self), Rotation($var|self)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] and $[var] (Mass)" + LINE " 5. [ 4, 6] and $[var] (Rotation)" + LINE " 6. [ 5, 7] each $var ($[var])" + LINE " 7. [ 6, 8] setvars " + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_var_self_interleaved(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Mass($var|self), Velocity(e), Rotation($var|self)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] and $[var] (Mass)" + LINE " 5. [ 4, 6] and $[var] (Rotation)" + LINE " 6. [ 5, 7] each $var ($[var])" + LINE " 7. [ 6, 8] setvars " + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_var_self_2_fixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Mass($var|self), Rotation($var|self), Position(e), Velocity(e)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] and $[var] (Mass)" + LINE " 5. [ 4, 6] and $[var] (Rotation)" + LINE " 6. [ 5, 7] each $var ($[var])" + LINE " 7. [ 6, 8] setvars " + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_1_fixed_1_var_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity($var|up)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] up $[var] (Velocity)" + LINE " 4. [ 3, 5] each $var ($[var])" + LINE " 5. [ 4, 6] setvars " + LINE " 6. [ 5, 7] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_var_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Velocity(e), Mass($var|up), Rotation($var|up)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] up $[var] (Mass)" + LINE " 5. [ 4, 6] up $[var] (Rotation)" + LINE " 6. [ 5, 7] each $var ($[var])" + LINE " 7. [ 6, 8] setvars " + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_fixed_2_var_up_interleaved(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(e), Mass($var|up), Velocity(e), Rotation($var|up)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] up $[var] (Mass)" + LINE " 5. [ 4, 6] up $[var] (Rotation)" + LINE " 6. [ 5, 7] each $var ($[var])" + LINE " 7. [ 6, 8] setvars " + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_populate_2_var_up_2_fixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Rotation); + ecs_add_pair(world, ecs_id(Rotation), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Mass($var|up), Rotation($var|up), Position(e), Velocity(e)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] selfup e (Position)" + LINE " 3. [ 2, 4] selfup e (Velocity)" + LINE " 4. [ 3, 5] up $[var] (Mass)" + LINE " 5. [ 4, 6] up $[var] (Rotation)" + LINE " 6. [ 5, 7] each $var ($[var])" + LINE " 7. [ 6, 8] setvars " + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_cache_2_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position || Velocity", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] xcache " + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_cache_2_or_w_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + + ecs_entity(world, { .name = "e" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position || Velocity, !Mass", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] xcache " + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_1_plan_any_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "RelA(_)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] ids $[_'1] (RelA)" + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + ecs_query_fini(r); + ecs_fini(world); +} + +void Plan_1_plan_not_any_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "!RelA(_)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 2] not " + LINE " 1. [ 0, 2] ids $[_'1] (RelA)" + LINE " 2. [ 0, 3] end $[_'1] (RelA)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_1_plan_optional_any_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "?RelA(_)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 2] option " + LINE " 1. [ 0, 2] ids $[_'1] (RelA)" + LINE " 2. [ 0, 3] end $[_'1] (RelA)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(r); + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_pair_first_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "(*, Tgt)", + .cache_kind = EcsQueryCacheDefault + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] and $[this] ($*'1, Tgt)" + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(r); + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_pair_first_wildcard_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "(*, Tgt)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] xcache " + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(r); + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_pair_second_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *r = ecs_query(world, { + .expr = "(Rel, *)", + .cache_kind = EcsQueryCacheDefault + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] and $[this] (Rel, $*'1)" + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(r); + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_pair_second_wildcard_cached(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *r = ecs_query(world, { + .expr = "(Rel, *)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(r != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] xcache " + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(r); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Plan_0_src_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(#0)" + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_0_src_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(#0)" + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_0_src_w_sparse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(#0)" + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_0_src_w_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(#0)" + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_0_src_w_union(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Movement); + + ecs_add_id(world, ecs_id(Movement), EcsUnion); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement(#0, *)" + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_0_src_w_sparse_and_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(#0), Velocity" + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] and $[this] (Velocity)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_0_src_w_toggle_and_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(#0), Velocity" + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] and $[this] (Velocity)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_0_src_w_union_and_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Movement); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Movement), EcsUnion); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement(#0, *), Velocity" + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setfix " + LINE " 1. [ 0, 2] setids " + LINE " 2. [ 1, 3] and $[this] (Velocity)" + LINE " 3. [ 2, 4] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_cached_isa_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "(IsA, Tgt)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] trav $[this] (IsA, Tgt)" + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_cached_isa_tgt_w_self_second(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "(IsA, Tgt|self)", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] xcache " + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_cached_isa_tgt_no_expr(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(EcsIsA, Tgt) }}, + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] trav $[this] (IsA, Tgt)" + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_cached_isa_tgt_w_self_second_no_expr(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(EcsIsA, Tgt), .second.id = EcsSelf }}, + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] xcache " + LINE " 1. [ 0, 2] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_cached_w_not_and_uncacheable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "(IsA, Tgt), !Foo", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] trav $[this] (IsA, Tgt)" + LINE " 2. [ 1, 4] not " + LINE " 3. [ 2, 4] and $[this] (Foo)" + LINE " 4. [ 2, 5] end $[this] (Foo)" + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_cached_w_optional_and_uncacheable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "(IsA, Tgt), ?Foo", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] trav $[this] (IsA, Tgt)" + LINE " 2. [ 1, 4] option " + LINE " 3. [ 2, 4] and $[this] (Foo)" + LINE " 4. [ 2, 5] end $[this] (Foo)" + LINE " 5. [ 4, 6] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Plan_cached_w_not_optional_and_uncacheable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tgt); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "(IsA, Tgt), !Foo, ?Bar", + .cache_kind = EcsQueryCacheAuto + }); + + test_assert(q != NULL); + + ecs_log_enable_colors(false); + + const char *expect = + HEAD " 0. [-1, 1] setids " + LINE " 1. [ 0, 2] trav $[this] (IsA, Tgt)" + LINE " 2. [ 1, 4] not " + LINE " 3. [ 2, 4] and $[this] (Foo)" + LINE " 4. [ 2, 5] end $[this] (Foo)" + LINE " 5. [ 4, 7] option " + LINE " 6. [ 5, 7] and $[this] (Bar)" + LINE " 7. [ 5, 8] end $[this] (Bar)" + LINE " 8. [ 7, 9] yield " + LINE ""; + char *plan = ecs_query_plan(q); + + test_str(expect, plan); + ecs_os_free(plan); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/QueryStr.c b/vendors/flecs/test/query/src/QueryStr.c new file mode 100644 index 000000000..a4f884a42 --- /dev/null +++ b/vendors/flecs/test/query/src/QueryStr.c @@ -0,0 +1,675 @@ +#include + +void QueryStr_one_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ TagA }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "TagA($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_inout(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA, .inout = EcsIn } + } + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "[in] TagA($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_two_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA }, + { TagB } + } + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "TagA($this), TagB($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_two_terms_w_inout(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA }, + { TagB, .inout = EcsIn } + } + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "TagA($this), [in] TagB($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_three_terms_w_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA }, + { TagB, .oper = EcsOr }, + { TagC } + } + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "TagA($this), TagB($this) || TagC($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_three_terms_w_or_inout(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA }, + { TagB, .oper = EcsOr, .inout = EcsIn }, + { TagC, .inout = EcsIn } + } + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "TagA($this), [in] TagB($this) || TagC($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_four_terms_three_w_or_inout(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagD); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA }, + { TagB, .oper = EcsOr, .inout = EcsIn }, + { TagC, .inout = EcsIn }, + { TagD, .inout = EcsIn } + } + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "TagA($this), [in] TagB($this) || TagC($this), [in] TagD($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Rel, Tgt) }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Rel($this,Tgt)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_pair_entity_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Src); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Rel, Tgt), .src.id = Src }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Rel(Src,Tgt)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Foo, .src.id = EcsSelf }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Foo, .src.id = EcsUp }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo($this|up ChildOf)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_cascade(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Foo, .src.id = EcsCascade }}, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo($this|cascade ChildOf)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Foo, .src.id = EcsSelf|EcsUp }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo($this|self|up ChildOf)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_0(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Foo, .src.id = EcsIsEntity }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo()"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_singleton(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Foo, .src.id = Foo }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo($)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_final_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, Final); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Foo }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_final_dont_inherit(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, Final, (OnInstantiate, DontInherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Foo }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_final_inherit(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, Final, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Foo }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo($this|self|up IsA)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_final_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, Final, (OnInstantiate, Override)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Foo }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Foo($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_src_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Tag, .src.id = EcsIsVariable, .src.name = "Var" }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Tag($Var)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_first_var(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = EcsIsVariable, .first.name = "Var" }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "[none] $Var($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_second_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Rel, .second.id = EcsIsVariable, .second.name = "Var" }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Rel($this,$Var)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_first_var_entity_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Src); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = EcsIsVariable, .first.name = "Var", .src.id = Src }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "[none] $Var(Src)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_one_term_w_pair_w_0_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = Rel, .second.id = Tgt, .src = { + .id = EcsIsEntity + } } + } + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "Rel(#0,Tgt)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_not_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ TagA, .oper = EcsNot }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "!TagA($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_wildcard_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ EcsWildcard }} + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "[none] *($this)"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_scopes(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, {TagB, {TagC}}" + }); + test_assert(q != NULL); + + char *str = ecs_query_str(q); + test_str(str, "TagA($this), {TagB($this), {TagC($this)}}"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + + +void QueryStr_pred_eq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == TagA" + }); + + char *str = ecs_query_str(q); + test_str(str, "$this == TagA"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_pred_neq(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this != TagA" + }); + + char *str = ecs_query_str(q); + test_str(str, "$this != TagA"); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_pred_eq_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == \"TagA\"" + }); + + char *str = ecs_query_str(q); + test_str(str, "$this == \"TagA\""); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_pred_neq_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this != \"TagA\"" + }); + + char *str = ecs_query_str(q); + test_str(str, "$this != \"TagA\""); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_pred_eq_m(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"TagA\"" + }); + + char *str = ecs_query_str(q); + test_str(str, "$this ~= \"TagA\""); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void QueryStr_pred_neq_m(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this ~= \"!TagA\"" + }); + + char *str = ecs_query_str(q); + test_str(str, "$this ~= \"!TagA\""); + ecs_os_free(str); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Recycled.c b/vendors/flecs/test/query/src/Recycled.c new file mode 100644 index 000000000..42a9635c3 --- /dev/null +++ b/vendors/flecs/test/query/src/Recycled.c @@ -0,0 +1,300 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void Recycled_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +static ecs_entity_t recycled_id(ecs_world_t *world, const char *name) { + ecs_entity_t result = ecs_new(world); + ecs_delete(world, result); + ecs_entity_t result_2 = ecs_new(world); + test_assert(result_2 != (uint32_t)result); + ecs_set_name(world, result_2, name); + return result_2; +} + +void Recycled_recycled_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t src = recycled_id(world, "src"); + ecs_entity_t rel = recycled_id(world, "rel"); + ecs_add(world, src, Tag); + ecs_add_id(world, src, rel); + + ecs_query_t *r = ecs_query(world, { + .expr = "$x($y), Tag($y)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int32_t x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + int32_t y_var = ecs_query_find_var(r, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(src, ecs_field_src(&it, 0)); + test_uint(src, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_iter_get_var(&it, x_var)); + test_uint(src, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(rel, ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(src, ecs_field_src(&it, 0)); + test_uint(src, ecs_field_src(&it, 1)); + test_uint(rel, ecs_iter_get_var(&it, x_var)); + test_uint(src, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Recycled_recycled_pair_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t src = recycled_id(world, "src"); + ecs_entity_t rel = recycled_id(world, "rel"); + ecs_entity_t tgt = recycled_id(world, "tgt"); + ecs_add(world, src, Tag); + ecs_add_pair(world, src, rel, tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "$x($y, $z), Tag($y)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int32_t x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + int32_t y_var = ecs_query_find_var(r, "y"); + test_assert(y_var != -1); + int32_t z_var = ecs_query_find_var(r, "z"); + test_assert(z_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(src, ecs_field_src(&it, 0)); + test_uint(src, ecs_field_src(&it, 1)); + test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); + test_uint(src, ecs_iter_get_var(&it, y_var)); + test_uint(EcsName, ecs_iter_get_var(&it, z_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(rel, tgt), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(src, ecs_field_src(&it, 0)); + test_uint(src, ecs_field_src(&it, 1)); + test_uint(rel, ecs_iter_get_var(&it, x_var)); + test_uint(src, ecs_iter_get_var(&it, y_var)); + test_uint(tgt, ecs_iter_get_var(&it, z_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Recycled_recycled_this_ent_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t src = recycled_id(world, "src"); + ecs_entity_t rel = recycled_id(world, "rel"); + ecs_entity_t tgt = recycled_id(world, "tgt"); + ecs_add(world, src, Tag); + ecs_add_pair(world, src, rel, tgt); + + ecs_query_t *r = ecs_query(world, { + .expr = "$x($y, $this), Tag($y)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int32_t x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + int32_t y_var = ecs_query_find_var(r, "y"); + test_assert(y_var != -1); + int32_t this_var = ecs_query_find_var(r, "this"); + test_assert(this_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(src, ecs_field_src(&it, 0)); + test_uint(src, ecs_field_src(&it, 1)); + test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); + test_uint(src, ecs_iter_get_var(&it, y_var)); + test_uint(EcsName, ecs_iter_get_var(&it, this_var)); + test_uint(EcsName, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(rel, tgt), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(src, ecs_field_src(&it, 0)); + test_uint(src, ecs_field_src(&it, 1)); + test_uint(rel, ecs_iter_get_var(&it, x_var)); + test_uint(src, ecs_iter_get_var(&it, y_var)); + test_uint(tgt, ecs_iter_get_var(&it, this_var)); + test_uint(tgt, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Recycled_has_recycled_id_from_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_entity_t src = recycled_id(world, "src"); + ecs_entity_t rel = recycled_id(world, "rel"); + ecs_entity_t tgt = recycled_id(world, "tgt"); + ecs_add_pair(world, src, rel, tgt); + ecs_add_id(world, src, tgt); + ecs_add(world, src, Tag); + + ecs_query_t *r = ecs_query(world, { + .expr = "$x($y, $z), $z($y), Tag($y)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int32_t x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + int32_t y_var = ecs_query_find_var(r, "y"); + test_assert(y_var != -1); + int32_t z_var = ecs_query_find_var(r, "z"); + test_assert(z_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(rel, tgt), ecs_field_id(&it, 0)); + test_uint(tgt, ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(src, ecs_field_src(&it, 0)); + test_uint(src, ecs_field_src(&it, 1)); + test_uint(src, ecs_field_src(&it, 2)); + test_uint(rel, ecs_iter_get_var(&it, x_var)); + test_uint(src, ecs_iter_get_var(&it, y_var)); + test_uint(tgt, ecs_iter_get_var(&it, z_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Recycled_recycled_pair(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t rel = ecs_entity(world, { .name = "Rel" }); + ecs_entity_t tgt = ecs_entity(world, { .name = "Tgt" }); + ecs_delete(world, rel); + ecs_delete(world, tgt); + rel = ecs_entity(world, { .name = "Rel" }); + tgt = ecs_entity(world, { .name = "Tgt" }); + + ecs_entity_t e = ecs_new_w_pair(world, rel, tgt); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { + .first.id = rel, + .second.id = tgt + }, + .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(it.count, 1); + test_uint(it.entities[0], e); + test_uint(ecs_field_id(&it, 0), ecs_pair(rel, tgt)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Recycled_recycled_component_id(void) { + ecs_world_t* ecs = ecs_mini(); + + for (int i = 0; i < FLECS_HI_COMPONENT_ID; i ++) { + ecs_new_low_id(ecs); + } + + ecs_entity_t e = ecs_new(ecs); + ecs_delete(ecs, e); + + ECS_COMPONENT(ecs, Position); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position) } + }, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {20, 30})); + + ecs_iter_t it = ecs_query_iter(ecs, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 2); + test_uint(it.entities[0], e1); + test_uint(it.entities[1], e2); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(ecs); +} diff --git a/vendors/flecs/test/query/src/Scopes.c b/vendors/flecs/test/query/src/Scopes.c new file mode 100644 index 000000000..4069b7b57 --- /dev/null +++ b/vendors/flecs/test/query/src/Scopes.c @@ -0,0 +1,508 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; +static ecs_entity_t on_instantiate = 0; + +void Scopes_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_kind '%s'\n", cache_param); + } + } + + const char *on_instantiate_param = test_param("on_instantiate"); + if (on_instantiate_param) { + if (!strcmp(on_instantiate_param, "override")) { + on_instantiate = EcsOverride; + } else if (!strcmp(on_instantiate_param, "inherit")) { + on_instantiate = EcsInherit; + } else { + printf("unexpected value for on_instantiate '%s'\n", cache_param); + } + } +} + +void Scopes_term_w_not_scope_1_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, on_instantiate); + + ecs_entity_t parent_1 = ecs_new_w(world, Root); + ecs_add(world, parent_1, TagA); + + ecs_entity_t parent_2 = ecs_new_w(world, Root); + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ TagA }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(parent_2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagB); + ecs_add_pair(world, TagB, EcsOnInstantiate, on_instantiate); + + ecs_entity_t parent_1 = ecs_new_w(world, Root); + ecs_add(world, parent_1, TagA); + + ecs_entity_t parent_2 = ecs_new_w(world, Root); + ecs_add(world, parent_2, TagB); + + ecs_entity_t parent_3 = ecs_new_w(world, Root); + ecs_add(world, parent_3, TagA); + ecs_add(world, parent_3, TagB); + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ TagA, TagB }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(parent_1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(parent_2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_1_term_w_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, on_instantiate); + + ecs_entity_t parent_1 = ecs_new_w(world, Root); + ecs_add(world, parent_1, TagA); + + ecs_new_w(world, Root); + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ !TagA }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(parent_1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_2_terms_w_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagB); + ecs_add_pair(world, TagB, EcsOnInstantiate, on_instantiate); + + ecs_entity_t parent_1 = ecs_new_w(world, Root); + ecs_add(world, parent_1, TagA); + + ecs_entity_t parent_2 = ecs_new_w(world, Root); + ecs_add(world, parent_2, TagB); + + ecs_entity_t parent_3 = ecs_new_w(world, Root); + ecs_add(world, parent_3, TagA); + ecs_add(world, parent_3, TagB); + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ TagA, !TagB }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(parent_2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(parent_3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_1_term_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + + ecs_entity_t parent_1 = ecs_new_w(world, Root); + ecs_new_w_pair(world, EcsChildOf, parent_1); + ecs_new_w_pair(world, EcsChildOf, parent_1); + + ecs_entity_t parent_2 = ecs_new_w(world, Root); + ecs_new_w_pair(world, EcsChildOf, parent_2); + + ecs_entity_t parent_3 = ecs_new_w(world, Root); + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ ChildOf($child, $this) }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(parent_3, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_2_terms_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Position); + + ecs_entity_t parent_0 = ecs_new_w(world, Root); + { + ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_0); + ecs_set(world, child_1, Position, {10, 20}); + ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent_0); + ecs_set(world, child_2, Position, {10, 20}); + } + + ecs_entity_t parent_1 = ecs_new_w(world, Root); + { + ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_1); + ecs_set(world, child_1, Position, {10, 20}); + ecs_new_w_pair(world, EcsChildOf, parent_1); + } + + ecs_entity_t parent_2 = ecs_new_w(world, Root); + { + ecs_new_w_pair(world, EcsChildOf, parent_2); + ecs_new_w_pair(world, EcsChildOf, parent_2); + } + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ ChildOf($child, $this), Position($child) }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(parent_2, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_1_term_w_not_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + + ecs_entity_t parent_0 = ecs_new_w(world, Root); + { + ecs_new_w_pair(world, EcsChildOf, parent_0); + ecs_new_w_pair(world, EcsChildOf, parent_0); + } + + ecs_new_w(world, Root); + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ !ChildOf($child, $this) }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_2_terms_w_not_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + ecs_add_pair(world, Foo, EcsOnInstantiate, on_instantiate); + + ecs_entity_t parent_0 = ecs_new_w(world, Root); + { + ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_0); + ecs_set(world, child_1, Position, {10, 20}); + ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent_0); + ecs_set(world, child_2, Position, {10, 20}); + ecs_entity_t child_3 = ecs_new_w_pair(world, EcsChildOf, parent_0); + ecs_set(world, child_3, Position, {10, 20}); + ecs_add(world, child_3, Foo); + } + + ecs_entity_t parent_1 = ecs_new_w(world, Root); + { + ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_1); + ecs_set(world, child_1, Position, {10, 20}); + ecs_new_w_pair(world, EcsChildOf, parent_1); + } + + ecs_entity_t parent_2 = ecs_new_w(world, Root); + { + ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent_2); + ecs_add(world, child_1, Foo); + ecs_new_w_pair(world, EcsChildOf, parent_2); + } + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ ChildOf($child, $this), !Position($child) }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(parent_0, it.entities[0]); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_2_terms_w_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagB); + ecs_add_pair(world, TagB, EcsOnInstantiate, on_instantiate); + + ecs_entity_t e1 = ecs_new_w(world, Root); + ecs_add(world, e1, TagA); + ecs_entity_t e2 = ecs_new_w(world, Root); + ecs_add(world, e2, TagB); + ecs_entity_t e3 = ecs_new_w(world, Root); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + ecs_entity_t e4 = ecs_new_w(world, Root); + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ TagA || TagB }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e4, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_3_terms_w_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Root); + ecs_add_pair(world, Root, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagB); + ecs_add_pair(world, TagB, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagC); + ecs_add_pair(world, TagC, EcsOnInstantiate, on_instantiate); + + ecs_entity_t e1 = ecs_new_w(world, Root); + ecs_add(world, e1, TagA); + ecs_entity_t e2 = ecs_new_w(world, Root); + ecs_add(world, e2, TagB); + ecs_entity_t e3 = ecs_new_w(world, Root); + ecs_add(world, e3, TagA); + ecs_add(world, e3, TagB); + ecs_entity_t e4 = ecs_new_w(world, Root); + ecs_add(world, e4, TagC); + ecs_entity_t e5 = ecs_new_w(world, Root); + + ecs_query_t *r = ecs_query(world, { + .expr = "Root, !{ TagA || TagB || TagC }", + .cache_kind = cache_kind + }); + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Root, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e5, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Scopes_term_w_not_scope_2_terms_w_before_after(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ecs_add_pair(world, TagA, EcsOnInstantiate, on_instantiate); + ECS_TAG(world, TagB); + ecs_add_pair(world, TagB, EcsOnInstantiate, on_instantiate); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !{ChildOf($child, $this), TagB($child)}, TagB" + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + + ecs_add(world, e1, TagB); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagB); + + ecs_entity_t child_e1_1 = ecs_entity(world, { .name = "child_1" }); + ecs_add_pair(world, child_e1_1, EcsChildOf, e1); + + ecs_entity_t child_e2_1 = ecs_entity(world, { .name = "child_1" }); + ecs_add_pair(world, child_e2_1, EcsChildOf, e2); + + ecs_entity_t child_e2_2 = ecs_entity(world, { .name = "child_2" }); + ecs_add_pair(world, child_e2_2, EcsChildOf, e2); + ecs_add(world, child_e2_2, TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Sparse.c b/vendors/flecs/test/query/src/Sparse.c new file mode 100644 index 000000000..d8e21b10c --- /dev/null +++ b/vendors/flecs/test/query/src/Sparse.c @@ -0,0 +1,1179 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void Sparse_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +void Sparse_1_fixed_sparse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + ecs_set(world, ent, Position, {10, 20}); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(ent)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ent, ecs_field_src(&it, 0)); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_fixed_sparse_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + ecs_set(world, ent, Position, {10, 20}); + + ecs_query_t *q = ecs_query(world, { + .expr = "[none] Position(ent)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ent, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_this_sparse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + + test_uint(e3, it.entities[2]); + { + Position *p = ecs_field_at(&it, Position, 0, 2); + test_assert(p != NULL); + test_int(p->x, 50); test_int(p->y, 60); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_this_sparse_simple(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position) }}, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + + test_uint(e3, it.entities[2]); + { + Position *p = ecs_field_at(&it, Position, 0, 2); + test_assert(p != NULL); + test_int(p->x, 50); test_int(p->y, 60); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_this_sparse_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "[none] Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_this_sparse_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + test_bool(true, !!(q->row_fields & (1llu << 1))); + + ecs_entity_t e1 = ecs_insert(world, {Foo}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {Foo}, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, {Foo}, ecs_value(Position, {50, 60})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 1, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 1, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + + test_uint(e3, it.entities[2]); + { + Position *p = ecs_field_at(&it, Position, 1, 2); + test_assert(p != NULL); + test_int(p->x, 50); test_int(p->y, 60); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_this_sparse_written_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, [none] Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + test_bool(false, !!(q->row_fields & (1llu << 1))); + + ecs_entity_t e1 = ecs_insert(world, {Foo}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {Foo}, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, {Foo}, ecs_value(Position, {50, 60})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_var_sparse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($x)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 50); test_int(p->y, 60); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_var_sparse_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "[none] Position($x)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {50, 60})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_var_sparse_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), Position($x)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + test_bool(true, !!(q->row_fields & (1llu << 1))); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, {Foo}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {Foo}, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, {Foo}, ecs_value(Position, {50, 60})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field_at(&it, Position, 1, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field_at(&it, Position, 1, 0); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field_at(&it, Position, 1, 0); + test_assert(p != NULL); + test_int(p->x, 50); test_int(p->y, 60); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_var_sparse_written_none(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x), [none] Position($x)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + test_bool(false, !!(q->row_fields & (1llu << 1))); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t e1 = ecs_insert(world, {Foo}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {Foo}, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, {Foo}, ecs_value(Position, {50, 60})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_2_sparse_simple(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_id(Position) }, { .id = ecs_id(Velocity) }}, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + test_bool(true, !!(q->row_fields & (1llu << 1))); + + ecs_entity_t e1 = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + ecs_entity_t e2 = ecs_insert(world, + ecs_value(Position, {30, 40}), + ecs_value(Velocity, {3, 4})); + ecs_entity_t e3 = ecs_insert(world, + ecs_value(Position, {50, 60}), + ecs_value(Velocity, {5, 6})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + { + Velocity *v = ecs_field_at(&it, Velocity, 1, 0); + test_assert(v != NULL); + test_int(v->x, 1); test_int(v->y, 2); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + { + Velocity *v = ecs_field_at(&it, Velocity, 1, 1); + test_assert(v != NULL); + test_int(v->x, 3); test_int(v->y, 4); + } + + test_uint(e3, it.entities[2]); + { + Position *p = ecs_field_at(&it, Position, 0, 2); + test_assert(p != NULL); + test_int(p->x, 50); test_int(p->y, 60); + } + { + Velocity *v = ecs_field_at(&it, Velocity, 1, 2); + test_assert(v != NULL); + test_int(v->x, 5); test_int(v->y, 6); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_2_sparse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + test_bool(true, !!(q->row_fields & (1llu << 1))); + + ecs_entity_t e1 = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + ecs_entity_t e2 = ecs_insert(world, + ecs_value(Position, {30, 40}), + ecs_value(Velocity, {3, 4})); + ecs_entity_t e3 = ecs_insert(world, + ecs_value(Position, {50, 60}), + ecs_value(Velocity, {5, 6})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + { + Velocity *v = ecs_field_at(&it, Velocity, 1, 0); + test_assert(v != NULL); + test_int(v->x, 1); test_int(v->y, 2); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + { + Velocity *v = ecs_field_at(&it, Velocity, 1, 1); + test_assert(v != NULL); + test_int(v->x, 3); test_int(v->y, 4); + } + + test_uint(e3, it.entities[2]); + { + Position *p = ecs_field_at(&it, Position, 0, 2); + test_assert(p != NULL); + test_int(p->x, 50); test_int(p->y, 60); + } + { + Velocity *v = ecs_field_at(&it, Velocity, 1, 2); + test_assert(v != NULL); + test_int(v->x, 5); test_int(v->y, 6); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_2_sparse_and_regular(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + test_bool(false, !!(q->row_fields & (1llu << 1))); + + ecs_entity_t e1 = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + ecs_entity_t e2 = ecs_insert(world, + ecs_value(Position, {30, 40}), + ecs_value(Velocity, {3, 4})); + ecs_entity_t e3 = ecs_insert(world, + ecs_value(Position, {50, 60}), + ecs_value(Velocity, {5, 6})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v[0].x, 1); test_int(v[0].y, 2); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v[1].x, 3); test_int(v[1].y, 4); + } + + test_uint(e3, it.entities[2]); + { + Position *p = ecs_field_at(&it, Position, 0, 2); + test_assert(p != NULL); + test_int(p->x, 50); test_int(p->y, 60); + } + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v[2].x, 5); test_int(v[2].y, 6); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_2_regular_and_sparse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Velocity), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + test_bool(true, !!(q->row_fields & (1llu << 1))); + + ecs_entity_t e1 = ecs_insert(world, + ecs_value(Position, {10, 20}), + ecs_value(Velocity, {1, 2})); + ecs_entity_t e2 = ecs_insert(world, + ecs_value(Position, {30, 40}), + ecs_value(Velocity, {3, 4})); + ecs_entity_t e3 = ecs_insert(world, + ecs_value(Position, {50, 60}), + ecs_value(Velocity, {5, 6})); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(3, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); test_int(p[0].y, 20); + } + { + Velocity *v = ecs_field_at(&it, Velocity, 1, 0); + test_assert(v != NULL); + test_int(v->x, 1); test_int(v->y, 2); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[1].x, 30); test_int(p[1].y, 40); + } + { + Velocity *v = ecs_field_at(&it, Velocity, 1, 1); + test_assert(v != NULL); + test_int(v->x, 3); test_int(v->y, 4); + } + + test_uint(e3, it.entities[2]); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[2].x, 50); test_int(p[2].y, 60); + } + { + Velocity *v = ecs_field_at(&it, Velocity, 1, 2); + test_assert(v != NULL); + test_int(v->x, 5); test_int(v->y, 6); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_sparse_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + + ecs_entity_t base = ecs_insert(world, {EcsPrefab}, ecs_value(Position, {1, 2})); + ecs_entity_t e1 = ecs_insert(world, {ecs_isa(base)}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {ecs_isa(base)}, ecs_value(Position, {30, 40})); + /* ecs_entity_t e3 = */ ecs_insert(world, {ecs_isa(base)}); + /* ecs_entity_t e4 = */ ecs_insert(world, {ecs_isa(base)}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_sparse_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up IsA)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + + ecs_entity_t base = ecs_insert(world, {EcsPrefab}, ecs_value(Position, {1, 2})); + ecs_entity_t e1 = ecs_insert(world, {ecs_isa(base)}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {ecs_isa(base)}, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, {ecs_isa(base)}); + ecs_entity_t e4 = ecs_insert(world, {ecs_isa(base)}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 1); test_int(p->y, 2); + } + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e3, it.entities[0]); + test_uint(e4, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 1); test_int(p->y, 2); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_sparse_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self|up IsA)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(true, !!(q->row_fields & (1llu << 0))); + + ecs_entity_t base = ecs_insert(world, {EcsPrefab}, ecs_value(Position, {1, 2})); + ecs_entity_t e1 = ecs_insert(world, {ecs_isa(base)}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {ecs_isa(base)}, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, {ecs_isa(base)}); + ecs_entity_t e4 = ecs_insert(world, {ecs_isa(base)}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e3, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 1); test_int(p->y, 2); + } + + test_uint(e4, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 1); test_int(p->y, 2); + } + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 0, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 0, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_sparse_written_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsDontInherit); + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, Position(self)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + test_bool(true, !!(q->row_fields & (1llu << 1))); + + ecs_entity_t base = ecs_insert(world, {EcsPrefab}, ecs_value(Position, {1, 2})); + ecs_entity_t e1 = ecs_insert(world, {ecs_isa(base)}, {Foo}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {ecs_isa(base)}, {Foo}, ecs_value(Position, {30, 40})); + /* ecs_entity_t e3 = */ ecs_insert(world, {Foo}, {ecs_isa(base)}); + /* ecs_entity_t e4 = */ ecs_insert(world, {Foo}, {ecs_isa(base)}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 1, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 1, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_sparse_written_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, Position(up IsA)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + test_bool(true, !!(q->row_fields & (1llu << 1))); + + ecs_entity_t base = ecs_insert(world, {EcsPrefab}, ecs_value(Position, {1, 2})); + ecs_entity_t e1 = ecs_insert(world, {ecs_isa(base)}, {Foo}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {ecs_isa(base)}, {Foo}, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, {ecs_isa(base)}, {Foo}); + ecs_entity_t e4 = ecs_insert(world, {ecs_isa(base)}, {Foo}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 1, 0); + test_assert(p != NULL); + test_int(p->x, 1); test_int(p->y, 2); + } + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e3, it.entities[0]); + test_uint(e4, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 1, 0); + test_assert(p != NULL); + test_int(p->x, 1); test_int(p->y, 2); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_1_sparse_written_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, Position(self|up IsA)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + test_bool(true, !!(q->row_fields & (1llu << 1))); + + ecs_entity_t base = ecs_insert(world, {EcsPrefab}, ecs_value(Position, {1, 2})); + ecs_entity_t e1 = ecs_insert(world, {ecs_isa(base)}, {Foo}, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, {ecs_isa(base)}, {Foo}, ecs_value(Position, {30, 40})); + ecs_entity_t e3 = ecs_insert(world, {ecs_isa(base)}, {Foo}); + ecs_entity_t e4 = ecs_insert(world, {ecs_isa(base)}, {Foo}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e1, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 1, 0); + test_assert(p != NULL); + test_int(p->x, 10); test_int(p->y, 20); + } + + test_uint(e2, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 1, 1); + test_assert(p != NULL); + test_int(p->x, 30); test_int(p->y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e3, it.entities[0]); + { + Position *p = ecs_field_at(&it, Position, 1, 0); + test_assert(p != NULL); + test_int(p->x, 1); test_int(p->y, 2); + } + + test_uint(e4, it.entities[1]); + { + Position *p = ecs_field_at(&it, Position, 1, 1); + test_assert(p != NULL); + test_int(p->x, 1); test_int(p->y, 2); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_sparse_0_src_only_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(#0)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Sparse_sparse_0_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_entity_t e = ecs_new_w(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(#0), Foo", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + test_bool(false, !!(q->row_fields & (1llu << 0))); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Toggle.c b/vendors/flecs/test/query/src/Toggle.c new file mode 100644 index 000000000..ace59e81f --- /dev/null +++ b/vendors/flecs/test/query/src/Toggle.c @@ -0,0 +1,6167 @@ +#include +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void Toggle_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +void Toggle_fixed_src_1_tag_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_add_id(world, Foo, EcsCanToggle); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(e)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e, Foo, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e, Foo, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_fixed_src_1_component_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(e)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, e, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, e, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, e, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_fixed_src_2_tag_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_id(world, Foo, EcsCanToggle); + ecs_add_id(world, Bar, EcsCanToggle); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(e), Bar(e)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, Foo); + ecs_add(world, e, Bar); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(e, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e, Foo, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e, Foo, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(e, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e, Bar, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e, Bar, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(e, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e, Foo, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_fixed_src_2_component_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(e), Velocity(e)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {1, 2}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(e, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, e, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, e, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(e, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, e, Velocity, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, e, Velocity, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(e, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, e, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_fixed_2_src_w_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_id(world, Foo, EcsCanToggle); + ecs_add_id(world, Bar, EcsCanToggle); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(e1), Bar(e2)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Bar); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e1, Foo, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e1, Foo, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e2, Bar, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e2, Bar, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e1, Foo, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_w_fixed_src_w_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_id(world, Foo, EcsCanToggle); + ecs_add_id(world, Bar, EcsCanToggle); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), Bar(e2)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Bar); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e1, Foo, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e1, Foo, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e2, Bar, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e2, Bar, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e1, Foo, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_fixed_src_w_this_w_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_id(world, Foo, EcsCanToggle); + ecs_add_id(world, Bar, EcsCanToggle); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(e1), Bar($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + ecs_add(world, e2, Bar); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e1, Foo, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e1, Foo, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e2, Bar, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e2, Bar, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_id(world, e1, Foo, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_from_nothing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = { + {TagA}, + {TagB, .src.id = EcsIsEntity} + }, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_id(world, TagA); + ecs_add(world, e1, TagB); + ecs_add_id(world, e1, ECS_TOGGLE | TagB); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_field_id(&it, 0), TagA); + test_uint(ecs_field_id(&it, 1), TagB); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_enable_component(world, e1, Position, true); + ecs_enable_component(world, e2, Position, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + + int32_t table_count = 0, count = 0; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e2); + test_assert(it.entities[i] == e1 || it.entities[i] == e3); + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + } + count += it.count; + table_count ++; + } + + test_int(count, 2); + test_int(table_count, 2); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tgt); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t e1 = ecs_new_w_pair(world, ecs_id(Position), Tgt); + ecs_entity_t e2 = ecs_new_w_pair(world, ecs_id(Position), Tgt); + ecs_entity_t e3 = ecs_new_w_pair(world, ecs_id(Position), Tgt); + + ecs_enable_pair(world, e1, Position, Tgt, true); + ecs_enable_pair(world, e2, Position, Tgt, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Position, Tgt)", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t table_count = 0, count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e2); + test_assert(it.entities[i] == e1 || it.entities[i] == e3); + test_assert(ecs_is_enabled_pair(world, it.entities[i], Position, Tgt)); + } + count += it.count; + table_count ++; + } + + test_int(count, 2); + test_int(table_count, 2); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_skip_initial(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_enable_component(world, e1, Position, false); + ecs_enable_component(world, e2, Position, true); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e1); + test_assert(it.entities[i] == e2 || it.entities[i] == e3); + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + } + count += it.count; + } + + test_int(count, 2); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_pair_skip_initial(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tgt); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t e1 = ecs_new_w_pair(world, ecs_id(Position), Tgt); + ecs_entity_t e2 = ecs_new_w_pair(world, ecs_id(Position), Tgt); + ecs_entity_t e3 = ecs_new_w_pair(world, ecs_id(Position), Tgt); + + ecs_enable_pair(world, e1, Position, Tgt, false); + ecs_enable_pair(world, e2, Position, Tgt, true); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Position, Tgt)", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e1); + test_assert(it.entities[i] == e2 || it.entities[i] == e3); + test_assert(ecs_is_enabled_pair(world, it.entities[i], Position, Tgt)); + } + count += it.count; + } + + test_int(count, 2); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_add_id(world, Foo, EcsCanToggle); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_entity_t e3 = ecs_new_w(world, Foo); + + ecs_enable_id(world, e1, Foo, true); + ecs_enable_id(world, e2, Foo, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo", + .cache_kind = cache_kind + }); + + int32_t table_count = 0, count = 0; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e2); + test_assert(it.entities[i] == e1 || it.entities[i] == e3); + test_assert(ecs_is_enabled_id(world, it.entities[i], Foo)); + } + count += it.count; + table_count ++; + } + + test_int(count, 2); + test_int(table_count, 2); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_tag_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_add_id(world, Rel, EcsCanToggle); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, Tgt); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, Tgt); + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, Tgt); + + ecs_enable_pair(world, e1, Rel, Tgt, true); + ecs_enable_pair(world, e2, Rel, Tgt, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, Tgt)", + .cache_kind = cache_kind + }); + + int32_t table_count = 0, count = 0; + ecs_iter_t it = ecs_query_iter(world, q); + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(it.entities[i] != e2); + test_assert(it.entities[i] == e1 || it.entities[i] == e3); + test_assert(ecs_is_enabled_pair(world, it.entities[i], Rel, Tgt)); + } + count += it.count; + table_count ++; + } + + test_int(count, 2); + test_int(table_count, 2); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_tag_pair_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_add_id(world, Rel, EcsCanToggle); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, Rel, TgtA); + + ecs_entity_t e4 = ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e5 = ecs_new_w_pair(world, Rel, TgtB); + ecs_entity_t e6 = ecs_new_w_pair(world, Rel, TgtB); + + ecs_enable_pair(world, e1, Rel, TgtA, true); + ecs_enable_pair(world, e2, Rel, TgtA, false); + ecs_enable_pair(world, e3, Rel, TgtA, true); + + ecs_enable_pair(world, e4, Rel, TgtB, false); + ecs_enable_pair(world, e5, Rel, TgtB, true); + ecs_enable_pair(world, e6, Rel, TgtB, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, *)", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self|up ChildOf)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(p, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(cache_kind == EcsQueryCacheDefault ? p : e, it.entities[0]); + test_uint(cache_kind == EcsQueryCacheDefault ? 0 : p, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(cache_kind == EcsQueryCacheDefault ? e : p, it.entities[0]); + test_uint(cache_kind == EcsQueryCacheDefault ? p : 0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up ChildOf)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(p, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(p, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_self_up_w_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self|up ChildOf), Velocity", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + ecs_set(world, p, Velocity, {1, 2}); + ecs_set(world, e, Velocity, {2, 4}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(p, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(cache_kind == EcsQueryCacheDefault ? p : e, it.entities[0]); + test_uint(cache_kind == EcsQueryCacheDefault ? 0 : p, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(cache_kind == EcsQueryCacheDefault ? 1 : 2, v->x); + test_int(cache_kind == EcsQueryCacheDefault ? 2 : 4, v->y); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(cache_kind == EcsQueryCacheDefault ? e : p, it.entities[0]); + test_uint(cache_kind == EcsQueryCacheDefault ? p : 0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(cache_kind == EcsQueryCacheDefault ? 2 : 1, v->x); + test_int(cache_kind == EcsQueryCacheDefault ? 4 : 2, v->y); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_up_w_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up ChildOf), Velocity", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + ecs_set(world, p, Velocity, {1, 2}); + ecs_set(world, e, Velocity, {2, 4}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(p, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(p, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_self_up_w_self_reverse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Velocity, Position(self|up ChildOf)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + ecs_set(world, p, Velocity, {1, 2}); + ecs_set(world, e, Velocity, {2, 4}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_up_w_self_reverse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Velocity, Position(up ChildOf)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + ecs_set(world, p, Velocity, {1, 2}); + ecs_set(world, e, Velocity, {2, 4}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_self_up_w_self_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self|up ChildOf), Velocity", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + ecs_set(world, p, Velocity, {1, 2}); + ecs_set(world, e, Velocity, {2, 4}); + + ecs_enable_component(world, p, Velocity, true); + ecs_enable_component(world, e, Velocity, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(p, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(cache_kind == EcsQueryCacheDefault ? p : e, it.entities[0]); + test_uint(cache_kind == EcsQueryCacheDefault ? 0 : p, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(cache_kind == EcsQueryCacheDefault ? 1 : 2, v->x); + test_int(cache_kind == EcsQueryCacheDefault ? 2 : 4, v->y); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(cache_kind == EcsQueryCacheDefault ? e : p, it.entities[0]); + test_uint(cache_kind == EcsQueryCacheDefault ? p : 0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(cache_kind == EcsQueryCacheDefault ? 2 : 1, v->x); + test_int(cache_kind == EcsQueryCacheDefault ? 4 : 2, v->y); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_up_w_self_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up ChildOf), Velocity", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + ecs_set(world, p, Velocity, {1, 2}); + ecs_set(world, e, Velocity, {2, 4}); + + ecs_enable_component(world, p, Velocity, true); + ecs_enable_component(world, e, Velocity, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(p, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(p, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + { + const Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + { + const Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_self_up_w_self_toggle_reverse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Velocity, Position(self|up ChildOf)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + ecs_set(world, p, Velocity, {1, 2}); + ecs_set(world, e, Velocity, {2, 4}); + + ecs_enable_component(world, p, Velocity, true); + ecs_enable_component(world, e, Velocity, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_shared_up_w_self_toggle_reverse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + + ecs_query_t *q = ecs_query(world, { + .expr = "Velocity, Position(up ChildOf)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + ecs_set(world, p, Velocity, {1, 2}); + ecs_set(world, e, Velocity, {2, 4}); + + ecs_enable_component(world, p, Velocity, true); + ecs_enable_component(world, e, Velocity, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + const Velocity *v = ecs_field(&it, Velocity, 0); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 4); + } + { + const Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_not_shared_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, p, Foo); + ecs_add(world, e, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, !Position(self|up ChildOf)", + .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(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_not_shared_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, p, Foo); + ecs_add(world, e, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, !Position(up ChildOf)", + .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(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_optional_shared_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, p, Foo); + ecs_add(world, e, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, ?Position(self|up ChildOf)", + .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(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_toggle_optional_shared_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, p, Foo); + ecs_add(world, e, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, ?Position(up ChildOf)", + .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(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_set(world, p, Position, {10, 20}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, false); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_enable_component(world, p, Position, true); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +static +void test_disable_toggle_mod(int32_t total, int32_t mod) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + int32_t i, total_count = 0, total_toggle_count = 0; + for (i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + + if (!(e % mod)) { + ecs_enable_component(world, e, Position, false); + } else { + ecs_enable_component(world, e, Position, true); + total_count ++; + total_toggle_count ++; + } + } + + for (i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + total_count ++; + } + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0, toggle_count = 0; + + while (ecs_query_next(&it)) { + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + + int32_t i; + for (i = 0; i < it.count; i ++) { + if (ecs_table_has_id(world, it.table, ECS_TOGGLE | ecs_id(Position))) { + test_assert(it.entities[i] % mod); + toggle_count ++; + } + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_int(p[i].x, it.entities[i]); + test_int(p[i].y, it.entities[i]); + } + count += it.count; + } + + test_int(toggle_count, total_toggle_count); + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +static +void test_enable_toggle_mod(int32_t total, int32_t mod) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + int32_t i, total_count = 0, total_toggle_count = 0; + for (i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + if (e % mod) { + ecs_enable_component(world, e, Position, false); + } else { + ecs_enable_component(world, e, Position, true); + total_count ++; + total_toggle_count ++; + } + } + + for (i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + total_count ++; + } + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0, toggle_count = 0; + + while (ecs_query_next(&it)) { + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + + int32_t i; + for (i = 0; i < it.count; i ++) { + if (ecs_table_has_id(world, it.table, ECS_TOGGLE | ecs_id(Position))) { + test_assert(!(it.entities[i] % mod)); + toggle_count ++; + } + + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_int(p[i].x, it.entities[i]); + test_int(p[i].y, it.entities[i]); + } + count += it.count; + } + + test_int(toggle_count, total_toggle_count); + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_64_mod_1(void) { + test_disable_toggle_mod(64, 1); +} + +void Toggle_this_64_mod_2(void) { + test_disable_toggle_mod(64, 2); +} + +void Toggle_this_64_mod_3(void) { + test_disable_toggle_mod(64, 3); +} + +void Toggle_this_64_mod_7(void) { + test_disable_toggle_mod(64, 7); +} + +void Toggle_this_64_mod_8(void) { + test_disable_toggle_mod(64, 8); +} + +void Toggle_this_64_mod_10(void) { + test_disable_toggle_mod(64, 10); +} + +void Toggle_this_64_mod_64(void) { + test_disable_toggle_mod(64, 64); +} + +void Toggle_this_64_mod_256(void) { + test_disable_toggle_mod(64, 256); +} + +void Toggle_this_64_mod_1024(void) { + test_disable_toggle_mod(64, 1024); +} + +void Toggle_this_100_mod_1(void) { + test_disable_toggle_mod(100, 1); +} + +void Toggle_this_100_mod_2(void) { + test_disable_toggle_mod(100, 2); +} + +void Toggle_this_100_mod_3(void) { + test_disable_toggle_mod(100, 3); +} + +void Toggle_this_100_mod_7(void) { + test_disable_toggle_mod(100, 7); +} + +void Toggle_this_100_mod_8(void) { + test_disable_toggle_mod(100, 8); +} + +void Toggle_this_100_mod_10(void) { + test_disable_toggle_mod(100, 10); +} + +void Toggle_this_100_mod_64(void) { + test_disable_toggle_mod(100, 64); +} + +void Toggle_this_100_mod_256(void) { + test_disable_toggle_mod(100, 256); +} + +void Toggle_this_100_mod_1024(void) { + test_disable_toggle_mod(100, 1024); +} + +void Toggle_this_128_mod_1(void) { + test_disable_toggle_mod(128, 1); +} + +void Toggle_this_128_mod_2(void) { + test_disable_toggle_mod(128, 2); +} + +void Toggle_this_128_mod_3(void) { + test_disable_toggle_mod(128, 3); +} + +void Toggle_this_128_mod_7(void) { + test_disable_toggle_mod(128, 7); +} + +void Toggle_this_128_mod_8(void) { + test_disable_toggle_mod(128, 8); +} + +void Toggle_this_128_mod_10(void) { + test_disable_toggle_mod(128, 10); +} + +void Toggle_this_128_mod_64(void) { + test_disable_toggle_mod(128, 64); +} + +void Toggle_this_128_mod_256(void) { + test_disable_toggle_mod(128, 256); +} + +void Toggle_this_128_mod_1024(void) { + test_disable_toggle_mod(128, 1024); +} + +void Toggle_this_200_mod_1(void) { + test_disable_toggle_mod(200, 1); +} + +void Toggle_this_200_mod_2(void) { + test_disable_toggle_mod(200, 2); +} + +void Toggle_this_200_mod_3(void) { + test_disable_toggle_mod(200, 3); +} + +void Toggle_this_200_mod_7(void) { + test_disable_toggle_mod(200, 7); +} + +void Toggle_this_200_mod_8(void) { + test_disable_toggle_mod(200, 8); +} + +void Toggle_this_200_mod_10(void) { + test_disable_toggle_mod(200, 10); +} + +void Toggle_this_200_mod_64(void) { + test_disable_toggle_mod(200, 64); +} + +void Toggle_this_200_mod_256(void) { + test_disable_toggle_mod(200, 256); +} + +void Toggle_this_200_mod_1024(void) { + test_disable_toggle_mod(200, 1024); +} + +void Toggle_this_1024_mod_1(void) { + test_disable_toggle_mod(1024, 1); +} + +void Toggle_this_1024_mod_2(void) { + test_disable_toggle_mod(1024, 2); +} + +void Toggle_this_1024_mod_3(void) { + test_disable_toggle_mod(1024, 3); +} + +void Toggle_this_1024_mod_7(void) { + test_disable_toggle_mod(1024, 7); +} + +void Toggle_this_1024_mod_8(void) { + test_disable_toggle_mod(1024, 8); +} + +void Toggle_this_1024_mod_10(void) { + test_disable_toggle_mod(1024, 10); +} + +void Toggle_this_1024_mod_64(void) { + test_disable_toggle_mod(1024, 64); +} + +void Toggle_this_1024_mod_256(void) { + test_disable_toggle_mod(1024, 256); +} + +void Toggle_this_1024_mod_1024(void) { + test_disable_toggle_mod(1024, 1024); +} + +void Toggle_this_enabled_64_mod_1(void) { + test_enable_toggle_mod(64, 1); +} + +void Toggle_this_enabled_64_mod_2(void) { + test_enable_toggle_mod(64, 2); +} + +void Toggle_this_enabled_64_mod_3(void) { + test_enable_toggle_mod(64, 3); +} + +void Toggle_this_enabled_64_mod_7(void) { + test_enable_toggle_mod(64, 7); +} + +void Toggle_this_enabled_64_mod_8(void) { + test_enable_toggle_mod(64, 8); +} + +void Toggle_this_enabled_64_mod_10(void) { + test_enable_toggle_mod(64, 10); +} + +void Toggle_this_enabled_64_mod_64(void) { + test_enable_toggle_mod(64, 64); +} + +void Toggle_this_enabled_64_mod_256(void) { + test_enable_toggle_mod(64, 256); +} + +void Toggle_this_enabled_64_mod_1024(void) { + test_enable_toggle_mod(64, 1024); +} + +void Toggle_this_enabled_100_mod_1(void) { + test_enable_toggle_mod(100, 1); +} + +void Toggle_this_enabled_100_mod_2(void) { + test_enable_toggle_mod(100, 2); +} + +void Toggle_this_enabled_100_mod_3(void) { + test_enable_toggle_mod(100, 3); +} + +void Toggle_this_enabled_100_mod_7(void) { + test_enable_toggle_mod(100, 7); +} + +void Toggle_this_enabled_100_mod_8(void) { + test_enable_toggle_mod(100, 8); +} + +void Toggle_this_enabled_100_mod_10(void) { + test_enable_toggle_mod(100, 10); +} + +void Toggle_this_enabled_100_mod_64(void) { + test_enable_toggle_mod(100, 64); +} + +void Toggle_this_enabled_100_mod_256(void) { + test_enable_toggle_mod(100, 256); +} + +void Toggle_this_enabled_100_mod_1024(void) { + test_enable_toggle_mod(100, 1024); +} + +void Toggle_this_enabled_128_mod_1(void) { + test_enable_toggle_mod(128, 1); +} + +void Toggle_this_enabled_128_mod_2(void) { + test_enable_toggle_mod(128, 2); +} + +void Toggle_this_enabled_128_mod_3(void) { + test_enable_toggle_mod(128, 3); +} + +void Toggle_this_enabled_128_mod_7(void) { + test_enable_toggle_mod(128, 7); +} + +void Toggle_this_enabled_128_mod_8(void) { + test_enable_toggle_mod(128, 8); +} + +void Toggle_this_enabled_128_mod_10(void) { + test_enable_toggle_mod(128, 10); +} + +void Toggle_this_enabled_128_mod_64(void) { + test_enable_toggle_mod(128, 64); +} + +void Toggle_this_enabled_128_mod_256(void) { + test_enable_toggle_mod(128, 256); +} + +void Toggle_this_enabled_128_mod_1024(void) { + test_enable_toggle_mod(128, 1024); +} + +void Toggle_this_enabled_200_mod_1(void) { + test_enable_toggle_mod(200, 1); +} + +void Toggle_this_enabled_200_mod_2(void) { + test_enable_toggle_mod(200, 2); +} + +void Toggle_this_enabled_200_mod_3(void) { + test_enable_toggle_mod(200, 3); +} + +void Toggle_this_enabled_200_mod_7(void) { + test_enable_toggle_mod(200, 7); +} + +void Toggle_this_enabled_200_mod_8(void) { + test_enable_toggle_mod(200, 8); +} + +void Toggle_this_enabled_200_mod_10(void) { + test_enable_toggle_mod(200, 10); +} + +void Toggle_this_enabled_200_mod_64(void) { + test_enable_toggle_mod(200, 64); +} + +void Toggle_this_enabled_200_mod_256(void) { + test_enable_toggle_mod(200, 256); +} + +void Toggle_this_enabled_200_mod_1024(void) { + test_enable_toggle_mod(200, 1024); +} + +void Toggle_this_enabled_1024_mod_1(void) { + test_enable_toggle_mod(1024, 1); +} + +void Toggle_this_enabled_1024_mod_2(void) { + test_enable_toggle_mod(1024, 2); +} + +void Toggle_this_enabled_1024_mod_3(void) { + test_enable_toggle_mod(1024, 3); +} + +void Toggle_this_enabled_1024_mod_7(void) { + test_enable_toggle_mod(1024, 7); +} + +void Toggle_this_enabled_1024_mod_8(void) { + test_enable_toggle_mod(1024, 8); +} + +void Toggle_this_enabled_1024_mod_10(void) { + test_enable_toggle_mod(1024, 10); +} + +void Toggle_this_enabled_1024_mod_64(void) { + test_enable_toggle_mod(1024, 64); +} + +void Toggle_this_enabled_1024_mod_256(void) { + test_enable_toggle_mod(1024, 256); +} + +void Toggle_this_enabled_1024_mod_1024(void) { + test_enable_toggle_mod(1024, 1024); +} + +void Toggle_this_mod_2_2_bitsets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 65536; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + if (!(e % 2)) { + ecs_enable_component(world, e, Position, true); + } else { + ecs_enable_component(world, e, Position, false); + } + + if (!(e % 3)) { + ecs_enable_component(world, e, Velocity, true); + } else { + ecs_enable_component(world, e, Velocity, false); + } + + if (!(e % 2) && !(e % 3)) { + total_count ++; + } + } + + test_assert(total_count != 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ecs_is_enabled(world, it.entities[i], Velocity)); + test_assert(!(it.entities[i] % 2) && !(it.entities[i] % 3)); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_mod_8_2_bitsets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 65536; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + if (!(e % 8)) { + ecs_enable_component(world, e, Position, true); + } else { + ecs_enable_component(world, e, Position, false); + } + + if (!(e % 4)) { + ecs_enable_component(world, e, Velocity, true); + } else { + ecs_enable_component(world, e, Velocity, false); + } + + if (!(e % 8) && !(e % 4)) { + total_count ++; + } + } + + test_assert(total_count != 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ecs_is_enabled(world, it.entities[i], Velocity)); + test_assert(!(it.entities[i] % 8) && !(it.entities[i] % 4)); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_mod_64_2_bitsets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 65536; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + if (!(e % 64)) { + ecs_enable_component(world, e, Position, true); + } else { + ecs_enable_component(world, e, Position, false); + } + + if (!(e % 16)) { + ecs_enable_component(world, e, Velocity, true); + } else { + ecs_enable_component(world, e, Velocity, false); + } + + if (!(e % 64) && !(e % 16)) { + total_count ++; + } + } + + test_assert(total_count != 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ecs_is_enabled(world, it.entities[i], Velocity)); + test_assert(!(it.entities[i] % 64) && !(it.entities[i] % 16)); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_mod_256_2_bitsets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 65536; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + if (!(e % 256)) { + ecs_enable_component(world, e, Position, true); + } else { + ecs_enable_component(world, e, Position, false); + } + + if (!(e % 64)) { + ecs_enable_component(world, e, Velocity, true); + } else { + ecs_enable_component(world, e, Velocity, false); + } + + if (!(e % 256) && !(e % 64)) { + total_count ++; + } + } + + test_assert(total_count != 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ecs_is_enabled(world, it.entities[i], Velocity)); + test_assert(!(it.entities[i] % 256) && !(it.entities[i] % 64)); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_mod_1024_2_bitsets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 65536; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + if (!(e % 1024)) { + ecs_enable_component(world, e, Position, true); + } else { + ecs_enable_component(world, e, Position, false); + } + + if (!(e % 128)) { + ecs_enable_component(world, e, Velocity, true); + } else { + ecs_enable_component(world, e, Velocity, false); + } + + if (!(e % 1024) && !(e % 128)) { + total_count ++; + } + } + + test_assert(total_count != 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ecs_is_enabled(world, it.entities[i], Velocity)); + test_assert(!(it.entities[i] % 1024) && !(it.entities[i] % 128)); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_randomized_2_bitsets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 65536; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + + bool enable_1 = rand() % 2; + ecs_enable_component(world, e, Position, enable_1); + bool enable_2 = rand() % 2; + ecs_enable_component(world, e, Velocity, enable_2); + + if (enable_1 && enable_2) { + total_count ++; + } + } + + test_assert(total_count != 0); + + for (i = 0; i < 65536; i ++) { + ecs_new_w(world, Position); // no match + ecs_new_w(world, Velocity); // no match + + { // match + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + total_count ++; + } + } + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ecs_is_enabled(world, it.entities[i], Velocity)); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_randomized_3_bitsets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + ecs_add_id(world, ecs_id(Mass), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 65536; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + ecs_add(world, e, Mass); + + bool enable_1 = rand() % 2; + ecs_enable_component(world, e, Position, enable_1); + bool enable_2 = rand() % 2; + ecs_enable_component(world, e, Velocity, enable_2); + bool enable_3 = rand() % 2; + ecs_enable_component(world, e, Mass, enable_3); + + if (enable_1 && enable_2 && enable_3) { + total_count ++; + } + } + + test_assert(total_count != 0); + + for (i = 0; i < 65536; i ++) { + ecs_new_w(world, Position); // no match + ecs_new_w(world, Velocity); // no match + ecs_new_w(world, Mass); // no match + + { // match + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + ecs_add(world, e, Mass); + total_count ++; + } + } + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity, Mass", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ecs_is_enabled(world, it.entities[i], Velocity)); + test_assert(ecs_is_enabled(world, it.entities[i], Mass)); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_randomized_4_bitsets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_COMPONENT(world, Rotation); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + ecs_add_id(world, ecs_id(Mass), EcsCanToggle); + ecs_add_id(world, ecs_id(Rotation), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 65536; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + ecs_add(world, e, Mass); + ecs_add(world, e, Rotation); + + bool enable_1 = rand() % 2; + ecs_enable_component(world, e, Position, enable_1); + bool enable_2 = rand() % 2; + ecs_enable_component(world, e, Velocity, enable_2); + bool enable_3 = rand() % 2; + ecs_enable_component(world, e, Mass, enable_3); + bool enable_4 = rand() % 2; + ecs_enable_component(world, e, Rotation, enable_4); + + if (enable_1 && enable_2 && enable_3 && enable_4) { + total_count ++; + } + } + + test_assert(total_count != 0); + + for (i = 0; i < 65536; i ++) { + ecs_new_w(world, Position); // no match + ecs_new_w(world, Velocity); // no match + ecs_new_w(world, Mass); // no match + ecs_new_w(world, Rotation); // no match + + { // match + ecs_entity_t e = ecs_new_w(world, Position); + ecs_add(world, e, Velocity); + ecs_add(world, e, Mass); + ecs_add(world, e, Rotation); + total_count ++; + } + } + + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity, Mass, Rotation", + .cache_kind = cache_kind + }); + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ecs_is_enabled(world, it.entities[i], Velocity)); + test_assert(ecs_is_enabled(world, it.entities[i], Mass)); + test_assert(ecs_is_enabled(world, it.entities[i], Rotation)); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_w_other_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 1024; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + + if (!(e % 3)) { + ecs_enable_component(world, e, Position, false); + if (!(e % 5)) { + ecs_add(world, e, Tag); + } + } else { + ecs_enable_component(world, e, Position, true); + if (!(e % 5)) { + ecs_add(world, e, Tag); + total_count ++; + } + } + } + + test_assert(total_count != 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Tag", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(it.entities[i] % 3); + test_assert(!(it.entities[i] % 5)); + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_int(p[i].x, it.entities[i]); + test_int(p[i].y, it.entities[i]); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_w_other_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + int32_t i, total_count = 0; + for (i = 0; i < 1024; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + + if (!(e % 3)) { + ecs_enable_component(world, e, Position, false); + if (!(e % 5)) { + ecs_set(world, e, Velocity, {e * 2, e * 2}); + } + } else { + ecs_enable_component(world, e, Position, true); + if (!(e % 5)) { + ecs_set(world, e, Velocity, {e * 2, e * 2}); + total_count ++; + } + } + } + + test_assert(total_count != 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Velocity", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + + int32_t count = 0; + + while (ecs_query_next(&it)) { + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + + int32_t i; + for (i = 0; i < it.count; i ++) { + test_assert(it.entities[i] % 3); + test_assert(!(it.entities[i] % 5)); + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_int(p[i].x, it.entities[i]); + test_int(p[i].y, it.entities[i]); + test_int(v[i].x, it.entities[i] * 2); + test_int(v[i].y, it.entities[i] * 2); + } + count += it.count; + } + + test_int(count, total_count); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + for (int i = 0; i < 1024; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + + if (!(e % 3)) { + ecs_enable_component(world, e, Position, false); + } else { + ecs_enable_component(world, e, Position, true); + } + } + + ecs_query_t *q = ecs_query(world, { + .expr = "!Position", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0, pos_count = 0; + + while (ecs_query_next(&it)) { + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + + int32_t i; + for (i = 0; i < it.count; i ++) { + if (ecs_has(world, it.entities[i], Position)) { + pos_count ++; + } + + test_assert(!ecs_is_enabled( + world, it.entities[i], Position)); + } + + count += it.count; + } + + test_assert(count != 0); + test_assert(pos_count != 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +static +void this_written_not(int total, int mod) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + int i, total_without_pos = 0; + for (i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + + if (!(e % mod)) { + ecs_enable_component(world, e, Position, false); + total_without_pos ++; + } else { + ecs_enable_component(world, e, Position, true); + } + + ecs_add(world, e, Tag); + } + + for (int i = 0; i < total; i ++) { + ecs_new_w(world, Tag); + total_without_pos ++; + } + + for (int i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + ecs_add(world, e, Tag); + } + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, !Position", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0, pos_count = 0; + + while (ecs_query_next(&it)) { + test_uint(Tag, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + + int32_t i; + for (i = 0; i < it.count; i ++) { + if (ecs_has(world, it.entities[i], Position)) { + pos_count ++; + test_assert(!(it.entities[i] % mod)); + } + + test_assert(!ecs_is_enabled( + world, it.entities[i], Position)); + } + + count += it.count; + } + + test_assert(count != 0); + test_assert(pos_count != 0); + test_int(count, total_without_pos); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_not_1024_mod_2(void) { + this_written_not(1024, 2); +} + +void Toggle_this_written_not_1024_mod_3(void) { + this_written_not(1024, 3); +} + +void Toggle_this_written_not_1024_mod_7(void) { + this_written_not(1024, 7); +} + +void Toggle_this_written_not_1024_mod_8(void) { + this_written_not(1024, 8); +} + +void Toggle_this_written_not_1024_mod_10(void) { + this_written_not(1024, 10); +} + +void Toggle_this_written_not_1024_mod_64(void) { + this_written_not(1024, 64); +} + +void Toggle_this_written_not_1024_mod_256(void) { + this_written_not(1024, 256); +} + +void Toggle_this_written_not_1024_mod_1024(void) { + this_written_not(1024, 1024); +} + +static +void this_optional(int total, int mod) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + int i, total_with_pos = 0; + for (i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + + if (!(e % mod)) { + ecs_enable_component(world, e, Position, true); + total_with_pos ++; + } else { + ecs_enable_component(world, e, Position, false); + } + } + + for (int i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + total_with_pos ++; + } + + ecs_query_t *q = ecs_query(world, { + .expr = "?Position", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0, with_pos_count = 0; + + while (ecs_query_next(&it)) { + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + Position *ptr = ecs_field(&it, Position, 0); + + if (ecs_table_has_id(world, it.table, ECS_TOGGLE | ecs_id(Position))) { + for (int32_t i = 0; i < it.count; i ++) { + if (ecs_is_enabled(world, it.entities[i], Position)) { + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(ptr != NULL); + test_assert(ptr[i].x == it.entities[i]); + test_assert(ptr[i].y == it.entities[i]); + with_pos_count ++; + } else { + test_bool(false, ecs_field_is_set(&it, 0)); + } + } + } else { + if (ecs_table_has_id(world, it.table, ecs_id(Position))) { + test_bool(true, ecs_field_is_set(&it, 0)); + test_assert(ptr != NULL); + for (int32_t i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ptr[i].x == it.entities[i]); + test_assert(ptr[i].y == it.entities[i]); + with_pos_count ++; + } + } else { + test_bool(false, ecs_field_is_set(&it, 0)); + for (int32_t i = 0; i < it.count; i ++) { + test_assert(!ecs_is_enabled(world, it.entities[i], Position)); + } + } + } + + count += it.count; + } + + test_assert(count != 0); + test_int(with_pos_count, total_with_pos); + + ecs_query_fini(q); + + ecs_fini(world); +} + +static +void this_written_optional(int total, int mod) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + int i, total_with_pos = 0, total_without_pos = 0; + for (i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + + if (!(e % mod)) { + ecs_enable_component(world, e, Position, true); + total_with_pos ++; + } else { + ecs_enable_component(world, e, Position, false); + total_without_pos ++; + } + + ecs_add(world, e, Tag); + } + + for (int i = 0; i < total; i ++) { + ecs_new_w(world, Tag); + total_without_pos ++; + } + + for (int i = 0; i < total; i ++) { + ecs_entity_t e = ecs_new_w(world, Position); + ecs_set(world, e, Position, {e, e}); + ecs_add(world, e, Tag); + total_with_pos ++; + } + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, ?Position", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0, with_pos_count = 0, without_pos_count = 0; + + while (ecs_query_next(&it)) { + test_uint(Tag, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + Position *ptr = ecs_field(&it, Position, 1); + + if (ecs_table_has_id(world, it.table, ECS_TOGGLE | ecs_id(Position))) { + for (int32_t i = 0; i < it.count; i ++) { + if (ecs_is_enabled(world, it.entities[i], Position)) { + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(ptr != NULL); + test_assert(ptr[i].x == it.entities[i]); + test_assert(ptr[i].y == it.entities[i]); + with_pos_count ++; + } else { + test_bool(false, ecs_field_is_set(&it, 1)); + without_pos_count ++; + } + } + } else { + if (ecs_table_has_id(world, it.table, ecs_id(Position))) { + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(ptr != NULL); + for (int32_t i = 0; i < it.count; i ++) { + test_assert(ecs_is_enabled(world, it.entities[i], Position)); + test_assert(ptr[i].x == it.entities[i]); + test_assert(ptr[i].y == it.entities[i]); + with_pos_count ++; + } + } else { + test_bool(false, ecs_field_is_set(&it, 1)); + for (int32_t i = 0; i < it.count; i ++) { + test_assert(!ecs_is_enabled(world, it.entities[i], Position)); + without_pos_count ++; + } + } + } + + count += it.count; + } + + test_assert(count != 0); + test_int(count, (with_pos_count + without_pos_count)); + test_int(with_pos_count, total_with_pos); + test_int(without_pos_count, total_without_pos); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_optional(void) { + this_optional(1024, 2); +} + +void Toggle_this_written_optional_1024_mod_2(void) { + this_written_optional(1024, 2); +} + +void Toggle_this_written_optional_1024_mod_3(void) { + this_written_optional(1024, 3); +} + +void Toggle_this_written_optional_1024_mod_7(void) { + this_written_optional(1024, 7); +} + +void Toggle_this_written_optional_1024_mod_8(void) { + this_written_optional(1024, 8); +} + +void Toggle_this_written_optional_1024_mod_10(void) { + this_written_optional(1024, 10); +} + +void Toggle_this_written_optional_1024_mod_64(void) { + this_written_optional(1024, 64); +} + +void Toggle_this_written_optional_1024_mod_256(void) { + this_written_optional(1024, 256); +} + +void Toggle_this_written_optional_1024_mod_1024(void) { + this_written_optional(1024, 1024); +} + +static +int compare_position(ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2) { + const Position *p1 = ptr1; + const Position *p2 = ptr2; + return (p1->x > p2->x) - (p1->x < p2->x); +} + +void Toggle_this_sort(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 2})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {2, 2})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {0, 2})); + + ecs_enable_component(world, e1, Position, true); + ecs_enable_component(world, e2, Position, true); + ecs_enable_component(world, e3, Position, false); + ecs_enable_component(world, e4, Position, false); + + test_bool(ecs_is_enabled(world, e1, Position), true); + test_bool(ecs_is_enabled(world, e2, Position), true); + test_bool(ecs_is_enabled(world, e3, Position), false); + test_bool(ecs_is_enabled(world, e4, Position), false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .order_by = ecs_id(Position), + .order_by_callback = compare_position, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(it.count, 2); + test_assert(it.entities[0] == e2); + test_assert(it.entities[1] == e1); + test_assert(!ecs_query_next(&it)); + + /* Entities will have shuffled around, ensure bits got shuffled too */ + test_bool(ecs_is_enabled(world, e1, Position), true); + test_bool(ecs_is_enabled(world, e2, Position), true); + test_bool(ecs_is_enabled(world, e3, Position), false); + test_bool(ecs_is_enabled(world, e4, Position), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_table_move_2_from_3(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position", + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + + ecs_enable_component(world, e1, Position, true); + ecs_enable_component(world, e2, Position, false); + ecs_enable_component(world, e3, Position, true); + + test_bool(ecs_is_enabled(world, e1, Position), true); + test_bool(ecs_is_enabled(world, e2, Position), false); + test_bool(ecs_is_enabled(world, e3, Position), true); + + ecs_add(world, e3, Tag); + ecs_add(world, e2, Tag); + + test_bool(ecs_is_enabled(world, e1, Position), true); + test_bool(ecs_is_enabled(world, e2, Position), false); + test_bool(ecs_is_enabled(world, e3, Position), true); + + ecs_iter_t it = ecs_query_iter(world, q); + { + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 10); test_int(p->y, 20); + } + { + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_int(p->x, 30); test_int(p->y, 40); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_toggle_w_not_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + /* ecs_entity_t e2 = */ ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + + /* Toggle Velocity == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + + /* Toggle Position == true, Velocity == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + + /* Toggle Position == true, Velocity == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + + /* Toggle Position == false, Velocity == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + + /* Toggle Position == false, Velocity == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, Position, !Velocity", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e9, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_not_toggle_w_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + /* ecs_entity_t e2 = */ ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + + /* Toggle Velocity == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + + /* Toggle Position == true, Velocity == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + + /* Toggle Position == true, Velocity == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + + /* Toggle Position == false, Velocity == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + + /* Toggle Position == false, Velocity == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, !Velocity, Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e9, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_toggle_w_optional_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + /* ecs_entity_t e2 = */ ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + + /* Toggle Velocity == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + + /* Toggle Position == true, Velocity == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + + /* Toggle Position == true, Velocity == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + + /* Toggle Position == false, Velocity == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + + /* Toggle Position == false, Velocity == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, Position, ?Velocity", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e8, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e9, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_optional_toggle_w_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + /* ecs_entity_t e2 = */ ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + + /* Toggle Velocity == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + + /* Toggle Position == true, Velocity == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + + /* Toggle Position == true, Velocity == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + + /* Toggle Position == false, Velocity == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + + /* Toggle Position == false, Velocity == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, ?Velocity, Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e8, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e9, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_not_w_optional_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + ecs_entity_t e2 = ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + + /* Toggle Velocity == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + + /* Toggle Position == true, Velocity == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + + /* Toggle Position == true, Velocity == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + + /* Toggle Position == false, Velocity == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + + /* Toggle Position == false, Velocity == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, !Velocity, ?Position", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e9, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e11, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Position), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_optional_w_not_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + ecs_entity_t e2 = ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + + /* Toggle Velocity == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + + /* Toggle Position == true, Velocity == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + + /* Toggle Position == true, Velocity == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + + /* Toggle Position == false, Velocity == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + + /* Toggle Position == false, Velocity == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, ?Position, !Velocity", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e9, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e11, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_2_not_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + ecs_entity_t e2 = ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + + /* Toggle Velocity == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + + /* Toggle Position == true, Velocity == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + + /* Toggle Position == true, Velocity == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + + /* Toggle Position == false, Velocity == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + + /* Toggle Position == false, Velocity == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, !Position, !Velocity", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e11, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_2_optional_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + ecs_entity_t e2 = ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + + /* Toggle Velocity == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + + /* Toggle Position == true, Velocity == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + + /* Toggle Position == true, Velocity == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + + /* Toggle Position == false, Velocity == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + + /* Toggle Position == false, Velocity == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, ?Position, ?Velocity", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e8, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e9, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e10, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e11, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_toggle_w_2_not_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + ecs_add_id(world, ecs_id(Mass), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + /* ecs_entity_t e2 = */ ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + ecs_add(world, e3, Mass); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true, Mass = true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + ecs_add(world, e6, Mass); + ecs_enable_component(world, e6, Mass, true); + + /* Toggle Velocity == false, Mass == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + ecs_add(world, e7, Mass); + ecs_enable_component(world, e7, Mass, false); + + /* Toggle Position == true, Velocity == true, Mass == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_add(world, e8, Mass); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + ecs_enable_component(world, e8, Mass, true); + + /* Toggle Position == true, Velocity == false, Mass == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_add(world, e9, Mass); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + ecs_enable_component(world, e9, Mass, false); + + /* Toggle Position == false, Velocity == true, Mass == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_add(world, e10, Mass); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + ecs_enable_component(world, e10, Mass, true); + + /* Toggle Position == false, Velocity == false, Mass == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_add(world, e11, Mass); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + ecs_enable_component(world, e11, Mass, false); + + /* Toggle Position == true, Velocity == true, Mass == true */ + ecs_entity_t e12 = ecs_new_w(world, Tag); + ecs_add(world, e12, Position); + ecs_add(world, e12, Velocity); + ecs_add(world, e12, Mass); + ecs_enable_component(world, e12, Position, true); + ecs_enable_component(world, e12, Velocity, true); + ecs_enable_component(world, e12, Mass, false); + + /* Toggle Position == true, Velocity == false, Mass == true */ + ecs_entity_t e13 = ecs_new_w(world, Tag); + ecs_add(world, e13, Position); + ecs_add(world, e13, Velocity); + ecs_add(world, e13, Mass); + ecs_enable_component(world, e13, Position, true); + ecs_enable_component(world, e13, Velocity, false); + ecs_enable_component(world, e13, Mass, true); + + /* Toggle Position == false, Velocity == true, Mass == false */ + ecs_entity_t e14 = ecs_new_w(world, Tag); + ecs_add(world, e14, Position); + ecs_add(world, e14, Velocity); + ecs_add(world, e14, Mass); + ecs_enable_component(world, e14, Position, false); + ecs_enable_component(world, e14, Velocity, true); + ecs_enable_component(world, e14, Mass, false); + + /* Toggle Position == false, Velocity == false, Mass == true */ + ecs_entity_t e15 = ecs_new_w(world, Tag); + ecs_add(world, e15, Position); + ecs_add(world, e15, Velocity); + ecs_add(world, e15, Mass); + ecs_enable_component(world, e15, Position, false); + ecs_enable_component(world, e15, Velocity, false); + ecs_enable_component(world, e15, Mass, true); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, Position, !Velocity, !Mass", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e9, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_toggle_w_2_optional_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + ecs_add_id(world, ecs_id(Mass), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + /* ecs_entity_t e2 = */ ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + ecs_add(world, e3, Mass); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true, Mass = true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + ecs_add(world, e6, Mass); + ecs_enable_component(world, e6, Mass, true); + + /* Toggle Velocity == false, Mass == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + ecs_add(world, e7, Mass); + ecs_enable_component(world, e7, Mass, false); + + /* Toggle Position == true, Velocity == true, Mass == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_add(world, e8, Mass); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + ecs_enable_component(world, e8, Mass, true); + + /* Toggle Position == true, Velocity == false, Mass == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_add(world, e9, Mass); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + ecs_enable_component(world, e9, Mass, false); + + /* Toggle Position == false, Velocity == true, Mass == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_add(world, e10, Mass); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + ecs_enable_component(world, e10, Mass, true); + + /* Toggle Position == false, Velocity == false, Mass == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_add(world, e11, Mass); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + ecs_enable_component(world, e11, Mass, false); + + /* Toggle Position == true, Velocity == true, Mass == true */ + ecs_entity_t e12 = ecs_new_w(world, Tag); + ecs_add(world, e12, Position); + ecs_add(world, e12, Velocity); + ecs_add(world, e12, Mass); + ecs_enable_component(world, e12, Position, true); + ecs_enable_component(world, e12, Velocity, true); + ecs_enable_component(world, e12, Mass, false); + + /* Toggle Position == true, Velocity == false, Mass == true */ + ecs_entity_t e13 = ecs_new_w(world, Tag); + ecs_add(world, e13, Position); + ecs_add(world, e13, Velocity); + ecs_add(world, e13, Mass); + ecs_enable_component(world, e13, Position, true); + ecs_enable_component(world, e13, Velocity, false); + ecs_enable_component(world, e13, Mass, true); + + /* Toggle Position == false, Velocity == true, Mass == false */ + ecs_entity_t e14 = ecs_new_w(world, Tag); + ecs_add(world, e14, Position); + ecs_add(world, e14, Velocity); + ecs_add(world, e14, Mass); + ecs_enable_component(world, e14, Position, false); + ecs_enable_component(world, e14, Velocity, true); + ecs_enable_component(world, e14, Mass, false); + + /* Toggle Position == false, Velocity == false, Mass == true */ + ecs_entity_t e15 = ecs_new_w(world, Tag); + ecs_add(world, e15, Position); + ecs_add(world, e15, Velocity); + ecs_add(world, e15, Mass); + ecs_enable_component(world, e15, Position, false); + ecs_enable_component(world, e15, Velocity, false); + ecs_enable_component(world, e15, Mass, true); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, Position, ?Velocity, ?Mass", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e8, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e9, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e12, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e13, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_2_toggle_w_not_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + ecs_add_id(world, ecs_id(Mass), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + /* ecs_entity_t e2 = */ ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + ecs_add(world, e3, Mass); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true, Mass = true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + ecs_add(world, e6, Mass); + ecs_enable_component(world, e6, Mass, true); + + /* Toggle Velocity == false, Mass == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + ecs_add(world, e7, Mass); + ecs_enable_component(world, e7, Mass, false); + + /* Toggle Position == true, Velocity == true, Mass == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_add(world, e8, Mass); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + ecs_enable_component(world, e8, Mass, true); + + /* Toggle Position == true, Velocity == false, Mass == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_add(world, e9, Mass); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + ecs_enable_component(world, e9, Mass, false); + + /* Toggle Position == false, Velocity == true, Mass == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_add(world, e10, Mass); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + ecs_enable_component(world, e10, Mass, true); + + /* Toggle Position == false, Velocity == false, Mass == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_add(world, e11, Mass); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + ecs_enable_component(world, e11, Mass, false); + + /* Toggle Position == true, Velocity == true, Mass == true */ + ecs_entity_t e12 = ecs_new_w(world, Tag); + ecs_add(world, e12, Position); + ecs_add(world, e12, Velocity); + ecs_add(world, e12, Mass); + ecs_enable_component(world, e12, Position, true); + ecs_enable_component(world, e12, Velocity, true); + ecs_enable_component(world, e12, Mass, false); + + /* Toggle Position == true, Velocity == false, Mass == true */ + ecs_entity_t e13 = ecs_new_w(world, Tag); + ecs_add(world, e13, Position); + ecs_add(world, e13, Velocity); + ecs_add(world, e13, Mass); + ecs_enable_component(world, e13, Position, true); + ecs_enable_component(world, e13, Velocity, false); + ecs_enable_component(world, e13, Mass, true); + + /* Toggle Position == false, Velocity == true, Mass == false */ + ecs_entity_t e14 = ecs_new_w(world, Tag); + ecs_add(world, e14, Position); + ecs_add(world, e14, Velocity); + ecs_add(world, e14, Mass); + ecs_enable_component(world, e14, Position, false); + ecs_enable_component(world, e14, Velocity, true); + ecs_enable_component(world, e14, Mass, false); + + /* Toggle Position == false, Velocity == false, Mass == true */ + ecs_entity_t e15 = ecs_new_w(world, Tag); + ecs_add(world, e15, Position); + ecs_add(world, e15, Velocity); + ecs_add(world, e15, Mass); + ecs_enable_component(world, e15, Position, false); + ecs_enable_component(world, e15, Velocity, false); + ecs_enable_component(world, e15, Mass, true); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, Position, Velocity, !Mass", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e12, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_this_written_2_toggle_w_optional_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + ECS_TAG(world, Tag); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + ecs_add_id(world, ecs_id(Velocity), EcsCanToggle); + ecs_add_id(world, ecs_id(Mass), EcsCanToggle); + + /* No toggles */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_add(world, e1, Position); + + /* No components */ + /* ecs_entity_t e2 = */ ecs_new_w(world, Tag); + + /* Components, No toggles */ + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_add(world, e3, Position); + ecs_add(world, e3, Velocity); + ecs_add(world, e3, Mass); + + /* Toggle Position == true */ + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_add(world, e4, Position); + ecs_enable_component(world, e4, Position, true); + + /* Toggle Position == false */ + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_add(world, e5, Position); + ecs_enable_component(world, e5, Position, false); + + /* Toggle Velocity == true, Mass = true */ + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_add(world, e6, Position); + ecs_add(world, e6, Velocity); + ecs_enable_component(world, e6, Velocity, true); + ecs_add(world, e6, Mass); + ecs_enable_component(world, e6, Mass, true); + + /* Toggle Velocity == false, Mass == false */ + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_add(world, e7, Position); + ecs_add(world, e7, Velocity); + ecs_enable_component(world, e7, Velocity, false); + ecs_add(world, e7, Mass); + ecs_enable_component(world, e7, Mass, false); + + /* Toggle Position == true, Velocity == true, Mass == true */ + ecs_entity_t e8 = ecs_new_w(world, Tag); + ecs_add(world, e8, Position); + ecs_add(world, e8, Velocity); + ecs_add(world, e8, Mass); + ecs_enable_component(world, e8, Position, true); + ecs_enable_component(world, e8, Velocity, true); + ecs_enable_component(world, e8, Mass, true); + + /* Toggle Position == true, Velocity == false, Mass == false */ + ecs_entity_t e9 = ecs_new_w(world, Tag); + ecs_add(world, e9, Position); + ecs_add(world, e9, Velocity); + ecs_add(world, e9, Mass); + ecs_enable_component(world, e9, Position, true); + ecs_enable_component(world, e9, Velocity, false); + ecs_enable_component(world, e9, Mass, false); + + /* Toggle Position == false, Velocity == true, Mass == true */ + ecs_entity_t e10 = ecs_new_w(world, Tag); + ecs_add(world, e10, Position); + ecs_add(world, e10, Velocity); + ecs_add(world, e10, Mass); + ecs_enable_component(world, e10, Position, false); + ecs_enable_component(world, e10, Velocity, true); + ecs_enable_component(world, e10, Mass, true); + + /* Toggle Position == false, Velocity == false, Mass == false */ + ecs_entity_t e11 = ecs_new_w(world, Tag); + ecs_add(world, e11, Position); + ecs_add(world, e11, Velocity); + ecs_add(world, e11, Mass); + ecs_enable_component(world, e11, Position, false); + ecs_enable_component(world, e11, Velocity, false); + ecs_enable_component(world, e11, Mass, false); + + /* Toggle Position == true, Velocity == true, Mass == true */ + ecs_entity_t e12 = ecs_new_w(world, Tag); + ecs_add(world, e12, Position); + ecs_add(world, e12, Velocity); + ecs_add(world, e12, Mass); + ecs_enable_component(world, e12, Position, true); + ecs_enable_component(world, e12, Velocity, true); + ecs_enable_component(world, e12, Mass, false); + + /* Toggle Position == true, Velocity == false, Mass == true */ + ecs_entity_t e13 = ecs_new_w(world, Tag); + ecs_add(world, e13, Position); + ecs_add(world, e13, Velocity); + ecs_add(world, e13, Mass); + ecs_enable_component(world, e13, Position, true); + ecs_enable_component(world, e13, Velocity, false); + ecs_enable_component(world, e13, Mass, true); + + /* Toggle Position == false, Velocity == true, Mass == false */ + ecs_entity_t e14 = ecs_new_w(world, Tag); + ecs_add(world, e14, Position); + ecs_add(world, e14, Velocity); + ecs_add(world, e14, Mass); + ecs_enable_component(world, e14, Position, false); + ecs_enable_component(world, e14, Velocity, true); + ecs_enable_component(world, e14, Mass, false); + + /* Toggle Position == false, Velocity == false, Mass == true */ + ecs_entity_t e15 = ecs_new_w(world, Tag); + ecs_add(world, e15, Position); + ecs_add(world, e15, Velocity); + ecs_add(world, e15, Mass); + ecs_enable_component(world, e15, Position, false); + ecs_enable_component(world, e15, Velocity, false); + ecs_enable_component(world, e15, Mass, true); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag, Position, Velocity, ?Mass", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e8, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 3)); + + test_bool(true, ecs_iter_next(&it)); + test_int(1, it.count); + test_uint(e12, it.entities[0]); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 3)); + + test_bool(false, ecs_iter_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_toggle_0_src_only_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(#0)", + .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(0, it.count); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Toggle_toggle_0_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_entity_t e = ecs_new_w(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(#0), Foo", + .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(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/addons/src/RulesTransitive.c b/vendors/flecs/test/query/src/Transitive.c similarity index 57% rename from vendors/flecs/test/addons/src/RulesTransitive.c rename to vendors/flecs/test/query/src/Transitive.c index 2d84854f7..f2365b93b 100644 --- a/vendors/flecs/test/addons/src/RulesTransitive.c +++ b/vendors/flecs/test/query/src/Transitive.c @@ -1,4 +1,4 @@ -#include +#include ECS_ENTITY_DECLARE(LocatedIn); ECS_TAG_DECLARE(Earth); @@ -29,268 +29,323 @@ void populate_facts(ecs_world_t *world) { ECS_ENTITY_DEFINE(world, Amsterdam, Location, (LocatedIn, NoordHolland)); } -void RulesTransitive_1_fact_0_lvl_true(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_fact_0_lvl_true(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, SanFrancisco)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_fact_1_lvl_true(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_fact_1_lvl_true(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, California)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_fact_2_lvl_true(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_fact_2_lvl_true(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, UnitedStates)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_fact_0_lvl_false(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_fact_0_lvl_false(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, Seattle)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_fact_1_lvl_false(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_fact_1_lvl_false(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, Washington)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_fact_2_lvl_false(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_fact_2_lvl_false(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, Netherlands)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_fact_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_fact_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ecs_add_id(world, LocatedIn, EcsReflexive); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, e1)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, e1), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, e1), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, e1), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, e1), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); + + ecs_fini(world); +} + +void Transitive_1_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, BaseA, 0); + ECS_ENTITY(world, BaseB, 0); + ECS_ENTITY(world, e1, (IsA, BaseB)); + ECS_ENTITY(world, e2, (IsA, BaseA)); + + ecs_query_t *q = ecs_query(world, { + .expr = "(IsA, BaseA)" + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(BaseA, it.entities[0]); + test_uint(ecs_isa(BaseA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_isa(BaseA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Transitive_1_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, BaseA, 0); + ECS_ENTITY(world, BaseB, 0); + ECS_ENTITY(world, e1, (ChildOf, BaseB)); + ECS_ENTITY(world, e2, (ChildOf, BaseA)); + + ecs_query_t *q = ecs_query(world, { + .expr = "(ChildOf, BaseA)" + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_childof(BaseA), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); ecs_fini(world); } -void RulesTransitive_1_this_src_written_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_written_0_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); populate_facts(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), LocatedIn($this, SanFrancisco)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -300,46 +355,46 @@ void RulesTransitive_1_this_src_written_0_lvl(void) { ecs_add_pair(world, e5, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); test_uint(e1, it.entities[0]); test_uint(e5, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_this_src_written_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_written_1_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); populate_facts(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), LocatedIn($this, California)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -349,54 +404,54 @@ void RulesTransitive_1_this_src_written_1_lvl(void) { ecs_add_pair(world, e5, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); test_uint(e1, it.entities[0]); test_uint(e5, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); test_uint(e2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_this_src_written_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_written_2_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); populate_facts(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), LocatedIn($this, UnitedStates)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -406,42 +461,42 @@ void RulesTransitive_1_this_src_written_2_lvl(void) { ecs_add_pair(world, e5, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); test_uint(e1, it.entities[0]); test_uint(e5, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); test_uint(e2, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); test_uint(e3, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_this_src_written_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_written_reflexive(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); @@ -449,36 +504,36 @@ void RulesTransitive_1_this_src_written_reflexive(void) { ecs_add_id(world, LocatedIn, EcsReflexive); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - ecs_entity_t e8 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_entity_t e8 = ecs_new_w(world, Tag); ecs_set_name(world, e1, "e1"); ecs_set_name(world, e2, "e2"); ecs_set_name(world, e3, "e3"); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), LocatedIn($this, e2)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); test_uint(e2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -491,55 +546,55 @@ void RulesTransitive_1_this_src_written_reflexive(void) { ecs_add_pair(world, e8, LocatedIn, e2); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); test_uint(e2, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); test_uint(e7, it.entities[0]); test_uint(e8, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_this_src_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_0_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - ecs_entity_t e5 = ecs_new_id(world); - ecs_entity_t e6 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, SanFrancisco)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -552,43 +607,43 @@ void RulesTransitive_1_this_src_0_lvl(void) { ecs_add(world, e2, Tag); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e1, it.entities[0]); test_uint(e6, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e2, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_this_src_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_1_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - ecs_entity_t e5 = ecs_new_id(world); - ecs_entity_t e6 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, California)" }); @@ -604,56 +659,56 @@ void RulesTransitive_1_this_src_1_lvl(void) { ecs_add(world, e2, Tag); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e1, it.entities[0]); test_uint(e6, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e2, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e3, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_this_src_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_2_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - ecs_entity_t e5 = ecs_new_id(world); - ecs_entity_t e6 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, UnitedStates)" }); @@ -669,62 +724,62 @@ void RulesTransitive_1_this_src_2_lvl(void) { ecs_add(world, e2, Tag); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e1, it.entities[0]); test_uint(e6, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e2, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e4, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_this_src_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); @@ -732,14 +787,14 @@ void RulesTransitive_1_this_src_reflexive(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - ecs_entity_t e5 = ecs_new_id(world); - ecs_entity_t e6 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, California)" }); @@ -755,208 +810,208 @@ void RulesTransitive_1_this_src_reflexive(void) { ecs_add(world, e2, Tag); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e1, it.entities[0]); test_uint(e6, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e2, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(e3, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_ent_src_tgt_var_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_ent_src_tgt_var_0_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, UnitedStates); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_ent_src_tgt_var_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_ent_src_tgt_var_1_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, California); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_ent_src_tgt_var_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_ent_src_tgt_var_2_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_ent_src_tgt_var_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_ent_src_tgt_var_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); @@ -964,191 +1019,191 @@ void RulesTransitive_1_ent_src_tgt_var_reflexive(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, e1), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, e1), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_this_src_tgt_var(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_tgt_var(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(UnitedStates, it.entities[0]); test_uint(Netherlands, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_this_src_tgt_var_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_this_src_tgt_var_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); @@ -1156,218 +1211,218 @@ void RulesTransitive_1_this_src_tgt_var_reflexive(void) { ECS_TAG(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Amsterdam, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Seattle, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(LosAngeles, ecs_iter_get_var(&it, x_var)); test_uint(LosAngeles, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(Washington, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(Netherlands, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(UnitedStates, it.entities[0]); test_uint(Netherlands, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_written_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_written_0_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); populate_facts(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($r), LocatedIn($r, SanFrancisco)" }); test_assert(r != NULL); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -1377,56 +1432,56 @@ void RulesTransitive_1_var_src_written_0_lvl(void) { ecs_add_pair(world, e5, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); test_uint(e1, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); test_uint(e5, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_written_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_written_1_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); populate_facts(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($r), LocatedIn($r, California)" }); test_assert(r != NULL); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -1436,64 +1491,64 @@ void RulesTransitive_1_var_src_written_1_lvl(void) { ecs_add_pair(world, e5, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); test_uint(e1, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); test_uint(e5, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); test_uint(e2, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_written_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_written_2_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); populate_facts(world); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($r), LocatedIn($r, UnitedStates)" }); test_assert(r != NULL); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -1503,49 +1558,49 @@ void RulesTransitive_1_var_src_written_2_lvl(void) { ecs_add_pair(world, e5, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); test_uint(e1, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); test_uint(e5, ecs_field_src(&it, 1)); - test_uint(e5, ecs_field_src(&it, 2)); test_uint(e5, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); test_uint(e2, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); test_uint(e3, ecs_field_src(&it, 1)); - test_uint(e3, ecs_field_src(&it, 2)); test_uint(e3, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_written_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_written_reflexive(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); @@ -1553,39 +1608,39 @@ void RulesTransitive_1_var_src_written_reflexive(void) { ecs_add_id(world, LocatedIn, EcsReflexive); - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); - ecs_entity_t e5 = ecs_new(world, Tag); - ecs_entity_t e6 = ecs_new(world, Tag); - ecs_entity_t e7 = ecs_new(world, Tag); - ecs_entity_t e8 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); + ecs_entity_t e5 = ecs_new_w(world, Tag); + ecs_entity_t e6 = ecs_new_w(world, Tag); + ecs_entity_t e7 = ecs_new_w(world, Tag); + ecs_entity_t e8 = ecs_new_w(world, Tag); ecs_set_name(world, e1, "e1"); ecs_set_name(world, e2, "e2"); ecs_set_name(world, e3, "e3"); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($r), LocatedIn($r, e2)" }); test_assert(r != NULL); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); test_uint(e2, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -1598,65 +1653,65 @@ void RulesTransitive_1_var_src_written_reflexive(void) { ecs_add_pair(world, e8, LocatedIn, e2); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); test_uint(e2, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 1)); + test_uint(e7, ecs_field_src(&it, 0)); test_uint(e7, ecs_field_src(&it, 1)); - test_uint(e7, ecs_field_src(&it, 2)); test_uint(e7, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, e2), ecs_field_id(&it, 1)); + test_uint(e8, ecs_field_src(&it, 0)); test_uint(e8, ecs_field_src(&it, 1)); - test_uint(e8, ecs_field_src(&it, 2)); test_uint(e8, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_0_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - ecs_entity_t e5 = ecs_new_id(world); - ecs_entity_t e6 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($r, SanFrancisco)" }); test_assert(r != NULL); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -1669,54 +1724,54 @@ void RulesTransitive_1_var_src_0_lvl(void) { ecs_add(world, e2, Tag); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e6, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e6, ecs_field_src(&it, 0)); test_uint(e6, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); test_uint(e2, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_1_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - ecs_entity_t e5 = ecs_new_id(world); - ecs_entity_t e6 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($r, California)" }); test_assert(r != NULL); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -1729,72 +1784,72 @@ void RulesTransitive_1_var_src_1_lvl(void) { ecs_add(world, e2, Tag); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e6, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e6, ecs_field_src(&it, 0)); test_uint(e6, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); test_uint(e2, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(e3, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); test_uint(e3, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_2_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - ecs_entity_t e5 = ecs_new_id(world); - ecs_entity_t e6 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($r, UnitedStates)" }); test_assert(r != NULL); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -1807,77 +1862,77 @@ void RulesTransitive_1_var_src_2_lvl(void) { ecs_add(world, e2, Tag); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e6, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e6, ecs_field_src(&it, 0)); test_uint(e6, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); test_uint(e2, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(e3, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); test_uint(e3, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 1)); - test_uint(e4, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 0)); + test_uint(e4, ecs_field_src(&it, 0)); test_uint(e4, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); @@ -1885,20 +1940,20 @@ void RulesTransitive_1_var_src_reflexive(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_id(world); - ecs_entity_t e2 = ecs_new_id(world); - ecs_entity_t e3 = ecs_new_id(world); - ecs_entity_t e4 = ecs_new_id(world); - ecs_entity_t e5 = ecs_new_id(world); - ecs_entity_t e6 = ecs_new_id(world); + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($r, California)" }); test_assert(r != NULL); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); @@ -1911,228 +1966,228 @@ void RulesTransitive_1_var_src_reflexive(void) { ecs_add(world, e2, Tag); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e6, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e6, ecs_field_src(&it, 0)); test_uint(e6, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e2, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); test_uint(e2, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(e3, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); test_uint(e3, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_tgt_var(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_tgt_var(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($r, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(UnitedStates, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Netherlands, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_tgt_var_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_tgt_var_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); @@ -2140,405 +2195,405 @@ void RulesTransitive_1_var_src_tgt_var_reflexive(void) { ECS_TAG(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($r, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Amsterdam, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Seattle, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(LosAngeles, ecs_iter_get_var(&it, x_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(UnitedStates, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(Netherlands, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(UnitedStates, ecs_iter_get_var(&it, r_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(Netherlands, ecs_iter_get_var(&it, r_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_ent_src_tgt_this_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_ent_src_tgt_this_0_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $this)" }); test_assert(r != NULL); - int this_var = ecs_rule_find_var(r, "This"); + int this_var = ecs_query_find_var(r, "this"); test_assert(this_var != -1); test_assert(this_var != 0); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, UnitedStates); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Earth, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_ent_src_tgt_this_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_ent_src_tgt_this_1_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $this)" }); test_assert(r != NULL); - int this_var = ecs_rule_find_var(r, "This"); + int this_var = ecs_query_find_var(r, "this"); test_assert(this_var != -1); test_assert(this_var != 0); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, California); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, this_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Earth, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_ent_src_tgt_this_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_ent_src_tgt_this_2_lvl(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $this)" }); test_assert(r != NULL); - int this_var = ecs_rule_find_var(r, "This"); + int this_var = ecs_query_find_var(r, "this"); test_assert(this_var != -1); test_assert(this_var != 0); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, this_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Earth, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_ent_src_tgt_this_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_ent_src_tgt_this_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); @@ -2546,257 +2601,257 @@ void RulesTransitive_1_ent_src_tgt_this_reflexive(void) { ECS_TAG(world, Tag); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $this)" }); test_assert(r != NULL); - int this_var = ecs_rule_find_var(r, "This"); + int this_var = ecs_query_find_var(r, "this"); test_assert(this_var != -1); test_assert(this_var != 0); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, e1), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, e1), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_iter_get_var(&it, this_var)); test_uint(e1, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, this_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Earth, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_tgt_this(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_tgt_this(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ECS_TAG(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($r, $this)" }); test_assert(r != NULL); - int this_var = ecs_rule_find_var(r, "This"); + int this_var = ecs_query_find_var(r, "this"); test_assert(this_var != -1); test_assert(this_var != 0); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, this_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, this_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); test_uint(Netherlands, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, this_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); test_uint(Netherlands, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, this_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); test_uint(Washington, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, this_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(UnitedStates, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Netherlands, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_var_src_tgt_this_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_var_src_tgt_this_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); @@ -2804,263 +2859,263 @@ void RulesTransitive_1_var_src_tgt_this_reflexive(void) { ECS_TAG(world, Tag); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($r, $this)" }); test_assert(r != NULL); - int this_var = ecs_rule_find_var(r, "This"); + int this_var = ecs_query_find_var(r, "this"); test_assert(this_var != -1); - int r_var = ecs_rule_find_var(r, "r"); + int r_var = ecs_query_find_var(r, "r"); test_assert(r_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); + ecs_iter_t it = ecs_query_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Amsterdam, ecs_iter_get_var(&it, this_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, this_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, this_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); test_uint(Netherlands, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Amsterdam, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, this_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, this_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); test_uint(Netherlands, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(NoordHolland, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Seattle, ecs_iter_get_var(&it, this_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, this_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); test_uint(Washington, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Seattle, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); test_uint(SanFrancisco, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(LosAngeles, ecs_iter_get_var(&it, this_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); test_uint(LosAngeles, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, this_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(LosAngeles, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, this_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, this_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); test_uint(Washington, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(California, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Washington, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(UnitedStates, ecs_iter_get_var(&it, r_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, this_var)); test_uint(Netherlands, ecs_iter_get_var(&it, r_var)); test_uint(Netherlands, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(UnitedStates, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, this_var)); test_uint(Netherlands, ecs_iter_get_var(&it, r_var)); test_uint(Earth, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_ent_src_constrain_tgt_var_before_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_ent_src_constrain_tgt_var_before_0_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, City); @@ -3071,42 +3126,42 @@ void RulesTransitive_2_ent_src_constrain_tgt_var_before_0_lvl(void) { ecs_add(world, Seattle, City); ecs_add(world, Amsterdam, City); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "City($x), LocatedIn(e1, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(City, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); + test_uint(City, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_ent_src_constrain_tgt_var_before_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_ent_src_constrain_tgt_var_before_1_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, State); @@ -3116,42 +3171,42 @@ void RulesTransitive_2_ent_src_constrain_tgt_var_before_1_lvl(void) { ecs_add(world, Washington, State); ecs_add(world, NoordHolland, State); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "State($x), LocatedIn(e1, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(State, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); - test_uint(California, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); + test_uint(State, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(California, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); test_uint(California, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_ent_src_constrain_tgt_var_before_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_ent_src_constrain_tgt_var_before_2_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Country); @@ -3160,42 +3215,42 @@ void RulesTransitive_2_ent_src_constrain_tgt_var_before_2_lvl(void) { ecs_add(world, UnitedStates, Country); ecs_add(world, Netherlands, Country); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, Amsterdam); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Country($x), LocatedIn(e1, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(Country, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); + test_uint(Country, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_ent_src_constrain_tgt_var_after_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_ent_src_constrain_tgt_var_after_0_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, City); @@ -3206,42 +3261,42 @@ void RulesTransitive_2_ent_src_constrain_tgt_var_after_0_lvl(void) { ecs_add(world, Seattle, City); ecs_add(world, Amsterdam, City); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $x), City($x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(City, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(City, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 1)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_ent_src_constrain_tgt_var_after_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_ent_src_constrain_tgt_var_after_1_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, State); @@ -3251,42 +3306,42 @@ void RulesTransitive_2_ent_src_constrain_tgt_var_after_1_lvl(void) { ecs_add(world, Washington, State); ecs_add(world, NoordHolland, State); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $x), State($x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(State, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(California, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(State, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(California, ecs_field_src(&it, 1)); test_uint(California, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_ent_src_constrain_tgt_var_after_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_ent_src_constrain_tgt_var_after_2_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Country); @@ -3295,42 +3350,42 @@ void RulesTransitive_2_ent_src_constrain_tgt_var_after_2_lvl(void) { ecs_add(world, UnitedStates, Country); ecs_add(world, Netherlands, Country); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, Amsterdam); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(e1, $x), Country($x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Country, ecs_field_id(&it, 2)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Country, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_this_src_constrain_tgt_var_before_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_this_src_constrain_tgt_var_before_0_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, City); @@ -3341,64 +3396,64 @@ void RulesTransitive_2_this_src_constrain_tgt_var_before_0_lvl(void) { ecs_add(world, Seattle, City); ecs_add(world, Amsterdam, City); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - ecs_entity_t e4 = ecs_new_entity(world, "e4"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); ecs_add_pair(world, e4, LocatedIn, Amsterdam); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "City($x), LocatedIn($this, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(City, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(City, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); test_uint(e1, it.entities[0]); test_uint(e2, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(City, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 2)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(City, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(LosAngeles, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(City, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 2)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(City, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 1)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(Amsterdam, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_this_src_constrain_tgt_var_before_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_this_src_constrain_tgt_var_before_1_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, State); @@ -3408,92 +3463,92 @@ void RulesTransitive_2_this_src_constrain_tgt_var_before_1_lvl(void) { ecs_add(world, Washington, State); ecs_add(world, NoordHolland, State); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - ecs_entity_t e4 = ecs_new_entity(world, "e4"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); ecs_add_pair(world, e4, LocatedIn, Amsterdam); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "State($x), LocatedIn($this, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(State, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); - test_uint(California, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(State, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(California, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(State, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); - test_uint(California, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(State, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); + test_uint(California, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(e1, it.entities[0]); test_uint(e2, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(State, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 2)); - test_uint(California, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(State, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); + test_uint(California, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(State, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 2)); - test_uint(Washington, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(State, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); + test_uint(Washington, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(State, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 2)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(State, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(State, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 2)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(State, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 1)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_this_src_constrain_tgt_var_before_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_this_src_constrain_tgt_var_before_2_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Country); @@ -3502,111 +3557,111 @@ void RulesTransitive_2_this_src_constrain_tgt_var_before_2_lvl(void) { ecs_add(world, UnitedStates, Country); ecs_add(world, Netherlands, Country); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - ecs_entity_t e4 = ecs_new_entity(world, "e4"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); ecs_add_pair(world, e4, LocatedIn, Amsterdam); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Country($x), LocatedIn($this, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Country, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(Country, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Country, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(Country, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Country, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(Country, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(e1, it.entities[0]); test_uint(e2, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Country, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 2)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(Country, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Country, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 2)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(Country, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Country, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 2)); - test_uint(Netherlands, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(Country, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); + test_uint(Netherlands, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Country, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 2)); - test_uint(Netherlands, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(Country, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); + test_uint(Netherlands, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Country, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 2)); - test_uint(Netherlands, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_uint(Country, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 1)); + test_uint(Netherlands, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_this_src_constrain_tgt_var_after_0_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_this_src_constrain_tgt_var_after_0_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, City); @@ -3617,64 +3672,64 @@ void RulesTransitive_2_this_src_constrain_tgt_var_after_0_lvl(void) { ecs_add(world, Seattle, City); ecs_add(world, Amsterdam, City); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - ecs_entity_t e4 = ecs_new_entity(world, "e4"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); ecs_add_pair(world, e4, LocatedIn, Amsterdam); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, $x), City($x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 1)); - test_uint(City, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 0)); + test_uint(City, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 1)); test_uint(Amsterdam, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(City, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(City, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 1)); test_uint(LosAngeles, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(City, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(City, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 1)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); test_uint(e1, it.entities[0]); test_uint(e2, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_this_src_constrain_tgt_var_after_1_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_this_src_constrain_tgt_var_after_1_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, State); @@ -3684,92 +3739,92 @@ void RulesTransitive_2_this_src_constrain_tgt_var_after_1_lvl(void) { ecs_add(world, Washington, State); ecs_add(world, NoordHolland, State); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - ecs_entity_t e4 = ecs_new_entity(world, "e4"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); ecs_add_pair(world, e4, LocatedIn, Amsterdam); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, $x), State($x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(State, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(State, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 1)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(State, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(California, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(State, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(California, ecs_field_src(&it, 1)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(State, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(California, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(State, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(California, ecs_field_src(&it, 1)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(e1, it.entities[0]); test_uint(e2, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(State, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(State, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 1)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(State, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(State, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 1)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(State, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(California, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(State, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(California, ecs_field_src(&it, 1)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_this_src_constrain_tgt_var_after_2_lvl(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_this_src_constrain_tgt_var_after_2_lvl(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Country); @@ -3778,117 +3833,117 @@ void RulesTransitive_2_this_src_constrain_tgt_var_after_2_lvl(void) { ecs_add(world, UnitedStates, Country); ecs_add(world, Netherlands, Country); - ecs_entity_t e1 = ecs_new_entity(world, "e1"); - ecs_entity_t e2 = ecs_new_entity(world, "e2"); - ecs_entity_t e3 = ecs_new_entity(world, "e3"); - ecs_entity_t e4 = ecs_new_entity(world, "e4"); + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + ecs_entity_t e4 = ecs_entity(world, { .name = "e4" }); ecs_add_pair(world, e1, LocatedIn, SanFrancisco); ecs_add_pair(world, e2, LocatedIn, SanFrancisco); ecs_add_pair(world, e3, LocatedIn, LosAngeles); ecs_add_pair(world, e4, LocatedIn, Amsterdam); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, $x), Country($x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Country, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Country, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 1)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Country, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Country, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Country, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Country, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(e1, it.entities[0]); test_uint(e2, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Country, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Country, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 1)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Country, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Country, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 1)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Country, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Country, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Country, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Country, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Country, ecs_field_id(&it, 2)); - test_uint(0, ecs_field_src(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Country, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 1)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_src_tgt_same_var(void) { +void Transitive_1_src_tgt_same_var(void) { ecs_log_set_level(-4); - ecs_world_t *world = ecs_init(); + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($x, $x)" }); @@ -3897,549 +3952,549 @@ void RulesTransitive_1_src_tgt_same_var(void) { ecs_fini(world); } -void RulesTransitive_1_src_tgt_same_var_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_src_tgt_same_var_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ecs_add_id(world, LocatedIn, EcsReflexive); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($x, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 1)); - test_uint(Amsterdam, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 0)); + test_uint(Amsterdam, ecs_field_src(&it, 0)); test_uint(Amsterdam, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(NoordHolland, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(NoordHolland, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 1)); - test_uint(Seattle, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 0)); + test_uint(Seattle, ecs_field_src(&it, 0)); test_uint(Seattle, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(SanFrancisco, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(SanFrancisco, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(LosAngeles, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(LosAngeles, ecs_field_src(&it, 0)); test_uint(LosAngeles, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(California, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(California, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(Washington, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(Washington, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(UnitedStates, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(UnitedStates, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Netherlands, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Netherlands, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_src_tgt_same_this_var_reflexive(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_src_tgt_same_this_var_reflexive(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); ecs_add_id(world, LocatedIn, EcsReflexive); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, $this)" }); test_assert(r != NULL); - int this_var = ecs_rule_find_var(r, "This"); + int this_var = ecs_query_find_var(r, "this"); test_assert(this_var != -1); test_assert(this_var != 0); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Amsterdam), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Amsterdam, ecs_iter_get_var(&it, this_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, this_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Seattle), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Seattle, ecs_iter_get_var(&it, this_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(SanFrancisco, ecs_iter_get_var(&it, this_var)); test_uint(SanFrancisco, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, LosAngeles), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(LosAngeles, ecs_iter_get_var(&it, this_var)); test_uint(LosAngeles, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, this_var)); test_uint(California, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, this_var)); test_uint(Washington, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, this_var)); test_uint(UnitedStates, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, this_var)); test_uint(Netherlands, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_1_any_src_tgt_var(void) { - ecs_world_t *world = ecs_init(); +void Transitive_1_any_src_tgt_var(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn(_, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_src(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_not_transitive_ent_tgt(void) { - ecs_world_t *world = ecs_init(); +void Transitive_not_transitive_ent_tgt(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "!LocatedIn($this, UnitedStates), Location($this)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(UnitedStates, it.entities[0]); test_uint(Netherlands, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(Amsterdam, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_not_transitive_var_tgt(void) { - ecs_world_t *world = ecs_init(); +void Transitive_not_transitive_var_tgt(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "!LocatedIn($this, $x), Location($this)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); test_uint(Earth, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_not_transitive_ent_tgt_written(void) { - ecs_world_t *world = ecs_init(); +void Transitive_not_transitive_ent_tgt_written(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Location($this), !LocatedIn($this, UnitedStates)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(UnitedStates, it.entities[0]); test_uint(Netherlands, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(Amsterdam, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_not_transitive_var_tgt_written(void) { - ecs_world_t *world = ecs_init(); +void Transitive_not_transitive_var_tgt_written(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Location($this), !LocatedIn($this, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); test_uint(Earth, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_optional_transitive_ent_tgt(void) { - ecs_world_t *world = ecs_init(); +void Transitive_optional_transitive_ent_tgt(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "?LocatedIn($this, UnitedStates), Location($this)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(UnitedStates, it.entities[0]); test_uint(Netherlands, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Location, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Location, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(Amsterdam, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_optional_transitive_var_tgt(void) { - ecs_world_t *world = ecs_init(); +void Transitive_optional_transitive_var_tgt(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "?LocatedIn($this, $x), Tag($this)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); ecs_add_pair(world, e2, LocatedIn, Earth); ecs_add_pair(world, e3, LocatedIn, SanFrancisco); @@ -4447,267 +4502,267 @@ void RulesTransitive_optional_transitive_var_tgt(void) { ecs_add_pair(world, e4, LocatedIn, NoordHolland); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(false, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); test_uint(e1, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(e2, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(Tag, ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_optional_transitive_ent_tgt_written(void) { - ecs_world_t *world = ecs_init(); +void Transitive_optional_transitive_ent_tgt_written(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Location($this), ?LocatedIn($this, UnitedStates)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(Earth, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(UnitedStates, it.entities[0]); test_uint(Netherlands, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Location, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Location, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(Amsterdam, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_optional_transitive_var_tgt_written(void) { - ecs_world_t *world = ecs_init(); +void Transitive_optional_transitive_var_tgt_written(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "Tag($this), ?LocatedIn($this, $x)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } - ecs_entity_t e1 = ecs_new(world, Tag); - ecs_entity_t e2 = ecs_new(world, Tag); - ecs_entity_t e3 = ecs_new(world, Tag); - ecs_entity_t e4 = ecs_new(world, Tag); + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + ecs_entity_t e4 = ecs_new_w(world, Tag); ecs_add_pair(world, e2, LocatedIn, Earth); ecs_add_pair(world, e3, LocatedIn, SanFrancisco); @@ -4715,168 +4770,168 @@ void RulesTransitive_optional_transitive_var_tgt_written(void) { ecs_add_pair(world, e4, LocatedIn, NoordHolland); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); - test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(false, ecs_field_is_set(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); test_uint(e1, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(e2, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(SanFrancisco, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(e3, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(Tag, ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); test_uint(0, ecs_field_src(&it, 1)); - test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(e4, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_2_var_src_w_same_tgt_ent(void) { - ecs_world_t *world = ecs_init(); +void Transitive_2_var_src_w_same_tgt_ent(void) { + ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($x, California), LocatedIn($y, California), Tag($x), Tag($y)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); - int y_var = ecs_rule_find_var(r, "y"); + int y_var = ecs_query_find_var(r, "y"); test_assert(y_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(false, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); } ecs_entity_t e1 = ecs_new_w_pair(world, LocatedIn, SanFrancisco); @@ -4890,212 +4945,384 @@ void RulesTransitive_2_var_src_w_same_tgt_ent(void) { ecs_add(world, e4, Tag); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 2)); test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(Tag, ecs_field_id(&it, 4)); + test_uint(e2, ecs_field_src(&it, 0)); test_uint(e2, ecs_field_src(&it, 1)); test_uint(e2, ecs_field_src(&it, 2)); test_uint(e2, ecs_field_src(&it, 3)); - test_uint(e2, ecs_field_src(&it, 4)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(e2, ecs_iter_get_var(&it, x_var)); test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(Tag, ecs_field_id(&it, 4)); - test_uint(e2, ecs_field_src(&it, 1)); - test_uint(e1, ecs_field_src(&it, 2)); - test_uint(e2, ecs_field_src(&it, 3)); - test_uint(e1, ecs_field_src(&it, 4)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e2, ecs_field_src(&it, 2)); + test_uint(e1, ecs_field_src(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(e2, ecs_iter_get_var(&it, x_var)); test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 2)); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 2)); test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(Tag, ecs_field_id(&it, 4)); - test_uint(e1, ecs_field_src(&it, 1)); - test_uint(e2, ecs_field_src(&it, 2)); - test_uint(e1, ecs_field_src(&it, 3)); - test_uint(e2, ecs_field_src(&it, 4)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_field_src(&it, 2)); + test_uint(e2, ecs_field_src(&it, 3)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(e1, ecs_iter_get_var(&it, x_var)); test_uint(e2, ecs_iter_get_var(&it, y_var)); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(0, it.count); + test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 0)); test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 1)); - test_uint(ecs_pair(LocatedIn, SanFrancisco), ecs_field_id(&it, 2)); + test_uint(Tag, ecs_field_id(&it, 2)); test_uint(Tag, ecs_field_id(&it, 3)); - test_uint(Tag, ecs_field_id(&it, 4)); + test_uint(e1, ecs_field_src(&it, 0)); test_uint(e1, ecs_field_src(&it, 1)); test_uint(e1, ecs_field_src(&it, 2)); test_uint(e1, ecs_field_src(&it, 3)); - test_uint(e1, ecs_field_src(&it, 4)); + test_bool(true, ecs_field_is_set(&it, 0)); test_bool(true, ecs_field_is_set(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 2)); test_uint(e1, ecs_iter_get_var(&it, x_var)); test_uint(e1, ecs_iter_get_var(&it, y_var)); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_self_target(void) { - ecs_world_t *world = ecs_init(); +void Transitive_self_target(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { - .expr = "LocatedIn($this, $x:self)" + ecs_query_t *r = ecs_query(world, { + .expr = "LocatedIn($this, $x|self)" }); test_assert(r != NULL); - int x_var = ecs_rule_find_var(r, "x"); + int x_var = ecs_query_find_var(r, "x"); test_assert(x_var != -1); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, Earth), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(Earth, ecs_iter_get_var(&it, x_var)); test_uint(UnitedStates, it.entities[0]); test_uint(Netherlands, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, UnitedStates), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(UnitedStates, ecs_iter_get_var(&it, x_var)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, California), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(California, ecs_iter_get_var(&it, x_var)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, Washington), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(Washington, ecs_iter_get_var(&it, x_var)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, Netherlands), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(Netherlands, ecs_iter_get_var(&it, x_var)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, NoordHolland), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(NoordHolland, ecs_iter_get_var(&it, x_var)); test_uint(Amsterdam, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); ecs_fini(world); } -void RulesTransitive_any_target(void) { - ecs_world_t *world = ecs_init(); +void Transitive_any_target(void) { + ecs_world_t *world = ecs_mini(); populate_facts(world); - ecs_rule_t *r = ecs_rule(world, { + ecs_query_t *r = ecs_query(world, { .expr = "LocatedIn($this, _)" }); test_assert(r != NULL); { - ecs_iter_t it = ecs_rule_iter(world, r); - test_bool(true, ecs_rule_next(&it)); + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(UnitedStates, it.entities[0]); test_uint(Netherlands, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(California, it.entities[0]); test_uint(Washington, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(2, it.count); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(SanFrancisco, it.entities[0]); test_uint(LosAngeles, it.entities[1]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(Seattle, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(NoordHolland, it.entities[0]); - test_bool(true, ecs_rule_next(&it)); + test_bool(true, ecs_query_next(&it)); test_uint(1, it.count); - test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 1)); - test_uint(0, ecs_field_src(&it, 1)); - test_bool(true, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(LocatedIn, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); test_uint(Amsterdam, it.entities[0]); - test_bool(false, ecs_rule_next(&it)); + test_bool(false, ecs_query_next(&it)); } - ecs_rule_fini(r); + ecs_query_fini(r); + + ecs_fini(world); +} + +void Transitive_isa_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t prefab = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(EcsIsA, prefab) }} + }); + + 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(inst, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Transitive_isa_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t prefab = ecs_new_w_id(world, EcsDisabled); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(EcsIsA, prefab) }} + }); + + 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(inst, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Transitive_isa_prefab_match_prefab_flag(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t prefab = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(EcsIsA, prefab) }}, + .flags = EcsQueryMatchPrefab + }); + + 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(prefab, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Transitive_isa_prefab_match_prefab_term(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t prefab = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(EcsIsA, prefab) }, + { EcsPrefab, .oper = EcsOptional } + } + }); + + 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(prefab, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Transitive_isa_disabled_match_disabled_flag(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t prefab = ecs_new_w_id(world, EcsDisabled); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(EcsIsA, prefab) }}, + .flags = EcsQueryMatchDisabled + }); + + 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(prefab, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Transitive_isa_disabled_match_disabled_term(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t prefab = ecs_new_w_id(world, EcsDisabled); + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, prefab); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(EcsIsA, prefab) }, + { EcsDisabled, .oper = EcsOptional } + } + }); + + 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(prefab, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(ecs_pair(EcsIsA, prefab), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); ecs_fini(world); } diff --git a/vendors/flecs/test/query/src/Traversal.c b/vendors/flecs/test/query/src/Traversal.c new file mode 100644 index 000000000..b33739007 --- /dev/null +++ b/vendors/flecs/test/query/src/Traversal.c @@ -0,0 +1,10698 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void Traversal_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +void Traversal_this_self_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_self_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($x|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(p1, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(p2, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(p3, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($x|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_written_self_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x|self), Foo($x|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(p1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(p2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(p3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e7, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_written_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x|self), Foo($x|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e7, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_set_var_self_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity(world, { .parent = p3 }); + + ecs_entity_t e8 = ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($x|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, p2); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(p2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e3); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e5); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e8); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_set_var_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity(world, { .parent = p3 }); + + ecs_entity_t e8 = ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($x|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, p2); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e3); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e5); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e8); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_set_var_written_self_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity(world, { .parent = p3 }); + + ecs_entity_t e8 = ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x|self), Foo($x|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, p2); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(p2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e3); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e5); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e8); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_set_var_written_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x|self), Foo($x|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity(world, { .parent = p3 }); + + ecs_entity_t e8 = ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, p2); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e3); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e5); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, r); + ecs_iter_set_var(&it, x_var, e8); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_ent_self_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(ent|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e, Foo); + ecs_add_pair(world, e, EcsChildOf, parent); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, parent, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(parent, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_ent_up_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(ent|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e, EcsChildOf, parent); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, parent, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(parent, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_implicit_this_self_up_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p1)) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p1)) }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p2)) }); + ecs_entity_t e4 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p2)) }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e2)) }); + ecs_entity_t e6 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e2)) }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p3)) }); + + ecs_entity(world, { .add = ecs_ids(ecs_isa(p0)) }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_implicit_this_up_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p1)) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p1)) }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p2)) }); + ecs_entity_t e4 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p2)) }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e2)) }); + ecs_entity_t e6 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e2)) }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p3)) }); + + ecs_entity(world, { .add = ecs_ids(ecs_isa(p0)) }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_implicit_var_self_up_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($x)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p1)) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p1)) }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p2)) }); + ecs_entity_t e4 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p2)) }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e2)) }); + ecs_entity_t e6 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e2)) }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p3)) }); + + ecs_entity(world, { .add = ecs_ids(ecs_isa(p0)) }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(p1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(p2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(p3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_implicit_var_up_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($x|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p1)) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p1)) }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p2)) }); + ecs_entity_t e4 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p2)) }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e2)) }); + ecs_entity_t e6 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e2)) }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p3)) }); + + ecs_entity(world, { .add = ecs_ids(ecs_isa(p0)) }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_implicit_ent_self_up_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(ent|self|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove(world, e, Foo); + ecs_add_pair(world, e, EcsIsA, parent); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, parent, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(parent, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_implicit_ent_up_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(ent|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e, EcsIsA, parent); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, parent, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(parent, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_self_up_2_targets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t parent_a = ecs_new(world); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, e_1, EcsIsA, parent_b); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_4 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_add_pair(world, e_4, EcsIsA, parent_c); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(parent_b, it.entities[0]); + test_uint(parent_c, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(parent_b, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(parent_b, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_4, it.entities[0]); + test_uint(parent_b, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_up_2_targets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t parent_a = ecs_new(world); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, e_1, EcsIsA, parent_b); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_4 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_add_pair(world, e_4, EcsIsA, parent_c); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(parent_b, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(parent_b, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_4, it.entities[0]); + test_uint(parent_b, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_self_up_2_targets_diamond(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t parent_root = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent_a = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, parent_c, EcsIsA, parent_b); + + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity_t e_3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_c)) }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_root, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(parent_b, it.entities[1]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_c, it.entities[0]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_3, it.entities[0]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_up_2_targets_diamond(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t parent_root = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent_a = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, parent_c, EcsIsA, parent_b); + + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity_t e_3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_c)) }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(parent_b, it.entities[1]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_c, it.entities[0]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_3, it.entities[0]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_written_self_up_2_targets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(self|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t parent_a = ecs_new(world); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, e_1, EcsIsA, parent_b); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_4 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_add_pair(world, e_4, EcsIsA, parent_c); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(parent_b, it.entities[0]); + test_uint(parent_c, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_written_up_2_targets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t parent_a = ecs_new(world); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, e_1, EcsIsA, parent_b); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_4 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_add_pair(world, e_4, EcsIsA, parent_c); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_written_self_up_2_targets_diamond(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(self|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t parent_root = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent_a = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, parent_c, EcsIsA, parent_b); + + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity_t e_3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_c)) }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_root, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(parent_b, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_c, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_written_up_2_targets_diamond(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t parent_root = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent_a = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, parent_c, EcsIsA, parent_b); + + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity_t e_3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_c)) }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(parent_b, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_c, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_root, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_2_self_up_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_set_with(world, Tag); + ecs_entity_t parent_a = ecs_new(world); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity_t e_3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e_2)) }); + ecs_set_with(world, 0); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag, Foo(self|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_b, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_2_self_up_terms_2_targets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_set_with(world, Tag); + ecs_entity_t parent_a = ecs_new(world); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, e_1, EcsIsA, parent_b); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_4 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_add_pair(world, e_4, EcsIsA, parent_c); + ecs_set_with(world, 0); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag, Foo(self|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(parent_b, it.entities[0]); + test_uint(parent_c, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent_b, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_self_up_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e1)) }); + ecs_delete(world, e2); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_up_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(e1)) }); + ecs_delete(world, e2); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_self_up_match_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Foo }}, + .cache_kind = cache_kind, + .flags = EcsQueryMatchEmptyTables + }); + + ecs_entity_t base = ecs_new(world); + ecs_table_t *table = ecs_table_add_id(world, NULL, ecs_pair(EcsIsA, base)); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, base, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_assert(table == it.table); + test_int(0, it.count); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(base, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_up_match_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Foo, .src.id = EcsUp, .trav = EcsIsA }}, + .cache_kind = cache_kind, + .flags = EcsQueryMatchEmptyTables + }); + + ecs_entity_t base = ecs_new_w(world, Foo); + ecs_table_t *table = ecs_table_add_id(world, NULL, ecs_pair(EcsIsA, base)); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_assert(table == it.table); + test_int(0, it.count); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(base, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_self_up_all_owned(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self|up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t parent_root = ecs_entity(world, {0}); + ecs_entity_t parent_a = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, parent_c, EcsIsA, parent_b); + + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity_t e_3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_c)) }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_root, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(parent_b, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_c, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_up_all_owned(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(up IsA)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t parent_root = ecs_entity(world, {0}); + ecs_entity_t parent_a = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_b = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_root)) }); + ecs_entity_t parent_c = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_add_pair(world, parent_c, EcsIsA, parent_b); + + ecs_entity_t e_1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_a)) }); + ecs_entity_t e_2 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_b)) }); + ecs_entity_t e_3 = ecs_entity(world, { .add = ecs_ids(ecs_isa(parent_c)) }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(parent_b, it.entities[1]); + test_uint(parent_root, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_1, it.entities[0]); + test_uint(parent_a, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_c, it.entities[0]); + test_uint(parent_a, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_2, it.entities[0]); + test_uint(parent_b, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e_3, it.entities[0]); + test_uint(parent_c, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_inherited_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base), Foo) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(parent, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_up_childof_inherited_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base), Foo) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(parent, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(base, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(base, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_inherited_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base), Foo) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_up_childof_inherited_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base), Foo) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(child, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(parent, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_self_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($x|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(base, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(child, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo($x|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(child, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_written_self_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x|self), Foo($x|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_set_with(world, Tag); + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(base, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(base, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(child, ecs_field_src(&it, 0)); + test_uint(base, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(child, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_written_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x|self), Foo($x|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_set_with(world, Tag); + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(child, ecs_field_src(&it, 0)); + test_uint(base, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + test_uint(child, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_ent_self_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_name(world, parent, "parent"); + ecs_set_name(world, child, "child"); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(parent.child|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, child, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(child, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_ent_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_name(world, parent, "parent"); + ecs_set_name(world, child, "child"); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(parent.child|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, child, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_ent_written_self_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_set_with(world, Tag); + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_name(world, parent, "parent"); + ecs_set_name(world, child, "child"); + ecs_set_with(world, 0); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(parent.child|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, child, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(child, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_ent_written_up_childof_inherited(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_set_with(world, Tag); + ecs_entity_t base = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_entity_t parent = ecs_entity(world, { .add = ecs_ids(ecs_isa(base)) }); + ecs_entity_t child = ecs_entity(world, { .parent = parent }); + ecs_set_name(world, parent, "parent"); + ecs_set_name(world, child, "child"); + ecs_set_with(world, 0); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(parent.child|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, child, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(base, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t p2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t p3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_up_childof_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t p2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t p3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Position(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_insert(world, {Tag}, ecs_value(Position, {10, 20})); + ecs_entity_t p2 = ecs_insert(world, {Tag}, ecs_value(Position, {20, 30})); + ecs_entity_t p3 = ecs_insert(world, {Tag}, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_up_childof_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Position(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_insert(world, {Tag}, ecs_value(Position, {10, 20})); + ecs_entity_t p2 = ecs_insert(world, {Tag}, ecs_value(Position, {20, 30})); + ecs_entity_t p3 = ecs_insert(world, {Tag}, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_self_up_childof_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($x|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t p2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t p3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(p3, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_up_childof_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position($x|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t p2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_entity_t p3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_written_self_up_childof_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x|self), Position($x|self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_insert(world, {Tag}, ecs_value(Position, {10, 20})); + ecs_entity_t p2 = ecs_insert(world, {Tag}, ecs_value(Position, {20, 30})); + ecs_entity_t p3 = ecs_insert(world, {Tag}, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(p1, ecs_iter_get_var(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(p2, ecs_iter_get_var(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(p3, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e7, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_var_written_up_childof_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag($x|self), Position($x|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_insert(world, {Tag}, ecs_value(Position, {10, 20})); + ecs_entity_t p2 = ecs_insert(world, {Tag}, ecs_value(Position, {20, 30})); + ecs_entity_t p3 = ecs_insert(world, {Tag}, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e7, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(e7, ecs_iter_get_var(&it, x_var)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_recycled_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_delete(world, ecs_new(world)); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + test_assert((uint32_t)p0 != p0); + test_assert((uint32_t)p1 != p1); + test_assert((uint32_t)p2 != p2); + test_assert((uint32_t)p3 != p3); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_up_childof_recycled_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_delete(world, ecs_new(world)); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + test_assert((uint32_t)p0 != p0); + test_assert((uint32_t)p1 != p1); + test_assert((uint32_t)p2 != p2); + test_assert((uint32_t)p3 != p3); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_recycled_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + test_assert((uint32_t)p0 != p0); + test_assert((uint32_t)p1 != p1); + test_assert((uint32_t)p2 != p2); + test_assert((uint32_t)p3 != p3); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_up_childof_recycled_parent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Foo(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids(Foo) }); + ecs_add(world, p3, Bar); + + test_assert((uint32_t)p0 != p0); + test_assert((uint32_t)p1 != p1); + test_assert((uint32_t)p2 != p2); + test_assert((uint32_t)p3 != p3); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(Foo, ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_recycled_parent_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_delete(world, ecs_new(world)); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + test_assert((uint32_t)p0 != p0); + test_assert((uint32_t)p1 != p1); + test_assert((uint32_t)p2 != p2); + test_assert((uint32_t)p3 != p3); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_up_childof_recycled_parent_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + + ecs_delete(world, ecs_new(world)); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + test_assert((uint32_t)p0 != p0); + test_assert((uint32_t)p1 != p1); + test_assert((uint32_t)p2 != p2); + test_assert((uint32_t)p3 != p3); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_recycled_parent_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Position(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p1 = ecs_insert(world, {Tag}, ecs_value(Position, {10, 20})); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p2 = ecs_insert(world, {Tag}, ecs_value(Position, {20, 30})); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p3 = ecs_insert(world, {Tag}, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + test_assert((uint32_t)p0 != p0); + test_assert((uint32_t)p1 != p1); + test_assert((uint32_t)p2 != p2); + test_assert((uint32_t)p3 != p3); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + test_int(p[1].x, 20); + test_int(p[1].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_up_childof_recycled_parent_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Position(up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p1 = ecs_insert(world, {Tag}, ecs_value(Position, {10, 20})); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p2 = ecs_insert(world, {Tag}, ecs_value(Position, {20, 30})); + ecs_delete(world, ecs_new(world)); + ecs_entity_t p3 = ecs_insert(world, {Tag}, ecs_value(Position, {30, 40})); + ecs_add(world, p3, Bar); + + test_assert((uint32_t)p0 != p0); + test_assert((uint32_t)p1 != p1); + test_assert((uint32_t)p2 != p2); + test_assert((uint32_t)p3 != p3); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tgt, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(self|up, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_up_childof_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tgt, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(up, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tgt, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Rel(self|up, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_up_childof_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tgt, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Rel(up, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, Tgt) ) }); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, Tgt), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_pair_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtC, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtA) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtB) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtC) ) }); + ecs_add_pair(world, p3, EcsChildOf, p2); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(self|up, *)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_up_childof_pair_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtC, (OnInstantiate, Inherit)); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtA) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtB) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtC) ) }); + ecs_add_pair(world, p3, EcsChildOf, p2); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(up, *)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_pair_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Rel(self|up, *)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtA) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtB) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtC) ) }); + ecs_add(world, p3, Bar); + ecs_add_pair(world, p3, EcsChildOf, p2); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_up_childof_pair_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Rel(up, *)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtA) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtB) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtC) ) }); + ecs_add(world, p3, Bar); + ecs_add_pair(world, p3, EcsChildOf, p2); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtC, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(self|up, $x)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtA) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtB) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtC) ) }); + ecs_add_pair(world, p3, EcsChildOf, p2); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 0)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TgtC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), Rel(self|up, $x)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtA) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtB) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TgtC) ) }); + ecs_add(world, p3, Bar); + ecs_add_pair(world, p3, EcsChildOf, p2); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtB), ecs_field_id(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtA), ecs_field_id(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, TgtC), ecs_field_id(&it, 1)); + test_uint(TgtC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tgt, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "$x(self|up, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(RelA, Tgt) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(RelB, Tgt) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(RelC, Tgt) ) }); + ecs_add_pair(world, p3, EcsChildOf, p2); + ecs_add(world, p3, Bar); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 0)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(p3, ecs_field_src(&it, 0)); + test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 0)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, RelA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, RelC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tgt, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Tag(self), $x(self|up, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_set_with(world, Tag); + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(RelA, Tgt) ) }); + ecs_entity_t p2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(RelB, Tgt) ) }); + ecs_entity_t p3 = ecs_entity(world, { .add = ecs_ids( ecs_pair(RelC, Tgt) ) }); + ecs_add(world, p3, Bar); + ecs_add_pair(world, p3, EcsChildOf, p2); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p1 }); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e4 = ecs_entity(world, { .parent = p2 }); + ecs_add(world, e4, Bar); + + ecs_entity_t e5 = ecs_entity(world, { .parent = e2 }); + ecs_entity_t e6 = ecs_entity(world, { .parent = e2 }); + ecs_add(world, e6, Bar); + + ecs_entity_t e7 = ecs_entity(world, { .parent = p3 }); + + ecs_entity(world, { .parent = p0 }); + ecs_set_with(world, 0); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 1)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, Tgt), ecs_field_id(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, Tgt), ecs_field_id(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelC, Tgt), ecs_field_id(&it, 1)); + test_uint(RelC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_pair_for_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ECS_ENTITY(world, ent, (Rel, TagA), (Rel, TagB), (Rel, TagC)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(ent, $x), $x(self|up ChildOf)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p1 = ecs_new_w(world, TagA); + ecs_entity_t p2 = ecs_new_w(world, TagB); + ecs_entity_t p3 = ecs_new_w(world, TagC); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e3 = ecs_entity(world, { .parent = p3 }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p2, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 0)); + test_uint(TagC, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 0)); + test_uint(TagC, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_up_childof_pair_for_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ECS_ENTITY(world, ent, (Rel, TagA), (Rel, TagB), (Rel, TagC)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(ent, $x), $x(up ChildOf)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p1 = ecs_new_w(world, TagA); + ecs_entity_t p2 = ecs_new_w(world, TagB); + ecs_entity_t p3 = ecs_new_w(world, TagC); + + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_entity_t e2 = ecs_entity(world, { .parent = p2 }); + ecs_entity_t e3 = ecs_entity(world, { .parent = p3 }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 0)); + test_uint(TagC, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_pair_for_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(self, $x), $x(self|up)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_new_w(world, TagA); + ecs_entity_t p2 = ecs_new_w(world, TagB); + ecs_entity_t p3 = ecs_new_w(world, TagC); + + ecs_entity_t e0 = ecs_entity(world, { .parent = p0 }); + ecs_add_pair(world, e0, Rel, TagA); + ecs_entity_t e1 = ecs_entity(world, { .parent = p1 }); + ecs_add_pair(world, e1, Rel, TagA); + ecs_entity_t e2 = ecs_entity(world, { .parent = p2 }); + ecs_add_pair(world, e2, Rel, TagB); + ecs_entity_t e3 = ecs_entity(world, { .parent = p3 }); + ecs_add_pair(world, e3, Rel, TagC); + + ecs_entity_t e4 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TagA) ) }); + ecs_add(world, e4, TagA); + ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TagB) ) }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 0)); + test_uint(TagC, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_pair_for_var_written_n_targets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Bar, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Contains, Traversable); // Non-exclusive relationship + + ECS_ENTITY(world, ent, (Rel, TagA), (Rel, TagB), (Rel, TagC)); + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(ent, $x), $x(self|up Contains)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p0_a = ecs_new_w(world, TagA); + ecs_entity_t p0_b = ecs_new_w(world, TagB); + ecs_add(world, p0_b, Foo); + ecs_entity_t p0_c = ecs_new_w(world, TagC); + ecs_add(world, p0_c, Foo); + ecs_add(world, p0_c, Bar); + ecs_entity_t p1 = ecs_new(world); + ecs_add_pair(world, p1, Contains, p0_a); + ecs_add_pair(world, p1, Contains, p0_b); + ecs_add_pair(world, p1, Contains, p0_c); + + ecs_entity(world, { .add = ecs_ids( ecs_pair(Contains, p0) ) }); + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Contains, p1)) }); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TagA) ) }); + ecs_add(world, e2, TagA); + ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TagB) ) }); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p0_a, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p0_a, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p0_a, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p0_b, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p0_b, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p0_b, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p0_c, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 0)); + test_uint(TagC, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p0_c, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 0)); + test_uint(TagC, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(p0_c, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 0)); + test_uint(TagC, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_pair_for_var_written_n_targets(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Contains, Traversable); // Non-exclusive relationship + + ecs_query_t *r = ecs_query(world, { + .expr = "Rel(self, $x), $x(self|up Contains)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + ecs_entity_t p0 = ecs_entity(world, {0}); + ecs_entity_t p1 = ecs_new_w(world, TagA); + ecs_entity_t p2 = ecs_new_w(world, TagB); + ecs_entity_t p3 = ecs_new_w(world, TagC); + + ecs_entity_t e0 = ecs_entity(world, { .add = ecs_ids(ecs_pair(Contains, p0)) }); + ecs_add_pair(world, e0, Rel, TagA); + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids(ecs_pair(Contains, p1)) }); + ecs_add_pair(world, e1, Contains, p2); + ecs_add_pair(world, e1, Contains, p3); + ecs_add_pair(world, e1, Rel, TagA); + ecs_entity_t e2 = ecs_entity(world, { .add = ecs_ids(ecs_pair(Contains, p1)) }); + ecs_add_pair(world, e2, Contains, p2); + ecs_add_pair(world, e2, Contains, p3); + ecs_add_pair(world, e2, Rel, TagB); + ecs_entity_t e3 = ecs_entity(world, { .add = ecs_ids(ecs_pair(Contains, p1)) }); + ecs_add_pair(world, e3, Contains, p2); + ecs_add_pair(world, e3, Contains, p3); + ecs_add_pair(world, e3, Rel, TagC); + + ecs_entity_t e4 = ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TagA) ) }); + ecs_add(world, e4, TagA); + ecs_entity(world, { .add = ecs_ids( ecs_pair(Rel, TagB) ) }); + + int x_var = ecs_query_find_var(r, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p2, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagB), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p3, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagC), ecs_field_id(&it, 0)); + test_uint(TagC, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Rel, TagA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_self_up_2_levels_w_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); + ecs_add_pair(world, base_2, EcsIsA, base); + + ecs_entity_t inst = ecs_entity(world, { .add = ecs_ids(ecs_isa(base_2)) }); + + ecs_query_t *f = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position) }, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(base, it.sources[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_self_up_2_levels_other_trav_rel_w_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); + ecs_add_pair(world, base_2, EcsIsA, base); + + ecs_entity(world, { .add = ecs_ids(ecs_isa(base_2)) }); + + ecs_query_t *f = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position), .src.id = EcsSelf|EcsUp }, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_up_2_levels_w_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); + ecs_add_pair(world, base_2, EcsIsA, base); + + ecs_entity_t inst = ecs_entity(world, { .add = ecs_ids(ecs_isa(base_2)) }); + + ecs_query_t *f = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position), .src.id = EcsUp, .trav = EcsIsA }, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(base, it.sources[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_up_2_levels_other_trav_rel_w_prefab(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w_id(world, EcsPrefab); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab); + ecs_add_pair(world, base_2, EcsIsA, base); + + ecs_entity(world, { .add = ecs_ids(ecs_isa(base_2)) }); + + ecs_query_t *f = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position), .src.id = EcsUp }, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_self_up_2_levels(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t base_2 = ecs_new(world); + ecs_add_pair(world, base_2, EcsIsA, base); + + ecs_entity_t inst = ecs_entity(world, { .add = ecs_ids(ecs_isa(base_2)) }); + + ecs_query_t *f = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position) }, + .cache_kind = cache_kind + }); + + test_assert(f->terms[0].trav == EcsIsA); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base, it.entities[0]); + test_uint(0, it.sources[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base_2, it.entities[0]); + test_uint(base, it.sources[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(base, it.sources[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_self_up_2_levels_other_trav_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t base_2 = ecs_new(world); + ecs_add_pair(world, base_2, EcsIsA, base); + + ecs_entity(world, { .add = ecs_ids(ecs_isa(base_2)) }); + + ecs_query_t *f = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position), .src.id = EcsSelf|EcsUp }, + .cache_kind = cache_kind + }); + + test_assert(f->terms[0].trav == EcsChildOf); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base, it.entities[0]); + test_uint(0, it.sources[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_up_2_levels(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t base_2 = ecs_new(world); + ecs_add_pair(world, base_2, EcsIsA, base); + + ecs_entity_t inst = ecs_entity(world, { .add = ecs_ids(ecs_isa(base_2)) }); + + ecs_query_t *f = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position), .src.id = EcsUp, .trav = EcsIsA }, + .cache_kind = cache_kind + }); + + test_assert(f->terms[0].trav == EcsIsA); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(base_2, it.entities[0]); + test_uint(base, it.sources[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(inst, it.entities[0]); + test_uint(base, it.sources[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_up_2_levels_other_trav_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new(world); + ecs_set(world, base, Position, {10, 20}); + + ecs_entity_t base_2 = ecs_new(world); + ecs_add_pair(world, base_2, EcsIsA, base); + + ecs_entity(world, { .add = ecs_ids(ecs_isa(base_2)) }); + + ecs_query_t *f = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position), .src.id = EcsUp }, + .cache_kind = cache_kind + }); + + test_assert(f->terms[0].trav == EcsChildOf); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_self_up_mixed_traversable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_entity_t p1 = ecs_new(world); + ecs_entity_t p2 = ecs_new(world); + ecs_entity_t c = ecs_new_w_pair(world, EcsChildOf, p2); + + ecs_add(world, p1, TagA); + ecs_add(world, p2, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA(self|up ChildOf)", + .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(2, it.count); + test_uint(p1, it.entities[0]); + test_uint(p2, it.entities[1]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(c, it.entities[0]); + test_uint(p2, ecs_field_src(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_not_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t p1 = ecs_new_w(world, TagB); + ecs_entity_t p2 = ecs_new(world); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); + + ecs_add_pair(world, e1, EcsChildOf, p1); + ecs_add_pair(world, e2, EcsChildOf, p2); + ecs_add(world, e4, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !TagB(up)", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_not_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_entity_t p1 = ecs_new_w(world, TagB); + ecs_entity_t p2 = ecs_new(world); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); + + ecs_add_pair(world, e1, EcsChildOf, p1); + ecs_add_pair(world, e2, EcsChildOf, p2); + ecs_add(world, e4, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !TagB(self|up)", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_not_up_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_TAG(world, TgtA); + + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t p2 = ecs_new(world); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); + + ecs_add_pair(world, e1, EcsChildOf, p1); + ecs_add_pair(world, e2, EcsChildOf, p2); + ecs_add_pair(world, e4, Rel, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !Rel(up, *)", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_not_self_up_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + ECS_TAG(world, TgtA); + + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, TgtA); + ecs_entity_t p2 = ecs_new(world); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); + + ecs_add_pair(world, e1, EcsChildOf, p1); + ecs_add_pair(world, e2, EcsChildOf, p2); + ecs_add_pair(world, e4, Rel, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !Rel(self|up, *)", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_not_up_disabled(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_query_t *f = ecs_query(world, { + .terms = { + { TagA }, + { .id = EcsDisabled, .src.id = EcsUp, .oper = EcsNot } + }, + .cache_kind = cache_kind + }); + test_assert(f != NULL); + + ecs_entity_t parent = ecs_new_w_id(world, EcsDisabled); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_add_id(world, e2, EcsDisabled); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, EcsChildOf, parent); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, it.ids[0]); + test_uint(0, it.sources[0]); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_up_2_rel_instances(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + + ecs_add_id(world, Rel, EcsTraversable); + + ecs_entity_t b1 = ecs_new_w(world, TagA); + ecs_entity_t b2 = ecs_new_w(world, TagA); + ecs_add(world, b1, TagB); + ecs_add(world, b2, TagB); + ecs_add(world, b1, TagC); + ecs_add(world, b2, TagC); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Rel, b1); + ecs_add_pair(world, e, Rel, b2); + + ecs_query_t *f = ecs_query(world, { + .terms = { + {TagC, .src.id = EcsUp, .trav = Rel } + }, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(it.sources[0], b1); + test_uint(it.ids[0], TagC); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_up_2_rel_instances_match_2nd(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagC, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + + ecs_add_id(world, Rel, EcsTraversable); + + ecs_entity_t b1 = ecs_new_w(world, TagA); + ecs_entity_t b2 = ecs_new_w(world, TagA); + ecs_add(world, b1, TagB); + ecs_add(world, b2, TagB); + ecs_add(world, b2, TagC); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Rel, b1); + ecs_add_pair(world, e, Rel, b2); + + ecs_query_t *f = ecs_query(world, { + .terms = { + { TagC, .src.id = EcsUp, .trav = Rel } + }, + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(it.sources[0], b2); + test_uint(it.ids[0], TagC); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_up_only_w_owned(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, R, Traversable); + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_entity_t e_0 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e_1 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_entity_t e_2 = ecs_insert(world, ecs_value(Position, {50, 60})); + ecs_entity_t e_3 = ecs_insert(world, ecs_value(Position, {70, 80})); + + ecs_add_pair(world, e_3, R, e_2); + ecs_add_pair(world, e_2, R, e_1); + ecs_add_pair(world, e_1, R, e_0); + + ecs_query_t *f = ecs_query(world, { + .expr = "Position(up R)", + .cache_kind = cache_kind + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_1); + test_uint(it.sources[0], e_0); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 10); + test_int(p[0].y, 20); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_3); + test_uint(it.sources[0], e_2); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 50); + test_int(p[0].y, 60); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e_2); + test_uint(it.sources[0], e_1); + p = ecs_field(&it, Position, 0); + test_int(p[0].x, 30); + test_int(p[0].y, 40); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(f); + + ecs_fini(world); +} + +void Traversal_this_up_trav_unused_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Foo, (OnInstantiate, Inherit)); + ECS_ENTITY(world, Rel, Traversable); + + ecs_query_t *r = ecs_query(world, { + .expr = "Foo(up Rel)", + .cache_kind = cache_kind + }); + + test_assert(r != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_entity(world, { .add = ecs_ids(Foo) }); + + { + ecs_iter_t it = ecs_query_iter(world, r); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(r); + + ecs_fini(world); +} + +void Traversal_this_optional_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add_pair(world, e1, EcsIsA, base); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "?Velocity(self), Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + + while (ecs_query_next(&it)) { + Velocity *v = ecs_field(&it, Velocity, 0); + Position *p = ecs_field(&it, Position, 1); + + test_int(it.count, 1); + test_assert(p != NULL); + + if (it.entities[0] == e1) { + test_bool(ecs_field_is_set(&it, 0), false); + test_bool(ecs_field_is_set(&it, 1), true); + } else if (it.entities[0] == e2) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + test_assert(v != NULL); + } else if (it.entities[0] == e3) { + test_bool(ecs_field_is_set(&it, 0), false); + test_bool(ecs_field_is_set(&it, 1), true); + } + + count ++; + } + + test_int(count, 3); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_optional_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add_pair(world, e1, EcsIsA, base); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "?Velocity(up IsA), Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + + while (ecs_query_next(&it)) { + Velocity *v = ecs_field(&it, Velocity, 0); + Position *p = ecs_field(&it, Position, 1); + + test_int(it.count, 1); + test_assert(p != NULL); + + if (it.entities[0] == e1) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + test_assert(v != NULL); + } else if (it.entities[0] == e2) { + test_bool(ecs_field_is_set(&it, 0), false); + test_bool(ecs_field_is_set(&it, 1), true); + } else if (it.entities[0] == e3) { + test_bool(ecs_field_is_set(&it, 0), false); + test_bool(ecs_field_is_set(&it, 1), true); + } + + count ++; + } + + test_int(count, 3); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_optional_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add_pair(world, e1, EcsIsA, base); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "?Velocity(self|up IsA), Position", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + + while (ecs_query_next(&it)) { + Velocity *v = ecs_field(&it, Velocity, 0); + Position *p = ecs_field(&it, Position, 1); + + test_int(it.count, 1); + test_assert(p != NULL); + + if (it.entities[0] == e1) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + test_assert(v != NULL); + } else if (it.entities[0] == e2) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + test_assert(v != NULL); + } else if (it.entities[0] == e3) { + test_bool(ecs_field_is_set(&it, 0), false); + test_bool(ecs_field_is_set(&it, 1), true); + } + + count ++; + } + + test_int(count, 3); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_optional_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add_pair(world, e1, EcsIsA, base); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Velocity(self)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + + while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + + test_int(it.count, 1); + test_assert(p != NULL); + + if (it.entities[0] == e1) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), false); + test_assert(v == NULL); + } else if (it.entities[0] == e2) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + test_assert(v != NULL); + } else if (it.entities[0] == e3) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), false); + test_assert(v == NULL); + } + + count ++; + } + + test_int(count, 3); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_optional_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add_pair(world, e1, EcsIsA, base); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Velocity(up IsA)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + + while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + + test_int(it.count, 1); + test_assert(p != NULL); + + if (it.entities[0] == e1) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + test_assert(v != NULL); + } else if (it.entities[0] == e2) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), false); + } else if (it.entities[0] == e3) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), false); + } + + count ++; + } + + test_int(count, 3); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_optional_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_entity_t base = ecs_new_w(world, Velocity); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add_pair(world, e1, EcsIsA, base); + + ecs_entity_t e2 = ecs_new_w(world, Position); + ecs_add(world, e2, Velocity); + + ecs_entity_t e3 = ecs_new_w(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, ?Velocity(self|up IsA)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + int32_t count = 0; + + while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + + test_int(it.count, 1); + test_assert(p != NULL); + + if (it.entities[0] == e1) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + test_assert(v != NULL); + } else if (it.entities[0] == e2) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), true); + test_assert(v != NULL); + } else if (it.entities[0] == e3) { + test_bool(ecs_field_is_set(&it, 0), true); + test_bool(ecs_field_is_set(&it, 1), false); + } + + count ++; + } + + test_int(count, 3); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_fixed_src_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Velocity); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ECS_COMPONENT(world, Mass); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + + ecs_entity_t Game = ecs_entity(world, { .name = "Game" }); + ecs_set(world, Game, Position, {10, 20}); + ecs_set(world, Game, Velocity, {1, 2}); + + ecs_entity_t p = ecs_new_w_id(world, EcsPrefab); + ecs_set(world, p, Mass, {30}); + ecs_entity_t e = ecs_entity(world, { .add = ecs_ids(ecs_isa(p)) }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Velocity(Game), Mass(up IsA)", + .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(e, it.entities[0]); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 0)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 1)); + test_uint(Game, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_match_empty_table_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = cache_kind, + .flags = EcsQueryMatchEmptyTables + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e1 = ecs_entity(world, { .parent = p }); + ecs_table_t *t1 = ecs_get_table(world, e1); + + ecs_add(world, e1, TagA); // (ChildOf, p) is empty + ecs_table_t *t2 = ecs_get_table(world, e1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_assert(it.table == t1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(p, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_assert(it.table == t2); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(p, ecs_field_src(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_match_empty_table_up_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagB, Position(up)", + .cache_kind = cache_kind, + .flags = EcsQueryMatchEmptyTables + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e1 = ecs_entity(world, { .parent = p }); + ecs_add(world, e1, TagB); + ecs_table_t *t1 = ecs_get_table(world, e1); + + ecs_add(world, e1, TagA); // TagB, (ChildOf, p) is empty + ecs_table_t *t2 = ecs_get_table(world, e1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_assert(it.table == t1); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_assert(it.table == t2); + test_uint(e1, it.entities[0]); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_match_empty_table_up_implicit_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up)", + .cache_kind = cache_kind, + .flags = EcsQueryMatchEmptyTables + }); + + test_assert(q != NULL); + + ecs_entity_t b = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t p = ecs_entity(world, { .add = ecs_ids(ecs_isa(b)) }); + ecs_entity_t e1 = ecs_entity(world, { .parent = p }); + ecs_table_t *t1 = ecs_get_table(world, e1); + + ecs_add(world, e1, TagA); // (ChildOf, p) is empty + ecs_table_t *t2 = ecs_get_table(world, e1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_assert(it.table == t1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(b, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_assert(it.table == t2); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(b, ecs_field_src(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_match_empty_table_up_written_implicit_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagB, Position(up)", + .cache_kind = cache_kind, + .flags = EcsQueryMatchEmptyTables + }); + + test_assert(q != NULL); + + ecs_entity_t b = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t p = ecs_entity(world, { .add = ecs_ids(ecs_isa(b)) }); + ecs_entity_t e1 = ecs_entity(world, { .parent = p }); + ecs_add(world, e1, TagB); + ecs_table_t *t1 = ecs_get_table(world, e1); + + ecs_add(world, e1, TagA); // TagB, (ChildOf, p) is empty + ecs_table_t *t2 = ecs_get_table(world, e1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_assert(it.table == t1); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(b, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_assert(it.table == t2); + test_uint(e1, it.entities[0]); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(b, ecs_field_src(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_match_empty_table_up_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(up IsA)", + .cache_kind = cache_kind, + .flags = EcsQueryMatchEmptyTables + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p)) }); + ecs_table_t *t1 = ecs_get_table(world, e1); + + ecs_add(world, e1, TagA); // (ChildOf, p) is empty + ecs_table_t *t2 = ecs_get_table(world, e1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_assert(it.table == t1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(p, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_assert(it.table == t2); + test_uint(e1, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(p, ecs_field_src(&it, 0)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_match_empty_table_up_written_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ECS_ENTITY(world, TagA, (OnInstantiate, Inherit)); + ECS_ENTITY(world, TagB, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagB, Position(up IsA)", + .cache_kind = cache_kind, + .flags = EcsQueryMatchEmptyTables + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e1 = ecs_entity(world, { .add = ecs_ids(ecs_isa(p)) }); + ecs_add(world, e1, TagB); + ecs_table_t *t1 = ecs_get_table(world, e1); + + ecs_add(world, e1, TagA); // TagB, (ChildOf, p) is empty + ecs_table_t *t2 = ecs_get_table(world, e1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_assert(it.table == t1); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_assert(it.table == t2); + test_uint(e1, it.entities[0]); + test_uint(TagB, ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(p, ecs_field_src(&it, 1)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_up_after_add_batched_to_parent(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = Foo, .src.id = EcsUp } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_defer_begin(world); + ecs_add(world, e1, Foo); + ecs_add(world, e1, Bar); + ecs_defer_end(world); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_up_component_after_parent_table_change(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Position), .src.id = EcsUp } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + } + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + } + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_up_component_w_singleton_after_parent_table_change(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Position), .src.id = EcsUp }, + { .id = ecs_id(Velocity), .src.id = EcsSingleton } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_singleton_set(world, Velocity, {1, 2}); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_assert(v != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + test_int(v->x, 1); test_int(v->y, 2); + } + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, ecs_id(Velocity))); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_assert(v != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + test_int(v->x, 1); test_int(v->y, 2); + } + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, ecs_id(Velocity))); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_up_component_w_var_after_parent_table_change(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, Position(up), (Velocity, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + ecs_set_pair(world, e2, Velocity, e2, {1, 2}); + + ecs_entity_t e3 = ecs_new(world); + ecs_add_pair(world, e3, EcsChildOf, e1); + ecs_set(world, e3, Position, {50, 60}); + ecs_set_pair(world, e3, Velocity, e3, {3, 4}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Velocity, e2), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_assert(v != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + test_int(v->x, 1); test_int(v->y, 2); + } + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, e2)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Velocity, e3), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_assert(v != NULL); + test_int(p1->x, 50); test_int(p1->y, 60); + test_int(p2->x, 10); test_int(p2->y, 20); + test_int(v->x, 3); test_int(v->y, 4); + } + + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e3)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, e3)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Velocity, e2), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_assert(v != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + test_int(v->x, 1); test_int(v->y, 2); + } + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, e2)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Velocity, e3), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_assert(v != NULL); + test_int(p1->x, 50); test_int(p1->y, 60); + test_int(p2->x, 10); test_int(p2->y, 20); + test_int(v->x, 3); test_int(v->y, 4); + } + + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e3)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, e3)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_test_up_component_after_parent_table_change(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Position), .src.id = EcsUp } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + } + + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_bool(false, ecs_query_next(&it)); + } + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + } + + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_bool(false, ecs_query_next(&it)); + } + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_test_up_component_w_singleton_after_parent_table_change(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Position), .src.id = EcsUp }, + { .id = ecs_id(Velocity), .src.id = EcsSingleton } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_singleton_set(world, Velocity, {1, 2}); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_assert(v != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + test_int(v->x, 1); test_int(v->y, 2); + } + + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, ecs_id(Velocity))); + test_bool(false, ecs_query_next(&it)); + } + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + { + Position *p1 = ecs_field(&it, Position, 0); + Position *p2 = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(p1 != NULL); + test_assert(p2 != NULL); + test_assert(v != NULL); + test_int(p1->x, 30); test_int(p1->y, 40); + test_int(p2->x, 10); test_int(p2->y, 20); + test_int(v->x, 1); test_int(v->y, 2); + } + + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, ecs_id(Velocity))); + test_bool(false, ecs_query_next(&it)); + } + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_up_component_after_parent_table_change_no_data(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Position), .src.id = EcsUp, .inout = EcsInOutNone } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_up_component_w_singleton_after_parent_table_change_no_data(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Position), .src.id = EcsUp, .inout = EcsInOutNone }, + { .id = ecs_id(Velocity), .src.id = EcsSingleton, .inout = EcsInOutNone } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_singleton_set(world, Velocity, {1, 2}); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, ecs_id(Velocity))); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, ecs_id(Velocity))); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_up_component_w_var_after_parent_table_change_no_data(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "[none] Position, [none] Position(up), [none] (Velocity, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + ecs_set_pair(world, e2, Velocity, e2, {1, 2}); + + ecs_entity_t e3 = ecs_new(world); + ecs_add_pair(world, e3, EcsChildOf, e1); + ecs_set(world, e3, Position, {50, 60}); + ecs_set_pair(world, e3, Velocity, e3, {3, 4}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Velocity, e2), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, e2)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Velocity, e3), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e3)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, e3)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Velocity, e2), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, e2)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Velocity, e3), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e3)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, e3)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_test_up_component_after_parent_table_change_no_data(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Position), .src.id = EcsUp, .inout = EcsInOutNone } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_bool(false, ecs_query_next(&it)); + } + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_bool(false, ecs_query_next(&it)); + } + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_test_up_component_w_singleton_after_parent_table_change_no_data(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position), .inout = EcsInOutNone }, + { .id = ecs_id(Position), .src.id = EcsUp, .inout = EcsInOutNone }, + { .id = ecs_id(Velocity), .src.id = EcsSingleton, .inout = EcsInOutNone } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_singleton_set(world, Velocity, {1, 2}); + + ecs_entity_t e1 = ecs_new(world); + ecs_set(world, e1, Position, {10, 20}); + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + ecs_set(world, e2, Position, {30, 40}); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, ecs_id(Velocity))); + test_bool(false, ecs_query_next(&it)); + } + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_src(&it, 2)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 2)); + test_assert(it.trs[0]->hdr.table == ecs_get_table(world, e2)); + test_assert(it.trs[1]->hdr.table == ecs_get_table(world, e1)); + test_assert(it.trs[2]->hdr.table == ecs_get_table(world, ecs_id(Velocity))); + test_bool(false, ecs_query_next(&it)); + } + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_up_childof_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsChildOf, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, e1); + + /* ecs_entity_t e = */ ecs_new_w_pair(world, EcsChildOf, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(e1, it.entities[0]); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_up_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_up_isa_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, e1); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_up_isa_childof_isa(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + /* ecs_entity_t e = */ ecs_new_w_pair(world, EcsIsA, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_up_isa_childof_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_entity_t e3 = ecs_new(world); + ecs_add_pair(world, e3, EcsIsA, e2); + + /* ecs_entity_t e = */ ecs_new_w_pair(world, EcsChildOf, e3); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_self_up_childof_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsChildOf, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, e1); + + /* ecs_entity_t e = */ ecs_new_w_pair(world, EcsChildOf, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(e0, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(e1, it.entities[0]); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_self_up_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(e0, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_self_up_isa_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, e1); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(e0, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_self_up_isa_childof_isa(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + /* ecs_entity_t e = */ ecs_new_w_pair(world, EcsIsA, e2); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(e0, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_self_up_isa_childof_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_entity_t e3 = ecs_new(world); + ecs_add_pair(world, e3, EcsIsA, e2); + + /* ecs_entity_t e = */ ecs_new_w_pair(world, EcsChildOf, e3); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(e0, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(e0, ecs_field_src(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_up_childof_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsChildOf, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, e1); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e2); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_up_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e1); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_up_isa_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, e1); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e2); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_up_isa_childof_isa(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, e2); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_up_isa_childof_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_entity_t e3 = ecs_new(world); + ecs_add_pair(world, e3, EcsIsA, e2); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e3); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_childof_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsChildOf, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, e1); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e2); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e1); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_isa_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsIsA, e1); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e2); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_isa_childof_isa(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, e2); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Traversal_this_written_self_up_isa_childof_isa_childof(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + + ecs_entity_t e0 = ecs_new(world); + ecs_add(world, e0, Foo); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, EcsIsA, e0); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, EcsChildOf, e1); + + ecs_entity_t e3 = ecs_new(world); + ecs_add_pair(world, e3, EcsIsA, e2); + + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, e3); + ecs_add(world, e, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Bar, Foo(self|up)" + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Union.c b/vendors/flecs/test/query/src/Union.c new file mode 100644 index 000000000..2ee85aa7d --- /dev/null +++ b/vendors/flecs/test/query/src/Union.c @@ -0,0 +1,4659 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void Union_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +void Union_1_fixed_union_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, e1, Movement, Walking); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement(ent, _)", + .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(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_fixed_union_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, e1, Movement, Walking); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement(ent, *)", + .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(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_fixed_union_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, e1, Movement, Walking); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement(ent, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Running); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_fixed_union_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, e1, Movement, Walking); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement(ent, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Running); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_fixed_union_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, e1, Movement, Walking); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == Walking, Movement(ent, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Running); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_this_union_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, _)", + .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(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e3, it.entities[0]); + test_uint(e4, it.entities[1]); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e5, it.entities[0]); + test_uint(e6, it.entities[1]); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_this_union_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, *)", + .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(e2, it.entities[0]); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_this_union_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Walking)", + .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(e2, it.entities[0]); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Running)", + .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(e5, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Sitting)", + .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(e6, it.entities[0]); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Union_1_this_union_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 0)); + test_uint(Sitting, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_this_union_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == Running, (Movement, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_var_union_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement($y, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_var_union_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement($y, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 0)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_var_union_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "Movement($y, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "Movement($y, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "Movement($y, Sitting)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 0)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_iter_get_var(&it, y_var)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Union_1_var_union_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Movement($y, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 0)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(Sitting, ecs_iter_get_var(&it, x_var)); + test_uint(e6, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_var_union_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == Running, Movement($y, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_this_written_union_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, TagA); + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, (Movement, _)", + .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(2, it.count); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e3, it.entities[0]); + test_uint(e4, it.entities[1]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e5, it.entities[0]); + test_uint(e6, it.entities[1]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_this_written_union_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, (Movement, *)", + .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(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_this_written_union_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, (Movement, Walking)", + .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(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, (Movement, Running)", + .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(e3, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, (Movement, Sitting)", + .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(e6, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Union_1_this_written_union_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, (Movement, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + test_uint(Sitting, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_this_written_union_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == Running, TagA, (Movement, $x)", + // Order changes for cached query + // .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_var_written_union_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($y), Movement($y, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_var_written_union_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($y), Movement($y, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_var_written_union_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($y), Movement($y, Walking)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($y), Movement($y, Running)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($y), Movement($y, Sitting)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, y_var)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Union_1_var_written_union_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($y), Movement($y, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + test_uint(Walking, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e4, ecs_field_src(&it, 0)); + test_uint(e4, ecs_field_src(&it, 1)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e5, ecs_field_src(&it, 0)); + test_uint(e5, ecs_field_src(&it, 1)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + test_uint(Sitting, ecs_iter_get_var(&it, x_var)); + test_uint(e6, ecs_field_src(&it, 0)); + test_uint(e6, ecs_field_src(&it, 1)); + test_uint(e6, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_1_var_written_union_tgt_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + /* ecs_entity_t e7 = */ ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == Running, TagA($y), Movement($y, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e5, ecs_field_src(&it, 1)); + test_uint(e5, ecs_field_src(&it, 2)); + test_uint(e5, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e4, ecs_field_src(&it, 1)); + test_uint(e4, ecs_field_src(&it, 2)); + test_uint(e4, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e3, ecs_field_src(&it, 2)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_not_fixed_union_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Movement(ent, _)", + .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(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Walking); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_not_fixed_union_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Movement(ent, *)", + .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(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Walking); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, Movement, EcsWildcard); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_not_fixed_union_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Movement(ent, Running)", + .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(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Walking); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Running); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_remove_pair(world, e1, Movement, EcsWildcard); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_not_fixed_union_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "!Movement(ent, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Walking); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_not_fixed_union_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == Running, !Movement(ent, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Walking); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(0, it.count); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Movement, Running); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_not_this_written_union_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + ecs_entity_t e7 = ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !(Movement, _)", + .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(e7, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_not_this_written_union_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + ecs_entity_t e7 = ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !(Movement, *)", + .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(e7, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_not_this_written_union_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + ecs_entity_t e7 = ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + { + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !(Movement, Walking)", + .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(e7, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !(Movement, Running)", + .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(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + { + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !(Movement, Sitting)", + .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(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e3, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e4, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e5, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(ecs_pair(Movement, Sitting), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + } + + ecs_fini(world); +} + +void Union_not_this_written_union_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + ecs_entity_t e7 = ecs_new_w(world, TagA); + ecs_entity_t e8 = ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, !(Movement, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_uint(e7, it.entities[0]); + test_uint(e8, it.entities[1]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Movement, EcsWildcard), ecs_field_id(&it, 1)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_not_this_written_union_var_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Sitting); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + ecs_entity_t e6 = ecs_new(world); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Walking); + ecs_add_pair(world, e3, Movement, Running); + ecs_add_pair(world, e4, Movement, Running); + ecs_add_pair(world, e5, Movement, Running); + ecs_add_pair(world, e6, Movement, Sitting); + + ecs_add(world, e1, TagA); + ecs_add(world, e2, TagA); + ecs_add(world, e3, TagA); + ecs_add(world, e4, TagA); + ecs_add(world, e5, TagA); + ecs_add(world, e6, TagA); + + ecs_entity_t e7 = ecs_new_w(world, TagA); + + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + ecs_add(world, e5, Bar); + ecs_add(world, e6, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x == Running, TagA, !(Movement, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e7, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e6, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 2)); + test_uint(Running, ecs_iter_get_var(&it, x_var)); + test_bool(true, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 2)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_query_switch(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, *)" + }); + test_assert(q != NULL); + + ECS_ENTITY(world, e1, (Movement, Running)); + ECS_ENTITY(world, e2, (Movement, Walking)); + ECS_ENTITY(world, e3, (Movement, Running)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_query_1_case_1_type(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Running)" + }); + test_assert(q != NULL); + + ECS_ENTITY(world, e1, (Movement, Running)); + ECS_ENTITY(world, e2, (Movement, Walking)); + ECS_ENTITY(world, e3, (Movement, Running)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(it.ids[0], ecs_pair(Movement, Running)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.ids[0], ecs_pair(Movement, Running)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_query_1_case_2_types(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Running)" + }); + test_assert(q != NULL); + + ECS_ENTITY(world, e1, (Movement, Running)); + ECS_ENTITY(world, e2, (Movement, Walking)); + ECS_ENTITY(world, e3, (Movement, Running)); + ECS_ENTITY(world, e4, (Movement, Walking), Position); + ECS_ENTITY(world, e5, (Movement, Running), Position); + ECS_ENTITY(world, e6, (Movement, Walking), Position); + ECS_ENTITY(world, e7, (Movement, Running), Position); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e7); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e5); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_query_2_cases_1_type(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_ENTITY(world, Direction, Union); + ECS_TAG(world, Front); + ECS_TAG(world, Back); + ECS_TAG(world, Left); + ECS_TAG(world, Right); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Running), (Direction, Front)" + }); + test_assert(q != NULL); + + ECS_ENTITY(world, e1, (Movement, Running), (Direction, Front)); + ECS_ENTITY(world, e2, (Movement, Walking), (Direction, Front)); + ECS_ENTITY(world, e3, (Movement, Running), (Direction, Back)); + ECS_ENTITY(world, e4, (Movement, Running), (Direction, Front)); + ECS_ENTITY(world, e5, (Movement, Walking), (Direction, Front)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e4); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_query_2_cases_2_types(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_ENTITY(world, Direction, Union); + ECS_TAG(world, Front); + ECS_TAG(world, Back); + ECS_TAG(world, Left); + ECS_TAG(world, Right); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Running), (Direction, Front)" + }); + test_assert(q != NULL); + + ECS_ENTITY(world, e1, (Movement, Running), (Direction, Front)); + ECS_ENTITY(world, e2, (Movement, Walking), (Direction, Front)); + ECS_ENTITY(world, e3, (Movement, Running), (Direction, Back)); + ECS_ENTITY(world, e4, (Movement, Running), (Direction, Front)); + ECS_ENTITY(world, e5, (Movement, Walking), (Direction, Front)); + ECS_ENTITY(world, e6, Position, (Movement, Walking), (Direction, Front)); + ECS_ENTITY(world, e7, Position, (Movement, Running), (Direction, Front)); + ECS_ENTITY(world, e8, Position, (Movement, Walking), (Direction, Front)); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e7); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e4); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 1)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_pair(Movement, Running), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Direction, Front), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_query_after_remove(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_ENTITY(world, e1, (Movement, Walking)); + ECS_ENTITY(world, e2, (Movement, Walking)); + ECS_ENTITY(world, e3, (Movement, Running)); + ECS_ENTITY(world, e4, (Movement, Running)); + ECS_ENTITY(world, e5, (Movement, Running)); + ECS_ENTITY(world, e6, (Movement, Jumping)); + ECS_ENTITY(world, e7, (Movement, Jumping)); + + ecs_query_t *q_walking = ecs_query(world, { + .expr = "(Movement, Walking)" + }); + ecs_query_t *q_running = ecs_query(world, { + .expr = "(Movement, Running)" + }); + ecs_query_t *q_jumping = ecs_query(world, { + .expr = "(Movement, Jumping)" + }); + + /* Verify all queries are correctly matched */ + ecs_iter_t it = ecs_query_iter(world, q_walking); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e2); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e1); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, q_running); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e5); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e4); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e3); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, q_jumping); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e7); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e6); + test_assert(!ecs_query_next(&it)); + + ecs_remove_pair(world, e4, Movement, Running); + test_assert(!ecs_has_pair(world, e4, Movement, Running)); + ecs_entity_t c = ecs_get_target(world, e4, Movement, 0); + test_int(c, 0); + + /* Verify queries are still correctly matched, now excluding e4 */ + it = ecs_query_iter(world, q_walking); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e2); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e1); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, q_running); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e5); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e3); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, q_jumping); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e7); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e6); + test_assert(!ecs_query_next(&it)); + + ecs_add_pair(world, e4, Movement, Running); + test_assert(ecs_has_pair(world, e4, Movement, Running)); + c = ecs_get_target(world, e4, Movement, 0); + test_int(c, Running); + + /* Verify e4 is now matched again */ + it = ecs_query_iter(world, q_walking); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e2); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e1); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, q_running); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e4); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e5); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e3); + test_assert(!ecs_query_next(&it)); + + it = ecs_query_iter(world, q_jumping); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e7); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); test_int(it.entities[0], e6); + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q_walking); + ecs_query_fini(q_jumping); + ecs_query_fini(q_running); + + ecs_fini(world); +} + +static +int compare_position(ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2) { + const Position *p1 = ptr1; + const Position *p2 = ptr2; + return (p1->x > p2->x) - (p1->x < p2->x); +} + +void Union_sort(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + ECS_TAG(world, Sitting); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {3, 2})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {2, 2})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {1, 2})); + ecs_entity_t e4 = ecs_insert(world, ecs_value(Position, {0, 2})); + + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e2, Movement, Running); + ecs_add_pair(world, e3, Movement, Jumping); + ecs_add_pair(world, e4, Movement, Sitting); + + ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .terms = {{ ecs_id(Position) }}, + .order_by = ecs_id(Position), + .order_by_callback = compare_position + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(it.count, 4); + test_assert(it.entities[0] == e4); + test_assert(it.entities[1] == e3); + test_assert(it.entities[2] == e2); + test_assert(it.entities[3] == e1); + test_assert(!ecs_query_next(&it)); + + /* Entities will have shuffled around, ensure cases got shuffled too */ + test_uint(ecs_get_target(world, e1, Movement, 0), Walking); + test_uint(ecs_get_target(world, e2, Movement, 0), Running); + test_uint(ecs_get_target(world, e3, Movement, 0), Jumping); + test_uint(ecs_get_target(world, e4, Movement, 0), Sitting); + + ecs_fini(world); +} + +void Union_query_recycled_tags(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + + ECS_TAG(world, Standing); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + test_assert(Standing > UINT32_MAX); + test_assert(Walking > UINT32_MAX); + test_assert(Running > UINT32_MAX); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, Movement, Standing); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Standing)" + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e); + test_uint(ecs_pair(Movement, Standing), ecs_field_id(&it, 0)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_query_single_case(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e1 != 0); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e2 != 0); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e3 != 0); + + test_assert(ecs_has_pair(world, e1, Movement, Walking)); + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e3, Movement, Walking)); + + ecs_delete(world, e1); + + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + test_assert(ecs_has_pair(world, e3, Movement, Walking)); + + ecs_delete(world, e3); + + test_assert(ecs_has_pair(world, e2, Movement, Walking)); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e2); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_match_switch_on_base_instance(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t base = ecs_new(world); + test_assert(base != 0); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + test_assert(e1 != 0); + ecs_add_pair(world, e1, EcsIsA, base); + + test_uint(ecs_get_target(world, e1, Movement, 0), Walking); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e1); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_switch_w_bitset_query(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_COMPONENT(world, Position); + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e1, Position, {10, 20}); + ecs_enable_component(world, e1, Position, true); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e2, Position, {11, 22}); + ecs_enable_component(world, e2, Position, false); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e3, Position, {13, 23}); + + test_bool(ecs_is_enabled(world, e1, Position), true); + test_bool(ecs_is_enabled(world, e2, Position), false); + + Position *p; + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e3); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e1); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_switch_w_bitset_query_inv(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_COMPONENT(world, Position); + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e1, Position, {10, 20}); + ecs_enable_component(world, e1, Position, true); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e2, Position, {11, 22}); + ecs_enable_component(world, e2, Position, true); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e3, Position, {13, 23}); + + Position *p; + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e3); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e1); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_switch_w_bitset_query_2_elems(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_COMPONENT(world, Position); + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e1, Position, {10, 20}); + ecs_enable_component(world, e1, Position, true); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e2, Position, {11, 22}); + ecs_enable_component(world, e2, Position, true); + + ecs_entity_t e0 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e0, Position, {11, 22}); + ecs_enable_component(world, e0, Position, false); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e3, Position, {13, 23}); + + Position *p; + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e3); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e1); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e2); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 22); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_switch_w_bitset_query_2_elems_skip(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_COMPONENT(world, Position); + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e1, Position, {10, 20}); + ecs_enable_component(world, e1, Position, true); + + ecs_entity_t e0 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e0, Position, {11, 22}); + ecs_enable_component(world, e0, Position, false); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e2, Position, {11, 22}); + ecs_enable_component(world, e2, Position, true); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e3, Position, {13, 23}); + + Position *p; + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e3); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e1); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e2); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 22); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_switch_w_bitset_query_elems_interleaved(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_COMPONENT(world, Position); + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e1, Position, {10, 20}); + ecs_enable_component(world, e1, Position, false); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e2, Position, {11, 22}); + ecs_enable_component(world, e2, Position, true); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e3, Position, {13, 23}); + ecs_enable_component(world, e3, Position, false); + + ecs_entity_t e4 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e4, Position, {13, 23}); + ecs_enable_component(world, e4, Position, true); + + ecs_entity_t e5 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e5, Position, {13, 23}); + ecs_enable_component(world, e5, Position, true); + + ecs_entity_t e6 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e6, Position, {13, 23}); + ecs_enable_component(world, e6, Position, true); + + ecs_entity_t e7 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e7, Position, {13, 23}); + ecs_enable_component(world, e7, Position, false); + + ecs_entity_t e8 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e8, Position, {13, 23}); + ecs_enable_component(world, e8, Position, true); + + Position *p; + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e5); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e8); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_switch_w_bitset_query_elems_interleaved_2_types(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_COMPONENT(world, Position); + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position, (Movement, Walking)" + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e1, Position, {10, 20}); + ecs_enable_component(world, e1, Position, false); + + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e2, Position, {11, 22}); + ecs_enable_component(world, e2, Position, true); + + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e3, Position, {13, 23}); + ecs_enable_component(world, e3, Position, false); + + ecs_entity_t e4 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e4, Position, {13, 23}); + ecs_enable_component(world, e4, Position, true); + + ecs_entity_t e5 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e5, Position, {13, 23}); + ecs_enable_component(world, e5, Position, true); + + ecs_entity_t e6 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e6, Position, {13, 23}); + ecs_enable_component(world, e6, Position, true); + + ecs_entity_t e7 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e7, Position, {13, 23}); + ecs_enable_component(world, e7, Position, false); + + ecs_entity_t e8 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e8, Position, {13, 23}); + ecs_enable_component(world, e8, Position, true); + + + ecs_entity_t e1_2 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e1_2, Position, {10, 20}); + ecs_enable_component(world, e1_2, Position, false); + ecs_add_id(world, e1_2, Tag); + + ecs_entity_t e2_2 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e2_2, Position, {11, 22}); + ecs_enable_component(world, e2_2, Position, true); + ecs_add_id(world, e2_2, Tag); + + ecs_entity_t e3_2 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e3_2, Position, {13, 23}); + ecs_enable_component(world, e3_2, Position, false); + ecs_add_id(world, e3_2, Tag); + + ecs_entity_t e4_2 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e4_2, Position, {13, 23}); + ecs_enable_component(world, e4_2, Position, true); + ecs_add_id(world, e4_2, Tag); + + ecs_entity_t e5_2 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e5_2, Position, {13, 23}); + ecs_enable_component(world, e5_2, Position, true); + ecs_add_id(world, e5_2, Tag); + + ecs_entity_t e6_2 = ecs_new_w_pair(world, Movement, Running); + ecs_set(world, e6_2, Position, {13, 23}); + ecs_enable_component(world, e6_2, Position, true); + ecs_add_id(world, e6_2, Tag); + + ecs_entity_t e7_2 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e7_2, Position, {13, 23}); + ecs_enable_component(world, e7_2, Position, false); + ecs_add_id(world, e7_2, Tag); + + ecs_entity_t e8_2 = ecs_new_w_pair(world, Movement, Walking); + ecs_set(world, e8_2, Position, {13, 23}); + ecs_enable_component(world, e8_2, Position, true); + ecs_add_id(world, e8_2, Tag); + + Position *p; + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e5); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e8); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e5_2); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_int(it.entities[0], e8_2); + p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + test_uint(ecs_pair(Movement, Walking), ecs_field_id(&it, 1)); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_has_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_entity_t e = ecs_new(world); + test_assert( !ecs_has_pair(world, e, Movement, EcsWildcard)); + + ecs_add_pair(world, e, Movement, Walking); + test_assert( ecs_has_pair(world, e, Movement, EcsWildcard)); + + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_assert( !ecs_has_pair(world, e, Movement, Running)); + test_assert( !ecs_has_pair(world, e, Movement, Jumping)); + test_assert( ecs_has_pair(world, e, Movement, EcsWildcard)); + + ecs_fini(world); +} + +void Union_remove_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ecs_entity_t e = ecs_new_w_pair(world, Movement, Walking); + test_assert(e != 0); + + test_assert( ecs_has_pair(world, e, Movement, Walking)); + test_assert( !ecs_has_pair(world, e, Movement, Running)); + test_assert( !ecs_has_pair(world, e, Movement, Jumping)); + test_uint(ecs_get_target(world, e, Movement, 0), Walking); + + ecs_remove_pair(world, e, Movement, EcsWildcard); + + test_assert( !ecs_has_pair(world, e, Movement, Walking)); + test_assert( !ecs_has_pair(world, e, Movement, Running)); + test_assert( !ecs_has_pair(world, e, Movement, Jumping)); + test_uint(ecs_get_target(world, e, Movement, 0), 0); + test_assert( !ecs_has_pair(world, e, Movement, EcsWildcard)); + + ecs_fini(world); +} + +void Union_component_relation(void) { + ecs_world_t *world = ecs_init(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + ECS_TAG(world, Jumping); + + ECS_ENTITY(world, Direction, Union); + ECS_TAG(world, Front); + ECS_TAG(world, Back); + ECS_TAG(world, Left); + ECS_TAG(world, Right); + + ecs_set(world, Movement, EcsComponent, { .size = 1, .alignment = 1 }); + ecs_set(world, Direction, EcsComponent, { .size = 1, .alignment = 1 }); + + ecs_entity_t e1 = ecs_new(world); + ecs_add_pair(world, e1, Movement, Walking); + ecs_add_pair(world, e1, Direction, Front); + + ecs_entity_t e2 = ecs_new(world); + ecs_add_pair(world, e2, Movement, Running); + ecs_add_pair(world, e2, Direction, Left); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Walking), (Direction, *)" + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(it.ids[0], ecs_pair(Movement, Walking)); + test_uint(it.ids[1], ecs_pair(Direction, Front)); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_switch_term_filter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Sw, Union); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e = ecs_new_w_pair(world, Sw, TagA); + ecs_table_t *table = ecs_get_table(world, e); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Sw, EcsWildcard), .inout = EcsInOutNone }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e); + test_uint(ecs_pair(Sw, TagA), ecs_field_id(&it, 0)); + test_assert(it.table == table); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_2_terms_switch_term_filter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Sw, Union); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new_w_pair(world, Sw, TagA); + ecs_add(world, e, Position); + ecs_table_t *table = ecs_get_table(world, e); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_pair(Sw, EcsWildcard), .inout = EcsInOutNone } + } + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e); + test_int(it.sizes[0], ECS_SIZEOF(Position)); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Sw, TagA), ecs_field_id(&it, 1)); + test_assert(it.table == table); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_match_switch_w_switch(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Sw, Union); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e = ecs_new_w_pair(world, Sw, TagA); + ecs_table_t *table = ecs_get_table(world, e); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(Sw, EcsWildcard) }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e); + test_uint(ecs_pair(Sw, TagA), ecs_field_id(&it, 0)); + test_assert(it.table == table); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_match_switch_w_case(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Sw, Union); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e = ecs_new_w_pair(world, Sw, TagA); + ecs_table_t *table = ecs_get_table(world, e); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(Sw, TagA) }} + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e); + test_assert(it.table == table); + test_uint(ecs_pair(Sw, TagA), ecs_field_id(&it, 0)); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_match_switch_w_case_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, SwX, Union); + ECS_ENTITY(world, SwY, Union); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagD); + + ecs_entity_t e = ecs_new_w_pair(world, SwX, TagA); + ecs_add_pair(world, e, SwY, TagC); + ecs_table_t *table = ecs_get_table(world, e); + + /* Not matched */ + ecs_new_w_pair(world, SwX, TagA); + ecs_new_w_pair(world, SwY, TagC); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(SwX, TagA) }, + { ecs_pair(SwY, TagC) } + } + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e); + test_assert(it.table == table); + test_uint(it.ids[0], ecs_pair(SwX, TagA)); + test_uint(it.ids[1], ecs_pair(SwY, TagC)); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, SwX, Union); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t b1 = ecs_new_w_pair(world, SwX, TagA); + ecs_entity_t b2 = ecs_new_w_pair(world, SwX, TagB); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, b1); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, b1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, b2); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, b2); + ecs_add_pair(world, e4, SwX, TagC); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(SwX, EcsWildcard), .src.id = EcsUp } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.entities[0], e1); + test_int(it.entities[1], e2); + test_uint(it.ids[0], ecs_pair(SwX, TagA)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_uint(it.ids[0], ecs_pair(SwX, TagB)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e4); + test_uint(it.ids[0], ecs_pair(SwX, TagB)); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, SwX, Union); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t b1 = ecs_new_w_pair(world, SwX, TagA); + ecs_entity_t b2 = ecs_new_w_pair(world, SwX, TagB); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, b1); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, b1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, b2); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, b2); + ecs_add_pair(world, e4, SwX, TagC); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(SwX, EcsWildcard), .src.id = EcsSelf|EcsUp } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], b1); + test_uint(it.ids[0], ecs_pair(SwX, TagA)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.entities[0], e1); + test_int(it.entities[1], e2); + test_uint(it.ids[0], ecs_pair(SwX, TagA)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e4); + test_uint(it.ids[0], ecs_pair(SwX, TagC)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], b2); + test_uint(it.ids[0], ecs_pair(SwX, TagB)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_uint(it.ids[0], ecs_pair(SwX, TagB)); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_up_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, SwX, Union); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, Foo); + + ecs_entity_t b1 = ecs_new_w_pair(world, SwX, TagA); + ecs_entity_t b2 = ecs_new_w_pair(world, SwX, TagB); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, b1); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, b1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, b2); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, b2); + ecs_add_pair(world, e4, SwX, TagC); + + ecs_add(world, b1, Foo); + ecs_add(world, b2, Foo); + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { Foo }, + { ecs_pair(SwX, EcsWildcard), .src.id = EcsUp } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.entities[0], e1); + test_int(it.entities[1], e2); + test_uint(it.ids[0], Foo); + test_uint(it.ids[1], ecs_pair(SwX, TagA)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_uint(it.ids[0], Foo); + test_uint(it.ids[1], ecs_pair(SwX, TagB)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e4); + test_uint(it.ids[0], Foo); + test_uint(it.ids[1], ecs_pair(SwX, TagB)); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_self_up_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, SwX, Union); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, Foo); + + ecs_entity_t b1 = ecs_new_w_pair(world, SwX, TagA); + ecs_entity_t b2 = ecs_new_w_pair(world, SwX, TagB); + ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, b1); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsChildOf, b1); + ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, b2); + ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, b2); + ecs_add_pair(world, e4, SwX, TagC); + + ecs_add(world, b1, Foo); + ecs_add(world, b2, Foo); + ecs_add(world, e1, Foo); + ecs_add(world, e2, Foo); + ecs_add(world, e3, Foo); + ecs_add(world, e4, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { Foo }, + { ecs_pair(SwX, EcsWildcard), .src.id = EcsSelf|EcsUp } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], b1); + test_uint(it.ids[0], Foo); + test_uint(it.ids[1], ecs_pair(SwX, TagA)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], b2); + test_uint(it.ids[0], Foo); + test_uint(it.ids[1], ecs_pair(SwX, TagB)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 2); + test_int(it.entities[0], e1); + test_int(it.entities[1], e2); + test_uint(it.ids[0], Foo); + test_uint(it.ids[1], ecs_pair(SwX, TagA)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e3); + test_uint(it.ids[0], Foo); + test_uint(it.ids[1], ecs_pair(SwX, TagB)); + + test_bool(ecs_query_next(&it), true); + test_int(it.count, 1); + test_int(it.entities[0], e4); + test_uint(it.ids[0], Foo); + test_uint(it.ids[1], ecs_pair(SwX, TagC)); + + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_existing_union_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, *)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_new_union_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, *)", + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + ecs_entity_t e2 = ecs_new_w_pair(world, Movement, Running); + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Running)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_existing_union_table_w_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + ecs_new_w_pair(world, Movement, Running); + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Walking)", + .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(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_new_union_table_w_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Movement, Walking)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + ecs_new_w_pair(world, Movement, Running); + ecs_entity_t e3 = ecs_new_w_pair(world, Movement, Walking); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_tgt_w_generation(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Union); + + ecs_entity_t tgt_1 = ecs_new(world); + ecs_delete(world, tgt_1); + tgt_1 = ecs_new(world); + test_assert(tgt_1 != (uint32_t)tgt_1); + + ecs_entity_t tgt_2 = ecs_new(world); + ecs_delete(world, tgt_2); + tgt_2 = ecs_new(world); + test_assert(tgt_2 != (uint32_t)tgt_2); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, *)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, tgt_1); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, tgt_2); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_field_id(&it, 0), ecs_pair(Rel, tgt_2)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_field_id(&it, 0), ecs_pair(Rel, tgt_1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_tgt_w_not_alive(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Union); + + ecs_entity_t tgt_1 = ecs_new(world); + ecs_delete(world, tgt_1); + tgt_1 = ecs_new(world); + test_assert(tgt_1 != (uint32_t)tgt_1); + + ecs_entity_t tgt_2 = ecs_new(world); + ecs_delete(world, tgt_2); + tgt_2 = ecs_new(world); + test_assert(tgt_2 != (uint32_t)tgt_2); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, *)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Rel, tgt_1); + ecs_entity_t e2 = ecs_new_w_pair(world, Rel, tgt_2); + + ecs_delete(world, tgt_1); + ecs_delete(world, tgt_2); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_field_id(&it, 0), ecs_pair(Rel, tgt_2)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_field_id(&it, 0), ecs_pair(Rel, tgt_1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_for_switch_filter_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_query_t *q = ecs_query(world, { + .expr = "[none] (Movement, *)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_pair(world, Movement, Walking); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_union_from_nothing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { Tag }, + { ecs_pair(Movement, EcsWildcard), .src.id = EcsIsEntity } + }, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_id(world, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_field_id(&it, 0), Tag); + test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, EcsWildcard)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_union_tgt_from_nothing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_query_t *q = ecs_query(world, { + .terms = { + {Tag}, + {ecs_pair(Movement, Walking), .src.id = EcsIsEntity } + }, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_new_w_id(world, Tag); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_field_id(&it, 0), Tag); + test_uint(ecs_field_id(&it, 1), ecs_pair(Movement, Walking)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Union_tgt_inherited(void) { + test_quarantine("Aug 1st 2022"); + + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Movement, Union); + ECS_TAG(world, Walking); + ECS_TAG(world, Running); + + ecs_entity_t base_1 = ecs_new_w_pair(world, Movement, Walking); + ecs_entity_t inst_1 = ecs_new_w_pair(world, EcsIsA, base_1); + + ecs_entity_t base_2 = ecs_new_w_pair(world, Movement, Running); + ecs_new_w_pair(world, EcsIsA, base_2); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Movement, Walking) } + }, + .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(it.count, 1); + test_uint(it.entities[0], base_1); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], inst_1); + test_uint(ecs_field_id(&it, 0), ecs_pair(Movement, Walking)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Validator.c b/vendors/flecs/test/query/src/Validator.c new file mode 100644 index 000000000..db6d40495 --- /dev/null +++ b/vendors/flecs/test/query/src/Validator.c @@ -0,0 +1,3600 @@ +#include + +static +void query_flags_to_str(uint64_t value) { + if (value & EcsQueryMatchPrefab) { + printf("EcsQueryMatchPrefab|"); + } + if (value & EcsQueryMatchDisabled) { + printf("EcsQueryMatchDisabled|"); + } + if (value & EcsQueryMatchEmptyTables) { + printf("EcsQueryMatchEmptyTables|"); + } + if (value & EcsQueryAllowUnresolvedByName) { + printf("EcsQueryAllowUnresolvedByName|"); + } + if (value & EcsQueryTableOnly) { + printf("EcsQueryTableOnly|"); + } + if (value & EcsQueryMatchThis) { + printf("EcsQueryMatchThis|"); + } + if (value & EcsQueryMatchOnlyThis) { + printf("EcsQueryMatchOnlyThis|"); + } + if (value & EcsQueryMatchOnlySelf) { + printf("EcsQueryMatchOnlySelf|"); + } + if (value & EcsQueryMatchWildcards) { + printf("EcsQueryMatchWildcards|"); + } + if (value & EcsQueryHasCondSet) { + printf("EcsQueryHasCondSet|"); + } + if (value & EcsQueryHasPred) { + printf("EcsQueryHasPred|"); + } + if (value & EcsQueryHasScopes) { + printf("EcsQueryHasScopes|"); + } + if (value & EcsQueryHasRefs) { + printf("EcsQueryHasRefs|"); + } + if (value & EcsQueryHasOutTerms) { + printf("EcsQueryHasOutTerms|"); + } + if (value & EcsQueryHasNonThisOutTerms) { + printf("EcsQueryHasNonThisOutTerms|"); + } + if (value & EcsQueryHasMonitor) { + printf("EcsQueryHasMonitor|"); + } + if (value & EcsQueryIsTrivial) { + printf("EcsQueryIsTrivial|"); + } + if (value & EcsQueryHasCacheable) { + printf("EcsQueryHasCacheable|"); + } + if (value & EcsQueryIsCacheable) { + printf("EcsQueryIsCacheable|"); + } + if (value & EcsQueryHasTableThisVar) { + printf("EcsQueryHasTableThisVar|"); + } + if (value & EcsQueryCacheYieldEmptyTables) { + printf("EcsQueryCacheYieldEmptyTables|"); + } + printf("\n"); +} + +#define test_query_flags(expect, value)\ + if ((expect) != (value)) {\ + printf("expected: ");\ + query_flags_to_str(expect);\ + printf("got: ");\ + query_flags_to_str(value);\ + test_uint(expect, value);\ + } + +void Validator_validate_1_term(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = {{TagA}} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].inout, EcsInOutDefault); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position) }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].inout, EcsInOutDefault); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_2_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{TagA}, {TagB}} + }); + + test_int(q->term_count, 2); + test_int(q->field_count, 2); + + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_uint(q->terms[1].id, TagB); + test_int(q->terms[1].oper, EcsAnd); + test_int(q->terms[1].field_index, 1); + test_uint(q->terms[1].first.id, TagB|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_3_terms(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .terms = {{TagA}, {TagB}, {TagC}} + }); + + test_int(q->term_count, 3); + test_int(q->field_count, 3); + + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_uint(q->terms[1].id, TagB); + test_int(q->terms[1].oper, EcsAnd); + test_int(q->terms[1].field_index, 1); + test_uint(q->terms[1].first.id, TagB|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_int(q->terms[2].id, TagC); + test_int(q->terms[2].oper, EcsAnd); + test_int(q->terms[2].field_index, 2); + test_int(q->terms[2].first.id, TagC|EcsSelf|EcsIsEntity); + test_int(q->terms[2].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_3_terms_w_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_query_t *q = ecs_query(world, { + .terms = {{TagA}, {TagB, .oper = EcsOr}, {TagC }} + }); + + test_int(q->term_count, 3); + test_int(q->field_count, 2); + + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_uint(q->terms[1].id, TagB); + test_int(q->terms[1].oper, EcsOr); + test_int(q->terms[1].field_index, 1); + test_uint(q->terms[1].first.id, TagB|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_int(q->terms[2].id, TagC); + test_int(q->terms[2].oper, EcsAnd); + test_int(q->terms[2].field_index, 1); + test_int(q->terms[2].first.id, TagC|EcsSelf|EcsIsEntity); + test_int(q->terms[2].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_4_terms_w_or_at_1(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagD); + + ecs_query_t *q = ecs_query(world, { + .terms = {{TagA}, {TagB, .oper = EcsOr}, {TagC}, {TagD}} + }); + + test_int(q->term_count, 4); + test_int(q->field_count, 3); + + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_uint(q->terms[1].id, TagB); + test_int(q->terms[1].oper, EcsOr); + test_int(q->terms[1].field_index, 1); + test_uint(q->terms[1].first.id, TagB|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_int(q->terms[2].id, TagC); + test_int(q->terms[2].oper, EcsAnd); + test_int(q->terms[2].field_index, 1); + test_int(q->terms[2].first.id, TagC|EcsSelf|EcsIsEntity); + test_int(q->terms[2].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_int(q->terms[3].id, TagD); + test_int(q->terms[3].oper, EcsAnd); + test_int(q->terms[3].field_index, 2); + test_int(q->terms[3].first.id, TagD|EcsSelf|EcsIsEntity); + test_int(q->terms[3].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ EcsWildcard }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, EcsWildcard); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, EcsWildcard|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_any(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ EcsAny }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, EcsAny); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_same_subj_obj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Rel, .src.name = "Foo", .second.name = "Foo" }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, Foo)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, Foo|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, Foo|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_acyclic_same_subj_obj(void) { + ecs_log_set_level(-4); + + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Traversable); + ECS_TAG(world, Foo); + + test_assert(NULL == ecs_query(world, { + .terms = {{ .first.id = Rel, .src.name = "Foo", .second.name = "Foo" }} + })); + + ecs_fini(world); +} + +void Validator_validate_1_term_acyclic_reflexive_same_subj_obj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Traversable, Reflexive); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Rel, .src.name = "Foo", .second.name = "Foo" }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, Foo)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, Foo|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, Foo|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_same_subj_obj_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Rel, + .src = { .name = "X", .id = EcsIsVariable }, + .second = { .name = "X", .id = EcsIsVariable } + }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, EcsWildcard)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_str(q->terms[0].src.name, "X"); + test_uint(q->terms[0].src.id, EcsSelf|EcsIsVariable); + test_str(q->terms[0].second.name, "X"); + test_uint(q->terms[0].second.id, EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_acyclic_same_subj_obj_var(void) { + ecs_log_set_level(-4); + + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Traversable); + + test_assert(NULL == ecs_query(world, { + .terms = {{ + .first.id = Rel, + .src = { .name = "X", .id = EcsIsVariable }, + .second = { .name = "X", .id = EcsIsVariable } + }} + })); + + ecs_fini(world); +} + +void Validator_validate_1_term_acyclic_reflexive_same_subj_obj_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Traversable, Reflexive); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Rel, + .src = { .name = "X", .id = EcsIsVariable }, + .second = { .name = "X", .id = EcsIsVariable } + }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, EcsWildcard)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_str(q->terms[0].src.name, "X"); + test_uint(q->terms[0].src.id, EcsSelf|EcsIsVariable); + test_str(q->terms[0].second.name, "X"); + test_uint(q->terms[0].second.id, EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_non_acyclic_superset(void) { + ecs_log_set_level(-4); + + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Rel); + + test_assert(NULL == ecs_query(world, { + .terms = {{ + .id = Tag, + .src.id = EcsUp, + .trav = Rel + }} + })); /* cyclic superset is invalid */ + + ecs_fini(world); +} + +void Validator_validate_1_term_dont_inherit_default_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, DontInherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Tag }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_dont_inherit_pair_default_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, DontInherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Rel, EcsWildcard) }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, EcsWildcard)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_inherit_default_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Tag }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsUp|EcsIsVariable); + test_uint(q->terms[0].trav, EcsIsA); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_inherit_pair_default_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Inherit)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Rel, EcsWildcard) }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, EcsWildcard)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsUp|EcsIsVariable); + test_uint(q->terms[0].trav, EcsIsA); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_override_default_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Override)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Tag }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_override_pair_default_set(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Override)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Rel, EcsWildcard) }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, EcsWildcard)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_up_no_inherit(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Tag, .src.id = EcsUp }} + }); + + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsUp|EcsIsVariable); + test_uint(q->terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_up_no_inherit_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.id = EcsUp }} + }); + + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, EcsWildcard)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsUp|EcsIsVariable); + test_uint(q->terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_up_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Override)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Tag, .src.id = EcsUp }} + }); + + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsUp|EcsIsVariable); + test_uint(q->terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_up_override_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Override)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.id = EcsUp }} + }); + + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, EcsWildcard)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsUp|EcsIsVariable); + test_uint(q->terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_up_isa_no_inherit(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Tag, .src.id = EcsUp, .trav = EcsIsA }} + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Validator_validate_1_term_up_isa_no_inherit_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.id = EcsUp, .trav = EcsIsA }} + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Validator_validate_1_term_up_isa_override(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, (OnInstantiate, Override)); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Tag, .src.id = EcsUp, .trav = EcsIsA }} + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Validator_validate_1_term_up_isa_override_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, (OnInstantiate, Override)); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.id = EcsUp, .trav = EcsIsA }} + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Validator_validate_1_term_cascade_implicit_trav(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .id = Tag, .src.id = EcsCascade }}, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsUp|EcsCascade|EcsIsVariable); + test_uint(q->terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_cascade_isa(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .id = Tag, + .trav = EcsIsA, .src.id = EcsCascade + }}, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsUp|EcsCascade|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_cascade_childof(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .id = Tag, + .trav = EcsChildOf, .src.id = EcsCascade + }}, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsUp|EcsCascade|EcsIsVariable); + test_uint(q->terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_cascade_down(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ecs_add_pair(world, Tag, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .id = Tag, + .trav = EcsChildOf, .src.id = EcsCascade + }}, + .cache_kind = EcsQueryCacheAuto + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsUp|EcsCascade|EcsIsVariable); + test_uint(q->terms[0].trav, EcsChildOf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_optional_only(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .id = Tag, + .oper = EcsOptional + }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsOptional); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_term_transitive_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Rel, Transitive); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .id = ecs_pair(Rel, Tgt) + }} + }); + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Rel, Tgt)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, Tgt|EcsSelf|EcsIsEntity); + test_assert(q->terms[0].flags_ & EcsTermTransitive); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_variable_as_pred_only(void) { + ecs_log_set_level(-4); + + ecs_world_t *world = ecs_mini(); + + test_assert(NULL == ecs_query(world, { + .terms = {{ .id = EcsVariable }} + })); + + ecs_fini(world); +} + +void Validator_validate_1_variable_as_pred_w_subj(void) { + ecs_log_set_level(-4); + + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Src); + + test_assert(NULL == ecs_query(world, { + .terms = {{ .id = EcsVariable, .src.id = Src }} + })); + + ecs_fini(world); +} + +void Validator_validate_1_variable_as_pred_w_pair(void) { + ecs_log_set_level(-4); + + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Src); + ECS_TAG(world, Tgt); + + test_assert(NULL == ecs_query(world, { + .terms = {{ + .first.id = EcsVariable, + .src.id = Src, + .second.id = Tgt + }} + })); + + ecs_fini(world); +} + +void Validator_validate_1_variable_as_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Foo, + .src.id = EcsVariable|EcsIsVariable + }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Foo); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, Foo|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_src_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Foo, + .src.id = EcsIsVariable, + .src.name = "Var" + }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, Foo); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_str(q->terms[0].src.name, "Var"); + test_uint(q->terms[0].src.id, EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_first_var(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = EcsIsVariable, + .first.name = "Var" + }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, EcsWildcard); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_str(q->terms[0].first.name, "Var"); + test_uint(q->terms[0].first.id, EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_second_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Foo, + .second.id = EcsIsVariable, + .second.name = "Var" + }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Foo, EcsWildcard)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_str(q->terms[0].second.name, "Var"); + test_uint(q->terms[0].second.id, EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_src_var_from_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Foo, + .src.name = "$Var" + }} + }); + + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_assert(q->terms != NULL); + test_int(q->terms[0].id, Foo); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_int(q->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_str(q->terms[0].src.name, "Var"); + test_int(q->terms[0].src.id, EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_first_first_var(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.name = "$Var" + }} + }); + + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_assert(q->terms != NULL); + test_int(q->terms[0].id, EcsWildcard); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_str(q->terms[0].first.name, "Var"); + test_int(q->terms[0].first.id, EcsSelf|EcsIsVariable); + test_int(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_second_second_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Foo, + .second.name = "$Var" + }} + }); + + test_assert(q != NULL); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_assert(q->terms != NULL); + test_int(q->terms[0].id, ecs_pair(Foo, EcsWildcard)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_int(q->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_int(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_str(q->terms[0].second.name, "Var"); + test_int(q->terms[0].second.id, EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_variable_as_obj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ + .first.id = Foo, + .second.id = EcsVariable|EcsIsVariable + }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(Foo, Foo)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, Foo|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_2_terms_or_w_dontinherit(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsDontInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ TagA, .oper = EcsOr }, { TagB }} + }); + + test_int(q->term_count, 2); + test_int(q->field_count, 1); + + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsOr); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsUp|EcsIsVariable); + + test_uint(q->terms[1].id, TagB); + test_int(q->terms[1].oper, EcsAnd); + test_int(q->terms[1].field_index, 0); + test_uint(q->terms[1].first.id, TagB|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[1].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_2_terms_or_w_both_dontinherit(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_add_pair(world, TagA, EcsOnInstantiate, EcsDontInherit); + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsDontInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ TagA, .oper = EcsOr }, { TagB }} + }); + + test_int(q->term_count, 2); + test_int(q->field_count, 1); + + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsOr); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].trav, 0); + + test_uint(q->terms[1].id, TagB); + test_int(q->terms[1].oper, EcsAnd); + test_int(q->terms[1].field_index, 0); + test_uint(q->terms[1].first.id, TagB|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[1].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pair_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_id_t pair = ecs_pair(Rel, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = {{pair}} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, pair); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, Tgt|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pred_obj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_id_t pair = ecs_pair(Rel, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = {{.first.id = Rel, .second.id = Tgt}} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, pair); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, Tgt|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pair_id_and_subj(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + ECS_TAG(world, Src); + + ecs_id_t pair = ecs_pair(Rel, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = {{.id = pair, .src.id = Src}} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, pair); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, Src|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, Tgt|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_w_pred_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.name = "Tag" + }); + + test_int(q->terms[0].id, Tag); + test_assert(q->terms[0].first.name == NULL); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_w_final_pred_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, Tag, Final); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.name = "Tag" + }); + + test_int(q->terms[0].id, Tag); + test_assert(q->terms[0].first.name == NULL); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_w_subj_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Src); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = Tag, + .terms[0].src.name = "Src" + }); + + test_int(q->terms[0].id, Tag); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_assert(q->terms[0].src.name == NULL); + test_uint(q->terms[0].src.id, Src|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_1_w_obj_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + ECS_TAG(world, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = Tag, + .terms[0].second.name = "Tgt" + }); + + test_int(q->terms[0].id, ecs_pair(Tag, Tgt)); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_assert(q->terms[0].second.name == NULL); + test_int(q->terms[0].second.id, Tgt|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_this_implicit_variable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = Tag, + .terms[0].src.id = EcsThis + }); + + test_int(q->terms[0].id, Tag); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_this_explicit_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = Tag, + .terms[0].src.id = EcsThis|EcsIsEntity, + }); + + test_int(q->terms[0].id, Tag); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_first_this_implicit_variable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Src); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = EcsThis, + .terms[0].src.id = Src + }); + + test_int(q->terms[0].id, EcsWildcard); + test_uint(q->terms[0].first.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, Src|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_first_this_explicit_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Src); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = EcsThis|EcsIsEntity, + .terms[0].src.id = Src + }); + + test_int(q->terms[0].id, EcsThis); + test_uint(q->terms[0].first.id, EcsThis|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, Src|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_second_this_implicit_variable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = Tag, + .terms[0].second.id = EcsThis + }); + + test_int(q->terms[0].id, ecs_pair(Tag, EcsWildcard)); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_int(q->terms[0].second.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_second_this_explicit_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = Tag, + .terms[0].second.id = EcsThis|EcsIsEntity, + }); + + test_int(q->terms[0].id, ecs_pair(Tag, EcsThis)); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_int(q->terms[0].second.id, EcsThis|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_this_variable_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = Tag, + .terms[0].src.name = "this", + .terms[0].src.id = EcsIsVariable + }); + + test_int(q->terms[0].id, Tag); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_assert(q->terms[0].src.name == NULL); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_0_source(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = Tag, + .terms[0].src.id = EcsIsEntity + }); + + test_int(q->terms[0].id, Tag); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsIsEntity); + test_uint(q->terms[0].trav, 0); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_0_target(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms[0].first.id = EcsChildOf, + .terms[0].second.id = EcsIsEntity + }); + + test_uint(q->terms[0].id, ecs_pair(EcsChildOf, 0)); + test_uint(q->terms[0].first.id, EcsChildOf|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].trav, 0); + test_uint(q->terms[0].second.id, EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_2_terms_w_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{TagA, .oper = EcsOr}, {TagB }} + }); + + test_int(q->terms[0].oper, EcsOr); + test_uint(q->terms[0].id, TagA); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_int(q->terms[1].oper, EcsAnd); + test_uint(q->terms[1].id, TagB); + test_uint(q->terms[1].first.id, TagB|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_2_terms_w_or_mixed_src_flags(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_add_pair(world, TagB, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + {TagA, .oper = EcsOr}, + {TagB, .oper = EcsAnd, .src.id = EcsUp, .trav = EcsIsA }, + } + }); + + ecs_entity_t base = ecs_new_w(world, TagB); + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w_pair(world, EcsIsA, base); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e1, it.entities[0]); + test_uint(TagA, it.ids[0]); + test_uint(0, it.sources[0]); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e2, it.entities[0]); + test_uint(TagB, it.ids[0]); + test_uint(base, it.sources[0]); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_2_terms_w_or_mixed_src_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ECS_TAG(world, SrcA); + ECS_TAG(world, SrcB); + + ecs_log_set_level(-4); + + test_assert(NULL == ecs_query(world, { + .terms = { + {TagA, .oper = EcsOr, .src.id = SrcA}, + {TagB, .oper = EcsOr, .src.id = SrcB} + } + })); + + ecs_fini(world); +} + +void Validator_validate_2_terms_w_or_mixed_src_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ECS_TAG(world, SrcA); + ECS_TAG(world, SrcB); + + ecs_log_set_level(-4); + + test_assert(NULL == ecs_query(world, { + .terms = { + {TagA, .oper = EcsOr, .src.name = "SrcA"}, + {TagB, .oper = EcsOr, .src.name = "SrcB"} + } + })); + + ecs_fini(world); +} + +void Validator_validate_2_terms_w_or_same_src_w_id_and_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ECS_TAG(world, SrcA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + {TagA, .oper = EcsOr, .src.name = "SrcA"}, + {TagB, .src.id = SrcA} + } + }); + + test_int(q->terms[0].oper, EcsOr); + test_uint(q->terms[0].id, TagA); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, SrcA|EcsSelf|EcsIsEntity); + + test_int(q->terms[1].oper, EcsAnd); + test_uint(q->terms[1].id, TagB); + test_uint(q->terms[1].first.id, TagB|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, SrcA|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_and_flag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA, .oper = EcsAndFrom } + } + }); + + test_int(q->term_count, 1); + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsAndFrom); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_or_flag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA, .oper = EcsOrFrom } + } + }); + + test_int(q->term_count, 1); + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsOrFrom); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_not_flag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA, .oper = EcsNotFrom } + } + }); + + test_int(q->term_count, 1); + test_assert(q->terms != NULL); + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsNotFrom); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_filter(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + {TagA, .inout = EcsInOutNone} + } + }); + + test_int(q->term_count, 1); + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsAnd); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_double_init(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q_1 = ecs_query(world, { + .terms = {{ Foo }} + }); + test_assert(q_1 != NULL); + + test_int(q_1->term_count, 1); + test_int(q_1->terms[0].id, Foo); + test_int(q_1->terms[0].oper, EcsAnd); + test_int(q_1->terms[0].field_index, 0); + test_int(q_1->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_int(q_1->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_desc_t desc = {0}; + ecs_os_memcpy_n(desc.terms, q_1->terms, ecs_term_t, FLECS_TERM_COUNT_MAX); + ecs_query_t *q_2 = ecs_query_init(world, &desc); + test_assert(q_2 != NULL); + + test_int(q_2->term_count, 1); + test_int(q_2->terms[0].id, Foo); + test_int(q_2->terms[0].oper, EcsAnd); + test_int(q_2->terms[0].field_index, 0); + test_int(q_2->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_int(q_2->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q_1); + ecs_query_fini(q_2); + + ecs_fini(world); +} + +void Validator_validate_double_init_w_expr(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q_1 = ecs_query(world, { + .expr = "Foo" + }); + test_assert(q_1 != NULL); + + test_int(q_1->term_count, 1); + test_int(q_1->terms[0].id, Foo); + test_int(q_1->terms[0].oper, EcsAnd); + test_int(q_1->terms[0].field_index, 0); + test_int(q_1->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_int(q_1->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_desc_t desc = {0}; + ecs_os_memcpy_n(desc.terms, q_1->terms, ecs_term_t, FLECS_TERM_COUNT_MAX); + ecs_query_t *q_2 = ecs_query_init(world, &desc); + test_assert(q_2 != NULL); + + test_int(q_2->term_count, 1); + test_int(q_2->terms[0].id, Foo); + test_int(q_2->terms[0].oper, EcsAnd); + test_int(q_2->terms[0].field_index, 0); + test_int(q_2->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_int(q_2->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q_1); + ecs_query_fini(q_2); + + ecs_fini(world); +} + +void Validator_validate_double_init_w_expr_optional(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q_1 = ecs_query(world, { + .expr = "?Foo" + }); + test_assert(q_1 != NULL); + + test_int(q_1->term_count, 1); + test_int(q_1->terms[0].id, Foo); + test_int(q_1->terms[0].oper, EcsOptional); + test_int(q_1->terms[0].field_index, 0); + test_int(q_1->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_int(q_1->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_desc_t desc = {0}; + ecs_os_memcpy_n(desc.terms, q_1->terms, ecs_term_t, FLECS_TERM_COUNT_MAX); + ecs_query_t *q_2 = ecs_query_init(world, &desc); + test_assert(q_2 != NULL); + + test_int(q_2->term_count, 1); + test_int(q_2->terms[0].id, Foo); + test_int(q_2->terms[0].oper, EcsOptional); + test_int(q_2->terms[0].field_index, 0); + test_int(q_2->terms[0].first.id, Foo|EcsSelf|EcsIsEntity); + test_int(q_2->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q_1); + ecs_query_fini(q_2); + + ecs_fini(world); +} + +void Validator_validate_w_tag_term_is_no_data(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ Foo }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(it.flags & EcsIterNoData); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_inout_none_term_is_no_data(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position), .inout = EcsInOutNone }} + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(it.flags & EcsIterNoData); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_tag_and_inout_none_term_is_no_data(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position), .inout = EcsInOutNone }, + { Foo } + } + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(it.flags & EcsIterNoData); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_not_term_is_no_data(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position), .oper = EcsNot } + } + }); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(it.flags & EcsIterNoData); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_no_transitive_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, LocatedIn); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = LocatedIn, .second.id = EcsWildcard } + } + }); + + test_assert(!(q->terms[0].flags_ & EcsTermTransitive)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_transitive_pair_any_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, LocatedIn, Transitive); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { + .first.id = LocatedIn, + .second.id = EcsWildcard, + .src.id = EcsAny + } + } + }); + + test_assert(!(q->terms[0].flags_ & EcsTermTransitive)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_transitive_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, LocatedIn, Transitive); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = LocatedIn, .second.id = EcsWildcard } + } + }); + + test_assert(q->terms[0].flags_ & EcsTermTransitive); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_transitive_tag_no_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, LocatedIn, Transitive); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = LocatedIn } + } + }); + + test_assert(!(q->terms[0].flags_ & EcsTermTransitive)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_transitive_tag_self_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, LocatedIn, Transitive); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = LocatedIn, .second.id = EcsWildcard|EcsSelf } + } + }); + + test_assert(!(q->terms[0].flags_ & EcsTermTransitive)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_transitive_tag_any_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_ENTITY(world, LocatedIn, Transitive); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = LocatedIn, .second.id = EcsAny } + } + }); + + test_assert(!(q->terms[0].flags_ & EcsTermTransitive)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pair_same_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { + .first.id = Rel, + .src.name = "a", + .src.id = EcsIsVariable, + .second.name = "a", + .second.id = EcsIsVariable, + } + } + }); + + test_assert(q->terms[0].src.id & EcsIsVariable); + test_assert(q->terms[0].second.id & EcsIsVariable); + test_str(q->terms[0].src.name, "a"); + test_str(q->terms[0].second.name, "a"); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pair_not_same_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { + .first.id = Rel, + .src.name = "a", + .src.id = EcsIsVariable, + .second.name = "b", + .second.id = EcsIsVariable, + } + } + }); + + test_assert(q->terms[0].src.id & EcsIsVariable); + test_assert(q->terms[0].second.id & EcsIsVariable); + test_str(q->terms[0].src.name, "a"); + test_str(q->terms[0].second.name, "b"); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pair_no_vars_not_same_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, A); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { + .first.id = Rel, + .src.id = A, + .second.id = A + } + } + }); + + test_uint(q->terms[0].src.id, A|EcsIsEntity|EcsSelf); + test_uint(q->terms[0].second.id, A|EcsIsEntity|EcsSelf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pair_wildcard_not_same_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { + .first.id = Rel, + .src.id = EcsWildcard, + .second.id = EcsWildcard + } + } + }); + + test_uint(q->terms[0].src.id, EcsWildcard|EcsIsVariable|EcsSelf); + test_uint(q->terms[0].second.id, EcsWildcard|EcsIsVariable|EcsSelf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pair_any_not_same_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { + .first.id = Rel, + .src.id = EcsAny, + .second.id = EcsAny + } + } + }); + + test_uint(q->terms[0].src.id, EcsAny|EcsIsVariable|EcsSelf); + test_uint(q->terms[0].second.id, EcsAny|EcsIsVariable|EcsSelf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_no_pair_not_same_vars(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { + .first.id = Rel, + .src.name = "a", + .src.id = EcsIsVariable + } + } + }); + + test_assert(q->terms[0].src.id & EcsIsVariable); + test_assert(!(q->terms[0].second.id & EcsIsVariable)); + test_str(q->terms[0].src.name, "a"); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_not_childof_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { + .first.id = EcsChildOf, + .second.id = EcsAny, + .oper = EcsNot + } + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(EcsChildOf, 0)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].inout, EcsInOutDefault); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, EcsChildOf|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_inherited_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Unit); + ECS_ENTITY(world, MeleeUnit, (IsA, Unit)); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = Unit } + } + }); + + test_assert(q->terms[0].flags_ & EcsTermIdInherited); + test_uint(q->terms[0].first.id, Unit|EcsIsEntity|EcsSelf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_inherited_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Unit); + ECS_ENTITY(world, MeleeUnit, (IsA, Unit)); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = Unit, .second.id = EcsWildcard } + } + }); + + test_assert(q->terms[0].flags_ & EcsTermIdInherited); + test_uint(q->terms[0].first.id, Unit|EcsIsEntity|EcsSelf); + test_uint(q->terms[0].second.id, EcsWildcard|EcsIsVariable|EcsSelf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_non_inherited_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Unit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = Unit } + } + }); + + test_assert(!(q->terms[0].flags_ & EcsTermIdInherited)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_non_inherited_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Unit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = Unit, .second.id = EcsWildcard } + } + }); + + test_assert(!(q->terms[0].flags_ & EcsTermIdInherited)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_first_wildcard_inout_none(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = EcsWildcard } + } + }); + + test_int(q->terms[0].inout, EcsInOutNone); + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_first_var_inout_none(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.name = "var", .first.id = EcsIsVariable } + } + }); + + test_int(q->terms[0].inout, EcsInOutNone); + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pair_wildcard_inout_none(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = EcsWildcard, .second.id = EcsWildcard } + } + }); + + test_int(q->terms[0].inout, EcsInOutNone); + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_pair_var_inout_none(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { + .first.name = "x", .first.id = EcsIsVariable, + .second.name = "y", .second.id = EcsIsVariable, + } + } + }); + + test_int(q->terms[0].inout, EcsInOutNone); + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_w_unresolved_by_name(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + test_assert(NULL == ecs_query(world, { + .terms = { { .first.name = "Foo" } }, + .flags = EcsQueryAllowUnresolvedByName + })); + + ecs_fini(world); +} + +void Validator_validate_w_unresolved_by_name_eq(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this == Foo", + .flags = EcsQueryAllowUnresolvedByName + }); + + test_assert(q->terms[0].second.id & EcsIsName); + test_str(q->terms[0].second.name, "Foo"); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_childof_this(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = { { .first.id = EcsChildOf, .second.id = EcsThis } } + }); + + test_uint(q->terms[0].id, ecs_pair(EcsChildOf, EcsWildcard)); + test_uint(q->terms[0].first.id, EcsChildOf|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_childof_this_entity(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .terms = { { .first.id = EcsChildOf, .second.id = EcsThis|EcsIsEntity } } + }); + + test_uint(q->terms[0].id, ecs_pair(EcsChildOf, EcsThis)); + test_uint(q->terms[0].first.id, EcsChildOf|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, EcsThis|EcsSelf|EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_childof_this_by_id(void) { + ecs_world_t *world = ecs_mini(); + + + ecs_log_set_level(-4); + test_assert(NULL == ecs_query(world, { + .terms = { { .id = ecs_pair(EcsChildOf, EcsThis) } } + })); + + ecs_fini(world); +} + +void Validator_validate_filter_flag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { .inout = EcsInOutFilter, .id = ecs_id(Position) } + } + }); + + test_assert(q != NULL); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_first_0_name(void) { + ecs_world_t *world = ecs_mini(); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.name = "#0" }} + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Validator_validate_src_0_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Tag, .src.name = "#0" }} + }); + + test_assert(q != NULL); + test_int(q->term_count, 1); + test_uint(q->terms[0].id, Tag); + test_uint(q->terms[0].first.id, Tag|EcsIsEntity|EcsSelf); + test_uint(q->terms[0].src.id, EcsIsEntity); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_second_0_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Tag, .second.name = "#0" }} + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Validator_validate_singleton_src_w_first_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Tag, .src.id = EcsVariable }} + }); + + test_assert(q != NULL); + test_int(q->term_count, 1); + test_uint(q->terms[0].id, Tag); + test_uint(q->terms[0].first.id, Tag|EcsIsEntity|EcsSelf); + test_uint(q->terms[0].src.id, Tag|EcsIsEntity|EcsSelf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_singleton_second_w_first_name(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = Tag, .second.id = EcsVariable }} + }); + + test_assert(q != NULL); + test_int(q->term_count, 1); + test_uint(q->terms[0].id, ecs_pair(Tag, Tag)); + test_uint(q->terms[0].first.id, Tag|EcsIsEntity|EcsSelf); + test_uint(q->terms[0].src.id, EcsThis|EcsIsVariable|EcsSelf); + test_uint(q->terms[0].second.id, Tag|EcsIsEntity|EcsSelf); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_not_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = EcsWildcard, .oper = EcsNot }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, EcsAny); + test_int(q->terms[0].oper, EcsNot); + test_int(q->terms[0].inout, EcsInOutNone); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_not_first_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = EcsWildcard, .second.id = ecs_id(Position), .oper = EcsNot }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(EcsAny, ecs_id(Position))); + test_int(q->terms[0].oper, EcsNot); + test_int(q->terms[0].inout, EcsInOutNone); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_not_second_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.id = ecs_id(Position), .second.id = EcsWildcard, .oper = EcsNot }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(ecs_id(Position), EcsAny)); + test_int(q->terms[0].oper, EcsNot); + test_int(q->terms[0].inout, EcsInOutNone); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_not_wildcard_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ EcsWildcard, .oper = EcsNot }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, EcsAny); + test_int(q->terms[0].oper, EcsNot); + test_int(q->terms[0].inout, EcsInOutNone); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_not_wildcard_first_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair(EcsWildcard, ecs_id(Position)), .oper = EcsNot }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair(EcsAny, ecs_id(Position))); + test_int(q->terms[0].oper, EcsNot); + test_int(q->terms[0].inout, EcsInOutNone); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].second.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_not_wildcard_second_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_pair_t(Position, EcsWildcard), .oper = EcsNot }} + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_uint(q->terms[0].id, ecs_pair_t(Position, EcsAny)); + test_int(q->terms[0].oper, EcsNot); + test_int(q->terms[0].inout, EcsInOutNone); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, EcsAny|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_or_same_type(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair_t(Position, TgtA), .oper = EcsOr }, + { ecs_pair_t(Position, TgtB) } + } + }); + + test_int(q->term_count, 2); + test_int(q->field_count, 1); + + test_uint(q->terms[0].id, ecs_pair_t(Position, TgtA)); + test_int(q->terms[0].oper, EcsOr); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_uint(q->terms[1].id, ecs_pair_t(Position, TgtB)); + test_int(q->terms[1].oper, EcsAnd); + test_int(q->terms[1].field_index, 0); + test_uint(q->terms[1].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[1].trav, 0); + + test_assert(q->ids[0] != 0); + test_int(q->sizes[0], ECS_SIZEOF(Position)); + test_assert(q->data_fields & 1); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_or_different_types(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position), .oper = EcsOr }, { ecs_id(Velocity) }} + }); + + test_int(q->term_count, 2); + test_int(q->field_count, 1); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsOr); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_uint(q->terms[1].id, ecs_id(Velocity)); + test_int(q->terms[1].oper, EcsAnd); + test_int(q->terms[1].field_index, 0); + test_uint(q->terms[1].first.id, ecs_id(Velocity)|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[1].trav, 0); + + test_assert(q->ids[0] == 0); + test_int(q->sizes[0], 0); + test_assert(!(q->data_fields & 1)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_or_different_types_1_and_2_or(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_TAG(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { TagA }, + { ecs_id(Position), .oper = EcsOr }, + { ecs_id(Velocity) } + } + }); + + test_int(q->term_count, 3); + test_int(q->field_count, 2); + + test_uint(q->terms[0].id, TagA); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, TagA|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_uint(q->terms[1].id, ecs_id(Position)); + test_int(q->terms[1].oper, EcsOr); + test_int(q->terms[1].field_index, 1); + test_uint(q->terms[1].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + + test_uint(q->terms[2].id, ecs_id(Velocity)); + test_int(q->terms[2].oper, EcsAnd); + test_int(q->terms[2].field_index, 1); + test_uint(q->terms[2].first.id, ecs_id(Velocity)|EcsSelf|EcsIsEntity); + test_uint(q->terms[2].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[2].trav, 0); + + test_assert(q->ids[1] == 0); + test_int(q->sizes[1], 0); + test_assert(!(q->data_fields & (1u << 1))); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_trav_isa_w_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = { + { EcsWildcard, .trav = EcsIsA } + } + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Validator_validate_trav_isa_w_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = { + { EcsAny, .trav = EcsIsA } + } + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Validator_validate_custom_trav_w_inherit_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_ENTITY(world, Rel, Traversable); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position), .trav = Rel }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsUp|EcsIsVariable); + test_uint(q->terms[0].trav, Rel); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_custom_trav_w_inherit_id_w_self_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_ENTITY(world, Rel, Traversable); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position), .trav = Rel, .src.id = EcsSelf|EcsUp }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsUp|EcsIsVariable); + test_uint(q->terms[0].trav, Rel); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_custom_trav_w_inherit_id_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_ENTITY(world, Rel, Traversable); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position), .trav = Rel, .src.id = EcsUp }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsUp|EcsIsVariable); + test_uint(q->terms[0].trav, Rel); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_1_term_is_cacheable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermIsCacheable|EcsTermIsTrivial|EcsTermKeepAlive); + + test_assert((q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryIsTrivial|EcsQueryIsCacheable| + EcsQueryHasCacheable|EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar|EcsQueryHasOutTerms, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_1_term_tag_is_cacheable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_new_w(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { Tag }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, Tag); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Tag|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermIsCacheable|EcsTermIsTrivial|EcsTermKeepAlive); + + test_assert(!(q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryIsTrivial|EcsQueryIsCacheable| + EcsQueryHasCacheable|EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_1_term_pair_is_cacheable(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t rel = ecs_new(world); + ecs_entity_t tgt = ecs_new(world); + + ecs_new_w_pair(world, rel, tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(rel, tgt) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_pair(rel, tgt)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, tgt|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermIsCacheable|EcsTermIsTrivial|EcsTermKeepAlive); + + test_assert(!(q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryIsTrivial|EcsQueryIsCacheable| + EcsQueryHasCacheable|EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_1_term_pair_recycled_is_cacheable(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t rel = ecs_new(world); + ecs_delete(world, rel); + rel = ecs_new(world); + test_assert((uint32_t)rel != rel); + + ecs_entity_t tgt = ecs_new(world); + ecs_delete(world, tgt); + tgt = ecs_new(world); + test_assert((uint32_t)tgt != tgt); + + ecs_new_w_pair(world, rel, tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(rel, tgt) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_pair(rel, tgt)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].second.id, tgt|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermIsCacheable|EcsTermIsTrivial|EcsTermKeepAlive); + + test_assert(!(q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryIsTrivial|EcsQueryIsCacheable| + EcsQueryHasCacheable|EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_2_term_is_cacheable(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Velocity) }, + } + }); + + test_int(q->term_count, 2); + test_int(q->field_count, 2); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermIsCacheable|EcsTermIsTrivial|EcsTermKeepAlive); + + test_uint(q->terms[1].id, ecs_id(Velocity)); + test_int(q->terms[1].oper, EcsAnd); + test_int(q->terms[1].field_index, 1); + test_uint(q->terms[1].first.id, ecs_id(Velocity)|EcsSelf|EcsIsEntity); + test_uint(q->terms[1].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[1].flags_, EcsTermIsCacheable|EcsTermIsTrivial|EcsTermKeepAlive); + + test_assert((q->data_fields & (1 << 0))); + test_assert((q->data_fields & (1 << 1))); + + test_query_flags(EcsQueryIsTrivial|EcsQueryIsCacheable| + EcsQueryHasCacheable|EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar|EcsQueryHasOutTerms, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_can_inherit(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsUp|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermIsCacheable|EcsTermIsTrivial|EcsTermKeepAlive); + + test_assert((q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryIsTrivial|EcsQueryIsCacheable| + EcsQueryHasCacheable|EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryHasTableThisVar|EcsQueryHasOutTerms, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_can_toggle(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsCanToggle); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermKeepAlive|EcsTermIsToggle|EcsTermIsCacheable); + + test_assert((q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryMatchOnlyThis|EcsQueryMatchThis|EcsQueryHasCacheable| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar|EcsQueryHasOutTerms, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_sparse(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsSparse); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermKeepAlive|EcsTermIsSparse); + + test_assert((q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar|EcsQueryHasOutTerms, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_union(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + + ecs_add_id(world, Rel, EcsUnion); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { Rel }, + }, + .cache_kind = EcsQueryCacheAuto + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, Rel); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermKeepAlive|EcsTermIsCacheable|EcsTermIsTrivial); + + test_assert(!(q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryIsTrivial|EcsQueryIsCacheable| + EcsQueryHasCacheable|EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_union_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + ecs_add_id(world, Rel, EcsUnion); + + ecs_new_w_pair(world, Rel, Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair(Rel, Tgt) }, + }, + .cache_kind = EcsQueryCacheAuto + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_pair(Rel, Tgt)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Rel|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermIsUnion); + + test_assert(!(q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_transitive(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsTransitive); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermIsCacheable|EcsTermIsTrivial|EcsTermKeepAlive); + + test_assert((q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryIsTrivial|EcsQueryIsCacheable| + EcsQueryHasCacheable|EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar|EcsQueryHasOutTerms, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_transitive_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tgt); + + ecs_add_id(world, ecs_id(Position), EcsTransitive); + + ecs_new_w_pair(world, ecs_id(Position), Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair_t(Position, Tgt) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_pair_t(Position, Tgt)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermKeepAlive|EcsTermTransitive); + + test_assert((q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar|EcsQueryHasOutTerms, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_reflexive(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_add_id(world, ecs_id(Position), EcsReflexive); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_id(Position)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermIsCacheable|EcsTermIsTrivial|EcsTermKeepAlive); + + test_assert((q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryIsTrivial|EcsQueryIsCacheable| + EcsQueryHasCacheable|EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar|EcsQueryHasOutTerms, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_reflexive_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Tgt); + + ecs_add_id(world, ecs_id(Position), EcsReflexive); + + ecs_new_w_pair(world, ecs_id(Position), Tgt); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_pair_t(Position, Tgt) }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, ecs_pair_t(Position, Tgt)); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, ecs_id(Position)|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermKeepAlive|EcsTermReflexive); + + test_assert((q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar|EcsQueryHasOutTerms, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Validator_validate_simple_w_inherited_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Unit); + ECS_ENTITY(world, MeleeUnit, (IsA, Unit)); + + ecs_new_w(world, Unit); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { Unit }, + } + }); + + test_int(q->term_count, 1); + test_int(q->field_count, 1); + test_int(q->var_count, 1); + test_assert(q->vars != NULL); + test_assert(q->vars[0] == NULL); + + test_uint(q->terms[0].id, Unit); + test_int(q->terms[0].oper, EcsAnd); + test_int(q->terms[0].field_index, 0); + test_uint(q->terms[0].first.id, Unit|EcsSelf|EcsIsEntity); + test_uint(q->terms[0].src.id, EcsThis|EcsSelf|EcsIsVariable); + test_uint(q->terms[0].flags_, EcsTermKeepAlive|EcsTermIdInherited); + + test_assert(!(q->data_fields & (1 << 0))); + + test_query_flags(EcsQueryMatchOnlyThis|EcsQueryMatchThis| + EcsQueryMatchOnlySelf|EcsQueryHasTableThisVar, + q->flags); + + ecs_query_fini(q); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/query/src/Variables.c b/vendors/flecs/test/query/src/Variables.c new file mode 100644 index 000000000..4fbe05f0b --- /dev/null +++ b/vendors/flecs/test/query/src/Variables.c @@ -0,0 +1,11563 @@ +#include + +static ecs_query_cache_kind_t cache_kind = EcsQueryCacheDefault; + +void Variables_setup(void) { + const char *cache_param = test_param("cache_kind"); + if (cache_param) { + if (!strcmp(cache_param, "default")) { + // already set to default + } else if (!strcmp(cache_param, "auto")) { + cache_kind = EcsQueryCacheAuto; + } else { + printf("unexpected value for cache_param '%s'\n", cache_param); + } + } +} + +void Variables_1_ent_src_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x(ent)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_w_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_w_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA(ent, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_w_pair_rel_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x(ent, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); + test_uint(EcsName, ecs_iter_get_var(&it, y_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); + test_uint(EcsName, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); + test_uint(EcsName, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(ecs_id(EcsIdentifier), EcsName), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(ecs_id(EcsIdentifier), ecs_iter_get_var(&it, x_var)); + test_uint(EcsName, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtB, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_w_pair_rel_tgt_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x(ent, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, TgtA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, TgtB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_w_pair_rel_tgt_same_var_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x(ent), $x(ent, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, TgtA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TgtA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(ent, ecs_field_src(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, TgtB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TgtA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(ent, ecs_field_src(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TgtA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(ent, ecs_field_src(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TgtB, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 1)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(ent, ecs_field_src(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this), $x($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(3, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e2, RelB); + ecs_add(world, e3, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtB); + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtB); + ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(3, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelB, TgtA); + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new_w_pair(world, RelB, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelB, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(3, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, TgtB); + ecs_add_pair(world, e3, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_pair_rel_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, Tag); + + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + ecs_query_t *q = ecs_query(world, { + .expr = "Tag($this), $x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int32_t y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(3, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, TgtB); + ecs_add_pair(world, e3, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtB, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtB, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(TgtB, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(Tag, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(TgtA, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_pair_rel_tgt_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new_w_pair(world, RelB, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelB, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, TgtA, TgtA); + ecs_add_pair(world, e2, TgtA, TgtA); + ecs_add_pair(world, e3, TgtA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(3, it.count); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, TgtB, TgtB); + ecs_add_pair(world, e3, TgtB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_pair_rel_tgt_same_var_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t e1 = ecs_new_w_pair(world, RelB, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelB, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this), $x($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, TgtA); + ecs_add_pair(world, e2, RelA, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, TgtA, TgtA); + ecs_add_pair(world, e2, TgtA, TgtA); + ecs_add_pair(world, e3, TgtA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, TgtA); + ecs_add(world, e2, TgtA); + ecs_add(world, e3, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(3, it.count); + test_uint(TgtA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + test_uint(e2, it.entities[1]); + test_uint(e3, it.entities[2]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, TgtB, TgtB); + ecs_add_pair(world, e3, TgtB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TgtA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(TgtA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e2, TgtB); + ecs_add(world, e3, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TgtA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(TgtA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(TgtB, ecs_field_id(&it, 0)); + test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_id_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_id(world, e3, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($x), TagA($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(e1, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(e1, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(e2, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_first_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, e1, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($x, TgtA), TagA($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e2, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_second_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, RelA, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x, $x), TagA($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_first_and_second_same_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, e1, e1); + ecs_entity_t e4 = ecs_new_w(world, TagA); + ecs_add_pair(world, e4, e4, e1); + ecs_entity_t e5 = ecs_new_w(world, TagA); + ecs_add_pair(world, e5, e1, e5); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($x, $x), TagA($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e2, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_id_same_var_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_id(world, e3, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), $x($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_first_same_var_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TagA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, e1, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), $x($x, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e2, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_second_same_var_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TagA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, RelA, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), RelA($x, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_first_and_second_same_var_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TagA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, e1, e1); + ecs_entity_t e4 = ecs_new_w(world, TagA); + ecs_add_pair(world, e4, e4, e1); + ecs_entity_t e5 = ecs_new_w(world, TagA); + ecs_add_pair(world, e5, e1, e5); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), $x($x, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e2, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_first_same_var_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_id(world, e3, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this($this), TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_second_same_var_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, e1, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this($this, TgtA), TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e2, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); + +} + +void Variables_1_src_pair_first_and_second_same_var_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, RelA, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $this), TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_id_same_var_this_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_id(world, e3, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($this), $this($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_first_same_var_this_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TagA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, e1, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($this), $this($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e2, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_second_same_var_this_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TagA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, RelA, e1); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($this), RelA($this, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_pair_first_and_second_same_var_this_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TagA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_add_pair(world, e3, e1, e1); + ecs_entity_t e4 = ecs_new_w(world, TagA); + ecs_add_pair(world, e4, e4, e1); + ecs_entity_t e5 = ecs_new_w(world, TagA); + ecs_add_pair(world, e5, e1, e5); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($this), $this($this, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, RelA, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e2, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_w_this_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this(ent)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, RelA); + ecs_add(world, ent, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, this_var)); + test_uint(RelA, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, this_var)); + test_uint(RelB, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_w_pair_this_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TgtA); + ecs_add(world, ent, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelB, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, this_var)); + test_uint(RelA, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, this_var)); + test_uint(RelB, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_w_pair_this_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA(ent, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TgtA); + ecs_add(world, ent, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, this_var)); + test_uint(TgtA, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, this_var)); + test_uint(TgtB, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_w_pair_this_rel_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this(ent, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TgtA); + ecs_add(world, ent, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelA, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, TgtA, TgtA); + ecs_add_pair(world, ent, TgtB, TgtB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(TgtA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, this_var)); + test_uint(TgtA, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(TgtB, TgtB), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, this_var)); + test_uint(TgtB, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t ent = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this($this), TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, ent, ent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ent, ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ent, ecs_iter_get_var(&it, this_var)); + test_uint(ent, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_pair_this_rel_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t ent = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this($this, $this), TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, ent, ent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, ent, ent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(ent, ent), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ent, ecs_iter_get_var(&it, this_var)); + test_uint(ent, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_this_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t ent = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($this), $this($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, ent, ent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ent, ecs_iter_get_var(&it, this_var)); + test_uint(ent, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_this_src_w_pair_this_rel_tgt_after_write(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t ent = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($this), $this($this, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, ent, TagB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, ent, ent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, ent, ent, ent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(ent, ent), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ent, ecs_iter_get_var(&it, this_var)); + test_uint(ent, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_src_from_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), RelB($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e1, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, e2, RelB); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_rel_from_src_w_ent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x(e2)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_rel_from_src_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x($y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_rel_from_src_w_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_pair_rel_from_src_w_ent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x(e2, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_pair_rel_from_src_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x($y, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_pair_rel_from_src_w_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, e1, TgtA); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_pair_tgt_from_src_w_ent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), RelA(e2, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_pair_tgt_from_src_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), RelA($y, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_pair_tgt_from_src_w_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), RelA($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_pair_rel_tgt_from_src_w_ent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x(e2, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_pair_rel_tgt_from_src_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x($y, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, RelA, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_constrain_pair_rel_tgt_from_src_w_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x), $x($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e3, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e3, e1, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(2, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, it.entities[0]); + test_uint(e3, it.entities[1]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_set_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + ecs_add(world, ent, RelA); + ecs_add(world, ent, RelB); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x(ent)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, RelA); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_set_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelB, TgtA); + ecs_add_pair(world, ent, RelC, TgtB); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x(ent, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, RelA); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, RelB); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, RelC); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_ent_src_set_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t ent = ecs_entity(world, { .name = "ent" }); + ecs_add_pair(world, ent, RelA, TgtA); + ecs_add_pair(world, ent, RelA, TgtB); + ecs_add_pair(world, ent, RelB, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA(ent, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, TgtA); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, TgtB); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(ent, ecs_field_src(&it, 0)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, TgtC); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelB); + ecs_entity_t e3 = ecs_new_w(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_var_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelC, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_var_w_pair_set_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, e1, TgtA); + ecs_add_pair(world, e2, e2, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($x, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e2); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_var_w_pair_set_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, RelA, e1); + ecs_add_pair(world, e2, RelA, e2); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($x, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e2); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_var_w_pair_set_rel_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, e1, e1); + ecs_add_pair(world, e2, e2, e2); + ecs_add_pair(world, e3, RelA, e3); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($x, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e2); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, x_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_entity_t e2 = ecs_new_w(world, RelB); + ecs_entity_t e3 = ecs_new_w(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var == 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e2 = ecs_new_w_pair(world, RelB, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelC, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_w_pair_set_rel(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, e1, TgtA); + ecs_add_pair(world, e2, e2, TgtA); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this($this, TgtA)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e2); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e2, TgtA), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_w_pair_set_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, RelA, e1); + ecs_add_pair(world, e2, RelA, e2); + ecs_add_pair(world, e3, RelA, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, e1), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e2); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, e2), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_w_pair_set_rel_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_add_pair(world, e1, e1, e1); + ecs_add_pair(world, e2, e2, e2); + ecs_add_pair(world, e3, RelA, e3); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this($this, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e1); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, e1), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e2); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e2, e2), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, this_var, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_to_empty_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_add(world, e1, TagB); + ecs_table_t *t1 = ecs_get_table(world, e1); + ecs_remove(world, e1, TagB); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ TagA }} + }); + + int this_var_id = ecs_query_find_var(q, "this"); + test_assert(this_var_id != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var_as_table(&it, this_var_id, t1); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == t1); + test_uint(ecs_field_id(&it, 0), TagA); + test_uint(ecs_field_size(&it, 0), 0); + ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); + test_assert(this_var != NULL); + test_assert(this_var == t1); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_to_empty_table_w_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add(world, e1, TagA); + ecs_table_t *t1 = ecs_get_table(world, e1); + ecs_remove(world, e1, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position) }} + }); + + int this_var_id = ecs_query_find_var(q, "this"); + test_assert(this_var_id != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var_as_table(&it, this_var_id, t1); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == t1); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); + test_assert(this_var != NULL); + test_assert(this_var == t1); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_to_empty_table_w_component_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, Position); + ecs_add(world, e1, TagA); + ecs_table_t *t1 = ecs_get_table(world, e1); + ecs_remove(world, e1, TagA); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ ecs_id(Position), .src.id = EcsSelf }}, + .cache_kind = cache_kind + }); + + int this_var_id = ecs_query_find_var(q, "this"); + test_assert(this_var_id != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var_as_table(&it, this_var_id, t1); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 0); + test_assert(it.table == t1); + test_uint(ecs_field_id(&it, 0), ecs_id(Position)); + test_uint(ecs_field_size(&it, 0), sizeof(Position)); + ecs_table_t* this_var = ecs_iter_get_var_as_table(&it, this_var_id); + test_assert(this_var != NULL); + test_assert(this_var == t1); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_to_entity_in_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms[0].id = ecs_id(Position), + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_to_entity_in_table_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position), .src.id = EcsSelf }, + .cache_kind = cache_kind + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Bar, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = Foo }, + .terms[1] = { .id = Bar }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_new_w(world, Bar); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_add(world, e1, Bar); + + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_add_pair(world, e3, EcsIsA, prefab); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(prefab, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_self(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Bar, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = Foo, .src.id = EcsSelf }, + .terms[1] = { .id = Bar, .src.id = EcsSelf }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_new_w(world, Bar); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_add(world, e1, Bar); + + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add(world, e2, Bar); + + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_add_pair(world, e3, EcsIsA, prefab); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position) }, + .terms[1] = { .id = ecs_id(Velocity) }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_insert(world, ecs_value(Velocity, {3, 4})); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_set(world, e2, Velocity, {2, 3}); + + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add_pair(world, e3, EcsIsA, prefab); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 3); + test_int(v->y, 4); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(prefab, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_self_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position), .src.id = EcsSelf }, + .terms[1] = { .id = ecs_id(Velocity), .src.id = EcsSelf }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_insert(world, ecs_value(Velocity, {4, 5})); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set(world, e1, Velocity, {1, 2}); + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_set(world, e2, Velocity, {2, 3}); + + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {40, 50})); + ecs_add_pair(world, e3, EcsIsA, prefab); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 3); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Bar, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Hello, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = Foo }, + .terms[1] = { .id = Bar }, + .terms[2] = { .id = Hello, .src.id = EcsUp, .trav = EcsChildOf }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_new_w(world, Bar); + ecs_entity_t parent = ecs_new_w(world, Hello); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_add(world, e1, Bar); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add(world, e2, Bar); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_add_pair(world, e3, EcsIsA, prefab); + ecs_add_pair(world, e3, EcsChildOf, parent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(Hello, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(Hello, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(Hello, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(prefab, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_self_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Bar, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Hello, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = Foo, .src.id = EcsSelf }, + .terms[1] = { .id = Bar, .src.id = EcsSelf }, + .terms[2] = { .id = Hello, .src.id = EcsUp, .trav = EcsChildOf }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_new_w(world, Bar); + ecs_entity_t parent = ecs_new_w(world, Hello); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_add(world, e1, Bar); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add(world, e2, Bar); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_add_pair(world, e3, EcsIsA, prefab); + ecs_add_pair(world, e3, EcsChildOf, parent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(Hello, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(Hello, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_component_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position) }, + .terms[1] = { .id = ecs_id(Velocity) }, + .terms[2] = { .id = ecs_id(Mass), .src.id = EcsUp, .trav = EcsChildOf }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_insert(world, ecs_value(Velocity, {3, 4})); + ecs_entity_t parent = ecs_insert(world, ecs_value(Mass, {60})); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_set(world, e2, Velocity, {2, 3}); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add_pair(world, e3, EcsIsA, prefab); + ecs_add_pair(world, e3, EcsChildOf, parent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(m[0], 60); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 3); + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(m[0], 60); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 3); + test_int(v->y, 4); + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(m[0], 60); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(prefab, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_self_component_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Velocity), EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = ecs_id(Position), .src.id = EcsSelf }, + .terms[1] = { .id = ecs_id(Velocity), .src.id = EcsSelf }, + .terms[2] = { .id = ecs_id(Mass), .src.id = EcsUp, .trav = EcsChildOf }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_insert(world, ecs_value(Velocity, {3, 4})); + ecs_entity_t parent = ecs_insert(world, ecs_value(Mass, {60})); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_set(world, e1, Velocity, {1, 2}); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {20, 30})); + ecs_set(world, e2, Velocity, {2, 3}); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {30, 40})); + ecs_add_pair(world, e3, EcsIsA, prefab); + ecs_add_pair(world, e3, EcsChildOf, parent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(m[0], 60); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 3); + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(m[0], 60); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_id(Mass), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(parent, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_w_exclusive_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Bar, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Hello, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = Foo }, + .terms[1] = { .id = Bar }, + .terms[2] = { .id = ecs_pair(EcsChildOf, EcsWildcard) }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_new_w(world, Bar); + ecs_entity_t parent = ecs_new_w(world, Hello); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_add(world, e1, Bar); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add(world, e2, Bar); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_add_pair(world, e3, EcsIsA, prefab); + ecs_add_pair(world, e3, EcsChildOf, parent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(prefab, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_self_w_exclusive_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = Foo, .src.id = EcsSelf }, + .terms[1] = { .id = ecs_pair(EcsChildOf, EcsWildcard) }, + .cache_kind = cache_kind + }); + + ecs_entity_t parent_1 = ecs_new_w(world, Hello); + ecs_entity_t parent_2 = ecs_new_w(world, Hello); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_add_pair(world, e1, EcsChildOf, parent_1); + + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add_pair(world, e2, EcsChildOf, parent_2); + + ecs_entity_t e3 = ecs_new_w(world, Foo); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, parent_1), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, parent_2), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_set_src_this_self_w_exclusive_wildcard_w_up(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + ECS_TAG(world, Hello); + + ecs_add_pair(world, Foo, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Bar, EcsOnInstantiate, EcsInherit); + ecs_add_pair(world, Hello, EcsOnInstantiate, EcsInherit); + + ecs_query_t *q = ecs_query(world, { + .terms[0] = { .id = Foo, .src.id = EcsSelf }, + .terms[1] = { .id = ecs_pair(EcsChildOf, EcsWildcard), .src.id = EcsSelf }, + .terms[2] = { .id = Bar, .src.id = EcsUp, .trav = EcsIsA }, + .cache_kind = cache_kind + }); + + ecs_entity_t prefab = ecs_new_w(world, Bar); + ecs_entity_t parent = ecs_new_w(world, Hello); + + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_add_pair(world, e1, EcsIsA, prefab); + ecs_add_pair(world, e1, EcsChildOf, parent); + + ecs_entity_t e2 = ecs_new_w(world, Foo); + ecs_add_pair(world, e2, EcsIsA, prefab); + ecs_add_pair(world, e2, EcsChildOf, parent); + + ecs_entity_t e3 = ecs_new_w(world, Foo); + ecs_add(world, e3, Bar); + ecs_add_pair(world, e3, EcsChildOf, parent); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 1)); + test_uint(Bar, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(prefab, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 1)); + test_uint(Bar, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(prefab, ecs_field_src(&it, 2)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_is_true(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position) } + }, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + /* ecs_entity_t e1 = */ ecs_insert(ecs, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {20, 30})); + /* ecs_entity_t e3 = */ ecs_insert(ecs, ecs_value(Position, {30, 40})); + ecs_entity_t e4 = ecs_insert(ecs, ecs_value(Velocity, {20, 30})); + + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_iter_is_true(&it)); + + it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e4); + test_bool(false, ecs_iter_is_true(&it)); + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_2_set_src_this_w_wildcard(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_TAG(ecs, Likes); + ECS_TAG(ecs, Apples); + ECS_TAG(ecs, Pears); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_pair(Likes, EcsWildcard) } + }, + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {20, 30})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {30, 40})); + + ecs_add_pair(ecs, e1, Likes, Apples); + ecs_add_pair(ecs, e2, Likes, Apples); + ecs_add_pair(ecs, e3, Likes, Apples); + + ecs_add_pair(ecs, e1, Likes, Pears); + ecs_add_pair(ecs, e2, Likes, Pears); + ecs_add_pair(ecs, e3, Likes, Pears); + + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + + { + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + test_uint(ecs_pair(Likes, Apples), ecs_field_id(&it, 1)); + } + { + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 0); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + test_uint(ecs_pair(Likes, Pears), ecs_field_id(&it, 1)); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_uncacheable_tag_tag_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_TAG(ecs, TagA); + ECS_TAG(ecs, TagB); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_new_w(ecs, TagA); + ecs_entity_t e2 = ecs_new_w(ecs, TagA); + ecs_entity_t e3 = ecs_new_w(ecs, TagA); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_add_pair(ecs, e1, EcsChildOf, p1); + ecs_add_pair(ecs, e2, EcsChildOf, p1); + ecs_add_pair(ecs, e3, EcsChildOf, p2); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .first.id = EcsChildOf, .second.name = "$parent" }, + { TagA }, + { TagB } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_childof(p1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_childof(p1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_childof(p2), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_uncacheable_tag_component_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_TAG(ecs, TagB); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_add_pair(ecs, e1, EcsChildOf, p1); + ecs_add_pair(ecs, e2, EcsChildOf, p1); + ecs_add_pair(ecs, e3, EcsChildOf, p2); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .first.id = EcsChildOf, .second.name = "$parent" }, + { ecs_id(Position) }, + { TagB } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_childof(p1), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_childof(p1), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_childof(p2), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_uncacheable_tag_component_component(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_set(ecs, e1, Velocity, {1, 1}); + ecs_set(ecs, e2, Velocity, {2, 2}); + ecs_set(ecs, e3, Velocity, {3, 3}); + ecs_add_pair(ecs, e1, EcsChildOf, p1); + ecs_add_pair(ecs, e2, EcsChildOf, p1); + ecs_add_pair(ecs, e3, EcsChildOf, p2); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .first.id = EcsChildOf, .second.name = "$parent" }, + { ecs_id(Position) }, + { ecs_id(Velocity) } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_childof(p1), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 1); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_childof(p1), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 2); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_childof(p2), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 3); + test_int(v->y, 3); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_tag_uncacheable_tag_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_TAG(ecs, TagA); + ECS_TAG(ecs, TagB); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_new_w(ecs, TagA); + ecs_entity_t e2 = ecs_new_w(ecs, TagA); + ecs_entity_t e3 = ecs_new_w(ecs, TagA); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_add_pair(ecs, e1, EcsChildOf, p1); + ecs_add_pair(ecs, e2, EcsChildOf, p1); + ecs_add_pair(ecs, e3, EcsChildOf, p2); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { TagA }, + { .first.id = EcsChildOf, .second.name = "$parent" }, + { TagB } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_childof(p2), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_component_uncacheable_tag_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_TAG(ecs, TagB); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_add_pair(ecs, e1, EcsChildOf, p1); + ecs_add_pair(ecs, e2, EcsChildOf, p1); + ecs_add_pair(ecs, e3, EcsChildOf, p2); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { ecs_id(Position) }, + { .first.id = EcsChildOf, .second.name = "$parent" }, + { TagB } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_childof(p2), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_component_uncacheable_tag_component(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_set(ecs, e1, Velocity, {1, 1}); + ecs_set(ecs, e2, Velocity, {2, 2}); + ecs_set(ecs, e3, Velocity, {3, 3}); + ecs_add_pair(ecs, e1, EcsChildOf, p1); + ecs_add_pair(ecs, e2, EcsChildOf, p1); + ecs_add_pair(ecs, e3, EcsChildOf, p2); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { ecs_id(Position) }, + { .first.id = EcsChildOf, .second.name = "$parent" }, + { ecs_id(Velocity) } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 1); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 2); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_childof(p2), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 3); + test_int(v->y, 3); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_tag_tag_uncacheable_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_TAG(ecs, TagA); + ECS_TAG(ecs, TagB); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_new_w(ecs, TagA); + ecs_entity_t e2 = ecs_new_w(ecs, TagA); + ecs_entity_t e3 = ecs_new_w(ecs, TagA); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_add_pair(ecs, e1, EcsChildOf, p1); + ecs_add_pair(ecs, e2, EcsChildOf, p1); + ecs_add_pair(ecs, e3, EcsChildOf, p2); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { TagA }, + { TagB }, + { .first.id = EcsChildOf, .second.name = "$parent" } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_childof(p2), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_component_tag_uncacheable_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_TAG(ecs, TagB); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_add_pair(ecs, e1, EcsChildOf, p1); + ecs_add_pair(ecs, e2, EcsChildOf, p1); + ecs_add_pair(ecs, e3, EcsChildOf, p2); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { ecs_id(Position) }, + { TagB }, + { .first.id = EcsChildOf, .second.name = "$parent" } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_childof(p2), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_component_component_uncacheable_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_set(ecs, e1, Velocity, {1, 1}); + ecs_set(ecs, e2, Velocity, {2, 2}); + ecs_set(ecs, e3, Velocity, {3, 3}); + ecs_add_pair(ecs, e1, EcsChildOf, p1); + ecs_add_pair(ecs, e2, EcsChildOf, p1); + ecs_add_pair(ecs, e3, EcsChildOf, p2); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Velocity) }, + { .first.id = EcsChildOf, .second.name = "$parent" } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 1); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_childof(p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 2); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_childof(p2), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 3); + test_int(v->y, 3); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_uncacheable_component_tag_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_TAG(ecs, TagA); + ECS_TAG(ecs, TagB); + ECS_COMPONENT(ecs, Mass); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_new_w(ecs, TagA); + ecs_entity_t e2 = ecs_new_w(ecs, TagA); + ecs_entity_t e3 = ecs_new_w(ecs, TagA); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_set_pair(ecs, e1, Mass, p1, {100}); + ecs_set_pair(ecs, e2, Mass, p1, {200}); + ecs_set_pair(ecs, e3, Mass, p2, {300}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .first.id = ecs_id(Mass), .second.name = "$parent" }, + { TagA }, + { TagB } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 0); + test_assert(m != NULL); + test_int(*m, 100); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 0); + test_assert(m != NULL); + test_int(*m, 200); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_pair_t(Mass, p2), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 0); + test_assert(m != NULL); + test_int(*m, 300); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_uncacheable_component_component_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Mass); + ECS_COMPONENT(ecs, Position); + ECS_TAG(ecs, TagB); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_set_pair(ecs, e1, Mass, p1, {100}); + ecs_set_pair(ecs, e2, Mass, p1, {200}); + ecs_set_pair(ecs, e3, Mass, p2, {300}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .first.id = ecs_id(Mass), .second.name = "$parent" }, + { ecs_id(Position) }, + { TagB } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 0); + test_assert(m != NULL); + test_int(*m, 100); + } + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 0); + test_assert(m != NULL); + test_int(*m, 200); + } + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_pair_t(Mass, p2), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 0); + test_assert(m != NULL); + test_int(*m, 300); + } + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_uncacheable_component_component_component(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Mass); + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_set(ecs, e1, Velocity, {1, 1}); + ecs_set(ecs, e2, Velocity, {2, 2}); + ecs_set(ecs, e3, Velocity, {3, 3}); + ecs_set_pair(ecs, e1, Mass, p1, {100}); + ecs_set_pair(ecs, e2, Mass, p1, {200}); + ecs_set_pair(ecs, e3, Mass, p2, {300}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { .first.id = ecs_id(Mass), .second.name = "$parent" }, + { ecs_id(Position) }, + { ecs_id(Velocity) } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 0); + test_assert(m != NULL); + test_int(*m, 100); + } + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 1); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 0); + test_assert(m != NULL); + test_int(*m, 200); + } + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 2); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_pair_t(Mass, p2), ecs_field_id(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 0); + test_assert(m != NULL); + test_int(*m, 300); + } + { + Position *p = ecs_field(&it, Position, 1); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 3); + test_int(v->y, 3); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_tag_uncacheable_component_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_TAG(ecs, TagA); + ECS_TAG(ecs, TagB); + ECS_COMPONENT(ecs, Mass); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_new_w(ecs, TagA); + ecs_entity_t e2 = ecs_new_w(ecs, TagA); + ecs_entity_t e3 = ecs_new_w(ecs, TagA); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_set_pair(ecs, e1, Mass, p1, {100}); + ecs_set_pair(ecs, e2, Mass, p1, {200}); + ecs_set_pair(ecs, e3, Mass, p2, {300}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { TagA }, + { .first.id = ecs_id(Mass), .second.name = "$parent" }, + { TagB } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 1); + test_assert(m != NULL); + test_int(*m, 100); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 1); + test_assert(m != NULL); + test_int(*m, 200); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair_t(Mass, p2), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 1); + test_assert(m != NULL); + test_int(*m, 300); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_component_uncacheable_component_tag(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Mass); + ECS_COMPONENT(ecs, Position); + ECS_TAG(ecs, TagB); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_set_pair(ecs, e1, Mass, p1, {100}); + ecs_set_pair(ecs, e2, Mass, p1, {200}); + ecs_set_pair(ecs, e3, Mass, p2, {300}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { ecs_id(Position) }, + { .first.id = ecs_id(Mass), .second.name = "$parent" }, + { TagB } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 1); + test_assert(m != NULL); + test_int(*m, 100); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 1); + test_assert(m != NULL); + test_int(*m, 200); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair_t(Mass, p2), ecs_field_id(&it, 1)); + test_uint(TagB, ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 1); + test_assert(m != NULL); + test_int(*m, 300); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_component_uncacheable_component_component(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Mass); + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_set(ecs, e1, Velocity, {1, 1}); + ecs_set(ecs, e2, Velocity, {2, 2}); + ecs_set(ecs, e3, Velocity, {3, 3}); + ecs_set_pair(ecs, e1, Mass, p1, {100}); + ecs_set_pair(ecs, e2, Mass, p1, {200}); + ecs_set_pair(ecs, e3, Mass, p2, {300}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { ecs_id(Position) }, + { .first.id = ecs_id(Mass), .second.name = "$parent" }, + { ecs_id(Velocity) } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 1); + test_assert(m != NULL); + test_int(*m, 100); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 1); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 1); + test_assert(m != NULL); + test_int(*m, 200); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 2); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair_t(Mass, p2), ecs_field_id(&it, 1)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 1); + test_assert(m != NULL); + test_int(*m, 300); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + { + Velocity *v = ecs_field(&it, Velocity, 2); + test_assert(v != NULL); + test_int(v->x, 3); + test_int(v->y, 3); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_tag_tag_uncacheable_component(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_TAG(ecs, TagA); + ECS_TAG(ecs, TagB); + ECS_COMPONENT(ecs, Mass); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_new_w(ecs, TagA); + ecs_entity_t e2 = ecs_new_w(ecs, TagA); + ecs_entity_t e3 = ecs_new_w(ecs, TagA); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_set_pair(ecs, e1, Mass, p1, {100}); + ecs_set_pair(ecs, e2, Mass, p1, {200}); + ecs_set_pair(ecs, e3, Mass, p2, {300}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { TagA }, + { TagB }, + { .first.id = ecs_id(Mass), .second.name = "$parent" } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 100); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 200); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Mass, p2), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 300); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_component_tag_uncacheable_component(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Mass); + ECS_COMPONENT(ecs, Position); + ECS_TAG(ecs, TagB); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_add(ecs, e1, TagB); + ecs_add(ecs, e2, TagB); + ecs_add(ecs, e3, TagB); + ecs_set_pair(ecs, e1, Mass, p1, {100}); + ecs_set_pair(ecs, e2, Mass, p1, {200}); + ecs_set_pair(ecs, e3, Mass, p2, {300}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { ecs_id(Position) }, + { TagB }, + { .first.id = ecs_id(Mass), .second.name = "$parent" } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 100); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 200); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Mass, p2), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 300); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_3_set_src_this_w_component_component_uncacheable_component(void) { + ecs_world_t* ecs = ecs_mini(); + + ECS_COMPONENT(ecs, Mass); + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_entity_t p1 = ecs_new(ecs); + ecs_entity_t p2 = ecs_new(ecs); + ecs_entity_t e1 = ecs_insert(ecs, ecs_value(Position, {11, 21})); + ecs_entity_t e2 = ecs_insert(ecs, ecs_value(Position, {12, 22})); + ecs_entity_t e3 = ecs_insert(ecs, ecs_value(Position, {13, 23})); + ecs_set(ecs, e1, Velocity, {1, 1}); + ecs_set(ecs, e2, Velocity, {2, 2}); + ecs_set(ecs, e3, Velocity, {3, 3}); + ecs_set_pair(ecs, e1, Mass, p1, {100}); + ecs_set_pair(ecs, e2, Mass, p1, {200}); + ecs_set_pair(ecs, e3, Mass, p2, {300}); + + ecs_query_t *q = ecs_query(ecs, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Velocity) }, + { .first.id = ecs_id(Mass), .second.name = "$parent" } + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t parent_var = ecs_query_find_var(q, "parent"); + test_assert(parent_var != -1); + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e1); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 100); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 1); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Mass, p1), ecs_field_id(&it, 2)); + test_uint(p1, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 200); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 12); + test_int(p->y, 22); + } + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 2); + } + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e3); + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e3); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_pair_t(Mass, p2), ecs_field_id(&it, 2)); + test_uint(p2, ecs_iter_get_var(&it, parent_var)); + { + Mass *m = ecs_field(&it, Mass, 2); + test_assert(m != NULL); + test_int(*m, 300); + } + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 13); + test_int(p->y, 23); + } + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 3); + test_int(v->y, 3); + } + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(ecs); +} + +void Variables_1_src_this_var_as_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagC); + ecs_add(world, e4, TagC); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ TagA }}, + .cache_kind = cache_kind + }); + + int this_var_id = ecs_query_find_var(q, "this"); + test_assert(this_var_id != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + ecs_entity_t this_var = ecs_iter_get_var(&it, this_var_id); + test_assert(this_var != 0); + test_assert(this_var == e1); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + this_var = ecs_iter_get_var(&it, this_var_id); + test_assert(this_var != 0); + test_assert(this_var == e2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 2); + test_uint(it.entities[0], e3); + test_uint(it.entities[1], e4); + this_var = ecs_iter_get_var(&it, this_var_id); + test_assert(this_var == 0); /* more than one entity matches */ + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_this_var_as_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagC); + ecs_add(world, e4, TagC); + + ecs_table_t *t1 = ecs_get_table(world, e1); + test_assert(t1 != NULL); + + ecs_table_t *t2 = ecs_get_table(world, e2); + test_assert(t2 != NULL); + test_assert(t2 != t1); + + ecs_table_t *t3 = ecs_get_table(world, e3); + test_assert(t3 != NULL); + test_assert(t3 != t1); + test_assert(t3 != t2); + test_assert(t3 == ecs_get_table(world, e4)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ TagA }}, + .cache_kind = cache_kind + }); + + int this_var_id = ecs_query_find_var(q, "this"); + test_assert(this_var_id != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + ecs_table_t *this_var = ecs_iter_get_var_as_table(&it, this_var_id); + test_assert(this_var != NULL); + test_assert(this_var == t1); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + this_var = ecs_iter_get_var_as_table(&it, this_var_id); + test_assert(this_var != NULL); + test_assert(this_var == t2); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 2); + test_uint(it.entities[0], e3); + test_uint(it.entities[1], e4); + this_var = ecs_iter_get_var_as_table(&it, this_var_id); + test_assert(this_var != NULL); + test_assert(this_var == t3); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_src_this_var_as_table_range(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + ecs_entity_t e4 = ecs_new_w(world, TagA); + ecs_add(world, e2, TagB); + ecs_add(world, e3, TagC); + ecs_add(world, e4, TagC); + + ecs_table_t *t1 = ecs_get_table(world, e1); + test_assert(t1 != NULL); + + ecs_table_t *t2 = ecs_get_table(world, e2); + test_assert(t2 != NULL); + test_assert(t2 != t1); + + ecs_table_t *t3 = ecs_get_table(world, e3); + test_assert(t3 != NULL); + test_assert(t3 != t1); + test_assert(t3 != t2); + test_assert(t3 == ecs_get_table(world, e4)); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ TagA }}, + .cache_kind = cache_kind + }); + + int this_var_id = ecs_query_find_var(q, "this"); + test_assert(this_var_id != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e1); + ecs_table_range_t this_var = ecs_iter_get_var_as_range(&it, this_var_id); + test_assert(this_var.table != NULL); + test_assert(this_var.table == t1); + test_assert(this_var.offset == 0); + test_assert(this_var.count == 1); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + this_var = ecs_iter_get_var_as_range(&it, this_var_id); + test_assert(this_var.table != NULL); + test_assert(this_var.table == t2); + test_assert(this_var.offset == 0); + test_assert(this_var.count == 1); + + test_assert(ecs_query_next(&it)); + test_int(it.count, 2); + test_uint(it.entities[0], e3); + test_uint(it.entities[1], e4); + this_var = ecs_iter_get_var_as_range(&it, this_var_id); + test_assert(this_var.table != NULL); + test_assert(this_var.table == t3); + test_assert(this_var.offset == 0); + test_assert(this_var.count == 2); + + test_assert(!ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_w_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Foo); + + ecs_query_t *q = ecs_query(world, { + .terms = {{ EcsAny }} + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(EcsWildcard, ecs_field_id(&it, 0)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_w_any_fixed(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Foo); + + ecs_entity_t f = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { EcsAny }, + { Bar, .src.id = f } + } + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, f, Bar); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(f, ecs_field_src(&it, 1)); + test_uint(EcsWildcard, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_set_src_this_w_fixed_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_entity_t e = ecs_new(world); + ecs_add(world, e, Foo); + + ecs_entity_t f = ecs_new(world); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { Bar, .src.id = f }, + { EcsAny } + } + }); + + test_assert(q != NULL); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add(world, f, Bar); + + { + ecs_iter_t it = ecs_query_iter(world, q); + ecs_iter_set_var(&it, 0, e); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(f, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(Bar, ecs_field_id(&it, 0)); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_join_by_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + + ecs_entity_t e1 = ecs_new_w(world, RelA); + ecs_add(world, e1, RelB); + ecs_add_pair(world, e1, TagA, TagB); + + ecs_entity_t e2 = ecs_new_w(world, RelA); + ecs_entity_t e3 = ecs_new_w(world, RelB); + ecs_new_w(world, RelC); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this), $x($y), TagA($this, TagB)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(TagA, TagB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelA, ecs_field_id(&it, 0)); + test_uint(RelA, ecs_field_id(&it, 1)); + test_uint(ecs_pair(TagA, TagB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(ecs_pair(TagA, TagB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(RelB, ecs_field_id(&it, 0)); + test_uint(RelB, ecs_field_id(&it, 1)); + test_uint(ecs_pair(TagA, TagB), ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_join_by_pair_rel_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + ECS_TAG(world, RelC); + ECS_TAG(world, TgtA); + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_add_pair(world, e1, RelB, TgtA); + ecs_add(world, e1, TagA); + + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelB, TgtA); + ecs_new_w_pair(world, RelC, TgtA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, TgtA), $x($y, TgtA), TagA", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(RelA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelB, TgtA), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(RelB, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_join_by_pair_tgt_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, RelA); + ECS_TAG(world, TgtA); + ECS_TAG(world, TgtB); + ECS_TAG(world, TgtC); + + ecs_entity_t e1 = ecs_new_w_pair(world, RelA, TgtA); + ecs_add_pair(world, e1, RelA, TgtB); + ecs_add(world, e1, TagA); + + ecs_entity_t e2 = ecs_new_w_pair(world, RelA, TgtA); + ecs_entity_t e3 = ecs_new_w_pair(world, RelA, TgtB); + ecs_new_w_pair(world, RelA, TgtC); + + ecs_query_t *q = ecs_query(world, { + .expr = "RelA($this, $x), RelA($y, $x), TagA", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtA), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(TgtA, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 0)); + test_uint(ecs_pair(RelA, TgtB), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(TgtB, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($y), $y($x), TagA($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e2); + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(e2, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e2, ecs_field_src(&it, 2)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(e1, ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_w_this_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this($y), $y($this), TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + if (cache_kind == EcsQueryCacheDefault) { + ecs_add_id(world, e1, e2); + ecs_add_id(world, e2, e1); + } else { + /* Cached query reverses the result */ + ecs_add_id(world, e2, e1); + ecs_add_id(world, e1, e2); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_w_var_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this), $this($x), TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_id(world, e1, e2); + ecs_add_id(world, e2, e1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e2, ecs_field_id(&it, 0)); + test_uint(e1, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(e1, ecs_field_id(&it, 0)); + test_uint(e2, ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_pair_w_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($y, $z), $x($z, $y), TagA($y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + int z_var = ecs_query_find_var(q, "z"); + test_assert(z_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, e3); + ecs_add_pair(world, e3, e1, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e2, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + test_uint(e3, ecs_iter_get_var(&it, z_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e3, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + test_uint(e2, ecs_iter_get_var(&it, z_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_pair_w_this_var_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$this($y, $z), $this($z, $y), TagA($y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + int z_var = ecs_query_find_var(q, "z"); + test_assert(z_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, e3); + ecs_add_pair(world, e3, e1, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e2, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + test_uint(e3, ecs_iter_get_var(&it, z_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e3, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + test_uint(e2, ecs_iter_get_var(&it, z_var)); + test_uint(e1, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_pair_w_var_this_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $z), $x($z, $this), TagA($this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + int z_var = ecs_query_find_var(q, "z"); + test_assert(z_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, e3); + ecs_add_pair(world, e3, e1, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e3, ecs_iter_get_var(&it, z_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(0, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, this_var)); + test_uint(e2, ecs_iter_get_var(&it, z_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_pair_w_var_var_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e1 = ecs_new_w(world, TagA); + ecs_entity_t e2 = ecs_new_w(world, TagA); + ecs_entity_t e3 = ecs_new_w(world, TagA); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($y, $this), $x($this, $y), TagA($y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e2, e1, e3); + ecs_add_pair(world, e3, e1, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + test_uint(e3, ecs_iter_get_var(&it, this_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(e1, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(e1, e3), ecs_field_id(&it, 1)); + test_uint(TagA, ecs_field_id(&it, 2)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e3, ecs_field_src(&it, 2)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_pair_ent_var_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Likes); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Likes($x, $y), Likes($y, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Likes, e2); + ecs_add_pair(world, e1, Likes, e1); + ecs_add_pair(world, e2, Likes, e1); + ecs_add_pair(world, e2, Likes, e3); + ecs_add_pair(world, e3, Likes, e1); + ecs_add_pair(world, e3, Likes, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_pair_ent_this_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Likes); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Likes($this, $y), Likes($y, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Likes, e2); + ecs_add_pair(world, e1, Likes, e1); + ecs_add_pair(world, e2, Likes, e1); + ecs_add_pair(world, e2, Likes, e3); + ecs_add_pair(world, e3, Likes, e1); + ecs_add_pair(world, e3, Likes, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e2, ecs_field_src(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, this_var)); + test_uint(e2, ecs_iter_get_var(&it, y_var)); + test_uint(e3, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e1, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e1, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(e3, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e3, ecs_iter_get_var(&it, y_var)); + test_uint(e2, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_cycle_pair_ent_var_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Likes); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + ecs_entity_t e3 = ecs_entity(world, { .name = "e3" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "Likes($x, $this), Likes($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_assert(this_var != 0); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(false, ecs_query_next(&it)); + } + + ecs_add_pair(world, e1, Likes, e2); + ecs_add_pair(world, e1, Likes, e1); + ecs_add_pair(world, e2, Likes, e1); + ecs_add_pair(world, e2, Likes, e3); + ecs_add_pair(world, e3, Likes, e1); + ecs_add_pair(world, e3, Likes, e2); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 1)); + test_uint(e1, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e1, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 1)); + test_uint(e3, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e3, ecs_iter_get_var(&it, x_var)); + test_uint(e2, ecs_iter_get_var(&it, this_var)); + test_uint(e2, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e1), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(e1, ecs_iter_get_var(&it, this_var)); + test_uint(e1, it.entities[0]); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(ecs_pair(Likes, e3), ecs_field_id(&it, 0)); + test_uint(ecs_pair(Likes, e2), ecs_field_id(&it, 1)); + test_uint(e2, ecs_field_src(&it, 0)); + test_uint(0, ecs_field_src(&it, 1)); + test_uint(e2, ecs_iter_get_var(&it, x_var)); + test_uint(e3, ecs_iter_get_var(&it, this_var)); + test_uint(e3, it.entities[0]); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_0_var(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, "")); + test_uint(it.variables[x_var].entity, EcsWildcard); + test_uint(it.variables[y_var].entity, EcsWildcard); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_1_var(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, "x:e1")); + test_uint(it.variables[x_var].entity, e1); + test_uint(it.variables[y_var].entity, EcsWildcard); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_2_vars(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, "x:e1,y:e2")); + test_uint(it.variables[x_var].entity, e1); + test_uint(it.variables[y_var].entity, e2); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_0_var_paren(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, "()")); + test_uint(it.variables[x_var].entity, EcsWildcard); + test_uint(it.variables[y_var].entity, EcsWildcard); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_1_var_paren(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, "(x:e1)")); + test_uint(it.variables[x_var].entity, e1); + test_uint(it.variables[y_var].entity, EcsWildcard); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_2_vars_paren(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, "(x:e1,y:e2)")); + test_uint(it.variables[x_var].entity, e1); + test_uint(it.variables[y_var].entity, e2); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_1_vars_w_path(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "parent.e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, "x:parent.e1")); + test_uint(it.variables[x_var].entity, e1); + test_uint(it.variables[y_var].entity, EcsWildcard); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_missing_close_paren(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_log_set_level(-4); + test_str(NULL, ecs_query_args_parse(q, &it, "(x:e1")); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_missing_open_paren(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_log_set_level(-4); + test_str(NULL, ecs_query_args_parse(q, &it, "x:e1)")); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_missing_value(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + ecs_log_set_level(-4); + test_str(NULL, ecs_query_args_parse(q, &it, "x:")); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_0_var_w_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, " ")); + test_uint(it.variables[x_var].entity, EcsWildcard); + test_uint(it.variables[y_var].entity, EcsWildcard); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_1_var_w_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, " x : e1 ")); + test_uint(it.variables[x_var].entity, e1); + test_uint(it.variables[y_var].entity, EcsWildcard); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_2_vars_w_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str("", ecs_query_args_parse(q, &it, " x : e1 , y : e2 ")); + test_uint(it.variables[x_var].entity, e1); + test_uint(it.variables[y_var].entity, e2); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_0_var_paren_w_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str(" ", ecs_query_args_parse(q, &it, " ( ) ")); + test_uint(it.variables[x_var].entity, EcsWildcard); + test_uint(it.variables[y_var].entity, EcsWildcard); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_1_var_paren_w_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str(" ", ecs_query_args_parse(q, &it, " ( x : e1 ) ")); + test_uint(it.variables[x_var].entity, e1); + test_uint(it.variables[y_var].entity, EcsWildcard); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_parse_2_vars_paren_w_spaces(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e1 = ecs_entity(world, { .name = "e1" }); + ecs_entity_t e2 = ecs_entity(world, { .name = "e2" }); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_str(" ", ecs_query_args_parse(q, &it, " ( x : e1 , y : e2 ) ")); + test_uint(it.variables[x_var].entity, e1); + test_uint(it.variables[y_var].entity, e2); + ecs_iter_fini(&it); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_var_count(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y), $x($this), $y($this)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + test_int(q->var_count, 3); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_var_name(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y), $x($this), $y($this)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + test_str(ecs_query_var_name(q, x_var), "x"); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + test_str(ecs_query_var_name(q, y_var), "y"); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_str(ecs_query_var_name(q, this_var), "this"); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_var_is_entity(void) { + ecs_world_t *world = ecs_mini(); + + ecs_query_t *q = ecs_query(world, { + .expr = "$x($this, $y), $x($this), $y($this)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + test_bool(ecs_query_var_is_entity(q, x_var), true); + + int y_var = ecs_query_find_var(q, "y"); + test_assert(y_var != -1); + test_bool(ecs_query_var_is_entity(q, y_var), true); + + int this_var = ecs_query_find_var(q, "this"); + test_assert(this_var != -1); + test_bool(ecs_query_var_is_entity(q, this_var), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_no_this_anonymous_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t e = ecs_new_w_id(world, TagA); + test_assert(e != 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($_x)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_uint(TagA, ecs_field_id(&it, 0)); + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_no_this_anonymous_src_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e = ecs_new_w_id(world, TagA); + test_assert(e != 0); + ecs_add_pair(world, e, EcsChildOf, parent); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($_x), ChildOf($_x, $Parent)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + int parent_var = ecs_query_find_var(q, "Parent"); + test_assert(parent_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, parent), ecs_field_id(&it, 1)); + test_uint(parent, ecs_iter_get_var(&it, parent_var)); + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_no_this_anonymous_component_src(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new_w(world, Position); + test_assert(e != 0); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position($x)", + .cache_kind = cache_kind + }); + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(ecs_query_next(&it), true); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_bool(ecs_query_next(&it), false); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_no_this_anonymous_component_src_w_pair(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e = ecs_new_w(world, Position); + test_assert(e != 0); + ecs_add_pair(world, e, EcsChildOf, parent); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .expr = "Position($_x), ChildOf($_x, $Parent)", + .cache_kind = cache_kind + }); + test_assert(q == NULL); + + ecs_fini(world); +} + +void Variables_lookup_from_table_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t p1 = ecs_new_w(world, TagA); + ecs_new_w(world, TagA); + ecs_entity_t p3 = ecs_new_w(world, TagA); + ecs_entity_t p4 = ecs_new_w(world, TagA); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, p1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, p3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, p4); + ecs_add(world, child_1, TagB); + ecs_add(world, child_4, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, TagB($this.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t child_var = ecs_query_find_var(q, "this.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p4, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(child_4, ecs_field_src(&it, 1)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_entity_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + ecs_entity_t t3 = ecs_new(world); + ecs_entity_t t4 = ecs_new(world); + + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); + ecs_new_w_pair(world, Rel, t2); + ecs_new_w_pair(world, Rel, t3); + ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, t1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, t3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, t4); + ecs_add(world, child_1, TagB); + ecs_add(world, child_4, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "Rel($x, $this), TagB($this.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "this.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(t1, it.entities[0]); + test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 0)); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(p1, ecs_iter_get_var(&it, x_var)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(t4, it.entities[0]); + test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 0)); + test_uint(p4, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(child_4, ecs_field_src(&it, 1)); + test_uint(p4, ecs_iter_get_var(&it, x_var)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_table(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t p1 = ecs_new_w(world, TagA); + ecs_new_w(world, TagA); + ecs_entity_t p3 = ecs_new_w(world, TagA); + ecs_entity_t p4 = ecs_new_w(world, TagA); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, p1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, p3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, p4); + ecs_add(world, child_1, TagB); + ecs_add(world, child_4, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), TagB($x.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "x.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(p1, ecs_iter_get_var(&it, x_var)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(p4, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(p4, ecs_iter_get_var(&it, x_var)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_entity(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + ecs_entity_t t3 = ecs_new(world); + ecs_entity_t t4 = ecs_new(world); + + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); + ecs_new_w_pair(world, Rel, t2); + ecs_new_w_pair(world, Rel, t3); + ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, t1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, t3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, t4); + ecs_add(world, child_1, TagB); + ecs_add(world, child_4, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, $x), TagB($x.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "x.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(t1, ecs_iter_get_var(&it, x_var)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p4, it.entities[0]); + test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(child_4, ecs_field_src(&it, 1)); + test_uint(t4, ecs_iter_get_var(&it, x_var)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_not_written(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + + ecs_log_set_level(-4); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($this.foo)", + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Variables_lookup_from_table_this_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_COMPONENT(world, Position); + + ecs_entity_t p1 = ecs_new_w(world, TagA); + ecs_new_w(world, TagA); + ecs_entity_t p3 = ecs_new_w(world, TagA); + ecs_entity_t p4 = ecs_new_w(world, TagA); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, p1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, p3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, p4); + ecs_set(world, child_1, Position, {10, 20}); + ecs_set(world, child_4, Position, {11, 22}); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, Position($this.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t child_var = ecs_query_find_var(q, "this.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + { + Position *ptr = ecs_field(&it, Position, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p4, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(child_4, ecs_field_src(&it, 1)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + { + Position *ptr = ecs_field(&it, Position, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 11); + test_int(ptr->y, 22); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_entity_this_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagA); + ECS_COMPONENT(world, Position); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + ecs_entity_t t3 = ecs_new(world); + ecs_entity_t t4 = ecs_new(world); + + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); + ecs_new_w_pair(world, Rel, t2); + ecs_new_w_pair(world, Rel, t3); + ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, t1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, t3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, t4); + ecs_set(world, child_1, Position, {10, 20}); + ecs_set(world, child_4, Position, {11, 22}); + + ecs_query_t *q = ecs_query(world, { + .expr = "Rel($x, $this), Position($this.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "this.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(t1, it.entities[0]); + test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 0)); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(p1, ecs_iter_get_var(&it, x_var)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + { + Position *ptr = ecs_field(&it, Position, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(t4, it.entities[0]); + test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 0)); + test_uint(p4, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(child_4, ecs_field_src(&it, 1)); + test_uint(p4, ecs_iter_get_var(&it, x_var)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + { + Position *ptr = ecs_field(&it, Position, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 11); + test_int(ptr->y, 22); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_table_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_COMPONENT(world, Position); + + ecs_entity_t p1 = ecs_new_w(world, TagA); + ecs_new_w(world, TagA); + ecs_entity_t p3 = ecs_new_w(world, TagA); + ecs_entity_t p4 = ecs_new_w(world, TagA); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, p1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, p3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, p4); + ecs_set(world, child_1, Position, {10, 20}); + ecs_set(world, child_4, Position, {11, 22}); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), Position($x.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "x.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(p1, ecs_iter_get_var(&it, x_var)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + { + Position *ptr = ecs_field(&it, Position, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(p4, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(p4, ecs_iter_get_var(&it, x_var)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + { + Position *ptr = ecs_field(&it, Position, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 11); + test_int(ptr->y, 22); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_entity_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagA); + ECS_COMPONENT(world, Position); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + ecs_entity_t t3 = ecs_new(world); + ecs_entity_t t4 = ecs_new(world); + + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); + ecs_new_w_pair(world, Rel, t2); + ecs_new_w_pair(world, Rel, t3); + ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, t1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, t3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, t4); + ecs_set(world, child_1, Position, {10, 20}); + ecs_set(world, child_4, Position, {11, 22}); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, $x), Position($x.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "x.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(t1, ecs_iter_get_var(&it, x_var)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + { + Position *ptr = ecs_field(&it, Position, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p4, it.entities[0]); + test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(ecs_id(Position), ecs_field_id(&it, 1)); + test_uint(child_4, ecs_field_src(&it, 1)); + test_uint(t4, ecs_iter_get_var(&it, x_var)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + { + Position *ptr = ecs_field(&it, Position, 1); + test_assert(ptr != NULL); + test_int(ptr->x, 11); + test_int(ptr->y, 22); + } + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_table_two_children(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t p1 = ecs_entity(world, { .name = "p1" }); ecs_add(world, p1, TagA); + ecs_new_w(world, TagA); + ecs_entity_t p3 = ecs_entity(world, { .name = "p3" }); ecs_add(world, p3, TagA); + ecs_entity_t p4 = ecs_entity(world, { .name = "p4" }); ecs_add(world, p4, TagA); + + ecs_entity_t child_a_1 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_1, EcsChildOf, p1); + ecs_entity_t child_a_3 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_3, EcsChildOf, p3); + ecs_entity_t child_a_4 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_4, EcsChildOf, p4); + ecs_add(world, child_a_1, TagB); + ecs_add(world, child_a_4, TagB); + + ecs_entity_t child_b_1 = ecs_entity(world, { .name = "child_b" }); + ecs_add_pair(world, child_b_1, EcsChildOf, p1); + ecs_entity_t child_b_3 = ecs_entity(world, { .name = "child_b" }); + ecs_add_pair(world, child_b_3, EcsChildOf, p3); + ecs_entity_t child_b_4 = ecs_entity(world, { .name = "child_b" }); + ecs_add_pair(world, child_b_4, EcsChildOf, p4); + ecs_add(world, child_b_1, TagC); + ecs_add(world, child_b_3, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, TagB($this.child_a), TagC($this.child_b)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t child_a_var = ecs_query_find_var(q, "this.child_a"); + test_assert(child_a_var != -1); + int32_t child_b_var = ecs_query_find_var(q, "this.child_b"); + test_assert(child_b_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(child_a_1, ecs_field_src(&it, 1)); + test_uint(child_b_1, ecs_field_src(&it, 2)); + test_uint(child_a_1, ecs_iter_get_var(&it, child_a_var)); + test_uint(child_b_1, ecs_iter_get_var(&it, child_b_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_entity_two_children(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + ecs_entity_t t3 = ecs_new(world); + ecs_entity_t t4 = ecs_new(world); + + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); + ecs_new_w_pair(world, Rel, t2); + ecs_new_w_pair(world, Rel, t3); + ecs_new_w_pair(world, Rel, t4); + + ecs_entity_t child_a_1 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_1, EcsChildOf, t1); + ecs_entity_t child_a_3 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_3, EcsChildOf, t3); + ecs_entity_t child_a_4 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_4, EcsChildOf, t4); + ecs_add(world, child_a_1, TagB); + ecs_add(world, child_a_4, TagB); + + ecs_entity_t child_b_1 = ecs_entity(world, { .name = "child_b" }); + ecs_add_pair(world, child_b_1, EcsChildOf, t1); + ecs_entity_t child_b_3 = ecs_entity(world, { .name = "child_b" }); + ecs_add_pair(world, child_b_3, EcsChildOf, t3); + ecs_entity_t child_b_4 = ecs_entity(world, { .name = "child_b" }); + ecs_add_pair(world, child_b_4, EcsChildOf, t4); + ecs_add(world, child_b_1, TagC); + ecs_add(world, child_b_3, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, $x), TagB($x.child_a), TagC($x.child_b)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_a_var = ecs_query_find_var(q, "x.child_a"); + test_assert(child_a_var != -1); + int32_t child_b_var = ecs_query_find_var(q, "x.child_b"); + test_assert(child_b_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(child_a_1, ecs_field_src(&it, 1)); + test_uint(child_b_1, ecs_field_src(&it, 2)); + test_uint(t1, ecs_iter_get_var(&it, x_var)); + test_uint(child_a_1, ecs_iter_get_var(&it, child_a_var)); + test_uint(child_b_1, ecs_iter_get_var(&it, child_b_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_table_same_child_twice(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t p1 = ecs_entity(world, { .name = "p1" }); ecs_add(world, p1, TagA); + ecs_new_w(world, TagA); + ecs_entity_t p3 = ecs_entity(world, { .name = "p3" }); ecs_add(world, p3, TagA); + ecs_entity_t p4 = ecs_entity(world, { .name = "p4" }); ecs_add(world, p4, TagA); + + ecs_entity_t child_a_1 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_1, EcsChildOf, p1); + ecs_entity_t child_a_3 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_3, EcsChildOf, p3); + ecs_entity_t child_a_4 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_4, EcsChildOf, p4); + ecs_add(world, child_a_1, TagB); + ecs_add(world, child_a_4, TagB); + ecs_add(world, child_a_1, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA, TagB($this.child_a), TagC($this.child_a)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t child_a_var = ecs_query_find_var(q, "this.child_a"); + test_assert(child_a_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(child_a_1, ecs_field_src(&it, 1)); + test_uint(child_a_1, ecs_field_src(&it, 2)); + test_uint(child_a_1, ecs_iter_get_var(&it, child_a_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_entity_same_child_twice(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + ecs_entity_t t3 = ecs_new(world); + ecs_entity_t t4 = ecs_new(world); + + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); + ecs_new_w_pair(world, Rel, t2); + ecs_new_w_pair(world, Rel, t3); + ecs_new_w_pair(world, Rel, t4); + + ecs_entity_t child_a_1 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_1, EcsChildOf, t1); + ecs_entity_t child_a_3 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_3, EcsChildOf, t3); + ecs_entity_t child_a_4 = ecs_entity(world, { .name = "child_a" }); + ecs_add_pair(world, child_a_4, EcsChildOf, t4); + ecs_add(world, child_a_1, TagB); + ecs_add(world, child_a_4, TagB); + ecs_add(world, child_a_1, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, $x), TagB($x.child_a), TagC($x.child_a)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_a_var = ecs_query_find_var(q, "x.child_a"); + test_assert(child_a_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(TagC, ecs_field_id(&it, 2)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(child_a_1, ecs_field_src(&it, 1)); + test_uint(child_a_1, ecs_field_src(&it, 2)); + test_uint(t1, ecs_iter_get_var(&it, x_var)); + test_uint(child_a_1, ecs_iter_get_var(&it, child_a_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_table_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t p1 = ecs_new_w(world, TagA); ecs_set_name(world, p1, "p1"); + ecs_entity_t p2 = ecs_new_w(world, TagA); ecs_set_name(world, p2, "p2"); + ecs_entity_t p3 = ecs_new_w(world, TagA); ecs_set_name(world, p3, "p3"); + ecs_entity_t p4 = ecs_new_w(world, TagA); ecs_set_name(world, p4, "p4"); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, p1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, p3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, p4); + ecs_add(world, child_1, TagB); + ecs_add(world, child_4, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), !TagB($x.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "x.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(p2, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(p2, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, child_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_uint(p3, ecs_field_src(&it, 0)); + test_bool(true, ecs_field_is_set(&it, 0)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(p3, ecs_iter_get_var(&it, x_var)); + test_uint(child_3, ecs_iter_get_var(&it, child_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_entity_not(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + ecs_entity_t t3 = ecs_new(world); + ecs_entity_t t4 = ecs_new(world); + + ecs_new_w_pair(world, Rel, t1); + ecs_entity_t p2 = ecs_new_w_pair(world, Rel, t2); + ecs_entity_t p3 = ecs_new_w_pair(world, Rel, t3); + ecs_new_w_pair(world, Rel, t4); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, t1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, t3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, t4); + ecs_add(world, child_1, TagB); + ecs_add(world, child_4, TagB); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, $x), !TagB($x.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "x.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p2, it.entities[0]); + test_uint(ecs_pair(Rel, t2), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(t2, ecs_iter_get_var(&it, x_var)); + test_uint(EcsWildcard, ecs_iter_get_var(&it, child_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p3, it.entities[0]); + test_uint(ecs_pair(Rel, t3), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(TagB, ecs_field_id(&it, 1)); + test_bool(false, ecs_field_is_set(&it, 1)); + test_uint(t3, ecs_iter_get_var(&it, x_var)); + test_uint(child_3, ecs_iter_get_var(&it, child_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_table_w_any_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t p1 = ecs_new_w(world, TagA); + ecs_new_w(world, TagA); + ecs_entity_t p3 = ecs_new_w(world, TagA); + ecs_entity_t p4 = ecs_new_w(world, TagA); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, p1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, p3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, p4); + ecs_add(world, child_1, TagB); + ecs_add(world, child_4, TagB); + ecs_add(world, child_4, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "TagA($x), _($x.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "x.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(p1, ecs_field_src(&it, 0)); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(p1, ecs_iter_get_var(&it, x_var)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(0, it.count); + test_uint(TagA, ecs_field_id(&it, 0)); + test_uint(p4, ecs_field_src(&it, 0)); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + test_uint(p4, ecs_iter_get_var(&it, x_var)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_from_entity_w_any_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + ecs_entity_t t1 = ecs_new(world); + ecs_entity_t t2 = ecs_new(world); + ecs_entity_t t3 = ecs_new(world); + ecs_entity_t t4 = ecs_new(world); + + ecs_entity_t p1 = ecs_new_w_pair(world, Rel, t1); + ecs_new_w_pair(world, Rel, t2); + ecs_new_w_pair(world, Rel, t3); + ecs_entity_t p4 = ecs_new_w_pair(world, Rel, t4); + + ecs_entity_t child_1 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_1, EcsChildOf, t1); + ecs_entity_t child_3 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_3, EcsChildOf, t3); + ecs_entity_t child_4 = ecs_entity(world, { .name = "child" }); + ecs_add_pair(world, child_4, EcsChildOf, t4); + ecs_add(world, child_1, TagB); + ecs_add(world, child_4, TagB); + ecs_add(world, child_4, TagC); + + ecs_query_t *q = ecs_query(world, { + .expr = "(Rel, $x), _($x.child)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int32_t x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int32_t child_var = ecs_query_find_var(q, "x.child"); + test_assert(child_var != -1); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p1, it.entities[0]); + test_uint(ecs_pair(Rel, t1), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + test_uint(child_1, ecs_field_src(&it, 1)); + test_uint(t1, ecs_iter_get_var(&it, x_var)); + test_uint(child_1, ecs_iter_get_var(&it, child_var)); + + test_bool(true, ecs_query_next(&it)); + test_uint(1, it.count); + test_uint(p4, it.entities[0]); + test_uint(ecs_pair(Rel, t4), ecs_field_id(&it, 0)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(EcsWildcard, ecs_field_id(&it, 1)); + test_uint(child_4, ecs_field_src(&it, 1)); + test_uint(t4, ecs_iter_get_var(&it, x_var)); + test_uint(child_4, ecs_iter_get_var(&it, child_var)); + + test_bool(false, ecs_query_next(&it)); + } + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_as_tag(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_entity_t parent_a = ecs_new_w(world, Foo); + ecs_entity_t parent_b = ecs_new_w(world, Foo); + ecs_entity_t parent_c = ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + ecs_entity_t tag_a = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_a, EcsChildOf, parent_a); + + ecs_entity_t tag_b = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_b, EcsChildOf, parent_b); + + ecs_entity_t tag_c = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_c, EcsChildOf, parent_c); + + ecs_entity_t a = ecs_new_w_id(world, tag_a); + ecs_entity_t c = ecs_new_w_id(world, tag_c); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, $this.Tag($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int tag_var = ecs_query_find_var(q, "this.Tag"); + test_assert(tag_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(tag_a, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(a, ecs_field_src(&it, 1)); + test_uint(a, ecs_iter_get_var(&it, x_var)); + test_uint(tag_a, ecs_iter_get_var(&it, tag_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_c, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(tag_c, ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(c, ecs_field_src(&it, 1)); + test_uint(c, ecs_iter_get_var(&it, x_var)); + test_uint(tag_c, ecs_iter_get_var(&it, tag_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_as_relationship(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Tgt); + + ecs_entity_t parent_a = ecs_new_w(world, Foo); + ecs_entity_t parent_b = ecs_new_w(world, Foo); + ecs_entity_t parent_c = ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + ecs_entity_t tag_a = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_a, EcsChildOf, parent_a); + + ecs_entity_t tag_b = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_b, EcsChildOf, parent_b); + + ecs_entity_t tag_c = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_c, EcsChildOf, parent_c); + + ecs_entity_t a = ecs_new_w_pair(world, tag_a, Tgt); + ecs_entity_t c = ecs_new_w_pair(world, tag_c, Tgt); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, $this.Tag($x, Tgt)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int tag_var = ecs_query_find_var(q, "this.Tag"); + test_assert(tag_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(tag_a, Tgt), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(a, ecs_field_src(&it, 1)); + test_uint(a, ecs_iter_get_var(&it, x_var)); + test_uint(tag_a, ecs_iter_get_var(&it, tag_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_c, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(tag_c, Tgt), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(c, ecs_field_src(&it, 1)); + test_uint(c, ecs_iter_get_var(&it, x_var)); + test_uint(tag_c, ecs_iter_get_var(&it, tag_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_as_target(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Foo); + + ecs_entity_t parent_a = ecs_new_w(world, Foo); + ecs_entity_t parent_b = ecs_new_w(world, Foo); + ecs_entity_t parent_c = ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + ecs_entity_t tag_a = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_a, EcsChildOf, parent_a); + + ecs_entity_t tag_b = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_b, EcsChildOf, parent_b); + + ecs_entity_t tag_c = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_c, EcsChildOf, parent_c); + + ecs_entity_t a = ecs_new_w_pair(world, Rel, tag_a); + ecs_entity_t c = ecs_new_w_pair(world, Rel, tag_c); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, Rel($x, $this.Tag)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int tag_var = ecs_query_find_var(q, "this.Tag"); + test_assert(tag_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, tag_a), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(a, ecs_field_src(&it, 1)); + test_uint(a, ecs_iter_get_var(&it, x_var)); + test_uint(tag_a, ecs_iter_get_var(&it, tag_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_c, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(Rel, tag_c), ecs_field_id(&it, 1)); + test_uint(0, ecs_field_src(&it, 0)); + test_uint(c, ecs_field_src(&it, 1)); + test_uint(c, ecs_iter_get_var(&it, x_var)); + test_uint(tag_c, ecs_iter_get_var(&it, tag_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_assign_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Foo); + + ecs_entity_t parent_a = ecs_new_w(world, Foo); + ecs_entity_t parent_b = ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + ecs_entity_t tag_a = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_a, EcsChildOf, parent_a); + + ecs_entity_t tag_b = ecs_entity(world, { .name = "Tag" }); + ecs_add_pair(world, tag_b, EcsChildOf, parent_b); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, $x == $this.Tag", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int tag_var = ecs_query_find_var(q, "this.Tag"); + test_assert(tag_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(tag_a, ecs_iter_get_var(&it, x_var)); + test_uint(tag_a, ecs_iter_get_var(&it, tag_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_b, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(tag_b, ecs_iter_get_var(&it, x_var)); + test_uint(tag_b, ecs_iter_get_var(&it, tag_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_eq_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Foo); + + ecs_entity_t parent_a = ecs_new_w(world, Foo); + ecs_entity_t parent_b = ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + ecs_entity_t tag_ax = ecs_entity(world, { .name = "TagX" }); + ecs_add_pair(world, tag_ax, EcsChildOf, parent_a); + ecs_entity_t tag_ay = ecs_entity(world, { .name = "TagY" }); + ecs_add_pair(world, tag_ay, EcsChildOf, parent_a); + ecs_entity_t tag_az = ecs_entity(world, { .name = "TagZ" }); + ecs_add_pair(world, tag_az, EcsChildOf, parent_a); + + ecs_entity_t tag_bx = ecs_entity(world, { .name = "TagX" }); + ecs_add_pair(world, tag_bx, EcsChildOf, parent_b); + ecs_entity_t tag_by = ecs_entity(world, { .name = "TagY" }); + ecs_add_pair(world, tag_by, EcsChildOf, parent_b); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, ChildOf($x, $this), $x == $this.TagY", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int tag_var = ecs_query_find_var(q, "this.TagY"); + test_assert(tag_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_childof(parent_a), ecs_field_id(&it, 1)); + test_uint(tag_ay, ecs_iter_get_var(&it, x_var)); + test_uint(tag_ay, ecs_iter_get_var(&it, tag_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_b, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_childof(parent_b), ecs_field_id(&it, 1)); + test_uint(tag_by, ecs_iter_get_var(&it, x_var)); + test_uint(tag_by, ecs_iter_get_var(&it, tag_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_neq_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Foo); + + ecs_entity_t parent_a = ecs_new_w(world, Foo); + ecs_entity_t parent_b = ecs_new_w(world, Foo); + ecs_new_w(world, Foo); + + ecs_entity_t tag_ax = ecs_entity(world, { .name = "TagX" }); + ecs_add_pair(world, tag_ax, EcsChildOf, parent_a); + ecs_entity_t tag_ay = ecs_entity(world, { .name = "TagY" }); + ecs_add_pair(world, tag_ay, EcsChildOf, parent_a); + ecs_entity_t tag_az = ecs_entity(world, { .name = "TagZ" }); + ecs_add_pair(world, tag_az, EcsChildOf, parent_a); + + ecs_entity_t tag_bx = ecs_entity(world, { .name = "TagX" }); + ecs_add_pair(world, tag_bx, EcsChildOf, parent_b); + ecs_entity_t tag_by = ecs_entity(world, { .name = "TagY" }); + ecs_add_pair(world, tag_by, EcsChildOf, parent_b); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo, ChildOf($x, $this), $x != $this.TagY", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + int tag_var = ecs_query_find_var(q, "this.TagY"); + test_assert(tag_var != -1); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_childof(parent_a), ecs_field_id(&it, 1)); + test_uint(tag_ax, ecs_iter_get_var(&it, x_var)); + test_uint(tag_ay, ecs_iter_get_var(&it, tag_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_a, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_childof(parent_a), ecs_field_id(&it, 1)); + test_uint(tag_az, ecs_iter_get_var(&it, x_var)); + test_uint(tag_ay, ecs_iter_get_var(&it, tag_var)); + + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(parent_b, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_childof(parent_b), ecs_field_id(&it, 1)); + test_uint(tag_bx, ecs_iter_get_var(&it, x_var)); + test_uint(tag_by, ecs_iter_get_var(&it, tag_var)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_lookup_unresolved_dependent(void) { + ecs_world_t *world = ecs_init(); + + ecs_log_set_level(-4); + ecs_query_t *r = ecs_query(world, { + .expr = "(ChildOf, $_tgt), ChildOf(flecs, $tgt.child)" + }); + + test_assert(r == NULL); + + ecs_fini(world); +} + +void Variables_check_vars_this(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(1, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(2, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + test_str("x", ecs_query_var_name(q, 1)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "*", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(1, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "_", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(1, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_var_as_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), ChildOf($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(2, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + test_str("x", ecs_query_var_name(q, 1)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_this_as_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), ChildOf($x, $this)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(3, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); // table + test_str("x", ecs_query_var_name(q, 1)); + test_str("this", ecs_query_var_name(q, 2)); // entity + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_this_w_lookup_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), ChildOf($x, $this.var)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(4, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + test_str("x", ecs_query_var_name(q, 1)); + test_str("this.var", ecs_query_var_name(q, 2)); + test_str("this", ecs_query_var_name(q, 3)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_var_w_lookup_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($y), ChildOf($x, $y.var)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(4, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + test_str("y", ecs_query_var_name(q, 1)); + test_str("x", ecs_query_var_name(q, 2)); + test_str("y.var", ecs_query_var_name(q, 3)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_anonymous_var_as_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), ChildOf($this, $_x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(2, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + test_str("_x", ecs_query_var_name(q, 1)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_wildcard_as_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), ChildOf($this, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(1, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_check_vars_any_as_tgt(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo($this), ChildOf($this, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + test_int(1, q->var_count); + test_str("this", ecs_query_var_name(q, 0)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_trivial_1_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self), ChildOf($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, e, Foo); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 1)); + test_uint(p, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_trivial_1_var(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self), Bar(self), ChildOf($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, e, Foo); + ecs_add(world, e, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 2)); + test_uint(p, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_trivial_1_var_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self), ChildOf($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set(world, e, Position, {10, 20}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_uint(p, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_trivial_1_var_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self), Velocity(self), ChildOf($this, $x)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + int x_var = ecs_query_find_var(q, "x"); + test_assert(x_var != -1); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {1, 2}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 2)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_assert(ecs_field(&it, Velocity, 1) != NULL); + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + test_uint(p, ecs_iter_get_var(&it, x_var)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_trivial_1_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self), ChildOf($this, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, e, Foo); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_trivial_1_wildcard(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self), Bar(self), ChildOf($this, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, e, Foo); + ecs_add(world, e, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 2)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_trivial_1_wildcard_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self), ChildOf($this, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set(world, e, Position, {10, 20}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_trivial_1_wildcard_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self), Velocity(self), ChildOf($this, *)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {1, 2}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, p), ecs_field_id(&it, 2)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_assert(ecs_field(&it, Velocity, 1) != NULL); + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_trivial_1_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self), ChildOf($this, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, e, Foo); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 1)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_trivial_1_any(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + ecs_query_t *q = ecs_query(world, { + .expr = "Foo(self), Bar(self), ChildOf($this, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_add(world, e, Foo); + ecs_add(world, e, Bar); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(Foo, ecs_field_id(&it, 0)); + test_uint(Bar, ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 2)); + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_1_trivial_1_any_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self), ChildOf($this, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set(world, e, Position, {10, 20}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 1)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_2_trivial_1_any_component(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_query_t *q = ecs_query(world, { + .expr = "Position(self), Velocity(self), ChildOf($this, _)", + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_entity_t p = ecs_new(world); + ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, p); + ecs_set(world, e, Position, {10, 20}); + ecs_set(world, e, Velocity, {1, 2}); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(ecs_id(Velocity), ecs_field_id(&it, 1)); + test_uint(ecs_pair(EcsChildOf, EcsWildcard), ecs_field_id(&it, 2)); + { + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + test_assert(ecs_field(&it, Velocity, 1) != NULL); + { + Velocity *v = ecs_field(&it, Velocity, 1); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + } + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +} + +void Variables_first_invalid_var_name_and_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = Tag, .first.name = "$this" } + }, + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Variables_src_invalid_var_name_and_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = Tag, .src.id = Tag, .src.name = "$this" } + }, + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + +void Variables_second_invalid_var_name_and_id(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + ecs_log_set_level(-4); + ecs_query_t *q = ecs_query(world, { + .terms = { + { .first.id = Tag, .second.id = Tag, .second.name = "$this" } + }, + .cache_kind = cache_kind + }); + + test_assert(q == NULL); + + ecs_fini(world); +} + diff --git a/vendors/flecs/test/query/src/main.c b/vendors/flecs/test/query/src/main.c new file mode 100644 index 000000000..232ecc82f --- /dev/null +++ b/vendors/flecs/test/query/src/main.c @@ -0,0 +1,10608 @@ + +/* A friendly warning from bake.test + * ---------------------------------------------------------------------------- + * This file is generated. To add/remove testcases modify the 'project.json' of + * the test project. ANY CHANGE TO THIS FILE IS LOST AFTER (RE)BUILDING! + * ---------------------------------------------------------------------------- + */ + +#include + +// Testsuite 'Validator' +void Validator_validate_1_term(void); +void Validator_validate_1_term_component(void); +void Validator_validate_2_terms(void); +void Validator_validate_3_terms(void); +void Validator_validate_3_terms_w_or(void); +void Validator_validate_4_terms_w_or_at_1(void); +void Validator_validate_1_term_wildcard(void); +void Validator_validate_1_term_any(void); +void Validator_validate_1_term_same_subj_obj(void); +void Validator_validate_1_term_acyclic_same_subj_obj(void); +void Validator_validate_1_term_acyclic_reflexive_same_subj_obj(void); +void Validator_validate_1_term_same_subj_obj_var(void); +void Validator_validate_1_term_acyclic_same_subj_obj_var(void); +void Validator_validate_1_term_acyclic_reflexive_same_subj_obj_var(void); +void Validator_validate_1_term_non_acyclic_superset(void); +void Validator_validate_1_term_dont_inherit_default_set(void); +void Validator_validate_1_term_dont_inherit_pair_default_set(void); +void Validator_validate_1_term_inherit_default_set(void); +void Validator_validate_1_term_inherit_pair_default_set(void); +void Validator_validate_1_term_override_default_set(void); +void Validator_validate_1_term_override_pair_default_set(void); +void Validator_validate_1_term_up_no_inherit(void); +void Validator_validate_1_term_up_no_inherit_pair(void); +void Validator_validate_1_term_up_override(void); +void Validator_validate_1_term_up_override_pair(void); +void Validator_validate_1_term_up_isa_no_inherit(void); +void Validator_validate_1_term_up_isa_no_inherit_pair(void); +void Validator_validate_1_term_up_isa_override(void); +void Validator_validate_1_term_up_isa_override_pair(void); +void Validator_validate_1_term_cascade_implicit_trav(void); +void Validator_validate_1_term_cascade_isa(void); +void Validator_validate_1_term_cascade_childof(void); +void Validator_validate_1_term_cascade_down(void); +void Validator_validate_1_term_optional_only(void); +void Validator_validate_1_term_transitive_pair(void); +void Validator_validate_1_variable_as_pred_only(void); +void Validator_validate_1_variable_as_pred_w_subj(void); +void Validator_validate_1_variable_as_pred_w_pair(void); +void Validator_validate_1_variable_as_subj(void); +void Validator_validate_1_variable_as_obj(void); +void Validator_validate_2_terms_or_w_dontinherit(void); +void Validator_validate_2_terms_or_w_both_dontinherit(void); +void Validator_validate_w_pair_id(void); +void Validator_validate_w_pred_obj(void); +void Validator_validate_w_pair_id_and_subj(void); +void Validator_validate_1_w_pred_name(void); +void Validator_validate_1_w_final_pred_name(void); +void Validator_validate_1_w_subj_name(void); +void Validator_validate_1_w_obj_name(void); +void Validator_validate_w_this_implicit_variable(void); +void Validator_validate_w_this_explicit_entity(void); +void Validator_validate_w_first_this_implicit_variable(void); +void Validator_validate_w_first_this_explicit_entity(void); +void Validator_validate_w_second_this_implicit_variable(void); +void Validator_validate_w_second_this_explicit_entity(void); +void Validator_validate_w_this_variable_name(void); +void Validator_validate_w_src_var(void); +void Validator_validate_w_first_var(void); +void Validator_validate_w_second_var(void); +void Validator_validate_w_src_var_from_name(void); +void Validator_validate_w_first_first_var(void); +void Validator_validate_w_second_second_var(void); +void Validator_validate_w_0_source(void); +void Validator_validate_w_0_target(void); +void Validator_validate_2_terms_w_or(void); +void Validator_validate_2_terms_w_or_mixed_src_flags(void); +void Validator_validate_2_terms_w_or_mixed_src_id(void); +void Validator_validate_2_terms_w_or_mixed_src_name(void); +void Validator_validate_2_terms_w_or_same_src_w_id_and_name(void); +void Validator_validate_w_and_flag(void); +void Validator_validate_w_or_flag(void); +void Validator_validate_w_not_flag(void); +void Validator_validate_filter(void); +void Validator_validate_double_init(void); +void Validator_validate_double_init_w_expr(void); +void Validator_validate_double_init_w_expr_optional(void); +void Validator_validate_w_tag_term_is_no_data(void); +void Validator_validate_w_inout_none_term_is_no_data(void); +void Validator_validate_w_tag_and_inout_none_term_is_no_data(void); +void Validator_validate_w_not_term_is_no_data(void); +void Validator_validate_w_no_transitive_pair(void); +void Validator_validate_w_transitive_pair_any_src(void); +void Validator_validate_w_transitive_pair(void); +void Validator_validate_w_transitive_tag_no_pair(void); +void Validator_validate_w_transitive_tag_self_tgt(void); +void Validator_validate_w_transitive_tag_any_tgt(void); +void Validator_validate_w_pair_same_vars(void); +void Validator_validate_w_pair_not_same_vars(void); +void Validator_validate_w_pair_no_vars_not_same_vars(void); +void Validator_validate_w_pair_wildcard_not_same_vars(void); +void Validator_validate_w_pair_any_not_same_vars(void); +void Validator_validate_w_no_pair_not_same_vars(void); +void Validator_validate_not_childof_any(void); +void Validator_validate_w_inherited_id(void); +void Validator_validate_w_inherited_pair(void); +void Validator_validate_w_non_inherited_id(void); +void Validator_validate_w_non_inherited_pair(void); +void Validator_validate_w_first_wildcard_inout_none(void); +void Validator_validate_w_first_var_inout_none(void); +void Validator_validate_w_pair_wildcard_inout_none(void); +void Validator_validate_w_pair_var_inout_none(void); +void Validator_validate_w_unresolved_by_name(void); +void Validator_validate_w_unresolved_by_name_eq(void); +void Validator_validate_childof_this(void); +void Validator_validate_childof_this_entity(void); +void Validator_validate_childof_this_by_id(void); +void Validator_validate_filter_flag(void); +void Validator_validate_first_0_name(void); +void Validator_validate_src_0_name(void); +void Validator_validate_second_0_name(void); +void Validator_validate_singleton_src_w_first_name(void); +void Validator_validate_singleton_second_w_first_name(void); +void Validator_not_wildcard(void); +void Validator_not_first_wildcard(void); +void Validator_not_second_wildcard(void); +void Validator_not_wildcard_id(void); +void Validator_not_wildcard_first_pair(void); +void Validator_not_wildcard_second_pair(void); +void Validator_validate_or_same_type(void); +void Validator_validate_or_different_types(void); +void Validator_validate_or_different_types(void); +void Validator_validate_or_different_types_1_and_2_or(void); +void Validator_validate_trav_isa_w_wildcard(void); +void Validator_validate_trav_isa_w_any(void); +void Validator_validate_custom_trav_w_inherit_id(void); +void Validator_validate_custom_trav_w_inherit_id_w_self_up(void); +void Validator_validate_custom_trav_w_inherit_id_w_up(void); +void Validator_validate_simple_1_term_is_cacheable(void); +void Validator_validate_simple_1_term_pair_is_cacheable(void); +void Validator_validate_simple_1_term_pair_recycled_is_cacheable(void); +void Validator_validate_simple_1_term_tag_is_cacheable(void); +void Validator_validate_simple_2_term_is_cacheable(void); +void Validator_validate_simple_w_can_inherit(void); +void Validator_validate_simple_w_can_toggle(void); +void Validator_validate_simple_w_sparse(void); +void Validator_validate_simple_w_union(void); +void Validator_validate_simple_w_union_pair(void); +void Validator_validate_simple_w_transitive(void); +void Validator_validate_simple_w_transitive_pair(void); +void Validator_validate_simple_w_reflexive(void); +void Validator_validate_simple_w_reflexive_pair(void); +void Validator_validate_simple_w_inherited_component(void); + +// Testsuite 'Parser' +void Parser_resolve_this(void); +void Parser_resolve_wildcard(void); +void Parser_resolve_any(void); +void Parser_resolve_is_a(void); +void Parser_0(void); +void Parser_component_implicit_subject(void); +void Parser_component_explicit_subject(void); +void Parser_component_explicit_subject_this(void); +void Parser_component_explicit_subject_this_by_name(void); +void Parser_component_explicit_subject_this_by_var_name(void); +void Parser_component_explicit_subject_wildcard(void); +void Parser_component_explicit_subject_any(void); +void Parser_component_explicit_subject_0(void); +void Parser_this_as_predicate(void); +void Parser_this_var_as_predicate(void); +void Parser_this_lowercase_var_as_predicate(void); +void Parser_this_as_object(void); +void Parser_this_var_as_object(void); +void Parser_pair_implicit_subject(void); +void Parser_pair_implicit_subject_wildcard_pred(void); +void Parser_pair_implicit_subject_wildcard_obj(void); +void Parser_pair_implicit_subject_any_pred(void); +void Parser_pair_implicit_subject_any_obj(void); +void Parser_pair_implicit_subject_this_pred(void); +void Parser_pair_implicit_subject_this_obj(void); +void Parser_pair_implicit_subject_pred_w_self(void); +void Parser_pair_implicit_subject_obj_w_self(void); +void Parser_pair_implicit_subject_pred_w_up(void); +void Parser_pair_implicit_subject_obj_w_up(void); +void Parser_pair_implicit_subject_pred_w_self_up(void); +void Parser_pair_implicit_subject_obj_w_self_up(void); +void Parser_pair_implicit_subject_pred_w_invalid_flags(void); +void Parser_pair_implicit_subject_obj_w_invalid_flags(void); +void Parser_pair_explicit_subject(void); +void Parser_pair_explicit_subject_this(void); +void Parser_pair_explicit_subject_this_by_name(void); +void Parser_pair_explicit_subject_this_by_var_name(void); +void Parser_pair_explicit_subject_wildcard_pred(void); +void Parser_pair_explicit_subject_wildcard_subj(void); +void Parser_pair_explicit_subject_wildcard_obj(void); +void Parser_pair_implicit_subject_0_name_object(void); +void Parser_pair_implicit_subject_0_name_not_found_object(void); +void Parser_pair_implicit_subject_0_digit_object(void); +void Parser_pair_explicit_subject_0_object(void); +void Parser_pair_explicit_subject_0(void); +void Parser_in_component_implicit_subject(void); +void Parser_in_component_explicit_subject(void); +void Parser_in_pair_implicit_subject(void); +void Parser_in_pair_explicit_subject(void); +void Parser_inout_component_implicit_subject(void); +void Parser_inout_component_explicit_subject(void); +void Parser_inout_pair_implicit_subject(void); +void Parser_inout_pair_explicit_subject(void); +void Parser_out_component_implicit_subject(void); +void Parser_out_component_explicit_subject(void); +void Parser_out_pair_implicit_subject(void); +void Parser_out_pair_explicit_subject(void); +void Parser_inout_filter_component(void); +void Parser_inout_w_not_operator(void); +void Parser_inout_w_optional_operator(void); +void Parser_component_singleton(void); +void Parser_this_singleton(void); +void Parser_component_implicit_no_subject(void); +void Parser_component_explicit_no_subject(void); +void Parser_pair_no_subject(void); +void Parser_variable_single_char(void); +void Parser_variable_multi_char(void); +void Parser_variable_multi_char_w_underscore(void); +void Parser_variable_multi_char_w_number(void); +void Parser_variable_multi_char_not_allcaps(void); +void Parser_pred_var(void); +void Parser_obj_var(void); +void Parser_component_not(void); +void Parser_pair_implicit_subject_not(void); +void Parser_pair_explicit_subject_not(void); +void Parser_2_component_not(void); +void Parser_2_component_not_no_space(void); +void Parser_component_optional(void); +void Parser_2_component_optional(void); +void Parser_2_component_optional_no_space(void); +void Parser_from_and(void); +void Parser_from_or(void); +void Parser_from_not(void); +void Parser_pair_implicit_subject_optional(void); +void Parser_pair_explicit_subject_optional(void); +void Parser_pred_implicit_subject_w_role(void); +void Parser_pred_explicit_subject_w_role(void); +void Parser_pred_no_subject_w_role(void); +void Parser_pair_implicit_subject_w_role(void); +void Parser_pair_explicit_subject_w_role(void); +void Parser_inout_role_pred_implicit_subject(void); +void Parser_inout_role_pred_no_subject(void); +void Parser_inout_role_pred_explicit_subject(void); +void Parser_inout_role_pair_implicit_subject(void); +void Parser_inout_role_pair_explicit_subject(void); +void Parser_2_pred_implicit_subject(void); +void Parser_2_pred_no_subject(void); +void Parser_2_pred_explicit_subject(void); +void Parser_2_pair_implicit_subject(void); +void Parser_2_pair_explicit_subject(void); +void Parser_2_pred_role(void); +void Parser_2_pair_implicit_subj_role(void); +void Parser_2_pair_explicit_subj_role(void); +void Parser_2_or_pred_implicit_subj(void); +void Parser_2_or_pred_explicit_subj(void); +void Parser_2_or_pair_implicit_subj(void); +void Parser_2_or_pair_explicit_subj(void); +void Parser_2_or_pred_inout(void); +void Parser_2_or_no_space(void); +void Parser_2_or_w_not_1st_arg(void); +void Parser_2_or_w_not_2nd_arg(void); +void Parser_2_or_w_optional_1st_arg(void); +void Parser_2_or_w_optional_2nd_arg(void); +void Parser_1_entity_id_pred_implicit_subj(void); +void Parser_1_entity_id_pred_no_subj(void); +void Parser_1_entity_id_pred_explicit_subj(void); +void Parser_1_entity_id_pair_implicit_subj(void); +void Parser_1_entity_id_pair_explicit_subj(void); +void Parser_1_digit_pred_implicit_subj(void); +void Parser_1_digit_pred_no_subj(void); +void Parser_1_digit_pred_explicit_subj(void); +void Parser_1_digit_pair_implicit_subj(void); +void Parser_1_digit_pair_explicit_subj(void); +void Parser_pred_implicit_subject_self(void); +void Parser_pred_implicit_subject_superset(void); +void Parser_pred_implicit_subject_superset_inclusive(void); +void Parser_pred_implicit_subject_superset_cascade(void); +void Parser_pred_implicit_subject_superset_inclusive_cascade(void); +void Parser_pred_implicit_subject_implicit_superset_cascade(void); +void Parser_pred_implicit_subject_implicit_superset_inclusive_cascade(void); +void Parser_pred_implicit_subject_implicit_superset_cascade_w_rel(void); +void Parser_pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel(void); +void Parser_pred_implicit_subject_superset_childof(void); +void Parser_pred_implicit_subject_cascade_superset_childof(void); +void Parser_pred_implicit_subject_superset_cascade_childof(void); +void Parser_pred_implicit_subject_superset_cascade_childof_optional(void); +void Parser_expr_w_symbol(void); +void Parser_expr_w_newline(void); +void Parser_subj_entity_w_explicit_self(void); +void Parser_subj_entity_w_explicit_self_superset(void); +void Parser_subj_entity_w_explicit_superset_relation(void); +void Parser_subj_entity_w_explicit_self_superset_relation(void); +void Parser_obj_entity_w_explicit_self(void); +void Parser_obj_entity_w_explicit_self_superset(void); +void Parser_obj_entity_w_explicit_superset_relation(void); +void Parser_obj_entity_w_explicit_self_superset_relation(void); +void Parser_pred_entity_w_explicit_self(void); +void Parser_pred_entity_w_explicit_self_superset(void); +void Parser_pred_entity_no_args_w_explicit_self(void); +void Parser_pred_entity_no_args_w_explicit_self_superset(void); +void Parser_pred_entity_no_args_2_terms_w_explicit_self(void); +void Parser_pred_entity_no_args_2_terms_w_explicit_self_superset(void); +void Parser_newline(void); +void Parser_2_newlines(void); +void Parser_3_newlines(void); +void Parser_space(void); +void Parser_2_spaces(void); +void Parser_trailing_newline(void); +void Parser_2_trailing_newlines(void); +void Parser_trailing_space(void); +void Parser_2_trailing_spaces(void); +void Parser_template_type(void); +void Parser_template_type_nested(void); +void Parser_template_type_multiple_args(void); +void Parser_template_type_multiple_args_nested(void); +void Parser_template_type_unbalanced_open(void); +void Parser_template_type_unbalanced_close(void); +void Parser_predicate_w_parens(void); +void Parser_not_alive_pred(void); +void Parser_not_alive_subj(void); +void Parser_not_alive_obj(void); +void Parser_this_subj_var_kind(void); +void Parser_this_obj_var_kind(void); +void Parser_this_subj_obj_var_kind(void); +void Parser_var_w_name(void); +void Parser_entity_pred_no_name(void); +void Parser_entity_subj_no_name(void); +void Parser_entity_obj_no_name(void); +void Parser_this_pred_no_name(void); +void Parser_this_subj_no_name(void); +void Parser_this_obj_no_name(void); +void Parser_invalid_variable_only(void); +void Parser_oneof_self_pred_w_relative_obj(void); +void Parser_oneof_other_pred_w_relative_obj(void); +void Parser_oneof_self_pred_w_invalid_obj(void); +void Parser_oneof_other_pred_w_invalid_obj(void); +void Parser_oneof_w_other_entity_w_same_name(void); +void Parser_oneof_w_other_entity_w_same_name_w_set_scope(void); +void Parser_oneof_w_wildcard(void); +void Parser_oneof_w_any(void); +void Parser_oneof_w_fullpath(void); +void Parser_pair_implicit_src_missing_rel(void); +void Parser_pair_implicit_src_missing_obj(void); +void Parser_pair_explicit_src_missing_src(void); +void Parser_pair_explicit_src_missing_obj(void); +void Parser_eq_id(void); +void Parser_eq_id_var(void); +void Parser_eq_var_id(void); +void Parser_eq_var_table(void); +void Parser_eq_var(void); +void Parser_neq_id(void); +void Parser_neq_id_var(void); +void Parser_neq_var_id(void); +void Parser_neq_var_table(void); +void Parser_neq_var(void); +void Parser_eq_name(void); +void Parser_eq_name_var(void); +void Parser_eq_var_name(void); +void Parser_neq_name(void); +void Parser_neq_name_var(void); +void Parser_neq_var_name(void); +void Parser_match_name(void); +void Parser_match_name_var(void); +void Parser_match_var_name(void); +void Parser_match_var(void); +void Parser_nmatch_name(void); +void Parser_nmatch_name_var(void); +void Parser_nmatch_var_name(void); +void Parser_eq_same_var(void); +void Parser_neq_same_var(void); +void Parser_eq_same_var_this(void); +void Parser_neq_same_var_this(void); +void Parser_eq_w_not(void); +void Parser_neq_w_not(void); +void Parser_match_w_not(void); +void Parser_eq_w_optional(void); +void Parser_neq_w_optional(void); +void Parser_match_w_optional(void); +void Parser_eq_0(void); +void Parser_neq_0(void); +void Parser_eq_wildcard(void); +void Parser_neq_wildcard(void); +void Parser_eq_any(void); +void Parser_neq_any(void); +void Parser_query_scope_1_term(void); +void Parser_query_scope_1_term_spaces(void); +void Parser_query_scope_2_terms(void); +void Parser_query_nested_scope(void); +void Parser_query_nested_scope_spaces(void); +void Parser_query_scope_unbalanced(void); +void Parser_query_not_scope(void); +void Parser_query_empty_scope(void); +void Parser_query_scope_newline_after_open(void); +void Parser_query_scope_newline_after_close(void); +void Parser_query_term_after_scope(void); +void Parser_override_tag(void); +void Parser_override_pair(void); +void Parser_pair_3_args(void); +void Parser_pair_3_args_implicit_this(void); +void Parser_pair_4_args(void); +void Parser_pair_4_args_implicit_this(void); +void Parser_pair_3_args_2_terms(void); +void Parser_pair_3_args_this_tgt(void); +void Parser_pair_3_args_2_terms_this_tgt(void); +void Parser_pair_3_args_2_terms_this_tgt_implicit_this(void); +void Parser_pair_3_or_args(void); +void Parser_pair_3_or_args_implicit_this(void); +void Parser_pair_4_or_args(void); +void Parser_pair_4_or_args_implicit_this(void); +void Parser_pair_3_args_w_vars(void); +void Parser_pair_4_args_w_vars(void); +void Parser_pair_3_args_w_vars_w_term_after(void); +void Parser_pair_4_args_w_vars_w_term_after(void); +void Parser_pair_or_before_and_oper(void); +void Parser_pair_and_before_or_oper(void); +void Parser_cascade_desc(void); +void Parser_newline_after_inout(void); +void Parser_newline_after_term_open(void); +void Parser_newline_after_term_src(void); +void Parser_newline_after_term_src_pair(void); +void Parser_newline_after_term_pair_comma(void); +void Parser_newline_after_term_pair_second(void); +void Parser_tag_w_space_implicit_this(void); +void Parser_tag_w_space(void); +void Parser_pair_first_w_space_implicit_this(void); +void Parser_pair_first_w_space(void); +void Parser_pair_second_w_space_implicit_this(void); +void Parser_pair_second_w_space(void); +void Parser_pair_src_w_space(void); +void Parser_empty_term(void); +void Parser_empty_term_w_space(void); +void Parser_empty_term_w_newline(void); +void Parser_mixed_1_desc_and_1_expr(void); +void Parser_mixed_2_desc_and_1_expr(void); +void Parser_mixed_1_desc_and_2_expr(void); +void Parser_not_wildcard(void); +void Parser_not_first_wildcard(void); +void Parser_not_second_wildcard(void); +void Parser_unterminated_paren(void); +void Parser_unterminated_after_first_paren(void); +void Parser_unterminated_after_src_var(void); +void Parser_eq_name_existing_entity(void); +void Parser_match_existing_entity(void); +void Parser_match_wildcard(void); +void Parser_match_any(void); +void Parser_match_variable_oper(void); +void Parser_escaped_identifier(void); +void Parser_escaped_identifier_first(void); +void Parser_escaped_identifier_second(void); +void Parser_n_tokens_test(void); + +// Testsuite 'Basic' +void Basic_setup(void); +void Basic_0_query(void); +void Basic_1_fact_w_tag(void); +void Basic_1_fact_w_component(void); +void Basic_1_fact_w_tag_pair(void); +void Basic_1_fact_w_component_pair(void); +void Basic_2_facts_same_src_w_tag(void); +void Basic_2_facts_same_src_w_component(void); +void Basic_2_facts_same_src_w_tag_pair(void); +void Basic_2_facts_same_src_w_component_pair(void); +void Basic_2_facts_other_src_w_tag(void); +void Basic_2_facts_other_src_w_component(void); +void Basic_2_facts_other_src_w_tag_pair(void); +void Basic_2_facts_other_src_w_component_pair(void); +void Basic_1_fact_w_any(void); +void Basic_1_fact_w_pair_any_tgt(void); +void Basic_1_fact_w_pair_any_rel(void); +void Basic_1_fact_w_pair_any_rel_tgt(void); +void Basic_2_facts_same_src_w_any(void); +void Basic_2_facts_same_src_w_pair_any_tgt(void); +void Basic_2_facts_same_src_w_pair_any_rel(void); +void Basic_2_facts_same_src_w_pair_any_rel_tgt(void); +void Basic_2_facts_other_src_w_any(void); +void Basic_2_facts_other_src_w_pair_any_tgt(void); +void Basic_2_facts_other_src_w_pair_any_rel(void); +void Basic_2_facts_other_src_w_pair_any_rel_tgt(void); +void Basic_1_this_src_w_tag(void); +void Basic_1_this_src_w_component(void); +void Basic_1_this_src_w_tag_pair(void); +void Basic_1_this_src_w_component_pair(void); +void Basic_1_this_src_w_tag_2_tables(void); +void Basic_1_this_src_w_component_2_tables(void); +void Basic_1_this_src_w_tag_pair_2_tables(void); +void Basic_1_this_src_w_component_pair_2_tables(void); +void Basic_2_this_src_w_tag(void); +void Basic_2_this_src_w_component(void); +void Basic_2_this_src_ent_src_w_tag(void); +void Basic_2_this_src_ent_src_w_component(void); +void Basic_2_ent_src_this_src_w_tag(void); +void Basic_2_ent_src_this_src_w_component(void); +void Basic_recycled_tag(void); +void Basic_recycled_src(void); +void Basic_recycled_pair_rel(void); +void Basic_recycled_pair_tgt(void); +void Basic_this_src_w_wildcard(void); +void Basic_this_src_w_pair_rel_wildcard(void); +void Basic_this_src_w_pair_tgt_wildcard(void); +void Basic_this_src_w_pair_rel_tgt_wildcard(void); +void Basic_this_src_w_any(void); +void Basic_this_src_w_any_written(void); +void Basic_this_src_w_pair_rel_any(void); +void Basic_this_src_w_pair_tgt_any(void); +void Basic_this_src_w_pair_tgt_any_n_tgts(void); +void Basic_this_src_w_pair_tgt_any_n_tgts_written(void); +void Basic_this_src_w_pair_rel_tgt_any(void); +void Basic_ent_src_w_wildcard(void); +void Basic_ent_src_w_pair_rel_wildcard(void); +void Basic_ent_src_w_pair_tgt_wildcard(void); +void Basic_ent_src_w_pair_rel_tgt_wildcard(void); +void Basic_1_wildcard_src(void); +void Basic_1_wildcard_src_w_pair(void); +void Basic_2_wildcard_src(void); +void Basic_2_wildcard_src_w_pair(void); +void Basic_1_wildcard_src_w_pair_tgt_var(void); +void Basic_1_wildcard_src_w_pair_rel_var(void); +void Basic_1_wildcard_src_w_pair_tgt_this(void); +void Basic_1_wildcard_src_w_pair_rel_this(void); +void Basic_1_any_src(void); +void Basic_1_any_src_w_pair(void); +void Basic_2_any_src(void); +void Basic_2_any_src_w_pair(void); +void Basic_1_any_src_w_pair_tgt_var(void); +void Basic_1_any_src_w_pair_rel_var(void); +void Basic_1_any_src_w_pair_tgt_this(void); +void Basic_1_any_src_w_pair_rel_this(void); +void Basic_not_any(void); +void Basic_rule_w_iter_next(void); +void Basic_empty_rule(void); +void Basic_invalid_rule(void); +void Basic_instanced_table_src(void); +void Basic_instanced_entity_src(void); +void Basic_instanced_mixed_src(void); +void Basic_instanced_inherited(void); +void Basic_in_term(void); +void Basic_out_term(void); +void Basic_inout_term(void); +void Basic_nodata_term(void); +void Basic_find_this_lowercase(void); +void Basic_find_this_uppercase(void); +void Basic_find_this_tgt_lowercase(void); +void Basic_find_this_tgt_uppercase(void); +void Basic_get_filter(void); +void Basic_iter_empty_source(void); +void Basic_iter_empty_source_2_terms(void); +void Basic_iter_empty_source_wildcard(void); +void Basic_iter_empty_source_pair(void); +void Basic_iter_empty_source_pair_wildcard(void); +void Basic_iter_empty_source_2_terms_pair(void); +void Basic_iter_empty_source_2_terms_mixed(void); +void Basic_iter_empty_source_2_terms_mixed_pair(void); +void Basic_iter_empty_source_2_terms_mixed_pair_wildcard(void); +void Basic_this_var_w_empty_entity(void); +void Basic_match_disabled(void); +void Basic_match_prefab(void); +void Basic_match_disabled_prefab(void); +void Basic_match_disabled_this_tgt(void); +void Basic_match_prefab_this_tgt(void); +void Basic_match_disabled_prefab_this_tgt(void); +void Basic_match_self_disabled(void); +void Basic_match_self_prefab(void); +void Basic_match_self_disabled_prefab(void); +void Basic_match_optional_disabled(void); +void Basic_match_optional_prefab(void); +void Basic_match_optional_disabled_prefab(void); +void Basic_match_optional_disabled_this_tgt(void); +void Basic_match_optional_prefab_this_tgt(void); +void Basic_match_optional_disabled_prefab_this_tgt(void); +void Basic_match_optional_self_disabled(void); +void Basic_match_optional_self_prefab(void); +void Basic_match_optional_self_disabled_prefab(void); +void Basic_inout_none_first_term(void); +void Basic_inout_none_first_term_self_up(void); +void Basic_inout_none_second_term(void); +void Basic_inout_none_second_term_self_up(void); +void Basic_inout_none_singleton(void); +void Basic_inout_none_singleton_w_or(void); +void Basic_inout_none_component_w_or(void); +void Basic_no_data_rule(void); +void Basic_frame_offset(void); +void Basic_frame_offset_no_data(void); +void Basic_match_empty_tables(void); +void Basic_match_empty_tables_no_data(void); +void Basic_match_empty_tables_w_not(void); +void Basic_match_empty_tables_w_wildcard(void); +void Basic_match_empty_tables_w_no_empty_tables(void); +void Basic_match_empty_tables_trivial(void); +void Basic_oneof_wildcard(void); +void Basic_oneof_any(void); +void Basic_instanced_w_singleton(void); +void Basic_instanced_w_base(void); +void Basic_unknown_before_known(void); +void Basic_unknown_before_known_after_or(void); +void Basic_unknown_before_known_after_not(void); +void Basic_unknown_before_known_after_optional(void); +void Basic_unknown_before_known_after_scope(void); +void Basic_2_trivial_mixed_2_tables(void); +void Basic_2_trivial_mixed_2_tables_component(void); +void Basic_2_trivial_mixed_2_tables_wildcard(void); +void Basic_2_trivial_1_unused_id(void); +void Basic_2_trivial_one_regular(void); +void Basic_1_trivial_one_regular_one_trivial(void); +void Basic_one_regular_2_trivial(void); +void Basic_2_trivial_w_prefab(void); +void Basic_3_trivial_w_prefab(void); +void Basic_2_trivial_w_disabled(void); +void Basic_3_trivial_w_disabled(void); +void Basic_2_this_w_fixed_src(void); +void Basic_2_fixed_src_w_this(void); +void Basic_2_this_w_fixed_src_no_match_fixed(void); +void Basic_2_fixed_src_w_this_no_match_fixed(void); +void Basic_2_this_w_fixed_src_no_match_this(void); +void Basic_2_fixed_src_w_this_no_match_this(void); +void Basic_query_count_results(void); +void Basic_query_count_entities(void); +void Basic_query_is_true(void); +void Basic_implicit_cleanup_1_term(void); +void Basic_implicit_cleanup_2_terms(void); +void Basic_implicit_cleanup_1_term_w_up(void); +void Basic_implicit_cleanup_2_terms_w_up(void); +void Basic_implicit_cleanup_2_queries(void); +void Basic_implicit_cleanup_2_queries_1_cleanup(void); +void Basic_iter_valid(void); +void Basic_iter_frame_offset(void); +void Basic_iter_nested_1(void); +void Basic_iter_nested_2(void); +void Basic_iter_interleaved(void); +void Basic_set_get_context(void); +void Basic_set_get_binding_context(void); +void Basic_set_get_context_w_free(void); +void Basic_set_get_binding_context_w_free(void); +void Basic_create_query_w_existing_entity(void); +void Basic_create_query_w_existing_entity_different_term_count(void); +void Basic_create_multi_component_query_w_existing_entity(void); +void Basic_delete_query_by_entity(void); +void Basic_eval_count(void); +void Basic_no_results_after_delete_tree(void); +void Basic_no_results_after_delete_tree_deferred(void); +void Basic_add_on_self_ref(void); +void Basic_add_on_self_ref_by_name(void); +void Basic_delete_id_after_delete_query(void); +void Basic_pair_sweep_tag(void); +void Basic_pair_sweep_wildcard_first(void); +void Basic_pair_sweep_wildcard_second(void); +void Basic_create_w_entity_deferred(void); +void Basic_32_terms(void); +void Basic_33_terms_expr(void); +void Basic_stage_query(void); +void Basic_world_query_w_stage_iter(void); +void Basic_stage_query_w_nth_stage(void); +void Basic_world_query_w_nth_stage_iter(void); +void Basic_match_empty(void); +void Basic_match_new_empty(void); +void Basic_match_empty_w_component(void); +void Basic_match_new_empty_w_component(void); +void Basic_match_empty_w_ref(void); +void Basic_match_new_empty_w_ref(void); +void Basic_match_empty_w_order_by(void); +void Basic_match_new_empty_w_order_by(void); +void Basic_match_empty_w_bitset(void); +void Basic_default_query_flags(void); +void Basic_ref_fields_this(void); +void Basic_ref_fields_static_src(void); +void Basic_ref_fields_variable_src(void); +void Basic_ref_fields_up_src(void); +void Basic_ref_fields_self_up_src(void); +void Basic_0_src_match_nothing(void); +void Basic_0_terms_match_nothing(void); +void Basic_any_record(void); +void Basic_pair_rel_any_record(void); +void Basic_pair_tgt_any_record(void); +void Basic_pair_any_any_record(void); +void Basic_written_any_record(void); +void Basic_written_pair_rel_any_record(void); +void Basic_written_pair_tgt_any_record(void); +void Basic_written_pair_any_any_record(void); +void Basic_pair_rel_any_record_component(void); +void Basic_pair_tgt_any_record_component(void); +void Basic_entity_iteration_w_match_empty_tables(void); +void Basic_get_cache_query_uncached(void); +void Basic_get_cache_query_cached(void); +void Basic_get_cache_query_partially_cached(void); + +// Testsuite 'Combinations' +void Combinations_setup(void); +void Combinations_singleton_and_self_and_self(void); +void Combinations_self_and_singleton_and_self(void); +void Combinations_self_and_self_and_singleton(void); +void Combinations_singleton_and_trav_and_self(void); +void Combinations_trav_and_singleton_and_self(void); +void Combinations_self_and_singleton_and_trav(void); +void Combinations_self_and_trav_and_singleton(void); +void Combinations_singleton_and_self_and_trav(void); +void Combinations_trav_and_self_and_singleton(void); + +// Testsuite 'Plan' +void Plan_reordered_plan_1(void); +void Plan_reordered_plan_2(void); +void Plan_reordered_plan_3(void); +void Plan_reordered_plan_4(void); +void Plan_reordered_plan_5(void); +void Plan_reordered_plan_6(void); +void Plan_reordered_plan_7(void); +void Plan_1_trivial_plan(void); +void Plan_2_trivial_plan(void); +void Plan_1_trivial_plan_component(void); +void Plan_2_trivial_plan_component(void); +void Plan_3_trivial_plan_w_pair(void); +void Plan_3_trivial_plan_w_wildcard(void); +void Plan_3_trivial_plan_w_any(void); +void Plan_3_trivial_plan_w_pair_component(void); +void Plan_3_trivial_plan_w_wildcard_component(void); +void Plan_3_trivial_plan_w_any_component(void); +void Plan_1_trivial_component_w_none(void); +void Plan_2_trivial_component_w_none(void); +void Plan_2_trivial_plan_w_wildcard(void); +void Plan_this_before_fixed_src(void); +void Plan_fixed_src_before_this(void); +void Plan_var_before_fixed_src(void); +void Plan_fixed_src_before_var(void); +void Plan_this_before_fixed_src_w_not(void); +void Plan_this_before_fixed_src_w_first_var(void); +void Plan_this_before_fixed_src_w_first_var_w_not(void); +void Plan_this_before_fixed_src_w_second_var(void); +void Plan_this_before_fixed_src_w_second_var_w_not(void); +void Plan_populate_1_fixed(void); +void Plan_populate_2_fixed(void); +void Plan_populate_1_fixed_1_this_self(void); +void Plan_populate_2_fixed_2_this_self(void); +void Plan_populate_2_fixed_2_this_self_interleaved(void); +void Plan_populate_2_this_self_2_fixed(void); +void Plan_populate_1_fixed_1_this_up(void); +void Plan_populate_2_fixed_2_this_up(void); +void Plan_populate_2_fixed_2_this_up_interleaved(void); +void Plan_populate_2_this_up_2_fixed(void); +void Plan_populate_1_fixed_1_this_self_cached(void); +void Plan_populate_2_fixed_2_this_self_cached(void); +void Plan_populate_2_fixed_2_this_self_interleaved_cached(void); +void Plan_populate_2_this_self_2_fixed_cached(void); +void Plan_populate_1_fixed_1_this_up_cached(void); +void Plan_populate_2_fixed_2_this_up_cached(void); +void Plan_populate_2_fixed_2_this_up_interleaved_cached(void); +void Plan_populate_2_this_up_2_fixed_cached(void); +void Plan_populate_1_fixed_1_var_self(void); +void Plan_populate_2_fixed_2_var_self(void); +void Plan_populate_2_fixed_2_var_self_interleaved(void); +void Plan_populate_2_var_self_2_fixed(void); +void Plan_populate_1_fixed_1_var_up(void); +void Plan_populate_2_fixed_2_var_up(void); +void Plan_populate_2_fixed_2_var_up_interleaved(void); +void Plan_populate_2_var_up_2_fixed(void); +void Plan_cache_2_or(void); +void Plan_cache_2_or_w_not(void); +void Plan_1_plan_any_src(void); +void Plan_1_plan_not_any_src(void); +void Plan_1_plan_optional_any_src(void); +void Plan_pair_first_wildcard(void); +void Plan_pair_first_wildcard_cached(void); +void Plan_pair_second_wildcard(void); +void Plan_pair_second_wildcard_cached(void); +void Plan_0_src_tag(void); +void Plan_0_src_component(void); +void Plan_0_src_w_sparse(void); +void Plan_0_src_w_toggle(void); +void Plan_0_src_w_union(void); +void Plan_0_src_w_sparse_and_component(void); +void Plan_0_src_w_toggle_and_component(void); +void Plan_0_src_w_union_and_component(void); +void Plan_cached_isa_tgt(void); +void Plan_cached_isa_tgt_w_self_second(void); +void Plan_cached_isa_tgt_no_expr(void); +void Plan_cached_isa_tgt_w_self_second_no_expr(void); +void Plan_cached_w_not_and_uncacheable(void); +void Plan_cached_w_optional_and_uncacheable(void); +void Plan_cached_w_not_optional_and_uncacheable(void); + +// Testsuite 'Variables' +void Variables_setup(void); +void Variables_1_ent_src_w_var(void); +void Variables_1_ent_src_w_pair_rel_var(void); +void Variables_1_ent_src_w_pair_tgt_var(void); +void Variables_1_ent_src_w_pair_rel_tgt_var(void); +void Variables_1_ent_src_w_pair_rel_tgt_same_var(void); +void Variables_1_ent_src_w_pair_rel_tgt_same_var_after_write(void); +void Variables_1_this_src_w_var(void); +void Variables_1_this_src_w_pair_rel_var(void); +void Variables_1_this_src_w_pair_tgt_var(void); +void Variables_1_this_src_w_pair_rel_tgt_var(void); +void Variables_1_this_src_w_pair_rel_tgt_same_var(void); +void Variables_1_this_src_w_pair_rel_tgt_same_var_after_write(void); +void Variables_1_src_id_same_var(void); +void Variables_1_src_pair_first_same_var(void); +void Variables_1_src_pair_second_same_var(void); +void Variables_1_src_pair_first_and_second_same_var(void); +void Variables_1_src_id_same_var_after_write(void); +void Variables_1_src_pair_first_same_var_after_write(void); +void Variables_1_src_pair_second_same_var_after_write(void); +void Variables_1_src_pair_first_and_second_same_var_after_write(void); +void Variables_1_src_pair_first_same_var_this(void); +void Variables_1_src_pair_second_same_var_this(void); +void Variables_1_src_pair_first_and_second_same_var_this(void); +void Variables_1_src_id_same_var_this_after_write(void); +void Variables_1_src_pair_first_same_var_this_after_write(void); +void Variables_1_src_pair_second_same_var_this_after_write(void); +void Variables_1_src_pair_first_and_second_same_var_this_after_write(void); +void Variables_1_ent_src_w_this_var(void); +void Variables_1_ent_src_w_pair_this_rel(void); +void Variables_1_ent_src_w_pair_this_tgt(void); +void Variables_1_ent_src_w_pair_this_rel_tgt(void); +void Variables_1_this_src_w_this(void); +void Variables_1_this_src_w_pair_this_rel_tgt(void); +void Variables_1_this_src_w_this_after_write(void); +void Variables_1_this_src_w_pair_this_rel_tgt_after_write(void); +void Variables_2_constrain_src_from_src(void); +void Variables_2_constrain_rel_from_src_w_ent(void); +void Variables_2_constrain_rel_from_src_w_var(void); +void Variables_2_constrain_rel_from_src_w_this(void); +void Variables_2_constrain_pair_rel_from_src_w_ent(void); +void Variables_2_constrain_pair_rel_from_src_w_var(void); +void Variables_2_constrain_pair_rel_from_src_w_this(void); +void Variables_2_constrain_pair_tgt_from_src_w_ent(void); +void Variables_2_constrain_pair_tgt_from_src_w_var(void); +void Variables_2_constrain_pair_tgt_from_src_w_this(void); +void Variables_2_constrain_pair_rel_tgt_from_src_w_ent(void); +void Variables_2_constrain_pair_rel_tgt_from_src_w_var(void); +void Variables_2_constrain_pair_rel_tgt_from_src_w_this(void); +void Variables_1_ent_src_set_rel_var(void); +void Variables_1_ent_src_set_pair_rel_var(void); +void Variables_1_ent_src_set_pair_tgt_var(void); +void Variables_1_set_src_var(void); +void Variables_1_set_src_var_w_pair(void); +void Variables_1_set_src_var_w_pair_set_rel(void); +void Variables_1_set_src_var_w_pair_set_tgt(void); +void Variables_1_set_src_var_w_pair_set_rel_tgt(void); +void Variables_1_set_src_this(void); +void Variables_1_set_src_this_w_pair(void); +void Variables_1_set_src_this_w_pair_set_rel(void); +void Variables_1_set_src_this_w_pair_set_tgt(void); +void Variables_1_set_src_this_w_pair_set_rel_tgt(void); +void Variables_1_set_src_this_to_empty_table(void); +void Variables_1_set_src_this_to_empty_table_w_component(void); +void Variables_1_set_src_this_to_empty_table_w_component_self(void); +void Variables_1_set_src_this_to_entity_in_table(void); +void Variables_1_set_src_this_to_entity_in_table_self(void); +void Variables_2_set_src_this(void); +void Variables_2_set_src_this_self(void); +void Variables_2_set_src_this_component(void); +void Variables_2_set_src_this_self_component(void); +void Variables_2_set_src_this_w_up(void); +void Variables_2_set_src_this_self_w_up(void); +void Variables_2_set_src_this_component_w_up(void); +void Variables_2_set_src_this_self_component_w_up(void); +void Variables_2_set_src_this_w_exclusive_wildcard(void); +void Variables_2_set_src_this_self_w_exclusive_wildcard(void); +void Variables_2_set_src_this_self_w_exclusive_wildcard_w_up(void); +void Variables_1_set_src_this_is_true(void); +void Variables_3_set_src_this_w_uncacheable_tag_tag_tag(void); +void Variables_3_set_src_this_w_uncacheable_tag_component_tag(void); +void Variables_3_set_src_this_w_uncacheable_tag_component_component(void); +void Variables_3_set_src_this_w_tag_uncacheable_tag_tag(void); +void Variables_3_set_src_this_w_component_uncacheable_tag_tag(void); +void Variables_3_set_src_this_w_component_uncacheable_tag_component(void); +void Variables_3_set_src_this_w_tag_tag_uncacheable_tag(void); +void Variables_3_set_src_this_w_component_tag_uncacheable_tag(void); +void Variables_3_set_src_this_w_component_component_uncacheable_tag(void); +void Variables_3_set_src_this_w_uncacheable_component_tag_tag(void); +void Variables_3_set_src_this_w_uncacheable_component_component_tag(void); +void Variables_3_set_src_this_w_uncacheable_component_component_component(void); +void Variables_3_set_src_this_w_tag_uncacheable_component_tag(void); +void Variables_3_set_src_this_w_component_uncacheable_component_tag(void); +void Variables_3_set_src_this_w_component_uncacheable_component_component(void); +void Variables_3_set_src_this_w_tag_tag_uncacheable_component(void); +void Variables_3_set_src_this_w_component_tag_uncacheable_component(void); +void Variables_3_set_src_this_w_component_component_uncacheable_component(void); +void Variables_2_set_src_this_w_wildcard(void); +void Variables_1_src_this_var_as_entity(void); +void Variables_1_src_this_var_as_table(void); +void Variables_1_src_this_var_as_table_range(void); +void Variables_1_set_src_this_w_any(void); +void Variables_1_set_src_this_w_any_fixed(void); +void Variables_1_set_src_this_w_fixed_any(void); +void Variables_2_join_by_rel_var(void); +void Variables_2_join_by_pair_rel_var(void); +void Variables_2_join_by_pair_tgt_var(void); +void Variables_2_cycle_w_var(void); +void Variables_2_cycle_w_this_var(void); +void Variables_2_cycle_w_var_this(void); +void Variables_2_cycle_pair_w_var(void); +void Variables_2_cycle_pair_w_this_var_var(void); +void Variables_2_cycle_pair_w_var_this_var(void); +void Variables_2_cycle_pair_w_var_var_this(void); +void Variables_2_cycle_pair_ent_var_var(void); +void Variables_2_cycle_pair_ent_this_var(void); +void Variables_2_cycle_pair_ent_var_this(void); +void Variables_parse_0_var(void); +void Variables_parse_1_var(void); +void Variables_parse_2_vars(void); +void Variables_parse_0_var_paren(void); +void Variables_parse_1_var_paren(void); +void Variables_parse_2_vars_paren(void); +void Variables_parse_1_vars_w_path(void); +void Variables_parse_missing_close_paren(void); +void Variables_parse_missing_open_paren(void); +void Variables_parse_missing_value(void); +void Variables_parse_0_var_w_spaces(void); +void Variables_parse_1_var_w_spaces(void); +void Variables_parse_2_vars_w_spaces(void); +void Variables_parse_0_var_paren_w_spaces(void); +void Variables_parse_1_var_paren_w_spaces(void); +void Variables_parse_2_vars_paren_w_spaces(void); +void Variables_var_count(void); +void Variables_var_name(void); +void Variables_var_is_entity(void); +void Variables_no_this_anonymous_src(void); +void Variables_no_this_anonymous_src_w_pair(void); +void Variables_no_this_anonymous_component_src(void); +void Variables_no_this_anonymous_component_src_w_pair(void); +void Variables_lookup_from_table_this(void); +void Variables_lookup_from_entity_this(void); +void Variables_lookup_from_table(void); +void Variables_lookup_from_entity(void); +void Variables_lookup_from_not_written(void); +void Variables_lookup_from_table_this_component(void); +void Variables_lookup_from_entity_this_component(void); +void Variables_lookup_from_table_component(void); +void Variables_lookup_from_entity_component(void); +void Variables_lookup_from_table_two_children(void); +void Variables_lookup_from_entity_two_children(void); +void Variables_lookup_from_table_same_child_twice(void); +void Variables_lookup_from_entity_same_child_twice(void); +void Variables_lookup_from_table_not(void); +void Variables_lookup_from_entity_not(void); +void Variables_lookup_from_table_w_any_component(void); +void Variables_lookup_from_entity_w_any_component(void); +void Variables_lookup_as_tag(void); +void Variables_lookup_as_relationship(void); +void Variables_lookup_as_target(void); +void Variables_lookup_assign_var(void); +void Variables_lookup_eq_var(void); +void Variables_lookup_neq_var(void); +void Variables_lookup_unresolved_dependent(void); +void Variables_check_vars_this(void); +void Variables_check_vars_var(void); +void Variables_check_vars_wildcard(void); +void Variables_check_vars_any(void); +void Variables_check_vars_var_as_tgt(void); +void Variables_check_vars_this_as_tgt(void); +void Variables_check_vars_anonymous_var_as_tgt(void); +void Variables_check_vars_wildcard_as_tgt(void); +void Variables_check_vars_any_as_tgt(void); +void Variables_check_vars_this_w_lookup_var(void); +void Variables_check_vars_var_w_lookup_var(void); +void Variables_1_trivial_1_var(void); +void Variables_2_trivial_1_var(void); +void Variables_1_trivial_1_var_component(void); +void Variables_2_trivial_1_var_component(void); +void Variables_1_trivial_1_wildcard(void); +void Variables_2_trivial_1_wildcard(void); +void Variables_1_trivial_1_wildcard_component(void); +void Variables_2_trivial_1_wildcard_component(void); +void Variables_1_trivial_1_any(void); +void Variables_2_trivial_1_any(void); +void Variables_1_trivial_1_any_component(void); +void Variables_2_trivial_1_any_component(void); +void Variables_first_invalid_var_name_and_id(void); +void Variables_src_invalid_var_name_and_id(void); +void Variables_second_invalid_var_name_and_id(void); + +// Testsuite 'Operators' +void Operators_setup(void); +void Operators_2_and_not(void); +void Operators_2_and_not_component(void); +void Operators_2_and_out_not(void); +void Operators_2_and_out_not_component(void); +void Operators_3_and_not_not(void); +void Operators_2_and_not_pair_rel_wildcard(void); +void Operators_2_and_not_pair_tgt_wildcard(void); +void Operators_2_and_not_pair_rel_tgt_wildcard(void); +void Operators_2_and_not_pair_rel_var(void); +void Operators_2_and_not_pair_tgt_var(void); +void Operators_2_and_not_pair_rel_tgt_var(void); +void Operators_2_and_not_pair_rel_tgt_same_var(void); +void Operators_2_and_not_pair_rel_var_written(void); +void Operators_2_and_not_pair_tgt_var_written(void); +void Operators_2_and_not_pair_rel_tgt_var_written(void); +void Operators_2_and_not_pair_rel_tgt_same_var_written(void); +void Operators_2_and_not_pair_rel_src_tgt_same_var_written(void); +void Operators_2_and_not_pair_any_rel(void); +void Operators_2_and_not_pair_any_tgt(void); +void Operators_2_and_not_pair_any_src(void); +void Operators_1_not_any_src_fixed_first(void); +void Operators_1_not_any_src_any_tgt_fixed_first(void); +void Operators_1_not_any_src_any_first_fixed_tgt(void); +void Operators_1_not_any_src_any_childof_pair_any_tgt(void); +void Operators_1_not_any_src_any_isa_pair_any_tgt(void); +void Operators_1_not_match_prefab(void); +void Operators_1_not_match_disabled(void); +void Operators_1_not_match_not_queryable(void); +void Operators_1_not_match_prefab_w_match_prefab_flag(void); +void Operators_1_not_match_disabled_w_match_disabled_flag(void); +void Operators_1_not_match_disabled_w_match_prefab_disabled_flag(void); +void Operators_2_and_optional(void); +void Operators_3_and_optional_optional(void); +void Operators_2_and_optional_pair_rel_wildcard(void); +void Operators_2_and_optional_pair_tgt_wildcard(void); +void Operators_2_and_optional_pair_rel_var(void); +void Operators_2_and_optional_pair_tgt_var(void); +void Operators_2_and_optional_pair_rel_tgt_var(void); +void Operators_2_and_optional_pair_rel_tgt_same_var(void); +void Operators_2_and_optional_pair_rel_var_written(void); +void Operators_2_and_optional_pair_tgt_var_written(void); +void Operators_2_and_optional_pair_rel_tgt_var_written(void); +void Operators_2_and_optional_pair_rel_tgt_same_var_written(void); +void Operators_2_and_optional_pair_rel_src_tgt_same_var_written(void); +void Operators_3_and_optional_optional_pair_w_var(void); +void Operators_2_and_optional_pair_any_rel(void); +void Operators_2_and_optional_pair_any_tgt(void); +void Operators_2_and_optional_pair_any_src(void); +void Operators_3_and_optional_dependent_and_pair_rel(void); +void Operators_3_and_optional_dependent_and_pair_tgt(void); +void Operators_3_and_optional_dependent_and_pair_rel_tgt(void); +void Operators_3_and_optional_dependent_and_pair_rel_tgt_same_var(void); +void Operators_3_and_optional_dependent_and_pair_rel_tgt_same_other_var(void); +void Operators_3_and_optional_dependent_and_pair_src(void); +void Operators_3_and_optional_dependent_optional_pair_rel(void); +void Operators_3_and_optional_dependent_optional_pair_tgt(void); +void Operators_3_and_optional_dependent_optional_pair_src(void); +void Operators_3_and_optional_dependent_not_pair_rel(void); +void Operators_3_and_optional_dependent_not_pair_tgt(void); +void Operators_3_and_optional_dependent_not_pair_src(void); +void Operators_1_optional_any_src_fixed_first(void); +void Operators_1_optional_any_src_any_tgt_fixed_first(void); +void Operators_1_optional_any_src_any_first_fixed_tgt(void); +void Operators_1_optional_any_src_any_childof_pair_any_tgt(void); +void Operators_1_optional_any_src_any_isa_pair_any_tgt(void); +void Operators_2_or(void); +void Operators_3_or(void); +void Operators_2_or_w_and(void); +void Operators_and_w_2_or_w_and(void); +void Operators_and_w_2_or_w_and_components(void); +void Operators_and_w_2_or_w_and_set_this(void); +void Operators_and_w_2_or_w_and_components_set_this(void); +void Operators_3_or_w_and(void); +void Operators_2_or_written(void); +void Operators_3_or_written(void); +void Operators_2_or_written_w_rel_var(void); +void Operators_3_or_written_w_rel_var(void); +void Operators_2_or_written_w_tgt_var(void); +void Operators_2_or_written_w_rel_tgt_var(void); +void Operators_2_or_written_w_rel_tgt_same_var(void); +void Operators_3_or_written_w_tgt_var(void); +void Operators_2_or_chains(void); +void Operators_2_or_chains_written(void); +void Operators_2_or_dependent(void); +void Operators_2_or_dependent_reverse(void); +void Operators_2_or_dependent_2_vars(void); +void Operators_2_or_written_dependent(void); +void Operators_2_or_written_dependent_2_vars(void); +void Operators_2_or_w_dependent(void); +void Operators_2_or_w_both(void); +void Operators_3_or_w_both(void); +void Operators_2_or_w_not(void); +void Operators_2_or_w_not_component(void); +void Operators_2_or_w_not_out_component(void); +void Operators_2_or_w_not_out_all_components(void); +void Operators_2_not_first(void); +void Operators_2_optional_first(void); +void Operators_only_not(void); +void Operators_only_not_component(void); +void Operators_only_not_out_component(void); +void Operators_only_optional(void); +void Operators_only_optional_component(void); +void Operators_not_after_fixed_src(void); +void Operators_optional_after_fixed_src(void); +void Operators_root_entities_empty(void); +void Operators_root_entities(void); +void Operators_root_entities_w_children(void); +void Operators_root_entities_w_optional_children(void); +void Operators_core_entities_w_optional_children(void); +void Operators_root_entities_w_not_children(void); +void Operators_core_entities_w_not_children(void); +void Operators_1_ent_src_not(void); +void Operators_1_ent_src_not_pair(void); +void Operators_1_ent_src_not_pair_rel_wildcard(void); +void Operators_1_ent_src_not_pair_tgt_wildcard(void); +void Operators_1_ent_src_not_pair_rel_tgt_wildcard(void); +void Operators_1_ent_src_not_pair_rel_any(void); +void Operators_1_ent_src_not_pair_tgt_any(void); +void Operators_1_ent_src_not_pair_rel_tgt_any(void); +void Operators_1_ent_src_not_pair_rel_var(void); +void Operators_1_ent_src_not_pair_tgt_var(void); +void Operators_1_ent_src_not_pair_rel_tgt_var(void); +void Operators_1_ent_src_not_pair_rel_tgt_same_var(void); +void Operators_1_this_src_not_pair_rel_var(void); +void Operators_1_this_src_not_pair_tgt_var(void); +void Operators_1_this_src_not_pair_rel_tgt_var(void); +void Operators_1_this_src_not_pair_rel_tgt_same_var(void); +void Operators_1_ent_src_not_pair_rel_var_written(void); +void Operators_1_ent_src_not_pair_tgt_var_written(void); +void Operators_1_ent_src_not_pair_rel_tgt_var_written(void); +void Operators_1_ent_src_not_pair_rel_tgt_same_var_written(void); +void Operators_and_from_fixed_src(void); +void Operators_not_from_fixed_src(void); +void Operators_or_from_fixed_src(void); +void Operators_and_from_this(void); +void Operators_not_from_this(void); +void Operators_or_from_this(void); +void Operators_and_from_this_written(void); +void Operators_not_from_this_written(void); +void Operators_or_from_this_written(void); +void Operators_and_from_empty(void); +void Operators_not_from_empty(void); +void Operators_or_from_empty(void); +void Operators_and_from_empty_w_tag(void); +void Operators_not_from_empty_w_tag(void); +void Operators_or_from_empty_w_tag(void); +void Operators_or_w_wildcard(void); +void Operators_or_w_component_and_tag(void); +void Operators_or_w_tag_and_component(void); + +// Testsuite 'Transitive' +void Transitive_1_fact_0_lvl_true(void); +void Transitive_1_fact_1_lvl_true(void); +void Transitive_1_fact_2_lvl_true(void); +void Transitive_1_fact_0_lvl_false(void); +void Transitive_1_fact_1_lvl_false(void); +void Transitive_1_fact_2_lvl_false(void); +void Transitive_1_fact_reflexive(void); +void Transitive_1_isa(void); +void Transitive_1_childof(void); +void Transitive_1_this_src_written_0_lvl(void); +void Transitive_1_this_src_written_1_lvl(void); +void Transitive_1_this_src_written_2_lvl(void); +void Transitive_1_this_src_written_reflexive(void); +void Transitive_1_this_src_0_lvl(void); +void Transitive_1_this_src_1_lvl(void); +void Transitive_1_this_src_2_lvl(void); +void Transitive_1_this_src_reflexive(void); +void Transitive_1_ent_src_tgt_var_0_lvl(void); +void Transitive_1_ent_src_tgt_var_1_lvl(void); +void Transitive_1_ent_src_tgt_var_2_lvl(void); +void Transitive_1_ent_src_tgt_var_reflexive(void); +void Transitive_1_this_src_tgt_var(void); +void Transitive_1_this_src_tgt_var_reflexive(void); +void Transitive_1_var_src_written_0_lvl(void); +void Transitive_1_var_src_written_1_lvl(void); +void Transitive_1_var_src_written_2_lvl(void); +void Transitive_1_var_src_written_reflexive(void); +void Transitive_1_var_src_0_lvl(void); +void Transitive_1_var_src_1_lvl(void); +void Transitive_1_var_src_2_lvl(void); +void Transitive_1_var_src_reflexive(void); +void Transitive_1_var_src_tgt_var(void); +void Transitive_1_var_src_tgt_var_reflexive(void); +void Transitive_1_ent_src_tgt_this_0_lvl(void); +void Transitive_1_ent_src_tgt_this_1_lvl(void); +void Transitive_1_ent_src_tgt_this_2_lvl(void); +void Transitive_1_ent_src_tgt_this_reflexive(void); +void Transitive_1_var_src_tgt_this(void); +void Transitive_1_var_src_tgt_this_reflexive(void); +void Transitive_2_ent_src_constrain_tgt_var_before_0_lvl(void); +void Transitive_2_ent_src_constrain_tgt_var_before_1_lvl(void); +void Transitive_2_ent_src_constrain_tgt_var_before_2_lvl(void); +void Transitive_2_ent_src_constrain_tgt_var_after_0_lvl(void); +void Transitive_2_ent_src_constrain_tgt_var_after_1_lvl(void); +void Transitive_2_ent_src_constrain_tgt_var_after_2_lvl(void); +void Transitive_2_this_src_constrain_tgt_var_before_0_lvl(void); +void Transitive_2_this_src_constrain_tgt_var_before_1_lvl(void); +void Transitive_2_this_src_constrain_tgt_var_before_2_lvl(void); +void Transitive_2_this_src_constrain_tgt_var_after_0_lvl(void); +void Transitive_2_this_src_constrain_tgt_var_after_1_lvl(void); +void Transitive_2_this_src_constrain_tgt_var_after_2_lvl(void); +void Transitive_1_src_tgt_same_var(void); +void Transitive_1_src_tgt_same_var_reflexive(void); +void Transitive_1_src_tgt_same_this_var_reflexive(void); +void Transitive_1_any_src_tgt_var(void); +void Transitive_not_transitive_ent_tgt(void); +void Transitive_not_transitive_var_tgt(void); +void Transitive_not_transitive_ent_tgt_written(void); +void Transitive_not_transitive_var_tgt_written(void); +void Transitive_optional_transitive_ent_tgt(void); +void Transitive_optional_transitive_var_tgt(void); +void Transitive_optional_transitive_ent_tgt_written(void); +void Transitive_optional_transitive_var_tgt_written(void); +void Transitive_2_var_src_w_same_tgt_ent(void); +void Transitive_self_target(void); +void Transitive_any_target(void); +void Transitive_isa_prefab(void); +void Transitive_isa_disabled(void); +void Transitive_isa_prefab_match_prefab_flag(void); +void Transitive_isa_prefab_match_prefab_term(void); +void Transitive_isa_disabled_match_disabled_flag(void); +void Transitive_isa_disabled_match_disabled_term(void); + +// Testsuite 'ComponentInheritance' +void ComponentInheritance_1_ent_0_lvl(void); +void ComponentInheritance_1_ent_1_lvl(void); +void ComponentInheritance_1_ent_2_lvl(void); +void ComponentInheritance_1_ent_3_lvl(void); +void ComponentInheritance_1_this_0_lvl(void); +void ComponentInheritance_1_this_1_lvl(void); +void ComponentInheritance_1_this_2_lvl(void); +void ComponentInheritance_1_this_3_lvl(void); +void ComponentInheritance_1_this_0_lvl_written(void); +void ComponentInheritance_1_this_1_lvl_written(void); +void ComponentInheritance_1_this_2_lvl_written(void); +void ComponentInheritance_1_this_3_lvl_written(void); +void ComponentInheritance_1_var_0_lvl(void); +void ComponentInheritance_1_var_1_lvl(void); +void ComponentInheritance_1_var_2_lvl(void); +void ComponentInheritance_1_var_3_lvl(void); +void ComponentInheritance_1_var_0_lvl_written(void); +void ComponentInheritance_1_var_1_lvl_written(void); +void ComponentInheritance_1_var_2_lvl_written(void); +void ComponentInheritance_1_var_3_lvl_written(void); +void ComponentInheritance_1_ent_1_lvl_self(void); +void ComponentInheritance_1_this_1_lvl_self(void); +void ComponentInheritance_1_this_1_lvl_written_self(void); +void ComponentInheritance_1_var_1_lvl_self(void); +void ComponentInheritance_1_var_1_lvl_written_self(void); +void ComponentInheritance_1_ent_src_not(void); +void ComponentInheritance_1_this_src_not(void); +void ComponentInheritance_1_var_src_not(void); +void ComponentInheritance_1_this_src_not_written(void); +void ComponentInheritance_1_var_src_not_written(void); +void ComponentInheritance_first_self(void); + +// Testsuite 'Recycled' +void Recycled_setup(void); +void Recycled_recycled_vars(void); +void Recycled_recycled_pair_vars(void); +void Recycled_recycled_this_ent_var(void); +void Recycled_has_recycled_id_from_pair(void); +void Recycled_recycled_pair(void); +void Recycled_recycled_component_id(void); + +// Testsuite 'BuiltinPredicates' +void BuiltinPredicates_setup(void); +void BuiltinPredicates_this_eq_id(void); +void BuiltinPredicates_this_eq_name(void); +void BuiltinPredicates_this_eq_var(void); +void BuiltinPredicates_this_eq_id_written(void); +void BuiltinPredicates_this_eq_id_written_no_match(void); +void BuiltinPredicates_this_eq_name_written(void); +void BuiltinPredicates_this_eq_name_written_no_match(void); +void BuiltinPredicates_this_eq_var_written(void); +void BuiltinPredicates_var_eq_id(void); +void BuiltinPredicates_var_eq_name(void); +void BuiltinPredicates_var_eq_var(void); +void BuiltinPredicates_var_eq_id_written(void); +void BuiltinPredicates_var_eq_id_written_no_match(void); +void BuiltinPredicates_var_eq_name_written(void); +void BuiltinPredicates_var_eq_name_written_no_match(void); +void BuiltinPredicates_var_eq_var_written(void); +void BuiltinPredicates_var_eq_this(void); +void BuiltinPredicates_this_neq_id(void); +void BuiltinPredicates_this_neq_name(void); +void BuiltinPredicates_this_neq_var(void); +void BuiltinPredicates_this_neq_id_written(void); +void BuiltinPredicates_this_neq_id_written_no_match(void); +void BuiltinPredicates_this_neq_name_written(void); +void BuiltinPredicates_this_neq_name_written_no_match(void); +void BuiltinPredicates_this_neq_var_written(void); +void BuiltinPredicates_var_neq_id(void); +void BuiltinPredicates_var_neq_name(void); +void BuiltinPredicates_var_neq_var(void); +void BuiltinPredicates_var_neq_id_written(void); +void BuiltinPredicates_var_neq_id_written_no_match(void); +void BuiltinPredicates_var_neq_name_written(void); +void BuiltinPredicates_var_neq_name_written_no_match(void); +void BuiltinPredicates_var_neq_var_written(void); +void BuiltinPredicates_var_neq_this(void); +void BuiltinPredicates_this_2_neq_id(void); +void BuiltinPredicates_this_2_neq_name(void); +void BuiltinPredicates_var_2_neq_id(void); +void BuiltinPredicates_var_2_neq_name(void); +void BuiltinPredicates_this_2_neq_id_written(void); +void BuiltinPredicates_this_2_neq_name_written(void); +void BuiltinPredicates_var_2_neq_id_written(void); +void BuiltinPredicates_var_2_neq_name_written(void); +void BuiltinPredicates_this_2_or_id(void); +void BuiltinPredicates_this_3_or_id(void); +void BuiltinPredicates_this_2_or_name(void); +void BuiltinPredicates_this_3_or_name(void); +void BuiltinPredicates_this_2_or_match(void); +void BuiltinPredicates_this_3_or_match(void); +void BuiltinPredicates_var_2_or_id(void); +void BuiltinPredicates_var_2_or_name(void); +void BuiltinPredicates_this_2_or_id_written(void); +void BuiltinPredicates_this_3_or_id_written(void); +void BuiltinPredicates_this_2_or_name_written(void); +void BuiltinPredicates_var_2_or_id_written(void); +void BuiltinPredicates_var_2_or_name_written(void); +void BuiltinPredicates_this_match_eq(void); +void BuiltinPredicates_var_match_eq(void); +void BuiltinPredicates_this_match_eq_written(void); +void BuiltinPredicates_this_match_eq_written_self(void); +void BuiltinPredicates_var_match_eq_written(void); +void BuiltinPredicates_this_match_neq(void); +void BuiltinPredicates_var_match_neq(void); +void BuiltinPredicates_this_match_neq_written(void); +void BuiltinPredicates_var_match_neq_written(void); +void BuiltinPredicates_this_match_2_neq(void); +void BuiltinPredicates_var_match_2_neq(void); +void BuiltinPredicates_this_match_2_neq_written(void); +void BuiltinPredicates_var_match_2_neq_written(void); +void BuiltinPredicates_this_match_2_or(void); +void BuiltinPredicates_this_match_2_or_written(void); +void BuiltinPredicates_this_match_3_or(void); +void BuiltinPredicates_this_match_3_or_written(void); +void BuiltinPredicates_unresolved_by_name(void); +void BuiltinPredicates_var_eq_wildcard(void); +void BuiltinPredicates_var_eq_any(void); +void BuiltinPredicates_var_eq_wildcard_after_write(void); +void BuiltinPredicates_var_eq_any_after_write(void); +void BuiltinPredicates_var_eq_after_var_0_src(void); +void BuiltinPredicates_2_or_w_eq_this(void); +void BuiltinPredicates_2_or_w_eq_lookup_var(void); +void BuiltinPredicates_3_or_w_eq_lookup_var(void); +void BuiltinPredicates_eq_variable(void); +void BuiltinPredicates_eq_wildcard(void); +void BuiltinPredicates_eq_any(void); +void BuiltinPredicates_neq_variable(void); +void BuiltinPredicates_neq_wildcard(void); +void BuiltinPredicates_neq_any(void); +void BuiltinPredicates_match_variable(void); +void BuiltinPredicates_match_wildcard(void); +void BuiltinPredicates_match_any(void); + +// Testsuite 'Scopes' +void Scopes_setup(void); +void Scopes_term_w_not_scope_1_term(void); +void Scopes_term_w_not_scope_2_terms(void); +void Scopes_term_w_not_scope_1_term_w_not(void); +void Scopes_term_w_not_scope_2_terms_w_not(void); +void Scopes_term_w_not_scope_1_term_w_var(void); +void Scopes_term_w_not_scope_2_terms_w_var(void); +void Scopes_term_w_not_scope_1_term_w_not_w_var(void); +void Scopes_term_w_not_scope_2_terms_w_not_w_var(void); +void Scopes_term_w_not_scope_2_terms_w_or(void); +void Scopes_term_w_not_scope_3_terms_w_or(void); +void Scopes_term_w_not_scope_2_terms_w_before_after(void); + +// Testsuite 'Traversal' +void Traversal_setup(void); +void Traversal_this_self_up_childof(void); +void Traversal_this_up_childof(void); +void Traversal_this_written_self_up_childof(void); +void Traversal_this_written_up_childof(void); +void Traversal_var_self_up_childof(void); +void Traversal_var_up_childof(void); +void Traversal_var_written_self_up_childof(void); +void Traversal_var_written_up_childof(void); +void Traversal_set_var_self_up_childof(void); +void Traversal_set_var_up_childof(void); +void Traversal_set_var_written_self_up_childof(void); +void Traversal_set_var_written_up_childof(void); +void Traversal_ent_self_up_childof(void); +void Traversal_ent_up_childof(void); +void Traversal_implicit_this_self_up_isa(void); +void Traversal_implicit_this_up_isa(void); +void Traversal_implicit_var_self_up_isa(void); +void Traversal_implicit_var_up_isa(void); +void Traversal_implicit_ent_self_up_isa(void); +void Traversal_implicit_ent_up_isa(void); +void Traversal_self_up_2_targets(void); +void Traversal_up_2_targets(void); +void Traversal_self_up_2_targets_diamond(void); +void Traversal_up_2_targets_diamond(void); +void Traversal_written_self_up_2_targets(void); +void Traversal_written_up_2_targets(void); +void Traversal_written_self_up_2_targets_diamond(void); +void Traversal_written_up_2_targets_diamond(void); +void Traversal_2_self_up_terms(void); +void Traversal_2_self_up_terms_2_targets(void); +void Traversal_self_up_empty_table(void); +void Traversal_up_empty_table(void); +void Traversal_self_up_match_empty_table(void); +void Traversal_up_match_empty_table(void); +void Traversal_self_up_all_owned(void); +void Traversal_up_all_owned(void); +void Traversal_this_self_up_childof_inherited(void); +void Traversal_this_up_childof_inherited(void); +void Traversal_this_self_up_childof_inherited_override(void); +void Traversal_this_up_childof_inherited_override(void); +void Traversal_this_written_self_up_childof_inherited(void); +void Traversal_this_written_up_childof_inherited(void); +void Traversal_this_written_self_up_childof_inherited_override(void); +void Traversal_this_written_up_childof_inherited_override(void); +void Traversal_var_self_up_childof_inherited(void); +void Traversal_var_up_childof_inherited(void); +void Traversal_var_written_self_up_childof_inherited(void); +void Traversal_var_written_up_childof_inherited(void); +void Traversal_ent_self_up_childof_inherited(void); +void Traversal_ent_up_childof_inherited(void); +void Traversal_ent_written_self_up_childof_inherited(void); +void Traversal_ent_written_up_childof_inherited(void); +void Traversal_this_self_up_childof_component(void); +void Traversal_this_up_childof_component(void); +void Traversal_this_written_self_up_childof_component(void); +void Traversal_this_written_up_childof_component(void); +void Traversal_var_self_up_childof_component(void); +void Traversal_var_up_childof_component(void); +void Traversal_var_written_self_up_childof_component(void); +void Traversal_var_written_up_childof_component(void); +void Traversal_this_self_up_childof_recycled_parent(void); +void Traversal_this_up_childof_recycled_parent(void); +void Traversal_this_written_self_up_childof_recycled_parent(void); +void Traversal_this_written_up_childof_recycled_parent(void); +void Traversal_this_self_up_childof_recycled_parent_component(void); +void Traversal_this_up_childof_recycled_parent_component(void); +void Traversal_this_written_self_up_childof_recycled_parent_component(void); +void Traversal_this_written_up_childof_recycled_parent_component(void); +void Traversal_this_self_up_childof_pair(void); +void Traversal_this_up_childof_pair(void); +void Traversal_this_written_self_up_childof_pair(void); +void Traversal_this_written_up_childof_pair(void); +void Traversal_this_self_up_childof_pair_wildcard(void); +void Traversal_this_up_childof_pair_wildcard(void); +void Traversal_this_written_self_up_childof_pair_wildcard(void); +void Traversal_this_written_up_childof_pair_wildcard(void); +void Traversal_this_self_up_childof_pair_tgt_var(void); +void Traversal_this_written_self_up_childof_pair_tgt_var(void); +void Traversal_this_self_up_childof_pair_rel_var(void); +void Traversal_this_written_self_up_childof_pair_rel_var(void); +void Traversal_this_self_up_childof_pair_for_var_written(void); +void Traversal_this_up_childof_pair_for_var_written(void); +void Traversal_this_written_self_up_childof_pair_for_var_written(void); +void Traversal_this_self_up_childof_pair_for_var_written_n_targets(void); +void Traversal_this_written_self_up_childof_pair_for_var_written_n_targets(void); +void Traversal_self_up_2_levels_w_prefab(void); +void Traversal_self_up_2_levels_other_trav_rel_w_prefab(void); +void Traversal_up_2_levels_w_prefab(void); +void Traversal_up_2_levels_other_trav_rel_w_prefab(void); +void Traversal_self_up_2_levels(void); +void Traversal_self_up_2_levels_other_trav_rel(void); +void Traversal_up_2_levels(void); +void Traversal_up_2_levels_other_trav_rel(void); +void Traversal_self_up_mixed_traversable(void); +void Traversal_not_up(void); +void Traversal_not_self_up(void); +void Traversal_not_up_wildcard(void); +void Traversal_not_self_up_wildcard(void); +void Traversal_not_up_disabled(void); +void Traversal_up_2_rel_instances(void); +void Traversal_up_2_rel_instances_match_2nd(void); +void Traversal_up_only_w_owned(void); +void Traversal_this_up_trav_unused_rel(void); +void Traversal_this_optional_self(void); +void Traversal_this_optional_up(void); +void Traversal_this_optional_self_up(void); +void Traversal_this_written_optional_self(void); +void Traversal_this_written_optional_up(void); +void Traversal_this_written_optional_self_up(void); +void Traversal_fixed_src_w_up(void); +void Traversal_match_empty_table_up(void); +void Traversal_match_empty_table_up_written(void); +void Traversal_match_empty_table_up_implicit_isa(void); +void Traversal_match_empty_table_up_written_implicit_isa(void); +void Traversal_match_empty_table_up_isa(void); +void Traversal_match_empty_table_up_written_isa(void); +void Traversal_up_after_add_batched_to_parent(void); +void Traversal_up_component_after_parent_table_change(void); +void Traversal_up_component_w_singleton_after_parent_table_change(void); +void Traversal_up_component_w_var_after_parent_table_change(void); +void Traversal_test_up_component_after_parent_table_change(void); +void Traversal_test_up_component_w_singleton_after_parent_table_change(void); +void Traversal_up_component_after_parent_table_change_no_data(void); +void Traversal_up_component_w_singleton_after_parent_table_change_no_data(void); +void Traversal_up_component_w_var_after_parent_table_change_no_data(void); +void Traversal_test_up_component_after_parent_table_change_no_data(void); +void Traversal_test_up_component_w_singleton_after_parent_table_change_no_data(void); +void Traversal_this_up_childof_isa_childof(void); +void Traversal_this_up_isa_childof(void); +void Traversal_this_up_isa_isa_childof(void); +void Traversal_this_up_isa_childof_isa(void); +void Traversal_this_up_isa_childof_isa_childof(void); +void Traversal_this_self_up_childof_isa_childof(void); +void Traversal_this_self_up_isa_childof(void); +void Traversal_this_self_up_isa_isa_childof(void); +void Traversal_this_self_up_isa_childof_isa(void); +void Traversal_this_self_up_isa_childof_isa_childof(void); +void Traversal_this_written_up_childof_isa_childof(void); +void Traversal_this_written_up_isa_childof(void); +void Traversal_this_written_up_isa_isa_childof(void); +void Traversal_this_written_up_isa_childof_isa(void); +void Traversal_this_written_up_isa_childof_isa_childof(void); +void Traversal_this_written_self_up_childof_isa_childof(void); +void Traversal_this_written_self_up_isa_childof(void); +void Traversal_this_written_self_up_isa_isa_childof(void); +void Traversal_this_written_self_up_isa_childof_isa(void); +void Traversal_this_written_self_up_isa_childof_isa_childof(void); + +// Testsuite 'Cascade' +void Cascade_parent_cascade(void); +void Cascade_existing_custom_rel_cascade(void); +void Cascade_new_custom_rel_cascade(void); +void Cascade_cascade_w_2_depths(void); +void Cascade_cascade_w_3_depths(void); +void Cascade_cascade_w_2_depths_desc(void); +void Cascade_cascade_w_3_depths_desc(void); +void Cascade_existing_isa_cascade(void); +void Cascade_new_isa_cascade(void); +void Cascade_childof_cascade(void); +void Cascade_cascade_rematch_2_lvls(void); +void Cascade_cascade_rematch_2_lvls_2_relations(void); +void Cascade_cascade_topological(void); +void Cascade_cascade_desc_rematch_2_lvls(void); +void Cascade_cascade_desc_rematch_2_lvls_2_relations(void); +void Cascade_cascade_desc_topological(void); +void Cascade_cascade_after_recycled_parent_change(void); +void Cascade_invalid_cascade_for_uncached(void); +void Cascade_invalid_cascade_for_first(void); +void Cascade_invalid_cascade_for_second(void); +void Cascade_invalid_desc_without_cascade(void); +void Cascade_invalid_desc_for_first(void); +void Cascade_invalid_desc_for_second(void); + +// Testsuite 'Cached' +void Cached_simple_query_existing_table(void); +void Cached_simple_query_2_existing_tables(void); +void Cached_simple_query_new_table(void); +void Cached_simple_query_2_new_tables(void); +void Cached_simple_query_existing_and_new_table(void); +void Cached_wildcard_query_existing_table(void); +void Cached_wildcard_query_new_table(void); +void Cached_wildcard_query_existing_table_2_results_p_table(void); +void Cached_wildcard_query_new_table_2_results_p_table(void); +void Cached_wildcard_query_2nd_term(void); +void Cached_wildcard_query_2nd_term_self(void); +void Cached_simple_query_existing_empty_table(void); +void Cached_simple_query_existing_empty_type(void); +void Cached_simple_query_new_empty_table(void); +void Cached_component_query_existing_table(void); +void Cached_component_query_new_table(void); +void Cached_component_query_existing_empty_table(void); +void Cached_2_component_query_existing_empty_table(void); +void Cached_2_component_query_existing_empty_type(void); +void Cached_only_optional(void); +void Cached_only_optional_new_empty_table(void); +void Cached_only_optional_new_empty_non_empty_table(void); +void Cached_only_optional_new_unset_tables(void); +void Cached_singleton_w_optional_new_empty_table(void); +void Cached_singleton_w_optional_new_empty_non_empty_table(void); +void Cached_singleton_w_optional_new_unset_tables(void); +void Cached_query_w_from_entity_match_after(void); +void Cached_query_w_from_singleton_match_after(void); +void Cached_query_rematch_optional_after_add(void); +void Cached_get_owned_tag(void); +void Cached_get_shared_tag(void); +void Cached_explicit_delete(void); +void Cached_get_column_size(void); +void Cached_stresstest_query_free(void); +void Cached_query_optional_tag(void); +void Cached_query_optional_shared_tag(void); +void Cached_query_iter_10_tags(void); +void Cached_query_iter_10_components(void); +void Cached_iter_type_set(void); +void Cached_filter_term(void); +void Cached_2_terms_1_filter(void); +void Cached_3_terms_2_filter(void); +void Cached_add_singleton_after_query(void); +void Cached_query_w_component_from_parent_from_non_this(void); +void Cached_create_query_while_pending(void); +void Cached_empty_query(void); +void Cached_not_pair_relation_wildcard(void); +void Cached_not_pair_object_wildcard(void); +void Cached_two_pair_wildcards_one_not(void); +void Cached_two_pair_wildcards_one_not_any(void); +void Cached_implicit_existing_isa_superset(void); +void Cached_implicit_new_isa_superset(void); +void Cached_isa_superset(void); +void Cached_isa_superset_2_lvls(void); +void Cached_isa_superset_3_lvls(void); +void Cached_isa_superset_2_lvls_owned(void); +void Cached_isa_superset_3_lvls_owned(void); +void Cached_isa_superset_owned_empty_table_after_match(void); +void Cached_isa_self_superset(void); +void Cached_childof_superset(void); +void Cached_superset_2_targets(void); +void Cached_superset_2_relations(void); +void Cached_superset_2_relations_instanced(void); +void Cached_superset_2_relations_w_component(void); +void Cached_superset_2_relations_instanced_w_component(void); +void Cached_parent(void); +void Cached_isa_rematch(void); +void Cached_childof_rematch(void); +void Cached_isa_unmatch(void); +void Cached_childof_unmatch(void); +void Cached_isa_rematch_2_lvls(void); +void Cached_childof_rematch_2_lvls(void); +void Cached_childof_rematch_from_isa(void); +void Cached_rematch_optional_ref(void); +void Cached_rematch_optional_ref_w_2_refs(void); +void Cached_rematch_optional_ref_tag_w_ref_component(void); +void Cached_rematch_after_add_to_recycled_parent(void); +void Cached_match_query_expr_from_scope(void); +void Cached_query_long_or_w_ref(void); +void Cached_query_w_pair_id_and_subj(void); +void Cached_rematch_after_delete_inherited_tag(void); +void Cached_rematch_after_delete_rel_of_inherited_pair(void); +void Cached_rematch_after_delete_obj_of_inherited_pair(void); +void Cached_rematch_empty(void); +void Cached_rematch_empty_table_w_superset(void); +void Cached_2_self_up_terms_new_tables(void); +void Cached_this_self_up_childof_pair_new_tables(void); + +// Testsuite 'ChangeDetection' +void ChangeDetection_query_changed_after_new(void); +void ChangeDetection_query_changed_after_delete(void); +void ChangeDetection_query_changed_after_add(void); +void ChangeDetection_query_changed_after_remove(void); +void ChangeDetection_query_changed_after_set(void); +void ChangeDetection_query_change_after_modified(void); +void ChangeDetection_query_change_after_out_system(void); +void ChangeDetection_query_change_after_out_query_no_data_flag(void); +void ChangeDetection_query_change_after_in_system(void); +void ChangeDetection_query_change_after_modified_out_term(void); +void ChangeDetection_query_change_check_iter(void); +void ChangeDetection_query_change_check_iter_after_skip_read(void); +void ChangeDetection_query_change_check_iter_after_skip_write(void); +void ChangeDetection_query_change_parent_term(void); +void ChangeDetection_query_change_prefab_term(void); +void ChangeDetection_query_change_parent_term_w_tag(void); +void ChangeDetection_query_change_prefab_term_w_tag(void); +void ChangeDetection_query_changed_w_or(void); +void ChangeDetection_query_changed_or(void); +void ChangeDetection_query_changed_w_singleton(void); +void ChangeDetection_query_changed_w_only_singleton(void); +void ChangeDetection_query_changed_w_only_singleton_after_set(void); +void ChangeDetection_query_changed_w_only_singleton_after_out_term(void); +void ChangeDetection_query_changed_w_only_singleton_after_singleton_out_term(void); +void ChangeDetection_query_changed_w_only_parent(void); +void ChangeDetection_query_changed_w_only_parent_after_set(void); +void ChangeDetection_query_changed_w_only_parent_after_out_term(void); +void ChangeDetection_query_changed_w_only_parent_after_parent_out_term(void); +void ChangeDetection_query_changed_tag(void); +void ChangeDetection_query_changed_no_source(void); +void ChangeDetection_query_changed_no_source_component(void); +void ChangeDetection_query_changed_w_not_out(void); +void ChangeDetection_query_change_w_optional(void); +void ChangeDetection_query_changed_after_count(void); + +// Testsuite 'GroupBy' +void GroupBy_group_by(void); +void GroupBy_group_by_w_ctx(void); +void GroupBy_group_by_w_sort_reverse_group_creation(void); +void GroupBy_group_by_iter_one(void); +void GroupBy_group_by_iter_one_all_groups(void); +void GroupBy_group_by_iter_one_empty(void); +void GroupBy_group_by_iter_one_empty_query(void); +void GroupBy_group_by_iter_one_empty_table(void); +void GroupBy_group_by_w_deleted_group_id(void); +void GroupBy_group_by_callbacks(void); +void GroupBy_group_by_default_action(void); +void GroupBy_group_table_count(void); + +// Testsuite 'MemberTarget' +void MemberTarget_setup(void); +void MemberTarget_this_member_eq_1(void); +void MemberTarget_this_member_eq_2(void); +void MemberTarget_this_member_eq_no_matches(void); +void MemberTarget_this_member_eq_all_matches(void); +void MemberTarget_this_member_wildcard(void); +void MemberTarget_this_member_var(void); +void MemberTarget_this_member_var_written(void); +void MemberTarget_this_member_var_read(void); +void MemberTarget_this_member_eq_1_2nd_member(void); +void MemberTarget_this_member_eq_2_2nd_member(void); +void MemberTarget_this_member_var_same_1(void); +void MemberTarget_this_member_var_same_2(void); +void MemberTarget_this_written_member_eq_1(void); +void MemberTarget_this_written_member_eq_2(void); +void MemberTarget_this_written_member_wildcard(void); +void MemberTarget_this_written_member_var(void); +void MemberTarget_this_member_neq_1(void); +void MemberTarget_this_member_neq_2(void); +void MemberTarget_this_member_neq_no_matches(void); +void MemberTarget_this_member_neq_all_matches(void); +void MemberTarget_this_member_neq_wildcard(void); +void MemberTarget_this_written_member_neq_1(void); +void MemberTarget_this_written_member_neq_2(void); +void MemberTarget_this_written_member_neq_no_matches(void); +void MemberTarget_this_written_member_neq_all_matches(void); +void MemberTarget_this_written_member_neq_wildcard(void); +void MemberTarget_this_member_eq_optional(void); +void MemberTarget_this_member_eq_optional_wildcard(void); +void MemberTarget_this_written_member_eq_optional(void); +void MemberTarget_this_written_member_eq_optional_wildcard(void); +void MemberTarget_this_member_eq_w_other_tag(void); +void MemberTarget_this_member_eq_w_other_component(void); +void MemberTarget_this_written_member_eq_w_other_tag(void); +void MemberTarget_this_written_member_eq_w_other_component(void); +void MemberTarget_this_written_member_eq_w_other_inherit_component(void); +void MemberTarget_this_2_or(void); +void MemberTarget_this_3_or(void); +void MemberTarget_this_written_2_or(void); +void MemberTarget_this_written_3_or(void); +void MemberTarget_var_2_or(void); +void MemberTarget_var_3_or(void); +void MemberTarget_this_2_or_w_2_members(void); +void MemberTarget_this_2_or_w_2_types(void); +void MemberTarget_this_written_2_or_w_2_members(void); +void MemberTarget_this_written_2_or_w_2_types(void); +void MemberTarget_this_2_or_2_types_wildcard(void); +void MemberTarget_this_2_or_2_types_dep_var(void); +void MemberTarget_var_written_2_or(void); +void MemberTarget_var_written_3_or(void); +void MemberTarget_var_member_eq(void); +void MemberTarget_var_member_eq_no_matches(void); +void MemberTarget_var_member_eq_all_matches(void); +void MemberTarget_var_member_wildcard(void); +void MemberTarget_var_member_neq(void); +void MemberTarget_var_member_neq_no_matches(void); +void MemberTarget_var_member_neq_all_matches(void); +void MemberTarget_var_written_member_eq(void); +void MemberTarget_var_written_member_eq_no_matches(void); +void MemberTarget_var_written_member_eq_all_matches(void); +void MemberTarget_var_written_member_wildcard(void); +void MemberTarget_var_written_member_neq(void); +void MemberTarget_var_written_member_neq_no_matches(void); +void MemberTarget_var_written_member_neq_all_matches(void); + +// Testsuite 'Toggle' +void Toggle_setup(void); +void Toggle_fixed_src_1_tag_toggle(void); +void Toggle_fixed_src_1_component_toggle(void); +void Toggle_fixed_src_2_tag_toggle(void); +void Toggle_fixed_src_2_component_toggle(void); +void Toggle_fixed_2_src_w_toggle(void); +void Toggle_this_w_fixed_src_w_toggle(void); +void Toggle_fixed_src_w_this_w_toggle(void); +void Toggle_this_from_nothing(void); +void Toggle_this(void); +void Toggle_this_skip_initial(void); +void Toggle_this_pair(void); +void Toggle_this_pair_skip_initial(void); +void Toggle_this_tag(void); +void Toggle_this_tag_pair(void); +void Toggle_this_tag_pair_wildcard(void); +void Toggle_this_toggle_shared_self_up(void); +void Toggle_this_toggle_shared_up(void); +void Toggle_this_toggle_shared_self_up_w_self(void); +void Toggle_this_toggle_shared_up_w_self(void); +void Toggle_this_toggle_shared_self_up_w_self_reverse(void); +void Toggle_this_toggle_shared_up_w_self_reverse(void); +void Toggle_this_toggle_shared_self_up_w_self_toggle(void); +void Toggle_this_toggle_shared_up_w_self_toggle(void); +void Toggle_this_toggle_shared_self_up_w_self_toggle_reverse(void); +void Toggle_this_toggle_shared_up_w_self_toggle_reverse(void); +void Toggle_this_toggle_not_shared_self_up(void); +void Toggle_this_toggle_not_shared_up(void); +void Toggle_this_toggle_optional_shared_self_up(void); +void Toggle_this_toggle_optional_shared_up(void); +void Toggle_this_64_mod_1(void); +void Toggle_this_64_mod_2(void); +void Toggle_this_64_mod_3(void); +void Toggle_this_64_mod_7(void); +void Toggle_this_64_mod_8(void); +void Toggle_this_64_mod_10(void); +void Toggle_this_64_mod_64(void); +void Toggle_this_64_mod_256(void); +void Toggle_this_64_mod_1024(void); +void Toggle_this_100_mod_1(void); +void Toggle_this_100_mod_2(void); +void Toggle_this_100_mod_3(void); +void Toggle_this_100_mod_7(void); +void Toggle_this_100_mod_8(void); +void Toggle_this_100_mod_10(void); +void Toggle_this_100_mod_64(void); +void Toggle_this_100_mod_256(void); +void Toggle_this_100_mod_1024(void); +void Toggle_this_128_mod_1(void); +void Toggle_this_128_mod_2(void); +void Toggle_this_128_mod_3(void); +void Toggle_this_128_mod_7(void); +void Toggle_this_128_mod_8(void); +void Toggle_this_128_mod_10(void); +void Toggle_this_128_mod_64(void); +void Toggle_this_128_mod_256(void); +void Toggle_this_128_mod_1024(void); +void Toggle_this_200_mod_1(void); +void Toggle_this_200_mod_2(void); +void Toggle_this_200_mod_3(void); +void Toggle_this_200_mod_7(void); +void Toggle_this_200_mod_8(void); +void Toggle_this_200_mod_10(void); +void Toggle_this_200_mod_64(void); +void Toggle_this_200_mod_256(void); +void Toggle_this_200_mod_1024(void); +void Toggle_this_1024_mod_1(void); +void Toggle_this_1024_mod_2(void); +void Toggle_this_1024_mod_3(void); +void Toggle_this_1024_mod_7(void); +void Toggle_this_1024_mod_8(void); +void Toggle_this_1024_mod_10(void); +void Toggle_this_1024_mod_64(void); +void Toggle_this_1024_mod_256(void); +void Toggle_this_1024_mod_1024(void); +void Toggle_this_enabled_64_mod_1(void); +void Toggle_this_enabled_64_mod_2(void); +void Toggle_this_enabled_64_mod_3(void); +void Toggle_this_enabled_64_mod_7(void); +void Toggle_this_enabled_64_mod_8(void); +void Toggle_this_enabled_64_mod_10(void); +void Toggle_this_enabled_64_mod_64(void); +void Toggle_this_enabled_64_mod_256(void); +void Toggle_this_enabled_64_mod_1024(void); +void Toggle_this_enabled_100_mod_1(void); +void Toggle_this_enabled_100_mod_2(void); +void Toggle_this_enabled_100_mod_3(void); +void Toggle_this_enabled_100_mod_7(void); +void Toggle_this_enabled_100_mod_8(void); +void Toggle_this_enabled_100_mod_10(void); +void Toggle_this_enabled_100_mod_64(void); +void Toggle_this_enabled_100_mod_256(void); +void Toggle_this_enabled_100_mod_1024(void); +void Toggle_this_enabled_128_mod_1(void); +void Toggle_this_enabled_128_mod_2(void); +void Toggle_this_enabled_128_mod_3(void); +void Toggle_this_enabled_128_mod_7(void); +void Toggle_this_enabled_128_mod_8(void); +void Toggle_this_enabled_128_mod_10(void); +void Toggle_this_enabled_128_mod_64(void); +void Toggle_this_enabled_128_mod_256(void); +void Toggle_this_enabled_128_mod_1024(void); +void Toggle_this_enabled_200_mod_1(void); +void Toggle_this_enabled_200_mod_2(void); +void Toggle_this_enabled_200_mod_3(void); +void Toggle_this_enabled_200_mod_7(void); +void Toggle_this_enabled_200_mod_8(void); +void Toggle_this_enabled_200_mod_10(void); +void Toggle_this_enabled_200_mod_64(void); +void Toggle_this_enabled_200_mod_256(void); +void Toggle_this_enabled_200_mod_1024(void); +void Toggle_this_enabled_1024_mod_1(void); +void Toggle_this_enabled_1024_mod_2(void); +void Toggle_this_enabled_1024_mod_3(void); +void Toggle_this_enabled_1024_mod_7(void); +void Toggle_this_enabled_1024_mod_8(void); +void Toggle_this_enabled_1024_mod_10(void); +void Toggle_this_enabled_1024_mod_64(void); +void Toggle_this_enabled_1024_mod_256(void); +void Toggle_this_enabled_1024_mod_1024(void); +void Toggle_this_mod_2_2_bitsets(void); +void Toggle_this_mod_8_2_bitsets(void); +void Toggle_this_mod_64_2_bitsets(void); +void Toggle_this_mod_256_2_bitsets(void); +void Toggle_this_mod_1024_2_bitsets(void); +void Toggle_this_randomized_2_bitsets(void); +void Toggle_this_randomized_3_bitsets(void); +void Toggle_this_randomized_4_bitsets(void); +void Toggle_this_w_other_tag(void); +void Toggle_this_w_other_component(void); +void Toggle_this_not(void); +void Toggle_this_written_not_1024_mod_2(void); +void Toggle_this_written_not_1024_mod_3(void); +void Toggle_this_written_not_1024_mod_7(void); +void Toggle_this_written_not_1024_mod_8(void); +void Toggle_this_written_not_1024_mod_10(void); +void Toggle_this_written_not_1024_mod_64(void); +void Toggle_this_written_not_1024_mod_256(void); +void Toggle_this_written_not_1024_mod_1024(void); +void Toggle_this_optional(void); +void Toggle_this_written_optional_1024_mod_2(void); +void Toggle_this_written_optional_1024_mod_3(void); +void Toggle_this_written_optional_1024_mod_7(void); +void Toggle_this_written_optional_1024_mod_8(void); +void Toggle_this_written_optional_1024_mod_10(void); +void Toggle_this_written_optional_1024_mod_64(void); +void Toggle_this_written_optional_1024_mod_256(void); +void Toggle_this_written_optional_1024_mod_1024(void); +void Toggle_this_written_toggle_w_not_toggle(void); +void Toggle_this_written_not_toggle_w_toggle(void); +void Toggle_this_written_toggle_w_optional_toggle(void); +void Toggle_this_written_optional_toggle_w_toggle(void); +void Toggle_this_written_not_w_optional_toggle(void); +void Toggle_this_written_optional_w_not_toggle(void); +void Toggle_this_written_2_not_toggle(void); +void Toggle_this_written_2_optional_toggle(void); +void Toggle_this_written_toggle_w_2_not_toggle(void); +void Toggle_this_written_toggle_w_2_optional_toggle(void); +void Toggle_this_written_2_toggle_w_not_toggle(void); +void Toggle_this_written_2_toggle_w_optional_toggle(void); +void Toggle_this_sort(void); +void Toggle_this_table_move_2_from_3(void); +void Toggle_toggle_0_src_only_term(void); +void Toggle_toggle_0_src(void); + +// Testsuite 'Sparse' +void Sparse_setup(void); +void Sparse_1_fixed_sparse(void); +void Sparse_1_fixed_sparse_none(void); +void Sparse_1_this_sparse_simple(void); +void Sparse_1_this_sparse(void); +void Sparse_1_this_sparse_none(void); +void Sparse_1_this_sparse_written(void); +void Sparse_1_this_sparse_written_none(void); +void Sparse_1_var_sparse(void); +void Sparse_1_var_sparse_none(void); +void Sparse_1_var_sparse_written(void); +void Sparse_1_var_sparse_written_none(void); +void Sparse_2_sparse_simple(void); +void Sparse_2_sparse(void); +void Sparse_2_sparse_and_regular(void); +void Sparse_2_regular_and_sparse(void); +void Sparse_1_sparse_self(void); +void Sparse_1_sparse_up(void); +void Sparse_1_sparse_self_up(void); +void Sparse_1_sparse_written_self(void); +void Sparse_1_sparse_written_up(void); +void Sparse_1_sparse_written_self_up(void); +void Sparse_sparse_0_src_only_term(void); +void Sparse_sparse_0_src(void); + +// Testsuite 'Union' +void Union_setup(void); +void Union_1_fixed_union_any(void); +void Union_1_fixed_union_wildcard(void); +void Union_1_fixed_union_tgt(void); +void Union_1_fixed_union_tgt_var(void); +void Union_1_fixed_union_tgt_var_written(void); +void Union_1_this_union_any(void); +void Union_1_this_union_wildcard(void); +void Union_1_this_union_tgt(void); +void Union_1_this_union_tgt_var(void); +void Union_1_this_union_tgt_var_written(void); +void Union_1_var_union_any(void); +void Union_1_var_union_wildcard(void); +void Union_1_var_union_tgt(void); +void Union_1_var_union_tgt_var(void); +void Union_1_var_union_tgt_var_written(void); +void Union_1_this_written_union_any(void); +void Union_1_this_written_union_wildcard(void); +void Union_1_this_written_union_tgt(void); +void Union_1_this_written_union_tgt_var(void); +void Union_1_this_written_union_tgt_var_written(void); +void Union_1_var_written_union_any(void); +void Union_1_var_written_union_wildcard(void); +void Union_1_var_written_union_tgt(void); +void Union_1_var_written_union_tgt_var(void); +void Union_1_var_written_union_tgt_var_written(void); +void Union_not_fixed_union_any(void); +void Union_not_fixed_union_wildcard(void); +void Union_not_fixed_union_tgt(void); +void Union_not_fixed_union_var(void); +void Union_not_fixed_union_var_written(void); +void Union_not_this_written_union_any(void); +void Union_not_this_written_union_wildcard(void); +void Union_not_this_written_union_tgt(void); +void Union_not_this_written_union_var(void); +void Union_not_this_written_union_var_written(void); +void Union_query_switch(void); +void Union_query_1_case_1_type(void); +void Union_query_1_case_2_types(void); +void Union_query_2_cases_1_type(void); +void Union_query_2_cases_2_types(void); +void Union_query_after_remove(void); +void Union_sort(void); +void Union_query_recycled_tags(void); +void Union_query_single_case(void); +void Union_match_switch_on_base_instance(void); +void Union_switch_w_bitset_query(void); +void Union_switch_w_bitset_query_inv(void); +void Union_switch_w_bitset_query_2_elems(void); +void Union_switch_w_bitset_query_2_elems_skip(void); +void Union_switch_w_bitset_query_elems_interleaved(void); +void Union_switch_w_bitset_query_elems_interleaved_2_types(void); +void Union_component_relation(void); +void Union_switch_term_filter(void); +void Union_2_terms_switch_term_filter(void); +void Union_match_switch_w_switch(void); +void Union_match_switch_w_case(void); +void Union_match_switch_w_case_2_terms(void); +void Union_up(void); +void Union_self_up(void); +void Union_up_written(void); +void Union_self_up_written(void); +void Union_existing_union_table(void); +void Union_new_union_table(void); +void Union_existing_union_table_w_tgt(void); +void Union_new_union_table_w_tgt(void); +void Union_tgt_w_generation(void); +void Union_tgt_w_not_alive(void); +void Union_for_switch_filter_term(void); +void Union_union_from_nothing(void); +void Union_union_tgt_from_nothing(void); +void Union_tgt_inherited(void); + +// Testsuite 'OrderBy' +void OrderBy_sort_by_component(void); +void OrderBy_sort_by_component_2_tables(void); +void OrderBy_sort_by_component_3_tables(void); +void OrderBy_sort_by_entity(void); +void OrderBy_sort_after_add(void); +void OrderBy_sort_after_remove(void); +void OrderBy_sort_after_delete(void); +void OrderBy_sort_after_set(void); +void OrderBy_sort_after_system(void); +void OrderBy_sort_after_query(void); +void OrderBy_sort_by_component_same_value_1(void); +void OrderBy_sort_by_component_same_value_2(void); +void OrderBy_sort_by_component_move_pivot(void); +void OrderBy_sort_1000_entities(void); +void OrderBy_sort_1000_entities_w_duplicates(void); +void OrderBy_sort_1000_entities_again(void); +void OrderBy_sort_1000_entities_2_types(void); +void OrderBy_sort_1500_entities_3_types(void); +void OrderBy_sort_2000_entities_4_types(void); +void OrderBy_sort_2_entities_2_types(void); +void OrderBy_sort_3_entities_3_types(void); +void OrderBy_sort_3_entities_3_types_2(void); +void OrderBy_sort_4_entities_4_types(void); +void OrderBy_sort_1000_entities_2_types_again(void); +void OrderBy_sort_1000_entities_add_type_after_sort(void); +void OrderBy_sort_shared_component(void); +void OrderBy_sort_shared_component_childof(void); +void OrderBy_sort_w_tags_only(void); +void OrderBy_sort_childof_marked(void); +void OrderBy_sort_isa_marked(void); +void OrderBy_sort_relation_marked(void); +void OrderBy_dont_resort_after_set_unsorted_component(void); +void OrderBy_dont_resort_after_set_unsorted_component_w_tag(void); +void OrderBy_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void); +void OrderBy_sort_component_not_queried_for(void); +void OrderBy_sort_by_wildcard(void); +void OrderBy_sort_shared_w_delete(void); +void OrderBy_sort_w_nontrivial_component(void); +void OrderBy_sort_by_wildcard(void); +void OrderBy_sort_not_term(void); +void OrderBy_sort_or_term(void); +void OrderBy_sort_optional_term(void); + +// Testsuite 'OrderByEntireTable' +void OrderByEntireTable_sort_by_component(void); +void OrderByEntireTable_sort_by_component_2_tables(void); +void OrderByEntireTable_sort_by_component_3_tables(void); +void OrderByEntireTable_sort_by_entity(void); +void OrderByEntireTable_sort_after_add(void); +void OrderByEntireTable_sort_after_remove(void); +void OrderByEntireTable_sort_after_delete(void); +void OrderByEntireTable_sort_after_set(void); +void OrderByEntireTable_sort_after_system(void); +void OrderByEntireTable_sort_after_query(void); +void OrderByEntireTable_sort_by_component_same_value_1(void); +void OrderByEntireTable_sort_by_component_same_value_2(void); +void OrderByEntireTable_sort_by_component_move_pivot(void); +void OrderByEntireTable_sort_1000_entities(void); +void OrderByEntireTable_sort_1000_entities_w_duplicates(void); +void OrderByEntireTable_sort_1000_entities_again(void); +void OrderByEntireTable_sort_1000_entities_2_types(void); +void OrderByEntireTable_sort_1500_entities_3_types(void); +void OrderByEntireTable_sort_2000_entities_4_types(void); +void OrderByEntireTable_sort_2_entities_2_types(void); +void OrderByEntireTable_sort_3_entities_3_types(void); +void OrderByEntireTable_sort_3_entities_3_types_2(void); +void OrderByEntireTable_sort_4_entities_4_types(void); +void OrderByEntireTable_sort_1000_entities_2_types_again(void); +void OrderByEntireTable_sort_1000_entities_add_type_after_sort(void); +void OrderByEntireTable_sort_shared_component(void); +void OrderByEntireTable_sort_w_tags_only(void); +void OrderByEntireTable_sort_childof_marked(void); +void OrderByEntireTable_sort_isa_marked(void); +void OrderByEntireTable_sort_relation_marked(void); +void OrderByEntireTable_dont_resort_after_set_unsorted_component(void); +void OrderByEntireTable_dont_resort_after_set_unsorted_component_w_tag(void); +void OrderByEntireTable_dont_resort_after_set_unsorted_component_w_tag_w_out_term(void); +void OrderByEntireTable_sort_shared_w_delete(void); +void OrderByEntireTable_sort_not_term(void); +void OrderByEntireTable_sort_or_term(void); +void OrderByEntireTable_sort_optional_term(void); + +// Testsuite 'QueryStr' +void QueryStr_one_term(void); +void QueryStr_one_term_w_inout(void); +void QueryStr_two_terms(void); +void QueryStr_two_terms_w_inout(void); +void QueryStr_three_terms_w_or(void); +void QueryStr_three_terms_w_or_inout(void); +void QueryStr_four_terms_three_w_or_inout(void); +void QueryStr_one_term_w_pair(void); +void QueryStr_one_term_w_pair_entity_src(void); +void QueryStr_one_term_w_self(void); +void QueryStr_one_term_w_up(void); +void QueryStr_one_term_w_self_up(void); +void QueryStr_one_term_w_cascade(void); +void QueryStr_one_term_w_0(void); +void QueryStr_one_term_w_singleton(void); +void QueryStr_one_term_w_final_pair(void); +void QueryStr_one_term_w_final_dont_inherit(void); +void QueryStr_one_term_w_final_inherit(void); +void QueryStr_one_term_w_final_override(void); +void QueryStr_one_term_w_src_var(void); +void QueryStr_one_term_w_first_var(void); +void QueryStr_one_term_w_second_var(void); +void QueryStr_one_term_w_first_var_entity_src(void); +void QueryStr_one_term_w_pair_w_0_entity(void); +void QueryStr_not_term(void); +void QueryStr_wildcard_term(void); +void QueryStr_scopes(void); +void QueryStr_pred_eq(void); +void QueryStr_pred_neq(void); +void QueryStr_pred_eq_name(void); +void QueryStr_pred_neq_name(void); +void QueryStr_pred_eq_m(void); +void QueryStr_pred_neq_m(void); + +bake_test_case Validator_testcases[] = { + { + "validate_1_term", + Validator_validate_1_term + }, + { + "validate_1_term_component", + Validator_validate_1_term_component + }, + { + "validate_2_terms", + Validator_validate_2_terms + }, + { + "validate_3_terms", + Validator_validate_3_terms + }, + { + "validate_3_terms_w_or", + Validator_validate_3_terms_w_or + }, + { + "validate_4_terms_w_or_at_1", + Validator_validate_4_terms_w_or_at_1 + }, + { + "validate_1_term_wildcard", + Validator_validate_1_term_wildcard + }, + { + "validate_1_term_any", + Validator_validate_1_term_any + }, + { + "validate_1_term_same_subj_obj", + Validator_validate_1_term_same_subj_obj + }, + { + "validate_1_term_acyclic_same_subj_obj", + Validator_validate_1_term_acyclic_same_subj_obj + }, + { + "validate_1_term_acyclic_reflexive_same_subj_obj", + Validator_validate_1_term_acyclic_reflexive_same_subj_obj + }, + { + "validate_1_term_same_subj_obj_var", + Validator_validate_1_term_same_subj_obj_var + }, + { + "validate_1_term_acyclic_same_subj_obj_var", + Validator_validate_1_term_acyclic_same_subj_obj_var + }, + { + "validate_1_term_acyclic_reflexive_same_subj_obj_var", + Validator_validate_1_term_acyclic_reflexive_same_subj_obj_var + }, + { + "validate_1_term_non_acyclic_superset", + Validator_validate_1_term_non_acyclic_superset + }, + { + "validate_1_term_dont_inherit_default_set", + Validator_validate_1_term_dont_inherit_default_set + }, + { + "validate_1_term_dont_inherit_pair_default_set", + Validator_validate_1_term_dont_inherit_pair_default_set + }, + { + "validate_1_term_inherit_default_set", + Validator_validate_1_term_inherit_default_set + }, + { + "validate_1_term_inherit_pair_default_set", + Validator_validate_1_term_inherit_pair_default_set + }, + { + "validate_1_term_override_default_set", + Validator_validate_1_term_override_default_set + }, + { + "validate_1_term_override_pair_default_set", + Validator_validate_1_term_override_pair_default_set + }, + { + "validate_1_term_up_no_inherit", + Validator_validate_1_term_up_no_inherit + }, + { + "validate_1_term_up_no_inherit_pair", + Validator_validate_1_term_up_no_inherit_pair + }, + { + "validate_1_term_up_override", + Validator_validate_1_term_up_override + }, + { + "validate_1_term_up_override_pair", + Validator_validate_1_term_up_override_pair + }, + { + "validate_1_term_up_isa_no_inherit", + Validator_validate_1_term_up_isa_no_inherit + }, + { + "validate_1_term_up_isa_no_inherit_pair", + Validator_validate_1_term_up_isa_no_inherit_pair + }, + { + "validate_1_term_up_isa_override", + Validator_validate_1_term_up_isa_override + }, + { + "validate_1_term_up_isa_override_pair", + Validator_validate_1_term_up_isa_override_pair + }, + { + "validate_1_term_cascade_implicit_trav", + Validator_validate_1_term_cascade_implicit_trav + }, + { + "validate_1_term_cascade_isa", + Validator_validate_1_term_cascade_isa + }, + { + "validate_1_term_cascade_childof", + Validator_validate_1_term_cascade_childof + }, + { + "validate_1_term_cascade_down", + Validator_validate_1_term_cascade_down + }, + { + "validate_1_term_optional_only", + Validator_validate_1_term_optional_only + }, + { + "validate_1_term_transitive_pair", + Validator_validate_1_term_transitive_pair + }, + { + "validate_1_variable_as_pred_only", + Validator_validate_1_variable_as_pred_only + }, + { + "validate_1_variable_as_pred_w_subj", + Validator_validate_1_variable_as_pred_w_subj + }, + { + "validate_1_variable_as_pred_w_pair", + Validator_validate_1_variable_as_pred_w_pair + }, + { + "validate_1_variable_as_subj", + Validator_validate_1_variable_as_subj + }, + { + "validate_1_variable_as_obj", + Validator_validate_1_variable_as_obj + }, + { + "validate_2_terms_or_w_dontinherit", + Validator_validate_2_terms_or_w_dontinherit + }, + { + "validate_2_terms_or_w_both_dontinherit", + Validator_validate_2_terms_or_w_both_dontinherit + }, + { + "validate_w_pair_id", + Validator_validate_w_pair_id + }, + { + "validate_w_pred_obj", + Validator_validate_w_pred_obj + }, + { + "validate_w_pair_id_and_subj", + Validator_validate_w_pair_id_and_subj + }, + { + "validate_1_w_pred_name", + Validator_validate_1_w_pred_name + }, + { + "validate_1_w_final_pred_name", + Validator_validate_1_w_final_pred_name + }, + { + "validate_1_w_subj_name", + Validator_validate_1_w_subj_name + }, + { + "validate_1_w_obj_name", + Validator_validate_1_w_obj_name + }, + { + "validate_w_this_implicit_variable", + Validator_validate_w_this_implicit_variable + }, + { + "validate_w_this_explicit_entity", + Validator_validate_w_this_explicit_entity + }, + { + "validate_w_first_this_implicit_variable", + Validator_validate_w_first_this_implicit_variable + }, + { + "validate_w_first_this_explicit_entity", + Validator_validate_w_first_this_explicit_entity + }, + { + "validate_w_second_this_implicit_variable", + Validator_validate_w_second_this_implicit_variable + }, + { + "validate_w_second_this_explicit_entity", + Validator_validate_w_second_this_explicit_entity + }, + { + "validate_w_this_variable_name", + Validator_validate_w_this_variable_name + }, + { + "validate_w_src_var", + Validator_validate_w_src_var + }, + { + "validate_w_first_var", + Validator_validate_w_first_var + }, + { + "validate_w_second_var", + Validator_validate_w_second_var + }, + { + "validate_w_src_var_from_name", + Validator_validate_w_src_var_from_name + }, + { + "validate_w_first_first_var", + Validator_validate_w_first_first_var + }, + { + "validate_w_second_second_var", + Validator_validate_w_second_second_var + }, + { + "validate_w_0_source", + Validator_validate_w_0_source + }, + { + "validate_w_0_target", + Validator_validate_w_0_target + }, + { + "validate_2_terms_w_or", + Validator_validate_2_terms_w_or + }, + { + "validate_2_terms_w_or_mixed_src_flags", + Validator_validate_2_terms_w_or_mixed_src_flags + }, + { + "validate_2_terms_w_or_mixed_src_id", + Validator_validate_2_terms_w_or_mixed_src_id + }, + { + "validate_2_terms_w_or_mixed_src_name", + Validator_validate_2_terms_w_or_mixed_src_name + }, + { + "validate_2_terms_w_or_same_src_w_id_and_name", + Validator_validate_2_terms_w_or_same_src_w_id_and_name + }, + { + "validate_w_and_flag", + Validator_validate_w_and_flag + }, + { + "validate_w_or_flag", + Validator_validate_w_or_flag + }, + { + "validate_w_not_flag", + Validator_validate_w_not_flag + }, + { + "validate_filter", + Validator_validate_filter + }, + { + "validate_double_init", + Validator_validate_double_init + }, + { + "validate_double_init_w_expr", + Validator_validate_double_init_w_expr + }, + { + "validate_double_init_w_expr_optional", + Validator_validate_double_init_w_expr_optional + }, + { + "validate_w_tag_term_is_no_data", + Validator_validate_w_tag_term_is_no_data + }, + { + "validate_w_inout_none_term_is_no_data", + Validator_validate_w_inout_none_term_is_no_data + }, + { + "validate_w_tag_and_inout_none_term_is_no_data", + Validator_validate_w_tag_and_inout_none_term_is_no_data + }, + { + "validate_w_not_term_is_no_data", + Validator_validate_w_not_term_is_no_data + }, + { + "validate_w_no_transitive_pair", + Validator_validate_w_no_transitive_pair + }, + { + "validate_w_transitive_pair_any_src", + Validator_validate_w_transitive_pair_any_src + }, + { + "validate_w_transitive_pair", + Validator_validate_w_transitive_pair + }, + { + "validate_w_transitive_tag_no_pair", + Validator_validate_w_transitive_tag_no_pair + }, + { + "validate_w_transitive_tag_self_tgt", + Validator_validate_w_transitive_tag_self_tgt + }, + { + "validate_w_transitive_tag_any_tgt", + Validator_validate_w_transitive_tag_any_tgt + }, + { + "validate_w_pair_same_vars", + Validator_validate_w_pair_same_vars + }, + { + "validate_w_pair_not_same_vars", + Validator_validate_w_pair_not_same_vars + }, + { + "validate_w_pair_no_vars_not_same_vars", + Validator_validate_w_pair_no_vars_not_same_vars + }, + { + "validate_w_pair_wildcard_not_same_vars", + Validator_validate_w_pair_wildcard_not_same_vars + }, + { + "validate_w_pair_any_not_same_vars", + Validator_validate_w_pair_any_not_same_vars + }, + { + "validate_w_no_pair_not_same_vars", + Validator_validate_w_no_pair_not_same_vars + }, + { + "validate_not_childof_any", + Validator_validate_not_childof_any + }, + { + "validate_w_inherited_id", + Validator_validate_w_inherited_id + }, + { + "validate_w_inherited_pair", + Validator_validate_w_inherited_pair + }, + { + "validate_w_non_inherited_id", + Validator_validate_w_non_inherited_id + }, + { + "validate_w_non_inherited_pair", + Validator_validate_w_non_inherited_pair + }, + { + "validate_w_first_wildcard_inout_none", + Validator_validate_w_first_wildcard_inout_none + }, + { + "validate_w_first_var_inout_none", + Validator_validate_w_first_var_inout_none + }, + { + "validate_w_pair_wildcard_inout_none", + Validator_validate_w_pair_wildcard_inout_none + }, + { + "validate_w_pair_var_inout_none", + Validator_validate_w_pair_var_inout_none + }, + { + "validate_w_unresolved_by_name", + Validator_validate_w_unresolved_by_name + }, + { + "validate_w_unresolved_by_name_eq", + Validator_validate_w_unresolved_by_name_eq + }, + { + "validate_childof_this", + Validator_validate_childof_this + }, + { + "validate_childof_this_entity", + Validator_validate_childof_this_entity + }, + { + "validate_childof_this_by_id", + Validator_validate_childof_this_by_id + }, + { + "validate_filter_flag", + Validator_validate_filter_flag + }, + { + "validate_first_0_name", + Validator_validate_first_0_name + }, + { + "validate_src_0_name", + Validator_validate_src_0_name + }, + { + "validate_second_0_name", + Validator_validate_second_0_name + }, + { + "validate_singleton_src_w_first_name", + Validator_validate_singleton_src_w_first_name + }, + { + "validate_singleton_second_w_first_name", + Validator_validate_singleton_second_w_first_name + }, + { + "not_wildcard", + Validator_not_wildcard + }, + { + "not_first_wildcard", + Validator_not_first_wildcard + }, + { + "not_second_wildcard", + Validator_not_second_wildcard + }, + { + "not_wildcard_id", + Validator_not_wildcard_id + }, + { + "not_wildcard_first_pair", + Validator_not_wildcard_first_pair + }, + { + "not_wildcard_second_pair", + Validator_not_wildcard_second_pair + }, + { + "validate_or_same_type", + Validator_validate_or_same_type + }, + { + "validate_or_different_types", + Validator_validate_or_different_types + }, + { + "validate_or_different_types", + Validator_validate_or_different_types + }, + { + "validate_or_different_types_1_and_2_or", + Validator_validate_or_different_types_1_and_2_or + }, + { + "validate_trav_isa_w_wildcard", + Validator_validate_trav_isa_w_wildcard + }, + { + "validate_trav_isa_w_any", + Validator_validate_trav_isa_w_any + }, + { + "validate_custom_trav_w_inherit_id", + Validator_validate_custom_trav_w_inherit_id + }, + { + "validate_custom_trav_w_inherit_id_w_self_up", + Validator_validate_custom_trav_w_inherit_id_w_self_up + }, + { + "validate_custom_trav_w_inherit_id_w_up", + Validator_validate_custom_trav_w_inherit_id_w_up + }, + { + "validate_simple_1_term_is_cacheable", + Validator_validate_simple_1_term_is_cacheable + }, + { + "validate_simple_1_term_pair_is_cacheable", + Validator_validate_simple_1_term_pair_is_cacheable + }, + { + "validate_simple_1_term_pair_recycled_is_cacheable", + Validator_validate_simple_1_term_pair_recycled_is_cacheable + }, + { + "validate_simple_1_term_tag_is_cacheable", + Validator_validate_simple_1_term_tag_is_cacheable + }, + { + "validate_simple_2_term_is_cacheable", + Validator_validate_simple_2_term_is_cacheable + }, + { + "validate_simple_w_can_inherit", + Validator_validate_simple_w_can_inherit + }, + { + "validate_simple_w_can_toggle", + Validator_validate_simple_w_can_toggle + }, + { + "validate_simple_w_sparse", + Validator_validate_simple_w_sparse + }, + { + "validate_simple_w_union", + Validator_validate_simple_w_union + }, + { + "validate_simple_w_union_pair", + Validator_validate_simple_w_union_pair + }, + { + "validate_simple_w_transitive", + Validator_validate_simple_w_transitive + }, + { + "validate_simple_w_transitive_pair", + Validator_validate_simple_w_transitive_pair + }, + { + "validate_simple_w_reflexive", + Validator_validate_simple_w_reflexive + }, + { + "validate_simple_w_reflexive_pair", + Validator_validate_simple_w_reflexive_pair + }, + { + "validate_simple_w_inherited_component", + Validator_validate_simple_w_inherited_component + } +}; + +bake_test_case Parser_testcases[] = { + { + "resolve_this", + Parser_resolve_this + }, + { + "resolve_wildcard", + Parser_resolve_wildcard + }, + { + "resolve_any", + Parser_resolve_any + }, + { + "resolve_is_a", + Parser_resolve_is_a + }, + { + "0", + Parser_0 + }, + { + "component_implicit_subject", + Parser_component_implicit_subject + }, + { + "component_explicit_subject", + Parser_component_explicit_subject + }, + { + "component_explicit_subject_this", + Parser_component_explicit_subject_this + }, + { + "component_explicit_subject_this_by_name", + Parser_component_explicit_subject_this_by_name + }, + { + "component_explicit_subject_this_by_var_name", + Parser_component_explicit_subject_this_by_var_name + }, + { + "component_explicit_subject_wildcard", + Parser_component_explicit_subject_wildcard + }, + { + "component_explicit_subject_any", + Parser_component_explicit_subject_any + }, + { + "component_explicit_subject_0", + Parser_component_explicit_subject_0 + }, + { + "this_as_predicate", + Parser_this_as_predicate + }, + { + "this_var_as_predicate", + Parser_this_var_as_predicate + }, + { + "this_lowercase_var_as_predicate", + Parser_this_lowercase_var_as_predicate + }, + { + "this_as_object", + Parser_this_as_object + }, + { + "this_var_as_object", + Parser_this_var_as_object + }, + { + "pair_implicit_subject", + Parser_pair_implicit_subject + }, + { + "pair_implicit_subject_wildcard_pred", + Parser_pair_implicit_subject_wildcard_pred + }, + { + "pair_implicit_subject_wildcard_obj", + Parser_pair_implicit_subject_wildcard_obj + }, + { + "pair_implicit_subject_any_pred", + Parser_pair_implicit_subject_any_pred + }, + { + "pair_implicit_subject_any_obj", + Parser_pair_implicit_subject_any_obj + }, + { + "pair_implicit_subject_this_pred", + Parser_pair_implicit_subject_this_pred + }, + { + "pair_implicit_subject_this_obj", + Parser_pair_implicit_subject_this_obj + }, + { + "pair_implicit_subject_pred_w_self", + Parser_pair_implicit_subject_pred_w_self + }, + { + "pair_implicit_subject_obj_w_self", + Parser_pair_implicit_subject_obj_w_self + }, + { + "pair_implicit_subject_pred_w_up", + Parser_pair_implicit_subject_pred_w_up + }, + { + "pair_implicit_subject_obj_w_up", + Parser_pair_implicit_subject_obj_w_up + }, + { + "pair_implicit_subject_pred_w_self_up", + Parser_pair_implicit_subject_pred_w_self_up + }, + { + "pair_implicit_subject_obj_w_self_up", + Parser_pair_implicit_subject_obj_w_self_up + }, + { + "pair_implicit_subject_pred_w_invalid_flags", + Parser_pair_implicit_subject_pred_w_invalid_flags + }, + { + "pair_implicit_subject_obj_w_invalid_flags", + Parser_pair_implicit_subject_obj_w_invalid_flags + }, + { + "pair_explicit_subject", + Parser_pair_explicit_subject + }, + { + "pair_explicit_subject_this", + Parser_pair_explicit_subject_this + }, + { + "pair_explicit_subject_this_by_name", + Parser_pair_explicit_subject_this_by_name + }, + { + "pair_explicit_subject_this_by_var_name", + Parser_pair_explicit_subject_this_by_var_name + }, + { + "pair_explicit_subject_wildcard_pred", + Parser_pair_explicit_subject_wildcard_pred + }, + { + "pair_explicit_subject_wildcard_subj", + Parser_pair_explicit_subject_wildcard_subj + }, + { + "pair_explicit_subject_wildcard_obj", + Parser_pair_explicit_subject_wildcard_obj + }, + { + "pair_implicit_subject_0_name_object", + Parser_pair_implicit_subject_0_name_object + }, + { + "pair_implicit_subject_0_name_not_found_object", + Parser_pair_implicit_subject_0_name_not_found_object + }, + { + "pair_implicit_subject_0_digit_object", + Parser_pair_implicit_subject_0_digit_object + }, + { + "pair_explicit_subject_0_object", + Parser_pair_explicit_subject_0_object + }, + { + "pair_explicit_subject_0", + Parser_pair_explicit_subject_0 + }, + { + "in_component_implicit_subject", + Parser_in_component_implicit_subject + }, + { + "in_component_explicit_subject", + Parser_in_component_explicit_subject + }, + { + "in_pair_implicit_subject", + Parser_in_pair_implicit_subject + }, + { + "in_pair_explicit_subject", + Parser_in_pair_explicit_subject + }, + { + "inout_component_implicit_subject", + Parser_inout_component_implicit_subject + }, + { + "inout_component_explicit_subject", + Parser_inout_component_explicit_subject + }, + { + "inout_pair_implicit_subject", + Parser_inout_pair_implicit_subject + }, + { + "inout_pair_explicit_subject", + Parser_inout_pair_explicit_subject + }, + { + "out_component_implicit_subject", + Parser_out_component_implicit_subject + }, + { + "out_component_explicit_subject", + Parser_out_component_explicit_subject + }, + { + "out_pair_implicit_subject", + Parser_out_pair_implicit_subject + }, + { + "out_pair_explicit_subject", + Parser_out_pair_explicit_subject + }, + { + "inout_filter_component", + Parser_inout_filter_component + }, + { + "inout_w_not_operator", + Parser_inout_w_not_operator + }, + { + "inout_w_optional_operator", + Parser_inout_w_optional_operator + }, + { + "component_singleton", + Parser_component_singleton + }, + { + "this_singleton", + Parser_this_singleton + }, + { + "component_implicit_no_subject", + Parser_component_implicit_no_subject + }, + { + "component_explicit_no_subject", + Parser_component_explicit_no_subject + }, + { + "pair_no_subject", + Parser_pair_no_subject + }, + { + "variable_single_char", + Parser_variable_single_char + }, + { + "variable_multi_char", + Parser_variable_multi_char + }, + { + "variable_multi_char_w_underscore", + Parser_variable_multi_char_w_underscore + }, + { + "variable_multi_char_w_number", + Parser_variable_multi_char_w_number + }, + { + "variable_multi_char_not_allcaps", + Parser_variable_multi_char_not_allcaps + }, + { + "pred_var", + Parser_pred_var + }, + { + "obj_var", + Parser_obj_var + }, + { + "component_not", + Parser_component_not + }, + { + "pair_implicit_subject_not", + Parser_pair_implicit_subject_not + }, + { + "pair_explicit_subject_not", + Parser_pair_explicit_subject_not + }, + { + "2_component_not", + Parser_2_component_not + }, + { + "2_component_not_no_space", + Parser_2_component_not_no_space + }, + { + "component_optional", + Parser_component_optional + }, + { + "2_component_optional", + Parser_2_component_optional + }, + { + "2_component_optional_no_space", + Parser_2_component_optional_no_space + }, + { + "from_and", + Parser_from_and + }, + { + "from_or", + Parser_from_or + }, + { + "from_not", + Parser_from_not + }, + { + "pair_implicit_subject_optional", + Parser_pair_implicit_subject_optional + }, + { + "pair_explicit_subject_optional", + Parser_pair_explicit_subject_optional + }, + { + "pred_implicit_subject_w_role", + Parser_pred_implicit_subject_w_role + }, + { + "pred_explicit_subject_w_role", + Parser_pred_explicit_subject_w_role + }, + { + "pred_no_subject_w_role", + Parser_pred_no_subject_w_role + }, + { + "pair_implicit_subject_w_role", + Parser_pair_implicit_subject_w_role + }, + { + "pair_explicit_subject_w_role", + Parser_pair_explicit_subject_w_role + }, + { + "inout_role_pred_implicit_subject", + Parser_inout_role_pred_implicit_subject + }, + { + "inout_role_pred_no_subject", + Parser_inout_role_pred_no_subject + }, + { + "inout_role_pred_explicit_subject", + Parser_inout_role_pred_explicit_subject + }, + { + "inout_role_pair_implicit_subject", + Parser_inout_role_pair_implicit_subject + }, + { + "inout_role_pair_explicit_subject", + Parser_inout_role_pair_explicit_subject + }, + { + "2_pred_implicit_subject", + Parser_2_pred_implicit_subject + }, + { + "2_pred_no_subject", + Parser_2_pred_no_subject + }, + { + "2_pred_explicit_subject", + Parser_2_pred_explicit_subject + }, + { + "2_pair_implicit_subject", + Parser_2_pair_implicit_subject + }, + { + "2_pair_explicit_subject", + Parser_2_pair_explicit_subject + }, + { + "2_pred_role", + Parser_2_pred_role + }, + { + "2_pair_implicit_subj_role", + Parser_2_pair_implicit_subj_role + }, + { + "2_pair_explicit_subj_role", + Parser_2_pair_explicit_subj_role + }, + { + "2_or_pred_implicit_subj", + Parser_2_or_pred_implicit_subj + }, + { + "2_or_pred_explicit_subj", + Parser_2_or_pred_explicit_subj + }, + { + "2_or_pair_implicit_subj", + Parser_2_or_pair_implicit_subj + }, + { + "2_or_pair_explicit_subj", + Parser_2_or_pair_explicit_subj + }, + { + "2_or_pred_inout", + Parser_2_or_pred_inout + }, + { + "2_or_no_space", + Parser_2_or_no_space + }, + { + "2_or_w_not_1st_arg", + Parser_2_or_w_not_1st_arg + }, + { + "2_or_w_not_2nd_arg", + Parser_2_or_w_not_2nd_arg + }, + { + "2_or_w_optional_1st_arg", + Parser_2_or_w_optional_1st_arg + }, + { + "2_or_w_optional_2nd_arg", + Parser_2_or_w_optional_2nd_arg + }, + { + "1_entity_id_pred_implicit_subj", + Parser_1_entity_id_pred_implicit_subj + }, + { + "1_entity_id_pred_no_subj", + Parser_1_entity_id_pred_no_subj + }, + { + "1_entity_id_pred_explicit_subj", + Parser_1_entity_id_pred_explicit_subj + }, + { + "1_entity_id_pair_implicit_subj", + Parser_1_entity_id_pair_implicit_subj + }, + { + "1_entity_id_pair_explicit_subj", + Parser_1_entity_id_pair_explicit_subj + }, + { + "1_digit_pred_implicit_subj", + Parser_1_digit_pred_implicit_subj + }, + { + "1_digit_pred_no_subj", + Parser_1_digit_pred_no_subj + }, + { + "1_digit_pred_explicit_subj", + Parser_1_digit_pred_explicit_subj + }, + { + "1_digit_pair_implicit_subj", + Parser_1_digit_pair_implicit_subj + }, + { + "1_digit_pair_explicit_subj", + Parser_1_digit_pair_explicit_subj + }, + { + "pred_implicit_subject_self", + Parser_pred_implicit_subject_self + }, + { + "pred_implicit_subject_superset", + Parser_pred_implicit_subject_superset + }, + { + "pred_implicit_subject_superset_inclusive", + Parser_pred_implicit_subject_superset_inclusive + }, + { + "pred_implicit_subject_superset_cascade", + Parser_pred_implicit_subject_superset_cascade + }, + { + "pred_implicit_subject_superset_inclusive_cascade", + Parser_pred_implicit_subject_superset_inclusive_cascade + }, + { + "pred_implicit_subject_implicit_superset_cascade", + Parser_pred_implicit_subject_implicit_superset_cascade + }, + { + "pred_implicit_subject_implicit_superset_inclusive_cascade", + Parser_pred_implicit_subject_implicit_superset_inclusive_cascade + }, + { + "pred_implicit_subject_implicit_superset_cascade_w_rel", + Parser_pred_implicit_subject_implicit_superset_cascade_w_rel + }, + { + "pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel", + Parser_pred_implicit_subject_implicit_superset_inclusive_cascade_w_rel + }, + { + "pred_implicit_subject_superset_childof", + Parser_pred_implicit_subject_superset_childof + }, + { + "pred_implicit_subject_cascade_superset_childof", + Parser_pred_implicit_subject_cascade_superset_childof + }, + { + "pred_implicit_subject_superset_cascade_childof", + Parser_pred_implicit_subject_superset_cascade_childof + }, + { + "pred_implicit_subject_superset_cascade_childof_optional", + Parser_pred_implicit_subject_superset_cascade_childof_optional + }, + { + "expr_w_symbol", + Parser_expr_w_symbol + }, + { + "expr_w_newline", + Parser_expr_w_newline + }, + { + "subj_entity_w_explicit_self", + Parser_subj_entity_w_explicit_self + }, + { + "subj_entity_w_explicit_self_superset", + Parser_subj_entity_w_explicit_self_superset + }, + { + "subj_entity_w_explicit_superset_relation", + Parser_subj_entity_w_explicit_superset_relation + }, + { + "subj_entity_w_explicit_self_superset_relation", + Parser_subj_entity_w_explicit_self_superset_relation + }, + { + "obj_entity_w_explicit_self", + Parser_obj_entity_w_explicit_self + }, + { + "obj_entity_w_explicit_self_superset", + Parser_obj_entity_w_explicit_self_superset + }, + { + "obj_entity_w_explicit_superset_relation", + Parser_obj_entity_w_explicit_superset_relation + }, + { + "obj_entity_w_explicit_self_superset_relation", + Parser_obj_entity_w_explicit_self_superset_relation + }, + { + "pred_entity_w_explicit_self", + Parser_pred_entity_w_explicit_self + }, + { + "pred_entity_w_explicit_self_superset", + Parser_pred_entity_w_explicit_self_superset + }, + { + "pred_entity_no_args_w_explicit_self", + Parser_pred_entity_no_args_w_explicit_self + }, + { + "pred_entity_no_args_w_explicit_self_superset", + Parser_pred_entity_no_args_w_explicit_self_superset + }, + { + "pred_entity_no_args_2_terms_w_explicit_self", + Parser_pred_entity_no_args_2_terms_w_explicit_self + }, + { + "pred_entity_no_args_2_terms_w_explicit_self_superset", + Parser_pred_entity_no_args_2_terms_w_explicit_self_superset + }, + { + "newline", + Parser_newline + }, + { + "2_newlines", + Parser_2_newlines + }, + { + "3_newlines", + Parser_3_newlines + }, + { + "space", + Parser_space + }, + { + "2_spaces", + Parser_2_spaces + }, + { + "trailing_newline", + Parser_trailing_newline + }, + { + "2_trailing_newlines", + Parser_2_trailing_newlines + }, + { + "trailing_space", + Parser_trailing_space + }, + { + "2_trailing_spaces", + Parser_2_trailing_spaces + }, + { + "template_type", + Parser_template_type + }, + { + "template_type_nested", + Parser_template_type_nested + }, + { + "template_type_multiple_args", + Parser_template_type_multiple_args + }, + { + "template_type_multiple_args_nested", + Parser_template_type_multiple_args_nested + }, + { + "template_type_unbalanced_open", + Parser_template_type_unbalanced_open + }, + { + "template_type_unbalanced_close", + Parser_template_type_unbalanced_close + }, + { + "predicate_w_parens", + Parser_predicate_w_parens + }, + { + "not_alive_pred", + Parser_not_alive_pred + }, + { + "not_alive_subj", + Parser_not_alive_subj + }, + { + "not_alive_obj", + Parser_not_alive_obj + }, + { + "this_subj_var_kind", + Parser_this_subj_var_kind + }, + { + "this_obj_var_kind", + Parser_this_obj_var_kind + }, + { + "this_subj_obj_var_kind", + Parser_this_subj_obj_var_kind + }, + { + "var_w_name", + Parser_var_w_name + }, + { + "entity_pred_no_name", + Parser_entity_pred_no_name + }, + { + "entity_subj_no_name", + Parser_entity_subj_no_name + }, + { + "entity_obj_no_name", + Parser_entity_obj_no_name + }, + { + "this_pred_no_name", + Parser_this_pred_no_name + }, + { + "this_subj_no_name", + Parser_this_subj_no_name + }, + { + "this_obj_no_name", + Parser_this_obj_no_name + }, + { + "invalid_variable_only", + Parser_invalid_variable_only + }, + { + "oneof_self_pred_w_relative_obj", + Parser_oneof_self_pred_w_relative_obj + }, + { + "oneof_other_pred_w_relative_obj", + Parser_oneof_other_pred_w_relative_obj + }, + { + "oneof_self_pred_w_invalid_obj", + Parser_oneof_self_pred_w_invalid_obj + }, + { + "oneof_other_pred_w_invalid_obj", + Parser_oneof_other_pred_w_invalid_obj + }, + { + "oneof_w_other_entity_w_same_name", + Parser_oneof_w_other_entity_w_same_name + }, + { + "oneof_w_other_entity_w_same_name_w_set_scope", + Parser_oneof_w_other_entity_w_same_name_w_set_scope + }, + { + "oneof_w_wildcard", + Parser_oneof_w_wildcard + }, + { + "oneof_w_any", + Parser_oneof_w_any + }, + { + "oneof_w_fullpath", + Parser_oneof_w_fullpath + }, + { + "pair_implicit_src_missing_rel", + Parser_pair_implicit_src_missing_rel + }, + { + "pair_implicit_src_missing_obj", + Parser_pair_implicit_src_missing_obj + }, + { + "pair_explicit_src_missing_src", + Parser_pair_explicit_src_missing_src + }, + { + "pair_explicit_src_missing_obj", + Parser_pair_explicit_src_missing_obj + }, + { + "eq_id", + Parser_eq_id + }, + { + "eq_id_var", + Parser_eq_id_var + }, + { + "eq_var_id", + Parser_eq_var_id + }, + { + "eq_var_table", + Parser_eq_var_table + }, + { + "eq_var", + Parser_eq_var + }, + { + "neq_id", + Parser_neq_id + }, + { + "neq_id_var", + Parser_neq_id_var + }, + { + "neq_var_id", + Parser_neq_var_id + }, + { + "neq_var_table", + Parser_neq_var_table + }, + { + "neq_var", + Parser_neq_var + }, + { + "eq_name", + Parser_eq_name + }, + { + "eq_name_var", + Parser_eq_name_var + }, + { + "eq_var_name", + Parser_eq_var_name + }, + { + "neq_name", + Parser_neq_name + }, + { + "neq_name_var", + Parser_neq_name_var + }, + { + "neq_var_name", + Parser_neq_var_name + }, + { + "match_name", + Parser_match_name + }, + { + "match_name_var", + Parser_match_name_var + }, + { + "match_var_name", + Parser_match_var_name + }, + { + "match_var", + Parser_match_var + }, + { + "nmatch_name", + Parser_nmatch_name + }, + { + "nmatch_name_var", + Parser_nmatch_name_var + }, + { + "nmatch_var_name", + Parser_nmatch_var_name + }, + { + "eq_same_var", + Parser_eq_same_var + }, + { + "neq_same_var", + Parser_neq_same_var + }, + { + "eq_same_var_this", + Parser_eq_same_var_this + }, + { + "neq_same_var_this", + Parser_neq_same_var_this + }, + { + "eq_w_not", + Parser_eq_w_not + }, + { + "neq_w_not", + Parser_neq_w_not + }, + { + "match_w_not", + Parser_match_w_not + }, + { + "eq_w_optional", + Parser_eq_w_optional + }, + { + "neq_w_optional", + Parser_neq_w_optional + }, + { + "match_w_optional", + Parser_match_w_optional + }, + { + "eq_0", + Parser_eq_0 + }, + { + "neq_0", + Parser_neq_0 + }, + { + "eq_wildcard", + Parser_eq_wildcard + }, + { + "neq_wildcard", + Parser_neq_wildcard + }, + { + "eq_any", + Parser_eq_any + }, + { + "neq_any", + Parser_neq_any + }, + { + "query_scope_1_term", + Parser_query_scope_1_term + }, + { + "query_scope_1_term_spaces", + Parser_query_scope_1_term_spaces + }, + { + "query_scope_2_terms", + Parser_query_scope_2_terms + }, + { + "query_nested_scope", + Parser_query_nested_scope + }, + { + "query_nested_scope_spaces", + Parser_query_nested_scope_spaces + }, + { + "query_scope_unbalanced", + Parser_query_scope_unbalanced + }, + { + "query_not_scope", + Parser_query_not_scope + }, + { + "query_empty_scope", + Parser_query_empty_scope + }, + { + "query_scope_newline_after_open", + Parser_query_scope_newline_after_open + }, + { + "query_scope_newline_after_close", + Parser_query_scope_newline_after_close + }, + { + "query_term_after_scope", + Parser_query_term_after_scope + }, + { + "override_tag", + Parser_override_tag + }, + { + "override_pair", + Parser_override_pair + }, + { + "pair_3_args", + Parser_pair_3_args + }, + { + "pair_3_args_implicit_this", + Parser_pair_3_args_implicit_this + }, + { + "pair_4_args", + Parser_pair_4_args + }, + { + "pair_4_args_implicit_this", + Parser_pair_4_args_implicit_this + }, + { + "pair_3_args_2_terms", + Parser_pair_3_args_2_terms + }, + { + "pair_3_args_this_tgt", + Parser_pair_3_args_this_tgt + }, + { + "pair_3_args_2_terms_this_tgt", + Parser_pair_3_args_2_terms_this_tgt + }, + { + "pair_3_args_2_terms_this_tgt_implicit_this", + Parser_pair_3_args_2_terms_this_tgt_implicit_this + }, + { + "pair_3_or_args", + Parser_pair_3_or_args + }, + { + "pair_3_or_args_implicit_this", + Parser_pair_3_or_args_implicit_this + }, + { + "pair_4_or_args", + Parser_pair_4_or_args + }, + { + "pair_4_or_args_implicit_this", + Parser_pair_4_or_args_implicit_this + }, + { + "pair_3_args_w_vars", + Parser_pair_3_args_w_vars + }, + { + "pair_4_args_w_vars", + Parser_pair_4_args_w_vars + }, + { + "pair_3_args_w_vars_w_term_after", + Parser_pair_3_args_w_vars_w_term_after + }, + { + "pair_4_args_w_vars_w_term_after", + Parser_pair_4_args_w_vars_w_term_after + }, + { + "pair_or_before_and_oper", + Parser_pair_or_before_and_oper + }, + { + "pair_and_before_or_oper", + Parser_pair_and_before_or_oper + }, + { + "cascade_desc", + Parser_cascade_desc + }, + { + "newline_after_inout", + Parser_newline_after_inout + }, + { + "newline_after_term_open", + Parser_newline_after_term_open + }, + { + "newline_after_term_src", + Parser_newline_after_term_src + }, + { + "newline_after_term_src_pair", + Parser_newline_after_term_src_pair + }, + { + "newline_after_term_pair_comma", + Parser_newline_after_term_pair_comma + }, + { + "newline_after_term_pair_second", + Parser_newline_after_term_pair_second + }, + { + "tag_w_space_implicit_this", + Parser_tag_w_space_implicit_this + }, + { + "tag_w_space", + Parser_tag_w_space + }, + { + "pair_first_w_space_implicit_this", + Parser_pair_first_w_space_implicit_this + }, + { + "pair_first_w_space", + Parser_pair_first_w_space + }, + { + "pair_second_w_space_implicit_this", + Parser_pair_second_w_space_implicit_this + }, + { + "pair_second_w_space", + Parser_pair_second_w_space + }, + { + "pair_src_w_space", + Parser_pair_src_w_space + }, + { + "empty_term", + Parser_empty_term + }, + { + "empty_term_w_space", + Parser_empty_term_w_space + }, + { + "empty_term_w_newline", + Parser_empty_term_w_newline + }, + { + "mixed_1_desc_and_1_expr", + Parser_mixed_1_desc_and_1_expr + }, + { + "mixed_2_desc_and_1_expr", + Parser_mixed_2_desc_and_1_expr + }, + { + "mixed_1_desc_and_2_expr", + Parser_mixed_1_desc_and_2_expr + }, + { + "not_wildcard", + Parser_not_wildcard + }, + { + "not_first_wildcard", + Parser_not_first_wildcard + }, + { + "not_second_wildcard", + Parser_not_second_wildcard + }, + { + "unterminated_paren", + Parser_unterminated_paren + }, + { + "unterminated_after_first_paren", + Parser_unterminated_after_first_paren + }, + { + "unterminated_after_src_var", + Parser_unterminated_after_src_var + }, + { + "eq_name_existing_entity", + Parser_eq_name_existing_entity + }, + { + "match_existing_entity", + Parser_match_existing_entity + }, + { + "match_wildcard", + Parser_match_wildcard + }, + { + "match_any", + Parser_match_any + }, + { + "match_variable_oper", + Parser_match_variable_oper + }, + { + "escaped_identifier", + Parser_escaped_identifier + }, + { + "escaped_identifier_first", + Parser_escaped_identifier_first + }, + { + "escaped_identifier_second", + Parser_escaped_identifier_second + }, + { + "n_tokens_test", + Parser_n_tokens_test + } +}; + +bake_test_case Basic_testcases[] = { + { + "0_query", + Basic_0_query + }, + { + "1_fact_w_tag", + Basic_1_fact_w_tag + }, + { + "1_fact_w_component", + Basic_1_fact_w_component + }, + { + "1_fact_w_tag_pair", + Basic_1_fact_w_tag_pair + }, + { + "1_fact_w_component_pair", + Basic_1_fact_w_component_pair + }, + { + "2_facts_same_src_w_tag", + Basic_2_facts_same_src_w_tag + }, + { + "2_facts_same_src_w_component", + Basic_2_facts_same_src_w_component + }, + { + "2_facts_same_src_w_tag_pair", + Basic_2_facts_same_src_w_tag_pair + }, + { + "2_facts_same_src_w_component_pair", + Basic_2_facts_same_src_w_component_pair + }, + { + "2_facts_other_src_w_tag", + Basic_2_facts_other_src_w_tag + }, + { + "2_facts_other_src_w_component", + Basic_2_facts_other_src_w_component + }, + { + "2_facts_other_src_w_tag_pair", + Basic_2_facts_other_src_w_tag_pair + }, + { + "2_facts_other_src_w_component_pair", + Basic_2_facts_other_src_w_component_pair + }, + { + "1_fact_w_any", + Basic_1_fact_w_any + }, + { + "1_fact_w_pair_any_tgt", + Basic_1_fact_w_pair_any_tgt + }, + { + "1_fact_w_pair_any_rel", + Basic_1_fact_w_pair_any_rel + }, + { + "1_fact_w_pair_any_rel_tgt", + Basic_1_fact_w_pair_any_rel_tgt + }, + { + "2_facts_same_src_w_any", + Basic_2_facts_same_src_w_any + }, + { + "2_facts_same_src_w_pair_any_tgt", + Basic_2_facts_same_src_w_pair_any_tgt + }, + { + "2_facts_same_src_w_pair_any_rel", + Basic_2_facts_same_src_w_pair_any_rel + }, + { + "2_facts_same_src_w_pair_any_rel_tgt", + Basic_2_facts_same_src_w_pair_any_rel_tgt + }, + { + "2_facts_other_src_w_any", + Basic_2_facts_other_src_w_any + }, + { + "2_facts_other_src_w_pair_any_tgt", + Basic_2_facts_other_src_w_pair_any_tgt + }, + { + "2_facts_other_src_w_pair_any_rel", + Basic_2_facts_other_src_w_pair_any_rel + }, + { + "2_facts_other_src_w_pair_any_rel_tgt", + Basic_2_facts_other_src_w_pair_any_rel_tgt + }, + { + "1_this_src_w_tag", + Basic_1_this_src_w_tag + }, + { + "1_this_src_w_component", + Basic_1_this_src_w_component + }, + { + "1_this_src_w_tag_pair", + Basic_1_this_src_w_tag_pair + }, + { + "1_this_src_w_component_pair", + Basic_1_this_src_w_component_pair + }, + { + "1_this_src_w_tag_2_tables", + Basic_1_this_src_w_tag_2_tables + }, + { + "1_this_src_w_component_2_tables", + Basic_1_this_src_w_component_2_tables + }, + { + "1_this_src_w_tag_pair_2_tables", + Basic_1_this_src_w_tag_pair_2_tables + }, + { + "1_this_src_w_component_pair_2_tables", + Basic_1_this_src_w_component_pair_2_tables + }, + { + "2_this_src_w_tag", + Basic_2_this_src_w_tag + }, + { + "2_this_src_w_component", + Basic_2_this_src_w_component + }, + { + "2_this_src_ent_src_w_tag", + Basic_2_this_src_ent_src_w_tag + }, + { + "2_this_src_ent_src_w_component", + Basic_2_this_src_ent_src_w_component + }, + { + "2_ent_src_this_src_w_tag", + Basic_2_ent_src_this_src_w_tag + }, + { + "2_ent_src_this_src_w_component", + Basic_2_ent_src_this_src_w_component + }, + { + "recycled_tag", + Basic_recycled_tag + }, + { + "recycled_src", + Basic_recycled_src + }, + { + "recycled_pair_rel", + Basic_recycled_pair_rel + }, + { + "recycled_pair_tgt", + Basic_recycled_pair_tgt + }, + { + "this_src_w_wildcard", + Basic_this_src_w_wildcard + }, + { + "this_src_w_pair_rel_wildcard", + Basic_this_src_w_pair_rel_wildcard + }, + { + "this_src_w_pair_tgt_wildcard", + Basic_this_src_w_pair_tgt_wildcard + }, + { + "this_src_w_pair_rel_tgt_wildcard", + Basic_this_src_w_pair_rel_tgt_wildcard + }, + { + "this_src_w_any", + Basic_this_src_w_any + }, + { + "this_src_w_any_written", + Basic_this_src_w_any_written + }, + { + "this_src_w_pair_rel_any", + Basic_this_src_w_pair_rel_any + }, + { + "this_src_w_pair_tgt_any", + Basic_this_src_w_pair_tgt_any + }, + { + "this_src_w_pair_tgt_any_n_tgts", + Basic_this_src_w_pair_tgt_any_n_tgts + }, + { + "this_src_w_pair_tgt_any_n_tgts_written", + Basic_this_src_w_pair_tgt_any_n_tgts_written + }, + { + "this_src_w_pair_rel_tgt_any", + Basic_this_src_w_pair_rel_tgt_any + }, + { + "ent_src_w_wildcard", + Basic_ent_src_w_wildcard + }, + { + "ent_src_w_pair_rel_wildcard", + Basic_ent_src_w_pair_rel_wildcard + }, + { + "ent_src_w_pair_tgt_wildcard", + Basic_ent_src_w_pair_tgt_wildcard + }, + { + "ent_src_w_pair_rel_tgt_wildcard", + Basic_ent_src_w_pair_rel_tgt_wildcard + }, + { + "1_wildcard_src", + Basic_1_wildcard_src + }, + { + "1_wildcard_src_w_pair", + Basic_1_wildcard_src_w_pair + }, + { + "2_wildcard_src", + Basic_2_wildcard_src + }, + { + "2_wildcard_src_w_pair", + Basic_2_wildcard_src_w_pair + }, + { + "1_wildcard_src_w_pair_tgt_var", + Basic_1_wildcard_src_w_pair_tgt_var + }, + { + "1_wildcard_src_w_pair_rel_var", + Basic_1_wildcard_src_w_pair_rel_var + }, + { + "1_wildcard_src_w_pair_tgt_this", + Basic_1_wildcard_src_w_pair_tgt_this + }, + { + "1_wildcard_src_w_pair_rel_this", + Basic_1_wildcard_src_w_pair_rel_this + }, + { + "1_any_src", + Basic_1_any_src + }, + { + "1_any_src_w_pair", + Basic_1_any_src_w_pair + }, + { + "2_any_src", + Basic_2_any_src + }, + { + "2_any_src_w_pair", + Basic_2_any_src_w_pair + }, + { + "1_any_src_w_pair_tgt_var", + Basic_1_any_src_w_pair_tgt_var + }, + { + "1_any_src_w_pair_rel_var", + Basic_1_any_src_w_pair_rel_var + }, + { + "1_any_src_w_pair_tgt_this", + Basic_1_any_src_w_pair_tgt_this + }, + { + "1_any_src_w_pair_rel_this", + Basic_1_any_src_w_pair_rel_this + }, + { + "not_any", + Basic_not_any + }, + { + "rule_w_iter_next", + Basic_rule_w_iter_next + }, + { + "empty_rule", + Basic_empty_rule + }, + { + "invalid_rule", + Basic_invalid_rule + }, + { + "instanced_table_src", + Basic_instanced_table_src + }, + { + "instanced_entity_src", + Basic_instanced_entity_src + }, + { + "instanced_mixed_src", + Basic_instanced_mixed_src + }, + { + "instanced_inherited", + Basic_instanced_inherited + }, + { + "in_term", + Basic_in_term + }, + { + "out_term", + Basic_out_term + }, + { + "inout_term", + Basic_inout_term + }, + { + "nodata_term", + Basic_nodata_term + }, + { + "find_this_lowercase", + Basic_find_this_lowercase + }, + { + "find_this_uppercase", + Basic_find_this_uppercase + }, + { + "find_this_tgt_lowercase", + Basic_find_this_tgt_lowercase + }, + { + "find_this_tgt_uppercase", + Basic_find_this_tgt_uppercase + }, + { + "get_filter", + Basic_get_filter + }, + { + "iter_empty_source", + Basic_iter_empty_source + }, + { + "iter_empty_source_2_terms", + Basic_iter_empty_source_2_terms + }, + { + "iter_empty_source_wildcard", + Basic_iter_empty_source_wildcard + }, + { + "iter_empty_source_pair", + Basic_iter_empty_source_pair + }, + { + "iter_empty_source_pair_wildcard", + Basic_iter_empty_source_pair_wildcard + }, + { + "iter_empty_source_2_terms_pair", + Basic_iter_empty_source_2_terms_pair + }, + { + "iter_empty_source_2_terms_mixed", + Basic_iter_empty_source_2_terms_mixed + }, + { + "iter_empty_source_2_terms_mixed_pair", + Basic_iter_empty_source_2_terms_mixed_pair + }, + { + "iter_empty_source_2_terms_mixed_pair_wildcard", + Basic_iter_empty_source_2_terms_mixed_pair_wildcard + }, + { + "this_var_w_empty_entity", + Basic_this_var_w_empty_entity + }, + { + "match_disabled", + Basic_match_disabled + }, + { + "match_prefab", + Basic_match_prefab + }, + { + "match_disabled_prefab", + Basic_match_disabled_prefab + }, + { + "match_disabled_this_tgt", + Basic_match_disabled_this_tgt + }, + { + "match_prefab_this_tgt", + Basic_match_prefab_this_tgt + }, + { + "match_disabled_prefab_this_tgt", + Basic_match_disabled_prefab_this_tgt + }, + { + "match_self_disabled", + Basic_match_self_disabled + }, + { + "match_self_prefab", + Basic_match_self_prefab + }, + { + "match_self_disabled_prefab", + Basic_match_self_disabled_prefab + }, + { + "match_optional_disabled", + Basic_match_optional_disabled + }, + { + "match_optional_prefab", + Basic_match_optional_prefab + }, + { + "match_optional_disabled_prefab", + Basic_match_optional_disabled_prefab + }, + { + "match_optional_disabled_this_tgt", + Basic_match_optional_disabled_this_tgt + }, + { + "match_optional_prefab_this_tgt", + Basic_match_optional_prefab_this_tgt + }, + { + "match_optional_disabled_prefab_this_tgt", + Basic_match_optional_disabled_prefab_this_tgt + }, + { + "match_optional_self_disabled", + Basic_match_optional_self_disabled + }, + { + "match_optional_self_prefab", + Basic_match_optional_self_prefab + }, + { + "match_optional_self_disabled_prefab", + Basic_match_optional_self_disabled_prefab + }, + { + "inout_none_first_term", + Basic_inout_none_first_term + }, + { + "inout_none_first_term_self_up", + Basic_inout_none_first_term_self_up + }, + { + "inout_none_second_term", + Basic_inout_none_second_term + }, + { + "inout_none_second_term_self_up", + Basic_inout_none_second_term_self_up + }, + { + "inout_none_singleton", + Basic_inout_none_singleton + }, + { + "inout_none_singleton_w_or", + Basic_inout_none_singleton_w_or + }, + { + "inout_none_component_w_or", + Basic_inout_none_component_w_or + }, + { + "no_data_rule", + Basic_no_data_rule + }, + { + "frame_offset", + Basic_frame_offset + }, + { + "frame_offset_no_data", + Basic_frame_offset_no_data + }, + { + "match_empty_tables", + Basic_match_empty_tables + }, + { + "match_empty_tables_no_data", + Basic_match_empty_tables_no_data + }, + { + "match_empty_tables_w_not", + Basic_match_empty_tables_w_not + }, + { + "match_empty_tables_w_wildcard", + Basic_match_empty_tables_w_wildcard + }, + { + "match_empty_tables_w_no_empty_tables", + Basic_match_empty_tables_w_no_empty_tables + }, + { + "match_empty_tables_trivial", + Basic_match_empty_tables_trivial + }, + { + "oneof_wildcard", + Basic_oneof_wildcard + }, + { + "oneof_any", + Basic_oneof_any + }, + { + "instanced_w_singleton", + Basic_instanced_w_singleton + }, + { + "instanced_w_base", + Basic_instanced_w_base + }, + { + "unknown_before_known", + Basic_unknown_before_known + }, + { + "unknown_before_known_after_or", + Basic_unknown_before_known_after_or + }, + { + "unknown_before_known_after_not", + Basic_unknown_before_known_after_not + }, + { + "unknown_before_known_after_optional", + Basic_unknown_before_known_after_optional + }, + { + "unknown_before_known_after_scope", + Basic_unknown_before_known_after_scope + }, + { + "2_trivial_mixed_2_tables", + Basic_2_trivial_mixed_2_tables + }, + { + "2_trivial_mixed_2_tables_component", + Basic_2_trivial_mixed_2_tables_component + }, + { + "2_trivial_mixed_2_tables_wildcard", + Basic_2_trivial_mixed_2_tables_wildcard + }, + { + "2_trivial_1_unused_id", + Basic_2_trivial_1_unused_id + }, + { + "2_trivial_one_regular", + Basic_2_trivial_one_regular + }, + { + "1_trivial_one_regular_one_trivial", + Basic_1_trivial_one_regular_one_trivial + }, + { + "one_regular_2_trivial", + Basic_one_regular_2_trivial + }, + { + "2_trivial_w_prefab", + Basic_2_trivial_w_prefab + }, + { + "3_trivial_w_prefab", + Basic_3_trivial_w_prefab + }, + { + "2_trivial_w_disabled", + Basic_2_trivial_w_disabled + }, + { + "3_trivial_w_disabled", + Basic_3_trivial_w_disabled + }, + { + "2_this_w_fixed_src", + Basic_2_this_w_fixed_src + }, + { + "2_fixed_src_w_this", + Basic_2_fixed_src_w_this + }, + { + "2_this_w_fixed_src_no_match_fixed", + Basic_2_this_w_fixed_src_no_match_fixed + }, + { + "2_fixed_src_w_this_no_match_fixed", + Basic_2_fixed_src_w_this_no_match_fixed + }, + { + "2_this_w_fixed_src_no_match_this", + Basic_2_this_w_fixed_src_no_match_this + }, + { + "2_fixed_src_w_this_no_match_this", + Basic_2_fixed_src_w_this_no_match_this + }, + { + "query_count_results", + Basic_query_count_results + }, + { + "query_count_entities", + Basic_query_count_entities + }, + { + "query_is_true", + Basic_query_is_true + }, + { + "implicit_cleanup_1_term", + Basic_implicit_cleanup_1_term + }, + { + "implicit_cleanup_2_terms", + Basic_implicit_cleanup_2_terms + }, + { + "implicit_cleanup_1_term_w_up", + Basic_implicit_cleanup_1_term_w_up + }, + { + "implicit_cleanup_2_terms_w_up", + Basic_implicit_cleanup_2_terms_w_up + }, + { + "implicit_cleanup_2_queries", + Basic_implicit_cleanup_2_queries + }, + { + "implicit_cleanup_2_queries_1_cleanup", + Basic_implicit_cleanup_2_queries_1_cleanup + }, + { + "iter_valid", + Basic_iter_valid + }, + { + "iter_frame_offset", + Basic_iter_frame_offset + }, + { + "iter_nested_1", + Basic_iter_nested_1 + }, + { + "iter_nested_2", + Basic_iter_nested_2 + }, + { + "iter_interleaved", + Basic_iter_interleaved + }, + { + "set_get_context", + Basic_set_get_context + }, + { + "set_get_binding_context", + Basic_set_get_binding_context + }, + { + "set_get_context_w_free", + Basic_set_get_context_w_free + }, + { + "set_get_binding_context_w_free", + Basic_set_get_binding_context_w_free + }, + { + "create_query_w_existing_entity", + Basic_create_query_w_existing_entity + }, + { + "create_query_w_existing_entity_different_term_count", + Basic_create_query_w_existing_entity_different_term_count + }, + { + "create_multi_component_query_w_existing_entity", + Basic_create_multi_component_query_w_existing_entity + }, + { + "delete_query_by_entity", + Basic_delete_query_by_entity + }, + { + "eval_count", + Basic_eval_count + }, + { + "no_results_after_delete_tree", + Basic_no_results_after_delete_tree + }, + { + "no_results_after_delete_tree_deferred", + Basic_no_results_after_delete_tree_deferred + }, + { + "add_on_self_ref", + Basic_add_on_self_ref + }, + { + "add_on_self_ref_by_name", + Basic_add_on_self_ref_by_name + }, + { + "delete_id_after_delete_query", + Basic_delete_id_after_delete_query + }, + { + "pair_sweep_tag", + Basic_pair_sweep_tag + }, + { + "pair_sweep_wildcard_first", + Basic_pair_sweep_wildcard_first + }, + { + "pair_sweep_wildcard_second", + Basic_pair_sweep_wildcard_second + }, + { + "create_w_entity_deferred", + Basic_create_w_entity_deferred + }, + { + "32_terms", + Basic_32_terms + }, + { + "33_terms_expr", + Basic_33_terms_expr + }, + { + "stage_query", + Basic_stage_query + }, + { + "world_query_w_stage_iter", + Basic_world_query_w_stage_iter + }, + { + "stage_query_w_nth_stage", + Basic_stage_query_w_nth_stage + }, + { + "world_query_w_nth_stage_iter", + Basic_world_query_w_nth_stage_iter + }, + { + "match_empty", + Basic_match_empty + }, + { + "match_new_empty", + Basic_match_new_empty + }, + { + "match_empty_w_component", + Basic_match_empty_w_component + }, + { + "match_new_empty_w_component", + Basic_match_new_empty_w_component + }, + { + "match_empty_w_ref", + Basic_match_empty_w_ref + }, + { + "match_new_empty_w_ref", + Basic_match_new_empty_w_ref + }, + { + "match_empty_w_order_by", + Basic_match_empty_w_order_by + }, + { + "match_new_empty_w_order_by", + Basic_match_new_empty_w_order_by + }, + { + "match_empty_w_bitset", + Basic_match_empty_w_bitset + }, + { + "default_query_flags", + Basic_default_query_flags + }, + { + "ref_fields_this", + Basic_ref_fields_this + }, + { + "ref_fields_static_src", + Basic_ref_fields_static_src + }, + { + "ref_fields_variable_src", + Basic_ref_fields_variable_src + }, + { + "ref_fields_up_src", + Basic_ref_fields_up_src + }, + { + "ref_fields_self_up_src", + Basic_ref_fields_self_up_src + }, + { + "0_src_match_nothing", + Basic_0_src_match_nothing + }, + { + "0_terms_match_nothing", + Basic_0_terms_match_nothing + }, + { + "any_record", + Basic_any_record + }, + { + "pair_rel_any_record", + Basic_pair_rel_any_record + }, + { + "pair_tgt_any_record", + Basic_pair_tgt_any_record + }, + { + "pair_any_any_record", + Basic_pair_any_any_record + }, + { + "written_any_record", + Basic_written_any_record + }, + { + "written_pair_rel_any_record", + Basic_written_pair_rel_any_record + }, + { + "written_pair_tgt_any_record", + Basic_written_pair_tgt_any_record + }, + { + "written_pair_any_any_record", + Basic_written_pair_any_any_record + }, + { + "pair_rel_any_record_component", + Basic_pair_rel_any_record_component + }, + { + "pair_tgt_any_record_component", + Basic_pair_tgt_any_record_component + }, + { + "entity_iteration_w_match_empty_tables", + Basic_entity_iteration_w_match_empty_tables + }, + { + "get_cache_query_uncached", + Basic_get_cache_query_uncached + }, + { + "get_cache_query_cached", + Basic_get_cache_query_cached + }, + { + "get_cache_query_partially_cached", + Basic_get_cache_query_partially_cached + } +}; + +bake_test_case Combinations_testcases[] = { + { + "singleton_and_self_and_self", + Combinations_singleton_and_self_and_self + }, + { + "self_and_singleton_and_self", + Combinations_self_and_singleton_and_self + }, + { + "self_and_self_and_singleton", + Combinations_self_and_self_and_singleton + }, + { + "singleton_and_trav_and_self", + Combinations_singleton_and_trav_and_self + }, + { + "trav_and_singleton_and_self", + Combinations_trav_and_singleton_and_self + }, + { + "self_and_singleton_and_trav", + Combinations_self_and_singleton_and_trav + }, + { + "self_and_trav_and_singleton", + Combinations_self_and_trav_and_singleton + }, + { + "singleton_and_self_and_trav", + Combinations_singleton_and_self_and_trav + }, + { + "trav_and_self_and_singleton", + Combinations_trav_and_self_and_singleton + } +}; + +bake_test_case Plan_testcases[] = { + { + "reordered_plan_1", + Plan_reordered_plan_1 + }, + { + "reordered_plan_2", + Plan_reordered_plan_2 + }, + { + "reordered_plan_3", + Plan_reordered_plan_3 + }, + { + "reordered_plan_4", + Plan_reordered_plan_4 + }, + { + "reordered_plan_5", + Plan_reordered_plan_5 + }, + { + "reordered_plan_6", + Plan_reordered_plan_6 + }, + { + "reordered_plan_7", + Plan_reordered_plan_7 + }, + { + "1_trivial_plan", + Plan_1_trivial_plan + }, + { + "2_trivial_plan", + Plan_2_trivial_plan + }, + { + "1_trivial_plan_component", + Plan_1_trivial_plan_component + }, + { + "2_trivial_plan_component", + Plan_2_trivial_plan_component + }, + { + "3_trivial_plan_w_pair", + Plan_3_trivial_plan_w_pair + }, + { + "3_trivial_plan_w_wildcard", + Plan_3_trivial_plan_w_wildcard + }, + { + "3_trivial_plan_w_any", + Plan_3_trivial_plan_w_any + }, + { + "3_trivial_plan_w_pair_component", + Plan_3_trivial_plan_w_pair_component + }, + { + "3_trivial_plan_w_wildcard_component", + Plan_3_trivial_plan_w_wildcard_component + }, + { + "3_trivial_plan_w_any_component", + Plan_3_trivial_plan_w_any_component + }, + { + "1_trivial_component_w_none", + Plan_1_trivial_component_w_none + }, + { + "2_trivial_component_w_none", + Plan_2_trivial_component_w_none + }, + { + "2_trivial_plan_w_wildcard", + Plan_2_trivial_plan_w_wildcard + }, + { + "this_before_fixed_src", + Plan_this_before_fixed_src + }, + { + "fixed_src_before_this", + Plan_fixed_src_before_this + }, + { + "var_before_fixed_src", + Plan_var_before_fixed_src + }, + { + "fixed_src_before_var", + Plan_fixed_src_before_var + }, + { + "this_before_fixed_src_w_not", + Plan_this_before_fixed_src_w_not + }, + { + "this_before_fixed_src_w_first_var", + Plan_this_before_fixed_src_w_first_var + }, + { + "this_before_fixed_src_w_first_var_w_not", + Plan_this_before_fixed_src_w_first_var_w_not + }, + { + "this_before_fixed_src_w_second_var", + Plan_this_before_fixed_src_w_second_var + }, + { + "this_before_fixed_src_w_second_var_w_not", + Plan_this_before_fixed_src_w_second_var_w_not + }, + { + "populate_1_fixed", + Plan_populate_1_fixed + }, + { + "populate_2_fixed", + Plan_populate_2_fixed + }, + { + "populate_1_fixed_1_this_self", + Plan_populate_1_fixed_1_this_self + }, + { + "populate_2_fixed_2_this_self", + Plan_populate_2_fixed_2_this_self + }, + { + "populate_2_fixed_2_this_self_interleaved", + Plan_populate_2_fixed_2_this_self_interleaved + }, + { + "populate_2_this_self_2_fixed", + Plan_populate_2_this_self_2_fixed + }, + { + "populate_1_fixed_1_this_up", + Plan_populate_1_fixed_1_this_up + }, + { + "populate_2_fixed_2_this_up", + Plan_populate_2_fixed_2_this_up + }, + { + "populate_2_fixed_2_this_up_interleaved", + Plan_populate_2_fixed_2_this_up_interleaved + }, + { + "populate_2_this_up_2_fixed", + Plan_populate_2_this_up_2_fixed + }, + { + "populate_1_fixed_1_this_self_cached", + Plan_populate_1_fixed_1_this_self_cached + }, + { + "populate_2_fixed_2_this_self_cached", + Plan_populate_2_fixed_2_this_self_cached + }, + { + "populate_2_fixed_2_this_self_interleaved_cached", + Plan_populate_2_fixed_2_this_self_interleaved_cached + }, + { + "populate_2_this_self_2_fixed_cached", + Plan_populate_2_this_self_2_fixed_cached + }, + { + "populate_1_fixed_1_this_up_cached", + Plan_populate_1_fixed_1_this_up_cached + }, + { + "populate_2_fixed_2_this_up_cached", + Plan_populate_2_fixed_2_this_up_cached + }, + { + "populate_2_fixed_2_this_up_interleaved_cached", + Plan_populate_2_fixed_2_this_up_interleaved_cached + }, + { + "populate_2_this_up_2_fixed_cached", + Plan_populate_2_this_up_2_fixed_cached + }, + { + "populate_1_fixed_1_var_self", + Plan_populate_1_fixed_1_var_self + }, + { + "populate_2_fixed_2_var_self", + Plan_populate_2_fixed_2_var_self + }, + { + "populate_2_fixed_2_var_self_interleaved", + Plan_populate_2_fixed_2_var_self_interleaved + }, + { + "populate_2_var_self_2_fixed", + Plan_populate_2_var_self_2_fixed + }, + { + "populate_1_fixed_1_var_up", + Plan_populate_1_fixed_1_var_up + }, + { + "populate_2_fixed_2_var_up", + Plan_populate_2_fixed_2_var_up + }, + { + "populate_2_fixed_2_var_up_interleaved", + Plan_populate_2_fixed_2_var_up_interleaved + }, + { + "populate_2_var_up_2_fixed", + Plan_populate_2_var_up_2_fixed + }, + { + "cache_2_or", + Plan_cache_2_or + }, + { + "cache_2_or_w_not", + Plan_cache_2_or_w_not + }, + { + "1_plan_any_src", + Plan_1_plan_any_src + }, + { + "1_plan_not_any_src", + Plan_1_plan_not_any_src + }, + { + "1_plan_optional_any_src", + Plan_1_plan_optional_any_src + }, + { + "pair_first_wildcard", + Plan_pair_first_wildcard + }, + { + "pair_first_wildcard_cached", + Plan_pair_first_wildcard_cached + }, + { + "pair_second_wildcard", + Plan_pair_second_wildcard + }, + { + "pair_second_wildcard_cached", + Plan_pair_second_wildcard_cached + }, + { + "0_src_tag", + Plan_0_src_tag + }, + { + "0_src_component", + Plan_0_src_component + }, + { + "0_src_w_sparse", + Plan_0_src_w_sparse + }, + { + "0_src_w_toggle", + Plan_0_src_w_toggle + }, + { + "0_src_w_union", + Plan_0_src_w_union + }, + { + "0_src_w_sparse_and_component", + Plan_0_src_w_sparse_and_component + }, + { + "0_src_w_toggle_and_component", + Plan_0_src_w_toggle_and_component + }, + { + "0_src_w_union_and_component", + Plan_0_src_w_union_and_component + }, + { + "cached_isa_tgt", + Plan_cached_isa_tgt + }, + { + "cached_isa_tgt_w_self_second", + Plan_cached_isa_tgt_w_self_second + }, + { + "cached_isa_tgt_no_expr", + Plan_cached_isa_tgt_no_expr + }, + { + "cached_isa_tgt_w_self_second_no_expr", + Plan_cached_isa_tgt_w_self_second_no_expr + }, + { + "cached_w_not_and_uncacheable", + Plan_cached_w_not_and_uncacheable + }, + { + "cached_w_optional_and_uncacheable", + Plan_cached_w_optional_and_uncacheable + }, + { + "cached_w_not_optional_and_uncacheable", + Plan_cached_w_not_optional_and_uncacheable + } +}; + +bake_test_case Variables_testcases[] = { + { + "1_ent_src_w_var", + Variables_1_ent_src_w_var + }, + { + "1_ent_src_w_pair_rel_var", + Variables_1_ent_src_w_pair_rel_var + }, + { + "1_ent_src_w_pair_tgt_var", + Variables_1_ent_src_w_pair_tgt_var + }, + { + "1_ent_src_w_pair_rel_tgt_var", + Variables_1_ent_src_w_pair_rel_tgt_var + }, + { + "1_ent_src_w_pair_rel_tgt_same_var", + Variables_1_ent_src_w_pair_rel_tgt_same_var + }, + { + "1_ent_src_w_pair_rel_tgt_same_var_after_write", + Variables_1_ent_src_w_pair_rel_tgt_same_var_after_write + }, + { + "1_this_src_w_var", + Variables_1_this_src_w_var + }, + { + "1_this_src_w_pair_rel_var", + Variables_1_this_src_w_pair_rel_var + }, + { + "1_this_src_w_pair_tgt_var", + Variables_1_this_src_w_pair_tgt_var + }, + { + "1_this_src_w_pair_rel_tgt_var", + Variables_1_this_src_w_pair_rel_tgt_var + }, + { + "1_this_src_w_pair_rel_tgt_same_var", + Variables_1_this_src_w_pair_rel_tgt_same_var + }, + { + "1_this_src_w_pair_rel_tgt_same_var_after_write", + Variables_1_this_src_w_pair_rel_tgt_same_var_after_write + }, + { + "1_src_id_same_var", + Variables_1_src_id_same_var + }, + { + "1_src_pair_first_same_var", + Variables_1_src_pair_first_same_var + }, + { + "1_src_pair_second_same_var", + Variables_1_src_pair_second_same_var + }, + { + "1_src_pair_first_and_second_same_var", + Variables_1_src_pair_first_and_second_same_var + }, + { + "1_src_id_same_var_after_write", + Variables_1_src_id_same_var_after_write + }, + { + "1_src_pair_first_same_var_after_write", + Variables_1_src_pair_first_same_var_after_write + }, + { + "1_src_pair_second_same_var_after_write", + Variables_1_src_pair_second_same_var_after_write + }, + { + "1_src_pair_first_and_second_same_var_after_write", + Variables_1_src_pair_first_and_second_same_var_after_write + }, + { + "1_src_pair_first_same_var_this", + Variables_1_src_pair_first_same_var_this + }, + { + "1_src_pair_second_same_var_this", + Variables_1_src_pair_second_same_var_this + }, + { + "1_src_pair_first_and_second_same_var_this", + Variables_1_src_pair_first_and_second_same_var_this + }, + { + "1_src_id_same_var_this_after_write", + Variables_1_src_id_same_var_this_after_write + }, + { + "1_src_pair_first_same_var_this_after_write", + Variables_1_src_pair_first_same_var_this_after_write + }, + { + "1_src_pair_second_same_var_this_after_write", + Variables_1_src_pair_second_same_var_this_after_write + }, + { + "1_src_pair_first_and_second_same_var_this_after_write", + Variables_1_src_pair_first_and_second_same_var_this_after_write + }, + { + "1_ent_src_w_this_var", + Variables_1_ent_src_w_this_var + }, + { + "1_ent_src_w_pair_this_rel", + Variables_1_ent_src_w_pair_this_rel + }, + { + "1_ent_src_w_pair_this_tgt", + Variables_1_ent_src_w_pair_this_tgt + }, + { + "1_ent_src_w_pair_this_rel_tgt", + Variables_1_ent_src_w_pair_this_rel_tgt + }, + { + "1_this_src_w_this", + Variables_1_this_src_w_this + }, + { + "1_this_src_w_pair_this_rel_tgt", + Variables_1_this_src_w_pair_this_rel_tgt + }, + { + "1_this_src_w_this_after_write", + Variables_1_this_src_w_this_after_write + }, + { + "1_this_src_w_pair_this_rel_tgt_after_write", + Variables_1_this_src_w_pair_this_rel_tgt_after_write + }, + { + "2_constrain_src_from_src", + Variables_2_constrain_src_from_src + }, + { + "2_constrain_rel_from_src_w_ent", + Variables_2_constrain_rel_from_src_w_ent + }, + { + "2_constrain_rel_from_src_w_var", + Variables_2_constrain_rel_from_src_w_var + }, + { + "2_constrain_rel_from_src_w_this", + Variables_2_constrain_rel_from_src_w_this + }, + { + "2_constrain_pair_rel_from_src_w_ent", + Variables_2_constrain_pair_rel_from_src_w_ent + }, + { + "2_constrain_pair_rel_from_src_w_var", + Variables_2_constrain_pair_rel_from_src_w_var + }, + { + "2_constrain_pair_rel_from_src_w_this", + Variables_2_constrain_pair_rel_from_src_w_this + }, + { + "2_constrain_pair_tgt_from_src_w_ent", + Variables_2_constrain_pair_tgt_from_src_w_ent + }, + { + "2_constrain_pair_tgt_from_src_w_var", + Variables_2_constrain_pair_tgt_from_src_w_var + }, + { + "2_constrain_pair_tgt_from_src_w_this", + Variables_2_constrain_pair_tgt_from_src_w_this + }, + { + "2_constrain_pair_rel_tgt_from_src_w_ent", + Variables_2_constrain_pair_rel_tgt_from_src_w_ent + }, + { + "2_constrain_pair_rel_tgt_from_src_w_var", + Variables_2_constrain_pair_rel_tgt_from_src_w_var + }, + { + "2_constrain_pair_rel_tgt_from_src_w_this", + Variables_2_constrain_pair_rel_tgt_from_src_w_this + }, + { + "1_ent_src_set_rel_var", + Variables_1_ent_src_set_rel_var + }, + { + "1_ent_src_set_pair_rel_var", + Variables_1_ent_src_set_pair_rel_var + }, + { + "1_ent_src_set_pair_tgt_var", + Variables_1_ent_src_set_pair_tgt_var + }, + { + "1_set_src_var", + Variables_1_set_src_var + }, + { + "1_set_src_var_w_pair", + Variables_1_set_src_var_w_pair + }, + { + "1_set_src_var_w_pair_set_rel", + Variables_1_set_src_var_w_pair_set_rel + }, + { + "1_set_src_var_w_pair_set_tgt", + Variables_1_set_src_var_w_pair_set_tgt + }, + { + "1_set_src_var_w_pair_set_rel_tgt", + Variables_1_set_src_var_w_pair_set_rel_tgt + }, + { + "1_set_src_this", + Variables_1_set_src_this + }, + { + "1_set_src_this_w_pair", + Variables_1_set_src_this_w_pair + }, + { + "1_set_src_this_w_pair_set_rel", + Variables_1_set_src_this_w_pair_set_rel + }, + { + "1_set_src_this_w_pair_set_tgt", + Variables_1_set_src_this_w_pair_set_tgt + }, + { + "1_set_src_this_w_pair_set_rel_tgt", + Variables_1_set_src_this_w_pair_set_rel_tgt + }, + { + "1_set_src_this_to_empty_table", + Variables_1_set_src_this_to_empty_table + }, + { + "1_set_src_this_to_empty_table_w_component", + Variables_1_set_src_this_to_empty_table_w_component + }, + { + "1_set_src_this_to_empty_table_w_component_self", + Variables_1_set_src_this_to_empty_table_w_component_self + }, + { + "1_set_src_this_to_entity_in_table", + Variables_1_set_src_this_to_entity_in_table + }, + { + "1_set_src_this_to_entity_in_table_self", + Variables_1_set_src_this_to_entity_in_table_self + }, + { + "2_set_src_this", + Variables_2_set_src_this + }, + { + "2_set_src_this_self", + Variables_2_set_src_this_self + }, + { + "2_set_src_this_component", + Variables_2_set_src_this_component + }, + { + "2_set_src_this_self_component", + Variables_2_set_src_this_self_component + }, + { + "2_set_src_this_w_up", + Variables_2_set_src_this_w_up + }, + { + "2_set_src_this_self_w_up", + Variables_2_set_src_this_self_w_up + }, + { + "2_set_src_this_component_w_up", + Variables_2_set_src_this_component_w_up + }, + { + "2_set_src_this_self_component_w_up", + Variables_2_set_src_this_self_component_w_up + }, + { + "2_set_src_this_w_exclusive_wildcard", + Variables_2_set_src_this_w_exclusive_wildcard + }, + { + "2_set_src_this_self_w_exclusive_wildcard", + Variables_2_set_src_this_self_w_exclusive_wildcard + }, + { + "2_set_src_this_self_w_exclusive_wildcard_w_up", + Variables_2_set_src_this_self_w_exclusive_wildcard_w_up + }, + { + "1_set_src_this_is_true", + Variables_1_set_src_this_is_true + }, + { + "3_set_src_this_w_uncacheable_tag_tag_tag", + Variables_3_set_src_this_w_uncacheable_tag_tag_tag + }, + { + "3_set_src_this_w_uncacheable_tag_component_tag", + Variables_3_set_src_this_w_uncacheable_tag_component_tag + }, + { + "3_set_src_this_w_uncacheable_tag_component_component", + Variables_3_set_src_this_w_uncacheable_tag_component_component + }, + { + "3_set_src_this_w_tag_uncacheable_tag_tag", + Variables_3_set_src_this_w_tag_uncacheable_tag_tag + }, + { + "3_set_src_this_w_component_uncacheable_tag_tag", + Variables_3_set_src_this_w_component_uncacheable_tag_tag + }, + { + "3_set_src_this_w_component_uncacheable_tag_component", + Variables_3_set_src_this_w_component_uncacheable_tag_component + }, + { + "3_set_src_this_w_tag_tag_uncacheable_tag", + Variables_3_set_src_this_w_tag_tag_uncacheable_tag + }, + { + "3_set_src_this_w_component_tag_uncacheable_tag", + Variables_3_set_src_this_w_component_tag_uncacheable_tag + }, + { + "3_set_src_this_w_component_component_uncacheable_tag", + Variables_3_set_src_this_w_component_component_uncacheable_tag + }, + { + "3_set_src_this_w_uncacheable_component_tag_tag", + Variables_3_set_src_this_w_uncacheable_component_tag_tag + }, + { + "3_set_src_this_w_uncacheable_component_component_tag", + Variables_3_set_src_this_w_uncacheable_component_component_tag + }, + { + "3_set_src_this_w_uncacheable_component_component_component", + Variables_3_set_src_this_w_uncacheable_component_component_component + }, + { + "3_set_src_this_w_tag_uncacheable_component_tag", + Variables_3_set_src_this_w_tag_uncacheable_component_tag + }, + { + "3_set_src_this_w_component_uncacheable_component_tag", + Variables_3_set_src_this_w_component_uncacheable_component_tag + }, + { + "3_set_src_this_w_component_uncacheable_component_component", + Variables_3_set_src_this_w_component_uncacheable_component_component + }, + { + "3_set_src_this_w_tag_tag_uncacheable_component", + Variables_3_set_src_this_w_tag_tag_uncacheable_component + }, + { + "3_set_src_this_w_component_tag_uncacheable_component", + Variables_3_set_src_this_w_component_tag_uncacheable_component + }, + { + "3_set_src_this_w_component_component_uncacheable_component", + Variables_3_set_src_this_w_component_component_uncacheable_component + }, + { + "2_set_src_this_w_wildcard", + Variables_2_set_src_this_w_wildcard + }, + { + "1_src_this_var_as_entity", + Variables_1_src_this_var_as_entity + }, + { + "1_src_this_var_as_table", + Variables_1_src_this_var_as_table + }, + { + "1_src_this_var_as_table_range", + Variables_1_src_this_var_as_table_range + }, + { + "1_set_src_this_w_any", + Variables_1_set_src_this_w_any + }, + { + "1_set_src_this_w_any_fixed", + Variables_1_set_src_this_w_any_fixed + }, + { + "1_set_src_this_w_fixed_any", + Variables_1_set_src_this_w_fixed_any + }, + { + "2_join_by_rel_var", + Variables_2_join_by_rel_var + }, + { + "2_join_by_pair_rel_var", + Variables_2_join_by_pair_rel_var + }, + { + "2_join_by_pair_tgt_var", + Variables_2_join_by_pair_tgt_var + }, + { + "2_cycle_w_var", + Variables_2_cycle_w_var + }, + { + "2_cycle_w_this_var", + Variables_2_cycle_w_this_var + }, + { + "2_cycle_w_var_this", + Variables_2_cycle_w_var_this + }, + { + "2_cycle_pair_w_var", + Variables_2_cycle_pair_w_var + }, + { + "2_cycle_pair_w_this_var_var", + Variables_2_cycle_pair_w_this_var_var + }, + { + "2_cycle_pair_w_var_this_var", + Variables_2_cycle_pair_w_var_this_var + }, + { + "2_cycle_pair_w_var_var_this", + Variables_2_cycle_pair_w_var_var_this + }, + { + "2_cycle_pair_ent_var_var", + Variables_2_cycle_pair_ent_var_var + }, + { + "2_cycle_pair_ent_this_var", + Variables_2_cycle_pair_ent_this_var + }, + { + "2_cycle_pair_ent_var_this", + Variables_2_cycle_pair_ent_var_this + }, + { + "parse_0_var", + Variables_parse_0_var + }, + { + "parse_1_var", + Variables_parse_1_var + }, + { + "parse_2_vars", + Variables_parse_2_vars + }, + { + "parse_0_var_paren", + Variables_parse_0_var_paren + }, + { + "parse_1_var_paren", + Variables_parse_1_var_paren + }, + { + "parse_2_vars_paren", + Variables_parse_2_vars_paren + }, + { + "parse_1_vars_w_path", + Variables_parse_1_vars_w_path + }, + { + "parse_missing_close_paren", + Variables_parse_missing_close_paren + }, + { + "parse_missing_open_paren", + Variables_parse_missing_open_paren + }, + { + "parse_missing_value", + Variables_parse_missing_value + }, + { + "parse_0_var_w_spaces", + Variables_parse_0_var_w_spaces + }, + { + "parse_1_var_w_spaces", + Variables_parse_1_var_w_spaces + }, + { + "parse_2_vars_w_spaces", + Variables_parse_2_vars_w_spaces + }, + { + "parse_0_var_paren_w_spaces", + Variables_parse_0_var_paren_w_spaces + }, + { + "parse_1_var_paren_w_spaces", + Variables_parse_1_var_paren_w_spaces + }, + { + "parse_2_vars_paren_w_spaces", + Variables_parse_2_vars_paren_w_spaces + }, + { + "var_count", + Variables_var_count + }, + { + "var_name", + Variables_var_name + }, + { + "var_is_entity", + Variables_var_is_entity + }, + { + "no_this_anonymous_src", + Variables_no_this_anonymous_src + }, + { + "no_this_anonymous_src_w_pair", + Variables_no_this_anonymous_src_w_pair + }, + { + "no_this_anonymous_component_src", + Variables_no_this_anonymous_component_src + }, + { + "no_this_anonymous_component_src_w_pair", + Variables_no_this_anonymous_component_src_w_pair + }, + { + "lookup_from_table_this", + Variables_lookup_from_table_this + }, + { + "lookup_from_entity_this", + Variables_lookup_from_entity_this + }, + { + "lookup_from_table", + Variables_lookup_from_table + }, + { + "lookup_from_entity", + Variables_lookup_from_entity + }, + { + "lookup_from_not_written", + Variables_lookup_from_not_written + }, + { + "lookup_from_table_this_component", + Variables_lookup_from_table_this_component + }, + { + "lookup_from_entity_this_component", + Variables_lookup_from_entity_this_component + }, + { + "lookup_from_table_component", + Variables_lookup_from_table_component + }, + { + "lookup_from_entity_component", + Variables_lookup_from_entity_component + }, + { + "lookup_from_table_two_children", + Variables_lookup_from_table_two_children + }, + { + "lookup_from_entity_two_children", + Variables_lookup_from_entity_two_children + }, + { + "lookup_from_table_same_child_twice", + Variables_lookup_from_table_same_child_twice + }, + { + "lookup_from_entity_same_child_twice", + Variables_lookup_from_entity_same_child_twice + }, + { + "lookup_from_table_not", + Variables_lookup_from_table_not + }, + { + "lookup_from_entity_not", + Variables_lookup_from_entity_not + }, + { + "lookup_from_table_w_any_component", + Variables_lookup_from_table_w_any_component + }, + { + "lookup_from_entity_w_any_component", + Variables_lookup_from_entity_w_any_component + }, + { + "lookup_as_tag", + Variables_lookup_as_tag + }, + { + "lookup_as_relationship", + Variables_lookup_as_relationship + }, + { + "lookup_as_target", + Variables_lookup_as_target + }, + { + "lookup_assign_var", + Variables_lookup_assign_var + }, + { + "lookup_eq_var", + Variables_lookup_eq_var + }, + { + "lookup_neq_var", + Variables_lookup_neq_var + }, + { + "lookup_unresolved_dependent", + Variables_lookup_unresolved_dependent + }, + { + "check_vars_this", + Variables_check_vars_this + }, + { + "check_vars_var", + Variables_check_vars_var + }, + { + "check_vars_wildcard", + Variables_check_vars_wildcard + }, + { + "check_vars_any", + Variables_check_vars_any + }, + { + "check_vars_var_as_tgt", + Variables_check_vars_var_as_tgt + }, + { + "check_vars_this_as_tgt", + Variables_check_vars_this_as_tgt + }, + { + "check_vars_anonymous_var_as_tgt", + Variables_check_vars_anonymous_var_as_tgt + }, + { + "check_vars_wildcard_as_tgt", + Variables_check_vars_wildcard_as_tgt + }, + { + "check_vars_any_as_tgt", + Variables_check_vars_any_as_tgt + }, + { + "check_vars_this_w_lookup_var", + Variables_check_vars_this_w_lookup_var + }, + { + "check_vars_var_w_lookup_var", + Variables_check_vars_var_w_lookup_var + }, + { + "1_trivial_1_var", + Variables_1_trivial_1_var + }, + { + "2_trivial_1_var", + Variables_2_trivial_1_var + }, + { + "1_trivial_1_var_component", + Variables_1_trivial_1_var_component + }, + { + "2_trivial_1_var_component", + Variables_2_trivial_1_var_component + }, + { + "1_trivial_1_wildcard", + Variables_1_trivial_1_wildcard + }, + { + "2_trivial_1_wildcard", + Variables_2_trivial_1_wildcard + }, + { + "1_trivial_1_wildcard_component", + Variables_1_trivial_1_wildcard_component + }, + { + "2_trivial_1_wildcard_component", + Variables_2_trivial_1_wildcard_component + }, + { + "1_trivial_1_any", + Variables_1_trivial_1_any + }, + { + "2_trivial_1_any", + Variables_2_trivial_1_any + }, + { + "1_trivial_1_any_component", + Variables_1_trivial_1_any_component + }, + { + "2_trivial_1_any_component", + Variables_2_trivial_1_any_component + }, + { + "first_invalid_var_name_and_id", + Variables_first_invalid_var_name_and_id + }, + { + "src_invalid_var_name_and_id", + Variables_src_invalid_var_name_and_id + }, + { + "second_invalid_var_name_and_id", + Variables_second_invalid_var_name_and_id + } +}; + +bake_test_case Operators_testcases[] = { + { + "2_and_not", + Operators_2_and_not + }, + { + "2_and_not_component", + Operators_2_and_not_component + }, + { + "2_and_out_not", + Operators_2_and_out_not + }, + { + "2_and_out_not_component", + Operators_2_and_out_not_component + }, + { + "3_and_not_not", + Operators_3_and_not_not + }, + { + "2_and_not_pair_rel_wildcard", + Operators_2_and_not_pair_rel_wildcard + }, + { + "2_and_not_pair_tgt_wildcard", + Operators_2_and_not_pair_tgt_wildcard + }, + { + "2_and_not_pair_rel_tgt_wildcard", + Operators_2_and_not_pair_rel_tgt_wildcard + }, + { + "2_and_not_pair_rel_var", + Operators_2_and_not_pair_rel_var + }, + { + "2_and_not_pair_tgt_var", + Operators_2_and_not_pair_tgt_var + }, + { + "2_and_not_pair_rel_tgt_var", + Operators_2_and_not_pair_rel_tgt_var + }, + { + "2_and_not_pair_rel_tgt_same_var", + Operators_2_and_not_pair_rel_tgt_same_var + }, + { + "2_and_not_pair_rel_var_written", + Operators_2_and_not_pair_rel_var_written + }, + { + "2_and_not_pair_tgt_var_written", + Operators_2_and_not_pair_tgt_var_written + }, + { + "2_and_not_pair_rel_tgt_var_written", + Operators_2_and_not_pair_rel_tgt_var_written + }, + { + "2_and_not_pair_rel_tgt_same_var_written", + Operators_2_and_not_pair_rel_tgt_same_var_written + }, + { + "2_and_not_pair_rel_src_tgt_same_var_written", + Operators_2_and_not_pair_rel_src_tgt_same_var_written + }, + { + "2_and_not_pair_any_rel", + Operators_2_and_not_pair_any_rel + }, + { + "2_and_not_pair_any_tgt", + Operators_2_and_not_pair_any_tgt + }, + { + "2_and_not_pair_any_src", + Operators_2_and_not_pair_any_src + }, + { + "1_not_any_src_fixed_first", + Operators_1_not_any_src_fixed_first + }, + { + "1_not_any_src_any_tgt_fixed_first", + Operators_1_not_any_src_any_tgt_fixed_first + }, + { + "1_not_any_src_any_first_fixed_tgt", + Operators_1_not_any_src_any_first_fixed_tgt + }, + { + "1_not_any_src_any_childof_pair_any_tgt", + Operators_1_not_any_src_any_childof_pair_any_tgt + }, + { + "1_not_any_src_any_isa_pair_any_tgt", + Operators_1_not_any_src_any_isa_pair_any_tgt + }, + { + "1_not_match_prefab", + Operators_1_not_match_prefab + }, + { + "1_not_match_disabled", + Operators_1_not_match_disabled + }, + { + "1_not_match_not_queryable", + Operators_1_not_match_not_queryable + }, + { + "1_not_match_prefab_w_match_prefab_flag", + Operators_1_not_match_prefab_w_match_prefab_flag + }, + { + "1_not_match_disabled_w_match_disabled_flag", + Operators_1_not_match_disabled_w_match_disabled_flag + }, + { + "1_not_match_disabled_w_match_prefab_disabled_flag", + Operators_1_not_match_disabled_w_match_prefab_disabled_flag + }, + { + "2_and_optional", + Operators_2_and_optional + }, + { + "3_and_optional_optional", + Operators_3_and_optional_optional + }, + { + "2_and_optional_pair_rel_wildcard", + Operators_2_and_optional_pair_rel_wildcard + }, + { + "2_and_optional_pair_tgt_wildcard", + Operators_2_and_optional_pair_tgt_wildcard + }, + { + "2_and_optional_pair_rel_var", + Operators_2_and_optional_pair_rel_var + }, + { + "2_and_optional_pair_tgt_var", + Operators_2_and_optional_pair_tgt_var + }, + { + "2_and_optional_pair_rel_tgt_var", + Operators_2_and_optional_pair_rel_tgt_var + }, + { + "2_and_optional_pair_rel_tgt_same_var", + Operators_2_and_optional_pair_rel_tgt_same_var + }, + { + "2_and_optional_pair_rel_var_written", + Operators_2_and_optional_pair_rel_var_written + }, + { + "2_and_optional_pair_tgt_var_written", + Operators_2_and_optional_pair_tgt_var_written + }, + { + "2_and_optional_pair_rel_tgt_var_written", + Operators_2_and_optional_pair_rel_tgt_var_written + }, + { + "2_and_optional_pair_rel_tgt_same_var_written", + Operators_2_and_optional_pair_rel_tgt_same_var_written + }, + { + "2_and_optional_pair_rel_src_tgt_same_var_written", + Operators_2_and_optional_pair_rel_src_tgt_same_var_written + }, + { + "3_and_optional_optional_pair_w_var", + Operators_3_and_optional_optional_pair_w_var + }, + { + "2_and_optional_pair_any_rel", + Operators_2_and_optional_pair_any_rel + }, + { + "2_and_optional_pair_any_tgt", + Operators_2_and_optional_pair_any_tgt + }, + { + "2_and_optional_pair_any_src", + Operators_2_and_optional_pair_any_src + }, + { + "3_and_optional_dependent_and_pair_rel", + Operators_3_and_optional_dependent_and_pair_rel + }, + { + "3_and_optional_dependent_and_pair_tgt", + Operators_3_and_optional_dependent_and_pair_tgt + }, + { + "3_and_optional_dependent_and_pair_rel_tgt", + Operators_3_and_optional_dependent_and_pair_rel_tgt + }, + { + "3_and_optional_dependent_and_pair_rel_tgt_same_var", + Operators_3_and_optional_dependent_and_pair_rel_tgt_same_var + }, + { + "3_and_optional_dependent_and_pair_rel_tgt_same_other_var", + Operators_3_and_optional_dependent_and_pair_rel_tgt_same_other_var + }, + { + "3_and_optional_dependent_and_pair_src", + Operators_3_and_optional_dependent_and_pair_src + }, + { + "3_and_optional_dependent_optional_pair_rel", + Operators_3_and_optional_dependent_optional_pair_rel + }, + { + "3_and_optional_dependent_optional_pair_tgt", + Operators_3_and_optional_dependent_optional_pair_tgt + }, + { + "3_and_optional_dependent_optional_pair_src", + Operators_3_and_optional_dependent_optional_pair_src + }, + { + "3_and_optional_dependent_not_pair_rel", + Operators_3_and_optional_dependent_not_pair_rel + }, + { + "3_and_optional_dependent_not_pair_tgt", + Operators_3_and_optional_dependent_not_pair_tgt + }, + { + "3_and_optional_dependent_not_pair_src", + Operators_3_and_optional_dependent_not_pair_src + }, + { + "1_optional_any_src_fixed_first", + Operators_1_optional_any_src_fixed_first + }, + { + "1_optional_any_src_any_tgt_fixed_first", + Operators_1_optional_any_src_any_tgt_fixed_first + }, + { + "1_optional_any_src_any_first_fixed_tgt", + Operators_1_optional_any_src_any_first_fixed_tgt + }, + { + "1_optional_any_src_any_childof_pair_any_tgt", + Operators_1_optional_any_src_any_childof_pair_any_tgt + }, + { + "1_optional_any_src_any_isa_pair_any_tgt", + Operators_1_optional_any_src_any_isa_pair_any_tgt + }, + { + "2_or", + Operators_2_or + }, + { + "3_or", + Operators_3_or + }, + { + "2_or_w_and", + Operators_2_or_w_and + }, + { + "and_w_2_or_w_and", + Operators_and_w_2_or_w_and + }, + { + "and_w_2_or_w_and_components", + Operators_and_w_2_or_w_and_components + }, + { + "and_w_2_or_w_and_set_this", + Operators_and_w_2_or_w_and_set_this + }, + { + "and_w_2_or_w_and_components_set_this", + Operators_and_w_2_or_w_and_components_set_this + }, + { + "3_or_w_and", + Operators_3_or_w_and + }, + { + "2_or_written", + Operators_2_or_written + }, + { + "3_or_written", + Operators_3_or_written + }, + { + "2_or_written_w_rel_var", + Operators_2_or_written_w_rel_var + }, + { + "3_or_written_w_rel_var", + Operators_3_or_written_w_rel_var + }, + { + "2_or_written_w_tgt_var", + Operators_2_or_written_w_tgt_var + }, + { + "2_or_written_w_rel_tgt_var", + Operators_2_or_written_w_rel_tgt_var + }, + { + "2_or_written_w_rel_tgt_same_var", + Operators_2_or_written_w_rel_tgt_same_var + }, + { + "3_or_written_w_tgt_var", + Operators_3_or_written_w_tgt_var + }, + { + "2_or_chains", + Operators_2_or_chains + }, + { + "2_or_chains_written", + Operators_2_or_chains_written + }, + { + "2_or_dependent", + Operators_2_or_dependent + }, + { + "2_or_dependent_reverse", + Operators_2_or_dependent_reverse + }, + { + "2_or_dependent_2_vars", + Operators_2_or_dependent_2_vars + }, + { + "2_or_written_dependent", + Operators_2_or_written_dependent + }, + { + "2_or_written_dependent_2_vars", + Operators_2_or_written_dependent_2_vars + }, + { + "2_or_w_dependent", + Operators_2_or_w_dependent + }, + { + "2_or_w_both", + Operators_2_or_w_both + }, + { + "3_or_w_both", + Operators_3_or_w_both + }, + { + "2_or_w_not", + Operators_2_or_w_not + }, + { + "2_or_w_not_component", + Operators_2_or_w_not_component + }, + { + "2_or_w_not_out_component", + Operators_2_or_w_not_out_component + }, + { + "2_or_w_not_out_all_components", + Operators_2_or_w_not_out_all_components + }, + { + "2_not_first", + Operators_2_not_first + }, + { + "2_optional_first", + Operators_2_optional_first + }, + { + "only_not", + Operators_only_not + }, + { + "only_not_component", + Operators_only_not_component + }, + { + "only_not_out_component", + Operators_only_not_out_component + }, + { + "only_optional", + Operators_only_optional + }, + { + "only_optional_component", + Operators_only_optional_component + }, + { + "not_after_fixed_src", + Operators_not_after_fixed_src + }, + { + "optional_after_fixed_src", + Operators_optional_after_fixed_src + }, + { + "root_entities_empty", + Operators_root_entities_empty + }, + { + "root_entities", + Operators_root_entities + }, + { + "root_entities_w_children", + Operators_root_entities_w_children + }, + { + "root_entities_w_optional_children", + Operators_root_entities_w_optional_children + }, + { + "core_entities_w_optional_children", + Operators_core_entities_w_optional_children + }, + { + "root_entities_w_not_children", + Operators_root_entities_w_not_children + }, + { + "core_entities_w_not_children", + Operators_core_entities_w_not_children + }, + { + "1_ent_src_not", + Operators_1_ent_src_not + }, + { + "1_ent_src_not_pair", + Operators_1_ent_src_not_pair + }, + { + "1_ent_src_not_pair_rel_wildcard", + Operators_1_ent_src_not_pair_rel_wildcard + }, + { + "1_ent_src_not_pair_tgt_wildcard", + Operators_1_ent_src_not_pair_tgt_wildcard + }, + { + "1_ent_src_not_pair_rel_tgt_wildcard", + Operators_1_ent_src_not_pair_rel_tgt_wildcard + }, + { + "1_ent_src_not_pair_rel_any", + Operators_1_ent_src_not_pair_rel_any + }, + { + "1_ent_src_not_pair_tgt_any", + Operators_1_ent_src_not_pair_tgt_any + }, + { + "1_ent_src_not_pair_rel_tgt_any", + Operators_1_ent_src_not_pair_rel_tgt_any + }, + { + "1_ent_src_not_pair_rel_var", + Operators_1_ent_src_not_pair_rel_var + }, + { + "1_ent_src_not_pair_tgt_var", + Operators_1_ent_src_not_pair_tgt_var + }, + { + "1_ent_src_not_pair_rel_tgt_var", + Operators_1_ent_src_not_pair_rel_tgt_var + }, + { + "1_ent_src_not_pair_rel_tgt_same_var", + Operators_1_ent_src_not_pair_rel_tgt_same_var + }, + { + "1_this_src_not_pair_rel_var", + Operators_1_this_src_not_pair_rel_var + }, + { + "1_this_src_not_pair_tgt_var", + Operators_1_this_src_not_pair_tgt_var + }, + { + "1_this_src_not_pair_rel_tgt_var", + Operators_1_this_src_not_pair_rel_tgt_var + }, + { + "1_this_src_not_pair_rel_tgt_same_var", + Operators_1_this_src_not_pair_rel_tgt_same_var + }, + { + "1_ent_src_not_pair_rel_var_written", + Operators_1_ent_src_not_pair_rel_var_written + }, + { + "1_ent_src_not_pair_tgt_var_written", + Operators_1_ent_src_not_pair_tgt_var_written + }, + { + "1_ent_src_not_pair_rel_tgt_var_written", + Operators_1_ent_src_not_pair_rel_tgt_var_written + }, + { + "1_ent_src_not_pair_rel_tgt_same_var_written", + Operators_1_ent_src_not_pair_rel_tgt_same_var_written + }, + { + "and_from_fixed_src", + Operators_and_from_fixed_src + }, + { + "not_from_fixed_src", + Operators_not_from_fixed_src + }, + { + "or_from_fixed_src", + Operators_or_from_fixed_src + }, + { + "and_from_this", + Operators_and_from_this + }, + { + "not_from_this", + Operators_not_from_this + }, + { + "or_from_this", + Operators_or_from_this + }, + { + "and_from_this_written", + Operators_and_from_this_written + }, + { + "not_from_this_written", + Operators_not_from_this_written + }, + { + "or_from_this_written", + Operators_or_from_this_written + }, + { + "and_from_empty", + Operators_and_from_empty + }, + { + "not_from_empty", + Operators_not_from_empty + }, + { + "or_from_empty", + Operators_or_from_empty + }, + { + "and_from_empty_w_tag", + Operators_and_from_empty_w_tag + }, + { + "not_from_empty_w_tag", + Operators_not_from_empty_w_tag + }, + { + "or_from_empty_w_tag", + Operators_or_from_empty_w_tag + }, + { + "or_w_wildcard", + Operators_or_w_wildcard + }, + { + "or_w_component_and_tag", + Operators_or_w_component_and_tag + }, + { + "or_w_tag_and_component", + Operators_or_w_tag_and_component + } +}; + +bake_test_case Transitive_testcases[] = { + { + "1_fact_0_lvl_true", + Transitive_1_fact_0_lvl_true + }, + { + "1_fact_1_lvl_true", + Transitive_1_fact_1_lvl_true + }, + { + "1_fact_2_lvl_true", + Transitive_1_fact_2_lvl_true + }, + { + "1_fact_0_lvl_false", + Transitive_1_fact_0_lvl_false + }, + { + "1_fact_1_lvl_false", + Transitive_1_fact_1_lvl_false + }, + { + "1_fact_2_lvl_false", + Transitive_1_fact_2_lvl_false + }, + { + "1_fact_reflexive", + Transitive_1_fact_reflexive + }, + { + "1_isa", + Transitive_1_isa + }, + { + "1_childof", + Transitive_1_childof + }, + { + "1_this_src_written_0_lvl", + Transitive_1_this_src_written_0_lvl + }, + { + "1_this_src_written_1_lvl", + Transitive_1_this_src_written_1_lvl + }, + { + "1_this_src_written_2_lvl", + Transitive_1_this_src_written_2_lvl + }, + { + "1_this_src_written_reflexive", + Transitive_1_this_src_written_reflexive + }, + { + "1_this_src_0_lvl", + Transitive_1_this_src_0_lvl + }, + { + "1_this_src_1_lvl", + Transitive_1_this_src_1_lvl + }, + { + "1_this_src_2_lvl", + Transitive_1_this_src_2_lvl + }, + { + "1_this_src_reflexive", + Transitive_1_this_src_reflexive + }, + { + "1_ent_src_tgt_var_0_lvl", + Transitive_1_ent_src_tgt_var_0_lvl + }, + { + "1_ent_src_tgt_var_1_lvl", + Transitive_1_ent_src_tgt_var_1_lvl + }, + { + "1_ent_src_tgt_var_2_lvl", + Transitive_1_ent_src_tgt_var_2_lvl + }, + { + "1_ent_src_tgt_var_reflexive", + Transitive_1_ent_src_tgt_var_reflexive + }, + { + "1_this_src_tgt_var", + Transitive_1_this_src_tgt_var + }, + { + "1_this_src_tgt_var_reflexive", + Transitive_1_this_src_tgt_var_reflexive + }, + { + "1_var_src_written_0_lvl", + Transitive_1_var_src_written_0_lvl + }, + { + "1_var_src_written_1_lvl", + Transitive_1_var_src_written_1_lvl + }, + { + "1_var_src_written_2_lvl", + Transitive_1_var_src_written_2_lvl + }, + { + "1_var_src_written_reflexive", + Transitive_1_var_src_written_reflexive + }, + { + "1_var_src_0_lvl", + Transitive_1_var_src_0_lvl + }, + { + "1_var_src_1_lvl", + Transitive_1_var_src_1_lvl + }, + { + "1_var_src_2_lvl", + Transitive_1_var_src_2_lvl + }, + { + "1_var_src_reflexive", + Transitive_1_var_src_reflexive + }, + { + "1_var_src_tgt_var", + Transitive_1_var_src_tgt_var + }, + { + "1_var_src_tgt_var_reflexive", + Transitive_1_var_src_tgt_var_reflexive + }, + { + "1_ent_src_tgt_this_0_lvl", + Transitive_1_ent_src_tgt_this_0_lvl + }, + { + "1_ent_src_tgt_this_1_lvl", + Transitive_1_ent_src_tgt_this_1_lvl + }, + { + "1_ent_src_tgt_this_2_lvl", + Transitive_1_ent_src_tgt_this_2_lvl + }, + { + "1_ent_src_tgt_this_reflexive", + Transitive_1_ent_src_tgt_this_reflexive + }, + { + "1_var_src_tgt_this", + Transitive_1_var_src_tgt_this + }, + { + "1_var_src_tgt_this_reflexive", + Transitive_1_var_src_tgt_this_reflexive + }, + { + "2_ent_src_constrain_tgt_var_before_0_lvl", + Transitive_2_ent_src_constrain_tgt_var_before_0_lvl + }, + { + "2_ent_src_constrain_tgt_var_before_1_lvl", + Transitive_2_ent_src_constrain_tgt_var_before_1_lvl + }, + { + "2_ent_src_constrain_tgt_var_before_2_lvl", + Transitive_2_ent_src_constrain_tgt_var_before_2_lvl + }, + { + "2_ent_src_constrain_tgt_var_after_0_lvl", + Transitive_2_ent_src_constrain_tgt_var_after_0_lvl + }, + { + "2_ent_src_constrain_tgt_var_after_1_lvl", + Transitive_2_ent_src_constrain_tgt_var_after_1_lvl + }, + { + "2_ent_src_constrain_tgt_var_after_2_lvl", + Transitive_2_ent_src_constrain_tgt_var_after_2_lvl + }, + { + "2_this_src_constrain_tgt_var_before_0_lvl", + Transitive_2_this_src_constrain_tgt_var_before_0_lvl + }, + { + "2_this_src_constrain_tgt_var_before_1_lvl", + Transitive_2_this_src_constrain_tgt_var_before_1_lvl + }, + { + "2_this_src_constrain_tgt_var_before_2_lvl", + Transitive_2_this_src_constrain_tgt_var_before_2_lvl + }, + { + "2_this_src_constrain_tgt_var_after_0_lvl", + Transitive_2_this_src_constrain_tgt_var_after_0_lvl + }, + { + "2_this_src_constrain_tgt_var_after_1_lvl", + Transitive_2_this_src_constrain_tgt_var_after_1_lvl + }, + { + "2_this_src_constrain_tgt_var_after_2_lvl", + Transitive_2_this_src_constrain_tgt_var_after_2_lvl + }, + { + "1_src_tgt_same_var", + Transitive_1_src_tgt_same_var + }, + { + "1_src_tgt_same_var_reflexive", + Transitive_1_src_tgt_same_var_reflexive + }, + { + "1_src_tgt_same_this_var_reflexive", + Transitive_1_src_tgt_same_this_var_reflexive + }, + { + "1_any_src_tgt_var", + Transitive_1_any_src_tgt_var + }, + { + "not_transitive_ent_tgt", + Transitive_not_transitive_ent_tgt + }, + { + "not_transitive_var_tgt", + Transitive_not_transitive_var_tgt + }, + { + "not_transitive_ent_tgt_written", + Transitive_not_transitive_ent_tgt_written + }, + { + "not_transitive_var_tgt_written", + Transitive_not_transitive_var_tgt_written + }, + { + "optional_transitive_ent_tgt", + Transitive_optional_transitive_ent_tgt + }, + { + "optional_transitive_var_tgt", + Transitive_optional_transitive_var_tgt + }, + { + "optional_transitive_ent_tgt_written", + Transitive_optional_transitive_ent_tgt_written + }, + { + "optional_transitive_var_tgt_written", + Transitive_optional_transitive_var_tgt_written + }, + { + "2_var_src_w_same_tgt_ent", + Transitive_2_var_src_w_same_tgt_ent + }, + { + "self_target", + Transitive_self_target + }, + { + "any_target", + Transitive_any_target + }, + { + "isa_prefab", + Transitive_isa_prefab + }, + { + "isa_disabled", + Transitive_isa_disabled + }, + { + "isa_prefab_match_prefab_flag", + Transitive_isa_prefab_match_prefab_flag + }, + { + "isa_prefab_match_prefab_term", + Transitive_isa_prefab_match_prefab_term + }, + { + "isa_disabled_match_disabled_flag", + Transitive_isa_disabled_match_disabled_flag + }, + { + "isa_disabled_match_disabled_term", + Transitive_isa_disabled_match_disabled_term + } +}; + +bake_test_case ComponentInheritance_testcases[] = { + { + "1_ent_0_lvl", + ComponentInheritance_1_ent_0_lvl + }, + { + "1_ent_1_lvl", + ComponentInheritance_1_ent_1_lvl + }, + { + "1_ent_2_lvl", + ComponentInheritance_1_ent_2_lvl + }, + { + "1_ent_3_lvl", + ComponentInheritance_1_ent_3_lvl + }, + { + "1_this_0_lvl", + ComponentInheritance_1_this_0_lvl + }, + { + "1_this_1_lvl", + ComponentInheritance_1_this_1_lvl + }, + { + "1_this_2_lvl", + ComponentInheritance_1_this_2_lvl + }, + { + "1_this_3_lvl", + ComponentInheritance_1_this_3_lvl + }, + { + "1_this_0_lvl_written", + ComponentInheritance_1_this_0_lvl_written + }, + { + "1_this_1_lvl_written", + ComponentInheritance_1_this_1_lvl_written + }, + { + "1_this_2_lvl_written", + ComponentInheritance_1_this_2_lvl_written + }, + { + "1_this_3_lvl_written", + ComponentInheritance_1_this_3_lvl_written + }, + { + "1_var_0_lvl", + ComponentInheritance_1_var_0_lvl + }, + { + "1_var_1_lvl", + ComponentInheritance_1_var_1_lvl + }, + { + "1_var_2_lvl", + ComponentInheritance_1_var_2_lvl + }, + { + "1_var_3_lvl", + ComponentInheritance_1_var_3_lvl + }, + { + "1_var_0_lvl_written", + ComponentInheritance_1_var_0_lvl_written + }, + { + "1_var_1_lvl_written", + ComponentInheritance_1_var_1_lvl_written + }, + { + "1_var_2_lvl_written", + ComponentInheritance_1_var_2_lvl_written + }, + { + "1_var_3_lvl_written", + ComponentInheritance_1_var_3_lvl_written + }, + { + "1_ent_1_lvl_self", + ComponentInheritance_1_ent_1_lvl_self + }, + { + "1_this_1_lvl_self", + ComponentInheritance_1_this_1_lvl_self + }, + { + "1_this_1_lvl_written_self", + ComponentInheritance_1_this_1_lvl_written_self + }, + { + "1_var_1_lvl_self", + ComponentInheritance_1_var_1_lvl_self + }, + { + "1_var_1_lvl_written_self", + ComponentInheritance_1_var_1_lvl_written_self + }, + { + "1_ent_src_not", + ComponentInheritance_1_ent_src_not + }, + { + "1_this_src_not", + ComponentInheritance_1_this_src_not + }, + { + "1_var_src_not", + ComponentInheritance_1_var_src_not + }, + { + "1_this_src_not_written", + ComponentInheritance_1_this_src_not_written + }, + { + "1_var_src_not_written", + ComponentInheritance_1_var_src_not_written + }, + { + "first_self", + ComponentInheritance_first_self + } +}; + +bake_test_case Recycled_testcases[] = { + { + "recycled_vars", + Recycled_recycled_vars + }, + { + "recycled_pair_vars", + Recycled_recycled_pair_vars + }, + { + "recycled_this_ent_var", + Recycled_recycled_this_ent_var + }, + { + "has_recycled_id_from_pair", + Recycled_has_recycled_id_from_pair + }, + { + "recycled_pair", + Recycled_recycled_pair + }, + { + "recycled_component_id", + Recycled_recycled_component_id + } +}; + +bake_test_case BuiltinPredicates_testcases[] = { + { + "this_eq_id", + BuiltinPredicates_this_eq_id + }, + { + "this_eq_name", + BuiltinPredicates_this_eq_name + }, + { + "this_eq_var", + BuiltinPredicates_this_eq_var + }, + { + "this_eq_id_written", + BuiltinPredicates_this_eq_id_written + }, + { + "this_eq_id_written_no_match", + BuiltinPredicates_this_eq_id_written_no_match + }, + { + "this_eq_name_written", + BuiltinPredicates_this_eq_name_written + }, + { + "this_eq_name_written_no_match", + BuiltinPredicates_this_eq_name_written_no_match + }, + { + "this_eq_var_written", + BuiltinPredicates_this_eq_var_written + }, + { + "var_eq_id", + BuiltinPredicates_var_eq_id + }, + { + "var_eq_name", + BuiltinPredicates_var_eq_name + }, + { + "var_eq_var", + BuiltinPredicates_var_eq_var + }, + { + "var_eq_id_written", + BuiltinPredicates_var_eq_id_written + }, + { + "var_eq_id_written_no_match", + BuiltinPredicates_var_eq_id_written_no_match + }, + { + "var_eq_name_written", + BuiltinPredicates_var_eq_name_written + }, + { + "var_eq_name_written_no_match", + BuiltinPredicates_var_eq_name_written_no_match + }, + { + "var_eq_var_written", + BuiltinPredicates_var_eq_var_written + }, + { + "var_eq_this", + BuiltinPredicates_var_eq_this + }, + { + "this_neq_id", + BuiltinPredicates_this_neq_id + }, + { + "this_neq_name", + BuiltinPredicates_this_neq_name + }, + { + "this_neq_var", + BuiltinPredicates_this_neq_var + }, + { + "this_neq_id_written", + BuiltinPredicates_this_neq_id_written + }, + { + "this_neq_id_written_no_match", + BuiltinPredicates_this_neq_id_written_no_match + }, + { + "this_neq_name_written", + BuiltinPredicates_this_neq_name_written + }, + { + "this_neq_name_written_no_match", + BuiltinPredicates_this_neq_name_written_no_match + }, + { + "this_neq_var_written", + BuiltinPredicates_this_neq_var_written + }, + { + "var_neq_id", + BuiltinPredicates_var_neq_id + }, + { + "var_neq_name", + BuiltinPredicates_var_neq_name + }, + { + "var_neq_var", + BuiltinPredicates_var_neq_var + }, + { + "var_neq_id_written", + BuiltinPredicates_var_neq_id_written + }, + { + "var_neq_id_written_no_match", + BuiltinPredicates_var_neq_id_written_no_match + }, + { + "var_neq_name_written", + BuiltinPredicates_var_neq_name_written + }, + { + "var_neq_name_written_no_match", + BuiltinPredicates_var_neq_name_written_no_match + }, + { + "var_neq_var_written", + BuiltinPredicates_var_neq_var_written + }, + { + "var_neq_this", + BuiltinPredicates_var_neq_this + }, + { + "this_2_neq_id", + BuiltinPredicates_this_2_neq_id + }, + { + "this_2_neq_name", + BuiltinPredicates_this_2_neq_name + }, + { + "var_2_neq_id", + BuiltinPredicates_var_2_neq_id + }, + { + "var_2_neq_name", + BuiltinPredicates_var_2_neq_name + }, + { + "this_2_neq_id_written", + BuiltinPredicates_this_2_neq_id_written + }, + { + "this_2_neq_name_written", + BuiltinPredicates_this_2_neq_name_written + }, + { + "var_2_neq_id_written", + BuiltinPredicates_var_2_neq_id_written + }, + { + "var_2_neq_name_written", + BuiltinPredicates_var_2_neq_name_written + }, + { + "this_2_or_id", + BuiltinPredicates_this_2_or_id + }, + { + "this_3_or_id", + BuiltinPredicates_this_3_or_id + }, + { + "this_2_or_name", + BuiltinPredicates_this_2_or_name + }, + { + "this_3_or_name", + BuiltinPredicates_this_3_or_name + }, + { + "this_2_or_match", + BuiltinPredicates_this_2_or_match + }, + { + "this_3_or_match", + BuiltinPredicates_this_3_or_match + }, + { + "var_2_or_id", + BuiltinPredicates_var_2_or_id + }, + { + "var_2_or_name", + BuiltinPredicates_var_2_or_name + }, + { + "this_2_or_id_written", + BuiltinPredicates_this_2_or_id_written + }, + { + "this_3_or_id_written", + BuiltinPredicates_this_3_or_id_written + }, + { + "this_2_or_name_written", + BuiltinPredicates_this_2_or_name_written + }, + { + "var_2_or_id_written", + BuiltinPredicates_var_2_or_id_written + }, + { + "var_2_or_name_written", + BuiltinPredicates_var_2_or_name_written + }, + { + "this_match_eq", + BuiltinPredicates_this_match_eq + }, + { + "var_match_eq", + BuiltinPredicates_var_match_eq + }, + { + "this_match_eq_written", + BuiltinPredicates_this_match_eq_written + }, + { + "this_match_eq_written_self", + BuiltinPredicates_this_match_eq_written_self + }, + { + "var_match_eq_written", + BuiltinPredicates_var_match_eq_written + }, + { + "this_match_neq", + BuiltinPredicates_this_match_neq + }, + { + "var_match_neq", + BuiltinPredicates_var_match_neq + }, + { + "this_match_neq_written", + BuiltinPredicates_this_match_neq_written + }, + { + "var_match_neq_written", + BuiltinPredicates_var_match_neq_written + }, + { + "this_match_2_neq", + BuiltinPredicates_this_match_2_neq + }, + { + "var_match_2_neq", + BuiltinPredicates_var_match_2_neq + }, + { + "this_match_2_neq_written", + BuiltinPredicates_this_match_2_neq_written + }, + { + "var_match_2_neq_written", + BuiltinPredicates_var_match_2_neq_written + }, + { + "this_match_2_or", + BuiltinPredicates_this_match_2_or + }, + { + "this_match_2_or_written", + BuiltinPredicates_this_match_2_or_written + }, + { + "this_match_3_or", + BuiltinPredicates_this_match_3_or + }, + { + "this_match_3_or_written", + BuiltinPredicates_this_match_3_or_written + }, + { + "unresolved_by_name", + BuiltinPredicates_unresolved_by_name + }, + { + "var_eq_wildcard", + BuiltinPredicates_var_eq_wildcard + }, + { + "var_eq_any", + BuiltinPredicates_var_eq_any + }, + { + "var_eq_wildcard_after_write", + BuiltinPredicates_var_eq_wildcard_after_write + }, + { + "var_eq_any_after_write", + BuiltinPredicates_var_eq_any_after_write + }, + { + "var_eq_after_var_0_src", + BuiltinPredicates_var_eq_after_var_0_src + }, + { + "2_or_w_eq_this", + BuiltinPredicates_2_or_w_eq_this + }, + { + "2_or_w_eq_lookup_var", + BuiltinPredicates_2_or_w_eq_lookup_var + }, + { + "3_or_w_eq_lookup_var", + BuiltinPredicates_3_or_w_eq_lookup_var + }, + { + "eq_variable", + BuiltinPredicates_eq_variable + }, + { + "eq_wildcard", + BuiltinPredicates_eq_wildcard + }, + { + "eq_any", + BuiltinPredicates_eq_any + }, + { + "neq_variable", + BuiltinPredicates_neq_variable + }, + { + "neq_wildcard", + BuiltinPredicates_neq_wildcard + }, + { + "neq_any", + BuiltinPredicates_neq_any + }, + { + "match_variable", + BuiltinPredicates_match_variable + }, + { + "match_wildcard", + BuiltinPredicates_match_wildcard + }, + { + "match_any", + BuiltinPredicates_match_any + } +}; + +bake_test_case Scopes_testcases[] = { + { + "term_w_not_scope_1_term", + Scopes_term_w_not_scope_1_term + }, + { + "term_w_not_scope_2_terms", + Scopes_term_w_not_scope_2_terms + }, + { + "term_w_not_scope_1_term_w_not", + Scopes_term_w_not_scope_1_term_w_not + }, + { + "term_w_not_scope_2_terms_w_not", + Scopes_term_w_not_scope_2_terms_w_not + }, + { + "term_w_not_scope_1_term_w_var", + Scopes_term_w_not_scope_1_term_w_var + }, + { + "term_w_not_scope_2_terms_w_var", + Scopes_term_w_not_scope_2_terms_w_var + }, + { + "term_w_not_scope_1_term_w_not_w_var", + Scopes_term_w_not_scope_1_term_w_not_w_var + }, + { + "term_w_not_scope_2_terms_w_not_w_var", + Scopes_term_w_not_scope_2_terms_w_not_w_var + }, + { + "term_w_not_scope_2_terms_w_or", + Scopes_term_w_not_scope_2_terms_w_or + }, + { + "term_w_not_scope_3_terms_w_or", + Scopes_term_w_not_scope_3_terms_w_or + }, + { + "term_w_not_scope_2_terms_w_before_after", + Scopes_term_w_not_scope_2_terms_w_before_after + } +}; + +bake_test_case Traversal_testcases[] = { + { + "this_self_up_childof", + Traversal_this_self_up_childof + }, + { + "this_up_childof", + Traversal_this_up_childof + }, + { + "this_written_self_up_childof", + Traversal_this_written_self_up_childof + }, + { + "this_written_up_childof", + Traversal_this_written_up_childof + }, + { + "var_self_up_childof", + Traversal_var_self_up_childof + }, + { + "var_up_childof", + Traversal_var_up_childof + }, + { + "var_written_self_up_childof", + Traversal_var_written_self_up_childof + }, + { + "var_written_up_childof", + Traversal_var_written_up_childof + }, + { + "set_var_self_up_childof", + Traversal_set_var_self_up_childof + }, + { + "set_var_up_childof", + Traversal_set_var_up_childof + }, + { + "set_var_written_self_up_childof", + Traversal_set_var_written_self_up_childof + }, + { + "set_var_written_up_childof", + Traversal_set_var_written_up_childof + }, + { + "ent_self_up_childof", + Traversal_ent_self_up_childof + }, + { + "ent_up_childof", + Traversal_ent_up_childof + }, + { + "implicit_this_self_up_isa", + Traversal_implicit_this_self_up_isa + }, + { + "implicit_this_up_isa", + Traversal_implicit_this_up_isa + }, + { + "implicit_var_self_up_isa", + Traversal_implicit_var_self_up_isa + }, + { + "implicit_var_up_isa", + Traversal_implicit_var_up_isa + }, + { + "implicit_ent_self_up_isa", + Traversal_implicit_ent_self_up_isa + }, + { + "implicit_ent_up_isa", + Traversal_implicit_ent_up_isa + }, + { + "self_up_2_targets", + Traversal_self_up_2_targets + }, + { + "up_2_targets", + Traversal_up_2_targets + }, + { + "self_up_2_targets_diamond", + Traversal_self_up_2_targets_diamond + }, + { + "up_2_targets_diamond", + Traversal_up_2_targets_diamond + }, + { + "written_self_up_2_targets", + Traversal_written_self_up_2_targets + }, + { + "written_up_2_targets", + Traversal_written_up_2_targets + }, + { + "written_self_up_2_targets_diamond", + Traversal_written_self_up_2_targets_diamond + }, + { + "written_up_2_targets_diamond", + Traversal_written_up_2_targets_diamond + }, + { + "2_self_up_terms", + Traversal_2_self_up_terms + }, + { + "2_self_up_terms_2_targets", + Traversal_2_self_up_terms_2_targets + }, + { + "self_up_empty_table", + Traversal_self_up_empty_table + }, + { + "up_empty_table", + Traversal_up_empty_table + }, + { + "self_up_match_empty_table", + Traversal_self_up_match_empty_table + }, + { + "up_match_empty_table", + Traversal_up_match_empty_table + }, + { + "self_up_all_owned", + Traversal_self_up_all_owned + }, + { + "up_all_owned", + Traversal_up_all_owned + }, + { + "this_self_up_childof_inherited", + Traversal_this_self_up_childof_inherited + }, + { + "this_up_childof_inherited", + Traversal_this_up_childof_inherited + }, + { + "this_self_up_childof_inherited_override", + Traversal_this_self_up_childof_inherited_override + }, + { + "this_up_childof_inherited_override", + Traversal_this_up_childof_inherited_override + }, + { + "this_written_self_up_childof_inherited", + Traversal_this_written_self_up_childof_inherited + }, + { + "this_written_up_childof_inherited", + Traversal_this_written_up_childof_inherited + }, + { + "this_written_self_up_childof_inherited_override", + Traversal_this_written_self_up_childof_inherited_override + }, + { + "this_written_up_childof_inherited_override", + Traversal_this_written_up_childof_inherited_override + }, + { + "var_self_up_childof_inherited", + Traversal_var_self_up_childof_inherited + }, + { + "var_up_childof_inherited", + Traversal_var_up_childof_inherited + }, + { + "var_written_self_up_childof_inherited", + Traversal_var_written_self_up_childof_inherited + }, + { + "var_written_up_childof_inherited", + Traversal_var_written_up_childof_inherited + }, + { + "ent_self_up_childof_inherited", + Traversal_ent_self_up_childof_inherited + }, + { + "ent_up_childof_inherited", + Traversal_ent_up_childof_inherited + }, + { + "ent_written_self_up_childof_inherited", + Traversal_ent_written_self_up_childof_inherited + }, + { + "ent_written_up_childof_inherited", + Traversal_ent_written_up_childof_inherited + }, + { + "this_self_up_childof_component", + Traversal_this_self_up_childof_component + }, + { + "this_up_childof_component", + Traversal_this_up_childof_component + }, + { + "this_written_self_up_childof_component", + Traversal_this_written_self_up_childof_component + }, + { + "this_written_up_childof_component", + Traversal_this_written_up_childof_component + }, + { + "var_self_up_childof_component", + Traversal_var_self_up_childof_component + }, + { + "var_up_childof_component", + Traversal_var_up_childof_component + }, + { + "var_written_self_up_childof_component", + Traversal_var_written_self_up_childof_component + }, + { + "var_written_up_childof_component", + Traversal_var_written_up_childof_component + }, + { + "this_self_up_childof_recycled_parent", + Traversal_this_self_up_childof_recycled_parent + }, + { + "this_up_childof_recycled_parent", + Traversal_this_up_childof_recycled_parent + }, + { + "this_written_self_up_childof_recycled_parent", + Traversal_this_written_self_up_childof_recycled_parent + }, + { + "this_written_up_childof_recycled_parent", + Traversal_this_written_up_childof_recycled_parent + }, + { + "this_self_up_childof_recycled_parent_component", + Traversal_this_self_up_childof_recycled_parent_component + }, + { + "this_up_childof_recycled_parent_component", + Traversal_this_up_childof_recycled_parent_component + }, + { + "this_written_self_up_childof_recycled_parent_component", + Traversal_this_written_self_up_childof_recycled_parent_component + }, + { + "this_written_up_childof_recycled_parent_component", + Traversal_this_written_up_childof_recycled_parent_component + }, + { + "this_self_up_childof_pair", + Traversal_this_self_up_childof_pair + }, + { + "this_up_childof_pair", + Traversal_this_up_childof_pair + }, + { + "this_written_self_up_childof_pair", + Traversal_this_written_self_up_childof_pair + }, + { + "this_written_up_childof_pair", + Traversal_this_written_up_childof_pair + }, + { + "this_self_up_childof_pair_wildcard", + Traversal_this_self_up_childof_pair_wildcard + }, + { + "this_up_childof_pair_wildcard", + Traversal_this_up_childof_pair_wildcard + }, + { + "this_written_self_up_childof_pair_wildcard", + Traversal_this_written_self_up_childof_pair_wildcard + }, + { + "this_written_up_childof_pair_wildcard", + Traversal_this_written_up_childof_pair_wildcard + }, + { + "this_self_up_childof_pair_tgt_var", + Traversal_this_self_up_childof_pair_tgt_var + }, + { + "this_written_self_up_childof_pair_tgt_var", + Traversal_this_written_self_up_childof_pair_tgt_var + }, + { + "this_self_up_childof_pair_rel_var", + Traversal_this_self_up_childof_pair_rel_var + }, + { + "this_written_self_up_childof_pair_rel_var", + Traversal_this_written_self_up_childof_pair_rel_var + }, + { + "this_self_up_childof_pair_for_var_written", + Traversal_this_self_up_childof_pair_for_var_written + }, + { + "this_up_childof_pair_for_var_written", + Traversal_this_up_childof_pair_for_var_written + }, + { + "this_written_self_up_childof_pair_for_var_written", + Traversal_this_written_self_up_childof_pair_for_var_written + }, + { + "this_self_up_childof_pair_for_var_written_n_targets", + Traversal_this_self_up_childof_pair_for_var_written_n_targets + }, + { + "this_written_self_up_childof_pair_for_var_written_n_targets", + Traversal_this_written_self_up_childof_pair_for_var_written_n_targets + }, + { + "self_up_2_levels_w_prefab", + Traversal_self_up_2_levels_w_prefab + }, + { + "self_up_2_levels_other_trav_rel_w_prefab", + Traversal_self_up_2_levels_other_trav_rel_w_prefab + }, + { + "up_2_levels_w_prefab", + Traversal_up_2_levels_w_prefab + }, + { + "up_2_levels_other_trav_rel_w_prefab", + Traversal_up_2_levels_other_trav_rel_w_prefab + }, + { + "self_up_2_levels", + Traversal_self_up_2_levels + }, + { + "self_up_2_levels_other_trav_rel", + Traversal_self_up_2_levels_other_trav_rel + }, + { + "up_2_levels", + Traversal_up_2_levels + }, + { + "up_2_levels_other_trav_rel", + Traversal_up_2_levels_other_trav_rel + }, + { + "self_up_mixed_traversable", + Traversal_self_up_mixed_traversable + }, + { + "not_up", + Traversal_not_up + }, + { + "not_self_up", + Traversal_not_self_up + }, + { + "not_up_wildcard", + Traversal_not_up_wildcard + }, + { + "not_self_up_wildcard", + Traversal_not_self_up_wildcard + }, + { + "not_up_disabled", + Traversal_not_up_disabled + }, + { + "up_2_rel_instances", + Traversal_up_2_rel_instances + }, + { + "up_2_rel_instances_match_2nd", + Traversal_up_2_rel_instances_match_2nd + }, + { + "up_only_w_owned", + Traversal_up_only_w_owned + }, + { + "this_up_trav_unused_rel", + Traversal_this_up_trav_unused_rel + }, + { + "this_optional_self", + Traversal_this_optional_self + }, + { + "this_optional_up", + Traversal_this_optional_up + }, + { + "this_optional_self_up", + Traversal_this_optional_self_up + }, + { + "this_written_optional_self", + Traversal_this_written_optional_self + }, + { + "this_written_optional_up", + Traversal_this_written_optional_up + }, + { + "this_written_optional_self_up", + Traversal_this_written_optional_self_up + }, + { + "fixed_src_w_up", + Traversal_fixed_src_w_up + }, + { + "match_empty_table_up", + Traversal_match_empty_table_up + }, + { + "match_empty_table_up_written", + Traversal_match_empty_table_up_written + }, + { + "match_empty_table_up_implicit_isa", + Traversal_match_empty_table_up_implicit_isa + }, + { + "match_empty_table_up_written_implicit_isa", + Traversal_match_empty_table_up_written_implicit_isa + }, + { + "match_empty_table_up_isa", + Traversal_match_empty_table_up_isa + }, + { + "match_empty_table_up_written_isa", + Traversal_match_empty_table_up_written_isa + }, + { + "up_after_add_batched_to_parent", + Traversal_up_after_add_batched_to_parent + }, + { + "up_component_after_parent_table_change", + Traversal_up_component_after_parent_table_change + }, + { + "up_component_w_singleton_after_parent_table_change", + Traversal_up_component_w_singleton_after_parent_table_change + }, + { + "up_component_w_var_after_parent_table_change", + Traversal_up_component_w_var_after_parent_table_change + }, + { + "test_up_component_after_parent_table_change", + Traversal_test_up_component_after_parent_table_change + }, + { + "test_up_component_w_singleton_after_parent_table_change", + Traversal_test_up_component_w_singleton_after_parent_table_change + }, + { + "up_component_after_parent_table_change_no_data", + Traversal_up_component_after_parent_table_change_no_data + }, + { + "up_component_w_singleton_after_parent_table_change_no_data", + Traversal_up_component_w_singleton_after_parent_table_change_no_data + }, + { + "up_component_w_var_after_parent_table_change_no_data", + Traversal_up_component_w_var_after_parent_table_change_no_data + }, + { + "test_up_component_after_parent_table_change_no_data", + Traversal_test_up_component_after_parent_table_change_no_data + }, + { + "test_up_component_w_singleton_after_parent_table_change_no_data", + Traversal_test_up_component_w_singleton_after_parent_table_change_no_data + }, + { + "this_up_childof_isa_childof", + Traversal_this_up_childof_isa_childof + }, + { + "this_up_isa_childof", + Traversal_this_up_isa_childof + }, + { + "this_up_isa_isa_childof", + Traversal_this_up_isa_isa_childof + }, + { + "this_up_isa_childof_isa", + Traversal_this_up_isa_childof_isa + }, + { + "this_up_isa_childof_isa_childof", + Traversal_this_up_isa_childof_isa_childof + }, + { + "this_self_up_childof_isa_childof", + Traversal_this_self_up_childof_isa_childof + }, + { + "this_self_up_isa_childof", + Traversal_this_self_up_isa_childof + }, + { + "this_self_up_isa_isa_childof", + Traversal_this_self_up_isa_isa_childof + }, + { + "this_self_up_isa_childof_isa", + Traversal_this_self_up_isa_childof_isa + }, + { + "this_self_up_isa_childof_isa_childof", + Traversal_this_self_up_isa_childof_isa_childof + }, + { + "this_written_up_childof_isa_childof", + Traversal_this_written_up_childof_isa_childof + }, + { + "this_written_up_isa_childof", + Traversal_this_written_up_isa_childof + }, + { + "this_written_up_isa_isa_childof", + Traversal_this_written_up_isa_isa_childof + }, + { + "this_written_up_isa_childof_isa", + Traversal_this_written_up_isa_childof_isa + }, + { + "this_written_up_isa_childof_isa_childof", + Traversal_this_written_up_isa_childof_isa_childof + }, + { + "this_written_self_up_childof_isa_childof", + Traversal_this_written_self_up_childof_isa_childof + }, + { + "this_written_self_up_isa_childof", + Traversal_this_written_self_up_isa_childof + }, + { + "this_written_self_up_isa_isa_childof", + Traversal_this_written_self_up_isa_isa_childof + }, + { + "this_written_self_up_isa_childof_isa", + Traversal_this_written_self_up_isa_childof_isa + }, + { + "this_written_self_up_isa_childof_isa_childof", + Traversal_this_written_self_up_isa_childof_isa_childof + } +}; + +bake_test_case Cascade_testcases[] = { + { + "parent_cascade", + Cascade_parent_cascade + }, + { + "existing_custom_rel_cascade", + Cascade_existing_custom_rel_cascade + }, + { + "new_custom_rel_cascade", + Cascade_new_custom_rel_cascade + }, + { + "cascade_w_2_depths", + Cascade_cascade_w_2_depths + }, + { + "cascade_w_3_depths", + Cascade_cascade_w_3_depths + }, + { + "cascade_w_2_depths_desc", + Cascade_cascade_w_2_depths_desc + }, + { + "cascade_w_3_depths_desc", + Cascade_cascade_w_3_depths_desc + }, + { + "existing_isa_cascade", + Cascade_existing_isa_cascade + }, + { + "new_isa_cascade", + Cascade_new_isa_cascade + }, + { + "childof_cascade", + Cascade_childof_cascade + }, + { + "cascade_rematch_2_lvls", + Cascade_cascade_rematch_2_lvls + }, + { + "cascade_rematch_2_lvls_2_relations", + Cascade_cascade_rematch_2_lvls_2_relations + }, + { + "cascade_topological", + Cascade_cascade_topological + }, + { + "cascade_desc_rematch_2_lvls", + Cascade_cascade_desc_rematch_2_lvls + }, + { + "cascade_desc_rematch_2_lvls_2_relations", + Cascade_cascade_desc_rematch_2_lvls_2_relations + }, + { + "cascade_desc_topological", + Cascade_cascade_desc_topological + }, + { + "cascade_after_recycled_parent_change", + Cascade_cascade_after_recycled_parent_change + }, + { + "invalid_cascade_for_uncached", + Cascade_invalid_cascade_for_uncached + }, + { + "invalid_cascade_for_first", + Cascade_invalid_cascade_for_first + }, + { + "invalid_cascade_for_second", + Cascade_invalid_cascade_for_second + }, + { + "invalid_desc_without_cascade", + Cascade_invalid_desc_without_cascade + }, + { + "invalid_desc_for_first", + Cascade_invalid_desc_for_first + }, + { + "invalid_desc_for_second", + Cascade_invalid_desc_for_second + } +}; + +bake_test_case Cached_testcases[] = { + { + "simple_query_existing_table", + Cached_simple_query_existing_table + }, + { + "simple_query_2_existing_tables", + Cached_simple_query_2_existing_tables + }, + { + "simple_query_new_table", + Cached_simple_query_new_table + }, + { + "simple_query_2_new_tables", + Cached_simple_query_2_new_tables + }, + { + "simple_query_existing_and_new_table", + Cached_simple_query_existing_and_new_table + }, + { + "wildcard_query_existing_table", + Cached_wildcard_query_existing_table + }, + { + "wildcard_query_new_table", + Cached_wildcard_query_new_table + }, + { + "wildcard_query_existing_table_2_results_p_table", + Cached_wildcard_query_existing_table_2_results_p_table + }, + { + "wildcard_query_new_table_2_results_p_table", + Cached_wildcard_query_new_table_2_results_p_table + }, + { + "wildcard_query_2nd_term", + Cached_wildcard_query_2nd_term + }, + { + "wildcard_query_2nd_term_self", + Cached_wildcard_query_2nd_term_self + }, + { + "simple_query_existing_empty_table", + Cached_simple_query_existing_empty_table + }, + { + "simple_query_existing_empty_type", + Cached_simple_query_existing_empty_type + }, + { + "simple_query_new_empty_table", + Cached_simple_query_new_empty_table + }, + { + "component_query_existing_table", + Cached_component_query_existing_table + }, + { + "component_query_new_table", + Cached_component_query_new_table + }, + { + "component_query_existing_empty_table", + Cached_component_query_existing_empty_table + }, + { + "2_component_query_existing_empty_table", + Cached_2_component_query_existing_empty_table + }, + { + "2_component_query_existing_empty_type", + Cached_2_component_query_existing_empty_type + }, + { + "only_optional", + Cached_only_optional + }, + { + "only_optional_new_empty_table", + Cached_only_optional_new_empty_table + }, + { + "only_optional_new_empty_non_empty_table", + Cached_only_optional_new_empty_non_empty_table + }, + { + "only_optional_new_unset_tables", + Cached_only_optional_new_unset_tables + }, + { + "singleton_w_optional_new_empty_table", + Cached_singleton_w_optional_new_empty_table + }, + { + "singleton_w_optional_new_empty_non_empty_table", + Cached_singleton_w_optional_new_empty_non_empty_table + }, + { + "singleton_w_optional_new_unset_tables", + Cached_singleton_w_optional_new_unset_tables + }, + { + "query_w_from_entity_match_after", + Cached_query_w_from_entity_match_after + }, + { + "query_w_from_singleton_match_after", + Cached_query_w_from_singleton_match_after + }, + { + "query_rematch_optional_after_add", + Cached_query_rematch_optional_after_add + }, + { + "get_owned_tag", + Cached_get_owned_tag + }, + { + "get_shared_tag", + Cached_get_shared_tag + }, + { + "explicit_delete", + Cached_explicit_delete + }, + { + "get_column_size", + Cached_get_column_size + }, + { + "stresstest_query_free", + Cached_stresstest_query_free + }, + { + "query_optional_tag", + Cached_query_optional_tag + }, + { + "query_optional_shared_tag", + Cached_query_optional_shared_tag + }, + { + "query_iter_10_tags", + Cached_query_iter_10_tags + }, + { + "query_iter_10_components", + Cached_query_iter_10_components + }, + { + "iter_type_set", + Cached_iter_type_set + }, + { + "filter_term", + Cached_filter_term + }, + { + "2_terms_1_filter", + Cached_2_terms_1_filter + }, + { + "3_terms_2_filter", + Cached_3_terms_2_filter + }, + { + "add_singleton_after_query", + Cached_add_singleton_after_query + }, + { + "query_w_component_from_parent_from_non_this", + Cached_query_w_component_from_parent_from_non_this + }, + { + "create_query_while_pending", + Cached_create_query_while_pending + }, + { + "empty_query", + Cached_empty_query + }, + { + "not_pair_relation_wildcard", + Cached_not_pair_relation_wildcard + }, + { + "not_pair_object_wildcard", + Cached_not_pair_object_wildcard + }, + { + "two_pair_wildcards_one_not", + Cached_two_pair_wildcards_one_not + }, + { + "two_pair_wildcards_one_not_any", + Cached_two_pair_wildcards_one_not_any + }, + { + "implicit_existing_isa_superset", + Cached_implicit_existing_isa_superset + }, + { + "implicit_new_isa_superset", + Cached_implicit_new_isa_superset + }, + { + "isa_superset", + Cached_isa_superset + }, + { + "isa_superset_2_lvls", + Cached_isa_superset_2_lvls + }, + { + "isa_superset_3_lvls", + Cached_isa_superset_3_lvls + }, + { + "isa_superset_2_lvls_owned", + Cached_isa_superset_2_lvls_owned + }, + { + "isa_superset_3_lvls_owned", + Cached_isa_superset_3_lvls_owned + }, + { + "isa_superset_owned_empty_table_after_match", + Cached_isa_superset_owned_empty_table_after_match + }, + { + "isa_self_superset", + Cached_isa_self_superset + }, + { + "childof_superset", + Cached_childof_superset + }, + { + "superset_2_targets", + Cached_superset_2_targets + }, + { + "superset_2_relations", + Cached_superset_2_relations + }, + { + "superset_2_relations_instanced", + Cached_superset_2_relations_instanced + }, + { + "superset_2_relations_w_component", + Cached_superset_2_relations_w_component + }, + { + "superset_2_relations_instanced_w_component", + Cached_superset_2_relations_instanced_w_component + }, + { + "parent", + Cached_parent + }, + { + "isa_rematch", + Cached_isa_rematch + }, + { + "childof_rematch", + Cached_childof_rematch + }, + { + "isa_unmatch", + Cached_isa_unmatch + }, + { + "childof_unmatch", + Cached_childof_unmatch + }, + { + "isa_rematch_2_lvls", + Cached_isa_rematch_2_lvls + }, + { + "childof_rematch_2_lvls", + Cached_childof_rematch_2_lvls + }, + { + "childof_rematch_from_isa", + Cached_childof_rematch_from_isa + }, + { + "rematch_optional_ref", + Cached_rematch_optional_ref + }, + { + "rematch_optional_ref_w_2_refs", + Cached_rematch_optional_ref_w_2_refs + }, + { + "rematch_optional_ref_tag_w_ref_component", + Cached_rematch_optional_ref_tag_w_ref_component + }, + { + "rematch_after_add_to_recycled_parent", + Cached_rematch_after_add_to_recycled_parent + }, + { + "match_query_expr_from_scope", + Cached_match_query_expr_from_scope + }, + { + "query_long_or_w_ref", + Cached_query_long_or_w_ref + }, + { + "query_w_pair_id_and_subj", + Cached_query_w_pair_id_and_subj + }, + { + "rematch_after_delete_inherited_tag", + Cached_rematch_after_delete_inherited_tag + }, + { + "rematch_after_delete_rel_of_inherited_pair", + Cached_rematch_after_delete_rel_of_inherited_pair + }, + { + "rematch_after_delete_obj_of_inherited_pair", + Cached_rematch_after_delete_obj_of_inherited_pair + }, + { + "rematch_empty", + Cached_rematch_empty + }, + { + "rematch_empty_table_w_superset", + Cached_rematch_empty_table_w_superset + }, + { + "2_self_up_terms_new_tables", + Cached_2_self_up_terms_new_tables + }, + { + "this_self_up_childof_pair_new_tables", + Cached_this_self_up_childof_pair_new_tables + } +}; + +bake_test_case ChangeDetection_testcases[] = { + { + "query_changed_after_new", + ChangeDetection_query_changed_after_new + }, + { + "query_changed_after_delete", + ChangeDetection_query_changed_after_delete + }, + { + "query_changed_after_add", + ChangeDetection_query_changed_after_add + }, + { + "query_changed_after_remove", + ChangeDetection_query_changed_after_remove + }, + { + "query_changed_after_set", + ChangeDetection_query_changed_after_set + }, + { + "query_change_after_modified", + ChangeDetection_query_change_after_modified + }, + { + "query_change_after_out_system", + ChangeDetection_query_change_after_out_system + }, + { + "query_change_after_out_query_no_data_flag", + ChangeDetection_query_change_after_out_query_no_data_flag + }, + { + "query_change_after_in_system", + ChangeDetection_query_change_after_in_system + }, + { + "query_change_after_modified_out_term", + ChangeDetection_query_change_after_modified_out_term + }, + { + "query_change_check_iter", + ChangeDetection_query_change_check_iter + }, + { + "query_change_check_iter_after_skip_read", + ChangeDetection_query_change_check_iter_after_skip_read + }, + { + "query_change_check_iter_after_skip_write", + ChangeDetection_query_change_check_iter_after_skip_write + }, + { + "query_change_parent_term", + ChangeDetection_query_change_parent_term + }, + { + "query_change_prefab_term", + ChangeDetection_query_change_prefab_term + }, + { + "query_change_parent_term_w_tag", + ChangeDetection_query_change_parent_term_w_tag + }, + { + "query_change_prefab_term_w_tag", + ChangeDetection_query_change_prefab_term_w_tag + }, + { + "query_changed_w_or", + ChangeDetection_query_changed_w_or + }, + { + "query_changed_or", + ChangeDetection_query_changed_or + }, + { + "query_changed_w_singleton", + ChangeDetection_query_changed_w_singleton + }, + { + "query_changed_w_only_singleton", + ChangeDetection_query_changed_w_only_singleton + }, + { + "query_changed_w_only_singleton_after_set", + ChangeDetection_query_changed_w_only_singleton_after_set + }, + { + "query_changed_w_only_singleton_after_out_term", + ChangeDetection_query_changed_w_only_singleton_after_out_term + }, + { + "query_changed_w_only_singleton_after_singleton_out_term", + ChangeDetection_query_changed_w_only_singleton_after_singleton_out_term + }, + { + "query_changed_w_only_parent", + ChangeDetection_query_changed_w_only_parent + }, + { + "query_changed_w_only_parent_after_set", + ChangeDetection_query_changed_w_only_parent_after_set + }, + { + "query_changed_w_only_parent_after_out_term", + ChangeDetection_query_changed_w_only_parent_after_out_term + }, + { + "query_changed_w_only_parent_after_parent_out_term", + ChangeDetection_query_changed_w_only_parent_after_parent_out_term + }, + { + "query_changed_tag", + ChangeDetection_query_changed_tag + }, + { + "query_changed_no_source", + ChangeDetection_query_changed_no_source + }, + { + "query_changed_no_source_component", + ChangeDetection_query_changed_no_source_component + }, + { + "query_changed_w_not_out", + ChangeDetection_query_changed_w_not_out + }, + { + "query_change_w_optional", + ChangeDetection_query_change_w_optional + }, + { + "query_changed_after_count", + ChangeDetection_query_changed_after_count + } +}; + +bake_test_case GroupBy_testcases[] = { + { + "group_by", + GroupBy_group_by + }, + { + "group_by_w_ctx", + GroupBy_group_by_w_ctx + }, + { + "group_by_w_sort_reverse_group_creation", + GroupBy_group_by_w_sort_reverse_group_creation + }, + { + "group_by_iter_one", + GroupBy_group_by_iter_one + }, + { + "group_by_iter_one_all_groups", + GroupBy_group_by_iter_one_all_groups + }, + { + "group_by_iter_one_empty", + GroupBy_group_by_iter_one_empty + }, + { + "group_by_iter_one_empty_query", + GroupBy_group_by_iter_one_empty_query + }, + { + "group_by_iter_one_empty_table", + GroupBy_group_by_iter_one_empty_table + }, + { + "group_by_w_deleted_group_id", + GroupBy_group_by_w_deleted_group_id + }, + { + "group_by_callbacks", + GroupBy_group_by_callbacks + }, + { + "group_by_default_action", + GroupBy_group_by_default_action + }, + { + "group_table_count", + GroupBy_group_table_count + } +}; + +bake_test_case MemberTarget_testcases[] = { + { + "this_member_eq_1", + MemberTarget_this_member_eq_1 + }, + { + "this_member_eq_2", + MemberTarget_this_member_eq_2 + }, + { + "this_member_eq_no_matches", + MemberTarget_this_member_eq_no_matches + }, + { + "this_member_eq_all_matches", + MemberTarget_this_member_eq_all_matches + }, + { + "this_member_wildcard", + MemberTarget_this_member_wildcard + }, + { + "this_member_var", + MemberTarget_this_member_var + }, + { + "this_member_var_written", + MemberTarget_this_member_var_written + }, + { + "this_member_var_read", + MemberTarget_this_member_var_read + }, + { + "this_member_eq_1_2nd_member", + MemberTarget_this_member_eq_1_2nd_member + }, + { + "this_member_eq_2_2nd_member", + MemberTarget_this_member_eq_2_2nd_member + }, + { + "this_member_var_same_1", + MemberTarget_this_member_var_same_1 + }, + { + "this_member_var_same_2", + MemberTarget_this_member_var_same_2 + }, + { + "this_written_member_eq_1", + MemberTarget_this_written_member_eq_1 + }, + { + "this_written_member_eq_2", + MemberTarget_this_written_member_eq_2 + }, + { + "this_written_member_wildcard", + MemberTarget_this_written_member_wildcard + }, + { + "this_written_member_var", + MemberTarget_this_written_member_var + }, + { + "this_member_neq_1", + MemberTarget_this_member_neq_1 + }, + { + "this_member_neq_2", + MemberTarget_this_member_neq_2 + }, + { + "this_member_neq_no_matches", + MemberTarget_this_member_neq_no_matches + }, + { + "this_member_neq_all_matches", + MemberTarget_this_member_neq_all_matches + }, + { + "this_member_neq_wildcard", + MemberTarget_this_member_neq_wildcard + }, + { + "this_written_member_neq_1", + MemberTarget_this_written_member_neq_1 + }, + { + "this_written_member_neq_2", + MemberTarget_this_written_member_neq_2 + }, + { + "this_written_member_neq_no_matches", + MemberTarget_this_written_member_neq_no_matches + }, + { + "this_written_member_neq_all_matches", + MemberTarget_this_written_member_neq_all_matches + }, + { + "this_written_member_neq_wildcard", + MemberTarget_this_written_member_neq_wildcard + }, + { + "this_member_eq_optional", + MemberTarget_this_member_eq_optional + }, + { + "this_member_eq_optional_wildcard", + MemberTarget_this_member_eq_optional_wildcard + }, + { + "this_written_member_eq_optional", + MemberTarget_this_written_member_eq_optional + }, + { + "this_written_member_eq_optional_wildcard", + MemberTarget_this_written_member_eq_optional_wildcard + }, + { + "this_member_eq_w_other_tag", + MemberTarget_this_member_eq_w_other_tag + }, + { + "this_member_eq_w_other_component", + MemberTarget_this_member_eq_w_other_component + }, + { + "this_written_member_eq_w_other_tag", + MemberTarget_this_written_member_eq_w_other_tag + }, + { + "this_written_member_eq_w_other_component", + MemberTarget_this_written_member_eq_w_other_component + }, + { + "this_written_member_eq_w_other_inherit_component", + MemberTarget_this_written_member_eq_w_other_inherit_component + }, + { + "this_2_or", + MemberTarget_this_2_or + }, + { + "this_3_or", + MemberTarget_this_3_or + }, + { + "this_written_2_or", + MemberTarget_this_written_2_or + }, + { + "this_written_3_or", + MemberTarget_this_written_3_or + }, + { + "var_2_or", + MemberTarget_var_2_or + }, + { + "var_3_or", + MemberTarget_var_3_or + }, + { + "this_2_or_w_2_members", + MemberTarget_this_2_or_w_2_members + }, + { + "this_2_or_w_2_types", + MemberTarget_this_2_or_w_2_types + }, + { + "this_written_2_or_w_2_members", + MemberTarget_this_written_2_or_w_2_members + }, + { + "this_written_2_or_w_2_types", + MemberTarget_this_written_2_or_w_2_types + }, + { + "this_2_or_2_types_wildcard", + MemberTarget_this_2_or_2_types_wildcard + }, + { + "this_2_or_2_types_dep_var", + MemberTarget_this_2_or_2_types_dep_var + }, + { + "var_written_2_or", + MemberTarget_var_written_2_or + }, + { + "var_written_3_or", + MemberTarget_var_written_3_or + }, + { + "var_member_eq", + MemberTarget_var_member_eq + }, + { + "var_member_eq_no_matches", + MemberTarget_var_member_eq_no_matches + }, + { + "var_member_eq_all_matches", + MemberTarget_var_member_eq_all_matches + }, + { + "var_member_wildcard", + MemberTarget_var_member_wildcard + }, + { + "var_member_neq", + MemberTarget_var_member_neq + }, + { + "var_member_neq_no_matches", + MemberTarget_var_member_neq_no_matches + }, + { + "var_member_neq_all_matches", + MemberTarget_var_member_neq_all_matches + }, + { + "var_written_member_eq", + MemberTarget_var_written_member_eq + }, + { + "var_written_member_eq_no_matches", + MemberTarget_var_written_member_eq_no_matches + }, + { + "var_written_member_eq_all_matches", + MemberTarget_var_written_member_eq_all_matches + }, + { + "var_written_member_wildcard", + MemberTarget_var_written_member_wildcard + }, + { + "var_written_member_neq", + MemberTarget_var_written_member_neq + }, + { + "var_written_member_neq_no_matches", + MemberTarget_var_written_member_neq_no_matches + }, + { + "var_written_member_neq_all_matches", + MemberTarget_var_written_member_neq_all_matches + } +}; + +bake_test_case Toggle_testcases[] = { + { + "fixed_src_1_tag_toggle", + Toggle_fixed_src_1_tag_toggle + }, + { + "fixed_src_1_component_toggle", + Toggle_fixed_src_1_component_toggle + }, + { + "fixed_src_2_tag_toggle", + Toggle_fixed_src_2_tag_toggle + }, + { + "fixed_src_2_component_toggle", + Toggle_fixed_src_2_component_toggle + }, + { + "fixed_2_src_w_toggle", + Toggle_fixed_2_src_w_toggle + }, + { + "this_w_fixed_src_w_toggle", + Toggle_this_w_fixed_src_w_toggle + }, + { + "fixed_src_w_this_w_toggle", + Toggle_fixed_src_w_this_w_toggle + }, + { + "this_from_nothing", + Toggle_this_from_nothing + }, + { + "this", + Toggle_this + }, + { + "this_skip_initial", + Toggle_this_skip_initial + }, + { + "this_pair", + Toggle_this_pair + }, + { + "this_pair_skip_initial", + Toggle_this_pair_skip_initial + }, + { + "this_tag", + Toggle_this_tag + }, + { + "this_tag_pair", + Toggle_this_tag_pair + }, + { + "this_tag_pair_wildcard", + Toggle_this_tag_pair_wildcard + }, + { + "this_toggle_shared_self_up", + Toggle_this_toggle_shared_self_up + }, + { + "this_toggle_shared_up", + Toggle_this_toggle_shared_up + }, + { + "this_toggle_shared_self_up_w_self", + Toggle_this_toggle_shared_self_up_w_self + }, + { + "this_toggle_shared_up_w_self", + Toggle_this_toggle_shared_up_w_self + }, + { + "this_toggle_shared_self_up_w_self_reverse", + Toggle_this_toggle_shared_self_up_w_self_reverse + }, + { + "this_toggle_shared_up_w_self_reverse", + Toggle_this_toggle_shared_up_w_self_reverse + }, + { + "this_toggle_shared_self_up_w_self_toggle", + Toggle_this_toggle_shared_self_up_w_self_toggle + }, + { + "this_toggle_shared_up_w_self_toggle", + Toggle_this_toggle_shared_up_w_self_toggle + }, + { + "this_toggle_shared_self_up_w_self_toggle_reverse", + Toggle_this_toggle_shared_self_up_w_self_toggle_reverse + }, + { + "this_toggle_shared_up_w_self_toggle_reverse", + Toggle_this_toggle_shared_up_w_self_toggle_reverse + }, + { + "this_toggle_not_shared_self_up", + Toggle_this_toggle_not_shared_self_up + }, + { + "this_toggle_not_shared_up", + Toggle_this_toggle_not_shared_up + }, + { + "this_toggle_optional_shared_self_up", + Toggle_this_toggle_optional_shared_self_up + }, + { + "this_toggle_optional_shared_up", + Toggle_this_toggle_optional_shared_up + }, + { + "this_64_mod_1", + Toggle_this_64_mod_1 + }, + { + "this_64_mod_2", + Toggle_this_64_mod_2 + }, + { + "this_64_mod_3", + Toggle_this_64_mod_3 + }, + { + "this_64_mod_7", + Toggle_this_64_mod_7 + }, + { + "this_64_mod_8", + Toggle_this_64_mod_8 + }, + { + "this_64_mod_10", + Toggle_this_64_mod_10 + }, + { + "this_64_mod_64", + Toggle_this_64_mod_64 + }, + { + "this_64_mod_256", + Toggle_this_64_mod_256 + }, + { + "this_64_mod_1024", + Toggle_this_64_mod_1024 + }, + { + "this_100_mod_1", + Toggle_this_100_mod_1 + }, + { + "this_100_mod_2", + Toggle_this_100_mod_2 + }, + { + "this_100_mod_3", + Toggle_this_100_mod_3 + }, + { + "this_100_mod_7", + Toggle_this_100_mod_7 + }, + { + "this_100_mod_8", + Toggle_this_100_mod_8 + }, + { + "this_100_mod_10", + Toggle_this_100_mod_10 + }, + { + "this_100_mod_64", + Toggle_this_100_mod_64 + }, + { + "this_100_mod_256", + Toggle_this_100_mod_256 + }, + { + "this_100_mod_1024", + Toggle_this_100_mod_1024 + }, + { + "this_128_mod_1", + Toggle_this_128_mod_1 + }, + { + "this_128_mod_2", + Toggle_this_128_mod_2 + }, + { + "this_128_mod_3", + Toggle_this_128_mod_3 + }, + { + "this_128_mod_7", + Toggle_this_128_mod_7 + }, + { + "this_128_mod_8", + Toggle_this_128_mod_8 + }, + { + "this_128_mod_10", + Toggle_this_128_mod_10 + }, + { + "this_128_mod_64", + Toggle_this_128_mod_64 + }, + { + "this_128_mod_256", + Toggle_this_128_mod_256 + }, + { + "this_128_mod_1024", + Toggle_this_128_mod_1024 + }, + { + "this_200_mod_1", + Toggle_this_200_mod_1 + }, + { + "this_200_mod_2", + Toggle_this_200_mod_2 + }, + { + "this_200_mod_3", + Toggle_this_200_mod_3 + }, + { + "this_200_mod_7", + Toggle_this_200_mod_7 + }, + { + "this_200_mod_8", + Toggle_this_200_mod_8 + }, + { + "this_200_mod_10", + Toggle_this_200_mod_10 + }, + { + "this_200_mod_64", + Toggle_this_200_mod_64 + }, + { + "this_200_mod_256", + Toggle_this_200_mod_256 + }, + { + "this_200_mod_1024", + Toggle_this_200_mod_1024 + }, + { + "this_1024_mod_1", + Toggle_this_1024_mod_1 + }, + { + "this_1024_mod_2", + Toggle_this_1024_mod_2 + }, + { + "this_1024_mod_3", + Toggle_this_1024_mod_3 + }, + { + "this_1024_mod_7", + Toggle_this_1024_mod_7 + }, + { + "this_1024_mod_8", + Toggle_this_1024_mod_8 + }, + { + "this_1024_mod_10", + Toggle_this_1024_mod_10 + }, + { + "this_1024_mod_64", + Toggle_this_1024_mod_64 + }, + { + "this_1024_mod_256", + Toggle_this_1024_mod_256 + }, + { + "this_1024_mod_1024", + Toggle_this_1024_mod_1024 + }, + { + "this_enabled_64_mod_1", + Toggle_this_enabled_64_mod_1 + }, + { + "this_enabled_64_mod_2", + Toggle_this_enabled_64_mod_2 + }, + { + "this_enabled_64_mod_3", + Toggle_this_enabled_64_mod_3 + }, + { + "this_enabled_64_mod_7", + Toggle_this_enabled_64_mod_7 + }, + { + "this_enabled_64_mod_8", + Toggle_this_enabled_64_mod_8 + }, + { + "this_enabled_64_mod_10", + Toggle_this_enabled_64_mod_10 + }, + { + "this_enabled_64_mod_64", + Toggle_this_enabled_64_mod_64 + }, + { + "this_enabled_64_mod_256", + Toggle_this_enabled_64_mod_256 + }, + { + "this_enabled_64_mod_1024", + Toggle_this_enabled_64_mod_1024 + }, + { + "this_enabled_100_mod_1", + Toggle_this_enabled_100_mod_1 + }, + { + "this_enabled_100_mod_2", + Toggle_this_enabled_100_mod_2 + }, + { + "this_enabled_100_mod_3", + Toggle_this_enabled_100_mod_3 + }, + { + "this_enabled_100_mod_7", + Toggle_this_enabled_100_mod_7 + }, + { + "this_enabled_100_mod_8", + Toggle_this_enabled_100_mod_8 + }, + { + "this_enabled_100_mod_10", + Toggle_this_enabled_100_mod_10 + }, + { + "this_enabled_100_mod_64", + Toggle_this_enabled_100_mod_64 + }, + { + "this_enabled_100_mod_256", + Toggle_this_enabled_100_mod_256 + }, + { + "this_enabled_100_mod_1024", + Toggle_this_enabled_100_mod_1024 + }, + { + "this_enabled_128_mod_1", + Toggle_this_enabled_128_mod_1 + }, + { + "this_enabled_128_mod_2", + Toggle_this_enabled_128_mod_2 + }, + { + "this_enabled_128_mod_3", + Toggle_this_enabled_128_mod_3 + }, + { + "this_enabled_128_mod_7", + Toggle_this_enabled_128_mod_7 + }, + { + "this_enabled_128_mod_8", + Toggle_this_enabled_128_mod_8 + }, + { + "this_enabled_128_mod_10", + Toggle_this_enabled_128_mod_10 + }, + { + "this_enabled_128_mod_64", + Toggle_this_enabled_128_mod_64 + }, + { + "this_enabled_128_mod_256", + Toggle_this_enabled_128_mod_256 + }, + { + "this_enabled_128_mod_1024", + Toggle_this_enabled_128_mod_1024 + }, + { + "this_enabled_200_mod_1", + Toggle_this_enabled_200_mod_1 + }, + { + "this_enabled_200_mod_2", + Toggle_this_enabled_200_mod_2 + }, + { + "this_enabled_200_mod_3", + Toggle_this_enabled_200_mod_3 + }, + { + "this_enabled_200_mod_7", + Toggle_this_enabled_200_mod_7 + }, + { + "this_enabled_200_mod_8", + Toggle_this_enabled_200_mod_8 + }, + { + "this_enabled_200_mod_10", + Toggle_this_enabled_200_mod_10 + }, + { + "this_enabled_200_mod_64", + Toggle_this_enabled_200_mod_64 + }, + { + "this_enabled_200_mod_256", + Toggle_this_enabled_200_mod_256 + }, + { + "this_enabled_200_mod_1024", + Toggle_this_enabled_200_mod_1024 + }, + { + "this_enabled_1024_mod_1", + Toggle_this_enabled_1024_mod_1 + }, + { + "this_enabled_1024_mod_2", + Toggle_this_enabled_1024_mod_2 + }, + { + "this_enabled_1024_mod_3", + Toggle_this_enabled_1024_mod_3 + }, + { + "this_enabled_1024_mod_7", + Toggle_this_enabled_1024_mod_7 + }, + { + "this_enabled_1024_mod_8", + Toggle_this_enabled_1024_mod_8 + }, + { + "this_enabled_1024_mod_10", + Toggle_this_enabled_1024_mod_10 + }, + { + "this_enabled_1024_mod_64", + Toggle_this_enabled_1024_mod_64 + }, + { + "this_enabled_1024_mod_256", + Toggle_this_enabled_1024_mod_256 + }, + { + "this_enabled_1024_mod_1024", + Toggle_this_enabled_1024_mod_1024 + }, + { + "this_mod_2_2_bitsets", + Toggle_this_mod_2_2_bitsets + }, + { + "this_mod_8_2_bitsets", + Toggle_this_mod_8_2_bitsets + }, + { + "this_mod_64_2_bitsets", + Toggle_this_mod_64_2_bitsets + }, + { + "this_mod_256_2_bitsets", + Toggle_this_mod_256_2_bitsets + }, + { + "this_mod_1024_2_bitsets", + Toggle_this_mod_1024_2_bitsets + }, + { + "this_randomized_2_bitsets", + Toggle_this_randomized_2_bitsets + }, + { + "this_randomized_3_bitsets", + Toggle_this_randomized_3_bitsets + }, + { + "this_randomized_4_bitsets", + Toggle_this_randomized_4_bitsets + }, + { + "this_w_other_tag", + Toggle_this_w_other_tag + }, + { + "this_w_other_component", + Toggle_this_w_other_component + }, + { + "this_not", + Toggle_this_not + }, + { + "this_written_not_1024_mod_2", + Toggle_this_written_not_1024_mod_2 + }, + { + "this_written_not_1024_mod_3", + Toggle_this_written_not_1024_mod_3 + }, + { + "this_written_not_1024_mod_7", + Toggle_this_written_not_1024_mod_7 + }, + { + "this_written_not_1024_mod_8", + Toggle_this_written_not_1024_mod_8 + }, + { + "this_written_not_1024_mod_10", + Toggle_this_written_not_1024_mod_10 + }, + { + "this_written_not_1024_mod_64", + Toggle_this_written_not_1024_mod_64 + }, + { + "this_written_not_1024_mod_256", + Toggle_this_written_not_1024_mod_256 + }, + { + "this_written_not_1024_mod_1024", + Toggle_this_written_not_1024_mod_1024 + }, + { + "this_optional", + Toggle_this_optional + }, + { + "this_written_optional_1024_mod_2", + Toggle_this_written_optional_1024_mod_2 + }, + { + "this_written_optional_1024_mod_3", + Toggle_this_written_optional_1024_mod_3 + }, + { + "this_written_optional_1024_mod_7", + Toggle_this_written_optional_1024_mod_7 + }, + { + "this_written_optional_1024_mod_8", + Toggle_this_written_optional_1024_mod_8 + }, + { + "this_written_optional_1024_mod_10", + Toggle_this_written_optional_1024_mod_10 + }, + { + "this_written_optional_1024_mod_64", + Toggle_this_written_optional_1024_mod_64 + }, + { + "this_written_optional_1024_mod_256", + Toggle_this_written_optional_1024_mod_256 + }, + { + "this_written_optional_1024_mod_1024", + Toggle_this_written_optional_1024_mod_1024 + }, + { + "this_written_toggle_w_not_toggle", + Toggle_this_written_toggle_w_not_toggle + }, + { + "this_written_not_toggle_w_toggle", + Toggle_this_written_not_toggle_w_toggle + }, + { + "this_written_toggle_w_optional_toggle", + Toggle_this_written_toggle_w_optional_toggle + }, + { + "this_written_optional_toggle_w_toggle", + Toggle_this_written_optional_toggle_w_toggle + }, + { + "this_written_not_w_optional_toggle", + Toggle_this_written_not_w_optional_toggle + }, + { + "this_written_optional_w_not_toggle", + Toggle_this_written_optional_w_not_toggle + }, + { + "this_written_2_not_toggle", + Toggle_this_written_2_not_toggle + }, + { + "this_written_2_optional_toggle", + Toggle_this_written_2_optional_toggle + }, + { + "this_written_toggle_w_2_not_toggle", + Toggle_this_written_toggle_w_2_not_toggle + }, + { + "this_written_toggle_w_2_optional_toggle", + Toggle_this_written_toggle_w_2_optional_toggle + }, + { + "this_written_2_toggle_w_not_toggle", + Toggle_this_written_2_toggle_w_not_toggle + }, + { + "this_written_2_toggle_w_optional_toggle", + Toggle_this_written_2_toggle_w_optional_toggle + }, + { + "this_sort", + Toggle_this_sort + }, + { + "this_table_move_2_from_3", + Toggle_this_table_move_2_from_3 + }, + { + "toggle_0_src_only_term", + Toggle_toggle_0_src_only_term + }, + { + "toggle_0_src", + Toggle_toggle_0_src + } +}; + +bake_test_case Sparse_testcases[] = { + { + "1_fixed_sparse", + Sparse_1_fixed_sparse + }, + { + "1_fixed_sparse_none", + Sparse_1_fixed_sparse_none + }, + { + "1_this_sparse_simple", + Sparse_1_this_sparse_simple + }, + { + "1_this_sparse", + Sparse_1_this_sparse + }, + { + "1_this_sparse_none", + Sparse_1_this_sparse_none + }, + { + "1_this_sparse_written", + Sparse_1_this_sparse_written + }, + { + "1_this_sparse_written_none", + Sparse_1_this_sparse_written_none + }, + { + "1_var_sparse", + Sparse_1_var_sparse + }, + { + "1_var_sparse_none", + Sparse_1_var_sparse_none + }, + { + "1_var_sparse_written", + Sparse_1_var_sparse_written + }, + { + "1_var_sparse_written_none", + Sparse_1_var_sparse_written_none + }, + { + "2_sparse_simple", + Sparse_2_sparse_simple + }, + { + "2_sparse", + Sparse_2_sparse + }, + { + "2_sparse_and_regular", + Sparse_2_sparse_and_regular + }, + { + "2_regular_and_sparse", + Sparse_2_regular_and_sparse + }, + { + "1_sparse_self", + Sparse_1_sparse_self + }, + { + "1_sparse_up", + Sparse_1_sparse_up + }, + { + "1_sparse_self_up", + Sparse_1_sparse_self_up + }, + { + "1_sparse_written_self", + Sparse_1_sparse_written_self + }, + { + "1_sparse_written_up", + Sparse_1_sparse_written_up + }, + { + "1_sparse_written_self_up", + Sparse_1_sparse_written_self_up + }, + { + "sparse_0_src_only_term", + Sparse_sparse_0_src_only_term + }, + { + "sparse_0_src", + Sparse_sparse_0_src + } +}; + +bake_test_case Union_testcases[] = { + { + "1_fixed_union_any", + Union_1_fixed_union_any + }, + { + "1_fixed_union_wildcard", + Union_1_fixed_union_wildcard + }, + { + "1_fixed_union_tgt", + Union_1_fixed_union_tgt + }, + { + "1_fixed_union_tgt_var", + Union_1_fixed_union_tgt_var + }, + { + "1_fixed_union_tgt_var_written", + Union_1_fixed_union_tgt_var_written + }, + { + "1_this_union_any", + Union_1_this_union_any + }, + { + "1_this_union_wildcard", + Union_1_this_union_wildcard + }, + { + "1_this_union_tgt", + Union_1_this_union_tgt + }, + { + "1_this_union_tgt_var", + Union_1_this_union_tgt_var + }, + { + "1_this_union_tgt_var_written", + Union_1_this_union_tgt_var_written + }, + { + "1_var_union_any", + Union_1_var_union_any + }, + { + "1_var_union_wildcard", + Union_1_var_union_wildcard + }, + { + "1_var_union_tgt", + Union_1_var_union_tgt + }, + { + "1_var_union_tgt_var", + Union_1_var_union_tgt_var + }, + { + "1_var_union_tgt_var_written", + Union_1_var_union_tgt_var_written + }, + { + "1_this_written_union_any", + Union_1_this_written_union_any + }, + { + "1_this_written_union_wildcard", + Union_1_this_written_union_wildcard + }, + { + "1_this_written_union_tgt", + Union_1_this_written_union_tgt + }, + { + "1_this_written_union_tgt_var", + Union_1_this_written_union_tgt_var + }, + { + "1_this_written_union_tgt_var_written", + Union_1_this_written_union_tgt_var_written + }, + { + "1_var_written_union_any", + Union_1_var_written_union_any + }, + { + "1_var_written_union_wildcard", + Union_1_var_written_union_wildcard + }, + { + "1_var_written_union_tgt", + Union_1_var_written_union_tgt + }, + { + "1_var_written_union_tgt_var", + Union_1_var_written_union_tgt_var + }, + { + "1_var_written_union_tgt_var_written", + Union_1_var_written_union_tgt_var_written + }, + { + "not_fixed_union_any", + Union_not_fixed_union_any + }, + { + "not_fixed_union_wildcard", + Union_not_fixed_union_wildcard + }, + { + "not_fixed_union_tgt", + Union_not_fixed_union_tgt + }, + { + "not_fixed_union_var", + Union_not_fixed_union_var + }, + { + "not_fixed_union_var_written", + Union_not_fixed_union_var_written + }, + { + "not_this_written_union_any", + Union_not_this_written_union_any + }, + { + "not_this_written_union_wildcard", + Union_not_this_written_union_wildcard + }, + { + "not_this_written_union_tgt", + Union_not_this_written_union_tgt + }, + { + "not_this_written_union_var", + Union_not_this_written_union_var + }, + { + "not_this_written_union_var_written", + Union_not_this_written_union_var_written + }, + { + "query_switch", + Union_query_switch + }, + { + "query_1_case_1_type", + Union_query_1_case_1_type + }, + { + "query_1_case_2_types", + Union_query_1_case_2_types + }, + { + "query_2_cases_1_type", + Union_query_2_cases_1_type + }, + { + "query_2_cases_2_types", + Union_query_2_cases_2_types + }, + { + "query_after_remove", + Union_query_after_remove + }, + { + "sort", + Union_sort + }, + { + "query_recycled_tags", + Union_query_recycled_tags + }, + { + "query_single_case", + Union_query_single_case + }, + { + "match_switch_on_base_instance", + Union_match_switch_on_base_instance + }, + { + "switch_w_bitset_query", + Union_switch_w_bitset_query + }, + { + "switch_w_bitset_query_inv", + Union_switch_w_bitset_query_inv + }, + { + "switch_w_bitset_query_2_elems", + Union_switch_w_bitset_query_2_elems + }, + { + "switch_w_bitset_query_2_elems_skip", + Union_switch_w_bitset_query_2_elems_skip + }, + { + "switch_w_bitset_query_elems_interleaved", + Union_switch_w_bitset_query_elems_interleaved + }, + { + "switch_w_bitset_query_elems_interleaved_2_types", + Union_switch_w_bitset_query_elems_interleaved_2_types + }, + { + "component_relation", + Union_component_relation + }, + { + "switch_term_filter", + Union_switch_term_filter + }, + { + "2_terms_switch_term_filter", + Union_2_terms_switch_term_filter + }, + { + "match_switch_w_switch", + Union_match_switch_w_switch + }, + { + "match_switch_w_case", + Union_match_switch_w_case + }, + { + "match_switch_w_case_2_terms", + Union_match_switch_w_case_2_terms + }, + { + "up", + Union_up + }, + { + "self_up", + Union_self_up + }, + { + "up_written", + Union_up_written + }, + { + "self_up_written", + Union_self_up_written + }, + { + "existing_union_table", + Union_existing_union_table + }, + { + "new_union_table", + Union_new_union_table + }, + { + "existing_union_table_w_tgt", + Union_existing_union_table_w_tgt + }, + { + "new_union_table_w_tgt", + Union_new_union_table_w_tgt + }, + { + "tgt_w_generation", + Union_tgt_w_generation + }, + { + "tgt_w_not_alive", + Union_tgt_w_not_alive + }, + { + "for_switch_filter_term", + Union_for_switch_filter_term + }, + { + "union_from_nothing", + Union_union_from_nothing + }, + { + "union_tgt_from_nothing", + Union_union_tgt_from_nothing + }, + { + "tgt_inherited", + Union_tgt_inherited + } +}; + +bake_test_case OrderBy_testcases[] = { + { + "sort_by_component", + OrderBy_sort_by_component + }, + { + "sort_by_component_2_tables", + OrderBy_sort_by_component_2_tables + }, + { + "sort_by_component_3_tables", + OrderBy_sort_by_component_3_tables + }, + { + "sort_by_entity", + OrderBy_sort_by_entity + }, + { + "sort_after_add", + OrderBy_sort_after_add + }, + { + "sort_after_remove", + OrderBy_sort_after_remove + }, + { + "sort_after_delete", + OrderBy_sort_after_delete + }, + { + "sort_after_set", + OrderBy_sort_after_set + }, + { + "sort_after_system", + OrderBy_sort_after_system + }, + { + "sort_after_query", + OrderBy_sort_after_query + }, + { + "sort_by_component_same_value_1", + OrderBy_sort_by_component_same_value_1 + }, + { + "sort_by_component_same_value_2", + OrderBy_sort_by_component_same_value_2 + }, + { + "sort_by_component_move_pivot", + OrderBy_sort_by_component_move_pivot + }, + { + "sort_1000_entities", + OrderBy_sort_1000_entities + }, + { + "sort_1000_entities_w_duplicates", + OrderBy_sort_1000_entities_w_duplicates + }, + { + "sort_1000_entities_again", + OrderBy_sort_1000_entities_again + }, + { + "sort_1000_entities_2_types", + OrderBy_sort_1000_entities_2_types + }, + { + "sort_1500_entities_3_types", + OrderBy_sort_1500_entities_3_types + }, + { + "sort_2000_entities_4_types", + OrderBy_sort_2000_entities_4_types + }, + { + "sort_2_entities_2_types", + OrderBy_sort_2_entities_2_types + }, + { + "sort_3_entities_3_types", + OrderBy_sort_3_entities_3_types + }, + { + "sort_3_entities_3_types_2", + OrderBy_sort_3_entities_3_types_2 + }, + { + "sort_4_entities_4_types", + OrderBy_sort_4_entities_4_types + }, + { + "sort_1000_entities_2_types_again", + OrderBy_sort_1000_entities_2_types_again + }, + { + "sort_1000_entities_add_type_after_sort", + OrderBy_sort_1000_entities_add_type_after_sort + }, + { + "sort_shared_component", + OrderBy_sort_shared_component + }, + { + "sort_shared_component_childof", + OrderBy_sort_shared_component_childof + }, + { + "sort_w_tags_only", + OrderBy_sort_w_tags_only + }, + { + "sort_childof_marked", + OrderBy_sort_childof_marked + }, + { + "sort_isa_marked", + OrderBy_sort_isa_marked + }, + { + "sort_relation_marked", + OrderBy_sort_relation_marked + }, + { + "dont_resort_after_set_unsorted_component", + OrderBy_dont_resort_after_set_unsorted_component + }, + { + "dont_resort_after_set_unsorted_component_w_tag", + OrderBy_dont_resort_after_set_unsorted_component_w_tag + }, + { + "dont_resort_after_set_unsorted_component_w_tag_w_out_term", + OrderBy_dont_resort_after_set_unsorted_component_w_tag_w_out_term + }, + { + "sort_component_not_queried_for", + OrderBy_sort_component_not_queried_for + }, + { + "sort_by_wildcard", + OrderBy_sort_by_wildcard + }, + { + "sort_shared_w_delete", + OrderBy_sort_shared_w_delete + }, + { + "sort_w_nontrivial_component", + OrderBy_sort_w_nontrivial_component + }, + { + "sort_by_wildcard", + OrderBy_sort_by_wildcard + }, + { + "sort_not_term", + OrderBy_sort_not_term + }, + { + "sort_or_term", + OrderBy_sort_or_term + }, + { + "sort_optional_term", + OrderBy_sort_optional_term + } +}; + +bake_test_case OrderByEntireTable_testcases[] = { + { + "sort_by_component", + OrderByEntireTable_sort_by_component + }, + { + "sort_by_component_2_tables", + OrderByEntireTable_sort_by_component_2_tables + }, + { + "sort_by_component_3_tables", + OrderByEntireTable_sort_by_component_3_tables + }, + { + "sort_by_entity", + OrderByEntireTable_sort_by_entity + }, + { + "sort_after_add", + OrderByEntireTable_sort_after_add + }, + { + "sort_after_remove", + OrderByEntireTable_sort_after_remove + }, + { + "sort_after_delete", + OrderByEntireTable_sort_after_delete + }, + { + "sort_after_set", + OrderByEntireTable_sort_after_set + }, + { + "sort_after_system", + OrderByEntireTable_sort_after_system + }, + { + "sort_after_query", + OrderByEntireTable_sort_after_query + }, + { + "sort_by_component_same_value_1", + OrderByEntireTable_sort_by_component_same_value_1 + }, + { + "sort_by_component_same_value_2", + OrderByEntireTable_sort_by_component_same_value_2 + }, + { + "sort_by_component_move_pivot", + OrderByEntireTable_sort_by_component_move_pivot + }, + { + "sort_1000_entities", + OrderByEntireTable_sort_1000_entities + }, + { + "sort_1000_entities_w_duplicates", + OrderByEntireTable_sort_1000_entities_w_duplicates + }, + { + "sort_1000_entities_again", + OrderByEntireTable_sort_1000_entities_again + }, + { + "sort_1000_entities_2_types", + OrderByEntireTable_sort_1000_entities_2_types + }, + { + "sort_1500_entities_3_types", + OrderByEntireTable_sort_1500_entities_3_types + }, + { + "sort_2000_entities_4_types", + OrderByEntireTable_sort_2000_entities_4_types + }, + { + "sort_2_entities_2_types", + OrderByEntireTable_sort_2_entities_2_types + }, + { + "sort_3_entities_3_types", + OrderByEntireTable_sort_3_entities_3_types + }, + { + "sort_3_entities_3_types_2", + OrderByEntireTable_sort_3_entities_3_types_2 + }, + { + "sort_4_entities_4_types", + OrderByEntireTable_sort_4_entities_4_types + }, + { + "sort_1000_entities_2_types_again", + OrderByEntireTable_sort_1000_entities_2_types_again + }, + { + "sort_1000_entities_add_type_after_sort", + OrderByEntireTable_sort_1000_entities_add_type_after_sort + }, + { + "sort_shared_component", + OrderByEntireTable_sort_shared_component + }, + { + "sort_w_tags_only", + OrderByEntireTable_sort_w_tags_only + }, + { + "sort_childof_marked", + OrderByEntireTable_sort_childof_marked + }, + { + "sort_isa_marked", + OrderByEntireTable_sort_isa_marked + }, + { + "sort_relation_marked", + OrderByEntireTable_sort_relation_marked + }, + { + "dont_resort_after_set_unsorted_component", + OrderByEntireTable_dont_resort_after_set_unsorted_component + }, + { + "dont_resort_after_set_unsorted_component_w_tag", + OrderByEntireTable_dont_resort_after_set_unsorted_component_w_tag + }, + { + "dont_resort_after_set_unsorted_component_w_tag_w_out_term", + OrderByEntireTable_dont_resort_after_set_unsorted_component_w_tag_w_out_term + }, + { + "sort_shared_w_delete", + OrderByEntireTable_sort_shared_w_delete + }, + { + "sort_not_term", + OrderByEntireTable_sort_not_term + }, + { + "sort_or_term", + OrderByEntireTable_sort_or_term + }, + { + "sort_optional_term", + OrderByEntireTable_sort_optional_term + } +}; + +bake_test_case QueryStr_testcases[] = { + { + "one_term", + QueryStr_one_term + }, + { + "one_term_w_inout", + QueryStr_one_term_w_inout + }, + { + "two_terms", + QueryStr_two_terms + }, + { + "two_terms_w_inout", + QueryStr_two_terms_w_inout + }, + { + "three_terms_w_or", + QueryStr_three_terms_w_or + }, + { + "three_terms_w_or_inout", + QueryStr_three_terms_w_or_inout + }, + { + "four_terms_three_w_or_inout", + QueryStr_four_terms_three_w_or_inout + }, + { + "one_term_w_pair", + QueryStr_one_term_w_pair + }, + { + "one_term_w_pair_entity_src", + QueryStr_one_term_w_pair_entity_src + }, + { + "one_term_w_self", + QueryStr_one_term_w_self + }, + { + "one_term_w_up", + QueryStr_one_term_w_up + }, + { + "one_term_w_self_up", + QueryStr_one_term_w_self_up + }, + { + "one_term_w_cascade", + QueryStr_one_term_w_cascade + }, + { + "one_term_w_0", + QueryStr_one_term_w_0 + }, + { + "one_term_w_singleton", + QueryStr_one_term_w_singleton + }, + { + "one_term_w_final_pair", + QueryStr_one_term_w_final_pair + }, + { + "one_term_w_final_dont_inherit", + QueryStr_one_term_w_final_dont_inherit + }, + { + "one_term_w_final_inherit", + QueryStr_one_term_w_final_inherit + }, + { + "one_term_w_final_override", + QueryStr_one_term_w_final_override + }, + { + "one_term_w_src_var", + QueryStr_one_term_w_src_var + }, + { + "one_term_w_first_var", + QueryStr_one_term_w_first_var + }, + { + "one_term_w_second_var", + QueryStr_one_term_w_second_var + }, + { + "one_term_w_first_var_entity_src", + QueryStr_one_term_w_first_var_entity_src + }, + { + "one_term_w_pair_w_0_entity", + QueryStr_one_term_w_pair_w_0_entity + }, + { + "not_term", + QueryStr_not_term + }, + { + "wildcard_term", + QueryStr_wildcard_term + }, + { + "scopes", + QueryStr_scopes + }, + { + "pred_eq", + QueryStr_pred_eq + }, + { + "pred_neq", + QueryStr_pred_neq + }, + { + "pred_eq_name", + QueryStr_pred_eq_name + }, + { + "pred_neq_name", + QueryStr_pred_neq_name + }, + { + "pred_eq_m", + QueryStr_pred_eq_m + }, + { + "pred_neq_m", + QueryStr_pred_neq_m + } +}; + +const char* Basic_cache_kind_param[] = {"default", "auto"}; +bake_test_param Basic_params[] = { + {"cache_kind", (char**)Basic_cache_kind_param, 2} +}; +const char* Combinations_cache_kind_param[] = {"default", "auto"}; +const char* Combinations_on_instantiate_param[] = {"override", "inherit", "dont_inherit"}; +bake_test_param Combinations_params[] = { + {"cache_kind", (char**)Combinations_cache_kind_param, 2}, + {"on_instantiate", (char**)Combinations_on_instantiate_param, 3} +}; +const char* Variables_cache_kind_param[] = {"default", "auto"}; +bake_test_param Variables_params[] = { + {"cache_kind", (char**)Variables_cache_kind_param, 2} +}; +const char* Operators_cache_kind_param[] = {"default", "auto"}; +bake_test_param Operators_params[] = { + {"cache_kind", (char**)Operators_cache_kind_param, 2} +}; +const char* Recycled_cache_kind_param[] = {"default", "auto"}; +bake_test_param Recycled_params[] = { + {"cache_kind", (char**)Recycled_cache_kind_param, 2} +}; +const char* BuiltinPredicates_cache_kind_param[] = {"default", "auto"}; +bake_test_param BuiltinPredicates_params[] = { + {"cache_kind", (char**)BuiltinPredicates_cache_kind_param, 2} +}; +const char* Scopes_cache_kind_param[] = {"default", "auto"}; +const char* Scopes_on_instantiate_param[] = {"override", "inherit"}; +bake_test_param Scopes_params[] = { + {"cache_kind", (char**)Scopes_cache_kind_param, 2}, + {"on_instantiate", (char**)Scopes_on_instantiate_param, 2} +}; +const char* Traversal_cache_kind_param[] = {"default", "auto"}; +bake_test_param Traversal_params[] = { + {"cache_kind", (char**)Traversal_cache_kind_param, 2} +}; +const char* MemberTarget_cache_kind_param[] = {"default", "auto"}; +bake_test_param MemberTarget_params[] = { + {"cache_kind", (char**)MemberTarget_cache_kind_param, 2} +}; +const char* Toggle_cache_kind_param[] = {"default", "auto"}; +bake_test_param Toggle_params[] = { + {"cache_kind", (char**)Toggle_cache_kind_param, 2} +}; +const char* Sparse_cache_kind_param[] = {"default", "auto"}; +bake_test_param Sparse_params[] = { + {"cache_kind", (char**)Sparse_cache_kind_param, 2} +}; +const char* Union_cache_kind_param[] = {"default", "auto"}; +bake_test_param Union_params[] = { + {"cache_kind", (char**)Union_cache_kind_param, 2} +}; + +static bake_test_suite suites[] = { + { + "Validator", + NULL, + NULL, + 142, + Validator_testcases + }, + { + "Parser", + NULL, + NULL, + 300, + Parser_testcases + }, + { + "Basic", + Basic_setup, + NULL, + 230, + Basic_testcases, + 1, + Basic_params + }, + { + "Combinations", + Combinations_setup, + NULL, + 9, + Combinations_testcases, + 2, + Combinations_params + }, + { + "Plan", + NULL, + NULL, + 79, + Plan_testcases + }, + { + "Variables", + Variables_setup, + NULL, + 189, + Variables_testcases, + 1, + Variables_params + }, + { + "Operators", + Operators_setup, + NULL, + 149, + Operators_testcases, + 1, + Operators_params + }, + { + "Transitive", + NULL, + NULL, + 72, + Transitive_testcases + }, + { + "ComponentInheritance", + NULL, + NULL, + 31, + ComponentInheritance_testcases + }, + { + "Recycled", + Recycled_setup, + NULL, + 6, + Recycled_testcases, + 1, + Recycled_params + }, + { + "BuiltinPredicates", + BuiltinPredicates_setup, + NULL, + 90, + BuiltinPredicates_testcases, + 1, + BuiltinPredicates_params + }, + { + "Scopes", + Scopes_setup, + NULL, + 11, + Scopes_testcases, + 2, + Scopes_params + }, + { + "Traversal", + Traversal_setup, + NULL, + 147, + Traversal_testcases, + 1, + Traversal_params + }, + { + "Cascade", + NULL, + NULL, + 23, + Cascade_testcases + }, + { + "Cached", + NULL, + NULL, + 87, + Cached_testcases + }, + { + "ChangeDetection", + NULL, + NULL, + 34, + ChangeDetection_testcases + }, + { + "GroupBy", + NULL, + NULL, + 12, + GroupBy_testcases + }, + { + "MemberTarget", + MemberTarget_setup, + NULL, + 63, + MemberTarget_testcases, + 1, + MemberTarget_params + }, + { + "Toggle", + Toggle_setup, + NULL, + 163, + Toggle_testcases, + 1, + Toggle_params + }, + { + "Sparse", + Sparse_setup, + NULL, + 23, + Sparse_testcases, + 1, + Sparse_params + }, + { + "Union", + Union_setup, + NULL, + 71, + Union_testcases, + 1, + Union_params + }, + { + "OrderBy", + NULL, + NULL, + 42, + OrderBy_testcases + }, + { + "OrderByEntireTable", + NULL, + NULL, + 37, + OrderByEntireTable_testcases + }, + { + "QueryStr", + NULL, + NULL, + 33, + QueryStr_testcases + } +}; + +int main(int argc, char *argv[]) { + return bake_test_run("query", argc, argv, suites, 24); +} diff --git a/vendors/flecs/test/query/src/util.c b/vendors/flecs/test/query/src/util.c new file mode 100644 index 000000000..2878e490d --- /dev/null +++ b/vendors/flecs/test/query/src/util.c @@ -0,0 +1,69 @@ +#include +#include + +void probe_system_w_ctx( + ecs_iter_t *it, + Probe *ctx) +{ + if (!ctx) { + return; + } + + ctx->param = it->param; + ctx->system = it->system; + ctx->event = it->event; + ctx->event_id = it->event_id; + ctx->offset = 0; + ctx->term_count = it->field_count; + ctx->term_index = it->term_index; + + int i; + for (i = 0; i < ctx->term_count; i ++) { + ctx->c[ctx->invoked][i] = it->ids[i]; + ctx->s[ctx->invoked][i] = ecs_field_src(it, i); + + ecs_id_t e = ecs_field_id(it, i); + test_assert(e != 0); + } + + for (i = 0; i < it->count; i ++) { + if (i + ctx->count < 256) { + ctx->e[i + ctx->count] = it->entities[i]; + } else { + /* can't store more than that, tests shouldn't rely on + * getting back more than 256 results */ + } + } + ctx->count += it->count; + + ctx->invoked ++; +} + +void probe_iter( + ecs_iter_t *it) +{ + Probe *ctx = ecs_get_ctx(it->world); + if (!ctx) { + ctx = it->ctx; + } + probe_system_w_ctx(it, ctx); +} + +void probe_has_entity(Probe *probe, ecs_entity_t e) { + int i; + for (i = 0; i < probe->count; i ++) { + if (probe->e[i] == e) { + break; + } + } + + test_assert(i != probe->count); +} + +void install_test_abort(void) { + ecs_os_set_api_defaults(); + ecs_os_api_t os_api = ecs_os_api; + os_api.abort_ = test_abort; + ecs_os_set_api(&os_api); + ecs_log_set_level(-5); +} diff --git a/vendors/flecs/test/script/include/script.h b/vendors/flecs/test/script/include/script.h new file mode 100644 index 000000000..dc5ab3510 --- /dev/null +++ b/vendors/flecs/test/script/include/script.h @@ -0,0 +1,75 @@ +#ifndef SCRIPT_H +#define SCRIPT_H + +/* This generated file contains includes for project dependencies */ +#include "script/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Multiline strings */ +#define HEAD +#define LINE "\n" + +#define MAX_SYS_COLUMNS (20) +#define MAX_ENTITIES (256) +#define MAX_INVOCATIONS (1024) + +typedef struct Probe { + ecs_entity_t system; + ecs_entity_t event; + ecs_id_t event_id; + int32_t offset; + int32_t count; + int32_t invoked; + int32_t term_count; + int32_t term_index; + ecs_entity_t e[MAX_ENTITIES]; + ecs_entity_t c[MAX_INVOCATIONS][MAX_SYS_COLUMNS]; + ecs_entity_t s[MAX_INVOCATIONS][MAX_SYS_COLUMNS]; + void *param; +} Probe; + +typedef struct IterData { + ecs_entity_t component; + ecs_entity_t component_2; + ecs_entity_t component_3; + ecs_entity_t new_entities[MAX_ENTITIES]; + int32_t entity_count; +} IterData; + +typedef struct { + float x, y; +} Position, Velocity; + +typedef struct { + int32_t x, y; +} PositionI; + +typedef struct { + float value; +} Mass; + +typedef struct { + float x, y; +} Point; + +typedef struct { + Point start, stop; +} Line; + +typedef struct { + PositionI start, stop; +} LineI; + +typedef struct { + float x, y, z; +} Vec3; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendors/flecs/test/script/include/script/bake_config.h b/vendors/flecs/test/script/include/script/bake_config.h new file mode 100644 index 000000000..172fcc1dd --- /dev/null +++ b/vendors/flecs/test/script/include/script/bake_config.h @@ -0,0 +1,25 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef SCRIPT_BAKE_CONFIG_H +#define SCRIPT_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include +#include + +#endif + diff --git a/vendors/flecs/test/script/project.json b/vendors/flecs/test/script/project.json new file mode 100644 index 000000000..3c653ec9f --- /dev/null +++ b/vendors/flecs/test/script/project.json @@ -0,0 +1,664 @@ +{ + "id": "script", + "type": "application", + "value": { + "author": "Sander Mertens", + "description": "Test project for flecs script", + "public": false, + "use": [ + "flecs" + ] + }, + "test": { + "testsuites": [{ + "id": "Eval", + "testcases": [ + "null", + "empty", + "space", + "space_newline", + "two_empty_newlines", + "three_empty_newlines", + "newline_trailing_space", + "newline_trailing_spaces", + "multiple_trailing_newlines", + "entity", + "entity_w_core_name", + "2_entities", + "line_comment", + "line_comment_before_stmt", + "line_comment_after_stmt", + "line_comment_between_stmt", + "multiple_line_comment", + "multiple_line_comment_w_newlines", + "line_comment_after_stmt_same_line", + "line_comment_before_scope_open", + "line_comment_after_newline_before_scope_open", + "line_comment_after_newline_before_newline_scope_open", + "multi_line_comment", + "multi_line_comment_before_stmt", + "multi_line_comment_after_stmt", + "multi_line_comment_between_stmt", + "multiple_multi_line_comment", + "multiple_multi_line_comment_w_newlines", + "multi_line_comment_after_stmt_same_line", + "multi_line_comment_before_scope_open", + "multi_line_comment_after_newline_before_scope_open", + "multi_line_comment_multiple_lines", + "hierarchy_1_child", + "hierarchy_2_children", + "hierarchy_1_child_same_line", + "hierarchy_2_children_same_line", + "hierarchy_2_children_same_line_no_trailing_comma", + "entity_after_hierarchy", + "newline_before_scope_open", + "newline_w_whitespace_before_scope_open", + "2_newline_before_scope_open", + "2_newline_w_whitespace_before_scope_open", + "hierarchy_2_levels", + "hierarchy_2_levels_2_subtrees", + "create_in_scope", + "hierarchy_w_pred_subj", + "hierarchy_custom_relation", + "hierarchy_custom_relation_2_levels", + "hierarchy_custom_relation_apply_to_object", + "hierarchy_custom_relation_apply_to_object_2_levels", + "entity_after_hierarchy_custom_relation", + "entity_after_hierarchy_custom_relation_2_levels", + "with_tag", + "with_tag_2_entities", + "with_tag_same_line", + "with_tag_2_entities_same_line", + "with_tag_2_entities_same_line_no_trailing_comma", + "with_tag_2_levels", + "with_tag_2_levels_2_subtrees", + "with_n_tags", + "with_n_tags_2_levels", + "with_after_scope", + "with_after_with", + "scope_inside_with_inside_scope", + "with_inside_scope", + "with_tag_core_name", + "inherit", + "inherit_newline", + "inherit_w_colon", + "inherit_w_colon_w_scope", + "assign_component_w_value", + "assign_tag_in_assign_scope", + "assign_tag_in_assign_scope_same_line", + "assign_tag_in_assign_scope_core_name", + "assign_component_value_in_assign_scope", + "assign_2_component_values_in_assign_scope", + "type_and_assign_in_plecs", + "type_and_assign_in_plecs_w_2_members", + "type_and_assign_in_plecs_w_3_members", + "type_and_assign_in_plecs_w_enum", + "type_and_assign_in_plecs_w_enum_using_meta", + "type_and_assign_in_plecs_w_enum_primitive_using_meta", + "type_and_assign_in_plecs_w_enum_primitive_and_struct", + "type_and_assign_in_plecs_nested_member", + "dot_assign_nested_member", + "dot_assign_binary_expr", + "open_scope_no_parent", + "create_subject_in_root_scope_w_resolvable_id", + "create_subject_in_scope_w_resolvable_id", + "create_subject_in_scope_w_resolvable_id_using", + "using_scope", + "using_nested_scope", + "using_nested_in_scope", + "using_with_scope", + "using_w_entity_ref_in_value_2_members", + "using_w_entity_ref_in_value_3_members", + "script_w_only_using", + "2_using_scope", + "2_using_in_different_scope", + "empty_scope_after_using", + "scope_after_assign", + "assign_after_inherit", + "multiple_tags_single_line", + "multiple_pairs_single_line", + "multiple_vars_single_line", + "multiple_assignments_single_line", + "2_stmts_in_scope_w_no_parent", + "assign_tag_to_parent", + "assign_component_to_parent", + "assign_to_parent_pair_w_new_entities_in_scope", + "assign_to_parent_pair_w_existing_entities_in_scope", + "default_child_component", + "default_child_component_w_assign", + "struct_type_w_default_child_component", + "struct_type_w_default_child_component_nested_member", + "enum_type_w_default_child_component", + "default_type_from_with", + "default_type_from_nested_with", + "default_type_from_with_in_entity_scope_w_default_type", + "default_type_from_entity_scope_in_with", + "scope_w_1_subj_and_2_pairs", + "inherit_from_multiple", + "assign_pair_component", + "assign_pair_component_in_scope", + "assign_pair_component_in_script", + "assign_pair_component_in_script_update", + "set_entity_names", + "oneof", + "brief_annotation", + "name_annotation", + "link_annotation", + "color_annotation", + "multiple_annotations", + "annotation_w_trailing_space", + "multiline_string", + "declaration_w_underscore_name", + "annotate_declaration", + "anonymous_entity", + "anonymous_entity_in_scope", + "anonymous_declaration", + "const_var_int", + "const_var_float", + "const_var_bool", + "const_var_string", + "const_var_struct", + "const_var_scoped", + "assign_component_from_var", + "assign_component_from_var_in_scope", + "scope_w_component_after_const_var", + "component_after_const_paren_expr", + "component_after_const_add_expr", + "component_after_const_sub_expr", + "component_after_const_mul_expr", + "component_after_const_div_expr", + "parse_with", + "parse_with_w_with", + "parse_with_w_tag", + "parse_with_value", + "parse_with_2_values", + "parse_with_2_nested_values", + "parse_with_var", + "parse_with_2_vars", + "parse_with_2_nested_vars", + "parse_with_var_in_scope", + "assign_const_w_expr", + "const_w_type", + "typed_const_w_composite_type", + "assign_var_to_typed_const_w_composite_type", + "using_wildcard", + "single_line_comment_in_value", + "single_line_comment_in_value_after_scope", + "multi_line_comment_in_value", + "multi_line_comment_in_value_after_scope", + "module_stmt", + "nested_module_stmt", + "module_stmt_w_scope", + "module_stmt_w_nested_scope", + "assign_singleton_tag", + "assign_singleton_component", + "assign_singleton_tag_w_scope", + "assign_singleton_2_tags_w_scope", + "assign_singleton_component_w_scope", + "assign_singleton_2_components_w_scope", + "with_pair_in_scope", + "with_pair_component_in_scope", + "pair_w_rel_var", + "pair_w_tgt_var", + "component_in_with_scope_in_scope", + "array_component", + "on_set_w_kind_paren_no_reflection", + "on_set_w_kind_paren", + "on_set_w_kind_no_paren_no_reflection", + "on_set_w_kind_no_paren", + "on_set_w_single_assign", + "on_set_w_single_assign_scoped_w_value", + "on_set_w_single_assign_scoped_no_value", + "if_true", + "if_true_no_else", + "if_false", + "if_true_var", + "if_false_var", + "if_10", + "if_0", + "if_true_in_scope", + "if_false_in_scope", + "if_lt", + "if_lt_const", + "isa_in_module", + "isa_hierarchy", + "isa_hierarchy_in_module", + "custom_isa_hierarchy_in_module", + "custom_isa_hierarchy_in_subtree", + "inherit_w_kind", + "inherit_w_kind_scope", + "inherit_w_kind_value", + "inherit_w_kind_value_scope", + "multiple_inherit_w_kind", + "multiple_inherit_w_kind_scope", + "auto_override_tag", + "auto_override_component", + "auto_override_pair", + "auto_override_pair_component", + "lowercase_prefab_kind", + "assign_component_to_const", + "assign_component_member_to_const", + "prefab_w_slot", + "prefab_w_slot_variant", + "const_w_component_expr", + "const_w_component_expr_in_scope", + "const_w_component_expr_in_module", + "const_w_component_in_scope_expr_in_scope", + "const_w_component_in_scope_expr_in_module", + "const_w_component_and_entity_in_scope_expr_in_scope", + "const_w_component_and_entity_in_scope_expr_in_module", + "path_tag_in_scope", + "path_tag_in_module", + "path_tag_in_nested_scope", + "path_tag_in_nested_module", + "dont_inherit_script_pair", + "update_script_w_anonymous", + "update_script_w_anonymous_paren", + "clear_script", + "clear_script_w_anonymous", + "clear_script_w_anonymous_paren", + "partial_assign", + "partial_assign_nontrivial", + "partial_assign_with", + "partial_assign_nontrivial_with", + "partial_assign_with_large_array", + "non_trivial_var_component", + "non_trivial_var_with", + "update_template_w_tag" + ] + }, { + "id": "Template", + "testcases": [ + "template_no_scope", + "template_no_props", + "template_prop", + "template_prop_space_colon", + "template_2_props", + "template_w_using", + "template_instance_w_default_values", + "template_instance_w_assign_default_values", + "template_instance_w_overridden_values", + "template_w_child", + "template_w_child_parse_script", + "template_w_child_parse_script_twice", + "template_w_child_update_after_parse", + "template_w_nested_child", + "template_w_prefab", + "template_w_prefab_tree", + "template_w_nested_template", + "instantiate_prefab_w_template", + "template_w_prefab_w_template", + "3_templates", + "template_nested_w_default_var", + "template_w_anonymous", + "template_w_anonymous_parse_again", + "template_w_composite_prop", + "template_with_with", + "module_w_template", + "module_w_nested_template", + "template_w_pair_w_this_var", + "prop_without_using_meta", + "hoist_var", + "anonymous_template_instance", + "anonymous_template_instance_no_scope", + "anonymous_template_instance_w_prop", + "anonymous_template_instance_w_prop_no_scope", + "with_after_template", + "with_in_scope_after_template" + ] + }, { + "id": "Error", + "testcases": [ + "multi_line_comment_after_newline_before_newline_scope_open", + "missing_end_of_scope", + "with_n_tags_2_levels_invalid_tag", + "assignment_to_non_component", + "struct_w_member_w_assignment_to_nothing", + "struct_w_member_w_assignment_to_empty_scope", + "invalid_nested_assignment", + "invalid_partial_pair_assignment", + "empty_assignment", + "empty_assignment_before_end_of_scope", + "default_type_with_tag", + "invalid_oneof", + "default_type_with_tag", + "unterminated_multiline_string", + "invalid_assign_multiline_string", + "const_var_redeclare", + "typed_const_w_composite_type_invalid_assignment", + "unterminated_multi_line_comment_in_value", + "pair_w_rel_var_invalid_type", + "pair_w_tgt_var_invalid_type", + "with_value_not_a_component", + "component_in_with_scope", + "component_in_with_scope_nested", + "assign_after_with_in_scope", + "not_an_array_component", + "array_component_w_curly_brackets", + "unknown_identifier", + "unknown_identifier_for_int_field", + "prefab_w_slot_no_parent", + "tag_not_found", + "component_not_found", + "pair_first_not_found", + "pair_second_not_found", + "kind_not_found", + "base_not_found", + "entity_w_anonymous_tag", + "member_expr_without_value_end_of_scope", + "member_expr_without_value_comma", + "member_expr_without_value_newline", + "2_member_expr_without_value", + "expr_junk_after_number", + "expr_junk_after_unary_minus", + "expr_comma_after_nothing", + "expr_digit_with_two_dots", + "template_empty", + "template_unresolved_tag", + "template_unresolved_component", + "template_unresolved_pair_relationship", + "template_unresolved_pair_target", + "template_unresolved_with_tag", + "template_unresolved_with_component", + "template_unresolved_with_pair_relationship", + "template_unresolved_with_pair_target", + "template_unresolved_tag_in_child", + "template_prop_no_type", + "template_prop_no_default", + "template_w_composite_prop_invalid_assignment", + "template_redeclare_prop_as_const", + "template_redeclare_prop_as_prop", + "template_redeclare_const_as_const", + "run_template_after_error", + "update_template_after_error", + "template_in_template" + ] + }, { + "id": "Expr", + "testcases": [ + "add_2_int_literals", + "add_2_int_literals_twice", + "sub_2_int_literals", + "mul_2_int_literals", + "div_2_int_literals", + "add_3_int_literals", + "add_3_int_literals_twice", + "sub_3_int_literals", + "mul_3_int_literals", + "div_3_int_literals", + "int_to_bool", + "bool_to_int", + "bool_to_uint", + "add_mul_3_int_literals", + "sub_mul_3_int_literals", + "div_mul_3_int_literals", + "add_div_3_int_literals", + "sub_div_3_int_literals", + "mul_div_3_int_literals", + "mul_add_mul_add_int_literals", + "mul_sub_mul_sub_int_literals", + "mul_div_mul_div_int_literals", + "div_add_div_add_int_literals", + "div_sub_div_sub_int_literals", + "div_sub_div_mul_int_literals", + "div_mul_div_mul_int_literals", + "add_2_flt_literals", + "sub_2_flt_literals", + "mul_2_flt_literals", + "div_2_flt_literals", + "add_2_int_neg_literals", + "sub_2_int_neg_literals", + "mul_2_int_neg_literals", + "div_2_int_neg_literals", + "mul_lparen_add_add_rparen_int_literals", + "mul_lparen_add_add_add_rparen_int_literals", + "mul_lparen_add_add_rparen_add_int_literals", + "lparen_add_add_rparen_mul_int_literals", + "lparen_add_add_add_rparen_mul_int_literals", + "double_paren_add_add", + "double_paren_literal", + "lparen_add_add_rparen_mul_lparen_add_add_rparen", + "float_result_add_2_int_literals", + "struct_result_add_2_int_literals", + "struct_result_add_2_2_fields_int_literals", + "struct_result_add_3_int_literals", + "struct_result_lparen_int_rparen", + "add_to_var", + "add_var_to", + "var_member", + "bool_cond_and_bool", + "bool_cond_or_bool", + "int_cond_and_int", + "int_cond_or_int", + "bool_cond_and_int", + "int_cond_and_bool", + "bool_cond_or_int", + "int_cond_or_bool", + "cond_eq_bool", + "cond_eq_int", + "cond_neq_bool", + "cond_neq_int", + "cond_eq_bool_int", + "cond_eq_int_flt", + "cond_eq_cond_and", + "cond_eq_cond_or", + "cond_gt_bool", + "cond_gt_int", + "cond_gt_flt", + "cond_gteq_bool", + "cond_gteq_int", + "cond_gteq_flt", + "cond_lt_bool", + "cond_lt_int", + "cond_lt_flt", + "cond_lteq_bool", + "cond_lteq_int", + "cond_lteq_flt", + "min_lparen_int_rparen", + "min_lparen_int_add_int_rparen", + "min_var", + "min_lparen_int_rparen_to_i64", + "min_lparen_int_rparen_to_i32", + "struct_w_min_var", + "struct_w_min_lparen_int_rparen", + "struct_w_min_lparen_var_rparen", + "shift_left_int", + "shift_right_int", + "shift_left_int_add_int", + "shift_left_int_mul_int", + "add_int_shift_left_int", + "mul_int_shift_left_int", + "add_int_shift_left_int_add_int", + "mul_int_shift_left_int_mul_int", + "entity_expr", + "entity_path_expr", + "entity_parent_func", + "entity_name_func", + "entity_doc_name_func", + "entity_chain_func", + "var_parent_func", + "var_name_func", + "var_doc_name_func", + "var_chain_func", + "interpolate_string_w_i32_var", + "interpolate_string_w_string_var", + "interpolate_string_w_entity_var", + "interpolate_string_w_id_var", + "interpolate_string_w_var_not_found", + "interpolate_string_w_entity_var_0", + "interpolate_string_w_var_special_chars", + "interpolate_string_w_var_before_after_text", + "interpolate_string_w_curly_brackets_var", + "interpolate_string_w_curly_brackets_expr", + "interpolate_string_w_curly_brackets_expr_w_var", + "interpolate_string_w_curly_brackets_expr_w_composite_var", + "interpolate_string_w_escape_var_operator", + "interpolate_string_w_escape_curly_brackets", + "interpolate_string_w_func", + "interpolate_string_w_func_chain", + "iter_to_vars_no_data", + "iter_to_vars_1_comp", + "iter_to_vars_2_comps", + "iter_to_vars_1_comp_1_tag", + "iter_to_vars_w_1_query_var", + "iter_to_vars_w_2_query_vars", + "component_expr", + "component_member_expr" + ] + }, { + "id": "Vars", + "testcases": [ + "declare_1_var", + "declare_2_vars", + "declare_vars_nested_scope", + "declare_vars_2_scopes", + "redeclare_var", + "i32_expr_w_i32_var", + "i32_expr_w_f32_var", + "i32_expr_w_string_var", + "string_expr_w_string_var", + "struct_expr_w_i32_vars", + "struct_expr_w_struct_var", + "nested_struct_expr_w_struct_var", + "redeclare_in_scope", + "init_fini_vars" + ] + }, { + "id": "Serialize", + "testcases": [ + "bool", + "byte", + "char", + "i8", + "i16", + "i32", + "i64", + "iptr", + "u8", + "u16", + "u32", + "u64", + "uptr", + "float", + "double", + "string", + "entity", + "entity_10k", + "id", + "enum", + "bitmask", + "float_nan", + "float_inf", + "double_nan", + "double_inf", + "struct_enum", + "struct_bitmask", + "struct_i32", + "struct_i32_i32", + "struct_entity", + "struct_id", + "array_i32_3", + "array_struct_i32_i32", + "array_array_i32_3", + "vector_i32_3", + "vector_struct_i32_i32", + "vector_array_i32_3", + "entity_entity_after_float", + "struct_nested_i32", + "struct_nested_i32_i32", + "struct_2_nested_i32_i32", + "struct_i32_array_3", + "struct_struct_i32_array_3", + "struct_struct_i32_i32_array_3", + "struct_w_array_type_i32_i32", + "struct_w_array_type_struct", + "struct_w_2_array_type_i32_i32", + "struct_w_2_array_type_struct", + "struct_partial", + "escape_simple_string", + "escape_newline", + "escape_2_newlines", + "escape_string_w_trailing_newline", + "escape_string_w_2_trailing_newlines", + "escape_string_w_delim" + ] + }, { + "id": "Deserialize", + "testcases": [ + "bool", + "byte", + "char", + "char_literal", + "i8", + "i16", + "i32", + "i64", + "iptr", + "u8", + "u16", + "u32", + "u64", + "uptr", + "float", + "double", + "negative_int", + "negative_float", + "invalid_i8", + "invalid_i16", + "invalid_i32", + "invalid_i64", + "invalid_iptr", + "invalid_u8", + "invalid_u16", + "invalid_u32", + "invalid_u64", + "invalid_uptr", + "invalid_float", + "invalid_double", + "string", + "entity", + "id", + "enum", + "bitmask", + "struct_enum", + "struct_bitmask", + "struct_i32", + "struct_i32_neg", + "struct_i32_i32", + "struct_entity", + "struct_id", + "struct_nested_i32", + "struct_nested_i32_i32", + "struct_2_nested_i32_i32", + "struct_member_i32", + "struct_member_i32_neg", + "struct_member_i32_i32", + "struct_member_nested_i32", + "struct_member_nested_i32_i32", + "struct_member_2_nested_i32_i32", + "struct_member_2_nested_i32_i32_reverse", + "struct_i32_array_3", + "struct_struct_i32_array_3", + "struct_struct_i32_i32_array_3", + "struct_w_array_type_i32_i32", + "struct_w_array_type_struct", + "struct_w_2_array_type_i32_i32", + "struct_w_2_array_type_struct", + "discover_type_int", + "discover_type_negative_int", + "discover_type_float", + "discover_type_negative_float", + "discover_type_string", + "discover_type_multiline_string", + "discover_type_entity", + "discover_type_bool", + "discover_type_unknown", + "discover_type_invalid", + "opaque_struct", + "opaque_struct_w_member", + "opaque_struct_w_member_reverse", + "struct_w_opaque_member" + ] + }] + } +} diff --git a/vendors/flecs/test/meta/src/DeserializeFromExpr.c b/vendors/flecs/test/script/src/Deserialize.c similarity index 61% rename from vendors/flecs/test/meta/src/DeserializeFromExpr.c rename to vendors/flecs/test/script/src/Deserialize.c index 8c0d9315a..a0e1d99ed 100644 --- a/vendors/flecs/test/meta/src/DeserializeFromExpr.c +++ b/vendors/flecs/test/script/src/Deserialize.c @@ -1,11 +1,11 @@ -#include +#include -void DeserializeFromExpr_bool(void) { +void Deserialize_bool(void) { ecs_world_t *world = ecs_init(); bool value = false; - const char *ptr = ecs_parse_expr(world, "true", &ecs_value(ecs_bool_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "true", &ecs_value_ptr(ecs_bool_t, &value), NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -14,12 +14,12 @@ void DeserializeFromExpr_bool(void) { ecs_fini(world); } -void DeserializeFromExpr_byte(void) { +void Deserialize_byte(void) { ecs_world_t *world = ecs_init(); ecs_byte_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_byte_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_byte_t, &value), NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -28,12 +28,12 @@ void DeserializeFromExpr_byte(void) { ecs_fini(world); } -void DeserializeFromExpr_char(void) { +void Deserialize_char(void) { ecs_world_t *world = ecs_init(); ecs_char_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_char_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_char_t, &value), NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -42,12 +42,12 @@ void DeserializeFromExpr_char(void) { ecs_fini(world); } -void DeserializeFromExpr_char_literal(void) { +void Deserialize_char_literal(void) { ecs_world_t *world = ecs_init(); ecs_char_t value = 0; - const char *ptr = ecs_parse_expr(world, "\"a\"", &ecs_value(ecs_char_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "\"a\"", &ecs_value_ptr(ecs_char_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 'a'); @@ -55,12 +55,12 @@ void DeserializeFromExpr_char_literal(void) { ecs_fini(world); } -void DeserializeFromExpr_i8(void) { +void Deserialize_i8(void) { ecs_world_t *world = ecs_init(); ecs_i8_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_i8_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_i8_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); @@ -68,12 +68,12 @@ void DeserializeFromExpr_i8(void) { ecs_fini(world); } -void DeserializeFromExpr_i16(void) { +void Deserialize_i16(void) { ecs_world_t *world = ecs_init(); ecs_i16_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_i16_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_i16_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); @@ -81,12 +81,12 @@ void DeserializeFromExpr_i16(void) { ecs_fini(world); } -void DeserializeFromExpr_i32(void) { +void Deserialize_i32(void) { ecs_world_t *world = ecs_init(); ecs_i32_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_i32_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_i32_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); @@ -94,12 +94,12 @@ void DeserializeFromExpr_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_i64(void) { +void Deserialize_i64(void) { ecs_world_t *world = ecs_init(); ecs_i64_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_i64_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_i64_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); @@ -107,12 +107,12 @@ void DeserializeFromExpr_i64(void) { ecs_fini(world); } -void DeserializeFromExpr_iptr(void) { +void Deserialize_iptr(void) { ecs_world_t *world = ecs_init(); ecs_iptr_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_iptr_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_iptr_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); @@ -120,12 +120,12 @@ void DeserializeFromExpr_iptr(void) { ecs_fini(world); } -void DeserializeFromExpr_u8(void) { +void Deserialize_u8(void) { ecs_world_t *world = ecs_init(); ecs_u8_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_u8_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_u8_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); @@ -133,12 +133,12 @@ void DeserializeFromExpr_u8(void) { ecs_fini(world); } -void DeserializeFromExpr_u16(void) { +void Deserialize_u16(void) { ecs_world_t *world = ecs_init(); ecs_u16_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_u16_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_u16_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); @@ -146,12 +146,12 @@ void DeserializeFromExpr_u16(void) { ecs_fini(world); } -void DeserializeFromExpr_u32(void) { +void Deserialize_u32(void) { ecs_world_t *world = ecs_init(); ecs_u32_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_u32_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_u32_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); @@ -159,29 +159,29 @@ void DeserializeFromExpr_u32(void) { ecs_fini(world); } -void DeserializeFromExpr_u64(void) { +void Deserialize_u64(void) { ecs_world_t *world = ecs_init(); { ecs_u64_t value = 0; - const char *ptr = ecs_parse_expr( - world, "0", &ecs_value(ecs_u64_t, &value), NULL); + const char *ptr = ecs_script_expr_run( + world, "0", &ecs_value_ptr(ecs_u64_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 0); } { ecs_u64_t value = 0; - const char *ptr = ecs_parse_expr( - world, "10", &ecs_value(ecs_u64_t, &value), NULL); + const char *ptr = ecs_script_expr_run( + world, "10", &ecs_value_ptr(ecs_u64_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); } { ecs_u64_t value = 0; - const char *ptr = ecs_parse_expr( - world, "2366700781656087864", &ecs_value(ecs_u64_t, &value), NULL); + const char *ptr = ecs_script_expr_run( + world, "2366700781656087864", &ecs_value_ptr(ecs_u64_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 2366700781656087864); } @@ -189,12 +189,12 @@ void DeserializeFromExpr_u64(void) { ecs_fini(world); } -void DeserializeFromExpr_uptr(void) { +void Deserialize_uptr(void) { ecs_world_t *world = ecs_init(); ecs_uptr_t value = 0; - const char *ptr = ecs_parse_expr(world, "10", &ecs_value(ecs_uptr_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10", &ecs_value_ptr(ecs_uptr_t, &value), NULL); test_assert(ptr != NULL); test_int(value, 10); @@ -202,12 +202,12 @@ void DeserializeFromExpr_uptr(void) { ecs_fini(world); } -void DeserializeFromExpr_float(void) { +void Deserialize_float(void) { ecs_world_t *world = ecs_init(); ecs_f32_t value = 0; - const char *ptr = ecs_parse_expr(world, "10.5", &ecs_value(ecs_f32_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10.5", &ecs_value_ptr(ecs_f32_t, &value), NULL); test_assert(ptr != NULL); test_flt(value, 10.5); @@ -215,12 +215,12 @@ void DeserializeFromExpr_float(void) { ecs_fini(world); } -void DeserializeFromExpr_double(void) { +void Deserialize_double(void) { ecs_world_t *world = ecs_init(); ecs_f64_t value = 0; - const char *ptr = ecs_parse_expr(world, "10.5", &ecs_value(ecs_f64_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "10.5", &ecs_value_ptr(ecs_f64_t, &value), NULL); test_assert(ptr != NULL); test_flt(value, 10.5); @@ -228,12 +228,12 @@ void DeserializeFromExpr_double(void) { ecs_fini(world); } -void DeserializeFromExpr_negative_int(void) { +void Deserialize_negative_int(void) { ecs_world_t *world = ecs_init(); ecs_i8_t value = 0; - const char *ptr = ecs_parse_expr(world, "-10", &ecs_value(ecs_i8_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "-10", &ecs_value_ptr(ecs_i8_t, &value), NULL); test_assert(ptr != NULL); test_int(value, -10); @@ -241,12 +241,12 @@ void DeserializeFromExpr_negative_int(void) { ecs_fini(world); } -void DeserializeFromExpr_negative_float(void) { +void Deserialize_negative_float(void) { ecs_world_t *world = ecs_init(); ecs_f32_t value = 0; - const char *ptr = ecs_parse_expr(world, "-10.5", &ecs_value(ecs_f32_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "-10.5", &ecs_value_ptr(ecs_f32_t, &value), NULL); test_assert(ptr != NULL); test_flt(value, -10.5); @@ -254,12 +254,168 @@ void DeserializeFromExpr_negative_float(void) { ecs_fini(world); } -void DeserializeFromExpr_string(void) { +void Deserialize_invalid_i8(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_i8_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_i16(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_i16_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_i32(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_i32_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_i64(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_i64_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_iptr(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_iptr_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_u8(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_u8_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_u16(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_u16_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_u32(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_u32_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_u64(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_u64_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_uptr(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_uptr_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_float(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_f32_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_invalid_double(void) { + ecs_world_t *world = ecs_init(); + + uint64_t value = 0; + + ecs_log_set_level(-4); + const char *ptr = ecs_script_expr_run( + world, "a", &ecs_value_ptr(ecs_f64_t, &value), NULL); + test_assert(ptr == NULL); + + ecs_fini(world); +} + +void Deserialize_string(void) { ecs_world_t *world = ecs_init(); ecs_string_t value = 0; - const char *ptr = ecs_parse_expr(world, "\"Hello World\"", &ecs_value(ecs_string_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "\"Hello World\"", &ecs_value_ptr(ecs_string_t, &value), NULL); test_assert(ptr != NULL); test_str(value, "Hello World"); @@ -269,37 +425,37 @@ void DeserializeFromExpr_string(void) { ecs_fini(world); } -void DeserializeFromExpr_entity(void) { +void Deserialize_entity(void) { ecs_world_t *world = ecs_init(); ecs_entity_t value = 0; - const char *ptr = ecs_parse_expr(world, "flecs.core", &ecs_value(ecs_entity_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "flecs.core", &ecs_value_ptr(ecs_entity_t, &value), NULL); test_assert(ptr != NULL); test_uint(value, EcsFlecsCore); - ptr = ecs_parse_expr(world, "0", &ecs_value(ecs_entity_t, &value), NULL); + ptr = ecs_script_expr_run(world, "0", &ecs_value_ptr(ecs_entity_t, &value), NULL); test_assert(ptr != NULL); test_uint(value, 0); ecs_fini(world); } -void DeserializeFromExpr_id(void) { +void Deserialize_id(void) { ecs_world_t *world = ecs_init(); ecs_id_t value = 0; - const char *ptr = ecs_parse_expr(world, "flecs.core", &ecs_value(ecs_id_t, &value), NULL); + const char *ptr = ecs_script_expr_run(world, "flecs.core", &ecs_value_ptr(ecs_id_t, &value), NULL); test_assert(ptr != NULL); test_uint(value, EcsFlecsCore); - ptr = ecs_parse_expr(world, "0", &ecs_value(ecs_id_t, &value), NULL); + ptr = ecs_script_expr_run(world, "0", &ecs_value_ptr(ecs_id_t, &value), NULL); test_assert(ptr != NULL); test_uint(value, 0); ecs_fini(world); } -void DeserializeFromExpr_enum(void) { +void Deserialize_enum(void) { typedef enum { Red, Blue, Green } T; @@ -316,21 +472,21 @@ void DeserializeFromExpr_enum(void) { { T value = 0; - const char *ptr = ecs_parse_expr(world, "Red", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "Red", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_int(value, Red); } { T value = 0; - const char *ptr = ecs_parse_expr(world, "Blue", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "Blue", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_int(value, Blue); } { T value = 0; - const char *ptr = ecs_parse_expr(world, "Green", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "Green", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_int(value, Green); } @@ -338,14 +494,14 @@ void DeserializeFromExpr_enum(void) { { ecs_log_set_level(-4); T value = 0; - const char *ptr = ecs_parse_expr(world, "Black", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "Black", &(ecs_value_t){t, &value}, NULL); test_assert(ptr == NULL); } ecs_fini(world); } -void DeserializeFromExpr_bitmask(void) { +void Deserialize_bitmask(void) { uint32_t Lettuce = 0x1; uint32_t Bacon = 0x1 << 1; uint32_t Tomato = 0x1 << 2; @@ -361,42 +517,42 @@ void DeserializeFromExpr_bitmask(void) { { uint32_t value = 0; - const char *ptr = ecs_parse_expr(world, "Lettuce", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "Lettuce", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value, Lettuce); } { uint32_t value = 0; - const char *ptr = ecs_parse_expr(world, "Lettuce|Bacon", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "Lettuce|Bacon", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value, Lettuce|Bacon); } { uint32_t value = 0; - const char *ptr = ecs_parse_expr(world, "Lettuce|Bacon|Tomato|Cheese", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "Lettuce|Bacon|Tomato|Cheese", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value, Lettuce|Bacon|Tomato|Cheese); } { uint32_t value = 0; - const char *ptr = ecs_parse_expr(world, "Lettuce | Bacon | Tomato | Cheese", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "Lettuce | Bacon | Tomato | Cheese", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value, Lettuce|Bacon|Tomato|Cheese); } { uint32_t value = 0; - const char *ptr = ecs_parse_expr(world, "BLT", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "BLT", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value, Lettuce|Bacon|Tomato); } { uint32_t value = 0; - const char *ptr = ecs_parse_expr(world, "0", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "0", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value, 0); } @@ -404,14 +560,14 @@ void DeserializeFromExpr_bitmask(void) { { ecs_log_set_level(-4); uint32_t value = 0; - const char *ptr = ecs_parse_expr(world, "Foo", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "Foo", &(ecs_value_t){t, &value}, NULL); test_assert(ptr == NULL); } ecs_fini(world); } -void DeserializeFromExpr_struct_enum(void) { +void Deserialize_struct_enum(void) { typedef enum { Red, Blue, Green } E; @@ -441,21 +597,21 @@ void DeserializeFromExpr_struct_enum(void) { { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v: Red}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v: Red}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_int(value.v, Red); } { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v: Blue}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v: Blue}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_int(value.v, Blue); } { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v: Green}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v: Green}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_int(value.v, Green); } @@ -463,14 +619,14 @@ void DeserializeFromExpr_struct_enum(void) { { ecs_log_set_level(-4); T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v: Black}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v: Black}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr == NULL); } ecs_fini(world); } -void DeserializeFromExpr_struct_bitmask(void) { +void Deserialize_struct_bitmask(void) { uint32_t Lettuce = 0x1; uint32_t Bacon = 0x1 << 1; uint32_t Tomato = 0x1 << 2; @@ -501,35 +657,35 @@ void DeserializeFromExpr_struct_bitmask(void) { { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v:Lettuce}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v:Lettuce}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value.v, Lettuce); } { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v:Lettuce|Bacon}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v:Lettuce|Bacon}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value.v, Lettuce|Bacon); } { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v:Lettuce|Bacon|Tomato|Cheese}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v:Lettuce|Bacon|Tomato|Cheese}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value.v, Lettuce|Bacon|Tomato|Cheese); } { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v:BLT}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v:BLT}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value.v, Lettuce|Bacon|Tomato); } { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v:0}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v:0}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_uint(value.v, 0); } @@ -537,14 +693,14 @@ void DeserializeFromExpr_struct_bitmask(void) { { ecs_log_set_level(-4); T value = {0}; - const char *ptr = ecs_parse_expr(world, "{v:Foo\"}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{v:Foo\"}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr == NULL); } ecs_fini(world); } -void DeserializeFromExpr_struct_i32(void) { +void Deserialize_struct_i32(void) { typedef struct { int32_t x; } T; @@ -560,7 +716,7 @@ void DeserializeFromExpr_struct_i32(void) { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{10}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{10}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -569,7 +725,7 @@ void DeserializeFromExpr_struct_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_i32_neg(void) { +void Deserialize_struct_i32_neg(void) { typedef struct { int32_t x; } T; @@ -585,7 +741,7 @@ void DeserializeFromExpr_struct_i32_neg(void) { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{-10}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{-10}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -594,7 +750,7 @@ void DeserializeFromExpr_struct_i32_neg(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_i32_i32(void) { +void Deserialize_struct_i32_i32(void) { typedef struct { int32_t x; int32_t y; @@ -612,7 +768,7 @@ void DeserializeFromExpr_struct_i32_i32(void) { T value = {0, 0}; - const char *ptr = ecs_parse_expr(world, "{10, 20}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{10, 20}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -622,7 +778,7 @@ void DeserializeFromExpr_struct_i32_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_entity(void) { +void Deserialize_struct_entity(void) { typedef struct { ecs_entity_t entity; } T; @@ -638,7 +794,7 @@ void DeserializeFromExpr_struct_entity(void) { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{flecs.core}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{flecs.core}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -647,7 +803,7 @@ void DeserializeFromExpr_struct_entity(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_id(void) { +void Deserialize_struct_id(void) { typedef struct { ecs_id_t entity; } T; @@ -663,7 +819,7 @@ void DeserializeFromExpr_struct_id(void) { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{flecs.core}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{flecs.core}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -672,7 +828,7 @@ void DeserializeFromExpr_struct_id(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_nested_i32(void) { +void Deserialize_struct_nested_i32(void) { typedef struct { ecs_i32_t x; } N1; @@ -699,7 +855,7 @@ void DeserializeFromExpr_struct_nested_i32(void) { T value = {{0}}; - const char *ptr = ecs_parse_expr(world, "{{10}}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{{10}}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -708,7 +864,7 @@ void DeserializeFromExpr_struct_nested_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_nested_i32_i32(void) { +void Deserialize_struct_nested_i32_i32(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -737,7 +893,7 @@ void DeserializeFromExpr_struct_nested_i32_i32(void) { T value = {{0}}; - const char *ptr = ecs_parse_expr(world, "{{10, 20}}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{{10, 20}}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -747,7 +903,7 @@ void DeserializeFromExpr_struct_nested_i32_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_2_nested_i32_i32(void) { +void Deserialize_struct_2_nested_i32_i32(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -778,7 +934,7 @@ void DeserializeFromExpr_struct_2_nested_i32_i32(void) { T value = {{0}}; - const char *ptr = ecs_parse_expr(world, "{{10, 20}, {30, 40}}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{{10, 20}, {30, 40}}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -790,7 +946,7 @@ void DeserializeFromExpr_struct_2_nested_i32_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_member_i32(void) { +void Deserialize_struct_member_i32(void) { typedef struct { int32_t x; } T; @@ -806,7 +962,7 @@ void DeserializeFromExpr_struct_member_i32(void) { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{x: 10}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{x: 10}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -815,7 +971,7 @@ void DeserializeFromExpr_struct_member_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_member_i32_neg(void) { +void Deserialize_struct_member_i32_neg(void) { typedef struct { int32_t x; } T; @@ -831,7 +987,7 @@ void DeserializeFromExpr_struct_member_i32_neg(void) { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{x: -10}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{x: -10}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -840,7 +996,7 @@ void DeserializeFromExpr_struct_member_i32_neg(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_member_i32_i32(void) { +void Deserialize_struct_member_i32_i32(void) { typedef struct { int32_t x; int32_t y; @@ -858,7 +1014,7 @@ void DeserializeFromExpr_struct_member_i32_i32(void) { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{x: 10, y: 20}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{x: 10, y: 20}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -868,7 +1024,7 @@ void DeserializeFromExpr_struct_member_i32_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_member_nested_i32(void) { +void Deserialize_struct_member_nested_i32(void) { typedef struct { ecs_i32_t x; } N1; @@ -895,7 +1051,7 @@ void DeserializeFromExpr_struct_member_nested_i32(void) { T value = {{0}}; - const char *ptr = ecs_parse_expr(world, "{n_1: {x: 10}}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{n_1: {x: 10}}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -904,7 +1060,7 @@ void DeserializeFromExpr_struct_member_nested_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_member_nested_i32_i32(void) { +void Deserialize_struct_member_nested_i32_i32(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -933,7 +1089,7 @@ void DeserializeFromExpr_struct_member_nested_i32_i32(void) { T value = {{0}}; - const char *ptr = ecs_parse_expr(world, "{n_1: {x: 10, y: 20}}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{n_1: {x: 10, y: 20}}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -943,7 +1099,7 @@ void DeserializeFromExpr_struct_member_nested_i32_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_member_2_nested_i32_i32(void) { +void Deserialize_struct_member_2_nested_i32_i32(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -980,7 +1136,7 @@ void DeserializeFromExpr_struct_member_2_nested_i32_i32(void) { LINE " n_2: {x: 30, y: 40}" LINE "}"; - const char *ptr = ecs_parse_expr(world, expr, &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, expr, &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -992,7 +1148,7 @@ void DeserializeFromExpr_struct_member_2_nested_i32_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_member_2_nested_i32_i32_reverse(void) { +void Deserialize_struct_member_2_nested_i32_i32_reverse(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -1029,7 +1185,7 @@ void DeserializeFromExpr_struct_member_2_nested_i32_i32_reverse(void) { LINE " n_1: {x: 10, y: 20}" LINE "}"; - const char *ptr = ecs_parse_expr(world, expr, &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, expr, &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1041,7 +1197,7 @@ void DeserializeFromExpr_struct_member_2_nested_i32_i32_reverse(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_i32_array_3(void) { +void Deserialize_struct_i32_array_3(void) { typedef struct { int32_t x[3]; } T; @@ -1057,7 +1213,7 @@ void DeserializeFromExpr_struct_i32_array_3(void) { T value = {0}; - const char *ptr = ecs_parse_expr(world, "{x: [10, 20, 30]}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{x: [10, 20, 30]}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1068,7 +1224,7 @@ void DeserializeFromExpr_struct_i32_array_3(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_struct_i32_array_3(void) { +void Deserialize_struct_struct_i32_array_3(void) { typedef struct { ecs_i32_t x; } N1; @@ -1095,7 +1251,7 @@ void DeserializeFromExpr_struct_struct_i32_array_3(void) { T value = {{{0}}}; - const char *ptr = ecs_parse_expr(world, "{n_1: [{x: 10}, {x: 20}, {x: 30}]}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{n_1: [{x: 10}, {x: 20}, {x: 30}]}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1106,7 +1262,7 @@ void DeserializeFromExpr_struct_struct_i32_array_3(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_struct_i32_i32_array_3(void) { +void Deserialize_struct_struct_i32_i32_array_3(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -1135,7 +1291,7 @@ void DeserializeFromExpr_struct_struct_i32_i32_array_3(void) { T value = {{{0}}}; - const char *ptr = ecs_parse_expr(world, "{n_1: [{x: 10, y: 20}, {x: 30, y: 40}, {x: 50, y: 60}]}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{n_1: [{x: 10, y: 20}, {x: 30, y: 40}, {x: 50, y: 60}]}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1149,7 +1305,7 @@ void DeserializeFromExpr_struct_struct_i32_i32_array_3(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_w_array_type_i32_i32(void) { +void Deserialize_struct_w_array_type_i32_i32(void) { typedef int32_t N1[2]; typedef struct { @@ -1173,7 +1329,7 @@ void DeserializeFromExpr_struct_w_array_type_i32_i32(void) { T value = {{ 0 }}; - const char *ptr = ecs_parse_expr(world, "{n_1: [10, 20]}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{n_1: [10, 20]}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1183,7 +1339,7 @@ void DeserializeFromExpr_struct_w_array_type_i32_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_w_2_array_type_i32_i32(void) { +void Deserialize_struct_w_2_array_type_i32_i32(void) { typedef int32_t N1[2]; typedef struct { @@ -1209,7 +1365,7 @@ void DeserializeFromExpr_struct_w_2_array_type_i32_i32(void) { T value = {{ 0 }}; - const char *ptr = ecs_parse_expr(world, "{n_1: [10, 20], n_2: [30, 40]}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{n_1: [10, 20], n_2: [30, 40]}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1221,7 +1377,7 @@ void DeserializeFromExpr_struct_w_2_array_type_i32_i32(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_w_array_type_struct(void) { +void Deserialize_struct_w_array_type_struct(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -1264,7 +1420,7 @@ void DeserializeFromExpr_struct_w_array_type_struct(void) { T value; - const char *ptr = ecs_parse_expr(world, "{n_1: [{x: 10, y: 20}, {x: 30, y: 40}]}", &(ecs_value_t){t, &value}, NULL); + const char *ptr = ecs_script_expr_run(world, "{n_1: [{x: 10, y: 20}, {x: 30, y: 40}]}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); test_assert(ptr[0] == '\0'); @@ -1276,7 +1432,7 @@ void DeserializeFromExpr_struct_w_array_type_struct(void) { ecs_fini(world); } -void DeserializeFromExpr_struct_w_2_array_type_struct(void) { +void Deserialize_struct_w_2_array_type_struct(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -1321,7 +1477,7 @@ void DeserializeFromExpr_struct_w_2_array_type_struct(void) { T value; - const char *ptr = ecs_parse_expr(world, + const char *ptr = ecs_script_expr_run(world, "{n_1: [{x: 10, y: 20}, {x: 30, y: 40}], n_2: [{x: 50, y: 60}, {x: 70, y: 80}]}", &(ecs_value_t){t, &value}, NULL); test_assert(ptr != NULL); @@ -1340,11 +1496,11 @@ void DeserializeFromExpr_struct_w_2_array_type_struct(void) { ecs_fini(world); } -void DeserializeFromExpr_discover_type_int(void) { +void Deserialize_discover_type_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10", &v, NULL) != NULL); test_uint(v.type, ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_int(*(uint64_t*)v.ptr, 10); @@ -1353,11 +1509,11 @@ void DeserializeFromExpr_discover_type_int(void) { ecs_fini(world); } -void DeserializeFromExpr_discover_type_negative_int(void) { +void Deserialize_discover_type_negative_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "-10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-10", &v, NULL) != NULL); test_uint(v.type, ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(int64_t*)v.ptr, -10); @@ -1366,11 +1522,11 @@ void DeserializeFromExpr_discover_type_negative_int(void) { ecs_fini(world); } -void DeserializeFromExpr_discover_type_float(void) { +void Deserialize_discover_type_float(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5", &v, NULL) != NULL); test_uint(v.type, ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 10.5); @@ -1379,11 +1535,11 @@ void DeserializeFromExpr_discover_type_float(void) { ecs_fini(world); } -void DeserializeFromExpr_discover_type_negative_float(void) { +void Deserialize_discover_type_negative_float(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "-10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-10.5", &v, NULL) != NULL); test_uint(v.type, ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, -10.5); @@ -1392,11 +1548,11 @@ void DeserializeFromExpr_discover_type_negative_float(void) { ecs_fini(world); } -void DeserializeFromExpr_discover_type_string(void) { +void Deserialize_discover_type_string(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "\"foo\"", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "\"foo\"", &v, NULL) != NULL); test_uint(v.type, ecs_id(ecs_string_t)); test_assert(v.ptr != NULL); test_str(*(ecs_string_t*)v.ptr, "foo"); @@ -1405,11 +1561,11 @@ void DeserializeFromExpr_discover_type_string(void) { ecs_fini(world); } -void DeserializeFromExpr_discover_type_multiline_string(void) { +void Deserialize_discover_type_multiline_string(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "`foo\nbar`", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "`foo\nbar`", &v, NULL) != NULL); test_uint(v.type, ecs_id(ecs_string_t)); test_assert(v.ptr != NULL); test_str(*(ecs_string_t*)v.ptr, "foo\nbar"); @@ -1418,13 +1574,13 @@ void DeserializeFromExpr_discover_type_multiline_string(void) { ecs_fini(world); } -void DeserializeFromExpr_discover_type_entity(void) { +void Deserialize_discover_type_entity(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t foo = ecs_new_entity(world, "foo"); + ecs_entity_t foo = ecs_entity(world, { .name = "foo" }); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "foo", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "foo", &v, NULL) != NULL); test_uint(v.type, ecs_id(ecs_entity_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_entity_t*)v.ptr, foo); @@ -1433,18 +1589,18 @@ void DeserializeFromExpr_discover_type_entity(void) { ecs_fini(world); } -void DeserializeFromExpr_discover_type_bool(void) { +void Deserialize_discover_type_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true", &v, NULL) != NULL); test_uint(v.type, ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(ecs_bool_t*)v.ptr, true); ecs_value_free(world, v.type, v.ptr); ecs_os_zeromem(&v); - test_assert(ecs_parse_expr(world, "false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false", &v, NULL) != NULL); test_uint(v.type, ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(ecs_bool_t*)v.ptr, false); @@ -1453,25 +1609,177 @@ void DeserializeFromExpr_discover_type_bool(void) { ecs_fini(world); } -void DeserializeFromExpr_discover_type_unknown(void) { +void Deserialize_discover_type_unknown(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; ecs_log_set_level(-4); - test_assert(ecs_parse_expr(world, "{10}", &v, NULL) == NULL); - test_assert(ecs_parse_expr(world, "[10]", &v, NULL) == NULL); + test_assert(ecs_script_expr_run(world, "{10}", &v, NULL) == NULL); + test_assert(ecs_script_expr_run(world, "[10]", &v, NULL) == NULL); ecs_fini(world); } -void DeserializeFromExpr_discover_type_invalid(void) { +void Deserialize_discover_type_invalid(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; ecs_log_set_level(-4); - test_assert(ecs_parse_expr(world, "-", &v, NULL) == NULL); + test_assert(ecs_script_expr_run(world, "-", &v, NULL) == NULL); + + ecs_fini(world); +} + +typedef struct { + int32_t _dummy_1; + int32_t x; + int32_t _dummy_2; + int32_t y; +} OpaqueStruct; + +static void* OpaqueStruct_member(void *ptr, const char *member) { + OpaqueStruct *data = ptr; + if (!strcmp(member, "x")) { + return &data->x; + } else if (!strcmp(member, "y")) { + return &data->y; + } else { + return NULL; + } +} + +void Deserialize_opaque_struct(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, OpaqueStruct); + + ecs_entity_t s = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ecs_opaque(world, { + .entity = ecs_id(OpaqueStruct), + .type.as_type = s, + .type.ensure_member = OpaqueStruct_member + }); + + OpaqueStruct v; + + const char *ptr = ecs_script_expr_run(world, "{10, 20}", &ecs_value_ptr(OpaqueStruct, &v), NULL); + test_assert(ptr != NULL); + test_assert(ptr[0] == '\0'); + + test_int(v.x, 10); + test_int(v.y, 20); + + ecs_fini(world); +} + +void Deserialize_opaque_struct_w_member(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, OpaqueStruct); + + ecs_entity_t s = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ecs_opaque(world, { + .entity = ecs_id(OpaqueStruct), + .type.as_type = s, + .type.ensure_member = OpaqueStruct_member + }); + + OpaqueStruct v; + + const char *ptr = ecs_script_expr_run(world, "{x: 10, y: 20}", &ecs_value_ptr(OpaqueStruct, &v), NULL); + test_assert(ptr != NULL); + test_assert(ptr[0] == '\0'); + + test_int(v.x, 10); + test_int(v.y, 20); + + ecs_fini(world); +} + +void Deserialize_opaque_struct_w_member_reverse(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, OpaqueStruct); + + ecs_entity_t s = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)}, + } + }); + + ecs_opaque(world, { + .entity = ecs_id(OpaqueStruct), + .type.as_type = s, + .type.ensure_member = OpaqueStruct_member + }); + + OpaqueStruct v; + + const char *ptr = ecs_script_expr_run(world, "{y: 10, x: 20}", &ecs_value_ptr(OpaqueStruct, &v), NULL); + test_assert(ptr != NULL); + test_assert(ptr[0] == '\0'); + + test_int(v.x, 20); + test_int(v.y, 10); + + ecs_fini(world); +} + +typedef struct Opaque_string { + int32_t len; + char *value; +} Opaque_string; + +typedef struct Struct_w_opaque { + Opaque_string str; +} Struct_w_opaque; + +static void Opaque_string_set(void *ptr, const char *value) { + ((Opaque_string*)ptr)->value = ecs_os_strdup(value); +} + +void Deserialize_struct_w_opaque_member(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Opaque_string); + ECS_COMPONENT(world, Struct_w_opaque); + + ecs_opaque(world, { + .entity = ecs_id(Opaque_string), + .type.as_type = ecs_id(ecs_string_t), + .type.assign_string = Opaque_string_set + }); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(Struct_w_opaque), + .members = { + {"str", ecs_id(Opaque_string)}, + } + }); + + Struct_w_opaque v = {{0, NULL}}; + + const char *ptr = ecs_script_expr_run(world, "{str: \"foobar\"}", &ecs_value_ptr(Struct_w_opaque, &v), NULL); + test_assert(ptr != NULL); + test_assert(ptr[0] == '\0'); + + test_str(v.str.value, "foobar"); + ecs_os_free(v.str.value); ecs_fini(world); } diff --git a/vendors/flecs/test/script/src/Error.c b/vendors/flecs/test/script/src/Error.c new file mode 100644 index 000000000..5f2f1b054 --- /dev/null +++ b/vendors/flecs/test/script/src/Error.c @@ -0,0 +1,1233 @@ +#include + +void Error_multi_line_comment_after_newline_before_newline_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent" + LINE "/* Some Comment() */" + LINE "" + LINE "{" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + ecs_log_set_level(-4); /* Newline after multiline comment is not ignored */ + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_missing_end_of_scope(void) { + ecs_log_set_level(-4); + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent {" + LINE " Child {}"; + + test_assert(ecs_script_run(world, NULL, expr) != 0); + + test_assert(ecs_get_scope(world) == 0); + test_assert(ecs_get_with(world) == 0); + + ecs_fini(world); +} + +void Error_with_n_tags_2_levels_invalid_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagD); + ECS_TAG(world, TagE); + + const char *expr = + HEAD "with TagA, TagB {" + LINE " with TagC, TagD, TagE {" + LINE " Foo {}" + LINE " }" + LINE " HelloA" + LINE " with TagF, TagG, TagH {" + LINE " Bar {}" + LINE " }" + LINE " HelloB {}" + LINE "}" + LINE "HelloC {}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_assignment_to_non_component(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo { Position: {x: 10, y: 20} }"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_struct_w_member_w_assignment_to_nothing(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "flecs.meta.struct Position {" + LINE " x { flecs.meta.Member: }" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_struct_w_member_w_assignment_to_empty_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "flecs.meta.struct Position {" + LINE " x { flecs.meta.member: { }" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_invalid_nested_assignment(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {" + LINE "Bar { Hello }"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_invalid_partial_pair_assignment(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo { (Hello, }"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_empty_assignment(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_empty_assignment_before_end_of_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "{Foo {}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_default_type_with_tag(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "with Foo {" + LINE " e1 = 10, 20" + LINE " e2 = 30, 40" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_invalid_oneof(void) { + ecs_log_set_level(-4); + + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "flecs.meta.enum Color {" + LINE " flecs.meta.constant Red" + LINE " flecs.meta.constant Green" + LINE " flecs.meta.constant Blue" + LINE "}" + LINE "e { (Color, Foo) }"; + + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_entity_t color = ecs_lookup(world, "Color"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t e = ecs_lookup(world, "e"); + + test_assert(color != 0); + test_assert(foo == 0); + test_assert(e != 0); + + test_assert( !ecs_has_pair(world, e, color, EcsWildcard)); + + ecs_fini(world); +} + +void Error_unterminated_multiline_string(void) { + ecs_world_t *world = ecs_init(); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "String"}), + .members = { + {"value", ecs_id(ecs_string_t)} + } + }); + + const char *expr = + HEAD "Foo { String: {value: `start" + LINE "Hello World" + LINE "Foo Bar" + LINE "Special characters }{\"\"''," + LINE "}}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_invalid_assign_multiline_string(void) { + ecs_world_t *world = ecs_init(); + + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "String"}), + .members = { + {"value", ecs_id(ecs_bool_t)} + } + }); + + const char *expr = + HEAD "Foo { String: {value: `foo`} }"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_const_var_redeclare(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "const var_x = 10" + LINE "const var_x = 20"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_typed_const_w_composite_type_invalid_assignment(void) { + ecs_world_t *world = ecs_init(); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const var_pos = Position: Position{10, 20}" + LINE "a { $var_pos }" + LINE ""; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_unterminated_multi_line_comment_in_value(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "e {Position: {\n" + LINE " x: 10\n" + LINE " /*\n" + LINE " * foo\n" + LINE " y: 20\n" + LINE "}}"; + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_pair_w_rel_var_invalid_type(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + const char *expr = + LINE "const rel = 10\n" + LINE "ent {\n" + LINE " ($rel, Tgt)\n" + LINE "}\n" + LINE "\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_pair_w_tgt_var_invalid_type(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + const char *expr = + LINE "const tgt = 10\n" + LINE "ent {\n" + LINE " (Rel, $tgt)\n" + LINE "}\n" + LINE "\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_with_value_not_a_component(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "with Foo(10) {\n" + LINE " Bar {}\n" + LINE "}\n" + LINE "\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_component_in_with_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "with Position(10, 20) {\n" + LINE " Bar\n" + LINE "}\n" + LINE "\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_component_in_with_scope_nested(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "with Position(10, 20) {\n" + LINE " with Velocity(10, 20) {\n" + LINE " Bar\n" + LINE " }\n" + LINE "}\n" + LINE "\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_assign_after_with_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "foo {\n" + LINE " Position: {10, 20}\n" + LINE " with Position(30, 40) {\n" + LINE " }\n" + LINE " Velocity: {1, 2}\n" + LINE "}\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + test_assert(ecs_has(world, foo, Velocity)); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, foo, Velocity); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Error_not_an_array_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "foo { Position: [10, 20] }\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_array_component_w_curly_brackets(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_array(world, { + .entity = ecs_id(Position), + .type = ecs_id(ecs_f32_t), + .count = 2 + }); + + const char *expr = + LINE "foo { Position: {10, 20} }\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_unknown_identifier(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Comp {" + LINE " value = entity" + LINE "}" + LINE + LINE "Foo { Comp: {A} }"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_unknown_identifier_for_int_field(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Comp {" + LINE " value = i32" + LINE "}" + LINE + LINE "Foo { Comp: {A} }"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_prefab_w_slot_no_parent(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "slot Base {}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_tag_not_found(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {" + LINE " Bar" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_component_not_found(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {" + LINE " Position: {10, 20}" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_pair_first_not_found(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tgt); + + const char *expr = + HEAD "Foo {" + LINE " (Rel, Tgt)" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_pair_second_not_found(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + + const char *expr = + HEAD "Foo {" + LINE " (Rel, Tgt)" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_kind_not_found(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + + const char *expr = + HEAD "Foo Bar"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_base_not_found(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + + const char *expr = + HEAD "Foo : Bar"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_entity_w_anonymous_tag(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "MyEntity {" + LINE " _" + LINE "}"; + + ecs_log_set_level(-4); + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s == 0); + + ecs_fini(world); +} + +void Error_member_expr_without_value_end_of_scope(void) { + test_quarantine("Mon Aug 5"); // address when porting expressions over to new parser + + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + test_assert(ecs_script_run(world, NULL, "Position(x:)") != 0); + + ecs_fini(world); +} + +void Error_member_expr_without_value_comma(void) { + test_quarantine("Mon Aug 5"); // address when porting expressions over to new parser + + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + test_assert(ecs_script_run(world, NULL, "Position(x:,0)") != 0); + + ecs_fini(world); +} + +void Error_member_expr_without_value_newline(void) { + test_quarantine("Mon Aug 5"); // address when porting expressions over to new parser + + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + test_assert(ecs_script_run(world, NULL, "Position(x:\n)") != 0); + + ecs_fini(world); +} + +void Error_2_member_expr_without_value(void) { + test_quarantine("Mon Aug 5"); // address when porting expressions over to new parser + + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + test_assert(ecs_script_run(world, NULL, "Position(x:y:)") != 0); + + ecs_fini(world); +} + +void Error_expr_junk_after_number(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, "Position(0abc)") != 0); + + ecs_fini(world); +} + +void Error_expr_junk_after_unary_minus(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, "Position(-abc)") != 0); + + ecs_fini(world); +} + +void Error_expr_comma_after_nothing(void) { + test_quarantine("Mon Aug 5"); // address when porting expressions over to new parser + + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + test_assert(ecs_script_run(world, NULL, "Position(,)") != 0); + + ecs_fini(world); +} + +void Error_expr_digit_with_two_dots(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, "Position(10.10.10)") != 0); + + ecs_fini(world); +} + +void Error_template_empty(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Tree {" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_fini(world); +} + +void Error_template_unresolved_tag(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Tree {" + LINE " Foo" + LINE "}" + LINE "Tree ent()"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_unresolved_component(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using flecs.meta" + LINE "template Tree {" + LINE " struct Position {" + LINE " x = f32" + LINE " }" + LINE "" + LINE " Position: {10, 20}" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_unresolved_pair_relationship(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Bar {}" + LINE "template Tree {" + LINE " (Foo, Bar)" + LINE "}" + LINE "Tree ent()"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_unresolved_pair_target(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {}" + LINE "template Tree {" + LINE " (Foo, Bar)" + LINE "}" + LINE "Tree ent()"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_unresolved_with_tag(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Tree {" + LINE " with Foo {" + LINE " }" + LINE "}" + LINE "Tree ent()"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_unresolved_with_component(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using flecs.meta" + LINE "template Tree {" + LINE " struct Position {" + LINE " x = f32" + LINE " }" + LINE "" + LINE " with Position(10, 20) {" + LINE " }" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_unresolved_with_pair_relationship(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Bar {}" + LINE "template Tree {" + LINE " with (Foo, Bar) {" + LINE " }" + LINE "}" + LINE "Tree ent()"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_unresolved_with_pair_target(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {}" + LINE "template Tree {" + LINE " with (Foo, Bar) {" + LINE " }" + LINE "}" + LINE "Tree ent()"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_unresolved_tag_in_child(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "template Tree {" + LINE " child {" + LINE " Foo" + LINE " }" + LINE "}" + LINE "Tree ent()"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_prop_no_type(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop height" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_prop_no_default(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop height: flecs.meta.f32" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_w_composite_prop_invalid_assignment(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop pos = Position: Position{10, 20}" + LINE " child { $pos }" + LINE "}" + LINE "" + LINE "t { Tree: {pos: {20, 30}} }"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_redeclare_prop_as_const(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Foo {\n" + LINE " prop x = flecs.meta.f32: 10\n" + LINE " prop y = flecs.meta.f32: 10\n" + LINE " const y = flecs.meta.f32: 20\n" + LINE "}" + LINE "ent { Foo: {} }\n" + LINE "\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_redeclare_prop_as_prop(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Foo {\n" + LINE " prop x = flecs.meta.f32: 10\n" + LINE " prop y = flecs.meta.f32: 10\n" + LINE " prop y = flecs.meta.f32: 20\n" + LINE "}" + LINE "ent { Foo: {} }\n" + LINE "\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_template_redeclare_const_as_const(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Foo {\n" + LINE " prop x = flecs.meta.f32: 10\n" + LINE " const y = flecs.meta.f32: 10\n" + LINE " const y = flecs.meta.f32: 20\n" + LINE "}" + LINE "ent { Foo: {} }\n" + LINE "\n"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} + +void Error_run_template_after_error(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {}" + LINE "template Tree {" + LINE " Foo" + LINE "}"; + + const char *expr_err = + HEAD "Foo {}" + LINE "template Tree {" + LINE " Fo" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + { + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + } + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr_err) != 0); + ecs_log_set_level(-1); + + /* Because script is not managed, entiites created by script aren't deleted */ + + { + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + } + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + { + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + } + + ecs_fini(world); +} + +void Error_update_template_after_error(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {}" + LINE "template Tree {" + LINE " Foo" + LINE "}"; + + const char *expr_err = + HEAD "Foo {}" + LINE "template Tree {" + LINE " Fo" + LINE "}"; + + ecs_entity_t s = ecs_script(world, { .code = expr }); + test_assert(s != 0); + + { + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + } + + ecs_log_set_level(-4); + test_assert(ecs_script_update(world, s, 0, expr_err) != 0); + ecs_log_set_level(-1); + + { + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo == 0); + } + + test_assert(ecs_script_update(world, s, 0, expr) == 0); + + { + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + } + + ecs_fini(world); +} + +void Error_template_in_template(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "template Tree {" + LINE " template Bark {" + LINE " }" + LINE "}"; + + ecs_log_set_level(-4); + test_assert(ecs_script_run(world, NULL, expr) != 0); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/script/src/Eval.c b/vendors/flecs/test/script/src/Eval.c new file mode 100644 index 000000000..6e149ee85 --- /dev/null +++ b/vendors/flecs/test/script/src/Eval.c @@ -0,0 +1,8277 @@ +#include + +void Eval_null(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, NULL) == 0); + + ecs_fini(world); +} + +void Eval_empty(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "") == 0); + + ecs_fini(world); +} + +void Eval_space(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, " ") == 0); + + ecs_fini(world); +} + +void Eval_space_newline(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, " \n \n") == 0); + + ecs_fini(world); +} + +void Eval_two_empty_newlines(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "\n\n") == 0); + + ecs_fini(world); +} + +void Eval_three_empty_newlines(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "\n\n\n") == 0); + + ecs_fini(world); +} + +void Eval_newline_trailing_space(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "\n ") == 0); + + ecs_fini(world); +} + +void Eval_newline_trailing_spaces(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "\n ") == 0); + + ecs_fini(world); +} + +void Eval_multiple_trailing_newlines(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "Foo{}\n\n\n") == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + test_str(ecs_get_name(world, foo), "Foo"); + test_int(ecs_get_type(world, foo)->count, 1); + + ecs_fini(world); +} + +void Eval_entity(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "Foo{}") == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + test_str(ecs_get_name(world, foo), "Foo"); + test_int(ecs_get_type(world, foo)->count, 1); + + ecs_fini(world); +} + +void Eval_entity_w_core_name(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "World{}") == 0); + + ecs_entity_t foo = ecs_lookup_child(world, 0, "World"); + test_assert(foo != 0); + test_assert(foo != EcsWorld); + test_str(ecs_get_name(world, foo), "World"); + test_int(ecs_get_type(world, foo)->count, 1); + + ecs_fini(world); +} + +void Eval_2_entities(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "Foo{}\nBar{}\n") == 0); + + ecs_entity_t e = ecs_lookup(world, "Foo"); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "Foo"); + + ecs_fini(world); +} + +void Eval_line_comment(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "// Foo{}\n") == 0); + + test_assert(ecs_lookup(world, "Foo") == 0); + + ecs_fini(world); +} + +void Eval_line_comment_before_stmt(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "// Hello\nFoo {}\n") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + + ecs_fini(world); +} + +void Eval_line_comment_after_stmt(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "Foo{}\n// Hello\n") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + + ecs_fini(world); +} + +void Eval_line_comment_between_stmt(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "Foo{}\n// Hello\nBar{}\n") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + test_assert(ecs_lookup(world, "Bar") != 0); + + ecs_fini(world); +} + +void Eval_multiple_line_comment(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "// Hello\n// World\nFoo{}") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup_child(world, 0, "World") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + + ecs_fini(world); +} + +void Eval_multiple_line_comment_w_newlines(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "// Hello\n\n// World\n\nFoo{}") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup_child(world, 0, "World") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + + ecs_fini(world); +} + +void Eval_line_comment_after_stmt_same_line(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "Foo{} // Hello\nBar{}\n") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + test_assert(ecs_lookup(world, "Bar") != 0); + + ecs_fini(world); +} + +void Eval_line_comment_before_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent // Some Comment()" + LINE "{" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + test_assert(ecs_lookup(world, "Some") == 0); + test_assert(ecs_lookup(world, "Comment") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_line_comment_after_newline_before_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent" + LINE "// Some Comment()" + LINE "{" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + test_assert(ecs_lookup(world, "Some") == 0); + test_assert(ecs_lookup(world, "Comment") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_line_comment_after_newline_before_newline_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent" + LINE "// Some Comment()" + LINE "" + LINE "{" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + test_assert(ecs_lookup(world, "Some") == 0); + test_assert(ecs_lookup(world, "Comment") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_multi_line_comment(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "/* Foo{} */") == 0); + + test_assert(ecs_lookup(world, "Foo") == 0); + + ecs_fini(world); +} + +void Eval_multi_line_comment_before_stmt(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "/* Hello */\nFoo {}") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + + ecs_fini(world); +} + +void Eval_multi_line_comment_after_stmt(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "Foo{}\n/* Hello */") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + + ecs_fini(world); +} + +void Eval_multi_line_comment_between_stmt(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "Foo{}\n /* Hello */ Bar{}\n") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + test_assert(ecs_lookup(world, "Bar") != 0); + + ecs_fini(world); +} + +void Eval_multiple_multi_line_comment(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "/* Hello *//* World*/ Foo{}") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup_child(world, 0, "World") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + + ecs_fini(world); +} + +void Eval_multiple_multi_line_comment_w_newlines(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "/* Hello */\n/* World */ Foo{}") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup_child(world, 0, "World") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + + ecs_fini(world); +} + +void Eval_multi_line_comment_after_stmt_same_line(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "Foo{} /* Hello */\nBar{}\n") == 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup(world, "Foo") != 0); + test_assert(ecs_lookup(world, "Bar") != 0); + + ecs_fini(world); +} + +void Eval_multi_line_comment_before_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent /* Some Comment() */" + LINE "{" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + test_assert(ecs_lookup(world, "Some") == 0); + test_assert(ecs_lookup(world, "Comment") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_multi_line_comment_after_newline_before_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent" + LINE "/* Some Comment() */" + LINE "{" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + test_assert(ecs_lookup(world, "Some") == 0); + test_assert(ecs_lookup(world, "Comment") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_multi_line_comment_multiple_lines(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {}" + LINE "/* Some{}" + LINE " * Multi Line{}" + LINE " * Comment{}" + LINE " */" + LINE "" + LINE "Bar {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Foo") != 0); + test_assert(ecs_lookup(world, "Bar") != 0); + + test_assert(ecs_lookup(world, "Some") == 0); + test_assert(ecs_lookup(world, "Multi") == 0); + test_assert(ecs_lookup(world, "Line") == 0); + test_assert(ecs_lookup(world, "Comment") == 0); + + ecs_fini(world); +} + +void Eval_hierarchy_1_child(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent {" + LINE " Child {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_hierarchy_2_children(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent {" + LINE " ChildA{}" + LINE " ChildB{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "ChildA") == 0); + test_assert(ecs_lookup(world, "ChildB") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child_a = ecs_lookup(world, "Parent.ChildA"); + ecs_entity_t child_b = ecs_lookup(world, "Parent.ChildB"); + + test_assert(parent != 0); + test_assert(child_a != 0); + test_assert(child_b != 0); + + test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent)); + test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_hierarchy_1_child_same_line(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent { Child{} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_hierarchy_2_children_same_line(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent { ChildA, ChildB, }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "ChildA") == 0); + test_assert(ecs_lookup(world, "ChildB") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child_a = ecs_lookup(world, "Parent.ChildA"); + ecs_entity_t child_b = ecs_lookup(world, "Parent.ChildB"); + + test_assert(parent != 0); + test_assert(child_a != 0); + test_assert(child_b != 0); + + test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent)); + test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_hierarchy_2_children_same_line_no_trailing_comma(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent { ChildA, ChildB }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "ChildA") == 0); + test_assert(ecs_lookup(world, "ChildB") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child_a = ecs_lookup(world, "Parent.ChildA"); + ecs_entity_t child_b = ecs_lookup(world, "Parent.ChildB"); + + test_assert(parent != 0); + test_assert(child_a != 0); + test_assert(child_b != 0); + + test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent)); + test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_entity_after_hierarchy(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent {" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(parent != 0); + test_assert(child != 0); + test_assert(foo != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + test_assert(!ecs_has_pair(world, foo, EcsChildOf, parent)); + test_assert(!ecs_has_pair(world, foo, EcsChildOf, EcsWildcard)); + + ecs_fini(world); +} + +void Eval_newline_before_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent" + LINE "{" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_newline_w_whitespace_before_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent" + LINE " {" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_2_newline_before_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent" + LINE "" + LINE "{" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_2_newline_w_whitespace_before_scope_open(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent" + LINE " " + LINE " {" + LINE " Child{}" + LINE "}" + LINE "Foo{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + + ecs_fini(world); +} + +void Eval_hierarchy_2_levels(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent {" + LINE " Child {" + LINE " GrandChild {}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + test_assert(ecs_lookup(world, "GrandChild") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + ecs_entity_t grandchild = ecs_lookup(world, "Parent.Child.GrandChild"); + + test_assert(parent != 0); + test_assert(child != 0); + test_assert(grandchild != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + test_assert(ecs_has_pair(world, grandchild, EcsChildOf, child)); + + ecs_fini(world); +} + +void Eval_hierarchy_2_levels_2_subtrees(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Parent {" + LINE " ChildA {" + LINE " GrandChild {}" + LINE " }" + LINE " ChildB {" + LINE " GrandChild {}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "ChildA") == 0); + test_assert(ecs_lookup(world, "ChildB") == 0); + test_assert(ecs_lookup(world, "GrandChild") == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child_a = ecs_lookup(world, "Parent.ChildA"); + ecs_entity_t child_b = ecs_lookup(world, "Parent.ChildB"); + ecs_entity_t grandchild_a = ecs_lookup(world, "Parent.ChildA.GrandChild"); + ecs_entity_t grandchild_b = ecs_lookup(world, "Parent.ChildB.GrandChild"); + + test_assert(parent != 0); + test_assert(child_a != 0); + test_assert(child_b != 0); + test_assert(child_a != child_b); + test_assert(grandchild_a != 0); + test_assert(grandchild_b != 0); + test_assert(grandchild_a != grandchild_b); + + test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent)); + test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent)); + test_assert(ecs_has_pair(world, grandchild_a, EcsChildOf, child_a)); + test_assert(ecs_has_pair(world, grandchild_b, EcsChildOf, child_b)); + + ecs_fini(world); +} + +void Eval_create_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Root); + + ecs_set_scope(world, Root); + + const char *expr = + HEAD "Parent { Child{} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_get_scope(world) == Root); + + test_assert(ecs_lookup(world, "Child") == 0); + + ecs_set_scope(world, 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + + test_assert(parent != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, child, EcsChildOf, parent)); + test_assert(!ecs_has_pair(world, parent, EcsChildOf, Root)); + + ecs_fini(world); +} + +void Eval_hierarchy_w_pred_subj(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + const char *expr = + HEAD "Foo Bar {" + LINE " Child{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Child") == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t child = ecs_lookup(world, "Bar.Child"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(child != 0); + + test_assert(ecs_has_id(world, bar, foo)); + test_assert(ecs_has_pair(world, child, EcsChildOf, bar)); + + ecs_fini(world); +} + +void Eval_hierarchy_custom_relation(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "(Foo, Bar) {" + LINE " Child{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t child = ecs_lookup(world, "Child"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(child != 0); + + test_assert(!ecs_has_id(world, bar, foo)); + test_assert(!ecs_has_id(world, foo, bar)); + test_assert(!ecs_has_pair(world, child, EcsChildOf, foo)); + test_assert(!ecs_has_pair(world, child, EcsChildOf, bar)); + test_assert(ecs_has_pair(world, child, foo, bar)); + + ecs_fini(world); +} + +void Eval_hierarchy_custom_relation_2_levels(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "(Foo, Bar) {" + LINE " (Rel, ObjA) {" + LINE " Child{}" + LINE " ChildA{}" + LINE " }" + LINE " (Rel, ObjB) {" + LINE " Child{}" + LINE " ChildB{}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t rel = ecs_lookup(world, "Rel"); + ecs_entity_t obj_a = ecs_lookup(world, "ObjA"); + ecs_entity_t obj_b = ecs_lookup(world, "ObjB"); + ecs_entity_t child = ecs_lookup(world, "Child"); + ecs_entity_t child_a = ecs_lookup(world, "ChildA"); + ecs_entity_t child_b = ecs_lookup(world, "ChildB"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(rel != 0); + test_assert(obj_a != 0); + test_assert(obj_b != 0); + test_assert(child != 0); + test_assert(child_a != 0); + test_assert(child_b != 0); + + test_assert(!ecs_has_id(world, bar, foo)); + test_assert(!ecs_has_id(world, foo, bar)); + + test_assert(!ecs_has_id(world, rel, obj_a)); + test_assert(!ecs_has_id(world, obj_a, rel)); + + test_assert(!ecs_has_id(world, rel, obj_b)); + test_assert(!ecs_has_id(world, obj_b, rel)); + + test_assert(!ecs_has_pair(world, child, EcsChildOf, foo)); + test_assert(!ecs_has_pair(world, child, EcsChildOf, bar)); + + test_assert(ecs_has_pair(world, child, rel, obj_a)); + test_assert(ecs_has_pair(world, child, rel, obj_b)); + + test_assert(ecs_has_pair(world, child_a, rel, obj_a)); + test_assert(ecs_has_pair(world, child_b, rel, obj_b)); + + ecs_fini(world); +} + +void Eval_entity_after_hierarchy_custom_relation(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "(Foo, Bar) {" + LINE " Child{}" + LINE "}" + LINE "Hello{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t child = ecs_lookup(world, "Child"); + ecs_entity_t hello = ecs_lookup(world, "Hello"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(child != 0); + test_assert(hello != 0); + + test_assert(!ecs_has_id(world, bar, foo)); + test_assert(!ecs_has_id(world, foo, bar)); + test_assert(!ecs_has_pair(world, child, EcsChildOf, foo)); + test_assert(!ecs_has_pair(world, child, EcsChildOf, bar)); + test_assert(ecs_has_pair(world, child, foo, bar)); + test_assert(!ecs_has_pair(world, hello, foo, bar)); + + ecs_fini(world); +} + +void Eval_entity_after_hierarchy_custom_relation_2_levels(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "(Foo, Bar) {" + LINE " (Rel, ObjA) {" + LINE " Child{}" + LINE " ChildA{}" + LINE " }" + LINE " TestA {}" + LINE " (Rel, ObjB) {" + LINE " Child{}" + LINE " ChildB{}" + LINE " }" + LINE "}" + LINE "TestB{}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t rel = ecs_lookup(world, "Rel"); + ecs_entity_t obj_a = ecs_lookup(world, "ObjA"); + ecs_entity_t obj_b = ecs_lookup(world, "ObjB"); + ecs_entity_t child = ecs_lookup(world, "Child"); + ecs_entity_t child_a = ecs_lookup(world, "ChildA"); + ecs_entity_t child_b = ecs_lookup(world, "ChildB"); + + ecs_entity_t test_a = ecs_lookup(world, "TestA"); + ecs_entity_t test_b = ecs_lookup(world, "TestB"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(rel != 0); + test_assert(obj_a != 0); + test_assert(obj_b != 0); + test_assert(child != 0); + test_assert(child_a != 0); + test_assert(child_b != 0); + + test_assert(test_a != 0); + test_assert(test_b != 0); + + test_assert(!ecs_has_id(world, bar, foo)); + test_assert(!ecs_has_id(world, foo, bar)); + + test_assert(!ecs_has_id(world, rel, obj_a)); + test_assert(!ecs_has_id(world, obj_a, rel)); + + test_assert(!ecs_has_id(world, rel, obj_b)); + test_assert(!ecs_has_id(world, obj_b, rel)); + + test_assert(!ecs_has_pair(world, child, EcsChildOf, foo)); + test_assert(!ecs_has_pair(world, child, EcsChildOf, bar)); + + test_assert(ecs_has_pair(world, child, rel, obj_a)); + test_assert(ecs_has_pair(world, child, rel, obj_b)); + + test_assert(ecs_has_pair(world, child_a, rel, obj_a)); + test_assert(ecs_has_pair(world, child_b, rel, obj_b)); + + test_assert(ecs_has_pair(world, test_a, foo, bar)); + test_assert(!ecs_has_pair(world, test_a, rel, EcsWildcard)); + + test_assert(!ecs_has_pair(world, test_b, foo, bar)); + test_assert(!ecs_has_pair(world, test_b, rel, EcsWildcard)); + + ecs_fini(world); +} + +void Eval_hierarchy_custom_relation_apply_to_object(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "(Rel, ObjA) {" + LINE " (Rel, ObjB) {" + LINE " ObjC{}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t rel = ecs_lookup(world, "Rel"); + ecs_entity_t obj_a = ecs_lookup(world, "ObjA"); + ecs_entity_t obj_b = ecs_lookup(world, "ObjB"); + ecs_entity_t obj_c = ecs_lookup(world, "ObjC"); + + test_assert(rel != 0); + test_assert(obj_a != 0); + test_assert(obj_b != 0); + test_assert(obj_c != 0); + + test_assert(ecs_has_pair(world, obj_b, rel, obj_a)); + test_assert(ecs_has_pair(world, obj_c, rel, obj_b)); + test_assert(!ecs_has_pair(world, obj_c, rel, obj_a)); + + ecs_fini(world); +} + +void Eval_hierarchy_custom_relation_apply_to_object_2_levels(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "(Rel, ObjA) {" + LINE " (Rel, ObjB) {" + LINE " (Rel, ObjC) {" + LINE " ObjD{}" + LINE " }" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t rel = ecs_lookup(world, "Rel"); + ecs_entity_t obj_a = ecs_lookup(world, "ObjA"); + ecs_entity_t obj_b = ecs_lookup(world, "ObjB"); + ecs_entity_t obj_c = ecs_lookup(world, "ObjC"); + ecs_entity_t obj_d = ecs_lookup(world, "ObjD"); + + test_assert(rel != 0); + test_assert(obj_a != 0); + test_assert(obj_b != 0); + test_assert(obj_c != 0); + test_assert(obj_d != 0); + + test_assert(ecs_has_pair(world, obj_b, rel, obj_a)); + test_assert(ecs_has_pair(world, obj_c, rel, obj_b)); + test_assert(!ecs_has_pair(world, obj_c, rel, obj_a)); + test_assert(ecs_has_pair(world, obj_d, rel, obj_c)); + test_assert(!ecs_has_pair(world, obj_d, rel, obj_b)); + test_assert(!ecs_has_pair(world, obj_d, rel, obj_a)); + + ecs_fini(world); +} + +void Eval_with_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + const char *expr = + HEAD "with Tag {" + LINE " Foo{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t tag = ecs_lookup(world, "Tag"); + + test_assert(foo != 0); + test_assert(tag != 0); + + test_assert(ecs_has_id(world, foo, tag)); + + ecs_fini(world); +} + +void Eval_with_tag_2_entities(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + const char *expr = + HEAD "with Tag {" + LINE " Foo{}" + LINE " Bar{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t tag = ecs_lookup(world, "Tag"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(tag != 0); + + test_assert(ecs_has_id(world, foo, tag)); + test_assert(ecs_has_id(world, bar, tag)); + + ecs_fini(world); +} + +void Eval_with_tag_same_line(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + const char *expr = + HEAD "with Tag { Foo{} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t tag = ecs_lookup(world, "Tag"); + + test_assert(foo != 0); + test_assert(tag != 0); + + test_assert(ecs_has_id(world, foo, tag)); + + ecs_fini(world); +} + +void Eval_with_tag_2_entities_same_line(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + const char *expr = + HEAD "with Tag { Foo, Bar, }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t tag = ecs_lookup(world, "Tag"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(tag != 0); + + test_assert(ecs_has_id(world, foo, tag)); + test_assert(ecs_has_id(world, bar, tag)); + + ecs_fini(world); +} + +void Eval_with_tag_2_entities_same_line_no_trailing_comma(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + const char *expr = + HEAD "with Tag { Foo, Bar }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t tag = ecs_lookup(world, "Tag"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(tag != 0); + + test_assert(ecs_has_id(world, foo, tag)); + test_assert(ecs_has_id(world, bar, tag)); + + ecs_fini(world); +} + +void Eval_with_tag_2_levels(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + const char *expr = + HEAD "with TagA {" + HEAD " with TagB {" + LINE " Foo {}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t tag_a = ecs_lookup(world, "TagA"); + ecs_entity_t tag_b = ecs_lookup(world, "TagB"); + + test_assert(foo != 0); + test_assert(tag_a != 0); + test_assert(tag_b != 0); + + test_assert(ecs_has_id(world, foo, tag_a)); + test_assert(ecs_has_id(world, foo, tag_b)); + + ecs_fini(world); +} + +void Eval_with_tag_2_levels_2_subtrees(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + + const char *expr = + HEAD "with TagA {" + HEAD " with TagB {" + LINE " Foo {}" + LINE " BarA {}" + LINE " }" + HEAD " with TagC {" + LINE " Foo {}" + LINE " BarB {}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t tag_a = ecs_lookup(world, "TagA"); + ecs_entity_t tag_b = ecs_lookup(world, "TagB"); + ecs_entity_t tag_c = ecs_lookup(world, "TagC"); + ecs_entity_t bar_a = ecs_lookup(world, "BarA"); + ecs_entity_t bar_b = ecs_lookup(world, "BarB"); + + test_assert(foo != 0); + test_assert(tag_a != 0); + test_assert(tag_b != 0); + test_assert(bar_a != 0); + test_assert(bar_b != 0); + + test_assert(ecs_has_id(world, foo, tag_a)); + test_assert(ecs_has_id(world, foo, tag_b)); + test_assert(ecs_has_id(world, foo, tag_c)); + + test_assert(ecs_has_id(world, bar_a, tag_a)); + test_assert(ecs_has_id(world, bar_b, tag_a)); + + test_assert(ecs_has_id(world, bar_a, tag_b)); + test_assert(ecs_has_id(world, bar_b, tag_c)); + + test_assert(!ecs_has_id(world, bar_b, tag_b)); + test_assert(!ecs_has_id(world, bar_a, tag_c)); + + ecs_fini(world); +} + +void Eval_with_n_tags(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + const char *expr = + HEAD "with TagA, TagB {" + LINE " Foo {}" + LINE " Bar {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t tag_a = ecs_lookup(world, "TagA"); + ecs_entity_t tag_b = ecs_lookup(world, "TagB"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(tag_a != 0); + test_assert(tag_b != 0); + + test_assert(ecs_has_id(world, foo, tag_a)); + test_assert(ecs_has_id(world, foo, tag_b)); + + test_assert(ecs_has_id(world, bar, tag_a)); + test_assert(ecs_has_id(world, bar, tag_b)); + + ecs_fini(world); +} + +void Eval_with_n_tags_2_levels(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, TagC); + ECS_TAG(world, TagD); + ECS_TAG(world, TagE); + ECS_TAG(world, TagF); + ECS_TAG(world, TagG); + ECS_TAG(world, TagH); + + const char *expr = + HEAD "with TagA, TagB {" + LINE " with TagC, TagD, TagE {" + LINE " Foo {}" + LINE " }" + LINE " HelloA {}" + LINE " with TagF, TagG, TagH {" + LINE " Bar {}" + LINE " }" + LINE " HelloB {}" + LINE "}" + LINE "HelloC {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tag_a = ecs_lookup(world, "TagA"); + ecs_entity_t tag_b = ecs_lookup(world, "TagB"); + ecs_entity_t tag_c = ecs_lookup(world, "TagC"); + ecs_entity_t tag_d = ecs_lookup(world, "TagD"); + ecs_entity_t tag_e = ecs_lookup(world, "TagE"); + ecs_entity_t tag_f = ecs_lookup(world, "TagF"); + ecs_entity_t tag_g = ecs_lookup(world, "TagG"); + ecs_entity_t tag_h = ecs_lookup(world, "TagH"); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + ecs_entity_t hello_a = ecs_lookup(world, "HelloA"); + ecs_entity_t hello_b = ecs_lookup(world, "HelloB"); + ecs_entity_t hello_c = ecs_lookup(world, "HelloC"); + + test_assert(tag_a != 0); + test_assert(tag_b != 0); + test_assert(tag_a != 0); + test_assert(tag_b != 0); + test_assert(tag_a != 0); + test_assert(tag_b != 0); + test_assert(tag_b != 0); + test_assert(tag_b != 0); + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(hello_a != 0); + test_assert(hello_b != 0); + test_assert(hello_c != 0); + + test_assert(ecs_has_id(world, foo, tag_a)); + test_assert(ecs_has_id(world, foo, tag_b)); + test_assert(ecs_has_id(world, foo, tag_c)); + test_assert(ecs_has_id(world, foo, tag_d)); + test_assert(ecs_has_id(world, foo, tag_e)); + test_assert(!ecs_has_id(world, foo, tag_f)); + test_assert(!ecs_has_id(world, foo, tag_g)); + test_assert(!ecs_has_id(world, foo, tag_h)); + + test_assert(ecs_has_id(world, bar, tag_a)); + test_assert(ecs_has_id(world, bar, tag_b)); + test_assert(!ecs_has_id(world, bar, tag_c)); + test_assert(!ecs_has_id(world, bar, tag_d)); + test_assert(!ecs_has_id(world, bar, tag_e)); + test_assert(ecs_has_id(world, bar, tag_f)); + test_assert(ecs_has_id(world, bar, tag_g)); + test_assert(ecs_has_id(world, bar, tag_h)); + + test_assert(ecs_has_id(world, hello_a, tag_a)); + test_assert(ecs_has_id(world, hello_a, tag_b)); + test_assert(!ecs_has_id(world, hello_a, tag_c)); + test_assert(!ecs_has_id(world, hello_a, tag_d)); + test_assert(!ecs_has_id(world, hello_a, tag_e)); + test_assert(!ecs_has_id(world, hello_a, tag_f)); + test_assert(!ecs_has_id(world, hello_a, tag_g)); + test_assert(!ecs_has_id(world, hello_a, tag_h)); + + test_assert(ecs_has_id(world, hello_b, tag_a)); + test_assert(ecs_has_id(world, hello_b, tag_b)); + test_assert(!ecs_has_id(world, hello_b, tag_c)); + test_assert(!ecs_has_id(world, hello_b, tag_d)); + test_assert(!ecs_has_id(world, hello_b, tag_e)); + test_assert(!ecs_has_id(world, hello_b, tag_f)); + test_assert(!ecs_has_id(world, hello_b, tag_g)); + test_assert(!ecs_has_id(world, hello_b, tag_h)); + + test_assert(!ecs_has_id(world, hello_c, tag_a)); + test_assert(!ecs_has_id(world, hello_c, tag_b)); + test_assert(!ecs_has_id(world, hello_c, tag_c)); + test_assert(!ecs_has_id(world, hello_c, tag_d)); + test_assert(!ecs_has_id(world, hello_c, tag_e)); + test_assert(!ecs_has_id(world, hello_c, tag_f)); + test_assert(!ecs_has_id(world, hello_c, tag_g)); + test_assert(!ecs_has_id(world, hello_c, tag_h)); + + ecs_fini(world); +} + +void Eval_with_after_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, E2); + ECS_TAG(world, E3); + + const char *expr = + HEAD "E1 { }" + HEAD "with E2 {" + LINE " E3 _ { }" + LINE " E4 { }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e1 = ecs_lookup(world, "E1"); + ecs_entity_t e2 = ecs_lookup(world, "E2"); + ecs_entity_t e3 = ecs_lookup(world, "E3"); + ecs_entity_t e4 = ecs_lookup(world, "E4"); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + test_assert(e4 != 0); + + test_assert(!ecs_has_pair(world, e2, EcsChildOf, e1)); + test_assert(!ecs_has_pair(world, e3, EcsChildOf, e1)); + test_assert(!ecs_has_pair(world, e4, EcsChildOf, e1)); + + test_assert(!ecs_has_id(world, e3, e2)); + test_assert(ecs_has_id(world, e4, e2)); + + ecs_fini(world); +} + +void Eval_with_after_with(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, E1); + ECS_TAG(world, E2); + ECS_TAG(world, E3); + + const char *expr = + HEAD "with E1 { }" + HEAD "with E2 {" + LINE " E3 _ { }" + LINE " E4 { }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e1 = ecs_lookup(world, "E1"); + ecs_entity_t e2 = ecs_lookup(world, "E2"); + ecs_entity_t e3 = ecs_lookup(world, "E3"); + ecs_entity_t e4 = ecs_lookup(world, "E4"); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + test_assert(e4 != 0); + + test_assert(!ecs_has_id(world, e2, e1)); + test_assert(!ecs_has_id(world, e3, e1)); + test_assert(!ecs_has_id(world, e4, e1)); + + test_assert(!ecs_has_id(world, e3, e2)); + test_assert(ecs_has_id(world, e4, e2)); + + ecs_fini(world); +} + +void Eval_scope_inside_with_inside_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Planet); + + const char *expr = + HEAD "Sun {" + LINE " with Planet {" + LINE " Earth {" + LINE " Moon {}" + LINE " }" + LINE " Mars {}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t sun = ecs_lookup(world, "Sun"); + ecs_entity_t planet = ecs_lookup(world, "Planet"); + ecs_entity_t earth = ecs_lookup(world, "Sun.Earth"); + ecs_entity_t moon = ecs_lookup(world, "Sun.Earth.Moon"); + ecs_entity_t mars = ecs_lookup(world, "Sun.Mars"); + + test_assert(sun != 0); + test_assert(planet != 0); + test_assert(earth != 0); + test_assert(moon != 0); + test_assert(mars != 0); + + test_assert(ecs_has_id(world, earth, planet)); + test_assert(ecs_has_id(world, moon, planet)); + test_assert(ecs_has_id(world, mars, planet)); + + test_assert(ecs_has_pair(world, earth, EcsChildOf, sun)); + test_assert(ecs_has_pair(world, moon, EcsChildOf, earth)); + test_assert(ecs_has_pair(world, mars, EcsChildOf, sun)); + + test_assert(!ecs_has_id(world, earth, sun)); + test_assert(!ecs_has_id(world, moon, earth)); + test_assert(!ecs_has_id(world, mars, sun)); + + test_assert(!ecs_has_pair(world, planet, EcsChildOf, sun)); + + ecs_fini(world); +} + +void Eval_with_tag_core_name(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "with World {" + LINE " Hello {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t hello = ecs_lookup(world, "Hello"); + test_assert(hello != 0); + test_assert(ecs_has_id(world, hello, EcsWorld)); + + ecs_fini(world); +} + +void Eval_with_inside_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Continent); + + const char *expr = + HEAD "Earth {" + LINE " with Continent {" + LINE " Europe {}" + LINE " }" + LINE " Europe {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t earth = ecs_lookup(world, "Earth"); + ecs_entity_t continent = ecs_lookup(world, "Continent"); + ecs_entity_t europe = ecs_lookup(world, "Earth.Europe"); + + test_assert(earth != 0); + test_assert(continent != 0); + test_assert(europe != 0); + + test_assert( ecs_has_pair(world, europe, EcsChildOf, earth)); + test_assert( !ecs_has_pair(world, continent, EcsChildOf, earth)); + test_assert( !ecs_has_id(world, europe, earth)); + test_assert( !ecs_has_id(world, continent, earth)); + + ecs_fini(world); +} + +void Eval_inherit(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo : Bar"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + test_assert(ecs_has_pair(world, foo, EcsIsA, bar)); + + ecs_fini(world); +} + +void Eval_inherit_newline(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo : Bar\n"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + test_assert(ecs_has_pair(world, foo, EcsIsA, bar)); + + ecs_fini(world); +} + +void Eval_inherit_w_colon(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo : Bar {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + test_assert(ecs_has_pair(world, foo, EcsIsA, bar)); + + ecs_fini(world); +} + +void Eval_inherit_w_colon_w_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo : Bar { Child{} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t child = ecs_lookup(world, "Foo.Child"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(child != 0); + + test_assert(ecs_has_pair(world, foo, EcsIsA, bar)); + test_assert(ecs_has_pair(world, child, EcsChildOf, foo)); + + ecs_fini(world); +} + +void Eval_assign_component_w_value(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo = Position: {10, 20}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_assign_tag_in_assign_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo {" + LINE " Bar" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(ecs_has_id(world, foo, bar)); + + ecs_fini(world); +} + +void Eval_assign_tag_in_assign_scope_same_line(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo { Bar }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(ecs_has_id(world, foo, bar)); + + ecs_fini(world); +} + +void Eval_assign_tag_in_assign_scope_core_name(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Hello {" + LINE " World" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t hello = ecs_lookup(world, "Hello"); + test_assert(hello != 0); + test_assert(ecs_has_id(world, hello, EcsWorld)); + + ecs_fini(world); +} + +void Eval_assign_component_value_in_assign_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo {" + LINE " Position: {10, 20}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_assign_2_component_values_in_assign_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo {" + LINE " Position: {10, 20}" + LINE " Velocity:{1, 2}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + test_assert(ecs_has(world, foo, Velocity)); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + { + const Velocity *ptr = ecs_get(world, foo, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + + ecs_fini(world); +} + +void Eval_type_and_assign_in_plecs(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "flecs.meta.struct Position {" + LINE " x { flecs.meta.member: {flecs.meta.f32} }" + LINE " y { flecs.meta.member: {flecs.meta.f32} }" + LINE "}" + LINE "" + LINE "Foo {" + LINE " Position: {10, 20}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(ecs_id(Position) != 0); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + +void Eval_type_and_assign_in_plecs_w_2_members(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "flecs.meta.struct Position {" + LINE " x { flecs.meta.member: {flecs.meta.f32} }" + LINE " y { flecs.meta.member: {flecs.meta.f32} }" + LINE "}" + LINE "" + LINE "Foo {" + LINE " Position: {x: 10, y: 20}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(ecs_id(Position) != 0); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + +void Eval_type_and_assign_in_plecs_w_3_members(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + float x; + float y; + float z; + } Position3D; + + const char *expr = + HEAD "flecs.meta.struct Position {" + LINE " x { flecs.meta.member: {flecs.meta.f32} }" + LINE " y { flecs.meta.member: {flecs.meta.f32} }" + LINE " z { flecs.meta.member: {flecs.meta.f32} }" + LINE "}" + LINE "" + LINE "Foo {" + LINE " Position: {x: 10, y: 20, z: 30}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Position3D) = ecs_lookup(world, "Position"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(ecs_id(Position3D) != 0); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position3D)); + + { + const Position3D *ptr = ecs_get(world, foo, Position3D); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + test_int(ptr->z, 30); + } + + ecs_fini(world); +} + +void Eval_type_and_assign_in_plecs_w_enum(void) { + ecs_world_t *world = ecs_init(); + + typedef enum { + Red, Green, Blue + } Color; + + typedef struct { + Color value; + } SomeType; + + const char *expr = + HEAD "flecs.meta.enum Color {" + LINE " flecs.meta.constant Red" + LINE " flecs.meta.constant Green" + LINE " flecs.meta.constant Blue" + LINE "}" + LINE "" + LINE "flecs.meta.struct SomeType {" + LINE " value { flecs.meta.member: {Color} }" + LINE "}" + LINE "" + LINE "Foo {" + LINE " SomeType: {value: Blue}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(SomeType) = ecs_lookup(world, "SomeType"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(ecs_id(SomeType) != 0); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, SomeType)); + + { + const SomeType *ptr = ecs_get(world, foo, SomeType); + test_assert(ptr != NULL); + test_int(ptr->value, Blue); + } + + ecs_fini(world); +} + +void Eval_type_and_assign_in_plecs_w_enum_using_meta(void) { + ecs_world_t *world = ecs_init(); + + typedef enum { + Red, Green, Blue + } Color; + + typedef struct { + Color value; + } SomeType; + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "enum Color {" + LINE " constant Red" + LINE " constant Green" + LINE " constant Blue" + LINE "}" + LINE + LINE "struct SomeType {" + LINE " value { member: {Color} }" + LINE "}" + LINE + LINE "Foo { SomeType: {value: Blue} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(SomeType) = ecs_lookup(world, "SomeType"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + /* Make sure no meta entities were created in the root */ + test_assert(ecs_lookup(world, "Enum") == 0); + test_assert(ecs_lookup(world, "Struct") == 0); + test_assert(ecs_lookup(world, "Constant") == 0); + test_assert(ecs_lookup(world, "Red") == 0); + test_assert(ecs_lookup(world, "Green") == 0); + test_assert(ecs_lookup(world, "Blue") == 0); + + test_assert(ecs_id(SomeType) != 0); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, SomeType)); + + { + const SomeType *ptr = ecs_get(world, foo, SomeType); + test_assert(ptr != NULL); + test_int(ptr->value, Blue); + } + + ecs_fini(world); +} + +void Eval_type_and_assign_in_plecs_w_enum_primitive_using_meta(void) { + ecs_world_t *world = ecs_init(); + + typedef enum { + Red, Green, Blue + } Color; + + const char *expr = + HEAD "using flecs.meta" + LINE "enum Color {" + LINE " constant Red" + LINE " constant Green" + LINE " constant Blue" + LINE "}" + LINE "" + LINE "Foo { Color: {Blue} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Color) = ecs_lookup(world, "Color"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + /* Make sure no meta entities were created in the root */ + test_assert(ecs_lookup(world, "Enum") == 0); + test_assert(ecs_lookup(world, "Struct") == 0); + test_assert(ecs_lookup(world, "Constant") == 0); + test_assert(ecs_lookup(world, "Red") == 0); + test_assert(ecs_lookup(world, "Green") == 0); + test_assert(ecs_lookup(world, "Blue") == 0); + + test_assert(ecs_id(Color) != 0); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Color)); + + { + const Color *ptr = ecs_get(world, foo, Color); + test_assert(ptr != NULL); + test_int(*ptr, Blue); + } + + ecs_fini(world); +} + +void Eval_type_and_assign_in_plecs_w_enum_primitive_and_struct(void) { + ecs_world_t *world = ecs_init(); + + typedef enum { + Red, Green, Blue + } Color; + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Position {" + LINE " x { member: {f32} }" + LINE " y { member: {f32} }" + LINE "}" + LINE + LINE "enum Color {" + LINE " constant Red" + LINE " constant Green" + LINE " constant Blue" + LINE "}" + LINE + LINE "Foo {" + LINE " Position: {x: 10, y: 20}" + LINE " Color: {Green}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); + ecs_entity_t ecs_id(Color) = ecs_lookup(world, "Color"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + /* Make sure no meta entities were created in the root */ + test_assert(ecs_lookup(world, "Enum") == 0); + test_assert(ecs_lookup(world, "Struct") == 0); + test_assert(ecs_lookup(world, "Constant") == 0); + test_assert(ecs_lookup(world, "Red") == 0); + test_assert(ecs_lookup(world, "Green") == 0); + test_assert(ecs_lookup(world, "Blue") == 0); + + test_assert(ecs_id(Position) != 0); + test_assert(ecs_id(Color) != 0); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + test_assert(ecs_has(world, foo, Color)); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + { + const Color *ptr = ecs_get(world, foo, Color); + test_assert(ptr != NULL); + test_int(*ptr, Green); + } + + ecs_fini(world); +} + +void Eval_type_and_assign_in_plecs_nested_member(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + struct { + float x; + float y; + } start; + + struct { + float x; + float y; + } stop; + } Line; + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Line {" + LINE " member start {" + LINE " x { member: {f32} }" + LINE " y { member: {f32} }" + LINE " }" + LINE " member stop {" + LINE " x { member: {f32} }" + LINE " y { member: {f32} }" + LINE " }" + LINE "}" + LINE + LINE "l { Line: {start: {x: 10, y: 20}, stop: {x: 30, y: 40}} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Line) = ecs_lookup(world, "Line"); + ecs_entity_t l = ecs_lookup(world, "l"); + + test_assert(ecs_id(Line) != 0); + test_assert(l != 0); + + test_assert( ecs_has(world, ecs_id(Line), EcsComponent)); + test_assert( ecs_has(world, ecs_id(Line), EcsStruct)); + test_assert( ecs_has(world, l, Line)); + + const Line *ptr = ecs_get(world, l, Line); + test_assert(ptr != NULL); + test_int(ptr->start.x, 10); + test_int(ptr->start.y, 20); + test_int(ptr->stop.x, 30); + test_int(ptr->stop.y, 40); + + + ecs_fini(world); +} + +void Eval_dot_assign_nested_member(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + struct { + float x; + float y; + } start; + + struct { + float x; + float y; + } stop; + } Line; + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Line {" + LINE " member start {" + LINE " x { member: {f32} }" + LINE " y { member: {f32} }" + LINE " }" + LINE " member stop {" + LINE " x { member: {f32} }" + LINE " y { member: {f32} }" + LINE " }" + LINE "}" + LINE + LINE "l { Line: {start.x: 10, start.y: 20, stop.x: 30, stop.y: 40} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Line) = ecs_lookup(world, "Line"); + ecs_entity_t l = ecs_lookup(world, "l"); + + test_assert(ecs_id(Line) != 0); + test_assert(l != 0); + + test_assert( ecs_has(world, ecs_id(Line), EcsComponent)); + test_assert( ecs_has(world, ecs_id(Line), EcsStruct)); + test_assert( ecs_has(world, l, Line)); + + const Line *ptr = ecs_get(world, l, Line); + test_assert(ptr != NULL); + test_int(ptr->start.x, 10); + test_int(ptr->start.y, 20); + test_int(ptr->stop.x, 30); + test_int(ptr->stop.y, 40); + + ecs_fini(world); +} + +void Eval_dot_assign_binary_expr(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + struct { + float x; + float y; + } start; + + struct { + float x; + float y; + } stop; + } Line; + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Line {" + LINE " member start {" + LINE " x { member: {f32} }" + LINE " y { member: {f32} }" + LINE " }" + LINE " member stop {" + LINE " x { member: {f32} }" + LINE " y { member: {f32} }" + LINE " }" + LINE "}" + LINE + LINE "l { Line: {start.x: 2 + 8, start.y: 5 + 15, stop.x: 10 + 20, stop.y: 15 + 25} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Line) = ecs_lookup(world, "Line"); + ecs_entity_t l = ecs_lookup(world, "l"); + + test_assert(ecs_id(Line) != 0); + test_assert(l != 0); + + test_assert( ecs_has(world, ecs_id(Line), EcsComponent)); + test_assert( ecs_has(world, ecs_id(Line), EcsStruct)); + test_assert( ecs_has(world, l, Line)); + + const Line *ptr = ecs_get(world, l, Line); + test_assert(ptr != NULL); + test_int(ptr->start.x, 10); + test_int(ptr->start.y, 20); + test_int(ptr->stop.x, 30); + test_int(ptr->stop.y, 40); + + + ecs_fini(world); +} + +void Eval_open_scope_no_parent(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {" + LINE " Bar {}" + LINE "}" + LINE "{" + LINE " Zoo {}" + LINE "}" + LINE "Hello {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Foo.Bar"); + ecs_entity_t zoo = ecs_lookup(world, "Zoo"); + ecs_entity_t hello = ecs_lookup(world, "Hello"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(zoo != 0); + test_assert(hello != 0); + + test_assert(ecs_has_pair(world, bar, EcsChildOf, foo)); + test_assert(!ecs_has_pair(world, zoo, EcsChildOf, EcsWildcard)); + test_assert(!ecs_has_pair(world, hello, EcsChildOf, EcsWildcard)); + + ecs_fini(world); +} + +void Eval_create_subject_in_root_scope_w_resolvable_id(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Tag {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t tag = ecs_lookup_child(world, 0, "Tag"); + test_assert(tag != 0); + test_assert(tag != EcsPairIsTag); + + ecs_fini(world); +} + +void Eval_create_subject_in_scope_w_resolvable_id(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {" + LINE " Tag {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + ecs_entity_t tag = ecs_lookup(world, "Foo.Tag"); + test_assert(tag != 0); + test_assert(tag != EcsPairIsTag); + + test_assert(ecs_has_pair(world, tag, EcsChildOf, foo)); + + ecs_fini(world); +} + +void Eval_create_subject_in_scope_w_resolvable_id_using(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Hello); + + const char *expr = + HEAD "Foo {" + LINE " Bar {}" + LINE "}" + LINE + LINE "using Foo" + LINE + LINE "Hello Bar {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Foo.Bar"); + ecs_entity_t root_bar = ecs_lookup(world, "Bar"); + ecs_entity_t hello = ecs_lookup(world, "Hello"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(root_bar != 0); + test_assert(hello != 0); + + test_assert(ecs_has_pair(world, bar, EcsChildOf, foo)); + + test_assert(ecs_has_id(world, root_bar, hello)); + test_assert(!ecs_has_id(world, bar, hello)); + + ecs_fini(world); +} + +void Eval_using_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {" + LINE " Bar {}" + LINE "}" + LINE "" + LINE "using Foo" + LINE "Bar Hello {}" + LINE "Foo.Bar World {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Foo.Bar"); + ecs_entity_t not_bar = ecs_lookup(world, "Bar"); + ecs_entity_t hello = ecs_lookup(world, "Hello"); + ecs_entity_t _world = ecs_lookup_child(world, 0, "World"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(hello != 0); + test_assert(_world != 0); + test_assert(not_bar == 0); + + test_assert(_world != EcsWorld); /* sanity check, verified by other tests */ + + test_assert(ecs_has_pair(world, bar, EcsChildOf, foo)); + test_assert(ecs_has_id(world, hello, bar)); + test_assert(ecs_has_id(world, _world, bar)); + + ecs_fini(world); +} + +void Eval_using_nested_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {" + LINE " Bar {" + LINE " Zoo {}" + LINE " }" + LINE "}" + LINE "" + LINE "using Foo.Bar" + LINE "Zoo Hello {}" + LINE "Foo.Bar.Zoo World {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Foo.Bar"); + ecs_entity_t zoo = ecs_lookup(world, "Foo.Bar.Zoo"); + ecs_entity_t not_bar = ecs_lookup(world, "Bar"); + ecs_entity_t not_zoo = ecs_lookup(world, "Zoo"); + ecs_entity_t hello = ecs_lookup(world, "Hello"); + ecs_entity_t _world = ecs_lookup_child(world, 0, "World"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(zoo != 0); + test_assert(hello != 0); + test_assert(_world != 0); + test_assert(not_bar == 0); + test_assert(not_zoo == 0); + + test_assert(_world != EcsWorld); /* sanity check, verified by other tests */ + + test_assert(ecs_has_id(world, hello, zoo)); + test_assert(ecs_has_id(world, _world, zoo)); + + ecs_fini(world); +} + +void Eval_using_nested_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Zoo); + + const char *expr = + HEAD "Foo {" + LINE " Bar {" + LINE " Zoo {}" + LINE " }" + LINE "}" + LINE "{" + LINE " using Foo.Bar" + LINE " Zoo Hello {}" + LINE "}" + LINE "Zoo World {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Foo.Bar"); + ecs_entity_t zoo = ecs_lookup(world, "Foo.Bar.Zoo"); + ecs_entity_t not_bar = ecs_lookup(world, "Bar"); + ecs_entity_t zoo_root = ecs_lookup(world, "Zoo"); + ecs_entity_t hello = ecs_lookup(world, "Hello"); + ecs_entity_t _world = ecs_lookup_child(world, 0, "World"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(zoo != 0); + test_assert(hello != 0); + test_assert(_world != 0); + test_assert(not_bar == 0); + test_assert(zoo_root != 0); + + test_assert(_world != EcsWorld); /* sanity check, verified by other tests */ + + test_assert(ecs_has_id(world, hello, zoo)); + test_assert(ecs_has_id(world, _world, zoo_root)); + + ecs_fini(world); +} + +void Eval_using_with_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using flecs.meta" + LINE "" + LINE "Scope {" + LINE "" + LINE "Foo {}" + LINE "" + LINE "struct Bar {" + LINE " x { member: {f32} }" + LINE "}" + LINE "" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t scope = ecs_lookup(world, "Scope"); + ecs_entity_t foo = ecs_lookup(world, "Scope.Foo"); + ecs_entity_t bar = ecs_lookup(world, "Scope.Bar"); + ecs_entity_t x = ecs_lookup(world, "Scope.Bar.x"); + + test_assert(ecs_lookup(world, "Foo") == 0); + test_assert(ecs_lookup(world, "Bar") == 0); + test_assert(ecs_lookup(world, "x") == 0); + test_assert(ecs_lookup(world, "Struct") == 0); + test_assert(ecs_lookup(world, "Member") == 0); + + test_assert(scope != 0); + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(x != 0); + + test_assert(ecs_has_pair(world, foo, EcsChildOf, scope)); + test_assert(ecs_has_pair(world, bar, EcsChildOf, scope)); + test_assert(ecs_has_pair(world, x, EcsChildOf, bar)); + + ecs_fini(world); +} + +void Eval_using_w_entity_ref_in_value_2_members(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Position {" + LINE " x { member: {f32} }" // member type is looked up in flecs.meta + LINE " y { member: {f32} }" + LINE "}" + LINE "" + LINE "Foo { Position: {x: 10, y: 20} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(ecs_id(Position) != 0); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + +void Eval_using_w_entity_ref_in_value_3_members(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + float x; + float y; + float z; + } Position3D; + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Position {" + LINE " x { member: {f32} }" // member type is looked up in flecs.meta + LINE " y { member: {f32} }" + LINE " z { member: {f32} }" + LINE "}" + LINE + LINE "Foo { Position: {x: 10, y: 20, z: 30} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t ecs_id(Position3D) = ecs_lookup(world, "Position"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + test_assert(ecs_id(Position3D) != 0); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position3D)); + + { + const Position3D *ptr = ecs_get(world, foo, Position3D); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + test_int(ptr->z, 30); + } + + ecs_fini(world); +} + +void Eval_script_w_only_using(void) { + ecs_world_t *world = ecs_init(); + + test_assert(ecs_script_run(world, NULL, "using flecs") == 0); + + ecs_fini(world); +} + +void Eval_2_using_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using Foo" + LINE "using Bar" + LINE + LINE "Foo {" + LINE " Hello {}" + LINE "}" + LINE + LINE "Bar {" + LINE " TheWorld {}" + LINE "}" + LINE + LINE "Hello E1 {}" + LINE "TheWorld E2 {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t hello = ecs_lookup(world, "Foo.Hello"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t _world = ecs_lookup(world, "Bar.TheWorld"); + + ecs_entity_t e1 = ecs_lookup(world, "E1"); + ecs_entity_t e2 = ecs_lookup(world, "E2"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(hello != 0); + test_assert(_world != 0); + test_assert(e1 != 0); + test_assert(e2 != 0); + + test_assert(ecs_lookup(world, "Hello") == 0); + test_assert(ecs_lookup(world, "TheWorld") == 0); + + test_assert(ecs_has_pair(world, hello, EcsChildOf, foo)); + test_assert(ecs_has_pair(world, _world, EcsChildOf, bar)); + test_assert(ecs_has_id(world, e1, hello)); + test_assert(ecs_has_id(world, e2, _world)); + + ecs_fini(world); +} + +void Eval_2_using_in_different_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TheWorld); + ECS_TAG(world, Hello); + + const char *expr = + LINE "Foo {" + LINE " Hello {}" + LINE "}" + LINE + LINE "Bar {" + LINE " TheWorld {}" + LINE "}" + LINE + LINE "E1 {" + LINE " using Foo" + LINE + LINE " Hello Child {}" + LINE " TheWorld Child {}" + LINE "}" + LINE + LINE "E2 {" + LINE " using Bar" + LINE + LINE " Hello Child {}" + LINE " TheWorld Child {}" + LINE "}" + LINE + LINE "Hello RootChild {}" + LINE "TheWorld RootChild {}" + LINE; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t hello = ecs_lookup(world, "Foo.Hello"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t _world = ecs_lookup(world, "Bar.TheWorld"); + + ecs_entity_t e1 = ecs_lookup(world, "E1"); + ecs_entity_t e1_child = ecs_lookup(world, "E1.Child"); + ecs_entity_t e2 = ecs_lookup(world, "E2"); + ecs_entity_t e2_child = ecs_lookup(world, "E2.Child"); + + ecs_entity_t root_hello = ecs_lookup(world, "Hello"); + ecs_entity_t root_world = ecs_lookup(world, "TheWorld"); + + ecs_entity_t root_child = ecs_lookup(world, "RootChild"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(hello != 0); + test_assert(_world != 0); + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e1_child != 0); + test_assert(e2_child != 0); + test_assert(root_hello != 0); + test_assert(root_world != 0); + test_assert(root_world != 0); + + test_assert(ecs_has_pair(world, hello, EcsChildOf, foo)); + test_assert(ecs_has_pair(world, _world, EcsChildOf, bar)); + + test_assert(ecs_has_pair(world, e1_child, EcsChildOf, e1)); + test_assert(ecs_has_pair(world, e2_child, EcsChildOf, e2)); + + test_assert(ecs_has_id(world, e1_child, hello)); + test_assert(ecs_has_id(world, e1_child, root_world)); + + test_assert(ecs_has_id(world, e2_child, root_hello)); + test_assert(ecs_has_id(world, e2_child, _world)); + + test_assert(ecs_has_id(world, root_child, root_hello)); + test_assert(ecs_has_id(world, root_child, root_world)); + + ecs_fini(world); +} + +void Eval_scope_after_assign(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Position Foo(10, 20) {" + LINE " Bar {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Foo.Bar"); + + test_assert(ecs_has_pair(world, bar, EcsChildOf, foo)); + + { + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Eval_assign_after_inherit(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo : Position {}" + LINE "Bar { Position: {10, 20} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + test_assert( ecs_has_pair(world, foo, EcsIsA, ecs_id(Position))); + test_assert( !ecs_has(world, foo, Position)); + + test_assert( !ecs_has_pair(world, bar, EcsIsA, ecs_id(Position))); + test_assert( ecs_has(world, bar, Position)); + + const Position *ptr = ecs_get(world, bar, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_multiple_tags_single_line(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Hello); + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo { Hello; Bar }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + ecs_entity_t hello = ecs_lookup(world, "Hello"); + test_assert(hello != 0); + + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(bar != 0); + + test_assert( ecs_has_id(world, foo, hello)); + test_assert( ecs_has_id(world, foo, bar)); + + ecs_fini(world); +} + +void Eval_multiple_pairs_single_line(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Hello); + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo { (Rel, Hello); (Rel, Bar) }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + ecs_entity_t rel = ecs_lookup(world, "Rel"); + test_assert(rel != 0); + + ecs_entity_t hello = ecs_lookup(world, "Hello"); + test_assert(hello != 0); + + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(bar != 0); + + test_assert( ecs_has_pair(world, foo, rel, hello)); + test_assert( ecs_has_pair(world, foo, rel, bar)); + + ecs_fini(world); +} + +void Eval_multiple_assignments_single_line(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo { Position: {10, 20}; Velocity: {1, 2} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + { + const Velocity *ptr = ecs_get(world, foo, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + + ecs_fini(world); +} + +void Eval_multiple_vars_single_line(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const pos = Position: {10, 20}" + LINE "const vel = Velocity: {1, 2}" + LINE "Foo { $pos; $vel }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + { + const Velocity *ptr = ecs_get(world, foo, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + + ecs_fini(world); +} + +void Eval_2_stmts_in_scope_w_no_parent(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "{" + LINE "Bar { }" + LINE "Foo { }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + test_assert( !ecs_has_id(world, foo, bar)); + test_assert( !ecs_has_pair(world, foo, EcsChildOf, bar)); + + test_assert( !ecs_has_id(world, bar, foo)); + test_assert( !ecs_has_pair(world, bar, EcsChildOf, foo)); + + ecs_fini(world); +} + +void Eval_empty_scope_after_using(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using flecs.meta" + LINE "{" + LINE " Foo {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert( foo != 0); + test_assert( !ecs_has_pair(world, foo, EcsChildOf, EcsWildcard)); + + ecs_fini(world); +} + +void Eval_assign_tag_to_parent(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo {" + LINE " Bar" + LINE " Child {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t child = ecs_lookup(world, "Foo.Child"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(child != 0); + + test_assert( ecs_has_id(world, foo, bar)); + test_assert( !ecs_has_id(world, child, bar)); + test_assert( !ecs_has_pair(world, bar, EcsChildOf, foo)); + test_assert( ecs_has_pair(world, child, EcsChildOf, foo)); + + ecs_fini(world); +} + +void Eval_assign_component_to_parent(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo {" + LINE " Position: {10, 20}" + LINE " Child {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t child = ecs_lookup(world, "Foo.Child"); + + test_assert(foo != 0); + test_assert(child != 0); + + test_assert( ecs_has(world, foo, Position)); + test_assert( !ecs_has(world, child, Position)); + test_assert( !ecs_has_pair(world, ecs_id(Position), EcsChildOf, foo)); + test_assert( ecs_has_pair(world, child, EcsChildOf, foo)); + + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_assign_to_parent_pair_w_new_entities_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Obj); + + const char *expr = + HEAD "Foo {" + LINE " (Rel, Obj)" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t rel = ecs_lookup(world, "Rel"); + ecs_entity_t obj = ecs_lookup(world, "Obj"); + + test_assert(foo != 0); + test_assert(rel != 0); + test_assert(obj != 0); + + test_assert( ecs_has_pair(world, foo, rel, obj)); + test_assert( !ecs_has_pair(world, rel, EcsChildOf, EcsWildcard)); + test_assert( !ecs_has_pair(world, obj, EcsChildOf, EcsWildcard)); + + ecs_fini(world); +} + +void Eval_assign_to_parent_pair_w_existing_entities_in_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Rel, Obj" + LINE "Foo {" + LINE " (Rel, Obj)" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t rel = ecs_lookup(world, "Rel"); + ecs_entity_t obj = ecs_lookup(world, "Obj"); + + test_assert(foo != 0); + test_assert(rel != 0); + test_assert(obj != 0); + + test_assert( ecs_has_pair(world, foo, rel, obj)); + test_assert( !ecs_has_pair(world, rel, EcsChildOf, EcsWildcard)); + test_assert( !ecs_has_pair(world, obj, EcsChildOf, EcsWildcard)); + + ecs_fini(world); +} + +void Eval_default_child_component(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Bar {}" + LINE "DefaultChildComponent Foo(Bar)" + LINE "Foo Parent {" + LINE " ChildA," + LINE " ChildB" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child_a = ecs_lookup(world, "Parent.ChildA"); + ecs_entity_t child_b = ecs_lookup(world, "Parent.ChildB"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(parent != 0); + test_assert(child_a != 0); + test_assert(child_b != 0); + + const EcsDefaultChildComponent *dcc = + ecs_get(world, foo, EcsDefaultChildComponent); + test_assert(dcc != NULL); + test_uint(dcc->component, bar); + + test_assert( ecs_has_id(world, parent, foo)); + test_assert( ecs_has_pair(world, child_a, EcsChildOf, parent)); + test_assert( ecs_has_pair(world, child_b, EcsChildOf, parent)); + + test_assert(ecs_has_id(world, child_a, bar)); + test_assert(ecs_has_id(world, child_b, bar)); + + ecs_fini(world); +} + +void Eval_default_child_component_w_assign(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "DefaultChildComponent Foo(Position)" + LINE "Foo Parent {" + LINE " ChildA = 10, 20" + LINE " ChildB = 10, 20" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child_a = ecs_lookup(world, "Parent.ChildA"); + ecs_entity_t child_b = ecs_lookup(world, "Parent.ChildB"); + + test_assert(foo != 0); + test_assert(parent != 0); + test_assert(child_a != 0); + test_assert(child_b != 0); + + const EcsDefaultChildComponent *dcc = + ecs_get(world, foo, EcsDefaultChildComponent); + test_assert(dcc != NULL); + test_uint(dcc->component, ecs_id(Position)); + + test_assert( ecs_has_id(world, parent, foo)); + test_assert( ecs_has_pair(world, child_a, EcsChildOf, parent)); + test_assert( ecs_has_pair(world, child_b, EcsChildOf, parent)); + + test_assert(ecs_has(world, child_a, Position)); + test_assert(ecs_has(world, child_b, Position)); + + ecs_fini(world); +} + +void Eval_struct_type_w_default_child_component(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Position {" + LINE " x = f32" + LINE " y = f32" + LINE "}" + LINE "Foo { Position: {x: 10, y: 20} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); + ecs_entity_t x = ecs_lookup(world, "Position.x"); + ecs_entity_t y = ecs_lookup(world, "Position.y"); + + test_assert(foo != 0); + test_assert(ecs_id(Position) != 0); + test_assert(x != 0); + test_assert(y != 0); + + test_assert( ecs_has(world, ecs_id(Position), EcsStruct)); + test_assert( ecs_has(world, x, EcsMember)); + test_assert( ecs_has(world, y, EcsMember)); + test_assert( !ecs_has_pair(world, x, EcsIsA, ecs_id(ecs_f32_t))); + test_assert( !ecs_has_pair(world, y, EcsIsA, ecs_id(ecs_f32_t))); + + test_assert( ecs_has(world, foo, Position)); + + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_struct_type_w_default_child_component_nested_member(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + struct { + float x; + float y; + } start; + + struct { + float x; + float y; + } stop; + } Line; + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Line {" + LINE " start {" + LINE " x = f32" + LINE " y = f32" + LINE " }" + LINE " stop {" + LINE " x = f32" + LINE " y = f32" + LINE " }" + LINE "}" + LINE + LINE "Foo { Line: {start: {10, 20}, stop: {30, 40}} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t ecs_id(Line) = ecs_lookup(world, "Line"); + + test_assert(foo != 0); + test_assert(ecs_id(Line) != 0); + + test_assert( ecs_has(world, foo, Line)); + + const Line *ptr = ecs_get(world, foo, Line); + test_assert(ptr != NULL); + test_int(ptr->start.x, 10); + test_int(ptr->start.y, 20); + test_int(ptr->stop.x, 30); + test_int(ptr->stop.y, 40); + + ecs_fini(world); +} + +void Eval_enum_type_w_default_child_component(void) { + ecs_world_t *world = ecs_init(); + + typedef enum { + Red, Green, Blue + } Color; + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "enum Color {" + LINE " Red, Green, Blue" + LINE "}" + LINE + LINE "Foo { Color: {Green} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t ecs_id(Color) = ecs_lookup(world, "Color"); + + test_assert(foo != 0); + test_assert(ecs_id(Color) != 0); + + test_assert( ecs_has(world, foo, Color)); + + const Color *ptr = ecs_get(world, foo, Color); + test_assert(ptr != NULL); + test_int(*ptr, Green); + + ecs_fini(world); +} + +void Eval_default_type_from_with(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "with Position {" + LINE " e1 = 10, 20" + LINE " e2 = 30, 40" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e1 = ecs_lookup(world, "e1"); + ecs_entity_t e2 = ecs_lookup(world, "e2"); + + test_assert(e1 != 0); + test_assert(e2 != 0); + + const Position *p = ecs_get(world, e1, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + p = ecs_get(world, e2, Position); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + + ecs_fini(world); +} + +void Eval_default_type_from_nested_with(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "with Velocity {" + LINE " e1 = 1, 2" + LINE " with Position {" + LINE " e2 = 10, 20" + LINE " e3 = 30, 40" + LINE " }" + LINE " e4 = 3, 4" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e1 = ecs_lookup(world, "e1"); + ecs_entity_t e2 = ecs_lookup(world, "e2"); + ecs_entity_t e3 = ecs_lookup(world, "e3"); + ecs_entity_t e4 = ecs_lookup(world, "e4"); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + test_assert(e4 != 0); + + const Position *p = ecs_get(world, e2, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + p = ecs_get(world, e3, Position); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + + const Velocity *v = ecs_get(world, e1, Velocity); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + + v = ecs_get(world, e4, Velocity); + test_assert(v != NULL); + test_int(v->x, 3); + test_int(v->y, 4); + + ecs_fini(world); +} + +void Eval_default_type_from_with_in_entity_scope_w_default_type(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Velocity"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo = DefaultChildComponent: {Velocity}" + LINE "Foo Bar {" + LINE " e3 = 1, 2" + LINE " with Position {" + LINE " e1 = 10, 20" + LINE " e2 = 30, 40" + LINE " }" + LINE " e4 = 2, 3" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e1 = ecs_lookup(world, "Bar.e1"); + ecs_entity_t e2 = ecs_lookup(world, "Bar.e2"); + ecs_entity_t e3 = ecs_lookup(world, "Bar.e3"); + ecs_entity_t e4 = ecs_lookup(world, "Bar.e4"); + + test_assert(ecs_has(world, e1, Position)); + test_assert(ecs_has(world, e2, Position)); + test_assert(!ecs_has(world, e3, Position)); + test_assert(!ecs_has(world, e4, Position)); + + test_assert(!ecs_has(world, e1, Velocity)); + test_assert(!ecs_has(world, e2, Velocity)); + test_assert(ecs_has(world, e3, Velocity)); + test_assert(ecs_has(world, e4, Velocity)); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + test_assert(e4 != 0); + + const Position *p = ecs_get(world, e1, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + p = ecs_get(world, e2, Position); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + + const Velocity *v = ecs_get(world, e3, Velocity); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + + v = ecs_get(world, e4, Velocity); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 3); + + ecs_fini(world); +} + +void Eval_default_type_from_entity_scope_in_with(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "using flecs.meta" + LINE "with Position {" + LINE " e1 = 10, 20" + LINE " struct Velocity {" + LINE " x = f32" + LINE " y = f32" + LINE " }" + LINE " e2 = 30, 40" + LINE "}" + LINE "e3 = Velocity: {1, 2}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e1 = ecs_lookup(world, "e1"); + ecs_entity_t e2 = ecs_lookup(world, "e2"); + ecs_entity_t e3 = ecs_lookup(world, "e3"); + ecs_entity_t ecs_id(Velocity) = ecs_lookup(world, "Velocity"); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + test_assert(ecs_id(Velocity) != 0); + + const Position *p = ecs_get(world, e1, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + p = ecs_get(world, e2, Position); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + + const Velocity *v = ecs_get(world, e3, Velocity); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Eval_scope_w_1_subj_and_2_pairs(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, RelA); + ECS_TAG(world, RelB); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Parent {" + LINE " Foo { (RelA, Bar) }" + LINE " Foo { (RelB, Bar) }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t rel_a = ecs_lookup(world, "RelA"); + ecs_entity_t rel_b = ecs_lookup(world, "RelB"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t foo = ecs_lookup(world, "Parent.Foo"); + + test_assert(parent != 0); + test_assert(rel_a != 0); + test_assert(rel_b != 0); + test_assert(foo != 0); + test_assert(bar != 0); + + test_assert( ecs_has_pair(world, foo, rel_a, bar)); + test_assert( ecs_has_pair(world, foo, rel_b, bar)); + + ecs_fini(world); +} + +void Eval_inherit_from_multiple(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Inst : Foo, Bar {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t inst = ecs_lookup(world, "Inst"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(inst != 0); + test_assert(foo != 0); + test_assert(bar != 0); + + test_assert(ecs_has_pair(world, inst, EcsIsA, foo)); + test_assert(ecs_has_pair(world, inst, EcsIsA, bar)); + + ecs_fini(world); +} + +void Eval_assign_pair_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Position {" + LINE " x = f32" + LINE " y = f32" + LINE "}" + LINE + LINE "Foo { (Position, Bar): {x: 10, y: 20} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(ecs_id(Position) != 0); + + test_assert( ecs_has_pair(world, foo, ecs_id(Position), bar)); + + const Position *ptr = ecs_get_pair(world, foo, Position, bar); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_assign_pair_component_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Position {" + LINE " x = f32" + LINE " y = f32" + LINE "}" + LINE + LINE "Parent {" + LINE " (Position, Foo): {x: 10, y: 20}" + LINE " (Position, Bar): {x: 20, y: 30}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); + + test_assert(parent != 0); + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(ecs_id(Position) != 0); + + test_assert( ecs_has_pair(world, parent, ecs_id(Position), foo)); + test_assert( ecs_has_pair(world, parent, ecs_id(Position), bar)); + + const Position * + ptr = ecs_get_pair(world, parent, Position, foo); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ptr = ecs_get_pair(world, parent, Position, bar); + test_assert(ptr != NULL); + test_int(ptr->x, 20); + test_int(ptr->y, 30); + + ecs_fini(world); +} + +void Eval_assign_pair_component_in_script(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Position {" + LINE " x = f32" + LINE " y = f32" + LINE "}" + LINE + LINE "Foo { (Position, Bar): {x: 10, y: 20} }"; + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s != 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(ecs_id(Position) != 0); + + test_assert( ecs_has_pair(world, foo, ecs_id(Position), bar)); + + const Position *ptr = ecs_get_pair(world, foo, Position, bar); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_assign_pair_component_in_script_update(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "using flecs.meta" + LINE + LINE "struct Position {" + LINE " x = f32" + LINE " y = f32" + LINE "}" + LINE + LINE "Foo { (Position, Bar): {x: 10, y: 20} }"; + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s != 0); + + test_assert(ecs_script_update(world, s, 0, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(ecs_id(Position) != 0); + + test_assert( ecs_has_pair(world, foo, ecs_id(Position), bar)); + + const Position *ptr = ecs_get_pair(world, foo, Position, bar); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_set_entity_names(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, foo); + + const char *expr = + HEAD "foo self {}" + LINE "foo parent {}" + LINE "foo cascade {}" + LINE "foo down {}" + LINE "foo up {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t self = ecs_lookup(world, "self"); + ecs_entity_t down = ecs_lookup(world, "down"); + ecs_entity_t up = ecs_lookup(world, "up"); + ecs_entity_t parent = ecs_lookup(world, "parent"); + ecs_entity_t cascade = ecs_lookup(world, "cascade"); + + test_assert(foo != 0); + test_assert(self != 0); + test_assert(down != 0); + test_assert(up != 0); + test_assert(parent != 0); + test_assert(cascade != 0); + + test_assert( ecs_has_id(world, self, foo)); + test_assert( ecs_has_id(world, down, foo)); + test_assert( ecs_has_id(world, up, foo)); + test_assert( ecs_has_id(world, parent, foo)); + test_assert( ecs_has_id(world, cascade, foo)); + + ecs_fini(world); +} + +void Eval_oneof(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "flecs.meta.enum Color {" + LINE " flecs.meta.constant Red" + LINE " flecs.meta.constant Green" + LINE " flecs.meta.constant Blue" + LINE "}" + LINE "e { (Color, Green) }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t color = ecs_lookup(world, "Color"); + ecs_entity_t green = ecs_lookup(world, "Color.Green"); + ecs_entity_t e = ecs_lookup(world, "e"); + + test_assert(color != 0); + test_assert(green != 0); + test_assert(e != 0); + test_assert(ecs_lookup(world, "Green") == 0); + + test_assert( ecs_has_id(world, color, EcsOneOf)); + test_assert( ecs_has_id(world, green, EcsConstant)); + test_assert( ecs_has_id(world, e, ecs_pair(color, green))); + + ecs_fini(world); +} + +void Eval_brief_annotation(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "@brief A description" + LINE "Foo {}" + LINE "Bar {}" + LINE "" + LINE "@brief Another description" + LINE "Baz {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t baz = ecs_lookup(world, "Baz"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(baz != 0); + + test_str(ecs_doc_get_brief(world, foo), "A description"); + test_str(ecs_doc_get_brief(world, bar), NULL); + test_str(ecs_doc_get_brief(world, baz), "Another description"); + + ecs_fini(world); +} + +void Eval_name_annotation(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "@name A name" + LINE "Foo {}" + LINE "Bar {}" + LINE "" + LINE "@name Another name" + LINE "Baz {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t baz = ecs_lookup(world, "Baz"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(baz != 0); + + test_str(ecs_doc_get_name(world, foo), "A name"); + test_str(ecs_doc_get_name(world, bar), "Bar"); + test_str(ecs_doc_get_name(world, baz), "Another name"); + + ecs_fini(world); +} + +void Eval_link_annotation(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "@link A link" + LINE "Foo {}" + LINE "Bar {}" + LINE "" + LINE "@link Another link" + LINE "Baz {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t baz = ecs_lookup(world, "Baz"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(baz != 0); + + test_str(ecs_doc_get_link(world, foo), "A link"); + test_str(ecs_doc_get_link(world, bar), NULL); + test_str(ecs_doc_get_link(world, baz), "Another link"); + + ecs_fini(world); +} + +void Eval_color_annotation(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "@color #554433" + LINE "Foo {}" + LINE "Bar {}" + LINE "" + LINE "@color rgb(10, 20, 30, 1.0)" + LINE "Baz {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t baz = ecs_lookup(world, "Baz"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(baz != 0); + + test_str(ecs_doc_get_color(world, foo), "#554433"); + test_str(ecs_doc_get_color(world, bar), NULL); + test_str(ecs_doc_get_color(world, baz), "rgb(10, 20, 30, 1.0)"); + + ecs_fini(world); +} + +void Eval_multiple_annotations(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "@brief A description" + LINE "@name A name" + LINE "@link A link" + LINE "@color #554433" + LINE "Foo {}" + LINE "Bar {}" + LINE "" + LINE "@brief Another description" + LINE "@name Another name" + LINE "Baz {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t baz = ecs_lookup(world, "Baz"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(baz != 0); + + test_str(ecs_doc_get_brief(world, foo), "A description"); + test_str(ecs_doc_get_name(world, foo), "A name"); + test_str(ecs_doc_get_link(world, foo), "A link"); + test_str(ecs_doc_get_color(world, foo), "#554433"); + test_str(ecs_doc_get_brief(world, bar), NULL); + test_str(ecs_doc_get_brief(world, baz), "Another description"); + test_str(ecs_doc_get_name(world, baz), "Another name"); + + ecs_fini(world); +} + +void Eval_annotation_w_trailing_space(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "@brief A description " + LINE "Foo {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + test_str(ecs_doc_get_brief(world, foo), "A description"); + + ecs_fini(world); +} + +typedef struct String { + char *value; +} String; + +void Eval_multiline_string(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(String) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "String"}), + .members = { + {"value", ecs_id(ecs_string_t)} + } + }); + + const char *expr = + HEAD "Foo { String: {value: `start" + LINE "Hello World" + LINE "Foo Bar" + LINE "Special characters }{\"\"''," + LINE "`}}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + test_assert(ecs_has(world, foo, String)); + + const String *ptr = ecs_get(world, foo, String); + test_assert(ptr != NULL); + test_assert(ptr->value != NULL); + test_str(ptr->value, + "start\n" + "Hello World\n" + "Foo Bar\n" + "Special characters }{\"\"'',\n" + ); + + ecs_fini(world); +} + +void Eval_annotate_declaration(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + const char *expr = + HEAD "@brief A brief description" + LINE "Foo Bar"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(bar != 0); + + test_assert(ecs_has_id(world, bar, foo)); + + test_assert(ecs_doc_get_brief(world, foo) == NULL); + test_assert(ecs_doc_get_brief(world, bar) != NULL); + test_str(ecs_doc_get_brief(world, bar), "A brief description"); + + ecs_fini(world); +} + +void Eval_declaration_w_underscore_name(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + const char *expr = + HEAD "Foo _Bar"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + ecs_entity_t bar = ecs_lookup(world, "_Bar"); + test_assert(bar != 0); + + test_assert(!ecs_has_id(world, foo, bar)); + test_assert(ecs_has_id(world, bar, foo)); + + ecs_fini(world); +} + +void Eval_anonymous_entity(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + const char *expr = + HEAD "_ { Foo }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + ecs_iter_t it = ecs_each_id(world, foo ); + test_assert(ecs_each_next(&it)); + test_int(it.count, 1); + ecs_entity_t e = it.entities[0]; + test_assert(e != 0); + test_str(ecs_get_name(world, e), NULL); + test_assert(!ecs_each_next(&it)); + + ecs_fini(world); +} + +void Eval_anonymous_entity_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + const char *expr = + HEAD "Parent {" + LINE " _ { Foo }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + ecs_entity_t parent = ecs_lookup(world, "Parent"); + test_assert(parent != 0); + + ecs_iter_t it = ecs_each_id(world, foo ); + test_assert(ecs_each_next(&it)); + test_int(it.count, 1); + ecs_entity_t e = it.entities[0]; + test_assert(e != 0); + test_str(ecs_get_name(world, e), NULL); + test_assert(ecs_has_pair(world, e, EcsChildOf, parent)); + test_assert(!ecs_each_next(&it)); + + ecs_fini(world); +} + +void Eval_anonymous_declaration(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + const char *expr = + HEAD "Foo _"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + ecs_iter_t it = ecs_each_id(world, foo ); + test_assert(ecs_each_next(&it)); + test_int(it.count, 1); + ecs_entity_t e = it.entities[0]; + test_assert(e != 0); + test_str(ecs_get_name(world, e), NULL); + test_assert(!ecs_each_next(&it)); + + ecs_fini(world); +} + +void Eval_const_var_int(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const var_x = 10" + LINE "const var_y = 20" + LINE "" + LINE "e { Position: {$var_x, $var_y} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_const_var_float(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const var_x = 10.5" + LINE "const var_y = 20.5" + LINE "" + LINE "e { Position: {$var_x, $var_y} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_flt(p->x, 10.5); + test_flt(p->y, 20.5); + + ecs_fini(world); +} + +void Eval_const_var_bool(void) { + ecs_world_t *world = ecs_init(); + + typedef struct Bools { + bool x; + bool y; + } Bools; + + ecs_entity_t ecs_id(Bools) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Bools"}), + .members = { + {"x", ecs_id(ecs_bool_t)}, + {"y", ecs_id(ecs_bool_t)} + } + }); + + const char *expr = + HEAD "const var_x = true" + LINE "const var_y = false" + LINE "" + LINE "e { Bools: {$var_x, $var_y} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has(world, e, Bools)); + + const Bools *p = ecs_get(world, e, Bools); + test_assert(p != NULL); + test_bool(p->x, true); + test_bool(p->y, false); + + ecs_fini(world); +} + +void Eval_const_var_string(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const var_x = \"10.5\"" + LINE "const var_y = \"20.5\"" + LINE "" + LINE "e { Position: {$var_x, $var_y} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_flt(p->x, 10.5); + test_flt(p->y, 20.5); + + ecs_fini(world); +} + +void Eval_const_var_struct(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Point) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Point"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t ecs_id(Line) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Line"}), + .members = { + {"start", ecs_id(Point)}, + {"stop", ecs_id(Point)} + } + }); + + const char *expr = + HEAD "const var_start = Point: {10.5, 20.5}" + LINE "const var_stop = Point: {30.5, 40.5}" + LINE "" + LINE "e { Line: {start: $var_start, stop: $var_stop} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has(world, e, Line)); + + const Line *l = ecs_get(world, e, Line); + test_assert(l != NULL); + test_flt(l->start.x, 10.5); + test_flt(l->start.y, 20.5); + test_flt(l->stop.x, 30.5); + test_flt(l->stop.y, 40.5); + + ecs_fini(world); +} + +void Eval_const_var_scoped(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const var_x = 10" + LINE "a { Position: {$var_x, $var_x} }" + LINE "a {" + LINE " const var_x = 20" + LINE " const var_y = 30" + LINE " b { Position: {$var_x, $var_y} }" + LINE "}" + LINE "a {" + LINE " const var_y = 20" + LINE " c { Position: {$var_x, $var_y} }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t a = ecs_lookup(world, "a"); + test_assert(a != 0); + ecs_entity_t b = ecs_lookup(world, "a.b"); + test_assert(b != 0); + ecs_entity_t c = ecs_lookup(world, "a.c"); + test_assert(c != 0); + + test_assert(ecs_has(world, a, Position)); + test_assert(ecs_has(world, b, Position)); + test_assert(ecs_has(world, c, Position)); + const Position *p; + + p = ecs_get(world, a, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 10); + + p = ecs_get(world, b, Position); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + + p = ecs_get(world, c, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_assign_component_from_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const var_pos = Position: {10, 20}" + LINE "a { $var_pos }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t var = ecs_lookup(world, "var_pos"); + test_assert(var == 0); + + ecs_entity_t a = ecs_lookup(world, "a"); + test_assert(a != 0); + + const Position *p = ecs_get(world, a, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_assign_component_from_var_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const var_pos = Position: {10, 20}" + LINE "a {" + LINE " $var_pos" + LINE "}" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t var = ecs_lookup(world, "var_pos"); + test_assert(var == 0); + + ecs_entity_t a = ecs_lookup(world, "a"); + test_assert(a != 0); + + const Position *p = ecs_get(world, a, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_scope_w_component_after_const_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo {" + LINE " const var = 5" + LINE " Position: {x: 10, y: $var}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + test_assert(ecs_has(world, foo, Position)); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 5); + + ecs_fini(world); +} + +void Eval_component_after_const_add_expr(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo {" + LINE " const var = 5 + 15" + LINE " Position: {x: 10, y: $var}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + test_assert(ecs_has(world, foo, Position)); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_component_after_const_sub_expr(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo {" + LINE " const var = 25 - 5" + LINE " Position: {x: 10, y: $var}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + test_assert(ecs_has(world, foo, Position)); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_component_after_const_mul_expr(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo {" + LINE " const var = 2 * 10" + LINE " Position: {x: 10, y: $var}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + test_assert(ecs_has(world, foo, Position)); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_component_after_const_div_expr(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo {" + LINE " const var = 40 / 2" + LINE " Position: {x: 10, y: $var}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + test_assert(ecs_has(world, foo, Position)); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_component_after_const_paren_expr(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "e {" + LINE " const val = (10 + 20)" + LINE " Position: {$val, $val * 2}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 60); + + ecs_fini(world); +} + +void Eval_parse_with(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Foo {}" + LINE "Bar {" + LINE " Hello {}" + LINE "}"; + + ECS_TAG(world, Tag); + + ecs_set_with(world, Tag); + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_set_with(world, 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t hello = ecs_lookup(world, "Bar.Hello"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(hello != 0); + + test_assert(ecs_has(world, foo, Tag)); + test_assert(ecs_has(world, bar, Tag)); + test_assert(ecs_has(world, hello, Tag)); + + ecs_fini(world); +} + +void Eval_parse_with_w_with(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + + const char *expr = + HEAD "Foo {}" + LINE "with TagB {" + LINE " Bar {" + LINE " Hello {}" + LINE " }" + LINE "}"; + + ecs_set_with(world, TagA); + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_set_with(world, 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t hello = ecs_lookup(world, "Bar.Hello"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(hello != 0); + + test_assert(ecs_has(world, foo, TagA)); + test_assert(ecs_has(world, bar, TagA)); + test_assert(ecs_has(world, hello, TagA)); + + test_assert(!ecs_has(world, TagB, TagA)); + test_assert(!ecs_has(world, foo, TagB)); + test_assert(ecs_has(world, bar, TagB)); + test_assert(ecs_has(world, hello, TagB)); + + ecs_fini(world); +} + +void Eval_parse_with_w_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, TagA); + ECS_TAG(world, TagB); + ECS_TAG(world, Hello); + + const char *expr = + HEAD "Foo {}" + LINE "with TagB {" + LINE " Bar {" + LINE " Hello" + LINE " }" + LINE "}"; + + ecs_set_with(world, TagA); + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_set_with(world, 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t hello = ecs_lookup(world, "Hello"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(hello != 0); + + test_assert(ecs_has(world, foo, TagA)); + test_assert(ecs_has(world, bar, TagA)); + test_assert(!ecs_has(world, hello, TagA)); + test_assert(ecs_has_id(world, bar, hello)); + + test_assert(!ecs_has(world, TagB, TagA)); + test_assert(!ecs_has(world, foo, TagB)); + test_assert(ecs_has(world, bar, TagB)); + test_assert(!ecs_has(world, hello, TagB)); + + ecs_fini(world); +} + +void Eval_parse_with_value(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "with Position(10, 20) {" + LINE " Foo {}" + LINE " Bar {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + { + const Position *ptr = ecs_get(world, bar, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + +void Eval_parse_with_2_values(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "with Position(10, 20), Velocity(1, 2) {" + LINE " Foo {}" + LINE " Bar {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + { + const Position *ptr = ecs_get(world, bar, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + { + const Velocity *ptr = ecs_get(world, foo, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + { + const Velocity *ptr = ecs_get(world, bar, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + + ecs_fini(world); +} + +void Eval_parse_with_2_nested_values(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "with Position(10, 20) {" + LINE " with Velocity(1, 2) {" + LINE " Foo {}" + LINE " Bar {}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + { + const Position *ptr = ecs_get(world, bar, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + { + const Velocity *ptr = ecs_get(world, foo, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + { + const Velocity *ptr = ecs_get(world, bar, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + + ecs_fini(world); +} + +void Eval_parse_with_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "const pos = Position: {10, 20}" + LINE "with $pos {" + LINE " Foo {}" + LINE " Bar {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + { + const Position *ptr = ecs_get(world, bar, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + +void Eval_parse_with_2_vars(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "const pos = Position: {10, 20}" + LINE "const vel = Velocity: {1, 2}" + LINE "with $pos, $vel {" + LINE " Foo {}" + LINE " Bar {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + { + const Position *ptr = ecs_get(world, bar, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + { + const Velocity *ptr = ecs_get(world, foo, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + { + const Velocity *ptr = ecs_get(world, bar, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + + ecs_fini(world); +} + +void Eval_parse_with_2_nested_vars(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "const pos = Position: {10, 20}" + LINE "const vel = Velocity: {1, 2}" + LINE "with $pos, $vel {" + LINE " with $vel {" + LINE " Foo {}" + LINE " Bar {}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + + test_assert(foo != 0); + test_assert(bar != 0); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + { + const Position *ptr = ecs_get(world, bar, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + { + const Velocity *ptr = ecs_get(world, foo, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + { + const Velocity *ptr = ecs_get(world, bar, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + + ecs_fini(world); +} + +void Eval_parse_with_var_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "Parent {" + LINE " const pos = Position: {10, 20}" + LINE " with $pos { " + LINE " Foo {}" + LINE " Bar {}" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t foo = ecs_lookup(world, "Parent.Foo"); + ecs_entity_t bar = ecs_lookup(world, "Parent.Bar"); + + test_assert(parent != 0); + test_assert(foo != 0); + test_assert(bar != 0); + + { + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + { + const Position *ptr = ecs_get(world, bar, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + + +void Eval_assign_const_w_expr(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "const var = 5 + 1" + LINE "e { Position: {x: $var, y: $var * 2} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 6); + test_int(p->y, 12); + + ecs_fini(world); +} + +void Eval_const_w_type(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "const var = flecs.meta.i32: 5 / 2" + LINE "e { Position: {x: $var, y: $var * 3} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has(world, e, Position)); + + const Position *p = ecs_get(world, e, Position); + test_assert(p != NULL); + test_int(p->x, 2); + test_int(p->y, 6); + + ecs_fini(world); +} + +void Eval_typed_const_w_composite_type(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const var_pos = Position: {10, 20}" + LINE "a { $var_pos }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t var = ecs_lookup(world, "var_pos"); + test_assert(var == 0); + + ecs_entity_t a = ecs_lookup(world, "a"); + test_assert(a != 0); + + const Position *p = ecs_get(world, a, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_assign_var_to_typed_const_w_composite_type(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "const var_pos_a = Position: {10, 20}" + HEAD "const var_pos_b = $var_pos_a" + LINE "a { $var_pos_b }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t var = ecs_lookup(world, "var_pos"); + test_assert(var == 0); + + ecs_entity_t a = ecs_lookup(world, "a"); + test_assert(a != 0); + + const Position *p = ecs_get(world, a, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_using_wildcard(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t p1 = ecs_entity(world, { .name = "foo.p1" }); + ecs_entity_t p2 = ecs_entity(world, { .name = "foo.p2" }); + + ecs_set_scope(world, p1); + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_set_scope(world, p2); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_set_scope(world, 0); + + const char *expr = + LINE "using foo.*\n" + LINE "e { Position: {10, 20} }\n" + LINE "e { Velocity: {1, 2} }\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t pos = ecs_lookup(world, "foo.p1.Position"); + test_assert(pos == ecs_id(Position)); + ecs_entity_t vel = ecs_lookup(world, "foo.p2.Velocity"); + test_assert(vel == ecs_id(Velocity)); + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has(world, e, Velocity)); + + { + const Position *ptr = ecs_get(world, e, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + { + const Velocity *ptr = ecs_get(world, e, Velocity); + test_assert(ptr != NULL); + test_int(ptr->x, 1); + test_int(ptr->y, 2); + } + + ecs_fini(world); +} + +void Eval_single_line_comment_in_value(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "e {Position: {\n" + LINE " x: 10\n" + LINE " //foo\n" + LINE " y: 20\n" + LINE "}}"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_assert(ecs_has(world, e, Position)); + + const Position *ptr = ecs_get(world, e, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_single_line_comment_in_value_after_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "e {Position:{\n" + LINE " // foo\n" + LINE " x: 10\n" + LINE " y: 20\n" + LINE "}}"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_assert(ecs_has(world, e, Position)); + + const Position *ptr = ecs_get(world, e, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_multi_line_comment_in_value(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "e {Position: {\n" + LINE " x: 10\n" + LINE " /*\n" + LINE " * foo\n" + LINE " */\n" + LINE " y: 20\n" + LINE "}}"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_assert(ecs_has(world, e, Position)); + + const Position *ptr = ecs_get(world, e, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_multi_line_comment_in_value_after_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "e {Position: {\n" + LINE " /*\n" + LINE " * foo\n" + LINE " */\n" + LINE " x: 10\n" + LINE " y: 20\n" + LINE "}}"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_assert(ecs_has(world, e, Position)); + + const Position *ptr = ecs_get(world, e, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + + ecs_fini(world); +} + +void Eval_module_stmt(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "module hello\n" + LINE "Foo {}\n" + LINE "e { Position: {10, 20} }\n" + LINE "e { Foo }\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_get_scope(world) == 0); + + ecs_entity_t hello = ecs_lookup(world, "hello"); + test_assert(hello != 0); + test_assert(ecs_has_id(world, hello, EcsModule)); + + ecs_entity_t e = ecs_lookup(world, "hello.e"); + test_assert(e != 0); + ecs_entity_t foo = ecs_lookup(world, "hello.Foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has_id(world, e, foo)); + + { + const Position *ptr = ecs_get(world, e, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + +void Eval_nested_module_stmt(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "module hello.world\n" + LINE "Foo {}\n" + LINE "e { Position: {10, 20} }\n" + LINE "e { Foo }\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t hello = ecs_lookup(world, "hello"); + test_assert(hello != 0); + test_assert(ecs_has_id(world, hello, EcsModule)); + ecs_entity_t hello_world = ecs_lookup(world, "hello.world"); + test_assert(hello_world != 0); + test_assert(ecs_has_id(world, hello_world, EcsModule)); + + ecs_entity_t e = ecs_lookup(world, "hello.world.e"); + test_assert(e != 0); + ecs_entity_t foo = ecs_lookup(world, "hello.world.Foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has_id(world, e, foo)); + + { + const Position *ptr = ecs_get(world, e, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + +void Eval_module_stmt_w_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "module hello.world\n" + LINE "Foo {}\n" + LINE "parent {\n" + LINE " e { Position: {10, 20} }\n" + LINE " e { Foo }\n" + LINE "}\n" + LINE "f { Position: {30, 40} }\n" + LINE "f { Foo }\n" + LINE ""; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t hello = ecs_lookup(world, "hello"); + test_assert(hello != 0); + test_assert(ecs_has_id(world, hello, EcsModule)); + ecs_entity_t hello_world = ecs_lookup(world, "hello.world"); + test_assert(hello_world != 0); + test_assert(ecs_has_id(world, hello_world, EcsModule)); + + ecs_entity_t parent = ecs_lookup(world, "hello.world.parent"); + test_assert(parent != 0); + ecs_entity_t e = ecs_lookup(world, "hello.world.parent.e"); + test_assert(e != 0); + ecs_entity_t f = ecs_lookup(world, "hello.world.f"); + test_assert(f != 0); + ecs_entity_t foo = ecs_lookup(world, "hello.world.Foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has_id(world, e, foo)); + test_assert(ecs_has(world, f, Position)); + test_assert(ecs_has_id(world, f, foo)); + + { + const Position *ptr = ecs_get(world, e, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + { + const Position *ptr = ecs_get(world, f, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 30); + test_int(ptr->y, 40); + } + + ecs_fini(world); +} + +void Eval_module_stmt_w_nested_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "module hello.world\n" + LINE "Foo {}\n" + LINE "parent {\n" + LINE " child {\n" + LINE " e { Position: {10, 20} }\n" + LINE " e { Foo }\n" + LINE " }\n" + LINE "}\n" + LINE "f { Position: {30, 40} }\n" + LINE "f { Foo }\n" + LINE ""; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t hello = ecs_lookup(world, "hello"); + test_assert(hello != 0); + test_assert(ecs_has_id(world, hello, EcsModule)); + ecs_entity_t hello_world = ecs_lookup(world, "hello.world"); + test_assert(hello_world != 0); + test_assert(ecs_has_id(world, hello_world, EcsModule)); + + ecs_entity_t parent = ecs_lookup(world, "hello.world.parent"); + test_assert(parent != 0); + ecs_entity_t child = ecs_lookup(world, "hello.world.parent.child"); + test_assert(child != 0); + ecs_entity_t e = ecs_lookup(world, "hello.world.parent.child.e"); + test_assert(e != 0); + ecs_entity_t f = ecs_lookup(world, "hello.world.f"); + test_assert(f != 0); + ecs_entity_t foo = ecs_lookup(world, "hello.world.Foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, e, Position)); + test_assert(ecs_has_id(world, e, foo)); + test_assert(ecs_has(world, f, Position)); + test_assert(ecs_has_id(world, f, foo)); + + { + const Position *ptr = ecs_get(world, e, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + { + const Position *ptr = ecs_get(world, f, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 30); + test_int(ptr->y, 40); + } + + ecs_fini(world); +} + +void Eval_assign_singleton_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + const char *expr = + LINE "$ { Foo }\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + test_assert(ecs_has_id(world, foo, foo)); + + ecs_fini(world); +} + +void Eval_assign_singleton_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "$ = Position: {10, 20}\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_has(world, ecs_id(Position), Position)); + + const Position *p = ecs_get(world, ecs_id(Position), Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_assign_singleton_tag_w_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + const char *expr = + LINE "$ {\n" + LINE " Foo\n" + LINE "}\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + test_assert(ecs_has_id(world, foo, foo)); + + ecs_fini(world); +} + +void Eval_assign_singleton_2_tags_w_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + const char *expr = + LINE "$ {\n" + LINE " Foo\n" + LINE " Bar\n" + LINE "}\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(bar != 0); + + test_assert(ecs_has_id(world, foo, foo)); + test_assert(ecs_has_id(world, bar, bar)); + + ecs_fini(world); +} + +void Eval_assign_singleton_component_w_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "$ {\n" + LINE " Position: {10, 20}\n" + LINE "}\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_has(world, ecs_id(Position), Position)); + + const Position *p = ecs_get(world, ecs_id(Position), Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_assign_singleton_2_components_w_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "$ {\n" + LINE " Position: {10, 20}\n" + LINE " Velocity: {1, 2}\n" + LINE "}\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + const Position *p = ecs_get(world, ecs_id(Position), Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = ecs_get(world, ecs_id(Velocity), Velocity); + test_assert(v != NULL); + test_int(v->x, 1); + test_int(v->y, 2); + + ecs_fini(world); +} + +void Eval_with_pair_in_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "Tgt {}\n" + LINE "Rel {}\n" + LINE "\n" + LINE "Parent {\n" + LINE " with (Rel, Tgt) {\n" + LINE " Child {}\n" + LINE " }\n" + LINE "}\n"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + ecs_entity_t rel = ecs_lookup(world, "Rel"); + ecs_entity_t tgt = ecs_lookup(world, "Tgt"); + + test_assert(!ecs_lookup(world, "Parent.Rel")); + test_assert(!ecs_lookup(world, "Parent.Tgt")); + + test_assert(parent != 0); + test_assert(child != 0); + test_assert(rel != 0); + test_assert(tgt != 0); + + test_assert(ecs_has_pair(world, child, rel, tgt)); + + ecs_fini(world); +} + +void Eval_with_pair_component_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, { .name = "Position" }), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "Tgt {}\n" + LINE "\n" + LINE "Parent {\n" + LINE " with (Position, Tgt)(10, 20) {\n" + LINE " Child {}\n" + LINE " }\n" + LINE "}\n"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t parent = ecs_lookup(world, "Parent"); + ecs_entity_t child = ecs_lookup(world, "Parent.Child"); + ecs_entity_t tgt = ecs_lookup(world, "Tgt"); + + test_assert(!ecs_lookup(world, "Parent.Rel")); + test_assert(!ecs_lookup(world, "Parent.Tgt")); + + test_assert(parent != 0); + test_assert(child != 0); + test_assert(tgt != 0); + + test_assert(ecs_has_pair(world, child, ecs_id(Position), tgt)); + + const Position *pos = ecs_get_pair(world, child, Position, tgt); + test_assert(pos != NULL); + test_int(pos->x, 10); + test_int(pos->y, 20); + + ecs_fini(world); +} + +void Eval_pair_w_rel_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + const char *expr = + LINE "const rel = Rel\n" + LINE "ent {\n" + LINE " ($rel, Tgt)\n" + LINE "}\n" + LINE "\n"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t ent = ecs_lookup(world, "ent"); + + test_assert(ecs_has_pair(world, ent, Rel, Tgt)); + + ecs_fini(world); +} + +void Eval_pair_w_tgt_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + const char *expr = + LINE "const tgt = Tgt\n" + LINE "ent {\n" + LINE " (Rel, $tgt)\n" + LINE "}\n" + LINE "\n"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t ent = ecs_lookup(world, "ent"); + + test_assert(ecs_has_pair(world, ent, Rel, Tgt)); + + ecs_fini(world); +} + +void Eval_component_in_with_scope_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_TAG(world, Bar); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "foo {\n" + LINE " Position: {10, 20}\n" + LINE " with Position(30, 40) {\n" + LINE " Bar\n" + LINE " }\n" + LINE "}\n"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(bar != 0); + + test_assert(ecs_has(world, foo, Position)); + test_assert(ecs_has_id(world, foo, bar)); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_array_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_array(world, { + .entity = ecs_id(Position), + .type = ecs_id(ecs_f32_t), + .count = 2 + }); + + const char *expr = + LINE "foo { Position: [10, 20] }\n"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +ECS_CTOR(Position, ptr, { + ptr->x = 100; + ptr->y = 200; +}) + +static int on_set_position_invoked = 0; + +static void on_set_position(ecs_iter_t *it) { + on_set_position_invoked ++; + + Position *p = ecs_field(it, Position, 0); + test_assert(p != NULL); +} + +void Eval_on_set_w_kind_paren_no_reflection(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_set = on_set_position + }); + + const char *expr = + HEAD "Position e()"; + + test_int(on_set_position_invoked, 0); + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_int(on_set_position_invoked, 1); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 100); + test_int(p->y, 200); + + ecs_fini(world); +} + +void Eval_on_set_w_kind_paren(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_set = on_set_position + }); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Position e()"; + + test_int(on_set_position_invoked, 0); + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_int(on_set_position_invoked, 1); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 100); + test_int(p->y, 200); + + ecs_fini(world); +} + +void Eval_on_set_w_kind_no_paren(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_set = on_set_position + }); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Position e()"; + + test_int(on_set_position_invoked, 0); + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_int(on_set_position_invoked, 1); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 100); + test_int(p->y, 200); + + ecs_fini(world); +} + +void Eval_on_set_w_kind_no_paren_no_reflection(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_set = on_set_position + }); + + const char *expr = + HEAD "Position e"; + + test_int(on_set_position_invoked, 0); + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_int(on_set_position_invoked, 1); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 100); + test_int(p->y, 200); + + ecs_fini(world); +} + +void Eval_on_set_w_single_assign(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_set = on_set_position + }); + + const char *expr = + HEAD "e = Position: {}"; + + test_int(on_set_position_invoked, 0); + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_int(on_set_position_invoked, 1); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 100); + test_int(p->y, 200); + + ecs_fini(world); +} + +void Eval_on_set_w_single_assign_scoped_w_value(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_set = on_set_position + }); + + const char *expr = + HEAD "e { Position: {} }"; + + test_int(on_set_position_invoked, 0); + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_int(on_set_position_invoked, 1); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 100); + test_int(p->y, 200); + + ecs_fini(world); +} + +void Eval_on_set_w_single_assign_scoped_no_value(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .ctor = ecs_ctor(Position), + .on_set = on_set_position + }); + + const char *expr = + HEAD "e { Position }"; + + test_int(on_set_position_invoked, 0); + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + test_int(on_set_position_invoked, 0); + + const Position *p = ecs_get(world, e, Position); + test_int(p->x, 100); + test_int(p->y, 200); + + ecs_fini(world); +} + +void Eval_if_true(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "if true {" + LINE " a{}" + LINE "} else {" + LINE " b{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") != 0); + test_assert(ecs_lookup(world, "b") == 0); + + ecs_fini(world); +} + +void Eval_if_true_no_else(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "if true {" + LINE " a{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") != 0); + + ecs_fini(world); +} + +void Eval_if_false(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "if false {" + LINE " a{}" + LINE "} else {" + LINE " b{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") == 0); + test_assert(ecs_lookup(world, "b") != 0); + + ecs_fini(world); +} + +void Eval_if_10(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "if 10 {" + LINE " a{}" + LINE "} else {" + LINE " b{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") != 0); + test_assert(ecs_lookup(world, "b") == 0); + + ecs_fini(world); +} + +void Eval_if_256(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "if 256 {" + LINE " a{}" + LINE "} else {" + LINE " b{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") != 0); + test_assert(ecs_lookup(world, "b") == 0); + + ecs_fini(world); +} + +void Eval_if_0(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "if 0 {" + LINE " a{}" + LINE "} else {" + LINE " b{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") == 0); + test_assert(ecs_lookup(world, "b") != 0); + + ecs_fini(world); +} + +void Eval_if_true_var(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "const v = true" + LINE "if $v {" + LINE " a{}" + LINE "} else {" + LINE " b{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") != 0); + test_assert(ecs_lookup(world, "b") == 0); + + ecs_fini(world); +} + +void Eval_if_false_var(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "const v = false" + LINE "if $v {" + LINE " a{}" + LINE "} else {" + LINE " b{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") == 0); + test_assert(ecs_lookup(world, "b") != 0); + + ecs_fini(world); +} + +void Eval_if_true_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + const char *expr = + HEAD "e {" + LINE " if true {" + LINE " Foo" + LINE " } else {" + LINE " Bar" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has_id(world, e, Foo)); + + ecs_fini(world); +} + +void Eval_if_false_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + ECS_TAG(world, Bar); + + const char *expr = + HEAD "e {" + LINE " if false {" + LINE " Foo" + LINE " } else {" + LINE " Bar" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + test_assert(ecs_has_id(world, e, Bar)); + + ecs_fini(world); +} + +void Eval_if_lt(void) { + ecs_world_t *world = ecs_init(); + + { + const char *expr = + HEAD "if 2.0 > 3.0 {" + LINE " a{}" + LINE "} else {" + LINE " b{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") == 0); + test_assert(ecs_lookup(world, "b") != 0); + } + + { + const char *expr = + HEAD "if 3.0 > 2.0 {" + LINE " c{}" + LINE "} else {" + LINE " d{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "c") != 0); + test_assert(ecs_lookup(world, "d") == 0); + } + + ecs_fini(world); +} + +void Eval_if_lt_const(void) { + ecs_world_t *world = ecs_init(); + + { + const char *expr = + HEAD "const v = 2.0" + LINE "if $v > 3.0 {" + LINE " a{}" + LINE "} else {" + LINE " b{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "a") == 0); + test_assert(ecs_lookup(world, "b") != 0); + } + + { + const char *expr = + HEAD "const v = 3.0" + LINE "if $v > 2.0 {" + LINE " c{}" + LINE "} else {" + LINE " d{}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + test_assert(ecs_lookup(world, "c") != 0); + test_assert(ecs_lookup(world, "d") == 0); + } + + ecs_fini(world); +} + +void Eval_isa_in_module(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "module things" + LINE "Animal {}" + LINE "Dog {" + LINE " (IsA, Animal)" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup_child(world, 0, "IsA") == 0); + test_assert(ecs_lookup(world, "things.IsA") == 0); + + ecs_entity_t animal = ecs_lookup(world, "things.Animal"); + ecs_entity_t dog = ecs_lookup(world, "things.Dog"); + + test_assert(animal != 0); + test_assert(dog != 0); + + test_assert(ecs_has_pair(world, dog, EcsIsA, animal)); + + ecs_fini(world); +} + +void Eval_isa_hierarchy(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "(IsA, Thing) {" + LINE " (IsA, Organism) {" + LINE " (IsA, Animal) {" + LINE " Dog {}" + LINE " Cat {}" + LINE " }" + LINE " (IsA, Tree) {" + LINE " Oak {}" + LINE " Maple {}" + LINE " }" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup_child(world, 0, "IsA") == 0); + + ecs_entity_t thing = ecs_lookup(world, "Thing"); + ecs_entity_t organism = ecs_lookup(world, "Organism"); + ecs_entity_t animal = ecs_lookup(world, "Animal"); + ecs_entity_t dog = ecs_lookup(world, "Dog"); + ecs_entity_t cat = ecs_lookup(world, "Cat"); + ecs_entity_t tree = ecs_lookup(world, "Tree"); + ecs_entity_t oak = ecs_lookup(world, "Oak"); + ecs_entity_t maple = ecs_lookup(world, "Maple"); + + test_assert(thing != 0); + test_assert(organism != 0); + test_assert(animal != 0); + test_assert(dog != 0); + test_assert(cat != 0); + test_assert(tree != 0); + test_assert(oak != 0); + test_assert(maple != 0); + + test_assert(ecs_has_pair(world, organism, EcsIsA, thing)); + test_assert(ecs_has_pair(world, animal, EcsIsA, organism)); + test_assert(ecs_has_pair(world, dog, EcsIsA, animal)); + test_assert(ecs_has_pair(world, cat, EcsIsA, animal)); + test_assert(ecs_has_pair(world, tree, EcsIsA, organism)); + test_assert(ecs_has_pair(world, oak, EcsIsA, tree)); + test_assert(ecs_has_pair(world, maple, EcsIsA, tree)); + + ecs_fini(world); +} + +void Eval_isa_hierarchy_in_module(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "module things" + LINE "(IsA, Thing) {" + LINE " (IsA, Organism) {" + LINE " (IsA, Animal) {" + LINE " Dog {}" + LINE " Cat {}" + LINE " }" + LINE " (IsA, Tree) {" + LINE " Oak {}" + LINE " Maple {}" + LINE " }" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup_child(world, 0, "IsA") == 0); + test_assert(ecs_lookup(world, "things.IsA") == 0); + + ecs_entity_t thing = ecs_lookup(world, "things.Thing"); + ecs_entity_t organism = ecs_lookup(world, "things.Organism"); + ecs_entity_t animal = ecs_lookup(world, "things.Animal"); + ecs_entity_t dog = ecs_lookup(world, "things.Dog"); + ecs_entity_t cat = ecs_lookup(world, "things.Cat"); + ecs_entity_t tree = ecs_lookup(world, "things.Tree"); + ecs_entity_t oak = ecs_lookup(world, "things.Oak"); + ecs_entity_t maple = ecs_lookup(world, "things.Maple"); + + test_assert(thing != 0); + test_assert(organism != 0); + test_assert(animal != 0); + test_assert(dog != 0); + test_assert(cat != 0); + test_assert(tree != 0); + test_assert(oak != 0); + test_assert(maple != 0); + + test_assert(ecs_has_pair(world, organism, EcsIsA, thing)); + test_assert(ecs_has_pair(world, animal, EcsIsA, organism)); + test_assert(ecs_has_pair(world, dog, EcsIsA, animal)); + test_assert(ecs_has_pair(world, cat, EcsIsA, animal)); + test_assert(ecs_has_pair(world, tree, EcsIsA, organism)); + test_assert(ecs_has_pair(world, oak, EcsIsA, tree)); + test_assert(ecs_has_pair(world, maple, EcsIsA, tree)); + + ecs_fini(world); +} + +void Eval_custom_isa_hierarchy_in_module(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "module things" + LINE "IsA {}" + LINE "(IsA, Thing) {" + LINE " (IsA, Organism) {" + LINE " (IsA, Animal) {" + LINE " Dog {}" + LINE " Cat {}" + LINE " }" + LINE " (IsA, Tree) {" + LINE " Oak {}" + LINE " Maple {}" + LINE " }" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup_child(world, 0, "IsA") == 0); + + ecs_entity_t isa = ecs_lookup(world, "things.IsA"); + ecs_entity_t thing = ecs_lookup(world, "things.Thing"); + ecs_entity_t organism = ecs_lookup(world, "things.Organism"); + ecs_entity_t animal = ecs_lookup(world, "things.Animal"); + ecs_entity_t dog = ecs_lookup(world, "things.Dog"); + ecs_entity_t cat = ecs_lookup(world, "things.Cat"); + ecs_entity_t tree = ecs_lookup(world, "things.Tree"); + ecs_entity_t oak = ecs_lookup(world, "things.Oak"); + ecs_entity_t maple = ecs_lookup(world, "things.Maple"); + + test_assert(isa != 0); + test_assert(thing != 0); + test_assert(organism != 0); + test_assert(animal != 0); + test_assert(dog != 0); + test_assert(cat != 0); + test_assert(tree != 0); + test_assert(oak != 0); + test_assert(maple != 0); + + test_assert(ecs_has_pair(world, organism, isa, thing)); + test_assert(ecs_has_pair(world, animal, isa, organism)); + test_assert(ecs_has_pair(world, dog, isa, animal)); + test_assert(ecs_has_pair(world, cat, isa, animal)); + test_assert(ecs_has_pair(world, tree, isa, organism)); + test_assert(ecs_has_pair(world, oak, isa, tree)); + test_assert(ecs_has_pair(world, maple, isa, tree)); + + ecs_fini(world); +} + +void Eval_custom_isa_hierarchy_in_subtree(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "module things" + LINE "(IsA, Thing) {" + LINE " IsA {}" + LINE " (IsA, Organism) {" + LINE " (IsA, Animal) {" + LINE " Dog {}" + LINE " Cat {}" + LINE " }" + LINE " (IsA, Tree) {" + LINE " Oak {}" + LINE " Maple {}" + LINE " }" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup_child(world, 0, "IsA") == 0); + + ecs_entity_t isa = ecs_lookup(world, "things.IsA"); + ecs_entity_t thing = ecs_lookup(world, "things.Thing"); + ecs_entity_t organism = ecs_lookup(world, "things.Organism"); + ecs_entity_t animal = ecs_lookup(world, "things.Animal"); + ecs_entity_t dog = ecs_lookup(world, "things.Dog"); + ecs_entity_t cat = ecs_lookup(world, "things.Cat"); + ecs_entity_t tree = ecs_lookup(world, "things.Tree"); + ecs_entity_t oak = ecs_lookup(world, "things.Oak"); + ecs_entity_t maple = ecs_lookup(world, "things.Maple"); + + test_assert(isa != 0); + test_assert(thing != 0); + test_assert(organism != 0); + test_assert(animal != 0); + test_assert(dog != 0); + test_assert(cat != 0); + test_assert(tree != 0); + test_assert(oak != 0); + test_assert(maple != 0); + + test_assert(ecs_has_pair(world, isa, EcsIsA, thing)); + test_assert(ecs_has_pair(world, organism, EcsIsA, thing)); + test_assert(ecs_has_pair(world, animal, isa, organism)); + test_assert(ecs_has_pair(world, dog, isa, animal)); + test_assert(ecs_has_pair(world, cat, isa, animal)); + test_assert(ecs_has_pair(world, tree, isa, organism)); + test_assert(ecs_has_pair(world, oak, isa, tree)); + test_assert(ecs_has_pair(world, maple, isa, tree)); + + ecs_fini(world); +} + +void Eval_inherit_w_kind(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Prefab Foo" + LINE "Prefab Bar : Foo"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(foo != 0); + test_assert(bar != 0); + ecs_has_id(world, foo, EcsPrefab); + ecs_has_id(world, bar, EcsPrefab); + test_assert(ecs_has_pair(world, bar, EcsIsA, foo)); + + ecs_fini(world); +} + +void Eval_inherit_w_kind_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Prefab Foo" + LINE "Prefab Bar : Foo {" + LINE " Child {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t child = ecs_lookup(world, "Bar.Child"); + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(child != 0); + ecs_has_id(world, foo, EcsPrefab); + ecs_has_id(world, bar, EcsPrefab); + test_assert(ecs_has_pair(world, bar, EcsIsA, foo)); + + ecs_fini(world); +} + +void Eval_inherit_w_kind_value(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Prefab Foo" + LINE "Position Bar : Foo (10, 20)"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(foo != 0); + test_assert(bar != 0); + ecs_has_id(world, foo, EcsPrefab); + ecs_has(world, bar, Position); + test_assert(ecs_has_pair(world, bar, EcsIsA, foo)); + + const Position *p = ecs_get(world, bar, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_inherit_w_kind_value_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Prefab Foo" + LINE "Position Bar : Foo (10, 20) {" + LINE " Child {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t child = ecs_lookup(world, "Bar.Child"); + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(child != 0); + ecs_has_id(world, foo, EcsPrefab); + ecs_has(world, bar, Position); + test_assert(ecs_has_pair(world, bar, EcsIsA, foo)); + + const Position *p = ecs_get(world, bar, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_multiple_inherit_w_kind(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Prefab Hello" + LINE "Prefab World" + LINE "Prefab Foo : Hello, World"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t hello = ecs_lookup(world, "Hello"); + ecs_entity_t world_ = ecs_lookup(world, "World"); + test_assert(foo != 0); + test_assert(hello != 0); + test_assert(world_ != 0); + test_assert(world_ != EcsWorld); + ecs_has_id(world, hello, EcsPrefab); + ecs_has_id(world, world_, EcsPrefab); + test_assert(ecs_has_pair(world, foo, EcsIsA, hello)); + test_assert(ecs_has_pair(world, foo, EcsIsA, world_)); + + ecs_fini(world); +} + +void Eval_multiple_inherit_w_kind_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "Prefab Hello" + LINE "Prefab World" + LINE "Prefab Foo : Hello, World {" + LINE " Child {}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t hello = ecs_lookup(world, "Hello"); + ecs_entity_t world_ = ecs_lookup(world, "World"); + ecs_entity_t child = ecs_lookup(world, "Foo.Child"); + test_assert(foo != 0); + test_assert(child != 0); + test_assert(hello != 0); + test_assert(world_ != 0); + test_assert(world_ != EcsWorld); + ecs_has_id(world, hello, EcsPrefab); + ecs_has_id(world, world_, EcsPrefab); + test_assert(ecs_has_pair(world, foo, EcsIsA, hello)); + test_assert(ecs_has_pair(world, foo, EcsIsA, world_)); + + ecs_fini(world); +} + +void Eval_auto_override_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + const char *expr = + LINE "Prefab Foo {" + LINE " auto_override | Tag" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + ecs_has_id(world, foo, EcsPrefab); + test_assert(ecs_has_id(world, foo, ECS_AUTO_OVERRIDE | Tag)); + test_assert(!ecs_has(world, foo, Tag)); + + ecs_fini(world); +} + +void Eval_auto_override_component(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "Prefab Foo {" + LINE " auto_override | Position: {10, 20}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + ecs_has_id(world, foo, EcsPrefab); + test_assert(ecs_has_id(world, foo, ECS_AUTO_OVERRIDE | ecs_id(Position))); + test_assert(ecs_has(world, foo, Position)); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_auto_override_pair(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + ECS_TAG(world, Tgt); + + const char *expr = + LINE "Prefab Foo {" + LINE " auto_override | (Rel, Tgt)" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + ecs_has_id(world, foo, EcsPrefab); + test_assert(ecs_has_id(world, foo, ECS_AUTO_OVERRIDE | ecs_pair(Rel, Tgt))); + test_assert(!ecs_has_pair(world, foo, Rel, Tgt)); + + ecs_fini(world); +} + +void Eval_auto_override_pair_component(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ECS_TAG(world, Tgt); + + const char *expr = + LINE "Prefab Foo {" + LINE " auto_override | (Position, Tgt): {10, 20}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + ecs_has_id(world, foo, EcsPrefab); + test_assert(ecs_has_id(world, foo, ECS_AUTO_OVERRIDE | ecs_pair_t(Position, Tgt))); + test_assert(ecs_has_pair(world, foo, ecs_id(Position), Tgt)); + + const Position *p = ecs_get_pair(world, foo, Position, Tgt); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_lowercase_prefab_kind(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + const char *expr = + HEAD "prefab Foo {" + LINE " Tag" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "prefab") == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + test_assert(ecs_has_id(world, foo, EcsPrefab)); + test_assert(ecs_has_id(world, foo, Tag)); + + ecs_fini(world); +} + +void Eval_assign_component_to_const(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "const pos = e[Position]" + LINE "foo {" + LINE " Position: {$pos.y, $pos.x}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 20); + test_int(ptr->y, 10); + + ecs_fini(world); +} + +void Eval_assign_component_member_to_const(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "const px = e[Position].x" + LINE "const py = e[Position].y" + LINE "" + LINE "foo {" + LINE " Position: {$py, $px}" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + const Position *ptr = ecs_get(world, foo, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 20); + test_int(ptr->y, 10); + + ecs_fini(world); +} + +void Eval_prefab_w_slot(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "prefab Turret {" + LINE " slot Base {}" + LINE " slot Cannon {}" + LINE "}" + LINE "" + LINE "inst : Turret"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t turret = ecs_lookup(world, "Turret"); + test_assert(turret != 0); + test_assert(ecs_has_id(world, turret, EcsPrefab)); + + ecs_entity_t base = ecs_lookup(world, "Turret.Base"); + test_assert(base != 0); + test_assert(ecs_has_id(world, base, EcsPrefab)); + test_assert(ecs_has_pair(world, base, EcsSlotOf, turret)); + + ecs_entity_t cannon = ecs_lookup(world, "Turret.Cannon"); + test_assert(cannon != 0); + test_assert(ecs_has_id(world, cannon, EcsPrefab)); + test_assert(ecs_has_pair(world, cannon, EcsSlotOf, turret)); + + ecs_entity_t inst = ecs_lookup(world, "inst"); + test_assert(inst != 0); + test_assert(ecs_has_pair(world, inst, EcsIsA, turret)); + + ecs_entity_t inst_base = ecs_lookup(world, "inst.Base"); + ecs_entity_t inst_cannon = ecs_lookup(world, "inst.Cannon"); + test_assert(inst_base != 0); + test_assert(inst_cannon != 0); + + test_assert(ecs_has_pair(world, inst, base, inst_base)); + test_assert(ecs_has_pair(world, inst, cannon, inst_cannon)); + + ecs_fini(world); +} + +void Eval_prefab_w_slot_variant(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Foo); + + const char *expr = + HEAD "prefab Turret {" + LINE " slot Head {}" + LINE "}" + LINE "prefab Variant : Turret {" + LINE " prefab Head {" + LINE " Foo" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t head = ecs_lookup(world, "Turret.Head"); + ecs_entity_t variant = ecs_lookup(world, "Variant"); + test_assert(head != 0); + test_assert(variant != 0); + + ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, variant); + ecs_entity_t inst_head = ecs_get_target(world, inst, head, 0); + ecs_entity_t inst_head_lookup = ecs_lookup_child(world, inst, "Head"); + test_assert(inst_head != 0); + test_assert(inst_head_lookup != 0); + test_assert(inst_head == inst_head_lookup); + + ecs_fini(world); +} + +void Eval_const_w_component_expr(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "const pos = e[Position]" + LINE "foo {" + LINE " $pos" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_const_w_component_expr_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "parent {" + LINE " const pos = e[Position]" + LINE " foo {" + LINE " $pos" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t parent = ecs_lookup(world, "parent"); + test_assert(parent != 0); + + ecs_entity_t foo = ecs_lookup(world, "parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_const_w_component_expr_in_module(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "module parent" + LINE "const pos = e[Position]" + LINE "foo {" + LINE " $pos" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t parent = ecs_lookup(world, "parent"); + test_assert(parent != 0); + + ecs_entity_t foo = ecs_lookup(world, "parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_const_w_component_in_scope_expr_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + test_assert(parent != 0); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position", .parent = parent }), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "parent {" + LINE " const pos = e[Position]" + LINE " foo {" + LINE " $pos" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_const_w_component_in_scope_expr_in_module(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + test_assert(parent != 0); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position", .parent = parent }), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "module parent" + LINE "const pos = e[Position]" + LINE "foo {" + LINE " $pos" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_const_w_component_and_entity_in_scope_expr_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + test_assert(parent != 0); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position", .parent = parent }), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e", .parent = parent }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "parent {" + LINE " const pos = e[Position]" + LINE " foo {" + LINE " $pos" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_const_w_component_and_entity_in_scope_expr_in_module(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); + test_assert(parent != 0); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position", .parent = parent }), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e", .parent = parent }); + ecs_set(world, e, Position, {10, 20}); + + const char *expr = + HEAD "module parent" + LINE "const pos = e[Position]" + LINE "foo {" + LINE " $pos" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has(world, foo, Position)); + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_path_tag_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t tag = ecs_entity(world, { .name = "Hello.World" }); + test_assert(tag != 0); + + const char *expr = + HEAD "parent {" + LINE " foo {" + LINE " Hello.World" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "parent.Hello.World") == 0); + + ecs_entity_t foo = ecs_lookup(world, "parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has_id(world, foo, tag)); + + ecs_fini(world); +} + +void Eval_path_tag_in_module(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t tag = ecs_entity(world, { .name = "Hello.World" }); + test_assert(tag != 0); + + const char *expr = + HEAD "module parent" + LINE "foo {" + LINE " Hello.World" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "parent.Hello.World") == 0); + + ecs_entity_t foo = ecs_lookup(world, "parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has_id(world, foo, tag)); + + ecs_fini(world); +} + +void Eval_path_tag_in_nested_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t tag = ecs_entity(world, { .name = "Hello.World" }); + test_assert(tag != 0); + + const char *expr = + HEAD "Hello.parent {" + LINE " foo {" + LINE " Hello.World" + LINE " }" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "parent.Hello.World") == 0); + + ecs_entity_t foo = ecs_lookup(world, "Hello.parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has_id(world, foo, tag)); + + ecs_fini(world); +} + +void Eval_path_tag_in_nested_module(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t tag = ecs_entity(world, { .name = "Hello.World" }); + test_assert(tag != 0); + + const char *expr = + HEAD "module Hello.parent" + LINE "foo {" + LINE " Hello.World" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + test_assert(ecs_lookup(world, "Hello.parent.Hello") == 0); + test_assert(ecs_lookup(world, "Hello.parent.Hello.World") == 0); + + ecs_entity_t foo = ecs_lookup(world, "Hello.parent.foo"); + test_assert(foo != 0); + + test_assert(ecs_has_id(world, foo, tag)); + + ecs_fini(world); +} + +void Eval_dont_inherit_script_pair(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + HEAD "using flecs.meta" + LINE "Bar {}" + LINE "prefab Foo {" + LINE " Bar" + LINE "}"; + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s != 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + test_assert(foo != 0); + test_assert(bar != 0); + + ecs_entity_t e = ecs_new(world); + ecs_add_pair(world, e, EcsIsA, foo); + + test_assert(ecs_has_pair(world, foo, ecs_id(EcsScript), s)); + test_assert(ecs_has_pair(world, bar, ecs_id(EcsScript), s)); + test_assert(!ecs_has_pair(world, e, ecs_id(EcsScript), s)); + test_assert(ecs_has_id(world, e, bar)); + + ecs_fini(world); +} + +void Eval_update_script_w_anonymous(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = "_ { Position: {x: 10, y: 20} }" + }); + test_assert(s != 0); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_each_next(&it)); + } + + ecs_script_update(world, s, 0, "_ { Position: {x: 30, y: 40} }"); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + test_bool(false, ecs_each_next(&it)); + } + + ecs_fini(world); +} + +void Eval_update_script_w_anonymous_paren(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = "Position(x: 10, y: 20)" + }); + test_assert(s != 0); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_each_next(&it)); + } + + ecs_script_update(world, s, 0, "Position(x: 30, y: 40)"); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 30); + test_int(p->y, 40); + test_bool(false, ecs_each_next(&it)); + } + + ecs_fini(world); +} + +void Eval_clear_script(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = "e { Position: {x: 10, y: 20} }" + }); + test_assert(s != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + test_uint(e, it.entities[0]); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_each_next(&it)); + } + + ecs_script_clear(world, s, 0); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(false, ecs_each_next(&it)); + } + + ecs_fini(world); +} + +void Eval_clear_script_w_anonymous(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = "_ { Position: {x: 10, y: 20} }" + }); + test_assert(s != 0); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_each_next(&it)); + } + + ecs_script_clear(world, s, 0); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(false, ecs_each_next(&it)); + } + + ecs_fini(world); +} + +void Eval_clear_script_w_anonymous_paren(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = "Position(x: 10, y: 20)" + }); + test_assert(s != 0); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + Position *p = ecs_field(&it, Position, 0); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + test_bool(false, ecs_each_next(&it)); + } + + ecs_script_clear(world, s, 0); + + { + ecs_iter_t it = ecs_each(world, Position); + test_bool(false, ecs_each_next(&it)); + } + + ecs_fini(world); +} + +void Eval_partial_assign(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = "Position foo(x: 10); Position foo(y: 20)" + }); + test_assert(s != 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 0); + test_int(p->y, 20); + + ecs_fini(world); +} + +typedef struct Strings { + char *a; + char *b; +} Strings; + +static int strings_ctor_invoked = 0; +static int strings_dtor_invoked = 0; +static int strings_move_invoked = 0; +static int strings_copy_invoked = 0; + +ECS_CTOR(Strings, ptr, { + ptr->a = NULL; + ptr->b = NULL; + strings_ctor_invoked ++; +}) + +ECS_DTOR(Strings, ptr, { + ecs_os_free(ptr->a); + ecs_os_free(ptr->b); + strings_dtor_invoked ++; +}) + +ECS_MOVE(Strings, dst, src, { + ecs_os_free(dst->a); + ecs_os_free(dst->b); + dst->a = src->a; + dst->b = src->b; + src->a = NULL; + dst->a = NULL; + strings_move_invoked ++; +}) + +ECS_COPY(Strings, dst, src, { + ecs_os_free(dst->a); + ecs_os_free(dst->b); + dst->a = ecs_os_strdup(src->a); + dst->b = ecs_os_strdup(src->b); + strings_copy_invoked ++; +}) + +void Eval_partial_assign_nontrivial(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Strings); + + ecs_set_hooks(world, Strings, { + .ctor = ecs_ctor(Strings), + .dtor = ecs_dtor(Strings), + .move = ecs_move(Strings), + .copy = ecs_copy(Strings) + }); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Strings"}), + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_string_t)} + } + }); + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = "Strings foo(a: \"hello\", b: \"world\"); Strings foo(b: \"bar\")" + }); + test_assert(s != 0); + + test_int(strings_ctor_invoked, 3); + test_int(strings_dtor_invoked, 2); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + const Strings *p = ecs_get(world, foo, Strings); + test_assert(p != NULL); + test_str(p->a, NULL); + test_str(p->b, "bar"); + + ecs_fini(world); + + test_int(strings_ctor_invoked, 3); + test_int(strings_dtor_invoked, 3); + test_int(strings_move_invoked, 0); + test_int(strings_copy_invoked, 0); +} + +void Eval_partial_assign_with(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "with Position(x: 10) {" + LINE " with Position(y: 20) {" + LINE " foo {}" + LINE " }" + LINE "}"; + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s != 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 0); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Eval_partial_assign_nontrivial_with(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Strings); + + ecs_set_hooks(world, Strings, { + .ctor = ecs_ctor(Strings), + .dtor = ecs_dtor(Strings), + .move = ecs_move(Strings), + .copy = ecs_copy(Strings) + }); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Strings"}), + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_string_t)} + } + }); + + const char *expr = + HEAD "with Strings(a: \"hello\", b: \"world\") {" + LINE " with Strings(b: \"bar\") {" + LINE " foo {}" + LINE " bar {}" + LINE " }" + LINE "}"; + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s != 0); + + test_int(strings_ctor_invoked, 4); + test_int(strings_dtor_invoked, 2); + test_int(strings_move_invoked, 0); + test_int(strings_copy_invoked, 4); + + { + ecs_entity_t e = ecs_lookup(world, "foo"); + test_assert(e != 0); + + const Strings *p = ecs_get(world, e, Strings); + test_assert(p != NULL); + test_str(p->a, NULL); + test_str(p->b, "bar"); + } + { + ecs_entity_t e = ecs_lookup(world, "bar"); + test_assert(e != 0); + + const Strings *p = ecs_get(world, e, Strings); + test_assert(p != NULL); + test_str(p->a, NULL); + test_str(p->b, "bar"); + } + + ecs_fini(world); + + test_int(strings_ctor_invoked, 4); + test_int(strings_dtor_invoked, 4); + test_int(strings_move_invoked, 0); + test_int(strings_copy_invoked, 4); +} + +typedef struct LargeArray { + int8_t values[1000]; +} LargeArray; + +void Eval_partial_assign_with_large_array(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, LargeArray); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "LargeArray"}), + .members = { + {"values", ecs_id(ecs_i8_t), .count = 1000} + } + }); + + const char *expr = + HEAD "with LargeArray(values: [1, 2, 3]) {" + LINE " foo {}" + LINE "}"; + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s != 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + const LargeArray *p = ecs_get(world, foo, LargeArray); + test_assert(p != NULL); + test_int(p->values[0], 1); + test_int(p->values[1], 2); + test_int(p->values[2], 3); + for (int i = 3; i < 1000; i ++) { + test_int(p->values[i], 0); + } + + ecs_fini(world); +} + +void Eval_non_trivial_var_component(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Strings); + + ecs_set_hooks(world, Strings, { + .ctor = ecs_ctor(Strings), + .dtor = ecs_dtor(Strings), + .move = ecs_move(Strings), + .copy = ecs_copy(Strings) + }); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Strings"}), + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_string_t)} + } + }); + + const char *expr = + HEAD "const val = Strings: {\"hello\", \"world\"}" + LINE "foo { $val }" + LINE "bar { $val }" + LINE ""; + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s != 0); + + test_int(strings_ctor_invoked, 3); + test_int(strings_dtor_invoked, 1); + test_int(strings_copy_invoked, 2); + test_int(strings_move_invoked, 0); + + { + ecs_entity_t e = ecs_lookup(world, "foo"); + test_assert(e != 0); + + const Strings *p = ecs_get(world, e, Strings); + test_assert(p != NULL); + test_str(p->a, "hello"); + test_str(p->b, "world"); + } + { + ecs_entity_t e = ecs_lookup(world, "bar"); + test_assert(e != 0); + + const Strings *p = ecs_get(world, e, Strings); + test_assert(p != NULL); + test_str(p->a, "hello"); + test_str(p->b, "world"); + } + + ecs_fini(world); + + test_int(strings_ctor_invoked, 3); + test_int(strings_dtor_invoked, 3); + test_int(strings_copy_invoked, 2); + test_int(strings_move_invoked, 0); +} + +void Eval_non_trivial_var_with(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Strings); + + ecs_set_hooks(world, Strings, { + .ctor = ecs_ctor(Strings), + .dtor = ecs_dtor(Strings), + .move = ecs_move(Strings), + .copy = ecs_copy(Strings) + }); + + ecs_struct(world, { + .entity = ecs_entity(world, {.name = "Strings"}), + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_string_t)} + } + }); + + const char *expr = + HEAD "const val = Strings: {\"hello\", \"world\"}" + HEAD "with $val {" + LINE " foo {}" + LINE " bar {}" + LINE "}" + LINE ""; + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s != 0); + + test_int(strings_ctor_invoked, 3); + test_int(strings_dtor_invoked, 1); + test_int(strings_copy_invoked, 2); + test_int(strings_move_invoked, 0); + + { + ecs_entity_t e = ecs_lookup(world, "foo"); + test_assert(e != 0); + + const Strings *p = ecs_get(world, e, Strings); + test_assert(p != NULL); + test_str(p->a, "hello"); + test_str(p->b, "world"); + } + { + ecs_entity_t e = ecs_lookup(world, "bar"); + test_assert(e != 0); + + const Strings *p = ecs_get(world, e, Strings); + test_assert(p != NULL); + test_str(p->a, "hello"); + test_str(p->b, "world"); + } + + ecs_fini(world); + + test_int(strings_ctor_invoked, 3); + test_int(strings_dtor_invoked, 3); + test_int(strings_copy_invoked, 2); + test_int(strings_move_invoked, 0); +} + +void Eval_update_template_w_tag(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "Foo {}" + LINE + LINE "template Bar {" + LINE " Foo" + LINE "}" + LINE + LINE "Bar e()"; + + ecs_entity_t s = ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }); + test_assert(s != 0); + + test_assert(ecs_script_update(world, s, 0, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t bar = ecs_lookup(world, "Bar"); + ecs_entity_t e = ecs_lookup(world, "e"); + + test_assert(foo != 0); + test_assert(bar != 0); + test_assert(e != 0); + + test_assert(ecs_has_id(world, e, foo)); + test_assert(ecs_has_id(world, e, bar)); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/meta/src/DeserExprOperators.c b/vendors/flecs/test/script/src/Expr.c similarity index 57% rename from vendors/flecs/test/meta/src/DeserExprOperators.c rename to vendors/flecs/test/script/src/Expr.c index d24bc51f8..a3bb9f2d3 100644 --- a/vendors/flecs/test/meta/src/DeserExprOperators.c +++ b/vendors/flecs/test/script/src/Expr.c @@ -1,10 +1,10 @@ -#include +#include -void DeserExprOperators_add_2_int_literals(void) { +void Expr_add_2_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 + 20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 10 + 20); @@ -13,16 +13,16 @@ void DeserExprOperators_add_2_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_add_2_int_literals_twice(void) { +void Expr_add_2_int_literals_twice(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 + 20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 10 + 20); - test_assert(ecs_parse_expr(world, "10 + 20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 10 + 20); @@ -31,11 +31,11 @@ void DeserExprOperators_add_2_int_literals_twice(void) { ecs_fini(world); } -void DeserExprOperators_sub_2_int_literals(void) { +void Expr_sub_2_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "20 - 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "20 - 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(int64_t*)v.ptr, 20 - 10); @@ -44,11 +44,11 @@ void DeserExprOperators_sub_2_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_2_int_literals(void) { +void Expr_mul_2_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "20 * 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "20 * 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, 20 * 10); @@ -57,11 +57,11 @@ void DeserExprOperators_mul_2_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_div_2_int_literals(void) { +void Expr_div_2_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 / 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 / 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 10 / 2); @@ -70,11 +70,11 @@ void DeserExprOperators_div_2_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_add_3_int_literals(void) { +void Expr_add_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 + 20 + 30", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 20 + 30", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 10 + 20 + 30); @@ -83,16 +83,16 @@ void DeserExprOperators_add_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_add_3_int_literals_twice(void) { +void Expr_add_3_int_literals_twice(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 + 20 + 30", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 20 + 30", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 10 + 20 + 30); - test_assert(ecs_parse_expr(world, "10 + 20 + 30", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 20 + 30", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 10 + 20 + 30); @@ -101,11 +101,11 @@ void DeserExprOperators_add_3_int_literals_twice(void) { ecs_fini(world); } -void DeserExprOperators_sub_3_int_literals(void) { +void Expr_sub_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "30 - 10 - 5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "30 - 10 - 5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(ecs_i64_t*)v.ptr, 30 - 10 - 5); @@ -114,11 +114,11 @@ void DeserExprOperators_sub_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_3_int_literals(void) { +void Expr_mul_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "2 * 5 * 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "2 * 5 * 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, 2 * 5 * 10); @@ -127,11 +127,11 @@ void DeserExprOperators_mul_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_div_3_int_literals(void) { +void Expr_div_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "40 / 5 / 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "40 / 5 / 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 40 / 5 / 2); @@ -140,23 +140,23 @@ void DeserExprOperators_div_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_int_to_bool(void) { +void Expr_int_to_bool(void) { ecs_world_t *world = ecs_init(); bool b = false; ecs_value_t v = { .type = ecs_id(ecs_bool_t), .ptr = &b }; - test_assert(ecs_parse_expr(world, "10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_bool_t*)v.ptr, true); - test_assert(ecs_parse_expr(world, "0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_bool_t*)v.ptr, false); - test_assert(ecs_parse_expr(world, "256", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "256", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_bool_t*)v.ptr, true); @@ -164,18 +164,18 @@ void DeserExprOperators_int_to_bool(void) { ecs_fini(world); } -void DeserExprOperators_bool_to_int(void) { +void Expr_bool_to_int(void) { ecs_world_t *world = ecs_init(); int32_t i = 0; ecs_value_t v = { .type = ecs_id(ecs_i32_t), .ptr = &i }; - test_assert(ecs_parse_expr(world, "true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i32_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_i32_t*)v.ptr, 1); - test_assert(ecs_parse_expr(world, "false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i32_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_i32_t*)v.ptr, 0); @@ -183,18 +183,18 @@ void DeserExprOperators_bool_to_int(void) { ecs_fini(world); } -void DeserExprOperators_bool_to_uint(void) { +void Expr_bool_to_uint(void) { ecs_world_t *world = ecs_init(); uint32_t i = 0; ecs_value_t v = { .type = ecs_id(ecs_u32_t), .ptr = &i }; - test_assert(ecs_parse_expr(world, "true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u32_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_u32_t*)v.ptr, 1); - test_assert(ecs_parse_expr(world, "false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u32_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_u32_t*)v.ptr, 0); @@ -202,11 +202,11 @@ void DeserExprOperators_bool_to_uint(void) { ecs_fini(world); } -void DeserExprOperators_add_mul_3_int_literals(void) { +void Expr_add_mul_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 + 20 * 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 20 * 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 10 + 20 * 2); @@ -215,11 +215,11 @@ void DeserExprOperators_add_mul_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_sub_mul_3_int_literals(void) { +void Expr_sub_mul_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "50 - 10 * 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "50 - 10 * 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(int64_t*)v.ptr, 50 - 10 * 2); @@ -228,11 +228,11 @@ void DeserExprOperators_sub_mul_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_div_mul_3_int_literals(void) { +void Expr_div_mul_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 / 5 * 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 / 5 * 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 10 / 5 * 2); @@ -241,11 +241,11 @@ void DeserExprOperators_div_mul_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_add_div_3_int_literals(void) { +void Expr_add_div_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 + 30 / 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 30 / 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_f64_t*)v.ptr, 10 + 30 / 2); @@ -254,11 +254,11 @@ void DeserExprOperators_add_div_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_sub_div_3_int_literals(void) { +void Expr_sub_div_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "30 - 10 / 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "30 - 10 / 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_f64_t*)v.ptr, 30 - 10 / 2); @@ -267,11 +267,11 @@ void DeserExprOperators_sub_div_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_div_3_int_literals(void) { +void Expr_mul_div_3_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "20 * 10 / 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "20 * 10 / 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_f64_t*)v.ptr, 20 * 10 / 2); @@ -280,11 +280,11 @@ void DeserExprOperators_mul_div_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_add_mul_add_int_literals(void) { +void Expr_mul_add_mul_add_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "2 * 4 + 6 * 8 + 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "2 * 4 + 6 * 8 + 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, 2 * 4 + 6 * 8 + 10); @@ -293,11 +293,11 @@ void DeserExprOperators_mul_add_mul_add_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_sub_mul_sub_int_literals(void) { +void Expr_mul_sub_mul_sub_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "2 * 4 - 6 * 8 - 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "2 * 4 - 6 * 8 - 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(ecs_i64_t*)v.ptr, 2 * 4 - 6 * 8 - 10); @@ -306,11 +306,11 @@ void DeserExprOperators_mul_sub_mul_sub_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_div_mul_div_int_literals(void) { +void Expr_mul_div_mul_div_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "2 * 4 / 6 * 8 / 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "2 * 4 / 6 * 8 / 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 2.0 * 4.0 / 6.0 * 8.0 / 10.0); @@ -319,11 +319,11 @@ void DeserExprOperators_mul_div_mul_div_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_div_add_div_add_int_literals(void) { +void Expr_div_add_div_add_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "2 / 4 + 6 / 8 + 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "2 / 4 + 6 / 8 + 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 2.0 / 4.0 + 6.0 / 8.0 + 10.0); @@ -332,11 +332,11 @@ void DeserExprOperators_div_add_div_add_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_div_sub_div_sub_int_literals(void) { +void Expr_div_sub_div_sub_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "2 / 4 - 6 / 8 - 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "2 / 4 - 6 / 8 - 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 2.0 / 4.0 - 6.0 / 8.0 - 10.0); @@ -345,11 +345,11 @@ void DeserExprOperators_div_sub_div_sub_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_div_mul_div_mul_int_literals(void) { +void Expr_div_mul_div_mul_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "2 / 4 * 6 / 8 * 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "2 / 4 * 6 / 8 * 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 2.0 / 4.0 * 6.0 / 8.0 * 10.0); @@ -358,11 +358,11 @@ void DeserExprOperators_div_mul_div_mul_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_div_sub_div_mul_int_literals(void) { +void Expr_div_sub_div_mul_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "2 / 4 - 6 / 8 * 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "2 / 4 - 6 / 8 * 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 2.0 / 4.0 - 6.0 / 8.0 * 10.0); @@ -371,11 +371,11 @@ void DeserExprOperators_div_sub_div_mul_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_add_2_flt_literals(void) { +void Expr_add_2_flt_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10.5 + 20.0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 + 20.0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 10.5 + 20.0); @@ -384,11 +384,11 @@ void DeserExprOperators_add_2_flt_literals(void) { ecs_fini(world); } -void DeserExprOperators_sub_2_flt_literals(void) { +void Expr_sub_2_flt_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "20.5 - 10.0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "20.5 - 10.0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 20.5 - 10.0); @@ -397,11 +397,11 @@ void DeserExprOperators_sub_2_flt_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_2_flt_literals(void) { +void Expr_mul_2_flt_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "20.5 * 10.0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "20.5 * 10.0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 20.5 * 10.0); @@ -410,11 +410,11 @@ void DeserExprOperators_mul_2_flt_literals(void) { ecs_fini(world); } -void DeserExprOperators_div_2_flt_literals(void) { +void Expr_div_2_flt_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10.5 / 2.0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 / 2.0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, 10.5 / 2.0); @@ -423,11 +423,11 @@ void DeserExprOperators_div_2_flt_literals(void) { ecs_fini(world); } -void DeserExprOperators_add_2_int_neg_literals(void) { +void Expr_add_2_int_neg_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "-10 + -20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-10 + -20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(ecs_i64_t*)v.ptr, -10 + -20); @@ -436,11 +436,11 @@ void DeserExprOperators_add_2_int_neg_literals(void) { ecs_fini(world); } -void DeserExprOperators_sub_2_int_neg_literals(void) { +void Expr_sub_2_int_neg_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "-10 - -20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-10 - -20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(ecs_i64_t*)v.ptr, -10 - -20); @@ -449,11 +449,11 @@ void DeserExprOperators_sub_2_int_neg_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_2_int_neg_literals(void) { +void Expr_mul_2_int_neg_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "-10 * -20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-10 * -20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(ecs_i64_t*)v.ptr, -10 * -20); @@ -462,11 +462,11 @@ void DeserExprOperators_mul_2_int_neg_literals(void) { ecs_fini(world); } -void DeserExprOperators_div_2_int_neg_literals(void) { +void Expr_div_2_int_neg_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "-10 / -20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-10 / -20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f64_t)); test_assert(v.ptr != NULL); test_flt(*(ecs_f64_t*)v.ptr, -10.0 / -20.0); @@ -475,11 +475,11 @@ void DeserExprOperators_div_2_int_neg_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_lparen_add_add_rparen_int_literals(void) { +void Expr_mul_lparen_add_add_rparen_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 * (20 + 30)", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 * (20 + 30)", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, 10 * (20 + 30)); @@ -488,11 +488,11 @@ void DeserExprOperators_mul_lparen_add_add_rparen_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_lparen_add_add_add_rparen_int_literals(void) { +void Expr_mul_lparen_add_add_add_rparen_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 * (20 + 30 + 40)", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 * (20 + 30 + 40)", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, 10 * (20 + 30 + 40)); @@ -501,11 +501,11 @@ void DeserExprOperators_mul_lparen_add_add_add_rparen_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_mul_lparen_add_add_rparen_add_int_literals(void) { +void Expr_mul_lparen_add_add_rparen_add_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 * (20 + 30) + 40", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 * (20 + 30) + 40", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, 10 * (20 + 30) + 40); @@ -514,11 +514,11 @@ void DeserExprOperators_mul_lparen_add_add_rparen_add_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_lparen_add_add_rparen_mul_int_literals(void) { +void Expr_lparen_add_add_rparen_mul_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "(20 + 30) * 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "(20 + 30) * 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, (20 + 30) * 10); @@ -527,11 +527,11 @@ void DeserExprOperators_lparen_add_add_rparen_mul_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_lparen_add_add_add_rparen_mul_int_literals(void) { +void Expr_lparen_add_add_add_rparen_mul_int_literals(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "(20 + 30 + 40) * 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "(20 + 30 + 40) * 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, (20 + 30 + 40) * 10); @@ -540,11 +540,11 @@ void DeserExprOperators_lparen_add_add_add_rparen_mul_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_double_paren_add_add(void) { +void Expr_double_paren_add_add(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "((20 + 30))", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "((20 + 30))", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, ((20 + 30))); @@ -553,11 +553,11 @@ void DeserExprOperators_double_paren_add_add(void) { ecs_fini(world); } -void DeserExprOperators_double_paren_literal(void) { +void Expr_double_paren_literal(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "((20))", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "((20))", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, ((20))); @@ -566,11 +566,11 @@ void DeserExprOperators_double_paren_literal(void) { ecs_fini(world); } -void DeserExprOperators_lparen_add_add_rparen_mul_lparen_add_add_rparen(void) { +void Expr_lparen_add_add_rparen_mul_lparen_add_add_rparen(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "(10 + 20) * (20 + 30)", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "(10 + 20) * (20 + 30)", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_u64_t*)v.ptr, (10 + 20) * (20 + 30)); @@ -579,7 +579,7 @@ void DeserExprOperators_lparen_add_add_rparen_mul_lparen_add_add_rparen(void) { ecs_fini(world); } -void DeserExprOperators_float_result_add_2_int_literals(void) { +void Expr_float_result_add_2_int_literals(void) { ecs_world_t *world = ecs_init(); float value = 0; @@ -587,7 +587,7 @@ void DeserExprOperators_float_result_add_2_int_literals(void) { .type = ecs_id(ecs_f32_t), .ptr = &value }; - test_assert(ecs_parse_expr(world, "10 + 20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_f32_t)); test_assert(v.ptr != NULL); test_flt(value, 10 + 20); @@ -595,7 +595,7 @@ void DeserExprOperators_float_result_add_2_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_struct_result_add_2_int_literals(void) { +void Expr_struct_result_add_2_int_literals(void) { ecs_world_t *world = ecs_init(); typedef struct { @@ -609,7 +609,7 @@ void DeserExprOperators_struct_result_add_2_int_literals(void) { }); Mass v = {0}; - const char *ptr = ecs_parse_expr(world, "{10 + 20}", &(ecs_value_t){ + const char *ptr = ecs_script_expr_run(world, "{10 + 20}", &(ecs_value_t){ .type = t, .ptr = &v }, NULL); test_assert(ptr != NULL); @@ -620,7 +620,7 @@ void DeserExprOperators_struct_result_add_2_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_struct_result_add_2_2_fields_int_literals(void) { +void Expr_struct_result_add_2_2_fields_int_literals(void) { ecs_world_t *world = ecs_init(); typedef struct { @@ -636,7 +636,7 @@ void DeserExprOperators_struct_result_add_2_2_fields_int_literals(void) { }); Mass v = {0}; - const char *ptr = ecs_parse_expr(world, "{10 + 20, 20 + 30}", &(ecs_value_t){ + const char *ptr = ecs_script_expr_run(world, "{10 + 20, 20 + 30}", &(ecs_value_t){ .type = t, .ptr = &v }, NULL); test_assert(ptr != NULL); @@ -648,7 +648,7 @@ void DeserExprOperators_struct_result_add_2_2_fields_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_struct_result_add_3_int_literals(void) { +void Expr_struct_result_add_3_int_literals(void) { ecs_world_t *world = ecs_init(); typedef struct { @@ -662,7 +662,7 @@ void DeserExprOperators_struct_result_add_3_int_literals(void) { }); Mass v = {0}; - const char *ptr = ecs_parse_expr(world, "{10 + 20 + 30}", &(ecs_value_t){ + const char *ptr = ecs_script_expr_run(world, "{10 + 20 + 30}", &(ecs_value_t){ .type = t, .ptr = &v }, NULL); test_assert(ptr != NULL); @@ -673,7 +673,7 @@ void DeserExprOperators_struct_result_add_3_int_literals(void) { ecs_fini(world); } -void DeserExprOperators_struct_result_lparen_int_rparen(void) { +void Expr_struct_result_lparen_int_rparen(void) { ecs_world_t *world = ecs_init(); typedef struct { @@ -687,7 +687,7 @@ void DeserExprOperators_struct_result_lparen_int_rparen(void) { }); Mass v = {0}; - const char *ptr = ecs_parse_expr(world, "{(10)}", &(ecs_value_t){ + const char *ptr = ecs_script_expr_run(world, "{(10)}", &(ecs_value_t){ .type = t, .ptr = &v }, NULL); test_assert(ptr != NULL); @@ -698,73 +698,71 @@ void DeserExprOperators_struct_result_lparen_int_rparen(void) { ecs_fini(world); } -void DeserExprOperators_add_to_var(void) { +void Expr_add_to_var(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_i32_t); *(int32_t*)var->value.ptr = 10; ecs_value_t v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr(world, "$foo + 20", &v, &desc); + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run(world, "$foo + 20", &v, &desc); test_assert(ptr != NULL); test_assert(!ptr[0]); test_int(*(int32_t*)v.ptr, 10 + 20); ecs_value_free(world, v.type, v.ptr); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_add_var_to(void) { +void Expr_add_var_to(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_i32_t); *(int32_t*)var->value.ptr = 10; ecs_value_t v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr(world, "20 + $foo", &v, &desc); + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run(world, "20 + $foo", &v, &desc); test_assert(ptr != NULL); test_assert(!ptr[0]); test_int(*(int32_t*)v.ptr, 20 + 10); ecs_value_free(world, v.type, v.ptr); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_var_member(void) { +void Expr_var_member(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); ecs_struct(world, { - .entity = ecs_id(Position), + .entity = ecs_id(PositionI), .members = { { .name = "x", .type = ecs_id(ecs_i32_t) }, { .name = "y", .type = ecs_id(ecs_i32_t) } } }); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare(&vars, "foo", ecs_id(Position)); - *(Position*)var->value.ptr = (Position){10, 20}; + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", PositionI); + *(PositionI*)var->value.ptr = (PositionI){10, 20}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; + ecs_script_expr_run_desc_t desc = { .vars = vars }; { ecs_value_t v = {0}; - const char *ptr = ecs_parse_expr(world, "$foo.x", &v, &desc); + const char *ptr = ecs_script_expr_run(world, "$foo.x", &v, &desc); test_assert(ptr != NULL); test_assert(!ptr[0]); test_uint(v.type, ecs_id(ecs_i32_t)); @@ -773,7 +771,7 @@ void DeserExprOperators_var_member(void) { } { ecs_value_t v = {0}; - const char *ptr = ecs_parse_expr(world, "$foo.y", &v, &desc); + const char *ptr = ecs_script_expr_run(world, "$foo.y", &v, &desc); test_assert(ptr != NULL); test_assert(!ptr[0]); test_uint(v.type, ecs_id(ecs_i32_t)); @@ -781,30 +779,30 @@ void DeserExprOperators_var_member(void) { ecs_value_free(world, v.type, v.ptr); } - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_bool_cond_and_bool(void) { +void Expr_bool_cond_and_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true && true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true && true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "true && false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true && false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false && true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false && true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false && false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false && false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -813,26 +811,26 @@ void DeserExprOperators_bool_cond_and_bool(void) { ecs_fini(world); } -void DeserExprOperators_bool_cond_or_bool(void) { +void Expr_bool_cond_or_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true || true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true || true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "true || false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true || false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false || true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false || true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false || false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false || false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -841,26 +839,26 @@ void DeserExprOperators_bool_cond_or_bool(void) { ecs_fini(world); } -void DeserExprOperators_int_cond_and_int(void) { +void Expr_int_cond_and_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 && 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 && 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10 && 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 && 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "0 && 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 && 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "0 && 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 && 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -869,26 +867,26 @@ void DeserExprOperators_int_cond_and_int(void) { ecs_fini(world); } -void DeserExprOperators_int_cond_or_int(void) { +void Expr_int_cond_or_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 || 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 || 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10 || 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 || 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "0 || 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 || 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "0 || 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 || 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -897,26 +895,26 @@ void DeserExprOperators_int_cond_or_int(void) { ecs_fini(world); } -void DeserExprOperators_bool_cond_and_int(void) { +void Expr_bool_cond_and_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true && 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true && 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "true && 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true && 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false && 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false && 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false && 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false && 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -925,26 +923,26 @@ void DeserExprOperators_bool_cond_and_int(void) { ecs_fini(world); } -void DeserExprOperators_int_cond_and_bool(void) { +void Expr_int_cond_and_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 && true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 && true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10 && false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 && false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "0 && true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 && true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "0 && false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 && false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -953,26 +951,26 @@ void DeserExprOperators_int_cond_and_bool(void) { ecs_fini(world); } -void DeserExprOperators_bool_cond_or_int(void) { +void Expr_bool_cond_or_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true || 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true || 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "true || 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true || 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false || 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false || 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false || 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false || 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -981,26 +979,26 @@ void DeserExprOperators_bool_cond_or_int(void) { ecs_fini(world); } -void DeserExprOperators_int_cond_or_bool(void) { +void Expr_int_cond_or_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 || true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 || true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10 || false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 || false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "0 || true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 || true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "0 || false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 || false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -1009,26 +1007,26 @@ void DeserExprOperators_int_cond_or_bool(void) { ecs_fini(world); } -void DeserExprOperators_cond_eq_bool(void) { +void Expr_cond_eq_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true == true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true == true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "true == false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true == false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false == true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false == true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false == false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false == false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); @@ -1037,26 +1035,26 @@ void DeserExprOperators_cond_eq_bool(void) { ecs_fini(world); } -void DeserExprOperators_cond_eq_int(void) { +void Expr_cond_eq_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 == 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 == 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10 == 20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 == 20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "10 == 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 == 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "0 == 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 == 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); @@ -1065,26 +1063,26 @@ void DeserExprOperators_cond_eq_int(void) { ecs_fini(world); } -void DeserExprOperators_cond_neq_bool(void) { +void Expr_cond_neq_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true != true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true != true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "true != false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true != false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false != true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false != true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false != false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false != false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -1093,26 +1091,26 @@ void DeserExprOperators_cond_neq_bool(void) { ecs_fini(world); } -void DeserExprOperators_cond_neq_int(void) { +void Expr_cond_neq_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 != 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 != 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "10 != 20", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 != 20", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10 != 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 != 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "0 != 0", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "0 != 0", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -1121,47 +1119,47 @@ void DeserExprOperators_cond_neq_int(void) { ecs_fini(world); } -void DeserExprOperators_cond_eq_bool_int(void) { +void Expr_cond_eq_bool_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; ecs_log_set_level(-4); - test_assert(ecs_parse_expr(world, "true == 1", &v, NULL) == NULL); - test_assert(ecs_parse_expr(world, "true == 0", &v, NULL) == NULL); - test_assert(ecs_parse_expr(world, "false == 0", &v, NULL) == NULL); + test_assert(ecs_script_expr_run(world, "true == 1", &v, NULL) == NULL); + test_assert(ecs_script_expr_run(world, "true == 0", &v, NULL) == NULL); + test_assert(ecs_script_expr_run(world, "false == 0", &v, NULL) == NULL); ecs_fini(world); } -void DeserExprOperators_cond_eq_int_flt(void) { +void Expr_cond_eq_int_flt(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; ecs_log_set_level(-4); - test_assert(ecs_parse_expr(world, "1 == 1.0", &v, NULL) == NULL); - test_assert(ecs_parse_expr(world, "1 == 0.0", &v, NULL) == NULL); - test_assert(ecs_parse_expr(world, "0 == 0.0", &v, NULL) == NULL); + test_assert(ecs_script_expr_run(world, "1 == 1.0", &v, NULL) == NULL); + test_assert(ecs_script_expr_run(world, "1 == 0.0", &v, NULL) == NULL); + test_assert(ecs_script_expr_run(world, "0 == 0.0", &v, NULL) == NULL); ecs_fini(world); } -void DeserExprOperators_cond_eq_cond_and(void) { +void Expr_cond_eq_cond_and(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true == true && false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true == true && false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true == true && false); - test_assert(ecs_parse_expr(world, "true && false == false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true && false == false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true && false == false); - test_assert(ecs_parse_expr(world, "true && true == true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true && true == true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true && true == true); @@ -1170,16 +1168,16 @@ void DeserExprOperators_cond_eq_cond_and(void) { ecs_fini(world); } -void DeserExprOperators_cond_eq_cond_or(void) { +void Expr_cond_eq_cond_or(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true == true || false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true == true || false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true == true || false); - test_assert(ecs_parse_expr(world, "true || false == false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true || false == false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true || false == false); @@ -1188,26 +1186,26 @@ void DeserExprOperators_cond_eq_cond_or(void) { ecs_fini(world); } -void DeserExprOperators_cond_gt_bool(void) { +void Expr_cond_gt_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true > false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true > false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "true > true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true > true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false > true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false > true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false > false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false > false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -1216,26 +1214,26 @@ void DeserExprOperators_cond_gt_bool(void) { ecs_fini(world); } -void DeserExprOperators_cond_gt_int(void) { +void Expr_cond_gt_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 > 5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 > 5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10 > 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 > 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "5 > 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5 > 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "5 > 5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5 > 5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -1244,26 +1242,26 @@ void DeserExprOperators_cond_gt_int(void) { ecs_fini(world); } -void DeserExprOperators_cond_gt_flt(void) { +void Expr_cond_gt_flt(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10.5 > 5.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 > 5.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10.5 > 10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 > 10.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "5.5 > 10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5.5 > 10.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "5.5 > 5.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5.5 > 5.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -1272,26 +1270,26 @@ void DeserExprOperators_cond_gt_flt(void) { ecs_fini(world); } -void DeserExprOperators_cond_gteq_bool(void) { +void Expr_cond_gteq_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true >= false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true >= false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "true >= true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true >= true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false >= true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false >= true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false >= false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false >= false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); @@ -1300,26 +1298,26 @@ void DeserExprOperators_cond_gteq_bool(void) { ecs_fini(world); } -void DeserExprOperators_cond_gteq_int(void) { +void Expr_cond_gteq_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 >= 5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 >= 5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10 >= 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 >= 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "5 >= 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5 >= 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "5 >= 5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5 >= 5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); @@ -1328,26 +1326,26 @@ void DeserExprOperators_cond_gteq_int(void) { ecs_fini(world); } -void DeserExprOperators_cond_gteq_flt(void) { +void Expr_cond_gteq_flt(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10.5 >= 5.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 >= 5.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "10.5 >= 10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 >= 10.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "5.5 >= 10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5.5 >= 10.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "5.5 >= 5.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5.5 >= 5.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); @@ -1356,26 +1354,26 @@ void DeserExprOperators_cond_gteq_flt(void) { ecs_fini(world); } -void DeserExprOperators_cond_lt_bool(void) { +void Expr_cond_lt_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true < false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true < false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "true < true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true < true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "false < true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false < true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false < false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false < false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -1384,26 +1382,26 @@ void DeserExprOperators_cond_lt_bool(void) { ecs_fini(world); } -void DeserExprOperators_cond_lt_int(void) { +void Expr_cond_lt_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 < 5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 < 5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "10 < 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 < 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "5 < 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5 < 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "5 < 5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5 < 5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -1412,26 +1410,26 @@ void DeserExprOperators_cond_lt_int(void) { ecs_fini(world); } -void DeserExprOperators_cond_lt_flt(void) { +void Expr_cond_lt_flt(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10.5 < 5.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 < 5.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "10.5 < 10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 < 10.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "5.5 < 10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5.5 < 10.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "5.5 < 5.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5.5 < 5.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); @@ -1440,26 +1438,26 @@ void DeserExprOperators_cond_lt_flt(void) { ecs_fini(world); } -void DeserExprOperators_cond_lteq_bool(void) { +void Expr_cond_lteq_bool(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "true <= false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true <= false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "true <= true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "true <= true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false <= true", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false <= true", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "false <= false", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "false <= false", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); @@ -1468,26 +1466,26 @@ void DeserExprOperators_cond_lteq_bool(void) { ecs_fini(world); } -void DeserExprOperators_cond_lteq_int(void) { +void Expr_cond_lteq_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 <= 5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 <= 5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "10 <= 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 <= 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "5 <= 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5 <= 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "5 <= 5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5 <= 5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); @@ -1496,26 +1494,26 @@ void DeserExprOperators_cond_lteq_int(void) { ecs_fini(world); } -void DeserExprOperators_cond_lteq_flt(void) { +void Expr_cond_lteq_flt(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10.5 <= 5.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 <= 5.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, false); - test_assert(ecs_parse_expr(world, "10.5 <= 10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10.5 <= 10.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "5.5 <= 10.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5.5 <= 10.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); - test_assert(ecs_parse_expr(world, "5.5 <= 5.5", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "5.5 <= 5.5", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_bool_t)); test_assert(v.ptr != NULL); test_bool(*(bool*)v.ptr, true); @@ -1524,11 +1522,11 @@ void DeserExprOperators_cond_lteq_flt(void) { ecs_fini(world); } -void DeserExprOperators_min_lparen_int_rparen(void) { +void Expr_min_lparen_int_rparen(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "-(10)", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-(10)", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(ecs_i64_t*)v.ptr, -10); @@ -1537,11 +1535,11 @@ void DeserExprOperators_min_lparen_int_rparen(void) { ecs_fini(world); } -void DeserExprOperators_min_lparen_int_add_int_rparen(void) { +void Expr_min_lparen_int_add_int_rparen(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "-(10 + 20)", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-(10 + 20)", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(ecs_i64_t*)v.ptr, -30); @@ -1550,35 +1548,34 @@ void DeserExprOperators_min_lparen_int_add_int_rparen(void) { ecs_fini(world); } -void DeserExprOperators_min_var(void) { +void Expr_min_var(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_u64_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_u64_t); *(ecs_u64_t*)var->value.ptr = 10; ecs_value_t v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - test_assert(ecs_parse_expr(world, "-$foo", &v, &desc) != NULL); + ecs_script_expr_run_desc_t desc = { .vars = vars }; + test_assert(ecs_script_expr_run(world, "-$foo", &v, &desc) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(*(ecs_i64_t*)v.ptr, -10); ecs_value_free(world, v.type, v.ptr); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_min_lparen_int_rparen_to_i64(void) { +void Expr_min_lparen_int_rparen_to_i64(void) { ecs_world_t *world = ecs_init(); ecs_i64_t vi = 0; ecs_value_t v = { .type = ecs_id(ecs_i64_t), .ptr = &vi }; - test_assert(ecs_parse_expr(world, "-(10)", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-(10)", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i64_t)); test_assert(v.ptr != NULL); test_int(vi, -10); @@ -1586,12 +1583,12 @@ void DeserExprOperators_min_lparen_int_rparen_to_i64(void) { ecs_fini(world); } -void DeserExprOperators_min_lparen_int_rparen_to_i32(void) { +void Expr_min_lparen_int_rparen_to_i32(void) { ecs_world_t *world = ecs_init(); ecs_i32_t vi = 0; ecs_value_t v = { .type = ecs_id(ecs_i32_t), .ptr = &vi }; - test_assert(ecs_parse_expr(world, "-(10)", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "-(10)", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_i32_t)); test_assert(v.ptr != NULL); test_int(vi, -10); @@ -1599,7 +1596,7 @@ void DeserExprOperators_min_lparen_int_rparen_to_i32(void) { ecs_fini(world); } -void DeserExprOperators_struct_w_min_var(void) { +void Expr_struct_w_min_var(void) { ecs_world_t *world = ecs_init(); typedef struct { @@ -1612,28 +1609,27 @@ void DeserExprOperators_struct_w_min_var(void) { } }); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_u64_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_u64_t); *(ecs_u64_t*)var->value.ptr = 10; Mass v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr(world, "{-$foo}", &(ecs_value_t){ + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run(world, "{-$foo}", &(ecs_value_t){ .type = t, .ptr = &v }, &desc); test_assert(ptr != NULL); test_assert(!ptr[0]); test_uint(v.value, -10); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_struct_w_min_lparen_int_rparen(void) { +void Expr_struct_w_min_lparen_int_rparen(void) { ecs_world_t *world = ecs_init(); typedef struct { @@ -1647,7 +1643,7 @@ void DeserExprOperators_struct_w_min_lparen_int_rparen(void) { }); Mass v = {0}; - const char *ptr = ecs_parse_expr(world, "{-(10)}", &(ecs_value_t){ + const char *ptr = ecs_script_expr_run(world, "{-(10)}", &(ecs_value_t){ .type = t, .ptr = &v }, NULL); test_assert(ptr != NULL); @@ -1658,7 +1654,7 @@ void DeserExprOperators_struct_w_min_lparen_int_rparen(void) { ecs_fini(world); } -void DeserExprOperators_struct_w_min_lparen_var_rparen(void) { +void Expr_struct_w_min_lparen_var_rparen(void) { ecs_world_t *world = ecs_init(); typedef struct { @@ -1671,32 +1667,31 @@ void DeserExprOperators_struct_w_min_lparen_var_rparen(void) { } }); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_u64_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_u64_t); *(ecs_u64_t*)var->value.ptr = 10; Mass v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - const char *ptr = ecs_parse_expr(world, "{-($foo)}", &(ecs_value_t){ + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run(world, "{-($foo)}", &(ecs_value_t){ .type = t, .ptr = &v }, &desc); test_assert(ptr != NULL); test_assert(!ptr[0]); test_uint(v.value, -10); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_shift_left_int(void) { +void Expr_shift_left_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "1 << 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "1 << 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 1 << 2); @@ -1705,11 +1700,11 @@ void DeserExprOperators_shift_left_int(void) { ecs_fini(world); } -void DeserExprOperators_shift_right_int(void) { +void Expr_shift_right_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "4 >> 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "4 >> 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 4 >> 2); @@ -1718,11 +1713,11 @@ void DeserExprOperators_shift_right_int(void) { ecs_fini(world); } -void DeserExprOperators_shift_left_int_add_int(void) { +void Expr_shift_left_int_add_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "1 << 2 + 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "1 << 2 + 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 1 << (2 + 10)); @@ -1731,11 +1726,11 @@ void DeserExprOperators_shift_left_int_add_int(void) { ecs_fini(world); } -void DeserExprOperators_shift_left_int_mul_int(void) { +void Expr_shift_left_int_mul_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "1 << 2 * 10", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "1 << 2 * 10", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 1 << 2 * 10); @@ -1744,11 +1739,11 @@ void DeserExprOperators_shift_left_int_mul_int(void) { ecs_fini(world); } -void DeserExprOperators_add_int_shift_left_int(void) { +void Expr_add_int_shift_left_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 + 1 << 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 1 << 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, (10 + 1) << 2); @@ -1757,11 +1752,11 @@ void DeserExprOperators_add_int_shift_left_int(void) { ecs_fini(world); } -void DeserExprOperators_mul_int_shift_left_int(void) { +void Expr_mul_int_shift_left_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 * 1 << 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 * 1 << 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 10 * 1 << 2); @@ -1770,11 +1765,11 @@ void DeserExprOperators_mul_int_shift_left_int(void) { ecs_fini(world); } -void DeserExprOperators_add_int_shift_left_int_add_int(void) { +void Expr_add_int_shift_left_int_add_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 + 1 << 2 + 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 + 1 << 2 + 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, (10 + 1) << (2 + 2)); @@ -1783,11 +1778,11 @@ void DeserExprOperators_add_int_shift_left_int_add_int(void) { ecs_fini(world); } -void DeserExprOperators_mul_int_shift_left_int_mul_int(void) { +void Expr_mul_int_shift_left_int_mul_int(void) { ecs_world_t *world = ecs_init(); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "10 * 1 << 2 * 2", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "10 * 1 << 2 * 2", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_u64_t)); test_assert(v.ptr != NULL); test_uint(*(uint64_t*)v.ptr, 10 * 1 << 2 * 2); @@ -1796,14 +1791,14 @@ void DeserExprOperators_mul_int_shift_left_int_mul_int(void) { ecs_fini(world); } -void DeserExprOperators_entity_expr(void) { +void Expr_entity_expr(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t foo = ecs_new_entity(world, "foo"); + ecs_entity_t foo = ecs_entity(world, { .name = "foo" }); test_assert(foo != 0); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "foo", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "foo", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_entity_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_entity_t*)v.ptr, foo); @@ -1812,17 +1807,17 @@ void DeserExprOperators_entity_expr(void) { ecs_fini(world); } -void DeserExprOperators_entity_path_expr(void) { +void Expr_entity_path_expr(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t foo = ecs_new_entity(world, "parent.foo"); + ecs_entity_t foo = ecs_entity(world, { .name = "parent.foo" }); test_assert(foo != 0); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "parent.foo", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "parent.foo", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_entity_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_entity_t*)v.ptr, foo); @@ -1831,17 +1826,17 @@ void DeserExprOperators_entity_path_expr(void) { ecs_fini(world); } -void DeserExprOperators_entity_parent_func(void) { +void Expr_entity_parent_func(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t foo = ecs_new_entity(world, "parent.foo"); + ecs_entity_t foo = ecs_entity(world, { .name = "parent.foo" }); test_assert(foo != 0); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "parent.foo.parent()", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "parent.foo.parent()", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_entity_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_entity_t*)v.ptr, parent); @@ -1850,17 +1845,17 @@ void DeserExprOperators_entity_parent_func(void) { ecs_fini(world); } -void DeserExprOperators_entity_name_func(void) { +void Expr_entity_name_func(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t foo = ecs_new_entity(world, "parent.foo"); + ecs_entity_t foo = ecs_entity(world, { .name = "parent.foo" }); test_assert(foo != 0); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "parent.foo.name()", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "parent.foo.name()", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_string_t)); test_assert(v.ptr != NULL); test_str(*(char**)v.ptr, "foo"); @@ -1869,18 +1864,18 @@ void DeserExprOperators_entity_name_func(void) { ecs_fini(world); } -void DeserExprOperators_entity_doc_name_func(void) { +void Expr_entity_doc_name_func(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t foo = ecs_new_entity(world, "parent.foo"); + ecs_entity_t foo = ecs_entity(world, { .name = "parent.foo" }); test_assert(foo != 0); ecs_doc_set_name(world, foo, "FooDoc"); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "parent.foo.doc_name()", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "parent.foo.doc_name()", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_string_t)); test_assert(v.ptr != NULL); test_str(*(char**)v.ptr, "FooDoc"); @@ -1889,17 +1884,17 @@ void DeserExprOperators_entity_doc_name_func(void) { ecs_fini(world); } -void DeserExprOperators_entity_chain_func(void) { +void Expr_entity_chain_func(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t foo = ecs_new_entity(world, "parent.foo"); + ecs_entity_t foo = ecs_entity(world, { .name = "parent.foo" }); test_assert(foo != 0); ecs_value_t v = {0}; - test_assert(ecs_parse_expr(world, "parent.foo.parent().name()", &v, NULL) != NULL); + test_assert(ecs_script_expr_run(world, "parent.foo.parent().name()", &v, NULL) != NULL); test_assert(v.type == ecs_id(ecs_string_t)); test_assert(v.ptr != NULL); test_str(*(char**)v.ptr, "parent"); @@ -1908,310 +1903,301 @@ void DeserExprOperators_entity_chain_func(void) { ecs_fini(world); } -void DeserExprOperators_var_parent_func(void) { +void Expr_var_parent_func(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t child = ecs_new_entity(world, "parent.child"); + ecs_entity_t child = ecs_entity(world, { .name = "parent.child" }); test_assert(child != 0); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare(&vars, "foo", ecs_id(ecs_entity_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_entity_t); test_assert(var != NULL); *(ecs_entity_t*)var->value.ptr = child; ecs_value_t v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - test_assert(ecs_parse_expr(world, "$foo.parent()", &v, &desc) != NULL); + ecs_script_expr_run_desc_t desc = { .vars = vars }; + test_assert(ecs_script_expr_run(world, "$foo.parent()", &v, &desc) != NULL); test_assert(v.type == ecs_id(ecs_entity_t)); test_assert(v.ptr != NULL); test_uint(*(ecs_entity_t*)v.ptr, parent); ecs_value_free(world, v.type, v.ptr); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_var_name_func(void) { +void Expr_var_name_func(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t child = ecs_new_entity(world, "parent.child"); + ecs_entity_t child = ecs_entity(world, { .name = "parent.child" }); test_assert(child != 0); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare(&vars, "foo", ecs_id(ecs_entity_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_entity_t); test_assert(var != NULL); *(ecs_entity_t*)var->value.ptr = child; ecs_value_t v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - test_assert(ecs_parse_expr(world, "$foo.name()", &v, &desc) != NULL); + ecs_script_expr_run_desc_t desc = { .vars = vars }; + test_assert(ecs_script_expr_run(world, "$foo.name()", &v, &desc) != NULL); test_assert(v.type == ecs_id(ecs_string_t)); test_assert(v.ptr != NULL); test_str(*(char**)v.ptr, "child"); ecs_value_free(world, v.type, v.ptr); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_var_doc_name_func(void) { +void Expr_var_doc_name_func(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t child = ecs_new_entity(world, "parent.child"); + ecs_entity_t child = ecs_entity(world, { .name = "parent.child" }); test_assert(child != 0); ecs_doc_set_name(world, child, "ChildDoc"); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare(&vars, "foo", ecs_id(ecs_entity_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_entity_t); test_assert(var != NULL); *(ecs_entity_t*)var->value.ptr = child; ecs_value_t v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - test_assert(ecs_parse_expr(world, "$foo.doc_name()", &v, &desc) != NULL); + ecs_script_expr_run_desc_t desc = { .vars = vars }; + test_assert(ecs_script_expr_run(world, "$foo.doc_name()", &v, &desc) != NULL); test_assert(v.type == ecs_id(ecs_string_t)); test_assert(v.ptr != NULL); test_str(*(char**)v.ptr, "ChildDoc"); ecs_value_free(world, v.type, v.ptr); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_var_chain_func(void) { +void Expr_var_chain_func(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t child = ecs_new_entity(world, "parent.child"); + ecs_entity_t child = ecs_entity(world, { .name = "parent.child" }); test_assert(child != 0); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare(&vars, "foo", ecs_id(ecs_entity_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_entity_t); test_assert(var != NULL); *(ecs_entity_t*)var->value.ptr = child; ecs_value_t v = {0}; - ecs_parse_expr_desc_t desc = { .vars = &vars }; - test_assert(ecs_parse_expr(world, "$foo.parent().name()", &v, &desc) != NULL); + ecs_script_expr_run_desc_t desc = { .vars = vars }; + test_assert(ecs_script_expr_run(world, "$foo.parent().name()", &v, &desc) != NULL); test_assert(v.type == ecs_id(ecs_string_t)); test_assert(v.ptr != NULL); test_str(*(char**)v.ptr, "parent"); ecs_value_free(world, v.type, v.ptr); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_i32_var(void) { +void Expr_interpolate_string_w_i32_var(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_i32_t); test_assert(v != NULL); *(int32_t*)v->value.ptr = 10; - char *result = ecs_interpolate_string(world, "$foo", &vars); + char *result = ecs_script_string_interpolate(world, "$foo", vars); test_assert(result != NULL); test_str(result, "10"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_string_var(void) { +void Expr_interpolate_string_w_string_var(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_string_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_string_t); test_assert(v != NULL); *(ecs_string_t*)v->value.ptr = ecs_os_strdup("Hello World"); - char *result = ecs_interpolate_string(world, "$foo", &vars); + char *result = ecs_script_string_interpolate(world, "$foo", vars); test_assert(result != NULL); test_str(result, "Hello World"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_entity_var(void) { +void Expr_interpolate_string_w_entity_var(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_entity_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_entity_t); test_assert(v != NULL); *(ecs_entity_t*)v->value.ptr = ecs_id(ecs_i32_t); - char *result = ecs_interpolate_string(world, "$foo", &vars); + char *result = ecs_script_string_interpolate(world, "$foo", vars); test_assert(result != NULL); test_str(result, "flecs.meta.i32"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_id_var(void) { +void Expr_interpolate_string_w_id_var(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_id_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_id_t); test_assert(v != NULL); *(ecs_id_t*)v->value.ptr = ecs_id(ecs_i32_t); - char *result = ecs_interpolate_string(world, "$foo", &vars); + char *result = ecs_script_string_interpolate(world, "$foo", vars); test_assert(result != NULL); test_str(result, "flecs.meta.i32"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_var_not_found(void) { +void Expr_interpolate_string_w_var_not_found(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); ecs_log_set_level(-4); - char *result = ecs_interpolate_string(world, "$foo", &vars); + char *result = ecs_script_string_interpolate(world, "$foo", vars); test_assert(result == NULL); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_entity_var_0(void) { +void Expr_interpolate_string_w_entity_var_0(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_entity_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_entity_t); test_assert(v != NULL); *(ecs_entity_t*)v->value.ptr = 0; - char *result = ecs_interpolate_string(world, "$foo", &vars); + char *result = ecs_script_string_interpolate(world, "$foo", vars); test_assert(result != NULL); - test_str(result, "0"); + test_str(result, "#0"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_var_special_chars(void) { +void Expr_interpolate_string_w_var_special_chars(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "_foo_bar_10", ecs_id(ecs_i32_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "_foo_bar_10", ecs_i32_t); test_assert(v != NULL); *(int32_t*)v->value.ptr = 10; - char *result = ecs_interpolate_string(world, "$_foo_bar_10", &vars); + char *result = ecs_script_string_interpolate(world, "$_foo_bar_10", vars); test_assert(result != NULL); test_str(result, "10"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_var_before_after_text(void) { +void Expr_interpolate_string_w_var_before_after_text(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_i32_t); test_assert(v != NULL); *(int32_t*)v->value.ptr = 10; - char *result = ecs_interpolate_string(world, "Hello $foo World", &vars); + char *result = ecs_script_string_interpolate(world, "Hello $foo World", vars); test_assert(result != NULL); test_str(result, "Hello 10 World"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_curly_brackets_var(void) { +void Expr_interpolate_string_w_curly_brackets_var(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_i32_t); test_assert(v != NULL); *(int32_t*)v->value.ptr = 10; - char *result = ecs_interpolate_string(world, "Hello {$foo}World", &vars); + char *result = ecs_script_string_interpolate(world, "Hello {$foo}World", vars); test_assert(result != NULL); test_str(result, "Hello 10World"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_curly_brackets_expr(void) { +void Expr_interpolate_string_w_curly_brackets_expr(void) { ecs_world_t *world = ecs_init(); - char *result = ecs_interpolate_string(world, "Hello {10 + 20}World", NULL); + char *result = ecs_script_string_interpolate(world, "Hello {10 + 20}World", NULL); test_assert(result != NULL); test_str(result, "Hello 30World"); ecs_os_free(result); @@ -2219,83 +2205,81 @@ void DeserExprOperators_interpolate_string_w_curly_brackets_expr(void) { ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_curly_brackets_expr_w_var(void) { +void Expr_interpolate_string_w_curly_brackets_expr_w_var(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_i32_t); test_assert(v != NULL); *(int32_t*)v->value.ptr = 10; - char *result = ecs_interpolate_string(world, "Hello {$foo + 5}World", &vars); + char *result = ecs_script_string_interpolate(world, "Hello {$foo + 5}World", vars); test_assert(result != NULL); test_str(result, "Hello 15World"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_curly_brackets_expr_w_composite_var(void) { +void Expr_interpolate_string_w_curly_brackets_expr_w_composite_var(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); ecs_struct(world, { - .entity = ecs_id(Position), + .entity = ecs_id(PositionI), .members = { { .name = "x", .type = ecs_id(ecs_i32_t) }, { .name = "y", .type = ecs_id(ecs_i32_t) } } }); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(Position)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", PositionI); test_assert(v != NULL); - *(Position*)v->value.ptr = (Position){10, 20}; + *(PositionI*)v->value.ptr = (PositionI){10, 20}; - char *result = ecs_interpolate_string(world, "Hello {$foo.x + $foo.y}World", &vars); + char *result = ecs_script_string_interpolate( + world, "Hello {$foo.x + $foo.y}World", vars); test_assert(result != NULL); test_str(result, "Hello 30World"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_escape_var_operator(void) { +void Expr_interpolate_string_w_escape_var_operator(void) { ecs_world_t *world = ecs_init(); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *v = ecs_vars_declare( - &vars, "foo", ecs_id(ecs_i32_t)); + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_i32_t); test_assert(v != NULL); *(int32_t*)v->value.ptr = 10; - char *result = ecs_interpolate_string(world, "Hello \\$foo World", &vars); + char *result = ecs_script_string_interpolate(world, "Hello \\$foo World", vars); test_assert(result != NULL); test_str(result, "Hello $foo World"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_escape_curly_brackets(void) { +void Expr_interpolate_string_w_escape_curly_brackets(void) { ecs_world_t *world = ecs_init(); - char *result = ecs_interpolate_string(world, "Hello \\{10 + 20}World", NULL); + char *result = ecs_script_string_interpolate(world, "Hello \\{10 + 20}World", NULL); test_assert(result != NULL); test_str(result, "Hello {10 + 20}World"); ecs_os_free(result); @@ -2303,463 +2287,523 @@ void DeserExprOperators_interpolate_string_w_escape_curly_brackets(void) { ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_func(void) { +void Expr_interpolate_string_w_func(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); - ecs_entity_t child = ecs_new_entity(world, "parent.child"); + ecs_entity_t child = ecs_entity(world, { .name = "parent.child" }); test_assert(child != 0); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare(&vars, "foo", ecs_id(ecs_entity_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_entity_t); test_assert(var != NULL); *(ecs_entity_t*)var->value.ptr = child; - char *result = ecs_interpolate_string(world, "Hello {$foo.name()} World", &vars); + char *result = ecs_script_string_interpolate( + world, "Hello {$foo.name()} World", vars); test_assert(result != NULL); test_str(result, "Hello child World"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_interpolate_string_w_func_chain(void) { +void Expr_interpolate_string_w_func_chain(void) { ecs_world_t *world = ecs_init(); - ecs_entity_t parent = ecs_new_entity(world, "parent"); + ecs_entity_t parent = ecs_entity(world, { .name = "parent" }); test_assert(parent != 0); ecs_doc_set_name(world, parent, "Parent"); - ecs_entity_t child = ecs_new_entity(world, "parent.child"); + ecs_entity_t child = ecs_entity(world, { .name = "parent.child" }); test_assert(child != 0); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_expr_var_t *var = ecs_vars_declare(&vars, "foo", ecs_id(ecs_entity_t)); + ecs_script_var_t *var = ecs_script_vars_define( + vars, "foo", ecs_entity_t); test_assert(var != NULL); *(ecs_entity_t*)var->value.ptr = child; - char *result = ecs_interpolate_string(world, "Hello {$foo.parent().doc_name()} World", &vars); + char *result = ecs_script_string_interpolate( + world, "Hello {$foo.parent().doc_name()} World", vars); test_assert(result != NULL); test_str(result, "Hello Parent World"); ecs_os_free(result); - ecs_vars_fini(&vars); + ecs_script_vars_fini(vars); ecs_fini(world); } -void DeserExprOperators_iter_to_vars_no_data(void) { +void Expr_iter_to_vars_no_data(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Foo); - ecs_rule_t *rule = ecs_rule(world, { .expr = "Foo" }); - test_assert(rule != NULL); + ecs_query_t *query = ecs_query(world, { .expr = "Foo" }); + test_assert(query != NULL); - ecs_entity_t e1 = ecs_new(world, Foo); - ecs_entity_t e2 = ecs_new(world, Foo); + ecs_entity_t e1 = ecs_new_w(world, Foo); + ecs_entity_t e2 = ecs_new_w(world, Foo); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_iter_t it = ecs_rule_iter(world, rule); - test_bool(ecs_rule_next(&it), true); + ecs_iter_t it = ecs_query_iter(world, query); + test_bool(ecs_query_next(&it), true); test_int(it.count, 2); test_uint(it.entities[0], e1); test_uint(it.entities[1], e2); - ecs_iter_to_vars(&it, &vars, 0); + ecs_script_vars_from_iter(&it, vars, 0); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "this"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "this"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_uint(*(ecs_entity_t*)v->value.ptr, e1); } - ecs_iter_to_vars(&it, &vars, 1); + ecs_script_vars_from_iter(&it, vars, 1); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "this"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "this"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_uint(*(ecs_entity_t*)v->value.ptr, e2); } - test_bool(ecs_rule_next(&it), false); + test_bool(ecs_query_next(&it), false); - ecs_vars_fini(&vars); - ecs_rule_fini(rule); + ecs_script_vars_fini(vars); + ecs_query_fini(query); ecs_fini(world); } -void DeserExprOperators_iter_to_vars_1_comp(void) { +void Expr_iter_to_vars_1_comp(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); - ecs_rule_t *rule = ecs_rule(world, { .expr = "Position" }); - test_assert(rule != NULL); + ecs_query_t *query = ecs_query(world, { .expr = "PositionI" }); + test_assert(query != NULL); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, PositionI); + ecs_entity_t e2 = ecs_new_w(world, PositionI); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_iter_t it = ecs_rule_iter(world, rule); - test_bool(ecs_rule_next(&it), true); + ecs_iter_t it = ecs_query_iter(world, query); + test_bool(ecs_query_next(&it), true); test_int(it.count, 2); test_uint(it.entities[0], e1); test_uint(it.entities[1], e2); - ecs_iter_to_vars(&it, &vars, 0); + ecs_script_vars_from_iter(&it, vars, 0); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "this"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "this"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_uint(*(ecs_entity_t*)v->value.ptr, e1); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e1, Position)); + test_assert(v->value.ptr == ecs_get(world, e1, PositionI)); } - ecs_iter_to_vars(&it, &vars, 1); + ecs_script_vars_from_iter(&it, vars, 1); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "this"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "this"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_uint(*(ecs_entity_t*)v->value.ptr, e2); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e2, Position)); + test_assert(v->value.ptr == ecs_get(world, e2, PositionI)); } - test_bool(ecs_rule_next(&it), false); + test_bool(ecs_query_next(&it), false); - ecs_vars_fini(&vars); - ecs_rule_fini(rule); + ecs_script_vars_fini(vars); + ecs_query_fini(query); ecs_fini(world); } -void DeserExprOperators_iter_to_vars_2_comps(void) { +void Expr_iter_to_vars_2_comps(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); ECS_COMPONENT(world, Velocity); - ecs_rule_t *rule = ecs_rule(world, { .expr = "Position, Velocity" }); - test_assert(rule != NULL); + ecs_query_t *query = ecs_query(world, { .expr = "PositionI, Velocity" }); + test_assert(query != NULL); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, PositionI); + ecs_entity_t e2 = ecs_new_w(world, PositionI); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_iter_t it = ecs_rule_iter(world, rule); - test_bool(ecs_rule_next(&it), true); + ecs_iter_t it = ecs_query_iter(world, query); + test_bool(ecs_query_next(&it), true); test_int(it.count, 2); test_uint(it.entities[0], e1); test_uint(it.entities[1], e2); - ecs_iter_to_vars(&it, &vars, 0); + ecs_script_vars_from_iter(&it, vars, 0); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "this"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "this"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_uint(*(ecs_entity_t*)v->value.ptr, e1); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e1, Position)); + test_assert(v->value.ptr == ecs_get(world, e1, PositionI)); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "2"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "1"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(Velocity)); test_assert(v->value.ptr != NULL); test_assert(v->value.ptr == ecs_get(world, e1, Velocity)); } - ecs_iter_to_vars(&it, &vars, 1); + ecs_script_vars_from_iter(&it, vars, 1); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "this"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "this"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_uint(*(ecs_entity_t*)v->value.ptr, e2); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e2, Position)); + test_assert(v->value.ptr == ecs_get(world, e2, PositionI)); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "2"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "1"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(Velocity)); test_assert(v->value.ptr != NULL); test_assert(v->value.ptr == ecs_get(world, e2, Velocity)); } - test_bool(ecs_rule_next(&it), false); + test_bool(ecs_query_next(&it), false); - ecs_vars_fini(&vars); - ecs_rule_fini(rule); + ecs_script_vars_fini(vars); + ecs_query_fini(query); ecs_fini(world); } -void DeserExprOperators_iter_to_vars_1_comp_1_tag(void) { +void Expr_iter_to_vars_1_comp_1_tag(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); ECS_COMPONENT(world, Velocity); ECS_TAG(world, Foo); - ecs_rule_t *rule = ecs_rule(world, { .expr = "Position, Foo, Velocity" }); - test_assert(rule != NULL); + ecs_query_t *query = ecs_query(world, { .expr = "PositionI, Foo, Velocity" }); + test_assert(query != NULL); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, PositionI); + ecs_entity_t e2 = ecs_new_w(world, PositionI); ecs_add(world, e1, Velocity); ecs_add(world, e2, Velocity); ecs_add(world, e1, Foo); ecs_add(world, e2, Foo); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_iter_t it = ecs_rule_iter(world, rule); - test_bool(ecs_rule_next(&it), true); + ecs_iter_t it = ecs_query_iter(world, query); + test_bool(ecs_query_next(&it), true); test_int(it.count, 2); test_uint(it.entities[0], e1); test_uint(it.entities[1], e2); - ecs_iter_to_vars(&it, &vars, 0); + ecs_script_vars_from_iter(&it, vars, 0); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "this"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "this"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_uint(*(ecs_entity_t*)v->value.ptr, e1); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e1, Position)); + test_assert(v->value.ptr == ecs_get(world, e1, PositionI)); } - test_assert(ecs_vars_lookup(&vars, "2") == NULL); + test_assert(ecs_script_vars_lookup(vars, "1") == NULL); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "3"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "2"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(Velocity)); test_assert(v->value.ptr != NULL); test_assert(v->value.ptr == ecs_get(world, e1, Velocity)); } - ecs_iter_to_vars(&it, &vars, 1); + ecs_script_vars_from_iter(&it, vars, 1); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "this"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "this"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_uint(*(ecs_entity_t*)v->value.ptr, e2); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e2, Position)); + test_assert(v->value.ptr == ecs_get(world, e2, PositionI)); } - test_assert(ecs_vars_lookup(&vars, "2") == NULL); + test_assert(ecs_script_vars_lookup(vars, "1") == NULL); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "3"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "2"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(Velocity)); test_assert(v->value.ptr != NULL); test_assert(v->value.ptr == ecs_get(world, e2, Velocity)); } - test_bool(ecs_rule_next(&it), false); + test_bool(ecs_query_next(&it), false); - ecs_vars_fini(&vars); - ecs_rule_fini(rule); + ecs_script_vars_fini(vars); + ecs_query_fini(query); ecs_fini(world); } -void DeserExprOperators_iter_to_vars_w_1_query_var(void) { +void Expr_iter_to_vars_w_1_query_var(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); - ecs_rule_t *rule = ecs_rule(world, { .expr = "Position($x)" }); - test_assert(rule != NULL); + ecs_query_t *query = ecs_query(world, { .expr = "PositionI($x)" }); + test_assert(query != NULL); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t e1 = ecs_new_w(world, PositionI); + ecs_entity_t e2 = ecs_new_w(world, PositionI); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_iter_t it = ecs_rule_iter(world, rule); - test_bool(ecs_rule_next(&it), true); + ecs_iter_t it = ecs_query_iter(world, query); + test_bool(ecs_query_next(&it), true); test_int(it.count, 0); - ecs_iter_to_vars(&it, &vars, 0); - test_assert(ecs_vars_lookup(&vars, "this") == NULL); + ecs_script_vars_from_iter(&it, vars, 0); + test_assert(ecs_script_vars_lookup(vars, "this") == NULL); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "x"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "x"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_assert(*(ecs_entity_t*)v->value.ptr == e1); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e1, Position)); + test_assert(v->value.ptr == ecs_get(world, e1, PositionI)); } - test_bool(ecs_rule_next(&it), true); + test_bool(ecs_query_next(&it), true); test_int(it.count, 0); - ecs_iter_to_vars(&it, &vars, 0); - test_assert(ecs_vars_lookup(&vars, "this") == NULL); + ecs_script_vars_from_iter(&it, vars, 0); + test_assert(ecs_script_vars_lookup(vars, "this") == NULL); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "x"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "x"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_assert(*(ecs_entity_t*)v->value.ptr == e2); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e2, Position)); + test_assert(v->value.ptr == ecs_get(world, e2, PositionI)); } - test_bool(ecs_rule_next(&it), false); + test_bool(ecs_query_next(&it), false); - ecs_vars_fini(&vars); - ecs_rule_fini(rule); + ecs_script_vars_fini(vars); + ecs_query_fini(query); ecs_fini(world); } -void DeserExprOperators_iter_to_vars_w_2_query_vars(void) { +void Expr_iter_to_vars_w_2_query_vars(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); - ecs_rule_t *rule = ecs_rule(world, { .expr = "Position($x), ChildOf($x, $y)" }); - test_assert(rule != NULL); + ecs_query_t *query = ecs_query(world, { .expr = "PositionI($x), ChildOf($x, $y)" }); + test_assert(query != NULL); - ecs_entity_t parent = ecs_new_id(world); - ecs_entity_t e1 = ecs_new(world, Position); - ecs_entity_t e2 = ecs_new(world, Position); + ecs_entity_t parent = ecs_new(world); + ecs_entity_t e1 = ecs_new_w(world, PositionI); + ecs_entity_t e2 = ecs_new_w(world, PositionI); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add_pair(world, e2, EcsChildOf, parent); - ecs_vars_t vars = {0}; - ecs_vars_init(world, &vars); + ecs_script_vars_t *vars = ecs_script_vars_init(world); - ecs_iter_t it = ecs_rule_iter(world, rule); - test_bool(ecs_rule_next(&it), true); + ecs_iter_t it = ecs_query_iter(world, query); + test_bool(ecs_query_next(&it), true); test_int(it.count, 0); - ecs_iter_to_vars(&it, &vars, 0); - test_assert(ecs_vars_lookup(&vars, "this") == NULL); + ecs_script_vars_from_iter(&it, vars, 0); + test_assert(ecs_script_vars_lookup(vars, "this") == NULL); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "x"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "x"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_assert(*(ecs_entity_t*)v->value.ptr == e1); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "y"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "y"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_assert(*(ecs_entity_t*)v->value.ptr == parent); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e1, Position)); + test_assert(v->value.ptr == ecs_get(world, e1, PositionI)); } - test_bool(ecs_rule_next(&it), true); + test_bool(ecs_query_next(&it), true); test_int(it.count, 0); - ecs_iter_to_vars(&it, &vars, 0); - test_assert(ecs_vars_lookup(&vars, "this") == NULL); + ecs_script_vars_from_iter(&it, vars, 0); + test_assert(ecs_script_vars_lookup(vars, "this") == NULL); { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "x"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "x"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_assert(*(ecs_entity_t*)v->value.ptr == e2); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "y"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "y"); test_assert(v != NULL); test_assert(v->value.type == ecs_id(ecs_entity_t)); test_assert(v->value.ptr != NULL); test_assert(*(ecs_entity_t*)v->value.ptr == parent); } { - ecs_expr_var_t *v = ecs_vars_lookup(&vars, "1"); + ecs_script_var_t *v = ecs_script_vars_lookup(vars, "0"); test_assert(v != NULL); - test_assert(v->value.type == ecs_id(Position)); + test_assert(v->value.type == ecs_id(PositionI)); test_assert(v->value.ptr != NULL); - test_assert(v->value.ptr == ecs_get(world, e2, Position)); + test_assert(v->value.ptr == ecs_get(world, e2, PositionI)); } - test_bool(ecs_rule_next(&it), false); + test_bool(ecs_query_next(&it), false); - ecs_vars_fini(&vars); - ecs_rule_fini(rule); + ecs_script_vars_fini(vars); + ecs_query_fini(query); + + ecs_fini(world); +} + +void Expr_component_expr(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + ecs_value_t v = {0}; + test_assert(ecs_script_expr_run(world, "e[Position]", &v, NULL) != NULL); + test_assert(v.type == ecs_id(Position)); + test_assert(v.ptr != NULL); + + Position *p = v.ptr; + test_int(p->x, 10); + test_int(p->y, 20); + ecs_value_free(world, v.type, v.ptr); + + ecs_fini(world); +} + +void Expr_component_member_expr(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "Position"}), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_entity_t e = ecs_entity(world, { .name = "e" }); + ecs_set(world, e, Position, {10, 20}); + + ecs_value_t v = {0}; + test_assert(ecs_script_expr_run(world, "e[Position].x", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f32_t)); + test_assert(v.ptr != NULL); + { + float *ptr = v.ptr; + test_int(*ptr, 10); + ecs_value_free(world, v.type, v.ptr); + } + + v = (ecs_value_t){0}; + test_assert(ecs_script_expr_run(world, "e[Position].y", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f32_t)); + test_assert(v.ptr != NULL); + { + float *ptr = v.ptr; + test_int(*ptr, 20); + ecs_value_free(world, v.type, v.ptr); + } ecs_fini(world); } diff --git a/vendors/flecs/test/meta/src/SerializeToExpr.c b/vendors/flecs/test/script/src/Serialize.c similarity index 90% rename from vendors/flecs/test/meta/src/SerializeToExpr.c rename to vendors/flecs/test/script/src/Serialize.c index e7dde63af..89584f5c0 100644 --- a/vendors/flecs/test/meta/src/SerializeToExpr.c +++ b/vendors/flecs/test/script/src/Serialize.c @@ -1,7 +1,7 @@ -#include +#include #include -void SerializeToExpr_bool(void) { +void Serialize_bool(void) { ecs_world_t *world = ecs_init(); { @@ -23,7 +23,7 @@ void SerializeToExpr_bool(void) { ecs_fini(world); } -void SerializeToExpr_byte(void) { +void Serialize_byte(void) { ecs_world_t *world = ecs_init(); { @@ -53,7 +53,7 @@ void SerializeToExpr_byte(void) { ecs_fini(world); } -void SerializeToExpr_char(void) { +void Serialize_char(void) { ecs_world_t *world = ecs_init(); { @@ -91,7 +91,7 @@ void SerializeToExpr_char(void) { ecs_fini(world); } -void SerializeToExpr_i8(void) { +void Serialize_i8(void) { ecs_world_t *world = ecs_init(); { @@ -121,7 +121,7 @@ void SerializeToExpr_i8(void) { ecs_fini(world); } -void SerializeToExpr_i16(void) { +void Serialize_i16(void) { ecs_world_t *world = ecs_init(); { @@ -151,7 +151,7 @@ void SerializeToExpr_i16(void) { ecs_fini(world); } -void SerializeToExpr_i32(void) { +void Serialize_i32(void) { ecs_world_t *world = ecs_init(); { @@ -181,7 +181,7 @@ void SerializeToExpr_i32(void) { ecs_fini(world); } -void SerializeToExpr_i64(void) { +void Serialize_i64(void) { ecs_world_t *world = ecs_init(); { @@ -211,7 +211,7 @@ void SerializeToExpr_i64(void) { ecs_fini(world); } -void SerializeToExpr_iptr(void) { +void Serialize_iptr(void) { ecs_world_t *world = ecs_init(); { @@ -241,7 +241,7 @@ void SerializeToExpr_iptr(void) { ecs_fini(world); } -void SerializeToExpr_u8(void) { +void Serialize_u8(void) { ecs_world_t *world = ecs_init(); { @@ -263,7 +263,7 @@ void SerializeToExpr_u8(void) { ecs_fini(world); } -void SerializeToExpr_u16(void) { +void Serialize_u16(void) { ecs_world_t *world = ecs_init(); { @@ -285,7 +285,7 @@ void SerializeToExpr_u16(void) { ecs_fini(world); } -void SerializeToExpr_u32(void) { +void Serialize_u32(void) { ecs_world_t *world = ecs_init(); { @@ -307,7 +307,7 @@ void SerializeToExpr_u32(void) { ecs_fini(world); } -void SerializeToExpr_u64(void) { +void Serialize_u64(void) { ecs_world_t *world = ecs_init(); { @@ -337,7 +337,7 @@ void SerializeToExpr_u64(void) { ecs_fini(world); } -void SerializeToExpr_uptr(void) { +void Serialize_uptr(void) { ecs_world_t *world = ecs_init(); { @@ -359,7 +359,7 @@ void SerializeToExpr_uptr(void) { ecs_fini(world); } -void SerializeToExpr_float(void) { +void Serialize_float(void) { ecs_world_t *world = ecs_init(); { @@ -389,7 +389,7 @@ void SerializeToExpr_float(void) { ecs_fini(world); } -void SerializeToExpr_double(void) { +void Serialize_double(void) { ecs_world_t *world = ecs_init(); { @@ -419,7 +419,7 @@ void SerializeToExpr_double(void) { ecs_fini(world); } -void SerializeToExpr_float_nan(void) { +void Serialize_float_nan(void) { ecs_world_t *world = ecs_init(); { @@ -433,7 +433,7 @@ void SerializeToExpr_float_nan(void) { ecs_fini(world); } -void SerializeToExpr_float_inf(void) { +void Serialize_float_inf(void) { ecs_world_t *world = ecs_init(); { @@ -455,7 +455,7 @@ void SerializeToExpr_float_inf(void) { ecs_fini(world); } -void SerializeToExpr_double_nan(void) { +void Serialize_double_nan(void) { ecs_world_t *world = ecs_init(); { @@ -469,7 +469,7 @@ void SerializeToExpr_double_nan(void) { ecs_fini(world); } -void SerializeToExpr_double_inf(void) { +void Serialize_double_inf(void) { ecs_world_t *world = ecs_init(); { @@ -491,7 +491,7 @@ void SerializeToExpr_double_inf(void) { ecs_fini(world); } -void SerializeToExpr_string(void) { +void Serialize_string(void) { ecs_world_t *world = ecs_init(); { @@ -529,14 +529,14 @@ void SerializeToExpr_string(void) { ecs_fini(world); } -void SerializeToExpr_entity(void) { +void Serialize_entity(void) { ecs_world_t *world = ecs_init(); { ecs_entity_t value = 0; char *expr = ecs_ptr_to_expr(world, ecs_id(ecs_entity_t), &value); test_assert(expr != NULL); - test_str(expr, "0"); + test_str(expr, "#0"); ecs_os_free(expr); } @@ -551,14 +551,28 @@ void SerializeToExpr_entity(void) { ecs_fini(world); } -void SerializeToExpr_id(void) { +void Serialize_entity_10k(void) { + ecs_world_t *world = ecs_init(); + + { + ecs_entity_t value = 10000; + char *expr = ecs_ptr_to_expr(world, ecs_id(ecs_entity_t), &value); + test_assert(expr != NULL); + test_str(expr, "#10000"); + ecs_os_free(expr); + } + + ecs_fini(world); +} + +void Serialize_id(void) { ecs_world_t *world = ecs_init(); { ecs_id_t value = 0; char *expr = ecs_ptr_to_expr(world, ecs_id(ecs_id_t), &value); test_assert(expr != NULL); - test_str(expr, "0"); + test_str(expr, "#0"); ecs_os_free(expr); } @@ -581,7 +595,7 @@ void SerializeToExpr_id(void) { ecs_fini(world); } -void SerializeToExpr_enum(void) { +void Serialize_enum(void) { typedef enum { Red, Blue, Green } T; @@ -630,7 +644,7 @@ void SerializeToExpr_enum(void) { ecs_fini(world); } -void SerializeToExpr_bitmask(void) { +void Serialize_bitmask(void) { uint32_t Lettuce = 0x1; uint32_t Bacon = 0x1 << 1; uint32_t Tomato = 0x1 << 2; @@ -696,7 +710,7 @@ void SerializeToExpr_bitmask(void) { ecs_fini(world); } -void SerializeToExpr_struct_enum(void) { +void Serialize_struct_enum(void) { typedef enum { Red, Blue, Green } E; @@ -758,7 +772,7 @@ void SerializeToExpr_struct_enum(void) { ecs_fini(world); } -void SerializeToExpr_struct_bitmask(void) { +void Serialize_struct_bitmask(void) { typedef struct { uint32_t x; } T; @@ -837,7 +851,7 @@ void SerializeToExpr_struct_bitmask(void) { ecs_fini(world); } -void SerializeToExpr_struct_i32(void) { +void Serialize_struct_i32(void) { typedef struct { int32_t x; } T; @@ -860,7 +874,7 @@ void SerializeToExpr_struct_i32(void) { ecs_fini(world); } -void SerializeToExpr_struct_i32_i32(void) { +void Serialize_struct_i32_i32(void) { typedef struct { int32_t x; int32_t y; @@ -885,7 +899,7 @@ void SerializeToExpr_struct_i32_i32(void) { ecs_fini(world); } -void SerializeToExpr_struct_entity(void) { +void Serialize_struct_entity(void) { typedef struct { ecs_entity_t e; } T; @@ -908,7 +922,7 @@ void SerializeToExpr_struct_entity(void) { ecs_fini(world); } -void SerializeToExpr_struct_id(void) { +void Serialize_struct_id(void) { typedef struct { ecs_id_t e; } T; @@ -931,7 +945,7 @@ void SerializeToExpr_struct_id(void) { ecs_fini(world); } -void SerializeToExpr_entity_entity_after_float(void) { +void Serialize_entity_entity_after_float(void) { typedef struct { int32_t v; ecs_entity_t e; @@ -956,7 +970,7 @@ void SerializeToExpr_entity_entity_after_float(void) { ecs_fini(world); } -void SerializeToExpr_struct_nested_i32(void) { +void Serialize_struct_nested_i32(void) { typedef struct { ecs_i32_t x; } N1; @@ -990,7 +1004,7 @@ void SerializeToExpr_struct_nested_i32(void) { ecs_fini(world); } -void SerializeToExpr_struct_nested_i32_i32(void) { +void Serialize_struct_nested_i32_i32(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -1026,7 +1040,7 @@ void SerializeToExpr_struct_nested_i32_i32(void) { ecs_fini(world); } -void SerializeToExpr_struct_2_nested_i32_i32(void) { +void Serialize_struct_2_nested_i32_i32(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -1064,7 +1078,7 @@ void SerializeToExpr_struct_2_nested_i32_i32(void) { ecs_fini(world); } -void SerializeToExpr_struct_i32_array_3(void) { +void Serialize_struct_i32_array_3(void) { typedef struct { int32_t x[3]; } T; @@ -1087,7 +1101,7 @@ void SerializeToExpr_struct_i32_array_3(void) { ecs_fini(world); } -void SerializeToExpr_struct_struct_i32_array_3(void) { +void Serialize_struct_struct_i32_array_3(void) { typedef struct { ecs_i32_t x; } N1; @@ -1121,7 +1135,7 @@ void SerializeToExpr_struct_struct_i32_array_3(void) { ecs_fini(world); } -void SerializeToExpr_struct_struct_i32_i32_array_3(void) { +void Serialize_struct_struct_i32_i32_array_3(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -1157,7 +1171,7 @@ void SerializeToExpr_struct_struct_i32_i32_array_3(void) { ecs_fini(world); } -void SerializeToExpr_struct_w_array_type_i32_i32(void) { +void Serialize_struct_w_array_type_i32_i32(void) { typedef int32_t N1[2]; typedef struct { @@ -1188,7 +1202,7 @@ void SerializeToExpr_struct_w_array_type_i32_i32(void) { ecs_fini(world); } -void SerializeToExpr_struct_w_2_array_type_i32_i32(void) { +void Serialize_struct_w_2_array_type_i32_i32(void) { typedef int32_t N1[2]; typedef struct { @@ -1221,7 +1235,7 @@ void SerializeToExpr_struct_w_2_array_type_i32_i32(void) { ecs_fini(world); } -void SerializeToExpr_struct_w_array_type_struct(void) { +void Serialize_struct_w_array_type_struct(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -1271,7 +1285,7 @@ void SerializeToExpr_struct_w_array_type_struct(void) { ecs_fini(world); } -void SerializeToExpr_struct_w_2_array_type_struct(void) { +void Serialize_struct_w_2_array_type_struct(void) { typedef struct { ecs_i32_t x; ecs_i32_t y; @@ -1327,19 +1341,19 @@ void SerializeToExpr_struct_w_2_array_type_struct(void) { ecs_fini(world); } -void SerializeToExpr_struct_partial(void) { +void Serialize_struct_partial(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(Position), + .entity = ecs_id(PositionI), .members = { - {"y", ecs_id(ecs_i32_t), .offset = offsetof(Position, y)} + {"y", ecs_id(ecs_i32_t), .offset = offsetof(PositionI, y)} } }); - Position value = {10, 20}; + PositionI value = {10, 20}; char *expr = ecs_ptr_to_expr(world, t, &value); test_assert(expr != NULL); test_str(expr, "{y: 20}"); @@ -1348,7 +1362,7 @@ void SerializeToExpr_struct_partial(void) { ecs_fini(world); } -void SerializeToExpr_array_i32_3(void) { +void Serialize_array_i32_3(void) { ecs_world_t *world = ecs_init(); ecs_entity_t t = ecs_array_init(world, &(ecs_array_desc_t){ @@ -1365,13 +1379,13 @@ void SerializeToExpr_array_i32_3(void) { ecs_fini(world); } -void SerializeToExpr_array_struct_i32_i32(void) { +void Serialize_array_struct_i32_i32(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(Position), + .entity = ecs_id(PositionI), .members = { {"x", ecs_id(ecs_i32_t)}, {"y", ecs_id(ecs_i32_t)} @@ -1379,11 +1393,11 @@ void SerializeToExpr_array_struct_i32_i32(void) { }); ecs_entity_t t = ecs_array_init(world, &(ecs_array_desc_t){ - .type = ecs_id(Position), + .type = ecs_id(PositionI), .count = 3 }); - Position value[] = {{10, 20}, {30, 40}, {50, 60}}; + PositionI value[] = {{10, 20}, {30, 40}, {50, 60}}; char *expr = ecs_ptr_to_expr(world, t, value); test_assert(expr != NULL); test_str(expr, "[{x: 10, y: 20}, {x: 30, y: 40}, {x: 50, y: 60}]"); @@ -1392,7 +1406,7 @@ void SerializeToExpr_array_struct_i32_i32(void) { ecs_fini(world); } -void SerializeToExpr_array_array_i32_3(void) { +void Serialize_array_array_i32_3(void) { ecs_world_t *world = ecs_init(); ecs_entity_t a = ecs_array_init(world, &(ecs_array_desc_t){ @@ -1416,7 +1430,7 @@ void SerializeToExpr_array_array_i32_3(void) { ecs_fini(world); } -void SerializeToExpr_vector_i32_3(void) { +void Serialize_vector_i32_3(void) { ecs_world_t *world = ecs_init(); ecs_entity_t t = ecs_vector_init(world, &(ecs_vector_desc_t){ @@ -1439,13 +1453,13 @@ void SerializeToExpr_vector_i32_3(void) { ecs_fini(world); } -void SerializeToExpr_vector_struct_i32_i32(void) { +void Serialize_vector_struct_i32_i32(void) { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, PositionI); ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(Position), + .entity = ecs_id(PositionI), .members = { {"x", ecs_id(ecs_i32_t)}, {"y", ecs_id(ecs_i32_t)} @@ -1453,26 +1467,26 @@ void SerializeToExpr_vector_struct_i32_i32(void) { }); ecs_entity_t t = ecs_vector_init(world, &(ecs_vector_desc_t){ - .type = ecs_id(Position) + .type = ecs_id(PositionI) }); ecs_vec_t v; - ecs_vec_init_t(NULL, &v, Position, 3); - ecs_vec_append_t(NULL, &v, Position)[0] = (Position){10, 20}; - ecs_vec_append_t(NULL, &v, Position)[0] = (Position){30, 40}; - ecs_vec_append_t(NULL, &v, Position)[0] = (Position){50, 60}; + ecs_vec_init_t(NULL, &v, PositionI, 3); + ecs_vec_append_t(NULL, &v, PositionI)[0] = (PositionI){10, 20}; + ecs_vec_append_t(NULL, &v, PositionI)[0] = (PositionI){30, 40}; + ecs_vec_append_t(NULL, &v, PositionI)[0] = (PositionI){50, 60}; char *expr = ecs_ptr_to_expr(world, t, &v); test_assert(expr != NULL); test_str(expr, "[{x: 10, y: 20}, {x: 30, y: 40}, {x: 50, y: 60}]"); ecs_os_free(expr); - ecs_vec_fini_t(NULL, &v, Position); + ecs_vec_fini_t(NULL, &v, PositionI); ecs_fini(world); } -void SerializeToExpr_vector_array_i32_3(void) { +void Serialize_vector_array_i32_3(void) { ecs_world_t *world = ecs_init(); ecs_entity_t a = ecs_array_init(world, &(ecs_array_desc_t){ @@ -1514,60 +1528,60 @@ void SerializeToExpr_vector_array_i32_3(void) { ecs_fini(world); } -void SerializeToExpr_escape_simple_string(void) { +void Serialize_escape_simple_string(void) { ecs_world_t *world = ecs_init(); - char *str = ecs_astresc('"', "Hello World"); + char *str = flecs_astresc('"', "Hello World"); test_str(str, "Hello World"); ecs_os_free(str); ecs_fini(world); } -void SerializeToExpr_escape_newline(void) { +void Serialize_escape_newline(void) { ecs_world_t *world = ecs_init(); - char *str = ecs_astresc('"', "\n"); + char *str = flecs_astresc('"', "\n"); test_str(str, "\\n"); ecs_os_free(str); ecs_fini(world); } -void SerializeToExpr_escape_2_newlines(void) { +void Serialize_escape_2_newlines(void) { ecs_world_t *world = ecs_init(); - char *str = ecs_astresc('"', "\n\n"); + char *str = flecs_astresc('"', "\n\n"); test_str(str, "\\n\\n"); ecs_os_free(str); ecs_fini(world); } -void SerializeToExpr_escape_string_w_trailing_newline(void) { +void Serialize_escape_string_w_trailing_newline(void) { ecs_world_t *world = ecs_init(); - char *str = ecs_astresc('"', "Hello World\n"); + char *str = flecs_astresc('"', "Hello World\n"); test_str(str, "Hello World\\n"); ecs_os_free(str); ecs_fini(world); } -void SerializeToExpr_escape_string_w_2_trailing_newlines(void) { +void Serialize_escape_string_w_2_trailing_newlines(void) { ecs_world_t *world = ecs_init(); - char *str = ecs_astresc('"', "Hello World\n\n"); + char *str = flecs_astresc('"', "Hello World\n\n"); test_str(str, "Hello World\\n\\n"); ecs_os_free(str); ecs_fini(world); } -void SerializeToExpr_escape_string_w_delim(void) { +void Serialize_escape_string_w_delim(void) { ecs_world_t *world = ecs_init(); - char *str = ecs_astresc('"', "\"Hello World\""); + char *str = flecs_astresc('"', "\"Hello World\""); test_str(str, "\\\"Hello World\\\""); ecs_os_free(str); diff --git a/vendors/flecs/test/script/src/Template.c b/vendors/flecs/test/script/src/Template.c new file mode 100644 index 000000000..e44ae3dba --- /dev/null +++ b/vendors/flecs/test/script/src/Template.c @@ -0,0 +1,1937 @@ +#include + +void Template_template_no_scope(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Tree"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_fini(world); +} + +void Template_template_no_props(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + const char *expr = + HEAD "Foo {}" + LINE "template Tree {" + LINE " Foo" + LINE "}" + LINE "Tree ent()"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + ecs_entity_t ent = ecs_lookup(world, "ent"); + test_assert(ent != 0); + test_assert(ecs_has_id(world, ent, foo)); + + ecs_fini(world); +} + +void Template_template_prop(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop height = flecs.meta.f32: 0" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + + ecs_fini(world); +} + +void Template_template_prop_space_colon(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop height = flecs.meta.f32: 0" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + + ecs_fini(world); +} + +void Template_template_2_props(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.i32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_i32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + ecs_fini(world); +} + +void Template_template_w_using(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "using flecs.meta" + LINE "Foo {}" + LINE "template Tree {" + LINE " prop width = i32: 10" + LINE " prop height = f32: 20" + LINE " Foo" + LINE "}" + LINE "Tree ent()"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + + ecs_entity_t ent = ecs_lookup(world, "ent"); + test_assert(ent != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_i32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, ent, tree)); + test_assert(ecs_has_id(world, ent, foo)); + + const void *ptr = ecs_get_id(world, ent, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 10, height: 20}"); + ecs_os_free(str); + + ecs_fini(world); +} + +void Template_template_instance_w_default_values(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 10" + LINE " prop height = flecs.meta.f32: 20" + LINE "}" + LINE "" + LINE "e { Tree }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 10, height: 20}"); + ecs_os_free(str); + + ecs_fini(world); +} + +void Template_template_instance_w_assign_default_values(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 10" + LINE " prop height = flecs.meta.f32: 20" + LINE "}" + LINE "" + LINE "e { Tree: {} }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 10, height: 20}"); + ecs_os_free(str); + + ecs_fini(world); +} + +void Template_template_instance_w_overridden_values(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 10" + LINE " prop height = flecs.meta.f32: 20" + LINE "}" + LINE "" + LINE "e { Tree: {width: 30, height: 40} }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 30, height: 40}"); + ecs_os_free(str); + + ecs_fini(world); +} + +void Template_template_w_child(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " child { Position: {$width * 10 + 1, $height * 20 + 2} }" + LINE "}" + LINE "" + LINE "e { Tree: {width: 1, height: 2} }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + ecs_entity_t root_child = ecs_lookup(world, "child"); + test_assert(root_child == 0); + + ecs_entity_t child = ecs_lookup(world, "e.child"); + test_assert(child != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 1, height: 2}"); + ecs_os_free(str); + + const Position *p = ecs_get(world, child, Position); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 42); + + ecs_fini(world); +} + +void Template_template_w_child_parse_script(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " child { Position: {$width * 10 + 1, $height * 20 + 2} }" + LINE "}" + LINE "" + LINE "e { Tree: {width: 1, height: 2} }" + LINE ""; + + test_assert(ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }) != 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + ecs_entity_t child = ecs_lookup(world, "e.child"); + test_assert(child != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 1, height: 2}"); + ecs_os_free(str); + + const Position *p = ecs_get(world, child, Position); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 42); + + ecs_fini(world); +} + +void Template_template_w_child_parse_script_twice(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " child { Position: {$width * 10 + 1, $height * 20 + 2} }" + LINE "}" + LINE "" + LINE "e { Tree: {width: 1, height: 2} }" + LINE ""; + + test_assert(ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }) != 0); + + test_assert(ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }) != 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + ecs_entity_t child = ecs_lookup(world, "e.child"); + test_assert(child != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 1, height: 2}"); + ecs_os_free(str); + + const Position *p = ecs_get(world, child, Position); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 42); + + ecs_fini(world); +} + +void Template_template_w_child_update_after_parse(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " child { Position: {$width * 10 + 1, $height * 20 + 2} }" + LINE "}" + LINE "" + LINE "e { Tree: {width: 1, height: 2} }" + LINE ""; + + test_assert(ecs_script(world, { + .entity = ecs_entity(world, { .name = "main" }), + .code = expr + }) != 0); + + { + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + ecs_entity_t child = ecs_lookup(world, "e.child"); + test_assert(child != 0); + } + + test_assert(!ecs_is_deferred(world)); + + const char *expr_update = + LINE "e { Tree: {width: 3, height: 4} }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr_update) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + ecs_entity_t child = ecs_lookup(world, "e.child"); + test_assert(child != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 3, height: 4}"); + ecs_os_free(str); + + const Position *p = ecs_get(world, child, Position); + test_assert(p != NULL); + test_int(p->x, 31); + test_int(p->y, 82); + + ecs_fini(world); +} + +void Template_template_w_nested_child(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " child {" + LINE " Position: {$width, $height}" + LINE " grand_child { Position: {$height, $width} }" + LINE " }" + LINE "}" + LINE "" + LINE "e { Tree: {width: 1, height: 2} }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + ecs_entity_t child = ecs_lookup(world, "e.child"); + test_assert(child != 0); + + ecs_entity_t grand_child = ecs_lookup(world, "e.child.grand_child"); + test_assert(grand_child != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 1, height: 2}"); + ecs_os_free(str); + + { + const Position *p = ecs_get(world, child, Position); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + { + const Position *p = ecs_get(world, grand_child, Position); + test_assert(p != NULL); + test_int(p->x, 2); + test_int(p->y, 1); + } + + ecs_fini(world); +} + +void Template_template_w_prefab(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " Prefab base {" + LINE " Velocity: {$width * 2, $height * 3}" + LINE " }" + LINE " child : base {" + LINE " Position: {$width, $height}" + LINE " }" + LINE "}" + LINE "" + LINE "e { Tree: {width: 1, height: 2} }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + ecs_entity_t child = ecs_lookup(world, "e.child"); + test_assert(child != 0); + + ecs_entity_t base = ecs_lookup(world, "e.base"); + test_assert(base != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 1, height: 2}"); + ecs_os_free(str); + + test_assert(ecs_has_pair(world, child, EcsIsA, base)); + + { + const Position *p = ecs_get(world, child, Position); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + { + const Velocity *v = ecs_get(world, child, Velocity); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 6); + } + + { + const Velocity *v = ecs_get(world, base, Velocity); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 6); + } + + ecs_fini(world); +} + +void Template_template_w_prefab_tree(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + ecs_struct(world, { + .entity = ecs_id(Velocity), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " Prefab base {" + LINE " Velocity: {$width * 2, $height * 3}" + LINE " Prefab child {" + LINE " Velocity: {$height * 3, $width * 2}" + LINE " }" + LINE " }" + LINE " child : base {" + LINE " Position: {$width, $height}" + LINE " }" + LINE "}" + LINE "" + LINE "e { Tree: {width: 1, height: 2} }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + + ecs_entity_t child = ecs_lookup(world, "e.child"); + test_assert(child != 0); + + ecs_entity_t base = ecs_lookup(world, "e.base"); + test_assert(base != 0); + + ecs_entity_t base_child = ecs_lookup(world, "e.base.child"); + test_assert(base_child != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + test_assert(ecs_has_id(world, e, tree)); + const void *ptr = ecs_get_id(world, e, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_assert(str != NULL); + test_str(str, "{width: 1, height: 2}"); + ecs_os_free(str); + + test_assert(ecs_has_pair(world, child, EcsIsA, base)); + + { + const Position *p = ecs_get(world, child, Position); + test_assert(p != NULL); + test_int(p->x, 1); + test_int(p->y, 2); + } + + { + const Velocity *v = ecs_get(world, child, Velocity); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 6); + } + + { + const Velocity *v = ecs_get(world, base, Velocity); + test_assert(v != NULL); + test_int(v->x, 2); + test_int(v->y, 6); + } + + { + const Velocity *v = ecs_get(world, base_child, Velocity); + test_assert(v != NULL); + test_int(v->x, 6); + test_int(v->y, 2); + } + + ecs_fini(world); +} + +void Template_template_w_nested_template(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " child {" + LINE " Position: {$width, $height}" + LINE " }" + LINE "}" + LINE "" + LINE "template Forest {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " tree_1 { Tree: {-$width, -$height} }" + LINE " tree_2 { Tree: {$width + 1, $height + 1} }" + LINE "}" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t forest = ecs_lookup(world, "Forest"); + test_assert(forest != 0); + + test_assert(ecs_lookup(world, "tree_1") == 0); + test_assert(ecs_lookup(world, "tree_2") == 0); + test_assert(ecs_lookup(world, "Forest.tree_1.child") == 0); + test_assert(ecs_lookup(world, "Forest.tree_2.child") == 0); + + { + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + } + + { + const EcsStruct *st = ecs_get(world, forest, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + } + + test_assert(!ecs_is_deferred(world)); + + const char *expr_instance = + LINE "f { Forest: {10, 20} }" + LINE ""; + test_assert(ecs_script_run(world, NULL, expr_instance) == 0); + + ecs_entity_t f = ecs_lookup(world, "f"); + test_assert(f != 0); + ecs_entity_t f_tree_1 = ecs_lookup(world, "f.tree_1"); + test_assert(f_tree_1 != 0); + ecs_entity_t f_tree_2 = ecs_lookup(world, "f.tree_2"); + test_assert(f_tree_2 != 0); + ecs_entity_t f_tree_1_child = ecs_lookup(world, "f.tree_1.child"); + test_assert(f_tree_1_child != 0); + ecs_entity_t f_tree_2_child = ecs_lookup(world, "f.tree_2.child"); + test_assert(f_tree_2_child != 0); + + { + const void *ptr = ecs_get_id(world, f, forest); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, forest, ptr); + test_str(str, "{width: 10, height: 20}"); + ecs_os_free(str); + } + { + const void *ptr = ecs_get_id(world, f_tree_1, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_str(str, "{width: -10, height: -20}"); + ecs_os_free(str); + } + { + const void *ptr = ecs_get_id(world, f_tree_2, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_str(str, "{width: 11, height: 21}"); + ecs_os_free(str); + } + + { + const Position *p = ecs_get(world, f_tree_1_child, Position); + test_assert(p != NULL); + test_int(p->x, -10); + test_int(p->y, -20); + } + { + const Position *p = ecs_get(world, f_tree_2_child, Position); + test_assert(p != NULL); + test_int(p->x, 11); + test_int(p->y, 21); + } + + ecs_fini(world); +} + +void Template_instantiate_prefab_w_template(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop width = flecs.meta.f32: 0" + LINE " prop height = flecs.meta.f32: 0" + LINE " child { Position: {$width, $height} }" + LINE "}" + LINE "" + LINE "Prefab p { Tree: {width: 10, height: 20} }" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t p = ecs_lookup(world, "p"); + test_assert(p != 0); + test_assert(ecs_lookup(world, "p.child") == 0); + test_assert(ecs_lookup(world, "child") == 0); + + const char *expr_instance = + LINE "e : p" + LINE ""; + test_assert(ecs_script_run(world, NULL, expr_instance) == 0); + + ecs_entity_t e = ecs_lookup(world, "e"); + test_assert(e != 0); + ecs_entity_t child = ecs_lookup(world, "e.child"); + test_assert(child != 0); + + test_assert(ecs_lookup(world, "e.child") != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 2); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t)); + + { + const void *ptr = ecs_get_id(world, p, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_str(str, "{width: 10, height: 20}"); + ecs_os_free(str); + } + + test_assert(ecs_has_pair(world, e, EcsIsA, p)); + + { + const Position *p = ecs_get(world, child, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Template_template_w_prefab_w_template(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Tree {" + LINE " prop count = flecs.meta.f32: 0" + LINE "}" + LINE "" + LINE "template Forest {" + LINE " prop count = flecs.meta.f32: 0" + LINE "" + LINE " Prefab TreePrefab {" + LINE " Tree: {count: $count}" + LINE " }" + LINE "" + LINE " child : TreePrefab" + LINE "}" + LINE "f { Forest: {10} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t forest = ecs_lookup(world, "Forest"); + test_assert(forest != 0); + + ecs_entity_t f = ecs_lookup(world, "f"); + test_assert(f != 0); + ecs_entity_t child = ecs_lookup(world, "f.child"); + test_assert(child != 0); + + { + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + } + + { + const EcsStruct *st = ecs_get(world, forest, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + } + + { + const void *ptr = ecs_get_id(world, f, forest); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, forest, ptr); + test_str(str, "{count: 10}"); + ecs_os_free(str); + } + + ecs_fini(world); +} + +void Template_3_templates(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Tree {" + LINE " prop t = flecs.meta.f32: 10" + LINE "}" + LINE "" + LINE "template Forest {" + LINE " prop f = flecs.meta.f32: 20" + LINE "}" + LINE "" + LINE "template Park {" + LINE " prop p = flecs.meta.f32: 30" + LINE "}" + LINE "" + LINE "Tree a()" + LINE "Forest b()" + LINE "Park c()" + LINE ""; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t forest = ecs_lookup(world, "Forest"); + test_assert(forest != 0); + ecs_entity_t park = ecs_lookup(world, "Park"); + test_assert(park != 0); + + ecs_entity_t a = ecs_lookup(world, "a"); + test_assert(a != 0); + ecs_entity_t b = ecs_lookup(world, "b"); + test_assert(b != 0); + ecs_entity_t c = ecs_lookup(world, "c"); + test_assert(c != 0); + + { + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "t"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + } + { + const EcsStruct *st = ecs_get(world, forest, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "f"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + } + { + const EcsStruct *st = ecs_get(world, park, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "p"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + } + + { + const void *ptr = ecs_get_id(world, a, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_str(str, "{t: 10}"); + ecs_os_free(str); + } + { + const void *ptr = ecs_get_id(world, b, forest); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, forest, ptr); + test_str(str, "{f: 20}"); + ecs_os_free(str); + } + { + const void *ptr = ecs_get_id(world, c, park); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, park, ptr); + test_str(str, "{p: 30}"); + ecs_os_free(str); + } + + ecs_fini(world); +} + +void Template_template_nested_w_default_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop count = flecs.meta.f32: 0" + LINE " trunk { Position: {$count, $count * 2} }" + LINE "}" + LINE "" + LINE "template Forest {" + LINE " prop count = flecs.meta.f32: 0" + LINE " child { Tree: {count:$} }" + LINE "}" + LINE "f { Forest: {10} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t forest = ecs_lookup(world, "Forest"); + test_assert(forest != 0); + + ecs_entity_t f = ecs_lookup(world, "f"); + test_assert(f != 0); + ecs_entity_t child = ecs_lookup(world, "f.child"); + test_assert(child != 0); + ecs_entity_t trunk = ecs_lookup(world, "f.child.trunk"); + test_assert(trunk != 0); + + { + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + } + + { + const EcsStruct *st = ecs_get(world, forest, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + } + + { + const void *ptr = ecs_get_id(world, f, forest); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, forest, ptr); + test_str(str, "{count: 10}"); + ecs_os_free(str); + } + { + const void *ptr = ecs_get_id(world, child, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_str(str, "{count: 10}"); + ecs_os_free(str); + } + + { + const Position *p = ecs_get(world, trunk, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Template_template_w_anonymous(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop count = flecs.meta.i32: 0" + LINE " _ { Position: {$count, $count * 2} }" + LINE " _ { Position: {$count, $count * 2} }" + LINE "}" + LINE "" + LINE "t { Tree: {10} }"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t t = ecs_lookup(world, "t"); + test_assert(t != 0); + + { + ecs_query_t *f = ecs_query(world, { + .terms = { + { .id = ecs_childof(t) }, + { .id = ecs_id(Position) }, + } + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_int(2, ecs_iter_count(&it)); + ecs_query_fini(f); + } + + ecs_fini(world); +} + +void Template_template_w_anonymous_parse_again(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop count = flecs.meta.i32: 0" + LINE " _ { Position: {$count, $count * 2} }" + LINE " _ { Position: {$count, $count * 2} }" + LINE "}" + LINE "" + LINE "t { Tree: {10} }"; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + { + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t t = ecs_lookup(world, "t"); + test_assert(t != 0); + + { + ecs_query_t *f = ecs_query(world, { + .terms = { + { .id = ecs_childof(t) }, + { .id = ecs_id(Position) }, + } + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_assert(ecs_get_name(world, it.entities[0]) == NULL); + Position *p = ecs_field(&it, Position, 1); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 10); test_int(p[1].y, 20); + test_bool(false, ecs_query_next(&it)); + ecs_query_fini(f); + } + } + + const char *expr_again = + LINE "t { Tree: {10} }"; + test_assert(ecs_script_run(world, NULL, expr_again) == 0); + + { + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t t = ecs_lookup(world, "t"); + test_assert(t != 0); + + { + ecs_query_t *f = ecs_query(world, { + .terms = { + { .id = ecs_childof(t) }, + { .id = ecs_id(Position) }, + } + }); + + ecs_iter_t it = ecs_query_iter(world, f); + test_bool(true, ecs_query_next(&it)); + test_int(2, it.count); + test_assert(ecs_get_name(world, it.entities[0]) == NULL); + Position *p = ecs_field(&it, Position, 1); + test_int(p[0].x, 10); test_int(p[0].y, 20); + test_int(p[1].x, 10); test_int(p[1].y, 20); + test_bool(false, ecs_query_next(&it)); + ecs_query_fini(f); + } + } + + ecs_fini(world); +} + +void Template_template_w_composite_prop(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "template Tree {" + LINE " prop pos = Position: {10, 20}" + LINE " child { $pos }" + LINE "}" + LINE "t { Tree: {pos: {20, 30}} }" + LINE ""; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t t = ecs_lookup(world, "t"); + test_assert(t != 0); + ecs_entity_t t_child = ecs_lookup(world, "t.child"); + test_assert(t_child != 0); + + { + const void *ptr = ecs_get_id(world, t, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_str(str, "{pos: {x: 20, y: 30}}"); + ecs_os_free(str); + } + + { + const Position *p = ecs_get(world, t_child, Position); + test_assert(p != NULL); + test_int(p->x, 20); + test_int(p->y, 30); + } + + ecs_fini(world); +} + +void Template_template_with_with(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "Foo {}" + LINE "template Tree {" + LINE " prop count = flecs.meta.i32: 0" + LINE " with Foo {" + LINE " child { Position: {$count, $count * 2} }" + LINE " }" + LINE "}" + LINE "t { Tree: {count: 10} }" + LINE ""; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + ecs_entity_t foo = ecs_lookup(world, "Foo"); + test_assert(foo != 0); + ecs_entity_t t = ecs_lookup(world, "t"); + test_assert(t != 0); + ecs_entity_t t_child = ecs_lookup(world, "t.child"); + test_assert(t_child != 0); + + { + const void *ptr = ecs_get_id(world, t, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_str(str, "{count: 10}"); + ecs_os_free(str); + } + + { + const Position *p = ecs_get(world, t_child, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + } + + ecs_fini(world); +} + +void Template_module_w_template(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "module hello.world\n" + LINE "template Tree {\n" + LINE " prop count = flecs.meta.i32: 0\n" + LINE " child { Position: {$count, $count * 2} }\n" + LINE "}\n" + LINE ""; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + const char *expr_inst = + LINE "t = hello.world.Tree: {10}\n"; + test_assert(ecs_script_run(world, NULL, expr_inst) == 0); + + ecs_entity_t tree = ecs_lookup(world, "hello.world.Tree"); + test_assert(tree != 0); + ecs_entity_t t = ecs_lookup(world, "t"); + test_assert(t != 0); + ecs_entity_t child = ecs_lookup(world, "t.child"); + test_assert(child != 0); + + { + const void *ptr = ecs_get_id(world, t, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_str(str, "{count: 10}"); + ecs_os_free(str); + } + + test_assert(ecs_has(world, child, Position)); + + { + const Position *ptr = ecs_get(world, child, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + +void Template_module_w_nested_template(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + LINE "module hello.world" + LINE "template Tree {" + LINE " prop count = flecs.meta.i32: 0" + LINE " child { Position: {$count, $count * 2} }" + LINE "}" + LINE "template Forest {" + LINE " prop count = flecs.meta.i32: 0" + LINE " t { Tree: {count:$} }" + LINE "}" + LINE ""; + test_assert(ecs_script_run(world, NULL, expr) == 0); + + const char *expr_inst = + LINE "f = hello.world.Forest: {10}\n"; + test_assert(ecs_script_run(world, NULL, expr_inst) == 0); + + ecs_entity_t forest = ecs_lookup(world, "hello.world.Forest"); + test_assert(forest != 0); + ecs_entity_t tree = ecs_lookup(world, "hello.world.Tree"); + test_assert(tree != 0); + ecs_entity_t f = ecs_lookup(world, "f"); + test_assert(f != 0); + ecs_entity_t t = ecs_lookup(world, "f.t"); + test_assert(t != 0); + ecs_entity_t child = ecs_lookup(world, "f.t.child"); + test_assert(child != 0); + + { + const void *ptr = ecs_get_id(world, f, forest); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, forest, ptr); + test_str(str, "{count: 10}"); + ecs_os_free(str); + } + { + const void *ptr = ecs_get_id(world, t, tree); + test_assert(ptr != NULL); + char *str = ecs_ptr_to_expr(world, tree, ptr); + test_str(str, "{count: 10}"); + ecs_os_free(str); + } + + test_assert(ecs_has(world, child, Position)); + + { + const Position *ptr = ecs_get(world, child, Position); + test_assert(ptr != NULL); + test_int(ptr->x, 10); + test_int(ptr->y, 20); + } + + ecs_fini(world); +} + +void Template_template_w_pair_w_this_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + + const char *expr = + LINE "template Foo {\n" + LINE " prop x = flecs.meta.f32: 10\n" // dummy prop + LINE " (Rel, $this)\n" + LINE "}\n" + LINE "ent { Foo: {} }\n" + LINE "\n"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t foo = ecs_lookup(world, "Foo"); + ecs_entity_t ent = ecs_lookup(world, "ent"); + + test_assert(ecs_has_id(world, ent, foo)); + test_assert(ecs_has_pair(world, ent, Rel, ent)); + + ecs_fini(world); +} + +void Template_prop_without_using_meta(void) { + ecs_world_t *world = ecs_init(); + + const char *expr = + LINE "template Tree {" + LINE " prop height = f32: 0" + LINE "}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + const EcsStruct *st = ecs_get(world, tree, EcsStruct); + test_assert(st != NULL); + test_int(st->members.count, 1); + test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "height"); + test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t)); + + ecs_fini(world); +} + +void Template_hoist_var(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "using flecs.meta" + LINE "const v = 10" + LINE "template Tree {" + LINE " prop height = f32: 0" + LINE " Position: {$v, $height}" + LINE "}" + LINE "Tree foo(height: 20)"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_entity_t foo = ecs_lookup(world, "foo"); + test_assert(foo != 0); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Template_anonymous_template_instance(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "template Tree {" + LINE " Position: {10, 20}" + LINE "}" + LINE "Tree() {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_iter_t it = ecs_each_id(world, tree); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + ecs_entity_t foo = it.entities[0]; + test_assert(foo != 0); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Template_anonymous_template_instance_no_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "template Tree {" + LINE " Position: {10, 20}" + LINE "}" + LINE "Tree()"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_iter_t it = ecs_each_id(world, tree); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + ecs_entity_t foo = it.entities[0]; + test_assert(foo != 0); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Template_anonymous_template_instance_w_prop(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "template Tree {" + LINE " prop height = f32: 10" + LINE " Position: {$height, $height * 2}" + LINE "}" + LINE "Tree() {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_iter_t it = ecs_each_id(world, tree); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + ecs_entity_t foo = it.entities[0]; + test_assert(foo != 0); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Template_anonymous_template_instance_w_prop_no_scope(void) { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_struct(world, { + .entity = ecs_id(Position), + .members = { + {"x", ecs_id(ecs_f32_t)}, + {"y", ecs_id(ecs_f32_t)} + } + }); + + const char *expr = + HEAD "template Tree {" + LINE " prop height = f32: 10" + LINE " Position: {$height, $height * 2}" + LINE "}" + LINE "Tree()"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t tree = ecs_lookup(world, "Tree"); + test_assert(tree != 0); + + ecs_iter_t it = ecs_each_id(world, tree); + test_bool(true, ecs_each_next(&it)); + test_int(1, it.count); + ecs_entity_t foo = it.entities[0]; + test_assert(foo != 0); + + const Position *p = ecs_get(world, foo, Position); + test_assert(p != NULL); + test_int(p->x, 10); + test_int(p->y, 20); + + ecs_fini(world); +} + +void Template_with_after_template(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "template Foo {}" + LINE "with Bar {" + LINE " withBar {}" + LINE "}" + LINE "withoutBar {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t withBar = ecs_lookup(world, "withBar"); + test_assert(withBar != 0); + ecs_entity_t withoutBar = ecs_lookup(world, "withoutBar"); + test_assert(withoutBar != 0); + + test_assert(ecs_has(world, withBar, Bar)); + test_assert(!ecs_has(world, withoutBar, Bar)); + + ecs_fini(world); +} + +void Template_with_in_scope_after_template(void) { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Bar); + + const char *expr = + HEAD "template Foo {}" + LINE "parent {" + LINE " with Bar {" + LINE " withBar {}" + LINE " }" + LINE " withoutBar {}" + LINE "}" + LINE "defWithoutBar {}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + + ecs_entity_t withBar = ecs_lookup(world, "parent.withBar"); + test_assert(withBar != 0); + ecs_entity_t withoutBar = ecs_lookup(world, "parent.withoutBar"); + test_assert(withoutBar != 0); + ecs_entity_t defWithoutBar = ecs_lookup(world, "defWithoutBar"); + test_assert(defWithoutBar != 0); + + test_assert(ecs_has(world, withBar, Bar)); + test_assert(!ecs_has(world, withoutBar, Bar)); + test_assert(!ecs_has(world, defWithoutBar, Bar)); + + ecs_fini(world); +} diff --git a/vendors/flecs/test/script/src/Vars.c b/vendors/flecs/test/script/src/Vars.c new file mode 100644 index 000000000..a1e7c6934 --- /dev/null +++ b/vendors/flecs/test/script/src/Vars.c @@ -0,0 +1,364 @@ +#include + +void Vars_declare_1_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *v = ecs_script_vars_define( + vars, "foo", ecs_i32_t); + test_assert(v != NULL); + test_assert(v->value.ptr != NULL); + test_assert(v->value.type != 0); + test_uint(v->value.type, ecs_id(ecs_i32_t)); + test_int(*(int32_t*)v->value.ptr, 0); + + const ecs_script_var_t *v2 = ecs_script_vars_lookup(vars, "foo"); + test_assert(v2 != NULL); + test_assert(v == v2); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_declare_2_vars(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define( + vars, "foo", ecs_i32_t); + test_assert(foo != NULL); + test_assert(foo->value.ptr != NULL); + test_assert(foo->value.type != 0); + test_uint(foo->value.type, ecs_id(ecs_i32_t)); + test_int(*(int32_t*)foo->value.ptr, 0); + + ecs_script_var_t *bar = ecs_script_vars_define( + vars, "bar", ecs_u32_t); + test_assert(bar != NULL); + test_assert(bar->value.ptr != NULL); + test_assert(bar->value.type != 0); + test_uint(bar->value.type, ecs_id(ecs_u32_t)); + test_uint(*(ecs_u32_t*)bar->value.ptr, 0); + + test_assert(foo == ecs_script_vars_lookup(vars, "foo")); + test_assert(bar == ecs_script_vars_lookup(vars, "bar")); + test_assert(foo != bar); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_declare_vars_nested_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + ecs_script_vars_t *root_vars = vars; + + vars = ecs_script_vars_push(vars); + + ecs_script_var_t *foo = ecs_script_vars_define( + vars, "foo", ecs_i32_t); + test_assert(foo != NULL); + test_assert(foo->value.ptr != NULL); + test_assert(foo->value.type != 0); + test_uint(foo->value.type, ecs_id(ecs_i32_t)); + test_int(*(int32_t*)foo->value.ptr, 0); + test_assert(foo == ecs_script_vars_lookup(vars, "foo")); + + vars = ecs_script_vars_pop(vars); + test_assert(vars != NULL); + test_assert(vars == root_vars); + + test_assert(NULL == ecs_script_vars_lookup(vars, "foo")); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_declare_vars_2_scopes(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define( + vars, "foo", ecs_i32_t); + test_assert(foo != NULL); + test_assert(foo->value.ptr != NULL); + test_assert(foo->value.type != 0); + test_uint(foo->value.type, ecs_id(ecs_i32_t)); + test_int(*(int32_t*)foo->value.ptr, 0); + test_assert(foo == ecs_script_vars_lookup(vars, "foo")); + + vars = ecs_script_vars_push(vars); + + ecs_script_var_t *bar = ecs_script_vars_define( + vars, "bar", ecs_u32_t); + test_assert(bar != NULL); + test_assert(bar->value.ptr != NULL); + test_assert(bar->value.type != 0); + test_uint(bar->value.type, ecs_id(ecs_u32_t)); + test_uint(*(ecs_u32_t*)bar->value.ptr, 0); + + test_assert(foo == ecs_script_vars_lookup(vars, "foo")); + test_assert(bar == ecs_script_vars_lookup(vars, "bar")); + test_assert(foo != bar); + + vars = ecs_script_vars_pop(vars); + + test_assert(foo == ecs_script_vars_lookup(vars, "foo")); + test_assert(NULL == ecs_script_vars_lookup(vars, "bar")); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_redeclare_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define( + vars, "foo", ecs_i32_t); + test_assert(foo != NULL); + test_assert(foo->value.ptr != NULL); + test_assert(foo->value.type != 0); + test_uint(foo->value.type, ecs_id(ecs_i32_t)); + test_int(*(int32_t*)foo->value.ptr, 0); + test_assert(foo == ecs_script_vars_lookup(vars, "foo")); + + ecs_log_set_level(-4); + ecs_script_var_t *foo_2 = ecs_script_vars_define( + vars, "foo", ecs_i32_t); + test_assert(foo_2 == NULL); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_i32_expr_w_i32_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define( + vars, "foo", ecs_i32_t); + *(int32_t*)foo->value.ptr = 10; + + int32_t v = 0; + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run( + world, "$foo", &ecs_value_ptr(ecs_i32_t, &v), &desc); + test_assert(ptr != NULL); + test_assert(ptr[0] == 0); + test_int(v, 10); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_i32_expr_w_f32_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define( + vars, "foo", ecs_f32_t); + *(float*)foo->value.ptr = 10.5; + + int32_t v = 0; + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run( + world, "$foo", &ecs_value_ptr(ecs_i32_t, &v), &desc); + test_assert(ptr != NULL); + test_assert(ptr[0] == 0); + test_int(v, 10); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_i32_expr_w_string_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define(vars, "foo", ecs_string_t); + *(char**)foo->value.ptr = "10"; + + int32_t v = 0; + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run( + world, "$foo", &ecs_value_ptr(ecs_i32_t, &v), &desc); + test_assert(ptr != NULL); + test_assert(ptr[0] == 0); + test_int(v, 10); + + *(char**)foo->value.ptr = NULL; + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_string_expr_w_string_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define(vars, "foo", ecs_string_t); + *(char**)foo->value.ptr = "Hello World"; + + char* v = NULL; + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run( + world, "$foo", &ecs_value_ptr(ecs_string_t, &v), &desc); + test_assert(ptr != NULL); + test_assert(ptr[0] == 0); + test_str(v, "Hello World"); + + ecs_os_free(v); + + *(char**)foo->value.ptr = NULL; + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_struct_expr_w_i32_vars(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t point = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define(vars, "foo", ecs_i32_t); + *(int32_t*)foo->value.ptr = 10; + ecs_script_var_t *bar = ecs_script_vars_define(vars, "bar", ecs_i32_t); + *(int32_t*)bar->value.ptr = 20; + + PositionI v = {0}; + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run(world, "{$foo, $bar}", &(ecs_value_t){point, &v}, &desc); + test_assert(ptr != NULL); + test_assert(ptr[0] == 0); + + test_int(v.x, 10); + test_int(v.y, 20); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_struct_expr_w_struct_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t point = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define_id(vars, "foo", point); + *(PositionI*)foo->value.ptr = (PositionI){10, 20}; + + PositionI v = {0}; + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run(world, "$foo", &(ecs_value_t){point, &v}, &desc); + test_assert(ptr != NULL); + test_assert(ptr[0] == 0); + + test_int(v.x, 10); + test_int(v.y, 20); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_nested_struct_expr_w_struct_var(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t point = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + ecs_entity_t line = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"start", point}, + {"stop", point} + } + }); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *foo = ecs_script_vars_define_id(vars, "foo", point); + *(PositionI*)foo->value.ptr = (PositionI){10, 20}; + ecs_script_var_t *bar = ecs_script_vars_define_id(vars, "bar", point); + *(PositionI*)bar->value.ptr = (PositionI){30, 40}; + + LineI v = {0}; + ecs_script_expr_run_desc_t desc = { .vars = vars }; + const char *ptr = ecs_script_expr_run(world, + "{start: $foo, stop: $bar}", &(ecs_value_t){line, &v}, &desc); + test_assert(ptr != NULL); + test_assert(ptr[0] == 0); + + test_int(v.start.x, 10); + test_int(v.start.y, 20); + test_int(v.stop.x, 30); + test_int(v.stop.y, 40); + + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_redeclare_in_scope(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_var_t *v1 = ecs_script_vars_define(vars, "foo", ecs_i32_t); + test_assert(v1 != NULL); + vars = ecs_script_vars_push(vars); + ecs_script_var_t *v2 = ecs_script_vars_define(vars, "foo", ecs_i32_t); + test_assert(v2 != NULL); + test_assert(v1 != v2); + test_assert(ecs_script_vars_lookup(vars, "foo") == v2); + vars = ecs_script_vars_pop(vars); + test_assert(ecs_script_vars_lookup(vars, "foo") == v1); + ecs_script_vars_fini(vars); + + ecs_fini(world); +} + +void Vars_init_fini_vars(void) { + ecs_world_t *world = ecs_init(); + + ecs_script_vars_t *vars = ecs_script_vars_init(world); + + ecs_script_vars_fini(vars); + ecs_fini(world); + + test_assert(true); +} diff --git a/vendors/flecs/test/script/src/main.c b/vendors/flecs/test/script/src/main.c new file mode 100644 index 000000000..26bcf8347 --- /dev/null +++ b/vendors/flecs/test/script/src/main.c @@ -0,0 +1,3206 @@ + +/* A friendly warning from bake.test + * ---------------------------------------------------------------------------- + * This file is generated. To add/remove testcases modify the 'project.json' of + * the test project. ANY CHANGE TO THIS FILE IS LOST AFTER (RE)BUILDING! + * ---------------------------------------------------------------------------- + */ + +#include + +// Testsuite 'Eval' +void Eval_null(void); +void Eval_empty(void); +void Eval_space(void); +void Eval_space_newline(void); +void Eval_two_empty_newlines(void); +void Eval_three_empty_newlines(void); +void Eval_newline_trailing_space(void); +void Eval_newline_trailing_spaces(void); +void Eval_multiple_trailing_newlines(void); +void Eval_entity(void); +void Eval_entity_w_core_name(void); +void Eval_2_entities(void); +void Eval_line_comment(void); +void Eval_line_comment_before_stmt(void); +void Eval_line_comment_after_stmt(void); +void Eval_line_comment_between_stmt(void); +void Eval_multiple_line_comment(void); +void Eval_multiple_line_comment_w_newlines(void); +void Eval_line_comment_after_stmt_same_line(void); +void Eval_line_comment_before_scope_open(void); +void Eval_line_comment_after_newline_before_scope_open(void); +void Eval_line_comment_after_newline_before_newline_scope_open(void); +void Eval_multi_line_comment(void); +void Eval_multi_line_comment_before_stmt(void); +void Eval_multi_line_comment_after_stmt(void); +void Eval_multi_line_comment_between_stmt(void); +void Eval_multiple_multi_line_comment(void); +void Eval_multiple_multi_line_comment_w_newlines(void); +void Eval_multi_line_comment_after_stmt_same_line(void); +void Eval_multi_line_comment_before_scope_open(void); +void Eval_multi_line_comment_after_newline_before_scope_open(void); +void Eval_multi_line_comment_multiple_lines(void); +void Eval_hierarchy_1_child(void); +void Eval_hierarchy_2_children(void); +void Eval_hierarchy_1_child_same_line(void); +void Eval_hierarchy_2_children_same_line(void); +void Eval_hierarchy_2_children_same_line_no_trailing_comma(void); +void Eval_entity_after_hierarchy(void); +void Eval_newline_before_scope_open(void); +void Eval_newline_w_whitespace_before_scope_open(void); +void Eval_2_newline_before_scope_open(void); +void Eval_2_newline_w_whitespace_before_scope_open(void); +void Eval_hierarchy_2_levels(void); +void Eval_hierarchy_2_levels_2_subtrees(void); +void Eval_create_in_scope(void); +void Eval_hierarchy_w_pred_subj(void); +void Eval_hierarchy_custom_relation(void); +void Eval_hierarchy_custom_relation_2_levels(void); +void Eval_hierarchy_custom_relation_apply_to_object(void); +void Eval_hierarchy_custom_relation_apply_to_object_2_levels(void); +void Eval_entity_after_hierarchy_custom_relation(void); +void Eval_entity_after_hierarchy_custom_relation_2_levels(void); +void Eval_with_tag(void); +void Eval_with_tag_2_entities(void); +void Eval_with_tag_same_line(void); +void Eval_with_tag_2_entities_same_line(void); +void Eval_with_tag_2_entities_same_line_no_trailing_comma(void); +void Eval_with_tag_2_levels(void); +void Eval_with_tag_2_levels_2_subtrees(void); +void Eval_with_n_tags(void); +void Eval_with_n_tags_2_levels(void); +void Eval_with_after_scope(void); +void Eval_with_after_with(void); +void Eval_scope_inside_with_inside_scope(void); +void Eval_with_inside_scope(void); +void Eval_with_tag_core_name(void); +void Eval_inherit(void); +void Eval_inherit_newline(void); +void Eval_inherit_w_colon(void); +void Eval_inherit_w_colon_w_scope(void); +void Eval_assign_component_w_value(void); +void Eval_assign_tag_in_assign_scope(void); +void Eval_assign_tag_in_assign_scope_same_line(void); +void Eval_assign_tag_in_assign_scope_core_name(void); +void Eval_assign_component_value_in_assign_scope(void); +void Eval_assign_2_component_values_in_assign_scope(void); +void Eval_type_and_assign_in_plecs(void); +void Eval_type_and_assign_in_plecs_w_2_members(void); +void Eval_type_and_assign_in_plecs_w_3_members(void); +void Eval_type_and_assign_in_plecs_w_enum(void); +void Eval_type_and_assign_in_plecs_w_enum_using_meta(void); +void Eval_type_and_assign_in_plecs_w_enum_primitive_using_meta(void); +void Eval_type_and_assign_in_plecs_w_enum_primitive_and_struct(void); +void Eval_type_and_assign_in_plecs_nested_member(void); +void Eval_dot_assign_nested_member(void); +void Eval_dot_assign_binary_expr(void); +void Eval_open_scope_no_parent(void); +void Eval_create_subject_in_root_scope_w_resolvable_id(void); +void Eval_create_subject_in_scope_w_resolvable_id(void); +void Eval_create_subject_in_scope_w_resolvable_id_using(void); +void Eval_using_scope(void); +void Eval_using_nested_scope(void); +void Eval_using_nested_in_scope(void); +void Eval_using_with_scope(void); +void Eval_using_w_entity_ref_in_value_2_members(void); +void Eval_using_w_entity_ref_in_value_3_members(void); +void Eval_script_w_only_using(void); +void Eval_2_using_scope(void); +void Eval_2_using_in_different_scope(void); +void Eval_empty_scope_after_using(void); +void Eval_scope_after_assign(void); +void Eval_assign_after_inherit(void); +void Eval_multiple_tags_single_line(void); +void Eval_multiple_pairs_single_line(void); +void Eval_multiple_vars_single_line(void); +void Eval_multiple_assignments_single_line(void); +void Eval_2_stmts_in_scope_w_no_parent(void); +void Eval_assign_tag_to_parent(void); +void Eval_assign_component_to_parent(void); +void Eval_assign_to_parent_pair_w_new_entities_in_scope(void); +void Eval_assign_to_parent_pair_w_existing_entities_in_scope(void); +void Eval_default_child_component(void); +void Eval_default_child_component_w_assign(void); +void Eval_struct_type_w_default_child_component(void); +void Eval_struct_type_w_default_child_component_nested_member(void); +void Eval_enum_type_w_default_child_component(void); +void Eval_default_type_from_with(void); +void Eval_default_type_from_nested_with(void); +void Eval_default_type_from_with_in_entity_scope_w_default_type(void); +void Eval_default_type_from_entity_scope_in_with(void); +void Eval_scope_w_1_subj_and_2_pairs(void); +void Eval_inherit_from_multiple(void); +void Eval_assign_pair_component(void); +void Eval_assign_pair_component_in_scope(void); +void Eval_assign_pair_component_in_script(void); +void Eval_assign_pair_component_in_script_update(void); +void Eval_set_entity_names(void); +void Eval_oneof(void); +void Eval_brief_annotation(void); +void Eval_name_annotation(void); +void Eval_link_annotation(void); +void Eval_color_annotation(void); +void Eval_multiple_annotations(void); +void Eval_annotation_w_trailing_space(void); +void Eval_multiline_string(void); +void Eval_declaration_w_underscore_name(void); +void Eval_annotate_declaration(void); +void Eval_anonymous_entity(void); +void Eval_anonymous_entity_in_scope(void); +void Eval_anonymous_declaration(void); +void Eval_const_var_int(void); +void Eval_const_var_float(void); +void Eval_const_var_bool(void); +void Eval_const_var_string(void); +void Eval_const_var_struct(void); +void Eval_const_var_scoped(void); +void Eval_assign_component_from_var(void); +void Eval_assign_component_from_var_in_scope(void); +void Eval_scope_w_component_after_const_var(void); +void Eval_component_after_const_paren_expr(void); +void Eval_component_after_const_add_expr(void); +void Eval_component_after_const_sub_expr(void); +void Eval_component_after_const_mul_expr(void); +void Eval_component_after_const_div_expr(void); +void Eval_parse_with(void); +void Eval_parse_with_w_with(void); +void Eval_parse_with_w_tag(void); +void Eval_parse_with_value(void); +void Eval_parse_with_2_values(void); +void Eval_parse_with_2_nested_values(void); +void Eval_parse_with_var(void); +void Eval_parse_with_2_vars(void); +void Eval_parse_with_2_nested_vars(void); +void Eval_parse_with_var_in_scope(void); +void Eval_assign_const_w_expr(void); +void Eval_const_w_type(void); +void Eval_typed_const_w_composite_type(void); +void Eval_assign_var_to_typed_const_w_composite_type(void); +void Eval_using_wildcard(void); +void Eval_single_line_comment_in_value(void); +void Eval_single_line_comment_in_value_after_scope(void); +void Eval_multi_line_comment_in_value(void); +void Eval_multi_line_comment_in_value_after_scope(void); +void Eval_module_stmt(void); +void Eval_nested_module_stmt(void); +void Eval_module_stmt_w_scope(void); +void Eval_module_stmt_w_nested_scope(void); +void Eval_assign_singleton_tag(void); +void Eval_assign_singleton_component(void); +void Eval_assign_singleton_tag_w_scope(void); +void Eval_assign_singleton_2_tags_w_scope(void); +void Eval_assign_singleton_component_w_scope(void); +void Eval_assign_singleton_2_components_w_scope(void); +void Eval_with_pair_in_scope(void); +void Eval_with_pair_component_in_scope(void); +void Eval_pair_w_rel_var(void); +void Eval_pair_w_tgt_var(void); +void Eval_component_in_with_scope_in_scope(void); +void Eval_array_component(void); +void Eval_on_set_w_kind_paren_no_reflection(void); +void Eval_on_set_w_kind_paren(void); +void Eval_on_set_w_kind_no_paren_no_reflection(void); +void Eval_on_set_w_kind_no_paren(void); +void Eval_on_set_w_single_assign(void); +void Eval_on_set_w_single_assign_scoped_w_value(void); +void Eval_on_set_w_single_assign_scoped_no_value(void); +void Eval_if_true(void); +void Eval_if_true_no_else(void); +void Eval_if_false(void); +void Eval_if_true_var(void); +void Eval_if_false_var(void); +void Eval_if_10(void); +void Eval_if_0(void); +void Eval_if_true_in_scope(void); +void Eval_if_false_in_scope(void); +void Eval_if_lt(void); +void Eval_if_lt_const(void); +void Eval_isa_in_module(void); +void Eval_isa_hierarchy(void); +void Eval_isa_hierarchy_in_module(void); +void Eval_custom_isa_hierarchy_in_module(void); +void Eval_custom_isa_hierarchy_in_subtree(void); +void Eval_inherit_w_kind(void); +void Eval_inherit_w_kind_scope(void); +void Eval_inherit_w_kind_value(void); +void Eval_inherit_w_kind_value_scope(void); +void Eval_multiple_inherit_w_kind(void); +void Eval_multiple_inherit_w_kind_scope(void); +void Eval_auto_override_tag(void); +void Eval_auto_override_component(void); +void Eval_auto_override_pair(void); +void Eval_auto_override_pair_component(void); +void Eval_lowercase_prefab_kind(void); +void Eval_assign_component_to_const(void); +void Eval_assign_component_member_to_const(void); +void Eval_prefab_w_slot(void); +void Eval_prefab_w_slot_variant(void); +void Eval_const_w_component_expr(void); +void Eval_const_w_component_expr_in_scope(void); +void Eval_const_w_component_expr_in_module(void); +void Eval_const_w_component_in_scope_expr_in_scope(void); +void Eval_const_w_component_in_scope_expr_in_module(void); +void Eval_const_w_component_and_entity_in_scope_expr_in_scope(void); +void Eval_const_w_component_and_entity_in_scope_expr_in_module(void); +void Eval_path_tag_in_scope(void); +void Eval_path_tag_in_module(void); +void Eval_path_tag_in_nested_scope(void); +void Eval_path_tag_in_nested_module(void); +void Eval_dont_inherit_script_pair(void); +void Eval_update_script_w_anonymous(void); +void Eval_update_script_w_anonymous_paren(void); +void Eval_clear_script(void); +void Eval_clear_script_w_anonymous(void); +void Eval_clear_script_w_anonymous_paren(void); +void Eval_partial_assign(void); +void Eval_partial_assign_nontrivial(void); +void Eval_partial_assign_with(void); +void Eval_partial_assign_nontrivial_with(void); +void Eval_partial_assign_with_large_array(void); +void Eval_non_trivial_var_component(void); +void Eval_non_trivial_var_with(void); +void Eval_update_template_w_tag(void); + +// Testsuite 'Template' +void Template_template_no_scope(void); +void Template_template_no_props(void); +void Template_template_prop(void); +void Template_template_prop_space_colon(void); +void Template_template_2_props(void); +void Template_template_w_using(void); +void Template_template_instance_w_default_values(void); +void Template_template_instance_w_assign_default_values(void); +void Template_template_instance_w_overridden_values(void); +void Template_template_w_child(void); +void Template_template_w_child_parse_script(void); +void Template_template_w_child_parse_script_twice(void); +void Template_template_w_child_update_after_parse(void); +void Template_template_w_nested_child(void); +void Template_template_w_prefab(void); +void Template_template_w_prefab_tree(void); +void Template_template_w_nested_template(void); +void Template_instantiate_prefab_w_template(void); +void Template_template_w_prefab_w_template(void); +void Template_3_templates(void); +void Template_template_nested_w_default_var(void); +void Template_template_w_anonymous(void); +void Template_template_w_anonymous_parse_again(void); +void Template_template_w_composite_prop(void); +void Template_template_with_with(void); +void Template_module_w_template(void); +void Template_module_w_nested_template(void); +void Template_template_w_pair_w_this_var(void); +void Template_prop_without_using_meta(void); +void Template_hoist_var(void); +void Template_anonymous_template_instance(void); +void Template_anonymous_template_instance_no_scope(void); +void Template_anonymous_template_instance_w_prop(void); +void Template_anonymous_template_instance_w_prop_no_scope(void); +void Template_with_after_template(void); +void Template_with_in_scope_after_template(void); + +// Testsuite 'Error' +void Error_multi_line_comment_after_newline_before_newline_scope_open(void); +void Error_missing_end_of_scope(void); +void Error_with_n_tags_2_levels_invalid_tag(void); +void Error_assignment_to_non_component(void); +void Error_struct_w_member_w_assignment_to_nothing(void); +void Error_struct_w_member_w_assignment_to_empty_scope(void); +void Error_invalid_nested_assignment(void); +void Error_invalid_partial_pair_assignment(void); +void Error_empty_assignment(void); +void Error_empty_assignment_before_end_of_scope(void); +void Error_default_type_with_tag(void); +void Error_invalid_oneof(void); +void Error_default_type_with_tag(void); +void Error_unterminated_multiline_string(void); +void Error_invalid_assign_multiline_string(void); +void Error_const_var_redeclare(void); +void Error_typed_const_w_composite_type_invalid_assignment(void); +void Error_unterminated_multi_line_comment_in_value(void); +void Error_pair_w_rel_var_invalid_type(void); +void Error_pair_w_tgt_var_invalid_type(void); +void Error_with_value_not_a_component(void); +void Error_component_in_with_scope(void); +void Error_component_in_with_scope_nested(void); +void Error_assign_after_with_in_scope(void); +void Error_not_an_array_component(void); +void Error_array_component_w_curly_brackets(void); +void Error_unknown_identifier(void); +void Error_unknown_identifier_for_int_field(void); +void Error_prefab_w_slot_no_parent(void); +void Error_tag_not_found(void); +void Error_component_not_found(void); +void Error_pair_first_not_found(void); +void Error_pair_second_not_found(void); +void Error_kind_not_found(void); +void Error_base_not_found(void); +void Error_entity_w_anonymous_tag(void); +void Error_member_expr_without_value_end_of_scope(void); +void Error_member_expr_without_value_comma(void); +void Error_member_expr_without_value_newline(void); +void Error_2_member_expr_without_value(void); +void Error_expr_junk_after_number(void); +void Error_expr_junk_after_unary_minus(void); +void Error_expr_comma_after_nothing(void); +void Error_expr_digit_with_two_dots(void); +void Error_template_empty(void); +void Error_template_unresolved_tag(void); +void Error_template_unresolved_component(void); +void Error_template_unresolved_pair_relationship(void); +void Error_template_unresolved_pair_target(void); +void Error_template_unresolved_with_tag(void); +void Error_template_unresolved_with_component(void); +void Error_template_unresolved_with_pair_relationship(void); +void Error_template_unresolved_with_pair_target(void); +void Error_template_unresolved_tag_in_child(void); +void Error_template_prop_no_type(void); +void Error_template_prop_no_default(void); +void Error_template_w_composite_prop_invalid_assignment(void); +void Error_template_redeclare_prop_as_const(void); +void Error_template_redeclare_prop_as_prop(void); +void Error_template_redeclare_const_as_const(void); +void Error_run_template_after_error(void); +void Error_update_template_after_error(void); +void Error_template_in_template(void); + +// Testsuite 'Expr' +void Expr_add_2_int_literals(void); +void Expr_add_2_int_literals_twice(void); +void Expr_sub_2_int_literals(void); +void Expr_mul_2_int_literals(void); +void Expr_div_2_int_literals(void); +void Expr_add_3_int_literals(void); +void Expr_add_3_int_literals_twice(void); +void Expr_sub_3_int_literals(void); +void Expr_mul_3_int_literals(void); +void Expr_div_3_int_literals(void); +void Expr_int_to_bool(void); +void Expr_bool_to_int(void); +void Expr_bool_to_uint(void); +void Expr_add_mul_3_int_literals(void); +void Expr_sub_mul_3_int_literals(void); +void Expr_div_mul_3_int_literals(void); +void Expr_add_div_3_int_literals(void); +void Expr_sub_div_3_int_literals(void); +void Expr_mul_div_3_int_literals(void); +void Expr_mul_add_mul_add_int_literals(void); +void Expr_mul_sub_mul_sub_int_literals(void); +void Expr_mul_div_mul_div_int_literals(void); +void Expr_div_add_div_add_int_literals(void); +void Expr_div_sub_div_sub_int_literals(void); +void Expr_div_sub_div_mul_int_literals(void); +void Expr_div_mul_div_mul_int_literals(void); +void Expr_add_2_flt_literals(void); +void Expr_sub_2_flt_literals(void); +void Expr_mul_2_flt_literals(void); +void Expr_div_2_flt_literals(void); +void Expr_add_2_int_neg_literals(void); +void Expr_sub_2_int_neg_literals(void); +void Expr_mul_2_int_neg_literals(void); +void Expr_div_2_int_neg_literals(void); +void Expr_mul_lparen_add_add_rparen_int_literals(void); +void Expr_mul_lparen_add_add_add_rparen_int_literals(void); +void Expr_mul_lparen_add_add_rparen_add_int_literals(void); +void Expr_lparen_add_add_rparen_mul_int_literals(void); +void Expr_lparen_add_add_add_rparen_mul_int_literals(void); +void Expr_double_paren_add_add(void); +void Expr_double_paren_literal(void); +void Expr_lparen_add_add_rparen_mul_lparen_add_add_rparen(void); +void Expr_float_result_add_2_int_literals(void); +void Expr_struct_result_add_2_int_literals(void); +void Expr_struct_result_add_2_2_fields_int_literals(void); +void Expr_struct_result_add_3_int_literals(void); +void Expr_struct_result_lparen_int_rparen(void); +void Expr_add_to_var(void); +void Expr_add_var_to(void); +void Expr_var_member(void); +void Expr_bool_cond_and_bool(void); +void Expr_bool_cond_or_bool(void); +void Expr_int_cond_and_int(void); +void Expr_int_cond_or_int(void); +void Expr_bool_cond_and_int(void); +void Expr_int_cond_and_bool(void); +void Expr_bool_cond_or_int(void); +void Expr_int_cond_or_bool(void); +void Expr_cond_eq_bool(void); +void Expr_cond_eq_int(void); +void Expr_cond_neq_bool(void); +void Expr_cond_neq_int(void); +void Expr_cond_eq_bool_int(void); +void Expr_cond_eq_int_flt(void); +void Expr_cond_eq_cond_and(void); +void Expr_cond_eq_cond_or(void); +void Expr_cond_gt_bool(void); +void Expr_cond_gt_int(void); +void Expr_cond_gt_flt(void); +void Expr_cond_gteq_bool(void); +void Expr_cond_gteq_int(void); +void Expr_cond_gteq_flt(void); +void Expr_cond_lt_bool(void); +void Expr_cond_lt_int(void); +void Expr_cond_lt_flt(void); +void Expr_cond_lteq_bool(void); +void Expr_cond_lteq_int(void); +void Expr_cond_lteq_flt(void); +void Expr_min_lparen_int_rparen(void); +void Expr_min_lparen_int_add_int_rparen(void); +void Expr_min_var(void); +void Expr_min_lparen_int_rparen_to_i64(void); +void Expr_min_lparen_int_rparen_to_i32(void); +void Expr_struct_w_min_var(void); +void Expr_struct_w_min_lparen_int_rparen(void); +void Expr_struct_w_min_lparen_var_rparen(void); +void Expr_shift_left_int(void); +void Expr_shift_right_int(void); +void Expr_shift_left_int_add_int(void); +void Expr_shift_left_int_mul_int(void); +void Expr_add_int_shift_left_int(void); +void Expr_mul_int_shift_left_int(void); +void Expr_add_int_shift_left_int_add_int(void); +void Expr_mul_int_shift_left_int_mul_int(void); +void Expr_entity_expr(void); +void Expr_entity_path_expr(void); +void Expr_entity_parent_func(void); +void Expr_entity_name_func(void); +void Expr_entity_doc_name_func(void); +void Expr_entity_chain_func(void); +void Expr_var_parent_func(void); +void Expr_var_name_func(void); +void Expr_var_doc_name_func(void); +void Expr_var_chain_func(void); +void Expr_interpolate_string_w_i32_var(void); +void Expr_interpolate_string_w_string_var(void); +void Expr_interpolate_string_w_entity_var(void); +void Expr_interpolate_string_w_id_var(void); +void Expr_interpolate_string_w_var_not_found(void); +void Expr_interpolate_string_w_entity_var_0(void); +void Expr_interpolate_string_w_var_special_chars(void); +void Expr_interpolate_string_w_var_before_after_text(void); +void Expr_interpolate_string_w_curly_brackets_var(void); +void Expr_interpolate_string_w_curly_brackets_expr(void); +void Expr_interpolate_string_w_curly_brackets_expr_w_var(void); +void Expr_interpolate_string_w_curly_brackets_expr_w_composite_var(void); +void Expr_interpolate_string_w_escape_var_operator(void); +void Expr_interpolate_string_w_escape_curly_brackets(void); +void Expr_interpolate_string_w_func(void); +void Expr_interpolate_string_w_func_chain(void); +void Expr_iter_to_vars_no_data(void); +void Expr_iter_to_vars_1_comp(void); +void Expr_iter_to_vars_2_comps(void); +void Expr_iter_to_vars_1_comp_1_tag(void); +void Expr_iter_to_vars_w_1_query_var(void); +void Expr_iter_to_vars_w_2_query_vars(void); +void Expr_component_expr(void); +void Expr_component_member_expr(void); + +// Testsuite 'Vars' +void Vars_declare_1_var(void); +void Vars_declare_2_vars(void); +void Vars_declare_vars_nested_scope(void); +void Vars_declare_vars_2_scopes(void); +void Vars_redeclare_var(void); +void Vars_i32_expr_w_i32_var(void); +void Vars_i32_expr_w_f32_var(void); +void Vars_i32_expr_w_string_var(void); +void Vars_string_expr_w_string_var(void); +void Vars_struct_expr_w_i32_vars(void); +void Vars_struct_expr_w_struct_var(void); +void Vars_nested_struct_expr_w_struct_var(void); +void Vars_redeclare_in_scope(void); +void Vars_init_fini_vars(void); + +// Testsuite 'Serialize' +void Serialize_bool(void); +void Serialize_byte(void); +void Serialize_char(void); +void Serialize_i8(void); +void Serialize_i16(void); +void Serialize_i32(void); +void Serialize_i64(void); +void Serialize_iptr(void); +void Serialize_u8(void); +void Serialize_u16(void); +void Serialize_u32(void); +void Serialize_u64(void); +void Serialize_uptr(void); +void Serialize_float(void); +void Serialize_double(void); +void Serialize_string(void); +void Serialize_entity(void); +void Serialize_entity_10k(void); +void Serialize_id(void); +void Serialize_enum(void); +void Serialize_bitmask(void); +void Serialize_float_nan(void); +void Serialize_float_inf(void); +void Serialize_double_nan(void); +void Serialize_double_inf(void); +void Serialize_struct_enum(void); +void Serialize_struct_bitmask(void); +void Serialize_struct_i32(void); +void Serialize_struct_i32_i32(void); +void Serialize_struct_entity(void); +void Serialize_struct_id(void); +void Serialize_array_i32_3(void); +void Serialize_array_struct_i32_i32(void); +void Serialize_array_array_i32_3(void); +void Serialize_vector_i32_3(void); +void Serialize_vector_struct_i32_i32(void); +void Serialize_vector_array_i32_3(void); +void Serialize_entity_entity_after_float(void); +void Serialize_struct_nested_i32(void); +void Serialize_struct_nested_i32_i32(void); +void Serialize_struct_2_nested_i32_i32(void); +void Serialize_struct_i32_array_3(void); +void Serialize_struct_struct_i32_array_3(void); +void Serialize_struct_struct_i32_i32_array_3(void); +void Serialize_struct_w_array_type_i32_i32(void); +void Serialize_struct_w_array_type_struct(void); +void Serialize_struct_w_2_array_type_i32_i32(void); +void Serialize_struct_w_2_array_type_struct(void); +void Serialize_struct_partial(void); +void Serialize_escape_simple_string(void); +void Serialize_escape_newline(void); +void Serialize_escape_2_newlines(void); +void Serialize_escape_string_w_trailing_newline(void); +void Serialize_escape_string_w_2_trailing_newlines(void); +void Serialize_escape_string_w_delim(void); + +// Testsuite 'Deserialize' +void Deserialize_bool(void); +void Deserialize_byte(void); +void Deserialize_char(void); +void Deserialize_char_literal(void); +void Deserialize_i8(void); +void Deserialize_i16(void); +void Deserialize_i32(void); +void Deserialize_i64(void); +void Deserialize_iptr(void); +void Deserialize_u8(void); +void Deserialize_u16(void); +void Deserialize_u32(void); +void Deserialize_u64(void); +void Deserialize_uptr(void); +void Deserialize_float(void); +void Deserialize_double(void); +void Deserialize_negative_int(void); +void Deserialize_negative_float(void); +void Deserialize_invalid_i8(void); +void Deserialize_invalid_i16(void); +void Deserialize_invalid_i32(void); +void Deserialize_invalid_i64(void); +void Deserialize_invalid_iptr(void); +void Deserialize_invalid_u8(void); +void Deserialize_invalid_u16(void); +void Deserialize_invalid_u32(void); +void Deserialize_invalid_u64(void); +void Deserialize_invalid_uptr(void); +void Deserialize_invalid_float(void); +void Deserialize_invalid_double(void); +void Deserialize_string(void); +void Deserialize_entity(void); +void Deserialize_id(void); +void Deserialize_enum(void); +void Deserialize_bitmask(void); +void Deserialize_struct_enum(void); +void Deserialize_struct_bitmask(void); +void Deserialize_struct_i32(void); +void Deserialize_struct_i32_neg(void); +void Deserialize_struct_i32_i32(void); +void Deserialize_struct_entity(void); +void Deserialize_struct_id(void); +void Deserialize_struct_nested_i32(void); +void Deserialize_struct_nested_i32_i32(void); +void Deserialize_struct_2_nested_i32_i32(void); +void Deserialize_struct_member_i32(void); +void Deserialize_struct_member_i32_neg(void); +void Deserialize_struct_member_i32_i32(void); +void Deserialize_struct_member_nested_i32(void); +void Deserialize_struct_member_nested_i32_i32(void); +void Deserialize_struct_member_2_nested_i32_i32(void); +void Deserialize_struct_member_2_nested_i32_i32_reverse(void); +void Deserialize_struct_i32_array_3(void); +void Deserialize_struct_struct_i32_array_3(void); +void Deserialize_struct_struct_i32_i32_array_3(void); +void Deserialize_struct_w_array_type_i32_i32(void); +void Deserialize_struct_w_array_type_struct(void); +void Deserialize_struct_w_2_array_type_i32_i32(void); +void Deserialize_struct_w_2_array_type_struct(void); +void Deserialize_discover_type_int(void); +void Deserialize_discover_type_negative_int(void); +void Deserialize_discover_type_float(void); +void Deserialize_discover_type_negative_float(void); +void Deserialize_discover_type_string(void); +void Deserialize_discover_type_multiline_string(void); +void Deserialize_discover_type_entity(void); +void Deserialize_discover_type_bool(void); +void Deserialize_discover_type_unknown(void); +void Deserialize_discover_type_invalid(void); +void Deserialize_opaque_struct(void); +void Deserialize_opaque_struct_w_member(void); +void Deserialize_opaque_struct_w_member_reverse(void); +void Deserialize_struct_w_opaque_member(void); + +bake_test_case Eval_testcases[] = { + { + "null", + Eval_null + }, + { + "empty", + Eval_empty + }, + { + "space", + Eval_space + }, + { + "space_newline", + Eval_space_newline + }, + { + "two_empty_newlines", + Eval_two_empty_newlines + }, + { + "three_empty_newlines", + Eval_three_empty_newlines + }, + { + "newline_trailing_space", + Eval_newline_trailing_space + }, + { + "newline_trailing_spaces", + Eval_newline_trailing_spaces + }, + { + "multiple_trailing_newlines", + Eval_multiple_trailing_newlines + }, + { + "entity", + Eval_entity + }, + { + "entity_w_core_name", + Eval_entity_w_core_name + }, + { + "2_entities", + Eval_2_entities + }, + { + "line_comment", + Eval_line_comment + }, + { + "line_comment_before_stmt", + Eval_line_comment_before_stmt + }, + { + "line_comment_after_stmt", + Eval_line_comment_after_stmt + }, + { + "line_comment_between_stmt", + Eval_line_comment_between_stmt + }, + { + "multiple_line_comment", + Eval_multiple_line_comment + }, + { + "multiple_line_comment_w_newlines", + Eval_multiple_line_comment_w_newlines + }, + { + "line_comment_after_stmt_same_line", + Eval_line_comment_after_stmt_same_line + }, + { + "line_comment_before_scope_open", + Eval_line_comment_before_scope_open + }, + { + "line_comment_after_newline_before_scope_open", + Eval_line_comment_after_newline_before_scope_open + }, + { + "line_comment_after_newline_before_newline_scope_open", + Eval_line_comment_after_newline_before_newline_scope_open + }, + { + "multi_line_comment", + Eval_multi_line_comment + }, + { + "multi_line_comment_before_stmt", + Eval_multi_line_comment_before_stmt + }, + { + "multi_line_comment_after_stmt", + Eval_multi_line_comment_after_stmt + }, + { + "multi_line_comment_between_stmt", + Eval_multi_line_comment_between_stmt + }, + { + "multiple_multi_line_comment", + Eval_multiple_multi_line_comment + }, + { + "multiple_multi_line_comment_w_newlines", + Eval_multiple_multi_line_comment_w_newlines + }, + { + "multi_line_comment_after_stmt_same_line", + Eval_multi_line_comment_after_stmt_same_line + }, + { + "multi_line_comment_before_scope_open", + Eval_multi_line_comment_before_scope_open + }, + { + "multi_line_comment_after_newline_before_scope_open", + Eval_multi_line_comment_after_newline_before_scope_open + }, + { + "multi_line_comment_multiple_lines", + Eval_multi_line_comment_multiple_lines + }, + { + "hierarchy_1_child", + Eval_hierarchy_1_child + }, + { + "hierarchy_2_children", + Eval_hierarchy_2_children + }, + { + "hierarchy_1_child_same_line", + Eval_hierarchy_1_child_same_line + }, + { + "hierarchy_2_children_same_line", + Eval_hierarchy_2_children_same_line + }, + { + "hierarchy_2_children_same_line_no_trailing_comma", + Eval_hierarchy_2_children_same_line_no_trailing_comma + }, + { + "entity_after_hierarchy", + Eval_entity_after_hierarchy + }, + { + "newline_before_scope_open", + Eval_newline_before_scope_open + }, + { + "newline_w_whitespace_before_scope_open", + Eval_newline_w_whitespace_before_scope_open + }, + { + "2_newline_before_scope_open", + Eval_2_newline_before_scope_open + }, + { + "2_newline_w_whitespace_before_scope_open", + Eval_2_newline_w_whitespace_before_scope_open + }, + { + "hierarchy_2_levels", + Eval_hierarchy_2_levels + }, + { + "hierarchy_2_levels_2_subtrees", + Eval_hierarchy_2_levels_2_subtrees + }, + { + "create_in_scope", + Eval_create_in_scope + }, + { + "hierarchy_w_pred_subj", + Eval_hierarchy_w_pred_subj + }, + { + "hierarchy_custom_relation", + Eval_hierarchy_custom_relation + }, + { + "hierarchy_custom_relation_2_levels", + Eval_hierarchy_custom_relation_2_levels + }, + { + "hierarchy_custom_relation_apply_to_object", + Eval_hierarchy_custom_relation_apply_to_object + }, + { + "hierarchy_custom_relation_apply_to_object_2_levels", + Eval_hierarchy_custom_relation_apply_to_object_2_levels + }, + { + "entity_after_hierarchy_custom_relation", + Eval_entity_after_hierarchy_custom_relation + }, + { + "entity_after_hierarchy_custom_relation_2_levels", + Eval_entity_after_hierarchy_custom_relation_2_levels + }, + { + "with_tag", + Eval_with_tag + }, + { + "with_tag_2_entities", + Eval_with_tag_2_entities + }, + { + "with_tag_same_line", + Eval_with_tag_same_line + }, + { + "with_tag_2_entities_same_line", + Eval_with_tag_2_entities_same_line + }, + { + "with_tag_2_entities_same_line_no_trailing_comma", + Eval_with_tag_2_entities_same_line_no_trailing_comma + }, + { + "with_tag_2_levels", + Eval_with_tag_2_levels + }, + { + "with_tag_2_levels_2_subtrees", + Eval_with_tag_2_levels_2_subtrees + }, + { + "with_n_tags", + Eval_with_n_tags + }, + { + "with_n_tags_2_levels", + Eval_with_n_tags_2_levels + }, + { + "with_after_scope", + Eval_with_after_scope + }, + { + "with_after_with", + Eval_with_after_with + }, + { + "scope_inside_with_inside_scope", + Eval_scope_inside_with_inside_scope + }, + { + "with_inside_scope", + Eval_with_inside_scope + }, + { + "with_tag_core_name", + Eval_with_tag_core_name + }, + { + "inherit", + Eval_inherit + }, + { + "inherit_newline", + Eval_inherit_newline + }, + { + "inherit_w_colon", + Eval_inherit_w_colon + }, + { + "inherit_w_colon_w_scope", + Eval_inherit_w_colon_w_scope + }, + { + "assign_component_w_value", + Eval_assign_component_w_value + }, + { + "assign_tag_in_assign_scope", + Eval_assign_tag_in_assign_scope + }, + { + "assign_tag_in_assign_scope_same_line", + Eval_assign_tag_in_assign_scope_same_line + }, + { + "assign_tag_in_assign_scope_core_name", + Eval_assign_tag_in_assign_scope_core_name + }, + { + "assign_component_value_in_assign_scope", + Eval_assign_component_value_in_assign_scope + }, + { + "assign_2_component_values_in_assign_scope", + Eval_assign_2_component_values_in_assign_scope + }, + { + "type_and_assign_in_plecs", + Eval_type_and_assign_in_plecs + }, + { + "type_and_assign_in_plecs_w_2_members", + Eval_type_and_assign_in_plecs_w_2_members + }, + { + "type_and_assign_in_plecs_w_3_members", + Eval_type_and_assign_in_plecs_w_3_members + }, + { + "type_and_assign_in_plecs_w_enum", + Eval_type_and_assign_in_plecs_w_enum + }, + { + "type_and_assign_in_plecs_w_enum_using_meta", + Eval_type_and_assign_in_plecs_w_enum_using_meta + }, + { + "type_and_assign_in_plecs_w_enum_primitive_using_meta", + Eval_type_and_assign_in_plecs_w_enum_primitive_using_meta + }, + { + "type_and_assign_in_plecs_w_enum_primitive_and_struct", + Eval_type_and_assign_in_plecs_w_enum_primitive_and_struct + }, + { + "type_and_assign_in_plecs_nested_member", + Eval_type_and_assign_in_plecs_nested_member + }, + { + "dot_assign_nested_member", + Eval_dot_assign_nested_member + }, + { + "dot_assign_binary_expr", + Eval_dot_assign_binary_expr + }, + { + "open_scope_no_parent", + Eval_open_scope_no_parent + }, + { + "create_subject_in_root_scope_w_resolvable_id", + Eval_create_subject_in_root_scope_w_resolvable_id + }, + { + "create_subject_in_scope_w_resolvable_id", + Eval_create_subject_in_scope_w_resolvable_id + }, + { + "create_subject_in_scope_w_resolvable_id_using", + Eval_create_subject_in_scope_w_resolvable_id_using + }, + { + "using_scope", + Eval_using_scope + }, + { + "using_nested_scope", + Eval_using_nested_scope + }, + { + "using_nested_in_scope", + Eval_using_nested_in_scope + }, + { + "using_with_scope", + Eval_using_with_scope + }, + { + "using_w_entity_ref_in_value_2_members", + Eval_using_w_entity_ref_in_value_2_members + }, + { + "using_w_entity_ref_in_value_3_members", + Eval_using_w_entity_ref_in_value_3_members + }, + { + "script_w_only_using", + Eval_script_w_only_using + }, + { + "2_using_scope", + Eval_2_using_scope + }, + { + "2_using_in_different_scope", + Eval_2_using_in_different_scope + }, + { + "empty_scope_after_using", + Eval_empty_scope_after_using + }, + { + "scope_after_assign", + Eval_scope_after_assign + }, + { + "assign_after_inherit", + Eval_assign_after_inherit + }, + { + "multiple_tags_single_line", + Eval_multiple_tags_single_line + }, + { + "multiple_pairs_single_line", + Eval_multiple_pairs_single_line + }, + { + "multiple_vars_single_line", + Eval_multiple_vars_single_line + }, + { + "multiple_assignments_single_line", + Eval_multiple_assignments_single_line + }, + { + "2_stmts_in_scope_w_no_parent", + Eval_2_stmts_in_scope_w_no_parent + }, + { + "assign_tag_to_parent", + Eval_assign_tag_to_parent + }, + { + "assign_component_to_parent", + Eval_assign_component_to_parent + }, + { + "assign_to_parent_pair_w_new_entities_in_scope", + Eval_assign_to_parent_pair_w_new_entities_in_scope + }, + { + "assign_to_parent_pair_w_existing_entities_in_scope", + Eval_assign_to_parent_pair_w_existing_entities_in_scope + }, + { + "default_child_component", + Eval_default_child_component + }, + { + "default_child_component_w_assign", + Eval_default_child_component_w_assign + }, + { + "struct_type_w_default_child_component", + Eval_struct_type_w_default_child_component + }, + { + "struct_type_w_default_child_component_nested_member", + Eval_struct_type_w_default_child_component_nested_member + }, + { + "enum_type_w_default_child_component", + Eval_enum_type_w_default_child_component + }, + { + "default_type_from_with", + Eval_default_type_from_with + }, + { + "default_type_from_nested_with", + Eval_default_type_from_nested_with + }, + { + "default_type_from_with_in_entity_scope_w_default_type", + Eval_default_type_from_with_in_entity_scope_w_default_type + }, + { + "default_type_from_entity_scope_in_with", + Eval_default_type_from_entity_scope_in_with + }, + { + "scope_w_1_subj_and_2_pairs", + Eval_scope_w_1_subj_and_2_pairs + }, + { + "inherit_from_multiple", + Eval_inherit_from_multiple + }, + { + "assign_pair_component", + Eval_assign_pair_component + }, + { + "assign_pair_component_in_scope", + Eval_assign_pair_component_in_scope + }, + { + "assign_pair_component_in_script", + Eval_assign_pair_component_in_script + }, + { + "assign_pair_component_in_script_update", + Eval_assign_pair_component_in_script_update + }, + { + "set_entity_names", + Eval_set_entity_names + }, + { + "oneof", + Eval_oneof + }, + { + "brief_annotation", + Eval_brief_annotation + }, + { + "name_annotation", + Eval_name_annotation + }, + { + "link_annotation", + Eval_link_annotation + }, + { + "color_annotation", + Eval_color_annotation + }, + { + "multiple_annotations", + Eval_multiple_annotations + }, + { + "annotation_w_trailing_space", + Eval_annotation_w_trailing_space + }, + { + "multiline_string", + Eval_multiline_string + }, + { + "declaration_w_underscore_name", + Eval_declaration_w_underscore_name + }, + { + "annotate_declaration", + Eval_annotate_declaration + }, + { + "anonymous_entity", + Eval_anonymous_entity + }, + { + "anonymous_entity_in_scope", + Eval_anonymous_entity_in_scope + }, + { + "anonymous_declaration", + Eval_anonymous_declaration + }, + { + "const_var_int", + Eval_const_var_int + }, + { + "const_var_float", + Eval_const_var_float + }, + { + "const_var_bool", + Eval_const_var_bool + }, + { + "const_var_string", + Eval_const_var_string + }, + { + "const_var_struct", + Eval_const_var_struct + }, + { + "const_var_scoped", + Eval_const_var_scoped + }, + { + "assign_component_from_var", + Eval_assign_component_from_var + }, + { + "assign_component_from_var_in_scope", + Eval_assign_component_from_var_in_scope + }, + { + "scope_w_component_after_const_var", + Eval_scope_w_component_after_const_var + }, + { + "component_after_const_paren_expr", + Eval_component_after_const_paren_expr + }, + { + "component_after_const_add_expr", + Eval_component_after_const_add_expr + }, + { + "component_after_const_sub_expr", + Eval_component_after_const_sub_expr + }, + { + "component_after_const_mul_expr", + Eval_component_after_const_mul_expr + }, + { + "component_after_const_div_expr", + Eval_component_after_const_div_expr + }, + { + "parse_with", + Eval_parse_with + }, + { + "parse_with_w_with", + Eval_parse_with_w_with + }, + { + "parse_with_w_tag", + Eval_parse_with_w_tag + }, + { + "parse_with_value", + Eval_parse_with_value + }, + { + "parse_with_2_values", + Eval_parse_with_2_values + }, + { + "parse_with_2_nested_values", + Eval_parse_with_2_nested_values + }, + { + "parse_with_var", + Eval_parse_with_var + }, + { + "parse_with_2_vars", + Eval_parse_with_2_vars + }, + { + "parse_with_2_nested_vars", + Eval_parse_with_2_nested_vars + }, + { + "parse_with_var_in_scope", + Eval_parse_with_var_in_scope + }, + { + "assign_const_w_expr", + Eval_assign_const_w_expr + }, + { + "const_w_type", + Eval_const_w_type + }, + { + "typed_const_w_composite_type", + Eval_typed_const_w_composite_type + }, + { + "assign_var_to_typed_const_w_composite_type", + Eval_assign_var_to_typed_const_w_composite_type + }, + { + "using_wildcard", + Eval_using_wildcard + }, + { + "single_line_comment_in_value", + Eval_single_line_comment_in_value + }, + { + "single_line_comment_in_value_after_scope", + Eval_single_line_comment_in_value_after_scope + }, + { + "multi_line_comment_in_value", + Eval_multi_line_comment_in_value + }, + { + "multi_line_comment_in_value_after_scope", + Eval_multi_line_comment_in_value_after_scope + }, + { + "module_stmt", + Eval_module_stmt + }, + { + "nested_module_stmt", + Eval_nested_module_stmt + }, + { + "module_stmt_w_scope", + Eval_module_stmt_w_scope + }, + { + "module_stmt_w_nested_scope", + Eval_module_stmt_w_nested_scope + }, + { + "assign_singleton_tag", + Eval_assign_singleton_tag + }, + { + "assign_singleton_component", + Eval_assign_singleton_component + }, + { + "assign_singleton_tag_w_scope", + Eval_assign_singleton_tag_w_scope + }, + { + "assign_singleton_2_tags_w_scope", + Eval_assign_singleton_2_tags_w_scope + }, + { + "assign_singleton_component_w_scope", + Eval_assign_singleton_component_w_scope + }, + { + "assign_singleton_2_components_w_scope", + Eval_assign_singleton_2_components_w_scope + }, + { + "with_pair_in_scope", + Eval_with_pair_in_scope + }, + { + "with_pair_component_in_scope", + Eval_with_pair_component_in_scope + }, + { + "pair_w_rel_var", + Eval_pair_w_rel_var + }, + { + "pair_w_tgt_var", + Eval_pair_w_tgt_var + }, + { + "component_in_with_scope_in_scope", + Eval_component_in_with_scope_in_scope + }, + { + "array_component", + Eval_array_component + }, + { + "on_set_w_kind_paren_no_reflection", + Eval_on_set_w_kind_paren_no_reflection + }, + { + "on_set_w_kind_paren", + Eval_on_set_w_kind_paren + }, + { + "on_set_w_kind_no_paren_no_reflection", + Eval_on_set_w_kind_no_paren_no_reflection + }, + { + "on_set_w_kind_no_paren", + Eval_on_set_w_kind_no_paren + }, + { + "on_set_w_single_assign", + Eval_on_set_w_single_assign + }, + { + "on_set_w_single_assign_scoped_w_value", + Eval_on_set_w_single_assign_scoped_w_value + }, + { + "on_set_w_single_assign_scoped_no_value", + Eval_on_set_w_single_assign_scoped_no_value + }, + { + "if_true", + Eval_if_true + }, + { + "if_true_no_else", + Eval_if_true_no_else + }, + { + "if_false", + Eval_if_false + }, + { + "if_true_var", + Eval_if_true_var + }, + { + "if_false_var", + Eval_if_false_var + }, + { + "if_10", + Eval_if_10 + }, + { + "if_0", + Eval_if_0 + }, + { + "if_true_in_scope", + Eval_if_true_in_scope + }, + { + "if_false_in_scope", + Eval_if_false_in_scope + }, + { + "if_lt", + Eval_if_lt + }, + { + "if_lt_const", + Eval_if_lt_const + }, + { + "isa_in_module", + Eval_isa_in_module + }, + { + "isa_hierarchy", + Eval_isa_hierarchy + }, + { + "isa_hierarchy_in_module", + Eval_isa_hierarchy_in_module + }, + { + "custom_isa_hierarchy_in_module", + Eval_custom_isa_hierarchy_in_module + }, + { + "custom_isa_hierarchy_in_subtree", + Eval_custom_isa_hierarchy_in_subtree + }, + { + "inherit_w_kind", + Eval_inherit_w_kind + }, + { + "inherit_w_kind_scope", + Eval_inherit_w_kind_scope + }, + { + "inherit_w_kind_value", + Eval_inherit_w_kind_value + }, + { + "inherit_w_kind_value_scope", + Eval_inherit_w_kind_value_scope + }, + { + "multiple_inherit_w_kind", + Eval_multiple_inherit_w_kind + }, + { + "multiple_inherit_w_kind_scope", + Eval_multiple_inherit_w_kind_scope + }, + { + "auto_override_tag", + Eval_auto_override_tag + }, + { + "auto_override_component", + Eval_auto_override_component + }, + { + "auto_override_pair", + Eval_auto_override_pair + }, + { + "auto_override_pair_component", + Eval_auto_override_pair_component + }, + { + "lowercase_prefab_kind", + Eval_lowercase_prefab_kind + }, + { + "assign_component_to_const", + Eval_assign_component_to_const + }, + { + "assign_component_member_to_const", + Eval_assign_component_member_to_const + }, + { + "prefab_w_slot", + Eval_prefab_w_slot + }, + { + "prefab_w_slot_variant", + Eval_prefab_w_slot_variant + }, + { + "const_w_component_expr", + Eval_const_w_component_expr + }, + { + "const_w_component_expr_in_scope", + Eval_const_w_component_expr_in_scope + }, + { + "const_w_component_expr_in_module", + Eval_const_w_component_expr_in_module + }, + { + "const_w_component_in_scope_expr_in_scope", + Eval_const_w_component_in_scope_expr_in_scope + }, + { + "const_w_component_in_scope_expr_in_module", + Eval_const_w_component_in_scope_expr_in_module + }, + { + "const_w_component_and_entity_in_scope_expr_in_scope", + Eval_const_w_component_and_entity_in_scope_expr_in_scope + }, + { + "const_w_component_and_entity_in_scope_expr_in_module", + Eval_const_w_component_and_entity_in_scope_expr_in_module + }, + { + "path_tag_in_scope", + Eval_path_tag_in_scope + }, + { + "path_tag_in_module", + Eval_path_tag_in_module + }, + { + "path_tag_in_nested_scope", + Eval_path_tag_in_nested_scope + }, + { + "path_tag_in_nested_module", + Eval_path_tag_in_nested_module + }, + { + "dont_inherit_script_pair", + Eval_dont_inherit_script_pair + }, + { + "update_script_w_anonymous", + Eval_update_script_w_anonymous + }, + { + "update_script_w_anonymous_paren", + Eval_update_script_w_anonymous_paren + }, + { + "clear_script", + Eval_clear_script + }, + { + "clear_script_w_anonymous", + Eval_clear_script_w_anonymous + }, + { + "clear_script_w_anonymous_paren", + Eval_clear_script_w_anonymous_paren + }, + { + "partial_assign", + Eval_partial_assign + }, + { + "partial_assign_nontrivial", + Eval_partial_assign_nontrivial + }, + { + "partial_assign_with", + Eval_partial_assign_with + }, + { + "partial_assign_nontrivial_with", + Eval_partial_assign_nontrivial_with + }, + { + "partial_assign_with_large_array", + Eval_partial_assign_with_large_array + }, + { + "non_trivial_var_component", + Eval_non_trivial_var_component + }, + { + "non_trivial_var_with", + Eval_non_trivial_var_with + }, + { + "update_template_w_tag", + Eval_update_template_w_tag + } +}; + +bake_test_case Template_testcases[] = { + { + "template_no_scope", + Template_template_no_scope + }, + { + "template_no_props", + Template_template_no_props + }, + { + "template_prop", + Template_template_prop + }, + { + "template_prop_space_colon", + Template_template_prop_space_colon + }, + { + "template_2_props", + Template_template_2_props + }, + { + "template_w_using", + Template_template_w_using + }, + { + "template_instance_w_default_values", + Template_template_instance_w_default_values + }, + { + "template_instance_w_assign_default_values", + Template_template_instance_w_assign_default_values + }, + { + "template_instance_w_overridden_values", + Template_template_instance_w_overridden_values + }, + { + "template_w_child", + Template_template_w_child + }, + { + "template_w_child_parse_script", + Template_template_w_child_parse_script + }, + { + "template_w_child_parse_script_twice", + Template_template_w_child_parse_script_twice + }, + { + "template_w_child_update_after_parse", + Template_template_w_child_update_after_parse + }, + { + "template_w_nested_child", + Template_template_w_nested_child + }, + { + "template_w_prefab", + Template_template_w_prefab + }, + { + "template_w_prefab_tree", + Template_template_w_prefab_tree + }, + { + "template_w_nested_template", + Template_template_w_nested_template + }, + { + "instantiate_prefab_w_template", + Template_instantiate_prefab_w_template + }, + { + "template_w_prefab_w_template", + Template_template_w_prefab_w_template + }, + { + "3_templates", + Template_3_templates + }, + { + "template_nested_w_default_var", + Template_template_nested_w_default_var + }, + { + "template_w_anonymous", + Template_template_w_anonymous + }, + { + "template_w_anonymous_parse_again", + Template_template_w_anonymous_parse_again + }, + { + "template_w_composite_prop", + Template_template_w_composite_prop + }, + { + "template_with_with", + Template_template_with_with + }, + { + "module_w_template", + Template_module_w_template + }, + { + "module_w_nested_template", + Template_module_w_nested_template + }, + { + "template_w_pair_w_this_var", + Template_template_w_pair_w_this_var + }, + { + "prop_without_using_meta", + Template_prop_without_using_meta + }, + { + "hoist_var", + Template_hoist_var + }, + { + "anonymous_template_instance", + Template_anonymous_template_instance + }, + { + "anonymous_template_instance_no_scope", + Template_anonymous_template_instance_no_scope + }, + { + "anonymous_template_instance_w_prop", + Template_anonymous_template_instance_w_prop + }, + { + "anonymous_template_instance_w_prop_no_scope", + Template_anonymous_template_instance_w_prop_no_scope + }, + { + "with_after_template", + Template_with_after_template + }, + { + "with_in_scope_after_template", + Template_with_in_scope_after_template + } +}; + +bake_test_case Error_testcases[] = { + { + "multi_line_comment_after_newline_before_newline_scope_open", + Error_multi_line_comment_after_newline_before_newline_scope_open + }, + { + "missing_end_of_scope", + Error_missing_end_of_scope + }, + { + "with_n_tags_2_levels_invalid_tag", + Error_with_n_tags_2_levels_invalid_tag + }, + { + "assignment_to_non_component", + Error_assignment_to_non_component + }, + { + "struct_w_member_w_assignment_to_nothing", + Error_struct_w_member_w_assignment_to_nothing + }, + { + "struct_w_member_w_assignment_to_empty_scope", + Error_struct_w_member_w_assignment_to_empty_scope + }, + { + "invalid_nested_assignment", + Error_invalid_nested_assignment + }, + { + "invalid_partial_pair_assignment", + Error_invalid_partial_pair_assignment + }, + { + "empty_assignment", + Error_empty_assignment + }, + { + "empty_assignment_before_end_of_scope", + Error_empty_assignment_before_end_of_scope + }, + { + "default_type_with_tag", + Error_default_type_with_tag + }, + { + "invalid_oneof", + Error_invalid_oneof + }, + { + "default_type_with_tag", + Error_default_type_with_tag + }, + { + "unterminated_multiline_string", + Error_unterminated_multiline_string + }, + { + "invalid_assign_multiline_string", + Error_invalid_assign_multiline_string + }, + { + "const_var_redeclare", + Error_const_var_redeclare + }, + { + "typed_const_w_composite_type_invalid_assignment", + Error_typed_const_w_composite_type_invalid_assignment + }, + { + "unterminated_multi_line_comment_in_value", + Error_unterminated_multi_line_comment_in_value + }, + { + "pair_w_rel_var_invalid_type", + Error_pair_w_rel_var_invalid_type + }, + { + "pair_w_tgt_var_invalid_type", + Error_pair_w_tgt_var_invalid_type + }, + { + "with_value_not_a_component", + Error_with_value_not_a_component + }, + { + "component_in_with_scope", + Error_component_in_with_scope + }, + { + "component_in_with_scope_nested", + Error_component_in_with_scope_nested + }, + { + "assign_after_with_in_scope", + Error_assign_after_with_in_scope + }, + { + "not_an_array_component", + Error_not_an_array_component + }, + { + "array_component_w_curly_brackets", + Error_array_component_w_curly_brackets + }, + { + "unknown_identifier", + Error_unknown_identifier + }, + { + "unknown_identifier_for_int_field", + Error_unknown_identifier_for_int_field + }, + { + "prefab_w_slot_no_parent", + Error_prefab_w_slot_no_parent + }, + { + "tag_not_found", + Error_tag_not_found + }, + { + "component_not_found", + Error_component_not_found + }, + { + "pair_first_not_found", + Error_pair_first_not_found + }, + { + "pair_second_not_found", + Error_pair_second_not_found + }, + { + "kind_not_found", + Error_kind_not_found + }, + { + "base_not_found", + Error_base_not_found + }, + { + "entity_w_anonymous_tag", + Error_entity_w_anonymous_tag + }, + { + "member_expr_without_value_end_of_scope", + Error_member_expr_without_value_end_of_scope + }, + { + "member_expr_without_value_comma", + Error_member_expr_without_value_comma + }, + { + "member_expr_without_value_newline", + Error_member_expr_without_value_newline + }, + { + "2_member_expr_without_value", + Error_2_member_expr_without_value + }, + { + "expr_junk_after_number", + Error_expr_junk_after_number + }, + { + "expr_junk_after_unary_minus", + Error_expr_junk_after_unary_minus + }, + { + "expr_comma_after_nothing", + Error_expr_comma_after_nothing + }, + { + "expr_digit_with_two_dots", + Error_expr_digit_with_two_dots + }, + { + "template_empty", + Error_template_empty + }, + { + "template_unresolved_tag", + Error_template_unresolved_tag + }, + { + "template_unresolved_component", + Error_template_unresolved_component + }, + { + "template_unresolved_pair_relationship", + Error_template_unresolved_pair_relationship + }, + { + "template_unresolved_pair_target", + Error_template_unresolved_pair_target + }, + { + "template_unresolved_with_tag", + Error_template_unresolved_with_tag + }, + { + "template_unresolved_with_component", + Error_template_unresolved_with_component + }, + { + "template_unresolved_with_pair_relationship", + Error_template_unresolved_with_pair_relationship + }, + { + "template_unresolved_with_pair_target", + Error_template_unresolved_with_pair_target + }, + { + "template_unresolved_tag_in_child", + Error_template_unresolved_tag_in_child + }, + { + "template_prop_no_type", + Error_template_prop_no_type + }, + { + "template_prop_no_default", + Error_template_prop_no_default + }, + { + "template_w_composite_prop_invalid_assignment", + Error_template_w_composite_prop_invalid_assignment + }, + { + "template_redeclare_prop_as_const", + Error_template_redeclare_prop_as_const + }, + { + "template_redeclare_prop_as_prop", + Error_template_redeclare_prop_as_prop + }, + { + "template_redeclare_const_as_const", + Error_template_redeclare_const_as_const + }, + { + "run_template_after_error", + Error_run_template_after_error + }, + { + "update_template_after_error", + Error_update_template_after_error + }, + { + "template_in_template", + Error_template_in_template + } +}; + +bake_test_case Expr_testcases[] = { + { + "add_2_int_literals", + Expr_add_2_int_literals + }, + { + "add_2_int_literals_twice", + Expr_add_2_int_literals_twice + }, + { + "sub_2_int_literals", + Expr_sub_2_int_literals + }, + { + "mul_2_int_literals", + Expr_mul_2_int_literals + }, + { + "div_2_int_literals", + Expr_div_2_int_literals + }, + { + "add_3_int_literals", + Expr_add_3_int_literals + }, + { + "add_3_int_literals_twice", + Expr_add_3_int_literals_twice + }, + { + "sub_3_int_literals", + Expr_sub_3_int_literals + }, + { + "mul_3_int_literals", + Expr_mul_3_int_literals + }, + { + "div_3_int_literals", + Expr_div_3_int_literals + }, + { + "int_to_bool", + Expr_int_to_bool + }, + { + "bool_to_int", + Expr_bool_to_int + }, + { + "bool_to_uint", + Expr_bool_to_uint + }, + { + "add_mul_3_int_literals", + Expr_add_mul_3_int_literals + }, + { + "sub_mul_3_int_literals", + Expr_sub_mul_3_int_literals + }, + { + "div_mul_3_int_literals", + Expr_div_mul_3_int_literals + }, + { + "add_div_3_int_literals", + Expr_add_div_3_int_literals + }, + { + "sub_div_3_int_literals", + Expr_sub_div_3_int_literals + }, + { + "mul_div_3_int_literals", + Expr_mul_div_3_int_literals + }, + { + "mul_add_mul_add_int_literals", + Expr_mul_add_mul_add_int_literals + }, + { + "mul_sub_mul_sub_int_literals", + Expr_mul_sub_mul_sub_int_literals + }, + { + "mul_div_mul_div_int_literals", + Expr_mul_div_mul_div_int_literals + }, + { + "div_add_div_add_int_literals", + Expr_div_add_div_add_int_literals + }, + { + "div_sub_div_sub_int_literals", + Expr_div_sub_div_sub_int_literals + }, + { + "div_sub_div_mul_int_literals", + Expr_div_sub_div_mul_int_literals + }, + { + "div_mul_div_mul_int_literals", + Expr_div_mul_div_mul_int_literals + }, + { + "add_2_flt_literals", + Expr_add_2_flt_literals + }, + { + "sub_2_flt_literals", + Expr_sub_2_flt_literals + }, + { + "mul_2_flt_literals", + Expr_mul_2_flt_literals + }, + { + "div_2_flt_literals", + Expr_div_2_flt_literals + }, + { + "add_2_int_neg_literals", + Expr_add_2_int_neg_literals + }, + { + "sub_2_int_neg_literals", + Expr_sub_2_int_neg_literals + }, + { + "mul_2_int_neg_literals", + Expr_mul_2_int_neg_literals + }, + { + "div_2_int_neg_literals", + Expr_div_2_int_neg_literals + }, + { + "mul_lparen_add_add_rparen_int_literals", + Expr_mul_lparen_add_add_rparen_int_literals + }, + { + "mul_lparen_add_add_add_rparen_int_literals", + Expr_mul_lparen_add_add_add_rparen_int_literals + }, + { + "mul_lparen_add_add_rparen_add_int_literals", + Expr_mul_lparen_add_add_rparen_add_int_literals + }, + { + "lparen_add_add_rparen_mul_int_literals", + Expr_lparen_add_add_rparen_mul_int_literals + }, + { + "lparen_add_add_add_rparen_mul_int_literals", + Expr_lparen_add_add_add_rparen_mul_int_literals + }, + { + "double_paren_add_add", + Expr_double_paren_add_add + }, + { + "double_paren_literal", + Expr_double_paren_literal + }, + { + "lparen_add_add_rparen_mul_lparen_add_add_rparen", + Expr_lparen_add_add_rparen_mul_lparen_add_add_rparen + }, + { + "float_result_add_2_int_literals", + Expr_float_result_add_2_int_literals + }, + { + "struct_result_add_2_int_literals", + Expr_struct_result_add_2_int_literals + }, + { + "struct_result_add_2_2_fields_int_literals", + Expr_struct_result_add_2_2_fields_int_literals + }, + { + "struct_result_add_3_int_literals", + Expr_struct_result_add_3_int_literals + }, + { + "struct_result_lparen_int_rparen", + Expr_struct_result_lparen_int_rparen + }, + { + "add_to_var", + Expr_add_to_var + }, + { + "add_var_to", + Expr_add_var_to + }, + { + "var_member", + Expr_var_member + }, + { + "bool_cond_and_bool", + Expr_bool_cond_and_bool + }, + { + "bool_cond_or_bool", + Expr_bool_cond_or_bool + }, + { + "int_cond_and_int", + Expr_int_cond_and_int + }, + { + "int_cond_or_int", + Expr_int_cond_or_int + }, + { + "bool_cond_and_int", + Expr_bool_cond_and_int + }, + { + "int_cond_and_bool", + Expr_int_cond_and_bool + }, + { + "bool_cond_or_int", + Expr_bool_cond_or_int + }, + { + "int_cond_or_bool", + Expr_int_cond_or_bool + }, + { + "cond_eq_bool", + Expr_cond_eq_bool + }, + { + "cond_eq_int", + Expr_cond_eq_int + }, + { + "cond_neq_bool", + Expr_cond_neq_bool + }, + { + "cond_neq_int", + Expr_cond_neq_int + }, + { + "cond_eq_bool_int", + Expr_cond_eq_bool_int + }, + { + "cond_eq_int_flt", + Expr_cond_eq_int_flt + }, + { + "cond_eq_cond_and", + Expr_cond_eq_cond_and + }, + { + "cond_eq_cond_or", + Expr_cond_eq_cond_or + }, + { + "cond_gt_bool", + Expr_cond_gt_bool + }, + { + "cond_gt_int", + Expr_cond_gt_int + }, + { + "cond_gt_flt", + Expr_cond_gt_flt + }, + { + "cond_gteq_bool", + Expr_cond_gteq_bool + }, + { + "cond_gteq_int", + Expr_cond_gteq_int + }, + { + "cond_gteq_flt", + Expr_cond_gteq_flt + }, + { + "cond_lt_bool", + Expr_cond_lt_bool + }, + { + "cond_lt_int", + Expr_cond_lt_int + }, + { + "cond_lt_flt", + Expr_cond_lt_flt + }, + { + "cond_lteq_bool", + Expr_cond_lteq_bool + }, + { + "cond_lteq_int", + Expr_cond_lteq_int + }, + { + "cond_lteq_flt", + Expr_cond_lteq_flt + }, + { + "min_lparen_int_rparen", + Expr_min_lparen_int_rparen + }, + { + "min_lparen_int_add_int_rparen", + Expr_min_lparen_int_add_int_rparen + }, + { + "min_var", + Expr_min_var + }, + { + "min_lparen_int_rparen_to_i64", + Expr_min_lparen_int_rparen_to_i64 + }, + { + "min_lparen_int_rparen_to_i32", + Expr_min_lparen_int_rparen_to_i32 + }, + { + "struct_w_min_var", + Expr_struct_w_min_var + }, + { + "struct_w_min_lparen_int_rparen", + Expr_struct_w_min_lparen_int_rparen + }, + { + "struct_w_min_lparen_var_rparen", + Expr_struct_w_min_lparen_var_rparen + }, + { + "shift_left_int", + Expr_shift_left_int + }, + { + "shift_right_int", + Expr_shift_right_int + }, + { + "shift_left_int_add_int", + Expr_shift_left_int_add_int + }, + { + "shift_left_int_mul_int", + Expr_shift_left_int_mul_int + }, + { + "add_int_shift_left_int", + Expr_add_int_shift_left_int + }, + { + "mul_int_shift_left_int", + Expr_mul_int_shift_left_int + }, + { + "add_int_shift_left_int_add_int", + Expr_add_int_shift_left_int_add_int + }, + { + "mul_int_shift_left_int_mul_int", + Expr_mul_int_shift_left_int_mul_int + }, + { + "entity_expr", + Expr_entity_expr + }, + { + "entity_path_expr", + Expr_entity_path_expr + }, + { + "entity_parent_func", + Expr_entity_parent_func + }, + { + "entity_name_func", + Expr_entity_name_func + }, + { + "entity_doc_name_func", + Expr_entity_doc_name_func + }, + { + "entity_chain_func", + Expr_entity_chain_func + }, + { + "var_parent_func", + Expr_var_parent_func + }, + { + "var_name_func", + Expr_var_name_func + }, + { + "var_doc_name_func", + Expr_var_doc_name_func + }, + { + "var_chain_func", + Expr_var_chain_func + }, + { + "interpolate_string_w_i32_var", + Expr_interpolate_string_w_i32_var + }, + { + "interpolate_string_w_string_var", + Expr_interpolate_string_w_string_var + }, + { + "interpolate_string_w_entity_var", + Expr_interpolate_string_w_entity_var + }, + { + "interpolate_string_w_id_var", + Expr_interpolate_string_w_id_var + }, + { + "interpolate_string_w_var_not_found", + Expr_interpolate_string_w_var_not_found + }, + { + "interpolate_string_w_entity_var_0", + Expr_interpolate_string_w_entity_var_0 + }, + { + "interpolate_string_w_var_special_chars", + Expr_interpolate_string_w_var_special_chars + }, + { + "interpolate_string_w_var_before_after_text", + Expr_interpolate_string_w_var_before_after_text + }, + { + "interpolate_string_w_curly_brackets_var", + Expr_interpolate_string_w_curly_brackets_var + }, + { + "interpolate_string_w_curly_brackets_expr", + Expr_interpolate_string_w_curly_brackets_expr + }, + { + "interpolate_string_w_curly_brackets_expr_w_var", + Expr_interpolate_string_w_curly_brackets_expr_w_var + }, + { + "interpolate_string_w_curly_brackets_expr_w_composite_var", + Expr_interpolate_string_w_curly_brackets_expr_w_composite_var + }, + { + "interpolate_string_w_escape_var_operator", + Expr_interpolate_string_w_escape_var_operator + }, + { + "interpolate_string_w_escape_curly_brackets", + Expr_interpolate_string_w_escape_curly_brackets + }, + { + "interpolate_string_w_func", + Expr_interpolate_string_w_func + }, + { + "interpolate_string_w_func_chain", + Expr_interpolate_string_w_func_chain + }, + { + "iter_to_vars_no_data", + Expr_iter_to_vars_no_data + }, + { + "iter_to_vars_1_comp", + Expr_iter_to_vars_1_comp + }, + { + "iter_to_vars_2_comps", + Expr_iter_to_vars_2_comps + }, + { + "iter_to_vars_1_comp_1_tag", + Expr_iter_to_vars_1_comp_1_tag + }, + { + "iter_to_vars_w_1_query_var", + Expr_iter_to_vars_w_1_query_var + }, + { + "iter_to_vars_w_2_query_vars", + Expr_iter_to_vars_w_2_query_vars + }, + { + "component_expr", + Expr_component_expr + }, + { + "component_member_expr", + Expr_component_member_expr + } +}; + +bake_test_case Vars_testcases[] = { + { + "declare_1_var", + Vars_declare_1_var + }, + { + "declare_2_vars", + Vars_declare_2_vars + }, + { + "declare_vars_nested_scope", + Vars_declare_vars_nested_scope + }, + { + "declare_vars_2_scopes", + Vars_declare_vars_2_scopes + }, + { + "redeclare_var", + Vars_redeclare_var + }, + { + "i32_expr_w_i32_var", + Vars_i32_expr_w_i32_var + }, + { + "i32_expr_w_f32_var", + Vars_i32_expr_w_f32_var + }, + { + "i32_expr_w_string_var", + Vars_i32_expr_w_string_var + }, + { + "string_expr_w_string_var", + Vars_string_expr_w_string_var + }, + { + "struct_expr_w_i32_vars", + Vars_struct_expr_w_i32_vars + }, + { + "struct_expr_w_struct_var", + Vars_struct_expr_w_struct_var + }, + { + "nested_struct_expr_w_struct_var", + Vars_nested_struct_expr_w_struct_var + }, + { + "redeclare_in_scope", + Vars_redeclare_in_scope + }, + { + "init_fini_vars", + Vars_init_fini_vars + } +}; + +bake_test_case Serialize_testcases[] = { + { + "bool", + Serialize_bool + }, + { + "byte", + Serialize_byte + }, + { + "char", + Serialize_char + }, + { + "i8", + Serialize_i8 + }, + { + "i16", + Serialize_i16 + }, + { + "i32", + Serialize_i32 + }, + { + "i64", + Serialize_i64 + }, + { + "iptr", + Serialize_iptr + }, + { + "u8", + Serialize_u8 + }, + { + "u16", + Serialize_u16 + }, + { + "u32", + Serialize_u32 + }, + { + "u64", + Serialize_u64 + }, + { + "uptr", + Serialize_uptr + }, + { + "float", + Serialize_float + }, + { + "double", + Serialize_double + }, + { + "string", + Serialize_string + }, + { + "entity", + Serialize_entity + }, + { + "entity_10k", + Serialize_entity_10k + }, + { + "id", + Serialize_id + }, + { + "enum", + Serialize_enum + }, + { + "bitmask", + Serialize_bitmask + }, + { + "float_nan", + Serialize_float_nan + }, + { + "float_inf", + Serialize_float_inf + }, + { + "double_nan", + Serialize_double_nan + }, + { + "double_inf", + Serialize_double_inf + }, + { + "struct_enum", + Serialize_struct_enum + }, + { + "struct_bitmask", + Serialize_struct_bitmask + }, + { + "struct_i32", + Serialize_struct_i32 + }, + { + "struct_i32_i32", + Serialize_struct_i32_i32 + }, + { + "struct_entity", + Serialize_struct_entity + }, + { + "struct_id", + Serialize_struct_id + }, + { + "array_i32_3", + Serialize_array_i32_3 + }, + { + "array_struct_i32_i32", + Serialize_array_struct_i32_i32 + }, + { + "array_array_i32_3", + Serialize_array_array_i32_3 + }, + { + "vector_i32_3", + Serialize_vector_i32_3 + }, + { + "vector_struct_i32_i32", + Serialize_vector_struct_i32_i32 + }, + { + "vector_array_i32_3", + Serialize_vector_array_i32_3 + }, + { + "entity_entity_after_float", + Serialize_entity_entity_after_float + }, + { + "struct_nested_i32", + Serialize_struct_nested_i32 + }, + { + "struct_nested_i32_i32", + Serialize_struct_nested_i32_i32 + }, + { + "struct_2_nested_i32_i32", + Serialize_struct_2_nested_i32_i32 + }, + { + "struct_i32_array_3", + Serialize_struct_i32_array_3 + }, + { + "struct_struct_i32_array_3", + Serialize_struct_struct_i32_array_3 + }, + { + "struct_struct_i32_i32_array_3", + Serialize_struct_struct_i32_i32_array_3 + }, + { + "struct_w_array_type_i32_i32", + Serialize_struct_w_array_type_i32_i32 + }, + { + "struct_w_array_type_struct", + Serialize_struct_w_array_type_struct + }, + { + "struct_w_2_array_type_i32_i32", + Serialize_struct_w_2_array_type_i32_i32 + }, + { + "struct_w_2_array_type_struct", + Serialize_struct_w_2_array_type_struct + }, + { + "struct_partial", + Serialize_struct_partial + }, + { + "escape_simple_string", + Serialize_escape_simple_string + }, + { + "escape_newline", + Serialize_escape_newline + }, + { + "escape_2_newlines", + Serialize_escape_2_newlines + }, + { + "escape_string_w_trailing_newline", + Serialize_escape_string_w_trailing_newline + }, + { + "escape_string_w_2_trailing_newlines", + Serialize_escape_string_w_2_trailing_newlines + }, + { + "escape_string_w_delim", + Serialize_escape_string_w_delim + } +}; + +bake_test_case Deserialize_testcases[] = { + { + "bool", + Deserialize_bool + }, + { + "byte", + Deserialize_byte + }, + { + "char", + Deserialize_char + }, + { + "char_literal", + Deserialize_char_literal + }, + { + "i8", + Deserialize_i8 + }, + { + "i16", + Deserialize_i16 + }, + { + "i32", + Deserialize_i32 + }, + { + "i64", + Deserialize_i64 + }, + { + "iptr", + Deserialize_iptr + }, + { + "u8", + Deserialize_u8 + }, + { + "u16", + Deserialize_u16 + }, + { + "u32", + Deserialize_u32 + }, + { + "u64", + Deserialize_u64 + }, + { + "uptr", + Deserialize_uptr + }, + { + "float", + Deserialize_float + }, + { + "double", + Deserialize_double + }, + { + "negative_int", + Deserialize_negative_int + }, + { + "negative_float", + Deserialize_negative_float + }, + { + "invalid_i8", + Deserialize_invalid_i8 + }, + { + "invalid_i16", + Deserialize_invalid_i16 + }, + { + "invalid_i32", + Deserialize_invalid_i32 + }, + { + "invalid_i64", + Deserialize_invalid_i64 + }, + { + "invalid_iptr", + Deserialize_invalid_iptr + }, + { + "invalid_u8", + Deserialize_invalid_u8 + }, + { + "invalid_u16", + Deserialize_invalid_u16 + }, + { + "invalid_u32", + Deserialize_invalid_u32 + }, + { + "invalid_u64", + Deserialize_invalid_u64 + }, + { + "invalid_uptr", + Deserialize_invalid_uptr + }, + { + "invalid_float", + Deserialize_invalid_float + }, + { + "invalid_double", + Deserialize_invalid_double + }, + { + "string", + Deserialize_string + }, + { + "entity", + Deserialize_entity + }, + { + "id", + Deserialize_id + }, + { + "enum", + Deserialize_enum + }, + { + "bitmask", + Deserialize_bitmask + }, + { + "struct_enum", + Deserialize_struct_enum + }, + { + "struct_bitmask", + Deserialize_struct_bitmask + }, + { + "struct_i32", + Deserialize_struct_i32 + }, + { + "struct_i32_neg", + Deserialize_struct_i32_neg + }, + { + "struct_i32_i32", + Deserialize_struct_i32_i32 + }, + { + "struct_entity", + Deserialize_struct_entity + }, + { + "struct_id", + Deserialize_struct_id + }, + { + "struct_nested_i32", + Deserialize_struct_nested_i32 + }, + { + "struct_nested_i32_i32", + Deserialize_struct_nested_i32_i32 + }, + { + "struct_2_nested_i32_i32", + Deserialize_struct_2_nested_i32_i32 + }, + { + "struct_member_i32", + Deserialize_struct_member_i32 + }, + { + "struct_member_i32_neg", + Deserialize_struct_member_i32_neg + }, + { + "struct_member_i32_i32", + Deserialize_struct_member_i32_i32 + }, + { + "struct_member_nested_i32", + Deserialize_struct_member_nested_i32 + }, + { + "struct_member_nested_i32_i32", + Deserialize_struct_member_nested_i32_i32 + }, + { + "struct_member_2_nested_i32_i32", + Deserialize_struct_member_2_nested_i32_i32 + }, + { + "struct_member_2_nested_i32_i32_reverse", + Deserialize_struct_member_2_nested_i32_i32_reverse + }, + { + "struct_i32_array_3", + Deserialize_struct_i32_array_3 + }, + { + "struct_struct_i32_array_3", + Deserialize_struct_struct_i32_array_3 + }, + { + "struct_struct_i32_i32_array_3", + Deserialize_struct_struct_i32_i32_array_3 + }, + { + "struct_w_array_type_i32_i32", + Deserialize_struct_w_array_type_i32_i32 + }, + { + "struct_w_array_type_struct", + Deserialize_struct_w_array_type_struct + }, + { + "struct_w_2_array_type_i32_i32", + Deserialize_struct_w_2_array_type_i32_i32 + }, + { + "struct_w_2_array_type_struct", + Deserialize_struct_w_2_array_type_struct + }, + { + "discover_type_int", + Deserialize_discover_type_int + }, + { + "discover_type_negative_int", + Deserialize_discover_type_negative_int + }, + { + "discover_type_float", + Deserialize_discover_type_float + }, + { + "discover_type_negative_float", + Deserialize_discover_type_negative_float + }, + { + "discover_type_string", + Deserialize_discover_type_string + }, + { + "discover_type_multiline_string", + Deserialize_discover_type_multiline_string + }, + { + "discover_type_entity", + Deserialize_discover_type_entity + }, + { + "discover_type_bool", + Deserialize_discover_type_bool + }, + { + "discover_type_unknown", + Deserialize_discover_type_unknown + }, + { + "discover_type_invalid", + Deserialize_discover_type_invalid + }, + { + "opaque_struct", + Deserialize_opaque_struct + }, + { + "opaque_struct_w_member", + Deserialize_opaque_struct_w_member + }, + { + "opaque_struct_w_member_reverse", + Deserialize_opaque_struct_w_member_reverse + }, + { + "struct_w_opaque_member", + Deserialize_struct_w_opaque_member + } +}; + + +static bake_test_suite suites[] = { + { + "Eval", + NULL, + NULL, + 252, + Eval_testcases + }, + { + "Template", + NULL, + NULL, + 36, + Template_testcases + }, + { + "Error", + NULL, + NULL, + 63, + Error_testcases + }, + { + "Expr", + NULL, + NULL, + 128, + Expr_testcases + }, + { + "Vars", + NULL, + NULL, + 14, + Vars_testcases + }, + { + "Serialize", + NULL, + NULL, + 55, + Serialize_testcases + }, + { + "Deserialize", + NULL, + NULL, + 73, + Deserialize_testcases + } +}; + +int main(int argc, char *argv[]) { + return bake_test_run("script", argc, argv, suites, 7); +} diff --git a/vendors/flecs/test/script/src/util.c b/vendors/flecs/test/script/src/util.c new file mode 100644 index 000000000..972f1a2e6 --- /dev/null +++ b/vendors/flecs/test/script/src/util.c @@ -0,0 +1,10 @@ +#include +#include + +void install_test_abort(void) { + ecs_os_set_api_defaults(); + ecs_os_api_t os_api = ecs_os_api; + os_api.abort_ = test_abort; + ecs_os_set_api(&os_api); + ecs_log_set_level(-5); +}

> + A* get_mut() const; + + /** Get mutable singleton pair. + */ + template + First* get_mut(Second second) const; /** Test if world has singleton component. */ @@ -20039,7 +21226,7 @@ struct world { bool has() const; /** Test if world has the provided pair. - * + * * @tparam First The first element of the pair * @tparam Second The second element of the pair */ @@ -20047,7 +21234,7 @@ struct world { bool has() const; /** Test if world has the provided pair. - * + * * @tparam First The first element of the pair * @param second The second element of the pair. */ @@ -20055,7 +21242,7 @@ struct world { bool has(flecs::id_t second) const; /** Test if world has the provided pair. - * + * * @param first The first element of the pair * @param second The second element of the pair */ @@ -20067,7 +21254,7 @@ struct world { void add() const; /** Adds a pair to the singleton component. - * + * * @tparam First The first element of the pair * @tparam Second The second element of the pair */ @@ -20075,7 +21262,7 @@ struct world { void add() const; /** Adds a pair to the singleton component. - * + * * @tparam First The first element of the pair * @param second The second element of the pair. */ @@ -20083,7 +21270,7 @@ struct world { void add(flecs::entity_t second) const; /** Adds a pair to the singleton entity. - * + * * @param first The first element of the pair * @param second The second element of the pair */ @@ -20095,7 +21282,7 @@ struct world { void remove() const; /** Removes the pair singleton component. - * + * * @tparam First The first element of the pair * @tparam Second The second element of the pair */ @@ -20103,7 +21290,7 @@ struct world { void remove() const; /** Removes the pair singleton component. - * + * * @tparam First The first element of the pair * @param second The second element of the pair. */ @@ -20111,15 +21298,18 @@ struct world { void remove(flecs::entity_t second) const; /** Removes the pair singleton component. - * + * * @param first The first element of the pair * @param second The second element of the pair */ void remove(flecs::entity_t first, flecs::entity_t second) const; - /** Iterate entities in root of world + /** Iterate entities in root of world * Accepts a callback with the following signature: - * void(*)(flecs::entity e); + * + * @code + * void(*)(flecs::entity e); + * @endcode */ template void children(Func&& f) const; @@ -20175,21 +21365,21 @@ struct world { * @param name Name of the entity. * @param alias Alias for the entity. */ - flecs::entity use(const char *name, const char *alias = nullptr) const; + flecs::entity use(const char *name, const char *alias = nullptr) const; /** Create alias for entity. * * @param entity Entity for which to create the alias. * @param alias Alias for the entity. */ - void use(flecs::entity entity, const char *alias = nullptr) const; + void use(flecs::entity entity, const char *alias = nullptr) const; /** Count entities matching a component. * * @param component_id The component id. */ int count(flecs::id_t component_id) const { - return ecs_count_id(m_world, component_id); + return ecs_count_id(world_, component_id); } /** Count entities matching a pair. @@ -20198,7 +21388,7 @@ struct world { * @param second The second element of the pair. */ int count(flecs::entity_t first, flecs::entity_t second) const { - return ecs_count_id(m_world, ecs_pair(first, second)); + return ecs_count_id(world_, ecs_pair(first, second)); } /** Count entities matching a component. @@ -20207,7 +21397,7 @@ struct world { */ template int count() const { - return count(_::cpp_type::id(m_world)); + return count(_::type::id(world_)); } /** Count entities matching a pair. @@ -20217,7 +21407,7 @@ struct world { */ template int count(flecs::entity_t second) const { - return count(_::cpp_type::id(m_world), second); + return count(_::type::id(world_), second); } /** Count entities matching a pair. @@ -20227,18 +21417,18 @@ struct world { */ template int count() const { - return count( - _::cpp_type::id(m_world), - _::cpp_type::id(m_world)); + return count( + _::type::id(world_), + _::type::id(world_)); } /** All entities created in function are created with id. */ template void with(id_t with_id, const Func& func) const { - ecs_id_t prev = ecs_set_with(m_world, with_id); + ecs_id_t prev = ecs_set_with(world_, with_id); func(); - ecs_set_with(m_world, prev); + ecs_set_with(world_, prev); } /** All entities created in function are created with type. @@ -20260,7 +21450,7 @@ struct world { template void with(id_t second, const Func& func) const { with(ecs_pair(this->id(), second), func); - } + } /** All entities created in function are created with pair. */ @@ -20274,16 +21464,16 @@ struct world { */ template void scope(id_t parent, const Func& func) const { - ecs_entity_t prev = ecs_set_scope(m_world, parent); + ecs_entity_t prev = ecs_set_scope(world_, parent); func(); - ecs_set_scope(m_world, prev); + ecs_set_scope(world_, prev); } - + /** Same as scope(parent, func), but with T as parent. */ template void scope(const Func& func) const { - flecs::id_t parent = _::cpp_type::id(m_world); + flecs::id_t parent = _::type::id(world_); scope(parent, func); } @@ -20299,7 +21489,7 @@ struct world { /** Delete all entities with specified id. */ void delete_with(id_t the_id) const { - ecs_delete_with(m_world, the_id); + ecs_delete_with(world_, the_id); } /** Delete all entities with specified pair. */ @@ -20310,18 +21500,24 @@ struct world { /** Delete all entities with specified component. */ template void delete_with() const { - delete_with(_::cpp_type::id(m_world)); + delete_with(_::type::id(world_)); } /** Delete all entities with specified pair. */ template void delete_with() const { - delete_with(_::cpp_type::id(m_world), _::cpp_type::id(m_world)); + delete_with(_::type::id(world_), _::type::id(world_)); + } + + /** Delete all entities with specified pair. */ + template + void delete_with(entity_t second) const { + delete_with(_::type::id(world_), second); } /** Remove all instances of specified id. */ void remove_all(id_t the_id) const { - ecs_remove_all(m_world, the_id); + ecs_remove_all(world_, the_id); } /** Remove all instances of specified pair. */ @@ -20332,95 +21528,116 @@ struct world { /** Remove all instances of specified component. */ template void remove_all() const { - remove_all(_::cpp_type::id(m_world)); + remove_all(_::type::id(world_)); } /** Remove all instances of specified pair. */ template void remove_all() const { - remove_all(_::cpp_type::id(m_world), _::cpp_type::id(m_world)); + remove_all(_::type::id(world_), _::type::id(world_)); + } + + /** Remove all instances of specified pair. */ + template + void remove_all(entity_t second) const { + remove_all(_::type::id(world_), second); } - /** Defer all operations called in function. If the world is already in - * deferred mode, do nothing. + /** Defer all operations called in function. + * + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_is_deferred() + * @see flecs::world::defer_resume() + * @see flecs::world::defer_suspend() */ template void defer(const Func& func) const { - ecs_defer_begin(m_world); + ecs_defer_begin(world_); func(); - ecs_defer_end(m_world); + ecs_defer_end(world_); } /** Suspend deferring operations. - * - * @see ecs_defer_suspend + * + * @see ecs_defer_suspend() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_is_deferred() + * @see flecs::world::defer_resume() */ void defer_suspend() const { - ecs_defer_suspend(m_world); + ecs_defer_suspend(world_); } /** Resume deferring operations. - * - * @see ecs_defer_suspend + * + * @see ecs_defer_resume() + * @see flecs::world::defer() + * @see flecs::world::defer_begin() + * @see flecs::world::defer_end() + * @see flecs::world::defer_is_deferred() + * @see flecs::world::defer_suspend() */ void defer_resume() const { - ecs_defer_resume(m_world); + ecs_defer_resume(world_); } /** Check if entity id exists in the world. - * - * @see ecs_exists + * + * @see ecs_exists() + * @see flecs::world::is_alive() + * @see flecs::world::is_valid() */ bool exists(flecs::entity_t e) const { - return ecs_exists(m_world, e); + return ecs_exists(world_, e); } /** Check if entity id exists in the world. * - * @see ecs_is_alive + * @see ecs_is_alive() + * @see flecs::world::exists() + * @see flecs::world::is_valid() */ bool is_alive(flecs::entity_t e) const { - return ecs_is_alive(m_world, e); + return ecs_is_alive(world_, e); } /** Check if entity id is valid. * Invalid entities cannot be used with API functions. - * - * @see ecs_is_valid + * + * @see ecs_is_valid() + * @see flecs::world::exists() + * @see flecs::world::is_alive() */ bool is_valid(flecs::entity_t e) const { - return ecs_is_valid(m_world, e); + return ecs_is_valid(world_, e); } /** Get alive entity for id. * Returns the entity with the current generation. - * - * @see ecs_get_alive + * + * @see ecs_get_alive() */ flecs::entity get_alive(flecs::entity_t e) const; -/* Prevent clashing with Unreal define. Unreal applications will have to use - * ecs_ensure. */ -#ifndef ensure - /** Ensures that entity with provided generation is alive. - * Ths operation will fail if an entity exists with the same id and a - * different, non-zero generation. - * - * @see ecs_ensure + /** + * @see ecs_make_alive() */ - flecs::entity ensure(flecs::entity_t e) const; -#endif + flecs::entity make_alive(flecs::entity_t e) const; /* Run callback after completing frame */ void run_post_frame(ecs_fini_action_t action, void *ctx) const { - ecs_run_post_frame(m_world, action, ctx); + ecs_run_post_frame(world_, action, ctx); } /** Get the world info. - * @see ecs_get_world_info + * + * @see ecs_get_world_info() */ const flecs::world_info_t* get_info() const{ - return ecs_get_world_info(m_world); + return ecs_get_world_info(world_); } /** Get delta_time */ @@ -20435,35 +21652,35 @@ struct world { /** Get id from a type. * - * \memberof flecs::world + * @memberof flecs::world */ template flecs::id id() const; /** Id factory. * - * \memberof flecs::world + * @memberof flecs::world */ template flecs::id id(Args&&... args) const; /** Get pair id from relationship, object. * - * \memberof flecs::world + * @memberof flecs::world */ template flecs::id pair() const; /** Get pair id from relationship, object. * - * \memberof flecs::world + * @memberof flecs::world */ template flecs::id pair(entity_t o) const; /** Get pair id from relationship, object. * - * \memberof flecs::world + * @memberof flecs::world */ flecs::id pair(entity_t r, entity_t o) const; @@ -20474,8 +21691,8 @@ flecs::id pair(entity_t r, entity_t o) const; /** Find or register component. * - * \ingroup cpp_components - * \memberof flecs::world + * @ingroup cpp_components + * @memberof flecs::world */ template flecs::component component(Args &&... args) const; @@ -20483,8 +21700,8 @@ flecs::component component(Args &&... args) const; /** Find or register untyped component. * Method available on flecs::world class. * - * \ingroup cpp_components - * \memberof flecs::world + * @ingroup cpp_components + * @memberof flecs::world */ template flecs::untyped_component component(Args &&... args) const; @@ -20496,48 +21713,48 @@ flecs::untyped_component component(Args &&... args) const; /** Create an entity. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template flecs::entity entity(Args &&... args) const; /** Convert enum constant to entity. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template ::value > = 0> flecs::id id(E value) const; /** Convert enum constant to entity. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template ::value > = 0> flecs::entity entity(E value) const; /** Create a prefab. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template flecs::entity prefab(Args &&... args) const; /** Create an entity that's associated with a type. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template flecs::entity entity(const char *name = nullptr) const; /** Create a prefab that's associated with a type. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template flecs::entity prefab(const char *name = nullptr) const; @@ -20549,25 +21766,25 @@ flecs::entity prefab(const char *name = nullptr) const; /** * @defgroup cpp_addons_event Events - * @brief API for emitting events. - * - * \ingroup cpp_addons + * @ingroup cpp_addons + * API for emitting events. + * * @{ */ /** Create a new event. - * - * \memberof flecs::world - * + * + * @memberof flecs::world + * * @param evt The event id. * @return Event builder. */ flecs::event_builder event(flecs::entity_t evt) const; /** Create a new event. - * - * \memberof flecs::world - * + * + * @memberof flecs::world + * * @tparam E The event type. * @return Event builder. */ @@ -20582,8 +21799,10 @@ flecs::event_builder_typed event() const; */ /** - * \memberof flecs::world - * \ingroup cpp_core_filters + * @memberof flecs::world + * @ingroup cpp_core_queries + * + * @{ */ /** Create a term. @@ -20604,54 +21823,6 @@ flecs::term term() const; /** @} */ -/** - * @file addons/cpp/mixins/filter/mixin.inl - * @brief Filter world mixin. - */ - -/** - * \memberof flecs::world - * \ingroup cpp_core_filters - */ - -/** Create a filter. - * - * @see ecs_filter_init - */ -template -flecs::filter filter(Args &&... args) const; - -/** Create a filter builder. - * - * @see ecs_filter_init - */ -template -flecs::filter_builder filter_builder(Args &&... args) const; - -/** Iterate over all entities with components in argument list of function. - * The function parameter must match the following signature: - * void(*)(T&, U&, ...) or - * void(*)(flecs::entity, T&, U&, ...) - * - */ -template -void each(Func&& func) const; - -/** Iterate over all entities with provided component. - * The function parameter must match the following signature: - * void(*)(T&) or - * void(*)(flecs::entity, T&) - * - */ -template -void each(Func&& func) const; - -/** Iterate over all entities with provided (component) id. */ -template -void each(flecs::id_t term_id, Func&& func) const; - -/** @} */ - /** * @file addons/cpp/mixins/observer/mixin.inl * @brief Observer world mixin. @@ -20659,8 +21830,10 @@ void each(flecs::id_t term_id, Func&& func) const; /** Observer builder. * - * \memberof flecs::world - * \ingroup cpp_observers + * @memberof flecs::world + * @ingroup cpp_observers + * + * @{ */ /** Upcast entity to an observer. @@ -20688,28 +21861,70 @@ flecs::observer_builder observer(Args &&... args) const; */ /** - * \memberof flecs::world - * \ingroup cpp_core_queries + * @memberof flecs::world + * @ingroup cpp_core_queries + * + * @{ */ /** Create a query. + * * @see ecs_query_init */ template flecs::query query(Args &&... args) const; -/** Create a subquery. +/** Create a query from entity. + * * @see ecs_query_init */ -template -flecs::query query(flecs::query_base& parent, Args &&... args) const; +flecs::query<> query(flecs::entity query_entity) const; /** Create a query builder. + * * @see ecs_query_init */ template flecs::query_builder query_builder(Args &&... args) const; +/** Iterate over all entities with components in argument list of function. + * The function parameter must match the following signature: + * + * @code + * void(*)(T&, U&, ...) + * @endcode + * + * or: + * + * @code + * void(*)(flecs::entity, T&, U&, ...) + * @endcode + * + */ +template +void each(Func&& func) const; + +/** Iterate over all entities with provided component. + * The function parameter must match the following signature: + * + * @code + * void(*)(T&) + * @endcode + * + * or: + * + * @code + * void(*)(flecs::entity, T&) + * @endcode + * + */ +template +void each(Func&& func) const; + +/** Iterate over all entities with provided (component) id. */ +template +void each(flecs::id_t term_id, Func&& func) const; + /** @} */ /** @@ -20719,8 +21934,8 @@ flecs::query_builder query_builder(Args &&... args) const; /** Convert enum constant to entity. * - * \memberof flecs::world - * \ingroup cpp_entities + * @memberof flecs::world + * @ingroup cpp_entities */ template ::value > = 0> flecs::entity to_entity(E constant) const; @@ -20733,8 +21948,8 @@ flecs::entity to_entity(E constant) const; */ /** - * \memberof flecs::world - * \ingroup cpp_addons_modules + * @memberof flecs::world + * @ingroup cpp_addons_modules * * @{ */ @@ -20767,8 +21982,10 @@ flecs::entity import(); */ /** - * \memberof flecs::world - * \ingroup cpp_pipelines + * @memberof flecs::world + * @ingroup cpp_pipelines + * + * @{ */ /** Create a new pipeline. @@ -20855,25 +22072,6 @@ bool using_task_threads() const; /** @} */ -# endif -# ifdef FLECS_SNAPSHOT -/** - * @file addons/cpp/mixins/snapshot/mixin.inl - * @brief Snapshot world mixin. - */ - -/** - * \memberof flecs::world - * \ingroup cpp_addons_snapshot - */ - -/** Create a snapshot. - */ -template -flecs::snapshot snapshot(Args &&... args) const; - -/** @} */ - # endif # ifdef FLECS_SYSTEM /** @@ -20882,8 +22080,10 @@ flecs::snapshot snapshot(Args &&... args) const; */ /** - * \memberof flecs::world - * \ingroup cpp_addons_system + * @memberof flecs::world + * @ingroup cpp_addons_systems + * + * @{ */ /** Upcast entity to a system. @@ -20913,10 +22113,14 @@ flecs::system_builder system(Args &&... args) const; */ /** - * \memberof flecs::world - * \ingroup cpp_addons_timer + * @memberof flecs::world + * @ingroup cpp_addons_timer */ +/** Find or register a singleton timer. */ +template +flecs::timer timer() const; + /** Find or register a timer. */ template flecs::timer timer(Args &&... args) const; @@ -20927,66 +22131,55 @@ flecs::timer timer(Args &&... args) const; void randomize_timers() const; # endif -# ifdef FLECS_RULES +# ifdef FLECS_SCRIPT /** - * @file addons/cpp/mixins/rule/mixin.inl - * @brief Rule world mixin. + * @file addons/cpp/mixins/script/mixin.inl + * @brief Script world mixin. */ /** - * \memberof flecs::world - * \ingroup cpp_addons_rules - */ - -/** Create a rule. - * @see ecs_rule_init - */ -template -flecs::rule rule(Args &&... args) const; - -/** Create a subrule. - * @see ecs_rule_init + * @defgroup cpp_addons_script Script + * @ingroup cpp_addons + * Data definition format for loading entity data. + * + * @{ */ -template -flecs::rule rule(flecs::rule_base& parent, Args &&... args) const; -/** Create a rule builder. - * @see ecs_rule_init +/** Run script. + * @see ecs_script_run */ -template -flecs::rule_builder rule_builder(Args &&... args) const; - -/** @} */ +int script_run(const char *name, const char *str) const { + return ecs_script_run(world_, name, str); +} -# endif -# ifdef FLECS_PLECS -/** - * @file addons/cpp/mixins/plecs/mixin.inl - * @brief Plecs world mixin. +/** Run script from file. + * @see ecs_script_run_file */ +int script_run_file(const char *filename) const { + return ecs_script_run_file(world_, filename); +} -/** - * @defgroup cpp_addons_plecs Plecs - * @brief Data definition format for loading entity data. - * - * \ingroup cpp_addons - * @{ +/** Build script. + * @see ecs_script_init */ +script_builder script(const char *name = nullptr) const { + return script_builder(world_, name); +} -/** Load plecs string. - * @see ecs_plecs_from_str - */ -int plecs_from_str(const char *name, const char *str) const { - return ecs_plecs_from_str(m_world, name, str); +/** Convert value to string */ +flecs::string to_expr(flecs::entity_t tid, const void* value) { + char *expr = ecs_ptr_to_expr(world_, tid, value); + return flecs::string(expr); } -/** Load plecs from file. - * @see ecs_plecs_from_file - */ -int plecs_from_file(const char *filename) const { - return ecs_plecs_from_file(m_world, filename); +/** Convert value to string */ +template +flecs::string to_expr(const T* value) { + flecs::entity_t tid = _::type::id(world_); + return to_expr(tid, value); } + /** @} */ # endif @@ -20997,34 +22190,21 @@ int plecs_from_file(const char *filename) const { */ /** - * \memberof flecs::world - * \ingroup cpp_addons_meta + * @memberof flecs::world + * @ingroup cpp_addons_meta * * @{ */ -/** 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::cursor cursor(flecs::entity_t tid, void *ptr) { - return flecs::cursor(m_world, tid, ptr); + return flecs::cursor(world_, tid, ptr); } /** Return meta cursor to value */ template flecs::cursor cursor(void *ptr) { - flecs::entity_t tid = _::cpp_type::id(m_world); + flecs::entity_t tid = _::type::id(world_); return cursor(tid, ptr); } @@ -21056,61 +22236,70 @@ flecs::entity vector(); /** Serialize untyped value to JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ flecs::string to_json(flecs::entity_t tid, const void* value) { - char *json = ecs_ptr_to_json(m_world, tid, value); + char *json = ecs_ptr_to_json(world_, tid, value); return flecs::string(json); } /** Serialize value to JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ template flecs::string to_json(const T* value) { - flecs::entity_t tid = _::cpp_type::id(m_world); + flecs::entity_t tid = _::type::id(world_); return to_json(tid, value); } /** Serialize world to JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ flecs::string to_json() { - return flecs::string( ecs_world_to_json(m_world, nullptr) ); + return flecs::string( ecs_world_to_json(world_, nullptr) ); } /** Deserialize value from JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ const char* from_json(flecs::entity_t tid, void* value, const char *json, flecs::from_json_desc_t *desc = nullptr) { - return ecs_ptr_from_json(m_world, tid, value, json, desc); + return ecs_ptr_from_json(world_, tid, value, json, desc); } /** Deserialize value from JSON. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ template const char* from_json(T* value, const char *json, flecs::from_json_desc_t *desc = nullptr) { - return ecs_ptr_from_json(m_world, _::cpp_type::id(m_world), + return ecs_ptr_from_json(world_, _::type::id(world_), value, json, desc); } /** Deserialize JSON into world. * - * \memberof flecs::world - * \ingroup cpp_addons_json + * @memberof flecs::world + * @ingroup cpp_addons_json */ const char* from_json(const char *json, flecs::from_json_desc_t *desc = nullptr) { - return ecs_world_from_json(m_world, json, desc); + return ecs_world_from_json(world_, json, desc); +} + +/** Deserialize JSON file into world. + * + * @memberof flecs::world + * @ingroup cpp_addons_json + */ +const char* from_json_file(const char *json, flecs::from_json_desc_t *desc = nullptr) { + return ecs_world_from_json_file(world_, json, desc); } # endif @@ -21120,18 +22309,23 @@ const char* from_json(const char *json, flecs::from_json_desc_t *desc = nullptr) * @brief App world addon mixin. */ +/** + * @ingroup cpp_addons_app + * @memberof flecs::world + * + * @{ + */ + /** Return app builder. * The app builder is a convenience wrapper around a loop that runs * world::progress. An app allows for writing platform agnostic code, * as it provides hooks to modules for overtaking the main loop which is * required for frameworks like emscripten. - * - * \ingroup cpp_addons_app - * \memberof flecs::world */ flecs::app_builder app() { - m_owned = false; // App takes ownership of world - return flecs::app_builder(m_world); + flecs::world_t *w = world_; + world_ = nullptr; // Take ownership + return flecs::app_builder(w); } /** @} */ @@ -21141,8 +22335,8 @@ flecs::app_builder app() { /** Create metric. * - * \ingroup cpp_addons_metrics - * \memberof flecs::world + * @ingroup cpp_addons_metrics + * @memberof flecs::world */ template flecs::metric_builder metric(Args &&... args) const; @@ -21152,8 +22346,8 @@ flecs::metric_builder metric(Args &&... args) const; /** Create alert. * - * \ingroup cpp_addons_alerts - * \memberof flecs::world + * @ingroup cpp_addons_alerts + * @memberof flecs::world */ template flecs::alert_builder alert(Args &&... args) const; @@ -21163,8 +22357,7 @@ flecs::alert_builder alert(Args &&... args) const; public: void init_builtin_components(); - world_t *m_world; - bool m_owned; + world_t *world_; }; /** Scoped world. @@ -21172,149 +22365,160 @@ flecs::alert_builder alert(Args &&... args) const; */ struct scoped_world : world { scoped_world( - flecs::world_t *w, - flecs::entity_t s) : world(nullptr) + flecs::world_t *w, + flecs::entity_t s) : world(w) { - m_prev_scope = ecs_set_scope(w, s); - m_world = w; - m_owned = false; + prev_scope_ = ecs_set_scope(w, s); } ~scoped_world() { - ecs_set_scope(m_world, m_prev_scope); + ecs_set_scope(world_, prev_scope_); } scoped_world(const scoped_world& obj) : world(nullptr) { - m_prev_scope = obj.m_prev_scope; - m_world = obj.m_world; - m_owned = obj.m_owned; + prev_scope_ = obj.prev_scope_; + world_ = obj.world_; + flecs_poly_claim(world_); } - flecs::entity_t m_prev_scope; + flecs::entity_t prev_scope_; }; /** @} */ } // namespace flecs + /** - * @file addons/cpp/iter.hpp - * @brief Wrapper classes for ecs_iter_t and component arrays. + * @file addons/cpp/field.hpp + * @brief Wrapper classes for fields returned by flecs::iter. */ #pragma once /** - * @defgroup cpp_iterator Iterators - * @brief Iterator operations. - * - * \ingroup cpp_core + * @defgroup cpp_field Fields + * @ingroup cpp_core + * Field helper types. + * * @{ */ -namespace flecs +namespace flecs { -/** Unsafe wrapper class around a column. - * This class can be used when a system does not know the type of a column at +/** Unsafe wrapper class around a field. + * This class can be used when a system does not know the type of a field at * compile time. - * - * \ingroup cpp_iterator + * + * @ingroup cpp_iterator */ -struct untyped_column { - untyped_column(void* array, size_t size, size_t count, bool is_shared = false) - : m_array(array) - , m_size(size) - , m_count(count) - , m_is_shared(is_shared) {} +struct untyped_field { + untyped_field(void* array, size_t size, size_t count, bool is_shared = false) + : data_(array) + , size_(size) + , count_(count) + , is_shared_(is_shared) {} /** Return element in component array. - * This operator may only be used if the column is not shared. - * + * This operator may only be used if the field is not shared. + * * @param index Index of element. * @return Reference to element. */ void* operator[](size_t index) const { - ecs_assert(index < m_count, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - ecs_assert(!m_is_shared, ECS_INVALID_PARAMETER, NULL); - return ECS_OFFSET(m_array, m_size * index); + ecs_assert(!is_shared_, ECS_INVALID_PARAMETER, + "invalid usage of [] operator for shared component field"); + ecs_assert(index < count_, ECS_COLUMN_INDEX_OUT_OF_RANGE, + "index %d out of range for field", index); + return ECS_OFFSET(data_, size_ * index); } protected: - void* m_array; - size_t m_size; - size_t m_count; - bool m_is_shared; + void* data_; + size_t size_; + size_t count_; + bool is_shared_; }; -/** Wrapper class around a column. - * - * @tparam T component type of the column. - * - * \ingroup cpp_iterator +/** Wrapper class around a field. + * + * @tparam T component type of the field. + * + * @ingroup cpp_iterator */ template -struct column { - static_assert(std::is_empty::value == false, - "invalid type for column, cannot iterate empty type"); +struct field { + static_assert(std::is_empty::value == false, + "invalid type for field, cannot iterate empty type"); - /** Create column from component array. + /** Create field from component array. * * @param array Pointer to the component array. * @param count Number of elements in component array. * @param is_shared Is the component shared or not. */ - column(T* array, size_t count, bool is_shared = false) - : m_array(array) - , m_count(count) - , m_is_shared(is_shared) {} + field(T* array, size_t count, bool is_shared = false) + : data_(array) + , count_(count) + , is_shared_(is_shared) {} - /** Create column from iterator. + /** Create field from iterator. * * @param iter Iterator object. - * @param column Index of the signature of the query being iterated over. + * @param field Index of the signature of the query being iterated over. */ - column(iter &iter, int column); + field(iter &iter, int field); /** Return element in component array. - * This operator may only be used if the column is not shared. - * + * This operator may only be used if the field is not shared. + * * @param index Index of element. * @return Reference to element. */ - T& operator[](size_t index) const { - ecs_assert(index < m_count, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - ecs_assert(!index || !m_is_shared, ECS_INVALID_PARAMETER, NULL); - ecs_assert(m_array != nullptr, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - return m_array[index]; - } - + T& operator[](size_t index) const; + /** Return first element of component array. - * This operator is typically used when the column is shared. + * This operator is typically used when the field is shared. * * @return Reference to the first element. */ - T& operator*() const { - ecs_assert(m_array != nullptr, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - return *m_array; - } + T& operator*() const; /** Return first element of component array. - * This operator is typically used when the column is shared. - * + * This operator is typically used when the field is shared. + * * @return Pointer to the first element. */ - T* operator->() const { - ecs_assert(m_array != nullptr, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); - return m_array; - } + T* operator->() const; protected: - T* m_array; - size_t m_count; - bool m_is_shared; + T* data_; + size_t count_; + bool is_shared_; }; +} // namespace flecs + +/** @} */ + +/** + * @file addons/cpp/iter.hpp + * @brief Wrapper classes for ecs_iter_t and component arrays. + */ + +#pragma once + +/** + * @defgroup cpp_iterator Iterators + * @ingroup cpp_core + * Iterator operations. + * + * @{ + */ + +namespace flecs +{ //////////////////////////////////////////////////////////////////////////////// @@ -21330,26 +22534,26 @@ template struct range_iterator { explicit range_iterator(T value) - : m_value(value){} + : value_(value){} bool operator!=(range_iterator const& other) const { - return m_value != other.m_value; + return value_ != other.value_; } T const& operator*() const { - return m_value; + return value_; } range_iterator& operator++() { - ++m_value; + ++value_; return *this; } private: - T m_value; + T value_; }; } // namespace _ @@ -21362,30 +22566,27 @@ namespace flecs //////////////////////////////////////////////////////////////////////////////// /** Class for iterating over query results. - * - * \ingroup cpp_iterator + * + * @ingroup cpp_iterator */ struct iter { private: using row_iterator = _::range_iterator; - + public: /** Construct iterator from C iterator object. * This operation is typically not invoked directly by the user. * * @param it Pointer to C iterator. */ - iter(ecs_iter_t *it) : m_iter(it) { - m_begin = 0; - m_end = static_cast(it->count); - } + iter(ecs_iter_t *it) : iter_(it) { } row_iterator begin() const { - return row_iterator(m_begin); + return row_iterator(0); } row_iterator end() const { - return row_iterator(m_end); + return row_iterator(static_cast(iter_->count)); } flecs::entity system() const; @@ -21397,56 +22598,62 @@ struct iter { flecs::world world() const; const flecs::iter_t* c_ptr() const { - return m_iter; + return iter_; } size_t count() const { - return static_cast(m_iter->count); + ecs_check(iter_->flags & EcsIterIsValid, ECS_INVALID_PARAMETER, + "operation invalid before calling next()"); + return static_cast(iter_->count); + error: + return 0; } ecs_ftime_t delta_time() const { - return m_iter->delta_time; + return iter_->delta_time; } ecs_ftime_t delta_system_time() const { - return m_iter->delta_system_time; + return iter_->delta_system_time; } flecs::type type() const; flecs::table table() const; + flecs::table other_table() const; + flecs::table_range range() const; - /** Access ctx. + /** Access ctx. * ctx contains the context pointer assigned to a system. */ void* ctx() { - return m_iter->ctx; + return iter_->ctx; } - /** Access ctx. + /** Access ctx. * ctx contains the context pointer assigned to a system. */ template T* ctx() { - return static_cast(m_iter->ctx); + return static_cast(iter_->ctx); } - /** Access param. + /** Access param. * param contains the pointer passed to the param argument of system::run */ void* param() { - return m_iter->param; + return iter_->param; } - /** Access param. + /** Access param. * param contains the pointer passed to the param argument of system::run */ template T* param() { /* TODO: type check */ - return static_cast(m_iter->param); + return static_cast(iter_->param); } /** Obtain mutable handle to entity being iterated over. @@ -21456,74 +22663,80 @@ struct iter { flecs::entity entity(size_t row) const; /** Returns whether field is matched on self. - * + * * @param index The field index. */ - bool is_self(int32_t index) const { - return ecs_field_is_self(m_iter, index); + bool is_self(int8_t index) const { + return ecs_field_is_self(iter_, index); } /** Returns whether field is set. - * + * * @param index The field index. */ - bool is_set(int32_t index) const { - return ecs_field_is_set(m_iter, index); + bool is_set(int8_t index) const { + return ecs_field_is_set(iter_, index); } /** Returns whether field is readonly. * * @param index The field index. */ - bool is_readonly(int32_t index) const { - return ecs_field_is_readonly(m_iter, index); + bool is_readonly(int8_t index) const { + return ecs_field_is_readonly(iter_, index); } - /** Number of fields in iteator. + /** Number of fields in iterator. */ int32_t field_count() const { - return m_iter->field_count; + return iter_->field_count; } /** Size of field data type. * * @param index The field id. */ - size_t size(int32_t index) const { - return ecs_field_size(m_iter, index); + size_t size(int8_t index) const { + return ecs_field_size(iter_, index); } /** Obtain field source (0 if This). * * @param index The field index. - */ - flecs::entity src(int32_t index) const; + */ + flecs::entity src(int8_t index) const; /** Obtain id matched for field. * * @param index The field index. */ - flecs::id id(int32_t index) const; + flecs::id id(int8_t index) const; /** Obtain pair id matched for field. * This operation will fail if the id is not a pair. - * + * * @param index The field index. */ - flecs::id pair(int32_t index) const; + flecs::id pair(int8_t index) const; /** Obtain column index for field. * * @param index The field index. - */ - int32_t column_index(int32_t index) const { - return ecs_field_column_index(m_iter, index); + */ + int32_t column_index(int8_t index) const { + return ecs_field_column(iter_, index); + } + + /** Obtain term that triggered an observer + */ + int8_t term_index() const { + return iter_->term_index; } /** Convert current iterator result to string. */ flecs::string str() const { - char *s = ecs_iter_str(m_iter); + char *s = ecs_iter_str(iter_); return flecs::string(s); } @@ -21537,12 +22750,10 @@ struct iter { */ template , typename std::enable_if::value, void>::type* = nullptr> - flecs::column field(int32_t index) const { - return get_field(index); - } + flecs::field field(int8_t index) const; /** Get read/write access to field data. - * If the matched id for the specified field does not match with the provided + * If the matched id for the specified field does not match with the provided * type or if the field is readonly, the function will assert. * * @tparam T Type of the field. @@ -21552,58 +22763,85 @@ struct iter { template , typename std::enable_if< std::is_const::value == false, void>::type* = nullptr> - flecs::column field(int32_t index) const { - ecs_assert(!ecs_field_is_readonly(m_iter, index), - ECS_ACCESS_VIOLATION, NULL); - return get_field(index); - } + flecs::field field(int8_t index) const; /** Get unchecked access to field data. * Unchecked access is required when a system does not know the type of a * field at compile time. * - * @param index The field index. + * @param index The field index. */ - flecs::untyped_column field(int32_t index) const { + flecs::untyped_field field(int8_t index) const { + ecs_assert(!(iter_->flags & EcsIterCppEach), ECS_INVALID_OPERATION, + "cannot .field from .each, use .field_at(%d, row) instead", index); return get_unchecked_field(index); } + /** Get pointer to field at row. */ + void* field_at(int8_t index, size_t row) const { + if (iter_->row_fields & (1llu << index)) { + return get_unchecked_field_at(index, row)[0]; + } else { + return get_unchecked_field(index)[row]; + } + } + + /** Get reference to field at row. */ + template , + typename std::enable_if::value, void>::type* = nullptr> + const A& field_at(int8_t index, size_t row) const { + if (iter_->row_fields & (1llu << index)) { + return get_field_at(index, row)[0]; + } else { + return get_field(index)[row]; + } + } + + /** Get reference to field at row. */ + template , + typename std::enable_if< + std::is_const::value == false, void>::type* = nullptr> + A& field_at(int8_t index, size_t row) const { + ecs_assert(!ecs_field_is_readonly(iter_, index), + ECS_ACCESS_VIOLATION, NULL); + if (iter_->row_fields & (1llu << index)) { + return get_field_at(index, row)[0]; + } else { + return get_field(index)[row]; + } + } + /** Get readonly access to entity ids. * * @return The entity ids. */ - flecs::column entities() const { - return flecs::column(m_iter->entities, static_cast(m_iter->count), false); - } - - /** Obtain the total number of tables the iterator will iterate over. */ - int32_t table_count() const { - return m_iter->table_count; + flecs::field entities() const { + return flecs::field( + iter_->entities, static_cast(iter_->count), false); } /** Check if the current table has changed since the last iteration. * Can only be used when iterating queries and/or systems. */ bool changed() { - return ecs_query_changed(nullptr, m_iter); + return ecs_iter_changed(iter_); } /** Skip current table. * This indicates to the query that the data in the current table is not - * modified. By default, iterating a table with a query will mark the + * modified. By default, iterating a table with a query will mark the * iterated components as dirty if they are annotated with InOut or Out. - * + * * When this operation is invoked, the components of the current table will * not be marked dirty. */ void skip() { - ecs_query_skip(m_iter); + ecs_iter_skip(iter_); } /* Return group id for current table (grouped queries only) */ uint64_t group_id() const { - return m_iter->group_id; + return iter_->group_id; } -#ifdef FLECS_RULES /** Get value of variable by id. * Get value of a query variable for current result. */ @@ -21613,22 +22851,71 @@ struct iter { * Get value of a query variable for current result. */ flecs::entity get_var(const char *name) const; -#endif + + /** Progress iterator. + * This operation should only be called from a context where the iterator is + * not being progressed automatically. An example of a valid context is + * inside of a run() callback. An example of an invalid context is inside of + * an each() callback. + */ + bool next() { + if (iter_->flags & EcsIterIsValid && iter_->table) { + ECS_TABLE_UNLOCK(iter_->world, iter_->table); + } + bool result = iter_->next(iter_); + iter_->flags |= EcsIterIsValid; + if (result && iter_->table) { + ECS_TABLE_LOCK(iter_->world, iter_->table); + } + return result; + } + + /** Forward to each. + * If a system has an each callback registered, this operation will forward + * the current iterator to the each callback. + */ + void each() { + iter_->callback(iter_); + } + + /** Iterate targets for pair field. + * + * @param index The field index. + * @param func Callback invoked for each target + */ + template + void targets(int8_t index, const Func& func); + + /** Free iterator resources. + * This operation only needs to be called when the iterator is not iterated + * until completion (e.g. the last call to next() did not return false). + * + * Failing to call this operation on an unfinished iterator will throw a + * fatal LEAK_DETECTED error. + * + * @see ecs_iter_fini() + */ + void fini() { + if (iter_->flags & EcsIterIsValid && iter_->table) { + ECS_TABLE_UNLOCK(iter_->world, iter_->table); + } + ecs_iter_fini(iter_); + } private: /* Get field, check if correct type is used */ template > - flecs::column get_field(int32_t index) const { + flecs::field get_field(int8_t index) const { #ifndef FLECS_NDEBUG - ecs_entity_t term_id = ecs_field_id(m_iter, index); + ecs_entity_t term_id = ecs_field_id(iter_, index); ecs_assert(ECS_HAS_ID_FLAG(term_id, PAIR) || - term_id == _::cpp_type::id(m_iter->world), + term_id == _::type::id(iter_->world), ECS_COLUMN_TYPE_MISMATCH, NULL); #endif size_t count; - bool is_shared = !ecs_field_is_self(m_iter, index); + bool is_shared = !ecs_field_is_self(iter_, index); /* If a shared column is retrieved with 'column', there will only be a * single value. Ensure that the application does not accidentally read @@ -21638,18 +22925,34 @@ struct iter { } else { /* If column is owned, there will be as many values as there are * entities. */ - count = static_cast(m_iter->count); + count = static_cast(iter_->count); } - - return flecs::column( - static_cast(ecs_field_w_size(m_iter, sizeof(A), index)), + + return flecs::field( + static_cast(ecs_field_w_size(iter_, sizeof(A), index)), count, is_shared); } - flecs::untyped_column get_unchecked_field(int32_t index) const { + /* Get field, check if correct type is used */ + template > + flecs::field get_field_at(int8_t index, int32_t row) const { + +#ifndef FLECS_NDEBUG + ecs_entity_t term_id = ecs_field_id(iter_, index); + ecs_assert(ECS_HAS_ID_FLAG(term_id, PAIR) || + term_id == _::type::id(iter_->world), + ECS_COLUMN_TYPE_MISMATCH, NULL); +#endif + + return flecs::field( + static_cast(ecs_field_at_w_size(iter_, sizeof(A), index, row)), + 1, false); + } + + flecs::untyped_field get_unchecked_field(int8_t index) const { size_t count; - size_t size = ecs_field_size(m_iter, index); - bool is_shared = !ecs_field_is_self(m_iter, index); + size_t size = ecs_field_size(iter_, index); + bool is_shared = !ecs_field_is_self(iter_, index); /* If a shared column is retrieved with 'column', there will only be a * single value. Ensure that the application does not accidentally read @@ -21659,16 +22962,21 @@ struct iter { } else { /* If column is owned, there will be as many values as there are * entities. */ - count = static_cast(m_iter->count); + count = static_cast(iter_->count); } - return flecs::untyped_column( - ecs_field_w_size(m_iter, 0, index), size, count, is_shared); - } + return flecs::untyped_field( + ecs_field_w_size(iter_, 0, index), size, count, is_shared); + } - flecs::iter_t *m_iter; - std::size_t m_begin; - std::size_t m_end; + flecs::untyped_field get_unchecked_field_at(int8_t index, size_t row) const { + size_t size = ecs_field_size(iter_, index); + return flecs::untyped_field( + ecs_field_at_w_size(iter_, 0, index, static_cast(row)), + size, 1, false); + } + + flecs::iter_t *iter_; }; } // namespace flecs @@ -21678,7 +22986,7 @@ struct iter { /** * @file addons/cpp/entity.hpp * @brief Entity class. - * + * * This class provides read/write access to entities. */ @@ -21699,7 +23007,7 @@ struct iter { #pragma once /** - * \ingroup cpp_entities + * @ingroup cpp_entities * @{ */ @@ -21709,7 +23017,7 @@ namespace flecs /** Entity view. * Class with read operations for entities. Base for flecs::entity. * - * \ingroup cpp_entities + * @ingroup cpp_entities */ struct entity_view : public id { @@ -21734,7 +23042,7 @@ struct entity_view : public id { * @return The integer entity id. */ entity_t id() const { - return m_id; + return id_; } /** Check if entity is valid. @@ -21742,7 +23050,7 @@ struct entity_view : public id { * @return True if the entity is alive, false otherwise. */ bool is_valid() const { - return m_world && ecs_is_valid(m_world, m_id); + return world_ && ecs_is_valid(world_, id_); } explicit operator bool() const { @@ -21754,7 +23062,7 @@ struct entity_view : public id { * @return True if the entity is alive, false otherwise. */ bool is_alive() const { - return m_world && ecs_is_alive(m_world, m_id); + return world_ && ecs_is_alive(world_, id_); } /** Return the entity name. @@ -21762,7 +23070,7 @@ struct entity_view : public id { * @return The entity name. */ flecs::string_view name() const { - return flecs::string_view(ecs_get_name(m_world, m_id)); + return flecs::string_view(ecs_get_name(world_, id_)); } /** Return the entity symbol. @@ -21770,7 +23078,7 @@ struct entity_view : public id { * @return The entity symbol. */ flecs::string_view symbol() const { - return flecs::string_view(ecs_get_symbol(m_world, m_id)); + return flecs::string_view(ecs_get_symbol(world_, id_)); } /** Return the entity path. @@ -21786,7 +23094,7 @@ struct entity_view : public id { * @return The relative hierarchical entity path. */ flecs::string path_from(flecs::entity_t parent, const char *sep = "::", const char *init_sep = "::") const { - char *path = ecs_get_path_w_sep(m_world, parent, m_id, sep, init_sep); + char *path = ecs_get_path_w_sep(world_, parent, id_, sep, init_sep); return flecs::string(path); } @@ -21796,11 +23104,11 @@ struct entity_view : public id { */ template flecs::string path_from(const char *sep = "::", const char *init_sep = "::") const { - return path_from(_::cpp_type::id(m_world), sep, init_sep); + return path_from(_::type::id(world_), sep, init_sep); } bool enabled() const { - return !ecs_has_id(m_world, m_id, flecs::Disabled); + return !ecs_has_id(world_, id_, flecs::Disabled); } /** Get the entity's type. @@ -21826,7 +23134,10 @@ struct entity_view : public id { /** Iterate (component) ids of an entity. * The function parameter must match the following signature: - * void(*)(flecs::id id) + * + * @code + * void(*)(flecs::id id) + * @endcode * * @param func The function invoked for each id. */ @@ -21835,7 +23146,10 @@ struct entity_view : public id { /** Iterate matching pair ids of an entity. * The function parameter must match the following signature: - * void(*)(flecs::id id) + * + * @code + * void(*)(flecs::id id) + * @endcode * * @param func The function invoked for each id. */ @@ -21844,7 +23158,10 @@ struct entity_view : public id { /** Iterate targets for a given relationship. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * @param rel The relationship for which to iterate the targets. * @param func The function invoked for each target. @@ -21854,19 +23171,25 @@ struct entity_view : public id { /** Iterate targets for a given relationship. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * @tparam First The relationship for which to iterate the targets. * @param func The function invoked for each target. */ template void each(const Func& func) const { - return each(_::cpp_type::id(m_world), func); + return each(_::type::id(world_), func); } /** Iterate children for entity. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * @param rel The relationship to follow. * @param func The function invoked for each child. @@ -21876,50 +23199,40 @@ struct entity_view : public id { /* When the entity is a wildcard, this would attempt to query for all * entities with (ChildOf, *) or (ChildOf, _) instead of querying for * the children of the wildcard entity. */ - if (m_id == flecs::Wildcard || m_id == flecs::Any) { + if (id_ == flecs::Wildcard || id_ == flecs::Any) { /* This is correct, wildcard entities don't have children */ return; } - flecs::world world(m_world); - - ecs_term_t terms[2]; - ecs_filter_t f = ECS_FILTER_INIT; - f.terms = terms; - f.term_count = 2; - - ecs_filter_desc_t desc = {}; - desc.terms[0].first.id = rel; - desc.terms[0].second.id = m_id; - desc.terms[0].second.flags = EcsIsEntity; - desc.terms[1].id = flecs::Prefab; - desc.terms[1].oper = EcsOptional; - desc.storage = &f; - if (ecs_filter_init(m_world, &desc) != nullptr) { - ecs_iter_t it = ecs_filter_iter(m_world, &f); - while (ecs_filter_next(&it)) { - _::each_delegate(FLECS_MOV(func)).invoke(&it); - } + flecs::world world(world_); - ecs_filter_fini(&f); + ecs_iter_t it = ecs_each_id(world_, ecs_pair(rel, id_)); + while (ecs_each_next(&it)) { + _::each_delegate(FLECS_MOV(func)).invoke(&it); } } /** Iterate children for entity. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * @tparam Rel The relationship to follow. * @param func The function invoked for each child. */ template void children(Func&& func) const { - children(_::cpp_type::id(m_world), FLECS_MOV(func)); + children(_::type::id(world_), FLECS_MOV(func)); } /** Iterate children for entity. * The function parameter must match the following signature: - * void(*)(flecs::entity target) + * + * @code + * void(*)(flecs::entity target) + * @endcode * * This operation follows the ChildOf relationship. * @@ -21938,9 +23251,10 @@ struct entity_view : public id { */ template ::value > = 0> const T* get() const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast(ecs_get_id(m_world, m_id, comp_id)); + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_id(world_, id_, comp_id)); } /** Get component value. @@ -21954,9 +23268,10 @@ struct entity_view : public id { template , if_t< flecs::is_pair::value > = 0> const A* get() const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - return static_cast(ecs_get_id(m_world, m_id, comp_id)); + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_id(world_, id_, comp_id)); } /** Get a pair. @@ -21979,10 +23294,11 @@ struct entity_view : public id { */ template::value> = 0> const First* get(Second second) const { - auto comp_id = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + auto first = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); return static_cast( - ecs_get_id(m_world, m_id, ecs_pair(comp_id, second))); + ecs_get_id(world_, id_, ecs_pair(first, second))); } /** Get a pair. @@ -21993,7 +23309,7 @@ struct entity_view : public id { */ template::value> = 0> const First* get(Second constant) const { - const auto& et = enum_type(this->m_world); + const auto& et = enum_type(this->world_); flecs::entity_t target = et.entity(constant); return get(target); } @@ -22005,7 +23321,7 @@ struct entity_view : public id { * have the component. */ const void* get(flecs::id_t comp) const { - return ecs_get_id(m_world, m_id, comp); + return ecs_get_id(world_, id_, comp); } /** Get a pair (untyped). @@ -22017,7 +23333,7 @@ struct entity_view : public id { * @param second The second element of the pair. */ const void* get(flecs::entity_t first, flecs::entity_t second) const { - return ecs_get_id(m_world, m_id, ecs_pair(first, second)); + return ecs_get_id(world_, id_, ecs_pair(first, second)); } /** Get 1..N components. @@ -22040,13 +23356,16 @@ struct entity_view : public id { * function will write-lock the table (see ecs_write_begin). * * Example: - * e.get([](Position& p, Velocity& v) { // write lock - * p.x += v.x; - * }); + * + * @code + * e.get([](Position& p, Velocity& v) { // write lock + * p.x += v.x; + * }); * - * e.get([](const Position& p) { // read lock - * std::cout << p.x << std::endl; - * }); + * e.get([](const Position& p) { // read lock + * std::cout << p.x << std::endl; + * }); + * @endcode * * @param func The callback to invoke. * @return True if the entity has all components, false if not. @@ -22071,10 +23390,15 @@ struct entity_view : public id { */ template const Second* get_second(flecs::entity_t first) const { - auto second = _::cpp_type::id(m_world); - ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); + auto second = _::type::id(world_); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second)) != NULL, + ECS_INVALID_PARAMETER, "pair is not a component"); + ecs_assert( ecs_get_type_info(world_, ecs_pair(first, second))->component == second, + ECS_INVALID_PARAMETER, "type of pair is not Second"); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); return static_cast( - ecs_get_id(m_world, m_id, ecs_pair(first, second))); + ecs_get_id(world_, id_, ecs_pair(first, second))); } /** Get the second part for a pair. @@ -22089,6 +23413,131 @@ struct entity_view : public id { return get>(); } + /** Get mutable component value. + * + * @tparam T The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + template ::value > = 0> + T* get_mut() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_mut_id(world_, id_, comp_id)); + } + + /** Get mutable component value. + * Overload for when T is not the same as the actual type, which happens + * when using pair types. + * + * @tparam T The component to get. + * @return Pointer to the component value, nullptr if the entity does not + * have the component. + */ + template , + if_t< flecs::is_pair::value > = 0> + A* get_mut() const { + auto comp_id = _::type::id(world_); + ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, + "operation invalid for empty type"); + return static_cast(ecs_get_mut_id(world_, id_, comp_id)); + } + + /** Get a mutable pair. + * This operation gets the value for a pair from the entity. + * + * @tparam First The first element of the pair. + * @tparam Second the second element of a pair. + */ + template , + typename A = actual_type_t